<div dir="auto"><div><br><br><div class="gmail_quote"><div dir="ltr">On Wed, Aug 8, 2018, 5:53 PM Sean Paul <<a href="mailto:seanpaul@chromium.org">seanpaul@chromium.org</a>> wrote:<br></div><div dir="ltr"><br></div><div dir="ltr">Just realized I put drm/panel in the subject, should be drm/bridge. </div><div dir="ltr"><br></div><div dir="ltr">Sean</div><div dir="ltr"><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">This was hand-rolled in the first version, and will surely be useful as<br>
we expand the driver to support more varied use cases.<br>
<br>
Cc: Sandeep Panda <<a href="mailto:spanda@codeaurora.org" target="_blank" rel="noreferrer">spanda@codeaurora.org</a>><br>
Signed-off-by: Sean Paul <<a href="mailto:seanpaul@chromium.org" target="_blank" rel="noreferrer">seanpaul@chromium.org</a>><br>
---<br>
drivers/gpu/drm/bridge/ti-sn65dsi86.c | 107 ++++++++++++++++++++++++--<br>
1 file changed, 100 insertions(+), 7 deletions(-)<br>
<br>
diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c<br>
index 1b6e8b72be58..50aa8c3c39fc 100644<br>
--- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c<br>
+++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c<br>
@@ -7,12 +7,14 @@<br>
#include <drm/drm_atomic.h><br>
#include <drm/drm_atomic_helper.h><br>
#include <drm/drm_crtc_helper.h><br>
+#include <drm/drm_dp_helper.h><br>
#include <drm/drm_mipi_dsi.h><br>
#include <drm/drm_of.h><br>
#include <drm/drm_panel.h><br>
#include <linux/clk.h><br>
#include <linux/gpio/consumer.h><br>
#include <linux/i2c.h><br>
+#include <linux/iopoll.h><br>
#include <linux/of_graph.h><br>
#include <linux/pm_runtime.h><br>
#include <linux/regmap.h><br>
@@ -29,12 +31,15 @@<br>
#define SN_DATARATE_CONFIG_REG 0x94<br>
#define SN_PLL_ENABLE_REG 0x0D<br>
#define SN_SCRAMBLE_CONFIG_REG 0x95<br>
-#define SN_AUX_WDATA0_REG 0x64<br>
+#define SN_AUX_WDATA_REG(x) (0x64 + x)<br>
#define SN_AUX_ADDR_19_16_REG 0x74<br>
#define SN_AUX_ADDR_15_8_REG 0x75<br>
#define SN_AUX_ADDR_7_0_REG 0x76<br>
#define SN_AUX_LENGTH_REG 0x77<br>
#define SN_AUX_CMD_REG 0x78<br>
+#define AUX_CMD_SEND 0x01<br>
+#define AUX_CMD_REQ(x) (x << 4)<br>
+#define SN_AUX_RDATA_REG(x) (0x79 + x)<br>
#define SN_ML_TX_MODE_REG 0x96<br>
/* video config specific registers */<br>
#define SN_CHA_ACTIVE_LINE_LENGTH_LOW_REG 0x20<br>
@@ -64,6 +69,9 @@<br>
#define SN_DP_DATA_RATE_OFFSET 5<br>
#define SN_SYNC_POLARITY_OFFSET 7<br>
<br>
+/* Matches DP_AUX_MAX_PAYLOAD_BYTES (for now) */<br>
+#define SN_AUX_MAX_PAYLOAD_BYTES 16<br>
+<br>
#define SN_ENABLE_VID_STREAM_BIT BIT(3)<br>
#define SN_REFCLK_FREQ_BITS GENMASK(3, 1)<br>
#define SN_DSIA_NUM_LANES_BITS GENMASK(4, 3)<br>
@@ -76,6 +84,7 @@<br>
struct ti_sn_bridge {<br>
struct device *dev;<br>
struct regmap *regmap;<br>
+ struct drm_dp_aux aux;<br>
struct drm_bridge bridge;<br>
struct drm_connector connector;<br>
struct device_node *host_node;<br>
@@ -473,12 +482,7 @@ static void ti_sn_bridge_enable(struct drm_bridge *bridge)<br>
* authentication method. We need to enable this method in the eDP panel<br>
* at DisplayPort address 0x0010A prior to link training.<br>
*/<br>
- regmap_write(pdata->regmap, SN_AUX_WDATA0_REG, 0x01);<br>
- regmap_write(pdata->regmap, SN_AUX_ADDR_19_16_REG, 0x00);<br>
- regmap_write(pdata->regmap, SN_AUX_ADDR_15_8_REG, 0x01);<br>
- regmap_write(pdata->regmap, SN_AUX_ADDR_7_0_REG, 0x0A);<br>
- regmap_write(pdata->regmap, SN_AUX_LENGTH_REG, 0x01);<br>
- regmap_write(pdata->regmap, SN_AUX_CMD_REG, 0x81);<br>
+ drm_dp_dpcd_writeb(&pdata->aux, DP_EDP_CONFIGURATION_SET, 0);<br>
usleep_range(10000, 10500); /* 10ms delay recommended by spec */<br>
<br>
/* Semi auto link training mode */<br>
@@ -527,6 +531,90 @@ static const struct drm_bridge_funcs ti_sn_bridge_funcs = {<br>
.post_disable = ti_sn_bridge_post_disable,<br>
};<br>
<br>
+static struct ti_sn_bridge *aux_to_ti_sn_bridge(struct drm_dp_aux *aux)<br>
+{<br>
+ return container_of(aux, struct ti_sn_bridge, aux);<br>
+}<br>
+<br>
+static bool ti_sn_cmd_done(struct ti_sn_bridge *pdata)<br>
+{<br>
+ int ret;<br>
+ unsigned int val;<br>
+<br>
+ ret = regmap_read(pdata->regmap, SN_AUX_CMD_REG, &val);<br>
+ WARN_ON(ret);<br>
+ return ret || !(val & AUX_CMD_SEND);<br>
+}<br>
+<br>
+static ssize_t ti_sn_aux_transfer(struct drm_dp_aux *aux,<br>
+ struct drm_dp_aux_msg *msg)<br>
+{<br>
+ struct ti_sn_bridge *pdata = aux_to_ti_sn_bridge(aux);<br>
+ u32 request = msg->request & ~DP_AUX_I2C_MOT;<br>
+ u32 request_val = AUX_CMD_REQ(msg->request);<br>
+ u8 *buf = (u8 *)msg->buffer;<br>
+ bool cmd_done;<br>
+ int ret, i;<br>
+<br>
+ if (msg->size > SN_AUX_MAX_PAYLOAD_BYTES)<br>
+ return -EINVAL;<br>
+<br>
+ switch (request) {<br>
+ case DP_AUX_NATIVE_WRITE:<br>
+ case DP_AUX_I2C_WRITE:<br>
+ case DP_AUX_NATIVE_READ:<br>
+ case DP_AUX_I2C_READ:<br>
+ regmap_write(pdata->regmap, SN_AUX_CMD_REG, request_val);<br>
+ break;<br>
+ default:<br>
+ return -EINVAL;<br>
+ }<br>
+<br>
+ regmap_write(pdata->regmap, SN_AUX_ADDR_19_16_REG,<br>
+ (msg->address >> 16) & 0xFF);<br>
+ regmap_write(pdata->regmap, SN_AUX_ADDR_15_8_REG,<br>
+ (msg->address >> 8) & 0xFF);<br>
+ regmap_write(pdata->regmap, SN_AUX_ADDR_7_0_REG, msg->address & 0xFF);<br>
+<br>
+ regmap_write(pdata->regmap, SN_AUX_LENGTH_REG, msg->size);<br>
+<br>
+ if (request == DP_AUX_NATIVE_WRITE || request == DP_AUX_I2C_WRITE) {<br>
+ for (i = 0; i < msg->size; i++)<br>
+ regmap_write(pdata->regmap, SN_AUX_WDATA_REG(i),<br>
+ buf[i]);<br>
+ }<br>
+<br>
+ regmap_write(pdata->regmap, SN_AUX_CMD_REG, request_val | AUX_CMD_SEND);<br>
+<br>
+ ret = readx_poll_timeout(ti_sn_cmd_done, pdata, cmd_done, cmd_done,<br>
+ 200, 50 * 1000);<br>
+ if (ret)<br>
+ return ret;<br>
+<br>
+ if (request == DP_AUX_NATIVE_READ || request == DP_AUX_I2C_READ) {<br>
+ unsigned int val;<br>
+<br>
+ ret = regmap_read(pdata->regmap, SN_AUX_LENGTH_REG, &val);<br>
+ if (ret)<br>
+ return ret;<br>
+ else if (val != msg->size)<br>
+ return -ENXIO;<br>
+<br>
+ for (i = 0; i < msg->size; i++) {<br>
+ unsigned int val;<br>
+ ret = regmap_read(pdata->regmap, SN_AUX_RDATA_REG(i),<br>
+ &val);<br>
+ if (ret)<br>
+ return ret;<br>
+<br>
+ WARN_ON(val & ~0xFF);<br>
+ buf[i] = (u8)(val & 0xFF);<br>
+ }<br>
+ }<br>
+<br>
+ return msg->size;<br>
+}<br>
+<br>
static int ti_sn_bridge_parse_dsi_host(struct ti_sn_bridge *pdata)<br>
{<br>
struct device_node *np = pdata->dev->of_node;<br>
@@ -606,6 +694,11 @@ static int ti_sn_bridge_probe(struct i2c_client *client,<br>
<br>
i2c_set_clientdata(client, pdata);<br>
<br>
+ pdata-><a href="http://aux.name" rel="noreferrer noreferrer" target="_blank">aux.name</a> = "ti-sn65dsi86-aux";<br>
+ pdata->aux.dev = pdata->dev;<br>
+ pdata->aux.transfer = ti_sn_aux_transfer;<br>
+ drm_dp_aux_register(&pdata->aux);<br>
+<br>
pdata->bridge.funcs = &ti_sn_bridge_funcs;<br>
pdata->bridge.of_node = client->dev.of_node;<br>
<br>
-- <br>
Sean Paul, Software Engineer, Google / Chromium OS<br>
<br>
</blockquote></div></div></div>