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