All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2] ARM: i.mx: mx3fb: add overlay support
@ 2012-04-22 13:31 ` Alex Gershgorin
  0 siblings, 0 replies; 2+ messages in thread
From: Alex Gershgorin @ 2012-04-22 13:31 UTC (permalink / raw)
  To: Florian Tobias Schandinat
  Cc: g.liakhovetski, s.hauer, laurent.pinchart, linux-fbdev,
	linux-media, Alex Gershgorin

This work is based on some earlier patch series
"i.MX31: dmaengine and framebuffer drivers" from 2008
by Guennadi Liakhovetski, the patch initializes overlay channel,
adds ioctl for configuring transparency of the overlay and graphics
planes, CONFIG_FB_MX3_OVERLAY is also supported.

In case that CONFIG_FB_MX3_OVERLAY is not defined, mx3fb is completely
backward compatible.

Blend mode, only global alpha blending has been tested.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Alex Gershgorin <alexg@meprolight.com>
---
Applies to v3.4-rc4

Changes since v1:
  *Some fixes after review
  *Added ioctl for setting overlay windows position
---
 drivers/video/Kconfig |    7 +
 drivers/video/mx3fb.c |  346 ++++++++++++++++++++++++++++++++++++++++++++----
 include/linux/mxcfb.h |   40 ++++++
 3 files changed, 364 insertions(+), 29 deletions(-)
 create mode 100644 include/linux/mxcfb.h

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index a290be5..acbfccc 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2368,6 +2368,13 @@ config FB_MX3
 	  far only synchronous displays are supported. If you plan to use
 	  an LCD display with your i.MX31 system, say Y here.
 
+config FB_MX3_OVERLAY
+	bool "MX3 Overlay support"
+	default n
+	depends on FB_MX3
+	---help---
+	  Say Y here to enable overlay support
+
 config FB_BROADSHEET
 	tristate "E-Ink Broadsheet/Epson S1D13521 controller support"
 	depends on FB
diff --git a/drivers/video/mx3fb.c b/drivers/video/mx3fb.c
index eec0d7b..09d7885 100644
--- a/drivers/video/mx3fb.c
+++ b/drivers/video/mx3fb.c
@@ -26,6 +26,7 @@
 #include <linux/console.h>
 #include <linux/clk.h>
 #include <linux/mutex.h>
+#include <linux/mxcfb.h>
 
 #include <mach/dma.h>
 #include <mach/hardware.h>
@@ -238,6 +239,7 @@ static const struct fb_videomode mx3fb_modedb[] = {
 
 struct mx3fb_data {
 	struct fb_info		*fbi;
+	struct fb_info		*fbi_ovl;
 	int			backlight_level;
 	void __iomem		*reg_base;
 	spinlock_t		lock;
@@ -246,6 +248,9 @@ struct mx3fb_data {
 	uint32_t		h_start_width;
 	uint32_t		v_start_width;
 	enum disp_data_mapping	disp_data_fmt;
+
+	/* IDMAC / dmaengine interface */
+	struct idmac_channel	*idmac_channel[2];	/* We need 2 channels */
 };
 
 struct dma_chan_request {
@@ -268,10 +273,19 @@ struct mx3fb_info {
 	struct dma_async_tx_descriptor	*txd;
 	dma_cookie_t			cookie;
 	struct scatterlist		sg[2];
+	struct list_head		ovl_list; /* overlay buffer list */
 
 	u32				sync;	/* preserve var->sync flags */
 };
 
+/* Allocated overlay buffer */
+struct mx3fb_alloc_list {
+	struct list_head	list;
+	dma_addr_t		phy_addr;
+	void			*cpu_addr;
+	size_t			size;
+};
+
 static void mx3fb_dma_done(void *);
 
 /* Used fb-mode and bpp. Can be set on kernel command line, therefore file-static. */
@@ -303,7 +317,13 @@ static void sdc_fb_init(struct mx3fb_info *fbi)
 	struct mx3fb_data *mx3fb = fbi->mx3fb;
 	uint32_t reg;
 
-	reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF);
+	reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF) & ~SDC_COM_GWSEL;
+
+	/* Also enable foreground for overlay graphic window is foreground */
+	if (mx3fb->fbi_ovl && fbi == mx3fb->fbi_ovl->par) {
+		reg |= SDC_COM_FG_EN | SDC_COM_GWSEL;
+		INIT_LIST_HEAD(&fbi->ovl_list);
+	}
 
 	mx3fb_write_reg(mx3fb, reg | SDC_COM_BG_EN, SDC_COM_CONF);
 }
@@ -312,13 +332,24 @@ static void sdc_fb_init(struct mx3fb_info *fbi)
 static uint32_t sdc_fb_uninit(struct mx3fb_info *fbi)
 {
 	struct mx3fb_data *mx3fb = fbi->mx3fb;
-	uint32_t reg;
+	uint32_t reg, chan_mask;
 
 	reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF);
 
-	mx3fb_write_reg(mx3fb, reg & ~SDC_COM_BG_EN, SDC_COM_CONF);
+	/*
+	 * Don't we have to automatically disable overlay when disabling
+	 * background? Attention: cannot test mx3fb->fbi_ovl->par, must
+	 * test mx3fb->fbi->par, because at the time this function is
+	 * called for the first time fbi_ovl is not assigned yet.
+	 */
+	if (fbi == mx3fb->fbi->par)
+		chan_mask = SDC_COM_BG_EN;
+	else
+		chan_mask = SDC_COM_FG_EN | SDC_COM_GWSEL;
+
+	mx3fb_write_reg(mx3fb, reg & ~chan_mask, SDC_COM_CONF);
 
-	return reg & SDC_COM_BG_EN;
+	return reg & chan_mask;
 }
 
 static void sdc_enable_channel(struct mx3fb_info *mx3_fbi)
@@ -412,13 +443,33 @@ static void sdc_disable_channel(struct mx3fb_info *mx3_fbi)
 static int sdc_set_window_pos(struct mx3fb_data *mx3fb, enum ipu_channel channel,
 			      int16_t x_pos, int16_t y_pos)
 {
-	if (channel != IDMAC_SDC_0)
-		return -EINVAL;
-
 	x_pos += mx3fb->h_start_width;
 	y_pos += mx3fb->v_start_width;
 
-	mx3fb_write_reg(mx3fb, (x_pos << 16) | y_pos, SDC_BG_POS);
+/*
+   If the overlay is defined, we just need to swap
+   the window position register relative to DMAC channel ID
+*/
+	switch (channel) {
+#ifndef CONFIG_FB_MX3_OVERLAY
+	case IDMAC_SDC_0:
+		mx3fb_write_reg(mx3fb, (x_pos << 16) | y_pos, SDC_BG_POS);
+		break;
+	case IDMAC_SDC_1:
+		mx3fb_write_reg(mx3fb, (x_pos << 16) | y_pos, SDC_FG_POS);
+		break;
+#else
+	case IDMAC_SDC_0:
+		mx3fb_write_reg(mx3fb, (x_pos << 16) | y_pos, SDC_FG_POS);
+		break;
+	case IDMAC_SDC_1:
+		mx3fb_write_reg(mx3fb, (x_pos << 16) | y_pos, SDC_BG_POS);
+		break;
+#endif
+	default:
+		return -EINVAL;
+	}
+
 	return 0;
 }
 
@@ -482,14 +533,17 @@ static int sdc_init_panel(struct mx3fb_data *mx3fb, enum ipu_panel panel,
 	mx3fb->h_start_width = h_start_width;
 	mx3fb->v_start_width = v_start_width;
 
+	reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF);
+
 	switch (panel) {
 	case IPU_PANEL_SHARP_TFT:
 		mx3fb_write_reg(mx3fb, 0x00FD0102L, SDC_SHARP_CONF_1);
 		mx3fb_write_reg(mx3fb, 0x00F500F4L, SDC_SHARP_CONF_2);
-		mx3fb_write_reg(mx3fb, SDC_COM_SHARP | SDC_COM_TFT_COLOR, SDC_COM_CONF);
+		mx3fb_write_reg(mx3fb, reg | SDC_COM_SHARP |
+				SDC_COM_TFT_COLOR, SDC_COM_CONF);
 		break;
 	case IPU_PANEL_TFT:
-		mx3fb_write_reg(mx3fb, SDC_COM_TFT_COLOR, SDC_COM_CONF);
+		mx3fb_write_reg(mx3fb, reg | SDC_COM_TFT_COLOR, SDC_COM_CONF);
 		break;
 	default:
 		return -EINVAL;
@@ -563,13 +617,12 @@ static int sdc_init_panel(struct mx3fb_data *mx3fb, enum ipu_panel panel,
 /**
  * sdc_set_color_key() - set the transparent color key for SDC graphic plane.
  * @mx3fb:	mx3fb context.
- * @channel:	IPU DMAC channel ID.
  * @enable:	boolean to enable or disable color keyl.
  * @color_key:	24-bit RGB color to use as transparent color key.
  * @return:	0 on success or negative error code on failure.
  */
-static int sdc_set_color_key(struct mx3fb_data *mx3fb, enum ipu_channel channel,
-			     bool enable, uint32_t color_key)
+static int sdc_set_color_key(struct mx3fb_data *mx3fb, bool enable,
+				uint32_t color_key)
 {
 	uint32_t reg, sdc_conf;
 	unsigned long lock_flags;
@@ -577,10 +630,6 @@ static int sdc_set_color_key(struct mx3fb_data *mx3fb, enum ipu_channel channel,
 	spin_lock_irqsave(&mx3fb->lock, lock_flags);
 
 	sdc_conf = mx3fb_read_reg(mx3fb, SDC_COM_CONF);
-	if (channel == IDMAC_SDC_0)
-		sdc_conf &= ~SDC_COM_GWSEL;
-	else
-		sdc_conf |= SDC_COM_GWSEL;
 
 	if (enable) {
 		reg = mx3fb_read_reg(mx3fb, SDC_GW_CTRL) & 0xFF000000L;
@@ -668,8 +717,16 @@ static int mx3fb_set_fix(struct fb_info *fbi)
 {
 	struct fb_fix_screeninfo *fix = &fbi->fix;
 	struct fb_var_screeninfo *var = &fbi->var;
+	struct mx3fb_info *mx3_fbi = fbi->par;
 
+#ifndef CONFIG_FB_MX3_OVERLAY
 	strncpy(fix->id, "DISP3 BG", 8);
+#else
+	if (mx3_fbi->ipu_ch == IDMAC_SDC_1)
+		strncpy(fix->id, "DISP3 BG", 8);
+	else
+		strncpy(fix->id, "DISP3 FG", 8);
+#endif
 
 	fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
 
@@ -689,13 +746,25 @@ static void mx3fb_dma_done(void *arg)
 	struct idmac_channel *ichannel = to_idmac_chan(chan);
 	struct mx3fb_data *mx3fb = ichannel->client;
 	struct mx3fb_info *mx3_fbi = mx3fb->fbi->par;
+	struct mx3fb_info *mx3_fbi_cur;
+	struct mx3fb_info *mx3_fbi_ovl = mx3fb->fbi_ovl ? mx3fb->fbi_ovl->par :
+		NULL;
 
 	dev_dbg(mx3fb->dev, "irq %d callback\n", ichannel->eof_irq);
 
+	if (ichannel == mx3_fbi->idmac_channel) {
+		mx3_fbi_cur = mx3_fbi;
+	} else if (mx3_fbi_ovl && ichannel == mx3_fbi_ovl->idmac_channel) {
+		mx3_fbi_cur = mx3_fbi_ovl;
+	} else {
+		WARN(1, "Cannot identify channel!\n");
+		return;
+	}
+
 	/* We only need one interrupt, it will be re-enabled as needed */
 	disable_irq_nosync(ichannel->eof_irq);
 
-	complete(&mx3_fbi->flip_cmpl);
+	complete(&mx3_fbi_cur->flip_cmpl);
 }
 
 static int __set_par(struct fb_info *fbi, bool lock)
@@ -1151,6 +1220,157 @@ static struct fb_ops mx3fb_ops = {
 	.fb_blank = mx3fb_blank,
 };
 
+#ifdef CONFIG_FB_MX3_OVERLAY
+static int mx3fb_blank_ovl(int blank, struct fb_info *fbi)
+{
+	struct mx3fb_info *mx3_fbi = fbi->par;
+
+	dev_dbg(fbi->device, "ovl blank = %d\n", blank);
+
+	if (mx3_fbi->blank == blank)
+		return 0;
+
+	mutex_lock(&mx3_fbi->mutex);
+	mx3_fbi->blank = blank;
+
+	switch (blank) {
+	case FB_BLANK_POWERDOWN:
+	case FB_BLANK_VSYNC_SUSPEND:
+	case FB_BLANK_HSYNC_SUSPEND:
+	case FB_BLANK_NORMAL:
+		sdc_disable_channel(mx3_fbi);
+		break;
+	case FB_BLANK_UNBLANK:
+		sdc_enable_channel(mx3_fbi);
+		break;
+	}
+	mutex_unlock(&mx3_fbi->mutex);
+
+	return 0;
+}
+
+/*
+ * Function to handle custom ioctls for MX3 framebuffer.
+ *
+ *  @inode	inode struct
+ *  @file	file struct
+ *  @cmd	Ioctl command to handle
+ *  @arg	User pointer to command arguments
+ *  @fbi	framebuffer information pointer
+ */
+static int mx3fb_ioctl_ovl(struct fb_info *fbi, unsigned int cmd,
+			   unsigned long arg)
+{
+	struct mx3fb_info *mx3_fbi = fbi->par;
+	struct mx3fb_data *mx3fb = mx3_fbi->mx3fb;
+	int retval = 0;
+	int __user *argp = (void __user *)arg;
+	struct mx3fb_alloc_list *mem;
+	int size;
+	unsigned long offset;
+	union {
+		struct mxcfb_pos pos;
+		struct mxcfb_gbl_alpha ga;
+		struct mxcfb_color_key key;
+	} s;
+
+	switch (cmd) {
+	case FBIO_ALLOC:
+		if (get_user(size, argp))
+			return -EFAULT;
+
+		mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+		if (mem == NULL)
+			return -ENOMEM;
+
+		mem->size = PAGE_ALIGN(size);
+
+		mem->cpu_addr = dma_alloc_coherent(fbi->device, size,
+						   &mem->phy_addr,
+						   GFP_DMA);
+		if (mem->cpu_addr == NULL) {
+			kfree(mem);
+			return -ENOMEM;
+		}
+
+		mutex_lock(&mx3_fbi->mutex);
+		list_add(&mem->list, &mx3_fbi->ovl_list);
+		mutex_unlock(&mx3_fbi->mutex);
+
+		dev_dbg(fbi->device, "allocated %d bytes  <at>  0x%08X\n",
+			mem->size, mem->phy_addr);
+
+		if (put_user(mem->phy_addr, argp))
+			return -EFAULT;
+
+		break;
+	case FBIO_FREE:
+		if (get_user(offset, argp))
+			return -EFAULT;
+
+		retval = -EINVAL;
+		mutex_lock(&mx3_fbi->mutex);
+		list_for_each_entry(mem, &mx3_fbi->ovl_list, list) {
+			if (mem->phy_addr == offset) {
+				list_del(&mem->list);
+				dma_free_coherent(fbi->device,
+						  mem->size,
+						  mem->cpu_addr,
+						  mem->phy_addr);
+				kfree(mem);
+				retval = 0;
+				break;
+			}
+		}
+		mutex_unlock(&mx3_fbi->mutex);
+
+		break;
+	case MXCFB_SET_GBL_ALPHA:
+		if (copy_from_user(&s.ga, (void *)arg, sizeof(s.ga)))
+			retval = -EFAULT;
+
+		sdc_set_global_alpha(mx3fb, (bool)s.ga.enable, s.ga.alpha);
+		dev_dbg(fbi->device, "Set global alpha to %d\n", s.ga.alpha);
+
+		break;
+	case MXCFB_SET_CLR_KEY:
+		if (copy_from_user(&s.key, (void *)arg, sizeof(s.key)))
+			retval = -EFAULT;
+
+		sdc_set_color_key(mx3fb, (bool)s.key.enable, s.key.color_key);
+		dev_dbg(fbi->device, "Set color key to %d\n", s.key.color_key);
+
+		break;
+	case MXCFB_SET_OVERLAY_POS:
+		if (copy_from_user(&s.pos, (void *)arg, sizeof(s.pos)))
+			retval = -EFAULT;
+
+		sdc_set_window_pos(mx3fb, mx3_fbi->ipu_ch, s.pos.x, s.pos.y);
+		dev_dbg(fbi->device, "Set win position to posx=%d, posy=%d\n",
+			s.pos.x, s.pos.y);
+
+		break;
+	default:
+		retval = -EINVAL;
+	}
+
+	return retval;
+}
+
+static struct fb_ops mx3fb_ovl_ops = {
+	.owner = THIS_MODULE,
+	.fb_set_par = mx3fb_set_par,
+	.fb_check_var = mx3fb_check_var,
+	.fb_setcolreg = mx3fb_setcolreg,
+	.fb_pan_display = mx3fb_pan_display,
+	.fb_ioctl = mx3fb_ioctl_ovl,
+	.fb_fillrect = cfb_fillrect,
+	.fb_copyarea = cfb_copyarea,
+	.fb_imageblit = cfb_imageblit,
+	.fb_blank = mx3fb_blank_ovl,
+};
+#endif
+
 #ifdef CONFIG_PM
 /*
  * Power management hooks.      Note that we won't be called from IRQ context,
@@ -1164,11 +1384,16 @@ static int mx3fb_suspend(struct platform_device *pdev, pm_message_t state)
 {
 	struct mx3fb_data *mx3fb = platform_get_drvdata(pdev);
 	struct mx3fb_info *mx3_fbi = mx3fb->fbi->par;
+	struct mx3fb_info *mx3_fbi_ovl = mx3fb->fbi_ovl->par;
 
 	console_lock();
 	fb_set_suspend(mx3fb->fbi, 1);
+	fb_set_suspend(mx3fb->fbi_ovl, 1);
 	console_unlock();
 
+	if (mx3_fbi_ovl->blank == FB_BLANK_UNBLANK)
+		sdc_disable_channel(mx3_fbi_ovl);
+
 	if (mx3_fbi->blank == FB_BLANK_UNBLANK) {
 		sdc_disable_channel(mx3_fbi);
 		sdc_set_brightness(mx3fb, 0);
@@ -1184,14 +1409,19 @@ static int mx3fb_resume(struct platform_device *pdev)
 {
 	struct mx3fb_data *mx3fb = platform_get_drvdata(pdev);
 	struct mx3fb_info *mx3_fbi = mx3fb->fbi->par;
+	struct mx3fb_info *mx3_fbi_ovl = mx3fb->fbi_ovl->par;
 
 	if (mx3_fbi->blank == FB_BLANK_UNBLANK) {
 		sdc_enable_channel(mx3_fbi);
 		sdc_set_brightness(mx3fb, mx3fb->backlight_level);
 	}
 
+	if (mx3_fbi_ovl->blank == FB_BLANK_UNBLANK)
+		sdc_enable_channel(mx3_fbi_ovl);
+
 	console_lock();
 	fb_set_suspend(mx3fb->fbi, 0);
+	fb_set_suspend(mx3fb->fbi_ovl, 0);
 	console_unlock();
 
 	return 0;
@@ -1333,8 +1563,9 @@ static int init_fb_chan(struct mx3fb_data *mx3fb, struct idmac_channel *ichan)
 	ichan->client = mx3fb;
 	irq = ichan->eof_irq;
 
-	if (ichan->dma_chan.chan_id != IDMAC_SDC_0)
-		return -EINVAL;
+	switch (ichan->dma_chan.chan_id) {
+
+	case IDMAC_SDC_0:
 
 	fbi = mx3fb_init_fbinfo(dev, &mx3fb_ops);
 	if (!fbi)
@@ -1375,7 +1606,29 @@ static int init_fb_chan(struct mx3fb_data *mx3fb, struct idmac_channel *ichan)
 
 	sdc_set_brightness(mx3fb, 255);
 	sdc_set_global_alpha(mx3fb, true, 0xFF);
-	sdc_set_color_key(mx3fb, IDMAC_SDC_0, false, 0);
+	sdc_set_color_key(mx3fb, false, 0);
+
+	break;
+#ifdef CONFIG_FB_MX3_OVERLAY
+	case IDMAC_SDC_1:
+
+		/* We know, that background has been allocated already! */
+		fbi = mx3fb_init_fbinfo(dev, &mx3fb_ovl_ops);
+		if (!fbi)
+			return -ENOMEM;
+
+		/* Default Y virtual size is 2x panel size */
+		fbi->var = mx3fb->fbi->var;
+		/* This shouldn't be necessary, it is already set up above */
+		fbi->var.yres_virtual = mx3fb->fbi->var.yres * 2;
+
+		mx3fb->fbi_ovl = fbi;
+
+		break;
+#endif
+	default:
+		return -EINVAL;
+	}
 
 	mx3fbi			= fbi->par;
 	mx3fbi->idmac_channel	= ichan;
@@ -1392,9 +1645,13 @@ static int init_fb_chan(struct mx3fb_data *mx3fb, struct idmac_channel *ichan)
 	if (ret < 0)
 		goto esetpar;
 
-	__blank(FB_BLANK_UNBLANK, fbi);
+	/* Overlay stays blanked by default */
+	if (ichan->dma_chan.chan_id == IDMAC_SDC_0) {
+		mx3fb_blank(FB_BLANK_UNBLANK, fbi);
 
-	dev_info(dev, "registered, using mode %s\n", fb_mode);
+		dev_info(dev, "mx3fb: fb registered, using mode %s [%c]\n",
+		fb_mode, list_empty(&ichan->queue) ? '-' : '+');
+	}
 
 	ret = register_framebuffer(fbi);
 	if (ret < 0)
@@ -1492,14 +1749,42 @@ static int mx3fb_probe(struct platform_device *pdev)
 
 	mx3fb->backlight_level = 255;
 
+	mx3fb->idmac_channel[0] = to_idmac_chan(chan);
+	mx3fb->idmac_channel[0]->client = mx3fb;
+
 	ret = init_fb_chan(mx3fb, to_idmac_chan(chan));
 	if (ret < 0)
 		goto eisdc0;
 
+#ifdef CONFIG_FB_MX3_OVERLAY
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+	dma_cap_set(DMA_PRIVATE, mask);
+	rq.id = IDMAC_SDC_1;
+	chan = dma_request_channel(mask, chan_filter, &rq);
+	if (!chan) {
+		ret = -EBUSY;
+		goto ersdc1;
+	}
+
+	mx3fb->idmac_channel[1] = to_idmac_chan(chan);
+	mx3fb->idmac_channel[1]->client = mx3fb;
+
+	ret = init_fb_chan(mx3fb, to_idmac_chan(chan));
+	if (ret < 0)
+		goto eisdc1;
+#endif
+
 	return 0;
 
+#ifdef CONFIG_FB_MX3_OVERLAY
+eisdc1:
+	dma_release_channel(&mx3fb->idmac_channel[1]->dma_chan);
+ersdc1:
+	release_fbi(mx3fb->fbi);
+#endif
 eisdc0:
-	dma_release_channel(chan);
+	dma_release_channel(&mx3fb->idmac_channel[0]->dma_chan);
 ersdc0:
 	dmaengine_put();
 	iounmap(mx3fb->reg_base);
@@ -1513,13 +1798,16 @@ static int mx3fb_remove(struct platform_device *dev)
 {
 	struct mx3fb_data *mx3fb = platform_get_drvdata(dev);
 	struct fb_info *fbi = mx3fb->fbi;
-	struct mx3fb_info *mx3_fbi = fbi->par;
-	struct dma_chan *chan;
 
-	chan = &mx3_fbi->idmac_channel->dma_chan;
-	release_fbi(fbi);
+	if (fbi)
+		release_fbi(fbi);
+
+	fbi = mx3fb->fbi_ovl;
+	if (fbi)
+		release_fbi(fbi);
 
-	dma_release_channel(chan);
+	dma_release_channel(&mx3fb->idmac_channel[1]->dma_chan);
+	dma_release_channel(&mx3fb->idmac_channel[0]->dma_chan);
 	dmaengine_put();
 
 	iounmap(mx3fb->reg_base);
diff --git a/include/linux/mxcfb.h b/include/linux/mxcfb.h
new file mode 100644
index 0000000..b5b681f
--- /dev/null
+++ b/include/linux/mxcfb.h
@@ -0,0 +1,40 @@
+/*
+ * File: include/linux/mxcfb.h
+ * Global header file for the MXC Framebuffer
+ *
+ * Copyright 2004-2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License.  You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#ifndef __LINUX_MXCFB_H__
+#define __LINUX_MXCFB_H__
+
+struct mxcfb_gbl_alpha {
+	int enable;
+	int alpha;
+};
+
+struct mxcfb_color_key {
+	int enable;
+	__u32 color_key;
+};
+
+struct mxcfb_pos {
+	__u16 x;
+	__u16 y;
+};
+
+/* IOCTL commands. */
+
+#define MXCFB_SET_GBL_ALPHA		_IOW('F', 0x21, struct mxcfb_gbl_alpha)
+#define MXCFB_SET_CLR_KEY		_IOW('F', 0x22, struct mxcfb_color_key)
+#define MXCFB_SET_OVERLAY_POS		_IOWR('F', 0x24, struct mxcfb_pos)
+
+#endif
+
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* [PATCH v2] ARM: i.mx: mx3fb: add overlay support
@ 2012-04-22 13:31 ` Alex Gershgorin
  0 siblings, 0 replies; 2+ messages in thread
From: Alex Gershgorin @ 2012-04-22 13:31 UTC (permalink / raw)
  To: Florian Tobias Schandinat
  Cc: g.liakhovetski, s.hauer, laurent.pinchart, linux-fbdev,
	linux-media, Alex Gershgorin

This work is based on some earlier patch series
"i.MX31: dmaengine and framebuffer drivers" from 2008
by Guennadi Liakhovetski, the patch initializes overlay channel,
adds ioctl for configuring transparency of the overlay and graphics
planes, CONFIG_FB_MX3_OVERLAY is also supported.

In case that CONFIG_FB_MX3_OVERLAY is not defined, mx3fb is completely
backward compatible.

Blend mode, only global alpha blending has been tested.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Alex Gershgorin <alexg@meprolight.com>
---
Applies to v3.4-rc4

Changes since v1:
  *Some fixes after review
  *Added ioctl for setting overlay windows position
---
 drivers/video/Kconfig |    7 +
 drivers/video/mx3fb.c |  346 ++++++++++++++++++++++++++++++++++++++++++++----
 include/linux/mxcfb.h |   40 ++++++
 3 files changed, 364 insertions(+), 29 deletions(-)
 create mode 100644 include/linux/mxcfb.h

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index a290be5..acbfccc 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2368,6 +2368,13 @@ config FB_MX3
 	  far only synchronous displays are supported. If you plan to use
 	  an LCD display with your i.MX31 system, say Y here.
 
+config FB_MX3_OVERLAY
+	bool "MX3 Overlay support"
+	default n
+	depends on FB_MX3
+	---help---
+	  Say Y here to enable overlay support
+
 config FB_BROADSHEET
 	tristate "E-Ink Broadsheet/Epson S1D13521 controller support"
 	depends on FB
diff --git a/drivers/video/mx3fb.c b/drivers/video/mx3fb.c
index eec0d7b..09d7885 100644
--- a/drivers/video/mx3fb.c
+++ b/drivers/video/mx3fb.c
@@ -26,6 +26,7 @@
 #include <linux/console.h>
 #include <linux/clk.h>
 #include <linux/mutex.h>
+#include <linux/mxcfb.h>
 
 #include <mach/dma.h>
 #include <mach/hardware.h>
@@ -238,6 +239,7 @@ static const struct fb_videomode mx3fb_modedb[] = {
 
 struct mx3fb_data {
 	struct fb_info		*fbi;
+	struct fb_info		*fbi_ovl;
 	int			backlight_level;
 	void __iomem		*reg_base;
 	spinlock_t		lock;
@@ -246,6 +248,9 @@ struct mx3fb_data {
 	uint32_t		h_start_width;
 	uint32_t		v_start_width;
 	enum disp_data_mapping	disp_data_fmt;
+
+	/* IDMAC / dmaengine interface */
+	struct idmac_channel	*idmac_channel[2];	/* We need 2 channels */
 };
 
 struct dma_chan_request {
@@ -268,10 +273,19 @@ struct mx3fb_info {
 	struct dma_async_tx_descriptor	*txd;
 	dma_cookie_t			cookie;
 	struct scatterlist		sg[2];
+	struct list_head		ovl_list; /* overlay buffer list */
 
 	u32				sync;	/* preserve var->sync flags */
 };
 
+/* Allocated overlay buffer */
+struct mx3fb_alloc_list {
+	struct list_head	list;
+	dma_addr_t		phy_addr;
+	void			*cpu_addr;
+	size_t			size;
+};
+
 static void mx3fb_dma_done(void *);
 
 /* Used fb-mode and bpp. Can be set on kernel command line, therefore file-static. */
@@ -303,7 +317,13 @@ static void sdc_fb_init(struct mx3fb_info *fbi)
 	struct mx3fb_data *mx3fb = fbi->mx3fb;
 	uint32_t reg;
 
-	reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF);
+	reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF) & ~SDC_COM_GWSEL;
+
+	/* Also enable foreground for overlay graphic window is foreground */
+	if (mx3fb->fbi_ovl && fbi = mx3fb->fbi_ovl->par) {
+		reg |= SDC_COM_FG_EN | SDC_COM_GWSEL;
+		INIT_LIST_HEAD(&fbi->ovl_list);
+	}
 
 	mx3fb_write_reg(mx3fb, reg | SDC_COM_BG_EN, SDC_COM_CONF);
 }
@@ -312,13 +332,24 @@ static void sdc_fb_init(struct mx3fb_info *fbi)
 static uint32_t sdc_fb_uninit(struct mx3fb_info *fbi)
 {
 	struct mx3fb_data *mx3fb = fbi->mx3fb;
-	uint32_t reg;
+	uint32_t reg, chan_mask;
 
 	reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF);
 
-	mx3fb_write_reg(mx3fb, reg & ~SDC_COM_BG_EN, SDC_COM_CONF);
+	/*
+	 * Don't we have to automatically disable overlay when disabling
+	 * background? Attention: cannot test mx3fb->fbi_ovl->par, must
+	 * test mx3fb->fbi->par, because at the time this function is
+	 * called for the first time fbi_ovl is not assigned yet.
+	 */
+	if (fbi = mx3fb->fbi->par)
+		chan_mask = SDC_COM_BG_EN;
+	else
+		chan_mask = SDC_COM_FG_EN | SDC_COM_GWSEL;
+
+	mx3fb_write_reg(mx3fb, reg & ~chan_mask, SDC_COM_CONF);
 
-	return reg & SDC_COM_BG_EN;
+	return reg & chan_mask;
 }
 
 static void sdc_enable_channel(struct mx3fb_info *mx3_fbi)
@@ -412,13 +443,33 @@ static void sdc_disable_channel(struct mx3fb_info *mx3_fbi)
 static int sdc_set_window_pos(struct mx3fb_data *mx3fb, enum ipu_channel channel,
 			      int16_t x_pos, int16_t y_pos)
 {
-	if (channel != IDMAC_SDC_0)
-		return -EINVAL;
-
 	x_pos += mx3fb->h_start_width;
 	y_pos += mx3fb->v_start_width;
 
-	mx3fb_write_reg(mx3fb, (x_pos << 16) | y_pos, SDC_BG_POS);
+/*
+   If the overlay is defined, we just need to swap
+   the window position register relative to DMAC channel ID
+*/
+	switch (channel) {
+#ifndef CONFIG_FB_MX3_OVERLAY
+	case IDMAC_SDC_0:
+		mx3fb_write_reg(mx3fb, (x_pos << 16) | y_pos, SDC_BG_POS);
+		break;
+	case IDMAC_SDC_1:
+		mx3fb_write_reg(mx3fb, (x_pos << 16) | y_pos, SDC_FG_POS);
+		break;
+#else
+	case IDMAC_SDC_0:
+		mx3fb_write_reg(mx3fb, (x_pos << 16) | y_pos, SDC_FG_POS);
+		break;
+	case IDMAC_SDC_1:
+		mx3fb_write_reg(mx3fb, (x_pos << 16) | y_pos, SDC_BG_POS);
+		break;
+#endif
+	default:
+		return -EINVAL;
+	}
+
 	return 0;
 }
 
@@ -482,14 +533,17 @@ static int sdc_init_panel(struct mx3fb_data *mx3fb, enum ipu_panel panel,
 	mx3fb->h_start_width = h_start_width;
 	mx3fb->v_start_width = v_start_width;
 
+	reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF);
+
 	switch (panel) {
 	case IPU_PANEL_SHARP_TFT:
 		mx3fb_write_reg(mx3fb, 0x00FD0102L, SDC_SHARP_CONF_1);
 		mx3fb_write_reg(mx3fb, 0x00F500F4L, SDC_SHARP_CONF_2);
-		mx3fb_write_reg(mx3fb, SDC_COM_SHARP | SDC_COM_TFT_COLOR, SDC_COM_CONF);
+		mx3fb_write_reg(mx3fb, reg | SDC_COM_SHARP |
+				SDC_COM_TFT_COLOR, SDC_COM_CONF);
 		break;
 	case IPU_PANEL_TFT:
-		mx3fb_write_reg(mx3fb, SDC_COM_TFT_COLOR, SDC_COM_CONF);
+		mx3fb_write_reg(mx3fb, reg | SDC_COM_TFT_COLOR, SDC_COM_CONF);
 		break;
 	default:
 		return -EINVAL;
@@ -563,13 +617,12 @@ static int sdc_init_panel(struct mx3fb_data *mx3fb, enum ipu_panel panel,
 /**
  * sdc_set_color_key() - set the transparent color key for SDC graphic plane.
  * @mx3fb:	mx3fb context.
- * @channel:	IPU DMAC channel ID.
  * @enable:	boolean to enable or disable color keyl.
  * @color_key:	24-bit RGB color to use as transparent color key.
  * @return:	0 on success or negative error code on failure.
  */
-static int sdc_set_color_key(struct mx3fb_data *mx3fb, enum ipu_channel channel,
-			     bool enable, uint32_t color_key)
+static int sdc_set_color_key(struct mx3fb_data *mx3fb, bool enable,
+				uint32_t color_key)
 {
 	uint32_t reg, sdc_conf;
 	unsigned long lock_flags;
@@ -577,10 +630,6 @@ static int sdc_set_color_key(struct mx3fb_data *mx3fb, enum ipu_channel channel,
 	spin_lock_irqsave(&mx3fb->lock, lock_flags);
 
 	sdc_conf = mx3fb_read_reg(mx3fb, SDC_COM_CONF);
-	if (channel = IDMAC_SDC_0)
-		sdc_conf &= ~SDC_COM_GWSEL;
-	else
-		sdc_conf |= SDC_COM_GWSEL;
 
 	if (enable) {
 		reg = mx3fb_read_reg(mx3fb, SDC_GW_CTRL) & 0xFF000000L;
@@ -668,8 +717,16 @@ static int mx3fb_set_fix(struct fb_info *fbi)
 {
 	struct fb_fix_screeninfo *fix = &fbi->fix;
 	struct fb_var_screeninfo *var = &fbi->var;
+	struct mx3fb_info *mx3_fbi = fbi->par;
 
+#ifndef CONFIG_FB_MX3_OVERLAY
 	strncpy(fix->id, "DISP3 BG", 8);
+#else
+	if (mx3_fbi->ipu_ch = IDMAC_SDC_1)
+		strncpy(fix->id, "DISP3 BG", 8);
+	else
+		strncpy(fix->id, "DISP3 FG", 8);
+#endif
 
 	fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
 
@@ -689,13 +746,25 @@ static void mx3fb_dma_done(void *arg)
 	struct idmac_channel *ichannel = to_idmac_chan(chan);
 	struct mx3fb_data *mx3fb = ichannel->client;
 	struct mx3fb_info *mx3_fbi = mx3fb->fbi->par;
+	struct mx3fb_info *mx3_fbi_cur;
+	struct mx3fb_info *mx3_fbi_ovl = mx3fb->fbi_ovl ? mx3fb->fbi_ovl->par :
+		NULL;
 
 	dev_dbg(mx3fb->dev, "irq %d callback\n", ichannel->eof_irq);
 
+	if (ichannel = mx3_fbi->idmac_channel) {
+		mx3_fbi_cur = mx3_fbi;
+	} else if (mx3_fbi_ovl && ichannel = mx3_fbi_ovl->idmac_channel) {
+		mx3_fbi_cur = mx3_fbi_ovl;
+	} else {
+		WARN(1, "Cannot identify channel!\n");
+		return;
+	}
+
 	/* We only need one interrupt, it will be re-enabled as needed */
 	disable_irq_nosync(ichannel->eof_irq);
 
-	complete(&mx3_fbi->flip_cmpl);
+	complete(&mx3_fbi_cur->flip_cmpl);
 }
 
 static int __set_par(struct fb_info *fbi, bool lock)
@@ -1151,6 +1220,157 @@ static struct fb_ops mx3fb_ops = {
 	.fb_blank = mx3fb_blank,
 };
 
+#ifdef CONFIG_FB_MX3_OVERLAY
+static int mx3fb_blank_ovl(int blank, struct fb_info *fbi)
+{
+	struct mx3fb_info *mx3_fbi = fbi->par;
+
+	dev_dbg(fbi->device, "ovl blank = %d\n", blank);
+
+	if (mx3_fbi->blank = blank)
+		return 0;
+
+	mutex_lock(&mx3_fbi->mutex);
+	mx3_fbi->blank = blank;
+
+	switch (blank) {
+	case FB_BLANK_POWERDOWN:
+	case FB_BLANK_VSYNC_SUSPEND:
+	case FB_BLANK_HSYNC_SUSPEND:
+	case FB_BLANK_NORMAL:
+		sdc_disable_channel(mx3_fbi);
+		break;
+	case FB_BLANK_UNBLANK:
+		sdc_enable_channel(mx3_fbi);
+		break;
+	}
+	mutex_unlock(&mx3_fbi->mutex);
+
+	return 0;
+}
+
+/*
+ * Function to handle custom ioctls for MX3 framebuffer.
+ *
+ *  @inode	inode struct
+ *  @file	file struct
+ *  @cmd	Ioctl command to handle
+ *  @arg	User pointer to command arguments
+ *  @fbi	framebuffer information pointer
+ */
+static int mx3fb_ioctl_ovl(struct fb_info *fbi, unsigned int cmd,
+			   unsigned long arg)
+{
+	struct mx3fb_info *mx3_fbi = fbi->par;
+	struct mx3fb_data *mx3fb = mx3_fbi->mx3fb;
+	int retval = 0;
+	int __user *argp = (void __user *)arg;
+	struct mx3fb_alloc_list *mem;
+	int size;
+	unsigned long offset;
+	union {
+		struct mxcfb_pos pos;
+		struct mxcfb_gbl_alpha ga;
+		struct mxcfb_color_key key;
+	} s;
+
+	switch (cmd) {
+	case FBIO_ALLOC:
+		if (get_user(size, argp))
+			return -EFAULT;
+
+		mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+		if (mem = NULL)
+			return -ENOMEM;
+
+		mem->size = PAGE_ALIGN(size);
+
+		mem->cpu_addr = dma_alloc_coherent(fbi->device, size,
+						   &mem->phy_addr,
+						   GFP_DMA);
+		if (mem->cpu_addr = NULL) {
+			kfree(mem);
+			return -ENOMEM;
+		}
+
+		mutex_lock(&mx3_fbi->mutex);
+		list_add(&mem->list, &mx3_fbi->ovl_list);
+		mutex_unlock(&mx3_fbi->mutex);
+
+		dev_dbg(fbi->device, "allocated %d bytes  <at>  0x%08X\n",
+			mem->size, mem->phy_addr);
+
+		if (put_user(mem->phy_addr, argp))
+			return -EFAULT;
+
+		break;
+	case FBIO_FREE:
+		if (get_user(offset, argp))
+			return -EFAULT;
+
+		retval = -EINVAL;
+		mutex_lock(&mx3_fbi->mutex);
+		list_for_each_entry(mem, &mx3_fbi->ovl_list, list) {
+			if (mem->phy_addr = offset) {
+				list_del(&mem->list);
+				dma_free_coherent(fbi->device,
+						  mem->size,
+						  mem->cpu_addr,
+						  mem->phy_addr);
+				kfree(mem);
+				retval = 0;
+				break;
+			}
+		}
+		mutex_unlock(&mx3_fbi->mutex);
+
+		break;
+	case MXCFB_SET_GBL_ALPHA:
+		if (copy_from_user(&s.ga, (void *)arg, sizeof(s.ga)))
+			retval = -EFAULT;
+
+		sdc_set_global_alpha(mx3fb, (bool)s.ga.enable, s.ga.alpha);
+		dev_dbg(fbi->device, "Set global alpha to %d\n", s.ga.alpha);
+
+		break;
+	case MXCFB_SET_CLR_KEY:
+		if (copy_from_user(&s.key, (void *)arg, sizeof(s.key)))
+			retval = -EFAULT;
+
+		sdc_set_color_key(mx3fb, (bool)s.key.enable, s.key.color_key);
+		dev_dbg(fbi->device, "Set color key to %d\n", s.key.color_key);
+
+		break;
+	case MXCFB_SET_OVERLAY_POS:
+		if (copy_from_user(&s.pos, (void *)arg, sizeof(s.pos)))
+			retval = -EFAULT;
+
+		sdc_set_window_pos(mx3fb, mx3_fbi->ipu_ch, s.pos.x, s.pos.y);
+		dev_dbg(fbi->device, "Set win position to posx=%d, posy=%d\n",
+			s.pos.x, s.pos.y);
+
+		break;
+	default:
+		retval = -EINVAL;
+	}
+
+	return retval;
+}
+
+static struct fb_ops mx3fb_ovl_ops = {
+	.owner = THIS_MODULE,
+	.fb_set_par = mx3fb_set_par,
+	.fb_check_var = mx3fb_check_var,
+	.fb_setcolreg = mx3fb_setcolreg,
+	.fb_pan_display = mx3fb_pan_display,
+	.fb_ioctl = mx3fb_ioctl_ovl,
+	.fb_fillrect = cfb_fillrect,
+	.fb_copyarea = cfb_copyarea,
+	.fb_imageblit = cfb_imageblit,
+	.fb_blank = mx3fb_blank_ovl,
+};
+#endif
+
 #ifdef CONFIG_PM
 /*
  * Power management hooks.      Note that we won't be called from IRQ context,
@@ -1164,11 +1384,16 @@ static int mx3fb_suspend(struct platform_device *pdev, pm_message_t state)
 {
 	struct mx3fb_data *mx3fb = platform_get_drvdata(pdev);
 	struct mx3fb_info *mx3_fbi = mx3fb->fbi->par;
+	struct mx3fb_info *mx3_fbi_ovl = mx3fb->fbi_ovl->par;
 
 	console_lock();
 	fb_set_suspend(mx3fb->fbi, 1);
+	fb_set_suspend(mx3fb->fbi_ovl, 1);
 	console_unlock();
 
+	if (mx3_fbi_ovl->blank = FB_BLANK_UNBLANK)
+		sdc_disable_channel(mx3_fbi_ovl);
+
 	if (mx3_fbi->blank = FB_BLANK_UNBLANK) {
 		sdc_disable_channel(mx3_fbi);
 		sdc_set_brightness(mx3fb, 0);
@@ -1184,14 +1409,19 @@ static int mx3fb_resume(struct platform_device *pdev)
 {
 	struct mx3fb_data *mx3fb = platform_get_drvdata(pdev);
 	struct mx3fb_info *mx3_fbi = mx3fb->fbi->par;
+	struct mx3fb_info *mx3_fbi_ovl = mx3fb->fbi_ovl->par;
 
 	if (mx3_fbi->blank = FB_BLANK_UNBLANK) {
 		sdc_enable_channel(mx3_fbi);
 		sdc_set_brightness(mx3fb, mx3fb->backlight_level);
 	}
 
+	if (mx3_fbi_ovl->blank = FB_BLANK_UNBLANK)
+		sdc_enable_channel(mx3_fbi_ovl);
+
 	console_lock();
 	fb_set_suspend(mx3fb->fbi, 0);
+	fb_set_suspend(mx3fb->fbi_ovl, 0);
 	console_unlock();
 
 	return 0;
@@ -1333,8 +1563,9 @@ static int init_fb_chan(struct mx3fb_data *mx3fb, struct idmac_channel *ichan)
 	ichan->client = mx3fb;
 	irq = ichan->eof_irq;
 
-	if (ichan->dma_chan.chan_id != IDMAC_SDC_0)
-		return -EINVAL;
+	switch (ichan->dma_chan.chan_id) {
+
+	case IDMAC_SDC_0:
 
 	fbi = mx3fb_init_fbinfo(dev, &mx3fb_ops);
 	if (!fbi)
@@ -1375,7 +1606,29 @@ static int init_fb_chan(struct mx3fb_data *mx3fb, struct idmac_channel *ichan)
 
 	sdc_set_brightness(mx3fb, 255);
 	sdc_set_global_alpha(mx3fb, true, 0xFF);
-	sdc_set_color_key(mx3fb, IDMAC_SDC_0, false, 0);
+	sdc_set_color_key(mx3fb, false, 0);
+
+	break;
+#ifdef CONFIG_FB_MX3_OVERLAY
+	case IDMAC_SDC_1:
+
+		/* We know, that background has been allocated already! */
+		fbi = mx3fb_init_fbinfo(dev, &mx3fb_ovl_ops);
+		if (!fbi)
+			return -ENOMEM;
+
+		/* Default Y virtual size is 2x panel size */
+		fbi->var = mx3fb->fbi->var;
+		/* This shouldn't be necessary, it is already set up above */
+		fbi->var.yres_virtual = mx3fb->fbi->var.yres * 2;
+
+		mx3fb->fbi_ovl = fbi;
+
+		break;
+#endif
+	default:
+		return -EINVAL;
+	}
 
 	mx3fbi			= fbi->par;
 	mx3fbi->idmac_channel	= ichan;
@@ -1392,9 +1645,13 @@ static int init_fb_chan(struct mx3fb_data *mx3fb, struct idmac_channel *ichan)
 	if (ret < 0)
 		goto esetpar;
 
-	__blank(FB_BLANK_UNBLANK, fbi);
+	/* Overlay stays blanked by default */
+	if (ichan->dma_chan.chan_id = IDMAC_SDC_0) {
+		mx3fb_blank(FB_BLANK_UNBLANK, fbi);
 
-	dev_info(dev, "registered, using mode %s\n", fb_mode);
+		dev_info(dev, "mx3fb: fb registered, using mode %s [%c]\n",
+		fb_mode, list_empty(&ichan->queue) ? '-' : '+');
+	}
 
 	ret = register_framebuffer(fbi);
 	if (ret < 0)
@@ -1492,14 +1749,42 @@ static int mx3fb_probe(struct platform_device *pdev)
 
 	mx3fb->backlight_level = 255;
 
+	mx3fb->idmac_channel[0] = to_idmac_chan(chan);
+	mx3fb->idmac_channel[0]->client = mx3fb;
+
 	ret = init_fb_chan(mx3fb, to_idmac_chan(chan));
 	if (ret < 0)
 		goto eisdc0;
 
+#ifdef CONFIG_FB_MX3_OVERLAY
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+	dma_cap_set(DMA_PRIVATE, mask);
+	rq.id = IDMAC_SDC_1;
+	chan = dma_request_channel(mask, chan_filter, &rq);
+	if (!chan) {
+		ret = -EBUSY;
+		goto ersdc1;
+	}
+
+	mx3fb->idmac_channel[1] = to_idmac_chan(chan);
+	mx3fb->idmac_channel[1]->client = mx3fb;
+
+	ret = init_fb_chan(mx3fb, to_idmac_chan(chan));
+	if (ret < 0)
+		goto eisdc1;
+#endif
+
 	return 0;
 
+#ifdef CONFIG_FB_MX3_OVERLAY
+eisdc1:
+	dma_release_channel(&mx3fb->idmac_channel[1]->dma_chan);
+ersdc1:
+	release_fbi(mx3fb->fbi);
+#endif
 eisdc0:
-	dma_release_channel(chan);
+	dma_release_channel(&mx3fb->idmac_channel[0]->dma_chan);
 ersdc0:
 	dmaengine_put();
 	iounmap(mx3fb->reg_base);
@@ -1513,13 +1798,16 @@ static int mx3fb_remove(struct platform_device *dev)
 {
 	struct mx3fb_data *mx3fb = platform_get_drvdata(dev);
 	struct fb_info *fbi = mx3fb->fbi;
-	struct mx3fb_info *mx3_fbi = fbi->par;
-	struct dma_chan *chan;
 
-	chan = &mx3_fbi->idmac_channel->dma_chan;
-	release_fbi(fbi);
+	if (fbi)
+		release_fbi(fbi);
+
+	fbi = mx3fb->fbi_ovl;
+	if (fbi)
+		release_fbi(fbi);
 
-	dma_release_channel(chan);
+	dma_release_channel(&mx3fb->idmac_channel[1]->dma_chan);
+	dma_release_channel(&mx3fb->idmac_channel[0]->dma_chan);
 	dmaengine_put();
 
 	iounmap(mx3fb->reg_base);
diff --git a/include/linux/mxcfb.h b/include/linux/mxcfb.h
new file mode 100644
index 0000000..b5b681f
--- /dev/null
+++ b/include/linux/mxcfb.h
@@ -0,0 +1,40 @@
+/*
+ * File: include/linux/mxcfb.h
+ * Global header file for the MXC Framebuffer
+ *
+ * Copyright 2004-2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License.  You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#ifndef __LINUX_MXCFB_H__
+#define __LINUX_MXCFB_H__
+
+struct mxcfb_gbl_alpha {
+	int enable;
+	int alpha;
+};
+
+struct mxcfb_color_key {
+	int enable;
+	__u32 color_key;
+};
+
+struct mxcfb_pos {
+	__u16 x;
+	__u16 y;
+};
+
+/* IOCTL commands. */
+
+#define MXCFB_SET_GBL_ALPHA		_IOW('F', 0x21, struct mxcfb_gbl_alpha)
+#define MXCFB_SET_CLR_KEY		_IOW('F', 0x22, struct mxcfb_color_key)
+#define MXCFB_SET_OVERLAY_POS		_IOWR('F', 0x24, struct mxcfb_pos)
+
+#endif
+
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2012-04-22 13:38 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-04-22 13:31 [PATCH v2] ARM: i.mx: mx3fb: add overlay support Alex Gershgorin
2012-04-22 13:31 ` Alex Gershgorin

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.