diff options
Diffstat (limited to 'drivers/video/mxs')
-rw-r--r-- | drivers/video/mxs/Kconfig | 28 | ||||
-rw-r--r-- | drivers/video/mxs/Makefile | 6 | ||||
-rw-r--r-- | drivers/video/mxs/lcd_43wvf1g.c | 289 | ||||
-rw-r--r-- | drivers/video/mxs/lcd_lms430.c | 300 | ||||
-rw-r--r-- | drivers/video/mxs/lcdif.c | 136 | ||||
-rw-r--r-- | drivers/video/mxs/mxsfb.c | 950 | ||||
-rw-r--r-- | drivers/video/mxs/regs-tvenc.h | 583 | ||||
-rw-r--r-- | drivers/video/mxs/tvenc.c | 279 |
8 files changed, 2571 insertions, 0 deletions
diff --git a/drivers/video/mxs/Kconfig b/drivers/video/mxs/Kconfig new file mode 100644 index 000000000000..35b896e95d4f --- /dev/null +++ b/drivers/video/mxs/Kconfig @@ -0,0 +1,28 @@ +config FB_MXS + tristate "MXS Framebuffer driver" + depends on FB && ARCH_MXS + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + default y + ---help--- + Say Y here to enable support for the framebuffer driver for the + Freescale MXS Board. + +config FB_MXS_LCD_43WVF1G + depends on FB_MXS + tristate "SEIKO 4.3' LCD WVGA(800x480) PANEL" + default y if ARCH_MX28 + +config FB_MXS_LCD_LMS430 + depends on FB_MXS + bool "LMS430" + default y if ARCH_MX23 + ---help--- + Use LMS430 dotclock LCD panel for MXS + +config FB_MXS_TVENC + depends on ARCH_MXS + bool "TVENC" + ---help--- + Use TVOUT encoder for MXS diff --git a/drivers/video/mxs/Makefile b/drivers/video/mxs/Makefile new file mode 100644 index 000000000000..fbab953718c7 --- /dev/null +++ b/drivers/video/mxs/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_ARCH_MXS) += lcdif.o +obj-$(CONFIG_FB_MXS) += mxsfb.o +obj-$(CONFIG_FB_MXS_LCD_43WVF1G) += lcd_43wvf1g.o +obj-$(CONFIG_FB_MXS_LCD_LMS430) += lcd_lms430.o +# TVOUT support +obj-$(CONFIG_FB_MXS_TVENC) += tvenc.o diff --git a/drivers/video/mxs/lcd_43wvf1g.c b/drivers/video/mxs/lcd_43wvf1g.c new file mode 100644 index 000000000000..1a8157f277a0 --- /dev/null +++ b/drivers/video/mxs/lcd_43wvf1g.c @@ -0,0 +1,289 @@ +/* + * Freescale MX28 Seiko 43WVF1G LCD panel driver + * + * Copyright (C) 2009-2010 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. + */ + +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/notifier.h> +#include <linux/regulator/consumer.h> +#include <linux/platform_device.h> + +#include <mach/device.h> +#include <mach/lcdif.h> +#include <mach/regs-pwm.h> +#include <mach/system.h> + +#define DOTCLK_H_ACTIVE 800 +#define DOTCLK_H_PULSE_WIDTH 10 +#define DOTCLK_HF_PORCH 164 +#define DOTCLK_HB_PORCH 89 +#define DOTCLK_H_WAIT_CNT (DOTCLK_H_PULSE_WIDTH + DOTCLK_HB_PORCH) +#define DOTCLK_H_PERIOD (DOTCLK_H_WAIT_CNT + DOTCLK_HF_PORCH + DOTCLK_H_ACTIVE) + +#define DOTCLK_V_ACTIVE 480 +#define DOTCLK_V_PULSE_WIDTH 10 +#define DOTCLK_VF_PORCH 10 +#define DOTCLK_VB_PORCH 23 +#define DOTCLK_V_WAIT_CNT (DOTCLK_V_PULSE_WIDTH + DOTCLK_VB_PORCH) +#define DOTCLK_V_PERIOD (DOTCLK_VF_PORCH + DOTCLK_V_ACTIVE + DOTCLK_V_WAIT_CNT) + +static struct mxs_platform_bl_data bl_data; +static struct clk *lcd_clk; + +static int init_panel(struct device *dev, dma_addr_t phys, int memsize, + struct mxs_platform_fb_entry *pentry) +{ + int ret = 0; + lcd_clk = clk_get(dev, "dis_lcdif"); + if (IS_ERR(lcd_clk)) { + ret = PTR_ERR(lcd_clk); + goto out; + } + ret = clk_enable(lcd_clk); + if (ret) { + clk_put(lcd_clk); + goto out; + } + + ret = clk_set_rate(lcd_clk, 1000000 / pentry->cycle_time_ns); /* kHz */ + if (ret) { + clk_disable(lcd_clk); + clk_put(lcd_clk); + goto out; + } + + /* + * Make sure we do a high-to-low transition to reset the panel. + * First make it low for 100 msec, hi for 10 msec, low for 10 msec, + * then hi. + */ + __raw_writel(BM_LCDIF_CTRL1_RESET, REGS_LCDIF_BASE + HW_LCDIF_CTRL1_CLR); /* low */ + mdelay(100); + __raw_writel(BM_LCDIF_CTRL1_RESET, REGS_LCDIF_BASE + HW_LCDIF_CTRL1_SET); /* high */ + mdelay(10); + __raw_writel(BM_LCDIF_CTRL1_RESET, REGS_LCDIF_BASE + HW_LCDIF_CTRL1_CLR); /* low */ + + /* For the Samsung, Reset must be held low at least 30 uSec + * Therefore, we'll hold it low for about 10 mSec just to be sure. + * Then we'll wait 1 mSec afterwards. + */ + mdelay(10); + __raw_writel(BM_LCDIF_CTRL1_RESET, REGS_LCDIF_BASE + HW_LCDIF_CTRL1_SET); /* high */ + mdelay(1); + + setup_dotclk_panel(DOTCLK_V_PULSE_WIDTH, DOTCLK_V_PERIOD, + DOTCLK_V_WAIT_CNT, DOTCLK_V_ACTIVE, + DOTCLK_H_PULSE_WIDTH, DOTCLK_H_PERIOD, + DOTCLK_H_WAIT_CNT, DOTCLK_H_ACTIVE, 0); + + ret = mxs_lcdif_dma_init(dev, phys, memsize); + if (ret) + goto out; + + mxs_lcd_set_bl_pdata(pentry->bl_data); + mxs_lcdif_notify_clients(MXS_LCDIF_PANEL_INIT, pentry); + return 0; + +out: + return ret; +} + +static void release_panel(struct device *dev, + struct mxs_platform_fb_entry *pentry) +{ + mxs_lcdif_notify_clients(MXS_LCDIF_PANEL_RELEASE, pentry); + release_dotclk_panel(); + mxs_lcdif_dma_release(); + clk_disable(lcd_clk); + clk_put(lcd_clk); +} + +static int blank_panel(int blank) +{ + int ret = 0, count; + + switch (blank) { + case FB_BLANK_NORMAL: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_POWERDOWN: + __raw_writel(BM_LCDIF_CTRL_BYPASS_COUNT, + REGS_LCDIF_BASE + HW_LCDIF_CTRL_CLR); + for (count = 10000; count; count--) { + if (__raw_readl(REGS_LCDIF_BASE + HW_LCDIF_STAT) & + BM_LCDIF_STAT_TXFIFO_EMPTY) + break; + udelay(1); + } + break; + + case FB_BLANK_UNBLANK: + __raw_writel(BM_LCDIF_CTRL_BYPASS_COUNT, + REGS_LCDIF_BASE + HW_LCDIF_CTRL_SET); + break; + + default: + ret = -EINVAL; + } + return ret; +} + +static struct mxs_platform_fb_entry fb_entry = { + .name = "43wvf1g", + .x_res = 480, + .y_res = 800, + .bpp = 32, + .cycle_time_ns = 30, + .lcd_type = MXS_LCD_PANEL_DOTCLK, + .init_panel = init_panel, + .release_panel = release_panel, + .blank_panel = blank_panel, + .run_panel = mxs_lcdif_run, + .stop_panel = mxs_lcdif_stop, + .pan_display = mxs_lcdif_pan_display, + .bl_data = &bl_data, +}; + +static struct clk *pwm_clk; + +static int init_bl(struct mxs_platform_bl_data *data) +{ + int ret = 0; + + pwm_clk = clk_get(NULL, "pwm"); + if (IS_ERR(pwm_clk)) { + ret = PTR_ERR(pwm_clk); + return ret; + } + clk_enable(pwm_clk); + mxs_reset_block(REGS_PWM_BASE, 1); + + __raw_writel(BF_PWM_ACTIVEn_INACTIVE(0) | + BF_PWM_ACTIVEn_ACTIVE(0), + REGS_PWM_BASE + HW_PWM_ACTIVEn(2)); + __raw_writel(BF_PWM_PERIODn_CDIV(6) | /* divide by 64 */ + BF_PWM_PERIODn_INACTIVE_STATE(2) | /* low */ + BF_PWM_PERIODn_ACTIVE_STATE(3) | /* high */ + BF_PWM_PERIODn_PERIOD(599), + REGS_PWM_BASE + HW_PWM_PERIODn(2)); + __raw_writel(BM_PWM_CTRL_PWM2_ENABLE, REGS_PWM_BASE + HW_PWM_CTRL_SET); + + return 0; +} + +static void free_bl(struct mxs_platform_bl_data *data) +{ + __raw_writel(BF_PWM_ACTIVEn_INACTIVE(0) | + BF_PWM_ACTIVEn_ACTIVE(0), + REGS_PWM_BASE + HW_PWM_ACTIVEn(2)); + __raw_writel(BF_PWM_PERIODn_CDIV(6) | /* divide by 64 */ + BF_PWM_PERIODn_INACTIVE_STATE(2) | /* low */ + BF_PWM_PERIODn_ACTIVE_STATE(3) | /* high */ + BF_PWM_PERIODn_PERIOD(599), + REGS_PWM_BASE + HW_PWM_PERIODn(2)); + __raw_writel(BM_PWM_CTRL_PWM2_ENABLE, REGS_PWM_BASE + HW_PWM_CTRL_CLR); + + clk_disable(pwm_clk); + clk_put(pwm_clk); +} + +static int values[] = { 0, 4, 9, 14, 20, 27, 35, 45, 57, 75, 100 }; + +static int power[] = { + 0, 1500, 3600, 6100, 10300, + 15500, 74200, 114200, 155200, + 190100, 191000 +}; + +static int bl_to_power(int br) +{ + int base; + int rem; + + if (br > 100) + br = 100; + base = power[br / 10]; + rem = br % 10; + if (!rem) + return base; + else + return base + (rem * (power[br / 10 + 1]) - base) / 10; +} + +static int set_bl_intensity(struct mxs_platform_bl_data *data, + struct backlight_device *bd, int suspended) +{ + int intensity = bd->props.brightness; + int scaled_int; + + if (bd->props.power != FB_BLANK_UNBLANK) + intensity = 0; + if (bd->props.fb_blank != FB_BLANK_UNBLANK) + intensity = 0; + if (suspended) + intensity = 0; + + /* + * This is not too cool but what can we do? + * Luminance changes non-linearly... + */ + if (regulator_set_current_limit + (data->regulator, bl_to_power(intensity), bl_to_power(intensity))) + return -EBUSY; + + scaled_int = values[intensity / 10]; + if (scaled_int < 100) { + int rem = intensity - 10 * (intensity / 10); /* r = i % 10; */ + scaled_int += rem * (values[intensity / 10 + 1] - + values[intensity / 10]) / 10; + } + __raw_writel(BF_PWM_ACTIVEn_INACTIVE(scaled_int) | + BF_PWM_ACTIVEn_ACTIVE(0), + REGS_PWM_BASE + HW_PWM_ACTIVEn(2)); + __raw_writel(BF_PWM_PERIODn_CDIV(6) | /* divide by 64 */ + BF_PWM_PERIODn_INACTIVE_STATE(2) | /* low */ + BF_PWM_PERIODn_ACTIVE_STATE(3) | /* high */ + BF_PWM_PERIODn_PERIOD(399), + REGS_PWM_BASE + HW_PWM_PERIODn(2)); + return 0; +} + +static struct mxs_platform_bl_data bl_data = { + .bl_max_intensity = 100, + .bl_default_intensity = 50, + .bl_cons_intensity = 50, + .init_bl = init_bl, + .free_bl = free_bl, + .set_bl_intensity = set_bl_intensity, +}; + +static int __init register_devices(void) +{ + struct platform_device *pdev; + pdev = mxs_get_device("mxs-fb", 0); + if (pdev == NULL || IS_ERR(pdev)) + return -ENODEV; + + mxs_lcd_register_entry(&fb_entry, pdev->dev.platform_data); + + return 0; +} + +subsys_initcall(register_devices); diff --git a/drivers/video/mxs/lcd_lms430.c b/drivers/video/mxs/lcd_lms430.c new file mode 100644 index 000000000000..c167774d5785 --- /dev/null +++ b/drivers/video/mxs/lcd_lms430.c @@ -0,0 +1,300 @@ +/* + * Freescale MXS Samsung LMS430 LCD panel initialization + * + * Embedded Alley Solutions, Inc <source@embeddedalley.com> + * + * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. + * Copyright 2009 Embedded Alley Solutions, 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. + */ + +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/notifier.h> +#include <linux/regulator/consumer.h> +#include <linux/platform_device.h> + +#include <mach/device.h> +#include <mach/lcdif.h> +#include <mach/regs-pwm.h> +#include <mach/system.h> + +#define REGS_PWM_BASE IO_ADDRESS(PWM_PHYS_ADDR) + +#define DOTCLK_H_ACTIVE 480 +#define DOTCLK_H_PULSE_WIDTH 1 +#define DOTCLK_HF_PORCH 8 +#define DOTCLK_HB_PORCH 15 +#define DOTCLK_H_WAIT_CNT (DOTCLK_H_PULSE_WIDTH + (3 * DOTCLK_HB_PORCH)) +#define DOTCLK_H_PERIOD (DOTCLK_H_WAIT_CNT + DOTCLK_HF_PORCH + DOTCLK_H_ACTIVE) + +#define DOTCLK_V_ACTIVE 272 +#define DOTCLK_V_PULSE_WIDTH 1 +#define DOTCLK_VF_PORCH 4 +#define DOTCLK_VB_PORCH 12 +#define DOTCLK_V_WAIT_CNT (DOTCLK_V_PULSE_WIDTH + DOTCLK_VB_PORCH) +#define DOTCLK_V_PERIOD (DOTCLK_VF_PORCH + DOTCLK_V_ACTIVE + DOTCLK_V_WAIT_CNT) + +static struct mxs_platform_bl_data bl_data; +static struct clk *lcd_clk; + +static int init_panel(struct device *dev, dma_addr_t phys, int memsize, + struct mxs_platform_fb_entry *pentry) +{ + int ret = 0; + lcd_clk = clk_get(NULL, "lcdif"); + if (IS_ERR(lcd_clk)) { + ret = PTR_ERR(lcd_clk); + goto out; + } + ret = clk_enable(lcd_clk); + if (ret) { + clk_put(lcd_clk); + goto out; + } + + ret = clk_set_rate(lcd_clk, 1000000000 / pentry->cycle_time_ns); /* Hz */ + if (ret) { + clk_disable(lcd_clk); + clk_put(lcd_clk); + goto out; + } + + /* + * Make sure we do a high-to-low transition to reset the panel. + * First make it low for 100 msec, hi for 10 msec, low for 10 msec, + * then hi. + */ + __raw_writel(BM_LCDIF_CTRL1_RESET, REGS_LCDIF_BASE + HW_LCDIF_CTRL1_CLR); /* low */ + mdelay(100); + __raw_writel(BM_LCDIF_CTRL1_RESET, REGS_LCDIF_BASE + HW_LCDIF_CTRL1_SET); /* high */ + mdelay(10); + __raw_writel(BM_LCDIF_CTRL1_RESET, REGS_LCDIF_BASE + HW_LCDIF_CTRL1_CLR); /* low */ + + /* For the Samsung, Reset must be held low at least 30 uSec + * Therefore, we'll hold it low for about 10 mSec just to be sure. + * Then we'll wait 1 mSec afterwards. + */ + mdelay(10); + __raw_writel(BM_LCDIF_CTRL1_RESET, REGS_LCDIF_BASE + HW_LCDIF_CTRL1_SET); /* high */ + mdelay(1); + + setup_dotclk_panel(DOTCLK_V_PULSE_WIDTH, DOTCLK_V_PERIOD, + DOTCLK_V_WAIT_CNT, DOTCLK_V_ACTIVE, + DOTCLK_H_PULSE_WIDTH, DOTCLK_H_PERIOD, + DOTCLK_H_WAIT_CNT, DOTCLK_H_ACTIVE, 0); + + ret = mxs_lcdif_dma_init(dev, phys, memsize); + if (ret) + goto out; + + mxs_lcd_set_bl_pdata(pentry->bl_data); + mxs_lcdif_notify_clients(MXS_LCDIF_PANEL_INIT, pentry); + return 0; + +out: + return ret; +} + +static void release_panel(struct device *dev, + struct mxs_platform_fb_entry *pentry) +{ + /* Reset LCD panel signel. */ + __raw_writel(BM_LCDIF_CTRL1_RESET, + REGS_LCDIF_BASE + HW_LCDIF_CTRL1_CLR); + mdelay(100); + mxs_lcdif_notify_clients(MXS_LCDIF_PANEL_RELEASE, pentry); + release_dotclk_panel(); + mxs_lcdif_dma_release(); + clk_disable(lcd_clk); + clk_put(lcd_clk); + __raw_writel(BM_LCDIF_CTRL_CLKGATE, + REGS_LCDIF_BASE + HW_LCDIF_CTRL_SET); +} + +static int blank_panel(int blank) +{ + int ret = 0, count; + + switch (blank) { + case FB_BLANK_NORMAL: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_POWERDOWN: + __raw_writel(BM_LCDIF_CTRL_BYPASS_COUNT, + REGS_LCDIF_BASE + HW_LCDIF_CTRL_CLR); + for (count = 10000; count; count--) { + if (__raw_readl(REGS_LCDIF_BASE + HW_LCDIF_STAT) & + BM_LCDIF_STAT_TXFIFO_EMPTY) + break; + udelay(1); + } + break; + + case FB_BLANK_UNBLANK: + __raw_writel(BM_LCDIF_CTRL_BYPASS_COUNT, + REGS_LCDIF_BASE + HW_LCDIF_CTRL_SET); + break; + + default: + ret = -EINVAL; + } + return ret; +} + +static struct mxs_platform_fb_entry fb_entry = { + .name = "lms430", + .x_res = 272, + .y_res = 480, + .bpp = 32, + .cycle_time_ns = 150, + .lcd_type = MXS_LCD_PANEL_DOTCLK, + .init_panel = init_panel, + .release_panel = release_panel, + .blank_panel = blank_panel, + .run_panel = mxs_lcdif_run, + .stop_panel = mxs_lcdif_stop, + .pan_display = mxs_lcdif_pan_display, + .bl_data = &bl_data, +}; + +static struct clk *pwm_clk; + +static int init_bl(struct mxs_platform_bl_data *data) +{ + int ret = 0; + + pwm_clk = clk_get(NULL, "pwm"); + if (IS_ERR(pwm_clk)) { + ret = PTR_ERR(pwm_clk); + return ret; + } + clk_enable(pwm_clk); + mxs_reset_block(REGS_PWM_BASE, 1); + + __raw_writel(BF_PWM_ACTIVEn_INACTIVE(0) | + BF_PWM_ACTIVEn_ACTIVE(0), + REGS_PWM_BASE + HW_PWM_ACTIVEn(2)); + __raw_writel(BF_PWM_PERIODn_CDIV(6) | /* divide by 64 */ + BF_PWM_PERIODn_INACTIVE_STATE(2) | /* low */ + BF_PWM_PERIODn_ACTIVE_STATE(3) | /* high */ + BF_PWM_PERIODn_PERIOD(599), + REGS_PWM_BASE + HW_PWM_PERIODn(2)); + __raw_writel(BM_PWM_CTRL_PWM2_ENABLE, REGS_PWM_BASE + HW_PWM_CTRL_SET); + + return 0; +} + +static void free_bl(struct mxs_platform_bl_data *data) +{ + __raw_writel(BF_PWM_ACTIVEn_INACTIVE(0) | + BF_PWM_ACTIVEn_ACTIVE(0), + REGS_PWM_BASE + HW_PWM_ACTIVEn(2)); + __raw_writel(BF_PWM_PERIODn_CDIV(6) | /* divide by 64 */ + BF_PWM_PERIODn_INACTIVE_STATE(2) | /* low */ + BF_PWM_PERIODn_ACTIVE_STATE(3) | /* high */ + BF_PWM_PERIODn_PERIOD(599), + REGS_PWM_BASE + HW_PWM_PERIODn(2)); + __raw_writel(BM_PWM_CTRL_PWM2_ENABLE, REGS_PWM_BASE + HW_PWM_CTRL_CLR); + + clk_disable(pwm_clk); + clk_put(pwm_clk); +} + +static int values[] = { 0, 4, 9, 14, 20, 27, 35, 45, 57, 75, 100 }; + +static int power[] = { + 0, 1500, 3600, 6100, 10300, + 15500, 74200, 114200, 155200, + 190100, 191000 +}; + +static int bl_to_power(int br) +{ + int base; + int rem; + + if (br > 100) + br = 100; + base = power[br / 10]; + rem = br % 10; + if (!rem) + return base; + else + return base + (rem * (power[br / 10 + 1]) - base) / 10; +} + +static int set_bl_intensity(struct mxs_platform_bl_data *data, + struct backlight_device *bd, int suspended) +{ + int intensity = bd->props.brightness; + int scaled_int; + + if (bd->props.power != FB_BLANK_UNBLANK) + intensity = 0; + if (bd->props.fb_blank != FB_BLANK_UNBLANK) + intensity = 0; + if (suspended) + intensity = 0; + + /* + * This is not too cool but what can we do? + * Luminance changes non-linearly... + */ + if (regulator_set_current_limit + (data->regulator, bl_to_power(intensity), bl_to_power(intensity))) + return -EBUSY; + + scaled_int = values[intensity / 10]; + if (scaled_int < 100) { + int rem = intensity - 10 * (intensity / 10); /* r = i % 10; */ + scaled_int += rem * (values[intensity / 10 + 1] - + values[intensity / 10]) / 10; + } + __raw_writel(BF_PWM_ACTIVEn_INACTIVE(scaled_int) | + BF_PWM_ACTIVEn_ACTIVE(0), + REGS_PWM_BASE + HW_PWM_ACTIVEn(2)); + __raw_writel(BF_PWM_PERIODn_CDIV(6) | /* divide by 64 */ + BF_PWM_PERIODn_INACTIVE_STATE(2) | /* low */ + BF_PWM_PERIODn_ACTIVE_STATE(3) | /* high */ + BF_PWM_PERIODn_PERIOD(399), + REGS_PWM_BASE + HW_PWM_PERIODn(2)); + return 0; +} + +static struct mxs_platform_bl_data bl_data = { + .bl_max_intensity = 100, + .bl_default_intensity = 50, + .bl_cons_intensity = 50, + .init_bl = init_bl, + .free_bl = free_bl, + .set_bl_intensity = set_bl_intensity, +}; + +static int __init register_devices(void) +{ + struct platform_device *pdev; + pdev = mxs_get_device("mxs-fb", 0); + if (pdev == NULL || IS_ERR(pdev)) + return -ENODEV; + + mxs_lcd_register_entry(&fb_entry, pdev->dev.platform_data); + + return 0; +} + +subsys_initcall(register_devices); diff --git a/drivers/video/mxs/lcdif.c b/drivers/video/mxs/lcdif.c new file mode 100644 index 000000000000..f7d48a6fb3e9 --- /dev/null +++ b/drivers/video/mxs/lcdif.c @@ -0,0 +1,136 @@ +/* + * Freescale MXS LCDIF low-level routines + * + * Author: Vitaly Wool <vital@embeddedalley.com> + * + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + * Copyright (C) 2009-2010 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. + */ + +#include <linux/delay.h> +#include <linux/dma-mapping.h> + +#include <mach/hardware.h> +#include <mach/lcdif.h> +#include <mach/regs-lcdif.h> +#include <mach/system.h> + +#define REGS_LCDIF_BASE IO_ADDRESS(LCDIF_PHYS_ADDR) + +void mxs_init_lcdif(void) +{ + __raw_writel(BM_LCDIF_CTRL_CLKGATE, + REGS_LCDIF_BASE + HW_LCDIF_CTRL_CLR); + /* Reset controller */ + __raw_writel(BM_LCDIF_CTRL_SFTRST, REGS_LCDIF_BASE + HW_LCDIF_CTRL_SET); + udelay(10); + + /* Take controller out of reset */ + __raw_writel(BM_LCDIF_CTRL_SFTRST | BM_LCDIF_CTRL_CLKGATE, + REGS_LCDIF_BASE + HW_LCDIF_CTRL_CLR); + + /* Setup the bus protocol */ + __raw_writel(BM_LCDIF_CTRL1_MODE86, + REGS_LCDIF_BASE + HW_LCDIF_CTRL1_CLR); + __raw_writel(BM_LCDIF_CTRL1_BUSY_ENABLE, + REGS_LCDIF_BASE + HW_LCDIF_CTRL1_CLR); + + /* Take display out of reset */ + __raw_writel(BM_LCDIF_CTRL1_RESET, + REGS_LCDIF_BASE + HW_LCDIF_CTRL1_SET); + + /* VSYNC is an input by default */ + __raw_writel(BM_LCDIF_VDCTRL0_VSYNC_OEB, + REGS_LCDIF_BASE + HW_LCDIF_VDCTRL0_SET); + + /* Reset display */ + __raw_writel(BM_LCDIF_CTRL1_RESET, + REGS_LCDIF_BASE + HW_LCDIF_CTRL1_CLR); + udelay(10); + __raw_writel(BM_LCDIF_CTRL1_RESET, + REGS_LCDIF_BASE + HW_LCDIF_CTRL1_SET); + udelay(10); +} +EXPORT_SYMBOL(mxs_init_lcdif); + +int mxs_lcdif_dma_init(struct device *dev, dma_addr_t phys, int memsize) +{ + __raw_writel(BM_LCDIF_CTRL_LCDIF_MASTER, + REGS_LCDIF_BASE + HW_LCDIF_CTRL_SET); + + __raw_writel(phys, REGS_LCDIF_BASE + HW_LCDIF_CUR_BUF); + __raw_writel(phys, REGS_LCDIF_BASE + HW_LCDIF_NEXT_BUF); + + return 0; +} +EXPORT_SYMBOL(mxs_lcdif_dma_init); + +void mxs_lcdif_dma_release(void) +{ + __raw_writel(BM_LCDIF_CTRL_LCDIF_MASTER, + REGS_LCDIF_BASE + HW_LCDIF_CTRL_CLR); + return; +} +EXPORT_SYMBOL(mxs_lcdif_dma_release); + +void mxs_lcdif_run(void) +{ + __raw_writel(BM_LCDIF_CTRL_LCDIF_MASTER, + REGS_LCDIF_BASE + HW_LCDIF_CTRL_SET); + __raw_writel(BM_LCDIF_CTRL_RUN, REGS_LCDIF_BASE + HW_LCDIF_CTRL_SET); +} +EXPORT_SYMBOL(mxs_lcdif_run); + +void mxs_lcdif_stop(void) +{ + __raw_writel(BM_LCDIF_CTRL_RUN, REGS_LCDIF_BASE + HW_LCDIF_CTRL_CLR); + __raw_writel(BM_LCDIF_CTRL_LCDIF_MASTER, + REGS_LCDIF_BASE + HW_LCDIF_CTRL_CLR); + udelay(100); +} +EXPORT_SYMBOL(mxs_lcdif_stop); + +int mxs_lcdif_pan_display(dma_addr_t addr) +{ + __raw_writel(addr, REGS_LCDIF_BASE + HW_LCDIF_NEXT_BUF); + + return 0; +} + +EXPORT_SYMBOL(mxs_lcdif_pan_display); + +static BLOCKING_NOTIFIER_HEAD(lcdif_client_list); + +int mxs_lcdif_register_client(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&lcdif_client_list, nb); +} + +EXPORT_SYMBOL(mxs_lcdif_register_client); + +void mxs_lcdif_unregister_client(struct notifier_block *nb) +{ + blocking_notifier_chain_unregister(&lcdif_client_list, nb); +} +EXPORT_SYMBOL(mxs_lcdif_unregister_client); + +void mxs_lcdif_notify_clients(unsigned long event, + struct mxs_platform_fb_entry *pentry) +{ + blocking_notifier_call_chain(&lcdif_client_list, event, pentry); +} +EXPORT_SYMBOL(mxs_lcdif_notify_clients); diff --git a/drivers/video/mxs/mxsfb.c b/drivers/video/mxs/mxsfb.c new file mode 100644 index 000000000000..0e58ced83066 --- /dev/null +++ b/drivers/video/mxs/mxsfb.c @@ -0,0 +1,950 @@ +/* + * Freescale MXS framebuffer driver + * + * Author: Vitaly Wool <vital@embeddedalley.com> + * + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + * Copyright (C) 2009-2010 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. + */ + +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/uaccess.h> +#include <linux/cpufreq.h> + +#include <mach/hardware.h> +#include <mach/regs-lcdif.h> +#include <mach/clock.h> +#include <mach/lcdif.h> + +#define NUM_SCREENS 1 + +enum { + F_DISABLE = 0, + F_ENABLE, + F_REENABLE, +}; + +struct mxs_fb_data { + struct fb_info info; + struct mxs_platform_fb_data *pdata; + struct work_struct work; + struct mutex blank_mutex; + u32 state; + u32 task_state; + ssize_t mem_size; + ssize_t map_size; + dma_addr_t phys_start; + dma_addr_t cur_phys; + int irq; + unsigned long regbase; + void *virt_start; + struct device *dev; + wait_queue_head_t vsync_wait_q; + u32 vsync_count; + void *par; +}; + +/* forward declaration */ +static int mxsfb_blank(int blank, struct fb_info *info); +static unsigned char *default_panel_name; +static struct mxs_fb_data *cdata; +static void init_timings(struct mxs_fb_data *data); + +static void mxsfb_enable_controller(struct mxs_fb_data *data) +{ + struct mxs_platform_fb_entry *pentry = data->pdata->cur; + + if (!data || !data->pdata || !data->pdata->cur) + return; + + mxs_init_lcdif(); + init_timings(data); + pentry->init_panel(data->dev, data->phys_start, + data->info.fix.smem_len, data->pdata->cur); + pentry->run_panel(); + + if (pentry->blank_panel) + pentry->blank_panel(FB_BLANK_UNBLANK); +} + +static void mxsfb_disable_controller(struct mxs_fb_data *data) +{ + struct mxs_platform_fb_entry *pentry = data->pdata->cur; + + if (!data || !data->pdata || !data->pdata->cur) + return; + + if (pentry->blank_panel) + pentry->blank_panel(FB_BLANK_POWERDOWN); + + if (pentry->stop_panel) + pentry->stop_panel(); + pentry->release_panel(data->dev, pentry); +} + +static void set_controller_state(struct mxs_fb_data *data, u32 state) +{ + struct mxs_platform_fb_entry *pentry = data->pdata->cur; + struct fb_info *info = &data->info; + u32 old_state; + + mutex_lock(&data->blank_mutex); + old_state = data->state; + pr_debug("%s, old_state %d, state %d\n", __func__, old_state, state); + + switch (state) { + case F_DISABLE: + /* + * Disable controller + */ + if (old_state != F_DISABLE) { + data->state = F_DISABLE; + mxsfb_disable_controller(data); + } + break; + + case F_REENABLE: + /* + * Re-enable the controller when panel changed. + */ + if (old_state == F_ENABLE) { + mxsfb_disable_controller(data); + + pentry = data->pdata->cur = data->pdata->next; + info->fix.smem_len = pentry->y_res * pentry->x_res * + pentry->bpp / 8; + info->screen_size = info->fix.smem_len; + memset((void *)info->screen_base, 0, info->screen_size); + + mxsfb_enable_controller(data); + + data->state = F_ENABLE; + } else if (old_state == F_DISABLE) { + pentry = data->pdata->cur = data->pdata->next; + info->fix.smem_len = pentry->y_res * pentry->x_res * + pentry->bpp / 8; + info->screen_size = info->fix.smem_len; + memset((void *)info->screen_base, 0, info->screen_size); + + data->state = F_DISABLE; + } + break; + + case F_ENABLE: + if (old_state != F_ENABLE) { + data->state = F_ENABLE; + mxsfb_enable_controller(data); + } + break; + } + mutex_unlock(&data->blank_mutex); + +} + +static void mxsfb_task(struct work_struct *work) +{ + struct mxs_fb_data *data = container_of(work, struct mxs_fb_data, work); + + u32 state = xchg(&data->task_state, -1); + pr_debug("%s: state = %d, data->task_state = %d\n", + __func__, state, data->task_state); + + set_controller_state(data, state); +} + +static void mxs_schedule_work(struct mxs_fb_data *data, u32 state) +{ + unsigned long flags; + + local_irq_save(flags); + + data->task_state = state; + schedule_work(&data->work); + + local_irq_restore(flags); +} + +static irqreturn_t lcd_irq_handler(int irq, void *dev_id) +{ + struct mxs_fb_data *data = dev_id; + u32 status_lcd = __raw_readl(data->regbase + HW_LCDIF_CTRL1); + pr_debug("%s: irq %d\n", __func__, irq); + + if (status_lcd & BM_LCDIF_CTRL1_VSYNC_EDGE_IRQ) { + pr_debug("%s: VSYNC irq\n", __func__); + data->vsync_count++; + __raw_writel(BM_LCDIF_CTRL1_VSYNC_EDGE_IRQ, + data->regbase + HW_LCDIF_CTRL1_CLR); + wake_up_interruptible(&data->vsync_wait_q); + } + if (status_lcd & BM_LCDIF_CTRL1_CUR_FRAME_DONE_IRQ) { + pr_debug("%s: frame done irq\n", __func__); + __raw_writel(BM_LCDIF_CTRL1_CUR_FRAME_DONE_IRQ, + data->regbase + HW_LCDIF_CTRL1_CLR); + data->vsync_count++; + } + if (status_lcd & BM_LCDIF_CTRL1_UNDERFLOW_IRQ) { + pr_debug("%s: underflow irq\n", __func__); + __raw_writel(BM_LCDIF_CTRL1_UNDERFLOW_IRQ, + data->regbase + HW_LCDIF_CTRL1_CLR); + } + if (status_lcd & BM_LCDIF_CTRL1_OVERFLOW_IRQ) { + pr_debug("%s: overflow irq\n", __func__); + __raw_writel(BM_LCDIF_CTRL1_OVERFLOW_IRQ, + data->regbase + HW_LCDIF_CTRL1_CLR); + } + return IRQ_HANDLED; +} + +static struct fb_var_screeninfo mxsfb_default __devinitdata = { + .activate = FB_ACTIVATE_TEST, + .height = -1, + .width = -1, + .pixclock = 20000, + .left_margin = 64, + .right_margin = 64, + .upper_margin = 32, + .lower_margin = 32, + .hsync_len = 64, + .vsync_len = 2, + .vmode = FB_VMODE_NONINTERLACED, +}; + +static struct fb_fix_screeninfo mxsfb_fix __devinitdata = { + .id = "mxsfb", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .xpanstep = 0, + .ypanstep = 0, + .ywrapstep = 0, + .accel = FB_ACCEL_NONE, +}; + +int mxsfb_get_info(struct fb_var_screeninfo *var, struct fb_fix_screeninfo *fix) +{ + if (!cdata) + return -ENODEV; + + *var = cdata->info.var; + *fix = cdata->info.fix; + return 0; +} + +void mxsfb_cfg_pxp(int enable, dma_addr_t pxp_phys) +{ + if (enable) + cdata->pdata->cur->pan_display(pxp_phys); + else + cdata->pdata->cur->pan_display(cdata->cur_phys); +} + +static int mxsfb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + struct mxs_fb_data *data = (struct mxs_fb_data *)info; + + unsigned long off = vma->vm_pgoff << PAGE_SHIFT; + + if (off < info->fix.smem_len) + return dma_mmap_writecombine(data->dev, vma, + data->virt_start, + data->phys_start, + info->fix.smem_len); + else + return -EINVAL; +} + +static int mxsfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info) +{ + if (regno >= 256) /* no. of hw registers */ + return 1; + /* + * Program hardware... do anything you want with transp + */ + + /* grayscale works only partially under directcolor */ + if (info->var.grayscale) { + /* grayscale = 0.30*R + 0.59*G + 0.11*B */ + red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; + } + + /* Directcolor: + * var->{color}.offset contains start of bitfield + * var->{color}.length contains length of bitfield + * {hardwarespecific} contains width of RAMDAC + * cmap[X] is programmed to + * (X << red.offset) | (X << green.offset) | (X << blue.offset) + * RAMDAC[X] is programmed to (red, green, blue) + * + * Pseudocolor: + * uses offset = 0 && length = RAMDAC register width. + * var->{color}.offset is 0 + * var->{color}.length contains widht of DAC + * cmap is not used + * RAMDAC[X] is programmed to (red, green, blue) + * Truecolor: + * does not use DAC. Usually 3 are present. + * var->{color}.offset contains start of bitfield + * var->{color}.length contains length of bitfield + * cmap is programmed to + * (red << red.offset) | (green << green.offset) | + * (blue << blue.offset) | (transp << transp.offset) + * RAMDAC does not exist + */ +#define CNVT_TOHW(val, width) ((((val)<<(width))+0x7FFF-(val))>>16) + switch (info->fix.visual) { + case FB_VISUAL_TRUECOLOR: + case FB_VISUAL_PSEUDOCOLOR: + red = CNVT_TOHW(red, info->var.red.length); + green = CNVT_TOHW(green, info->var.green.length); + blue = CNVT_TOHW(blue, info->var.blue.length); + transp = CNVT_TOHW(transp, info->var.transp.length); + break; + case FB_VISUAL_DIRECTCOLOR: + red = CNVT_TOHW(red, 8); /* expect 8 bit DAC */ + green = CNVT_TOHW(green, 8); + blue = CNVT_TOHW(blue, 8); + /* hey, there is bug in transp handling... */ + transp = CNVT_TOHW(transp, 8); + break; + } +#undef CNVT_TOHW + /* Truecolor has hardware independent palette */ + if (info->fix.visual == FB_VISUAL_TRUECOLOR) { + + if (regno >= 16) + return 1; + + ((u32 *) (info->pseudo_palette))[regno] = + (red << info->var.red.offset) | + (green << info->var.green.offset) | + (blue << info->var.blue.offset) | + (transp << info->var.transp.offset); + } + return 0; +} + +static inline u_long get_line_length(int xres_virtual, int bpp) +{ + u_long length; + + length = xres_virtual * bpp; + length = (length + 31) & ~31; + length >>= 3; + return length; +} + +static int get_matching_pentry(struct mxs_platform_fb_entry *pentry, + void *data, int ret_prev) +{ + struct fb_var_screeninfo *info = data; + pr_debug("%s: %d:%d:%d vs %d:%d:%d\n", __func__, + pentry->x_res, pentry->y_res, pentry->bpp, + info->yres, info->xres, info->bits_per_pixel); + if (pentry->x_res == info->yres && pentry->y_res == info->xres && + pentry->bpp == info->bits_per_pixel) + ret_prev = (int)pentry; + return ret_prev; +} + +static int get_matching_pentry_by_name(struct mxs_platform_fb_entry *pentry, + void *data, int ret_prev) +{ + unsigned char *name = data; + if (!strcmp(pentry->name, name)) + ret_prev = (int)pentry; + return ret_prev; +} + +/* + * This routine actually sets the video mode. It's in here where we + * the hardware state info->par and fix which can be affected by the + * change in par. For this driver it doesn't do much. + * + * XXX: REVISIT + */ +static int mxsfb_set_par(struct fb_info *info) +{ + struct mxs_fb_data *data = (struct mxs_fb_data *)info; + struct mxs_platform_fb_data *pdata = data->pdata; + struct mxs_platform_fb_entry *pentry; + pentry = (void *)mxs_lcd_iterate_pdata(pdata, + get_matching_pentry, &info->var); + + dev_dbg(data->dev, "%s: xres %d, yres %d, bpp %d\n", + __func__, + info->var.xres, info->var.yres, info->var.bits_per_pixel); + if (!pentry) + return -EINVAL; + + info->fix.line_length = get_line_length(info->var.xres_virtual, + info->var.bits_per_pixel); + + if (pentry == pdata->cur || !pdata->cur) + return 0; + + /* init next panel */ + pdata->next = pentry; + + set_controller_state(data, F_REENABLE); + + return 0; +} + +static int mxsfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + u32 line_length; + struct mxs_fb_data *data = (struct mxs_fb_data *)info; + struct mxs_platform_fb_data *pdata = data->pdata; + + /* + * FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal! + * as FB_VMODE_SMOOTH_XPAN is only used internally + */ + + if (var->vmode & FB_VMODE_CONUPDATE) { + var->vmode |= FB_VMODE_YWRAP; + var->xoffset = info->var.xoffset; + var->yoffset = info->var.yoffset; + } + + pr_debug("%s: xres %d, yres %d, bpp %d\n", __func__, + var->xres, var->yres, var->bits_per_pixel); + /* + * Some very basic checks + */ + if (!var->xres) + var->xres = 1; + if (!var->yres) + var->yres = 1; + if (var->xres > var->xres_virtual) + var->xres_virtual = var->xres; + if (var->yres > var->yres_virtual) + var->yres_virtual = var->yres; + + if (var->xres_virtual < var->xoffset + var->xres) + var->xres_virtual = var->xoffset + var->xres; + if (var->yres_virtual < var->yoffset + var->yres) + var->yres_virtual = var->yoffset + var->yres; + + line_length = get_line_length(var->xres_virtual, var->bits_per_pixel); + dev_dbg(data->dev, + "line_length %d, var->yres_virtual %d, data->mem_size %d\n", + line_length, var->yres_virtual, data->mem_size); + if (line_length * var->yres_virtual > data->map_size) + return -ENOMEM; + + if (!mxs_lcd_iterate_pdata(pdata, get_matching_pentry, var)) + return -EINVAL; + + if (var->bits_per_pixel == 16) { + /* RGBA 5551 */ + if (var->transp.length) { + var->red.offset = 0; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 5; + var->blue.offset = 10; + var->blue.length = 5; + var->transp.offset = 15; + var->transp.length = 1; + } else { /* RGB 565 */ + var->red.offset = 0; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 11; + var->blue.length = 5; + var->transp.offset = 0; + var->transp.length = 0; + } + } else { + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + } + + var->red.msb_right = 0; + var->green.msb_right = 0; + var->blue.msb_right = 0; + var->transp.msb_right = 0; + + return 0; +} + +static int mxsfb_wait_for_vsync(u32 channel, struct fb_info *info) +{ + struct mxs_fb_data *data = (struct mxs_fb_data *)info; + u32 count = data->vsync_count; + int ret = 0; + + __raw_writel(BM_LCDIF_CTRL1_VSYNC_EDGE_IRQ_EN, + data->regbase + HW_LCDIF_CTRL1_SET); + ret = wait_event_interruptible_timeout(data->vsync_wait_q, + count != data->vsync_count, + HZ / 10); + __raw_writel(BM_LCDIF_CTRL1_VSYNC_EDGE_IRQ_EN, + data->regbase + HW_LCDIF_CTRL1_CLR); + if (!ret) { + dev_err(data->dev, "wait for vsync timed out\n"); + ret = -ETIMEDOUT; + } + return ret; +} + +static int mxsfb_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + u32 channel = 0; + int ret = -EINVAL; + + switch (cmd) { + case FBIO_WAITFORVSYNC: + if (!get_user(channel, (__u32 __user *) arg)) + ret = mxsfb_wait_for_vsync(channel, info); + break; + default: + break; + } + return ret; +} + +static int mxsfb_blank(int blank, struct fb_info *info) +{ + struct mxs_fb_data *data = (struct mxs_fb_data *)info; + int ret = 0; + + switch (blank) { + case FB_BLANK_NORMAL: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_POWERDOWN: + pr_debug("%s: FB_BLANK_POWERDOWN\n", __func__); + mxs_schedule_work(data, F_DISABLE); + break; + + case FB_BLANK_UNBLANK: + pr_debug("%s: FB_BLANK_UNBLANK\n", __func__); + mxs_schedule_work(data, F_ENABLE); + break; + + default: + ret = -EINVAL; + } + return ret; +} + +static int mxsfb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct mxs_fb_data *data = (struct mxs_fb_data *)info; + int ret = 0; + + pr_debug("%s: var->xoffset %d, info->var.xoffset %d\n", + __func__, var->xoffset, info->var.xoffset); + /* check if var is valid; also, xpan is not supported */ + if (!var || (var->xoffset != info->var.xoffset) || + (var->yoffset + var->yres > var->yres_virtual)) { + ret = -EINVAL; + goto out; + } + + if (!data->pdata->cur->pan_display) { + ret = -EINVAL; + goto out; + } + + /* update framebuffer visual */ + data->cur_phys = data->phys_start + + info->fix.line_length * var->yoffset; + data->pdata->cur->pan_display(data->cur_phys); +out: + return ret; +} + +static struct fb_ops mxsfb_ops = { + .owner = THIS_MODULE, + .fb_check_var = mxsfb_check_var, + .fb_set_par = mxsfb_set_par, + .fb_mmap = mxsfb_mmap, + .fb_setcolreg = mxsfb_setcolreg, + .fb_ioctl = mxsfb_ioctl, + .fb_blank = mxsfb_blank, + .fb_pan_display = mxsfb_pan_display, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +static void init_timings(struct mxs_fb_data *data) +{ + unsigned phase_time; + unsigned timings; + + /* Just use a phase_time of 1. As optimal as it gets, now. */ + phase_time = 1; + + /* Program all 4 timings the same */ + timings = phase_time; + timings |= timings << 8; + timings |= timings << 16; + __raw_writel(timings, data->regbase + HW_LCDIF_TIMING); +} + +#ifdef CONFIG_CPU_FREQ + +struct mxsfb_notifier_block { + struct mxs_fb_data *fb_data; + struct notifier_block nb; +}; + +static int mxsfb_notifier(struct notifier_block *self, + unsigned long phase, void *p) +{ + struct mxsfb_notifier_block *block = + container_of(self, struct mxsfb_notifier_block, nb); + struct mxs_fb_data *data = block->fb_data; + struct mxs_platform_fb_entry *pentry = data->pdata->cur; + u32 old_state = data->state; + + if (!data || !data->pdata || !data->pdata->cur) + return NOTIFY_BAD; + + /* REVISIT */ + switch (phase) { + case CPUFREQ_PRECHANGE: + if (old_state == F_ENABLE) + if (pentry->blank_panel) + pentry->blank_panel(FB_BLANK_POWERDOWN); + break; + + case CPUFREQ_POSTCHANGE: + if (old_state == F_ENABLE) + if (pentry->blank_panel) + pentry->blank_panel(FB_BLANK_UNBLANK); + break; + + default: + dev_dbg(data->dev, "didn't handle notify %ld\n", phase); + } + + return NOTIFY_DONE; +} + +static struct mxsfb_notifier_block mxsfb_nb = { + .nb = { + .notifier_call = mxsfb_notifier, + }, +}; +#endif /* CONFIG_CPU_FREQ */ + +static int get_max_memsize(struct mxs_platform_fb_entry *pentry, + void *data, int ret_prev) +{ + struct mxs_fb_data *fbdata = data; + int sz = pentry->x_res * pentry->y_res * pentry->bpp / 8; + fbdata->mem_size = sz < ret_prev ? ret_prev : sz; + pr_debug("%s: mem_size now %d\n", __func__, fbdata->mem_size); + return fbdata->mem_size; +} + +static int __devinit mxsfb_probe(struct platform_device *pdev) +{ + int ret = 0; + struct mxs_fb_data *data; + struct resource *res; + struct fb_info *info; + struct mxs_platform_fb_data *pdata = pdev->dev.platform_data; + struct mxs_platform_fb_entry *pentry = NULL; + + if (pdata == NULL) { + ret = -ENODEV; + goto out; + } + + if (default_panel_name) { + pentry = (void *)mxs_lcd_iterate_pdata(pdata, + get_matching_pentry_by_name, + default_panel_name); + if (pentry) { + mxs_lcd_move_pentry_up(pentry, pdata); + pdata->cur = pentry; + } + } + if (!default_panel_name || !pentry) + pentry = pdata->cur; + if (!pentry || !pentry->init_panel || !pentry->run_panel || + !pentry->release_panel) { + ret = -EINVAL; + goto out; + } + + data = + (struct mxs_fb_data *)framebuffer_alloc(sizeof(struct mxs_fb_data) + + sizeof(u32) * 256 - + sizeof(struct fb_info), + &pdev->dev); + if (data == NULL) { + ret = -ENOMEM; + goto out; + } + + cdata = data; + data->dev = &pdev->dev; + data->pdata = pdata; + platform_set_drvdata(pdev, data); + info = &data->info; + + dev_dbg(&pdev->dev, "resolution %dx%d, bpp %d\n", pentry->x_res, + pentry->y_res, pentry->bpp); + + mxs_lcd_iterate_pdata(pdata, get_max_memsize, data); + + data->map_size = PAGE_ALIGN(data->mem_size) * NUM_SCREENS; + dev_dbg(&pdev->dev, "memory to allocate: %d\n", data->map_size); + + data->virt_start = dma_alloc_writecombine(&pdev->dev, + data->map_size, + &data->phys_start, + GFP_KERNEL); + + if (data->virt_start == NULL) { + ret = -ENOMEM; + goto out_dma; + } + dev_dbg(&pdev->dev, "allocated at %p:0x%x\n", data->virt_start, + data->phys_start); + mutex_init(&data->blank_mutex); + INIT_WORK(&data->work, mxsfb_task); + data->state = F_ENABLE; + + mxsfb_default.bits_per_pixel = pentry->bpp; + /* NB: rotated */ + mxsfb_default.xres = pentry->y_res; + mxsfb_default.yres = pentry->x_res; + mxsfb_default.xres_virtual = pentry->y_res; + mxsfb_default.yres_virtual = data->map_size / + (pentry->y_res * pentry->bpp / 8); + if (mxsfb_default.yres_virtual >= mxsfb_default.yres * 2) + mxsfb_default.yres_virtual = mxsfb_default.yres * 2; + else + mxsfb_default.yres_virtual = mxsfb_default.yres; + + mxsfb_fix.smem_start = data->phys_start; + mxsfb_fix.smem_len = pentry->y_res * pentry->x_res * pentry->bpp / 8; + mxsfb_fix.ypanstep = 1; + + switch (pentry->bpp) { + case 32: + case 24: + mxsfb_default.red.offset = 16; + mxsfb_default.red.length = 8; + mxsfb_default.green.offset = 8; + mxsfb_default.green.length = 8; + mxsfb_default.blue.offset = 0; + mxsfb_default.blue.length = 8; + break; + + case 16: + mxsfb_default.red.offset = 11; + mxsfb_default.red.length = 5; + mxsfb_default.green.offset = 5; + mxsfb_default.green.length = 6; + mxsfb_default.blue.offset = 0; + mxsfb_default.blue.length = 5; + break; + + default: + dev_err(&pdev->dev, "unsupported bitwidth %d\n", pentry->bpp); + ret = -EINVAL; + goto out_dma; + } + + info->screen_base = data->virt_start; + info->fbops = &mxsfb_ops; + info->var = mxsfb_default; + info->fix = mxsfb_fix; + info->pseudo_palette = &data->par; + data->par = NULL; + info->flags = FBINFO_FLAG_DEFAULT; + + init_waitqueue_head(&data->vsync_wait_q); + data->vsync_count = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "cannot get IRQ resource\n"); + ret = -ENODEV; + goto out_dma; + } + data->regbase = (unsigned long)IO_ADDRESS(res->start); + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res == NULL) { + dev_err(&pdev->dev, "cannot get IRQ resource\n"); + ret = -ENODEV; + goto out_dma; + } + data->irq = res->start; + + mxsfb_check_var(&info->var, info); + + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret) + goto out_cmap; + + mxsfb_set_par(info); + + mxs_init_lcdif(); + ret = pentry->init_panel(data->dev, data->phys_start, + mxsfb_fix.smem_len, pentry); + if (ret) { + dev_err(&pdev->dev, "cannot initialize LCD panel\n"); + goto out_panel; + } + dev_dbg(&pdev->dev, "LCD panel initialized\n"); + init_timings(data); + + ret = request_irq(data->irq, lcd_irq_handler, 0, "fb_irq", data); + if (ret) { + dev_err(&pdev->dev, "request_irq (%d) failed with error %d\n", + data->irq, ret); + goto out_panel; + } + ret = register_framebuffer(info); + if (ret) + goto out_irq; + + pentry->run_panel(); + /* REVISIT: temporary workaround for MX23EVK */ + mxsfb_disable_controller(data); + mxsfb_enable_controller(data); + data->cur_phys = data->phys_start; + dev_dbg(&pdev->dev, "LCD running now\n"); + +#ifdef CONFIG_CPU_FREQ + mxsfb_nb.fb_data = data; + cpufreq_register_notifier(&mxsfb_nb.nb, CPUFREQ_TRANSITION_NOTIFIER); +#endif /* CONFIG_CPU_FREQ */ + + goto out; + +out_irq: + free_irq(data->irq, data); +out_panel: + fb_dealloc_cmap(&info->cmap); +out_cmap: + dma_free_writecombine(&pdev->dev, data->map_size, data->virt_start, + data->phys_start); +out_dma: + kfree(data); +out: + return ret; +} + +static int mxsfb_remove(struct platform_device *pdev) +{ + struct mxs_fb_data *data = platform_get_drvdata(pdev); + + set_controller_state(data, F_DISABLE); + + unregister_framebuffer(&data->info); + framebuffer_release(&data->info); + fb_dealloc_cmap(&data->info.cmap); + free_irq(data->irq, data); + dma_free_writecombine(&pdev->dev, data->map_size, data->virt_start, + data->phys_start); + kfree(data); + platform_set_drvdata(pdev, NULL); + return 0; +} + +#ifdef CONFIG_PM +static int mxsfb_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct mxs_fb_data *data = platform_get_drvdata(pdev); + + set_controller_state(data, F_DISABLE); + + return 0; +} + +static int mxsfb_resume(struct platform_device *pdev) +{ + struct mxs_fb_data *data = platform_get_drvdata(pdev); + + set_controller_state(data, F_ENABLE); + return 0; +} +#else +#define mxsfb_suspend NULL +#define mxsfb_resume NULL +#endif + +static struct platform_driver mxsfb_driver = { + .probe = mxsfb_probe, + .remove = mxsfb_remove, + .suspend = mxsfb_suspend, + .resume = mxsfb_resume, + .driver = { + .name = "mxs-fb", + .owner = THIS_MODULE, + }, +}; + +static int __init mxsfb_init(void) +{ + return platform_driver_register(&mxsfb_driver); +} + +static void __exit mxsfb_exit(void) +{ + platform_driver_unregister(&mxsfb_driver); +} + +module_init(mxsfb_init); +module_exit(mxsfb_exit); + +/* + * LCD panel select + */ +static int __init default_panel_select(char *str) +{ + default_panel_name = str; + return 0; +} + +__setup("lcd_panel=", default_panel_select); + +MODULE_AUTHOR("Vitaly Wool <vital@embeddedalley.com>"); +MODULE_DESCRIPTION("MXS Framebuffer Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/mxs/regs-tvenc.h b/drivers/video/mxs/regs-tvenc.h new file mode 100644 index 000000000000..bd2493e2dee5 --- /dev/null +++ b/drivers/video/mxs/regs-tvenc.h @@ -0,0 +1,583 @@ +/* + * Freescale TVENC Register Definitions + * + * Copyright 2008-2010 Freescale Semiconductor, Inc. + * Copyright 2008 Embedded Alley Solutions, 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 + * + * This file is created by xml file. Don't Edit it. + * + * Xml Revision: 1.00 + * Template revision: 26195 + */ + +#ifndef __ARCH_ARM___TVENC_H +#define __ARCH_ARM___TVENC_H + + +#define HW_TVENC_CTRL (0x00000000) +#define HW_TVENC_CTRL_SET (0x00000004) +#define HW_TVENC_CTRL_CLR (0x00000008) +#define HW_TVENC_CTRL_TOG (0x0000000c) + +#define BM_TVENC_CTRL_SFTRST 0x80000000 +#define BM_TVENC_CTRL_CLKGATE 0x40000000 +#define BM_TVENC_CTRL_TVENC_MACROVISION_PRESENT 0x20000000 +#define BM_TVENC_CTRL_TVENC_COMPOSITE_PRESENT 0x10000000 +#define BM_TVENC_CTRL_TVENC_SVIDEO_PRESENT 0x08000000 +#define BM_TVENC_CTRL_TVENC_COMPONENT_PRESENT 0x04000000 +#define BP_TVENC_CTRL_RSRVD1 6 +#define BM_TVENC_CTRL_RSRVD1 0x03FFFFC0 +#define BF_TVENC_CTRL_RSRVD1(v) \ + (((v) << 6) & BM_TVENC_CTRL_RSRVD1) +#define BM_TVENC_CTRL_DAC_FIFO_NO_WRITE 0x00000020 +#define BM_TVENC_CTRL_DAC_FIFO_NO_READ 0x00000010 +#define BM_TVENC_CTRL_DAC_DATA_FIFO_RST 0x00000008 +#define BP_TVENC_CTRL_RSRVD2 1 +#define BM_TVENC_CTRL_RSRVD2 0x00000006 +#define BF_TVENC_CTRL_RSRVD2(v) \ + (((v) << 1) & BM_TVENC_CTRL_RSRVD2) +#define BM_TVENC_CTRL_DAC_MUX_MODE 0x00000001 + +#define HW_TVENC_CONFIG (0x00000010) +#define HW_TVENC_CONFIG_SET (0x00000014) +#define HW_TVENC_CONFIG_CLR (0x00000018) +#define HW_TVENC_CONFIG_TOG (0x0000001c) + +#define BP_TVENC_CONFIG_RSRVD5 28 +#define BM_TVENC_CONFIG_RSRVD5 0xF0000000 +#define BF_TVENC_CONFIG_RSRVD5(v) \ + (((v) << 28) & BM_TVENC_CONFIG_RSRVD5) +#define BM_TVENC_CONFIG_DEFAULT_PICFORM 0x08000000 +#define BP_TVENC_CONFIG_YDEL_ADJ 24 +#define BM_TVENC_CONFIG_YDEL_ADJ 0x07000000 +#define BF_TVENC_CONFIG_YDEL_ADJ(v) \ + (((v) << 24) & BM_TVENC_CONFIG_YDEL_ADJ) +#define BM_TVENC_CONFIG_RSRVD4 0x00800000 +#define BM_TVENC_CONFIG_RSRVD3 0x00400000 +#define BM_TVENC_CONFIG_ADD_YPBPR_PED 0x00200000 +#define BM_TVENC_CONFIG_PAL_SHAPE 0x00100000 +#define BM_TVENC_CONFIG_NO_PED 0x00080000 +#define BM_TVENC_CONFIG_COLOR_BAR_EN 0x00040000 +#define BP_TVENC_CONFIG_YGAIN_SEL 16 +#define BM_TVENC_CONFIG_YGAIN_SEL 0x00030000 +#define BF_TVENC_CONFIG_YGAIN_SEL(v) \ + (((v) << 16) & BM_TVENC_CONFIG_YGAIN_SEL) +#define BP_TVENC_CONFIG_CGAIN 14 +#define BM_TVENC_CONFIG_CGAIN 0x0000C000 +#define BF_TVENC_CONFIG_CGAIN(v) \ + (((v) << 14) & BM_TVENC_CONFIG_CGAIN) +#define BP_TVENC_CONFIG_CLK_PHS 12 +#define BM_TVENC_CONFIG_CLK_PHS 0x00003000 +#define BF_TVENC_CONFIG_CLK_PHS(v) \ + (((v) << 12) & BM_TVENC_CONFIG_CLK_PHS) +#define BM_TVENC_CONFIG_RSRVD2 0x00000800 +#define BM_TVENC_CONFIG_FSYNC_ENBL 0x00000400 +#define BM_TVENC_CONFIG_FSYNC_PHS 0x00000200 +#define BM_TVENC_CONFIG_HSYNC_PHS 0x00000100 +#define BM_TVENC_CONFIG_VSYNC_PHS 0x00000080 +#define BP_TVENC_CONFIG_SYNC_MODE 4 +#define BM_TVENC_CONFIG_SYNC_MODE 0x00000070 +#define BF_TVENC_CONFIG_SYNC_MODE(v) \ + (((v) << 4) & BM_TVENC_CONFIG_SYNC_MODE) +#define BM_TVENC_CONFIG_RSRVD1 0x00000008 +#define BP_TVENC_CONFIG_ENCD_MODE 0 +#define BM_TVENC_CONFIG_ENCD_MODE 0x00000007 +#define BF_TVENC_CONFIG_ENCD_MODE(v) \ + (((v) << 0) & BM_TVENC_CONFIG_ENCD_MODE) + +#define HW_TVENC_FILTCTRL (0x00000020) +#define HW_TVENC_FILTCTRL_SET (0x00000024) +#define HW_TVENC_FILTCTRL_CLR (0x00000028) +#define HW_TVENC_FILTCTRL_TOG (0x0000002c) + +#define BP_TVENC_FILTCTRL_RSRVD1 20 +#define BM_TVENC_FILTCTRL_RSRVD1 0xFFF00000 +#define BF_TVENC_FILTCTRL_RSRVD1(v) \ + (((v) << 20) & BM_TVENC_FILTCTRL_RSRVD1) +#define BM_TVENC_FILTCTRL_YSHARP_BW 0x00080000 +#define BM_TVENC_FILTCTRL_YD_OFFSETSEL 0x00040000 +#define BM_TVENC_FILTCTRL_SEL_YLPF 0x00020000 +#define BM_TVENC_FILTCTRL_SEL_CLPF 0x00010000 +#define BM_TVENC_FILTCTRL_SEL_YSHARP 0x00008000 +#define BM_TVENC_FILTCTRL_YLPF_COEFSEL 0x00004000 +#define BM_TVENC_FILTCTRL_COEFSEL_CLPF 0x00002000 +#define BM_TVENC_FILTCTRL_YS_GAINSGN 0x00001000 +#define BP_TVENC_FILTCTRL_YS_GAINSEL 10 +#define BM_TVENC_FILTCTRL_YS_GAINSEL 0x00000C00 +#define BF_TVENC_FILTCTRL_YS_GAINSEL(v) \ + (((v) << 10) & BM_TVENC_FILTCTRL_YS_GAINSEL) +#define BM_TVENC_FILTCTRL_RSRVD2 0x00000200 +#define BM_TVENC_FILTCTRL_RSRVD3 0x00000100 +#define BP_TVENC_FILTCTRL_RSRVD4 0 +#define BM_TVENC_FILTCTRL_RSRVD4 0x000000FF +#define BF_TVENC_FILTCTRL_RSRVD4(v) \ + (((v) << 0) & BM_TVENC_FILTCTRL_RSRVD4) + +#define HW_TVENC_SYNCOFFSET (0x00000030) +#define HW_TVENC_SYNCOFFSET_SET (0x00000034) +#define HW_TVENC_SYNCOFFSET_CLR (0x00000038) +#define HW_TVENC_SYNCOFFSET_TOG (0x0000003c) + +#define BM_TVENC_SYNCOFFSET_RSRVD1 0x80000000 +#define BP_TVENC_SYNCOFFSET_HSO 20 +#define BM_TVENC_SYNCOFFSET_HSO 0x7FF00000 +#define BF_TVENC_SYNCOFFSET_HSO(v) \ + (((v) << 20) & BM_TVENC_SYNCOFFSET_HSO) +#define BP_TVENC_SYNCOFFSET_VSO 10 +#define BM_TVENC_SYNCOFFSET_VSO 0x000FFC00 +#define BF_TVENC_SYNCOFFSET_VSO(v) \ + (((v) << 10) & BM_TVENC_SYNCOFFSET_VSO) +#define BP_TVENC_SYNCOFFSET_HLC 0 +#define BM_TVENC_SYNCOFFSET_HLC 0x000003FF +#define BF_TVENC_SYNCOFFSET_HLC(v) \ + (((v) << 0) & BM_TVENC_SYNCOFFSET_HLC) + +#define HW_TVENC_HTIMINGSYNC0 (0x00000040) +#define HW_TVENC_HTIMINGSYNC0_SET (0x00000044) +#define HW_TVENC_HTIMINGSYNC0_CLR (0x00000048) +#define HW_TVENC_HTIMINGSYNC0_TOG (0x0000004c) + +#define BP_TVENC_HTIMINGSYNC0_RSRVD2 26 +#define BM_TVENC_HTIMINGSYNC0_RSRVD2 0xFC000000 +#define BF_TVENC_HTIMINGSYNC0_RSRVD2(v) \ + (((v) << 26) & BM_TVENC_HTIMINGSYNC0_RSRVD2) +#define BP_TVENC_HTIMINGSYNC0_SYNC_END 16 +#define BM_TVENC_HTIMINGSYNC0_SYNC_END 0x03FF0000 +#define BF_TVENC_HTIMINGSYNC0_SYNC_END(v) \ + (((v) << 16) & BM_TVENC_HTIMINGSYNC0_SYNC_END) +#define BP_TVENC_HTIMINGSYNC0_RSRVD1 10 +#define BM_TVENC_HTIMINGSYNC0_RSRVD1 0x0000FC00 +#define BF_TVENC_HTIMINGSYNC0_RSRVD1(v) \ + (((v) << 10) & BM_TVENC_HTIMINGSYNC0_RSRVD1) +#define BP_TVENC_HTIMINGSYNC0_SYNC_STRT 0 +#define BM_TVENC_HTIMINGSYNC0_SYNC_STRT 0x000003FF +#define BF_TVENC_HTIMINGSYNC0_SYNC_STRT(v) \ + (((v) << 0) & BM_TVENC_HTIMINGSYNC0_SYNC_STRT) + +#define HW_TVENC_HTIMINGSYNC1 (0x00000050) +#define HW_TVENC_HTIMINGSYNC1_SET (0x00000054) +#define HW_TVENC_HTIMINGSYNC1_CLR (0x00000058) +#define HW_TVENC_HTIMINGSYNC1_TOG (0x0000005c) + +#define BP_TVENC_HTIMINGSYNC1_RSRVD2 26 +#define BM_TVENC_HTIMINGSYNC1_RSRVD2 0xFC000000 +#define BF_TVENC_HTIMINGSYNC1_RSRVD2(v) \ + (((v) << 26) & BM_TVENC_HTIMINGSYNC1_RSRVD2) +#define BP_TVENC_HTIMINGSYNC1_SYNC_EQEND 16 +#define BM_TVENC_HTIMINGSYNC1_SYNC_EQEND 0x03FF0000 +#define BF_TVENC_HTIMINGSYNC1_SYNC_EQEND(v) \ + (((v) << 16) & BM_TVENC_HTIMINGSYNC1_SYNC_EQEND) +#define BP_TVENC_HTIMINGSYNC1_RSRVD1 10 +#define BM_TVENC_HTIMINGSYNC1_RSRVD1 0x0000FC00 +#define BF_TVENC_HTIMINGSYNC1_RSRVD1(v) \ + (((v) << 10) & BM_TVENC_HTIMINGSYNC1_RSRVD1) +#define BP_TVENC_HTIMINGSYNC1_SYNC_SREND 0 +#define BM_TVENC_HTIMINGSYNC1_SYNC_SREND 0x000003FF +#define BF_TVENC_HTIMINGSYNC1_SYNC_SREND(v) \ + (((v) << 0) & BM_TVENC_HTIMINGSYNC1_SYNC_SREND) + +#define HW_TVENC_HTIMINGACTIVE (0x00000060) +#define HW_TVENC_HTIMINGACTIVE_SET (0x00000064) +#define HW_TVENC_HTIMINGACTIVE_CLR (0x00000068) +#define HW_TVENC_HTIMINGACTIVE_TOG (0x0000006c) + +#define BP_TVENC_HTIMINGACTIVE_RSRVD2 26 +#define BM_TVENC_HTIMINGACTIVE_RSRVD2 0xFC000000 +#define BF_TVENC_HTIMINGACTIVE_RSRVD2(v) \ + (((v) << 26) & BM_TVENC_HTIMINGACTIVE_RSRVD2) +#define BP_TVENC_HTIMINGACTIVE_ACTV_END 16 +#define BM_TVENC_HTIMINGACTIVE_ACTV_END 0x03FF0000 +#define BF_TVENC_HTIMINGACTIVE_ACTV_END(v) \ + (((v) << 16) & BM_TVENC_HTIMINGACTIVE_ACTV_END) +#define BP_TVENC_HTIMINGACTIVE_RSRVD1 10 +#define BM_TVENC_HTIMINGACTIVE_RSRVD1 0x0000FC00 +#define BF_TVENC_HTIMINGACTIVE_RSRVD1(v) \ + (((v) << 10) & BM_TVENC_HTIMINGACTIVE_RSRVD1) +#define BP_TVENC_HTIMINGACTIVE_ACTV_STRT 0 +#define BM_TVENC_HTIMINGACTIVE_ACTV_STRT 0x000003FF +#define BF_TVENC_HTIMINGACTIVE_ACTV_STRT(v) \ + (((v) << 0) & BM_TVENC_HTIMINGACTIVE_ACTV_STRT) + +#define HW_TVENC_HTIMINGBURST0 (0x00000070) +#define HW_TVENC_HTIMINGBURST0_SET (0x00000074) +#define HW_TVENC_HTIMINGBURST0_CLR (0x00000078) +#define HW_TVENC_HTIMINGBURST0_TOG (0x0000007c) + +#define BP_TVENC_HTIMINGBURST0_RSRVD2 26 +#define BM_TVENC_HTIMINGBURST0_RSRVD2 0xFC000000 +#define BF_TVENC_HTIMINGBURST0_RSRVD2(v) \ + (((v) << 26) & BM_TVENC_HTIMINGBURST0_RSRVD2) +#define BP_TVENC_HTIMINGBURST0_WBRST_STRT 16 +#define BM_TVENC_HTIMINGBURST0_WBRST_STRT 0x03FF0000 +#define BF_TVENC_HTIMINGBURST0_WBRST_STRT(v) \ + (((v) << 16) & BM_TVENC_HTIMINGBURST0_WBRST_STRT) +#define BP_TVENC_HTIMINGBURST0_RSRVD1 10 +#define BM_TVENC_HTIMINGBURST0_RSRVD1 0x0000FC00 +#define BF_TVENC_HTIMINGBURST0_RSRVD1(v) \ + (((v) << 10) & BM_TVENC_HTIMINGBURST0_RSRVD1) +#define BP_TVENC_HTIMINGBURST0_NBRST_STRT 0 +#define BM_TVENC_HTIMINGBURST0_NBRST_STRT 0x000003FF +#define BF_TVENC_HTIMINGBURST0_NBRST_STRT(v) \ + (((v) << 0) & BM_TVENC_HTIMINGBURST0_NBRST_STRT) + +#define HW_TVENC_HTIMINGBURST1 (0x00000080) +#define HW_TVENC_HTIMINGBURST1_SET (0x00000084) +#define HW_TVENC_HTIMINGBURST1_CLR (0x00000088) +#define HW_TVENC_HTIMINGBURST1_TOG (0x0000008c) + +#define BP_TVENC_HTIMINGBURST1_RSRVD1 10 +#define BM_TVENC_HTIMINGBURST1_RSRVD1 0xFFFFFC00 +#define BF_TVENC_HTIMINGBURST1_RSRVD1(v) \ + (((v) << 10) & BM_TVENC_HTIMINGBURST1_RSRVD1) +#define BP_TVENC_HTIMINGBURST1_BRST_END 0 +#define BM_TVENC_HTIMINGBURST1_BRST_END 0x000003FF +#define BF_TVENC_HTIMINGBURST1_BRST_END(v) \ + (((v) << 0) & BM_TVENC_HTIMINGBURST1_BRST_END) + +#define HW_TVENC_VTIMING0 (0x00000090) +#define HW_TVENC_VTIMING0_SET (0x00000094) +#define HW_TVENC_VTIMING0_CLR (0x00000098) +#define HW_TVENC_VTIMING0_TOG (0x0000009c) + +#define BP_TVENC_VTIMING0_RSRVD3 26 +#define BM_TVENC_VTIMING0_RSRVD3 0xFC000000 +#define BF_TVENC_VTIMING0_RSRVD3(v) \ + (((v) << 26) & BM_TVENC_VTIMING0_RSRVD3) +#define BP_TVENC_VTIMING0_VSTRT_PREEQ 16 +#define BM_TVENC_VTIMING0_VSTRT_PREEQ 0x03FF0000 +#define BF_TVENC_VTIMING0_VSTRT_PREEQ(v) \ + (((v) << 16) & BM_TVENC_VTIMING0_VSTRT_PREEQ) +#define BP_TVENC_VTIMING0_RSRVD2 14 +#define BM_TVENC_VTIMING0_RSRVD2 0x0000C000 +#define BF_TVENC_VTIMING0_RSRVD2(v) \ + (((v) << 14) & BM_TVENC_VTIMING0_RSRVD2) +#define BP_TVENC_VTIMING0_VSTRT_ACTV 8 +#define BM_TVENC_VTIMING0_VSTRT_ACTV 0x00003F00 +#define BF_TVENC_VTIMING0_VSTRT_ACTV(v) \ + (((v) << 8) & BM_TVENC_VTIMING0_VSTRT_ACTV) +#define BP_TVENC_VTIMING0_RSRVD1 6 +#define BM_TVENC_VTIMING0_RSRVD1 0x000000C0 +#define BF_TVENC_VTIMING0_RSRVD1(v) \ + (((v) << 6) & BM_TVENC_VTIMING0_RSRVD1) +#define BP_TVENC_VTIMING0_VSTRT_SUBPH 0 +#define BM_TVENC_VTIMING0_VSTRT_SUBPH 0x0000003F +#define BF_TVENC_VTIMING0_VSTRT_SUBPH(v) \ + (((v) << 0) & BM_TVENC_VTIMING0_VSTRT_SUBPH) + +#define HW_TVENC_VTIMING1 (0x000000a0) +#define HW_TVENC_VTIMING1_SET (0x000000a4) +#define HW_TVENC_VTIMING1_CLR (0x000000a8) +#define HW_TVENC_VTIMING1_TOG (0x000000ac) + +#define BP_TVENC_VTIMING1_RSRVD3 30 +#define BM_TVENC_VTIMING1_RSRVD3 0xC0000000 +#define BF_TVENC_VTIMING1_RSRVD3(v) \ + (((v) << 30) & BM_TVENC_VTIMING1_RSRVD3) +#define BP_TVENC_VTIMING1_VSTRT_POSTEQ 24 +#define BM_TVENC_VTIMING1_VSTRT_POSTEQ 0x3F000000 +#define BF_TVENC_VTIMING1_VSTRT_POSTEQ(v) \ + (((v) << 24) & BM_TVENC_VTIMING1_VSTRT_POSTEQ) +#define BP_TVENC_VTIMING1_RSRVD2 22 +#define BM_TVENC_VTIMING1_RSRVD2 0x00C00000 +#define BF_TVENC_VTIMING1_RSRVD2(v) \ + (((v) << 22) & BM_TVENC_VTIMING1_RSRVD2) +#define BP_TVENC_VTIMING1_VSTRT_SERRA 16 +#define BM_TVENC_VTIMING1_VSTRT_SERRA 0x003F0000 +#define BF_TVENC_VTIMING1_VSTRT_SERRA(v) \ + (((v) << 16) & BM_TVENC_VTIMING1_VSTRT_SERRA) +#define BP_TVENC_VTIMING1_RSRVD1 10 +#define BM_TVENC_VTIMING1_RSRVD1 0x0000FC00 +#define BF_TVENC_VTIMING1_RSRVD1(v) \ + (((v) << 10) & BM_TVENC_VTIMING1_RSRVD1) +#define BP_TVENC_VTIMING1_LAST_FLD_LN 0 +#define BM_TVENC_VTIMING1_LAST_FLD_LN 0x000003FF +#define BF_TVENC_VTIMING1_LAST_FLD_LN(v) \ + (((v) << 0) & BM_TVENC_VTIMING1_LAST_FLD_LN) + +#define HW_TVENC_MISC (0x000000b0) +#define HW_TVENC_MISC_SET (0x000000b4) +#define HW_TVENC_MISC_CLR (0x000000b8) +#define HW_TVENC_MISC_TOG (0x000000bc) + +#define BP_TVENC_MISC_RSRVD3 25 +#define BM_TVENC_MISC_RSRVD3 0xFE000000 +#define BF_TVENC_MISC_RSRVD3(v) \ + (((v) << 25) & BM_TVENC_MISC_RSRVD3) +#define BP_TVENC_MISC_LPF_RST_OFF 16 +#define BM_TVENC_MISC_LPF_RST_OFF 0x01FF0000 +#define BF_TVENC_MISC_LPF_RST_OFF(v) \ + (((v) << 16) & BM_TVENC_MISC_LPF_RST_OFF) +#define BP_TVENC_MISC_RSRVD2 12 +#define BM_TVENC_MISC_RSRVD2 0x0000F000 +#define BF_TVENC_MISC_RSRVD2(v) \ + (((v) << 12) & BM_TVENC_MISC_RSRVD2) +#define BM_TVENC_MISC_NTSC_LN_CNT 0x00000800 +#define BM_TVENC_MISC_PAL_FSC_PHASE_ALT 0x00000400 +#define BP_TVENC_MISC_FSC_PHASE_RST 8 +#define BM_TVENC_MISC_FSC_PHASE_RST 0x00000300 +#define BF_TVENC_MISC_FSC_PHASE_RST(v) \ + (((v) << 8) & BM_TVENC_MISC_FSC_PHASE_RST) +#define BP_TVENC_MISC_BRUCHB 6 +#define BM_TVENC_MISC_BRUCHB 0x000000C0 +#define BF_TVENC_MISC_BRUCHB(v) \ + (((v) << 6) & BM_TVENC_MISC_BRUCHB) +#define BP_TVENC_MISC_AGC_LVL_CTRL 4 +#define BM_TVENC_MISC_AGC_LVL_CTRL 0x00000030 +#define BF_TVENC_MISC_AGC_LVL_CTRL(v) \ + (((v) << 4) & BM_TVENC_MISC_AGC_LVL_CTRL) +#define BM_TVENC_MISC_RSRVD1 0x00000008 +#define BM_TVENC_MISC_CS_INVERT_CTRL 0x00000004 +#define BP_TVENC_MISC_Y_BLANK_CTRL 0 +#define BM_TVENC_MISC_Y_BLANK_CTRL 0x00000003 +#define BF_TVENC_MISC_Y_BLANK_CTRL(v) \ + (((v) << 0) & BM_TVENC_MISC_Y_BLANK_CTRL) + +#define HW_TVENC_COLORSUB0 (0x000000c0) +#define HW_TVENC_COLORSUB0_SET (0x000000c4) +#define HW_TVENC_COLORSUB0_CLR (0x000000c8) +#define HW_TVENC_COLORSUB0_TOG (0x000000cc) + +#define BP_TVENC_COLORSUB0_PHASE_INC 0 +#define BM_TVENC_COLORSUB0_PHASE_INC 0xFFFFFFFF +#define BF_TVENC_COLORSUB0_PHASE_INC(v) (v) + +#define HW_TVENC_COLORSUB1 (0x000000d0) +#define HW_TVENC_COLORSUB1_SET (0x000000d4) +#define HW_TVENC_COLORSUB1_CLR (0x000000d8) +#define HW_TVENC_COLORSUB1_TOG (0x000000dc) + +#define BP_TVENC_COLORSUB1_PHASE_OFFSET 0 +#define BM_TVENC_COLORSUB1_PHASE_OFFSET 0xFFFFFFFF +#define BF_TVENC_COLORSUB1_PHASE_OFFSET(v) (v) + +#define HW_TVENC_COPYPROTECT (0x000000e0) +#define HW_TVENC_COPYPROTECT_SET (0x000000e4) +#define HW_TVENC_COPYPROTECT_CLR (0x000000e8) +#define HW_TVENC_COPYPROTECT_TOG (0x000000ec) + +#define BP_TVENC_COPYPROTECT_RSRVD1 16 +#define BM_TVENC_COPYPROTECT_RSRVD1 0xFFFF0000 +#define BF_TVENC_COPYPROTECT_RSRVD1(v) \ + (((v) << 16) & BM_TVENC_COPYPROTECT_RSRVD1) +#define BM_TVENC_COPYPROTECT_WSS_ENBL 0x00008000 +#define BM_TVENC_COPYPROTECT_CGMS_ENBL 0x00004000 +#define BP_TVENC_COPYPROTECT_WSS_CGMS_DATA 0 +#define BM_TVENC_COPYPROTECT_WSS_CGMS_DATA 0x00003FFF +#define BF_TVENC_COPYPROTECT_WSS_CGMS_DATA(v) \ + (((v) << 0) & BM_TVENC_COPYPROTECT_WSS_CGMS_DATA) + +#define HW_TVENC_CLOSEDCAPTION (0x000000f0) +#define HW_TVENC_CLOSEDCAPTION_SET (0x000000f4) +#define HW_TVENC_CLOSEDCAPTION_CLR (0x000000f8) +#define HW_TVENC_CLOSEDCAPTION_TOG (0x000000fc) + +#define BP_TVENC_CLOSEDCAPTION_RSRVD1 20 +#define BM_TVENC_CLOSEDCAPTION_RSRVD1 0xFFF00000 +#define BF_TVENC_CLOSEDCAPTION_RSRVD1(v) \ + (((v) << 20) & BM_TVENC_CLOSEDCAPTION_RSRVD1) +#define BP_TVENC_CLOSEDCAPTION_CC_ENBL 18 +#define BM_TVENC_CLOSEDCAPTION_CC_ENBL 0x000C0000 +#define BF_TVENC_CLOSEDCAPTION_CC_ENBL(v) \ + (((v) << 18) & BM_TVENC_CLOSEDCAPTION_CC_ENBL) +#define BP_TVENC_CLOSEDCAPTION_CC_FILL 16 +#define BM_TVENC_CLOSEDCAPTION_CC_FILL 0x00030000 +#define BF_TVENC_CLOSEDCAPTION_CC_FILL(v) \ + (((v) << 16) & BM_TVENC_CLOSEDCAPTION_CC_FILL) +#define BP_TVENC_CLOSEDCAPTION_CC_DATA 0 +#define BM_TVENC_CLOSEDCAPTION_CC_DATA 0x0000FFFF +#define BF_TVENC_CLOSEDCAPTION_CC_DATA(v) \ + (((v) << 0) & BM_TVENC_CLOSEDCAPTION_CC_DATA) + +#define HW_TVENC_COLORBURST (0x00000140) +#define HW_TVENC_COLORBURST_SET (0x00000144) +#define HW_TVENC_COLORBURST_CLR (0x00000148) +#define HW_TVENC_COLORBURST_TOG (0x0000014c) + +#define BP_TVENC_COLORBURST_NBA 24 +#define BM_TVENC_COLORBURST_NBA 0xFF000000 +#define BF_TVENC_COLORBURST_NBA(v) \ + (((v) << 24) & BM_TVENC_COLORBURST_NBA) +#define BP_TVENC_COLORBURST_PBA 16 +#define BM_TVENC_COLORBURST_PBA 0x00FF0000 +#define BF_TVENC_COLORBURST_PBA(v) \ + (((v) << 16) & BM_TVENC_COLORBURST_PBA) +#define BP_TVENC_COLORBURST_RSRVD1 12 +#define BM_TVENC_COLORBURST_RSRVD1 0x0000F000 +#define BF_TVENC_COLORBURST_RSRVD1(v) \ + (((v) << 12) & BM_TVENC_COLORBURST_RSRVD1) +#define BP_TVENC_COLORBURST_RSRVD2 0 +#define BM_TVENC_COLORBURST_RSRVD2 0x00000FFF +#define BF_TVENC_COLORBURST_RSRVD2(v) \ + (((v) << 0) & BM_TVENC_COLORBURST_RSRVD2) + +#define HW_TVENC_MACROVISION0 (0x00000150) +#define HW_TVENC_MACROVISION0_SET (0x00000154) +#define HW_TVENC_MACROVISION0_CLR (0x00000158) +#define HW_TVENC_MACROVISION0_TOG (0x0000015c) + +#define BP_TVENC_MACROVISION0_DATA 0 +#define BM_TVENC_MACROVISION0_DATA 0xFFFFFFFF +#define BF_TVENC_MACROVISION0_DATA(v) (v) + +#define HW_TVENC_MACROVISION1 (0x00000160) +#define HW_TVENC_MACROVISION1_SET (0x00000164) +#define HW_TVENC_MACROVISION1_CLR (0x00000168) +#define HW_TVENC_MACROVISION1_TOG (0x0000016c) + +#define BP_TVENC_MACROVISION1_DATA 0 +#define BM_TVENC_MACROVISION1_DATA 0xFFFFFFFF +#define BF_TVENC_MACROVISION1_DATA(v) (v) + +#define HW_TVENC_MACROVISION2 (0x00000170) +#define HW_TVENC_MACROVISION2_SET (0x00000174) +#define HW_TVENC_MACROVISION2_CLR (0x00000178) +#define HW_TVENC_MACROVISION2_TOG (0x0000017c) + +#define BP_TVENC_MACROVISION2_DATA 0 +#define BM_TVENC_MACROVISION2_DATA 0xFFFFFFFF +#define BF_TVENC_MACROVISION2_DATA(v) (v) + +#define HW_TVENC_MACROVISION3 (0x00000180) +#define HW_TVENC_MACROVISION3_SET (0x00000184) +#define HW_TVENC_MACROVISION3_CLR (0x00000188) +#define HW_TVENC_MACROVISION3_TOG (0x0000018c) + +#define BP_TVENC_MACROVISION3_DATA 0 +#define BM_TVENC_MACROVISION3_DATA 0xFFFFFFFF +#define BF_TVENC_MACROVISION3_DATA(v) (v) + +#define HW_TVENC_MACROVISION4 (0x00000190) +#define HW_TVENC_MACROVISION4_SET (0x00000194) +#define HW_TVENC_MACROVISION4_CLR (0x00000198) +#define HW_TVENC_MACROVISION4_TOG (0x0000019c) + +#define BP_TVENC_MACROVISION4_RSRVD2 24 +#define BM_TVENC_MACROVISION4_RSRVD2 0xFF000000 +#define BF_TVENC_MACROVISION4_RSRVD2(v) \ + (((v) << 24) & BM_TVENC_MACROVISION4_RSRVD2) +#define BP_TVENC_MACROVISION4_MACV_TST 16 +#define BM_TVENC_MACROVISION4_MACV_TST 0x00FF0000 +#define BF_TVENC_MACROVISION4_MACV_TST(v) \ + (((v) << 16) & BM_TVENC_MACROVISION4_MACV_TST) +#define BP_TVENC_MACROVISION4_RSRVD1 11 +#define BM_TVENC_MACROVISION4_RSRVD1 0x0000F800 +#define BF_TVENC_MACROVISION4_RSRVD1(v) \ + (((v) << 11) & BM_TVENC_MACROVISION4_RSRVD1) +#define BP_TVENC_MACROVISION4_DATA 0 +#define BM_TVENC_MACROVISION4_DATA 0x000007FF +#define BF_TVENC_MACROVISION4_DATA(v) \ + (((v) << 0) & BM_TVENC_MACROVISION4_DATA) + +#define HW_TVENC_DACCTRL (0x000001a0) +#define HW_TVENC_DACCTRL_SET (0x000001a4) +#define HW_TVENC_DACCTRL_CLR (0x000001a8) +#define HW_TVENC_DACCTRL_TOG (0x000001ac) + +#define BM_TVENC_DACCTRL_TEST3 0x80000000 +#define BM_TVENC_DACCTRL_RSRVD1 0x40000000 +#define BM_TVENC_DACCTRL_RSRVD2 0x20000000 +#define BM_TVENC_DACCTRL_JACK1_DIS_DET_EN 0x10000000 +#define BM_TVENC_DACCTRL_TEST2 0x08000000 +#define BM_TVENC_DACCTRL_RSRVD3 0x04000000 +#define BM_TVENC_DACCTRL_RSRVD4 0x02000000 +#define BM_TVENC_DACCTRL_JACK1_DET_EN 0x01000000 +#define BM_TVENC_DACCTRL_TEST1 0x00800000 +#define BM_TVENC_DACCTRL_DISABLE_GND_DETECT 0x00400000 +#define BP_TVENC_DACCTRL_JACK_DIS_ADJ 20 +#define BM_TVENC_DACCTRL_JACK_DIS_ADJ 0x00300000 +#define BF_TVENC_DACCTRL_JACK_DIS_ADJ(v) \ + (((v) << 20) & BM_TVENC_DACCTRL_JACK_DIS_ADJ) +#define BM_TVENC_DACCTRL_GAINDN 0x00080000 +#define BM_TVENC_DACCTRL_GAINUP 0x00040000 +#define BM_TVENC_DACCTRL_INVERT_CLK 0x00020000 +#define BM_TVENC_DACCTRL_SELECT_CLK 0x00010000 +#define BM_TVENC_DACCTRL_BYPASS_ACT_CASCODE 0x00008000 +#define BM_TVENC_DACCTRL_RSRVD5 0x00004000 +#define BM_TVENC_DACCTRL_RSRVD6 0x00002000 +#define BM_TVENC_DACCTRL_PWRUP1 0x00001000 +#define BM_TVENC_DACCTRL_WELL_TOVDD 0x00000800 +#define BM_TVENC_DACCTRL_RSRVD7 0x00000400 +#define BM_TVENC_DACCTRL_RSRVD8 0x00000200 +#define BM_TVENC_DACCTRL_DUMP_TOVDD1 0x00000100 +#define BM_TVENC_DACCTRL_LOWER_SIGNAL 0x00000080 +#define BP_TVENC_DACCTRL_RVAL 4 +#define BM_TVENC_DACCTRL_RVAL 0x00000070 +#define BF_TVENC_DACCTRL_RVAL(v) \ + (((v) << 4) & BM_TVENC_DACCTRL_RVAL) +#define BM_TVENC_DACCTRL_NO_INTERNAL_TERM 0x00000008 +#define BM_TVENC_DACCTRL_HALF_CURRENT 0x00000004 +#define BP_TVENC_DACCTRL_CASC_ADJ 0 +#define BM_TVENC_DACCTRL_CASC_ADJ 0x00000003 +#define BF_TVENC_DACCTRL_CASC_ADJ(v) \ + (((v) << 0) & BM_TVENC_DACCTRL_CASC_ADJ) + +#define HW_TVENC_DACSTATUS (0x000001b0) +#define HW_TVENC_DACSTATUS_SET (0x000001b4) +#define HW_TVENC_DACSTATUS_CLR (0x000001b8) +#define HW_TVENC_DACSTATUS_TOG (0x000001bc) + +#define BP_TVENC_DACSTATUS_RSRVD1 13 +#define BM_TVENC_DACSTATUS_RSRVD1 0xFFFFE000 +#define BF_TVENC_DACSTATUS_RSRVD1(v) \ + (((v) << 13) & BM_TVENC_DACSTATUS_RSRVD1) +#define BM_TVENC_DACSTATUS_RSRVD2 0x00001000 +#define BM_TVENC_DACSTATUS_RSRVD3 0x00000800 +#define BM_TVENC_DACSTATUS_JACK1_DET_STATUS 0x00000400 +#define BM_TVENC_DACSTATUS_RSRVD4 0x00000200 +#define BM_TVENC_DACSTATUS_RSRVD5 0x00000100 +#define BM_TVENC_DACSTATUS_JACK1_GROUNDED 0x00000080 +#define BM_TVENC_DACSTATUS_RSRVD6 0x00000040 +#define BM_TVENC_DACSTATUS_RSRVD7 0x00000020 +#define BM_TVENC_DACSTATUS_JACK1_DIS_DET_IRQ 0x00000010 +#define BM_TVENC_DACSTATUS_RSRVD8 0x00000008 +#define BM_TVENC_DACSTATUS_RSRVD9 0x00000004 +#define BM_TVENC_DACSTATUS_JACK1_DET_IRQ 0x00000002 +#define BM_TVENC_DACSTATUS_ENIRQ_JACK 0x00000001 + +#define HW_TVENC_VDACTEST (0x000001c0) +#define HW_TVENC_VDACTEST_SET (0x000001c4) +#define HW_TVENC_VDACTEST_CLR (0x000001c8) +#define HW_TVENC_VDACTEST_TOG (0x000001cc) + +#define BP_TVENC_VDACTEST_RSRVD1 14 +#define BM_TVENC_VDACTEST_RSRVD1 0xFFFFC000 +#define BF_TVENC_VDACTEST_RSRVD1(v) \ + (((v) << 14) & BM_TVENC_VDACTEST_RSRVD1) +#define BM_TVENC_VDACTEST_ENABLE_PIX_INT_GAIN 0x00002000 +#define BM_TVENC_VDACTEST_BYPASS_PIX_INT 0x00001000 +#define BM_TVENC_VDACTEST_BYPASS_PIX_INT_DROOP 0x00000800 +#define BM_TVENC_VDACTEST_TEST_FIFO_FULL 0x00000400 +#define BP_TVENC_VDACTEST_DATA 0 +#define BM_TVENC_VDACTEST_DATA 0x000003FF +#define BF_TVENC_VDACTEST_DATA(v) \ + (((v) << 0) & BM_TVENC_VDACTEST_DATA) + +#define HW_TVENC_VERSION (0x000001d0) + +#define BP_TVENC_VERSION_MAJOR 24 +#define BM_TVENC_VERSION_MAJOR 0xFF000000 +#define BF_TVENC_VERSION_MAJOR(v) \ + (((v) << 24) & BM_TVENC_VERSION_MAJOR) +#define BP_TVENC_VERSION_MINOR 16 +#define BM_TVENC_VERSION_MINOR 0x00FF0000 +#define BF_TVENC_VERSION_MINOR(v) \ + (((v) << 16) & BM_TVENC_VERSION_MINOR) +#define BP_TVENC_VERSION_STEP 0 +#define BM_TVENC_VERSION_STEP 0x0000FFFF +#define BF_TVENC_VERSION_STEP(v) \ + (((v) << 0) & BM_TVENC_VERSION_STEP) +#endif /* __ARCH_ARM___TVENC_H */ diff --git a/drivers/video/mxs/tvenc.c b/drivers/video/mxs/tvenc.c new file mode 100644 index 000000000000..7aaa1fd013b0 --- /dev/null +++ b/drivers/video/mxs/tvenc.c @@ -0,0 +1,279 @@ +/* + * Freescale STMP378X dvi panel initialization + * + * Embedded Alley Solutions, Inc <source@embeddedalley.com> + * + * Copyright 2008-2010 Freescale Semiconductor, Inc. + * Copyright 2008 Embedded Alley Solutions, 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 + */ +/* #define DEBUG */ + +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <mach/regs-lcdif.h> +#include <mach/regs-lradc.h> +#include <mach/regs-pwm.h> +#include <mach/regs-apbh.h> +#include <mach/gpio.h> +#include <mach/lcdif.h> +#include "regs-tvenc.h" + +enum { + TVENC_MODE_OFF = 0, + TVENC_MODE_NTSC, + TVENC_MODE_PAL, +}; + +#define REGS_TVENC_BASE (IO_ADDRESS(TVENC_PHYS_ADDR)) + +/* NTSC 720x480 mode */ +#define NTSC_X_RES 720 +#define NTSC_Y_RES 480 +#define NTSC_H_BLANKING 262 +#define NTSC_V_LINES 525 + +/* PAL 720x576 mode */ +#define PAL_X_RES 720 +#define PAL_Y_RES 576 +#define PAL_H_BLANKING 274 +#define PAL_V_LINES 625 + +/* frame size */ +#define DVI_H_BLANKING(m) (m == TVENC_MODE_NTSC ? \ + NTSC_H_BLANKING : PAL_H_BLANKING) +#define DVI_V_LINES(m) (m == TVENC_MODE_NTSC ? \ + NTSC_V_LINES : PAL_V_LINES) +#define DVI_H_ACTIVE(m) (m == TVENC_MODE_NTSC ? NTSC_X_RES : PAL_X_RES) +#define DVI_V_ACTIVE(m) (m == TVENC_MODE_NTSC ? NTSC_Y_RES : PAL_Y_RES) +/* fileds range */ +#define DVI_F1_START(m) 1 +#define DVI_F1_END(m) (DVI_V_LINES(m) / 2) +#define DVI_F2_START(m) (DVI_F1_END(m) + 1) +#define DVI_F2_END(m) DVI_V_LINES(m) +/* blanking range */ +#define DVI_V1_BLANK_START(m) DVI_F1_END(m) +#define DVI_V1_BLANK_END(m) (DVI_V1_BLANK_START(m) + \ + (DVI_V_LINES(m) - DVI_V_ACTIVE(m)) / 2) +#define DVI_V2_BLANK_START(m) DVI_F2_END(m) +#define DVI_V2_BLANK_END(m) ((DVI_V2_BLANK_START(m) + \ + (DVI_V_LINES(m) - DVI_V_ACTIVE(m)) / 2 - 1) % \ + DVI_V_LINES(m)) + +static struct clk *lcd_clk; +static struct clk *clk_tv108M_ng; +static struct clk *clk_tv27M; + +static int tvenc_mode; + +static void init_tvenc_hw(int mode) +{ + /* Reset module */ + __raw_writel(BM_TVENC_CTRL_SFTRST, REGS_TVENC_BASE + HW_TVENC_CTRL_SET); + udelay(10); + + /* Take module out of reset */ + __raw_writel(BM_TVENC_CTRL_SFTRST | BM_TVENC_CTRL_CLKGATE, + REGS_TVENC_BASE + HW_TVENC_CTRL_CLR); + + if (mode == TVENC_MODE_NTSC) { + /* Config NTSC-M mode, 8-bit Y/C in, SYNC out */ + __raw_writel(BM_TVENC_CONFIG_SYNC_MODE | + BM_TVENC_CONFIG_PAL_SHAPE | + BM_TVENC_CONFIG_YGAIN_SEL | + BM_TVENC_CONFIG_CGAIN, + REGS_TVENC_BASE + HW_TVENC_CONFIG_CLR); + __raw_writel(BM_TVENC_CONFIG_FSYNC_PHS | + BF_TVENC_CONFIG_SYNC_MODE(0x4), + REGS_TVENC_BASE + HW_TVENC_CONFIG_SET); + + /* 859 pixels/line for NTSC */ + __raw_writel(857, REGS_TVENC_BASE + HW_TVENC_SYNCOFFSET); + + __raw_writel(0x21F07C1F, REGS_TVENC_BASE + HW_TVENC_COLORSUB0); + __raw_writel(BM_TVENC_COLORBURST_NBA | + BM_TVENC_COLORBURST_PBA, + REGS_TVENC_BASE + HW_TVENC_COLORBURST_CLR); + __raw_writel(BF_TVENC_COLORBURST_NBA(0xc8) | + BF_TVENC_COLORBURST_PBA(0x0), + REGS_TVENC_BASE + HW_TVENC_COLORBURST_SET); + } else if (mode == TVENC_MODE_PAL) { + /* Config PAL-B mode, 8-bit Y/C in, SYNC out */ + __raw_writel(BM_TVENC_CONFIG_SYNC_MODE | + BM_TVENC_CONFIG_ENCD_MODE | + BM_TVENC_CONFIG_YGAIN_SEL | + BM_TVENC_CONFIG_CGAIN | + BM_TVENC_CONFIG_FSYNC_PHS, + REGS_TVENC_BASE + HW_TVENC_CONFIG_CLR); + __raw_writel(BM_TVENC_CONFIG_PAL_SHAPE | + BF_TVENC_CONFIG_YGAIN_SEL(0x1) + | BF_TVENC_CONFIG_CGAIN(0x1) + | BF_TVENC_CONFIG_ENCD_MODE(0x1) + | BF_TVENC_CONFIG_SYNC_MODE(0x4), + REGS_TVENC_BASE + HW_TVENC_CONFIG_SET); + + /* 863 pixels/line for PAL */ + __raw_writel(863, REGS_TVENC_BASE + HW_TVENC_SYNCOFFSET); + + __raw_writel(0x2A098ACB, REGS_TVENC_BASE + HW_TVENC_COLORSUB0); + __raw_writel(BM_TVENC_COLORBURST_NBA | + BM_TVENC_COLORBURST_PBA, + REGS_TVENC_BASE + HW_TVENC_COLORBURST_CLR); + __raw_writel(BF_TVENC_COLORBURST_NBA(0xd6) | + BF_TVENC_COLORBURST_PBA(0x2a), + REGS_TVENC_BASE + HW_TVENC_COLORBURST_SET); + } + + /* Power up DAC */ + __raw_writel(BM_TVENC_DACCTRL_GAINDN | + BM_TVENC_DACCTRL_GAINUP | + BM_TVENC_DACCTRL_PWRUP1 | + BM_TVENC_DACCTRL_DUMP_TOVDD1 | + BF_TVENC_DACCTRL_RVAL(0x3), + REGS_TVENC_BASE + HW_TVENC_DACCTRL); + + /* set all to zero is a requirement for NTSC */ + __raw_writel(0, REGS_TVENC_BASE + HW_TVENC_MACROVISION0); + __raw_writel(0, REGS_TVENC_BASE + HW_TVENC_MACROVISION1); + __raw_writel(0, REGS_TVENC_BASE + HW_TVENC_MACROVISION2); + __raw_writel(0, REGS_TVENC_BASE + HW_TVENC_MACROVISION3); + __raw_writel(0, REGS_TVENC_BASE + HW_TVENC_MACROVISION4); +} + +static int init_panel(struct device *dev, dma_addr_t phys, int memsize, + struct mxs_platform_fb_entry *pentry) +{ + int ret = 0; + + lcd_clk = clk_get(dev, "lcdif"); + clk_enable(lcd_clk); + clk_set_rate(lcd_clk, 1000000000 / pentry->cycle_time_ns);/* kHz */ + + clk_tv108M_ng = clk_get(NULL, "tv108M_ng"); + clk_tv27M = clk_get(NULL, "tv27M"); + clk_enable(clk_tv108M_ng); + clk_enable(clk_tv27M); + + tvenc_mode = pentry->x_res == NTSC_Y_RES ? TVENC_MODE_NTSC : + TVENC_MODE_PAL; + + init_tvenc_hw(tvenc_mode); + + setup_dvi_panel(DVI_H_ACTIVE(tvenc_mode), DVI_V_ACTIVE(tvenc_mode), + DVI_H_BLANKING(tvenc_mode), DVI_V_LINES(tvenc_mode), + DVI_V1_BLANK_START(tvenc_mode), + DVI_V1_BLANK_END(tvenc_mode), + DVI_V2_BLANK_START(tvenc_mode), + DVI_V2_BLANK_END(tvenc_mode), + DVI_F1_START(tvenc_mode), DVI_F1_END(tvenc_mode), + DVI_F2_START(tvenc_mode), DVI_F2_END(tvenc_mode)); + + ret = mxs_lcdif_dma_init(dev, phys, memsize); + mxs_lcdif_notify_clients(MXS_LCDIF_PANEL_INIT, pentry); + + return ret; +} + +static void release_panel(struct device *dev, + struct mxs_platform_fb_entry *pentry) +{ + mxs_lcdif_notify_clients(MXS_LCDIF_PANEL_RELEASE, pentry); + release_dvi_panel(); + + mxs_lcdif_dma_release(); + + clk_disable(clk_tv108M_ng); + clk_disable(clk_tv27M); + clk_disable(lcd_clk); + clk_put(clk_tv108M_ng); + clk_put(clk_tv27M); + clk_put(lcd_clk); +} + +static int blank_panel(int blank) +{ + int ret = 0, count; + + switch (blank) { + case FB_BLANK_NORMAL: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_POWERDOWN: + __raw_writel(BM_LCDIF_CTRL_BYPASS_COUNT, + REGS_LCDIF_BASE + HW_LCDIF_CTRL_CLR); + + /* Wait until current transfer is complete, max 30ms */ + for (count = 30000; count > 0; count--) { + if (__raw_readl(REGS_LCDIF_BASE + HW_LCDIF_STAT) & + BM_LCDIF_STAT_TXFIFO_EMPTY) + break; + udelay(1); + } + break; + + case FB_BLANK_UNBLANK: + __raw_writel(BM_LCDIF_CTRL_BYPASS_COUNT, + REGS_LCDIF_BASE + HW_LCDIF_CTRL_SET); + break; + + default: + ret = -EINVAL; + } + + return ret; +} + +static struct mxs_platform_fb_entry ntsc_fb_entry = { + .name = "tvenc_ntsc", + /* x/y swapped */ + .x_res = NTSC_Y_RES, + .y_res = NTSC_X_RES, + .bpp = 32, + /* the pix_clk should be near 27Mhz for proper syncronization */ + .cycle_time_ns = 37, + .lcd_type = MXS_LCD_PANEL_DVI, + .init_panel = init_panel, + .release_panel = release_panel, + .blank_panel = blank_panel, + .run_panel = mxs_lcdif_run, + .pan_display = mxs_lcdif_pan_display, +}; + +static struct mxs_platform_fb_entry pal_fb_entry = { + .name = "tvenc_pal", + /* x/y swapped */ + .x_res = PAL_Y_RES, + .y_res = PAL_X_RES, + .bpp = 32, + /* the pix_clk should be near 27Mhz for proper syncronization */ + .cycle_time_ns = 37, + .lcd_type = MXS_LCD_PANEL_DVI, + .init_panel = init_panel, + .release_panel = release_panel, + .blank_panel = blank_panel, + .run_panel = mxs_lcdif_run, + .pan_display = mxs_lcdif_pan_display, +}; + +static int __init register_devices(void) +{ + struct platform_device *pdev; + pdev = mxs_get_device("mxs-fb", 0); + if (pdev == NULL || IS_ERR(pdev)) + return -ENODEV; + + mxs_lcd_register_entry(&ntsc_fb_entry, pdev->dev.platform_data); + mxs_lcd_register_entry(&pal_fb_entry, pdev->dev.platform_data); + return 0; +} + +subsys_initcall(register_devices); |