[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