au1100fb.c 19.6 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;
L
Linus Torvalds 已提交
167

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

P
Pete Popov 已提交
239 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
		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 已提交
265 266
	}

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

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

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

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

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

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

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

	}
	return 0;
}

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

326 327
	fbdev = to_au1100fb_device(fbi);

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

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

395 396
	fbdev = to_au1100fb_device(fbi);

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

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

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

	vma->vm_flags |= VM_IO;

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

	return 0;
}

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


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

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

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

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

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

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

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

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

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

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

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

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



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

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

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

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

	return 0;
}

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

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

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

610
int au1100fb_drv_suspend(struct device *dev, pm_message_t state)
P
Pete Popov 已提交
611
{
612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
	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 已提交
628 629 630
	return 0;
}

631
int au1100fb_drv_resume(struct device *dev)
L
Linus Torvalds 已提交
632
{
633 634 635 636 637 638 639 640 641 642 643 644 645
	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 已提交
646
	return 0;
L
Linus Torvalds 已提交
647
}
648 649 650 651
#else
#define au1100fb_drv_suspend NULL
#define au1100fb_drv_resume NULL
#endif
L
Linus Torvalds 已提交
652

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

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

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

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

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

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

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

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

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

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

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

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