@@ -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;
+}
@@ -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_ */
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@nvidia.com> --- drivers/gpu/drm/drm_dp_helper.c | 78 +++++++++++++++++++++++++++++++++++++++++ include/drm/drm_dp_helper.h | 4 +++ 2 files changed, 82 insertions(+)