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