[PATCH 2/2] drm/bridge: Add IT6151 bridge driver

CK Hu ck.hu at mediatek.com
Tue Mar 10 20:25:56 PDT 2015


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

************* Email Confidentiality Notice ********************
The information contained in this e-mail message (including any 
attachments) may be confidential, proprietary, privileged, or otherwise
exempt from disclosure under applicable laws. It is intended to be 
conveyed only to the designated recipient(s). Any use, dissemination, 
distribution, printing, retaining or copying of this e-mail (including its 
attachments) by unintended recipient(s) is strictly prohibited and may 
be unlawful. If you are not an intended recipient of this e-mail, or believe 
that you have received this e-mail in error, please notify the sender 
immediately (by replying to this e-mail), delete any and all copies of 
this e-mail (including any attachments) from your system, and do not
disclose the content of this e-mail to any other person. Thank you!



More information about the Linux-mediatek mailing list