[PATCH v1 1/2] drm/dp: Add drm_dp_aux_register_ddc/chardev() helpers

Dmitry Osipenko
2021-11-07T23:08:32Z

Add drm_dp_aux_register_ddc/chardev() helpers that allow DP drivers
to register I2C DDC adapter and character device separately.

Cc: <stable@vger.kernel.org> # 5.13+
Reported-by: Thomas Graichen <thomas.graichen@gmail.com> # T124 Nyan Big
Tested-by: Thomas Graichen <thomas.graichen@gmail.com> # T124 Nyan Big
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
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