[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