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 <ilialin@codeaurora.org>
Signed-off-by: Siddharth Zaveri <szaveri@codeaurora.org>
[cip@codeaurora.org: Moved adb7533.c location]
Signed-off-by: Clarence Ip <cip@codeaurora.org>
This commit is contained in:
Ilia Lin 2015-05-06 10:22:01 +03:00 committed by David Keitel
parent 3d30cc8dde
commit 311c44cffc
4 changed files with 859 additions and 1 deletions

View file

@ -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>;
};
};
};

View file

@ -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.

View file

@ -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

View file

@ -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 <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#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 = &reg;
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");