lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Wed,  7 Jul 2010 15:12:06 +0200
From:	Thibaut Girka <thib@...edethib.com>
To:	Samuel Ortiz <sameo@...ux.intel.com>
Cc:	linux-kernel@...r.kernel.org, Thibaut Girka <thib@...edethib.com>,
	linux-fbdev@...r.kernel.org
Subject: [PATCH 4/4] Smedia Glamo 3362 Framebuffer driver

Signed-off-by: Thibaut Girka <thib@...edethib.com>
---
 drivers/video/Kconfig    |   17 +
 drivers/video/Makefile   |    1 +
 drivers/video/glamo-fb.c |  943 ++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/fb.h       |    1 +
 include/linux/glamofb.h  |   21 +
 5 files changed, 983 insertions(+), 0 deletions(-)
 create mode 100644 drivers/video/glamo-fb.c
 create mode 100644 include/linux/glamofb.h

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 3d94a14..8f9e4e4 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2229,6 +2229,23 @@ config FB_BROADSHEET
 	  and could also have been called by other names when coupled with
 	  a bridge adapter.
 
+config FB_GLAMO
+	tristate "Smedia Glamo 336x/337x framebuffer support"
+	depends on FB && MFD_GLAMO
+	select FB_CFB_FILLRECT
+	select FB_CFB_COPYAREA
+	select FB_CFB_IMAGEBLIT
+	help
+	  Frame buffer driver for the LCD controller in the Smedia Glamo
+	  336x/337x.
+
+	  This driver is also available as a module ( = code which can be
+	  inserted and removed from the running kernel whenever you want). The
+	  module will be called glamofb. If you want to compile it as a module,
+	  say M here and read <file:Documentation/modules.txt>.
+
+	  If unsure, say N.
+
 source "drivers/video/omap/Kconfig"
 source "drivers/video/omap2/Kconfig"
 
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index ddc2af2..90d949b 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -131,6 +131,7 @@ obj-$(CONFIG_FB_CARMINE)          += carminefb.o
 obj-$(CONFIG_FB_MB862XX)	  += mb862xx/
 obj-$(CONFIG_FB_MSM)              += msm/
 obj-$(CONFIG_FB_NUC900)           += nuc900fb.o
+obj-$(CONFIG_FB_GLAMO)            += glamo-fb.o
 
 # Platform or fallback drivers go here
 obj-$(CONFIG_FB_UVESA)            += uvesafb.o
diff --git a/drivers/video/glamo-fb.c b/drivers/video/glamo-fb.c
new file mode 100644
index 0000000..3f8ec8d
--- /dev/null
+++ b/drivers/video/glamo-fb.c
@@ -0,0 +1,943 @@
+/* Smedia Glamo 336x/337x driver
+ *
+ * (C) 2007-2008 by Openmoko, Inc.
+ * Author: Harald Welte <laforge@...nmoko.org>
+ * 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 as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/mfd/glamo.h>
+#include <linux/mfd/glamo-core.h>
+#include <linux/mfd/glamo-regs.h>
+
+#include <asm/div64.h>
+
+#ifdef CONFIG_PM
+#include <linux/pm.h>
+#endif
+
+#include <linux/glamofb.h>
+
+struct glamofb_handle {
+	struct glamo_core *core;
+	struct fb_info *fb;
+	struct device *dev;
+
+	struct resource *reg;
+	struct resource *fb_res;
+	void __iomem *base;
+	void __iomem *cursor_addr;
+
+	struct glamo_fb_platform_data *mach_info;
+
+	int cursor_on;
+	int blank_mode;
+	int mode_set; /* 0 if the current display mode hasn't been set on the glamo */
+	int output_enabled; /* 0 if the video output is disabled */
+
+	spinlock_t lock_cmd;
+	uint32_t pseudo_pal[16];
+};
+
+static void glamofb_program_mode(struct glamofb_handle *glamo);
+
+static void glamo_output_enable(struct glamofb_handle *gfb)
+{
+	struct glamo_core *gcore = gfb->core;
+
+	if (gfb->output_enabled)
+		return;
+
+	/* enable the pixel clock if off */
+	glamo_engine_enable(gcore, GLAMO_ENGINE_LCD);
+
+	gfb->output_enabled = 1;
+	if (!gfb->mode_set)
+		glamofb_program_mode(gfb);
+}
+
+static void glamo_output_disable(struct glamofb_handle *gfb)
+{
+	struct glamo_core *gcore = gfb->core;
+
+	if (!gfb->output_enabled)
+		return;
+
+	/* enable the pixel clock if off */
+	glamo_engine_suspend(gcore, GLAMO_ENGINE_LCD);
+
+	gfb->output_enabled = 0;
+}
+
+
+static inline int glamofb_reg_read(struct glamofb_handle *glamo, uint16_t reg)
+{
+	return readw(glamo->base + reg);
+}
+
+static inline void glamofb_reg_write(struct glamofb_handle *glamo, uint16_t reg,
+	uint16_t val)
+{
+	writew(val, glamo->base + reg);
+}
+
+static const struct glamo_script glamo_regs[] = {
+	{ GLAMO_REG_LCD_MODE1, 0x0020 },
+	/* no display rotation, no hardware cursor, no dither, no gamma,
+	 * no retrace flip, vsync low-active, hsync low active,
+	 * no TVCLK, no partial display, hw dest color from fb,
+	 * no partial display mode, LCD1, software flip,  */
+	{ GLAMO_REG_LCD_MODE2, 0x9020 },
+	  /* video flip, no ptr, no ptr, dhclk off,
+	   * normal mode,  no cpuif,
+	   * res, serial msb first, single fb, no fr ctrl,
+	   * cpu if bits all zero, no crc
+	   * 0000 0000 0010  0000 */
+	{ GLAMO_REG_LCD_MODE3, 0x0b40 },
+	  /* src data rgb565, res, 18bit rgb666
+	   * 000 01 011 0100 0000 */
+	{ GLAMO_REG_LCD_POLARITY, 0x440c },
+	  /* DE high active, no cpu/lcd if, cs0 force low, a0 low active,
+	   * np cpu if, 9bit serial data, sclk rising edge latch data
+	   * 01 00 0 100 0 000 01 0 0 */
+	/* The following values assume 640*480@...pp */
+	{ GLAMO_REG_LCD_A_BASE1, 0x0000 }, /* display A base address 15:0 */
+	{ GLAMO_REG_LCD_A_BASE2, 0x4000 }, /* display A base address 22:16 */
+	{ GLAMO_REG_LCD_CURSOR_BASE1, 0xC000 }, /* cursor base address 15:0 */
+	{ GLAMO_REG_LCD_CURSOR_BASE2, 0x0012 }, /* cursor base address 22:16 */
+	{ GLAMO_REG_LCD_COMMAND2, 0x0000 }, /* display page A */
+};
+
+static int glamofb_run_script(struct glamofb_handle *glamo,
+	const struct glamo_script *script, size_t len)
+{
+	size_t i;
+
+	for (i = 0; i < len; i++) {
+		const struct glamo_script *line = &script[i];
+
+		if (line->reg == 0xffff)
+			return 0;
+		else if (line->reg == 0xfffe)
+			msleep(line->val);
+		else
+			glamofb_reg_write(glamo, script[i].reg, script[i].val);
+	}
+
+	return 0;
+}
+
+static int glamofb_check_var(struct fb_var_screeninfo *var,
+	struct fb_info *info)
+{
+	struct glamofb_handle *glamo = info->par;
+
+	if (var->bits_per_pixel != 16)
+		var->bits_per_pixel = 16;
+
+	var->height = glamo->mach_info->height;
+	var->width = glamo->mach_info->width;
+
+	/* FIXME: set rgb positions */
+	switch (var->bits_per_pixel) {
+	case 16:
+		switch (glamofb_reg_read(glamo, GLAMO_REG_LCD_MODE3) & 0xc000) {
+		case GLAMO_LCD_SRC_RGB565:
+			var->red.offset		= 11;
+			var->green.offset	= 5;
+			var->blue.offset	= 0;
+			var->red.length		= 5;
+			var->green.length	= 6;
+			var->blue.length	= 5;
+			var->transp.length	= 0;
+			break;
+		case GLAMO_LCD_SRC_ARGB1555:
+			var->transp.offset	= 15;
+			var->red.offset		= 10;
+			var->green.offset	= 5;
+			var->blue.offset	= 0;
+			var->transp.length	= 1;
+			var->red.length		= 5;
+			var->green.length	= 5;
+			var->blue.length	= 5;
+			break;
+		case GLAMO_LCD_SRC_ARGB4444:
+			var->transp.offset	= 12;
+			var->red.offset		= 8;
+			var->green.offset	= 4;
+			var->blue.offset	= 0;
+			var->transp.length	= 4;
+			var->red.length		= 4;
+			var->green.length	= 4;
+			var->blue.length	= 4;
+			break;
+		}
+		break;
+	case 24:
+	case 32:
+	default:
+		/* The Smedia Glamo doesn't support anything but 16bit color */
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void glamofb_reg_set_bit_mask(struct glamofb_handle *glamo, uint16_t reg,
+	uint16_t mask, uint16_t val)
+{
+	uint16_t tmp;
+
+	val &= mask;
+
+	tmp = glamofb_reg_read(glamo, reg);
+	tmp &= ~mask;
+	tmp |= val;
+	glamofb_reg_write(glamo, reg, tmp);
+}
+
+#define GLAMO_LCD_WIDTH_MASK 0x03FF
+#define GLAMO_LCD_HEIGHT_MASK 0x03FF
+#define GLAMO_LCD_PITCH_MASK 0x07FE
+#define GLAMO_LCD_HV_TOTAL_MASK 0x03FF
+#define GLAMO_LCD_HV_RETR_START_MASK 0x03FF
+#define GLAMO_LCD_HV_RETR_END_MASK 0x03FF
+#define GLAMO_LCD_HV_RETR_DISP_START_MASK 0x03FF
+#define GLAMO_LCD_HV_RETR_DISP_END_MASK 0x03FF
+
+/* the caller has to ensure lock_cmd is held and we are in cmd mode */
+static void __rotate_lcd(struct glamofb_handle *glamo, uint32_t rotation)
+{
+	uint32_t glamo_rot;
+
+	switch (rotation) {
+	case FB_ROTATE_CW:
+		glamo_rot = GLAMO_LCD_ROT_MODE_90;
+		break;
+	case FB_ROTATE_UD:
+		glamo_rot = GLAMO_LCD_ROT_MODE_180;
+		break;
+	case FB_ROTATE_CCW:
+		glamo_rot = GLAMO_LCD_ROT_MODE_270;
+		break;
+	default:
+		glamo_rot = GLAMO_LCD_ROT_MODE_0;
+		break;
+	}
+
+	glamofb_reg_set_bit_mask(glamo,
+			 GLAMO_REG_LCD_WIDTH,
+			 GLAMO_LCD_ROT_MODE_MASK,
+			 glamo_rot);
+	glamofb_reg_set_bit_mask(glamo,
+			 GLAMO_REG_LCD_MODE1,
+			 GLAMO_LCD_MODE1_ROTATE_EN,
+			 (glamo_rot != GLAMO_LCD_ROT_MODE_0) ?
+				 GLAMO_LCD_MODE1_ROTATE_EN : 0);
+}
+
+static inline int glamofb_cmdq_empty(struct glamofb_handle *gfb)
+{
+	/* DGCMdQempty -- 1 == command queue is empty */
+	return glamofb_reg_read(gfb, GLAMO_REG_LCD_STATUS1) & (1 << 15);
+}
+
+/* call holding gfb->lock_cmd  when locking, until you unlock */
+static int glamofb_cmd_mode(struct glamofb_handle *gfb, int on)
+{
+	int timeout = 2000000;
+
+	dev_dbg(gfb->dev, "glamofb_cmd_mode(gfb=%p, on=%d)\n", gfb, on);
+	if (on) {
+		dev_dbg(gfb->dev, "%s: waiting for cmdq empty: ",
+			__func__);
+		while (!glamofb_cmdq_empty(gfb) && (timeout--))
+			cpu_relax();
+		if (timeout < 0) {
+			printk(KERN_ERR "glamofb cmd_queue never got empty\n");
+			return -EIO;
+		}
+		dev_dbg(gfb->dev, "empty!\n");
+
+		/* display the entire frame then switch to command */
+		glamofb_reg_write(gfb, GLAMO_REG_LCD_COMMAND1,
+			  GLAMO_LCD_CMD_TYPE_DISP |
+			  GLAMO_LCD_CMD_DATA_FIRE_VSYNC);
+
+		/* wait until lcd idle */
+		dev_dbg(gfb->dev, "waiting for lcd idle: ");
+		timeout = 2000000;
+		while (!(glamofb_reg_read(gfb, GLAMO_REG_LCD_STATUS2) & (1 << 12)) &&
+		      (timeout--))
+			cpu_relax();
+		if (timeout < 0) {
+			printk(KERN_ERR"*************"
+				       "glamofb lcd never idle"
+				       "*************\n");
+			return -EIO;
+		}
+
+		mdelay(100);
+
+		dev_dbg(gfb->dev, "cmd mode entered\n");
+
+	} else {
+		/* RGB interface needs vsync/hsync */
+		if (glamofb_reg_read(gfb, GLAMO_REG_LCD_MODE3) & GLAMO_LCD_MODE3_RGB)
+			glamofb_reg_write(gfb, GLAMO_REG_LCD_COMMAND1,
+				  GLAMO_LCD_CMD_TYPE_DISP |
+				  GLAMO_LCD_CMD_DATA_DISP_SYNC);
+
+		glamofb_reg_write(gfb, GLAMO_REG_LCD_COMMAND1,
+			  GLAMO_LCD_CMD_TYPE_DISP |
+			  GLAMO_LCD_CMD_DATA_DISP_FIRE);
+	}
+
+	return 0;
+}
+
+static void glamofb_program_mode(struct glamofb_handle *gfb)
+{
+	unsigned int sync, bp, disp, fp, total;
+	unsigned long flags;
+	struct glamo_core *gcore = gfb->core;
+	struct fb_var_screeninfo *var = &gfb->fb->var;
+
+	dev_dbg(&gcore->pdev->dev,
+			  "glamofb_program_mode spin_lock_irqsave\n");
+	spin_lock_irqsave(&gfb->lock_cmd, flags);
+
+	if (glamofb_cmd_mode(gfb, 1))
+		goto out_unlock;
+
+	if (var->pixclock)
+		glamo_engine_reclock(gcore, GLAMO_ENGINE_LCD,
+				 (1000000000UL / gfb->fb->var.pixclock) * 1000);
+
+	glamofb_reg_set_bit_mask(gfb,
+			 GLAMO_REG_LCD_WIDTH,
+			 GLAMO_LCD_WIDTH_MASK,
+			 var->xres);
+	glamofb_reg_set_bit_mask(gfb,
+			 GLAMO_REG_LCD_HEIGHT,
+			 GLAMO_LCD_HEIGHT_MASK,
+			 var->yres);
+	glamofb_reg_set_bit_mask(gfb,
+			 GLAMO_REG_LCD_PITCH,
+			 GLAMO_LCD_PITCH_MASK,
+			 gfb->fb->fix.line_length);
+
+	/* honour the rotation request */
+	__rotate_lcd(gfb, var->rotate);
+
+	/* update scannout timings */
+	sync = 0;
+	bp = sync + var->hsync_len;
+	disp = bp + var->left_margin;
+	fp = disp + var->xres;
+	total = fp + var->right_margin;
+
+	glamofb_reg_set_bit_mask(gfb, GLAMO_REG_LCD_HORIZ_TOTAL,
+			 GLAMO_LCD_HV_TOTAL_MASK, total);
+	glamofb_reg_set_bit_mask(gfb, GLAMO_REG_LCD_HORIZ_RETR_START,
+			 GLAMO_LCD_HV_RETR_START_MASK, sync);
+	glamofb_reg_set_bit_mask(gfb, GLAMO_REG_LCD_HORIZ_RETR_END,
+			 GLAMO_LCD_HV_RETR_END_MASK, bp);
+	glamofb_reg_set_bit_mask(gfb, GLAMO_REG_LCD_HORIZ_DISP_START,
+			  GLAMO_LCD_HV_RETR_DISP_START_MASK, disp);
+	glamofb_reg_set_bit_mask(gfb, GLAMO_REG_LCD_HORIZ_DISP_END,
+			 GLAMO_LCD_HV_RETR_DISP_END_MASK, fp);
+
+	sync = 0;
+	bp = sync + var->vsync_len;
+	disp = bp + var->upper_margin;
+	fp = disp + var->yres;
+	total = fp + var->lower_margin;
+
+	glamofb_reg_set_bit_mask(gfb, GLAMO_REG_LCD_VERT_TOTAL,
+			 GLAMO_LCD_HV_TOTAL_MASK, total);
+	glamofb_reg_set_bit_mask(gfb, GLAMO_REG_LCD_VERT_RETR_START,
+			  GLAMO_LCD_HV_RETR_START_MASK, sync);
+	glamofb_reg_set_bit_mask(gfb, GLAMO_REG_LCD_VERT_RETR_END,
+			 GLAMO_LCD_HV_RETR_END_MASK, bp);
+	glamofb_reg_set_bit_mask(gfb, GLAMO_REG_LCD_VERT_DISP_START,
+			 GLAMO_LCD_HV_RETR_DISP_START_MASK, disp);
+	glamofb_reg_set_bit_mask(gfb, GLAMO_REG_LCD_VERT_DISP_END,
+			 GLAMO_LCD_HV_RETR_DISP_END_MASK, fp);
+
+	glamofb_cmd_mode(gfb, 0);
+
+	gfb->mode_set = 1;
+
+out_unlock:
+	dev_dbg(&gcore->pdev->dev,
+		  "glamofb_program_mode spin_unlock_irqrestore\n");
+	spin_unlock_irqrestore(&gfb->lock_cmd, flags);
+}
+
+static struct fb_videomode *glamofb_find_mode(struct fb_info *info,
+	struct fb_var_screeninfo *var)
+{
+	struct glamofb_handle *glamo = info->par;
+	struct glamo_fb_platform_data *pdata = glamo->mach_info;
+	struct fb_videomode *mode;
+	int i;
+
+	for (i = pdata->num_modes, mode = pdata->modes; i; --i, ++mode) {
+		if (mode->xres == var->xres &&
+			mode->yres == var->yres)
+			return mode;
+	}
+
+	return NULL;
+}
+
+static int glamofb_set_par(struct fb_info *info)
+{
+	struct glamofb_handle *glamo = info->par;
+	struct fb_var_screeninfo *var = &info->var;
+	struct fb_videomode *mode;
+
+	mode = glamofb_find_mode(info, var);
+	if (!mode)
+		return -EINVAL;
+
+	fb_videomode_to_var(var, mode);
+
+	info->mode = mode;
+
+	glamo->mode_set = 0;
+
+	switch (var->rotate) {
+	case FB_ROTATE_CW:
+	case FB_ROTATE_CCW:
+		info->fix.line_length = (var->yres * var->bits_per_pixel) / 8;
+		/* FIXME: Limit pixelclock */
+		var->pixclock *= 2;
+		break;
+	default:
+		info->fix.line_length = (var->xres * var->bits_per_pixel) / 8;
+		break;
+	}
+
+	if (glamo->output_enabled)
+		glamofb_program_mode(glamo);
+
+	return 0;
+}
+
+static int glamofb_blank(int blank_mode, struct fb_info *info)
+{
+	struct glamofb_handle *gfb = info->par;
+
+	dev_dbg(gfb->dev, "glamofb_blank(%u)\n", blank_mode);
+
+	switch (blank_mode) {
+	case FB_BLANK_VSYNC_SUSPEND:
+	case FB_BLANK_HSYNC_SUSPEND:
+		/* FIXME: add pdata hook/flag to indicate whether
+		 * we should already switch off pixel clock here */
+		break;
+	case FB_BLANK_POWERDOWN:
+		/* disable the pixel clock */
+		glamo_output_disable(gfb);
+		gfb->blank_mode = blank_mode;
+		break;
+	case FB_BLANK_UNBLANK:
+	case FB_BLANK_NORMAL:
+		glamo_output_enable(gfb);
+		gfb->blank_mode = blank_mode;
+		break;
+	}
+
+	/* FIXME: once we have proper clock management in glamo-core,
+	 * we can determine if other units need MCLK1 or the PLL, and
+	 * disable it if not used. */
+	return 0;
+}
+
+static inline unsigned int chan_to_field(unsigned int chan,
+	struct fb_bitfield *bf)
+{
+	chan &= 0xffff;
+	chan >>= 16 - bf->length;
+	return chan << bf->offset;
+}
+
+static int glamofb_setcolreg(unsigned regno, unsigned red, unsigned green,
+	unsigned blue, unsigned transp, struct fb_info *info)
+{
+	struct glamofb_handle *glamo = info->par;
+	unsigned int val;
+
+	switch (glamo->fb->fix.visual) {
+	case FB_VISUAL_TRUECOLOR:
+	case FB_VISUAL_DIRECTCOLOR:
+		/* true-colour, use pseuo-palette */
+
+		if (regno < 16) {
+			u32 *pal = glamo->fb->pseudo_palette;
+
+			val  = chan_to_field(red, &glamo->fb->var.red);
+			val |= chan_to_field(green, &glamo->fb->var.green);
+			val |= chan_to_field(blue, &glamo->fb->var.blue);
+
+			pal[regno] = val;
+		};
+		break;
+	default:
+		return 1; /* unknown type */
+	}
+
+	return 0;
+}
+
+static int glamofb_ioctl(struct fb_info *info, unsigned int cmd,
+	unsigned long arg)
+{
+	struct glamofb_handle *gfb = (struct glamofb_handle *)info->par;
+	struct glamo_core *gcore = gfb->core;
+	int retval = -ENOTTY;
+
+	switch (cmd) {
+	case GLAMOFB_ENGINE_ENABLE:
+		retval = glamo_engine_enable(gcore, arg);
+		break;
+	case GLAMOFB_ENGINE_DISABLE:
+		retval = glamo_engine_disable(gcore, arg);
+		break;
+	case GLAMOFB_ENGINE_RESET:
+		glamo_engine_reset(gcore, arg);
+		retval = 0;
+		break;
+	default:
+		break;
+	}
+
+	return retval;
+}
+
+
+#ifdef CONFIG_MFD_GLAMO_HWACCEL
+static inline void glamofb_vsync_wait(struct glamofb_handle *glamo, int line,
+	int size, int range)
+{
+	int count[2];
+
+	do {
+		count[0] = glamofb_reg_read(glamo, GLAMO_REG_LCD_STATUS2) & 0x3ff;
+		count[1] = glamofb_reg_read(glamo, GLAMO_REG_LCD_STATUS2) & 0x3ff;
+	} while (count[0] != count[1] ||
+			(line < count[0] + range &&
+			 size > count[0] - range) ||
+			count[0] < range * 2);
+}
+
+/*
+ * Enable/disable the hardware cursor mode altogether
+ * (for blinking and such, use glamofb_cursor()).
+ */
+static void glamofb_cursor_onoff(struct glamofb_handle *glamo, int on)
+{
+	int y, size;
+
+	if (glamo->cursor_on) {
+		y = glamofb_reg_read(glamo, GLAMO_REG_LCD_CURSOR_Y_POS);
+		size = glamofb_reg_read(glamo, GLAMO_REG_LCD_CURSOR_Y_SIZE);
+
+		glamofb_vsync_wait(glamo, y, size, 30);
+	}
+
+	glamofb_reg_set_bit_mask(glamo, GLAMO_REG_LCD_MODE1,
+			GLAMO_LCD_MODE1_CURSOR_EN,
+			on ? GLAMO_LCD_MODE1_CURSOR_EN : 0);
+	glamo->cursor_on = on;
+
+	/* Hide the cursor by default */
+	glamofb_reg_write(glamo, GLAMO_REG_LCD_CURSOR_X_SIZE, 0);
+}
+
+static int glamofb_cursor(struct fb_info *info, struct fb_cursor *cursor)
+{
+	struct glamofb_handle *glamo = info->par;
+	unsigned long flags;
+
+	spin_lock_irqsave(&glamo->lock_cmd, flags);
+
+	glamofb_reg_write(glamo, GLAMO_REG_LCD_CURSOR_X_SIZE,
+			cursor->enable ? cursor->image.width : 0);
+
+	if (cursor->set & FB_CUR_SETPOS) {
+		glamofb_reg_write(glamo, GLAMO_REG_LCD_CURSOR_X_POS,
+			  cursor->image.dx);
+		glamofb_reg_write(glamo, GLAMO_REG_LCD_CURSOR_Y_POS,
+			  cursor->image.dy);
+	}
+
+	if (cursor->set & FB_CUR_SETCMAP) {
+		uint16_t fg = glamo->pseudo_pal[cursor->image.fg_color];
+		uint16_t bg = glamo->pseudo_pal[cursor->image.bg_color];
+
+		glamofb_reg_write(glamo, GLAMO_REG_LCD_CURSOR_FG_COLOR, fg);
+		glamofb_reg_write(glamo, GLAMO_REG_LCD_CURSOR_BG_COLOR, bg);
+		glamofb_reg_write(glamo, GLAMO_REG_LCD_CURSOR_DST_COLOR, fg);
+	}
+
+	if (cursor->set & FB_CUR_SETHOT)
+		glamofb_reg_write(glamo, GLAMO_REG_LCD_CURSOR_PRESET,
+				(cursor->hot.x << 8) | cursor->hot.y);
+
+	if ((cursor->set & FB_CUR_SETSIZE) ||
+	    (cursor->set & (FB_CUR_SETIMAGE | FB_CUR_SETSHAPE))) {
+		int x, y, pitch, op;
+		const uint8_t *pcol = cursor->image.data;
+		const uint8_t *pmsk = cursor->mask;
+		uint8_t __iomem *dst = glamo->cursor_addr;
+		uint8_t dcol = 0;
+		uint8_t dmsk = 0;
+		uint8_t byte = 0;
+
+		if (cursor->image.depth > 1) {
+			spin_unlock_irqrestore(&glamo->lock_cmd, flags);
+			return -EINVAL;
+		}
+
+		pitch = ((cursor->image.width + 7) >> 2) & ~1;
+		glamofb_reg_write(glamo, GLAMO_REG_LCD_CURSOR_PITCH,
+			pitch);
+		glamofb_reg_write(glamo, GLAMO_REG_LCD_CURSOR_Y_SIZE,
+			cursor->image.height);
+
+		for (y = 0; y < cursor->image.height; y++) {
+			byte = 0;
+			for (x = 0; x < cursor->image.width; x++) {
+				if ((x % 8) == 0) {
+					dcol = *pcol++;
+					dmsk = *pmsk++;
+				} else {
+					dcol >>= 1;
+					dmsk >>= 1;
+				}
+
+				if (cursor->rop == ROP_COPY)
+					op = (dmsk & 1) ?
+						(dcol & 1) ? 1 : 3 : 0;
+				else
+					op = ((dmsk & 1) << 1) |
+						((dcol & 1) << 0);
+				byte |= op << ((x & 3) << 1);
+
+				if (x % 4 == 3) {
+					writeb(byte, dst + x / 4);
+					byte = 0;
+				}
+			}
+			if (x % 4) {
+				writeb(byte, dst + x / 4);
+				byte = 0;
+			}
+
+			dst += pitch;
+		}
+	}
+
+	spin_unlock_irqrestore(&glamo->lock_cmd, flags);
+
+	return 0;
+}
+#endif
+
+static struct fb_ops glamofb_ops = {
+	.owner		= THIS_MODULE,
+	.fb_check_var	= glamofb_check_var,
+	.fb_set_par	= glamofb_set_par,
+	.fb_blank	= glamofb_blank,
+	.fb_setcolreg	= glamofb_setcolreg,
+	.fb_ioctl = glamofb_ioctl,
+#ifdef CONFIG_MFD_GLAMO_HWACCEL
+	.fb_cursor	= glamofb_cursor,
+#endif
+	.fb_fillrect	= cfb_fillrect,
+	.fb_copyarea	= cfb_copyarea,
+	.fb_imageblit	= cfb_imageblit,
+};
+
+static int glamofb_init_regs(struct glamofb_handle *glamo)
+{
+	struct fb_info *info = glamo->fb;
+
+	glamofb_check_var(&info->var, info);
+	glamofb_run_script(glamo, glamo_regs, ARRAY_SIZE(glamo_regs));
+	glamofb_set_par(info);
+
+	return 0;
+}
+
+static int __devinit glamofb_probe(struct platform_device *pdev)
+{
+	int rc = -EIO;
+	struct fb_info *fbinfo;
+	struct glamofb_handle *glamofb;
+	struct glamo_core *core = dev_get_drvdata(pdev->dev.parent);
+	struct glamo_fb_platform_data *mach_info;
+
+	printk(KERN_INFO "SMEDIA Glamo frame buffer driver (C) 2007 "
+		"Openmoko, Inc.\n");
+
+	if (!core->pdata || !core->pdata->fb_data)
+		return -ENOENT;
+
+
+	fbinfo = framebuffer_alloc(sizeof(struct glamofb_handle), &pdev->dev);
+	if (!fbinfo)
+		return -ENOMEM;
+
+
+	glamofb = fbinfo->par;
+	glamofb->fb = fbinfo;
+	glamofb->dev = &pdev->dev;
+
+	glamofb->blank_mode = FB_BLANK_POWERDOWN;
+
+	strcpy(fbinfo->fix.id, "SMedia Glamo");
+
+	glamofb->reg = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						    "glamo-fb-regs");
+	if (!glamofb->reg) {
+		dev_err(&pdev->dev, "platform device with no registers?\n");
+		rc = -ENOENT;
+		goto out_free;
+	}
+
+	glamofb->fb_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+							"glamo-fb-mem");
+	if (!glamofb->fb_res) {
+		dev_err(&pdev->dev, "platform device with no memory ?\n");
+		rc = -ENOENT;
+		goto out_free;
+	}
+
+	glamofb->reg = request_mem_region(glamofb->reg->start,
+					      resource_size(glamofb->reg),
+					      pdev->name);
+	if (!glamofb->reg) {
+		dev_err(&pdev->dev, "failed to request mmio region\n");
+		goto out_free;
+	}
+
+	glamofb->fb_res = request_mem_region(glamofb->fb_res->start,
+						 resource_size(glamofb->fb_res),
+						 pdev->name);
+	if (!glamofb->fb_res) {
+		dev_err(&pdev->dev, "failed to request vram region\n");
+		goto out_release_reg;
+	}
+
+	/* we want to remap only the registers required for this core
+	 * driver. */
+	glamofb->base = ioremap_nocache(glamofb->reg->start,
+					resource_size(glamofb->reg));
+	if (!glamofb->base) {
+		dev_err(&pdev->dev, "failed to ioremap() mmio memory\n");
+		goto out_release_fb;
+	}
+
+	fbinfo->fix.smem_start = (unsigned long)glamofb->fb_res->start;
+	fbinfo->fix.smem_len = (__u32)resource_size(glamofb->fb_res);
+
+	fbinfo->screen_base = ioremap(glamofb->fb_res->start,
+					   resource_size(glamofb->fb_res));
+	if (!fbinfo->screen_base) {
+		dev_err(&pdev->dev, "failed to ioremap() vram memory\n");
+		goto out_release_fb;
+	}
+	glamofb->cursor_addr = fbinfo->screen_base + 0x12C000;
+
+	platform_set_drvdata(pdev, glamofb);
+
+	mach_info = core->pdata->fb_data;
+	glamofb->core = core;
+	glamofb->mach_info = mach_info;
+
+	fbinfo->fix.visual = FB_VISUAL_TRUECOLOR;
+	fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
+	fbinfo->fix.type_aux = 0;
+	fbinfo->fix.xpanstep = 0;
+	fbinfo->fix.ypanstep = 0;
+	fbinfo->fix.ywrapstep = 0;
+	fbinfo->fix.accel = FB_ACCEL_GLAMO;
+
+
+	fbinfo->fbops = &glamofb_ops;
+	fbinfo->flags = FBINFO_FLAG_DEFAULT;
+	fbinfo->pseudo_palette = &glamofb->pseudo_pal;
+
+	fbinfo->mode = mach_info->modes;
+	fb_videomode_to_var(&fbinfo->var, fbinfo->mode);
+	fbinfo->var.bits_per_pixel = 16;
+	fbinfo->var.nonstd = 0;
+	fbinfo->var.activate = FB_ACTIVATE_NOW;
+	fbinfo->var.height = mach_info->height;
+	fbinfo->var.width = mach_info->width;
+	fbinfo->var.accel_flags = 0;
+	fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
+
+	glamo_engine_enable(core, GLAMO_ENGINE_LCD);
+	glamo_engine_reset(core, GLAMO_ENGINE_LCD);
+	glamofb->output_enabled = 1;
+	glamofb->mode_set = 1;
+
+	dev_info(&pdev->dev, "spin_lock_init\n");
+	spin_lock_init(&glamofb->lock_cmd);
+	glamofb_init_regs(glamofb);
+#ifdef CONFIG_MFD_GLAMO_HWACCEL
+	glamofb_cursor_onoff(glamofb, 1);
+#endif
+
+	fb_videomode_to_modelist(mach_info->modes, mach_info->num_modes,
+				 &fbinfo->modelist);
+
+	rc = register_framebuffer(fbinfo);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "failed to register framebuffer\n");
+		goto out_unmap_fb;
+	}
+
+	printk(KERN_INFO "fb%d: %s frame buffer device\n",
+		fbinfo->node, fbinfo->fix.id);
+
+	return 0;
+
+out_unmap_fb:
+	iounmap(fbinfo->screen_base);
+	iounmap(glamofb->base);
+out_release_fb:
+	release_mem_region(glamofb->fb_res->start,
+				resource_size(glamofb->fb_res));
+out_release_reg:
+	release_mem_region(glamofb->reg->start,
+				resource_size(glamofb->reg));
+out_free:
+	framebuffer_release(fbinfo);
+	return rc;
+}
+
+static int __devexit glamofb_remove(struct platform_device *pdev)
+{
+	struct glamofb_handle *glamofb = platform_get_drvdata(pdev);
+
+	iounmap(glamofb->fb->screen_base);
+	iounmap(glamofb->base);
+
+	release_mem_region(glamofb->fb_res->start,
+				resource_size(glamofb->fb_res));
+	release_mem_region(glamofb->reg->start, resource_size(glamofb->reg));
+
+	platform_set_drvdata(pdev, NULL);
+	framebuffer_release(glamofb->fb);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int glamofb_suspend(struct device *dev)
+{
+	struct glamofb_handle *gfb = dev_get_drvdata(dev);
+
+	acquire_console_sem();
+	fb_set_suspend(gfb->fb, 1);
+	release_console_sem();
+
+	/* seriously -- nobody is allowed to touch glamo memory when we
+	 * are suspended or we lock on nWAIT
+	 */
+	/* iounmap(gfb->fb->screen_base); */
+
+	return 0;
+}
+
+static int glamofb_resume(struct device *dev)
+{
+	struct glamofb_handle *gfb = dev_get_drvdata(dev);
+
+	/* OK let's allow framebuffer ops again */
+	/* gfb->fb->screen_base = ioremap(gfb->fb_res->start,
+					   resource_size(gfb->fb_res)); */
+	glamo_engine_enable(gfb->core, GLAMO_ENGINE_LCD);
+	glamo_engine_reset(gfb->core, GLAMO_ENGINE_LCD);
+
+	glamofb_init_regs(gfb);
+#ifdef CONFIG_MFD_GLAMO_HWACCEL
+	glamofb_cursor_onoff(gfb, 1);
+#endif
+
+	acquire_console_sem();
+	fb_set_suspend(gfb->fb, 0);
+	release_console_sem();
+
+	return 0;
+}
+
+static const struct dev_pm_ops glamofb_pm_ops = {
+	.suspend = glamofb_suspend,
+	.resume = glamofb_resume,
+};
+
+#define GLAMOFB_PM_OPS (&glamofb_pm_ops)
+
+#else
+#define GLAMOFB_PM_OPS NULL
+#endif
+
+static struct platform_driver glamofb_driver = {
+	.probe		= glamofb_probe,
+	.remove		= __devexit_p(glamofb_remove),
+	.driver		= {
+		.name	= "glamo-fb",
+		.owner	= THIS_MODULE,
+		.pm	= GLAMOFB_PM_OPS
+	},
+};
+
+static int __init glamofb_init(void)
+{
+	return platform_driver_register(&glamofb_driver);
+}
+module_init(glamofb_init);
+
+static void __exit glamofb_cleanup(void)
+{
+	platform_driver_unregister(&glamofb_driver);
+}
+module_exit(glamofb_cleanup);
+
+MODULE_AUTHOR("Harald Welte <laforge@...nmoko.org>");
+MODULE_DESCRIPTION("Smedia Glamo 336x/337x framebuffer driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:glamo-fb");
diff --git a/include/linux/fb.h b/include/linux/fb.h
index 907ace3..d6a03c2 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -132,6 +132,7 @@
 #define FB_ACCEL_NEOMAGIC_NM2360 97	/* NeoMagic NM2360              */
 #define FB_ACCEL_NEOMAGIC_NM2380 98	/* NeoMagic NM2380              */
 #define FB_ACCEL_PXA3XX		 99	/* PXA3xx			*/
+#define FB_ACCEL_GLAMO		100	/* Smedia Glamo 3362            */
 
 #define FB_ACCEL_SAVAGE4        0x80	/* S3 Savage4                   */
 #define FB_ACCEL_SAVAGE3D       0x81	/* S3 Savage3D                  */
diff --git a/include/linux/glamofb.h b/include/linux/glamofb.h
new file mode 100644
index 0000000..896579b
--- /dev/null
+++ b/include/linux/glamofb.h
@@ -0,0 +1,21 @@
+#ifndef _LINUX_GLAMOFB_H
+#define _LINUX_GLAMOFB_H
+
+#ifdef __KERNEL__
+
+#include <linux/fb.h>
+
+struct glamo_fb_platform_data {
+    int width, height;
+
+    int num_modes;
+    struct fb_videomode *modes;
+};
+
+#endif
+
+#define GLAMOFB_ENGINE_ENABLE _IOW('F', 0x1, __u32)
+#define GLAMOFB_ENGINE_DISABLE _IOW('F', 0x2, __u32)
+#define GLAMOFB_ENGINE_RESET _IOW('F', 0x3, __u32)
+
+#endif
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists