au1100fb.c 19.7 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>
 *
10 11
 * PM support added by Rodolfo Giometti <giometti@linux.it>
 *
L
Linus Torvalds 已提交
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 41 42 43 44 45 46 47 48 49
 * 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.
 */
#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 已提交
50 51 52
#include <linux/interrupt.h>
#include <linux/ctype.h>
#include <linux/dma-mapping.h>
53
#include <linux/platform_device.h>
L
Linus Torvalds 已提交
54

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

P
Pete Popov 已提交
57 58 59
#define DEBUG 0

#include "au1100fb.h"
L
Linus Torvalds 已提交
60 61 62 63 64

/*
 * 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 已提交
65 66 67 68
#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 已提交
69
#else
P
Pete Popov 已提交
70
  #error "Unknown Au1100 board, Au1100 FB driver not supported"
L
Linus Torvalds 已提交
71 72
#endif

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

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

P
Pete Popov 已提交
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
/* 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 已提交
94 95
};

P
Pete Popov 已提交
96 97 98 99 100 101
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 已提交
102 103
};

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

P
Pete Popov 已提交
111
static struct au1100fb_drv_info drv_info;
L
Linus Torvalds 已提交
112

P
Pete Popov 已提交
113 114 115
/*
 * 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 已提交
116
	 */
P
Pete Popov 已提交
117
int au1100fb_setmode(struct au1100fb_device *fbdev)
L
Linus Torvalds 已提交
118
{
P
Pete Popov 已提交
119 120 121
	struct fb_info *info = &fbdev->info;
	u32 words;
	int index;
L
Linus Torvalds 已提交
122

P
Pete Popov 已提交
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 162 163
	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 已提交
164 165
	}

P
Pete Popov 已提交
166
	info->screen_size = info->fix.line_length * info->var.yres_virtual;
167 168
	info->var.rotate = ((fbdev->panel->control_base&LCD_CONTROL_SM_MASK) \
				>> LCD_CONTROL_SM_BIT) * 90;
L
Linus Torvalds 已提交
169

P
Pete Popov 已提交
170
	/* Determine BPP mode and format */
171
	fbdev->regs->lcd_control = fbdev->panel->control_base;
L
Linus Torvalds 已提交
172

P
Pete Popov 已提交
173 174
	fbdev->regs->lcd_intenable = 0;
	fbdev->regs->lcd_intstatus = 0;
L
Linus Torvalds 已提交
175

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

P
Pete Popov 已提交
178 179 180
	fbdev->regs->lcd_verttiming = fbdev->panel->verttiming;

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

P
Pete Popov 已提交
182 183 184 185 186 187 188 189 190 191 192 193
	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 已提交
194 195
	}

P
Pete Popov 已提交
196 197 198 199 200 201 202 203
	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 已提交
204

P
Pete Popov 已提交
205 206
	fbdev->regs->lcd_pwmdiv = 0;
	fbdev->regs->lcd_pwmhi = 0;
L
Linus Torvalds 已提交
207

P
Pete Popov 已提交
208 209
	/* Resume controller */
	fbdev->regs->lcd_control |= LCD_CONTROL_GO;
L
Linus Torvalds 已提交
210

P
Pete Popov 已提交
211
	return 0;
L
Linus Torvalds 已提交
212 213
}

P
Pete Popov 已提交
214 215 216 217
/* 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 已提交
218
{
219 220
	struct au1100fb_device *fbdev;
	u32 *palette;
P
Pete Popov 已提交
221
	u32 value;
L
Linus Torvalds 已提交
222

223 224 225
	fbdev = to_au1100fb_device(fbi);
	palette = fbdev->regs->lcd_pallettebase;

P
Pete Popov 已提交
226 227
	if (regno > (AU1100_LCD_NBR_PALETTE_ENTRIES - 1))
		return -EINVAL;
L
Linus Torvalds 已提交
228

P
Pete Popov 已提交
229 230 231 232 233
	if (fbi->var.grayscale) {
		/* Convert color to grayscale */
		red = green = blue =
			(19595 * red + 38470 * green + 7471 * blue) >> 16;
	}
L
Linus Torvalds 已提交
234

P
Pete Popov 已提交
235 236 237 238
	if (fbi->fix.visual == FB_VISUAL_TRUECOLOR) {
		/* Place color in the pseudopalette */
		if (regno > 16)
			return -EINVAL;
L
Linus Torvalds 已提交
239

P
Pete Popov 已提交
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
		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 已提交
266 267
	}

P
Pete Popov 已提交
268 269
	palette[regno] = value;

L
Linus Torvalds 已提交
270 271 272
	return 0;
}

P
Pete Popov 已提交
273 274 275 276 277
/* 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 已提交
278
{
P
Pete Popov 已提交
279 280 281
	struct au1100fb_device *fbdev = to_au1100fb_device(fbi);

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

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

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

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

	}
	return 0;
}

P
Pete Popov 已提交
319 320 321 322
/* 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 已提交
323
{
324
	struct au1100fb_device *fbdev;
P
Pete Popov 已提交
325 326
	int dy;

327 328
	fbdev = to_au1100fb_device(fbi);

P
Pete Popov 已提交
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 375 376 377 378 379 380 381 382
	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 已提交
383 384 385
	}
}

P
Pete Popov 已提交
386 387 388 389
/* 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)
 */
390
int au1100fb_fb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
L
Linus Torvalds 已提交
391
{
392
	struct au1100fb_device *fbdev;
L
Linus Torvalds 已提交
393 394 395
	unsigned int len;
	unsigned long start=0, off;

396 397
	fbdev = to_au1100fb_device(fbi);

L
Linus Torvalds 已提交
398 399 400 401
	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) {
		return -EINVAL;
	}

P
Pete Popov 已提交
402 403
	start = fbdev->fb_phys & PAGE_MASK;
	len = PAGE_ALIGN((start & ~PAGE_MASK) + fbdev->fb_len);
L
Linus Torvalds 已提交
404 405 406 407 408 409 410 411 412 413

	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 已提交
414
	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
L
Linus Torvalds 已提交
415 416 417 418
	pgprot_val(vma->vm_page_prot) |= (6 << 9); //CCA=6

	vma->vm_flags |= VM_IO;

419
	if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
L
Linus Torvalds 已提交
420 421 422 423 424 425 426 427
				vma->vm_end - vma->vm_start,
				vma->vm_page_prot)) {
		return -EAGAIN;
	}

	return 0;
}

P
Pete Popov 已提交
428
static struct fb_ops au1100fb_ops =
L
Linus Torvalds 已提交
429
{
P
Pete Popov 已提交
430 431 432 433 434 435 436 437 438 439
	.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 已提交
440 441


P
Pete Popov 已提交
442
/*-------------------------------------------------------------------------*/
L
Linus Torvalds 已提交
443

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

P
Pete Popov 已提交
446
int au1100fb_drv_probe(struct device *dev)
L
Linus Torvalds 已提交
447
{
P
Pete Popov 已提交
448 449 450 451 452 453
	struct au1100fb_device *fbdev = NULL;
	struct resource *regs_res;
	unsigned long page;
	u32 sys_clksrc;

	if (!dev)
L
Linus Torvalds 已提交
454
			return -EINVAL;
P
Pete Popov 已提交
455 456 457 458 459

	/* 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 已提交
460
	}
P
Pete Popov 已提交
461
	memset((void*)fbdev, 0, sizeof(struct au1100fb_device));
L
Linus Torvalds 已提交
462

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

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

P
Pete Popov 已提交
467 468 469 470 471 472
	/* 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 已提交
473

P
Pete Popov 已提交
474 475
	au1100fb_fix.mmio_start = regs_res->start;
	au1100fb_fix.mmio_len = regs_res->end - regs_res->start + 1;
L
Linus Torvalds 已提交
476

P
Pete Popov 已提交
477 478
	if (!request_mem_region(au1100fb_fix.mmio_start, au1100fb_fix.mmio_len,
				DRIVER_NAME)) {
479
		print_err("fail to lock memory region at 0x%08lx",
P
Pete Popov 已提交
480 481 482
				au1100fb_fix.mmio_start);
		return -EBUSY;
	}
L
Linus Torvalds 已提交
483

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

P
Pete Popov 已提交
486 487
	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 已提交
488 489 490



P
Pete Popov 已提交
491 492 493 494 495 496 497 498 499
	/* 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 已提交
500 501
		return -ENOMEM;
	}
P
Pete Popov 已提交
502 503 504

	au1100fb_fix.smem_start = fbdev->fb_phys;
	au1100fb_fix.smem_len = fbdev->fb_len;
L
Linus Torvalds 已提交
505 506 507 508 509

	/*
	 * Set page reserved so that mmap will work. This is necessary
	 * since we'll be remapping normal memory.
	 */
P
Pete Popov 已提交
510 511
	for (page = (unsigned long)fbdev->fb_mem;
	     page < PAGE_ALIGN((unsigned long)fbdev->fb_mem + fbdev->fb_len);
L
Linus Torvalds 已提交
512
	     page += PAGE_SIZE) {
P
Pete Popov 已提交
513 514 515
#if CONFIG_DMA_NONCOHERENT
		SetPageReserved(virt_to_page(CAC_ADDR(page)));
#else
L
Linus Torvalds 已提交
516
		SetPageReserved(virt_to_page(page));
P
Pete Popov 已提交
517
#endif
L
Linus Torvalds 已提交
518 519
	}

P
Pete Popov 已提交
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 565 566 567 568 569 570 571 572 573 574
	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 已提交
575 576 577 578

	return 0;
}

P
Pete Popov 已提交
579 580 581 582 583 584 585 586 587 588 589 590 591
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 已提交
592

P
Pete Popov 已提交
593 594 595 596 597 598 599 600 601 602 603 604 605 606
	/* 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;
}

607 608 609 610
#ifdef CONFIG_PM
static u32 sys_clksrc;
static struct au1100fb_regs fbregs;

611
int au1100fb_drv_suspend(struct device *dev, pm_message_t state)
P
Pete Popov 已提交
612
{
613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
	struct au1100fb_device *fbdev = dev_get_drvdata(dev);

	if (!fbdev)
		return 0;

	/* Save the clock source state */
	sys_clksrc = au_readl(SYS_CLKSRC);

	/* Blank the LCD */
	au1100fb_fb_blank(VESA_POWERDOWN, &fbdev->info);

	/* Stop LCD clocking */
	au_writel(sys_clksrc & ~SYS_CS_ML_MASK, SYS_CLKSRC);

	memcpy(&fbregs, fbdev->regs, sizeof(struct au1100fb_regs));

P
Pete Popov 已提交
629 630 631
	return 0;
}

632
int au1100fb_drv_resume(struct device *dev)
L
Linus Torvalds 已提交
633
{
634 635 636 637 638 639 640 641 642 643 644 645 646
	struct au1100fb_device *fbdev = dev_get_drvdata(dev);

	if (!fbdev)
		return 0;

	memcpy(fbdev->regs, &fbregs, sizeof(struct au1100fb_regs));

	/* Restart LCD clocking */
	au_writel(sys_clksrc, SYS_CLKSRC);

	/* Unblank the LCD */
	au1100fb_fb_blank(VESA_NO_BLANKING, &fbdev->info);

P
Pete Popov 已提交
647
	return 0;
L
Linus Torvalds 已提交
648
}
649 650 651 652
#else
#define au1100fb_drv_suspend NULL
#define au1100fb_drv_resume NULL
#endif
L
Linus Torvalds 已提交
653

P
Pete Popov 已提交
654 655 656
static struct device_driver au1100fb_driver = {
	.name		= "au1100-lcd",
	.bus		= &platform_bus_type,
L
Linus Torvalds 已提交
657

P
Pete Popov 已提交
658 659 660 661 662 663 664 665 666 667 668
	.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 已提交
669 670
{
	char* this_opt;
P
Pete Popov 已提交
671 672 673
	int num_panels = ARRAY_SIZE(known_lcd_panels);
	char* mode = NULL;
	int panel_idx = 0;
L
Linus Torvalds 已提交
674

P
Pete Popov 已提交
675 676 677 678
	if (num_panels <= 0) {
		print_err("No LCD panels supported by driver!");
		return -EFAULT;
			}
L
Linus Torvalds 已提交
679

P
Pete Popov 已提交
680 681 682
	if (options) {
		while ((this_opt = strsep(&options,",")) != NULL) {
			/* Panel option */
L
Linus Torvalds 已提交
683
		if (!strncmp(this_opt, "panel:", 6)) {
P
Pete Popov 已提交
684 685 686 687 688
				int i;
				this_opt += 6;
				for (i = 0; i < num_panels; i++) {
					if (!strncmp(this_opt,
					      	     known_lcd_panels[i].name,
L
Linus Torvalds 已提交
689
							strlen(this_opt))) {
P
Pete Popov 已提交
690
						panel_idx = i;
L
Linus Torvalds 已提交
691 692 693
					break;
				}
			}
P
Pete Popov 已提交
694 695 696 697 698 699 700 701 702 703 704 705
				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 已提交
706 707 708 709
		}
		}
	}

P
Pete Popov 已提交
710 711
	drv_info.panel_idx = panel_idx;
	drv_info.opt_mode = mode;
L
Linus Torvalds 已提交
712

P
Pete Popov 已提交
713 714 715
	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 已提交
716

P
Pete Popov 已提交
717 718
	return 0;
}
L
Linus Torvalds 已提交
719

P
Pete Popov 已提交
720
int __init au1100fb_init(void)
L
Linus Torvalds 已提交
721
{
P
Pete Popov 已提交
722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739
	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 已提交
740 741
}

P
Pete Popov 已提交
742
void __exit au1100fb_cleanup(void)
L
Linus Torvalds 已提交
743
{
P
Pete Popov 已提交
744 745
	driver_unregister(&au1100fb_driver);

746
	kfree(drv_info.opt_mode);
L
Linus Torvalds 已提交
747 748
}

P
Pete Popov 已提交
749 750 751 752 753
module_init(au1100fb_init);
module_exit(au1100fb_cleanup);

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