diff options
author | Guennadi Liakhovetski <lg@denx.de> | 2009-01-19 15:36:21 -0700 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2009-01-19 15:36:21 -0700 |
commit | 5296b56d1b2000b60fb966be161c1f8fb629786b (patch) | |
tree | 18277748caa9ba43610f76a310d34a3b2155e1a5 | |
parent | ef560682a97491f62ef538931a4861b57d66c52c (diff) |
i.MX31: Image Processing Unit DMA and IRQ drivers
i.MX3x SoCs contain an Image Processing Unit, consisting of a Control
Module (CM), Display Interface (DI), Synchronous Display Controller (SDC),
Asynchronous Display Controller (ADC), Image Converter (IC), Post-Filter
(PF), Camera Sensor Interface (CSI), and an Image DMA Controller (IDMAC).
CM contains, among other blocks, an Interrupt Generator (IG) and a Clock
and Reset Control Unit (CRCU). This driver serves IDMAC and IG. They are
supported over dmaengine and irq-chip APIs respectively.
IDMAC is a specialised DMA controller, its DMA channels cannot be used for
general-purpose operations, even though it might be possible to configure
a memory-to-memory channel for memcpy operation. This driver will not work
with generic dmaengine clients, clients, wishing to use it must use
respective wrapper structures, they also must specify which channels they
require, as channels are hard-wired to specific IPU functions.
Acked-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Guennadi Liakhovetski <lg@denx.de>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
-rw-r--r-- | arch/arm/plat-mxc/include/mach/ipu.h | 181 | ||||
-rw-r--r-- | arch/arm/plat-mxc/include/mach/irqs.h | 10 | ||||
-rw-r--r-- | drivers/dma/Kconfig | 19 | ||||
-rw-r--r-- | drivers/dma/Makefile | 1 | ||||
-rw-r--r-- | drivers/dma/ipu/Makefile | 1 | ||||
-rw-r--r-- | drivers/dma/ipu/ipu_idmac.c | 1740 | ||||
-rw-r--r-- | drivers/dma/ipu/ipu_intern.h | 176 | ||||
-rw-r--r-- | drivers/dma/ipu/ipu_irq.c | 413 |
8 files changed, 2540 insertions, 1 deletions
diff --git a/arch/arm/plat-mxc/include/mach/ipu.h b/arch/arm/plat-mxc/include/mach/ipu.h new file mode 100644 index 000000000000..a9221f1cc1a0 --- /dev/null +++ b/arch/arm/plat-mxc/include/mach/ipu.h @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2008 + * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de> + * + * Copyright (C) 2005-2007 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 version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _IPU_H_ +#define _IPU_H_ + +#include <linux/types.h> +#include <linux/dmaengine.h> + +/* IPU DMA Controller channel definitions. */ +enum ipu_channel { + IDMAC_IC_0 = 0, /* IC (encoding task) to memory */ + IDMAC_IC_1 = 1, /* IC (viewfinder task) to memory */ + IDMAC_ADC_0 = 1, + IDMAC_IC_2 = 2, + IDMAC_ADC_1 = 2, + IDMAC_IC_3 = 3, + IDMAC_IC_4 = 4, + IDMAC_IC_5 = 5, + IDMAC_IC_6 = 6, + IDMAC_IC_7 = 7, /* IC (sensor data) to memory */ + IDMAC_IC_8 = 8, + IDMAC_IC_9 = 9, + IDMAC_IC_10 = 10, + IDMAC_IC_11 = 11, + IDMAC_IC_12 = 12, + IDMAC_IC_13 = 13, + IDMAC_SDC_0 = 14, /* Background synchronous display data */ + IDMAC_SDC_1 = 15, /* Foreground data (overlay) */ + IDMAC_SDC_2 = 16, + IDMAC_SDC_3 = 17, + IDMAC_ADC_2 = 18, + IDMAC_ADC_3 = 19, + IDMAC_ADC_4 = 20, + IDMAC_ADC_5 = 21, + IDMAC_ADC_6 = 22, + IDMAC_ADC_7 = 23, + IDMAC_PF_0 = 24, + IDMAC_PF_1 = 25, + IDMAC_PF_2 = 26, + IDMAC_PF_3 = 27, + IDMAC_PF_4 = 28, + IDMAC_PF_5 = 29, + IDMAC_PF_6 = 30, + IDMAC_PF_7 = 31, +}; + +/* Order significant! */ +enum ipu_channel_status { + IPU_CHANNEL_FREE, + IPU_CHANNEL_INITIALIZED, + IPU_CHANNEL_READY, + IPU_CHANNEL_ENABLED, +}; + +#define IPU_CHANNELS_NUM 32 + +enum pixel_fmt { + /* 1 byte */ + IPU_PIX_FMT_GENERIC, + IPU_PIX_FMT_RGB332, + IPU_PIX_FMT_YUV420P, + IPU_PIX_FMT_YUV422P, + IPU_PIX_FMT_YUV420P2, + IPU_PIX_FMT_YVU422P, + /* 2 bytes */ + IPU_PIX_FMT_RGB565, + IPU_PIX_FMT_RGB666, + IPU_PIX_FMT_BGR666, + IPU_PIX_FMT_YUYV, + IPU_PIX_FMT_UYVY, + /* 3 bytes */ + IPU_PIX_FMT_RGB24, + IPU_PIX_FMT_BGR24, + /* 4 bytes */ + IPU_PIX_FMT_GENERIC_32, + IPU_PIX_FMT_RGB32, + IPU_PIX_FMT_BGR32, + IPU_PIX_FMT_ABGR32, + IPU_PIX_FMT_BGRA32, + IPU_PIX_FMT_RGBA32, +}; + +enum ipu_color_space { + IPU_COLORSPACE_RGB, + IPU_COLORSPACE_YCBCR, + 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; + uint32_t in_pixel_fmt; + unsigned short out_width; + unsigned short out_height; + uint32_t out_pixel_fmt; + unsigned short out_stride; + bool graphics_combine_en; + bool global_alpha_en; + bool key_color_en; + enum display_port disp; + unsigned short out_left; + unsigned short out_top; +}; + +/* + * Union of initialization parameters for a logical channel. So far only video + * parameters are used. + */ +union ipu_channel_param { + struct idmac_video_param video; +}; + +struct idmac_tx_desc { + struct dma_async_tx_descriptor txd; + struct scatterlist *sg; /* scatterlist for this */ + unsigned int sg_len; /* tx-descriptor. */ + struct list_head list; +}; + +struct idmac_channel { + struct dma_chan dma_chan; + dma_cookie_t completed; /* last completed cookie */ + union ipu_channel_param params; + enum ipu_channel link; /* input channel, linked to the output */ + enum ipu_channel_status status; + void *client; /* Only one client per channel */ + unsigned int n_tx_desc; + struct idmac_tx_desc *desc; /* allocated tx-descriptors */ + struct scatterlist *sg[2]; /* scatterlist elements in buffer-0 and -1 */ + struct list_head free_list; /* free tx-descriptors */ + struct list_head queue; /* queued tx-descriptors */ + spinlock_t lock; /* protects sg[0,1], queue */ + struct mutex chan_mutex; /* protects status, cookie, free_list */ + bool sec_chan_en; + int active_buffer; + unsigned int eof_irq; + char eof_name[16]; /* EOF IRQ name for request_irq() */ +}; + +#define to_tx_desc(tx) container_of(tx, struct idmac_tx_desc, txd) +#define to_idmac_chan(c) container_of(c, struct idmac_channel, dma_chan) + +#endif diff --git a/arch/arm/plat-mxc/include/mach/irqs.h b/arch/arm/plat-mxc/include/mach/irqs.h index e06d3cb0ee11..c02b8fc2d821 100644 --- a/arch/arm/plat-mxc/include/mach/irqs.h +++ b/arch/arm/plat-mxc/include/mach/irqs.h @@ -35,7 +35,15 @@ #define MXC_BOARD_IRQ_START (MXC_INTERNAL_IRQS + MXC_GPIO_IRQS) #define MXC_BOARD_IRQS 16 -#define NR_IRQS (MXC_BOARD_IRQ_START + MXC_BOARD_IRQS) +#define MXC_IPU_IRQ_START (MXC_BOARD_IRQ_START + MXC_BOARD_IRQS) + +#ifdef CONFIG_MX3_IPU_IRQS +#define MX3_IPU_IRQS CONFIG_MX3_IPU_IRQS +#else +#define MX3_IPU_IRQS 0 +#endif + +#define NR_IRQS (MXC_IPU_IRQ_START + MX3_IPU_IRQS) extern void imx_irq_set_priority(unsigned char irq, unsigned char prio); diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index e34b06420816..48ea59e79672 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -62,6 +62,25 @@ config MV_XOR ---help--- Enable support for the Marvell XOR engine. +config MX3_IPU + bool "MX3x Image Processing Unit support" + depends on ARCH_MX3 + select DMA_ENGINE + default y + help + If you plan to use the Image Processing unit in the i.MX3x, say + Y here. If unsure, select Y. + +config MX3_IPU_IRQS + int "Number of dynamically mapped interrupts for IPU" + depends on MX3_IPU + range 2 137 + default 4 + help + Out of 137 interrupt sources on i.MX31 IPU only very few are used. + To avoid bloating the irq_desc[] array we allocate a sufficient + number of IRQ slots and map them dynamically to specific sources. + config DMA_ENGINE bool diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 14f59527d4f6..2e5dc96700d2 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_INTEL_IOP_ADMA) += iop-adma.o obj-$(CONFIG_FSL_DMA) += fsldma.o obj-$(CONFIG_MV_XOR) += mv_xor.o obj-$(CONFIG_DW_DMAC) += dw_dmac.o +obj-$(CONFIG_MX3_IPU) += ipu/ diff --git a/drivers/dma/ipu/Makefile b/drivers/dma/ipu/Makefile new file mode 100644 index 000000000000..6704cf48326d --- /dev/null +++ b/drivers/dma/ipu/Makefile @@ -0,0 +1 @@ +obj-y += ipu_irq.o ipu_idmac.o diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c new file mode 100644 index 000000000000..1f154d08e98f --- /dev/null +++ b/drivers/dma/ipu/ipu_idmac.c @@ -0,0 +1,1740 @@ +/* + * Copyright (C) 2008 + * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de> + * + * Copyright (C) 2005-2007 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 version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/list.h> +#include <linux/clk.h> +#include <linux/vmalloc.h> +#include <linux/string.h> +#include <linux/interrupt.h> +#include <linux/io.h> + +#include <mach/ipu.h> + +#include "ipu_intern.h" + +#define FS_VF_IN_VALID 0x00000002 +#define FS_ENC_IN_VALID 0x00000001 + +/* + * There can be only one, we could allocate it dynamically, but then we'd have + * to add an extra parameter to some functions, and use something as ugly as + * struct ipu *ipu = to_ipu(to_idmac(ichan->dma_chan.device)); + * in the ISR + */ +static struct ipu ipu_data; + +#define to_ipu(id) container_of(id, struct ipu, idmac) + +static u32 __idmac_read_icreg(struct ipu *ipu, unsigned long reg) +{ + return __raw_readl(ipu->reg_ic + reg); +} + +#define idmac_read_icreg(ipu, reg) __idmac_read_icreg(ipu, reg - IC_CONF) + +static void __idmac_write_icreg(struct ipu *ipu, u32 value, unsigned long reg) +{ + __raw_writel(value, ipu->reg_ic + reg); +} + +#define idmac_write_icreg(ipu, v, reg) __idmac_write_icreg(ipu, v, reg - IC_CONF) + +static u32 idmac_read_ipureg(struct ipu *ipu, unsigned long reg) +{ + return __raw_readl(ipu->reg_ipu + reg); +} + +static void idmac_write_ipureg(struct ipu *ipu, u32 value, unsigned long reg) +{ + __raw_writel(value, ipu->reg_ipu + reg); +} + +/***************************************************************************** + * IPU / IC common functions + */ +static void dump_idmac_reg(struct ipu *ipu) +{ + dev_dbg(ipu->dev, "IDMAC_CONF 0x%x, IC_CONF 0x%x, IDMAC_CHA_EN 0x%x, " + "IDMAC_CHA_PRI 0x%x, IDMAC_CHA_BUSY 0x%x\n", + idmac_read_icreg(ipu, IDMAC_CONF), + idmac_read_icreg(ipu, IC_CONF), + idmac_read_icreg(ipu, IDMAC_CHA_EN), + idmac_read_icreg(ipu, IDMAC_CHA_PRI), + idmac_read_icreg(ipu, IDMAC_CHA_BUSY)); + dev_dbg(ipu->dev, "BUF0_RDY 0x%x, BUF1_RDY 0x%x, CUR_BUF 0x%x, " + "DB_MODE 0x%x, TASKS_STAT 0x%x\n", + idmac_read_ipureg(ipu, IPU_CHA_BUF0_RDY), + idmac_read_ipureg(ipu, IPU_CHA_BUF1_RDY), + idmac_read_ipureg(ipu, IPU_CHA_CUR_BUF), + idmac_read_ipureg(ipu, IPU_CHA_DB_MODE_SEL), + idmac_read_ipureg(ipu, IPU_TASKS_STAT)); +} + +static uint32_t bytes_per_pixel(enum pixel_fmt fmt) +{ + switch (fmt) { + case IPU_PIX_FMT_GENERIC: /* generic data */ + case IPU_PIX_FMT_RGB332: + case IPU_PIX_FMT_YUV420P: + case IPU_PIX_FMT_YUV422P: + default: + return 1; + case IPU_PIX_FMT_RGB565: + case IPU_PIX_FMT_YUYV: + case IPU_PIX_FMT_UYVY: + return 2; + case IPU_PIX_FMT_BGR24: + case IPU_PIX_FMT_RGB24: + return 3; + case IPU_PIX_FMT_GENERIC_32: /* generic data */ + case IPU_PIX_FMT_BGR32: + case IPU_PIX_FMT_RGB32: + case IPU_PIX_FMT_ABGR32: + return 4; + } +} + +/* Enable / disable direct write to memory by the Camera Sensor Interface */ +static void ipu_ic_enable_task(struct ipu *ipu, enum ipu_channel channel) +{ + uint32_t ic_conf, mask; + + switch (channel) { + case IDMAC_IC_0: + mask = IC_CONF_PRPENC_EN; + break; + case IDMAC_IC_7: + mask = IC_CONF_RWS_EN | IC_CONF_PRPENC_EN; + break; + default: + return; + } + ic_conf = idmac_read_icreg(ipu, IC_CONF) | mask; + idmac_write_icreg(ipu, ic_conf, IC_CONF); +} + +static void ipu_ic_disable_task(struct ipu *ipu, enum ipu_channel channel) +{ + uint32_t ic_conf, mask; + + switch (channel) { + case IDMAC_IC_0: + mask = IC_CONF_PRPENC_EN; + break; + case IDMAC_IC_7: + mask = IC_CONF_RWS_EN | IC_CONF_PRPENC_EN; + break; + default: + return; + } + ic_conf = idmac_read_icreg(ipu, IC_CONF) & ~mask; + idmac_write_icreg(ipu, ic_conf, IC_CONF); +} + +static uint32_t ipu_channel_status(struct ipu *ipu, enum ipu_channel channel) +{ + uint32_t stat = TASK_STAT_IDLE; + uint32_t task_stat_reg = idmac_read_ipureg(ipu, IPU_TASKS_STAT); + + switch (channel) { + case IDMAC_IC_7: + stat = (task_stat_reg & TSTAT_CSI2MEM_MASK) >> + TSTAT_CSI2MEM_OFFSET; + break; + case IDMAC_IC_0: + case IDMAC_SDC_0: + case IDMAC_SDC_1: + default: + break; + } + return stat; +} + +struct chan_param_mem_planar { + /* Word 0 */ + u32 xv:10; + u32 yv:10; + u32 xb:12; + + u32 yb:12; + u32 res1:2; + u32 nsb:1; + u32 lnpb:6; + u32 ubo_l:11; + + u32 ubo_h:15; + u32 vbo_l:17; + + u32 vbo_h:9; + u32 res2:3; + u32 fw:12; + u32 fh_l:8; + + u32 fh_h:4; + u32 res3:28; + + /* Word 1 */ + u32 eba0; + + u32 eba1; + + u32 bpp:3; + u32 sl:14; + u32 pfs:3; + u32 bam:3; + u32 res4:2; + u32 npb:6; + u32 res5:1; + + u32 sat:2; + u32 res6:30; +} __attribute__ ((packed)); + +struct chan_param_mem_interleaved { + /* Word 0 */ + u32 xv:10; + u32 yv:10; + u32 xb:12; + + u32 yb:12; + u32 sce:1; + u32 res1:1; + u32 nsb:1; + u32 lnpb:6; + u32 sx:10; + u32 sy_l:1; + + u32 sy_h:9; + u32 ns:10; + u32 sm:10; + u32 sdx_l:3; + + u32 sdx_h:2; + u32 sdy:5; + u32 sdrx:1; + u32 sdry:1; + u32 sdr1:1; + u32 res2:2; + u32 fw:12; + u32 fh_l:8; + + u32 fh_h:4; + u32 res3:28; + + /* Word 1 */ + u32 eba0; + + u32 eba1; + + u32 bpp:3; + u32 sl:14; + u32 pfs:3; + u32 bam:3; + u32 res4:2; + u32 npb:6; + u32 res5:1; + + u32 sat:2; + u32 scc:1; + u32 ofs0:5; + u32 ofs1:5; + u32 ofs2:5; + u32 ofs3:5; + u32 wid0:3; + u32 wid1:3; + u32 wid2:3; + + u32 wid3:3; + u32 dec_sel:1; + u32 res6:28; +} __attribute__ ((packed)); + +union chan_param_mem { + struct chan_param_mem_planar pp; + struct chan_param_mem_interleaved ip; +}; + +static void ipu_ch_param_set_plane_offset(union chan_param_mem *params, + u32 u_offset, u32 v_offset) +{ + params->pp.ubo_l = u_offset & 0x7ff; + params->pp.ubo_h = u_offset >> 11; + params->pp.vbo_l = v_offset & 0x1ffff; + params->pp.vbo_h = v_offset >> 17; +} + +static void ipu_ch_param_set_size(union chan_param_mem *params, + uint32_t pixel_fmt, uint16_t width, + uint16_t height, uint16_t stride) +{ + u32 u_offset; + u32 v_offset; + + params->pp.fw = width - 1; + params->pp.fh_l = height - 1; + params->pp.fh_h = (height - 1) >> 8; + params->pp.sl = stride - 1; + + switch (pixel_fmt) { + case IPU_PIX_FMT_GENERIC: + /*Represents 8-bit Generic data */ + params->pp.bpp = 3; + params->pp.pfs = 7; + params->pp.npb = 31; + params->pp.sat = 2; /* SAT = use 32-bit access */ + break; + case IPU_PIX_FMT_GENERIC_32: + /*Represents 32-bit Generic data */ + params->pp.bpp = 0; + params->pp.pfs = 7; + params->pp.npb = 7; + params->pp.sat = 2; /* SAT = use 32-bit access */ + break; + case IPU_PIX_FMT_RGB565: + params->ip.bpp = 2; + params->ip.pfs = 4; + params->ip.npb = 7; + params->ip.sat = 2; /* SAT = 32-bit access */ + params->ip.ofs0 = 0; /* Red bit offset */ + params->ip.ofs1 = 5; /* Green bit offset */ + params->ip.ofs2 = 11; /* Blue bit offset */ + params->ip.ofs3 = 16; /* Alpha bit offset */ + params->ip.wid0 = 4; /* Red bit width - 1 */ + params->ip.wid1 = 5; /* Green bit width - 1 */ + params->ip.wid2 = 4; /* Blue bit width - 1 */ + break; + case IPU_PIX_FMT_BGR24: + params->ip.bpp = 1; /* 24 BPP & RGB PFS */ + params->ip.pfs = 4; + params->ip.npb = 7; + params->ip.sat = 2; /* SAT = 32-bit access */ + params->ip.ofs0 = 0; /* Red bit offset */ + params->ip.ofs1 = 8; /* Green bit offset */ + params->ip.ofs2 = 16; /* Blue bit offset */ + params->ip.ofs3 = 24; /* Alpha bit offset */ + params->ip.wid0 = 7; /* Red bit width - 1 */ + params->ip.wid1 = 7; /* Green bit width - 1 */ + params->ip.wid2 = 7; /* Blue bit width - 1 */ + break; + case IPU_PIX_FMT_RGB24: + params->ip.bpp = 1; /* 24 BPP & RGB PFS */ + params->ip.pfs = 4; + params->ip.npb = 7; + params->ip.sat = 2; /* SAT = 32-bit access */ + params->ip.ofs0 = 16; /* Red bit offset */ + params->ip.ofs1 = 8; /* Green bit offset */ + params->ip.ofs2 = 0; /* Blue bit offset */ + params->ip.ofs3 = 24; /* Alpha bit offset */ + params->ip.wid0 = 7; /* Red bit width - 1 */ + params->ip.wid1 = 7; /* Green bit width - 1 */ + params->ip.wid2 = 7; /* Blue bit width - 1 */ + break; + case IPU_PIX_FMT_BGRA32: + case IPU_PIX_FMT_BGR32: + params->ip.bpp = 0; + params->ip.pfs = 4; + params->ip.npb = 7; + params->ip.sat = 2; /* SAT = 32-bit access */ + params->ip.ofs0 = 8; /* Red bit offset */ + params->ip.ofs1 = 16; /* Green bit offset */ + params->ip.ofs2 = 24; /* Blue bit offset */ + params->ip.ofs3 = 0; /* Alpha bit offset */ + params->ip.wid0 = 7; /* Red bit width - 1 */ + params->ip.wid1 = 7; /* Green bit width - 1 */ + params->ip.wid2 = 7; /* Blue bit width - 1 */ + params->ip.wid3 = 7; /* Alpha bit width - 1 */ + break; + case IPU_PIX_FMT_RGBA32: + case IPU_PIX_FMT_RGB32: + params->ip.bpp = 0; + params->ip.pfs = 4; + params->ip.npb = 7; + params->ip.sat = 2; /* SAT = 32-bit access */ + params->ip.ofs0 = 24; /* Red bit offset */ + params->ip.ofs1 = 16; /* Green bit offset */ + params->ip.ofs2 = 8; /* Blue bit offset */ + params->ip.ofs3 = 0; /* Alpha bit offset */ + params->ip.wid0 = 7; /* Red bit width - 1 */ + params->ip.wid1 = 7; /* Green bit width - 1 */ + params->ip.wid2 = 7; /* Blue bit width - 1 */ + params->ip.wid3 = 7; /* Alpha bit width - 1 */ + break; + case IPU_PIX_FMT_ABGR32: + params->ip.bpp = 0; + params->ip.pfs = 4; + params->ip.npb = 7; + params->ip.sat = 2; /* SAT = 32-bit access */ + params->ip.ofs0 = 8; /* Red bit offset */ + params->ip.ofs1 = 16; /* Green bit offset */ + params->ip.ofs2 = 24; /* Blue bit offset */ + params->ip.ofs3 = 0; /* Alpha bit offset */ + params->ip.wid0 = 7; /* Red bit width - 1 */ + params->ip.wid1 = 7; /* Green bit width - 1 */ + params->ip.wid2 = 7; /* Blue bit width - 1 */ + params->ip.wid3 = 7; /* Alpha bit width - 1 */ + break; + case IPU_PIX_FMT_UYVY: + params->ip.bpp = 2; + params->ip.pfs = 6; + params->ip.npb = 7; + params->ip.sat = 2; /* SAT = 32-bit access */ + break; + case IPU_PIX_FMT_YUV420P2: + case IPU_PIX_FMT_YUV420P: + params->ip.bpp = 3; + params->ip.pfs = 3; + params->ip.npb = 7; + params->ip.sat = 2; /* SAT = 32-bit access */ + u_offset = stride * height; + v_offset = u_offset + u_offset / 4; + ipu_ch_param_set_plane_offset(params, u_offset, v_offset); + break; + case IPU_PIX_FMT_YVU422P: + params->ip.bpp = 3; + params->ip.pfs = 2; + params->ip.npb = 7; + params->ip.sat = 2; /* SAT = 32-bit access */ + v_offset = stride * height; + u_offset = v_offset + v_offset / 2; + ipu_ch_param_set_plane_offset(params, u_offset, v_offset); + break; + case IPU_PIX_FMT_YUV422P: + params->ip.bpp = 3; + params->ip.pfs = 2; + params->ip.npb = 7; + params->ip.sat = 2; /* SAT = 32-bit access */ + u_offset = stride * height; + v_offset = u_offset + u_offset / 2; + ipu_ch_param_set_plane_offset(params, u_offset, v_offset); + break; + default: + dev_err(ipu_data.dev, + "mxc ipu: unimplemented pixel format %d\n", pixel_fmt); + break; + } + + params->pp.nsb = 1; +} + +static void ipu_ch_param_set_burst_size(union chan_param_mem *params, + uint16_t burst_pixels) +{ + params->pp.npb = burst_pixels - 1; +}; + +static void ipu_ch_param_set_buffer(union chan_param_mem *params, + dma_addr_t buf0, dma_addr_t buf1) +{ + params->pp.eba0 = buf0; + params->pp.eba1 = buf1; +}; + +static void ipu_ch_param_set_rotation(union chan_param_mem *params, + enum ipu_rotate_mode rotate) +{ + params->pp.bam = rotate; +}; + +static void ipu_write_param_mem(uint32_t addr, uint32_t *data, + uint32_t num_words) +{ + for (; num_words > 0; num_words--) { + dev_dbg(ipu_data.dev, + "write param mem - addr = 0x%08X, data = 0x%08X\n", + addr, *data); + idmac_write_ipureg(&ipu_data, addr, IPU_IMA_ADDR); + idmac_write_ipureg(&ipu_data, *data++, IPU_IMA_DATA); + addr++; + if ((addr & 0x7) == 5) { + addr &= ~0x7; /* set to word 0 */ + addr += 8; /* increment to next row */ + } + } +} + +static int calc_resize_coeffs(uint32_t in_size, uint32_t out_size, + uint32_t *resize_coeff, + uint32_t *downsize_coeff) +{ + uint32_t temp_size; + uint32_t temp_downsize; + + *resize_coeff = 1 << 13; + *downsize_coeff = 1 << 13; + + /* Cannot downsize more than 8:1 */ + if (out_size << 3 < in_size) + return -EINVAL; + + /* compute downsizing coefficient */ + temp_downsize = 0; + temp_size = in_size; + while (temp_size >= out_size * 2 && temp_downsize < 2) { + temp_size >>= 1; + temp_downsize++; + } + *downsize_coeff = temp_downsize; + + /* + * compute resizing coefficient using the following formula: + * resize_coeff = M*(SI -1)/(SO - 1) + * where M = 2^13, SI - input size, SO - output size + */ + *resize_coeff = (8192L * (temp_size - 1)) / (out_size - 1); + if (*resize_coeff >= 16384L) { + dev_err(ipu_data.dev, "Warning! Overflow on resize coeff.\n"); + *resize_coeff = 0x3FFF; + } + + dev_dbg(ipu_data.dev, "resizing from %u -> %u pixels, " + "downsize=%u, resize=%u.%lu (reg=%u)\n", in_size, out_size, + *downsize_coeff, *resize_coeff >= 8192L ? 1 : 0, + ((*resize_coeff & 0x1FFF) * 10000L) / 8192L, *resize_coeff); + + return 0; +} + +static enum ipu_color_space format_to_colorspace(enum pixel_fmt fmt) +{ + switch (fmt) { + case IPU_PIX_FMT_RGB565: + case IPU_PIX_FMT_BGR24: + case IPU_PIX_FMT_RGB24: + case IPU_PIX_FMT_BGR32: + case IPU_PIX_FMT_RGB32: + return IPU_COLORSPACE_RGB; + default: + return IPU_COLORSPACE_YCBCR; + } +} + +static int ipu_ic_init_prpenc(struct ipu *ipu, + union ipu_channel_param *params, bool src_is_csi) +{ + uint32_t reg, ic_conf; + uint32_t downsize_coeff, resize_coeff; + enum ipu_color_space in_fmt, out_fmt; + + /* Setup vertical resizing */ + calc_resize_coeffs(params->video.in_height, + params->video.out_height, + &resize_coeff, &downsize_coeff); + reg = (downsize_coeff << 30) | (resize_coeff << 16); + + /* Setup horizontal resizing */ + calc_resize_coeffs(params->video.in_width, + params->video.out_width, + &resize_coeff, &downsize_coeff); + reg |= (downsize_coeff << 14) | resize_coeff; + + /* Setup color space conversion */ + in_fmt = format_to_colorspace(params->video.in_pixel_fmt); + out_fmt = format_to_colorspace(params->video.out_pixel_fmt); + + /* + * Colourspace conversion unsupported yet - see _init_csc() in + * Freescale sources + */ + if (in_fmt != out_fmt) { + dev_err(ipu->dev, "Colourspace conversion unsupported!\n"); + return -EOPNOTSUPP; + } + + idmac_write_icreg(ipu, reg, IC_PRP_ENC_RSC); + + ic_conf = idmac_read_icreg(ipu, IC_CONF); + + if (src_is_csi) + ic_conf &= ~IC_CONF_RWS_EN; + else + ic_conf |= IC_CONF_RWS_EN; + + idmac_write_icreg(ipu, ic_conf, IC_CONF); + + return 0; +} + +static uint32_t dma_param_addr(uint32_t dma_ch) +{ + /* Channel Parameter Memory */ + return 0x10000 | (dma_ch << 4); +}; + +static void ipu_channel_set_priority(struct ipu *ipu, enum ipu_channel channel, + bool prio) +{ + u32 reg = idmac_read_icreg(ipu, IDMAC_CHA_PRI); + + if (prio) + reg |= 1UL << channel; + else + reg &= ~(1UL << channel); + + idmac_write_icreg(ipu, reg, IDMAC_CHA_PRI); + + dump_idmac_reg(ipu); +} + +static uint32_t ipu_channel_conf_mask(enum ipu_channel channel) +{ + uint32_t mask; + + switch (channel) { + case IDMAC_IC_0: + case IDMAC_IC_7: + mask = IPU_CONF_CSI_EN | IPU_CONF_IC_EN; + break; + case IDMAC_SDC_0: + case IDMAC_SDC_1: + mask = IPU_CONF_SDC_EN | IPU_CONF_DI_EN; + break; + default: + mask = 0; + break; + } + + return mask; +} + +/** + * ipu_enable_channel() - enable an IPU channel. + * @channel: channel ID. + * @return: 0 on success or negative error code on failure. + */ +static int ipu_enable_channel(struct idmac *idmac, struct idmac_channel *ichan) +{ + struct ipu *ipu = to_ipu(idmac); + enum ipu_channel channel = ichan->dma_chan.chan_id; + uint32_t reg; + unsigned long flags; + + spin_lock_irqsave(&ipu->lock, flags); + + /* Reset to buffer 0 */ + idmac_write_ipureg(ipu, 1UL << channel, IPU_CHA_CUR_BUF); + ichan->active_buffer = 0; + ichan->status = IPU_CHANNEL_ENABLED; + + switch (channel) { + case IDMAC_SDC_0: + case IDMAC_SDC_1: + case IDMAC_IC_7: + ipu_channel_set_priority(ipu, channel, true); + default: + break; + } + + reg = idmac_read_icreg(ipu, IDMAC_CHA_EN); + + idmac_write_icreg(ipu, reg | (1UL << channel), IDMAC_CHA_EN); + + ipu_ic_enable_task(ipu, channel); + + spin_unlock_irqrestore(&ipu->lock, flags); + return 0; +} + +/** + * ipu_init_channel_buffer() - initialize a buffer for logical IPU channel. + * @channel: channel ID. + * @pixel_fmt: pixel format of buffer. Pixel format is a FOURCC ASCII code. + * @width: width of buffer in pixels. + * @height: height of buffer in pixels. + * @stride: stride length of buffer in pixels. + * @rot_mode: rotation mode of buffer. A rotation setting other than + * IPU_ROTATE_VERT_FLIP should only be used for input buffers of + * rotation channels. + * @phyaddr_0: buffer 0 physical address. + * @phyaddr_1: buffer 1 physical address. Setting this to a value other than + * NULL enables double buffering mode. + * @return: 0 on success or negative error code on failure. + */ +static int ipu_init_channel_buffer(struct idmac_channel *ichan, + enum pixel_fmt pixel_fmt, + uint16_t width, uint16_t height, + uint32_t stride, + enum ipu_rotate_mode rot_mode, + dma_addr_t phyaddr_0, dma_addr_t phyaddr_1) +{ + enum ipu_channel channel = ichan->dma_chan.chan_id; + struct idmac *idmac = to_idmac(ichan->dma_chan.device); + struct ipu *ipu = to_ipu(idmac); + union chan_param_mem params = {}; + unsigned long flags; + uint32_t reg; + uint32_t stride_bytes; + + stride_bytes = stride * bytes_per_pixel(pixel_fmt); + + if (stride_bytes % 4) { + dev_err(ipu->dev, + "Stride length must be 32-bit aligned, stride = %d, bytes = %d\n", + stride, stride_bytes); + return -EINVAL; + } + + /* IC channel's stride must be a multiple of 8 pixels */ + if ((channel <= 13) && (stride % 8)) { + dev_err(ipu->dev, "Stride must be 8 pixel multiple\n"); + return -EINVAL; + } + + /* Build parameter memory data for DMA channel */ + ipu_ch_param_set_size(¶ms, pixel_fmt, width, height, stride_bytes); + ipu_ch_param_set_buffer(¶ms, phyaddr_0, phyaddr_1); + ipu_ch_param_set_rotation(¶ms, rot_mode); + /* Some channels (rotation) have restriction on burst length */ + switch (channel) { + case IDMAC_IC_7: /* Hangs with burst 8, 16, other values + invalid - Table 44-30 */ +/* + ipu_ch_param_set_burst_size(¶ms, 8); + */ + break; + case IDMAC_SDC_0: + case IDMAC_SDC_1: + /* In original code only IPU_PIX_FMT_RGB565 was setting burst */ + ipu_ch_param_set_burst_size(¶ms, 16); + break; + case IDMAC_IC_0: + default: + break; + } + + spin_lock_irqsave(&ipu->lock, flags); + + ipu_write_param_mem(dma_param_addr(channel), (uint32_t *)¶ms, 10); + + reg = idmac_read_ipureg(ipu, IPU_CHA_DB_MODE_SEL); + + if (phyaddr_1) + reg |= 1UL << channel; + else + reg &= ~(1UL << channel); + + idmac_write_ipureg(ipu, reg, IPU_CHA_DB_MODE_SEL); + + ichan->status = IPU_CHANNEL_READY; + + spin_unlock_irqrestore(ipu->lock, flags); + + return 0; +} + +/** + * ipu_select_buffer() - mark a channel's buffer as ready. + * @channel: channel ID. + * @buffer_n: buffer number to mark ready. + */ +static void ipu_select_buffer(enum ipu_channel channel, int buffer_n) +{ + /* No locking - this is a write-one-to-set register, cleared by IPU */ + if (buffer_n == 0) + /* Mark buffer 0 as ready. */ + idmac_write_ipureg(&ipu_data, 1UL << channel, IPU_CHA_BUF0_RDY); + else + /* Mark buffer 1 as ready. */ + idmac_write_ipureg(&ipu_data, 1UL << channel, IPU_CHA_BUF1_RDY); +} + +/** + * ipu_update_channel_buffer() - update physical address of a channel buffer. + * @channel: channel ID. + * @buffer_n: buffer number to update. + * 0 or 1 are the only valid values. + * @phyaddr: buffer physical address. + * @return: Returns 0 on success or negative error code on failure. This + * function will fail if the buffer is set to ready. + */ +/* Called under spin_lock(_irqsave)(&ichan->lock) */ +static int ipu_update_channel_buffer(enum ipu_channel channel, + int buffer_n, dma_addr_t phyaddr) +{ + uint32_t reg; + unsigned long flags; + + spin_lock_irqsave(&ipu_data.lock, flags); + + if (buffer_n == 0) { + reg = idmac_read_ipureg(&ipu_data, IPU_CHA_BUF0_RDY); + if (reg & (1UL << channel)) { + spin_unlock_irqrestore(&ipu_data.lock, flags); + return -EACCES; + } + + /* 44.3.3.1.9 - Row Number 1 (WORD1, offset 0) */ + idmac_write_ipureg(&ipu_data, dma_param_addr(channel) + + 0x0008UL, IPU_IMA_ADDR); + idmac_write_ipureg(&ipu_data, phyaddr, IPU_IMA_DATA); + } else { + reg = idmac_read_ipureg(&ipu_data, IPU_CHA_BUF1_RDY); + if (reg & (1UL << channel)) { + spin_unlock_irqrestore(&ipu_data.lock, flags); + return -EACCES; + } + + /* Check if double-buffering is already enabled */ + reg = idmac_read_ipureg(&ipu_data, IPU_CHA_DB_MODE_SEL); + + if (!(reg & (1UL << channel))) + idmac_write_ipureg(&ipu_data, reg | (1UL << channel), + IPU_CHA_DB_MODE_SEL); + + /* 44.3.3.1.9 - Row Number 1 (WORD1, offset 1) */ + idmac_write_ipureg(&ipu_data, dma_param_addr(channel) + + 0x0009UL, IPU_IMA_ADDR); + idmac_write_ipureg(&ipu_data, phyaddr, IPU_IMA_DATA); + } + + spin_unlock_irqrestore(&ipu_data.lock, flags); + + return 0; +} + +/* Called under spin_lock_irqsave(&ichan->lock) */ +static int ipu_submit_channel_buffers(struct idmac_channel *ichan, + struct idmac_tx_desc *desc) +{ + struct scatterlist *sg; + int i, ret = 0; + + for (i = 0, sg = desc->sg; i < 2 && sg; i++) { + if (!ichan->sg[i]) { + ichan->sg[i] = sg; + + /* + * On first invocation this shouldn't be necessary, the + * call to ipu_init_channel_buffer() above will set + * addresses for us, so we could make it conditional + * on status >= IPU_CHANNEL_ENABLED, but doing it again + * shouldn't hurt either. + */ + ret = ipu_update_channel_buffer(ichan->dma_chan.chan_id, i, + sg_dma_address(sg)); + if (ret < 0) + return ret; + + ipu_select_buffer(ichan->dma_chan.chan_id, i); + + sg = sg_next(sg); + } + } + + return ret; +} + +static dma_cookie_t idmac_tx_submit(struct dma_async_tx_descriptor *tx) +{ + struct idmac_tx_desc *desc = to_tx_desc(tx); + struct idmac_channel *ichan = to_idmac_chan(tx->chan); + struct idmac *idmac = to_idmac(tx->chan->device); + struct ipu *ipu = to_ipu(idmac); + dma_cookie_t cookie; + unsigned long flags; + + /* Sanity check */ + if (!list_empty(&desc->list)) { + /* The descriptor doesn't belong to client */ + dev_err(&ichan->dma_chan.dev->device, + "Descriptor %p not prepared!\n", tx); + return -EBUSY; + } + + mutex_lock(&icha |