diff options
author | Qiang.li <b19715@freescale.com> | 2013-02-01 15:10:47 +0800 |
---|---|---|
committer | Eric Nelson <eric.nelson@boundarydevices.com> | 2013-09-03 14:09:31 -0700 |
commit | bdb86a56a2c1f52fd0ae730dbfdd674ee51a006c (patch) | |
tree | 3fecad1742c1dcfa66bba0d122849ffdefd91743 /drivers | |
parent | 05e94b34e3921c2b5d2ca848c4624827378f034f (diff) |
Support adv739x TV encoder for BT656 output.
modified: arch/arm/mach-mx6/board-mx6q_sabresd.c
modified: drivers/video/mxc/Kconfig
modified: drivers/video/mxc/Makefile
new file: drivers/video/mxc/mxcfb_adv739x.c
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/video/mxc/Kconfig | 4 | ||||
-rw-r--r-- | drivers/video/mxc/Makefile | 1 | ||||
-rwxr-xr-x | drivers/video/mxc/mxcfb_adv739x.c | 379 |
3 files changed, 384 insertions, 0 deletions
diff --git a/drivers/video/mxc/Kconfig b/drivers/video/mxc/Kconfig index 0729771220fe..b57373a500b8 100644 --- a/drivers/video/mxc/Kconfig +++ b/drivers/video/mxc/Kconfig @@ -77,6 +77,10 @@ config FB_MXC_TVOUT_CH7024 tristate "CH7024 TV Out Encoder" depends on FB_MXC_SYNC_PANEL +config FB_MXC_TVOUT_ADV739X + tristate "ADV7390/7391 TV Out Encoder" + depends on FB_MXC_SYNC_PANEL && I2C + config FB_MXC_LOW_PWR_DISPLAY bool "Low Power Display Refresh Mode" depends on FB_MXC_SYNC_PANEL && MXC_FB_IRAM diff --git a/drivers/video/mxc/Makefile b/drivers/video/mxc/Makefile index 064d1ce56a80..c32879579899 100644 --- a/drivers/video/mxc/Makefile +++ b/drivers/video/mxc/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_FB_MXC_SII902X) += mxcfb_sii902x.o obj-$(CONFIG_FB_MXC_SII902X_ELCDIF) += mxcfb_sii902x_elcdif.o obj-$(CONFIG_FB_MXC_LDB) += ldb.o obj-$(CONFIG_FB_MXC_BT656) += mxc_bt656if.o +obj-$(CONFIG_FB_MXC_TVOUT_ADV739X) += mxcfb_adv739x.o obj-$(CONFIG_FB_MXC_MIPI_DSI) += mipi_dsi.o obj-$(CONFIG_FB_MXC_TRULY_WVGA_SYNC_PANEL) += mxcfb_hx8369_wvga.o obj-$(CONFIG_FB_MXC_EDID) += mxc_edid.o diff --git a/drivers/video/mxc/mxcfb_adv739x.c b/drivers/video/mxc/mxcfb_adv739x.c new file mode 100755 index 000000000000..efeb38b2b20c --- /dev/null +++ b/drivers/video/mxc/mxcfb_adv739x.c @@ -0,0 +1,379 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/mxcfb.h> +#include <linux/fsl_devices.h> +#include "mxc_dispdrv.h" + +#define DISPDRV_ADV739X "adv739x" + +#define ADV739X_MODE_NTSC 0 +#define ADV739X_MODE_PAL 1 + + +struct adv739x_data { + struct platform_device *pdev; + struct i2c_client *client; + struct mxc_dispdrv_handle *disp_adv739x; + struct fb_info *fbi; + + int ipu_id; + int disp_id; + int default_ifmt; + + int cur_mode; + int enabled; + struct notifier_block nb; +}; + +/* + * left_margin: used for field0 vStart width in lines + * + * right_margin: used for field0 vEnd width in lines + * + * up_margin: used for field1 vStart width in lines + * + * down_margin: used for field1 vEnd width in lines + * + * hsync_len: EAV Code + Blanking Video + SAV Code (in pixel clock count) + * For BT656 NTSC, it is 4 + 67*4 + 4 = 276. + * For BT1120 NTSC, it is 4 + 67*2 + 4 = 142. + * For BT656 PAL, it is 4 + 70*4 + 4 = 288. + * For BT1120 PAL, it is 4 + 70*2 + 4 = 148. + * + * vsync_len: not used, set to 1 + */ +static struct fb_videomode adv739x_modedb[] = { + { + /* NTSC Interlaced output */ + "BT656-NTSC", 60, 720, 480, 37037, + 19, 3, + 20, 3, + 276, 1, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_INTERLACED, + FB_MODE_IS_DETAILED,}, + { + /* PAL Interlaced output */ + "BT656-PAL", 50, 720, 576, 37037, + 22, 2, + 23, 2, + 288, 1, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_INTERLACED, + FB_MODE_IS_DETAILED,}, +}; +static int adv739x_modedb_sz = ARRAY_SIZE(adv739x_modedb); + +static int adv739x_write(struct i2c_client *client, u8 reg, u8 data) +{ + int ret = 0; + ret = i2c_smbus_write_byte_data(client, reg, data); + + return ret; +} + +static int adv739x_read(struct i2c_client *client, u8 reg) +{ + int data = 0; + data = i2c_smbus_read_byte_data(client, reg); + + return data; +} + +static void adv739x_setmode(struct adv739x_data *adv739x, int mode) +{ + struct i2c_client *client = adv739x->client; + + if(adv739x->enabled == 0) + return; + + dev_dbg(&adv739x->client->dev, "adv739x_setmode: mode = %d.\n", mode); + switch (mode) { + case ADV739X_MODE_NTSC: + // Reg 0x17: reset + adv739x_write(client, 0x17, 0x02); + + mdelay(20); + + // Reg 0x00: DAC1~3 power on + adv739x_write(client, 0x00, 0x1C); + + // Reg 0x01: SD input + adv739x_write(client, 0x01, 0x00); + + //NTSC + // Reg 0x80: SD, NTSC + adv739x_write(client, 0x80, 0x10); + + // Reg 0x82: SD, CVBS + adv739x_write(client, 0x82, 0xCB); + break; + + case ADV739X_MODE_PAL: + // Reg 0x17: reset + adv739x_write(client, 0x17, 0x02); + + mdelay(20); + + // Reg 0x00: DAC1~3 power on + adv739x_write(client, 0x00, 0x1C); + + // Reg 0x01: SD input + adv739x_write(client, 0x01, 0x00); + + // Reg 0x80: SD, PAL + adv739x_write(client, 0x80, 0x11); + + // Reg 0x82: SD, CVBS + adv739x_write(client, 0x82, 0xC3); + adv739x_write(client, 0x8C, 0xCB); + adv739x_write(client, 0x8D, 0x8A); + adv739x_write(client, 0x8E, 0x09); + adv739x_write(client, 0x8F, 0x2A); + break; + + default: + dev_err(&adv739x->client->dev, "unsupported mode.\n"); + break; + } +} + +static void adv739x_poweroff(struct adv739x_data *adv739x) +{ + struct fsl_mxc_lcd_platform_data *plat = adv739x->client->dev.platform_data; + + if (adv739x->enabled != 0) { + dev_dbg(&adv739x->client->dev, "adv739x_poweroff.\n"); + + /* power off the adv739x */ + adv739x_write(adv739x->client, 0x00, 0x1F); + + /* Disable pins */ + if (plat->disable_pins) + plat->disable_pins(); + + adv739x->enabled = 0; + } +} + +static void adv739x_poweron(struct adv739x_data *adv739x) +{ + struct fsl_mxc_lcd_platform_data *plat = adv739x->client->dev.platform_data; + + if (adv739x->enabled == 0) { + dev_dbg(&adv739x->client->dev, "adv739x_poweron.\n"); + + /* Enable pins */ + if (plat->enable_pins) + plat->enable_pins(); + + if (plat->reset) + plat->reset(); + + adv739x->enabled = 1; + adv739x_setmode(adv739x, adv739x->cur_mode); + } +} + +int adv739x_fb_event(struct notifier_block *nb, unsigned long val, void *v) +{ + struct fb_event *event = v; + struct fb_info *fbi = event->info; + struct adv739x_data *adv739x = container_of(nb, struct adv739x_data, nb); + + if (strcmp(event->info->fix.id, adv739x->fbi->fix.id)) + return 0; + + switch (val) { + case FB_EVENT_MODE_CHANGE: + if(strcmp(fbi->mode->name, "BT656-NTSC") == 0) + adv739x->cur_mode = ADV739X_MODE_NTSC; + else if(strcmp(fbi->mode->name, "BT656-PAL") == 0) + adv739x->cur_mode = ADV739X_MODE_PAL; + adv739x_setmode(adv739x, adv739x->cur_mode); + break; + case FB_EVENT_BLANK: + if (*((int *)event->data) == FB_BLANK_UNBLANK) + adv739x_poweron(adv739x); + else + adv739x_poweroff(adv739x); + break; + } + return 0; +} + +static int adv739x_disp_init(struct mxc_dispdrv_handle *disp, + struct mxc_dispdrv_setting *setting) +{ + int ret = 0, i; + struct adv739x_data *adv739x = mxc_dispdrv_getdata(disp); + struct fsl_mxc_lcd_platform_data *plat = adv739x->client->dev.platform_data; + struct fb_videomode *modedb = adv739x_modedb; + int modedb_sz = adv739x_modedb_sz; + static bool inited = false; + + if (inited) + return -EBUSY; + + inited = true; + + setting->dev_id = plat->ipu_id; + setting->disp_id = plat->disp_id; + + ret = fb_find_mode(&setting->fbi->var, setting->fbi, setting->dft_mode_str, + modedb, modedb_sz, NULL, setting->default_bpp); + if (!ret) { + fb_videomode_to_var(&setting->fbi->var, &modedb[0]); + setting->if_fmt = plat->default_ifmt; + } + + INIT_LIST_HEAD(&setting->fbi->modelist); + for (i = 0; i < modedb_sz; i++) { + fb_add_videomode(&modedb[i], + &setting->fbi->modelist); + } + + adv739x->fbi = setting->fbi; + adv739x->enabled = 0; + adv739x->cur_mode = ADV739X_MODE_NTSC; //default mode + + adv739x->pdev = platform_device_register_simple("mxc_adv739x", 0, NULL, 0); + if (IS_ERR(adv739x->pdev)) { + dev_err(&adv739x->client->dev, + "Unable to register adv739x as a platform device\n"); + ret = PTR_ERR(adv739x->pdev); + goto register_pltdev_failed; + } + + /* Claim pins */ + if (plat->get_pins) + if (!plat->get_pins()) { + ret = -EACCES; + goto get_pins_failed; + } + + adv739x->nb.notifier_call = adv739x_fb_event; + ret = fb_register_client(&adv739x->nb); + if (ret < 0) + goto reg_fbclient_failed; + + return ret; + +reg_fbclient_failed: +get_pins_failed: + platform_device_unregister(adv739x->pdev); +register_pltdev_failed: + return ret; +} + +static void adv739x_disp_deinit(struct mxc_dispdrv_handle *disp) +{ + struct adv739x_data *adv739x = mxc_dispdrv_getdata(disp); + struct fsl_mxc_lcd_platform_data *plat = adv739x->client->dev.platform_data; + + if (adv739x->client->irq) + free_irq(adv739x->client->irq, adv739x); + + fb_unregister_client(&adv739x->nb); + + adv739x_poweroff(adv739x); + + /* Release pins */ + if (plat->put_pins) + plat->put_pins(); + + platform_device_unregister(adv739x->pdev); +} + +static struct mxc_dispdrv_driver adv739x_drv = { + .name = DISPDRV_ADV739X, + .init = adv739x_disp_init, + .deinit = adv739x_disp_deinit, +}; + +static int __devinit adv739x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adv739x_data *adv739x; + int ret = 0; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + adv739x = kzalloc(sizeof(struct adv739x_data), GFP_KERNEL); + if (!adv739x) { + ret = -ENOMEM; + goto alloc_failed; + } + + adv739x->client = client; + + adv739x->disp_adv739x = mxc_dispdrv_register(&adv739x_drv); + mxc_dispdrv_setdata(adv739x->disp_adv739x, adv739x); + + i2c_set_clientdata(client, adv739x); + +alloc_failed: + return ret; + +} + +static int __devexit adv739x_remove(struct i2c_client *client) +{ + struct adv739x_data *adv739x = i2c_get_clientdata(client); + + mxc_dispdrv_puthandle(adv739x->disp_adv739x); + mxc_dispdrv_unregister(adv739x->disp_adv739x); + kfree(adv739x); + return 0; +} + +static const struct i2c_device_id adv739x_id[] = { + { "mxc_adv739x", 0 }, +}; + +MODULE_DEVICE_TABLE(i2c, adv739x_id); + +static struct i2c_driver adv739x_i2c_driver = { + .driver = { + .name = "mxc_adv739x", + }, + .probe = adv739x_probe, + .remove = adv739x_remove, + .id_table = adv739x_id, +}; + +static int __init adv739x_i2c_init(void) +{ + return i2c_add_driver(&adv739x_i2c_driver); +} + +static void __exit adv739x_i2c_exit(void) +{ + i2c_del_driver(&adv739x_i2c_driver); +} + +module_init(adv739x_i2c_init); +module_exit(adv739x_i2c_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("ADV739x TV encoder driver"); +MODULE_LICENSE("GPL"); + |