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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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




[Index of Archives]     [Linux DRI Users]     [Linux Intel Graphics]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux