From cbba37b0bbb959a5229e677cf0b8780cce927128 Mon Sep 17 00:00:00 2001 From: yzq Date: Tue, 27 Mar 2012 20:09:59 -0700 Subject: [PATCH] rk610 hdmi lcd lvds tvout codec support --- arch/arm/mach-rk29/board-rk29-ddr3sdk.c | 29 + arch/arm/mach-rk29/clock.c | 4 +- drivers/mfd/Kconfig | 7 + drivers/mfd/Makefile | 1 + drivers/mfd/rk610-core.c | 272 ++++ drivers/video/display/Kconfig | 3 + drivers/video/display/Makefile | 4 +- drivers/video/display/display-sys.c | 404 ++++++ drivers/video/display/display-sysfs.c | 219 ---- drivers/video/display/lcd/Kconfig | 6 + drivers/video/display/lcd/Makefile | 4 + drivers/video/display/lcd/rk610_lcd.c | 300 +++++ drivers/video/display/lcd/rk610_lcd.h | 165 +++ drivers/video/display/screen/Kconfig | 10 + drivers/video/display/screen/Makefile | 2 + .../video/display/screen/lcd_hdmi_1024x768.c | 153 +++ .../video/display/screen/lcd_hdmi_800x480.c | 271 ++++ drivers/video/display/screen/lcd_hsd800x480.c | 24 +- drivers/video/display/screen/screen.h | 67 +- drivers/video/display/tve/Kconfig | 13 + drivers/video/display/tve/Makefile | 6 + drivers/video/display/tve/rk610_tv.c | 246 ++++ drivers/video/display/tve/rk610_tv.h | 129 ++ drivers/video/display/tve/rk610_tv_cvbs.c | 209 +++ drivers/video/display/tve/rk610_tv_ypbpr.c | 229 ++++ drivers/video/hdmi/Kconfig | 10 +- drivers/video/hdmi/chips/Kconfig | 5 + drivers/video/hdmi/chips/Makefile | 4 +- .../video/hdmi/chips/{ => anx7150}/anx7150.c | 0 .../video/hdmi/chips/{ => anx7150}/anx7150.h | 0 .../hdmi/chips/{ => anx7150}/anx7150_hw.c | 0 .../hdmi/chips/{ => anx7150}/anx7150_hw.h | 0 drivers/video/hdmi/chips/rk610/rk610_hdmi.c | 299 +++++ drivers/video/hdmi/chips/rk610/rk610_hdmi.h | 36 + .../video/hdmi/chips/rk610/rk610_hdmi_hw.c | 1094 ++++++++++++++++ .../video/hdmi/chips/rk610/rk610_hdmi_hw.h | 208 +++ drivers/video/hdmi/hdmi-core.c | 20 +- drivers/video/hdmi/hdmi-fb.c | 30 +- drivers/video/rk29_fb.c | 30 +- include/linux/display-sys.h | 69 + include/linux/hdmi.h | 2 +- include/linux/mfd/rk610_core.h | 142 ++ sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 3 +- sound/soc/codecs/rk610_codec.c | 1137 +++++++++++++++++ sound/soc/codecs/rk610_codec.h | 267 ++++ sound/soc/rk29/Kconfig | 23 +- sound/soc/rk29/Makefile | 6 + sound/soc/rk29/rk29_jetta_codec.c | 280 ++++ 49 files changed, 6178 insertions(+), 269 deletions(-) create mode 100644 drivers/mfd/rk610-core.c create mode 100644 drivers/video/display/display-sys.c delete mode 100644 drivers/video/display/display-sysfs.c create mode 100644 drivers/video/display/lcd/Kconfig create mode 100644 drivers/video/display/lcd/Makefile create mode 100644 drivers/video/display/lcd/rk610_lcd.c create mode 100644 drivers/video/display/lcd/rk610_lcd.h create mode 100644 drivers/video/display/screen/lcd_hdmi_1024x768.c create mode 100644 drivers/video/display/screen/lcd_hdmi_800x480.c create mode 100644 drivers/video/display/tve/Kconfig create mode 100644 drivers/video/display/tve/Makefile create mode 100644 drivers/video/display/tve/rk610_tv.c create mode 100644 drivers/video/display/tve/rk610_tv.h create mode 100644 drivers/video/display/tve/rk610_tv_cvbs.c create mode 100644 drivers/video/display/tve/rk610_tv_ypbpr.c rename drivers/video/hdmi/chips/{ => anx7150}/anx7150.c (100%) mode change 100755 => 100644 rename drivers/video/hdmi/chips/{ => anx7150}/anx7150.h (100%) mode change 100755 => 100644 rename drivers/video/hdmi/chips/{ => anx7150}/anx7150_hw.c (100%) rename drivers/video/hdmi/chips/{ => anx7150}/anx7150_hw.h (100%) mode change 100755 => 100644 create mode 100644 drivers/video/hdmi/chips/rk610/rk610_hdmi.c create mode 100644 drivers/video/hdmi/chips/rk610/rk610_hdmi.h create mode 100644 drivers/video/hdmi/chips/rk610/rk610_hdmi_hw.c create mode 100644 drivers/video/hdmi/chips/rk610/rk610_hdmi_hw.h create mode 100644 include/linux/display-sys.h create mode 100644 include/linux/mfd/rk610_core.h create mode 100644 sound/soc/codecs/rk610_codec.c create mode 100644 sound/soc/codecs/rk610_codec.h create mode 100644 sound/soc/rk29/rk29_jetta_codec.c diff --git a/arch/arm/mach-rk29/board-rk29-ddr3sdk.c b/arch/arm/mach-rk29/board-rk29-ddr3sdk.c index 4bae125b8657..0fbcc942a229 100755 --- a/arch/arm/mach-rk29/board-rk29-ddr3sdk.c +++ b/arch/arm/mach-rk29/board-rk29-ddr3sdk.c @@ -1705,6 +1705,35 @@ static struct i2c_board_info __initdata board_i2c1_devices[] = { .platform_data = &bu92747guw_pdata, }, #endif +#ifdef CONFIG_MFD_RK610 + { + .type = "rk610_ctl", + .addr = 0x40, + .flags = 0, + }, +#endif +#ifdef CONFIG_RK610_TVOUT + { + .type = "rk610_tvout", + .addr = 0x42, + .flags = 0, + }, +#endif +#ifdef CONFIG_RK610_HDMI + { + .type = "rk610_hdmi", + .addr = 0x46, + .flags = 0, + .irq = RK29_PIN1_PD7, + }, +#endif +#ifdef CONFIG_SND_SOC_RK610 + { + .type = "rk610_i2c_codec", + .addr = 0x60, + .flags = 0, + }, +#endif }; #endif diff --git a/arch/arm/mach-rk29/clock.c b/arch/arm/mach-rk29/clock.c index d88ad3ac6ca7..f9fdc9985bd2 100755 --- a/arch/arm/mach-rk29/clock.c +++ b/arch/arm/mach-rk29/clock.c @@ -626,8 +626,8 @@ static const struct codec_pll_set codec_pll[] = { // rate parent band NR NF NO CODEC_PLL(108000, 24, LOW, 1, 18, 4), // for TV CODEC_PLL(648000, 24, HIGH, 1, 27, 1), - CODEC_PLL(148500, 27, LOW, 1, 22, 4), // for HDMI - CODEC_PLL(297000, 27, LOW, 1, 22, 2), + CODEC_PLL(148500, 27, LOW, 2, 88, 8), //change for jetta hdmi dclk jitter 20120322// for HDMI + CODEC_PLL(297000, 27, LOW, 2, 88, 4), //change for jetta hdmi dclk jitter 20120322// for HDMI CODEC_PLL(445500, 27, LOW, 2, 33, 1), CODEC_PLL(594000, 27, HIGH, 1, 22, 1), CODEC_PLL(891000, 27, HIGH, 1, 33, 1), diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index b91fe3180fd6..af404a902e2f 100755 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -742,6 +742,13 @@ config MFD_TPS65910 config TPS65911_COMPARATOR tristate +config MFD_RK610 + bool "RK610(Jetta) Multimedia support" + depends on I2C=y && GPIOLIB + select MFD_CORE + help + if you say yes here you get support for the RK610, with func as + HDMI LCD LVDS TVOUT CODEC. endif # MFD_SUPPORT menu "Multimedia Capabilities Port drivers" diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 670e4c0fca63..d0188cbcf8d3 100755 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -96,3 +96,4 @@ obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o obj-$(CONFIG_MFD_TPS65910) += tps65910.o tps65910-irq.o obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o +obj-$(CONFIG_MFD_RK610) += rk610-core.o \ No newline at end of file diff --git a/drivers/mfd/rk610-core.c b/drivers/mfd/rk610-core.c new file mode 100644 index 000000000000..93e24d65c816 --- /dev/null +++ b/drivers/mfd/rk610-core.c @@ -0,0 +1,272 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RK610_RESET_PIN RK29_PIN6_PC1 + +static struct i2c_client *rk610_control_client = NULL; +#ifdef CONFIG_RK610_LCD +extern int rk610_lcd_init(struct i2c_client *client); +#else +int rk610_lcd_init(struct i2c_client *client){} +#endif +int rk610_control_send_byte(const char reg, const char data) +{ + int ret; + + printk("reg = 0x%02x, val=0x%02x\n", reg ,data); + + if(rk610_control_client == NULL) + return -1; + //i2c_master_reg8_send + ret = i2c_master_reg8_send(rk610_control_client, reg, &data, 1, 100*1000); + if (ret > 0) + ret = 0; + + return ret; +} + +#ifdef CONFIG_SND_SOC_RK610 +static unsigned int current_pll_value = 0; +int rk610_codec_pll_set(unsigned int rate) +{ + char N, M, NO, DIV; + unsigned int F; + char data; + + if(current_pll_value == rate) + return 0; + + // Input clock is 12MHz. + if(rate == 11289600) { + // For 11.2896MHz, N = 2 M= 75 F = 0.264(0x43958) NO = 8 + N = 2; + NO = 3; + M = 75; + F = 0x43958; + DIV = 5; + } + else if(rate == 12288000) { + // For 12.2888MHz, N = 2 M= 75 F = 0.92(0xEB851) NO = 8 + N = 2; + NO = 3; + M = 75; + F = 0xEB851; + DIV = 5; + } + else { + printk(KERN_ERR "[%s] not support such frequency\n", __FUNCTION__); + return -1; + } + + //Enable codec pll fractional number and power down. + data = 0x00; + rk610_control_send_byte(RK610_CONTROL_REG_C_PLL_CON5, data); + msleep(10); + + data = (N << 4) | NO; + rk610_control_send_byte(RK610_CONTROL_REG_C_PLL_CON0, data); + // M + data = M; + rk610_control_send_byte(RK610_CONTROL_REG_C_PLL_CON1, data); + // F + data = F & 0xFF; + rk610_control_send_byte(RK610_CONTROL_REG_C_PLL_CON2, data); + data = (F >> 8) & 0xFF; + rk610_control_send_byte(RK610_CONTROL_REG_C_PLL_CON3, data); + data = (F >> 16) & 0xFF; + rk610_control_send_byte(RK610_CONTROL_REG_C_PLL_CON4, data); + + // i2s mclk = codec_pll/5; + i2c_master_reg8_recv(rk610_control_client, RK610_CONTROL_REG_CLOCK_CON1, &data, 1, 100*1000); + data &= ~CLOCK_CON1_I2S_DVIDER_MASK; + data |= (DIV - 1); + rk610_control_send_byte(RK610_CONTROL_REG_CLOCK_CON1, data); + + // Power up codec pll. + data |= C_PLL_POWER_ON; + rk610_control_send_byte(RK610_CONTROL_REG_C_PLL_CON5, data); + + current_pll_value = rate; + printk(KERN_ERR "[%s] rate %u\n", __FUNCTION__, rate); + + return 0; +} + +void rk610_control_init_codec(void) +{ + struct i2c_client *client = rk610_control_client; + char data = 0; + int ret; + + if(rk610_control_client == NULL) + return; + printk(KERN_ERR "[%s] start\n", __FUNCTION__); + + //gpio_set_value(RK610_RESET_PIN, GPIO_LOW); //reset rk601 + // mdelay(100); + //gpio_set_value(RK610_RESET_PIN, GPIO_HIGH); + //mdelay(100); + + // Set i2c glitch timeout. + data = 0x22; + ret = i2c_master_reg8_send(client, RK610_CONTROL_REG_I2C_CON, &data, 1, 20*1000); + +// rk610_codec_pll_set(11289600); + + //use internal codec, enable DAC ADC LRCK output. +// i2c_master_reg8_recv(client, RK610_CONTROL_REG_CODEC_CON, &data, 1, 100*1000); +// data = CODEC_CON_BIT_DAC_LRCL_OUTPUT_DISABLE | CODEC_CON_BIT_ADC_LRCK_OUTPUT_DISABLE; +// data = CODEC_CON_BIT_ADC_LRCK_OUTPUT_DISABLE; + data = 0; + rk610_control_send_byte(RK610_CONTROL_REG_CODEC_CON, data); + + // Select internal i2s clock from codec_pll. + i2c_master_reg8_recv(rk610_control_client, RK610_CONTROL_REG_CLOCK_CON1, &data, 1, 100*1000); +// data |= CLOCK_CON1_I2S_CLK_CODEC_PLL; + data = 0; + rk610_control_send_byte(RK610_CONTROL_REG_CLOCK_CON1, data); + + i2c_master_reg8_recv(client, RK610_CONTROL_REG_CODEC_CON, &data, 1, 100*1000); + printk(KERN_ERR "[%s] RK610_CONTROL_REG_CODEC_CON is %x\n", __FUNCTION__, data); + + i2c_master_reg8_recv(client, RK610_CONTROL_REG_CLOCK_CON1, &data, 1, 100*1000); + printk(KERN_ERR "[%s] RK610_CONTROL_REG_CLOCK_CON1 is %x\n", __FUNCTION__, data); +} +#endif +#ifdef CONFIG_RK610_DEBUG +static int rk610_read_p0_reg(struct i2c_client *client, char reg, char *val) +{ + return i2c_master_reg8_recv(client, reg, val, 1, 100*1000) > 0? 0: -EINVAL; +} + +static int rk610_write_p0_reg(struct i2c_client *client, char reg, char *val) +{ + return i2c_master_reg8_send(client, reg, val, 1, 100*1000) > 0? 0: -EINVAL; +} +static ssize_t rk610_show_reg_attrs(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + + int i,size=0; + char val; + struct i2c_client *client=rk610_control_client; + + for(i=0;i<256;i++) + { + rk610_read_p0_reg(client, i, &val); + if(i%16==0) + size += sprintf(buf+size,"\n>>>rk610_hdmi %x:",i); + size += sprintf(buf+size," %2x",val); + } + + return size; +} +static ssize_t rk610_store_reg_attrs(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct i2c_client *client=NULL; + static char val=0,reg=0; + client = rk610_control_client; + printk("/**********rk610 reg config******/"); + + sscanf(buf, "%x%x", &val,®); + printk("reg=%x val=%x\n",reg,val); + rk610_write_p0_reg(client, reg, &val); + printk("val=%x\n",val); + return size; +} + +static struct device_attribute rk610_attrs[] = { + __ATTR(reg_ctl, 0777,rk610_show_reg_attrs,rk610_store_reg_attrs), +}; +#endif +static int rk610_control_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct clk *iis_clk; + + iis_clk = clk_get_sys("rk29_i2s.0", "i2s"); + if (IS_ERR(iis_clk)) { + printk("failed to get i2s clk\n"); + ret = PTR_ERR(iis_clk); + }else{ + printk("got i2s clk ok!\n"); + clk_enable(iis_clk); + clk_set_rate(iis_clk, 11289600); + rk29_mux_api_set(GPIO2D0_I2S0CLK_MIIRXCLKIN_NAME, GPIO2H_I2S0_CLK); + clk_put(iis_clk); + } + + rk610_control_client = client; + msleep(100); + if(RK610_RESET_PIN != INVALID_GPIO) { + ret = gpio_request(RK610_RESET_PIN, "rk610 reset"); + if (ret){ + printk(KERN_ERR "rk610_control_probe request gpio fail\n"); + } + else { + printk(KERN_ERR "rk610_control_probe request gpio ok\n"); + gpio_direction_output(RK610_RESET_PIN, GPIO_HIGH); + msleep(100); + gpio_direction_output(RK610_RESET_PIN, GPIO_LOW); + msleep(100); + gpio_set_value(RK610_RESET_PIN, GPIO_HIGH); + } + } + rk610_lcd_init(client); + #ifdef CONFIG_RK610_DEBUG + device_create_file(&(client->dev), &rk610_attrs[0]); + #endif + return 0; +} + +static int rk610_control_remove(struct i2c_client *client) +{ + return 0; +} + +static const struct i2c_device_id rk610_control_id[] = { + { "rk610_ctl", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rk610_control_id); + +static struct i2c_driver rk610_control_driver = { + .driver = { + .name = "rk610_ctl", + }, + .probe = rk610_control_probe, + .remove = rk610_control_remove, + .id_table = rk610_control_id, +}; + +static int __init rk610_control_init(void) +{ + return i2c_add_driver(&rk610_control_driver); +} + +static void __exit rk610_control_exit(void) +{ + i2c_del_driver(&rk610_control_driver); +} + +fs_initcall(rk610_control_init); +//module_init(rk610_control_init); +module_exit(rk610_control_exit); + + +MODULE_DESCRIPTION("RK610 control driver"); +MODULE_AUTHOR("Rock-chips, "); +MODULE_LICENSE("GPL"); + diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig index 1b497d4c64fb..98ea0494b6bb 100644 --- a/drivers/video/display/Kconfig +++ b/drivers/video/display/Kconfig @@ -22,5 +22,8 @@ config DISPLAY_SUPPORT comment "Display hardware drivers" depends on DISPLAY_SUPPORT source "drivers/video/display/screen/Kconfig" +source "drivers/video/display/lcd/Kconfig" +source "drivers/video/display/tve/Kconfig" endmenu + diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile index c7d6d8b6b696..ad3bc7ff7c72 100644 --- a/drivers/video/display/Makefile +++ b/drivers/video/display/Makefile @@ -1,6 +1,8 @@ # Display drivers -display-objs := display-sysfs.o +display-objs := display-sys.o obj-$(CONFIG_DISPLAY_SUPPORT) += display.o obj-$(CONFIG_DISPLAY_SUPPORT) += screen/ +obj-y += lcd/ +obj-y += tve/ \ No newline at end of file diff --git a/drivers/video/display/display-sys.c b/drivers/video/display/display-sys.c new file mode 100644 index 000000000000..3501d40a379c --- /dev/null +++ b/drivers/video/display/display-sys.c @@ -0,0 +1,404 @@ +#include +#include +#include +#include +#include +#include + +static struct list_head display_device_list; + +static ssize_t display_show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rk_display_device *dsp = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%s\n", dsp->name); +} + +static ssize_t display_show_type(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rk_display_device *dsp = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%s\n", dsp->type); +} + +static ssize_t display_show_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rk_display_device *dsp = dev_get_drvdata(dev); + int enable; + if(dsp->ops && dsp->ops->getenable) + enable = dsp->ops->getenable(dsp); + else + return 0; + return snprintf(buf, PAGE_SIZE, "%d\n", enable); +} + +static ssize_t display_store_enable(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct rk_display_device *dsp = dev_get_drvdata(dev); + int enable; + + sscanf(buf, "%d", &enable); + if(dsp->ops && dsp->ops->setenable) + dsp->ops->setenable(dsp, enable); + return size; +} + +static ssize_t display_show_connect(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rk_display_device *dsp = dev_get_drvdata(dev); + int connect; + if(dsp->ops && dsp->ops->getstatus) + connect = dsp->ops->getstatus(dsp); + else + return 0; + return snprintf(buf, PAGE_SIZE, "%d\n", connect); +} + +static int mode_string(char *buf, unsigned int offset, + const struct fb_videomode *mode) +{ +// char m = 'U'; + char v = 'p'; + +// if (mode->flag & FB_MODE_IS_DETAILED) +// m = 'D'; +// if (mode->flag & FB_MODE_IS_VESA) +// m = 'V'; +// if (mode->flag & FB_MODE_IS_STANDARD) +// m = 'S'; + + if (mode->vmode & FB_VMODE_INTERLACED) + v = 'i'; + if (mode->vmode & FB_VMODE_DOUBLE) + v = 'd'; + + return snprintf(&buf[offset], PAGE_SIZE - offset, "%dx%d%c-%d\n", + mode->xres, mode->yres, v, mode->refresh); +} +static ssize_t display_show_modes(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rk_display_device *dsp = dev_get_drvdata(dev); + struct list_head *modelist, *pos; + struct fb_modelist *fb_modelist; + const struct fb_videomode *mode; + int i; + if(dsp->ops && dsp->ops->getmodelist) + { + if(dsp->ops->getmodelist(dsp, &modelist)) + return -EINVAL; + } + else + return 0; + + i = 0; + list_for_each(pos, modelist) { + fb_modelist = list_entry(pos, struct fb_modelist, list); + mode = &fb_modelist->mode; + i += mode_string(buf, i, mode); + } + return i; +} + +static ssize_t display_show_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rk_display_device *dsp = dev_get_drvdata(dev); + struct fb_videomode mode; + + if(dsp->ops && dsp->ops->getmode) + if(dsp->ops->getmode(dsp, &mode) == 0) + return mode_string(buf, 0, &mode); + return 0; +} + +static ssize_t display_store_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rk_display_device *dsp = dev_get_drvdata(dev); + char mstr[100]; + struct list_head *modelist, *pos; + struct fb_modelist *fb_modelist; + struct fb_videomode *mode; + size_t i; + + if(dsp->ops && dsp->ops->getmodelist) + { + if(dsp->ops && dsp->ops->getmodelist) + { + if(dsp->ops->getmodelist(dsp, &modelist)) + return -EINVAL; + } + list_for_each(pos, modelist) { + fb_modelist = list_entry(pos, struct fb_modelist, list); + mode = &fb_modelist->mode; + i = mode_string(mstr, 0, mode); + if (strncmp(mstr, buf, max(count, i)) == 0) { + if(dsp->ops && dsp->ops->setmode) + dsp->ops->setmode(dsp, mode); + return count; + } + } + } + return -EINVAL; +} + +static struct device_attribute display_attrs[] = { + __ATTR(name, S_IRUGO, display_show_name, NULL), + __ATTR(type, S_IRUGO, display_show_type, NULL), + __ATTR(enable, S_IRUGO | /*S_IWUGO*/S_IWUSR, display_show_enable, display_store_enable), + __ATTR(connect, S_IRUGO, display_show_connect, NULL), + __ATTR(modes, S_IRUGO, display_show_modes, NULL), + __ATTR(mode, S_IRUGO | /*S_IWUGO*/S_IWUSR, display_show_mode, display_store_mode) +}; + +static int display_suspend(struct device *dev, pm_message_t state) +{ + struct rk_display_device *dsp = dev_get_drvdata(dev); + + mutex_lock(&dsp->lock); + if (likely(dsp->driver->suspend)) + dsp->driver->suspend(dsp, state); + mutex_unlock(&dsp->lock); + return 0; +}; + +static int display_resume(struct device *dev) +{ + struct rk_display_device *dsp = dev_get_drvdata(dev); + + mutex_lock(&dsp->lock); + if (likely(dsp->driver->resume)) + dsp->driver->resume(dsp); + mutex_unlock(&dsp->lock); + return 0; +}; + +void rk_display_device_enable(struct rk_display_device *ddev) +{ +#ifndef CONFIG_DISPLAY_AUTO_SWITCH + return; +#else + struct list_head *pos, *head = &display_device_list; + struct rk_display_device *dev = NULL, *dev_enabled = NULL, *dev_enable = NULL; + int enable = 0,connect, has_connect = 0; + + list_for_each(pos, head) { + dev = list_entry(pos, struct rk_display_device, list); + enable = dev->ops->getenable(dev); + connect = dev->ops->getstatus(dev); + if(connect) + dev_enable = dev; + if(enable == 1) + dev_enabled = dev; + } + // If no device is connected, enable highest priority device. + if(dev_enable == NULL) { + dev->ops->setenable(dev, 1); + return; + } + + if(dev_enable == dev_enabled) { + if(dev_enable != ddev) + ddev->ops->setenable(ddev, 0); + } + else { + if(dev_enabled) + dev_enabled->ops->setenable(dev_enabled, 0); + dev_enable->ops->setenable(dev_enable, 1); + } + + +#endif +} +EXPORT_SYMBOL(rk_display_device_enable); + +void rk_display_device_enable_other(struct rk_display_device *ddev) +{ +#ifndef CONFIG_DISPLAY_AUTO_SWITCH + return; +#else + struct list_head *pos, *head = &display_device_list; + struct rk_display_device *dev; + int connect = 0; + + list_for_each_prev(pos, head) { + dev = list_entry(pos, struct rk_display_device, list); + if(dev != ddev) + { + connect = dev->ops->getstatus(dev); + if(connect) + { + dev->ops->setenable(dev, 1); + return; + } + } + } +#endif +} +EXPORT_SYMBOL(rk_display_device_enable_other); + +void rk_display_device_disable_other(struct rk_display_device *ddev) +{ +#ifndef CONFIG_DISPLAY_AUTO_SWITCH + return; +#else + struct list_head *pos, *head = &display_device_list; + struct rk_display_device *dev; + int enable = 0; + + list_for_each(pos, head) { + dev = list_entry(pos, struct rk_display_device, list); + if(dev != ddev) + { + enable = dev->ops->getenable(dev); + if(enable) + dev->ops->setenable(dev, 0); + } + } + ddev->ops->setenable(ddev, 1); +#endif +} +EXPORT_SYMBOL(rk_display_device_disable_other); + +void rk_display_device_select(int priority) +{ + struct list_head *pos, *head = &display_device_list; + struct rk_display_device *dev; + int enable, found = 0; + + list_for_each(pos, head) { + dev = list_entry(pos, struct rk_display_device, list); + if(dev->priority == priority) + found = 1; + } + + if(!found) + { + printk("[%s] select display interface %d not exist\n", __FUNCTION__, priority); + return; + } + + list_for_each(pos, head) { + dev = list_entry(pos, struct rk_display_device, list); + enable = dev->ops->getenable(dev); + if(dev->priority == priority) + { + if(!enable) + dev->ops->setenable(dev, 1); + } + else if(enable) + dev->ops->setenable(dev, 0); + } +} +EXPORT_SYMBOL(rk_display_device_select); +static struct mutex allocated_dsp_lock; +static DEFINE_IDR(allocated_dsp); +static struct class *display_class; + +struct rk_display_device *rk_display_device_register(struct rk_display_driver *driver, + struct device *parent, void *devdata) +{ + struct rk_display_device *new_dev = NULL; + int ret = -EINVAL; + + if (unlikely(!driver)) + return ERR_PTR(ret); + + mutex_lock(&allocated_dsp_lock); + ret = idr_pre_get(&allocated_dsp, GFP_KERNEL); + mutex_unlock(&allocated_dsp_lock); + if (!ret) + return ERR_PTR(ret); + + new_dev = kzalloc(sizeof(struct rk_display_device), GFP_KERNEL); + if (likely(new_dev) && unlikely(driver->probe(new_dev, devdata))) { + // Reserve the index for this display + mutex_lock(&allocated_dsp_lock); + ret = idr_get_new(&allocated_dsp, new_dev, &new_dev->idx); + mutex_unlock(&allocated_dsp_lock); + + if (!ret) { + new_dev->dev = device_create(display_class, parent, + MKDEV(0, 0), new_dev, + "%s", new_dev->type); + if (!IS_ERR(new_dev->dev)) { + new_dev->parent = parent; + new_dev->driver = driver; + new_dev->dev->driver = parent->driver; + mutex_init(&new_dev->lock); + // Add new device to display device list. + { + struct list_head *pos, *head = &display_device_list; + struct rk_display_device *dev; + + list_for_each(pos, head) { + dev = list_entry(pos, struct rk_display_device, list); + if(dev->priority > new_dev->priority) + break; + } + list_add_tail(&new_dev->list, pos); + } + return new_dev; + } + mutex_lock(&allocated_dsp_lock); + idr_remove(&allocated_dsp, new_dev->idx); + mutex_unlock(&allocated_dsp_lock); + ret = -EINVAL; + } + } + kfree(new_dev); + return ERR_PTR(ret); +} +EXPORT_SYMBOL(rk_display_device_register); + +void rk_display_device_unregister(struct rk_display_device *ddev) +{ + if (!ddev) + return; + // Free device + mutex_lock(&ddev->lock); + device_unregister(ddev->dev); + mutex_unlock(&ddev->lock); + // Mark device index as avaliable + mutex_lock(&allocated_dsp_lock); + idr_remove(&allocated_dsp, ddev->idx); + mutex_unlock(&allocated_dsp_lock); + list_del(&ddev->list); + kfree(ddev); +} +EXPORT_SYMBOL(rk_display_device_unregister); + +static int __init rk_display_class_init(void) +{ + display_class = class_create(THIS_MODULE, "display"); + if (IS_ERR(display_class)) { + printk(KERN_ERR "Failed to create display class\n"); + display_class = NULL; + return -EINVAL; + } + display_class->dev_attrs = display_attrs; + display_class->suspend = display_suspend; + display_class->resume = display_resume; + mutex_init(&allocated_dsp_lock); + INIT_LIST_HEAD(&display_device_list); + return 0; +} + +static void __exit rk_display_class_exit(void) +{ + class_destroy(display_class); +} + +subsys_initcall(rk_display_class_init); +module_exit(rk_display_class_exit); + + +MODULE_AUTHOR("zhengyang@rock-chips.com"); +MODULE_DESCRIPTION("Driver for rk display device"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/display/display-sysfs.c b/drivers/video/display/display-sysfs.c deleted file mode 100644 index 0c647d7af0ee..000000000000 --- a/drivers/video/display/display-sysfs.c +++ /dev/null @@ -1,219 +0,0 @@ -/* - * display-sysfs.c - Display output driver sysfs interface - * - * Copyright (C) 2007 James Simmons - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ -#include -#include -#include -#include -#include -#include -#include - -static ssize_t display_show_name(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct display_device *dsp = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%s\n", dsp->name); -} - -static ssize_t display_show_type(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct display_device *dsp = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%s\n", dsp->type); -} - -static ssize_t display_show_contrast(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct display_device *dsp = dev_get_drvdata(dev); - ssize_t rc = -ENXIO; - - mutex_lock(&dsp->lock); - if (likely(dsp->driver) && dsp->driver->get_contrast) - rc = sprintf(buf, "%d\n", dsp->driver->get_contrast(dsp)); - mutex_unlock(&dsp->lock); - return rc; -} - -static ssize_t display_store_contrast(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct display_device *dsp = dev_get_drvdata(dev); - ssize_t ret = -EINVAL, size; - int contrast; - char *endp; - - contrast = simple_strtoul(buf, &endp, 0); - size = endp - buf; - - if (isspace(*endp)) - size++; - - if (size != count) - return ret; - - mutex_lock(&dsp->lock); - if (likely(dsp->driver && dsp->driver->set_contrast)) { - pr_debug("display: set contrast to %d\n", contrast); - dsp->driver->set_contrast(dsp, contrast); - ret = count; - } - mutex_unlock(&dsp->lock); - return ret; -} - -static ssize_t display_show_max_contrast(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct display_device *dsp = dev_get_drvdata(dev); - ssize_t rc = -ENXIO; - - mutex_lock(&dsp->lock); - if (likely(dsp->driver)) - rc = sprintf(buf, "%d\n", dsp->driver->max_contrast); - mutex_unlock(&dsp->lock); - return rc; -} - -static struct device_attribute display_attrs[] = { - __ATTR(name, S_IRUGO, display_show_name, NULL), - __ATTR(type, S_IRUGO, display_show_type, NULL), - __ATTR(contrast, S_IRUGO | S_IWUSR, display_show_contrast, display_store_contrast), - __ATTR(max_contrast, S_IRUGO, display_show_max_contrast, NULL), -}; - -static int display_suspend(struct device *dev, pm_message_t state) -{ - struct display_device *dsp = dev_get_drvdata(dev); - - mutex_lock(&dsp->lock); - if (likely(dsp->driver->suspend)) - dsp->driver->suspend(dsp, state); - mutex_unlock(&dsp->lock); - return 0; -}; - -static int display_resume(struct device *dev) -{ - struct display_device *dsp = dev_get_drvdata(dev); - - mutex_lock(&dsp->lock); - if (likely(dsp->driver->resume)) - dsp->driver->resume(dsp); - mutex_unlock(&dsp->lock); - return 0; -}; - -static struct mutex allocated_dsp_lock; -static DEFINE_IDR(allocated_dsp); -static struct class *display_class; - -struct display_device *display_device_register(struct display_driver *driver, - struct device *parent, void *devdata) -{ - struct display_device *new_dev = NULL; - int ret = -EINVAL; - - if (unlikely(!driver)) - return ERR_PTR(ret); - - mutex_lock(&allocated_dsp_lock); - ret = idr_pre_get(&allocated_dsp, GFP_KERNEL); - mutex_unlock(&allocated_dsp_lock); - if (!ret) - return ERR_PTR(ret); - - new_dev = kzalloc(sizeof(struct display_device), GFP_KERNEL); - if (likely(new_dev) && unlikely(driver->probe(new_dev, devdata))) { - // Reserve the index for this display - mutex_lock(&allocated_dsp_lock); - ret = idr_get_new(&allocated_dsp, new_dev, &new_dev->idx); - mutex_unlock(&allocated_dsp_lock); - - if (!ret) { - new_dev->dev = device_create(display_class, parent, - MKDEV(0, 0), new_dev, - "display%d", new_dev->idx); - if (!IS_ERR(new_dev->dev)) { - new_dev->parent = parent; - new_dev->driver = driver; - mutex_init(&new_dev->lock); - return new_dev; - } - mutex_lock(&allocated_dsp_lock); - idr_remove(&allocated_dsp, new_dev->idx); - mutex_unlock(&allocated_dsp_lock); - ret = -EINVAL; - } - } - kfree(new_dev); - return ERR_PTR(ret); -} -EXPORT_SYMBOL(display_device_register); - -void display_device_unregister(struct display_device *ddev) -{ - if (!ddev) - return; - // Free device - mutex_lock(&ddev->lock); - device_unregister(ddev->dev); - mutex_unlock(&ddev->lock); - // Mark device index as available - mutex_lock(&allocated_dsp_lock); - idr_remove(&allocated_dsp, ddev->idx); - mutex_unlock(&allocated_dsp_lock); - kfree(ddev); -} -EXPORT_SYMBOL(display_device_unregister); - -static int __init display_class_init(void) -{ - display_class = class_create(THIS_MODULE, "display"); - if (IS_ERR(display_class)) { - printk(KERN_ERR "Failed to create display class\n"); - display_class = NULL; - return -EINVAL; - } - display_class->dev_attrs = display_attrs; - display_class->suspend = display_suspend; - display_class->resume = display_resume; - mutex_init(&allocated_dsp_lock); - return 0; -} - -static void __exit display_class_exit(void) -{ - class_destroy(display_class); -} - -module_init(display_class_init); -module_exit(display_class_exit); - -MODULE_DESCRIPTION("Display Hardware handling"); -MODULE_AUTHOR("James Simmons "); -MODULE_LICENSE("GPL"); - diff --git a/drivers/video/display/lcd/Kconfig b/drivers/video/display/lcd/Kconfig new file mode 100644 index 000000000000..3d15a66d7974 --- /dev/null +++ b/drivers/video/display/lcd/Kconfig @@ -0,0 +1,6 @@ +config RK610_LCD + bool "RK610(Jetta) lcd support" + depends on MFD_RK610 + default y if MFD_RK610 + help + Support Jetta(RK610) to output LCD1 and LVDS. diff --git a/drivers/video/display/lcd/Makefile b/drivers/video/display/lcd/Makefile new file mode 100644 index 000000000000..a124d84e794f --- /dev/null +++ b/drivers/video/display/lcd/Makefile @@ -0,0 +1,4 @@ +# +# Makefile for the jetta tv control. +# +obj-$(CONFIG_RK610_LCD) += rk610_lcd.o diff --git a/drivers/video/display/lcd/rk610_lcd.c b/drivers/video/display/lcd/rk610_lcd.c new file mode 100644 index 000000000000..4a400b46aa14 --- /dev/null +++ b/drivers/video/display/lcd/rk610_lcd.c @@ -0,0 +1,300 @@ +#include +#include +#include +#include +#include + +#include +#include "rk610_lcd.h" +#include +#include "../../rk29_fb.h" +static struct i2c_client *rk610_g_lcd_client=NULL; +//static int rk610_scaler_read_p0_reg(struct i2c_client *client, char reg, char *val) +//{ + //return i2c_master_reg8_recv(client, reg, val, 1, 100*1000) > 0? 0: -EINVAL; +//} + +static int rk610_scaler_write_p0_reg(struct i2c_client *client, char reg, char *val) +{ + return i2c_master_reg8_send(client, reg, val, 1, 100*1000) > 0? 0: -EINVAL; +} +static void rk610_scaler_pll_enable(struct i2c_client *client) +{ + char c; + RK610_DBG(&client->dev,"%s \n",__FUNCTION__); + c = S_PLL_PWR(0)|S_PLL_RESET(0)|S_PLL_BYPASS(0); + rk610_scaler_write_p0_reg(client, S_PLL_CON2, &c); +} +static void rk610_scaler_pll_disable(struct i2c_client *client) +{ + char c; + RK610_DBG(&client->dev,"%s \n",__FUNCTION__); + c = S_PLL_PWR(1) |S_PLL_RESET(0) |S_PLL_BYPASS(1); + rk610_scaler_write_p0_reg(client, S_PLL_CON2, &c); +} +static void rk610_scaler_enable(struct i2c_client *client) +{ + char c; + RK610_DBG(&client->dev,"%s \n",__FUNCTION__); + + c= SCL_BYPASS(0) |SCL_DEN_INV(0) |SCL_H_V_SYNC_INV(0) |SCL_OUT_CLK_INV(0) |SCL_ENABLE(ENABLE); + rk610_scaler_write_p0_reg(client, SCL_CON0, &c); +} +static void rk610_scaler_disable(struct i2c_client *client) +{ + char c; + RK610_DBG(&client->dev,"%s \n",__FUNCTION__); + + c= SCL_BYPASS(1) |SCL_DEN_INV(0) |SCL_H_V_SYNC_INV(0) |SCL_OUT_CLK_INV(0) |SCL_ENABLE(DISABLE); + rk610_scaler_write_p0_reg(client, SCL_CON0, &c); +} +static int rk610_output_config(struct i2c_client *client,struct rk29fb_screen *screen,bool enable) +{ + char c=0; + RK610_DBG(&client->dev,"%s \n",__FUNCTION__); + if(SCREEN_LVDS == screen->type){ + c = LVDS_OUT_CLK_PIN(0) |LVDS_OUT_CLK_PWR_PIN(1) |LVDS_PLL_PWR_PIN(0) \ + |LVDS_LANE_IN_FORMAT(DATA_D0_MSB) |LVDS_INPUT_SOURCE(FROM_LCD0_OR_SCL) \ + |LVDS_OUTPUT_FORMAT(screen->hw_format) ; + rk610_scaler_write_p0_reg(client, LVDS_CON0, &c); + c = LVDS_OUT_ENABLE(0x0) |LVDS_TX_PWR_ENABLE(0x0); + rk610_scaler_write_p0_reg(client, LVDS_CON1, &c); + }else if(SCREEN_RGB == screen->type){ + c = LCD1_OUT_ENABLE(LCD1_AS_OUT) | LCD1_OUT_SRC(enable?LCD1_FROM_SCL : LCD1_FROM_LCD0); + rk610_scaler_write_p0_reg(client, LCD1_CON, &c); + } + return 0; +} +#ifdef CONFIG_HDMI_DUAL_DISP +static int rk610_scaler_pll_set(struct i2c_client *client,struct rk29fb_screen *screen,u32 clkin ) +{ + char c=0; + char M=0,N=0,OD=0; + RK610_DBG(&client->dev,"%s \n",__FUNCTION__); + /***************SET SCALER PLL FROM CLKIN ,DIV 0*/ + if(screen->s_pixclock != 0){ + OD = (screen->s_pixclock)&0x3; + N = (screen->s_pixclock >>4)&0xf; + M = (screen->s_pixclock >>8)&0xff; + }else { + RK610_ERR(&client->dev,"RK610 Scaler pll not support rate \n"); + } + c = S_PLL_FROM_DIV<<3 | S_PLL_DIV(0); + rk610_scaler_write_p0_reg(client, CLOCK_CON0, &c); + + c = S_DIV_N(N)| S_DIV_OD(OD); + rk610_scaler_write_p0_reg(client, S_PLL_CON0, &c); + c = S_DIV_M(M); + rk610_scaler_write_p0_reg(client, S_PLL_CON1, &c); + rk610_scaler_pll_enable(client); + return 0; +} + + +static int scale_hv_factor(struct i2c_client *client ,u32 Hin_act, u32 Hout_act, u32 Vin_act, u32 Vout_act) + { + char c; + u32 hfactor_f,vfactor_f,scl_factor_f; + int hfactor; + int vfactor; + struct scl_hv_info HV2; + hfactor_f = ((Hin_act-1)*4096)/(Hout_act-1); + if(hfactor_f==4096) + {hfactor = 0x1000;} + else if(hfactor_f>(int)hfactor_f) + {hfactor = (int)hfactor_f+1;} + else + {hfactor = (int)hfactor_f;} + + scl_factor_f = Vin_act/Vout_act; + if(scl_factor_f<2) + {vfactor_f = ((Vin_act-1)*4096)/(Vout_act-1);} + else + {vfactor_f = ((Vin_act-2)*4096)/(Vout_act-1);} + if(vfactor_f==4096) + {vfactor = 0x1000;} + else if(vfactor_f>(int)vfactor_f) + {vfactor = (int)vfactor_f+1;} + else + {vfactor = (int)vfactor_f;} + + HV2.scl_h= hfactor; + HV2.scl_v= vfactor; + /* SCL FACTOR */ + c = SCL_H_FACTOR_LSB(HV2.scl_h); + rk610_scaler_write_p0_reg(client, SCL_CON1, &c); + c = SCL_H_FACTOR_MSB(HV2.scl_h); + rk610_scaler_write_p0_reg(client, SCL_CON2, &c); + + c = SCL_V_FACTOR_LSB(HV2.scl_v); + rk610_scaler_write_p0_reg(client, SCL_CON3, &c); + c = SCL_V_FACTOR_MSB(HV2.scl_v); + rk610_scaler_write_p0_reg(client, SCL_CON4, &c); + return 0; + } + +static int rk610_scaler_fator_config(struct i2c_client *client ,struct rk29fb_screen *screen) +{ + switch(screen->hdmi_resolution){ + case HDMI_1920x1080p_60Hz: + case HDMI_1920x1080p_50Hz: + rk610_scaler_pll_set(client,screen,148500000); + /***************set scaler factor********************/ + scale_hv_factor(client,1920,screen->x_res,1080,screen->y_res); + break; + case HDMI_1280x720p_60Hz: + case HDMI_1280x720p_50Hz: + rk610_scaler_pll_set(client,screen,74250000); + /***************set scaler factor********************/ + scale_hv_factor(client,1280,screen->x_res,720,screen->y_res); + break; + case HDMI_720x576p_50Hz_16x9: + case HDMI_720x576p_50Hz_4x3: + rk610_scaler_pll_set(client,screen,27000000); + /***************set scaler factor********************/ + scale_hv_factor(client,720,screen->x_res,576,screen->y_res); + break; + case HDMI_720x480p_60Hz_16x9: + case HDMI_720x480p_60Hz_4x3: + rk610_scaler_pll_set(client,screen,27000000); + /***************set scaler factor********************/ + scale_hv_factor(client,720,screen->x_res,480,screen->y_res); + break; + default : + RK610_ERR(&client->dev,"RK610 not support dual display at hdmi resolution=%d \n",screen->hdmi_resolution); + return -1; + break; + } +} +static int rk610_scaler_output_timing_config(struct i2c_client *client,struct rk29fb_screen *screen) +{ + char c; + int h_st = screen->s_hsync_st; + int hs_end = screen->s_hsync_len; + int h_act_st = hs_end + screen->s_left_margin; + int xres = screen->x_res; + int h_act_end = h_act_st + xres; + int h_total = h_act_end + screen->s_right_margin; + int v_st = screen->s_vsync_st; + int vs_end = screen->s_vsync_len; + int v_act_st = vs_end + screen->s_upper_margin; + int yres = screen->y_res; + int v_act_end = v_act_st + yres; + int v_total = v_act_end + screen->s_lower_margin; + + /* SCL display Frame start point */ + c = SCL_DSP_HST_LSB(h_st); + rk610_scaler_write_p0_reg(client, SCL_CON5, &c); + c = SCL_DSP_HST_MSB(h_st); + rk610_scaler_write_p0_reg(client, SCL_CON6, &c); + + c = SCL_DSP_VST_LSB(v_st); + rk610_scaler_write_p0_reg(client, SCL_CON7, &c); + c = SCL_DSP_VST_MSB(v_st); + rk610_scaler_write_p0_reg(client, SCL_CON8, &c); + /* SCL output timing */ + + c = SCL_DSP_HTOTAL_LSB(h_total); + rk610_scaler_write_p0_reg(client, SCL_CON9, &c); + c = SCL_DSP_HTOTAL_MSB(h_total); + rk610_scaler_write_p0_reg(client, SCL_CON10, &c); + + c = SCL_DSP_HS_END(hs_end); + rk610_scaler_write_p0_reg(client, SCL_CON11, &c); + + c = SCL_DSP_HACT_ST_LSB(h_act_st); + rk610_scaler_write_p0_reg(client, SCL_CON12, &c); + c = SCL_DSP_HACT_ST_MSB(h_act_st); + rk610_scaler_write_p0_reg(client, SCL_CON13, &c); + + c = SCL_DSP_HACT_END_LSB(h_act_end); + rk610_scaler_write_p0_reg(client, SCL_CON14, &c); + c = SCL_DSP_HACT_END_MSB(h_act_end); + rk610_scaler_write_p0_reg(client, SCL_CON15, &c); + + c = SCL_DSP_VTOTAL_LSB(v_total); + rk610_scaler_write_p0_reg(client, SCL_CON16, &c); + c = SCL_DSP_VTOTAL_MSB(v_total); + rk610_scaler_write_p0_reg(client, SCL_CON17, &c); + + c = SCL_DSP_VS_END(vs_end); + rk610_scaler_write_p0_reg(client, SCL_CON18, &c); + + c = SCL_DSP_VACT_ST(v_act_st); + rk610_scaler_write_p0_reg(client, SCL_CON19, &c); + + c = SCL_DSP_VACT_END_LSB(v_act_end); + rk610_scaler_write_p0_reg(client, SCL_CON20, &c); + c = SCL_DSP_VACT_END_MSB(v_act_end); + rk610_scaler_write_p0_reg(client, SCL_CON21, &c); + + c = SCL_H_BORD_ST_LSB(h_act_st); + rk610_scaler_write_p0_reg(client, SCL_CON22, &c); + c = SCL_H_BORD_ST_MSB(h_act_st); + rk610_scaler_write_p0_reg(client, SCL_CON23, &c); + + c = SCL_H_BORD_END_LSB(h_act_end); + rk610_scaler_write_p0_reg(client, SCL_CON24, &c); + c = SCL_H_BORD_END_MSB(h_act_end); + rk610_scaler_write_p0_reg(client, SCL_CON25, &c); + + c = SCL_V_BORD_ST(v_act_st); + rk610_scaler_write_p0_reg(client, SCL_CON26, &c); + + c = SCL_V_BORD_END_LSB(v_act_end); + rk610_scaler_write_p0_reg(client, SCL_CON27, &c); + c = SCL_V_BORD_END_MSB(v_act_end); + rk610_scaler_write_p0_reg(client, SCL_CON28, &c); + + return 0; +} +static int rk610_scaler_chg(struct i2c_client *client ,struct rk29fb_screen *screen) +{ + + RK610_DBG(&client->dev,"%s screen->hdmi_resolution=%d\n",__FUNCTION__,screen->hdmi_resolution); + rk610_scaler_fator_config(client,screen); + rk610_scaler_enable(client); + rk610_scaler_output_timing_config(client,screen); + + return 0; + +} +#endif +static int rk610_lcd_scaler_bypass(struct i2c_client *client,bool enable)//enable:0 bypass 1: scale +{ + RK610_DBG(&client->dev,"%s \n",__FUNCTION__); + + rk610_scaler_pll_disable(client); + rk610_scaler_disable(client); + + return 0; +} +int rk610_lcd_scaler_set_param(struct rk29fb_screen *screen,bool enable )//enable:0 bypass 1: scale +{ + int ret=0; + struct i2c_client *client = rk610_g_lcd_client; + RK610_DBG(&client->dev,"%s \n",__FUNCTION__); + if(client == NULL){ + RK610_ERR(&client->dev,"%s client == NULL FAIL\n",__FUNCTION__); + return -1; + } + +#ifdef CONFIG_HDMI_DUAL_DISP + if(enable == 1){ + rk610_output_config(client,screen,1); + ret = rk610_scaler_chg(client,screen); + } + else +#endif + { + rk610_output_config(client,screen,0); + ret = rk610_lcd_scaler_bypass(client,enable); + } + return ret; +} +int rk610_lcd_init(struct i2c_client *client) +{ + RK610_DBG(&client->dev,"%s \n",__FUNCTION__); + rk610_g_lcd_client = client; + return 0; +} diff --git a/drivers/video/display/lcd/rk610_lcd.h b/drivers/video/display/lcd/rk610_lcd.h new file mode 100644 index 000000000000..06880b3455b3 --- /dev/null +++ b/drivers/video/display/lcd/rk610_lcd.h @@ -0,0 +1,165 @@ +#ifndef _RK610_LCD_H +#define _RK610_LCD_H +#include "../screen/screen.h" +#define ENABLE 1 +#define DISABLE 0 +/* LVDS config */ +/* LVDS ÍⲿÁ¬Ïß½Ó·¨ */ +#define LVDS_8BIT_1 0x00 +#define LVDS_8BIT_2 0x01 +#define LVDS_8BIT_3 0x10 +#define LVDS_6BIT 0x11 +//LVDS lane input format +#define DATA_D0_MSB 0 +#define DATA_D7_MSB 1 +//LVDS input source +#define FROM_LCD1 0 +#define FROM_LCD0_OR_SCL 1 + +/* LCD1 config */ +#define LCD1_AS_IN 0 +#define LCD1_AS_OUT 1 + +//LCD1 output source +#define LCD1_FROM_LCD0 0 +#define LCD1_FROM_SCL 1 + +/* clock config */ +#define S_PLL_FROM_DIV 0 +#define S_PLL_FROM_CLKIN 1 +#define S_PLL_DIV(x) ((x)&0x7) +/*********S_PLL_CON************/ +//S_PLL_CON0 +#define S_DIV_N(x) (((x)&0xf)<<4) +#define S_DIV_OD(x) (((x)&3)<<0) +//S_PLL_CON1 +#define S_DIV_M(x) ((x)&0xff) +//S_PLL_CON2 +#define S_PLL_UNLOCK (0<<7) //0:unlock 1:pll_lock +#define S_PLL_LOCK (1<<7) //0:unlock 1:pll_lock +#define S_PLL_PWR(x) (((x)&1)<<2) //0:POWER UP 1:POWER DOWN +#define S_PLL_RESET(x) (((x)&1)<<1) //0:normal 1:reset M/N dividers +#define S_PLL_BYPASS(x) (((x)&1)<<0) //0:normal 1:bypass +//LVDS_CON0 +#define LVDS_OUT_CLK_PIN(x) (((x)&1)<<7) //clk enable pin, 0: enable +#define LVDS_OUT_CLK_PWR_PIN(x) (((x)&1)<<6) //clk pwr enable pin, 1: enable +#define LVDS_PLL_PWR_PIN(x) (((x)&1)<<5) //pll pwr enable pin, 0:enable +#define LVDS_LANE_IN_FORMAT(x) (((x)&1)<<3) //0: msb on D0 1:msb on D7 +#define LVDS_INPUT_SOURCE(x) (((x)&1)<<2) //0: from lcd1 1:from lcd0 or scaler +#define LVDS_OUTPUT_FORMAT(x) (((x)&3)<<0) //00:8bit format-1 01:8bit format-2 10:8bit format-3 11:6bit format +//LVDS_CON1 +#define LVDS_OUT_ENABLE(x) (((x)&0xf)<<4) //0:output enable 1:output disable +#define LVDS_TX_PWR_ENABLE(x) (((x)&0xf)<<0) //0:working mode 1:power down +//LCD1_CON +#define LCD1_OUT_ENABLE(x) (((x)&1)<<1) //0:lcd1 as input 1:lcd1 as output +#define LCD1_OUT_SRC(x) (((x)&1)<<0) //0:from lcd0 1:from scaler +//SCL_CON0 +#define SCL_BYPASS(x) (((x)&1)<<4) //0:not bypass 1:bypass +#define SCL_DEN_INV(x) (((x)&1)<<3) //scl_den_inv +#define SCL_H_V_SYNC_INV(x) (((x)&1)<<2) //scl_sync_inv +#define SCL_OUT_CLK_INV(x) (((x)&1)<<1) //scl_dclk_inv +#define SCL_ENABLE(x) (((x)&1)<<0) //scaler enable +//SCL_CON1 +#define SCL_H_FACTOR_LSB(x) ((x)&0xff) //scl_h_factor[7:0] +//SCL_CON2 +#define SCL_H_FACTOR_MSB(x) (((x)>>8)&0x3f) //scl_h_factor[13:8] +//SCL_CON3 +#define SCL_V_FACTOR_LSB(x) ((x)&0xff) //scl_v_factor[7:0] +//SCL_CON4 +#define SCL_V_FACTOR_MSB(x) (((x)>>8)&0x3f) //scl_v_factor[13:8] +//SCL_CON5 +#define SCL_DSP_HST_LSB(x) ((x)&0xff) //dsp_frame_hst[7:0] +//SCL_CON6 +#define SCL_DSP_HST_MSB(x) (((x)>>8)&0xf) //dsp_frame_hst[11:8] +//SCL_CON7 +#define SCL_DSP_VST_LSB(x) ((x)&0xff) //dsp_frame_vst[7:0] +//SCL_CON8 +#define SCL_DSP_VST_MSB(x) (((x)>>8)&0xf) //dsp_frame_vst[11:8] +//SCL_CON9 +#define SCL_DSP_HTOTAL_LSB(x) ((x)&0xff) //dsp_frame_htotal[7:0] +//SCL_CON10 +#define SCL_DSP_HTOTAL_MSB(x) (((x)>>8)&0xf) //dsp_frame_htotal[11:8] +//SCL_CON11 +#define SCL_DSP_HS_END(x) ((x)&0xff) //dsp_hs_end +//SCL_CON12 +#define SCL_DSP_HACT_ST_LSB(x) ((x)&0xff) //dsp_hact_st[7:0] +//SCL_CON13 +#define SCL_DSP_HACT_ST_MSB(x) (((x)>>8)&0x3) //dsp_hact_st[9:8] +//SCL_CON14 +#define SCL_DSP_HACT_END_LSB(x) ((x)&0xff) //dsp_hact_end[7:0] +//SCL_CON15 +#define SCL_DSP_HACT_END_MSB(x) (((x)>>8)&0xf) //dsp_frame_htotal[11:8] +//SCL_CON16 +#define SCL_DSP_VTOTAL_LSB(x) ((x)&0xff) //dsp_frame_vtotal[7:0] +//SCL_CON17 +#define SCL_DSP_VTOTAL_MSB(x) (((x)>>8)&0xf) //dsp_frame_vtotal[11:8] +//SCL_CON18 +#define SCL_DSP_VS_END(x) ((x)&0xff) //dsp_vs_end +//SCL_CON19 +#define SCL_DSP_VACT_ST(x) ((x)&0xff) //dsp_vact_st[7:0] +//SCL_CON20 +#define SCL_DSP_VACT_END_LSB(x) ((x)&0xff) //dsp_vact_end[7:0] +//SCL_CON21 +#define SCL_DSP_VACT_END_MSB(x) (((x)>>8)&0xf) //dsp_frame_vtotal[11:8] +//SCL_CON22 +#define SCL_H_BORD_ST_LSB(x) ((x)&0xff) //dsp_hbord_st[7:0] +//SCL_CON23 +#define SCL_H_BORD_ST_MSB(x) (((x)>>8)&0x3) //dsp_hbord_st[9:8] +//SCL_CON24 +#define SCL_H_BORD_END_LSB(x) ((x)&0xff) //dsp_hbord_end[7:0] +//SCL_CON25 +#define SCL_H_BORD_END_MSB(x) (((x)>>8)&0xf) //dsp_hbord_end[11:8] +//SCL_CON26 +#define SCL_V_BORD_ST(x) ((x)&0xff) //dsp_vbord_st[7:0] +//SCL_CON27 +#define SCL_V_BORD_END_LSB(x) ((x)&0xff) //dsp_vbord_end[7:0] +//SCL_CON25 +#define SCL_V_BORD_END_MSB(x) (((x)>>8)&0xf) //dsp_vbord_end[11:8] +#if 0 +/****************LCD STRUCT********/ +#define PLL_CLKOD(i) ((i) & 0x03) +#define PLL_NO_1 PLL_CLKOD(0) +#define PLL_NO_2 PLL_CLKOD(1) +#define PLL_NO_4 PLL_CLKOD(2) +#define PLL_NO_8 PLL_CLKOD(3) +#define SCALE_PLL(_parent_rate , _rate, _m, _n, _od) \ +{ \ + .parent_rate = _parent_rate, \ + .rate = _rate, \ + .m = _m, \ + .n = _n, \ + .od = _od, \ +} +#endif +struct rk610_pll_info{ + u32 parent_rate; + u32 rate; + int m; + int n; + int od; +}; +struct lcd_mode_inf{ + int h_pw; + int h_bp; + int h_vd; + int h_fp; + int v_pw; + int v_bp; + int v_vd; + int v_fp; + int f_hst; + int f_vst; + struct rk610_pll_info pllclk; +}; +struct scl_hv_info{ + int scl_h ; + int scl_v; + }; +struct rk610_lcd_info{ + int enable; + struct scl_hv_info scl; + struct lcd_mode_inf *lcd_mode; +}; +extern int rk610_lcd_init(struct i2c_client *client); +extern int rk610_lcd_scaler_set_param(struct rk29fb_screen *screen,bool enable ); +#endif diff --git a/drivers/video/display/screen/Kconfig b/drivers/video/display/screen/Kconfig index 83dc4da9b065..a93e42cb9ef6 100644 --- a/drivers/video/display/screen/Kconfig +++ b/drivers/video/display/screen/Kconfig @@ -68,6 +68,16 @@ config LCD_A050VL01 bool "RGB A050VL01" config LCD_B101EW05 bool "RGB lcd panel B101EW05" +config LCD_HDMI_1024x768 + depends on MFD_RK610 + bool "RGB Hannstar LCD_HDMI_1024X768" + ---help--- + if support RK610, this setting can support dual screen output +config LCD_HDMI_800x480 + depends on MFD_RK610 + bool "RGB Hannstar LCD_HDMI_800x480" + ---help--- + if support RK610, this setting can support dual screen output endchoice diff --git a/drivers/video/display/screen/Makefile b/drivers/video/display/screen/Makefile index b37569a13afc..fd03740b4aa5 100644 --- a/drivers/video/display/screen/Makefile +++ b/drivers/video/display/screen/Makefile @@ -22,6 +22,8 @@ obj-$(CONFIG_LCD_CPTCLAA038LA31XE) += lcd_CPTclaa038la31xe.o obj-$(CONFIG_LCD_HX8357) += lcd_hx8357.o obj-$(CONFIG_LCD_HSD100PXN) += lcd_hsd100pxn.o +obj-$(CONFIG_LCD_HDMI_1024x768) += lcd_hdmi_1024x768.o +obj-$(CONFIG_LCD_HDMI_800x480) += lcd_hdmi_800x480.o obj-$(CONFIG_LCD_HSD07PFW1) += lcd_hsd07pfw1.o obj-$(CONFIG_LCD_B101AW06) += lcd_B101AW06.o obj-$(CONFIG_LCD_NT35510) += lcd_nt35510.o diff --git a/drivers/video/display/screen/lcd_hdmi_1024x768.c b/drivers/video/display/screen/lcd_hdmi_1024x768.c new file mode 100644 index 000000000000..821cc59b74c7 --- /dev/null +++ b/drivers/video/display/screen/lcd_hdmi_1024x768.c @@ -0,0 +1,153 @@ +#include +#include +#include +#include +#include +#include "screen.h" +#include +#include "../../rk29_fb.h" +#include "../lcd/rk610_lcd.h" + +/* Base */ +#define OUT_TYPE SCREEN_LVDS + +#define OUT_FORMAT LVDS_8BIT_3 +#define OUT_FACE OUT_D888_P666 +#define OUT_CLK 65000000 +#define LCDC_ACLK 500000000//312000000 //29 lcdc axi DMA ƵÂÊ + +/* Timing */ +#define H_PW 48 //10 +#define H_BP 88 //100 +#define H_VD 800 //1024 +#define H_FP 40 //210 + +#define V_PW 3 //10 +#define V_BP 32 //10 +#define V_VD 480 //768 +#define V_FP 13 //18 + +#define LCD_WIDTH 202 +#define LCD_HEIGHT 152 + +/* scaler Timing */ +//1920*1080*60 +#define S_OUT_CLK SCALE_RATE(148500000,66000000) +#define S_H_PW 100 +#define S_H_BP 100 +#define S_H_VD 1024 +#define S_H_FP 151 + +#define S_V_PW 5 +#define S_V_BP 15 +#define S_V_VD 768 +#define S_V_FP 12 + +#define S_H_ST 1757 +#define S_V_ST 14 + +//1920*1080*50 +#define S1_OUT_CLK SCALE_RATE(148500000,54000000) +#define S1_H_PW 100 +#define S1_H_BP 100 +#define S1_H_VD 1024 +#define S1_H_FP 126 + +#define S1_V_PW 5 +#define S1_V_BP 15 +#define S1_V_VD 768 +#define S1_V_FP 12 + +#define S1_H_ST 1757 +#define S1_V_ST 14 +/* Other */ +#define DCLK_POL 0 +#define SWAP_RB 0 +#ifdef CONFIG_HDMI_DUAL_DISP +static int set_scaler_info(struct rk29fb_screen *screen, u8 hdmi_resolution) +{ + switch(hdmi_resolution){ + case HDMI_1920x1080p_60Hz: + /* Scaler Timing */ + screen->hdmi_resolution = hdmi_resolution; + screen->s_pixclock = S_OUT_CLK; + screen->s_hsync_len = S_H_PW; + screen->s_left_margin = S_H_BP; + screen->s_right_margin = S_H_FP; + screen->s_hsync_len = S_H_PW; + screen->s_upper_margin = S_V_BP; + screen->s_lower_margin = S_V_FP; + screen->s_vsync_len = S_V_PW; + screen->s_hsync_st = S_H_ST; + screen->s_vsync_st = S_V_ST; + break; + case HDMI_1920x1080p_50Hz: + /* Scaler Timing */ + screen->hdmi_resolution = hdmi_resolution; + screen->s_pixclock = S1_OUT_CLK; + screen->s_hsync_len = S1_H_PW; + screen->s_left_margin = S1_H_BP; + screen->s_right_margin = S1_H_FP; + screen->s_hsync_len = S1_H_PW; + screen->s_upper_margin = S1_V_BP; + screen->s_lower_margin = S1_V_FP; + screen->s_vsync_len = S1_V_PW; + screen->s_hsync_st = S1_H_ST; + screen->s_vsync_st = S1_V_ST; + break; + default : + printk("%s lcd not support dual display at this hdmi resolution %d \n",__func__,hdmi_resolution); + return -1; + break; + } + + return 0; +} +#else +static int set_scaler_info(struct rk29fb_screen *screen, u8 hdmi_resolution){} +#endif + +void set_lcd_info(struct rk29fb_screen *screen, struct rk29lcd_info *lcd_info ) +{ + /* screen type & face */ + screen->type = OUT_TYPE; + screen->face = OUT_FACE; + + /* Screen size */ + screen->x_res = H_VD; + screen->y_res = V_VD; + + screen->width = LCD_WIDTH; + screen->height = LCD_HEIGHT; + + /* Timing */ + screen->lcdc_aclk = LCDC_ACLK; + screen->pixclock = OUT_CLK; + screen->left_margin = H_BP; + screen->right_margin = H_FP; + screen->hsync_len = H_PW; + screen->upper_margin = V_BP; + screen->lower_margin = V_FP; + screen->vsync_len = V_PW; + + /* Pin polarity */ + screen->pin_hsync = 0; + screen->pin_vsync = 0; + screen->pin_den = 0; + screen->pin_dclk = DCLK_POL; + + /* Swap rule */ + screen->swap_rb = SWAP_RB; + screen->swap_rg = 0; + screen->swap_gb = 0; + screen->swap_delta = 0; + screen->swap_dumy = 0; + + /* Operation function*/ + screen->init = NULL; + screen->standby = NULL; + screen->sscreen_get = set_scaler_info; +#ifdef CONFIG_RK610_LCD + screen->sscreen_set = rk610_lcd_scaler_set_param; +#endif +} \ No newline at end of file diff --git a/drivers/video/display/screen/lcd_hdmi_800x480.c b/drivers/video/display/screen/lcd_hdmi_800x480.c new file mode 100644 index 000000000000..925957702d49 --- /dev/null +++ b/drivers/video/display/screen/lcd_hdmi_800x480.c @@ -0,0 +1,271 @@ +#include +#include +#include +#include +#include +#include "screen.h" +#include +#include "../../rk29_fb.h" +#include "../lcd/rk610_lcd.h" + +/* Base */ +#define OUT_TYPE SCREEN_RGB + +#define OUT_FACE OUT_P888 +#define OUT_CLK 33000000 +#define LCDC_ACLK 150000000//312000000 //29 lcdc axi DMA ƵÂÊ + +/* Timing */ +#define H_PW 1 +#define H_BP 88 +#define H_VD 800 +#define H_FP 40 + +#define V_PW 3 +#define V_BP 29 +#define V_VD 480 +#define V_FP 13 + +#define LCD_WIDTH 154 +#define LCD_HEIGHT 85 + +/* scaler Timing */ +//1920*1080*60 + +#define S_OUT_CLK SCALE_RATE(148500000,33000000) +#define S_H_PW 1 +#define S_H_BP 88 +#define S_H_VD 800 +#define S_H_FP 211 + +#define S_V_PW 3 +#define S_V_BP 10 +#define S_V_VD 480 +#define S_V_FP 7 + +#define S_H_ST 244 +#define S_V_ST 11 + +//1920*1080*50 +#define S1_OUT_CLK SCALE_RATE(148500000,30375000) +#define S1_H_PW 1 +#define S1_H_BP 88 +#define S1_H_VD 800 +#define S1_H_FP 326 + +#define S1_V_PW 3 +#define S1_V_BP 9 +#define S1_V_VD 480 +#define S1_V_FP 8 + +#define S1_H_ST 270 +#define S1_V_ST 13 +//1280*720*60 +#define S2_OUT_CLK SCALE_RATE(74250000,33000000) +#define S2_H_PW 1 +#define S2_H_BP 88 +#define S2_H_VD 800 +#define S2_H_FP 211 + +#define S2_V_PW 3 +#define S2_V_BP 9 +#define S2_V_VD 480 +#define S2_V_FP 8 + +#define S2_H_ST 0 +#define S2_V_ST 8 +//1280*720*50 + +#define S3_OUT_CLK SCALE_RATE(74250000,30375000) +#define S3_H_PW 1 +#define S3_H_BP 88 +#define S3_H_VD 800 +#define S3_H_FP 326 + +#define S3_V_PW 3 +#define S3_V_BP 9 +#define S3_V_VD 480 +#define S3_V_FP 8 + +#define S3_H_ST 0 +#define S3_V_ST 8 + +//720*576*50 +#define S4_OUT_CLK SCALE_RATE(27000000,30000000) +#define S4_H_PW 1 +#define S4_H_BP 88 +#define S4_H_VD 800 +#define S4_H_FP 263 + +#define S4_V_PW 3 +#define S4_V_BP 9 +#define S4_V_VD 480 +#define S4_V_FP 28 + +#define S4_H_ST 0 +#define S4_V_ST 33 +//720*480*60 +#define S5_OUT_CLK SCALE_RATE(27000000,31500000) +#define S5_H_PW 1 +#define S5_H_BP 88 +#define S5_H_VD 800 +#define S5_H_FP 112 + +#define S5_V_PW 3 +#define S5_V_BP 9 +#define S5_V_VD 480 +#define S5_V_FP 28 + +#define S5_H_ST 0 +#define S5_V_ST 29 +/* Other */ +#define DCLK_POL 0 +#define SWAP_RB 0 + +#ifdef CONFIG_HDMI_DUAL_DISP +static int set_scaler_info(struct rk29fb_screen *screen, u8 hdmi_resolution) +{ + switch(hdmi_resolution){ + case HDMI_1920x1080p_60Hz: + /* Scaler Timing */ + screen->hdmi_resolution = hdmi_resolution; + screen->s_pixclock = S_OUT_CLK; + screen->s_hsync_len = S_H_PW; + screen->s_left_margin = S_H_BP; + screen->s_right_margin = S_H_FP; + screen->s_hsync_len = S_H_PW; + screen->s_upper_margin = S_V_BP; + screen->s_lower_margin = S_V_FP; + screen->s_vsync_len = S_V_PW; + screen->s_hsync_st = S_H_ST; + screen->s_vsync_st = S_V_ST; + break; + case HDMI_1920x1080p_50Hz: + /* Scaler Timing */ + screen->hdmi_resolution = hdmi_resolution; + screen->s_pixclock = S1_OUT_CLK; + screen->s_hsync_len = S1_H_PW; + screen->s_left_margin = S1_H_BP; + screen->s_right_margin = S1_H_FP; + screen->s_hsync_len = S1_H_PW; + screen->s_upper_margin = S1_V_BP; + screen->s_lower_margin = S1_V_FP; + screen->s_vsync_len = S1_V_PW; + screen->s_hsync_st = S1_H_ST; + screen->s_vsync_st = S1_V_ST; + break; + case HDMI_1280x720p_60Hz: + /* Scaler Timing */ + screen->hdmi_resolution = hdmi_resolution; + screen->s_pixclock = S2_OUT_CLK; + screen->s_hsync_len = S2_H_PW; + screen->s_left_margin = S2_H_BP; + screen->s_right_margin = S2_H_FP; + screen->s_hsync_len = S2_H_PW; + screen->s_upper_margin = S2_V_BP; + screen->s_lower_margin = S2_V_FP; + screen->s_vsync_len = S2_V_PW; + screen->s_hsync_st = S2_H_ST; + screen->s_vsync_st = S2_V_ST; + break; + case HDMI_1280x720p_50Hz: + /* Scaler Timing */ + screen->hdmi_resolution = hdmi_resolution; + screen->s_pixclock = S3_OUT_CLK; + screen->s_hsync_len = S3_H_PW; + screen->s_left_margin = S3_H_BP; + screen->s_right_margin = S3_H_FP; + screen->s_hsync_len = S3_H_PW; + screen->s_upper_margin = S3_V_BP; + screen->s_lower_margin = S3_V_FP; + screen->s_vsync_len = S3_V_PW; + screen->s_hsync_st = S3_H_ST; + screen->s_vsync_st = S3_V_ST; + break; + case HDMI_720x576p_50Hz_4x3: + case HDMI_720x576p_50Hz_16x9: + /* Scaler Timing */ + screen->hdmi_resolution = hdmi_resolution; + screen->s_pixclock = S4_OUT_CLK; + screen->s_hsync_len = S4_H_PW; + screen->s_left_margin = S4_H_BP; + screen->s_right_margin = S4_H_FP; + screen->s_hsync_len = S4_H_PW; + screen->s_upper_margin = S4_V_BP; + screen->s_lower_margin = S4_V_FP; + screen->s_vsync_len = S4_V_PW; + screen->s_hsync_st = S4_H_ST; + screen->s_vsync_st = S4_V_ST; + break; + case HDMI_720x480p_60Hz_16x9: + case HDMI_720x480p_60Hz_4x3: + /* Scaler Timing */ + screen->hdmi_resolution = hdmi_resolution; + screen->s_pixclock = S5_OUT_CLK; + screen->s_hsync_len = S5_H_PW; + screen->s_left_margin = S5_H_BP; + screen->s_right_margin = S5_H_FP; + screen->s_hsync_len = S5_H_PW; + screen->s_upper_margin = S5_V_BP; + screen->s_lower_margin = S5_V_FP; + screen->s_vsync_len = S5_V_PW; + screen->s_hsync_st = S5_H_ST; + screen->s_vsync_st = S5_V_ST; + break; + default : + printk("%s lcd not support dual display at this hdmi resolution %d \n",__func__,hdmi_resolution); + return -1; + break; + } + + return 0; +} +#else +static int set_scaler_info(struct rk29fb_screen *screen, u8 hdmi_resolution){} +#endif + +void set_lcd_info(struct rk29fb_screen *screen, struct rk29lcd_info *lcd_info ) +{ + /* screen type & face */ + screen->type = OUT_TYPE; + screen->face = OUT_FACE; + + /* Screen size */ + screen->x_res = H_VD; + screen->y_res = V_VD; + + screen->width = LCD_WIDTH; + screen->height = LCD_HEIGHT; + + /* Timing */ + screen->lcdc_aclk = LCDC_ACLK; + screen->pixclock = OUT_CLK; + screen->left_margin = H_BP; + screen->right_margin = H_FP; + screen->hsync_len = H_PW; + screen->upper_margin = V_BP; + screen->lower_margin = V_FP; + screen->vsync_len = V_PW; + + /* Pin polarity */ + screen->pin_hsync = 0; + screen->pin_vsync = 0; + screen->pin_den = 0; + screen->pin_dclk = DCLK_POL; + + /* Swap rule */ + screen->swap_rb = SWAP_RB; + screen->swap_rg = 0; + screen->swap_gb = 0; + screen->swap_delta = 0; + screen->swap_dumy = 0; + + /* Operation function*/ + screen->init = NULL; + screen->standby = NULL; + screen->sscreen_get = set_scaler_info; +#ifdef CONFIG_RK610_LCD + screen->sscreen_set = rk610_lcd_scaler_set_param; +#endif +} + diff --git a/drivers/video/display/screen/lcd_hsd800x480.c b/drivers/video/display/screen/lcd_hsd800x480.c index 56ea15fee984..b55cd3fb707e 100644 --- a/drivers/video/display/screen/lcd_hsd800x480.c +++ b/drivers/video/display/screen/lcd_hsd800x480.c @@ -11,26 +11,26 @@ /* Base */ #define OUT_TYPE SCREEN_RGB #define OUT_FACE OUT_P888 -#define OUT_CLK 28000000 +#define OUT_CLK 33000000 #define LCDC_ACLK 150000000 //29 lcdc axi DMA ƵÂÊ /* Timing */ -#define H_PW 1 -#define H_BP 88 -#define H_VD 800 -#define H_FP 40 +#define H_PW 8 //10 +#define H_BP 88 //100 +#define H_VD 800 //1024 +#define H_FP 40 //210 -#define V_PW 3 -#define V_BP 29 -#define V_VD 480 -#define V_FP 13 +#define V_PW 3 //10 +#define V_BP 10 //10 +#define V_VD 480 //768 +#define V_FP 32 //18 /* Other */ -#define DCLK_POL 1 +#define DCLK_POL 0 #define SWAP_RB 0 -#define LCD_WIDTH 800 //need modify -#define LCD_HEIGHT 480 +#define LCD_WIDTH 154 //need modify +#define LCD_HEIGHT 85 #define TXD_PORT gLcd_info->txd_pin #define CLK_PORT gLcd_info->clk_pin diff --git a/drivers/video/display/screen/screen.h b/drivers/video/display/screen/screen.h index d07ad0692e7c..5096d7cda698 100755 --- a/drivers/video/display/screen/screen.h +++ b/drivers/video/display/screen/screen.h @@ -1,8 +1,51 @@ +#ifndef _SCREEN_H +#define _SCREEN_H #include +#ifdef CONFIG_HDMI_DUAL_DISP +/* Scaler PLL CONFIG */ +#define S_PLL_NO_1 0 +#define S_PLL_NO_2 1 +#define S_PLL_NO_4 2 +#define S_PLL_NO_8 3 +#define S_PLL_M(x) (((x)&0xff)<<8) +#define S_PLL_N(x) (((x)&0xf)<<4) +#define S_PLL_NO(x) ((S_PLL_NO_##x)&0x3) + +enum{ + HDMI_RATE_148500000, + HDMI_RATE_74250000, + HDMI_RATE_27000000, +}; +/* Scaler clk setting */ +#define SCALE_PLL(_parent_rate,_rate,_m,_n,_no) \ + HDMI_RATE_ ## _parent_rate ##_S_RATE_ ## _rate \ + = S_PLL_M(_m) | S_PLL_N(_n) | S_PLL_NO(_no) +#define SCALE_RATE(_parent_rate , _rate) \ + (HDMI_RATE_ ## _parent_rate ## _S_RATE_ ## _rate) + +enum{ + SCALE_PLL(148500000, 66000000, 16, 9, 4), + SCALE_PLL(148500000, 54000000, 16, 11, 4), + SCALE_PLL(148500000, 33000000, 16, 9, 8), + SCALE_PLL(148500000, 30375000, 18, 11, 8), + SCALE_PLL(148500000, 29700000, 16, 10, 8), + SCALE_PLL(148500000, 25312500, 15, 11, 8), + + SCALE_PLL(74250000, 66000000, 32, 9, 4), + SCALE_PLL(74250000, 54000000, 32, 11, 4), + SCALE_PLL(74250000, 33000000, 32, 9, 8), + SCALE_PLL(74250000, 30375000, 36, 11, 8), + SCALE_PLL(74250000, 25312500, 30, 11, 8), + + SCALE_PLL(27000000, 31500000, 28, 3, 8), + SCALE_PLL(27000000, 30000000, 80, 9, 8), +}; +#endif typedef enum _SCREEN_TYPE { SCREEN_NULL = 0, SCREEN_RGB, + SCREEN_LVDS, SCREEN_MCU, SCREEN_TVOUT, SCREEN_HDMI, @@ -37,8 +80,9 @@ typedef enum _MCU_STATUS { /* Screen description */ typedef struct rk29fb_screen { - /* screen type & out face */ + /* screen type & hardware connect format & out face */ u16 type; + u16 hw_format; u16 face; /* Screen size */ @@ -47,6 +91,7 @@ typedef struct rk29fb_screen { u16 width; u16 height; + u32 mode; /* Timing */ u32 pixclock; u16 left_margin; @@ -55,7 +100,19 @@ typedef struct rk29fb_screen { u16 upper_margin; u16 lower_margin; u16 vsync_len; - +#ifdef CONFIG_HDMI_DUAL_DISP + /* Scaler mode Timing */ + u32 s_pixclock; + u16 s_left_margin; + u16 s_right_margin; + u16 s_hsync_len; + u16 s_upper_margin; + u16 s_lower_margin; + u16 s_vsync_len; + u16 s_hsync_st; + u16 s_vsync_st; +#endif + u8 hdmi_resolution; /* mcu need */ u8 mcu_wrperiod; u8 mcu_usefmk; @@ -82,10 +139,12 @@ typedef struct rk29fb_screen { int (*refresh)(u8 arg); int (*scandir)(u16 dir); int (*disparea)(u8 area); - + int (*sscreen_get)(struct rk29fb_screen *screen, u8 resolution); + int (*sscreen_set)(struct rk29fb_screen *screen, bool type);// 1: use scaler 0:bypass } rk_screen; + extern void set_lcd_info(struct rk29fb_screen *screen, struct rk29lcd_info *lcd_info); extern void set_tv_info(struct rk29fb_screen *screen); extern void set_hdmi_info(struct rk29fb_screen *screen); - +#endif diff --git a/drivers/video/display/tve/Kconfig b/drivers/video/display/tve/Kconfig new file mode 100644 index 000000000000..68639bdd419b --- /dev/null +++ b/drivers/video/display/tve/Kconfig @@ -0,0 +1,13 @@ +config RK610_TVOUT + bool "RK610(Jetta) tvout support" + depends on MFD_RK610 + default y if MFD_RK610 + help + Support Jetta(RK610) to output YPbPr and CVBS. + +config RK610_TVOUT_YPbPr + bool "support YPbPr output" + depends on RK610_TVOUT +config RK610_TVOUT_CVBS + bool "support CVBS output" + depends on RK610_TVOUT diff --git a/drivers/video/display/tve/Makefile b/drivers/video/display/tve/Makefile new file mode 100644 index 000000000000..b7d457326d26 --- /dev/null +++ b/drivers/video/display/tve/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the jetta tv control. +# +obj-$(CONFIG_RK610_TVOUT) += rk610_tv.o +obj-$(CONFIG_RK610_TVOUT_YPbPr) += rk610_tv_ypbpr.o +obj-$(CONFIG_RK610_TVOUT_CVBS) += rk610_tv_cvbs.o \ No newline at end of file diff --git a/drivers/video/display/tve/rk610_tv.c b/drivers/video/display/tve/rk610_tv.c new file mode 100644 index 000000000000..03237ce4a410 --- /dev/null +++ b/drivers/video/display/tve/rk610_tv.c @@ -0,0 +1,246 @@ +/* + * rk610_tv.c + * + * Driver for rockchip rk610 tv control + * Copyright (C) 2009 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rk610_tv.h" +#include "../../rk29_fb.h" + +#define DRV_NAME "rk610_tvout" +#define RK610_I2C_RATE 100*1000 + +volatile int rk610_tv_output_status = RK610_TVOUT_DEAULT; +static struct i2c_client *rk610_tv_i2c_client = NULL; + +int rk610_tv_wirte_reg(u8 reg, u8 data) +{ + int ret; + if(rk610_tv_i2c_client == NULL) + return -1; + ret = i2c_master_reg8_send(rk610_tv_i2c_client, reg, &data, 1, RK610_I2C_RATE); + if (ret > 0) + ret = 0; + return ret; +} + +int rk610_switch_fb(const struct fb_videomode *modedb, int tv_mode) +{ + struct rk29fb_screen *screen; + + if(modedb == NULL) + return -1; + screen = kzalloc(sizeof(struct rk29fb_screen), GFP_KERNEL); + if(screen == NULL) + return -1; + + memset(screen, 0, sizeof(struct rk29fb_screen)); + /* screen type & face */ + screen->type = SCREEN_HDMI; + screen->mode = modedb->vmode; + screen->face = modedb->flag; + /* Screen size */ + screen->x_res = modedb->xres; + screen->y_res = modedb->yres; + + /* Timing */ + screen->pixclock = modedb->pixclock; + + screen->lcdc_aclk = 500000000; + screen->left_margin = modedb->left_margin; + screen->right_margin = modedb->right_margin; + screen->hsync_len = modedb->hsync_len; + screen->upper_margin = modedb->upper_margin; + screen->lower_margin = modedb->lower_margin; + screen->vsync_len = modedb->vsync_len; + + /* Pin polarity */ + if(FB_SYNC_HOR_HIGH_ACT & modedb->sync) + screen->pin_hsync = 1; + else + screen->pin_hsync = 0; + if(FB_SYNC_VERT_HIGH_ACT & modedb->sync) + screen->pin_vsync = 1; + else + screen->pin_vsync = 0; + screen->pin_den = 0; + screen->pin_dclk = 0; + + /* Swap rule */ + screen->swap_rb = 0; + screen->swap_rg = 0; + screen->swap_gb = 0; + screen->swap_delta = 0; + screen->swap_dumy = 0; + + /* Operation function*/ + screen->init = NULL; + screen->standby = NULL; + + switch(tv_mode) + { +#ifdef CONFIG_RK610_TVOUT_CVBS + case TVOUT_CVBS_NTSC: + case TVOUT_CVBS_PAL: + screen->init = rk610_tv_cvbs_init;; + break; +#endif + +#ifdef CONFIG_RK610_TVOUT_YPbPr + case TVOUT_YPbPr_720x480p_60: + case TVOUT_YPbPr_720x576p_50: + case TVOUT_YPbPr_1280x720p_50: + case TVOUT_YPbPr_1280x720p_60: + //case TVOUT_YPbPr_1920x1080i_50: + case TVOUT_YPbPr_1920x1080i_60: + case TVOUT_YPbPr_1920x1080p_50: + case TVOUT_YPbPr_1920x1080p_60: + screen->init = rk610_tv_ypbpr_init; + break; +#endif + default:{ + kfree(screen); + return -1; + } + break; + } + rk610_tv_output_status = tv_mode; + FB_Switch_Screen(screen, 1); + kfree(screen); + return 0; +} + +int rk610_tv_standby(int type) +{ + int ret; + + switch(type) + { + #ifdef CONFIG_RK610_TVOUT_CVBS + case RK610_TVOUT_CVBS: + if(rk610_cvbs_monspecs.enable == 0) + return 0; + #ifdef CONFIG_RK610_TVOUT_YPbPr + if(rk610_ypbpr_monspecs.enable == 1) + return 0; + #endif + break; + #endif + #ifdef CONFIG_RK610_TVOUT_YPbPr + case RK610_TVOUT_YPBPR: + if(rk610_ypbpr_monspecs.enable == 0) + return 0; + #ifdef CONFIG_RK610_TVOUT_CVBS + if(rk610_cvbs_monspecs.enable == 1) + return 0; + #endif + break; + #endif + default: + break; + } + + ret = rk610_tv_wirte_reg(TVE_POWERCR, 0); + if(ret < 0){ + printk("[%s] rk610_tv_wirte_reg err!\n", __FUNCTION__); + return ret; + } + + ret = rk610_control_send_byte(RK610_CONTROL_REG_TVE_CON, 0); + if(ret < 0){ + printk("[%s] rk610_control_send_byte err!\n", __FUNCTION__); + return ret; + } + return 0; +} + +static int rk610_tv_probe(struct i2c_client *client,const struct i2c_device_id *id) +{ + int rc = 0; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + rc = -ENODEV; + goto failout; + } + rk610_tv_i2c_client = client; + +#ifdef CONFIG_RK610_TVOUT_YPbPr + rk610_register_display_ypbpr(&client->dev); + if(rk610_tv_output_status > TVOUT_CVBS_PAL) + rk_display_device_enable(rk610_ypbpr_monspecs.ddev); +#endif + +#ifdef CONFIG_RK610_TVOUT_CVBS + rk610_register_display_cvbs(&client->dev); + if(rk610_tv_output_status < TVOUT_YPbPr_720x480p_60) + rk_display_device_enable(rk610_cvbs_monspecs.ddev); +#endif + + printk(KERN_INFO "rk610_tv ver 1.0 probe ok\n"); + return 0; +failout: + kfree(client); + return rc; +} + +static int rk610_tv_remove(struct i2c_client *client) +{ + return 0; +} + + +static const struct i2c_device_id rk610_tv_id[] = { + { DRV_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rk610_tv_id); + +static struct i2c_driver rk610_tv_driver = { + .driver = { + .name = DRV_NAME, + }, + .id_table = rk610_tv_id, + .probe = rk610_tv_probe, + .remove = rk610_tv_remove, +}; + +static int __init rk610_tv_init(void) +{ + int ret = 0; + ret = i2c_add_driver(&rk610_tv_driver); + if(ret < 0){ + printk("i2c_add_driver err, ret = %d\n", ret); + } + return ret; +} + +static void __exit rk610_tv_exit(void) +{ + i2c_del_driver(&rk610_tv_driver); +} + +module_init(rk610_tv_init); +//late_initcall(rk610_tv_init); +module_exit(rk610_tv_exit); + +/* Module information */ +MODULE_DESCRIPTION("ROCKCHIP RK610 TV Output"); +MODULE_LICENSE("GPL"); + + diff --git a/drivers/video/display/tve/rk610_tv.h b/drivers/video/display/tve/rk610_tv.h new file mode 100644 index 000000000000..95ca56106cc0 --- /dev/null +++ b/drivers/video/display/tve/rk610_tv.h @@ -0,0 +1,129 @@ +#ifndef _RK610_TV_H +#define _RK610_TV_H +#include +#include +#include +#include +#include +#include "../screen/screen.h" +#include "../../rk29_fb.h" +#include + +#define TVE_VFCR 0x00 + #define TVE_VFCR_ENABLE_SUBCARRIER_RESET 0 << 6 + #define TVE_VFCR_DISABLE_SUBCARRIER_RESET 1 << 6 + #define TVE_VFCR_VIN_RANGE_16_235 0 << 3 + #define TVE_VFCR_VIN_RANGE_1_254 1 << 3 + #define TVE_VFCR_BLACK_7_5_IRE 0 << 2 + #define TVE_VFCR_BLACK_0_IRE 1 << 2 + #define TVE_VFCR_NTSC 0 + #define TVE_VFCR_PAL_M 1 + #define TVE_VFCR_PAL_B_N 2 + #define TVE_VFCR_PAL_NC 3 + +#define TVE_VINCR 0x01 + #define TVE_VINCR_PIX_DATA_DELAY(n) (n << 5) + #define TVE_VINCR_H_SYNC_POLARITY_NEGTIVE 0 << 4 + #define TVE_VINCR_H_SYNC_POLARITY_POSITIVE 1 << 4 + #define TVE_VINCR_V_SYNC_POLARITY_NEGTIVE 0 << 3 + #define TVE_VINCR_V_SYNC_POLARITY_POSITIVE 1 << 3 +enum { + INPUT_FORMAT_BT601_SLAVE = 0, + INPUT_FORMAT_BT656, + INPUT_FORMAT_BT601_MASTER, + INPUT_FORMAT_INTERNAL_COLLOR_BAR +}; + #define TVE_VINCR_INPUT_FORMAT(n) (n << 1) + #define TVE_VINCR_VSYNC_FUNCTION_VSYNC 0 + #define TVE_VINCR_VSYNC_FUNCTION_FIELD 1 + +#define TVE_VOUTCR 0x02 + #define TVE_VOUTCR_OUTPUT_CVBS 0 << 6 + #define TVE_VOUTCR_OUTPUT_YPBPR 1 << 6 + #define TVE_VOUTCR_OUTPUT_ENABLE_BLUE 1 << 5 + #define TVE_VOUTCR_OUTPUT_ENABLE_BLACK 1 << 4 + #define TVE_VOUTCR_DISABLE_CVBS_COLOR 1 << 3 + #define TVE_VOUTCR_CVBS_Y2C_DELAY(n) (n << 0) + +#define TVE_POWERCR 0x03 + #define TVE_PIX_CLK_INVERSE_ENABLE 1 << 4 + #define TVE_DAC_CLK_INVERSE_DISABLE 1 << 3 + #define TVE_DAC_Y_ENABLE 1 << 2 + #define TVE_DAC_U_ENABLE 1 << 1 + #define TVE_DAC_V_ENABLE 1 << 0 + +#define TVE_HDTVCR 0x05 + #define TVE_RESET 1 << 7 + #define TVE_FILTER(n) (n << 5) + #define TVE_COLOR_CONVERT_REC601 0 << 4 + #define TVE_COLOR_CONVERT_REC709 1 << 4 + #define TVE_INPUT_DATA_RGB 0 << 3 + #define TVE_INPUT_DATA_YUV 1 << 3 + #define TVE_OUTPUT_50HZ 0 << 2 + #define TVE_OUTPUT_60HZ 1 << 2 + #define TVE_OUTPUT_MODE_PAL_NTSC 0 + #define TVE_OUTPUT_MODE_576P 1 + #define TVE_OUTPUT_MODE_480P 2 + #define TVE_OUTPUT_MODE_720P 3 + +#define TVE_YADJCR 0x06 + #define TVE_OUTPUT_MODE_1080P 1 << 6 + #define TVE_OUTPUT_MODE_1080I 1 << 5 + #define TVE_Y_ADJ_VALUE(n) n +#define TVE_YCBADJCR 0x07 +#define TVE_YCRADJCR 0x08 + +/******************* TVOUT OUTPUT TYPE **********************/ +struct rk610_monspecs { + struct rk_display_device *ddev; + unsigned int enable; + struct fb_videomode *mode; + struct list_head modelist; + unsigned int mode_set; +}; + +enum { + TVOUT_CVBS_NTSC = 1, + TVOUT_CVBS_PAL, + TVOUT_YPbPr_720x480p_60, + TVOUT_YPbPr_720x576p_50, + TVOUT_YPbPr_1280x720p_50, + TVOUT_YPbPr_1280x720p_60, + //TVOUT_YPbPr_1920x1080i_50, + TVOUT_YPbPr_1920x1080i_60, + TVOUT_YPbPr_1920x1080p_50, + TVOUT_YPbPr_1920x1080p_60 +}; + +#define RK610_TVOUT_DEAULT TVOUT_CVBS_NTSC + +enum { + RK610_TVOUT_CVBS = 0, + RK610_TVOUT_YC, + RK610_TVOUT_YPBPR, +}; + +extern volatile int rk610_tv_output_status; +extern struct rk_display_ops rk610_display_ops; + +extern int FB_Switch_Screen( struct rk29fb_screen *screen, u32 enable ); + +extern int rk610_tv_wirte_reg(u8 reg, u8 data); +extern int rk610_tv_standby(int type); +extern int rk610_switch_fb(const struct fb_videomode *modedb, int tv_mode); +extern int rk610_register_display(struct device *parent); + +#ifdef CONFIG_RK610_TVOUT_YPbPr +extern int rk610_tv_ypbpr_init(void); +extern int rk610_register_display_ypbpr(struct device *parent); +extern struct rk610_monspecs rk610_ypbpr_monspecs; +#endif + +#ifdef CONFIG_RK610_TVOUT_CVBS +extern int rk610_tv_cvbs_init(void); +extern int rk610_register_display_cvbs(struct device *parent); +extern struct rk610_monspecs rk610_cvbs_monspecs; +#endif + +#endif + diff --git a/drivers/video/display/tve/rk610_tv_cvbs.c b/drivers/video/display/tve/rk610_tv_cvbs.c new file mode 100644 index 000000000000..ea0fe8a0d79c --- /dev/null +++ b/drivers/video/display/tve/rk610_tv_cvbs.c @@ -0,0 +1,209 @@ +#include +#include +#include +#include "rk610_tv.h" + + +#ifdef CONFIG_DISPLAY_KEY_LED_CONTROL +#define RK610_LED_CVBS_PIN RK29_PIN4_PD3 +#else +#define RK610_LED_CVBS_PIN INVALID_GPIO +#endif + +#ifdef USE_RGB2CCIR +static const struct fb_videomode rk610_cvbs_mode [] = { + //name refresh xres yres pixclock h_bp h_fp v_bp v_fp h_pw v_pw polariry PorI flag + { "NTSC", 60, 720, 480, 27000000, 116, 16, 25, 14, 6, 6, 0, 1, OUT_P888 }, + { "PAL", 50, 720, 576, 27000000, 126, 12, 37, 6, 6, 6, 0, 1, OUT_P888 }, +}; +#else +static const struct fb_videomode rk610_cvbs_mode [] = { + //name refresh xres yres pixclock h_bp h_fp v_bp v_fp h_pw v_pw polariry PorI flag + { "NTSC", 60, 720, 480, 27000000, 116, 16, 16, 3, 6, 3, 0, 1, OUT_CCIR656 }, + { "PAL", 50, 720, 576, 27000000, 126, 12, 19, 2, 6, 3, 0, 1, OUT_CCIR656 }, +}; +#endif + +struct rk610_monspecs rk610_cvbs_monspecs; + + +int rk610_tv_cvbs_init(void) +{ + unsigned char TVE_Regs[9]; + unsigned char TVE_CON_Reg; + int ret, i; + + rk610_tv_wirte_reg(TVE_HDTVCR, TVE_RESET); + + memset(TVE_Regs, 0, 9); + TVE_CON_Reg = TVE_CONTROL_CVBS_3_CHANNEL_ENALBE; + TVE_Regs[TVE_VINCR] = TVE_VINCR_PIX_DATA_DELAY(0) | TVE_VINCR_H_SYNC_POLARITY_NEGTIVE | TVE_VINCR_V_SYNC_POLARITY_NEGTIVE | TVE_VINCR_VSYNC_FUNCTION_VSYNC; + TVE_Regs[TVE_POWERCR] = TVE_DAC_Y_ENABLE | TVE_DAC_U_ENABLE | TVE_DAC_V_ENABLE; + TVE_Regs[TVE_VOUTCR] = TVE_VOUTCR_OUTPUT_CVBS; + TVE_Regs[TVE_YADJCR] = 0x17; + TVE_Regs[TVE_YCBADJCR] = 0x10; + TVE_Regs[TVE_YCRADJCR] = 0x10; + + switch(rk610_tv_output_status) { + case TVOUT_CVBS_NTSC: + TVE_Regs[TVE_VFCR] = TVE_VFCR_ENABLE_SUBCARRIER_RESET | TVE_VFCR_VIN_RANGE_16_235 | TVE_VFCR_BLACK_7_5_IRE | TVE_VFCR_NTSC; + #ifdef USE_RGB2CCIR + TVE_Regs[TVE_VINCR] |= TVE_VINCR_INPUT_FORMAT(INPUT_FORMAT_BT601_SLAVE); + TVE_Regs[TVE_HDTVCR] = TVE_FILTER(0) | TVE_COLOR_CONVERT_REC601 | TVE_INPUT_DATA_RGB | TVE_OUTPUT_MODE_PAL_NTSC; + TVE_CON_Reg |= RGB2CCIR_INPUT_DATA_FORMAT(0) | RGB2CCIR_RGB_SWAP_DISABLE | RGB2CCIR_INPUT_PROGRESSIVE | RGB2CCIR_CVBS_NTSC | RGB2CCIR_ENABLE; + #else + TVE_Regs[TVE_VINCR] |= TVE_VINCR_INPUT_FORMAT(INPUT_FORMAT_BT656); + TVE_Regs[TVE_HDTVCR] = TVE_FILTER(0) | TVE_INPUT_DATA_YUV | TVE_OUTPUT_MODE_PAL_NTSC; + #endif + break; + case TVOUT_CVBS_PAL: + TVE_Regs[TVE_VFCR] = TVE_VFCR_ENABLE_SUBCARRIER_RESET | TVE_VFCR_VIN_RANGE_16_235 | TVE_VFCR_BLACK_0_IRE | TVE_VFCR_PAL_B_N; + #ifdef USE_RGB2CCIR + TVE_Regs[TVE_VINCR] |= TVE_VINCR_INPUT_FORMAT(INPUT_FORMAT_BT601_SLAVE); + TVE_Regs[TVE_HDTVCR] = TVE_FILTER(0) | TVE_COLOR_CONVERT_REC601 | TVE_INPUT_DATA_RGB | TVE_OUTPUT_MODE_PAL_NTSC; + TVE_CON_Reg |= RGB2CCIR_INPUT_DATA_FORMAT(0) | RGB2CCIR_RGB_SWAP_DISABLE | RGB2CCIR_INPUT_PROGRESSIVE | RGB2CCIR_CVBS_PAL | RGB2CCIR_ENABLE; + #else + TVE_Regs[TVE_VINCR] |= TVE_VINCR_INPUT_FORMAT(INPUT_FORMAT_BT656); + TVE_Regs[TVE_HDTVCR] = TVE_FILTER(0) | TVE_INPUT_DATA_YUV | TVE_OUTPUT_MODE_PAL_NTSC; + #endif + break; + default: + return -1; + } + + for(i = 0; i < sizeof(TVE_Regs); i++){ +// printk(KERN_ERR "reg[%d] = 0x%02x\n", i, TVE_Regs[i]); + ret = rk610_tv_wirte_reg(i, TVE_Regs[i]); + if(ret < 0){ + printk(KERN_ERR "rk610_tv_wirte_reg %d err!\n", i); + return ret; + } + } +// printk(KERN_ERR "TVE_CON_Reg = 0x%02x\n", TVE_CON_Reg); + rk610_control_send_byte(RK610_CONTROL_REG_TVE_CON, TVE_CON_Reg); + #ifdef USE_RGB2CCIR + rk610_control_send_byte(RK610_CONTROL_REG_CCIR_RESET, 0x01); + #endif + return 0; +} + +static int rk610_cvbs_set_enable(struct rk_display_device *device, int enable) +{ + if(rk610_cvbs_monspecs.enable != enable || rk610_cvbs_monspecs.mode_set != rk610_tv_output_status) + { + if(enable == 0) + { + rk610_tv_standby(RK610_TVOUT_CVBS); + rk610_cvbs_monspecs.enable = 0; + if(RK610_LED_CVBS_PIN != INVALID_GPIO) + gpio_direction_output(RK610_LED_CVBS_PIN, GPIO_HIGH); + } + else if(enable == 1) + { + rk610_switch_fb(rk610_cvbs_monspecs.mode, rk610_cvbs_monspecs.mode_set); + rk610_cvbs_monspecs.enable = 1; + if(RK610_LED_CVBS_PIN != INVALID_GPIO) + gpio_direction_output(RK610_LED_CVBS_PIN, GPIO_LOW); + } + } + return 0; +} + +static int rk610_cvbs_get_enable(struct rk_display_device *device) +{ + return rk610_cvbs_monspecs.enable; +} + +static int rk610_cvbs_get_status(struct rk_display_device *device) +{ + if(rk610_tv_output_status < TVOUT_YPbPr_720x480p_60) + return 1; + else + return 0; +} + +static int rk610_cvbs_get_modelist(struct rk_display_device *device, struct list_head **modelist) +{ + *modelist = &(rk610_cvbs_monspecs.modelist); + return 0; +} + +static int rk610_cvbs_set_mode(struct rk_display_device *device, struct fb_videomode *mode) +{ + int i; + + for(i = 0; i < ARRAY_SIZE(rk610_cvbs_mode); i++) + { + if(fb_mode_is_equal(&rk610_cvbs_mode[i], mode)) + { + if( ((i + 1) != rk610_tv_output_status) ) + { + rk610_cvbs_monspecs.mode_set = i + 1; + rk610_cvbs_monspecs.mode = (struct fb_videomode *)&rk610_cvbs_mode[i]; + } + return 0; + } + } + + return -1; +} + +static int rk610_cvbs_get_mode(struct rk_display_device *device, struct fb_videomode *mode) +{ + *mode = *(rk610_cvbs_monspecs.mode); + return 0; +} + +static struct rk_display_ops rk610_cvbs_display_ops = { + .setenable = rk610_cvbs_set_enable, + .getenable = rk610_cvbs_get_enable, + .getstatus = rk610_cvbs_get_status, + .getmodelist = rk610_cvbs_get_modelist, + .setmode = rk610_cvbs_set_mode, + .getmode = rk610_cvbs_get_mode, +}; + +static int rk610_display_cvbs_probe(struct rk_display_device *device, void *devdata) +{ + device->owner = THIS_MODULE; + strcpy(device->type, "TV"); + device->priority = DISPLAY_PRIORITY_TV; + device->priv_data = devdata; + device->ops = &rk610_cvbs_display_ops; + return 1; +} + +static struct rk_display_driver display_rk610_cvbs = { + .probe = rk610_display_cvbs_probe, +}; + +int rk610_register_display_cvbs(struct device *parent) +{ + int i; + + memset(&rk610_cvbs_monspecs, 0, sizeof(struct rk610_monspecs)); + INIT_LIST_HEAD(&rk610_cvbs_monspecs.modelist); + for(i = 0; i < ARRAY_SIZE(rk610_cvbs_mode); i++) + fb_add_videomode(&rk610_cvbs_mode[i], &rk610_cvbs_monspecs.modelist); + if(rk610_tv_output_status < TVOUT_YPbPr_720x480p_60) { + rk610_cvbs_monspecs.mode = (struct fb_videomode *)&(rk610_cvbs_mode[rk610_tv_output_status - 1]); + rk610_cvbs_monspecs.mode_set = rk610_tv_output_status; + } + else { + rk610_cvbs_monspecs.mode = (struct fb_videomode *)&(rk610_cvbs_mode[0]); + rk610_cvbs_monspecs.mode_set = TVOUT_CVBS_NTSC; + } + rk610_cvbs_monspecs.ddev = rk_display_device_register(&display_rk610_cvbs, parent, NULL); + if(RK610_LED_CVBS_PIN != INVALID_GPIO) + { + if(gpio_request(RK610_LED_CVBS_PIN, NULL) != 0) + { + gpio_free(RK610_LED_CVBS_PIN); + dev_err(rk610_cvbs_monspecs.ddev->dev, ">>>>>> RK610_LED_CVBS_PIN gpio_request err \n "); + return -1; + } + gpio_pull_updown(RK610_LED_CVBS_PIN,GPIOPullUp); + gpio_direction_output(RK610_LED_CVBS_PIN, GPIO_HIGH); + } + return 0; +} diff --git a/drivers/video/display/tve/rk610_tv_ypbpr.c b/drivers/video/display/tve/rk610_tv_ypbpr.c new file mode 100644 index 000000000000..af74126f7604 --- /dev/null +++ b/drivers/video/display/tve/rk610_tv_ypbpr.c @@ -0,0 +1,229 @@ +#include +#include +#include +#include "rk610_tv.h" + + +#ifdef CONFIG_DISPLAY_KEY_LED_CONTROL +#define RK610_LED_YPbPr_PIN RK29_PIN4_PD5 +#else +#define RK610_LED_YPbPr_PIN INVALID_GPIO +#endif +#define E(fmt, arg...) printk("<3>!!!%s:%d: " fmt, __FILE__, __LINE__, ##arg) + +static const struct fb_videomode rk610_YPbPr_mode [] = { + //name refresh xres yres pixclock h_bp h_fp v_bp v_fp h_pw v_pw polariry PorI flag + { "YPbPr480p", 60, 720, 480, 27000000, 55, 19, 37, 5, 64, 5, 0, 0, OUT_P888 }, + { "YPbPr576p", 50, 720, 576, 27000000, 68, 12, 39, 5, 64, 5, 0, 0, OUT_P888 }, + { "YPbPr720p@50", 50, 1280, 720, 74250000, 600, 0, 20, 5, 100, 5, 0, 0, OUT_P888 }, + { "YPbPr720p@60", 60, 1280, 720, 74250000, 270, 0, 20, 5, 100, 5, 0, 0, OUT_P888 }, + //{ "YPbPr1080i@50", 50, 1920, 1080, 148500000, 620, 0, 15, 2, 100, 5, 0, 1, OUT_CCIR656 }, + { "YPbPr1080i@60", 60, 1920, 1080, 148500000, 180, 0, 15, 2, 100, 5, 0, 1, OUT_CCIR656 }, + { "YPbPr1080p@50", 50, 1920, 1080, 148500000, 620, 0, 36, 4, 100, 5, 0, 0, OUT_P888 }, + { "YPbPr1080p@60", 60, 1920, 1080, 148500000, 180, 0, 36, 4, 100, 5, 0, 0, OUT_P888 }, +}; + +struct rk610_monspecs rk610_ypbpr_monspecs; + +int rk610_tv_ypbpr_init(void) +{ + unsigned char TVE_Regs[9]; + unsigned char TVE_CON_Reg; + int i, ret; + + rk610_tv_wirte_reg(TVE_HDTVCR, TVE_RESET); + memset(TVE_Regs, 0, 9); + + TVE_CON_Reg = 0x00; + + TVE_Regs[TVE_VINCR] = TVE_VINCR_PIX_DATA_DELAY(0) | TVE_VINCR_H_SYNC_POLARITY_NEGTIVE | TVE_VINCR_V_SYNC_POLARITY_NEGTIVE | TVE_VINCR_VSYNC_FUNCTION_VSYNC; + TVE_Regs[TVE_POWERCR] = TVE_DAC_CLK_INVERSE_DISABLE | TVE_DAC_Y_ENABLE | TVE_DAC_U_ENABLE | TVE_DAC_V_ENABLE; + TVE_Regs[TVE_VOUTCR] = TVE_VOUTCR_OUTPUT_YPBPR; + TVE_Regs[TVE_YADJCR] = 0x17; + TVE_Regs[TVE_YCBADJCR] = 0x10; + TVE_Regs[TVE_YCRADJCR] = 0x10; + + switch(rk610_tv_output_status) + { + case TVOUT_YPbPr_720x480p_60: + TVE_Regs[TVE_VFCR] = TVE_VFCR_BLACK_0_IRE; + TVE_Regs[TVE_VINCR] |= TVE_VINCR_INPUT_FORMAT(INPUT_FORMAT_BT601_SLAVE); + TVE_Regs[TVE_HDTVCR] = TVE_FILTER(0) | TVE_COLOR_CONVERT_REC601 | TVE_INPUT_DATA_RGB | TVE_OUTPUT_60HZ | TVE_OUTPUT_MODE_480P; + break; + case TVOUT_YPbPr_720x576p_50: + TVE_Regs[TVE_VFCR] = TVE_VFCR_BLACK_0_IRE | TVE_VFCR_PAL_NC; + TVE_Regs[TVE_VINCR] |= TVE_VINCR_INPUT_FORMAT(INPUT_FORMAT_BT601_SLAVE); + TVE_Regs[TVE_HDTVCR] = TVE_FILTER(0) | TVE_COLOR_CONVERT_REC601 | TVE_INPUT_DATA_RGB | TVE_OUTPUT_50HZ | TVE_OUTPUT_MODE_576P; + break; + case TVOUT_YPbPr_1280x720p_50: + TVE_Regs[TVE_VFCR] = TVE_VFCR_BLACK_0_IRE | TVE_VFCR_PAL_NC; + TVE_Regs[TVE_VINCR] |= TVE_VINCR_INPUT_FORMAT(INPUT_FORMAT_BT601_SLAVE); + TVE_Regs[TVE_HDTVCR] = TVE_FILTER(0) | TVE_COLOR_CONVERT_REC709 | TVE_INPUT_DATA_RGB | TVE_OUTPUT_50HZ | TVE_OUTPUT_MODE_720P; + break; + case TVOUT_YPbPr_1280x720p_60: + TVE_Regs[TVE_VFCR] = TVE_VFCR_BLACK_0_IRE | TVE_VFCR_PAL_NC; + TVE_Regs[TVE_VINCR] |= TVE_VINCR_INPUT_FORMAT(INPUT_FORMAT_BT601_SLAVE); + TVE_Regs[TVE_HDTVCR] = TVE_FILTER(0) | TVE_COLOR_CONVERT_REC709 | TVE_INPUT_DATA_RGB | TVE_OUTPUT_60HZ | TVE_OUTPUT_MODE_720P; + break; + /*case TVOUT_YPbPr_1920x1080i_50: + TVE_Regs[TVE_VFCR] = TVE_VFCR_BLACK_0_IRE | TVE_VFCR_PAL_NC; + TVE_Regs[TVE_VINCR] |= TVE_VINCR_INPUT_FORMAT(INPUT_FORMAT_BT656); + TVE_Regs[TVE_HDTVCR] = TVE_FILTER(0) | TVE_INPUT_DATA_YUV | TVE_OUTPUT_50HZ; + TVE_Regs[TVE_YADJCR] |= TVE_OUTPUT_MODE_1080I; + break; + */ + case TVOUT_YPbPr_1920x1080i_60: + TVE_Regs[TVE_VFCR] = TVE_VFCR_BLACK_0_IRE | TVE_VFCR_PAL_NC; + TVE_Regs[TVE_VINCR] |= TVE_VINCR_INPUT_FORMAT(INPUT_FORMAT_BT656); + TVE_Regs[TVE_HDTVCR] = TVE_FILTER(0) | TVE_INPUT_DATA_YUV | TVE_OUTPUT_60HZ; + TVE_Regs[TVE_YADJCR] |= TVE_OUTPUT_MODE_1080I; + break; + case TVOUT_YPbPr_1920x1080p_50: + TVE_Regs[TVE_VFCR] = TVE_VFCR_BLACK_0_IRE | TVE_VFCR_PAL_NC; + TVE_Regs[TVE_VINCR] |= TVE_VINCR_INPUT_FORMAT(INPUT_FORMAT_BT601_SLAVE); + TVE_Regs[TVE_HDTVCR] = TVE_FILTER(0) | TVE_COLOR_CONVERT_REC709 | TVE_INPUT_DATA_RGB | TVE_OUTPUT_50HZ; + TVE_Regs[TVE_YADJCR] |= TVE_OUTPUT_MODE_1080P; + break; + case TVOUT_YPbPr_1920x1080p_60: + TVE_Regs[TVE_VFCR] = TVE_VFCR_BLACK_0_IRE | TVE_VFCR_PAL_NC; + TVE_Regs[TVE_VINCR] |= TVE_VINCR_INPUT_FORMAT(INPUT_FORMAT_BT601_SLAVE); + TVE_Regs[TVE_HDTVCR] = TVE_FILTER(0) | TVE_COLOR_CONVERT_REC709 | TVE_INPUT_DATA_RGB | TVE_OUTPUT_60HZ; + TVE_Regs[TVE_YADJCR] |= TVE_OUTPUT_MODE_1080P; + break; + default: + return -1; + } + + rk610_control_send_byte(RK610_CONTROL_REG_TVE_CON, TVE_CON_Reg); + + for(i = 0; i < sizeof(TVE_Regs); i++){ +// printk(KERN_ERR "reg[%d] = 0x%02x\n", i, TVE_Regs[i]); + ret = rk610_tv_wirte_reg(i, TVE_Regs[i]); + if(ret < 0){ + E("rk610_tv_wirte_reg %d err!\n", i); + return ret; + } + } + return 0; +} + +static int rk610_ypbpr_set_enable(struct rk_display_device *device, int enable) +{ + if(rk610_ypbpr_monspecs.enable != enable || rk610_ypbpr_monspecs.mode_set != rk610_tv_output_status) + { + if(enable == 0) + { + rk610_tv_standby(RK610_TVOUT_YPBPR); + rk610_ypbpr_monspecs.enable = 0; + if(RK610_LED_YPbPr_PIN != INVALID_GPIO) + gpio_direction_output(RK610_LED_YPbPr_PIN, GPIO_HIGH); + } + else if(enable == 1) + { + rk610_switch_fb(rk610_ypbpr_monspecs.mode, rk610_ypbpr_monspecs.mode_set); + rk610_ypbpr_monspecs.enable = 1; + if(RK610_LED_YPbPr_PIN != INVALID_GPIO) + gpio_direction_output(RK610_LED_YPbPr_PIN, GPIO_LOW); + } + } + return 0; +} + +static int rk610_ypbpr_get_enable(struct rk_display_device *device) +{ + return rk610_ypbpr_monspecs.enable; +} + +static int rk610_ypbpr_get_status(struct rk_display_device *device) +{ + if(rk610_tv_output_status > TVOUT_CVBS_PAL) + return 1; + else + return 0; +} + +static int rk610_ypbpr_get_modelist(struct rk_display_device *device, struct list_head **modelist) +{ + *modelist = &(rk610_ypbpr_monspecs.modelist); + return 0; +} + +static int rk610_ypbpr_set_mode(struct rk_display_device *device, struct fb_videomode *mode) +{ + int i; + + for(i = 0; i < ARRAY_SIZE(rk610_YPbPr_mode); i++) + { + if(fb_mode_is_equal(&rk610_YPbPr_mode[i], mode)) + { + if( (i + 3) != rk610_tv_output_status ) + { + rk610_ypbpr_monspecs.mode_set = i + 3; + rk610_ypbpr_monspecs.mode = (struct fb_videomode *)&rk610_YPbPr_mode[i]; + } + return 0; + } + } + + return -1; +} + +static int rk610_ypbpr_get_mode(struct rk_display_device *device, struct fb_videomode *mode) +{ + *mode = *(rk610_ypbpr_monspecs.mode); + return 0; +} + +static struct rk_display_ops rk610_ypbpr_display_ops = { + .setenable = rk610_ypbpr_set_enable, + .getenable = rk610_ypbpr_get_enable, + .getstatus = rk610_ypbpr_get_status, + .getmodelist = rk610_ypbpr_get_modelist, + .setmode = rk610_ypbpr_set_mode, + .getmode = rk610_ypbpr_get_mode, +}; + +static int rk610_display_YPbPr_probe(struct rk_display_device *device, void *devdata) +{ + device->owner = THIS_MODULE; + strcpy(device->type, "YPbPr"); + device->priority = DISPLAY_PRIORITY_YPbPr; + device->priv_data = devdata; + device->ops = &rk610_ypbpr_display_ops; + return 1; +} + +static struct rk_display_driver display_rk610_YPbPr = { + .probe = rk610_display_YPbPr_probe, +}; + +int rk610_register_display_ypbpr(struct device *parent) +{ + int i; + + memset(&rk610_ypbpr_monspecs, 0, sizeof(struct rk610_monspecs)); + INIT_LIST_HEAD(&rk610_ypbpr_monspecs.modelist); + for(i = 0; i < ARRAY_SIZE(rk610_YPbPr_mode); i++) + fb_add_videomode(&rk610_YPbPr_mode[i], &rk610_ypbpr_monspecs.modelist); + if(rk610_tv_output_status > TVOUT_CVBS_PAL) { + rk610_ypbpr_monspecs.mode = (struct fb_videomode *)&(rk610_YPbPr_mode[rk610_tv_output_status - 3]); + rk610_ypbpr_monspecs.mode_set = rk610_tv_output_status; + } + else { + rk610_ypbpr_monspecs.mode = (struct fb_videomode *)&(rk610_YPbPr_mode[3]); + rk610_ypbpr_monspecs.mode_set = TVOUT_YPbPr_1280x720p_60; + } + rk610_ypbpr_monspecs.ddev = rk_display_device_register(&display_rk610_YPbPr, parent, NULL); + if(RK610_LED_YPbPr_PIN != INVALID_GPIO) + { + if(gpio_request(RK610_LED_YPbPr_PIN, NULL) != 0) + { + gpio_free(RK610_LED_YPbPr_PIN); + dev_err(rk610_ypbpr_monspecs.ddev->dev, ">>>>>> RK610_LED_YPbPr_PIN gpio_request err \n "); + return -1; + } + gpio_pull_updown(RK610_LED_YPbPr_PIN,GPIOPullUp); + gpio_direction_output(RK610_LED_YPbPr_PIN, GPIO_HIGH); + } + return 0; +} diff --git a/drivers/video/hdmi/Kconfig b/drivers/video/hdmi/Kconfig index 2d68da6d7f92..fe8bf2f46697 100755 --- a/drivers/video/hdmi/Kconfig +++ b/drivers/video/hdmi/Kconfig @@ -14,11 +14,13 @@ config HDMI_SAVE_DATA bool "enable hdmi save data" help Enable hdmi save data in rtc register + +config HDMI_DUAL_DISP + bool "dual display support" + depends on RK610_HDMI + help + Support output lcd and hdmi at the same time. -#config HDMI_DUAL_DISP -# bool "hdmi support dual display" -# help -# nothing #config HDMI_DEBUG # bool "hdmi debug" endif diff --git a/drivers/video/hdmi/chips/Kconfig b/drivers/video/hdmi/chips/Kconfig index a1b838f76179..9ad25b699768 100755 --- a/drivers/video/hdmi/chips/Kconfig +++ b/drivers/video/hdmi/chips/Kconfig @@ -4,4 +4,9 @@ config ANX7150 bool "anx7150" config ANX9030 bool "anx9030" +config RK610_HDMI + bool "RK610(Jetta) hdmi support" + depends on MFD_RK610 + help + Support Jetta(RK610) to hdmi. endchoice diff --git a/drivers/video/hdmi/chips/Makefile b/drivers/video/hdmi/chips/Makefile index 92959ebe9772..48245f5cfe5b 100755 --- a/drivers/video/hdmi/chips/Makefile +++ b/drivers/video/hdmi/chips/Makefile @@ -1,2 +1,2 @@ -obj-$(CONFIG_ANX7150) += anx7150_hw.o anx7150.o - +obj-$(CONFIG_ANX7150) += anx7150/anx7150.o anx7150/anx7150_hw.o +obj-$(CONFIG_RK610_HDMI) += rk610/rk610_hdmi.o rk610/rk610_hdmi_hw.o diff --git a/drivers/video/hdmi/chips/anx7150.c b/drivers/video/hdmi/chips/anx7150/anx7150.c old mode 100755 new mode 100644 similarity index 100% rename from drivers/video/hdmi/chips/anx7150.c rename to drivers/video/hdmi/chips/anx7150/anx7150.c diff --git a/drivers/video/hdmi/chips/anx7150.h b/drivers/video/hdmi/chips/anx7150/anx7150.h old mode 100755 new mode 100644 similarity index 100% rename from drivers/video/hdmi/chips/anx7150.h rename to drivers/video/hdmi/chips/anx7150/anx7150.h diff --git a/drivers/video/hdmi/chips/anx7150_hw.c b/drivers/video/hdmi/chips/anx7150/anx7150_hw.c similarity index 100% rename from drivers/video/hdmi/chips/anx7150_hw.c rename to drivers/video/hdmi/chips/anx7150/anx7150_hw.c diff --git a/drivers/video/hdmi/chips/anx7150_hw.h b/drivers/video/hdmi/chips/anx7150/anx7150_hw.h old mode 100755 new mode 100644 similarity index 100% rename from drivers/video/hdmi/chips/anx7150_hw.h rename to drivers/video/hdmi/chips/anx7150/anx7150_hw.h diff --git a/drivers/video/hdmi/chips/rk610/rk610_hdmi.c b/drivers/video/hdmi/chips/rk610/rk610_hdmi.c new file mode 100644 index 000000000000..6c33f941d702 --- /dev/null +++ b/drivers/video/hdmi/chips/rk610/rk610_hdmi.c @@ -0,0 +1,299 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "rk610_hdmi.h" +#include "rk610_hdmi_hw.h" + + +struct i2c_client *rk610_g_hdmi_client=NULL; +static bool hpd=0; + +static void rk610_handler(struct work_struct *work) +{ + struct i2c_client *client = rk610_g_hdmi_client; + if(client==NULL){ + printk(">>> %s client==NULL\n",__func__); + } + Rk610_hdmi_event_work(client,&hpd); +} + +static DECLARE_DELAYED_WORK(rk610_irq_work, rk610_handler); +static int rk610_hdmi_precent(struct hdmi *hdmi) +{ + //struct rk610_hdmi_inf *rk610_hdmi = hdmi_priv(hdmi); + schedule_delayed_work(&rk610_irq_work, msecs_to_jiffies(30)); + return hpd; +} + +static int rk610_hdmi_param_chg(struct rk610_hdmi_inf *rk610_hdmi) +{ + RK610_DBG(&rk610_hdmi->client->dev,"%s \n",__FUNCTION__); + hdmi_switch_fb(rk610_hdmi->hdmi, rk610_hdmi->hdmi->display_on); + Rk610_hdmi_Set_Video(rk610_hdmi->hdmi->resolution); + Rk610_hdmi_Set_Audio(rk610_hdmi->hdmi->audio_fs); + Rk610_hdmi_Config_Done(rk610_hdmi->client); + return 0; +} + +static int rk610_hdmi_set_param(struct hdmi *hdmi) +{ + struct rk610_hdmi_inf *rk610_hdmi = hdmi_priv(hdmi); + RK610_DBG(&rk610_hdmi->client->dev,"%s \n",__FUNCTION__); + if(rk610_hdmi->init == 1) + return 0; + + rk610_hdmi_param_chg(rk610_hdmi); + return 0; +} +static int rk610_hdmi_insert(struct hdmi *hdmi) +{ + struct rk610_hdmi_inf *rk610_hdmi = hdmi_priv(hdmi); + RK610_DBG(&rk610_hdmi->client->dev,"%s \n",__FUNCTION__); + if(rk610_hdmi->init == 1) + return -1; + rk610_hdmi_param_chg(rk610_hdmi); + hdmi_set_spk(HDMI_DISABLE); + printk("rk610_hdmi_insert hdmi->display_on=%d\n",hdmi->display_on); + hdmi->scale = hdmi->scale_set; + return 0; +} +static int rk610_hdmi_remove(struct hdmi *hdmi) +{ + struct rk610_hdmi_inf *rk610_hdmi = hdmi_priv(hdmi); + RK610_DBG(&rk610_hdmi->client->dev,"%s \n",__FUNCTION__); + if(rk610_hdmi->init == 1) + return -1; + hdmi_set_spk(HDMI_ENABLE); + hdmi_switch_fb(hdmi, HDMI_DISABLE); + printk("rk610_hdmi_remove hdmi->display_on=%d\n",hdmi->display_on); + return 0; +} +#ifdef CONFIG_HAS_EARLYSUSPEND +static void rk610_hdmi_early_suspend(struct early_suspend *h) +{ + struct rk610_hdmi_inf *rk610_hdmi = container_of(h, + struct rk610_hdmi_inf, + early_suspend); + printk( "rk610_hdmi enter early suspend\n"); + hdmi_suspend(rk610_hdmi->hdmi); + Rk610_hdmi_suspend(rk610_hdmi->client); + return; +} + +static void rk610_hdmi_early_resume(struct early_suspend *h) +{ + struct rk610_hdmi_inf *rk610_hdmi = container_of(h, + struct rk610_hdmi_inf, + early_suspend); + printk("rk610_hdmi exit early suspend\n"); + hdmi_resume(rk610_hdmi->hdmi); + Rk610_hdmi_resume(rk610_hdmi->client); + return; +} +#endif + +static int rk610_hdmi_init(struct hdmi *hdmi) +{ + struct rk610_hdmi_inf *rk610_hdmi = hdmi_priv(hdmi); +#ifdef CONFIG_HDMI_SAVE_DATA + int hdmi_data = hdmi_get_data(); + if(hdmi_data<0){ + hdmi_set_data((hdmi->resolution&0x7)|((hdmi->scale&0x1f)<<3)); + } + else{ + hdmi->resolution = hdmi_data&0x7; + hdmi->scale_set= ((hdmi_data>>3)&0x1f) + MIN_SCALE; + hdmi->scale = hdmi->scale_set; + } +#endif + RK610_DBG(&rk610_hdmi->client->dev,"%s \n",__FUNCTION__); + rk610_hdmi->init =0; + Rk610_hdmi_init(rk610_hdmi->client); + hdmi_changed(hdmi,1); + Rk610_hdmi_Set_Video(hdmi->resolution); + Rk610_hdmi_Set_Audio(hdmi->audio_fs); + Rk610_hdmi_Config_Done(rk610_hdmi->client); + return 0; +} +static struct hdmi_ops rk610_hdmi_ops = { + .set_param = rk610_hdmi_set_param, + .hdmi_precent = rk610_hdmi_precent, + .insert = rk610_hdmi_insert, + .remove = rk610_hdmi_remove, + .init = rk610_hdmi_init, +}; +#ifdef CONFIG_RK610_DEBUG +static int rk610_read_p0_reg(struct i2c_client *client, char reg, char *val) +{ + return i2c_master_reg8_recv(client, reg, val, 1, 100*1000) > 0? 0: -EINVAL; +} + +static int rk610_write_p0_reg(struct i2c_client *client, char reg, char *val) +{ + return i2c_master_reg8_send(client, reg, val, 1, 100*1000) > 0? 0: -EINVAL; +} +static ssize_t rk610_show_reg_attrs(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + + int i,size=0; + char val; + struct i2c_client *client=rk610_g_hdmi_client; + + for(i=0;i<256;i++) + { + rk610_read_p0_reg(client, i, &val); + if(i%16==0) + size += sprintf(buf+size,"\n>>>rk610_hdmi %x:",i); + size += sprintf(buf+size," %2x",val); + } + + return size; +} +static ssize_t rk610_store_reg_attrs(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct i2c_client *client=NULL; + char val,reg,addr; + client = rk610_g_hdmi_client; + printk("/**********rk610 reg config******/"); + + sscanf(buf, "%x%x%x", &val,®,&addr); + printk("addr=%x ,reg=%x val=%x\n",addr,reg,val); + rk610_write_p0_reg(client, reg, &val); + printk("val=%x\n",val); + return size; +} + +static struct device_attribute rk610_attrs[] = { + __ATTR(reg_ctl, 0777,rk610_show_reg_attrs,rk610_store_reg_attrs), +}; +#endif +#if 0 +static irqreturn_t rk610_hdmi_interrupt(int irq, void *dev_id) +{ + struct hdmi *hdmi = (struct hdmi *)dev_id; + unsigned long lock_flags = 0; + printk("The rk610_hdmi interrupt handeler is working..\n"); + return IRQ_HANDLED; +} +#endif + +static int rk610_hdmi_i2c_probe(struct i2c_client *client,const struct i2c_device_id *id) +{ + int ret = 0; + struct hdmi *hdmi = NULL; + struct rk610_hdmi_inf *rk610_hdmi = NULL; + + struct hdmi_platform_data *pdata = client->dev.platform_data; + rk610_g_hdmi_client = client; + if(pdata && pdata->io_init) + { + printk("rk610_hdmi_i2c_probe io_init \n"); + pdata->io_init(); + } + hdmi = hdmi_register(sizeof(struct rk610_hdmi_inf), &client->dev); + if (!hdmi) + { + dev_err(&client->dev, "fail to register hdmi\n"); + return -ENOMEM; + } + hdmi->ops = &rk610_hdmi_ops; + hdmi->display_on = HDMI_DEFAULT_MODE; + hdmi->hdcp_on = HDMI_DISABLE; + hdmi->audio_fs = HDMI_I2S_DEFAULT_Fs; + hdmi->resolution = HDMI_DEFAULT_RESOLUTION; + hdmi->dual_disp = DUAL_DISP_CAP; + hdmi->mode = DISP_ON_LCD; + hdmi->scale = 100; + hdmi->scale_set = 100; + + rk610_hdmi = hdmi_priv(hdmi); + rk610_hdmi->init = 1; + rk610_hdmi->hdmi = hdmi; + i2c_set_clientdata(client, rk610_hdmi); + rk610_hdmi->client = client; + if((gpio_request(client->irq, "hdmi gpio")) < 0) + { + dev_err(&client->dev, "fail to request gpio %d\n", client->irq); + goto err_gpio_free; + } + rk610_hdmi->irq = gpio_to_irq(client->irq); + rk610_hdmi->gpio = client->irq; + + gpio_direction_input(client->irq); + #if 0 + if((ret = request_irq(rk610_hdmi->irq, rk610_hdmi_interrupt, IRQ_TYPE_EDGE_RISING,client->name, hdmi))<0){ + RK610_ERR(&client->dev, "fail to request gpio %d\n", client->irq); + goto err_gpio_free; + } + #endif +#ifdef CONFIG_HAS_EARLYSUSPEND + rk610_hdmi->early_suspend.suspend = rk610_hdmi_early_suspend; + rk610_hdmi->early_suspend.resume = rk610_hdmi_early_resume; + rk610_hdmi->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN - 1; + register_early_suspend(&rk610_hdmi->early_suspend); +#endif +#ifdef CONFIG_RK610_DEBUG + device_create_file(&(client->dev), &rk610_attrs[0]); +#endif + rk610_hdmi_init(rk610_hdmi->hdmi); + dev_info(&client->dev, "rk610_hdmi i2c probe ok\n"); + return 0; +err_gpio_free: + gpio_free(client->irq); +err_hdmi_unregister: + hdmi_unregister(hdmi); + rk610_hdmi = NULL; + return ret; +} + +static int __devexit rk610_hdmi_i2c_remove(struct i2c_client *client) +{ + struct rk610_hdmi_inf *rk610_hdmi = (struct rk610_hdmi_inf *)i2c_get_clientdata(client); + struct hdmi *hdmi = rk610_hdmi->hdmi; + + gpio_free(client->irq); + hdmi_unregister(hdmi); + rk610_hdmi = NULL; + return 0; +} +static const struct i2c_device_id rk610_hdmi_id[] = { + { "rk610_hdmi", 0 }, + { } +}; + +static struct i2c_driver rk610_hdmi_i2c_driver = { + .driver = { + .name = "rk610_hdmi", + }, + .probe = &rk610_hdmi_i2c_probe, + .remove = &rk610_hdmi_i2c_remove, + .id_table = rk610_hdmi_id, +}; + +static int __init rk610_hdmi_module_init(void) +{ + return i2c_add_driver(&rk610_hdmi_i2c_driver); +} + +static void __exit rk610_hdmi_module_exit(void) +{ + i2c_del_driver(&rk610_hdmi_i2c_driver); +} + +late_initcall(rk610_hdmi_module_init); +//module_init(rk610_hdmi_module_init); +module_exit(rk610_hdmi_module_exit); diff --git a/drivers/video/hdmi/chips/rk610/rk610_hdmi.h b/drivers/video/hdmi/chips/rk610/rk610_hdmi.h new file mode 100644 index 000000000000..03ae3c5a9190 --- /dev/null +++ b/drivers/video/hdmi/chips/rk610/rk610_hdmi.h @@ -0,0 +1,36 @@ +#ifndef _RK610_H +#define _RK610_H + +#include +#include + +/************RK610 device addr***********/ +#define RK610_CTRL_ADDR 0x40 +#define RK610_TVE_ADDR 0x42 +#define RK610_HDMI_ADDR 0x46 +#define RK610_CODEC_ADDR 0xc0 // 0x11xxxxxx + + +/****************HDMI STRUCT********************************/ + + +struct rk610_hdmi_inf{ + int irq; + int gpio; + int init; + struct i2c_client *client; + struct hdmi *hdmi; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif +}; + +/******************TVE STRUCT **************/ + +/*************RK610 STRUCT**********************************/ +//struct rk610_pdata { +// struct rk610_hdmi_inf hdmi; +// struct rk610_lcd_info lcd; +//}; +/*****************END ***********************************/ +#endif diff --git a/drivers/video/hdmi/chips/rk610/rk610_hdmi_hw.c b/drivers/video/hdmi/chips/rk610/rk610_hdmi_hw.c new file mode 100644 index 000000000000..ba34084b2a83 --- /dev/null +++ b/drivers/video/hdmi/chips/rk610/rk610_hdmi_hw.c @@ -0,0 +1,1094 @@ +#include +#include +#include "rk610_hdmi.h" +#include "rk610_hdmi_hw.h" +static struct rk610_hdmi_hw_inf g_hw_inf; +static EDID_INF g_edid; +static byte edid_buf[EDID_BLOCK_SIZE]; +static int RK610_hdmi_soft_reset(struct i2c_client *client); +static int Rk610_hdmi_i2c_read_p0_reg(struct i2c_client *client, char reg, char *val) +{ + return i2c_master_reg8_recv(client, reg, val, 1, 100*1000) > 0? 0: -EINVAL; +} +static int Rk610_hdmi_i2c_write_p0_reg(struct i2c_client *client, char reg, char *val) +{ + return i2c_master_reg8_send(client, reg, val, 1, 100*1000) > 0? 0: -EINVAL; +} + +static int Rk610_hdmi_pwr_mode(struct i2c_client *client, int mode) +{ + char c; + int ret=0; + switch(mode){ + case NORMAL: + c=0x82; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0xe3, &c); + c=0x00; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0xe5, &c); + c=0x00; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0xe7, &c); + c=0x00; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0xe4, &c); + c=0x8e; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0xe1, &c); + break; + case LOWER_PWR: + c=0x02; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0xe3, &c); + c=0x1c; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0xe5, &c); + break; + case SHUTDOWN: + c=0x02; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0xe3, &c); + c=0x1c; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0xe5, &c); + c=0x04; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0xe7, &c); + c=0x03; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0xe4, &c); + c=0x8c; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0xe1, &c); + break; + default: + RK610_ERR(&client->dev,"unkown rk610 hdmi pwr mode %d\n",mode); + } + return ret; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +int Rk610_hdmi_suspend(struct i2c_client *client) +{ + int ret = 0; + RK610_DBG(&client->dev,"%s \n",__FUNCTION__); + ret = Rk610_hdmi_pwr_mode(client,SHUTDOWN); + return ret; +} +int Rk610_hdmi_resume(struct i2c_client *client) +{ + int ret = 0; + RK610_DBG(&client->dev, "%s \n",__FUNCTION__); + ret = Rk610_hdmi_pwr_mode(client,NORMAL); + RK610_hdmi_soft_reset(client); + return ret; +} +#endif +static int Rk610_hdmi_sys_power_down(struct i2c_client *client) +{ + char c = 0; + int ret = 0; + RK610_DBG(&client->dev,"%s \n",__FUNCTION__); + c= RK610_SYS_CLK<<2 |RK610_SYS_PWR_OFF<<1 |RK610_INT_POL; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0x00, &c); + return ret; +} +static int Rk610_hdmi_sys_power_up(struct i2c_client *client) +{ + char c = 0; + int ret = 0; + RK610_DBG(&client->dev,"%s \n",__FUNCTION__); + c= RK610_SYS_CLK<<2 |RK610_SYS_PWR_ON<<1 |RK610_INT_POL; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0x00, &c); + return ret; +} +//X=11.2896M/(4*100k), X = {0x4c,0x4b} +static int RK610_DDC_BUS_CONFIG(struct i2c_client *client) +{ + char c = 0; + int ret = 0; + c= RK610_DDC_CONFIG&0xff; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0x4b, &c); + c= (RK610_DDC_CONFIG>>8)&0xff; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0x4c, &c); + return ret; +} +static int RK610_read_edid_block(struct i2c_client *client,u8 block, u8 * buf) +{ + char c = 0; + int ret = 0,i; + u8 Segment = 0; + u8 Offset = 0; + if(block%2) + Offset = EDID_BLOCK_SIZE; + if(block/2) + Segment = 1; + RK610_DBG(&client->dev,"EDID DATA (Segment = %d Block = %d Offset = %d):\n", (int) Segment, (int) block, (int) Offset); + //set edid fifo first addr + c = 0x00; + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0x4f, &c); + //set edid word address 00/80 + c = Offset; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0x4e, &c); + //set edid segment pointer + c = Segment; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0x4d, &c); + + //enable edid interrupt + c=0xc6; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0xc0, &c); + //wait edid interrupt + msleep(100); + RK610_DBG(&client->dev,"Interrupt generated\n"); + c=0x00; + ret =Rk610_hdmi_i2c_read_p0_reg(client, 0xc1, &c); + RK610_DBG(&client->dev,"Interrupt reg=%x \n",c); + //clear EDID interrupt reg + c=0x04; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0xc1, &c); + msleep(100); + for(i=0; i Extension Tag Error\n"); + return EDID_EXT_TAG_ERROR; + } + + if (Data[EDID_REV_ADDR] != EDID_REV_THREE) + { + RK610_HDMI_ERR("EDID -> Revision Error\n")); + return EDID_REV_ADDR_ERROR; + } + + LongDescriptorOffset = Data[LONG_DESCR_PTR_IDX]; // block offset where long descriptors start + + g_edid.UnderScan = ((Data[MISC_SUPPORT_IDX]) >> 7) & LSBIT; // byte #3 of CEA extension version 3 + g_edid.BasicAudio = ((Data[MISC_SUPPORT_IDX]) >> 6) & LSBIT; + g_edid.YCbCr_4_4_4 = ((Data[MISC_SUPPORT_IDX]) >> 5) & LSBIT; + g_edid.YCbCr_4_2_2 = ((Data[MISC_SUPPORT_IDX]) >> 4) & LSBIT; + + DataIndex = EDID_DATA_START; // 4 + + while (DataIndex < LongDescriptorOffset) + { + TagCode = (Data[DataIndex] >> 5) & THREE_LSBITS; + DataBlockLength = Data[DataIndex++] & FIVE_LSBITS; + if ((DataIndex + DataBlockLength) > LongDescriptorOffset) + { + RK610_HDMI_ERR("EDID -> V Descriptor Overflow\n"); + return EDID_V_DESCR_OVERFLOW; + } + + i = 0; // num of short video descriptors in current data block + + switch (TagCode) + { + case VIDEO_D_BLOCK: + while ((i < DataBlockLength) && (i < MAX_V_DESCRIPTORS)) // each SVD is 1 byte long + { + g_edid.VideoDescriptor[V_DescriptorIndex++] = Data[DataIndex++]; + i++; + } + DataIndex += DataBlockLength - i; // if there are more STDs than MAX_V_DESCRIPTORS, skip the last ones. Update DataIndex + + RK610_RK610_DBG(&client->dev,"EDID -> Short Descriptor Video Block\n"); + break; + + case AUDIO_D_BLOCK: + while (i < DataBlockLength/3) // each SAD is 3 bytes long + { + j = 0; + while (j < AUDIO_DESCR_SIZE) // 3 + { + g_edid.AudioDescriptor[A_DescriptorIndex][j++] = Data[DataIndex++]; + } + A_DescriptorIndex++; + i++; + } + RK610_HDMI_DBG("EDID -> Short Descriptor Audio Block\n"); + break; + + case SPKR_ALLOC_D_BLOCK: + g_edid.SpkrAlloc[i++] = Data[DataIndex++]; // although 3 bytes are assigned to Speaker Allocation, only + DataIndex += 2; // the first one carries information, so the next two are ignored by this code. + RK610_HDMI_DBG("EDID -> Short Descriptor Speaker Allocation Block\n"); + break; + + case USE_EXTENDED_TAG: + ExtendedTagCode = Data[DataIndex++]; + + switch (ExtendedTagCode) + { + case VIDEO_CAPABILITY_D_BLOCK: + RK610_HDMI_DBG("EDID -> Short Descriptor Video Capability Block\n"); + + // TO BE ADDED HERE: Save "video capability" parameters in g_edid data structure + // Need to modify that structure definition + // In the meantime: just increment DataIndex by 1 + DataIndex += 1; // replace with reading and saving the proper data per CEA-861 sec. 7.5.6 while incrementing DataIndex + break; + + case COLORIMETRY_D_BLOCK: + g_edid.ColorimetrySupportFlags = Data[DataIndex++] & BITS_1_0; + g_edid.MetadataProfile = Data[DataIndex++] & BITS_2_1_0; + + RK610_HDMI_DBG("EDID -> Short Descriptor Colorimetry Block\n"); + break; + } + break; + + case VENDOR_SPEC_D_BLOCK: + VSDB_BaseOffset = DataIndex - 1; + + if ((Data[DataIndex++] == 0x03) && // check if sink is HDMI compatible + (Data[DataIndex++] == 0x0C) && + (Data[DataIndex++] == 0x00)) + + g_edid.RK610_HDMI_Sink = TRUE; + else + g_edid.RK610_HDMI_Sink = FALSE; + + g_edid.CEC_A_B = Data[DataIndex++]; // CEC Physical address + g_edid.CEC_C_D = Data[DataIndex++]; + +#ifdef DEV_SUPPORT_CEC + // Take the Address that was passed in the EDID and use this API + // to set the physical address for CEC. + { + word phyAddr; + phyAddr = (word)g_edid.CEC_C_D; // Low-order nibbles + phyAddr |= ((word)g_edid.CEC_A_B << 8); // Hi-order nibbles + // Is the new PA different from the current PA? + if (phyAddr != SI_CecGetDevicePA ()) + { + // Yes! So change the PA + SI_CecSetDevicePA (phyAddr); + } + } +#endif + + if ((DataIndex + 7) > VSDB_BaseOffset + DataBlockLength) // Offset of 3D_Present bit in VSDB + g_edid._3D_Supported = FALSE; + else if (Data[DataIndex + 7] >> 7) + g_edid._3D_Supported = TRUE; + else + g_edid._3D_Supported = FALSE; + + DataIndex += DataBlockLength - RK610_HDMI_SIGNATURE_LEN - CEC_PHYS_ADDR_LEN; // Point to start of next block + RK610_HDMI_DBG("EDID -> Short Descriptor Vendor Block\n"); + break; + + default: + RK610_HDMI_DBG("EDID -> Unknown Tag Code\n"); + return EDID_UNKNOWN_TAG_CODE; + + } // End, Switch statement + } // End, while (DataIndex < LongDescriptorOffset) statement + + return EDID_SHORT_DESCRIPTORS_OK; +} + +//------------------------------------------------------------------------------ +// Function Name: Parse861LongDescriptors() +// Function Description: Parse CEA-861 extension long descriptors of the EDID block +// passed as a parameter and printf() them to the screen. +// +// Accepts: A pointer to the EDID block being parsed +// Returns: An error code if no long descriptors found; EDID_PARSED_OK if descriptors found. +// Globals: none +//------------------------------------------------------------------------------ +byte Parse861LongDescriptors (byte *Data) +{ + byte LongDescriptorsOffset; + byte DescriptorNum = 1; + + LongDescriptorsOffset = Data[LONG_DESCR_PTR_IDX]; // EDID block offset 2 holds the offset + + if (!LongDescriptorsOffset) // per CEA-861-D, table 27 + { + TPI_DEBUG_PRINT(("EDID -> No Detailed Descriptors\n")); + return EDID_NO_DETAILED_DESCRIPTORS; + } + + // of the 1st 18-byte descriptor + while (LongDescriptorsOffset + LONG_DESCR_LEN < EDID_BLOCK_SIZE) + { + TPI_EDID_PRINT(("Parse Results - CEA-861 Long Descriptor #%d:\n", (int) DescriptorNum)); + TPI_EDID_PRINT(("===============================================================\n")); + +#if (CONF__TPI_EDID_PRINT == ENABLE) + if (!ParseDetailedTiming(Data, LongDescriptorsOffset, EDID_BLOCK_2_3)) + break; +#endif + LongDescriptorsOffset += LONG_DESCR_LEN; + DescriptorNum++; + } + + return EDID_LONG_DESCRIPTORS_OK; +} + +//------------------------------------------------------------------------------ +// Function Name: Parse861Extensions() +// Function Description: Parse CEA-861 extensions from EDID ROM (EDID blocks beyond +// block #0). Save short descriptors in global structure +// g_edid. printf() long descriptors to the screen. +// +// Accepts: The number of extensions in the EDID being parsed +// Returns: EDID_PARSED_OK if EDID parsed correctly. Error code if failed. +// Globals: EDID data +// NOTE: Fields that are not supported by the 9022/4 (such as deep color) were not parsed. +//------------------------------------------------------------------------------ +byte Parse861Extensions (struct i2c_client *client,byte NumOfExtensions) +{ + byte i,j,k; + + byte ErrCode; + +// byte V_DescriptorIndex = 0; +// byte A_DescriptorIndex = 0; + + byte Block = 0; + + g_edid.HDMI_Sink = FALSE; + + do + { + Block++; + HDMI_DBG("\n"); + + for (j=0, i=0; j<128; j++) + { + k = edid_buf[j]; + HDMI_DBG("%2.2X ", (int) k); + i++; + + if (i == 0x10) + { + HDMI_DBG("\n"); + i = 0; + } + } + HDMI_DBG("\n"); + RK610_read_edid_block(client,Block, edid_buf); + if ((NumOfExtensions > 1) && (Block == 1)) + { + continue; + } + + ErrCode = Parse861ShortDescriptors(edid_buf); + if (ErrCode != EDID_SHORT_DESCRIPTORS_OK) + { + return ErrCode; + } + + ErrCode = Parse861LongDescriptors(edid_buf); + if (ErrCode != EDID_LONG_DESCRIPTORS_OK) + { + return ErrCode; + } + + } while (Block < NumOfExtensions); + + return EDID_OK; +} + +//------------------------------------------------------------------------------ +// Function Name: ParseEDID() +// Function Description: Extract sink properties from its EDID file and save them in +// global structure g_edid. +// +// Accepts: none +// Returns: TRUE or FLASE +// Globals: EDID data +// NOTE: Fields that are not supported by the 9022/4 (such as deep color) were not parsed. +//------------------------------------------------------------------------------ +static byte ParseEDID (byte *pEdid, byte *numExt) +{ + if (!CheckEDID_Header(pEdid)) + { + // first 8 bytes of EDID must be {0, FF, FF, FF, FF, FF, FF, 0} + HDMI_ERR("EDID -> Incorrect Header\n"); + return EDID_INCORRECT_HEADER; + } + + if (!DoEDID_Checksum(pEdid)) + { + // non-zero EDID checksum + HDMI_ERR("EDID -> Checksum Error\n"); + return EDID_CHECKSUM_ERROR; + } + + *numExt = pEdid[NUM_OF_EXTEN_ADDR]; // read # of extensions from offset 0x7E of block 0 + HDMI_DBG("EDID -> 861 Extensions = %d\n", (int) *numExt); + + if (!(*numExt)) + { + // No extensions to worry about + HDMI_DBG("EDID -> EDID_NO_861_EXTENSIONS\n"); + return EDID_NO_861_EXTENSIONS; + } + return EDID_OK; +} +#endif + +//------------------------------------------------------------------------------ +// Function Name: CheckEDID_Header() +// Function Description: Checks if EDID header is correct per VESA E-EDID standard +// +// Accepts: Pointer to 1st EDID block +// Returns: TRUE or FLASE +// Globals: EDID data +//------------------------------------------------------------------------------ +byte CheckEDID_Header (byte *Block) +{ + byte i = 0; + + if (Block[i]) // byte 0 must be 0 + return FALSE; + + for (i = 1; i < 1 + EDID_HDR_NO_OF_FF; i++) + { + if(Block[i] != 0xFF) // bytes [1..6] must be 0xFF + return FALSE; + } + + if (Block[i]) // byte 7 must be 0 + return FALSE; + + return TRUE; +} + +//------------------------------------------------------------------------------ +// Function Name: DoEDID_Checksum() +// Function Description: Calculte checksum of the 128 byte block pointed to by the +// pointer passed as parameter +// +// Accepts: Pointer to a 128 byte block whose checksum needs to be calculated +// Returns: TRUE or FLASE +// Globals: EDID data +//------------------------------------------------------------------------------ +byte DoEDID_Checksum (byte *Block) +{ + byte i; + byte CheckSum = 0; + + for (i = 0; i < EDID_BLOCK_SIZE; i++) + CheckSum += Block[i]; + + if (CheckSum) + return FALSE; + + return TRUE; +} +//------------------------------------------------------------------------------ +// Function Name: Parse861ShortDescriptors() +// Function Description: Parse CEA-861 extension short descriptors of the EDID block +// passed as a parameter and save them in global structure g_edid. +// +// Accepts: A pointer to the EDID 861 Extension block being parsed. +// Returns: EDID_PARSED_OK if EDID parsed correctly. Error code if failed. +// Globals: EDID data +// NOTE: Fields that are not supported by the 9022/4 (such as deep color) were not parsed. +//------------------------------------------------------------------------------ +byte Parse861ShortDescriptors (struct i2c_client *client,byte *Data) +{ + byte LongDescriptorOffset; + byte DataBlockLength; + byte DataIndex; + byte ExtendedTagCode; + byte VSDB_BaseOffset = 0; + + byte V_DescriptorIndex = 0; // static to support more than one extension + byte A_DescriptorIndex = 0; // static to support more than one extension + + byte TagCode; + + byte i; + byte j; + + if (Data[EDID_TAG_ADDR] != EDID_EXTENSION_TAG) + { + RK610_ERR(&client->dev,"EDID -> Extension Tag Error\n"); + return EDID_EXT_TAG_ERROR; + } + + if (Data[EDID_REV_ADDR] != EDID_REV_THREE) + { + RK610_ERR(&client->dev,"EDID -> Revision Error\n"); + return EDID_REV_ADDR_ERROR; + } + + LongDescriptorOffset = Data[LONG_DESCR_PTR_IDX]; // block offset where long descriptors start + + g_edid.UnderScan = ((Data[MISC_SUPPORT_IDX]) >> 7) & LSBIT; // byte #3 of CEA extension version 3 + g_edid.BasicAudio = ((Data[MISC_SUPPORT_IDX]) >> 6) & LSBIT; + g_edid.YCbCr_4_4_4 = ((Data[MISC_SUPPORT_IDX]) >> 5) & LSBIT; + g_edid.YCbCr_4_2_2 = ((Data[MISC_SUPPORT_IDX]) >> 4) & LSBIT; + + DataIndex = EDID_DATA_START; // 4 + + while (DataIndex < LongDescriptorOffset) + { + TagCode = (Data[DataIndex] >> 5) & THREE_LSBITS; + DataBlockLength = Data[DataIndex++] & FIVE_LSBITS; + if ((DataIndex + DataBlockLength) > LongDescriptorOffset) + { + RK610_ERR(&client->dev,"EDID -> V Descriptor Overflow\n"); + return EDID_V_DESCR_OVERFLOW; + } + + i = 0; // num of short video descriptors in current data block + + switch (TagCode) + { + case VIDEO_D_BLOCK: + while ((i < DataBlockLength) && (i < MAX_V_DESCRIPTORS)) // each SVD is 1 byte long + { + g_edid.VideoDescriptor[V_DescriptorIndex++] = Data[DataIndex++]; + i++; + } + DataIndex += DataBlockLength - i; // if there are more STDs than MAX_V_DESCRIPTORS, skip the last ones. Update DataIndex + + RK610_DBG(&client->dev,"EDID -> Short Descriptor Video Block\n"); + break; + + case AUDIO_D_BLOCK: + while (i < DataBlockLength/3) // each SAD is 3 bytes long + { + j = 0; + while (j < AUDIO_DESCR_SIZE) // 3 + { + g_edid.AudioDescriptor[A_DescriptorIndex][j++] = Data[DataIndex++]; + } + A_DescriptorIndex++; + i++; + } + RK610_DBG(&client->dev,"EDID -> Short Descriptor Audio Block\n"); + break; + + case SPKR_ALLOC_D_BLOCK: + g_edid.SpkrAlloc[i++] = Data[DataIndex++]; // although 3 bytes are assigned to Speaker Allocation, only + DataIndex += 2; // the first one carries information, so the next two are ignored by this code. + RK610_DBG(&client->dev,"EDID -> Short Descriptor Speaker Allocation Block\n"); + break; + + case USE_EXTENDED_TAG: + ExtendedTagCode = Data[DataIndex++]; + + switch (ExtendedTagCode) + { + case VIDEO_CAPABILITY_D_BLOCK: + RK610_DBG(&client->dev,"EDID -> Short Descriptor Video Capability Block\n"); + + // TO BE ADDED HERE: Save "video capability" parameters in g_edid data structure + // Need to modify that structure definition + // In the meantime: just increment DataIndex by 1 + DataIndex += 1; // replace with reading and saving the proper data per CEA-861 sec. 7.5.6 while incrementing DataIndex + break; + + case COLORIMETRY_D_BLOCK: + g_edid.ColorimetrySupportFlags = Data[DataIndex++] & BITS_1_0; + g_edid.MetadataProfile = Data[DataIndex++] & BITS_2_1_0; + + RK610_DBG(&client->dev,"EDID -> Short Descriptor Colorimetry Block\n"); + break; + } + break; + + case VENDOR_SPEC_D_BLOCK: + VSDB_BaseOffset = DataIndex - 1; + + if ((Data[DataIndex++] == 0x03) && // check if sink is HDMI compatible + (Data[DataIndex++] == 0x0C) && + (Data[DataIndex++] == 0x00)) + + g_edid.HDMI_Sink = TRUE; + else + g_edid.HDMI_Sink = FALSE; + + g_edid.CEC_A_B = Data[DataIndex++]; // CEC Physical address + g_edid.CEC_C_D = Data[DataIndex++]; + +#ifdef DEV_SUPPORT_CEC + // Take the Address that was passed in the EDID and use this API + // to set the physical address for CEC. + { + word phyAddr; + phyAddr = (word)g_edid.CEC_C_D; // Low-order nibbles + phyAddr |= ((word)g_edid.CEC_A_B << 8); // Hi-order nibbles + // Is the new PA different from the current PA? + if (phyAddr != SI_CecGetDevicePA ()) + { + // Yes! So change the PA + SI_CecSetDevicePA (phyAddr); + } + } +#endif + + if ((DataIndex + 7) > VSDB_BaseOffset + DataBlockLength) // Offset of 3D_Present bit in VSDB + g_edid._3D_Supported = FALSE; + else if (Data[DataIndex + 7] >> 7) + g_edid._3D_Supported = TRUE; + else + g_edid._3D_Supported = FALSE; + + DataIndex += DataBlockLength - HDMI_SIGNATURE_LEN - CEC_PHYS_ADDR_LEN; // Point to start of next block + RK610_DBG(&client->dev,"EDID -> Short Descriptor Vendor Block\n"); + break; + + default: + RK610_ERR(&client->dev,"EDID -> Unknown Tag Code\n"); + return EDID_UNKNOWN_TAG_CODE; + + } // End, Switch statement + } // End, while (DataIndex < LongDescriptorOffset) statement + + return EDID_SHORT_DESCRIPTORS_OK; +} +//------------------------------------------------------------------------------ +// Function Name: ParseEDID() +// Function Description: Extract sink properties from its EDID file and save them in +// global structure g_edid. +// +// Accepts: none +// Returns: TRUE or FLASE +// Globals: EDID data +// NOTE: Fields that are not supported by the 9022/4 (such as deep color) were not parsed. +//------------------------------------------------------------------------------ +static byte ParseEDID (struct i2c_client *client,byte *pEdid, byte *numExt) +{ + if (!CheckEDID_Header(pEdid)) + { + // first 8 bytes of EDID must be {0, FF, FF, FF, FF, FF, FF, 0} + RK610_ERR(&client->dev,"EDID -> Incorrect Header\n"); + return EDID_INCORRECT_HEADER; + } + + if (!DoEDID_Checksum(pEdid)) + { + // non-zero EDID checksum + RK610_ERR(&client->dev,"EDID -> Checksum Error\n"); + return EDID_CHECKSUM_ERROR; + } + + *numExt = pEdid[NUM_OF_EXTEN_ADDR]; // read # of extensions from offset 0x7E of block 0 + RK610_DBG(&client->dev,"EDID -> 861 Extensions = %d\n", (int) *numExt); + + if (!(*numExt)) + { + // No extensions to worry about + RK610_DBG(&client->dev,"EDID -> EDID_NO_861_EXTENSIONS\n"); + return EDID_NO_861_EXTENSIONS; + } + return EDID_OK; +} +byte DoEdidRead (struct i2c_client *client) +{ + u8 NumOfExtensions=0; + u8 Result; + u8 i,j; + // If we already have valid EDID data, ship this whole thing + if (g_edid.edidDataValid == FALSE) + { + Rk610_hdmi_sys_power_up(client); + // Request access to DDC bus from the receiver + RK610_DDC_BUS_CONFIG(client); + memset(edid_buf,0,EDID_BLOCK_SIZE); + RK610_read_edid_block(client,EDID_BLOCK0, edid_buf); // read first 128 bytes of EDID ROM + RK610_DBG(&client->dev,"/************first block*******/\n"); + for (j=0; jdev,"\n/************block %d*******/\n",i); + memset(edid_buf,0,EDID_BLOCK_SIZE); + RK610_read_edid_block(client,i, edid_buf); + Parse861ShortDescriptors(client,edid_buf); + for (j=0; jdev,"EDID -> No 861 Extensions\n"); + g_edid.HDMI_Sink = FALSE; + g_edid.YCbCr_4_4_4 = FALSE; + g_edid.YCbCr_4_2_2 = FALSE; + g_edid.CEC_A_B = 0x00; + g_edid.CEC_C_D = 0x00; + } + else + { + RK610_DBG(&client->dev,"EDID -> Parse FAILED\n"); + g_edid.HDMI_Sink = TRUE; + g_edid.YCbCr_4_4_4 = FALSE; + g_edid.YCbCr_4_2_2 = FALSE; + g_edid.CEC_A_B = 0x00; + g_edid.CEC_C_D = 0x00; + } + } + else + { + RK610_DBG(&client->dev,"EDID -> Parse OK\n"); + Result = Parse861Extensions(NumOfExtensions); // Parse 861 Extensions (short and long descriptors); + if (Result != EDID_OK) + { + RK610_DBG(&client->dev,"EDID -> Extension Parse FAILED\n"); + g_edid.HDMI_Sink = FALSE; + g_edid.YCbCr_4_4_4 = FALSE; + g_edid.YCbCr_4_2_2 = FALSE; + g_edid.CEC_A_B = 0x00; + g_edid.CEC_C_D = 0x00; + } + else + { + RK610_DBG(&client->dev,"EDID -> Extension Parse OK\n"); + g_edid.HDMI_Sink = TRUE; + } + } +#endif + RK610_DBG(&client->dev,"EDID -> NumOfExtensions = %d\n", NumOfExtensions); + RK610_DBG(&client->dev,"EDID -> g_edid.HDMI_Sink = %d\n", (int)g_edid.HDMI_Sink); + //RK610_DBG(&client->dev,"EDID -> g_edid.YCbCr_4_4_4 = %d\n", (int)g_edid.YCbCr_4_4_4); + //RK610_DBG(&client->dev,"EDID -> g_edid.YCbCr_4_2_2 = %d\n", (int)g_edid.YCbCr_4_2_2); + //RK610_DBG(&client->dev,"EDID -> g_edid.CEC_A_B = 0x%x\n", (int)g_edid.CEC_A_B); + //RK610_DBG(&client->dev,"EDID -> g_edid.CEC_C_D = 0x%x\n", (int)g_edid.CEC_C_D); + + g_edid.edidDataValid = TRUE; + } + return TRUE; +} +static int Rk610_hdmi_Display_switch(struct i2c_client *client) +{ + char c; + int ret=0; + int mode; + mode = (g_edid.HDMI_Sink == TRUE)? DISPLAY_HDMI:DISPLAY_DVI; + ret = Rk610_hdmi_i2c_read_p0_reg(client, 0x52, &c); + c &= ((~(1<<1))| mode<<1); + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0x52, &c); + RK610_DBG(&client->dev,">>>%s mode=%d,c=%x",__func__,mode,c); + return ret; +} + +static int Rk610_hdmi_Config_audio_informat(struct i2c_client *client) +{ + char c; + int ret=0; + RK610_DBG(&client->dev,"%s \n",__FUNCTION__); + //Select configure for Audio Info + c=0x08; + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0x9f, &c); + //Configure the Audio info to HDMI RX. + c=0x84; //HB0 + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0xa0, &c); + c=0x01; //HB1 + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0xa1, &c); + c=0x0a; //HB2 + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0xa2, &c); + //c=0x00; //PB0 + //ret = Rk610_hdmi_i2c_write_p0_reg(client, 0xa3, &c); + c=0x11; //PB1 + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0xa4, &c); + c=0x09; //PB2 + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0xa5, &c); + c=0x00; //PB3 + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0xa6, &c); + c=0x00; //PB4 + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0xa7, &c); + c=0x01; //PB5 + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0xa8, &c); + return ret; +} + +static int Rk610_hdmi_Config_Avi_informat(struct i2c_client *client ,u8 vic) +{ + char c; + int ret=0; + RK610_DBG(&client->dev,"%s \n",__FUNCTION__); + //Select configure for AVI Info + c = 0x06; + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0x9f, &c); + + //Configure the AVI info to HDMI RX + c = 0x82; //HB0 + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0xa0, &c); + c = 0x02; //HB1 + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0xa1, &c); + c = 0x0d; //HB2 + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0xa2, &c); + //c=0x00; //PB0 + //ret = Rk610_hdmi_i2c_write_p0_reg(client, 0xa3, &c); + c = 0x00; //PB1 + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0xa4, &c); + c = 0x08; //PB2 + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0xa5, &c); + c = 0x70; //PB3 + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0xa6, &c); + c = vic; //PB4 + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0xa7, &c); + c = 0x40; //PB5 + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0xa8, &c); + return ret; +} +static int Rk610_hdmi_Config_Video(struct i2c_client *client, u8 video_format) +{ + char vic; + int ret = 0; + RK610_DBG(&client->dev,"%s \n",__FUNCTION__); + switch(video_format){ + case HDMI_720x480p_60Hz_4x3: + case HDMI_720x480p_60Hz_16x9: + vic = 0x02; + break; + case HDMI_720x576p_50Hz_4x3: + case HDMI_720x576p_50Hz_16x9: + vic = 0x11; + break; + case HDMI_1280x720p_50Hz: + vic = 0x13; + break; + case HDMI_1280x720p_60Hz: + vic = 0x04; + break; + case HDMI_1920x1080p_50Hz: + vic = 0x1f; + break; + case HDMI_1920x1080p_60Hz: + vic = 0x10; + break; + default: + vic = 0x04; + break; + } + ret = Rk610_hdmi_Config_Avi_informat(client,vic); + return ret; +} +static int Rk610_hdmi_Config_Audio(struct i2c_client *client ,u8 audio_fs) +{ + char c=0; + int ret = 0; + RK610_DBG(&client->dev,"%s \n",__FUNCTION__); + c=0x01; + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0x35, &c); + c=0x3c; + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0x38, &c); + c=0x00; + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0x39, &c); + c=0x18; + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0x40, &c); + switch(audio_fs){ + case HDMI_I2S_Fs_44100: + c=0x80; + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0x41, &c); + break; + case HDMI_I2S_Fs_48000: + c=0x92; + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0x41, &c); + break; + default: + c=0x80; + ret = Rk610_hdmi_i2c_write_p0_reg(client, 0x41, &c); + break; + } + Rk610_hdmi_Config_audio_informat(client); + return ret; +} + +int Rk610_hdmi_Set_Video(u8 video_format) +{ + if(g_hw_inf.video_format !=video_format){ + g_hw_inf.video_format = video_format; + g_hw_inf.config_param |= VIDEO_CHANGE; + } + return 0; +} +int Rk610_hdmi_Set_Audio(u8 audio_fs) +{ + if(g_hw_inf.audio_fs !=audio_fs){ + g_hw_inf.audio_fs = audio_fs; + g_hw_inf.config_param |= AUDIO_CHANGE; + } + return 0; +} +static int RK610_hdmi_Driver_mode(struct i2c_client *client) +{ + char c; + int ret=0; + c=0x8e; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0xe1, &c); + c=0x04; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0xe2, &c); + return 0; +} +static int RK610_hdmi_PLL_mode(struct i2c_client *client) +{ + char c; + int ret=0; + c=0x10; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0xe8, &c); + c=0x2c; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0xe6, &c); + c=0x00; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0xe5, &c); + return 0; +} +void Rk610_hdmi_event_work(struct i2c_client *client, bool *hpd) +{ + char c=0; + int ret=0; + + c=0x00; + ret =Rk610_hdmi_i2c_read_p0_reg(client, 0xc1, &c); + if(c & RK610_HPD_EVENT){ + RK610_DBG(&client->dev,">>>HPD EVENT\n"); + /**********clear hpd event******/ + c=RK610_HPD_EVENT; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0xc1, &c); + c=0x00; + ret =Rk610_hdmi_i2c_read_p0_reg(client, 0xc8, &c); + if(c & RK610_HPD_PLUG ){ + RK610_DBG(&client->dev,">>> hdmi plug \n"); + DoEdidRead(client); + Rk610_hdmi_Display_switch(client); + Rk610_hdmi_pwr_mode(client,NORMAL); + *hpd=1; + } + else{ + RK610_DBG(&client->dev,">>> hdmi unplug \n"); + g_edid.edidDataValid = FALSE; + Rk610_hdmi_pwr_mode(client,LOWER_PWR); + *hpd=0; + } + + } + if(c & RK610_EDID_EVENT){ + /**********clear hpd event******/ + c=RK610_EDID_EVENT; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0xc1, &c); + RK610_DBG(&client->dev,">>>EDID EVENT\n"); + /*************clear edid event*********/ + } +} +int Rk610_hdmi_Config_Done(struct i2c_client *client) +{ + char c; + int ret=0; + RK610_DBG(&client->dev,"%s \n",__FUNCTION__); + + ret =Rk610_hdmi_sys_power_up(client); + + if(g_hw_inf.config_param != 0){ + c=0x08; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0x04, &c); + c=0x01; + ret =Rk610_hdmi_i2c_write_p0_reg(client, 0x01, &c); + if(g_hw_inf.config_param & VIDEO_CHANGE){ + Rk610_hdmi_Config_Video(client,g_hw_inf.video_format); + g_hw_inf.config_param &= (~VIDEO_CHANGE); + } + if(g_hw_inf.config_param & AUDIO_CHANGE){ + Rk610_hdmi_Config_Audio(client,g_hw_inf.audio_fs); + g_hw_inf.config_param &= (~AUDIO_CHANGE); + } + } + ret =Rk610_hdmi_sys_power_down(client); + ret =Rk610_hdmi_sys_power_up(client); + ret =Rk610_hdmi_sys_power_down(client); + + return ret; +} +#if 0 +int Rk610_hdmi_hpd(struct i2c_client *client) +{ + char c; + RK610_DBG(&client->dev,"%s \n",__FUNCTION__); + if(Rk610_hdmi_i2c_read_p0_reg(client, 0xc8, &c)<0){ + RK610_ERR(">>>%s I2c trans err",__FUNCTION__); + return -1; + } + if() + return (c & RK610_HPD_PLUG)?1:0; +} +#endif +static int RK610_hdmi_soft_reset(struct i2c_client *client) +{ + char c; + int ret; + //soft reset + c=0x00; + ret =Rk610_hdmi_i2c_read_p0_reg(client, 0xce, &c); + msleep(10); + c=0x01; + ret =Rk610_hdmi_i2c_read_p0_reg(client, 0xce, &c); + msleep(100); + return ret; +} +static void Rk610_hdmi_Variable_Initial(void) +{ + memset(&g_hw_inf,0,sizeof(struct rk610_hdmi_hw_inf)); + g_edid.edidDataValid = FALSE; + g_hw_inf.edid_inf = &g_edid; + g_hw_inf.audio_fs = HDMI_I2S_DEFAULT_Fs; + g_hw_inf.video_format = HDMI_DEFAULT_RESOLUTION; + g_hw_inf.config_param = AUDIO_CHANGE | VIDEO_CHANGE; + +} +int Rk610_hdmi_init(struct i2c_client *client) +{ + RK610_DBG(&client->dev,"%s \n",__FUNCTION__); + Rk610_hdmi_Variable_Initial(); + RK610_hdmi_soft_reset(client); + RK610_hdmi_Driver_mode(client); + RK610_hdmi_PLL_mode(client); + Rk610_hdmi_Set_Video(g_hw_inf.video_format); + Rk610_hdmi_Set_Audio(g_hw_inf.audio_fs); + Rk610_hdmi_Config_Done(client); + + return 0; +} diff --git a/drivers/video/hdmi/chips/rk610/rk610_hdmi_hw.h b/drivers/video/hdmi/chips/rk610/rk610_hdmi_hw.h new file mode 100644 index 000000000000..59c296578f05 --- /dev/null +++ b/drivers/video/hdmi/chips/rk610/rk610_hdmi_hw.h @@ -0,0 +1,208 @@ +#ifndef _RK610_HDMI_HW_H +#define _RK610_HDMI_HW_H +#include + +#define MAX_V_DESCRIPTORS 20 +#define MAX_A_DESCRIPTORS 10 +#define MAX_SPEAKER_CONFIGURATIONS 4 +#define AUDIO_DESCR_SIZE 3 + +#define EDID_BLOCK_SIZE 128 +#define NUM_OF_EXTEN_ADDR 0x7e +#define EDID_HDR_NO_OF_FF 0x06 + +// Data Block Tag Codes +//==================================================== +#define AUDIO_D_BLOCK 0x01 +#define VIDEO_D_BLOCK 0x02 +#define VENDOR_SPEC_D_BLOCK 0x03 +#define SPKR_ALLOC_D_BLOCK 0x04 +#define USE_EXTENDED_TAG 0x07 +// Extended Data Block Tag Codes +//==================================================== +#define COLORIMETRY_D_BLOCK 0x05 + +#define HDMI_SIGNATURE_LEN 0x03 + +#define CEC_PHYS_ADDR_LEN 0x02 +#define EDID_EXTENSION_TAG 0x02 +#define EDID_REV_THREE 0x03 +#define EDID_DATA_START 0x04 + +#define EDID_BLOCK_0 0x00 +#define EDID_BLOCK_2_3 0x01 + +#define VIDEO_CAPABILITY_D_BLOCK 0x00 + +//#define DEV_SUPPORT_CEC +#if 1 +#define MSBIT 0x80 +#define LSBIT 0x01 + +#define TWO_LSBITS 0x03 +#define THREE_LSBITS 0x07 +#define FOUR_LSBITS 0x0F +#define FIVE_LSBITS 0x1F +#define SEVEN_LSBITS 0x7F +#define TWO_MSBITS 0xC0 +#define EIGHT_BITS 0xFF +#define BYTE_SIZE 0x08 +#define BITS_1_0 0x03 +#define BITS_2_1 0x06 +#define BITS_2_1_0 0x07 +#define BITS_3_2 0x0C +#define BITS_4_3_2 0x1C +#define BITS_5_4 0x30 +#define BITS_5_4_3 0x38 +#define BITS_6_5 0x60 +#define BITS_6_5_4 0x70 +#define BITS_7_6 0xC0 + +#define TPI_INTERNAL_PAGE_REG 0xBC +#define TPI_INDEXED_OFFSET_REG 0xBD +#define TPI_INDEXED_VALUE_REG 0xBE + +#define EDID_TAG_ADDR 0x00 +#define EDID_REV_ADDR 0x01 +#define EDID_TAG_IDX 0x02 +#define LONG_DESCR_PTR_IDX 0x02 +#define MISC_SUPPORT_IDX 0x03 + +#define ESTABLISHED_TIMING_INDEX 35 // Offset of Established Timing in EDID block +#define NUM_OF_STANDARD_TIMINGS 8 +#define STANDARD_TIMING_OFFSET 38 +#define LONG_DESCR_LEN 18 +#define NUM_OF_DETAILED_DESCRIPTORS 4 + +#define DETAILED_TIMING_OFFSET 0x36 +#endif +enum{ + EDID_BLOCK0=0, + EDID_BLOCK1, + EDID_BLOCK2, + EDID_BLOCK3, +}; +#define RK610_SYS_FREG_CLK 11289600 +#define RK610_SCL_RATE (100*1000) +#define RK610_DDC_CONFIG (RK610_SYS_FREG_CLK>>2)/RK610_SCL_RATE + +#define FALSE 0 +#define TRUE 1 + +//EVENT +#define RK610_HPD_EVENT 1<<7 +#define RK610_HPD_PLUG 1<<7 +#define RK610_EDID_EVENT 1<<2 + +//output mode 0x52 +#define DISPLAY_DVI 0 +#define DISPLAY_HDMI 1 + +//0x00 +#define RK610_INT_POL 1 +#define RK610_SYS_PWR_ON 1 +#define RK610_SYS_PWR_OFF 0 +#define RK610_PHY_CLK 0 +#define RK610_SYS_CLK 1 + +#define RK610_MCLK_FS 0x01 //256fs +//0x01 +// INPUT_VIDEO_FORMAT +#define RGB_YUV444 0x00 +#define DDR_RGB444_YUV444 0x05 +#define DDR_YUV422 0x06 + +//0x02 +//video output format +#define RGB444 0x00 +#define YUV444 0x01 +#define YUV422 0x02 + +//DATA WIDTH +#define DATA_12BIT 0X00 +#define DATA_10BIT 0X01 +#define DATA_8BIT 0X03 + +//0X04 +//1:after 0:not After 1st sof for external DE sample +#define DE_AFTER_SOF 0 +#define DE_NOAFTER_SOF 1 + +#define CSC_ENABLE 0 +#define CSC_DISABLE 1 + +//0X05 +#define CLEAR_AVMUTE(x) (x)<<7 +#define SET_AVMUTE(x) (x)<<6 +#define AUDIO_MUTE(x) (x)<<1 +#define VIDEO_BLACK(x) (x)<<0 //1:black 0:normal + +//0x08 +#define VSYNC_POL(x) (x)<<3 //0:Negative 1:Positive +#define HSYNC_POL(x) (x)<<2 //0:Negative 1:Positive +#define INTER_PROGRESSIVE(x) (x)<<1 //0: progressive 1:interlace +#define VIDEO_SET_ENABLE(x) (x)<<0 //0:disable 1: enable + +/**********CONFIG CHANGE ************/ +#define VIDEO_CHANGE 1<<0 +#define AUDIO_CHANGE 1<<1 + +#define byte u8 + +typedef struct edid_info +{ // for storing EDID parsed data + byte edidDataValid; + byte VideoDescriptor[MAX_V_DESCRIPTORS]; // maximum number of video descriptors + byte AudioDescriptor[MAX_A_DESCRIPTORS][3]; // maximum number of audio descriptors + byte SpkrAlloc[MAX_SPEAKER_CONFIGURATIONS]; // maximum number of speaker configurations + byte UnderScan; // "1" if DTV monitor underscans IT video formats by default + byte BasicAudio; // Sink supports Basic Audio + byte YCbCr_4_4_4; // Sink supports YCbCr 4:4:4 + byte YCbCr_4_2_2; // Sink supports YCbCr 4:2:2 + byte HDMI_Sink; // "1" if HDMI signature found + byte CEC_A_B; // CEC Physical address. See HDMI 1.3 Table 8-6 + byte CEC_C_D; + byte ColorimetrySupportFlags; // IEC 61966-2-4 colorimetry support: 1 - xvYCC601; 2 - xvYCC709 + byte MetadataProfile; + byte _3D_Supported; + +} EDID_INF; +enum EDID_ErrorCodes +{ + EDID_OK, + EDID_INCORRECT_HEADER, + EDID_CHECKSUM_ERROR, + EDID_NO_861_EXTENSIONS, + EDID_SHORT_DESCRIPTORS_OK, + EDID_LONG_DESCRIPTORS_OK, + EDID_EXT_TAG_ERROR, + EDID_REV_ADDR_ERROR, + EDID_V_DESCR_OVERFLOW, + EDID_UNKNOWN_TAG_CODE, + EDID_NO_DETAILED_DESCRIPTORS, + EDID_DDC_BUS_REQ_FAILURE, + EDID_DDC_BUS_RELEASE_FAILURE +}; +enum PWR_MODE{ + NORMAL, + LOWER_PWR, + SHUTDOWN, +}; +struct rk610_hdmi_hw_inf{ + struct i2c_client *client; + EDID_INF *edid_inf; + u8 video_format; + u8 audio_fs; + u8 config_param; +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +extern int Rk610_hdmi_suspend(struct i2c_client *client); +extern int Rk610_hdmi_resume(struct i2c_client *client); +#endif +extern int Rk610_hdmi_Set_Video(u8 video_format); +extern int Rk610_hdmi_Set_Audio(u8 audio_fs); +extern int Rk610_hdmi_Config_Done(struct i2c_client *client); +extern void Rk610_hdmi_event_work(struct i2c_client *client, bool *hpd); +extern int Rk610_hdmi_init(struct i2c_client *client); +#endif \ No newline at end of file diff --git a/drivers/video/hdmi/hdmi-core.c b/drivers/video/hdmi/hdmi-core.c index 6e16767e35ad..1b17185b6da9 100755 --- a/drivers/video/hdmi/hdmi-core.c +++ b/drivers/video/hdmi/hdmi-core.c @@ -31,18 +31,24 @@ static void __hdmi_changed(struct hdmi *hdmi) mutex_lock(&hdmi->lock); precent = hdmi->ops->hdmi_precent(hdmi); - - if(precent && hdmi->mode == DISP_ON_LCD && hdmi->display_on){ + if(precent && (hdmi->mode == DISP_ON_LCD) && hdmi->display_on){ if(hdmi->ops->insert(hdmi) == 0){ - hdmi->mode = DISP_ON_HDMI; + hdmi->mode = hdmi->display_on; kobject_uevent(&hdmi->dev->kobj, KOBJ_CHANGE); } else hdmi_dbg(hdmi->dev, "insert error\n"); + hdmi_set_backlight(hdmi->display_on==DISP_ON_HDMI?HDMI_DISABLE: HDMI_ENABLE); + + } + else if(precent &&(hdmi->mode != hdmi->display_on)&& hdmi->display_on){ + hdmi->mode = hdmi->display_on; + hdmi_set_backlight(hdmi->display_on==DISP_ON_HDMI?HDMI_DISABLE: HDMI_ENABLE); } - else if((!precent || !hdmi->display_on) && hdmi->mode == DISP_ON_HDMI){ + else if((!precent || !hdmi->display_on) && hdmi->mode != DISP_ON_LCD){ if(hdmi->ops->remove(hdmi) == 0){ hdmi->mode = DISP_ON_LCD; + hdmi_set_backlight(HDMI_ENABLE); kobject_uevent(&hdmi->dev->kobj, KOBJ_CHANGE); } else @@ -61,7 +67,7 @@ void hdmi_suspend(struct hdmi *hdmi) { del_timer(&hdmi->timer); flush_delayed_work(&hdmi->work); - if(hdmi->mode == DISP_ON_HDMI){ + if(hdmi->mode != DISP_ON_LCD){ hdmi->ops->remove(hdmi); hdmi->mode = DISP_ON_LCD; } @@ -93,7 +99,7 @@ static void hdmi_detect_timer(unsigned long data) int precent = hdmi->ops->hdmi_precent(hdmi); if((precent && hdmi->mode == DISP_ON_LCD) || - (!precent && hdmi->mode == DISP_ON_HDMI)) + (!precent && hdmi->mode != DISP_ON_LCD)) hdmi_changed(hdmi, 100); mod_timer(&hdmi->timer, jiffies + msecs_to_jiffies(200)); } @@ -189,7 +195,7 @@ int hdmi_get_scale(void) struct hdmi* hdmi = get_hdmi_struct(0); if(!hdmi) return 100; - else if(hdmi->mode == DISP_ON_HDMI) + else if(hdmi->mode != DISP_ON_LCD) return hdmi->scale; else return 100; diff --git a/drivers/video/hdmi/hdmi-fb.c b/drivers/video/hdmi/hdmi-fb.c index 5d8b8da10a59..87269e7c6030 100755 --- a/drivers/video/hdmi/hdmi-fb.c +++ b/drivers/video/hdmi/hdmi-fb.c @@ -55,10 +55,10 @@ #define H_BP3 60 #define H_VD3 720 #define H_FP3 16 -#define V_PW3 5 -#define V_BP3 35 +#define V_PW3 6 +#define V_BP3 30 #define V_VD3 480 -#define V_FP3 5 +#define V_FP3 9 /* 1080p@50Hz Timing */ #define OUT_CLK5 148500000 @@ -67,9 +67,9 @@ #define H_VD4 1920 #define H_FP4 528 #define V_PW4 5 -#define V_BP4 35 +#define V_BP4 36 #define V_VD4 1080 -#define V_FP4 5 +#define V_FP4 4 /* 1080p@60Hz Timing */ #define OUT_CLK4 148500000 @@ -78,9 +78,9 @@ #define H_VD5 1920 #define H_FP5 88 #define V_PW5 5 -#define V_BP5 35 +#define V_BP5 36 #define V_VD5 1080 -#define V_FP5 5 +#define V_FP5 4 extern int FB_Switch_Screen( struct rk29fb_screen *screen, u32 enable ); @@ -98,6 +98,7 @@ static int anx7150_standby(u8 enable) struct rk29fb_screen hdmi_info[] = { { + .hdmi_resolution = HDMI_1280x720p_50Hz, .type = OUT_TYPE, .face = OUT_FACE, .x_res = H_VD0, @@ -123,6 +124,7 @@ struct rk29fb_screen hdmi_info[] = { .standby = anx7150_standby, }, //HDMI_1280x720p_50Hz { + .hdmi_resolution = HDMI_1280x720p_60Hz, .type = OUT_TYPE, .face = OUT_FACE, .x_res = H_VD1, @@ -148,6 +150,7 @@ struct rk29fb_screen hdmi_info[] = { .standby = anx7150_standby, }, //HDMI_1280x720p_60Hz { + .hdmi_resolution = HDMI_720x576p_50Hz_4x3, .type = OUT_TYPE, .face = OUT_FACE, .x_res = H_VD2, @@ -173,6 +176,7 @@ struct rk29fb_screen hdmi_info[] = { .standby = anx7150_standby, }, //HDMI_720x576p_50Hz_4x3 { + .hdmi_resolution = HDMI_720x576p_50Hz_16x9, .type = OUT_TYPE, .face = OUT_FACE, .x_res = H_VD2, @@ -198,6 +202,7 @@ struct rk29fb_screen hdmi_info[] = { .standby = anx7150_standby, }, //HDMI_720x576p_50Hz_16x9 { + .hdmi_resolution = HDMI_720x480p_60Hz_4x3, .type = OUT_TYPE, .face = OUT_FACE, .x_res = H_VD3, @@ -223,6 +228,7 @@ struct rk29fb_screen hdmi_info[] = { .standby = anx7150_standby, }, //HDMI_720x480p_60Hz_4x3 { + .hdmi_resolution = HDMI_720x480p_60Hz_16x9, .type = OUT_TYPE, .face = OUT_FACE, .x_res = H_VD3, @@ -248,6 +254,7 @@ struct rk29fb_screen hdmi_info[] = { .standby = anx7150_standby, }, //HDMI_720x480p_60Hz_16x9 { + .hdmi_resolution = HDMI_1920x1080p_50Hz, .type = OUT_TYPE, .face = OUT_FACE, .x_res = H_VD4, @@ -260,8 +267,8 @@ struct rk29fb_screen hdmi_info[] = { .upper_margin = V_BP4, .lower_margin = V_FP4, .vsync_len = V_PW4, - .pin_hsync = 0, - .pin_vsync = 0, + .pin_hsync = 1, + .pin_vsync = 1, .pin_den = 0, .pin_dclk = DCLK_POL, .swap_rb = SWAP_RB, @@ -273,6 +280,7 @@ struct rk29fb_screen hdmi_info[] = { .standby = anx7150_standby, }, //HDMI_1920x1080p_50Hz { + .hdmi_resolution = HDMI_1920x1080p_60Hz, .type = OUT_TYPE, .face = OUT_FACE, .x_res = H_VD5, @@ -285,8 +293,8 @@ struct rk29fb_screen hdmi_info[] = { .upper_margin = V_BP5, .lower_margin = V_FP5, .vsync_len = V_PW5, - .pin_hsync = 0, - .pin_vsync = 0, + .pin_hsync = 1, + .pin_vsync = 1, .pin_den = 0, .pin_dclk = DCLK_POL, .swap_rb = SWAP_RB, diff --git a/drivers/video/rk29_fb.c b/drivers/video/rk29_fb.c index e36f6e21eb55..9db07658bcd2 100644 --- a/drivers/video/rk29_fb.c +++ b/drivers/video/rk29_fb.c @@ -50,6 +50,7 @@ #include #endif + #include #include @@ -61,6 +62,9 @@ #include "./display/screen/screen.h" +#ifdef CONFIG_MFD_RK610 +#include "./display/lcd/rk610_lcd.h" +#endif #define ANDROID_USE_THREE_BUFS 0 //android use three buffers to accelerate UI display in rgb plane #define CURSOR_BUF_SIZE 256 //RK2818 cursor need 256B buf int rk29_cursor_buf[CURSOR_BUF_SIZE]; @@ -232,7 +236,18 @@ struct rk29fb_inf { #endif }; - +#ifdef CONFIG_BACKLIGHT_RK29_BL +/* drivers/video/backlight/rk29_backlight.c */ +extern void rk29_backlight_set(bool on); +#else +void rk29_backlight_set(bool on) +{ + /* please add backlight switching-related code here or on your backlight driver + parameter: on=1 ==> open spk + on=0 ==> close spk + */ +} +#endif typedef enum _TRSP_MODE { TRSP_CLOSE = 0, @@ -2922,10 +2937,13 @@ int FB_Switch_Screen( struct rk29fb_screen *screen, u32 enable ) if(inf->cur_screen->standby) inf->cur_screen->standby(1); // operate the display_on pin to power down the lcd - +#ifdef CONFIG_HDMI_DUAL_DISP + inf->panel1_info.sscreen_get(&inf->panel1_info,inf->panel2_info.hdmi_resolution); + inf->panel1_info.sscreen_set(&inf->panel1_info,enable); +#else if(enable && mach_info->io_disable)mach_info->io_disable(); //close lcd out else if (mach_info->io_enable)mach_info->io_enable(); //open lcd out - +#endif load_screen(inf->fb0, 0); mcu_refresh(inf); @@ -2933,6 +2951,7 @@ int FB_Switch_Screen( struct rk29fb_screen *screen, u32 enable ) fb0_set_par(inf->fb0); LcdMskReg(inf, DSP_CTRL1, m_BLACK_MODE, v_BLACK_MODE(0)); LcdWrReg(inf, REG_CFG_DONE, 0x01); + rk29fb_notify(inf, enable ? RK29FB_EVENT_HDMI_ON : RK29FB_EVENT_HDMI_OFF); return 0; } @@ -3204,6 +3223,7 @@ static int __devinit rk29fb_probe (struct platform_device *pdev) #else set_lcd_info(&inf->panel1_info, mach_info->lcd_info); #endif + inf->cur_screen = &inf->panel1_info; screen = inf->cur_screen; if(SCREEN_NULL==screen->type) @@ -3486,7 +3506,9 @@ static int __devinit rk29fb_probe (struct platform_device *pdev) goto release_irq; } } - + #ifdef CONFIG_MFD_RK610 + rk610_lcd_scaler_set_param(&inf->panel1_info,0); + #endif #if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO) fb0_set_par(inf->fb0); if (fb_prepare_logo(inf->fb0, FB_ROTATE_UR)) { diff --git a/include/linux/display-sys.h b/include/linux/display-sys.h new file mode 100644 index 000000000000..6eb0080398e0 --- /dev/null +++ b/include/linux/display-sys.h @@ -0,0 +1,69 @@ +#ifndef _LINUX_DISPLAY_RK_H +#define _LINUX_DISPLAY_RK_H + +#include +#include +#include + +struct rk_display_device; + +enum rk_display_priority { + DISPLAY_PRIORITY_TV = 0, + DISPLAY_PRIORITY_YPbPr, + DISPLAY_PRIORITY_VGA, + DISPLAY_PRIORITY_HDMI, + DISPLAY_PRIORITY_LCD, +}; + +/* This structure defines all the properties of a Display. */ +struct rk_display_driver { + void (*suspend)(struct rk_display_device *, pm_message_t state); + void (*resume)(struct rk_display_device *); + int (*probe)(struct rk_display_device *, void *); + int (*remove)(struct rk_display_device *); +}; + +struct rk_display_ops { + int (*setenable)(struct rk_display_device *, int enable); + int (*getenable)(struct rk_display_device *); + int (*getstatus)(struct rk_display_device *); + int (*getmodelist)(struct rk_display_device *, struct list_head **modelist); + int (*setmode)(struct rk_display_device *, struct fb_videomode *mode); + int (*getmode)(struct rk_display_device *, struct fb_videomode *mode); +}; + +struct rk_display_device { + struct module *owner; /* Owner module */ + struct rk_display_driver *driver; + struct device *parent; /* This is the parent */ + struct device *dev; /* This is this display device */ + struct mutex lock; + void *priv_data; + char type[16]; + char *name; + int idx; + struct rk_display_ops *ops; + int priority; + struct list_head list; +}; + +struct rk_display_devicelist { + struct list_head list; + struct rk_display_device *dev; +}; + +extern struct rk_display_device *rk_display_device_register(struct rk_display_driver *driver, + struct device *dev, void *devdata); +extern void rk_display_device_unregister(struct rk_display_device *dev); + +extern void rk_display_device_enable(struct rk_display_device *ddev); + +extern void rk_display_device_enable_other(struct rk_display_device *ddev); +extern void rk_display_device_disable_other(struct rk_display_device *ddev); + + +extern void rk_display_device_select(int priority); + +#define to_rk_display_device(obj) container_of(obj, struct rk_display_device, class_dev) + +#endif diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h index 551d2694f0fa..27958ba9c6b0 100755 --- a/include/linux/hdmi.h +++ b/include/linux/hdmi.h @@ -62,7 +62,7 @@ typedef int BOOL; #define HDMI_720x480p_60Hz_16x9 7 /* HDMI default resolution */ -#define HDMI_DEFAULT_RESOLUTION HDMI_1920x1080p_50Hz +#define HDMI_DEFAULT_RESOLUTION HDMI_1920x1080p_50Hz /* I2S Fs */ #define HDMI_I2S_Fs_44100 0 #define HDMI_I2S_Fs_48000 2 diff --git a/include/linux/mfd/rk610_core.h b/include/linux/mfd/rk610_core.h new file mode 100644 index 000000000000..bc1c50361233 --- /dev/null +++ b/include/linux/mfd/rk610_core.h @@ -0,0 +1,142 @@ +#ifndef __RK610_CONTROL_H_ +#define __RK610_CONTROL_H_ + +#define INVALID_GPIO -1 +#define RK610_DEBUG 1 + +#if RK610_DEBUG +#define RK610_DBG(dev, format, arg...) \ +do{\ + dev_printk(KERN_INFO , dev , format , ## arg);\ +}while(0) +#else +#define RK610_DBG(dev, format, arg...) +#endif +#define RK610_ERR(dev, format, arg...) \ +do{\ + dev_printk(KERN_ERR , dev , format , ## arg);\ +}while(0) + +#define RK610_CONTROL_REG_C_PLL_CON0 0x00 +#define RK610_CONTROL_REG_C_PLL_CON1 0x01 +#define RK610_CONTROL_REG_C_PLL_CON2 0x02 +#define RK610_CONTROL_REG_C_PLL_CON3 0x03 +#define RK610_CONTROL_REG_C_PLL_CON4 0x04 +#define RK610_CONTROL_REG_C_PLL_CON5 0x05 + #define C_PLL_DISABLE_FRAC 1 << 0 + #define C_PLL_BYPSS_ENABLE 1 << 1 + #define C_PLL_POWER_ON 1 << 2 + #define C_PLL_LOCLED 1 << 7 + +#define RK610_CONTROL_REG_TVE_CON 0x29 + #define TVE_CONTROL_VDAC_R_BYPASS_ENABLE 1 << 7 + #define TVE_CONTROL_VDAC_R_BYPASS_DISABLE 0 << 7 + #define TVE_CONTROL_CVBS_3_CHANNEL_ENALBE 1 << 6 + #define TVE_CONTROL_CVBS_3_CHANNEL_DISALBE 0 << 5 +enum { + INPUT_DATA_FORMAT_RGB888 = 0, + INPUT_DATA_FORMAT_RGB666, + INPUT_DATA_FORMAT_RGB565, + INPUT_DATA_FORMAT_YUV +}; + #define RGB2CCIR_INPUT_DATA_FORMAT(n) n << 4 + + #define RGB2CCIR_RGB_SWAP_ENABLE 1 << 3 + #define RGB2CCIR_RGB_SWAP_DISABLE 0 << 3 + + #define RGB2CCIR_INPUT_INTERLACE 1 << 2 + #define RGB2CCIR_INPUT_PROGRESSIVE 0 << 2 + + #define RGB2CCIR_CVBS_PAL 0 << 1 + #define RGB2CCIR_CVBS_NTSC 1 << 1 + + #define RGB2CCIR_DISABLE 0 + #define RGB2CCIR_ENABLE 1 + +#define RK610_CONTROL_REG_CCIR_RESET 0x2a + +#define RK610_CONTROL_REG_CLOCK_CON0 0x2b +#define RK610_CONTROL_REG_CLOCK_CON1 0x2c + #define CLOCK_CON1_I2S_CLK_CODEC_PLL 1 << 5 + #define CLOCK_CON1_I2S_DVIDER_MASK 0x1F +#define RK610_CONTROL_REG_CODEC_CON 0x2d + #define CODEC_CON_BIT_HDMI_BLCK_INTERANL 1<<4 + #define CODEC_CON_BIT_DAC_LRCL_OUTPUT_DISABLE 1<<3 + #define CODEC_CON_BIT_ADC_LRCK_OUTPUT_DISABLE 1<<2 + #define CODEC_CON_BIT_INTERAL_CODEC_DISABLE 1<<0 + +#define RK610_CONTROL_REG_I2C_CON 0x2e + +/******************************************************************** +** ½á¹¹¶¨Òå * +********************************************************************/ +/* RK610µÄ¼Ä´æÆ÷½á¹¹ */ +/* CODEC PLL REG */ +#define C_PLL_CON0 0x00 +#define C_PLL_CON1 0x01 +#define C_PLL_CON2 0x02 +#define C_PLL_CON3 0x03 +#define C_PLL_CON4 0x04 +#define C_PLL_CON5 0x05 + +/* SCALER PLL REG */ +#define S_PLL_CON0 0x06 +#define S_PLL_CON1 0x07 +#define S_PLL_CON2 0x08 + +/* LVDS REG */ +#define LVDS_CON0 0x09 +#define LVDS_CON1 0x0a + +/* LCD1 REG */ +#define LCD1_CON 0x0b + +/* SCALER REG */ +#define SCL_CON0 0x0c +#define SCL_CON1 0x0d +#define SCL_CON2 0x0e +#define SCL_CON3 0x0f +#define SCL_CON4 0x10 +#define SCL_CON5 0x11 +#define SCL_CON6 0x12 +#define SCL_CON7 0x13 +#define SCL_CON8 0x14 +#define SCL_CON9 0x15 +#define SCL_CON10 0x16 +#define SCL_CON11 0x17 +#define SCL_CON12 0x18 +#define SCL_CON13 0x19 +#define SCL_CON14 0x1a +#define SCL_CON15 0x1b +#define SCL_CON16 0x1c +#define SCL_CON17 0x1d +#define SCL_CON18 0x1e +#define SCL_CON19 0x1f +#define SCL_CON20 0x20 +#define SCL_CON21 0x21 +#define SCL_CON22 0x22 +#define SCL_CON23 0x23 +#define SCL_CON24 0x24 +#define SCL_CON25 0x25 +#define SCL_CON26 0x26 +#define SCL_CON27 0x27 +#define SCL_CON28 0x28 + +/* TVE REG */ +#define TVE_CON 0x29 + +/* CCIR REG */ +#define CCIR_RESET 0X2a + +/* CLOCK REG */ +#define CLOCK_CON0 0X2b +#define CLOCK_CON1 0X2c + +/* CODEC REG */ +#define CODEC_CON 0x2e +#define I2C_CON 0x2f + + +extern int rk610_control_send_byte(const char reg, const char data); + +#endif /*end of __RK610_CONTROL_H_*/ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 012792738795..594a1662839f 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -78,6 +78,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_RT5621 if I2C select SND_SOC_RT5631 if I2C select SND_SOC_RT5625 if I2C + select SND_SOC_RK610 if I2C select SND_SOC_WM8903 if I2C select SND_SOC_WM8904 if I2C select SND_SOC_WM8915 if I2C @@ -395,6 +396,10 @@ config SND_SOC_RK1000 tristate # depends on RK1000_CONTROL +config SND_SOC_RK610 + tristate + depends on MFD_RK610 + # Amp config SND_SOC_LM4857 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index e9d88b2d3ba5..3720639b5912 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -87,6 +87,7 @@ snd-soc-wm9713-objs := wm9713.o snd-soc-wm-hubs-objs := wm_hubs.o snd-soc-rk1000-objs := rk1000_codec.o snd-soc-jz4740-codec-objs := jz4740.o +snd-soc-rk610-objs := rk610_codec.o # Amp snd-soc-lm4857-objs := lm4857.o @@ -184,7 +185,7 @@ obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o obj-$(CONFIG_SND_SOC_RK1000) += snd-soc-rk1000.o - +obj-$(CONFIG_SND_SOC_RK610) += snd-soc-rk610.o # Amp obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o diff --git a/sound/soc/codecs/rk610_codec.c b/sound/soc/codecs/rk610_codec.c new file mode 100644 index 000000000000..380309b31080 --- /dev/null +++ b/sound/soc/codecs/rk610_codec.c @@ -0,0 +1,1137 @@ +/* + * rk610.c -- RK610 ALSA SoC audio driver + * + * Copyright (C) 2009 rockchip lhh + * + * + * Based on RK610.c + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rk610_codec.h" + +#define RK610_SPK_CTRL_PIN RK29_PIN6_PB6 + + +/* + * Debug + */ +#if 0 +#define DBG(x...) printk(KERN_ERR x) +#else +#define DBG(x...) +#endif + +#define err(format, arg...) \ + printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg) +#define info(format, arg...) \ + printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg) + +#define OUT_CAPLESS (0) //ÊÇ·ñΪÎÞµçÈÝÊä³ö£¬1:ÎÞµçÈÝÊä³ö£¬0:ÓеçÈÝÊä³ö + +static u32 gVolReg = 0x00; ///0x0f; //ÓÃÓڼǼÒôÁ¿¼Ä´æÆ÷ +//static u32 gCodecVol = 0x0f; +static u8 gR0AReg = 0; //ÓÃÓڼǼR0A¼Ä´æÆ÷µÄÖµ£¬ÓÃÓڸıä²ÉÑùÂÊǰͨ¹ýR0AÍ£Ö¹clk +static u8 gR0BReg = 0; //ÓÃÓڼǼR0B¼Ä´æÆ÷µÄÖµ£¬ÓÃÓڸıä²ÉÑùÂÊǰͨ¹ýR0BÍ£Ö¹interplateºÍdecimation +//static u8 gR1314Reg = 0; //ÓÃÓڼǼR13,R14¼Ä´æÆ÷µÄÖµ£¬ÓÃÓÚFMÒôÁ¿Îª0ʱ + +/* + * rk610 register cache + * We can't read the RK610 register space when we + * are using 2 wire for device control, so we cache them instead. + */ +static const u16 rk610_codec_reg[] = { + 0x0005, 0x0004, 0x00fd, 0x00f3, /* 0 */ + 0x0003, 0x0000, 0x0000, 0x0000, /* 4 */ + 0x0000, 0x0005, 0x0000, 0x0000, /* 8 */ + 0x0097, 0x0097, 0x0097, 0x0097, /* 12 */ + 0x0097, 0x0097, 0x00cc, 0x0000, /* 16 */ + 0x0000, 0x00f1, 0x0090, 0x00ff, /* 20 */ + 0x00ff, 0x00ff, 0x009c, 0x0000, /* 24 */ + 0x0000, 0x00ff, 0x00ff, 0x00ff, /* 28 */ +}; + +static struct snd_soc_codec *rk610_codec_codec; +/* codec private data */ +struct rk610_codec_priv { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) + enum snd_soc_control_type control_type; +#endif + unsigned int sysclk; + struct snd_soc_codec codec; + struct snd_pcm_hw_constraint_list *sysclk_constraints; + u16 reg_cache[RK610_CODEC_NUM_REG]; +}; + +extern int rk610_control_init_codec(void); +extern int rk610_codec_pll_set(unsigned int rate); + +/* + * read rk610 register cache + */ +static inline unsigned int rk610_codec_read_reg_cache(struct snd_soc_codec *codec, + unsigned int reg) +{ + u16 *cache = codec->reg_cache; + if (reg > RK610_CACHE_REGNUM) + return -1; + return cache[reg]; +} + +static unsigned int rk610_codec_read(struct snd_soc_codec *codec, unsigned int r) +{ + struct i2c_msg xfer[1]; + u8 reg = r; + int ret; + struct i2c_client *client = codec->control_data; + + /* Read register */ + xfer[0].addr = (client->addr& 0x60)|(reg); + xfer[0].flags = I2C_M_RD; + xfer[0].len = 1; + xfer[0].buf = ® + xfer[0].scl_rate = 100000; + ret = i2c_transfer(client->adapter, xfer, 1); + if (ret != 1) { + dev_err(&client->dev, "i2c_transfer() returned %d\n", ret); + return 0; + } + + return reg; +} +/* + * write rk610 register cache + */ +static inline void rk610_codec_write_reg_cache(struct snd_soc_codec *codec, + unsigned int reg, unsigned int value) +{ + u16 *cache = codec->reg_cache; + if (reg > RK610_CACHE_REGNUM) + return; + cache[reg] = value; +} + +static int rk610_codec_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u8 data[2]; + struct i2c_client *i2c; + DBG("Enter::%s, %d, reg=0x%02X, value=0x%02X\n",__FUNCTION__,__LINE__, reg, value); + data[0] = value & 0x00ff; + rk610_codec_write_reg_cache (codec, reg, value); + i2c = (struct i2c_client *)codec->control_data; + i2c->addr = (i2c->addr & 0x60)|reg; + + if (codec->hw_write(codec->control_data, data, 1) == 1){ +// DBG("================%s %d Run OK================\n",__FUNCTION__,__LINE__); + return 0; + }else{ + DBG("================%s %d Run EIO================\n",__FUNCTION__,__LINE__); + return -EIO; + } +} + +void rk610_codec_reg_read(void) +{ + struct snd_soc_codec *codec = rk610_codec_codec; + int i; + unsigned int data; + + for (i=0; i<=0x1f; i++){ + data = rk610_codec_read(codec, i); + DBG("reg[0x%x]=0x%x\n",i,data); + } +} + +static const struct snd_kcontrol_new rk610_codec_snd_controls[] = { +SOC_DOUBLE_R("Capture Volume", ACCELCODEC_R0C, ACCELCODEC_R0D, 0, 15, 0), +SOC_DOUBLE_R("Capture Switch", ACCELCODEC_R0C, ACCELCODEC_R0D, 7, 1, 1), +SOC_DOUBLE_R("PCM Volume", ACCELCODEC_R0D, ACCELCODEC_R0E, 0, 7, 0), +//SOC_SINGLE("Left ADC Capture Volume", ACCELCODEC_R17, 0, 63, 0), +//SOC_SINGLE("Right ADC Capture Volume", ACCELCODEC_R18, 0, 63, 0), +}; + +/* Left Mixer */ +static const struct snd_kcontrol_new rk610_codec_left_mixer_controls[] = { +SOC_DAPM_SINGLE("Left Playback Switch", ACCELCODEC_R15, 6, 1, 0), +SOC_DAPM_SINGLE("Left Bypass Switch", ACCELCODEC_R15, 2, 1, 0), +}; + +/* Right Mixer */ +static const struct snd_kcontrol_new rk610_codec_right_mixer_controls[] = { +SOC_DAPM_SINGLE("Right Playback Switch", ACCELCODEC_R15, 7, 1, 0), +SOC_DAPM_SINGLE("Right Bypass Switch", ACCELCODEC_R15, 3, 1, 0), +}; + +static const struct snd_soc_dapm_widget rk610_codec_dapm_widgets[] = { + SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0, + &rk610_codec_left_mixer_controls[0], + ARRAY_SIZE(rk610_codec_left_mixer_controls)), + SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0, + &rk610_codec_right_mixer_controls[0], + ARRAY_SIZE(rk610_codec_right_mixer_controls)), + + //SND_SOC_DAPM_PGA("Right Out 1", ACCELCODEC_R1E, 0, 0, NULL, 0), + //SND_SOC_DAPM_PGA("Left Out 1", ACCELCODEC_R1E, 1, 0, NULL, 0), + //SND_SOC_DAPM_DAC("Right DAC", "Right Playback", ACCELCODEC_R1F, 1, 0), + //SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ACCELCODEC_R1F, 2, 0), + + SND_SOC_DAPM_ADC("ADC", "Capture", ACCELCODEC_R1D, 6, 1), + SND_SOC_DAPM_ADC("ADC BUFF", "Capture BUFF", ACCELCODEC_R1D, 2, 0), + + + SND_SOC_DAPM_OUTPUT("LOUT1"), + SND_SOC_DAPM_OUTPUT("ROUT1"), + + SND_SOC_DAPM_INPUT("LINPUT1"), + SND_SOC_DAPM_INPUT("RINPUT1"), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /* left mixer */ + {"Left Mixer", "Left Playback Switch", "Left DAC"}, + {"Left Mixer", "Left Bypass Switch", "Left Line Mux"}, + {"Right Mixer", "Right Playback Switch", "Right DAC"}, + {"Right Mixer", "Right Bypass Switch", "Right Line Mux"}, + + /* left out 1 */ + {"Left Out 1", NULL, "Left Mixer"}, + {"LOUT1", NULL, "Left Out 1"}, + + + /* right out 1 */ + {"Right Out 1", NULL, "Right Mixer"}, + {"ROUT1", NULL, "Right Out 1"}, + + /* Left Line Mux */ + {"Left Line Mux", "Line 1", "LINPUT1"}, + {"Left Line Mux", "PGA", "Left PGA Mux"}, + {"Left Line Mux", "Differential", "Differential Mux"}, + + /* Right Line Mux */ + {"Right Line Mux", "Line 1", "RINPUT1"}, + {"Right Line Mux", "PGA", "Right PGA Mux"}, + {"Right Line Mux", "Differential", "Differential Mux"}, + + /* Left PGA Mux */ + {"Left PGA Mux", "Line 1", "LINPUT1"}, + {"Left PGA Mux", "Line 2", "LINPUT2"}, + {"Left PGA Mux", "Line 3", "LINPUT3"}, + {"Left PGA Mux", "Differential", "Differential Mux"}, + + /* Right PGA Mux */ + {"Right PGA Mux", "Line 1", "RINPUT1"}, + {"Right PGA Mux", "Differential", "Differential Mux"}, + + /* Differential Mux */ + {"Differential Mux", "Line 1", "LINPUT1"}, + {"Differential Mux", "Line 1", "RINPUT1"}, + + /* Left ADC Mux */ + {"Left ADC Mux", "Stereo", "Left PGA Mux"}, + {"Left ADC Mux", "Mono (Left)", "Left PGA Mux"}, + {"Left ADC Mux", "Digital Mono", "Left PGA Mux"}, + + /* Right ADC Mux */ + {"Right ADC Mux", "Stereo", "Right PGA Mux"}, + {"Right ADC Mux", "Mono (Right)", "Right PGA Mux"}, + {"Right ADC Mux", "Digital Mono", "Right PGA Mux"}, + + /* ADC */ + {"Left ADC", NULL, "Left ADC Mux"}, + {"Right ADC", NULL, "Right ADC Mux"}, + + /* terminator */ +// {NULL, NULL, NULL}, +}; + +struct _coeff_div { + u32 mclk; + u32 rate; + u16 fs; + u8 sr:5; + u8 usb:1; + u8 bclk; +}; + +/* codec hifi mclk clock divider coefficients */ +static const struct _coeff_div coeff_div[] = { + /* 8k */ + {12288000, 8000, 1536, 0x6, 0x0,ASC_BCLKDIV_16}, + {11289600, 8000, 1408, 0x16, 0x0,ASC_BCLKDIV_16}, + {18432000, 8000, 2304, 0x7, 0x0,ASC_BCLKDIV_16}, + {16934400, 8000, 2112, 0x17, 0x0,ASC_BCLKDIV_16}, + {8192000, 8000, 1024, 0x0, 0x0,ASC_BCLKDIV_16}, + {12000000, 8000, 1500, 0x6, 0x1,ASC_BCLKDIV_16}, + + /* 11.025k */ + {11289600, 11025, 1024, 0x18, 0x0,ASC_BCLKDIV_16}, + {16934400, 11025, 1536, 0x19, 0x0,ASC_BCLKDIV_16}, + {12000000, 11025, 1088, 0x19, 0x1,ASC_BCLKDIV_16}, + + /* 12k */ + {12288000, 12000, 1024, 0x8, 0x0,ASC_BCLKDIV_16}, + {18432000, 12000, 1536, 0x9, 0x0,ASC_BCLKDIV_16}, + {12000000, 12000, 1000, 0x8, 0x1,ASC_BCLKDIV_16}, + + /* 16k */ + {12288000, 16000, 768, 0xa, 0x0,ASC_BCLKDIV_8}, + {18432000, 16000, 1152, 0xb, 0x0,ASC_BCLKDIV_8}, + {12000000, 16000, 750, 0xa, 0x1,ASC_BCLKDIV_8}, + + /* 22.05k */ + {11289600, 22050, 512, 0x1a, 0x0,ASC_BCLKDIV_8}, + {16934400, 22050, 768, 0x1b, 0x0,ASC_BCLKDIV_8}, + {12000000, 22050, 544, 0x1b, 0x1,ASC_BCLKDIV_8}, + + /* 24k */ + {12288000, 24000, 512, 0x1c, 0x0,ASC_BCLKDIV_8}, + {18432000, 24000, 768, 0x1d, 0x0,ASC_BCLKDIV_8}, + {12000000, 24000, 500, 0x1c, 0x1,ASC_BCLKDIV_8}, + + /* 32k */ + {12288000, 32000, 384, 0xc, 0x0,ASC_BCLKDIV_8}, + {18432000, 32000, 576, 0xd, 0x0,ASC_BCLKDIV_8}, + {12000000, 32000, 375, 0xa, 0x1,ASC_BCLKDIV_8}, + + /* 44.1k */ + {11289600, 44100, 256, 0x10, 0x0,ASC_BCLKDIV_4}, + {16934400, 44100, 384, 0x11, 0x0,ASC_BCLKDIV_8}, + {12000000, 44100, 272, 0x11, 0x1,ASC_BCLKDIV_8}, + + /* 48k */ + {12288000, 48000, 256, 0x0, 0x0,ASC_BCLKDIV_4}, + {18432000, 48000, 384, 0x1, 0x0,ASC_BCLKDIV_4}, + {12000000, 48000, 250, 0x0, 0x1,ASC_BCLKDIV_4}, + + /* 88.2k */ + {11289600, 88200, 128, 0x1e, 0x0,ASC_BCLKDIV_4}, + {16934400, 88200, 192, 0x1f, 0x0,ASC_BCLKDIV_4}, + {12000000, 88200, 136, 0x1f, 0x1,ASC_BCLKDIV_4}, + + /* 96k */ + {12288000, 96000, 128, 0xe, 0x0,ASC_BCLKDIV_4}, + {18432000, 96000, 192, 0xf, 0x0,ASC_BCLKDIV_4}, + {12000000, 96000, 125, 0xe, 0x1,ASC_BCLKDIV_4}, +}; + +static inline int get_coeff(int mclk, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { + if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) + return i; + } + + return -EINVAL; +} + +/* The set of rates we can generate from the above for each SYSCLK */ + +static unsigned int rates_12288[] = { + 8000, 12000, 16000, 24000, 24000, 32000, 48000, 96000, +}; + +static struct snd_pcm_hw_constraint_list constraints_12288 = { + .count = ARRAY_SIZE(rates_12288), + .list = rates_12288, +}; + +static unsigned int rates_112896[] = { + 8000, 11025, 22050, 44100, +}; + +static struct snd_pcm_hw_constraint_list constraints_112896 = { + .count = ARRAY_SIZE(rates_112896), + .list = rates_112896, +}; + +static unsigned int rates_12[] = { + 8000, 11025, 12000, 16000, 22050, 2400, 32000, 41100, 48000, + 48000, 88235, 96000, +}; + +static struct snd_pcm_hw_constraint_list constraints_12 = { + .count = ARRAY_SIZE(rates_12), + .list = rates_12, +}; + +/* + * Note that this should be called from init rather than from hw_params. + */ +static int rk610_codec_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) + struct rk610_codec_priv *rk610_codec =snd_soc_codec_get_drvdata(codec); +#else + struct rk610_codec_priv *rk610_codec = codec->private_data; +#endif + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + +// if(rk610_codec_pll_set(freq)) +// return -EINVAL; + + switch (freq) { + case 11289600: + case 18432000: + case 22579200: + case 36864000: + rk610_codec->sysclk_constraints = &constraints_112896; + rk610_codec->sysclk = freq; + break; + + case 12288000: + case 16934400: + case 24576000: + case 33868800: + rk610_codec->sysclk_constraints = &constraints_12288; + rk610_codec->sysclk = freq; + break; + + case 12000000: + case 24000000: + rk610_codec->sysclk_constraints = &constraints_12; + rk610_codec->sysclk = freq; + break; + default: + return -EINVAL; + } + return 0; +} + +static int rk610_codec_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 iface = 0; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + iface = 0x0040; + break; + case SND_SOC_DAIFMT_CBS_CFS: + iface = 0x0000; + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface |= 0x0002; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + iface |= 0x0001; + break; + case SND_SOC_DAIFMT_DSP_A: + iface |= 0x0003; + break; + case SND_SOC_DAIFMT_DSP_B: + iface |= 0x0013; + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + iface |= 0x0090; + break; + case SND_SOC_DAIFMT_IB_NF: + iface |= 0x0080; + break; + case SND_SOC_DAIFMT_NB_IF: + iface |= 0x0010; + break; + default: + return -EINVAL; + } + + DBG("Enter::%s----%d iface=%x\n",__FUNCTION__,__LINE__,iface); + rk610_codec_write(codec, ACCELCODEC_R09, iface); + return 0; +} + +static int rk610_codec_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) + struct snd_soc_codec *codec = rtd->codec; + struct rk610_codec_priv *rk610_codec =snd_soc_codec_get_drvdata(codec); +#else + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->card->codec; + struct rk610_codec_priv *rk610_codec = codec->private_data; +#endif + u16 iface = rk610_codec_read_reg_cache(codec, ACCELCODEC_R09) & 0x1f3; + u16 srate = rk610_codec_read_reg_cache(codec, ACCELCODEC_R00) & 0x180; + int coeff; + + /*by Vincent Hsiung for EQ Vol Change*/ + #define HW_PARAMS_FLAG_EQVOL_ON 0x21 + #define HW_PARAMS_FLAG_EQVOL_OFF 0x22 + if (params->flags == HW_PARAMS_FLAG_EQVOL_ON) + { + u16 r17 = rk610_codec_read_reg_cache(codec, ACCELCODEC_R17); + u16 r18 = rk610_codec_read_reg_cache(codec, ACCELCODEC_R18); + + r17 &= (~0x3f); //6db + r18 &= (~0x3f); //6db + + rk610_codec_write(codec, ACCELCODEC_R17, r17); + rk610_codec_write(codec, ACCELCODEC_R18, r18); + + return 0; + } + else if (params->flags == HW_PARAMS_FLAG_EQVOL_OFF) + { + u16 r17 = rk610_codec_read_reg_cache(codec, ACCELCODEC_R17); + u16 r18 = rk610_codec_read_reg_cache(codec, ACCELCODEC_R18); + + r17 &= (~0x3f); + r17 |= 0x0f; //0db + + r18 &= (~0x3f); + r18 |= 0x0f; //0db + + rk610_codec_write(codec, ACCELCODEC_R17, r17); + rk610_codec_write(codec, ACCELCODEC_R18, r18); + return 0; + } + + coeff = get_coeff(rk610_codec->sysclk, params_rate(params)); + DBG("Enter::%s----%d rk610_codec->sysclk=%d coeff = %d\n",__FUNCTION__,__LINE__,rk610_codec->sysclk, coeff); + /* bit size */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + iface |= 0x0004; + break; + case SNDRV_PCM_FORMAT_S24_LE: + iface |= 0x0008; + break; + case SNDRV_PCM_FORMAT_S32_LE: + iface |= 0x000c; + break; + } + DBG("Enter::%s----%d iface=%x srate =%x rate=%d\n",__FUNCTION__,__LINE__,iface,srate,params_rate(params)); + +// rk610_codec_write(codec,ACCELCODEC_R0C, 0x17); + rk610_codec_write(codec,ACCELCODEC_R04, ASC_INT_MUTE_L|ASC_INT_MUTE_R|ASC_SIDETONE_L_OFF|ASC_SIDETONE_R_OFF); //soft mute + //±ØÐëÏȽ«clkºÍEN_INT¶¼disableµô£¬·ñÔòÇл»bclk·ÖƵֵ¿ÉÄܵ¼ÖÂcodecÄÚ²¿Ê±Ðò»ìÂÒµô£¬ + //±íÏÖ³öÀ´µÄÏÖÏóÊÇ£¬ÒÔºóµÄÒôÀÖ¶¼±ä³ÉÁËÔëÒô£¬¶øÇÒ¾ÍËã°ÑÊäÈëcodecµÄI2S_DATAOUT¶Ï¿ªÒ²Ò»Ñù³öÔëÒô + rk610_codec_write(codec,ACCELCODEC_R0B, ASC_DEC_DISABLE|ASC_INT_DISABLE); //0x00 + + /* set iface & srate */ + #ifdef CONFIG_SND_RK29_CODEC_SOC_MASTER + iface |= ASC_INVERT_BCLK;//·­×ªBCLK master״̬ËͳöµÄÉÙÁË°ë¸öʱÖÓ£¬µ¼ÖÂδµ½×î´óÒôÁ¿µÄʱºòÆÆÒô¡¢ + #endif + rk610_codec_write(codec, ACCELCODEC_R09, iface); + if (coeff >= 0){ + rk610_codec_write(codec, ACCELCODEC_R00, srate|coeff_div[coeff].bclk); + rk610_codec_write(codec, ACCELCODEC_R0A, (coeff_div[coeff].sr << 1) | coeff_div[coeff].usb|ASC_CLKNODIV|ASC_CLK_ENABLE); + } + rk610_codec_write(codec,ACCELCODEC_R0B, gR0BReg); + + return 0; +} + +void PhaseOut(struct snd_soc_codec *codec,u32 nStep, u32 us) +{ + DBG("%s[%d]\n",__FUNCTION__,__LINE__); + rk610_codec_write(codec,ACCELCODEC_R17, gVolReg|ASC_OUTPUT_ACTIVE|ASC_CROSSZERO_EN); //AOL + rk610_codec_write(codec,ACCELCODEC_R18, gVolReg|ASC_OUTPUT_ACTIVE|ASC_CROSSZERO_EN); //AOR + udelay(us); +} + +void PhaseIn(struct snd_soc_codec *codec,u32 nStep, u32 us) +{ + DBG("%s[%d]\n",__FUNCTION__,__LINE__); + rk610_codec_write(codec,ACCELCODEC_R17, gVolReg|ASC_OUTPUT_ACTIVE|ASC_CROSSZERO_EN); //AOL gVolReg|ASC_OUTPUT_ACTIVE|ASC_CROSSZERO_EN); //AOL + rk610_codec_write(codec,ACCELCODEC_R18, gVolReg|ASC_OUTPUT_ACTIVE|ASC_CROSSZERO_EN); //gVolReg|ASC_OUTPUT_ACTIVE|ASC_CROSSZERO_EN); //AOR + udelay(us); +} + +static int rk610_codec_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + + DBG("Enter::%s----%d--mute=%d\n",__FUNCTION__,__LINE__,mute); + + if (mute) + { + PhaseOut(codec,1, 5000); + rk610_codec_write(codec,ACCELCODEC_R19, 0xFF); //AOM + rk610_codec_write(codec,ACCELCODEC_R04, ASC_INT_MUTE_L|ASC_INT_MUTE_R|ASC_SIDETONE_L_OFF|ASC_SIDETONE_R_OFF); //soft mute + //add for standby + if(!dai->capture_active) + { + rk610_codec_write(codec, ACCELCODEC_R1D, 0xFE); + rk610_codec_write(codec, ACCELCODEC_R1E, 0xFF); + rk610_codec_write(codec, ACCELCODEC_R1F, 0xFF); + } + } + else + { + rk610_codec_write(codec,ACCELCODEC_R1D, 0x2a); //setup Vmid and Vref, other module power down + rk610_codec_write(codec,ACCELCODEC_R1E, 0x40); ///|ASC_PDASDML_ENABLE); + + #if OUT_CAPLESS + rk610_codec_write(codec,ACCELCODEC_R1F, 0x09|ASC_PDMIXM_ENABLE); + #else + rk610_codec_write(codec,ACCELCODEC_R1F, 0x09|ASC_PDMIXM_ENABLE|ASC_PDPAM_ENABLE); + #endif + + PhaseIn(codec,1, 5000); + //if(gCodecVol != 0) + //{ + rk610_codec_write(codec,ACCELCODEC_R04, ASC_INT_ACTIVE_L|ASC_INT_ACTIVE_R|ASC_SIDETONE_L_OFF|ASC_SIDETONE_R_OFF); + //} + rk610_codec_write(codec,ACCELCODEC_R19, 0x7F); //AOM +#if 0 + /*disable speaker */ + gpio_set_value(RK29_PIN6_PB6, GPIO_LOW); +#endif + rk610_codec_reg_read(); + } + + return 0; +} + +static int rk610_codec_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + DBG("Enter::%s----%d level =%d\n",__FUNCTION__,__LINE__,level); + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + /* VREF, VMID=2x50k, digital enabled */ + // rk610_codec_write(codec, ACCELCODEC_R1D, pwr_reg | 0x0080); + break; + + case SND_SOC_BIAS_STANDBY: + printk("rk610 standby\n"); + rk610_codec_write(codec, ACCELCODEC_R1D, 0xFE); + rk610_codec_write(codec, ACCELCODEC_R1E, 0xFF); + rk610_codec_write(codec, ACCELCODEC_R1F, 0xFF); + break; + + case SND_SOC_BIAS_OFF: + printk("rk610 power off\n"); + rk610_codec_write(codec, ACCELCODEC_R1D, 0xFF); + rk610_codec_write(codec, ACCELCODEC_R1E, 0xFF); + rk610_codec_write(codec, ACCELCODEC_R1F, 0xFF); + break; + } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) + codec->dapm.bias_level = level; +#else + codec->bias_level = level; +#endif + return 0; +} + +#define RK610_CODEC_RATES SNDRV_PCM_RATE_8000_96000 + +#define RK610_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_ops rk610_codec_ops = { + .hw_params = rk610_codec_pcm_hw_params, + .set_fmt = rk610_codec_set_dai_fmt, + .set_sysclk = rk610_codec_set_dai_sysclk, + .digital_mute = rk610_codec_mute, +}; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) +static struct snd_soc_dai_driver rk610_codec_dai = +#else +struct snd_soc_dai rk610_codec_dai = +#endif +{ + .name = "rk610_codec_xx", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RK610_CODEC_RATES, + .formats = RK610_CODEC_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RK610_CODEC_RATES, + .formats = RK610_CODEC_FORMATS, + }, + .ops = &rk610_codec_ops, + .symmetric_rates = 1, +}; +EXPORT_SYMBOL_GPL(rk610_codec_dai); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) +static int rk610_codec_suspend(struct snd_soc_codec *codec, pm_message_t state) +#else +static int rk610_codec_suspend(struct platform_device *pdev, pm_message_t state) +#endif +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)) + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; +#endif + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + rk610_codec_set_bias_level(codec, SND_SOC_BIAS_OFF); + rk610_codec_reg_read(); + + return 0; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) +static int rk610_codec_resume(struct snd_soc_codec *codec) +#else +static int rk610_codec_resume(struct platform_device *pdev) +#endif +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)) + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->card->codec; +#endif + int i; + u8 data[2]; + struct i2c_client *i2c; + u16 *cache = codec->reg_cache; + + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + /* Sync reg_cache with the hardware */ + for (i = 0; i < RK610_CODEC_NUM_REG; i++) { + data[0] = cache[i] & 0x00ff; + i2c = (struct i2c_client *)codec->control_data; + i2c->addr = (i2c->addr & 0x60)|i; + codec->hw_write(codec->control_data, data, 1); + } + + rk610_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} + +#if 1 +#define USE_MIC_IN +#define USE_LPF 1 +void rk610_codec_reg_set(void) +{ + struct snd_soc_codec *codec = rk610_codec_codec; + unsigned int digital_gain; + + rk610_codec_write(codec,ACCELCODEC_R1D, 0xFF); + rk610_codec_write(codec,ACCELCODEC_R1E, 0xFF); + rk610_codec_write(codec,ACCELCODEC_R1F, 0xFF); + +#if USE_LPF + // Route R-LPF->R-Mixer, L-LPF->L-Mixer + rk610_codec_write(codec,ACCELCODEC_R15, 0xC1); +#else + // Route RDAC->R-Mixer, LDAC->L->Mixer + rk610_codec_write(codec,ACCELCODEC_R15, 0x0C); +#endif + // With Cap Output, VMID ramp up slow + rk610_codec_write(codec,ACCELCODEC_R1A, 0x14); + mdelay(10); + + rk610_codec_write(codec,ACCELCODEC_R0C, 0x10|ASC_INPUT_VOL_0DB); //LIL + rk610_codec_write(codec,ACCELCODEC_R0D, 0x10|ASC_INPUT_VOL_0DB); //LIR + rk610_codec_write(codec,ACCELCODEC_R0E, 0x10|ASC_INPUT_VOL_0DB); //MIC +#ifdef USE_MIC_IN + rk610_codec_write(codec,ACCELCODEC_R12, 0x4c|ASC_MIC_INPUT|ASC_MIC_BOOST_20DB); //Select MIC input + rk610_codec_write(codec,ACCELCODEC_R1C, ASC_DEM_ENABLE); //0x00); //use default value +#else + rk610_codec_write(codec,ACCELCODEC_R12, 0x4c); //Select Line input +#endif + // Diable route PGA->R/L Mixer, PGA gain 0db. + rk610_codec_write(codec,ACCELCODEC_R13, 0x05 | 0 << 3); + rk610_codec_write(codec,ACCELCODEC_R14, 0x05 | 0 << 3); + + //2soft mute + rk610_codec_write(codec,ACCELCODEC_R04, ASC_INT_MUTE_L|ASC_INT_MUTE_R|ASC_SIDETONE_L_OFF|ASC_SIDETONE_R_OFF); //soft mute + + //2set default SR and clk + rk610_codec_write(codec,ACCELCODEC_R0A, ASC_NORMAL_MODE|(0x10 << 1)|ASC_CLKNODIV|ASC_CLK_DISABLE); + gR0AReg = ASC_NORMAL_MODE|(0x10 << 1)|ASC_CLKNODIV|ASC_CLK_DISABLE; + //2Config audio interface + rk610_codec_write(codec,ACCELCODEC_R09, ASC_I2S_MODE|ASC_16BIT_MODE|ASC_NORMAL_LRCLK|ASC_LRSWAP_DISABLE|ASC_MASTER_MODE|ASC_NORMAL_BCLK); + rk610_codec_write(codec,ACCELCODEC_R00, ASC_HPF_ENABLE|ASC_DSM_MODE_DISABLE|ASC_SCRAMBLE_DISABLE|ASC_DITHER_ENABLE|ASC_BCLKDIV_4); + //2volume,input,output + digital_gain = 0xE42; + rk610_codec_write(codec,ACCELCODEC_R05, (digital_gain >> 8) & 0xFF); + rk610_codec_write(codec,ACCELCODEC_R06, digital_gain & 0xFF); + rk610_codec_write(codec,ACCELCODEC_R07, (digital_gain >> 8) & 0xFF); + rk610_codec_write(codec,ACCELCODEC_R08, digital_gain & 0xFF); +// rk610_codec_write(codec,ACCELCODEC_R05, 0x0e); +// rk610_codec_write(codec,ACCELCODEC_R06, 0x42); +// rk610_codec_write(codec,ACCELCODEC_R07, 0x0e); +// rk610_codec_write(codec,ACCELCODEC_R08, 0x42); + + rk610_codec_write(codec,ACCELCODEC_R0B, ASC_DEC_ENABLE|ASC_INT_ENABLE); + gR0BReg = ASC_DEC_ENABLE|ASC_INT_ENABLE; //ASC_DEC_DISABLE|ASC_INT_ENABLE; + + rk610_codec_write(codec,ACCELCODEC_R1D, 0x30); + rk610_codec_write(codec,ACCELCODEC_R1E, 0x40); + #if OUT_CAPLESS + rk610_codec_write(codec,ACCELCODEC_R1F, 0x09|ASC_PDMIXM_ENABLE); + #else + rk610_codec_write(codec,ACCELCODEC_R1F, 0x09|ASC_PDMIXM_ENABLE|ASC_PDPAM_ENABLE); + #endif +} +#else +void rk610_codec_reg_set(void) +{ + struct snd_soc_codec *codec = rk610_codec_codec; + int reg; + int i; + unsigned int data; + + rk610_codec_write(codec,ACCELCODEC_R1D, 0x00); + rk610_codec_write(codec,ACCELCODEC_R17, 0xFF); //AOL + rk610_codec_write(codec,ACCELCODEC_R18, 0xFF); //AOR + rk610_codec_write(codec,ACCELCODEC_R19, 0xFF); //AOM + + rk610_codec_write(codec,ACCELCODEC_R1F, 0xDF); + mdelay(10); + rk610_codec_write(codec,ACCELCODEC_R1F, 0x5F); + rk610_codec_write(codec,ACCELCODEC_R19, 0x7F); //AOM + rk610_codec_write(codec,ACCELCODEC_R15, 0xC1);//rk610_codec_write(codec,ACCELCODEC_R15, 0xCD);//by Vincent Hsiung + rk610_codec_write(codec,ACCELCODEC_R1A, 0x1C); + mdelay(100); + rk610_codec_write(codec,ACCELCODEC_R1F, 0x09); + rk610_codec_write(codec,ACCELCODEC_R1E, 0x00); + mdelay(10); + rk610_codec_write(codec,ACCELCODEC_R1A, 0x14); + rk610_codec_write(codec,ACCELCODEC_R1D, 0xFE); + rk610_codec_write(codec,ACCELCODEC_R17, 0xBF); //AOL + rk610_codec_write(codec,ACCELCODEC_R18, 0xBF); //AOR + rk610_codec_write(codec,ACCELCODEC_R19, 0x7F); //AOM + rk610_codec_write(codec,ACCELCODEC_R1F, 0xDF); + + //2soft mute + rk610_codec_write(codec,ACCELCODEC_R04, ASC_INT_MUTE_L|ASC_INT_MUTE_R|ASC_SIDETONE_L_OFF|ASC_SIDETONE_R_OFF); //soft mute + + //2set default SR and clk + rk610_codec_write(codec,ACCELCODEC_R0A, ASC_USB_MODE|FREQ48kHz|ASC_CLKNODIV|ASC_CLK_DISABLE); + gR0AReg = ASC_USB_MODE|FREQ48kHz|ASC_CLKNODIV|ASC_CLK_DISABLE; + //2Config audio interface + rk610_codec_write(codec,ACCELCODEC_R09, ASC_I2S_MODE|ASC_16BIT_MODE|ASC_NORMAL_LRCLK|ASC_LRSWAP_DISABLE|ASC_MASTER_MODE|ASC_NORMAL_BCLK); + rk610_codec_write(codec,ACCELCODEC_R00, ASC_HPF_ENABLE|ASC_DSM_MODE_DISABLE|ASC_SCRAMBLE_ENABLE|ASC_DITHER_ENABLE|ASC_BCLKDIV_8); //BCLK div 8 + //2volume,input,outpu + rk610_codec_write(codec,ACCELCODEC_R05, 0x0e); + rk610_codec_write(codec,ACCELCODEC_R06, 0x42); + rk610_codec_write(codec,ACCELCODEC_R07, 0x0e); + rk610_codec_write(codec,ACCELCODEC_R08, 0x42); + + rk610_codec_write(codec,ACCELCODEC_R0C, 0x10|ASC_INPUT_VOL_0DB|ASC_INPUT_MUTE); //LIL + rk610_codec_write(codec,ACCELCODEC_R0D, 0x10|ASC_INPUT_VOL_0DB); //LIR + rk610_codec_write(codec,ACCELCODEC_R0E, 0x10|ASC_INPUT_VOL_0DB); //MIC + rk610_codec_write(codec,ACCELCODEC_R12, 0x4c|ASC_MIC_INPUT|ASC_MIC_BOOST_20DB); //mic input and boost 20dB + rk610_codec_write(codec,ACCELCODEC_R13, ASC_LPGAMX_DISABLE|ASC_ALMX_DISABLE|((LINE_2_MIXER_GAIN & 0x7) << 4)|0x0); + rk610_codec_write(codec,ACCELCODEC_R14, ASC_RPGAMX_DISABLE|ASC_ARMX_DISABLE|((LINE_2_MIXER_GAIN & 0x7) << 4)|0x0); + gR1314Reg = ASC_RPGAMX_DISABLE|ASC_ARMX_DISABLE|((LINE_2_MIXER_GAIN & 0x7) << 4)|0x0; + + //2other + rk610_codec_write(codec,ACCELCODEC_R0B, ASC_DEC_DISABLE|ASC_INT_DISABLE); //0x00 + gR0BReg = ASC_DEC_DISABLE|ASC_INT_DISABLE; + rk610_codec_write(codec,ACCELCODEC_R15, \ + 0x01|ASC_RLPFMX_DISABLE|ASC_LLPFMX_DISABLE|ASC_LDAMX_DISABLE|ASC_RDAMX_DISABLE|ASC_LSCF_ACTIVE|ASC_RSCF_ACTIVE); //0x3c + rk610_codec_write(codec,ACCELCODEC_R1B, 0x32); + rk610_codec_write(codec,ACCELCODEC_R1C, ASC_DEM_ENABLE); ///0x00); //use default value + + ///dac mode + rk610_codec_write(codec,ACCELCODEC_R17, 0xBF); //AOL ÒôÁ¿×îµÍ + rk610_codec_write(codec,ACCELCODEC_R18, 0xBF); //AOR + + //2power down useless module + rk610_codec_write(codec,ACCELCODEC_R1D, 0x2a|ASC_PDSDL_ENABLE|ASC_PDBSTL_ENABLE|ASC_PDPGAL_ENABLE); //setup Vmid and Vref, other module power down + rk610_codec_write(codec,ACCELCODEC_R1E, 0x40|ASC_PDASDML_ENABLE); + #if OUT_CAPLESS + rk610_codec_write(codec,ACCELCODEC_R1F, 0x09|ASC_PDMICB_ENABLE|ASC_PDMIXM_ENABLE); + #else + rk610_codec_write(codec,ACCELCODEC_R1F, 0x09|ASC_PDMICB_ENABLE|ASC_PDMIXM_ENABLE|ASC_PDPAM_ENABLE); + #endif + + //2other + rk610_codec_write(codec,ACCELCODEC_R0B, ASC_DEC_DISABLE|ASC_INT_DISABLE); + gR0BReg = ASC_DEC_ENABLE|ASC_INT_ENABLE; //ASC_DEC_DISABLE|ASC_INT_ENABLE; + rk610_codec_write(codec,ACCELCODEC_R15, 0xC1);//rk610_codec_write(codec,ACCELCODEC_R15, 0xCD);//by Vincent Hsiung + rk610_codec_write(codec,ACCELCODEC_R0C, 0x10|ASC_INPUT_VOL_0DB); //LIL + rk610_codec_write(codec,ACCELCODEC_R0D, 0x10|ASC_INPUT_VOL_0DB); //LIR + rk610_codec_write(codec,ACCELCODEC_R0E, 0x10|ASC_INPUT_VOL_0DB); //MIC +// rk610_codec_write(codec,ACCELCODEC_R12, 0x4c|ASC_MIC_INPUT|ASC_MIC_BOOST_20DB); //mic input and boost 20dB + rk610_codec_write(codec,ACCELCODEC_R12, 0x4c); //line input and boost 20dB + rk610_codec_write(codec,ACCELCODEC_R13, 0x00); + rk610_codec_write(codec,ACCELCODEC_R14, 0x00); + gR1314Reg = 0x00; + rk610_codec_write(codec,ACCELCODEC_R1C, ASC_DEM_ENABLE); //0x00); //use default value +} +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) +static int rk610_codec_probe(struct snd_soc_codec *codec) +{ + struct rk610_codec_priv *rk610_codec = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = &codec->dapm; + int ret; + + rk610_codec_codec = codec; + printk(KERN_ERR "[%s] start\n", __FUNCTION__); + ret = snd_soc_codec_set_cache_io(codec, 8, 16, rk610_codec->control_type); + if (ret != 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + //For RK610, i2c write&read method is special, do not use system default method. + codec->write = rk610_codec_write; + codec->read = rk610_codec_read; + codec->hw_write = (hw_write_t)i2c_master_send; +#else +static int rk610_codec_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + int ret = 0; +#endif + if (rk610_codec_codec == NULL) { + #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) + dev_err(codec->dev, "Codec device not registered\n"); + #else + dev_err(&pdev->dev, "Codec device not registered\n"); + #endif + return -ENODEV; + } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)) + socdev->card->codec = rk610_codec_codec; + codec = rk610_codec_codec; + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + dev_err(codec->dev, "failed to create pcms: %d\n", ret); + goto pcm_err; + } +#endif + + snd_soc_add_controls(codec, rk610_codec_snd_controls, + ARRAY_SIZE(rk610_codec_snd_controls)); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)) + snd_soc_dapm_new_controls(codec, rk610_codec_dapm_widgets, + ARRAY_SIZE(rk610_codec_dapm_widgets)); + snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_new_widgets(codec); + + ret = snd_soc_init_card(socdev); + if (ret < 0) { + dev_err(codec->dev, "failed to register card: %d\n", ret); + goto card_err; + } +#else + snd_soc_dapm_new_controls(dapm, rk610_codec_dapm_widgets, + ARRAY_SIZE(rk610_codec_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); + +#endif + +#if defined(RK610_SPK_CTRL_PIN) + ret = gpio_request(RK610_SPK_CTRL_PIN, "rk610 spk_ctrl"); + if (ret){ + printk("rk610_control request gpio fail\n"); + //goto err1; + } + gpio_set_value(RK610_SPK_CTRL_PIN, GPIO_HIGH); + gpio_direction_output(RK610_SPK_CTRL_PIN, GPIO_HIGH); +#endif + + rk610_control_init_codec(); + + rk610_codec_reg_set(); + rk610_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return ret; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)) +card_err: + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); +pcm_err: + return ret; +#endif +} + +/* power down chip */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) +static int rk610_codec_remove(struct snd_soc_codec *codec) +{ + rk610_codec_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} +#else +static int rk610_codec_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + printk("rk610_codec_remove\n"); + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); + return 0; +} +#endif + +static struct snd_soc_codec_driver soc_codec_dev_rk610_codec = { + .probe = rk610_codec_probe, + .remove = rk610_codec_remove, + .suspend = rk610_codec_suspend, + .resume = rk610_codec_resume, + .set_bias_level = rk610_codec_set_bias_level, +// .volatile_register = wm8900_volatile_register, + .reg_cache_size = ARRAY_SIZE(rk610_codec_reg), + .reg_word_size = sizeof(u16), + .reg_cache_default = rk610_codec_reg, + .dapm_widgets = rk610_codec_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rk610_codec_dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), +}; + +EXPORT_SYMBOL_GPL(soc_codec_dev_rk610_codec); + + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static int rk610_codec_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct rk610_codec_priv *rk610_codec; + int ret; + printk("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ %s start $$$$$$$$$$$$$$$$$$$$$$$$$$$\n", __FUNCTION__); + rk610_codec = kzalloc(sizeof(struct rk610_codec_priv), GFP_KERNEL); + if (rk610_codec == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, rk610_codec); + rk610_codec->control_type = SND_SOC_I2C; + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_rk610_codec, &rk610_codec_dai, 1); + if (ret < 0) { + printk("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ %s: snd_soc_register_codec error!!!!\n", __FUNCTION__); + dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); + kfree(rk610_codec); + } + return ret; +} + + +static int rk610_codec_i2c_remove(struct i2c_client *client) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) + snd_soc_unregister_codec(&client->dev); + kfree(i2c_get_clientdata(client)); +#else + struct rk610_codec_priv *rk610_codec = i2c_get_clientdata(client); + rk610_codec_unregister(rk610_codec); +#endif + return 0; +} + +#ifdef CONFIG_PM +static int rk610_codec_i2c_suspend(struct i2c_client *client, pm_message_t msg) +{ + #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) + return 0; + #else + return snd_soc_suspend_device(&client->dev); + #endif +} + +static int rk610_codec_i2c_resume(struct i2c_client *client) +{ + #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) + return 0; + #else + return snd_soc_resume_device(&client->dev); + #endif +} +#else +#define rk610_codec_i2c_suspend NULL +#define rk610_codec_i2c_resume NULL +#endif + +static const struct i2c_device_id rk610_codec_i2c_id[] = { + { "rk610_i2c_codec", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rk610_codec_i2c_id); + +/* corgi i2c codec control layer */ +static struct i2c_driver rk610_codec_i2c_driver = { + .driver = { + .name = "RK610_CODEC", + .owner = THIS_MODULE, + }, + .probe = rk610_codec_i2c_probe, + .remove = rk610_codec_i2c_remove, + //.suspend = rk610_codec_i2c_suspend, + //.resume = rk610_codec_i2c_resume, + .id_table = rk610_codec_i2c_id, +}; +#endif + +static int __init rk610_codec_modinit(void) +{ + int ret; +// DBG("[%s] start\n", __FUNCTION__); + ret = i2c_add_driver(&rk610_codec_i2c_driver); + if (ret != 0) + pr_err("rk610 codec: Unable to register I2C driver: %d\n", ret); + return ret; +} +module_init(rk610_codec_modinit); + +static void __exit rk610_codec_exit(void) +{ + i2c_del_driver(&rk610_codec_i2c_driver); +} +module_exit(rk610_codec_exit); + +MODULE_DESCRIPTION("ASoC RK610 CODEC driver"); +MODULE_AUTHOR("rk@rock-chips.com"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rk610_codec.h b/sound/soc/codecs/rk610_codec.h new file mode 100644 index 000000000000..1e31047c4688 --- /dev/null +++ b/sound/soc/codecs/rk610_codec.h @@ -0,0 +1,267 @@ +/* + * + * Copyright (C) 2009 rockchip lhh + * + * Based on WM8750.h + * + * 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. + * + */ + +#ifndef _RK610_CODEC_H +#define _RK610_CODEC_H + +/* RK610 register space */ + +#define ACCELCODEC_R00 0x00 //ADC High Pass Filter / DSM +#define ACCELCODEC_R01 0x01 //DITHER power +#define ACCELCODEC_R02 0x02 //DITHER power +#define ACCELCODEC_R03 0x03 //DITHER power +#define ACCELCODEC_R04 0x04 //Soft mute / sidetone gain control +#define ACCELCODEC_R05 0x05 //Right interpolate filter volume control (MSB) +#define ACCELCODEC_R06 0x06 //Right interpolate filter volume control (LSB) +#define ACCELCODEC_R07 0x07 //Left interpolate filter volume control (MSB) +#define ACCELCODEC_R08 0x08 //Left interpolate filter volume control (LSB) +#define ACCELCODEC_R09 0x09 //Audio interface control +#define ACCELCODEC_R0A 0x0A //Sample Rate / CLK control +#define ACCELCODEC_R0B 0x0B //Decimation filter / Interpolate filter enable +#define ACCELCODEC_R0C 0x0C //LIN volume +#define ACCELCODEC_R0D 0x0D //LIP volume +#define ACCELCODEC_R0E 0x0E //AL volume +//#define ACCELCODEC_R0F 0x0F //RIN volume +//#define ACCELCODEC_R10 0x10 //RIP volume +//#define ACCELCODEC_R11 0x11 //AR volume +#define ACCELCODEC_R12 0x12 //Input volume +#define ACCELCODEC_R13 0x13 //Left out mix +#define ACCELCODEC_R14 0x14 //Right out mix +#define ACCELCODEC_R15 0x15 //LPF out mix / SCF +#define ACCELCODEC_R16 0x16 //SCF control +#define ACCELCODEC_R17 0x17 //LOUT (AOL) volume +#define ACCELCODEC_R18 0x18 //ROUT (AOR) volume +#define ACCELCODEC_R19 0x19 //MONOOUT (AOM) volume +#define ACCELCODEC_R1A 0x1A //MONOOUT / Reference control +#define ACCELCODEC_R1B 0x1B //Bias Current control +#define ACCELCODEC_R1C 0x1C //ADC control +#define ACCELCODEC_R1D 0x1D //Power Mrg 1 +#define ACCELCODEC_R1E 0x1E //Power Mrg 2 +#define ACCELCODEC_R1F 0x1F //Power Mrg 3 + +#define RK610_CACHE_REGNUM 0x1F + +//ACCELCODEC_R00 +#define ASC_HPF_ENABLE (0x1) //high_pass filter +#define ASC_HPF_DISABLE (0x0) + +#define ASC_DSM_MODE_ENABLE (0x1 << 1) +#define ASC_DSM_MODE_DISABLE (0x0 << 1) + +#define ASC_SCRAMBLE_ENABLE (0x1 << 2) +#define ASC_SCRAMBLE_DISABLE (0x0 << 2) + +#define ASC_DITHER_ENABLE (0x1 << 3) +#define ASC_DITHER_DISABLE (0x0 << 3) + +#define ASC_BCLKDIV_4 (0x1 << 4) +#define ASC_BCLKDIV_8 (0x2 << 4) +#define ASC_BCLKDIV_16 (0x3 << 4) + +//ACCECODEC_R04 +#define ASC_INT_MUTE_L (0x1) +#define ASC_INT_ACTIVE_L (0x0) +#define ASC_INT_MUTE_R (0x1 << 1) +#define ASC_INT_ACTIVE_R (0x0 << 1) + +#define ASC_SIDETONE_L_OFF (0x0 << 2) +#define ASC_SIDETONE_L_GAIN_MAX (0x1 << 2) +#define ASC_SIDETONE_R_OFF (0x0 << 5) +#define ASC_SIDETONE_R_GAIN_MAX (0x1 << 5) + + +//ACCELCODEC_R05 +#define ASC_INT_VOL_0DB (0x0) + + +//ACCELCODEC_R09 +#define ASC_DSP_MODE (0x3) +#define ASC_I2S_MODE (0x2) +#define ASC_LEFT_MODE (0x1) +//#define ASC_RIGHT_MODE (0x0) + +#define ASC_32BIT_MODE (0x3 << 2) +#define ASC_24BIT_MODE (0x2 << 2) +#define ASC_20BIT_MODE (0x1 << 2) +#define ASC_16BIT_MODE (0x0 << 2) + +#define ASC_INVERT_LRCLK (0x1 << 4) +#define ASC_NORMAL_LRCLK (0x0 << 4) + +#define ASC_LRSWAP_ENABLE (0x1 << 5) +#define ASC_LRSWAP_DISABLE (0x0 << 5) + +#define ASC_MASTER_MODE (0x1 << 6) +#define ASC_SLAVE_MODE (0x0 << 6) + +#define ASC_INVERT_BCLK (0x1 << 7) +#define ASC_NORMAL_BCLK (0x0 << 7) + +//ACCELCODEC_R0A +#define ASC_USB_MODE (0x1) +#define ASC_NORMAL_MODE (0x0) + +#define FREQ96kHz (0x0e << 1) +#define FREQ48kHz (0x00 << 1) +#define FREQ441kHz (0x11 << 1) +#define FREQ32kHz (0x0c << 1) +#define FREQ24kHz (0x1c << 1) +#define FREQ2205kHz (0x1B << 1) +#define FREQ16kHz (0x0a << 1) +#define FREQ12kHz (0x08 << 1) +#define FREQ11025kHz (0x19 << 1) +//#define FREQ9k6Hz 0x09 +#define FREQ8kHz (0x06<<1) + +#define ASC_CLKDIV2 (0x1 << 6) +#define ASC_CLKNODIV (0x0 << 6) + +#define ASC_CLK_ENABLE (0x1 << 7) +#define ASC_CLK_DISABLE (0x0 << 7) + +//ACCELCODEC_R0B +#define ASC_DEC_ENABLE (0x1) //decimation filter enable +#define ASC_DEC_DISABLE (0x0) +#define ASC_INT_ENABLE (0x1 << 1) //interpolate filter enable +#define ASC_INT_DISABLE (0x0 << 1) + +//Input +#define ASC_INPUT_MUTE (0x1 << 7) +#define ASC_INPUT_ACTIVE (0x0 << 7) +#define ASC_INPUT_VOL_0DB (0x0) + +//ACCELCODEC_R12 +#define ASC_LINE_INPUT (0) +#define ASC_MIC_INPUT (1 << 7) + +#define ASC_MIC_BOOST_0DB (0) +#define ASC_MIC_BOOST_20DB (1 << 5) + +//ACCELCODEC_R13 +#define ASC_LPGAMXVOL_0DB (0x5) +#define ASC_LPGAMX_ENABLE (0x1 << 3) //the left channel PGA output is directly fed into the left mixer +#define ASC_LPGAMX_DISABLE (0x0 << 3) +#define ASC_ALMXVOL_0DB (0x5 << 4) +#define ASC_ALMX_ENABLE (0x1 << 7) //the left second line input is directly fed into the left mixer +#define ASC_ALMX_DISABLE (0x0 << 7) + +//ACCELCODEC_R14 +#define ASC_RPGAMXVOL_0DB (0x5) +#define ASC_RPGAMX_ENABLE (0x1 << 3) //the right channel PGA output is directly fed into the right mixer +#define ASC_RPGAMX_DISABLE (0x0 << 3) +#define ASC_ARMXVOL_0DB (0x5 << 4) +#define ASC_ARMX_ENABLE (0x1 << 7) //)the right second line input is directly fed into the right mixer +#define ASC_ARMX_DISABLE (0x0 << 7) + +//ACCELCODEC_R15 +#define ASC_LDAMX_ENABLE (0x1 << 2) //the left differential signal from DAC is directly fed into the left mixer +#define ASC_LDAMX_DISABLE (0x0 << 2) +#define ASC_RDAMX_ENABLE (0x1 << 3) //the right differential signal from DAC is directly fed into the right mixer +#define ASC_RDAMX_DISABLE (0x0 << 3) +#define ASC_LSCF_MUTE (0x1 << 4) //the left channel LPF is mute +#define ASC_LSCF_ACTIVE (0x0 << 4) +#define ASC_RSCF_MUTE (0x1 << 5) //the right channel LPF is mute +#define ASC_RSCF_ACTIVE (0x0 << 5) +#define ASC_LLPFMX_ENABLE (0x1 << 6) //the left channel LPF output is fed into the left into the mixer +#define ASC_LLPFMX_DISABLE (0x0 << 6) +#define ASC_RLPFMX_ENABLE (0x1 << 7) //the right channel LPF output is fed into the right into the mixer. +#define ASC_RLPFMX_DISABLE (0x0 << 7) + +//ACCELCODEC_R17/R18 +#define ASC_OUTPUT_MUTE (0x1 << 6) +#define ASC_OUTPUT_ACTIVE (0x0 << 6) +#define ASC_CROSSZERO_EN (0x1 << 7) +#define ASC_OUTPUT_VOL_0DB (0x0F) +//ACCELCODEC_R19 +#define ASC_MONO_OUTPUT_MUTE (0x1 << 7) +#define ASC_MONO_OUTPUT_ACTIVE (0x0 << 7) +#define ASC_MONO_CROSSZERO_EN (0x1 << 6) + +//ACCELCODEC_R1A +#define ASC_VMDSCL_SLOWEST (0x0 << 2) +#define ASC_VMDSCL_SLOW (0x1 << 2) +#define ASC_VMDSCL_FAST (0x2 << 2) +#define ASC_VMDSCL_FASTEST (0x3 << 2) + +#define ASC_MICBIAS_09 (0x1 << 4) +#define ASC_MICBIAS_06 (0x0 << 4) + +#define ASC_L2M_ENABLE (0x1 << 5) //the right channel LPF output is fed to mono PA +#define ASC_L2M_DISABLE (0x0 << 5) +#define ASC_R2M_ENABLE (0x1 << 6) //the left channel LPF output is fed to mono PA +#define ASC_R2M_DISABLE (0x0 << 6) +#define ASC_CAPLESS_ENABLE (0x1 << 7) //the capless connection is enable +#define ASC_CAPLESS_DISABLE (0x0 << 7) + +//ACCELCODEC_R1C +#define ASC_DITH_0_DIV (0x0 << 3) //the amplitude setting of the ASDM dither(div=vdd/48) +#define ASC_DITH_2_DIV (0x1 << 3) +#define ASC_DITH_4_DIV (0x2 << 3) +#define ASC_DITH_8_DIV (0x3 << 3) + +#define ASC_DITH_ENABLE (0x1 << 5) //the ASDM dither is enabled +#define ASC_DITH_DISABLE (0x0 << 5) + +#define ASC_DEM_ENABLE (0x1 << 7) //the ASDM DEM is enabled +#define ASC_DEM_DISABLE (0x0 << 7) + +//ACCELCODEC_R1D +#define ASC_PDVMID_ENABLE (0x1) //the VMID reference is powered down. VMID is connected to GND +#define ASC_PDVMID_DISABLE (0x0) +#define ASC_PDSDL_ENABLE (0x1 << 2) //the PGA S2D buffer is power down +#define ASC_PDSDL_DISABLE (0x0 << 2) +#define ASC_PDBSTL_ENABLE (0x1 << 4) //the micphone input Op-Amp is power down +#define ASC_PDBSTL_DISABLE (0x0 << 4) +#define ASC_PDPGAL_ENABLE (0x1 << 6) //the PGA is power down +#define ASC_PDPGAL_DISABLE (0x0 << 6) +#define ASC_PDREF_ENABLE (0x1 << 7) //reference generator is power down +#define ASC_PDREF_DISABLE (0x0 << 7) + +//ACCELCODEC_R1E +#define ASC_PDPAR_ENABLE (0x1) //the right channel PA is power down +#define ASC_PDPAR_DISABLE (0x0) +#define ASC_PDPAL_ENABLE (0x1 << 1) //the left channel power amplifier is power down +#define ASC_PDPAL_DISABLE (0x0 << 1) +#define ASC_PDMIXR_ENABLE (0x1 << 2) //the right mixer is power down +#define ASC_PDMIXR_DISABLE (0x0 << 2) +#define ASC_PDMIXL_ENABLE (0x1 << 3) //the left mixer is power down +#define ASC_PDMIXL_DISABLE (0x0 << 3) +#define ASC_PDLPFR_ENABLE (0x1 << 4) //the right RC LPF is power down +#define ASC_PDLPFR_DISABLE (0x0 << 4) +#define ASC_PDLPFL_ENABLE (0x1 << 5) //the left channel RC LPF is power down +#define ASC_PDLPFL_DISABLE (0x0 << 5) +#define ASC_PDASDML_ENABLE (0x1 << 7) //the ASDM is power down +#define ASC_PDASDML_DISABLE (0x0 << 7) + +//ACCELCODEC_R1F +#define ASC_PDSCFR_ENABLE (0x1 << 1) //the right channel DAC is power down +#define ASC_PDSCFR_DISABLE (0x0 << 1) +#define ASC_PDSCFL_ENABLE (0x1 << 2) //the left channel DAC is power down +#define ASC_PDSCFL_DISABLE (0x0 << 2) +#define ASC_PDMICB_ENABLE (0x1 << 4) //the micbias is power down +#define ASC_PDMICB_DISABLE (0x0 << 4) +#define ASC_PDIB_ENABLE (0x1 << 5) //the left channel LPF is power down +#define ASC_PDIB_DISABLE (0x0 << 5) +#define ASC_PDMIXM_ENABLE (0x1 << 6) //the mon mixer is power down +#define ASC_PDMIXM_DISABLE (0x0 << 6) +#define ASC_PDPAM_ENABLE (0x1 << 7) //the mono PA is power down. +#define ASC_PDPAM_DISABLE (0x0 << 7) + +#define LINE_2_MIXER_GAIN (0x5) //left and right PA gain +#define RK610_CODEC_NUM_REG 0x20 + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)) +extern struct snd_soc_dai rk610_codec_dai; +extern struct snd_soc_codec_device soc_codec_dev_rk610_codec; +#endif + +#endif diff --git a/sound/soc/rk29/Kconfig b/sound/soc/rk29/Kconfig index 98169399de09..fcfb95479a21 100755 --- a/sound/soc/rk29/Kconfig +++ b/sound/soc/rk29/Kconfig @@ -42,6 +42,12 @@ choice endchoice endif +config SND_RK29_SOC_SPDIF + bool "Soc RK29 SPDIF support" + depends on SND_RK29_SOC + depends on SND_RK29_SOC_I2S + help + This supports the use of SPDIF interface on rk29 processors config SND_RK29_SOC_WM8988 tristate "SoC I2S Audio support for rockchip - WM8988" depends on SND_RK29_SOC @@ -136,8 +142,23 @@ config SND_RK29_SOC_RK1000 help Say Y if you want to add support for SoC audio on rockchip with the RK1000. +config SND_RK29_SOC_HDMI + tristate "SoC I2S Audio support for rockchip - HDMI" + depends on SND_RK29_SOC && HDMI_ITV + select SND_RK29_SOC_I2S + help + Say Y if you want to add support for SoC audio on rockchip + with the HDMI. +config SND_RK29_SOC_RK610 + tristate "SoC I2S Audio support for rockchip - RK610" + depends on SND_RK29_SOC && MFD_RK610 && I2C_RK29 + select SND_RK29_SOC_I2S + select SND_SOC_RK610 + help + Say Y if you want to add support for SoC audio on rockchip + with the RK610(JETTA). -if SND_RK29_SOC_WM8988 || SND_RK29_SOC_RK1000 || SND_RK29_SOC_WM8994 || SND_RK29_SOC_WM8900 || SND_RK29_SOC_RT5621 || SND_RK29_SOC_RT5631 || SND_RK29_SOC_RT5625 || SND_RK29_SOC_CS42L52 || SND_RK29_SOC_AIC3111 +if SND_RK29_SOC_WM8988 || SND_RK29_SOC_RK1000 || SND_RK29_SOC_WM8994 || SND_RK29_SOC_WM8900 || SND_RK29_SOC_RT5621 || SND_RK29_SOC_RT5631 || SND_RK29_SOC_RT5625 || SND_RK29_SOC_CS42L52 || SND_RK29_SOC_AIC3111 || SND_RK29_SOC_HDMI || SND_RK29_SOC_RK610 choice bool "Set i2s type" default SND_RK29_CODEC_SOC_SLAVE diff --git a/sound/soc/rk29/Makefile b/sound/soc/rk29/Makefile index 06bed425d071..81759578ebfb 100644 --- a/sound/soc/rk29/Makefile +++ b/sound/soc/rk29/Makefile @@ -6,9 +6,11 @@ endif ifdef CONFIG_ARCH_RK30 snd-soc-rockchip-i2s-objs := rk30_i2s.o endif +snd-soc-rockchip-spdif-objs := rk29_spdif.o obj-$(CONFIG_SND_RK29_SOC) += snd-soc-rockchip.o obj-$(CONFIG_SND_RK29_SOC_I2S) += snd-soc-rockchip-i2s.o +obj-$(CONFIG_SND_RK29_SOC_SPDIF) += snd-soc-rockchip-spdif.o # ROCKCHIP Machine Support snd-soc-wm8900-objs := rk29_wm8900.o @@ -20,6 +22,8 @@ snd-soc-aic3111-objs := rk29_aic3111.o snd-soc-wm8988-objs := rk29_wm8988.o snd-soc-rk1000-objs := rk29_rk1000codec.o snd-soc-wm8994-objs := rk29_wm8994.o +snd-soc-hdmi-objs := rk29_hdmi.o +snd-soc-rk610-objs := rk29_jetta_codec.o obj-$(CONFIG_SND_RK29_SOC_WM8994) += snd-soc-wm8994.o obj-$(CONFIG_SND_RK29_SOC_WM8988) += snd-soc-wm8988.o @@ -30,3 +34,5 @@ obj-$(CONFIG_SND_RK29_SOC_RT5625) += snd-soc-rt5625.o obj-$(CONFIG_SND_RK29_SOC_RK1000) += snd-soc-rk1000.o obj-$(CONFIG_SND_RK29_SOC_CS42L52) += snd-soc-cs42l52.o obj-$(CONFIG_SND_RK29_SOC_AIC3111) += snd-soc-aic3111.o +obj-$(CONFIG_SND_RK29_SOC_HDMI) += snd-soc-hdmi.o +obj-$(CONFIG_SND_RK29_SOC_RK610) += snd-soc-rk610.o \ No newline at end of file diff --git a/sound/soc/rk29/rk29_jetta_codec.c b/sound/soc/rk29/rk29_jetta_codec.c new file mode 100644 index 000000000000..c657f7a2124d --- /dev/null +++ b/sound/soc/rk29/rk29_jetta_codec.c @@ -0,0 +1,280 @@ +/* + * rk29_wm8988.c -- SoC audio for rockchip + * + * Driver for rockchip wm8988 audio + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../codecs/rk610_codec.h" +#include "rk29_pcm.h" +#include "rk29_i2s.h" + +#if 0 +#define DBG(x...) printk(KERN_ERR x) +#else +#define DBG(x...) +#endif + +static int rk29_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; +#else + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; +#endif + int ret; + unsigned int pll_out = 0; + int div_bclk,div_mclk; +// struct clk *general_pll; + + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + /*by Vincent Hsiung for EQ Vol Change*/ + #define HW_PARAMS_FLAG_EQVOL_ON 0x21 + #define HW_PARAMS_FLAG_EQVOL_OFF 0x22 + if ((params->flags == HW_PARAMS_FLAG_EQVOL_ON)||(params->flags == HW_PARAMS_FLAG_EQVOL_OFF)) + { + #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) + ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai); //by Vincent + #else + ret = codec_dai->ops->hw_params(substream, params, codec_dai); //by Vincent + #endif + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + } + else + { + /* set codec DAI configuration */ + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + #if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) + #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) + ret = codec_dai->driver->ops->set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + #else + ret = codec_dai->ops->set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + #endif + #endif + #if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER) + #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) + ret = codec_dai->driver->ops->set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM ); + #else + ret = codec_dai->ops->set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM ); + #endif + #endif + if (ret < 0) + return ret; + /* set cpu DAI configuration */ + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + #if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) + #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) + ret = cpu_dai->driver->ops->set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); + #else + ret = cpu_dai->ops->set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); + #endif + #endif + #if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER) + #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) + ret = cpu_dai->driver->ops->set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + #else + ret = cpu_dai->ops->set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + #endif + #endif + if (ret < 0) + return ret; + } + + switch(params_rate(params)) { + case 8000: + case 16000: + case 24000: + case 32000: + case 48000: + case 96000: + pll_out = 12288000; + break; + case 11025: + case 22050: + case 44100: + case 88200: + pll_out = 11289600; + break; + case 176400: + pll_out = 11289600*2; + break; + case 192000: + pll_out = 12288000*2; + break; + default: + DBG("Enter:%s, %d, Error rate=%d\n",__FUNCTION__,__LINE__,params_rate(params)); + return -EINVAL; + break; + } + DBG("Enter:%s, %d, rate=%d\n",__FUNCTION__,__LINE__,params_rate(params)); + snd_soc_dai_set_sysclk(codec_dai, 0, pll_out, SND_SOC_CLOCK_IN); + +// #if defined (CONFIG_SND_RK29_CODEC_SOC_MASTER) +// snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0); +// #endif + #if defined (CONFIG_SND_RK29_CODEC_SOC_SLAVE) + div_bclk = 63; + div_mclk = pll_out/(params_rate(params)*64) - 1; + + DBG("func is%s,pll_out=%ld,div_mclk=%ld div_bclk=%ld\n", + __FUNCTION__,pll_out,div_mclk, div_bclk); + snd_soc_dai_set_sysclk(cpu_dai, 0, pll_out, 0); + snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_BCLK,div_bclk); + snd_soc_dai_set_clkdiv(cpu_dai, ROCKCHIP_DIV_MCLK, div_mclk); +// DBG("Enter:%s, %d, LRCK=%d\n",__FUNCTION__,__LINE__,(pll_out/4)/params_rate(params)); + #endif + return 0; +} + +static const struct snd_soc_dapm_widget rk29_dapm_widgets[] = { + SND_SOC_DAPM_LINE("Audio Out", NULL), + SND_SOC_DAPM_LINE("Line in", NULL), + SND_SOC_DAPM_MIC("Micn", NULL), + SND_SOC_DAPM_MIC("Micp", NULL), +}; + +static const struct snd_soc_dapm_route audio_map[]= { + + {"Audio Out", NULL, "LOUT1"}, + {"Audio Out", NULL, "ROUT1"}, + {"Line in", NULL, "RINPUT1"}, + {"Line in", NULL, "LINPUT1"}, +// {"Micn", NULL, "RINPUT2"}, +// {"Micp", NULL, "LINPUT2"}, +}; + +/* + * Logic for a RK610 codec as connected on a rockchip board. + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) +static int rk29_RK610_codec_init(struct snd_soc_pcm_runtime *rtd) { + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + /* Add specific widgets */ + snd_soc_dapm_new_controls(dapm, rk29_dapm_widgets, + ARRAY_SIZE(rk29_dapm_widgets)); + + /* Set up specific audio path audio_mapnects */ + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); + snd_soc_dapm_sync(dapm); + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + + return 0; +} +#else +static int rk29_RK610_codec_init(struct snd_soc_codec *codec) { +// struct snd_soc_dai *codec_dai = &codec->dai[0]; + int ret; + + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + +// ret = snd_soc_dai_set_sysclk(codec_dai, 0, +// 11289600, SND_SOC_CLOCK_IN); +// if (ret < 0) { +// printk(KERN_ERR "Failed to set WM8988 SYSCLK: %d\n", ret); +// return ret; +// } + + /* Add specific widgets */ + snd_soc_dapm_new_controls(codec, rk29_dapm_widgets, + ARRAY_SIZE(rk29_dapm_widgets)); + + /* Set up specific audio path audio_mapnects */ + snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + + snd_soc_dapm_sync(codec); + + return 0; +} +#endif +static struct snd_soc_ops rk29_ops = { + .hw_params = rk29_hw_params, +}; + +static struct snd_soc_dai_link rk29_dai = { + .name = "RK610", + .stream_name = "RK610 CODEC PCM", + .codec_name = "RK610_CODEC.1-0060", + .platform_name = "rockchip-audio", +#if defined(CONFIG_SND_RK29_SOC_I2S_8CH) + .cpu_dai_name = "rk29_i2s.0", +#elif defined(CONFIG_SND_RK29_SOC_I2S_2CH) + .cpu_dai_name = "rk29_i2s.1", +#endif + .codec_dai_name = "rk610_codec_xx", + .init = rk29_RK610_codec_init, + .ops = &rk29_ops, +}; +static struct snd_soc_card snd_soc_card_rk29 = { + .name = "RK29_RK610", + .dai_link = &rk29_dai, + .num_links = 1, +}; + +static struct platform_device *rk29_snd_device; + +static int __init audio_card_init(void) +{ + int ret =0; + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + printk(KERN_ERR "[%s] start\n", __FUNCTION__); + rk29_snd_device = platform_device_alloc("soc-audio", -1); + if (!rk29_snd_device) { + printk("[%s] platform device allocation failed\n", __FUNCTION__); + ret = -ENOMEM; + return ret; + } + #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) + platform_set_drvdata(rk29_snd_device, &snd_soc_card_rk29); + #else + platform_set_drvdata(rk29_snd_device, &rk29_snd_devdata); + rk29_snd_devdata.dev = &rk29_snd_device->dev; + #endif + ret = platform_device_add(rk29_snd_device); + DBG("Enter::%s----%d\n",__FUNCTION__,__LINE__); + if (ret) { + DBG("platform device add failed\n"); + platform_device_put(rk29_snd_device); + } + return ret; +} +static void __exit audio_card_exit(void) +{ + platform_device_unregister(rk29_snd_device); +} + +module_init(audio_card_init); +module_exit(audio_card_exit); +/* Module information */ +MODULE_AUTHOR("rockchip"); +MODULE_DESCRIPTION("ROCKCHIP i2s ASoC Interface"); +MODULE_LICENSE("GPL"); -- 2.34.1