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@xxxxxxxxxx> --- 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 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel