[PATCH 9/133] [Jaunty SRU] ARM.imx51 Freescale:ENGR00107699 ipu: support new ipu device lib
Brad Figg
brad.figg at canonical.com
Thu Jul 9 16:47:59 UTC 2009
1.Add ipu_is_channel_busy functions to support
new ipu device lib.
2.Ipu device driver modification to support.
change the irq get event method to make the
multi ipu device operations work.
3.Add suspend/resume for the case that entering
power gating mode while some ipu channel are
running.
NOTE: there still is a bug for ipu suspend/resume
function, ipu may not resume correctly while ipu
were using double buffer, cause we can not set ipu
current buffer register.
Ubuntu Note: This is a partial application of the original patch. We do
not have drivers/mxc/ipu/ipu_common.c nor drivers/mxc/ipu/ipu_device.c
in our tree.
Signed-off-by: Jason Chen <b02280 at freescale.com>
Signed-off-by: Brad Figg <brad.figg at canonical.com>
---
drivers/mxc/ipu3/ipu_common.c | 167 ++++++++++++++++++++++++++++++++++++++---
drivers/mxc/ipu3/ipu_device.c | 97 ++++++++++++++++++------
include/linux/ipu.h | 6 +-
3 files changed, 235 insertions(+), 35 deletions(-)
diff --git a/drivers/mxc/ipu3/ipu_common.c b/drivers/mxc/ipu3/ipu_common.c
index 5dc278f..704fb54 100644
--- a/drivers/mxc/ipu3/ipu_common.c
+++ b/drivers/mxc/ipu3/ipu_common.c
@@ -51,6 +51,7 @@ int g_ipu_irq[2];
int g_ipu_hw_rev;
bool g_sec_chan_en[21];
uint32_t g_channel_init_mask;
+uint32_t g_channel_enable_mask;
DEFINE_SPINLOCK(ipu_lock);
struct device *g_ipu_dev;
@@ -66,6 +67,14 @@ static int ipu_rot_use_count;
static int ipu_di_use_count[2];
static int ipu_csi_use_count[2];
+/* for power gating */
+static uint32_t ipu_conf_reg;
+static uint32_t ic_conf_reg;
+static uint32_t ipu_cha_db_mode_reg[4];
+static uint32_t ipu_cha_cur_buf_reg[4];
+static uint32_t idma_enable_reg[2];
+static uint32_t buf_ready_reg[8];
+
u32 *ipu_cm_reg;
u32 *ipu_idmac_reg;
u32 *ipu_dp_reg;
@@ -1275,6 +1284,33 @@ err:
EXPORT_SYMBOL(ipu_unlink_channels);
/*!
+ * This function check whether a logical channel was enabled.
+ *
+ * @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(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 = __raw_readl(IDMAC_CHA_EN(in_dma));
+ if (reg & idma_mask(in_dma))
+ return 1;
+ reg = __raw_readl(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 channel Input parameter for the logical channel ID.
@@ -1289,6 +1325,11 @@ int32_t ipu_enable_channel(ipu_channel_t channel)
uint32_t in_dma;
uint32_t out_dma;
+ if (g_channel_enable_mask & (1L << IPU_CHAN_ID(channel))) {
+ dev_err(g_ipu_dev, "Warning: channel already enabled %d\n",
+ IPU_CHAN_ID(channel));
+ }
+
/* 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);
@@ -1312,7 +1353,10 @@ int32_t ipu_enable_channel(ipu_channel_t channel)
_ipu_is_irt_chan(in_dma) || _ipu_is_irt_chan(out_dma))
_ipu_ic_enable_task(channel);
+ g_channel_enable_mask |= 1L << IPU_CHAN_ID(channel);
+
spin_unlock_irqrestore(&ipu_lock, lock_flags);
+
return 0;
}
EXPORT_SYMBOL(ipu_enable_channel);
@@ -1336,13 +1380,20 @@ int32_t ipu_disable_channel(ipu_channel_t channel, bool wait_for_stop)
uint32_t out_dma;
uint32_t timeout;
+ if ((g_channel_enable_mask & (1L << IPU_CHAN_ID(channel))) == 0) {
+ dev_err(g_ipu_dev, "Channel already disabled %d\n",
+ IPU_CHAN_ID(channel));
+ return 0;
+ }
+
/* 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(IDMAC_CHA_EN, in_dma))
- return -EINVAL;
- if (idma_is_valid(out_dma) && !idma_is_set(IDMAC_CHA_EN, out_dma))
+ if ((idma_is_valid(in_dma) &&
+ !idma_is_set(IDMAC_CHA_EN, in_dma))
+ && (idma_is_valid(out_dma) &&
+ !idma_is_set(IDMAC_CHA_EN, out_dma)))
return -EINVAL;
if ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC) ||
@@ -1406,6 +1457,8 @@ int32_t ipu_disable_channel(ipu_channel_t channel, bool wait_for_stop)
}
__raw_writel(0x0, IPU_GPR); /* write one to set */
+ g_channel_enable_mask &= ~(1L << IPU_CHAN_ID(channel));
+
spin_unlock_irqrestore(&ipu_lock, lock_flags);
return 0;
@@ -1740,7 +1793,67 @@ EXPORT_SYMBOL(ipu_set_csc_coefficients);
static int ipu_suspend(struct platform_device *pdev, pm_message_t state)
{
+ if (g_ipu_clk_enabled) {
+ /* save and disable enabled channels*/
+ idma_enable_reg[0] = __raw_readl(IDMAC_CHA_EN(0));
+ idma_enable_reg[1] = __raw_readl(IDMAC_CHA_EN(32));
+ while ((__raw_readl(IDMAC_CHA_BUSY(0)) & idma_enable_reg[0])
+ || (__raw_readl(IDMAC_CHA_BUSY(32)) &
+ idma_enable_reg[1])) {
+ /* disable channel not busy already */
+ uint32_t chan_should_disable, timeout = 1000, time = 0;
+
+ chan_should_disable =
+ __raw_readl(IDMAC_CHA_BUSY(0))
+ ^ idma_enable_reg[0];
+ __raw_writel((~chan_should_disable) &
+ idma_enable_reg[0], IDMAC_CHA_EN(0));
+ chan_should_disable =
+ __raw_readl(IDMAC_CHA_BUSY(1))
+ ^ idma_enable_reg[1];
+ __raw_writel((~chan_should_disable) &
+ idma_enable_reg[1], IDMAC_CHA_EN(32));
+ msleep(2);
+ time += 2;
+ if (time >= timeout)
+ return -1;
+ }
+ __raw_writel(0, IDMAC_CHA_EN(0));
+ __raw_writel(0, IDMAC_CHA_EN(32));
+
+ /* save double buffer select regs */
+ ipu_cha_db_mode_reg[0] = __raw_readl(IPU_CHA_DB_MODE_SEL(0));
+ ipu_cha_db_mode_reg[1] = __raw_readl(IPU_CHA_DB_MODE_SEL(32));
+ ipu_cha_db_mode_reg[2] =
+ __raw_readl(IPU_ALT_CHA_DB_MODE_SEL(0));
+ ipu_cha_db_mode_reg[3] =
+ __raw_readl(IPU_ALT_CHA_DB_MODE_SEL(32));
+
+ /* save current buffer regs */
+ ipu_cha_cur_buf_reg[0] = __raw_readl(IPU_CHA_CUR_BUF(0));
+ ipu_cha_cur_buf_reg[1] = __raw_readl(IPU_CHA_CUR_BUF(32));
+ ipu_cha_cur_buf_reg[2] = __raw_readl(IPU_ALT_CUR_BUF0);
+ ipu_cha_cur_buf_reg[3] = __raw_readl(IPU_ALT_CUR_BUF1);
+
+ /* save sub-modules status and disable all */
+ ic_conf_reg = __raw_readl(IC_CONF);
+ __raw_writel(0, IC_CONF);
+ ipu_conf_reg = __raw_readl(IPU_CONF);
+ __raw_writel(0, IPU_CONF);
+
+ /* save buf ready regs */
+ buf_ready_reg[0] = __raw_readl(IPU_CHA_BUF0_RDY(0));
+ buf_ready_reg[1] = __raw_readl(IPU_CHA_BUF0_RDY(32));
+ buf_ready_reg[2] = __raw_readl(IPU_CHA_BUF1_RDY(0));
+ buf_ready_reg[3] = __raw_readl(IPU_CHA_BUF1_RDY(32));
+ buf_ready_reg[4] = __raw_readl(IPU_ALT_CHA_BUF0_RDY(0));
+ buf_ready_reg[5] = __raw_readl(IPU_ALT_CHA_BUF0_RDY(32));
+ buf_ready_reg[6] = __raw_readl(IPU_ALT_CHA_BUF1_RDY(0));
+ buf_ready_reg[7] = __raw_readl(IPU_ALT_CHA_BUF1_RDY(32));
+ }
+
mxc_pg_enable(pdev);
+
return 0;
}
@@ -1748,15 +1861,49 @@ static int ipu_resume(struct platform_device *pdev)
{
mxc_pg_disable(pdev);
- clk_enable(g_ipu_clk);
-
- _ipu_dmfc_init();
- _ipu_init_dc_mappings();
+ if (g_ipu_clk_enabled) {
+
+ /* restore buf ready regs */
+ __raw_writel(buf_ready_reg[0], IPU_CHA_BUF0_RDY(0));
+ __raw_writel(buf_ready_reg[1], IPU_CHA_BUF0_RDY(32));
+ __raw_writel(buf_ready_reg[2], IPU_CHA_BUF1_RDY(0));
+ __raw_writel(buf_ready_reg[3], IPU_CHA_BUF1_RDY(32));
+ __raw_writel(buf_ready_reg[4], IPU_ALT_CHA_BUF0_RDY(0));
+ __raw_writel(buf_ready_reg[5], IPU_ALT_CHA_BUF0_RDY(32));
+ __raw_writel(buf_ready_reg[6], IPU_ALT_CHA_BUF1_RDY(0));
+ __raw_writel(buf_ready_reg[7], IPU_ALT_CHA_BUF1_RDY(32));
+
+ /* re-enable sub-modules*/
+ __raw_writel(ipu_conf_reg, IPU_CONF);
+ __raw_writel(ic_conf_reg, IC_CONF);
+
+ /* restore double buffer select regs */
+ __raw_writel(ipu_cha_db_mode_reg[0], IPU_CHA_DB_MODE_SEL(0));
+ __raw_writel(ipu_cha_db_mode_reg[1], IPU_CHA_DB_MODE_SEL(32));
+ __raw_writel(ipu_cha_db_mode_reg[2],
+ IPU_ALT_CHA_DB_MODE_SEL(0));
+ __raw_writel(ipu_cha_db_mode_reg[3],
+ IPU_ALT_CHA_DB_MODE_SEL(32));
+
+ /* restore current buffer select regs */
+ __raw_writel(~(ipu_cha_cur_buf_reg[0]), IPU_CHA_CUR_BUF(0));
+ __raw_writel(~(ipu_cha_cur_buf_reg[1]), IPU_CHA_CUR_BUF(32));
+ __raw_writel(~(ipu_cha_cur_buf_reg[2]), IPU_ALT_CUR_BUF0);
+ __raw_writel(~(ipu_cha_cur_buf_reg[3]), IPU_ALT_CUR_BUF1);
+
+ /* restart idma channel*/
+ __raw_writel(idma_enable_reg[0], IDMAC_CHA_EN(0));
+ __raw_writel(idma_enable_reg[1], IDMAC_CHA_EN(32));
+ } else {
+ clk_enable(g_ipu_clk);
+ _ipu_dmfc_init();
+ _ipu_init_dc_mappings();
- /* Set sync refresh channels as high priority */
- __raw_writel(0x18800000L, IDMAC_CHA_PRI(0));
+ /* Set sync refresh channels as high priority */
+ __raw_writel(0x18800000L, IDMAC_CHA_PRI(0));
+ clk_disable(g_ipu_clk);
+ }
- clk_disable(g_ipu_clk);
return 0;
}
diff --git a/drivers/mxc/ipu3/ipu_device.c b/drivers/mxc/ipu3/ipu_device.c
index 1105735..c81c82b 100644
--- a/drivers/mxc/ipu3/ipu_device.c
+++ b/drivers/mxc/ipu3/ipu_device.c
@@ -54,39 +54,63 @@ static int pending_events;
int read_ptr;
int write_ptr;
-typedef struct _event_type {
- int irq;
- void *dev;
-} event_type;
-
-event_type events[MAX_Q_SIZE];
+ipu_event_info events[MAX_Q_SIZE];
int register_ipu_device(void);
/* Static functions */
-int get_events(event_type *p)
+int get_events(ipu_event_info *p)
{
unsigned long flags;
- int ret = 0;
+ int ret = 0, i, cnt, found = 0;
+
spin_lock_irqsave(&queue_lock, flags);
if (pending_events != 0) {
- *p = events[read_ptr];
- read_ptr++;
- pending_events--;
- if (read_ptr >= MAX_Q_SIZE)
- read_ptr = 0;
+ if (write_ptr > read_ptr)
+ cnt = write_ptr - read_ptr;
+ else
+ cnt = MAX_Q_SIZE - read_ptr + write_ptr;
+ for (i = 0; i < cnt; i++) {
+ if (p->irq == events[read_ptr].irq) {
+ *p = events[read_ptr];
+ events[read_ptr].irq = 0;
+ read_ptr++;
+ if (read_ptr >= MAX_Q_SIZE)
+ read_ptr = 0;
+ found = 1;
+ break;
+ }
+
+ if (events[read_ptr].irq) {
+ events[write_ptr] = events[read_ptr];
+ events[read_ptr].irq = 0;
+ write_ptr++;
+ if (write_ptr >= MAX_Q_SIZE)
+ write_ptr = 0;
+ } else
+ pending_events--;
+
+ read_ptr++;
+ if (read_ptr >= MAX_Q_SIZE)
+ read_ptr = 0;
+ }
+ if (found)
+ pending_events--;
+ else
+ ret = -1;
} else {
ret = -1;
}
spin_unlock_irqrestore(&queue_lock, flags);
+
return ret;
}
static irqreturn_t mxc_ipu_generic_handler(int irq, void *dev_id)
{
- event_type e;
+ ipu_event_info e;
e.irq = irq;
e.dev = dev_id;
@@ -95,6 +119,7 @@ static irqreturn_t mxc_ipu_generic_handler(int irq, void *dev_id)
if (write_ptr >= MAX_Q_SIZE)
write_ptr = 0;
pending_events++;
+
/* Wakeup any blocking user context */
wake_up_interruptible(&waitq);
return IRQ_HANDLED;
@@ -281,12 +306,18 @@ static int mxc_ipu_ioctl(struct inode *inode, struct file *file,
case IPU_FREE_IRQ:
{
ipu_irq_info info;
+ int i;
+
if (copy_from_user
(&info, (ipu_irq_info *) arg,
sizeof(ipu_irq_info)))
return -EFAULT;
ipu_free_irq(info.irq, info.dev_id);
+ for (i = 0; i < MAX_Q_SIZE; i++) {
+ if (events[i].irq == info.irq)
+ events[i].irq = 0;
+ }
}
break;
case IPU_REQUEST_IRQ_STATUS:
@@ -316,18 +347,24 @@ static int mxc_ipu_ioctl(struct inode *inode, struct file *file,
/* User will have to allocate event_type
structure and pass the pointer in arg */
{
- event_type ev;
+ ipu_event_info info;
int r = -1;
- r = get_events(&ev);
+
+ if (copy_from_user
+ (&info, (ipu_event_info *) arg,
+ sizeof(ipu_event_info)))
+ return -EFAULT;
+
+ r = get_events(&info);
if (r == -1) {
- wait_event_interruptible(waitq,
- (pending_events != 0));
- r = get_events(&ev);
+ wait_event_interruptible_timeout(waitq,
+ (pending_events != 0), 2 * HZ);
+ r = get_events(&info);
}
ret = -1;
if (r == 0) {
- if (!copy_to_user((event_type *) arg,
- &ev, sizeof(event_type)))
+ if (!copy_to_user((ipu_event_info *) arg,
+ &info, sizeof(ipu_event_info)))
ret = 0;
}
}
@@ -361,13 +398,27 @@ static int mxc_ipu_ioctl(struct inode *inode, struct file *file,
sizeof(ipu_mem_info)))
return -EFAULT;
- if (info.vaddr != 0)
+ if (info.vaddr)
dma_free_coherent(0, PAGE_ALIGN(info.size),
- info.vaddr, info.paddr);
+ info.vaddr, info.paddr);
else
return -EFAULT;
}
break;
+ case IPU_IS_CHAN_BUSY:
+ {
+ ipu_channel_t chan;
+ if (copy_from_user
+ (&chan, (ipu_channel_t *)arg,
+ sizeof(ipu_channel_t)))
+ return -EFAULT;
+
+ if (ipu_is_channel_busy(chan))
+ ret = 1;
+ else
+ ret = 0;
+ }
+ break;
default:
break;
}
diff --git a/include/linux/ipu.h b/include/linux/ipu.h
index 34f5fcd..81002a9 100644
--- a/include/linux/ipu.h
+++ b/include/linux/ipu.h
@@ -850,6 +850,7 @@ int32_t ipu_select_buffer(ipu_channel_t channel,
int32_t ipu_link_channels(ipu_channel_t src_ch, ipu_channel_t dest_ch);
int32_t ipu_unlink_channels(ipu_channel_t src_ch, ipu_channel_t dest_ch);
+int32_t ipu_is_channel_busy(ipu_channel_t channel);
int32_t ipu_enable_channel(ipu_channel_t channel);
int32_t ipu_disable_channel(ipu_channel_t channel, bool wait_for_stop);
@@ -1156,9 +1157,10 @@ typedef struct _ipu_mem_info {
#define IPU_CSI_SET_WIN_SIZE _IOW('I',0x1F,ipu_csi_window_size)
#define IPU_CSI_SET_WINDOW _IOW('I',0x20,ipu_csi_window)
#define IPU_PF_SET_PAUSE_ROW _IOW('I',0x21, uint32_t)
-#define IPU_REGISTER_GENERIC_ISR _IOW('I',0x22,ipu_event_info)
-#define IPU_GET_EVENT _IOR('I',0x23,ipu_event_info)
+#define IPU_REGISTER_GENERIC_ISR _IOW('I', 0x22, ipu_event_info)
+#define IPU_GET_EVENT _IOWR('I', 0x23, ipu_event_info)
#define IPU_ALOC_MEM _IOWR('I', 0x24, ipu_mem_info)
#define IPU_FREE_MEM _IOW('I', 0x25, ipu_mem_info)
+#define IPU_IS_CHAN_BUSY _IOW('I', 0x26, ipu_channel_t)
#endif
--
1.6.0.4
More information about the kernel-team
mailing list