drm: Remove unnecessary drm_panel_attach and drm_panel_detach
[linux-2.6-microblaze.git] / drivers / gpu / drm / bridge / ti-sn65dsi86.c
index 9a2dd98..454544e 100644 (file)
@@ -1,12 +1,14 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (c) 2018, The Linux Foundation. All rights reserved.
- * datasheet: http://www.ti.com/lit/ds/symlink/sn65dsi86.pdf
+ * datasheet: https://www.ti.com/lit/ds/symlink/sn65dsi86.pdf
  */
 
+#include <linux/bits.h>
 #include <linux/clk.h>
 #include <linux/debugfs.h>
 #include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
 #include <linux/i2c.h>
 #include <linux/iopoll.h>
 #include <linux/module.h>
 #define SN_CHA_VERTICAL_BACK_PORCH_REG         0x36
 #define SN_CHA_HORIZONTAL_FRONT_PORCH_REG      0x38
 #define SN_CHA_VERTICAL_FRONT_PORCH_REG                0x3A
+#define SN_LN_ASSIGN_REG                       0x59
+#define  LN_ASSIGN_WIDTH                       2
 #define SN_ENH_FRAME_REG                       0x5A
 #define  VSTREAM_ENABLE                                BIT(3)
+#define  LN_POLRS_OFFSET                       4
+#define  LN_POLRS_MASK                         0xf0
 #define SN_DATA_FORMAT_REG                     0x5B
+#define  BPP_18_RGB                            BIT(0)
 #define SN_HPD_DISABLE_REG                     0x5C
 #define  HPD_DISABLE                           BIT(0)
+#define SN_GPIO_IO_REG                         0x5E
+#define  SN_GPIO_INPUT_SHIFT                   4
+#define  SN_GPIO_OUTPUT_SHIFT                  0
+#define SN_GPIO_CTRL_REG                       0x5F
+#define  SN_GPIO_MUX_INPUT                     0
+#define  SN_GPIO_MUX_OUTPUT                    1
+#define  SN_GPIO_MUX_SPECIAL                   2
+#define  SN_GPIO_MUX_MASK                      0x3
 #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_REGULATOR_SUPPLY_NUM                4
 
+#define SN_MAX_DP_LANES                        4
+#define SN_NUM_GPIOS                   4
+#define SN_GPIO_PHYSICAL_OFFSET                1
+
+/**
+ * struct ti_sn_bridge - Platform data for ti-sn65dsi86 driver.
+ * @dev:          Pointer to our device.
+ * @regmap:       Regmap for accessing i2c.
+ * @aux:          Our aux channel.
+ * @bridge:       Our bridge.
+ * @connector:    Our connector.
+ * @debugfs:      Used for managing our debugfs.
+ * @host_node:    Remote DSI node.
+ * @dsi:          Our MIPI DSI source.
+ * @refclk:       Our reference clock.
+ * @panel:        Our panel.
+ * @enable_gpio:  The GPIO we toggle to enable the bridge.
+ * @supplies:     Data for bulk enabling/disabling our regulators.
+ * @dp_lanes:     Count of dp_lanes we're using.
+ * @ln_assign:    Value to program to the LN_ASSIGN register.
+ * @ln_polrs:     Value for the 4-bit LN_POLRS field of SN_ENH_FRAME_REG.
+ *
+ * @gchip:        If we expose our GPIOs, this is used.
+ * @gchip_output: A cache of whether we've set GPIOs to output.  This
+ *                serves double-duty of keeping track of the direction and
+ *                also keeping track of whether we've incremented the
+ *                pm_runtime reference count for this pin, which we do
+ *                whenever a pin is configured as an output.  This is a
+ *                bitmap so we can do atomic ops on it without an extra
+ *                lock so concurrent users of our 4 GPIOs don't stomp on
+ *                each other's read-modify-write.
+ */
 struct ti_sn_bridge {
        struct device                   *dev;
        struct regmap                   *regmap;
@@ -100,6 +147,14 @@ struct ti_sn_bridge {
        struct drm_panel                *panel;
        struct gpio_desc                *enable_gpio;
        struct regulator_bulk_data      supplies[SN_REGULATOR_SUPPLY_NUM];
+       int                             dp_lanes;
+       u8                              ln_assign;
+       u8                              ln_polrs;
+
+#if defined(CONFIG_OF_GPIO)
+       struct gpio_chip                gchip;
+       DECLARE_BITMAP(gchip_output, SN_NUM_GPIOS);
+#endif
 };
 
 static const struct regmap_range ti_sn_bridge_volatile_ranges[] = {
@@ -157,6 +212,8 @@ static int __maybe_unused ti_sn_bridge_suspend(struct device *dev)
 
 static const struct dev_pm_ops ti_sn_bridge_pm_ops = {
        SET_RUNTIME_PM_OPS(ti_sn_bridge_suspend, ti_sn_bridge_resume, NULL)
+       SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+                               pm_runtime_force_resume)
 };
 
 static int status_show(struct seq_file *s, void *data)
@@ -264,7 +321,8 @@ static int ti_sn_bridge_parse_regulators(struct ti_sn_bridge *pdata)
                                       pdata->supplies);
 }
 
-static int ti_sn_bridge_attach(struct drm_bridge *bridge)
+static int ti_sn_bridge_attach(struct drm_bridge *bridge,
+                              enum drm_bridge_attach_flags flags)
 {
        int ret, val;
        struct ti_sn_bridge *pdata = bridge_to_ti_sn_bridge(bridge);
@@ -275,6 +333,11 @@ static int ti_sn_bridge_attach(struct drm_bridge *bridge)
                                                   .node = NULL,
                                                 };
 
+       if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
+               DRM_ERROR("Fix bridge driver to make connector optional!");
+               return -EINVAL;
+       }
+
        ret = drm_connector_init(bridge->dev, &pdata->connector,
                                 &ti_sn_bridge_connector_funcs,
                                 DRM_MODE_CONNECTOR_eDP);
@@ -312,7 +375,7 @@ static int ti_sn_bridge_attach(struct drm_bridge *bridge)
                goto err_dsi_host;
        }
 
-       /* TODO: setting to 4 lanes always for now */
+       /* TODO: setting to 4 MIPI lanes always for now */
        dsi->lanes = 4;
        dsi->format = MIPI_DSI_FMT_RGB888;
        dsi->mode_flags = MIPI_DSI_MODE_VIDEO;
@@ -331,9 +394,6 @@ static int ti_sn_bridge_attach(struct drm_bridge *bridge)
        }
        pdata->dsi = dsi;
 
-       /* attach panel to bridge */
-       drm_panel_attach(pdata->panel, &pdata->connector);
-
        return 0;
 
 err_dsi_attach:
@@ -417,19 +477,10 @@ static void ti_sn_bridge_set_refclk_freq(struct ti_sn_bridge *pdata)
                           REFCLK_FREQ(i));
 }
 
-/**
- * LUT index corresponds to register value and
- * LUT values corresponds to dp data rate supported
- * by the bridge in Mbps unit.
- */
-static const unsigned int ti_sn_bridge_dp_rate_lut[] = {
-       0, 1620, 2160, 2430, 2700, 3240, 4320, 5400
-};
-
-static void ti_sn_bridge_set_dsi_dp_rate(struct ti_sn_bridge *pdata)
+static void ti_sn_bridge_set_dsi_rate(struct ti_sn_bridge *pdata)
 {
-       unsigned int bit_rate_mhz, clk_freq_mhz, dp_rate_mhz;
-       unsigned int val, i;
+       unsigned int bit_rate_mhz, clk_freq_mhz;
+       unsigned int val;
        struct drm_display_mode *mode =
                &pdata->bridge.encoder->crtc->state->adjusted_mode;
 
@@ -442,16 +493,125 @@ static void ti_sn_bridge_set_dsi_dp_rate(struct ti_sn_bridge *pdata)
        val = (MIN_DSI_CLK_FREQ_MHZ / 5) +
                (((clk_freq_mhz - MIN_DSI_CLK_FREQ_MHZ) / 5) & 0xFF);
        regmap_write(pdata->regmap, SN_DSIA_CLK_FREQ_REG, val);
+}
+
+static unsigned int ti_sn_bridge_get_bpp(struct ti_sn_bridge *pdata)
+{
+       if (pdata->connector.display_info.bpc <= 6)
+               return 18;
+       else
+               return 24;
+}
 
-       /* set DP data rate */
-       dp_rate_mhz = ((bit_rate_mhz / pdata->dsi->lanes) * DP_CLK_FUDGE_NUM) /
-                                                       DP_CLK_FUDGE_DEN;
-       for (i = 0; i < ARRAY_SIZE(ti_sn_bridge_dp_rate_lut) - 1; i++)
-               if (ti_sn_bridge_dp_rate_lut[i] > dp_rate_mhz)
+/*
+ * LUT index corresponds to register value and
+ * LUT values corresponds to dp data rate supported
+ * by the bridge in Mbps unit.
+ */
+static const unsigned int ti_sn_bridge_dp_rate_lut[] = {
+       0, 1620, 2160, 2430, 2700, 3240, 4320, 5400
+};
+
+static int ti_sn_bridge_calc_min_dp_rate_idx(struct ti_sn_bridge *pdata)
+{
+       unsigned int bit_rate_khz, dp_rate_mhz;
+       unsigned int i;
+       struct drm_display_mode *mode =
+               &pdata->bridge.encoder->crtc->state->adjusted_mode;
+
+       /* Calculate minimum bit rate based on our pixel clock. */
+       bit_rate_khz = mode->clock * ti_sn_bridge_get_bpp(pdata);
+
+       /* Calculate minimum DP data rate, taking 80% as per DP spec */
+       dp_rate_mhz = DIV_ROUND_UP(bit_rate_khz * DP_CLK_FUDGE_NUM,
+                                  1000 * pdata->dp_lanes * DP_CLK_FUDGE_DEN);
+
+       for (i = 1; i < ARRAY_SIZE(ti_sn_bridge_dp_rate_lut) - 1; i++)
+               if (ti_sn_bridge_dp_rate_lut[i] >= dp_rate_mhz)
                        break;
 
-       regmap_update_bits(pdata->regmap, SN_DATARATE_CONFIG_REG,
-                          DP_DATARATE_MASK, DP_DATARATE(i));
+       return i;
+}
+
+static void ti_sn_bridge_read_valid_rates(struct ti_sn_bridge *pdata,
+                                         bool rate_valid[])
+{
+       unsigned int rate_per_200khz;
+       unsigned int rate_mhz;
+       u8 dpcd_val;
+       int ret;
+       int i, j;
+
+       ret = drm_dp_dpcd_readb(&pdata->aux, DP_EDP_DPCD_REV, &dpcd_val);
+       if (ret != 1) {
+               DRM_DEV_ERROR(pdata->dev,
+                             "Can't read eDP rev (%d), assuming 1.1\n", ret);
+               dpcd_val = DP_EDP_11;
+       }
+
+       if (dpcd_val >= DP_EDP_14) {
+               /* eDP 1.4 devices must provide a custom table */
+               __le16 sink_rates[DP_MAX_SUPPORTED_RATES];
+
+               ret = drm_dp_dpcd_read(&pdata->aux, DP_SUPPORTED_LINK_RATES,
+                                      sink_rates, sizeof(sink_rates));
+
+               if (ret != sizeof(sink_rates)) {
+                       DRM_DEV_ERROR(pdata->dev,
+                               "Can't read supported rate table (%d)\n", ret);
+
+                       /* By zeroing we'll fall back to DP_MAX_LINK_RATE. */
+                       memset(sink_rates, 0, sizeof(sink_rates));
+               }
+
+               for (i = 0; i < ARRAY_SIZE(sink_rates); i++) {
+                       rate_per_200khz = le16_to_cpu(sink_rates[i]);
+
+                       if (!rate_per_200khz)
+                               break;
+
+                       rate_mhz = rate_per_200khz * 200 / 1000;
+                       for (j = 0;
+                            j < ARRAY_SIZE(ti_sn_bridge_dp_rate_lut);
+                            j++) {
+                               if (ti_sn_bridge_dp_rate_lut[j] == rate_mhz)
+                                       rate_valid[j] = true;
+                       }
+               }
+
+               for (i = 0; i < ARRAY_SIZE(ti_sn_bridge_dp_rate_lut); i++) {
+                       if (rate_valid[i])
+                               return;
+               }
+               DRM_DEV_ERROR(pdata->dev,
+                             "No matching eDP rates in table; falling back\n");
+       }
+
+       /* On older versions best we can do is use DP_MAX_LINK_RATE */
+       ret = drm_dp_dpcd_readb(&pdata->aux, DP_MAX_LINK_RATE, &dpcd_val);
+       if (ret != 1) {
+               DRM_DEV_ERROR(pdata->dev,
+                             "Can't read max rate (%d); assuming 5.4 GHz\n",
+                             ret);
+               dpcd_val = DP_LINK_BW_5_4;
+       }
+
+       switch (dpcd_val) {
+       default:
+               DRM_DEV_ERROR(pdata->dev,
+                             "Unexpected max rate (%#x); assuming 5.4 GHz\n",
+                             (int)dpcd_val);
+               /* fall through */
+       case DP_LINK_BW_5_4:
+               rate_valid[7] = 1;
+               /* fall through */
+       case DP_LINK_BW_2_7:
+               rate_valid[4] = 1;
+               /* fall through */
+       case DP_LINK_BW_1_62:
+               rate_valid[1] = 1;
+               break;
+       }
 }
 
 static void ti_sn_bridge_set_video_timings(struct ti_sn_bridge *pdata)
@@ -493,24 +653,30 @@ static void ti_sn_bridge_set_video_timings(struct ti_sn_bridge *pdata)
        usleep_range(10000, 10500); /* 10ms delay recommended by spec */
 }
 
-static void ti_sn_bridge_enable(struct drm_bridge *bridge)
+static unsigned int ti_sn_get_max_lanes(struct ti_sn_bridge *pdata)
 {
-       struct ti_sn_bridge *pdata = bridge_to_ti_sn_bridge(bridge);
-       unsigned int val;
+       u8 data;
        int ret;
 
-       /* DSI_A lane config */
-       val = CHA_DSI_LANES(4 - pdata->dsi->lanes);
-       regmap_update_bits(pdata->regmap, SN_DSI_LANES_REG,
-                          CHA_DSI_LANES_MASK, val);
+       ret = drm_dp_dpcd_readb(&pdata->aux, DP_MAX_LANE_COUNT, &data);
+       if (ret != 1) {
+               DRM_DEV_ERROR(pdata->dev,
+                             "Can't read lane count (%d); assuming 4\n", ret);
+               return 4;
+       }
 
-       /* DP lane config */
-       val = DP_NUM_LANES(pdata->dsi->lanes - 1);
-       regmap_update_bits(pdata->regmap, SN_SSC_CONFIG_REG, DP_NUM_LANES_MASK,
-                          val);
+       return data & DP_LANE_COUNT_MASK;
+}
 
-       /* set dsi/dp clk frequency value */
-       ti_sn_bridge_set_dsi_dp_rate(pdata);
+static int ti_sn_link_training(struct ti_sn_bridge *pdata, int dp_rate_idx,
+                              const char **last_err_str)
+{
+       unsigned int val;
+       int ret;
+
+       /* set dp clk frequency value */
+       regmap_update_bits(pdata->regmap, SN_DATARATE_CONFIG_REG,
+                          DP_DATARATE_MASK, DP_DATARATE(dp_rate_idx));
 
        /* enable DP PLL */
        regmap_write(pdata->regmap, SN_PLL_ENABLE_REG, 1);
@@ -519,10 +685,56 @@ static void ti_sn_bridge_enable(struct drm_bridge *bridge)
                                       val & DPPLL_SRC_DP_PLL_LOCK, 1000,
                                       50 * 1000);
        if (ret) {
-               DRM_ERROR("DP_PLL_LOCK polling failed (%d)\n", ret);
-               return;
+               *last_err_str = "DP_PLL_LOCK polling failed";
+               goto exit;
        }
 
+       /* Semi auto link training mode */
+       regmap_write(pdata->regmap, SN_ML_TX_MODE_REG, 0x0A);
+       ret = regmap_read_poll_timeout(pdata->regmap, SN_ML_TX_MODE_REG, val,
+                                      val == ML_TX_MAIN_LINK_OFF ||
+                                      val == ML_TX_NORMAL_MODE, 1000,
+                                      500 * 1000);
+       if (ret) {
+               *last_err_str = "Training complete polling failed";
+       } else if (val == ML_TX_MAIN_LINK_OFF) {
+               *last_err_str = "Link training failed, link is off";
+               ret = -EIO;
+       }
+
+exit:
+       /* Disable the PLL if we failed */
+       if (ret)
+               regmap_write(pdata->regmap, SN_PLL_ENABLE_REG, 0);
+
+       return ret;
+}
+
+static void ti_sn_bridge_enable(struct drm_bridge *bridge)
+{
+       struct ti_sn_bridge *pdata = bridge_to_ti_sn_bridge(bridge);
+       bool rate_valid[ARRAY_SIZE(ti_sn_bridge_dp_rate_lut)] = { };
+       const char *last_err_str = "No supported DP rate";
+       int dp_rate_idx;
+       unsigned int val;
+       int ret = -EINVAL;
+       int max_dp_lanes;
+
+       max_dp_lanes = ti_sn_get_max_lanes(pdata);
+       pdata->dp_lanes = min(pdata->dp_lanes, max_dp_lanes);
+
+       /* DSI_A lane config */
+       val = CHA_DSI_LANES(SN_MAX_DP_LANES - pdata->dsi->lanes);
+       regmap_update_bits(pdata->regmap, SN_DSI_LANES_REG,
+                          CHA_DSI_LANES_MASK, val);
+
+       regmap_write(pdata->regmap, SN_LN_ASSIGN_REG, pdata->ln_assign);
+       regmap_update_bits(pdata->regmap, SN_ENH_FRAME_REG, LN_POLRS_MASK,
+                          pdata->ln_polrs << LN_POLRS_OFFSET);
+
+       /* set dsi clk frequency value */
+       ti_sn_bridge_set_dsi_rate(pdata);
+
        /**
         * The SN65DSI86 only supports ASSR Display Authentication method and
         * this method is enabled by default. An eDP panel must support this
@@ -532,17 +744,30 @@ static void ti_sn_bridge_enable(struct drm_bridge *bridge)
        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);
-       ret = regmap_read_poll_timeout(pdata->regmap, SN_ML_TX_MODE_REG, val,
-                                      val == ML_TX_MAIN_LINK_OFF ||
-                                      val == ML_TX_NORMAL_MODE, 1000,
-                                      500 * 1000);
+       /* Set the DP output format (18 bpp or 24 bpp) */
+       val = (ti_sn_bridge_get_bpp(pdata) == 18) ? BPP_18_RGB : 0;
+       regmap_update_bits(pdata->regmap, SN_DATA_FORMAT_REG, BPP_18_RGB, val);
+
+       /* DP lane config */
+       val = DP_NUM_LANES(min(pdata->dp_lanes, 3));
+       regmap_update_bits(pdata->regmap, SN_SSC_CONFIG_REG, DP_NUM_LANES_MASK,
+                          val);
+
+       ti_sn_bridge_read_valid_rates(pdata, rate_valid);
+
+       /* Train until we run out of rates */
+       for (dp_rate_idx = ti_sn_bridge_calc_min_dp_rate_idx(pdata);
+            dp_rate_idx < ARRAY_SIZE(ti_sn_bridge_dp_rate_lut);
+            dp_rate_idx++) {
+               if (!rate_valid[dp_rate_idx])
+                       continue;
+
+               ret = ti_sn_link_training(pdata, dp_rate_idx, &last_err_str);
+               if (!ret)
+                       break;
+       }
        if (ret) {
-               DRM_ERROR("Training complete polling failed (%d)\n", ret);
-               return;
-       } else if (val == ML_TX_MAIN_LINK_OFF) {
-               DRM_ERROR("Link training failed, link is off\n");
+               DRM_DEV_ERROR(pdata->dev, "%s (%d)\n", last_err_str, ret);
                return;
        }
 
@@ -648,6 +873,12 @@ static ssize_t ti_sn_aux_transfer(struct drm_dp_aux *aux,
                                     buf[i]);
        }
 
+       /* Clear old status bits before start so we don't get confused */
+       regmap_write(pdata->regmap, SN_AUX_CMD_STATUS_REG,
+                    AUX_IRQ_STATUS_NAT_I2C_FAIL |
+                    AUX_IRQ_STATUS_AUX_RPLY_TOUT |
+                    AUX_IRQ_STATUS_AUX_SHORT);
+
        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,
@@ -695,6 +926,236 @@ static int ti_sn_bridge_parse_dsi_host(struct ti_sn_bridge *pdata)
        return 0;
 }
 
+#if defined(CONFIG_OF_GPIO)
+
+static int tn_sn_bridge_of_xlate(struct gpio_chip *chip,
+                                const struct of_phandle_args *gpiospec,
+                                u32 *flags)
+{
+       if (WARN_ON(gpiospec->args_count < chip->of_gpio_n_cells))
+               return -EINVAL;
+
+       if (gpiospec->args[0] > chip->ngpio || gpiospec->args[0] < 1)
+               return -EINVAL;
+
+       if (flags)
+               *flags = gpiospec->args[1];
+
+       return gpiospec->args[0] - SN_GPIO_PHYSICAL_OFFSET;
+}
+
+static int ti_sn_bridge_gpio_get_direction(struct gpio_chip *chip,
+                                          unsigned int offset)
+{
+       struct ti_sn_bridge *pdata = gpiochip_get_data(chip);
+
+       /*
+        * We already have to keep track of the direction because we use
+        * that to figure out whether we've powered the device.  We can
+        * just return that rather than (maybe) powering up the device
+        * to ask its direction.
+        */
+       return test_bit(offset, pdata->gchip_output) ?
+               GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
+}
+
+static int ti_sn_bridge_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+       struct ti_sn_bridge *pdata = gpiochip_get_data(chip);
+       unsigned int val;
+       int ret;
+
+       /*
+        * When the pin is an input we don't forcibly keep the bridge
+        * powered--we just power it on to read the pin.  NOTE: part of
+        * the reason this works is that the bridge defaults (when
+        * powered back on) to all 4 GPIOs being configured as GPIO input.
+        * Also note that if something else is keeping the chip powered the
+        * pm_runtime functions are lightweight increments of a refcount.
+        */
+       pm_runtime_get_sync(pdata->dev);
+       ret = regmap_read(pdata->regmap, SN_GPIO_IO_REG, &val);
+       pm_runtime_put(pdata->dev);
+
+       if (ret)
+               return ret;
+
+       return !!(val & BIT(SN_GPIO_INPUT_SHIFT + offset));
+}
+
+static void ti_sn_bridge_gpio_set(struct gpio_chip *chip, unsigned int offset,
+                                 int val)
+{
+       struct ti_sn_bridge *pdata = gpiochip_get_data(chip);
+       int ret;
+
+       if (!test_bit(offset, pdata->gchip_output)) {
+               dev_err(pdata->dev, "Ignoring GPIO set while input\n");
+               return;
+       }
+
+       val &= 1;
+       ret = regmap_update_bits(pdata->regmap, SN_GPIO_IO_REG,
+                                BIT(SN_GPIO_OUTPUT_SHIFT + offset),
+                                val << (SN_GPIO_OUTPUT_SHIFT + offset));
+       if (ret)
+               dev_warn(pdata->dev,
+                        "Failed to set bridge GPIO %u: %d\n", offset, ret);
+}
+
+static int ti_sn_bridge_gpio_direction_input(struct gpio_chip *chip,
+                                            unsigned int offset)
+{
+       struct ti_sn_bridge *pdata = gpiochip_get_data(chip);
+       int shift = offset * 2;
+       int ret;
+
+       if (!test_and_clear_bit(offset, pdata->gchip_output))
+               return 0;
+
+       ret = regmap_update_bits(pdata->regmap, SN_GPIO_CTRL_REG,
+                                SN_GPIO_MUX_MASK << shift,
+                                SN_GPIO_MUX_INPUT << shift);
+       if (ret) {
+               set_bit(offset, pdata->gchip_output);
+               return ret;
+       }
+
+       /*
+        * NOTE: if nobody else is powering the device this may fully power
+        * it off and when it comes back it will have lost all state, but
+        * that's OK because the default is input and we're now an input.
+        */
+       pm_runtime_put(pdata->dev);
+
+       return 0;
+}
+
+static int ti_sn_bridge_gpio_direction_output(struct gpio_chip *chip,
+                                             unsigned int offset, int val)
+{
+       struct ti_sn_bridge *pdata = gpiochip_get_data(chip);
+       int shift = offset * 2;
+       int ret;
+
+       if (test_and_set_bit(offset, pdata->gchip_output))
+               return 0;
+
+       pm_runtime_get_sync(pdata->dev);
+
+       /* Set value first to avoid glitching */
+       ti_sn_bridge_gpio_set(chip, offset, val);
+
+       /* Set direction */
+       ret = regmap_update_bits(pdata->regmap, SN_GPIO_CTRL_REG,
+                                SN_GPIO_MUX_MASK << shift,
+                                SN_GPIO_MUX_OUTPUT << shift);
+       if (ret) {
+               clear_bit(offset, pdata->gchip_output);
+               pm_runtime_put(pdata->dev);
+       }
+
+       return ret;
+}
+
+static void ti_sn_bridge_gpio_free(struct gpio_chip *chip, unsigned int offset)
+{
+       /* We won't keep pm_runtime if we're input, so switch there on free */
+       ti_sn_bridge_gpio_direction_input(chip, offset);
+}
+
+static const char * const ti_sn_bridge_gpio_names[SN_NUM_GPIOS] = {
+       "GPIO1", "GPIO2", "GPIO3", "GPIO4"
+};
+
+static int ti_sn_setup_gpio_controller(struct ti_sn_bridge *pdata)
+{
+       int ret;
+
+       /* Only init if someone is going to use us as a GPIO controller */
+       if (!of_property_read_bool(pdata->dev->of_node, "gpio-controller"))
+               return 0;
+
+       pdata->gchip.label = dev_name(pdata->dev);
+       pdata->gchip.parent = pdata->dev;
+       pdata->gchip.owner = THIS_MODULE;
+       pdata->gchip.of_xlate = tn_sn_bridge_of_xlate;
+       pdata->gchip.of_gpio_n_cells = 2;
+       pdata->gchip.free = ti_sn_bridge_gpio_free;
+       pdata->gchip.get_direction = ti_sn_bridge_gpio_get_direction;
+       pdata->gchip.direction_input = ti_sn_bridge_gpio_direction_input;
+       pdata->gchip.direction_output = ti_sn_bridge_gpio_direction_output;
+       pdata->gchip.get = ti_sn_bridge_gpio_get;
+       pdata->gchip.set = ti_sn_bridge_gpio_set;
+       pdata->gchip.can_sleep = true;
+       pdata->gchip.names = ti_sn_bridge_gpio_names;
+       pdata->gchip.ngpio = SN_NUM_GPIOS;
+       pdata->gchip.base = -1;
+       ret = devm_gpiochip_add_data(pdata->dev, &pdata->gchip, pdata);
+       if (ret)
+               dev_err(pdata->dev, "can't add gpio chip\n");
+
+       return ret;
+}
+
+#else
+
+static inline int ti_sn_setup_gpio_controller(struct ti_sn_bridge *pdata)
+{
+       return 0;
+}
+
+#endif
+
+static void ti_sn_bridge_parse_lanes(struct ti_sn_bridge *pdata,
+                                    struct device_node *np)
+{
+       u32 lane_assignments[SN_MAX_DP_LANES] = { 0, 1, 2, 3 };
+       u32 lane_polarities[SN_MAX_DP_LANES] = { };
+       struct device_node *endpoint;
+       u8 ln_assign = 0;
+       u8 ln_polrs = 0;
+       int dp_lanes;
+       int i;
+
+       /*
+        * Read config from the device tree about lane remapping and lane
+        * polarities.  These are optional and we assume identity map and
+        * normal polarity if nothing is specified.  It's OK to specify just
+        * data-lanes but not lane-polarities but not vice versa.
+        *
+        * Error checking is light (we just make sure we don't crash or
+        * buffer overrun) and we assume dts is well formed and specifying
+        * mappings that the hardware supports.
+        */
+       endpoint = of_graph_get_endpoint_by_regs(np, 1, -1);
+       dp_lanes = of_property_count_u32_elems(endpoint, "data-lanes");
+       if (dp_lanes > 0 && dp_lanes <= SN_MAX_DP_LANES) {
+               of_property_read_u32_array(endpoint, "data-lanes",
+                                          lane_assignments, dp_lanes);
+               of_property_read_u32_array(endpoint, "lane-polarities",
+                                          lane_polarities, dp_lanes);
+       } else {
+               dp_lanes = SN_MAX_DP_LANES;
+       }
+       of_node_put(endpoint);
+
+       /*
+        * Convert into register format.  Loop over all lanes even if
+        * data-lanes had fewer elements so that we nicely initialize
+        * the LN_ASSIGN register.
+        */
+       for (i = SN_MAX_DP_LANES - 1; i >= 0; i--) {
+               ln_assign = ln_assign << LN_ASSIGN_WIDTH | lane_assignments[i];
+               ln_polrs = ln_polrs << 1 | lane_polarities[i];
+       }
+
+       /* Stash in our struct for when we power on */
+       pdata->dp_lanes = dp_lanes;
+       pdata->ln_assign = ln_assign;
+       pdata->ln_polrs = ln_polrs;
+}
+
 static int ti_sn_bridge_probe(struct i2c_client *client,
                              const struct i2c_device_id *id)
 {
@@ -737,6 +1198,8 @@ static int ti_sn_bridge_probe(struct i2c_client *client,
                return ret;
        }
 
+       ti_sn_bridge_parse_lanes(pdata, client->dev.of_node);
+
        ret = ti_sn_bridge_parse_regulators(pdata);
        if (ret) {
                DRM_ERROR("failed to parse regulators\n");
@@ -758,6 +1221,12 @@ static int ti_sn_bridge_probe(struct i2c_client *client,
 
        pm_runtime_enable(pdata->dev);
 
+       ret = ti_sn_setup_gpio_controller(pdata);
+       if (ret) {
+               pm_runtime_disable(pdata->dev);
+               return ret;
+       }
+
        i2c_set_clientdata(client, pdata);
 
        pdata->aux.name = "ti-sn65dsi86-aux";