From patchwork Wed Mar 11 03:25:56 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?b?Q0sgSHUgKOiDoeS/iuWFiSk=?= X-Patchwork-Id: 5982041 Return-Path: X-Original-To: patchwork-dri-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 0E9CC9F318 for ; Wed, 11 Mar 2015 03:39:53 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 3DEFD203C1 for ; Wed, 11 Mar 2015 03:39:51 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id 84634203DF for ; Wed, 11 Mar 2015 03:39:49 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 410436E78C; Tue, 10 Mar 2015 20:39:48 -0700 (PDT) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org X-Greylist: delayed 307 seconds by postgrey-1.34 at gabe; Tue, 10 Mar 2015 20:39:45 PDT Received: from mailgw01.mediatek.com (unknown [210.61.82.183]) by gabe.freedesktop.org (Postfix) with ESMTP id 70BC26E78B for ; Tue, 10 Mar 2015 20:39:44 -0700 (PDT) X-Listener-Flag: 11101 Received: from mtkhts07.mediatek.inc [(172.21.101.69)] by mailgw01.mediatek.com (envelope-from ) (mhqrelay.mediatek.com ESMTP with TLS) with ESMTP id 551008354; Wed, 11 Mar 2015 11:34:35 +0800 Received: from mtkslt303.mediatek.inc (10.21.14.116) by mtkhts07.mediatek.inc (172.21.101.73) with Microsoft SMTP Server id 14.3.181.6; Wed, 11 Mar 2015 11:34:33 +0800 From: CK Hu To: Rob Herring , Mark Rutland , David Airlie Subject: [PATCH 2/2] drm/bridge: Add IT6151 bridge driver Date: Wed, 11 Mar 2015 11:25:56 +0800 Message-ID: <1426044356-54844-2-git-send-email-ck.hu@mediatek.com> X-Mailer: git-send-email 1.8.1.1.dirty In-Reply-To: <1426044356-54844-1-git-send-email-ck.hu@mediatek.com> References: <1426044356-54844-1-git-send-email-ck.hu@mediatek.com> MIME-Version: 1.0 Cc: devicetree@vger.kernel.org, Jitao Shi , Sascha Hauer , srv_heupstream@mediatek.com, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, linux-mediatek@lists.infradead.org X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_RP_MATCHES_RCVD,UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch adds a drm_bridge driver for the IT6151 MIPI to eDP bridge chip. Signed-off-by: CK Hu Signed-off-by: Jitao Shi --- 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + + +#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@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 +