Add drm_dp_aux_register_ddc/chardev() helpers that allow DP drivers to register I2C DDC adapter and character device separately. Cc: <stable@xxxxxxxxxxxxxxx> # 5.13+ Reported-by: Thomas Graichen <thomas.graichen@xxxxxxxxx> # T124 Nyan Big Tested-by: Thomas Graichen <thomas.graichen@xxxxxxxxx> # T124 Nyan Big Signed-off-by: Dmitry Osipenko <digetx@xxxxxxxxx> --- drivers/gpu/drm/drm_dp_helper.c | 112 +++++++++++++++++++++++++++----- include/drm/drm_dp_helper.h | 4 ++ 2 files changed, 99 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 4d0d1e8e51fa..56e3e57e6dc7 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -1775,7 +1775,7 @@ EXPORT_SYMBOL(drm_dp_remote_aux_init); * drm_dp_aux_init() - minimally initialise an aux channel * @aux: DisplayPort AUX channel * - * If you need to use the drm_dp_aux's i2c adapter prior to registering it with + * If you need to use the drm_dp_aux handle prior to registering it with * the outside world, call drm_dp_aux_init() first. For drivers which are * grandparents to their AUX adapters (e.g. the AUX adapter is parented by a * &drm_connector), you must still call drm_dp_aux_register() once the connector @@ -1790,6 +1790,9 @@ EXPORT_SYMBOL(drm_dp_remote_aux_init); */ void drm_dp_aux_init(struct drm_dp_aux *aux) { + if (aux->ddc.algo) + return; + mutex_init(&aux->hw_mutex); mutex_init(&aux->cec.lock); INIT_WORK(&aux->crc_work, drm_dp_aux_crc_work); @@ -1827,31 +1830,23 @@ EXPORT_SYMBOL(drm_dp_aux_init); * mentioned above need to call drm_dp_aux_init() in order to use the AUX * channel before registration. * + * Don't mix drm_dp_aux_register() with drm_dp_aux_register_chardev/ddc(). + * * Returns 0 on success or a negative error code on failure. */ int drm_dp_aux_register(struct drm_dp_aux *aux) { int ret; - WARN_ON_ONCE(!aux->drm_dev); - - if (!aux->ddc.algo) - drm_dp_aux_init(aux); - - aux->ddc.class = I2C_CLASS_DDC; - aux->ddc.owner = THIS_MODULE; - aux->ddc.dev.parent = aux->dev; + drm_dp_aux_init(aux); - strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(aux->dev), - sizeof(aux->ddc.name)); - - ret = drm_dp_aux_register_devnode(aux); + ret = drm_dp_aux_register_ddc(aux); if (ret) return ret; - ret = i2c_add_adapter(&aux->ddc); + ret = drm_dp_aux_register_chardev(aux); if (ret) { - drm_dp_aux_unregister_devnode(aux); + drm_dp_aux_unregister_ddc(aux); return ret; } @@ -1865,11 +1860,94 @@ EXPORT_SYMBOL(drm_dp_aux_register); */ void drm_dp_aux_unregister(struct drm_dp_aux *aux) { - drm_dp_aux_unregister_devnode(aux); - i2c_del_adapter(&aux->ddc); + drm_dp_aux_unregister_chardev(aux); + drm_dp_aux_unregister_ddc(aux); } EXPORT_SYMBOL(drm_dp_aux_unregister); +/** + * drm_dp_aux_register_ddc() - initialise and register I2C DDC part of AUX channel + * @aux: DisplayPort AUX channel + * + * Automatically calls drm_dp_aux_init() if this hasn't been done yet. + * If you need to use the drm_dp_aux's I2C adapter prior to registering it with + * the outside world, call drm_dp_aux_register_ddc() first. For drivers which + * are grandparents to their AUX adapters (e.g. the AUX adapter is parented by a + * &drm_connector), you must still call drm_dp_aux_register_chardev() once the + * connector has been registered to allow userspace access to the auxiliary DP + * channel. + * + * For devices which use a separate platform device for their AUX adapters, this + * may be called as early as required by the driver. + * + * Returns 0 on success or a negative error code on failure. + */ +int drm_dp_aux_register_ddc(struct drm_dp_aux *aux) +{ + drm_dp_aux_init(aux); + + if (WARN_ON(aux->ddc.class == I2C_CLASS_DDC)) + return -EBUSY; + + aux->ddc.class = I2C_CLASS_DDC; + aux->ddc.owner = THIS_MODULE; + aux->ddc.dev.parent = aux->dev; + + strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(aux->dev), + sizeof(aux->ddc.name)); + + return i2c_add_adapter(&aux->ddc); +} +EXPORT_SYMBOL(drm_dp_aux_register_ddc); + +/** + * drm_dp_aux_unregister_ddc() - unregister I2C DDC part of AUX channel + * @aux: DisplayPort AUX channel + */ +void drm_dp_aux_unregister_ddc(struct drm_dp_aux *aux) +{ + i2c_del_adapter(&aux->ddc); + + aux->ddc.class = 0; +} +EXPORT_SYMBOL(drm_dp_aux_unregister_ddc); + +/** + * drm_dp_aux_register_chardev() - initialise and register userspace part of AUX channel + * @aux: DisplayPort AUX channel + * + * Automatically calls drm_dp_aux_init() if this hasn't been done yet. This + * should only be called once the parent of @aux, &drm_dp_aux.dev, is + * initialized. For devices which are grandparents of their AUX channels, + * &drm_dp_aux.dev will typically be the &drm_connector &device which + * corresponds to @aux. For these devices, it's advised to call + * drm_dp_aux_register_chardev() in &drm_connector_funcs.late_register, and + * likewise to call drm_dp_aux_unregister_chardev() in + * &drm_connector_funcs.early_unregister. Functions which don't follow this + * will likely Oops when %CONFIG_DRM_DP_AUX_CHARDEV is enabled. + * + * Returns 0 on success or a negative error code on failure. + */ +int drm_dp_aux_register_chardev(struct drm_dp_aux *aux) +{ + WARN_ON_ONCE(!aux->drm_dev); + + drm_dp_aux_init(aux); + + return drm_dp_aux_register_devnode(aux); +} +EXPORT_SYMBOL(drm_dp_aux_register_chardev); + +/** + * drm_dp_aux_unregister() - unregister userspace part of AUX channel + * @aux: DisplayPort AUX channel + */ +void drm_dp_aux_unregister_chardev(struct drm_dp_aux *aux) +{ + drm_dp_aux_unregister_devnode(aux); +} +EXPORT_SYMBOL(drm_dp_aux_unregister_chardev); + #define PSR_SETUP_TIME(x) [DP_PSR_SETUP_TIME_ ## x >> DP_PSR_SETUP_TIME_SHIFT] = (x) /** diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index b52df4db3e8f..80130458188d 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -2130,6 +2130,10 @@ void drm_dp_remote_aux_init(struct drm_dp_aux *aux); void drm_dp_aux_init(struct drm_dp_aux *aux); int drm_dp_aux_register(struct drm_dp_aux *aux); void drm_dp_aux_unregister(struct drm_dp_aux *aux); +int drm_dp_aux_register_ddc(struct drm_dp_aux *aux); +void drm_dp_aux_unregister_ddc(struct drm_dp_aux *aux); +int drm_dp_aux_register_chardev(struct drm_dp_aux *aux); +void drm_dp_aux_unregister_chardev(struct drm_dp_aux *aux); int drm_dp_start_crc(struct drm_dp_aux *aux, struct drm_crtc *crtc); int drm_dp_stop_crc(struct drm_dp_aux *aux); -- 2.33.1