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