mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	drm/dp: add a hw mutex around the transfer functions. (v2)
This should avoid races between connector probing and HPD irqs in the future, currently mode_config.mutex blocks this possibility. Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch> Signed-off-by: Dave Airlie <airlied@redhat.com>
This commit is contained in:
		
							parent
							
								
									885ae1c55a
								
							
						
					
					
						commit
						4f71d0cb76
					
				
					 6 changed files with 29 additions and 20 deletions
				
			
		| 
						 | 
					@ -216,7 +216,7 @@ i2c_dp_aux_prepare_bus(struct i2c_adapter *adapter)
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * IMPORTANT:
 | 
					 * IMPORTANT:
 | 
				
			||||||
 * This interface is deprecated, please switch to the new dp aux helpers and
 | 
					 * This interface is deprecated, please switch to the new dp aux helpers and
 | 
				
			||||||
 * drm_dp_aux_register_i2c_bus().
 | 
					 * drm_dp_aux_register().
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int
 | 
					int
 | 
				
			||||||
i2c_dp_aux_add_bus(struct i2c_adapter *adapter)
 | 
					i2c_dp_aux_add_bus(struct i2c_adapter *adapter)
 | 
				
			||||||
| 
						 | 
					@ -382,7 +382,10 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request,
 | 
				
			||||||
	 * transactions.
 | 
						 * transactions.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	for (retry = 0; retry < 7; retry++) {
 | 
						for (retry = 0; retry < 7; retry++) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							mutex_lock(&aux->hw_mutex);
 | 
				
			||||||
		err = aux->transfer(aux, &msg);
 | 
							err = aux->transfer(aux, &msg);
 | 
				
			||||||
 | 
							mutex_unlock(&aux->hw_mutex);
 | 
				
			||||||
		if (err < 0) {
 | 
							if (err < 0) {
 | 
				
			||||||
			if (err == -EBUSY)
 | 
								if (err == -EBUSY)
 | 
				
			||||||
				continue;
 | 
									continue;
 | 
				
			||||||
| 
						 | 
					@ -596,7 +599,9 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
 | 
				
			||||||
	 * before giving up the AUX transaction.
 | 
						 * before giving up the AUX transaction.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	for (retry = 0; retry < 7; retry++) {
 | 
						for (retry = 0; retry < 7; retry++) {
 | 
				
			||||||
 | 
							mutex_lock(&aux->hw_mutex);
 | 
				
			||||||
		err = aux->transfer(aux, msg);
 | 
							err = aux->transfer(aux, msg);
 | 
				
			||||||
 | 
							mutex_unlock(&aux->hw_mutex);
 | 
				
			||||||
		if (err < 0) {
 | 
							if (err < 0) {
 | 
				
			||||||
			if (err == -EBUSY)
 | 
								if (err == -EBUSY)
 | 
				
			||||||
				continue;
 | 
									continue;
 | 
				
			||||||
| 
						 | 
					@ -729,13 +734,15 @@ static const struct i2c_algorithm drm_dp_i2c_algo = {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * drm_dp_aux_register_i2c_bus() - register an I2C adapter for I2C-over-AUX
 | 
					 * drm_dp_aux_register() - initialise and register aux channel
 | 
				
			||||||
 * @aux: DisplayPort AUX channel
 | 
					 * @aux: DisplayPort AUX channel
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Returns 0 on success or a negative error code on failure.
 | 
					 * Returns 0 on success or a negative error code on failure.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int drm_dp_aux_register_i2c_bus(struct drm_dp_aux *aux)
 | 
					int drm_dp_aux_register(struct drm_dp_aux *aux)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						mutex_init(&aux->hw_mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	aux->ddc.algo = &drm_dp_i2c_algo;
 | 
						aux->ddc.algo = &drm_dp_i2c_algo;
 | 
				
			||||||
	aux->ddc.algo_data = aux;
 | 
						aux->ddc.algo_data = aux;
 | 
				
			||||||
	aux->ddc.retries = 3;
 | 
						aux->ddc.retries = 3;
 | 
				
			||||||
| 
						 | 
					@ -750,14 +757,14 @@ int drm_dp_aux_register_i2c_bus(struct drm_dp_aux *aux)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return i2c_add_adapter(&aux->ddc);
 | 
						return i2c_add_adapter(&aux->ddc);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(drm_dp_aux_register_i2c_bus);
 | 
					EXPORT_SYMBOL(drm_dp_aux_register);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * drm_dp_aux_unregister_i2c_bus() - unregister an I2C-over-AUX adapter
 | 
					 * drm_dp_aux_unregister() - unregister an AUX adapter
 | 
				
			||||||
 * @aux: DisplayPort AUX channel
 | 
					 * @aux: DisplayPort AUX channel
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
void drm_dp_aux_unregister_i2c_bus(struct drm_dp_aux *aux)
 | 
					void drm_dp_aux_unregister(struct drm_dp_aux *aux)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	i2c_del_adapter(&aux->ddc);
 | 
						i2c_del_adapter(&aux->ddc);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(drm_dp_aux_unregister_i2c_bus);
 | 
					EXPORT_SYMBOL(drm_dp_aux_unregister);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -702,9 +702,9 @@ intel_dp_aux_init(struct intel_dp *intel_dp, struct intel_connector *connector)
 | 
				
			||||||
	DRM_DEBUG_KMS("registering %s bus for %s\n", name,
 | 
						DRM_DEBUG_KMS("registering %s bus for %s\n", name,
 | 
				
			||||||
		      connector->base.kdev->kobj.name);
 | 
							      connector->base.kdev->kobj.name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = drm_dp_aux_register_i2c_bus(&intel_dp->aux);
 | 
						ret = drm_dp_aux_register(&intel_dp->aux);
 | 
				
			||||||
	if (ret < 0) {
 | 
						if (ret < 0) {
 | 
				
			||||||
		DRM_ERROR("drm_dp_aux_register_i2c_bus() for %s failed (%d)\n",
 | 
							DRM_ERROR("drm_dp_aux_register() for %s failed (%d)\n",
 | 
				
			||||||
			  name, ret);
 | 
								  name, ret);
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -714,7 +714,7 @@ intel_dp_aux_init(struct intel_dp *intel_dp, struct intel_connector *connector)
 | 
				
			||||||
				intel_dp->aux.ddc.dev.kobj.name);
 | 
									intel_dp->aux.ddc.dev.kobj.name);
 | 
				
			||||||
	if (ret < 0) {
 | 
						if (ret < 0) {
 | 
				
			||||||
		DRM_ERROR("sysfs_create_link() for %s failed (%d)\n", name, ret);
 | 
							DRM_ERROR("sysfs_create_link() for %s failed (%d)\n", name, ret);
 | 
				
			||||||
		drm_dp_aux_unregister_i2c_bus(&intel_dp->aux);
 | 
							drm_dp_aux_unregister(&intel_dp->aux);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3662,7 +3662,7 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder)
 | 
				
			||||||
	struct intel_dp *intel_dp = &intel_dig_port->dp;
 | 
						struct intel_dp *intel_dp = &intel_dig_port->dp;
 | 
				
			||||||
	struct drm_device *dev = intel_dp_to_dev(intel_dp);
 | 
						struct drm_device *dev = intel_dp_to_dev(intel_dp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	drm_dp_aux_unregister_i2c_bus(&intel_dp->aux);
 | 
						drm_dp_aux_unregister(&intel_dp->aux);
 | 
				
			||||||
	drm_encoder_cleanup(encoder);
 | 
						drm_encoder_cleanup(encoder);
 | 
				
			||||||
	if (is_edp(intel_dp)) {
 | 
						if (is_edp(intel_dp)) {
 | 
				
			||||||
		cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
 | 
							cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
 | 
				
			||||||
| 
						 | 
					@ -4244,7 +4244,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
 | 
				
			||||||
	intel_dp->psr_setup_done = false;
 | 
						intel_dp->psr_setup_done = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!intel_edp_init_connector(intel_dp, intel_connector, &power_seq)) {
 | 
						if (!intel_edp_init_connector(intel_dp, intel_connector, &power_seq)) {
 | 
				
			||||||
		drm_dp_aux_unregister_i2c_bus(&intel_dp->aux);
 | 
							drm_dp_aux_unregister(&intel_dp->aux);
 | 
				
			||||||
		if (is_edp(intel_dp)) {
 | 
							if (is_edp(intel_dp)) {
 | 
				
			||||||
			cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
 | 
								cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
 | 
				
			||||||
			mutex_lock(&dev->mode_config.connection_mutex);
 | 
								mutex_lock(&dev->mode_config.connection_mutex);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -222,11 +222,12 @@ void radeon_dp_aux_init(struct radeon_connector *radeon_connector)
 | 
				
			||||||
	radeon_connector->ddc_bus->rec.hpd = radeon_connector->hpd.hpd;
 | 
						radeon_connector->ddc_bus->rec.hpd = radeon_connector->hpd.hpd;
 | 
				
			||||||
	radeon_connector->ddc_bus->aux.dev = radeon_connector->base.kdev;
 | 
						radeon_connector->ddc_bus->aux.dev = radeon_connector->base.kdev;
 | 
				
			||||||
	radeon_connector->ddc_bus->aux.transfer = radeon_dp_aux_transfer;
 | 
						radeon_connector->ddc_bus->aux.transfer = radeon_dp_aux_transfer;
 | 
				
			||||||
	ret = drm_dp_aux_register_i2c_bus(&radeon_connector->ddc_bus->aux);
 | 
					
 | 
				
			||||||
 | 
						ret = drm_dp_aux_register(&radeon_connector->ddc_bus->aux);
 | 
				
			||||||
	if (!ret)
 | 
						if (!ret)
 | 
				
			||||||
		radeon_connector->ddc_bus->has_aux = true;
 | 
							radeon_connector->ddc_bus->has_aux = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	WARN(ret, "drm_dp_aux_register_i2c_bus() failed with error %d\n", ret);
 | 
						WARN(ret, "drm_dp_aux_register() failed with error %d\n", ret);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/***** general DP utility functions *****/
 | 
					/***** general DP utility functions *****/
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -988,7 +988,7 @@ void radeon_i2c_destroy(struct radeon_i2c_chan *i2c)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	i2c_del_adapter(&i2c->adapter);
 | 
						i2c_del_adapter(&i2c->adapter);
 | 
				
			||||||
	if (i2c->has_aux)
 | 
						if (i2c->has_aux)
 | 
				
			||||||
		drm_dp_aux_unregister_i2c_bus(&i2c->aux);
 | 
							drm_dp_aux_unregister(&i2c->aux);
 | 
				
			||||||
	kfree(i2c);
 | 
						kfree(i2c);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -332,7 +332,7 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
 | 
				
			||||||
	dpaux->aux.transfer = tegra_dpaux_transfer;
 | 
						dpaux->aux.transfer = tegra_dpaux_transfer;
 | 
				
			||||||
	dpaux->aux.dev = &pdev->dev;
 | 
						dpaux->aux.dev = &pdev->dev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = drm_dp_aux_register_i2c_bus(&dpaux->aux);
 | 
						err = drm_dp_aux_register(&dpaux->aux);
 | 
				
			||||||
	if (err < 0)
 | 
						if (err < 0)
 | 
				
			||||||
		return err;
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -355,7 +355,7 @@ static int tegra_dpaux_remove(struct platform_device *pdev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct tegra_dpaux *dpaux = platform_get_drvdata(pdev);
 | 
						struct tegra_dpaux *dpaux = platform_get_drvdata(pdev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	drm_dp_aux_unregister_i2c_bus(&dpaux->aux);
 | 
						drm_dp_aux_unregister(&dpaux->aux);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mutex_lock(&dpaux_lock);
 | 
						mutex_lock(&dpaux_lock);
 | 
				
			||||||
	list_del(&dpaux->list);
 | 
						list_del(&dpaux->list);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -514,6 +514,7 @@ struct drm_dp_aux_msg {
 | 
				
			||||||
 * @name: user-visible name of this AUX channel and the I2C-over-AUX adapter
 | 
					 * @name: user-visible name of this AUX channel and the I2C-over-AUX adapter
 | 
				
			||||||
 * @ddc: I2C adapter that can be used for I2C-over-AUX communication
 | 
					 * @ddc: I2C adapter that can be used for I2C-over-AUX communication
 | 
				
			||||||
 * @dev: pointer to struct device that is the parent for this AUX channel
 | 
					 * @dev: pointer to struct device that is the parent for this AUX channel
 | 
				
			||||||
 | 
					 * @hw_mutex: internal mutex used for locking transfers
 | 
				
			||||||
 * @transfer: transfers a message representing a single AUX transaction
 | 
					 * @transfer: transfers a message representing a single AUX transaction
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * The .dev field should be set to a pointer to the device that implements
 | 
					 * The .dev field should be set to a pointer to the device that implements
 | 
				
			||||||
| 
						 | 
					@ -546,7 +547,7 @@ struct drm_dp_aux {
 | 
				
			||||||
	const char *name;
 | 
						const char *name;
 | 
				
			||||||
	struct i2c_adapter ddc;
 | 
						struct i2c_adapter ddc;
 | 
				
			||||||
	struct device *dev;
 | 
						struct device *dev;
 | 
				
			||||||
 | 
						struct mutex hw_mutex;
 | 
				
			||||||
	ssize_t (*transfer)(struct drm_dp_aux *aux,
 | 
						ssize_t (*transfer)(struct drm_dp_aux *aux,
 | 
				
			||||||
			    struct drm_dp_aux_msg *msg);
 | 
								    struct drm_dp_aux_msg *msg);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -605,7 +606,7 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link);
 | 
				
			||||||
int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link);
 | 
					int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link);
 | 
				
			||||||
int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link);
 | 
					int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int drm_dp_aux_register_i2c_bus(struct drm_dp_aux *aux);
 | 
					int drm_dp_aux_register(struct drm_dp_aux *aux);
 | 
				
			||||||
void drm_dp_aux_unregister_i2c_bus(struct drm_dp_aux *aux);
 | 
					void drm_dp_aux_unregister(struct drm_dp_aux *aux);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* _DRM_DP_HELPER_H_ */
 | 
					#endif /* _DRM_DP_HELPER_H_ */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue