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>
This commit is contained in:
Родитель
ef560682a9
Коммит
5296b56d1b
|
@ -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
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
obj-y += ipu_irq.o ipu_idmac.o
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _IPU_INTERN_H_
|
||||
#define _IPU_INTERN_H_
|
||||
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
/* IPU Common registers */
|
||||
#define IPU_CONF 0x00
|
||||
#define IPU_CHA_BUF0_RDY 0x04
|
||||
#define IPU_CHA_BUF1_RDY 0x08
|
||||
#define IPU_CHA_DB_MODE_SEL 0x0C
|
||||
#define IPU_CHA_CUR_BUF 0x10
|
||||
#define IPU_FS_PROC_FLOW 0x14
|
||||
#define IPU_FS_DISP_FLOW 0x18
|
||||
#define IPU_TASKS_STAT 0x1C
|
||||
#define IPU_IMA_ADDR 0x20
|
||||
#define IPU_IMA_DATA 0x24
|
||||
#define IPU_INT_CTRL_1 0x28
|
||||
#define IPU_INT_CTRL_2 0x2C
|
||||
#define IPU_INT_CTRL_3 0x30
|
||||
#define IPU_INT_CTRL_4 0x34
|
||||
#define IPU_INT_CTRL_5 0x38
|
||||
#define IPU_INT_STAT_1 0x3C
|
||||
#define IPU_INT_STAT_2 0x40
|
||||
#define IPU_INT_STAT_3 0x44
|
||||
#define IPU_INT_STAT_4 0x48
|
||||
#define IPU_INT_STAT_5 0x4C
|
||||
#define IPU_BRK_CTRL_1 0x50
|
||||
#define IPU_BRK_CTRL_2 0x54
|
||||
#define IPU_BRK_STAT 0x58
|
||||
#define IPU_DIAGB_CTRL 0x5C
|
||||
|
||||
/* IPU_CONF Register bits */
|
||||
#define IPU_CONF_CSI_EN 0x00000001
|
||||
#define IPU_CONF_IC_EN 0x00000002
|
||||
#define IPU_CONF_ROT_EN 0x00000004
|
||||
#define IPU_CONF_PF_EN 0x00000008
|
||||
#define IPU_CONF_SDC_EN 0x00000010
|
||||
#define IPU_CONF_ADC_EN 0x00000020
|
||||
#define IPU_CONF_DI_EN 0x00000040
|
||||
#define IPU_CONF_DU_EN 0x00000080
|
||||
#define IPU_CONF_PXL_ENDIAN 0x00000100
|
||||
|
||||
/* Image Converter Registers */
|
||||
#define IC_CONF 0x88
|
||||
#define IC_PRP_ENC_RSC 0x8C
|
||||
#define IC_PRP_VF_RSC 0x90
|
||||
#define IC_PP_RSC 0x94
|
||||
#define IC_CMBP_1 0x98
|
||||
#define IC_CMBP_2 0x9C
|
||||
#define PF_CONF 0xA0
|
||||
#define IDMAC_CONF 0xA4
|
||||
#define IDMAC_CHA_EN 0xA8
|
||||
#define IDMAC_CHA_PRI 0xAC
|
||||
#define IDMAC_CHA_BUSY 0xB0
|
||||
|
||||
/* Image Converter Register bits */
|
||||
#define IC_CONF_PRPENC_EN 0x00000001
|
||||
#define IC_CONF_PRPENC_CSC1 0x00000002
|
||||
#define IC_CONF_PRPENC_ROT_EN 0x00000004
|
||||
#define IC_CONF_PRPVF_EN 0x00000100
|
||||
#define IC_CONF_PRPVF_CSC1 0x00000200
|
||||
#define IC_CONF_PRPVF_CSC2 0x00000400
|
||||
#define IC_CONF_PRPVF_CMB 0x00000800
|
||||
#define IC_CONF_PRPVF_ROT_EN 0x00001000
|
||||
#define IC_CONF_PP_EN 0x00010000
|
||||
#define IC_CONF_PP_CSC1 0x00020000
|
||||
#define IC_CONF_PP_CSC2 0x00040000
|
||||
#define IC_CONF_PP_CMB 0x00080000
|
||||
#define IC_CONF_PP_ROT_EN 0x00100000
|
||||
#define IC_CONF_IC_GLB_LOC_A 0x10000000
|
||||
#define IC_CONF_KEY_COLOR_EN 0x20000000
|
||||
#define IC_CONF_RWS_EN 0x40000000
|
||||
#define IC_CONF_CSI_MEM_WR_EN 0x80000000
|
||||
|
||||
#define IDMA_CHAN_INVALID 0x000000FF
|
||||
#define IDMA_IC_0 0x00000001
|
||||
#define IDMA_IC_1 0x00000002
|
||||
#define IDMA_IC_2 0x00000004
|
||||
#define IDMA_IC_3 0x00000008
|
||||
#define IDMA_IC_4 0x00000010
|
||||
#define IDMA_IC_5 0x00000020
|
||||
#define IDMA_IC_6 0x00000040
|
||||
#define IDMA_IC_7 0x00000080
|
||||
#define IDMA_IC_8 0x00000100
|
||||
#define IDMA_IC_9 0x00000200
|
||||
#define IDMA_IC_10 0x00000400
|
||||
#define IDMA_IC_11 0x00000800
|
||||
#define IDMA_IC_12 0x00001000
|
||||
#define IDMA_IC_13 0x00002000
|
||||
#define IDMA_SDC_BG 0x00004000
|
||||
#define IDMA_SDC_FG 0x00008000
|
||||
#define IDMA_SDC_MASK 0x00010000
|
||||
#define IDMA_SDC_PARTIAL 0x00020000
|
||||
#define IDMA_ADC_SYS1_WR 0x00040000
|
||||
#define IDMA_ADC_SYS2_WR 0x00080000
|
||||
#define IDMA_ADC_SYS1_CMD 0x00100000
|
||||
#define IDMA_ADC_SYS2_CMD 0x00200000
|
||||
#define IDMA_ADC_SYS1_RD 0x00400000
|
||||
#define IDMA_ADC_SYS2_RD 0x00800000
|
||||
#define IDMA_PF_QP 0x01000000
|
||||
#define IDMA_PF_BSP 0x02000000
|
||||
#define IDMA_PF_Y_IN 0x04000000
|
||||
#define IDMA_PF_U_IN 0x08000000
|
||||
#define IDMA_PF_V_IN 0x10000000
|
||||
#define IDMA_PF_Y_OUT 0x20000000
|
||||
#define IDMA_PF_U_OUT 0x40000000
|
||||
#define IDMA_PF_V_OUT 0x80000000
|
||||
|
||||
#define TSTAT_PF_H264_PAUSE 0x00000001
|
||||
#define TSTAT_CSI2MEM_MASK 0x0000000C
|
||||
#define TSTAT_CSI2MEM_OFFSET 2
|
||||
#define TSTAT_VF_MASK 0x00000600
|
||||
#define TSTAT_VF_OFFSET 9
|
||||
#define TSTAT_VF_ROT_MASK 0x000C0000
|
||||
#define TSTAT_VF_ROT_OFFSET 18
|
||||
#define TSTAT_ENC_MASK 0x00000180
|
||||
#define TSTAT_ENC_OFFSET 7
|
||||
#define TSTAT_ENC_ROT_MASK 0x00030000
|
||||
#define TSTAT_ENC_ROT_OFFSET 16
|
||||
#define TSTAT_PP_MASK 0x00001800
|
||||
#define TSTAT_PP_OFFSET 11
|
||||
#define TSTAT_PP_ROT_MASK 0x00300000
|
||||
#define TSTAT_PP_ROT_OFFSET 20
|
||||
#define TSTAT_PF_MASK 0x00C00000
|
||||
#define TSTAT_PF_OFFSET 22
|
||||
#define TSTAT_ADCSYS1_MASK 0x03000000
|
||||
#define TSTAT_ADCSYS1_OFFSET 24
|
||||
#define TSTAT_ADCSYS2_MASK 0x0C000000
|
||||
#define TSTAT_ADCSYS2_OFFSET 26
|
||||
|
||||
#define TASK_STAT_IDLE 0
|
||||
#define TASK_STAT_ACTIVE 1
|
||||
#define TASK_STAT_WAIT4READY 2
|
||||
|
||||
struct idmac {
|
||||
struct dma_device dma;
|
||||
};
|
||||
|
||||
struct ipu {
|
||||
void __iomem *reg_ipu;
|
||||
void __iomem *reg_ic;
|
||||
unsigned int irq_fn; /* IPU Function IRQ to the CPU */
|
||||
unsigned int irq_err; /* IPU Error IRQ to the CPU */
|
||||
unsigned int irq_base; /* Beginning of the IPU IRQ range */
|
||||
unsigned long channel_init_mask;
|
||||
spinlock_t lock;
|
||||
struct clk *ipu_clk;
|
||||
struct device *dev;
|
||||
struct idmac idmac;
|
||||
struct idmac_channel channel[IPU_CHANNELS_NUM];
|
||||
struct tasklet_struct tasklet;
|
||||
};
|
||||
|
||||
#define to_idmac(d) container_of(d, struct idmac, dma)
|
||||
|
||||
extern int ipu_irq_attach_irq(struct ipu *ipu, struct platform_device *dev);
|
||||
extern void ipu_irq_detach_irq(struct ipu *ipu, struct platform_device *dev);
|
||||
|
||||
extern bool ipu_irq_status(uint32_t irq);
|
||||
extern int ipu_irq_map(unsigned int source);
|
||||
extern int ipu_irq_unmap(unsigned int source);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,413 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <mach/ipu.h>
|
||||
|
||||
#include "ipu_intern.h"
|
||||
|
||||
/*
|
||||
* Register read / write - shall be inlined by the compiler
|
||||
*/
|
||||
static u32 ipu_read_reg(struct ipu *ipu, unsigned long reg)
|
||||
{
|
||||
return __raw_readl(ipu->reg_ipu + reg);
|
||||
}
|
||||
|
||||
static void ipu_write_reg(struct ipu *ipu, u32 value, unsigned long reg)
|
||||
{
|
||||
__raw_writel(value, ipu->reg_ipu + reg);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* IPU IRQ chip driver
|
||||
*/
|
||||
|
||||
#define IPU_IRQ_NR_FN_BANKS 3
|
||||
#define IPU_IRQ_NR_ERR_BANKS 2
|
||||
#define IPU_IRQ_NR_BANKS (IPU_IRQ_NR_FN_BANKS + IPU_IRQ_NR_ERR_BANKS)
|
||||
|
||||
struct ipu_irq_bank {
|
||||
unsigned int control;
|
||||
unsigned int status;
|
||||
spinlock_t lock;
|
||||
struct ipu *ipu;
|
||||
};
|
||||
|
||||
static struct ipu_irq_bank irq_bank[IPU_IRQ_NR_BANKS] = {
|
||||
/* 3 groups of functional interrupts */
|
||||
{
|
||||
.control = IPU_INT_CTRL_1,
|
||||
.status = IPU_INT_STAT_1,
|
||||
}, {
|
||||
.control = IPU_INT_CTRL_2,
|
||||
.status = IPU_INT_STAT_2,
|
||||
}, {
|
||||
.control = IPU_INT_CTRL_3,
|
||||
.status = IPU_INT_STAT_3,
|
||||
},
|
||||
/* 2 groups of error interrupts */
|
||||
{
|
||||
.control = IPU_INT_CTRL_4,
|
||||
.status = IPU_INT_STAT_4,
|
||||
}, {
|
||||
.control = IPU_INT_CTRL_5,
|
||||
.status = IPU_INT_STAT_5,
|
||||
},
|
||||
};
|
||||
|
||||
struct ipu_irq_map {
|
||||
unsigned int irq;
|
||||
int source;
|
||||
struct ipu_irq_bank *bank;
|
||||
struct ipu *ipu;
|
||||
};
|
||||
|
||||
static struct ipu_irq_map irq_map[CONFIG_MX3_IPU_IRQS];
|
||||
/* Protects allocations from the above array of maps */
|
||||
static DEFINE_MUTEX(map_lock);
|
||||
/* Protects register accesses and individual mappings */
|
||||
static DEFINE_SPINLOCK(bank_lock);
|
||||
|
||||
static struct ipu_irq_map *src2map(unsigned int src)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CONFIG_MX3_IPU_IRQS; i++)
|
||||
if (irq_map[i].source == src)
|
||||
return irq_map + i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void ipu_irq_unmask(unsigned int irq)
|
||||
{
|
||||
struct ipu_irq_map *map = get_irq_chip_data(irq);
|
||||
struct ipu_irq_bank *bank;
|
||||
uint32_t reg;
|
||||
unsigned long lock_flags;
|
||||
|
||||
spin_lock_irqsave(&bank_lock, lock_flags);
|
||||
|
||||
bank = map->bank;
|
||||
if (!bank) {
|
||||
spin_unlock_irqrestore(&bank_lock, lock_flags);
|
||||
pr_err("IPU: %s(%u) - unmapped!\n", __func__, irq);
|
||||
return;
|
||||
}
|
||||
|
||||
reg = ipu_read_reg(bank->ipu, bank->control);
|
||||
reg |= (1UL << (map->source & 31));
|
||||
ipu_write_reg(bank->ipu, reg, bank->control);
|
||||
|
||||
spin_unlock_irqrestore(&bank_lock, lock_flags);
|
||||
}
|
||||
|
||||
static void ipu_irq_mask(unsigned int irq)
|
||||
{
|
||||
struct ipu_irq_map *map = get_irq_chip_data(irq);
|
||||
struct ipu_irq_bank *bank;
|
||||
uint32_t reg;
|
||||
unsigned long lock_flags;
|
||||
|
||||
spin_lock_irqsave(&bank_lock, lock_flags);
|
||||
|
||||
bank = map->bank;
|
||||
if (!bank) {
|
||||
spin_unlock_irqrestore(&bank_lock, lock_flags);
|
||||
pr_err("IPU: %s(%u) - unmapped!\n", __func__, irq);
|
||||
return;
|
||||
}
|
||||
|
||||
reg = ipu_read_reg(bank->ipu, bank->control);
|
||||
reg &= ~(1UL << (map->source & 31));
|
||||
ipu_write_reg(bank->ipu, reg, bank->control);
|
||||
|
||||
spin_unlock_irqrestore(&bank_lock, lock_flags);
|
||||
}
|
||||
|
||||
static void ipu_irq_ack(unsigned int irq)
|
||||
{
|
||||
struct ipu_irq_map *map = get_irq_chip_data(irq);
|
||||
struct ipu_irq_bank *bank;
|
||||
unsigned long lock_flags;
|
||||
|
||||
spin_lock_irqsave(&bank_lock, lock_flags);
|
||||
|
||||
bank = map->bank;
|
||||
if (!bank) {
|
||||
spin_unlock_irqrestore(&bank_lock, lock_flags);
|
||||
pr_err("IPU: %s(%u) - unmapped!\n", __func__, irq);
|
||||
return;
|
||||
}
|
||||
|
||||
ipu_write_reg(bank->ipu, 1UL << (map->source & 31), bank->status);
|
||||
spin_unlock_irqrestore(&bank_lock, lock_flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* ipu_irq_status() - returns the current interrupt status of the specified IRQ.
|
||||
* @irq: interrupt line to get status for.
|
||||
* @return: true if the interrupt is pending/asserted or false if the
|
||||
* interrupt is not pending.
|
||||
*/
|
||||
bool ipu_irq_status(unsigned int irq)
|
||||
{
|
||||
struct ipu_irq_map *map = get_irq_chip_data(irq);
|
||||
struct ipu_irq_bank *bank;
|
||||
unsigned long lock_flags;
|
||||
bool ret;
|
||||
|
||||
spin_lock_irqsave(&bank_lock, lock_flags);
|
||||
bank = map->bank;
|
||||
ret = bank && ipu_read_reg(bank->ipu, bank->status) &
|
||||
(1UL << (map->source & 31));
|
||||
spin_unlock_irqrestore(&bank_lock, lock_flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipu_irq_map() - map an IPU interrupt source to an IRQ number
|
||||
* @source: interrupt source bit position (see below)
|
||||
* @return: mapped IRQ number or negative error code
|
||||
*
|
||||
* The source parameter has to be explained further. On i.MX31 IPU has 137 IRQ
|
||||
* sources, they are broken down in 5 32-bit registers, like 32, 32, 24, 32, 17.
|
||||
* However, the source argument of this function is not the sequence number of
|
||||
* the possible IRQ, but rather its bit position. So, first interrupt in fourth
|
||||
* register has source number 96, and not 88. This makes calculations easier,
|
||||
* and also provides forward compatibility with any future IPU implementations
|
||||
* with any interrupt bit assignments.
|
||||
*/
|
||||
int ipu_irq_map(unsigned int source)
|
||||
{
|
||||
int i, ret = -ENOMEM;
|
||||
struct ipu_irq_map *map;
|
||||
|
||||
might_sleep();
|
||||
|
||||
mutex_lock(&map_lock);
|
||||
map = src2map(source);
|
||||
if (map) {
|
||||
pr_err("IPU: Source %u already mapped to IRQ %u\n", source, map->irq);
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < CONFIG_MX3_IPU_IRQS; i++) {
|
||||
if (irq_map[i].source < 0) {
|
||||
unsigned long lock_flags;
|
||||
|
||||
spin_lock_irqsave(&bank_lock, lock_flags);
|
||||
irq_map[i].source = source;
|
||||
irq_map[i].bank = irq_bank + source / 32;
|
||||
spin_unlock_irqrestore(&bank_lock, lock_flags);
|
||||
|
||||
ret = irq_map[i].irq;
|
||||
pr_debug("IPU: mapped source %u to IRQ %u\n",
|
||||
source, ret);
|
||||
break;
|
||||
}
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&map_lock);
|
||||
|
||||
if (ret < 0)
|
||||
pr_err("IPU: couldn't map source %u: %d\n", source, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipu_irq_map() - map an IPU interrupt source to an IRQ number
|
||||
* @source: interrupt source bit position (see ipu_irq_map())
|
||||
* @return: 0 or negative error code
|
||||
*/
|
||||
int ipu_irq_unmap(unsigned int source)
|
||||
{
|
||||
int i, ret = -EINVAL;
|
||||
|
||||
might_sleep();
|
||||
|
||||
mutex_lock(&map_lock);
|
||||
for (i = 0; i < CONFIG_MX3_IPU_IRQS; i++) {
|
||||
if (irq_map[i].source == source) {
|
||||
unsigned long lock_flags;
|
||||
|
||||
pr_debug("IPU: unmapped source %u from IRQ %u\n",
|
||||
source, irq_map[i].irq);
|
||||
|
||||
spin_lock_irqsave(&bank_lock, lock_flags);
|
||||
irq_map[i].source = -EINVAL;
|
||||
irq_map[i].bank = NULL;
|
||||
spin_unlock_irqrestore(&bank_lock, lock_flags);
|
||||
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&map_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Chained IRQ handler for IPU error interrupt */
|
||||
static void ipu_irq_err(unsigned int irq, struct irq_desc *desc)
|
||||
{
|
||||
struct ipu *ipu = get_irq_data(irq);
|
||||
u32 status;
|
||||
int i, line;
|
||||
|
||||
for (i = IPU_IRQ_NR_FN_BANKS; i < IPU_IRQ_NR_BANKS; i++) {
|
||||
struct ipu_irq_bank *bank = irq_bank + i;
|
||||
|
||||
spin_lock(&bank_lock);
|
||||
status = ipu_read_reg(ipu, bank->status);
|
||||
/*
|
||||
* Don't think we have to clear all interrupts here, they will
|
||||
* be acked by ->handle_irq() (handle_level_irq). However, we
|
||||
* might want to clear unhandled interrupts after the loop...
|
||||
*/
|
||||
status &= ipu_read_reg(ipu, bank->control);
|
||||
spin_unlock(&bank_lock);
|
||||
while ((line = ffs(status))) {
|
||||
struct ipu_irq_map *map;
|
||||
|
||||
line--;
|
||||
status &= ~(1UL << line);
|
||||
|
||||
spin_lock(&bank_lock);
|
||||
map = src2map(32 * i + line);
|
||||
if (map)
|
||||
irq = map->irq;
|
||||
spin_unlock(&bank_lock);
|
||||
|
||||
if (!map) {
|
||||
pr_err("IPU: Interrupt on unmapped source %u bank %d\n",
|
||||
line, i);
|
||||
continue;
|
||||
}
|
||||
generic_handle_irq(irq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Chained IRQ handler for IPU function interrupt */
|
||||
static void ipu_irq_fn(unsigned int irq, struct irq_desc *desc)
|
||||
{
|
||||
struct ipu *ipu = get_irq_data(irq);
|
||||
u32 status;
|
||||
int i, line;
|
||||
|
||||
for (i = 0; i < IPU_IRQ_NR_FN_BANKS; i++) {
|
||||
struct ipu_irq_bank *bank = irq_bank + i;
|
||||
|
||||
spin_lock(&bank_lock);
|
||||
status = ipu_read_reg(ipu, bank->status);
|
||||
/* Not clearing all interrupts, see above */
|
||||
status &= ipu_read_reg(ipu, bank->control);
|
||||
spin_unlock(&bank_lock);
|
||||
while ((line = ffs(status))) {
|
||||
struct ipu_irq_map *map;
|
||||
|
||||
line--;
|
||||
status &= ~(1UL << line);
|
||||
|
||||
spin_lock(&bank_lock);
|
||||
map = src2map(32 * i + line);
|
||||
if (map)
|
||||
irq = map->irq;
|
||||
spin_unlock(&bank_lock);
|
||||
|
||||
if (!map) {
|
||||
pr_err("IPU: Interrupt on unmapped source %u bank %d\n",
|
||||
line, i);
|
||||
continue;
|
||||
}
|
||||
generic_handle_irq(irq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct irq_chip ipu_irq_chip = {
|
||||
.name = "ipu_irq",
|
||||
.ack = ipu_irq_ack,
|
||||
.mask = ipu_irq_mask,
|
||||
.unmask = ipu_irq_unmask,
|
||||
};
|
||||
|
||||
/* Install the IRQ handler */
|
||||
int ipu_irq_attach_irq(struct ipu *ipu, struct platform_device *dev)
|
||||
{
|
||||
struct ipu_platform_data *pdata = dev->dev.platform_data;
|
||||
unsigned int irq, irq_base, i;
|
||||
|
||||
irq_base = pdata->irq_base;
|
||||
|
||||
for (i = 0; i < IPU_IRQ_NR_BANKS; i++)
|
||||
irq_bank[i].ipu = ipu;
|
||||
|
||||
for (i = 0; i < CONFIG_MX3_IPU_IRQS; i++) {
|
||||
int ret;
|
||||
|
||||
irq = irq_base + i;
|
||||
ret = set_irq_chip(irq, &ipu_irq_chip);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = set_irq_chip_data(irq, irq_map + i);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
irq_map[i].ipu = ipu;
|
||||
irq_map[i].irq = irq;
|
||||
irq_map[i].source = -EINVAL;
|
||||
set_irq_handler(irq, handle_level_irq);
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
|
||||
#endif
|
||||
}
|
||||
|
||||
set_irq_data(ipu->irq_fn, ipu);
|
||||
set_irq_chained_handler(ipu->irq_fn, ipu_irq_fn);
|
||||
|
||||
set_irq_data(ipu->irq_err, ipu);
|
||||
set_irq_chained_handler(ipu->irq_err, ipu_irq_err);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ipu_irq_detach_irq(struct ipu *ipu, struct platform_device *dev)
|
||||
{
|
||||
struct ipu_platform_data *pdata = dev->dev.platform_data;
|
||||
unsigned int irq, irq_base;
|
||||
|
||||
irq_base = pdata->irq_base;
|
||||
|
||||
set_irq_chained_handler(ipu->irq_fn, NULL);
|
||||
set_irq_data(ipu->irq_fn, NULL);
|
||||
|
||||
set_irq_chained_handler(ipu->irq_err, NULL);
|
||||
set_irq_data(ipu->irq_err, NULL);
|
||||
|
||||
for (irq = irq_base; irq < irq_base + CONFIG_MX3_IPU_IRQS; irq++) {
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(irq, 0);
|
||||
#endif
|
||||
set_irq_chip(irq, NULL);
|
||||
set_irq_chip_data(irq, NULL);
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче