From 311c44cffccdb72e87ef3eb9ddfd05f79c754eec Mon Sep 17 00:00:00 2001 From: Ilia Lin Date: Wed, 6 May 2015 10:22:01 +0300 Subject: [PATCH] msm: adv7533: add DSI to HDMI bridge chip support Add driver for ADV7533 to support DSI to HDMI output. Configure the ADV7533 via I2C for audio/video setup. Change-Id: Iec0b922e1257d6974ea1755b49a6087f85eb8499 Signed-off-by: Ilia Lin Signed-off-by: Siddharth Zaveri [cip@codeaurora.org: Moved adb7533.c location] Signed-off-by: Clarence Ip --- .../devicetree/bindings/fb/adv7533.txt | 40 + drivers/video/fbdev/msm/msm_dba/Kconfig | 11 +- drivers/video/fbdev/msm/msm_dba/Makefile | 1 + drivers/video/fbdev/msm/msm_dba/adv7533.c | 808 ++++++++++++++++++ 4 files changed, 859 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/fb/adv7533.txt create mode 100644 drivers/video/fbdev/msm/msm_dba/adv7533.c diff --git a/Documentation/devicetree/bindings/fb/adv7533.txt b/Documentation/devicetree/bindings/fb/adv7533.txt new file mode 100644 index 000000000000..33dd1dd73f1c --- /dev/null +++ b/Documentation/devicetree/bindings/fb/adv7533.txt @@ -0,0 +1,40 @@ +ADV7533 DSI to HDMI bridge + + +Required properties: +- compatible: Must be "adv7533" +- reg: Main I2C slave ID (for I2C host driver) +- adi,video-mode: Excepted a number and possible inputs are 0 to 3, while: + 3 = 1080p + 2 = 720p + 1 = 480p + 0 = 1080p pattern +- adi,main-addr: Main I2C slave ID +- adi,cec-dsi-addr: CEC DSI I2C slave ID + +Optional properties: +- adi,enable-audio: +- adi,disable-gpios: +- adi,irq-gpio: Main IRQ gpio mapping +- adi,hpd-irq-gpio: HPD IRQ gpio mapping +- adi,switch-gpio: DSI switch gpio mapping + +Example: +&soc { + i2c@78b8000 { + adv7533@39 { + compatible = "adv7533"; + reg = <0x39>; + adi,video-mode = <3>; /* 3 = 1080p */ + adi,main-addr = <0x39>; + adi,cec-dsi-addr = <0x3C>; + adi,enable-audio; + pinctrl-names = "pmx_adv7533_active","pmx_adv7533_suspend"; + pinctrl-0 = <&adv7533_int_active &adv7533_hpd_int_active &adv7533_switch_active>; + pinctrl-1 = <&adv7533_int_suspend &adv7533_hpd_int_suspend &adv7533_switch_suspend>; + adi,irq-gpio = <&msm_gpio 31 0x2002>; + adi,hpd-irq-gpio = <&msm_gpio 20 0x2003>; + adi,switch-gpio = <&msm_gpio 32 0x0>; + }; + }; +}; diff --git a/drivers/video/fbdev/msm/msm_dba/Kconfig b/drivers/video/fbdev/msm/msm_dba/Kconfig index a20d59c16a0d..7725a37ce156 100644 --- a/drivers/video/fbdev/msm/msm_dba/Kconfig +++ b/drivers/video/fbdev/msm/msm_dba/Kconfig @@ -4,7 +4,7 @@ config MSM_DBA bool "MSM Display Bridge Abstraction support" - depends on ARM + depends on ARM || ARM64 ---help--- Support for MSM display bridge abstraction interface. MSM display drivers can use the same interface to interact with different third @@ -13,3 +13,12 @@ config MSM_DBA bridge chip. The MSM DBA driver maintains a list of devices supported on the platform and allow clients to register and access these devices. + +config MSM_DBA_ADV7533 + bool "ADV7533 driver support through MSM DBA interface" + depends on MSM_DBA + default n + ---help--- + Support for ADV7533 DSI to HDMI display bridge driver. The driver + controls the ADV7533 HW through the I2C interface and configures + the DSi input and HDMI output video format. diff --git a/drivers/video/fbdev/msm/msm_dba/Makefile b/drivers/video/fbdev/msm/msm_dba/Makefile index 68a1adf3f88e..cf28ad447aeb 100644 --- a/drivers/video/fbdev/msm/msm_dba/Makefile +++ b/drivers/video/fbdev/msm/msm_dba/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_MSM_DBA) += msm_dba.o msm_dba_init.o msm_dba_helpers.o msm_dba_debug.o +obj-$(CONFIG_MSM_DBA_ADV7533) += adv7533.o clean: rm *.o diff --git a/drivers/video/fbdev/msm/msm_dba/adv7533.c b/drivers/video/fbdev/msm/msm_dba/adv7533.c new file mode 100644 index 000000000000..eda959736b4e --- /dev/null +++ b/drivers/video/fbdev/msm/msm_dba/adv7533.c @@ -0,0 +1,808 @@ +/* Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only 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 +#include +#include +#include + +#define ADV7533_REG_CHIP_REVISION (0x00) +#define ADV7533_RESET_DELAY (100) + +#define PINCTRL_STATE_ACTIVE "pmx_adv7533_active" +#define PINCTRL_STATE_SUSPEND "pmx_adv7533_suspend" + +#define MDSS_MAX_PANEL_LEN 256 + +enum adv7533_i2c_addr { + ADV7533_MAIN = 0x39, /* 7a main right shift 1 */ + ADV7533_CEC_DSI = 0x3C, +}; + +enum adv7533_video_mode { + ADV7533_VIDEO_PATTERN, + ADV7533_VIDEO_480P, + ADV7533_VIDEO_720P, + ADV7533_VIDEO_1080P, +}; + +struct adv7533_reg_cfg { + u8 i2c_addr; + u8 reg; + u8 val; +}; + +struct adv7533_platform_data { + u8 main_i2c_addr; + u8 cec_dsi_i2c_addr; + u8 video_mode; + int irq; + u32 irq_gpio; + u32 irq_flags; + int hpd_irq; + u32 hpd_irq_gpio; + u32 hpd_irq_flags; + u32 switch_gpio; + u32 switch_flags; + struct pinctrl *ts_pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + bool audio; + bool disable_gpios; + bool adv_output; +}; + +static struct adv7533_reg_cfg setup_cfg[] = { + {ADV7533_MAIN, 0x41, 0x10}, /* HDMI normal */ + {ADV7533_MAIN, 0xd6, 0x48}, /* HPD overridden */ + {ADV7533_CEC_DSI, 0x03, 0x89}, /* HDMI enabled */ + {ADV7533_MAIN, 0x16, 0x20}, + {ADV7533_MAIN, 0x9A, 0xE0}, + {ADV7533_MAIN, 0xBA, 0x70}, + {ADV7533_MAIN, 0xDE, 0x82}, + {ADV7533_MAIN, 0xE4, 0xC0}, + {ADV7533_MAIN, 0xE5, 0x80}, + {ADV7533_CEC_DSI, 0x15, 0xD0}, + {ADV7533_CEC_DSI, 0x17, 0xD0}, + {ADV7533_CEC_DSI, 0x24, 0x20}, + {ADV7533_CEC_DSI, 0x57, 0x11}, + /* hdmi or dvi mode: hdmi */ + {ADV7533_MAIN, 0xAF, 0x06}, + {ADV7533_MAIN, 0x40, 0x80}, + {ADV7533_MAIN, 0x4C, 0x04}, + {ADV7533_MAIN, 0x49, 0x02}, + {ADV7533_MAIN, 0x0D, 1 << 6}, +}; + +static struct adv7533_reg_cfg tg_cfg_1080p[] = { + /* 4 lanes */ + {ADV7533_CEC_DSI, 0x1C, 0x40}, + /* hsync and vsync active low */ + {ADV7533_MAIN, 0x17, 0x02}, + /* Control for Pixel Clock Divider */ + {ADV7533_CEC_DSI, 0x16, 0x00}, + /* Timing Generator Enable */ + {ADV7533_CEC_DSI, 0x27, 0xCB}, + /* h_width 0x898 2200*/ + {ADV7533_CEC_DSI, 0x28, 0x89}, + {ADV7533_CEC_DSI, 0x29, 0x80}, + /* hsync_width 0x2C 44*/ + {ADV7533_CEC_DSI, 0x2A, 0x02}, + {ADV7533_CEC_DSI, 0x2B, 0xC0}, + /* hfp 0x58 88 */ + {ADV7533_CEC_DSI, 0x2C, 0x05}, + {ADV7533_CEC_DSI, 0x2D, 0x80}, + /* hbp 0x94 148 */ + {ADV7533_CEC_DSI, 0x2E, 0x09}, + {ADV7533_CEC_DSI, 0x2F, 0x40}, + /* v_total 0x465 1125 */ + {ADV7533_CEC_DSI, 0x30, 0x46}, + {ADV7533_CEC_DSI, 0x31, 0x50}, + /* vsync_width 0x05 5*/ + {ADV7533_CEC_DSI, 0x32, 0x00}, + {ADV7533_CEC_DSI, 0x33, 0x50}, + /* vfp 0x04 4 */ + {ADV7533_CEC_DSI, 0x34, 0x00}, + {ADV7533_CEC_DSI, 0x35, 0x40}, + /* vbp 0x24 36 */ + {ADV7533_CEC_DSI, 0x36, 0x02}, + {ADV7533_CEC_DSI, 0x37, 0x40}, + /* Timing Generator Enable */ + {ADV7533_CEC_DSI, 0x27, 0xCB}, + {ADV7533_CEC_DSI, 0x27, 0x8B}, + {ADV7533_CEC_DSI, 0x27, 0xCB}, + /* Reset Internal Timing Generator */ + {ADV7533_MAIN, 0xAF, 0x16}, + /* HDMI Mode Select */ + {ADV7533_CEC_DSI, 0x78, 0x03}, + /* HDMI Output Enable */ + {ADV7533_MAIN, 0x40, 0x80}, + /* GC Packet Enable */ + {ADV7533_MAIN, 0x4C, 0x04}, + /* Colour Depth 24-bit per pixel */ + {ADV7533_MAIN, 0x49, 0x00}, + /* Down Dither Output 8-bit Colour Depth */ + {ADV7533_CEC_DSI, 0x05, 0xC8}, + /* ADI Required Write */ + {ADV7533_CEC_DSI, 0xBE, 0x3D}, + /* Test Pattern Disable (0x55[7] = 0) */ + {ADV7533_CEC_DSI, 0x55, 0x00}, +}; + +static struct adv7533_reg_cfg tg_cfg_pattern_1080p[] = { + /* 4 lanes */ + {ADV7533_CEC_DSI, 0x1C, 0x40}, + /* hsync and vsync active low */ + {ADV7533_MAIN, 0x17, 0x02}, + /* Control for Pixel Clock Divider */ + {ADV7533_CEC_DSI, 0x16, 0x00}, + /* Timing Generator Enable */ + {ADV7533_CEC_DSI, 0x27, 0xCB}, + /* h_width 0x898 2200*/ + {ADV7533_CEC_DSI, 0x28, 0x89}, + {ADV7533_CEC_DSI, 0x29, 0x80}, + /* hsync_width 0x2C 44*/ + {ADV7533_CEC_DSI, 0x2A, 0x02}, + {ADV7533_CEC_DSI, 0x2B, 0xC0}, + /* hfp 0x58 88 */ + {ADV7533_CEC_DSI, 0x2C, 0x05}, + {ADV7533_CEC_DSI, 0x2D, 0x80}, + /* hbp 0x94 148 */ + {ADV7533_CEC_DSI, 0x2E, 0x09}, + {ADV7533_CEC_DSI, 0x2F, 0x40}, + /* v_total 0x465 1125 */ + {ADV7533_CEC_DSI, 0x30, 0x46}, + {ADV7533_CEC_DSI, 0x31, 0x50}, + /* vsync_width 0x05 5*/ + {ADV7533_CEC_DSI, 0x32, 0x00}, + {ADV7533_CEC_DSI, 0x33, 0x50}, + /* vfp 0x04 4 */ + {ADV7533_CEC_DSI, 0x34, 0x00}, + {ADV7533_CEC_DSI, 0x35, 0x40}, + /* vbp 0x24 36 */ + {ADV7533_CEC_DSI, 0x36, 0x02}, + {ADV7533_CEC_DSI, 0x37, 0x40}, + /* Timing Generator Enable */ + {ADV7533_CEC_DSI, 0x27, 0xCB}, + {ADV7533_CEC_DSI, 0x27, 0x8B}, + {ADV7533_CEC_DSI, 0x27, 0xCB}, + /* Reset Internal Timing Generator */ + {ADV7533_MAIN, 0xAF, 0x16}, + /* HDMI Mode Select */ + {ADV7533_CEC_DSI, 0x78, 0x03}, + /* HDMI Output Enable */ + {ADV7533_MAIN, 0x40, 0x80}, + /* GC Packet Enable */ + {ADV7533_MAIN, 0x4C, 0x04}, + /* Colour Depth 24-bit per pixel */ + {ADV7533_MAIN, 0x49, 0x00}, + /* Down Dither Output 8-bit Colour Depth */ + {ADV7533_CEC_DSI, 0x05, 0xC8}, + /* ADI Required Write */ + {ADV7533_CEC_DSI, 0xBE, 0x3D}, + /* Test Pattern Enable (0x55[7] = 1) */ + {ADV7533_CEC_DSI, 0x55, 0x80}, +}; + +static struct adv7533_reg_cfg tg_cfg_720p[] = { + {ADV7533_CEC_DSI, 0x1C, 0x30}, + /* hsync and vsync active low */ + {ADV7533_MAIN, 0x17, 0x02}, + /* Control for Pixel Clock Divider */ + {ADV7533_CEC_DSI, 0x16, 0x24}, + /* h_width 0x898 2200*/ + {ADV7533_CEC_DSI, 0x28, 0x67}, + {ADV7533_CEC_DSI, 0x29, 0x20}, + /* hsync_width 0x2C 44*/ + {ADV7533_CEC_DSI, 0x2A, 0x02}, + {ADV7533_CEC_DSI, 0x2B, 0x80}, + /* hfp 0x58 88 */ + {ADV7533_CEC_DSI, 0x2C, 0x06}, + {ADV7533_CEC_DSI, 0x2D, 0xE0}, + /* hbp 0x94 148 */ + {ADV7533_CEC_DSI, 0x2E, 0x0D}, + {ADV7533_CEC_DSI, 0x2F, 0xC0}, + /* v_total 0x465 1125 */ + {ADV7533_CEC_DSI, 0x30, 0x2E}, + {ADV7533_CEC_DSI, 0x31, 0xE0}, + /* vsync_width 0x05 5*/ + {ADV7533_CEC_DSI, 0x32, 0x00}, + {ADV7533_CEC_DSI, 0x33, 0x50}, + /* vfp 0x04 4 */ + {ADV7533_CEC_DSI, 0x34, 0x00}, + {ADV7533_CEC_DSI, 0x35, 0x50}, + /* vbp 0x24 36 */ + {ADV7533_CEC_DSI, 0x36, 0x01}, + {ADV7533_CEC_DSI, 0x37, 0x40}, + /* Test Pattern Disable (0x55[7] = 0) */ + {ADV7533_CEC_DSI, 0x55, 0x00}, + /* HDMI disabled */ + {ADV7533_CEC_DSI, 0x03, 0x09}, + /* HDMI enabled */ + {ADV7533_CEC_DSI, 0x03, 0x89}, +}; + +static struct adv7533_reg_cfg tg_cfg_pattern_720p[] = { + {ADV7533_CEC_DSI, 0x1C, 0x30}, + /* hsync and vsync active low */ + {ADV7533_MAIN, 0x17, 0x02}, + /* Control for Pixel Clock Divider */ + {ADV7533_CEC_DSI, 0x16, 0x24}, + /* h_width 0x898 2200*/ + {ADV7533_CEC_DSI, 0x28, 0x67}, + {ADV7533_CEC_DSI, 0x29, 0x20}, + /* hsync_width 0x2C 44*/ + {ADV7533_CEC_DSI, 0x2A, 0x02}, + {ADV7533_CEC_DSI, 0x2B, 0x80}, + /* hfp 0x58 88 */ + {ADV7533_CEC_DSI, 0x2C, 0x06}, + {ADV7533_CEC_DSI, 0x2D, 0xE0}, + /* hbp 0x94 148 */ + {ADV7533_CEC_DSI, 0x2E, 0x0D}, + {ADV7533_CEC_DSI, 0x2F, 0xC0}, + /* v_total 0x465 1125 */ + {ADV7533_CEC_DSI, 0x30, 0x2E}, + {ADV7533_CEC_DSI, 0x31, 0xE0}, + /* vsync_width 0x05 5*/ + {ADV7533_CEC_DSI, 0x32, 0x00}, + {ADV7533_CEC_DSI, 0x33, 0x50}, + /* vfp 0x04 4 */ + {ADV7533_CEC_DSI, 0x34, 0x00}, + {ADV7533_CEC_DSI, 0x35, 0x50}, + /* vbp 0x24 36 */ + {ADV7533_CEC_DSI, 0x36, 0x01}, + {ADV7533_CEC_DSI, 0x37, 0x40}, + /* DSI Internal Timing Generator Enable register bit (0x27[7] = 1) */ + {ADV7533_CEC_DSI, 0x27, 0x8B}, + /* Test Pattern Enable (0x55[7] = 1) */ + {ADV7533_CEC_DSI, 0x55, 0x80}, + /* DSI Internal Timing Generator Reset Enable + register bit (0x27[6] = 0) */ + {ADV7533_CEC_DSI, 0x27, 0x8B}, + /* DSI Internal Timing Generator Reset Enable + register bit (0x27[6] = 1) */ + {ADV7533_CEC_DSI, 0x27, 0xCB}, + + {ADV7533_CEC_DSI, 0x03, 0x09},/* HDMI disabled */ + {ADV7533_CEC_DSI, 0x03, 0x89},/* HDMI enabled */ +}; + +static struct adv7533_reg_cfg I2S_cfg[] = { + {ADV7533_MAIN, 0x0D, 0x18}, /* Bit width = 16Bits*/ + {ADV7533_MAIN, 0x15, 0x20}, /* Sampling Frequency = 48kHz*/ + {ADV7533_MAIN, 0x02, 0x18}, /* N value 6144 --> 0x1800*/ + {ADV7533_MAIN, 0x14, 0x02}, /* Word Length = 16Bits*/ + {ADV7533_MAIN, 0x73, 0x01}, /* Channel Count = 2 channels */ +}; + +static struct adv7533_reg_cfg irq_config[] = { + {ADV7533_CEC_DSI, 0x38, 0xCE}, + {ADV7533_CEC_DSI, 0x38, 0xC0}, +}; + +static struct i2c_client *client; +static struct adv7533_platform_data *pdata; +static char mdss_mdp_panel[MDSS_MAX_PANEL_LEN]; + +/* + * If i2c read or write fails, wait for 100ms to try again, and try + * max 3 times. + */ +#define MAX_WAIT_TIME (100) +#define MAX_RW_TRIES (3) + +static int adv7533_read(u8 addr, u8 reg, u8 *buf, u8 len) +{ + int ret = 0, i = 0; + struct i2c_msg msg[2]; + + if (!client) { + pr_err("%s: no adv7533 i2c client\n", __func__); + ret = -ENODEV; + goto r_err; + } + + if (NULL == buf) { + pr_err("%s: no adv7533 i2c client\n", __func__); + ret = -EINVAL; + goto r_err; + } + + client->addr = addr; + + msg[0].addr = addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = ® + + msg[1].addr = addr; + msg[1].flags = I2C_M_RD; + msg[1].len = len; + msg[1].buf = buf; + + do { + if (i2c_transfer(client->adapter, msg, 2) == 2) { + ret = 0; + goto r_err; + } + msleep(MAX_WAIT_TIME); + } while (++i < MAX_RW_TRIES); + + ret = -EIO; + pr_err("%s adv7533 i2c read failed after %d tries\n", __func__, + MAX_RW_TRIES); + +r_err: + return ret; +} + +int adv7533_read_byte(u8 addr, u8 reg, u8 *buf) +{ + return adv7533_read(addr, reg, buf, 1); +} + +static int adv7533_write_byte(u8 addr, u8 reg, u8 val) +{ + int ret = 0, i = 0; + u8 buf[2] = {reg, val}; + struct i2c_msg msg[1]; + + if (!client) { + pr_err("%s: no adv7533 i2c client\n", __func__); + ret = -ENODEV; + goto w_err; + } + + client->addr = addr; + + msg[0].addr = addr; + msg[0].flags = 0; + msg[0].len = 2; + msg[0].buf = buf; + + do { + if (i2c_transfer(client->adapter, msg, 1) >= 1) { + ret = 0; + goto w_err; + } + msleep(MAX_WAIT_TIME); + } while (++i < MAX_RW_TRIES); + + ret = -EIO; + pr_err("%s: adv7533 i2c write failed after %d tries\n", __func__, + MAX_RW_TRIES); + +w_err: + if (ret != 0) + pr_err("%s: Exiting with ret = %d after %d retries\n", + __func__, ret, i); + return ret; +} + +static int adv7533_write_regs(struct adv7533_platform_data *pdata, + struct adv7533_reg_cfg *cfg, int size) +{ + int ret = 0; + int i; + + for (i = 0; i < size; i++) { + switch (cfg[i].i2c_addr) { + case ADV7533_MAIN: + ret = adv7533_write_byte(pdata->main_i2c_addr, + cfg[i].reg, cfg[i].val); + if (ret != 0) + pr_err("%s: adv7533_write_byte returned %d\n", + __func__, ret); + break; + case ADV7533_CEC_DSI: + ret = adv7533_write_byte(pdata->cec_dsi_i2c_addr, + cfg[i].reg, cfg[i].val); + if (ret != 0) + pr_err("%s: adv7533_write_byte returned %d\n", + __func__, ret); + break; + default: + ret = -EINVAL; + pr_err("%s: Default case? BUG!\n", __func__); + break; + } + if (ret != 0) { + pr_err("%s: adv7533 reg writes failed. ", __func__); + pr_err("Last write %02X to %02X\n", + cfg[i].val, cfg[i].reg); + goto w_regs_fail; + } + } + +w_regs_fail: + if (ret != 0) + pr_err("%s: Exiting with ret = %d after %d writes\n", + __func__, ret, i); + return ret; +} + +static int adv7533_read_device_rev(void) +{ + u8 rev = 0; + int ret; + + ret = adv7533_read_byte(ADV7533_MAIN, ADV7533_REG_CHIP_REVISION, + &rev); + + if (!ret) + pr_debug("%s: adv7533 revision 0x%X\n", __func__, rev); + else + pr_err("%s: adv7533 rev error\n", __func__); + + return ret; +} + +static int adv7533_parse_dt(struct device *dev, + struct adv7533_platform_data *pdata) +{ + struct device_node *np = dev->of_node; + u32 temp_val; + int ret = 0; + + ret = of_property_read_u32(np, "adi,main-addr", &temp_val); + pr_debug("%s: DT property %s is %X\n", __func__, "adi,main-addr", + temp_val); + if (ret) + goto end; + pdata->main_i2c_addr = (u8)temp_val; + + ret = of_property_read_u32(np, "adi,cec-dsi-addr", &temp_val); + pr_debug("%s: DT property %s is %X\n", __func__, "adi,cec-dsi-addr", + temp_val); + if (ret) + goto end; + pdata->cec_dsi_i2c_addr = (u8)temp_val; + + ret = of_property_read_u32(np, "adi,video-mode", &temp_val); + pr_debug("%s: DT property %s is %X\n", __func__, "adi,video-mode", + temp_val); + if (ret) + goto end; + pdata->video_mode = (u8)temp_val; + + pdata->audio = of_property_read_bool(np, "adi,enable-audio"); + + /* Get pinctrl if target uses pinctrl */ + pdata->ts_pinctrl = devm_pinctrl_get(dev); + if (IS_ERR_OR_NULL(pdata->ts_pinctrl)) { + ret = PTR_ERR(pdata->ts_pinctrl); + pr_err("%s: Pincontrol DT property returned %X\n", + __func__, ret); + } + + pdata->pinctrl_state_active = pinctrl_lookup_state(pdata->ts_pinctrl, + "pmx_adv7533_active"); + if (IS_ERR_OR_NULL(pdata->pinctrl_state_active)) { + ret = PTR_ERR(pdata->pinctrl_state_active); + pr_err("Can not lookup %s pinstate %d\n", + PINCTRL_STATE_ACTIVE, ret); + } + + pdata->pinctrl_state_suspend = pinctrl_lookup_state(pdata->ts_pinctrl, + "pmx_adv7533_suspend"); + if (IS_ERR_OR_NULL(pdata->pinctrl_state_suspend)) { + ret = PTR_ERR(pdata->pinctrl_state_suspend); + pr_err("Can not lookup %s pinstate %d\n", + PINCTRL_STATE_SUSPEND, ret); + } + + pdata->disable_gpios = of_property_read_bool(np, + "adi,disable-gpios"); + + if (!(pdata->disable_gpios)) { + pdata->irq_gpio = of_get_named_gpio_flags(np, + "adi,irq-gpio", 0, &pdata->irq_flags); + + pdata->hpd_irq_gpio = of_get_named_gpio_flags(np, + "adi,hpd-irq-gpio", 0, + &pdata->hpd_irq_flags); + + pdata->switch_gpio = of_get_named_gpio_flags(np, + "adi,switch-gpio", 0, &pdata->switch_flags); + } + +end: + return ret; +} + +static int adv7533_gpio_configure(struct adv7533_platform_data *pdata, + bool on) +{ + int ret = 0; + + if (pdata->disable_gpios) + return 0; + + if (on) { + if (gpio_is_valid(pdata->irq_gpio)) { + ret = gpio_request(pdata->irq_gpio, "adv7533_irq_gpio"); + if (ret) { + pr_err("unable to request gpio [%d]\n", + pdata->irq_gpio); + goto err_none; + } + ret = gpio_direction_input(pdata->irq_gpio); + if (ret) { + pr_err("unable to set dir for gpio[%d]\n", + pdata->irq_gpio); + goto err_irq_gpio; + } + } else { + pr_err("irq gpio not provided\n"); + goto err_none; + } + + if (gpio_is_valid(pdata->hpd_irq_gpio)) { + ret = gpio_request(pdata->hpd_irq_gpio, + "adv7533_hpd_irq_gpio"); + if (ret) { + pr_err("unable to request gpio [%d]\n", + pdata->hpd_irq_gpio); + goto err_irq_gpio; + } + ret = gpio_direction_input(pdata->hpd_irq_gpio); + if (ret) { + pr_err("unable to set dir for gpio[%d]\n", + pdata->hpd_irq_gpio); + goto err_hpd_irq_gpio; + } + } else { + pr_err("hpd irq gpio not provided\n"); + goto err_irq_gpio; + } + + if (gpio_is_valid(pdata->switch_gpio)) { + ret = gpio_request(pdata->switch_gpio, + "adv7533_switch_gpio"); + if (ret) { + pr_err("unable to request gpio [%d]\n", + pdata->switch_gpio); + goto err_hpd_irq_gpio; + } + + ret = gpio_direction_output(pdata->switch_gpio, 1); + if (ret) { + pr_err("unable to set dir for gpio [%d]\n", + pdata->switch_gpio); + goto err_switch_gpio; + } + + gpio_set_value(pdata->switch_gpio, 1); + msleep(ADV7533_RESET_DELAY); + } + + goto err_none; + } else { + if (gpio_is_valid(pdata->irq_gpio)) + gpio_free(pdata->irq_gpio); + if (gpio_is_valid(pdata->hpd_irq_gpio)) + gpio_free(pdata->hpd_irq_gpio); + if (gpio_is_valid(pdata->switch_gpio)) + gpio_free(pdata->switch_gpio); + + return 0; + } + +err_switch_gpio: + if (gpio_is_valid(pdata->switch_gpio)) + gpio_free(pdata->switch_gpio); +err_hpd_irq_gpio: + if (gpio_is_valid(pdata->hpd_irq_gpio)) + gpio_free(pdata->hpd_irq_gpio); +err_irq_gpio: + if (gpio_is_valid(pdata->irq_gpio)) + gpio_free(pdata->irq_gpio); +err_none: + return ret; +} + +static void adv7533_get_cmdline_config(void) +{ + int len = 0; + char *t; + + len = strlen(mdss_mdp_panel); + + if (len <= 0) + return; + + t = strnstr(mdss_mdp_panel, "hdmi", MDSS_MAX_PANEL_LEN); + if (t) + pdata->adv_output = true; + else + pdata->adv_output = false; + + t = strnstr(mdss_mdp_panel, "720", MDSS_MAX_PANEL_LEN); + if (t) + pdata->video_mode = ADV7533_VIDEO_720P; + + t = strnstr(mdss_mdp_panel, "1080", MDSS_MAX_PANEL_LEN); + if (t) + pdata->video_mode = ADV7533_VIDEO_1080P; +} + +static struct i2c_device_id adv7533_id[] = { + { "adv7533", 0}, + {} +}; + +static int adv7533_probe(struct i2c_client *client_, + const struct i2c_device_id *id) +{ + int ret = 0; + + client = client_; + + if (client->dev.of_node) { + pdata = devm_kzalloc(&client->dev, + sizeof(struct adv7533_platform_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + ret = adv7533_parse_dt(&client->dev, pdata); + if (ret) { + pr_err("%s: Failed to parse DT\n", __func__); + goto p_err; + } + } + + ret = adv7533_read_device_rev(); + if (ret != 0) { + pr_err("%s: Failed to read revision\n", __func__); + goto p_err; + } + + ret = pinctrl_select_state(pdata->ts_pinctrl, + pdata->pinctrl_state_active); + if (ret < 0) + pr_err("%s: Failed to select %s pinstate %d\n", + __func__, PINCTRL_STATE_ACTIVE, ret); + + pdata->adv_output = true; + adv7533_get_cmdline_config(); + + if (!(pdata->disable_gpios)) { + ret = adv7533_gpio_configure(pdata, true); + if (ret) { + pr_err("%s: Failed to configure GPIOs\n", __func__); + goto p_err; + } + + if (pdata->adv_output) { + gpio_set_value(pdata->switch_gpio, 0); + } else { + gpio_set_value(pdata->switch_gpio, 1); + goto p_err; + } + } + + ret = adv7533_write_regs(pdata, setup_cfg, ARRAY_SIZE(setup_cfg)); + if (ret != 0) { + pr_err("%s: Failed to write common config\n", __func__); + goto p_err; + } + + switch (pdata->video_mode) { + case ADV7533_VIDEO_PATTERN: + ret = adv7533_write_regs(pdata, tg_cfg_pattern_1080p, + ARRAY_SIZE(tg_cfg_pattern_1080p)); + if (ret != 0) { + pr_err("%s: adv7533 pattern config i2c fails [%d]\n", + __func__, ret); + goto p_err; + } + break; + case ADV7533_VIDEO_480P: + case ADV7533_VIDEO_720P: + ret = adv7533_write_regs(pdata, tg_cfg_pattern_720p, + ARRAY_SIZE(tg_cfg_pattern_720p)); + ret = adv7533_write_regs(pdata, tg_cfg_720p, + ARRAY_SIZE(tg_cfg_720p)); + if (ret != 0) { + pr_err("%s: adv7533 pattern config i2c fails [%d]\n", + __func__, ret); + goto p_err; + } + break; + case ADV7533_VIDEO_1080P: + default: + ret = adv7533_write_regs(pdata, tg_cfg_1080p, + ARRAY_SIZE(tg_cfg_1080p)); + if (ret != 0) { + pr_err("%s: adv7533 1080p config i2c fails [%d]\n", + __func__, ret); + goto p_err; + } + break; + } + + ret = adv7533_write_regs(pdata, irq_config, ARRAY_SIZE(irq_config)); + if (ret != 0) { + pr_err("%s: adv7533 interrupt config i2c fails with ret = %d!\n", + __func__, ret); + goto p_err; + } + + if (pdata->audio) { + ret = adv7533_write_regs(pdata, I2S_cfg, ARRAY_SIZE(I2S_cfg)); + if (ret != 0) { + pr_err("%s: I2S configuration fail = %d!\n", + __func__, ret); + goto p_err; + } + } + + pm_runtime_enable(&client->dev); + pm_runtime_set_active(&client->dev); + + return 0; + +p_err: + adv7533_gpio_configure(pdata, false); + devm_kfree(&client->dev, pdata); + return ret; +} + +static int adv7533_remove(struct i2c_client *client) +{ + int ret = 0; + + pm_runtime_disable(&client->dev); + ret = adv7533_gpio_configure(pdata, false); + + devm_kfree(&client->dev, pdata); + return ret; +} + +static struct i2c_driver adv7533_driver = { + .driver = { + .name = "adv7533", + .owner = THIS_MODULE, + }, + .probe = adv7533_probe, + .remove = adv7533_remove, + .id_table = adv7533_id, +}; + +static int __init adv7533_init(void) +{ + return i2c_add_driver(&adv7533_driver); +} + +static void __exit adv7533_exit(void) +{ + i2c_del_driver(&adv7533_driver); +} + +module_param_string(panel, mdss_mdp_panel, MDSS_MAX_PANEL_LEN, 0); + +module_init(adv7533_init); +module_exit(adv7533_exit); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("adv7533 driver");