Root/
1 | /* |
2 | * linux/drivers/video/ep93xx-fb.c |
3 | * |
4 | * Framebuffer support for the EP93xx series. |
5 | * |
6 | * Copyright (C) 2007 Bluewater Systems Ltd |
7 | * Author: Ryan Mallon |
8 | * |
9 | * Copyright (c) 2009 H Hartley Sweeten <hsweeten@visionengravers.com> |
10 | * |
11 | * Based on the Cirrus Logic ep93xxfb driver, and various other ep93xxfb |
12 | * drivers. |
13 | * |
14 | * This program is free software; you can redistribute it and/or modify |
15 | * it under the terms of the GNU General Public License version 2 as |
16 | * published by the Free Software Foundation. |
17 | * |
18 | */ |
19 | |
20 | #include <linux/platform_device.h> |
21 | #include <linux/module.h> |
22 | #include <linux/dma-mapping.h> |
23 | #include <linux/slab.h> |
24 | #include <linux/clk.h> |
25 | #include <linux/fb.h> |
26 | #include <linux/io.h> |
27 | |
28 | #include <linux/platform_data/video-ep93xx.h> |
29 | |
30 | /* Vertical Frame Timing Registers */ |
31 | #define EP93XXFB_VLINES_TOTAL 0x0000 /* SW locked */ |
32 | #define EP93XXFB_VSYNC 0x0004 /* SW locked */ |
33 | #define EP93XXFB_VACTIVE 0x0008 /* SW locked */ |
34 | #define EP93XXFB_VBLANK 0x0228 /* SW locked */ |
35 | #define EP93XXFB_VCLK 0x000c /* SW locked */ |
36 | |
37 | /* Horizontal Frame Timing Registers */ |
38 | #define EP93XXFB_HCLKS_TOTAL 0x0010 /* SW locked */ |
39 | #define EP93XXFB_HSYNC 0x0014 /* SW locked */ |
40 | #define EP93XXFB_HACTIVE 0x0018 /* SW locked */ |
41 | #define EP93XXFB_HBLANK 0x022c /* SW locked */ |
42 | #define EP93XXFB_HCLK 0x001c /* SW locked */ |
43 | |
44 | /* Frame Buffer Memory Configuration Registers */ |
45 | #define EP93XXFB_SCREEN_PAGE 0x0028 |
46 | #define EP93XXFB_SCREEN_HPAGE 0x002c |
47 | #define EP93XXFB_SCREEN_LINES 0x0030 |
48 | #define EP93XXFB_LINE_LENGTH 0x0034 |
49 | #define EP93XXFB_VLINE_STEP 0x0038 |
50 | #define EP93XXFB_LINE_CARRY 0x003c /* SW locked */ |
51 | #define EP93XXFB_EOL_OFFSET 0x0230 |
52 | |
53 | /* Other Video Registers */ |
54 | #define EP93XXFB_BRIGHTNESS 0x0020 |
55 | #define EP93XXFB_ATTRIBS 0x0024 /* SW locked */ |
56 | #define EP93XXFB_SWLOCK 0x007c /* SW locked */ |
57 | #define EP93XXFB_AC_RATE 0x0214 |
58 | #define EP93XXFB_FIFO_LEVEL 0x0234 |
59 | #define EP93XXFB_PIXELMODE 0x0054 |
60 | #define EP93XXFB_PIXELMODE_32BPP (0x7 << 0) |
61 | #define EP93XXFB_PIXELMODE_24BPP (0x6 << 0) |
62 | #define EP93XXFB_PIXELMODE_16BPP (0x4 << 0) |
63 | #define EP93XXFB_PIXELMODE_8BPP (0x2 << 0) |
64 | #define EP93XXFB_PIXELMODE_SHIFT_1P_24B (0x0 << 3) |
65 | #define EP93XXFB_PIXELMODE_SHIFT_1P_18B (0x1 << 3) |
66 | #define EP93XXFB_PIXELMODE_COLOR_LUT (0x0 << 10) |
67 | #define EP93XXFB_PIXELMODE_COLOR_888 (0x4 << 10) |
68 | #define EP93XXFB_PIXELMODE_COLOR_555 (0x5 << 10) |
69 | #define EP93XXFB_PARL_IF_OUT 0x0058 |
70 | #define EP93XXFB_PARL_IF_IN 0x005c |
71 | |
72 | /* Blink Control Registers */ |
73 | #define EP93XXFB_BLINK_RATE 0x0040 |
74 | #define EP93XXFB_BLINK_MASK 0x0044 |
75 | #define EP93XXFB_BLINK_PATTRN 0x0048 |
76 | #define EP93XXFB_PATTRN_MASK 0x004c |
77 | #define EP93XXFB_BKGRND_OFFSET 0x0050 |
78 | |
79 | /* Hardware Cursor Registers */ |
80 | #define EP93XXFB_CURSOR_ADR_START 0x0060 |
81 | #define EP93XXFB_CURSOR_ADR_RESET 0x0064 |
82 | #define EP93XXFB_CURSOR_SIZE 0x0068 |
83 | #define EP93XXFB_CURSOR_COLOR1 0x006c |
84 | #define EP93XXFB_CURSOR_COLOR2 0x0070 |
85 | #define EP93XXFB_CURSOR_BLINK_COLOR1 0x021c |
86 | #define EP93XXFB_CURSOR_BLINK_COLOR2 0x0220 |
87 | #define EP93XXFB_CURSOR_XY_LOC 0x0074 |
88 | #define EP93XXFB_CURSOR_DSCAN_HY_LOC 0x0078 |
89 | #define EP93XXFB_CURSOR_BLINK_RATE_CTRL 0x0224 |
90 | |
91 | /* LUT Registers */ |
92 | #define EP93XXFB_GRY_SCL_LUTR 0x0080 |
93 | #define EP93XXFB_GRY_SCL_LUTG 0x0280 |
94 | #define EP93XXFB_GRY_SCL_LUTB 0x0300 |
95 | #define EP93XXFB_LUT_SW_CONTROL 0x0218 |
96 | #define EP93XXFB_LUT_SW_CONTROL_SWTCH (1 << 0) |
97 | #define EP93XXFB_LUT_SW_CONTROL_SSTAT (1 << 1) |
98 | #define EP93XXFB_COLOR_LUT 0x0400 |
99 | |
100 | /* Video Signature Registers */ |
101 | #define EP93XXFB_VID_SIG_RSLT_VAL 0x0200 |
102 | #define EP93XXFB_VID_SIG_CTRL 0x0204 |
103 | #define EP93XXFB_VSIG 0x0208 |
104 | #define EP93XXFB_HSIG 0x020c |
105 | #define EP93XXFB_SIG_CLR_STR 0x0210 |
106 | |
107 | /* Minimum / Maximum resolutions supported */ |
108 | #define EP93XXFB_MIN_XRES 64 |
109 | #define EP93XXFB_MIN_YRES 64 |
110 | #define EP93XXFB_MAX_XRES 1024 |
111 | #define EP93XXFB_MAX_YRES 768 |
112 | |
113 | struct ep93xx_fbi { |
114 | struct ep93xxfb_mach_info *mach_info; |
115 | struct clk *clk; |
116 | struct resource *res; |
117 | void __iomem *mmio_base; |
118 | unsigned int pseudo_palette[256]; |
119 | }; |
120 | |
121 | static int check_screenpage_bug = 1; |
122 | module_param(check_screenpage_bug, int, 0644); |
123 | MODULE_PARM_DESC(check_screenpage_bug, |
124 | "Check for bit 27 screen page bug. Default = 1"); |
125 | |
126 | static inline unsigned int ep93xxfb_readl(struct ep93xx_fbi *fbi, |
127 | unsigned int off) |
128 | { |
129 | return __raw_readl(fbi->mmio_base + off); |
130 | } |
131 | |
132 | static inline void ep93xxfb_writel(struct ep93xx_fbi *fbi, |
133 | unsigned int val, unsigned int off) |
134 | { |
135 | __raw_writel(val, fbi->mmio_base + off); |
136 | } |
137 | |
138 | /* |
139 | * Write to one of the locked raster registers. |
140 | */ |
141 | static inline void ep93xxfb_out_locked(struct ep93xx_fbi *fbi, |
142 | unsigned int val, unsigned int reg) |
143 | { |
144 | /* |
145 | * We don't need a lock or delay here since the raster register |
146 | * block will remain unlocked until the next access. |
147 | */ |
148 | ep93xxfb_writel(fbi, 0xaa, EP93XXFB_SWLOCK); |
149 | ep93xxfb_writel(fbi, val, reg); |
150 | } |
151 | |
152 | static void ep93xxfb_set_video_attribs(struct fb_info *info) |
153 | { |
154 | struct ep93xx_fbi *fbi = info->par; |
155 | unsigned int attribs; |
156 | |
157 | attribs = EP93XXFB_ENABLE; |
158 | attribs |= fbi->mach_info->flags; |
159 | ep93xxfb_out_locked(fbi, attribs, EP93XXFB_ATTRIBS); |
160 | } |
161 | |
162 | static int ep93xxfb_set_pixelmode(struct fb_info *info) |
163 | { |
164 | struct ep93xx_fbi *fbi = info->par; |
165 | unsigned int val; |
166 | |
167 | info->var.transp.offset = 0; |
168 | info->var.transp.length = 0; |
169 | |
170 | switch (info->var.bits_per_pixel) { |
171 | case 8: |
172 | val = EP93XXFB_PIXELMODE_8BPP | EP93XXFB_PIXELMODE_COLOR_LUT | |
173 | EP93XXFB_PIXELMODE_SHIFT_1P_18B; |
174 | |
175 | info->var.red.offset = 0; |
176 | info->var.red.length = 8; |
177 | info->var.green.offset = 0; |
178 | info->var.green.length = 8; |
179 | info->var.blue.offset = 0; |
180 | info->var.blue.length = 8; |
181 | info->fix.visual = FB_VISUAL_PSEUDOCOLOR; |
182 | break; |
183 | |
184 | case 16: |
185 | val = EP93XXFB_PIXELMODE_16BPP | EP93XXFB_PIXELMODE_COLOR_555 | |
186 | EP93XXFB_PIXELMODE_SHIFT_1P_18B; |
187 | |
188 | info->var.red.offset = 11; |
189 | info->var.red.length = 5; |
190 | info->var.green.offset = 5; |
191 | info->var.green.length = 6; |
192 | info->var.blue.offset = 0; |
193 | info->var.blue.length = 5; |
194 | info->fix.visual = FB_VISUAL_TRUECOLOR; |
195 | break; |
196 | |
197 | case 24: |
198 | val = EP93XXFB_PIXELMODE_24BPP | EP93XXFB_PIXELMODE_COLOR_888 | |
199 | EP93XXFB_PIXELMODE_SHIFT_1P_24B; |
200 | |
201 | info->var.red.offset = 16; |
202 | info->var.red.length = 8; |
203 | info->var.green.offset = 8; |
204 | info->var.green.length = 8; |
205 | info->var.blue.offset = 0; |
206 | info->var.blue.length = 8; |
207 | info->fix.visual = FB_VISUAL_TRUECOLOR; |
208 | break; |
209 | |
210 | case 32: |
211 | val = EP93XXFB_PIXELMODE_32BPP | EP93XXFB_PIXELMODE_COLOR_888 | |
212 | EP93XXFB_PIXELMODE_SHIFT_1P_24B; |
213 | |
214 | info->var.red.offset = 16; |
215 | info->var.red.length = 8; |
216 | info->var.green.offset = 8; |
217 | info->var.green.length = 8; |
218 | info->var.blue.offset = 0; |
219 | info->var.blue.length = 8; |
220 | info->fix.visual = FB_VISUAL_TRUECOLOR; |
221 | break; |
222 | |
223 | default: |
224 | return -EINVAL; |
225 | } |
226 | |
227 | ep93xxfb_writel(fbi, val, EP93XXFB_PIXELMODE); |
228 | return 0; |
229 | } |
230 | |
231 | static void ep93xxfb_set_timing(struct fb_info *info) |
232 | { |
233 | struct ep93xx_fbi *fbi = info->par; |
234 | unsigned int vlines_total, hclks_total, start, stop; |
235 | |
236 | vlines_total = info->var.yres + info->var.upper_margin + |
237 | info->var.lower_margin + info->var.vsync_len - 1; |
238 | |
239 | hclks_total = info->var.xres + info->var.left_margin + |
240 | info->var.right_margin + info->var.hsync_len - 1; |
241 | |
242 | ep93xxfb_out_locked(fbi, vlines_total, EP93XXFB_VLINES_TOTAL); |
243 | ep93xxfb_out_locked(fbi, hclks_total, EP93XXFB_HCLKS_TOTAL); |
244 | |
245 | start = vlines_total; |
246 | stop = vlines_total - info->var.vsync_len; |
247 | ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VSYNC); |
248 | |
249 | start = vlines_total - info->var.vsync_len - info->var.upper_margin; |
250 | stop = info->var.lower_margin - 1; |
251 | ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VBLANK); |
252 | ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VACTIVE); |
253 | |
254 | start = vlines_total; |
255 | stop = vlines_total + 1; |
256 | ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VCLK); |
257 | |
258 | start = hclks_total; |
259 | stop = hclks_total - info->var.hsync_len; |
260 | ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HSYNC); |
261 | |
262 | start = hclks_total - info->var.hsync_len - info->var.left_margin; |
263 | stop = info->var.right_margin - 1; |
264 | ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HBLANK); |
265 | ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HACTIVE); |
266 | |
267 | start = hclks_total; |
268 | stop = hclks_total; |
269 | ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HCLK); |
270 | |
271 | ep93xxfb_out_locked(fbi, 0x0, EP93XXFB_LINE_CARRY); |
272 | } |
273 | |
274 | static int ep93xxfb_set_par(struct fb_info *info) |
275 | { |
276 | struct ep93xx_fbi *fbi = info->par; |
277 | |
278 | clk_set_rate(fbi->clk, 1000 * PICOS2KHZ(info->var.pixclock)); |
279 | |
280 | ep93xxfb_set_timing(info); |
281 | |
282 | info->fix.line_length = info->var.xres_virtual * |
283 | info->var.bits_per_pixel / 8; |
284 | |
285 | ep93xxfb_writel(fbi, info->fix.smem_start, EP93XXFB_SCREEN_PAGE); |
286 | ep93xxfb_writel(fbi, info->var.yres - 1, EP93XXFB_SCREEN_LINES); |
287 | ep93xxfb_writel(fbi, ((info->var.xres * info->var.bits_per_pixel) |
288 | / 32) - 1, EP93XXFB_LINE_LENGTH); |
289 | ep93xxfb_writel(fbi, info->fix.line_length / 4, EP93XXFB_VLINE_STEP); |
290 | ep93xxfb_set_video_attribs(info); |
291 | return 0; |
292 | } |
293 | |
294 | static int ep93xxfb_check_var(struct fb_var_screeninfo *var, |
295 | struct fb_info *info) |
296 | { |
297 | int err; |
298 | |
299 | err = ep93xxfb_set_pixelmode(info); |
300 | if (err) |
301 | return err; |
302 | |
303 | var->xres = max_t(unsigned int, var->xres, EP93XXFB_MIN_XRES); |
304 | var->xres = min_t(unsigned int, var->xres, EP93XXFB_MAX_XRES); |
305 | var->xres_virtual = max(var->xres_virtual, var->xres); |
306 | |
307 | var->yres = max_t(unsigned int, var->yres, EP93XXFB_MIN_YRES); |
308 | var->yres = min_t(unsigned int, var->yres, EP93XXFB_MAX_YRES); |
309 | var->yres_virtual = max(var->yres_virtual, var->yres); |
310 | |
311 | return 0; |
312 | } |
313 | |
314 | static int ep93xxfb_mmap(struct fb_info *info, struct vm_area_struct *vma) |
315 | { |
316 | unsigned int offset = vma->vm_pgoff << PAGE_SHIFT; |
317 | |
318 | if (offset < info->fix.smem_len) { |
319 | return dma_mmap_writecombine(info->dev, vma, info->screen_base, |
320 | info->fix.smem_start, |
321 | info->fix.smem_len); |
322 | } |
323 | |
324 | return -EINVAL; |
325 | } |
326 | |
327 | static int ep93xxfb_blank(int blank_mode, struct fb_info *info) |
328 | { |
329 | struct ep93xx_fbi *fbi = info->par; |
330 | unsigned int attribs = ep93xxfb_readl(fbi, EP93XXFB_ATTRIBS); |
331 | |
332 | if (blank_mode) { |
333 | if (fbi->mach_info->blank) |
334 | fbi->mach_info->blank(blank_mode, info); |
335 | ep93xxfb_out_locked(fbi, attribs & ~EP93XXFB_ENABLE, |
336 | EP93XXFB_ATTRIBS); |
337 | clk_disable(fbi->clk); |
338 | } else { |
339 | clk_enable(fbi->clk); |
340 | ep93xxfb_out_locked(fbi, attribs | EP93XXFB_ENABLE, |
341 | EP93XXFB_ATTRIBS); |
342 | if (fbi->mach_info->blank) |
343 | fbi->mach_info->blank(blank_mode, info); |
344 | } |
345 | |
346 | return 0; |
347 | } |
348 | |
349 | static inline int ep93xxfb_convert_color(int val, int width) |
350 | { |
351 | return ((val << width) + 0x7fff - val) >> 16; |
352 | } |
353 | |
354 | static int ep93xxfb_setcolreg(unsigned int regno, unsigned int red, |
355 | unsigned int green, unsigned int blue, |
356 | unsigned int transp, struct fb_info *info) |
357 | { |
358 | struct ep93xx_fbi *fbi = info->par; |
359 | unsigned int *pal = info->pseudo_palette; |
360 | unsigned int ctrl, i, rgb, lut_current, lut_stat; |
361 | |
362 | switch (info->fix.visual) { |
363 | case FB_VISUAL_PSEUDOCOLOR: |
364 | if (regno > 255) |
365 | return 1; |
366 | rgb = ((red & 0xff00) << 8) | (green & 0xff00) | |
367 | ((blue & 0xff00) >> 8); |
368 | |
369 | pal[regno] = rgb; |
370 | ep93xxfb_writel(fbi, rgb, (EP93XXFB_COLOR_LUT + (regno << 2))); |
371 | ctrl = ep93xxfb_readl(fbi, EP93XXFB_LUT_SW_CONTROL); |
372 | lut_stat = !!(ctrl & EP93XXFB_LUT_SW_CONTROL_SSTAT); |
373 | lut_current = !!(ctrl & EP93XXFB_LUT_SW_CONTROL_SWTCH); |
374 | |
375 | if (lut_stat == lut_current) { |
376 | for (i = 0; i < 256; i++) { |
377 | ep93xxfb_writel(fbi, pal[i], |
378 | EP93XXFB_COLOR_LUT + (i << 2)); |
379 | } |
380 | |
381 | ep93xxfb_writel(fbi, |
382 | ctrl ^ EP93XXFB_LUT_SW_CONTROL_SWTCH, |
383 | EP93XXFB_LUT_SW_CONTROL); |
384 | } |
385 | break; |
386 | |
387 | case FB_VISUAL_TRUECOLOR: |
388 | if (regno > 16) |
389 | return 1; |
390 | |
391 | red = ep93xxfb_convert_color(red, info->var.red.length); |
392 | green = ep93xxfb_convert_color(green, info->var.green.length); |
393 | blue = ep93xxfb_convert_color(blue, info->var.blue.length); |
394 | transp = ep93xxfb_convert_color(transp, |
395 | info->var.transp.length); |
396 | |
397 | pal[regno] = (red << info->var.red.offset) | |
398 | (green << info->var.green.offset) | |
399 | (blue << info->var.blue.offset) | |
400 | (transp << info->var.transp.offset); |
401 | break; |
402 | |
403 | default: |
404 | return 1; |
405 | } |
406 | |
407 | return 0; |
408 | } |
409 | |
410 | static struct fb_ops ep93xxfb_ops = { |
411 | .owner = THIS_MODULE, |
412 | .fb_check_var = ep93xxfb_check_var, |
413 | .fb_set_par = ep93xxfb_set_par, |
414 | .fb_blank = ep93xxfb_blank, |
415 | .fb_fillrect = cfb_fillrect, |
416 | .fb_copyarea = cfb_copyarea, |
417 | .fb_imageblit = cfb_imageblit, |
418 | .fb_setcolreg = ep93xxfb_setcolreg, |
419 | .fb_mmap = ep93xxfb_mmap, |
420 | }; |
421 | |
422 | static int __init ep93xxfb_calc_fbsize(struct ep93xxfb_mach_info *mach_info) |
423 | { |
424 | int i, fb_size = 0; |
425 | |
426 | if (mach_info->num_modes == EP93XXFB_USE_MODEDB) { |
427 | fb_size = EP93XXFB_MAX_XRES * EP93XXFB_MAX_YRES * |
428 | mach_info->bpp / 8; |
429 | } else { |
430 | for (i = 0; i < mach_info->num_modes; i++) { |
431 | const struct fb_videomode *mode; |
432 | int size; |
433 | |
434 | mode = &mach_info->modes[i]; |
435 | size = mode->xres * mode->yres * mach_info->bpp / 8; |
436 | if (size > fb_size) |
437 | fb_size = size; |
438 | } |
439 | } |
440 | |
441 | return fb_size; |
442 | } |
443 | |
444 | static int __init ep93xxfb_alloc_videomem(struct fb_info *info) |
445 | { |
446 | struct ep93xx_fbi *fbi = info->par; |
447 | char __iomem *virt_addr; |
448 | dma_addr_t phys_addr; |
449 | unsigned int fb_size; |
450 | |
451 | fb_size = ep93xxfb_calc_fbsize(fbi->mach_info); |
452 | virt_addr = dma_alloc_writecombine(info->dev, fb_size, |
453 | &phys_addr, GFP_KERNEL); |
454 | if (!virt_addr) |
455 | return -ENOMEM; |
456 | |
457 | /* |
458 | * There is a bug in the ep93xx framebuffer which causes problems |
459 | * if bit 27 of the physical address is set. |
460 | * See: http://marc.info/?l=linux-arm-kernel&m=110061245502000&w=2 |
461 | * There does not seem to be any official errata for this, but I |
462 | * have confirmed the problem exists on my hardware (ep9315) at |
463 | * least. |
464 | */ |
465 | if (check_screenpage_bug && phys_addr & (1 << 27)) { |
466 | dev_err(info->dev, "ep93xx framebuffer bug. phys addr (0x%x) " |
467 | "has bit 27 set: cannot init framebuffer\n", |
468 | phys_addr); |
469 | |
470 | dma_free_coherent(info->dev, fb_size, virt_addr, phys_addr); |
471 | return -ENOMEM; |
472 | } |
473 | |
474 | info->fix.smem_start = phys_addr; |
475 | info->fix.smem_len = fb_size; |
476 | info->screen_base = virt_addr; |
477 | |
478 | return 0; |
479 | } |
480 | |
481 | static void ep93xxfb_dealloc_videomem(struct fb_info *info) |
482 | { |
483 | if (info->screen_base) |
484 | dma_free_coherent(info->dev, info->fix.smem_len, |
485 | info->screen_base, info->fix.smem_start); |
486 | } |
487 | |
488 | static int ep93xxfb_probe(struct platform_device *pdev) |
489 | { |
490 | struct ep93xxfb_mach_info *mach_info = pdev->dev.platform_data; |
491 | struct fb_info *info; |
492 | struct ep93xx_fbi *fbi; |
493 | struct resource *res; |
494 | char *video_mode; |
495 | int err; |
496 | |
497 | if (!mach_info) |
498 | return -EINVAL; |
499 | |
500 | info = framebuffer_alloc(sizeof(struct ep93xx_fbi), &pdev->dev); |
501 | if (!info) |
502 | return -ENOMEM; |
503 | |
504 | info->dev = &pdev->dev; |
505 | platform_set_drvdata(pdev, info); |
506 | fbi = info->par; |
507 | fbi->mach_info = mach_info; |
508 | |
509 | err = fb_alloc_cmap(&info->cmap, 256, 0); |
510 | if (err) |
511 | goto failed_cmap; |
512 | |
513 | err = ep93xxfb_alloc_videomem(info); |
514 | if (err) |
515 | goto failed_videomem; |
516 | |
517 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
518 | if (!res) { |
519 | err = -ENXIO; |
520 | goto failed_resource; |
521 | } |
522 | |
523 | /* |
524 | * FIXME - We don't do a request_mem_region here because we are |
525 | * sharing the register space with the backlight driver (see |
526 | * drivers/video/backlight/ep93xx_bl.c) and doing so will cause |
527 | * the second loaded driver to return -EBUSY. |
528 | * |
529 | * NOTE: No locking is required; the backlight does not touch |
530 | * any of the framebuffer registers. |
531 | */ |
532 | fbi->res = res; |
533 | fbi->mmio_base = devm_ioremap(&pdev->dev, res->start, |
534 | resource_size(res)); |
535 | if (!fbi->mmio_base) { |
536 | err = -ENXIO; |
537 | goto failed_resource; |
538 | } |
539 | |
540 | strcpy(info->fix.id, pdev->name); |
541 | info->fbops = &ep93xxfb_ops; |
542 | info->fix.type = FB_TYPE_PACKED_PIXELS; |
543 | info->fix.accel = FB_ACCEL_NONE; |
544 | info->var.activate = FB_ACTIVATE_NOW; |
545 | info->var.vmode = FB_VMODE_NONINTERLACED; |
546 | info->flags = FBINFO_DEFAULT; |
547 | info->node = -1; |
548 | info->state = FBINFO_STATE_RUNNING; |
549 | info->pseudo_palette = &fbi->pseudo_palette; |
550 | |
551 | fb_get_options("ep93xx-fb", &video_mode); |
552 | err = fb_find_mode(&info->var, info, video_mode, |
553 | fbi->mach_info->modes, fbi->mach_info->num_modes, |
554 | fbi->mach_info->default_mode, fbi->mach_info->bpp); |
555 | if (err == 0) { |
556 | dev_err(info->dev, "No suitable video mode found\n"); |
557 | err = -EINVAL; |
558 | goto failed_resource; |
559 | } |
560 | |
561 | if (mach_info->setup) { |
562 | err = mach_info->setup(pdev); |
563 | if (err) |
564 | goto failed_resource; |
565 | } |
566 | |
567 | err = ep93xxfb_check_var(&info->var, info); |
568 | if (err) |
569 | goto failed_check; |
570 | |
571 | fbi->clk = devm_clk_get(&pdev->dev, NULL); |
572 | if (IS_ERR(fbi->clk)) { |
573 | err = PTR_ERR(fbi->clk); |
574 | fbi->clk = NULL; |
575 | goto failed_check; |
576 | } |
577 | |
578 | ep93xxfb_set_par(info); |
579 | clk_enable(fbi->clk); |
580 | |
581 | err = register_framebuffer(info); |
582 | if (err) |
583 | goto failed_check; |
584 | |
585 | dev_info(info->dev, "registered. Mode = %dx%d-%d\n", |
586 | info->var.xres, info->var.yres, info->var.bits_per_pixel); |
587 | return 0; |
588 | |
589 | failed_check: |
590 | if (fbi->mach_info->teardown) |
591 | fbi->mach_info->teardown(pdev); |
592 | failed_resource: |
593 | ep93xxfb_dealloc_videomem(info); |
594 | failed_videomem: |
595 | fb_dealloc_cmap(&info->cmap); |
596 | failed_cmap: |
597 | kfree(info); |
598 | platform_set_drvdata(pdev, NULL); |
599 | |
600 | return err; |
601 | } |
602 | |
603 | static int ep93xxfb_remove(struct platform_device *pdev) |
604 | { |
605 | struct fb_info *info = platform_get_drvdata(pdev); |
606 | struct ep93xx_fbi *fbi = info->par; |
607 | |
608 | unregister_framebuffer(info); |
609 | clk_disable(fbi->clk); |
610 | ep93xxfb_dealloc_videomem(info); |
611 | fb_dealloc_cmap(&info->cmap); |
612 | |
613 | if (fbi->mach_info->teardown) |
614 | fbi->mach_info->teardown(pdev); |
615 | |
616 | kfree(info); |
617 | platform_set_drvdata(pdev, NULL); |
618 | |
619 | return 0; |
620 | } |
621 | |
622 | static struct platform_driver ep93xxfb_driver = { |
623 | .probe = ep93xxfb_probe, |
624 | .remove = ep93xxfb_remove, |
625 | .driver = { |
626 | .name = "ep93xx-fb", |
627 | .owner = THIS_MODULE, |
628 | }, |
629 | }; |
630 | |
631 | static int ep93xxfb_init(void) |
632 | { |
633 | return platform_driver_register(&ep93xxfb_driver); |
634 | } |
635 | |
636 | static void __exit ep93xxfb_exit(void) |
637 | { |
638 | platform_driver_unregister(&ep93xxfb_driver); |
639 | } |
640 | |
641 | module_init(ep93xxfb_init); |
642 | module_exit(ep93xxfb_exit); |
643 | |
644 | MODULE_DESCRIPTION("EP93XX Framebuffer Driver"); |
645 | MODULE_ALIAS("platform:ep93xx-fb"); |
646 | MODULE_AUTHOR("Ryan Mallon, " |
647 | "H Hartley Sweeten <hsweeten@visionengravers.com"); |
648 | MODULE_LICENSE("GPL"); |
649 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9