[PATCH 4/4] drm/dp: Allow registering AUX channels as I2C busses

Thierry Reding thierry.reding at gmail.com
Thu Dec 12 06:53:50 PST 2013


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



More information about the dri-devel mailing list