diff options
29 files changed, 5066 insertions, 9 deletions
diff --git a/arch/arm/mach-mvf/board-twr_vf600.c b/arch/arm/mach-mvf/board-twr_vf600.c index 5f956e458f21..6c7ac64b3470 100644 --- a/arch/arm/mach-mvf/board-twr_vf600.c +++ b/arch/arm/mach-mvf/board-twr_vf600.c @@ -354,6 +354,28 @@ static struct platform_device lpt_device = { .resource = lpt_resources, }; +static struct mvf_dcuv4_platform_data dcuv4_data[] = { + { + .rev = 1, + }, { + .rev = 1, + }, +}; + +static struct dcuv4_fb_platform_data mvf_fb_data[] = { + { + .disp_dev = "lcd", + .interface_pix_fmt = V4L2_PIX_FMT_RGB32, + .mode_str = "NEC-WQVGA", + .default_bpp = 32, + }, +}; + +static struct fsl_mvf_lcd_platform_data lcdif_data = { + .dcu_id = 0, + .default_ifmt = V4L2_PIX_FMT_RGB32, +}; + static void twr_vf600_suspend_enter(void) { /* suspend preparation */ @@ -397,6 +419,13 @@ static void __init twr_vf600_init(void) vf6xx_add_imx_snvs_rtc(); mvf_init_fec(fec_data); + printk("TWR_VF600: Adding dcuv4 \n"); + vf600_add_dcuv4(0, &dcuv4_data[0]); + printk("TWR_VF600: Adding dcuv4-fb\n"); + mvf_add_dcuv4_fb(0, &mvf_fb_data[0]); + printk("TWR_VF600: Adding lcdif\n"); + mvf_add_lcdif(&lcdif_data); + platform_device_register(&edma_device0); platform_device_register(&edma_device1); platform_device_register(&pit_device); diff --git a/arch/arm/mach-mvf/clock.c b/arch/arm/mach-mvf/clock.c index 3ea6961df483..cd7e95fc749a 100644 --- a/arch/arm/mach-mvf/clock.c +++ b/arch/arm/mach-mvf/clock.c @@ -2559,10 +2559,10 @@ static unsigned long _clk_dcu_get_rate(struct clk *clk) reg = __raw_readl(MXC_CCM_CSCDR3); if (clk == &dcu0_clk_root) - div = ((reg & ~MXC_CCM_CSCDR3_DCU0_DIV_MASK) >> + div = ((reg & MXC_CCM_CSCDR3_DCU0_DIV_MASK) >> MXC_CCM_CSCDR3_DCU0_DIV_OFFSET) + 1; else - div = ((reg & ~MXC_CCM_CSCDR3_DCU1_DIV_MASK) >> + div = ((reg & MXC_CCM_CSCDR3_DCU1_DIV_MASK) >> MXC_CCM_CSCDR3_DCU1_DIV_OFFSET) + 1; return clk_get_rate(clk->parent) / div; @@ -2617,7 +2617,7 @@ static int _clk_dcu_set_parent(struct clk *clk, struct clk *parent) static struct clk dcu0_clk_root = { __INIT_CLK_DEBUG(dcu0_clk_root) - .parent = &pll1_pfd2, //FIXME + .parent = &pll3_480_usb1_main_clk, .enable_shift = MXC_CCM_CSCDR3_DCU0_EN_OFFSET, .enable = _clk_dcu_enable, .disable = _clk_dcu_disable, diff --git a/arch/arm/mach-mvf/devices-mvf.h b/arch/arm/mach-mvf/devices-mvf.h index 7fa46ca9c8fa..00ea16ec6147 100644 --- a/arch/arm/mach-mvf/devices-mvf.h +++ b/arch/arm/mach-mvf/devices-mvf.h @@ -21,6 +21,9 @@ #include <mach/mvf.h> #include <mach/devices-common.h> +extern const struct mvf_dcuv4_data vf600_dcuv4_data[] __initconst; +#define vf600_add_dcuv4(id, pdata) mvf_add_dcuv4(id, &vf600_dcuv4_data[id], pdata) + extern const struct imx_imx_uart_1irq_data mvf_imx_uart_data[] __initconst; #define mvf_add_imx_uart(id, pdata) \ imx_add_imx_uart_1irq(&mvf_imx_uart_data[id], pdata) @@ -29,6 +32,10 @@ extern const struct imx_snvs_rtc_data vf6xx_imx_snvs_rtc_data __initconst; #define vf6xx_add_imx_snvs_rtc() \ imx_add_snvs_rtc(&vf6xx_imx_snvs_rtc_data) +#define mvf_add_lcdif(pdata) \ + platform_device_register_resndata(NULL, "mvf_lcdif",\ + 0, NULL, 0, pdata, sizeof(*pdata)); + #if 0 extern const struct imx_dma_data vf6xx_dma_data __initconst; #define vf6xx_add_dma() imx_add_dma(&vf6xx_dma_data); diff --git a/arch/arm/plat-mxc/devices/Kconfig b/arch/arm/plat-mxc/devices/Kconfig index 3eead8ebda92..500e3267dc19 100755 --- a/arch/arm/plat-mxc/devices/Kconfig +++ b/arch/arm/plat-mxc/devices/Kconfig @@ -169,3 +169,7 @@ config IMX_HAVE_PLATFORM_IMX_MIPI_DSI config IMX_HAVE_PLATFORM_IMX_MIPI_CSI2 bool + +config MVF_HAVE_PLATFORM_DCUV4 + bool + default y if ARCH_MVF diff --git a/arch/arm/plat-mxc/devices/Makefile b/arch/arm/plat-mxc/devices/Makefile index 71cfefa7cccb..64a57a1f7e36 100755 --- a/arch/arm/plat-mxc/devices/Makefile +++ b/arch/arm/plat-mxc/devices/Makefile @@ -61,3 +61,4 @@ obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_HDMI) += platform-imx-hdmi-soc-dai.o obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_ASRC) += platform-imx-asrc.o obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_MIPI_DSI) += platform-imx-mipi_dsi.o obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_MIPI_CSI2) += platform-imx-mipi_csi2.o +obj-$(CONFIG_MVF_HAVE_PLATFORM_DCUV4) += platform-mvf_dcuv4.o diff --git a/arch/arm/plat-mxc/devices/platform-mvf_dcuv4.c b/arch/arm/plat-mxc/devices/platform-mvf_dcuv4.c new file mode 100644 index 000000000000..8f53c0e5e983 --- /dev/null +++ b/arch/arm/plat-mxc/devices/platform-mvf_dcuv4.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2011-2012 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 <mach/hardware.h> +#include <mach/devices-common.h> +#include <linux/clk.h> + +#define mvf_dcu4_data_entry_single(soc, id, size, dcu_pg, dcu_blank) \ + { \ + .iobase = soc ## _DCU ## id ## _BASE_ADDR, \ + .irq = soc ## _INT_DCU ## id, \ + .iosize = size, \ + .pg = dcu_pg, \ + .blank = dcu_blank, \ + } + +#ifdef CONFIG_SOC_VF6XX +#include <mach/iomux-vmvf.h> +#include <mach/iomux-vf6xx.h> +void vf600_dcuv4_pg(int id, int enable) +{ + /*TODO; get rid of this and use clock enable?*/ + //if(enable) + //val = readl(MX51_IO_ADDRESS(MX51_SRC_BASE_ADDR)); + //writel(MXC_PGCR_PCR, MX51_PGC_IPU_PGCR); + +} + +void vf600_dcuv4_blank(int id, int enable) +{ + /*TODO: Add check for which DCU*/ + /*TODO: Get rid of the backlight enable and use PWM module */ + if(enable) + { + if(id == 0) + { + //disable pclk + mxc_iomux_vmvf_setup_pad(VF6XX_PAD_PAD_110__RGPIOC_GPIO110); + //disable backlight + mxc_iomux_vmvf_setup_pad((_VF6XX_PAD_PAD_30__RGPIOC_GPIO30 | MUX_CTRL_PAD(VF6XX_PAD_CTRL_OBE))); + } + + } + else + { + if(id == 0) + { + //enable pclk + mxc_iomux_vmvf_setup_pad(VF6XX_PAD_PAD_110__TCON0_DATA_OUT18); + //enable backlight + mxc_iomux_vmvf_setup_pad((_VF6XX_PAD_PAD_30__RGPIOC_GPIO30 | MUX_CTRL_PAD(NO_PAD_CTRL))); + } + } +} + +const struct mvf_dcuv4_data vf600_dcuv4_data[] __initconst = { + mvf_dcu4_data_entry_single(MVF, 0, SZ_4M, + vf600_dcuv4_pg, vf600_dcuv4_blank), + mvf_dcu4_data_entry_single(MVF, 1, SZ_4M, + vf600_dcuv4_pg, vf600_dcuv4_blank), +}; +#endif + +struct platform_device *__init mvf_add_dcuv4( + const int id, + const struct mvf_dcuv4_data *data, + struct mvf_dcuv4_platform_data *pdata) +{ + struct resource res[] = { + { + .start = data->iobase, + .end = data->iobase + data->iosize - 1, + .flags = IORESOURCE_MEM, + }, { + .start = data->irq, + .end = data->irq, + .flags = IORESOURCE_IRQ, + }, + }; + + pdata->pg = data->pg; + pdata->blank = data->blank; + + return imx_add_platform_device_dmamask("mvf-dcuv4", id, + res, ARRAY_SIZE(res), pdata, sizeof(*pdata), + DMA_BIT_MASK(32)); +} + +struct platform_device *__init mvf_add_dcuv4_fb( + const int id, + const struct dcuv4_fb_platform_data *pdata) +{ + if (pdata->res_size[0] > 0) { + struct resource res[] = { + { + .start = pdata->res_base[0], + .end = pdata->res_base[0] + pdata->res_size[0] - 1, + .flags = IORESOURCE_MEM, + }, { + .start = 0, + .end = 0, + .flags = IORESOURCE_MEM, + }, + }; + + if (pdata->res_size[1] > 0) { + res[1].start = pdata->res_base[1]; + res[1].end = pdata->res_base[1] + + pdata->res_size[1] - 1; + } + + return imx_add_platform_device_dmamask("mvf_dcu4_fb", + id, res, ARRAY_SIZE(res), pdata, + sizeof(*pdata), DMA_BIT_MASK(32)); + } else + return imx_add_platform_device_dmamask("mvf_dcu4_fb", id, + NULL, 0, pdata, sizeof(*pdata), + DMA_BIT_MASK(32)); +} diff --git a/arch/arm/plat-mxc/include/mach/dcu-v4.h b/arch/arm/plat-mxc/include/mach/dcu-v4.h new file mode 100644 index 000000000000..b1e04c09f983 --- /dev/null +++ b/arch/arm/plat-mxc/include/mach/dcu-v4.h @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de> + * 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. + */ + +#ifndef __MACH_DCU_V4_H_ +#define __MACH_DCU_V4_H_ + +#include <linux/ipu.h> + + +typedef struct +{ + unsigned enable:1; + unsigned tile_en:1; + unsigned data_sel:1; + unsigned saftey_en:1; + unsigned trans:8; + unsigned bpp:4; + unsigned rle_en:1; + unsigned luoffs:11; + unsigned bb:1; + unsigned ab:2; +}dcu_layer_cfg_t; + +typedef struct +{ + unsigned color_r:8; + unsigned color_g:8; + unsigned color_b:8; +}dcu_color_t; + +/*! + * Union of initialization parameters for a logical layer. + */ +typedef union { + + struct { + uint32_t width; + uint32_t height; + uint32_t pos_x; + uint32_t pos_y; + uint32_t addr; + dcu_layer_cfg_t layer_cfg; + dcu_color_t ckmax; + dcu_color_t ckmin; + uint32_t tile_ver_size; + uint32_t tile_hor_size; + uint32_t fg_fcolor; + uint32_t fg_bcolor; + }layer_mem; +}dcu_layer_params_t; + +/*! + * Enumeration of DCU interrupt sources. + */ +enum dcu_irq_line { + DCU_IRQ_VSYNC = 0, + DCU_IRQ_UNDRUN = DCU_IRQ_VSYNC + 1, + DCU_IRQ_LS_BF_VS = DCU_IRQ_VSYNC + 2, + DCU_IRQ_VS_BLANK = DCU_IRQ_VSYNC + 3, + DCU_IRQ_CRC_READY = DCU_IRQ_VSYNC + 4, + DCU_IRQ_CRC_OVERFLOW = DCU_IRQ_VSYNC + 5, + DCU_IRQ_P1_FIFO_LO_FLAG = DCU_IRQ_VSYNC + 6, + DCU_IRQ_P1_FIFO_HI_FLAG = DCU_IRQ_VSYNC + 7, + DCU_IRQ_P2_FIFO_LO_FLAG = DCU_IRQ_VSYNC + 8, + DCU_IRQ_P2_FIFO_HI_FLAG = DCU_IRQ_VSYNC + 9, + DCU_IRQ_PROG_END = DCU_IRQ_VSYNC + 10, + /* DCU_IRQ_IPM_ERROR = DCU_IRQ_VSYNC + 11, */ + DCU_IRQ_LYR_TRANS_FINISH = DCU_IRQ_VSYNC + 12, + DCU_IRQ_DMA_TRANS_FINISH = DCU_IRQ_VSYNC + 14, + DCU_IRQ_P3_FIFO_LO_FLAG = DCU_IRQ_VSYNC + 16, + DCU_IRQ_P3_FIFO_HI_FLAG = DCU_IRQ_VSYNC + 17, + DCU_IRQ_P4_FIFO_LO_FLAG = DCU_IRQ_VSYNC + 18, + DCU_IRQ_P4_FIFO_HI_FLAG = DCU_IRQ_VSYNC + 19, + DCU_IRQ_P5_FIFO_LO_FLAG = DCU_IRQ_VSYNC + 20, + DCU_IRQ_P5_FIFO_HI_FLAG = DCU_IRQ_VSYNC + 21, + DCU_IRQ_P6_FIFO_LO_FLAG = DCU_IRQ_VSYNC + 22, + DCU_IRQ_P6_FIFO_HI_FLAG = DCU_IRQ_VSYNC + 23, + DCU_IRQ_P1_EMPTY = DCU_IRQ_VSYNC + 26, + DCU_IRQ_P2_EMPTY = DCU_IRQ_VSYNC + 27, + DCU_IRQ_P3_EMPTY = DCU_IRQ_VSYNC + 28, + DCU_IRQ_P4_EMPTY = DCU_IRQ_VSYNC + 29, + DCU_IRQ_P5_EMPTY = DCU_IRQ_VSYNC + 30, + DCU_IRQ_P6_EMPTY = DCU_IRQ_VSYNC + 31, + DCU_IRQ_COUNT +}; + +enum dcu_mode +{ + DCU_OFF = 0, + DCU_NORMAL_MODE, + DCU_TEST_MODE, + DCU_COLOR_BAR_MODE +}; + +typedef enum +{ + BPP1_CLUT = 0, + BPP2_CLUT, + BPP4_CLUT, + BPP8_CLUT, + BPP16_RGB565, + BPP24_RGB888, + BPP32_ARGB8888, + BPP4_TRANSPARENCY_MODE, + BPP8_TRANSPARENCY_MODE, + BPP4_LUMINANCE_OFFSET_MODE, + BPP8_LUMINANCE_OFFSET_MODE, + BPP16_ARGB1555, + BPP16_ARGB4444, + BPP16_APAL8, + YCbCr422, + BPP_INVALID +}dcu_bpp_format; + +/* TODO: Give users more options */ +typedef enum +{ + ALPHA_BLEND_DISABLED = 0, + ALPHA_BLEND_ENABLED = 2 +}alpha_aa_config; + +/*! + * Bitfield of Display Interface signal polarities. + */ +typedef struct { + unsigned interlaced:1; + unsigned data_pol:1; /* true = inverted */ + unsigned clk_pol:1; /* true = rising edge */ + unsigned Hsync_pol:1; /* true = active high */ + unsigned Vsync_pol:1; +} dcu_di_signal_cfg_t; + + +struct dcu_soc; +struct dcu_soc *dcu_get_soc(int id); + +/* DCU Layer support */ +int32_t dcu_init_layer(struct dcu_soc *dcu, uint8_t layer, + uint32_t pixel_fmt, + uint16_t width, uint16_t height, + dma_addr_t phyaddr_0, + int16_t x_pos, int16_t y_pos); +void dcu_uninit_layer(struct dcu_soc *dcu, uint8_t layer); + +int32_t dcu_update_layer_buffer(struct dcu_soc *dcu, uint8_t layer, + dma_addr_t phyaddr); + +int32_t dcu_set_layer_position(struct dcu_soc *dcu, uint8_t layer, int16_t x_pos, + int16_t y_pos); + +int32_t dcu_get_layer_position(struct dcu_soc *dcu, uint8_t layer, int16_t *x_pos, + int16_t *y_pos); + +int32_t dcu_is_layer_enabled(struct dcu_soc *dcu, uint8_t layer); + +int32_t dcu_enable_layer(struct dcu_soc *dcu, uint8_t layer); + +int32_t dcu_disable_layer(struct dcu_soc *dcu, uint8_t layer, bool wait_for_stop); + +int32_t dcu_config_layer_alpha(struct dcu_soc *dcu, uint8_t layer, + uint8_t alpha_value, alpha_aa_config aa); + +int32_t dcu_set_chroma_keying(struct dcu_soc *dcu, uint8_t layer, + dcu_color_t color_max, dcu_color_t color_min, bool enable); + +/* DCU IRQ Support */ +void dcu_enable_irq(struct dcu_soc *dcu, uint32_t irq); + +void dcu_disable_irq(struct dcu_soc *dcu, uint32_t irq); + +void dcu_clear_irq(struct dcu_soc *dcu, uint32_t irq); + +bool dcu_get_irq_status(struct dcu_soc *dcu, uint32_t irq); + +int dcu_request_irq(struct dcu_soc *dcu, uint32_t irq, + irqreturn_t(*handler) (int, void *), + uint32_t irq_flags, const char *devname, void *dev_id); + +void dcu_free_irq(struct dcu_soc *dcu, uint32_t irq, void *dev_id); + +/* DCU device driver support */ +int register_dcu_device(struct dcu_soc *dcu, int id); + +void unregister_dcu_device(struct dcu_soc *dcu, int id); + +/* Display API */ +int32_t dcu_init_panel(struct dcu_soc *dcu, uint32_t pixel_clk, + uint16_t width, uint16_t height, + uint16_t h_start_width, uint16_t h_sync_width, + uint16_t h_end_width, uint16_t v_start_width, + uint16_t v_sync_width, uint16_t v_end_width, + uint32_t v_to_h_sync, dcu_di_signal_cfg_t sig); + +void dcu_uninit_panel(struct dcu_soc *dcu); + +void dcu_enable(struct dcu_soc *dcu); + +void dcu_disable(struct dcu_soc *dcu); + +/* DCU global feature support */ +void dcu_set_bgnd_color(struct dcu_soc *dcu, dcu_color_t color); + + + +/* Later releases of DCU should support these + * +int32_t dcu_disp_set_gamma_correction(struct dcu_soc *dcu, uint8_t layer, bool enable, + int constk[], int slopek[]); +*/ + +struct dcuv4_fb_platform_data { + char disp_dev[32]; + u32 interface_pix_fmt; + char *mode_str; + int default_bpp; + int dcu_id; + + /* reserved mem */ + resource_size_t res_base[2]; + resource_size_t res_size[2]; +}; + +struct mvf_dcuv4_platform_data { + int rev; + void (*pg) (int, int); + void (*blank) (int, int); +}; + +#endif /* __MACH_DCU_V4_H_ */ diff --git a/arch/arm/plat-mxc/include/mach/devices-common.h b/arch/arm/plat-mxc/include/mach/devices-common.h index 2f5f04c39638..b98db656f8f9 100755 --- a/arch/arm/plat-mxc/include/mach/devices-common.h +++ b/arch/arm/plat-mxc/include/mach/devices-common.h @@ -352,6 +352,23 @@ struct platform_device *__init imx_add_ipuv3_fb( const int id, const struct ipuv3_fb_platform_data *pdata); +#include <mach/dcu-v4.h> +struct mvf_dcuv4_data { + resource_size_t iobase; + resource_size_t iosize; + resource_size_t irq; + void (*pg) (int, int); + void (*blank) (int, int); +}; +struct platform_device *__init mvf_add_dcuv4( + const int id, + const struct mvf_dcuv4_data *data, + struct mvf_dcuv4_platform_data *pdata); + +struct platform_device *__init mvf_add_dcuv4_fb( + const int id, + const struct dcuv4_fb_platform_data *pdata); + #include <mach/mxc_vpu.h> struct imx_vpu_data { resource_size_t iobase; diff --git a/arch/arm/plat-mxc/include/mach/iomux-vf6xx.h b/arch/arm/plat-mxc/include/mach/iomux-vf6xx.h index f7c5c7620ae2..e1ab00606fcc 100755 --- a/arch/arm/plat-mxc/include/mach/iomux-vf6xx.h +++ b/arch/arm/plat-mxc/include/mach/iomux-vf6xx.h @@ -3293,7 +3293,7 @@ #define VF6XX_PAD_PAD_105__RGPIOC_GPIO105 \ (_VF6XX_PAD_PAD_105__RGPIOC_GPIO105 | MUX_CTRL_PAD(NO_PAD_CTRL)) #define VF6XX_PAD_PAD_105__TCON0_TCON1 \ - (_VF6XX_PAD_PAD_105__TCON0_TCON1 | MUX_CTRL_PAD(VF6XX_PAD_CTRL_IOBE)) /* FIXME */ + (_VF6XX_PAD_PAD_105__TCON0_TCON1 | MUX_CTRL_PAD(VF6XX_PAD_CTRL_OBE)) #define VF6XX_PAD_PAD_105__SRC_BMODE1 \ (_VF6XX_PAD_PAD_105__SRC_BMODE1 | MUX_CTRL_PAD(NO_PAD_CTRL)) #define VF6XX_PAD_PAD_105__LCD_64F6B_LCD0 \ @@ -3304,7 +3304,7 @@ #define VF6XX_PAD_PAD_106__RGPIOC_GPIO106 \ (_VF6XX_PAD_PAD_106__RGPIOC_GPIO106 | MUX_CTRL_PAD(NO_PAD_CTRL)) #define VF6XX_PAD_PAD_106__TCON0_TCON2 \ - (_VF6XX_PAD_PAD_106__TCON0_TCON2 | MUX_CTRL_PAD(VF6XX_PAD_CTRL_IOBE)) /* FIXME */ + (_VF6XX_PAD_PAD_106__TCON0_TCON2 | MUX_CTRL_PAD(VF6XX_PAD_CTRL_OBE)) #define VF6XX_PAD_PAD_106__SRC_BMODE0 \ (_VF6XX_PAD_PAD_106__SRC_BMODE0 | MUX_CTRL_PAD(NO_PAD_CTRL)) #define VF6XX_PAD_PAD_106__LCD_64F6B_LCD1 \ @@ -3315,7 +3315,7 @@ #define VF6XX_PAD_PAD_107__RGPIOC_GPIO107 \ (_VF6XX_PAD_PAD_107__RGPIOC_GPIO107 | MUX_CTRL_PAD(NO_PAD_CTRL)) #define VF6XX_PAD_PAD_107__TCON0_DATA_OUT1 \ - (_VF6XX_PAD_PAD_107__TCON0_DATA_OUT1 | MUX_CTRL_PAD(VF6XX_PAD_CTRL_IOBE)) + (_VF6XX_PAD_PAD_107__TCON0_DATA_OUT1 | MUX_CTRL_PAD(VF6XX_PAD_CTRL_OBE)) #define VF6XX_PAD_PAD_107__LCD_64F6B_LCD2 \ (_VF6XX_PAD_PAD_107__LCD_64F6B_LCD2 | MUX_CTRL_PAD(NO_PAD_CTRL)) #define VF6XX_PAD_PAD_107__VIU_MUX_DEBUG_OUT31 \ @@ -3333,7 +3333,7 @@ #define VF6XX_PAD_PAD_109__RGPIOC_GPIO109 \ (_VF6XX_PAD_PAD_109__RGPIOC_GPIO109 | MUX_CTRL_PAD(NO_PAD_CTRL)) #define VF6XX_PAD_PAD_109__TCON0_TCON3 \ - (_VF6XX_PAD_PAD_109__TCON0_TCON3 | MUX_CTRL_PAD(VF6XX_PAD_CTRL_IOBE)) + (_VF6XX_PAD_PAD_109__TCON0_TCON3 | MUX_CTRL_PAD(VF6XX_PAD_CTRL_OBE)) #define VF6XX_PAD_PAD_109__LCD_64F6B_LCD4 \ (_VF6XX_PAD_PAD_109__LCD_64F6B_LCD4 | MUX_CTRL_PAD(NO_PAD_CTRL)) #define VF6XX_PAD_PAD_109__VIU_MUX_DEBUG_OUT33 \ diff --git a/arch/arm/plat-mxc/include/mach/mvf.h b/arch/arm/plat-mxc/include/mach/mvf.h index 6cbea664b6e5..8c5aaad02d70 100644 --- a/arch/arm/plat-mxc/include/mach/mvf.h +++ b/arch/arm/plat-mxc/include/mach/mvf.h @@ -329,8 +329,8 @@ #define MXC_INT_DDRMC 58 #define MXC_INT_SDHC0 59 #define MXC_INT_SDHC1 60 -#define MXC_INT_DCU0 62 -#define MXC_INT_DCU1 63 +#define MVF_INT_DCU0 62 +#define MVF_INT_DCU1 63 #define MXC_INT_VIU 64 #define MXC_INT_GPU 66 #define MXC_INT_RLE 67 diff --git a/drivers/Kconfig b/drivers/Kconfig index 65f7142817a6..2eb96886b19e 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -128,4 +128,6 @@ source "drivers/clocksource/Kconfig" source "drivers/mxc/Kconfig" +source "drivers/mvf/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 3c66d7069ec7..d62002a0fce8 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -97,6 +97,7 @@ obj-$(CONFIG_CPU_FREQ) += cpufreq/ obj-$(CONFIG_CPU_IDLE) += cpuidle/ obj-$(CONFIG_MMC) += mmc/ obj-$(CONFIG_ARCH_MXC) += mxc/ +obj-$(CONFIG_ARCH_MVF) += mvf/ obj-$(CONFIG_MEMSTICK) += memstick/ obj-y += leds/ obj-$(CONFIG_INFINIBAND) += infiniband/ diff --git a/drivers/mvf/Kconfig b/drivers/mvf/Kconfig new file mode 100644 index 000000000000..babb8a1f2839 --- /dev/null +++ b/drivers/mvf/Kconfig @@ -0,0 +1,19 @@ +#drivers/video/mvf/Kconfig + +if ARCH_MVF + +menu "MVF support drivers" + +config MVF_DCU + bool "Display Controller Unit Driver" + depends on ARCH_VF6XX + select MVF_DCU_V4 if ARCH_VF6XX + help + If you plan to use the Display Controller unit, say + Y here. DCU is needed by Framebuffer and V4L2 drivers. + +source "drivers/mvf/dcu4/Kconfig" + +endmenu + +endif diff --git a/drivers/mvf/Makefile b/drivers/mvf/Makefile new file mode 100644 index 000000000000..c346ef61a446 --- /dev/null +++ b/drivers/mvf/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_MVF_DCU) += dcu4/ diff --git a/drivers/mvf/dcu4/Kconfig b/drivers/mvf/dcu4/Kconfig new file mode 100644 index 000000000000..b935b261531b --- /dev/null +++ b/drivers/mvf/dcu4/Kconfig @@ -0,0 +1,2 @@ +config MVF_DCU_V4 + bool diff --git a/drivers/mvf/dcu4/Makefile b/drivers/mvf/dcu4/Makefile new file mode 100644 index 000000000000..690add975de8 --- /dev/null +++ b/drivers/mvf/dcu4/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_MVF_DCU) = mvf_dcu.o + +mvf_dcu-objs := dcu4_driver.o + diff --git a/drivers/mvf/dcu4/dcu.h b/drivers/mvf/dcu4/dcu.h new file mode 100644 index 000000000000..4792ab4d68ee --- /dev/null +++ b/drivers/mvf/dcu4/dcu.h @@ -0,0 +1,79 @@ +/* + * Copyright 2005-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 + */ +#ifndef __INCLUDE_DCU_H__ +#define __INCLUDE_DCU_H__ + +#include <linux/types.h> +#include <linux/device.h> +#include <mach/clock.h> +#include <linux/clkdev.h> +#include <linux/interrupt.h> +#include <linux/fsl_devices.h> + + +#define MVF_DCU_MAX_NUM 1 + +/* Globals */ +struct dcu_irq_node { + irqreturn_t(*handler) (int, void *); /*!< the ISR */ + const char *name; /*!< device associated with the interrupt */ + void *dev_id; /*!< some unique information for the ISR */ + __u32 flags; /*!< not used */ +}; + +struct dcu_soc { + bool online; + bool display_configured; + + /*clk*/ + struct clk *dcu_clk; + struct clk pixel_clk; + + /*irq*/ + int irq_generic; + struct dcu_irq_node irq_list[DCU_IRQ_COUNT]; + + /*reg*/ + u32 *dcu_base_reg; + u32 *clut_tile_mem_base; + u32 *gamma_r_mem_base; + u32 *gamma_g_mem_base; + u32 *gamma_b_mem_base; + u32 *cursor_mem_base; + + struct device *platform_dev; + struct device *dcu_cdev; + + uint32_t dcu_layer_transfer_complete_irq; + struct completion layer_transfer_complete; + + /*use count*/ + atomic_t dcu_use_count; + atomic_t layer_use_count; + + struct mutex mutex_lock; + spinlock_t spin_lock; +}; + +static inline u32 dcu_read(struct dcu_soc *dcu, unsigned offset) +{ + return readl(dcu->dcu_base_reg + offset); +} + +static inline void dcu_write(struct dcu_soc *dcu, + u32 value, unsigned offset) +{ + writel(value, dcu->dcu_base_reg + offset); +} + +#endif /* __INCLUDE_DCU_H__ */ diff --git a/drivers/mvf/dcu4/dcu4_driver.c b/drivers/mvf/dcu4/dcu4_driver.c new file mode 100644 index 000000000000..e6a637cf5f95 --- /dev/null +++ b/drivers/mvf/dcu4/dcu4_driver.c @@ -0,0 +1,1942 @@ +/* + * Copyright 2005-2012 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 + */ + +/*! + * @file dcu4_driver.c + * + * @brief This file contains the DCU driver common API functions. + * + * @ingroup DCU + */ +#include <linux/types.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/irqdesc.h> +#include <linux/clk.h> +#include <mach/clock.h> +#include <mach/hardware.h> +#include <mach/dcu-v4.h> +#include <mach/devices-common.h> +#include <asm/cacheflush.h> +#include <linux/delay.h> + +#include "dcu.h" +#include "dcu4_regs.h" + +/* + * DCU Driver name + */ +#define MVF_DCU "mvf-dcuv4" + +/* + * DCU V4 limitations + */ +#define MAX_DISP_WIDTH 2048 +#define MAX_DISP_HEIGHT 2048 +#define MAX_DCU_LAYERS 64 +#define DCU_UNBLANK 0 +#define DCU_BLANK 1 + +#define DCU_UNIT_TEST + +static struct dcu_soc dcu_array[MVF_DCU_MAX_NUM]; +static int g_dcu_hw_rev; +static int major; +static struct class *dcu_class; +static uint32_t *tcon0_ctrl1; + +/* Static functions */ +static irqreturn_t dcu_irq_handler(int irq, void *desc); +void dcu_enable_colorbar_mode(struct dcu_soc *dcu); +void dcu_disable_colorbar_mode(struct dcu_soc *dcu); + +static dcu_bpp_format format_to_layerencoding(uint32_t fmt) +{ + switch (fmt){ + case V4L2_PIX_FMT_RGB565: + return BPP16_RGB565; + case V4L2_PIX_FMT_RGB24: + return BPP24_RGB888; + case V4L2_PIX_FMT_RGB32: + return BPP32_ARGB8888; + default: + return BPP_INVALID; + } +} + +static uint8_t layerencoding_to_min_width(dcu_bpp_format layerencoding) +{ + switch (layerencoding){ + case BPP1_CLUT: + case BPP2_CLUT: + return 16; + case BPP4_CLUT: + case BPP4_LUMINANCE_OFFSET_MODE: + case BPP4_TRANSPARENCY_MODE: + return 8; + case BPP8_CLUT: + case BPP8_TRANSPARENCY_MODE: + case BPP8_LUMINANCE_OFFSET_MODE: + case YCbCr422: + case BPP24_RGB888: + return 4; + case BPP16_RGB565: + case BPP16_ARGB1555: + case BPP16_ARGB4444: + case BPP16_APAL8: + return 2; + case BPP32_ARGB8888: + return 1; + default: + return BPP_INVALID; + } +} + +static void _dcu_lock(struct dcu_soc *dcu) +{ + /*TODO:Do we need the irq check?? See the flow of operations + * from V4L2 and FB*/ + if (!in_irq() && !in_softirq()) + { + mutex_lock(&dcu->mutex_lock); + + if(dcu_read(dcu, DCU_UPDATE_MODE_OFFSET) & DCU_UPDATE_MODE_READREG_MASK) + { + int retval; + retval = wait_for_completion_interruptible_timeout( + &dcu->layer_transfer_complete, 1 * HZ); + if (retval == 0) { + dev_err(dcu->platform_dev, "MVF DCU lock: timeout\n"); + } + } + } +} + +static void _dcu_unlock(struct dcu_soc *dcu) +{ + uint32_t reg; + /* TODO: Make this more efficient? */ + reg = dcu_read(dcu, DCU_UPDATE_MODE_OFFSET); + if(reg & DCU_UPDATE_MODE_MODE_MASK) + dev_err(dcu->platform_dev, "MVF DCU configured for unsupported automatic DCU update mode\n"); + else + reg |= DCU_UPDATE_MODE_READREG_MASK; + dcu_write(dcu, reg, DCU_UPDATE_MODE_OFFSET); + + init_completion(&dcu->layer_transfer_complete); + dcu_clear_irq(dcu, dcu->dcu_layer_transfer_complete_irq); + dcu_enable_irq(dcu, dcu->dcu_layer_transfer_complete_irq); + + /*TODO:Do we need the irq check?? See the flow of operations + * from V4L2 and FB*/ + if (!in_irq() && !in_softirq()) + mutex_unlock(&dcu->mutex_lock); +} + +static void _dcu_get(struct dcu_soc *dcu) +{ + if (atomic_inc_return(&dcu->dcu_use_count) == 1) + clk_enable(dcu->dcu_clk); +} + +static void _dcu_put(struct dcu_soc *dcu) +{ + if (atomic_dec_return(&dcu->dcu_use_count) == 0) + clk_disable(dcu->dcu_clk); +} +#if 0 +static void dcu_reset(struct dcu_soc *dcu) +{ + u32 reg; + reg = dcu_read(dcu, DCU_DCU_MODE_OFFSET); + dcu_write(dcu, reg | DCU_DCU_MODE_DCU_SW_RESET_MASK, DCU_DCU_MODE_OFFSET); +} +#endif + +static inline struct dcu_soc *pixelclk2dcu(struct clk *clk) +{ + struct dcu_soc *dcu; + struct clk *base = clk - clk->id; + + dcu = container_of(base, struct dcu_soc, pixel_clk); + + return dcu; +} + +static unsigned long _dcu_pixel_clk_get_rate(struct clk *clk) +{ + struct dcu_soc *dcu = pixelclk2dcu(clk); + u32 div; + u64 final_rate = clk_get_rate(clk->parent); + + _dcu_get(dcu); + div = dcu_read(dcu, DCU_DIV_RATIO_OFFSET); + _dcu_put(dcu); + /* Actual value in register is div-1, so add 1 to get the real divider */ + div++; + do_div(final_rate, div); + return (unsigned long)final_rate; +} + +static unsigned long _dcu_pixel_clk_round_rate(struct clk *clk, unsigned long rate) +{ + u64 div, final_rate; + u32 remainder; + u64 parent_rate = (unsigned long long)clk_get_rate(clk->parent); + + div = parent_rate; + remainder = do_div(div, rate); + /* Round the divider value */ + if (remainder > (rate/2)) + div++; + if (div == 0) /* Min DI disp clock divider is 1 */ + div = 1; + else if (div > (DCU_DIV_RATIO_DIV_RATIO_MASK+1)) + div = DCU_DIV_RATIO_DIV_RATIO_MASK+1; + + final_rate = parent_rate; + do_div(final_rate, div); + return final_rate; +} + +static int _dcu_pixel_clk_set_rate(struct clk *clk, unsigned long rate) +{ + u64 div, parent_rate; + u32 remainder; + struct dcu_soc *dcu = pixelclk2dcu(clk); + + parent_rate = (unsigned long long)clk_get_rate(clk->parent); + div = parent_rate; + remainder = do_div(div, rate); + /* Round the divider value */ + if (remainder > (rate/2)) + div++; + if (div == 0) /* Min DI disp clock divider is 1 */ + div = 1; + if (div > (DCU_DIV_RATIO_DIV_RATIO_MASK+1)) + return -EINVAL; + + /* While writing to the DIV_RATIO we need to subtract 1 */ + div--; + dcu_write(dcu, (u32)div, DCU_DIV_RATIO_OFFSET); + + return 0; +} + +static int _dcu_pixel_clk_enable(struct clk *clk) +{ + /* We do not have an option to enable or disable this clock */ + /* TODO: Do we need to look at the IOMUX settings to enable the clock + * to the pin/ + */ + return 0; +} + +static void _dcu_pixel_clk_disable(struct clk *clk) +{ + /* We do not have an option to enable or disable this clock */ + /* TODO: Do we need to look at the IOMUX settings to enable the clock + * to the pin/ + */ +} + +static int _dcu_pixel_clk_set_parent(struct clk *clk, struct clk *parent) +{ + /* The parent for the pixel clock is always DCU clock */ + /* However even though we cannot change this we do not have a good way + * of tell which DCU is the parent of this clock. So for now we will go + * ahead and implement this stub just for registering the parent + */ + /* TODO: Get rid of this function and setup the parent of this clock in + * a more elegant fashion + */ + return 0; +} + +#ifdef CONFIG_CLK_DEBUG +#define __INIT_CLK_DEBUG(n) .name = #n, +#else +#define __INIT_CLK_DEBUG(n) +#endif +struct clk dcu_pixel_clk = { + __INIT_CLK_DEBUG(pixel_clk) + .id = 0, + .get_rate = _dcu_pixel_clk_get_rate, + .set_rate = _dcu_pixel_clk_set_rate, + .round_rate = _dcu_pixel_clk_round_rate, + .set_parent = _dcu_pixel_clk_set_parent, + .enable = _dcu_pixel_clk_enable, + .disable = _dcu_pixel_clk_disable, +}; + +struct clk_lookup dcu_lookups[MVF_DCU_MAX_NUM] = { + { + .con_id = "pixel_clk", + }, +#if(MVF_DCU_MAX_NUM > 1) + { + .con_id = "pixel_clk", + }, +#endif +}; + + +static int __devinit dcu_clk_setup_enable(struct dcu_soc *dcu, + struct platform_device *pdev) +{ + char dcu_clk[] = "dcu0_clk_root"; + char dcu_clk_pg[] = "dcu0_clk"; + struct clk *dcu_clk_pg_base; + + dcu_clk[3] += pdev->id; + + dcu_clk_pg_base = clk_get(dcu->platform_dev, dcu_clk_pg); + if (IS_ERR(dcu_clk_pg_base)) { + dev_err(dcu->platform_dev, "dcu_pg_clk_get failed"); + return PTR_ERR(dcu_clk_pg_base); + } + clk_enable(dcu_clk_pg_base); + + dcu->dcu_clk = clk_get(dcu->platform_dev, dcu_clk); + if (IS_ERR(dcu->dcu_clk)) { + dev_err(dcu->platform_dev, "dcu_clk_get failed"); + return PTR_ERR(dcu->dcu_clk); + } + clk_set_rate(dcu->dcu_clk,120000000); + dev_dbg(dcu->platform_dev, "dcu_clk = %lu\n", clk_get_rate(dcu->dcu_clk)); + + dcu->pixel_clk = dcu_pixel_clk; + + dcu_lookups[pdev->id].clk = &dcu->pixel_clk; + dcu_lookups[pdev->id].dev_id = dev_name(dcu->platform_dev); + + clkdev_add(&dcu_lookups[pdev->id]); + clk_debug_register(&dcu->pixel_clk); + clk_enable(dcu->dcu_clk); + clk_set_parent(&dcu->pixel_clk, dcu->dcu_clk); + + return 0; +} + + +/*! + * This function is called to initialize a LCD panel. It is called by the FB driver + * when any display related FB parameters changes or on init. + * Currently applies only to FB0 or FB1 and not for overlay FB's + * + * @param dcu dcu handler + * + * @param pixel_clk Desired pixel clock frequency in Hz. + * + * @param width The width of panel in pixels. + * + * @param height The height of panel in pixels. + * + * @param hStartWidth The number of pixel clocks between the HSYNC + * signal pulse and the start of valid data. + * + * @param hSyncWidth The width of the HSYNC signal in units of pixel + * clocks. + * + * @param hEndWidth The number of pixel clocks between the end of + * valid data and the HSYNC signal for next line. + * + * @param vStartWidth The number of lines between the VSYNC + * signal pulse and the start of valid data. + * + * @param vSyncWidth The width of the VSYNC signal in units of lines + * + * @param vEndWidth The number of lines between the end of valid + * data and the VSYNC signal for next frame. + * + * @param sig Bitfield of signal polarities for LCD interface. + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int32_t dcu_init_panel(struct dcu_soc *dcu, uint32_t pixel_clk, + uint16_t width, uint16_t height, + uint16_t h_start_width, uint16_t h_sync_width, + uint16_t h_end_width, uint16_t v_start_width, + uint16_t v_sync_width, uint16_t v_end_width, + uint32_t v_to_h_sync, dcu_di_signal_cfg_t sig) +{ + uint32_t reg; + uint32_t rounded_pixel_clk; + + /* Setup panel resolution and timings*/ + dev_dbg(dcu->platform_dev, "panel size = %d x %d\n", width, height); + + if ((h_sync_width == 0) || (v_sync_width == 0) || + (h_start_width == 0) || (v_start_width == 0) || + (h_end_width == 0) || (v_end_width == 0)) + { + dev_dbg(dcu->platform_dev, "HSYNC, VSYNC width and front/back porch width should be a minimum of 1\n"); + return -EINVAL; + } + + if((width % 16) != 0) + { + dev_dbg(dcu->platform_dev, "Display width needs to be a multiple of 16\n"); + return -EINVAL; + } + + if((width > MAX_DISP_WIDTH) || (height > MAX_DISP_HEIGHT)) + { + dev_dbg(dcu->platform_dev, "Max width supported is %d, max height supported is %d\n", + MAX_DISP_WIDTH, MAX_DISP_HEIGHT); + return -EINVAL; + } + + if (sig.interlaced) { + dev_dbg(dcu->platform_dev, "DCU does not support interlaced format\n"); + _dcu_unlock(dcu); + return -EINVAL; + } + + _dcu_lock(dcu); + reg = (uint32_t)((width/16) << DCU_DISP_SIZE_DELTA_X_SHIFT) | + (uint32_t)(height << DCU_DISP_SIZE_DELTA_Y_SHIFT); + + dcu_write(dcu, reg, DCU_DISP_SIZE_OFFSET); + + reg = (uint32_t)(h_sync_width << DCU_HSYN_PARA_PW_H_SHIFT) | + (uint32_t)(h_start_width << DCU_HSYN_PARA_BP_H_SHIFT) | + (uint32_t)(h_end_width << DCU_HSYN_PARA_FP_H_SHIFT); + + dcu_write(dcu, reg, DCU_HSYN_PARA_OFFSET); + + reg = (uint32_t)(v_sync_width << DCU_VSYN_PARA_PW_V_SHIFT) | + (uint32_t)(v_start_width << DCU_VSYN_PARA_BP_V_SHIFT) | + (uint32_t)(v_end_width << DCU_VSYN_PARA_FP_V_SHIFT); + + dcu_write(dcu, reg, DCU_VSYN_PARA_OFFSET); + + /* Setup signal polarity data*/ + /* Read the signal polarity register first since its tied in with the PDI*/ + reg = dcu_read(dcu, DCU_SYNPOL_OFFSET); + reg &= ~(DCU_SYNPOL_INV_HS_MASK | DCU_SYNPOL_INV_VS_MASK | + DCU_SYNPOL_NEG_MASK | DCU_SYNPOL_INV_PXCK_MASK); + + if (!sig.Hsync_pol) + reg |= DCU_SYNPOL_INV_HS_MASK; + if (!sig.Vsync_pol) + reg |= DCU_SYNPOL_INV_VS_MASK; + if (sig.data_pol) + reg |= DCU_SYNPOL_NEG_MASK; + if (sig.clk_pol) + reg |= DCU_SYNPOL_INV_PXCK_MASK; + + dcu_write(dcu, reg, DCU_SYNPOL_OFFSET); + _dcu_unlock(dcu); + + /* Init clocking */ + dev_dbg(dcu->platform_dev, "pixel clk = %d\n", pixel_clk); + + clk_set_parent(&dcu->pixel_clk, dcu->dcu_clk); + rounded_pixel_clk = clk_round_rate(&dcu->pixel_clk, pixel_clk); + clk_set_rate(&dcu->pixel_clk, rounded_pixel_clk); + dcu->display_configured = true; + + return 0; +} +EXPORT_SYMBOL(dcu_init_panel); + +static void dcu_blank(struct dcu_soc *dcu, int blank) +{ + struct mvf_dcuv4_platform_data *plat_data = dcu->platform_dev->platform_data; + struct platform_device *pdev = to_platform_device(dcu->platform_dev); + if (plat_data->blank) + plat_data->blank(pdev->id, blank); +} + +void dcu_enable(struct dcu_soc *dcu) +{ + u32 reg; + dcu_blank(dcu, DCU_UNBLANK); + /* TODO: Use count and clock */ + _dcu_lock(dcu); + /* Enable the VSYNC and HSYNC */ + reg = dcu_read(dcu, DCU_DCU_MODE_OFFSET); + reg |= DCU_DCU_MODE_RASTER_EN_MASK; + reg &= ~DCU_DCU_MODE_DCU_MODE_MASK; + reg |= (DCU_NORMAL_MODE << DCU_DCU_MODE_DCU_MODE_SHIFT); + dcu_write(dcu, reg, DCU_DCU_MODE_OFFSET); + _dcu_unlock(dcu); +} +EXPORT_SYMBOL(dcu_enable); + +void dcu_disable(struct dcu_soc *dcu) +{ + u32 reg; + /* TODO: Use count and clock */ + _dcu_lock(dcu); + /* Disable the VSYNC and HSYNC */ + reg = dcu_read(dcu, DCU_DCU_MODE_OFFSET); + reg &= ~DCU_DCU_MODE_RASTER_EN_MASK; + reg &= ~DCU_DCU_MODE_DCU_MODE_MASK; + dcu_write(dcu, reg, DCU_DCU_MODE_OFFSET); + _dcu_unlock(dcu); + dcu_blank(dcu, DCU_BLANK); +} +EXPORT_SYMBOL(dcu_disable); + +void dcu_uninit_panel(struct dcu_soc *dcu) +{ + _dcu_lock(dcu); + + /* TODO: Support blanking the display??? */ + + _dcu_unlock(dcu); + +} +EXPORT_SYMBOL(dcu_uninit_panel); + +struct dcu_soc *dcu_get_soc(int id) +{ + if (id >= MVF_DCU_MAX_NUM) + return ERR_PTR(-ENODEV); + else if (!dcu_array[id].online) + return ERR_PTR(-ENODEV); + else + return &(dcu_array[id]); +} +EXPORT_SYMBOL_GPL(dcu_get_soc); + +static irqreturn_t dcu_layer_transfer_complete_irq_handler(int irq, void *dev_id) +{ + struct dcu_soc *dcu = dev_id; + complete(&dcu->layer_transfer_complete); + dcu_disable_irq(dcu, irq); + return IRQ_HANDLED; +} + +/*! + * This function is called by the driver framework to initialize the DCU + * hardware. This function performs the following operations: + * 1. Initialize software DCU module + * - Setup use count, +* 2. Register the IRQ handler +* 3. Remap the IO memory +* 4. Initialize DCU clock ?? +* 5. Reset the DCU internal state and critical registers +* 6. Registering the DCU device driver + * + * @param pdev The device structure for the DCU passed in by the + * driver framework. + * + * @return Returns 0 on success or negative error code on error + */ +static int __devinit dcu_probe(struct platform_device *pdev) +{ + struct mvf_dcuv4_platform_data *plat_data = pdev->dev.platform_data; + struct dcu_soc *dcu; + struct resource *res; + unsigned long dcu_base; + unsigned int layer_count; + int ret = 0; + + if (pdev->id >= MVF_DCU_MAX_NUM) + return -ENODEV; + + dcu = &dcu_array[pdev->id]; + memset(dcu, 0, sizeof(struct dcu_soc)); + + spin_lock_init(&dcu->spin_lock); + mutex_init(&dcu->mutex_lock); + atomic_set(&dcu->dcu_use_count, 1); + atomic_set(&dcu->layer_use_count, 1); + + /* Only one rev of DCU4, do not have any variants yet + * so store and forget */ + g_dcu_hw_rev = plat_data->rev; + + dcu->platform_dev = &pdev->dev; + + dcu->irq_generic = platform_get_irq(pdev, 0); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (!res || dcu->irq_generic < 0) { + ret = -ENODEV; + goto failed_get_res; + } + + if (request_irq(dcu->irq_generic, dcu_irq_handler, 0, pdev->name, dcu) != 0) { + dev_err(dcu->platform_dev, "request DCU generic interrupt failed\n"); + ret = -EBUSY; + goto failed_req_irq_generic; + } + + dcu_base = res->start; + + dcu->dcu_base_reg = ioremap(dcu_base, SZ_8K); + dcu->clut_tile_mem_base = ioremap(dcu_base + DCU_CLUT_OFFSET, SZ_8K); + dcu->gamma_r_mem_base = ioremap(dcu_base + DCU_GAMMARED_OFFSET, PAGE_SIZE); + dcu->gamma_g_mem_base = ioremap(dcu_base + DCU_GAMMAGREEN_OFFSET, PAGE_SIZE); + dcu->gamma_b_mem_base = ioremap(dcu_base + DCU_GAMMABLUE_OFFSET, PAGE_SIZE); + dcu->cursor_mem_base = ioremap(dcu_base + DCU_CURSOR_OFFSET, PAGE_SIZE); + + if (!dcu->dcu_base_reg || !dcu->clut_tile_mem_base || !dcu->gamma_r_mem_base || + !dcu->gamma_g_mem_base || !dcu->gamma_b_mem_base || !dcu->cursor_mem_base) { + ret = -ENOMEM; + goto failed_ioremap; + } + + dev_dbg(dcu->platform_dev, "DCU Base Regs = %p\n", dcu->dcu_base_reg); + dev_dbg(dcu->platform_dev, "DCU CLUT mem = %p\n", dcu->clut_tile_mem_base); + dev_dbg(dcu->platform_dev, "DCU Gamma R mem = %p\n", dcu->gamma_r_mem_base); + dev_dbg(dcu->platform_dev, "DCU Gamma G mem = %p\n", dcu->gamma_g_mem_base); + dev_dbg(dcu->platform_dev, "DCU Gamma B mem = %p\n", dcu->gamma_b_mem_base); + dev_dbg(dcu->platform_dev, "DCU Cursor mem = %p\n", dcu->cursor_mem_base); + + ret = dcu_clk_setup_enable(dcu, pdev); + if (ret < 0) { + dev_err(dcu->platform_dev, "dcu clk setup failed\n"); + goto failed_clk_setup; + } + + platform_set_drvdata(pdev, dcu); + + /* dcu_reset(dcu); */ + + /* Layer config registers are RAM with unknown values. + * Initialize layers to be off, this is a must since reset value is unknown */ + for(layer_count=0;layer_count<MAX_DCU_LAYERS;layer_count++) + { + dcu_write(dcu, 0, DCU_CTRLDESCLx_y_OFFSET(layer_count,DCU_CTRLDESCL_OFFSET4)); + } + register_dcu_device(dcu, pdev->id); + /* TODO: Cleanup and move to TCON header file/driver */ + tcon0_ctrl1 = ioremap(TCON0_BASE + TCON_CTRL1_OFFSET, PAGE_SIZE); + writel(TCON_CTRL1_TCON_BYPASS_MASK, tcon0_ctrl1); + + /*Setup the layer transfer IRQ */ + dcu->dcu_layer_transfer_complete_irq = DCU_IRQ_LYR_TRANS_FINISH; + if (dcu_request_irq(dcu, dcu->dcu_layer_transfer_complete_irq, dcu_layer_transfer_complete_irq_handler, 0, + MVF_DCU, dcu) != 0) { + dev_err(dcu->platform_dev, "Error registering DCU irq %d\n", + dcu->dcu_layer_transfer_complete_irq); + goto failed_clk_setup; + } + dcu_disable_irq(dcu, dcu->dcu_layer_transfer_complete_irq); + + + /* TODO: Why is the clock being disabled? */ + //clk_disable(dcu->dcu_clk); + dcu->online = true; + dcu->display_configured = false; + + return ret; + +failed_clk_setup: + iounmap(dcu->dcu_base_reg); + iounmap(dcu->clut_tile_mem_base); + iounmap(dcu->gamma_r_mem_base); + iounmap(dcu->gamma_g_mem_base); + iounmap(dcu->gamma_b_mem_base); + iounmap(dcu->cursor_mem_base); +failed_ioremap: + if (dcu->irq_generic) + free_irq(dcu->irq_generic, dcu); +failed_req_irq_generic: +failed_get_res: + return ret; +} + +int __devexit dcu_remove(struct platform_device *pdev) +{ + struct dcu_soc *dcu = platform_get_drvdata(pdev); + + unregister_dcu_device(dcu, pdev->id); + + if (dcu->irq_generic) + free_irq(dcu->irq_generic, dcu); + + clk_put(dcu->dcu_clk); + + iounmap(dcu->dcu_base_reg); + iounmap(dcu->clut_tile_mem_base); + iounmap(dcu->gamma_r_mem_base); + iounmap(dcu->gamma_g_mem_base); + iounmap(dcu->gamma_b_mem_base); + iounmap(dcu->cursor_mem_base); + return 0; +} + +#ifdef DCU_UNIT_TEST +static const uint32_t white_color[]= { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF +}; + +static const uint32_t black_color[]= { + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 +}; + +void dcu_unit_test_config_display(struct dcu_soc *dcu) +{ + dcu_di_signal_cfg_t sig; + sig.Hsync_pol = 0; + sig.Vsync_pol = 0; + dcu_init_panel(dcu, 11000000, 480, 272, 2, 41, 2, 1, + 2, 1, 0, sig); +} + +void dcu_unit_test(struct dcu_soc *dcu) +{ + dcu_color_t color; + color.color_r = 0xFF; + + //Color bar mode test + dcu_enable_colorbar_mode(dcu); + printk("Colorbar mode enabled\n"); + msleep(5000); + dcu_disable_colorbar_mode(dcu); + printk("Colorbar mode disabled\n"); + msleep(5000); + + //Background color test + dcu_set_bgnd_color(dcu, color); + printk("Background color set to red\n"); + dcu_enable(dcu); + msleep(5000); + color.color_r = 0x00; + color.color_g = 0xFF; + dcu_set_bgnd_color(dcu, color); + printk("Background color set to green\n"); + msleep(5000); + + //Layer test + dcu_init_layer(dcu, 0, + V4L2_PIX_FMT_RGB32, 16, 16, + (dma_addr_t)black_color, + 0, 0); + dcu_enable_layer(dcu,0); + printk("Layer 0 enabled, 16x16 Black square @top left corner\n"); + msleep(5000); + dcu_init_layer(dcu, 1, + V4L2_PIX_FMT_RGB32, 16, 16, + (dma_addr_t)black_color, + 16, 16); + dcu_enable_layer(dcu,1); + printk("Layer 1 enabled, 16x16 Black square @offset 16x16 from top left corner\n"); + msleep(5000); + dcu_set_layer_position(dcu, 1, 32,32); + printk("Layer 1 position now @offset 32x32 from top left corner\n"); + dcu_update_layer_buffer(dcu, 1, (dma_addr_t)white_color); + printk("Layer 1 image updated to white color\n"); + msleep(5000); + dcu_config_layer_alpha(dcu, 1, 0x3F, ALPHA_BLEND_ENABLED); + printk("Layer 1 alpha set to 0x3F\n"); + msleep(5000); + + //Test enable disable of DCU and layer + dcu_disable(dcu); + printk("DCU disabled\n"); + msleep(5000); + printk("DCU enabled\n"); + dcu_enable(dcu); + msleep(5000); + dcu_disable_layer(dcu, 1, 0); + dcu_uninit_layer(dcu, 1); + printk("Layer 1 disabled and uninit\n"); + msleep(5000); + dcu_disable_layer(dcu, 0, 0); + dcu_uninit_layer(dcu, 0); + printk("Layer 0 disabled and uninit\n"); + dcu_disable(dcu); + printk("DCU disabled\n"); +} +#endif + +void dcu_dump_registers(struct dcu_soc *dcu) +{ + /* Display related */ + dev_dbg(dcu->platform_dev, "DCU_DISP_SIZE = \t0x%08X\n", dcu_read(dcu, DCU_DISP_SIZE_OFFSET)); + dev_dbg(dcu->platform_dev, "DCU_HSYN_PARA = \t0x%08X\n", dcu_read(dcu, DCU_HSYN_PARA_OFFSET)); + dev_dbg(dcu->platform_dev, "DCU_VSYN_PARA = \t0x%08X\n", dcu_read(dcu, DCU_VSYN_PARA_OFFSET)); + dev_dbg(dcu->platform_dev, "DCU_SYNPOL = \t0x%08X\n", dcu_read(dcu, DCU_SYNPOL_OFFSET)); + dev_dbg(dcu->platform_dev, "DCU_DIV_RATIO = \t0x%08X\n", dcu_read(dcu, DCU_DIV_RATIO_OFFSET)); + + /* Global DCU related */ + dev_dbg(dcu->platform_dev, "DCU_DCU_MODE = \t0x%08X\n", dcu_read(dcu, DCU_DCU_MODE_OFFSET)); + dev_dbg(dcu->platform_dev, "DCU_BGND = \t0x%08X\n", dcu_read(dcu, DCU_BGND_OFFSET)); + + /* Interrupt status */ + dev_dbg(dcu->platform_dev, "DCU_PARR_ERR_STATUS1 = \t0x%08X\n", dcu_read(dcu, DCU_PARR_ERR_STATUS1_OFFSET)); + dev_dbg(dcu->platform_dev, "DCU_PARR_ERR_STATUS2 = \t0x%08X\n", dcu_read(dcu, DCU_PARR_ERR_STATUS2_OFFSET)); + dev_dbg(dcu->platform_dev, "DCU_PARR_ERR_STATUS3 = \t0x%08X\n", dcu_read(dcu, DCU_PARR_ERR_STATUS3_OFFSET)); + + /* Layer 0 info*/ + dev_dbg(dcu->platform_dev, "DCU_CTRLDESCL0_OFFSET1 = \t0x%08X\n", + dcu_read(dcu, DCU_CTRLDESCLx_y_OFFSET(0,DCU_CTRLDESCL_OFFSET1))); + dev_dbg(dcu->platform_dev, "DCU_CTRLDESCL0_OFFSET2 = \t0x%08X\n", + dcu_read(dcu, DCU_CTRLDESCLx_y_OFFSET(0,DCU_CTRLDESCL_OFFSET2))); + dev_dbg(dcu->platform_dev, "DCU_CTRLDESCL0_OFFSET3 = \t0x%08X\n", + dcu_read(dcu, DCU_CTRLDESCLx_y_OFFSET(0,DCU_CTRLDESCL_OFFSET3))); + dev_dbg(dcu->platform_dev, "DCU_CTRLDESCL0_OFFSET4 = \t0x%08X\n", + dcu_read(dcu, DCU_CTRLDESCLx_y_OFFSET(0,DCU_CTRLDESCL_OFFSET4))); + +} + + +/*! + * This function is called to initialize buffer(s) for logical DCU layer. + * + * @param dcu dcu handler + * + * @param layer Input parameter for the logical layer ID. + * + * @param pixel_fmt Input parameter for pixel format of buffer. + * Pixel format is a FOURCC ASCII code. + * + * @param width Input parameter for width of buffer in pixels. + * + * @param height Input parameter for height of buffer in pixels. + * + * @param phyaddr Input parameter buffer physical address. + * + * @param u private u offset for additional cropping, + * zero if not used. + * + * @param v private v offset for additional cropping, + * zero if not used. + * + * @return Returns 0 on success or negative error code on fail + */ +int32_t dcu_init_layer(struct dcu_soc *dcu, uint8_t layer, + uint32_t pixel_fmt, + uint16_t width, uint16_t height, + dma_addr_t phyaddr_0, + int16_t x_pos, int16_t y_pos) +{ + int ret = 0; + uint32_t reg; + uint8_t layer_encoding_format; + uint8_t min_width; + + dev_dbg(dcu->platform_dev, "init layer = %d\n", layer); + + /* Check for valid layer number and resolution as supported by DCU */ + if(layer >= MAX_DCU_LAYERS) + { + dev_err(dcu->platform_dev, "Invalid layer number %d\n", layer); + ret = -EINVAL; + goto err; + } + + if((width > MAX_DISP_WIDTH) || (height > MAX_DISP_HEIGHT)) + { + dev_err(dcu->platform_dev, "Max width supported is %d, max height supported is %d\n", + MAX_DISP_WIDTH, MAX_DISP_HEIGHT); + ret = -EINVAL; + goto err; + } + + if((x_pos > (MAX_DISP_WIDTH-1) ) || (x_pos < -MAX_DISP_WIDTH) || + (y_pos > (MAX_DISP_HEIGHT-1)) || (y_pos < -MAX_DISP_HEIGHT)) + { + dev_err(dcu->platform_dev, "Offset is out of range. Range for x offset is %d to %d\n \ + y offset is %d to %d\n", (MAX_DISP_WIDTH-1), -MAX_DISP_WIDTH, + (MAX_DISP_HEIGHT-1), -MAX_DISP_HEIGHT); + ret = -EINVAL; + goto err; + } + + /* Check if the DCU supports the pixel format provided by the user */ + layer_encoding_format = format_to_layerencoding(pixel_fmt); + + if(layer_encoding_format == BPP_INVALID) + { + dev_err(dcu->platform_dev, "Image format %d is not supported by hardware\n", pixel_fmt); + ret = -EINVAL; + goto err; + } + + min_width = layerencoding_to_min_width(layer_encoding_format); + if(width % min_width) + { + dev_err(dcu->platform_dev, "Width must be multiple integer multiple of the number of\ + pixels that are represented by a 32-bit word: %d\n", min_width); + ret = -EINVAL; + goto err; + } + + _dcu_get(dcu); + _dcu_lock(dcu); + + /* Check if layer is already being used */ + reg = dcu_read(dcu, DCU_CTRLDESCLx_y_OFFSET(layer,DCU_CTRLDESCL_OFFSET4)); + if (reg & DCU_CTRLDESCLn_4_EN_MASK) { + dev_warn(dcu->platform_dev, "Warning: layer already initialized %d\n", layer); + } + + /* Write all the parameters passed in */ + reg = (uint32_t)(layer_encoding_format << DCU_CTRLDESCLn_4_BPP_SHIFT); + dcu_write(dcu, reg, DCU_CTRLDESCLx_y_OFFSET(layer,DCU_CTRLDESCL_OFFSET4)); + + reg = (uint32_t)(width << DCU_CTRLDESCLn_1_WIDTH_SHIFT) | + (uint32_t)(height << DCU_CTRLDESCLn_1_HEIGHT_SHIFT); + dcu_write(dcu, reg, DCU_CTRLDESCLx_y_OFFSET(layer,DCU_CTRLDESCL_OFFSET1)); + + dcu_write(dcu, phyaddr_0, DCU_CTRLDESCLx_y_OFFSET(layer,DCU_CTRLDESCL_OFFSET3)); + + /* Default offset from display is 0,0. User will can change this using IOCTL */ + /* TODO: Check if 2's complement format will cause any issues */ + reg = (uint32_t)(y_pos << DCU_CTRLDESCLn_2_POSY_SHIFT) | + (uint32_t)(x_pos << DCU_CTRLDESCLn_2_POSX_SHIFT); + + dcu_write(dcu, reg, DCU_CTRLDESCLx_y_OFFSET(layer,DCU_CTRLDESCL_OFFSET2)); + + _dcu_unlock(dcu); + +err: + return ret; +} +EXPORT_SYMBOL(dcu_init_layer); + +/*! + * This function is called to uninitialize a DCU layer. + * + * @param dcu dcu handler + * @param layer Input parameter for the logical layer ID to uninit. + */ +void dcu_uninit_layer(struct dcu_soc *dcu, uint8_t layer) +{ + uint32_t reg; + uint32_t reg_offset_count; + + /* Check for valid layer numbe as supported by DCU */ + if(layer >= MAX_DCU_LAYERS) + { + dev_err(dcu->platform_dev, "Invalid layer number %d\n", layer); + return; + } + + _dcu_lock(dcu); + reg = dcu_read(dcu, DCU_CTRLDESCLx_y_OFFSET(layer,DCU_CTRLDESCL_OFFSET4)); + if (reg & DCU_CTRLDESCLn_4_EN_MASK) { + dev_err(dcu->platform_dev, "Disable layer first %d\n", layer); + _dcu_unlock(dcu); + return; + } + + /* Clear out all layer data */ + for(reg_offset_count=0; reg_offset_count <= DCU_CTRLDESCL_OFFSET_MAX; reg_offset_count++) + dcu_write(dcu, 0, DCU_CTRLDESCLx_y_OFFSET(layer, reg_offset_count)); + + /* TODO: Do we need to decrement the usecount for each layer + * after we uninit it? + */ + + /* TODO: Do we need to disable any interrupts here? */ + + /* TODO: Do we need to disable the DCU/clocks once all the layers are disabled */ + + /* TODO: Do we need to clear any status registers associated with this layer? */ + + /* Sample code for waiting from IPU + + if(wait_for_stop) + { + int timeout = 50; + + dcu_write(dcu, IPUIRQ_2_MASK(IPU_IRQ_BG_SYNC_EOF), + IPUIRQ_2_STATREG(IPU_IRQ_BG_SYNC_EOF)); + while ((dcu_read(dcu, IPUIRQ_2_STATREG(IPU_IRQ_BG_SYNC_EOF)) & + IPUIRQ_2_MASK(IPU_IRQ_BG_SYNC_EOF)) == 0) { + msleep(10); + timeout -= 10; + if (timeout <= 0) { + dev_err(dcu->platform_dev, "warning: wait for bg sync eof timeout\n"); + break; + } + } + } + */ + _dcu_unlock(dcu); + _dcu_put(dcu); + + //WARN_ON(dcu->dcu_use_count < 0); +} +EXPORT_SYMBOL(dcu_uninit_layer); + +/*! + * This function is called to update the physical address of a buffer for + * a logical DCU layer. + * + * @param dcu dcu handler + * @param layer Input parameter for the logical layer ID. + * @param phyaddr Input parameter buffer physical address + * @return Returns 0 on success or negative error code on fail + */ +int32_t dcu_update_layer_buffer(struct dcu_soc *dcu, uint8_t layer, + dma_addr_t phyaddr) +{ + /* Check for valid layer numbe as supported by DCU */ + if(layer >= MAX_DCU_LAYERS) + { + dev_err(dcu->platform_dev, "Invalid layer number %d\n", layer); + return -EINVAL; + } + + _dcu_lock(dcu); + + dcu_write(dcu, phyaddr, DCU_CTRLDESCLx_y_OFFSET(layer,DCU_CTRLDESCL_OFFSET3)); + + _dcu_unlock(dcu); + + return 0; +} +EXPORT_SYMBOL(dcu_update_layer_buffer); + + +/*! + * This function is called to initialize a buffer for logical DCU layer. + * + * @param dcu dcu handler + * @param layer Input parameter for the logical layer ID. + * @param x_pos x offset from display start, can be positive or negative + * @param y_pos y offset from display start, can be positive or negative + * @return Returns 0 on success or negative error code on fail + */ +int32_t dcu_set_layer_position(struct dcu_soc *dcu, uint8_t layer, int16_t x_pos, + int16_t y_pos) +{ + uint32_t reg; + /* Check for valid layer number as supported by DCU */ + if(layer >= MAX_DCU_LAYERS) + { + dev_err(dcu->platform_dev, "Invalid layer number %d\n", layer); + return -EINVAL; + } + + if((x_pos > (MAX_DISP_WIDTH-1) ) || (x_pos < -MAX_DISP_WIDTH) || + (y_pos > (MAX_DISP_HEIGHT-1)) || (y_pos < -MAX_DISP_HEIGHT)) + { + dev_err(dcu->platform_dev, "Offset is out of range. Range for x offset is %d to %d\n \ + y offset is %d to %d\n", (MAX_DISP_WIDTH-1), -MAX_DISP_WIDTH, + (MAX_DISP_HEIGHT-1), -MAX_DISP_HEIGHT); + return -EINVAL; + } + + _dcu_lock(dcu); + + /* TODO: Do we need to check 2's complement format for negative position */ + reg = (uint32_t)(y_pos << DCU_CTRLDESCLn_2_POSY_SHIFT) | + (uint32_t)(x_pos << DCU_CTRLDESCLn_2_POSX_SHIFT); + + dcu_write(dcu, reg, DCU_CTRLDESCLx_y_OFFSET(layer,DCU_CTRLDESCL_OFFSET2)); + + _dcu_unlock(dcu); + return 0; +} +EXPORT_SYMBOL(dcu_set_layer_position); + + +int32_t dcu_get_layer_position(struct dcu_soc *dcu, uint8_t layer, int16_t *x_pos, + int16_t *y_pos) +{ + uint32_t reg; + /* Check for valid layer number as supported by DCU */ + if(layer >= MAX_DCU_LAYERS) + { + dev_err(dcu->platform_dev, "Invalid layer number %d\n", layer); + return -EINVAL; + } + + _dcu_lock(dcu); + reg = dcu_read(dcu, DCU_CTRLDESCLx_y_OFFSET(layer,DCU_CTRLDESCL_OFFSET2)); + _dcu_unlock(dcu); + + *y_pos = (reg & DCU_CTRLDESCLn_2_POSY_MASK) >> DCU_CTRLDESCLn_2_POSY_SHIFT; + *x_pos = (reg & DCU_CTRLDESCLn_2_POSX_MASK) >> DCU_CTRLDESCLn_2_POSX_SHIFT; + + return 0; +} +EXPORT_SYMBOL(dcu_get_layer_position); + +/*! + * This function check whether a logical layer was enabled. + * + * @param dcu dcu handler + * @param layer Input parameter for the logical layer ID. + * + * @return This function returns 1 while request layer is enabled or + * 0 for not enabled. + */ +int32_t dcu_is_layer_enabled(struct dcu_soc *dcu, uint8_t layer) +{ + uint32_t reg; + /* Check for valid layer number as supported by DCU */ + if(layer >= MAX_DCU_LAYERS) + { + dev_err(dcu->platform_dev, "Invalid layer number %d\n", layer); + return -EINVAL; + } + + reg = dcu_read(dcu, DCU_CTRLDESCLx_y_OFFSET(layer,DCU_CTRLDESCL_OFFSET4)); + if (reg & DCU_CTRLDESCLn_4_EN_MASK) + return 1; + + return 0; +} +EXPORT_SYMBOL(dcu_is_layer_enabled); + +/*! + * This function enables a logical layer. + * + * @param dcu dcu handler + * @param layer Input parameter for the logical layer ID. + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int32_t dcu_enable_layer(struct dcu_soc *dcu, uint8_t layer) +{ + u32 reg; + /* Check for valid layer number as supported by DCU */ + if(layer >= MAX_DCU_LAYERS) + { + dev_err(dcu->platform_dev, "Invalid layer number %d\n", layer); + return -EINVAL; + } + + _dcu_lock(dcu); + reg = dcu_read(dcu, DCU_CTRLDESCLx_y_OFFSET(layer,DCU_CTRLDESCL_OFFSET4)); + if (reg & DCU_CTRLDESCLn_4_EN_MASK) + { + dev_err(dcu->platform_dev, "Warning: layer already enabled %d\n", layer); + _dcu_unlock(dcu); + return -EACCES; + } + + /* TODO: Do we need to check the usecount for each layer + * to ensure it is initialized before it is enabled? + */ + reg |= DCU_CTRLDESCLn_4_EN_MASK; + dcu_write(dcu, reg, DCU_CTRLDESCLx_y_OFFSET(layer,DCU_CTRLDESCL_OFFSET4)); + + _dcu_unlock(dcu); + return 0; +} +EXPORT_SYMBOL(dcu_enable_layer); + +/*! + * This function disables a logical layer. + * + * @param dcu dcu handler + * @param layer Input parameter for the logical layer ID. + * + * @param wait_for_stop Flag to set whether to wait for layer end + * of frame or return immediately. + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int32_t dcu_disable_layer(struct dcu_soc *dcu, uint8_t layer, bool wait_for_stop) +{ + uint32_t reg; + + /* Check for valid layer number as supported by DCU */ + if(layer >= MAX_DCU_LAYERS) + { + dev_err(dcu->platform_dev, "Invalid layer number %d\n", layer); + return -EINVAL; + } + + _dcu_lock(dcu); + + reg = dcu_read(dcu, DCU_CTRLDESCLx_y_OFFSET(layer,DCU_CTRLDESCL_OFFSET4)); + if (!(reg & DCU_CTRLDESCLn_4_EN_MASK)) + { + dev_err(dcu->platform_dev, "Warning: layer already disabled %d\n", layer); + _dcu_unlock(dcu); + return -EACCES; + } + + reg &= ~DCU_CTRLDESCLn_4_EN_MASK; + dcu_write(dcu, reg, DCU_CTRLDESCLx_y_OFFSET(layer,DCU_CTRLDESCL_OFFSET4)); + + _dcu_unlock(dcu); + + return 0; +} +EXPORT_SYMBOL(dcu_disable_layer); + +/*! + * This function sets the DCU Background color. + * + * @param dcu dcu handler + * @param color desired rgb color for background + * + */ +void dcu_set_bgnd_color(struct dcu_soc *dcu, dcu_color_t color) +{ + uint32_t reg; + + _dcu_lock(dcu); + + reg = (uint32_t)(color.color_r << DCU_BGND_BGND_R_SHIFT) | + (uint32_t)(color.color_g << DCU_BGND_BGND_G_SHIFT) | + (uint32_t)(color.color_b << DCU_BGND_BGND_B_SHIFT); + dcu_write(dcu, reg, DCU_BGND_OFFSET); + + _dcu_unlock(dcu); +} +EXPORT_SYMBOL(dcu_set_bgnd_color); + +/*! + * This function configures alpha . + * + * @param dcu dcu handler + * @param layer layer id + * @param alpha_value desired layer transparency value + * @param aa mode of alpha blending + * + */ +int32_t dcu_config_layer_alpha(struct dcu_soc *dcu, uint8_t layer, + uint8_t alpha_value, alpha_aa_config aa) +{ + uint32_t reg; + /* Check for valid layer number as supported by DCU */ + if(layer >= MAX_DCU_LAYERS) + { + dev_err(dcu->platform_dev, "Invalid layer number %d\n", layer); + return -EINVAL; + } + + _dcu_lock(dcu); + reg = dcu_read(dcu, DCU_CTRLDESCLx_y_OFFSET(layer,DCU_CTRLDESCL_OFFSET4)); + reg |= (uint32_t)(alpha_value << DCU_CTRLDESCLn_4_TRANS_SHIFT); + reg |= (uint32_t)(aa << DCU_CTRLDESCLn_4_AB_SHIFT); + dcu_write(dcu, reg, DCU_CTRLDESCLx_y_OFFSET(layer,DCU_CTRLDESCL_OFFSET4)); + _dcu_unlock(dcu); + + return 0; +} +EXPORT_SYMBOL(dcu_config_layer_alpha); + +/*! + * This function sets chroma keying RGB color values + * + * @param dcu dcu handler + * @param layer layer id + * @param color_max max color value + * @param color_min min color value + * @param enable enable or disable chroma keying + */ +int32_t dcu_set_chroma_keying(struct dcu_soc *dcu, uint8_t layer, + dcu_color_t color_max, dcu_color_t color_min, bool enable) +{ + uint32_t reg; + /* Check for valid layer number as supported by DCU */ + if(layer >= MAX_DCU_LAYERS) + { + dev_err(dcu->platform_dev, "Invalid layer number %d\n", layer); + return -EINVAL; + } + + _dcu_lock(dcu); + reg = (uint32_t)(color_max.color_r << DCU_BGND_BGND_R_SHIFT) | + (uint32_t)(color_max.color_g << DCU_BGND_BGND_G_SHIFT) | + (uint32_t)(color_max.color_b << DCU_BGND_BGND_B_SHIFT); + dcu_write(dcu, reg, DCU_CTRLDESCLx_y_OFFSET(layer,DCU_CTRLDESCL_OFFSET5)); + + reg = (uint32_t)(color_min.color_r << DCU_BGND_BGND_R_SHIFT) | + (uint32_t)(color_min.color_g << DCU_BGND_BGND_G_SHIFT) | + (uint32_t)(color_min.color_b << DCU_BGND_BGND_B_SHIFT); + dcu_write(dcu, reg, DCU_CTRLDESCLx_y_OFFSET(layer,DCU_CTRLDESCL_OFFSET6)); + + reg = dcu_read(dcu, DCU_CTRLDESCLx_y_OFFSET(layer,DCU_CTRLDESCL_OFFSET4)); + if(enable) + reg |= DCU_CTRLDESCLn_4_BB_MASK; + else + reg &= ~DCU_CTRLDESCLn_4_BB_MASK; + dcu_write(dcu, reg, DCU_CTRLDESCLx_y_OFFSET(layer,DCU_CTRLDESCL_OFFSET4)); + _dcu_unlock(dcu); + + return 0; +} +EXPORT_SYMBOL(dcu_set_chroma_keying); + +static irqreturn_t dcu_irq_handler(int irq, void *desc) +{ + struct dcu_soc *dcu = desc; + uint32_t line; + irqreturn_t result = IRQ_NONE; + uint32_t int_stat; + unsigned long lock_flags = 0; + + //Param 1 interrupts + spin_lock_irqsave(&dcu->spin_lock, lock_flags); + int_stat = dcu_read(dcu, DCU_PARR_ERR_STATUS1_OFFSET); + int_stat &= ~(dcu_read(dcu, DCU_MASK_PARR_ERR_STATUS1_OFFSET)); + + if (int_stat) { + dcu_write(dcu, int_stat, DCU_PARR_ERR_STATUS1_OFFSET); + dev_err(dcu->platform_dev, + "DCU Error - DCU_PARR_ERR_STATUS1 = 0x%08X\n", int_stat); + /* Disable interrupts so we only get error once */ + int_stat |= + dcu_read(dcu, DCU_PARR_ERR_STATUS1_OFFSET); + dcu_write(dcu, int_stat, DCU_MASK_PARR_ERR_STATUS1_OFFSET); + } + spin_unlock_irqrestore(&dcu->spin_lock, lock_flags); + + //Param 2 interrupts + spin_lock_irqsave(&dcu->spin_lock, lock_flags); + int_stat = dcu_read(dcu, DCU_PARR_ERR_STATUS2_OFFSET); + int_stat &= ~(dcu_read(dcu, DCU_MASK_PARR_ERR_STATUS2_OFFSET)); + + if (int_stat) { + dcu_write(dcu, int_stat, DCU_PARR_ERR_STATUS2_OFFSET); + dev_err(dcu->platform_dev, + "DCU Error - DCU_PARR_ERR_STATUS2 = 0x%08X\n", int_stat); + /* Disable interrupts so we only get error once */ + int_stat |= + dcu_read(dcu, DCU_PARR_ERR_STATUS2_OFFSET); + dcu_write(dcu, int_stat, DCU_MASK_PARR_ERR_STATUS2_OFFSET); + } + spin_unlock_irqrestore(&dcu->spin_lock, lock_flags); + + //Param 3 interrupts + spin_lock_irqsave(&dcu->spin_lock, lock_flags); + int_stat = dcu_read(dcu, DCU_PARR_ERR_STATUS3_OFFSET); + int_stat &= ~(dcu_read(dcu, DCU_MASK_PARR_ERR_STATUS3_OFFSET)); + + if (int_stat) { + dcu_write(dcu, int_stat, DCU_PARR_ERR_STATUS3_OFFSET); + dev_err(dcu->platform_dev, + "DCU Error - DCU_PARR_ERR_STATUS3 = 0x%08X\n", int_stat); + /* Disable interrupts so we only get error once */ + int_stat |= + dcu_read(dcu, DCU_PARR_ERR_STATUS3_OFFSET); + dcu_write(dcu, int_stat, DCU_MASK_PARR_ERR_STATUS3_OFFSET); + } + spin_unlock_irqrestore(&dcu->spin_lock, lock_flags); + + spin_lock_irqsave(&dcu->spin_lock, lock_flags); + int_stat = dcu_read(dcu, DCU_INT_STATUS_OFFSET); + int_stat &= ~(dcu_read(dcu, DCU_INT_MASK_OFFSET)); + dcu_write(dcu, int_stat, DCU_INT_STATUS_OFFSET); + spin_unlock_irqrestore(&dcu->spin_lock, lock_flags); + while ((line = ffs(int_stat)) != 0) { + line--; + int_stat &= ~(1UL << line); + result |= + dcu->irq_list[line].handler(line, + dcu->irq_list[line]. + dev_id); + } + + return result; +} + +/*! + * This function enables the interrupt for the specified interrupt line. + * The interrupt lines are defined in \b dcu_irq_line enum. + * + * @param dcu dcu handler + * @param irq Interrupt line to enable interrupt for. + * + */ +void dcu_enable_irq(struct dcu_soc *dcu, uint32_t irq) +{ + uint32_t reg; + unsigned long lock_flags; + + _dcu_get(dcu); + + spin_lock_irqsave(&dcu->spin_lock, lock_flags); + + /* TODO: Make this code more flexible to handle PDI and Error IRQ */ + reg = dcu_read(dcu, DCU_INT_MASK_OFFSET); + reg &= ~DCU_IRQ_MASK(irq); + dcu_write(dcu, reg, DCU_INT_MASK_OFFSET); + + spin_unlock_irqrestore(&dcu->spin_lock, lock_flags); + + _dcu_put(dcu); +} +EXPORT_SYMBOL(dcu_enable_irq); + +/*! + * This function disables the interrupt for the specified interrupt line. + * The interrupt lines are defined in \b dcu_irq_line enum. + * + * @param dcu dcu handler + * @param irq Interrupt line to disable interrupt for. + * + */ +void dcu_disable_irq(struct dcu_soc *dcu, uint32_t irq) +{ + uint32_t reg; + unsigned long lock_flags; + + _dcu_get(dcu); + + spin_lock_irqsave(&dcu->spin_lock, lock_flags); + + /* TODO: Make this code more flexible to handle PDI and Error IRQ */ + reg = dcu_read(dcu, DCU_INT_MASK_OFFSET); + reg |= DCU_IRQ_MASK(irq); + dcu_write(dcu, reg, DCU_INT_MASK_OFFSET); + + spin_unlock_irqrestore(&dcu->spin_lock, lock_flags); + + _dcu_put(dcu); +} +EXPORT_SYMBOL(dcu_disable_irq); + +/*! + * This function clears the interrupt for the specified interrupt line. + * The interrupt lines are defined in \b dcu_irq_line enum. + * + * @param dcu dcu handler + * @param irq Interrupt line to clear interrupt for. + * + */ +void dcu_clear_irq(struct dcu_soc *dcu, uint32_t irq) +{ + unsigned long lock_flags; + + _dcu_get(dcu); + + spin_lock_irqsave(&dcu->spin_lock, lock_flags); + + /* TODO: Make this code more flexible to handle PDI and Error IRQ */ + dcu_write(dcu, DCU_IRQ_MASK(irq), DCU_INT_STATUS_OFFSET); + + spin_unlock_irqrestore(&dcu->spin_lock, lock_flags); + + _dcu_put(dcu); +} +EXPORT_SYMBOL(dcu_clear_irq); + +/*! + * This function returns the current interrupt status for the specified + * interrupt line. The interrupt lines are defined in \b dcu_irq_line enum. + * + * @param dcu dcu handler + * @param irq Interrupt line to get status for. + * + * @return Returns true if the interrupt is pending/asserted or false if + * the interrupt is not pending. + */ +bool dcu_get_irq_status(struct dcu_soc *dcu, uint32_t irq) +{ + uint32_t reg; + + _dcu_get(dcu); + + /* TODO: Make this code more flexible to handle PDI and Error IRQ */ + /* TODO: Why does this code not have irq save? */ + reg = dcu_read(dcu, DCU_INT_STATUS_OFFSET); + + _dcu_put(dcu); + + if (reg & DCU_IRQ_MASK(irq)) + return true; + else + return false; +} +EXPORT_SYMBOL(dcu_get_irq_status); + +/*! + * This function registers an interrupt handler function for the specified + * interrupt line. The interrupt lines are defined in \b dcu_irq_line enum. + * + * @param dcu dcu handler + * @param irq Interrupt line to get status for. + * + * @param handler Input parameter for address of the handler + * function. + * + * @param irq_flags Flags for interrupt mode. Currently not used. + * + * @param devname Input parameter for string name of driver + * registering the handler. + * + * @param dev_id Input parameter for pointer of data to be + * passed to the handler. + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int dcu_request_irq(struct dcu_soc *dcu, uint32_t irq, + irqreturn_t(*handler) (int, void *), + uint32_t irq_flags, const char *devname, void *dev_id) +{ + unsigned long lock_flags; + + BUG_ON(irq >= DCU_IRQ_COUNT); + + _dcu_get(dcu); + + spin_lock_irqsave(&dcu->spin_lock, lock_flags); + + if (dcu->irq_list[irq].handler != NULL) { + dev_err(dcu->platform_dev, + "handler already installed on irq %d\n", irq); + spin_unlock_irqrestore(&dcu->spin_lock, lock_flags); + return -EINVAL; + } + + dcu->irq_list[irq].handler = handler; + dcu->irq_list[irq].flags = irq_flags; + dcu->irq_list[irq].dev_id = dev_id; + dcu->irq_list[irq].name = devname; + + /* TODO: Make this code more flexible to handle PDI and Error IRQ */ + /* clear irq stat for previous use */ + dcu_write(dcu, DCU_IRQ_MASK(irq), DCU_INT_STATUS_OFFSET); + + spin_unlock_irqrestore(&dcu->spin_lock, lock_flags); + + _dcu_put(dcu); + + dcu_enable_irq(dcu, irq); /* enable the interrupt */ + + return 0; +} +EXPORT_SYMBOL(dcu_request_irq); + +/*! + * This function unregisters an interrupt handler for the specified interrupt + * line. The interrupt lines are defined in \b dcu_irq_line enum. + * + * @param dcu dcu handler + * @param irq Interrupt line to get status for. + * + * @param dev_id Input parameter for pointer of data to be passed + * to the handler. This must match value passed to + * dcu_request_irq(). + * + */ +void dcu_free_irq(struct dcu_soc *dcu, uint32_t irq, void *dev_id) +{ + unsigned long lock_flags; + + dcu_disable_irq(dcu, irq); /* disable the interrupt */ + + spin_lock_irqsave(&dcu->spin_lock, lock_flags); + if (dcu->irq_list[irq].dev_id == dev_id) + dcu->irq_list[irq].handler = NULL; + spin_unlock_irqrestore(&dcu->spin_lock, lock_flags); +} +EXPORT_SYMBOL(dcu_free_irq); + +static int dcu_suspend_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mvf_dcuv4_platform_data *plat_data = pdev->dev.platform_data; + struct dcu_soc *dcu = platform_get_drvdata(pdev); + + if (atomic_read(&dcu->dcu_use_count)) { + /* save and disable enabled layers*/ + + /* save sub-modules status and disable all */ + + + clk_disable(dcu->dcu_clk); + } + + if (plat_data->pg) + plat_data->pg(pdev->id, 1); + + return 0; +} + +static int dcu_resume_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mvf_dcuv4_platform_data *plat_data = pdev->dev.platform_data; + struct dcu_soc *dcu = platform_get_drvdata(pdev); + + if (plat_data->pg) + plat_data->pg(pdev->id, 0); + + if (atomic_read(&dcu->dcu_use_count)) { + clk_enable(dcu->dcu_clk); + + /* restore buf ready regs */ + + /* re-enable sub-modules*/ + + /* restore idamc sub addr regs */ + + /* restart idma layer*/ + } else { + _dcu_get(dcu); +// _dcu_dmfc_init(dcu, dmfc_type_setup, 1); +// _dcu_init_dc_mappings(dcu); + /* Set sync refresh layers as high priority */ +// dcu_idmac_write(dcu, 0x18800001L, IDMAC_CHA_PRI(0)); + _dcu_put(dcu); + } + + return 0; +} + +static int mvf_dcu_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static long mvf_dcu_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + //int __user *argp = (void __user *)arg; + int ret = 0; + + switch (cmd) { + + default: + break; + } + return ret; +} + +static int mvf_dcu_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static struct file_operations mvf_dcu_fops = { + .owner = THIS_MODULE, + .open = mvf_dcu_open, + .release = mvf_dcu_release, + .unlocked_ioctl = mvf_dcu_ioctl, +}; + +/*! + * This function enables the DCU colorbar mode + * + * @param dcu dcu handler + * + */ +void dcu_enable_colorbar_mode(struct dcu_soc *dcu) +{ + uint32_t reg; + /* TODO: ensure clock is enabled */ + /* TODO: Do we need to check usecount? */ + dcu_blank(dcu, DCU_UNBLANK); + _dcu_lock(dcu); + /* Enable the VSYNC and HSYNC */ + reg = dcu_read(dcu, DCU_DCU_MODE_OFFSET); + reg |= DCU_DCU_MODE_RASTER_EN_MASK; + reg &= ~DCU_DCU_MODE_DCU_MODE_MASK; + reg |= (DCU_COLOR_BAR_MODE << DCU_DCU_MODE_DCU_MODE_SHIFT); + dcu_write(dcu, reg, DCU_DCU_MODE_OFFSET); + _dcu_unlock(dcu); +} +EXPORT_SYMBOL(dcu_enable_colorbar_mode); + +/*! + * This function disables the DCU colorbar mode + * + * @param dcu dcu handler + * + */ +void dcu_disable_colorbar_mode(struct dcu_soc *dcu) +{ + uint32_t reg; + + _dcu_lock(dcu); + reg = dcu_read(dcu, DCU_DCU_MODE_OFFSET); + reg &= ~DCU_DCU_MODE_RASTER_EN_MASK; + reg &= ~DCU_DCU_MODE_DCU_MODE_MASK; + reg |= (DCU_OFF << DCU_DCU_MODE_DCU_MODE_SHIFT); + /* TODO: Do we need to check usecount? */ + dcu_write(dcu, reg, DCU_DCU_MODE_OFFSET); + _dcu_unlock(dcu); + dcu_blank(dcu, DCU_BLANK); + +} +EXPORT_SYMBOL(dcu_disable_colorbar_mode); + +static ssize_t get_dcu_colorbar_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + uint32_t reg; + struct dcu_soc *dcu = &dcu_array[0]; + int i; + + if(dcu->display_configured == false) + { + return sprintf(buf, "Display currently not configured\n"); + } + + for(i=0; i<MVF_DCU_MAX_NUM; i++) + { + if(dcu_array[i].dcu_cdev == dev) + { + dcu = &dcu_array[i]; + break; + } + } + + _dcu_lock(dcu); + reg = dcu_read(dcu, DCU_DCU_MODE_OFFSET); + reg &= DCU_DCU_MODE_DCU_MODE_MASK; + _dcu_unlock(dcu); + + reg = reg >> DCU_DCU_MODE_DCU_MODE_SHIFT; + if(reg == DCU_COLOR_BAR_MODE) + return sprintf(buf, "Colorbar mode enabled\n"); + else + return sprintf(buf, "Colorbar mode not enabled\n"); +} + +static ssize_t set_dcu_colorbar_state(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dcu_soc *dcu = &dcu_array[0]; + int colorbarmode, r,i; + + for(i=0; i<MVF_DCU_MAX_NUM; i++) + { + if(dcu_array[i].dcu_cdev == dev) + { + dcu = &dcu_array[i]; + break; + } + } + + if(dcu->display_configured == false) + { +#ifdef DCU_UNIT_TEST + dcu_unit_test_config_display(dcu); +#else + printk("Display currently not configured\n"); + return count; +#endif + } + + r = kstrtoint(buf, 0, &colorbarmode); + if (r) + return r; + + /* TODO: Do we need to ensure DCU, display, clocks. TCON etc is enabled? */ + if(colorbarmode) + dcu_enable_colorbar_mode(dcu); + else + dcu_disable_colorbar_mode(dcu); + + return count; +} +DEVICE_ATTR(dcu_colorbar_mode, 0644, get_dcu_colorbar_state, set_dcu_colorbar_state); + +#ifdef DCU_UNIT_TEST +static ssize_t perform_dcu_unit_test(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dcu_soc *dcu = &dcu_array[0]; + int i; + + for(i=0; i<MVF_DCU_MAX_NUM; i++) + { + if(dcu_array[i].dcu_cdev == dev) + { + dcu = &dcu_array[i]; + break; + } + } + + if(dcu->display_configured == false) + { + dcu_unit_test_config_display(dcu); + } + dcu_unit_test(dcu); + return count; +} +DEVICE_ATTR(dcu_unit_test, 0644, NULL, perform_dcu_unit_test); +#endif + +static struct device_attribute *dcu_sysfs_attrs[] = { + &dev_attr_dcu_colorbar_mode, +#ifdef DCU_UNIT_TEST + &dev_attr_dcu_unit_test, +#endif + NULL +}; + +/*! + * This function performs the following operations: + * 1. Register DCU as char device + * 2. Creates a DCU class + * 3. Setups up the device attributes * + */ +int register_dcu_device(struct dcu_soc *dcu, int id) +{ + int ret = 0; + int i; + struct device_attribute *attr; + + if (!major) { + major = register_chrdev(0, "mvf_dcu", &mvf_dcu_fops); + if (major < 0) { + printk(KERN_ERR "Unable to register mvf_dcu as a char device\n"); + ret = major; + goto register_cdev_fail; + } + + dcu_class = class_create(THIS_MODULE, "mvf_dcu"); + if (IS_ERR(dcu_class)) { + ret = PTR_ERR(dcu_class); + goto dcu_class_fail; + } + + dcu->dcu_cdev = device_create(dcu_class, NULL, MKDEV(major, 0), + NULL, "mvf_dcu"); + if (IS_ERR(dcu->dcu_cdev)) { + ret = PTR_ERR(dcu->dcu_cdev); + goto dev_create_fail; + } + + /* create device sysfs files */ + i = 0; + while ((attr = dcu_sysfs_attrs[i++]) != NULL) { + ret = device_create_file(dcu->dcu_cdev, attr); + if (ret) + dev_err(dcu->dcu_cdev, "Error %d on creating file\n", ret); + } + /* + ret = device_create_file(dcu->dcu_cdev, &dcu_colorbar_mode); + if (ret){ + dev_err(dcu->dcu_cdev, "Error %d on creating file\n", ret); + } + */ + dcu->dcu_cdev->dma_mask = kmalloc(sizeof(*dcu->dcu_cdev->dma_mask), GFP_KERNEL); + *dcu->dcu_cdev->dma_mask = DMA_BIT_MASK(32); + dcu->dcu_cdev->coherent_dma_mask = DMA_BIT_MASK(32); + } + + return ret; + +dev_create_fail: + if (id == 0) { + class_destroy(dcu_class); + unregister_chrdev(major, "mvf_dcu"); + } +dcu_class_fail: + if (id == 0) + unregister_chrdev(major, "mvf_dcu"); +register_cdev_fail: + return ret; +} + +void unregister_dcu_device(struct dcu_soc *dcu, int id) +{ + int i; + struct device_attribute *attr; + + if (major) { + i = 0; + while ((attr = dcu_sysfs_attrs[i++]) != NULL) { + device_remove_file(dcu->dcu_cdev, attr); + } + device_destroy(dcu_class, MKDEV(major, 0)); + class_destroy(dcu_class); + unregister_chrdev(major, "mvf_dcu"); + major = 0; + } +} + +static const struct dev_pm_ops mvfdcu_pm_ops = { + .suspend_noirq = dcu_suspend_noirq, + .resume_noirq = dcu_resume_noirq, +}; + +/*! + * This structure contains pointers to the power management callback functions. + */ +static struct platform_driver mvfdcu_driver = { + .driver = { + .name = MVF_DCU, + .pm = &mvfdcu_pm_ops, + }, + .probe = dcu_probe, + .remove = dcu_remove, +}; + +static int32_t __init dcu_gen_init(void) +{ + int32_t ret; + + ret = platform_driver_register(&mvfdcu_driver); + return 0; +} + +subsys_initcall(dcu_gen_init); + +static void __exit dcu_gen_uninit(void) +{ + platform_driver_unregister(&mvfdcu_driver); +} + +module_exit(dcu_gen_uninit); diff --git a/drivers/mvf/dcu4/dcu4_regs.h b/drivers/mvf/dcu4/dcu4_regs.h new file mode 100644 index 000000000000..f638096f9187 --- /dev/null +++ b/drivers/mvf/dcu4/dcu4_regs.h @@ -0,0 +1,722 @@ +/* + * Copyright (C) 2005-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 + */ + +/* + * @file dcu4_regs.h + * + * @brief DCU Register definitions + * + * @ingroup DCU + */ +#ifndef __DCU4_REGS_INCLUDED__ +#define __DCU4_REGS_INCLUDED__ + +/* DCU - Internal RAM Offsets */ +#define DCU_CLUT_OFFSET 0x00002000 +#define DCU_GAMMARED_OFFSET 0x00004000 +#define DCU_GAMMAGREEN_OFFSET 0x00004400 +#define DCU_GAMMABLUE_OFFSET 0x00004800 +#define DCU_CURSOR_OFFSET 0x00004C00 + +/* DCU - Register offsets */ +#define DCU_CTRLDESCCURSOR1_OFFSET (0x00000000/4) +#define DCU_CTRLDESCCURSOR2_OFFSET (0x00000004/4) +#define DCU_CTRLDESCCURSOR3_OFFSET (0x00000008/4) +#define DCU_CTRLDESCCURSOR4_OFFSET (0x0000000C/4) +#define DCU_DCU_MODE_OFFSET (0x00000010/4) +#define DCU_BGND_OFFSET (0x00000014/4) +#define DCU_DISP_SIZE_OFFSET (0x00000018/4) +#define DCU_HSYN_PARA_OFFSET (0x0000001C/4) +#define DCU_VSYN_PARA_OFFSET (0x00000020/4) +#define DCU_SYNPOL_OFFSET (0x00000024/4) +#define DCU_THRESHOLD_OFFSET (0x00000028/4) +#define DCU_INT_STATUS_OFFSET (0x0000002C/4) +#define DCU_INT_MASK_OFFSET (0x00000030/4) +#define DCU_COLBAR_1_OFFSET (0x00000034/4) +#define DCU_COLBAR_2_OFFSET (0x00000038/4) +#define DCU_COLBAR_3_OFFSET (0x0000003C/4) +#define DCU_COLBAR_4_OFFSET (0x00000040/4) +#define DCU_COLBAR_5_OFFSET (0x00000044/4) +#define DCU_COLBAR_6_OFFSET (0x00000048/4) +#define DCU_COLBAR_7_OFFSET (0x0000004C/4) +#define DCU_COLBAR_8_OFFSET (0x00000050/4) +#define DCU_DIV_RATIO_OFFSET (0x00000054/4) +#define DCU_SIGN_CALC_1_OFFSET (0x00000058/4) +#define DCU_SIGN_CALC_2_OFFSET (0x0000005C/4) +#define DCU_CRC_VAL_OFFSET (0x00000060/4) +#define DCU_PARR_ERR_STATUS1_OFFSET (0x0000006C/4) +#define DCU_PARR_ERR_STATUS2_OFFSET (0x00000070/4) +#define DCU_PARR_ERR_STATUS3_OFFSET (0x0000007C/4) +#define DCU_MASK_PARR_ERR_STATUS1_OFFSET (0x00000080/4) +#define DCU_MASK_PARR_ERR_STATUS2_OFFSET (0x00000084/4) +#define DCU_MASK_PARR_ERR_STATUS3_OFFSET (0x00000090/4) +#define DCU_THRESHOLD_INP_BUF_1_OFFSET (0x00000094/4) +#define DCU_THRESHOLD_INP_BUF_2_OFFSET (0x00000098/4) +#define DCU_THRESHOLD_INP_BUF_3_OFFSET (0x0000009C/4) +#define DCU_LUMA_COMP_OFFSET (0x000000A0/4) +#define DCU_CHROMA_RED_OFFSET (0x000000A4/4) +#define DCU_CHROMA_GREEN_OFFSET (0x000000A8/4) +#define DCU_CHROMA_BLUE_OFFSET (0x000000AC/4) +#define DCU_CRC_POS_OFFSET (0x000000B0/4) +#define DCU_LYR_INTPOL_EN_OFFSET (0x000000B4/4) +#define DCU_LYR_LUMA_COMP_OFFSET (0x000000B8/4) +#define DCU_LYR_CHROMA_RED_OFFSET (0x000000BC/4) +#define DCU_LYR_CHROMA_GREEN_OFFSET (0x000000C0/4) +#define DCU_LYR_CHROMA_BLUE_OFFSET (0x000000C4/4) +#define DCU_COMP_IMSIZE_OFFSET (0x000000C8/4) +#define DCU_UPDATE_MODE_OFFSET (0x000000CC/4) +#define DCU_UNDERRUN_OFFSET (0x000000D0/4) + +#define DCU_LAYER_CTRLDESC_BASE (0x00000200/4) +#define DCU_CTRLDESCLx_OFFSET(LayerNum) (DCU_LAYER_CTRLDESC_BASE + LayerNum*16) +#define DCU_CTRLDESCLx_y_OFFSET(LayerNum,RegisterOffset) (DCU_CTRLDESCLx_OFFSET(LayerNum) + RegisterOffset) +#define DCU_CTRLDESCL_OFFSET1 (0) +#define DCU_CTRLDESCL_OFFSET2 (1) +#define DCU_CTRLDESCL_OFFSET3 (2) +#define DCU_CTRLDESCL_OFFSET4 (3) +#define DCU_CTRLDESCL_OFFSET5 (4) +#define DCU_CTRLDESCL_OFFSET6 (5) +#define DCU_CTRLDESCL_OFFSET7 (6) +#define DCU_CTRLDESCL_OFFSET8 (7) +#define DCU_CTRLDESCL_OFFSET9 (8) +#define DCU_CTRLDESCL_OFFSET_MAX (8) + +/* Field definitions for CTRLDESCCURSOR4 */ +#define DCU_CTRLDESCCURSOR4_HWC_BLINK_OFF_SHIFT (16) +#define DCU_CTRLDESCCURSOR4_HWC_BLINK_OFF_MASK ((0x000000FF) << (DCU_CTRLDESCCURSOR4_HWC_BLINK_OFF_SHIFT)) + +#define DCU_CTRLDESCCURSOR4_EN_BLINK_SHIFT (8) +#define DCU_CTRLDESCCURSOR4_EN_BLINK_MASK ((1) << (DCU_CTRLDESCCURSOR4_EN_BLINK_SHIFT)) + +#define DCU_CTRLDESCCURSOR4_HWC_BLINK_ON_SHIFT (0) +#define DCU_CTRLDESCCURSOR4_HWC_BLINK_ON_MASK ((0x000000FF) << (DCU_CTRLDESCCURSOR4_HWC_BLINK_ON_SHIFT)) + + + +/* Field definitions for DCU_MODE */ +#define DCU_DCU_MODE_DCU_SW_RESET_SHIFT (31) +#define DCU_DCU_MODE_DCU_SW_RESET_MASK ((1) << (DCU_DCU_MODE_DCU_SW_RESET_SHIFT)) + +#define DCU_DCU_MODE_EN_DITHER_SHIFT (30) +#define DCU_DCU_MODE_EN_DITHER_MASK ((1) << (DCU_DCU_MODE_EN_DITHER_SHIFT)) + +#define DCU_DCU_MODE_ADDB_SHIFT (28) +#define DCU_DCU_MODE_ADDB_MASK ((0x00000003) << (DCU_DCU_MODE_ADDB_SHIFT)) + +#define DCU_DCU_MODE_ADDG_SHIFT (26) +#define DCU_DCU_MODE_ADDG_MASK ((0x00000003) << (DCU_DCU_MODE_ADDG_SHIFT)) + +#define DCU_DCU_MODE_ADDR_SHIFT (24) +#define DCU_DCU_MODE_ADDR_MASK ((0x00000003) << (DCU_DCU_MODE_ADDR_SHIFT)) + +#define DCU_DCU_MODE_DDR_MODE_SHIFT (23) +#define DCU_DCU_MODE_DDR_MODE_MASK ((1) << (DCU_DCU_MODE_DDR_MODE_SHIFT)) + +#define DCU_DCU_MODE_BLEND_ITER_SHIFT (20) +#define DCU_DCU_MODE_BLEND_ITER_MASK ((0x00000007) << (DCU_DCU_MODE_BLEND_ITER_SHIFT)) + +#define DCU_DCU_MODE_RASTER_EN_SHIFT (14) +#define DCU_DCU_MODE_RASTER_EN_MASK ((1) << (DCU_DCU_MODE_RASTER_EN_SHIFT)) + +#define DCU_DCU_MODE_TAG_EN_SHIFT (6) +#define DCU_DCU_MODE_TAG_EN_MASK ((1) << (DCU_DCU_MODE_TAG_EN_SHIFT)) + +#define DCU_DCU_MODE_SIG_EN_SHIFT (5) +#define DCU_DCU_MODE_SIG_EN_MASK ((1) << (DCU_DCU_MODE_SIG_EN_SHIFT)) + +#define DCU_DCU_MODE_EN_GAMMA_SHIFT (2) +#define DCU_DCU_MODE_EN_GAMMA_MASK ((1) << (DCU_DCU_MODE_EN_GAMMA_SHIFT)) + +#define DCU_DCU_MODE_DCU_MODE_SHIFT (0) +#define DCU_DCU_MODE_DCU_MODE_MASK ((0x00000003) << (DCU_DCU_MODE_DCU_MODE_SHIFT)) + + + +/* Field definitions for BGND */ +#define DCU_BGND_BGND_R_SHIFT (16) +#define DCU_BGND_BGND_R_MASK ((0x000000FF) << (DCU_BGND_BGND_R_SHIFT)) + +#define DCU_BGND_BGND_G_SHIFT (8) +#define DCU_BGND_BGND_G_MASK ((0x000000FF) << (DCU_BGND_BGND_G_SHIFT)) + +#define DCU_BGND_BGND_B_SHIFT (0) +#define DCU_BGND_BGND_B_MASK ((0x000000FF) << (DCU_BGND_BGND_B_SHIFT)) + + + +/* Field definitions for DISP_SIZE */ +#define DCU_DISP_SIZE_DELTA_Y_SHIFT (16) +#define DCU_DISP_SIZE_DELTA_Y_MASK ((0x000007FF) << (DCU_DISP_SIZE_DELTA_Y_SHIFT)) + +#define DCU_DISP_SIZE_DELTA_X_SHIFT (0) +#define DCU_DISP_SIZE_DELTA_X_MASK ((0x0000007F) << (DCU_DISP_SIZE_DELTA_X_SHIFT)) + + + +/* Field definitions for HSYN_PARA */ +#define DCU_HSYN_PARA_BP_H_SHIFT (22) +#define DCU_HSYN_PARA_BP_H_MASK ((0x000001FF) << (DCU_HSYN_PARA_BP_H_SHIFT)) + +#define DCU_HSYN_PARA_PW_H_SHIFT (11) +#define DCU_HSYN_PARA_PW_H_MASK ((0x000001FF) << (DCU_HSYN_PARA_PW_H_SHIFT)) + +#define DCU_HSYN_PARA_FP_H_SHIFT (0) +#define DCU_HSYN_PARA_FP_H_MASK ((0x000001FF) << (DCU_HSYN_PARA_FP_H_SHIFT)) + + + +/* Field definitions for VSYN_PARA */ +#define DCU_VSYN_PARA_BP_V_SHIFT (22) +#define DCU_VSYN_PARA_BP_V_MASK ((0x000001FF) << (DCU_VSYN_PARA_BP_V_SHIFT)) + +#define DCU_VSYN_PARA_PW_V_SHIFT (11) +#define DCU_VSYN_PARA_PW_V_MASK ((0x000001FF) << (DCU_VSYN_PARA_PW_V_SHIFT)) + +#define DCU_VSYN_PARA_FP_V_SHIFT (0) +#define DCU_VSYN_PARA_FP_V_MASK ((0x000001FF) << (DCU_VSYN_PARA_FP_V_SHIFT)) + + + +/* Field definitions for SYNPOL */ +#define DCU_SYNPOL_INV_PXCK_SHIFT (6) +#define DCU_SYNPOL_INV_PXCK_MASK ((1) << (DCU_SYNPOL_INV_PXCK_SHIFT)) + +#define DCU_SYNPOL_NEG_SHIFT (5) +#define DCU_SYNPOL_NEG_MASK ((1) << (DCU_SYNPOL_NEG_SHIFT)) + +#define DCU_SYNPOL_INV_VS_SHIFT (1) +#define DCU_SYNPOL_INV_VS_MASK ((1) << (DCU_SYNPOL_INV_VS_SHIFT)) + +#define DCU_SYNPOL_INV_HS_SHIFT (0) +#define DCU_SYNPOL_INV_HS_MASK ((1) << (DCU_SYNPOL_INV_HS_SHIFT)) + + + +/* Field definitions for THRESHOLD */ +#define DCU_THRESHOLD_LS_BF_VS_SHIFT (16) +#define DCU_THRESHOLD_LS_BF_VS_MASK ((0x000003FF) << (DCU_THRESHOLD_LS_BF_VS_SHIFT)) + +#define DCU_THRESHOLD_OUT_BUF_HIGH_SHIFT (8) +#define DCU_THRESHOLD_OUT_BUF_HIGH_MASK ((0x000000FF) << (DCU_THRESHOLD_OUT_BUF_HIGH_SHIFT)) + +#define DCU_THRESHOLD_OUT_BUF_LOW_SHIFT (0) +#define DCU_THRESHOLD_OUT_BUF_LOW_MASK ((0x000000FF) << (DCU_THRESHOLD_OUT_BUF_LOW_SHIFT)) + + + +/* Field definitions for INT_STATUS */ +#define DCU_INT_STATUS_P6_EMPTY_SHIFT (31) +#define DCU_INT_STATUS_P6_EMPTY_MASK ((1) << (DCU_INT_STATUS_P6_EMPTY_SHIFT)) + +#define DCU_INT_STATUS_P5_EMPTY_SHIFT (30) +#define DCU_INT_STATUS_P5_EMPTY_MASK ((1) << (DCU_INT_STATUS_P5_EMPTY_SHIFT)) + +#define DCU_INT_STATUS_P4_EMPTY_SHIFT (29) +#define DCU_INT_STATUS_P4_EMPTY_MASK ((1) << (DCU_INT_STATUS_P4_EMPTY_SHIFT)) + +#define DCU_INT_STATUS_P3_EMPTY_SHIFT (28) +#define DCU_INT_STATUS_P3_EMPTY_MASK ((1) << (DCU_INT_STATUS_P3_EMPTY_SHIFT)) + +#define DCU_INT_STATUS_P2_EMPTY_SHIFT (27) +#define DCU_INT_STATUS_P2_EMPTY_MASK ((1) << (DCU_INT_STATUS_P2_EMPTY_SHIFT)) + +#define DCU_INT_STATUS_P1_EMPTY_SHIFT (26) +#define DCU_INT_STATUS_P1_EMPTY_MASK ((1) << (DCU_INT_STATUS_P1_EMPTY_SHIFT)) + +#define DCU_INT_STATUS_P6_FIFO_HI_FLAG_SHIFT (23) +#define DCU_INT_STATUS_P6_FIFO_HI_FLAG_MASK ((1) << (DCU_INT_STATUS_P6_FIFO_HI_FLAG_SHIFT)) + +#define DCU_INT_STATUS_P6_FIFO_LO_FLAG_SHIFT (22) +#define DCU_INT_STATUS_P6_FIFO_LO_FLAG_MASK ((1) << (DCU_INT_STATUS_P6_FIFO_LO_FLAG_SHIFT)) + +#define DCU_INT_STATUS_P5_FIFO_HI_FLAG_SHIFT (21) +#define DCU_INT_STATUS_P5_FIFO_HI_FLAG_MASK ((1) << (DCU_INT_STATUS_P5_FIFO_HI_FLAG_SHIFT)) + +#define DCU_INT_STATUS_P5_FIFO_LO_FLAG_SHIFT (20) +#define DCU_INT_STATUS_P5_FIFO_LO_FLAG_MASK ((1) << (DCU_INT_STATUS_P5_FIFO_LO_FLAG_SHIFT)) + +#define DCU_INT_STATUS_P4_FIFO_HI_FLAG_SHIFT (19) +#define DCU_INT_STATUS_P4_FIFO_HI_FLAG_MASK ((1) << (DCU_INT_STATUS_P4_FIFO_HI_FLAG_SHIFT)) + +#define DCU_INT_STATUS_P4_FIFO_LO_FLAG_SHIFT (18) +#define DCU_INT_STATUS_P4_FIFO_LO_FLAG_MASK ((1) << (DCU_INT_STATUS_P4_FIFO_LO_FLAG_SHIFT)) + +#define DCU_INT_STATUS_P3_FIFO_HI_FLAG_SHIFT (17) +#define DCU_INT_STATUS_P3_FIFO_HI_FLAG_MASK ((1) << (DCU_INT_STATUS_P3_FIFO_HI_FLAG_SHIFT)) + +#define DCU_INT_STATUS_P3_FIFO_LO_FLAG_SHIFT (16) +#define DCU_INT_STATUS_P3_FIFO_LO_FLAG_MASK ((1) << (DCU_INT_STATUS_P3_FIFO_LO_FLAG_SHIFT)) + +#define DCU_INT_STATUS_DMA_TRANS_FINISH_SHIFT (14) +#define DCU_INT_STATUS_DMA_TRANS_FINISH_MASK ((1) << (DCU_INT_STATUS_DMA_TRANS_FINISH_SHIFT)) + +#define DCU_INT_STATUS_LYR_TRANS_FINISH_SHIFT (12) +#define DCU_INT_STATUS_LYR_TRANS_FINISH_MASK ((1) << (DCU_INT_STATUS_LYR_TRANS_FINISH_SHIFT)) +#define DCU_INT_STATUS_IPM_ERROR_SHIFT (11) +#define DCU_INT_STATUS_IPM_ERROR_MASK ((1) << (DCU_INT_STATUS_IPM_ERROR_SHIFT)) + +#define DCU_INT_STATUS_PROG_END_SHIFT (10) +#define DCU_INT_STATUS_PROG_END_MASK ((1) << (DCU_INT_STATUS_PROG_END_SHIFT)) + +#define DCU_INT_STATUS_P2_FIFO_HI_FLAG_SHIFT (9) +#define DCU_INT_STATUS_P2_FIFO_HI_FLAG_MASK ((1) << (DCU_INT_STATUS_P2_FIFO_HI_FLAG_SHIFT)) + +#define DCU_INT_STATUS_P2_FIFO_LO_FLAG_SHIFT (8) +#define DCU_INT_STATUS_P2_FIFO_LO_FLAG_MASK ((1) << (DCU_INT_STATUS_P2_FIFO_LO_FLAG_SHIFT)) + +#define DCU_INT_STATUS_P1_FIFO_HI_FLAG_SHIFT (7) +#define DCU_INT_STATUS_P1_FIFO_HI_FLAG_MASK ((1) << (DCU_INT_STATUS_P1_FIFO_HI_FLAG_SHIFT)) + +#define DCU_INT_STATUS_P1_FIFO_LO_FLAG_SHIFT (6) +#define DCU_INT_STATUS_P1_FIFO_LO_FLAG_MASK ((1) << (DCU_INT_STATUS_P1_FIFO_LO_FLAG_SHIFT)) + +#define DCU_INT_STATUS_CRC_OVERFLOW_SHIFT (5) +#define DCU_INT_STATUS_CRC_OVERFLOW_MASK ((1) << (DCU_INT_STATUS_CRC_OVERFLOW_SHIFT)) + +#define DCU_INT_STATUS_CRC_READY_SHIFT (4) +#define DCU_INT_STATUS_CRC_READY_MASK ((1) << (DCU_INT_STATUS_CRC_READY_SHIFT)) + +#define DCU_INT_STATUS_VS_BLANK_SHIFT (3) +#define DCU_INT_STATUS_VS_BLANK_MASK ((1) << (DCU_INT_STATUS_VS_BLANK_SHIFT)) + +#define DCU_INT_STATUS_LS_BF_VS_SHIFT (2) +#define DCU_INT_STATUS_LS_BF_VS_MASK ((1) << (DCU_INT_STATUS_LS_BF_VS_SHIFT)) + +#define DCU_INT_STATUS_UNDRUN_SHIFT (1) +#define DCU_INT_STATUS_UNDRUN_MASK ((1) << (DCU_INT_STATUS_UNDRUN_SHIFT)) + +#define DCU_INT_STATUS_VSYNC_SHIFT (0) +#define DCU_INT_STATUS_VSYNC_MASK ((1) << (DCU_INT_STATUS_VSYNC_SHIFT)) + + + +/* Field definitions for INT_MASK */ +#define DCU_INT_MASK_M_P6_EMPTY_SHIFT (31) +#define DCU_INT_MASK_M_P6_EMPTY_MASK ((1) << (DCU_INT_MASK_M_P6_EMPTY_SHIFT)) + +#define DCU_INT_MASK_M_P5_EMPTY_SHIFT (30) +#define DCU_INT_MASK_M_P5_EMPTY_MASK ((1) << (DCU_INT_MASK_M_P5_EMPTY_SHIFT)) + +#define DCU_INT_MASK_M_P4_EMPTY_SHIFT (29) +#define DCU_INT_MASK_M_P4_EMPTY_MASK ((1) << (DCU_INT_MASK_M_P4_EMPTY_SHIFT)) + +#define DCU_INT_MASK_M_P3_EMPTY_SHIFT (28) +#define DCU_INT_MASK_M_P3_EMPTY_MASK ((1) << (DCU_INT_MASK_M_P3_EMPTY_SHIFT)) + +#define DCU_INT_MASK_M_P2_EMPTY_SHIFT (27) +#define DCU_INT_MASK_M_P2_EMPTY_MASK ((1) << (DCU_INT_MASK_M_P2_EMPTY_SHIFT)) + +#define DCU_INT_MASK_M_P1_EMPTY_SHIFT (26) +#define DCU_INT_MASK_M_P1_EMPTY_MASK ((1) << (DCU_INT_MASK_M_P1_EMPTY_SHIFT)) + +#define DCU_INT_MASK_M_P6_FIFO_HI_FLAG_SHIFT (23) +#define DCU_INT_MASK_M_P6_FIFO_HI_FLAG_MASK ((1) << (DCU_INT_MASK_M_P6_FIFO_HI_FLAG_SHIFT)) + +#define DCU_INT_MASK_M_P6_FIFO_LO_FLAG_SHIFT (22) +#define DCU_INT_MASK_M_P6_FIFO_LO_FLAG_MASK ((1) << (DCU_INT_MASK_M_P6_FIFO_LO_FLAG_SHIFT)) + +#define DCU_INT_MASK_M_P5_FIFO_HI_FLAG_SHIFT (21) +#define DCU_INT_MASK_M_P5_FIFO_HI_FLAG_MASK ((1) << (DCU_INT_MASK_M_P5_FIFO_HI_FLAG_SHIFT)) + +#define DCU_INT_MASK_M_P5_FIFO_LO_FLAG_SHIFT (20) +#define DCU_INT_MASK_M_P5_FIFO_LO_FLAG_MASK ((1) << (DCU_INT_MASK_M_P5_FIFO_LO_FLAG_SHIFT)) + +#define DCU_INT_MASK_M_P4_FIFO_HI_FLAG_SHIFT (19) +#define DCU_INT_MASK_M_P4_FIFO_HI_FLAG_MASK ((1) << (DCU_INT_MASK_M_P4_FIFO_HI_FLAG_SHIFT)) + +#define DCU_INT_MASK_M_P4_FIFO_LO_FLAG_SHIFT (18) +#define DCU_INT_MASK_M_P4_FIFO_LO_FLAG_MASK ((1) << (DCU_INT_MASK_M_P4_FIFO_LO_FLAG_SHIFT)) + +#define DCU_INT_MASK_M_P3_FIFO_HI_FLAG_SHIFT (17) +#define DCU_INT_MASK_M_P3_FIFO_HI_FLAG_MASK ((1) << (DCU_INT_MASK_M_P3_FIFO_HI_FLAG_SHIFT)) + +#define DCU_INT_MASK_M_P3_FIFO_LO_FLAG_SHIFT (16) +#define DCU_INT_MASK_M_P3_FIFO_LO_FLAG_MASK ((1) << (DCU_INT_MASK_M_P3_FIFO_LO_FLAG_SHIFT)) + +#define DCU_INT_MASK_M_DMA_TRANS_FINISH_SHIFT (14) +#define DCU_INT_MASK_M_DMA_TRANS_FINISH_MASK ((1) << (DCU_INT_MASK_M_DMA_TRANS_FINISH_SHIFT)) + +#define DCU_INT_MASK_M_LYR_TRANS_FINISH_SHIFT (12) +#define DCU_INT_MASK_M_LYR_TRANS_FINISH_MASK ((1) << (DCU_INT_MASK_M_LYR_TRANS_FINISH_SHIFT)) + +#define DCU_INT_MASK_M_IPM_ERROR_SHIFT (11) +#define DCU_INT_MASK_M_IPM_ERROR_MASK ((1) << (DCU_INT_MASK_M_IPM_ERROR_SHIFT)) + +#define DCU_INT_MASK_M_PROG_END_SHIFT (10) +#define DCU_INT_MASK_M_PROG_END_MASK ((1) << (DCU_INT_MASK_M_PROG_END_SHIFT)) + +#define DCU_INT_MASK_M_P2_FIFO_HI_FLAG_SHIFT (9) +#define DCU_INT_MASK_M_P2_FIFO_HI_FLAG_MASK ((1) << (DCU_INT_MASK_M_P2_FIFO_HI_FLAG_SHIFT)) + +#define DCU_INT_MASK_M_P2_FIFO_LO_FLAG_SHIFT (8) +#define DCU_INT_MASK_M_P2_FIFO_LO_FLAG_MASK ((1) << (DCU_INT_MASK_M_P2_FIFO_LO_FLAG_SHIFT)) + +#define DCU_INT_MASK_M_P1_FIFO_HI_FLAG_SHIFT (7) +#define DCU_INT_MASK_M_P1_FIFO_HI_FLAG_MASK ((1) << (DCU_INT_MASK_M_P1_FIFO_HI_FLAG_SHIFT)) + +#define DCU_INT_MASK_M_P1_FIFO_LO_FLAG_SHIFT (6) +#define DCU_INT_MASK_M_P1_FIFO_LO_FLAG_MASK ((1) << (DCU_INT_MASK_M_P1_FIFO_LO_FLAG_SHIFT)) + +#define DCU_INT_MASK_M_CRC_OVERFLOW_SHIFT (5) +#define DCU_INT_MASK_M_CRC_OVERFLOW_MASK ((1) << (DCU_INT_MASK_M_CRC_OVERFLOW_SHIFT)) + +#define DCU_INT_MASK_M_CRC_READY_SHIFT (4) +#define DCU_INT_MASK_M_CRC_READY_MASK ((1) << (DCU_INT_MASK_M_CRC_READY_SHIFT)) + +#define DCU_INT_MASK_M_VS_BLANK_SHIFT (3) +#define DCU_INT_MASK_M_VS_BLANK_MASK ((1) << (DCU_INT_MASK_M_VS_BLANK_SHIFT)) + +#define DCU_INT_MASK_M_LS_BF_VS_SHIFT (2) +#define DCU_INT_MASK_M_LS_BF_VS_MASK ((1) << (DCU_INT_MASK_M_LS_BF_VS_SHIFT)) + +#define DCU_INT_MASK_M_UNDRUN_SHIFT (1) +#define DCU_INT_MASK_M_UNDRUN_MASK ((1) << (DCU_INT_MASK_M_UNDRUN_SHIFT)) + +#define DCU_INT_MASK_M_VSYNC_SHIFT (0) +#define DCU_INT_MASK_M_VSYNC_MASK ((1) << (DCU_INT_MASK_M_VSYNC_SHIFT)) + +#define DCU_IRQ_MASK(irq) (1UL << (irq & 0x1F)) + +/* Field definitions for COLBAR_n */ +#define DCU_COLBAR_n_R_SHIFT (16) +#define DCU_COLBAR_n_R_MASK ((0x000000FF) << (DCU_COLBAR_n_R_SHIFT)) + +#define DCU_COLBAR_n_G_SHIFT (8) +#define DCU_COLBAR_n_G_MASK ((0x000000FF) << (DCU_COLBAR_n_G_SHIFT)) + +#define DCU_COLBAR_n_B_SHIFT (0) +#define DCU_COLBAR_n_B_MASK ((0x000000FF) << (DCU_COLBAR_n_B_SHIFT)) + + + + +/* Field definitions for DIV_RATIO */ +#define DCU_DIV_RATIO_DIV_RATIO_SHIFT (0) +#define DCU_DIV_RATIO_DIV_RATIO_MASK ((0x000000FF) << (DCU_DIV_RATIO_DIV_RATIO_SHIFT)) + + + +/* Field definitions for SIGN_CALC_1 */ +#define DCU_SIGN_CALC_1_SIG_VER_SIZE_SHIFT (16) +#define DCU_SIGN_CALC_1_SIG_VER_SIZE_MASK ((0x000007FF) << (DCU_SIGN_CALC_1_SIG_VER_SIZE_SHIFT)) + +#define DCU_SIGN_CALC_1_SIG_HOR_SIZE_SHIFT (0) +#define DCU_SIGN_CALC_1_SIG_HOR_SIZE_MASK ((0x000007FF) << (DCU_SIGN_CALC_1_SIG_HOR_SIZE_SHIFT)) + + + +/* Field definitions for SIGN_CALC_2 */ +#define DCU_SIGN_CALC_2_SIG_VER_POS_SHIFT (16) +#define DCU_SIGN_CALC_2_SIG_VER_POS_MASK ((0x000007FF) << (DCU_SIGN_CALC_2_SIG_VER_POS_SHIFT)) + +#define DCU_SIGN_CALC_2_SIG_HOR_POS_SHIFT (0) +#define DCU_SIGN_CALC_2_SIG_HOR_POS_MASK ((0x000007FF) << (DCU_SIGN_CALC_2_SIG_HOR_POS_SHIFT)) + + + +/* Field definitions for CRC_VAL */ +#define DCU_CRC_VAL_CRC_VAL_SHIFT (0) +#define DCU_CRC_VAL_CRC_VAL_MASK ((0xFFFFFFFF) << (DCU_CRC_VAL_CRC_VAL_SHIFT)) + + + +/* Field definitions for PARR_ERR_STATUS1/2 */ +#define DCU_PARR_ERR_STATUS_Lx_MASK(Layer) ((1) << (Layer % 32)) + +/* Field definitions for MASK_PARR_ERR_STATUS1/2 */ +#define DCU_MASK_PARR_ERR_STATUS_Lx_MASK(Layer) ((1) << (Layer % 32)) + + +/* Field definitions for MASK_PARR_ERR_STATUS3 */ +#define DCU_MASK_PARR_ERR_STATUS3_M_RLE_ERR_SHIFT (3) +#define DCU_MASK_PARR_ERR_STATUS3_M_RLE_ERR_MASK ((1) << (DCU_MASK_PARR_ERR_STATUS3_M_RLE_ERR_SHIFT)) + +#define DCU_MASK_PARR_ERR_STATUS3_M_HWC_ERR_SHIFT (2) +#define DCU_MASK_PARR_ERR_STATUS3_M_HWC_ERR_MASK ((1) << (DCU_MASK_PARR_ERR_STATUS3_M_HWC_ERR_SHIFT)) + +#define DCU_MASK_PARR_ERR_STATUS3_M_SIG_ERR_SHIFT (1) +#define DCU_MASK_PARR_ERR_STATUS3_M_SIG_ERR_MASK ((1) << (DCU_MASK_PARR_ERR_STATUS3_M_SIG_ERR_SHIFT)) + +#define DCU_MASK_PARR_ERR_STATUS3_M_DISP_ERR_SHIFT (0) +#define DCU_MASK_PARR_ERR_STATUS3_M_DISP_ERR_MASK ((1) << (DCU_MASK_PARR_ERR_STATUS3_M_DISP_ERR_SHIFT)) + + + +/* Field definitions for THRESHOLD_INP_BUF_1 */ +#define DCU_THRESHOLD_INP_BUF_1_INP_BUF_P2_HI_SHIFT (24) +#define DCU_THRESHOLD_INP_BUF_1_INP_BUF_P2_HI_MASK ((0x0000007F) << (DCU_THRESHOLD_INP_BUF_1_INP_BUF_P2_HI_SHIFT)) + +#define DCU_THRESHOLD_INP_BUF_1_INP_BUF_P2_LO_SHIFT (16) +#define DCU_THRESHOLD_INP_BUF_1_INP_BUF_P2_LO_MASK ((0x0000007F) << (DCU_THRESHOLD_INP_BUF_1_INP_BUF_P2_LO_SHIFT)) + +#define DCU_THRESHOLD_INP_BUF_1_INP_BUF_P1_HI_SHIFT (8) +#define DCU_THRESHOLD_INP_BUF_1_INP_BUF_P1_HI_MASK ((0x0000007F) << (DCU_THRESHOLD_INP_BUF_1_INP_BUF_P1_HI_SHIFT)) + +#define DCU_THRESHOLD_INP_BUF_1_INP_BUF_P1_LO_SHIFT (0) +#define DCU_THRESHOLD_INP_BUF_1_INP_BUF_P1_LO_MASK ((0x0000007F) << (DCU_THRESHOLD_INP_BUF_1_INP_BUF_P1_LO_SHIFT)) + + + +/* Field definitions for THRESHOLD_INP_BUF_2 */ +#define DCU_THRESHOLD_INP_BUF_2_INP_BUF_P4_HI_SHIFT (24) +#define DCU_THRESHOLD_INP_BUF_2_INP_BUF_P4_HI_MASK ((0x0000007F) << (DCU_THRESHOLD_INP_BUF_2_INP_BUF_P4_HI_SHIFT)) + +#define DCU_THRESHOLD_INP_BUF_2_INP_BUF_P4_LO_SHIFT (16) +#define DCU_THRESHOLD_INP_BUF_2_INP_BUF_P4_LO_MASK ((0x0000007F) << (DCU_THRESHOLD_INP_BUF_2_INP_BUF_P4_LO_SHIFT)) + +#define DCU_THRESHOLD_INP_BUF_2_INP_BUF_P3_HI_SHIFT (8) +#define DCU_THRESHOLD_INP_BUF_2_INP_BUF_P3_HI_MASK ((0x0000007F) << (DCU_THRESHOLD_INP_BUF_2_INP_BUF_P3_HI_SHIFT)) + +#define DCU_THRESHOLD_INP_BUF_2_INP_BUF_P3_LO_SHIFT (0) +#define DCU_THRESHOLD_INP_BUF_2_INP_BUF_P3_LO_MASK ((0x0000007F) << (DCU_THRESHOLD_INP_BUF_2_INP_BUF_P3_LO_SHIFT)) + + + +/* Field definitions for THRESHOLD_INP_BUF_3 */ +#define DCU_THRESHOLD_INP_BUF_3_INP_BUF_P6_HI_SHIFT (24) +#define DCU_THRESHOLD_INP_BUF_3_INP_BUF_P6_HI_MASK ((0x0000007F) << (DCU_THRESHOLD_INP_BUF_3_INP_BUF_P6_HI_SHIFT)) + +#define DCU_THRESHOLD_INP_BUF_3_INP_BUF_P6_LO_SHIFT (16) +#define DCU_THRESHOLD_INP_BUF_3_INP_BUF_P6_LO_MASK ((0x0000007F) << (DCU_THRESHOLD_INP_BUF_3_INP_BUF_P6_LO_SHIFT)) + +#define DCU_THRESHOLD_INP_BUF_3_INP_BUF_P5_HI_SHIFT (8) +#define DCU_THRESHOLD_INP_BUF_3_INP_BUF_P5_HI_MASK ((0x0000007F) << (DCU_THRESHOLD_INP_BUF_3_INP_BUF_P5_HI_SHIFT)) + +#define DCU_THRESHOLD_INP_BUF_3_INP_BUF_P5_LO_SHIFT (0) +#define DCU_THRESHOLD_INP_BUF_3_INP_BUF_P5_LO_MASK ((0x0000007F) << (DCU_THRESHOLD_INP_BUF_3_INP_BUF_P5_LO_SHIFT)) + + + +/* Field definitions for LUMA_COMP */ +#define DCU_LUMA_COMP_Y_RED_SHIFT (22) +#define DCU_LUMA_COMP_Y_RED_MASK ((0x000003FF) << (DCU_LUMA_COMP_Y_RED_SHIFT)) + +#define DCU_LUMA_COMP_Y_GREEN_SHIFT (11) +#define DCU_LUMA_COMP_Y_GREEN_MASK ((0x000003FF) << (DCU_LUMA_COMP_Y_GREEN_SHIFT)) + +#define DCU_LUMA_COMP_Y_BLUE_SHIFT (0) +#define DCU_LUMA_COMP_Y_BLUE_MASK ((0x000003FF) << (DCU_LUMA_COMP_Y_BLUE_SHIFT)) + + + +/* Field definitions for CHROMA_RED */ +#define DCU_CHROMA_RED_CR_RED_SHIFT (16) +#define DCU_CHROMA_RED_CR_RED_MASK ((0x000007FF) << (DCU_CHROMA_RED_CR_RED_SHIFT)) + +#define DCU_CHROMA_RED_CB_GREEN_SHIFT (0) +#define DCU_CHROMA_RED_CB_GREEN_MASK ((0x00000FFF) << (DCU_CHROMA_RED_CB_GREEN_SHIFT)) + + + +/* Field definitions for CHROMA_GREEN */ +#define DCU_CHROMA_GREEN_CR_GREEN_SHIFT (16) +#define DCU_CHROMA_GREEN_CR_GREEN_MASK ((0x000007FF) << (DCU_CHROMA_GREEN_CR_GREEN_SHIFT)) + +#define DCU_CHROMA_GREEN_CB_GREEN_SHIFT (0) +#define DCU_CHROMA_GREEN_CB_GREEN_MASK ((0x00000FFF) << (DCU_CHROMA_GREEN_CB_GREEN_SHIFT)) + + + +/* Field definitions for CHROMA_BLUE */ +#define DCU_CHROMA_BLUE_CR_BLUE_SHIFT (16) +#define DCU_CHROMA_BLUE_CR_BLUE_MASK ((0x000007FF) << (DCU_CHROMA_BLUE_CR_BLUE_SHIFT)) + +#define DCU_CHROMA_BLUE_CB_BLUE_SHIFT (0) +#define DCU_CHROMA_BLUE_CB_BLUE_MASK ((0x00000FFF) << (DCU_CHROMA_BLUE_CB_BLUE_SHIFT)) + + + +/* Field definitions for CRC_POS */ +#define DCU_CRC_POS_CRC_POS_SHIFT (0) +#define DCU_CRC_POS_CRC_POS_MASK ((0xFFFFFFFF) << (DCU_CRC_POS_CRC_POS_SHIFT)) + + + +/* Field definitions for LYR_INTPOL_EN */ +#define DCU_LYR_INTPOL_EN_EN_SHIFT (0) +#define DCU_LYR_INTPOL_EN_EN_MASK ((1) << (DCU_LYR_INTPOL_EN_EN_SHIFT)) + + + +/* Field definitions for LYR_LUMA_COMP */ +#define DCU_LYR_LUMA_COMP_Y_RED_SHIFT (22) +#define DCU_LYR_LUMA_COMP_Y_RED_MASK ((0x000003FF) << (DCU_LYR_LUMA_COMP_Y_RED_SHIFT)) + +#define DCU_LYR_LUMA_COMP_Y_GREEN_SHIFT (11) +#define DCU_LYR_LUMA_COMP_Y_GREEN_MASK ((0x000003FF) << (DCU_LYR_LUMA_COMP_Y_GREEN_SHIFT)) + +#define DCU_LYR_LUMA_COMP_Y_BLUE_SHIFT (0) +#define DCU_LYR_LUMA_COMP_Y_BLUE_MASK ((0x000003FF) << (DCU_LYR_LUMA_COMP_Y_BLUE_SHIFT)) + + +/* Field definitions for LYR_CHROMA_RED */ +#define DCU_LYR_CHROMA_RED_CR_RED_SHIFT (16) +#define DCU_LYR_CHROMA_RED_CR_RED_MASK ((0x000007FF) << (DCU_LYR_CHROMA_RED_CR_RED_SHIFT)) + +#define DCU_LYR_CHROMA_RED_CB_RED_SHIFT (0) +#define DCU_LYR_CHROMA_RED_CB_RED_MASK ((0x00000FFF) << (DCU_LYR_CHROMA_RED_CB_RED_SHIFT)) + + + +/* Field definitions for LYR_CHROMA_GREEN */ +#define DCU_LYR_CHROMA_GREEN_CR_GREEN_SHIFT (16) +#define DCU_LYR_CHROMA_GREEN_CR_GREEN_MASK ((0x000007FF) << (DCU_LYR_CHROMA_GREEN_CR_GREEN_SHIFT)) + +#define DCU_LYR_CHROMA_GREEN_CB_GREEN_SHIFT (0) +#define DCU_LYR_CHROMA_GREEN_CB_GREEN_MASK ((0x00000FFF) << (DCU_LYR_CHROMA_GREEN_CB_GREEN_SHIFT)) + + +/* Field definitions for LYR_CHROMA_BLUE */ +#define DCU_LYR_CHROMA_BLUE_CR_BLUE_SHIFT (16) +#define DCU_LYR_CHROMA_BLUE_CR_BLUE_MASK ((0x000007FF) << (DCU_LYR_CHROMA_BLUE_CR_BLUE_SHIFT)) + +#define DCU_LYR_CHROMA_BLUE_CB_BLUE_SHIFT (0) +#define DCU_LYR_CHROMA_BLUE_CB_BLUE_MASK ((0x00000FFF) << (DCU_LYR_CHROMA_BLUE_CB_BLUE_SHIFT)) + +/* Field definitions for COMP_IMSIZE */ +#define DCU_COMP_IMSIZE_SIZE_SHIFT (0) +#define DCU_COMP_IMSIZE_SIZE_MASK ((0x003FFFFF) << (DCU_COMP_IMSIZE_SIZE_SHIFT)) + + + +/* Field definitions for UPDATE_MODE */ +#define DCU_UPDATE_MODE_MODE_SHIFT (31) +#define DCU_UPDATE_MODE_MODE_MASK ((1) << (DCU_UPDATE_MODE_MODE_SHIFT)) + +#define DCU_UPDATE_MODE_READREG_SHIFT (30) +#define DCU_UPDATE_MODE_READREG_MASK ((1) << (DCU_UPDATE_MODE_READREG_SHIFT)) + + + +/* Field definitions for UNDERRUN */ +#define DCU_UNDERRUN_LINE_SHIFT (16) +#define DCU_UNDERRUN_LINE_MASK ((0x000007FF) << (DCU_UNDERRUN_LINE_SHIFT)) + +#define DCU_UNDERRUN_PIXEL_SHIFT (0) +#define DCU_UNDERRUN_PIXEL_MASK ((0x000007FF) << (DCU_UNDERRUN_PIXEL_SHIFT)) + + +/* Field definitions for CTRLDESCLn_1 */ +#define DCU_CTRLDESCLn_1_HEIGHT_SHIFT (16) +#define DCU_CTRLDESCLn_1_HEIGHT_MASK ((0x000007FF) << (DCU_CTRLDESCLn_1_HEIGHT_SHIFT)) + +#define DCU_CTRLDESCLn_1_WIDTH_SHIFT (0) +#define DCU_CTRLDESCLn_1_WIDTH_MASK ((0x000007FF) << (DCU_CTRLDESCLn_1_WIDTH_SHIFT)) + + + +/* Field definitions for CTRLDESCLn_2 */ +#define DCU_CTRLDESCLn_2_POSY_SHIFT (16) +#define DCU_CTRLDESCLn_2_POSY_MASK ((0x000007FF) << (DCU_CTRLDESCLn_2_POSY_SHIFT)) + +#define DCU_CTRLDESCLn_2_POSX_SHIFT (0) +#define DCU_CTRLDESCLn_2_POSX_MASK ((0x000007FF) << (DCU_CTRLDESCLn_2_POSX_SHIFT)) + + + +/* Field definitions for CTRLDESCLn_3 */ +#define DCU_CTRLDESCLn_3_ADDR_SHIFT (0) +#define DCU_CTRLDESCLn_3_ADDR_MASK ((0xFFFFFFFF) << (DCU_CTRLDESCLn_3_ADDR_SHIFT)) + + +/* Field definitions for CTRLDESCLn_4 */ +#define DCU_CTRLDESCLn_4_EN_SHIFT (31) +#define DCU_CTRLDESCLn_4_EN_MASK ((1) << (DCU_CTRLDESCLn_4_EN_SHIFT)) + +#define DCU_CTRLDESCLn_4_TILE_EN_SHIFT (30) +#define DCU_CTRLDESCLn_4_TILE_EN_MASK ((1) << (DCU_CTRLDESCLn_4_TILE_EN_SHIFT)) + +#define DCU_CTRLDESCLn_4_DATA_SEL_SHIFT (29) +#define DCU_CTRLDESCLn_4_DATA_SEL_MASK ((1) << (DCU_CTRLDESCLn_4_DATA_SEL_SHIFT)) + +#define DCU_CTRLDESCLn_4_SAFETY_EN_SHIFT (28) +#define DCU_CTRLDESCLn_4_SAFETY_EN_MASK ((1) << (DCU_CTRLDESCLn_4_SAFETY_EN_SHIFT)) + +#define DCU_CTRLDESCLn_4_TRANS_SHIFT (20) +#define DCU_CTRLDESCLn_4_TRANS_MASK ((0x000000FF) << (DCU_CTRLDESCLn_4_TRANS_SHIFT)) + +#define DCU_CTRLDESCLn_4_BPP_SHIFT (16) +#define DCU_CTRLDESCLn_4_BPP_MASK ((0x0000000F) << (DCU_CTRLDESCLn_4_BPP_SHIFT)) + +#define DCU_CTRLDESCLn_4_EN_RLE_SHIFT (15) +#define DCU_CTRLDESCLn_4_EN_RLE_MASK ((1) << (DCU_CTRLDESCLn_4_EN_RLE_SHIFT)) + +#define DCU_CTRLDESCLn_4_LUOFFS_SHIFT (4) +#define DCU_CTRLDESCLn_4_LUOFFS_MASK ((0x000007FF) << (DCU_CTRLDESCLn_4_LUOFFS_SHIFT)) + +#define DCU_CTRLDESCLn_4_BB_SHIFT (2) +#define DCU_CTRLDESCLn_4_BB_MASK ((1) << (DCU_CTRLDESCLn_4_BB_SHIFT)) + +#define DCU_CTRLDESCLn_4_AB_SHIFT (0) +#define DCU_CTRLDESCLn_4_AB_MASK ((0x00000003) << (DCU_CTRLDESCLn_4_AB_SHIFT)) + + + +/* Field definitions for CTRLDESCLn_5 */ +#define DCU_CTRLDESCLn_5_CKMAX_R_SHIFT (16) +#define DCU_CTRLDESCLn_5_CKMAX_R_MASK ((0x000000FF) << (DCU_CTRLDESCLn_5_CKMAX_R_SHIFT)) + +#define DCU_CTRLDESCLn_5_CKMAX_G_SHIFT (8) +#define DCU_CTRLDESCLn_5_CKMAX_G_MASK ((0x000000FF) << (DCU_CTRLDESCLn_5_CKMAX_G_SHIFT)) + +#define DCU_CTRLDESCLn_5_CKMAX_B_SHIFT (0) +#define DCU_CTRLDESCLn_5_CKMAX_B_MASK ((0x000000FF) << (DCU_CTRLDESCLn_5_CKMAX_B_SHIFT)) + + + +/* Field definitions for CTRLDESCLn_6 */ +#define DCU_CTRLDESCLn_6_CKMIN_R_SHIFT (16) +#define DCU_CTRLDESCLn_6_CKMIN_R_MASK ((0x000000FF) << (DCU_CTRLDESCLn_6_CKMIN_R_SHIFT)) + +#define DCU_CTRLDESCLn_6_CKMIN_G_SHIFT (8) +#define DCU_CTRLDESCLn_6_CKMIN_G_MASK ((0x000000FF) << (DCU_CTRLDESCLn_6_CKMIN_G_SHIFT)) + +#define DCU_CTRLDESCLn_6_CKMIN_B_SHIFT (0) +#define DCU_CTRLDESCLn_6_CKMIN_B_MASK ((0x000000FF) << (DCU_CTRLDESCLn_6_CKMIN_B_SHIFT)) + + + +/* Field definitions for CTRLDESCLn_7 */ +#define DCU_CTRLDESCLn_7_TILE_VER_SIZE_SHIFT (16) +#define DCU_CTRLDESCLn_7_TILE_VER_SIZE_MASK ((0x000007FF) << (DCU_CTRLDESCLn_7_TILE_VER_SIZE_SHIFT)) + +#define DCU_CTRLDESCLn_7_TILE_HOR_SIZE_SHIFT (0) +#define DCU_CTRLDESCLn_7_TILE_HOR_SIZE_MASK ((0x0000007F) << (DCU_CTRLDESCLn_7_TILE_HOR_SIZE_SHIFT)) + + + +/* Field definitions for CTRLDESCLn_8 */ +#define DCU_CTRLDESCLn_8_COLOR_SHIFT (0) +#define DCU_CTRLDESCLn_8_COLOR_MASK ((0x00FFFFFF) << (DCU_CTRLDESCLn_8_COLOR_SHIFT)) + + + +/* Field definitions for CTRLDESCLn_9 */ +#define DCU_CTRLDESCLn_9_COLOR_SHIFT (0) +#define DCU_CTRLDESCLn_9_COLOR_MASK ((0x00FFFFFF) << (DCU_CTRLDESCLn_9_COLOR_SHIFT)) + + +/*** End of bit definitions ***/ + +/*** define base address ***/ +#define DCU0_BASE 0x40058000 +#define DCU1_BASE 0x400D8000 + +/* TODO: Cleanup and move to TCON header file/driver */ +#define TCON0_BASE 0x4003D000 +#define TCON_CTRL1_OFFSET (0x00000000/4) +#define TCON_CTRL1_TCON_BYPASS_SHIFT (29) +#define TCON_CTRL1_TCON_BYPASS_MASK ((1) << (TCON_CTRL1_TCON_BYPASS_SHIFT)) + + +#endif diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 7ca56f93cf53..7b61c433f265 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2400,6 +2400,9 @@ config FB_MXC_HDMI help Driver for the on-chip MXC HDMI controller. +if ARCH_MVF +source "drivers/video/mvf/Kconfig" +endif if VT source "drivers/video/console/Kconfig" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 0ebfeecca8db..3b9db9e573d7 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -50,6 +50,7 @@ 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_MVF) += mvf/ obj-$(CONFIG_FB_NEOMAGIC) += neofb.o obj-$(CONFIG_FB_3DFX) += tdfxfb.o obj-$(CONFIG_FB_CONTROL) += controlfb.o diff --git a/drivers/video/mvf/Kconfig b/drivers/video/mvf/Kconfig new file mode 100644 index 000000000000..2c928a5c9147 --- /dev/null +++ b/drivers/video/mvf/Kconfig @@ -0,0 +1,20 @@ +config FB_MVF + tristate "MVF Framebuffer support" + depends on FB && (MVF_DCU || ARCH_VF6XX) + default y + help + This is a framebuffer device for the MVF LCD Controller. + See <http://www.linux-fbdev.org/> for information on framebuffer + devices. + + If you plan to use the LCD display with your MVF system, say + Y here. + +config FB_MVF_NUM_FBS + int "Number of Framebuffers" + range 1 32 + default 1 + depends on FB_MVF + help + Select the number of framebuffers corresponding to layers + desired. diff --git a/drivers/video/mvf/Makefile b/drivers/video/mvf/Makefile new file mode 100644 index 000000000000..2f5dbde80084 --- /dev/null +++ b/drivers/video/mvf/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_FB_MVF) += mvf_dispdrv.o mvffb_nec_wqvga.o mvf_dcu4_fb.o diff --git a/drivers/video/mvf/mvf_dcu4_fb.c b/drivers/video/mvf/mvf_dcu4_fb.c new file mode 100644 index 000000000000..956bd3e29a55 --- /dev/null +++ b/drivers/video/mvf/mvf_dcu4_fb.c @@ -0,0 +1,1411 @@ +/* + * 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 Driver for DCU4 + */ + +/*! + * @file mvf_dcu4_fb.c + * + * @brief MVF Frame buffer driver for DCU4 + * + * @ingroup Framebuffer + */ + +/*! + * Include files + */ +#include "mvf_dispdrv.h" +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/fb.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/dma-mapping.h> +#include <linux/clk.h> +#include <linux/console.h> +#include <linux/io.h> +#include <linux/mvffb.h> +#include <linux/uaccess.h> +#include <linux/fsl_devices.h> +#include <asm/mach-types.h> +#include <mach/dcu-v4.h> +#include <linux/platform_device.h> +#include <linux/videodev2.h> + +/* + * Driver name + */ +#define MVFFB_NAME "mvf_dcu4_fb" + +/*! + * Structure containing the MVF specific framebuffer information. + */ +//TODO: How are multiple overlays handled? +struct mvffb_info { + int default_bpp; + int cur_blank; + int next_blank; + int dcu_id; + u32 dcu_pix_fmt; + bool layer; + uint32_t dcu_layer_transfer_finish; + struct fb_info *ovfbi[32]; + void *dcu; + struct mvf_dispdrv_handle *dispdrv; + + bool alpha_en; + dma_addr_t alpha_phy_addr0; + dma_addr_t alpha_phy_addr1; + void *alpha_virt_addr0; + void *alpha_virt_addr1; + uint32_t alpha_mem_len; + uint32_t cur_dcu_buf; + uint32_t cur_dcu_alpha_buf; + u32 pseudo_palette[16]; + struct semaphore flip_sem; + struct semaphore alpha_flip_sem; +}; + +struct mvffb_alloc_list { + struct list_head list; + dma_addr_t phy_addr; + void *cpu_addr; + u32 size; +}; + +static bool g_dp_in_use[2]; +LIST_HEAD(fb_alloc_list); + +static uint32_t bpp_to_pixfmt(struct fb_info *fbi) +{ + //FIXME: For now Supporting 3 formats as given below + uint32_t pixfmt = 0; + + if (fbi->var.nonstd) + return fbi->var.nonstd; + + switch (fbi->var.bits_per_pixel) { + case 24: + pixfmt = V4L2_PIX_FMT_RGB24; + break; + case 32: + pixfmt = V4L2_PIX_FMT_RGB32; + break; + case 16: + pixfmt = V4L2_PIX_FMT_RGB565; + break; + } + return pixfmt; +} + +static bool dcu_usage[2]; +static int dcu_set_usage(int dcu) +{ + if (dcu_usage[dcu]) + return -EBUSY; + else + dcu_usage[dcu] = true; + return 0; +} + +static void dcu_clear_usage(int dcu) +{ + dcu_usage[dcu] = false; +} + +static struct fb_info *found_registered_fb(int layer_id, int dcu_id) +{ + int i; + struct mvffb_info *mvf_fbi; + struct fb_info *fbi = NULL; + + for (i = 0; i < num_registered_fb; i++) { + mvf_fbi = + ((struct mvffb_info *)(registered_fb[i]->par)); + + if ((mvf_fbi->dcu_id == dcu_id) && (registered_fb[i]->node == layer_id)) { + fbi = registered_fb[i]; + break; + } + } + return fbi; +} + +static int mvffb_blank(int blank, struct fb_info *info); +static int mvffb_map_video_memory(struct fb_info *fbi); +static int mvffb_unmap_video_memory(struct fb_info *fbi); + +/* + * Set fixed framebuffer parameters based on variable settings. + * + * @param info framebuffer information pointer + */ +static int mvffb_set_fix(struct fb_info *info) +{ + struct fb_fix_screeninfo *fix = &info->fix; + struct fb_var_screeninfo *var = &info->var; + + fix->line_length = var->xres_virtual * var->bits_per_pixel / 8; + + fix->type = FB_TYPE_PACKED_PIXELS; + fix->accel = FB_ACCEL_NONE; + fix->visual = FB_VISUAL_TRUECOLOR; + fix->xpanstep = 0; + fix->ywrapstep = 0; + fix->ypanstep = 0; + + return 0; +} + +/* + * Set framebuffer parameters and change the operating mode. + * + * @param info framebuffer information pointer + */ +static int mvffb_set_par(struct fb_info *fbi) +{ + int retval = 0; + u32 mem_len; + dcu_di_signal_cfg_t sig_cfg; + struct mvffb_info *mvf_fbi = (struct mvffb_info *)fbi->par; + + dev_dbg(fbi->device, "Reconfiguring framebuffer\n"); + + if (mvf_fbi->dispdrv && mvf_fbi->dispdrv->drv->setup) { + retval = mvf_fbi->dispdrv->drv->setup(mvf_fbi->dispdrv, fbi); + if (retval < 0) { + dev_err(fbi->device, "setup error, dispdrv:%s.\n", + mvf_fbi->dispdrv->drv->name); + return -EINVAL; + } + } + + if(dcu_is_layer_enabled(mvf_fbi->dcu, fbi->node)) + { + retval = dcu_disable_layer(mvf_fbi->dcu, fbi->node, true); + if (retval < 0) { + dev_err(fbi->device, "Error disabling layer, %d\n", fbi->node); + return -EINVAL; + } + dcu_uninit_layer(mvf_fbi->dcu, fbi->node); + } + + mvffb_set_fix(fbi); + + mem_len = fbi->var.yres_virtual * fbi->fix.line_length; + if (!fbi->fix.smem_start || (mem_len > fbi->fix.smem_len)) { + if (fbi->fix.smem_start) + mvffb_unmap_video_memory(fbi); + + if (mvffb_map_video_memory(fbi) < 0) + return -ENOMEM; + } + + if (mvf_fbi->next_blank != FB_BLANK_UNBLANK) + return retval; + + if (!mvf_fbi->layer) { + printk("Framebuffer: Configuring layer in set_par"); + + memset(&sig_cfg, 0, sizeof(sig_cfg)); + if (fbi->var.vmode & FB_VMODE_INTERLACED) + sig_cfg.interlaced = true; + else + sig_cfg.interlaced = false; + if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT) + sig_cfg.Hsync_pol = true; + else + sig_cfg.Hsync_pol = false; + if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT) + sig_cfg.Vsync_pol = true; + else + sig_cfg.Vsync_pol = false; + if (fbi->var.sync & FB_SYNC_INV_PIX_CLK) + sig_cfg.clk_pol = true; + else + sig_cfg.clk_pol = false; + if (fbi->var.sync & FB_SYNC_DATA_INVERT) + sig_cfg.data_pol = true; + else + sig_cfg.data_pol = false; + + printk("Framebuffer: FB Params are Resolution x = %d, y = %d, left_margin = %d, hsync_len= %d, right_margin = %d, upper_margin = %d, vsync_len = %d lower_margin = %d bpp = %d\n", \ + fbi->var.xres, fbi->var.yres, fbi->var.left_margin, fbi->var.hsync_len, fbi->var.right_margin, \ + fbi->var.upper_margin, fbi->var.vsync_len, fbi->var.lower_margin, fbi->var.bits_per_pixel); + + dev_dbg(fbi->device, "pixclock = %ul Hz\n", + (u32) (PICOS2KHZ(fbi->var.pixclock) * 1000UL)); + + if (dcu_init_panel(mvf_fbi->dcu, + (PICOS2KHZ(fbi->var.pixclock)) * 1000UL, + fbi->var.xres, fbi->var.yres, + fbi->var.left_margin, + fbi->var.hsync_len, + fbi->var.right_margin, + fbi->var.upper_margin, + fbi->var.vsync_len, + fbi->var.lower_margin, + 0, sig_cfg) != 0) { + dev_err(fbi->device, + "mvffb: Error initializing panel.\n"); + return -EINVAL; + } + dcu_enable(mvf_fbi->dcu); + } + + fbi->mode = + (struct fb_videomode *)fb_match_mode(&fbi->var, + &fbi->modelist); + + dcu_init_layer(mvf_fbi->dcu, fbi->node, bpp_to_pixfmt(fbi), fbi->var.xres, fbi->var.yres, fbi->fix.smem_start, 0, 0); + dcu_update_layer_buffer(mvf_fbi->dcu, fbi->node, fbi->fix.smem_start); + dcu_enable_layer(mvf_fbi->dcu, fbi->node); + dcu_set_layer_position(mvf_fbi->dcu, fbi->node, 0,0); + + if (mvf_fbi->dispdrv && mvf_fbi->dispdrv->drv->enable) { + retval = mvf_fbi->dispdrv->drv->enable(mvf_fbi->dispdrv); + if (retval < 0) { + dev_err(fbi->device, "enable error, dispdrv:%s.\n", + mvf_fbi->dispdrv->drv->name); + return -EINVAL; + } + } + + return retval; +} + +/* + * Check framebuffer variable parameters and adjust to valid values. + * + * @param var framebuffer variable parameters + * + * @param info framebuffer information pointer + */ +static int mvffb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + u32 vtotal; + u32 htotal; + +/* DCU does not support rotation, TODO: add software implementation? + if (var->rotate > IPU_ROTATE_VERT_FLIP) + var->rotate = IPU_ROTATE_NONE; +*/ + if (var->xres_virtual < var->xres) + var->xres_virtual = var->xres; + +/* TODO: Remove magic number 2 and replace with double buffer or not */ + if (var->yres_virtual < var->yres) + var->yres_virtual = var->yres * 2; + + if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) && + (var->bits_per_pixel != 16) && (var->bits_per_pixel != 8)) + var->bits_per_pixel = 16; + + switch (var->bits_per_pixel) { + case 8: + var->red.length = 3; + var->red.offset = 5; + var->red.msb_right = 0; + + var->green.length = 3; + var->green.offset = 2; + var->green.msb_right = 0; + + var->blue.length = 2; + var->blue.offset = 0; + var->blue.msb_right = 0; + + var->transp.length = 0; + var->transp.offset = 0; + var->transp.msb_right = 0; + break; + case 16: + var->red.length = 5; + var->red.offset = 11; + var->red.msb_right = 0; + + var->green.length = 6; + var->green.offset = 5; + var->green.msb_right = 0; + + var->blue.length = 5; + var->blue.offset = 0; + var->blue.msb_right = 0; + + var->transp.length = 0; + var->transp.offset = 0; + var->transp.msb_right = 0; + break; + case 24: + var->red.length = 8; + var->red.offset = 16; + var->red.msb_right = 0; + + var->green.length = 8; + var->green.offset = 8; + var->green.msb_right = 0; + + var->blue.length = 8; + var->blue.offset = 0; + var->blue.msb_right = 0; + + var->transp.length = 0; + var->transp.offset = 0; + var->transp.msb_right = 0; + break; + case 32: + var->red.length = 8; + var->red.offset = 16; + var->red.msb_right = 0; + + var->green.length = 8; + var->green.offset = 8; + var->green.msb_right = 0; + + var->blue.length = 8; + var->blue.offset = 0; + var->blue.msb_right = 0; + + var->transp.length = 8; + var->transp.offset = 24; + var->transp.msb_right = 0; + break; + } + + if (var->pixclock < 1000) { + htotal = var->xres + var->right_margin + var->hsync_len + + var->left_margin; + vtotal = var->yres + var->lower_margin + var->vsync_len + + var->upper_margin; + var->pixclock = (vtotal * htotal * 6UL) / 100UL; + var->pixclock = KHZ2PICOS(var->pixclock); + dev_dbg(info->device, + "pixclock set for 60Hz refresh = %u ps\n", + var->pixclock); + } + + var->height = -1; + var->width = -1; + var->grayscale = 0; + + return 0; +} + +/* + * Function to handle custom ioctls for MVF framebuffer. + * + * @param inode inode struct + * + * @param file file struct + * + * @param cmd Ioctl command to handle + * + * @param arg User pointer to command arguments + * + * @param fbi framebuffer information pointer + */ +static int mvffb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg) +{ + int retval = 0; + int __user *argp = (void __user *)arg; + struct mvffb_info *mvf_fbi = (struct mvffb_info *)fbi->par; + + switch (cmd) { + case MVFFB_SET_LAYER_ALPHA: + { + struct mvffb_layer_alpha ga; + + if (copy_from_user(&ga, (void *)arg, sizeof(ga))) { + retval = -EFAULT; + break; + } + + if(dcu_config_layer_alpha(mvf_fbi->dcu, + fbi->node, + ga.alpha, + ga.blend_enable)) { + retval = -EINVAL; + break; + } + + printk("Framebuffer: Set Layer Alpha of %s to %d\n", fbi->fix.id, ga.alpha); + dev_dbg(fbi->device, "Set Layer Alpha of %s to %d\n", fbi->fix.id, ga.alpha); + + break; + } + case FBIO_ALLOC: + { + int size; + struct mvffb_alloc_list *mem; + + mem = kzalloc(sizeof(*mem), GFP_KERNEL); + if (mem == NULL) + return -ENOMEM; + + if (get_user(size, argp)) + return -EFAULT; + + mem->size = PAGE_ALIGN(size); + + mem->cpu_addr = dma_alloc_coherent(fbi->device, size, + &mem->phy_addr, + GFP_DMA); + if (mem->cpu_addr == NULL) { + kfree(mem); + return -ENOMEM; + } + + list_add(&mem->list, &fb_alloc_list); + + dev_dbg(fbi->device, "allocated %d bytes @ 0x%08X\n", + mem->size, mem->phy_addr); + + if (put_user(mem->phy_addr, argp)) + return -EFAULT; + + break; + } + case FBIO_FREE: + { + unsigned long offset; + struct mvffb_alloc_list *mem; + + if (get_user(offset, argp)) + return -EFAULT; + + retval = -EINVAL; + list_for_each_entry(mem, &fb_alloc_list, list) { + if (mem->phy_addr == offset) { + list_del(&mem->list); + dma_free_coherent(fbi->device, + mem->size, + mem->cpu_addr, + mem->phy_addr); + kfree(mem); + retval = 0; + break; + } + } + + break; + } + case MVFFB_GET_FB_BLANK: + { + struct mvffb_info *mvf_fbi = + (struct mvffb_info *)fbi->par; + + if (put_user(mvf_fbi->cur_blank, argp)) + return -EFAULT; + break; + } + case MVFFB_SETUP_LAYER: + { + struct mvffb_layer_pos pos; + struct fb_info *bg_fbi = NULL; + struct mvffb_info *bg_mvffbi = NULL; + + if (copy_from_user(&pos, (void *)arg, sizeof(pos))) { + retval = -EFAULT; + break; + } + + bg_fbi = found_registered_fb(0, mvf_fbi->dcu_id); + if (bg_fbi) + bg_mvffbi = ((struct mvffb_info *)(bg_fbi->par)); + + if (bg_fbi == NULL) { + printk("Framebuffer: Cannot find the background framebuffer\n"); + dev_err(fbi->device, "Cannot find the " + "background framebuffer \n"); + retval = -ENOENT; + break; + } + + // if fb is unblank, check if the pos fit the display + if (mvf_fbi->cur_blank == FB_BLANK_UNBLANK) { + if (fbi->var.xres + pos.x > bg_fbi->var.xres) { + if (bg_fbi->var.xres < fbi->var.xres) + pos.x = 0; + else + pos.x = bg_fbi->var.xres - fbi->var.xres; + } + if (fbi->var.yres + pos.y > bg_fbi->var.yres) { + if (bg_fbi->var.yres < fbi->var.yres) + pos.y = 0; + else + pos.y = bg_fbi->var.yres - fbi->var.yres; + } + } + + retval = dcu_set_layer_position(mvf_fbi->dcu, fbi->node, pos.x, pos.y); + if(retval != 0) + { + break; + } + + if (copy_to_user((void *)arg, &pos, sizeof(pos))) { + retval = -EFAULT; + break; + } + + break; + } + default: + retval = -EINVAL; + } + return retval; +} + +/* + * mvffb_blank(): + * Blank the display. + */ +static int mvffb_blank(int blank, struct fb_info *info) +{ + struct mvffb_info *mvf_fbi = (struct mvffb_info *)info->par; + int ret = 0; + + dev_dbg(info->device, "blank = %d\n", blank); + + if (mvf_fbi->cur_blank == blank) + return 0; + + mvf_fbi->next_blank = blank; + + switch (blank) { + case FB_BLANK_POWERDOWN: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_NORMAL: + if (mvf_fbi->dispdrv && mvf_fbi->dispdrv->drv->disable) + mvf_fbi->dispdrv->drv->disable(mvf_fbi->dispdrv); + //TODO: Remove / understand true. Not used in dcu driver. + dcu_disable_layer(mvf_fbi->dcu, info->node, true); + dcu_uninit_layer(mvf_fbi->dcu, info->node); + dcu_uninit_panel(mvf_fbi->dcu); + break; + case FB_BLANK_UNBLANK: + ret = mvffb_set_par(info); + break; + } + if (!ret) + mvf_fbi->cur_blank = blank; + return ret; +} + +/* + * Pan or Wrap the Display + * + * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag + * + * @param var Variable screen buffer information + * @param info Framebuffer information pointer + */ +static int +mvffb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +{ + /* + struct mvffb_info *mvf_fbi = (struct mvffb_info *)info->par, + *mvf_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; + int i; + + if (info->var.yoffset == var->yoffset) + return 0; // No change, do nothing + + // no pan display during fb blank + if (mvf_fbi->cur_blank != FB_BLANK_UNBLANK) + return -EINVAL; + + y_bottom = var->yoffset; + + if (y_bottom > info->var.yres_virtual) + return -EINVAL; + + fb_stride = info->fix.line_length; + + 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 + for (i = 0; i < num_registered_fb; i++) { + char *bg_id = "DISP3 BG"; + char *fg_id = "DISP3 FG"; + char *idstr = registered_fb[i]->fix.id; + bg_id[4] += mvf_fbi->dcu_id; + fg_id[4] += mvf_fbi->dcu_id; + if ((strcmp(idstr, bg_id) == 0 || + strcmp(idstr, fg_id) == 0) && + ((struct mvffb_info *) + (registered_fb[i]->par))->alpha_chan_en) { + loc_alpha_en = true; + mvf_graphic_fbi = (struct mvffb_info *) + (registered_fb[i]->par); + active_alpha_phy_addr = + mvf_fbi->cur_dcu_alpha_buf ? + mvf_graphic_fbi->alpha_phy_addr1 : + mvf_graphic_fbi->alpha_phy_addr0; + dev_dbg(info->device, "Updating SDC alpha " + "buf %d address=0x%08lX\n", + !mvf_fbi->cur_dcu_alpha_buf, + active_alpha_phy_addr); + break; + } + } + + if (down_timeout(&mvf_fbi->flip_sem, HZ/2)) { + dev_err(info->device, "timeout when waiting for flip irq\n"); + return -ETIMEDOUT; + } + + ++mvf_fbi->cur_dcu_buf; + mvf_fbi->cur_dcu_buf %= 3; + mvf_fbi->cur_dcu_alpha_buf = !mvf_fbi->cur_dcu_alpha_buf; + + dev_dbg(info->device, "Updating SDC %s buf %d address=0x%08lX\n", + info->fix.id, mvf_fbi->cur_dcu_buf, base); + + if (dcu_update_layer_buffer(mvf_fbi->dcu, mvf_fbi->dcu_ch, IPU_INPUT_BUFFER, + mvf_fbi->cur_dcu_buf, base) == 0) { + // Update the DP local alpha buffer only for graphic plane + if (loc_alpha_en && mvf_graphic_fbi == mvf_fbi && + dcu_update_layer_buffer(mvf_graphic_fbi->dcu, mvf_graphic_fbi->dcu_ch, + IPU_ALPHA_IN_BUFFER, + mvf_fbi->cur_dcu_alpha_buf, + active_alpha_phy_addr) == 0) { + dcu_select_buffer(mvf_graphic_fbi->dcu, mvf_graphic_fbi->dcu_ch, + IPU_ALPHA_IN_BUFFER, + mvf_fbi->cur_dcu_alpha_buf); + } + + // update u/v offset + dcu_update_layer_offset(mvf_fbi->dcu, mvf_fbi->dcu_ch, + IPU_INPUT_BUFFER, + bpp_to_pixfmt(info), + fr_w, + fr_h, + fr_w, + 0, 0, + fr_yoff, + fr_xoff); + + dcu_select_buffer(mvf_fbi->dcu, mvf_fbi->dcu_ch, IPU_INPUT_BUFFER, + mvf_fbi->cur_dcu_buf); + dcu_clear_irq(mvf_fbi->dcu, mvf_fbi->dcu_ch_irq); + dcu_enable_irq(mvf_fbi->dcu, mvf_fbi->dcu_ch_irq); + } else { + dev_err(info->device, + "Error updating SDC buf %d to address=0x%08lX, " + "current buf %d, buf0 ready %d, buf1 ready %d, " + "buf2 ready %d\n", mvf_fbi->cur_dcu_buf, base, + dcu_get_cur_buffer_idx(mvf_fbi->dcu, mvf_fbi->dcu_ch, + IPU_INPUT_BUFFER), + dcu_check_buffer_ready(mvf_fbi->dcu, mvf_fbi->dcu_ch, + IPU_INPUT_BUFFER, 0), + dcu_check_buffer_ready(mvf_fbi->dcu, mvf_fbi->dcu_ch, + IPU_INPUT_BUFFER, 1), + dcu_check_buffer_ready(mvf_fbi->dcu, mvf_fbi->dcu_ch, + IPU_INPUT_BUFFER, 2)); + ++mvf_fbi->cur_dcu_buf; + mvf_fbi->cur_dcu_buf %= 3; + ++mvf_fbi->cur_dcu_buf; + mvf_fbi->cur_dcu_buf %= 3; + mvf_fbi->cur_dcu_alpha_buf = !mvf_fbi->cur_dcu_alpha_buf; + dcu_clear_irq(mvf_fbi->dcu, mvf_fbi->dcu_ch_irq); + dcu_enable_irq(mvf_fbi->dcu, mvf_fbi->dcu_ch_irq); + return -EBUSY; + } + + dev_dbg(info->device, "Update complete\n"); + + info->var.yoffset = var->yoffset; + */ + return 0; +} + +/* + * Function to handle custom mmap for MVF framebuffer. + * + * @param fbi framebuffer information pointer + * + * @param vma Pointer to vm_area_struct + */ +static int mvffb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) +{ + bool found = false; + u32 len; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + struct mvffb_alloc_list *mem; + struct mvffb_info *mvf_fbi = (struct mvffb_info *)fbi->par; + + if (offset < fbi->fix.smem_len) { + /* mapping framebuffer memory */ + len = fbi->fix.smem_len - offset; + vma->vm_pgoff = (fbi->fix.smem_start + offset) >> PAGE_SHIFT; + } else if ((vma->vm_pgoff == + (mvf_fbi->alpha_phy_addr0 >> PAGE_SHIFT)) || + (vma->vm_pgoff == + (mvf_fbi->alpha_phy_addr1 >> PAGE_SHIFT))) { + len = mvf_fbi->alpha_mem_len; + } else { + list_for_each_entry(mem, &fb_alloc_list, list) { + if (offset == mem->phy_addr) { + found = true; + len = mem->size; + break; + } + } + if (!found) + return -EINVAL; + } + + len = PAGE_ALIGN(len); + if (vma->vm_end - vma->vm_start > len) + return -EINVAL; + + /* make buffers bufferable */ + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + vma->vm_flags |= VM_IO | VM_RESERVED; + + if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma->vm_end - vma->vm_start, vma->vm_page_prot)) { + dev_dbg(fbi->device, "mmap remap_pfn_range failed\n"); + return -ENOBUFS; + } + + return 0; +} + +/*! + * This structure contains the pointers to the control functions that are + * invoked by the core framebuffer driver to perform operations like + * blitting, rectangle filling, copy regions and cursor definition. + */ +static struct fb_ops mvffb_ops = { + .owner = THIS_MODULE, + .fb_set_par = mvffb_set_par, + .fb_check_var = mvffb_check_var, + .fb_pan_display = mvffb_pan_display, + .fb_ioctl = mvffb_ioctl, + .fb_mmap = mvffb_mmap, + //.fb_fillrect = cfb_fillrect, + //.fb_copyarea = cfb_copyarea, + //.fb_imageblit = cfb_imageblit, + .fb_blank = mvffb_blank, +}; + +/* + * Suspends the framebuffer and blanks the screen. Power management support + */ +static int mvffb_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct fb_info *fbi = platform_get_drvdata(pdev); + struct mvffb_info *mvf_fbi = (struct mvffb_info *)fbi->par; + int saved_blank; + + console_lock(); + fb_set_suspend(fbi, 1); + saved_blank = mvf_fbi->cur_blank; + mvffb_blank(FB_BLANK_POWERDOWN, fbi); + mvf_fbi->next_blank = saved_blank; + console_unlock(); + + return 0; +} + +/* + * Resumes the framebuffer and unblanks the screen. Power management support + */ +static int mvffb_resume(struct platform_device *pdev) +{ + struct fb_info *fbi = platform_get_drvdata(pdev); + struct mvffb_info *mvf_fbi = (struct mvffb_info *)fbi->par; + + console_lock(); + mvffb_blank(mvf_fbi->next_blank, fbi); + fb_set_suspend(fbi, 0); + console_unlock(); + + return 0; +} + +/* + * Main framebuffer functions + */ + +/*! + * Allocates the DRAM memory for the frame buffer. This buffer is remapped + * into a non-cached, non-buffered, memory region to allow palette and pixel + * writes to occur without flushing the cache. Once this area is remapped, + * all virtual memory access to the video memory should occur at the new region. + * + * @param fbi framebuffer information pointer + * + * @return Error code indicating success or failure + */ +static int mvffb_map_video_memory(struct fb_info *fbi) +{ + if (fbi->fix.smem_len < fbi->var.yres_virtual * fbi->fix.line_length) + fbi->fix.smem_len = fbi->var.yres_virtual * + fbi->fix.line_length; + + fbi->screen_base = dma_alloc_writecombine(fbi->device, + fbi->fix.smem_len, + (dma_addr_t *)&fbi->fix.smem_start, + GFP_DMA); + if (fbi->screen_base == 0) { + dev_err(fbi->device, "Unable to allocate framebuffer memory\n"); + fbi->fix.smem_len = 0; + fbi->fix.smem_start = 0; + return -EBUSY; + } + + dev_dbg(fbi->device, "allocated fb @ paddr=0x%08X, size=%d.\n", + (uint32_t) fbi->fix.smem_start, fbi->fix.smem_len); + + fbi->screen_size = fbi->fix.smem_len; + + /* Clear the screen */ + memset((char *)fbi->screen_base, 0, fbi->fix.smem_len); + + return 0; +} + +/*! + * De-allocates the DRAM memory for the frame buffer. + * + * @param fbi framebuffer information pointer + * + * @return Error code indicating success or failure + */ +static int mvffb_unmap_video_memory(struct fb_info *fbi) +{ + dma_free_writecombine(fbi->device, fbi->fix.smem_len, + fbi->screen_base, fbi->fix.smem_start); + fbi->screen_base = 0; + fbi->fix.smem_start = 0; + fbi->fix.smem_len = 0; + return 0; +} + +/*! + * Initializes the framebuffer information pointer. After allocating + * sufficient memory for the framebuffer structure, the fields are + * filled with custom information passed in from the configurable + * structures. This includes information such as bits per pixel, + * color maps, screen width/height and RGBA offsets. + * + * @return Framebuffer structure initialized with our information + */ +static struct fb_info *mvffb_init_fbinfo(struct device *dev, struct fb_ops *ops) +{ + struct fb_info *fbi; + struct mvffb_info *mvffbi; + + /* + * Allocate sufficient memory for the fb structure + */ + fbi = framebuffer_alloc(sizeof(struct mvffb_info), dev); + if (!fbi) + return NULL; + + mvffbi = (struct mvffb_info *)fbi->par; + + fbi->var.activate = FB_ACTIVATE_NOW; + + fbi->fbops = ops; + fbi->flags = FBINFO_MODULE; + fbi->pseudo_palette = mvffbi->pseudo_palette; + + /* + * Allocate colormap + */ + fb_alloc_cmap(&fbi->cmap, 16, 0); + + return fbi; +} + +static int mvffb_dispdrv_init(struct platform_device *pdev, + struct fb_info *fbi) +{ + struct dcuv4_fb_platform_data *plat_data = pdev->dev.platform_data; + struct mvffb_info *mvffbi = (struct mvffb_info *)fbi->par; + struct mvf_dispdrv_setting setting; + char disp_dev[32], *default_dev = "lcd"; + int ret = 0; + + setting.if_fmt = plat_data->interface_pix_fmt; + setting.dft_mode_str = plat_data->mode_str; + setting.default_bpp = plat_data->default_bpp; + + //TODO: What should be our default bpp? + if (!setting.default_bpp) + setting.default_bpp = 16; + setting.fbi = fbi; + if (!strlen(plat_data->disp_dev)) { + memcpy(disp_dev, default_dev, strlen(default_dev)); + disp_dev[strlen(default_dev)] = '\0'; + } else { + memcpy(disp_dev, plat_data->disp_dev, + strlen(plat_data->disp_dev)); + disp_dev[strlen(plat_data->disp_dev)] = '\0'; + } + + dev_info(&pdev->dev, "Register mvf display driver %s\n", disp_dev); + + mvffbi->dispdrv = mvf_dispdrv_gethandle(disp_dev, &setting); + if (IS_ERR(mvffbi->dispdrv)) { + ret = PTR_ERR(mvffbi->dispdrv); + dev_err(&pdev->dev, "NO mvf display driver found!\n"); + return ret; + } else { + // fix-up + mvffbi->dcu_pix_fmt = setting.if_fmt; + mvffbi->default_bpp = setting.default_bpp; + + // setting - set default setting of dev_id if plat_data dcu_id is DCU0 + if(!plat_data->dcu_id) + mvffbi->dcu_id = setting.dev_id; + } + + return ret; +} + +/* + * Parse user specified options (`video=trident:') + * example: + * orig - video=mvffb0:dev=lcd,800x480M-16@55,if=RGB565,bpp=16,noaccel + * TODO: video=mvffb0:dev=lcd,if=BGRA8888,dcu=0,NEC-WQVGA,primary video=mvffb1:dev=lcd,if=BGRA8888,dcu=1,NEC-WQVGA + * Proposed: video=mvfdcu0fb:BGRA8888,NEC-WQVGA dcu0_primary + * Supported Below: video=mvffb0:dev=lcd,if=BGRA8888,dcu=0,NEC-WQVGA + * TODO: re-visit the supported formats below to enhance the list + */ +static int mvffb_option_setup(struct platform_device *pdev) +{ + struct dcuv4_fb_platform_data *pdata = pdev->dev.platform_data; + char *options, *opt, *fb_mode_str = NULL; + char name[] = "mvffb0"; + + name[5] += pdev->id; + fb_get_options(name, &options); + + if (!options || !*options) + return 0; + + while ((opt = strsep(&options, ",")) != NULL) { + if (!*opt) + continue; + + if (!strncmp(opt, "dev=", 4)) { + memcpy(pdata->disp_dev, opt + 4, strlen(opt) - 4); + pdata->disp_dev[strlen(opt) - 4] = '\0'; + continue; + } + if (!strncmp(opt, "if=", 3)) { + if (!strncmp(opt+3, "RGB24", 5)) { + pdata->interface_pix_fmt = V4L2_PIX_FMT_RGB24; + continue; + } + if (!strncmp(opt+3, "RGB565", 6)) { + pdata->interface_pix_fmt = V4L2_PIX_FMT_RGB565; + continue; + } + if (!strncmp(opt+3, "RGB32", 5)) { + pdata->interface_pix_fmt = V4L2_PIX_FMT_RGB32; + continue; + } + + /*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, "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; + } + */ + } + + if(!strncmp(opt,"dcu=", 4)) + pdata->dcu_id = + simple_strtoul(opt + 4, NULL, 0); + if (!strncmp(opt, "bpp=", 4)) + pdata->default_bpp = + simple_strtoul(opt + 4, NULL, 0); + else + fb_mode_str = opt; + } + + if (fb_mode_str) + pdata->mode_str = fb_mode_str; + + return 0; +} + +static int mvffb_register(struct fb_info *fbi) +{ + struct mvffb_info *mvffbi = (struct mvffb_info *)fbi->par; + struct fb_videomode m; + int ret = 0; + + char disp_id[] = "DCU0 BG LAYER"; + char layer_id[] = "DCU0 FG LAYER"; + + if(!mvffbi->layer) + { + disp_id[3] += mvffbi->dcu_id; + strcpy(fbi->fix.id, disp_id); + } + else + { + layer_id[3] += mvffbi->dcu_id; + strcpy(fbi->fix.id, layer_id); + } + + mvffb_check_var(&fbi->var, fbi); + + mvffb_set_fix(fbi); + + /*added first mode to fbi modelist*/ + if (!fbi->modelist.next || !fbi->modelist.prev) + INIT_LIST_HEAD(&fbi->modelist); + fb_var_to_videomode(&m, &fbi->var); + fb_add_videomode(&m, &fbi->modelist); + + fbi->var.activate |= FB_ACTIVATE_FORCE; + console_lock(); + fbi->flags |= FBINFO_MISC_USEREVENT; + ret = fb_set_var(fbi, &fbi->var); + fbi->flags &= ~FBINFO_MISC_USEREVENT; + console_unlock(); + + if (mvffbi->next_blank == FB_BLANK_UNBLANK) { + console_lock(); + fb_blank(fbi, FB_BLANK_UNBLANK); + console_unlock(); + } + + ret = register_framebuffer(fbi); + + return ret; +} + +static void mvffb_unregister(struct fb_info *fbi) +{ + unregister_framebuffer(fbi); +} + +//Setting up the overlay frame buffer and registering the same. +static int mvffb_setup_overlay(struct platform_device *pdev, + struct fb_info *fbi_bg, struct resource *res) +{ + struct fb_info *ovfbi; + struct mvffb_info *mvffbi_bg = (struct mvffb_info *)fbi_bg->par; + struct mvffb_info *mvffbi_fg; + int ret = 0; + + ovfbi = mvffb_init_fbinfo(&pdev->dev, &mvffb_ops); + if (!ovfbi) { + ret = -ENOMEM; + goto init_ovfbinfo_failed; + } + mvffbi_fg = (struct mvffb_info *)ovfbi->par; + + mvffbi_fg->dcu = dcu_get_soc(mvffbi_bg->dcu_id); + if (IS_ERR(mvffbi_fg->dcu)) { + ret = -ENODEV; + goto get_dcu_failed; + } + mvffbi_fg->dcu_id = mvffbi_bg->dcu_id; + mvffbi_fg->dcu_pix_fmt = mvffbi_bg->dcu_pix_fmt; + mvffbi_fg->layer = true; + mvffbi_fg->cur_blank = mvffbi_fg->next_blank = FB_BLANK_POWERDOWN; + + // Need dummy values until real panel is configured + ovfbi->var.xres = 240; + ovfbi->var.yres = 320; + + if (res && res->start && res->end) { + ovfbi->fix.smem_len = res->end - res->start + 1; + ovfbi->fix.smem_start = res->start; + ovfbi->screen_base = ioremap( + ovfbi->fix.smem_start, + ovfbi->fix.smem_len); + } + + ret = mvffb_register(ovfbi); + if (ret < 0) + goto register_ov_failed; + + mvffbi_bg->ovfbi[ovfbi->node] = ovfbi; + + return ret; + +register_ov_failed: +get_dcu_failed: + fb_dealloc_cmap(&ovfbi->cmap); + framebuffer_release(ovfbi); +init_ovfbinfo_failed: + return ret; +} + +static void mvffb_unsetup_overlay(struct fb_info *fbi_bg, int layer_num) +{ + struct mvffb_info *mvffbi_bg = (struct mvffb_info *)fbi_bg->par; + struct fb_info *ovfbi = mvffbi_bg->ovfbi[layer_num]; + + mvffb_unregister(ovfbi); + + if (&ovfbi->cmap) + fb_dealloc_cmap(&ovfbi->cmap); + framebuffer_release(ovfbi); +} + +/*! + * Probe routine for the framebuffer driver. It is called during the + * driver binding process. The following functions are performed in + * this routine: Framebuffer initialization, Memory allocation and + * mapping, Framebuffer registration, DCU initialization. + * + * @return Appropriate error code to the kernel common code + */ +static int mvffb_probe(struct platform_device *pdev) +{ + //struct dcuv4_fb_platform_data *plat_data = pdev->dev.platform_data; + struct fb_info *fbi; + struct mvffb_info *mvffbi; + struct resource *res; + int ret = 0, i = 0; + + printk("Framebuffer: Probe Entered!!!!\n"); + /* + * Initialize FB structures + */ + fbi = mvffb_init_fbinfo(&pdev->dev, &mvffb_ops); + if (!fbi) { + ret = -ENOMEM; + goto init_fbinfo_failed; + } + printk("Framebuffer: init complete!!!!\n"); + + mvffb_option_setup(pdev); + + printk("Framebuffer: option setup complete!!!!\n"); + + mvffbi = (struct mvffb_info *)fbi->par; + ret = mvffb_dispdrv_init(pdev, fbi); + if (ret < 0) + goto init_dispdrv_failed; + + ret = dcu_set_usage(mvffbi->dcu_id); + if (ret < 0) { + dev_err(&pdev->dev, "dcu%d already in use\n", + mvffbi->dcu_id); + goto dcu_in_busy; + } + + printk("Framebuffer: disp drv init complete!!!!\n"); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res && res->start && res->end) { + fbi->fix.smem_len = res->end - res->start + 1; + fbi->fix.smem_start = res->start; + fbi->screen_base = ioremap(fbi->fix.smem_start, fbi->fix.smem_len); + memset(fbi->screen_base, 0, fbi->fix.smem_len); + } + + mvffbi->dcu = dcu_get_soc(mvffbi->dcu_id); + if (IS_ERR(mvffbi->dcu)) { + ret = -ENODEV; + goto get_dcu_failed; + } + + printk("Framebuffer: got dcu soc handle!!!!\n"); + + /* first user uses DP(display processor) with alpha feature */ + if (!g_dp_in_use[mvffbi->dcu_id]) { + mvffbi->cur_blank = mvffbi->next_blank = FB_BLANK_UNBLANK; + + if(dcu_config_layer_alpha(mvffbi->dcu, fbi->node, 0x80, ALPHA_BLEND_ENABLED)) { + ret = -EINVAL; + goto mvffb_register_failed; + } + //dcu_disp_set_color_key(mvffbi->dcu, mvffbi->dcu_ch, false, 0); + + ret = mvffb_register(fbi); + if (ret < 0) + goto mvffb_register_failed; + + printk("Framebuffer: registered fb with alpha complete!!!!\n"); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + for(i = 1; i <= (CONFIG_FB_MVF_NUM_FBS-1); i++) + { + printk("Framebuffer: Registering Layer %d\n", i); + ret = mvffb_setup_overlay(pdev, fbi, res); + + if (ret < 0) { + mvffb_unregister(fbi); + goto mvffb_setupoverlay_failed; + } + } + + g_dp_in_use[mvffbi->dcu_id] = true; + } else { + mvffbi->cur_blank = mvffbi->next_blank = FB_BLANK_POWERDOWN; + + ret = mvffb_register(fbi); + if (ret < 0) + goto mvffb_register_failed; + printk("Framebuffer: registred fb complete!!!!\n"); + + } + + platform_set_drvdata(pdev, fbi); + +#ifdef CONFIG_LOGO + printk("Framebuffer: showing logo...!!!!\n"); + fb_prepare_logo(fbi, 0); + fb_show_logo(fbi, 0); +#endif + + return 0; + +mvffb_setupoverlay_failed: +mvffb_register_failed: +get_dcu_failed: + dcu_clear_usage(mvffbi->dcu_id); +dcu_in_busy: +init_dispdrv_failed: + fb_dealloc_cmap(&fbi->cmap); + framebuffer_release(fbi); +init_fbinfo_failed: + return ret; +} + +static int mvffb_remove(struct platform_device *pdev) +{ + struct fb_info *fbi = platform_get_drvdata(pdev); + struct mvffb_info *mvf_fbi = fbi->par; + int i = 0; + + if (!fbi) + return 0; + + mvffb_blank(FB_BLANK_POWERDOWN, fbi); + mvffb_unregister(fbi); + mvffb_unmap_video_memory(fbi); + + for(i = 1; i <= (CONFIG_FB_MVF_NUM_FBS-1); i++) + { + if (mvf_fbi->ovfbi[i]) { + mvffb_blank(FB_BLANK_POWERDOWN, mvf_fbi->ovfbi[i]); + mvffb_unsetup_overlay(fbi, i); + mvffb_unmap_video_memory(mvf_fbi->ovfbi[i]); + } + } + + dcu_clear_usage(mvf_fbi->dcu_id); + if (&fbi->cmap) + fb_dealloc_cmap(&fbi->cmap); + framebuffer_release(fbi); + return 0; +} + +/*! + * This structure contains pointers to the power management callback functions. + */ +static struct platform_driver mvffb_driver = { + .driver = { + .name = MVFFB_NAME, + }, + .probe = mvffb_probe, + .remove = mvffb_remove, + .suspend = mvffb_suspend, + .resume = mvffb_resume, +}; + +/*! + * Main entry function for the framebuffer. The function registers the power + * management callback functions with the kernel and also registers the MVFFB + * callback functions with the core Linux framebuffer driver \b fbmem.c + * + * @return Error code indicating success or failure + */ +int __init mvffb_init(void) +{ + return platform_driver_register(&mvffb_driver); +} + +void mvffb_exit(void) +{ + platform_driver_unregister(&mvffb_driver); +} + +module_init(mvffb_init); +module_exit(mvffb_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MVF frame buffer driver"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("fb"); diff --git a/drivers/video/mvf/mvf_dispdrv.c b/drivers/video/mvf/mvf_dispdrv.c new file mode 100644 index 000000000000..84aed84bb6d9 --- /dev/null +++ b/drivers/video/mvf/mvf_dispdrv.c @@ -0,0 +1,144 @@ +/* + * 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 + */ + +/*! + * @file mvf_dispdrv.c + * @brief mvf display driver framework. + * + * A display device driver could call mvf_dispdrv_register(drv) in its dev_probe() function. + * Move all dev_probe() things into mvf_dispdrv_driver->init(), init() function should init + * and feedback setting; + * Move all dev_remove() things into mvf_dispdrv_driver->deinit(); + * 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; + * + * DCU4 fb driver could call mvf_dispdrv_gethandle(name, setting) before a fb + * need be added, with fbi param passing by setting, after + * mvf_dispdrv_gethandle() return, FB driver should get the basic setting + * about fbi info and dcuv4-hw. + * + * @ingroup Framebuffer + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/string.h> +#include "mvf_dispdrv.h" + +static LIST_HEAD(dispdrv_list); +static DEFINE_MUTEX(dispdrv_lock); + +struct mvf_dispdrv_entry { + /* Note: drv always the first element */ + struct mvf_dispdrv_driver *drv; + bool active; + void *priv; + struct list_head list; +}; + +struct mvf_dispdrv_handle *mvf_dispdrv_register(struct mvf_dispdrv_driver *drv) +{ + struct mvf_dispdrv_entry *new; + + mutex_lock(&dispdrv_lock); + + new = kzalloc(sizeof(struct mvf_dispdrv_entry), GFP_KERNEL); + if (!new) { + mutex_unlock(&dispdrv_lock); + return ERR_PTR(-ENOMEM); + } + + new->drv = drv; + list_add_tail(&new->list, &dispdrv_list); + + mutex_unlock(&dispdrv_lock); + + return (struct mvf_dispdrv_handle *)new; +} +EXPORT_SYMBOL_GPL(mvf_dispdrv_register); + +int mvf_dispdrv_unregister(struct mvf_dispdrv_handle *handle) +{ + struct mvf_dispdrv_entry *entry = (struct mvf_dispdrv_entry *)handle; + + if (entry) { + mutex_lock(&dispdrv_lock); + list_del(&entry->list); + mutex_unlock(&dispdrv_lock); + kfree(entry); + return 0; + } else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(mvf_dispdrv_unregister); + +struct mvf_dispdrv_handle *mvf_dispdrv_gethandle(char *name, + struct mvf_dispdrv_setting *setting) +{ + int ret, found = 0; + struct mvf_dispdrv_entry *entry; + + mutex_lock(&dispdrv_lock); + list_for_each_entry(entry, &dispdrv_list, list) { + if (!strcmp(entry->drv->name, name) && (entry->drv->init)) { + ret = entry->drv->init((struct mvf_dispdrv_handle *) + entry, setting); + if (ret >= 0) { + entry->active = true; + found = 1; + break; + } + } + } + mutex_unlock(&dispdrv_lock); + + return found ? (struct mvf_dispdrv_handle *)entry:ERR_PTR(-ENODEV); +} +EXPORT_SYMBOL_GPL(mvf_dispdrv_gethandle); + +void mvf_dispdrv_puthandle(struct mvf_dispdrv_handle *handle) +{ + struct mvf_dispdrv_entry *entry = (struct mvf_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(mvf_dispdrv_puthandle); + +int mvf_dispdrv_setdata(struct mvf_dispdrv_handle *handle, void *data) +{ + struct mvf_dispdrv_entry *entry = (struct mvf_dispdrv_entry *)handle; + + if (entry) { + entry->priv = data; + return 0; + } else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(mvf_dispdrv_setdata); + +void *mvf_dispdrv_getdata(struct mvf_dispdrv_handle *handle) +{ + struct mvf_dispdrv_entry *entry = (struct mvf_dispdrv_entry *)handle; + + if (entry) { + return entry->priv; + } else + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL_GPL(mvf_dispdrv_getdata); diff --git a/drivers/video/mvf/mvf_dispdrv.h b/drivers/video/mvf/mvf_dispdrv.h new file mode 100644 index 000000000000..5de389cb91a4 --- /dev/null +++ b/drivers/video/mvf/mvf_dispdrv.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 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 + */ +#ifndef __MVF_DISPDRV_H__ +#define __MVF_DISPDRV_H__ +#include <linux/fb.h> + +struct mvf_dispdrv_handle { + struct mvf_dispdrv_driver *drv; +}; + +struct mvf_dispdrv_setting { + /*input-feedback parameter*/ + struct fb_info *fbi; + int if_fmt; + int default_bpp; + char *dft_mode_str; + + /*feedback parameter*/ + int dev_id; +}; + +struct mvf_dispdrv_driver { + const char *name; + int (*init) (struct mvf_dispdrv_handle *, struct mvf_dispdrv_setting *); + void (*deinit) (struct mvf_dispdrv_handle *); + /* display driver enable function for extension */ + int (*enable) (struct mvf_dispdrv_handle *); + /* display driver disable function, called at early part of fb_blank */ + void (*disable) (struct mvf_dispdrv_handle *); + /* display driver setup function, called at early part of fb_set_par */ + int (*setup) (struct mvf_dispdrv_handle *, struct fb_info *fbi); +}; + +struct mvf_dispdrv_handle *mvf_dispdrv_register(struct mvf_dispdrv_driver *drv); +int mvf_dispdrv_unregister(struct mvf_dispdrv_handle *handle); +struct mvf_dispdrv_handle *mvf_dispdrv_gethandle(char *name, + struct mvf_dispdrv_setting *setting); +void mvf_dispdrv_puthandle(struct mvf_dispdrv_handle *handle); +int mvf_dispdrv_setdata(struct mvf_dispdrv_handle *handle, void *data); +void *mvf_dispdrv_getdata(struct mvf_dispdrv_handle *handle); +#endif diff --git a/drivers/video/mvf/mvffb_nec_wqvga.c b/drivers/video/mvf/mvffb_nec_wqvga.c new file mode 100644 index 000000000000..82d8ff371aa5 --- /dev/null +++ b/drivers/video/mvf/mvffb_nec_wqvga.c @@ -0,0 +1,145 @@ +/* + * Copyright (C) 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 + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/mvffb.h> +#include <linux/fsl_devices.h> +#include "mvf_dispdrv.h" + +struct mvf_lcdif_data { + struct platform_device *pdev; + struct mvf_dispdrv_handle *disp_lcdif; +}; + +#define DISPDRV_LCD "lcd" + +static struct fb_videomode lcdif_modedb[] = { + { + /* 480x272 @ 75 Hz pixel clock - typical - 10.87Mhz*/ + "NEC-WQVGA", 75, 480, 272, KHZ2PICOS(10870), 2, 2, 1, 1, 41, 2, + /*Active low HSYC/VSYNC, Sample on falling edge of pxl clk, output not negated*/ + 0, + FB_VMODE_NONINTERLACED, + 0,}, +}; + +static int lcdif_modedb_sz = ARRAY_SIZE(lcdif_modedb); + +static int lcdif_init(struct mvf_dispdrv_handle *disp, + struct mvf_dispdrv_setting *setting) +{ + int ret, i; + struct mvf_lcdif_data *lcdif = mvf_dispdrv_getdata(disp); + struct fsl_mvf_lcd_platform_data *plat_data + = lcdif->pdev->dev.platform_data; + struct fb_videomode *modedb = lcdif_modedb; + int modedb_sz = lcdif_modedb_sz; + + printk("lcd-if: init called with dcu = %d!!\n", plat_data->dcu_id); + printk("The pixel clock in modedb, picosecs = %ld, HZ = %ld\n", KHZ2PICOS(10870), ((PICOS2KHZ(91996)) * 1000UL)); + + /* use platform defined dcu */ + setting->dev_id = plat_data->dcu_id; + + ret = fb_find_mode(&setting->fbi->var, setting->fbi, setting->dft_mode_str, + modedb, modedb_sz, NULL, setting->default_bpp); + if (!ret) { + fb_videomode_to_var(&setting->fbi->var, &modedb[0]); + setting->if_fmt = plat_data->default_ifmt; + } + + INIT_LIST_HEAD(&setting->fbi->modelist); + for (i = 0; i < modedb_sz; i++) { + struct fb_videomode m; + fb_var_to_videomode(&m, &setting->fbi->var); + if (fb_mode_is_equal(&m, &modedb[i])) { + fb_add_videomode(&modedb[i], + &setting->fbi->modelist); + break; + } + } + + return ret; +} + +void lcdif_deinit(struct mvf_dispdrv_handle *disp) +{ + /*TODO*/ +} + +static struct mvf_dispdrv_driver lcdif_drv = { + .name = DISPDRV_LCD, + .init = lcdif_init, + .deinit = lcdif_deinit, +}; + +static int mvf_lcdif_probe(struct platform_device *pdev) +{ + int ret = 0; + struct mvf_lcdif_data *lcdif; + printk("lcdif: probe called!!!\n"); + + lcdif = kzalloc(sizeof(struct mvf_lcdif_data), GFP_KERNEL); + if (!lcdif) { + ret = -ENOMEM; + goto alloc_failed; + } + + lcdif->pdev = pdev; + lcdif->disp_lcdif = mvf_dispdrv_register(&lcdif_drv); + mvf_dispdrv_setdata(lcdif->disp_lcdif, lcdif); + + dev_set_drvdata(&pdev->dev, lcdif); + + printk("lcdif: registeration and setdata complete\n"); +alloc_failed: + return ret; +} + +static int mvf_lcdif_remove(struct platform_device *pdev) +{ + struct mvf_lcdif_data *lcdif = dev_get_drvdata(&pdev->dev); + + mvf_dispdrv_puthandle(lcdif->disp_lcdif); + mvf_dispdrv_unregister(lcdif->disp_lcdif); + kfree(lcdif); + return 0; +} + +static struct platform_driver mvf_lcdif_driver = { + .driver = { + .name = "mvf_lcdif", + }, + .probe = mvf_lcdif_probe, + .remove = mvf_lcdif_remove, +}; + +static int __init mvf_lcdif_init(void) +{ + return platform_driver_register(&mvf_lcdif_driver); +} + +static void __exit mvf_lcdif_exit(void) +{ + platform_driver_unregister(&mvf_lcdif_driver); +} + +module_init(mvf_lcdif_init); +module_exit(mvf_lcdif_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MVF DCUV4 LCD driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index 8ebf770c3af9..7a1a770532d0 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h @@ -303,6 +303,16 @@ struct fsl_mxc_tvin_platform_data { bool cvbs; }; +struct fsl_mvf_lcd_platform_data { + void (*reset) (void); + int (*get_pins) (void); + void (*put_pins) (void); + void (*enable_pins) (void); + void (*disable_pins) (void); + int default_ifmt; + int dcu_id; +}; + struct mpc8xx_pcmcia_ops { void(*hw_ctrl)(int slot, int enable); int(*voltage_set)(int slot, int vcc, int vpp); diff --git a/include/linux/mvffb.h b/include/linux/mvffb.h new file mode 100644 index 000000000000..436ba9a1238b --- /dev/null +++ b/include/linux/mvffb.h @@ -0,0 +1,64 @@ +/* + * Copyright 2004-2012 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU Lesser General + * Public License. You may obtain a copy of the GNU Lesser General + * Public License Version 2.1 or later at the following locations: + * + * http://www.opensource.org/licenses/lgpl-license.html + * http://www.gnu.org/copyleft/lgpl.html + */ + +/* + * @file arch-mvf/ mvffb.h + * + * @brief Global header file for the MVF Frame buffer + * + * @ingroup Framebuffer + */ +#ifndef __ASM_ARCH_MVFFB_H__ +#define __ASM_ARCH_MVFFB_H__ + +#include <linux/fb.h> + +#define FB_SYNC_INV_PIX_CLK 0x80000000 /* If set Display samples data on the rising edge */ +#define FB_SYNC_DATA_INVERT 0x20000000 /* If set Output to display to be negated */ + +enum { + + DISABLE_ALPHA_BLEND = 0, + ENABLE_ALPHA_BLEND = 2, +}; + +struct mvffb_layer_alpha { + int blend_enable; + int alpha; +}; + +struct mvffb_layer_pos { + __u16 x; //position + __u16 y; +}; + +/* Custom IOCTL's to support advanced FB operations */ +#define MVFFB_WAIT_FOR_VSYNC _IOW('F', 0x20, u_int32_t) +#define MVFFB_SET_LAYER_ALPHA _IOW('F', 0x21, struct mvffb_layer_alpha) +#define MVFFB_GET_FB_BLANK _IOR('F', 0x22, u_int32_t) +#define MVFFB_SETUP_LAYER _IOW('F', 0x23, struct mvffb_layer_pos) +//#define MVFFB_SET_CLR_KEY _IOW('F', 0x22, struct mvffb_color_key) +//#define MVFFB_SET_OVERLAY_POS _IOWR('F', 0x24, struct mvffb_pos) +//#define MVFFB_SET_LOC_ALPHA _IOWR('F', 0x26, struct mvffb_loc_alpha) +//#define MVFFB_SET_LOC_ALP_BUF _IOW('F', 0x27, unsigned long) +//#define MVFFB_SET_GAMMA _IOW('F', 0x28, struct mvffb_gamma) + +//#define MVFFB_ENABLE_OVERLAY_DOUBLE_BUFFER _IOW('F',0x2d, u_int32_t) + +#ifdef __KERNEL__ + +extern struct fb_videomode mvffb_modedb[]; +extern int mvffb_modedb_sz; + +#endif /* __KERNEL__ */ +#endif |