au1100fb.c 18.8 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4
/*
 * BRIEF MODULE DESCRIPTION
 *	Au1100 LCD Driver.
 *
P
Pete Popov 已提交
5 6 7 8 9
 * Rewritten for 2.6 by Embedded Alley Solutions
 * 	<source@embeddedalley.com>, based on submissions by
 *  	Karl Lessard <klessard@sunrisetelecom.com>
 *  	<c.pellegrin@exadron.com>
 *
L
Linus Torvalds 已提交
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
 * Copyright 2002 MontaVista Software
 * Author: MontaVista Software, Inc.
 *		ppopov@mvista.com or source@mvista.com
 *
 * Copyright 2002 Alchemy Semiconductor
 * Author: Alchemy Semiconductor
 *
 * Based on:
 * linux/drivers/video/skeletonfb.c -- Skeleton for a frame buffer device
 *  Created 28 Dec 1997 by Geert Uytterhoeven
 *
 *  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  SOFTWARE  IS PROVIDED	  ``AS	IS'' AND   ANY	EXPRESS OR IMPLIED
 *  WARRANTIES,	  INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
 *  NO	EVENT  SHALL   THE AUTHOR  BE	 LIABLE FOR ANY	  DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED	  TO, PROCUREMENT OF  SUBSTITUTE GOODS	OR SERVICES; LOSS OF
 *  USE, DATA,	OR PROFITS; OR	BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN	 CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *  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.,
 *  675 Mass Ave, Cambridge, MA 02139, USA.
 */
P
Pete Popov 已提交
41
#include <linux/config.h>
L
Linus Torvalds 已提交
42 43 44 45 46 47 48
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/fb.h>
#include <linux/init.h>
P
Pete Popov 已提交
49 50 51
#include <linux/interrupt.h>
#include <linux/ctype.h>
#include <linux/dma-mapping.h>
L
Linus Torvalds 已提交
52

P
Pete Popov 已提交
53
#include <asm/mach-au1x00/au1000.h>
L
Linus Torvalds 已提交
54

P
Pete Popov 已提交
55 56 57
#define DEBUG 0

#include "au1100fb.h"
L
Linus Torvalds 已提交
58 59 60 61 62

/*
 * Sanity check. If this is a new Au1100 based board, search for
 * the PB1100 ifdefs to make sure you modify the code accordingly.
 */
P
Pete Popov 已提交
63 64 65 66
#if defined(CONFIG_MIPS_PB1100)
  #include <asm/mach-pb1x00/pb1100.h>
#elif defined(CONFIG_MIPS_DB1100)
  #include <asm/mach-db1x00/db1x00.h>
L
Linus Torvalds 已提交
67
#else
P
Pete Popov 已提交
68
  #error "Unknown Au1100 board, Au1100 FB driver not supported"
L
Linus Torvalds 已提交
69 70
#endif

P
Pete Popov 已提交
71 72
#define DRIVER_NAME "au1100fb"
#define DRIVER_DESC "LCD controller driver for AU1100 processors"
L
Linus Torvalds 已提交
73

P
Pete Popov 已提交
74 75
#define to_au1100fb_device(_info) \
	  (_info ? container_of(_info, struct au1100fb_device, info) : NULL);
L
Linus Torvalds 已提交
76

P
Pete Popov 已提交
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
/* Bitfields format supported by the controller. Note that the order of formats
 * SHOULD be the same as in the LCD_CONTROL_SBPPF field, so we can retrieve the
 * right pixel format by doing rgb_bitfields[LCD_CONTROL_SBPPF_XXX >> LCD_CONTROL_SBPPF]
 */
struct fb_bitfield rgb_bitfields[][4] =
{
  	/*     Red, 	   Green, 	 Blue, 	     Transp   */
	{ { 10, 6, 0 }, { 5, 5, 0 }, { 0, 5, 0 }, { 0, 0, 0 } },
	{ { 11, 5, 0 }, { 5, 6, 0 }, { 0, 5, 0 }, { 0, 0, 0 } },
	{ { 11, 5, 0 }, { 6, 5, 0 }, { 0, 6, 0 }, { 0, 0, 0 } },
	{ { 10, 5, 0 }, { 5, 5, 0 }, { 0, 5, 0 }, { 15, 1, 0 } },
	{ { 11, 5, 0 }, { 6, 5, 0 }, { 1, 5, 0 }, { 0, 1, 0 } },

	/* The last is used to describe 12bpp format */
	{ { 8, 4, 0 },  { 4, 4, 0 }, { 0, 4, 0 }, { 0, 0, 0 } },
L
Linus Torvalds 已提交
92 93
};

P
Pete Popov 已提交
94 95 96 97 98 99
static struct fb_fix_screeninfo au1100fb_fix __initdata = {
	.id		= "AU1100 FB",
	.xpanstep 	= 1,
	.ypanstep 	= 1,
	.type		= FB_TYPE_PACKED_PIXELS,
	.accel		= FB_ACCEL_NONE,
L
Linus Torvalds 已提交
100 101
};

P
Pete Popov 已提交
102 103 104 105 106
static struct fb_var_screeninfo au1100fb_var __initdata = {
	.activate	= FB_ACTIVATE_NOW,
	.height		= -1,
	.width		= -1,
	.vmode		= FB_VMODE_NONINTERLACED,
L
Linus Torvalds 已提交
107 108
};

P
Pete Popov 已提交
109
static struct au1100fb_drv_info drv_info;
L
Linus Torvalds 已提交
110

P
Pete Popov 已提交
111 112 113
/*
 * Set hardware with var settings. This will enable the controller with a specific
 * mode, normally validated with the fb_check_var method
L
Linus Torvalds 已提交
114
	 */
P
Pete Popov 已提交
115
int au1100fb_setmode(struct au1100fb_device *fbdev)
L
Linus Torvalds 已提交
116
{
P
Pete Popov 已提交
117 118 119
	struct fb_info *info = &fbdev->info;
	u32 words;
	int index;
L
Linus Torvalds 已提交
120

P
Pete Popov 已提交
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
	if (!fbdev)
		return -EINVAL;

	/* Update var-dependent FB info */
	if (panel_is_active(fbdev->panel) || panel_is_color(fbdev->panel)) {
		if (info->var.bits_per_pixel <= 8) {
			/* palettized */
			info->var.red.offset    = 0;
			info->var.red.length    = info->var.bits_per_pixel;
			info->var.red.msb_right = 0;

			info->var.green.offset  = 0;
			info->var.green.length  = info->var.bits_per_pixel;
			info->var.green.msb_right = 0;

			info->var.blue.offset   = 0;
			info->var.blue.length   = info->var.bits_per_pixel;
			info->var.blue.msb_right = 0;

			info->var.transp.offset = 0;
			info->var.transp.length = 0;
			info->var.transp.msb_right = 0;

			info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
			info->fix.line_length = info->var.xres_virtual /
							(8/info->var.bits_per_pixel);
		} else {
			/* non-palettized */
			index = (fbdev->panel->control_base & LCD_CONTROL_SBPPF_MASK) >> LCD_CONTROL_SBPPF_BIT;
			info->var.red = rgb_bitfields[index][0];
			info->var.green = rgb_bitfields[index][1];
			info->var.blue = rgb_bitfields[index][2];
			info->var.transp = rgb_bitfields[index][3];

			info->fix.visual = FB_VISUAL_TRUECOLOR;
			info->fix.line_length = info->var.xres_virtual << 1; /* depth=16 */
	}
	} else {
		/* mono */
		info->fix.visual = FB_VISUAL_MONO10;
		info->fix.line_length = info->var.xres_virtual / 8;
L
Linus Torvalds 已提交
162 163
	}

P
Pete Popov 已提交
164
	info->screen_size = info->fix.line_length * info->var.yres_virtual;
L
Linus Torvalds 已提交
165

P
Pete Popov 已提交
166 167 168
	/* Determine BPP mode and format */
	fbdev->regs->lcd_control = fbdev->panel->control_base |
			    ((info->var.rotate/90) << LCD_CONTROL_SM_BIT);
L
Linus Torvalds 已提交
169

P
Pete Popov 已提交
170 171
	fbdev->regs->lcd_intenable = 0;
	fbdev->regs->lcd_intstatus = 0;
L
Linus Torvalds 已提交
172

P
Pete Popov 已提交
173
	fbdev->regs->lcd_horztiming = fbdev->panel->horztiming;
L
Linus Torvalds 已提交
174

P
Pete Popov 已提交
175 176 177
	fbdev->regs->lcd_verttiming = fbdev->panel->verttiming;

	fbdev->regs->lcd_clkcontrol = fbdev->panel->clkcontrol_base;
L
Linus Torvalds 已提交
178

P
Pete Popov 已提交
179 180 181 182 183 184 185 186 187 188 189 190
	fbdev->regs->lcd_dmaaddr0 = LCD_DMA_SA_N(fbdev->fb_phys);

	if (panel_is_dual(fbdev->panel)) {
		/* Second panel display seconf half of screen if possible,
		 * otherwise display the same as the first panel */
		if (info->var.yres_virtual >= (info->var.yres << 1)) {
			fbdev->regs->lcd_dmaaddr1 = LCD_DMA_SA_N(fbdev->fb_phys +
							  (info->fix.line_length *
						          (info->var.yres_virtual >> 1)));
		} else {
			fbdev->regs->lcd_dmaaddr1 = LCD_DMA_SA_N(fbdev->fb_phys);
		}
L
Linus Torvalds 已提交
191 192
	}

P
Pete Popov 已提交
193 194 195 196 197 198 199 200
	words = info->fix.line_length / sizeof(u32);
	if (!info->var.rotate || (info->var.rotate == 180)) {
		words *= info->var.yres_virtual;
		if (info->var.rotate /* 180 */) {
			words -= (words % 8); /* should be divisable by 8 */
		}
	}
	fbdev->regs->lcd_words = LCD_WRD_WRDS_N(words);
L
Linus Torvalds 已提交
201

P
Pete Popov 已提交
202 203
	fbdev->regs->lcd_pwmdiv = 0;
	fbdev->regs->lcd_pwmhi = 0;
L
Linus Torvalds 已提交
204

P
Pete Popov 已提交
205 206
	/* Resume controller */
	fbdev->regs->lcd_control |= LCD_CONTROL_GO;
L
Linus Torvalds 已提交
207

P
Pete Popov 已提交
208
	return 0;
L
Linus Torvalds 已提交
209 210
}

P
Pete Popov 已提交
211 212 213 214
/* fb_setcolreg
 * Set color in LCD palette.
 */
int au1100fb_fb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *fbi)
L
Linus Torvalds 已提交
215
{
P
Pete Popov 已提交
216 217 218
	struct au1100fb_device *fbdev = to_au1100fb_device(fbi);
	u32 *palette = fbdev->regs->lcd_pallettebase;
	u32 value;
L
Linus Torvalds 已提交
219

P
Pete Popov 已提交
220 221
	if (regno > (AU1100_LCD_NBR_PALETTE_ENTRIES - 1))
		return -EINVAL;
L
Linus Torvalds 已提交
222

P
Pete Popov 已提交
223 224 225 226 227
	if (fbi->var.grayscale) {
		/* Convert color to grayscale */
		red = green = blue =
			(19595 * red + 38470 * green + 7471 * blue) >> 16;
	}
L
Linus Torvalds 已提交
228

P
Pete Popov 已提交
229 230 231 232
	if (fbi->fix.visual == FB_VISUAL_TRUECOLOR) {
		/* Place color in the pseudopalette */
		if (regno > 16)
			return -EINVAL;
L
Linus Torvalds 已提交
233

P
Pete Popov 已提交
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
		palette = (u32*)fbi->pseudo_palette;

		red   >>= (16 - fbi->var.red.length);
		green >>= (16 - fbi->var.green.length);
		blue  >>= (16 - fbi->var.blue.length);

		value = (red   << fbi->var.red.offset) 	|
			(green << fbi->var.green.offset)|
			(blue  << fbi->var.blue.offset);
		value &= 0xFFFF;

	} else if (panel_is_active(fbdev->panel)) {
		/* COLOR TFT PALLETTIZED (use RGB 565) */
		value = (red & 0xF800)|((green >> 5) & 0x07E0)|((blue >> 11) & 0x001F);
		value &= 0xFFFF;

	} else if (panel_is_color(fbdev->panel)) {
		/* COLOR STN MODE */
		value = (((panel_swap_rgb(fbdev->panel) ? blue : red) >> 12) & 0x000F) |
			((green >> 8) & 0x00F0) |
			(((panel_swap_rgb(fbdev->panel) ? red : blue) >> 4) & 0x0F00);
		value &= 0xFFF;
	} else {
		/* MONOCHROME MODE */
		value = (green >> 12) & 0x000F;
		value &= 0xF;
L
Linus Torvalds 已提交
260 261
	}

P
Pete Popov 已提交
262 263
	palette[regno] = value;

L
Linus Torvalds 已提交
264 265 266
	return 0;
}

P
Pete Popov 已提交
267 268 269 270 271
/* fb_blank
 * Blank the screen. Depending on the mode, the screen will be
 * activated with the backlight color, or desactivated
 */
int au1100fb_fb_blank(int blank_mode, struct fb_info *fbi)
L
Linus Torvalds 已提交
272
{
P
Pete Popov 已提交
273 274 275
	struct au1100fb_device *fbdev = to_au1100fb_device(fbi);

	print_dbg("fb_blank %d %p", blank_mode, fbi);
L
Linus Torvalds 已提交
276 277

	switch (blank_mode) {
P
Pete Popov 已提交
278

L
Linus Torvalds 已提交
279
	case VESA_NO_BLANKING:
P
Pete Popov 已提交
280 281
			/* Turn on panel */
			fbdev->regs->lcd_control |= LCD_CONTROL_GO;
L
Linus Torvalds 已提交
282
#ifdef CONFIG_MIPS_PB1100
P
Pete Popov 已提交
283 284 285
			if (drv_info.panel_idx == 1) {
				au_writew(au_readw(PB1100_G_CONTROL)
					  | (PB1100_G_CONTROL_BL | PB1100_G_CONTROL_VDD),
L
Linus Torvalds 已提交
286
			PB1100_G_CONTROL);
P
Pete Popov 已提交
287
			}
L
Linus Torvalds 已提交
288 289 290 291 292 293 294
#endif
		au_sync();
		break;

	case VESA_VSYNC_SUSPEND:
	case VESA_HSYNC_SUSPEND:
	case VESA_POWERDOWN:
P
Pete Popov 已提交
295 296
			/* Turn off panel */
			fbdev->regs->lcd_control &= ~LCD_CONTROL_GO;
L
Linus Torvalds 已提交
297
#ifdef CONFIG_MIPS_PB1100
P
Pete Popov 已提交
298 299 300
			if (drv_info.panel_idx == 1) {
				au_writew(au_readw(PB1100_G_CONTROL)
				  	  & ~(PB1100_G_CONTROL_BL | PB1100_G_CONTROL_VDD),
L
Linus Torvalds 已提交
301
			PB1100_G_CONTROL);
P
Pete Popov 已提交
302
			}
L
Linus Torvalds 已提交
303 304 305 306 307 308 309 310 311 312
#endif
		au_sync();
		break;
	default:
		break;

	}
	return 0;
}

P
Pete Popov 已提交
313 314 315 316
/* fb_pan_display
 * Pan display in x and/or y as specified
 */
int au1100fb_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *fbi)
L
Linus Torvalds 已提交
317
{
P
Pete Popov 已提交
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
	struct au1100fb_device *fbdev = to_au1100fb_device(fbi);
	int dy;

	print_dbg("fb_pan_display %p %p", var, fbi);

	if (!var || !fbdev) {
		return -EINVAL;
	}

	if (var->xoffset - fbi->var.xoffset) {
		/* No support for X panning for now! */
		return -EINVAL;
	}

	print_dbg("fb_pan_display 2 %p %p", var, fbi);
	dy = var->yoffset - fbi->var.yoffset;
	if (dy) {

		u32 dmaaddr;

		print_dbg("Panning screen of %d lines", dy);

		dmaaddr = fbdev->regs->lcd_dmaaddr0;
		dmaaddr += (fbi->fix.line_length * dy);

		/* TODO: Wait for current frame to finished */
		fbdev->regs->lcd_dmaaddr0 = LCD_DMA_SA_N(dmaaddr);

		if (panel_is_dual(fbdev->panel)) {
			dmaaddr = fbdev->regs->lcd_dmaaddr1;
			dmaaddr += (fbi->fix.line_length * dy);
			fbdev->regs->lcd_dmaaddr0 = LCD_DMA_SA_N(dmaaddr);
	}
	}
	print_dbg("fb_pan_display 3 %p %p", var, fbi);

	return 0;
}

/* fb_rotate
 * Rotate the display of this angle. This doesn't seems to be used by the core,
 * but as our hardware supports it, so why not implementing it...
 */
void au1100fb_fb_rotate(struct fb_info *fbi, int angle)
{
	struct au1100fb_device *fbdev = to_au1100fb_device(fbi);

	print_dbg("fb_rotate %p %d", fbi, angle);

	if (fbdev && (angle > 0) && !(angle % 90)) {

		fbdev->regs->lcd_control &= ~LCD_CONTROL_GO;

		fbdev->regs->lcd_control &= ~(LCD_CONTROL_SM_MASK);
		fbdev->regs->lcd_control |= ((angle/90) << LCD_CONTROL_SM_BIT);

		fbdev->regs->lcd_control |= LCD_CONTROL_GO;
L
Linus Torvalds 已提交
375 376 377
	}
}

P
Pete Popov 已提交
378 379 380 381 382
/* fb_mmap
 * Map video memory in user space. We don't use the generic fb_mmap method mainly
 * to allow the use of the TLB streaming flag (CCA=6)
 */
int au1100fb_fb_mmap(struct fb_info *fbi, struct file *file, struct vm_area_struct *vma)
L
Linus Torvalds 已提交
383
{
P
Pete Popov 已提交
384
	struct au1100fb_device *fbdev = to_au1100fb_device(fbi);
L
Linus Torvalds 已提交
385 386 387 388 389 390 391
	unsigned int len;
	unsigned long start=0, off;

	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) {
		return -EINVAL;
	}

P
Pete Popov 已提交
392 393
	start = fbdev->fb_phys & PAGE_MASK;
	len = PAGE_ALIGN((start & ~PAGE_MASK) + fbdev->fb_len);
L
Linus Torvalds 已提交
394 395 396 397 398 399 400 401 402 403

	off = vma->vm_pgoff << PAGE_SHIFT;

	if ((vma->vm_end - vma->vm_start + off) > len) {
		return -EINVAL;
	}

	off += start;
	vma->vm_pgoff = off >> PAGE_SHIFT;

P
Pete Popov 已提交
404
	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
L
Linus Torvalds 已提交
405 406 407 408
	pgprot_val(vma->vm_page_prot) |= (6 << 9); //CCA=6

	vma->vm_flags |= VM_IO;

P
Pete Popov 已提交
409
	if (io_remap_page_range(vma, vma->vm_start, off,
L
Linus Torvalds 已提交
410 411 412 413 414 415 416 417
				vma->vm_end - vma->vm_start,
				vma->vm_page_prot)) {
		return -EAGAIN;
	}

	return 0;
}

P
Pete Popov 已提交
418
static struct fb_ops au1100fb_ops =
L
Linus Torvalds 已提交
419
{
P
Pete Popov 已提交
420 421 422 423 424 425 426 427 428 429
	.owner			= THIS_MODULE,
	.fb_setcolreg		= au1100fb_fb_setcolreg,
	.fb_blank		= au1100fb_fb_blank,
	.fb_pan_display		= au1100fb_fb_pan_display,
	.fb_fillrect		= cfb_fillrect,
	.fb_copyarea		= cfb_copyarea,
	.fb_imageblit		= cfb_imageblit,
	.fb_rotate		= au1100fb_fb_rotate,
	.fb_mmap		= au1100fb_fb_mmap,
};
L
Linus Torvalds 已提交
430 431


P
Pete Popov 已提交
432
/*-------------------------------------------------------------------------*/
L
Linus Torvalds 已提交
433

P
Pete Popov 已提交
434
/* AU1100 LCD controller device driver */
L
Linus Torvalds 已提交
435

P
Pete Popov 已提交
436
int au1100fb_drv_probe(struct device *dev)
L
Linus Torvalds 已提交
437
{
P
Pete Popov 已提交
438 439 440 441 442 443
	struct au1100fb_device *fbdev = NULL;
	struct resource *regs_res;
	unsigned long page;
	u32 sys_clksrc;

	if (!dev)
L
Linus Torvalds 已提交
444
			return -EINVAL;
P
Pete Popov 已提交
445 446 447 448 449

	/* Allocate new device private */
	if (!(fbdev = kmalloc(sizeof(struct au1100fb_device), GFP_KERNEL))) {
		print_err("fail to allocate device private record");
		return -ENOMEM;
L
Linus Torvalds 已提交
450
	}
P
Pete Popov 已提交
451
	memset((void*)fbdev, 0, sizeof(struct au1100fb_device));
L
Linus Torvalds 已提交
452

P
Pete Popov 已提交
453
	fbdev->panel = &known_lcd_panels[drv_info.panel_idx];
L
Linus Torvalds 已提交
454

P
Pete Popov 已提交
455
	dev_set_drvdata(dev, (void*)fbdev);
L
Linus Torvalds 已提交
456

P
Pete Popov 已提交
457 458 459 460 461 462
	/* Allocate region for our registers and map them */
	if (!(regs_res = platform_get_resource(to_platform_device(dev),
					IORESOURCE_MEM, 0))) {
		print_err("fail to retrieve registers resource");
		return -EFAULT;
	}
L
Linus Torvalds 已提交
463

P
Pete Popov 已提交
464 465
	au1100fb_fix.mmio_start = regs_res->start;
	au1100fb_fix.mmio_len = regs_res->end - regs_res->start + 1;
L
Linus Torvalds 已提交
466

P
Pete Popov 已提交
467 468 469 470 471 472
	if (!request_mem_region(au1100fb_fix.mmio_start, au1100fb_fix.mmio_len,
				DRIVER_NAME)) {
		print_err("fail to lock memory region at 0x%08x",
				au1100fb_fix.mmio_start);
		return -EBUSY;
	}
L
Linus Torvalds 已提交
473

P
Pete Popov 已提交
474
	fbdev->regs = (struct au1100fb_regs*)KSEG1ADDR(au1100fb_fix.mmio_start);
L
Linus Torvalds 已提交
475

P
Pete Popov 已提交
476 477
	print_dbg("Register memory map at %p", fbdev->regs);
	print_dbg("phys=0x%08x, size=%d", fbdev->regs_phys, fbdev->regs_len);
L
Linus Torvalds 已提交
478 479 480



P
Pete Popov 已提交
481 482 483 484 485 486 487 488 489
	/* Allocate the framebuffer to the maximum screen size * nbr of video buffers */
	fbdev->fb_len = fbdev->panel->xres * fbdev->panel->yres *
		  	(fbdev->panel->bpp >> 3) * AU1100FB_NBR_VIDEO_BUFFERS;

	fbdev->fb_mem = dma_alloc_coherent(dev, PAGE_ALIGN(fbdev->fb_len),
					&fbdev->fb_phys, GFP_KERNEL);
	if (!fbdev->fb_mem) {
		print_err("fail to allocate frambuffer (size: %dK))",
			  fbdev->fb_len / 1024);
L
Linus Torvalds 已提交
490 491
		return -ENOMEM;
	}
P
Pete Popov 已提交
492 493 494

	au1100fb_fix.smem_start = fbdev->fb_phys;
	au1100fb_fix.smem_len = fbdev->fb_len;
L
Linus Torvalds 已提交
495 496 497 498 499

	/*
	 * Set page reserved so that mmap will work. This is necessary
	 * since we'll be remapping normal memory.
	 */
P
Pete Popov 已提交
500 501
	for (page = (unsigned long)fbdev->fb_mem;
	     page < PAGE_ALIGN((unsigned long)fbdev->fb_mem + fbdev->fb_len);
L
Linus Torvalds 已提交
502
	     page += PAGE_SIZE) {
P
Pete Popov 已提交
503 504 505
#if CONFIG_DMA_NONCOHERENT
		SetPageReserved(virt_to_page(CAC_ADDR(page)));
#else
L
Linus Torvalds 已提交
506
		SetPageReserved(virt_to_page(page));
P
Pete Popov 已提交
507
#endif
L
Linus Torvalds 已提交
508 509
	}

P
Pete Popov 已提交
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564
	print_dbg("Framebuffer memory map at %p", fbdev->fb_mem);
	print_dbg("phys=0x%08x, size=%dK", fbdev->fb_phys, fbdev->fb_len / 1024);

	/* Setup LCD clock to AUX (48 MHz) */
	sys_clksrc = au_readl(SYS_CLKSRC) & ~(SYS_CS_ML_MASK | SYS_CS_DL | SYS_CS_CL);
	au_writel((sys_clksrc | (1 << SYS_CS_ML_BIT)), SYS_CLKSRC);

	/* load the panel info into the var struct */
	au1100fb_var.bits_per_pixel = fbdev->panel->bpp;
	au1100fb_var.xres = fbdev->panel->xres;
	au1100fb_var.xres_virtual = au1100fb_var.xres;
	au1100fb_var.yres = fbdev->panel->yres;
	au1100fb_var.yres_virtual = au1100fb_var.yres;

	fbdev->info.screen_base = fbdev->fb_mem;
	fbdev->info.fbops = &au1100fb_ops;
	fbdev->info.fix = au1100fb_fix;

	if (!(fbdev->info.pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL))) {
		return -ENOMEM;
	}
	memset(fbdev->info.pseudo_palette, 0, sizeof(u32) * 16);

	if (fb_alloc_cmap(&fbdev->info.cmap, AU1100_LCD_NBR_PALETTE_ENTRIES, 0) < 0) {
		print_err("Fail to allocate colormap (%d entries)",
			   AU1100_LCD_NBR_PALETTE_ENTRIES);
		kfree(fbdev->info.pseudo_palette);
		return -EFAULT;
	}

	fbdev->info.var = au1100fb_var;

	/* Set h/w registers */
	au1100fb_setmode(fbdev);

	/* Register new framebuffer */
	if (register_framebuffer(&fbdev->info) < 0) {
		print_err("cannot register new framebuffer");
		goto failed;
	}

	return 0;

failed:
	if (fbdev->regs) {
		release_mem_region(fbdev->regs_phys, fbdev->regs_len);
	}
	if (fbdev->fb_mem) {
		dma_free_noncoherent(dev, fbdev->fb_len, fbdev->fb_mem, fbdev->fb_phys);
	}
	if (fbdev->info.cmap.len != 0) {
		fb_dealloc_cmap(&fbdev->info.cmap);
	}
	kfree(fbdev);
	dev_set_drvdata(dev, NULL);
L
Linus Torvalds 已提交
565 566 567 568

	return 0;
}

P
Pete Popov 已提交
569 570 571 572 573 574 575 576 577 578 579 580 581
int au1100fb_drv_remove(struct device *dev)
{
	struct au1100fb_device *fbdev = NULL;

	if (!dev)
		return -ENODEV;

	fbdev = (struct au1100fb_device*) dev_get_drvdata(dev);

#if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO)
	au1100fb_fb_blank(VESA_POWERDOWN, &fbdev->info);
#endif
	fbdev->regs->lcd_control &= ~LCD_CONTROL_GO;
L
Linus Torvalds 已提交
582

P
Pete Popov 已提交
583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603
	/* Clean up all probe data */
	unregister_framebuffer(&fbdev->info);

	release_mem_region(fbdev->regs_phys, fbdev->regs_len);

	dma_free_coherent(dev, PAGE_ALIGN(fbdev->fb_len), fbdev->fb_mem, fbdev->fb_phys);

	fb_dealloc_cmap(&fbdev->info.cmap);
	kfree(fbdev->info.pseudo_palette);
	kfree((void*)fbdev);

	return 0;
}

int au1100fb_drv_suspend(struct device *dev, u32 state, u32 level)
{
	/* TODO */
	return 0;
}

int au1100fb_drv_resume(struct device *dev, u32 level)
L
Linus Torvalds 已提交
604
{
P
Pete Popov 已提交
605 606
	/* TODO */
	return 0;
L
Linus Torvalds 已提交
607 608
}

P
Pete Popov 已提交
609 610 611
static struct device_driver au1100fb_driver = {
	.name		= "au1100-lcd",
	.bus		= &platform_bus_type,
L
Linus Torvalds 已提交
612

P
Pete Popov 已提交
613 614 615 616 617 618 619 620 621 622 623
	.probe		= au1100fb_drv_probe,
        .remove		= au1100fb_drv_remove,
	.suspend	= au1100fb_drv_suspend,
        .resume		= au1100fb_drv_resume,
};

/*-------------------------------------------------------------------------*/

/* Kernel driver */

int au1100fb_setup(char *options)
L
Linus Torvalds 已提交
624 625
{
	char* this_opt;
P
Pete Popov 已提交
626 627 628
	int num_panels = ARRAY_SIZE(known_lcd_panels);
	char* mode = NULL;
	int panel_idx = 0;
L
Linus Torvalds 已提交
629

P
Pete Popov 已提交
630 631 632 633
	if (num_panels <= 0) {
		print_err("No LCD panels supported by driver!");
		return -EFAULT;
			}
L
Linus Torvalds 已提交
634

P
Pete Popov 已提交
635 636 637
	if (options) {
		while ((this_opt = strsep(&options,",")) != NULL) {
			/* Panel option */
L
Linus Torvalds 已提交
638
		if (!strncmp(this_opt, "panel:", 6)) {
P
Pete Popov 已提交
639 640 641 642 643
				int i;
				this_opt += 6;
				for (i = 0; i < num_panels; i++) {
					if (!strncmp(this_opt,
					      	     known_lcd_panels[i].name,
L
Linus Torvalds 已提交
644
							strlen(this_opt))) {
P
Pete Popov 已提交
645
						panel_idx = i;
L
Linus Torvalds 已提交
646 647 648
					break;
				}
			}
P
Pete Popov 已提交
649 650 651 652 653 654 655 656 657 658 659 660
				if (i >= num_panels) {
 					print_warn("Panel %s not supported!", this_opt);
				}
			}
			/* Mode option (only option that start with digit) */
			else if (isdigit(this_opt[0])) {
				mode = kmalloc(strlen(this_opt) + 1, GFP_KERNEL);
				strncpy(mode, this_opt, strlen(this_opt) + 1);
			}
			/* Unsupported option */
			else {
				print_warn("Unsupported option \"%s\"", this_opt);
L
Linus Torvalds 已提交
661 662 663 664
		}
		}
	}

P
Pete Popov 已提交
665 666
	drv_info.panel_idx = panel_idx;
	drv_info.opt_mode = mode;
L
Linus Torvalds 已提交
667

P
Pete Popov 已提交
668 669 670
	print_info("Panel=%s Mode=%s",
			known_lcd_panels[drv_info.panel_idx].name,
		      	drv_info.opt_mode ? drv_info.opt_mode : "default");
L
Linus Torvalds 已提交
671

P
Pete Popov 已提交
672 673
	return 0;
}
L
Linus Torvalds 已提交
674

P
Pete Popov 已提交
675
int __init au1100fb_init(void)
L
Linus Torvalds 已提交
676
{
P
Pete Popov 已提交
677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694
	char* options;
	int ret;

	print_info("" DRIVER_DESC "");

	memset(&drv_info, 0, sizeof(drv_info));

	if (fb_get_options(DRIVER_NAME, &options))
		return -ENODEV;

	/* Setup driver with options */
	ret = au1100fb_setup(options);
	if (ret < 0) {
		print_err("Fail to setup driver");
		return ret;
	}

	return driver_register(&au1100fb_driver);
L
Linus Torvalds 已提交
695 696
}

P
Pete Popov 已提交
697
void __exit au1100fb_cleanup(void)
L
Linus Torvalds 已提交
698
{
P
Pete Popov 已提交
699 700 701 702
	driver_unregister(&au1100fb_driver);

	if (drv_info.opt_mode)
		kfree(drv_info.opt_mode);
L
Linus Torvalds 已提交
703 704
}

P
Pete Popov 已提交
705 706 707 708 709
module_init(au1100fb_init);
module_exit(au1100fb_cleanup);

MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");