diff options
25 files changed, 12052 insertions, 44 deletions
diff --git a/arch/arm/mach-mx5/Kconfig b/arch/arm/mach-mx5/Kconfig index 419e43ef76f..d9d89b92ba1 100644 --- a/arch/arm/mach-mx5/Kconfig +++ b/arch/arm/mach-mx5/Kconfig @@ -32,6 +32,7 @@ config SOC_IMX51 select ARCH_HAS_CPUFREQ select ARCH_MX5 select IMX_HAVE_PLATFORM_IMX_IIM + select IMX_HAVE_PLATFORM_IMX_IPUV3 config SOC_IMX53 bool @@ -43,6 +44,7 @@ config SOC_IMX53 select ARCH_MX5 select ARCH_MX53 select IMX_HAVE_PLATFORM_IMX_IIM + select IMX_HAVE_PLATFORM_IMX_IPUV3 if ARCH_MX50_SUPPORTED #comment "i.MX50 machines:" diff --git a/arch/arm/mach-mx5/clock.c b/arch/arm/mach-mx5/clock.c index fc28dfc7855..5eb4f0bf799 100644 --- a/arch/arm/mach-mx5/clock.c +++ b/arch/arm/mach-mx5/clock.c @@ -4447,9 +4447,9 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK(NULL, "vpu_core_clk", vpu_clk[1]), _REGISTER_CLOCK(NULL, "nfc_clk", emi_enfc_clk), _REGISTER_CLOCK("imx-sdma", NULL, sdma_clk[0]), - _REGISTER_CLOCK(NULL, "ipu_clk", ipu_clk[0]), - _REGISTER_CLOCK(NULL, "ipu_di0_clk", ipu_di_clk[0]), - _REGISTER_CLOCK(NULL, "ipu_di1_clk", ipu_di_clk[1]), + _REGISTER_CLOCK(NULL, "ipu1_clk", ipu_clk[0]), + _REGISTER_CLOCK(NULL, "ipu1_di0_clk", ipu_di_clk[0]), + _REGISTER_CLOCK(NULL, "ipu1_di1_clk", ipu_di_clk[1]), _REGISTER_CLOCK(NULL, "csi_mclk1", csi0_clk), _REGISTER_CLOCK(NULL, "csi_mclk2", csi1_clk), _REGISTER_CLOCK(NULL, "tve_clk", tve_clk), diff --git a/arch/arm/mach-mx5/devices-imx51.h b/arch/arm/mach-mx5/devices-imx51.h index e11bc0e0ec4..5446680cc3c 100644 --- a/arch/arm/mach-mx5/devices-imx51.h +++ b/arch/arm/mach-mx5/devices-imx51.h @@ -52,3 +52,7 @@ extern const struct imx_mxc_pwm_data imx51_mxc_pwm_data[]; extern const struct imx_imx_keypad_data imx51_imx_keypad_data; #define imx51_add_imx_keypad(pdata) \ imx_add_imx_keypad(&imx51_imx_keypad_data, pdata) + +extern const struct imx_ipuv3_data imx51_ipuv3_data __initconst; +#define imx51_add_ipuv3(id, pdata) imx_add_ipuv3(id, &imx51_ipuv3_data, pdata) +#define imx51_add_ipuv3fb(id, pdata) imx_add_ipuv3_fb(id, pdata) diff --git a/arch/arm/mach-mx5/devices-imx53.h b/arch/arm/mach-mx5/devices-imx53.h index c7e42deda99..00f03df6d7c 100644 --- a/arch/arm/mach-mx5/devices-imx53.h +++ b/arch/arm/mach-mx5/devices-imx53.h @@ -52,3 +52,7 @@ extern const struct imx_iim_data imx53_imx_iim_data __initconst; extern const struct imx_ahci_imx_data imx53_ahci_imx_data __initconst; #define imx53_add_ahci_imx(id, pdata) \ imx_add_ahci_imx(&imx53_ahci_imx_data, pdata) + +extern const struct imx_ipuv3_data imx53_ipuv3_data __initconst; +#define imx53_add_ipuv3(id, pdata) imx_add_ipuv3(id, &imx53_ipuv3_data, pdata) +#define imx53_add_ipuv3fb(id, pdata) imx_add_ipuv3_fb(id, pdata) diff --git a/arch/arm/plat-mxc/devices/Kconfig b/arch/arm/plat-mxc/devices/Kconfig index 7974585091a..3e78ca6d6d7 100644 --- a/arch/arm/plat-mxc/devices/Kconfig +++ b/arch/arm/plat-mxc/devices/Kconfig @@ -43,6 +43,9 @@ config IMX_HAVE_PLATFORM_IMX_UART config IMX_HAVE_PLATFORM_IMX_UDC bool +config IMX_HAVE_PLATFORM_IMX_IPUV3 + bool + config IMX_HAVE_PLATFORM_IPU_CORE bool diff --git a/arch/arm/plat-mxc/devices/Makefile b/arch/arm/plat-mxc/devices/Makefile index 7b491d870f3..c0286b910c6 100644 --- a/arch/arm/plat-mxc/devices/Makefile +++ b/arch/arm/plat-mxc/devices/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_SSI) += platform-imx-ssi.o obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_UART) += platform-imx-uart.o obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_UDC) += platform-imx_udc.o obj-$(CONFIG_IMX_HAVE_PLATFORM_IPU_CORE) += platform-ipu-core.o +obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_IPUV3) += platform-imx_ipuv3.o obj-$(CONFIG_IMX_HAVE_PLATFORM_MX1_CAMERA) += platform-mx1-camera.o obj-$(CONFIG_IMX_HAVE_PLATFORM_MX2_CAMERA) += platform-mx2-camera.o obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_EHCI) += platform-mxc-ehci.o diff --git a/arch/arm/plat-mxc/devices/platform-imx_ipuv3.c b/arch/arm/plat-mxc/devices/platform-imx_ipuv3.c new file mode 100644 index 00000000000..a353060bc95 --- /dev/null +++ b/arch/arm/plat-mxc/devices/platform-imx_ipuv3.c @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <mach/hardware.h> +#include <mach/devices-common.h> +#include <linux/dma-mapping.h> +#include <linux/clk.h> + +#define imx5_ipuv3_data_entry_single(soc, size, ipu_init, ipu_pg) \ + { \ + .iobase = soc ## _IPU_CTRL_BASE_ADDR, \ + .irq_err = soc ## _INT_IPU_ERR, \ + .irq = soc ## _INT_IPU_SYN, \ + .irq_start = MXC_IPU_IRQ_START, \ + .iosize = size, \ + .init = ipu_init, \ + .pg = ipu_pg, \ + } + +#define imx6_ipuv3_data_entry_single(soc, id, size, ipu_init, ipu_pg) \ + { \ + .iobase = soc ## _IPU ## id ## _ARB_BASE_ADDR, \ + .irq_err = soc ## _INT_IPU ## id ## _ERR, \ + .irq = soc ## _INT_IPU ## id ## _SYN, \ + .irq_start = MXC_IPU_IRQ_START, \ + .iosize = size, \ + .init = ipu_init, \ + .pg = ipu_pg, \ + } + +#ifdef CONFIG_SOC_IMX51 +/* + * The MIPI HSC unit has been removed from the i.MX51 Reference Manual by + * the Freescale marketing division. However this did not remove the + * hardware from the chip which still needs to be configured... + */ +static int __init ipu_mipi_setup(void) +{ + struct clk *hsc_clk; + void __iomem *hsc_addr; + int ret = 0; + + hsc_addr = ioremap(MX51_MIPI_HSC_BASE_ADDR, PAGE_SIZE); + if (!hsc_addr) + return -ENOMEM; + + hsc_clk = clk_get_sys(NULL, "mipi_hsp"); + if (IS_ERR(hsc_clk)) { + ret = PTR_ERR(hsc_clk); + goto unmap; + } + clk_enable(hsc_clk); + + /* setup MIPI module to legacy mode */ + writel(0xF00, hsc_addr); + + /* CSI mode: reserved; DI control mode: legacy (from Freescale BSP) */ + writel(readl(hsc_addr + 0x800) | 0x30ff, + hsc_addr + 0x800); + + clk_disable(hsc_clk); + clk_put(hsc_clk); +unmap: + iounmap(hsc_addr); + + return ret; +} + +int __init mx51_ipuv3_init(int id) +{ + int ret = 0; + u32 val; + + ret = ipu_mipi_setup(); + + /* hard reset the IPU */ + val = readl(MX51_IO_ADDRESS(MX51_SRC_BASE_ADDR)); + val |= 1 << 3; + writel(val, MX51_IO_ADDRESS(MX51_SRC_BASE_ADDR)); + + return ret; +} + +void mx51_ipuv3_pg(int enable) +{ + if (enable) { + writel(MXC_PGCR_PCR, MX51_PGC_IPU_PGCR); + writel(MXC_PGSR_PSR, MX51_PGC_IPU_PGSR); + } else { + writel(0x0, MX51_PGC_IPU_PGCR); + if (readl(MX51_PGC_IPU_PGSR) & MXC_PGSR_PSR) + printk(KERN_DEBUG "power gating successful\n"); + writel(MXC_PGSR_PSR, MX51_PGC_IPU_PGSR); + } +} + +const struct imx_ipuv3_data imx51_ipuv3_data __initconst = + imx5_ipuv3_data_entry_single(MX51, SZ_512M, + mx51_ipuv3_init, mx51_ipuv3_pg); +#endif + +#ifdef CONFIG_SOC_IMX53 +int __init mx53_ipuv3_init(int id) +{ + int ret = 0; + u32 val; + + /* hard reset the IPU */ + val = readl(MX53_IO_ADDRESS(MX53_SRC_BASE_ADDR)); + val |= 1 << 3; + writel(val, MX53_IO_ADDRESS(MX53_SRC_BASE_ADDR)); + + return ret; +} + +void mx53_ipuv3_pg(int enable) +{ + if (enable) { + writel(MXC_PGCR_PCR, MX53_PGC_IPU_PGCR); + writel(MXC_PGSR_PSR, MX53_PGC_IPU_PGSR); + } else { + writel(0x0, MX53_PGC_IPU_PGCR); + if (readl(MX53_PGC_IPU_PGSR) & MXC_PGSR_PSR) + printk(KERN_DEBUG "power gating successful\n"); + writel(MXC_PGSR_PSR, MX53_PGC_IPU_PGSR); + } +} + +const struct imx_ipuv3_data imx53_ipuv3_data __initconst = + imx5_ipuv3_data_entry_single(MX53, SZ_128M, + mx53_ipuv3_init, mx53_ipuv3_pg); +#endif + +#ifdef CONFIG_SOC_IMX6Q +int __init mx6q_ipuv3_init(int id) +{ + int ret = 0; + u32 val; + + /* hard reset the IPU */ + val = readl(MX6_IO_ADDRESS(SRC_BASE_ADDR)); + if (id == 0) + val |= 1 << 3; + else + val |= 1 << 12; + writel(val, MX6_IO_ADDRESS(SRC_BASE_ADDR)); + + return ret; +} + +void mx6q_ipuv3_pg(int enable) +{ + /*TODO*/ +} + +const struct imx_ipuv3_data imx6q_ipuv3_data[] __initconst = { + imx6_ipuv3_data_entry_single(MX6Q, 1, SZ_4M, + mx6q_ipuv3_init, mx6q_ipuv3_pg), + imx6_ipuv3_data_entry_single(MX6Q, 2, SZ_4M, + mx6q_ipuv3_init, mx6q_ipuv3_pg), +}; +#endif + +struct platform_device *__init imx_add_ipuv3( + const int id, + const struct imx_ipuv3_data *data, + struct imx_ipuv3_platform_data *pdata) +{ + struct resource res[] = { + { + .start = data->iobase, + .end = data->iobase + data->iosize - 1, + .flags = IORESOURCE_MEM, + }, { + .start = data->irq_err, + .end = data->irq_err, + .flags = IORESOURCE_IRQ, + }, { + .start = data->irq, + .end = data->irq, + .flags = IORESOURCE_IRQ, + }, + }; + + pdata->init = data->init; + pdata->pg = data->pg; + + return imx_add_platform_device_dmamask("imx-ipuv3", id, + res, ARRAY_SIZE(res), pdata, sizeof(*pdata), + DMA_BIT_MASK(32)); +} + +struct platform_device *__init imx_add_ipuv3_fb( + const int id, + const struct ipuv3_fb_platform_data *pdata) +{ + if (pdata->res_size > 0) { + struct resource res[] = { + { + .start = pdata->res_base, + .end = pdata->res_base + pdata->res_size - 1, + .flags = IORESOURCE_MEM, + }, + }; + + return imx_add_platform_device_dmamask("mxc_sdc_fb", + id, res, ARRAY_SIZE(res), pdata, + sizeof(*pdata), DMA_BIT_MASK(32)); + } else + return imx_add_platform_device_dmamask("mxc_sdc_fb", id, + NULL, 0, pdata, sizeof(*pdata), + DMA_BIT_MASK(32)); +} diff --git a/arch/arm/plat-mxc/include/mach/devices-common.h b/arch/arm/plat-mxc/include/mach/devices-common.h index 9b1b7df5344..72d71517320 100644 --- a/arch/arm/plat-mxc/include/mach/devices-common.h +++ b/arch/arm/plat-mxc/include/mach/devices-common.h @@ -171,23 +171,24 @@ struct platform_device *__init imx_add_imx_udc( const struct imx_imx_udc_data *data, const struct imxusb_platform_data *pdata); -#include <mach/ipu.h> -#include <mach/mx3fb.h> -#include <mach/mx3_camera.h> -struct imx_ipu_core_data { +#include <mach/ipu-v3.h> +struct imx_ipuv3_data { resource_size_t iobase; - resource_size_t synirq; - resource_size_t errirq; + resource_size_t iosize; + resource_size_t irq_err; + resource_size_t irq; + unsigned int irq_start; + int (*init) (int); + void (*pg) (int); }; -struct platform_device *__init imx_add_ipu_core( - const struct imx_ipu_core_data *data, - const struct ipu_platform_data *pdata); -struct platform_device *__init imx_alloc_mx3_camera( - const struct imx_ipu_core_data *data, - const struct mx3_camera_pdata *pdata); -struct platform_device *__init imx_add_mx3_sdc_fb( - const struct imx_ipu_core_data *data, - struct mx3fb_platform_data *pdata); +struct platform_device *__init imx_add_ipuv3( + const int id, + const struct imx_ipuv3_data *data, + struct imx_ipuv3_platform_data *pdata); + +struct platform_device *__init imx_add_ipuv3_fb( + const int id, + const struct ipuv3_fb_platform_data *pdata); #include <mach/mx1_camera.h> struct imx_mx1_camera_data { diff --git a/arch/arm/plat-mxc/include/mach/ipu-v3.h b/arch/arm/plat-mxc/include/mach/ipu-v3.h new file mode 100644 index 00000000000..713ee9f62d5 --- /dev/null +++ b/arch/arm/plat-mxc/include/mach/ipu-v3.h @@ -0,0 +1,770 @@ +/* + * 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_IPU_V3_H_ +#define __MACH_IPU_V3_H_ + +#include <linux/ipu.h> + +/* IPU Pixel format definitions */ +/* Four-character-code (FOURCC) */ +#define fourcc(a, b, c, d)\ + (((__u32)(a)<<0)|((__u32)(b)<<8)|((__u32)(c)<<16)|((__u32)(d)<<24)) + +/*! + * @name IPU Pixel Formats + * + * Pixel formats are defined with ASCII FOURCC code. The pixel format codes are + * the same used by V4L2 API. + */ + +/*! @{ */ +/*! @name Generic or Raw Data Formats */ +/*! @{ */ +#define IPU_PIX_FMT_GENERIC fourcc('I', 'P', 'U', '0') /*!< IPU Generic Data */ +#define IPU_PIX_FMT_GENERIC_32 fourcc('I', 'P', 'U', '1') /*!< IPU Generic Data */ +#define IPU_PIX_FMT_LVDS666 fourcc('L', 'V', 'D', '6') /*!< IPU Generic Data */ +#define IPU_PIX_FMT_LVDS888 fourcc('L', 'V', 'D', '8') /*!< IPU Generic Data */ +/*! @} */ +/*! @name RGB Formats */ +/*! @{ */ +#define IPU_PIX_FMT_RGB332 fourcc('R', 'G', 'B', '1') /*!< 8 RGB-3-3-2 */ +#define IPU_PIX_FMT_RGB555 fourcc('R', 'G', 'B', 'O') /*!< 16 RGB-5-5-5 */ +#define IPU_PIX_FMT_RGB565 fourcc('R', 'G', 'B', 'P') /*!< 1 6 RGB-5-6-5 */ +#define IPU_PIX_FMT_RGB666 fourcc('R', 'G', 'B', '6') /*!< 18 RGB-6-6-6 */ +#define IPU_PIX_FMT_BGR666 fourcc('B', 'G', 'R', '6') /*!< 18 BGR-6-6-6 */ +#define IPU_PIX_FMT_BGR24 fourcc('B', 'G', 'R', '3') /*!< 24 BGR-8-8-8 */ +#define IPU_PIX_FMT_RGB24 fourcc('R', 'G', 'B', '3') /*!< 24 RGB-8-8-8 */ +#define IPU_PIX_FMT_GBR24 fourcc('G', 'B', 'R', '3') /*!< 24 GBR-8-8-8 */ +#define IPU_PIX_FMT_BGR32 fourcc('B', 'G', 'R', '4') /*!< 32 BGR-8-8-8-8 */ +#define IPU_PIX_FMT_BGRA32 fourcc('B', 'G', 'R', 'A') /*!< 32 BGR-8-8-8-8 */ +#define IPU_PIX_FMT_RGB32 fourcc('R', 'G', 'B', '4') /*!< 32 RGB-8-8-8-8 */ +#define IPU_PIX_FMT_RGBA32 fourcc('R', 'G', 'B', 'A') /*!< 32 RGB-8-8-8-8 */ +#define IPU_PIX_FMT_ABGR32 fourcc('A', 'B', 'G', 'R') /*!< 32 ABGR-8-8-8-8 */ +/*! @} */ +/*! @name YUV Interleaved Formats */ +/*! @{ */ +#define IPU_PIX_FMT_YUYV fourcc('Y', 'U', 'Y', 'V') /*!< 16 YUV 4:2:2 */ +#define IPU_PIX_FMT_UYVY fourcc('U', 'Y', 'V', 'Y') /*!< 16 YUV 4:2:2 */ +#define IPU_PIX_FMT_YVYU fourcc('Y', 'V', 'Y', 'U') /*!< 16 YVYU 4:2:2 */ +#define IPU_PIX_FMT_VYUY fourcc('V', 'Y', 'U', 'Y') /*!< 16 VYYU 4:2:2 */ +#define IPU_PIX_FMT_Y41P fourcc('Y', '4', '1', 'P') /*!< 12 YUV 4:1:1 */ +#define IPU_PIX_FMT_YUV444 fourcc('Y', '4', '4', '4') /*!< 24 YUV 4:4:4 */ +#define IPU_PIX_FMT_VYU444 fourcc('V', '4', '4', '4') /*!< 24 VYU 4:4:4 */ +/* two planes -- one Y, one Cb + Cr interleaved */ +#define IPU_PIX_FMT_NV12 fourcc('N', 'V', '1', '2') /* 12 Y/CbCr 4:2:0 */ +/*! @} */ +/*! @name YUV Planar Formats */ +/*! @{ */ +#define IPU_PIX_FMT_GREY fourcc('G', 'R', 'E', 'Y') /*!< 8 Greyscale */ +#define IPU_PIX_FMT_YVU410P fourcc('Y', 'V', 'U', '9') /*!< 9 YVU 4:1:0 */ +#define IPU_PIX_FMT_YUV410P fourcc('Y', 'U', 'V', '9') /*!< 9 YUV 4:1:0 */ +#define IPU_PIX_FMT_YVU420P fourcc('Y', 'V', '1', '2') /*!< 12 YVU 4:2:0 */ +#define IPU_PIX_FMT_YUV420P fourcc('I', '4', '2', '0') /*!< 12 YUV 4:2:0 */ +#define IPU_PIX_FMT_YUV420P2 fourcc('Y', 'U', '1', '2') /*!< 12 YUV 4:2:0 */ +#define IPU_PIX_FMT_YVU422P fourcc('Y', 'V', '1', '6') /*!< 16 YVU 4:2:2 */ +#define IPU_PIX_FMT_YUV422P fourcc('4', '2', '2', 'P') /*!< 16 YUV 4:2:2 */ +/*! @} */ + +/* IPU Driver channels definitions. */ +/* Note these are different from IDMA channels */ +#define IPU_MAX_CH 32 +#define _MAKE_CHAN(num, v_in, g_in, a_in, out) \ + ((num << 24) | (v_in << 18) | (g_in << 12) | (a_in << 6) | out) +#define _MAKE_ALT_CHAN(ch) (ch | (IPU_MAX_CH << 24)) +#define IPU_CHAN_ID(ch) (ch >> 24) +#define IPU_CHAN_ALT(ch) (ch & 0x02000000) +#define IPU_CHAN_ALPHA_IN_DMA(ch) ((uint32_t) (ch >> 6) & 0x3F) +#define IPU_CHAN_GRAPH_IN_DMA(ch) ((uint32_t) (ch >> 12) & 0x3F) +#define IPU_CHAN_VIDEO_IN_DMA(ch) ((uint32_t) (ch >> 18) & 0x3F) +#define IPU_CHAN_OUT_DMA(ch) ((uint32_t) (ch & 0x3F)) +#define NO_DMA 0x3F +#define ALT 1 +/*! + * Enumeration of IPU logical channels. An IPU logical channel is defined as a + * combination of an input (memory to IPU), output (IPU to memory), and/or + * secondary input IDMA channels and in some cases an Image Converter task. + * Some channels consist of only an input or output. + */ +typedef enum { + CHAN_NONE = -1, + MEM_ROT_ENC_MEM = _MAKE_CHAN(1, 45, NO_DMA, NO_DMA, 48), + MEM_ROT_VF_MEM = _MAKE_CHAN(2, 46, NO_DMA, NO_DMA, 49), + MEM_ROT_PP_MEM = _MAKE_CHAN(3, 47, NO_DMA, NO_DMA, 50), + + MEM_PRP_ENC_MEM = _MAKE_CHAN(4, 12, 14, 17, 20), + MEM_PRP_VF_MEM = _MAKE_CHAN(5, 12, 14, 17, 21), + MEM_PP_MEM = _MAKE_CHAN(6, 11, 15, 18, 22), + + MEM_DC_SYNC = _MAKE_CHAN(7, 28, NO_DMA, NO_DMA, NO_DMA), + MEM_DC_ASYNC = _MAKE_CHAN(8, 41, NO_DMA, NO_DMA, NO_DMA), + MEM_BG_SYNC = _MAKE_CHAN(9, 23, NO_DMA, 51, NO_DMA), + MEM_FG_SYNC = _MAKE_CHAN(10, 27, NO_DMA, 31, NO_DMA), + + MEM_BG_ASYNC0 = _MAKE_CHAN(11, 24, NO_DMA, 52, NO_DMA), + MEM_FG_ASYNC0 = _MAKE_CHAN(12, 29, NO_DMA, 33, NO_DMA), + MEM_BG_ASYNC1 = _MAKE_ALT_CHAN(MEM_BG_ASYNC0), + MEM_FG_ASYNC1 = _MAKE_ALT_CHAN(MEM_FG_ASYNC0), + + DIRECT_ASYNC0 = _MAKE_CHAN(13, NO_DMA, NO_DMA, NO_DMA, NO_DMA), + DIRECT_ASYNC1 = _MAKE_CHAN(14, NO_DMA, NO_DMA, NO_DMA, NO_DMA), + + CSI_MEM0 = _MAKE_CHAN(15, NO_DMA, NO_DMA, NO_DMA, 0), + CSI_MEM1 = _MAKE_CHAN(16, NO_DMA, NO_DMA, NO_DMA, 1), + CSI_MEM2 = _MAKE_CHAN(17, NO_DMA, NO_DMA, NO_DMA, 2), + CSI_MEM3 = _MAKE_CHAN(18, NO_DMA, NO_DMA, NO_DMA, 3), + + CSI_MEM = CSI_MEM0, + + CSI_PRP_ENC_MEM = _MAKE_CHAN(19, NO_DMA, NO_DMA, NO_DMA, 20), + CSI_PRP_VF_MEM = _MAKE_CHAN(20, NO_DMA, NO_DMA, NO_DMA, 21), + + MEM_VDI_PRP_VF_MEM_P = _MAKE_CHAN(21, 8, 14, 17, 21), + MEM_VDI_PRP_VF_MEM = _MAKE_CHAN(22, 9, 14, 17, 21), + MEM_VDI_PRP_VF_MEM_N = _MAKE_CHAN(23, 10, 14, 17, 21), + + MEM_PP_ADC = CHAN_NONE, + ADC_SYS2 = CHAN_NONE, + +} ipu_channel_t; + +/*! + * Enumeration of types of buffers for a logical channel. + */ +typedef enum { + IPU_OUTPUT_BUFFER = 0, /*!< Buffer for output from IPU */ + IPU_ALPHA_IN_BUFFER = 1, /*!< Buffer for input to IPU */ + IPU_GRAPH_IN_BUFFER = 2, /*!< Buffer for input to IPU */ + IPU_VIDEO_IN_BUFFER = 3, /*!< Buffer for input to IPU */ + IPU_INPUT_BUFFER = IPU_VIDEO_IN_BUFFER, + IPU_SEC_INPUT_BUFFER = IPU_GRAPH_IN_BUFFER, +} ipu_buffer_t; + +#define IPU_PANEL_SERIAL 1 +#define IPU_PANEL_PARALLEL 2 + +/*! + * Enumeration of ADC channel operation mode. + */ +typedef enum { + Disable, + WriteTemplateNonSeq, + ReadTemplateNonSeq, + WriteTemplateUnCon, + ReadTemplateUnCon, + WriteDataWithRS, + WriteDataWoRS, + WriteCmd +} mcu_mode_t; + +/*! + * Enumeration of ADC channel addressing mode. + */ +typedef enum { + FullWoBE, + FullWithBE, + XY +} display_addressing_t; + +/*! + * Union of initialization parameters for a logical channel. + */ +typedef union { + struct { + uint32_t csi; + uint32_t mipi_id; + bool mipi_en; + bool interlaced; + } csi_mem; + struct { + uint32_t in_width; + uint32_t in_height; + uint32_t in_pixel_fmt; + uint32_t out_width; + uint32_t out_height; + uint32_t out_pixel_fmt; + uint32_t csi; + } csi_prp_enc_mem; + struct { + uint32_t in_width; + uint32_t in_height; + uint32_t in_pixel_fmt; + uint32_t out_width; + uint32_t out_height; + uint32_t out_pixel_fmt; + uint32_t outh_resize_ratio; + uint32_t outv_resize_ratio; + } mem_prp_enc_mem; + struct { + uint32_t in_width; + uint32_t in_height; + uint32_t in_pixel_fmt; + uint32_t out_width; + uint32_t out_height; + uint32_t out_pixel_fmt; + } mem_rot_enc_mem; + struct { + uint32_t in_width; + uint32_t in_height; + uint32_t in_pixel_fmt; + uint32_t out_width; + uint32_t out_height; + uint32_t out_pixel_fmt; + bool graphics_combine_en; + bool global_alpha_en; + bool key_color_en; + uint32_t csi; + } csi_prp_vf_mem; + struct { + uint32_t in_width; + uint32_t in_height; + uint32_t in_pixel_fmt; + uint32_t out_width; + uint32_t out_height; + uint32_t out_pixel_fmt; + bool graphics_combine_en; + bool global_alpha_en; + bool key_color_en; + display_port_t disp; + uint32_t out_left; + uint32_t out_top; + } csi_prp_vf_adc; + struct { + uint32_t in_width; + uint32_t in_height; + uint32_t in_pixel_fmt; + uint32_t out_width; + uint32_t out_height; + uint32_t out_pixel_fmt; + uint32_t outh_resize_ratio; + uint32_t outv_resize_ratio; + bool graphics_combine_en; + bool global_alpha_en; + bool key_color_en; + uint32_t in_g_pixel_fmt; + uint8_t alpha; + uint32_t key_color; + bool alpha_chan_en; + ipu_motion_sel motion_sel; + enum v4l2_field field_fmt; + } mem_prp_vf_mem; + struct { + uint32_t temp; + } mem_prp_vf_adc; + struct { + uint32_t temp; + } mem_rot_vf_mem; + struct { + uint32_t in_width; + uint32_t in_height; + uint32_t in_pixel_fmt; + uint32_t out_width; + uint32_t out_height; + uint32_t out_pixel_fmt; + uint32_t outh_resize_ratio; + uint32_t outv_resize_ratio; + bool graphics_combine_en; + bool global_alpha_en; + bool key_color_en; + uint32_t in_g_pixel_fmt; + uint8_t alpha; + uint32_t key_color; + bool alpha_chan_en; + } mem_pp_mem; + struct { + uint32_t temp; + } mem_rot_mem; + struct { + uint32_t in_width; + uint32_t in_height; + uint32_t in_pixel_fmt; + uint32_t out_width; + uint32_t out_height; + uint32_t out_pixel_fmt; + bool graphics_combine_en; + bool global_alpha_en; + bool key_color_en; + display_port_t disp; + uint32_t out_left; + uint32_t out_top; + } mem_pp_adc; + struct { + uint32_t di; + bool interlaced; + uint32_t in_pixel_fmt; + uint32_t out_pixel_fmt; + } mem_dc_sync; + struct { + uint32_t temp; + } mem_sdc_fg; + struct { + uint32_t di; + bool interlaced; + uint32_t in_pixel_fmt; + uint32_t out_pixel_fmt; + bool alpha_chan_en; + } mem_dp_bg_sync; + struct { + uint32_t temp; + } mem_sdc_bg; + struct { + uint32_t di; + bool interlaced; + uint32_t in_pixel_fmt; + uint32_t out_pixel_fmt; + bool alpha_chan_en; + } mem_dp_fg_sync; + struct { + uint32_t di; + } direct_async; + struct { + display_port_t disp; + mcu_mode_t ch_mode; + uint32_t out_left; + uint32_t out_top; + } adc_sys1; + struct { + display_port_t disp; + mcu_mode_t ch_mode; + uint32_t out_left; + uint32_t out_top; + } adc_sys2; +} ipu_channel_params_t; + +/*! + * Enumeration of IPU interrupt sources. + */ +enum ipu_irq_line { + IPU_IRQ_CSI0_OUT_EOF = 0, + IPU_IRQ_CSI1_OUT_EOF = 1, + IPU_IRQ_CSI2_OUT_EOF = 2, + IPU_IRQ_CSI3_OUT_EOF = 3, + IPU_IRQ_VDI_P_IN_EOF = 8, + IPU_IRQ_VDI_C_IN_EOF = 9, + IPU_IRQ_VDI_N_IN_EOF = 10, + IPU_IRQ_PP_IN_EOF = 11, + IPU_IRQ_PRP_IN_EOF = 12, + IPU_IRQ_PRP_GRAPH_IN_EOF = 14, + IPU_IRQ_PP_GRAPH_IN_EOF = 15, + IPU_IRQ_PRP_ALPHA_IN_EOF = 17, + IPU_IRQ_PP_ALPHA_IN_EOF = 18, + IPU_IRQ_PRP_ENC_OUT_EOF = 20, + IPU_IRQ_PRP_VF_OUT_EOF = 21, + IPU_IRQ_PP_OUT_EOF = 22, + IPU_IRQ_BG_SYNC_EOF = 23, + IPU_IRQ_BG_ASYNC_EOF = 24, + IPU_IRQ_FG_SYNC_EOF = 27, + IPU_IRQ_DC_SYNC_EOF = 28, + IPU_IRQ_FG_ASYNC_EOF = 29, + IPU_IRQ_FG_ALPHA_SYNC_EOF = 31, + + IPU_IRQ_FG_ALPHA_ASYNC_EOF = 33, + IPU_IRQ_DC_READ_EOF = 40, + IPU_IRQ_DC_ASYNC_EOF = 41, + IPU_IRQ_DC_CMD1_EOF = 42, + IPU_IRQ_DC_CMD2_EOF = 43, + IPU_IRQ_DC_MASK_EOF = 44, + IPU_IRQ_PRP_ENC_ROT_IN_EOF = 45, + IPU_IRQ_PRP_VF_ROT_IN_EOF = 46, + IPU_IRQ_PP_ROT_IN_EOF = 47, + IPU_IRQ_PRP_ENC_ROT_OUT_EOF = 48, + IPU_IRQ_PRP_VF_ROT_OUT_EOF = 49, + IPU_IRQ_PP_ROT_OUT_EOF = 50, + IPU_IRQ_BG_ALPHA_SYNC_EOF = 51, + IPU_IRQ_BG_ALPHA_ASYNC_EOF = 52, + + IPU_IRQ_DP_SF_START = 448 + 2, + IPU_IRQ_DP_SF_END = 448 + 3, + IPU_IRQ_BG_SF_END = IPU_IRQ_DP_SF_END, + IPU_IRQ_DC_FC_0 = 448 + 8, + IPU_IRQ_DC_FC_1 = 448 + 9, + IPU_IRQ_DC_FC_2 = 448 + 10, + IPU_IRQ_DC_FC_3 = 448 + 11, + IPU_IRQ_DC_FC_4 = 448 + 12, + IPU_IRQ_DC_FC_6 = 448 + 13, + IPU_IRQ_VSYNC_PRE_0 = 448 + 14, + IPU_IRQ_VSYNC_PRE_1 = 448 + 15, + + IPU_IRQ_COUNT +}; + +/*! + * Bitfield of Display Interface signal polarities. + */ +typedef struct { + unsigned datamask_en:1; + unsigned int_clk:1; + unsigned interlaced:1; + unsigned odd_field_first:1; + unsigned clksel_en:1; + unsigned clkidle_en:1; + unsigned data_pol:1; /* true = inverted */ + unsigned clk_pol:1; /* true = rising edge */ + unsigned enable_pol:1; + unsigned Hsync_pol:1; /* true = active high */ + unsigned Vsync_pol:1; +} ipu_di_signal_cfg_t; + +/*! + * Bitfield of CSI signal polarities and modes. + */ + +typedef struct { + unsigned data_width:4; + unsigned clk_mode:3; + unsigned ext_vsync:1; + unsigned Vsync_pol:1; + unsigned Hsync_pol:1; + unsigned pixclk_pol:1; + unsigned data_pol:1; + unsigned sens_clksrc:1; + unsigned pack_tight:1; + unsigned force_eof:1; + unsigned data_en_pol:1; + unsigned data_fmt; + unsigned csi; + unsigned mclk; +} ipu_csi_signal_cfg_t; + +/*! + * Enumeration of CSI data bus widths. + */ +enum { + IPU_CSI_DATA_WIDTH_4, + IPU_CSI_DATA_WIDTH_8, + IPU_CSI_DATA_WIDTH_10, + IPU_CSI_DATA_WIDTH_16, +}; + +/*! + * Enumeration of CSI clock modes. + */ +enum { + IPU_CSI_CLK_MODE_GATED_CLK, + IPU_CSI_CLK_MODE_NONGATED_CLK, + IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE, + IPU_CSI_CLK_MODE_CCIR656_INTERLACED, + IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR, + IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR, + IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR, + IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR, +}; + +enum { + IPU_CSI_MIPI_DI0, + IPU_CSI_MIPI_DI1, + IPU_CSI_MIPI_DI2, + IPU_CSI_MIPI_DI3, +}; + +typedef enum { + RGB, + YCbCr, + YUV +} ipu_color_space_t; + +/*! + * Enumeration of ADC vertical sync mode. + */ +typedef enum { + VsyncNone, + VsyncInternal, + VsyncCSI, + VsyncExternal +} vsync_t; + +typedef enum { + DAT, + CMD +} cmddata_t; + +/*! + * Enumeration of ADC display update mode. + */ +typedef enum { + IPU_ADC_REFRESH_NONE, + IPU_ADC_AUTO_REFRESH, + IPU_ADC_AUTO_REFRESH_SNOOP, + IPU_ADC_SNOOPING, +} ipu_adc_update_mode_t; + +/*! + * Enumeration of ADC display interface types (serial or parallel). + */ +enum { + IPU_ADC_IFC_MODE_SYS80_TYPE1, + IPU_ADC_IFC_MODE_SYS80_TYPE2, + IPU_ADC_IFC_MODE_SYS68K_TYPE1, + IPU_ADC_IFC_MODE_SYS68K_TYPE2, + IPU_ADC_IFC_MODE_3WIRE_SERIAL, + IPU_ADC_IFC_MODE_4WIRE_SERIAL, + IPU_ADC_IFC_MODE_5WIRE_SERIAL_CLK, + IPU_ADC_IFC_MODE_5WIRE_SERIAL_CS, +}; + +enum { + IPU_ADC_IFC_WIDTH_8, + IPU_ADC_IFC_WIDTH_16, +}; + +/*! + * Enumeration of ADC display interface burst mode. + */ +enum { + IPU_ADC_BURST_WCS, + IPU_ADC_BURST_WBLCK, + IPU_ADC_BURST_NONE, + IPU_ADC_BURST_SERIAL, +}; + +/*! + * Enumeration of ADC display interface RW signal timing modes. + */ +enum { + IPU_ADC_SER_NO_RW, + IPU_ADC_SER_RW_BEFORE_RS, + IPU_ADC_SER_RW_AFTER_RS, +}; + +/*! + * Bitfield of ADC signal polarities and modes. + */ +typedef struct { + unsigned data_pol:1; + unsigned clk_pol:1; + unsigned cs_pol:1; + unsigned rs_pol:1; + unsigned addr_pol:1; + unsigned read_pol:1; + unsigned write_pol:1; + unsigned Vsync_pol:1; + unsigned burst_pol:1; + unsigned burst_mode:2; + unsigned ifc_mode:3; + unsigned ifc_width:5; + unsigned ser_preamble_len:4; + unsigned ser_preamble:8; + unsigned ser_rw_mode:2; +} ipu_adc_sig_cfg_t; + +/*! + * Enumeration of ADC template commands. + */ +enum { + RD_DATA, + RD_ACK, + RD_WAIT, + WR_XADDR, + WR_YADDR, + WR_ADDR, + WR_CMND, + WR_DATA, +}; + +/*! + * Enumeration of ADC template command flow control. + */ +enum { + SINGLE_STEP, + PAUSE, + STOP, +}; + + +/*Define template constants*/ +#define ATM_ADDR_RANGE 0x20 /*offset address of DISP */ +#define TEMPLATE_BUF_SIZE 0x20 /*size of template */ + +/*! + * Define to create ADC template command entry. + */ +#define ipu_adc_template_gen(oc, rs, fc, dat) (((rs) << 29) | ((fc) << 27) | \ + ((oc) << 24) | (dat)) + +typedef struct { + u32 reg; + u32 value; +} ipu_lpmc_reg_t; + +#define IPU_LPMC_REG_READ 0x80000000L + +#define CSI_MCLK_VF 1 +#define CSI_MCLK_ENC 2 +#define CSI_MCLK_RAW 4 +#define CSI_MCLK_I2C 8 + +struct ipu_soc; +/* Common IPU API */ +struct ipu_soc *ipu_get_soc(int id); +int32_t ipu_init_channel(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel_params_t *params); +void ipu_uninit_channel(struct ipu_soc *ipu, ipu_channel_t channel); + +static inline bool ipu_can_rotate_in_place(ipu_rotate_mode_t rot) +{ +#ifdef CONFIG_MXC_IPU_V3D + return (rot < IPU_ROTATE_HORIZ_FLIP); +#else + return (rot < IPU_ROTATE_90_RIGHT); +#endif +} + +int32_t ipu_init_channel_buffer(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type, + uint32_t pixel_fmt, + uint16_t width, uint16_t height, + uint32_t stride, + ipu_rotate_mode_t rot_mode, + dma_addr_t phyaddr_0, dma_addr_t phyaddr_1, + dma_addr_t phyaddr_2, + uint32_t u_offset, uint32_t v_offset); + +int32_t ipu_update_channel_buffer(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type, + uint32_t bufNum, dma_addr_t phyaddr); + +int32_t ipu_update_channel_offset(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type, + uint32_t pixel_fmt, + uint16_t width, uint16_t height, + uint32_t stride, + uint32_t u, uint32_t v, + uint32_t vertical_offset, uint32_t horizontal_offset); + +int32_t ipu_select_buffer(struct ipu_soc *ipu, ipu_channel_t channel, + ipu_buffer_t type, uint32_t bufNum); +int32_t ipu_select_multi_vdi_buffer(struct ipu_soc *ipu, uint32_t bufNum); + +int32_t ipu_link_channels(struct ipu_soc *ipu, ipu_channel_t src_ch, ipu_channel_t dest_ch); +int32_t ipu_unlink_channels(struct ipu_soc *ipu, ipu_channel_t src_ch, ipu_channel_t dest_ch); + +int32_t ipu_is_channel_busy(struct ipu_soc *ipu, ipu_channel_t channel); +int32_t ipu_check_buffer_ready(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type, + uint32_t bufNum); +void ipu_clear_buffer_ready(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type, + uint32_t bufNum); +uint32_t ipu_get_cur_buffer_idx(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type); +int32_t ipu_enable_channel(struct ipu_soc *ipu, ipu_channel_t channel); +int32_t ipu_disable_channel(struct ipu_soc *ipu, ipu_channel_t channel, bool wait_for_stop); +int32_t ipu_swap_channel(struct ipu_soc *ipu, ipu_channel_t from_ch, ipu_channel_t to_ch); + +int32_t ipu_enable_csi(struct ipu_soc *ipu, uint32_t csi); +int32_t ipu_disable_csi(struct ipu_soc *ipu, uint32_t csi); + +int ipu_lowpwr_display_enable(void); +int ipu_lowpwr_display_disable(void); + +void ipu_enable_irq(struct ipu_soc *ipu, uint32_t irq); +void ipu_disable_irq(struct ipu_soc *ipu, uint32_t irq); +void ipu_clear_irq(struct ipu_soc *ipu, uint32_t irq); +int ipu_request_irq(struct ipu_soc *ipu, uint32_t irq, + irqreturn_t(*handler) (int, void *), + uint32_t irq_flags, const char *devname, void *dev_id); +void ipu_free_irq(struct ipu_soc *ipu, uint32_t irq, void *dev_id); +bool ipu_get_irq_status(struct ipu_soc *ipu, uint32_t irq); +void ipu_set_csc_coefficients(struct ipu_soc *ipu, ipu_channel_t channel, int32_t param[][3]); + +/* two stripe calculations */ +struct stripe_param{ + unsigned int input_width; /* width of the input stripe */ + unsigned int output_width; /* width of the output stripe */ + unsigned int input_column; /* the first column on the input stripe */ + unsigned int output_column; /* the first column on the output stripe */ + unsigned int idr; + /* inverse downisizing ratio parameter; expressed as a power of 2 */ + unsigned int irr; + /* inverse resizing ratio parameter; expressed as a multiple of 2^-13 */ +}; +int ipu_calc_stripes_sizes(const unsigned int input_frame_width, + unsigned int output_frame_width, + const unsigned int maximal_stripe_width, + const unsigned long long cirr, + const unsigned int equal_stripes, + u32 input_pixelformat, + u32 output_pixelformat, + struct stripe_param *left, + struct stripe_param *right); + +/* SDC API */ +int32_t ipu_init_sync_panel(struct ipu_soc *ipu, int disp, + uint32_t pixel_clk, + uint16_t width, uint16_t height, + uint32_t pixel_fmt, + 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, ipu_di_signal_cfg_t sig); + +void ipu_uninit_sync_panel(struct ipu_soc *ipu, int disp); + +int32_t ipu_disp_set_window_pos(struct ipu_soc *ipu, ipu_channel_t channel, int16_t x_pos, + int16_t y_pos); +int32_t ipu_disp_get_window_pos(struct ipu_soc *ipu, ipu_channel_t channel, int16_t *x_pos, + int16_t *y_pos); +int32_t ipu_disp_set_global_alpha(struct ipu_soc *ipu, ipu_channel_t channel, bool enable, + uint8_t alpha); +int32_t ipu_disp_set_color_key(struct ipu_soc *ipu, ipu_channel_t channel, bool enable, + uint32_t colorKey); +int32_t ipu_disp_set_gamma_correction(struct ipu_soc *ipu, ipu_channel_t channel, bool enable, + int constk[], int slopek[]); + +int ipu_init_async_panel(struct ipu_soc *ipu, int disp, int type, uint32_t cycle_time, + uint32_t pixel_fmt, ipu_adc_sig_cfg_t sig); +void ipu_disp_direct_write(struct ipu_soc *ipu, ipu_channel_t channel, u32 value, u32 offset); +void ipu_reset_disp_panel(struct ipu_soc *ipu); + +/* CMOS Sensor Interface API */ +int32_t ipu_csi_init_interface(struct ipu_soc *ipu, uint16_t width, uint16_t height, + uint32_t pixel_fmt, ipu_csi_signal_cfg_t sig); + +int32_t ipu_csi_get_sensor_protocol(struct ipu_soc *ipu, uint32_t csi); + +int32_t ipu_csi_enable_mclk(struct ipu_soc *ipu, int src, bool flag, bool wait); + +static inline int32_t ipu_csi_enable_mclk_if(struct ipu_soc *ipu, int src, uint32_t csi, + bool flag, bool wait) +{ + return ipu_csi_enable_mclk(ipu, csi, flag, wait); +} + +int ipu_csi_read_mclk_flag(void); + +void ipu_csi_flash_strobe(bool flag); + +void ipu_csi_get_window_size(struct ipu_soc *ipu, uint32_t *width, uint32_t *height, uint32_t csi); + +void ipu_csi_set_window_size(struct ipu_soc *ipu, uint32_t width, uint32_t height, uint32_t csi); + +void ipu_csi_set_window_pos(struct ipu_soc *ipu, uint32_t left, uint32_t top, uint32_t csi); + +uint32_t bytes_per_pixel(uint32_t fmt); + +struct ipuv3_fb_platform_data { + char disp_dev[32]; + u32 interface_pix_fmt; + char *mode_str; + int default_bpp; + bool int_clk; + + /* reserved mem */ + resource_size_t res_base; + resource_size_t res_size; +}; + +struct imx_ipuv3_platform_data { + int rev; + int (*init) (int); + void (*pg) (int); + + char *csi_clk[2]; +}; + +#endif /* __MACH_IPU_V3_H_ */ diff --git a/arch/arm/plat-mxc/include/mach/ipu.h b/arch/arm/plat-mxc/include/mach/ipu.h index a9221f1cc1a..f7344ab81e4 100644 --- a/arch/arm/plat-mxc/include/mach/ipu.h +++ b/arch/arm/plat-mxc/include/mach/ipu.h @@ -14,6 +14,7 @@ #include <linux/types.h> #include <linux/dmaengine.h> +#include <linux/ipu.h> /* IPU DMA Controller channel definitions. */ enum ipu_channel { @@ -95,35 +96,10 @@ enum ipu_color_space { IPU_COLORSPACE_YUV }; -/* - * Enumeration of IPU rotation modes - */ -enum ipu_rotate_mode { - /* Note the enum values correspond to BAM value */ - IPU_ROTATE_NONE = 0, - IPU_ROTATE_VERT_FLIP = 1, - IPU_ROTATE_HORIZ_FLIP = 2, - IPU_ROTATE_180 = 3, - IPU_ROTATE_90_RIGHT = 4, - IPU_ROTATE_90_RIGHT_VFLIP = 5, - IPU_ROTATE_90_RIGHT_HFLIP = 6, - IPU_ROTATE_90_LEFT = 7, -}; - struct ipu_platform_data { unsigned int irq_base; }; -/* - * Enumeration of DI ports for ADC. - */ -enum display_port { - DISP0, - DISP1, - DISP2, - DISP3 -}; - struct idmac_video_param { unsigned short in_width; unsigned short in_height; @@ -135,7 +111,7 @@ struct idmac_video_param { bool graphics_combine_en; bool global_alpha_en; bool key_color_en; - enum display_port disp; + display_port_t disp; unsigned short out_left; unsigned short out_top; }; diff --git a/arch/arm/plat-mxc/include/mach/mx53.h b/arch/arm/plat-mxc/include/mach/mx53.h index 2c4344740e1..d815e4e1393 100644 --- a/arch/arm/plat-mxc/include/mach/mx53.h +++ b/arch/arm/plat-mxc/include/mach/mx53.h @@ -157,6 +157,18 @@ #define MX53_IO_P2V(x) IMX_IO_P2V(x) #define MX53_IO_ADDRESS(x) IOMEM(MX53_IO_P2V(x)) +/* PGC */ +#define MX53_GPC_BASE (MX53_IO_ADDRESS(MX53_GPC_BASE_ADDR)) +#define MX53_PGC_IPU_BASE (MX53_GPC_BASE + 0x220) +#define MX53_PGC_VPU_BASE (MX53_GPC_BASE + 0x240) +#define MX53_PGC_GPU_BASE (MX53_GPC_BASE + 0x260) +#define MX53_PGC_IPU_PGCR (MX53_PGC_IPU_BASE + 0x0) +#define MX53_PGC_IPU_PGSR (MX53_PGC_IPU_BASE + 0xC) +#define MX53_PGC_VPU_PGCR (MX53_PGC_VPU_BASE + 0x0) +#define MX53_PGC_VPU_PGSR (MX53_PGC_VPU_BASE + 0xC) +#define MX53_PGC_GPU_PGCR (MX53_PGC_GPU_BASE + 0x0) +#define MX53_PGC_GPU_PGSR (MX53_PGC_GPU_BASE + 0xC) + /* * defines for SPBA modules */ diff --git a/drivers/mxc/Kconfig b/drivers/mxc/Kconfig index 98b65af7c95..2e5b7cfeb1e 100644 --- a/drivers/mxc/Kconfig +++ b/drivers/mxc/Kconfig @@ -4,6 +4,21 @@ if ARCH_MXC menu "MXC support drivers" +config MXC_IPU + bool "Image Processing Unit Driver" + depends on !ARCH_MX21 + depends on !ARCH_MX27 + depends on !ARCH_MX25 + select MXC_IPU_V1 if !ARCH_MX37 && !ARCH_MX5 + select MXC_IPU_V3 if ARCH_MX37 || ARCH_MX5 + select MXC_IPU_V3D if ARCH_MX37 + select MXC_IPU_V3EX if ARCH_MX5 + help + If you plan to use the Image Processing unit, say + Y here. IPU is needed by Framebuffer and V4L2 drivers. + +source "drivers/mxc/ipu3/Kconfig" + endmenu endif diff --git a/drivers/mxc/Makefile b/drivers/mxc/Makefile index 40b08a249a5..1d87f1c2459 100644 --- a/drivers/mxc/Makefile +++ b/drivers/mxc/Makefile @@ -1,2 +1,3 @@ # drivers/mxc/Makefile +obj-$(CONFIG_MXC_IPU_V3) += ipu3/ diff --git a/drivers/mxc/ipu3/Kconfig b/drivers/mxc/ipu3/Kconfig new file mode 100644 index 00000000000..b1461ce4624 --- /dev/null +++ b/drivers/mxc/ipu3/Kconfig @@ -0,0 +1,11 @@ +config MXC_IPU_V3 + bool + +config MXC_IPU_V3D + bool + +config MXC_IPU_V3EX + bool + +config MXC_IPU_V3H + bool diff --git a/drivers/mxc/ipu3/Makefile b/drivers/mxc/ipu3/Makefile new file mode 100644 index 00000000000..8be29736b80 --- /dev/null +++ b/drivers/mxc/ipu3/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_MXC_IPU_V3) = mxc_ipu.o + +mxc_ipu-objs := ipu_common.o ipu_ic.o ipu_disp.o ipu_capture.o ipu_device.o ipu_calc_stripes_sizes.o diff --git a/drivers/mxc/ipu3/ipu_calc_stripes_sizes.c b/drivers/mxc/ipu3/ipu_calc_stripes_sizes.c new file mode 100644 index 00000000000..c4e5b4012c5 --- /dev/null +++ b/drivers/mxc/ipu3/ipu_calc_stripes_sizes.c @@ -0,0 +1,377 @@ +/* + * Copyright 2009-2011 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/* + * @file ipu_calc_stripes_sizes.c + * + * @brief IPU IC functions + * + * @ingroup IPU + */ + +#include <linux/module.h> +#include <linux/ipu.h> +#include <mach/ipu-v3.h> +#include <asm/div64.h> + +#define BPP_32 0 +#define BPP_16 3 +#define BPP_8 5 +#define BPP_24 1 +#define BPP_12 4 +#define BPP_18 2 + +static u64 _do_div(u64 a, u32 b) +{ + u64 div; + div = a; + do_div(div, b); + return div; +} + +static u32 truncate(u32 up, /* 0: down; else: up */ + u64 a, /* must be non-negative */ + u32 b) +{ + u32 d; + u64 div; + div = _do_div(a, b); + d = b * (div >> 32); + if (up && (a > (((u64)d) << 32))) + return d+b; + else + return d; +} + +static unsigned int f_calc(unsigned int pfs, unsigned int bpp, unsigned int *write) +{/* return input_f */ + unsigned int f_calculated = 0; + switch (pfs) { + case IPU_PIX_FMT_YVU422P: + case IPU_PIX_FMT_YUV422P: + case IPU_PIX_FMT_YUV420P2: + case IPU_PIX_FMT_YUV420P: + case IPU_PIX_FMT_YVU420P: + f_calculated = 16; + break; + + case IPU_PIX_FMT_NV12: + f_calculated = 8; + break; + + default: + f_calculated = 0; + break; + + } + if (!f_calculated) { + switch (bpp) { + case BPP_32: + f_calculated = 2; + break; + + case BPP_16: + f_calculated = 4; + break; + + case BPP_8: + case BPP_24: + f_calculated = 8; + break; + + case BPP_12: + f_calculated = 16; + break; + + case BPP_18: + f_calculated = 32; + break; + + default: + f_calculated = 0; + break; + } + } + return f_calculated; +} + + +static unsigned int m_calc(unsigned int pfs) +{ + unsigned int m_calculated = 0; + switch (pfs) { + case IPU_PIX_FMT_YUV420P2: + case IPU_PIX_FMT_YUV420P: + case IPU_PIX_FMT_YVU422P: + case IPU_PIX_FMT_YUV422P: + case IPU_PIX_FMT_YVU420P: + case IPU_PIX_FMT_NV12: + m_calculated = 8; + break; + + case IPU_PIX_FMT_YUYV: + case IPU_PIX_FMT_UYVY: + m_calculated = 2; + break; + + default: + m_calculated = 1; + break; + + } + return m_calculated; +} + + +/* Stripe parameters calculator */ +/************************************************************************** +Notes: +MSW = the maximal width allowed for a stripe + i.MX31: 720, i.MX35: 800, i.MX37/51/53: 1024 +cirr = the maximal inverse resizing ratio for which overlap in the input + is requested; typically cirr~2 +equal_stripes: + 0: each stripe is allowed to have independent parameters + for maximal image quality + 1: the stripes are requested to have identical parameters + (except the base address), for maximal performance +If performance is the top priority (above image quality) + Avoid overlap, by setting CIRR = 0 + This will also force effectively identical_stripes = 1 + Choose IF & OF that corresponds to the same IOX/SX for both stripes + Choose IFW & OFW such that + IFW/IM, IFW/IF, OFW/OM, OFW/OF are even integers + The function returns an error status: + 0: no error + 1: invalid input parameters -> aborted without result + Valid parameters should satisfy the following conditions + IFW <= OFW, otherwise downsizing is required + - which is not supported yet + 4 <= IFW,OFW, so some interpolation may be needed even without overlap + IM, OM, IF, OF should not vanish + 2*IF <= IFW + so the frame can be split to two equal stripes, even without overlap + 2*(OF+IF/irr_opt) <= OFW + so a valid positive INW exists even for equal stripes + OF <= MSW, otherwise, the left stripe cannot be sufficiently large + MSW < OFW, so splitting to stripes is required + OFW <= 2*MSW, so two stripes are sufficient + (this also implies that 2<=MSW) + 2: OF is not a multiple of OM - not fully-supported yet + Output is produced but OW is not guaranited to be a multiple of OM + 4: OFW reduced to be a multiple of OM + 8: CIRR > 1: truncated to 1 + Overlap is not supported (and not needed) y for upsizing) +**************************************************************************/ +int ipu_calc_stripes_sizes(const unsigned int input_frame_width, + /* input frame width;>1 */ + unsigned int output_frame_width, /* output frame width; >1 */ + const unsigned int maximal_stripe_width, + /* the maximal width allowed for a stripe */ + const unsigned long long cirr, /* see above */ + const unsigned int equal_stripes, /* see above */ + u32 input_pixelformat,/* pixel format after of read channel*/ + u32 output_pixelformat,/* pixel format after of write channel*/ + struct stripe_param *left, + struct stripe_param *right) +{ + const unsigned int irr_frac_bits = 13; + const unsigned long irr_steps = 1 << irr_frac_bits; + const u64 dirr = ((u64)1) << (32 - 2); + /* The maximum relative difference allowed between the irrs */ + const u64 cr = ((u64)4) << 32; + /* The importance ratio between the two terms in the cost function below */ + + unsigned int status; + unsigned int temp; + unsigned int onw_min; + unsigned int inw, onw, inw_best = 0; + /* number of pixels in the left stripe NOT hidden by the right stripe */ + u64 irr_opt; /* the optimal inverse resizing ratio */ + u64 rr_opt; /* the optimal resizing ratio = 1/irr_opt*/ + u64 dinw; /* the misalignment between the stripes */ + /* (measured in units of input columns) */ + u64 difwl, difwr; + /* The number of input columns not reflected in the output */ + /* the resizing ratio used for the right stripe is */ + /* left->irr and right->irr respectively */ + u64 cost, cost_min; + u64 div; /* result of division */ + + unsigned int input_m, input_f, output_m, output_f; /* parameters for upsizing by stripes */ + + status = 0; + + /* M, F calculations */ + /* read back pfs from params */ + + input_f = 16; + input_m = 16; + /* BPP should be used in the out_F calc */ + /* Temporarily not used */ + /* out_F = F_calc(idmac->pfs, idmac->bpp, NULL); */ + + output_f = 16; + output_m = 16; + + + if ((input_frame_width < 4) || (output_frame_width < 4)) + return 1; + + irr_opt = _do_div((((u64)(input_frame_width - 1)) << 32), + (output_frame_width - 1)); + rr_opt = _do_div((((u64)(output_frame_width - 1)) << 32), + (input_frame_width - 1)); + + if ((input_m == 0) || (output_m == 0) || (input_f == 0) || (output_f == 0) + || (input_frame_width < (2 * input_f)) + || ((((u64)output_frame_width) << 32) < + (2 * ((((u64)output_f) << 32) + (input_f * rr_opt)))) + || (maximal_stripe_width < output_f) + || (output_frame_width <= maximal_stripe_width) + || ((2 * maximal_stripe_width) < output_frame_width)) + return 1; + + if (output_f % output_m) + status += 2; + + temp = truncate(0, (((u64)output_frame_width) << 32), output_m); + if (temp < output_frame_width) { + output_frame_width = temp; + status += 4; + } + + if (equal_stripes) { + if ((irr_opt > cirr) /* overlap in the input is not requested */ + && ((input_frame_width % (input_m << 1)) == 0) + && ((input_frame_width % (input_f << 1)) == 0) + && ((output_frame_width % (output_m << 1)) == 0) + && ((output_frame_width % (output_f << 1)) == 0)) { + /* without overlap */ + left->input_width = right->input_width = right->input_column = + input_frame_width >> 1; + left->output_width = right->output_width = right->output_column = + output_frame_width >> 1; + left->input_column = 0; + left->output_column = 0; + div = _do_div(((((u64)irr_steps) << 32) * + (right->input_width - 1)), (right->output_width - 1)); + left->irr = right->irr = truncate(0, div, 1); + } else { /* with overlap */ + onw = truncate(0, (((u64)output_frame_width - 1) << 32) >> 1, + output_f); + inw = truncate(0, onw * irr_opt, input_f); + /* this is the maximal inw which allows the same resizing ratio */ + /* in both stripes */ + onw = truncate(1, (inw * rr_opt), output_f); + div = _do_div((((u64)(irr_steps * inw)) << + 32), onw); + left->irr = right->irr = truncate(0, div, 1); + left->output_width = right->output_width = + output_frame_width - onw; + /* These are valid assignments for output_width, */ + /* assuming output_f is a multiple of output_m */ + div = (((u64)(left->output_width-1) * (left->irr)) << 32); + div = (((u64)1) << 32) + _do_div(div, irr_steps); + + left->input_width = right->input_width = truncate(1, div, input_m); + + div = _do_div((((u64)((right->output_width - 1) * right->irr)) << + 32), irr_steps); + difwr = (((u64)(input_frame_width - 1 - inw)) << 32) - div; + div = _do_div((difwr + (((u64)input_f) << 32)), 2); + left->input_column = truncate(0, div, input_f); + + + /* This splits the truncated input columns evenly */ + /* between the left and right margins */ + right->input_column = left->input_column + inw; + left->output_column = 0; + right->output_column = onw; + } + } else { /* independent stripes */ + onw_min = output_frame_width - maximal_stripe_width; + /* onw is a multiple of output_f, in the range */ + /* [max(output_f,output_frame_width-maximal_stripe_width),*/ + /*min(output_frame_width-2,maximal_stripe_width)] */ + /* definitely beyond the cost of any valid setting */ + cost_min = (((u64)input_frame_width) << 32) + cr; + onw = truncate(0, ((u64)maximal_stripe_width), output_f); + if (output_frame_width - onw == 1) + onw -= output_f; /* => onw and output_frame_width-1-onw are positive */ + inw = truncate(0, onw * irr_opt, input_f); + /* this is the maximal inw which allows the same resizing ratio */ + /* in both stripes */ + onw = truncate(1, inw * rr_opt, output_f); + do { + div = _do_div((((u64)(irr_steps * inw)) << 32), onw); + left->irr = truncate(0, div, 1); + div = _do_div((((u64)(onw * left->irr)) << 32), + irr_steps); + dinw = (((u64)inw) << 32) - div; + + div = _do_div((((u64)((output_frame_width - 1 - onw) * left->irr)) << + 32), irr_steps); + + difwl = (((u64)(input_frame_width - 1 - inw)) << 32) - div; + + cost = difwl + (((u64)(cr * dinw)) >> 32); + + if (cost < cost_min) { + inw_best = inw; + cost_min = cost; + } + + inw -= input_f; + onw = truncate(1, inw * rr_opt, output_f); + /* This is the minimal onw which allows the same resizing ratio */ + /* in both stripes */ + } while (onw >= onw_min); + + inw = inw_best; + onw = truncate(1, inw * rr_opt, output_f); + div = _do_div((((u64)(irr_steps * inw)) << 32), onw); + left->irr = truncate(0, div, 1); + + left->output_width = onw; + right->output_width = output_frame_width - onw; + /* These are valid assignments for output_width, */ + /* assuming output_f is a multiple of output_m */ + left->input_width = truncate(1, ((u64)(inw + 1)) << 32, input_m); + right->input_width = truncate(1, ((u64)(input_frame_width - inw)) << + 32, input_m); + + div = _do_div((((u64)(irr_steps * (input_frame_width - 1 - inw))) << + 32), (right->output_width - 1)); + right->irr = truncate(0, div, 1); + temp = truncate(0, ((u64)left->irr) * ((((u64)1) << 32) + dirr), 1); + if (temp < right->irr) + right->irr = temp; + div = _do_div(((u64)((right->output_width - 1) * right->irr) << + 32), irr_steps); + difwr = (u64)(input_frame_width - 1 - inw) - div; + + + div = _do_div((difwr + (((u64)input_f) << 32)), 2); + left->input_column = truncate(0, div, input_f); + + /* This splits the truncated input columns evenly */ + /* between the left and right margins */ + right->input_column = left->input_column + inw; + left->output_column = 0; + right->output_column = onw; + } + + return status; +} +EXPORT_SYMBOL(ipu_calc_stripes_sizes); diff --git a/drivers/mxc/ipu3/ipu_capture.c b/drivers/mxc/ipu3/ipu_capture.c new file mode 100644 index 00000000000..26b8b1573ca --- /dev/null +++ b/drivers/mxc/ipu3/ipu_capture.c @@ -0,0 +1,751 @@ +/* + * Copyright 2008-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 ipu_capture.c + * + * @brief IPU capture dase functions + * + * @ingroup IPU + */ +#include <linux/types.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/errno.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/ipu.h> +#include <linux/clk.h> + +#include "ipu_prv.h" +#include "ipu_regs.h" + +/*! + * ipu_csi_init_interface + * Sets initial values for the CSI registers. + * The width and height of the sensor and the actual frame size will be + * set to the same values. + * @param ipu ipu handler + * @param width Sensor width + * @param height Sensor height + * @param pixel_fmt pixel format + * @param cfg_param ipu_csi_signal_cfg_t structure + * @param csi csi 0 or csi 1 + * + * @return 0 for success, -EINVAL for error + */ +int32_t +ipu_csi_init_interface(struct ipu_soc *ipu, uint16_t width, uint16_t height, + uint32_t pixel_fmt, ipu_csi_signal_cfg_t cfg_param) +{ + uint32_t data = 0; + uint32_t csi = cfg_param.csi; + unsigned long lock_flags; + + /* Set SENS_DATA_FORMAT bits (8, 9 and 10) + RGB or YUV444 is 0 which is current value in data so not set + explicitly + This is also the default value if attempts are made to set it to + something invalid. */ + switch (pixel_fmt) { + case IPU_PIX_FMT_YUYV: + cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV; + break; + case IPU_PIX_FMT_UYVY: + cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY; + break; + case IPU_PIX_FMT_RGB24: + case IPU_PIX_FMT_BGR24: + cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB_YUV444; + break; + case IPU_PIX_FMT_GENERIC: + cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER; + break; + case IPU_PIX_FMT_RGB565: + cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB565; + break; + case IPU_PIX_FMT_RGB555: + cfg_param.data_fmt = CSI_SENS_CONF_DATA_FMT_RGB555; + break; + default: + return -EINVAL; + } + + /* Set the CSI_SENS_CONF register remaining fields */ + data |= cfg_param.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT | + cfg_param.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT | + cfg_param.data_pol << CSI_SENS_CONF_DATA_POL_SHIFT | + cfg_param.Vsync_pol << CSI_SENS_CONF_VSYNC_POL_SHIFT | + cfg_param.Hsync_pol << CSI_SENS_CONF_HSYNC_POL_SHIFT | + cfg_param.pixclk_pol << CSI_SENS_CONF_PIX_CLK_POL_SHIFT | + cfg_param.ext_vsync << CSI_SENS_CONF_EXT_VSYNC_SHIFT | + cfg_param.clk_mode << CSI_SENS_CONF_SENS_PRTCL_SHIFT | + cfg_param.pack_tight << CSI_SENS_CONF_PACK_TIGHT_SHIFT | + cfg_param.force_eof << CSI_SENS_CONF_FORCE_EOF_SHIFT | + cfg_param.data_en_pol << CSI_SENS_CONF_DATA_EN_POL_SHIFT; + + _ipu_get(ipu); + + _ipu_lock(ipu, &lock_flags); + + ipu_csi_write(ipu, csi, data, CSI_SENS_CONF); + + /* Setup sensor frame size */ + ipu_csi_write(ipu, csi, (width - 1) | (height - 1) << 16, CSI_SENS_FRM_SIZE); + + /* Set CCIR registers */ + if (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE) { + ipu_csi_write(ipu, csi, 0x40030, CSI_CCIR_CODE_1); + ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3); + } else if (cfg_param.clk_mode == IPU_CSI_CLK_MODE_CCIR656_INTERLACED) { + if (width == 720 && height == 625) { + /* PAL case */ + /* + * Field0BlankEnd = 0x6, Field0BlankStart = 0x2, + * Field0ActiveEnd = 0x4, Field0ActiveStart = 0 + */ + ipu_csi_write(ipu, csi, 0x40596, CSI_CCIR_CODE_1); + /* + * Field1BlankEnd = 0x7, Field1BlankStart = 0x3, + * Field1ActiveEnd = 0x5, Field1ActiveStart = 0x1 + */ + ipu_csi_write(ipu, csi, 0xD07DF, CSI_CCIR_CODE_2); + ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3); + } else if (width == 720 && height == 525) { + /* NTSC case */ + /* + * Field0BlankEnd = 0x7, Field0BlankStart = 0x3, + * Field0ActiveEnd = 0x5, Field0ActiveStart = 0x1 + */ + ipu_csi_write(ipu, csi, 0xD07DF, CSI_CCIR_CODE_1); + /* + * Field1BlankEnd = 0x6, Field1BlankStart = 0x2, + * Field1ActiveEnd = 0x4, Field1ActiveStart = 0 + */ + ipu_csi_write(ipu, csi, 0x40596, CSI_CCIR_CODE_2); + ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3); + } else { + dev_err(ipu->dev, "Unsupported CCIR656 interlaced " + "video mode\n"); + _ipu_unlock(ipu, &lock_flags); + _ipu_put(ipu); + return -EINVAL; + } + _ipu_csi_ccir_err_detection_enable(ipu, csi); + } else if ((cfg_param.clk_mode == + IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR) || + (cfg_param.clk_mode == + IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR) || + (cfg_param.clk_mode == + IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_DDR) || + (cfg_param.clk_mode == + IPU_CSI_CLK_MODE_CCIR1120_INTERLACED_SDR)) { + ipu_csi_write(ipu, csi, 0x40030, CSI_CCIR_CODE_1); + ipu_csi_write(ipu, csi, 0xFF0000, CSI_CCIR_CODE_3); + _ipu_csi_ccir_err_detection_enable(ipu, csi); + } else if ((cfg_param.clk_mode == IPU_CSI_CLK_MODE_GATED_CLK) || + (cfg_param.clk_mode == IPU_CSI_CLK_MODE_NONGATED_CLK)) { + _ipu_csi_ccir_err_detection_disable(ipu, csi); + } + + dev_dbg(ipu->dev, "CSI_SENS_CONF = 0x%08X\n", + ipu_csi_read(ipu, csi, CSI_SENS_CONF)); + dev_dbg(ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n", + ipu_csi_read(ipu, csi, CSI_ACT_FRM_SIZE)); + + _ipu_unlock(ipu, &lock_flags); + + _ipu_put(ipu); + + return 0; +} +EXPORT_SYMBOL(ipu_csi_init_interface); + +/*! + * ipu_csi_get_sensor_protocol + * + * @param ipu ipu handler + * @param csi csi 0 or csi 1 + * + * @return Returns sensor protocol + */ +int32_t ipu_csi_get_sensor_protocol(struct ipu_soc *ipu, uint32_t csi) +{ + return (ipu_csi_read(ipu, csi, CSI_SENS_CONF) & + CSI_SENS_CONF_SENS_PRTCL_MASK) >> + CSI_SENS_CONF_SENS_PRTCL_SHIFT; +} +EXPORT_SYMBOL(ipu_csi_get_sensor_protocol); + +/*! + * _ipu_csi_mclk_set + * + * @param ipu ipu handler + * @param pixel_clk desired pixel clock frequency in Hz + * @param csi csi 0 or csi 1 + * + * @return Returns 0 on success or negative error code on fail + */ +int _ipu_csi_mclk_set(struct ipu_soc *ipu, uint32_t pixel_clk, uint32_t csi) +{ + uint32_t temp; + uint32_t div_ratio; + + div_ratio = (clk_get_rate(ipu->ipu_clk) / pixel_clk) - 1; + + if (div_ratio > 0xFF || div_ratio < 0) { + dev_dbg(ipu->dev, "The value of pixel_clk extends normal range\n"); + return -EINVAL; + } + + temp = ipu_csi_read(ipu, csi, CSI_SENS_CONF); + temp &= ~CSI_SENS_CONF_DIVRATIO_MASK; + ipu_csi_write(ipu, csi, temp | (div_ratio << CSI_SENS_CONF_DIVRATIO_SHIFT), + CSI_SENS_CONF); + + return 0; +} + +/*! + * ipu_csi_enable_mclk + * + * @param ipu ipu handler + * @param csi csi 0 or csi 1 + * @param flag true to enable mclk, false to disable mclk + * @param wait true to wait 100ms make clock stable, false not wait + * + * @return Returns 0 on success + */ +int ipu_csi_enable_mclk(struct ipu_soc *ipu, int csi, bool flag, bool wait) +{ + if (flag) { + clk_enable(ipu->csi_clk[csi]); + if (wait == true) + msleep(10); + } else { + clk_disable(ipu->csi_clk[csi]); + } + + return 0; +} +EXPORT_SYMBOL(ipu_csi_enable_mclk); + +/*! + * ipu_csi_get_window_size + * + * @param ipu ipu handler + * @param width pointer to window width + * @param height pointer to window height + * @param csi csi 0 or csi 1 + */ +void ipu_csi_get_window_size(struct ipu_soc *ipu, uint32_t *width, uint32_t *height, uint32_t csi) +{ + uint32_t reg; + unsigned long lock_flags; + + _ipu_get(ipu); + + _ipu_lock(ipu, &lock_flags); + + reg = ipu_csi_read(ipu, csi, CSI_ACT_FRM_SIZE); + *width = (reg & 0xFFFF) + 1; + *height = (reg >> 16 & 0xFFFF) + 1; + + _ipu_unlock(ipu, &lock_flags); + + _ipu_put(ipu); +} +EXPORT_SYMBOL(ipu_csi_get_window_size); + +/*! + * ipu_csi_set_window_size + * + * @param ipu ipu handler + * @param width window width + * @param height window height + * @param csi csi 0 or csi 1 + */ +void ipu_csi_set_window_size(struct ipu_soc *ipu, uint32_t width, uint32_t height, uint32_t csi) +{ + unsigned long lock_flags; + + _ipu_get(ipu); + + _ipu_lock(ipu, &lock_flags); + + ipu_csi_write(ipu, csi, (width - 1) | (height - 1) << 16, CSI_ACT_FRM_SIZE); + + _ipu_unlock(ipu, &lock_flags); + + _ipu_put(ipu); +} +EXPORT_SYMBOL(ipu_csi_set_window_size); + +/*! + * ipu_csi_set_window_pos + * + * @param ipu ipu handler + * @param left uint32 window x start + * @param top uint32 window y start + * @param csi csi 0 or csi 1 + */ +void ipu_csi_set_window_pos(struct ipu_soc *ipu, uint32_t left, uint32_t top, uint32_t csi) +{ + uint32_t temp; + unsigned long lock_flags; + + _ipu_get(ipu); + + _ipu_lock(ipu, &lock_flags); + + temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL); + temp &= ~(CSI_HSC_MASK | CSI_VSC_MASK); + temp |= ((top << CSI_VSC_SHIFT) | (left << CSI_HSC_SHIFT)); + ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL); + + _ipu_unlock(ipu, &lock_flags); + + _ipu_put(ipu); +} +EXPORT_SYMBOL(ipu_csi_set_window_pos); + +/*! + * _ipu_csi_horizontal_downsize_enable + * Enable horizontal downsizing(decimation) by 2. + * + * @param ipu ipu handler + * @param csi csi 0 or csi 1 + */ +void _ipu_csi_horizontal_downsize_enable(struct ipu_soc *ipu, uint32_t csi) +{ + uint32_t temp; + + temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL); + temp |= CSI_HORI_DOWNSIZE_EN; + ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL); +} + +/*! + * _ipu_csi_horizontal_downsize_disable + * Disable horizontal downsizing(decimation) by 2. + * + * @param ipu ipu handler + * @param csi csi 0 or csi 1 + */ +void _ipu_csi_horizontal_downsize_disable(struct ipu_soc *ipu, uint32_t csi) +{ + uint32_t temp; + + temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL); + temp &= ~CSI_HORI_DOWNSIZE_EN; + ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL); +} + +/*! + * _ipu_csi_vertical_downsize_enable + * Enable vertical downsizing(decimation) by 2. + * + * @param ipu ipu handler + * @param csi csi 0 or csi 1 + */ +void _ipu_csi_vertical_downsize_enable(struct ipu_soc *ipu, uint32_t csi) +{ + uint32_t temp; + + temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL); + temp |= CSI_VERT_DOWNSIZE_EN; + ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL); +} + +/*! + * _ipu_csi_vertical_downsize_disable + * Disable vertical downsizing(decimation) by 2. + * + * @param ipu ipu handler + * @param csi csi 0 or csi 1 + */ +void _ipu_csi_vertical_downsize_disable(struct ipu_soc *ipu, uint32_t csi) +{ + uint32_t temp; + + temp = ipu_csi_read(ipu, csi, CSI_OUT_FRM_CTRL); + temp &= ~CSI_VERT_DOWNSIZE_EN; + ipu_csi_write(ipu, csi, temp, CSI_OUT_FRM_CTRL); +} + +/*! + * _ipu_csi_set_test_generator + * + * @param ipu ipu handler + * @param active 1 for active and 0 for inactive + * @param r_value red value for the generated pattern of even pixel + * @param g_value green value for the generated pattern of even + * pixel + * @param b_value blue value for the generated pattern of even pixel + * @param pixel_clk desired pixel clock frequency in Hz + * @param csi csi 0 or csi 1 + */ +void _ipu_csi_set_test_generator(struct ipu_soc *ipu, bool active, uint32_t r_value, + uint32_t g_value, uint32_t b_value, uint32_t pix_clk, uint32_t csi) +{ + uint32_t temp; + + temp = ipu_csi_read(ipu, csi, CSI_TST_CTRL); + + if (active == false) { + temp &= ~CSI_TEST_GEN_MODE_EN; + ipu_csi_write(ipu, csi, temp, CSI_TST_CTRL); + } else { + /* Set sensb_mclk div_ratio*/ + _ipu_csi_mclk_set(ipu, pix_clk, csi); + + temp &= ~(CSI_TEST_GEN_R_MASK | CSI_TEST_GEN_G_MASK | + CSI_TEST_GEN_B_MASK); + temp |= CSI_TEST_GEN_MODE_EN; + temp |= (r_value << CSI_TEST_GEN_R_SHIFT) | + (g_value << CSI_TEST_GEN_G_SHIFT) | + (b_value << CSI_TEST_GEN_B_SHIFT); + ipu_csi_write(ipu, csi, temp, CSI_TST_CTRL); + } +} + +/*! + * _ipu_csi_ccir_err_detection_en + * Enable error detection and correction for + * CCIR interlaced mode with protection bit. + * + * @param ipu ipu handler + * @param csi csi 0 or csi 1 + */ +void _ipu_csi_ccir_err_detection_enable(struct ipu_soc *ipu, uint32_t csi) +{ + uint32_t temp; + + temp = ipu_csi_read(ipu, csi, CSI_CCIR_CODE_1); + temp |= CSI_CCIR_ERR_DET_EN; + ipu_csi_write(ipu, csi, temp, CSI_CCIR_CODE_1); + +} + +/*! + * _ipu_csi_ccir_err_detection_disable + * Disable error detection and correction for + * CCIR interlaced mode with protection bit. + * + * @param ipu ipu handler + * @param csi csi 0 or csi 1 + */ +void _ipu_csi_ccir_err_detection_disable(struct ipu_soc *ipu, uint32_t csi) +{ + uint32_t temp; + + temp = ipu_csi_read(ipu, csi, CSI_CCIR_CODE_1); + temp &= ~CSI_CCIR_ERR_DET_EN; + ipu_csi_write(ipu, csi, temp, CSI_CCIR_CODE_1); + +} + +/*! + * _ipu_csi_set_mipi_di + * + * @param ipu ipu handler + * @param num MIPI data identifier 0-3 handled by CSI + * @param di_val data identifier value + * @param csi csi 0 or csi 1 + * + * @return Returns 0 on success or negative error code on fail + */ +int _ipu_csi_set_mipi_di(struct ipu_soc *ipu, uint32_t num, uint32_t di_val, uint32_t csi) +{ + uint32_t temp; + int retval = 0; + + if (di_val > 0xFFL) { + retval = -EINVAL; + goto err; + } + + temp = ipu_csi_read(ipu, csi, CSI_MIPI_DI); + + switch (num) { + case IPU_CSI_MIPI_DI0: + temp &= ~CSI_MIPI_DI0_MASK; + temp |= (di_val << CSI_MIPI_DI0_SHIFT); + ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI); + break; + case IPU_CSI_MIPI_DI1: + temp &= ~CSI_MIPI_DI1_MASK; + temp |= (di_val << CSI_MIPI_DI1_SHIFT); + ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI); + break; + case IPU_CSI_MIPI_DI2: + temp &= ~CSI_MIPI_DI2_MASK; + temp |= (di_val << CSI_MIPI_DI2_SHIFT); + ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI); + break; + case IPU_CSI_MIPI_DI3: + temp &= ~CSI_MIPI_DI3_MASK; + temp |= (di_val << CSI_MIPI_DI3_SHIFT); + ipu_csi_write(ipu, csi, temp, CSI_MIPI_DI); + break; + default: + retval = -EINVAL; + } + +err: + return retval; +} + +/*! + * _ipu_csi_set_skip_isp + * + * @param ipu ipu handler + * @param skip select frames to be skipped and set the + * correspond bits to 1 + * @param max_ratio number of frames in a skipping set and the + * maximum value of max_ratio is 5 + * @param csi csi 0 or csi 1 + * + * @return Returns 0 on success or negative error code on fail + */ +int _ipu_csi_set_skip_isp(struct ipu_soc *ipu, uint32_t skip, uint32_t max_ratio, uint32_t csi) +{ + uint32_t temp; + int retval = 0; + + if (max_ratio > 5) { + retval = -EINVAL; + goto err; + } + + temp = ipu_csi_read(ipu, csi, CSI_SKIP); + temp &= ~(CSI_MAX_RATIO_SKIP_ISP_MASK | CSI_SKIP_ISP_MASK); + temp |= (max_ratio << CSI_MAX_RATIO_SKIP_ISP_SHIFT) | + (skip << CSI_SKIP_ISP_SHIFT); + ipu_csi_write(ipu, csi, temp, CSI_SKIP); + +err: + return retval; +} + +/*! + * _ipu_csi_set_skip_smfc + * + * @param ipu ipu handler + * @param skip select frames to be skipped and set the + * correspond bits to 1 + * @param max_ratio number of frames in a skipping set and the + * maximum value of max_ratio is 5 + * @param id csi to smfc skipping id + * @param csi csi 0 or csi 1 + * + * @return Returns 0 on success or negative error code on fail + */ +int _ipu_csi_set_skip_smfc(struct ipu_soc *ipu, uint32_t skip, + uint32_t max_ratio, uint32_t id, uint32_t csi) +{ + uint32_t temp; + int retval = 0; + + if (max_ratio > 5 || id > 3) { + retval = -EINVAL; + goto err; + } + + temp = ipu_csi_read(ipu, csi, CSI_SKIP); + temp &= ~(CSI_MAX_RATIO_SKIP_SMFC_MASK | CSI_ID_2_SKIP_MASK | + CSI_SKIP_SMFC_MASK); + temp |= (max_ratio << CSI_MAX_RATIO_SKIP_SMFC_SHIFT) | + (id << CSI_ID_2_SKIP_SHIFT) | + (skip << CSI_SKIP_SMFC_SHIFT); + ipu_csi_write(ipu, csi, temp, CSI_SKIP); + +err: + return retval; +} + +/*! + * _ipu_smfc_init + * Map CSI frames to IDMAC channels. + * + * @param ipu ipu handler + * @param channel IDMAC channel 0-3 + * @param mipi_id mipi id number 0-3 + * @param csi csi0 or csi1 + */ +void _ipu_smfc_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t mipi_id, uint32_t csi) +{ + uint32_t temp; + + temp = ipu_smfc_read(ipu, SMFC_MAP); + + switch (channel) { + case CSI_MEM0: + temp &= ~SMFC_MAP_CH0_MASK; + temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH0_SHIFT; + break; + case CSI_MEM1: + temp &= ~SMFC_MAP_CH1_MASK; + temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH1_SHIFT; + break; + case CSI_MEM2: + temp &= ~SMFC_MAP_CH2_MASK; + temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH2_SHIFT; + break; + case CSI_MEM3: + temp &= ~SMFC_MAP_CH3_MASK; + temp |= ((csi << 2) | mipi_id) << SMFC_MAP_CH3_SHIFT; + break; + default: + return; + } + + ipu_smfc_write(ipu, temp, SMFC_MAP); +} + +/*! + * _ipu_smfc_set_wmc + * Caution: The number of required channels, the enabled channels + * and the FIFO size per channel are configured restrictedly. + * + * @param ipu ipu handler + * @param channel IDMAC channel 0-3 + * @param set set 1 or clear 0 + * @param level water mark level when FIFO is on the + * relative size + */ +void _ipu_smfc_set_wmc(struct ipu_soc *ipu, ipu_channel_t channel, bool set, uint32_t level) +{ + uint32_t temp; + + temp = ipu_smfc_read(ipu, SMFC_WMC); + + switch (channel) { + case CSI_MEM0: + if (set == true) { + temp &= ~SMFC_WM0_SET_MASK; + temp |= level << SMFC_WM0_SET_SHIFT; + } else { + temp &= ~SMFC_WM0_CLR_MASK; + temp |= level << SMFC_WM0_CLR_SHIFT; + } + break; + case CSI_MEM1: + if (set == true) { + temp &= ~SMFC_WM1_SET_MASK; + temp |= level << SMFC_WM1_SET_SHIFT; + } else { + temp &= ~SMFC_WM1_CLR_MASK; + temp |= level << SMFC_WM1_CLR_SHIFT; + } + break; + case CSI_MEM2: + if (set == true) { + temp &= ~SMFC_WM2_SET_MASK; + temp |= level << SMFC_WM2_SET_SHIFT; + } else { + temp &= ~SMFC_WM2_CLR_MASK; + temp |= level << SMFC_WM2_CLR_SHIFT; + } + break; + case CSI_MEM3: + if (set == true) { + temp &= ~SMFC_WM3_SET_MASK; + temp |= level << SMFC_WM3_SET_SHIFT; + } else { + temp &= ~SMFC_WM3_CLR_MASK; + temp |= level << SMFC_WM3_CLR_SHIFT; + } + break; + default: + return; + } + + ipu_smfc_write(ipu, temp, SMFC_WMC); +} + +/*! + * _ipu_smfc_set_burst_size + * + * @param ipu ipu handler + * @param channel IDMAC channel 0-3 + * @param bs burst size of IDMAC channel, + * the value programmed here shoud be BURST_SIZE-1 + */ +void _ipu_smfc_set_burst_size(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t bs) +{ + uint32_t temp; + + temp = ipu_smfc_read(ipu, SMFC_BS); + + switch (channel) { + case CSI_MEM0: + temp &= ~SMFC_BS0_MASK; + temp |= bs << SMFC_BS0_SHIFT; + break; + case CSI_MEM1: + temp &= ~SMFC_BS1_MASK; + temp |= bs << SMFC_BS1_SHIFT; + break; + case CSI_MEM2: + temp &= ~SMFC_BS2_MASK; + temp |= bs << SMFC_BS2_SHIFT; + break; + case CSI_MEM3: + temp &= ~SMFC_BS3_MASK; + temp |= bs << SMFC_BS3_SHIFT; + break; + default: + return; + } + + ipu_smfc_write(ipu, temp, SMFC_BS); +} + +/*! + * _ipu_csi_init + * + * @param ipu ipu handler + * @param channel IDMAC channel + * @param csi csi 0 or csi 1 + * + * @return Returns 0 on success or negative error code on fail + */ +int _ipu_csi_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t csi) +{ + uint32_t csi_sens_conf, csi_dest; + int retval = 0; + + switch (channel) { + case CSI_MEM0: + case CSI_MEM1: + case CSI_MEM2: + case CSI_MEM3: + csi_dest = CSI_DATA_DEST_IDMAC; + break; + case CSI_PRP_ENC_MEM: + case CSI_PRP_VF_MEM: + csi_dest = CSI_DATA_DEST_IC; + break; + default: + retval = -EINVAL; + goto err; + } + + csi_sens_conf = ipu_csi_read(ipu, csi, CSI_SENS_CONF); + csi_sens_conf &= ~CSI_SENS_CONF_DATA_DEST_MASK; + ipu_csi_write(ipu, csi, csi_sens_conf | (csi_dest << + CSI_SENS_CONF_DATA_DEST_SHIFT), CSI_SENS_CONF); +err: + return retval; +} diff --git a/drivers/mxc/ipu3/ipu_common.c b/drivers/mxc/ipu3/ipu_common.c new file mode 100644 index 00000000000..fb06f56b1a9 --- /dev/null +++ b/drivers/mxc/ipu3/ipu_common.c @@ -0,0 +1,2760 @@ +/* + * 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 + */ + +/*! + * @file ipu_common.c + * + * @brief This file contains the IPU driver common API functions. + * + * @ingroup IPU + */ +#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/ipu.h> +#include <linux/clk.h> +#include <mach/clock.h> +#include <mach/hardware.h> +#include <mach/devices-common.h> +#include <asm/cacheflush.h> +#include <linux/delay.h> + +#include "ipu_prv.h" +#include "ipu_regs.h" +#include "ipu_param_mem.h" + +static struct ipu_soc ipu_array[MXC_IPU_MAX_NUM]; +int g_ipu_hw_rev; + +/* Static functions */ +static irqreturn_t ipu_irq_handler(int irq, void *desc); + +static inline uint32_t channel_2_dma(ipu_channel_t ch, ipu_buffer_t type) +{ + return ((uint32_t) ch >> (6 * type)) & 0x3F; +}; + +static inline int _ipu_is_ic_chan(uint32_t dma_chan) +{ + return ((dma_chan >= 11) && (dma_chan <= 22) && (dma_chan != 17) && (dma_chan != 18)); +} + +static inline int _ipu_is_ic_graphic_chan(uint32_t dma_chan) +{ + return (dma_chan == 14 || dma_chan == 15); +} + +/* Either DP BG or DP FG can be graphic window */ +static inline int _ipu_is_dp_graphic_chan(uint32_t dma_chan) +{ + return (dma_chan == 23 || dma_chan == 27); +} + +static inline int _ipu_is_irt_chan(uint32_t dma_chan) +{ + return ((dma_chan >= 45) && (dma_chan <= 50)); +} + +static inline int _ipu_is_dmfc_chan(uint32_t dma_chan) +{ + return ((dma_chan >= 23) && (dma_chan <= 29)); +} + +static inline int _ipu_is_smfc_chan(uint32_t dma_chan) +{ + return ((dma_chan >= 0) && (dma_chan <= 3)); +} + +static inline int _ipu_is_trb_chan(uint32_t dma_chan) +{ + return (((dma_chan == 8) || (dma_chan == 9) || + (dma_chan == 10) || (dma_chan == 13) || + (dma_chan == 21) || (dma_chan == 23) || + (dma_chan == 27) || (dma_chan == 28)) && + (g_ipu_hw_rev >= 2)); +} + +#define idma_is_valid(ch) (ch != NO_DMA) +#define idma_mask(ch) (idma_is_valid(ch) ? (1UL << (ch & 0x1F)) : 0) +#define idma_is_set(ipu, reg, dma) (ipu_idmac_read(ipu, reg(dma)) & idma_mask(dma)) +#define tri_cur_buf_mask(ch) (idma_mask(ch*2) * 3) +#define tri_cur_buf_shift(ch) (ffs(idma_mask(ch*2)) - 1) + +static int ipu_reset(struct ipu_soc *ipu) +{ + int timeout = 1000; + + ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST); + + while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) { + if (!timeout--) + return -ETIME; + msleep(1); + } + + return 0; +} + +static int __devinit ipu_clk_setup_enable(struct ipu_soc *ipu, + struct platform_device *pdev) +{ + struct imx_ipuv3_platform_data *plat_data = pdev->dev.platform_data; + char ipu_clk[] = "ipu1_clk"; + char di0_clk[] = "ipu1_di0_clk"; + char di1_clk[] = "ipu1_di1_clk"; + + ipu_clk[3] += pdev->id; + di0_clk[3] += pdev->id; + di1_clk[3] += pdev->id; + + ipu->ipu_clk = clk_get(ipu->dev, ipu_clk); + if (IS_ERR(ipu->ipu_clk)) { + dev_err(ipu->dev, "clk_get failed"); + return PTR_ERR(ipu->ipu_clk); + } + dev_dbg(ipu->dev, "ipu_clk = %lu\n", clk_get_rate(ipu->ipu_clk)); + + ipu->pixel_clk[0] = ipu_pixel_clk[0]; + ipu->pixel_clk[1] = ipu_pixel_clk[1]; + + ipu_lookups[0].clk = &ipu->pixel_clk[0]; + ipu_lookups[1].clk = &ipu->pixel_clk[1]; + clkdev_add(&ipu_lookups[0]); + clkdev_add(&ipu_lookups[1]); + + clk_debug_register(&ipu->pixel_clk[0]); + clk_debug_register(&ipu->pixel_clk[1]); + + clk_enable(ipu->ipu_clk); + + clk_set_parent(&ipu->pixel_clk[0], ipu->ipu_clk); + clk_set_parent(&ipu->pixel_clk[1], ipu->ipu_clk); + + ipu->di_clk[0] = clk_get(ipu->dev, di0_clk); + ipu->di_clk[1] = clk_get(ipu->dev, di1_clk); + + ipu->csi_clk[0] = clk_get(ipu->dev, plat_data->csi_clk[0]); + ipu->csi_clk[1] = clk_get(ipu->dev, plat_data->csi_clk[1]); + + return 0; +} + +struct ipu_soc *ipu_get_soc(int id) +{ + if (id >= MXC_IPU_MAX_NUM) + return ERR_PTR(-ENODEV); + else + return &(ipu_array[id]); +} +EXPORT_SYMBOL_GPL(ipu_get_soc); + +void _ipu_lock(struct ipu_soc *ipu, unsigned long *flags) +{ + spin_lock_irqsave(&ipu->spin_lock, *flags); +} + +void _ipu_unlock(struct ipu_soc *ipu, unsigned long *flags) +{ + spin_unlock_irqrestore(&ipu->spin_lock, *flags); +} + +void _ipu_get(struct ipu_soc *ipu) +{ + if (ipu->ipu_use_count++ == 0) + clk_enable(ipu->ipu_clk); +} + +void _ipu_put(struct ipu_soc *ipu) +{ + if (--ipu->ipu_use_count == 0) + clk_disable(ipu->ipu_clk); +} + +/*! + * This function is called by the driver framework to initialize the IPU + * hardware. + * + * @param dev The device structure for the IPU passed in by the + * driver framework. + * + * @return Returns 0 on success or negative error code on error + */ +static int __devinit ipu_probe(struct platform_device *pdev) +{ + struct imx_ipuv3_platform_data *plat_data = pdev->dev.platform_data; + struct ipu_soc *ipu; + struct resource *res; + unsigned long ipu_base; + int ret = 0; + + if (pdev->id >= MXC_IPU_MAX_NUM) + return -ENODEV; + + ipu = &ipu_array[pdev->id]; + memset(ipu, 0, sizeof(struct ipu_soc)); + + spin_lock_init(&ipu->spin_lock); + mutex_init(&ipu->mutex_lock); + + g_ipu_hw_rev = plat_data->rev; + + ipu->dev = &pdev->dev; + + if (plat_data->init) + plat_data->init(pdev->id); + + ipu->irq_sync = platform_get_irq(pdev, 0); + ipu->irq_err = platform_get_irq(pdev, 1); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (!res || ipu->irq_sync < 0 || ipu->irq_err < 0) { + ret = -ENODEV; + goto failed_get_res; + } + + if (request_irq(ipu->irq_sync, ipu_irq_handler, 0, pdev->name, ipu) != 0) { + dev_err(ipu->dev, "request SYNC interrupt failed\n"); + ret = -EBUSY; + goto failed_req_irq_sync; + } + /* Some platforms have 2 IPU interrupts */ + if (ipu->irq_err >= 0) { + if (request_irq + (ipu->irq_err, ipu_irq_handler, 0, pdev->name, ipu) != 0) { + dev_err(ipu->dev, "request ERR interrupt failed\n"); + ret = -EBUSY; + goto failed_req_irq_err; + } + } + + ipu_base = res->start; + /* base fixup */ + if (g_ipu_hw_rev == 4) /* IPUv3H */ + ipu_base += IPUV3H_REG_BASE; + else if (g_ipu_hw_rev == 3) /* IPUv3M */ + ipu_base += IPUV3M_REG_BASE; + else /* IPUv3D, v3E, v3EX */ + ipu_base += IPUV3DEX_REG_BASE; + + ipu->cm_reg = ioremap(ipu_base + IPU_CM_REG_BASE, PAGE_SIZE); + ipu->ic_reg = ioremap(ipu_base + IPU_IC_REG_BASE, PAGE_SIZE); + ipu->idmac_reg = ioremap(ipu_base + IPU_IDMAC_REG_BASE, PAGE_SIZE); + /* DP Registers are accessed thru the SRM */ + ipu->dp_reg = ioremap(ipu_base + IPU_SRM_REG_BASE, PAGE_SIZE); + ipu->dc_reg = ioremap(ipu_base + IPU_DC_REG_BASE, PAGE_SIZE); + ipu->dmfc_reg = ioremap(ipu_base + IPU_DMFC_REG_BASE, PAGE_SIZE); + ipu->di_reg[0] = ioremap(ipu_base + IPU_DI0_REG_BASE, PAGE_SIZE); + ipu->di_reg[1] = ioremap(ipu_base + IPU_DI1_REG_BASE, PAGE_SIZE); + ipu->smfc_reg = ioremap(ipu_base + IPU_SMFC_REG_BASE, PAGE_SIZE); + ipu->csi_reg[0] = ioremap(ipu_base + IPU_CSI0_REG_BASE, PAGE_SIZE); + ipu->csi_reg[1] = ioremap(ipu_base + IPU_CSI1_REG_BASE, PAGE_SIZE); + ipu->cpmem_base = ioremap(ipu_base + IPU_CPMEM_REG_BASE, SZ_128K); + ipu->tpmem_base = ioremap(ipu_base + IPU_TPM_REG_BASE, SZ_64K); + ipu->dc_tmpl_reg = ioremap(ipu_base + IPU_DC_TMPL_REG_BASE, SZ_128K); + ipu->vdi_reg = ioremap(ipu_base + IPU_VDI_REG_BASE, PAGE_SIZE); + ipu->disp_base[1] = ioremap(ipu_base + IPU_DISP1_BASE, SZ_4K); + + if (!ipu->cm_reg || !ipu->ic_reg || !ipu->idmac_reg || + !ipu->dp_reg || !ipu->dc_reg || !ipu->dmfc_reg || + !ipu->di_reg[0] || !ipu->di_reg[1] || !ipu->smfc_reg || + !ipu->csi_reg[0] || !ipu->csi_reg[1] || !ipu->cpmem_base || + !ipu->tpmem_base || !ipu->dc_tmpl_reg || !ipu->disp_base[1] + || !ipu->vdi_reg) { + ret = -ENOMEM; + goto failed_ioremap; + } + + dev_dbg(ipu->dev, "IPU CM Regs = %p\n", ipu->cm_reg); + dev_dbg(ipu->dev, "IPU IC Regs = %p\n", ipu->ic_reg); + dev_dbg(ipu->dev, "IPU IDMAC Regs = %p\n", ipu->idmac_reg); + dev_dbg(ipu->dev, "IPU DP Regs = %p\n", ipu->dp_reg); + dev_dbg(ipu->dev, "IPU DC Regs = %p\n", ipu->dc_reg); + dev_dbg(ipu->dev, "IPU DMFC Regs = %p\n", ipu->dmfc_reg); + dev_dbg(ipu->dev, "IPU DI0 Regs = %p\n", ipu->di_reg[0]); + dev_dbg(ipu->dev, "IPU DI1 Regs = %p\n", ipu->di_reg[1]); + dev_dbg(ipu->dev, "IPU SMFC Regs = %p\n", ipu->smfc_reg); + dev_dbg(ipu->dev, "IPU CSI0 Regs = %p\n", ipu->csi_reg[0]); + dev_dbg(ipu->dev, "IPU CSI1 Regs = %p\n", ipu->csi_reg[1]); + dev_dbg(ipu->dev, "IPU CPMem = %p\n", ipu->cpmem_base); + dev_dbg(ipu->dev, "IPU TPMem = %p\n", ipu->tpmem_base); + dev_dbg(ipu->dev, "IPU DC Template Mem = %p\n", ipu->dc_tmpl_reg); + dev_dbg(ipu->dev, "IPU Display Region 1 Mem = %p\n", ipu->disp_base[1]); + dev_dbg(ipu->dev, "IPU VDI Regs = %p\n", ipu->vdi_reg); + + ret = ipu_clk_setup_enable(ipu, pdev); + if (ret < 0) { + dev_err(ipu->dev, "ipu clk setup failed\n"); + goto failed_clk_setup; + } + + platform_set_drvdata(pdev, ipu); + + ipu_reset(ipu); + + ipu_disp_init(ipu); + + /* Set sync refresh channels and CSI->mem channel as high priority */ + ipu_idmac_write(ipu, 0x18800001L, IDMAC_CHA_PRI(0)); + + /* Set MCU_T to divide MCU access window into 2 */ + ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18), IPU_DISP_GEN); + + clk_disable(ipu->ipu_clk); + + register_ipu_device(ipu, pdev->id); + + return ret; + +failed_clk_setup: + iounmap(ipu->cm_reg); + iounmap(ipu->ic_reg); + iounmap(ipu->idmac_reg); + iounmap(ipu->dc_reg); + iounmap(ipu->dp_reg); + iounmap(ipu->dmfc_reg); + iounmap(ipu->di_reg[0]); + iounmap(ipu->di_reg[1]); + iounmap(ipu->smfc_reg); + iounmap(ipu->csi_reg[0]); + iounmap(ipu->csi_reg[1]); + iounmap(ipu->cpmem_base); + iounmap(ipu->tpmem_base); + iounmap(ipu->dc_tmpl_reg); + iounmap(ipu->disp_base[1]); + iounmap(ipu->vdi_reg); +failed_ioremap: + if (ipu->irq_sync) + free_irq(ipu->irq_err, ipu); +failed_req_irq_err: + free_irq(ipu->irq_sync, ipu); +failed_req_irq_sync: +failed_get_res: + return ret; +} + +int __devexit ipu_remove(struct platform_device *pdev) +{ + struct ipu_soc *ipu = platform_get_drvdata(pdev); + + unregister_ipu_device(ipu, pdev->id); + + if (ipu->irq_sync) + free_irq(ipu->irq_sync, ipu); + if (ipu->irq_err) + free_irq(ipu->irq_err, ipu); + + clk_put(ipu->ipu_clk); + + iounmap(ipu->cm_reg); + iounmap(ipu->ic_reg); + iounmap(ipu->idmac_reg); + iounmap(ipu->dc_reg); + iounmap(ipu->dp_reg); + iounmap(ipu->dmfc_reg); + iounmap(ipu->di_reg[0]); + iounmap(ipu->di_reg[1]); + iounmap(ipu->smfc_reg); + iounmap(ipu->csi_reg[0]); + iounmap(ipu->csi_reg[1]); + iounmap(ipu->cpmem_base); + iounmap(ipu->tpmem_base); + iounmap(ipu->dc_tmpl_reg); + iounmap(ipu->disp_base[1]); + iounmap(ipu->vdi_reg); + + return 0; +} + +void ipu_dump_registers(struct ipu_soc *ipu) +{ + dev_dbg(ipu->dev, "IPU_CONF = \t0x%08X\n", ipu_cm_read(ipu, IPU_CONF)); + dev_dbg(ipu->dev, "IDMAC_CONF = \t0x%08X\n", ipu_idmac_read(ipu, IDMAC_CONF)); + dev_dbg(ipu->dev, "IDMAC_CHA_EN1 = \t0x%08X\n", + ipu_idmac_read(ipu, IDMAC_CHA_EN(0))); + dev_dbg(ipu->dev, "IDMAC_CHA_EN2 = \t0x%08X\n", + ipu_idmac_read(ipu, IDMAC_CHA_EN(32))); + dev_dbg(ipu->dev, "IDMAC_CHA_PRI1 = \t0x%08X\n", + ipu_idmac_read(ipu, IDMAC_CHA_PRI(0))); + dev_dbg(ipu->dev, "IDMAC_CHA_PRI2 = \t0x%08X\n", + ipu_idmac_read(ipu, IDMAC_CHA_PRI(32))); + dev_dbg(ipu->dev, "IDMAC_BAND_EN1 = \t0x%08X\n", + ipu_idmac_read(ipu, IDMAC_BAND_EN(0))); + dev_dbg(ipu->dev, "IDMAC_BAND_EN2 = \t0x%08X\n", + ipu_idmac_read(ipu, IDMAC_BAND_EN(32))); + dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n", + ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0))); + dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n", + ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32))); + if (g_ipu_hw_rev >= 2) { + dev_dbg(ipu->dev, "IPU_CHA_TRB_MODE_SEL0 = \t0x%08X\n", + ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(0))); + dev_dbg(ipu->dev, "IPU_CHA_TRB_MODE_SEL1 = \t0x%08X\n", + ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(32))); + } + dev_dbg(ipu->dev, "DMFC_WR_CHAN = \t0x%08X\n", + ipu_dmfc_read(ipu, DMFC_WR_CHAN)); + dev_dbg(ipu->dev, "DMFC_WR_CHAN_DEF = \t0x%08X\n", + ipu_dmfc_read(ipu, DMFC_WR_CHAN_DEF)); + dev_dbg(ipu->dev, "DMFC_DP_CHAN = \t0x%08X\n", + ipu_dmfc_read(ipu, DMFC_DP_CHAN)); + dev_dbg(ipu->dev, "DMFC_DP_CHAN_DEF = \t0x%08X\n", + ipu_dmfc_read(ipu, DMFC_DP_CHAN_DEF)); + dev_dbg(ipu->dev, "DMFC_IC_CTRL = \t0x%08X\n", + ipu_dmfc_read(ipu, DMFC_IC_CTRL)); + dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW1 = \t0x%08X\n", + ipu_cm_read(ipu, IPU_FS_PROC_FLOW1)); + dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW2 = \t0x%08X\n", + ipu_cm_read(ipu, IPU_FS_PROC_FLOW2)); + dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW3 = \t0x%08X\n", + ipu_cm_read(ipu, IPU_FS_PROC_FLOW3)); + dev_dbg(ipu->dev, "IPU_FS_DISP_FLOW1 = \t0x%08X\n", + ipu_cm_read(ipu, IPU_FS_DISP_FLOW1)); +} + +/*! + * This function is called to initialize a logical IPU channel. + * + * @param ipu ipu handler + * @param channel Input parameter for the logical channel ID to init. + * + * @param params Input parameter containing union of channel + * initialization parameters. + * + * @return Returns 0 on success or negative error code on fail + */ +int32_t ipu_init_channel(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel_params_t *params) +{ + int ret = 0; + uint32_t ipu_conf; + uint32_t reg; + unsigned long lock_flags; + + dev_dbg(ipu->dev, "init channel = %d\n", IPU_CHAN_ID(channel)); + + _ipu_get(ipu); + + _ipu_lock(ipu, &lock_flags); + + if (ipu->channel_init_mask & (1L << IPU_CHAN_ID(channel))) { + dev_warn(ipu->dev, "Warning: channel already initialized %d\n", + IPU_CHAN_ID(channel)); + } + + ipu_conf = ipu_cm_read(ipu, IPU_CONF); + + switch (channel) { + case CSI_MEM0: + case CSI_MEM1: + case CSI_MEM2: + case CSI_MEM3: + if (params->csi_mem.csi > 1) { + ret = -EINVAL; + goto err; + } + + if (params->csi_mem.interlaced) + ipu->chan_is_interlaced[channel_2_dma(channel, + IPU_OUTPUT_BUFFER)] = true; + else + ipu->chan_is_interlaced[channel_2_dma(channel, + IPU_OUTPUT_BUFFER)] = false; + + ipu->smfc_use_count++; + ipu->csi_channel[params->csi_mem.csi] = channel; + + /*SMFC setting*/ + if (params->csi_mem.mipi_en) { + ipu_conf |= (1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + + params->csi_mem.csi)); + _ipu_smfc_init(ipu, channel, params->csi_mem.mipi_id, + params->csi_mem.csi); + } else { + ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + + params->csi_mem.csi)); + _ipu_smfc_init(ipu, channel, 0, params->csi_mem.csi); + } + + /*CSI data (include compander) dest*/ + _ipu_csi_init(ipu, channel, params->csi_mem.csi); + break; + case CSI_PRP_ENC_MEM: + if (params->csi_prp_enc_mem.csi > 1) { + ret = -EINVAL; + goto err; + } + if (ipu->using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) { + ret = -EINVAL; + goto err; + } + ipu->using_ic_dirct_ch = CSI_PRP_ENC_MEM; + + ipu->ic_use_count++; + ipu->csi_channel[params->csi_prp_enc_mem.csi] = channel; + + /*Without SMFC, CSI only support parallel data source*/ + ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + + params->csi_prp_enc_mem.csi)); + + /*CSI0/1 feed into IC*/ + ipu_conf &= ~IPU_CONF_IC_INPUT; + if (params->csi_prp_enc_mem.csi) + ipu_conf |= IPU_CONF_CSI_SEL; + else + ipu_conf &= ~IPU_CONF_CSI_SEL; + + /*PRP skip buffer in memory, only valid when RWS_EN is true*/ + reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); + ipu_cm_write(ipu, reg & ~FS_ENC_IN_VALID, IPU_FS_PROC_FLOW1); + + /*CSI data (include compander) dest*/ + _ipu_csi_init(ipu, channel, params->csi_prp_enc_mem.csi); + _ipu_ic_init_prpenc(ipu, params, true); + break; + case CSI_PRP_VF_MEM: + if (params->csi_prp_vf_mem.csi > 1) { + ret = -EINVAL; + goto err; + } + if (ipu->using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) { + ret = -EINVAL; + goto err; + } + ipu->using_ic_dirct_ch = CSI_PRP_VF_MEM; + + ipu->ic_use_count++; + ipu->csi_channel[params->csi_prp_vf_mem.csi] = channel; + + /*Without SMFC, CSI only support parallel data source*/ + ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + + params->csi_prp_vf_mem.csi)); + + /*CSI0/1 feed into IC*/ + ipu_conf &= ~IPU_CONF_IC_INPUT; + if (params->csi_prp_vf_mem.csi) + ipu_conf |= IPU_CONF_CSI_SEL; + else + ipu_conf &= ~IPU_CONF_CSI_SEL; + + /*PRP skip buffer in memory, only valid when RWS_EN is true*/ + reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); + ipu_cm_write(ipu, reg & ~FS_VF_IN_VALID, IPU_FS_PROC_FLOW1); + + /*CSI data (include compander) dest*/ + _ipu_csi_init(ipu, channel, params->csi_prp_vf_mem.csi); + _ipu_ic_init_prpvf(ipu, params, true); + break; + case MEM_PRP_VF_MEM: + ipu->ic_use_count++; + reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); + ipu_cm_write(ipu, reg | FS_VF_IN_VALID, IPU_FS_PROC_FLOW1); + + if (params->mem_prp_vf_mem.graphics_combine_en) + ipu->sec_chan_en[IPU_CHAN_ID(channel)] = true; + if (params->mem_prp_vf_mem.alpha_chan_en) + ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = true; + + _ipu_ic_init_prpvf(ipu, params, false); + break; + case MEM_VDI_PRP_VF_MEM: + if ((ipu->using_ic_dirct_ch == CSI_PRP_VF_MEM) || + (ipu->using_ic_dirct_ch == CSI_PRP_ENC_MEM)) { + ret = -EINVAL; + goto err; + } + ipu->using_ic_dirct_ch = MEM_VDI_PRP_VF_MEM; + ipu->ic_use_count++; + ipu->vdi_use_count++; + reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); + reg &= ~FS_VDI_SRC_SEL_MASK; + ipu_cm_write(ipu, reg , IPU_FS_PROC_FLOW1); + + if (params->mem_prp_vf_mem.graphics_combine_en) + ipu->sec_chan_en[IPU_CHAN_ID(channel)] = true; + _ipu_ic_init_prpvf(ipu, params, false); + _ipu_vdi_init(ipu, channel, params); + break; + case MEM_VDI_PRP_VF_MEM_P: + _ipu_vdi_init(ipu, channel, params); + break; + case MEM_VDI_PRP_VF_MEM_N: + _ipu_vdi_init(ipu, channel, params); + break; + case MEM_ROT_VF_MEM: + ipu->ic_use_count++; + ipu->rot_use_count++; + _ipu_ic_init_rotate_vf(ipu, params); + break; + case MEM_PRP_ENC_MEM: + ipu->ic_use_count++; + reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); + ipu_cm_write(ipu, reg | FS_ENC_IN_VALID, IPU_FS_PROC_FLOW1); + _ipu_ic_init_prpenc(ipu, params, false); + break; + case MEM_ROT_ENC_MEM: + ipu->ic_use_count++; + ipu->rot_use_count++; + _ipu_ic_init_rotate_enc(ipu, params); + break; + case MEM_PP_MEM: + if (params->mem_pp_mem.graphics_combine_en) + ipu->sec_chan_en[IPU_CHAN_ID(channel)] = true; + if (params->mem_pp_mem.alpha_chan_en) + ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = true; + _ipu_ic_init_pp(ipu, params); + ipu->ic_use_count++; + break; + case MEM_ROT_PP_MEM: + _ipu_ic_init_rotate_pp(ipu, params); + ipu->ic_use_count++; + ipu->rot_use_count++; + break; + case MEM_DC_SYNC: + if (params->mem_dc_sync.di > 1) { + ret = -EINVAL; + goto err; + } + + ipu->dc_di_assignment[1] = params->mem_dc_sync.di; + _ipu_dc_init(ipu, 1, params->mem_dc_sync.di, + params->mem_dc_sync.interlaced, + params->mem_dc_sync.out_pixel_fmt); + ipu->di_use_count[params->mem_dc_sync.di]++; + ipu->dc_use_count++; + ipu->dmfc_use_count++; + break; + case MEM_BG_SYNC: + if (params->mem_dp_bg_sync.di > 1) { + ret = -EINVAL; + goto err; + } + + if (params->mem_dp_bg_sync.alpha_chan_en) + ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = true; + + ipu->dc_di_assignment[5] = params->mem_dp_bg_sync.di; + _ipu_dp_init(ipu, channel, params->mem_dp_bg_sync.in_pixel_fmt, + params->mem_dp_bg_sync.out_pixel_fmt); + _ipu_dc_init(ipu, 5, params->mem_dp_bg_sync.di, + params->mem_dp_bg_sync.interlaced, + params->mem_dp_bg_sync.out_pixel_fmt); + ipu->di_use_count[params->mem_dp_bg_sync.di]++; + ipu->dc_use_count++; + ipu->dp_use_count++; + ipu->dmfc_use_count++; + break; + case MEM_FG_SYNC: + _ipu_dp_init(ipu, channel, params->mem_dp_fg_sync.in_pixel_fmt, + params->mem_dp_fg_sync.out_pixel_fmt); + + if (params->mem_dp_fg_sync.alpha_chan_en) + ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = true; + + ipu->dc_use_count++; + ipu->dp_use_count++; + ipu->dmfc_use_count++; + break; + case DIRECT_ASYNC0: + if (params->direct_async.di > 1) { + ret = -EINVAL; + goto err; + } + + ipu->dc_di_assignment[8] = params->direct_async.di; + _ipu_dc_init(ipu, 8, params->direct_async.di, false, IPU_PIX_FMT_GENERIC); + ipu->di_use_count[params->direct_async.di]++; + ipu->dc_use_count++; + break; + case DIRECT_ASYNC1: + if (params->direct_async.di > 1) { + ret = -EINVAL; + goto err; + } + + ipu->dc_di_assignment[9] = params->direct_async.di; + _ipu_dc_init(ipu, 9, params->direct_async.di, false, IPU_PIX_FMT_GENERIC); + ipu->di_use_count[params->direct_async.di]++; + ipu->dc_use_count++; + break; + default: + dev_err(ipu->dev, "Missing channel initialization\n"); + break; + } + + /* Enable IPU sub module */ + ipu->channel_init_mask |= 1L << IPU_CHAN_ID(channel); + + ipu_cm_write(ipu, ipu_conf, IPU_CONF); + +err: + _ipu_unlock(ipu, &lock_flags); + return ret; +} +EXPORT_SYMBOL(ipu_init_channel); + +/*! + * This function is called to uninitialize a logical IPU channel. + * + * @param ipu ipu handler + * @param channel Input parameter for the logical channel ID to uninit. + */ +void ipu_uninit_channel(struct ipu_soc *ipu, ipu_channel_t channel) +{ + uint32_t reg; + uint32_t in_dma, out_dma = 0; + uint32_t ipu_conf; + unsigned long lock_flags; + + _ipu_lock(ipu, &lock_flags); + + if ((ipu->channel_init_mask & (1L << IPU_CHAN_ID(channel))) == 0) { + dev_err(ipu->dev, "Channel already uninitialized %d\n", + IPU_CHAN_ID(channel)); + _ipu_unlock(ipu, &lock_flags); + return; + } + + /* Make sure channel is disabled */ + /* Get input and output dma channels */ + in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER); + out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER); + + if (idma_is_set(ipu, IDMAC_CHA_EN, in_dma) || + idma_is_set(ipu, IDMAC_CHA_EN, out_dma)) { + dev_err(ipu->dev, + "Channel %d is not disabled, disable first\n", + IPU_CHAN_ID(channel)); + _ipu_unlock(ipu, &lock_flags); + return; + } + + ipu_conf = ipu_cm_read(ipu, IPU_CONF); + + /* Reset the double buffer */ + reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(in_dma)); + ipu_cm_write(ipu, reg & ~idma_mask(in_dma), IPU_CHA_DB_MODE_SEL(in_dma)); + reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(out_dma)); + ipu_cm_write(ipu, reg & ~idma_mask(out_dma), IPU_CHA_DB_MODE_SEL(out_dma)); + + /* Reset the triple buffer */ + reg = ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(in_dma)); + ipu_cm_write(ipu, reg & ~idma_mask(in_dma), IPU_CHA_TRB_MODE_SEL(in_dma)); + reg = ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(out_dma)); + ipu_cm_write(ipu, reg & ~idma_mask(out_dma), IPU_CHA_TRB_MODE_SEL(out_dma)); + + if (_ipu_is_ic_chan(in_dma) || _ipu_is_dp_graphic_chan(in_dma)) { + ipu->sec_chan_en[IPU_CHAN_ID(channel)] = false; + ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = false; + } + + switch (channel) { + case CSI_MEM0: + case CSI_MEM1: + case CSI_MEM2: + case CSI_MEM3: + ipu->smfc_use_count--; + if (ipu->csi_channel[0] == channel) { + ipu->csi_channel[0] = CHAN_NONE; + } else if (ipu->csi_channel[1] == channel) { + ipu->csi_channel[1] = CHAN_NONE; + } + break; + case CSI_PRP_ENC_MEM: + ipu->ic_use_count--; + if (ipu->using_ic_dirct_ch == CSI_PRP_ENC_MEM) + ipu->using_ic_dirct_ch = 0; + _ipu_ic_uninit_prpenc(ipu); + if (ipu->csi_channel[0] == channel) { + ipu->csi_channel[0] = CHAN_NONE; + } else if (ipu->csi_channel[1] == channel) { + ipu->csi_channel[1] = CHAN_NONE; + } + break; + case CSI_PRP_VF_MEM: + ipu->ic_use_count--; + if (ipu->using_ic_dirct_ch == CSI_PRP_VF_MEM) + ipu->using_ic_dirct_ch = 0; + _ipu_ic_uninit_prpvf(ipu); + if (ipu->csi_channel[0] == channel) { + ipu->csi_channel[0] = CHAN_NONE; + } else if (ipu->csi_channel[1] == channel) { + ipu->csi_channel[1] = CHAN_NONE; + } + break; + case MEM_PRP_VF_MEM: + ipu->ic_use_count--; + _ipu_ic_uninit_prpvf(ipu); + reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); + ipu_cm_write(ipu, reg & ~FS_VF_IN_VALID, IPU_FS_PROC_FLOW1); + break; + case MEM_VDI_PRP_VF_MEM: + ipu->ic_use_count--; + ipu->vdi_use_count--; + if (ipu->using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) + ipu->using_ic_dirct_ch = 0; + _ipu_ic_uninit_prpvf(ipu); + _ipu_vdi_uninit(ipu); + reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); + ipu_cm_write(ipu, reg & ~FS_VF_IN_VALID, IPU_FS_PROC_FLOW1); + break; + case MEM_VDI_PRP_VF_MEM_P: + case MEM_VDI_PRP_VF_MEM_N: + break; + case MEM_ROT_VF_MEM: + ipu->rot_use_count--; + ipu->ic_use_count--; + _ipu_ic_uninit_rotate_vf(ipu); + break; + case MEM_PRP_ENC_MEM: + ipu->ic_use_count--; + _ipu_ic_uninit_prpenc(ipu); + reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); + ipu_cm_write(ipu, reg & ~FS_ENC_IN_VALID, IPU_FS_PROC_FLOW1); + break; + case MEM_ROT_ENC_MEM: + ipu->rot_use_count--; + ipu->ic_use_count--; + _ipu_ic_uninit_rotate_enc(ipu); + break; + case MEM_PP_MEM: + ipu->ic_use_count--; + _ipu_ic_uninit_pp(ipu); + break; + case MEM_ROT_PP_MEM: + ipu->rot_use_count--; + ipu->ic_use_count--; + _ipu_ic_uninit_rotate_pp(ipu); + break; + case MEM_DC_SYNC: + _ipu_dc_uninit(ipu, 1); + ipu->di_use_count[ipu->dc_di_assignment[1]]--; + ipu->dc_use_count--; + ipu->dmfc_use_count--; + break; + case MEM_BG_SYNC: + _ipu_dp_uninit(ipu, channel); + _ipu_dc_uninit(ipu, 5); + ipu->di_use_count[ipu->dc_di_assignment[5]]--; + ipu->dc_use_count--; + ipu->dp_use_count--; + ipu->dmfc_use_count--; + break; + case MEM_FG_SYNC: + _ipu_dp_uninit(ipu, channel); + ipu->dc_use_count--; + ipu->dp_use_count--; + ipu->dmfc_use_count--; + break; + case DIRECT_ASYNC0: + _ipu_dc_uninit(ipu, 8); + ipu->di_use_count[ipu->dc_di_assignment[8]]--; + ipu->dc_use_count--; + break; + case DIRECT_ASYNC1: + _ipu_dc_uninit(ipu, 9); + ipu->di_use_count[ipu->dc_di_assignment[9]]--; + ipu->dc_use_count--; + break; + default: + break; + } + + if (ipu->ic_use_count == 0) + ipu_conf &= ~IPU_CONF_IC_EN; + if (ipu->vdi_use_count == 0) { + ipu_conf &= ~IPU_CONF_ISP_EN; + ipu_conf &= ~IPU_CONF_VDI_EN; + ipu_conf &= ~IPU_CONF_IC_INPUT; + } + if (ipu->rot_use_count == 0) + ipu_conf &= ~IPU_CONF_ROT_EN; + if (ipu->dc_use_count == 0) + ipu_conf &= ~IPU_CONF_DC_EN; + if (ipu->dp_use_count == 0) + ipu_conf &= ~IPU_CONF_DP_EN; + if (ipu->dmfc_use_count == 0) + ipu_conf &= ~IPU_CONF_DMFC_EN; + if (ipu->di_use_count[0] == 0) { + ipu_conf &= ~IPU_CONF_DI0_EN; + } + if (ipu->di_use_count[1] == 0) { + ipu_conf &= ~IPU_CONF_DI1_EN; + } + if (ipu->smfc_use_count == 0) + ipu_conf &= ~IPU_CONF_SMFC_EN; + + ipu_cm_write(ipu, ipu_conf, IPU_CONF); + + ipu->channel_init_mask &= ~(1L << IPU_CHAN_ID(channel)); + + _ipu_unlock(ipu, &lock_flags); + + _ipu_put(ipu); + + WARN_ON(ipu->ic_use_count < 0); + WARN_ON(ipu->vdi_use_count < 0); + WARN_ON(ipu->rot_use_count < 0); + WARN_ON(ipu->dc_use_count < 0); + WARN_ON(ipu->dp_use_count < 0); + WARN_ON(ipu->dmfc_use_count < 0); + WARN_ON(ipu->smfc_use_count < 0); +} +EXPORT_SYMBOL(ipu_uninit_channel); + +/*! + * This function is called to initialize buffer(s) for logical IPU channel. + * + * @param ipu ipu handler + * + * @param channel Input parameter for the logical channel ID. + * + * @param type Input parameter which buffer to initialize. + * + * @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 stride Input parameter for stride length of buffer + * in pixels. + * + * @param rot_mode Input parameter for rotation setting of buffer. + * A rotation setting other than + * IPU_ROTATE_VERT_FLIP + * should only be used for input buffers of + * rotation channels. + * + * @param phyaddr_0 Input parameter buffer 0 physical address. + * + * @param phyaddr_1 Input parameter buffer 1 physical address. + * Setting this to a value other than NULL enables + * double buffering mode. + * + * @param phyaddr_2 Input parameter buffer 2 physical address. + * Setting this to a value other than NULL enables + * triple buffering mode, phyaddr_1 should not be + * NULL then. + * + * @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 ipu_init_channel_buffer(struct ipu_soc *ipu, ipu_channel_t channel, + ipu_buffer_t type, + uint32_t pixel_fmt, + uint16_t width, uint16_t height, + uint32_t stride, + ipu_rotate_mode_t rot_mode, + dma_addr_t phyaddr_0, dma_addr_t phyaddr_1, + dma_addr_t phyaddr_2, + uint32_t u, uint32_t v) +{ + uint32_t reg; + uint32_t dma_chan; + uint32_t burst_size; + unsigned long lock_flags; + + dma_chan = channel_2_dma(channel, type); + if (!idma_is_valid(dma_chan)) + return -EINVAL; + + if (stride < width * bytes_per_pixel(pixel_fmt)) + stride = width * bytes_per_pixel(pixel_fmt); + + if (stride % 4) { + dev_err(ipu->dev, + "Stride not 32-bit aligned, stride = %d\n", stride); + return -EINVAL; + } + /* IC & IRT channels' width must be multiple of 8 pixels */ + if ((_ipu_is_ic_chan(dma_chan) || _ipu_is_irt_chan(dma_chan)) + && (width % 8)) { + dev_err(ipu->dev, "Width must be 8 pixel multiple\n"); + return -EINVAL; + } + + /* IPUv3EX and IPUv3M support triple buffer */ + if ((!_ipu_is_trb_chan(dma_chan)) && phyaddr_2) { + dev_err(ipu->dev, "Chan%d doesn't support triple buffer " + "mode\n", dma_chan); + return -EINVAL; + } + if (!phyaddr_1 && phyaddr_2) { + dev_err(ipu->dev, "Chan%d's buf1 physical addr is NULL for " + "triple buffer mode\n", dma_chan); + return -EINVAL; + } + + _ipu_lock(ipu, &lock_flags); + + /* Build parameter memory data for DMA channel */ + _ipu_ch_param_init(ipu, dma_chan, pixel_fmt, width, height, stride, u, v, 0, + phyaddr_0, phyaddr_1, phyaddr_2); + + /* Set correlative channel parameter of local alpha channel */ + if ((_ipu_is_ic_graphic_chan(dma_chan) || + _ipu_is_dp_graphic_chan(dma_chan)) && + (ipu->thrd_chan_en[IPU_CHAN_ID(channel)] == true)) { + _ipu_ch_param_set_alpha_use_separate_channel(ipu, dma_chan, true); + _ipu_ch_param_set_alpha_buffer_memory(ipu, dma_chan); + _ipu_ch_param_set_alpha_condition_read(ipu, dma_chan); + /* fix alpha width as 8 and burst size as 16*/ + _ipu_ch_params_set_alpha_width(ipu, dma_chan, 8); + _ipu_ch_param_set_burst_size(ipu, dma_chan, 16); + } else if (_ipu_is_ic_graphic_chan(dma_chan) && + ipu_pixel_format_has_alpha(pixel_fmt)) + _ipu_ch_param_set_alpha_use_separate_channel(ipu, dma_chan, false); + + if (rot_mode) + _ipu_ch_param_set_rotation(ipu, dma_chan, rot_mode); + + /* IC and ROT channels have restriction of 8 or 16 pix burst length */ + if (_ipu_is_ic_chan(dma_chan)) { + if ((width % 16) == 0) + _ipu_ch_param_set_burst_size(ipu, dma_chan, 16); + else + _ipu_ch_param_set_burst_size(ipu, dma_chan, 8); + } else if (_ipu_is_irt_chan(dma_chan)) { + _ipu_ch_param_set_burst_size(ipu, dma_chan, 8); + _ipu_ch_param_set_block_mode(ipu, dma_chan); + } else if (_ipu_is_dmfc_chan(dma_chan)) { + burst_size = _ipu_ch_param_get_burst_size(ipu, dma_chan); + _ipu_dmfc_set_wait4eot(ipu, dma_chan, width); + _ipu_dmfc_set_burst_size(ipu, dma_chan, burst_size); + } + + if (_ipu_disp_chan_is_interlaced(ipu, channel) || + ipu->chan_is_interlaced[dma_chan]) + _ipu_ch_param_set_interlaced_scan(ipu, dma_chan); + + if (_ipu_is_ic_chan(dma_chan) || _ipu_is_irt_chan(dma_chan)) { + burst_size = _ipu_ch_param_get_burst_size(ipu, dma_chan); + _ipu_ic_idma_init(ipu, dma_chan, width, height, burst_size, + rot_mode); + } else if (_ipu_is_smfc_chan(dma_chan)) { + burst_size = _ipu_ch_param_get_burst_size(ipu, dma_chan); + if ((pixel_fmt == IPU_PIX_FMT_GENERIC) && + ((_ipu_ch_param_get_bpp(ipu, dma_chan) == 5) || + (_ipu_ch_param_get_bpp(ipu, dma_chan) == 3))) + burst_size = burst_size >> 4; + else + burst_size = burst_size >> 2; + _ipu_smfc_set_burst_size(ipu, channel, burst_size-1); + } + + /* AXI-id */ + if (idma_is_set(ipu, IDMAC_CHA_PRI, dma_chan)) { + unsigned reg = IDMAC_CH_LOCK_EN_1; + uint32_t value = 0; + if (cpu_is_mx53()) { + _ipu_ch_param_set_axi_id(ipu, dma_chan, 0); + switch (dma_chan) { + case 5: + value = 0x3; + break; + case 11: + value = 0x3 << 2; + break; + case 12: + value = 0x3 << 4; + break; + case 14: + value = 0x3 << 6; + break; + case 15: + value = 0x3 << 8; + break; + case 20: + value = 0x3 << 10; + break; + case 21: + value = 0x3 << 12; + break; + case 22: + value = 0x3 << 14; + break; + case 23: + value = 0x3 << 16; + break; + case 27: + value = 0x3 << 18; + break; + case 28: + value = 0x3 << 20; + break; + case 45: + reg = IDMAC_CH_LOCK_EN_2; + value = 0x3 << 0; + break; + case 46: + reg = IDMAC_CH_LOCK_EN_2; + value = 0x3 << 2; + break; + case 47: + reg = IDMAC_CH_LOCK_EN_2; + value = 0x3 << 4; + break; + case 48: + reg = IDMAC_CH_LOCK_EN_2; + value = 0x3 << 6; + break; + case 49: + reg = IDMAC_CH_LOCK_EN_2; + value = 0x3 << 8; + break; + case 50: + reg = IDMAC_CH_LOCK_EN_2; + value = 0x3 << 10; + break; + default: + break; + } + value |= ipu_idmac_read(ipu, reg); + ipu_idmac_write(ipu, value, reg); + } else + _ipu_ch_param_set_axi_id(ipu, dma_chan, 1); + } + + _ipu_ch_param_dump(ipu, dma_chan); + + if (phyaddr_2 && g_ipu_hw_rev >= 2) { + reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(dma_chan)); + reg &= ~idma_mask(dma_chan); + ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(dma_chan)); + + reg = ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(dma_chan)); + reg |= idma_mask(dma_chan); + ipu_cm_write(ipu, reg, IPU_CHA_TRB_MODE_SEL(dma_chan)); + + /* Set IDMAC third buffer's cpmem number */ + /* See __ipu_ch_get_third_buf_cpmem_num() for mapping */ + ipu_idmac_write(ipu, 0x00444047L, IDMAC_SUB_ADDR_4); + ipu_idmac_write(ipu, 0x46004241L, IDMAC_SUB_ADDR_3); + ipu_idmac_write(ipu, 0x00000045L, IDMAC_SUB_ADDR_1); + + /* Reset to buffer 0 */ + ipu_cm_write(ipu, tri_cur_buf_mask(dma_chan), + IPU_CHA_TRIPLE_CUR_BUF(dma_chan)); + } else { + reg = ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(dma_chan)); + reg &= ~idma_mask(dma_chan); + ipu_cm_write(ipu, reg, IPU_CHA_TRB_MODE_SEL(dma_chan)); + + reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(dma_chan)); + if (phyaddr_1) + reg |= idma_mask(dma_chan); + else + reg &= ~idma_mask(dma_chan); + ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(dma_chan)); + + /* Reset to buffer 0 */ + ipu_cm_write(ipu, idma_mask(dma_chan), + IPU_CHA_CUR_BUF(dma_chan)); + + } + + _ipu_unlock(ipu, &lock_flags); + + return 0; +} +EXPORT_SYMBOL(ipu_init_channel_buffer); + +/*! + * This function is called to update the physical address of a buffer for + * a logical IPU channel. + * + * @param ipu ipu handler + * @param channel Input parameter for the logical channel ID. + * + * @param type Input parameter which buffer to initialize. + * + * @param bufNum Input parameter for buffer number to update. + * 0 or 1 are the only valid values. + * + * @param phyaddr Input parameter buffer physical address. + * + * @return This function returns 0 on success or negative error code on + * fail. This function will fail if the buffer is set to ready. + */ +int32_t ipu_update_channel_buffer(struct ipu_soc *ipu, ipu_channel_t channel, + ipu_buffer_t type, uint32_t bufNum, dma_addr_t phyaddr) +{ + uint32_t reg; + int ret = 0; + uint32_t dma_chan = channel_2_dma(channel, type); + unsigned long lock_flags; + + if (dma_chan == IDMA_CHAN_INVALID) + return -EINVAL; + + _ipu_lock(ipu, &lock_flags); + + if (bufNum == 0) + reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(dma_chan)); + else if (bufNum == 1) + reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(dma_chan)); + else + reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(dma_chan)); + + if ((reg & idma_mask(dma_chan)) == 0) + _ipu_ch_param_set_buffer(ipu, dma_chan, bufNum, phyaddr); + else + ret = -EACCES; + + _ipu_unlock(ipu, &lock_flags); + + return ret; +} +EXPORT_SYMBOL(ipu_update_channel_buffer); + + +/*! + * This function is called to initialize a buffer for logical IPU channel. + * + * @param ipu ipu handler + * @param channel Input parameter for the logical channel ID. + * + * @param type Input parameter which buffer to initialize. + * + * @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 stride Input parameter for stride length of buffer + * in pixels. + * + * @param u predefined private u offset for additional cropping, + * zero if not used. + * + * @param v predefined private v offset for additional cropping, + * zero if not used. + * + * @param vertical_offset vertical offset for Y coordinate + * in the existed frame + * + * + * @param horizontal_offset horizontal offset for X coordinate + * in the existed frame + * + * + * @return Returns 0 on success or negative error code on fail + * This function will fail if any buffer is set to ready. + */ + +int32_t ipu_update_channel_offset(struct ipu_soc *ipu, + ipu_channel_t channel, ipu_buffer_t type, + uint32_t pixel_fmt, + uint16_t width, uint16_t height, + uint32_t stride, + uint32_t u, uint32_t v, + uint32_t vertical_offset, uint32_t horizontal_offset) +{ + int ret = 0; + uint32_t dma_chan = channel_2_dma(channel, type); + unsigned long lock_flags; + + if (dma_chan == IDMA_CHAN_INVALID) + return -EINVAL; + + _ipu_lock(ipu, &lock_flags); + + if ((ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(dma_chan)) & idma_mask(dma_chan)) || + (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(dma_chan)) & idma_mask(dma_chan)) || + ((ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(dma_chan)) & idma_mask(dma_chan)) && + (ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(dma_chan)) & idma_mask(dma_chan)) && + _ipu_is_trb_chan(dma_chan))) + ret = -EACCES; + else + _ipu_ch_offset_update(ipu, dma_chan, pixel_fmt, width, height, stride, + u, v, 0, vertical_offset, horizontal_offset); + + _ipu_unlock(ipu, &lock_flags); + return ret; +} +EXPORT_SYMBOL(ipu_update_channel_offset); + + +/*! + * This function is called to set a channel's buffer as ready. + * + * @param ipu ipu handler + * @param channel Input parameter for the logical channel ID. + * + * @param type Input parameter which buffer to initialize. + * + * @param bufNum Input parameter for which buffer number set to + * ready state. + * + * @return Returns 0 on success or negative error code on fail + */ +int32_t ipu_select_buffer(struct ipu_soc *ipu, ipu_channel_t channel, + ipu_buffer_t type, uint32_t bufNum) +{ + uint32_t dma_chan = channel_2_dma(channel, type); + unsigned long lock_flags; + + if (dma_chan == IDMA_CHAN_INVALID) + return -EINVAL; + + /* Mark buffer to be ready. */ + _ipu_lock(ipu, &lock_flags); + if (bufNum == 0) + ipu_cm_write(ipu, idma_mask(dma_chan), + IPU_CHA_BUF0_RDY(dma_chan)); + else if (bufNum == 1) + ipu_cm_write(ipu, idma_mask(dma_chan), + IPU_CHA_BUF1_RDY(dma_chan)); + else + ipu_cm_write(ipu, idma_mask(dma_chan), + IPU_CHA_BUF2_RDY(dma_chan)); + _ipu_unlock(ipu, &lock_flags); + return 0; +} +EXPORT_SYMBOL(ipu_select_buffer); + +/*! + * This function is called to set a channel's buffer as ready. + * + * @param ipu ipu handler + * @param bufNum Input parameter for which buffer number set to + * ready state. + * + * @return Returns 0 on success or negative error code on fail + */ +int32_t ipu_select_multi_vdi_buffer(struct ipu_soc *ipu, uint32_t bufNum) +{ + + uint32_t dma_chan = channel_2_dma(MEM_VDI_PRP_VF_MEM, IPU_INPUT_BUFFER); + uint32_t mask_bit = + idma_mask(channel_2_dma(MEM_VDI_PRP_VF_MEM_P, IPU_INPUT_BUFFER))| + idma_mask(dma_chan)| + idma_mask(channel_2_dma(MEM_VDI_PRP_VF_MEM_N, IPU_INPUT_BUFFER)); + unsigned long lock_flags; + + /* Mark buffers to be ready. */ + _ipu_lock(ipu, &lock_flags); + if (bufNum == 0) + ipu_cm_write(ipu, mask_bit, IPU_CHA_BUF0_RDY(dma_chan)); + else + ipu_cm_write(ipu, mask_bit, IPU_CHA_BUF1_RDY(dma_chan)); + _ipu_unlock(ipu, &lock_flags); + return 0; +} +EXPORT_SYMBOL(ipu_select_multi_vdi_buffer); + +#define NA -1 +static int proc_dest_sel[] = { + 0, 1, 1, 3, 5, 5, 4, 7, 8, 9, 10, 11, 12, 14, 15, 16, + 0, 1, 1, 5, 5, 5, 5, 5, 7, 8, 9, 10, 11, 12, 14, 31 }; +static int proc_src_sel[] = { 0, 6, 7, 6, 7, 8, 5, NA, NA, NA, + NA, NA, NA, NA, NA, 1, 2, 3, 4, 7, 8, NA, 8, NA }; +static int disp_src_sel[] = { 0, 6, 7, 8, 3, 4, 5, NA, NA, NA, + NA, NA, NA, NA, NA, 1, NA, 2, NA, 3, 4, 4, 4, 4 }; + + +/*! + * This function links 2 channels together for automatic frame + * synchronization. The output of the source channel is linked to the input of + * the destination channel. + * + * @param ipu ipu handler + * @param src_ch Input parameter for the logical channel ID of + * the source channel. + * + * @param dest_ch Input parameter for the logical channel ID of + * the destination channel. + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int32_t ipu_link_channels(struct ipu_soc *ipu, ipu_channel_t src_ch, ipu_channel_t dest_ch) +{ + int retval = 0; + uint32_t fs_proc_flow1; + uint32_t fs_proc_flow2; + uint32_t fs_proc_flow3; + uint32_t fs_disp_flow1; + unsigned long lock_flags; + + _ipu_lock(ipu, &lock_flags); + + fs_proc_flow1 = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); + fs_proc_flow2 = ipu_cm_read(ipu, IPU_FS_PROC_FLOW2); + fs_proc_flow3 = ipu_cm_read(ipu, IPU_FS_PROC_FLOW3); + fs_disp_flow1 = ipu_cm_read(ipu, IPU_FS_DISP_FLOW1); + + switch (src_ch) { + case CSI_MEM0: + fs_proc_flow3 &= ~FS_SMFC0_DEST_SEL_MASK; + fs_proc_flow3 |= + proc_dest_sel[IPU_CHAN_ID(dest_ch)] << + FS_SMFC0_DEST_SEL_OFFSET; + break; + case CSI_MEM1: + fs_proc_flow3 &= ~FS_SMFC1_DEST_SEL_MASK; + fs_proc_flow3 |= + proc_dest_sel[IPU_CHAN_ID(dest_ch)] << + FS_SMFC1_DEST_SEL_OFFSET; + break; + case CSI_MEM2: + fs_proc_flow3 &= ~FS_SMFC2_DEST_SEL_MASK; + fs_proc_flow3 |= + proc_dest_sel[IPU_CHAN_ID(dest_ch)] << + FS_SMFC2_DEST_SEL_OFFSET; + break; + case CSI_MEM3: + fs_proc_flow3 &= ~FS_SMFC3_DEST_SEL_MASK; + fs_proc_flow3 |= + proc_dest_sel[IPU_CHAN_ID(dest_ch)] << + FS_SMFC3_DEST_SEL_OFFSET; + break; + case CSI_PRP_ENC_MEM: + fs_proc_flow2 &= ~FS_PRPENC_DEST_SEL_MASK; + fs_proc_flow2 |= + proc_dest_sel[IPU_CHAN_ID(dest_ch)] << + FS_PRPENC_DEST_SEL_OFFSET; + break; + case CSI_PRP_VF_MEM: + fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK; + fs_proc_flow2 |= + proc_dest_sel[IPU_CHAN_ID(dest_ch)] << + FS_PRPVF_DEST_SEL_OFFSET; + break; + case MEM_PP_MEM: + fs_proc_flow2 &= ~FS_PP_DEST_SEL_MASK; + fs_proc_flow2 |= + proc_dest_sel[IPU_CHAN_ID(dest_ch)] << + FS_PP_DEST_SEL_OFFSET; + break; + case MEM_ROT_PP_MEM: + fs_proc_flow2 &= ~FS_PP_ROT_DEST_SEL_MASK; + fs_proc_flow2 |= + proc_dest_sel[IPU_CHAN_ID(dest_ch)] << + FS_PP_ROT_DEST_SEL_OFFSET; + break; + case MEM_PRP_ENC_MEM: + fs_proc_flow2 &= ~FS_PRPENC_DEST_SEL_MASK; + fs_proc_flow2 |= + proc_dest_sel[IPU_CHAN_ID(dest_ch)] << + FS_PRPENC_DEST_SEL_OFFSET; + break; + case MEM_ROT_ENC_MEM: + fs_proc_flow2 &= ~FS_PRPENC_ROT_DEST_SEL_MASK; + fs_proc_flow2 |= + proc_dest_sel[IPU_CHAN_ID(dest_ch)] << + FS_PRPENC_ROT_DEST_SEL_OFFSET; + break; + case MEM_PRP_VF_MEM: + fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK; + fs_proc_flow2 |= + proc_dest_sel[IPU_CHAN_ID(dest_ch)] << + FS_PRPVF_DEST_SEL_OFFSET; + break; + case MEM_VDI_PRP_VF_MEM: + fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK; + fs_proc_flow2 |= + proc_dest_sel[IPU_CHAN_ID(dest_ch)] << + FS_PRPVF_DEST_SEL_OFFSET; + break; + case MEM_ROT_VF_MEM: + fs_proc_flow2 &= ~FS_PRPVF_ROT_DEST_SEL_MASK; + fs_proc_flow2 |= + proc_dest_sel[IPU_CHAN_ID(dest_ch)] << + FS_PRPVF_ROT_DEST_SEL_OFFSET; + break; + default: + retval = -EINVAL; + goto err; + } + + switch (dest_ch) { + case MEM_PP_MEM: + fs_proc_flow1 &= ~FS_PP_SRC_SEL_MASK; + fs_proc_flow1 |= + proc_src_sel[IPU_CHAN_ID(src_ch)] << FS_PP_SRC_SEL_OFFSET; + break; + case MEM_ROT_PP_MEM: + fs_proc_flow1 &= ~FS_PP_ROT_SRC_SEL_MASK; + fs_proc_flow1 |= + proc_src_sel[IPU_CHAN_ID(src_ch)] << + FS_PP_ROT_SRC_SEL_OFFSET; + break; + case MEM_PRP_ENC_MEM: + fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK; + fs_proc_flow1 |= + proc_src_sel[IPU_CHAN_ID(src_ch)] << FS_PRP_SRC_SEL_OFFSET; + break; + case MEM_ROT_ENC_MEM: + fs_proc_flow1 &= ~FS_PRPENC_ROT_SRC_SEL_MASK; + fs_proc_flow1 |= + proc_src_sel[IPU_CHAN_ID(src_ch)] << + FS_PRPENC_ROT_SRC_SEL_OFFSET; + break; + case MEM_PRP_VF_MEM: + fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK; + fs_proc_flow1 |= + proc_src_sel[IPU_CHAN_ID(src_ch)] << FS_PRP_SRC_SEL_OFFSET; + break; + case MEM_VDI_PRP_VF_MEM: + fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK; + fs_proc_flow1 |= + proc_src_sel[IPU_CHAN_ID(src_ch)] << FS_PRP_SRC_SEL_OFFSET; + break; + case MEM_ROT_VF_MEM: + fs_proc_flow1 &= ~FS_PRPVF_ROT_SRC_SEL_MASK; + fs_proc_flow1 |= + proc_src_sel[IPU_CHAN_ID(src_ch)] << + FS_PRPVF_ROT_SRC_SEL_OFFSET; + break; + case MEM_DC_SYNC: + fs_disp_flow1 &= ~FS_DC1_SRC_SEL_MASK; + fs_disp_flow1 |= + disp_src_sel[IPU_CHAN_ID(src_ch)] << FS_DC1_SRC_SEL_OFFSET; + break; + case MEM_BG_SYNC: + fs_disp_flow1 &= ~FS_DP_SYNC0_SRC_SEL_MASK; + fs_disp_flow1 |= + disp_src_sel[IPU_CHAN_ID(src_ch)] << + FS_DP_SYNC0_SRC_SEL_OFFSET; + break; + case MEM_FG_SYNC: + fs_disp_flow1 &= ~FS_DP_SYNC1_SRC_SEL_MASK; + fs_disp_flow1 |= + disp_src_sel[IPU_CHAN_ID(src_ch)] << + FS_DP_SYNC1_SRC_SEL_OFFSET; + break; + case MEM_DC_ASYNC: + fs_disp_flow1 &= ~FS_DC2_SRC_SEL_MASK; + fs_disp_flow1 |= + disp_src_sel[IPU_CHAN_ID(src_ch)] << FS_DC2_SRC_SEL_OFFSET; + break; + case MEM_BG_ASYNC0: + fs_disp_flow1 &= ~FS_DP_ASYNC0_SRC_SEL_MASK; + fs_disp_flow1 |= + disp_src_sel[IPU_CHAN_ID(src_ch)] << + FS_DP_ASYNC0_SRC_SEL_OFFSET; + break; + case MEM_FG_ASYNC0: + fs_disp_flow1 &= ~FS_DP_ASYNC1_SRC_SEL_MASK; + fs_disp_flow1 |= + disp_src_sel[IPU_CHAN_ID(src_ch)] << + FS_DP_ASYNC1_SRC_SEL_OFFSET; + break; + default: + retval = -EINVAL; + goto err; + } + + ipu_cm_write(ipu, fs_proc_flow1, IPU_FS_PROC_FLOW1); + ipu_cm_write(ipu, fs_proc_flow2, IPU_FS_PROC_FLOW2); + ipu_cm_write(ipu, fs_proc_flow3, IPU_FS_PROC_FLOW3); + ipu_cm_write(ipu, fs_disp_flow1, IPU_FS_DISP_FLOW1); + +err: + _ipu_unlock(ipu, &lock_flags); + return retval; +} +EXPORT_SYMBOL(ipu_link_channels); + +/*! + * This function unlinks 2 channels and disables automatic frame + * synchronization. + * + * @param ipu ipu handler + * @param src_ch Input parameter for the logical channel ID of + * the source channel. + * + * @param dest_ch Input parameter for the logical channel ID of + * the destination channel. + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int32_t ipu_unlink_channels(struct ipu_soc *ipu, ipu_channel_t src_ch, ipu_channel_t dest_ch) +{ + int retval = 0; + uint32_t fs_proc_flow1; + uint32_t fs_proc_flow2; + uint32_t fs_proc_flow3; + uint32_t fs_disp_flow1; + unsigned long lock_flags; + + _ipu_lock(ipu, &lock_flags); + + fs_proc_flow1 = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); + fs_proc_flow2 = ipu_cm_read(ipu, IPU_FS_PROC_FLOW2); + fs_proc_flow3 = ipu_cm_read(ipu, IPU_FS_PROC_FLOW3); + fs_disp_flow1 = ipu_cm_read(ipu, IPU_FS_DISP_FLOW1); + + switch (src_ch) { + case CSI_MEM0: + fs_proc_flow3 &= ~FS_SMFC0_DEST_SEL_MASK; + break; + case CSI_MEM1: + fs_proc_flow3 &= ~FS_SMFC1_DEST_SEL_MASK; + break; + case CSI_MEM2: + fs_proc_flow3 &= ~FS_SMFC2_DEST_SEL_MASK; + break; + case CSI_MEM3: + fs_proc_flow3 &= ~FS_SMFC3_DEST_SEL_MASK; + break; + case CSI_PRP_ENC_MEM: + fs_proc_flow2 &= ~FS_PRPENC_DEST_SEL_MASK; + break; + case CSI_PRP_VF_MEM: + fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK; + break; + case MEM_PP_MEM: + fs_proc_flow2 &= ~FS_PP_DEST_SEL_MASK; + break; + case MEM_ROT_PP_MEM: + fs_proc_flow2 &= ~FS_PP_ROT_DEST_SEL_MASK; + break; + case MEM_PRP_ENC_MEM: + fs_proc_flow2 &= ~FS_PRPENC_DEST_SEL_MASK; + break; + case MEM_ROT_ENC_MEM: + fs_proc_flow2 &= ~FS_PRPENC_ROT_DEST_SEL_MASK; + break; + case MEM_PRP_VF_MEM: + fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK; + break; + case MEM_VDI_PRP_VF_MEM: + fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK; + break; + case MEM_ROT_VF_MEM: + fs_proc_flow2 &= ~FS_PRPVF_ROT_DEST_SEL_MASK; + break; + default: + retval = -EINVAL; + goto err; + } + + switch (dest_ch) { + case MEM_PP_MEM: + fs_proc_flow1 &= ~FS_PP_SRC_SEL_MASK; + break; + case MEM_ROT_PP_MEM: + fs_proc_flow1 &= ~FS_PP_ROT_SRC_SEL_MASK; + break; + case MEM_PRP_ENC_MEM: + fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK; + break; + case MEM_ROT_ENC_MEM: + fs_proc_flow1 &= ~FS_PRPENC_ROT_SRC_SEL_MASK; + break; + case MEM_PRP_VF_MEM: + fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK; + break; + case MEM_VDI_PRP_VF_MEM: + fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK; + break; + case MEM_ROT_VF_MEM: + fs_proc_flow1 &= ~FS_PRPVF_ROT_SRC_SEL_MASK; + break; + case MEM_DC_SYNC: + fs_disp_flow1 &= ~FS_DC1_SRC_SEL_MASK; + break; + case MEM_BG_SYNC: + fs_disp_flow1 &= ~FS_DP_SYNC0_SRC_SEL_MASK; + break; + case MEM_FG_SYNC: + fs_disp_flow1 &= ~FS_DP_SYNC1_SRC_SEL_MASK; + break; + case MEM_DC_ASYNC: + fs_disp_flow1 &= ~FS_DC2_SRC_SEL_MASK; + break; + case MEM_BG_ASYNC0: + fs_disp_flow1 &= ~FS_DP_ASYNC0_SRC_SEL_MASK; + break; + case MEM_FG_ASYNC0: + fs_disp_flow1 &= ~FS_DP_ASYNC1_SRC_SEL_MASK; + break; + default: + retval = -EINVAL; + goto err; + } + + ipu_cm_write(ipu, fs_proc_flow1, IPU_FS_PROC_FLOW1); + ipu_cm_write(ipu, fs_proc_flow2, IPU_FS_PROC_FLOW2); + ipu_cm_write(ipu, fs_proc_flow3, IPU_FS_PROC_FLOW3); + ipu_cm_write(ipu, fs_disp_flow1, IPU_FS_DISP_FLOW1); + +err: + _ipu_unlock(ipu, &lock_flags); + return retval; +} +EXPORT_SYMBOL(ipu_unlink_channels); + +/*! + * This function check whether a logical channel was enabled. + * + * @param ipu ipu handler + * @param channel Input parameter for the logical channel ID. + * + * @return This function returns 1 while request channel is enabled or + * 0 for not enabled. + */ +int32_t ipu_is_channel_busy(struct ipu_soc *ipu, ipu_channel_t channel) +{ + uint32_t reg; + uint32_t in_dma; + uint32_t out_dma; + + out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER); + in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER); + + reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(in_dma)); + if (reg & idma_mask(in_dma)) + return 1; + reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(out_dma)); + if (reg & idma_mask(out_dma)) + return 1; + return 0; +} +EXPORT_SYMBOL(ipu_is_channel_busy); + +/*! + * This function enables a logical channel. + * + * @param ipu ipu handler + * @param channel Input parameter for the logical channel ID. + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int32_t ipu_enable_channel(struct ipu_soc *ipu, ipu_channel_t channel) +{ + uint32_t reg; + uint32_t ipu_conf; + uint32_t in_dma; + uint32_t out_dma; + uint32_t sec_dma; + uint32_t thrd_dma; + unsigned long lock_flags; + + _ipu_lock(ipu, &lock_flags); + + if (ipu->channel_enable_mask & (1L << IPU_CHAN_ID(channel))) { + dev_err(ipu->dev, "Warning: channel already enabled %d\n", + IPU_CHAN_ID(channel)); + _ipu_unlock(ipu, &lock_flags); + return -EACCES; + } + + /* Get input and output dma channels */ + out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER); + in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER); + + ipu_conf = ipu_cm_read(ipu, IPU_CONF); + if (ipu->di_use_count[0] > 0) { + ipu_conf |= IPU_CONF_DI0_EN; + } + if (ipu->di_use_count[1] > 0) { + ipu_conf |= IPU_CONF_DI1_EN; + } + if (ipu->dp_use_count > 0) + ipu_conf |= IPU_CONF_DP_EN; + if (ipu->dc_use_count > 0) + ipu_conf |= IPU_CONF_DC_EN; + if (ipu->dmfc_use_count > 0) + ipu_conf |= IPU_CONF_DMFC_EN; + if (ipu->ic_use_count > 0) + ipu_conf |= IPU_CONF_IC_EN; + if (ipu->vdi_use_count > 0) { + ipu_conf |= IPU_CONF_ISP_EN; + ipu_conf |= IPU_CONF_VDI_EN; + ipu_conf |= IPU_CONF_IC_INPUT; + } + if (ipu->rot_use_count > 0) + ipu_conf |= IPU_CONF_ROT_EN; + if (ipu->smfc_use_count > 0) + ipu_conf |= IPU_CONF_SMFC_EN; + ipu_cm_write(ipu, ipu_conf, IPU_CONF); + + if (idma_is_valid(in_dma)) { + reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(in_dma)); + ipu_idmac_write(ipu, reg | idma_mask(in_dma), IDMAC_CHA_EN(in_dma)); + } + if (idma_is_valid(out_dma)) { + reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(out_dma)); + ipu_idmac_write(ipu, reg | idma_mask(out_dma), IDMAC_CHA_EN(out_dma)); + } + + if ((ipu->sec_chan_en[IPU_CHAN_ID(channel)]) && + ((channel == MEM_PP_MEM) || (channel == MEM_PRP_VF_MEM) || + (channel == MEM_VDI_PRP_VF_MEM))) { + sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER); + reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(sec_dma)); + ipu_idmac_write(ipu, reg | idma_mask(sec_dma), IDMAC_CHA_EN(sec_dma)); + } + if ((ipu->thrd_chan_en[IPU_CHAN_ID(channel)]) && + ((channel == MEM_PP_MEM) || (channel == MEM_PRP_VF_MEM))) { + thrd_dma = channel_2_dma(channel, IPU_ALPHA_IN_BUFFER); + reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(thrd_dma)); + ipu_idmac_write(ipu, reg | idma_mask(thrd_dma), IDMAC_CHA_EN(thrd_dma)); + + sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER); + reg = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA); + ipu_idmac_write(ipu, reg | idma_mask(sec_dma), IDMAC_SEP_ALPHA); + } else if ((ipu->thrd_chan_en[IPU_CHAN_ID(channel)]) && + ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC))) { + thrd_dma = channel_2_dma(channel, IPU_ALPHA_IN_BUFFER); + reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(thrd_dma)); + ipu_idmac_write(ipu, reg | idma_mask(thrd_dma), IDMAC_CHA_EN(thrd_dma)); + reg = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA); + ipu_idmac_write(ipu, reg | idma_mask(in_dma), IDMAC_SEP_ALPHA); + } + + if ((channel == MEM_DC_SYNC) || (channel == MEM_BG_SYNC) || + (channel == MEM_FG_SYNC)) { + reg = ipu_idmac_read(ipu, IDMAC_WM_EN(in_dma)); + ipu_idmac_write(ipu, reg | idma_mask(in_dma), IDMAC_WM_EN(in_dma)); + + _ipu_dp_dc_enable(ipu, channel); + } + + if (_ipu_is_ic_chan(in_dma) || _ipu_is_ic_chan(out_dma) || + _ipu_is_irt_chan(in_dma) || _ipu_is_irt_chan(out_dma)) + _ipu_ic_enable_task(ipu, channel); + + ipu->channel_enable_mask |= 1L << IPU_CHAN_ID(channel); + + _ipu_unlock(ipu, &lock_flags); + + return 0; +} +EXPORT_SYMBOL(ipu_enable_channel); + +/*! + * This function check buffer ready for a logical channel. + * + * @param ipu ipu handler + * @param channel Input parameter for the logical channel ID. + * + * @param type Input parameter which buffer to clear. + * + * @param bufNum Input parameter for which buffer number clear + * ready state. + * + */ +int32_t ipu_check_buffer_ready(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type, + uint32_t bufNum) +{ + uint32_t dma_chan = channel_2_dma(channel, type); + uint32_t reg; + + if (dma_chan == IDMA_CHAN_INVALID) + return -EINVAL; + + if (bufNum == 0) + reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(dma_chan)); + else if (bufNum == 1) + reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(dma_chan)); + else + reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(dma_chan)); + + if (reg & idma_mask(dma_chan)) + return 1; + else + return 0; +} +EXPORT_SYMBOL(ipu_check_buffer_ready); + +/*! + * This function clear buffer ready for a logical channel. + * + * @param ipu ipu handler + * @param channel Input parameter for the logical channel ID. + * + * @param type Input parameter which buffer to clear. + * + * @param bufNum Input parameter for which buffer number clear + * ready state. + * + */ +void _ipu_clear_buffer_ready(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type, + uint32_t bufNum) +{ + uint32_t dma_ch = channel_2_dma(channel, type); + + if (!idma_is_valid(dma_ch)) + return; + + ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */ + if (bufNum == 0) + ipu_cm_write(ipu, idma_mask(dma_ch), + IPU_CHA_BUF0_RDY(dma_ch)); + else if (bufNum == 1) + ipu_cm_write(ipu, idma_mask(dma_ch), + IPU_CHA_BUF1_RDY(dma_ch)); + else + ipu_cm_write(ipu, idma_mask(dma_ch), + IPU_CHA_BUF2_RDY(dma_ch)); + ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ +} + +void ipu_clear_buffer_ready(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type, + uint32_t bufNum) +{ + unsigned long lock_flags; + _ipu_lock(ipu, &lock_flags); + _ipu_clear_buffer_ready(ipu, channel, type, bufNum); + _ipu_unlock(ipu, &lock_flags); +} +EXPORT_SYMBOL(ipu_clear_buffer_ready); + +/*! + * This function disables a logical channel. + * + * @param ipu ipu handler + * @param channel Input parameter for the logical channel ID. + * + * @param wait_for_stop Flag to set whether to wait for channel end + * of frame or return immediately. + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int32_t ipu_disable_channel(struct ipu_soc *ipu, ipu_channel_t channel, bool wait_for_stop) +{ + uint32_t reg; + uint32_t in_dma; + uint32_t out_dma; + uint32_t sec_dma = NO_DMA; + uint32_t thrd_dma = NO_DMA; + uint16_t fg_pos_x, fg_pos_y; + unsigned long lock_flags; + + _ipu_lock(ipu, &lock_flags); + + if ((ipu->channel_enable_mask & (1L << IPU_CHAN_ID(channel))) == 0) { + dev_err(ipu->dev, "Channel already disabled %d\n", + IPU_CHAN_ID(channel)); + _ipu_unlock(ipu, &lock_flags); + return -EACCES; + } + + /* Get input and output dma channels */ + out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER); + in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER); + + if ((idma_is_valid(in_dma) && + !idma_is_set(ipu, IDMAC_CHA_EN, in_dma)) + && (idma_is_valid(out_dma) && + !idma_is_set(ipu, IDMAC_CHA_EN, out_dma))) { + _ipu_unlock(ipu, &lock_flags); + return -EINVAL; + } + + if (ipu->sec_chan_en[IPU_CHAN_ID(channel)]) + sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER); + if (ipu->thrd_chan_en[IPU_CHAN_ID(channel)]) { + sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER); + thrd_dma = channel_2_dma(channel, IPU_ALPHA_IN_BUFFER); + } + + _ipu_unlock(ipu, &lock_flags); + if ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC) || + (channel == MEM_DC_SYNC)) { + if (channel == MEM_FG_SYNC) { + _ipu_disp_get_window_pos(ipu, channel, &fg_pos_x, &fg_pos_y); + _ipu_disp_set_window_pos(ipu, channel, 0, 0); + } + + _ipu_dp_dc_disable(ipu, channel, false); + + /* + * wait for BG channel EOF then disable FG-IDMAC, + * it avoid FG NFB4EOF error. + */ + if (channel == MEM_FG_SYNC) { + int timeout = 50; + + ipu_cm_write(ipu, IPUIRQ_2_MASK(IPU_IRQ_BG_SYNC_EOF), + IPUIRQ_2_STATREG(IPU_IRQ_BG_SYNC_EOF)); + while ((ipu_cm_read(ipu, 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(ipu->dev, "warning: wait for bg sync eof timeout\n"); + break; + } + } + } + } else if (wait_for_stop) { + while (idma_is_set(ipu, IDMAC_CHA_BUSY, in_dma) || + idma_is_set(ipu, IDMAC_CHA_BUSY, out_dma) || + (ipu->sec_chan_en[IPU_CHAN_ID(channel)] && + idma_is_set(ipu, IDMAC_CHA_BUSY, sec_dma)) || + (ipu->thrd_chan_en[IPU_CHAN_ID(channel)] && + idma_is_set(ipu, IDMAC_CHA_BUSY, thrd_dma))) { + uint32_t irq = 0xffffffff; + DECLARE_COMPLETION_ONSTACK(disable_comp); + int timeout = 50; + + if (idma_is_set(ipu, IDMAC_CHA_BUSY, out_dma)) + irq = out_dma; + if (ipu->sec_chan_en[IPU_CHAN_ID(channel)] && + idma_is_set(ipu, IDMAC_CHA_BUSY, sec_dma)) + irq = sec_dma; + if (ipu->thrd_chan_en[IPU_CHAN_ID(channel)] && + idma_is_set(ipu, IDMAC_CHA_BUSY, thrd_dma)) + irq = thrd_dma; + if (idma_is_set(ipu, IDMAC_CHA_BUSY, in_dma)) + irq = in_dma; + + if (irq == 0xffffffff) { + dev_err(ipu->dev, "warning: no channel busy, break\n"); + break; + } + + dev_err(ipu->dev, "warning: channel %d busy, need wait\n", irq); + + ipu_cm_write(ipu, IPUIRQ_2_MASK(irq), + IPUIRQ_2_STATREG(irq)); + while ((ipu_cm_read(ipu, IPUIRQ_2_STATREG(irq)) & + IPUIRQ_2_MASK(irq)) == 0) { + msleep(10); + timeout -= 10; + if (timeout <= 0) { + ipu_dump_registers(ipu); + dev_err(ipu->dev, "warning: disable ipu dma channel %d during its busy state\n", irq); + break; + } + } + + } + } + _ipu_lock(ipu, &lock_flags); + + if ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC) || + (channel == MEM_DC_SYNC)) { + reg = ipu_idmac_read(ipu, IDMAC_WM_EN(in_dma)); + ipu_idmac_write(ipu, reg & ~idma_mask(in_dma), IDMAC_WM_EN(in_dma)); + } + + /* Disable IC task */ + if (_ipu_is_ic_chan(in_dma) || _ipu_is_ic_chan(out_dma) || + _ipu_is_irt_chan(in_dma) || _ipu_is_irt_chan(out_dma)) + _ipu_ic_disable_task(ipu, channel); + + /* Disable DMA channel(s) */ + if (idma_is_valid(in_dma)) { + reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(in_dma)); + ipu_idmac_write(ipu, reg & ~idma_mask(in_dma), IDMAC_CHA_EN(in_dma)); + ipu_cm_write(ipu, idma_mask(in_dma), IPU_CHA_CUR_BUF(in_dma)); + ipu_cm_write(ipu, tri_cur_buf_mask(in_dma), + IPU_CHA_TRIPLE_CUR_BUF(in_dma)); + } + if (idma_is_valid(out_dma)) { + reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(out_dma)); + ipu_idmac_write(ipu, reg & ~idma_mask(out_dma), IDMAC_CHA_EN(out_dma)); + ipu_cm_write(ipu, idma_mask(out_dma), IPU_CHA_CUR_BUF(out_dma)); + ipu_cm_write(ipu, tri_cur_buf_mask(out_dma), + IPU_CHA_TRIPLE_CUR_BUF(out_dma)); + } + if (ipu->sec_chan_en[IPU_CHAN_ID(channel)] && idma_is_valid(sec_dma)) { + reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(sec_dma)); + ipu_idmac_write(ipu, reg & ~idma_mask(sec_dma), IDMAC_CHA_EN(sec_dma)); + ipu_cm_write(ipu, idma_mask(sec_dma), IPU_CHA_CUR_BUF(sec_dma)); + } + if (ipu->thrd_chan_en[IPU_CHAN_ID(channel)] && idma_is_valid(thrd_dma)) { + reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(thrd_dma)); + ipu_idmac_write(ipu, reg & ~idma_mask(thrd_dma), IDMAC_CHA_EN(thrd_dma)); + if (channel == MEM_BG_SYNC || channel == MEM_FG_SYNC) { + reg = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA); + ipu_idmac_write(ipu, reg & ~idma_mask(in_dma), IDMAC_SEP_ALPHA); + } else { + reg = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA); + ipu_idmac_write(ipu, reg & ~idma_mask(sec_dma), IDMAC_SEP_ALPHA); + } + ipu_cm_write(ipu, idma_mask(thrd_dma), IPU_CHA_CUR_BUF(thrd_dma)); + } + + ipu->channel_enable_mask &= ~(1L << IPU_CHAN_ID(channel)); + + if (channel == MEM_FG_SYNC) + _ipu_disp_set_window_pos(ipu, channel, fg_pos_x, fg_pos_y); + + /* Set channel buffers NOT to be ready */ + if (idma_is_valid(in_dma)) { + _ipu_clear_buffer_ready(ipu, channel, IPU_VIDEO_IN_BUFFER, 0); + _ipu_clear_buffer_ready(ipu, channel, IPU_VIDEO_IN_BUFFER, 1); + _ipu_clear_buffer_ready(ipu, channel, IPU_VIDEO_IN_BUFFER, 2); + } + if (idma_is_valid(out_dma)) { + _ipu_clear_buffer_ready(ipu, channel, IPU_OUTPUT_BUFFER, 0); + _ipu_clear_buffer_ready(ipu, channel, IPU_OUTPUT_BUFFER, 1); + } + if (ipu->sec_chan_en[IPU_CHAN_ID(channel)] && idma_is_valid(sec_dma)) { + _ipu_clear_buffer_ready(ipu, channel, IPU_GRAPH_IN_BUFFER, 0); + _ipu_clear_buffer_ready(ipu, channel, IPU_GRAPH_IN_BUFFER, 1); + } + if (ipu->thrd_chan_en[IPU_CHAN_ID(channel)] && idma_is_valid(thrd_dma)) { + _ipu_clear_buffer_ready(ipu, channel, IPU_ALPHA_IN_BUFFER, 0); + _ipu_clear_buffer_ready(ipu, channel, IPU_ALPHA_IN_BUFFER, 1); + } + + _ipu_unlock(ipu, &lock_flags); + + return 0; +} +EXPORT_SYMBOL(ipu_disable_channel); + +/*! + * This function enables CSI. + * + * @param ipu ipu handler + * @param csi csi num 0 or 1 + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int32_t ipu_enable_csi(struct ipu_soc *ipu, uint32_t csi) +{ + uint32_t reg; + unsigned long lock_flags; + + if (csi > 1) { + dev_err(ipu->dev, "Wrong csi num_%d\n", csi); + return -EINVAL; + } + + _ipu_lock(ipu, &lock_flags); + ipu->csi_use_count[csi]++; + + if (ipu->csi_use_count[csi] == 1) { + reg = ipu_cm_read(ipu, IPU_CONF); + if (csi == 0) + ipu_cm_write(ipu, reg | IPU_CONF_CSI0_EN, IPU_CONF); + else + ipu_cm_write(ipu, reg | IPU_CONF_CSI1_EN, IPU_CONF); + } + _ipu_unlock(ipu, &lock_flags); + return 0; +} +EXPORT_SYMBOL(ipu_enable_csi); + +/*! + * This function disables CSI. + * + * @param ipu ipu handler + * @param csi csi num 0 or 1 + * + * @return This function returns 0 on success or negative error code on + * fail. + */ +int32_t ipu_disable_csi(struct ipu_soc *ipu, uint32_t csi) +{ + uint32_t reg; + unsigned long lock_flags; + + if (csi > 1) { + dev_err(ipu->dev, "Wrong csi num_%d\n", csi); + return -EINVAL; + } + + _ipu_lock(ipu, &lock_flags); + ipu->csi_use_count[csi]--; + if (ipu->csi_use_count[csi] == 0) { + reg = ipu_cm_read(ipu, IPU_CONF); + if (csi == 0) + ipu_cm_write(ipu, reg & ~IPU_CONF_CSI0_EN, IPU_CONF); + else + ipu_cm_write(ipu, reg & ~IPU_CONF_CSI1_EN, IPU_CONF); + } + _ipu_unlock(ipu, &lock_flags); + return 0; +} +EXPORT_SYMBOL(ipu_disable_csi); + +static irqreturn_t ipu_irq_handler(int irq, void *desc) +{ + struct ipu_soc *ipu = desc; + int i; + uint32_t line; + irqreturn_t result = IRQ_NONE; + uint32_t int_stat; + const int err_reg[] = { 5, 6, 9, 10, 0 }; + const int int_reg[] = { 1, 2, 3, 4, 11, 12, 13, 14, 15, 0 }; + + for (i = 0;; i++) { + if (err_reg[i] == 0) + break; + int_stat = ipu_cm_read(ipu, IPU_INT_STAT(err_reg[i])); + int_stat &= ipu_cm_read(ipu, IPU_INT_CTRL(err_reg[i])); + if (int_stat) { + ipu_cm_write(ipu, int_stat, IPU_INT_STAT(err_reg[i])); + dev_err(ipu->dev, + "IPU Error - IPU_INT_STAT_%d = 0x%08X\n", + err_reg[i], int_stat); + /* Disable interrupts so we only get error once */ + int_stat = + ipu_cm_read(ipu, IPU_INT_CTRL(err_reg[i])) & ~int_stat; + ipu_cm_write(ipu, int_stat, IPU_INT_CTRL(err_reg[i])); + } + } + + for (i = 0;; i++) { + if (int_reg[i] == 0) + break; + int_stat = ipu_cm_read(ipu, IPU_INT_STAT(int_reg[i])); + int_stat &= ipu_cm_read(ipu, IPU_INT_CTRL(int_reg[i])); + ipu_cm_write(ipu, int_stat, IPU_INT_STAT(int_reg[i])); + while ((line = ffs(int_stat)) != 0) { + line--; + int_stat &= ~(1UL << line); + line += (int_reg[i] - 1) * 32; + result |= + ipu->irq_list[line].handler(line, + ipu->irq_list[line]. + dev_id); + } + } + + return result; +} + +/*! + * This function enables the interrupt for the specified interrupt line. + * The interrupt lines are defined in \b ipu_irq_line enum. + * + * @param ipu ipu handler + * @param irq Interrupt line to enable interrupt for. + * + */ +void ipu_enable_irq(struct ipu_soc *ipu, uint32_t irq) +{ + uint32_t reg; + unsigned long lock_flags; + + _ipu_get(ipu); + + spin_lock_irqsave(&ipu->spin_lock, lock_flags); + + reg = ipu_cm_read(ipu, IPUIRQ_2_CTRLREG(irq)); + reg |= IPUIRQ_2_MASK(irq); + ipu_cm_write(ipu, reg, IPUIRQ_2_CTRLREG(irq)); + + spin_unlock_irqrestore(&ipu->spin_lock, lock_flags); + + _ipu_put(ipu); +} +EXPORT_SYMBOL(ipu_enable_irq); + +/*! + * This function disables the interrupt for the specified interrupt line. + * The interrupt lines are defined in \b ipu_irq_line enum. + * + * @param ipu ipu handler + * @param irq Interrupt line to disable interrupt for. + * + */ +void ipu_disable_irq(struct ipu_soc *ipu, uint32_t irq) +{ + uint32_t reg; + unsigned long lock_flags; + + _ipu_get(ipu); + + spin_lock_irqsave(&ipu->spin_lock, lock_flags); + + reg = ipu_cm_read(ipu, IPUIRQ_2_CTRLREG(irq)); + reg &= ~IPUIRQ_2_MASK(irq); + ipu_cm_write(ipu, reg, IPUIRQ_2_CTRLREG(irq)); + + spin_unlock_irqrestore(&ipu->spin_lock, lock_flags); + + _ipu_put(ipu); +} +EXPORT_SYMBOL(ipu_disable_irq); + +/*! + * This function clears the interrupt for the specified interrupt line. + * The interrupt lines are defined in \b ipu_irq_line enum. + * + * @param ipu ipu handler + * @param irq Interrupt line to clear interrupt for. + * + */ +void ipu_clear_irq(struct ipu_soc *ipu, uint32_t irq) +{ + _ipu_get(ipu); + + ipu_cm_write(ipu, IPUIRQ_2_MASK(irq), IPUIRQ_2_STATREG(irq)); + + _ipu_put(ipu); +} +EXPORT_SYMBOL(ipu_clear_irq); + +/*! + * This function returns the current interrupt status for the specified + * interrupt line. The interrupt lines are defined in \b ipu_irq_line enum. + * + * @param ipu ipu 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 ipu_get_irq_status(struct ipu_soc *ipu, uint32_t irq) +{ + uint32_t reg; + + _ipu_get(ipu); + + reg = ipu_cm_read(ipu, IPUIRQ_2_STATREG(irq)); + + _ipu_put(ipu); + + if (reg & IPUIRQ_2_MASK(irq)) + return true; + else + return false; +} +EXPORT_SYMBOL(ipu_get_irq_status); + +/*! + * This function registers an interrupt handler function for the specified + * interrupt line. The interrupt lines are defined in \b ipu_irq_line enum. + * + * @param ipu ipu 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 ipu_request_irq(struct ipu_soc *ipu, 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 >= IPU_IRQ_COUNT); + + spin_lock_irqsave(&ipu->spin_lock, lock_flags); + + if (ipu->irq_list[irq].handler != NULL) { + dev_err(ipu->dev, + "handler already installed on irq %d\n", irq); + spin_unlock_irqrestore(&ipu->spin_lock, lock_flags); + return -EINVAL; + } + + ipu->irq_list[irq].handler = handler; + ipu->irq_list[irq].flags = irq_flags; + ipu->irq_list[irq].dev_id = dev_id; + ipu->irq_list[irq].name = devname; + + spin_unlock_irqrestore(&ipu->spin_lock, lock_flags); + + ipu_enable_irq(ipu, irq); /* enable the interrupt */ + + return 0; +} +EXPORT_SYMBOL(ipu_request_irq); + +/*! + * This function unregisters an interrupt handler for the specified interrupt + * line. The interrupt lines are defined in \b ipu_irq_line enum. + * + * @param ipu ipu 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 + * ipu_request_irq(). + * + */ +void ipu_free_irq(struct ipu_soc *ipu, uint32_t irq, void *dev_id) +{ + ipu_disable_irq(ipu, irq); /* disable the interrupt */ + + if (ipu->irq_list[irq].dev_id == dev_id) + ipu->irq_list[irq].handler = NULL; +} +EXPORT_SYMBOL(ipu_free_irq); + +uint32_t ipu_get_cur_buffer_idx(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type) +{ + uint32_t reg, dma_chan; + + dma_chan = channel_2_dma(channel, type); + if (!idma_is_valid(dma_chan)) + return -EINVAL; + + reg = ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(dma_chan)); + if ((reg & idma_mask(dma_chan)) && _ipu_is_trb_chan(dma_chan)) { + reg = ipu_cm_read(ipu, IPU_CHA_TRIPLE_CUR_BUF(dma_chan)); + return (reg & tri_cur_buf_mask(dma_chan)) >> + tri_cur_buf_shift(dma_chan); + } else { + reg = ipu_cm_read(ipu, IPU_CHA_CUR_BUF(dma_chan)); + if (reg & idma_mask(dma_chan)) + return 1; + else + return 0; + } +} +EXPORT_SYMBOL(ipu_get_cur_buffer_idx); + +uint32_t _ipu_channel_status(struct ipu_soc *ipu, ipu_channel_t channel) +{ + uint32_t stat = 0; + uint32_t task_stat_reg = ipu_cm_read(ipu, IPU_PROC_TASK_STAT); + + switch (channel) { + case MEM_PRP_VF_MEM: + stat = (task_stat_reg & TSTAT_VF_MASK) >> TSTAT_VF_OFFSET; + break; + case MEM_VDI_PRP_VF_MEM: + stat = (task_stat_reg & TSTAT_VF_MASK) >> TSTAT_VF_OFFSET; + break; + case MEM_ROT_VF_MEM: + stat = + (task_stat_reg & TSTAT_VF_ROT_MASK) >> TSTAT_VF_ROT_OFFSET; + break; + case MEM_PRP_ENC_MEM: + stat = (task_stat_reg & TSTAT_ENC_MASK) >> TSTAT_ENC_OFFSET; + break; + case MEM_ROT_ENC_MEM: + stat = + (task_stat_reg & TSTAT_ENC_ROT_MASK) >> + TSTAT_ENC_ROT_OFFSET; + break; + case MEM_PP_MEM: + stat = (task_stat_reg & TSTAT_PP_MASK) >> TSTAT_PP_OFFSET; + break; + case MEM_ROT_PP_MEM: + stat = + (task_stat_reg & TSTAT_PP_ROT_MASK) >> TSTAT_PP_ROT_OFFSET; + break; + + default: + stat = TASK_STAT_IDLE; + break; + } + return stat; +} + +int32_t ipu_swap_channel(struct ipu_soc *ipu, ipu_channel_t from_ch, ipu_channel_t to_ch) +{ + uint32_t reg; + unsigned long lock_flags; + + int from_dma = channel_2_dma(from_ch, IPU_INPUT_BUFFER); + int to_dma = channel_2_dma(to_ch, IPU_INPUT_BUFFER); + + _ipu_lock(ipu, &lock_flags); + + /* enable target channel */ + reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(to_dma)); + ipu_idmac_write(ipu, reg | idma_mask(to_dma), IDMAC_CHA_EN(to_dma)); + + ipu->channel_enable_mask |= 1L << IPU_CHAN_ID(to_ch); + + /* switch dp dc */ + _ipu_unlock(ipu, &lock_flags); + _ipu_dp_dc_disable(ipu, from_ch, true); + _ipu_lock(ipu, &lock_flags); + + /* disable source channel */ + reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(from_dma)); + ipu_idmac_write(ipu, reg & ~idma_mask(from_dma), IDMAC_CHA_EN(from_dma)); + ipu_cm_write(ipu, idma_mask(from_dma), IPU_CHA_CUR_BUF(from_dma)); + ipu_cm_write(ipu, tri_cur_buf_mask(from_dma), + IPU_CHA_TRIPLE_CUR_BUF(from_dma)); + + ipu->channel_enable_mask &= ~(1L << IPU_CHAN_ID(from_ch)); + + _ipu_clear_buffer_ready(ipu, from_ch, IPU_VIDEO_IN_BUFFER, 0); + _ipu_clear_buffer_ready(ipu, from_ch, IPU_VIDEO_IN_BUFFER, 1); + _ipu_clear_buffer_ready(ipu, from_ch, IPU_VIDEO_IN_BUFFER, 2); + + _ipu_unlock(ipu, &lock_flags); + + return 0; +} +EXPORT_SYMBOL(ipu_swap_channel); + +uint32_t bytes_per_pixel(uint32_t fmt) +{ + switch (fmt) { + case IPU_PIX_FMT_GENERIC: /*generic data */ + case IPU_PIX_FMT_RGB332: + case IPU_PIX_FMT_YUV420P: + case IPU_PIX_FMT_YVU420P: + case IPU_PIX_FMT_YUV422P: + return 1; + break; + case IPU_PIX_FMT_RGB565: + case IPU_PIX_FMT_YUYV: + case IPU_PIX_FMT_UYVY: + return 2; + break; + case IPU_PIX_FMT_BGR24: + case IPU_PIX_FMT_RGB24: + return 3; + break; + case IPU_PIX_FMT_GENERIC_32: /*generic data */ + case IPU_PIX_FMT_BGR32: + case IPU_PIX_FMT_BGRA32: + case IPU_PIX_FMT_RGB32: + case IPU_PIX_FMT_RGBA32: + case IPU_PIX_FMT_ABGR32: + return 4; + break; + default: + return 1; + break; + } + return 0; +} +EXPORT_SYMBOL(bytes_per_pixel); + +ipu_color_space_t format_to_colorspace(uint32_t fmt) +{ + switch (fmt) { + case IPU_PIX_FMT_RGB666: + case IPU_PIX_FMT_RGB565: + case IPU_PIX_FMT_BGR24: + case IPU_PIX_FMT_RGB24: + case IPU_PIX_FMT_GBR24: + case IPU_PIX_FMT_BGR32: + case IPU_PIX_FMT_BGRA32: + case IPU_PIX_FMT_RGB32: + case IPU_PIX_FMT_RGBA32: + case IPU_PIX_FMT_ABGR32: + case IPU_PIX_FMT_LVDS666: + case IPU_PIX_FMT_LVDS888: + return RGB; + break; + + default: + return YCbCr; + break; + } + return RGB; +} + +bool ipu_pixel_format_has_alpha(uint32_t fmt) +{ + switch (fmt) { + case IPU_PIX_FMT_RGBA32: + case IPU_PIX_FMT_BGRA32: + case IPU_PIX_FMT_ABGR32: + return true; + break; + default: + return false; + break; + } + return false; +} + +static int ipu_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct imx_ipuv3_platform_data *plat_data = pdev->dev.platform_data; + struct ipu_soc *ipu = platform_get_drvdata(pdev); + + if (ipu->ipu_use_count) { + /* save and disable enabled channels*/ + ipu->idma_enable_reg[0] = ipu_idmac_read(ipu, IDMAC_CHA_EN(0)); + ipu->idma_enable_reg[1] = ipu_idmac_read(ipu, IDMAC_CHA_EN(32)); + while ((ipu_idmac_read(ipu, IDMAC_CHA_BUSY(0)) + & ipu->idma_enable_reg[0]) + || (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(32)) + & ipu->idma_enable_reg[1])) { + /* disable channel not busy already */ + uint32_t chan_should_disable, timeout = 1000, time = 0; + + chan_should_disable = + ipu_idmac_read(ipu, IDMAC_CHA_BUSY(0)) + ^ ipu->idma_enable_reg[0]; + ipu_idmac_write(ipu, (~chan_should_disable) & + ipu->idma_enable_reg[0], IDMAC_CHA_EN(0)); + chan_should_disable = + ipu_idmac_read(ipu, IDMAC_CHA_BUSY(1)) + ^ ipu->idma_enable_reg[1]; + ipu_idmac_write(ipu, (~chan_should_disable) & + ipu->idma_enable_reg[1], IDMAC_CHA_EN(32)); + msleep(2); + time += 2; + if (time >= timeout) + return -1; + } + ipu_idmac_write(ipu, 0, IDMAC_CHA_EN(0)); + ipu_idmac_write(ipu, 0, IDMAC_CHA_EN(32)); + + /* save double buffer select regs */ + ipu->cha_db_mode_reg[0] = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0)); + ipu->cha_db_mode_reg[1] = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32)); + ipu->cha_db_mode_reg[2] = + ipu_cm_read(ipu, IPU_ALT_CHA_DB_MODE_SEL(0)); + ipu->cha_db_mode_reg[3] = + ipu_cm_read(ipu, IPU_ALT_CHA_DB_MODE_SEL(32)); + + /* save triple buffer select regs */ + ipu->cha_trb_mode_reg[0] = ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(0)); + ipu->cha_trb_mode_reg[1] = ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(32)); + + /* save idamc sub addr regs */ + ipu->idma_sub_addr_reg[0] = ipu_idmac_read(ipu, IDMAC_SUB_ADDR_0); + ipu->idma_sub_addr_reg[1] = ipu_idmac_read(ipu, IDMAC_SUB_ADDR_1); + ipu->idma_sub_addr_reg[2] = ipu_idmac_read(ipu, IDMAC_SUB_ADDR_2); + ipu->idma_sub_addr_reg[3] = ipu_idmac_read(ipu, IDMAC_SUB_ADDR_3); + ipu->idma_sub_addr_reg[4] = ipu_idmac_read(ipu, IDMAC_SUB_ADDR_4); + + /* save sub-modules status and disable all */ + ipu->ic_conf_reg = ipu_ic_read(ipu, IC_CONF); + ipu_ic_write(ipu, 0, IC_CONF); + ipu->ipu_conf_reg = ipu_cm_read(ipu, IPU_CONF); + ipu_cm_write(ipu, 0, IPU_CONF); + + /* save buf ready regs */ + ipu->buf_ready_reg[0] = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(0)); + ipu->buf_ready_reg[1] = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(32)); + ipu->buf_ready_reg[2] = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(0)); + ipu->buf_ready_reg[3] = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(32)); + ipu->buf_ready_reg[4] = ipu_cm_read(ipu, IPU_ALT_CHA_BUF0_RDY(0)); + ipu->buf_ready_reg[5] = ipu_cm_read(ipu, IPU_ALT_CHA_BUF0_RDY(32)); + ipu->buf_ready_reg[6] = ipu_cm_read(ipu, IPU_ALT_CHA_BUF1_RDY(0)); + ipu->buf_ready_reg[7] = ipu_cm_read(ipu, IPU_ALT_CHA_BUF1_RDY(32)); + ipu->buf_ready_reg[8] = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(0)); + ipu->buf_ready_reg[9] = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(32)); + } + + if (plat_data->pg) + plat_data->pg(1); + + return 0; +} + +static int ipu_resume(struct platform_device *pdev) +{ + struct imx_ipuv3_platform_data *plat_data = pdev->dev.platform_data; + struct ipu_soc *ipu = platform_get_drvdata(pdev); + + if (plat_data->pg) + plat_data->pg(0); + + if (ipu->ipu_use_count) { + + /* restore buf ready regs */ + ipu_cm_write(ipu, ipu->buf_ready_reg[0], IPU_CHA_BUF0_RDY(0)); + ipu_cm_write(ipu, ipu->buf_ready_reg[1], IPU_CHA_BUF0_RDY(32)); + ipu_cm_write(ipu, ipu->buf_ready_reg[2], IPU_CHA_BUF1_RDY(0)); + ipu_cm_write(ipu, ipu->buf_ready_reg[3], IPU_CHA_BUF1_RDY(32)); + ipu_cm_write(ipu, ipu->buf_ready_reg[4], IPU_ALT_CHA_BUF0_RDY(0)); + ipu_cm_write(ipu, ipu->buf_ready_reg[5], IPU_ALT_CHA_BUF0_RDY(32)); + ipu_cm_write(ipu, ipu->buf_ready_reg[6], IPU_ALT_CHA_BUF1_RDY(0)); + ipu_cm_write(ipu, ipu->buf_ready_reg[7], IPU_ALT_CHA_BUF1_RDY(32)); + ipu_cm_write(ipu, ipu->buf_ready_reg[8], IPU_CHA_BUF2_RDY(0)); + ipu_cm_write(ipu, ipu->buf_ready_reg[9], IPU_CHA_BUF2_RDY(32)); + + /* re-enable sub-modules*/ + ipu_cm_write(ipu, ipu->ipu_conf_reg, IPU_CONF); + ipu_ic_write(ipu, ipu->ic_conf_reg, IC_CONF); + + /* restore double buffer select regs */ + ipu_cm_write(ipu, ipu->cha_db_mode_reg[0], IPU_CHA_DB_MODE_SEL(0)); + ipu_cm_write(ipu, ipu->cha_db_mode_reg[1], IPU_CHA_DB_MODE_SEL(32)); + ipu_cm_write(ipu, ipu->cha_db_mode_reg[2], + IPU_ALT_CHA_DB_MODE_SEL(0)); + ipu_cm_write(ipu, ipu->cha_db_mode_reg[3], + IPU_ALT_CHA_DB_MODE_SEL(32)); + + /* restore triple buffer select regs */ + ipu_cm_write(ipu, ipu->cha_trb_mode_reg[0], IPU_CHA_TRB_MODE_SEL(0)); + ipu_cm_write(ipu, ipu->cha_trb_mode_reg[1], IPU_CHA_TRB_MODE_SEL(32)); + + /* restore idamc sub addr regs */ + ipu_idmac_write(ipu, ipu->idma_sub_addr_reg[0], IDMAC_SUB_ADDR_0); + ipu_idmac_write(ipu, ipu->idma_sub_addr_reg[1], IDMAC_SUB_ADDR_1); + ipu_idmac_write(ipu, ipu->idma_sub_addr_reg[2], IDMAC_SUB_ADDR_2); + ipu_idmac_write(ipu, ipu->idma_sub_addr_reg[3], IDMAC_SUB_ADDR_3); + ipu_idmac_write(ipu, ipu->idma_sub_addr_reg[4], IDMAC_SUB_ADDR_4); + + /* restart idma channel*/ + ipu_idmac_write(ipu, ipu->idma_enable_reg[0], IDMAC_CHA_EN(0)); + ipu_idmac_write(ipu, ipu->idma_enable_reg[1], IDMAC_CHA_EN(32)); + } else { + _ipu_get(ipu); + _ipu_dmfc_init(ipu, dmfc_type_setup, 1); + _ipu_init_dc_mappings(ipu); + /* Set sync refresh channels as high priority */ + ipu_idmac_write(ipu, 0x18800001L, IDMAC_CHA_PRI(0)); + _ipu_put(ipu); + } + + return 0; +} + +/*! + * This structure contains pointers to the power management callback functions. + */ +static struct platform_driver mxcipu_driver = { + .driver = { + .name = "imx-ipuv3", + }, + .probe = ipu_probe, + .remove = ipu_remove, + .suspend = ipu_suspend, + .resume = ipu_resume, +}; + +int32_t __init ipu_gen_init(void) +{ + int32_t ret; + + ret = platform_driver_register(&mxcipu_driver); + return 0; +} + +subsys_initcall(ipu_gen_init); + +static void __exit ipu_gen_uninit(void) +{ + platform_driver_unregister(&mxcipu_driver); +} + +module_exit(ipu_gen_uninit); diff --git a/drivers/mxc/ipu3/ipu_device.c b/drivers/mxc/ipu3/ipu_device.c new file mode 100644 index 00000000000..c00f0df9859 --- /dev/null +++ b/drivers/mxc/ipu3/ipu_device.c @@ -0,0 +1,2109 @@ +/* + * 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 + */ + +/*! + * @file ipu_device.c + * + * @brief This file contains the IPUv3 driver device interface and fops functions. + * + * @ingroup IPU + */ +#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/clk.h> +#include <linux/poll.h> +#include <linux/sched.h> +#include <linux/time.h> +#include <linux/wait.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> +#include <linux/io.h> +#include <linux/ipu.h> +#include <linux/kthread.h> +#include <asm/cacheflush.h> + +#include "ipu_prv.h" +#include "ipu_regs.h" +#include "ipu_param_mem.h" + +/* Strucutures and variables for exporting MXC IPU as device*/ +typedef enum { + RGB_CS, + YUV_CS, + NULL_CS +} cs_t; + +typedef enum { + STATE_OK = 0, + STATE_NO_IPU, + STATE_NO_IRQ, + STATE_IRQ_FAIL, + STATE_IRQ_TIMEOUT, + STATE_INIT_CHAN_FAIL, + STATE_LINK_CHAN_FAIL, + STATE_INIT_CHAN_BUF_FAIL, +} ipu_state_t; + +struct ipu_state_msg { + int state; + char *msg; +} state_msg[] = { + {STATE_OK, "ok"}, + {STATE_NO_IPU, "no ipu found"}, + {STATE_NO_IRQ, "no irq found for task"}, + {STATE_IRQ_FAIL, "request irq failed"}, + {STATE_IRQ_TIMEOUT, "wait for irq timeout"}, + {STATE_INIT_CHAN_FAIL, "ipu init channel fail"}, + {STATE_LINK_CHAN_FAIL, "ipu link channel fail"}, + {STATE_INIT_CHAN_BUF_FAIL, "ipu init channel buffer fail"}, +}; + +struct stripe_setting { + u32 iw; + u32 ih; + u32 ow; + u32 oh; + u32 outh_resize_ratio; + u32 outv_resize_ratio; + u32 i_left_pos; + u32 i_right_pos; + u32 i_top_pos; + u32 i_bottom_pos; + u32 o_left_pos; + u32 o_right_pos; + u32 o_top_pos; + u32 o_bottom_pos; +}; + +struct task_set { +#define NULL_MODE 0x0 +#define IC_MODE 0x1 +#define ROT_MODE 0x2 +#define VDI_MODE 0x4 + u8 mode; +#define IC_VF 0x1 +#define IC_PP 0x2 +#define ROT_VF 0x4 +#define ROT_PP 0x8 +#define VDI_VF 0x10 + u8 task; + + ipu_channel_t ic_chan; + ipu_channel_t rot_chan; + ipu_channel_t vdi_ic_p_chan; + ipu_channel_t vdi_ic_n_chan; + + u32 i_off; + u32 i_uoff; + u32 i_voff; + u32 istride; + + u32 ov_off; + u32 ov_uoff; + u32 ov_voff; + u32 ovstride; + + u32 ov_alpha_off; + u32 ov_alpha_stride; + + u32 o_off; + u32 o_uoff; + u32 o_voff; + u32 ostride; + + u32 r_fmt; + u32 r_width; + u32 r_height; + u32 r_stride; + dma_addr_t r_paddr; + +#define NO_SPLIT 0x0 +#define RL_SPLIT 0x1 +#define UD_SPLIT 0x2 +#define LEFT_STRIPE 0x1 +#define RIGHT_STRIPE 0x2 +#define UP_STRIPE 0x4 +#define DOWN_STRIPE 0x8 + u8 split_mode; + struct stripe_setting sp_setting; +}; + +struct ipu_split_task { + struct ipu_task task; + struct ipu_task_entry *parent_task; + struct task_struct *thread; + bool could_finish; + wait_queue_head_t waitq; + int ret; +}; + +struct ipu_task_entry { + struct ipu_input input; + struct ipu_output output; + + bool overlay_en; + struct ipu_overlay overlay; + + u8 priority; + u8 task_id; +#define DEF_TIMEOUT_MS 1000 + int timeout; + + struct list_head node; + struct device *dev; + struct task_set set; + struct completion comp; + ipu_state_t state; +}; + +struct ipu_alloc_list { + struct list_head list; + dma_addr_t phy_addr; + void *cpu_addr; + u32 size; +}; +LIST_HEAD(ipu_alloc_list); + +static int major; +static struct class *ipu_class; +static struct device *ipu_dev; + +int ipu_queue_sp_task(struct ipu_split_task *sp_task); + +static bool deinterlace_3_field(struct ipu_task_entry *t) +{ + return ((t->set.mode & VDI_MODE) && + (t->input.deinterlace.motion != HIGH_MOTION)); +} + +static u32 fmt_to_bpp(u32 pixelformat) +{ + u32 bpp; + + switch (pixelformat) { + case IPU_PIX_FMT_RGB565: + /*interleaved 422*/ + case IPU_PIX_FMT_YUYV: + case IPU_PIX_FMT_UYVY: + /*non-interleaved 422*/ + case IPU_PIX_FMT_YUV422P: + case IPU_PIX_FMT_YVU422P: + bpp = 16; + break; + case IPU_PIX_FMT_BGR24: + case IPU_PIX_FMT_RGB24: + case IPU_PIX_FMT_YUV444: + bpp = 24; + break; + case IPU_PIX_FMT_BGR32: + case IPU_PIX_FMT_BGRA32: + case IPU_PIX_FMT_RGB32: + case IPU_PIX_FMT_RGBA32: + case IPU_PIX_FMT_ABGR32: + bpp = 32; + break; + /*non-interleaved 420*/ + case IPU_PIX_FMT_YUV420P: + case IPU_PIX_FMT_YVU420P: + case IPU_PIX_FMT_YUV420P2: + case IPU_PIX_FMT_NV12: + bpp = 12; + break; + default: + bpp = 8; + break; + } + return bpp; +} + +static cs_t colorspaceofpixel(int fmt) +{ + switch (fmt) { + case IPU_PIX_FMT_RGB565: + case IPU_PIX_FMT_BGR24: + case IPU_PIX_FMT_RGB24: + case IPU_PIX_FMT_BGRA32: + case IPU_PIX_FMT_BGR32: + case IPU_PIX_FMT_RGBA32: + case IPU_PIX_FMT_RGB32: + case IPU_PIX_FMT_ABGR32: + return RGB_CS; + break; + case IPU_PIX_FMT_UYVY: + case IPU_PIX_FMT_YUYV: + case IPU_PIX_FMT_YUV420P2: + case IPU_PIX_FMT_YUV420P: + case IPU_PIX_FMT_YVU420P: + case IPU_PIX_FMT_YVU422P: + case IPU_PIX_FMT_YUV422P: + case IPU_PIX_FMT_YUV444: + case IPU_PIX_FMT_NV12: + return YUV_CS; + break; + default: + return NULL_CS; + } +} + +static int need_csc(int ifmt, int ofmt) +{ + cs_t ics, ocs; + + ics = colorspaceofpixel(ifmt); + ocs = colorspaceofpixel(ofmt); + + if ((ics == NULL_CS) || (ocs == NULL_CS)) + return -1; + else if (ics != ocs) + return 1; + + return 0; +} + +static int soc_max_in_width(void) +{ + return 4096; +} + +static int soc_max_in_height(void) +{ + return 4096; +} + +static int soc_max_out_width(void) +{ + /* mx51/mx53/mx6q is 1024*/ + return 1024; +} + +static int soc_max_out_height(void) +{ + /* mx51/mx53/mx6q is 1024*/ + return 1024; +} + +static int list_size(struct list_head *head) +{ + struct list_head *p, *n; + int size = 0; + + list_for_each_safe(p, n, head) + size++; + + return size; +} + +static int get_task_size(struct ipu_soc *ipu, int id) +{ + struct list_head *task_list; + + if (id == IPU_TASK_ID_VF) + task_list = &ipu->task_list[0]; + else if (id == IPU_TASK_ID_PP) + task_list = &ipu->task_list[1]; + else { + printk(KERN_ERR "query error task id\n"); + return -EINVAL; + } + + return list_size(task_list); +} + +static struct ipu_soc *most_free_ipu_task(struct ipu_task_entry *t) +{ + unsigned int task_num[2][2] = { + {0xffffffff, 0xffffffff}, + {0xffffffff, 0xffffffff} }; + struct ipu_soc *ipu; + int ipu_idx, task_id; + int i; + + /* decide task_id */ + if (t->task_id >= IPU_TASK_ID_MAX) + t->task_id %= IPU_TASK_ID_MAX; + /* must use task_id VF for VDI task*/ + if ((t->set.mode & VDI_MODE) && + (t->task_id != IPU_TASK_ID_VF)) + t->task_id = IPU_TASK_ID_VF; + + for (i = 0; i < MXC_IPU_MAX_NUM; i++) { + ipu = ipu_get_soc(i); + if (!IS_ERR(ipu)) { + task_num[i][0] = get_task_size(ipu, IPU_TASK_ID_VF); + task_num[i][1] = get_task_size(ipu, IPU_TASK_ID_PP); + } + } + + task_id = t->task_id; + if (t->task_id == IPU_TASK_ID_VF) { + if (task_num[0][0] < task_num[1][0]) + ipu_idx = 0; + else + ipu_idx = 1; + } else if (t->task_id == IPU_TASK_ID_PP) { + if (task_num[0][1] < task_num[1][1]) + ipu_idx = 0; + else + ipu_idx = 1; + } else { + unsigned int min; + ipu_idx = 0; + task_id = IPU_TASK_ID_VF; + min = task_num[0][0]; + if (task_num[0][1] < min) { + min = task_num[0][1]; + task_id = IPU_TASK_ID_PP; + } + if (task_num[1][0] < min) { + min = task_num[1][0]; + ipu_idx = 1; + task_id = IPU_TASK_ID_VF; + } + if (task_num[1][1] < min) { + ipu_idx = 1; + task_id = IPU_TASK_ID_PP; + } + } + + t->task_id = task_id; + ipu = ipu_get_soc(ipu_idx); + + return ipu; +} + +static void dump_task_info(struct ipu_task_entry *t) +{ + dev_dbg(t->dev, "[0x%p]input:\n", (void *)t); + dev_dbg(t->dev, "[0x%p]\tformat = 0x%x\n", (void *)t, t->input.format); + dev_dbg(t->dev, "[0x%p]\twidth = %d\n", (void *)t, t->input.width); + dev_dbg(t->dev, "[0x%p]\theight = %d\n", (void *)t, t->input.height); + dev_dbg(t->dev, "[0x%p]\tcrop.w = %d\n", (void *)t, t->input.crop.w); + dev_dbg(t->dev, "[0x%p]\tcrop.h = %d\n", (void *)t, t->input.crop.h); + dev_dbg(t->dev, "[0x%p]\tcrop.pos.x = %d\n", + (void *)t, t->input.crop.pos.x); + dev_dbg(t->dev, "[0x%p]\tcrop.pos.y = %d\n", + (void *)t, t->input.crop.pos.y); + dev_dbg(t->dev, "[0x%p]input buffer:\n", (void *)t); + dev_dbg(t->dev, "[0x%p]\tpaddr = 0x%x\n", (void *)t, t->input.paddr); + dev_dbg(t->dev, "[0x%p]\ti_off = 0x%x\n", (void *)t, t->set.i_off); + dev_dbg(t->dev, "[0x%p]\ti_uoff = 0x%x\n", (void *)t, t->set.i_uoff); + dev_dbg(t->dev, "[0x%p]\ti_voff = 0x%x\n", (void *)t, t->set.i_voff); + dev_dbg(t->dev, "[0x%p]\tistride = %d\n", (void *)t, t->set.istride); + if (t->input.deinterlace.enable) { + dev_dbg(t->dev, "[0x%p]deinterlace enabled with:\n", (void *)t); + if (t->input.deinterlace.motion != HIGH_MOTION) { + dev_dbg(t->dev, "[0x%p]\tlow/medium motion\n", (void *)t); + dev_dbg(t->dev, "[0x%p]\tpaddr_n = 0x%x\n", + (void *)t, t->input.paddr_n); + } else + dev_dbg(t->dev, "[0x%p]\thigh motion\n", (void *)t); + } + + dev_dbg(t->dev, "[0x%p]output:\n", (void *)t); + dev_dbg(t->dev, "[0x%p]\tformat = 0x%x\n", (void *)t, t->output.format); + dev_dbg(t->dev, "[0x%p]\twidth = %d\n", (void *)t, t->output.width); + dev_dbg(t->dev, "[0x%p]\theight = %d\n", (void *)t, t->output.height); + dev_dbg(t->dev, "[0x%p]\tcrop.w = %d\n", (void *)t, t->output.crop.w); + dev_dbg(t->dev, "[0x%p]\tcrop.h = %d\n", (void *)t, t->output.crop.h); + dev_dbg(t->dev, "[0x%p]\tcrop.pos.x = %d\n", + (void *)t, t->output.crop.pos.x); + dev_dbg(t->dev, "[0x%p]\tcrop.pos.y = %d\n", + (void *)t, t->output.crop.pos.y); + dev_dbg(t->dev, "[0x%p]\trotate = %d\n", (void *)t, t->output.rotate); + dev_dbg(t->dev, "[0x%p]output buffer:\n", (void *)t); + dev_dbg(t->dev, "[0x%p]\tpaddr = 0x%x\n", (void *)t, t->output.paddr); + dev_dbg(t->dev, "[0x%p]\to_off = 0x%x\n", (void *)t, t->set.o_off); + dev_dbg(t->dev, "[0x%p]\to_uoff = 0x%x\n", (void *)t, t->set.o_uoff); + dev_dbg(t->dev, "[0x%p]\to_voff = 0x%x\n", (void *)t, t->set.o_voff); + dev_dbg(t->dev, "[0x%p]\tostride = %d\n", (void *)t, t->set.ostride); + + if (t->overlay_en) { + dev_dbg(t->dev, "[0x%p]overlay:\n", (void *)t); + dev_dbg(t->dev, "[0x%p]\tformat = 0x%x\n", + (void *)t, t->overlay.format); + dev_dbg(t->dev, "[0x%p]\twidth = %d\n", + (void *)t, t->overlay.width); + dev_dbg(t->dev, "[0x%p]\theight = %d\n", + (void *)t, t->overlay.height); + dev_dbg(t->dev, "[0x%p]\tcrop.w = %d\n", + (void *)t, t->overlay.crop.w); + dev_dbg(t->dev, "[0x%p]\tcrop.h = %d\n", + (void *)t, t->overlay.crop.h); + dev_dbg(t->dev, "[0x%p]\tcrop.pos.x = %d\n", + (void *)t, t->overlay.crop.pos.x); + dev_dbg(t->dev, "[0x%p]\tcrop.pos.y = %d\n", + (void *)t, t->overlay.crop.pos.y); + dev_dbg(t->dev, "[0x%p]overlay buffer:\n", (void *)t); + dev_dbg(t->dev, "[0x%p]\tpaddr = 0x%x\n", + (void *)t, t->overlay.paddr); + dev_dbg(t->dev, "[0x%p]\tov_off = 0x%x\n", + (void *)t, t->set.ov_off); + dev_dbg(t->dev, "[0x%p]\tov_uoff = 0x%x\n", + (void *)t, t->set.ov_uoff); + dev_dbg(t->dev, "[0x%p]\tov_voff = 0x%x\n", + (void *)t, t->set.ov_voff); + dev_dbg(t->dev, "[0x%p]\tovstride = %d\n", + (void *)t, t->set.ovstride); + if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) { + dev_dbg(t->dev, "[0x%p]local alpha enabled with:\n", + (void *)t); + dev_dbg(t->dev, "[0x%p]\tpaddr = 0x%x\n", + (void *)t, t->overlay.alpha.loc_alp_paddr); + dev_dbg(t->dev, "[0x%p]\tov_alpha_off = 0x%x\n", + (void *)t, t->set.ov_alpha_off); + dev_dbg(t->dev, "[0x%p]\tov_alpha_stride = %d\n", + (void *)t, t->set.ov_alpha_stride); + } else + dev_dbg(t->dev, "[0x%p]globle alpha enabled with value 0x%x\n", + (void *)t, t->overlay.alpha.gvalue); + if (t->overlay.colorkey.enable) + dev_dbg(t->dev, "[0x%p]colorkey enabled with value 0x%x\n", + (void *)t, t->overlay.colorkey.value); + } + + dev_dbg(t->dev, "[0x%p]want task_id = %d\n", (void *)t, t->task_id); + dev_dbg(t->dev, "[0x%p]want task mode is 0x%x\n", + (void *)t, t->set.mode); + dev_dbg(t->dev, "[0x%p]\tIC_MODE = 0x%x\n", (void *)t, IC_MODE); + dev_dbg(t->dev, "[0x%p]\tROT_MODE = 0x%x\n", (void *)t, ROT_MODE); + dev_dbg(t->dev, "[0x%p]\tVDI_MODE = 0x%x\n", (void *)t, VDI_MODE); +} + +static void dump_check_err(struct device *dev, int err) +{ + switch (err) { + case IPU_CHECK_ERR_INPUT_CROP: + dev_err(dev, "input crop setting error\n"); + break; + case IPU_CHECK_ERR_OUTPUT_CROP: + dev_err(dev, "output crop setting error\n"); + break; + case IPU_CHECK_ERR_OVERLAY_CROP: + dev_err(dev, "overlay crop setting error\n"); + break; + case IPU_CHECK_ERR_INPUT_OVER_LIMIT: + dev_err(dev, "input over limitation\n"); + break; + case IPU_CHECK_ERR_OVERLAY_WITH_VDI: + dev_err(dev, "do not support overlay with deinterlace\n"); + break; + case IPU_CHECK_ERR_OV_OUT_NO_FIT: + dev_err(dev, + "width/height of overlay and ic output should be same\n"); + break; + case IPU_CHECK_ERR_PROC_NO_NEED: + dev_err(dev, "no ipu processing need\n"); + break; + case IPU_CHECK_ERR_SPLIT_INPUTW_OVER: + dev_err(dev, "split mode input width overflow\n"); + break; + case IPU_CHECK_ERR_SPLIT_INPUTH_OVER: + dev_err(dev, "split mode input height overflow\n"); + break; + case IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER: + dev_err(dev, "split mode output width overflow\n"); + break; + case IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER: + dev_err(dev, "split mode output height overflow\n"); + break; + default: + break; + } +} + +static void dump_check_warn(struct device *dev, int warn) +{ + if (warn & IPU_CHECK_WARN_INPUT_OFFS_NOT8ALIGN) + dev_warn(dev, "input u/v offset not 8 align\n"); + if (warn & IPU_CHECK_WARN_OUTPUT_OFFS_NOT8ALIGN) + dev_warn(dev, "output u/v offset not 8 align\n"); + if (warn & IPU_CHECK_WARN_OVERLAY_OFFS_NOT8ALIGN) + dev_warn(dev, "overlay u/v offset not 8 align\n"); +} + +static int set_crop(struct ipu_crop *crop, int width, int height) +{ + if (crop->w || crop->h) { + if (((crop->w + crop->pos.x) > width) + || ((crop->h + crop->pos.y) > height)) + return -EINVAL; + } else { + crop->pos.x = 0; + crop->pos.y = 0; + crop->w = width; + crop->h = height; + } + crop->w -= crop->w%8; + crop->h -= crop->h%8; + + return 0; +} + +static void update_offset(unsigned int fmt, + unsigned int width, unsigned int height, + unsigned int pos_x, unsigned int pos_y, + int *off, int *uoff, int *voff, int *stride) +{ + /* NOTE: u v offset should based on start point of off*/ + switch (fmt) { + case IPU_PIX_FMT_YUV420P2: + case IPU_PIX_FMT_YUV420P: + *off = pos_y * width + pos_x; + *uoff = (width * (height - pos_y) - pos_x) + + ((width/2 * pos_y/2) + pos_x/2); + *voff = *uoff + (width/2 * height/2); + break; + case IPU_PIX_FMT_YVU420P: + *off = pos_y * width + pos_x; + *voff = (width * (height - pos_y) - pos_x) + + ((width/2 * pos_y/2) + pos_x/2); + *uoff = *voff + (width/2 * height/2); + break; + case IPU_PIX_FMT_YVU422P: + *off = pos_y * width + pos_x; + *voff = (width * (height - pos_y) - pos_x) + + ((width * pos_y)/2 + pos_x/2); + *uoff = *voff + (width * height)/2; + break; + case IPU_PIX_FMT_YUV422P: + *off = pos_y * width + pos_x; + *uoff = (width * (height - pos_y) - pos_x) + + (width * pos_y)/2 + pos_x/2; + *voff = *uoff + (width * height)/2; + break; + case IPU_PIX_FMT_NV12: + *off = pos_y * width + pos_x; + *uoff = (width * (height - pos_y) - pos_x) + + width * pos_y/2 + pos_x; + break; + default: + *off = (pos_y * width + pos_x) * fmt_to_bpp(fmt)/8; + break; + } + *stride = width * bytes_per_pixel(fmt); +} + +static int update_split_setting(struct ipu_task_entry *t) +{ + struct stripe_param left_stripe; + struct stripe_param right_stripe; + struct stripe_param up_stripe; + struct stripe_param down_stripe; + u32 iw, ih, ow, oh; + + iw = t->input.crop.w; + ih = t->input.crop.h; + if (t->output.rotate >= IPU_ROTATE_90_RIGHT) { + ow = t->output.crop.h; + oh = t->output.crop.w; + } else { + ow = t->output.crop.w; + oh = t->output.crop.h; + } + + if (t->set.split_mode & RL_SPLIT) { + ipu_calc_stripes_sizes(iw, + ow, + soc_max_out_width(), + (((unsigned long long)1) << 32), /* 32bit for fractional*/ + 1, /* equal stripes */ + t->input.format, + t->output.format, + &left_stripe, + &right_stripe); + t->set.sp_setting.iw = left_stripe.input_width; + t->set.sp_setting.ow = left_stripe.output_width; + t->set.sp_setting.outh_resize_ratio = left_stripe.irr; + t->set.sp_setting.i_left_pos = left_stripe.input_column; + t->set.sp_setting.o_left_pos = left_stripe.output_column; + t->set.sp_setting.i_right_pos = right_stripe.input_column; + t->set.sp_setting.o_right_pos = right_stripe.output_column; + } else { + t->set.sp_setting.iw = iw; + t->set.sp_setting.ow = ow; + t->set.sp_setting.outh_resize_ratio = 0; + t->set.sp_setting.i_left_pos = 0; + t->set.sp_setting.o_left_pos = 0; + t->set.sp_setting.i_right_pos = 0; + t->set.sp_setting.o_right_pos = 0; + } + if ((t->set.sp_setting.iw + t->set.sp_setting.i_right_pos) > iw) + return IPU_CHECK_ERR_SPLIT_INPUTW_OVER; + if (((t->set.sp_setting.ow + t->set.sp_setting.o_right_pos) > ow) + || (t->set.sp_setting.ow > soc_max_out_width())) + return IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER; + + if (t->set.split_mode & UD_SPLIT) { + ipu_calc_stripes_sizes(ih, + oh, + soc_max_out_height(), + (((unsigned long long)1) << 32), /* 32bit for fractional*/ + 1, /* equal stripes */ + t->input.format, + t->output.format, + &up_stripe, + &down_stripe); + t->set.sp_setting.ih = up_stripe.input_width; + t->set.sp_setting.oh = up_stripe.output_width; + t->set.sp_setting.outv_resize_ratio = up_stripe.irr; + t->set.sp_setting.i_top_pos = up_stripe.input_column; + t->set.sp_setting.o_top_pos = up_stripe.output_column; + t->set.sp_setting.i_bottom_pos = down_stripe.input_column; + t->set.sp_setting.o_bottom_pos = down_stripe.output_column; + } else { + t->set.sp_setting.ih = ih; + t->set.sp_setting.oh = oh; + t->set.sp_setting.outv_resize_ratio = 0; + t->set.sp_setting.i_top_pos = 0; + t->set.sp_setting.o_top_pos = 0; + t->set.sp_setting.i_bottom_pos = 0; + t->set.sp_setting.o_bottom_pos = 0; + } + if ((t->set.sp_setting.ih + t->set.sp_setting.i_bottom_pos) > ih) + return IPU_CHECK_ERR_SPLIT_INPUTH_OVER; + if (((t->set.sp_setting.oh + t->set.sp_setting.o_bottom_pos) > oh) + || (t->set.sp_setting.oh > soc_max_out_height())) + return IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER; + + return IPU_CHECK_OK; +} + +static int check_task(struct ipu_task_entry *t) +{ + int tmp; + int ret = IPU_CHECK_OK; + + /* check input */ + ret = set_crop(&t->input.crop, t->input.width, t->input.height); + if (ret < 0) { + ret = IPU_CHECK_ERR_INPUT_CROP; + goto done; + } else + update_offset(t->input.format, t->input.width, t->input.height, + t->input.crop.pos.x, t->input.crop.pos.y, + &t->set.i_off, &t->set.i_uoff, + &t->set.i_voff, &t->set.istride); + + /* check output */ + ret = set_crop(&t->output.crop, t->output.width, t->output.height); + if (ret < 0) { + ret = IPU_CHECK_ERR_OUTPUT_CROP; + goto done; + } else + update_offset(t->output.format, + t->output.width, t->output.height, + t->output.crop.pos.x, t->output.crop.pos.y, + &t->set.o_off, &t->set.o_uoff, + &t->set.o_voff, &t->set.ostride); + + /* check overlay if there is */ + if (t->overlay_en) { + if (t->input.deinterlace.enable) { + ret = IPU_CHECK_ERR_OVERLAY_WITH_VDI; + goto done; + } + ret = set_crop(&t->overlay.crop, t->overlay.width, t->overlay.height); + if (ret < 0) { + ret = IPU_CHECK_ERR_OVERLAY_CROP; + goto done; + } else { + int ow = t->output.crop.w; + int oh = t->output.crop.h; + + if (t->output.rotate >= IPU_ROTATE_90_RIGHT) { + ow = t->output.crop.h; + oh = t->output.crop.w; + } + if ((t->overlay.crop.w != ow) || (t->overlay.crop.h != oh)) { + ret = IPU_CHECK_ERR_OV_OUT_NO_FIT; + goto done; + } + + update_offset(t->overlay.format, + t->overlay.width, t->overlay.height, + t->overlay.crop.pos.x, t->overlay.crop.pos.y, + &t->set.ov_off, &t->set.ov_uoff, + &t->set.ov_voff, &t->set.ovstride); + if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) { + t->set.ov_alpha_stride = t->overlay.width; + t->set.ov_alpha_off = t->overlay.crop.pos.y * + t->overlay.width + t->overlay.crop.pos.x; + } + } + } + + /* input overflow? */ + if ((t->input.crop.w > soc_max_in_width()) || + (t->input.crop.h > soc_max_in_height())) { + ret = IPU_CHECK_ERR_INPUT_OVER_LIMIT; + goto done; + } + + /* check task mode */ + t->set.mode = NULL_MODE; + t->set.split_mode = NO_SPLIT; + + if (t->output.rotate >= IPU_ROTATE_90_RIGHT) { + /*output swap*/ + tmp = t->output.crop.w; + t->output.crop.w = t->output.crop.h; + t->output.crop.h = tmp; + } + + if (t->output.rotate >= IPU_ROTATE_90_RIGHT) + t->set.mode |= ROT_MODE; + + /*need resize or CSC?*/ + if ((t->input.crop.w != t->output.crop.w) || + (t->input.crop.h != t->output.crop.h) || + need_csc(t->input.format, t->output.format)) + t->set.mode |= IC_MODE; + + /*need flip?*/ + if ((t->set.mode == NULL_MODE) && (t->output.rotate > IPU_ROTATE_NONE)) + t->set.mode |= IC_MODE; + + /*need IDMAC do format(same color space)?*/ + if ((t->set.mode == NULL_MODE) && (t->input.format != t->output.format)) + t->set.mode |= IC_MODE; + + /*overlay support*/ + if (t->overlay_en) + t->set.mode |= IC_MODE; + + /*deinterlace*/ + if (t->input.deinterlace.enable) { + t->set.mode &= ~IC_MODE; + t->set.mode |= VDI_MODE; + } + + if (t->set.mode & (IC_MODE | VDI_MODE)) { + if (t->output.crop.w > soc_max_out_width()) + t->set.split_mode |= RL_SPLIT; + if (t->output.crop.h > soc_max_out_height()) + t->set.split_mode |= UD_SPLIT; + ret = update_split_setting(t); + if (ret > IPU_CHECK_ERR_MIN) + goto done; + } + + if (t->output.rotate >= IPU_ROTATE_90_RIGHT) { + /*output swap*/ + tmp = t->output.crop.w; + t->output.crop.w = t->output.crop.h; + t->output.crop.h = tmp; + } + + if (t->set.mode == NULL_MODE) { + ret = IPU_CHECK_ERR_PROC_NO_NEED; + goto done; + } + + if ((t->set.i_uoff % 8) || (t->set.i_voff % 8)) + ret |= IPU_CHECK_WARN_INPUT_OFFS_NOT8ALIGN; + if ((t->set.o_uoff % 8) || (t->set.o_voff % 8)) + ret |= IPU_CHECK_WARN_OUTPUT_OFFS_NOT8ALIGN; + if (t->overlay_en && ((t->set.ov_uoff % 8) || (t->set.ov_voff % 8))) + ret |= IPU_CHECK_WARN_OVERLAY_OFFS_NOT8ALIGN; + +done: + /* dump msg */ + if (ret > IPU_CHECK_ERR_MIN) + dump_check_err(t->dev, ret); + else if (ret != IPU_CHECK_OK) + dump_check_warn(t->dev, ret); + + return ret; +} + +static int prepare_task(struct ipu_task_entry *t) +{ + int ret = 0; + + ret = check_task(t); + if (ret > IPU_CHECK_ERR_MIN) + return -EINVAL; + + dump_task_info(t); + + return ret; +} + +/* should call from a process context */ +static int queue_task(struct ipu_task_entry *t) +{ + int ret = 0; + struct ipu_soc *ipu; + struct list_head *task_list = NULL; + struct mutex *task_lock = NULL; + wait_queue_head_t *waitq = NULL; + + ipu = most_free_ipu_task(t); + t->dev = ipu->dev; + + dev_dbg(t->dev, "[0x%p]Queue task: id %d\n", (void *)t, t->task_id); + + init_completion(&t->comp); + + t->set.task = 0; + switch (t->task_id) { + case IPU_TASK_ID_VF: + task_list = &ipu->task_list[0]; + task_lock = &ipu->task_lock[0]; + waitq = &ipu->waitq[0]; + if (t->set.mode & IC_MODE) + t->set.task |= IC_VF; + else if (t->set.mode & VDI_MODE) + t->set.task |= VDI_VF; + if (t->set.mode & ROT_MODE) + t->set.task |= ROT_VF; + break; + case IPU_TASK_ID_PP: + task_list = &ipu->task_list[1]; + task_lock = &ipu->task_lock[1]; + waitq = &ipu->waitq[1]; + if (t->set.mode & IC_MODE) + t->set.task |= IC_PP; + if (t->set.mode & ROT_MODE) + t->set.task |= ROT_PP; + break; + default: + dev_err(t->dev, "[0x%p]should never come here\n", (void *)t); + } + + dev_dbg(t->dev, "[0x%p]choose task_id[%d] mode[0x%x]\n", + (void *)t, t->task_id, t->set.task); + dev_dbg(t->dev, "[0x%p]\tIPU_TASK_ID_VF = %d\n", + (void *)t, IPU_TASK_ID_VF); + dev_dbg(t->dev, "[0x%p]\tIPU_TASK_ID_PP = %d\n", + (void *)t, IPU_TASK_ID_PP); + dev_dbg(t->dev, "[0x%p]\tIC_VF = 0x%x\n", (void *)t, IC_VF); + dev_dbg(t->dev, "[0x%p]\tIC_PP = 0x%x\n", (void *)t, IC_PP); + dev_dbg(t->dev, "[0x%p]\tROT_VF = 0x%x\n", (void *)t, ROT_VF); + dev_dbg(t->dev, "[0x%p]\tROT_PP = 0x%x\n", (void *)t, ROT_PP); + dev_dbg(t->dev, "[0x%p]\tVDI_VF = 0x%x\n", (void *)t, VDI_VF); + + /* add and wait task */ + mutex_lock(task_lock); + list_add_tail(&t->node, task_list); + mutex_unlock(task_lock); + + wake_up_interruptible(waitq); + + wait_for_completion(&t->comp); + + dev_dbg(t->dev, "[0x%p]Queue task finished\n", (void *)t); + + if (t->state != STATE_OK) { + dev_err(t->dev, "[0x%p]state %d: %s\n", + (void *)t, t->state, state_msg[t->state].msg); + ret = -ECANCELED; + } + + return ret; +} + +static bool need_split(struct ipu_task_entry *t) +{ + return (t->set.split_mode != NO_SPLIT); +} + +static int split_task_thread(void *data) +{ + struct ipu_split_task *t = data; + + t->ret = ipu_queue_sp_task(t); + + while (!kthread_should_stop()) + wait_event_interruptible(t->waitq, t->could_finish); + + return 0; +} + +static int create_split_task( + int stripe, + struct ipu_split_task *sp_task) +{ + struct ipu_task *task = &(sp_task->task); + struct ipu_task_entry *t = sp_task->parent_task; + + task->input = t->input; + task->output = t->output; + task->overlay_en = t->overlay_en; + if (task->overlay_en) + task->overlay = t->overlay; + task->priority = t->priority; + task->task_id = t->task_id; + task->timeout = t->timeout; + + task->input.crop.w = t->set.sp_setting.iw; + task->input.crop.h = t->set.sp_setting.ih; + if (task->overlay_en) { + task->overlay.crop.w = t->set.sp_setting.ow; + task->overlay.crop.h = t->set.sp_setting.oh; + } + if (t->output.rotate >= IPU_ROTATE_90_RIGHT) { + task->output.crop.w = t->set.sp_setting.oh; + task->output.crop.h = t->set.sp_setting.ow; + } else { + task->output.crop.w = t->set.sp_setting.ow; + task->output.crop.h = t->set.sp_setting.oh; + } + + if (stripe & LEFT_STRIPE) + task->input.crop.pos.x += t->set.sp_setting.i_left_pos; + else if (stripe & RIGHT_STRIPE) + task->input.crop.pos.x += t->set.sp_setting.i_right_pos; + if (stripe & UP_STRIPE) + task->input.crop.pos.y += t->set.sp_setting.i_top_pos; + else if (stripe & DOWN_STRIPE) + task->input.crop.pos.y += t->set.sp_setting.i_bottom_pos; + + if (task->overlay_en) { + if (stripe & LEFT_STRIPE) + task->overlay.crop.pos.x += t->set.sp_setting.o_left_pos; + else if (stripe & RIGHT_STRIPE) + task->overlay.crop.pos.x += t->set.sp_setting.o_right_pos; + if (stripe & UP_STRIPE) + task->overlay.crop.pos.y += t->set.sp_setting.o_top_pos; + else if (stripe & DOWN_STRIPE) + task->overlay.crop.pos.y += t->set.sp_setting.o_bottom_pos; + } + + switch (t->output.rotate) { + case IPU_ROTATE_NONE: + if (stripe & LEFT_STRIPE) + task->output.crop.pos.x += t->set.sp_setting.o_left_pos; + else if (stripe & RIGHT_STRIPE) + task->output.crop.pos.x += t->set.sp_setting.o_right_pos; + if (stripe & UP_STRIPE) + task->output.crop.pos.y += t->set.sp_setting.o_top_pos; + else if (stripe & DOWN_STRIPE) + task->output.crop.pos.y += t->set.sp_setting.o_bottom_pos; + break; + case IPU_ROTATE_VERT_FLIP: + if (stripe & LEFT_STRIPE) + task->output.crop.pos.x += t->set.sp_setting.o_left_pos; + else if (stripe & RIGHT_STRIPE) + task->output.crop.pos.x += t->set.sp_setting.o_right_pos; + if (stripe & UP_STRIPE) + task->output.crop.pos.y = + t->output.crop.pos.y + t->output.crop.h + - t->set.sp_setting.o_top_pos - t->set.sp_setting.oh; + else if (stripe & DOWN_STRIPE) + task->output.crop.pos.y = + t->output.crop.pos.y + t->output.crop.h + - t->set.sp_setting.o_bottom_pos - t->set.sp_setting.oh; + break; + case IPU_ROTATE_HORIZ_FLIP: + if (stripe & LEFT_STRIPE) + task->output.crop.pos.x = + t->output.crop.pos.x + t->output.crop.w + - t->set.sp_setting.o_left_pos - t->set.sp_setting.ow; + else if (stripe & RIGHT_STRIPE) + task->output.crop.pos.x = + t->output.crop.pos.x + t->output.crop.w + - t->set.sp_setting.o_right_pos - t->set.sp_setting.ow; + if (stripe & UP_STRIPE) + task->output.crop.pos.y += t->set.sp_setting.o_top_pos; + else if (stripe & DOWN_STRIPE) + task->output.crop.pos.y += t->set.sp_setting.o_bottom_pos; + break; + case IPU_ROTATE_180: + if (stripe & LEFT_STRIPE) + task->output.crop.pos.x = + t->output.crop.pos.x + t->output.crop.w + - t->set.sp_setting.o_left_pos - t->set.sp_setting.ow; + else if (stripe & RIGHT_STRIPE) + task->output.crop.pos.x = + t->output.crop.pos.x + t->output.crop.w + - t->set.sp_setting.o_right_pos - t->set.sp_setting.ow; + if (stripe & UP_STRIPE) + task->output.crop.pos.y = + t->output.crop.pos.y + t->output.crop.h + - t->set.sp_setting.o_top_pos - t->set.sp_setting.oh; + else if (stripe & DOWN_STRIPE) + task->output.crop.pos.y = + t->output.crop.pos.y + t->output.crop.h + - t->set.sp_setting.o_bottom_pos - t->set.sp_setting.oh; + break; + case IPU_ROTATE_90_RIGHT: + if (stripe & UP_STRIPE) + task->output.crop.pos.x = + t->output.crop.pos.x + t->output.crop.w + - t->set.sp_setting.o_top_pos - t->set.sp_setting.oh; + else if (stripe & DOWN_STRIPE) + task->output.crop.pos.x = + t->output.crop.pos.x + t->output.crop.w + - t->set.sp_setting.o_bottom_pos - t->set.sp_setting.oh; + if (stripe & LEFT_STRIPE) + task->output.crop.pos.y += t->set.sp_setting.o_left_pos; + else if (stripe & RIGHT_STRIPE) + task->output.crop.pos.y += t->set.sp_setting.o_right_pos; + break; + case IPU_ROTATE_90_RIGHT_HFLIP: + if (stripe & UP_STRIPE) + task->output.crop.pos.x += t->set.sp_setting.o_top_pos; + else if (stripe & DOWN_STRIPE) + task->output.crop.pos.x += t->set.sp_setting.o_bottom_pos; + if (stripe & LEFT_STRIPE) + task->output.crop.pos.y += t->set.sp_setting.o_left_pos; + else if (stripe & RIGHT_STRIPE) + task->output.crop.pos.y += t->set.sp_setting.o_right_pos; + break; + case IPU_ROTATE_90_RIGHT_VFLIP: + if (stripe & UP_STRIPE) + task->output.crop.pos.x = + t->output.crop.pos.x + t->output.crop.w + - t->set.sp_setting.o_top_pos - t->set.sp_setting.oh; + else if (stripe & DOWN_STRIPE) + task->output.crop.pos.x = + t->output.crop.pos.x + t->output.crop.w + - t->set.sp_setting.o_bottom_pos - t->set.sp_setting.oh; + if (stripe & LEFT_STRIPE) + task->output.crop.pos.y = + t->output.crop.pos.y + t->output.crop.h + - t->set.sp_setting.o_left_pos - t->set.sp_setting.ow; + else if (stripe & RIGHT_STRIPE) + task->output.crop.pos.y = + t->output.crop.pos.y + t->output.crop.h + - t->set.sp_setting.o_right_pos - t->set.sp_setting.ow; + break; + case IPU_ROTATE_90_LEFT: + if (stripe & UP_STRIPE) + task->output.crop.pos.x += t->set.sp_setting.o_top_pos; + else if (stripe & DOWN_STRIPE) + task->output.crop.pos.x += t->set.sp_setting.o_bottom_pos; + if (stripe & LEFT_STRIPE) + task->output.crop.pos.y = + t->output.crop.pos.y + t->output.crop.h + - t->set.sp_setting.o_left_pos - t->set.sp_setting.ow; + else if (stripe & RIGHT_STRIPE) + task->output.crop.pos.y = + t->output.crop.pos.y + t->output.crop.h + - t->set.sp_setting.o_right_pos - t->set.sp_setting.ow; + break; + default: + dev_err(t->dev, "should not be here\n"); + break; + } + + sp_task->thread = kthread_run(split_task_thread, sp_task, + "ipu_split_task"); + if (IS_ERR(sp_task->thread)) { + dev_err(t->dev, "split thread can not create\n"); + return PTR_ERR(sp_task->thread); + } + + return 0; +} + +static int queue_split_task(struct ipu_task_entry *t) +{ + struct ipu_split_task sp_task[4]; + int i, ret = 0, size; + + dev_dbg(t->dev, "Split task 0x%p\n", (void *)t); + + if ((t->set.split_mode == RL_SPLIT) || (t->set.split_mode == UD_SPLIT)) + size = 2; + else + size = 4; + + for (i = 0; i < size; i++) { + memset(&sp_task[i], 0, sizeof(struct ipu_split_task)); + init_waitqueue_head(&(sp_task[i].waitq)); + sp_task[i].could_finish = false; + sp_task[i].parent_task = t; + } + + if (t->set.split_mode == RL_SPLIT) { + create_split_task(LEFT_STRIPE, &sp_task[0]); + create_split_task(RIGHT_STRIPE, &sp_task[1]); + } else if (t->set.split_mode == UD_SPLIT) { + create_split_task(UP_STRIPE, &sp_task[0]); + create_split_task(DOWN_STRIPE, &sp_task[1]); + } else { + create_split_task(LEFT_STRIPE | UP_STRIPE, &sp_task[0]); + create_split_task(RIGHT_STRIPE | UP_STRIPE, &sp_task[1]); + create_split_task(LEFT_STRIPE | DOWN_STRIPE, &sp_task[2]); + create_split_task(RIGHT_STRIPE | DOWN_STRIPE, &sp_task[3]); + } + + for (i = 0; i < size; i++) { + sp_task[i].could_finish = true; + wake_up_interruptible(&sp_task[i].waitq); + kthread_stop(sp_task[i].thread); + if (sp_task[i].ret < 0) { + ret = sp_task[i].ret; + dev_err(t->dev, + "split task %d fail with ret %d\n", + i, ret); + } + } + + return ret; +} + +static struct ipu_task_entry *create_task_entry(struct ipu_task *task) +{ + struct ipu_task_entry *tsk; + + tsk = kzalloc(sizeof(struct ipu_task_entry), GFP_KERNEL); + if (!tsk) + return ERR_PTR(-ENOMEM); + + tsk->dev = ipu_dev; + tsk->input = task->input; + tsk->output = task->output; + tsk->overlay_en = task->overlay_en; + if (tsk->overlay_en) + tsk->overlay = task->overlay; + tsk->priority = task->priority; + tsk->task_id = task->task_id; + if (task->timeout && (task->timeout > DEF_TIMEOUT_MS)) + tsk->timeout = task->timeout; + else + tsk->timeout = DEF_TIMEOUT_MS; + + return tsk; +} + +int ipu_check_task(struct ipu_task *task) +{ + struct ipu_task_entry *tsk; + int ret = 0; + + tsk = create_task_entry(task); + if (IS_ERR(tsk)) + return PTR_ERR(tsk); + + ret = check_task(tsk); + + task->input = tsk->input; + task->output = tsk->output; + task->overlay = tsk->overlay; + + kfree(tsk); + + return ret; +} +EXPORT_SYMBOL_GPL(ipu_check_task); + +int ipu_queue_sp_task(struct ipu_split_task *sp_task) +{ + struct ipu_task_entry *tsk; + int ret; + + tsk = create_task_entry(&sp_task->task); + if (IS_ERR(tsk)) + return PTR_ERR(tsk); + + ret = prepare_task(tsk); + if (ret < 0) + goto done; + + tsk->set.sp_setting = sp_task->parent_task->set.sp_setting; + tsk->set.sp_setting = sp_task->parent_task->set.sp_setting; + + ret = queue_task(tsk); +done: + kfree(tsk); + return ret; +} + +int ipu_queue_task(struct ipu_task *task) +{ + struct ipu_task_entry *tsk; + int ret; + + tsk = create_task_entry(task); + if (IS_ERR(tsk)) + return PTR_ERR(tsk); + + ret = prepare_task(tsk); + if (ret < 0) + goto done; + + if (need_split(tsk)) + ret = queue_split_task(tsk); + else + ret = queue_task(tsk); +done: + kfree(tsk); + return ret; +} +EXPORT_SYMBOL_GPL(ipu_queue_task); + +static bool only_ic(u8 mode) +{ + return ((mode == IC_MODE) || (mode == VDI_MODE)); +} + +static bool only_rot(u8 mode) +{ + return (mode == ROT_MODE); +} + +static bool ic_and_rot(u8 mode) +{ + return ((mode == (IC_MODE | ROT_MODE)) || + (mode == (VDI_MODE | ROT_MODE))); +} + +static int init_ic(struct ipu_soc *ipu, struct ipu_task_entry *t) +{ + int ret = 0; + ipu_channel_params_t params; + dma_addr_t inbuf = 0, ovbuf = 0, ov_alp_buf = 0; + dma_addr_t inbuf_p = 0, inbuf_n = 0; + dma_addr_t outbuf = 0; + int out_uoff = 0, out_voff = 0, out_rot; + int out_w = 0, out_h = 0, out_stride; + int out_fmt; + + memset(¶ms, 0, sizeof(params)); + + /* is it need link a rot channel */ + if (ic_and_rot(t->set.mode)) { + outbuf = t->set.r_paddr; + out_w = t->set.r_width; + out_h = t->set.r_height; + out_stride = t->set.r_stride; + out_fmt = t->set.r_fmt; + out_uoff = 0; + out_voff = 0; + out_rot = IPU_ROTATE_NONE; + } else { + outbuf = t->output.paddr + t->set.o_off; + out_w = t->output.crop.w; + out_h = t->output.crop.h; + out_stride = t->set.ostride; + out_fmt = t->output.format; + out_uoff = t->set.o_uoff; + out_voff = t->set.o_voff; + out_rot = t->output.rotate; + } + + /* settings */ + params.mem_prp_vf_mem.in_width = t->input.crop.w; + params.mem_prp_vf_mem.out_width = out_w; + params.mem_prp_vf_mem.in_height = t->input.crop.h; + params.mem_prp_vf_mem.out_height = out_h; + params.mem_prp_vf_mem.in_pixel_fmt = t->input.format; + params.mem_prp_vf_mem.out_pixel_fmt = out_fmt; + params.mem_prp_vf_mem.motion_sel = t->input.deinterlace.motion; + + params.mem_prp_vf_mem.outh_resize_ratio = + t->set.sp_setting.outh_resize_ratio; + params.mem_prp_vf_mem.outv_resize_ratio = + t->set.sp_setting.outv_resize_ratio; + + if (t->overlay_en) { + params.mem_prp_vf_mem.in_g_pixel_fmt = t->overlay.format; + params.mem_prp_vf_mem.graphics_combine_en = 1; + if (t->overlay.alpha.mode == IPU_ALPHA_MODE_GLOBAL) + params.mem_prp_vf_mem.global_alpha_en = 1; + else + params.mem_prp_vf_mem.alpha_chan_en = 1; + params.mem_prp_vf_mem.alpha = t->overlay.alpha.gvalue; + if (t->overlay.colorkey.enable) { + params.mem_prp_vf_mem.key_color_en = 1; + params.mem_prp_vf_mem.key_color = t->overlay.colorkey.value; + } + } + + /* init channels */ + ret = ipu_init_channel(ipu, t->set.ic_chan, ¶ms); + if (ret < 0) { + t->state = STATE_INIT_CHAN_FAIL; + goto done; + } + + if (deinterlace_3_field(t)) { + ret = ipu_init_channel(ipu, t->set.vdi_ic_p_chan, ¶ms); + if (ret < 0) { + t->state = STATE_INIT_CHAN_FAIL; + goto done; + } + ret = ipu_init_channel(ipu, t->set.vdi_ic_n_chan, ¶ms); + if (ret < 0) { + t->state = STATE_INIT_CHAN_FAIL; + goto done; + } + } + + /* init channel bufs */ + if (deinterlace_3_field(t)) { + inbuf_p = t->input.paddr + t->set.istride + t->set.i_off; + inbuf = t->input.paddr_n + t->set.i_off; + inbuf_n = t->input.paddr_n + t->set.istride + t->set.i_off; + } else + inbuf = t->input.paddr + t->set.i_off; + + if (t->overlay_en) { + ovbuf = t->overlay.paddr + t->set.ov_off; + if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) + ov_alp_buf = t->overlay.alpha.loc_alp_paddr + + t->set.ov_alpha_off; + } + + ret = ipu_init_channel_buffer(ipu, + t->set.ic_chan, + IPU_INPUT_BUFFER, + t->input.format, + t->input.crop.w, + t->input.crop.h, + t->set.istride, + IPU_ROTATE_NONE, + inbuf, + 0, + 0, + t->set.i_uoff, + t->set.i_voff); + if (ret < 0) { + t->state = STATE_INIT_CHAN_BUF_FAIL; + goto done; + } + + if (deinterlace_3_field(t)) { + ret = ipu_init_channel_buffer(ipu, + t->set.vdi_ic_p_chan, + IPU_INPUT_BUFFER, + t->input.format, + t->input.crop.w, + t->input.crop.h, + t->set.istride, + IPU_ROTATE_NONE, + inbuf_p, + 0, + 0, + t->set.i_uoff, + t->set.i_voff); + if (ret < 0) { + t->state = STATE_INIT_CHAN_BUF_FAIL; + goto done; + } + + ret = ipu_init_channel_buffer(ipu, + t->set.vdi_ic_n_chan, + IPU_INPUT_BUFFER, + t->input.format, + t->input.crop.w, + t->input.crop.h, + t->set.istride, + IPU_ROTATE_NONE, + inbuf_n, + 0, + 0, + t->set.i_uoff, + t->set.i_voff); + if (ret < 0) { + t->state = STATE_INIT_CHAN_BUF_FAIL; + goto done; + } + } + + if (t->overlay_en) { + ret = ipu_init_channel_buffer(ipu, + t->set.ic_chan, + IPU_GRAPH_IN_BUFFER, + t->overlay.format, + t->overlay.crop.w, + t->overlay.crop.h, + t->set.ovstride, + IPU_ROTATE_NONE, + ovbuf, + 0, + 0, + t->set.ov_uoff, + t->set.ov_voff); + if (ret < 0) { + t->state = STATE_INIT_CHAN_BUF_FAIL; + goto done; + } + + if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) { + ret = ipu_init_channel_buffer(ipu, + t->set.ic_chan, + IPU_ALPHA_IN_BUFFER, + IPU_PIX_FMT_GENERIC, + t->overlay.crop.w, + t->overlay.crop.h, + t->set.ov_alpha_stride, + IPU_ROTATE_NONE, + ov_alp_buf, + 0, + 0, + 0, 0); + if (ret < 0) { + t->state = STATE_INIT_CHAN_BUF_FAIL; + goto done; + } + } + } + + ret = ipu_init_channel_buffer(ipu, + t->set.ic_chan, + IPU_OUTPUT_BUFFER, + out_fmt, + out_w, + out_h, + out_stride, + out_rot, + outbuf, + 0, + 0, + out_uoff, + out_voff); + if (ret < 0) { + t->state = STATE_INIT_CHAN_BUF_FAIL; + goto done; + } + +done: + return ret; +} + +static void uninit_ic(struct ipu_soc *ipu, struct ipu_task_entry *t) +{ + ipu_uninit_channel(ipu, t->set.ic_chan); + if (deinterlace_3_field(t)) { + ipu_uninit_channel(ipu, t->set.vdi_ic_p_chan); + ipu_uninit_channel(ipu, t->set.vdi_ic_n_chan); + } +} + +static int init_rot(struct ipu_soc *ipu, struct ipu_task_entry *t) +{ + int ret = 0; + dma_addr_t inbuf = 0, outbuf = 0; + int in_uoff = 0, in_voff = 0; + int in_fmt, in_width, in_height, in_stride; + + /* init channel */ + ret = ipu_init_channel(ipu, t->set.rot_chan, NULL); + if (ret < 0) { + t->state = STATE_INIT_CHAN_FAIL; + goto done; + } + + /* init channel buf */ + /* is it need link to a ic channel */ + if (ic_and_rot(t->set.mode)) { + in_fmt = t->set.r_fmt; + in_width = t->set.r_width; + in_height = t->set.r_height; + in_stride = t->set.r_stride; + inbuf = t->set.r_paddr; + in_uoff = 0; + in_voff = 0; + } else { + in_fmt = t->input.format; + in_width = t->input.crop.w; + in_height = t->input.crop.h; + in_stride = t->set.istride; + inbuf = t->input.paddr + t->set.i_off; + in_uoff = t->set.i_uoff; + in_voff = t->set.i_voff; + } + outbuf = t->output.paddr + t->set.o_off; + + ret = ipu_init_channel_buffer(ipu, + t->set.rot_chan, + IPU_INPUT_BUFFER, + in_fmt, + in_width, + in_height, + t->set.istride, + t->output.rotate, + inbuf, + 0, + 0, + in_uoff, + in_voff); + if (ret < 0) { + t->state = STATE_INIT_CHAN_BUF_FAIL; + goto done; + } + + ret = ipu_init_channel_buffer(ipu, + t->set.rot_chan, + IPU_OUTPUT_BUFFER, + t->output.format, + t->output.crop.w, + t->output.crop.h, + t->set.ostride, + IPU_ROTATE_NONE, + outbuf, + 0, + 0, + t->set.o_uoff, + t->set.o_voff); + if (ret < 0) { + t->state = STATE_INIT_CHAN_BUF_FAIL; + goto done; + } + +done: + return ret; +} + +static void uninit_rot(struct ipu_soc *ipu, struct ipu_task_entry *t) +{ + ipu_uninit_channel(ipu, t->set.rot_chan); +} + +static int get_irq(struct ipu_task_entry *t) +{ + int irq; + ipu_channel_t chan; + + if (only_ic(t->set.mode)) + chan = t->set.ic_chan; + else + chan = t->set.rot_chan; + + switch (chan) { + case MEM_ROT_VF_MEM: + irq = IPU_IRQ_PRP_VF_ROT_OUT_EOF; + break; + case MEM_ROT_PP_MEM: + irq = IPU_IRQ_PP_ROT_OUT_EOF; + break; + case MEM_VDI_PRP_VF_MEM: + case MEM_PRP_VF_MEM: + irq = IPU_IRQ_PRP_VF_OUT_EOF; + break; + case MEM_PP_MEM: + irq = IPU_IRQ_PP_OUT_EOF; + break; + default: + irq = -EINVAL; + } + + return irq; +} + +static irqreturn_t task_irq_handler(int irq, void *dev_id) +{ + struct completion *comp = dev_id; + complete(comp); + return IRQ_HANDLED; +} + +static void do_task(struct ipu_soc *ipu, struct ipu_task_entry *t) +{ + struct completion comp; + void *r_vaddr; + int r_size; + int irq; + int ret; + + if (!ipu) { + t->state = STATE_NO_IPU; + return; + } + + dev_dbg(ipu->dev, "[0x%p]Do task: id %d\n", (void *)t, t->task_id); + dump_task_info(t); + + if (t->set.task & IC_PP) { + t->set.ic_chan = MEM_PP_MEM; + dev_dbg(ipu->dev, "[0x%p]ic channel MEM_PP_MEM\n", (void *)t); + } else if (t->set.task & IC_VF) { + t->set.ic_chan = MEM_PRP_VF_MEM; + dev_dbg(ipu->dev, "[0x%p]ic channel MEM_PRP_VF_MEM\n", (void *)t); + } else if (t->set.task & VDI_VF) { + t->set.ic_chan = MEM_VDI_PRP_VF_MEM; + if (deinterlace_3_field(t)) { + t->set.vdi_ic_p_chan = MEM_VDI_PRP_VF_MEM_P; + t->set.vdi_ic_n_chan = MEM_VDI_PRP_VF_MEM_N; + } + dev_dbg(ipu->dev, "[0x%p]ic channel MEM_VDI_PRP_VF_MEM\n", (void *)t); + } + + if (t->set.task & ROT_PP) { + t->set.rot_chan = MEM_ROT_PP_MEM; + dev_dbg(ipu->dev, "[0x%p]rot channel MEM_ROT_PP_MEM\n", (void *)t); + } else if (t->set.task & ROT_VF) { + t->set.rot_chan = MEM_ROT_VF_MEM; + dev_dbg(ipu->dev, "[0x%p]rot channel MEM_ROT_VF_MEM\n", (void *)t); + } + + /* channel setup */ + if (only_ic(t->set.mode)) { + dev_dbg(t->dev, "[0x%p]only ic mode\n", (void *)t); + ret = init_ic(ipu, t); + if (ret < 0) + goto chan_done; + } else if (only_rot(t->set.mode)) { + dev_dbg(t->dev, "[0x%p]only rot mode\n", (void *)t); + ret = init_rot(ipu, t); + if (ret < 0) + goto chan_done; + } else if (ic_and_rot(t->set.mode)) { + dev_dbg(t->dev, "[0x%p]ic + rot mode\n", (void *)t); + t->set.r_fmt = t->output.format; + if (t->output.rotate >= IPU_ROTATE_90_RIGHT) { + t->set.r_width = t->output.crop.h; + t->set.r_height = t->output.crop.w; + } else { + t->set.r_width = t->output.crop.w; + t->set.r_height = t->output.crop.h; + } + t->set.r_stride = t->set.r_width * + bytes_per_pixel(t->set.r_fmt); + r_size = t->set.r_width * t->set.r_height + * fmt_to_bpp(t->set.r_fmt)/8; + r_vaddr = dma_alloc_coherent(t->dev, + r_size, + &t->set.r_paddr, + GFP_DMA | GFP_KERNEL); + if (r_vaddr == NULL) { + ret = -ENOMEM; + goto chan_done; + } + + dev_dbg(t->dev, "[0x%p]rotation:\n", (void *)t); + dev_dbg(t->dev, "[0x%p]\tformat = 0x%x\n", (void *)t, t->set.r_fmt); + dev_dbg(t->dev, "[0x%p]\twidth = %d\n", (void *)t, t->set.r_width); + dev_dbg(t->dev, "[0x%p]\theight = %d\n", (void *)t, t->set.r_height); + dev_dbg(t->dev, "[0x%p]\tpaddr = 0x%x\n", (void *)t, t->set.r_paddr); + dev_dbg(t->dev, "[0x%p]\trstride = %d\n", (void *)t, t->set.r_stride); + + ret = init_ic(ipu, t); + if (ret < 0) + goto chan_done; + ret = init_rot(ipu, t); + if (ret < 0) + goto chan_done; + ret = ipu_link_channels(ipu, t->set.ic_chan, + t->set.rot_chan); + if (ret < 0) { + t->state = STATE_LINK_CHAN_FAIL; + goto chan_done; + } + } else { + dev_err(t->dev, "[0x%p]do_task: should not be here\n", (void *)t); + return; + } + + /* irq setup */ + irq = get_irq(t); + if (irq < 0) { + t->state = STATE_NO_IRQ; + goto chan_done; + } + + dev_dbg(t->dev, "[0x%p]task irq is %d\n", (void *)t, irq); + + init_completion(&comp); + ipu_clear_irq(ipu, irq); + ret = ipu_request_irq(ipu, irq, task_irq_handler, 0, NULL, &comp); + if (ret < 0) { + t->state = STATE_IRQ_FAIL; + goto chan_done; + } + + /* enable/start channel */ + if (only_ic(t->set.mode)) { + ipu_enable_channel(ipu, t->set.ic_chan); + if (deinterlace_3_field(t)) { + ipu_enable_channel(ipu, t->set.vdi_ic_p_chan); + ipu_enable_channel(ipu, t->set.vdi_ic_n_chan); + } + + ipu_select_buffer(ipu, t->set.ic_chan, IPU_OUTPUT_BUFFER, 0); + if (t->overlay_en) { + ipu_select_buffer(ipu, t->set.ic_chan, IPU_GRAPH_IN_BUFFER, 0); + if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) + ipu_select_buffer(ipu, t->set.ic_chan, IPU_ALPHA_IN_BUFFER, 0); + } + if (deinterlace_3_field(t)) + ipu_select_multi_vdi_buffer(ipu, 0); + else + ipu_select_buffer(ipu, t->set.ic_chan, IPU_INPUT_BUFFER, 0); + } else if (only_rot(t->set.mode)) { + ipu_enable_channel(ipu, t->set.rot_chan); + ipu_select_buffer(ipu, t->set.rot_chan, IPU_OUTPUT_BUFFER, 0); + ipu_select_buffer(ipu, t->set.rot_chan, IPU_INPUT_BUFFER, 0); + } else if (ic_and_rot(t->set.mode)) { + ipu_enable_channel(ipu, t->set.rot_chan); + ipu_enable_channel(ipu, t->set.ic_chan); + if (deinterlace_3_field(t)) { + ipu_enable_channel(ipu, t->set.vdi_ic_p_chan); + ipu_enable_channel(ipu, t->set.vdi_ic_n_chan); + } + + ipu_select_buffer(ipu, t->set.rot_chan, IPU_OUTPUT_BUFFER, 0); + if (t->overlay_en) { + ipu_select_buffer(ipu, t->set.ic_chan, IPU_GRAPH_IN_BUFFER, 0); + if (t->overlay.alpha.mode == IPU_ALPHA_MODE_LOCAL) + ipu_select_buffer(ipu, t->set.ic_chan, IPU_ALPHA_IN_BUFFER, 0); + } + if (deinterlace_3_field(t)) + ipu_select_multi_vdi_buffer(ipu, 0); + else + ipu_select_buffer(ipu, t->set.ic_chan, IPU_INPUT_BUFFER, 0); + ipu_select_buffer(ipu, t->set.ic_chan, IPU_OUTPUT_BUFFER, 0); + } + + ret = wait_for_completion_timeout(&comp, msecs_to_jiffies(t->timeout)); + if (ret == 0) + t->state = STATE_IRQ_TIMEOUT; + + ipu_free_irq(ipu, irq, &comp); + if (t->set.task & IC_VF) { + ipu_clear_irq(ipu, IPU_IRQ_PRP_IN_EOF); + ipu_clear_irq(ipu, IPU_IRQ_PRP_VF_OUT_EOF); + } else if (t->set.task & IC_PP) { + ipu_clear_irq(ipu, IPU_IRQ_PP_IN_EOF); + ipu_clear_irq(ipu, IPU_IRQ_PP_OUT_EOF); + } else if (t->set.task & VDI_VF) { + ipu_clear_irq(ipu, IPU_IRQ_VDI_C_IN_EOF); + if (deinterlace_3_field(t)) { + ipu_clear_irq(ipu, IPU_IRQ_VDI_P_IN_EOF); + ipu_clear_irq(ipu, IPU_IRQ_VDI_N_IN_EOF); + } + ipu_clear_irq(ipu, IPU_IRQ_PRP_VF_OUT_EOF); + } + if (t->set.task & ROT_VF) { + ipu_clear_irq(ipu, IPU_IRQ_PRP_VF_ROT_IN_EOF); + ipu_clear_irq(ipu, IPU_IRQ_PRP_VF_ROT_OUT_EOF); + } else if (t->set.task & ROT_PP) { + ipu_clear_irq(ipu, IPU_IRQ_PP_ROT_IN_EOF); + ipu_clear_irq(ipu, IPU_IRQ_PP_ROT_OUT_EOF); + } + + if (only_ic(t->set.mode)) { + ipu_disable_channel(ipu, t->set.ic_chan, true); + if (deinterlace_3_field(t)) { + ipu_disable_channel(ipu, t->set.vdi_ic_p_chan, true); + ipu_disable_channel(ipu, t->set.vdi_ic_n_chan, true); + } + } else if (only_rot(t->set.mode)) + ipu_disable_channel(ipu, t->set.rot_chan, true); + else if (ic_and_rot(t->set.mode)) { + ipu_disable_channel(ipu, t->set.ic_chan, true); + if (deinterlace_3_field(t)) { + ipu_disable_channel(ipu, t->set.vdi_ic_p_chan, true); + ipu_disable_channel(ipu, t->set.vdi_ic_n_chan, true); + } + ipu_disable_channel(ipu, t->set.rot_chan, true); + } + +chan_done: + if (only_ic(t->set.mode)) + uninit_ic(ipu, t); + else if (only_rot(t->set.mode)) + uninit_rot(ipu, t); + else if (ic_and_rot(t->set.mode)) { + ipu_unlink_channels(ipu, t->set.ic_chan, + t->set.rot_chan); + uninit_ic(ipu, t); + uninit_rot(ipu, t); + if (r_vaddr) + dma_free_coherent(t->dev, + r_size, + r_vaddr, + t->set.r_paddr); + } + return; +} + +static int thread_loop(struct ipu_soc *ipu, int id) +{ + struct ipu_task_entry *tsk; + struct list_head *task_list = &ipu->task_list[id]; + struct mutex *task_lock = &ipu->task_lock[id]; + int ret; + + while (!kthread_should_stop()) { + int found = 0; + + ret = wait_event_interruptible(ipu->waitq[id], !list_empty(task_list)); + if (0 != ret) + continue; + + mutex_lock(task_lock); + + list_for_each_entry(tsk, task_list, node) { + if (tsk->priority == IPU_TASK_PRIORITY_HIGH) { + found = 1; + break; + } + } + + if (!found) + tsk = list_first_entry(task_list, struct ipu_task_entry, node); + + mutex_unlock(task_lock); + + do_task(ipu, tsk); + + mutex_lock(task_lock); + list_del(&tsk->node); + mutex_unlock(task_lock); + + complete(&tsk->comp); + } + + return 0; +} + +static int task_vf_thread(void *data) +{ + struct ipu_soc *ipu = data; + + thread_loop(ipu, 0); + + return 0; +} + +static int task_pp_thread(void *data) +{ + struct ipu_soc *ipu = data; + + thread_loop(ipu, 1); + + return 0; +} + +static int mxc_ipu_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static long mxc_ipu_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int __user *argp = (void __user *)arg; + int ret = 0; + + switch (cmd) { + case IPU_CHECK_TASK: + { + struct ipu_task task; + + if (copy_from_user + (&task, (struct ipu_task *) arg, + sizeof(struct ipu_task))) + return -EFAULT; + ret = ipu_check_task(&task); + if (copy_to_user((struct ipu_task *) arg, + &task, sizeof(struct ipu_task))) + return -EFAULT; + break; + } + case IPU_QUEUE_TASK: + { + struct ipu_task task; + + if (copy_from_user + (&task, (struct ipu_task *) arg, + sizeof(struct ipu_task))) + return -EFAULT; + ret = ipu_queue_task(&task); + break; + } + case IPU_ALLOC: + { + int size; + struct ipu_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(ipu_dev, size, + &mem->phy_addr, + GFP_DMA); + if (mem->cpu_addr == NULL) { + kfree(mem); + return -ENOMEM; + } + + list_add(&mem->list, &ipu_alloc_list); + + dev_dbg(ipu_dev, "allocated %d bytes @ 0x%08X\n", + mem->size, mem->phy_addr); + + if (put_user(mem->phy_addr, argp)) + return -EFAULT; + + break; + } + case IPU_FREE: + { + unsigned long offset; + struct ipu_alloc_list *mem; + + if (get_user(offset, argp)) + return -EFAULT; + + ret = -EINVAL; + list_for_each_entry(mem, &ipu_alloc_list, list) { + if (mem->phy_addr == offset) { + list_del(&mem->list); + dma_free_coherent(ipu_dev, + mem->size, + mem->cpu_addr, + mem->phy_addr); + kfree(mem); + ret = 0; + break; + } + } + + break; + } + default: + break; + } + return ret; +} + +static int mxc_ipu_mmap(struct file *file, struct vm_area_struct *vma) +{ + bool found = false; + u32 len; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + struct ipu_alloc_list *mem; + + list_for_each_entry(mem, &ipu_alloc_list, list) { + if (offset == mem->phy_addr) { + found = true; + len = mem->size; + break; + } + } + if (!found) + return -EINVAL; + + if (vma->vm_end - vma->vm_start > len) + return -EINVAL; + + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) { + printk(KERN_ERR + "mmap failed!\n"); + return -ENOBUFS; + } + return 0; +} + +static int mxc_ipu_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static struct file_operations mxc_ipu_fops = { + .owner = THIS_MODULE, + .open = mxc_ipu_open, + .mmap = mxc_ipu_mmap, + .release = mxc_ipu_release, + .unlocked_ioctl = mxc_ipu_ioctl, +}; + +int register_ipu_device(struct ipu_soc *ipu, int id) +{ + int ret = 0; + + if (!major) { + major = register_chrdev(0, "mxc_ipu", &mxc_ipu_fops); + if (major < 0) { + printk(KERN_ERR "Unable to register mxc_ipu as a char device\n"); + ret = major; + goto register_cdev_fail; + } + + ipu_class = class_create(THIS_MODULE, "mxc_ipu"); + if (IS_ERR(ipu_class)) { + ret = PTR_ERR(ipu_class); + goto ipu_class_fail; + } + + ipu_dev = device_create(ipu_class, NULL, MKDEV(major, 0), + NULL, "mxc_ipu"); + if (IS_ERR(ipu_dev)) { + ret = PTR_ERR(ipu_dev); + goto dev_create_fail; + } + ipu_dev->dma_mask = kmalloc(sizeof(*ipu_dev->dma_mask), GFP_KERNEL); + *ipu_dev->dma_mask = DMA_BIT_MASK(32); + ipu_dev->coherent_dma_mask = DMA_BIT_MASK(32); + } + + INIT_LIST_HEAD(&ipu->task_list[0]); + INIT_LIST_HEAD(&ipu->task_list[1]); + init_waitqueue_head(&ipu->waitq[0]); + init_waitqueue_head(&ipu->waitq[1]); + mutex_init(&ipu->task_lock[0]); + mutex_init(&ipu->task_lock[1]); + ipu->thread[0] = kthread_run(task_vf_thread, ipu, + "ipu%d_process-vf", id); + if (IS_ERR(ipu->thread[0])) { + ret = PTR_ERR(ipu->thread[0]); + goto kthread0_fail; + } + + ipu->thread[1] = kthread_run(task_pp_thread, ipu, + "ipu%d_process-pp", id); + if (IS_ERR(ipu->thread[1])) { + ret = PTR_ERR(ipu->thread[1]); + goto kthread1_fail; + } + + return ret; + +kthread1_fail: + kthread_stop(ipu->thread[0]); +kthread0_fail: + if (id == 0) + device_destroy(ipu_class, MKDEV(major, 0)); +dev_create_fail: + if (id == 0) { + class_destroy(ipu_class); + unregister_chrdev(major, "mxc_ipu"); + } +ipu_class_fail: + if (id == 0) + unregister_chrdev(major, "mxc_ipu"); +register_cdev_fail: + return ret; +} + +void unregister_ipu_device(struct ipu_soc *ipu, int id) +{ + kthread_stop(ipu->thread[0]); + kthread_stop(ipu->thread[1]); + if (major) { + device_destroy(ipu_class, MKDEV(major, 0)); + class_destroy(ipu_class); + unregister_chrdev(major, "mxc_ipu"); + major = 0; + } +} diff --git a/drivers/mxc/ipu3/ipu_disp.c b/drivers/mxc/ipu3/ipu_disp.c new file mode 100644 index 00000000000..9867d5717df --- /dev/null +++ b/drivers/mxc/ipu3/ipu_disp.c @@ -0,0 +1,2063 @@ +/* + * 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 + */ + +/*! + * @file ipu_disp.c + * + * @brief IPU display submodule API functions + * + * @ingroup IPU + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/spinlock.h> +#include <linux/io.h> +#include <linux/ipu.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <asm/atomic.h> +#include <mach/clock.h> +#include "ipu_prv.h" +#include "ipu_regs.h" +#include "ipu_param_mem.h" + +struct dp_csc_param_t { + int mode; + void *coeff; +}; + +#define SYNC_WAVE 0 +#define ASYNC_SER_WAVE 6 + +/* DC display ID assignments */ +#define DC_DISP_ID_SYNC(di) (di) +#define DC_DISP_ID_SERIAL 2 +#define DC_DISP_ID_ASYNC 3 + +static inline struct ipu_soc *pixelclk2ipu(struct clk *clk) +{ + struct ipu_soc *ipu; + struct clk *base = clk - clk->id; + + ipu = container_of(base, struct ipu_soc, pixel_clk[0]); + + return ipu; +} + +static unsigned long _ipu_pixel_clk_get_rate(struct clk *clk) +{ + struct ipu_soc *ipu = pixelclk2ipu(clk); + u32 div = ipu_di_read(ipu, clk->id, DI_BS_CLKGEN0); + if (div == 0) + return 0; + return (clk_get_rate(clk->parent) * 16) / div; +} + +static unsigned long _ipu_pixel_clk_round_rate(struct clk *clk, unsigned long rate) +{ + u32 div, div1; + u32 parent_rate = clk_get_rate(clk->parent) * 16; + /* + * Calculate divider + * Fractional part is 4 bits, + * so simply multiply by 2^4 to get fractional part. + */ + div = parent_rate / rate; + + if (div < 0x10) /* Min DI disp clock divider is 1 */ + div = 0x10; + if (div & ~0xFEF) + div &= 0xFF8; + else { + div1 = div & 0xFE0; + if ((parent_rate / div1 - parent_rate / div) < rate / 4) + div = div1; + else + div &= 0xFF8; + } + return parent_rate / div; +} + +static int _ipu_pixel_clk_set_rate(struct clk *clk, unsigned long rate) +{ + struct ipu_soc *ipu = pixelclk2ipu(clk); + u32 div = (clk_get_rate(clk->parent) * 16) / rate; + + ipu_di_write(ipu, clk->id, div, DI_BS_CLKGEN0); + + /* Setup pixel clock timing */ + /* FIXME: needs to be more flexible */ + /* Down time is half of period */ + ipu_di_write(ipu, clk->id, (div / 16) << 16, DI_BS_CLKGEN1); + + return 0; +} + +static int _ipu_pixel_clk_enable(struct clk *clk) +{ + struct ipu_soc *ipu = pixelclk2ipu(clk); + u32 disp_gen = ipu_cm_read(ipu, IPU_DISP_GEN); + disp_gen |= clk->id ? DI1_COUNTER_RELEASE : DI0_COUNTER_RELEASE; + ipu_cm_write(ipu, disp_gen, IPU_DISP_GEN); + + return 0; +} + +static void _ipu_pixel_clk_disable(struct clk *clk) +{ + struct ipu_soc *ipu = pixelclk2ipu(clk); + + u32 disp_gen = ipu_cm_read(ipu, IPU_DISP_GEN); + disp_gen &= clk->id ? ~DI1_COUNTER_RELEASE : ~DI0_COUNTER_RELEASE; + ipu_cm_write(ipu, disp_gen, IPU_DISP_GEN); +} + +static int _ipu_pixel_clk_set_parent(struct clk *clk, struct clk *parent) +{ + struct ipu_soc *ipu = pixelclk2ipu(clk); + u32 di_gen; + + di_gen = ipu_di_read(ipu, clk->id, DI_GENERAL); + if (parent == ipu->ipu_clk) + di_gen &= ~DI_GEN_DI_CLK_EXT; + else if (!IS_ERR(ipu->di_clk[clk->id]) && parent == ipu->di_clk[clk->id]) + di_gen |= DI_GEN_DI_CLK_EXT; + else { + return -EINVAL; + } + + ipu_di_write(ipu, clk->id, di_gen, DI_GENERAL); + return 0; +} + +#ifdef CONFIG_CLK_DEBUG +#define __INIT_CLK_DEBUG(n) .name = #n, +#else +#define __INIT_CLK_DEBUG(n) +#endif +struct clk ipu_pixel_clk[] = { + { + __INIT_CLK_DEBUG(pixel_clk_0) + .id = 0, + .get_rate = _ipu_pixel_clk_get_rate, + .set_rate = _ipu_pixel_clk_set_rate, + .round_rate = _ipu_pixel_clk_round_rate, + .set_parent = _ipu_pixel_clk_set_parent, + .enable = _ipu_pixel_clk_enable, + .disable = _ipu_pixel_clk_disable, + }, + { + __INIT_CLK_DEBUG(pixel_clk_1) + .id = 1, + .get_rate = _ipu_pixel_clk_get_rate, + .set_rate = _ipu_pixel_clk_set_rate, + .round_rate = _ipu_pixel_clk_round_rate, + .set_parent = _ipu_pixel_clk_set_parent, + .enable = _ipu_pixel_clk_enable, + .disable = _ipu_pixel_clk_disable, + }, +}; + +struct clk_lookup ipu_lookups[] = { + { + .dev_id = NULL, + .con_id = "pixel_clk_0", + }, + { + .dev_id = NULL, + .con_id = "pixel_clk_1", + }, +}; + +int dmfc_type_setup; + +void _ipu_dmfc_init(struct ipu_soc *ipu, int dmfc_type, int first) +{ + u32 dmfc_wr_chan, dmfc_dp_chan; + + if (first) { + if (dmfc_type_setup > dmfc_type) + dmfc_type = dmfc_type_setup; + else + dmfc_type_setup = dmfc_type; + + /* disable DMFC-IC channel*/ + ipu_dmfc_write(ipu, 0x2, DMFC_IC_CTRL); + } else if (dmfc_type_setup >= DMFC_HIGH_RESOLUTION_DC) { + dev_dbg(ipu->dev, "DMFC high resolution has set, will not change\n"); + return; + } else + dmfc_type_setup = dmfc_type; + + if (dmfc_type == DMFC_HIGH_RESOLUTION_DC) { + /* 1 - segment 0~3; + * 5B - segement 4, 5; + * 5F - segement 6, 7; + * 1C, 2C and 6B, 6F unused; + */ + dev_info(ipu->dev, "IPU DMFC DC HIGH RESOLUTION: 1(0~3), 5B(4,5), 5F(6,7)\n"); + dmfc_wr_chan = 0x00000088; + dmfc_dp_chan = 0x00009694; + ipu->dmfc_size_28 = 256*4; + ipu->dmfc_size_29 = 0; + ipu->dmfc_size_24 = 0; + ipu->dmfc_size_27 = 128*4; + ipu->dmfc_size_23 = 128*4; + } else if (dmfc_type == DMFC_HIGH_RESOLUTION_DP) { + /* 1 - segment 0, 1; + * 5B - segement 2~5; + * 5F - segement 6,7; + * 1C, 2C and 6B, 6F unused; + */ + dev_info(ipu->dev, "IPU DMFC DP HIGH RESOLUTION: 1(0,1), 5B(2~5), 5F(6,7)\n"); + dmfc_wr_chan = 0x00000090; + dmfc_dp_chan = 0x0000968a; + ipu->dmfc_size_28 = 128*4; + ipu->dmfc_size_29 = 0; + ipu->dmfc_size_24 = 0; + ipu->dmfc_size_27 = 128*4; + ipu->dmfc_size_23 = 256*4; + } else if (dmfc_type == DMFC_HIGH_RESOLUTION_ONLY_DP) { + /* 5B - segement 0~3; + * 5F - segement 4~7; + * 1, 1C, 2C and 6B, 6F unused; + */ + dev_info(ipu->dev, "IPU DMFC ONLY-DP HIGH RESOLUTION: 5B(0~3), 5F(4~7)\n"); + dmfc_wr_chan = 0x00000000; + dmfc_dp_chan = 0x00008c88; + ipu->dmfc_size_28 = 0; + ipu->dmfc_size_29 = 0; + ipu->dmfc_size_24 = 0; + ipu->dmfc_size_27 = 256*4; + ipu->dmfc_size_23 = 256*4; + } else { + /* 1 - segment 0, 1; + * 5B - segement 4, 5; + * 5F - segement 6, 7; + * 1C, 2C and 6B, 6F unused; + */ + dev_info(ipu->dev, "IPU DMFC NORMAL mode: 1(0~1), 5B(4,5), 5F(6,7)\n"); + dmfc_wr_chan = 0x00000090; + dmfc_dp_chan = 0x00009694; + ipu->dmfc_size_28 = 128*4; + ipu->dmfc_size_29 = 0; + ipu->dmfc_size_24 = 0; + ipu->dmfc_size_27 = 128*4; + ipu->dmfc_size_23 = 128*4; + } + ipu_dmfc_write(ipu, dmfc_wr_chan, DMFC_WR_CHAN); + ipu_dmfc_write(ipu, 0x202020F6, DMFC_WR_CHAN_DEF); + ipu_dmfc_write(ipu, dmfc_dp_chan, DMFC_DP_CHAN); + /* Enable chan 5 watermark set at 5 bursts and clear at 7 bursts */ + ipu_dmfc_write(ipu, 0x2020F6F6, DMFC_DP_CHAN_DEF); +} + +static int __init dmfc_setup(char *options) +{ + get_option(&options, &dmfc_type_setup); + if (dmfc_type_setup > DMFC_HIGH_RESOLUTION_ONLY_DP) + dmfc_type_setup = DMFC_HIGH_RESOLUTION_ONLY_DP; + return 1; +} +__setup("dmfc=", dmfc_setup); + +void _ipu_dmfc_set_wait4eot(struct ipu_soc *ipu, int dma_chan, int width) +{ + u32 dmfc_gen1 = ipu_dmfc_read(ipu, DMFC_GENERAL1); + + if (width >= HIGH_RESOLUTION_WIDTH) { + if (dma_chan == 23) + _ipu_dmfc_init(ipu, DMFC_HIGH_RESOLUTION_DP, 0); + else if (dma_chan == 28) + _ipu_dmfc_init(ipu, DMFC_HIGH_RESOLUTION_DC, 0); + } + + if (dma_chan == 23) { /*5B*/ + if (ipu->dmfc_size_23/width > 3) + dmfc_gen1 |= 1UL << 20; + else + dmfc_gen1 &= ~(1UL << 20); + } else if (dma_chan == 24) { /*6B*/ + if (ipu->dmfc_size_24/width > 1) + dmfc_gen1 |= 1UL << 22; + else + dmfc_gen1 &= ~(1UL << 22); + } else if (dma_chan == 27) { /*5F*/ + if (ipu->dmfc_size_27/width > 2) + dmfc_gen1 |= 1UL << 21; + else + dmfc_gen1 &= ~(1UL << 21); + } else if (dma_chan == 28) { /*1*/ + if (ipu->dmfc_size_28/width > 2) + dmfc_gen1 |= 1UL << 16; + else + dmfc_gen1 &= ~(1UL << 16); + } else if (dma_chan == 29) { /*6F*/ + if (ipu->dmfc_size_29/width > 1) + dmfc_gen1 |= 1UL << 23; + else + dmfc_gen1 &= ~(1UL << 23); + } + + ipu_dmfc_write(ipu, dmfc_gen1, DMFC_GENERAL1); +} + +void _ipu_dmfc_set_burst_size(struct ipu_soc *ipu, int dma_chan, int burst_size) +{ + u32 dmfc_wr_chan = ipu_dmfc_read(ipu, DMFC_WR_CHAN); + u32 dmfc_dp_chan = ipu_dmfc_read(ipu, DMFC_DP_CHAN); + int dmfc_bs = 0; + + switch (burst_size) { + case 64: + dmfc_bs = 0x40; + break; + case 32: + case 20: + dmfc_bs = 0x80; + break; + case 16: + dmfc_bs = 0xc0; + break; + default: + dev_err(ipu->dev, "Unsupported burst size %d\n", + burst_size); + return; + } + + if (dma_chan == 23) { /*5B*/ + dmfc_dp_chan &= ~(0xc0); + dmfc_dp_chan |= dmfc_bs; + } else if (dma_chan == 27) { /*5F*/ + dmfc_dp_chan &= ~(0xc000); + dmfc_dp_chan |= (dmfc_bs << 8); + } else if (dma_chan == 28) { /*1*/ + dmfc_wr_chan &= ~(0xc0); + dmfc_wr_chan |= dmfc_bs; + } + + ipu_dmfc_write(ipu, dmfc_wr_chan, DMFC_WR_CHAN); + ipu_dmfc_write(ipu, dmfc_dp_chan, DMFC_DP_CHAN); +} + +static void _ipu_di_data_wave_config(struct ipu_soc *ipu, + int di, int wave_gen, + int access_size, int component_size) +{ + u32 reg; + reg = (access_size << DI_DW_GEN_ACCESS_SIZE_OFFSET) | + (component_size << DI_DW_GEN_COMPONENT_SIZE_OFFSET); + ipu_di_write(ipu, di, reg, DI_DW_GEN(wave_gen)); +} + +static void _ipu_di_data_pin_config(struct ipu_soc *ipu, + int di, int wave_gen, int di_pin, int set, + int up, int down) +{ + u32 reg; + + reg = ipu_di_read(ipu, di, DI_DW_GEN(wave_gen)); + reg &= ~(0x3 << (di_pin * 2)); + reg |= set << (di_pin * 2); + ipu_di_write(ipu, di, reg, DI_DW_GEN(wave_gen)); + + ipu_di_write(ipu, di, (down << 16) | up, DI_DW_SET(wave_gen, set)); +} + +static void _ipu_di_sync_config(struct ipu_soc *ipu, + int di, int wave_gen, + int run_count, int run_src, + int offset_count, int offset_src, + int repeat_count, int cnt_clr_src, + int cnt_polarity_gen_en, + int cnt_polarity_clr_src, + int cnt_polarity_trigger_src, + int cnt_up, int cnt_down) +{ + u32 reg; + + if ((run_count >= 0x1000) || (offset_count >= 0x1000) || (repeat_count >= 0x1000) || + (cnt_up >= 0x400) || (cnt_down >= 0x400)) { + dev_err(ipu->dev, "DI%d counters out of range.\n", di); + return; + } + + reg = (run_count << 19) | (++run_src << 16) | + (offset_count << 3) | ++offset_src; + ipu_di_write(ipu, di, reg, DI_SW_GEN0(wave_gen)); + reg = (cnt_polarity_gen_en << 29) | (++cnt_clr_src << 25) | + (++cnt_polarity_trigger_src << 12) | (++cnt_polarity_clr_src << 9); + reg |= (cnt_down << 16) | cnt_up; + if (repeat_count == 0) { + /* Enable auto reload */ + reg |= 0x10000000; + } + ipu_di_write(ipu, di, reg, DI_SW_GEN1(wave_gen)); + reg = ipu_di_read(ipu, di, DI_STP_REP(wave_gen)); + reg &= ~(0xFFFF << (16 * ((wave_gen - 1) & 0x1))); + reg |= repeat_count << (16 * ((wave_gen - 1) & 0x1)); + ipu_di_write(ipu, di, reg, DI_STP_REP(wave_gen)); +} + +static void _ipu_dc_map_link(struct ipu_soc *ipu, + int current_map, + int base_map_0, int buf_num_0, + int base_map_1, int buf_num_1, + int base_map_2, int buf_num_2) +{ + int ptr_0 = base_map_0 * 3 + buf_num_0; + int ptr_1 = base_map_1 * 3 + buf_num_1; + int ptr_2 = base_map_2 * 3 + buf_num_2; + int ptr; + u32 reg; + ptr = (ptr_2 << 10) + (ptr_1 << 5) + ptr_0; + + reg = ipu_dc_read(ipu, DC_MAP_CONF_PTR(current_map)); + reg &= ~(0x1F << ((16 * (current_map & 0x1)))); + reg |= ptr << ((16 * (current_map & 0x1))); + ipu_dc_write(ipu, reg, DC_MAP_CONF_PTR(current_map)); +} + +static void _ipu_dc_map_config(struct ipu_soc *ipu, + int map, int byte_num, int offset, int mask) +{ + int ptr = map * 3 + byte_num; + u32 reg; + + reg = ipu_dc_read(ipu, DC_MAP_CONF_VAL(ptr)); + reg &= ~(0xFFFF << (16 * (ptr & 0x1))); + reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1)); + ipu_dc_write(ipu, reg, DC_MAP_CONF_VAL(ptr)); + + reg = ipu_dc_read(ipu, DC_MAP_CONF_PTR(map)); + reg &= ~(0x1F << ((16 * (map & 0x1)) + (5 * byte_num))); + reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num)); + ipu_dc_write(ipu, reg, DC_MAP_CONF_PTR(map)); +} + +static void _ipu_dc_map_clear(struct ipu_soc *ipu, int map) +{ + u32 reg = ipu_dc_read(ipu, DC_MAP_CONF_PTR(map)); + ipu_dc_write(ipu, reg & ~(0xFFFF << (16 * (map & 0x1))), + DC_MAP_CONF_PTR(map)); +} + +static void _ipu_dc_write_tmpl(struct ipu_soc *ipu, + int word, u32 opcode, u32 operand, int map, + int wave, int glue, int sync, int stop) +{ + u32 reg; + + if (opcode == WRG) { + reg = sync; + reg |= (glue << 4); + reg |= (++wave << 11); + reg |= ((operand & 0x1FFFF) << 15); + ipu_dc_tmpl_write(ipu, reg, word * 2); + + reg = (operand >> 17); + reg |= opcode << 7; + reg |= (stop << 9); + ipu_dc_tmpl_write(ipu, reg, word * 2 + 1); + } else { + reg = sync; + reg |= (glue << 4); + reg |= (++wave << 11); + reg |= (++map << 15); + reg |= (operand << 20) & 0xFFF00000; + ipu_dc_tmpl_write(ipu, reg, word * 2); + + reg = (operand >> 12); + reg |= opcode << 4; + reg |= (stop << 9); + ipu_dc_tmpl_write(ipu, reg, word * 2 + 1); + } +} + +static void _ipu_dc_link_event(struct ipu_soc *ipu, + int chan, int event, int addr, int priority) +{ + u32 reg; + u32 address_shift; + if (event < DC_EVEN_UGDE0) { + reg = ipu_dc_read(ipu, DC_RL_CH(chan, event)); + reg &= ~(0xFFFF << (16 * (event & 0x1))); + reg |= ((addr << 8) | priority) << (16 * (event & 0x1)); + ipu_dc_write(ipu, reg, DC_RL_CH(chan, event)); + } else { + reg = ipu_dc_read(ipu, DC_UGDE_0((event - DC_EVEN_UGDE0) / 2)); + if ((event - DC_EVEN_UGDE0) & 0x1) { + reg &= ~(0x2FF << 16); + reg |= (addr << 16); + reg |= priority ? (2 << 24) : 0x0; + } else { + reg &= ~0xFC00FFFF; + if (priority) + chan = (chan >> 1) + + ((((chan & 0x1) + ((chan & 0x2) >> 1))) | (chan >> 3)); + else + chan = 0x7; + address_shift = ((event - DC_EVEN_UGDE0) >> 1) ? 7 : 8; + reg |= (addr << address_shift) | (priority << 3) | chan; + } + ipu_dc_write(ipu, reg, DC_UGDE_0((event - DC_EVEN_UGDE0) / 2)); + } +} + +/* Y = R * 1.200 + G * 2.343 + B * .453 + 0.250; + U = R * -.672 + G * -1.328 + B * 2.000 + 512.250.; + V = R * 2.000 + G * -1.672 + B * -.328 + 512.250.;*/ +static const int rgb2ycbcr_coeff[5][3] = { + {0x4D, 0x96, 0x1D}, + {-0x2B, -0x55, 0x80}, + {0x80, -0x6B, -0x15}, + {0x0000, 0x0200, 0x0200}, /* B0, B1, B2 */ + {0x2, 0x2, 0x2}, /* S0, S1, S2 */ +}; + +/* R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128)); + G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128)); + B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128); */ +static const int ycbcr2rgb_coeff[5][3] = { + {0x095, 0x000, 0x0CC}, + {0x095, 0x3CE, 0x398}, + {0x095, 0x0FF, 0x000}, + {0x3E42, 0x010A, 0x3DD6}, /*B0,B1,B2 */ + {0x1, 0x1, 0x1}, /*S0,S1,S2 */ +}; + +#define mask_a(a) ((u32)(a) & 0x3FF) +#define mask_b(b) ((u32)(b) & 0x3FFF) + +/* Pls keep S0, S1 and S2 as 0x2 by using this convertion */ +static int _rgb_to_yuv(int n, int red, int green, int blue) +{ + int c; + c = red * rgb2ycbcr_coeff[n][0]; + c += green * rgb2ycbcr_coeff[n][1]; + c += blue * rgb2ycbcr_coeff[n][2]; + c /= 16; + c += rgb2ycbcr_coeff[3][n] * 4; + c += 8; + c /= 16; + if (c < 0) + c = 0; + if (c > 255) + c = 255; + return c; +} + +/* + * Row is for BG: RGB2YUV YUV2RGB RGB2RGB YUV2YUV CSC_NONE + * Column is for FG: RGB2YUV YUV2RGB RGB2RGB YUV2YUV CSC_NONE + */ +static struct dp_csc_param_t dp_csc_array[CSC_NUM][CSC_NUM] = { +{{DP_COM_CONF_CSC_DEF_BOTH, &rgb2ycbcr_coeff}, {0, 0}, {0, 0}, {DP_COM_CONF_CSC_DEF_BG, &rgb2ycbcr_coeff}, {DP_COM_CONF_CSC_DEF_BG, &rgb2ycbcr_coeff} }, +{{0, 0}, {DP_COM_CONF_CSC_DEF_BOTH, &ycbcr2rgb_coeff}, {DP_COM_CONF_CSC_DEF_BG, &ycbcr2rgb_coeff}, {0, 0}, {DP_COM_CONF_CSC_DEF_BG, &ycbcr2rgb_coeff} }, +{{0, 0}, {DP_COM_CONF_CSC_DEF_FG, &ycbcr2rgb_coeff}, {0, 0}, {0, 0}, {0, 0} }, +{{DP_COM_CONF_CSC_DEF_FG, &rgb2ycbcr_coeff}, {0, 0}, {0, 0}, {0, 0}, {0, 0} }, +{{DP_COM_CONF_CSC_DEF_FG, &rgb2ycbcr_coeff}, {DP_COM_CONF_CSC_DEF_FG, &ycbcr2rgb_coeff}, {0, 0}, {0, 0}, {0, 0} } +}; + +void __ipu_dp_csc_setup(struct ipu_soc *ipu, + int dp, struct dp_csc_param_t dp_csc_param, + bool srm_mode_update) +{ + u32 reg; + const int (*coeff)[5][3]; + + if (dp_csc_param.mode >= 0) { + reg = ipu_dp_read(ipu, DP_COM_CONF(dp)); + reg &= ~DP_COM_CONF_CSC_DEF_MASK; + reg |= dp_csc_param.mode; + ipu_dp_write(ipu, reg, DP_COM_CONF(dp)); + } + + coeff = dp_csc_param.coeff; + + if (coeff) { + ipu_dp_write(ipu, mask_a((*coeff)[0][0]) | + (mask_a((*coeff)[0][1]) << 16), DP_CSC_A_0(dp)); + ipu_dp_write(ipu, mask_a((*coeff)[0][2]) | + (mask_a((*coeff)[1][0]) << 16), DP_CSC_A_1(dp)); + ipu_dp_write(ipu, mask_a((*coeff)[1][1]) | + (mask_a((*coeff)[1][2]) << 16), DP_CSC_A_2(dp)); + ipu_dp_write(ipu, mask_a((*coeff)[2][0]) | + (mask_a((*coeff)[2][1]) << 16), DP_CSC_A_3(dp)); + ipu_dp_write(ipu, mask_a((*coeff)[2][2]) | + (mask_b((*coeff)[3][0]) << 16) | + ((*coeff)[4][0] << 30), DP_CSC_0(dp)); + ipu_dp_write(ipu, mask_b((*coeff)[3][1]) | ((*coeff)[4][1] << 14) | + (mask_b((*coeff)[3][2]) << 16) | + ((*coeff)[4][2] << 30), DP_CSC_1(dp)); + } + + if (srm_mode_update) { + reg = ipu_cm_read(ipu, IPU_SRM_PRI2) | 0x8; + ipu_cm_write(ipu, reg, IPU_SRM_PRI2); + } +} + +int _ipu_dp_init(struct ipu_soc *ipu, + ipu_channel_t channel, uint32_t in_pixel_fmt, + uint32_t out_pixel_fmt) +{ + int in_fmt, out_fmt; + int dp; + int partial = false; + uint32_t reg; + + if (channel == MEM_FG_SYNC) { + dp = DP_SYNC; + partial = true; + } else if (channel == MEM_BG_SYNC) { + dp = DP_SYNC; + partial = false; + } else if (channel == MEM_BG_ASYNC0) { + dp = DP_ASYNC0; + partial = false; + } else { + return -EINVAL; + } + + in_fmt = format_to_colorspace(in_pixel_fmt); + out_fmt = format_to_colorspace(out_pixel_fmt); + + if (partial) { + if (in_fmt == RGB) { + if (out_fmt == RGB) + ipu->fg_csc_type = RGB2RGB; + else + ipu->fg_csc_type = RGB2YUV; + } else { + if (out_fmt == RGB) + ipu->fg_csc_type = YUV2RGB; + else + ipu->fg_csc_type = YUV2YUV; + } + } else { + if (in_fmt == RGB) { + if (out_fmt == RGB) + ipu->bg_csc_type = RGB2RGB; + else + ipu->bg_csc_type = RGB2YUV; + } else { + if (out_fmt == RGB) + ipu->bg_csc_type = YUV2RGB; + else + ipu->bg_csc_type = YUV2YUV; + } + } + + /* Transform color key from rgb to yuv if CSC is enabled */ + reg = ipu_dp_read(ipu, DP_COM_CONF(dp)); + if (ipu->color_key_4rgb && (reg & DP_COM_CONF_GWCKE) && + (((ipu->fg_csc_type == RGB2YUV) && (ipu->bg_csc_type == YUV2YUV)) || + ((ipu->fg_csc_type == YUV2YUV) && (ipu->bg_csc_type == RGB2YUV)) || + ((ipu->fg_csc_type == YUV2YUV) && (ipu->bg_csc_type == YUV2YUV)) || + ((ipu->fg_csc_type == YUV2RGB) && (ipu->bg_csc_type == YUV2RGB)))) { + int red, green, blue; + int y, u, v; + uint32_t color_key = ipu_dp_read(ipu, DP_GRAPH_WIND_CTRL(dp)) & 0xFFFFFFL; + + dev_dbg(ipu->dev, "_ipu_dp_init color key 0x%x need change to yuv fmt!\n", color_key); + + red = (color_key >> 16) & 0xFF; + green = (color_key >> 8) & 0xFF; + blue = color_key & 0xFF; + + y = _rgb_to_yuv(0, red, green, blue); + u = _rgb_to_yuv(1, red, green, blue); + v = _rgb_to_yuv(2, red, green, blue); + color_key = (y << 16) | (u << 8) | v; + + reg = ipu_dp_read(ipu, DP_GRAPH_WIND_CTRL(dp)) & 0xFF000000L; + ipu_dp_write(ipu, reg | color_key, DP_GRAPH_WIND_CTRL(dp)); + ipu->color_key_4rgb = false; + + dev_dbg(ipu->dev, "_ipu_dp_init color key change to yuv fmt 0x%x!\n", color_key); + } + + __ipu_dp_csc_setup(ipu, dp, dp_csc_array[ipu->bg_csc_type][ipu->fg_csc_type], true); + + return 0; +} + +void _ipu_dp_uninit(struct ipu_soc *ipu, ipu_channel_t channel) +{ + int dp; + int partial = false; + + if (channel == MEM_FG_SYNC) { + dp = DP_SYNC; + partial = true; + } else if (channel == MEM_BG_SYNC) { + dp = DP_SYNC; + partial = false; + } else if (channel == MEM_BG_ASYNC0) { + dp = DP_ASYNC0; + partial = false; + } else { + return; + } + + if (partial) + ipu->fg_csc_type = CSC_NONE; + else + ipu->bg_csc_type = CSC_NONE; + + __ipu_dp_csc_setup(ipu, dp, dp_csc_array[ipu->bg_csc_type][ipu->fg_csc_type], false); +} + +void _ipu_dc_init(struct ipu_soc *ipu, int dc_chan, int di, bool interlaced, uint32_t pixel_fmt) +{ + u32 reg = 0; + + if ((dc_chan == 1) || (dc_chan == 5)) { + if (interlaced) { + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NL, 0, 3); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_EOL, 0, 2); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_DATA, 0, 1); + } else { + if (di) { + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NL, 2, 3); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_EOL, 3, 2); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_DATA, 4, 1); + if ((pixel_fmt == IPU_PIX_FMT_YUYV) || + (pixel_fmt == IPU_PIX_FMT_UYVY) || + (pixel_fmt == IPU_PIX_FMT_YVYU) || + (pixel_fmt == IPU_PIX_FMT_VYUY)) { + _ipu_dc_link_event(ipu, dc_chan, DC_ODD_UGDE1, 9, 5); + _ipu_dc_link_event(ipu, dc_chan, DC_EVEN_UGDE1, 8, 5); + } + } else { + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NL, 5, 3); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_EOL, 6, 2); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_DATA, 7, 1); + if ((pixel_fmt == IPU_PIX_FMT_YUYV) || + (pixel_fmt == IPU_PIX_FMT_UYVY) || + (pixel_fmt == IPU_PIX_FMT_YVYU) || + (pixel_fmt == IPU_PIX_FMT_VYUY)) { + _ipu_dc_link_event(ipu, dc_chan, DC_ODD_UGDE0, 10, 5); + _ipu_dc_link_event(ipu, dc_chan, DC_EVEN_UGDE0, 11, 5); + } + } + } + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NF, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NFIELD, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_EOF, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_EOFIELD, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_CHAN, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_ADDR, 0, 0); + + reg = 0x2; + reg |= DC_DISP_ID_SYNC(di) << DC_WR_CH_CONF_PROG_DISP_ID_OFFSET; + reg |= di << 2; + if (interlaced) + reg |= DC_WR_CH_CONF_FIELD_MODE; + } else if ((dc_chan == 8) || (dc_chan == 9)) { + /* async channels */ + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_DATA_W_0, 0x64, 1); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_DATA_W_1, 0x64, 1); + + reg = 0x3; + reg |= DC_DISP_ID_SERIAL << DC_WR_CH_CONF_PROG_DISP_ID_OFFSET; + } + ipu_dc_write(ipu, reg, DC_WR_CH_CONF(dc_chan)); + + ipu_dc_write(ipu, 0x00000000, DC_WR_CH_ADDR(dc_chan)); + + ipu_dc_write(ipu, 0x00000084, DC_GEN); +} + +void _ipu_dc_uninit(struct ipu_soc *ipu, int dc_chan) +{ + if ((dc_chan == 1) || (dc_chan == 5)) { + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NL, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_EOL, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_DATA, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NF, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NFIELD, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_EOF, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_EOFIELD, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_CHAN, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_ADDR, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_ODD_UGDE0, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_EVEN_UGDE0, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_ODD_UGDE1, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_EVEN_UGDE1, 0, 0); + } else if ((dc_chan == 8) || (dc_chan == 9)) { + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_ADDR_W_0, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_ADDR_W_1, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_CHAN_W_0, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_CHAN_W_1, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_DATA_W_0, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_DATA_W_1, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_ADDR_R_0, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_ADDR_R_1, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_CHAN_R_0, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_CHAN_R_1, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_DATA_R_0, 0, 0); + _ipu_dc_link_event(ipu, dc_chan, DC_EVT_NEW_DATA_R_1, 0, 0); + } +} + +int _ipu_disp_chan_is_interlaced(struct ipu_soc *ipu, ipu_channel_t channel) +{ + if (channel == MEM_DC_SYNC) + return !!(ipu_dc_read(ipu, DC_WR_CH_CONF_1) & + DC_WR_CH_CONF_FIELD_MODE); + else if ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC)) + return !!(ipu_dc_read(ipu, DC_WR_CH_CONF_5) & + DC_WR_CH_CONF_FIELD_MODE); + return 0; +} + +void _ipu_dp_dc_enable(struct ipu_soc *ipu, ipu_channel_t channel) +{ + int di; + uint32_t reg; + uint32_t dc_chan; + int irq = 0; + + if (channel == MEM_FG_SYNC) + irq = IPU_IRQ_DP_SF_END; + else if (channel == MEM_DC_SYNC) + dc_chan = 1; + else if (channel == MEM_BG_SYNC) + dc_chan = 5; + else + return; + + if (channel == MEM_FG_SYNC) { + /* Enable FG channel */ + reg = ipu_dp_read(ipu, DP_COM_CONF(DP_SYNC)); + ipu_dp_write(ipu, reg | DP_COM_CONF_FG_EN, DP_COM_CONF(DP_SYNC)); + + reg = ipu_cm_read(ipu, IPU_SRM_PRI2) | 0x8; + ipu_cm_write(ipu, reg, IPU_SRM_PRI2); + return; + } + + di = ipu->dc_di_assignment[dc_chan]; + + /* Make sure other DC sync channel is not assigned same DI */ + reg = ipu_dc_read(ipu, DC_WR_CH_CONF(6 - dc_chan)); + if ((di << 2) == (reg & DC_WR_CH_CONF_PROG_DI_ID)) { + reg &= ~DC_WR_CH_CONF_PROG_DI_ID; + reg |= di ? 0 : DC_WR_CH_CONF_PROG_DI_ID; + ipu_dc_write(ipu, reg, DC_WR_CH_CONF(6 - dc_chan)); + } + + reg = ipu_dc_read(ipu, DC_WR_CH_CONF(dc_chan)); + reg |= 4 << DC_WR_CH_CONF_PROG_TYPE_OFFSET; + ipu_dc_write(ipu, reg, DC_WR_CH_CONF(dc_chan)); + + clk_enable(&ipu->pixel_clk[di]); +} + +static irqreturn_t dc_irq_handler(int irq, void *dev_id) +{ + struct ipu_soc *ipu = dev_id; + struct completion *comp = &ipu->dc_comp; + uint32_t reg; + uint32_t dc_chan; + + if (irq == IPU_IRQ_DC_FC_1) + dc_chan = 1; + else + dc_chan = 5; + + if (!ipu->dc_swap) { + reg = ipu_dc_read(ipu, DC_WR_CH_CONF(dc_chan)); + reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; + ipu_dc_write(ipu, reg, DC_WR_CH_CONF(dc_chan)); + + reg = ipu_cm_read(ipu, IPU_DISP_GEN); + if (ipu->dc_di_assignment[dc_chan]) + reg &= ~DI1_COUNTER_RELEASE; + else + reg &= ~DI0_COUNTER_RELEASE; + ipu_cm_write(ipu, reg, IPU_DISP_GEN); + } + + complete(comp); + return IRQ_HANDLED; +} + +void _ipu_dp_dc_disable(struct ipu_soc *ipu, ipu_channel_t channel, bool swap) +{ + int ret; + uint32_t reg; + uint32_t csc; + uint32_t dc_chan; + int irq = 0; + int timeout = 50; + + ipu->dc_swap = swap; + + if (channel == MEM_DC_SYNC) { + dc_chan = 1; + irq = IPU_IRQ_DC_FC_1; + } else if (channel == MEM_BG_SYNC) { + dc_chan = 5; + irq = IPU_IRQ_DP_SF_END; + } else if (channel == MEM_FG_SYNC) { + /* Disable FG channel */ + dc_chan = 5; + + reg = ipu_dp_read(ipu, DP_COM_CONF(DP_SYNC)); + csc = reg & DP_COM_CONF_CSC_DEF_MASK; + if (csc == DP_COM_CONF_CSC_DEF_FG) + reg &= ~DP_COM_CONF_CSC_DEF_MASK; + + reg &= ~DP_COM_CONF_FG_EN; + ipu_dp_write(ipu, reg, DP_COM_CONF(DP_SYNC)); + + reg = ipu_cm_read(ipu, IPU_SRM_PRI2) | 0x8; + ipu_cm_write(ipu, reg, IPU_SRM_PRI2); + + ipu_cm_write(ipu, IPUIRQ_2_MASK(IPU_IRQ_DP_SF_END), + IPUIRQ_2_STATREG(IPU_IRQ_DP_SF_END)); + while ((ipu_cm_read(ipu, IPUIRQ_2_STATREG(IPU_IRQ_DP_SF_END)) & + IPUIRQ_2_MASK(IPU_IRQ_DP_SF_END)) == 0) { + msleep(2); + timeout -= 2; + if (timeout <= 0) + break; + } + return; + } else { + return; + } + + init_completion(&ipu->dc_comp); + ipu_clear_irq(ipu, irq); + ret = ipu_request_irq(ipu, irq, dc_irq_handler, 0, NULL, ipu); + if (ret < 0) { + dev_err(ipu->dev, "DC irq %d in use\n", irq); + return; + } + ret = wait_for_completion_timeout(&ipu->dc_comp, msecs_to_jiffies(50)); + ipu_free_irq(ipu, irq, ipu); + dev_dbg(ipu->dev, "DC stop timeout - %d * 10ms\n", 5 - ret); + + if (ipu->dc_swap) { + /* Swap DC channel 1 and 5 settings, and disable old dc chan */ + reg = ipu_dc_read(ipu, DC_WR_CH_CONF(dc_chan)); + ipu_dc_write(ipu, reg, DC_WR_CH_CONF(6 - dc_chan)); + reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK; + reg ^= DC_WR_CH_CONF_PROG_DI_ID; + ipu_dc_write(ipu, reg, DC_WR_CH_CONF(dc_chan)); + } else + /* Clock is already off because it must be done quickly, but + we need to fix the ref count */ + clk_disable(&ipu->pixel_clk[ipu->dc_di_assignment[dc_chan]]); +} + +void _ipu_init_dc_mappings(struct ipu_soc *ipu) +{ + /* IPU_PIX_FMT_RGB24 */ + _ipu_dc_map_clear(ipu, 0); + _ipu_dc_map_config(ipu, 0, 0, 7, 0xFF); + _ipu_dc_map_config(ipu, 0, 1, 15, 0xFF); + _ipu_dc_map_config(ipu, 0, 2, 23, 0xFF); + + /* IPU_PIX_FMT_RGB666 */ + _ipu_dc_map_clear(ipu, 1); + _ipu_dc_map_config(ipu, 1, 0, 5, 0xFC); + _ipu_dc_map_config(ipu, 1, 1, 11, 0xFC); + _ipu_dc_map_config(ipu, 1, 2, 17, 0xFC); + + /* IPU_PIX_FMT_YUV444 */ + _ipu_dc_map_clear(ipu, 2); + _ipu_dc_map_config(ipu, 2, 0, 15, 0xFF); + _ipu_dc_map_config(ipu, 2, 1, 23, 0xFF); + _ipu_dc_map_config(ipu, 2, 2, 7, 0xFF); + + /* IPU_PIX_FMT_RGB565 */ + _ipu_dc_map_clear(ipu, 3); + _ipu_dc_map_config(ipu, 3, 0, 4, 0xF8); + _ipu_dc_map_config(ipu, 3, 1, 10, 0xFC); + _ipu_dc_map_config(ipu, 3, 2, 15, 0xF8); + + /* IPU_PIX_FMT_LVDS666 */ + _ipu_dc_map_clear(ipu, 4); + _ipu_dc_map_config(ipu, 4, 0, 5, 0xFC); + _ipu_dc_map_config(ipu, 4, 1, 13, 0xFC); + _ipu_dc_map_config(ipu, 4, 2, 21, 0xFC); + + /* IPU_PIX_FMT_VYUY 16bit width */ + _ipu_dc_map_clear(ipu, 5); + _ipu_dc_map_config(ipu, 5, 0, 7, 0xFF); + _ipu_dc_map_config(ipu, 5, 1, 0, 0x0); + _ipu_dc_map_config(ipu, 5, 2, 15, 0xFF); + _ipu_dc_map_clear(ipu, 6); + _ipu_dc_map_config(ipu, 6, 0, 0, 0x0); + _ipu_dc_map_config(ipu, 6, 1, 7, 0xFF); + _ipu_dc_map_config(ipu, 6, 2, 15, 0xFF); + + /* IPU_PIX_FMT_UYUV 16bit width */ + _ipu_dc_map_clear(ipu, 7); + _ipu_dc_map_link(ipu, 7, 6, 0, 6, 1, 6, 2); + _ipu_dc_map_clear(ipu, 8); + _ipu_dc_map_link(ipu, 8, 5, 0, 5, 1, 5, 2); + + /* IPU_PIX_FMT_YUYV 16bit width */ + _ipu_dc_map_clear(ipu, 9); + _ipu_dc_map_link(ipu, 9, 5, 2, 5, 1, 5, 0); + _ipu_dc_map_clear(ipu, 10); + _ipu_dc_map_link(ipu, 10, 5, 1, 5, 2, 5, 0); + + /* IPU_PIX_FMT_YVYU 16bit width */ + _ipu_dc_map_clear(ipu, 11); + _ipu_dc_map_link(ipu, 11, 5, 1, 5, 2, 5, 0); + _ipu_dc_map_clear(ipu, 12); + _ipu_dc_map_link(ipu, 12, 5, 2, 5, 1, 5, 0); + + /* IPU_PIX_FMT_GBR24 */ + /* IPU_PIX_FMT_VYU444 */ + _ipu_dc_map_clear(ipu, 13); + _ipu_dc_map_link(ipu, 13, 0, 2, 0, 0, 0, 1); + + /* IPU_PIX_FMT_BGR24 */ + _ipu_dc_map_clear(ipu, 14); + _ipu_dc_map_link(ipu, 14, 0, 2, 0, 1, 0, 0); +} + +int _ipu_pixfmt_to_map(uint32_t fmt) +{ + switch (fmt) { + case IPU_PIX_FMT_GENERIC: + case IPU_PIX_FMT_RGB24: + return 0; + case IPU_PIX_FMT_RGB666: + return 1; + case IPU_PIX_FMT_YUV444: + return 2; + case IPU_PIX_FMT_RGB565: + return 3; + case IPU_PIX_FMT_LVDS666: + return 4; + case IPU_PIX_FMT_VYUY: + return 6; + case IPU_PIX_FMT_UYVY: + return 8; + case IPU_PIX_FMT_YUYV: + return 10; + case IPU_PIX_FMT_YVYU: + return 12; + case IPU_PIX_FMT_GBR24: + case IPU_PIX_FMT_VYU444: + return 13; + case IPU_PIX_FMT_BGR24: + return 14; + } + + return -1; +} + +/*! + * This function sets the colorspace for of dp. + * modes. + * + * @param ipu ipu handler + * @param channel Input parameter for the logical channel ID. + * + * @param param If it's not NULL, update the csc table + * with this parameter. + * + * @return N/A + */ +void _ipu_dp_set_csc_coefficients(struct ipu_soc *ipu, ipu_channel_t channel, int32_t param[][3]) +{ + int dp; + struct dp_csc_param_t dp_csc_param; + + if (channel == MEM_FG_SYNC) + dp = DP_SYNC; + else if (channel == MEM_BG_SYNC) + dp = DP_SYNC; + else if (channel == MEM_BG_ASYNC0) + dp = DP_ASYNC0; + else + return; + + dp_csc_param.mode = -1; + dp_csc_param.coeff = param; + __ipu_dp_csc_setup(ipu, dp, dp_csc_param, true); +} + +void ipu_set_csc_coefficients(struct ipu_soc *ipu, ipu_channel_t channel, int32_t param[][3]) +{ + _ipu_dp_set_csc_coefficients(ipu, channel, param); +} +EXPORT_SYMBOL(ipu_set_csc_coefficients); + +/*! + * This function is called to adapt synchronous LCD panel to IPU restriction. + * + */ +void adapt_panel_to_ipu_restricitions(struct ipu_soc *ipu, uint16_t *v_start_width, + uint16_t *v_sync_width, + uint16_t *v_end_width) +{ + if (*v_end_width < 2) { + uint16_t diff = 2 - *v_end_width; + if (*v_start_width >= diff) { + *v_end_width = 2; + *v_start_width = *v_start_width - diff; + } else if (*v_sync_width > diff) { + *v_end_width = 2; + *v_sync_width = *v_sync_width - diff; + } else + dev_err(ipu->dev, "WARNING: try to adapt timming, but failed\n"); + dev_err(ipu->dev, "WARNING: adapt panel end blank lines\n"); + } +} + +/*! + * This function is called to initialize a synchronous LCD panel. + * + * @param ipu ipu handler + * @param disp The DI the panel is attached to. + * + * @param pixel_clk Desired pixel clock frequency in Hz. + * + * @param pixel_fmt Input parameter for pixel format of buffer. + * Pixel format is a FOURCC ASCII code. + * + * @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 ipu_init_sync_panel(struct ipu_soc *ipu, int disp, uint32_t pixel_clk, + uint16_t width, uint16_t height, + uint32_t pixel_fmt, + 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, ipu_di_signal_cfg_t sig) +{ + uint32_t field0_offset = 0; + uint32_t field1_offset; + uint32_t reg; + uint32_t di_gen, vsync_cnt; + uint32_t div, rounded_pixel_clk, rounded_parent_clk; + uint32_t h_total, v_total; + int map; + struct clk *di_parent; + unsigned long lock_flags; + + dev_dbg(ipu->dev, "panel size = %d x %d\n", width, height); + + if ((v_sync_width == 0) || (h_sync_width == 0)) + return EINVAL; + + adapt_panel_to_ipu_restricitions(ipu, &v_start_width, &v_sync_width, &v_end_width); + h_total = width + h_sync_width + h_start_width + h_end_width; + v_total = height + v_sync_width + v_start_width + v_end_width; + + /* Init clocking */ + dev_dbg(ipu->dev, "pixel clk = %d\n", pixel_clk); + + di_parent = clk_get_parent(ipu->di_clk[disp]); + if (clk_get(NULL, "tve_clk") == di_parent || + clk_get(NULL, "ldb_di0_clk") == di_parent || + clk_get(NULL, "ldb_di1_clk") == di_parent) { + /* if di clk parent is tve/ldb, then keep it;*/ + dev_dbg(ipu->dev, "use special clk parent\n"); + clk_set_parent(&ipu->pixel_clk[disp], ipu->di_clk[disp]); + } else { + /* try ipu clk first*/ + dev_dbg(ipu->dev, "try ipu internal clk\n"); + clk_set_parent(&ipu->pixel_clk[disp], ipu->ipu_clk); + rounded_pixel_clk = clk_round_rate(&ipu->pixel_clk[disp], pixel_clk); + /* + * we will only use 1/2 fraction for ipu clk, + * so if the clk rate is not fit, try ext clk. + */ + if (!sig.int_clk && + ((rounded_pixel_clk >= pixel_clk + pixel_clk/200) || + (rounded_pixel_clk <= pixel_clk - pixel_clk/200))) { + dev_dbg(ipu->dev, "try ipu ext di clk\n"); + if (clk_get_usecount(di_parent)) + dev_warn(ipu->dev, + "ext di clk already in use, go back to internal clk\n"); + else { + rounded_pixel_clk = pixel_clk * 2; + rounded_parent_clk = clk_round_rate(di_parent, + rounded_pixel_clk); + while (rounded_pixel_clk < rounded_parent_clk) { + /* the max divider from parent to di is 8 */ + if (rounded_parent_clk / pixel_clk < 8) + rounded_pixel_clk += pixel_clk * 2; + else + rounded_pixel_clk *= 2; + } + clk_set_rate(di_parent, rounded_pixel_clk); + rounded_pixel_clk = + clk_round_rate(ipu->di_clk[disp], pixel_clk); + clk_set_rate(ipu->di_clk[disp], rounded_pixel_clk); + clk_set_parent(&ipu->pixel_clk[disp], ipu->di_clk[disp]); + } + } + } + rounded_pixel_clk = clk_round_rate(&ipu->pixel_clk[disp], pixel_clk); + clk_set_rate(&ipu->pixel_clk[disp], rounded_pixel_clk); + msleep(5); + /* Get integer portion of divider */ + div = clk_get_rate(clk_get_parent(&ipu->pixel_clk[disp])) / rounded_pixel_clk; + + _ipu_lock(ipu, &lock_flags); + + _ipu_di_data_wave_config(ipu, disp, SYNC_WAVE, div - 1, div - 1); + _ipu_di_data_pin_config(ipu, disp, SYNC_WAVE, DI_PIN15, 3, 0, div * 2); + + map = _ipu_pixfmt_to_map(pixel_fmt); + if (map < 0) { + dev_dbg(ipu->dev, "IPU_DISP: No MAP\n"); + _ipu_unlock(ipu, &lock_flags); + return -EINVAL; + } + + /*clear DI*/ + di_gen = ipu_di_read(ipu, disp, DI_GENERAL); + ipu_di_write(ipu, disp, + di_gen & (0x3 << 20), DI_GENERAL); + + if (sig.interlaced) { + if (g_ipu_hw_rev >= 2) { + /* Setup internal HSYNC waveform */ + _ipu_di_sync_config(ipu, + disp, /* display */ + 1, /* counter */ + h_total/2 - 1, /* run count */ + DI_SYNC_CLK, /* run_resolution */ + 0, /* offset */ + DI_SYNC_NONE, /* offset resolution */ + 0, /* repeat count */ + DI_SYNC_NONE, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 0 /* COUNT DOWN */ + ); + + /* Field 1 VSYNC waveform */ + _ipu_di_sync_config(ipu, + disp, /* display */ + 2, /* counter */ + h_total - 1, /* run count */ + DI_SYNC_CLK, /* run_resolution */ + 0, /* offset */ + DI_SYNC_NONE, /* offset resolution */ + 0, /* repeat count */ + DI_SYNC_NONE, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 2*div /* COUNT DOWN */ + ); + + /* Setup internal HSYNC waveform */ + _ipu_di_sync_config(ipu, + disp, /* display */ + 3, /* counter */ + v_total*2 - 1, /* run count */ + DI_SYNC_INT_HSYNC, /* run_resolution */ + 1, /* offset */ + DI_SYNC_INT_HSYNC, /* offset resolution */ + 0, /* repeat count */ + DI_SYNC_NONE, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 2*div /* COUNT DOWN */ + ); + + /* Active Field ? */ + _ipu_di_sync_config(ipu, + disp, /* display */ + 4, /* counter */ + v_total/2 - 1, /* run count */ + DI_SYNC_HSYNC, /* run_resolution */ + v_start_width, /* offset */ + DI_SYNC_HSYNC, /* offset resolution */ + 2, /* repeat count */ + DI_SYNC_VSYNC, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 0 /* COUNT DOWN */ + ); + + /* Active Line */ + _ipu_di_sync_config(ipu, + disp, /* display */ + 5, /* counter */ + 0, /* run count */ + DI_SYNC_HSYNC, /* run_resolution */ + 0, /* offset */ + DI_SYNC_NONE, /* offset resolution */ + height/2, /* repeat count */ + 4, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 0 /* COUNT DOWN */ + ); + + /* Field 0 VSYNC waveform */ + _ipu_di_sync_config(ipu, + disp, /* display */ + 6, /* counter */ + v_total - 1, /* run count */ + DI_SYNC_HSYNC, /* run_resolution */ + 0, /* offset */ + DI_SYNC_NONE, /* offset resolution */ + 0, /* repeat count */ + DI_SYNC_NONE, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 0 /* COUNT DOWN */ + ); + + /* DC VSYNC waveform */ + vsync_cnt = 7; + _ipu_di_sync_config(ipu, + disp, /* display */ + 7, /* counter */ + v_total/2 - 1, /* run count */ + DI_SYNC_HSYNC, /* run_resolution */ + 9, /* offset */ + DI_SYNC_HSYNC, /* offset resolution */ + 2, /* repeat count */ + DI_SYNC_VSYNC, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 0 /* COUNT DOWN */ + ); + + /* active pixel waveform */ + _ipu_di_sync_config(ipu, + disp, /* display */ + 8, /* counter */ + 0, /* run count */ + DI_SYNC_CLK, /* run_resolution */ + h_start_width, /* offset */ + DI_SYNC_CLK, /* offset resolution */ + width, /* repeat count */ + 5, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 0 /* COUNT DOWN */ + ); + + /* Second VSYNC */ + _ipu_di_sync_config(ipu, + disp, /* display */ + 9, /* counter */ + v_total - 1, /* run count */ + DI_SYNC_INT_HSYNC, /* run_resolution */ + v_total/2, /* offset */ + DI_SYNC_INT_HSYNC, /* offset resolution */ + 0, /* repeat count */ + DI_SYNC_HSYNC, /* CNT_CLR_SEL */ + 0, /* CNT_POLARITY_GEN_EN */ + DI_SYNC_NONE, /* CNT_POLARITY_CLR_SEL */ + DI_SYNC_NONE, /* CNT_POLARITY_TRIGGER_SEL */ + 0, /* COUNT UP */ + 2*div /* COUNT DOWN */ + ); + + /* set gentime select and tag sel */ + reg = ipu_di_read(ipu, disp, DI_SW_GEN1(9)); + reg &= 0x1FFFFFFF; + reg |= (3-1)<<29 | 0x00008000; + ipu_di_write(ipu, disp, reg, DI_SW_GEN1(9)); + + ipu_di_write(ipu, disp, v_total / 2 - 1, DI_SCR_CONF); + + /* set y_sel = 1 */ + di_gen |= 0x10000000; + di_gen |= DI_GEN_POLARITY_5; + di_gen |= DI_GEN_POLARITY_8; + } else { + /* Setup internal HSYNC waveform */ + _ipu_di_sync_config(ipu, disp, 1, h_total - 1, DI_SYNC_CLK, + 0, DI_SYNC_NONE, 0, DI_SYNC_NONE, 0, DI_SYNC_NONE, + DI_SYNC_NONE, 0, 0); + + field1_offset = v_sync_width + v_start_width + height / 2 + + v_end_width; + if (sig.odd_field_first) { + field0_offset = field1_offset - 1; + field1_offset = 0; + } + v_total += v_start_width + v_end_width; + + /* Field 1 VSYNC waveform */ + _ipu_di_sync_config(ipu, disp, 2, v_total - 1, 1, + field0_offset, + field0_offset ? 1 : DI_SYNC_NONE, + 0, DI_SYNC_NONE, 0, + DI_SYNC_NONE, DI_SYNC_NONE, 0, 4); + + /* Setup internal HSYNC waveform */ + _ipu_di_sync_config(ipu, disp, 3, h_total - 1, DI_SYNC_CLK, + 0, DI_SYNC_NONE, 0, DI_SYNC_NONE, 0, + DI_SYNC_NONE, DI_SYNC_NONE, 0, 4); + + /* Active Field ? */ + _ipu_di_sync_config(ipu, disp, 4, + field0_offset ? + field0_offset : field1_offset - 2, + 1, v_start_width + v_sync_width, 1, 2, 2, + 0, DI_SYNC_NONE, DI_SYNC_NONE, 0, 0); + + /* Active Line */ + _ipu_di_sync_config(ipu, disp, 5, 0, 1, + 0, DI_SYNC_NONE, + height / 2, 4, 0, DI_SYNC_NONE, + DI_SYNC_NONE, 0, 0); + + /* Field 0 VSYNC waveform */ + _ipu_di_sync_config(ipu, disp, 6, v_total - 1, 1, + 0, DI_SYNC_NONE, + 0, DI_SYNC_NONE, 0, DI_SYNC_NONE, + DI_SYNC_NONE, 0, 0); + + /* DC VSYNC waveform */ + vsync_cnt = 7; + _ipu_di_sync_config(ipu, disp, 7, 0, 1, + field1_offset, + field1_offset ? 1 : DI_SYNC_NONE, + 1, 2, 0, DI_SYNC_NONE, DI_SYNC_NONE, 0, 0); + + /* active pixel waveform */ + _ipu_di_sync_config(ipu, disp, 8, 0, DI_SYNC_CLK, + h_sync_width + h_start_width, DI_SYNC_CLK, + width, 5, 0, DI_SYNC_NONE, DI_SYNC_NONE, + 0, 0); + + /* ??? */ + _ipu_di_sync_config(ipu, disp, 9, v_total - 1, 2, + 0, DI_SYNC_NONE, + 0, DI_SYNC_NONE, 6, DI_SYNC_NONE, + DI_SYNC_NONE, 0, 0); + + reg = ipu_di_read(ipu, disp, DI_SW_GEN1(9)); + reg |= 0x8000; + ipu_di_write(ipu, disp, reg, DI_SW_GEN1(9)); + + ipu_di_write(ipu, disp, v_sync_width + v_start_width + + v_end_width + height / 2 - 1, DI_SCR_CONF); + } + + /* Init template microcode */ + _ipu_dc_write_tmpl(ipu, 0, WROD(0), 0, map, SYNC_WAVE, 0, 8, 1); + + if (sig.Hsync_pol) + di_gen |= DI_GEN_POLARITY_3; + if (sig.Vsync_pol) + di_gen |= DI_GEN_POLARITY_2; + } else { + /* Setup internal HSYNC waveform */ + _ipu_di_sync_config(ipu, disp, 1, h_total - 1, DI_SYNC_CLK, + 0, DI_SYNC_NONE, 0, DI_SYNC_NONE, 0, DI_SYNC_NONE, + DI_SYNC_NONE, 0, 0); + + /* Setup external (delayed) HSYNC waveform */ + _ipu_di_sync_config(ipu, disp, DI_SYNC_HSYNC, h_total - 1, + DI_SYNC_CLK, div * v_to_h_sync, DI_SYNC_CLK, + 0, DI_SYNC_NONE, 1, DI_SYNC_NONE, + DI_SYNC_CLK, 0, h_sync_width * 2); + /* Setup VSYNC waveform */ + vsync_cnt = DI_SYNC_VSYNC; + _ipu_di_sync_config(ipu, disp, DI_SYNC_VSYNC, v_total - 1, + DI_SYNC_INT_HSYNC, 0, DI_SYNC_NONE, 0, + DI_SYNC_NONE, 1, DI_SYNC_NONE, + DI_SYNC_INT_HSYNC, 0, v_sync_width * 2); + ipu_di_write(ipu, disp, v_total - 1, DI_SCR_CONF); + + /* Setup active data waveform to sync with DC */ + _ipu_di_sync_config(ipu, disp, 4, 0, DI_SYNC_HSYNC, + v_sync_width + v_start_width, DI_SYNC_HSYNC, height, + DI_SYNC_VSYNC, 0, DI_SYNC_NONE, + DI_SYNC_NONE, 0, 0); + _ipu_di_sync_config(ipu, disp, 5, 0, DI_SYNC_CLK, + h_sync_width + h_start_width, DI_SYNC_CLK, + width, 4, 0, DI_SYNC_NONE, DI_SYNC_NONE, 0, + 0); + + /* set VGA delayed hsync/vsync no matter VGA enabled */ + if (disp) { + /* couter 7 for VGA delay HSYNC */ + _ipu_di_sync_config(ipu, disp, 7, + h_total - 1, DI_SYNC_CLK, + 18, DI_SYNC_CLK, + 0, DI_SYNC_NONE, + 1, DI_SYNC_NONE, DI_SYNC_CLK, + 0, h_sync_width * 2); + + /* couter 8 for VGA delay VSYNC */ + _ipu_di_sync_config(ipu, disp, 8, + v_total - 1, DI_SYNC_INT_HSYNC, + 1, DI_SYNC_INT_HSYNC, + 0, DI_SYNC_NONE, + 1, DI_SYNC_NONE, DI_SYNC_INT_HSYNC, + 0, v_sync_width * 2); + } + + /* reset all unused counters */ + ipu_di_write(ipu, disp, 0, DI_SW_GEN0(6)); + ipu_di_write(ipu, disp, 0, DI_SW_GEN1(6)); + if (!disp) { + ipu_di_write(ipu, disp, 0, DI_SW_GEN0(7)); + ipu_di_write(ipu, disp, 0, DI_SW_GEN1(7)); + ipu_di_write(ipu, disp, 0, DI_STP_REP(7)); + ipu_di_write(ipu, disp, 0, DI_SW_GEN0(8)); + ipu_di_write(ipu, disp, 0, DI_SW_GEN1(8)); + ipu_di_write(ipu, disp, 0, DI_STP_REP(8)); + } + ipu_di_write(ipu, disp, 0, DI_SW_GEN0(9)); + ipu_di_write(ipu, disp, 0, DI_SW_GEN1(9)); + ipu_di_write(ipu, disp, 0, DI_STP_REP(9)); + + reg = ipu_di_read(ipu, disp, DI_STP_REP(6)); + reg &= 0x0000FFFF; + ipu_di_write(ipu, disp, reg, DI_STP_REP(6)); + + /* Init template microcode */ + if (disp) { + if ((pixel_fmt == IPU_PIX_FMT_YUYV) || + (pixel_fmt == IPU_PIX_FMT_UYVY) || + (pixel_fmt == IPU_PIX_FMT_YVYU) || + (pixel_fmt == IPU_PIX_FMT_VYUY)) { + _ipu_dc_write_tmpl(ipu, 8, WROD(0), 0, (map - 1), SYNC_WAVE, 0, 5, 1); + _ipu_dc_write_tmpl(ipu, 9, WROD(0), 0, map, SYNC_WAVE, 0, 5, 1); + /* configure user events according to DISP NUM */ + ipu_dc_write(ipu, (width - 1), DC_UGDE_3(disp)); + } + _ipu_dc_write_tmpl(ipu, 2, WROD(0), 0, map, SYNC_WAVE, 8, 5, 1); + _ipu_dc_write_tmpl(ipu, 3, WRG, 0, map, SYNC_WAVE, 4, 5, 1); + _ipu_dc_write_tmpl(ipu, 4, WROD(0), 0, map, SYNC_WAVE, 0, 5, 1); + } else { + if ((pixel_fmt == IPU_PIX_FMT_YUYV) || + (pixel_fmt == IPU_PIX_FMT_UYVY) || + (pixel_fmt == IPU_PIX_FMT_YVYU) || + (pixel_fmt == IPU_PIX_FMT_VYUY)) { + _ipu_dc_write_tmpl(ipu, 10, WROD(0), 0, (map - 1), SYNC_WAVE, 0, 5, 1); + _ipu_dc_write_tmpl(ipu, 11, WROD(0), 0, map, SYNC_WAVE, 0, 5, 1); + /* configure user events according to DISP NUM */ + ipu_dc_write(ipu, width - 1, DC_UGDE_3(disp)); + } + _ipu_dc_write_tmpl(ipu, 5, WROD(0), 0, map, SYNC_WAVE, 8, 5, 1); + _ipu_dc_write_tmpl(ipu, 6, WRG, 0, map, SYNC_WAVE, 4, 5, 1); + _ipu_dc_write_tmpl(ipu, 7, WROD(0), 0, map, SYNC_WAVE, 0, 5, 1); + } + + if (sig.Hsync_pol) { + di_gen |= DI_GEN_POLARITY_2; + if (disp) + di_gen |= DI_GEN_POLARITY_7; + } + if (sig.Vsync_pol) { + di_gen |= DI_GEN_POLARITY_3; + if (disp) + di_gen |= DI_GEN_POLARITY_8; + } + } + /* changinc DISP_CLK polarity: it can be wrong for some applications */ + if ((pixel_fmt == IPU_PIX_FMT_YUYV) || + (pixel_fmt == IPU_PIX_FMT_UYVY) || + (pixel_fmt == IPU_PIX_FMT_YVYU) || + (pixel_fmt == IPU_PIX_FMT_VYUY)) + di_gen |= 0x00020000; + + if (!sig.clk_pol) + di_gen |= DI_GEN_POLARITY_DISP_CLK; + + ipu_di_write(ipu, disp, di_gen, DI_GENERAL); + + ipu_di_write(ipu, disp, (--vsync_cnt << DI_VSYNC_SEL_OFFSET) | + 0x00000002, DI_SYNC_AS_GEN); + reg = ipu_di_read(ipu, disp, DI_POL); + reg &= ~(DI_POL_DRDY_DATA_POLARITY | DI_POL_DRDY_POLARITY_15); + if (sig.enable_pol) + reg |= DI_POL_DRDY_POLARITY_15; + if (sig.data_pol) + reg |= DI_POL_DRDY_DATA_POLARITY; + ipu_di_write(ipu, disp, reg, DI_POL); + + ipu_dc_write(ipu, width, DC_DISP_CONF2(DC_DISP_ID_SYNC(disp))); + + _ipu_unlock(ipu, &lock_flags); + + return 0; +} +EXPORT_SYMBOL(ipu_init_sync_panel); + +void ipu_uninit_sync_panel(struct ipu_soc *ipu, int disp) +{ + uint32_t reg; + uint32_t di_gen; + unsigned long lock_flags; + + if ((disp != 0) || (disp != 1)) + return; + + _ipu_lock(ipu, &lock_flags); + + di_gen = ipu_di_read(ipu, disp, DI_GENERAL); + di_gen |= 0x3ff | DI_GEN_POLARITY_DISP_CLK; + ipu_di_write(ipu, disp, di_gen, DI_GENERAL); + + reg = ipu_di_read(ipu, disp, DI_POL); + reg |= 0x3ffffff; + ipu_di_write(ipu, disp, reg, DI_POL); + + _ipu_unlock(ipu, &lock_flags); +} +EXPORT_SYMBOL(ipu_uninit_sync_panel); + +int ipu_init_async_panel(struct ipu_soc *ipu, int disp, int type, uint32_t cycle_time, + uint32_t pixel_fmt, ipu_adc_sig_cfg_t sig) +{ + int map; + u32 ser_conf = 0; + u32 div; + u32 di_clk = clk_get_rate(ipu->ipu_clk); + unsigned long lock_flags; + + /* round up cycle_time, then calcalate the divider using scaled math */ + cycle_time += (1000000000UL / di_clk) - 1; + div = (cycle_time * (di_clk / 256UL)) / (1000000000UL / 256UL); + + map = _ipu_pixfmt_to_map(pixel_fmt); + if (map < 0) + return -EINVAL; + + _ipu_lock(ipu, &lock_flags); + + if (type == IPU_PANEL_SERIAL) { + ipu_di_write(ipu, disp, (div << 24) | ((sig.ifc_width - 1) << 4), + DI_DW_GEN(ASYNC_SER_WAVE)); + + _ipu_di_data_pin_config(ipu, disp, ASYNC_SER_WAVE, DI_PIN_CS, + 0, 0, (div * 2) + 1); + _ipu_di_data_pin_config(ipu, disp, ASYNC_SER_WAVE, DI_PIN_SER_CLK, + 1, div, div * 2); + _ipu_di_data_pin_config(ipu, disp, ASYNC_SER_WAVE, DI_PIN_SER_RS, + 2, 0, 0); + + _ipu_dc_write_tmpl(ipu, 0x64, WROD(0), 0, map, ASYNC_SER_WAVE, 0, 0, 1); + + /* Configure DC for serial panel */ + ipu_dc_write(ipu, 0x14, DC_DISP_CONF1(DC_DISP_ID_SERIAL)); + + if (sig.clk_pol) + ser_conf |= DI_SER_CONF_SERIAL_CLK_POL; + if (sig.data_pol) + ser_conf |= DI_SER_CONF_SERIAL_DATA_POL; + if (sig.rs_pol) + ser_conf |= DI_SER_CONF_SERIAL_RS_POL; + if (sig.cs_pol) + ser_conf |= DI_SER_CONF_SERIAL_CS_POL; + ipu_di_write(ipu, disp, ser_conf, DI_SER_CONF); + } + + _ipu_unlock(ipu, &lock_flags); + return 0; +} +EXPORT_SYMBOL(ipu_init_async_panel); + +/*! + * This function sets the foreground and background plane global alpha blending + * modes. This function also sets the DP graphic plane according to the + * parameter of IPUv3 DP channel. + * + * @param ipu ipu handler + * @param channel IPUv3 DP channel + * + * @param enable Boolean to enable or disable global alpha + * blending. If disabled, local blending is used. + * + * @param alpha Global alpha value. + * + * @return Returns 0 on success or negative error code on fail + */ +int32_t ipu_disp_set_global_alpha(struct ipu_soc *ipu, ipu_channel_t channel, + bool enable, uint8_t alpha) +{ + uint32_t reg; + uint32_t flow; + bool bg_chan; + unsigned long lock_flags; + + if (channel == MEM_BG_SYNC || channel == MEM_FG_SYNC) + flow = DP_SYNC; + else if (channel == MEM_BG_ASYNC0 || channel == MEM_FG_ASYNC0) + flow = DP_ASYNC0; + else if (channel == MEM_BG_ASYNC1 || channel == MEM_FG_ASYNC1) + flow = DP_ASYNC1; + else + return -EINVAL; + + if (channel == MEM_BG_SYNC || channel == MEM_BG_ASYNC0 || + channel == MEM_BG_ASYNC1) + bg_chan = true; + else + bg_chan = false; + + _ipu_get(ipu); + + _ipu_lock(ipu, &lock_flags); + + if (bg_chan) { + reg = ipu_dp_read(ipu, DP_COM_CONF(flow)); + ipu_dp_write(ipu, reg & ~DP_COM_CONF_GWSEL, DP_COM_CONF(flow)); + } else { + reg = ipu_dp_read(ipu, DP_COM_CONF(flow)); + ipu_dp_write(ipu, reg | DP_COM_CONF_GWSEL, DP_COM_CONF(flow)); + } + + if (enable) { + reg = ipu_dp_read(ipu, DP_GRAPH_WIND_CTRL(flow)) & 0x00FFFFFFL; + ipu_dp_write(ipu, reg | ((uint32_t) alpha << 24), + DP_GRAPH_WIND_CTRL(flow)); + + reg = ipu_dp_read(ipu, DP_COM_CONF(flow)); + ipu_dp_write(ipu, reg | DP_COM_CONF_GWAM, DP_COM_CONF(flow)); + } else { + reg = ipu_dp_read(ipu, DP_COM_CONF(flow)); + ipu_dp_write(ipu, reg & ~DP_COM_CONF_GWAM, DP_COM_CONF(flow)); + } + + reg = ipu_cm_read(ipu, IPU_SRM_PRI2) | 0x8; + ipu_cm_write(ipu, reg, IPU_SRM_PRI2); + + _ipu_unlock(ipu, &lock_flags); + + _ipu_put(ipu); + + return 0; +} +EXPORT_SYMBOL(ipu_disp_set_global_alpha); + +/*! + * This function sets the transparent color key for SDC graphic plane. + * + * @param ipu ipu handler + * @param channel Input parameter for the logical channel ID. + * + * @param enable Boolean to enable or disable color key + * + * @param colorKey 24-bit RGB color for transparent color key. + * + * @return Returns 0 on success or negative error code on fail + */ +int32_t ipu_disp_set_color_key(struct ipu_soc *ipu, ipu_channel_t channel, + bool enable, uint32_t color_key) +{ + uint32_t reg, flow; + int y, u, v; + int red, green, blue; + unsigned long lock_flags; + + if (channel == MEM_BG_SYNC || channel == MEM_FG_SYNC) + flow = DP_SYNC; + else if (channel == MEM_BG_ASYNC0 || channel == MEM_FG_ASYNC0) + flow = DP_ASYNC0; + else if (channel == MEM_BG_ASYNC1 || channel == MEM_FG_ASYNC1) + flow = DP_ASYNC1; + else + return -EINVAL; + + _ipu_get(ipu); + + _ipu_lock(ipu, &lock_flags); + + ipu->color_key_4rgb = true; + /* Transform color key from rgb to yuv if CSC is enabled */ + if (((ipu->fg_csc_type == RGB2YUV) && (ipu->bg_csc_type == YUV2YUV)) || + ((ipu->fg_csc_type == YUV2YUV) && (ipu->bg_csc_type == RGB2YUV)) || + ((ipu->fg_csc_type == YUV2YUV) && (ipu->bg_csc_type == YUV2YUV)) || + ((ipu->fg_csc_type == YUV2RGB) && (ipu->bg_csc_type == YUV2RGB))) { + + dev_dbg(ipu->dev, "color key 0x%x need change to yuv fmt\n", color_key); + + red = (color_key >> 16) & 0xFF; + green = (color_key >> 8) & 0xFF; + blue = color_key & 0xFF; + + y = _rgb_to_yuv(0, red, green, blue); + u = _rgb_to_yuv(1, red, green, blue); + v = _rgb_to_yuv(2, red, green, blue); + color_key = (y << 16) | (u << 8) | v; + + ipu->color_key_4rgb = false; + + dev_dbg(ipu->dev, "color key change to yuv fmt 0x%x\n", color_key); + } + + if (enable) { + reg = ipu_dp_read(ipu, DP_GRAPH_WIND_CTRL(flow)) & 0xFF000000L; + ipu_dp_write(ipu, reg | color_key, DP_GRAPH_WIND_CTRL(flow)); + + reg = ipu_dp_read(ipu, DP_COM_CONF(flow)); + ipu_dp_write(ipu, reg | DP_COM_CONF_GWCKE, DP_COM_CONF(flow)); + } else { + reg = ipu_dp_read(ipu, DP_COM_CONF(flow)); + ipu_dp_write(ipu, reg & ~DP_COM_CONF_GWCKE, DP_COM_CONF(flow)); + } + + reg = ipu_cm_read(ipu, IPU_SRM_PRI2) | 0x8; + ipu_cm_write(ipu, reg, IPU_SRM_PRI2); + + _ipu_unlock(ipu, &lock_flags); + + _ipu_put(ipu); + + return 0; +} +EXPORT_SYMBOL(ipu_disp_set_color_key); + +/*! + * This function sets the gamma correction for DP output. + * + * @param ipu ipu handler + * @param channel Input parameter for the logical channel ID. + * + * @param enable Boolean to enable or disable gamma correction. + * + * @param constk Gamma piecewise linear approximation constk coeff. + * + * @param slopek Gamma piecewise linear approximation slopek coeff. + * + * @return Returns 0 on success or negative error code on fail + */ +int32_t ipu_disp_set_gamma_correction(struct ipu_soc *ipu, ipu_channel_t channel, bool enable, int constk[], int slopek[]) +{ + uint32_t reg, flow, i; + unsigned long lock_flags; + + if (channel == MEM_BG_SYNC || channel == MEM_FG_SYNC) + flow = DP_SYNC; + else if (channel == MEM_BG_ASYNC0 || channel == MEM_FG_ASYNC0) + flow = DP_ASYNC0; + else if (channel == MEM_BG_ASYNC1 || channel == MEM_FG_ASYNC1) + flow = DP_ASYNC1; + else + return -EINVAL; + + _ipu_get(ipu); + + _ipu_lock(ipu, &lock_flags); + + for (i = 0; i < 8; i++) + ipu_dp_write(ipu, (constk[2*i] & 0x1ff) | ((constk[2*i+1] & 0x1ff) << 16), DP_GAMMA_C(flow, i)); + for (i = 0; i < 4; i++) + ipu_dp_write(ipu, (slopek[4*i] & 0xff) | ((slopek[4*i+1] & 0xff) << 8) | + ((slopek[4*i+2] & 0xff) << 16) | ((slopek[4*i+3] & 0xff) << 24), DP_GAMMA_S(flow, i)); + + reg = ipu_dp_read(ipu, DP_COM_CONF(flow)); + if (enable) { + if ((ipu->bg_csc_type == RGB2YUV) || (ipu->bg_csc_type == YUV2YUV)) + reg |= DP_COM_CONF_GAMMA_YUV_EN; + else + reg &= ~DP_COM_CONF_GAMMA_YUV_EN; + ipu_dp_write(ipu, reg | DP_COM_CONF_GAMMA_EN, DP_COM_CONF(flow)); + } else + ipu_dp_write(ipu, reg & ~DP_COM_CONF_GAMMA_EN, DP_COM_CONF(flow)); + + reg = ipu_cm_read(ipu, IPU_SRM_PRI2) | 0x8; + ipu_cm_write(ipu, reg, IPU_SRM_PRI2); + + _ipu_unlock(ipu, &lock_flags); + + _ipu_put(ipu); + + return 0; +} +EXPORT_SYMBOL(ipu_disp_set_gamma_correction); + +/*! + * This function sets the window position of the foreground or background plane. + * modes. + * + * @param ipu ipu handler + * @param channel Input parameter for the logical channel ID. + * + * @param x_pos The X coordinate position to place window at. + * The position is relative to the top left corner. + * + * @param y_pos The Y coordinate position to place window at. + * The position is relative to the top left corner. + * + * @return Returns 0 on success or negative error code on fail + */ +int32_t _ipu_disp_set_window_pos(struct ipu_soc *ipu, ipu_channel_t channel, + int16_t x_pos, int16_t y_pos) +{ + u32 reg; + uint32_t flow = 0; + uint32_t dp_srm_shift; + + if ((channel == MEM_FG_SYNC) || (channel == MEM_BG_SYNC)) { + flow = DP_SYNC; + dp_srm_shift = 3; + } else if (channel == MEM_FG_ASYNC0) { + flow = DP_ASYNC0; + dp_srm_shift = 5; + } else if (channel == MEM_FG_ASYNC1) { + flow = DP_ASYNC1; + dp_srm_shift = 7; + } else + return -EINVAL; + + ipu_dp_write(ipu, (x_pos << 16) | y_pos, DP_FG_POS(flow)); + + if (ipu_is_channel_busy(ipu, channel)) { + /* controled by FSU if channel enabled */ + reg = ipu_cm_read(ipu, IPU_SRM_PRI2) & (~(0x3 << dp_srm_shift)); + reg |= (0x1 << dp_srm_shift); + ipu_cm_write(ipu, reg, IPU_SRM_PRI2); + } else { + /* disable auto swap, controled by MCU if channel disabled */ + reg = ipu_cm_read(ipu, IPU_SRM_PRI2) & (~(0x3 << dp_srm_shift)); + ipu_cm_write(ipu, reg, IPU_SRM_PRI2); + } + + return 0; +} + +int32_t ipu_disp_set_window_pos(struct ipu_soc *ipu, ipu_channel_t channel, + int16_t x_pos, int16_t y_pos) +{ + int ret; + unsigned long lock_flags; + + _ipu_get(ipu); + _ipu_lock(ipu, &lock_flags); + ret = _ipu_disp_set_window_pos(ipu, channel, x_pos, y_pos); + _ipu_unlock(ipu, &lock_flags); + _ipu_put(ipu); + return ret; +} +EXPORT_SYMBOL(ipu_disp_set_window_pos); + +int32_t _ipu_disp_get_window_pos(struct ipu_soc *ipu, ipu_channel_t channel, + int16_t *x_pos, int16_t *y_pos) +{ + u32 reg; + uint32_t flow = 0; + + if (channel == MEM_FG_SYNC) + flow = DP_SYNC; + else if (channel == MEM_FG_ASYNC0) + flow = DP_ASYNC0; + else if (channel == MEM_FG_ASYNC1) + flow = DP_ASYNC1; + else + return -EINVAL; + + reg = ipu_dp_read(ipu, DP_FG_POS(flow)); + + *x_pos = (reg >> 16) & 0x7FF; + *y_pos = reg & 0x7FF; + + return 0; +} +int32_t ipu_disp_get_window_pos(struct ipu_soc *ipu, ipu_channel_t channel, + int16_t *x_pos, int16_t *y_pos) +{ + int ret; + unsigned long lock_flags; + + _ipu_get(ipu); + _ipu_lock(ipu, &lock_flags); + ret = _ipu_disp_get_window_pos(ipu, channel, x_pos, y_pos); + _ipu_unlock(ipu, &lock_flags); + _ipu_put(ipu); + return ret; +} +EXPORT_SYMBOL(ipu_disp_get_window_pos); + +void ipu_disp_direct_write(struct ipu_soc *ipu, ipu_channel_t channel, u32 value, u32 offset) +{ + if (channel == DIRECT_ASYNC0) + writel(value, ipu->disp_base[0] + offset); + else if (channel == DIRECT_ASYNC1) + writel(value, ipu->disp_base[1] + offset); +} +EXPORT_SYMBOL(ipu_disp_direct_write); + +void ipu_reset_disp_panel(struct ipu_soc *ipu) +{ + uint32_t tmp; + + tmp = ipu_di_read(ipu, 1, DI_GENERAL); + ipu_di_write(ipu, 1, tmp | 0x08, DI_GENERAL); + msleep(10); /* tRES >= 100us */ + tmp = ipu_di_read(ipu, 1, DI_GENERAL); + ipu_di_write(ipu, 1, tmp & ~0x08, DI_GENERAL); + msleep(60); + + return; +} +EXPORT_SYMBOL(ipu_reset_disp_panel); + +void __devinit ipu_disp_init(struct ipu_soc *ipu) +{ + ipu->fg_csc_type = ipu->bg_csc_type = CSC_NONE; + ipu->color_key_4rgb = true; + _ipu_init_dc_mappings(ipu); + _ipu_dmfc_init(ipu, DMFC_NORMAL, 1); +} diff --git a/drivers/mxc/ipu3/ipu_ic.c b/drivers/mxc/ipu3/ipu_ic.c new file mode 100644 index 00000000000..7a31da200ed --- /dev/null +++ b/drivers/mxc/ipu3/ipu_ic.c @@ -0,0 +1,840 @@ +/* + * 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 + */ + +/* + * @file ipu_ic.c + * + * @brief IPU IC functions + * + * @ingroup IPU + */ +#include <linux/types.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/spinlock.h> +#include <linux/videodev2.h> +#include <linux/io.h> +#include <linux/ipu.h> + +#include "ipu_prv.h" +#include "ipu_regs.h" +#include "ipu_param_mem.h" + +enum { + IC_TASK_VIEWFINDER, + IC_TASK_ENCODER, + IC_TASK_POST_PROCESSOR +}; + +static void _init_csc(struct ipu_soc *ipu, uint8_t ic_task, ipu_color_space_t in_format, + ipu_color_space_t out_format, int csc_index); +static bool _calc_resize_coeffs(struct ipu_soc *ipu, + uint32_t inSize, uint32_t outSize, + uint32_t *resizeCoeff, + uint32_t *downsizeCoeff); + +void _ipu_vdi_set_top_field_man(struct ipu_soc *ipu, bool top_field_0) +{ + uint32_t reg; + + reg = ipu_vdi_read(ipu, VDI_C); + if (top_field_0) + reg &= ~VDI_C_TOP_FIELD_MAN_1; + else + reg |= VDI_C_TOP_FIELD_MAN_1; + ipu_vdi_write(ipu, reg, VDI_C); +} + +void _ipu_vdi_set_motion(struct ipu_soc *ipu, ipu_motion_sel motion_sel) +{ + uint32_t reg; + + reg = ipu_vdi_read(ipu, VDI_C); + reg &= ~(VDI_C_MOT_SEL_FULL | VDI_C_MOT_SEL_MED | VDI_C_MOT_SEL_LOW); + if (motion_sel == HIGH_MOTION) + reg |= VDI_C_MOT_SEL_FULL; + else if (motion_sel == MED_MOTION) + reg |= VDI_C_MOT_SEL_MED; + else + reg |= VDI_C_MOT_SEL_LOW; + + ipu_vdi_write(ipu, reg, VDI_C); +} + +void ic_dump_register(struct ipu_soc *ipu) +{ + printk(KERN_DEBUG "IC_CONF = \t0x%08X\n", ipu_ic_read(ipu, IC_CONF)); + printk(KERN_DEBUG "IC_PRP_ENC_RSC = \t0x%08X\n", + ipu_ic_read(ipu, IC_PRP_ENC_RSC)); + printk(KERN_DEBUG "IC_PRP_VF_RSC = \t0x%08X\n", + ipu_ic_read(ipu, IC_PRP_VF_RSC)); + printk(KERN_DEBUG "IC_PP_RSC = \t0x%08X\n", ipu_ic_read(ipu, IC_PP_RSC)); + printk(KERN_DEBUG "IC_IDMAC_1 = \t0x%08X\n", ipu_ic_read(ipu, IC_IDMAC_1)); + printk(KERN_DEBUG "IC_IDMAC_2 = \t0x%08X\n", ipu_ic_read(ipu, IC_IDMAC_2)); + printk(KERN_DEBUG "IC_IDMAC_3 = \t0x%08X\n", ipu_ic_read(ipu, IC_IDMAC_3)); +} + +void _ipu_ic_enable_task(struct ipu_soc *ipu, ipu_channel_t channel) +{ + uint32_t ic_conf; + + ic_conf = ipu_ic_read(ipu, IC_CONF); + switch (channel) { + case CSI_PRP_VF_MEM: + case MEM_PRP_VF_MEM: + ic_conf |= IC_CONF_PRPVF_EN; + break; + case MEM_VDI_PRP_VF_MEM: + ic_conf |= IC_CONF_PRPVF_EN; + break; + case MEM_ROT_VF_MEM: + ic_conf |= IC_CONF_PRPVF_ROT_EN; + break; + case CSI_PRP_ENC_MEM: + case MEM_PRP_ENC_MEM: + ic_conf |= IC_CONF_PRPENC_EN; + break; + case MEM_ROT_ENC_MEM: + ic_conf |= IC_CONF_PRPENC_ROT_EN; + break; + case MEM_PP_MEM: + ic_conf |= IC_CONF_PP_EN; + break; + case MEM_ROT_PP_MEM: + ic_conf |= IC_CONF_PP_ROT_EN; + break; + default: + break; + } + ipu_ic_write(ipu, ic_conf, IC_CONF); +} + +void _ipu_ic_disable_task(struct ipu_soc *ipu, ipu_channel_t channel) +{ + uint32_t ic_conf; + + ic_conf = ipu_ic_read(ipu, IC_CONF); + switch (channel) { + case CSI_PRP_VF_MEM: + case MEM_PRP_VF_MEM: + ic_conf &= ~IC_CONF_PRPVF_EN; + break; + case MEM_VDI_PRP_VF_MEM: + ic_conf &= ~IC_CONF_PRPVF_EN; + break; + case MEM_ROT_VF_MEM: + ic_conf &= ~IC_CONF_PRPVF_ROT_EN; + break; + case CSI_PRP_ENC_MEM: + case MEM_PRP_ENC_MEM: + ic_conf &= ~IC_CONF_PRPENC_EN; + break; + case MEM_ROT_ENC_MEM: + ic_conf &= ~IC_CONF_PRPENC_ROT_EN; + break; + case MEM_PP_MEM: + ic_conf &= ~IC_CONF_PP_EN; + break; + case MEM_ROT_PP_MEM: + ic_conf &= ~IC_CONF_PP_ROT_EN; + break; + default: + break; + } + ipu_ic_write(ipu, ic_conf, IC_CONF); +} + +void _ipu_vdi_init(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel_params_t *params) +{ + uint32_t reg; + uint32_t pixel_fmt; + + reg = ((params->mem_prp_vf_mem.in_height-1) << 16) | + (params->mem_prp_vf_mem.in_width-1); + ipu_vdi_write(ipu, reg, VDI_FSIZE); + + /* Full motion, only vertical filter is used + Burst size is 4 accesses */ + if (params->mem_prp_vf_mem.in_pixel_fmt == + IPU_PIX_FMT_UYVY || + params->mem_prp_vf_mem.in_pixel_fmt == + IPU_PIX_FMT_YUYV) + pixel_fmt = VDI_C_CH_422; + else + pixel_fmt = VDI_C_CH_420; + + reg = ipu_vdi_read(ipu, VDI_C); + reg |= pixel_fmt; + switch (channel) { + case MEM_VDI_PRP_VF_MEM: + reg |= VDI_C_BURST_SIZE2_4; + break; + case MEM_VDI_PRP_VF_MEM_P: + reg |= VDI_C_BURST_SIZE1_4 | VDI_C_VWM1_SET_1 | VDI_C_VWM1_CLR_2; + break; + case MEM_VDI_PRP_VF_MEM_N: + reg |= VDI_C_BURST_SIZE3_4 | VDI_C_VWM3_SET_1 | VDI_C_VWM3_CLR_2; + break; + default: + break; + } + ipu_vdi_write(ipu, reg, VDI_C); + + if (params->mem_prp_vf_mem.field_fmt == V4L2_FIELD_INTERLACED_TB) + _ipu_vdi_set_top_field_man(ipu, false); + else if (params->mem_prp_vf_mem.field_fmt == V4L2_FIELD_INTERLACED_BT) + _ipu_vdi_set_top_field_man(ipu, true); + + _ipu_vdi_set_motion(ipu, params->mem_prp_vf_mem.motion_sel); + + reg = ipu_ic_read(ipu, IC_CONF); + reg &= ~IC_CONF_RWS_EN; + ipu_ic_write(ipu, reg, IC_CONF); +} + +void _ipu_vdi_uninit(struct ipu_soc *ipu) +{ + ipu_vdi_write(ipu, 0, VDI_FSIZE); + ipu_vdi_write(ipu, 0, VDI_C); +} + +void _ipu_ic_init_prpvf(struct ipu_soc *ipu, ipu_channel_params_t *params, bool src_is_csi) +{ + uint32_t reg, ic_conf; + uint32_t downsizeCoeff, resizeCoeff; + ipu_color_space_t in_fmt, out_fmt; + + /* Setup vertical resizing */ + if (!(params->mem_prp_vf_mem.outv_resize_ratio)) { + _calc_resize_coeffs(ipu, params->mem_prp_vf_mem.in_height, + params->mem_prp_vf_mem.out_height, + &resizeCoeff, &downsizeCoeff); + reg = (downsizeCoeff << 30) | (resizeCoeff << 16); + } else + reg = (params->mem_prp_vf_mem.outv_resize_ratio) << 16; + + /* Setup horizontal resizing */ + /* Upadeted for IC split case */ + if (!(params->mem_prp_vf_mem.outh_resize_ratio)) { + _calc_resize_coeffs(ipu, params->mem_prp_vf_mem.in_width, + params->mem_prp_vf_mem.out_width, + &resizeCoeff, &downsizeCoeff); + reg |= (downsizeCoeff << 14) | resizeCoeff; + } else + reg |= params->mem_prp_vf_mem.outh_resize_ratio; + + ipu_ic_write(ipu, reg, IC_PRP_VF_RSC); + + ic_conf = ipu_ic_read(ipu, IC_CONF); + + /* Setup color space conversion */ + in_fmt = format_to_colorspace(params->mem_prp_vf_mem.in_pixel_fmt); + out_fmt = format_to_colorspace(params->mem_prp_vf_mem.out_pixel_fmt); + if (in_fmt == RGB) { + if ((out_fmt == YCbCr) || (out_fmt == YUV)) { + /* Enable RGB->YCBCR CSC1 */ + _init_csc(ipu, IC_TASK_VIEWFINDER, RGB, out_fmt, 1); + ic_conf |= IC_CONF_PRPVF_CSC1; + } + } + if ((in_fmt == YCbCr) || (in_fmt == YUV)) { + if (out_fmt == RGB) { + /* Enable YCBCR->RGB CSC1 */ + _init_csc(ipu, IC_TASK_VIEWFINDER, YCbCr, RGB, 1); + ic_conf |= IC_CONF_PRPVF_CSC1; + } else { + /* TODO: Support YUV<->YCbCr conversion? */ + } + } + + if (params->mem_prp_vf_mem.graphics_combine_en) { + ic_conf |= IC_CONF_PRPVF_CMB; + + if (!(ic_conf & IC_CONF_PRPVF_CSC1)) { + /* need transparent CSC1 conversion */ + _init_csc(ipu, IC_TASK_VIEWFINDER, RGB, RGB, 1); + ic_conf |= IC_CONF_PRPVF_CSC1; /* Enable RGB->RGB CSC */ + } + in_fmt = format_to_colorspace(params->mem_prp_vf_mem.in_g_pixel_fmt); + out_fmt = format_to_colorspace(params->mem_prp_vf_mem.out_pixel_fmt); + if (in_fmt == RGB) { + if ((out_fmt == YCbCr) || (out_fmt == YUV)) { + /* Enable RGB->YCBCR CSC2 */ + _init_csc(ipu, IC_TASK_VIEWFINDER, RGB, out_fmt, 2); + ic_conf |= IC_CONF_PRPVF_CSC2; + } + } + if ((in_fmt == YCbCr) || (in_fmt == YUV)) { + if (out_fmt == RGB) { + /* Enable YCBCR->RGB CSC2 */ + _init_csc(ipu, IC_TASK_VIEWFINDER, YCbCr, RGB, 2); + ic_conf |= IC_CONF_PRPVF_CSC2; + } else { + /* TODO: Support YUV<->YCbCr conversion? */ + } + } + + if (params->mem_prp_vf_mem.global_alpha_en) { + ic_conf |= IC_CONF_IC_GLB_LOC_A; + reg = ipu_ic_read(ipu, IC_CMBP_1); + reg &= ~(0xff); + reg |= params->mem_prp_vf_mem.alpha; + ipu_ic_write(ipu, reg, IC_CMBP_1); + } else + ic_conf &= ~IC_CONF_IC_GLB_LOC_A; + + if (params->mem_prp_vf_mem.key_color_en) { + ic_conf |= IC_CONF_KEY_COLOR_EN; + ipu_ic_write(ipu, params->mem_prp_vf_mem.key_color, + IC_CMBP_2); + } else + ic_conf &= ~IC_CONF_KEY_COLOR_EN; + } else { + ic_conf &= ~IC_CONF_PRPVF_CMB; + } + + if (src_is_csi) + ic_conf &= ~IC_CONF_RWS_EN; + else + ic_conf |= IC_CONF_RWS_EN; + + ipu_ic_write(ipu, ic_conf, IC_CONF); +} + +void _ipu_ic_uninit_prpvf(struct ipu_soc *ipu) +{ + uint32_t reg; + + reg = ipu_ic_read(ipu, IC_CONF); + reg &= ~(IC_CONF_PRPVF_EN | IC_CONF_PRPVF_CMB | + IC_CONF_PRPVF_CSC2 | IC_CONF_PRPVF_CSC1); + ipu_ic_write(ipu, reg, IC_CONF); +} + +void _ipu_ic_init_rotate_vf(struct ipu_soc *ipu, ipu_channel_params_t *params) +{ +} + +void _ipu_ic_uninit_rotate_vf(struct ipu_soc *ipu) +{ + uint32_t reg; + reg = ipu_ic_read(ipu, IC_CONF); + reg &= ~IC_CONF_PRPVF_ROT_EN; + ipu_ic_write(ipu, reg, IC_CONF); +} + +void _ipu_ic_init_prpenc(struct ipu_soc *ipu, ipu_channel_params_t *params, bool src_is_csi) +{ + uint32_t reg, ic_conf; + uint32_t downsizeCoeff, resizeCoeff; + ipu_color_space_t in_fmt, out_fmt; + + /* Setup vertical resizing */ + if (!(params->mem_prp_enc_mem.outv_resize_ratio)) { + _calc_resize_coeffs(ipu, params->mem_prp_enc_mem.in_height, + params->mem_prp_enc_mem.out_height, + &resizeCoeff, &downsizeCoeff); + reg = (downsizeCoeff << 30) | (resizeCoeff << 16); + } else + reg = (params->mem_prp_enc_mem.outv_resize_ratio) << 16; + + /* Setup horizontal resizing */ + /* Upadeted for IC split case */ + if (!(params->mem_prp_enc_mem.outh_resize_ratio)) { + _calc_resize_coeffs(ipu, params->mem_prp_enc_mem.in_width, + params->mem_prp_enc_mem.out_width, + &resizeCoeff, &downsizeCoeff); + reg |= (downsizeCoeff << 14) | resizeCoeff; + } else + reg |= params->mem_prp_enc_mem.outh_resize_ratio; + + ipu_ic_write(ipu, reg, IC_PRP_ENC_RSC); + + ic_conf = ipu_ic_read(ipu, IC_CONF); + + /* Setup color space conversion */ + in_fmt = format_to_colorspace(params->mem_prp_enc_mem.in_pixel_fmt); + out_fmt = format_to_colorspace(params->mem_prp_enc_mem.out_pixel_fmt); + if (in_fmt == RGB) { + if ((out_fmt == YCbCr) || (out_fmt == YUV)) { + /* Enable RGB->YCBCR CSC1 */ + _init_csc(ipu, IC_TASK_ENCODER, RGB, out_fmt, 1); + ic_conf |= IC_CONF_PRPENC_CSC1; + } + } + if ((in_fmt == YCbCr) || (in_fmt == YUV)) { + if (out_fmt == RGB) { + /* Enable YCBCR->RGB CSC1 */ + _init_csc(ipu, IC_TASK_ENCODER, YCbCr, RGB, 1); + ic_conf |= IC_CONF_PRPENC_CSC1; + } else { + /* TODO: Support YUV<->YCbCr conversion? */ + } + } + + if (src_is_csi) + ic_conf &= ~IC_CONF_RWS_EN; + else + ic_conf |= IC_CONF_RWS_EN; + + ipu_ic_write(ipu, ic_conf, IC_CONF); +} + +void _ipu_ic_uninit_prpenc(struct ipu_soc *ipu) +{ + uint32_t reg; + + reg = ipu_ic_read(ipu, IC_CONF); + reg &= ~(IC_CONF_PRPENC_EN | IC_CONF_PRPENC_CSC1); + ipu_ic_write(ipu, reg, IC_CONF); +} + +void _ipu_ic_init_rotate_enc(struct ipu_soc *ipu, ipu_channel_params_t *params) +{ +} + +void _ipu_ic_uninit_rotate_enc(struct ipu_soc *ipu) +{ + uint32_t reg; + + reg = ipu_ic_read(ipu, IC_CONF); + reg &= ~(IC_CONF_PRPENC_ROT_EN); + ipu_ic_write(ipu, reg, IC_CONF); +} + +void _ipu_ic_init_pp(struct ipu_soc *ipu, ipu_channel_params_t *params) +{ + uint32_t reg, ic_conf; + uint32_t downsizeCoeff, resizeCoeff; + ipu_color_space_t in_fmt, out_fmt; + + /* Setup vertical resizing */ + if (!(params->mem_pp_mem.outv_resize_ratio)) { + _calc_resize_coeffs(ipu, params->mem_pp_mem.in_height, + params->mem_pp_mem.out_height, + &resizeCoeff, &downsizeCoeff); + reg = (downsizeCoeff << 30) | (resizeCoeff << 16); + } else { + reg = (params->mem_pp_mem.outv_resize_ratio) << 16; + } + + /* Setup horizontal resizing */ + /* Upadeted for IC split case */ + if (!(params->mem_pp_mem.outh_resize_ratio)) { + _calc_resize_coeffs(ipu, params->mem_pp_mem.in_width, + params->mem_pp_mem.out_width, + &resizeCoeff, &downsizeCoeff); + reg |= (downsizeCoeff << 14) | resizeCoeff; + } else { + reg |= params->mem_pp_mem.outh_resize_ratio; + } + + ipu_ic_write(ipu, reg, IC_PP_RSC); + + ic_conf = ipu_ic_read(ipu, IC_CONF); + + /* Setup color space conversion */ + in_fmt = format_to_colorspace(params->mem_pp_mem.in_pixel_fmt); + out_fmt = format_to_colorspace(params->mem_pp_mem.out_pixel_fmt); + if (in_fmt == RGB) { + if ((out_fmt == YCbCr) || (out_fmt == YUV)) { + /* Enable RGB->YCBCR CSC1 */ + _init_csc(ipu, IC_TASK_POST_PROCESSOR, RGB, out_fmt, 1); + ic_conf |= IC_CONF_PP_CSC1; + } + } + if ((in_fmt == YCbCr) || (in_fmt == YUV)) { + if (out_fmt == RGB) { + /* Enable YCBCR->RGB CSC1 */ + _init_csc(ipu, IC_TASK_POST_PROCESSOR, YCbCr, RGB, 1); + ic_conf |= IC_CONF_PP_CSC1; + } else { + /* TODO: Support YUV<->YCbCr conversion? */ + } + } + + if (params->mem_pp_mem.graphics_combine_en) { + ic_conf |= IC_CONF_PP_CMB; + + if (!(ic_conf & IC_CONF_PP_CSC1)) { + /* need transparent CSC1 conversion */ + _init_csc(ipu, IC_TASK_POST_PROCESSOR, RGB, RGB, 1); + ic_conf |= IC_CONF_PP_CSC1; /* Enable RGB->RGB CSC */ + } + + in_fmt = format_to_colorspace(params->mem_pp_mem.in_g_pixel_fmt); + out_fmt = format_to_colorspace(params->mem_pp_mem.out_pixel_fmt); + if (in_fmt == RGB) { + if ((out_fmt == YCbCr) || (out_fmt == YUV)) { + /* Enable RGB->YCBCR CSC2 */ + _init_csc(ipu, IC_TASK_POST_PROCESSOR, RGB, out_fmt, 2); + ic_conf |= IC_CONF_PP_CSC2; + } + } + if ((in_fmt == YCbCr) || (in_fmt == YUV)) { + if (out_fmt == RGB) { + /* Enable YCBCR->RGB CSC2 */ + _init_csc(ipu, IC_TASK_POST_PROCESSOR, YCbCr, RGB, 2); + ic_conf |= IC_CONF_PP_CSC2; + } else { + /* TODO: Support YUV<->YCbCr conversion? */ + } + } + + if (params->mem_pp_mem.global_alpha_en) { + ic_conf |= IC_CONF_IC_GLB_LOC_A; + reg = ipu_ic_read(ipu, IC_CMBP_1); + reg &= ~(0xff00); + reg |= (params->mem_pp_mem.alpha << 8); + ipu_ic_write(ipu, reg, IC_CMBP_1); + } else + ic_conf &= ~IC_CONF_IC_GLB_LOC_A; + + if (params->mem_pp_mem.key_color_en) { + ic_conf |= IC_CONF_KEY_COLOR_EN; + ipu_ic_write(ipu, params->mem_pp_mem.key_color, + IC_CMBP_2); + } else + ic_conf &= ~IC_CONF_KEY_COLOR_EN; + } else { + ic_conf &= ~IC_CONF_PP_CMB; + } + + ipu_ic_write(ipu, ic_conf, IC_CONF); +} + +void _ipu_ic_uninit_pp(struct ipu_soc *ipu) +{ + uint32_t reg; + + reg = ipu_ic_read(ipu, IC_CONF); + reg &= ~(IC_CONF_PP_EN | IC_CONF_PP_CSC1 | IC_CONF_PP_CSC2 | + IC_CONF_PP_CMB); + ipu_ic_write(ipu, reg, IC_CONF); +} + +void _ipu_ic_init_rotate_pp(struct ipu_soc *ipu, ipu_channel_params_t *params) +{ +} + +void _ipu_ic_uninit_rotate_pp(struct ipu_soc *ipu) +{ + uint32_t reg; + reg = ipu_ic_read(ipu, IC_CONF); + reg &= ~IC_CONF_PP_ROT_EN; + ipu_ic_write(ipu, reg, IC_CONF); +} + +int _ipu_ic_idma_init(struct ipu_soc *ipu, int dma_chan, + uint16_t width, uint16_t height, + int burst_size, ipu_rotate_mode_t rot) +{ + u32 ic_idmac_1, ic_idmac_2, ic_idmac_3; + u32 temp_rot = bitrev8(rot) >> 5; + bool need_hor_flip = false; + + if ((burst_size != 8) && (burst_size != 16)) { + dev_dbg(ipu->dev, "Illegal burst length for IC\n"); + return -EINVAL; + } + + width--; + height--; + + if (temp_rot & 0x2) /* Need horizontal flip */ + need_hor_flip = true; + + ic_idmac_1 = ipu_ic_read(ipu, IC_IDMAC_1); + ic_idmac_2 = ipu_ic_read(ipu, IC_IDMAC_2); + ic_idmac_3 = ipu_ic_read(ipu, IC_IDMAC_3); + if (dma_chan == 22) { /* PP output - CB2 */ + if (burst_size == 16) + ic_idmac_1 |= IC_IDMAC_1_CB2_BURST_16; + else + ic_idmac_1 &= ~IC_IDMAC_1_CB2_BURST_16; + + if (need_hor_flip) + ic_idmac_1 |= IC_IDMAC_1_PP_FLIP_RS; + else + ic_idmac_1 &= ~IC_IDMAC_1_PP_FLIP_RS; + + ic_idmac_2 &= ~IC_IDMAC_2_PP_HEIGHT_MASK; + ic_idmac_2 |= height << IC_IDMAC_2_PP_HEIGHT_OFFSET; + + ic_idmac_3 &= ~IC_IDMAC_3_PP_WIDTH_MASK; + ic_idmac_3 |= width << IC_IDMAC_3_PP_WIDTH_OFFSET; + } else if (dma_chan == 11) { /* PP Input - CB5 */ + if (burst_size == 16) + ic_idmac_1 |= IC_IDMAC_1_CB5_BURST_16; + else + ic_idmac_1 &= ~IC_IDMAC_1_CB5_BURST_16; + } else if (dma_chan == 47) { /* PP Rot input */ + ic_idmac_1 &= ~IC_IDMAC_1_PP_ROT_MASK; + ic_idmac_1 |= temp_rot << IC_IDMAC_1_PP_ROT_OFFSET; + } + + if (dma_chan == 12) { /* PRP Input - CB6 */ + if (burst_size == 16) + ic_idmac_1 |= IC_IDMAC_1_CB6_BURST_16; + else + ic_idmac_1 &= ~IC_IDMAC_1_CB6_BURST_16; + } + + if (dma_chan == 20) { /* PRP ENC output - CB0 */ + if (burst_size == 16) + ic_idmac_1 |= IC_IDMAC_1_CB0_BURST_16; + else + ic_idmac_1 &= ~IC_IDMAC_1_CB0_BURST_16; + + if (need_hor_flip) + ic_idmac_1 |= IC_IDMAC_1_PRPENC_FLIP_RS; + else + ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_FLIP_RS; + + ic_idmac_2 &= ~IC_IDMAC_2_PRPENC_HEIGHT_MASK; + ic_idmac_2 |= height << IC_IDMAC_2_PRPENC_HEIGHT_OFFSET; + + ic_idmac_3 &= ~IC_IDMAC_3_PRPENC_WIDTH_MASK; + ic_idmac_3 |= width << IC_IDMAC_3_PRPENC_WIDTH_OFFSET; + + } else if (dma_chan == 45) { /* PRP ENC Rot input */ + ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_ROT_MASK; + ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPENC_ROT_OFFSET; + } + + if (dma_chan == 21) { /* PRP VF output - CB1 */ + if (burst_size == 16) + ic_idmac_1 |= IC_IDMAC_1_CB1_BURST_16; + else + ic_idmac_1 &= ~IC_IDMAC_1_CB1_BURST_16; + + if (need_hor_flip) + ic_idmac_1 |= IC_IDMAC_1_PRPVF_FLIP_RS; + else + ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_FLIP_RS; + + ic_idmac_2 &= ~IC_IDMAC_2_PRPVF_HEIGHT_MASK; + ic_idmac_2 |= height << IC_IDMAC_2_PRPVF_HEIGHT_OFFSET; + + ic_idmac_3 &= ~IC_IDMAC_3_PRPVF_WIDTH_MASK; + ic_idmac_3 |= width << IC_IDMAC_3_PRPVF_WIDTH_OFFSET; + + } else if (dma_chan == 46) { /* PRP VF Rot input */ + ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_ROT_MASK; + ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPVF_ROT_OFFSET; + } + + if (dma_chan == 14) { /* PRP VF graphics combining input - CB3 */ + if (burst_size == 16) + ic_idmac_1 |= IC_IDMAC_1_CB3_BURST_16; + else + ic_idmac_1 &= ~IC_IDMAC_1_CB3_BURST_16; + } else if (dma_chan == 15) { /* PP graphics combining input - CB4 */ + if (burst_size == 16) + ic_idmac_1 |= IC_IDMAC_1_CB4_BURST_16; + else + ic_idmac_1 &= ~IC_IDMAC_1_CB4_BURST_16; + } + + ipu_ic_write(ipu, ic_idmac_1, IC_IDMAC_1); + ipu_ic_write(ipu, ic_idmac_2, IC_IDMAC_2); + ipu_ic_write(ipu, ic_idmac_3, IC_IDMAC_3); + + return 0; +} + +static void _init_csc(struct ipu_soc *ipu, uint8_t ic_task, ipu_color_space_t in_format, + ipu_color_space_t out_format, int csc_index) +{ + +/* Y = R * .299 + G * .587 + B * .114; + U = R * -.169 + G * -.332 + B * .500 + 128.; + V = R * .500 + G * -.419 + B * -.0813 + 128.;*/ + static const uint32_t rgb2ycbcr_coeff[4][3] = { + {0x004D, 0x0096, 0x001D}, + {0x01D5, 0x01AB, 0x0080}, + {0x0080, 0x0195, 0x01EB}, + {0x0000, 0x0200, 0x0200}, /* A0, A1, A2 */ + }; + + /* transparent RGB->RGB matrix for combining + */ + static const uint32_t rgb2rgb_coeff[4][3] = { + {0x0080, 0x0000, 0x0000}, + {0x0000, 0x0080, 0x0000}, + {0x0000, 0x0000, 0x0080}, + {0x0000, 0x0000, 0x0000}, /* A0, A1, A2 */ + }; + +/* R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128)); + G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128)); + B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128); */ + static const uint32_t ycbcr2rgb_coeff[4][3] = { + {149, 0, 204}, + {149, 462, 408}, + {149, 255, 0}, + {8192 - 446, 266, 8192 - 554}, /* A0, A1, A2 */ + }; + + uint32_t param; + uint32_t *base = NULL; + + if (ic_task == IC_TASK_ENCODER) { + base = ipu->tpmem_base + 0x2008 / 4; + } else if (ic_task == IC_TASK_VIEWFINDER) { + if (csc_index == 1) + base = ipu->tpmem_base + 0x4028 / 4; + else + base = ipu->tpmem_base + 0x4040 / 4; + } else if (ic_task == IC_TASK_POST_PROCESSOR) { + if (csc_index == 1) + base = ipu->tpmem_base + 0x6060 / 4; + else + base = ipu->tpmem_base + 0x6078 / 4; + } else { + BUG(); + } + + if ((in_format == YCbCr) && (out_format == RGB)) { + /* Init CSC (YCbCr->RGB) */ + param = (ycbcr2rgb_coeff[3][0] << 27) | + (ycbcr2rgb_coeff[0][0] << 18) | + (ycbcr2rgb_coeff[1][1] << 9) | ycbcr2rgb_coeff[2][2]; + writel(param, base++); + /* scale = 2, sat = 0 */ + param = (ycbcr2rgb_coeff[3][0] >> 5) | (2L << (40 - 32)); + writel(param, base++); + + param = (ycbcr2rgb_coeff[3][1] << 27) | + (ycbcr2rgb_coeff[0][1] << 18) | + (ycbcr2rgb_coeff[1][0] << 9) | ycbcr2rgb_coeff[2][0]; + writel(param, base++); + param = (ycbcr2rgb_coeff[3][1] >> 5); + writel(param, base++); + + param = (ycbcr2rgb_coeff[3][2] << 27) | + (ycbcr2rgb_coeff[0][2] << 18) | + (ycbcr2rgb_coeff[1][2] << 9) | ycbcr2rgb_coeff[2][1]; + writel(param, base++); + param = (ycbcr2rgb_coeff[3][2] >> 5); + writel(param, base++); + } else if ((in_format == RGB) && (out_format == YCbCr)) { + /* Init CSC (RGB->YCbCr) */ + param = (rgb2ycbcr_coeff[3][0] << 27) | + (rgb2ycbcr_coeff[0][0] << 18) | + (rgb2ycbcr_coeff[1][1] << 9) | rgb2ycbcr_coeff[2][2]; + writel(param, base++); + /* scale = 1, sat = 0 */ + param = (rgb2ycbcr_coeff[3][0] >> 5) | (1UL << 8); + writel(param, base++); + + param = (rgb2ycbcr_coeff[3][1] << 27) | + (rgb2ycbcr_coeff[0][1] << 18) | + (rgb2ycbcr_coeff[1][0] << 9) | rgb2ycbcr_coeff[2][0]; + writel(param, base++); + param = (rgb2ycbcr_coeff[3][1] >> 5); + writel(param, base++); + + param = (rgb2ycbcr_coeff[3][2] << 27) | + (rgb2ycbcr_coeff[0][2] << 18) | + (rgb2ycbcr_coeff[1][2] << 9) | rgb2ycbcr_coeff[2][1]; + writel(param, base++); + param = (rgb2ycbcr_coeff[3][2] >> 5); + writel(param, base++); + } else if ((in_format == RGB) && (out_format == RGB)) { + /* Init CSC */ + param = + (rgb2rgb_coeff[3][0] << 27) | (rgb2rgb_coeff[0][0] << 18) | + (rgb2rgb_coeff[1][1] << 9) | rgb2rgb_coeff[2][2]; + writel(param, base++); + /* scale = 2, sat = 0 */ + param = (rgb2rgb_coeff[3][0] >> 5) | (2UL << 8); + writel(param, base++); + + param = + (rgb2rgb_coeff[3][1] << 27) | (rgb2rgb_coeff[0][1] << 18) | + (rgb2rgb_coeff[1][0] << 9) | rgb2rgb_coeff[2][0]; + writel(param, base++); + param = (rgb2rgb_coeff[3][1] >> 5); + writel(param, base++); + + param = + (rgb2rgb_coeff[3][2] << 27) | (rgb2rgb_coeff[0][2] << 18) | + (rgb2rgb_coeff[1][2] << 9) | rgb2rgb_coeff[2][1]; + writel(param, base++); + param = (rgb2rgb_coeff[3][2] >> 5); + writel(param, base++); + } else { + dev_err(ipu->dev, "Unsupported color space conversion\n"); + } +} + +static bool _calc_resize_coeffs(struct ipu_soc *ipu, + uint32_t inSize, uint32_t outSize, + uint32_t *resizeCoeff, + uint32_t *downsizeCoeff) +{ + uint32_t tempSize; + uint32_t tempDownsize; + + /* Input size cannot be more than 4096 */ + /* Output size cannot be more than 1024 */ + if ((inSize > 4096) || (outSize > 1024)) + return false; + + /* Cannot downsize more than 8:1 */ + if ((outSize << 3) < inSize) + return false; + + /* Compute downsizing coefficient */ + /* Output of downsizing unit cannot be more than 1024 */ + tempDownsize = 0; + tempSize = inSize; + while (((tempSize > 1024) || (tempSize >= outSize * 2)) && + (tempDownsize < 2)) { + tempSize >>= 1; + tempDownsize++; + } + *downsizeCoeff = tempDownsize; + + /* compute resizing coefficient using the following equation: + resizeCoeff = M*(SI -1)/(SO - 1) + where M = 2^13, SI - input size, SO - output size */ + *resizeCoeff = (8192L * (tempSize - 1)) / (outSize - 1); + if (*resizeCoeff >= 16384L) { + dev_err(ipu->dev, "Warning! Overflow on resize coeff.\n"); + *resizeCoeff = 0x3FFF; + } + + dev_dbg(ipu->dev, "resizing from %u -> %u pixels, " + "downsize=%u, resize=%u.%lu (reg=%u)\n", inSize, outSize, + *downsizeCoeff, (*resizeCoeff >= 8192L) ? 1 : 0, + ((*resizeCoeff & 0x1FFF) * 10000L) / 8192L, *resizeCoeff); + + return true; +} + +void _ipu_vdi_toggle_top_field_man(struct ipu_soc *ipu) +{ + uint32_t reg; + uint32_t mask_reg; + + reg = ipu_vdi_read(ipu, VDI_C); + mask_reg = reg & VDI_C_TOP_FIELD_MAN_1; + if (mask_reg == VDI_C_TOP_FIELD_MAN_1) + reg &= ~VDI_C_TOP_FIELD_MAN_1; + else + reg |= VDI_C_TOP_FIELD_MAN_1; + + ipu_vdi_write(ipu, reg, VDI_C); +} diff --git a/drivers/mxc/ipu3/ipu_param_mem.h b/drivers/mxc/ipu3/ipu_param_mem.h new file mode 100644 index 00000000000..d2ad2695b68 --- /dev/null +++ b/drivers/mxc/ipu3/ipu_param_mem.h @@ -0,0 +1,798 @@ +/* + * 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_IPU_PARAM_MEM_H__ +#define __INCLUDE_IPU_PARAM_MEM_H__ + +#include <linux/types.h> +#include <linux/bitrev.h> + +extern u32 *ipu_cpmem_base; + +struct ipu_ch_param_word { + uint32_t data[5]; + uint32_t res[3]; +}; + +struct ipu_ch_param { + struct ipu_ch_param_word word[2]; +}; + +#define ipu_ch_param_addr(ipu, ch) (((struct ipu_ch_param *)ipu->cpmem_base) + (ch)) + +#define _param_word(base, w) \ + (((struct ipu_ch_param *)(base))->word[(w)].data) + +#define ipu_ch_param_set_field(base, w, bit, size, v) { \ + int i = (bit) / 32; \ + int off = (bit) % 32; \ + _param_word(base, w)[i] |= (v) << off; \ + if (((bit)+(size)-1)/32 > i) { \ + _param_word(base, w)[i + 1] |= (v) >> (off ? (32 - off) : 0); \ + } \ +} + +#define ipu_ch_param_set_field_io(base, w, bit, size, v) { \ + int i = (bit) / 32; \ + int off = (bit) % 32; \ + unsigned reg_offset; \ + u32 temp; \ + reg_offset = sizeof(struct ipu_ch_param_word) * w / 4; \ + reg_offset += i; \ + temp = readl((u32 *)base + reg_offset); \ + temp |= (v) << off; \ + writel(temp, (u32 *)base + reg_offset); \ + if (((bit)+(size)-1)/32 > i) { \ + reg_offset++; \ + temp = readl((u32 *)base + reg_offset); \ + temp |= (v) >> (off ? (32 - off) : 0); \ + writel(temp, (u32 *)base + reg_offset); \ + } \ +} + +#define ipu_ch_param_mod_field(base, w, bit, size, v) { \ + int i = (bit) / 32; \ + int off = (bit) % 32; \ + u32 mask = (1UL << size) - 1; \ + u32 temp = _param_word(base, w)[i]; \ + temp &= ~(mask << off); \ + _param_word(base, w)[i] = temp | (v) << off; \ + if (((bit)+(size)-1)/32 > i) { \ + temp = _param_word(base, w)[i + 1]; \ + temp &= ~(mask >> (32 - off)); \ + _param_word(base, w)[i + 1] = \ + temp | ((v) >> (off ? (32 - off) : 0)); \ + } \ +} + +#define ipu_ch_param_mod_field_io(base, w, bit, size, v) { \ + int i = (bit) / 32; \ + int off = (bit) % 32; \ + u32 mask = (1UL << size) - 1; \ + unsigned reg_offset; \ + u32 temp; \ + reg_offset = sizeof(struct ipu_ch_param_word) * w / 4; \ + reg_offset += i; \ + temp = readl((u32 *)base + reg_offset); \ + temp &= ~(mask << off); \ + temp |= (v) << off; \ + writel(temp, (u32 *)base + reg_offset); \ + if (((bit)+(size)-1)/32 > i) { \ + reg_offset++; \ + temp = readl((u32 *)base + reg_offset); \ + temp &= ~(mask >> (32 - off)); \ + temp |= ((v) >> (off ? (32 - off) : 0)); \ + writel(temp, (u32 *)base + reg_offset); \ + } \ +} + +#define ipu_ch_param_read_field(base, w, bit, size) ({ \ + u32 temp2; \ + int i = (bit) / 32; \ + int off = (bit) % 32; \ + u32 mask = (1UL << size) - 1; \ + u32 temp1 = _param_word(base, w)[i]; \ + temp1 = mask & (temp1 >> off); \ + if (((bit)+(size)-1)/32 > i) { \ + temp2 = _param_word(base, w)[i + 1]; \ + temp2 &= mask >> (off ? (32 - off) : 0); \ + temp1 |= temp2 << (off ? (32 - off) : 0); \ + } \ + temp1; \ +}) + +#define ipu_ch_param_read_field_io(base, w, bit, size) ({ \ + u32 temp1, temp2; \ + int i = (bit) / 32; \ + int off = (bit) % 32; \ + u32 mask = (1UL << size) - 1; \ + unsigned reg_offset; \ + reg_offset = sizeof(struct ipu_ch_param_word) * w / 4; \ + reg_offset += i; \ + temp1 = readl((u32 *)base + reg_offset); \ + temp1 = mask & (temp1 >> off); \ + if (((bit)+(size)-1)/32 > i) { \ + reg_offset++; \ + temp2 = readl((u32 *)base + reg_offset); \ + temp2 &= mask >> (off ? (32 - off) : 0); \ + temp1 |= temp2 << (off ? (32 - off) : 0); \ + } \ + temp1; \ +}) + +static inline int __ipu_ch_get_third_buf_cpmem_num(int ch) +{ + switch (ch) { + case 8: + return 64; + case 9: + return 65; + case 10: + return 66; + case 13: + return 67; + case 21: + return 68; + case 23: + return 69; + case 27: + return 70; + case 28: + return 71; + default: + return -EINVAL; + } + return 0; +} + +static inline void _ipu_ch_params_set_packing(struct ipu_ch_param *p, + int red_width, int red_offset, + int green_width, int green_offset, + int blue_width, int blue_offset, + int alpha_width, int alpha_offset) +{ + /* Setup red width and offset */ + ipu_ch_param_set_field(p, 1, 116, 3, red_width - 1); + ipu_ch_param_set_field(p, 1, 128, 5, red_offset); + /* Setup green width and offset */ + ipu_ch_param_set_field(p, 1, 119, 3, green_width - 1); + ipu_ch_param_set_field(p, 1, 133, 5, green_offset); + /* Setup blue width and offset */ + ipu_ch_param_set_field(p, 1, 122, 3, blue_width - 1); + ipu_ch_param_set_field(p, 1, 138, 5, blue_offset); + /* Setup alpha width and offset */ + ipu_ch_param_set_field(p, 1, 125, 3, alpha_width - 1); + ipu_ch_param_set_field(p, 1, 143, 5, alpha_offset); +} + +static inline void _ipu_ch_param_dump(struct ipu_soc *ipu, int ch) +{ + struct ipu_ch_param *p = ipu_ch_param_addr(ipu, ch); + dev_dbg(ipu->dev, "ch %d word 0 - %08X %08X %08X %08X %08X\n", ch, + p->word[0].data[0], p->word[0].data[1], p->word[0].data[2], + p->word[0].data[3], p->word[0].data[4]); + dev_dbg(ipu->dev, "ch %d word 1 - %08X %08X %08X %08X %08X\n", ch, + p->word[1].data[0], p->word[1].data[1], p->word[1].data[2], + p->word[1].data[3], p->word[1].data[4]); + dev_dbg(ipu->dev, "PFS 0x%x, ", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 85, 4)); + dev_dbg(ipu->dev, "BPP 0x%x, ", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 107, 3)); + dev_dbg(ipu->dev, "NPB 0x%x\n", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 78, 7)); + + dev_dbg(ipu->dev, "FW %d, ", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 125, 13)); + dev_dbg(ipu->dev, "FH %d, ", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 138, 12)); + dev_dbg(ipu->dev, "Stride %d\n", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 102, 14)); + + dev_dbg(ipu->dev, "Width0 %d+1, ", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 116, 3)); + dev_dbg(ipu->dev, "Width1 %d+1, ", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 119, 3)); + dev_dbg(ipu->dev, "Width2 %d+1, ", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 122, 3)); + dev_dbg(ipu->dev, "Width3 %d+1, ", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 125, 3)); + dev_dbg(ipu->dev, "Offset0 %d, ", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 128, 5)); + dev_dbg(ipu->dev, "Offset1 %d, ", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 133, 5)); + dev_dbg(ipu->dev, "Offset2 %d, ", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 138, 5)); + dev_dbg(ipu->dev, "Offset3 %d\n", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 143, 5)); +} + +static inline void _ipu_ch_param_init(struct ipu_soc *ipu, int ch, + uint32_t pixel_fmt, uint32_t width, + uint32_t height, uint32_t stride, + uint32_t u, uint32_t v, + uint32_t uv_stride, dma_addr_t addr0, + dma_addr_t addr1, dma_addr_t addr2) +{ + uint32_t u_offset = 0; + uint32_t v_offset = 0; + int32_t sub_ch = 0; + struct ipu_ch_param params; + + memset(¶ms, 0, sizeof(params)); + + ipu_ch_param_set_field(¶ms, 0, 125, 13, width - 1); + + if ((ch == 8) || (ch == 9) || (ch == 10)) { + ipu_ch_param_set_field(¶ms, 0, 138, 12, (height / 2) - 1); + ipu_ch_param_set_field(¶ms, 1, 102, 14, (stride * 2) - 1); + } else { + ipu_ch_param_set_field(¶ms, 0, 138, 12, height - 1); + ipu_ch_param_set_field(¶ms, 1, 102, 14, stride - 1); + } + + /* EBA is 8-byte aligned */ + ipu_ch_param_set_field(¶ms, 1, 0, 29, addr0 >> 3); + ipu_ch_param_set_field(¶ms, 1, 29, 29, addr1 >> 3); + if (addr0%8) + dev_warn(ipu->dev, + "IDMAC%d's EBA0 is not 8-byte aligned\n", ch); + if (addr1%8) + dev_warn(ipu->dev, + "IDMAC%d's EBA1 is not 8-byte aligned\n", ch); + + switch (pixel_fmt) { + case IPU_PIX_FMT_GENERIC: + /*Represents 8-bit Generic data */ + ipu_ch_param_set_field(¶ms, 0, 107, 3, 5); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 6); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 63); /* burst size */ + + break; + case IPU_PIX_FMT_GENERIC_32: + /*Represents 32-bit Generic data */ + break; + case IPU_PIX_FMT_RGB565: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + + _ipu_ch_params_set_packing(¶ms, 5, 0, 6, 5, 5, 11, 8, 16); + break; + case IPU_PIX_FMT_BGR24: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 1); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 19); /* burst size */ + + _ipu_ch_params_set_packing(¶ms, 8, 0, 8, 8, 8, 16, 8, 24); + break; + case IPU_PIX_FMT_RGB24: + case IPU_PIX_FMT_YUV444: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 1); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 19); /* burst size */ + + _ipu_ch_params_set_packing(¶ms, 8, 16, 8, 8, 8, 0, 8, 24); + break; + case IPU_PIX_FMT_VYU444: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 1); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 19); /* burst size */ + + _ipu_ch_params_set_packing(¶ms, 8, 8, 8, 0, 8, 16, 8, 24); + break; + case IPU_PIX_FMT_BGRA32: + case IPU_PIX_FMT_BGR32: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + + _ipu_ch_params_set_packing(¶ms, 8, 8, 8, 16, 8, 24, 8, 0); + break; + case IPU_PIX_FMT_RGBA32: + case IPU_PIX_FMT_RGB32: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + + _ipu_ch_params_set_packing(¶ms, 8, 24, 8, 16, 8, 8, 8, 0); + break; + case IPU_PIX_FMT_ABGR32: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + + _ipu_ch_params_set_packing(¶ms, 8, 0, 8, 8, 8, 16, 8, 24); + break; + case IPU_PIX_FMT_UYVY: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 0xA); /* pix format */ + if ((ch == 8) || (ch == 9) || (ch == 10)) { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + } else { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + } + break; + case IPU_PIX_FMT_YUYV: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 0x8); /* pix format */ + if ((ch == 8) || (ch == 9) || (ch == 10)) { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + } else { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + } + break; + case IPU_PIX_FMT_YUV420P2: + case IPU_PIX_FMT_YUV420P: + ipu_ch_param_set_field(¶ms, 1, 85, 4, 2); /* pix format */ + + if (uv_stride < stride / 2) + uv_stride = stride / 2; + + u_offset = stride * height; + v_offset = u_offset + (uv_stride * height / 2); + if ((ch == 8) || (ch == 9) || (ch == 10)) { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + uv_stride = uv_stride*2; + } else { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + } + break; + case IPU_PIX_FMT_YVU420P: + ipu_ch_param_set_field(¶ms, 1, 85, 4, 2); /* pix format */ + + if (uv_stride < stride / 2) + uv_stride = stride / 2; + + v_offset = stride * height; + u_offset = v_offset + (uv_stride * height / 2); + if ((ch == 8) || (ch == 9) || (ch == 10)) { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + uv_stride = uv_stride*2; + } else { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + } + break; + case IPU_PIX_FMT_YVU422P: + /* BPP & pixel format */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 1); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + + if (uv_stride < stride / 2) + uv_stride = stride / 2; + + v_offset = (v == 0) ? stride * height : v; + u_offset = (u == 0) ? v_offset + v_offset / 2 : u; + break; + case IPU_PIX_FMT_YUV422P: + /* BPP & pixel format */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 1); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + + if (uv_stride < stride / 2) + uv_stride = stride / 2; + + u_offset = (u == 0) ? stride * height : u; + v_offset = (v == 0) ? u_offset + u_offset / 2 : v; + break; + case IPU_PIX_FMT_NV12: + /* BPP & pixel format */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 4); /* pix format */ + uv_stride = stride; + u_offset = (u == 0) ? stride * height : u; + if ((ch == 8) || (ch == 9) || (ch == 10)) { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + uv_stride = uv_stride*2; + } else { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + } + break; + default: + dev_err(ipu->dev, "mxc ipu: unimplemented pixel format\n"); + break; + } + /*set burst size to 16*/ + + + if (uv_stride) + ipu_ch_param_set_field(¶ms, 1, 128, 14, uv_stride - 1); + + /* Get the uv offset from user when need cropping */ + if (u || v) { + u_offset = u; + v_offset = v; + } + + /* UBO and VBO are 22-bit and 8-byte aligned */ + if (u_offset/8 > 0x3fffff) + dev_warn(ipu->dev, + "IDMAC%d's U offset exceeds IPU limitation\n", ch); + if (v_offset/8 > 0x3fffff) + dev_warn(ipu->dev, + "IDMAC%d's V offset exceeds IPU limitation\n", ch); + if (u_offset%8) + dev_warn(ipu->dev, + "IDMAC%d's U offset is not 8-byte aligned\n", ch); + if (v_offset%8) + dev_warn(ipu->dev, + "IDMAC%d's V offset is not 8-byte aligned\n", ch); + + ipu_ch_param_set_field(¶ms, 0, 46, 22, u_offset / 8); + ipu_ch_param_set_field(¶ms, 0, 68, 22, v_offset / 8); + + dev_dbg(ipu->dev, "initializing idma ch %d @ %p\n", ch, ipu_ch_param_addr(ipu, ch)); + memcpy(ipu_ch_param_addr(ipu, ch), ¶ms, sizeof(params)); + if (addr2) { + ipu_ch_param_set_field(¶ms, 1, 0, 29, addr2 >> 3); + ipu_ch_param_set_field(¶ms, 1, 29, 29, 0); + + sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch); + if (sub_ch <= 0) + return; + + dev_dbg(ipu->dev, "initializing idma ch %d @ %p sub cpmem\n", ch, + ipu_ch_param_addr(ipu, sub_ch)); + memcpy(ipu_ch_param_addr(ipu, sub_ch), ¶ms, sizeof(params)); + } +}; + +static inline void _ipu_ch_param_set_burst_size(struct ipu_soc *ipu, + uint32_t ch, + uint16_t burst_pixels) +{ + int32_t sub_ch = 0; + + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 78, 7, + burst_pixels - 1); + + sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch); + if (sub_ch <= 0) + return; + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 78, 7, + burst_pixels - 1); +}; + +static inline int _ipu_ch_param_get_burst_size(struct ipu_soc *ipu, uint32_t ch) +{ + return ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 78, 7) + 1; +}; + +static inline int _ipu_ch_param_get_bpp(struct ipu_soc *ipu, uint32_t ch) +{ + return ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 107, 3); +}; + +static inline void _ipu_ch_param_set_buffer(struct ipu_soc *ipu, uint32_t ch, + int bufNum, dma_addr_t phyaddr) +{ + if (bufNum == 2) { + ch = __ipu_ch_get_third_buf_cpmem_num(ch); + if (ch <= 0) + return; + bufNum = 0; + } + + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 29 * bufNum, 29, + phyaddr / 8); +}; + +static inline void _ipu_ch_param_set_rotation(struct ipu_soc *ipu, uint32_t ch, + ipu_rotate_mode_t rot) +{ + u32 temp_rot = bitrev8(rot) >> 5; + int32_t sub_ch = 0; + + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 0, 119, 3, temp_rot); + + sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch); + if (sub_ch <= 0) + return; + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 119, 3, temp_rot); +}; + +static inline void _ipu_ch_param_set_block_mode(struct ipu_soc *ipu, uint32_t ch) +{ + int32_t sub_ch = 0; + + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 0, 117, 2, 1); + + sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch); + if (sub_ch <= 0) + return; + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 117, 2, 1); +}; + +static inline void _ipu_ch_param_set_alpha_use_separate_channel(struct ipu_soc *ipu, + uint32_t ch, + bool option) +{ + int32_t sub_ch = 0; + + if (option) { + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 89, 1, 1); + } else { + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 89, 1, 0); + } + + sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch); + if (sub_ch <= 0) + return; + + if (option) { + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 89, 1, 1); + } else { + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 89, 1, 0); + } +}; + +static inline void _ipu_ch_param_set_alpha_condition_read(struct ipu_soc *ipu, uint32_t ch) +{ + int32_t sub_ch = 0; + + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 149, 1, 1); + + sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch); + if (sub_ch <= 0) + return; + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 149, 1, 1); +}; + +static inline void _ipu_ch_param_set_alpha_buffer_memory(struct ipu_soc *ipu, uint32_t ch) +{ + int alp_mem_idx; + int32_t sub_ch = 0; + + switch (ch) { + case 14: /* PRP graphic */ + alp_mem_idx = 0; + break; + case 15: /* PP graphic */ + alp_mem_idx = 1; + break; + case 23: /* DP BG SYNC graphic */ + alp_mem_idx = 4; + break; + case 27: /* DP FG SYNC graphic */ + alp_mem_idx = 2; + break; + default: + dev_err(ipu->dev, "unsupported correlative channel of local " + "alpha channel\n"); + return; + } + + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 90, 3, alp_mem_idx); + + sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch); + if (sub_ch <= 0) + return; + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 90, 3, alp_mem_idx); +}; + +static inline void _ipu_ch_param_set_interlaced_scan(struct ipu_soc *ipu, uint32_t ch) +{ + u32 stride; + int32_t sub_ch = 0; + + sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch); + + ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, ch), 0, 113, 1, 1); + if (sub_ch > 0) + ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 113, 1, 1); + stride = ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 102, 14) + 1; + /* ILO is 20-bit and 8-byte aligned */ + if (stride/8 > 0xfffff) + dev_warn(ipu->dev, + "IDMAC%d's ILO exceeds IPU limitation\n", ch); + if (stride%8) + dev_warn(ipu->dev, + "IDMAC%d's ILO is not 8-byte aligned\n", ch); + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 58, 20, stride / 8); + if (sub_ch > 0) + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 58, 20, + stride / 8); + stride *= 2; + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 102, 14, stride - 1); + if (sub_ch > 0) + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 102, 14, + stride - 1); +}; + +static inline void _ipu_ch_param_set_axi_id(struct ipu_soc *ipu, uint32_t ch, uint32_t id) +{ + int32_t sub_ch = 0; + + id %= 4; + + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 93, 2, id); + + sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch); + if (sub_ch <= 0) + return; + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 93, 2, id); +}; + +/* IDMAC U/V offset changing support */ +/* U and V input is not affected, */ +/* the update is done by new calculation according to */ +/* vertical_offset and horizontal_offset */ +static inline void _ipu_ch_offset_update(struct ipu_soc *ipu, + int ch, + uint32_t pixel_fmt, + uint32_t width, + uint32_t height, + uint32_t stride, + uint32_t u, + uint32_t v, + uint32_t uv_stride, + uint32_t vertical_offset, + uint32_t horizontal_offset) +{ + uint32_t u_offset = 0; + uint32_t v_offset = 0; + uint32_t u_fix = 0; + uint32_t v_fix = 0; + int32_t sub_ch = 0; + + switch (pixel_fmt) { + case IPU_PIX_FMT_GENERIC: + case IPU_PIX_FMT_GENERIC_32: + case IPU_PIX_FMT_RGB565: + case IPU_PIX_FMT_BGR24: + case IPU_PIX_FMT_RGB24: + case IPU_PIX_FMT_YUV444: + case IPU_PIX_FMT_BGRA32: + case IPU_PIX_FMT_BGR32: + case IPU_PIX_FMT_RGBA32: + case IPU_PIX_FMT_RGB32: + case IPU_PIX_FMT_ABGR32: + case IPU_PIX_FMT_UYVY: + case IPU_PIX_FMT_YUYV: + break; + + case IPU_PIX_FMT_YUV420P2: + case IPU_PIX_FMT_YUV420P: + if (uv_stride < stride / 2) + uv_stride = stride / 2; + + u_offset = stride * (height - vertical_offset - 1) + + (stride - horizontal_offset) + + (uv_stride * vertical_offset / 2) + + horizontal_offset / 2; + v_offset = u_offset + (uv_stride * height / 2); + u_fix = u ? (u + (uv_stride * vertical_offset / 2) + + (horizontal_offset / 2) - + (stride * vertical_offset) - (horizontal_offset)) : + u_offset; + v_fix = v ? (v + (uv_stride * vertical_offset / 2) + + (horizontal_offset / 2) - + (stride * vertical_offset) - (horizontal_offset)) : + v_offset; + + break; + case IPU_PIX_FMT_YVU420P: + if (uv_stride < stride / 2) + uv_stride = stride / 2; + + v_offset = stride * (height - vertical_offset - 1) + + (stride - horizontal_offset) + + (uv_stride * vertical_offset / 2) + + horizontal_offset / 2; + u_offset = v_offset + (uv_stride * height / 2); + u_fix = u ? (u + (uv_stride * vertical_offset / 2) + + (horizontal_offset / 2) - + (stride * vertical_offset) - (horizontal_offset)) : + u_offset; + v_fix = v ? (v + (uv_stride * vertical_offset / 2) + + (horizontal_offset / 2) - + (stride * vertical_offset) - (horizontal_offset)) : + v_offset; + + break; + case IPU_PIX_FMT_YVU422P: + if (uv_stride < stride / 2) + uv_stride = stride / 2; + + v_offset = stride * (height - vertical_offset - 1) + + (stride - horizontal_offset) + + (uv_stride * vertical_offset) + + horizontal_offset / 2; + u_offset = v_offset + uv_stride * height; + u_fix = u ? (u + (uv_stride * vertical_offset) + + horizontal_offset / 2 - + (stride * vertical_offset) - (horizontal_offset)) : + u_offset; + v_fix = v ? (v + (uv_stride * vertical_offset) + + horizontal_offset / 2 - + (stride * vertical_offset) - (horizontal_offset)) : + v_offset; + break; + case IPU_PIX_FMT_YUV422P: + if (uv_stride < stride / 2) + uv_stride = stride / 2; + + u_offset = stride * (height - vertical_offset - 1) + + (stride - horizontal_offset) + + (uv_stride * vertical_offset) + + horizontal_offset / 2; + v_offset = u_offset + uv_stride * height; + u_fix = u ? (u + (uv_stride * vertical_offset) + + horizontal_offset / 2 - + (stride * vertical_offset) - (horizontal_offset)) : + u_offset; + v_fix = v ? (v + (uv_stride * vertical_offset) + + horizontal_offset / 2 - + (stride * vertical_offset) - (horizontal_offset)) : + v_offset; + break; + + case IPU_PIX_FMT_NV12: + uv_stride = stride; + u_offset = stride * (height - vertical_offset - 1) + + (stride - horizontal_offset) + + (uv_stride * vertical_offset / 2) + + horizontal_offset; + u_fix = u ? (u + (uv_stride * vertical_offset / 2) + + horizontal_offset - + (stride * vertical_offset) - (horizontal_offset)) : + u_offset; + + break; + default: + dev_err(ipu->dev, "mxc ipu: unimplemented pixel format\n"); + break; + } + + + + if (u_fix > u_offset) + u_offset = u_fix; + + if (v_fix > v_offset) + v_offset = v_fix; + + /* UBO and VBO are 22-bit and 8-byte aligned */ + if (u_offset/8 > 0x3fffff) + dev_warn(ipu->dev, + "IDMAC%d's U offset exceeds IPU limitation\n", ch); + if (v_offset/8 > 0x3fffff) + dev_warn(ipu->dev, + "IDMAC%d's V offset exceeds IPU limitation\n", ch); + if (u_offset%8) + dev_warn(ipu->dev, + "IDMAC%d's U offset is not 8-byte aligned\n", ch); + if (v_offset%8) + dev_warn(ipu->dev, + "IDMAC%d's V offset is not 8-byte aligned\n", ch); + + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 0, 46, 22, u_offset / 8); + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 0, 68, 22, v_offset / 8); + + sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch); + if (sub_ch <= 0) + return; + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 46, 22, u_offset / 8); + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 68, 22, v_offset / 8); +}; + +static inline void _ipu_ch_params_set_alpha_width(struct ipu_soc *ipu, uint32_t ch, int alpha_width) +{ + int32_t sub_ch = 0; + + ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, ch), 1, 125, 3, alpha_width - 1); + + sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch); + if (sub_ch <= 0) + return; + ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 125, 3, alpha_width - 1); +}; + +#endif diff --git a/drivers/mxc/ipu3/ipu_prv.h b/drivers/mxc/ipu3/ipu_prv.h new file mode 100644 index 00000000000..0114cc167ee --- /dev/null +++ b/drivers/mxc/ipu3/ipu_prv.h @@ -0,0 +1,332 @@ +/* + * 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_IPU_PRV_H__ +#define __INCLUDE_IPU_PRV_H__ + +#include <linux/types.h> +#include <linux/device.h> +#include <linux/clkdev.h> +#include <linux/interrupt.h> +#include <linux/fsl_devices.h> +#include <mach/clock.h> +#include <mach/ipu-v3.h> + +#ifdef CONFIG_MXC_IPU_V3H +#define MXC_IPU_MAX_NUM 2 +#else +#define MXC_IPU_MAX_NUM 1 +#endif + +/* Globals */ +extern int dmfc_type_setup; +extern struct clk ipu_pixel_clk[]; +extern struct clk_lookup ipu_lookups[]; + +#define IDMA_CHAN_INVALID 0xFF +#define HIGH_RESOLUTION_WIDTH 1024 + +struct ipu_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 */ +}; + +enum csc_type_t { + RGB2YUV = 0, + YUV2RGB, + RGB2RGB, + YUV2YUV, + CSC_NONE, + CSC_NUM +}; + +struct ipu_soc { + /*clk*/ + struct clk *ipu_clk; + struct clk *di_clk[2]; + struct clk *csi_clk[2]; + struct clk pixel_clk[2]; + + /*irq*/ + int irq_sync; + int irq_err; + struct ipu_irq_node irq_list[IPU_IRQ_COUNT]; + + /*reg*/ + u32 *cm_reg; + u32 *idmac_reg; + u32 *dp_reg; + u32 *ic_reg; + u32 *dc_reg; + u32 *dc_tmpl_reg; + u32 *dmfc_reg; + u32 *di_reg[2]; + u32 *smfc_reg; + u32 *csi_reg[2]; + u32 *cpmem_base; + u32 *tpmem_base; + u32 *disp_base[2]; + u32 *vdi_reg; + + struct device *dev; + + ipu_channel_t csi_channel[2]; + ipu_channel_t using_ic_dirct_ch; + unsigned char dc_di_assignment[10]; + bool sec_chan_en[24]; + bool thrd_chan_en[24]; + bool chan_is_interlaced[52]; + uint32_t channel_init_mask; + uint32_t channel_enable_mask; + + /*use count*/ + int ipu_use_count; + int dc_use_count; + int dp_use_count; + int dmfc_use_count; + int smfc_use_count; + int ic_use_count; + int rot_use_count; + int vdi_use_count; + int di_use_count[2]; + int csi_use_count[2]; + + struct mutex mutex_lock; + spinlock_t spin_lock; + + int dmfc_size_28; + int dmfc_size_29; + int dmfc_size_24; + int dmfc_size_27; + int dmfc_size_23; + + enum csc_type_t fg_csc_type; + enum csc_type_t bg_csc_type; + bool color_key_4rgb; + bool dc_swap; + struct completion dc_comp; + + /* for power gating */ + u32 ipu_conf_reg; + u32 ic_conf_reg; + u32 cha_db_mode_reg[4]; + u32 cha_trb_mode_reg[2]; + u32 idma_sub_addr_reg[5]; + u32 idma_enable_reg[2]; + u32 buf_ready_reg[10]; + + /*ipu processing driver*/ + struct list_head task_list[2]; + struct mutex task_lock[2]; + wait_queue_head_t waitq[2]; + struct task_struct *thread[2]; +}; + +struct ipu_channel { + u8 video_in_dma; + u8 alpha_in_dma; + u8 graph_in_dma; + u8 out_dma; +}; + +enum ipu_dmfc_type { + DMFC_NORMAL = 0, + DMFC_HIGH_RESOLUTION_DC, + DMFC_HIGH_RESOLUTION_DP, + DMFC_HIGH_RESOLUTION_ONLY_DP, +}; + +static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset) +{ + return readl(ipu->cm_reg + offset); +} + +static inline void ipu_cm_write(struct ipu_soc *ipu, + u32 value, unsigned offset) +{ + writel(value, ipu->cm_reg + offset); +} + +static inline u32 ipu_idmac_read(struct ipu_soc *ipu, unsigned offset) +{ + return readl(ipu->idmac_reg + offset); +} + +static inline void ipu_idmac_write(struct ipu_soc *ipu, + u32 value, unsigned offset) +{ + writel(value, ipu->idmac_reg + offset); +} + +static inline u32 ipu_dc_read(struct ipu_soc *ipu, unsigned offset) +{ + return readl(ipu->dc_reg + offset); +} + +static inline void ipu_dc_write(struct ipu_soc *ipu, + u32 value, unsigned offset) +{ + writel(value, ipu->dc_reg + offset); +} + +static inline u32 ipu_dc_tmpl_read(struct ipu_soc *ipu, unsigned offset) +{ + return readl(ipu->dc_tmpl_reg + offset); +} + +static inline void ipu_dc_tmpl_write(struct ipu_soc *ipu, + u32 value, unsigned offset) +{ + writel(value, ipu->dc_tmpl_reg + offset); +} + +static inline u32 ipu_dmfc_read(struct ipu_soc *ipu, unsigned offset) +{ + return readl(ipu->dmfc_reg + offset); +} + +static inline void ipu_dmfc_write(struct ipu_soc *ipu, + u32 value, unsigned offset) +{ + writel(value, ipu->dmfc_reg + offset); +} + +static inline u32 ipu_dp_read(struct ipu_soc *ipu, unsigned offset) +{ + return readl(ipu->dp_reg + offset); +} + +static inline void ipu_dp_write(struct ipu_soc *ipu, + u32 value, unsigned offset) +{ + writel(value, ipu->dp_reg + offset); +} + +static inline u32 ipu_di_read(struct ipu_soc *ipu, int di, unsigned offset) +{ + return readl(ipu->di_reg[di] + offset); +} + +static inline void ipu_di_write(struct ipu_soc *ipu, int di, + u32 value, unsigned offset) +{ + writel(value, ipu->di_reg[di] + offset); +} + +static inline u32 ipu_csi_read(struct ipu_soc *ipu, int csi, unsigned offset) +{ + return readl(ipu->csi_reg[csi] + offset); +} + +static inline void ipu_csi_write(struct ipu_soc *ipu, int csi, + u32 value, unsigned offset) +{ + writel(value, ipu->csi_reg[csi] + offset); +} + +static inline u32 ipu_smfc_read(struct ipu_soc *ipu, unsigned offset) +{ + return readl(ipu->smfc_reg + offset); +} + +static inline void ipu_smfc_write(struct ipu_soc *ipu, + u32 value, unsigned offset) +{ + writel(value, ipu->smfc_reg + offset); +} + +static inline u32 ipu_vdi_read(struct ipu_soc *ipu, unsigned offset) +{ + return readl(ipu->vdi_reg + offset); +} + +static inline void ipu_vdi_write(struct ipu_soc *ipu, + u32 value, unsigned offset) +{ + writel(value, ipu->vdi_reg + offset); +} + +static inline u32 ipu_ic_read(struct ipu_soc *ipu, unsigned offset) +{ + return readl(ipu->ic_reg + offset); +} + +static inline void ipu_ic_write(struct ipu_soc *ipu, + u32 value, unsigned offset) +{ + writel(value, ipu->ic_reg + offset); +} + +int register_ipu_device(struct ipu_soc *ipu, int id); +void unregister_ipu_device(struct ipu_soc *ipu, int id); +ipu_color_space_t format_to_colorspace(uint32_t fmt); +bool ipu_pixel_format_has_alpha(uint32_t fmt); + +void ipu_dump_registers(struct ipu_soc *ipu); + +uint32_t _ipu_channel_status(struct ipu_soc *ipu, ipu_channel_t channel); + +void ipu_disp_init(struct ipu_soc *ipu); +void _ipu_init_dc_mappings(struct ipu_soc *ipu); +int _ipu_dp_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t in_pixel_fmt, + uint32_t out_pixel_fmt); +void _ipu_dp_uninit(struct ipu_soc *ipu, ipu_channel_t channel); +void _ipu_dc_init(struct ipu_soc *ipu, int dc_chan, int di, bool interlaced, uint32_t pixel_fmt); +void _ipu_dc_uninit(struct ipu_soc *ipu, int dc_chan); +void _ipu_dp_dc_enable(struct ipu_soc *ipu, ipu_channel_t channel); +void _ipu_dp_dc_disable(struct ipu_soc *ipu, ipu_channel_t channel, bool swap); +void _ipu_dmfc_init(struct ipu_soc *ipu, int dmfc_type, int first); +void _ipu_dmfc_set_wait4eot(struct ipu_soc *ipu, int dma_chan, int width); +void _ipu_dmfc_set_burst_size(struct ipu_soc *ipu, int dma_chan, int burst_size); +int _ipu_disp_chan_is_interlaced(struct ipu_soc *ipu, ipu_channel_t channel); + +void _ipu_ic_enable_task(struct ipu_soc *ipu, ipu_channel_t channel); +void _ipu_ic_disable_task(struct ipu_soc *ipu, ipu_channel_t channel); +void _ipu_ic_init_prpvf(struct ipu_soc *ipu, ipu_channel_params_t *params, bool src_is_csi); +void _ipu_vdi_init(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel_params_t *params); +void _ipu_vdi_uninit(struct ipu_soc *ipu); +void _ipu_ic_uninit_prpvf(struct ipu_soc *ipu); +void _ipu_ic_init_rotate_vf(struct ipu_soc *ipu, ipu_channel_params_t *params); +void _ipu_ic_uninit_rotate_vf(struct ipu_soc *ipu); +void _ipu_ic_init_csi(struct ipu_soc *ipu, ipu_channel_params_t *params); +void _ipu_ic_uninit_csi(struct ipu_soc *ipu); +void _ipu_ic_init_prpenc(struct ipu_soc *ipu, ipu_channel_params_t *params, bool src_is_csi); +void _ipu_ic_uninit_prpenc(struct ipu_soc *ipu); +void _ipu_ic_init_rotate_enc(struct ipu_soc *ipu, ipu_channel_params_t *params); +void _ipu_ic_uninit_rotate_enc(struct ipu_soc *ipu); +void _ipu_ic_init_pp(struct ipu_soc *ipu, ipu_channel_params_t *params); +void _ipu_ic_uninit_pp(struct ipu_soc *ipu); +void _ipu_ic_init_rotate_pp(struct ipu_soc *ipu, ipu_channel_params_t *params); +void _ipu_ic_uninit_rotate_pp(struct ipu_soc *ipu); +int _ipu_ic_idma_init(struct ipu_soc *ipu, int dma_chan, uint16_t width, uint16_t height, + int burst_size, ipu_rotate_mode_t rot); +void _ipu_vdi_toggle_top_field_man(struct ipu_soc *ipu); +int _ipu_csi_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t csi); +void ipu_csi_set_test_generator(struct ipu_soc *ipu, bool active, uint32_t r_value, + uint32_t g_value, uint32_t b_value, + uint32_t pix_clk, uint32_t csi); +void _ipu_csi_ccir_err_detection_enable(struct ipu_soc *ipu, uint32_t csi); +void _ipu_csi_ccir_err_detection_disable(struct ipu_soc *ipu, uint32_t csi); +void _ipu_smfc_init(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t mipi_id, uint32_t csi); +void _ipu_smfc_set_burst_size(struct ipu_soc *ipu, ipu_channel_t channel, uint32_t bs); +void _ipu_dp_set_csc_coefficients(struct ipu_soc *ipu, ipu_channel_t channel, int32_t param[][3]); +int32_t _ipu_disp_set_window_pos(struct ipu_soc *ipu, ipu_channel_t channel, + int16_t x_pos, int16_t y_pos); +int32_t _ipu_disp_get_window_pos(struct ipu_soc *ipu, ipu_channel_t channel, + int16_t *x_pos, int16_t *y_pos); +void _ipu_get(struct ipu_soc *ipu); +void _ipu_put(struct ipu_soc *ipu); +void _ipu_lock(struct ipu_soc *ipu, unsigned long *flags); +void _ipu_unlock(struct ipu_soc *ipu, unsigned long *flags); +#endif /* __INCLUDE_IPU_PRV_H__ */ diff --git a/drivers/mxc/ipu3/ipu_regs.h b/drivers/mxc/ipu3/ipu_regs.h new file mode 100644 index 00000000000..c63c9323113 --- /dev/null +++ b/drivers/mxc/ipu3/ipu_regs.h @@ -0,0 +1,697 @@ +/* + * 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 ipu_regs.h + * + * @brief IPU Register definitions + * + * @ingroup IPU + */ +#ifndef __IPU_REGS_INCLUDED__ +#define __IPU_REGS_INCLUDED__ + +/* + * hw_rev 2: IPUV3DEX + * hw_rev 3: IPUV3M + * hw_rev 4: IPUV3H + */ +extern int g_ipu_hw_rev; + +#define IPU_DISP0_BASE 0x00000000 +#define IPU_MCU_T_DEFAULT 8 +#define IPU_DISP1_BASE ({g_ipu_hw_rev < 4 ? \ + (IPU_MCU_T_DEFAULT << 25) : \ + (0x00000000); }) +#define IPUV3DEX_REG_BASE 0x1E000000 +#define IPUV3M_REG_BASE 0x06000000 +#define IPUV3H_REG_BASE 0x00200000 + +#define IPU_CM_REG_BASE 0x00000000 +#define IPU_IDMAC_REG_BASE 0x00008000 +#define IPU_ISP_REG_BASE 0x00010000 +#define IPU_DP_REG_BASE 0x00018000 +#define IPU_IC_REG_BASE 0x00020000 +#define IPU_IRT_REG_BASE 0x00028000 +#define IPU_CSI0_REG_BASE 0x00030000 +#define IPU_CSI1_REG_BASE 0x00038000 +#define IPU_DI0_REG_BASE 0x00040000 +#define IPU_DI1_REG_BASE 0x00048000 +#define IPU_SMFC_REG_BASE 0x00050000 +#define IPU_DC_REG_BASE 0x00058000 +#define IPU_DMFC_REG_BASE 0x00060000 +#define IPU_VDI_REG_BASE 0x00068000 +#define IPU_CPMEM_REG_BASE ({g_ipu_hw_rev >= 4 ? \ + (0x00100000) : \ + (0x01000000); }) +#define IPU_LUT_REG_BASE 0x01020000 +#define IPU_SRM_REG_BASE ({g_ipu_hw_rev >= 4 ? \ + (0x00140000) : \ + (0x01040000); }) +#define IPU_TPM_REG_BASE ({g_ipu_hw_rev >= 4 ? \ + (0x00160000) : \ + (0x01060000); }) +#define IPU_DC_TMPL_REG_BASE ({g_ipu_hw_rev >= 4 ? \ + (0x00180000) : \ + (0x01080000); }) +#define IPU_ISP_TBPR_REG_BASE 0x010C0000 + +/* Register addresses */ +/* IPU Common registers */ +#define IPU_CONF (0) + +#define IPU_SRM_PRI1 (0x00A0/4) +#define IPU_SRM_PRI2 (0x00A4/4) +#define IPU_FS_PROC_FLOW1 (0x00A8/4) +#define IPU_FS_PROC_FLOW2 (0x00AC/4) +#define IPU_FS_PROC_FLOW3 (0x00B0/4) +#define IPU_FS_DISP_FLOW1 (0x00B4/4) +#define IPU_FS_DISP_FLOW2 (0x00B8/4) +#define IPU_SKIP (0x00BC/4) +#define IPU_DISP_ALT_CONF (0x00C0/4) +#define IPU_DISP_GEN (0x00C4/4) +#define IPU_DISP_ALT1 (0x00C8/4) +#define IPU_DISP_ALT2 (0x00CC/4) +#define IPU_DISP_ALT3 (0x00D0/4) +#define IPU_DISP_ALT4 (0x00D4/4) +#define IPU_SNOOP (0x00D8/4) +#define IPU_MEM_RST (0x00DC/4) +#define IPU_PM (0x00E0/4) +#define IPU_GPR (0x00E4/4) +#define IPU_CHA_DB_MODE_SEL(ch) (0x0150/4 + (ch / 32)) +#define IPU_ALT_CHA_DB_MODE_SEL(ch) (0x0168/4 + (ch / 32)) +/* + * IPUv3D doesn't support triple buffer, so point + * IPU_CHA_TRB_MODE_SEL, IPU_CHA_TRIPLE_CUR_BUF and + * IPU_CHA_BUF2_RDY to readonly + * IPU_ALT_CUR_BUF0 for IPUv3D. + */ +#define IPU_CHA_TRB_MODE_SEL(ch) ({g_ipu_hw_rev >= 2 ? \ + (0x0178/4 + (ch / 32)) : \ + (0x012C/4); }) +#define IPU_CHA_TRIPLE_CUR_BUF(ch) ({g_ipu_hw_rev >= 2 ? \ + (0x0258/4 + ((ch*2) / 32)) : \ + (0x012C/4); }) +#define IPU_CHA_BUF2_RDY(ch) ({g_ipu_hw_rev >= 2 ? \ + (0x0288/4 + (ch / 32)) : \ + (0x012C/4); }) +#define IPU_CHA_CUR_BUF(ch) ({g_ipu_hw_rev >= 2 ? \ + (0x023C/4 + (ch / 32)) : \ + (0x0124/4 + (ch / 32)); }) +#define IPU_ALT_CUR_BUF0 ({g_ipu_hw_rev >= 2 ? \ + (0x0244/4) : \ + (0x012C/4); }) +#define IPU_ALT_CUR_BUF1 ({g_ipu_hw_rev >= 2 ? \ + (0x0248/4) : \ + (0x0130/4); }) +#define IPU_SRM_STAT ({g_ipu_hw_rev >= 2 ? \ + (0x024C/4) : \ + (0x0134/4); }) +#define IPU_PROC_TASK_STAT ({g_ipu_hw_rev >= 2 ? \ + (0x0250/4) : \ + (0x0138/4); }) +#define IPU_DISP_TASK_STAT ({g_ipu_hw_rev >= 2 ? \ + (0x0254/4) : \ + (0x013C/4); }) +#define IPU_CHA_BUF0_RDY(ch) ({g_ipu_hw_rev >= 2 ? \ + (0x0268/4 + (ch / 32)) : \ + (0x0140/4 + (ch / 32)); }) +#define IPU_CHA_BUF1_RDY(ch) ({g_ipu_hw_rev >= 2 ? \ + (0x0270/4 + (ch / 32)) : \ + (0x0148/4 + (ch / 32)); }) +#define IPU_ALT_CHA_BUF0_RDY(ch) ({g_ipu_hw_rev >= 2 ? \ + (0x0278/4 + (ch / 32)) : \ + (0x0158/4 + (ch / 32)); }) +#define IPU_ALT_CHA_BUF1_RDY(ch) ({g_ipu_hw_rev >= 2 ? \ + (0x0280/4 + (ch / 32)) : \ + (0x0160/4 + (ch / 32)); }) + +#define IPU_INT_CTRL(n) (0x003C/4 + ((n) - 1)) +#define IPU_INT_CTRL_IRQ(irq) IPU_INT_CTRL(((irq) / 32)) +#define IPU_INT_STAT_IRQ(irq) IPU_INT_STAT(((irq) / 32)) +#define IPU_INT_STAT(n) ({g_ipu_hw_rev >= 2 ? \ + (0x0200/4 + ((n) - 1)) : \ + (0x00E8/4 + ((n) - 1)); }) + +#define IPUIRQ_2_STATREG(irq) (IPU_INT_STAT(1) + ((irq) / 32)) +#define IPUIRQ_2_CTRLREG(irq) (IPU_INT_CTRL(1) + ((irq) / 32)) +#define IPUIRQ_2_MASK(irq) (1UL << ((irq) & 0x1F)) + +#define VDI_FSIZE (0) +#define VDI_C (0x0004/4) + +/* CMOS Sensor Interface Registers */ +#define CSI_SENS_CONF (0) +#define CSI_SENS_FRM_SIZE (0x0004/4) +#define CSI_ACT_FRM_SIZE (0x0008/4) +#define CSI_OUT_FRM_CTRL (0x000C/4) +#define CSI_TST_CTRL (0x0010/4) +#define CSI_CCIR_CODE_1 (0x0014/4) +#define CSI_CCIR_CODE_2 (0x0018/4) +#define CSI_CCIR_CODE_3 (0x001C/4) +#define CSI_MIPI_DI (0x0020/4) +#define CSI_SKIP (0x0024/4) +#define CSI_CPD_CTRL (0x0028/4) +#define CSI_CPD_RC(n) (0x002C/4 + n) +#define CSI_CPD_RS(n) (0x004C/4 + n) +#define CSI_CPD_GRC(n) (0x005C/4 + n) +#define CSI_CPD_GRS(n) (0x007C/4 + n) +#define CSI_CPD_GBC(n) (0x008C/4 + n) +#define CSI_CPD_GBS(n) (0x00AC/4 + n) +#define CSI_CPD_BC(n) (0x00BC/4 + n) +#define CSI_CPD_BS(n) (0x00DC/4 + n) +#define CSI_CPD_OFFSET1 (0x00EC/4) +#define CSI_CPD_OFFSET2 (0x00F0/4) + +/*SMFC Registers */ +#define SMFC_MAP (0) +#define SMFC_WMC (0x0004/4) +#define SMFC_BS (0x0008/4) + +/* Image Converter Registers */ +#define IC_CONF 0 +#define IC_PRP_ENC_RSC (0x0004/4) +#define IC_PRP_VF_RSC (0x0008/4) +#define IC_PP_RSC (0x000C/4) +#define IC_CMBP_1 (0x0010/4) +#define IC_CMBP_2 (0x0014/4) +#define IC_IDMAC_1 (0x0018/4) +#define IC_IDMAC_2 (0x001C/4) +#define IC_IDMAC_3 (0x0020/4) +#define IC_IDMAC_4 (0x0024/4) + +#define IDMAC_CONF (0x0000) +#define IDMAC_CHA_EN(ch) (0x0004/4 + (ch/32)) +#define IDMAC_SEP_ALPHA (0x000C/4) +#define IDMAC_ALT_SEP_ALPHA (0x0010/4) +#define IDMAC_CHA_PRI(ch) (0x0014/4 + (ch/32)) +#define IDMAC_WM_EN(ch) (0x001C/4 + (ch/32)) +#define IDMAC_CH_LOCK_EN_1 ({g_ipu_hw_rev >= 2 ? \ + (0x0024/4) : 0; }) +#define IDMAC_CH_LOCK_EN_2 ({g_ipu_hw_rev >= 2 ? \ + (0x0028/4) : \ + (0x0024/4); }) +#define IDMAC_SUB_ADDR_0 ({g_ipu_hw_rev >= 2 ? \ + (0x002C/4) : \ + (0x0028/4); }) +#define IDMAC_SUB_ADDR_1 ({g_ipu_hw_rev >= 2 ? \ + (0x0030/4) : \ + (0x002C/4); }) +#define IDMAC_SUB_ADDR_2 ({g_ipu_hw_rev >= 2 ? \ + (0x0034/4) : \ + (0x0030/4); }) +/* + * IPUv3D doesn't support IDMAC_SUB_ADDR_3 and IDMAC_SUB_ADDR_4, + * so point them to readonly IDMAC_CHA_BUSY1 for IPUv3D. + */ +#define IDMAC_SUB_ADDR_3 ({g_ipu_hw_rev >= 2 ? \ + (0x0038/4) : \ + (0x0040/4); }) +#define IDMAC_SUB_ADDR_4 ({g_ipu_hw_rev >= 2 ? \ + (0x003c/4) : \ + (0x0040/4); }) +#define IDMAC_BAND_EN(ch) ({g_ipu_hw_rev >= 2 ? \ + (0x0040/4 + (ch/32)) : \ + (0x0034/4 + (ch/32)); }) +#define IDMAC_CHA_BUSY(ch) ({g_ipu_hw_rev >= 2 ? \ + (0x0100/4 + (ch/32)) : \ + (0x0040/4 + (ch/32)); }) + +#define DI_GENERAL (0) +#define DI_BS_CLKGEN0 (0x0004/4) +#define DI_BS_CLKGEN1 (0x0008/4) + +#define DI_SW_GEN0(gen) (0x000C/4 + (gen - 1)) +#define DI_SW_GEN1(gen) (0x0030/4 + (gen - 1)) +#define DI_STP_REP(gen) (0x0148/4 + (gen - 1)/2) +#define DI_SYNC_AS_GEN (0x0054/4) +#define DI_DW_GEN(gen) (0x0058/4 + gen) +#define DI_DW_SET(gen, set) (0x0088/4 + gen + 0xC*set) +#define DI_SER_CONF (0x015C/4) +#define DI_SSC (0x0160/4) +#define DI_POL (0x0164/4) +#define DI_AW0 (0x0168/4) +#define DI_AW1 (0x016C/4) +#define DI_SCR_CONF (0x0170/4) +#define DI_STAT (0x0174/4) + +#define DMFC_RD_CHAN (0) +#define DMFC_WR_CHAN (0x0004/4) +#define DMFC_WR_CHAN_DEF (0x0008/4) +#define DMFC_DP_CHAN (0x000C/4) +#define DMFC_DP_CHAN_DEF (0x0010/4) +#define DMFC_GENERAL1 (0x0014/4) +#define DMFC_GENERAL2 (0x0018/4) +#define DMFC_IC_CTRL (0x001C/4) +#define DMFC_STAT (0x0020/4) + +#define DC_MAP_CONF_PTR(n) (0x0108/4 + n/2) +#define DC_MAP_CONF_VAL(n) (0x0144/4 + n/2) + +#define _RL_CH_2_OFFSET(ch) ((ch == 0) ? 8 : ( \ + (ch == 1) ? 0x24 : ( \ + (ch == 2) ? 0x40 : ( \ + (ch == 5) ? 0x64 : ( \ + (ch == 6) ? 0x80 : ( \ + (ch == 8) ? 0x9C : ( \ + (ch == 9) ? 0xBC : (-1)))))))) +#define DC_RL_CH(ch, evt) (_RL_CH_2_OFFSET(ch)/4 + evt/2) + +#define DC_EVT_NF 0 +#define DC_EVT_NL 1 +#define DC_EVT_EOF 2 +#define DC_EVT_NFIELD 3 +#define DC_EVT_EOL 4 +#define DC_EVT_EOFIELD 5 +#define DC_EVT_NEW_ADDR 6 +#define DC_EVT_NEW_CHAN 7 +#define DC_EVT_NEW_DATA 8 + +#define DC_EVT_NEW_ADDR_W_0 0 +#define DC_EVT_NEW_ADDR_W_1 1 +#define DC_EVT_NEW_CHAN_W_0 2 +#define DC_EVT_NEW_CHAN_W_1 3 +#define DC_EVT_NEW_DATA_W_0 4 +#define DC_EVT_NEW_DATA_W_1 5 +#define DC_EVT_NEW_ADDR_R_0 6 +#define DC_EVT_NEW_ADDR_R_1 7 +#define DC_EVT_NEW_CHAN_R_0 8 +#define DC_EVT_NEW_CHAN_R_1 9 +#define DC_EVT_NEW_DATA_R_0 10 +#define DC_EVT_NEW_DATA_R_1 11 +#define DC_EVEN_UGDE0 12 +#define DC_ODD_UGDE0 13 +#define DC_EVEN_UGDE1 14 +#define DC_ODD_UGDE1 15 +#define DC_EVEN_UGDE2 16 +#define DC_ODD_UGDE2 17 +#define DC_EVEN_UGDE3 18 +#define DC_ODD_UGDE3 19 + +#define dc_ch_offset(ch) \ +({ \ + const u8 _offset[] = { \ + 0, 0x1C, 0x38, 0x54, 0x58, 0x5C, 0x78, 0, 0x94, 0xB4}; \ + _offset[ch]; \ +}) +#define DC_WR_CH_CONF(ch) (dc_ch_offset(ch)/4) +#define DC_WR_CH_ADDR(ch) (dc_ch_offset(ch)/4 + 4/4) + +#define DC_WR_CH_CONF_1 (0x001C/4) +#define DC_WR_CH_ADDR_1 (0x0020/4) +#define DC_WR_CH_CONF_5 (0x005C/4) +#define DC_WR_CH_ADDR_5 (0x0060/4) +#define DC_GEN (0x00D4/4) +#define DC_DISP_CONF1(disp) (0x00D8/4 + disp) +#define DC_DISP_CONF2(disp) (0x00E8/4 + disp) +#define DC_STAT (0x01C8/4) +#define DC_UGDE_0(evt) (0x0174/4 + evt*4) +#define DC_UGDE_1(evt) (0x0178/4 + evt*4) +#define DC_UGDE_2(evt) (0x017C/4 + evt*4) +#define DC_UGDE_3(evt) (0x0180/4 + evt*4) + +#define DP_SYNC 0 +#define DP_ASYNC0 0x60 +#define DP_ASYNC1 0xBC +#define DP_COM_CONF(flow) (flow/4) +#define DP_GRAPH_WIND_CTRL(flow) (0x0004/4 + flow/4) +#define DP_FG_POS(flow) (0x0008/4 + flow/4) +#define DP_GAMMA_C(flow, i) (0x0014/4 + flow/4 + i) +#define DP_GAMMA_S(flow, i) (0x0034/4 + flow/4 + i) +#define DP_CSC_A_0(flow) (0x0044/4 + flow/4) +#define DP_CSC_A_1(flow) (0x0048/4 + flow/4) +#define DP_CSC_A_2(flow) (0x004C/4 + flow/4) +#define DP_CSC_A_3(flow) (0x0050/4 + flow/4) +#define DP_CSC_0(flow) (0x0054/4 + flow/4) +#define DP_CSC_1(flow) (0x0058/4 + flow/4) + +enum { + IPU_CONF_CSI0_EN = 0x00000001, + IPU_CONF_CSI1_EN = 0x00000002, + IPU_CONF_IC_EN = 0x00000004, + IPU_CONF_ROT_EN = 0x00000008, + IPU_CONF_ISP_EN = 0x00000010, + IPU_CONF_DP_EN = 0x00000020, + IPU_CONF_DI0_EN = 0x00000040, + IPU_CONF_DI1_EN = 0x00000080, + IPU_CONF_DMFC_EN = 0x00000400, + IPU_CONF_SMFC_EN = 0x00000100, + IPU_CONF_DC_EN = 0x00000200, + IPU_CONF_VDI_EN = 0x00001000, + IPU_CONF_IDMAC_DIS = 0x00400000, + IPU_CONF_IC_DMFC_SEL = 0x02000000, + IPU_CONF_IC_DMFC_SYNC = 0x04000000, + IPU_CONF_VDI_DMFC_SYNC = 0x08000000, + IPU_CONF_CSI0_DATA_SOURCE = 0x10000000, + IPU_CONF_CSI0_DATA_SOURCE_OFFSET = 28, + IPU_CONF_CSI1_DATA_SOURCE = 0x20000000, + IPU_CONF_IC_INPUT = 0x40000000, + IPU_CONF_CSI_SEL = 0x80000000, + + DI0_COUNTER_RELEASE = 0x01000000, + DI1_COUNTER_RELEASE = 0x02000000, + + FS_PRPVF_ROT_SRC_SEL_MASK = 0x00000F00, + FS_PRPVF_ROT_SRC_SEL_OFFSET = 8, + FS_PRPENC_ROT_SRC_SEL_MASK = 0x0000000F, + FS_PRPENC_ROT_SRC_SEL_OFFSET = 0, + FS_PP_ROT_SRC_SEL_MASK = 0x000F0000, + FS_PP_ROT_SRC_SEL_OFFSET = 16, + FS_PP_SRC_SEL_MASK = 0x0000F000, + FS_PP_SRC_SEL_OFFSET = 12, + FS_PRP_SRC_SEL_MASK = 0x0F000000, + FS_PRP_SRC_SEL_OFFSET = 24, + FS_VF_IN_VALID = 0x80000000, + FS_ENC_IN_VALID = 0x40000000, + FS_VDI_SRC_SEL_MASK = 0x30000000, + FS_VDI_SRC_SEL_OFFSET = 28, + + + FS_PRPENC_DEST_SEL_MASK = 0x0000000F, + FS_PRPENC_DEST_SEL_OFFSET = 0, + FS_PRPVF_DEST_SEL_MASK = 0x000000F0, + FS_PRPVF_DEST_SEL_OFFSET = 4, + FS_PRPVF_ROT_DEST_SEL_MASK = 0x00000F00, + FS_PRPVF_ROT_DEST_SEL_OFFSET = 8, + FS_PP_DEST_SEL_MASK = 0x0000F000, + FS_PP_DEST_SEL_OFFSET = 12, + FS_PP_ROT_DEST_SEL_MASK = 0x000F0000, + FS_PP_ROT_DEST_SEL_OFFSET = 16, + FS_PRPENC_ROT_DEST_SEL_MASK = 0x00F00000, + FS_PRPENC_ROT_DEST_SEL_OFFSET = 20, + + FS_SMFC0_DEST_SEL_MASK = 0x0000000F, + FS_SMFC0_DEST_SEL_OFFSET = 0, + FS_SMFC1_DEST_SEL_MASK = 0x00000070, + FS_SMFC1_DEST_SEL_OFFSET = 4, + FS_SMFC2_DEST_SEL_MASK = 0x00000780, + FS_SMFC2_DEST_SEL_OFFSET = 7, + FS_SMFC3_DEST_SEL_MASK = 0x00003800, + FS_SMFC3_DEST_SEL_OFFSET = 11, + + FS_DC1_SRC_SEL_MASK = 0x00F00000, + FS_DC1_SRC_SEL_OFFSET = 20, + FS_DC2_SRC_SEL_MASK = 0x000F0000, + FS_DC2_SRC_SEL_OFFSET = 16, + FS_DP_SYNC0_SRC_SEL_MASK = 0x0000000F, + FS_DP_SYNC0_SRC_SEL_OFFSET = 0, + FS_DP_SYNC1_SRC_SEL_MASK = 0x000000F0, + FS_DP_SYNC1_SRC_SEL_OFFSET = 4, + FS_DP_ASYNC0_SRC_SEL_MASK = 0x00000F00, + FS_DP_ASYNC0_SRC_SEL_OFFSET = 8, + FS_DP_ASYNC1_SRC_SEL_MASK = 0x0000F000, + FS_DP_ASYNC1_SRC_SEL_OFFSET = 12, + + FS_AUTO_REF_PER_MASK = 0, + FS_AUTO_REF_PER_OFFSET = 16, + + TSTAT_VF_MASK = 0x0000000C, + TSTAT_VF_OFFSET = 2, + TSTAT_VF_ROT_MASK = 0x00000300, + TSTAT_VF_ROT_OFFSET = 8, + TSTAT_ENC_MASK = 0x00000003, + TSTAT_ENC_OFFSET = 0, + TSTAT_ENC_ROT_MASK = 0x000000C0, + TSTAT_ENC_ROT_OFFSET = 6, + TSTAT_PP_MASK = 0x00000030, + TSTAT_PP_OFFSET = 4, + TSTAT_PP_ROT_MASK = 0x00000C00, + TSTAT_PP_ROT_OFFSET = 10, + + TASK_STAT_IDLE = 0, + TASK_STAT_ACTIVE = 1, + TASK_STAT_WAIT4READY = 2, + + /* Image Converter Register bits */ + IC_CONF_PRPENC_EN = 0x00000001, + IC_CONF_PRPENC_CSC1 = 0x00000002, + IC_CONF_PRPENC_ROT_EN = 0x00000004, + IC_CONF_PRPVF_EN = 0x00000100, + IC_CONF_PRPVF_CSC1 = 0x00000200, + IC_CONF_PRPVF_CSC2 = 0x00000400, + IC_CONF_PRPVF_CMB = 0x00000800, + IC_CONF_PRPVF_ROT_EN = 0x00001000, + IC_CONF_PP_EN = 0x00010000, + IC_CONF_PP_CSC1 = 0x00020000, + IC_CONF_PP_CSC2 = 0x00040000, + IC_CONF_PP_CMB = 0x00080000, + IC_CONF_PP_ROT_EN = 0x00100000, + IC_CONF_IC_GLB_LOC_A = 0x10000000, + IC_CONF_KEY_COLOR_EN = 0x20000000, + IC_CONF_RWS_EN = 0x40000000, + IC_CONF_CSI_MEM_WR_EN = 0x80000000, + + IC_IDMAC_1_CB0_BURST_16 = 0x00000001, + IC_IDMAC_1_CB1_BURST_16 = 0x00000002, + IC_IDMAC_1_CB2_BURST_16 = 0x00000004, + IC_IDMAC_1_CB3_BURST_16 = 0x00000008, + IC_IDMAC_1_CB4_BURST_16 = 0x00000010, + IC_IDMAC_1_CB5_BURST_16 = 0x00000020, + IC_IDMAC_1_CB6_BURST_16 = 0x00000040, + IC_IDMAC_1_CB7_BURST_16 = 0x00000080, + IC_IDMAC_1_PRPENC_ROT_MASK = 0x00003800, + IC_IDMAC_1_PRPENC_ROT_OFFSET = 11, + IC_IDMAC_1_PRPVF_ROT_MASK = 0x0001C000, + IC_IDMAC_1_PRPVF_ROT_OFFSET = 14, + IC_IDMAC_1_PP_ROT_MASK = 0x000E0000, + IC_IDMAC_1_PP_ROT_OFFSET = 17, + IC_IDMAC_1_PP_FLIP_RS = 0x00400000, + IC_IDMAC_1_PRPVF_FLIP_RS = 0x00200000, + IC_IDMAC_1_PRPENC_FLIP_RS = 0x00100000, + + IC_IDMAC_2_PRPENC_HEIGHT_MASK = 0x000003FF, + IC_IDMAC_2_PRPENC_HEIGHT_OFFSET = 0, + IC_IDMAC_2_PRPVF_HEIGHT_MASK = 0x000FFC00, + IC_IDMAC_2_PRPVF_HEIGHT_OFFSET = 10, + IC_IDMAC_2_PP_HEIGHT_MASK = 0x3FF00000, + IC_IDMAC_2_PP_HEIGHT_OFFSET = 20, + + IC_IDMAC_3_PRPENC_WIDTH_MASK = 0x000003FF, + IC_IDMAC_3_PRPENC_WIDTH_OFFSET = 0, + IC_IDMAC_3_PRPVF_WIDTH_MASK = 0x000FFC00, + IC_IDMAC_3_PRPVF_WIDTH_OFFSET = 10, + IC_IDMAC_3_PP_WIDTH_MASK = 0x3FF00000, + IC_IDMAC_3_PP_WIDTH_OFFSET = 20, + + CSI_SENS_CONF_DATA_FMT_SHIFT = 8, + CSI_SENS_CONF_DATA_FMT_MASK = 0x00000700, + CSI_SENS_CONF_DATA_FMT_RGB_YUV444 = 0L, + CSI_SENS_CONF_DATA_FMT_YUV422_YUYV = 1L, + CSI_SENS_CONF_DATA_FMT_YUV422_UYVY = 2L, + CSI_SENS_CONF_DATA_FMT_BAYER = 3L, + CSI_SENS_CONF_DATA_FMT_RGB565 = 4L, + CSI_SENS_CONF_DATA_FMT_RGB555 = 5L, + CSI_SENS_CONF_DATA_FMT_RGB444 = 6L, + CSI_SENS_CONF_DATA_FMT_JPEG = 7L, + + CSI_SENS_CONF_VSYNC_POL_SHIFT = 0, + CSI_SENS_CONF_HSYNC_POL_SHIFT = 1, + CSI_SENS_CONF_DATA_POL_SHIFT = 2, + CSI_SENS_CONF_PIX_CLK_POL_SHIFT = 3, + CSI_SENS_CONF_SENS_PRTCL_MASK = 0x00000070L, + CSI_SENS_CONF_SENS_PRTCL_SHIFT = 4, + CSI_SENS_CONF_PACK_TIGHT_SHIFT = 7, + CSI_SENS_CONF_DATA_WIDTH_SHIFT = 11, + CSI_SENS_CONF_EXT_VSYNC_SHIFT = 15, + CSI_SENS_CONF_DIVRATIO_SHIFT = 16, + + CSI_SENS_CONF_DIVRATIO_MASK = 0x00FF0000L, + CSI_SENS_CONF_DATA_DEST_SHIFT = 24, + CSI_SENS_CONF_DATA_DEST_MASK = 0x07000000L, + CSI_SENS_CONF_JPEG8_EN_SHIFT = 27, + CSI_SENS_CONF_JPEG_EN_SHIFT = 28, + CSI_SENS_CONF_FORCE_EOF_SHIFT = 29, + CSI_SENS_CONF_DATA_EN_POL_SHIFT = 31, + + CSI_DATA_DEST_ISP = 1L, + CSI_DATA_DEST_IC = 2L, + CSI_DATA_DEST_IDMAC = 4L, + + CSI_CCIR_ERR_DET_EN = 0x01000000L, + CSI_HORI_DOWNSIZE_EN = 0x80000000L, + CSI_VERT_DOWNSIZE_EN = 0x40000000L, + CSI_TEST_GEN_MODE_EN = 0x01000000L, + + CSI_HSC_MASK = 0x1FFF0000, + CSI_HSC_SHIFT = 16, + CSI_VSC_MASK = 0x00000FFF, + CSI_VSC_SHIFT = 0, + + CSI_TEST_GEN_R_MASK = 0x000000FFL, + CSI_TEST_GEN_R_SHIFT = 0, + CSI_TEST_GEN_G_MASK = 0x0000FF00L, + CSI_TEST_GEN_G_SHIFT = 8, + CSI_TEST_GEN_B_MASK = 0x00FF0000L, + CSI_TEST_GEN_B_SHIFT = 16, + + CSI_MIPI_DI0_MASK = 0x000000FFL, + CSI_MIPI_DI0_SHIFT = 0, + CSI_MIPI_DI1_MASK = 0x0000FF00L, + CSI_MIPI_DI1_SHIFT = 8, + CSI_MIPI_DI2_MASK = 0x00FF0000L, + CSI_MIPI_DI2_SHIFT = 16, + CSI_MIPI_DI3_MASK = 0xFF000000L, + CSI_MIPI_DI3_SHIFT = 24, + + CSI_MAX_RATIO_SKIP_ISP_MASK = 0x00070000L, + CSI_MAX_RATIO_SKIP_ISP_SHIFT = 16, + CSI_SKIP_ISP_MASK = 0x00F80000L, + CSI_SKIP_ISP_SHIFT = 19, + CSI_MAX_RATIO_SKIP_SMFC_MASK = 0x00000007L, + CSI_MAX_RATIO_SKIP_SMFC_SHIFT = 0, + CSI_SKIP_SMFC_MASK = 0x000000F8L, + CSI_SKIP_SMFC_SHIFT = 3, + CSI_ID_2_SKIP_MASK = 0x00000300L, + CSI_ID_2_SKIP_SHIFT = 8, + + CSI_COLOR_FIRST_ROW_MASK = 0x00000002L, + CSI_COLOR_FIRST_COMP_MASK = 0x00000001L, + + SMFC_MAP_CH0_MASK = 0x00000007L, + SMFC_MAP_CH0_SHIFT = 0, + SMFC_MAP_CH1_MASK = 0x00000038L, + SMFC_MAP_CH1_SHIFT = 3, + SMFC_MAP_CH2_MASK = 0x000001C0L, + SMFC_MAP_CH2_SHIFT = 6, + SMFC_MAP_CH3_MASK = 0x00000E00L, + SMFC_MAP_CH3_SHIFT = 9, + + SMFC_WM0_SET_MASK = 0x00000007L, + SMFC_WM0_SET_SHIFT = 0, + SMFC_WM1_SET_MASK = 0x000001C0L, + SMFC_WM1_SET_SHIFT = 6, + SMFC_WM2_SET_MASK = 0x00070000L, + SMFC_WM2_SET_SHIFT = 16, + SMFC_WM3_SET_MASK = 0x01C00000L, + SMFC_WM3_SET_SHIFT = 22, + + SMFC_WM0_CLR_MASK = 0x00000038L, + SMFC_WM0_CLR_SHIFT = 3, + SMFC_WM1_CLR_MASK = 0x00000E00L, + SMFC_WM1_CLR_SHIFT = 9, + SMFC_WM2_CLR_MASK = 0x00380000L, + SMFC_WM2_CLR_SHIFT = 19, + SMFC_WM3_CLR_MASK = 0x0E000000L, + SMFC_WM3_CLR_SHIFT = 25, + + SMFC_BS0_MASK = 0x0000000FL, + SMFC_BS0_SHIFT = 0, + SMFC_BS1_MASK = 0x000000F0L, + SMFC_BS1_SHIFT = 4, + SMFC_BS2_MASK = 0x00000F00L, + SMFC_BS2_SHIFT = 8, + SMFC_BS3_MASK = 0x0000F000L, + SMFC_BS3_SHIFT = 12, + + PF_CONF_TYPE_MASK = 0x00000007, + PF_CONF_TYPE_SHIFT = 0, + PF_CONF_PAUSE_EN = 0x00000010, + PF_CONF_RESET = 0x00008000, + PF_CONF_PAUSE_ROW_MASK = 0x00FF0000, + PF_CONF_PAUSE_ROW_SHIFT = 16, + + DI_DW_GEN_ACCESS_SIZE_OFFSET = 24, + DI_DW_GEN_COMPONENT_SIZE_OFFSET = 16, + + DI_GEN_DI_CLK_EXT = 0x100000, + DI_GEN_POLARITY_DISP_CLK = 0x00020000, + DI_GEN_POLARITY_1 = 0x00000001, + DI_GEN_POLARITY_2 = 0x00000002, + DI_GEN_POLARITY_3 = 0x00000004, + DI_GEN_POLARITY_4 = 0x00000008, + DI_GEN_POLARITY_5 = 0x00000010, + DI_GEN_POLARITY_6 = 0x00000020, + DI_GEN_POLARITY_7 = 0x00000040, + DI_GEN_POLARITY_8 = 0x00000080, + + DI_POL_DRDY_DATA_POLARITY = 0x00000080, + DI_POL_DRDY_POLARITY_15 = 0x00000010, + + DI_VSYNC_SEL_OFFSET = 13, + + DC_WR_CH_CONF_FIELD_MODE = 0x00000200, + DC_WR_CH_CONF_PROG_TYPE_OFFSET = 5, + DC_WR_CH_CONF_PROG_TYPE_MASK = 0x000000E0, + DC_WR_CH_CONF_PROG_DI_ID = 0x00000004, + DC_WR_CH_CONF_PROG_DISP_ID_OFFSET = 3, + DC_WR_CH_CONF_PROG_DISP_ID_MASK = 0x00000018, + + DC_UGDE_0_ODD_EN = 0x02000000, + DC_UGDE_0_ID_CODED_MASK = 0x00000007, + DC_UGDE_0_ID_CODED_OFFSET = 0, + DC_UGDE_0_EV_PRIORITY_MASK = 0x00000078, + DC_UGDE_0_EV_PRIORITY_OFFSET = 3, + + DP_COM_CONF_FG_EN = 0x00000001, + DP_COM_CONF_GWSEL = 0x00000002, + DP_COM_CONF_GWAM = 0x00000004, + DP_COM_CONF_GWCKE = 0x00000008, + DP_COM_CONF_CSC_DEF_MASK = 0x00000300, + DP_COM_CONF_CSC_DEF_OFFSET = 8, + DP_COM_CONF_CSC_DEF_FG = 0x00000300, + DP_COM_CONF_CSC_DEF_BG = 0x00000200, + DP_COM_CONF_CSC_DEF_BOTH = 0x00000100, + DP_COM_CONF_GAMMA_EN = 0x00001000, + DP_COM_CONF_GAMMA_YUV_EN = 0x00002000, + + DI_SER_CONF_LLA_SER_ACCESS = 0x00000020, + DI_SER_CONF_SERIAL_CLK_POL = 0x00000010, + DI_SER_CONF_SERIAL_DATA_POL = 0x00000008, + DI_SER_CONF_SERIAL_RS_POL = 0x00000004, + DI_SER_CONF_SERIAL_CS_POL = 0x00000002, + DI_SER_CONF_WAIT4SERIAL = 0x00000001, + + VDI_C_CH_420 = 0x00000000, + VDI_C_CH_422 = 0x00000002, + VDI_C_MOT_SEL_FULL = 0x00000008, + VDI_C_MOT_SEL_LOW = 0x00000004, + VDI_C_MOT_SEL_MED = 0x00000000, + VDI_C_BURST_SIZE1_4 = 0x00000030, + VDI_C_BURST_SIZE2_4 = 0x00000300, + VDI_C_BURST_SIZE3_4 = 0x00003000, + VDI_C_VWM1_SET_1 = 0x00000000, + VDI_C_VWM1_CLR_2 = 0x00080000, + VDI_C_VWM3_SET_1 = 0x00000000, + VDI_C_VWM3_CLR_2 = 0x02000000, + VDI_C_TOP_FIELD_MAN_1 = 0x40000000, + VDI_C_TOP_FIELD_AUTO_1 = 0x80000000, +}; + +enum di_pins { + DI_PIN11 = 0, + DI_PIN12 = 1, + DI_PIN13 = 2, + DI_PIN14 = 3, + DI_PIN15 = 4, + DI_PIN16 = 5, + DI_PIN17 = 6, + DI_PIN_CS = 7, + + DI_PIN_SER_CLK = 0, + DI_PIN_SER_RS = 1, +}; + +enum di_sync_wave { + DI_SYNC_NONE = -1, + DI_SYNC_CLK = 0, + DI_SYNC_INT_HSYNC = 1, + DI_SYNC_HSYNC = 2, + DI_SYNC_VSYNC = 3, + DI_SYNC_DE = 5, +}; + +/* DC template opcodes */ +#define WROD(lf) (0x18 | (lf << 1)) +#define WRG (0x01) + +#endif diff --git a/include/linux/ipu.h b/include/linux/ipu.h new file mode 100644 index 00000000000..d6c1588b82c --- /dev/null +++ b/include/linux/ipu.h @@ -0,0 +1,248 @@ +/* + * Copyright 2005-2011 Freescale Semiconductor, Inc. + */ + +/* + * 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 + */ + +/*! + * @defgroup IPU MXC Image Processing Unit (IPU) Driver + */ +/*! + * @file arch-mxc/ipu.h + * + * @brief This file contains the IPU driver API declarations. + * + * @ingroup IPU + */ + +#ifndef __ASM_ARCH_IPU_H__ +#define __ASM_ARCH_IPU_H__ + +#include <linux/types.h> +#include <linux/videodev2.h> +#ifdef __KERNEL__ +#include <linux/interrupt.h> +#else +#ifndef __cplusplus +typedef unsigned char bool; +#endif +#define irqreturn_t int +#define dma_addr_t int +#define uint32_t unsigned int +#define uint16_t unsigned short +#define uint8_t unsigned char +#define u32 unsigned int +#define u8 unsigned char +#define __u32 u32 +#endif + +/*! + * Enumeration of IPU rotation modes + */ +typedef enum { + /* Note the enum values correspond to BAM value */ + IPU_ROTATE_NONE = 0, + IPU_ROTATE_VERT_FLIP = 1, + IPU_ROTATE_HORIZ_FLIP = 2, + IPU_ROTATE_180 = 3, + IPU_ROTATE_90_RIGHT = 4, + IPU_ROTATE_90_RIGHT_VFLIP = 5, + IPU_ROTATE_90_RIGHT_HFLIP = 6, + IPU_ROTATE_90_LEFT = 7, +} ipu_rotate_mode_t; + +/*! + * Enumeration of VDI MOTION select + */ +typedef enum { + MED_MOTION = 0, + LOW_MOTION = 1, + HIGH_MOTION = 2, +} ipu_motion_sel; + +/*! + * Enumeration of DI ports for ADC. + */ +typedef enum { + DISP0, + DISP1, + DISP2, + DISP3 +} display_port_t; + +/* IPU Pixel format definitions */ +/* Four-character-code (FOURCC) */ +#define fourcc(a, b, c, d)\ + (((__u32)(a)<<0)|((__u32)(b)<<8)|((__u32)(c)<<16)|((__u32)(d)<<24)) + +/*! + * @name IPU Pixel Formats + * + * Pixel formats are defined with ASCII FOURCC code. The pixel format codes are + * the same used by V4L2 API. + */ + +/*! @{ */ +/*! @name Generic or Raw Data Formats */ +/*! @{ */ +#define IPU_PIX_FMT_GENERIC fourcc('I', 'P', 'U', '0') /*!< IPU Generic Data */ +#define IPU_PIX_FMT_GENERIC_32 fourcc('I', 'P', 'U', '1') /*!< IPU Generic Data */ +#define IPU_PIX_FMT_LVDS666 fourcc('L', 'V', 'D', '6') /*!< IPU Generic Data */ +#define IPU_PIX_FMT_LVDS888 fourcc('L', 'V', 'D', '8') /*!< IPU Generic Data */ +/*! @} */ +/*! @name RGB Formats */ +/*! @{ */ +#define IPU_PIX_FMT_RGB332 fourcc('R', 'G', 'B', '1') /*!< 8 RGB-3-3-2 */ +#define IPU_PIX_FMT_RGB555 fourcc('R', 'G', 'B', 'O') /*!< 16 RGB-5-5-5 */ +#define IPU_PIX_FMT_RGB565 fourcc('R', 'G', 'B', 'P') /*!< 1 6 RGB-5-6-5 */ +#define IPU_PIX_FMT_RGB666 fourcc('R', 'G', 'B', '6') /*!< 18 RGB-6-6-6 */ +#define IPU_PIX_FMT_BGR666 fourcc('B', 'G', 'R', '6') /*!< 18 BGR-6-6-6 */ +#define IPU_PIX_FMT_BGR24 fourcc('B', 'G', 'R', '3') /*!< 24 BGR-8-8-8 */ +#define IPU_PIX_FMT_RGB24 fourcc('R', 'G', 'B', '3') /*!< 24 RGB-8-8-8 */ +#define IPU_PIX_FMT_GBR24 fourcc('G', 'B', 'R', '3') /*!< 24 GBR-8-8-8 */ +#define IPU_PIX_FMT_BGR32 fourcc('B', 'G', 'R', '4') /*!< 32 BGR-8-8-8-8 */ +#define IPU_PIX_FMT_BGRA32 fourcc('B', 'G', 'R', 'A') /*!< 32 BGR-8-8-8-8 */ +#define IPU_PIX_FMT_RGB32 fourcc('R', 'G', 'B', '4') /*!< 32 RGB-8-8-8-8 */ +#define IPU_PIX_FMT_RGBA32 fourcc('R', 'G', 'B', 'A') /*!< 32 RGB-8-8-8-8 */ +#define IPU_PIX_FMT_ABGR32 fourcc('A', 'B', 'G', 'R') /*!< 32 ABGR-8-8-8-8 */ +/*! @} */ +/*! @name YUV Interleaved Formats */ +/*! @{ */ +#define IPU_PIX_FMT_YUYV fourcc('Y', 'U', 'Y', 'V') /*!< 16 YUV 4:2:2 */ +#define IPU_PIX_FMT_UYVY fourcc('U', 'Y', 'V', 'Y') /*!< 16 YUV 4:2:2 */ +#define IPU_PIX_FMT_YVYU fourcc('Y', 'V', 'Y', 'U') /*!< 16 YVYU 4:2:2 */ +#define IPU_PIX_FMT_VYUY fourcc('V', 'Y', 'U', 'Y') /*!< 16 VYYU 4:2:2 */ +#define IPU_PIX_FMT_Y41P fourcc('Y', '4', '1', 'P') /*!< 12 YUV 4:1:1 */ +#define IPU_PIX_FMT_YUV444 fourcc('Y', '4', '4', '4') /*!< 24 YUV 4:4:4 */ +#define IPU_PIX_FMT_VYU444 fourcc('V', '4', '4', '4') /*!< 24 VYU 4:4:4 */ +/* two planes -- one Y, one Cb + Cr interleaved */ +#define IPU_PIX_FMT_NV12 fourcc('N', 'V', '1', '2') /* 12 Y/CbCr 4:2:0 */ +/*! @} */ +/*! @name YUV Planar Formats */ +/*! @{ */ +#define IPU_PIX_FMT_GREY fourcc('G', 'R', 'E', 'Y') /*!< 8 Greyscale */ +#define IPU_PIX_FMT_YVU410P fourcc('Y', 'V', 'U', '9') /*!< 9 YVU 4:1:0 */ +#define IPU_PIX_FMT_YUV410P fourcc('Y', 'U', 'V', '9') /*!< 9 YUV 4:1:0 */ +#define IPU_PIX_FMT_YVU420P fourcc('Y', 'V', '1', '2') /*!< 12 YVU 4:2:0 */ +#define IPU_PIX_FMT_YUV420P fourcc('I', '4', '2', '0') /*!< 12 YUV 4:2:0 */ +#define IPU_PIX_FMT_YUV420P2 fourcc('Y', 'U', '1', '2') /*!< 12 YUV 4:2:0 */ +#define IPU_PIX_FMT_YVU422P fourcc('Y', 'V', '1', '6') /*!< 16 YVU 4:2:2 */ +#define IPU_PIX_FMT_YUV422P fourcc('4', '2', '2', 'P') /*!< 16 YUV 4:2:2 */ +/*! @} */ + +/* IPU device */ +struct ipu_pos { + u32 x; + u32 y; +}; + +struct ipu_crop { + struct ipu_pos pos; + u32 w; + u32 h; +}; + +struct ipu_deinterlace { + bool enable; + u8 motion; /*see ipu_motion_sel*/ +#define IPU_DEINTERLACE_FIELD_TOP 0 +#define IPU_DEINTERLACE_FIELD_BOTTOM 1 + u8 field_fmt; +}; + +struct ipu_input { + u32 width; + u32 height; + u32 format; + struct ipu_crop crop; + dma_addr_t paddr; + + struct ipu_deinterlace deinterlace; + dma_addr_t paddr_n; /*valid when deinterlace enable*/ +}; + +struct ipu_alpha { +#define IPU_ALPHA_MODE_GLOBAL 0 +#define IPU_ALPHA_MODE_LOCAL 1 + u8 mode; + u8 gvalue; /* 0~255 */ + dma_addr_t loc_alp_paddr; +}; + +struct ipu_colorkey { + bool enable; + u32 value; /* RGB 24bit */ +}; + +struct ipu_overlay { + u32 width; + u32 height; + u32 format; + struct ipu_crop crop; + struct ipu_alpha alpha; + struct ipu_colorkey colorkey; + dma_addr_t paddr; +}; + +struct ipu_output { + u32 width; + u32 height; + u32 format; + u8 rotate; + struct ipu_crop crop; + dma_addr_t paddr; +}; + +struct ipu_task { + struct ipu_input input; + struct ipu_output output; + + bool overlay_en; + struct ipu_overlay overlay; + +#define IPU_TASK_PRIORITY_NORMAL 0 +#define IPU_TASK_PRIORITY_HIGH 1 + u8 priority; + +#define IPU_TASK_ID_ANY 0 +#define IPU_TASK_ID_VF 1 +#define IPU_TASK_ID_PP 2 +#define IPU_TASK_ID_MAX 3 + u8 task_id; + + int timeout; +}; + +enum { + IPU_CHECK_OK = 0, + IPU_CHECK_WARN_INPUT_OFFS_NOT8ALIGN = 0x1, + IPU_CHECK_WARN_OUTPUT_OFFS_NOT8ALIGN = 0x2, + IPU_CHECK_WARN_OVERLAY_OFFS_NOT8ALIGN = 0x4, + IPU_CHECK_ERR_MIN, + IPU_CHECK_ERR_INPUT_CROP, + IPU_CHECK_ERR_OUTPUT_CROP, + IPU_CHECK_ERR_OVERLAY_CROP, + IPU_CHECK_ERR_INPUT_OVER_LIMIT, + IPU_CHECK_ERR_OV_OUT_NO_FIT, + IPU_CHECK_ERR_OVERLAY_WITH_VDI, + IPU_CHECK_ERR_PROC_NO_NEED, + IPU_CHECK_ERR_SPLIT_INPUTW_OVER, + IPU_CHECK_ERR_SPLIT_INPUTH_OVER, + IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER, + IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER, +}; + +/* IOCTL commands */ +#define IPU_CHECK_TASK _IOWR('I', 0x1, struct ipu_task) +#define IPU_QUEUE_TASK _IOW('I', 0x2, struct ipu_task) +#define IPU_ALLOC _IOWR('I', 0x3, int) +#define IPU_FREE _IOW('I', 0x4, int) + +#endif |