1 /*
2  *  linux/drivers/video/amba-clcd.c
3  *
4  * Copyright (C) 2001 ARM Limited, by David A Rusling
5  * Updated to 2.5, Deep Blue Solutions Ltd.
6  *
7  * This file is subject to the terms and conditions of the GNU General Public
8  * License.  See the file COPYING in the main directory of this archive
9  * for more details.
10  *
11  *  ARM PrimeCell PL110 Color LCD Controller
12  */
13 #include <linux/module.h>
14 #include <linux/kernel.h>
15 #include <linux/errno.h>
16 #include <linux/string.h>
17 #include <linux/slab.h>
18 #include <linux/delay.h>
19 #include <linux/mm.h>
20 #include <linux/fb.h>
21 #include <linux/init.h>
22 #include <linux/ioport.h>
23 #include <linux/list.h>
24 #include <linux/amba/bus.h>
25 #include <linux/amba/clcd.h>
26 #include <linux/clk.h>
27 #include <linux/hardirq.h>
28 
29 #include <asm/sizes.h>
30 
31 #define to_clcd(info)	container_of(info, struct clcd_fb, fb)
32 
33 /* This is limited to 16 characters when displayed by X startup */
34 static const char *clcd_name = "CLCD FB";
35 
36 /*
37  * Unfortunately, the enable/disable functions may be called either from
38  * process or IRQ context, and we _need_ to delay.  This is _not_ good.
39  */
clcdfb_sleep(unsigned int ms)40 static inline void clcdfb_sleep(unsigned int ms)
41 {
42 	if (in_atomic()) {
43 		mdelay(ms);
44 	} else {
45 		msleep(ms);
46 	}
47 }
48 
clcdfb_set_start(struct clcd_fb * fb)49 static inline void clcdfb_set_start(struct clcd_fb *fb)
50 {
51 	unsigned long ustart = fb->fb.fix.smem_start;
52 	unsigned long lstart;
53 
54 	ustart += fb->fb.var.yoffset * fb->fb.fix.line_length;
55 	lstart = ustart + fb->fb.var.yres * fb->fb.fix.line_length / 2;
56 
57 	writel(ustart, fb->regs + CLCD_UBAS);
58 	writel(lstart, fb->regs + CLCD_LBAS);
59 }
60 
clcdfb_disable(struct clcd_fb * fb)61 static void clcdfb_disable(struct clcd_fb *fb)
62 {
63 	u32 val;
64 
65 	if (fb->board->disable)
66 		fb->board->disable(fb);
67 
68 	val = readl(fb->regs + fb->off_cntl);
69 	if (val & CNTL_LCDPWR) {
70 		val &= ~CNTL_LCDPWR;
71 		writel(val, fb->regs + fb->off_cntl);
72 
73 		clcdfb_sleep(20);
74 	}
75 	if (val & CNTL_LCDEN) {
76 		val &= ~CNTL_LCDEN;
77 		writel(val, fb->regs + fb->off_cntl);
78 	}
79 
80 	/*
81 	 * Disable CLCD clock source.
82 	 */
83 	if (fb->clk_enabled) {
84 		fb->clk_enabled = false;
85 		clk_disable(fb->clk);
86 	}
87 }
88 
clcdfb_enable(struct clcd_fb * fb,u32 cntl)89 static void clcdfb_enable(struct clcd_fb *fb, u32 cntl)
90 {
91 	/*
92 	 * Enable the CLCD clock source.
93 	 */
94 	if (!fb->clk_enabled) {
95 		fb->clk_enabled = true;
96 		clk_enable(fb->clk);
97 	}
98 
99 	/*
100 	 * Bring up by first enabling..
101 	 */
102 	cntl |= CNTL_LCDEN;
103 	writel(cntl, fb->regs + fb->off_cntl);
104 
105 	clcdfb_sleep(20);
106 
107 	/*
108 	 * and now apply power.
109 	 */
110 	cntl |= CNTL_LCDPWR;
111 	writel(cntl, fb->regs + fb->off_cntl);
112 
113 	/*
114 	 * finally, enable the interface.
115 	 */
116 	if (fb->board->enable)
117 		fb->board->enable(fb);
118 }
119 
120 static int
clcdfb_set_bitfields(struct clcd_fb * fb,struct fb_var_screeninfo * var)121 clcdfb_set_bitfields(struct clcd_fb *fb, struct fb_var_screeninfo *var)
122 {
123 	u32 caps;
124 	int ret = 0;
125 
126 	if (fb->panel->caps && fb->board->caps)
127 		caps = fb->panel->caps & fb->board->caps;
128 	else {
129 		/* Old way of specifying what can be used */
130 		caps = fb->panel->cntl & CNTL_BGR ?
131 			CLCD_CAP_BGR : CLCD_CAP_RGB;
132 		/* But mask out 444 modes as they weren't supported */
133 		caps &= ~CLCD_CAP_444;
134 	}
135 
136 	/* Only TFT panels can do RGB888/BGR888 */
137 	if (!(fb->panel->cntl & CNTL_LCDTFT))
138 		caps &= ~CLCD_CAP_888;
139 
140 	memset(&var->transp, 0, sizeof(var->transp));
141 
142 	var->red.msb_right = 0;
143 	var->green.msb_right = 0;
144 	var->blue.msb_right = 0;
145 
146 	switch (var->bits_per_pixel) {
147 	case 1:
148 	case 2:
149 	case 4:
150 	case 8:
151 		/* If we can't do 5551, reject */
152 		caps &= CLCD_CAP_5551;
153 		if (!caps) {
154 			ret = -EINVAL;
155 			break;
156 		}
157 
158 		var->red.length		= var->bits_per_pixel;
159 		var->red.offset		= 0;
160 		var->green.length	= var->bits_per_pixel;
161 		var->green.offset	= 0;
162 		var->blue.length	= var->bits_per_pixel;
163 		var->blue.offset	= 0;
164 		break;
165 
166 	case 16:
167 		/* If we can't do 444, 5551 or 565, reject */
168 		if (!(caps & (CLCD_CAP_444 | CLCD_CAP_5551 | CLCD_CAP_565))) {
169 			ret = -EINVAL;
170 			break;
171 		}
172 
173 		/*
174 		 * Green length can be 4, 5 or 6 depending whether
175 		 * we're operating in 444, 5551 or 565 mode.
176 		 */
177 		if (var->green.length == 4 && caps & CLCD_CAP_444)
178 			caps &= CLCD_CAP_444;
179 		if (var->green.length == 5 && caps & CLCD_CAP_5551)
180 			caps &= CLCD_CAP_5551;
181 		else if (var->green.length == 6 && caps & CLCD_CAP_565)
182 			caps &= CLCD_CAP_565;
183 		else {
184 			/*
185 			 * PL110 officially only supports RGB555,
186 			 * but may be wired up to allow RGB565.
187 			 */
188 			if (caps & CLCD_CAP_565) {
189 				var->green.length = 6;
190 				caps &= CLCD_CAP_565;
191 			} else if (caps & CLCD_CAP_5551) {
192 				var->green.length = 5;
193 				caps &= CLCD_CAP_5551;
194 			} else {
195 				var->green.length = 4;
196 				caps &= CLCD_CAP_444;
197 			}
198 		}
199 
200 		if (var->green.length >= 5) {
201 			var->red.length = 5;
202 			var->blue.length = 5;
203 		} else {
204 			var->red.length = 4;
205 			var->blue.length = 4;
206 		}
207 		break;
208 	case 32:
209 		/* If we can't do 888, reject */
210 		caps &= CLCD_CAP_888;
211 		if (!caps) {
212 			ret = -EINVAL;
213 			break;
214 		}
215 
216 		var->red.length = 8;
217 		var->green.length = 8;
218 		var->blue.length = 8;
219 		break;
220 	default:
221 		ret = -EINVAL;
222 		break;
223 	}
224 
225 	/*
226 	 * >= 16bpp displays have separate colour component bitfields
227 	 * encoded in the pixel data.  Calculate their position from
228 	 * the bitfield length defined above.
229 	 */
230 	if (ret == 0 && var->bits_per_pixel >= 16) {
231 		bool bgr, rgb;
232 
233 		bgr = caps & CLCD_CAP_BGR && var->blue.offset == 0;
234 		rgb = caps & CLCD_CAP_RGB && var->red.offset == 0;
235 
236 		if (!bgr && !rgb)
237 			/*
238 			 * The requested format was not possible, try just
239 			 * our capabilities.  One of BGR or RGB must be
240 			 * supported.
241 			 */
242 			bgr = caps & CLCD_CAP_BGR;
243 
244 		if (bgr) {
245 			var->blue.offset = 0;
246 			var->green.offset = var->blue.offset + var->blue.length;
247 			var->red.offset = var->green.offset + var->green.length;
248 		} else {
249 			var->red.offset = 0;
250 			var->green.offset = var->red.offset + var->red.length;
251 			var->blue.offset = var->green.offset + var->green.length;
252 		}
253 	}
254 
255 	return ret;
256 }
257 
clcdfb_check_var(struct fb_var_screeninfo * var,struct fb_info * info)258 static int clcdfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
259 {
260 	struct clcd_fb *fb = to_clcd(info);
261 	int ret = -EINVAL;
262 
263 	if (fb->board->check)
264 		ret = fb->board->check(fb, var);
265 
266 	if (ret == 0 &&
267 	    var->xres_virtual * var->bits_per_pixel / 8 *
268 	    var->yres_virtual > fb->fb.fix.smem_len)
269 		ret = -EINVAL;
270 
271 	if (ret == 0)
272 		ret = clcdfb_set_bitfields(fb, var);
273 
274 	return ret;
275 }
276 
clcdfb_set_par(struct fb_info * info)277 static int clcdfb_set_par(struct fb_info *info)
278 {
279 	struct clcd_fb *fb = to_clcd(info);
280 	struct clcd_regs regs;
281 
282 	fb->fb.fix.line_length = fb->fb.var.xres_virtual *
283 				 fb->fb.var.bits_per_pixel / 8;
284 
285 	if (fb->fb.var.bits_per_pixel <= 8)
286 		fb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
287 	else
288 		fb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
289 
290 	fb->board->decode(fb, &regs);
291 
292 	clcdfb_disable(fb);
293 
294 	writel(regs.tim0, fb->regs + CLCD_TIM0);
295 	writel(regs.tim1, fb->regs + CLCD_TIM1);
296 	writel(regs.tim2, fb->regs + CLCD_TIM2);
297 	writel(regs.tim3, fb->regs + CLCD_TIM3);
298 
299 	clcdfb_set_start(fb);
300 
301 	clk_set_rate(fb->clk, (1000000000 / regs.pixclock) * 1000);
302 
303 	fb->clcd_cntl = regs.cntl;
304 
305 	clcdfb_enable(fb, regs.cntl);
306 
307 #ifdef DEBUG
308 	printk(KERN_INFO
309 	       "CLCD: Registers set to\n"
310 	       "  %08x %08x %08x %08x\n"
311 	       "  %08x %08x %08x %08x\n",
312 		readl(fb->regs + CLCD_TIM0), readl(fb->regs + CLCD_TIM1),
313 		readl(fb->regs + CLCD_TIM2), readl(fb->regs + CLCD_TIM3),
314 		readl(fb->regs + CLCD_UBAS), readl(fb->regs + CLCD_LBAS),
315 		readl(fb->regs + fb->off_ienb), readl(fb->regs + fb->off_cntl));
316 #endif
317 
318 	return 0;
319 }
320 
convert_bitfield(int val,struct fb_bitfield * bf)321 static inline u32 convert_bitfield(int val, struct fb_bitfield *bf)
322 {
323 	unsigned int mask = (1 << bf->length) - 1;
324 
325 	return (val >> (16 - bf->length) & mask) << bf->offset;
326 }
327 
328 /*
329  *  Set a single color register. The values supplied have a 16 bit
330  *  magnitude.  Return != 0 for invalid regno.
331  */
332 static int
clcdfb_setcolreg(unsigned int regno,unsigned int red,unsigned int green,unsigned int blue,unsigned int transp,struct fb_info * info)333 clcdfb_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
334 		 unsigned int blue, unsigned int transp, struct fb_info *info)
335 {
336 	struct clcd_fb *fb = to_clcd(info);
337 
338 	if (regno < 16)
339 		fb->cmap[regno] = convert_bitfield(transp, &fb->fb.var.transp) |
340 				  convert_bitfield(blue, &fb->fb.var.blue) |
341 				  convert_bitfield(green, &fb->fb.var.green) |
342 				  convert_bitfield(red, &fb->fb.var.red);
343 
344 	if (fb->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR && regno < 256) {
345 		int hw_reg = CLCD_PALETTE + ((regno * 2) & ~3);
346 		u32 val, mask, newval;
347 
348 		newval  = (red >> 11)  & 0x001f;
349 		newval |= (green >> 6) & 0x03e0;
350 		newval |= (blue >> 1)  & 0x7c00;
351 
352 		/*
353 		 * 3.2.11: if we're configured for big endian
354 		 * byte order, the palette entries are swapped.
355 		 */
356 		if (fb->clcd_cntl & CNTL_BEBO)
357 			regno ^= 1;
358 
359 		if (regno & 1) {
360 			newval <<= 16;
361 			mask = 0x0000ffff;
362 		} else {
363 			mask = 0xffff0000;
364 		}
365 
366 		val = readl(fb->regs + hw_reg) & mask;
367 		writel(val | newval, fb->regs + hw_reg);
368 	}
369 
370 	return regno > 255;
371 }
372 
373 /*
374  *  Blank the screen if blank_mode != 0, else unblank. If blank == NULL
375  *  then the caller blanks by setting the CLUT (Color Look Up Table) to all
376  *  black. Return 0 if blanking succeeded, != 0 if un-/blanking failed due
377  *  to e.g. a video mode which doesn't support it. Implements VESA suspend
378  *  and powerdown modes on hardware that supports disabling hsync/vsync:
379  *    blank_mode == 2: suspend vsync
380  *    blank_mode == 3: suspend hsync
381  *    blank_mode == 4: powerdown
382  */
clcdfb_blank(int blank_mode,struct fb_info * info)383 static int clcdfb_blank(int blank_mode, struct fb_info *info)
384 {
385 	struct clcd_fb *fb = to_clcd(info);
386 
387 	if (blank_mode != 0) {
388 		clcdfb_disable(fb);
389 	} else {
390 		clcdfb_enable(fb, fb->clcd_cntl);
391 	}
392 	return 0;
393 }
394 
clcdfb_mmap(struct fb_info * info,struct vm_area_struct * vma)395 static int clcdfb_mmap(struct fb_info *info,
396 		       struct vm_area_struct *vma)
397 {
398 	struct clcd_fb *fb = to_clcd(info);
399 	unsigned long len, off = vma->vm_pgoff << PAGE_SHIFT;
400 	int ret = -EINVAL;
401 
402 	len = info->fix.smem_len;
403 
404 	if (off <= len && vma->vm_end - vma->vm_start <= len - off &&
405 	    fb->board->mmap)
406 		ret = fb->board->mmap(fb, vma);
407 
408 	return ret;
409 }
410 
411 static struct fb_ops clcdfb_ops = {
412 	.owner		= THIS_MODULE,
413 	.fb_check_var	= clcdfb_check_var,
414 	.fb_set_par	= clcdfb_set_par,
415 	.fb_setcolreg	= clcdfb_setcolreg,
416 	.fb_blank	= clcdfb_blank,
417 	.fb_fillrect	= cfb_fillrect,
418 	.fb_copyarea	= cfb_copyarea,
419 	.fb_imageblit	= cfb_imageblit,
420 	.fb_mmap	= clcdfb_mmap,
421 };
422 
clcdfb_register(struct clcd_fb * fb)423 static int clcdfb_register(struct clcd_fb *fb)
424 {
425 	int ret;
426 
427 	/*
428 	 * ARM PL111 always has IENB at 0x1c; it's only PL110
429 	 * which is reversed on some platforms.
430 	 */
431 	if (amba_manf(fb->dev) == 0x41 && amba_part(fb->dev) == 0x111) {
432 		fb->off_ienb = CLCD_PL111_IENB;
433 		fb->off_cntl = CLCD_PL111_CNTL;
434 	} else {
435 #ifdef CONFIG_ARCH_VERSATILE
436 		fb->off_ienb = CLCD_PL111_IENB;
437 		fb->off_cntl = CLCD_PL111_CNTL;
438 #else
439 		fb->off_ienb = CLCD_PL110_IENB;
440 		fb->off_cntl = CLCD_PL110_CNTL;
441 #endif
442 	}
443 
444 	fb->clk = clk_get(&fb->dev->dev, NULL);
445 	if (IS_ERR(fb->clk)) {
446 		ret = PTR_ERR(fb->clk);
447 		goto out;
448 	}
449 
450 	ret = clk_prepare(fb->clk);
451 	if (ret)
452 		goto free_clk;
453 
454 	fb->fb.device		= &fb->dev->dev;
455 
456 	fb->fb.fix.mmio_start	= fb->dev->res.start;
457 	fb->fb.fix.mmio_len	= resource_size(&fb->dev->res);
458 
459 	fb->regs = ioremap(fb->fb.fix.mmio_start, fb->fb.fix.mmio_len);
460 	if (!fb->regs) {
461 		printk(KERN_ERR "CLCD: unable to remap registers\n");
462 		ret = -ENOMEM;
463 		goto clk_unprep;
464 	}
465 
466 	fb->fb.fbops		= &clcdfb_ops;
467 	fb->fb.flags		= FBINFO_FLAG_DEFAULT;
468 	fb->fb.pseudo_palette	= fb->cmap;
469 
470 	strncpy(fb->fb.fix.id, clcd_name, sizeof(fb->fb.fix.id));
471 	fb->fb.fix.type		= FB_TYPE_PACKED_PIXELS;
472 	fb->fb.fix.type_aux	= 0;
473 	fb->fb.fix.xpanstep	= 0;
474 	fb->fb.fix.ypanstep	= 0;
475 	fb->fb.fix.ywrapstep	= 0;
476 	fb->fb.fix.accel	= FB_ACCEL_NONE;
477 
478 	fb->fb.var.xres		= fb->panel->mode.xres;
479 	fb->fb.var.yres		= fb->panel->mode.yres;
480 	fb->fb.var.xres_virtual	= fb->panel->mode.xres;
481 	fb->fb.var.yres_virtual	= fb->panel->mode.yres;
482 	fb->fb.var.bits_per_pixel = fb->panel->bpp;
483 	fb->fb.var.grayscale	= fb->panel->grayscale;
484 	fb->fb.var.pixclock	= fb->panel->mode.pixclock;
485 	fb->fb.var.left_margin	= fb->panel->mode.left_margin;
486 	fb->fb.var.right_margin	= fb->panel->mode.right_margin;
487 	fb->fb.var.upper_margin	= fb->panel->mode.upper_margin;
488 	fb->fb.var.lower_margin	= fb->panel->mode.lower_margin;
489 	fb->fb.var.hsync_len	= fb->panel->mode.hsync_len;
490 	fb->fb.var.vsync_len	= fb->panel->mode.vsync_len;
491 	fb->fb.var.sync		= fb->panel->mode.sync;
492 	fb->fb.var.vmode	= fb->panel->mode.vmode;
493 	fb->fb.var.activate	= FB_ACTIVATE_NOW;
494 	fb->fb.var.nonstd	= 0;
495 	fb->fb.var.height	= fb->panel->height;
496 	fb->fb.var.width	= fb->panel->width;
497 	fb->fb.var.accel_flags	= 0;
498 
499 	fb->fb.monspecs.hfmin	= 0;
500 	fb->fb.monspecs.hfmax   = 100000;
501 	fb->fb.monspecs.vfmin	= 0;
502 	fb->fb.monspecs.vfmax	= 400;
503 	fb->fb.monspecs.dclkmin = 1000000;
504 	fb->fb.monspecs.dclkmax	= 100000000;
505 
506 	/*
507 	 * Make sure that the bitfields are set appropriately.
508 	 */
509 	clcdfb_set_bitfields(fb, &fb->fb.var);
510 
511 	/*
512 	 * Allocate colourmap.
513 	 */
514 	ret = fb_alloc_cmap(&fb->fb.cmap, 256, 0);
515 	if (ret)
516 		goto unmap;
517 
518 	/*
519 	 * Ensure interrupts are disabled.
520 	 */
521 	writel(0, fb->regs + fb->off_ienb);
522 
523 	fb_set_var(&fb->fb, &fb->fb.var);
524 
525 	dev_info(&fb->dev->dev, "%s hardware, %s display\n",
526 	         fb->board->name, fb->panel->mode.name);
527 
528 	ret = register_framebuffer(&fb->fb);
529 	if (ret == 0)
530 		goto out;
531 
532 	printk(KERN_ERR "CLCD: cannot register framebuffer (%d)\n", ret);
533 
534 	fb_dealloc_cmap(&fb->fb.cmap);
535  unmap:
536 	iounmap(fb->regs);
537  clk_unprep:
538 	clk_unprepare(fb->clk);
539  free_clk:
540 	clk_put(fb->clk);
541  out:
542 	return ret;
543 }
544 
clcdfb_probe(struct amba_device * dev,const struct amba_id * id)545 static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id)
546 {
547 	struct clcd_board *board = dev->dev.platform_data;
548 	struct clcd_fb *fb;
549 	int ret;
550 
551 	if (!board)
552 		return -EINVAL;
553 
554 	ret = amba_request_regions(dev, NULL);
555 	if (ret) {
556 		printk(KERN_ERR "CLCD: unable to reserve regs region\n");
557 		goto out;
558 	}
559 
560 	fb = kzalloc(sizeof(struct clcd_fb), GFP_KERNEL);
561 	if (!fb) {
562 		printk(KERN_INFO "CLCD: could not allocate new clcd_fb struct\n");
563 		ret = -ENOMEM;
564 		goto free_region;
565 	}
566 
567 	fb->dev = dev;
568 	fb->board = board;
569 
570 	dev_info(&fb->dev->dev, "PL%03x rev%u at 0x%08llx\n",
571 		amba_part(dev), amba_rev(dev),
572 		(unsigned long long)dev->res.start);
573 
574 	ret = fb->board->setup(fb);
575 	if (ret)
576 		goto free_fb;
577 
578 	ret = clcdfb_register(fb);
579 	if (ret == 0) {
580 		amba_set_drvdata(dev, fb);
581 		goto out;
582 	}
583 
584 	fb->board->remove(fb);
585  free_fb:
586 	kfree(fb);
587  free_region:
588 	amba_release_regions(dev);
589  out:
590 	return ret;
591 }
592 
clcdfb_remove(struct amba_device * dev)593 static int clcdfb_remove(struct amba_device *dev)
594 {
595 	struct clcd_fb *fb = amba_get_drvdata(dev);
596 
597 	amba_set_drvdata(dev, NULL);
598 
599 	clcdfb_disable(fb);
600 	unregister_framebuffer(&fb->fb);
601 	if (fb->fb.cmap.len)
602 		fb_dealloc_cmap(&fb->fb.cmap);
603 	iounmap(fb->regs);
604 	clk_unprepare(fb->clk);
605 	clk_put(fb->clk);
606 
607 	fb->board->remove(fb);
608 
609 	kfree(fb);
610 
611 	amba_release_regions(dev);
612 
613 	return 0;
614 }
615 
616 static struct amba_id clcdfb_id_table[] = {
617 	{
618 		.id	= 0x00041110,
619 		.mask	= 0x000ffffe,
620 	},
621 	{ 0, 0 },
622 };
623 
624 MODULE_DEVICE_TABLE(amba, clcdfb_id_table);
625 
626 static struct amba_driver clcd_driver = {
627 	.drv 		= {
628 		.name	= "clcd-pl11x",
629 	},
630 	.probe		= clcdfb_probe,
631 	.remove		= clcdfb_remove,
632 	.id_table	= clcdfb_id_table,
633 };
634 
amba_clcdfb_init(void)635 static int __init amba_clcdfb_init(void)
636 {
637 	if (fb_get_options("ambafb", NULL))
638 		return -ENODEV;
639 
640 	return amba_driver_register(&clcd_driver);
641 }
642 
643 module_init(amba_clcdfb_init);
644 
amba_clcdfb_exit(void)645 static void __exit amba_clcdfb_exit(void)
646 {
647 	amba_driver_unregister(&clcd_driver);
648 }
649 
650 module_exit(amba_clcdfb_exit);
651 
652 MODULE_DESCRIPTION("ARM PrimeCell PL110 CLCD core driver");
653 MODULE_LICENSE("GPL");
654