This patch adds a drm_bridge driver for the IT6151 MIPI to eDP
bridge chip.

Signed-off-by: CK Hu <ck.hu at mediatek.com>
Signed-off-by: Jitao Shi <jitao.shi at mediatek.com>
---
 drivers/gpu/drm/bridge/Kconfig  |  10 +
 drivers/gpu/drm/bridge/Makefile |   1 +
 drivers/gpu/drm/bridge/it6151.c | 601 ++++++++++++++++++++++++++++++++++++++++
 include/drm/bridge/it6151.h     |  34 +++
 4 files changed, 646 insertions(+)
 create mode 100644 drivers/gpu/drm/bridge/it6151.c
 create mode 100644 include/drm/bridge/it6151.h

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index f38bbcd..2b3a78e 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -11,3 +11,13 @@ config DRM_PTN3460
        select DRM_PANEL
        ---help---
          ptn3460 eDP-LVDS bridge chip driver.
+
+config DRM_IT6151
+       bool "Enable IT6151FN : MIPI to eDP Converter"
+       depends on DRM
+       select DRM_KMS_HELPER
+       help
+         Choose this option if you have IT6151 for display
+         The IT6151 is a high-performance and low-power
+         MIPI to eDP converter
+
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index d8a8cfd..98edb74 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -2,3 +2,4 @@ ccflags-y := -Iinclude/drm

 obj-$(CONFIG_DRM_PTN3460) += ptn3460.o
 obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o
+obj-$(CONFIG_DRM_IT6151) += it6151.o
diff --git a/drivers/gpu/drm/bridge/it6151.c b/drivers/gpu/drm/bridge/it6151.c
new file mode 100644
index 0000000..039fe4b
--- /dev/null
+++ b/drivers/gpu/drm/bridge/it6151.c
@@ -0,0 +1,601 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+
+
+
+#define MIPI_RX_SW_RST            0x05
+#define MIPI_RX_INT_MASK          0x09
+#define MIPI_RX_SYS_CFG           0x0c
+#define MIPI_RX_MCLK              0x11
+#define MIPI_RX_PHY_IF_1          0x19
+#define MIPI_RX_PKT_DEC           0x27
+#define MIPI_RX_UFO_BLK_HIGH      0x28
+#define MIPI_RX_UFO_BLK_LOW       0x29
+#define MIPI_RX_UFO_HDE_DELAY     0x2e
+#define MIPI_RX_UFO_RESYNC        0x2f
+#define MIPI_RX_RESYNC_POL        0x4e
+#define MIPI_RX_PCLK_HSCLK        0x80
+#define MIPI_RX_AMP_TERM          0x84
+#define MIPI_RX_TIMER_INT_CNT     0x92
+
+#define DP_TX_VEN_ID_LOW        0x00
+#define DP_TX_VEN_ID_HIGH       0x01
+#define DP_TX_DEV_ID_LOW        0x02
+#define DP_TX_DEV_ID_HIGH       0x03
+#define DP_TX_REV_ID            0x04
+#define DP_TX_SW_RST            0x05
+#define DP_TX_INT_STA_0         0x06
+#define DP_TX_INT_STA_1         0x07
+#define DP_TX_INT_STA_2         0x08
+#define DP_TX_INT_MASK_0        0x09
+#define DP_TX_INT_MASK_1        0x0a
+#define DP_TX_INT_MASK_2        0x0b
+#define DP_TX_SYS_CFG           0x0c
+#define DP_TX_SYS_DBG           0x0f
+#define DP_TX_LANE              0x16
+#define DP_TX_TRAIN             0x17
+#define DP_TX_AUX_CH_FIFO       0x21
+#define DP_TX_AUX_CLK           0x22
+#define DP_TX_PC_REQ_FIFO       0x23
+#define DP_TX_PC_REQ_OFST_0     0x24
+#define DP_TX_PC_REQ_OFST_1     0x25
+#define DP_TX_PC_REQ_OFST_2     0x26
+#define DP_TX_PC_REQ_WD_0       0x27
+#define DP_TX_PC_REQ_SEL        0x2b
+#define DP_TX_HDP               0x3a
+#define DP_TX_LNPWDB            0x5c
+#define DP_TX_EQ                0x5f
+#define DP_TX_COL_CONV_19       0x76
+#define DP_TX_COL_CONV_20       0x77
+#define DP_TX_PG_H_DE_END_L     0x7e
+#define DP_TX_PG_H_DE_END_H     0x7f
+#define DP_TX_PG_H_SYNC_START_L 0x80
+#define DP_TX_PG_H_SYNC_START_H 0x81
+#define DP_TX_PG_H_SYNC_END_L   0x82
+#define DP_TX_PG_H_SYNC_END_H   0x83
+#define DP_TX_PG_V_DE_END_L     0x88
+#define DP_TX_PG_V_DE_END_H     0x89
+#define DP_TX_PG_V_DE_START_L   0x8a
+#define DP_TX_IN_VDO_TM_15      0xb5
+#define DP_TX_IN_VDO_TM_17      0xb7
+#define DP_TX_PSR_CTRL_0        0xc4
+#define DP_TX_PSR_CTRL_1        0xc5
+#define DP_TX_HDP_IRQ_TM        0xc9
+#define DP_TX_AUX_DBG           0xca
+#define DP_TX_AUX_MASTER        0xcb
+#define DP_TX_PKT_OPT           0xce
+#define DP_TX_VDO_FIFO          0xd3
+#define DP_TX_VDO_STMP          0xd4
+#define DP_TX_PKT               0xe8
+#define DP_TX_PKT_AVI_VIC       0xec
+#define DP_TX_MIPI_PORT         0xfd
+
+enum {
+       MIPI_1_LANE = 0,
+       MIPI_2_LANE = 1,
+       MIPI_3_LANE = 2,
+       MIPI_4_LANE = 3,
+};
+
+enum {
+       RGB_24b     = 0x3E,
+       RGB_30b     = 0x0D,
+       RGB_36b     = 0x1D,
+       RGB_18b_P   = 0x1E,
+       RGB_18b_L   = 0x2E,
+       YCbCr_16b   = 0x2C,
+       YCbCr_20b   = 0x0C,
+       YCbCr_24b   = 0x1C,
+};
+
+enum {
+       B_HBR   = 0,
+       B_LBR   = 1,
+};
+
+enum {
+       B_1_LANE    = 0,
+       B_2_LANE    = 1,
+       B_4_LANE    = 3,
+};
+
+enum {
+       B_SSC_DISABLE   = 0,
+       B_SSC_ENABLE    = 1,
+};
+
+struct it6151_driver_data {
+       u8 training_bitrate;
+       u8 dptx_ssc_setting;
+       u8 mp_mclk_inv;
+       u8 mp_continuous_clk;
+       u8 mp_lane_deskew;
+       u8 mp_pclk_div;
+       u8 mp_lane_swap;
+       u8 mp_pn_swap;
+
+       u8 dp_pn_swap;
+       u8 dp_aux_pn_swap;
+       u8 dp_lane_swap;
+       u8 int_mask;
+       u8 mipi_int_mask;
+       u8 timer_cnt;
+
+       u16 panel_width;
+       u8 vic;
+       u8 mp_hpol;
+       u8 mp_vpol;
+       u8 mipi_lane_count;
+       u8 dptx_lane_count;
+       u8 en_ufo;
+       u8 mipi_packed_fmt;
+       u8 mp_h_resync;
+       u8 mp_v_resync;
+};
+
+struct it6151_bridge {
+       struct drm_connector connector;
+       struct i2c_client *client;
+       struct drm_encoder *encoder;
+       struct it6151_driver_data *driver_data;
+       int gpio_rst_n;
+       u16 rx_reg;
+       u16 tx_reg;
+       bool enabled;
+};
+
+static int it6151_regr(struct i2c_client *client, u16 i2c_addr,
+       u8 reg, u8 *value)
+{
+       int r;
+       u8 tx_data[] = {
+               reg,
+       };
+       u8 rx_data[1];
+       struct i2c_msg msgs[] = {
+               {
+                       .addr = i2c_addr,
+                       .flags = 0,
+                       .buf = tx_data,
+                       .len = ARRAY_SIZE(tx_data),
+               },
+               {
+                       .addr = i2c_addr,
+                       .flags = I2C_M_RD,
+                       .buf = rx_data,
+                       .len = ARRAY_SIZE(rx_data),
+                },
+       };
+
+       r = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+       if (r < 0) {
+               dev_err(&client->dev, "%s: reg 0x%02x error %d\n", __func__,
+                       reg, r);
+               return r;
+       }
+
+       if (r < ARRAY_SIZE(msgs)) {
+               dev_err(&client->dev, "%s: reg 0x%02x msgs %d\n", __func__,
+                       reg, r);
+               return -EAGAIN;
+       }
+
+       *value = rx_data[0];
+
+       dev_dbg(&client->dev, "%s: reg 0x%02x value 0x%02x\n", __func__,
+               reg, *value);
+
+       return 0;
+}
+
+static int it6151_regw(struct i2c_client *client, u16 i2c_addr,
+       u8 reg, u8 value)
+{
+       int r;
+       u8 tx_data[] = {
+               reg,
+               value,
+       };
+       struct i2c_msg msgs[] = {
+               {
+                       .addr = i2c_addr,
+                       .flags = 0,
+                       .buf = tx_data,
+                       .len = ARRAY_SIZE(tx_data),
+               },
+       };
+
+       r = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+       if (r < 0) {
+               dev_err(&client->dev, "%s: reg 0x%02x val 0x%02x error %d\n",
+                       __func__, reg, value, r);
+               return r;
+       }
+
+       dev_dbg(&client->dev, "%s: reg 0x%02x val 0x%02x\n",
+                       __func__, reg, value);
+
+       return 0;
+}
+
+static const struct drm_display_mode it6151_drm_default_modes[] = {
+       /* 1368x768 at 60Hz */
+       { DRM_MODE("1368x768", DRM_MODE_TYPE_DRIVER, 72070,
+               1368, 1368 + 58, 1368 + 58 + 58, 1368 + 58 + 58 + 58, 0,
+               768, 768 + 4, 768 + 4 + 4, 768 + 4 + 4 + 4, 0, 0) },
+};
+
+static struct it6151_driver_data it6151_driver_data_1368x768 = {
+       .training_bitrate = B_HBR,
+       .dptx_ssc_setting = B_SSC_ENABLE,
+       .mp_mclk_inv = 1,
+       .mp_continuous_clk = 1,
+       .mp_lane_deskew = 1,
+       .mp_pclk_div = 2,
+       .mp_lane_swap = 0,
+       .mp_pn_swap = 0,
+
+       .dp_pn_swap = 0,
+       .dp_aux_pn_swap = 0,
+       .dp_lane_swap = 0,
+       .int_mask = 3,
+       .mipi_int_mask = 0,
+       .timer_cnt = 0xa,
+
+       .panel_width = 1368,
+       .vic = 0,
+       .mp_hpol = 0,
+       .mp_vpol = 1,
+       .mipi_lane_count = MIPI_4_LANE,
+       .dptx_lane_count = B_1_LANE,
+       .en_ufo = 0,
+       .mipi_packed_fmt = RGB_24b,
+       .mp_h_resync = 1,
+       .mp_v_resync = 0,
+};
+
+static struct it6151_driver_data *it6151_get_driver_data(void)
+{
+       return &it6151_driver_data_1368x768;
+}
+
+static int it6151_check_valid_id(struct it6151_bridge *ite_bridge)
+{
+       struct i2c_client *client = ite_bridge->client;
+       u16 tx_reg = ite_bridge->tx_reg;
+       u8 ven_id_low, ven_id_high, dev_id_low, dev_id_high;
+       int retry_cnt = 0;
+
+       do {
+               it6151_regr(client, tx_reg, DP_TX_VEN_ID_LOW, &ven_id_low);
+               if (ven_id_low != 0x54)
+                       DRM_ERROR("ven_id_low = 0x%x\n", ven_id_low);
+       } while ((retry_cnt++ < 10) && (ven_id_low != 0x54));
+
+       it6151_regr(client, tx_reg, DP_TX_VEN_ID_HIGH, &ven_id_high);
+       it6151_regr(client, tx_reg, DP_TX_DEV_ID_LOW, &dev_id_low);
+       it6151_regr(client, tx_reg, DP_TX_DEV_ID_HIGH, &dev_id_high);
+
+       if ((ven_id_low == 0x54) && (ven_id_high == 0x49) &&
+               (dev_id_low == 0x51) && (dev_id_high == 0x61))
+               return 1;
+
+       return 0;
+}
+
+static void it6151_mipirx_init(struct it6151_bridge *ite_bridge)
+{
+       struct i2c_client *client = ite_bridge->client;
+       struct it6151_driver_data *drv_data = ite_bridge->driver_data;
+       u16 rx_reg = ite_bridge->rx_reg;
+       u16 tx_reg = ite_bridge->tx_reg;
+       unsigned char rev_id;
+
+       it6151_regr(client, tx_reg, DP_TX_REV_ID, &rev_id);
+
+       it6151_regw(client, rx_reg, MIPI_RX_SW_RST, 0x00);
+       it6151_regw(client, rx_reg, MIPI_RX_SYS_CFG,
+               (drv_data->mp_lane_swap << 7) | (drv_data->mp_pn_swap << 6) |
+               (drv_data->mipi_lane_count << 4) | drv_data->en_ufo);
+       it6151_regw(client, rx_reg, MIPI_RX_MCLK, drv_data->mp_mclk_inv);
+
+       if (rev_id == 0xA1)
+               it6151_regw(client, rx_reg,
+                       MIPI_RX_PHY_IF_1, drv_data->mp_lane_deskew);
+       else
+               it6151_regw(client, rx_reg,
+                       MIPI_RX_PHY_IF_1,
+                       (drv_data->mp_continuous_clk << 1) |
+                       drv_data->mp_lane_deskew);
+
+       it6151_regw(client, rx_reg, MIPI_RX_PKT_DEC, drv_data->mipi_packed_fmt);
+       it6151_regw(client, rx_reg, MIPI_RX_UFO_BLK_HIGH,
+               ((drv_data->panel_width/4-1)>>2)&0xC0);
+       it6151_regw(client, rx_reg, MIPI_RX_UFO_BLK_LOW,
+               (drv_data->panel_width/4-1)&0xFF);
+       it6151_regw(client, rx_reg, MIPI_RX_UFO_HDE_DELAY, 0x34);
+       it6151_regw(client, rx_reg, MIPI_RX_UFO_RESYNC, 0x01);
+       it6151_regw(client, rx_reg, MIPI_RX_RESYNC_POL,
+               (drv_data->mp_v_resync<<3)|(drv_data->mp_h_resync<<2)|
+               (drv_data->mp_vpol<<1)|(drv_data->mp_hpol));
+       it6151_regw(client, rx_reg, MIPI_RX_PCLK_HSCLK,
+               (drv_data->en_ufo<<5)|drv_data->mp_pclk_div);
+       it6151_regw(client, rx_reg, MIPI_RX_AMP_TERM, 0x8f);
+       it6151_regw(client, rx_reg, MIPI_RX_INT_MASK, drv_data->mipi_int_mask);
+       it6151_regw(client, rx_reg, MIPI_RX_TIMER_INT_CNT, drv_data->timer_cnt);
+}
+
+static void it6151_dptx_init(struct it6151_bridge *ite_bridge)
+{
+       struct i2c_client *client = ite_bridge->client;
+       struct it6151_driver_data *drv_data = ite_bridge->driver_data;
+       u16 tx_reg = ite_bridge->tx_reg;
+
+       it6151_regw(client, tx_reg, DP_TX_SW_RST, 0x29);
+       it6151_regw(client, tx_reg, DP_TX_SW_RST, 0x00);
+       it6151_regw(client, tx_reg, DP_TX_INT_MASK_0, drv_data->int_mask);
+       it6151_regw(client, tx_reg, DP_TX_INT_MASK_1, 0x00);
+       it6151_regw(client, tx_reg, DP_TX_INT_MASK_2, 0x00);
+       it6151_regw(client, tx_reg, DP_TX_PSR_CTRL_1, 0xc1);
+       it6151_regw(client, tx_reg, DP_TX_IN_VDO_TM_15, 0x00);
+       it6151_regw(client, tx_reg, DP_TX_IN_VDO_TM_17, 0x80);
+       it6151_regw(client, tx_reg, DP_TX_PSR_CTRL_0, 0xF0);
+       it6151_regw(client, tx_reg, DP_TX_INT_STA_0, 0xFF);
+       it6151_regw(client, tx_reg, DP_TX_INT_STA_1, 0xFF);
+       it6151_regw(client, tx_reg, DP_TX_INT_STA_2, 0xFF);
+       it6151_regw(client, tx_reg, DP_TX_SW_RST, 0x00);
+       it6151_regw(client, tx_reg, DP_TX_SYS_CFG, 0x08);
+       it6151_regw(client, tx_reg, DP_TX_AUX_CH_FIFO, 0x05);
+       it6151_regw(client, tx_reg, DP_TX_HDP, 0x04);
+       it6151_regw(client, tx_reg, DP_TX_EQ, 0x06);
+       it6151_regw(client, tx_reg, DP_TX_HDP_IRQ_TM, 0xf5);
+       it6151_regw(client, tx_reg, DP_TX_AUX_DBG, 0x4c);
+       it6151_regw(client, tx_reg, DP_TX_AUX_MASTER, 0x37);
+       it6151_regw(client, tx_reg, DP_TX_PKT_OPT, 0x80);
+       it6151_regw(client, tx_reg, DP_TX_VDO_FIFO, 0x03);
+       it6151_regw(client, tx_reg, DP_TX_VDO_STMP, 0x60);
+       it6151_regw(client, tx_reg, DP_TX_PKT, 0x11);
+       it6151_regw(client, tx_reg, DP_TX_PKT_AVI_VIC, drv_data->vic);
+       mdelay(5);
+       it6151_regw(client, tx_reg, DP_TX_PC_REQ_FIFO, 0x42);
+       it6151_regw(client, tx_reg, DP_TX_PC_REQ_OFST_0, 0x07);
+       it6151_regw(client, tx_reg, DP_TX_PC_REQ_OFST_1, 0x01);
+       it6151_regw(client, tx_reg, DP_TX_PC_REQ_OFST_2, 0x00);
+       it6151_regw(client, tx_reg, DP_TX_PC_REQ_WD_0, 0x10);
+       it6151_regw(client, tx_reg, DP_TX_PC_REQ_SEL, 0x05);
+       it6151_regw(client, tx_reg, DP_TX_PC_REQ_FIFO, 0x40);
+       it6151_regw(client, tx_reg, DP_TX_AUX_CLK,
+               (drv_data->dp_aux_pn_swap<<3)|(drv_data->dp_pn_swap<<2)|0x03);
+       it6151_regw(client, tx_reg, DP_TX_LANE,
+               (drv_data->dptx_ssc_setting<<4)|(drv_data->dp_lane_swap<<3)|
+               (drv_data->dptx_lane_count<<1)|drv_data->training_bitrate);
+       it6151_regw(client, tx_reg, DP_TX_SYS_DBG, 0x01);
+       it6151_regw(client, tx_reg, DP_TX_COL_CONV_19, 0xa7);
+       it6151_regw(client, tx_reg, DP_TX_COL_CONV_20, 0xaf);
+       it6151_regw(client, tx_reg, DP_TX_PG_H_DE_END_L, 0x8f);
+       it6151_regw(client, tx_reg, DP_TX_PG_H_DE_END_H, 0x07);
+       it6151_regw(client, tx_reg, DP_TX_PG_H_SYNC_START_L, 0xef);
+       it6151_regw(client, tx_reg, DP_TX_PG_H_SYNC_START_H, 0x5f);
+       it6151_regw(client, tx_reg, DP_TX_PG_H_SYNC_END_L, 0xef);
+       it6151_regw(client, tx_reg, DP_TX_PG_H_SYNC_END_H, 0x07);
+       it6151_regw(client, tx_reg, DP_TX_PG_V_DE_END_L, 0x38);
+       it6151_regw(client, tx_reg, DP_TX_PG_V_DE_END_H, 0x1f);
+       it6151_regw(client, tx_reg, DP_TX_PG_V_DE_START_L, 0x48);
+       it6151_regw(client, tx_reg, DP_TX_SYS_DBG, 0x00);
+       it6151_regw(client, tx_reg, DP_TX_LNPWDB, 0xf3);
+       it6151_regw(client, tx_reg, DP_TX_TRAIN, 0x04);
+       it6151_regw(client, tx_reg, DP_TX_TRAIN, 0x01);
+       mdelay(5);
+}
+
+static int it6151_bdg_enable(struct it6151_bridge *ite_bridge)
+{
+       struct i2c_client *client = ite_bridge->client;
+       u16 tx_reg = ite_bridge->tx_reg;
+
+       if (it6151_check_valid_id(ite_bridge)) {
+               it6151_regw(client, tx_reg, DP_TX_SW_RST, 0x04);
+               it6151_regw(client, tx_reg, DP_TX_MIPI_PORT,
+                       (ite_bridge->rx_reg<<1)|1);
+
+               it6151_mipirx_init(ite_bridge);
+               it6151_dptx_init(ite_bridge);
+
+               return 0;
+       }
+
+       return -1;
+}
+
+static void it6151_pre_enable(struct drm_bridge *bridge)
+{
+       /* drm framework doesn't check NULL. */
+}
+
+static void it6151_enable(struct drm_bridge *bridge)
+{
+       struct it6151_bridge *ite_bridge = bridge->driver_private;
+
+       gpio_direction_output(ite_bridge->gpio_rst_n, 0);
+       udelay(15);
+       gpio_direction_output(ite_bridge->gpio_rst_n, 1);
+
+       it6151_bdg_enable(ite_bridge);
+
+       ite_bridge->enabled = true;
+}
+
+static void it6151_disable(struct drm_bridge *bridge)
+{
+       struct it6151_bridge *ite_bridge = bridge->driver_private;
+
+       if (!ite_bridge->enabled)
+               return;
+
+       ite_bridge->enabled = false;
+
+       if (gpio_is_valid(ite_bridge->gpio_rst_n))
+               gpio_set_value(ite_bridge->gpio_rst_n, 1);
+}
+
+static void it6151_post_disable(struct drm_bridge *bridge)
+{
+       /* drm framework doesn't check NULL. */
+}
+
+static struct drm_bridge_funcs it6151_bridge_funcs = {
+       .pre_enable = it6151_pre_enable,
+       .enable = it6151_enable,
+       .disable = it6151_disable,
+       .post_disable = it6151_post_disable,
+};
+
+static int it6151_get_modes(struct drm_connector *connector)
+{
+       const struct drm_display_mode *ptr = &it6151_drm_default_modes[0];
+       struct drm_display_mode *mode;
+       int count = 0;
+
+       mode = drm_mode_duplicate(connector->dev, ptr);
+       if (mode) {
+               drm_mode_probed_add(connector, mode);
+               count++;
+       }
+
+       connector->display_info.width_mm = mode->hdisplay;
+       connector->display_info.height_mm = mode->vdisplay;
+
+       return count;
+}
+
+static int it6151_mode_valid(struct drm_connector *connector,
+               struct drm_display_mode *mode)
+{
+       return MODE_OK;
+}
+
+static struct drm_encoder *it6151_best_encoder(struct drm_connector *connector)
+{
+       struct it6151_bridge *ite_bridge;
+
+       ite_bridge = container_of(connector, struct it6151_bridge, connector);
+       return ite_bridge->encoder;
+}
+
+static struct drm_connector_helper_funcs it6151_connector_helper_funcs = {
+       .get_modes = it6151_get_modes,
+       .mode_valid = it6151_mode_valid,
+       .best_encoder = it6151_best_encoder,
+};
+
+static enum drm_connector_status it6151_detect(struct drm_connector *connector,
+               bool force)
+{
+       return connector_status_connected;
+}
+
+static void it6151_connector_destroy(struct drm_connector *connector)
+{
+       drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs it6151_connector_funcs = {
+       .dpms = drm_helper_connector_dpms,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .detect = it6151_detect,
+       .destroy = it6151_connector_destroy,
+};
+
+int it6151_init(struct drm_device *dev, struct drm_encoder *encoder,
+       struct i2c_client *client, struct device_node *node)
+{
+       int ret;
+       struct drm_bridge *bridge;
+       struct it6151_bridge *ite_bridge;
+       u32 rx_reg, tx_reg;
+
+       bridge = devm_kzalloc(dev->dev, sizeof(*bridge), GFP_KERNEL);
+       if (!bridge)
+               return -ENOMEM;
+
+       ite_bridge = devm_kzalloc(dev->dev, sizeof(*ite_bridge), GFP_KERNEL);
+       if (!ite_bridge)
+               return -ENOMEM;
+
+       ite_bridge->encoder = encoder;
+       ite_bridge->client = client;
+       ite_bridge->driver_data = it6151_get_driver_data();
+
+       ite_bridge->gpio_rst_n = of_get_named_gpio(node, "reset-gpio", 0);
+       if (gpio_is_valid(ite_bridge->gpio_rst_n)) {
+               ret = gpio_request_one(ite_bridge->gpio_rst_n,
+                       GPIOF_OUT_INIT_LOW, "mtk_rst");
+               if (ret)
+                       return ret;
+       }
+
+       ret = of_property_read_u32(node, "reg", &tx_reg);
+       if (ret) {
+               DRM_ERROR("Can't read reg value\n");
+               goto err;
+       }
+       ite_bridge->tx_reg = tx_reg;
+
+       ret = of_property_read_u32(node, "rxreg", &rx_reg);
+       if (ret) {
+               DRM_ERROR("Can't read rxreg value\n");
+               goto err;
+       }
+       ite_bridge->rx_reg = rx_reg;
+
+       bridge->funcs = &it6151_bridge_funcs;
+       ret = drm_bridge_attach(dev, bridge);
+       if (ret)
+               goto err;
+
+       bridge->driver_private = ite_bridge;
+       encoder->bridge = bridge;
+
+       ret = drm_connector_init(dev, &ite_bridge->connector,
+               &it6151_connector_funcs, DRM_MODE_CONNECTOR_eDP);
+       if (ret)
+               goto err;
+
+       drm_connector_helper_add(&ite_bridge->connector,
+               &it6151_connector_helper_funcs);
+       drm_connector_register(&ite_bridge->connector);
+       drm_mode_connector_attach_encoder(&ite_bridge->connector, encoder);
+
+       return 0;
+
+err:
+       if (gpio_is_valid(ite_bridge->gpio_rst_n))
+               gpio_free(ite_bridge->gpio_rst_n);
+
+       return ret;
+}
+
diff --git a/include/drm/bridge/it6151.h b/include/drm/bridge/it6151.h
new file mode 100644
index 0000000..5503ed9
--- /dev/null
+++ b/include/drm/bridge/it6151.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _DRM_BRIDGE_IT6151_H_
+#define _DRM_BRIDGE_IT6151_H_
+
+struct drm_device;
+struct drm_encoder;
+struct i2c_client;
+struct device_node;
+
+#if defined(CONFIG_DRM_IT6151)
+int it6151_init(struct drm_device *dev, struct drm_encoder *encoder,
+       struct i2c_client *client, struct device_node *node);
+#else
+inline int it6151_init(struct drm_device *dev, struct drm_encoder *encoder,
+       struct i2c_client *client, struct device_node *node)
+{
+       return 0;
+}
+#endif
+
+#endif
+
-- 
1.8.1.1.dirty

Reply via email to