[PATCH v3 3/7] drm/bridge: ti-sn65dsi86: Implement AUX channel
spanda at codeaurora.org
spanda at codeaurora.org
Tue Aug 14 11:06:49 UTC 2018
On 2018-08-14 03:00, Sean Paul wrote:
> From: Sean Paul <seanpaul at chromium.org>
>
> 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
> Changes in v3:
> - Added to the set
> - Wrapped (x) in WDATA/RDATA #defines
> - Replace readx_poll* with regmap_read_poll*
>
> Cc: Sandeep Panda <spanda at codeaurora.org>
> Signed-off-by: Sean Paul <seanpaul at chromium.org>
> ---
> drivers/gpu/drm/bridge/ti-sn65dsi86.c | 103 ++++++++++++++++++++++++--
> 1 file changed, 95 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c
> b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
> index 587d4e4f5674c..501f4a81ea5ab 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>
> @@ -46,7 +48,7 @@
> #define SN_DATA_FORMAT_REG 0x5B
> #define SN_HPD_DISABLE_REG 0x5C
> #define HPD_DISABLE BIT(0)
> -#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
> @@ -54,6 +56,7 @@
> #define SN_AUX_CMD_REG 0x78
> #define AUX_CMD_SEND BIT(1)
> #define AUX_CMD_REQ(x) ((x) << 4)
> +#define SN_AUX_RDATA_REG(x) (0x79 + (x))
> #define SN_SSC_CONFIG_REG 0x93
> #define DP_NUM_LANES_MASK GENMASK(5, 4)
> #define DP_NUM_LANES(x) ((x) << 4)
> @@ -63,6 +66,10 @@
> #define SN_ML_TX_MODE_REG 0x96
> #define ML_TX_MAIN_LINK_OFF 0
> #define ML_TX_NORMAL_MODE BIT(0)
> +#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
>
> @@ -70,11 +77,15 @@
> #define DP_CLK_FUDGE_NUM 10
> #define DP_CLK_FUDGE_DEN 8
>
> +/* Matches DP_AUX_MAX_PAYLOAD_BYTES (for now) */
> +#define SN_AUX_MAX_PAYLOAD_BYTES 16
> +
> #define SN_REGULATOR_SUPPLY_NUM 4
>
> 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;
> @@ -471,13 +482,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);
> @@ -525,6 +531,82 @@ 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 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;
> + 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 = regmap_read_poll_timeout(pdata->regmap, SN_AUX_CMD_REG, val,
> + !(val & AUX_CMD_SEND), 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;
> @@ -604,6 +686,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;
Reviewed-by: Sandeep Panda <spanda at codeaurora.org>
More information about the dri-devel
mailing list