linux-fbdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH RFC] pxafb: introduce "struct pxafb_output" to abstract the output interface
@ 2008-04-08  9:59 eric miao
  2008-04-16  0:22 ` Andrew Morton
  0 siblings, 1 reply; 2+ messages in thread
From: eric miao @ 2008-04-08  9:59 UTC (permalink / raw)
  To: linux-arm-kernel email list, linux-fbdev-devel
  Cc: Andrew Morton, Russell King - ARM Linux, Daniel Mack

From 8a735df5e45b35f76672e79b44aeedece1dd68da Mon Sep 17 00:00:00 2001
From: Eric Miao <eric.miao@marvell.com>
Date: Tue, 8 Apr 2008 17:48:09 +0800
Subject: [PATCH] pxafb: introduce "struct pxafb_output" to abstract
the output interface

Currently two output interfaces are supported:

    ppi - parallel port interface
    spi - smart panel interface

and possibly MIPI or video output in the future.

Signed-off-by: Eric Miao <eric.miao@marvell.com>
---
 drivers/video/Kconfig |    8 +-
 drivers/video/pxafb.c |  550 +++++++++++++++++++++++++++++++++----------------
 drivers/video/pxafb.h |   26 ++-
 3 files changed, 394 insertions(+), 190 deletions(-)

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 9721b5c..f769a2f 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -1742,9 +1742,13 @@ config FB_PXA

 	  If unsure, say N.

-config FB_PXA_SMARTPANEL
-	bool "PXA Smartpanel LCD support"
+config FB_PXA_PPI
+	bool "PXA LCD Parallel Port Interface support"
+	depends on FB_PXA
 	default y
+
+config FB_PXA_SPI
+	bool "PXA LCD Smart Panel Interface support"
 	depends on FB_PXA

 config FB_PXA_PARAMETERS
diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c
index 0e9eeac..b33e7cf 100644
--- a/drivers/video/pxafb.c
+++ b/drivers/video/pxafb.c
@@ -22,6 +22,8 @@
  *
  */

+#define DEBUG
+
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/kernel.h>
@@ -304,21 +306,8 @@ static int pxafb_check_var(struct
fb_var_screeninfo *var, struct fb_info *info)
 	if (var->yres < MIN_YRES)
 		var->yres = MIN_YRES;

-	if (inf->fixed_modes) {
-		struct pxafb_mode_info *mode;
-
-		mode = pxafb_getmode(inf, var);
-		if (!mode)
-			return -EINVAL;
-		pxafb_setmode(var, mode);
-	} else {
-		if (var->xres > inf->modes->xres)
-			return -EINVAL;
-		if (var->yres > inf->modes->yres)
-			return -EINVAL;
-		if (var->bits_per_pixel > inf->modes->bpp)
-			return -EINVAL;
-	}
+	if (fbi->output->check(fbi, var))
+		return -EINVAL;

 	var->xres_virtual =
 		max(var->xres_virtual, var->xres);
@@ -545,62 +534,205 @@ unsigned long pxafb_get_hsync_time(struct device *dev)
 }
 EXPORT_SYMBOL(pxafb_get_hsync_time);

-static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal,
-		unsigned int offset, size_t size)
+static int dummy_ret(struct pxafb_info *fbi)
 {
-	struct pxafb_dma_descriptor *dma_desc, *pal_desc;
-	unsigned int dma_desc_off, pal_desc_off;
+	return 0;
+}

-	if (dma < 0 || dma >= DMA_MAX)
-		return -EINVAL;
+static void dummy_noret(struct pxafb_info *fbi)
+{
+}

-	dma_desc = &fbi->dma_buff->dma_desc[dma];
-	dma_desc_off = offsetof(struct pxafb_dma_buff, dma_desc[dma]);
+static int dummy_check(struct pxafb_info *fbi, struct fb_var_screeninfo *var)
+{
+	return 0;
+}

-	dma_desc->fsadr = fbi->screen_dma + offset;
-	dma_desc->fidr  = 0;
-	dma_desc->ldcmd = size;
+static void dummy_irq(struct pxafb_info *fbi, uint32_t lcsr)
+{
+}

-	if (pal < 0 || pal >= PAL_MAX) {
-		dma_desc->fdadr = fbi->dma_buff_phys + dma_desc_off;
-		fbi->fdadr[dma] = fbi->dma_buff_phys + dma_desc_off;
+static struct pxafb_output pxafb_output_dummy = {
+	.name		= "dummy",
+	.init		= dummy_ret,
+	.exit		= dummy_ret,
+	.check		= dummy_check,
+	.setup		= dummy_check,
+	.enable		= dummy_noret,
+	.disable	= dummy_noret,
+	.handle_irq	= dummy_irq,
+};
+
+#ifdef CONFIG_FB_PXA_PPI
+struct pxafb_output_ppi_priv {
+	struct completion	disable_done;
+};
+
+static void pxafb_ppi_irq(struct pxafb_info *fbi, uint32_t lcsr)
+{
+	struct pxafb_output_ppi_priv *ppi = fbi->output_priv;
+	uint32_t lccr0;
+
+	if (lcsr & LCSR_LDD) {
+		lccr0 = lcd_readl(fbi, LCCR0);
+		lcd_writel(fbi, LCCR0, lccr0 | LCCR0_LDM);
+		complete(&ppi->disable_done);
+	}
+}
+
+static int pxafb_ppi_check(struct pxafb_info *fbi,
+			   struct fb_var_screeninfo *var)
+{
+	struct pxafb_mach_info *inf = fbi->dev->platform_data;
+
+	if (inf->fixed_modes) {
+		struct pxafb_mode_info *mode;
+
+		mode = pxafb_getmode(inf, var);
+		if (!mode)
+			return -EINVAL;
+		pxafb_setmode(var, mode);
 	} else {
-		pal_desc = &fbi->dma_buff->pal_desc[dma];
-		pal_desc_off = offsetof(struct pxafb_dma_buff, dma_desc[pal]);
+		if (var->xres > inf->modes->xres)
+			return -EINVAL;
+		if (var->yres > inf->modes->yres)
+			return -EINVAL;
+		if (var->bits_per_pixel > inf->modes->bpp)
+			return -EINVAL;
+	}

-		pal_desc->fsadr = fbi->dma_buff_phys + pal * PALETTE_SIZE;
-		pal_desc->fidr  = 0;
+	return 0;
+}

-		if ((fbi->lccr4 & LCCR4_PAL_FOR_MASK) == LCCR4_PAL_FOR_0)
-			pal_desc->ldcmd = fbi->palette_size * sizeof(u16);
-		else
-			pal_desc->ldcmd = fbi->palette_size * sizeof(u32);
+static int pxafb_ppi_setup(struct pxafb_info *fbi,
+			   struct fb_var_screeninfo *var)
+{
+	unsigned int lines_per_panel, pcd = get_pcd(fbi, var->pixclock);

-		pal_desc->ldcmd |= LDCMD_PAL;
+	fbi->reg_lccr1 =
+		LCCR1_DisWdth(var->xres) +
+		LCCR1_HorSnchWdth(var->hsync_len) +
+		LCCR1_BegLnDel(var->left_margin) +
+		LCCR1_EndLnDel(var->right_margin);

-		/* flip back and forth between palette and frame buffer */
-		pal_desc->fdadr = fbi->dma_buff_phys + dma_desc_off;
-		dma_desc->fdadr = fbi->dma_buff_phys + pal_desc_off;
-		fbi->fdadr[dma] = fbi->dma_buff_phys + dma_desc_off;
+	/*
+	 * If we have a dual scan LCD, we need to halve
+	 * the YRES parameter.
+	 */
+	lines_per_panel = var->yres;
+	if ((fbi->lccr0 & LCCR0_SDS) == LCCR0_Dual)
+		lines_per_panel /= 2;
+
+	fbi->reg_lccr2 =
+		LCCR2_DisHght(lines_per_panel) +
+		LCCR2_VrtSnchWdth(var->vsync_len) +
+		LCCR2_BegFrmDel(var->upper_margin) +
+		LCCR2_EndFrmDel(var->lower_margin);
+
+	fbi->reg_lccr3 = fbi->lccr3 |
+		(var->sync & FB_SYNC_HOR_HIGH_ACT ?
+		 LCCR3_HorSnchH : LCCR3_HorSnchL) |
+		(var->sync & FB_SYNC_VERT_HIGH_ACT ?
+		 LCCR3_VrtSnchH : LCCR3_VrtSnchL);
+
+	if (pcd) {
+		fbi->reg_lccr3 |= LCCR3_PixClkDiv(pcd);
+		set_hsync_time(fbi, pcd);
 	}
+	return 0;
+}

+static void pxafb_ppi_enable(struct pxafb_info *fbi)
+{
+	lcd_writel(fbi, LCCR0, fbi->reg_lccr0 | LCCR0_ENB);
+}
+
+static void pxafb_ppi_disable(struct pxafb_info *fbi)
+{
+	struct pxafb_output_ppi_priv *ppi = fbi->output_priv;
+	uint32_t lccr0;
+	int timeout = 200 * HZ / 1000;
+
+	/* Clear LCD Status Register */
+	lcd_writel(fbi, LCSR, 0xffffffff);
+
+	lccr0 = lcd_readl(fbi, LCCR0) & ~LCCR0_LDM;
+	lcd_writel(fbi, LCCR0, lccr0);
+	lcd_writel(fbi, LCCR0, lccr0 | LCCR0_DIS);
+
+	if (wait_for_completion_timeout(&ppi->disable_done, timeout) == 0)
+		pr_warning("%s: time out waiting for stop\n", __func__);
+}
+
+static int pxafb_ppi_init(struct pxafb_info *fbi)
+{
+	struct pxafb_output_ppi_priv *ppi;
+
+	printk("%s:\n", __func__);
+	ppi = kzalloc(sizeof(*ppi), GFP_KERNEL);
+	if (ppi == NULL)
+		return -ENOMEM;
+
+	init_completion(&ppi->disable_done);
+	fbi->output_priv = ppi;
+	return 0;
+}
+
+static int pxafb_ppi_exit(struct pxafb_info *fbi)
+{
+	kfree(fbi->output_priv);
 	return 0;
 }

-#ifdef CONFIG_FB_PXA_SMARTPANEL
+struct pxafb_output pxafb_output_ppi = {
+	.name		= "parallel",
+	.init		= pxafb_ppi_init,
+	.exit		= pxafb_ppi_exit,
+	.check		= pxafb_ppi_check,
+	.setup		= pxafb_ppi_setup,
+	.enable		= pxafb_ppi_enable,
+	.disable	= pxafb_ppi_disable,
+	.handle_irq	= pxafb_ppi_irq,
+};
+#else
+#define pxafb_output_ppi	pxafb_output_dummy
+#endif
+
+#ifdef CONFIG_FB_PXA_SPI
+#define CMD_BUFF_SIZE	(1024 * 50)
+#define MAX_CMD_NR	(CMD_BUFF_SIZE / sizeof(uint16_t))
+
+struct pxafb_output_spi_priv {
+	dma_addr_t		cmd_buff_dma;
+	uint16_t		*smart_cmds;
+	size_t			n_smart_cmds;
+	struct completion	command_done;
+	struct completion	refresh_done;
+	struct task_struct	*smart_thread;
+	struct pxafb_mode_info	*mode;
+};
+
+static void pxafb_spi_irq(struct pxafb_info *fbi, uint32_t lcsr)
+{
+	struct pxafb_output_spi_priv *spi = fbi->output_priv;
+
+	if (lcsr & LCSR_CMD_INT)
+		complete(&spi->command_done);
+}
+
 static int setup_smart_dma(struct pxafb_info *fbi)
 {
+	struct pxafb_output_spi_priv *spi = fbi->output_priv;
 	struct pxafb_dma_descriptor *dma_desc;
-	unsigned long dma_desc_off, cmd_buff_off;
+	unsigned long dma_desc_off;

 	dma_desc = &fbi->dma_buff->dma_desc[DMA_CMD];
 	dma_desc_off = offsetof(struct pxafb_dma_buff, dma_desc[DMA_CMD]);
-	cmd_buff_off = offsetof(struct pxafb_dma_buff, cmd_buff);

 	dma_desc->fdadr = fbi->dma_buff_phys + dma_desc_off;
-	dma_desc->fsadr = fbi->dma_buff_phys + cmd_buff_off;
+	dma_desc->fsadr = spi->cmd_buff_dma;
 	dma_desc->fidr  = 0;
-	dma_desc->ldcmd = fbi->n_smart_cmds * sizeof(uint16_t);
+	dma_desc->ldcmd = spi->n_smart_cmds * sizeof(uint16_t);

 	fbi->fdadr[DMA_CMD] = dma_desc->fdadr;
 	return 0;
@@ -609,6 +741,7 @@ static int setup_smart_dma(struct pxafb_info *fbi)
 int pxafb_smart_flush(struct fb_info *info)
 {
 	struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb);
+	struct pxafb_output_spi_priv *spi = fbi->output_priv;
 	uint32_t prsr;
 	int ret = 0;

@@ -620,11 +753,11 @@ int pxafb_smart_flush(struct fb_info *info)
 	 *    keep track of the end of the transfer
 	 */

-	while (fbi->n_smart_cmds & 1)
-		fbi->smart_cmds[fbi->n_smart_cmds++] = SMART_CMD_NOOP;
+	while (spi->n_smart_cmds & 1)
+		spi->smart_cmds[spi->n_smart_cmds++] = SMART_CMD_NOOP;

-	fbi->smart_cmds[fbi->n_smart_cmds++] = SMART_CMD_INTERRUPT;
-	fbi->smart_cmds[fbi->n_smart_cmds++] = SMART_CMD_WAIT_FOR_VSYNC;
+	spi->smart_cmds[spi->n_smart_cmds++] = SMART_CMD_INTERRUPT;
+	spi->smart_cmds[spi->n_smart_cmds++] = SMART_CMD_WAIT_FOR_VSYNC;
 	setup_smart_dma(fbi);

 	/* continue to execute next command */
@@ -646,7 +779,7 @@ int pxafb_smart_flush(struct fb_info *info)
 	/* begin sending */
 	lcd_writel(fbi, LCCR0, fbi->reg_lccr0 | LCCR0_ENB);

-	if (wait_for_completion_timeout(&fbi->command_done, HZ/2) == 0) {
+	if (wait_for_completion_timeout(&spi->command_done, HZ/2) == 0) {
 		pr_warning("%s: timeout waiting for command done\n",
 				__func__);
 		ret = -ETIMEDOUT;
@@ -657,23 +790,74 @@ int pxafb_smart_flush(struct fb_info *info)
 	lcd_writel(fbi, PRSR, prsr);
 	lcd_writel(fbi, LCCR0, fbi->reg_lccr0 & ~LCCR0_ENB);
 	lcd_writel(fbi, FDADR6, 0);
-	fbi->n_smart_cmds = 0;
+	spi->n_smart_cmds = 0;
 	return ret;
 }

 int pxafb_smart_queue(struct fb_info *info, uint16_t *cmds, int n_cmds)
 {
-	int i;
 	struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb);
+	struct pxafb_output_spi_priv *spi = fbi->output_priv;
+	int i;

 	/* leave 2 commands for INTERRUPT and WAIT_FOR_SYNC */
 	for (i = 0; i < n_cmds; i++) {
-		if (fbi->n_smart_cmds == CMD_BUFF_SIZE - 8)
+		if (spi->n_smart_cmds == MAX_CMD_NR - 8)
 			pxafb_smart_flush(info);

-		fbi->smart_cmds[fbi->n_smart_cmds++] = *cmds++;
+		spi->smart_cmds[spi->n_smart_cmds++] = *cmds++;
+	}
+
+	return 0;
+}
+
+static int pxafb_smart_thread(void *arg)
+{
+	struct pxafb_info *fbi = (struct pxafb_info *) arg;
+	struct pxafb_output_spi_priv *spi = fbi->output_priv;
+	struct pxafb_mach_info *inf = fbi->dev->platform_data;
+
+	if (!fbi || !inf->smart_update) {
+		pr_err("%s: not properly initialized, thread terminated\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	pr_debug("%s(): task starting\n", __func__);
+
+	set_freezable();
+	while (!kthread_should_stop()) {
+
+		if (try_to_freeze())
+			continue;
+
+		if (fbi->state == C_ENABLE) {
+			inf->smart_update(&fbi->fb);
+			complete(&spi->refresh_done);
+		}
+
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(30 * HZ / 1000);
 	}

+	pr_debug("%s(): task ending\n", __func__);
+	return 0;
+}
+
+/* smartpanel doesn't understand the timing information in
+ * fb_var_screeninfo, they are ignored and pxafb_getmode()
+ * is used to check the suitable mode
+ */
+static int pxafb_spi_check(struct pxafb_info *fbi,
+			   struct fb_var_screeninfo *var)
+{
+	struct pxafb_output_spi_priv *spi = fbi->output_priv;
+	struct pxafb_mach_info *inf = fbi->dev->platform_data;
+
+	spi->mode = pxafb_getmode(inf, var);
+	if (spi->mode == NULL)
+		return -EINVAL;
+
 	return 0;
 }

@@ -683,11 +867,12 @@ static unsigned int __smart_timing(unsigned
time_ns, unsigned long lcd_clk)
 	return (t == 0) ? 1 : t;
 }

-static void setup_smart_timing(struct pxafb_info *fbi,
-				struct fb_var_screeninfo *var)
+static int pxafb_spi_setup(struct pxafb_info *fbi,
+			   struct fb_var_screeninfo *var)
 {
-	struct pxafb_mach_info *inf = fbi->dev->platform_data;
-	struct pxafb_mode_info *mode = &inf->modes[0];
+	struct pxafb_output_spi_priv *spi = fbi->output_priv;
+	struct pxafb_mode_info *mode = spi->mode;
+
 	unsigned long lclk = clk_get_rate(fbi->clk);
 	unsigned t1, t2, t3, t4;

@@ -707,98 +892,154 @@ static void setup_smart_timing(struct pxafb_info *fbi,

 	/* FIXME: make this configurable */
 	fbi->reg_cmdcr = 1;
+	return 0;
 }

-static int pxafb_smart_thread(void *arg)
+static void pxafb_spi_enable(struct pxafb_info *fbi)
 {
-	struct pxafb_info *fbi = (struct pxafb_info *) arg;
-	struct pxafb_mach_info *inf = fbi->dev->platform_data;
+	struct pxafb_output_spi_priv *spi = fbi->output_priv;

-	if (!fbi || !inf->smart_update) {
-		pr_err("%s: not properly initialized, thread terminated\n",
-				__func__);
-		return -EINVAL;
-	}
+	wake_up_process(spi->smart_thread);
+}

-	pr_debug("%s(): task starting\n", __func__);
+static void pxafb_spi_disable(struct pxafb_info *fbi)
+{
+	struct pxafb_output_spi_priv *spi = fbi->output_priv;
+	int timeout = 200 * HZ / 1000;

-	set_freezable();
-	while (!kthread_should_stop()) {
+	if (wait_for_completion_timeout(&spi->refresh_done, timeout) == 0)
+		pr_warning("%s: time out waiting for stop\n", __func__);
+}

-		if (try_to_freeze())
-			continue;
+static int pxafb_spi_init(struct pxafb_info *fbi)
+{
+	struct pxafb_output_spi_priv *spi;
+	int ret = -EINVAL;

-		if (fbi->state == C_ENABLE) {
-			inf->smart_update(&fbi->fb);
-			complete(&fbi->refresh_done);
-		}
+	spi = kzalloc(sizeof(*spi), GFP_KERNEL);
+	if (spi == NULL)
+		return -ENOMEM;

-		set_current_state(TASK_INTERRUPTIBLE);
-		schedule_timeout(30 * HZ / 1000);
+	spi->smart_cmds = dma_alloc_coherent(fbi->dev, CMD_BUFF_SIZE,
+				&spi->cmd_buff_dma, GFP_KERNEL);
+	if (spi->smart_cmds == NULL) {
+		ret = -ENOMEM;
+		goto err_free_spi;
 	}

-	pr_debug("%s(): task ending\n", __func__);
+	spi->smart_thread = kthread_create(pxafb_smart_thread, fbi,
+				"lcdrefresh");
+	if (IS_ERR(spi->smart_thread)) {
+		ret = PTR_ERR(spi->smart_thread);
+		goto err_free_dma;
+	}
+
+	init_completion(&spi->command_done);
+	init_completion(&spi->refresh_done);
+	spi->mode = NULL;
+
+	fbi->output_priv = spi;
 	return 0;
+err_free_dma:
+	dma_free_coherent(fbi->dev, CMD_BUFF_SIZE,
+			&spi->cmd_buff_dma, GFP_KERNEL);
+err_free_spi:
+	kfree(spi);
+	return ret;
 }

-static int pxafb_smart_init(struct pxafb_info *fbi)
+static int pxafb_spi_exit(struct pxafb_info *fbi)
 {
-	fbi->smart_thread = kthread_run(pxafb_smart_thread, fbi,
-					"lcd_refresh");
-	if (IS_ERR(fbi->smart_thread)) {
-		printk(KERN_ERR "%s: unable to create kernel thread\n",
-				__func__);
-		return PTR_ERR(fbi->smart_thread);
-	}
+	struct pxafb_output_spi_priv *spi = fbi->output_priv;
+
+	kthread_stop(spi->smart_thread);
+	dma_free_coherent(fbi->dev, CMD_BUFF_SIZE,
+			&spi->cmd_buff_dma, GFP_KERNEL);
+	kfree(spi);
+	fbi->output_priv = NULL;
 	return 0;
 }
+
+struct pxafb_output pxafb_output_spi = {
+	.name		= "smartpanel",
+	.init		= pxafb_spi_init,
+	.exit		= pxafb_spi_exit,
+	.check		= pxafb_spi_check,
+	.setup		= pxafb_spi_setup,
+	.enable		= pxafb_spi_enable,
+	.disable	= pxafb_spi_disable,
+	.handle_irq	= pxafb_spi_irq,
+};
 #else
-int pxafb_smart_queue(struct fb_info *info, uint16_t *cmds, int n_cmds)
+#define pxafb_output_spi	pxafb_output_dummy
+
+int pxafb_smart_flush(struct fb_info *info)
 {
 	return 0;
 }

-int pxafb_smart_flush(struct fb_info *info)
+int pxafb_smart_queue(struct fb_info *info, uint16_t *cmds, int n_cmds)
 {
 	return 0;
 }
-#endif /* CONFIG_FB_SMART_PANEL */
+#endif

-static void setup_parallel_timing(struct pxafb_info *fbi,
-				  struct fb_var_screeninfo *var)
+static int pxafb_output_init(struct pxafb_info *fbi,
+			     struct pxafb_output *output)
 {
-	unsigned int lines_per_panel, pcd = get_pcd(fbi, var->pixclock);
+	if (output == NULL)
+		output = &pxafb_output_dummy;

-	fbi->reg_lccr1 =
-		LCCR1_DisWdth(var->xres) +
-		LCCR1_HorSnchWdth(var->hsync_len) +
-		LCCR1_BegLnDel(var->left_margin) +
-		LCCR1_EndLnDel(var->right_margin);
+	if (fbi->output) {
+		fbi->output->exit(fbi);
+		fbi->output = NULL;
+		return 0;
+	}

-	/*
-	 * If we have a dual scan LCD, we need to halve
-	 * the YRES parameter.
-	 */
-	lines_per_panel = var->yres;
-	if ((fbi->lccr0 & LCCR0_SDS) == LCCR0_Dual)
-		lines_per_panel /= 2;
+	fbi->output = output;
+	return output->init(fbi);
+}

-	fbi->reg_lccr2 =
-		LCCR2_DisHght(lines_per_panel) +
-		LCCR2_VrtSnchWdth(var->vsync_len) +
-		LCCR2_BegFrmDel(var->upper_margin) +
-		LCCR2_EndFrmDel(var->lower_margin);
+static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal,
+		unsigned int offset, size_t size)
+{
+	struct pxafb_dma_descriptor *dma_desc, *pal_desc;
+	unsigned int dma_desc_off, pal_desc_off;

-	fbi->reg_lccr3 = fbi->lccr3 |
-		(var->sync & FB_SYNC_HOR_HIGH_ACT ?
-		 LCCR3_HorSnchH : LCCR3_HorSnchL) |
-		(var->sync & FB_SYNC_VERT_HIGH_ACT ?
-		 LCCR3_VrtSnchH : LCCR3_VrtSnchL);
+	if (dma < 0 || dma >= DMA_MAX)
+		return -EINVAL;

-	if (pcd) {
-		fbi->reg_lccr3 |= LCCR3_PixClkDiv(pcd);
-		set_hsync_time(fbi, pcd);
+	dma_desc = &fbi->dma_buff->dma_desc[dma];
+	dma_desc_off = offsetof(struct pxafb_dma_buff, dma_desc[dma]);
+
+	dma_desc->fsadr = fbi->screen_dma + offset;
+	dma_desc->fidr  = 0;
+	dma_desc->ldcmd = size;
+
+	if (pal < 0 || pal >= PAL_MAX) {
+		dma_desc->fdadr = fbi->dma_buff_phys + dma_desc_off;
+		fbi->fdadr[dma] = fbi->dma_buff_phys + dma_desc_off;
+	} else {
+		pal_desc = &fbi->dma_buff->pal_desc[dma];
+		pal_desc_off = offsetof(struct pxafb_dma_buff, dma_desc[pal]);
+
+		pal_desc->fsadr = fbi->dma_buff_phys + pal * PALETTE_SIZE;
+		pal_desc->fidr  = 0;
+
+		if ((fbi->lccr4 & LCCR4_PAL_FOR_MASK) == LCCR4_PAL_FOR_0)
+			pal_desc->ldcmd = fbi->palette_size * sizeof(u16);
+		else
+			pal_desc->ldcmd = fbi->palette_size * sizeof(u32);
+
+		pal_desc->ldcmd |= LDCMD_PAL;
+
+		/* flip back and forth between palette and frame buffer */
+		pal_desc->fdadr = fbi->dma_buff_phys + dma_desc_off;
+		dma_desc->fdadr = fbi->dma_buff_phys + pal_desc_off;
+		fbi->fdadr[dma] = fbi->dma_buff_phys + dma_desc_off;
 	}
+
+	return 0;
 }

 /*
@@ -856,12 +1097,8 @@ static int pxafb_activate_var(struct
fb_var_screeninfo *var,
 	/* Update shadow copy atomically */
 	local_irq_save(flags);

-#ifdef CONFIG_FB_PXA_SMARTPANEL
-	if (fbi->lccr0 & LCCR0_LCDT)
-		setup_smart_timing(fbi, var);
-	else
-#endif
-		setup_parallel_timing(fbi, var);
+	/* setup timing */
+	fbi->output->setup(fbi, var);

 	fbi->reg_lccr0 = fbi->lccr0 |
 		(LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM |
@@ -979,9 +1216,6 @@ static void pxafb_enable_controller(struct pxafb_info *fbi)
 	/* enable LCD controller clock */
 	clk_enable(fbi->clk);

-	if (fbi->lccr0 & LCCR0_LCDT)
-		return;
-
 	/* Sequence from 11.7.10 */
 	lcd_writel(fbi, LCCR3, fbi->reg_lccr3);
 	lcd_writel(fbi, LCCR2, fbi->reg_lccr2);
@@ -990,29 +1224,13 @@ static void pxafb_enable_controller(struct
pxafb_info *fbi)

 	lcd_writel(fbi, FDADR0, fbi->fdadr[0]);
 	lcd_writel(fbi, FDADR1, fbi->fdadr[1]);
-	lcd_writel(fbi, LCCR0, fbi->reg_lccr0 | LCCR0_ENB);
+
+	fbi->output->enable(fbi);
 }

 static void pxafb_disable_controller(struct pxafb_info *fbi)
 {
-	uint32_t lccr0;
-
-#ifdef CONFIG_FB_PXA_SMARTPANEL
-	if (fbi->lccr0 & LCCR0_LCDT) {
-		wait_for_completion_timeout(&fbi->refresh_done,
-				200 * HZ / 1000);
-		return;
-	}
-#endif
-
-	/* Clear LCD Status Register */
-	lcd_writel(fbi, LCSR, 0xffffffff);
-
-	lccr0 = lcd_readl(fbi, LCCR0) & ~LCCR0_LDM;
-	lcd_writel(fbi, LCCR0, lccr0);
-	lcd_writel(fbi, LCCR0, lccr0 | LCCR0_DIS);
-
-	wait_for_completion_timeout(&fbi->disable_done, 200 * HZ / 1000);
+	fbi->output->disable(fbi);

 	/* disable LCD controller clock */
 	clk_disable(fbi->clk);
@@ -1024,18 +1242,9 @@ static void pxafb_disable_controller(struct
pxafb_info *fbi)
 static irqreturn_t pxafb_handle_irq(int irq, void *dev_id)
 {
 	struct pxafb_info *fbi = dev_id;
-	unsigned int lccr0, lcsr = lcd_readl(fbi, LCSR);
+	uint32_t lcsr = lcd_readl(fbi, LCSR);

-	if (lcsr & LCSR_LDD) {
-		lccr0 = lcd_readl(fbi, LCCR0);
-		lcd_writel(fbi, LCCR0, lccr0 | LCCR0_LDM);
-		complete(&fbi->disable_done);
-	}
-
-#ifdef CONFIG_FB_PXA_SMARTPANEL
-	if (lcsr & LCSR_CMD_INT)
-		complete(&fbi->command_done);
-#endif
+	fbi->output->handle_irq(fbi, lcsr);

 	lcd_writel(fbi, LCSR, lcsr);
 	return IRQ_HANDLED;
@@ -1267,11 +1476,6 @@ static int __init pxafb_map_video_memory(struct
pxafb_info *fbi)
 		fbi->dma_buff = (void *) fbi->map_cpu;
 		fbi->dma_buff_phys = fbi->map_dma;
 		fbi->palette_cpu = (u16 *) fbi->dma_buff->palette;
-
-#ifdef CONFIG_FB_PXA_SMARTPANEL
-		fbi->smart_cmds = (uint16_t *) fbi->dma_buff->cmd_buff;
-		fbi->n_smart_cmds = 0;
-#endif
 	}

 	return fbi->map_cpu ? 0 : -ENOMEM;
@@ -1324,7 +1528,6 @@ static int pxafb_decode_mach_info(struct pxafb_info *fbi,
 		fbi->lccr0 = inf->lccr0;
 		fbi->lccr3 = inf->lccr3;
 		fbi->lccr4 = inf->lccr4;
-		return -EINVAL;
 	}

 	if (lcd_conn == LCD_MONO_STN_8BPP)
@@ -1335,6 +1538,12 @@ static int pxafb_decode_mach_info(struct pxafb_info *fbi,
 	fbi->lccr3 |= (lcd_conn & LCD_PCLK_EDGE_FALL)  ? LCCR3_PCP : 0;

 	pxafb_decode_mode_info(fbi, inf->modes, inf->num_modes);
+
+	if (fbi->lccr0 & LCCR0_LCDT)
+		pxafb_output_init(fbi, &pxafb_output_spi);
+	else
+		pxafb_output_init(fbi, &pxafb_output_ppi);
+
 	return 0;
 }

@@ -1343,7 +1552,6 @@ static struct pxafb_info * __init
pxafb_init_fbinfo(struct device *dev)
 	struct pxafb_info *fbi;
 	void *addr;
 	struct pxafb_mach_info *inf = dev->platform_data;
-	struct pxafb_mode_info *mode = inf->modes;

 	/* Alloc the pxafb_info and pseudo_palette in one step */
 	fbi = kmalloc(sizeof(struct pxafb_info) + sizeof(u32) * 16, GFP_KERNEL);
@@ -1391,11 +1599,6 @@ static struct pxafb_info * __init
pxafb_init_fbinfo(struct device *dev)
 	init_waitqueue_head(&fbi->ctrlr_wait);
 	INIT_WORK(&fbi->task, pxafb_task);
 	init_MUTEX(&fbi->ctrlr_sem);
-	init_completion(&fbi->disable_done);
-#ifdef CONFIG_FB_PXA_SMARTPANEL
-	init_completion(&fbi->command_done);
-	init_completion(&fbi->refresh_done);
-#endif

 	return fbi;
 }
@@ -1715,13 +1918,6 @@ static int __init pxafb_probe(struct
platform_device *dev)
 		goto failed_free_mem;
 	}

-#ifdef CONFIG_FB_PXA_SMARTPANEL
-	ret = pxafb_smart_init(fbi);
-	if (ret) {
-		dev_err(&dev->dev, "failed to initialize smartpanel\n");
-		goto failed_free_irq;
-	}
-#endif
 	/*
 	 * This makes sure that our colour bitfield
 	 * descriptors are correctly initialised.
diff --git a/drivers/video/pxafb.h b/drivers/video/pxafb.h
index 8238dc8..b2660bd 100644
--- a/drivers/video/pxafb.h
+++ b/drivers/video/pxafb.h
@@ -52,15 +52,26 @@ enum {

 /* maximum palette size - 256 entries, each 4 bytes long */
 #define PALETTE_SIZE	(256 * 4)
-#define CMD_BUFF_SIZE	(1024 * 50)

 struct pxafb_dma_buff {
 	unsigned char palette[PAL_MAX * PALETTE_SIZE];
-	uint16_t cmd_buff[CMD_BUFF_SIZE];
 	struct pxafb_dma_descriptor pal_desc[PAL_MAX];
 	struct pxafb_dma_descriptor dma_desc[DMA_MAX];
 };

+/* Output Interface - e.g. Parallel Port or Smart Panel */
+struct pxafb_info;
+struct pxafb_output {
+	const char *name;
+	int	(*init)(struct pxafb_info *);
+	int	(*exit)(struct pxafb_info *);
+	int	(*check)(struct pxafb_info *, struct fb_var_screeninfo *);
+	int	(*setup)(struct pxafb_info *, struct fb_var_screeninfo *);
+	void	(*enable)(struct pxafb_info *);
+	void	(*disable)(struct pxafb_info *);
+	void	(*handle_irq)(struct pxafb_info *, uint32_t lcsr);
+};
+
 struct pxafb_info {
 	struct fb_info		fb;
 	struct device		*dev;
@@ -110,15 +121,8 @@ struct pxafb_info {
 	wait_queue_head_t	ctrlr_wait;
 	struct work_struct	task;

-	struct completion	disable_done;
-
-#ifdef CONFIG_FB_PXA_SMARTPANEL
-	uint16_t		*smart_cmds;
-	size_t			n_smart_cmds;
-	struct completion	command_done;
-	struct completion	refresh_done;
-	struct task_struct	*smart_thread;
-#endif
+	struct pxafb_output	*output;
+	void			*output_priv;

 #ifdef CONFIG_CPU_FREQ
 	struct notifier_block	freq_transition;
-- 
1.5.4.3


Cheers
- eric

-------------------------------------------------------------------------
This SF.net email is sponsored by the 2008 JavaOne(SM) Conference 
Register now and save $200. Hurry, offer ends at 11:59 p.m., 
Monday, April 7! Use priority code J8TLD2. 
http://ad.doubleclick.net/clk;198757673;13503038;p?http://java.sun.com/javaone

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

end of thread, other threads:[~2008-04-16  0:23 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-04-08  9:59 [PATCH RFC] pxafb: introduce "struct pxafb_output" to abstract the output interface eric miao
2008-04-16  0:22 ` Andrew Morton

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).