/*++
* linux/drivers/video/wmt/cs8556.c
* WonderMedia video post processor (VPP) driver
*
* Copyright c 2013 WonderMedia Technologies, Inc.
*
* 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, see .
*
* WonderMedia Technologies, Inc.
* 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
--*/
/********************************************************************************
* REVISON HISTORY
*
* VERSION | DATE | AUTHORS | DESCRIPTION
* 1.0 | 2013/08/24 | Howay Huo | First Release
*******************************************************************************/
#define CS8556_C
// #define DEBUG
/*----------------------- DEPENDENCE -----------------------------------------*/
#include
#include
#include
#include
#include
#include
#include "../vout.h"
/*----------------------- PRIVATE MACRO --------------------------------------*/
/* #define VT1632_XXXX xxxx *//*Example*/
//#define CONFIG_CS8556_INTERRUPT
/*----------------------- PRIVATE CONSTANTS ----------------------------------*/
/* #define VT1632_XXXX 1 *//*Example*/
#define CS8556_ADDR 0x3d
#define CS8556_NAME "CS8556"
/*----------------------- PRIVATE TYPE --------------------------------------*/
/* typedef xxxx vt1632_xxx_t; *//*Example*/
typedef struct {
unsigned int flag;
unsigned int gpiono;
unsigned int act;
}avdetect_gpio_t;
/*----------EXPORTED PRIVATE VARIABLES are defined in vt1632.h -------------*/
/*----------------------- INTERNAL PRIVATE VARIABLES - -----------------------*/
/* int vt1632_xxx; *//*Example*/
static int s_cs8556_ready;
static int s_cs8556_init;
static struct i2c_client *s_cs8556_client;
static int s_irq_init;
static avdetect_gpio_t s_avdetect_gpio = {0, WMT_PIN_GP0_GPIO5, 1};
static unsigned char s_CS8556_Original_Offset0[]={
0xF0,0x7F,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x02,0x01,0x00,0x00,0x01,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
static unsigned char s_RGB888_To_PAL_Offset0[]={
0x01,0x80,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x02,0x02,0x00,0x00,0x00,0x00,
0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x5F,0x03,0x3F,0x00,0x7D,0x00,0x53,0x03,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,
0x70,0x02,0x04,0x00,0x2E,0x00,0x62,0x02,0x00,0x00,0x84,0x00,0x2B,0x00,0x36,0x00,
0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xBF,0x06,0x7F,0x00,0xFE,0x00,0xA4,0x06,0x00,0x00,0x2D,0x11,0x3C,0x01,0x3A,0x01,
0x70,0x02,0x04,0x00,0x12,0x00,0x34,0x01,0x00,0x00,0x70,0x70,0x70,0x00,0x00,0x00,
0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x04,0x41,0x18,0x09,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0xFF,0x00,0x00,0xFF,0xFF,0x00,0x00,0x80,0x80,0x00,0x00,0x80,0x80,0x00,0x00,
0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x00,
0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x01,0x24,0x1A,0x00,0x01,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x04,0x01,0xA4,0x06,0x0B,0x00,0x07,0x01,0xF0,0x00,0x00,0x00,0x00,0x04,0x40,0x01
};
static unsigned char s_RGB888_To_NTSC_Offset0[]={
0x01,0x80,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x02,0x02,0x00,0x00,0x00,0x00,
0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x59,0x03,0x3D,0x00,0x7E,0x00,0x49,0x03,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,
0x0C,0x02,0x05,0x00,0x21,0x00,0x03,0x02,0x00,0x00,0x7A,0x00,0x23,0x00,0x16,0x00,
0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xB3,0x06,0x7F,0x00,0x00,0x01,0xA4,0x06,0x00,0x00,0x05,0x50,0x00,0x01,0x07,0x01,
0x0C,0x02,0x02,0x00,0x12,0x00,0x07,0x01,0x00,0x00,0x70,0x70,0x70,0x00,0x00,0x00,
0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x04,0x41,0x18,0x09,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0xFF,0x00,0x00,0xFF,0xFF,0x00,0x00,0x80,0x80,0x00,0x00,0x80,0x80,0x00,0x00,
0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x00,
0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x01,0x24,0x1A,0x00,0x01,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x04,0x01,0xA4,0x06,0x0B,0x00,0x07,0x01,0xF0,0x00,0x00,0x00,0x00,0x04,0x00,0x00
};
/*--------------------- INTERNAL PRIVATE FUNCTIONS ---------------------------*/
/* void vt1632_xxx(void); *//*Example*/
static int I2CMultiPageRead(unsigned char maddr, unsigned char page, unsigned char saddr, int number, unsigned char *value)
{
int ret;
unsigned char wbuf[2];
struct i2c_msg rd[2];
wbuf[0] = page;
wbuf[1] = saddr;
rd[0].addr = maddr;
rd[0].flags = 0;
rd[0].len = 2;
rd[0].buf = wbuf;
rd[1].addr = maddr;
rd[1].flags = I2C_M_RD;
rd[1].len = number;
rd[1].buf = value;
ret = i2c_transfer(s_cs8556_client->adapter, rd, ARRAY_SIZE(rd));
if (ret != ARRAY_SIZE(rd)) {
DBG_ERR("fail\n");
return -1;
}
return 0;
}
static int I2CMultiPageWrite(unsigned char maddr, unsigned char page, unsigned char saddr, int number, unsigned char *value)
{
int ret;
unsigned char *pbuf;
struct i2c_msg wr[1];
pbuf = kmalloc(number + 2, GFP_KERNEL);
if(!pbuf) {
DBG_ERR("alloc memory fail\n");
return -1;
}
*pbuf = page;
*(pbuf + 1) = saddr;
memcpy(pbuf + 2, value, number);
wr[0].addr = maddr;
wr[0].flags = 0;
wr[0].len = number + 2;
wr[0].buf = pbuf;
ret = i2c_transfer(s_cs8556_client->adapter, wr, ARRAY_SIZE(wr));
if (ret != ARRAY_SIZE(wr)) {
DBG_ERR("fail\n");
kfree(pbuf);
return -1;
}
kfree(pbuf);
return 0 ;
}
/************************** i2c device struct definition **************************/
static int __devinit cs8556_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
DBGMSG("cs8556_i2c_probe\n");
return 0;
}
static int __devexit cs8556_i2c_remove(struct i2c_client *client)
{
DBGMSG("cs8556_i2c_remove\n");
return 0;
}
static const struct i2c_device_id cs8556_i2c_id[] = {
{CS8556_NAME, 0},
{ },
};
MODULE_DEVICE_TABLE(i2c, cs8556_i2c_id);
static struct i2c_board_info __initdata cs8556_i2c_board_info[] = {
{
I2C_BOARD_INFO(CS8556_NAME, CS8556_ADDR),
},
};
static struct i2c_driver cs8556_i2c_driver = {
.driver = {
.name = CS8556_NAME,
.owner = THIS_MODULE,
},
.probe = cs8556_i2c_probe,
.remove = __devexit_p(cs8556_i2c_remove),
.id_table = cs8556_i2c_id,
};
/*----------------------- Function Body --------------------------------------*/
static void avdetect_irq_enable(void)
{
wmt_gpio_unmask_irq(s_avdetect_gpio.gpiono);
}
static void avdetect_irq_disable(void)
{
wmt_gpio_mask_irq(s_avdetect_gpio.gpiono);
}
int avdetect_irq_hw_init(int resume)
{
int ret;
if(!resume) {
ret = gpio_request(s_avdetect_gpio.gpiono, "avdetect irq"); //enable gpio
if(ret < 0) {
DBG_ERR("gpio(%d) request fail for avdetect irq\n", s_avdetect_gpio.gpiono);
return ret;
}
}else
gpio_re_enabled(s_avdetect_gpio.gpiono); //re-enable gpio
gpio_direction_input(s_avdetect_gpio.gpiono); //gpio input
wmt_gpio_setpull(s_avdetect_gpio.gpiono, WMT_GPIO_PULL_UP); //enable pull and pull-up
wmt_gpio_mask_irq(s_avdetect_gpio.gpiono); //disable interrupt
wmt_gpio_set_irq_type(s_avdetect_gpio.gpiono, IRQ_TYPE_EDGE_BOTH); //rise edge and clear interrupt
return 0;
}
/*
static void avdetect_irq_hw_free(void)
{
gpio_free(AVDETECT_IRQ_PIN);
}
*/
static irqreturn_t avdetect_irq_handler(int irq, void *dev_id)
{
//printk("avdetect_irq_handler\n");
if(!gpio_irqstatus(s_avdetect_gpio.gpiono))
return IRQ_NONE;
wmt_gpio_ack_irq(s_avdetect_gpio.gpiono); //clear interrupt
//printk("cvbs hotplug interrupt\n");
if(!is_gpio_irqenable(s_avdetect_gpio.gpiono)) {
//pr_err("avdetect irq is disabled\n");
return IRQ_HANDLED;
}else
return IRQ_WAKE_THREAD;
}
static irqreturn_t avdetect_irq_thread(int irq, void *dev)
{
//printk(cvbs_hotplug_irq_thread\n");
if(s_avdetect_gpio.act == 1) {
if(gpio_get_value(s_avdetect_gpio.gpiono))
printk("av plug in\n");
else
printk("av plug out\n");
}
else {
if(gpio_get_value(s_avdetect_gpio.gpiono))
printk("av plug out\n");
else
printk("av plug in\n");
}
return IRQ_HANDLED;
}
void cs8556_set_tv_mode(int ntsc)
{
int ret;
unsigned char rbuf[256] = {0};
if( !s_cs8556_ready )
return;
ret = I2CMultiPageRead(CS8556_ADDR, 0x00, 0x00, 256, rbuf);
if(ret) {
DBG_ERR("I2C read Offset0 fail\n");
return;
}
if(ntsc < 0) {
if(memcmp(rbuf, s_CS8556_Original_Offset0, 0x11) != 0) {
ret = I2CMultiPageWrite(CS8556_ADDR, 0x00, 0x00, 256, s_CS8556_Original_Offset0);
if(ret)
DBG_ERR("I2C write Original_Offset0 fail\n");
else {
if(s_irq_init)
avdetect_irq_disable();
}
}
return;
}
if(ntsc) {
if(memcmp(rbuf, s_RGB888_To_NTSC_Offset0, 0x50) !=0) {
ret = I2CMultiPageWrite(CS8556_ADDR, 0x00, 0x00, 256, s_RGB888_To_NTSC_Offset0);
if(ret)
DBG_ERR("I2C write NTSC_Offset0 fail\n");
}
} else {
if(memcmp(rbuf, s_RGB888_To_PAL_Offset0, 0x50) != 0) {
ret = I2CMultiPageWrite(CS8556_ADDR, 0x00, 0x00, 256, s_RGB888_To_PAL_Offset0);
if(ret)
DBG_ERR("I2C write PAL_Offset0 fail\n");
}
}
}
int cs8556_check_plugin(int hotplug)
{
return 1;
}
int cs8556_init(struct vout_s *vo)
{
int ret;
char buf[40] = {0};
int varlen = 40;
int no = 1; //default i2c1
int num;
unsigned char rbuf[256] = {0};
struct i2c_adapter *adapter = NULL;
vout_tvformat_t tvformat = TV_MAX;
DPRINT("cs8556_init\n");
if(wmt_getsyspara("wmt.display.tvformat", buf, &varlen) == 0) {
if(!strnicmp(buf, "PAL", 3))
tvformat = TV_PAL;
else if(!strnicmp(buf, "NTSC", 4))
tvformat = TV_NTSC;
else
tvformat = TV_UNDEFINED;
} else
tvformat = TV_UNDEFINED;
if(tvformat == TV_UNDEFINED)
goto err0;
if(!s_cs8556_init) {
if(wmt_getsyspara("wmt.cs8556.i2c", buf, &varlen) == 0) {
if(strlen(buf) > 0)
no = buf[0] - '0';
}
adapter = i2c_get_adapter(no);
if (adapter == NULL) {
DBG_ERR("Can not get i2c adapter, client address error\n");
goto err0;
}
s_cs8556_client = i2c_new_device(adapter, cs8556_i2c_board_info);
if (s_cs8556_client == NULL) {
DBG_ERR("allocate i2c client failed\n");
goto err0;
}
i2c_put_adapter(adapter);
ret = i2c_add_driver(&cs8556_i2c_driver);
if (ret != 0) {
DBG_ERR("Failed to register CS8556 I2C driver: %d\n", ret);
goto err1;
}
if(wmt_getsyspara("wmt.io.avdetect", buf, &varlen) == 0) {
num = sscanf(buf, "%d:%d:%d", &s_avdetect_gpio.flag, &s_avdetect_gpio.gpiono,
&s_avdetect_gpio.act);
if(num != 3)
DBG_ERR("wmt.io.avdetect is error. param num = %d\n", num);
else {
if(s_avdetect_gpio.gpiono > 19)
DBG_ERR("invalid avdetect gpio no: %d\n", s_avdetect_gpio.gpiono);
else {
ret = avdetect_irq_hw_init(0);
if(!ret) {
ret = request_threaded_irq(IRQ_GPIO, avdetect_irq_handler,
avdetect_irq_thread, IRQF_SHARED, CS8556_NAME, s_cs8556_client);
if(ret)
DBG_ERR("%s: irq request failed: %d\n", __func__, ret);
else {
s_irq_init = 1;
DPRINT("avdetect irq request success\n");
}
}
}
}
}
s_cs8556_init = 1;
}
else {
if(s_irq_init)
avdetect_irq_hw_init(1);
}
ret = I2CMultiPageRead(CS8556_ADDR, 0x00, 0x00, 256, rbuf);
if(ret) {
DBG_ERR("I2C address 0x%02X is not found\n", CS8556_ADDR);
goto err0;
}
//if(s_cs8556_ready) {
// DPRINT("cs8556_reinit");
switch(tvformat) {
case TV_PAL:
ret = I2CMultiPageWrite(CS8556_ADDR, 0x00, 0x00, 256, s_RGB888_To_PAL_Offset0);
if(ret) {
DBG_ERR("PAL init fail\n");
goto err0;
}
break;
case TV_NTSC:
ret = I2CMultiPageWrite(CS8556_ADDR, 0x00, 0x00, 256, s_RGB888_To_NTSC_Offset0);
if(ret) {
DBG_ERR("NTSC init fail\n");
goto err0;
}
break;
default:
goto err0;
break;
}
//}
if(s_irq_init)
avdetect_irq_enable();
s_cs8556_ready = 1;
return 0;
//err3:
// cvbs_hotplug_irq_disable();
// free_irq(IRQ_GPIO, s_cs8556_client);
// cvbs_hotplug_irq_hw_free();
//err2:
// i2c_del_driver(&cs8556_i2c_driver);
err1:
i2c_unregister_device(s_cs8556_client);
err0:
s_cs8556_ready = 0;
return -1;
}
static int cs8556_set_mode(unsigned int *option)
{
if(!s_cs8556_ready)
return -1;
return 0;
}
static void cs8556_set_power_down(int enable)
{
int ret;
vout_t *vo;
unsigned char rbuf[256] = {0};
if( !s_cs8556_ready )
return;
vo = vout_get_entry(VPP_VOUT_NUM_DVI);
if (vo->status & (VPP_VOUT_STS_BLANK + VPP_VOUT_STS_POWERDN)) {
enable = 1;
}
DPRINT("cs8556_set_power_down(%d)\n",enable);
ret = I2CMultiPageRead(CS8556_ADDR, 0x00, 0x00, 0x11, rbuf);
if(ret) {
DBG_ERR("I2C read Offset0 fail\n");
return;
}
if( enable ){
if(memcmp(rbuf, s_CS8556_Original_Offset0, 0x11) != 0) {
ret = I2CMultiPageWrite(CS8556_ADDR, 0x00, 0x00, 0x11, s_CS8556_Original_Offset0);
if(ret)
DBG_ERR("I2C write Offset0 to power down CS8556 fail\n");
else {
if(s_irq_init)
avdetect_irq_disable();
}
}
} else {
if(memcmp(rbuf, s_RGB888_To_PAL_Offset0, 0x11) != 0) {
ret = I2CMultiPageWrite(CS8556_ADDR, 0x00, 0x00, 0x11, s_RGB888_To_PAL_Offset0);
if(ret)
DBG_ERR("I2C write offset0 to power up CS8556 fail\n");
}
}
}
static int cs8556_config(vout_info_t *info)
{
int ntsc;
if(!s_cs8556_ready)
return -1;
if(info->resx == 720 && (info->resy == 576 || info->resy == 480)) {
ntsc = (info->resy == 480) ? 1 : 0;
DPRINT("cs8556_config (%dx%d@%d) %s\n", info->resx, info->resy, info->fps, ntsc ? "NTSC" : "PAL");
cs8556_set_tv_mode(ntsc);
} else {
DPRINT("cs8556_config (%dx%d@%d)\n", info->resx, info->resy, info->fps);
cs8556_set_tv_mode(-1);
}
return 0;
}
static int cs8556_get_edid(char *buf)
{
return -1;
}
#ifdef CONFIG_CS8556_INTERRUPT
static int cs8556_interrupt(void)
{
return cs8556_check_plugin(1);
}
#endif
void cs8556_read(void)
{
int i, ret;
unsigned char rbuf[256] = {0};
ret = I2CMultiPageRead(CS8556_ADDR, 0x00, 0x00, 256, rbuf);
if(!ret) {
printk("CS8556 Read offset0 data as follows:\n");
for(i = 0; i < 256;) {
printk("0x%02X,", rbuf[i]);
if((++i) % 16 == 0)
printk("\n");
}
}
}
/*----------------------- vout device plugin --------------------------------------*/
vout_dev_t cs8556_vout_dev_ops = {
.name = CS8556_NAME,
.mode = VOUT_INF_DVI,
.init = cs8556_init,
.set_power_down = cs8556_set_power_down,
.set_mode = cs8556_set_mode,
.config = cs8556_config,
.check_plugin = cs8556_check_plugin,
.get_edid = cs8556_get_edid,
#ifdef CONFIG_CS8556_INTERRUPT
.interrupt = cs8556_interrupt,
#endif
};
int cs8556_module_init(void)
{
vout_device_register(&cs8556_vout_dev_ops);
return 0;
} /* End of cs8556_module_init */
module_init(cs8556_module_init);
/*--------------------End of Function Body -----------------------------------*/
#undef CS8556_C