From eb422cf546b00683c4cf209ee135f5c99bf76fa4 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Mon, 28 Nov 2011 11:49:16 +0800 Subject: imx6q: change FORCE_MAX_ZONEORDER to 14 Signed-off-by: Jason Chen --- arch/arm/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 0d9bfb600e9..106422c1b98 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1701,6 +1701,7 @@ config FORCE_MAX_ZONEORDER range 11 64 if ARCH_SHMOBILE default "9" if SA1111 default "14" if ARCH_MX5 + default "14" if SOC_IMX6Q default "11" help The kernel memory allocator divides physically contiguous memory -- cgit v1.2.3 From 29f41b992de42841d79a3617e86b0b0edb5f42b6 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Mon, 28 Nov 2011 11:48:17 +0800 Subject: imx6q-sabrelite: change DEFAULT_CONSISTENT_DMA_SIZE to 64M Signed-off-by: Jason Chen --- arch/arm/mach-imx/mach-imx6q.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c index e026dd17594..44587dea2cc 100644 --- a/arch/arm/mach-imx/mach-imx6q.c +++ b/arch/arm/mach-imx/mach-imx6q.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -121,6 +122,8 @@ static void __init imx6q_map_io(void) imx_scu_map_io(); imx6q_clock_map_io(); imx6q_iomux_map_io(); + + init_consistent_dma_size(SZ_64M); } static int __init imx6q_gpio_add_irq_domain(struct device_node *np, -- cgit v1.2.3 From 8bc98972c8aaa80e50d29478fc8bfa45d4359083 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Tue, 6 Dec 2011 12:46:00 +0800 Subject: IPU3: merge to fsl imx_2.6.38 branch base to commit 433c6306fe9455163cff3591b4cf8e2f22bc6cc8 add basic ipu drivers. Signed-off-by: Jason Chen --- arch/arm/plat-mxc/include/mach/ipu-v3.h | 1 + drivers/mxc/Kconfig | 15 ++++++++------- drivers/mxc/ipu3/ipu_calc_stripes_sizes.c | 11 +++++------ drivers/mxc/ipu3/ipu_common.c | 9 +++++++-- drivers/mxc/ipu3/ipu_ic.c | 1 + drivers/mxc/ipu3/ipu_prv.h | 1 + 6 files changed, 23 insertions(+), 15 deletions(-) diff --git a/arch/arm/plat-mxc/include/mach/ipu-v3.h b/arch/arm/plat-mxc/include/mach/ipu-v3.h index 0816ae9b4c4..480b4ea338e 100644 --- a/arch/arm/plat-mxc/include/mach/ipu-v3.h +++ b/arch/arm/plat-mxc/include/mach/ipu-v3.h @@ -125,6 +125,7 @@ typedef union { struct { uint32_t csi; uint32_t mipi_id; + uint32_t mipi_vc; bool mipi_en; bool interlaced; } csi_mem; diff --git a/drivers/mxc/Kconfig b/drivers/mxc/Kconfig index d2b05814832..8b4e6ccd7f7 100644 --- a/drivers/mxc/Kconfig +++ b/drivers/mxc/Kconfig @@ -6,13 +6,14 @@ menu "MXC support drivers" config MXC_IPU bool "Image Processing Unit Driver" - depends on !ARCH_MX21 - depends on !ARCH_MX27 - depends on !ARCH_MX25 - select MXC_IPU_V1 if !ARCH_MX37 && !ARCH_MX5 - select MXC_IPU_V3 if ARCH_MX37 || ARCH_MX5 - select MXC_IPU_V3D if ARCH_MX37 - select MXC_IPU_V3EX if ARCH_MX5 + depends on !SOC_MX21 + depends on !SOC_MX27 + depends on !SOC_MX25 + select MXC_IPU_V1 if !SOC_MX37 && !SOC_MX5 && !SOC_IMX6Q + select MXC_IPU_V3 if SOC_MX37 || SOC_MX5 || SOC_IMX6Q + select MXC_IPU_V3D if SOC_MX37 + select MXC_IPU_V3EX if SOC_MX5 + select MXC_IPU_V3H if SOC_IMX6Q help If you plan to use the Image Processing unit, say Y here. IPU is needed by Framebuffer and V4L2 drivers. diff --git a/drivers/mxc/ipu3/ipu_calc_stripes_sizes.c b/drivers/mxc/ipu3/ipu_calc_stripes_sizes.c index 1e05cc5808b..aa9fdaf27cd 100644 --- a/drivers/mxc/ipu3/ipu_calc_stripes_sizes.c +++ b/drivers/mxc/ipu3/ipu_calc_stripes_sizes.c @@ -52,8 +52,8 @@ static u32 truncate(u32 up, /* 0: down; else: up */ return d; } -/*static unsigned int f_calc(unsigned int pfs, unsigned int bpp, unsigned int *write) -{[> return input_f <] +static unsigned int f_calc(unsigned int pfs, unsigned int bpp, unsigned int *write) +{/* return input_f */ unsigned int f_calculated = 0; switch (pfs) { case IPU_PIX_FMT_YVU422P: @@ -129,7 +129,7 @@ static unsigned int m_calc(unsigned int pfs) } return m_calculated; -}*/ +} /* Stripe parameters calculator */ @@ -214,14 +214,14 @@ int ipu_calc_stripes_sizes(const unsigned int input_frame_width, /* M, F calculations */ /* read back pfs from params */ - input_f = 16; + input_f = f_calc(input_pixelformat, 0, NULL); input_m = 16; /* BPP should be used in the out_F calc */ /* Temporarily not used */ /* out_F = F_calc(idmac->pfs, idmac->bpp, NULL); */ output_f = 16; - output_m = 16; + output_m = m_calc(output_pixelformat); if ((input_frame_width < 4) || (output_frame_width < 4)) @@ -370,7 +370,6 @@ int ipu_calc_stripes_sizes(const unsigned int input_frame_width, left->output_column = 0; right->output_column = onw; } - return status; } EXPORT_SYMBOL(ipu_calc_stripes_sizes); diff --git a/drivers/mxc/ipu3/ipu_common.c b/drivers/mxc/ipu3/ipu_common.c index 014125921c8..4a7ebc02548 100644 --- a/drivers/mxc/ipu3/ipu_common.c +++ b/drivers/mxc/ipu3/ipu_common.c @@ -662,8 +662,10 @@ int32_t ipu_init_channel(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel if (params->csi_mem.mipi_en) { ipu_conf |= (1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + params->csi_mem.csi)); - _ipu_smfc_init(ipu, channel, params->csi_mem.mipi_id, + _ipu_smfc_init(ipu, channel, params->csi_mem.mipi_vc, params->csi_mem.csi); + _ipu_csi_set_mipi_di(ipu, params->csi_mem.mipi_vc, + params->csi_mem.mipi_id, params->csi_mem.csi); } else { ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + params->csi_mem.csi)); @@ -2316,6 +2318,7 @@ int32_t ipu_enable_csi(struct ipu_soc *ipu, uint32_t csi) return -EINVAL; } + _ipu_get(ipu); _ipu_lock(ipu); ipu->csi_use_count[csi]++; @@ -2327,6 +2330,7 @@ int32_t ipu_enable_csi(struct ipu_soc *ipu, uint32_t csi) ipu_cm_write(ipu, reg | IPU_CONF_CSI1_EN, IPU_CONF); } _ipu_unlock(ipu); + _ipu_put(ipu); return 0; } EXPORT_SYMBOL(ipu_enable_csi); @@ -2348,7 +2352,7 @@ int32_t ipu_disable_csi(struct ipu_soc *ipu, uint32_t csi) dev_err(ipu->dev, "Wrong csi num_%d\n", csi); return -EINVAL; } - + _ipu_get(ipu); _ipu_lock(ipu); ipu->csi_use_count[csi]--; if (ipu->csi_use_count[csi] == 0) { @@ -2359,6 +2363,7 @@ int32_t ipu_disable_csi(struct ipu_soc *ipu, uint32_t csi) ipu_cm_write(ipu, reg & ~IPU_CONF_CSI1_EN, IPU_CONF); } _ipu_unlock(ipu); + _ipu_put(ipu); return 0; } EXPORT_SYMBOL(ipu_disable_csi); diff --git a/drivers/mxc/ipu3/ipu_ic.c b/drivers/mxc/ipu3/ipu_ic.c index 7387e518deb..c34e65e1317 100644 --- a/drivers/mxc/ipu3/ipu_ic.c +++ b/drivers/mxc/ipu3/ipu_ic.c @@ -18,6 +18,7 @@ * * @ingroup IPU */ +#include #include #include #include diff --git a/drivers/mxc/ipu3/ipu_prv.h b/drivers/mxc/ipu3/ipu_prv.h index 7c803a1693d..42060c954a1 100644 --- a/drivers/mxc/ipu3/ipu_prv.h +++ b/drivers/mxc/ipu3/ipu_prv.h @@ -319,6 +319,7 @@ int _ipu_ic_idma_init(struct ipu_soc *ipu, int dma_chan, uint16_t width, uint16_ int burst_size, ipu_rotate_mode_t rot); void _ipu_vdi_toggle_top_field_man(struct ipu_soc *ipu); int _ipu_csi_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t csi); +int _ipu_csi_set_mipi_di(struct ipu_soc *ipu, uint32_t num, uint32_t di_val, uint32_t csi); void ipu_csi_set_test_generator(struct ipu_soc *ipu, bool active, uint32_t r_value, uint32_t g_value, uint32_t b_value, uint32_t pix_clk, uint32_t csi); -- cgit v1.2.3 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 From 987f59611acf5270c7e65eca5ab697c662a575ad Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Thu, 24 Nov 2011 10:53:01 +0800 Subject: MXC HDMI: porting to fsl imx_2.6.38 branch base to base commit 433c6306fe9455163cff3591b4cf8e2f22bc6cc8 add mfd mxc hdmi core add mxc hdmi Signed-off-by: Jason Chen --- arch/arm/plat-mxc/include/mach/mxc_hdmi.h | 1028 +++++++++++++++ drivers/mfd/Kconfig | 8 + drivers/mfd/Makefile | 1 + drivers/mfd/mxc-hdmi-core.c | 538 ++++++++ drivers/video/Kconfig | 7 + drivers/video/Makefile | 1 + drivers/video/mxc_hdmi.c | 1968 +++++++++++++++++++++++++++++ include/linux/fsl_devices.h | 13 + include/linux/mfd/mxc-hdmi-core.h | 39 + 9 files changed, 3603 insertions(+) create mode 100644 arch/arm/plat-mxc/include/mach/mxc_hdmi.h create mode 100644 drivers/mfd/mxc-hdmi-core.c create mode 100644 drivers/video/mxc_hdmi.c create mode 100644 include/linux/mfd/mxc-hdmi-core.h diff --git a/arch/arm/plat-mxc/include/mach/mxc_hdmi.h b/arch/arm/plat-mxc/include/mach/mxc_hdmi.h new file mode 100644 index 00000000000..a7f22de9b40 --- /dev/null +++ b/arch/arm/plat-mxc/include/mach/mxc_hdmi.h @@ -0,0 +1,1028 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __MXC_HDMI_H__ +#define __MXC_HDMI_H__ + +/* + * Hdmi controller registers + */ + +/* Identification Registers */ +#define HDMI_DESIGN_ID 0x0000 +#define HDMI_REVISION_ID 0x0001 +#define HDMI_PRODUCT_ID0 0x0002 +#define HDMI_PRODUCT_ID1 0x0003 +#define HDMI_CONFIG0_ID 0x0004 +#define HDMI_CONFIG1_ID 0x0005 +#define HDMI_CONFIG2_ID 0x0006 +#define HDMI_CONFIG3_ID 0x0007 + +/* Interrupt Registers */ +#define HDMI_IH_FC_STAT0 0x0100 +#define HDMI_IH_FC_STAT1 0x0101 +#define HDMI_IH_FC_STAT2 0x0102 +#define HDMI_IH_AS_STAT0 0x0103 +#define HDMI_IH_PHY_STAT0 0x0104 +#define HDMI_IH_I2CM_STAT0 0x0105 +#define HDMI_IH_CEC_STAT0 0x0106 +#define HDMI_IH_VP_STAT0 0x0107 +#define HDMI_IH_I2CMPHY_STAT0 0x0108 +#define HDMI_IH_AHBDMAAUD_STAT0 0x0109 + +#define HDMI_IH_MUTE_FC_STAT0 0x0180 +#define HDMI_IH_MUTE_FC_STAT1 0x0181 +#define HDMI_IH_MUTE_FC_STAT2 0x0182 +#define HDMI_IH_MUTE_AS_STAT0 0x0183 +#define HDMI_IH_MUTE_PHY_STAT0 0x0184 +#define HDMI_IH_MUTE_I2CM_STAT0 0x0185 +#define HDMI_IH_MUTE_CEC_STAT0 0x0186 +#define HDMI_IH_MUTE_VP_STAT0 0x0187 +#define HDMI_IH_MUTE_I2CMPHY_STAT0 0x0188 +#define HDMI_IH_MUTE_AHBDMAAUD_STAT0 0x0189 +#define HDMI_IH_MUTE 0x01FF + +/* Video Sample Registers */ +#define HDMI_TX_INVID0 0x0200 +#define HDMI_TX_INSTUFFING 0x0201 +#define HDMI_TX_GYDATA0 0x0202 +#define HDMI_TX_GYDATA1 0x0203 +#define HDMI_TX_RCRDATA0 0x0204 +#define HDMI_TX_RCRDATA1 0x0205 +#define HDMI_TX_BCBDATA0 0x0206 +#define HDMI_TX_BCBDATA1 0x0207 + +/* Video Packetizer Registers */ +#define HDMI_VP_STATUS 0x0800 +#define HDMI_VP_PR_CD 0x0801 +#define HDMI_VP_STUFF 0x0802 +#define HDMI_VP_REMAP 0x0803 +#define HDMI_VP_CONF 0x0804 +#define HDMI_VP_STAT 0x0805 +#define HDMI_VP_INT 0x0806 +#define HDMI_VP_MASK 0x0807 +#define HDMI_VP_POL 0x0808 + +/* Frame Composer Registers */ +#define HDMI_FC_INVIDCONF 0x1000 +#define HDMI_FC_INHACTV0 0x1001 +#define HDMI_FC_INHACTV1 0x1002 +#define HDMI_FC_INHBLANK0 0x1003 +#define HDMI_FC_INHBLANK1 0x1004 +#define HDMI_FC_INVACTV0 0x1005 +#define HDMI_FC_INVACTV1 0x1006 +#define HDMI_FC_INVBLANK 0x1007 +#define HDMI_FC_HSYNCINDELAY0 0x1008 +#define HDMI_FC_HSYNCINDELAY1 0x1009 +#define HDMI_FC_HSYNCINWIDTH0 0x100A +#define HDMI_FC_HSYNCINWIDTH1 0x100B +#define HDMI_FC_VSYNCINDELAY 0x100C +#define HDMI_FC_VSYNCINWIDTH 0x100D +#define HDMI_FC_INFREQ0 0x100E +#define HDMI_FC_INFREQ1 0x100F +#define HDMI_FC_INFREQ2 0x1010 +#define HDMI_FC_CTRLDUR 0x1011 +#define HDMI_FC_EXCTRLDUR 0x1012 +#define HDMI_FC_EXCTRLSPAC 0x1013 +#define HDMI_FC_CH0PREAM 0x1014 +#define HDMI_FC_CH1PREAM 0x1015 +#define HDMI_FC_CH2PREAM 0x1016 +#define HDMI_FC_AVICONF3 0x1017 +#define HDMI_FC_GCP 0x1018 +#define HDMI_FC_AVICONF0 0x1019 +#define HDMI_FC_AVICONF1 0x101A +#define HDMI_FC_AVICONF2 0x101B +#define HDMI_FC_AVIVID 0x101C +#define HDMI_FC_AVIETB0 0x101D +#define HDMI_FC_AVIETB1 0x101E +#define HDMI_FC_AVISBB0 0x101F +#define HDMI_FC_AVISBB1 0x1020 +#define HDMI_FC_AVIELB0 0x1021 +#define HDMI_FC_AVIELB1 0x1022 +#define HDMI_FC_AVISRB0 0x1023 +#define HDMI_FC_AVISRB1 0x1024 +#define HDMI_FC_AUDICONF0 0x1025 +#define HDMI_FC_AUDICONF1 0x1026 +#define HDMI_FC_AUDICONF2 0x1027 +#define HDMI_FC_AUDICONF3 0x1028 +#define HDMI_FC_VSDIEEEID0 0x1029 +#define HDMI_FC_VSDSIZE 0x102A +#define HDMI_FC_VSDIEEEID1 0x1030 +#define HDMI_FC_VSDIEEEID2 0x1031 +#define HDMI_FC_VSDPAYLOAD0 0x1032 +#define HDMI_FC_VSDPAYLOAD1 0x1033 +#define HDMI_FC_VSDPAYLOAD2 0x1034 +#define HDMI_FC_VSDPAYLOAD3 0x1035 +#define HDMI_FC_VSDPAYLOAD4 0x1036 +#define HDMI_FC_VSDPAYLOAD5 0x1037 +#define HDMI_FC_VSDPAYLOAD6 0x1038 +#define HDMI_FC_VSDPAYLOAD7 0x1039 +#define HDMI_FC_VSDPAYLOAD8 0x103A +#define HDMI_FC_VSDPAYLOAD9 0x103B +#define HDMI_FC_VSDPAYLOAD10 0x103C +#define HDMI_FC_VSDPAYLOAD11 0x103D +#define HDMI_FC_VSDPAYLOAD12 0x103E +#define HDMI_FC_VSDPAYLOAD13 0x103F +#define HDMI_FC_VSDPAYLOAD14 0x1040 +#define HDMI_FC_VSDPAYLOAD15 0x1041 +#define HDMI_FC_VSDPAYLOAD16 0x1042 +#define HDMI_FC_VSDPAYLOAD17 0x1043 +#define HDMI_FC_VSDPAYLOAD18 0x1044 +#define HDMI_FC_VSDPAYLOAD19 0x1045 +#define HDMI_FC_VSDPAYLOAD20 0x1046 +#define HDMI_FC_VSDPAYLOAD21 0x1047 +#define HDMI_FC_VSDPAYLOAD22 0x1048 +#define HDMI_FC_VSDPAYLOAD23 0x1049 +#define HDMI_FC_SPDVENDORNAME0 0x104A +#define HDMI_FC_SPDVENDORNAME1 0x104B +#define HDMI_FC_SPDVENDORNAME2 0x104C +#define HDMI_FC_SPDVENDORNAME3 0x104D +#define HDMI_FC_SPDVENDORNAME4 0x104E +#define HDMI_FC_SPDVENDORNAME5 0x104F +#define HDMI_FC_SPDVENDORNAME6 0x1050 +#define HDMI_FC_SPDVENDORNAME7 0x1051 +#define HDMI_FC_SDPPRODUCTNAME0 0x1052 +#define HDMI_FC_SDPPRODUCTNAME1 0x1053 +#define HDMI_FC_SDPPRODUCTNAME2 0x1054 +#define HDMI_FC_SDPPRODUCTNAME3 0x1055 +#define HDMI_FC_SDPPRODUCTNAME4 0x1056 +#define HDMI_FC_SDPPRODUCTNAME5 0x1057 +#define HDMI_FC_SDPPRODUCTNAME6 0x1058 +#define HDMI_FC_SDPPRODUCTNAME7 0x1059 +#define HDMI_FC_SDPPRODUCTNAME8 0x105A +#define HDMI_FC_SDPPRODUCTNAME9 0x105B +#define HDMI_FC_SDPPRODUCTNAME10 0x105C +#define HDMI_FC_SDPPRODUCTNAME11 0x105D +#define HDMI_FC_SDPPRODUCTNAME12 0x105E +#define HDMI_FC_SDPPRODUCTNAME13 0x105F +#define HDMI_FC_SDPPRODUCTNAME14 0x1060 +#define HDMI_FC_SPDPRODUCTNAME15 0x1061 +#define HDMI_FC_SPDDEVICEINF 0x1062 +#define HDMI_FC_AUDSCONF 0x1063 +#define HDMI_FC_AUDSSTAT 0x1064 +#define HDMI_FC_DATACH0FILL 0x1070 +#define HDMI_FC_DATACH1FILL 0x1071 +#define HDMI_FC_DATACH2FILL 0x1072 +#define HDMI_FC_CTRLQHIGH 0x1073 +#define HDMI_FC_CTRLQLOW 0x1074 +#define HDMI_FC_ACP0 0x1075 +#define HDMI_FC_ACP28 0x1076 +#define HDMI_FC_ACP27 0x1077 +#define HDMI_FC_ACP26 0x1078 +#define HDMI_FC_ACP25 0x1079 +#define HDMI_FC_ACP24 0x107A +#define HDMI_FC_ACP23 0x107B +#define HDMI_FC_ACP22 0x107C +#define HDMI_FC_ACP21 0x107D +#define HDMI_FC_ACP20 0x107E +#define HDMI_FC_ACP19 0x107F +#define HDMI_FC_ACP18 0x1080 +#define HDMI_FC_ACP17 0x1081 +#define HDMI_FC_ACP16 0x1082 +#define HDMI_FC_ACP15 0x1083 +#define HDMI_FC_ACP14 0x1084 +#define HDMI_FC_ACP13 0x1085 +#define HDMI_FC_ACP12 0x1086 +#define HDMI_FC_ACP11 0x1087 +#define HDMI_FC_ACP10 0x1088 +#define HDMI_FC_ACP9 0x1089 +#define HDMI_FC_ACP8 0x108A +#define HDMI_FC_ACP7 0x108B +#define HDMI_FC_ACP6 0x108C +#define HDMI_FC_ACP5 0x108D +#define HDMI_FC_ACP4 0x108E +#define HDMI_FC_ACP3 0x108F +#define HDMI_FC_ACP2 0x1090 +#define HDMI_FC_ACP1 0x1091 +#define HDMI_FC_ISCR1_0 0x1092 +#define HDMI_FC_ISCR1_16 0x1093 +#define HDMI_FC_ISCR1_15 0x1094 +#define HDMI_FC_ISCR1_14 0x1095 +#define HDMI_FC_ISCR1_13 0x1096 +#define HDMI_FC_ISCR1_12 0x1097 +#define HDMI_FC_ISCR1_11 0x1098 +#define HDMI_FC_ISCR1_10 0x1099 +#define HDMI_FC_ISCR1_9 0x109A +#define HDMI_FC_ISCR1_8 0x109B +#define HDMI_FC_ISCR1_7 0x109C +#define HDMI_FC_ISCR1_6 0x109D +#define HDMI_FC_ISCR1_5 0x109E +#define HDMI_FC_ISCR1_4 0x109F +#define HDMI_FC_ISCR1_3 0x10A0 +#define HDMI_FC_ISCR1_2 0x10A1 +#define HDMI_FC_ISCR1_1 0x10A2 +#define HDMI_FC_ISCR2_15 0x10A3 +#define HDMI_FC_ISCR2_14 0x10A4 +#define HDMI_FC_ISCR2_13 0x10A5 +#define HDMI_FC_ISCR2_12 0x10A6 +#define HDMI_FC_ISCR2_11 0x10A7 +#define HDMI_FC_ISCR2_10 0x10A8 +#define HDMI_FC_ISCR2_9 0x10A9 +#define HDMI_FC_ISCR2_8 0x10AA +#define HDMI_FC_ISCR2_7 0x10AB +#define HDMI_FC_ISCR2_6 0x10AC +#define HDMI_FC_ISCR2_5 0x10AD +#define HDMI_FC_ISCR2_4 0x10AE +#define HDMI_FC_ISCR2_3 0x10AF +#define HDMI_FC_ISCR2_2 0x10B0 +#define HDMI_FC_ISCR2_1 0x10B1 +#define HDMI_FC_ISCR2_0 0x10B2 +#define HDMI_FC_DATAUTO0 0x10B3 +#define HDMI_FC_DATAUTO1 0x10B4 +#define HDMI_FC_DATAUTO2 0x10B5 +#define HDMI_FC_DATMAN 0x10B6 +#define HDMI_FC_DATAUTO3 0x10B7 +#define HDMI_FC_RDRB0 0x10B8 +#define HDMI_FC_RDRB1 0x10B9 +#define HDMI_FC_RDRB2 0x10BA +#define HDMI_FC_RDRB3 0x10BB +#define HDMI_FC_RDRB4 0x10BC +#define HDMI_FC_RDRB5 0x10BD +#define HDMI_FC_RDRB6 0x10BE +#define HDMI_FC_RDRB7 0x10BF +#define HDMI_FC_STAT0 0x10D0 +#define HDMI_FC_INT0 0x10D1 +#define HDMI_FC_MASK0 0x10D2 +#define HDMI_FC_POL0 0x10D3 +#define HDMI_FC_STAT1 0x10D4 +#define HDMI_FC_INT1 0x10D5 +#define HDMI_FC_MASK1 0x10D6 +#define HDMI_FC_POL1 0x10D7 +#define HDMI_FC_STAT2 0x10D8 +#define HDMI_FC_INT2 0x10D9 +#define HDMI_FC_MASK2 0x10DA +#define HDMI_FC_POL2 0x10DB +#define HDMI_FC_PRCONF 0x10E0 + +#define HDMI_FC_GMD_STAT 0x1100 +#define HDMI_FC_GMD_EN 0x1101 +#define HDMI_FC_GMD_UP 0x1102 +#define HDMI_FC_GMD_CONF 0x1103 +#define HDMI_FC_GMD_HB 0x1104 +#define HDMI_FC_GMD_PB0 0x1105 +#define HDMI_FC_GMD_PB1 0x1106 +#define HDMI_FC_GMD_PB2 0x1107 +#define HDMI_FC_GMD_PB3 0x1108 +#define HDMI_FC_GMD_PB4 0x1109 +#define HDMI_FC_GMD_PB5 0x110A +#define HDMI_FC_GMD_PB6 0x110B +#define HDMI_FC_GMD_PB7 0x110C +#define HDMI_FC_GMD_PB8 0x110D +#define HDMI_FC_GMD_PB9 0x110E +#define HDMI_FC_GMD_PB10 0x110F +#define HDMI_FC_GMD_PB11 0x1110 +#define HDMI_FC_GMD_PB12 0x1111 +#define HDMI_FC_GMD_PB13 0x1112 +#define HDMI_FC_GMD_PB14 0x1113 +#define HDMI_FC_GMD_PB15 0x1114 +#define HDMI_FC_GMD_PB16 0x1115 +#define HDMI_FC_GMD_PB17 0x1116 +#define HDMI_FC_GMD_PB18 0x1117 +#define HDMI_FC_GMD_PB19 0x1118 +#define HDMI_FC_GMD_PB20 0x1119 +#define HDMI_FC_GMD_PB21 0x111A +#define HDMI_FC_GMD_PB22 0x111B +#define HDMI_FC_GMD_PB23 0x111C +#define HDMI_FC_GMD_PB24 0x111D +#define HDMI_FC_GMD_PB25 0x111E +#define HDMI_FC_GMD_PB26 0x111F +#define HDMI_FC_GMD_PB27 0x1120 + +#define HDMI_FC_DBGFORCE 0x1200 +#define HDMI_FC_DBGAUD0CH0 0x1201 +#define HDMI_FC_DBGAUD1CH0 0x1202 +#define HDMI_FC_DBGAUD2CH0 0x1203 +#define HDMI_FC_DBGAUD0CH1 0x1204 +#define HDMI_FC_DBGAUD1CH1 0x1205 +#define HDMI_FC_DBGAUD2CH1 0x1206 +#define HDMI_FC_DBGAUD0CH2 0x1207 +#define HDMI_FC_DBGAUD1CH2 0x1208 +#define HDMI_FC_DBGAUD2CH2 0x1209 +#define HDMI_FC_DBGAUD0CH3 0x120A +#define HDMI_FC_DBGAUD1CH3 0x120B +#define HDMI_FC_DBGAUD2CH3 0x120C +#define HDMI_FC_DBGAUD0CH4 0x120D +#define HDMI_FC_DBGAUD1CH4 0x120E +#define HDMI_FC_DBGAUD2CH4 0x120F +#define HDMI_FC_DBGAUD0CH5 0x1210 +#define HDMI_FC_DBGAUD1CH5 0x1211 +#define HDMI_FC_DBGAUD2CH5 0x1212 +#define HDMI_FC_DBGAUD0CH6 0x1213 +#define HDMI_FC_DBGAUD1CH6 0x1214 +#define HDMI_FC_DBGAUD2CH6 0x1215 +#define HDMI_FC_DBGAUD0CH7 0x1216 +#define HDMI_FC_DBGAUD1CH7 0x1217 +#define HDMI_FC_DBGAUD2CH7 0x1218 +#define HDMI_FC_DBGTMDS0 0x1219 +#define HDMI_FC_DBGTMDS1 0x121A +#define HDMI_FC_DBGTMDS2 0x121B + +/* HDMI Source PHY Registers */ +#define HDMI_PHY_CONF0 0x3000 +#define HDMI_PHY_TST0 0x3001 +#define HDMI_PHY_TST1 0x3002 +#define HDMI_PHY_TST2 0x3003 +#define HDMI_PHY_STAT0 0x3004 +#define HDMI_PHY_INT0 0x3005 +#define HDMI_PHY_MASK0 0x3006 +#define HDMI_PHY_POL0 0x3007 + +/* HDMI Master PHY Registers */ +#define HDMI_PHY_I2CM_SLAVE_ADDR 0x3020 +#define HDMI_PHY_I2CM_ADDRESS_ADDR 0x3021 +#define HDMI_PHY_I2CM_DATAO_1_ADDR 0x3022 +#define HDMI_PHY_I2CM_DATAO_0_ADDR 0x3023 +#define HDMI_PHY_I2CM_DATAI_1_ADDR 0x3024 +#define HDMI_PHY_I2CM_DATAI_0_ADDR 0x3025 +#define HDMI_PHY_I2CM_OPERATION_ADDR 0x3026 +#define HDMI_PHY_I2CM_INT_ADDR 0x3027 +#define HDMI_PHY_I2CM_CTLINT_ADDR 0x3028 +#define HDMI_PHY_I2CM_DIV_ADDR 0x3029 +#define HDMI_PHY_I2CM_SOFTRSTZ_ADDR 0x302a +#define HDMI_PHY_I2CM_SS_SCL_HCNT_1_ADDR 0x302b +#define HDMI_PHY_I2CM_SS_SCL_HCNT_0_ADDR 0x302c +#define HDMI_PHY_I2CM_SS_SCL_LCNT_1_ADDR 0x302d +#define HDMI_PHY_I2CM_SS_SCL_LCNT_0_ADDR 0x302e +#define HDMI_PHY_I2CM_FS_SCL_HCNT_1_ADDR 0x302f +#define HDMI_PHY_I2CM_FS_SCL_HCNT_0_ADDR 0x3030 +#define HDMI_PHY_I2CM_FS_SCL_LCNT_1_ADDR 0x3031 +#define HDMI_PHY_I2CM_FS_SCL_LCNT_0_ADDR 0x3032 + +/* Audio Sampler Registers */ +#define HDMI_AUD_CONF0 0x3100 +#define HDMI_AUD_CONF1 0x3101 +#define HDMI_AUD_INT 0x3102 +#define HDMI_AUD_CONF2 0x3103 +#define HDMI_AUD_N1 0x3200 +#define HDMI_AUD_N2 0x3201 +#define HDMI_AUD_N3 0x3202 +#define HDMI_AUD_CTS1 0x3203 +#define HDMI_AUD_CTS2 0x3204 +#define HDMI_AUD_CTS3 0x3205 +#define HDMI_AUD_INPUTCLKFS 0x3206 +#define HDMI_AUD_CONF0_HBR 0x3400 +#define HDMI_AUD_HBR_STATUS 0x3401 +#define HDMI_AUD_HBR_INT 0x3402 +#define HDMI_AUD_HBR_POL 0x3403 +#define HDMI_AUD_HBR_MASK 0x3404 + +/* Generic Parallel Audio Interface Registers */ +/* Not used as GPAUD interface is not enabled in hw */ +#define HDMI_GP_CONF0 0x3500 +#define HDMI_GP_CONF1 0x3501 +#define HDMI_GP_CONF2 0x3502 +#define HDMI_GP_STAT 0x3503 +#define HDMI_GP_INT 0x3504 +#define HDMI_GP_MASK 0x3505 +#define HDMI_GP_POL 0x3506 + +/* Audio DMA Registers */ +#define HDMI_AHB_DMA_CONF0 0x3600 +#define HDMI_AHB_DMA_START 0x3601 +#define HDMI_AHB_DMA_STOP 0x3602 +#define HDMI_AHB_DMA_THRSLD 0x3603 +#define HDMI_AHB_DMA_STRADDR0 0x3604 +#define HDMI_AHB_DMA_STRADDR1 0x3605 +#define HDMI_AHB_DMA_STRADDR2 0x3606 +#define HDMI_AHB_DMA_STRADDR3 0x3607 +#define HDMI_AHB_DMA_STPADDR0 0x3608 +#define HDMI_AHB_DMA_STPADDR1 0x3609 +#define HDMI_AHB_DMA_STPADDR2 0x360a +#define HDMI_AHB_DMA_STPADDR3 0x360b +#define HDMI_AHB_DMA_BSTADDR0 0x360c +#define HDMI_AHB_DMA_BSTADDR1 0x360d +#define HDMI_AHB_DMA_BSTADDR2 0x360e +#define HDMI_AHB_DMA_BSTADDR3 0x360f +#define HDMI_AHB_DMA_MBLENGTH0 0x3610 +#define HDMI_AHB_DMA_MBLENGTH1 0x3611 +#define HDMI_AHB_DMA_STAT 0x3612 +#define HDMI_AHB_DMA_INT 0x3613 +#define HDMI_AHB_DMA_MASK 0x3614 +#define HDMI_AHB_DMA_POL 0x3615 +#define HDMI_AHB_DMA_CONF1 0x3616 +#define HDMI_AHB_DMA_BUFFSTAT 0x3617 +#define HDMI_AHB_DMA_BUFFINT 0x3618 +#define HDMI_AHB_DMA_BUFFMASK 0x3619 +#define HDMI_AHB_DMA_BUFFPOL 0x361a + +/* Main Controller Registers */ +#define HDMI_MC_SFRDIV 0x4000 +#define HDMI_MC_CLKDIS 0x4001 +#define HDMI_MC_SWRSTZ 0x4002 +#define HDMI_MC_OPCTRL 0x4003 +#define HDMI_MC_FLOWCTRL 0x4004 +#define HDMI_MC_PHYRSTZ 0x4005 +#define HDMI_MC_LOCKONCLOCK 0x4006 +#define HDMI_MC_HEACPHY_RST 0x4007 + +/* Color Space Converter Registers */ +#define HDMI_CSC_CFG 0x4100 +#define HDMI_CSC_SCALE 0x4101 +#define HDMI_CSC_COEF_A1_MSB 0x4102 +#define HDMI_CSC_COEF_A1_LSB 0x4103 +#define HDMI_CSC_COEF_A2_MSB 0x4104 +#define HDMI_CSC_COEF_A2_LSB 0x4105 +#define HDMI_CSC_COEF_A3_MSB 0x4106 +#define HDMI_CSC_COEF_A3_LSB 0x4107 +#define HDMI_CSC_COEF_A4_MSB 0x4108 +#define HDMI_CSC_COEF_A4_LSB 0x4109 +#define HDMI_CSC_COEF_B1_MSB 0x410A +#define HDMI_CSC_COEF_B1_LSB 0x410B +#define HDMI_CSC_COEF_B2_MSB 0x410C +#define HDMI_CSC_COEF_B2_LSB 0x410D +#define HDMI_CSC_COEF_B3_MSB 0x410E +#define HDMI_CSC_COEF_B3_LSB 0x410F +#define HDMI_CSC_COEF_B4_MSB 0x4110 +#define HDMI_CSC_COEF_B4_LSB 0x4111 +#define HDMI_CSC_COEF_C1_MSB 0x4112 +#define HDMI_CSC_COEF_C1_LSB 0x4113 +#define HDMI_CSC_COEF_C2_MSB 0x4114 +#define HDMI_CSC_COEF_C2_LSB 0x4115 +#define HDMI_CSC_COEF_C3_MSB 0x4116 +#define HDMI_CSC_COEF_C3_LSB 0x4117 +#define HDMI_CSC_COEF_C4_MSB 0x4118 +#define HDMI_CSC_COEF_C4_LSB 0x4119 + +/* HDCP Encryption Engine Registers */ +#define HDMI_A_HDCPCFG0 0x5000 +#define HDMI_A_HDCPCFG1 0x5001 +#define HDMI_A_HDCPOBS0 0x5002 +#define HDMI_A_HDCPOBS1 0x5003 +#define HDMI_A_HDCPOBS2 0x5004 +#define HDMI_A_HDCPOBS3 0x5005 +#define HDMI_A_APIINTCLR 0x5006 +#define HDMI_A_APIINTSTAT 0x5007 +#define HDMI_A_APIINTMSK 0x5008 +#define HDMI_A_VIDPOLCFG 0x5009 +#define HDMI_A_OESSWCFG 0x500A +#define HDMI_A_TIMER1SETUP0 0x500B +#define HDMI_A_TIMER1SETUP1 0x500C +#define HDMI_A_TIMER2SETUP0 0x500D +#define HDMI_A_TIMER2SETUP1 0x500E +#define HDMI_A_100MSCFG 0x500F +#define HDMI_A_2SCFG0 0x5010 +#define HDMI_A_2SCFG1 0x5011 +#define HDMI_A_5SCFG0 0x5012 +#define HDMI_A_5SCFG1 0x5013 +#define HDMI_A_SRMVERLSB 0x5014 +#define HDMI_A_SRMVERMSB 0x5015 +#define HDMI_A_SRMCTRL 0x5016 +#define HDMI_A_SFRSETUP 0x5017 +#define HDMI_A_I2CHSETUP 0x5018 +#define HDMI_A_INTSETUP 0x5019 +#define HDMI_A_PRESETUP 0x501A +#define HDMI_A_SRM_BASE 0x5020 + +/* CEC Engine Registers */ +#define HDMI_CEC_CTRL 0x7D00 +#define HDMI_CEC_STAT 0x7D01 +#define HDMI_CEC_MASK 0x7D02 +#define HDMI_CEC_POLARITY 0x7D03 +#define HDMI_CEC_INT 0x7D04 +#define HDMI_CEC_ADDR_L 0x7D05 +#define HDMI_CEC_ADDR_H 0x7D06 +#define HDMI_CEC_TX_CNT 0x7D07 +#define HDMI_CEC_RX_CNT 0x7D08 +#define HDMI_CEC_TX_DATA0 0x7D10 +#define HDMI_CEC_TX_DATA1 0x7D11 +#define HDMI_CEC_TX_DATA2 0x7D12 +#define HDMI_CEC_TX_DATA3 0x7D13 +#define HDMI_CEC_TX_DATA4 0x7D14 +#define HDMI_CEC_TX_DATA5 0x7D15 +#define HDMI_CEC_TX_DATA6 0x7D16 +#define HDMI_CEC_TX_DATA7 0x7D17 +#define HDMI_CEC_TX_DATA8 0x7D18 +#define HDMI_CEC_TX_DATA9 0x7D19 +#define HDMI_CEC_TX_DATA10 0x7D1a +#define HDMI_CEC_TX_DATA11 0x7D1b +#define HDMI_CEC_TX_DATA12 0x7D1c +#define HDMI_CEC_TX_DATA13 0x7D1d +#define HDMI_CEC_TX_DATA14 0x7D1e +#define HDMI_CEC_TX_DATA15 0x7D1f +#define HDMI_CEC_RX_DATA0 0x7D20 +#define HDMI_CEC_RX_DATA1 0x7D21 +#define HDMI_CEC_RX_DATA2 0x7D22 +#define HDMI_CEC_RX_DATA3 0x7D23 +#define HDMI_CEC_RX_DATA4 0x7D24 +#define HDMI_CEC_RX_DATA5 0x7D25 +#define HDMI_CEC_RX_DATA6 0x7D26 +#define HDMI_CEC_RX_DATA7 0x7D27 +#define HDMI_CEC_RX_DATA8 0x7D28 +#define HDMI_CEC_RX_DATA9 0x7D29 +#define HDMI_CEC_RX_DATA10 0x7D2a +#define HDMI_CEC_RX_DATA11 0x7D2b +#define HDMI_CEC_RX_DATA12 0x7D2c +#define HDMI_CEC_RX_DATA13 0x7D2d +#define HDMI_CEC_RX_DATA14 0x7D2e +#define HDMI_CEC_RX_DATA15 0x7D2f +#define HDMI_CEC_LOCK 0x7D30 +#define HDMI_CEC_WKUPCTRL 0x7D31 + +/* I2C Master Registers (E-DDC) */ +#define HDMI_I2CM_SLAVE 0x7E00 +#define HDMI_I2CMESS 0x7E01 +#define HDMI_I2CM_DATAO 0x7E02 +#define HDMI_I2CM_DATAI 0x7E03 +#define HDMI_I2CM_OPERATION 0x7E04 +#define HDMI_I2CM_INT 0x7E05 +#define HDMI_I2CM_CTLINT 0x7E06 +#define HDMI_I2CM_DIV 0x7E07 +#define HDMI_I2CM_SEGADDR 0x7E08 +#define HDMI_I2CM_SOFTRSTZ 0x7E09 +#define HDMI_I2CM_SEGPTR 0x7E0A +#define HDMI_I2CM_SS_SCL_HCNT_1_ADDR 0x7E0B +#define HDMI_I2CM_SS_SCL_HCNT_0_ADDR 0x7E0C +#define HDMI_I2CM_SS_SCL_LCNT_1_ADDR 0x7E0D +#define HDMI_I2CM_SS_SCL_LCNT_0_ADDR 0x7E0E +#define HDMI_I2CM_FS_SCL_HCNT_1_ADDR 0x7E0F +#define HDMI_I2CM_FS_SCL_HCNT_0_ADDR 0x7E10 +#define HDMI_I2CM_FS_SCL_LCNT_1_ADDR 0x7E11 +#define HDMI_I2CM_FS_SCL_LCNT_0_ADDR 0x7E12 + +/* Random Number Generator Registers (RNG) */ +#define HDMI_RNG_BASE 0x8000 + + +/* + * Register field definitions + */ +enum { +/* IH_PHY_STAT0 field values */ + HDMI_IH_PHY_STAT0_RX_SENSE3 = 0x20, + HDMI_IH_PHY_STAT0_RX_SENSE2 = 0x10, + HDMI_IH_PHY_STAT0_RX_SENSE1 = 0x8, + HDMI_IH_PHY_STAT0_RX_SENSE0 = 0x4, + HDMI_IH_PHY_STAT0_TX_PHY_LOCK = 0x2, + HDMI_IH_PHY_STAT0_HPD = 0x1, + +/* IH_MUTE_I2CMPHY_STAT0 field values */ + HDMI_IH_MUTE_I2CMPHY_STAT0_I2CMPHYDONE = 0x2, + HDMI_IH_MUTE_I2CMPHY_STAT0_I2CMPHYERROR = 0x1, + +/* IH_AHBDMAAUD_STAT0 field values */ + HDMI_IH_AHBDMAAUD_STAT0_ERROR = 0x20, + HDMI_IH_AHBDMAAUD_STAT0_LOST = 0x10, + HDMI_IH_AHBDMAAUD_STAT0_RETRY = 0x08, + HDMI_IH_AHBDMAAUD_STAT0_DONE = 0x04, + HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = 0x02, + HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = 0x01, + +/* IH_MUTE_AHBDMAAUD_STAT0 field values */ + HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = 0x20, + HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = 0x10, + HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = 0x08, + HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = 0x04, + HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = 0x02, + HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = 0x01, + +/* IH_MUTE field values */ + HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT = 0x2, + HDMI_IH_MUTE_MUTE_ALL_INTERRUPT = 0x1, + +/* TX_INVID0 field values */ + HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_MASK = 0x80, + HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_ENABLE = 0x80, + HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE = 0x00, + HDMI_TX_INVID0_VIDEO_MAPPING_MASK = 0x1F, + HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET = 0, + +/* TX_INSTUFFING field values */ + HDMI_TX_INSTUFFING_BDBDATA_STUFFING_MASK = 0x4, + HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE = 0x4, + HDMI_TX_INSTUFFING_BDBDATA_STUFFING_DISABLE = 0x0, + HDMI_TX_INSTUFFING_RCRDATA_STUFFING_MASK = 0x2, + HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE = 0x2, + HDMI_TX_INSTUFFING_RCRDATA_STUFFING_DISABLE = 0x0, + HDMI_TX_INSTUFFING_GYDATA_STUFFING_MASK = 0x1, + HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE = 0x1, + HDMI_TX_INSTUFFING_GYDATA_STUFFING_DISABLE = 0x0, + +/* VP_PR_CD field values */ + HDMI_VP_PR_CD_COLOR_DEPTH_MASK = 0xF0, + HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET = 4, + HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK = 0x0F, + HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET = 0, + +/* VP_STUFF field values */ + HDMI_VP_STUFF_IDEFAULT_PHASE_MASK = 0x20, + HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET = 5, + HDMI_VP_STUFF_IFIX_PP_TO_LAST_MASK = 0x10, + HDMI_VP_STUFF_IFIX_PP_TO_LAST_OFFSET = 4, + HDMI_VP_STUFF_ICX_GOTO_P0_ST_MASK = 0x8, + HDMI_VP_STUFF_ICX_GOTO_P0_ST_OFFSET = 3, + HDMI_VP_STUFF_YCC422_STUFFING_MASK = 0x4, + HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE = 0x4, + HDMI_VP_STUFF_YCC422_STUFFING_DIRECT_MODE = 0x0, + HDMI_VP_STUFF_PP_STUFFING_MASK = 0x2, + HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE = 0x2, + HDMI_VP_STUFF_PP_STUFFING_DIRECT_MODE = 0x0, + HDMI_VP_STUFF_PR_STUFFING_MASK = 0x1, + HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE = 0x1, + HDMI_VP_STUFF_PR_STUFFING_DIRECT_MODE = 0x0, + +/* VP_CONF field values */ + HDMI_VP_CONF_BYPASS_EN_MASK = 0x40, + HDMI_VP_CONF_BYPASS_EN_ENABLE = 0x40, + HDMI_VP_CONF_BYPASS_EN_DISABLE = 0x00, + HDMI_VP_CONF_PP_EN_ENMASK = 0x20, + HDMI_VP_CONF_PP_EN_ENABLE = 0x20, + HDMI_VP_CONF_PP_EN_DISABLE = 0x00, + HDMI_VP_CONF_PR_EN_MASK = 0x10, + HDMI_VP_CONF_PR_EN_ENABLE = 0x10, + HDMI_VP_CONF_PR_EN_DISABLE = 0x00, + HDMI_VP_CONF_YCC422_EN_MASK = 0x8, + HDMI_VP_CONF_YCC422_EN_ENABLE = 0x8, + HDMI_VP_CONF_YCC422_EN_DISABLE = 0x0, + HDMI_VP_CONF_BYPASS_SELECT_MASK = 0x4, + HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER = 0x4, + HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER = 0x0, + HDMI_VP_CONF_OUTPUT_SELECTOR_MASK = 0x3, + HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS = 0x3, + HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422 = 0x1, + HDMI_VP_CONF_OUTPUT_SELECTOR_PP = 0x0, + +/* VP_REMAP field values */ + HDMI_VP_REMAP_MASK = 0x3, + HDMI_VP_REMAP_YCC422_24bit = 0x2, + HDMI_VP_REMAP_YCC422_20bit = 0x1, + HDMI_VP_REMAP_YCC422_16bit = 0x0, + +/* FC_INVIDCONF field values */ + HDMI_FC_INVIDCONF_HDCP_KEEPOUT_MASK = 0x80, + HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE = 0x80, + HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE = 0x00, + HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_MASK = 0x40, + HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH = 0x40, + HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW = 0x00, + HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_MASK = 0x20, + HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH = 0x20, + HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW = 0x00, + HDMI_FC_INVIDCONF_DE_IN_POLARITY_MASK = 0x10, + HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH = 0x10, + HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW = 0x00, + HDMI_FC_INVIDCONF_DVI_MODEZ_MASK = 0x8, + HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE = 0x8, + HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE = 0x0, + HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_MASK = 0x2, + HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH = 0x2, + HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW = 0x0, + HDMI_FC_INVIDCONF_IN_I_P_MASK = 0x1, + HDMI_FC_INVIDCONF_IN_I_P_INTERLACED = 0x1, + HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE = 0x0, + +/* FC_AUDICONF0 field values */ + HDMI_FC_AUDICONF0_CC_OFFSET = 4, + HDMI_FC_AUDICONF0_CC_MASK = 0x70, + HDMI_FC_AUDICONF0_CT_OFFSET = 0, + HDMI_FC_AUDICONF0_CT_MASK = 0xF, + +/* FC_AUDICONF1 field values */ + HDMI_FC_AUDICONF1_SS_OFFSET = 3, + HDMI_FC_AUDICONF1_SS_MASK = 0x18, + HDMI_FC_AUDICONF1_SF_OFFSET = 0, + HDMI_FC_AUDICONF1_SF_MASK = 0x7, + +/* FC_AUDICONF3 field values */ + HDMI_FC_AUDICONF3_LFEPBL_OFFSET = 5, + HDMI_FC_AUDICONF3_LFEPBL_MASK = 0x60, + HDMI_FC_AUDICONF3_DM_INH_OFFSET = 4, + HDMI_FC_AUDICONF3_DM_INH_MASK = 0x10, + HDMI_FC_AUDICONF3_LSV_OFFSET = 0, + HDMI_FC_AUDICONF3_LSV_MASK = 0xF, + +/* FC_AUDSCHNLS0 field values */ + HDMI_FC_AUDSCHNLS0_CGMSA_OFFSET = 4, + HDMI_FC_AUDSCHNLS0_CGMSA_MASK = 0x30, + HDMI_FC_AUDSCHNLS0_COPYRIGHT_OFFSET = 0, + HDMI_FC_AUDSCHNLS0_COPYRIGHT_MASK = 0x01, + +/* FC_AUDSCHNLS3-6 field values */ + HDMI_FC_AUDSCHNLS3_OIEC_CH0_OFFSET = 0, + HDMI_FC_AUDSCHNLS3_OIEC_CH0_MASK = 0x0f, + HDMI_FC_AUDSCHNLS3_OIEC_CH1_OFFSET = 4, + HDMI_FC_AUDSCHNLS3_OIEC_CH1_MASK = 0xf0, + HDMI_FC_AUDSCHNLS4_OIEC_CH2_OFFSET = 0, + HDMI_FC_AUDSCHNLS4_OIEC_CH2_MASK = 0x0f, + HDMI_FC_AUDSCHNLS4_OIEC_CH3_OFFSET = 4, + HDMI_FC_AUDSCHNLS4_OIEC_CH3_MASK = 0xf0, + + HDMI_FC_AUDSCHNLS5_OIEC_CH0_OFFSET = 0, + HDMI_FC_AUDSCHNLS5_OIEC_CH0_MASK = 0x0f, + HDMI_FC_AUDSCHNLS5_OIEC_CH1_OFFSET = 4, + HDMI_FC_AUDSCHNLS5_OIEC_CH1_MASK = 0xf0, + HDMI_FC_AUDSCHNLS6_OIEC_CH2_OFFSET = 0, + HDMI_FC_AUDSCHNLS6_OIEC_CH2_MASK = 0x0f, + HDMI_FC_AUDSCHNLS6_OIEC_CH3_OFFSET = 4, + HDMI_FC_AUDSCHNLS6_OIEC_CH3_MASK = 0xf0, + +/* HDMI_FC_AUDSCHNLS7 field values */ + HDMI_FC_AUDSCHNLS7_ACCURACY_OFFSET = 4, + HDMI_FC_AUDSCHNLS7_ACCURACY_MASK = 0x30, + +/* HDMI_FC_AUDSCHNLS8 field values */ + HDMI_FC_AUDSCHNLS8_ORIGSAMPFREQ_MASK = 0xf0, + HDMI_FC_AUDSCHNLS8_ORIGSAMPFREQ_OFFSET = 4, + HDMI_FC_AUDSCHNLS8_WORDLEGNTH_MASK = 0x0f, + HDMI_FC_AUDSCHNLS8_WORDLEGNTH_OFFSET = 0, + +/* FC_AUDSCONF field values */ + HDMI_FC_AUDSCONF_AUD_PACKET_SAMPFIT_MASK = 0xF0, + HDMI_FC_AUDSCONF_AUD_PACKET_SAMPFIT_OFFSET = 4, + HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_MASK = 0x1, + HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_OFFSET = 0, + HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT1 = 0x1, + HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT0 = 0x0, + +/* FC_PRCONF field values */ + HDMI_FC_PRCONF_INCOMING_PR_FACTOR_MASK = 0xF0, + HDMI_FC_PRCONF_INCOMING_PR_FACTOR_OFFSET = 4, + HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK = 0x0F, + HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_OFFSET = 0, + +/* FC_AVICONF0-FC_AVICONF2 field values */ + HDMI_FC_AVICONF0_PIX_FMT_MASK = 0x60, + HDMI_FC_AVICONF0_PIX_FMT_RGB = 0x00, + HDMI_FC_AVICONF0_PIX_FMT_YCBCR422 = 0x20, + HDMI_FC_AVICONF0_PIX_FMT_YCBCR444 = 0x40, + HDMI_FC_AVICONF0_ACTIVE_FMT_MASK = 0x10, + HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT = 0x10, + HDMI_FC_AVICONF0_ACTIVE_FMT_NO_INFO = 0x00, + HDMI_FC_AVICONF0_BAR_DATA_MASK = 0x0C, + HDMI_FC_AVICONF0_BAR_DATA_NO_DATA = 0x00, + HDMI_FC_AVICONF0_BAR_DATA_VERT_BAR = 0x40, + HDMI_FC_AVICONF0_BAR_DATA_HORIZ_BAR = 0x80, + HDMI_FC_AVICONF0_BAR_DATA_VERT_HORIZ_BAR = 0xC0, + HDMI_FC_AVICONF0_SCAN_INFO_MASK = 0x03, + HDMI_FC_AVICONF0_SCAN_INFO_OVERSCAN = 0x01, + HDMI_FC_AVICONF0_SCAN_INFO_UNDERSCAN = 0x02, + HDMI_FC_AVICONF0_SCAN_INFO_NODATA = 0x00, + + HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_MASK = 0x0F, + HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_USE_CODED = 0x09, + HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_4_3 = 0x09, + HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_16_9 = 0x0A, + HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_14_9 = 0x0B, + HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_MASK = 0x30, + HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_NO_DATA = 0x00, + HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_4_3 = 0x10, + HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_16_9 = 0x20, + HDMI_FC_AVICONF1_COLORIMETRY_MASK = 0xC0, + HDMI_FC_AVICONF1_COLORIMETRY_NO_DATA = 0x00, + HDMI_FC_AVICONF1_COLORIMETRY_SMPTE = 0x40, + HDMI_FC_AVICONF1_COLORIMETRY_ITUR = 0x80, + HDMI_FC_AVICONF1_COLORIMETRY_EXTENDED_INFO = 0xC0, + + HDMI_FC_AVICONF2_SCALING_MASK = 0x03, + HDMI_FC_AVICONF2_SCALING_NONE = 0x00, + HDMI_FC_AVICONF2_SCALING_HORIZ = 0x01, + HDMI_FC_AVICONF2_SCALING_VERT = 0x02, + HDMI_FC_AVICONF2_SCALING_HORIZ_VERT = 0x03, + HDMI_FC_AVICONF2_RGB_QUANT_MASK = 0x0C, + HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT = 0x00, + HDMI_FC_AVICONF2_RGB_QUANT_LIMITED_RANGE = 0x04, + HDMI_FC_AVICONF2_RGB_QUANT_FULL_RANGE = 0x08, + HDMI_FC_AVICONF2_EXT_COLORIMETRY_MASK = 0x70, + HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC601 = 0x00, + HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC709 = 0x10, + HDMI_FC_AVICONF2_EXT_COLORIMETRY_SYCC601 = 0x20, + HDMI_FC_AVICONF2_EXT_COLORIMETRY_ADOBE_YCC601 = 0x30, + HDMI_FC_AVICONF2_EXT_COLORIMETRY_ADOBE_RGB = 0x40, + HDMI_FC_AVICONF2_IT_CONTENT_MASK = 0x80, + HDMI_FC_AVICONF2_IT_CONTENT_NO_DATA = 0x00, + HDMI_FC_AVICONF2_IT_CONTENT_VALID = 0x80, + + HDMI_FC_AVICONF3_IT_CONTENT_TYPE_MASK = 0x03, + HDMI_FC_AVICONF2_IT_CONTENT_TYPE_GRAPHICS = 0x00, + HDMI_FC_AVICONF2_IT_CONTENT_TYPE_PHOTO = 0x01, + HDMI_FC_AVICONF2_IT_CONTENT_TYPE_CINEMA = 0x02, + HDMI_FC_AVICONF2_IT_CONTENT_TYPE_GAME = 0x03, + HDMI_FC_AVICONF3_QUANT_RANGE_MASK = 0x0C, + HDMI_FC_AVICONF3_QUANT_RANGE_LIMITED = 0x00, + HDMI_FC_AVICONF3_QUANT_RANGE_FULL = 0x04, + +/* FC_DBGFORCE field values */ + HDMI_FC_DBGFORCE_FORCEAUDIO = 0x10, + HDMI_FC_DBGFORCE_FORCEVIDEO = 0x1, + +/* PHY_CONF0 field values */ + HDMI_PHY_CONF0_PDZ = 0x80, + HDMI_PHY_CONF0_PDZ_MASK = 0x80, + HDMI_PHY_CONF0_PDZ_OFFSET = 7, + HDMI_PHY_CONF0_ENTMDS = 0x40, + HDMI_PHY_CONF0_ENTMDS_MASK = 0x40, + HDMI_PHY_CONF0_ENTMDS_OFFSET = 6, + HDMI_PHY_CONF0_SPARECTRL = 0x20, + HDMI_PHY_CONF0_GEN2_PDDQ_MASK = 0x10, + HDMI_PHY_CONF0_GEN2_PDDQ_ENABLE = 0x10, + HDMI_PHY_CONF0_GEN2_PDDQ_DISABLE = 0x00, + HDMI_PHY_CONF0_GEN2_TXPWRON_MASK = 0x8, + HDMI_PHY_CONF0_GEN2_TXPWRON_POWER_ON = 0x8, + HDMI_PHY_CONF0_GEN2_TXPWRON_POWER_OFF = 0x0, + HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE = 0x4, + HDMI_PHY_CONF0_SELDATAENPOL = 0x2, + HDMI_PHY_CONF0_SELDATAENPOL_MASK = 0x2, + HDMI_PHY_CONF0_SELDATAENPOL_OFFSET = 1, + HDMI_PHY_CONF0_SELDIPIF = 0x1, + HDMI_PHY_CONF0_SELDIPIF_MASK = 0x1, + HDMI_PHY_CONF0_SELDIPIF_OFFSET = 0, + +/* PHY_TST0 field values */ + HDMI_PHY_TST0_TSTCLR_MASK = 0x20, + HDMI_PHY_TST0_TSTCLR_OFFSET = 5, + HDMI_PHY_TST0_TSTEN_MASK = 0x10, + HDMI_PHY_TST0_TSTEN_OFFSET = 4, + HDMI_PHY_TST0_TSTCLK_MASK = 0x1, + HDMI_PHY_TST0_TSTCLK_OFFSET = 0, + +/* PHY_STAT0 field values */ + HDMI_PHY_RX_SENSE3 = 0x80, + HDMI_PHY_RX_SENSE2 = 0x40, + HDMI_PHY_RX_SENSE1 = 0x20, + HDMI_PHY_RX_SENSE0 = 0x10, + HDMI_PHY_HPD = 0x02, + HDMI_PHY_TX_PHY_LOCK = 0x01, + +/* PHY_I2CM_SLAVE_ADDR field values */ + HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2 = 0x69, + HDMI_PHY_I2CM_SLAVE_ADDR_HEAC_PHY = 0x49, + +/* PHY_I2CM_OPERATION_ADDR field values */ + HDMI_PHY_I2CM_OPERATION_ADDR_WRITE = 0x10, + HDMI_PHY_I2CM_OPERATION_ADDR_READ = 0x1, + +/* AUD_CTS3 field values */ + HDMI_AUD_CTS3_N_SHIFT_OFFSET = 5, + HDMI_AUD_CTS3_N_SHIFT_MASK = 0xe0, + HDMI_AUD_CTS3_N_SHIFT_1 = 0, + HDMI_AUD_CTS3_N_SHIFT_16 = 0x20, + HDMI_AUD_CTS3_N_SHIFT_32 = 0x40, + HDMI_AUD_CTS3_N_SHIFT_64 = 0x60, + HDMI_AUD_CTS3_N_SHIFT_128 = 0x80, + HDMI_AUD_CTS3_N_SHIFT_256 = 0xa0, + /* note that the CTS3 MANUAL bit has been removed + from our part. Can't set it, will read as 0. */ + HDMI_AUD_CTS3_CTS_MANUAL = 0x10, + HDMI_AUD_CTS3_AUDCTS19_16_MASK = 0x0f, + +/* AHB_DMA_CONF0 field values */ + HDMI_AHB_DMA_CONF0_SW_FIFO_RST_OFFSET = 7, + HDMI_AHB_DMA_CONF0_SW_FIFO_RST_MASK = 0x80, + HDMI_AHB_DMA_CONF0_HBR = 0x10, + HDMI_AHB_DMA_CONF0_EN_HLOCK_OFFSET = 3, + HDMI_AHB_DMA_CONF0_EN_HLOCK_MASK = 0x08, + HDMI_AHB_DMA_CONF0_INCR_TYPE_OFFSET = 1, + HDMI_AHB_DMA_CONF0_INCR_TYPE_MASK = 0x06, + HDMI_AHB_DMA_CONF0_INCR4 = 0x0, + HDMI_AHB_DMA_CONF0_INCR8 = 0x2, + HDMI_AHB_DMA_CONF0_INCR16 = 0x4, + HDMI_AHB_DMA_CONF0_BURST_MODE = 0x1, + +/* HDMI_AHB_DMA_START field values */ + HDMI_AHB_DMA_START_START_OFFSET = 0, + HDMI_AHB_DMA_START_START_MASK = 0x01, + +/* HDMI_AHB_DMA_STOP field values */ + HDMI_AHB_DMA_STOP_STOP_OFFSET = 0, + HDMI_AHB_DMA_STOP_STOP_MASK = 0x01, + +/* AHB_DMA_STAT, AHB_DMA_INT, AHB_DMA_MASK, AHB_DMA_POL field values */ + HDMI_AHB_DMA_DONE = 0x80, + HDMI_AHB_DMA_RETRY_SPLIT = 0x40, + HDMI_AHB_DMA_LOSTOWNERSHIP = 0x20, + HDMI_AHB_DMA_ERROR = 0x10, + HDMI_AHB_DMA_FIFO_THREMPTY = 0x04, + HDMI_AHB_DMA_FIFO_FULL = 0x02, + HDMI_AHB_DMA_FIFO_EMPTY = 0x01, + +/* AHB_DMA_BUFFSTAT, AHB_DMA_BUFFINT, AHB_DMA_BUFFMASK, AHB_DMA_BUFFPOL field values */ + HDMI_AHB_DMA_BUFFSTAT_FULL = 0x02, + HDMI_AHB_DMA_BUFFSTAT_EMPTY = 0x01, + +/* MC_CLKDIS field values */ + HDMI_MC_CLKDIS_HDCPCLK_DISABLE_MASK = 0x40, + HDMI_MC_CLKDIS_HDCPCLK_DISABLE_DISABLE = 0x40, + HDMI_MC_CLKDIS_HDCPCLK_DISABLE_ENABLE = 0x00, + HDMI_MC_CLKDIS_CECCLK_DISABLE_MASK = 0x20, + HDMI_MC_CLKDIS_CECCLK_DISABLE_DISABLE = 0x20, + HDMI_MC_CLKDIS_CECCLK_DISABLE_ENABLE = 0x00, + HDMI_MC_CLKDIS_CSCCLK_DISABLE_MASK = 0x10, + HDMI_MC_CLKDIS_CSCCLK_DISABLE_DISABLE = 0x10, + HDMI_MC_CLKDIS_CSCCLK_DISABLE_ENABLE = 0x00, + HDMI_MC_CLKDIS_AUDCLK_DISABLE_MASK = 0x8, + HDMI_MC_CLKDIS_AUDCLK_DISABLE_DISABLE = 0x8, + HDMI_MC_CLKDIS_AUDCLK_DISABLE_ENABLE = 0x0, + HDMI_MC_CLKDIS_PREPCLK_DISABLE_MASK = 0x4, + HDMI_MC_CLKDIS_PREPCLK_DISABLE_DISABLE = 0x4, + HDMI_MC_CLKDIS_PREPCLK_DISABLE_ENABLE = 0x0, + HDMI_MC_CLKDIS_TMDSCLK_DISABLE_MASK = 0x2, + HDMI_MC_CLKDIS_TMDSCLK_DISABLE_DISABLE = 0x2, + HDMI_MC_CLKDIS_TMDSCLK_DISABLE_ENABLE = 0x0, + HDMI_MC_CLKDIS_PIXELCLK_DISABLE_MASK = 0x1, + HDMI_MC_CLKDIS_PIXELCLK_DISABLE_DISABLE = 0x1, + HDMI_MC_CLKDIS_PIXELCLK_DISABLE_ENABLE = 0x0, + +/* MC_FLOWCTRL field values */ + HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_MASK = 0x1, + HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH = 0x1, + HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS = 0x0, + +/* MC_PHYRSTZ field values */ + HDMI_MC_PHYRSTZ_ASSERT = 0x0, + HDMI_MC_PHYRSTZ_DEASSERT = 0x1, + +/* MC_HEACPHY_RST field values */ + HDMI_MC_HEACPHY_RST_ASSERT = 0x1, + HDMI_MC_HEACPHY_RST_DEASSERT = 0x0, + +/* CSC_CFG field values */ + HDMI_CSC_CFG_INTMODE_MASK = 0x30, + HDMI_CSC_CFG_INTMODE_OFFSET = 4, + HDMI_CSC_CFG_INTMODE_DISABLE = 0x00, + HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA1 = 0x10, + HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA2 = 0x20, + HDMI_CSC_CFG_DECMODE_MASK = 0x3, + HDMI_CSC_CFG_DECMODE_OFFSET = 0, + HDMI_CSC_CFG_DECMODE_DISABLE = 0x0, + HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA1 = 0x1, + HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA2 = 0x2, + HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA3 = 0x3, + +/* CSC_SCALE field values */ + HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK = 0xF0, + HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP = 0x00, + HDMI_CSC_SCALE_CSC_COLORDE_PTH_30BPP = 0x50, + HDMI_CSC_SCALE_CSC_COLORDE_PTH_36BPP = 0x60, + HDMI_CSC_SCALE_CSC_COLORDE_PTH_48BPP = 0x70, + HDMI_CSC_SCALE_CSCSCALE_MASK = 0x03, + +/* A_HDCPCFG0 field values */ + HDMI_A_HDCPCFG0_ELVENA_MASK = 0x80, + HDMI_A_HDCPCFG0_ELVENA_ENABLE = 0x80, + HDMI_A_HDCPCFG0_ELVENA_DISABLE = 0x00, + HDMI_A_HDCPCFG0_I2CFASTMODE_MASK = 0x40, + HDMI_A_HDCPCFG0_I2CFASTMODE_ENABLE = 0x40, + HDMI_A_HDCPCFG0_I2CFASTMODE_DISABLE = 0x00, + HDMI_A_HDCPCFG0_BYPENCRYPTION_MASK = 0x20, + HDMI_A_HDCPCFG0_BYPENCRYPTION_ENABLE = 0x20, + HDMI_A_HDCPCFG0_BYPENCRYPTION_DISABLE = 0x00, + HDMI_A_HDCPCFG0_SYNCRICHECK_MASK = 0x10, + HDMI_A_HDCPCFG0_SYNCRICHECK_ENABLE = 0x10, + HDMI_A_HDCPCFG0_SYNCRICHECK_DISABLE = 0x00, + HDMI_A_HDCPCFG0_AVMUTE_MASK = 0x8, + HDMI_A_HDCPCFG0_AVMUTE_ENABLE = 0x8, + HDMI_A_HDCPCFG0_AVMUTE_DISABLE = 0x0, + HDMI_A_HDCPCFG0_RXDETECT_MASK = 0x4, + HDMI_A_HDCPCFG0_RXDETECT_ENABLE = 0x4, + HDMI_A_HDCPCFG0_RXDETECT_DISABLE = 0x0, + HDMI_A_HDCPCFG0_EN11FEATURE_MASK = 0x2, + HDMI_A_HDCPCFG0_EN11FEATURE_ENABLE = 0x2, + HDMI_A_HDCPCFG0_EN11FEATURE_DISABLE = 0x0, + HDMI_A_HDCPCFG0_HDMIDVI_MASK = 0x1, + HDMI_A_HDCPCFG0_HDMIDVI_HDMI = 0x1, + HDMI_A_HDCPCFG0_HDMIDVI_DVI = 0x0, + +/* A_HDCPCFG1 field values */ + HDMI_A_HDCPCFG1_DISSHA1CHECK_MASK = 0x8, + HDMI_A_HDCPCFG1_DISSHA1CHECK_DISABLE = 0x8, + HDMI_A_HDCPCFG1_DISSHA1CHECK_ENABLE = 0x0, + HDMI_A_HDCPCFG1_PH2UPSHFTENC_MASK = 0x4, + HDMI_A_HDCPCFG1_PH2UPSHFTENC_ENABLE = 0x4, + HDMI_A_HDCPCFG1_PH2UPSHFTENC_DISABLE = 0x0, + HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK = 0x2, + HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE = 0x2, + HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_ENABLE = 0x0, + HDMI_A_HDCPCFG1_SWRESET_MASK = 0x1, + HDMI_A_HDCPCFG1_SWRESET_ASSERT = 0x0, + +/* A_VIDPOLCFG field values */ + HDMI_A_VIDPOLCFG_UNENCRYPTCONF_MASK = 0x60, + HDMI_A_VIDPOLCFG_UNENCRYPTCONF_OFFSET = 5, + HDMI_A_VIDPOLCFG_DATAENPOL_MASK = 0x10, + HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH = 0x10, + HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW = 0x0, + HDMI_A_VIDPOLCFG_VSYNCPOL_MASK = 0x8, + HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_HIGH = 0x8, + HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_LOW = 0x0, + HDMI_A_VIDPOLCFG_HSYNCPOL_MASK = 0x2, + HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_HIGH = 0x2, + HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_LOW = 0x0, +}; + +#endif /* __MXC_HDMI_H__ */ diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 7726c728a7f..f90c819e4f4 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -809,6 +809,14 @@ config MFD_INTEL_MSIC Passage) chip. This chip embeds audio, battery, GPIO, etc. devices used in Intel Medfield platforms. +config MFD_MXC_HDMI + tristate "MXC HDMI Core" + select MFD_CORE + default y + help + This is the core driver for the i.Mx on-chip HDMI. This MFD + driver connects with the video and audio drivers for HDMI. + endmenu endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index abad5831ac5..9da178bf2e5 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -106,3 +106,4 @@ obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o +obj-$(CONFIG_MFD_MXC_HDMI) += mxc-hdmi-core.o diff --git a/drivers/mfd/mxc-hdmi-core.c b/drivers/mfd/mxc-hdmi-core.c new file mode 100644 index 00000000000..48cc57cc1b8 --- /dev/null +++ b/drivers/mfd/mxc-hdmi-core.c @@ -0,0 +1,538 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include "../mxc/ipu3/ipu_prv.h" +#include +#include + +struct mxc_hdmi_data { + struct platform_device *pdev; + unsigned long __iomem *reg_base; + unsigned long reg_phys_base; + struct device *dev; +}; + +static unsigned long hdmi_base; +struct clk *isfr_clk; +struct clk *iahb_clk; +static unsigned int irq_enable_cnt; +spinlock_t irq_spinlock; +bool irq_initialized; +bool irq_enabled; +unsigned int sample_rate; +unsigned long pixel_clk_rate; +struct clk *pixel_clk; +int hdmi_ratio; +int mxc_hdmi_ipu_id; +int mxc_hdmi_disp_id; + +u8 hdmi_readb(unsigned int reg) +{ + u8 value; + + value = __raw_readb(hdmi_base + reg); + + pr_debug("hdmi rd: 0x%04x = 0x%02x\n", reg, value); + + return value; +} + +void hdmi_writeb(u8 value, unsigned int reg) +{ + pr_debug("hdmi wr: 0x%04x = 0x%02x\n", reg, value); + __raw_writeb(value, hdmi_base + reg); +} + +void hdmi_mask_writeb(u8 data, unsigned int reg, u8 shift, u8 mask) +{ + u8 value = hdmi_readb(reg) & ~mask; + value |= (data << shift) & mask; + hdmi_writeb(value, reg); +} + +unsigned int hdmi_read4(unsigned int reg) +{ + /* read a four byte address from registers */ + return (hdmi_readb(reg + 3) << 24) | + (hdmi_readb(reg + 2) << 16) | + (hdmi_readb(reg + 1) << 8) | + hdmi_readb(reg); +} + +void hdmi_write4(unsigned int value, unsigned int reg) +{ + /* write a four byte address to hdmi regs */ + hdmi_writeb(value & 0xff, reg); + hdmi_writeb((value >> 8) & 0xff, reg + 1); + hdmi_writeb((value >> 16) & 0xff, reg + 2); + hdmi_writeb((value >> 24) & 0xff, reg + 3); +} + +void hdmi_irq_init() +{ + /* First time IRQ is initialized, set enable_cnt to 1, + * since IRQ starts out enabled after request_irq */ + if (!irq_initialized) { + irq_enable_cnt = 1; + irq_initialized = true; + irq_enabled = true; + } +} + +void hdmi_irq_enable(int irq) +{ + unsigned long flags; + + spin_lock_irqsave(&irq_spinlock, flags); + + if (!irq_enabled) { + enable_irq(irq); + irq_enabled = true; + } + + irq_enable_cnt++; + + spin_unlock_irqrestore(&irq_spinlock, flags); +} + +unsigned int hdmi_irq_disable(int irq) +{ + unsigned long flags; + + spin_lock_irqsave(&irq_spinlock, flags); + + WARN_ON (irq_enable_cnt == 0); + + irq_enable_cnt--; + + /* Only disable HDMI IRQ if IAHB clk is off */ + if ((irq_enable_cnt == 0) && (clk_get_usecount(iahb_clk) == 0)) { + disable_irq_nosync(irq); + irq_enabled = false; + spin_unlock_irqrestore(&irq_spinlock, flags); + return IRQ_DISABLE_SUCCEED; + } + + spin_unlock_irqrestore(&irq_spinlock, flags); + + return IRQ_DISABLE_FAIL; +} + +static void initialize_hdmi_ih_mutes(void) +{ + u8 ih_mute; + + /* + * Boot up defaults are: + * HDMI_IH_MUTE = 0x03 (disabled) + * HDMI_IH_MUTE_* = 0x00 (enabled) + */ + + /* Disable top level interrupt bits in HDMI block */ + ih_mute = hdmi_readb(HDMI_IH_MUTE) | + HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT | + HDMI_IH_MUTE_MUTE_ALL_INTERRUPT; + + hdmi_writeb(ih_mute, HDMI_IH_MUTE); + + /* Disable interrupts in the IH_MUTE_* registers */ + hdmi_writeb(0xff, HDMI_IH_MUTE_FC_STAT0); + hdmi_writeb(0xff, HDMI_IH_MUTE_FC_STAT1); + hdmi_writeb(0xff, HDMI_IH_MUTE_FC_STAT2); + hdmi_writeb(0xff, HDMI_IH_MUTE_AS_STAT0); + hdmi_writeb(0xff, HDMI_IH_MUTE_PHY_STAT0); + hdmi_writeb(0xff, HDMI_IH_MUTE_I2CM_STAT0); + hdmi_writeb(0xff, HDMI_IH_MUTE_CEC_STAT0); + hdmi_writeb(0xff, HDMI_IH_MUTE_VP_STAT0); + hdmi_writeb(0xff, HDMI_IH_MUTE_I2CMPHY_STAT0); + hdmi_writeb(0xff, HDMI_IH_MUTE_AHBDMAAUD_STAT0); + + /* Enable top level interrupt bits in HDMI block */ + ih_mute &= ~(HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT | + HDMI_IH_MUTE_MUTE_ALL_INTERRUPT); + hdmi_writeb(ih_mute, HDMI_IH_MUTE); +} + +static void hdmi_set_clock_regenerator_n(unsigned int value) +{ + hdmi_writeb(value & 0xff, HDMI_AUD_N1); + hdmi_writeb((value >> 8) & 0xff, HDMI_AUD_N2); + hdmi_writeb((value >> 16) & 0xff, HDMI_AUD_N3); +} + +static void hdmi_set_clock_regenerator_cts(unsigned int cts) +{ + hdmi_writeb(cts & 0xff, HDMI_AUD_CTS1); + hdmi_writeb((cts >> 8) & 0xff, HDMI_AUD_CTS2); + hdmi_writeb(((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) | + HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3); +} + +static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk, + unsigned int ratio) +{ + unsigned int n = (128 * freq) / 1000; + + switch (freq) { + case 32000: + if (pixel_clk == 25170000) + n = (ratio == 150) ? 9152 : 4576; + else if (pixel_clk == 27020000) + n = (ratio == 150) ? 8192 : 4096; + else if (pixel_clk == 74170000 || pixel_clk == 148350000) + n = 11648; + else + n = 4096; + break; + + case 44100: + if (pixel_clk == 25170000) + n = 7007; + else if (pixel_clk == 74170000) + n = 17836; + else if (pixel_clk == 148350000) + n = (ratio == 150) ? 17836 : 8918; + else + n = 6272; + break; + + case 48000: + if (pixel_clk == 25170000) + n = (ratio == 150) ? 9152 : 6864; + else if (pixel_clk == 27020000) + n = (ratio == 150) ? 8192 : 6144; + else if (pixel_clk == 74170000) + n = 11648; + else if (pixel_clk == 148350000) + n = (ratio == 150) ? 11648 : 5824; + else + n = 6144; + break; + + case 88200: + n = hdmi_compute_n(44100, pixel_clk, ratio) * 2; + break; + + case 96000: + n = hdmi_compute_n(48000, pixel_clk, ratio) * 2; + break; + + case 176400: + n = hdmi_compute_n(44100, pixel_clk, ratio) * 4; + break; + + case 192000: + n = hdmi_compute_n(48000, pixel_clk, ratio) * 4; + break; + + default: + break; + } + + return n; +} + +static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk, + unsigned int ratio) +{ + unsigned int cts = 0; + switch (freq) { + case 32000: + if (pixel_clk == 297000000) { + cts = 222750; + break; + } + case 48000: + case 96000: + case 192000: + switch (pixel_clk) { + case 25200000: + case 27000000: + case 54000000: + case 74250000: + case 148500000: + cts = pixel_clk / 1000; + break; + case 297000000: + cts = 247500; + break; + /* + * All other TMDS clocks are not supported by + * DWC_hdmi_tx. The TMDS clocks divided or + * multiplied by 1,001 coefficients are not + * supported. + */ + default: + break; + } + break; + case 44100: + case 88200: + case 176400: + switch (pixel_clk) { + case 25200000: + cts = 28000; + break; + case 27000000: + cts = 30000; + break; + case 54000000: + cts = 60000; + break; + case 74250000: + cts = 82500; + break; + case 148500000: + cts = 165000; + break; + case 297000000: + cts = 247500; + break; + default: + break; + } + break; + default: + break; + } + if (ratio == 100) + return cts; + else + return (cts * ratio) / 100; +} + +static void hdmi_get_pixel_clk(void) +{ + struct ipu_soc *ipu; + + if (pixel_clk == NULL) { + ipu = ipu_get_soc(mxc_hdmi_ipu_id); + pixel_clk = clk_get(ipu->dev, "pixel_clk_0"); + if (IS_ERR(pixel_clk)) { + pr_err("%s could not get pixel_clk_0\n", __func__); + return; + } + } + + pixel_clk_rate = clk_get_rate(pixel_clk); +} + +/* + * input: audio sample rate and video pixel rate + * output: N and cts written to the HDMI regs. + */ +void hdmi_set_clk_regenerator(void) +{ + unsigned int clk_n, clk_cts; + + /* Get pixel clock from ipu */ + hdmi_get_pixel_clk(); + + pr_debug("%s: sample rate is %d ; ratio is %d ; pixel clk is %d\n", + __func__, sample_rate, hdmi_ratio, (int)pixel_clk_rate); + + clk_n = hdmi_compute_n(sample_rate, pixel_clk_rate, hdmi_ratio); + clk_cts = hdmi_compute_cts(sample_rate, pixel_clk_rate, hdmi_ratio); + + if (clk_cts == 0) { + pr_err("%s: pixel clock not supported: %d\n", + __func__, (int)pixel_clk_rate); + return; + } + + clk_enable(isfr_clk); + clk_enable(iahb_clk); + + hdmi_set_clock_regenerator_n(clk_n); + hdmi_set_clock_regenerator_cts(clk_cts); + + clk_disable(iahb_clk); + clk_disable(isfr_clk); +} + +void hdmi_set_sample_rate(unsigned int rate) +{ + sample_rate = rate; + hdmi_set_clk_regenerator(); +} + +static int mxc_hdmi_core_probe(struct platform_device *pdev) +{ + struct fsl_mxc_hdmi_core_platform_data *pdata = pdev->dev.platform_data; + struct mxc_hdmi_data *hdmi_data; + struct resource *res; + int ret = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; + + hdmi_data = kzalloc(sizeof(struct mxc_hdmi_data), GFP_KERNEL); + if (!hdmi_data) { + dev_err(&pdev->dev, "Couldn't allocate mxc hdmi mfd device\n"); + return -ENOMEM; + } + hdmi_data->pdev = pdev; + + pixel_clk = NULL; + sample_rate = 48000; + pixel_clk_rate = 74250000; + hdmi_ratio = 100; + + irq_enable_cnt = 0; + irq_initialized = false; + irq_enabled = true; + spin_lock_init(&irq_spinlock); + + isfr_clk = clk_get(&hdmi_data->pdev->dev, "hdmi_isfr_clk"); + if (IS_ERR(isfr_clk)) { + ret = PTR_ERR(isfr_clk); + dev_err(&hdmi_data->pdev->dev, + "Unable to get HDMI isfr clk: %d\n", ret); + goto eclkg; + } + + ret = clk_enable(isfr_clk); + if (ret < 0) { + dev_err(&pdev->dev, "Cannot enable HDMI clock: %d\n", ret); + goto eclke; + } + + pr_debug("%s isfr_clk:%d\n", __func__, + (int)clk_get_rate(isfr_clk)); + + iahb_clk = clk_get(&hdmi_data->pdev->dev, "hdmi_iahb_clk"); + if (IS_ERR(iahb_clk)) { + ret = PTR_ERR(iahb_clk); + dev_err(&hdmi_data->pdev->dev, + "Unable to get HDMI iahb clk: %d\n", ret); + goto eclkg2; + } + + ret = clk_enable(iahb_clk); + if (ret < 0) { + dev_err(&pdev->dev, "Cannot enable HDMI clock: %d\n", ret); + goto eclke2; + } + + hdmi_data->reg_phys_base = res->start; + if (!request_mem_region(res->start, resource_size(res), + dev_name(&pdev->dev))) { + dev_err(&pdev->dev, "request_mem_region failed\n"); + ret = -EBUSY; + goto emem; + } + + hdmi_data->reg_base = ioremap(res->start, resource_size(res)); + if (!hdmi_data->reg_base) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto eirq; + } + hdmi_base = (unsigned long)hdmi_data->reg_base; + + pr_debug("\n%s hdmi hw base = 0x%08x\n\n", __func__, (int)res->start); + + mxc_hdmi_ipu_id = pdata->ipu_id; + mxc_hdmi_disp_id = pdata->disp_id; + + initialize_hdmi_ih_mutes(); + + /* Disable HDMI clocks until video/audio sub-drivers are initialized */ + clk_disable(isfr_clk); + clk_disable(iahb_clk); + + /* Replace platform data coming in with a local struct */ + platform_set_drvdata(pdev, hdmi_data); + + return ret; + +eirq: + release_mem_region(res->start, resource_size(res)); +emem: + clk_disable(iahb_clk); +eclke2: + clk_put(iahb_clk); +eclkg2: + clk_disable(isfr_clk); +eclke: + clk_put(isfr_clk); +eclkg: + kfree(hdmi_data); + return ret; +} + + +static int __exit mxc_hdmi_core_remove(struct platform_device *pdev) +{ + struct mxc_hdmi_data *hdmi_data = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + iounmap(hdmi_data->reg_base); + release_mem_region(res->start, resource_size(res)); + + kfree(hdmi_data); + + return 0; +} + +static struct platform_driver mxc_hdmi_core_driver = { + .driver = { + .name = "mxc_hdmi_core", + .owner = THIS_MODULE, + }, + .remove = __exit_p(mxc_hdmi_core_remove), +}; + +static int __init mxc_hdmi_core_init(void) +{ + return platform_driver_probe(&mxc_hdmi_core_driver, + mxc_hdmi_core_probe); +} + +static void __exit mxc_hdmi_core_exit(void) +{ + platform_driver_unregister(&mxc_hdmi_core_driver); +} + +subsys_initcall(mxc_hdmi_core_init); +module_exit(mxc_hdmi_core_exit); + +MODULE_DESCRIPTION("Core driver for Freescale i.Mx on-chip HDMI"); +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index de5806bb9ba..2a5e760b313 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2419,6 +2419,13 @@ if ARCH_MXC source "drivers/video/mxc/Kconfig" endif +config FB_MXC_HDMI + depends on FB_MXC_SYNC_PANEL && I2C + tristate "MXC HDMI driver support" + select MFD_MXC_HDMI + help + Driver for the on-chip MXC HDMI controller. + if VT source "drivers/video/console/Kconfig" endif diff --git a/drivers/video/Makefile b/drivers/video/Makefile index bc77579e8de..9ea192b5cac 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_FB_KYRO) += kyro/ obj-$(CONFIG_FB_SAVAGE) += savage/ obj-$(CONFIG_FB_GEODE) += geode/ obj-$(CONFIG_FB_MBX) += mbx/ +obj-$(CONFIG_FB_MXC_HDMI) += mxc_hdmi.o obj-$(CONFIG_FB_MXC) += mxc/ obj-$(CONFIG_FB_NEOMAGIC) += neofb.o obj-$(CONFIG_FB_3DFX) += tdfxfb.o diff --git a/drivers/video/mxc_hdmi.c b/drivers/video/mxc_hdmi.c new file mode 100644 index 00000000000..0298c243b1a --- /dev/null +++ b/drivers/video/mxc_hdmi.c @@ -0,0 +1,1968 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/* + * SH-Mobile High-Definition Multimedia Interface (HDMI) driver + * for SLISHDMI13T and SLIPHDMIT IP cores + * + * Copyright (C) 2010, Guennadi Liakhovetski + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include "mxc/mxc_dispdrv.h" + +#include +#include + +#define DISPDRV_HDMI "hdmi" +#define HDMI_EDID_LEN 512 + +#define TRUE 1 +#define FALSE 0 + +#define NUM_CEA_VIDEO_MODES 64 +#define DEFAULT_VIDEO_MODE 16 /* 1080P */ + +#define RGB 0 +#define YCBCR444 1 +#define YCBCR422_16BITS 2 +#define YCBCR422_8BITS 3 +#define XVYCC444 4 + +enum hdmi_datamap { + RGB444_8B = 0x01, + RGB444_10B = 0x03, + RGB444_12B = 0x05, + RGB444_16B = 0x07, + YCbCr444_8B = 0x09, + YCbCr444_10B = 0x0B, + YCbCr444_12B = 0x0D, + YCbCr444_16B = 0x0F, + YCbCr422_8B = 0x16, + YCbCr422_10B = 0x14, + YCbCr422_12B = 0x12, +}; + +enum hdmi_colorimetry { + eITU601, + eITU709, +}; + +struct hdmi_vmode { + unsigned int mHdmiDviSel; + unsigned int mHSyncPolarity; + unsigned int mVSyncPolarity; + unsigned int mInterlaced; + unsigned int mDataEnablePolarity; + unsigned int mPixelClock; + unsigned int mPixelRepetitionInput; + unsigned int mPixelRepetitionOutput; +}; + +struct hdmi_data_info { + unsigned int enc_in_format; + unsigned int enc_out_format; + unsigned int enc_color_depth; + unsigned int colorimetry; + unsigned int pix_repet_factor; + unsigned int hdcp_enable; + struct hdmi_vmode video_mode; +}; + +struct mxc_hdmi { + struct platform_device *pdev; + struct platform_device *core_pdev; + struct mxc_dispdrv_entry *disp_mxc_hdmi; + struct fb_info *fbi; + struct clk *hdmi_isfr_clk; + struct clk *hdmi_iahb_clk; + struct delayed_work det_work; + struct notifier_block nb; + + struct hdmi_data_info hdmi_data; + int vic; + struct mxc_edid_cfg edid_cfg; + u8 edid[HDMI_EDID_LEN]; + bool fb_reg; + bool need_mode_change; + bool cable_plugin; + u8 latest_intr_stat; + bool irq_enabled; + spinlock_t irq_lock; +}; + +struct i2c_client *hdmi_i2c; + +extern const struct fb_videomode mxc_cea_mode[64]; + +/*! + * this submodule is responsible for the video data synchronization. + * for example, for RGB 4:4:4 input, the data map is defined as + * pin{47~40} <==> R[7:0] + * pin{31~24} <==> G[7:0] + * pin{15~8} <==> B[7:0] + */ +void hdmi_video_sample(struct mxc_hdmi *hdmi) +{ + int color_format = 0; + u8 val; + + if (hdmi->hdmi_data.enc_in_format == RGB) { + if (hdmi->hdmi_data.enc_color_depth == 8) + color_format = 0x01; + else if (hdmi->hdmi_data.enc_color_depth == 10) + color_format = 0x03; + else if (hdmi->hdmi_data.enc_color_depth == 12) + color_format = 0x05; + else if (hdmi->hdmi_data.enc_color_depth == 16) + color_format = 0x07; + else + return; + } else if (hdmi->hdmi_data.enc_in_format == XVYCC444) { + if (hdmi->hdmi_data.enc_color_depth == 8) + color_format = 0x09; + else if (hdmi->hdmi_data.enc_color_depth == 10) + color_format = 0x0B; + else if (hdmi->hdmi_data.enc_color_depth == 12) + color_format = 0x0D; + else if (hdmi->hdmi_data.enc_color_depth == 16) + color_format = 0x0F; + else + return; + } else if (hdmi->hdmi_data.enc_in_format == YCBCR422_8BITS) { + if (hdmi->hdmi_data.enc_color_depth == 8) + color_format = 0x16; + else if (hdmi->hdmi_data.enc_color_depth == 10) + color_format = 0x14; + else if (hdmi->hdmi_data.enc_color_depth == 12) + color_format = 0x12; + else + return; + } + + val = HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE | + ((color_format << HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET) & + HDMI_TX_INVID0_VIDEO_MAPPING_MASK); + hdmi_writeb(val, HDMI_TX_INVID0); + + /* Enable TX stuffing: When DE is inactive, fix the output data to 0 */ + val = HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE | + HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE | + HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE; + hdmi_writeb(val, HDMI_TX_INSTUFFING); + hdmi_writeb(0x0, HDMI_TX_GYDATA0); + hdmi_writeb(0x0, HDMI_TX_GYDATA1); + hdmi_writeb(0x0, HDMI_TX_RCRDATA0); + hdmi_writeb(0x0, HDMI_TX_RCRDATA1); + hdmi_writeb(0x0, HDMI_TX_BCBDATA0); + hdmi_writeb(0x0, HDMI_TX_BCBDATA1); +} + +static int isColorSpaceConversion(struct mxc_hdmi *hdmi) +{ + return (hdmi->hdmi_data.enc_in_format != + hdmi->hdmi_data.enc_out_format) ? TRUE : FALSE; +} + +static int isColorSpaceDecimation(struct mxc_hdmi *hdmi) +{ + return ((hdmi->hdmi_data.enc_out_format == YCBCR422_8BITS) && + (hdmi->hdmi_data.enc_in_format == RGB || + hdmi->hdmi_data.enc_in_format == XVYCC444)) ? TRUE : FALSE; +} + +static int isColorSpaceInterpolation(struct mxc_hdmi *hdmi) +{ + return ((hdmi->hdmi_data.enc_in_format == YCBCR422_8BITS) && + (hdmi->hdmi_data.enc_out_format == RGB + || hdmi->hdmi_data.enc_out_format == XVYCC444)) ? + TRUE : FALSE; +} + +/*! + * update the color space conversion coefficients. + */ +void update_csc_coeffs(struct mxc_hdmi *hdmi) +{ + unsigned short csc_coeff[3][4]; + unsigned int csc_scale = 1; + u8 val; + bool coeff_selected = false; + + if (isColorSpaceConversion(hdmi)) { /* csc needed */ + if (hdmi->hdmi_data.enc_out_format == RGB) { + if (hdmi->hdmi_data.colorimetry == eITU601) { + csc_coeff[0][0] = 0x2000; + csc_coeff[0][1] = 0x6926; + csc_coeff[0][2] = 0x74fd; + csc_coeff[0][3] = 0x010e; + + csc_coeff[1][0] = 0x2000; + csc_coeff[1][1] = 0x2cdd; + csc_coeff[1][2] = 0x0000; + csc_coeff[1][3] = 0x7e9a; + + csc_coeff[2][0] = 0x2000; + csc_coeff[2][1] = 0x0000; + csc_coeff[2][2] = 0x38b4; + csc_coeff[2][3] = 0x7e3b; + + csc_scale = 1; + coeff_selected = true; + } else if (hdmi->hdmi_data.colorimetry == eITU709) { + csc_coeff[0][0] = 0x2000; + csc_coeff[0][1] = 0x7106; + csc_coeff[0][2] = 0x7a02; + csc_coeff[0][3] = 0x00a7; + + csc_coeff[1][0] = 0x2000; + csc_coeff[1][1] = 0x3264; + csc_coeff[1][2] = 0x0000; + csc_coeff[1][3] = 0x7e6d; + + csc_coeff[2][0] = 0x2000; + csc_coeff[2][1] = 0x0000; + csc_coeff[2][2] = 0x3b61; + csc_coeff[2][3] = 0x7e25; + + csc_scale = 1; + coeff_selected = true; + } + } else if (hdmi->hdmi_data.enc_in_format == RGB) { + if (hdmi->hdmi_data.colorimetry == eITU601) { + csc_coeff[0][0] = 0x2591; + csc_coeff[0][1] = 0x1322; + csc_coeff[0][2] = 0x074b; + csc_coeff[0][3] = 0x0000; + + csc_coeff[1][0] = 0x6535; + csc_coeff[1][1] = 0x2000; + csc_coeff[1][2] = 0x7acc; + csc_coeff[1][3] = 0x0200; + + csc_coeff[2][0] = 0x6acd; + csc_coeff[2][1] = 0x7534; + csc_coeff[2][2] = 0x2000; + csc_coeff[2][3] = 0x0200; + + csc_scale = 0; + coeff_selected = true; + } else if (hdmi->hdmi_data.colorimetry == eITU709) { + csc_coeff[0][0] = 0x2dc5; + csc_coeff[0][1] = 0x0d9b; + csc_coeff[0][2] = 0x049e; + csc_coeff[0][3] = 0x0000; + + csc_coeff[1][0] = 0x62f0; + csc_coeff[1][1] = 0x2000; + csc_coeff[1][2] = 0x7d11; + csc_coeff[1][3] = 0x0200; + + csc_coeff[2][0] = 0x6756; + csc_coeff[2][1] = 0x78ab; + csc_coeff[2][2] = 0x2000; + csc_coeff[2][3] = 0x0200; + + csc_scale = 0; + coeff_selected = true; + } + } + } + + if (!coeff_selected) { + csc_coeff[0][0] = 0x2000; + csc_coeff[0][1] = 0x0000; + csc_coeff[0][2] = 0x0000; + csc_coeff[0][3] = 0x0000; + + csc_coeff[1][0] = 0x0000; + csc_coeff[1][1] = 0x2000; + csc_coeff[1][2] = 0x0000; + csc_coeff[1][3] = 0x0000; + + csc_coeff[2][0] = 0x0000; + csc_coeff[2][1] = 0x0000; + csc_coeff[2][2] = 0x2000; + csc_coeff[2][3] = 0x0000; + + csc_scale = 1; + } + + /* Update CSC parameters in HDMI CSC registers */ + hdmi_writeb((unsigned char)(csc_coeff[0][0] & 0xFF), + HDMI_CSC_COEF_A1_LSB); + hdmi_writeb((unsigned char)(csc_coeff[0][0] >> 8), + HDMI_CSC_COEF_A1_MSB); + hdmi_writeb((unsigned char)(csc_coeff[0][1] & 0xFF), + HDMI_CSC_COEF_A2_LSB); + hdmi_writeb((unsigned char)(csc_coeff[0][1] >> 8), + HDMI_CSC_COEF_A2_MSB); + hdmi_writeb((unsigned char)(csc_coeff[0][2] & 0xFF), + HDMI_CSC_COEF_A3_LSB); + hdmi_writeb((unsigned char)(csc_coeff[0][2] >> 8), + HDMI_CSC_COEF_A3_MSB); + hdmi_writeb((unsigned char)(csc_coeff[0][3] & 0xFF), + HDMI_CSC_COEF_A4_LSB); + hdmi_writeb((unsigned char)(csc_coeff[0][3] >> 8), + HDMI_CSC_COEF_A4_MSB); + + hdmi_writeb((unsigned char)(csc_coeff[1][0] & 0xFF), + HDMI_CSC_COEF_B1_LSB); + hdmi_writeb((unsigned char)(csc_coeff[1][0] >> 8), + HDMI_CSC_COEF_B1_MSB); + hdmi_writeb((unsigned char)(csc_coeff[1][1] & 0xFF), + HDMI_CSC_COEF_B2_LSB); + hdmi_writeb((unsigned char)(csc_coeff[1][1] >> 8), + HDMI_CSC_COEF_B2_MSB); + hdmi_writeb((unsigned char)(csc_coeff[1][2] & 0xFF), + HDMI_CSC_COEF_B3_LSB); + hdmi_writeb((unsigned char)(csc_coeff[1][2] >> 8), + HDMI_CSC_COEF_B3_MSB); + hdmi_writeb((unsigned char)(csc_coeff[1][3] & 0xFF), + HDMI_CSC_COEF_B4_LSB); + hdmi_writeb((unsigned char)(csc_coeff[1][3] >> 8), + HDMI_CSC_COEF_B4_MSB); + + hdmi_writeb((unsigned char)(csc_coeff[2][0] & 0xFF), + HDMI_CSC_COEF_C1_LSB); + hdmi_writeb((unsigned char)(csc_coeff[2][0] >> 8), + HDMI_CSC_COEF_C1_MSB); + hdmi_writeb((unsigned char)(csc_coeff[2][1] & 0xFF), + HDMI_CSC_COEF_C2_LSB); + hdmi_writeb((unsigned char)(csc_coeff[2][1] >> 8), + HDMI_CSC_COEF_C2_MSB); + hdmi_writeb((unsigned char)(csc_coeff[2][2] & 0xFF), + HDMI_CSC_COEF_C3_LSB); + hdmi_writeb((unsigned char)(csc_coeff[2][2] >> 8), + HDMI_CSC_COEF_C3_MSB); + hdmi_writeb((unsigned char)(csc_coeff[2][3] & 0xFF), + HDMI_CSC_COEF_C4_LSB); + hdmi_writeb((unsigned char)(csc_coeff[2][3] >> 8), + HDMI_CSC_COEF_C4_MSB); + + val = hdmi_readb(HDMI_CSC_SCALE); + val &= ~HDMI_CSC_SCALE_CSCSCALE_MASK; + val |= csc_scale & HDMI_CSC_SCALE_CSCSCALE_MASK; + hdmi_writeb(val, HDMI_CSC_SCALE); +} + +void hdmi_video_csc(struct mxc_hdmi *hdmi) +{ + int color_depth = 0; + int interpolation = HDMI_CSC_CFG_INTMODE_DISABLE; + int decimation = 0; + u8 val; + + /* YCC422 interpolation to 444 mode */ + if (isColorSpaceInterpolation(hdmi)) + interpolation = HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA1; + else if (isColorSpaceDecimation(hdmi)) + decimation = HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA1; + + if (hdmi->hdmi_data.enc_color_depth == 8) + color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP; + else if (hdmi->hdmi_data.enc_color_depth == 10) + color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_30BPP; + else if (hdmi->hdmi_data.enc_color_depth == 12) + color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_36BPP; + else if (hdmi->hdmi_data.enc_color_depth == 16) + color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_48BPP; + else + return; + + /*configure the CSC registers */ + hdmi_writeb(interpolation | decimation, HDMI_CSC_CFG); + val = hdmi_readb(HDMI_CSC_SCALE); + val &= ~HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK; + val |= color_depth; + hdmi_writeb(val, HDMI_CSC_SCALE); + + update_csc_coeffs(hdmi); +} + +/*! + * HDMI video packetizer is used to packetize the data. + * for example, if input is YCC422 mode or repeater is used, + * data should be repacked this module can be bypassed. + */ +void hdmi_video_packetize(struct mxc_hdmi *hdmi) +{ + unsigned int color_depth = 0; + unsigned int remap_size = HDMI_VP_REMAP_YCC422_16bit; + unsigned int output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_PP; + struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data; + u8 val; + + if (hdmi_data->enc_out_format == RGB + || hdmi_data->enc_out_format == YCBCR444) { + if (hdmi_data->enc_color_depth == 0) + output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS; + else if (hdmi_data->enc_color_depth == 8) { + color_depth = 4; + output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS; + } else if (hdmi_data->enc_color_depth == 10) + color_depth = 5; + else if (hdmi_data->enc_color_depth == 12) + color_depth = 6; + else if (hdmi_data->enc_color_depth == 16) + color_depth = 7; + else + return; + } else if (hdmi_data->enc_out_format == YCBCR422_8BITS) { + if (hdmi_data->enc_color_depth == 0 || + hdmi_data->enc_color_depth == 8) + remap_size = HDMI_VP_REMAP_YCC422_16bit; + else if (hdmi_data->enc_color_depth == 10) + remap_size = HDMI_VP_REMAP_YCC422_20bit; + else if (hdmi_data->enc_color_depth == 12) + remap_size = HDMI_VP_REMAP_YCC422_24bit; + else + return; + output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422; + } else + return; + + /* set the packetizer registers */ + val = ((color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) & + HDMI_VP_PR_CD_COLOR_DEPTH_MASK) | + ((hdmi_data->pix_repet_factor << + HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET) & + HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK); + hdmi_writeb(val, HDMI_VP_PR_CD); + + val = hdmi_readb(HDMI_VP_STUFF); + val &= ~HDMI_VP_STUFF_PR_STUFFING_MASK; + val |= HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE; + hdmi_writeb(val, HDMI_VP_STUFF); + + /* Data from pixel repeater block */ + if (hdmi_data->pix_repet_factor > 1) { + val = hdmi_readb(HDMI_VP_CONF); + val &= ~(HDMI_VP_CONF_PR_EN_MASK | + HDMI_VP_CONF_BYPASS_SELECT_MASK); + val |= HDMI_VP_CONF_PR_EN_ENABLE | + HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER; + hdmi_writeb(val, HDMI_VP_CONF); + } else { /* data from packetizer block */ + val = hdmi_readb(HDMI_VP_CONF); + val &= ~(HDMI_VP_CONF_PR_EN_MASK | + HDMI_VP_CONF_BYPASS_SELECT_MASK); + val |= HDMI_VP_CONF_PR_EN_DISABLE | + HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER; + hdmi_writeb(val, HDMI_VP_CONF); + } + + val = hdmi_readb(HDMI_VP_STUFF); + val &= ~HDMI_VP_STUFF_IDEFAULT_PHASE_MASK; + val |= 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET; + hdmi_writeb(val, HDMI_VP_STUFF); + + hdmi_writeb(remap_size, HDMI_VP_REMAP); + + if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_PP) { + val = hdmi_readb(HDMI_VP_CONF); + val &= ~(HDMI_VP_CONF_BYPASS_EN_MASK | + HDMI_VP_CONF_PP_EN_ENMASK | + HDMI_VP_CONF_YCC422_EN_MASK); + val |= HDMI_VP_CONF_BYPASS_EN_DISABLE | + HDMI_VP_CONF_PP_EN_ENABLE | + HDMI_VP_CONF_YCC422_EN_DISABLE; + hdmi_writeb(val, HDMI_VP_CONF); + } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422) { + val = hdmi_readb(HDMI_VP_CONF); + val &= ~(HDMI_VP_CONF_BYPASS_EN_MASK | + HDMI_VP_CONF_PP_EN_ENMASK | + HDMI_VP_CONF_YCC422_EN_MASK); + val |= HDMI_VP_CONF_BYPASS_EN_DISABLE | + HDMI_VP_CONF_PP_EN_DISABLE | + HDMI_VP_CONF_YCC422_EN_ENABLE; + hdmi_writeb(val, HDMI_VP_CONF); + } else if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS) { + val = hdmi_readb(HDMI_VP_CONF); + val &= ~(HDMI_VP_CONF_BYPASS_EN_MASK | + HDMI_VP_CONF_PP_EN_ENMASK | + HDMI_VP_CONF_YCC422_EN_MASK); + val |= HDMI_VP_CONF_BYPASS_EN_ENABLE | + HDMI_VP_CONF_PP_EN_DISABLE | + HDMI_VP_CONF_YCC422_EN_DISABLE; + hdmi_writeb(val, HDMI_VP_CONF); + } else { + return; + } + + val = hdmi_readb(HDMI_VP_STUFF); + val &= ~(HDMI_VP_STUFF_PP_STUFFING_MASK | + HDMI_VP_STUFF_YCC422_STUFFING_MASK); + val |= HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE | + HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE; + hdmi_writeb(val, HDMI_VP_STUFF); + + val = hdmi_readb(HDMI_VP_CONF); + val &= ~HDMI_VP_CONF_OUTPUT_SELECTOR_MASK; + val |= output_select; + hdmi_writeb(val, HDMI_VP_CONF); +} + +void hdmi_video_force_output(struct mxc_hdmi *hdmi, unsigned char force) +{ + u8 val; + + if (force == TRUE) { + hdmi_writeb(0x00, HDMI_FC_DBGTMDS2); /* R */ + hdmi_writeb(0x00, HDMI_FC_DBGTMDS1); /* G */ + hdmi_writeb(0xFF, HDMI_FC_DBGTMDS0); /* B */ + val = hdmi_readb(HDMI_FC_DBGFORCE); + val |= HDMI_FC_DBGFORCE_FORCEVIDEO; + hdmi_writeb(val, HDMI_FC_DBGFORCE); + } else { + val = hdmi_readb(HDMI_FC_DBGFORCE); + val &= ~HDMI_FC_DBGFORCE_FORCEVIDEO; + hdmi_writeb(val, HDMI_FC_DBGFORCE); + hdmi_writeb(0x00, HDMI_FC_DBGTMDS2); /* R */ + hdmi_writeb(0x00, HDMI_FC_DBGTMDS1); /* G */ + hdmi_writeb(0x00, HDMI_FC_DBGTMDS0); /* B */ + } +} + +static inline void hdmi_phy_test_clear(struct mxc_hdmi *hdmi, + unsigned char bit) +{ + u8 val = hdmi_readb(HDMI_PHY_TST0); + val &= ~HDMI_PHY_TST0_TSTCLR_MASK; + val |= (bit << HDMI_PHY_TST0_TSTCLR_OFFSET) & + HDMI_PHY_TST0_TSTCLR_MASK; + hdmi_writeb(val, HDMI_PHY_TST0); +} + +static inline void hdmi_phy_test_enable(struct mxc_hdmi *hdmi, + unsigned char bit) +{ + u8 val = hdmi_readb(HDMI_PHY_TST0); + val &= ~HDMI_PHY_TST0_TSTEN_MASK; + val |= (bit << HDMI_PHY_TST0_TSTEN_OFFSET) & + HDMI_PHY_TST0_TSTEN_MASK; + hdmi_writeb(val, HDMI_PHY_TST0); +} + +static inline void hdmi_phy_test_clock(struct mxc_hdmi *hdmi, + unsigned char bit) +{ + u8 val = hdmi_readb(HDMI_PHY_TST0); + val &= ~HDMI_PHY_TST0_TSTCLK_MASK; + val |= (bit << HDMI_PHY_TST0_TSTCLK_OFFSET) & + HDMI_PHY_TST0_TSTCLK_MASK; + hdmi_writeb(val, HDMI_PHY_TST0); +} + +static inline void hdmi_phy_test_din(struct mxc_hdmi *hdmi, + unsigned char bit) +{ + hdmi_writeb(bit, HDMI_PHY_TST1); +} + +static inline void hdmi_phy_test_dout(struct mxc_hdmi *hdmi, + unsigned char bit) +{ + hdmi_writeb(bit, HDMI_PHY_TST2); +} + +int hdmi_phy_wait_i2c_done(struct mxc_hdmi *hdmi, int msec) +{ + unsigned char val = 0; + val = hdmi_readb(HDMI_IH_I2CMPHY_STAT0) & 0x3; + while (val == 0) { + udelay(1000); + if (msec-- == 0) + return FALSE; + val = hdmi_readb(HDMI_IH_I2CMPHY_STAT0) & 0x3; + } + return TRUE; +} + +int hdmi_phy_i2c_write(struct mxc_hdmi *hdmi, unsigned short data, + unsigned char addr) +{ + hdmi_writeb(0xFF, HDMI_IH_I2CMPHY_STAT0); + hdmi_writeb(addr, HDMI_PHY_I2CM_ADDRESS_ADDR); + hdmi_writeb((unsigned char)(data >> 8), + HDMI_PHY_I2CM_DATAO_1_ADDR); + hdmi_writeb((unsigned char)(data >> 0), + HDMI_PHY_I2CM_DATAO_0_ADDR); + hdmi_writeb(HDMI_PHY_I2CM_OPERATION_ADDR_WRITE, + HDMI_PHY_I2CM_OPERATION_ADDR); + hdmi_phy_wait_i2c_done(hdmi, 1000); + return TRUE; +} + +unsigned short hdmi_phy_i2c_read(struct mxc_hdmi *hdmi, unsigned char addr) +{ + unsigned short data; + unsigned char msb = 0, lsb = 0; + hdmi_writeb(0xFF, HDMI_IH_I2CMPHY_STAT0); + hdmi_writeb(addr, HDMI_PHY_I2CM_ADDRESS_ADDR); + hdmi_writeb(HDMI_PHY_I2CM_OPERATION_ADDR_READ, + HDMI_PHY_I2CM_OPERATION_ADDR); + hdmi_phy_wait_i2c_done(hdmi, 1000); + msb = hdmi_readb(HDMI_PHY_I2CM_DATAI_1_ADDR); + lsb = hdmi_readb(HDMI_PHY_I2CM_DATAI_0_ADDR); + data = (msb << 8) | lsb; + return data; +} + +int hdmi_phy_i2c_write_verify(struct mxc_hdmi *hdmi, unsigned short data, + unsigned char addr) +{ + unsigned short val = 0; + hdmi_phy_i2c_write(hdmi, data, addr); + val = hdmi_phy_i2c_read(hdmi, addr); + if (val != data) + return FALSE; + return TRUE; +} + +int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, + unsigned char cRes, int cscOn, int audioOn, + int cecOn, int hdcpOn) +{ + u8 val; + + /* color resolution 0 is 8 bit colour depth */ + if (cRes == 0) + cRes = 8; + + if (pRep != 0) + return FALSE; + else if (cRes != 8 && cRes != 12) + return FALSE; + + if (cscOn) + val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH; + else + val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS; + + hdmi_writeb(val, HDMI_MC_FLOWCTRL); + + /* clock gate == 0 => turn on modules */ + val = hdcpOn ? HDMI_MC_CLKDIS_HDCPCLK_DISABLE_ENABLE : + HDMI_MC_CLKDIS_HDCPCLK_DISABLE_DISABLE; + val |= HDMI_MC_CLKDIS_PIXELCLK_DISABLE_ENABLE; + val |= HDMI_MC_CLKDIS_TMDSCLK_DISABLE_ENABLE; + val |= (pRep > 0) ? HDMI_MC_CLKDIS_PREPCLK_DISABLE_ENABLE : + HDMI_MC_CLKDIS_PREPCLK_DISABLE_DISABLE; + val |= cecOn ? HDMI_MC_CLKDIS_CECCLK_DISABLE_ENABLE : + HDMI_MC_CLKDIS_CECCLK_DISABLE_DISABLE; + val |= cscOn ? HDMI_MC_CLKDIS_CSCCLK_DISABLE_ENABLE : + HDMI_MC_CLKDIS_CSCCLK_DISABLE_DISABLE; + val |= audioOn ? HDMI_MC_CLKDIS_AUDCLK_DISABLE_ENABLE : + HDMI_MC_CLKDIS_AUDCLK_DISABLE_DISABLE; + hdmi_writeb(val, HDMI_MC_CLKDIS); + + /* gen2 tx power off */ + val = hdmi_readb(HDMI_PHY_CONF0); + val &= ~HDMI_PHY_CONF0_GEN2_TXPWRON_MASK; + val |= HDMI_PHY_CONF0_GEN2_TXPWRON_POWER_OFF; + hdmi_writeb(val, HDMI_PHY_CONF0); + + /* gen2 pddq */ + val = hdmi_readb(HDMI_PHY_CONF0); + val &= ~HDMI_PHY_CONF0_GEN2_PDDQ_MASK; + val |= HDMI_PHY_CONF0_GEN2_PDDQ_ENABLE; + hdmi_writeb(val, HDMI_PHY_CONF0); + + /* PHY reset */ + hdmi_writeb(HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ); + hdmi_writeb(HDMI_MC_PHYRSTZ_ASSERT, HDMI_MC_PHYRSTZ); + + hdmi_writeb(HDMI_MC_HEACPHY_RST_ASSERT, + HDMI_MC_HEACPHY_RST); + + hdmi_phy_test_clear(hdmi, 1); + hdmi_writeb(HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2, + HDMI_PHY_I2CM_SLAVE_ADDR); + hdmi_phy_test_clear(hdmi, 0); + + if (hdmi->hdmi_data.video_mode.mPixelClock < 0) { + dev_dbg(&hdmi->pdev->dev, "Pixel clock (%d) must be positive\n", + hdmi->hdmi_data.video_mode.mPixelClock); + return FALSE; + } + + if (hdmi->hdmi_data.video_mode.mPixelClock <= 45250000) { + switch (cRes) { + case 8: + /* PLL/MPLL Cfg */ + hdmi_phy_i2c_write(hdmi, 0x01e0, 0x06); + hdmi_phy_i2c_write(hdmi, 0x0000, 0x15); /* GMPCTRL */ + break; + case 10: + hdmi_phy_i2c_write(hdmi, 0x21e1, 0x06); + hdmi_phy_i2c_write(hdmi, 0x0000, 0x15); + break; + case 12: + hdmi_phy_i2c_write(hdmi, 0x41e2, 0x06); + hdmi_phy_i2c_write(hdmi, 0x0000, 0x15); + break; + default: + return FALSE; + } + } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 92500000) { + switch (cRes) { + case 8: + hdmi_phy_i2c_write(hdmi, 0x0140, 0x06); + hdmi_phy_i2c_write(hdmi, 0x0005, 0x15); + break; + case 10: + hdmi_phy_i2c_write(hdmi, 0x2141, 0x06); + hdmi_phy_i2c_write(hdmi, 0x0005, 0x15); + break; + case 12: + hdmi_phy_i2c_write(hdmi, 0x4142, 0x06); + hdmi_phy_i2c_write(hdmi, 0x0005, 0x15); + default: + return FALSE; + } + } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 148500000) { + switch (cRes) { + case 8: + hdmi_phy_i2c_write(hdmi, 0x00a0, 0x06); + hdmi_phy_i2c_write(hdmi, 0x000a, 0x15); + break; + case 10: + hdmi_phy_i2c_write(hdmi, 0x20a1, 0x06); + hdmi_phy_i2c_write(hdmi, 0x000a, 0x15); + break; + case 12: + hdmi_phy_i2c_write(hdmi, 0x40a2, 0x06); + hdmi_phy_i2c_write(hdmi, 0x000a, 0x15); + default: + return FALSE; + } + } else { + switch (cRes) { + case 8: + hdmi_phy_i2c_write(hdmi, 0x00a0, 0x06); + hdmi_phy_i2c_write(hdmi, 0x000a, 0x15); + break; + case 10: + hdmi_phy_i2c_write(hdmi, 0x2001, 0x06); + hdmi_phy_i2c_write(hdmi, 0x000f, 0x15); + break; + case 12: + hdmi_phy_i2c_write(hdmi, 0x4002, 0x06); + hdmi_phy_i2c_write(hdmi, 0x000f, 0x15); + default: + return FALSE; + } + } + + if (hdmi->hdmi_data.video_mode.mPixelClock <= 54000000) { + switch (cRes) { + case 8: + hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); /* CURRCTRL */ + break; + case 10: + hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); + break; + case 12: + hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); + break; + default: + return FALSE; + } + } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 58400000) { + switch (cRes) { + case 8: + hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); + break; + case 10: + hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); + break; + case 12: + hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); + break; + default: + return FALSE; + } + } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 72000000) { + switch (cRes) { + case 8: + hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); + break; + case 10: + hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); + break; + case 12: + hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); + break; + default: + return FALSE; + } + } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 74250000) { + switch (cRes) { + case 8: + hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); + break; + case 10: + hdmi_phy_i2c_write(hdmi, 0x0b5c, 0x10); + break; + case 12: + hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); + break; + default: + return FALSE; + } + } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 118800000) { + switch (cRes) { + case 8: + hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); + break; + case 10: + hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); + break; + case 12: + hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); + break; + default: + return FALSE; + } + } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 216000000) { + switch (cRes) { + case 8: + hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); + break; + case 10: + hdmi_phy_i2c_write(hdmi, 0x0b5c, 0x10); + break; + case 12: + hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); + break; + default: + return FALSE; + } + } else { + dev_err(&hdmi->pdev->dev, + "Pixel clock %d - unsupported by HDMI\n", + hdmi->hdmi_data.video_mode.mPixelClock); + return FALSE; + } + + hdmi_phy_i2c_write(hdmi, 0x0000, 0x13); /* PLLPHBYCTRL */ + hdmi_phy_i2c_write(hdmi, 0x0006, 0x17); + /* RESISTANCE TERM 133Ohm Cfg */ + hdmi_phy_i2c_write(hdmi, 0x0005, 0x19); /* TXTERM */ + /* PREEMP Cgf 0.00 */ + hdmi_phy_i2c_write(hdmi, 0x8009, 0x09); /* CKSYMTXCTRL */ + /* TX/CK LVL 10 */ + hdmi_phy_i2c_write(hdmi, 0x0210, 0x0E); /* VLEVCTRL */ + /* REMOVE CLK TERM */ + hdmi_phy_i2c_write(hdmi, 0x8000, 0x05); /* CKCALCTRL */ + + if (hdmi->hdmi_data.video_mode.mPixelClock > 148500000) { + hdmi_phy_i2c_write(hdmi, 0x800b, 0x09); + hdmi_phy_i2c_write(hdmi, 0x0129, 0x0E); + } + + /* gen2 tx power on */ + val = hdmi_readb(HDMI_PHY_CONF0); + val &= ~HDMI_PHY_CONF0_GEN2_TXPWRON_MASK; + val |= HDMI_PHY_CONF0_GEN2_TXPWRON_POWER_ON; + hdmi_writeb(val, HDMI_PHY_CONF0); + + val = hdmi_readb(HDMI_PHY_CONF0); + val &= ~HDMI_PHY_CONF0_GEN2_PDDQ_MASK; + val |= HDMI_PHY_CONF0_GEN2_PDDQ_DISABLE; + hdmi_writeb(val, HDMI_PHY_CONF0); + + udelay(1000); + + if ((hdmi_readb(HDMI_PHY_STAT0) & 0x01) == 0) + return FALSE; + + return TRUE; +} + +void hdmi_phy_init(struct mxc_hdmi *hdmi, unsigned char de) +{ + u8 val; + + /* set the DE polarity */ + val = (de << HDMI_PHY_CONF0_SELDATAENPOL_OFFSET) & + HDMI_PHY_CONF0_SELDATAENPOL_MASK; + /* set ENHPDRXSENSE to 1 */ + val |= HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE; + /* set the interface control to 0 */ + val |= (0 << HDMI_PHY_CONF0_SELDIPIF_OFFSET) & + HDMI_PHY_CONF0_SELDIPIF_MASK; + /* enable TMDS output */ + val |= (1 << HDMI_PHY_CONF0_ENTMDS_OFFSET) & + HDMI_PHY_CONF0_ENTMDS_MASK; + /* PHY power enable */ + val |= (1 << HDMI_PHY_CONF0_PDZ_OFFSET) & + HDMI_PHY_CONF0_PDZ_MASK; + hdmi_writeb(val, HDMI_PHY_CONF0); + + /* TODO: Enable CSC */ + hdmi_phy_configure(hdmi, 0, 8, FALSE, TRUE, TRUE, FALSE); +} + +void hdmi_tx_hdcp_config(struct mxc_hdmi *hdmi) +{ + u8 de, val; + + if (hdmi->hdmi_data.video_mode.mDataEnablePolarity) + de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH; + else + de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW; + + /* disable rx detect */ + val = hdmi_readb(HDMI_A_HDCPCFG0); + val &= HDMI_A_HDCPCFG0_RXDETECT_MASK; + val |= HDMI_A_HDCPCFG0_RXDETECT_DISABLE; + hdmi_writeb(val, HDMI_A_HDCPCFG0); + + val = hdmi_readb(HDMI_A_VIDPOLCFG); + val &= HDMI_A_VIDPOLCFG_DATAENPOL_MASK; + val |= de; + hdmi_writeb(val, HDMI_A_VIDPOLCFG); + + val = hdmi_readb(HDMI_A_HDCPCFG1); + val &= HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK; + val |= HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE; + hdmi_writeb(val, HDMI_A_HDCPCFG1); +} + +void preamble_filter_set(struct mxc_hdmi *hdmi, unsigned char value, + unsigned char channel) +{ + if (channel == 0) + hdmi_writeb(value, HDMI_FC_CH0PREAM); + else if (channel == 1) + hdmi_writeb(value, HDMI_FC_CH1PREAM); + else if (channel == 2) + hdmi_writeb(value, HDMI_FC_CH2PREAM); + else + + return; +} + +static void hdmi_config_AVI(struct mxc_hdmi *hdmi) +{ + u8 val; + u8 pix_fmt; + u8 act_ratio, coded_ratio, colorimetry, ext_colorimetry; + struct fb_videomode mode; + const struct fb_videomode *edid_mode; + bool aspect_16_9; + + dev_dbg(&hdmi->pdev->dev, "set up AVI frame\n"); + + fb_var_to_videomode(&mode, &hdmi->fbi->var); + /* Use mode from list extracted from EDID to get aspect ratio */ + if (!list_empty(&hdmi->fbi->modelist)) { + edid_mode = fb_find_nearest_mode(&mode, &hdmi->fbi->modelist); + if (edid_mode->vmode & FB_VMODE_ASPECT_16_9) + aspect_16_9 = true; + else + aspect_16_9 = false; + } else + aspect_16_9 = false; + + /******************************************** + * AVI Data Byte 1 + ********************************************/ + if (hdmi->edid_cfg.cea_ycbcr444) + pix_fmt = HDMI_FC_AVICONF0_PIX_FMT_YCBCR444; + else if (hdmi->edid_cfg.cea_ycbcr422) + pix_fmt = HDMI_FC_AVICONF0_PIX_FMT_YCBCR422; + else + pix_fmt = HDMI_FC_AVICONF0_PIX_FMT_RGB; + + /* + * Active format identification data is present in the AVI InfoFrame. + * No scan info, no bar data + */ + val = pix_fmt | + HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT | + HDMI_FC_AVICONF0_BAR_DATA_NO_DATA | + HDMI_FC_AVICONF0_SCAN_INFO_NODATA; + + hdmi_writeb(val, HDMI_FC_AVICONF0); + + /******************************************** + * AVI Data Byte 2 + ********************************************/ + + /* Set the Aspect Ratio */ + if (aspect_16_9) { + act_ratio = HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_16_9; + coded_ratio = HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_16_9; + } else { + act_ratio = HDMI_FC_AVICONF1_ACTIVE_ASPECT_RATIO_4_3; + coded_ratio = HDMI_FC_AVICONF1_CODED_ASPECT_RATIO_4_3; + } + + /* Set up colorimetry */ + if (hdmi->hdmi_data.enc_out_format == XVYCC444) { + colorimetry = HDMI_FC_AVICONF1_COLORIMETRY_EXTENDED_INFO; + if (hdmi->hdmi_data.colorimetry == eITU601) + ext_colorimetry = + HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC601; + else /* hdmi->hdmi_data.colorimetry == eITU709 */ + ext_colorimetry = + HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC709; + } else if (hdmi->hdmi_data.enc_out_format != RGB) { + if (hdmi->hdmi_data.colorimetry == eITU601) + colorimetry = HDMI_FC_AVICONF1_COLORIMETRY_SMPTE; + else /* hdmi->hdmi_data.colorimetry == eITU709 */ + colorimetry = HDMI_FC_AVICONF1_COLORIMETRY_ITUR; + ext_colorimetry = HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC601; + } else { /* Carries no data */ + colorimetry = HDMI_FC_AVICONF1_COLORIMETRY_NO_DATA; + ext_colorimetry = HDMI_FC_AVICONF2_EXT_COLORIMETRY_XVYCC601; + } + + val = colorimetry | coded_ratio | act_ratio; + hdmi_writeb(val, HDMI_FC_AVICONF1); + + /******************************************** + * AVI Data Byte 3 + ********************************************/ + + val = HDMI_FC_AVICONF2_IT_CONTENT_NO_DATA | ext_colorimetry | + HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT | + HDMI_FC_AVICONF2_SCALING_NONE; + hdmi_writeb(val, HDMI_FC_AVICONF2); + + /******************************************** + * AVI Data Byte 4 + ********************************************/ + hdmi_writeb(hdmi->vic, HDMI_FC_AVIVID); + + /******************************************** + * AVI Data Byte 5 + ********************************************/ + + /* Set up input and output pixel repetition */ + val = (((hdmi->hdmi_data.video_mode.mPixelRepetitionInput + 1) << + HDMI_FC_PRCONF_INCOMING_PR_FACTOR_OFFSET) & + HDMI_FC_PRCONF_INCOMING_PR_FACTOR_MASK) | + ((hdmi->hdmi_data.video_mode.mPixelRepetitionOutput << + HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_OFFSET) & + HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK); + hdmi_writeb(val, HDMI_FC_PRCONF); + + /* IT Content and quantization range = don't care */ + val = HDMI_FC_AVICONF2_IT_CONTENT_TYPE_GRAPHICS | + HDMI_FC_AVICONF3_QUANT_RANGE_LIMITED; + hdmi_writeb(val, HDMI_FC_AVICONF3); + + /******************************************** + * AVI Data Bytes 6-13 + ********************************************/ + hdmi_writeb(0, HDMI_FC_AVIETB0); + hdmi_writeb(0, HDMI_FC_AVIETB1); + hdmi_writeb(0, HDMI_FC_AVISBB0); + hdmi_writeb(0, HDMI_FC_AVISBB1); + hdmi_writeb(0, HDMI_FC_AVIELB0); + hdmi_writeb(0, HDMI_FC_AVIELB1); + hdmi_writeb(0, HDMI_FC_AVISRB0); + hdmi_writeb(0, HDMI_FC_AVISRB1); +} + +/*! + * this submodule is responsible for the video/audio data composition. + */ +void hdmi_av_composer(struct mxc_hdmi *hdmi) +{ + unsigned char i = 0; + u8 val; + struct fb_info *fbi = hdmi->fbi; + struct fb_videomode fb_mode; + struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode; + int hblank, vblank; + + fb_var_to_videomode(&fb_mode, &fbi->var); + + vmode->mHSyncPolarity = + (fb_mode.sync & FB_SYNC_HOR_HIGH_ACT) ? TRUE : FALSE; + vmode->mVSyncPolarity = + (fb_mode.sync & FB_SYNC_VERT_HIGH_ACT) ? TRUE : FALSE; + vmode->mInterlaced = + (fb_mode.vmode & FB_VMODE_INTERLACED) ? TRUE : FALSE; + vmode->mPixelClock = (fb_mode.xres + fb_mode.left_margin + + fb_mode.right_margin + fb_mode.hsync_len) * (fb_mode.yres + + fb_mode.upper_margin + fb_mode.lower_margin + + fb_mode.vsync_len) * fb_mode.refresh; + + dev_dbg(&hdmi->pdev->dev, "final pixclk = %d\n", vmode->mPixelClock); + + /* Set up HDMI_FC_INVIDCONF */ + val = ((hdmi->hdmi_data.hdcp_enable == TRUE) ? + HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE : + HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE); + val |= ((vmode->mVSyncPolarity == TRUE) ? + HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH : + HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW); + val |= ((vmode->mHSyncPolarity == TRUE) ? + HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH : + HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW); + val |= ((vmode->mDataEnablePolarity == TRUE) ? + HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH : + HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW); + val |= ((vmode->mHdmiDviSel == TRUE) ? + HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE : + HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE); + if (hdmi->vic == 39) + val |= HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH; + else + val |= ((vmode->mInterlaced == TRUE) ? + HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH : + HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW); + val |= ((vmode->mInterlaced == TRUE) ? + HDMI_FC_INVIDCONF_IN_I_P_INTERLACED : + HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE); + hdmi_writeb(val, HDMI_FC_INVIDCONF); + + /* Set up horizontal active pixel region width */ + hdmi_writeb(fb_mode.xres, + HDMI_FC_INHACTV0); + hdmi_writeb(fb_mode.xres >> 8, + HDMI_FC_INHACTV1); + + /* Set up horizontal blanking pixel region width */ + hblank = fb_mode.left_margin + fb_mode.right_margin + + fb_mode.hsync_len; + hdmi_writeb(hblank, HDMI_FC_INHBLANK0); + hdmi_writeb(hblank >> 8, HDMI_FC_INHBLANK1); + + /* Set up vertical blanking pixel region width */ + hdmi_writeb(fb_mode.yres, HDMI_FC_INVACTV0); + hdmi_writeb(fb_mode.yres >> 8, HDMI_FC_INVACTV1); + + /* Set up vertical blanking pixel region width */ + vblank = fb_mode.upper_margin + fb_mode.lower_margin + + fb_mode.vsync_len; + hdmi_writeb(vblank, HDMI_FC_INVBLANK); + + /* Set up HSYNC active edge delay width (in pixel clks) */ + hdmi_writeb(fb_mode.right_margin, HDMI_FC_HSYNCINDELAY0); + hdmi_writeb(fb_mode.right_margin >> 8, HDMI_FC_HSYNCINDELAY1); + + /* Set up HSYNC active pulse width (in pixel clks) */ + hdmi_writeb(fb_mode.hsync_len, HDMI_FC_HSYNCINWIDTH0); + hdmi_writeb(fb_mode.hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1); + + /* Set up VSYNC active edge delay (in pixel clks) */ + hdmi_writeb(fb_mode.lower_margin, HDMI_FC_VSYNCINDELAY); + + /* Set up VSYNC active edge delay (in pixel clks) */ + hdmi_writeb(fb_mode.vsync_len, HDMI_FC_VSYNCINWIDTH); + + /* control period minimum duration */ + hdmi_writeb(12, HDMI_FC_CTRLDUR); + hdmi_writeb(32, HDMI_FC_EXCTRLDUR); + hdmi_writeb(1, HDMI_FC_EXCTRLSPAC); + + for (i = 0; i < 3; i++) + preamble_filter_set(hdmi, (i + 1) * 11, i); + + /* configure AVI InfoFrame */ + hdmi_config_AVI(hdmi); +} + +static int mxc_hdmi_read_edid(struct mxc_hdmi *hdmi, + struct fb_info *fbi) +{ + int ret; + u8 edid_old[HDMI_EDID_LEN]; + + /* save old edid */ + memcpy(edid_old, hdmi->edid, HDMI_EDID_LEN); + + /* edid reading */ + ret = mxc_edid_read(hdmi_i2c->adapter, hdmi_i2c->addr, + hdmi->edid, &hdmi->edid_cfg, fbi); + + if (ret < 0) + return ret; + + if (!memcmp(edid_old, hdmi->edid, HDMI_EDID_LEN)) + ret = -2; + return ret; +} + +static void mxc_hdmi_poweron(struct mxc_hdmi *hdmi) +{ + struct fsl_mxc_hdmi_platform_data *plat = hdmi->pdev->dev.platform_data; + + dev_dbg(&hdmi->pdev->dev, "power on\n"); + + /* Enable pins to HDMI */ + if (plat->enable_pins) + plat->enable_pins(); +} + +static void mxc_hdmi_poweroff(struct mxc_hdmi *hdmi) +{ + struct fsl_mxc_hdmi_platform_data *plat = hdmi->pdev->dev.platform_data; + + dev_dbg(&hdmi->pdev->dev, "power off\n"); + + /* Disable pins to HDMI */ + if (plat->disable_pins) + plat->disable_pins(); +} + +static void mxc_hdmi_enable(struct mxc_hdmi *hdmi) +{ + u8 val; + + dev_dbg(&hdmi->pdev->dev, "hdmi enable\n"); + + clk_enable(hdmi->hdmi_iahb_clk); + + /* Enable HDMI PHY - Set PDDQ=0 and TXPWRON=1 */ + val = hdmi_readb(HDMI_PHY_CONF0); + val &= ~(HDMI_PHY_CONF0_GEN2_PDDQ_MASK | + HDMI_PHY_CONF0_GEN2_TXPWRON_MASK); + val |= HDMI_PHY_CONF0_GEN2_PDDQ_DISABLE | + HDMI_PHY_CONF0_GEN2_TXPWRON_POWER_ON; + hdmi_writeb(val, HDMI_PHY_CONF0); + + if (hdmi->need_mode_change && hdmi->fb_reg) { + dev_dbg(&hdmi->pdev->dev, "HDMI changing FB mode\n"); + hdmi->fbi->var.activate |= FB_ACTIVATE_FORCE; + console_lock(); + hdmi->fbi->flags |= FBINFO_MISC_USEREVENT; + fb_set_var(hdmi->fbi, &hdmi->fbi->var); + hdmi->fbi->flags &= ~FBINFO_MISC_USEREVENT; + console_unlock(); + hdmi->need_mode_change = false; + } +} + +static void mxc_hdmi_disable(struct mxc_hdmi *hdmi) +{ + u8 val; + + dev_dbg(&hdmi->pdev->dev, "hdmi disable\n"); + + /* Disable HDMI PHY - Set PDDQ=1 and TXPWRON=0 */ + val = hdmi_readb(HDMI_PHY_CONF0); + val &= ~(HDMI_PHY_CONF0_GEN2_PDDQ_MASK | + HDMI_PHY_CONF0_GEN2_TXPWRON_MASK); + val |= HDMI_PHY_CONF0_GEN2_PDDQ_ENABLE | + HDMI_PHY_CONF0_GEN2_TXPWRON_POWER_OFF; + hdmi_writeb(val, HDMI_PHY_CONF0); + + clk_disable(hdmi->hdmi_iahb_clk); +} + +static void mxc_hdmi_default_modelist(struct mxc_hdmi *hdmi) +{ + u32 i; + const struct fb_videomode *mode; + + fb_destroy_modelist(&hdmi->fbi->modelist); + + for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) { + mode = &mxc_cea_mode[i]; + if ((mode->xres == hdmi->fbi->var.xres) && + (mode->yres == hdmi->fbi->var.yres) && + !(mode->vmode & FB_VMODE_INTERLACED)) + fb_add_videomode(mode, &hdmi->fbi->modelist); + } +} + +static int mxc_hdmi_cable_connected(struct mxc_hdmi *hdmi) +{ + int ret; + struct fb_videomode m; + const struct fb_videomode *mode; + + dev_dbg(&hdmi->pdev->dev, "cable connected\n"); + + hdmi->cable_plugin = true; + + /* edid read */ + ret = mxc_hdmi_read_edid(hdmi, hdmi->fbi); + if (ret == -2) + dev_info(&hdmi->pdev->dev, "same edid\n"); + else if (hdmi->fbi->monspecs.modedb_len > 0) { + int i; + + fb_destroy_modelist(&hdmi->fbi->modelist); + + for (i = 0; i < hdmi->fbi->monspecs.modedb_len; i++) { + /* + * We might check here if mode is supported by HDMI. + * We do not currently support interlaced modes + */ + if (!(hdmi->fbi->monspecs.modedb[i].vmode + & FB_VMODE_INTERLACED)) { + dev_dbg(&hdmi->pdev->dev, "Added mode %d:", i); + dev_dbg(&hdmi->pdev->dev, + "xres = %d, yres = %d, freq = %d\n", + hdmi->fbi->monspecs.modedb[i].xres, + hdmi->fbi->monspecs.modedb[i].yres, + hdmi->fbi->monspecs.modedb[i].refresh); + fb_add_videomode(&hdmi->fbi->monspecs.modedb[i], + &hdmi->fbi->modelist); + } + } + + fb_var_to_videomode(&m, &hdmi->fbi->var); + mode = fb_find_nearest_mode(&m, + &hdmi->fbi->modelist); + + fb_videomode_to_var(&hdmi->fbi->var, mode); + hdmi->need_mode_change = true; + } else { + /* If not EDID data readed, setup default modelist */ + dev_info(&hdmi->pdev->dev, "No modes read from edid\n"); + mxc_hdmi_default_modelist(hdmi); + + fb_var_to_videomode(&m, &hdmi->fbi->var); + mode = fb_find_nearest_mode(&m, + &hdmi->fbi->modelist); + + fb_videomode_to_var(&hdmi->fbi->var, mode); + hdmi->need_mode_change = true; + } + + + return 0; +} + +static void mxc_hdmi_cable_disconnected(struct mxc_hdmi *hdmi) +{ + hdmi->cable_plugin = false; +} + +static void det_worker(struct work_struct *work) +{ + struct delayed_work *delay_work = to_delayed_work(work); + struct mxc_hdmi *hdmi = + container_of(delay_work, struct mxc_hdmi, det_work); + u32 phy_int_stat, phy_int_pol, phy_int_mask; + u8 val; + bool hdmi_disable = false; + int irq = platform_get_irq(hdmi->pdev, 0); + unsigned long flags; + + if (!hdmi->irq_enabled) { + clk_enable(hdmi->hdmi_iahb_clk); + + /* Capture status - used in det_worker ISR */ + phy_int_stat = hdmi_readb(HDMI_IH_PHY_STAT0); + if ((phy_int_stat & HDMI_IH_PHY_STAT0_HPD) == 0) { + clk_disable(hdmi->hdmi_iahb_clk); + return; /* No interrupts to handle */ + } + + dev_dbg(&hdmi->pdev->dev, "Hotplug interrupt received\n"); + + /* Unmask interrupts until handled */ + val = hdmi_readb(HDMI_PHY_MASK0); + val |= HDMI_PHY_HPD; + hdmi_writeb(val, HDMI_PHY_MASK0); + + /* Clear Hotplug interrupts */ + hdmi_writeb(HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0); + + phy_int_pol = hdmi_readb(HDMI_PHY_POL0); + + clk_disable(hdmi->hdmi_iahb_clk); + } else { + /* Use saved interrupt status, since it was cleared in IST */ + phy_int_stat = hdmi->latest_intr_stat; + phy_int_pol = hdmi_readb(HDMI_PHY_POL0); + } + + /* Re-enable HDMI irq now that our interrupts have been masked off */ + hdmi_irq_enable(irq); + + /* check cable status */ + if (phy_int_stat & HDMI_IH_PHY_STAT0_HPD) { + /* cable connection changes */ + if (phy_int_pol & HDMI_PHY_HPD) { + dev_dbg(&hdmi->pdev->dev, "EVENT=plugin\n"); + mxc_hdmi_cable_connected(hdmi); + mxc_hdmi_enable(hdmi); + + /* Make HPD intr active low to capture unplug event */ + val = hdmi_readb(HDMI_PHY_POL0); + val &= ~HDMI_PHY_HPD; + hdmi_writeb(val, HDMI_PHY_POL0); + } else if (!(phy_int_pol & HDMI_PHY_HPD)) { + dev_dbg(&hdmi->pdev->dev, "EVENT=plugout\n"); + mxc_hdmi_cable_disconnected(hdmi); + hdmi_disable = true; + + /* Make HPD intr active high to capture plugin event */ + val = hdmi_readb(HDMI_PHY_POL0); + val |= HDMI_PHY_HPD; + hdmi_writeb(val, HDMI_PHY_POL0); + } else + dev_dbg(&hdmi->pdev->dev, "EVENT=none?\n"); + } + + /* Lock here to ensure full powerdown sequence + * completed before next interrupt processed */ + spin_lock_irqsave(&hdmi->irq_lock, flags); + + /* Re-enable HPD interrupts */ + phy_int_mask = hdmi_readb(HDMI_PHY_MASK0); + phy_int_mask &= ~HDMI_PHY_HPD; + hdmi_writeb(phy_int_mask, HDMI_PHY_MASK0); + + if (hdmi_disable) + mxc_hdmi_disable(hdmi); + + spin_unlock_irqrestore(&hdmi->irq_lock, flags); +} + +static irqreturn_t mxc_hdmi_hotplug(int irq, void *data) +{ + struct mxc_hdmi *hdmi = data; + unsigned int ret; + u8 val, intr_stat; + unsigned long flags; + + spin_lock_irqsave(&hdmi->irq_lock, flags); + + /* + * We have to disable the irq, rather than just masking + * off the HDMI interrupts using HDMI registers. This is + * because the HDMI iahb clock is required to be on to + * access the HDMI registers, and we cannot enable it + * in an IST. This IRQ will be re-enabled in the + * interrupt handler workqueue function. + */ + ret = hdmi_irq_disable(irq); + if (ret == IRQ_DISABLE_FAIL) { + /* Capture status - used in det_worker ISR */ + intr_stat = hdmi_readb(HDMI_IH_PHY_STAT0); + if ((intr_stat & HDMI_IH_PHY_STAT0_HPD) == 0) { + hdmi_irq_enable(irq); + spin_unlock_irqrestore(&hdmi->irq_lock, flags); + return IRQ_HANDLED; + } + dev_dbg(&hdmi->pdev->dev, "Hotplug interrupt received\n"); + hdmi->latest_intr_stat = intr_stat; + + /* Unmask interrupts until handled */ + val = hdmi_readb(HDMI_PHY_MASK0); + val |= HDMI_PHY_HPD; + hdmi_writeb(val, HDMI_PHY_MASK0); + + /* Clear Hotplug interrupts */ + hdmi_writeb(HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0); + + hdmi->irq_enabled = true; + } else + hdmi->irq_enabled = false; + + schedule_delayed_work(&(hdmi->det_work), msecs_to_jiffies(20)); + + spin_unlock_irqrestore(&hdmi->irq_lock, flags); + + return IRQ_HANDLED; +} + +static int mxc_hdmi_setup(struct mxc_hdmi *hdmi) +{ + struct fb_videomode m; + const struct fb_videomode *edid_mode; + + fb_var_to_videomode(&m, &hdmi->fbi->var); + if (!list_empty(&hdmi->fbi->modelist)) { + edid_mode = fb_find_nearest_mode(&m, &hdmi->fbi->modelist); + + hdmi->vic = mxc_edid_mode_to_vic(edid_mode); + } else + hdmi->vic = 0; + + if (hdmi->vic == 0) { + dev_dbg(&hdmi->pdev->dev, "Non-CEA mode used in HDMI\n"); + hdmi->hdmi_data.video_mode.mHdmiDviSel = FALSE; + } else + hdmi->hdmi_data.video_mode.mHdmiDviSel = TRUE; + + if ((hdmi->vic == 6) || (hdmi->vic == 7) || + (hdmi->vic == 21) || (hdmi->vic == 22) || + (hdmi->vic == 2) || (hdmi->vic == 3) || + (hdmi->vic == 17) || (hdmi->vic == 18)) + hdmi->hdmi_data.colorimetry = eITU601; + else + hdmi->hdmi_data.colorimetry = eITU709; + + if ((hdmi->vic == 10) || (hdmi->vic == 11) || + (hdmi->vic == 12) || (hdmi->vic == 13) || + (hdmi->vic == 14) || (hdmi->vic == 15) || + (hdmi->vic == 25) || (hdmi->vic == 26) || + (hdmi->vic == 27) || (hdmi->vic == 28) || + (hdmi->vic == 29) || (hdmi->vic == 30) || + (hdmi->vic == 35) || (hdmi->vic == 36) || + (hdmi->vic == 37) || (hdmi->vic == 38)) + hdmi->hdmi_data.video_mode.mPixelRepetitionOutput = 1; + else + hdmi->hdmi_data.video_mode.mPixelRepetitionOutput = 0; + + hdmi->hdmi_data.video_mode.mPixelRepetitionInput = 0; + + /* TODO: Get input format from IPU (via FB driver iface) */ + hdmi->hdmi_data.enc_in_format = RGB; + + hdmi->hdmi_data.enc_out_format = RGB; + if (hdmi->edid_cfg.hdmi_cap) { + if (hdmi->edid_cfg.cea_ycbcr444) + hdmi->hdmi_data.enc_out_format = YCBCR444; + else if (hdmi->edid_cfg.cea_ycbcr422) + hdmi->hdmi_data.enc_out_format = YCBCR422_8BITS; + } + + hdmi->hdmi_data.enc_color_depth = 8; + hdmi->hdmi_data.pix_repet_factor = 0; + hdmi->hdmi_data.hdcp_enable = 0; + hdmi->hdmi_data.video_mode.mDataEnablePolarity = TRUE; + + hdmi_video_force_output(hdmi, TRUE); + hdmi_av_composer(hdmi); + hdmi_video_packetize(hdmi); + hdmi_video_csc(hdmi); + hdmi_video_sample(hdmi); + hdmi_tx_hdcp_config(hdmi); + hdmi_phy_init(hdmi, TRUE); + hdmi_video_force_output(hdmi, FALSE); + hdmi_set_clk_regenerator(); + + return 0; +} + +static int mxc_hdmi_fb_event(struct notifier_block *nb, + unsigned long val, void *v) +{ + struct fb_event *event = v; + struct mxc_hdmi *hdmi = container_of(nb, struct mxc_hdmi, nb); + + if (strcmp(event->info->fix.id, hdmi->fbi->fix.id)) + return 0; + + switch (val) { + case FB_EVENT_FB_REGISTERED: + hdmi->fb_reg = true; + break; + case FB_EVENT_FB_UNREGISTERED: + hdmi->fb_reg = false; + break; + case FB_EVENT_MODE_CHANGE: + mxc_hdmi_setup(hdmi); + break; + case FB_EVENT_BLANK: + if (*((int *)event->data) == FB_BLANK_UNBLANK) + mxc_hdmi_poweron(hdmi); + else + mxc_hdmi_poweroff(hdmi); + break; + } + return 0; +} + +static int mxc_hdmi_disp_init(struct mxc_dispdrv_entry *disp) +{ + int ret = 0; + struct mxc_hdmi *hdmi = mxc_dispdrv_getdata(disp); + struct mxc_dispdrv_setting *setting = mxc_dispdrv_getsetting(disp); + struct fsl_mxc_hdmi_platform_data *plat = hdmi->pdev->dev.platform_data; + int irq = platform_get_irq(hdmi->pdev, 0); + bool found = false; + u8 val; + const struct fb_videomode *mode; + struct fb_videomode m; + struct fb_var_screeninfo var; + + if (!plat || irq < 0) + return -ENODEV; + + setting->dev_id = mxc_hdmi_ipu_id; + setting->disp_id = mxc_hdmi_disp_id; + setting->if_fmt = IPU_PIX_FMT_RGB24; + + hdmi->fbi = setting->fbi; + + /* Claim HDMI pins */ + if (plat->get_pins) + if (!plat->get_pins()) { + ret = -EACCES; + goto egetpins; + } + + /* Initialize HDMI */ + if (plat->init) + plat->init(mxc_hdmi_ipu_id, mxc_hdmi_disp_id); + + hdmi->hdmi_isfr_clk = clk_get(&hdmi->pdev->dev, "hdmi_isfr_clk"); + if (IS_ERR(hdmi->hdmi_isfr_clk)) { + ret = PTR_ERR(hdmi->hdmi_isfr_clk); + dev_err(&hdmi->pdev->dev, + "Unable to get HDMI clk: %d\n", ret); + goto egetclk1; + } + + ret = clk_enable(hdmi->hdmi_isfr_clk); + if (ret < 0) { + dev_err(&hdmi->pdev->dev, + "Cannot enable HDMI isfr clock: %d\n", ret); + goto erate1; + } + + hdmi->hdmi_iahb_clk = clk_get(&hdmi->pdev->dev, "hdmi_iahb_clk"); + if (IS_ERR(hdmi->hdmi_iahb_clk)) { + ret = PTR_ERR(hdmi->hdmi_iahb_clk); + dev_err(&hdmi->pdev->dev, + "Unable to get HDMI clk: %d\n", ret); + goto egetclk2; + } + + ret = clk_enable(hdmi->hdmi_iahb_clk); + if (ret < 0) { + dev_err(&hdmi->pdev->dev, + "Cannot enable HDMI iahb clock: %d\n", ret); + goto erate2; + } + + dev_dbg(&hdmi->pdev->dev, "Enabled HDMI clocks\n"); + + /* Product and revision IDs */ + dev_info(&hdmi->pdev->dev, + "Detected HDMI controller 0x%x:0x%x:0x%x:0x%x\n", + hdmi_readb(HDMI_DESIGN_ID), + hdmi_readb(HDMI_REVISION_ID), + hdmi_readb(HDMI_PRODUCT_ID0), + hdmi_readb(HDMI_PRODUCT_ID1)); + + INIT_LIST_HEAD(&hdmi->fbi->modelist); + + /* try to read edid */ + ret = mxc_hdmi_read_edid(hdmi, hdmi->fbi); + if (ret < 0) { + /* If not EDID data readed, setup default modelist */ + dev_info(&hdmi->pdev->dev, "No modes read from edid\n"); + mxc_hdmi_default_modelist(hdmi); + + fb_var_to_videomode(&m, &hdmi->fbi->var); + mode = fb_find_nearest_mode(&m, + &hdmi->fbi->modelist); + + fb_videomode_to_var(&hdmi->fbi->var, mode); + hdmi->need_mode_change = true; + } else if (hdmi->fbi->monspecs.modedb_len > 0) { + int i; + + for (i = 0; i < hdmi->fbi->monspecs.modedb_len; i++) { + /* + * We might check here if mode is supported by HDMI. + * Also, we do not currently support interlaced modes + */ + fb_videomode_to_var(&var, &hdmi->fbi->monspecs.modedb[i]); + if (!(hdmi->fbi->monspecs.modedb[i].vmode + & FB_VMODE_INTERLACED)) { + dev_dbg(&hdmi->pdev->dev, "Adding mode %d:", i); + dev_dbg(&hdmi->pdev->dev, + "xres = %d, yres = %d, freq = %d\n", + hdmi->fbi->monspecs.modedb[i].xres, + hdmi->fbi->monspecs.modedb[i].yres, + hdmi->fbi->monspecs.modedb[i].refresh); + fb_add_videomode( + &hdmi->fbi->monspecs.modedb[i], + &hdmi->fbi->modelist); + } + } + + fb_find_mode(&hdmi->fbi->var, hdmi->fbi, + setting->dft_mode_str, NULL, 0, NULL, + setting->default_bpp); + + fb_var_to_videomode(&m, &hdmi->fbi->var); + mode = fb_find_nearest_mode(&m, + &hdmi->fbi->modelist); + fb_videomode_to_var(&hdmi->fbi->var, mode); + found = 1; + + hdmi->need_mode_change = true; + } + + if (!found) { + ret = fb_find_mode(&hdmi->fbi->var, hdmi->fbi, + setting->dft_mode_str, NULL, 0, NULL, + setting->default_bpp); + if (!ret) { + ret = -EINVAL; + goto efindmode; + } + } + + spin_lock_init(&hdmi->irq_lock); + + INIT_DELAYED_WORK(&(hdmi->det_work), det_worker); + + /* Configure registers related to HDMI interrupt + * generation before registering IRQ. */ + hdmi_writeb(HDMI_PHY_HPD, HDMI_PHY_POL0); + + /* enable cable hot plug irq */ + val = ~HDMI_PHY_HPD; + hdmi_writeb(val, HDMI_PHY_MASK0); + + /* Clear Hotplug interrupts */ + hdmi_writeb(HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0); + + hdmi_writeb(~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0); + + hdmi->nb.notifier_call = mxc_hdmi_fb_event; + ret = fb_register_client(&hdmi->nb); + if (ret < 0) + goto efbclient; + + memset(&hdmi->hdmi_data, 0, sizeof(struct hdmi_data_info)); + + mxc_hdmi_setup(hdmi); + + /* Disable IAHB clock while waiting for hotplug interrupt. + * ISFR clock must remain enabled for hotplug to work. */ + clk_disable(hdmi->hdmi_iahb_clk); + + /* Initialize IRQ at HDMI core level */ + hdmi_irq_init(); + + ret = request_irq(irq, mxc_hdmi_hotplug, IRQF_SHARED, + dev_name(&hdmi->pdev->dev), hdmi); + if (ret < 0) { + dev_err(&hdmi->pdev->dev, + "Unable to request irq: %d\n", ret); + goto ereqirq; + } + + return ret; + +efbclient: + free_irq(irq, hdmi); +efindmode: +ereqirq: + clk_disable(hdmi->hdmi_iahb_clk); +erate2: + clk_put(hdmi->hdmi_iahb_clk); +egetclk2: + clk_disable(hdmi->hdmi_isfr_clk); +erate1: + clk_put(hdmi->hdmi_isfr_clk); +egetclk1: + plat->put_pins(); +egetpins: + return ret; +} + +static void mxc_hdmi_disp_deinit(struct mxc_dispdrv_entry *disp) +{ + struct mxc_hdmi *hdmi = mxc_dispdrv_getdata(disp); + struct fsl_mxc_hdmi_platform_data *plat = hdmi->pdev->dev.platform_data; + + fb_unregister_client(&hdmi->nb); + + mxc_hdmi_poweroff(hdmi); + + clk_disable(hdmi->hdmi_isfr_clk); + clk_put(hdmi->hdmi_isfr_clk); + clk_disable(hdmi->hdmi_iahb_clk); + clk_put(hdmi->hdmi_iahb_clk); + + /* Release HDMI pins */ + if (plat->put_pins) + plat->put_pins(); + + platform_device_unregister(hdmi->pdev); + + kfree(hdmi); +} + +static struct mxc_dispdrv_driver mxc_hdmi_drv = { + .name = DISPDRV_HDMI, + .init = mxc_hdmi_disp_init, + .deinit = mxc_hdmi_disp_deinit, +}; + +static int __devinit mxc_hdmi_probe(struct platform_device *pdev) +{ + struct mxc_hdmi *hdmi; + int ret = 0; + + /* Check that I2C driver is loaded and available */ + if (!hdmi_i2c) + return -ENODEV; + + hdmi = kzalloc(sizeof(*hdmi), GFP_KERNEL); + if (!hdmi) { + dev_err(&pdev->dev, "Cannot allocate device data\n"); + ret = -ENOMEM; + goto ealloc; + } + + hdmi->pdev = pdev; + + hdmi->core_pdev = platform_device_alloc("mxc_hdmi_core", -1); + if (!hdmi->core_pdev) { + pr_err("%s failed platform_device_alloc for hdmi core\n", + __func__); + ret = -ENOMEM; + goto ecore; + } + + hdmi->disp_mxc_hdmi = mxc_dispdrv_register(&mxc_hdmi_drv); + if (IS_ERR(hdmi->disp_mxc_hdmi)) { + dev_err(&pdev->dev, "Failed to register dispdrv - 0x%x\n", + (int)hdmi->disp_mxc_hdmi); + ret = (int)hdmi->disp_mxc_hdmi; + goto edispdrv; + } + mxc_dispdrv_setdata(hdmi->disp_mxc_hdmi, hdmi); + + platform_set_drvdata(pdev, hdmi); + + return 0; +edispdrv: + platform_device_put(hdmi->core_pdev); +ecore: + kfree(hdmi); +ealloc: + return ret; +} + +static int mxc_hdmi_remove(struct platform_device *pdev) +{ + struct mxc_hdmi *hdmi = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + fb_unregister_client(&hdmi->nb); + + /* No new work will be scheduled, wait for running ISR */ + free_irq(irq, hdmi); + kfree(hdmi); + + return 0; +} + +static struct platform_driver mxc_hdmi_driver = { + .probe = mxc_hdmi_probe, + .remove = mxc_hdmi_remove, + .driver = { + .name = "mxc_hdmi", + .owner = THIS_MODULE, + }, +}; + +static int __init mxc_hdmi_init(void) +{ + return platform_driver_register(&mxc_hdmi_driver); +} +module_init(mxc_hdmi_init); + +static void __exit mxc_hdmi_exit(void) +{ + platform_driver_unregister(&mxc_hdmi_driver); +} +module_exit(mxc_hdmi_exit); + + +static int __devinit mxc_hdmi_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C)) + return -ENODEV; + + hdmi_i2c = client; + + return 0; +} + +static int __devexit mxc_hdmi_i2c_remove(struct i2c_client *client) +{ + hdmi_i2c = NULL; + return 0; +} + +static const struct i2c_device_id mxc_hdmi_i2c_id[] = { + { "mxc_hdmi_i2c", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, mxc_hdmi_i2c_id); + +static struct i2c_driver mxc_hdmi_i2c_driver = { + .driver = { + .name = "mxc_hdmi_i2c", + }, + .probe = mxc_hdmi_i2c_probe, + .remove = mxc_hdmi_i2c_remove, + .id_table = mxc_hdmi_i2c_id, +}; + +static int __init mxc_hdmi_i2c_init(void) +{ + return i2c_add_driver(&mxc_hdmi_i2c_driver); +} + +static void __exit mxc_hdmi_i2c_exit(void) +{ + i2c_del_driver(&mxc_hdmi_i2c_driver); +} + +module_init(mxc_hdmi_i2c_init); +module_exit(mxc_hdmi_i2c_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index b342652135f..caf89353116 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h @@ -324,6 +324,19 @@ struct fsl_mxc_dvi_platform_data { int disp_id; }; +struct fsl_mxc_hdmi_platform_data { + void (*init) (int, int); + int (*get_pins) (void); + void (*put_pins) (void); + void (*enable_pins) (void); + void (*disable_pins) (void); +}; + +struct fsl_mxc_hdmi_core_platform_data { + int ipu_id; + int disp_id; +}; + struct fsl_mxc_ldb_platform_data { char *lvds_bg_reg; u32 ext_ref; diff --git a/include/linux/mfd/mxc-hdmi-core.h b/include/linux/mfd/mxc-hdmi-core.h new file mode 100644 index 00000000000..f449946a50f --- /dev/null +++ b/include/linux/mfd/mxc-hdmi-core.h @@ -0,0 +1,39 @@ +/* + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __LINUX_MXC_HDMI_CORE_H_ +#define __LINUX_MXC_HDMI_CORE_H_ + +#define IRQ_DISABLE_SUCCEED 0 +#define IRQ_DISABLE_FAIL 1 + +u8 hdmi_readb(unsigned int reg); +void hdmi_writeb(u8 value, unsigned int reg); +void hdmi_mask_writeb(u8 data, unsigned int addr, u8 shift, u8 mask); +unsigned int hdmi_read4(unsigned int reg); +void hdmi_write4(unsigned int value, unsigned int reg); +void hdmi_irq_init(void); +void hdmi_irq_enable(int irq); +unsigned int hdmi_irq_disable(int irq); +void hdmi_set_sample_rate(unsigned int rate); +void hdmi_set_clk_regenerator(void); + +extern int mxc_hdmi_ipu_id; +extern int mxc_hdmi_disp_id; + +#endif -- cgit v1.2.3 From d7de772d2ea23a3cf013d5c0bcf08452dc8a4385 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Mon, 5 Dec 2011 17:47:35 +0800 Subject: imx6q-saberlite: add ipu3 dt support Signed-off-by: Jason Chen --- arch/arm/boot/dts/imx6q.dtsi | 14 ++++++++++++++ arch/arm/mach-imx/clock-imx6q.c | 6 ++++++ arch/arm/mach-imx/mach-imx6q.c | 26 +++++++++++++++++++++++++- arch/arm/mach-imx/src.c | 13 +++++++++++++ arch/arm/plat-mxc/include/mach/common.h | 1 + arch/arm/plat-mxc/include/mach/mx6q.h | 3 +++ drivers/mxc/ipu3/ipu_common.c | 19 +++++++++++++++++-- 7 files changed, 79 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi index 263e8f3664b..f19e37775d5 100644 --- a/arch/arm/boot/dts/imx6q.dtsi +++ b/arch/arm/boot/dts/imx6q.dtsi @@ -571,5 +571,19 @@ status = "disabled"; }; }; + + ipu@0x02400000 { /* IPU1 */ + compatible = "fsl,ipuv3"; + reg = <0x02400000 0x400000>; + interrupts = <0 5 0x04 0 6 0x04>; + revision = <4>; + }; + + ipu@0x02800000 { /* IPU2 */ + compatible = "fsl,ipuv3"; + reg = <0x02800000 0x400000>; + interrupts = <0 7 0x04 0 8 0x04>; + revision = <4>; + }; }; }; diff --git a/arch/arm/mach-imx/clock-imx6q.c b/arch/arm/mach-imx/clock-imx6q.c index 9aa7b1a2aab..1d19f7dbc5c 100644 --- a/arch/arm/mach-imx/clock-imx6q.c +++ b/arch/arm/mach-imx/clock-imx6q.c @@ -1929,6 +1929,12 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK(NULL, "gpmi_io_clk", gpmi_io_clk), _REGISTER_CLOCK(NULL, "usboh3_clk", usboh3_clk), _REGISTER_CLOCK(NULL, "sata_clk", sata_clk), + _REGISTER_CLOCK(NULL, "ipu1_clk", ipu1_clk), + _REGISTER_CLOCK(NULL, "ipu2_clk", ipu2_clk), + _REGISTER_CLOCK(NULL, "ipu1_di0_clk", ipu1_di0_clk), + _REGISTER_CLOCK(NULL, "ipu1_di1_clk", ipu1_di1_clk), + _REGISTER_CLOCK(NULL, "ipu2_di0_clk", ipu2_di0_clk), + _REGISTER_CLOCK(NULL, "ipu2_di1_clk", ipu2_di1_clk), }; int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode) diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c index 44587dea2cc..0584524611f 100644 --- a/arch/arm/mach-imx/mach-imx6q.c +++ b/arch/arm/mach-imx/mach-imx6q.c @@ -29,6 +29,7 @@ #include #include #include +#include static iomux_v3_cfg_t imx6q_sabrelite_pads[] = { /* DISPLAY */ @@ -85,6 +86,28 @@ static int ksz9021rn_phy_fixup(struct phy_device *phydev) return 0; } +static int mx6q_ipuv3_init(int id) +{ + imx_reset_ipu(id); + return 0; +} + +static void mx6q_ipuv3_pg(int enable) +{ + /*TODO*/ +} + +static struct imx_ipuv3_platform_data ipuv3_pdata = { + .rev = 4, + .init = mx6q_ipuv3_init, + .pg = mx6q_ipuv3_pg, +}; + +static const struct of_dev_auxdata imx6q_auxdata_lookup[] __initconst = { + OF_DEV_AUXDATA("fsl,ipuv3", MX6Q_IPU1_BASE_ADDR, "imx-ipuv3.0", &ipuv3_pdata), + OF_DEV_AUXDATA("fsl,ipuv3", MX6Q_IPU2_BASE_ADDR, "imx-ipuv3.1", &ipuv3_pdata), +}; + static void __init imx6q_init_machine(void) { if (of_machine_is_compatible("fsl,imx6q-sabrelite")) { @@ -94,7 +117,8 @@ static void __init imx6q_init_machine(void) ARRAY_SIZE(imx6q_sabrelite_pads)); } - of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); + of_platform_populate(NULL, of_default_bus_match_table, + imx6q_auxdata_lookup, NULL); iram_init(MX6Q_IRAM_BASE_ADDR, MX6Q_IRAM_SIZE); diff --git a/arch/arm/mach-imx/src.c b/arch/arm/mach-imx/src.c index a8e33681b73..ac8eb58aeeb 100644 --- a/arch/arm/mach-imx/src.c +++ b/arch/arm/mach-imx/src.c @@ -28,6 +28,19 @@ static void __iomem *src_base; #define cpu_logical_map(cpu) 0 #endif +void imx_reset_ipu(int ipu) +{ + u32 val; + + /* hard reset the IPU */ + val = readl_relaxed(src_base); + if (ipu == 0) + val |= 1 << 3; + else + val |= 1 << 12; + writel_relaxed(val, src_base); +} + void imx_enable_cpu(int cpu, bool enable) { u32 mask, val; diff --git a/arch/arm/plat-mxc/include/mach/common.h b/arch/arm/plat-mxc/include/mach/common.h index a9727648c72..21f4a9282cd 100644 --- a/arch/arm/plat-mxc/include/mach/common.h +++ b/arch/arm/plat-mxc/include/mach/common.h @@ -121,6 +121,7 @@ extern void imx_smp_prepare(void); static inline void imx_scu_map_io(void) {} static inline void imx_smp_prepare(void) {} #endif +extern void imx_reset_ipu(int ipu); extern void imx_enable_cpu(int cpu, bool enable); extern void imx_set_cpu_jump(int cpu, void *jump_addr); extern void imx_src_init(void); diff --git a/arch/arm/plat-mxc/include/mach/mx6q.h b/arch/arm/plat-mxc/include/mach/mx6q.h index a1265a01a5f..28b1561adf2 100644 --- a/arch/arm/plat-mxc/include/mach/mx6q.h +++ b/arch/arm/plat-mxc/include/mach/mx6q.h @@ -39,4 +39,7 @@ #define MX6Q_IRAM_BASE_ADDR 0x00900000 #define MX6Q_IRAM_SIZE (SZ_256K - SZ_4K) +#define MX6Q_IPU1_BASE_ADDR 0x02400000 +#define MX6Q_IPU2_BASE_ADDR 0x02800000 + #endif /* __MACH_MX6Q_H__ */ diff --git a/drivers/mxc/ipu3/ipu_common.c b/drivers/mxc/ipu3/ipu_common.c index 4a7ebc02548..3113e7b933b 100644 --- a/drivers/mxc/ipu3/ipu_common.c +++ b/drivers/mxc/ipu3/ipu_common.c @@ -42,6 +42,7 @@ #include "ipu_param_mem.h" static struct ipu_soc ipu_array[MXC_IPU_MAX_NUM]; +static int ipu_idx; int g_ipu_hw_rev; /* Static functions */ @@ -381,9 +382,12 @@ static int __devinit ipu_probe(struct platform_device *pdev) unsigned long ipu_base; int ret = 0; - if (pdev->id >= MXC_IPU_MAX_NUM) + if (ipu_idx >= MXC_IPU_MAX_NUM) return -ENODEV; + pdev->id = ipu_idx; + ipu_idx++; + ipu = &ipu_array[pdev->id]; memset(ipu, 0, sizeof(struct ipu_soc)); @@ -391,7 +395,10 @@ static int __devinit ipu_probe(struct platform_device *pdev) mutex_init(&ipu->mutex_lock); atomic_set(&ipu->ipu_use_count, 0); - g_ipu_hw_rev = plat_data->rev; + ret = of_property_read_u32(pdev->dev.of_node, + "revision", &g_ipu_hw_rev); + if (ret < 0 && plat_data) + g_ipu_hw_rev = plat_data->rev; ipu->dev = &pdev->dev; @@ -2933,13 +2940,21 @@ static const struct dev_pm_ops mxcipu_pm_ops = { .resume_noirq = ipu_resume_noirq, }; +static const struct of_device_id mxc_ipu_dt_ids[] = { + { .compatible = "fsl,ipuv3", }, + { /* sentinel */ } +}; + /*! * This structure contains pointers to the power management callback functions. */ static struct platform_driver mxcipu_driver = { .driver = { .name = "imx-ipuv3", +#ifdef CONFIG_PM .pm = &mxcipu_pm_ops, +#endif + .of_match_table = mxc_ipu_dt_ids, }, .probe = ipu_probe, .remove = ipu_remove, -- cgit v1.2.3 From 95756b30fb488275e2a2b9925c1e52f8a06aeca1 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Mon, 5 Dec 2011 17:54:54 +0800 Subject: imx6q-sabrelite: add mxc hdmi and related i2c dt support Signed-off-by: Jason Chen --- arch/arm/boot/dts/imx6q-sabrelite.dts | 15 ++++++++++ arch/arm/boot/dts/imx6q.dtsi | 6 ++++ arch/arm/mach-imx/clock-imx6q.c | 1 + drivers/i2c/busses/i2c-imx.c | 1 + drivers/mfd/mxc-hdmi-core.c | 55 +++++++++++++++++++++++++++++++++-- drivers/video/mxc_hdmi.c | 9 +++++- 6 files changed, 84 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/imx6q-sabrelite.dts b/arch/arm/boot/dts/imx6q-sabrelite.dts index 08d920de728..a8bd4dcdd4d 100644 --- a/arch/arm/boot/dts/imx6q-sabrelite.dts +++ b/arch/arm/boot/dts/imx6q-sabrelite.dts @@ -44,6 +44,21 @@ uart2: uart@021e8000 { status = "okay"; }; + + i2c@021a4000 { /* I2C2 */ + status = "okay"; + clock-frequency = <400000>; + + ddc: ddc@50 { + compatible = "fsl,imx6q-hdmi-ddc"; + reg = <0x50>; + }; + }; + }; + + hdmi@0x00120000 { /* HDMI */ + ipu = <0>; + di = <0>; }; }; }; diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi index f19e37775d5..c46e917a799 100644 --- a/arch/arm/boot/dts/imx6q.dtsi +++ b/arch/arm/boot/dts/imx6q.dtsi @@ -572,6 +572,12 @@ }; }; + hdmi@0x00120000 { /* HDMI */ + compatible = "fsl,imx6q-hdmi-core"; + reg = <0x00120000 0x9000>; + interrupts = <0 115 0x04 0 116 0x04>; + }; + ipu@0x02400000 { /* IPU1 */ compatible = "fsl,ipuv3"; reg = <0x02400000 0x400000>; diff --git a/arch/arm/mach-imx/clock-imx6q.c b/arch/arm/mach-imx/clock-imx6q.c index 1d19f7dbc5c..34567c0af25 100644 --- a/arch/arm/mach-imx/clock-imx6q.c +++ b/arch/arm/mach-imx/clock-imx6q.c @@ -1935,6 +1935,7 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK(NULL, "ipu1_di1_clk", ipu1_di1_clk), _REGISTER_CLOCK(NULL, "ipu2_di0_clk", ipu2_di0_clk), _REGISTER_CLOCK(NULL, "ipu2_di1_clk", ipu2_di1_clk), + _REGISTER_CLOCK(NULL, "hdmi_iahb_clk", hdmi_iahb_clk), }; int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 58832e578ff..08432ce7c67 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -130,6 +130,7 @@ struct imx_i2c_struct { static const struct of_device_id i2c_imx_dt_ids[] = { { .compatible = "fsl,imx1-i2c", }, + { .compatible = "fsl,imx6q-i2c", }, { /* sentinel */ } }; diff --git a/drivers/mfd/mxc-hdmi-core.c b/drivers/mfd/mxc-hdmi-core.c index 48cc57cc1b8..1ecdc0d9d8e 100644 --- a/drivers/mfd/mxc-hdmi-core.c +++ b/drivers/mfd/mxc-hdmi-core.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include @@ -391,12 +393,40 @@ void hdmi_set_sample_rate(unsigned int rate) hdmi_set_clk_regenerator(); } +static void hdmi_init(int ipu_id, int disp_id) +{ + int hdmi_mux_setting; + + if ((ipu_id > 1) || (ipu_id < 0)) { + printk(KERN_ERR"Invalid IPU select for HDMI: %d. Set to 0\n", + ipu_id); + ipu_id = 0; + } + + if ((disp_id > 1) || (disp_id < 0)) { + printk(KERN_ERR"Invalid DI select for HDMI: %d. Set to 0\n", + disp_id); + disp_id = 0; + } + + /* Configure the connection between IPU1/2 and HDMI */ + hdmi_mux_setting = 2*ipu_id + disp_id; + + /* GPR3, bits 2-3 = HDMI_MUX_CTL */ + /*mxc_iomux_set_gpr_register(3, 2, 2, hdmi_mux_setting);*/ +} + +static struct fsl_mxc_hdmi_platform_data hdmi_vdata = { + .init = hdmi_init, +}; + static int mxc_hdmi_core_probe(struct platform_device *pdev) { struct fsl_mxc_hdmi_core_platform_data *pdata = pdev->dev.platform_data; struct mxc_hdmi_data *hdmi_data; struct resource *res; int ret = 0; + struct platform_device_info pdevinfo_hdmi_v; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) @@ -468,8 +498,13 @@ static int mxc_hdmi_core_probe(struct platform_device *pdev) pr_debug("\n%s hdmi hw base = 0x%08x\n\n", __func__, (int)res->start); - mxc_hdmi_ipu_id = pdata->ipu_id; - mxc_hdmi_disp_id = pdata->disp_id; + if (pdata) { + mxc_hdmi_ipu_id = pdata->ipu_id; + mxc_hdmi_disp_id = pdata->disp_id; + } else { + of_property_read_u32(pdev->dev.of_node, "ipu", &mxc_hdmi_ipu_id); + of_property_read_u32(pdev->dev.of_node, "di", &mxc_hdmi_disp_id); + } initialize_hdmi_ih_mutes(); @@ -480,6 +515,16 @@ static int mxc_hdmi_core_probe(struct platform_device *pdev) /* Replace platform data coming in with a local struct */ platform_set_drvdata(pdev, hdmi_data); + /* register hdmi video */ + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + pdevinfo_hdmi_v.name = "mxc_hdmi"; + pdevinfo_hdmi_v.res = res; + pdevinfo_hdmi_v.num_res = 1; + pdevinfo_hdmi_v.data = &hdmi_vdata; + pdevinfo_hdmi_v.size_data = sizeof(hdmi_vdata); + pdevinfo_hdmi_v.dma_mask = DMA_BIT_MASK(32); + platform_device_register_full(&pdevinfo_hdmi_v); + return ret; eirq: @@ -511,10 +556,16 @@ static int __exit mxc_hdmi_core_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id mxc_hdmi_core_dt_ids[] = { + { .compatible = "fsl,imx6q-hdmi-core", }, + { /* sentinel */ } +}; + static struct platform_driver mxc_hdmi_core_driver = { .driver = { .name = "mxc_hdmi_core", .owner = THIS_MODULE, + .of_match_table = mxc_hdmi_core_dt_ids, }, .remove = __exit_p(mxc_hdmi_core_remove), }; diff --git a/drivers/video/mxc_hdmi.c b/drivers/video/mxc_hdmi.c index 0298c243b1a..924264c32e2 100644 --- a/drivers/video/mxc_hdmi.c +++ b/drivers/video/mxc_hdmi.c @@ -1580,7 +1580,8 @@ static int mxc_hdmi_setup(struct mxc_hdmi *hdmi) hdmi_tx_hdcp_config(hdmi); hdmi_phy_init(hdmi, TRUE); hdmi_video_force_output(hdmi, FALSE); - hdmi_set_clk_regenerator(); + /*FIXME: audio CTS/N should be set after ipu display timming finish */ + /*hdmi_set_clk_regenerator();*/ return 0; } @@ -1943,9 +1944,15 @@ static const struct i2c_device_id mxc_hdmi_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, mxc_hdmi_i2c_id); +static const struct of_device_id mxc_hdmi_ddc_dt_ids[] = { + { .compatible = "fsl,imx6q-hdmi-ddc", }, + { /* sentinel */ } +}; + static struct i2c_driver mxc_hdmi_i2c_driver = { .driver = { .name = "mxc_hdmi_i2c", + .of_match_table = mxc_hdmi_ddc_dt_ids, }, .probe = mxc_hdmi_i2c_probe, .remove = mxc_hdmi_i2c_remove, -- cgit v1.2.3 From ddf51fe00663c6ddeacbf0c2b77bf5aa23425ae3 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Mon, 5 Dec 2011 18:18:59 +0800 Subject: imx6q-sabrelite: add ipuv3 fb dt support Signed-off-by: Jason Chen --- arch/arm/boot/dts/imx6q-sabrelite.dts | 35 +++++++ arch/arm/plat-mxc/include/mach/ipu-v3.h | 2 +- drivers/video/mxc/mxc_ipuv3_fb.c | 161 ++++++++++++++++++++++---------- 3 files changed, 148 insertions(+), 50 deletions(-) diff --git a/arch/arm/boot/dts/imx6q-sabrelite.dts b/arch/arm/boot/dts/imx6q-sabrelite.dts index a8bd4dcdd4d..b199cd9a7e6 100644 --- a/arch/arm/boot/dts/imx6q-sabrelite.dts +++ b/arch/arm/boot/dts/imx6q-sabrelite.dts @@ -61,4 +61,39 @@ di = <0>; }; }; + + leds { + compatible = "gpio-leds"; + + debug-led { + label = "Heartbeat"; + gpios = <&gpio2 25 0>; /* GPIO3_25 */ + linux,default-trigger = "heartbeat"; + }; + }; + + displays { + #address-cells = <1>; + #size-cells = <1>; + compatible = "simple-bus"; + ranges; + + disp1: fb@0 { + compatible = "fsl,mxcfb-ipuv3"; + disp_dev = "hdmi"; + interface_pix_fmt = "RGB24"; + mode_str = "1280x720M@60"; + internal_clk = "false"; + reg = <0x00000000 0x0>; /* reserve fb mem */ + }; + + disp2: fb@1 { + compatible = "fsl,mxcfb-ipuv3"; + disp_dev = "ldb"; + interface_pix_fmt = "RGB666"; + mode_str = "LDB-XGA"; + internal_clk = "false"; + reg = <0x00000001 0x0>; /* reserve fb mem */ + }; + }; }; diff --git a/arch/arm/plat-mxc/include/mach/ipu-v3.h b/arch/arm/plat-mxc/include/mach/ipu-v3.h index 480b4ea338e..bccb8811673 100644 --- a/arch/arm/plat-mxc/include/mach/ipu-v3.h +++ b/arch/arm/plat-mxc/include/mach/ipu-v3.h @@ -691,7 +691,7 @@ uint32_t bytes_per_pixel(uint32_t fmt); struct ipuv3_fb_platform_data { char disp_dev[32]; u32 interface_pix_fmt; - char *mode_str; + char mode_str[32]; int default_bpp; bool int_clk; diff --git a/drivers/video/mxc/mxc_ipuv3_fb.c b/drivers/video/mxc/mxc_ipuv3_fb.c index 63807917350..1447c455202 100644 --- a/drivers/video/mxc/mxc_ipuv3_fb.c +++ b/drivers/video/mxc/mxc_ipuv3_fb.c @@ -91,6 +91,8 @@ struct mxcfb_info { void *ipu; struct fb_info *ovfbi; + + struct ipuv3_fb_platform_data of_data; }; struct mxcfb_alloc_list { @@ -109,6 +111,7 @@ enum { static bool g_dp_in_use[2]; LIST_HEAD(fb_alloc_list); +static int of_dev_id; static uint32_t bpp_to_pixfmt(struct fb_info *fbi) { @@ -131,6 +134,38 @@ static uint32_t bpp_to_pixfmt(struct fb_info *fbi) return pixfmt; } +static int if_fmt_parse(const char *fmt) +{ + int if_fmt; + + if (!strncmp(fmt, "RGB24", 5)) + if_fmt = IPU_PIX_FMT_RGB24; + else if (!strncmp(fmt, "BGR24", 5)) + if_fmt = IPU_PIX_FMT_BGR24; + else if (!strncmp(fmt, "GBR24", 5)) + if_fmt = IPU_PIX_FMT_GBR24; + else if (!strncmp(fmt, "RGB565", 6)) + if_fmt = IPU_PIX_FMT_RGB565; + else if (!strncmp(fmt, "RGB666", 6)) + if_fmt = IPU_PIX_FMT_RGB666; + else if (!strncmp(fmt, "YUV444", 6)) + if_fmt = IPU_PIX_FMT_YUV444; + else if (!strncmp(fmt, "LVDS666", 7)) + if_fmt = IPU_PIX_FMT_LVDS666; + else if (!strncmp(fmt, "YUYV16", 6)) + if_fmt = IPU_PIX_FMT_YUYV; + else if (!strncmp(fmt, "UYVY16", 6)) + if_fmt = IPU_PIX_FMT_UYVY; + else if (!strncmp(fmt, "YVYU16", 6)) + if_fmt = IPU_PIX_FMT_YVYU; + else if (!strncmp(fmt, "VYUY16", 6)) + if_fmt = IPU_PIX_FMT_VYUY; + else + if_fmt = IPU_PIX_FMT_RGB24; + + return if_fmt; +} + static struct fb_info *found_registered_fb(ipu_channel_t ipu_ch, int ipu_id) { int i; @@ -1233,7 +1268,8 @@ mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) down(&mxc_fbi->flip_sem); - mxc_fbi->cur_ipu_buf = (++mxc_fbi->cur_ipu_buf) % 3; + mxc_fbi->cur_ipu_buf++; + mxc_fbi->cur_ipu_buf %= 3; mxc_fbi->cur_ipu_alpha_buf = !mxc_fbi->cur_ipu_alpha_buf; dev_dbg(info->device, "Updating SDC %s buf %d address=0x%08lX\n", @@ -1280,8 +1316,10 @@ mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) IPU_INPUT_BUFFER, 1), ipu_check_buffer_ready(mxc_fbi->ipu, mxc_fbi->ipu_ch, IPU_INPUT_BUFFER, 2)); - mxc_fbi->cur_ipu_buf = (++mxc_fbi->cur_ipu_buf) % 3; - mxc_fbi->cur_ipu_buf = (++mxc_fbi->cur_ipu_buf) % 3; + mxc_fbi->cur_ipu_buf++; + mxc_fbi->cur_ipu_buf %= 3; + mxc_fbi->cur_ipu_buf++; + mxc_fbi->cur_ipu_buf %= 3; mxc_fbi->cur_ipu_alpha_buf = !mxc_fbi->cur_ipu_alpha_buf; ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq); ipu_enable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq); @@ -1672,49 +1710,8 @@ static int mxcfb_option_setup(struct platform_device *pdev) continue; } if (!strncmp(opt, "if=", 3)) { - if (!strncmp(opt+3, "RGB24", 5)) { - pdata->interface_pix_fmt = IPU_PIX_FMT_RGB24; - continue; - } else if (!strncmp(opt+6, "BGR24", 5)) { - pdata->interface_pix_fmt = IPU_PIX_FMT_BGR24; - continue; - } - if (!strncmp(opt+3, "GBR24", 5)) { - pdata->interface_pix_fmt = IPU_PIX_FMT_GBR24; - continue; - } - if (!strncmp(opt+3, "RGB565", 6)) { - pdata->interface_pix_fmt = IPU_PIX_FMT_RGB565; - continue; - } - if (!strncmp(opt+3, "RGB666", 6)) { - pdata->interface_pix_fmt = IPU_PIX_FMT_RGB666; - continue; - } - if (!strncmp(opt+3, "YUV444", 6)) { - pdata->interface_pix_fmt = IPU_PIX_FMT_YUV444; - continue; - } - if (!strncmp(opt+3, "LVDS666", 7)) { - pdata->interface_pix_fmt = IPU_PIX_FMT_LVDS666; - continue; - } - if (!strncmp(opt+3, "YUYV16", 6)) { - pdata->interface_pix_fmt = IPU_PIX_FMT_YUYV; - continue; - } - if (!strncmp(opt+3, "UYVY16", 6)) { - pdata->interface_pix_fmt = IPU_PIX_FMT_UYVY; - continue; - } - if (!strncmp(opt+3, "YVYU16", 6)) { - pdata->interface_pix_fmt = IPU_PIX_FMT_YVYU; - continue; - } - if (!strncmp(opt+3, "VYUY16", 6)) { - pdata->interface_pix_fmt = IPU_PIX_FMT_VYUY; - continue; - } + pdata->interface_pix_fmt = if_fmt_parse(opt+3); + continue; } if (!strncmp(opt, "int_clk", 7)) { pdata->int_clk = true; @@ -1727,8 +1724,10 @@ static int mxcfb_option_setup(struct platform_device *pdev) fb_mode_str = opt; } - if (fb_mode_str) - pdata->mode_str = fb_mode_str; + if (fb_mode_str) { + memcpy(pdata->mode_str, fb_mode_str, strlen(fb_mode_str)); + pdata->mode_str[strlen(fb_mode_str)] = '\0'; + } return 0; } @@ -1896,6 +1895,54 @@ static void ipu_clear_usage(int ipu, int di) ipu_usage[ipu][di] = false; } +static int of_get_fb_data(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct ipuv3_fb_platform_data *plat_data = pdev->dev.platform_data; + const char *disp_dev, *if_fmt, *mode_str, *int_clk; + const char *default_dev = "lcd"; + const char *default_mode_str = "800x480M@55"; + int ret; + + if (!np) + return -EINVAL; + + pdev->id = of_dev_id; + of_dev_id++; + + plat_data->default_bpp = 16; + + ret = of_property_read_string(np, "disp_dev", &disp_dev); + if (ret < 0) + memcpy(plat_data->disp_dev, default_dev, strlen(default_dev)); + else + memcpy(plat_data->disp_dev, disp_dev, strlen(disp_dev)); + + ret = of_property_read_string(np, "interface_pix_fmt", &if_fmt); + if (ret < 0) + plat_data->interface_pix_fmt = IPU_PIX_FMT_RGB24; + else + plat_data->interface_pix_fmt = if_fmt_parse(if_fmt); + + ret = of_property_read_string(np, "mode_str", &mode_str); + if (ret < 0) + memcpy(plat_data->mode_str, default_mode_str, strlen(default_mode_str)); + else + memcpy(plat_data->mode_str, mode_str, strlen(mode_str)); + + ret = of_property_read_string(np, "internal_clk", &int_clk); + if (ret < 0) + plat_data->int_clk = false; + else { + if (!strcmp(int_clk, "true")) + plat_data->int_clk = true; + else + plat_data->int_clk = false; + } + + return 0; +} + /*! * Probe routine for the framebuffer driver. It is called during the * driver binding process. The following functions are performed in @@ -1921,9 +1968,18 @@ static int mxcfb_probe(struct platform_device *pdev) goto init_fbinfo_failed; } + mxcfbi = (struct mxcfb_info *)fbi->par; + + if (!plat_data) { + plat_data = pdev->dev.platform_data = &mxcfbi->of_data; + if (of_get_fb_data(pdev) < 0) { + dev_err(&pdev->dev, "no platform data\n"); + goto platform_data_err; + } + } + mxcfb_option_setup(pdev); - mxcfbi = (struct mxcfb_info *)fbi->par; mxcfbi->ipu_int_clk = plat_data->int_clk; ret = mxcfb_dispdrv_init(pdev, fbi); if (ret < 0) @@ -2000,6 +2056,7 @@ get_ipu_failed: ipu_clear_usage(mxcfbi->ipu_id, mxcfbi->ipu_di); ipu_in_busy: init_dispdrv_failed: +platform_data_err: fb_dealloc_cmap(&fbi->cmap); framebuffer_release(fbi); init_fbinfo_failed: @@ -2031,12 +2088,18 @@ static int mxcfb_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id mxcfb_ipuv3_dt_ids[] = { + { .compatible = "fsl,mxcfb-ipuv3", }, + { /* sentinel */ } +}; + /*! * This structure contains pointers to the power management callback functions. */ static struct platform_driver mxcfb_driver = { .driver = { .name = MXCFB_NAME, + .of_match_table = mxcfb_ipuv3_dt_ids, }, .probe = mxcfb_probe, .remove = mxcfb_remove, -- cgit v1.2.3 From 17157c9ca7d42aaa38b3c6698a6f83cadc845de0 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Mon, 5 Dec 2011 18:12:27 +0800 Subject: imx6q-sabrelite: add pwm and pwm-bl dt support Signed-off-by: Jason Chen --- arch/arm/boot/dts/imx6q-sabrelite.dts | 14 ++++++++++++ arch/arm/boot/dts/imx6q.dtsi | 12 +++++++---- arch/arm/mach-imx/clock-imx6q.c | 8 +++---- arch/arm/plat-mxc/pwm.c | 9 ++++++++ drivers/video/backlight/pwm_bl.c | 40 +++++++++++++++++++++++++++++++++-- 5 files changed, 73 insertions(+), 10 deletions(-) diff --git a/arch/arm/boot/dts/imx6q-sabrelite.dts b/arch/arm/boot/dts/imx6q-sabrelite.dts index b199cd9a7e6..965dfbc802c 100644 --- a/arch/arm/boot/dts/imx6q-sabrelite.dts +++ b/arch/arm/boot/dts/imx6q-sabrelite.dts @@ -72,6 +72,20 @@ }; }; + backlight { + #address-cells = <1>; + #size-cells = <0>; + compatible = "simple-bus"; + + pwm_bl { + compatible = "pwm-bl"; + pwm-parent = <&pwm4>; + max_brightness = <255>; + dft_brightness = <128>; + pwm_period_ns = <50000>; + }; + }; + displays { #address-cells = <1>; #size-cells = <1>; diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi index c46e917a799..3b7436008ea 100644 --- a/arch/arm/boot/dts/imx6q.dtsi +++ b/arch/arm/boot/dts/imx6q.dtsi @@ -211,22 +211,26 @@ reg = <0x0207c000 0x4000>; }; - pwm@02080000 { /* PWM1 */ + pwm1: pwm@02080000 { /* PWM1 */ + compatible = "fsl,imx6q-pwm"; reg = <0x02080000 0x4000>; interrupts = <0 83 0x04>; }; - pwm@02084000 { /* PWM2 */ + pwm2: pwm@02084000 { /* PWM2 */ + compatible = "fsl,imx6q-pwm"; reg = <0x02084000 0x4000>; interrupts = <0 84 0x04>; }; - pwm@02088000 { /* PWM3 */ + pwm3: pwm@02088000 { /* PWM3 */ + compatible = "fsl,imx6q-pwm"; reg = <0x02088000 0x4000>; interrupts = <0 85 0x04>; }; - pwm@0208c000 { /* PWM4 */ + pwm4: pwm@0208c000 { /* PWM4 */ + compatible = "fsl,imx6q-pwm"; reg = <0x0208c000 0x4000>; interrupts = <0 86 0x04>; }; diff --git a/arch/arm/mach-imx/clock-imx6q.c b/arch/arm/mach-imx/clock-imx6q.c index 34567c0af25..eca30e4b8c1 100644 --- a/arch/arm/mach-imx/clock-imx6q.c +++ b/arch/arm/mach-imx/clock-imx6q.c @@ -1922,10 +1922,10 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK(NULL, "iim_clk", iim_clk), _REGISTER_CLOCK(NULL, "mlb_clk", mlb_clk), _REGISTER_CLOCK(NULL, "openvg_axi_clk", openvg_axi_clk), - _REGISTER_CLOCK(NULL, "pwm1_clk", pwm1_clk), - _REGISTER_CLOCK(NULL, "pwm2_clk", pwm2_clk), - _REGISTER_CLOCK(NULL, "pwm3_clk", pwm3_clk), - _REGISTER_CLOCK(NULL, "pwm4_clk", pwm4_clk), + _REGISTER_CLOCK("2080000.pwm", NULL, pwm1_clk), + _REGISTER_CLOCK("2084000.pwm", NULL, pwm2_clk), + _REGISTER_CLOCK("2088000.pwm", NULL, pwm3_clk), + _REGISTER_CLOCK("208c000.pwm", NULL, pwm4_clk), _REGISTER_CLOCK(NULL, "gpmi_io_clk", gpmi_io_clk), _REGISTER_CLOCK(NULL, "usboh3_clk", usboh3_clk), _REGISTER_CLOCK(NULL, "sata_clk", sata_clk), diff --git a/arch/arm/plat-mxc/pwm.c b/arch/arm/plat-mxc/pwm.c index e032717f7d0..be76dcb4921 100644 --- a/arch/arm/plat-mxc/pwm.c +++ b/arch/arm/plat-mxc/pwm.c @@ -165,6 +165,9 @@ struct pwm_device *pwm_request(int pwm_id, const char *label) if (pwm->pwm_id == pwm_id) { found = 1; break; + } else if ((int)pwm->pdev->dev.of_node == pwm_id) { + found = 1; + break; } } @@ -282,9 +285,15 @@ static int __devexit mxc_pwm_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id mxc_pwm_dt_ids[] = { + { .compatible = "fsl,imx6q-pwm", }, + { /* sentinel */ } +}; + static struct platform_driver mxc_pwm_driver = { .driver = { .name = "mxc_pwm", + .of_match_table = mxc_pwm_dt_ids, }, .probe = mxc_pwm_probe, .remove = __devexit_p(mxc_pwm_remove), diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 8b5b2a4124c..63ca212818a 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -83,6 +83,33 @@ static const struct backlight_ops pwm_backlight_ops = { .check_fb = pwm_backlight_check_fb, }; +struct platform_pwm_backlight_data of_data; +static int of_get_pwm_data(struct platform_device *pdev, + struct platform_pwm_backlight_data *data) +{ + const __be32 *parp; + struct device_node *pwm_np, *np = pdev->dev.of_node; + + if (!np) + return -EINVAL; + + parp = of_get_property(np, "pwm-parent", NULL); + if (parp == NULL) + return -EINVAL; + of_node_put(np); + + pwm_np = of_find_node_by_phandle(be32_to_cpup(parp)); + if (pwm_np) + of_node_put(pwm_np); + data->pwm_id = (int)pwm_np; + + of_property_read_u32(np, "max_brightness", &data->max_brightness); + of_property_read_u32(np, "dft_brightness", &data->dft_brightness); + of_property_read_u32(np, "pwm_period_ns", &data->pwm_period_ns); + + return 0; +} + static int pwm_backlight_probe(struct platform_device *pdev) { struct backlight_properties props; @@ -92,8 +119,11 @@ static int pwm_backlight_probe(struct platform_device *pdev) int ret; if (!data) { - dev_err(&pdev->dev, "failed to find platform data\n"); - return -EINVAL; + data = pdev->dev.platform_data = &of_data; + if (of_get_pwm_data(pdev, data) < 0) { + dev_err(&pdev->dev, "failed to find platform data\n"); + return -EINVAL; + } } if (data->init) { @@ -196,10 +226,16 @@ static int pwm_backlight_resume(struct platform_device *pdev) #define pwm_backlight_resume NULL #endif +static const struct of_device_id pwm_bl_dt_ids[] = { + { .compatible = "pwm-bl", }, + { /* sentinel */ } +}; + static struct platform_driver pwm_backlight_driver = { .driver = { .name = "pwm-backlight", .owner = THIS_MODULE, + .of_match_table = pwm_bl_dt_ids, }, .probe = pwm_backlight_probe, .remove = pwm_backlight_remove, -- cgit v1.2.3 From 10953b93796ccbfaa6eb5ca2329ba137e6ef47ce Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Mon, 5 Dec 2011 18:16:43 +0800 Subject: imx6q-sabrelite: add ldb dt support Signed-off-by: Jason Chen --- arch/arm/boot/dts/imx6q-sabrelite.dts | 9 +++ arch/arm/boot/dts/imx6q.dtsi | 5 ++ arch/arm/mach-imx/clock-imx6q.c | 2 + drivers/video/mxc/ldb.c | 107 ++++++++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+) diff --git a/arch/arm/boot/dts/imx6q-sabrelite.dts b/arch/arm/boot/dts/imx6q-sabrelite.dts index 965dfbc802c..d2e0cd9166c 100644 --- a/arch/arm/boot/dts/imx6q-sabrelite.dts +++ b/arch/arm/boot/dts/imx6q-sabrelite.dts @@ -22,6 +22,15 @@ }; soc { + aips-bus@02000000 { /* AIPS1 */ + ldb@020e0000 { + disp-pwr-gpios = <&gpio1 0 0>; /* gpio2 0 */ + mode = "sin0"; + ext_ref = "true"; + lvds0 = <1 0>; + }; + }; + aips-bus@02100000 { /* AIPS2 */ enet@02188000 { phy-mode = "rgmii"; diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi index 3b7436008ea..c24f7723f15 100644 --- a/arch/arm/boot/dts/imx6q.dtsi +++ b/arch/arm/boot/dts/imx6q.dtsi @@ -393,6 +393,11 @@ reg = <0x020e0000 0x4000>; }; + ldb@020e0000 { + compatible = "fsl,imx6q-ldb"; + reg = <0x020e0000 0x4>; + }; + dcic@020e4000 { /* DCIC1 */ reg = <0x020e4000 0x4000>; interrupts = <0 124 0x04>; diff --git a/arch/arm/mach-imx/clock-imx6q.c b/arch/arm/mach-imx/clock-imx6q.c index eca30e4b8c1..c436a711d7e 100644 --- a/arch/arm/mach-imx/clock-imx6q.c +++ b/arch/arm/mach-imx/clock-imx6q.c @@ -1936,6 +1936,8 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK(NULL, "ipu2_di0_clk", ipu2_di0_clk), _REGISTER_CLOCK(NULL, "ipu2_di1_clk", ipu2_di1_clk), _REGISTER_CLOCK(NULL, "hdmi_iahb_clk", hdmi_iahb_clk), + _REGISTER_CLOCK(NULL, "ldb_di0_clk", ldb_di0_clk), + _REGISTER_CLOCK(NULL, "ldb_di1_clk", ldb_di1_clk), }; int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode) diff --git a/drivers/video/mxc/ldb.c b/drivers/video/mxc/ldb.c index b3a75a5868c..983dbc957da 100644 --- a/drivers/video/mxc/ldb.c +++ b/drivers/video/mxc/ldb.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include "mxc_dispdrv.h" @@ -407,11 +408,83 @@ static int ldb_ipu_ldb_route(int ipu, int di, struct ldb_data *ldb) return 0; } +static int of_get_ldb_data(struct platform_device *pdev, + struct fsl_mxc_ldb_platform_data *plat_data) +{ + struct device_node *np = pdev->dev.of_node; + uint32_t lvds0[2] = {0}, lvds1[2] = {0}; + const char *mode, *ext_ref; + int ret; + + if (!np) + return -EINVAL; + + ret = of_property_read_string(np, "mode", &mode); + if (ret < 0) + g_ldb_mode = LDB_SEP0; + else { + if (!strcmp(mode, "spl0")) + g_ldb_mode = LDB_SPL_DI0; + else if (!strcmp(mode, "spl1")) + g_ldb_mode = LDB_SPL_DI1; + else if (!strcmp(mode, "dul0")) + g_ldb_mode = LDB_DUL_DI0; + else if (!strcmp(mode, "dul1")) + g_ldb_mode = LDB_DUL_DI1; + else if (!strcmp(mode, "sin0")) + g_ldb_mode = LDB_SIN0; + else if (!strcmp(mode, "sin1")) + g_ldb_mode = LDB_SIN1; + else if (!strcmp(mode, "sep0")) + g_ldb_mode = LDB_SEP0; + else if (!strcmp(mode, "sep1")) + g_ldb_mode = LDB_SEP1; + } + + ret = of_property_read_string(np, "ext_ref", &ext_ref); + if (ret < 0) + plat_data->ext_ref = 1; + else if (!strcmp(ext_ref, "true")) + plat_data->ext_ref = 1; + + of_property_read_u32_array(np, "lvds0", + lvds0, ARRAY_SIZE(lvds0)); + of_property_read_u32_array(np, "lvds1", + lvds1, ARRAY_SIZE(lvds1)); + + if ((g_ldb_mode == LDB_SPL_DI0) || (g_ldb_mode == LDB_DUL_DI0)) { + plat_data->ipu_id = lvds0[0]; + plat_data->disp_id = 0; + } else if ((g_ldb_mode == LDB_SPL_DI1) || (g_ldb_mode == LDB_DUL_DI1)) { + plat_data->ipu_id = lvds0[0]; + plat_data->disp_id = 1; + } else if (g_ldb_mode == LDB_SIN0) { + plat_data->ipu_id = lvds0[0]; + plat_data->disp_id = lvds0[1]; + } else if (g_ldb_mode == LDB_SIN1) { + plat_data->ipu_id = lvds1[0]; + plat_data->disp_id = lvds1[1]; + } else if (g_ldb_mode == LDB_SEP0) { + plat_data->ipu_id = lvds0[0]; + plat_data->disp_id = lvds0[1]; + plat_data->sec_ipu_id = lvds1[0]; + plat_data->sec_disp_id = lvds1[1]; + } else if (g_ldb_mode == LDB_SEP1) { + plat_data->ipu_id = lvds1[0]; + plat_data->disp_id = lvds1[1]; + plat_data->sec_ipu_id = lvds0[0]; + plat_data->sec_disp_id = lvds0[1]; + } + + return 0; +} + static int ldb_disp_init(struct mxc_dispdrv_entry *disp) { int ret = 0, i; struct ldb_data *ldb = mxc_dispdrv_getdata(disp); struct mxc_dispdrv_setting *setting = mxc_dispdrv_getsetting(disp); + struct fsl_mxc_ldb_platform_data of_data; struct fsl_mxc_ldb_platform_data *plat_data = ldb->pdev->dev.platform_data; struct resource *res; uint32_t base_addr; @@ -424,6 +497,14 @@ static int ldb_disp_init(struct mxc_dispdrv_entry *disp) setting->if_fmt = IPU_PIX_FMT_RGB666; } + if (!plat_data) { + plat_data = &of_data; + if (of_get_ldb_data(ldb->pdev, plat_data) < 0) { + dev_err(&ldb->pdev->dev, "no platform data\n"); + return -EINVAL; + } + } + if (!ldb->inited) { char di_clk[] = "ipu1_di0_clk"; char ldb_clk[] = "ldb_di0_clk"; @@ -711,6 +792,24 @@ static struct mxc_dispdrv_driver ldb_drv = { .deinit = ldb_disp_deinit, }; +static void ldb_disp_pwr_up(struct platform_device *pdev) +{ + int ret, gpio_pwr; + struct device_node *np = pdev->dev.of_node; + + if (!np) + return; + + gpio_pwr = of_get_named_gpio(np, "disp-pwr-gpios", 0); + if (gpio_pwr < 0) + dev_warn(&pdev->dev, "no pwr gpio defined\n"); + else { + ret = gpio_request_one(gpio_pwr, GPIOF_OUT_INIT_HIGH, "disp-pwr"); + if (ret) + dev_warn(&pdev->dev, "fail to request pwr gpio\n"); + } +} + /*! * This function is called by the driver framework to initialize the LDB * device. @@ -737,6 +836,8 @@ static int ldb_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, ldb); + ldb_disp_pwr_up(pdev); + alloc_failed: return ret; } @@ -750,9 +851,15 @@ static int ldb_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id mxc_ldb_dt_ids[] = { + { .compatible = "fsl,imx6q-ldb", }, + { /* sentinel */ } +}; + static struct platform_driver mxcldb_driver = { .driver = { .name = "mxc_ldb", + .of_match_table = mxc_ldb_dt_ids, }, .probe = ldb_probe, .remove = ldb_remove, -- cgit v1.2.3 From 01026a75447a669033410f7af51acb86dbf35936 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Mon, 5 Dec 2011 18:21:09 +0800 Subject: MXC V4L2 output: porting to fsl imx_2.6.38 branch base to base commit 433c6306fe9455163cff3591b4cf8e2f22bc6cc8 add v4l2 output driver. Signed-off-by: Jason Chen --- drivers/media/video/mxc/output/Kconfig | 1 + drivers/media/video/mxc/output/mxc_vout.c | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/mxc/output/Kconfig b/drivers/media/video/mxc/output/Kconfig index 72b32879697..4f747509792 100644 --- a/drivers/media/video/mxc/output/Kconfig +++ b/drivers/media/video/mxc/output/Kconfig @@ -1,6 +1,7 @@ config VIDEO_MXC_IPU_OUTPUT bool "IPU v4l2 support" depends on VIDEO_MXC_OUTPUT && MXC_IPU && FB_MXC + select VIDEOBUF_DMA_CONTIG default y ---help--- This is the video4linux2 driver for IPU post processing video output. diff --git a/drivers/media/video/mxc/output/mxc_vout.c b/drivers/media/video/mxc/output/mxc_vout.c index 08edfb3087e..57e684f9930 100644 --- a/drivers/media/video/mxc/output/mxc_vout.c +++ b/drivers/media/video/mxc/output/mxc_vout.c @@ -772,9 +772,10 @@ static int mxc_vidioc_g_fmt_vid_out(struct file *file, void *fh, return 0; } -static inline int ipu_try_task(struct ipu_task *task) +static inline int ipu_try_task(struct mxc_vout_output *vout) { int ret; + struct ipu_task *task = &vout->task; again: ret = ipu_check_task(task); @@ -789,11 +790,19 @@ again: goto again; } if (ret == IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER) { - task->output.crop.w -= 8; + if (vout->disp_support_windows) { + task->output.width -= 8; + task->output.crop.w = task->output.width; + } else + task->output.crop.w -= 8; goto again; } if (ret == IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER) { - task->output.crop.h -= 8; + if (vout->disp_support_windows) { + task->output.height -= 8; + task->output.crop.h = task->output.height; + } else + task->output.crop.h -= 8; goto again; } ret = -EINVAL; @@ -830,7 +839,7 @@ static int mxc_vout_try_task(struct mxc_vout_output *vout) else vout->task.output.format = IPU_PIX_FMT_RGB565; } - ret = ipu_try_task(&vout->task); + ret = ipu_try_task(vout); } return ret; @@ -1575,7 +1584,7 @@ static int __init mxc_vout_setup_output(struct mxc_vout_dev *dev) return ret; } -static int __init mxc_vout_probe(struct platform_device *pdev) +static int mxc_vout_probe(struct platform_device *pdev) { int ret; struct mxc_vout_dev *dev; -- cgit v1.2.3 From b46087c0c37052ba9062d546264fbed1166c1b7a Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Mon, 5 Dec 2011 16:41:46 +0800 Subject: MXC V4L2 output: add dt support Signed-off-by: Jason Chen --- arch/arm/boot/dts/imx6q-sabrelite.dts | 4 ++++ drivers/media/video/mxc/output/mxc_vout.c | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/arch/arm/boot/dts/imx6q-sabrelite.dts b/arch/arm/boot/dts/imx6q-sabrelite.dts index d2e0cd9166c..35938d88577 100644 --- a/arch/arm/boot/dts/imx6q-sabrelite.dts +++ b/arch/arm/boot/dts/imx6q-sabrelite.dts @@ -118,5 +118,9 @@ internal_clk = "false"; reg = <0x00000001 0x0>; /* reserve fb mem */ }; + + v4l2-out@0 { + compatible = "fsl,vout_ipuv3"; + }; }; }; diff --git a/drivers/media/video/mxc/output/mxc_vout.c b/drivers/media/video/mxc/output/mxc_vout.c index 57e684f9930..8092e670114 100644 --- a/drivers/media/video/mxc/output/mxc_vout.c +++ b/drivers/media/video/mxc/output/mxc_vout.c @@ -1630,9 +1630,15 @@ static int mxc_vout_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id mxc_vout_dt_ids[] = { + { .compatible = "fsl,vout_ipuv3", }, + { /* sentinel */ } +}; + static struct platform_driver mxc_vout_driver = { .driver = { .name = "mxc_v4l2_output", + .of_match_table = mxc_vout_dt_ids, }, .probe = mxc_vout_probe, .remove = mxc_vout_remove, -- cgit v1.2.3 From 7c621e29907194e4114d044c4eb5abd1b1d61d1f Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Mon, 5 Dec 2011 18:46:05 +0800 Subject: mxc.h: add dummy cpu_is_mx37() Signed-off-by: Jason Chen --- arch/arm/plat-mxc/include/mach/mxc.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/plat-mxc/include/mach/mxc.h b/arch/arm/plat-mxc/include/mach/mxc.h index eb49d7114ca..a74b87db893 100644 --- a/arch/arm/plat-mxc/include/mach/mxc.h +++ b/arch/arm/plat-mxc/include/mach/mxc.h @@ -192,6 +192,7 @@ extern unsigned int __mxc_cpu_type; # define cpu_is_mx53() (0) #endif +#define cpu_is_mx37() (0) #define cpu_is_mx6q() (0) #ifndef __ASSEMBLY__ -- cgit v1.2.3 From d0f289f67f0ff403d92880c410b009f1fd4e69f3 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Tue, 6 Dec 2011 15:47:50 +0800 Subject: MXC VPU: merge to fsl 2.6.38 branch to vpu commit c4c513c0f6d8ae7b77e05300a9496498c77fc767 Signed-off-by: Jason Chen --- arch/arm/plat-mxc/include/mach/mxc_vpu.h | 3 ++ drivers/mxc/vpu/Kconfig | 2 +- drivers/mxc/vpu/mxc_vpu.c | 66 ++++++++++++++++++++++---------- 3 files changed, 50 insertions(+), 21 deletions(-) diff --git a/arch/arm/plat-mxc/include/mach/mxc_vpu.h b/arch/arm/plat-mxc/include/mach/mxc_vpu.h index 0f81fb9eb7d..dd162a22dfe 100644 --- a/arch/arm/plat-mxc/include/mach/mxc_vpu.h +++ b/arch/arm/plat-mxc/include/mach/mxc_vpu.h @@ -55,11 +55,14 @@ struct vpu_mem_desc { #define VPU_IOC_REQ_VSHARE_MEM _IO(VPU_IOC_MAGIC, 9) #define VPU_IOC_SYS_SW_RESET _IO(VPU_IOC_MAGIC, 11) #define VPU_IOC_GET_SHARE_MEM _IO(VPU_IOC_MAGIC, 12) +#define VPU_IOC_QUERY_BITWORK_MEM _IO(VPU_IOC_MAGIC, 13) +#define VPU_IOC_SET_BITWORK_MEM _IO(VPU_IOC_MAGIC, 14) #define BIT_CODE_RUN 0x000 #define BIT_CODE_DOWN 0x004 #define BIT_INT_CLEAR 0x00C #define BIT_INT_STATUS 0x010 +#define BIT_INT_REASON 0x174 #define MJPEG_PIC_STATUS_REG 0x3004 diff --git a/drivers/mxc/vpu/Kconfig b/drivers/mxc/vpu/Kconfig index dada2040e2a..f712241fa85 100644 --- a/drivers/mxc/vpu/Kconfig +++ b/drivers/mxc/vpu/Kconfig @@ -6,7 +6,7 @@ menu "MXC VPU(Video Processing Unit) support" config MXC_VPU tristate "Support for MXC VPU(Video Processing Unit)" - depends on (ARCH_MX3 || ARCH_MX27 || ARCH_MX37 || ARCH_MX5 || ARCH_MX6) + depends on (ARCH_MX3 || ARCH_MX27 || ARCH_MX37 || ARCH_MX5 || SOC_IMX6Q) default y ---help--- The VPU codec device provides codec function for H.264/MPEG4/H.263, diff --git a/drivers/mxc/vpu/mxc_vpu.c b/drivers/mxc/vpu/mxc_vpu.c index 7bfe9bc8d37..27b09e56d5a 100644 --- a/drivers/mxc/vpu/mxc_vpu.c +++ b/drivers/mxc/vpu/mxc_vpu.c @@ -42,7 +42,6 @@ #include #include #include - #include struct vpu_priv { @@ -86,6 +85,7 @@ static struct mxc_vpu_platform_data *vpu_plat; static struct iram_setting iram; /* implement the blocking ioctl */ +static int irq_status; static int codec_done; static wait_queue_head_t vpu_queue; @@ -211,14 +211,17 @@ static inline void vpu_worker_callback(struct work_struct *w) if (dev->async_queue) kill_fasync(&dev->async_queue, SIGIO, POLL_IN); - codec_done = 1; - wake_up_interruptible(&vpu_queue); - + irq_status = 1; /* * Clock is gated on when dec/enc started, gate it off when - * interrupt is received. + * codec is done. */ - clk_disable(vpu_clk); + if (codec_done) { + clk_disable(vpu_clk); + codec_done = 0; + } + + wake_up_interruptible(&vpu_queue); } /*! @@ -227,8 +230,11 @@ static inline void vpu_worker_callback(struct work_struct *w) static irqreturn_t vpu_ipi_irq_handler(int irq, void *dev_id) { struct vpu_priv *dev = dev_id; + unsigned long reg; - READ_REG(BIT_INT_STATUS); + reg = READ_REG(BIT_INT_REASON); + if (reg & 0x8) + codec_done = 1; WRITE_REG(0x1, BIT_INT_CLEAR); queue_work(dev->workqueue, &dev->work); @@ -243,8 +249,11 @@ static irqreturn_t vpu_ipi_irq_handler(int irq, void *dev_id) static irqreturn_t vpu_jpu_irq_handler(int irq, void *dev_id) { struct vpu_priv *dev = dev_id; + unsigned long reg; - WRITE_REG(0, MJPEG_PIC_STATUS_REG); + reg = READ_REG(MJPEG_PIC_STATUS_REG); + if (reg & 0x3) + codec_done = 1; queue_work(dev->workqueue, &dev->work); @@ -351,7 +360,7 @@ static long vpu_ioctl(struct file *filp, u_int cmd, { u_long timeout = (u_long) arg; if (!wait_event_interruptible_timeout - (vpu_queue, codec_done != 0, + (vpu_queue, irq_status != 0, msecs_to_jiffies(timeout))) { printk(KERN_WARNING "VPU blocking: timeout.\n"); ret = -ETIME; @@ -360,7 +369,7 @@ static long vpu_ioctl(struct file *filp, u_int cmd, "VPU interrupt received.\n"); ret = -ERESTARTSYS; } else - codec_done = 0; + irq_status = 0; break; } case VPU_IOC_IRAM_SETTING: @@ -470,6 +479,26 @@ static long vpu_ioctl(struct file *filp, u_int cmd, } break; } + /* + * The following two ioctl is used when user allocates working buffer + * and register it to vpu driver. + */ + case VPU_IOC_QUERY_BITWORK_MEM: + { + if (copy_to_user((void __user *)arg, + &bitwork_mem, + sizeof(struct vpu_mem_desc))) + ret = -EFAULT; + break; + } + case VPU_IOC_SET_BITWORK_MEM: + { + if (copy_from_user(&bitwork_mem, + (struct vpu_mem_desc *)arg, + sizeof(struct vpu_mem_desc))) + ret = -EFAULT; + break; + } case VPU_IOC_SYS_SW_RESET: { if (vpu_plat->reset) @@ -624,7 +653,7 @@ static int vpu_dev_probe(struct platform_device *pdev) iram.end = addr + vpu_plat->iram_size - 1; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpu_regs"); if (!res) { printk(KERN_ERR "vpu: unable to get vpu base addr\n"); return -ENODEV; @@ -658,30 +687,27 @@ static int vpu_dev_probe(struct platform_device *pdev) goto err_out_class; } - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { + vpu_ipi_irq = platform_get_irq_byname(pdev, "vpu_ipi_irq"); + if (vpu_ipi_irq < 0) { printk(KERN_ERR "vpu: unable to get vpu interrupt\n"); err = -ENXIO; goto err_out_class; } - vpu_ipi_irq = res->start; - err = request_irq(vpu_ipi_irq, vpu_ipi_irq_handler, 0, "VPU_CODEC_IRQ", (void *)(&vpu_data)); if (err) goto err_out_class; #ifdef MXC_VPU_HAS_JPU - res = platform_get_resource(pdev, IORESOURCE_IRQ, 1); - if (!res) { + vpu_jpu_irq = platform_get_irq_byname(pdev, "vpu_jpu_irq"); + if (vpu_jpu_irq < 0) { printk(KERN_ERR "vpu: unable to get vpu jpu interrupt\n"); err = -ENXIO; free_irq(vpu_ipi_irq, &vpu_data); goto err_out_class; } - vpu_jpu_irq = res->start; - err = request_irq(vpu_jpu_irq, vpu_jpu_irq_handler, 0, "VPU_JPG_IRQ", - (void *)(&vpu_data)); + err = request_irq(vpu_jpu_irq, vpu_jpu_irq_handler, IRQF_TRIGGER_RISING, + "VPU_JPG_IRQ", (void *)(&vpu_data)); if (err) { free_irq(vpu_ipi_irq, &vpu_data); goto err_out_class; -- cgit v1.2.3 From a8ac1b26e2bdb8e9125e7689e5c1fbe0b535c7dc Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Tue, 6 Dec 2011 17:02:50 +0800 Subject: imx6q-sabrelite: add vpu dt support Signed-off-by: Jason Chen --- arch/arm/boot/dts/imx6q.dtsi | 3 ++- arch/arm/mach-imx/clock-imx6q.c | 1 + arch/arm/mach-imx/mach-imx6q.c | 16 ++++++++++++++++ arch/arm/mach-imx/src.c | 16 ++++++++++++++++ arch/arm/plat-mxc/include/mach/common.h | 1 + arch/arm/plat-mxc/include/mach/mx6q.h | 1 + drivers/mxc/vpu/mxc_vpu.c | 22 +++++++++++++++++++--- 7 files changed, 56 insertions(+), 4 deletions(-) diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi index c24f7723f15..c82bdee711c 100644 --- a/arch/arm/boot/dts/imx6q.dtsi +++ b/arch/arm/boot/dts/imx6q.dtsi @@ -203,8 +203,9 @@ }; vpu@02040000 { + compatible = "fsl,vpu"; reg = <0x02040000 0x3c000>; - interrupts = <0 3 0x04 0 12 0x04>; + interrupts = <0 12 0x04 0 3 0x04>; }; aipstz@0207c000 { /* AIPSTZ1 */ diff --git a/arch/arm/mach-imx/clock-imx6q.c b/arch/arm/mach-imx/clock-imx6q.c index c436a711d7e..5cdafb0e14a 100644 --- a/arch/arm/mach-imx/clock-imx6q.c +++ b/arch/arm/mach-imx/clock-imx6q.c @@ -1938,6 +1938,7 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK(NULL, "hdmi_iahb_clk", hdmi_iahb_clk), _REGISTER_CLOCK(NULL, "ldb_di0_clk", ldb_di0_clk), _REGISTER_CLOCK(NULL, "ldb_di1_clk", ldb_di1_clk), + _REGISTER_CLOCK(NULL, "vpu_clk", vpu_clk), }; int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode) diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c index 0584524611f..f5b31867953 100644 --- a/arch/arm/mach-imx/mach-imx6q.c +++ b/arch/arm/mach-imx/mach-imx6q.c @@ -30,6 +30,7 @@ #include #include #include +#include static iomux_v3_cfg_t imx6q_sabrelite_pads[] = { /* DISPLAY */ @@ -103,9 +104,21 @@ static struct imx_ipuv3_platform_data ipuv3_pdata = { .pg = mx6q_ipuv3_pg, }; +static void mx6q_vpu_reset(void) +{ + imx_reset_vpu(); +} + +static struct mxc_vpu_platform_data vpu_pdata = { + .iram_enable = true, + .iram_size = 0x21000, + .reset = mx6q_vpu_reset, +}; + static const struct of_dev_auxdata imx6q_auxdata_lookup[] __initconst = { OF_DEV_AUXDATA("fsl,ipuv3", MX6Q_IPU1_BASE_ADDR, "imx-ipuv3.0", &ipuv3_pdata), OF_DEV_AUXDATA("fsl,ipuv3", MX6Q_IPU2_BASE_ADDR, "imx-ipuv3.1", &ipuv3_pdata), + OF_DEV_AUXDATA("fsl,vpu", MX6Q_VPU_BASE_ADDR, "mxc_vpu.0", &vpu_pdata), }; static void __init imx6q_init_machine(void) @@ -148,6 +161,9 @@ static void __init imx6q_map_io(void) imx6q_iomux_map_io(); init_consistent_dma_size(SZ_64M); + + if (!system_rev) + system_rev = 0x63000; } static int __init imx6q_gpio_add_irq_domain(struct device_node *np, diff --git a/arch/arm/mach-imx/src.c b/arch/arm/mach-imx/src.c index ac8eb58aeeb..bcc4e93acc5 100644 --- a/arch/arm/mach-imx/src.c +++ b/arch/arm/mach-imx/src.c @@ -28,6 +28,22 @@ static void __iomem *src_base; #define cpu_logical_map(cpu) 0 #endif +void imx_reset_vpu(void) +{ + u32 val; + + /* mask interrupt due to vpu passed reset */ + val = readl_relaxed(src_base + 0x18); + val |= 0x02; + writel_relaxed(val, src_base + 0x18); + + val = readl_relaxed(src_base); + val |= 0x5; /* warm reset vpu */ + writel_relaxed(val, src_base); + while (readl_relaxed(src_base) & 0x04) + ; +} + void imx_reset_ipu(int ipu) { u32 val; diff --git a/arch/arm/plat-mxc/include/mach/common.h b/arch/arm/plat-mxc/include/mach/common.h index 21f4a9282cd..d6a191de71f 100644 --- a/arch/arm/plat-mxc/include/mach/common.h +++ b/arch/arm/plat-mxc/include/mach/common.h @@ -122,6 +122,7 @@ static inline void imx_scu_map_io(void) {} static inline void imx_smp_prepare(void) {} #endif extern void imx_reset_ipu(int ipu); +extern void imx_reset_vpu(void); extern void imx_enable_cpu(int cpu, bool enable); extern void imx_set_cpu_jump(int cpu, void *jump_addr); extern void imx_src_init(void); diff --git a/arch/arm/plat-mxc/include/mach/mx6q.h b/arch/arm/plat-mxc/include/mach/mx6q.h index 28b1561adf2..9292e2e4ecc 100644 --- a/arch/arm/plat-mxc/include/mach/mx6q.h +++ b/arch/arm/plat-mxc/include/mach/mx6q.h @@ -39,6 +39,7 @@ #define MX6Q_IRAM_BASE_ADDR 0x00900000 #define MX6Q_IRAM_SIZE (SZ_256K - SZ_4K) +#define MX6Q_VPU_BASE_ADDR 0x02040000 #define MX6Q_IPU1_BASE_ADDR 0x02400000 #define MX6Q_IPU2_BASE_ADDR 0x02800000 diff --git a/drivers/mxc/vpu/mxc_vpu.c b/drivers/mxc/vpu/mxc_vpu.c index 27b09e56d5a..4e2ff133bb1 100644 --- a/drivers/mxc/vpu/mxc_vpu.c +++ b/drivers/mxc/vpu/mxc_vpu.c @@ -19,6 +19,7 @@ * @ingroup VPU */ +#include #include #include #include @@ -653,7 +654,10 @@ static int vpu_dev_probe(struct platform_device *pdev) iram.end = addr + vpu_plat->iram_size - 1; } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpu_regs"); + if (pdev->dev.of_node) + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + else + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpu_regs"); if (!res) { printk(KERN_ERR "vpu: unable to get vpu base addr\n"); return -ENODEV; @@ -687,7 +691,10 @@ static int vpu_dev_probe(struct platform_device *pdev) goto err_out_class; } - vpu_ipi_irq = platform_get_irq_byname(pdev, "vpu_ipi_irq"); + if (pdev->dev.of_node) + vpu_ipi_irq = platform_get_irq(pdev, 0); + else + vpu_ipi_irq = platform_get_irq_byname(pdev, "vpu_ipi_irq"); if (vpu_ipi_irq < 0) { printk(KERN_ERR "vpu: unable to get vpu interrupt\n"); err = -ENXIO; @@ -699,7 +706,10 @@ static int vpu_dev_probe(struct platform_device *pdev) goto err_out_class; #ifdef MXC_VPU_HAS_JPU - vpu_jpu_irq = platform_get_irq_byname(pdev, "vpu_jpu_irq"); + if (pdev->dev.of_node) + vpu_jpu_irq = platform_get_irq(pdev, 1); + else + vpu_jpu_irq = platform_get_irq_byname(pdev, "vpu_jpu_irq"); if (vpu_jpu_irq < 0) { printk(KERN_ERR "vpu: unable to get vpu jpu interrupt\n"); err = -ENXIO; @@ -872,12 +882,18 @@ recover_clk: #define vpu_resume NULL #endif /* !CONFIG_PM */ +static const struct of_device_id mxc_vpu_dt_ids[] = { + { .compatible = "fsl,vpu", }, + { /* sentinel */ } +}; + /*! Driver definition * */ static struct platform_driver mxcvpu_driver = { .driver = { .name = "mxc_vpu", + .of_match_table = mxc_vpu_dt_ids, }, .probe = vpu_dev_probe, .remove = vpu_dev_remove, -- cgit v1.2.3 From 2aaf79f9e7595dd73bc0e9e53e982c9d616cb6d3 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Wed, 7 Dec 2011 11:37:37 +0800 Subject: MXC LDB: add hwtype support some setting related with hwtype. Signed-off-by: Jason Chen --- drivers/video/mxc/ldb.c | 49 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/drivers/video/mxc/ldb.c b/drivers/video/mxc/ldb.c index 983dbc957da..0fbf57444a6 100644 --- a/drivers/video/mxc/ldb.c +++ b/drivers/video/mxc/ldb.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include "mxc_dispdrv.h" @@ -98,6 +99,30 @@ struct ldb_data { int di; } setting[2]; struct notifier_block nb; + uint8_t hwtype; +}; + +enum mxc_ldb_hwtype { + IMX5_LDB, + IMX6_LDB, +}; + +static struct platform_device_id mxc_ldb_devtype[] = { + { + .name = "imx5-ldb", + .driver_data = IMX5_LDB, + }, { + .name = "imx6-ldb", + .driver_data = IMX6_LDB, + }, { + /* sentinel */ + } +}; + +static const struct of_device_id mxc_ldb_dt_ids[] = { + { .compatible = "fsl,imx5-ldb", .data = &mxc_ldb_devtype[IMX5_LDB], }, + { .compatible = "fsl,imx6q-ldb", .data = &mxc_ldb_devtype[IMX6_LDB], }, + { /* sentinel */ } }; static int g_ldb_mode; @@ -408,9 +433,12 @@ static int ldb_ipu_ldb_route(int ipu, int di, struct ldb_data *ldb) return 0; } -static int of_get_ldb_data(struct platform_device *pdev, +static int of_get_ldb_data(struct ldb_data *ldb, struct fsl_mxc_ldb_platform_data *plat_data) { + struct platform_device *pdev = ldb->pdev; + const struct of_device_id *of_id = + of_match_device(mxc_ldb_dt_ids, &pdev->dev); struct device_node *np = pdev->dev.of_node; uint32_t lvds0[2] = {0}, lvds1[2] = {0}; const char *mode, *ext_ref; @@ -419,6 +447,10 @@ static int of_get_ldb_data(struct platform_device *pdev, if (!np) return -EINVAL; + if (of_id) + pdev->id_entry = of_id->data; + ldb->hwtype = pdev->id_entry->driver_data; + ret = of_property_read_string(np, "mode", &mode); if (ret < 0) g_ldb_mode = LDB_SEP0; @@ -499,7 +531,7 @@ static int ldb_disp_init(struct mxc_dispdrv_entry *disp) if (!plat_data) { plat_data = &of_data; - if (of_get_ldb_data(ldb->pdev, plat_data) < 0) { + if (of_get_ldb_data(ldb, plat_data) < 0) { dev_err(&ldb->pdev->dev, "no platform data\n"); return -EINVAL; } @@ -621,7 +653,7 @@ static int ldb_disp_init(struct mxc_dispdrv_entry *disp) writel(reg, ldb->control_reg); /* clock setting */ - if (cpu_is_mx6q() && + if ((ldb->hwtype == IMX6_LDB) && ((ldb->mode == LDB_SEP0) || (ldb->mode == LDB_SEP1))) ldb_clk[6] += lvds_channel; else @@ -669,7 +701,7 @@ static int ldb_disp_init(struct mxc_dispdrv_entry *disp) return -EINVAL; } - if (cpu_is_mx6q()) { + if (ldb->hwtype == IMX6_LDB) { setting->dev_id = plat_data->sec_ipu_id; setting->disp_id = plat_data->sec_disp_id; } else { @@ -707,7 +739,7 @@ static int ldb_disp_init(struct mxc_dispdrv_entry *disp) writel(reg, ldb->control_reg); /* clock setting */ - if (cpu_is_mx6q()) + if (ldb->hwtype == IMX6_LDB) ldb_clk[6] += lvds_channel; else ldb_clk[6] += setting->disp_id; @@ -729,7 +761,7 @@ static int ldb_disp_init(struct mxc_dispdrv_entry *disp) setting_idx = 1; } - if (cpu_is_mx6q()) { + if (ldb->hwtype == IMX6_LDB) { 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; @@ -851,11 +883,6 @@ static int ldb_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id mxc_ldb_dt_ids[] = { - { .compatible = "fsl,imx6q-ldb", }, - { /* sentinel */ } -}; - static struct platform_driver mxcldb_driver = { .driver = { .name = "mxc_ldb", -- cgit v1.2.3 From 2320fa476ab5a56bd907d3f72f0ae94bc55e97e2 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Thu, 8 Dec 2011 15:38:39 +0800 Subject: imx6: change ipu_perclk to 8.25M according to fsl branch Signed-off-by: Jason Chen --- arch/arm/mach-imx/clock-imx6q.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/mach-imx/clock-imx6q.c b/arch/arm/mach-imx/clock-imx6q.c index 5cdafb0e14a..1029a3b2f6a 100644 --- a/arch/arm/mach-imx/clock-imx6q.c +++ b/arch/arm/mach-imx/clock-imx6q.c @@ -421,14 +421,17 @@ static unsigned long get_low_reference_clock_rate(struct clk *clk) } static struct clk ckil_clk = { + __INIT_CLK_DEBUG(ckil_clk) .get_rate = get_low_reference_clock_rate, }; static struct clk ckih_clk = { + __INIT_CLK_DEBUG(ckih_clk) .get_rate = get_high_reference_clock_rate, }; static struct clk osc_clk = { + __INIT_CLK_DEBUG(osc_clk) .get_rate = get_oscillator_reference_clock_rate, }; @@ -1939,6 +1942,7 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK(NULL, "ldb_di0_clk", ldb_di0_clk), _REGISTER_CLOCK(NULL, "ldb_di1_clk", ldb_di1_clk), _REGISTER_CLOCK(NULL, "vpu_clk", vpu_clk), + _REGISTER_CLOCK(NULL, "gpt_clk", gpt_clk), }; int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode) @@ -2035,6 +2039,9 @@ int __init mx6q_clocks_init(void) clk_set_rate(&asrc_serial_clk, 1500000); clk_set_rate(&enfc_clk, 11000000); + /* Lower the ipg_perclk frequency to 8.25MHz. */ + clk_set_rate(&ipg_perclk, 8250000); + /* * Before pinctrl API is available, we have to rely on the pad * configuration set up by bootloader. For usdhc example here, -- cgit v1.2.3 From b844c16efcd79ef2aaf71974b44403af20746c6a Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Thu, 8 Dec 2011 17:06:01 +0800 Subject: mxc hdmi core: add Kconfig dependency of MXC_IPU Signed-off-by: Jason Chen --- drivers/mfd/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index f90c819e4f4..6db8fb5a186 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -811,6 +811,7 @@ config MFD_INTEL_MSIC config MFD_MXC_HDMI tristate "MXC HDMI Core" + depends on MXC_IPU select MFD_CORE default y help -- cgit v1.2.3 From 07aede42ca7714453009892477efa1a7f164e1d4 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Fri, 9 Dec 2011 11:22:41 +0800 Subject: MXC SRC: refine IPU VPU reset function Signed-off-by: Jason Chen --- arch/arm/mach-imx/src.c | 46 +++++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/arch/arm/mach-imx/src.c b/arch/arm/mach-imx/src.c index bcc4e93acc5..9c6b01c488f 100644 --- a/arch/arm/mach-imx/src.c +++ b/arch/arm/mach-imx/src.c @@ -18,9 +18,16 @@ #include #define SRC_SCR 0x000 +#define SRC_SIMR 0x018 #define SRC_GPR1 0x020 #define BP_SRC_SCR_CORE1_RST 14 #define BP_SRC_SCR_CORE1_ENABLE 22 +#define BP_SRC_SCR_VPU_RST 2 +#define BP_SRC_SCR_IPU1_RST 3 +#define BP_SRC_SCR_IPU2_RST 12 +#define BP_SRC_SIMR_VPU_MASK 1 +#define BP_SRC_SIMR_IPU1_MASK 2 +#define BP_SRC_SIMR_IPU2_MASK 4 static void __iomem *src_base; @@ -33,28 +40,37 @@ void imx_reset_vpu(void) u32 val; /* mask interrupt due to vpu passed reset */ - val = readl_relaxed(src_base + 0x18); - val |= 0x02; - writel_relaxed(val, src_base + 0x18); - - val = readl_relaxed(src_base); - val |= 0x5; /* warm reset vpu */ - writel_relaxed(val, src_base); - while (readl_relaxed(src_base) & 0x04) + val = readl_relaxed(src_base + SRC_SIMR); + val |= (1 << BP_SRC_SIMR_VPU_MASK); + writel_relaxed(val, src_base + SRC_SIMR); + + val = readl_relaxed(src_base + SRC_SCR); + val |= (1 << BP_SRC_SCR_VPU_RST); /* reset vpu */ + writel_relaxed(val, src_base + SRC_SCR); + while (readl_relaxed(src_base + SRC_SCR) & + (1 << BP_SRC_SCR_VPU_RST)) ; } void imx_reset_ipu(int ipu) { u32 val; + u32 scr_off = ipu ? BP_SRC_SCR_IPU2_RST : BP_SRC_SCR_IPU1_RST; + u32 simr_off = ipu ? BP_SRC_SIMR_IPU2_MASK : BP_SRC_SIMR_IPU1_MASK; + + /* mask interrupt due to ipu passed reset */ + val = readl_relaxed(src_base + SRC_SIMR); + val |= (1 << simr_off); + writel_relaxed(val, src_base + SRC_SIMR); - /* hard reset the IPU */ - val = readl_relaxed(src_base); - if (ipu == 0) - val |= 1 << 3; - else - val |= 1 << 12; - writel_relaxed(val, src_base); + /* reset the IPU */ + val = readl_relaxed(src_base + SRC_SCR); + val |= (1 << scr_off); + writel_relaxed(val, src_base + SRC_SCR); + + while (readl_relaxed(src_base + SRC_SCR) & + (1 << scr_off)) + ; } void imx_enable_cpu(int cpu, bool enable) -- cgit v1.2.3 From 72efba263987891a9e252ac52f18c49a3adc715c Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Fri, 16 Dec 2011 17:49:19 +0800 Subject: SAUCE: fix hdmi registration failure Signed-off-by: Eric Miao --- drivers/mfd/mxc-hdmi-core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mfd/mxc-hdmi-core.c b/drivers/mfd/mxc-hdmi-core.c index 1ecdc0d9d8e..a211cd0f087 100644 --- a/drivers/mfd/mxc-hdmi-core.c +++ b/drivers/mfd/mxc-hdmi-core.c @@ -518,11 +518,13 @@ static int mxc_hdmi_core_probe(struct platform_device *pdev) /* register hdmi video */ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); pdevinfo_hdmi_v.name = "mxc_hdmi"; + pdevinfo_hdmi_v.id = pdev->id; pdevinfo_hdmi_v.res = res; pdevinfo_hdmi_v.num_res = 1; pdevinfo_hdmi_v.data = &hdmi_vdata; pdevinfo_hdmi_v.size_data = sizeof(hdmi_vdata); pdevinfo_hdmi_v.dma_mask = DMA_BIT_MASK(32); + pdevinfo_hdmi_v.parent = &pdev->dev; platform_device_register_full(&pdevinfo_hdmi_v); return ret; -- cgit v1.2.3 From ba1a90607765be2ff834e5fd5915f0f7db6be9e3 Mon Sep 17 00:00:00 2001 From: Tony Lin Date: Wed, 30 Nov 2011 16:17:15 +0800 Subject: ENGR00163616 [mx6q]hdmi will hang in daily build image make sure the pointer is valid before accessing Signed-off-by: Tony Lin --- drivers/video/mxc_hdmi.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/video/mxc_hdmi.c b/drivers/video/mxc_hdmi.c index 924264c32e2..1fcd760103c 100644 --- a/drivers/video/mxc_hdmi.c +++ b/drivers/video/mxc_hdmi.c @@ -1359,9 +1359,10 @@ static int mxc_hdmi_cable_connected(struct mxc_hdmi *hdmi) fb_var_to_videomode(&m, &hdmi->fbi->var); mode = fb_find_nearest_mode(&m, &hdmi->fbi->modelist); + if (mode) + fb_videomode_to_var(&hdmi->fbi->var, mode); - fb_videomode_to_var(&hdmi->fbi->var, mode); - hdmi->need_mode_change = true; + hdmi->need_mode_change = mode ? true : false; } else { /* If not EDID data readed, setup default modelist */ dev_info(&hdmi->pdev->dev, "No modes read from edid\n"); @@ -1370,9 +1371,10 @@ static int mxc_hdmi_cable_connected(struct mxc_hdmi *hdmi) fb_var_to_videomode(&m, &hdmi->fbi->var); mode = fb_find_nearest_mode(&m, &hdmi->fbi->modelist); + if (mode) + fb_videomode_to_var(&hdmi->fbi->var, mode); - fb_videomode_to_var(&hdmi->fbi->var, mode); - hdmi->need_mode_change = true; + hdmi->need_mode_change = mode ? true : false; } -- cgit v1.2.3 From 5ef5f6d89dc4996ebe21df5dcfe408cfcec4f158 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Tue, 6 Dec 2011 14:01:16 +0800 Subject: ENGR00169509-1 ipuv3 fb: change wait for vsync ioctl irq from eof to nfack change wait for vsync ioctl irq from eof to nfack Signed-off-by: Jason Chen --- arch/arm/plat-mxc/include/mach/ipu-v3.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/plat-mxc/include/mach/ipu-v3.h b/arch/arm/plat-mxc/include/mach/ipu-v3.h index bccb8811673..0405f2f9ed0 100644 --- a/arch/arm/plat-mxc/include/mach/ipu-v3.h +++ b/arch/arm/plat-mxc/include/mach/ipu-v3.h @@ -326,6 +326,10 @@ enum ipu_irq_line { IPU_IRQ_BG_ALPHA_SYNC_EOF = 51, IPU_IRQ_BG_ALPHA_ASYNC_EOF = 52, + IPU_IRQ_BG_SYNC_NFACK = 64 + 23, + IPU_IRQ_FG_SYNC_NFACK = 64 + 27, + IPU_IRQ_DC_SYNC_NFACK = 64 + 28, + IPU_IRQ_DP_SF_START = 448 + 2, IPU_IRQ_DP_SF_END = 448 + 3, IPU_IRQ_BG_SF_END = IPU_IRQ_DP_SF_END, -- cgit v1.2.3 From 338032137e0efe25b7079e3a4d03dcfecf9c505f Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Tue, 6 Dec 2011 14:01:34 +0800 Subject: ENGR00169509-2 ipuv3 fb: change wait for vsync ioctl irq from eof to nfack change wait for vsync ioctl irq from eof to nfack Signed-off-by: Jason Chen --- drivers/video/mxc/mxc_ipuv3_fb.c | 78 ++++++++++++++++++++++++++++++---------- 1 file changed, 60 insertions(+), 18 deletions(-) diff --git a/drivers/video/mxc/mxc_ipuv3_fb.c b/drivers/video/mxc/mxc_ipuv3_fb.c index 1447c455202..e5a4389b8ff 100644 --- a/drivers/video/mxc/mxc_ipuv3_fb.c +++ b/drivers/video/mxc/mxc_ipuv3_fb.c @@ -77,6 +77,7 @@ struct mxcfb_info { void *alpha_virt_addr1; uint32_t alpha_mem_len; uint32_t ipu_ch_irq; + uint32_t ipu_ch_nf_irq; uint32_t ipu_alp_ch_irq; uint32_t cur_ipu_buf; uint32_t cur_ipu_alpha_buf; @@ -84,7 +85,6 @@ struct mxcfb_info { u32 pseudo_palette[16]; bool mode_found; - volatile bool wait4vsync; struct semaphore flip_sem; struct semaphore alpha_flip_sem; struct completion vsync_complete; @@ -186,6 +186,7 @@ static struct fb_info *found_registered_fb(ipu_channel_t ipu_ch, int ipu_id) } static irqreturn_t mxcfb_irq_handler(int irq, void *dev_id); +static irqreturn_t mxcfb_nf_irq_handler(int irq, void *dev_id); static int mxcfb_blank(int blank, struct fb_info *info); static int mxcfb_map_video_memory(struct fb_info *fbi); static int mxcfb_unmap_video_memory(struct fb_info *fbi); @@ -324,6 +325,8 @@ static int mxcfb_set_par(struct fb_info *fbi) ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq); ipu_disable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq); + ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_nf_irq); + ipu_disable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_nf_irq); ipu_disable_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch, true); ipu_uninit_channel(mxc_fbi->ipu, mxc_fbi->ipu_ch); mxcfb_set_fix(fbi); @@ -468,6 +471,9 @@ static int _swap_channels(struct fb_info *fbi_from, tmp = mxc_fbi_from->ipu_ch_irq; mxc_fbi_from->ipu_ch_irq = mxc_fbi_to->ipu_ch_irq; mxc_fbi_to->ipu_ch_irq = tmp; + tmp = mxc_fbi_from->ipu_ch_nf_irq; + mxc_fbi_from->ipu_ch_nf_irq = mxc_fbi_to->ipu_ch_nf_irq; + mxc_fbi_to->ipu_ch_nf_irq = tmp; ovfbi = mxc_fbi_from->ovfbi; mxc_fbi_from->ovfbi = mxc_fbi_to->ovfbi; mxc_fbi_to->ovfbi = ovfbi; @@ -516,6 +522,10 @@ static int swap_channels(struct fb_info *fbi_from) ipu_clear_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_irq); ipu_free_irq(mxc_fbi_from->ipu, mxc_fbi_from->ipu_ch_irq, fbi_from); ipu_free_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_irq, fbi_to); + ipu_clear_irq(mxc_fbi_from->ipu, mxc_fbi_from->ipu_ch_nf_irq); + ipu_clear_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_nf_irq); + ipu_free_irq(mxc_fbi_from->ipu, mxc_fbi_from->ipu_ch_nf_irq, fbi_from); + ipu_free_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_nf_irq, fbi_to); if (mxc_fbi_from->cur_blank == FB_BLANK_UNBLANK) { if (mxc_fbi_to->cur_blank == FB_BLANK_UNBLANK) @@ -549,6 +559,9 @@ static int swap_channels(struct fb_info *fbi_from) i = mxc_fbi_from->ipu_ch_irq; mxc_fbi_from->ipu_ch_irq = mxc_fbi_to->ipu_ch_irq; mxc_fbi_to->ipu_ch_irq = i; + i = mxc_fbi_from->ipu_ch_nf_irq; + mxc_fbi_from->ipu_ch_nf_irq = mxc_fbi_to->ipu_ch_nf_irq; + mxc_fbi_to->ipu_ch_nf_irq = i; break; default: break; @@ -568,6 +581,20 @@ static int swap_channels(struct fb_info *fbi_from) return -EBUSY; } ipu_disable_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_irq); + if (ipu_request_irq(mxc_fbi_from->ipu, mxc_fbi_from->ipu_ch_nf_irq, mxcfb_nf_irq_handler, 0, + MXCFB_NAME, fbi_from) != 0) { + dev_err(fbi_from->device, "Error registering irq %d\n", + mxc_fbi_from->ipu_ch_nf_irq); + return -EBUSY; + } + ipu_disable_irq(mxc_fbi_from->ipu, mxc_fbi_from->ipu_ch_nf_irq); + if (ipu_request_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_nf_irq, mxcfb_irq_handler, 0, + MXCFB_NAME, fbi_to) != 0) { + dev_err(fbi_to->device, "Error registering irq %d\n", + mxc_fbi_to->ipu_ch_nf_irq); + return -EBUSY; + } + ipu_disable_irq(mxc_fbi_to->ipu, mxc_fbi_to->ipu_ch_nf_irq); return 0; } @@ -963,17 +990,14 @@ static int mxcfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg) } init_completion(&mxc_fbi->vsync_complete); - - ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq); - mxc_fbi->wait4vsync = true; - ipu_enable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq); + ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_nf_irq); + ipu_enable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_nf_irq); retval = wait_for_completion_interruptible_timeout( &mxc_fbi->vsync_complete, 1 * HZ); if (retval == 0) { dev_err(fbi->device, "MXCFB_WAIT_FOR_VSYNC: timeout %d\n", retval); - mxc_fbi->wait4vsync = false; retval = -ETIME; } else if (retval > 0) { retval = 0; @@ -1411,14 +1435,18 @@ static irqreturn_t mxcfb_irq_handler(int irq, void *dev_id) struct fb_info *fbi = dev_id; struct mxcfb_info *mxc_fbi = fbi->par; - if (mxc_fbi->wait4vsync) { - complete(&mxc_fbi->vsync_complete); - ipu_disable_irq(mxc_fbi->ipu, irq); - mxc_fbi->wait4vsync = false; - } else { - up(&mxc_fbi->flip_sem); - ipu_disable_irq(mxc_fbi->ipu, irq); - } + up(&mxc_fbi->flip_sem); + ipu_disable_irq(mxc_fbi->ipu, irq); + return IRQ_HANDLED; +} + +static irqreturn_t mxcfb_nf_irq_handler(int irq, void *dev_id) +{ + struct fb_info *fbi = dev_id; + struct mxcfb_info *mxc_fbi = fbi->par; + + complete(&mxc_fbi->vsync_complete); + ipu_disable_irq(mxc_fbi->ipu, irq); return IRQ_HANDLED; } @@ -1754,11 +1782,18 @@ static int mxcfb_register(struct fb_info *fbi) if (ipu_request_irq(mxcfbi->ipu, mxcfbi->ipu_ch_irq, mxcfb_irq_handler, 0, MXCFB_NAME, fbi) != 0) { - dev_err(fbi->device, "Error registering BG irq handler.\n"); + dev_err(fbi->device, "Error registering EOF irq handler.\n"); ret = -EBUSY; goto err0; } ipu_disable_irq(mxcfbi->ipu, mxcfbi->ipu_ch_irq); + if (ipu_request_irq(mxcfbi->ipu, mxcfbi->ipu_ch_nf_irq, mxcfb_nf_irq_handler, 0, + MXCFB_NAME, fbi) != 0) { + dev_err(fbi->device, "Error registering NFACK irq handler.\n"); + ret = -EBUSY; + goto err1; + } + ipu_disable_irq(mxcfbi->ipu, mxcfbi->ipu_ch_nf_irq); if (mxcfbi->ipu_alp_ch_irq != -1) if (ipu_request_irq(mxcfbi->ipu, mxcfbi->ipu_alp_ch_irq, @@ -1767,7 +1802,7 @@ static int mxcfb_register(struct fb_info *fbi) dev_err(fbi->device, "Error registering alpha irq " "handler.\n"); ret = -EBUSY; - goto err1; + goto err2; } mxcfb_check_var(&fbi->var, fbi); @@ -1795,12 +1830,14 @@ static int mxcfb_register(struct fb_info *fbi) ret = register_framebuffer(fbi); if (ret < 0) - goto err2; + goto err3; return ret; -err2: +err3: if (mxcfbi->ipu_alp_ch_irq != -1) ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_alp_ch_irq, fbi); +err2: + ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_ch_nf_irq, fbi); err1: ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_ch_irq, fbi); err0: @@ -1815,6 +1852,8 @@ static void mxcfb_unregister(struct fb_info *fbi) ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_alp_ch_irq, fbi); if (mxcfbi->ipu_ch_irq) ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_ch_irq, fbi); + if (mxcfbi->ipu_ch_nf_irq) + ipu_free_irq(mxcfbi->ipu, mxcfbi->ipu_ch_nf_irq, fbi); unregister_framebuffer(fbi); } @@ -1841,6 +1880,7 @@ static int mxcfb_setup_overlay(struct platform_device *pdev, } mxcfbi_fg->ipu_id = mxcfbi_bg->ipu_id; mxcfbi_fg->ipu_ch_irq = IPU_IRQ_FG_SYNC_EOF; + mxcfbi_fg->ipu_ch_nf_irq = IPU_IRQ_FG_SYNC_NFACK; mxcfbi_fg->ipu_alp_ch_irq = IPU_IRQ_FG_ALPHA_SYNC_EOF; mxcfbi_fg->ipu_ch = MEM_FG_SYNC; mxcfbi_fg->ipu_di = -1; @@ -2008,6 +2048,7 @@ static int mxcfb_probe(struct platform_device *pdev) /* first user uses DP with alpha feature */ if (!g_dp_in_use[mxcfbi->ipu_id]) { mxcfbi->ipu_ch_irq = IPU_IRQ_BG_SYNC_EOF; + mxcfbi->ipu_ch_nf_irq = IPU_IRQ_BG_SYNC_NFACK; mxcfbi->ipu_alp_ch_irq = IPU_IRQ_BG_ALPHA_SYNC_EOF; mxcfbi->ipu_ch = MEM_BG_SYNC; mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_UNBLANK; @@ -2028,6 +2069,7 @@ static int mxcfb_probe(struct platform_device *pdev) g_dp_in_use[mxcfbi->ipu_id] = true; } else { mxcfbi->ipu_ch_irq = IPU_IRQ_DC_SYNC_EOF; + mxcfbi->ipu_ch_nf_irq = IPU_IRQ_DC_SYNC_NFACK; mxcfbi->ipu_alp_ch_irq = -1; mxcfbi->ipu_ch = MEM_DC_SYNC; mxcfbi->cur_blank = mxcfbi->next_blank = FB_BLANK_POWERDOWN; -- cgit v1.2.3 From fd4d7eaf2f052878decaba0c95d12d6088c4ffac Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Tue, 27 Dec 2011 16:14:56 +0800 Subject: ENGR00163669-1 mxc fb: remove FB_EVENT_PREMODE_CHANGE for mxc fb drivers remove FB_EVENT_PREMODE_CHANGE for mxc ldb/tve drivers add dispdrv setup interface for ldb/tve drivers re-structure the dispdrv framework for display devices Signed-off-by: Wayne Zou --- drivers/video/fbmem.c | 11 ----- drivers/video/mxc/ldb.c | 98 ++++++++++++++++++++++----------------- drivers/video/mxc/mxc_dispdrv.c | 94 ++++++++++++++++++------------------- drivers/video/mxc/mxc_dispdrv.h | 34 ++++++++------ drivers/video/mxc/mxc_dvi.c | 9 ++-- drivers/video/mxc/mxc_ipuv3_fb.c | 19 ++++++-- drivers/video/mxc/mxc_lcdif.c | 9 ++-- drivers/video/mxc/mxcfb_sii902x.c | 9 ++-- drivers/video/mxc/tve.c | 37 +++++++++------ drivers/video/mxc_hdmi.c | 10 ++-- include/linux/fb.h | 2 - 11 files changed, 180 insertions(+), 152 deletions(-) diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index a65f5b57391..ad936295d8f 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -991,17 +991,6 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) old_var = info->var; info->var = *var; - /* call pre-mode change */ - if (flags & FBINFO_MISC_USEREVENT) { - struct fb_event event; - int evnt = FB_EVENT_PREMODE_CHANGE; - - info->flags &= ~FBINFO_MISC_USEREVENT; - event.info = info; - event.data = &mode; - fb_notifier_call_chain(evnt, &event); - } - if (info->fbops->fb_set_par) { ret = info->fbops->fb_set_par(info); diff --git a/drivers/video/mxc/ldb.c b/drivers/video/mxc/ldb.c index 0fbf57444a6..29c90a1fdd0 100644 --- a/drivers/video/mxc/ldb.c +++ b/drivers/video/mxc/ldb.c @@ -83,7 +83,7 @@ struct ldb_data { struct platform_device *pdev; - struct mxc_dispdrv_entry *disp_ldb; + struct mxc_dispdrv_handle *disp_ldb; uint32_t *reg; uint32_t *control_reg; uint32_t *gpr3_reg; @@ -236,6 +236,55 @@ static int find_ldb_setting(struct ldb_data *ldb, struct fb_info *fbi) return -EINVAL; } +static int ldb_disp_setup(struct mxc_dispdrv_handle *disp, struct fb_info *fbi) +{ + uint32_t reg; + uint32_t pixel_clk, rounded_pixel_clk; + struct clk *ldb_clk_parent; + struct ldb_data *ldb = mxc_dispdrv_getdata(disp); + int setting_idx, di; + + setting_idx = find_ldb_setting(ldb, fbi); + if (setting_idx < 0) + return setting_idx; + + di = ldb->setting[setting_idx].di; + + /* vsync setup */ + reg = readl(ldb->control_reg); + if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT) { + if (di == 0) + reg = (reg & ~LDB_DI0_VS_POL_MASK) + | LDB_DI0_VS_POL_ACT_HIGH; + else + reg = (reg & ~LDB_DI1_VS_POL_MASK) + | LDB_DI1_VS_POL_ACT_HIGH; + } else { + if (di == 0) + reg = (reg & ~LDB_DI0_VS_POL_MASK) + | LDB_DI0_VS_POL_ACT_LOW; + else + reg = (reg & ~LDB_DI1_VS_POL_MASK) + | LDB_DI1_VS_POL_ACT_LOW; + } + writel(reg, ldb->control_reg); + + /* clk setup */ + pixel_clk = (PICOS2KHZ(fbi->var.pixclock)) * 1000UL; + ldb_clk_parent = clk_get_parent(ldb->ldb_di_clk[di]); + if ((ldb->mode == LDB_SPL_DI0) || (ldb->mode == LDB_SPL_DI1)) + clk_set_rate(ldb_clk_parent, pixel_clk * 7 / 2); + else + clk_set_rate(ldb_clk_parent, pixel_clk * 7); + rounded_pixel_clk = clk_round_rate(ldb->ldb_di_clk[di], + pixel_clk); + clk_set_rate(ldb->ldb_di_clk[di], rounded_pixel_clk); + clk_enable(ldb->ldb_di_clk[di]); + ldb->setting[setting_idx].clk_en = true; + + return 0; +} + int ldb_fb_event(struct notifier_block *nb, unsigned long val, void *v) { struct ldb_data *ldb = container_of(nb, struct ldb_data, nb); @@ -264,45 +313,6 @@ int ldb_fb_event(struct notifier_block *nb, unsigned long val, void *v) } switch (val) { - case FB_EVENT_PREMODE_CHANGE: - { - uint32_t reg; - uint32_t pixel_clk, rounded_pixel_clk; - struct clk *ldb_clk_parent; - - /* vsync setup */ - reg = readl(ldb->control_reg); - if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT) { - if (di == 0) - reg = (reg & ~LDB_DI0_VS_POL_MASK) - | LDB_DI0_VS_POL_ACT_HIGH; - else - reg = (reg & ~LDB_DI1_VS_POL_MASK) - | LDB_DI1_VS_POL_ACT_HIGH; - } else { - if (di == 0) - reg = (reg & ~LDB_DI0_VS_POL_MASK) - | LDB_DI0_VS_POL_ACT_LOW; - else - reg = (reg & ~LDB_DI1_VS_POL_MASK) - | LDB_DI1_VS_POL_ACT_LOW; - } - writel(reg, ldb->control_reg); - - /* clk setup */ - pixel_clk = (PICOS2KHZ(fbi->var.pixclock)) * 1000UL; - ldb_clk_parent = clk_get_parent(ldb->ldb_di_clk[di]); - if ((ldb->mode == LDB_SPL_DI0) || (ldb->mode == LDB_SPL_DI1)) - clk_set_rate(ldb_clk_parent, pixel_clk * 7 / 2); - else - clk_set_rate(ldb_clk_parent, pixel_clk * 7); - rounded_pixel_clk = clk_round_rate(ldb->ldb_di_clk[di], - pixel_clk); - clk_set_rate(ldb->ldb_di_clk[di], rounded_pixel_clk); - clk_enable(ldb->ldb_di_clk[di]); - ldb->setting[setting_idx].clk_en = true; - break; - } case FB_EVENT_BLANK: { if (*((int *)event->data) == FB_BLANK_UNBLANK) { @@ -511,11 +521,11 @@ static int of_get_ldb_data(struct ldb_data *ldb, return 0; } -static int ldb_disp_init(struct mxc_dispdrv_entry *disp) +static int ldb_disp_init(struct mxc_dispdrv_handle *disp, + struct mxc_dispdrv_setting *setting) { int ret = 0, i; struct ldb_data *ldb = mxc_dispdrv_getdata(disp); - struct mxc_dispdrv_setting *setting = mxc_dispdrv_getsetting(disp); struct fsl_mxc_ldb_platform_data of_data; struct fsl_mxc_ldb_platform_data *plat_data = ldb->pdev->dev.platform_data; struct resource *res; @@ -801,7 +811,7 @@ static int ldb_disp_init(struct mxc_dispdrv_entry *disp) return ret; } -static void ldb_disp_deinit(struct mxc_dispdrv_entry *disp) +static void ldb_disp_deinit(struct mxc_dispdrv_handle *disp) { struct ldb_data *ldb = mxc_dispdrv_getdata(disp); int i; @@ -822,6 +832,7 @@ static struct mxc_dispdrv_driver ldb_drv = { .name = DISPDRV_LDB, .init = ldb_disp_init, .deinit = ldb_disp_deinit, + .setup = ldb_disp_setup, }; static void ldb_disp_pwr_up(struct platform_device *pdev) @@ -878,6 +889,7 @@ static int ldb_remove(struct platform_device *pdev) { struct ldb_data *ldb = dev_get_drvdata(&pdev->dev); + mxc_dispdrv_puthandle(ldb->disp_ldb); mxc_dispdrv_unregister(ldb->disp_ldb); kfree(ldb); return 0; diff --git a/drivers/video/mxc/mxc_dispdrv.c b/drivers/video/mxc/mxc_dispdrv.c index 06b7af944f5..b455a6ab06f 100644 --- a/drivers/video/mxc/mxc_dispdrv.c +++ b/drivers/video/mxc/mxc_dispdrv.c @@ -22,8 +22,9 @@ * Move all dev_suspend() things into fb_notifier for SUSPEND, if there is; * Move all dev_resume() things into fb_notifier for RESUME, if there is; * - * ipuv3 fb driver could call mxc_dispdrv_init(setting) before a fb need be added, with fbi param - * passing by setting, after mxc_dispdrv_init() return, FB driver should get the basic setting + * ipuv3 fb driver could call mxc_dispdrv_gethandle(name, setting) before a fb + * need be added, with fbi param passing by setting, after + * mxc_dispdrv_gethandle() return, FB driver should get the basic setting * about fbi info and ipuv3-hw (ipu_id and disp_id). * * @ingroup Framebuffer @@ -42,16 +43,15 @@ static LIST_HEAD(dispdrv_list); static DEFINE_MUTEX(dispdrv_lock); struct mxc_dispdrv_entry { - const char *name; - struct list_head list; - int (*init) (struct mxc_dispdrv_entry *); - void (*deinit) (struct mxc_dispdrv_entry *); + /* Note: drv always the first element */ + struct mxc_dispdrv_driver *drv; bool active; struct mxc_dispdrv_setting setting; void *priv; + struct list_head list; }; -struct mxc_dispdrv_entry *mxc_dispdrv_register(struct mxc_dispdrv_driver *drv) +struct mxc_dispdrv_handle *mxc_dispdrv_register(struct mxc_dispdrv_driver *drv) { struct mxc_dispdrv_entry *new; @@ -63,23 +63,20 @@ struct mxc_dispdrv_entry *mxc_dispdrv_register(struct mxc_dispdrv_driver *drv) return ERR_PTR(-ENOMEM); } - new->name = drv->name; - new->init = drv->init; - new->deinit = drv->deinit; - + new->drv = drv; list_add_tail(&new->list, &dispdrv_list); mutex_unlock(&dispdrv_lock); - return new; + return (struct mxc_dispdrv_handle *)new; } EXPORT_SYMBOL_GPL(mxc_dispdrv_register); -int mxc_dispdrv_unregister(struct mxc_dispdrv_entry *entry) +int mxc_dispdrv_unregister(struct mxc_dispdrv_handle *handle) { + struct mxc_dispdrv_entry *entry = (struct mxc_dispdrv_entry *)handle; + if (entry) { mutex_lock(&dispdrv_lock); - if (entry->active && entry->deinit) - entry->deinit(entry); list_del(&entry->list); mutex_unlock(&dispdrv_lock); kfree(entry); @@ -89,41 +86,48 @@ int mxc_dispdrv_unregister(struct mxc_dispdrv_entry *entry) } EXPORT_SYMBOL_GPL(mxc_dispdrv_unregister); -int mxc_dispdrv_init(char *name, struct mxc_dispdrv_setting *setting) +struct mxc_dispdrv_handle *mxc_dispdrv_gethandle(char *name, + struct mxc_dispdrv_setting *setting) { - int ret = 0, found = 0; - struct mxc_dispdrv_entry *disp; + int ret, found = 0; + struct mxc_dispdrv_entry *entry; mutex_lock(&dispdrv_lock); - list_for_each_entry(disp, &dispdrv_list, list) { - if (!strcmp(disp->name, name)) { - if (disp->init) { - memcpy(&disp->setting, setting, - sizeof(struct mxc_dispdrv_setting)); - ret = disp->init(disp); - if (ret >= 0) { - disp->active = true; - /* setting may need fix-up */ - memcpy(setting, &disp->setting, - sizeof(struct mxc_dispdrv_setting)); - found = 1; - break; - } + list_for_each_entry(entry, &dispdrv_list, list) { + if (!strcmp(entry->drv->name, name) && (entry->drv->init)) { + ret = entry->drv->init((struct mxc_dispdrv_handle *) + entry, setting); + if (ret >= 0) { + entry->active = true; + found = 1; + break; } } } - if (!found) - ret = -EINVAL; - mutex_unlock(&dispdrv_lock); - return ret; + return found ? (struct mxc_dispdrv_handle *)entry:ERR_PTR(-ENODEV); +} +EXPORT_SYMBOL_GPL(mxc_dispdrv_gethandle); + +void mxc_dispdrv_puthandle(struct mxc_dispdrv_handle *handle) +{ + struct mxc_dispdrv_entry *entry = (struct mxc_dispdrv_entry *)handle; + + mutex_lock(&dispdrv_lock); + if (entry && entry->active && entry->drv->deinit) { + entry->drv->deinit(handle); + entry->active = false; + } + mutex_unlock(&dispdrv_lock); } -EXPORT_SYMBOL_GPL(mxc_dispdrv_init); +EXPORT_SYMBOL_GPL(mxc_dispdrv_puthandle); -int mxc_dispdrv_setdata(struct mxc_dispdrv_entry *entry, void *data) +int mxc_dispdrv_setdata(struct mxc_dispdrv_handle *handle, void *data) { + struct mxc_dispdrv_entry *entry = (struct mxc_dispdrv_entry *)handle; + if (entry) { entry->priv = data; return 0; @@ -132,21 +136,13 @@ int mxc_dispdrv_setdata(struct mxc_dispdrv_entry *entry, void *data) } EXPORT_SYMBOL_GPL(mxc_dispdrv_setdata); -void *mxc_dispdrv_getdata(struct mxc_dispdrv_entry *entry) +void *mxc_dispdrv_getdata(struct mxc_dispdrv_handle *handle) { + struct mxc_dispdrv_entry *entry = (struct mxc_dispdrv_entry *)handle; + if (entry) { return entry->priv; } else return ERR_PTR(-EINVAL); } EXPORT_SYMBOL_GPL(mxc_dispdrv_getdata); - -struct mxc_dispdrv_setting - *mxc_dispdrv_getsetting(struct mxc_dispdrv_entry *entry) -{ - if (entry) { - return &entry->setting; - } else - return ERR_PTR(-EINVAL); -} -EXPORT_SYMBOL_GPL(mxc_dispdrv_getsetting); diff --git a/drivers/video/mxc/mxc_dispdrv.h b/drivers/video/mxc/mxc_dispdrv.h index f5f62a2c3cb..580e9d2c561 100644 --- a/drivers/video/mxc/mxc_dispdrv.h +++ b/drivers/video/mxc/mxc_dispdrv.h @@ -13,12 +13,8 @@ #ifndef __MXC_DISPDRV_H__ #define __MXC_DISPDRV_H__ -struct mxc_dispdrv_entry; - -struct mxc_dispdrv_driver { - const char *name; - int (*init) (struct mxc_dispdrv_entry *); - void (*deinit) (struct mxc_dispdrv_entry *); +struct mxc_dispdrv_handle { + struct mxc_dispdrv_driver *drv; }; struct mxc_dispdrv_setting { @@ -33,11 +29,23 @@ struct mxc_dispdrv_setting { int disp_id; }; -struct mxc_dispdrv_entry *mxc_dispdrv_register(struct mxc_dispdrv_driver *drv); -int mxc_dispdrv_unregister(struct mxc_dispdrv_entry *entry); -int mxc_dispdrv_init(char *name, struct mxc_dispdrv_setting *setting); -int mxc_dispdrv_setdata(struct mxc_dispdrv_entry *entry, void *data); -void *mxc_dispdrv_getdata(struct mxc_dispdrv_entry *entry); -struct mxc_dispdrv_setting - *mxc_dispdrv_getsetting(struct mxc_dispdrv_entry *entry); +struct mxc_dispdrv_driver { + const char *name; + int (*init) (struct mxc_dispdrv_handle *, struct mxc_dispdrv_setting *); + void (*deinit) (struct mxc_dispdrv_handle *); + /* display driver enable function for extension */ + int (*enable) (struct mxc_dispdrv_handle *); + /* display driver disable function, called at early part of fb_blank */ + void (*disable) (struct mxc_dispdrv_handle *); + /* display driver setup function, called at early part of fb_set_par */ + int (*setup) (struct mxc_dispdrv_handle *, struct fb_info *fbi); +}; + +struct mxc_dispdrv_handle *mxc_dispdrv_register(struct mxc_dispdrv_driver *drv); +int mxc_dispdrv_unregister(struct mxc_dispdrv_handle *handle); +struct mxc_dispdrv_handle *mxc_dispdrv_gethandle(char *name, + struct mxc_dispdrv_setting *setting); +void mxc_dispdrv_puthandle(struct mxc_dispdrv_handle *handle); +int mxc_dispdrv_setdata(struct mxc_dispdrv_handle *handle, void *data); +void *mxc_dispdrv_getdata(struct mxc_dispdrv_handle *handle); #endif diff --git a/drivers/video/mxc/mxc_dvi.c b/drivers/video/mxc/mxc_dvi.c index 765670c8098..a286e51014e 100644 --- a/drivers/video/mxc/mxc_dvi.c +++ b/drivers/video/mxc/mxc_dvi.c @@ -54,7 +54,7 @@ struct mxc_dvi_data { struct i2c_client *client; struct platform_device *pdev; - struct mxc_dispdrv_entry *disp_dvi; + struct mxc_dispdrv_handle *disp_dvi; struct delayed_work det_work; struct fb_info *fbi; struct mxc_edid_cfg edid_cfg; @@ -180,11 +180,11 @@ static irqreturn_t mxc_dvi_detect_handler(int irq, void *data) return IRQ_HANDLED; } -static int dvi_init(struct mxc_dispdrv_entry *disp) +static int dvi_init(struct mxc_dispdrv_handle *disp, + struct mxc_dispdrv_setting *setting) { 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; @@ -283,7 +283,7 @@ err: return ret; } -static void dvi_deinit(struct mxc_dispdrv_entry *disp) +static void dvi_deinit(struct mxc_dispdrv_handle *disp) { struct mxc_dvi_data *dvi = mxc_dispdrv_getdata(disp); @@ -341,6 +341,7 @@ static int __devexit mxc_dvi_remove(struct i2c_client *client) { struct mxc_dvi_data *dvi = i2c_get_clientdata(client); + mxc_dispdrv_puthandle(dvi->disp_dvi); mxc_dispdrv_unregister(dvi->disp_dvi); platform_device_unregister(dvi->pdev); kfree(dvi); diff --git a/drivers/video/mxc/mxc_ipuv3_fb.c b/drivers/video/mxc/mxc_ipuv3_fb.c index e5a4389b8ff..70b7be1df0b 100644 --- a/drivers/video/mxc/mxc_ipuv3_fb.c +++ b/drivers/video/mxc/mxc_ipuv3_fb.c @@ -92,6 +92,7 @@ struct mxcfb_info { void *ipu; struct fb_info *ovfbi; + struct mxc_dispdrv_handle *dispdrv; struct ipuv3_fb_platform_data of_data; }; @@ -323,6 +324,15 @@ static int mxcfb_set_par(struct fb_info *fbi) dev_dbg(fbi->device, "Reconfiguring framebuffer\n"); + if (mxc_fbi->dispdrv && mxc_fbi->dispdrv->drv->setup) { + retval = mxc_fbi->dispdrv->drv->setup(mxc_fbi->dispdrv, fbi); + if (retval < 0) { + dev_err(fbi->device, "setup error, dispdrv:%s.\n", + mxc_fbi->dispdrv->drv->name); + return -EINVAL; + } + } + ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq); ipu_disable_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_irq); ipu_clear_irq(mxc_fbi->ipu, mxc_fbi->ipu_ch_nf_irq); @@ -1694,10 +1704,11 @@ static int mxcfb_dispdrv_init(struct platform_device *pdev, dev_info(&pdev->dev, "register mxc display driver %s\n", disp_dev); - ret = mxc_dispdrv_init(disp_dev, &setting); - if (ret < 0) { - dev_err(&pdev->dev, - "register mxc display driver failed with %d\n", ret); + mxcfbi->dispdrv = mxc_dispdrv_gethandle(disp_dev, &setting); + if (IS_ERR(mxcfbi->dispdrv)) { + ret = PTR_ERR(mxcfbi->dispdrv); + dev_err(&pdev->dev, "NO mxc display driver found!\n"); + return ret; } else { /* fix-up */ mxcfbi->ipu_di_pix_fmt = setting.if_fmt; diff --git a/drivers/video/mxc/mxc_lcdif.c b/drivers/video/mxc/mxc_lcdif.c index d9d7fa306a8..5cbdc73c4e7 100644 --- a/drivers/video/mxc/mxc_lcdif.c +++ b/drivers/video/mxc/mxc_lcdif.c @@ -21,7 +21,7 @@ struct mxc_lcdif_data { struct platform_device *pdev; - struct mxc_dispdrv_entry *disp_lcdif; + struct mxc_dispdrv_handle *disp_lcdif; }; #define DISPDRV_LCD "lcd" @@ -42,11 +42,11 @@ static struct fb_videomode lcdif_modedb[] = { }; static int lcdif_modedb_sz = ARRAY_SIZE(lcdif_modedb); -static int lcdif_init(struct mxc_dispdrv_entry *disp) +static int lcdif_init(struct mxc_dispdrv_handle *disp, + struct mxc_dispdrv_setting *setting) { int ret, i; struct mxc_lcdif_data *lcdif = mxc_dispdrv_getdata(disp); - struct mxc_dispdrv_setting *setting = mxc_dispdrv_getsetting(disp); struct fsl_mxc_lcd_platform_data *plat_data = lcdif->pdev->dev.platform_data; struct fb_videomode *modedb = lcdif_modedb; @@ -77,7 +77,7 @@ static int lcdif_init(struct mxc_dispdrv_entry *disp) return ret; } -void lcdif_deinit(struct mxc_dispdrv_entry *disp) +void lcdif_deinit(struct mxc_dispdrv_handle *disp) { /*TODO*/ } @@ -113,6 +113,7 @@ static int mxc_lcdif_remove(struct platform_device *pdev) { struct mxc_lcdif_data *lcdif = dev_get_drvdata(&pdev->dev); + mxc_dispdrv_puthandle(lcdif->disp_lcdif); mxc_dispdrv_unregister(lcdif->disp_lcdif); kfree(lcdif); return 0; diff --git a/drivers/video/mxc/mxcfb_sii902x.c b/drivers/video/mxc/mxcfb_sii902x.c index f626f649f9c..f917674423c 100644 --- a/drivers/video/mxc/mxcfb_sii902x.c +++ b/drivers/video/mxc/mxcfb_sii902x.c @@ -188,7 +188,7 @@ struct sii902x_data { struct platform_device *pdev; struct i2c_client *client; - struct mxc_dispdrv_entry *disp_hdmi; + struct mxc_dispdrv_handle *disp_hdmi; struct regulator *io_reg; struct regulator *analog_reg; struct delayed_work det_work; @@ -1064,11 +1064,11 @@ static int sii902x_TPI_init(struct i2c_client *client) return 0; } -static int sii902x_disp_init(struct mxc_dispdrv_entry *disp) +static int sii902x_disp_init(struct mxc_dispdrv_handle *disp, + struct mxc_dispdrv_setting *setting) { int ret = 0; struct sii902x_data *sii902x = mxc_dispdrv_getdata(disp); - struct mxc_dispdrv_setting *setting = mxc_dispdrv_getsetting(disp); struct fsl_mxc_lcd_platform_data *plat = sii902x->client->dev.platform_data; bool found = false; static bool inited; @@ -1215,7 +1215,7 @@ register_pltdev_failed: return ret; } -static void sii902x_disp_deinit(struct mxc_dispdrv_entry *disp) +static void sii902x_disp_deinit(struct mxc_dispdrv_handle *disp) { struct sii902x_data *sii902x = mxc_dispdrv_getdata(disp); struct fsl_mxc_lcd_platform_data *plat = sii902x->client->dev.platform_data; @@ -1273,6 +1273,7 @@ static int __devexit sii902x_remove(struct i2c_client *client) { struct sii902x_data *sii902x = i2c_get_clientdata(client); + mxc_dispdrv_puthandle(sii902x->disp_hdmi); mxc_dispdrv_unregister(sii902x->disp_hdmi); kfree(sii902x); return 0; diff --git a/drivers/video/mxc/tve.c b/drivers/video/mxc/tve.c index 98b506d3730..bd125964a6d 100644 --- a/drivers/video/mxc/tve.c +++ b/drivers/video/mxc/tve.c @@ -120,8 +120,8 @@ struct tve_data { struct delayed_work cd_work; struct tve_reg_mapping *regs; struct tve_reg_fields_mapping *reg_fields; - struct mxc_dispdrv_entry *disp_tve; - struct mxc_dispdrv_entry *disp_vga; + struct mxc_dispdrv_handle *disp_tve; + struct mxc_dispdrv_handle *disp_vga; struct notifier_block nb; }; @@ -934,6 +934,15 @@ int tve_fb_setup(struct tve_data *tve, struct fb_info *fbi) return 0; } +static inline int tve_disp_setup(struct mxc_dispdrv_handle *disp, + struct fb_info *fbi) +{ + struct tve_data *tve = mxc_dispdrv_getdata(disp); + + tve_fb_setup(tve, fbi); + return 0; +} + int tve_fb_event(struct notifier_block *nb, unsigned long val, void *v) { struct tve_data *tve = container_of(nb, struct tve_data, nb); @@ -945,11 +954,6 @@ int tve_fb_event(struct notifier_block *nb, unsigned long val, void *v) return 0; switch (val) { - case FB_EVENT_PREMODE_CHANGE: - { - tve_fb_setup(tve, fbi); - break; - } case FB_EVENT_BLANK: if (fbi->mode == NULL) return 0; @@ -1028,11 +1032,11 @@ static int _tve_get_revision(struct tve_data *tve) return rev; } -static int tve_drv_init(struct mxc_dispdrv_entry *disp, bool vga) +static int tve_drv_init(struct mxc_dispdrv_handle *disp, bool vga, + struct mxc_dispdrv_setting *setting) { int ret; struct tve_data *tve = mxc_dispdrv_getdata(disp); - struct mxc_dispdrv_setting *setting = mxc_dispdrv_getsetting(disp); struct fsl_mxc_tve_platform_data *plat_data = tve->pdev->dev.platform_data; struct resource *res; @@ -1190,17 +1194,19 @@ get_res_failed: } -static int tvout_init(struct mxc_dispdrv_entry *disp) +static int tvout_init(struct mxc_dispdrv_handle *disp, + struct mxc_dispdrv_setting *setting) { - return tve_drv_init(disp, 0); + return tve_drv_init(disp, 0, setting); } -static int vga_init(struct mxc_dispdrv_entry *disp) +static int vga_init(struct mxc_dispdrv_handle *disp, + struct mxc_dispdrv_setting *setting) { - return tve_drv_init(disp, 1); + return tve_drv_init(disp, 1, setting); } -void tvout_deinit(struct mxc_dispdrv_entry *disp) +void tvout_deinit(struct mxc_dispdrv_handle *disp) { struct tve_data *tve = mxc_dispdrv_getdata(disp); @@ -1223,6 +1229,7 @@ static struct mxc_dispdrv_driver vga_drv = { .name = DISPDRV_VGA, .init = vga_init, .deinit = tvout_deinit, + .setup = tve_disp_setup, }; static int tve_dispdrv_init(struct tve_data *tve) @@ -1236,6 +1243,8 @@ static int tve_dispdrv_init(struct tve_data *tve) static void tve_dispdrv_deinit(struct tve_data *tve) { + mxc_dispdrv_puthandle(tve->disp_tve); + mxc_dispdrv_puthandle(tve->disp_vga); mxc_dispdrv_unregister(tve->disp_tve); mxc_dispdrv_unregister(tve->disp_vga); } diff --git a/drivers/video/mxc_hdmi.c b/drivers/video/mxc_hdmi.c index 1fcd760103c..0977210b477 100644 --- a/drivers/video/mxc_hdmi.c +++ b/drivers/video/mxc_hdmi.c @@ -117,7 +117,7 @@ struct hdmi_data_info { struct mxc_hdmi { struct platform_device *pdev; struct platform_device *core_pdev; - struct mxc_dispdrv_entry *disp_mxc_hdmi; + struct mxc_dispdrv_handle *disp_mxc_hdmi; struct fb_info *fbi; struct clk *hdmi_isfr_clk; struct clk *hdmi_iahb_clk; @@ -1617,11 +1617,11 @@ static int mxc_hdmi_fb_event(struct notifier_block *nb, return 0; } -static int mxc_hdmi_disp_init(struct mxc_dispdrv_entry *disp) +static int mxc_hdmi_disp_init(struct mxc_dispdrv_handle *disp, + struct mxc_dispdrv_setting *setting) { int ret = 0; struct mxc_hdmi *hdmi = mxc_dispdrv_getdata(disp); - struct mxc_dispdrv_setting *setting = mxc_dispdrv_getsetting(disp); struct fsl_mxc_hdmi_platform_data *plat = hdmi->pdev->dev.platform_data; int irq = platform_get_irq(hdmi->pdev, 0); bool found = false; @@ -1811,7 +1811,7 @@ egetpins: return ret; } -static void mxc_hdmi_disp_deinit(struct mxc_dispdrv_entry *disp) +static void mxc_hdmi_disp_deinit(struct mxc_dispdrv_handle *disp) { struct mxc_hdmi *hdmi = mxc_dispdrv_getdata(disp); struct fsl_mxc_hdmi_platform_data *plat = hdmi->pdev->dev.platform_data; @@ -1893,6 +1893,8 @@ static int mxc_hdmi_remove(struct platform_device *pdev) fb_unregister_client(&hdmi->nb); + mxc_dispdrv_puthandle(hdmi->disp_mxc_hdmi); + mxc_dispdrv_unregister(hdmi->disp_mxc_hdmi); /* No new work will be scheduled, wait for running ISR */ free_irq(irq, hdmi); kfree(hdmi); diff --git a/include/linux/fb.h b/include/linux/fb.h index a67fe1e2b02..1d6836c498d 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -549,8 +549,6 @@ struct fb_cursor_user { #define FB_EVENT_FB_UNBIND 0x0E /* CONSOLE-SPECIFIC: remap all consoles to new fb - for vga switcheroo */ #define FB_EVENT_REMAP_ALL_CONSOLE 0x0F -/* PRE MODE CHANGE added by fsl */ -#define FB_EVENT_PREMODE_CHANGE 0x10 struct fb_event { struct fb_info *info; -- cgit v1.2.3 From e9c4ab4d44dca63faafba62054896ef196623095 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Wed, 7 Dec 2011 10:22:57 +0800 Subject: ENGR00169657 mxc_edid: no aspect vmode setting for detailed timing block Add aspect ratio setting into vmode for detailed timing block. Signed-off-by: Jason Chen --- drivers/video/mxc/mxc_edid.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/video/mxc/mxc_edid.c b/drivers/video/mxc/mxc_edid.c index 85a9063037a..5c407b9bc92 100644 --- a/drivers/video/mxc/mxc_edid.c +++ b/drivers/video/mxc/mxc_edid.c @@ -33,7 +33,6 @@ #include "../edid.h" #undef DEBUG /* define this for verbose EDID parsing output */ - #ifdef DEBUG #define DPRINTK(fmt, args...) printk(fmt, ## args) #else @@ -186,6 +185,19 @@ static void get_detailed_timing(unsigned char *block, } mode->flag = FB_MODE_IS_DETAILED; + if ((H_SIZE / 16) == (V_SIZE / 9)) + mode->vmode |= FB_VMODE_ASPECT_16_9; + else if ((H_SIZE / 4) == (V_SIZE / 3)) + mode->vmode |= FB_VMODE_ASPECT_4_3; + else if ((mode->xres / 16) == (mode->yres / 9)) + mode->vmode |= FB_VMODE_ASPECT_16_9; + else if ((mode->xres / 4) == (mode->yres / 3)) + mode->vmode |= FB_VMODE_ASPECT_4_3; + + if (mode->vmode & FB_VMODE_ASPECT_16_9) + DPRINTK("Aspect ratio: 16:9\n"); + if (mode->vmode & FB_VMODE_ASPECT_4_3) + DPRINTK("Aspect ratio: 4:3\n"); DPRINTK(" %d MHz ", PIXEL_CLOCK/1000000); DPRINTK("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET, H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING); -- cgit v1.2.3 From b80aa11ee88cc4a3a9764eb6312f011da15c566b Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Wed, 14 Dec 2011 14:02:16 +0800 Subject: ENGR00170168-1 ipuv3 fb: add non FB_VMODE_YWRAP support when use pan display, the case could be: 1. a small window wrap in a big frame buffer 2. a frame switch in a serial buffers the ipuv3 fb driver used to support case 1, and for case 2, if the fb format is interleaved, there is no problem, but for non-interleaved format (like I420), there will be a display bug. Signed-off-by: Jason Chen --- drivers/video/mxc/mxc_ipuv3_fb.c | 63 +++++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/drivers/video/mxc/mxc_ipuv3_fb.c b/drivers/video/mxc/mxc_ipuv3_fb.c index 70b7be1df0b..9ccafc2d95b 100644 --- a/drivers/video/mxc/mxc_ipuv3_fb.c +++ b/drivers/video/mxc/mxc_ipuv3_fb.c @@ -247,6 +247,7 @@ static int _setup_disp_channel2(struct fb_info *fbi) struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; int fb_stride; unsigned long base; + unsigned int fr_xoff, fr_yoff, fr_w, fr_h; switch (bpp_to_pixfmt(fbi)) { case IPU_PIX_FMT_YUV420P2: @@ -261,16 +262,28 @@ static int _setup_disp_channel2(struct fb_info *fbi) fb_stride = fbi->fix.line_length; } + base = fbi->fix.smem_start; + fr_xoff = fbi->var.xoffset; + fr_w = fbi->var.xres_virtual; + if (!(fbi->var.vmode & FB_VMODE_YWRAP)) { + dev_dbg(fbi->device, "Y wrap disabled\n"); + fr_yoff = fbi->var.yoffset % fbi->var.yres; + fr_h = fbi->var.yres; + base += fbi->fix.line_length * fbi->var.yres * + (fbi->var.yoffset / fbi->var.yres); + } else { + dev_dbg(fbi->device, "Y wrap enabled\n"); + fr_yoff = fbi->var.yoffset; + fr_h = fbi->var.yres_virtual; + } + base += fr_yoff * fb_stride + fr_xoff; + mxc_fbi->cur_ipu_buf = 2; sema_init(&mxc_fbi->flip_sem, 1); if (mxc_fbi->alpha_chan_en) { mxc_fbi->cur_ipu_alpha_buf = 1; sema_init(&mxc_fbi->alpha_flip_sem, 1); } - fbi->var.xoffset = 0; - - base = (fbi->var.yoffset * fb_stride + fbi->var.xoffset); - base += fbi->fix.smem_start; retval = ipu_init_channel_buffer(mxc_fbi->ipu, mxc_fbi->ipu_ch, IPU_INPUT_BUFFER, @@ -288,6 +301,17 @@ static int _setup_disp_channel2(struct fb_info *fbi) "ipu_init_channel_buffer error %d\n", retval); } + /* update u/v offset */ + ipu_update_channel_offset(mxc_fbi->ipu, mxc_fbi->ipu_ch, + IPU_INPUT_BUFFER, + bpp_to_pixfmt(fbi), + fr_w, + fr_h, + fr_w, + 0, 0, + fr_yoff, + fr_xoff); + if (mxc_fbi->alpha_chan_en) { retval = ipu_init_channel_buffer(mxc_fbi->ipu, mxc_fbi->ipu_ch, @@ -1224,6 +1248,7 @@ mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)info->par, *mxc_graphic_fbi = NULL; u_int y_bottom; + unsigned int fr_xoff, fr_yoff, fr_w, fr_h; unsigned long base, active_alpha_phy_addr = 0; bool loc_alpha_en = false; int fb_stride; @@ -1250,9 +1275,6 @@ mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) y_bottom = var->yoffset; - if (!(var->vmode & FB_VMODE_YWRAP)) - y_bottom += var->yres; - if (y_bottom > info->var.yres_virtual) return -EINVAL; @@ -1269,8 +1291,21 @@ mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) fb_stride = info->fix.line_length; } - base = (var->yoffset * fb_stride + var->xoffset); - base += info->fix.smem_start; + base = info->fix.smem_start; + fr_xoff = var->xoffset; + fr_w = info->var.xres_virtual; + if (!(var->vmode & FB_VMODE_YWRAP)) { + dev_dbg(info->device, "Y wrap disabled\n"); + fr_yoff = var->yoffset % info->var.yres; + fr_h = info->var.yres; + base += info->fix.line_length * info->var.yres * + (var->yoffset / info->var.yres); + } else { + dev_dbg(info->device, "Y wrap enabled\n"); + fr_yoff = var->yoffset; + fr_h = info->var.yres_virtual; + } + base += fr_yoff * fb_stride + fr_xoff; /* Check if DP local alpha is enabled and find the graphic fb */ if (mxc_fbi->ipu_ch == MEM_BG_SYNC || mxc_fbi->ipu_ch == MEM_FG_SYNC) { @@ -1326,12 +1361,12 @@ mxcfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) ipu_update_channel_offset(mxc_fbi->ipu, mxc_fbi->ipu_ch, IPU_INPUT_BUFFER, bpp_to_pixfmt(info), - info->var.xres_virtual, - info->var.yres_virtual, - info->var.xres_virtual, + fr_w, + fr_h, + fr_w, 0, 0, - var->yoffset, - var->xoffset); + fr_yoff, + fr_xoff); ipu_select_buffer(mxc_fbi->ipu, mxc_fbi->ipu_ch, IPU_INPUT_BUFFER, mxc_fbi->cur_ipu_buf); -- cgit v1.2.3 From 1ad2c03f1a95144f96e499e76a23188784a0718e Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Wed, 14 Dec 2011 14:09:08 +0800 Subject: ENGR00170168-2 mxc_vout: add non FB_VMODE_YWRAP support when use pan display, the case could be: 1. a small window wrap in a big frame buffer 2. a frame switch in a serial buffers the ipuv3 fb driver used to support case 1, and for case 2, if the fb format is interleaved, there is no problem, but for non-interleaved format (like I420), there will be a display bug. Signed-off-by: Jason Chen --- drivers/media/video/mxc/output/mxc_vout.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/video/mxc/output/mxc_vout.c b/drivers/media/video/mxc/output/mxc_vout.c index 8092e670114..319aea86caa 100644 --- a/drivers/media/video/mxc/output/mxc_vout.c +++ b/drivers/media/video/mxc/output/mxc_vout.c @@ -1328,10 +1328,12 @@ static int config_disp_output(struct mxc_vout_output *vout) else var.yres_virtual = var.yres; var.rotate = vout->task.output.rotate; + var.vmode |= FB_VMODE_YWRAP; } else { fb_num = FB_BUFS; var.xres_virtual = var.xres; var.yres_virtual = fb_num * var.yres; + var.vmode &= ~FB_VMODE_YWRAP; } var.bits_per_pixel = fmt_to_bpp(vout->task.output.format); var.nonstd = vout->task.output.format; -- cgit v1.2.3 From fa0148176bacf218ccb26d3b69f5a5452f85d4b6 Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Thu, 8 Dec 2011 13:43:42 -0600 Subject: ENGR00169872-1 rework hdmi initialization and hotplug sequence This commit intends to implement the flowchart and details documented in the HDMI Transmitter Controller User Guide section entitled "Programming Model". Some input is also from the Synopsys API code. The HDMI specification requires HDMI to set itself to VGA DVI mode before reading the EDID. So follow this sequence when HDMI is hotplugged: 1. Hdmi connector is plugged in, HDMI video gets an interrupt. 2. Clear out video mode list. Add only VGA DVI mode to list. 3. Request VGA DVI mode (call fb_set_var()) 4. HDMI video driver will get FB_EVENT_MODE_CHANGE callback and call mxc_hdmi_setup() to set up HDMI. 5. Read the edid and add video modes from edid. Select the video mode that is similar to the command line default. 6. Request VGA DVI mode (call fb_set_var()) 7. HDMI video driver will get FB_EVENT_MODE_CHANGE callback and do mxc_hdmi_setup(). Also included is a workaround for an overflow condition in the HDMI. The frame composer has an arithmetic unit that gets updated every time we write to one of the FC registers. But sometimes, depending on the relation between the tmds and sfr clocks, it may happen that this unit doesn't get updated, even though the registers are holding correct values. The workaround for this is, after completing the controller configuration, to rewrite one of the FC registers (i.e. FC_INVIDCONF) three or four times with the same value, and then follow it up by a SW reset to the TMDS clock domain (MC_SWRSTZ). We clear the overflow condition as described above every time we change video mode. Also an overflow interupt handler will clear the overflow condition if it happens again. This overflow condition is expected (and not a problem) when we are in DVI (non-HDMI) mode, so we do not worry about it in that case. Signed-off-by: Alan Tull --- arch/arm/plat-mxc/include/mach/mxc_edid.h | 1 + arch/arm/plat-mxc/include/mach/mxc_hdmi.h | 85 ++++++++++++++++++++----------- 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/arch/arm/plat-mxc/include/mach/mxc_edid.h b/arch/arm/plat-mxc/include/mach/mxc_edid.h index 59688f657a2..4cbbb78ad70 100644 --- a/arch/arm/plat-mxc/include/mach/mxc_edid.h +++ b/arch/arm/plat-mxc/include/mach/mxc_edid.h @@ -28,6 +28,7 @@ #define FB_VMODE_ASPECT_4_3 0x10 #define FB_VMODE_ASPECT_16_9 0x20 +#define FB_VMODE_ASPECT_MASK (FB_VMODE_ASPECT_4_3 | FB_VMODE_ASPECT_16_9) struct mxc_edid_cfg { bool cea_underscan; diff --git a/arch/arm/plat-mxc/include/mach/mxc_hdmi.h b/arch/arm/plat-mxc/include/mach/mxc_hdmi.h index a7f22de9b40..56729b22958 100644 --- a/arch/arm/plat-mxc/include/mach/mxc_hdmi.h +++ b/arch/arm/plat-mxc/include/mach/mxc_hdmi.h @@ -377,6 +377,7 @@ #define HDMI_AUD_CTS2 0x3204 #define HDMI_AUD_CTS3 0x3205 #define HDMI_AUD_INPUTCLKFS 0x3206 +#define HDMI_AUD_SPDIFINT 0x3302 #define HDMI_AUD_CONF0_HBR 0x3400 #define HDMI_AUD_HBR_STATUS 0x3401 #define HDMI_AUD_HBR_INT 0x3402 @@ -564,6 +565,16 @@ * Register field definitions */ enum { +/* IH_FC_INT2 field values */ + HDMI_IH_FC_INT2_OVERFLOW_MASK = 0x03, + HDMI_IH_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02, + HDMI_IH_FC_INT2_HIGH_PRIORITY_OVERFLOW = 0x01, + +/* IH_FC_STAT2 field values */ + HDMI_IH_FC_STAT2_OVERFLOW_MASK = 0x03, + HDMI_IH_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02, + HDMI_IH_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01, + /* IH_PHY_STAT0 field values */ HDMI_IH_PHY_STAT0_RX_SENSE3 = 0x20, HDMI_IH_PHY_STAT0_RX_SENSE2 = 0x10, @@ -584,6 +595,11 @@ enum { HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = 0x02, HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = 0x01, +/* IH_MUTE_FC_STAT2 field values */ + HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK = 0x03, + HDMI_IH_MUTE_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02, + HDMI_IH_MUTE_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01, + /* IH_MUTE_AHBDMAAUD_STAT0 field values */ HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = 0x20, HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = 0x10, @@ -750,6 +766,21 @@ enum { HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT1 = 0x1, HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT0 = 0x0, +/* FC_STAT2 field values */ + HDMI_FC_STAT2_OVERFLOW_MASK = 0x03, + HDMI_FC_STAT2_LOW_PRIORITY_OVERFLOW = 0x02, + HDMI_FC_STAT2_HIGH_PRIORITY_OVERFLOW = 0x01, + +/* FC_INT2 field values */ + HDMI_FC_INT2_OVERFLOW_MASK = 0x03, + HDMI_FC_INT2_LOW_PRIORITY_OVERFLOW = 0x02, + HDMI_FC_INT2_HIGH_PRIORITY_OVERFLOW = 0x01, + +/* FC_MASK2 field values */ + HDMI_FC_MASK2_OVERFLOW_MASK = 0x03, + HDMI_FC_MASK2_LOW_PRIORITY_OVERFLOW = 0x02, + HDMI_FC_MASK2_HIGH_PRIORITY_OVERFLOW = 0x01, + /* FC_PRCONF field values */ HDMI_FC_PRCONF_INCOMING_PR_FACTOR_MASK = 0xF0, HDMI_FC_PRCONF_INCOMING_PR_FACTOR_OFFSET = 4, @@ -822,24 +853,19 @@ enum { HDMI_FC_DBGFORCE_FORCEVIDEO = 0x1, /* PHY_CONF0 field values */ - HDMI_PHY_CONF0_PDZ = 0x80, HDMI_PHY_CONF0_PDZ_MASK = 0x80, HDMI_PHY_CONF0_PDZ_OFFSET = 7, - HDMI_PHY_CONF0_ENTMDS = 0x40, HDMI_PHY_CONF0_ENTMDS_MASK = 0x40, HDMI_PHY_CONF0_ENTMDS_OFFSET = 6, HDMI_PHY_CONF0_SPARECTRL = 0x20, HDMI_PHY_CONF0_GEN2_PDDQ_MASK = 0x10, - HDMI_PHY_CONF0_GEN2_PDDQ_ENABLE = 0x10, - HDMI_PHY_CONF0_GEN2_PDDQ_DISABLE = 0x00, + HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET = 4, HDMI_PHY_CONF0_GEN2_TXPWRON_MASK = 0x8, - HDMI_PHY_CONF0_GEN2_TXPWRON_POWER_ON = 0x8, - HDMI_PHY_CONF0_GEN2_TXPWRON_POWER_OFF = 0x0, - HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE = 0x4, - HDMI_PHY_CONF0_SELDATAENPOL = 0x2, + HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET = 3, + HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_MASK = 0x4, + HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_OFFSET = 2, HDMI_PHY_CONF0_SELDATAENPOL_MASK = 0x2, HDMI_PHY_CONF0_SELDATAENPOL_OFFSET = 1, - HDMI_PHY_CONF0_SELDIPIF = 0x1, HDMI_PHY_CONF0_SELDIPIF_MASK = 0x1, HDMI_PHY_CONF0_SELDIPIF_OFFSET = 0, @@ -867,6 +893,16 @@ enum { HDMI_PHY_I2CM_OPERATION_ADDR_WRITE = 0x10, HDMI_PHY_I2CM_OPERATION_ADDR_READ = 0x1, +/* HDMI_PHY_I2CM_INT_ADDR */ + HDMI_PHY_I2CM_INT_ADDR_DONE_POL = 0x08, + HDMI_PHY_I2CM_INT_ADDR_DONE_MASK = 0x04, + +/* HDMI_PHY_I2CM_CTLINT_ADDR */ + HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL = 0x80, + HDMI_PHY_I2CM_CTLINT_ADDR_NAC_MASK = 0x40, + HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL = 0x08, + HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_MASK = 0x04, + /* AUD_CTS3 field values */ HDMI_AUD_CTS3_N_SHIFT_OFFSET = 5, HDMI_AUD_CTS3_N_SHIFT_MASK = 0xe0, @@ -916,27 +952,16 @@ enum { HDMI_AHB_DMA_BUFFSTAT_EMPTY = 0x01, /* MC_CLKDIS field values */ - HDMI_MC_CLKDIS_HDCPCLK_DISABLE_MASK = 0x40, - HDMI_MC_CLKDIS_HDCPCLK_DISABLE_DISABLE = 0x40, - HDMI_MC_CLKDIS_HDCPCLK_DISABLE_ENABLE = 0x00, - HDMI_MC_CLKDIS_CECCLK_DISABLE_MASK = 0x20, - HDMI_MC_CLKDIS_CECCLK_DISABLE_DISABLE = 0x20, - HDMI_MC_CLKDIS_CECCLK_DISABLE_ENABLE = 0x00, - HDMI_MC_CLKDIS_CSCCLK_DISABLE_MASK = 0x10, - HDMI_MC_CLKDIS_CSCCLK_DISABLE_DISABLE = 0x10, - HDMI_MC_CLKDIS_CSCCLK_DISABLE_ENABLE = 0x00, - HDMI_MC_CLKDIS_AUDCLK_DISABLE_MASK = 0x8, - HDMI_MC_CLKDIS_AUDCLK_DISABLE_DISABLE = 0x8, - HDMI_MC_CLKDIS_AUDCLK_DISABLE_ENABLE = 0x0, - HDMI_MC_CLKDIS_PREPCLK_DISABLE_MASK = 0x4, - HDMI_MC_CLKDIS_PREPCLK_DISABLE_DISABLE = 0x4, - HDMI_MC_CLKDIS_PREPCLK_DISABLE_ENABLE = 0x0, - HDMI_MC_CLKDIS_TMDSCLK_DISABLE_MASK = 0x2, - HDMI_MC_CLKDIS_TMDSCLK_DISABLE_DISABLE = 0x2, - HDMI_MC_CLKDIS_TMDSCLK_DISABLE_ENABLE = 0x0, - HDMI_MC_CLKDIS_PIXELCLK_DISABLE_MASK = 0x1, - HDMI_MC_CLKDIS_PIXELCLK_DISABLE_DISABLE = 0x1, - HDMI_MC_CLKDIS_PIXELCLK_DISABLE_ENABLE = 0x0, + HDMI_MC_CLKDIS_HDCPCLK_DISABLE = 0x40, + HDMI_MC_CLKDIS_CECCLK_DISABLE = 0x20, + HDMI_MC_CLKDIS_CSCCLK_DISABLE = 0x10, + HDMI_MC_CLKDIS_AUDCLK_DISABLE = 0x8, + HDMI_MC_CLKDIS_PREPCLK_DISABLE = 0x4, + HDMI_MC_CLKDIS_TMDSCLK_DISABLE = 0x2, + HDMI_MC_CLKDIS_PIXELCLK_DISABLE = 0x1, + +/* MC_SWRSTZ field values */ + HDMI_MC_SWRSTZ_TMDSSWRST_REQ = 0x02, /* MC_FLOWCTRL field values */ HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_MASK = 0x1, -- cgit v1.2.3 From 7420896deb69b8801f2acf48f4b3605ef55d21c4 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Tue, 27 Dec 2011 16:16:10 +0800 Subject: ENGR00169872-2 rework hdmi initialization and hotplug sequence This commit intends to implement the flowchart and details documented in the HDMI Transmitter Controller User Guide section entitled "Programming Model". Some input is also from the Synopsys API code. The HDMI specification requires HDMI to set itself to VGA DVI mode before reading the EDID. So follow this sequence when HDMI is hotplugged: 1. Hdmi connector is plugged in, HDMI video gets an interrupt. 2. Clear out video mode list. Add only VGA DVI mode to list. 3. Request VGA DVI mode (call fb_set_var()) 4. HDMI video driver will get FB_EVENT_MODE_CHANGE callback and call mxc_hdmi_setup() to set up HDMI. 5. Read the edid and add video modes from edid. Select the video mode that is similar to the command line default. 6. Request VGA DVI mode (call fb_set_var()) 7. HDMI video driver will get FB_EVENT_MODE_CHANGE callback and do mxc_hdmi_setup(). Also included is a workaround for an overflow condition in the HDMI. The frame composer has an arithmetic unit that gets updated every time we write to one of the FC registers. But sometimes, depending on the relation between the tmds and sfr clocks, it may happen that this unit doesn't get updated, even though the registers are holding correct values. The workaround for this is, after completing the controller configuration, to rewrite one of the FC registers (i.e. FC_INVIDCONF) three or four times with the same value, and then follow it up by a SW reset to the TMDS clock domain (MC_SWRSTZ). We clear the overflow condition as described above every time we change video mode. Also an overflow interupt handler will clear the overflow condition if it happens again. This overflow condition is expected (and not a problem) when we are in DVI (non-HDMI) mode, so we do not worry about it in that case. Signed-off-by: Alan Tull --- drivers/mfd/mxc-hdmi-core.c | 109 +++- drivers/video/mxc/mxc_edid.c | 19 +- drivers/video/mxc_hdmi.c | 1025 ++++++++++++++++++++++++------------- include/linux/mfd/mxc-hdmi-core.h | 7 +- 4 files changed, 770 insertions(+), 390 deletions(-) diff --git a/drivers/mfd/mxc-hdmi-core.c b/drivers/mfd/mxc-hdmi-core.c index a211cd0f087..24afc502cea 100644 --- a/drivers/mfd/mxc-hdmi-core.c +++ b/drivers/mfd/mxc-hdmi-core.c @@ -74,10 +74,40 @@ u8 hdmi_readb(unsigned int reg) return value; } +#ifdef DEBUG +static bool overflow_lo; +static bool overflow_hi; + +bool hdmi_check_overflow(void) +{ + u8 val, lo, hi; + + val = hdmi_readb(HDMI_IH_FC_STAT2); + lo = (val & HDMI_IH_FC_STAT2_LOW_PRIORITY_OVERFLOW) != 0; + hi = (val & HDMI_IH_FC_STAT2_HIGH_PRIORITY_OVERFLOW) != 0; + + if ((lo != overflow_lo) || (hi != overflow_hi)) { + pr_debug("%s LowPriority=%d HighPriority=%d <=======================\n", + __func__, lo, hi); + overflow_lo = lo; + overflow_hi = hi; + return true; + } + return false; +} +#else +bool hdmi_check_overflow(void) +{ + return false; +} +#endif + void hdmi_writeb(u8 value, unsigned int reg) { + hdmi_check_overflow(); pr_debug("hdmi wr: 0x%04x = 0x%02x\n", reg, value); __raw_writeb(value, hdmi_base + reg); + hdmi_check_overflow(); } void hdmi_mask_writeb(u8 data, unsigned int reg, u8 shift, u8 mask) @@ -172,6 +202,23 @@ static void initialize_hdmi_ih_mutes(void) hdmi_writeb(ih_mute, HDMI_IH_MUTE); + /* by default mask all interrupts */ + hdmi_writeb(0xff, HDMI_VP_MASK); + hdmi_writeb(0xff, HDMI_FC_MASK0); + hdmi_writeb(0xff, HDMI_FC_MASK1); + hdmi_writeb(0xff, HDMI_FC_MASK2); + hdmi_writeb(0xff, HDMI_PHY_MASK0); + hdmi_writeb(0xff, HDMI_PHY_I2CM_INT_ADDR); + hdmi_writeb(0xff, HDMI_PHY_I2CM_CTLINT_ADDR); + hdmi_writeb(0xff, HDMI_AUD_INT); + hdmi_writeb(0xff, HDMI_AUD_SPDIFINT); + hdmi_writeb(0xff, HDMI_AUD_HBR_MASK); + hdmi_writeb(0xff, HDMI_GP_MASK); + hdmi_writeb(0xff, HDMI_A_APIINTMSK); + hdmi_writeb(0xff, HDMI_CEC_MASK); + hdmi_writeb(0xff, HDMI_I2CM_INT); + hdmi_writeb(0xff, HDMI_I2CM_CTLINT); + /* Disable interrupts in the IH_MUTE_* registers */ hdmi_writeb(0xff, HDMI_IH_MUTE_FC_STAT0); hdmi_writeb(0xff, HDMI_IH_MUTE_FC_STAT1); @@ -192,13 +239,27 @@ static void initialize_hdmi_ih_mutes(void) static void hdmi_set_clock_regenerator_n(unsigned int value) { + u8 val; + hdmi_writeb(value & 0xff, HDMI_AUD_N1); hdmi_writeb((value >> 8) & 0xff, HDMI_AUD_N2); - hdmi_writeb((value >> 16) & 0xff, HDMI_AUD_N3); + hdmi_writeb((value >> 16) & 0x0f, HDMI_AUD_N3); + + /* nshift factor = 0 */ + val = hdmi_readb(HDMI_AUD_CTS3); + val &= ~HDMI_AUD_CTS3_N_SHIFT_MASK; + hdmi_writeb(val, HDMI_AUD_CTS3); } static void hdmi_set_clock_regenerator_cts(unsigned int cts) { + u8 val; + + /* Must be set/cleared first */ + val = hdmi_readb(HDMI_AUD_CTS3); + val &= ~HDMI_AUD_CTS3_CTS_MANUAL; + hdmi_writeb(val, HDMI_AUD_CTS3); + hdmi_writeb(cts & 0xff, HDMI_AUD_CTS1); hdmi_writeb((cts >> 8) & 0xff, HDMI_AUD_CTS2); hdmi_writeb(((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) | @@ -341,6 +402,7 @@ static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk, static void hdmi_get_pixel_clk(void) { struct ipu_soc *ipu; + unsigned long rate; if (pixel_clk == NULL) { ipu = ipu_get_soc(mxc_hdmi_ipu_id); @@ -351,28 +413,20 @@ static void hdmi_get_pixel_clk(void) } } - pixel_clk_rate = clk_get_rate(pixel_clk); + rate = clk_get_rate(pixel_clk); + if (rate != 0) + pixel_clk_rate = rate; } -/* - * input: audio sample rate and video pixel rate - * output: N and cts written to the HDMI regs. - */ -void hdmi_set_clk_regenerator(void) +static void hdmi_set_clk_regenerator(void) { unsigned int clk_n, clk_cts; - /* Get pixel clock from ipu */ - hdmi_get_pixel_clk(); - - pr_debug("%s: sample rate is %d ; ratio is %d ; pixel clk is %d\n", - __func__, sample_rate, hdmi_ratio, (int)pixel_clk_rate); - clk_n = hdmi_compute_n(sample_rate, pixel_clk_rate, hdmi_ratio); clk_cts = hdmi_compute_cts(sample_rate, pixel_clk_rate, hdmi_ratio); if (clk_cts == 0) { - pr_err("%s: pixel clock not supported: %d\n", + pr_debug("%s: pixel clock not supported: %d\n", __func__, (int)pixel_clk_rate); return; } @@ -380,6 +434,10 @@ void hdmi_set_clk_regenerator(void) clk_enable(isfr_clk); clk_enable(iahb_clk); + pr_debug("%s: samplerate=%d ratio=%d pixelclk=%d N=%d cts=%d\n", + __func__, sample_rate, hdmi_ratio, (int)pixel_clk_rate, + clk_n, clk_cts); + hdmi_set_clock_regenerator_n(clk_n); hdmi_set_clock_regenerator_cts(clk_cts); @@ -387,6 +445,23 @@ void hdmi_set_clk_regenerator(void) clk_disable(isfr_clk); } +/* Need to run this before phy is enabled the first time to prevent + * overflow condition in HDMI_IH_FC_STAT2 */ +void hdmi_init_clk_regenerator(void) +{ + if (pixel_clk_rate == 0) { + pixel_clk_rate = 74250000; + hdmi_set_clk_regenerator(); + } +} + +void hdmi_clk_regenerator_update_pixel_clock(void) +{ + /* Get pixel clock from ipu */ + hdmi_get_pixel_clk(); + hdmi_set_clk_regenerator(); +} + void hdmi_set_sample_rate(unsigned int rate) { sample_rate = rate; @@ -428,6 +503,10 @@ static int mxc_hdmi_core_probe(struct platform_device *pdev) int ret = 0; struct platform_device_info pdevinfo_hdmi_v; +#ifdef DEBUG + overflow_lo = false; + overflow_hi = false; +#endif res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENOENT; @@ -441,7 +520,7 @@ static int mxc_hdmi_core_probe(struct platform_device *pdev) pixel_clk = NULL; sample_rate = 48000; - pixel_clk_rate = 74250000; + pixel_clk_rate = 0; hdmi_ratio = 100; irq_enable_cnt = 0; diff --git a/drivers/video/mxc/mxc_edid.c b/drivers/video/mxc/mxc_edid.c index 5c407b9bc92..de4ba8ec8bf 100644 --- a/drivers/video/mxc/mxc_edid.c +++ b/drivers/video/mxc/mxc_edid.c @@ -139,9 +139,17 @@ const struct fb_videomode mxc_cea_mode[64] = { * 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) +int mxc_edid_fb_mode_is_equal(bool use_aspect, + const struct fb_videomode *mode1, + const struct fb_videomode *mode2) { + u32 mask; + + if (use_aspect) + mask = ~0; + else + mask = ~FB_VMODE_ASPECT_MASK; + return (mode1->xres == mode2->xres && mode1->yres == mode2->yres && mode1->hsync_len == mode2->hsync_len && @@ -151,7 +159,7 @@ int mxc_edid_fb_mode_is_equal(const struct fb_videomode *mode1, mode1->upper_margin == mode2->upper_margin && mode1->lower_margin == mode2->lower_margin && mode1->sync == mode2->sync && - mode1->vmode == mode2->vmode); + (mode1->vmode & mask) == (mode2->vmode & mask)); } static void get_detailed_timing(unsigned char *block, @@ -451,7 +459,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 (mxc_edid_fb_mode_is_equal(&m, &mxc_cea_mode[i])) + if (mxc_edid_fb_mode_is_equal(false, &m, &mxc_cea_mode[i])) break; } @@ -466,9 +474,10 @@ EXPORT_SYMBOL(mxc_edid_var_to_vic); int mxc_edid_mode_to_vic(const struct fb_videomode *mode) { int i; + bool use_aspect = (mode->vmode & FB_VMODE_ASPECT_MASK); for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) { - if (mxc_edid_fb_mode_is_equal(mode, &mxc_cea_mode[i])) + if (mxc_edid_fb_mode_is_equal(use_aspect, mode, &mxc_cea_mode[i])) break; } diff --git a/drivers/video/mxc_hdmi.c b/drivers/video/mxc_hdmi.c index 0977210b477..c9b1a835a97 100644 --- a/drivers/video/mxc_hdmi.c +++ b/drivers/video/mxc_hdmi.c @@ -62,8 +62,11 @@ #define DISPDRV_HDMI "hdmi" #define HDMI_EDID_LEN 512 -#define TRUE 1 -#define FALSE 0 +/* status codes for reading edid */ +#define HDMI_EDID_SUCCESS 0 +#define HDMI_EDID_FAIL -1 +#define HDMI_EDID_SAME -2 +#define HDMI_EDID_NO_MODES -3 #define NUM_CEA_VIDEO_MODES 64 #define DEFAULT_VIDEO_MODE 16 /* 1080P */ @@ -74,6 +77,25 @@ #define YCBCR422_8BITS 3 #define XVYCC444 4 +/* + * We follow a flowchart which is in the "Synopsys DesignWare Courses + * HDMI Transmitter Controller User Guide, 1.30a", section 3.1 + * (dwc_hdmi_tx_user.pdf) + * + * Below are notes that say "HDMI Initialization Step X" + * These correspond to the flowchart. + */ + +/* + * We are required to configure VGA mode before reading edid + * in HDMI Initialization Step B + */ +static const struct fb_videomode vga_mode = { + /* 640x480 @ 60 Hz, 31.5 kHz hsync */ + NULL, 60, 640, 480, 39721, 48, 16, 33, 10, 96, 2, 0, + FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0, +}; + enum hdmi_datamap { RGB444_8B = 0x01, RGB444_10B = 0x03, @@ -94,11 +116,12 @@ enum hdmi_colorimetry { }; struct hdmi_vmode { - unsigned int mHdmiDviSel; - unsigned int mHSyncPolarity; - unsigned int mVSyncPolarity; - unsigned int mInterlaced; - unsigned int mDataEnablePolarity; + bool mDVI; + bool mHSyncPolarity; + bool mVSyncPolarity; + bool mInterlaced; + bool mDataEnablePolarity; + unsigned int mPixelClock; unsigned int mPixelRepetitionInput; unsigned int mPixelRepetitionOutput; @@ -121,7 +144,7 @@ struct mxc_hdmi { struct fb_info *fbi; struct clk *hdmi_isfr_clk; struct clk *hdmi_iahb_clk; - struct delayed_work det_work; + struct delayed_work hotplug_work; struct notifier_block nb; struct hdmi_data_info hdmi_data; @@ -129,17 +152,36 @@ struct mxc_hdmi { struct mxc_edid_cfg edid_cfg; u8 edid[HDMI_EDID_LEN]; bool fb_reg; - bool need_mode_change; bool cable_plugin; + bool dft_mode_set; + char *dft_mode_str; + int default_bpp; u8 latest_intr_stat; bool irq_enabled; spinlock_t irq_lock; + bool phy_enabled; + struct fb_videomode previous_mode; + struct fb_videomode previous_non_vga_mode; + bool requesting_vga_for_initialization; }; struct i2c_client *hdmi_i2c; extern const struct fb_videomode mxc_cea_mode[64]; +#ifdef DEBUG +static void dump_fb_videomode(struct fb_videomode *m) +{ + pr_debug("fb_videomode = %d %d %d %d %d %d %d %d %d %d %d %d %d\n", + m->refresh, m->xres, m->yres, m->pixclock, m->left_margin, + m->right_margin, m->upper_margin, m->lower_margin, + m->hsync_len, m->vsync_len, m->sync, m->vmode, m->flag); +} +#else +static void dump_fb_videomode(struct fb_videomode *m) +{} +#endif + /*! * this submodule is responsible for the video data synchronization. * for example, for RGB 4:4:4 input, the data map is defined as @@ -147,7 +189,7 @@ extern const struct fb_videomode mxc_cea_mode[64]; * pin{31~24} <==> G[7:0] * pin{15~8} <==> B[7:0] */ -void hdmi_video_sample(struct mxc_hdmi *hdmi) +static void hdmi_video_sample(struct mxc_hdmi *hdmi) { int color_format = 0; u8 val; @@ -206,28 +248,27 @@ void hdmi_video_sample(struct mxc_hdmi *hdmi) static int isColorSpaceConversion(struct mxc_hdmi *hdmi) { return (hdmi->hdmi_data.enc_in_format != - hdmi->hdmi_data.enc_out_format) ? TRUE : FALSE; + hdmi->hdmi_data.enc_out_format); } static int isColorSpaceDecimation(struct mxc_hdmi *hdmi) { return ((hdmi->hdmi_data.enc_out_format == YCBCR422_8BITS) && (hdmi->hdmi_data.enc_in_format == RGB || - hdmi->hdmi_data.enc_in_format == XVYCC444)) ? TRUE : FALSE; + hdmi->hdmi_data.enc_in_format == XVYCC444)); } static int isColorSpaceInterpolation(struct mxc_hdmi *hdmi) { return ((hdmi->hdmi_data.enc_in_format == YCBCR422_8BITS) && - (hdmi->hdmi_data.enc_out_format == RGB - || hdmi->hdmi_data.enc_out_format == XVYCC444)) ? - TRUE : FALSE; + (hdmi->hdmi_data.enc_out_format == RGB + || hdmi->hdmi_data.enc_out_format == XVYCC444)); } /*! * update the color space conversion coefficients. */ -void update_csc_coeffs(struct mxc_hdmi *hdmi) +static void update_csc_coeffs(struct mxc_hdmi *hdmi) { unsigned short csc_coeff[3][4]; unsigned int csc_scale = 1; @@ -391,7 +432,7 @@ void update_csc_coeffs(struct mxc_hdmi *hdmi) hdmi_writeb(val, HDMI_CSC_SCALE); } -void hdmi_video_csc(struct mxc_hdmi *hdmi) +static void hdmi_video_csc(struct mxc_hdmi *hdmi) { int color_depth = 0; int interpolation = HDMI_CSC_CFG_INTMODE_DISABLE; @@ -430,7 +471,7 @@ void hdmi_video_csc(struct mxc_hdmi *hdmi) * for example, if input is YCC422 mode or repeater is used, * data should be repacked this module can be bypassed. */ -void hdmi_video_packetize(struct mxc_hdmi *hdmi) +static void hdmi_video_packetize(struct mxc_hdmi *hdmi) { unsigned int color_depth = 0; unsigned int remap_size = HDMI_VP_REMAP_YCC422_16bit; @@ -548,11 +589,15 @@ void hdmi_video_packetize(struct mxc_hdmi *hdmi) hdmi_writeb(val, HDMI_VP_CONF); } -void hdmi_video_force_output(struct mxc_hdmi *hdmi, unsigned char force) +#if 0 +/* Force a fixed color screen */ +static void hdmi_video_force_output(struct mxc_hdmi *hdmi, unsigned char force) { u8 val; - if (force == TRUE) { + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + + if (force) { hdmi_writeb(0x00, HDMI_FC_DBGTMDS2); /* R */ hdmi_writeb(0x00, HDMI_FC_DBGTMDS1); /* G */ hdmi_writeb(0xFF, HDMI_FC_DBGTMDS0); /* B */ @@ -568,6 +613,7 @@ void hdmi_video_force_output(struct mxc_hdmi *hdmi, unsigned char force) hdmi_writeb(0x00, HDMI_FC_DBGTMDS0); /* B */ } } +#endif static inline void hdmi_phy_test_clear(struct mxc_hdmi *hdmi, unsigned char bit) @@ -611,21 +657,21 @@ static inline void hdmi_phy_test_dout(struct mxc_hdmi *hdmi, hdmi_writeb(bit, HDMI_PHY_TST2); } -int hdmi_phy_wait_i2c_done(struct mxc_hdmi *hdmi, int msec) +static bool hdmi_phy_wait_i2c_done(struct mxc_hdmi *hdmi, int msec) { unsigned char val = 0; val = hdmi_readb(HDMI_IH_I2CMPHY_STAT0) & 0x3; while (val == 0) { udelay(1000); if (msec-- == 0) - return FALSE; + return false; val = hdmi_readb(HDMI_IH_I2CMPHY_STAT0) & 0x3; } - return TRUE; + return true; } -int hdmi_phy_i2c_write(struct mxc_hdmi *hdmi, unsigned short data, - unsigned char addr) +static void hdmi_phy_i2c_write(struct mxc_hdmi *hdmi, unsigned short data, + unsigned char addr) { hdmi_writeb(0xFF, HDMI_IH_I2CMPHY_STAT0); hdmi_writeb(addr, HDMI_PHY_I2CM_ADDRESS_ADDR); @@ -636,10 +682,11 @@ int hdmi_phy_i2c_write(struct mxc_hdmi *hdmi, unsigned short data, hdmi_writeb(HDMI_PHY_I2CM_OPERATION_ADDR_WRITE, HDMI_PHY_I2CM_OPERATION_ADDR); hdmi_phy_wait_i2c_done(hdmi, 1000); - return TRUE; } -unsigned short hdmi_phy_i2c_read(struct mxc_hdmi *hdmi, unsigned char addr) +#if 0 +static unsigned short hdmi_phy_i2c_read(struct mxc_hdmi *hdmi, + unsigned char addr) { unsigned short data; unsigned char msb = 0, lsb = 0; @@ -654,31 +701,84 @@ unsigned short hdmi_phy_i2c_read(struct mxc_hdmi *hdmi, unsigned char addr) return data; } -int hdmi_phy_i2c_write_verify(struct mxc_hdmi *hdmi, unsigned short data, - unsigned char addr) +static int hdmi_phy_i2c_write_verify(struct mxc_hdmi *hdmi, unsigned short data, + unsigned char addr) { unsigned short val = 0; hdmi_phy_i2c_write(hdmi, data, addr); val = hdmi_phy_i2c_read(hdmi, addr); - if (val != data) - return FALSE; - return TRUE; + return (val == data); +} +#endif + +/* "Power-down enable (active low)" + * That mean that power up == 1! */ +static void mxc_hdmi_phy_enable_power(u8 enable) +{ + hdmi_mask_writeb(enable, HDMI_PHY_CONF0, + HDMI_PHY_CONF0_PDZ_OFFSET, + HDMI_PHY_CONF0_PDZ_MASK); +} + +static void mxc_hdmi_phy_enable_tmds(u8 enable) +{ + hdmi_mask_writeb(enable, HDMI_PHY_CONF0, + HDMI_PHY_CONF0_ENTMDS_OFFSET, + HDMI_PHY_CONF0_ENTMDS_MASK); } -int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, - unsigned char cRes, int cscOn, int audioOn, - int cecOn, int hdcpOn) +static void mxc_hdmi_phy_gen2_pddq(u8 enable) +{ + hdmi_mask_writeb(enable, HDMI_PHY_CONF0, + HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET, + HDMI_PHY_CONF0_GEN2_PDDQ_MASK); +} + +static void mxc_hdmi_phy_gen2_txpwron(u8 enable) +{ + hdmi_mask_writeb(enable, HDMI_PHY_CONF0, + HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET, + HDMI_PHY_CONF0_GEN2_TXPWRON_MASK); +} + +#if 0 +static void mxc_hdmi_phy_gen2_enhpdrxsense(u8 enable) +{ + hdmi_mask_writeb(enable, HDMI_PHY_CONF0, + HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_OFFSET, + HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE_MASK); +} +#endif + +static void mxc_hdmi_phy_sel_data_en_pol(u8 enable) +{ + hdmi_mask_writeb(enable, HDMI_PHY_CONF0, + HDMI_PHY_CONF0_SELDATAENPOL_OFFSET, + HDMI_PHY_CONF0_SELDATAENPOL_MASK); +} + +static void mxc_hdmi_phy_sel_interface_control(u8 enable) +{ + hdmi_mask_writeb(enable, HDMI_PHY_CONF0, + HDMI_PHY_CONF0_SELDIPIF_OFFSET, + HDMI_PHY_CONF0_SELDIPIF_MASK); +} + +static int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, + unsigned char cRes, int cscOn) { u8 val; + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + /* color resolution 0 is 8 bit colour depth */ if (cRes == 0) cRes = 8; if (pRep != 0) - return FALSE; + return false; else if (cRes != 8 && cRes != 12) - return FALSE; + return false; if (cscOn) val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_IN_PATH; @@ -687,39 +787,17 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, hdmi_writeb(val, HDMI_MC_FLOWCTRL); - /* clock gate == 0 => turn on modules */ - val = hdcpOn ? HDMI_MC_CLKDIS_HDCPCLK_DISABLE_ENABLE : - HDMI_MC_CLKDIS_HDCPCLK_DISABLE_DISABLE; - val |= HDMI_MC_CLKDIS_PIXELCLK_DISABLE_ENABLE; - val |= HDMI_MC_CLKDIS_TMDSCLK_DISABLE_ENABLE; - val |= (pRep > 0) ? HDMI_MC_CLKDIS_PREPCLK_DISABLE_ENABLE : - HDMI_MC_CLKDIS_PREPCLK_DISABLE_DISABLE; - val |= cecOn ? HDMI_MC_CLKDIS_CECCLK_DISABLE_ENABLE : - HDMI_MC_CLKDIS_CECCLK_DISABLE_DISABLE; - val |= cscOn ? HDMI_MC_CLKDIS_CSCCLK_DISABLE_ENABLE : - HDMI_MC_CLKDIS_CSCCLK_DISABLE_DISABLE; - val |= audioOn ? HDMI_MC_CLKDIS_AUDCLK_DISABLE_ENABLE : - HDMI_MC_CLKDIS_AUDCLK_DISABLE_DISABLE; - hdmi_writeb(val, HDMI_MC_CLKDIS); - /* gen2 tx power off */ - val = hdmi_readb(HDMI_PHY_CONF0); - val &= ~HDMI_PHY_CONF0_GEN2_TXPWRON_MASK; - val |= HDMI_PHY_CONF0_GEN2_TXPWRON_POWER_OFF; - hdmi_writeb(val, HDMI_PHY_CONF0); + mxc_hdmi_phy_gen2_txpwron(0); /* gen2 pddq */ - val = hdmi_readb(HDMI_PHY_CONF0); - val &= ~HDMI_PHY_CONF0_GEN2_PDDQ_MASK; - val |= HDMI_PHY_CONF0_GEN2_PDDQ_ENABLE; - hdmi_writeb(val, HDMI_PHY_CONF0); + mxc_hdmi_phy_gen2_pddq(1); /* PHY reset */ hdmi_writeb(HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ); hdmi_writeb(HDMI_MC_PHYRSTZ_ASSERT, HDMI_MC_PHYRSTZ); - hdmi_writeb(HDMI_MC_HEACPHY_RST_ASSERT, - HDMI_MC_HEACPHY_RST); + hdmi_writeb(HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST); hdmi_phy_test_clear(hdmi, 1); hdmi_writeb(HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2, @@ -729,7 +807,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, if (hdmi->hdmi_data.video_mode.mPixelClock < 0) { dev_dbg(&hdmi->pdev->dev, "Pixel clock (%d) must be positive\n", hdmi->hdmi_data.video_mode.mPixelClock); - return FALSE; + return false; } if (hdmi->hdmi_data.video_mode.mPixelClock <= 45250000) { @@ -748,7 +826,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, hdmi_phy_i2c_write(hdmi, 0x0000, 0x15); break; default: - return FALSE; + return false; } } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 92500000) { switch (cRes) { @@ -764,7 +842,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, hdmi_phy_i2c_write(hdmi, 0x4142, 0x06); hdmi_phy_i2c_write(hdmi, 0x0005, 0x15); default: - return FALSE; + return false; } } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 148500000) { switch (cRes) { @@ -780,7 +858,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, hdmi_phy_i2c_write(hdmi, 0x40a2, 0x06); hdmi_phy_i2c_write(hdmi, 0x000a, 0x15); default: - return FALSE; + return false; } } else { switch (cRes) { @@ -796,7 +874,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, hdmi_phy_i2c_write(hdmi, 0x4002, 0x06); hdmi_phy_i2c_write(hdmi, 0x000f, 0x15); default: - return FALSE; + return false; } } @@ -812,7 +890,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); break; default: - return FALSE; + return false; } } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 58400000) { switch (cRes) { @@ -826,7 +904,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); break; default: - return FALSE; + return false; } } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 72000000) { switch (cRes) { @@ -840,7 +918,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); break; default: - return FALSE; + return false; } } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 74250000) { switch (cRes) { @@ -854,7 +932,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); break; default: - return FALSE; + return false; } } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 118800000) { switch (cRes) { @@ -868,7 +946,7 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, hdmi_phy_i2c_write(hdmi, 0x06dc, 0x10); break; default: - return FALSE; + return false; } } else if (hdmi->hdmi_data.video_mode.mPixelClock <= 216000000) { switch (cRes) { @@ -882,13 +960,13 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, hdmi_phy_i2c_write(hdmi, 0x091c, 0x10); break; default: - return FALSE; + return false; } } else { dev_err(&hdmi->pdev->dev, "Pixel clock %d - unsupported by HDMI\n", hdmi->hdmi_data.video_mode.mPixelClock); - return FALSE; + return false; } hdmi_phy_i2c_write(hdmi, 0x0000, 0x13); /* PLLPHBYCTRL */ @@ -907,50 +985,50 @@ int hdmi_phy_configure(struct mxc_hdmi *hdmi, unsigned char pRep, hdmi_phy_i2c_write(hdmi, 0x0129, 0x0E); } - /* gen2 tx power on */ - val = hdmi_readb(HDMI_PHY_CONF0); - val &= ~HDMI_PHY_CONF0_GEN2_TXPWRON_MASK; - val |= HDMI_PHY_CONF0_GEN2_TXPWRON_POWER_ON; - hdmi_writeb(val, HDMI_PHY_CONF0); + mxc_hdmi_phy_enable_power(1); - val = hdmi_readb(HDMI_PHY_CONF0); - val &= ~HDMI_PHY_CONF0_GEN2_PDDQ_MASK; - val |= HDMI_PHY_CONF0_GEN2_PDDQ_DISABLE; - hdmi_writeb(val, HDMI_PHY_CONF0); + /* toggle TMDS enable */ + mxc_hdmi_phy_enable_tmds(0); + mxc_hdmi_phy_enable_tmds(1); + + /* gen2 tx power on */ + mxc_hdmi_phy_gen2_txpwron(1); + mxc_hdmi_phy_gen2_pddq(0); udelay(1000); - if ((hdmi_readb(HDMI_PHY_STAT0) & 0x01) == 0) - return FALSE; + /* wait for the phy PLL to lock */ + while ((hdmi_readb(HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK) == 0) + ; + + /* Has the PHY PLL locked? */ + if ((hdmi_readb(HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK) == 0) + return false; - return TRUE; + return true; } -void hdmi_phy_init(struct mxc_hdmi *hdmi, unsigned char de) +static void mxc_hdmi_phy_init(struct mxc_hdmi *hdmi) { - u8 val; + int i; + + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + + /* HDMI Phy spec says to do the phy initialization sequence twice */ + for (i = 0 ; i < 2 ; i++) { + mxc_hdmi_phy_sel_data_en_pol(1); + mxc_hdmi_phy_sel_interface_control(0); + mxc_hdmi_phy_enable_tmds(0); + mxc_hdmi_phy_enable_power(0); - /* set the DE polarity */ - val = (de << HDMI_PHY_CONF0_SELDATAENPOL_OFFSET) & - HDMI_PHY_CONF0_SELDATAENPOL_MASK; - /* set ENHPDRXSENSE to 1 */ - val |= HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE; - /* set the interface control to 0 */ - val |= (0 << HDMI_PHY_CONF0_SELDIPIF_OFFSET) & - HDMI_PHY_CONF0_SELDIPIF_MASK; - /* enable TMDS output */ - val |= (1 << HDMI_PHY_CONF0_ENTMDS_OFFSET) & - HDMI_PHY_CONF0_ENTMDS_MASK; - /* PHY power enable */ - val |= (1 << HDMI_PHY_CONF0_PDZ_OFFSET) & - HDMI_PHY_CONF0_PDZ_MASK; - hdmi_writeb(val, HDMI_PHY_CONF0); - - /* TODO: Enable CSC */ - hdmi_phy_configure(hdmi, 0, 8, FALSE, TRUE, TRUE, FALSE); + /* TODO: Enable CSC */ + hdmi_phy_configure(hdmi, 0, 8, false); + } + + hdmi->phy_enabled = true; } -void hdmi_tx_hdcp_config(struct mxc_hdmi *hdmi) +static void hdmi_tx_hdcp_config(struct mxc_hdmi *hdmi) { u8 de, val; @@ -976,20 +1054,6 @@ void hdmi_tx_hdcp_config(struct mxc_hdmi *hdmi) hdmi_writeb(val, HDMI_A_HDCPCFG1); } -void preamble_filter_set(struct mxc_hdmi *hdmi, unsigned char value, - unsigned char channel) -{ - if (channel == 0) - hdmi_writeb(value, HDMI_FC_CH0PREAM); - else if (channel == 1) - hdmi_writeb(value, HDMI_FC_CH1PREAM); - else if (channel == 2) - hdmi_writeb(value, HDMI_FC_CH2PREAM); - else - - return; -} - static void hdmi_config_AVI(struct mxc_hdmi *hdmi) { u8 val; @@ -1117,23 +1181,21 @@ static void hdmi_config_AVI(struct mxc_hdmi *hdmi) /*! * this submodule is responsible for the video/audio data composition. */ -void hdmi_av_composer(struct mxc_hdmi *hdmi) +static void hdmi_av_composer(struct mxc_hdmi *hdmi) { - unsigned char i = 0; - u8 val; + u8 inv_val; struct fb_info *fbi = hdmi->fbi; struct fb_videomode fb_mode; struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode; int hblank, vblank; + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + fb_var_to_videomode(&fb_mode, &fbi->var); - vmode->mHSyncPolarity = - (fb_mode.sync & FB_SYNC_HOR_HIGH_ACT) ? TRUE : FALSE; - vmode->mVSyncPolarity = - (fb_mode.sync & FB_SYNC_VERT_HIGH_ACT) ? TRUE : FALSE; - vmode->mInterlaced = - (fb_mode.vmode & FB_VMODE_INTERLACED) ? TRUE : FALSE; + vmode->mHSyncPolarity = ((fb_mode.sync & FB_SYNC_HOR_HIGH_ACT) != 0); + vmode->mVSyncPolarity = ((fb_mode.sync & FB_SYNC_VERT_HIGH_ACT) != 0); + vmode->mInterlaced = ((fb_mode.vmode & FB_VMODE_INTERLACED) != 0); vmode->mPixelClock = (fb_mode.xres + fb_mode.left_margin + fb_mode.right_margin + fb_mode.hsync_len) * (fb_mode.yres + fb_mode.upper_margin + fb_mode.lower_margin + @@ -1142,47 +1204,52 @@ void hdmi_av_composer(struct mxc_hdmi *hdmi) dev_dbg(&hdmi->pdev->dev, "final pixclk = %d\n", vmode->mPixelClock); /* Set up HDMI_FC_INVIDCONF */ - val = ((hdmi->hdmi_data.hdcp_enable == TRUE) ? + inv_val = (hdmi->hdmi_data.hdcp_enable ? HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE : HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE); - val |= ((vmode->mVSyncPolarity == TRUE) ? + + inv_val |= (vmode->mVSyncPolarity ? HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH : HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_LOW); - val |= ((vmode->mHSyncPolarity == TRUE) ? + + inv_val |= (vmode->mHSyncPolarity ? HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_HIGH : HDMI_FC_INVIDCONF_HSYNC_IN_POLARITY_ACTIVE_LOW); - val |= ((vmode->mDataEnablePolarity == TRUE) ? + + inv_val |= (vmode->mDataEnablePolarity ? HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH : HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_LOW); - val |= ((vmode->mHdmiDviSel == TRUE) ? - HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE : - HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE); + if (hdmi->vic == 39) - val |= HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH; + inv_val |= HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH; else - val |= ((vmode->mInterlaced == TRUE) ? + inv_val |= (vmode->mInterlaced ? HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_HIGH : HDMI_FC_INVIDCONF_R_V_BLANK_IN_OSC_ACTIVE_LOW); - val |= ((vmode->mInterlaced == TRUE) ? + + inv_val |= (vmode->mInterlaced ? HDMI_FC_INVIDCONF_IN_I_P_INTERLACED : HDMI_FC_INVIDCONF_IN_I_P_PROGRESSIVE); - hdmi_writeb(val, HDMI_FC_INVIDCONF); + + inv_val |= (vmode->mDVI ? + HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE : + HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE); + + hdmi_writeb(inv_val, HDMI_FC_INVIDCONF); /* Set up horizontal active pixel region width */ - hdmi_writeb(fb_mode.xres, - HDMI_FC_INHACTV0); - hdmi_writeb(fb_mode.xres >> 8, - HDMI_FC_INHACTV1); + hdmi_writeb(fb_mode.xres >> 8, HDMI_FC_INHACTV1); + hdmi_writeb(fb_mode.xres, HDMI_FC_INHACTV0); + + /* Set up vertical blanking pixel region width */ + hdmi_writeb(fb_mode.yres >> 8, HDMI_FC_INVACTV1); + hdmi_writeb(fb_mode.yres, HDMI_FC_INVACTV0); /* Set up horizontal blanking pixel region width */ hblank = fb_mode.left_margin + fb_mode.right_margin + fb_mode.hsync_len; - hdmi_writeb(hblank, HDMI_FC_INHBLANK0); hdmi_writeb(hblank >> 8, HDMI_FC_INHBLANK1); - - /* Set up vertical blanking pixel region width */ - hdmi_writeb(fb_mode.yres, HDMI_FC_INVACTV0); - hdmi_writeb(fb_mode.yres >> 8, HDMI_FC_INVACTV1); + hdmi_writeb(hblank, HDMI_FC_INHBLANK0); /* Set up vertical blanking pixel region width */ vblank = fb_mode.upper_margin + fb_mode.lower_margin + @@ -1190,117 +1257,250 @@ void hdmi_av_composer(struct mxc_hdmi *hdmi) hdmi_writeb(vblank, HDMI_FC_INVBLANK); /* Set up HSYNC active edge delay width (in pixel clks) */ - hdmi_writeb(fb_mode.right_margin, HDMI_FC_HSYNCINDELAY0); hdmi_writeb(fb_mode.right_margin >> 8, HDMI_FC_HSYNCINDELAY1); - - /* Set up HSYNC active pulse width (in pixel clks) */ - hdmi_writeb(fb_mode.hsync_len, HDMI_FC_HSYNCINWIDTH0); - hdmi_writeb(fb_mode.hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1); + hdmi_writeb(fb_mode.right_margin, HDMI_FC_HSYNCINDELAY0); /* Set up VSYNC active edge delay (in pixel clks) */ hdmi_writeb(fb_mode.lower_margin, HDMI_FC_VSYNCINDELAY); + /* Set up HSYNC active pulse width (in pixel clks) */ + hdmi_writeb(fb_mode.hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1); + hdmi_writeb(fb_mode.hsync_len, HDMI_FC_HSYNCINWIDTH0); + /* Set up VSYNC active edge delay (in pixel clks) */ hdmi_writeb(fb_mode.vsync_len, HDMI_FC_VSYNCINWIDTH); - /* control period minimum duration */ - hdmi_writeb(12, HDMI_FC_CTRLDUR); - hdmi_writeb(32, HDMI_FC_EXCTRLDUR); - hdmi_writeb(1, HDMI_FC_EXCTRLSPAC); - - for (i = 0; i < 3; i++) - preamble_filter_set(hdmi, (i + 1) * 11, i); - - /* configure AVI InfoFrame */ - hdmi_config_AVI(hdmi); + dev_dbg(&hdmi->pdev->dev, "%s exit\n", __func__); } -static int mxc_hdmi_read_edid(struct mxc_hdmi *hdmi, - struct fb_info *fbi) +static int mxc_hdmi_read_edid(struct mxc_hdmi *hdmi) { int ret; u8 edid_old[HDMI_EDID_LEN]; + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + /* save old edid */ memcpy(edid_old, hdmi->edid, HDMI_EDID_LEN); - /* edid reading */ - ret = mxc_edid_read(hdmi_i2c->adapter, hdmi_i2c->addr, - hdmi->edid, &hdmi->edid_cfg, fbi); + ret = mxc_edid_read(hdmi_i2c->adapter, hdmi_i2c->addr, hdmi->edid, + &hdmi->edid_cfg, hdmi->fbi); if (ret < 0) - return ret; + return HDMI_EDID_FAIL; - if (!memcmp(edid_old, hdmi->edid, HDMI_EDID_LEN)) - ret = -2; - return ret; + if (!memcmp(edid_old, hdmi->edid, HDMI_EDID_LEN)) { + dev_info(&hdmi->pdev->dev, "same edid\n"); + return HDMI_EDID_SAME; + } + + if (hdmi->fbi->monspecs.modedb_len == 0) { + dev_info(&hdmi->pdev->dev, "No modes read from edid\n"); + return HDMI_EDID_NO_MODES; + } + + return HDMI_EDID_SUCCESS; } -static void mxc_hdmi_poweron(struct mxc_hdmi *hdmi) +static void mxc_hdmi_enable_pins(struct mxc_hdmi *hdmi) { struct fsl_mxc_hdmi_platform_data *plat = hdmi->pdev->dev.platform_data; - dev_dbg(&hdmi->pdev->dev, "power on\n"); + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); /* Enable pins to HDMI */ if (plat->enable_pins) plat->enable_pins(); } -static void mxc_hdmi_poweroff(struct mxc_hdmi *hdmi) +static void mxc_hdmi_disable_pins(struct mxc_hdmi *hdmi) { struct fsl_mxc_hdmi_platform_data *plat = hdmi->pdev->dev.platform_data; - dev_dbg(&hdmi->pdev->dev, "power off\n"); + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); /* Disable pins to HDMI */ if (plat->disable_pins) plat->disable_pins(); } -static void mxc_hdmi_enable(struct mxc_hdmi *hdmi) +static void mxc_hdmi_phy_disable(struct mxc_hdmi *hdmi) { - u8 val; + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); - dev_dbg(&hdmi->pdev->dev, "hdmi enable\n"); + if (!hdmi->phy_enabled) + return; - clk_enable(hdmi->hdmi_iahb_clk); + mxc_hdmi_phy_enable_tmds(0); + mxc_hdmi_phy_enable_power(0); - /* Enable HDMI PHY - Set PDDQ=0 and TXPWRON=1 */ - val = hdmi_readb(HDMI_PHY_CONF0); - val &= ~(HDMI_PHY_CONF0_GEN2_PDDQ_MASK | - HDMI_PHY_CONF0_GEN2_TXPWRON_MASK); - val |= HDMI_PHY_CONF0_GEN2_PDDQ_DISABLE | - HDMI_PHY_CONF0_GEN2_TXPWRON_POWER_ON; - hdmi_writeb(val, HDMI_PHY_CONF0); - - if (hdmi->need_mode_change && hdmi->fb_reg) { - dev_dbg(&hdmi->pdev->dev, "HDMI changing FB mode\n"); - hdmi->fbi->var.activate |= FB_ACTIVATE_FORCE; - console_lock(); - hdmi->fbi->flags |= FBINFO_MISC_USEREVENT; - fb_set_var(hdmi->fbi, &hdmi->fbi->var); - hdmi->fbi->flags &= ~FBINFO_MISC_USEREVENT; - console_unlock(); - hdmi->need_mode_change = false; - } + hdmi->phy_enabled = false; + dev_dbg(&hdmi->pdev->dev, "%s - exit\n", __func__); +} + +/* HDMI Initialization Step B.4 */ +static void mxc_hdmi_enable_video_path(struct mxc_hdmi *hdmi) +{ + u8 clkdis; + + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + + /* control period minimum duration */ + hdmi_writeb(12, HDMI_FC_CTRLDUR); + hdmi_writeb(32, HDMI_FC_EXCTRLDUR); + hdmi_writeb(1, HDMI_FC_EXCTRLSPAC); + + /* Set to fill TMDS data channels */ + hdmi_writeb(0x0B, HDMI_FC_CH0PREAM); + hdmi_writeb(0x16, HDMI_FC_CH1PREAM); + hdmi_writeb(0x21, HDMI_FC_CH2PREAM); + + /* Enable pixel clock and tmds data path */ + clkdis = 0x7F; + clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE; + hdmi_writeb(clkdis, HDMI_MC_CLKDIS); + + clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE; + hdmi_writeb(clkdis, HDMI_MC_CLKDIS); +} + +static void hdmi_enable_audio_clk(struct mxc_hdmi *hdmi) +{ + u8 clkdis; + + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + + clkdis = hdmi_readb(HDMI_MC_CLKDIS); + clkdis &= ~HDMI_MC_CLKDIS_AUDCLK_DISABLE; + hdmi_writeb(clkdis, HDMI_MC_CLKDIS); } -static void mxc_hdmi_disable(struct mxc_hdmi *hdmi) +/* Workaround to clear the overflow condition */ +static void mxc_hdmi_clear_overflow(void) { + int count; u8 val; - dev_dbg(&hdmi->pdev->dev, "hdmi disable\n"); + val = hdmi_readb(HDMI_FC_INVIDCONF); - /* Disable HDMI PHY - Set PDDQ=1 and TXPWRON=0 */ - val = hdmi_readb(HDMI_PHY_CONF0); - val &= ~(HDMI_PHY_CONF0_GEN2_PDDQ_MASK | - HDMI_PHY_CONF0_GEN2_TXPWRON_MASK); - val |= HDMI_PHY_CONF0_GEN2_PDDQ_ENABLE | - HDMI_PHY_CONF0_GEN2_TXPWRON_POWER_OFF; - hdmi_writeb(val, HDMI_PHY_CONF0); + for (count = 0 ; count < 5 ; count++) + hdmi_writeb(val, HDMI_FC_INVIDCONF); - clk_disable(hdmi->hdmi_iahb_clk); + /* TMDS software reset */ + hdmi_writeb((u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ); +} + +static void hdmi_enable_overflow_interrupts(void) +{ + pr_debug("%s\n", __func__); + hdmi_writeb(0, HDMI_FC_MASK2); + hdmi_writeb(0, HDMI_IH_MUTE_FC_STAT2); +} + +static void hdmi_disable_overflow_interrupts(void) +{ + pr_debug("%s\n", __func__); + hdmi_writeb(HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK, + HDMI_IH_MUTE_FC_STAT2); +} + +static void mxc_hdmi_notify_fb(struct mxc_hdmi *hdmi) +{ + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + + /* Don't notify if we aren't registered yet */ + WARN_ON(!hdmi->fb_reg); + + /* disable the phy before ipu changes mode */ + mxc_hdmi_phy_disable(hdmi); + + /* + * Note that fb_set_var will block. During this time, + * FB_EVENT_MODE_CHANGE callback will happen. + * So by the end of this function, mxc_hdmi_setup() + * will be done. + */ + hdmi->fbi->var.activate |= FB_ACTIVATE_FORCE; + console_lock(); + hdmi->fbi->flags |= FBINFO_MISC_USEREVENT; + fb_set_var(hdmi->fbi, &hdmi->fbi->var); + hdmi->fbi->flags &= ~FBINFO_MISC_USEREVENT; + console_unlock(); + + dev_dbg(&hdmi->pdev->dev, "%s exit\n", __func__); +} + +static void mxc_hdmi_set_mode_to_previous(struct mxc_hdmi *hdmi) +{ + const struct fb_videomode *mode; + + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + + mode = fb_find_nearest_mode(&hdmi->previous_non_vga_mode, + &hdmi->fbi->modelist); + if (mode) { + fb_videomode_to_var(&hdmi->fbi->var, mode); + mxc_hdmi_notify_fb(hdmi); + } else + pr_err("%s: could not find mode in modelist\n", __func__); +} + +static void mxc_hdmi_edid_rebuild_modelist(struct mxc_hdmi *hdmi) +{ + int i; + const struct fb_videomode *mode; + struct fb_videomode m; + + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + + console_lock(); + + fb_destroy_modelist(&hdmi->fbi->modelist); + fb_add_videomode(&vga_mode, &hdmi->fbi->modelist); + + for (i = 0; i < hdmi->fbi->monspecs.modedb_len; i++) { + /* + * We might check here if mode is supported by HDMI. + * We do not currently support interlaced modes + */ + if (!(hdmi->fbi->monspecs.modedb[i].vmode & + FB_VMODE_INTERLACED)) { + dev_dbg(&hdmi->pdev->dev, "Added mode %d:", i); + dev_dbg(&hdmi->pdev->dev, + "xres = %d, yres = %d, freq = %d\n", + hdmi->fbi->monspecs.modedb[i].xres, + hdmi->fbi->monspecs.modedb[i].yres, + hdmi->fbi->monspecs.modedb[i].refresh); + + fb_add_videomode(&hdmi->fbi->monspecs.modedb[i], + &hdmi->fbi->modelist); + } + } + + console_unlock(); + + /* Set the default mode only once. */ + if (!hdmi->dft_mode_set) { + dev_dbg(&hdmi->pdev->dev, "%s: setting to default=%s bpp=%d\n", + __func__, hdmi->dft_mode_str, hdmi->default_bpp); + + fb_find_mode(&hdmi->fbi->var, hdmi->fbi, + hdmi->dft_mode_str, NULL, 0, NULL, + hdmi->default_bpp); + + hdmi->dft_mode_set = true; + } else + mxc_hdmi_set_mode_to_previous(hdmi); + + fb_var_to_videomode(&m, &hdmi->fbi->var); + dump_fb_videomode(&m); + mode = fb_find_nearest_mode(&m, &hdmi->fbi->modelist); + if (mode) { + fb_videomode_to_var(&hdmi->fbi->var, mode); + dump_fb_videomode((struct fb_videomode *)mode); + mxc_hdmi_notify_fb(hdmi); + } else + pr_err("%s: could not find mode in modelist\n", __func__); } static void mxc_hdmi_default_modelist(struct mxc_hdmi *hdmi) @@ -1308,89 +1508,103 @@ static void mxc_hdmi_default_modelist(struct mxc_hdmi *hdmi) u32 i; const struct fb_videomode *mode; + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + + /* If not EDID data read, set up default modelist */ + dev_info(&hdmi->pdev->dev, "No modes read from edid\n"); + + /* Set the default mode only once. */ + if (!hdmi->dft_mode_set) { + dev_dbg(&hdmi->pdev->dev, "%s: setting to default=%s bpp=%d\n", + __func__, hdmi->dft_mode_str, hdmi->default_bpp); + + fb_find_mode(&hdmi->fbi->var, hdmi->fbi, + hdmi->dft_mode_str, NULL, 0, NULL, + hdmi->default_bpp); + + hdmi->dft_mode_set = true; + } else { + fb_videomode_to_var(&hdmi->fbi->var, &hdmi->previous_non_vga_mode); + } + + console_lock(); + fb_destroy_modelist(&hdmi->fbi->modelist); for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) { mode = &mxc_cea_mode[i]; if ((mode->xres == hdmi->fbi->var.xres) && - (mode->yres == hdmi->fbi->var.yres) && - !(mode->vmode & FB_VMODE_INTERLACED)) - fb_add_videomode(mode, &hdmi->fbi->modelist); + (mode->yres == hdmi->fbi->var.yres) && + !(mode->vmode & FB_VMODE_INTERLACED)) + fb_add_videomode(mode, &hdmi->fbi->modelist); } + + console_unlock(); + + mxc_hdmi_notify_fb(hdmi); } -static int mxc_hdmi_cable_connected(struct mxc_hdmi *hdmi) +static void mxc_hdmi_set_mode_to_vga_dvi(struct mxc_hdmi *hdmi) { - int ret; - struct fb_videomode m; - const struct fb_videomode *mode; + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); - dev_dbg(&hdmi->pdev->dev, "cable connected\n"); + hdmi_disable_overflow_interrupts(); - hdmi->cable_plugin = true; + fb_videomode_to_var(&hdmi->fbi->var, &vga_mode); - /* edid read */ - ret = mxc_hdmi_read_edid(hdmi, hdmi->fbi); - if (ret == -2) - dev_info(&hdmi->pdev->dev, "same edid\n"); - else if (hdmi->fbi->monspecs.modedb_len > 0) { - int i; + hdmi->requesting_vga_for_initialization = true; + mxc_hdmi_notify_fb(hdmi); + hdmi->requesting_vga_for_initialization = false; +} - fb_destroy_modelist(&hdmi->fbi->modelist); +static void mxc_hdmi_cable_connected(struct mxc_hdmi *hdmi) +{ + int edid_status; - for (i = 0; i < hdmi->fbi->monspecs.modedb_len; i++) { - /* - * We might check here if mode is supported by HDMI. - * We do not currently support interlaced modes - */ - if (!(hdmi->fbi->monspecs.modedb[i].vmode - & FB_VMODE_INTERLACED)) { - dev_dbg(&hdmi->pdev->dev, "Added mode %d:", i); - dev_dbg(&hdmi->pdev->dev, - "xres = %d, yres = %d, freq = %d\n", - hdmi->fbi->monspecs.modedb[i].xres, - hdmi->fbi->monspecs.modedb[i].yres, - hdmi->fbi->monspecs.modedb[i].refresh); - fb_add_videomode(&hdmi->fbi->monspecs.modedb[i], - &hdmi->fbi->modelist); - } - } + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); - fb_var_to_videomode(&m, &hdmi->fbi->var); - mode = fb_find_nearest_mode(&m, - &hdmi->fbi->modelist); - if (mode) - fb_videomode_to_var(&hdmi->fbi->var, mode); + hdmi->cable_plugin = true; - hdmi->need_mode_change = mode ? true : false; - } else { - /* If not EDID data readed, setup default modelist */ - dev_info(&hdmi->pdev->dev, "No modes read from edid\n"); - mxc_hdmi_default_modelist(hdmi); + /* HDMI Initialization Step B */ + mxc_hdmi_set_mode_to_vga_dvi(hdmi); - fb_var_to_videomode(&m, &hdmi->fbi->var); - mode = fb_find_nearest_mode(&m, - &hdmi->fbi->modelist); - if (mode) - fb_videomode_to_var(&hdmi->fbi->var, mode); + /* HDMI Initialization Step C */ + edid_status = mxc_hdmi_read_edid(hdmi); - hdmi->need_mode_change = mode ? true : false; - } + /* HDMI Initialization Steps D, E, F */ + switch (edid_status) { + case HDMI_EDID_SUCCESS: + mxc_hdmi_edid_rebuild_modelist(hdmi); + break; + case HDMI_EDID_SAME: + mxc_hdmi_set_mode_to_previous(hdmi); + break; - return 0; + case HDMI_EDID_NO_MODES: + case HDMI_EDID_FAIL: + default: + mxc_hdmi_default_modelist(hdmi); + break; + } + + dev_dbg(&hdmi->pdev->dev, "%s exit\n", __func__); } static void mxc_hdmi_cable_disconnected(struct mxc_hdmi *hdmi) { + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + + hdmi_disable_overflow_interrupts(); + hdmi->cable_plugin = false; } -static void det_worker(struct work_struct *work) +static void hotplug_worker(struct work_struct *work) { struct delayed_work *delay_work = to_delayed_work(work); struct mxc_hdmi *hdmi = - container_of(delay_work, struct mxc_hdmi, det_work); + container_of(delay_work, struct mxc_hdmi, hotplug_work); u32 phy_int_stat, phy_int_pol, phy_int_mask; u8 val; bool hdmi_disable = false; @@ -1398,16 +1612,17 @@ static void det_worker(struct work_struct *work) unsigned long flags; if (!hdmi->irq_enabled) { + /* Enable clock long enough to do a few register accesses */ clk_enable(hdmi->hdmi_iahb_clk); - /* Capture status - used in det_worker ISR */ + /* Capture status - used in hotplug_worker ISR */ phy_int_stat = hdmi_readb(HDMI_IH_PHY_STAT0); if ((phy_int_stat & HDMI_IH_PHY_STAT0_HPD) == 0) { clk_disable(hdmi->hdmi_iahb_clk); return; /* No interrupts to handle */ } - dev_dbg(&hdmi->pdev->dev, "Hotplug interrupt received\n"); + dev_dbg(&hdmi->pdev->dev, "\nHotplug interrupt received\n"); /* Unmask interrupts until handled */ val = hdmi_readb(HDMI_PHY_MASK0); @@ -1433,15 +1648,22 @@ static void det_worker(struct work_struct *work) if (phy_int_stat & HDMI_IH_PHY_STAT0_HPD) { /* cable connection changes */ if (phy_int_pol & HDMI_PHY_HPD) { + /* + * Plugin event = assume that iahb clock was disabled. + */ dev_dbg(&hdmi->pdev->dev, "EVENT=plugin\n"); + + clk_enable(hdmi->hdmi_iahb_clk); mxc_hdmi_cable_connected(hdmi); - mxc_hdmi_enable(hdmi); /* Make HPD intr active low to capture unplug event */ val = hdmi_readb(HDMI_PHY_POL0); val &= ~HDMI_PHY_HPD; hdmi_writeb(val, HDMI_PHY_POL0); } else if (!(phy_int_pol & HDMI_PHY_HPD)) { + /* + * Plugout event = assume that iahb clock was enabled. + */ dev_dbg(&hdmi->pdev->dev, "EVENT=plugout\n"); mxc_hdmi_cable_disconnected(hdmi); hdmi_disable = true; @@ -1463,8 +1685,14 @@ static void det_worker(struct work_struct *work) phy_int_mask &= ~HDMI_PHY_HPD; hdmi_writeb(phy_int_mask, HDMI_PHY_MASK0); - if (hdmi_disable) - mxc_hdmi_disable(hdmi); + if (hdmi_readb(HDMI_FC_INT2) & HDMI_FC_INT2_OVERFLOW_MASK) + mxc_hdmi_clear_overflow(); + + /* We keep the iahb clock enabled only if we are plugged in. */ + if (hdmi_disable) { + mxc_hdmi_phy_disable(hdmi); + clk_disable(hdmi->hdmi_iahb_clk); + } spin_unlock_irqrestore(&hdmi->irq_lock, flags); } @@ -1488,7 +1716,19 @@ static irqreturn_t mxc_hdmi_hotplug(int irq, void *data) */ ret = hdmi_irq_disable(irq); if (ret == IRQ_DISABLE_FAIL) { - /* Capture status - used in det_worker ISR */ + if (hdmi_readb(HDMI_FC_INT2) & HDMI_FC_INT2_OVERFLOW_MASK) { + mxc_hdmi_clear_overflow(); + + /* clear irq status */ + hdmi_writeb(HDMI_IH_FC_STAT2_OVERFLOW_MASK, + HDMI_IH_FC_STAT2); + } + + /* + * We could not disable the irq. Probably the audio driver + * has enabled it. That also means that iahb clk is enabled. + */ + /* Capture status - used in hotplug_worker ISR */ intr_stat = hdmi_readb(HDMI_IH_PHY_STAT0); if ((intr_stat & HDMI_IH_PHY_STAT0_HPD) == 0) { hdmi_irq_enable(irq); @@ -1510,31 +1750,59 @@ static irqreturn_t mxc_hdmi_hotplug(int irq, void *data) } else hdmi->irq_enabled = false; - schedule_delayed_work(&(hdmi->det_work), msecs_to_jiffies(20)); + schedule_delayed_work(&(hdmi->hotplug_work), msecs_to_jiffies(20)); spin_unlock_irqrestore(&hdmi->irq_lock, flags); return IRQ_HANDLED; } -static int mxc_hdmi_setup(struct mxc_hdmi *hdmi) +static void mxc_hdmi_setup(struct mxc_hdmi *hdmi) { struct fb_videomode m; const struct fb_videomode *edid_mode; + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + fb_var_to_videomode(&m, &hdmi->fbi->var); - if (!list_empty(&hdmi->fbi->modelist)) { - edid_mode = fb_find_nearest_mode(&m, &hdmi->fbi->modelist); + dump_fb_videomode(&m); - hdmi->vic = mxc_edid_mode_to_vic(edid_mode); - } else - hdmi->vic = 0; + /* Exit the setup if we are already set to this video mode */ + if (fb_mode_is_equal(&hdmi->previous_mode, &m)) { + dev_dbg(&hdmi->pdev->dev, + "%s video mode did not change.\n", __func__); + mxc_hdmi_phy_init(hdmi); + return; + } + dev_dbg(&hdmi->pdev->dev, "%s - video mode changed\n", __func__); + + hdmi_disable_overflow_interrupts(); + + /* Save mode as 'previous_mode' so that we can know if mode changed. */ + memcpy(&hdmi->previous_mode, &m, sizeof(struct fb_videomode)); + + hdmi->vic = 0; + if (!hdmi->requesting_vga_for_initialization) { + /* Save mode if this isn't the result of requesting + * vga default. */ + memcpy(&hdmi->previous_non_vga_mode, &m, + sizeof(struct fb_videomode)); + + if (!list_empty(&hdmi->fbi->modelist)) { + edid_mode = fb_find_nearest_mode(&m, &hdmi->fbi->modelist); + pr_debug("edid mode "); + dump_fb_videomode((struct fb_videomode *)edid_mode); + hdmi->vic = mxc_edid_mode_to_vic(edid_mode); + } + } if (hdmi->vic == 0) { dev_dbg(&hdmi->pdev->dev, "Non-CEA mode used in HDMI\n"); - hdmi->hdmi_data.video_mode.mHdmiDviSel = FALSE; - } else - hdmi->hdmi_data.video_mode.mHdmiDviSel = TRUE; + hdmi->hdmi_data.video_mode.mDVI = true; + } else { + dev_dbg(&hdmi->pdev->dev, "CEA mode used vic=%d\n", hdmi->vic); + hdmi->hdmi_data.video_mode.mDVI = false; + } if ((hdmi->vic == 6) || (hdmi->vic == 7) || (hdmi->vic == 21) || (hdmi->vic == 22) || @@ -1572,20 +1840,78 @@ static int mxc_hdmi_setup(struct mxc_hdmi *hdmi) hdmi->hdmi_data.enc_color_depth = 8; hdmi->hdmi_data.pix_repet_factor = 0; hdmi->hdmi_data.hdcp_enable = 0; - hdmi->hdmi_data.video_mode.mDataEnablePolarity = TRUE; + hdmi->hdmi_data.video_mode.mDataEnablePolarity = true; - hdmi_video_force_output(hdmi, TRUE); + /* HDMI Initialization Step B.1 */ hdmi_av_composer(hdmi); + + /* HDMI Initializateion Step B.2 */ + mxc_hdmi_phy_init(hdmi); + + /* HDMI Initialization Step B.3 */ + mxc_hdmi_enable_video_path(hdmi); + + /* not for DVI mode */ + if (hdmi->hdmi_data.video_mode.mDVI) + dev_dbg(&hdmi->pdev->dev, "%s DVI mode\n", __func__); + else { + dev_dbg(&hdmi->pdev->dev, "%s CEA mode\n", __func__); + + /* HDMI Initialization Step E - Configure audio */ + hdmi_clk_regenerator_update_pixel_clock(); + hdmi_enable_audio_clk(hdmi); + + /* HDMI Initialization Step F - Configure AVI InfoFrame */ + hdmi_config_AVI(hdmi); + } + hdmi_video_packetize(hdmi); hdmi_video_csc(hdmi); hdmi_video_sample(hdmi); hdmi_tx_hdcp_config(hdmi); - hdmi_phy_init(hdmi, TRUE); - hdmi_video_force_output(hdmi, FALSE); - /*FIXME: audio CTS/N should be set after ipu display timming finish */ - /*hdmi_set_clk_regenerator();*/ - return 0; + mxc_hdmi_clear_overflow(); + if (hdmi->cable_plugin && !hdmi->hdmi_data.video_mode.mDVI) + hdmi_enable_overflow_interrupts(); + + dev_dbg(&hdmi->pdev->dev, "%s exit\n\n", __func__); +} + +/* Wait until we are registered to enable interrupts */ +static void mxc_hdmi_fb_registered(struct mxc_hdmi *hdmi) +{ + unsigned long flags; + + if (hdmi->fb_reg) + return; + + spin_lock_irqsave(&hdmi->irq_lock, flags); + + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + + clk_enable(hdmi->hdmi_iahb_clk); + + hdmi_writeb(HDMI_PHY_I2CM_INT_ADDR_DONE_POL, + HDMI_PHY_I2CM_INT_ADDR); + + hdmi_writeb(HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL | + HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL, + HDMI_PHY_I2CM_CTLINT_ADDR); + + /* enable cable hot plug irq */ + hdmi_writeb((u8)~HDMI_PHY_HPD, HDMI_PHY_MASK0); + + /* Clear Hotplug interrupts */ + hdmi_writeb(HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0); + + /* Unmute interrupts */ + hdmi_writeb(~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0); + + hdmi->fb_reg = true; + + clk_disable(hdmi->hdmi_iahb_clk); + + spin_unlock_irqrestore(&hdmi->irq_lock, flags); } static int mxc_hdmi_fb_event(struct notifier_block *nb, @@ -1599,44 +1925,61 @@ static int mxc_hdmi_fb_event(struct notifier_block *nb, switch (val) { case FB_EVENT_FB_REGISTERED: - hdmi->fb_reg = true; + dev_dbg(&hdmi->pdev->dev, "event=FB_EVENT_FB_REGISTERED\n"); + mxc_hdmi_fb_registered(hdmi); break; + case FB_EVENT_FB_UNREGISTERED: + dev_dbg(&hdmi->pdev->dev, "event=FB_EVENT_FB_UNREGISTERED\n"); hdmi->fb_reg = false; break; + case FB_EVENT_MODE_CHANGE: - mxc_hdmi_setup(hdmi); + dev_dbg(&hdmi->pdev->dev, "event=FB_EVENT_MODE_CHANGE\n"); + if (hdmi->fb_reg) + mxc_hdmi_setup(hdmi); break; + case FB_EVENT_BLANK: - if (*((int *)event->data) == FB_BLANK_UNBLANK) - mxc_hdmi_poweron(hdmi); - else - mxc_hdmi_poweroff(hdmi); + if (*((int *)event->data) == FB_BLANK_UNBLANK) { + dev_dbg(&hdmi->pdev->dev, + "event=FB_EVENT_BLANK - UNBLANK\n"); + mxc_hdmi_enable_pins(hdmi); + } else { + dev_dbg(&hdmi->pdev->dev, + "event=FB_EVENT_BLANK - BLANK\n"); + mxc_hdmi_disable_pins(hdmi); + } break; } return 0; } +/* HDMI Initialization Step A */ static int mxc_hdmi_disp_init(struct mxc_dispdrv_handle *disp, - struct mxc_dispdrv_setting *setting) + struct mxc_dispdrv_setting *setting) { int ret = 0; struct mxc_hdmi *hdmi = mxc_dispdrv_getdata(disp); struct fsl_mxc_hdmi_platform_data *plat = hdmi->pdev->dev.platform_data; int irq = platform_get_irq(hdmi->pdev, 0); - bool found = false; - u8 val; - const struct fb_videomode *mode; - struct fb_videomode m; - struct fb_var_screeninfo var; + + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); if (!plat || irq < 0) return -ENODEV; + hdmi->dft_mode_set = false; + setting->dev_id = mxc_hdmi_ipu_id; setting->disp_id = mxc_hdmi_disp_id; setting->if_fmt = IPU_PIX_FMT_RGB24; + hdmi->dft_mode_str = setting->dft_mode_str; + hdmi->default_bpp = setting->default_bpp; + dev_dbg(&hdmi->pdev->dev, "%s - default mode %s bpp=%d\n", + __func__, hdmi->dft_mode_str, hdmi->default_bpp); + hdmi->fbi = setting->fbi; /* Claim HDMI pins */ @@ -1690,84 +2033,26 @@ static int mxc_hdmi_disp_init(struct mxc_dispdrv_handle *disp, hdmi_readb(HDMI_PRODUCT_ID0), hdmi_readb(HDMI_PRODUCT_ID1)); - INIT_LIST_HEAD(&hdmi->fbi->modelist); - - /* try to read edid */ - ret = mxc_hdmi_read_edid(hdmi, hdmi->fbi); - if (ret < 0) { - /* If not EDID data readed, setup default modelist */ - dev_info(&hdmi->pdev->dev, "No modes read from edid\n"); - mxc_hdmi_default_modelist(hdmi); + /* To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator + * N and cts values before enabling phy */ + hdmi_init_clk_regenerator(); - fb_var_to_videomode(&m, &hdmi->fbi->var); - mode = fb_find_nearest_mode(&m, - &hdmi->fbi->modelist); - - fb_videomode_to_var(&hdmi->fbi->var, mode); - hdmi->need_mode_change = true; - } else if (hdmi->fbi->monspecs.modedb_len > 0) { - int i; - - for (i = 0; i < hdmi->fbi->monspecs.modedb_len; i++) { - /* - * We might check here if mode is supported by HDMI. - * Also, we do not currently support interlaced modes - */ - fb_videomode_to_var(&var, &hdmi->fbi->monspecs.modedb[i]); - if (!(hdmi->fbi->monspecs.modedb[i].vmode - & FB_VMODE_INTERLACED)) { - dev_dbg(&hdmi->pdev->dev, "Adding mode %d:", i); - dev_dbg(&hdmi->pdev->dev, - "xres = %d, yres = %d, freq = %d\n", - hdmi->fbi->monspecs.modedb[i].xres, - hdmi->fbi->monspecs.modedb[i].yres, - hdmi->fbi->monspecs.modedb[i].refresh); - fb_add_videomode( - &hdmi->fbi->monspecs.modedb[i], - &hdmi->fbi->modelist); - } - } - - fb_find_mode(&hdmi->fbi->var, hdmi->fbi, - setting->dft_mode_str, NULL, 0, NULL, - setting->default_bpp); - - fb_var_to_videomode(&m, &hdmi->fbi->var); - mode = fb_find_nearest_mode(&m, - &hdmi->fbi->modelist); - fb_videomode_to_var(&hdmi->fbi->var, mode); - found = 1; - - hdmi->need_mode_change = true; - } - - if (!found) { - ret = fb_find_mode(&hdmi->fbi->var, hdmi->fbi, - setting->dft_mode_str, NULL, 0, NULL, - setting->default_bpp); - if (!ret) { - ret = -EINVAL; - goto efindmode; - } - } + INIT_LIST_HEAD(&hdmi->fbi->modelist); spin_lock_init(&hdmi->irq_lock); - INIT_DELAYED_WORK(&(hdmi->det_work), det_worker); + fb_add_videomode(&vga_mode, &hdmi->fbi->modelist); + fb_videomode_to_var(&hdmi->fbi->var, &vga_mode); + + INIT_DELAYED_WORK(&hdmi->hotplug_work, hotplug_worker); /* Configure registers related to HDMI interrupt * generation before registering IRQ. */ hdmi_writeb(HDMI_PHY_HPD, HDMI_PHY_POL0); - /* enable cable hot plug irq */ - val = ~HDMI_PHY_HPD; - hdmi_writeb(val, HDMI_PHY_MASK0); - /* Clear Hotplug interrupts */ hdmi_writeb(HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0); - hdmi_writeb(~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0); - hdmi->nb.notifier_call = mxc_hdmi_fb_event; ret = fb_register_client(&hdmi->nb); if (ret < 0) @@ -1775,8 +2060,6 @@ static int mxc_hdmi_disp_init(struct mxc_dispdrv_handle *disp, memset(&hdmi->hdmi_data, 0, sizeof(struct hdmi_data_info)); - mxc_hdmi_setup(hdmi); - /* Disable IAHB clock while waiting for hotplug interrupt. * ISFR clock must remain enabled for hotplug to work. */ clk_disable(hdmi->hdmi_iahb_clk); @@ -1792,11 +2075,12 @@ static int mxc_hdmi_disp_init(struct mxc_dispdrv_handle *disp, goto ereqirq; } + dev_dbg(&hdmi->pdev->dev, "%s exit\n", __func__); + return ret; efbclient: free_irq(irq, hdmi); -efindmode: ereqirq: clk_disable(hdmi->hdmi_iahb_clk); erate2: @@ -1808,6 +2092,8 @@ erate1: egetclk1: plat->put_pins(); egetpins: + dev_dbg(&hdmi->pdev->dev, "%s error exit\n", __func__); + return ret; } @@ -1816,9 +2102,11 @@ static void mxc_hdmi_disp_deinit(struct mxc_dispdrv_handle *disp) struct mxc_hdmi *hdmi = mxc_dispdrv_getdata(disp); struct fsl_mxc_hdmi_platform_data *plat = hdmi->pdev->dev.platform_data; + dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); + fb_unregister_client(&hdmi->nb); - mxc_hdmi_poweroff(hdmi); + mxc_hdmi_disable_pins(hdmi); clk_disable(hdmi->hdmi_isfr_clk); clk_put(hdmi->hdmi_isfr_clk); @@ -1923,7 +2211,6 @@ static void __exit mxc_hdmi_exit(void) } module_exit(mxc_hdmi_exit); - static int __devinit mxc_hdmi_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { diff --git a/include/linux/mfd/mxc-hdmi-core.h b/include/linux/mfd/mxc-hdmi-core.h index f449946a50f..8c6ce456dda 100644 --- a/include/linux/mfd/mxc-hdmi-core.h +++ b/include/linux/mfd/mxc-hdmi-core.h @@ -22,16 +22,21 @@ #define IRQ_DISABLE_SUCCEED 0 #define IRQ_DISABLE_FAIL 1 +bool hdmi_check_overflow(void); + u8 hdmi_readb(unsigned int reg); void hdmi_writeb(u8 value, unsigned int reg); void hdmi_mask_writeb(u8 data, unsigned int addr, u8 shift, u8 mask); unsigned int hdmi_read4(unsigned int reg); void hdmi_write4(unsigned int value, unsigned int reg); + void hdmi_irq_init(void); void hdmi_irq_enable(int irq); unsigned int hdmi_irq_disable(int irq); + void hdmi_set_sample_rate(unsigned int rate); -void hdmi_set_clk_regenerator(void); +void hdmi_init_clk_regenerator(void); +void hdmi_clk_regenerator_update_pixel_clock(void); extern int mxc_hdmi_ipu_id; extern int mxc_hdmi_disp_id; -- cgit v1.2.3 From 95dc3a0113076aaf7b0dd80568417903a3dec98f Mon Sep 17 00:00:00 2001 From: Sandor Yu Date: Mon, 19 Dec 2011 11:17:48 +0800 Subject: ENGR00170534 mxc hdmi: hot-plug detect state notify and recording 1. create sys node for fb name, cable state, edid data 2. call kobject_uevent_env pass cable state Signed-off-by: Sandor Yu --- drivers/video/mxc_hdmi.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/drivers/video/mxc_hdmi.c b/drivers/video/mxc_hdmi.c index c9b1a835a97..7e3c8af7838 100644 --- a/drivers/video/mxc_hdmi.c +++ b/drivers/video/mxc_hdmi.c @@ -182,6 +182,52 @@ static void dump_fb_videomode(struct fb_videomode *m) {} #endif +static ssize_t mxc_hdmi_show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxc_hdmi *hdmi = dev_get_drvdata(dev); + + strcpy(buf, hdmi->fbi->fix.id); + sprintf(buf+strlen(buf), "\n"); + + return strlen(buf); +} + +static DEVICE_ATTR(fb_name, S_IRUGO, mxc_hdmi_show_name, NULL); + +static ssize_t mxc_hdmi_show_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxc_hdmi *hdmi = dev_get_drvdata(dev); + + if (hdmi->cable_plugin == false) + strcpy(buf, "plugout\n"); + else + strcpy(buf, "plugin\n"); + + return strlen(buf); +} + +static DEVICE_ATTR(cable_state, S_IRUGO, mxc_hdmi_show_state, NULL); + +static ssize_t mxc_hdmi_show_edid(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxc_hdmi *hdmi = dev_get_drvdata(dev); + int i, j, len = 0; + + for (j = 0; j < HDMI_EDID_LEN/16; j++) { + for (i = 0; i < 16; i++) + len += sprintf(buf+len, "0x%02X ", + hdmi->edid[j*16 + i]); + len += sprintf(buf+len, "\n"); + } + + return len; +} + +static DEVICE_ATTR(edid, S_IRUGO, mxc_hdmi_show_edid, NULL); + /*! * this submodule is responsible for the video data synchronization. * for example, for RGB 4:4:4 input, the data map is defined as @@ -1610,6 +1656,8 @@ static void hotplug_worker(struct work_struct *work) bool hdmi_disable = false; int irq = platform_get_irq(hdmi->pdev, 0); unsigned long flags; + char event_string[16]; + char *envp[] = { event_string, NULL }; if (!hdmi->irq_enabled) { /* Enable clock long enough to do a few register accesses */ @@ -1660,6 +1708,10 @@ static void hotplug_worker(struct work_struct *work) val = hdmi_readb(HDMI_PHY_POL0); val &= ~HDMI_PHY_HPD; hdmi_writeb(val, HDMI_PHY_POL0); + + sprintf(event_string, "EVENT=plugin"); + kobject_uevent_env(&hdmi->pdev->dev.kobj, KOBJ_CHANGE, envp); + } else if (!(phy_int_pol & HDMI_PHY_HPD)) { /* * Plugout event = assume that iahb clock was enabled. @@ -1672,6 +1724,10 @@ static void hotplug_worker(struct work_struct *work) val = hdmi_readb(HDMI_PHY_POL0); val |= HDMI_PHY_HPD; hdmi_writeb(val, HDMI_PHY_POL0); + + sprintf(event_string, "EVENT=plugout"); + kobject_uevent_env(&hdmi->pdev->dev.kobj, KOBJ_CHANGE, envp); + } else dev_dbg(&hdmi->pdev->dev, "EVENT=none?\n"); } @@ -2075,6 +2131,19 @@ static int mxc_hdmi_disp_init(struct mxc_dispdrv_handle *disp, goto ereqirq; } + ret = device_create_file(&hdmi->pdev->dev, &dev_attr_fb_name); + if (ret < 0) + dev_warn(&hdmi->pdev->dev, + "cound not create sys node for fb name\n"); + ret = device_create_file(&hdmi->pdev->dev, &dev_attr_cable_state); + if (ret < 0) + dev_warn(&hdmi->pdev->dev, + "cound not create sys node for cable state\n"); + ret = device_create_file(&hdmi->pdev->dev, &dev_attr_edid); + if (ret < 0) + dev_warn(&hdmi->pdev->dev, + "cound not create sys node for edid\n"); + dev_dbg(&hdmi->pdev->dev, "%s exit\n", __func__); return ret; -- cgit v1.2.3 From 2492cf1a2963900f46fbaba4e5e2df2146181d23 Mon Sep 17 00:00:00 2001 From: Sandor Yu Date: Wed, 21 Dec 2011 21:48:02 +0800 Subject: ENGR00170800 mxc hdmi add more video mode to default modelist Add all non-interlaced CEA mode to default modelist Add XGA and SXGA video mode to default modelist Signed-off-by: Sandor Yu --- drivers/video/mxc_hdmi.c | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/drivers/video/mxc_hdmi.c b/drivers/video/mxc_hdmi.c index 7e3c8af7838..97547a90adc 100644 --- a/drivers/video/mxc_hdmi.c +++ b/drivers/video/mxc_hdmi.c @@ -96,6 +96,19 @@ static const struct fb_videomode vga_mode = { FB_VMODE_NONINTERLACED | FB_VMODE_ASPECT_4_3, 0, }; +static const struct fb_videomode xga_mode = { + /* 13 1024x768-60 VESA */ + NULL, 60, 1024, 768, 15384, 160, 24, 29, 3, 136, 6, + 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA +}; + +static const struct fb_videomode sxga_mode = { + /* 20 1280x1024-60 VESA */ + NULL, 60, 1280, 1024, 9259, 248, 48, 38, 1, 112, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA +}; + enum hdmi_datamap { RGB444_8B = 0x01, RGB444_10B = 0x03, @@ -1553,11 +1566,13 @@ static void mxc_hdmi_default_modelist(struct mxc_hdmi *hdmi) { u32 i; const struct fb_videomode *mode; + struct fb_videomode m; dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); /* If not EDID data read, set up default modelist */ dev_info(&hdmi->pdev->dev, "No modes read from edid\n"); + dev_info(&hdmi->pdev->dev, "create default modelist\n"); /* Set the default mode only once. */ if (!hdmi->dft_mode_set) { @@ -1577,17 +1592,30 @@ static void mxc_hdmi_default_modelist(struct mxc_hdmi *hdmi) fb_destroy_modelist(&hdmi->fbi->modelist); + /*Add all no interlaced CEA mode to default modelist */ for (i = 0; i < ARRAY_SIZE(mxc_cea_mode); i++) { mode = &mxc_cea_mode[i]; - if ((mode->xres == hdmi->fbi->var.xres) && - (mode->yres == hdmi->fbi->var.yres) && - !(mode->vmode & FB_VMODE_INTERLACED)) + if (!(mode->vmode & FB_VMODE_INTERLACED) && (mode->xres != 0)) fb_add_videomode(mode, &hdmi->fbi->modelist); } + /*Add XGA and SXGA to default modelist */ + fb_add_videomode(&xga_mode, &hdmi->fbi->modelist); + fb_add_videomode(&sxga_mode, &hdmi->fbi->modelist); + console_unlock(); - mxc_hdmi_notify_fb(hdmi); + fb_var_to_videomode(&m, &hdmi->fbi->var); + dump_fb_videomode(&m); + mode = fb_find_nearest_mode(&m, &hdmi->fbi->modelist); + if (mode) { + fb_videomode_to_var(&hdmi->fbi->var, mode); + dump_fb_videomode((struct fb_videomode *)mode); + dev_warn(&hdmi->pdev->dev, + "Default modelist,the video mode may not support by monitor.\n"); + mxc_hdmi_notify_fb(hdmi); + } else + pr_err("%s: could not find mode in default modelist\n", __func__); } static void mxc_hdmi_set_mode_to_vga_dvi(struct mxc_hdmi *hdmi) -- cgit v1.2.3 From 379543c8773b5b45b061e97b7a99cd5453942568 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Tue, 27 Dec 2011 13:43:40 +0800 Subject: ARM: imx6q-sabrelite: set DDC i2c rate to 100k Signed-off-by: Jason Chen --- arch/arm/boot/dts/imx6q-sabrelite.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/imx6q-sabrelite.dts b/arch/arm/boot/dts/imx6q-sabrelite.dts index 35938d88577..1f6623847f9 100644 --- a/arch/arm/boot/dts/imx6q-sabrelite.dts +++ b/arch/arm/boot/dts/imx6q-sabrelite.dts @@ -56,7 +56,7 @@ i2c@021a4000 { /* I2C2 */ status = "okay"; - clock-frequency = <400000>; + clock-frequency = <100000>; ddc: ddc@50 { compatible = "fsl,imx6q-hdmi-ddc"; -- cgit v1.2.3 From 484b9c5a319170a45802d24ad0e70aa59dd4ad78 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Tue, 27 Dec 2011 16:12:07 +0800 Subject: mxc: hdmi: fix potention deadlock issue Signed-off-by: Jason Chen --- drivers/video/mxc_hdmi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/video/mxc_hdmi.c b/drivers/video/mxc_hdmi.c index 97547a90adc..b212ad128b7 100644 --- a/drivers/video/mxc_hdmi.c +++ b/drivers/video/mxc_hdmi.c @@ -1969,12 +1969,12 @@ static void mxc_hdmi_fb_registered(struct mxc_hdmi *hdmi) if (hdmi->fb_reg) return; + clk_enable(hdmi->hdmi_iahb_clk); + spin_lock_irqsave(&hdmi->irq_lock, flags); dev_dbg(&hdmi->pdev->dev, "%s\n", __func__); - clk_enable(hdmi->hdmi_iahb_clk); - hdmi_writeb(HDMI_PHY_I2CM_INT_ADDR_DONE_POL, HDMI_PHY_I2CM_INT_ADDR); @@ -1993,9 +1993,9 @@ static void mxc_hdmi_fb_registered(struct mxc_hdmi *hdmi) hdmi->fb_reg = true; - clk_disable(hdmi->hdmi_iahb_clk); - spin_unlock_irqrestore(&hdmi->irq_lock, flags); + + clk_disable(hdmi->hdmi_iahb_clk); } static int mxc_hdmi_fb_event(struct notifier_block *nb, -- cgit v1.2.3 From 102d6eece5d76c906e95e850b2f6bad3fef5c3bc Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Thu, 29 Dec 2011 14:32:44 +0800 Subject: imx6q-sabrelite: switch framebuffer order of HDMI and LVDS Signed-off-by: Eric Miao --- arch/arm/boot/dts/imx6q-sabrelite.dts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/arm/boot/dts/imx6q-sabrelite.dts b/arch/arm/boot/dts/imx6q-sabrelite.dts index 1f6623847f9..4effcd8604e 100644 --- a/arch/arm/boot/dts/imx6q-sabrelite.dts +++ b/arch/arm/boot/dts/imx6q-sabrelite.dts @@ -103,18 +103,18 @@ disp1: fb@0 { compatible = "fsl,mxcfb-ipuv3"; - disp_dev = "hdmi"; - interface_pix_fmt = "RGB24"; - mode_str = "1280x720M@60"; + disp_dev = "ldb"; + interface_pix_fmt = "RGB666"; + mode_str = "LDB-XGA"; internal_clk = "false"; reg = <0x00000000 0x0>; /* reserve fb mem */ }; disp2: fb@1 { compatible = "fsl,mxcfb-ipuv3"; - disp_dev = "ldb"; - interface_pix_fmt = "RGB666"; - mode_str = "LDB-XGA"; + disp_dev = "hdmi"; + interface_pix_fmt = "RGB24"; + mode_str = "1280x720M@60"; internal_clk = "false"; reg = <0x00000001 0x0>; /* reserve fb mem */ }; -- cgit v1.2.3 From 072a583f8857d6bf194885c54836f10d28d8469e Mon Sep 17 00:00:00 2001 From: Frank Li Date: Wed, 20 Jul 2011 18:41:50 +0800 Subject: input: add support for egalax touch screen controller Signed-off-by: Frank Li Signed-off-by: Eric Miao --- drivers/input/touchscreen/Kconfig | 10 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/egalax_ts.c | 357 ++++++++++++++++++++++++++++++++++ 3 files changed, 368 insertions(+) create mode 100644 drivers/input/touchscreen/egalax_ts.c diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 0a3433db0c8..12314e2c023 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -177,6 +177,16 @@ config TOUCHSCREEN_EETI To compile this driver as a module, choose M here: the module will be called eeti_ts. +config TOUCHSCREEN_EGALAX + tristate "EETI eGalax multi-touchscreen panel support" + depends on I2C + help + Say Y here to enable support for I2C connected EETI + eGalax multiple touch panels. + + To compile this driver as a module, choose M here: the + module will be called egalax_ts. + config TOUCHSCREEN_FUJITSU tristate "Fujitsu serial touchscreen" select SERIO diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index dbc9563dadb..71e19bfebfe 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o +obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c new file mode 100644 index 00000000000..58273b9e356 --- /dev/null +++ b/drivers/input/touchscreen/egalax_ts.c @@ -0,0 +1,357 @@ +/* + * Driver for EETI eGalax Multiple Touch Controller + * + * Copyright (C) 2011 Freescale Semiconductor, Inc. + * + * based on max11801_ts.c + * + * 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. + */ + +/* EETI eGalax serial touch screen controller is a I2C based multiple + * touch screen controller, it can supports 5 pointer multiple + * touch. */ + +/* TODO: + - auto idle mode support + - early suspend support for android +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REPORT_MODE_SINGLE 0x1 +#define REPORT_MODE_VENDOR 0x3 +#define REPORT_MODE_MTTOUCH 0x4 + +#define MAX_SUPPORT_POINTS 5 + +#define EVENT_MODE 0 +#define EVENT_STATUS 1 +#define EVENT_VALID_OFFSET 7 +#define EVENT_VAILD_MASK (0x1 << EVENT_VALID_OFFSET) +#define EVENT_ID_OFFSET 2 +#define EVENT_ID_MASK (0xf << EVENT_ID_OFFSET) +#define EVENT_IN_RANGE (0x1 << 1) +#define EVENT_DOWN_UP (0X1 << 0) + +#define MAX_I2C_DATA_LEN 10 + +struct egalax_pointer { + bool valid; + bool status; + u16 x; + u16 y; +}; + +struct egalax_ts { + struct i2c_client *client; + struct input_dev *input_dev; + struct egalax_pointer events[MAX_SUPPORT_POINTS]; +}; + +static irqreturn_t egalax_ts_interrupt(int irq, void *dev_id) +{ + struct egalax_ts *data = dev_id; + struct input_dev *input_dev = data->input_dev; + struct i2c_client *client = data->client; + struct egalax_pointer *events = data->events; + u8 buf[MAX_I2C_DATA_LEN]; + int i, id, ret, x, y; + bool down, valid; + u8 state; + +retry: + ret = i2c_master_recv(client, buf, MAX_I2C_DATA_LEN); + if (ret == -EAGAIN) + goto retry; + + if (ret < 0) + return IRQ_HANDLED; + + dev_dbg(&client->dev, "recv ret:%d", ret); + for (i = 0; i < MAX_I2C_DATA_LEN; i++) + printk(KERN_DEBUG " %x ", buf[i]); + + if (buf[0] != REPORT_MODE_VENDOR + && buf[0] != REPORT_MODE_SINGLE + && buf[0] != REPORT_MODE_MTTOUCH) { + /* invalid point */ + return IRQ_HANDLED; + } + + if (buf[0] == REPORT_MODE_VENDOR) { + dev_dbg(&client->dev, "vendor message, ignored\n"); + return IRQ_HANDLED; + } + + state = buf[1]; + x = (buf[3] << 8) | buf[2]; + y = (buf[5] << 8) | buf[4]; + + /* Currently, the panel Freescale using on SMD board _NOT_ + * support single pointer mode. All event are going to + * multiple pointer mode. Add single pointer mode according + * to EETI eGalax I2C programming manual. + */ + if (buf[0] == REPORT_MODE_SINGLE) { + input_report_abs(input_dev, ABS_X, x); + input_report_abs(input_dev, ABS_Y, y); + input_report_key(input_dev, BTN_TOUCH, !!state); + input_sync(input_dev); + return IRQ_HANDLED; + } + + /* deal with multiple touch */ + valid = state & EVENT_VAILD_MASK; + id = (state & EVENT_ID_MASK) >> EVENT_ID_OFFSET; + down = state & EVENT_DOWN_UP; + + if (!valid || id > MAX_SUPPORT_POINTS) { + dev_dbg(&client->dev, "point invalid\n"); + return IRQ_HANDLED; + } + + if (down) { + /* should also report old pointers */ + events[id].valid = valid; + events[id].status = down; + events[id].x = x; + events[id].y = y; + +#ifdef FORCE_SINGLE_POINTER_SUPPORT + input_report_abs(input_dev, ABS_X, x); + input_report_abs(input_dev, ABS_Y, y); + input_event(data->input_dev, EV_KEY, BTN_TOUCH, 1); + input_report_abs(input_dev, ABS_PRESSURE, 1); +#else + for (i = 0; i < MAX_SUPPORT_POINTS; i++) { + if (!events[i].valid) + continue; + dev_dbg(&client->dev, "report id:%d valid:%d x:%d y:%d", + i, valid, x, y); + + input_report_abs(input_dev, + ABS_MT_TRACKING_ID, i); + input_report_abs(input_dev, + ABS_MT_TOUCH_MAJOR, 1); + input_report_abs(input_dev, + ABS_MT_POSITION_X, events[i].x); + input_report_abs(input_dev, + ABS_MT_POSITION_Y, events[i].y); + input_mt_sync(input_dev); + } +#endif + } else { + dev_dbg(&client->dev, "release id:%d\n", id); + events[id].valid = 0; + events[id].status = 0; +#ifdef FORCE_SINGLE_POINTER_SUPPORT + input_report_key(input_dev, BTN_TOUCH, 0); + input_report_abs(input_dev, ABS_PRESSURE, 0); +#else + input_report_abs(input_dev, ABS_MT_TRACKING_ID, id); + input_event(input_dev, EV_ABS, ABS_MT_TOUCH_MAJOR, 0); + input_mt_sync(input_dev); +#endif + } + + input_sync(input_dev); + return IRQ_HANDLED; +} + +static int egalax_wake_up_device(struct i2c_client *client) +{ + int gpio = irq_to_gpio(client->irq); + int ret; + + ret = gpio_request(gpio, "egalax_irq"); + if (ret < 0) { + dev_err(&client->dev, "request gpio failed:%d\n", ret); + return ret; + } + /* wake up controller via an falling edge on IRQ. */ + gpio_direction_output(gpio, 0); + gpio_set_value(gpio, 1); + /* controller should be waken up, return irq. */ + gpio_direction_input(gpio); + gpio_free(gpio); + return 0; +} + +static int egalax_7200_firmware_version(struct i2c_client *client) +{ + static const u8 cmd[MAX_I2C_DATA_LEN] = { 0x03, 0x03, 0xa, 0x01, 0x41 }; + int ret; + ret = i2c_master_send(client, cmd, MAX_I2C_DATA_LEN); + if (ret < 0) + return ret; + return 0; +} + +static int __devinit egalax_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct egalax_ts *data; + struct input_dev *input_dev; + int ret; + + data = kzalloc(sizeof(struct egalax_ts), GFP_KERNEL); + if (!data) { + dev_err(&client->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(&client->dev, "Failed to allocate memory\n"); + ret = -ENOMEM; + goto err_free_data; + } + + data->client = client; + data->input_dev = input_dev; + egalax_wake_up_device(client); + ret = egalax_7200_firmware_version(client); + if (ret < 0) { + dev_err(&client->dev, + "egalax_ts: failed to read firmware version\n"); + ret = -EIO; + goto err_free_dev; + } + + input_dev->name = "eGalax Touch Screen"; + input_dev->phys = "I2C", + input_dev->id.bustype = BUS_I2C; + input_dev->id.vendor = 0x0EEF; + input_dev->id.product = 0x0020; + input_dev->id.version = 0x0001; + input_dev->dev.parent = &client->dev; + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + __set_bit(ABS_X, input_dev->absbit); + __set_bit(ABS_Y, input_dev->absbit); + __set_bit(ABS_PRESSURE, input_dev->absbit); + input_set_abs_params(input_dev, ABS_X, 0, 32767, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, 32767, 0, 0); + +#ifndef FORCE_SINGLE_POINTER_SUPPORT + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, 32767, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, 32767, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, + MAX_SUPPORT_POINTS, 0, 0); +#endif + input_set_drvdata(input_dev, data); + + ret = request_threaded_irq(client->irq, NULL, egalax_ts_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "egalax_ts", data); + if (ret < 0) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_free_dev; + } + + ret = input_register_device(data->input_dev); + if (ret < 0) + goto err_free_irq; + i2c_set_clientdata(client, data); + return 0; + +err_free_irq: + free_irq(client->irq, data); +err_free_dev: + input_free_device(input_dev); +err_free_data: + kfree(data); + + return ret; +} + +static __devexit int egalax_ts_remove(struct i2c_client *client) +{ + struct egalax_ts *data = i2c_get_clientdata(client); + + free_irq(client->irq, data); + input_unregister_device(data->input_dev); + input_free_device(data->input_dev); + kfree(data); + + return 0; +} + +static const struct i2c_device_id egalax_ts_id[] = { + {"egalax_ts", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, egalax_ts_id); + +#ifdef CONFIG_PM_SLEEP +static int egalax_ts_suspend(struct device *dev) +{ + int ret; + u8 suspend_cmd[MAX_I2C_DATA_LEN] = {0x3, 0x6, 0xa, 0x3, 0x36, + 0x3f, 0x2, 0, 0, 0}; + struct i2c_client *client = to_i2c_client(dev); + ret = i2c_master_send(client, suspend_cmd, + MAX_I2C_DATA_LEN); + return ret > 0 ? 0 : ret; +} + +static int egalax_ts_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + return egalax_wake_up_device(client); +} +#endif + +static SIMPLE_DEV_PM_OPS(egalax_ts_pm_ops, egalax_ts_suspend, egalax_ts_resume); +static struct i2c_driver egalax_ts_driver = { + .driver = { + .name = "egalax_ts", + .pm = &egalax_ts_pm_ops, + }, + .id_table = egalax_ts_id, + .probe = egalax_ts_probe, + .remove = __devexit_p(egalax_ts_remove), +}; + +static int __init egalax_ts_init(void) +{ + return i2c_add_driver(&egalax_ts_driver); +} + +static void __exit egalax_ts_exit(void) +{ + i2c_del_driver(&egalax_ts_driver); +} + +module_init(egalax_ts_init); +module_exit(egalax_ts_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("Touchscreen driver for EETI eGalax touch controller"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 925e814d9b46346af50dc3c7cf7a6106be5c9723 Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Fri, 30 Dec 2011 17:40:50 +0800 Subject: input: add device tree support for eGalax touch Signed-off-by: Eric Miao --- arch/arm/boot/dts/imx6q-sabrelite.dts | 13 ++++++++++ arch/arm/mach-imx/mach-imx6q.c | 3 +++ drivers/input/touchscreen/egalax_ts.c | 48 +++++++++++++++++++++++++---------- 3 files changed, 50 insertions(+), 14 deletions(-) diff --git a/arch/arm/boot/dts/imx6q-sabrelite.dts b/arch/arm/boot/dts/imx6q-sabrelite.dts index 4effcd8604e..e6d2cb73243 100644 --- a/arch/arm/boot/dts/imx6q-sabrelite.dts +++ b/arch/arm/boot/dts/imx6q-sabrelite.dts @@ -63,6 +63,19 @@ reg = <0x50>; }; }; + + i2c@021a8000 { /* I2C3 */ + status = "okay"; + clock-frequency = <400000>; + + egalax@04 { + compatible = "eeti,egalax"; + reg = <0x04>; + interrupt-parent = <&gpio1>; + interrupts = <9>; + interrupt-gpio = <&gpio1 9 0>; + }; + }; }; hdmi@0x00120000 { /* HDMI */ diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c index f5b31867953..3f46730ac06 100644 --- a/arch/arm/mach-imx/mach-imx6q.c +++ b/arch/arm/mach-imx/mach-imx6q.c @@ -67,6 +67,9 @@ static iomux_v3_cfg_t imx6q_sabrelite_pads[] = { /* I2C2 */ MX6Q_PAD_KEY_COL3__I2C2_SCL, MX6Q_PAD_KEY_ROW3__I2C2_SDA, + /* I2C3 */ + MX6Q_PAD_GPIO_5__I2C3_SCL, + MX6Q_PAD_GPIO_16__I2C3_SDA, /* GPIO */ MX6Q_PAD_NANDF_D0__GPIO_2_0, MX6Q_PAD_EIM_D23__GPIO_3_23, diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c index 58273b9e356..58184dad7a2 100644 --- a/drivers/input/touchscreen/egalax_ts.c +++ b/drivers/input/touchscreen/egalax_ts.c @@ -39,6 +39,9 @@ #include #include #include +#include +#include +#include #define REPORT_MODE_SINGLE 0x1 #define REPORT_MODE_VENDOR 0x3 @@ -68,6 +71,7 @@ struct egalax_ts { struct i2c_client *client; struct input_dev *input_dev; struct egalax_pointer events[MAX_SUPPORT_POINTS]; + int gpio_irq; }; static irqreturn_t egalax_ts_interrupt(int irq, void *dev_id) @@ -91,7 +95,7 @@ retry: dev_dbg(&client->dev, "recv ret:%d", ret); for (i = 0; i < MAX_I2C_DATA_LEN; i++) - printk(KERN_DEBUG " %x ", buf[i]); + dev_dbg(&client->dev, " %x ", buf[i]); if (buf[0] != REPORT_MODE_VENDOR && buf[0] != REPORT_MODE_SINGLE @@ -180,23 +184,24 @@ retry: return IRQ_HANDLED; } -static int egalax_wake_up_device(struct i2c_client *client) +static void egalax_wake_up_device(struct i2c_client *client) { - int gpio = irq_to_gpio(client->irq); - int ret; + struct egalax_ts *data = i2c_get_clientdata(client); + int ret, gpio_irq = data->gpio_irq; - ret = gpio_request(gpio, "egalax_irq"); + if (!gpio_is_valid(gpio_irq)) + return; + + ret = gpio_request_one(gpio_irq, GPIOF_OUT_INIT_HIGH, "egalax_irq"); if (ret < 0) { dev_err(&client->dev, "request gpio failed:%d\n", ret); - return ret; + return; } /* wake up controller via an falling edge on IRQ. */ - gpio_direction_output(gpio, 0); - gpio_set_value(gpio, 1); + gpio_set_value(gpio_irq, 0); /* controller should be waken up, return irq. */ - gpio_direction_input(gpio); - gpio_free(gpio); - return 0; + gpio_direction_input(gpio_irq); + gpio_free(gpio_irq); } static int egalax_7200_firmware_version(struct i2c_client *client) @@ -212,6 +217,7 @@ static int egalax_7200_firmware_version(struct i2c_client *client) static int __devinit egalax_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct device_node *n = client->dev.of_node; struct egalax_ts *data; struct input_dev *input_dev; int ret; @@ -231,6 +237,12 @@ static int __devinit egalax_ts_probe(struct i2c_client *client, data->client = client; data->input_dev = input_dev; + data->gpio_irq = of_get_named_gpio(n, "interrupt-gpio", 0); + if (!gpio_is_valid(data->gpio_irq)) + dev_warn(&client->dev, "invalid interrupt GPIO\n"); + + i2c_set_clientdata(client, data); + egalax_wake_up_device(client); ret = egalax_7200_firmware_version(client); if (ret < 0) { @@ -275,10 +287,9 @@ static int __devinit egalax_ts_probe(struct i2c_client *client, goto err_free_dev; } - ret = input_register_device(data->input_dev); + return input_register_device(data->input_dev); if (ret < 0) goto err_free_irq; - i2c_set_clientdata(client, data); return 0; err_free_irq: @@ -324,15 +335,24 @@ static int egalax_ts_suspend(struct device *dev) static int egalax_ts_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); - return egalax_wake_up_device(client); + egalax_wake_up_device(client); + return 0; } #endif +static struct of_device_id egalax_dt_ids[] = { + { .compatible = "eeti,egalax" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_uart_dt_ids); + static SIMPLE_DEV_PM_OPS(egalax_ts_pm_ops, egalax_ts_suspend, egalax_ts_resume); + static struct i2c_driver egalax_ts_driver = { .driver = { .name = "egalax_ts", .pm = &egalax_ts_pm_ops, + .of_match_table = egalax_dt_ids, }, .id_table = egalax_ts_id, .probe = egalax_ts_probe, -- cgit v1.2.3