Branch data Line data Source code
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/dma-mapping.h>
14 : : #include <linux/module.h>
15 : : #include <linux/kernel.h>
16 : : #include <linux/errno.h>
17 : : #include <linux/string.h>
18 : : #include <linux/slab.h>
19 : : #include <linux/delay.h>
20 : : #include <linux/dma-mapping.h>
21 : : #include <linux/memblock.h>
22 : : #include <linux/mm.h>
23 : : #include <linux/of.h>
24 : : #include <linux/fb.h>
25 : : #include <linux/init.h>
26 : : #include <linux/ioport.h>
27 : : #include <linux/list.h>
28 : : #include <linux/amba/bus.h>
29 : : #include <linux/amba/clcd.h>
30 : : #include <linux/clk.h>
31 : : #include <linux/hardirq.h>
32 : :
33 : : #include <asm/sizes.h>
34 : :
35 : : #define to_clcd(info) container_of(info, struct clcd_fb, fb)
36 : :
37 : : #ifdef CONFIG_ARM
38 : : #define clcdfb_dma_alloc dma_alloc_writecombine
39 : : #define clcdfb_dma_free dma_free_writecombine
40 : : #define clcdfb_dma_mmap dma_mmap_writecombine
41 : : #else
42 : : #define clcdfb_dma_alloc dma_alloc_coherent
43 : : #define clcdfb_dma_free dma_free_coherent
44 : : #define clcdfb_dma_mmap dma_mmap_coherent
45 : : #endif
46 : :
47 : : /* This is limited to 16 characters when displayed by X startup */
48 : : static const char *clcd_name = "CLCD FB";
49 : : static char *def_mode;
50 : : module_param_named(mode, def_mode, charp, 0);
51 : :
52 : : /*
53 : : * Unfortunately, the enable/disable functions may be called either from
54 : : * process or IRQ context, and we _need_ to delay. This is _not_ good.
55 : : */
56 : : static inline void clcdfb_sleep(unsigned int ms)
57 : : {
58 [ # # ][ # # ]: 0 : if (in_atomic()) {
59 [ # # ][ # # ]: 0 : mdelay(ms);
60 : : } else {
61 : 0 : msleep(ms);
62 : : }
63 : : }
64 : :
65 : : static inline void clcdfb_set_start(struct clcd_fb *fb)
66 : : {
67 : 0 : unsigned long ustart = fb->fb.fix.smem_start;
68 : : unsigned long lstart;
69 : :
70 : 0 : ustart += fb->fb.var.yoffset * fb->fb.fix.line_length;
71 : 0 : lstart = ustart + fb->fb.var.yres * fb->fb.fix.line_length / 2;
72 : :
73 : 0 : writel(ustart, fb->regs + CLCD_UBAS);
74 : 0 : writel(lstart, fb->regs + CLCD_LBAS);
75 : : }
76 : :
77 : 0 : static void clcdfb_disable(struct clcd_fb *fb)
78 : : {
79 : : u32 val;
80 : :
81 [ # # ]: 0 : if (fb->board->disable)
82 : 0 : fb->board->disable(fb);
83 : :
84 : 0 : val = readl(fb->regs + fb->off_cntl);
85 [ # # ]: 0 : if (val & CNTL_LCDPWR) {
86 : 0 : val &= ~CNTL_LCDPWR;
87 : 0 : writel(val, fb->regs + fb->off_cntl);
88 : :
89 : : clcdfb_sleep(20);
90 : : }
91 [ # # ]: 0 : if (val & CNTL_LCDEN) {
92 : 0 : val &= ~CNTL_LCDEN;
93 : 0 : writel(val, fb->regs + fb->off_cntl);
94 : : }
95 : :
96 : : /*
97 : : * Disable CLCD clock source.
98 : : */
99 [ # # ]: 0 : if (fb->clk_enabled) {
100 : 0 : fb->clk_enabled = false;
101 : 0 : clk_disable(fb->clk);
102 : : }
103 : 0 : }
104 : :
105 : 0 : static void clcdfb_enable(struct clcd_fb *fb, u32 cntl)
106 : : {
107 : : /*
108 : : * Enable the CLCD clock source.
109 : : */
110 [ # # ]: 0 : if (!fb->clk_enabled) {
111 : 0 : fb->clk_enabled = true;
112 : 0 : clk_enable(fb->clk);
113 : : }
114 : :
115 : : /*
116 : : * Bring up by first enabling..
117 : : */
118 : 0 : cntl |= CNTL_LCDEN;
119 : 0 : writel(cntl, fb->regs + fb->off_cntl);
120 : :
121 : : clcdfb_sleep(20);
122 : :
123 : : /*
124 : : * and now apply power.
125 : : */
126 : 0 : cntl |= CNTL_LCDPWR;
127 : 0 : writel(cntl, fb->regs + fb->off_cntl);
128 : :
129 : : /*
130 : : * finally, enable the interface.
131 : : */
132 [ # # ]: 0 : if (fb->board->enable)
133 : 0 : fb->board->enable(fb);
134 : 0 : }
135 : :
136 : : static int
137 : 0 : clcdfb_set_bitfields(struct clcd_fb *fb, struct fb_var_screeninfo *var)
138 : : {
139 : : u32 caps;
140 : : int ret = 0;
141 : :
142 [ # # ][ # # ]: 0 : if (fb->panel->caps && fb->board->caps)
143 : 0 : caps = fb->panel->caps & fb->board->caps;
144 : : else {
145 : : /* Old way of specifying what can be used */
146 [ # # ]: 0 : caps = fb->panel->cntl & CNTL_BGR ?
147 : : CLCD_CAP_BGR : CLCD_CAP_RGB;
148 : : /* But mask out 444 modes as they weren't supported */
149 : 0 : caps &= ~CLCD_CAP_444;
150 : : }
151 : :
152 : : /* Only TFT panels can do RGB888/BGR888 */
153 [ # # ]: 0 : if (!(fb->panel->cntl & CNTL_LCDTFT))
154 : 0 : caps &= ~CLCD_CAP_888;
155 : :
156 : 0 : memset(&var->transp, 0, sizeof(var->transp));
157 : :
158 : 0 : var->red.msb_right = 0;
159 : 0 : var->green.msb_right = 0;
160 : 0 : var->blue.msb_right = 0;
161 : :
162 [ # # ][ # # ]: 0 : switch (var->bits_per_pixel) {
[ # # ][ # # ]
163 : : case 1:
164 : : case 2:
165 : : case 4:
166 : : case 8:
167 : : /* If we can't do 5551, reject */
168 : 0 : caps &= CLCD_CAP_5551;
169 [ # # ]: 0 : if (!caps) {
170 : : ret = -EINVAL;
171 : : break;
172 : : }
173 : :
174 : 0 : var->red.length = var->bits_per_pixel;
175 : 0 : var->red.offset = 0;
176 : 0 : var->green.length = var->bits_per_pixel;
177 : 0 : var->green.offset = 0;
178 : 0 : var->blue.length = var->bits_per_pixel;
179 : 0 : var->blue.offset = 0;
180 : : break;
181 : :
182 : : case 16:
183 : : /* If we can't do 444, 5551 or 565, reject */
184 [ # # ]: 0 : if (!(caps & (CLCD_CAP_444 | CLCD_CAP_5551 | CLCD_CAP_565))) {
185 : : ret = -EINVAL;
186 : : break;
187 : : }
188 : :
189 : : /*
190 : : * Green length can be 4, 5 or 6 depending whether
191 : : * we're operating in 444, 5551 or 565 mode.
192 : : */
193 [ # # ][ # # ]: 0 : if (var->green.length == 4 && caps & CLCD_CAP_444)
194 : : caps &= CLCD_CAP_444;
195 [ # # ][ # # ]: 0 : if (var->green.length == 5 && caps & CLCD_CAP_5551)
196 : : caps &= CLCD_CAP_5551;
197 [ # # ][ # # ]: 0 : else if (var->green.length == 6 && caps & CLCD_CAP_565)
198 : : caps &= CLCD_CAP_565;
199 : : else {
200 : : /*
201 : : * PL110 officially only supports RGB555,
202 : : * but may be wired up to allow RGB565.
203 : : */
204 [ # # ]: 0 : if (caps & CLCD_CAP_565) {
205 : 0 : var->green.length = 6;
206 : : caps &= CLCD_CAP_565;
207 [ # # ]: 0 : } else if (caps & CLCD_CAP_5551) {
208 : 0 : var->green.length = 5;
209 : : caps &= CLCD_CAP_5551;
210 : : } else {
211 : 0 : var->green.length = 4;
212 : 0 : caps &= CLCD_CAP_444;
213 : : }
214 : : }
215 : :
216 [ # # ]: 0 : if (var->green.length >= 5) {
217 : 0 : var->red.length = 5;
218 : 0 : var->blue.length = 5;
219 : : } else {
220 : 0 : var->red.length = 4;
221 : 0 : var->blue.length = 4;
222 : : }
223 : : break;
224 : : case 32:
225 : : /* If we can't do 888, reject */
226 : 0 : caps &= CLCD_CAP_888;
227 [ # # ]: 0 : if (!caps) {
228 : : ret = -EINVAL;
229 : : break;
230 : : }
231 : :
232 : 0 : var->red.length = 8;
233 : 0 : var->green.length = 8;
234 : 0 : var->blue.length = 8;
235 : : break;
236 : : default:
237 : : ret = -EINVAL;
238 : : break;
239 : : }
240 : :
241 : : /*
242 : : * >= 16bpp displays have separate colour component bitfields
243 : : * encoded in the pixel data. Calculate their position from
244 : : * the bitfield length defined above.
245 : : */
246 [ # # ][ # # ]: 0 : if (ret == 0 && var->bits_per_pixel >= 16) {
247 : : bool bgr, rgb;
248 : :
249 [ # # ][ # # ]: 0 : bgr = caps & CLCD_CAP_BGR && var->blue.offset == 0;
250 [ # # ][ # # ]: 0 : rgb = caps & CLCD_CAP_RGB && var->red.offset == 0;
251 : :
252 : : /*
253 : : * Seems that for 32-bit mode there is confusion about RGB
254 : : * ordering somewhere between user-side, kernel and hardware.
255 : : * The following hack seems get things working, at least on
256 : : * vexpress hardware and models...
257 : : */
258 [ # # ]: 0 : if (var->bits_per_pixel == 32) {
259 : : bgr = false;
260 : : rgb = true;
261 : : }
262 : :
263 [ # # ]: 0 : if (!bgr && !rgb)
264 : : /*
265 : : * The requested format was not possible, try just
266 : : * our capabilities. One of BGR or RGB must be
267 : : * supported.
268 : : */
269 : 0 : bgr = caps & CLCD_CAP_BGR;
270 : :
271 [ # # ]: 0 : if (bgr) {
272 : 0 : var->blue.offset = 0;
273 : 0 : var->green.offset = var->blue.offset + var->blue.length;
274 : 0 : var->red.offset = var->green.offset + var->green.length;
275 : : } else {
276 : 0 : var->red.offset = 0;
277 : 0 : var->green.offset = var->red.offset + var->red.length;
278 : 0 : var->blue.offset = var->green.offset + var->green.length;
279 : : }
280 : : }
281 : :
282 : 0 : return ret;
283 : : }
284 : :
285 : 0 : static int clcdfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
286 : : {
287 : 0 : struct clcd_fb *fb = to_clcd(info);
288 : : int ret = -EINVAL;
289 : :
290 [ # # ]: 0 : if (fb->board->check)
291 : 0 : ret = fb->board->check(fb, var);
292 : :
293 [ # # ][ # # ]: 0 : if (ret == 0 &&
294 : 0 : var->xres_virtual * var->bits_per_pixel / 8 *
295 : 0 : var->yres_virtual > fb->fb.fix.smem_len)
296 : : ret = -EINVAL;
297 : :
298 [ # # ]: 0 : if (ret == 0)
299 : 0 : ret = clcdfb_set_bitfields(fb, var);
300 : :
301 : 0 : return ret;
302 : : }
303 : :
304 : 0 : static int clcdfb_set_par(struct fb_info *info)
305 : : {
306 : : struct clcd_fb *fb = to_clcd(info);
307 : : struct clcd_regs regs;
308 : :
309 : 0 : fb->fb.fix.line_length = fb->fb.var.xres_virtual *
310 : 0 : fb->fb.var.bits_per_pixel / 8;
311 : :
312 [ # # ]: 0 : if (fb->fb.var.bits_per_pixel <= 8)
313 : 0 : fb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
314 : : else
315 : 0 : fb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
316 : :
317 : 0 : fb->board->decode(fb, ®s);
318 : :
319 : 0 : clcdfb_disable(fb);
320 : :
321 : 0 : writel(regs.tim0, fb->regs + CLCD_TIM0);
322 : 0 : writel(regs.tim1, fb->regs + CLCD_TIM1);
323 : 0 : writel(regs.tim2, fb->regs + CLCD_TIM2);
324 : 0 : writel(regs.tim3, fb->regs + CLCD_TIM3);
325 : :
326 : : clcdfb_set_start(fb);
327 : :
328 : 0 : clk_set_rate(fb->clk, (1000000000 / regs.pixclock) * 1000);
329 : :
330 : 0 : fb->clcd_cntl = regs.cntl;
331 : :
332 : 0 : clcdfb_enable(fb, regs.cntl);
333 : :
334 : : #ifdef DEBUG
335 : : printk(KERN_INFO
336 : : "CLCD: Registers set to\n"
337 : : " %08x %08x %08x %08x\n"
338 : : " %08x %08x %08x %08x\n",
339 : : readl(fb->regs + CLCD_TIM0), readl(fb->regs + CLCD_TIM1),
340 : : readl(fb->regs + CLCD_TIM2), readl(fb->regs + CLCD_TIM3),
341 : : readl(fb->regs + CLCD_UBAS), readl(fb->regs + CLCD_LBAS),
342 : : readl(fb->regs + fb->off_ienb), readl(fb->regs + fb->off_cntl));
343 : : #endif
344 : :
345 : 0 : return 0;
346 : : }
347 : :
348 : : static inline u32 convert_bitfield(int val, struct fb_bitfield *bf)
349 : : {
350 : 0 : unsigned int mask = (1 << bf->length) - 1;
351 : :
352 : 0 : return (val >> (16 - bf->length) & mask) << bf->offset;
353 : : }
354 : :
355 : : /*
356 : : * Set a single color register. The values supplied have a 16 bit
357 : : * magnitude. Return != 0 for invalid regno.
358 : : */
359 : : static int
360 : 0 : clcdfb_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
361 : : unsigned int blue, unsigned int transp, struct fb_info *info)
362 : : {
363 : : struct clcd_fb *fb = to_clcd(info);
364 : :
365 [ # # ]: 0 : if (regno < 16)
366 : 0 : fb->cmap[regno] = convert_bitfield(transp, &fb->fb.var.transp) |
367 : 0 : convert_bitfield(blue, &fb->fb.var.blue) |
368 : 0 : convert_bitfield(green, &fb->fb.var.green) |
369 : 0 : convert_bitfield(red, &fb->fb.var.red);
370 : :
371 [ # # ][ # # ]: 0 : if (fb->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR && regno < 256) {
372 : 0 : int hw_reg = CLCD_PALETTE + ((regno * 2) & ~3);
373 : : u32 val, mask, newval;
374 : :
375 : 0 : newval = (red >> 11) & 0x001f;
376 : 0 : newval |= (green >> 6) & 0x03e0;
377 : 0 : newval |= (blue >> 1) & 0x7c00;
378 : :
379 : : /*
380 : : * 3.2.11: if we're configured for big endian
381 : : * byte order, the palette entries are swapped.
382 : : */
383 [ # # ]: 0 : if (fb->clcd_cntl & CNTL_BEBO)
384 : 0 : regno ^= 1;
385 : :
386 [ # # ]: 0 : if (regno & 1) {
387 : 0 : newval <<= 16;
388 : : mask = 0x0000ffff;
389 : : } else {
390 : : mask = 0xffff0000;
391 : : }
392 : :
393 : 0 : val = readl(fb->regs + hw_reg) & mask;
394 : 0 : writel(val | newval, fb->regs + hw_reg);
395 : : }
396 : :
397 : 0 : return regno > 255;
398 : : }
399 : :
400 : : /*
401 : : * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
402 : : * then the caller blanks by setting the CLUT (Color Look Up Table) to all
403 : : * black. Return 0 if blanking succeeded, != 0 if un-/blanking failed due
404 : : * to e.g. a video mode which doesn't support it. Implements VESA suspend
405 : : * and powerdown modes on hardware that supports disabling hsync/vsync:
406 : : * blank_mode == 2: suspend vsync
407 : : * blank_mode == 3: suspend hsync
408 : : * blank_mode == 4: powerdown
409 : : */
410 : 0 : static int clcdfb_blank(int blank_mode, struct fb_info *info)
411 : : {
412 : : struct clcd_fb *fb = to_clcd(info);
413 : :
414 [ # # ]: 0 : if (blank_mode != 0) {
415 : 0 : clcdfb_disable(fb);
416 : : } else {
417 : 0 : clcdfb_enable(fb, fb->clcd_cntl);
418 : : }
419 : 0 : return 0;
420 : : }
421 : :
422 : 0 : int clcdfb_mmap_dma(struct clcd_fb *fb, struct vm_area_struct *vma)
423 : : {
424 : 0 : return clcdfb_dma_mmap(&fb->dev->dev, vma,
425 : 0 : fb->fb.screen_base,
426 : 0 : fb->fb.fix.smem_start,
427 : : fb->fb.fix.smem_len);
428 : : }
429 : :
430 : 0 : int clcdfb_mmap_io(struct clcd_fb *fb, struct vm_area_struct *vma)
431 : : {
432 : : unsigned long user_count, count, pfn, off;
433 : :
434 : 0 : user_count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
435 : 0 : count = PAGE_ALIGN(fb->fb.fix.smem_len) >> PAGE_SHIFT;
436 : 0 : pfn = fb->fb.fix.smem_start >> PAGE_SHIFT;
437 : 0 : off = vma->vm_pgoff;
438 : :
439 : 0 : vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
440 : :
441 [ # # ][ # # ]: 0 : if (off < count && user_count <= (count - off))
442 : 0 : return remap_pfn_range(vma, vma->vm_start, pfn + off,
443 : : user_count << PAGE_SHIFT,
444 : : vma->vm_page_prot);
445 : :
446 : : return -ENXIO;
447 : : }
448 : :
449 : 0 : void clcdfb_remove_dma(struct clcd_fb *fb)
450 : : {
451 : 0 : clcdfb_dma_free(&fb->dev->dev, fb->fb.fix.smem_len,
452 : 0 : fb->fb.screen_base, fb->fb.fix.smem_start);
453 : 0 : }
454 : :
455 : 0 : void clcdfb_remove_io(struct clcd_fb *fb)
456 : : {
457 : 0 : iounmap(fb->fb.screen_base);
458 : 0 : }
459 : :
460 : 0 : static int clcdfb_mmap(struct fb_info *info,
461 : : struct vm_area_struct *vma)
462 : : {
463 : : struct clcd_fb *fb = to_clcd(info);
464 : 0 : unsigned long len, off = vma->vm_pgoff << PAGE_SHIFT;
465 : : int ret = -EINVAL;
466 : :
467 : 0 : len = info->fix.smem_len;
468 : :
469 [ # # ][ # # ]: 0 : if (off <= len && vma->vm_end - vma->vm_start <= len - off &&
[ # # ]
470 : 0 : fb->board->mmap)
471 : 0 : ret = fb->board->mmap(fb, vma);
472 : :
473 : 0 : return ret;
474 : : }
475 : :
476 : : static struct fb_ops clcdfb_ops = {
477 : : .owner = THIS_MODULE,
478 : : .fb_check_var = clcdfb_check_var,
479 : : .fb_set_par = clcdfb_set_par,
480 : : .fb_setcolreg = clcdfb_setcolreg,
481 : : .fb_blank = clcdfb_blank,
482 : : .fb_fillrect = cfb_fillrect,
483 : : .fb_copyarea = cfb_copyarea,
484 : : .fb_imageblit = cfb_imageblit,
485 : : .fb_mmap = clcdfb_mmap,
486 : : };
487 : :
488 : 0 : static int clcdfb_register(struct clcd_fb *fb)
489 : : {
490 : : int ret;
491 : :
492 : : /*
493 : : * ARM PL111 always has IENB at 0x1c; it's only PL110
494 : : * which is reversed on some platforms.
495 : : */
496 [ # # ][ # # ]: 0 : if (amba_manf(fb->dev) == 0x41 && amba_part(fb->dev) == 0x111) {
497 : 0 : fb->off_ienb = CLCD_PL111_IENB;
498 : 0 : fb->off_cntl = CLCD_PL111_CNTL;
499 : : } else {
500 : : #ifdef CONFIG_ARCH_VERSATILE
501 : : fb->off_ienb = CLCD_PL111_IENB;
502 : : fb->off_cntl = CLCD_PL111_CNTL;
503 : : #else
504 : 0 : fb->off_ienb = CLCD_PL110_IENB;
505 : 0 : fb->off_cntl = CLCD_PL110_CNTL;
506 : : #endif
507 : : }
508 : :
509 : 0 : fb->clk = clk_get(&fb->dev->dev, NULL);
510 [ # # ]: 0 : if (IS_ERR(fb->clk)) {
511 : : ret = PTR_ERR(fb->clk);
512 : 0 : goto out;
513 : : }
514 : :
515 : 0 : ret = clk_prepare(fb->clk);
516 [ # # ]: 0 : if (ret)
517 : : goto free_clk;
518 : :
519 : 0 : fb->fb.device = &fb->dev->dev;
520 : :
521 : 0 : fb->fb.fix.mmio_start = fb->dev->res.start;
522 : 0 : fb->fb.fix.mmio_len = resource_size(&fb->dev->res);
523 : :
524 : 0 : fb->regs = ioremap(fb->fb.fix.mmio_start, fb->fb.fix.mmio_len);
525 [ # # ]: 0 : if (!fb->regs) {
526 : 0 : printk(KERN_ERR "CLCD: unable to remap registers\n");
527 : : ret = -ENOMEM;
528 : 0 : goto clk_unprep;
529 : : }
530 : :
531 : 0 : fb->fb.fbops = &clcdfb_ops;
532 : 0 : fb->fb.flags = FBINFO_FLAG_DEFAULT;
533 : 0 : fb->fb.pseudo_palette = fb->cmap;
534 : :
535 : 0 : strncpy(fb->fb.fix.id, clcd_name, sizeof(fb->fb.fix.id));
536 : 0 : fb->fb.fix.type = FB_TYPE_PACKED_PIXELS;
537 : 0 : fb->fb.fix.type_aux = 0;
538 : 0 : fb->fb.fix.xpanstep = 0;
539 : 0 : fb->fb.fix.ypanstep = 0;
540 : 0 : fb->fb.fix.ywrapstep = 0;
541 : 0 : fb->fb.fix.accel = FB_ACCEL_NONE;
542 : :
543 : 0 : fb->fb.var.xres = fb->panel->mode.xres;
544 : 0 : fb->fb.var.yres = fb->panel->mode.yres;
545 : 0 : fb->fb.var.xres_virtual = fb->panel->mode.xres;
546 : 0 : fb->fb.var.yres_virtual = fb->panel->mode.yres;
547 : 0 : fb->fb.var.bits_per_pixel = fb->panel->bpp;
548 : 0 : fb->fb.var.grayscale = fb->panel->grayscale;
549 : 0 : fb->fb.var.pixclock = fb->panel->mode.pixclock;
550 : 0 : fb->fb.var.left_margin = fb->panel->mode.left_margin;
551 : 0 : fb->fb.var.right_margin = fb->panel->mode.right_margin;
552 : 0 : fb->fb.var.upper_margin = fb->panel->mode.upper_margin;
553 : 0 : fb->fb.var.lower_margin = fb->panel->mode.lower_margin;
554 : 0 : fb->fb.var.hsync_len = fb->panel->mode.hsync_len;
555 : 0 : fb->fb.var.vsync_len = fb->panel->mode.vsync_len;
556 : 0 : fb->fb.var.sync = fb->panel->mode.sync;
557 : 0 : fb->fb.var.vmode = fb->panel->mode.vmode;
558 : 0 : fb->fb.var.activate = FB_ACTIVATE_NOW;
559 : 0 : fb->fb.var.nonstd = 0;
560 : 0 : fb->fb.var.height = fb->panel->height;
561 : 0 : fb->fb.var.width = fb->panel->width;
562 : 0 : fb->fb.var.accel_flags = 0;
563 : :
564 : 0 : fb->fb.monspecs.hfmin = 0;
565 : 0 : fb->fb.monspecs.hfmax = 100000;
566 : 0 : fb->fb.monspecs.vfmin = 0;
567 : 0 : fb->fb.monspecs.vfmax = 400;
568 : 0 : fb->fb.monspecs.dclkmin = 1000000;
569 : 0 : fb->fb.monspecs.dclkmax = 100000000;
570 : :
571 : : /*
572 : : * Make sure that the bitfields are set appropriately.
573 : : */
574 : 0 : clcdfb_set_bitfields(fb, &fb->fb.var);
575 : :
576 : : /*
577 : : * Allocate colourmap.
578 : : */
579 : 0 : ret = fb_alloc_cmap(&fb->fb.cmap, 256, 0);
580 [ # # ]: 0 : if (ret)
581 : : goto unmap;
582 : :
583 : : /*
584 : : * Ensure interrupts are disabled.
585 : : */
586 : 0 : writel(0, fb->regs + fb->off_ienb);
587 : :
588 : 0 : fb_set_var(&fb->fb, &fb->fb.var);
589 : :
590 : 0 : dev_info(&fb->dev->dev, "%s hardware, %s display\n",
591 : : fb->board->name, fb->panel->mode.name);
592 : :
593 : 0 : ret = register_framebuffer(&fb->fb);
594 [ # # ]: 0 : if (ret == 0)
595 : : goto out;
596 : :
597 : 0 : printk(KERN_ERR "CLCD: cannot register framebuffer (%d)\n", ret);
598 : :
599 : 0 : fb_dealloc_cmap(&fb->fb.cmap);
600 : : unmap:
601 : 0 : iounmap(fb->regs);
602 : : clk_unprep:
603 : 0 : clk_unprepare(fb->clk);
604 : : free_clk:
605 : 0 : clk_put(fb->clk);
606 : : out:
607 : 0 : return ret;
608 : : }
609 : :
610 : : struct string_lookup {
611 : : const char *string;
612 : : const u32 val;
613 : : };
614 : :
615 : : static struct string_lookup vmode_lookups[] = {
616 : : { "FB_VMODE_NONINTERLACED", FB_VMODE_NONINTERLACED},
617 : : { "FB_VMODE_INTERLACED", FB_VMODE_INTERLACED},
618 : : { "FB_VMODE_DOUBLE", FB_VMODE_DOUBLE},
619 : : { "FB_VMODE_ODD_FLD_FIRST", FB_VMODE_ODD_FLD_FIRST},
620 : : { NULL, 0 },
621 : : };
622 : :
623 : : static struct string_lookup tim2_lookups[] = {
624 : : { "TIM2_CLKSEL", TIM2_CLKSEL},
625 : : { "TIM2_IVS", TIM2_IVS},
626 : : { "TIM2_IHS", TIM2_IHS},
627 : : { "TIM2_IPC", TIM2_IPC},
628 : : { "TIM2_IOE", TIM2_IOE},
629 : : { "TIM2_BCD", TIM2_BCD},
630 : : { NULL, 0},
631 : : };
632 : : static struct string_lookup cntl_lookups[] = {
633 : : {"CNTL_LCDEN", CNTL_LCDEN},
634 : : {"CNTL_LCDBPP1", CNTL_LCDBPP1},
635 : : {"CNTL_LCDBPP2", CNTL_LCDBPP2},
636 : : {"CNTL_LCDBPP4", CNTL_LCDBPP4},
637 : : {"CNTL_LCDBPP8", CNTL_LCDBPP8},
638 : : {"CNTL_LCDBPP16", CNTL_LCDBPP16},
639 : : {"CNTL_LCDBPP16_565", CNTL_LCDBPP16_565},
640 : : {"CNTL_LCDBPP16_444", CNTL_LCDBPP16_444},
641 : : {"CNTL_LCDBPP24", CNTL_LCDBPP24},
642 : : {"CNTL_LCDBW", CNTL_LCDBW},
643 : : {"CNTL_LCDTFT", CNTL_LCDTFT},
644 : : {"CNTL_LCDMONO8", CNTL_LCDMONO8},
645 : : {"CNTL_LCDDUAL", CNTL_LCDDUAL},
646 : : {"CNTL_BGR", CNTL_BGR},
647 : : {"CNTL_BEBO", CNTL_BEBO},
648 : : {"CNTL_BEPO", CNTL_BEPO},
649 : : {"CNTL_LCDPWR", CNTL_LCDPWR},
650 : : {"CNTL_LCDVCOMP(1)", CNTL_LCDVCOMP(1)},
651 : : {"CNTL_LCDVCOMP(2)", CNTL_LCDVCOMP(2)},
652 : : {"CNTL_LCDVCOMP(3)", CNTL_LCDVCOMP(3)},
653 : : {"CNTL_LCDVCOMP(4)", CNTL_LCDVCOMP(4)},
654 : : {"CNTL_LCDVCOMP(5)", CNTL_LCDVCOMP(5)},
655 : : {"CNTL_LCDVCOMP(6)", CNTL_LCDVCOMP(6)},
656 : : {"CNTL_LCDVCOMP(7)", CNTL_LCDVCOMP(7)},
657 : : {"CNTL_LDMAFIFOTIME", CNTL_LDMAFIFOTIME},
658 : : {"CNTL_WATERMARK", CNTL_WATERMARK},
659 : : { NULL, 0},
660 : : };
661 : : static struct string_lookup caps_lookups[] = {
662 : : {"CLCD_CAP_RGB444", CLCD_CAP_RGB444},
663 : : {"CLCD_CAP_RGB5551", CLCD_CAP_RGB5551},
664 : : {"CLCD_CAP_RGB565", CLCD_CAP_RGB565},
665 : : {"CLCD_CAP_RGB888", CLCD_CAP_RGB888},
666 : : {"CLCD_CAP_BGR444", CLCD_CAP_BGR444},
667 : : {"CLCD_CAP_BGR5551", CLCD_CAP_BGR5551},
668 : : {"CLCD_CAP_BGR565", CLCD_CAP_BGR565},
669 : : {"CLCD_CAP_BGR888", CLCD_CAP_BGR888},
670 : : {"CLCD_CAP_444", CLCD_CAP_444},
671 : : {"CLCD_CAP_5551", CLCD_CAP_5551},
672 : : {"CLCD_CAP_565", CLCD_CAP_565},
673 : : {"CLCD_CAP_888", CLCD_CAP_888},
674 : : {"CLCD_CAP_RGB", CLCD_CAP_RGB},
675 : : {"CLCD_CAP_BGR", CLCD_CAP_BGR},
676 : : {"CLCD_CAP_ALL", CLCD_CAP_ALL},
677 : : { NULL, 0},
678 : : };
679 : :
680 : 0 : u32 parse_setting(struct string_lookup *lookup, const char *name)
681 : : {
682 : : int i = 0;
683 [ # # ]: 0 : while (lookup[i].string != NULL) {
684 [ # # ]: 0 : if (strcmp(lookup[i].string, name) == 0)
685 : 0 : return lookup[i].val;
686 : 0 : ++i;
687 : : }
688 : : return -EINVAL;
689 : : }
690 : :
691 : 0 : u32 get_string_lookup(struct device_node *node, const char *name,
692 : : struct string_lookup *lookup)
693 : : {
694 : : const char *string;
695 : : int count, i, ret = 0;
696 : :
697 : 0 : count = of_property_count_strings(node, name);
698 [ # # ]: 0 : if (count >= 0)
699 [ # # ]: 0 : for (i = 0; i < count; i++)
700 [ # # ]: 0 : if (of_property_read_string_index(node, name, i,
701 : : &string) == 0)
702 : 0 : ret |= parse_setting(lookup, string);
703 : 0 : return ret;
704 : : }
705 : :
706 : 0 : int get_val(struct device_node *node, const char *string)
707 : : {
708 : 0 : u32 ret = 0;
709 : :
710 [ # # ]: 0 : if (of_property_read_u32(node, string, &ret))
711 : 0 : ret = -1;
712 : 0 : return ret;
713 : : }
714 : :
715 : 0 : struct clcd_panel *getPanel(struct device_node *node)
716 : : {
717 : : static struct clcd_panel panel;
718 : :
719 : 0 : panel.mode.refresh = get_val(node, "refresh");
720 : 0 : panel.mode.xres = get_val(node, "xres");
721 : 0 : panel.mode.yres = get_val(node, "yres");
722 : 0 : panel.mode.pixclock = get_val(node, "pixclock");
723 : 0 : panel.mode.left_margin = get_val(node, "left_margin");
724 : 0 : panel.mode.right_margin = get_val(node, "right_margin");
725 : 0 : panel.mode.upper_margin = get_val(node, "upper_margin");
726 : 0 : panel.mode.lower_margin = get_val(node, "lower_margin");
727 : 0 : panel.mode.hsync_len = get_val(node, "hsync_len");
728 : 0 : panel.mode.vsync_len = get_val(node, "vsync_len");
729 : 0 : panel.mode.sync = get_val(node, "sync");
730 : 0 : panel.bpp = get_val(node, "bpp");
731 : 0 : panel.width = (signed short) get_val(node, "width");
732 : 0 : panel.height = (signed short) get_val(node, "height");
733 : :
734 : 0 : panel.mode.vmode = get_string_lookup(node, "vmode", vmode_lookups);
735 : 0 : panel.tim2 = get_string_lookup(node, "tim2", tim2_lookups);
736 : 0 : panel.cntl = get_string_lookup(node, "cntl", cntl_lookups);
737 : 0 : panel.caps = get_string_lookup(node, "caps", caps_lookups);
738 : :
739 : 0 : return &panel;
740 : : }
741 : :
742 : 0 : struct clcd_panel *clcdfb_get_panel(const char *name)
743 : : {
744 : : struct device_node *node = NULL;
745 : : const char *mode;
746 : : struct clcd_panel *panel = NULL;
747 : :
748 : : do {
749 : 0 : node = of_find_compatible_node(node, NULL, "panel");
750 [ # # ]: 0 : if (node)
751 [ # # ]: 0 : if (of_property_read_string(node, "mode", &mode) == 0)
752 [ # # ]: 0 : if (strcmp(mode, name) == 0) {
753 : 0 : panel = getPanel(node);
754 : 0 : panel->mode.name = name;
755 : : }
756 [ # # ]: 0 : } while (node != NULL);
757 : :
758 : 0 : return panel;
759 : : }
760 : :
761 : : #ifdef CONFIG_OF
762 : 0 : static int clcdfb_dt_init(struct clcd_fb *fb)
763 : : {
764 : : int err = 0;
765 : : struct device_node *node;
766 : : const char *mode;
767 : : dma_addr_t dma;
768 : : u32 use_dma;
769 : : const __be32 *prop;
770 : : int len, na, ns;
771 : : phys_addr_t fb_base, fb_size;
772 : :
773 : 0 : node = fb->dev->dev.of_node;
774 [ # # ]: 0 : if (!node)
775 : : return -ENODEV;
776 : :
777 : 0 : na = of_n_addr_cells(node);
778 : 0 : ns = of_n_size_cells(node);
779 : :
780 [ # # ][ # # ]: 0 : if (def_mode && strlen(def_mode) > 0) {
781 : 0 : fb->panel = clcdfb_get_panel(def_mode);
782 [ # # ]: 0 : if (!fb->panel)
783 : 0 : printk(KERN_ERR "CLCD: invalid mode specified on the command line (%s)\n", def_mode);
784 : : }
785 : :
786 [ # # ]: 0 : if (!fb->panel) {
787 [ # # ][ # # ]: 0 : if (WARN_ON(of_property_read_string(node, "mode", &mode)))
788 : : return -ENODEV;
789 : 0 : fb->panel = clcdfb_get_panel(mode);
790 : : }
791 : :
792 [ # # ]: 0 : if (!fb->panel)
793 : : return -EINVAL;
794 : 0 : fb->fb.fix.smem_len = fb->panel->mode.xres * fb->panel->mode.yres * 4;
795 : :
796 : 0 : fb->board->name = "Device Tree CLCD PL111";
797 : 0 : fb->board->caps = CLCD_CAP_5551 | CLCD_CAP_565 | CLCD_CAP_888;
798 : 0 : fb->board->check = clcdfb_check;
799 : 0 : fb->board->decode = clcdfb_decode;
800 : :
801 [ # # ]: 0 : if (of_property_read_u32(node, "use_dma", &use_dma))
802 : 0 : use_dma = 0;
803 : :
804 [ # # ]: 0 : if (use_dma) {
805 : 0 : fb->fb.screen_base = clcdfb_dma_alloc(&fb->dev->dev,
806 : : fb->fb.fix.smem_len,
807 : : &dma, GFP_KERNEL);
808 [ # # ]: 0 : if (!fb->fb.screen_base) {
809 : 0 : pr_err("CLCD: unable to map framebuffer\n");
810 : 0 : return -ENOMEM;
811 : : }
812 : :
813 : 0 : fb->fb.fix.smem_start = dma;
814 : 0 : fb->board->mmap = clcdfb_mmap_dma;
815 : 0 : fb->board->remove = clcdfb_remove_dma;
816 : : } else {
817 : 0 : prop = of_get_property(node, "framebuffer", &len);
818 [ # # ][ # # ]: 0 : if (WARN_ON(!prop || len < (na + ns) * sizeof(*prop)))
[ # # ][ # # ]
819 : : return -EINVAL;
820 : :
821 : 0 : fb_base = of_read_number(prop, na);
822 : 0 : fb_size = of_read_number(prop + na, ns);
823 : :
824 : 0 : fb->fb.fix.smem_start = fb_base;
825 : 0 : fb->fb.screen_base = ioremap_wc(fb_base, fb_size);
826 : 0 : fb->board->mmap = clcdfb_mmap_io;
827 : 0 : fb->board->remove = clcdfb_remove_io;
828 : : }
829 : :
830 : : return err;
831 : : }
832 : : #endif /* CONFIG_OF */
833 : :
834 : 0 : static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id)
835 : : {
836 : 0 : struct clcd_board *board = dev_get_platdata(&dev->dev);
837 : : struct clcd_fb *fb;
838 : : int ret;
839 : :
840 [ # # ]: 0 : if (!board) {
841 : : #ifdef CONFIG_OF
842 [ # # ]: 0 : if (dev->dev.of_node) {
843 : : board = kzalloc(sizeof(struct clcd_board), GFP_KERNEL);
844 [ # # ]: 0 : if (!board)
845 : : return -ENOMEM;
846 : 0 : board->setup = clcdfb_dt_init;
847 : : } else
848 : : #endif
849 : : return -EINVAL;
850 : : }
851 : :
852 : 0 : ret = dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32));
853 [ # # ]: 0 : if (ret)
854 : : goto out;
855 : :
856 : 0 : ret = amba_request_regions(dev, NULL);
857 [ # # ]: 0 : if (ret) {
858 : 0 : printk(KERN_ERR "CLCD: unable to reserve regs region\n");
859 : 0 : goto out;
860 : : }
861 : :
862 : : fb = kzalloc(sizeof(struct clcd_fb), GFP_KERNEL);
863 [ # # ]: 0 : if (!fb) {
864 : 0 : printk(KERN_INFO "CLCD: could not allocate new clcd_fb struct\n");
865 : : ret = -ENOMEM;
866 : 0 : goto free_region;
867 : : }
868 : :
869 : 0 : fb->dev = dev;
870 : 0 : fb->board = board;
871 : :
872 : 0 : dev_info(&fb->dev->dev, "PL%03x rev%u at 0x%08llx\n",
873 : : amba_part(dev), amba_rev(dev),
874 : : (unsigned long long)dev->res.start);
875 : :
876 : 0 : ret = fb->board->setup(fb);
877 [ # # ]: 0 : if (ret)
878 : : goto free_fb;
879 : :
880 : 0 : ret = clcdfb_register(fb);
881 [ # # ]: 0 : if (ret == 0) {
882 : 0 : amba_set_drvdata(dev, fb);
883 : 0 : goto out;
884 : : }
885 : :
886 : 0 : fb->board->remove(fb);
887 : : free_fb:
888 : 0 : kfree(fb);
889 : : free_region:
890 : 0 : amba_release_regions(dev);
891 : : out:
892 : 0 : return ret;
893 : : }
894 : :
895 : 0 : static int clcdfb_remove(struct amba_device *dev)
896 : : {
897 : 0 : struct clcd_fb *fb = amba_get_drvdata(dev);
898 : :
899 : 0 : clcdfb_disable(fb);
900 : 0 : unregister_framebuffer(&fb->fb);
901 [ # # ]: 0 : if (fb->fb.cmap.len)
902 : 0 : fb_dealloc_cmap(&fb->fb.cmap);
903 : 0 : iounmap(fb->regs);
904 : 0 : clk_unprepare(fb->clk);
905 : 0 : clk_put(fb->clk);
906 : :
907 : 0 : fb->board->remove(fb);
908 : :
909 : 0 : kfree(fb);
910 : :
911 : 0 : amba_release_regions(dev);
912 : :
913 : 0 : return 0;
914 : : }
915 : :
916 : : static struct amba_id clcdfb_id_table[] = {
917 : : {
918 : : .id = 0x00041110,
919 : : .mask = 0x000ffffe,
920 : : },
921 : : { 0, 0 },
922 : : };
923 : :
924 : : MODULE_DEVICE_TABLE(amba, clcdfb_id_table);
925 : :
926 : : static struct amba_driver clcd_driver = {
927 : : .drv = {
928 : : .name = "clcd-pl11x",
929 : : },
930 : : .probe = clcdfb_probe,
931 : : .remove = clcdfb_remove,
932 : : .id_table = clcdfb_id_table,
933 : : };
934 : :
935 : 0 : static int __init amba_clcdfb_init(void)
936 : : {
937 [ # # ]: 0 : if (fb_get_options("ambafb", NULL))
938 : : return -ENODEV;
939 : :
940 : 0 : return amba_driver_register(&clcd_driver);
941 : : }
942 : :
943 : : module_init(amba_clcdfb_init);
944 : :
945 : 0 : static void __exit amba_clcdfb_exit(void)
946 : : {
947 : 0 : amba_driver_unregister(&clcd_driver);
948 : 0 : }
949 : :
950 : : module_exit(amba_clcdfb_exit);
951 : :
952 : : MODULE_DESCRIPTION("ARM PrimeCell PL110 CLCD core driver");
953 : : MODULE_LICENSE("GPL");
|