aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2009-01-26 10:13:08 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2009-01-26 10:13:08 -0800
commit7954d5cf39ee1ce9bb0a4b19fcf1924885a9cad1 (patch)
tree3515ebb6ed3f15ead563ac3e30e30fd4b37992fb
parent37f5fed55559a030c430550bcacec75e6a833f1b (diff)
parent86528da229a448577a8401a17c295883640d336c (diff)
Merge branch 'fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/djbw/async_tx
* 'fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/djbw/async_tx: i.MX31: framebuffer driver i.MX31: Image Processing Unit DMA and IRQ drivers dmaengine: add async_tx_clear_ack() macro dmaengine: dma_issue_pending_all == nop when CONFIG_DMA_ENGINE=n dmaengine: kill some dubious WARN_ONCEs fsldma: print correct IRQ on mpc83xx fsldma: check for NO_IRQ in fsl_dma_chan_remove() dmatest: Use custom map/unmap for destination buffer fsldma: use a valid 'device' for dma_pool_create dmaengine: fix dependency chaining
-rw-r--r--arch/arm/plat-mxc/include/mach/ipu.h181
-rw-r--r--arch/arm/plat-mxc/include/mach/irqs.h10
-rw-r--r--arch/arm/plat-mxc/include/mach/mx3fb.h38
-rw-r--r--drivers/dma/Kconfig19
-rw-r--r--drivers/dma/Makefile1
-rw-r--r--drivers/dma/dmaengine.c8
-rw-r--r--drivers/dma/dmatest.c35
-rw-r--r--drivers/dma/fsldma.c8
-rw-r--r--drivers/dma/ipu/Makefile1
-rw-r--r--drivers/dma/ipu/ipu_idmac.c1740
-rw-r--r--drivers/dma/ipu/ipu_intern.h176
-rw-r--r--drivers/dma/ipu/ipu_irq.c413
-rw-r--r--drivers/video/Kconfig12
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/mx3fb.c1555
-rw-r--r--include/linux/dmaengine.h11
16 files changed, 4194 insertions, 15 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 00000000000..a9221f1cc1a
--- /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 e06d3cb0ee1..c02b8fc2d82 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/arch/arm/plat-mxc/include/mach/mx3fb.h b/arch/arm/plat-mxc/include/mach/mx3fb.h
new file mode 100644
index 00000000000..e391a76ca87
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/mx3fb.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2008
+ * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
+ *
+ * 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 __ASM_ARCH_MX3FB_H__
+#define __ASM_ARCH_MX3FB_H__
+
+#include <linux/device.h>
+#include <linux/fb.h>
+
+/* Proprietary FB_SYNC_ flags */
+#define FB_SYNC_OE_ACT_HIGH 0x80000000
+#define FB_SYNC_CLK_INVERT 0x40000000
+#define FB_SYNC_DATA_INVERT 0x20000000
+#define FB_SYNC_CLK_IDLE_EN 0x10000000
+#define FB_SYNC_SHARP_MODE 0x08000000
+#define FB_SYNC_SWAP_RGB 0x04000000
+#define FB_SYNC_CLK_SEL_EN 0x02000000
+
+/**
+ * struct mx3fb_platform_data - mx3fb platform data
+ *
+ * @dma_dev: pointer to the dma-device, used for dma-slave connection
+ * @mode: pointer to a platform-provided per mxc_register_fb() videomode
+ */
+struct mx3fb_platform_data {
+ struct device *dma_dev;
+ const char *name;
+ const struct fb_videomode *mode;
+ int num_modes;
+};
+
+#endif
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index e34b0642081..48ea59e7967 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 14f59527d4f..2e5dc96700d 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/dmaengine.c b/drivers/dma/dmaengine.c
index 403dbe78112..a58993011ed 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -329,9 +329,6 @@ struct dma_chan *dma_find_channel(enum dma_transaction_type tx_type)
struct dma_chan *chan;
int cpu;
- WARN_ONCE(dmaengine_ref_count == 0,
- "client called %s without a reference", __func__);
-
cpu = get_cpu();
chan = per_cpu_ptr(channel_table[tx_type], cpu)->chan;
put_cpu();
@@ -348,9 +345,6 @@ void dma_issue_pending_all(void)
struct dma_device *device;
struct dma_chan *chan;
- WARN_ONCE(dmaengine_ref_count == 0,
- "client called %s without a reference", __func__);
-
rcu_read_lock();
list_for_each_entry_rcu(device, &dma_device_list, global_node) {
if (dma_has_cap(DMA_PRIVATE, device->cap_mask))
@@ -961,6 +955,8 @@ void dma_run_dependencies(struct dma_async_tx_descriptor *tx)
if (!dep)
return;
+ /* we'll submit tx->next now, so clear the link */
+ tx->next = NULL;
chan = dep->chan;
/* keep submitting up until a channel switch is detected
diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c
index 3603f1ea5b2..732fa1ec36a 100644
--- a/drivers/dma/dmatest.c
+++ b/drivers/dma/dmatest.c
@@ -217,6 +217,10 @@ static int dmatest_func(void *data)
chan = thread->chan;
while (!kthread_should_stop()) {
+ struct dma_device *dev = chan->device;
+ struct dma_async_tx_descriptor *tx;
+ dma_addr_t dma_src, dma_dest;
+
total_tests++;
len = dmatest_random() % test_buf_size + 1;
@@ -226,10 +230,30 @@ static int dmatest_func(void *data)
dmatest_init_srcbuf(thread->srcbuf, src_off, len);
dmatest_init_dstbuf(thread->dstbuf, dst_off, len);
- cookie = dma_async_memcpy_buf_to_buf(chan,
- thread->dstbuf + dst_off,
- thread->srcbuf + src_off,
- len);
+ dma_src = dma_map_single(dev->dev, thread->srcbuf + src_off,
+ len, DMA_TO_DEVICE);
+ /* map with DMA_BIDIRECTIONAL to force writeback/invalidate */
+ dma_dest = dma_map_single(dev->dev, thread->dstbuf,
+ test_buf_size, DMA_BIDIRECTIONAL);
+
+ tx = dev->device_prep_dma_memcpy(chan, dma_dest + dst_off,
+ dma_src, len,
+ DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP);
+ if (!tx) {
+ dma_unmap_single(dev->dev, dma_src, len, DMA_TO_DEVICE);
+ dma_unmap_single(dev->dev, dma_dest,
+ test_buf_size, DMA_BIDIRECTIONAL);
+ pr_warning("%s: #%u: prep error with src_off=0x%x "
+ "dst_off=0x%x len=0x%x\n",
+ thread_name, total_tests - 1,
+ src_off, dst_off, len);
+ msleep(100);
+ failed_tests++;
+ continue;
+ }
+ tx->callback = NULL;
+ cookie = tx->tx_submit(tx);
+
if (dma_submit_error(cookie)) {
pr_warning("%s: #%u: submit error %d with src_off=0x%x "
"dst_off=0x%x len=0x%x\n",
@@ -253,6 +277,9 @@ static int dmatest_func(void *data)
failed_tests++;
continue;
}
+ /* Unmap by myself (see DMA_COMPL_SKIP_DEST_UNMAP above) */
+ dma_unmap_single(dev->dev, dma_dest,
+ test_buf_size, DMA_BIDIRECTIONAL);
error_count = 0;
diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c
index ca70a21afc6..70126a60623 100644
--- a/drivers/dma/fsldma.c
+++ b/drivers/dma/fsldma.c
@@ -822,7 +822,7 @@ static int __devinit fsl_dma_chan_probe(struct fsl_dma_device *fdev,
*/
WARN_ON(fdev->feature != new_fsl_chan->feature);
- new_fsl_chan->dev = &new_fsl_chan->common.dev->device;
+ new_fsl_chan->dev = fdev->dev;
new_fsl_chan->reg_base = ioremap(new_fsl_chan->reg.start,
new_fsl_chan->reg.end - new_fsl_chan->reg.start + 1);
@@ -875,7 +875,8 @@ static int __devinit fsl_dma_chan_probe(struct fsl_dma_device *fdev,
}
dev_info(fdev->dev, "#%d (%s), irq %d\n", new_fsl_chan->id,
- compatible, new_fsl_chan->irq);
+ compatible,
+ new_fsl_chan->irq != NO_IRQ ? new_fsl_chan->irq : fdev->irq);
return 0;
@@ -890,7 +891,8 @@ err_no_reg:
static void fsl_dma_chan_remove(struct fsl_dma_chan *fchan)
{
- free_irq(fchan->irq, fchan);
+ if (fchan->irq != NO_IRQ)
+ free_irq(fchan->irq, fchan);
list_del(&fchan->common.device_node);
iounmap(fchan->reg_base);
kfree(fchan);
diff --git a/drivers/dma/ipu/Makefile b/drivers/dma/ipu/Makefile
new file mode 100644
index 00000000000..6704cf48326
--- /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 00000000000..1f154d08e98
--- /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: