This reuses the existing I2C-over-AUX implementation by translating the
messages to ones compatible with the struct drm_dp_aux implementation.

Signed-off-by: Thierry Reding <treding at nvidia.com>
---
 drivers/gpu/drm/drm_dp_helper.c | 78 +++++++++++++++++++++++++++++++++++++++++
 include/drm/drm_dp_helper.h     |  4 +++
 2 files changed, 82 insertions(+)

diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
index 873aedccc84e..2864a47e6abe 100644
--- a/drivers/gpu/drm/drm_dp_helper.c
+++ b/drivers/gpu/drm/drm_dp_helper.c
@@ -471,3 +471,81 @@ int drm_dp_link_power_up(struct drm_dp_aux *aux, struct 
drm_dp_link *link)

        return 0;
 }
+
+struct drm_dp_i2c_adapter {
+       struct i2c_algo_dp_aux_data algo;
+       struct i2c_adapter adapter;
+       struct drm_dp_aux *aux;
+};
+
+static int drm_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
+                            uint8_t write_byte, uint8_t *read_byte)
+{
+       struct drm_dp_i2c_adapter *dp = container_of(adapter, struct 
drm_dp_i2c_adapter, adapter);
+       struct i2c_algo_dp_aux_data *algo = adapter->algo_data;
+       struct drm_dp_aux_msg msg;
+       u8 data = 0;
+       int err;
+
+       if (mode & MODE_I2C_START)
+               return 0;
+
+       memset(&msg, 0, sizeof(msg));
+       msg.address = algo->address;
+       msg.flags = DRM_DP_AUX_I2C;
+
+       if ((mode & MODE_I2C_STOP) == 0)
+               msg.flags |= DRM_DP_AUX_I2C_MOT;
+
+       if (mode & MODE_I2C_WRITE) {
+               msg.flags |= DRM_DP_AUX_WRITE;
+               msg.buffer = &write_byte;
+               msg.size = 1;
+       } else {
+               msg.buffer = &data;
+               msg.size = 1;
+       }
+
+       err = dp->aux->transfer(dp->aux, &msg);
+       if (err < 0)
+               return err;
+
+       /*
+        * Allow the transfer() functions to be ignorant about whether or not
+        * the read buffer passed in is valid or not.
+        */
+       if (((mode & MODE_I2C_WRITE) == 0) && read_byte)
+               *read_byte = data;
+
+       return err;
+}
+
+struct i2c_adapter *drm_dp_add_i2c_bus(struct drm_dp_aux *aux)
+{
+       struct drm_dp_i2c_adapter *adapter;
+       int err;
+
+       adapter = kzalloc(sizeof(*adapter), GFP_KERNEL);
+       if (!adapter)
+               return ERR_PTR(-ENOMEM);
+
+       adapter->algo.running = false;
+       adapter->algo.address = 0;
+       adapter->algo.aux_ch = drm_dp_i2c_aux_ch;
+       adapter->aux = aux;
+
+       adapter->adapter.class = I2C_CLASS_DDC;
+       adapter->adapter.owner = THIS_MODULE;
+       strncpy(adapter->adapter.name, "DPAUX", sizeof(adapter->adapter.name));
+       adapter->adapter.algo_data = &adapter->algo;
+       adapter->adapter.dev.parent = aux->dev;
+       adapter->adapter.dev.of_node = aux->dev->of_node;
+
+       err = i2c_dp_aux_add_bus(&adapter->adapter);
+       if (err < 0) {
+               kfree(adapter);
+               return ERR_PTR(err);
+       }
+
+       return &adapter->adapter;
+}
diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
index cf03a6ff8634..8b0f6c44251e 100644
--- a/include/drm/drm_dp_helper.h
+++ b/include/drm/drm_dp_helper.h
@@ -444,6 +444,8 @@ struct drm_dp_aux_msg {
  * @transfer: transfers a message representing a single AUX transaction
  */
 struct drm_dp_aux {
+       struct device *dev;
+
        ssize_t (*transfer)(struct drm_dp_aux *aux,
                            struct drm_dp_aux_msg *msg);
 };
@@ -515,4 +517,6 @@ 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);

+struct i2c_adapter *drm_dp_add_i2c_bus(struct drm_dp_aux *aux);
+
 #endif /* _DRM_DP_HELPER_H_ */
-- 
1.8.4.2

Reply via email to