From b96c105cf80e19a8abd0e183189865b9b86b36b1 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Mon, 5 Dec 2011 17:41:05 +0800 Subject: IPU3 FB: merge to fsl imx_2.6.38 branch base to base commit 433c6306fe9455163cff3591b4cf8e2f22bc6cc8 add ipuv3 fb driver. add mxc display driver. add mxc edid driver. add display device driver for lcd/ldb/tve/dvi/sii902x Signed-off-by: Jason Chen --- arch/arm/plat-mxc/include/mach/mxc_edid.h | 52 ++++ drivers/video/mxc/Kconfig | 7 +- drivers/video/mxc/Makefile | 2 +- drivers/video/mxc/ldb.c | 138 ++++++++++- drivers/video/mxc/mxc_dvi.c | 380 ++++++++++++++++++++++++++++++ drivers/video/mxc/mxc_edid.c | 103 ++++++-- drivers/video/mxc/mxc_ipuv3_fb.c | 2 - drivers/video/mxc/mxcfb_sii902x.c | 8 +- drivers/video/mxc/tve.c | 13 +- include/linux/fsl_devices.h | 8 + 10 files changed, 679 insertions(+), 34 deletions(-) create mode 100644 arch/arm/plat-mxc/include/mach/mxc_edid.h create mode 100644 drivers/video/mxc/mxc_dvi.c diff --git a/arch/arm/plat-mxc/include/mach/mxc_edid.h b/arch/arm/plat-mxc/include/mach/mxc_edid.h new file mode 100644 index 00000000000..59688f657a2 --- /dev/null +++ b/arch/arm/plat-mxc/include/mach/mxc_edid.h @@ -0,0 +1,52 @@ +/* + * Copyright 2009-2011 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 + */ + +/*! + * @defgroup Framebuffer Framebuffer Driver for SDC and ADC. + */ + +/*! + * @file mxc_edid.h + * + * @brief MXC EDID tools + * + * @ingroup Framebuffer + */ + +#ifndef MXC_EDID_H +#define MXC_EDID_H + +#define FB_VMODE_ASPECT_4_3 0x10 +#define FB_VMODE_ASPECT_16_9 0x20 + +struct mxc_edid_cfg { + bool cea_underscan; + bool cea_basicaudio; + bool cea_ycbcr444; + bool cea_ycbcr422; + bool hdmi_cap; + + /*VSD*/ + bool vsd_dc_48bit; + bool vsd_dc_36bit; + bool vsd_dc_30bit; + bool vsd_dc_y444; + bool vsd_dvi_dual; +}; + +int mxc_edid_var_to_vic(struct fb_var_screeninfo *var); +int mxc_edid_mode_to_vic(const struct fb_videomode *mode); +int mxc_edid_read(struct i2c_adapter *adp, unsigned short addr, + unsigned char *edid, struct mxc_edid_cfg *cfg, struct fb_info *fbi); + +#endif diff --git a/drivers/video/mxc/Kconfig b/drivers/video/mxc/Kconfig index bb836c3a63f..053f62b7632 100644 --- a/drivers/video/mxc/Kconfig +++ b/drivers/video/mxc/Kconfig @@ -14,6 +14,11 @@ config FB_MXC If you plan to use the LCD display with your MXC system, say Y here. +config FB_MXC_EDID + depends on FB_MXC && I2C + tristate "MXC EDID support" + default y + config FB_MXC_SYNC_PANEL depends on FB_MXC tristate "Synchronous Panel Framebuffer" @@ -25,7 +30,7 @@ config FB_MXC_TVOUT_TVE depends on MXC_IPU_V3 config FB_MXC_SII902X - depends on FB_MXC_SYNC_PANEL + depends on FB_MXC_SYNC_PANEL && I2C tristate "Si Image SII9022 DVI/HDMI Interface Chip" config FB_MXC_LDB diff --git a/drivers/video/mxc/Makefile b/drivers/video/mxc/Makefile index 4c75e1be2d3..e0d47ed90b4 100644 --- a/drivers/video/mxc/Makefile +++ b/drivers/video/mxc/Makefile @@ -1,5 +1,5 @@ obj-$(CONFIG_FB_MXC_TVOUT_TVE) += tve.o obj-$(CONFIG_FB_MXC_SII902X) += mxcfb_sii902x.o obj-$(CONFIG_FB_MXC_LDB) += ldb.o -obj-$(CONFIG_FB_MODE_HELPERS) += mxc_edid.o +obj-$(CONFIG_FB_MXC_EDID) += mxc_edid.o mxc_dvi.o obj-$(CONFIG_FB_MXC_SYNC_PANEL) += mxc_dispdrv.o mxc_lcdif.o mxc_ipuv3_fb.o diff --git a/drivers/video/mxc/ldb.c b/drivers/video/mxc/ldb.c index 180bf7f6f8d..b3a75a5868c 100644 --- a/drivers/video/mxc/ldb.c +++ b/drivers/video/mxc/ldb.c @@ -297,6 +297,116 @@ int ldb_fb_event(struct notifier_block *nb, unsigned long val, void *v) return 0; } +#define LVDS0_MUX_CTL_MASK (3 << 6) +#define LVDS1_MUX_CTL_MASK (3 << 8) +#define LVDS0_MUX_CTL_OFFS 6 +#define LVDS1_MUX_CTL_OFFS 8 +#define ROUTE_IPU0_DI0 0 +#define ROUTE_IPU0_DI1 1 +#define ROUTE_IPU1_DI0 2 +#define ROUTE_IPU1_DI1 3 +static int ldb_ipu_ldb_route(int ipu, int di, struct ldb_data *ldb) +{ + uint32_t reg; + int mode = ldb->mode; + + reg = readl(ldb->gpr3_reg); + if ((mode == LDB_SPL_DI0) || (mode == LDB_DUL_DI0)) { + reg &= ~(LVDS0_MUX_CTL_MASK | LVDS1_MUX_CTL_MASK); + if (ipu == 0) + reg |= (ROUTE_IPU0_DI0 << LVDS0_MUX_CTL_OFFS) | + (ROUTE_IPU0_DI0 << LVDS1_MUX_CTL_OFFS); + else + reg |= (ROUTE_IPU1_DI0 << LVDS0_MUX_CTL_OFFS) | + (ROUTE_IPU1_DI0 << LVDS1_MUX_CTL_OFFS); + dev_dbg(&ldb->pdev->dev, + "Dual/Split mode both channels route to IPU%d-DI0\n", ipu); + } else if ((mode == LDB_SPL_DI1) || (mode == LDB_DUL_DI1)) { + reg &= ~(LVDS0_MUX_CTL_MASK | LVDS1_MUX_CTL_MASK); + if (ipu == 0) + reg |= (ROUTE_IPU0_DI1 << LVDS0_MUX_CTL_OFFS) | + (ROUTE_IPU0_DI1 << LVDS1_MUX_CTL_OFFS); + else + reg |= (ROUTE_IPU1_DI1 << LVDS0_MUX_CTL_OFFS) | + (ROUTE_IPU1_DI1 << LVDS1_MUX_CTL_OFFS); + dev_dbg(&ldb->pdev->dev, + "Dual/Split mode both channels route to IPU%d-DI1\n", ipu); + } else if (mode == LDB_SIN0) { + reg &= ~LVDS0_MUX_CTL_MASK; + if ((ipu == 0) && (di == 0)) + reg |= ROUTE_IPU0_DI0 << LVDS0_MUX_CTL_OFFS; + else if ((ipu == 0) && (di == 1)) + reg |= ROUTE_IPU0_DI1 << LVDS0_MUX_CTL_OFFS; + else if ((ipu == 1) && (di == 0)) + reg |= ROUTE_IPU1_DI0 << LVDS0_MUX_CTL_OFFS; + else + reg |= ROUTE_IPU1_DI1 << LVDS0_MUX_CTL_OFFS; + dev_dbg(&ldb->pdev->dev, + "Single mode channel 0 route to IPU%d-DI%d\n", ipu, di); + } else if (mode == LDB_SIN1) { + reg &= ~LVDS1_MUX_CTL_MASK; + if ((ipu == 0) && (di == 0)) + reg |= ROUTE_IPU0_DI0 << LVDS1_MUX_CTL_OFFS; + else if ((ipu == 0) && (di == 1)) + reg |= ROUTE_IPU0_DI1 << LVDS1_MUX_CTL_OFFS; + else if ((ipu == 1) && (di == 0)) + reg |= ROUTE_IPU1_DI0 << LVDS1_MUX_CTL_OFFS; + else + reg |= ROUTE_IPU1_DI1 << LVDS1_MUX_CTL_OFFS; + dev_dbg(&ldb->pdev->dev, + "Single mode channel 1 route to IPU%d-DI%d\n", ipu, di); + } else { + static bool first = true; + int channel; + + if (first) { + if (mode == LDB_SEP0) { + reg &= ~LVDS0_MUX_CTL_MASK; + channel = 0; + } else { + reg &= ~LVDS1_MUX_CTL_MASK; + channel = 1; + } + first = false; + } else { + if (mode == LDB_SEP0) { + reg &= ~LVDS1_MUX_CTL_MASK; + channel = 1; + } else { + reg &= ~LVDS0_MUX_CTL_MASK; + channel = 0; + } + } + + if ((ipu == 0) && (di == 0)) { + if (channel == 0) + reg |= ROUTE_IPU0_DI0 << LVDS0_MUX_CTL_OFFS; + else + reg |= ROUTE_IPU0_DI0 << LVDS1_MUX_CTL_OFFS; + } else if ((ipu == 0) && (di == 1)) { + if (channel == 0) + reg |= ROUTE_IPU0_DI1 << LVDS0_MUX_CTL_OFFS; + else + reg |= ROUTE_IPU0_DI1 << LVDS1_MUX_CTL_OFFS; + } else if ((ipu == 1) && (di == 0)) { + if (channel == 0) + reg |= ROUTE_IPU1_DI0 << LVDS0_MUX_CTL_OFFS; + else + reg |= ROUTE_IPU1_DI0 << LVDS1_MUX_CTL_OFFS; + } else { + if (channel == 0) + reg |= ROUTE_IPU1_DI1 << LVDS0_MUX_CTL_OFFS; + else + reg |= ROUTE_IPU1_DI1 << LVDS1_MUX_CTL_OFFS; + } + + dev_dbg(&ldb->pdev->dev, "Separate mode channel %d route to IPU%d-DI%d\n", channel, ipu, di); + } + writel(reg, ldb->gpr3_reg); + + return 0; +} + static int ldb_disp_init(struct mxc_dispdrv_entry *disp) { int ret = 0, i; @@ -430,7 +540,11 @@ static int ldb_disp_init(struct mxc_dispdrv_entry *disp) writel(reg, ldb->control_reg); /* clock setting */ - ldb_clk[6] += setting->disp_id; + if (cpu_is_mx6q() && + ((ldb->mode == LDB_SEP0) || (ldb->mode == LDB_SEP1))) + ldb_clk[6] += lvds_channel; + else + ldb_clk[6] += setting->disp_id; ldb->ldb_di_clk[0] = clk_get(&ldb->pdev->dev, ldb_clk); if (IS_ERR(ldb->ldb_di_clk[0])) { dev_err(&ldb->pdev->dev, "get ldb clk0 failed\n"); @@ -474,8 +588,13 @@ static int ldb_disp_init(struct mxc_dispdrv_entry *disp) return -EINVAL; } - setting->dev_id = plat_data->ipu_id; - setting->disp_id = !plat_data->disp_id; + if (cpu_is_mx6q()) { + setting->dev_id = plat_data->sec_ipu_id; + setting->disp_id = plat_data->sec_disp_id; + } else { + setting->dev_id = plat_data->ipu_id; + setting->disp_id = !plat_data->disp_id; + } /* second output is LVDS0 or LVDS1 */ if (ldb->mode == LDB_SEP0) @@ -507,7 +626,10 @@ static int ldb_disp_init(struct mxc_dispdrv_entry *disp) writel(reg, ldb->control_reg); /* clock setting */ - ldb_clk[6] += setting->disp_id; + if (cpu_is_mx6q()) + ldb_clk[6] += lvds_channel; + else + ldb_clk[6] += setting->disp_id; ldb->ldb_di_clk[1] = clk_get(&ldb->pdev->dev, ldb_clk); if (IS_ERR(ldb->ldb_di_clk[1])) { dev_err(&ldb->pdev->dev, "get ldb clk1 failed\n"); @@ -526,6 +648,14 @@ static int ldb_disp_init(struct mxc_dispdrv_entry *disp) setting_idx = 1; } + if (cpu_is_mx6q()) { + reg = readl(ldb->control_reg); + reg &= ~(LDB_CH0_MODE_MASK | LDB_CH1_MODE_MASK); + reg |= LDB_CH0_MODE_EN_TO_DI0 | LDB_CH1_MODE_EN_TO_DI1; + writel(reg, ldb->control_reg); + ldb_ipu_ldb_route(setting->dev_id, setting->disp_id, ldb); + } + /* * ldb_di0_clk -> ipux_di0_clk * ldb_di1_clk -> ipux_di1_clk diff --git a/drivers/video/mxc/mxc_dvi.c b/drivers/video/mxc/mxc_dvi.c new file mode 100644 index 00000000000..765670c8098 --- /dev/null +++ b/drivers/video/mxc/mxc_dvi.c @@ -0,0 +1,380 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/*! + * @defgroup Framebuffer Framebuffer Driver for SDC and ADC. + */ + +/*! + * @file mxc_dvi.c + * + * @brief MXC DVI driver + * + * @ingroup Framebuffer + */ + +/*! + * Include files + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mxc_dispdrv.h" +#include "../edid.h" + +#define MXC_EDID_LENGTH (EDID_LENGTH*4) + +#define DISPDRV_DVI "dvi" + +struct mxc_dvi_data { + struct i2c_client *client; + struct platform_device *pdev; + struct mxc_dispdrv_entry *disp_dvi; + struct delayed_work det_work; + struct fb_info *fbi; + struct mxc_edid_cfg edid_cfg; + u8 cable_plugin; + u8 edid[MXC_EDID_LENGTH]; + + u32 ipu; + u32 di; + void (*init)(void); + int (*update)(void); + struct regulator *analog_reg; +}; + +static ssize_t mxc_dvi_show_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxc_dvi_data *dvi = dev_get_drvdata(dev); + + if (dvi->cable_plugin == 0) + strcpy(buf, "plugout\n"); + else + strcpy(buf, "plugin\n"); + + return strlen(buf); +} + +static DEVICE_ATTR(cable_state, S_IRUGO, mxc_dvi_show_state, NULL); + +static ssize_t mxc_dvi_show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxc_dvi_data *dvi = dev_get_drvdata(dev); + + strcpy(buf, dvi->fbi->fix.id); + sprintf(buf+strlen(buf), "\n"); + + return strlen(buf); +} + +static DEVICE_ATTR(fb_name, S_IRUGO, mxc_dvi_show_name, NULL); + +static ssize_t mxc_dvi_show_edid(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxc_dvi_data *dvi = dev_get_drvdata(dev); + int i, j, len = 0; + + for (j = 0; j < MXC_EDID_LENGTH/16; j++) { + for (i = 0; i < 16; i++) + len += sprintf(buf+len, "0x%02X ", + dvi->edid[j*16 + i]); + len += sprintf(buf+len, "\n"); + } + + return len; +} + +static DEVICE_ATTR(edid, S_IRUGO, mxc_dvi_show_edid, NULL); + +static void det_worker(struct work_struct *work) +{ + struct delayed_work *delay_work = to_delayed_work(work); + struct mxc_dvi_data *dvi = + container_of(delay_work, struct mxc_dvi_data, det_work); + char event_string[16]; + char *envp[] = { event_string, NULL }; + + /* cable connection changes */ + if (dvi->update()) { + u8 edid_old[MXC_EDID_LENGTH]; + dvi->cable_plugin = 1; + sprintf(event_string, "EVENT=plugin"); + + memcpy(edid_old, dvi->edid, MXC_EDID_LENGTH); + + if (mxc_edid_read(dvi->client->adapter, dvi->client->addr, + dvi->edid, &dvi->edid_cfg, dvi->fbi) < 0) + dev_err(&dvi->client->dev, + "MXC dvi: read edid fail\n"); + else { + if (!memcmp(edid_old, dvi->edid, MXC_EDID_LENGTH)) + dev_info(&dvi->client->dev, + "Sii902x: same edid\n"); + else if (dvi->fbi->monspecs.modedb_len > 0) { + int i; + const struct fb_videomode *mode; + struct fb_videomode m; + + fb_destroy_modelist(&dvi->fbi->modelist); + + for (i = 0; i < dvi->fbi->monspecs.modedb_len; i++) + /*FIXME now we do not support interlaced mode */ + if (!(dvi->fbi->monspecs.modedb[i].vmode & FB_VMODE_INTERLACED)) + fb_add_videomode(&dvi->fbi->monspecs.modedb[i], + &dvi->fbi->modelist); + + fb_var_to_videomode(&m, &dvi->fbi->var); + mode = fb_find_nearest_mode(&m, + &dvi->fbi->modelist); + + fb_videomode_to_var(&dvi->fbi->var, mode); + + dvi->fbi->var.activate |= FB_ACTIVATE_FORCE; + console_lock(); + dvi->fbi->flags |= FBINFO_MISC_USEREVENT; + fb_set_var(dvi->fbi, &dvi->fbi->var); + dvi->fbi->flags &= ~FBINFO_MISC_USEREVENT; + console_unlock(); + } + } + } else { + dvi->cable_plugin = 0; + sprintf(event_string, "EVENT=plugout"); + } + + kobject_uevent_env(&dvi->pdev->dev.kobj, KOBJ_CHANGE, envp); +} + +static irqreturn_t mxc_dvi_detect_handler(int irq, void *data) +{ + struct mxc_dvi_data *dvi = data; + schedule_delayed_work(&(dvi->det_work), msecs_to_jiffies(300)); + return IRQ_HANDLED; +} + +static int dvi_init(struct mxc_dispdrv_entry *disp) +{ + int ret = 0; + struct mxc_dvi_data *dvi = mxc_dispdrv_getdata(disp); + struct mxc_dispdrv_setting *setting = mxc_dispdrv_getsetting(disp); + struct fsl_mxc_dvi_platform_data *plat = dvi->client->dev.platform_data; + + setting->dev_id = dvi->ipu = plat->ipu_id; + setting->disp_id = dvi->di = plat->disp_id; + setting->if_fmt = IPU_PIX_FMT_RGB24; + dvi->fbi = setting->fbi; + dvi->init = plat->init; + dvi->update = plat->update; + + dvi->analog_reg = regulator_get(&dvi->pdev->dev, plat->analog_regulator); + if (!IS_ERR(dvi->analog_reg)) { + regulator_set_voltage(dvi->analog_reg, 2775000, 2775000); + regulator_enable(dvi->analog_reg); + } + + if (dvi->init) + dvi->init(); + + /* get video mode from edid */ + if (!dvi->update) + return -EINVAL; + else { + bool found = false; + + INIT_LIST_HEAD(&dvi->fbi->modelist); + if (dvi->update()) { + dvi->cable_plugin = 1; + /* try to read edid */ + if (mxc_edid_read(dvi->client->adapter, dvi->client->addr, + dvi->edid, &dvi->edid_cfg, dvi->fbi) < 0) + dev_warn(&dvi->client->dev, "Can not read edid\n"); + else if (dvi->fbi->monspecs.modedb_len > 0) { + int i; + const struct fb_videomode *mode; + struct fb_videomode m; + + for (i = 0; i < dvi->fbi->monspecs.modedb_len; i++) { + /*FIXME now we do not support interlaced mode */ + if (!(dvi->fbi->monspecs.modedb[i].vmode + & FB_VMODE_INTERLACED)) + fb_add_videomode( + &dvi->fbi->monspecs.modedb[i], + &dvi->fbi->modelist); + } + + fb_find_mode(&dvi->fbi->var, dvi->fbi, setting->dft_mode_str, + NULL, 0, NULL, setting->default_bpp); + + fb_var_to_videomode(&m, &dvi->fbi->var); + mode = fb_find_nearest_mode(&m, + &dvi->fbi->modelist); + fb_videomode_to_var(&dvi->fbi->var, mode); + found = 1; + } + } else + dvi->cable_plugin = 0; + + if (!found) { + ret = fb_find_mode(&dvi->fbi->var, dvi->fbi, setting->dft_mode_str, + NULL, 0, NULL, setting->default_bpp); + if (!ret) + return -EINVAL; + } + } + + /* cable detection */ + if (dvi->client->irq) { + ret = request_irq(dvi->client->irq, mxc_dvi_detect_handler, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "dvi_det", dvi); + if (ret < 0) { + dev_warn(&dvi->client->dev, + "MXC dvi: cound not request det irq %d\n", + dvi->client->irq); + goto err; + } else { + INIT_DELAYED_WORK(&(dvi->det_work), det_worker); + ret = device_create_file(&dvi->pdev->dev, &dev_attr_fb_name); + if (ret < 0) + dev_warn(&dvi->client->dev, + "MXC dvi: cound not create sys node for fb name\n"); + ret = device_create_file(&dvi->pdev->dev, &dev_attr_cable_state); + if (ret < 0) + dev_warn(&dvi->client->dev, + "MXC dvi: cound not create sys node for cable state\n"); + ret = device_create_file(&dvi->pdev->dev, &dev_attr_edid); + if (ret < 0) + dev_warn(&dvi->client->dev, + "MXC dvi: cound not create sys node for edid\n"); + + dev_set_drvdata(&dvi->pdev->dev, dvi); + } + } + +err: + return ret; +} + +static void dvi_deinit(struct mxc_dispdrv_entry *disp) +{ + struct mxc_dvi_data *dvi = mxc_dispdrv_getdata(disp); + + if (!IS_ERR(dvi->analog_reg)) + regulator_disable(dvi->analog_reg); + + free_irq(dvi->client->irq, dvi); +} + +static struct mxc_dispdrv_driver dvi_drv = { + .name = DISPDRV_DVI, + .init = dvi_init, + .deinit = dvi_deinit, +}; + +static int __devinit mxc_dvi_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mxc_dvi_data *dvi; + int ret = 0; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C)) + return -ENODEV; + + dvi = kzalloc(sizeof(struct mxc_dvi_data), GFP_KERNEL); + if (!dvi) { + ret = -ENOMEM; + goto alloc_failed; + } + + dvi->pdev = platform_device_register_simple("mxc_dvi", 0, NULL, 0); + if (IS_ERR(dvi->pdev)) { + printk(KERN_ERR + "Unable to register MXC DVI as a platform device\n"); + ret = PTR_ERR(dvi->pdev); + goto pdev_reg_failed; + } + + dvi->client = client; + dvi->disp_dvi = mxc_dispdrv_register(&dvi_drv); + mxc_dispdrv_setdata(dvi->disp_dvi, dvi); + + i2c_set_clientdata(client, dvi); + + return ret; + +pdev_reg_failed: + kfree(dvi); +alloc_failed: + return ret; +} + +static int __devexit mxc_dvi_remove(struct i2c_client *client) +{ + struct mxc_dvi_data *dvi = i2c_get_clientdata(client); + + mxc_dispdrv_unregister(dvi->disp_dvi); + platform_device_unregister(dvi->pdev); + kfree(dvi); + return 0; +} + +static const struct i2c_device_id mxc_dvi_id[] = { + { "mxc_dvi", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, mxc_dvi_id); + +static struct i2c_driver mxc_dvi_i2c_driver = { + .driver = { + .name = "mxc_dvi", + }, + .probe = mxc_dvi_probe, + .remove = mxc_dvi_remove, + .id_table = mxc_dvi_id, +}; + +static int __init mxc_dvi_init(void) +{ + return i2c_add_driver(&mxc_dvi_i2c_driver); +} + +static void __exit mxc_dvi_exit(void) +{ + i2c_del_driver(&mxc_dvi_i2c_driver); +} + +module_init(mxc_dvi_init); +module_exit(mxc_dvi_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MXC DVI driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/mxc/mxc_edid.c b/drivers/video/mxc/mxc_edid.c index 0fa5cf97602..85a9063037a 100644 --- a/drivers/video/mxc/mxc_edid.c +++ b/drivers/video/mxc/mxc_edid.c @@ -29,7 +29,7 @@ #include #include #include -#include "mxc_edid.h" +#include #include "../edid.h" #undef DEBUG /* define this for verbose EDID parsing output */ @@ -41,80 +41,120 @@ #endif const struct fb_videomode mxc_cea_mode[64] = { - /* #1: 640x480p@59.94/60Hz */ + /* #1: 640x480p@59.94/60Hz 4:3 */ [1] = { NULL, 60, 640, 480, 39722, 48, 16, 33, 10, 96, 2, 0, - FB_VMODE_NONINTERLACED, 0, + FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0, }, - /* #3: 720x480p@59.94/60Hz */ + /* #2: 720x480p@59.94/60Hz 4:3 */ + [2] = { + NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0, + FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0, + }, + /* #3: 720x480p@59.94/60Hz 16:9 */ [3] = { NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0, - FB_VMODE_NONINTERLACED, 0, + FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0, }, - /* #4: 1280x720p@59.94/60Hz */ + /* #4: 1280x720p@59.94/60Hz 16:9 */ [4] = { NULL, 60, 1280, 720, 13468, 220, 110, 20, 5, 40, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - FB_VMODE_NONINTERLACED, 0, + FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0 }, - /* #5: 1920x1080i@59.94/60Hz */ + /* #5: 1920x1080i@59.94/60Hz 16:9 */ [5] = { NULL, 60, 1920, 1080, 13763, 148, 88, 15, 2, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - FB_VMODE_INTERLACED, 0, + FB_VMODE_INTERLACED | FB_VMODE_ASPECT_16_9, 0, + }, + /* #6: 720(1440)x480iH@59.94/60Hz 4:3 */ + [6] = { + NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0, + FB_VMODE_INTERLACED | FB_VMODE_ASPECT_4_3, 0, }, - /* #7: 720(1440)x480iH@59.94/60Hz */ + /* #7: 720(1440)x480iH@59.94/60Hz 16:9 */ [7] = { NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0, - FB_VMODE_INTERLACED, 0, + FB_VMODE_INTERLACED | FB_VMODE_ASPECT_16_9, 0, }, - /* #9: 720(1440)x240pH@59.94/60Hz */ + /* #8: 720(1440)x240pH@59.94/60Hz 4:3 */ + [8] = { + NULL, 60, 1440, 240, 18554, 114, 38, 16, 4, 124, 3, 0, + FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0, + }, + /* #9: 720(1440)x240pH@59.94/60Hz 16:9 */ [9] = { NULL, 60, 1440, 240, 18554, 114, 38, 16, 4, 124, 3, 0, - FB_VMODE_NONINTERLACED, 0, + FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0, }, - /* #16: 1920x1080p@60Hz */ + /* #16: 1920x1080p@60Hz 16:9 */ [16] = { NULL, 60, 1920, 1080, 6734, 148, 88, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - FB_VMODE_NONINTERLACED, 0, + FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0, + }, + /* #17: 720x576pH@50Hz 4:3 */ + [17] = { + NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0, + FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0, }, - /* #18: 720x576pH@50Hz */ + /* #18: 720x576pH@50Hz 16:9 */ [18] = { NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0, - FB_VMODE_NONINTERLACED, 0, + FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0, }, /* #19: 1280x720p@50Hz */ [19] = { NULL, 50, 1280, 720, 13468, 220, 440, 20, 5, 40, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - FB_VMODE_NONINTERLACED, 0, + FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0, }, /* #20: 1920x1080i@50Hz */ [20] = { NULL, 50, 1920, 1080, 13480, 148, 528, 15, 5, 528, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - FB_VMODE_INTERLACED, 0, + FB_VMODE_INTERLACED | FB_VMODE_ASPECT_16_9, 0, }, /* #31: 1920x1080p@50Hz */ [31] = { NULL, 50, 1920, 1080, 6734, 148, 528, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - FB_VMODE_NONINTERLACED, 0, + FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0, }, /* #32: 1920x1080p@23.98/24Hz */ [32] = { NULL, 24, 1920, 1080, 13468, 148, 638, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, - FB_VMODE_NONINTERLACED, 0, + FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_16_9, 0, }, /* #35: (2880)x480p4x@59.94/60Hz */ [35] = { NULL, 60, 2880, 480, 9250, 240, 64, 30, 9, 248, 6, 0, - FB_VMODE_NONINTERLACED, 0, + FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0, }, }; +/* + * We have a special version of fb_mode_is_equal that ignores + * pixclock, since for many CEA modes, 2 frequencies are supported + * e.g. 640x480 @ 60Hz or 59.94Hz + */ +int mxc_edid_fb_mode_is_equal(const struct fb_videomode *mode1, + const struct fb_videomode *mode2) +{ + return (mode1->xres == mode2->xres && + mode1->yres == mode2->yres && + mode1->hsync_len == mode2->hsync_len && + mode1->vsync_len == mode2->vsync_len && + mode1->left_margin == mode2->left_margin && + mode1->right_margin == mode2->right_margin && + mode1->upper_margin == mode2->upper_margin && + mode1->lower_margin == mode2->lower_margin && + mode1->sync == mode2->sync && + mode1->vmode == mode2->vmode); +} + static void get_detailed_timing(unsigned char *block, struct fb_videomode *mode) { @@ -399,7 +439,7 @@ int mxc_edid_var_to_vic(struct fb_var_screeninfo *var) for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) { fb_var_to_videomode(&m, var); - if (fb_mode_is_equal(&m, &mxc_cea_mode[i])) + if (mxc_edid_fb_mode_is_equal(&m, &mxc_cea_mode[i])) break; } @@ -408,8 +448,25 @@ int mxc_edid_var_to_vic(struct fb_var_screeninfo *var) return i; } + EXPORT_SYMBOL(mxc_edid_var_to_vic); +int mxc_edid_mode_to_vic(const struct fb_videomode *mode) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) { + if (mxc_edid_fb_mode_is_equal(mode, &mxc_cea_mode[i])) + break; + } + + if (i == ARRAY_SIZE(mxc_cea_mode)) + return 0; + + return i; +} +EXPORT_SYMBOL(mxc_edid_mode_to_vic); + /* make sure edid has 512 bytes*/ int mxc_edid_read(struct i2c_adapter *adp, unsigned short addr, unsigned char *edid, struct mxc_edid_cfg *cfg, struct fb_info *fbi) diff --git a/drivers/video/mxc/mxc_ipuv3_fb.c b/drivers/video/mxc/mxc_ipuv3_fb.c index 260b41a3ddf..63807917350 100644 --- a/drivers/video/mxc/mxc_ipuv3_fb.c +++ b/drivers/video/mxc/mxc_ipuv3_fb.c @@ -1787,8 +1787,6 @@ static int mxcfb_register(struct fb_info *fbi) ret = fb_set_var(fbi, &fbi->var); fbi->flags &= ~FBINFO_MISC_USEREVENT; console_unlock(); - if (ret < 0) - goto err2; if (mxcfbi->next_blank == FB_BLANK_UNBLANK) { console_lock(); diff --git a/drivers/video/mxc/mxcfb_sii902x.c b/drivers/video/mxc/mxcfb_sii902x.c index 17ce8fca68b..f626f649f9c 100644 --- a/drivers/video/mxc/mxcfb_sii902x.c +++ b/drivers/video/mxc/mxcfb_sii902x.c @@ -50,7 +50,7 @@ #include #include #include -#include "mxc_edid.h" +#include #include "mxc_dispdrv.h" #define DISPDRV_SII "hdmi" @@ -671,7 +671,11 @@ static void sii902x_setup(struct sii902x_data *sii902x, struct fb_info *fbi) /* reg 0x0a: set output format to RGB */ sii902x->tpivmode[2] = 0x00; - if (fbi->var.xres/16 == fbi->var.yres/9) + if (fbi->var.vmode & FB_VMODE_ASPECT_16_9) + sii902x->aspect_ratio = VMD_ASPECT_RATIO_16x9; + else if (fbi->var.vmode & FB_VMODE_ASPECT_4_3) + sii902x->aspect_ratio = VMD_ASPECT_RATIO_4x3; + else if (fbi->var.xres/16 == fbi->var.yres/9) sii902x->aspect_ratio = VMD_ASPECT_RATIO_16x9; else sii902x->aspect_ratio = VMD_ASPECT_RATIO_4x3; diff --git a/drivers/video/mxc/tve.c b/drivers/video/mxc/tve.c index 35b100349fe..98b506d3730 100644 --- a/drivers/video/mxc/tve.c +++ b/drivers/video/mxc/tve.c @@ -38,7 +38,6 @@ #include #include #include -#include #include "mxc_dispdrv.h" #define TVE_ENABLE (1UL) @@ -1105,6 +1104,18 @@ static int tve_drv_init(struct mxc_dispdrv_entry *disp, bool vga) tve->reg_fields = &tve_reg_fields_v2; } + /* adjust video mode for mx37 */ + if (cpu_is_mx37()) { + video_modes_tve[0].left_margin = 121; + video_modes_tve[0].right_margin = 16; + video_modes_tve[0].upper_margin = 17; + video_modes_tve[0].lower_margin = 5; + video_modes_tve[1].left_margin = 131; + video_modes_tve[1].right_margin = 12; + video_modes_tve[1].upper_margin = 21; + video_modes_tve[1].lower_margin = 3; + } + if (vga && cpu_is_mx53()) { setting->if_fmt = IPU_PIX_FMT_GBR24; modedb = video_modes_vga; diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index cb624eaff19..b342652135f 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h @@ -316,6 +316,14 @@ struct fsl_mxc_lcd_platform_data { int disp_id; }; +struct fsl_mxc_dvi_platform_data { + void (*init) (void); + int (*update) (void); + char *analog_regulator; + int ipu_id; + int disp_id; +}; + struct fsl_mxc_ldb_platform_data { char *lvds_bg_reg; u32 ext_ref; -- cgit v1.2.3