/*++ * 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*/ /*----------------------- PRIVATE CONSTANTS ----------------------------------*/ /* #define VT1632_XXXX 1 *//*Example*/ #define CS8556_ADDR 0x3d #define CS8556_NAME "CS8556" /*----------------------- PRIVATE TYPE --------------------------------------*/ /* typedef xxxx vt1632_xxx_t; *//*Example*/ struct avdetect_gpio_t { unsigned int flag; unsigned int gpiono; unsigned int act; }; /*----------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 enum vout_tvformat_t s_tvformat = TV_MAX; static int s_irq_init; static struct 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 */ /* enable pull and pull-up */ wmt_gpio_setpull(s_avdetect_gpio.gpiono, WMT_GPIO_PULL_UP); /* disable interrupt */ wmt_gpio_mask_irq(s_avdetect_gpio.gpiono); /* rise edge and clear interrupt */ wmt_gpio_set_irq_type(s_avdetect_gpio.gpiono, IRQ_TYPE_EDGE_BOTH); 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) { /* DPRINT("avdetect_irq_handler\n"); */ if (!gpio_irqstatus(s_avdetect_gpio.gpiono)) return IRQ_NONE; wmt_gpio_ack_irq(s_avdetect_gpio.gpiono); /* clear interrupt */ /* DPRINT("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) { /* DPRINT(cvbs_hotplug_irq_thread\n"); */ if (s_avdetect_gpio.act == 1) { if (gpio_get_value(s_avdetect_gpio.gpiono)) DPRINT("av plug in\n"); else DPRINT("av plug out\n"); } else { if (gpio_get_value(s_avdetect_gpio.gpiono)) DPRINT("av plug out\n"); else DPRINT("av plug in\n"); } return IRQ_HANDLED; } int cs8556_check_plugin(int hotplug) { return 1; } int cs8556_init(struct vout_t *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; DPRINT("cs8556_init\n"); if (s_tvformat == TV_MAX) { if (wmt_getsyspara("wmt.display.tvformat", buf, &varlen) == 0) { if (!strnicmp(buf, "PAL", 3)) s_tvformat = TV_PAL; else if (!strnicmp(buf, "NTSC", 4)) s_tvformat = TV_NTSC; else s_tvformat = TV_UNDEFINED; } else s_tvformat = TV_UNDEFINED; } if (s_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't 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 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 err. num = %d\n", num); else { if (s_avdetect_gpio.gpiono > 19) DBG_ERR("invalid avdetect gpio : %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("irq req %d\n", ret); else { s_irq_init = 1; DPRINT("avdetect irq"); DPRINT("req 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; } switch (s_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; #if 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); #endif 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; unsigned char rbuf[256] = {0}; if (!s_cs8556_ready) return; DBGMSG("cs8556_set_power_down(%d)\n", enable); if (enable) { ret = I2CMultiPageWrite(CS8556_ADDR, 0x00, 0x00, 5, s_CS8556_Original_Offset0); if (ret) DBG_ERR("I2C write Original_Offset0 fail\n"); else { if (s_irq_init) avdetect_irq_disable(); } } else { ret = I2CMultiPageRead(CS8556_ADDR, 0x00, 0x00, 256, rbuf); if (ret) { DBG_ERR("I2C read Offset0 fail\n"); return; } switch (s_tvformat) { case TV_PAL: 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"); } break; case TV_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 wr NTSC_Offset0 fail\n"); } break; default: break; } } } static int cs8556_config(struct vout_info_t *info) { return 0; } static int cs8556_get_edid(char *buf) { return -1; } /* static int cs8556_interrupt(void) { return cs8556_check_plugin(1); } */ void cs8556_read(void) { int i, ret; unsigned char rbuf[256] = {0}; ret = I2CMultiPageRead(CS8556_ADDR, 0x00, 0x00, 256, rbuf); if (!ret) { DPRINT("CS8556 Read offset0 data as follows:\n"); for (i = 0; i < 256;) { DPRINT("0x%02X,", rbuf[i]); if ((++i) % 16 == 0) DPRINT("\n"); } } } int cvbs_is_ready(void) { return s_cs8556_ready; } /*----------------------- vout device plugin ---------------------------------*/ struct 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, /* .interrupt = cs8556_interrupt, */ }; 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