rk610 hdmi lcd lvds tvout codec support

This commit is contained in:
yzq 2012-03-27 20:09:59 -07:00
parent ac1afdbe8f
commit cbba37b0bb
49 changed files with 6178 additions and 269 deletions

View File

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

View File

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

View File

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

View File

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

272
drivers/mfd/rk610-core.c Normal file
View File

@ -0,0 +1,272 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <asm/gpio.h>
#include <linux/mfd/rk610_core.h>
#include <linux/clk.h>
#include <mach/iomux.h>
#include <linux/err.h>
#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,&reg);
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, <www.rock-chips.com>");
MODULE_LICENSE("GPL");

View File

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

View File

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

View File

@ -0,0 +1,404 @@
#include <linux/module.h>
#include <linux/ctype.h>
#include <linux/idr.h>
#include <linux/err.h>
#include <linux/kdev_t.h>
#include <linux/display-sys.h>
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");

View File

@ -1,219 +0,0 @@
/*
* display-sysfs.c - Display output driver sysfs interface
*
* Copyright (C) 2007 James Simmons <jsimmons@infradead.org>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* 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 <linux/module.h>
#include <linux/display.h>
#include <linux/ctype.h>
#include <linux/idr.h>
#include <linux/err.h>
#include <linux/kdev_t.h>
#include <linux/slab.h>
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 <jsimmons@infradead.org>");
MODULE_LICENSE("GPL");

View File

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

View File

@ -0,0 +1,4 @@
#
# Makefile for the jetta tv control.
#
obj-$(CONFIG_RK610_LCD) += rk610_lcd.o

View File

@ -0,0 +1,300 @@
#include <linux/fb.h>
#include <linux/delay.h>
#include <mach/gpio.h>
#include <mach/iomux.h>
#include <mach/board.h>
#include <linux/hdmi.h>
#include "rk610_lcd.h"
#include <linux/mfd/rk610_core.h>
#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;
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,153 @@
#include <linux/fb.h>
#include <linux/delay.h>
#include <mach/gpio.h>
#include <mach/iomux.h>
#include <mach/board.h>
#include "screen.h"
#include <linux/hdmi.h>
#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
}

View File

@ -0,0 +1,271 @@
#include <linux/fb.h>
#include <linux/delay.h>
#include <mach/gpio.h>
#include <mach/iomux.h>
#include <mach/board.h>
#include "screen.h"
#include <linux/hdmi.h>
#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
}

View File

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

View File

@ -1,8 +1,51 @@
#ifndef _SCREEN_H
#define _SCREEN_H
#include <mach/board.h>
#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

View File

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

View File

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

View File

@ -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 <linux/module.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/fcntl.h>
#include <linux/fs.h>
#include <linux/fb.h>
#include <linux/console.h>
#include <asm/uaccess.h>
#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");

View File

@ -0,0 +1,129 @@
#ifndef _RK610_TV_H
#define _RK610_TV_H
#include <linux/display-sys.h>
#include <linux/fb.h>
#include <mach/board.h>
#include <mach/gpio.h>
#include <mach/rk29_iomap.h>
#include "../screen/screen.h"
#include "../../rk29_fb.h"
#include <linux/mfd/rk610_core.h>
#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

View File

@ -0,0 +1,209 @@
#include <linux/ctype.h>
#include <linux/string.h>
#include <linux/display-sys.h>
#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;
}

View File

@ -0,0 +1,229 @@
#include <linux/ctype.h>
#include <linux/string.h>
#include <linux/display-sys.h>
#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;
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,299 @@
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/hdmi.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <mach/gpio.h>
#include <mach/iomux.h>
#include <mach/board.h>
#include <linux/irq.h>
#include <linux/mfd/rk610_core.h>
#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,&reg,&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);

View File

@ -0,0 +1,36 @@
#ifndef _RK610_H
#define _RK610_H
#include <linux/hdmi.h>
#include <linux/earlysuspend.h>
/************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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,208 @@
#ifndef _RK610_HDMI_HW_H
#define _RK610_HDMI_HW_H
#include <linux/earlysuspend.h>
#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

View File

@ -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->display_on) && hdmi->mode == DISP_ON_HDMI){
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_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;

View File

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

View File

@ -50,6 +50,7 @@
#include <linux/hdmi.h>
#endif
#include <mach/iomux.h>
#include <mach/gpio.h>
@ -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)) {

View File

@ -0,0 +1,69 @@
#ifndef _LINUX_DISPLAY_RK_H
#define _LINUX_DISPLAY_RK_H
#include <linux/device.h>
#include <linux/fb.h>
#include <linux/list.h>
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

View File

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

View File

@ -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_*/

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@ -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 <linux/module.h>
#include <linux/device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <asm/io.h>
#include <mach/hardware.h>
#include <mach/rk29_iomap.h>
#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");