This was hand-rolled in the first version, and will surely be useful as
we expand the driver to support more varied use cases.

Changes in v2:
- Change subject prefix s/panel/bridge/
- Downgrade warning in poll function to error message
- Fix DP_EDP_CONFIGURATION_SET write value (Sandeep)
- Mask upper 8 bits of msg->address (Sandeep)
- Check aux cmd status for errors after completing the send (Sandeep)
- Remove length check since it's covered in the aux status
- Flip the READ check in transfer to WRITE check + early exit

Cc: Sandeep Panda <spa...@codeaurora.org>
Signed-off-by: Sean Paul <seanp...@chromium.org>
---
 drivers/gpu/drm/bridge/ti-sn65dsi86.c | 119 ++++++++++++++++++++++++--
 1 file changed, 111 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c 
b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
index 1b6e8b72be58..02df26fd0084 100644
--- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c
+++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
@@ -7,12 +7,14 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
 #include <drm/drm_mipi_dsi.h>
 #include <drm/drm_of.h>
 #include <drm/drm_panel.h>
 #include <linux/clk.h>
 #include <linux/gpio/consumer.h>
 #include <linux/i2c.h>
+#include <linux/iopoll.h>
 #include <linux/of_graph.h>
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
@@ -29,12 +31,15 @@
 #define SN_DATARATE_CONFIG_REG                 0x94
 #define SN_PLL_ENABLE_REG                      0x0D
 #define SN_SCRAMBLE_CONFIG_REG                 0x95
-#define SN_AUX_WDATA0_REG                      0x64
+#define SN_AUX_WDATA_REG(x)                    (0x64 + x)
 #define SN_AUX_ADDR_19_16_REG                  0x74
 #define SN_AUX_ADDR_15_8_REG                   0x75
 #define SN_AUX_ADDR_7_0_REG                    0x76
 #define SN_AUX_LENGTH_REG                      0x77
 #define SN_AUX_CMD_REG                         0x78
+#define  AUX_CMD_SEND                          0x01
+#define  AUX_CMD_REQ(x)                                (x << 4)
+#define SN_AUX_RDATA_REG(x)                    (0x79 + x)
 #define SN_ML_TX_MODE_REG                      0x96
 /* video config specific registers */
 #define SN_CHA_ACTIVE_LINE_LENGTH_LOW_REG      0x20
@@ -48,6 +53,10 @@
 #define SN_CHA_HORIZONTAL_FRONT_PORCH_REG      0x38
 #define SN_CHA_VERTICAL_FRONT_PORCH_REG                0x3A
 #define SN_DATA_FORMAT_REG                     0x5B
+#define SN_AUX_CMD_STATUS_REG                  0xF4
+#define  AUX_IRQ_STATUS_AUX_RPLY_TOUT          BIT(3)
+#define  AUX_IRQ_STATUS_AUX_SHORT              BIT(5)
+#define  AUX_IRQ_STATUS_NAT_I2C_FAIL           BIT(6)
 
 #define MIN_DSI_CLK_FREQ_MHZ   40
 
@@ -64,6 +73,9 @@
 #define SN_DP_DATA_RATE_OFFSET 5
 #define SN_SYNC_POLARITY_OFFSET        7
 
+/* Matches DP_AUX_MAX_PAYLOAD_BYTES (for now) */
+#define SN_AUX_MAX_PAYLOAD_BYTES       16
+
 #define SN_ENABLE_VID_STREAM_BIT       BIT(3)
 #define SN_REFCLK_FREQ_BITS            GENMASK(3, 1)
 #define SN_DSIA_NUM_LANES_BITS         GENMASK(4, 3)
@@ -76,6 +88,7 @@
 struct ti_sn_bridge {
        struct device                   *dev;
        struct regmap                   *regmap;
+       struct drm_dp_aux               aux;
        struct drm_bridge               bridge;
        struct drm_connector            connector;
        struct device_node              *host_node;
@@ -473,13 +486,8 @@ static void ti_sn_bridge_enable(struct drm_bridge *bridge)
         * authentication method. We need to enable this method in the eDP panel
         * at DisplayPort address 0x0010A prior to link training.
         */
-       regmap_write(pdata->regmap, SN_AUX_WDATA0_REG, 0x01);
-       regmap_write(pdata->regmap, SN_AUX_ADDR_19_16_REG, 0x00);
-       regmap_write(pdata->regmap, SN_AUX_ADDR_15_8_REG, 0x01);
-       regmap_write(pdata->regmap, SN_AUX_ADDR_7_0_REG, 0x0A);
-       regmap_write(pdata->regmap, SN_AUX_LENGTH_REG, 0x01);
-       regmap_write(pdata->regmap, SN_AUX_CMD_REG, 0x81);
-       usleep_range(10000, 10500); /* 10ms delay recommended by spec */
+       drm_dp_dpcd_writeb(&pdata->aux, DP_EDP_CONFIGURATION_SET,
+                          DP_ALTERNATE_SCRAMBLER_RESET_ENABLE);
 
        /* Semi auto link training mode */
        regmap_write(pdata->regmap, SN_ML_TX_MODE_REG, 0x0A);
@@ -527,6 +535,96 @@ static const struct drm_bridge_funcs ti_sn_bridge_funcs = {
        .post_disable = ti_sn_bridge_post_disable,
 };
 
+static struct ti_sn_bridge *aux_to_ti_sn_bridge(struct drm_dp_aux *aux)
+{
+       return container_of(aux, struct ti_sn_bridge, aux);
+}
+
+static bool ti_sn_cmd_done(struct ti_sn_bridge *pdata)
+{
+       int ret;
+       unsigned int val;
+
+       ret = regmap_read(pdata->regmap, SN_AUX_CMD_REG, &val);
+       if (ret) {
+               DRM_ERROR("Error returned reading AUX_CMD_REG (%d)\n", ret);
+               return true;
+       }
+
+       return !(val & AUX_CMD_SEND);
+}
+
+static ssize_t ti_sn_aux_transfer(struct drm_dp_aux *aux,
+                                 struct drm_dp_aux_msg *msg)
+{
+       struct ti_sn_bridge *pdata = aux_to_ti_sn_bridge(aux);
+       u32 request = msg->request & ~DP_AUX_I2C_MOT;
+       u32 request_val = AUX_CMD_REQ(msg->request);
+       u8 *buf = (u8 *)msg->buffer;
+       unsigned int val;
+       bool cmd_done;
+       int ret, i;
+
+       if (msg->size > SN_AUX_MAX_PAYLOAD_BYTES)
+               return -EINVAL;
+
+       switch (request) {
+       case DP_AUX_NATIVE_WRITE:
+       case DP_AUX_I2C_WRITE:
+       case DP_AUX_NATIVE_READ:
+       case DP_AUX_I2C_READ:
+               regmap_write(pdata->regmap, SN_AUX_CMD_REG, request_val);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       regmap_write(pdata->regmap, SN_AUX_ADDR_19_16_REG,
+                    (msg->address >> 16) & 0xF);
+       regmap_write(pdata->regmap, SN_AUX_ADDR_15_8_REG,
+                    (msg->address >> 8) & 0xFF);
+       regmap_write(pdata->regmap, SN_AUX_ADDR_7_0_REG, msg->address & 0xFF);
+
+       regmap_write(pdata->regmap, SN_AUX_LENGTH_REG, msg->size);
+
+       if (request == DP_AUX_NATIVE_WRITE || request == DP_AUX_I2C_WRITE) {
+               for (i = 0; i < msg->size; i++)
+                       regmap_write(pdata->regmap, SN_AUX_WDATA_REG(i),
+                                    buf[i]);
+       }
+
+       regmap_write(pdata->regmap, SN_AUX_CMD_REG, request_val | AUX_CMD_SEND);
+
+       ret = readx_poll_timeout(ti_sn_cmd_done, pdata, cmd_done, cmd_done,
+                                200, 50 * 1000);
+       if (ret)
+               return ret;
+
+       ret = regmap_read(pdata->regmap, SN_AUX_CMD_STATUS_REG, &val);
+       if (ret)
+               return ret;
+       else if ((val & AUX_IRQ_STATUS_NAT_I2C_FAIL)
+                || (val & AUX_IRQ_STATUS_AUX_RPLY_TOUT)
+                || (val & AUX_IRQ_STATUS_AUX_SHORT))
+               return -ENXIO;
+
+       if (request == DP_AUX_NATIVE_WRITE || request == DP_AUX_I2C_WRITE)
+               return msg->size;
+
+       for (i = 0; i < msg->size; i++) {
+               unsigned int val;
+               ret = regmap_read(pdata->regmap, SN_AUX_RDATA_REG(i),
+                                 &val);
+               if (ret)
+                       return ret;
+
+               WARN_ON(val & ~0xFF);
+               buf[i] = (u8)(val & 0xFF);
+       }
+
+       return msg->size;
+}
+
 static int ti_sn_bridge_parse_dsi_host(struct ti_sn_bridge *pdata)
 {
        struct device_node *np = pdata->dev->of_node;
@@ -606,6 +704,11 @@ static int ti_sn_bridge_probe(struct i2c_client *client,
 
        i2c_set_clientdata(client, pdata);
 
+       pdata->aux.name = "ti-sn65dsi86-aux";
+       pdata->aux.dev = pdata->dev;
+       pdata->aux.transfer = ti_sn_aux_transfer;
+       drm_dp_aux_register(&pdata->aux);
+
        pdata->bridge.funcs = &ti_sn_bridge_funcs;
        pdata->bridge.of_node = client->dev.of_node;
 
-- 
Sean Paul, Software Engineer, Google / Chromium OS

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

Reply via email to