diff options
author | Mike Rapoport <mike@compulab.co.il> | 2006-07-14 03:24:34 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-07-15 00:53:55 -0400 |
commit | 22caf04297896e515c6d5cdfb8e08a79a523946c (patch) | |
tree | dbb53f07800e51f1ed285ab530e200edb997aeb5 /drivers/video/mbx/mbxfb.c | |
parent | b04ea3cebf79d6808632808072f276dbc98aaf01 (diff) |
[PATCH] mbxfb: Add framebuffer driver for the Intel 2700G
Add frame buffer driver for the 2700G LCD controller present on CompuLab
CM-X270 computer module.
[adaplas]
- Add more informative help text to Kconfig
- Make DEBUG a Kconfig option as FB_MBX_DEBUG
- Remove #include mbxdebug.c, this is frowned upon
- Remove redundant casts
- Arrange #include's alphabetically
- Trivial whitespace
Signed-off-by: Mike Rapoport <mike@compulab.co.il>
Signed-off-by: Antonino Daplas <adaplas@pol.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/video/mbx/mbxfb.c')
-rw-r--r-- | drivers/video/mbx/mbxfb.c | 683 |
1 files changed, 683 insertions, 0 deletions
diff --git a/drivers/video/mbx/mbxfb.c b/drivers/video/mbx/mbxfb.c new file mode 100644 index 000000000000..6849ab75d403 --- /dev/null +++ b/drivers/video/mbx/mbxfb.c | |||
@@ -0,0 +1,683 @@ | |||
1 | /* | ||
2 | * linux/drivers/video/mbx/mbxfb.c | ||
3 | * | ||
4 | * Copyright (C) 2006 Compulab, Ltd. | ||
5 | * Mike Rapoport <mike@compulab.co.il> | ||
6 | * | ||
7 | * Based on pxafb.c | ||
8 | * | ||
9 | * This file is subject to the terms and conditions of the GNU General Public | ||
10 | * License. See the file COPYING in the main directory of this archive for | ||
11 | * more details. | ||
12 | * | ||
13 | * Intel 2700G (Marathon) Graphics Accelerator Frame Buffer Driver | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #include <linux/delay.h> | ||
18 | #include <linux/fb.h> | ||
19 | #include <linux/init.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | |||
23 | #include <asm/io.h> | ||
24 | |||
25 | #include <video/mbxfb.h> | ||
26 | |||
27 | #include "regs.h" | ||
28 | #include "reg_bits.h" | ||
29 | |||
30 | static unsigned long virt_base_2700; | ||
31 | |||
32 | #define MIN_XRES 16 | ||
33 | #define MIN_YRES 16 | ||
34 | #define MAX_XRES 2048 | ||
35 | #define MAX_YRES 2048 | ||
36 | |||
37 | #define MAX_PALETTES 16 | ||
38 | |||
39 | /* FIXME: take care of different chip revisions with different sizes | ||
40 | of ODFB */ | ||
41 | #define MEMORY_OFFSET 0x60000 | ||
42 | |||
43 | struct mbxfb_info { | ||
44 | struct device *dev; | ||
45 | |||
46 | struct resource *fb_res; | ||
47 | struct resource *fb_req; | ||
48 | |||
49 | struct resource *reg_res; | ||
50 | struct resource *reg_req; | ||
51 | |||
52 | void __iomem *fb_virt_addr; | ||
53 | unsigned long fb_phys_addr; | ||
54 | |||
55 | void __iomem *reg_virt_addr; | ||
56 | unsigned long reg_phys_addr; | ||
57 | |||
58 | int (*platform_probe) (struct fb_info * fb); | ||
59 | int (*platform_remove) (struct fb_info * fb); | ||
60 | |||
61 | u32 pseudo_palette[MAX_PALETTES]; | ||
62 | #ifdef CONFIG_FB_MBX_DEBUG | ||
63 | void *debugfs_data; | ||
64 | #endif | ||
65 | |||
66 | }; | ||
67 | |||
68 | static struct fb_var_screeninfo mbxfb_default __devinitdata = { | ||
69 | .xres = 640, | ||
70 | .yres = 480, | ||
71 | .xres_virtual = 640, | ||
72 | .yres_virtual = 480, | ||
73 | .bits_per_pixel = 16, | ||
74 | .red = {11, 5, 0}, | ||
75 | .green = {5, 6, 0}, | ||
76 | .blue = {0, 5, 0}, | ||
77 | .activate = FB_ACTIVATE_TEST, | ||
78 | .height = -1, | ||
79 | .width = -1, | ||
80 | .pixclock = 40000, | ||
81 | .left_margin = 48, | ||
82 | .right_margin = 16, | ||
83 | .upper_margin = 33, | ||
84 | .lower_margin = 10, | ||
85 | .hsync_len = 96, | ||
86 | .vsync_len = 2, | ||
87 | .vmode = FB_VMODE_NONINTERLACED, | ||
88 | .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | ||
89 | }; | ||
90 | |||
91 | static struct fb_fix_screeninfo mbxfb_fix __devinitdata = { | ||
92 | .id = "MBX", | ||
93 | .type = FB_TYPE_PACKED_PIXELS, | ||
94 | .visual = FB_VISUAL_TRUECOLOR, | ||
95 | .xpanstep = 0, | ||
96 | .ypanstep = 0, | ||
97 | .ywrapstep = 0, | ||
98 | .accel = FB_ACCEL_NONE, | ||
99 | }; | ||
100 | |||
101 | struct pixclock_div { | ||
102 | u8 m; | ||
103 | u8 n; | ||
104 | u8 p; | ||
105 | }; | ||
106 | |||
107 | static unsigned int mbxfb_get_pixclock(unsigned int pixclock_ps, | ||
108 | struct pixclock_div *div) | ||
109 | { | ||
110 | u8 m, n, p; | ||
111 | unsigned int err = 0; | ||
112 | unsigned int min_err = ~0x0; | ||
113 | unsigned int clk; | ||
114 | unsigned int best_clk = 0; | ||
115 | unsigned int ref_clk = 13000; /* FIXME: take from platform data */ | ||
116 | unsigned int pixclock; | ||
117 | |||
118 | /* convert pixclock to KHz */ | ||
119 | pixclock = PICOS2KHZ(pixclock_ps); | ||
120 | |||
121 | for (m = 1; m < 64; m++) { | ||
122 | for (n = 1; n < 8; n++) { | ||
123 | for (p = 0; p < 8; p++) { | ||
124 | clk = (ref_clk * m) / (n * (1 << p)); | ||
125 | err = (clk > pixclock) ? (clk - pixclock) : | ||
126 | (pixclock - clk); | ||
127 | if (err < min_err) { | ||
128 | min_err = err; | ||
129 | best_clk = clk; | ||
130 | div->m = m; | ||
131 | div->n = n; | ||
132 | div->p = p; | ||
133 | } | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | return KHZ2PICOS(best_clk); | ||
138 | } | ||
139 | |||
140 | static int mbxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, | ||
141 | u_int trans, struct fb_info *info) | ||
142 | { | ||
143 | u32 val, ret = 1; | ||
144 | |||
145 | if (regno < MAX_PALETTES) { | ||
146 | u32 *pal = info->pseudo_palette; | ||
147 | |||
148 | val = (red & 0xf800) | ((green & 0xfc00) >> 5) | | ||
149 | ((blue & 0xf800) >> 11); | ||
150 | pal[regno] = val; | ||
151 | ret = 0; | ||
152 | } | ||
153 | |||
154 | return ret; | ||
155 | } | ||
156 | |||
157 | static int mbxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | ||
158 | { | ||
159 | struct pixclock_div div; | ||
160 | |||
161 | var->pixclock = mbxfb_get_pixclock(var->pixclock, &div); | ||
162 | |||
163 | if (var->xres < MIN_XRES) | ||
164 | var->xres = MIN_XRES; | ||
165 | if (var->yres < MIN_YRES) | ||
166 | var->yres = MIN_YRES; | ||
167 | if (var->xres > MAX_XRES) | ||
168 | return -EINVAL; | ||
169 | if (var->yres > MAX_YRES) | ||
170 | return -EINVAL; | ||
171 | var->xres_virtual = max(var->xres_virtual, var->xres); | ||
172 | var->yres_virtual = max(var->yres_virtual, var->yres); | ||
173 | |||
174 | switch (var->bits_per_pixel) { | ||
175 | /* 8 bits-per-pixel is not supported yet */ | ||
176 | case 8: | ||
177 | return -EINVAL; | ||
178 | case 16: | ||
179 | var->green.length = (var->green.length == 5) ? 5 : 6; | ||
180 | var->red.length = 5; | ||
181 | var->blue.length = 5; | ||
182 | var->transp.length = 6 - var->green.length; | ||
183 | var->blue.offset = 0; | ||
184 | var->green.offset = 5; | ||
185 | var->red.offset = 5 + var->green.length; | ||
186 | var->transp.offset = (5 + var->red.offset) & 15; | ||
187 | break; | ||
188 | case 24: /* RGB 888 */ | ||
189 | case 32: /* RGBA 8888 */ | ||
190 | var->red.offset = 16; | ||
191 | var->red.length = 8; | ||
192 | var->green.offset = 8; | ||
193 | var->green.length = 8; | ||
194 | var->blue.offset = 0; | ||
195 | var->blue.length = 8; | ||
196 | var->transp.length = var->bits_per_pixel - 24; | ||
197 | var->transp.offset = (var->transp.length) ? 24 : 0; | ||
198 | break; | ||
199 | } | ||
200 | var->red.msb_right = 0; | ||
201 | var->green.msb_right = 0; | ||
202 | var->blue.msb_right = 0; | ||
203 | var->transp.msb_right = 0; | ||
204 | |||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | static int mbxfb_set_par(struct fb_info *info) | ||
209 | { | ||
210 | struct fb_var_screeninfo *var = &info->var; | ||
211 | struct pixclock_div div; | ||
212 | ushort hbps, ht, hfps, has; | ||
213 | ushort vbps, vt, vfps, vas; | ||
214 | u32 gsctrl = readl(GSCTRL); | ||
215 | u32 gsadr = readl(GSADR); | ||
216 | |||
217 | info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8; | ||
218 | |||
219 | /* setup color mode */ | ||
220 | gsctrl &= ~(FMsk(GSCTRL_GPIXFMT)); | ||
221 | /* FIXME: add *WORKING* support for 8-bits per color */ | ||
222 | if (info->var.bits_per_pixel == 8) { | ||
223 | return -EINVAL; | ||
224 | } else { | ||
225 | fb_dealloc_cmap(&info->cmap); | ||
226 | gsctrl &= ~GSCTRL_LUT_EN; | ||
227 | |||
228 | info->fix.visual = FB_VISUAL_TRUECOLOR; | ||
229 | switch (info->var.bits_per_pixel) { | ||
230 | case 16: | ||
231 | if (info->var.green.length == 5) | ||
232 | gsctrl |= GSCTRL_GPIXFMT_ARGB1555; | ||
233 | else | ||
234 | gsctrl |= GSCTRL_GPIXFMT_RGB565; | ||
235 | break; | ||
236 | case 24: | ||
237 | gsctrl |= GSCTRL_GPIXFMT_RGB888; | ||
238 | break; | ||
239 | case 32: | ||
240 | gsctrl |= GSCTRL_GPIXFMT_ARGB8888; | ||
241 | break; | ||
242 | } | ||
243 | } | ||
244 | |||
245 | /* setup resolution */ | ||
246 | gsctrl &= ~(FMsk(GSCTRL_GSWIDTH) | FMsk(GSCTRL_GSHEIGHT)); | ||
247 | gsctrl |= Gsctrl_Width(info->var.xres - 1) | | ||
248 | Gsctrl_Height(info->var.yres - 1); | ||
249 | writel(gsctrl, GSCTRL); | ||
250 | udelay(1000); | ||
251 | |||
252 | gsadr &= ~(FMsk(GSADR_SRCSTRIDE)); | ||
253 | gsadr |= Gsadr_Srcstride(info->var.xres * info->var.bits_per_pixel / | ||
254 | (8 * 16) - 1); | ||
255 | writel(gsadr, GSADR); | ||
256 | udelay(1000); | ||
257 | |||
258 | /* setup timings */ | ||
259 | var->pixclock = mbxfb_get_pixclock(info->var.pixclock, &div); | ||
260 | |||
261 | writel((Disp_Pll_M(div.m) | Disp_Pll_N(div.n) | | ||
262 | Disp_Pll_P(div.p) | DISP_PLL_EN), DISPPLL); | ||
263 | |||
264 | hbps = var->hsync_len; | ||
265 | has = hbps + var->left_margin; | ||
266 | hfps = has + var->xres; | ||
267 | ht = hfps + var->right_margin; | ||
268 | |||
269 | vbps = var->vsync_len; | ||
270 | vas = vbps + var->upper_margin; | ||
271 | vfps = vas + var->yres; | ||
272 | vt = vfps + var->lower_margin; | ||
273 | |||
274 | writel((Dht01_Hbps(hbps) | Dht01_Ht(ht)), DHT01); | ||
275 | writel((Dht02_Hlbs(has) | Dht02_Has(has)), DHT02); | ||
276 | writel((Dht03_Hfps(hfps) | Dht03_Hrbs(hfps)), DHT03); | ||
277 | writel((Dhdet_Hdes(has) | Dhdet_Hdef(hfps)), DHDET); | ||
278 | |||
279 | writel((Dvt01_Vbps(vbps) | Dvt01_Vt(vt)), DVT01); | ||
280 | writel((Dvt02_Vtbs(vas) | Dvt02_Vas(vas)), DVT02); | ||
281 | writel((Dvt03_Vfps(vfps) | Dvt03_Vbbs(vfps)), DVT03); | ||
282 | writel((Dvdet_Vdes(vas) | Dvdet_Vdef(vfps)), DVDET); | ||
283 | writel((Dvectrl_Vevent(vfps) | Dvectrl_Vfetch(vbps)), DVECTRL); | ||
284 | |||
285 | writel((readl(DSCTRL) | DSCTRL_SYNCGEN_EN), DSCTRL); | ||
286 | |||
287 | return 0; | ||
288 | } | ||
289 | |||
290 | static int mbxfb_blank(int blank, struct fb_info *info) | ||
291 | { | ||
292 | switch (blank) { | ||
293 | case FB_BLANK_POWERDOWN: | ||
294 | case FB_BLANK_VSYNC_SUSPEND: | ||
295 | case FB_BLANK_HSYNC_SUSPEND: | ||
296 | case FB_BLANK_NORMAL: | ||
297 | writel((readl(DSCTRL) & ~DSCTRL_SYNCGEN_EN), DSCTRL); | ||
298 | udelay(1000); | ||
299 | writel((readl(PIXCLK) & ~PIXCLK_EN), PIXCLK); | ||
300 | udelay(1000); | ||
301 | writel((readl(VOVRCLK) & ~VOVRCLK_EN), VOVRCLK); | ||
302 | udelay(1000); | ||
303 | break; | ||
304 | case FB_BLANK_UNBLANK: | ||
305 | writel((readl(DSCTRL) | DSCTRL_SYNCGEN_EN), DSCTRL); | ||
306 | udelay(1000); | ||
307 | writel((readl(PIXCLK) | PIXCLK_EN), PIXCLK); | ||
308 | udelay(1000); | ||
309 | break; | ||
310 | } | ||
311 | return 0; | ||
312 | } | ||
313 | |||
314 | static struct fb_ops mbxfb_ops = { | ||
315 | .owner = THIS_MODULE, | ||
316 | .fb_check_var = mbxfb_check_var, | ||
317 | .fb_set_par = mbxfb_set_par, | ||
318 | .fb_setcolreg = mbxfb_setcolreg, | ||
319 | .fb_fillrect = cfb_fillrect, | ||
320 | .fb_copyarea = cfb_copyarea, | ||
321 | .fb_imageblit = cfb_imageblit, | ||
322 | .fb_blank = mbxfb_blank, | ||
323 | }; | ||
324 | |||
325 | /* | ||
326 | Enable external SDRAM controller. Assume that all clocks are active | ||
327 | by now. | ||
328 | */ | ||
329 | static void __devinit setup_memc(struct fb_info *fbi) | ||
330 | { | ||
331 | struct mbxfb_info *mfbi = fbi->par; | ||
332 | unsigned long tmp; | ||
333 | int i; | ||
334 | |||
335 | /* FIXME: use platfrom specific parameters */ | ||
336 | /* setup SDRAM controller */ | ||
337 | writel((LMCFG_LMC_DS | LMCFG_LMC_TS | LMCFG_LMD_TS | | ||
338 | LMCFG_LMA_TS), | ||
339 | LMCFG); | ||
340 | udelay(1000); | ||
341 | |||
342 | writel(LMPWR_MC_PWR_ACT, LMPWR); | ||
343 | udelay(1000); | ||
344 | |||
345 | /* setup SDRAM timings */ | ||
346 | writel((Lmtim_Tras(7) | Lmtim_Trp(3) | Lmtim_Trcd(3) | | ||
347 | Lmtim_Trc(9) | Lmtim_Tdpl(2)), | ||
348 | LMTIM); | ||
349 | udelay(1000); | ||
350 | /* setup SDRAM refresh rate */ | ||
351 | writel(0xc2b, LMREFRESH); | ||
352 | udelay(1000); | ||
353 | /* setup SDRAM type parameters */ | ||
354 | writel((LMTYPE_CASLAT_3 | LMTYPE_BKSZ_2 | LMTYPE_ROWSZ_11 | | ||
355 | LMTYPE_COLSZ_8), | ||
356 | LMTYPE); | ||
357 | udelay(1000); | ||
358 | /* enable memory controller */ | ||
359 | writel(LMPWR_MC_PWR_ACT, LMPWR); | ||
360 | udelay(1000); | ||
361 | |||
362 | /* perform dummy reads */ | ||
363 | for ( i = 0; i < 16; i++ ) { | ||
364 | tmp = readl(fbi->screen_base); | ||
365 | } | ||
366 | } | ||
367 | |||
368 | static void enable_clocks(struct fb_info *fbi) | ||
369 | { | ||
370 | /* enable clocks */ | ||
371 | writel(SYSCLKSRC_PLL_2, SYSCLKSRC); | ||
372 | udelay(1000); | ||
373 | writel(PIXCLKSRC_PLL_1, PIXCLKSRC); | ||
374 | udelay(1000); | ||
375 | writel(0x00000000, CLKSLEEP); | ||
376 | udelay(1000); | ||
377 | writel((Core_Pll_M(0x17) | Core_Pll_N(0x3) | Core_Pll_P(0x0) | | ||
378 | CORE_PLL_EN), | ||
379 | COREPLL); | ||
380 | udelay(1000); | ||
381 | writel((Disp_Pll_M(0x1b) | Disp_Pll_N(0x7) | Disp_Pll_P(0x1) | | ||
382 | DISP_PLL_EN), | ||
383 | DISPPLL); | ||
384 | |||
385 | writel(0x00000000, VOVRCLK); | ||
386 | udelay(1000); | ||
387 | writel(PIXCLK_EN, PIXCLK); | ||
388 | udelay(1000); | ||
389 | writel(MEMCLK_EN, MEMCLK); | ||
390 | udelay(1000); | ||
391 | writel(0x00000006, M24CLK); | ||
392 | udelay(1000); | ||
393 | writel(0x00000006, MBXCLK); | ||
394 | udelay(1000); | ||
395 | writel(SDCLK_EN, SDCLK); | ||
396 | udelay(1000); | ||
397 | writel(0x00000001, PIXCLKDIV); | ||
398 | udelay(1000); | ||
399 | } | ||
400 | |||
401 | static void __devinit setup_graphics(struct fb_info *fbi) | ||
402 | { | ||
403 | unsigned long gsctrl; | ||
404 | |||
405 | gsctrl = GSCTRL_GAMMA_EN | Gsctrl_Width(fbi->var.xres - 1) | | ||
406 | Gsctrl_Height(fbi->var.yres - 1); | ||
407 | switch (fbi->var.bits_per_pixel) { | ||
408 | case 16: | ||
409 | if (fbi->var.green.length == 5) | ||
410 | gsctrl |= GSCTRL_GPIXFMT_ARGB1555; | ||
411 | else | ||
412 | gsctrl |= GSCTRL_GPIXFMT_RGB565; | ||
413 | break; | ||
414 | case 24: | ||
415 | gsctrl |= GSCTRL_GPIXFMT_RGB888; | ||
416 | break; | ||
417 | case 32: | ||
418 | gsctrl |= GSCTRL_GPIXFMT_ARGB8888; | ||
419 | break; | ||
420 | } | ||
421 | |||
422 | writel(gsctrl, GSCTRL); | ||
423 | udelay(1000); | ||
424 | writel(0x00000000, GBBASE); | ||
425 | udelay(1000); | ||
426 | writel(0x00ffffff, GDRCTRL); | ||
427 | udelay(1000); | ||
428 | writel((GSCADR_STR_EN | Gscadr_Gbase_Adr(0x6000)), GSCADR); | ||
429 | udelay(1000); | ||
430 | writel(0x00000000, GPLUT); | ||
431 | udelay(1000); | ||
432 | } | ||
433 | |||
434 | static void __devinit setup_display(struct fb_info *fbi) | ||
435 | { | ||
436 | unsigned long dsctrl = 0; | ||
437 | |||
438 | dsctrl = DSCTRL_BLNK_POL; | ||
439 | if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT) | ||
440 | dsctrl |= DSCTRL_HS_POL; | ||
441 | if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT) | ||
442 | dsctrl |= DSCTRL_VS_POL; | ||
443 | writel(dsctrl, DSCTRL); | ||
444 | udelay(1000); | ||
445 | writel(0xd0303010, DMCTRL); | ||
446 | udelay(1000); | ||
447 | writel((readl(DSCTRL) | DSCTRL_SYNCGEN_EN), DSCTRL); | ||
448 | } | ||
449 | |||
450 | static void __devinit enable_controller(struct fb_info *fbi) | ||
451 | { | ||
452 | writel(SYSRST_RST, SYSRST); | ||
453 | udelay(1000); | ||
454 | |||
455 | |||
456 | enable_clocks(fbi); | ||
457 | setup_memc(fbi); | ||
458 | setup_graphics(fbi); | ||
459 | setup_display(fbi); | ||
460 | } | ||
461 | |||
462 | #ifdef CONFIG_PM | ||
463 | /* | ||
464 | * Power management hooks. Note that we won't be called from IRQ context, | ||
465 | * unlike the blank functions above, so we may sleep. | ||
466 | */ | ||
467 | static int mbxfb_suspend(struct platform_device *dev, pm_message_t state) | ||
468 | { | ||
469 | /* make frame buffer memory enter self-refresh mode */ | ||
470 | writel(LMPWR_MC_PWR_SRM, LMPWR); | ||
471 | while (LMPWRSTAT != LMPWRSTAT_MC_PWR_SRM) | ||
472 | ; /* empty statement */ | ||
473 | |||
474 | /* reset the device, since it's initial state is 'mostly sleeping' */ | ||
475 | writel(SYSRST_RST, SYSRST); | ||
476 | return 0; | ||
477 | } | ||
478 | |||
479 | static int mbxfb_resume(struct platform_device *dev) | ||
480 | { | ||
481 | struct fb_info *fbi = platform_get_drvdata(dev); | ||
482 | |||
483 | enable_clocks(fbi); | ||
484 | /* setup_graphics(fbi); */ | ||
485 | /* setup_display(fbi); */ | ||
486 | |||
487 | writel((readl(DSCTRL) | DSCTRL_SYNCGEN_EN), DSCTRL); | ||
488 | return 0; | ||
489 | } | ||
490 | #else | ||
491 | #define mbxfb_suspend NULL | ||
492 | #define mbxfb_resume NULL | ||
493 | #endif | ||
494 | |||
495 | /* debugfs entries */ | ||
496 | #ifndef CONFIG_FB_MBX_DEBUG | ||
497 | #define mbxfb_debugfs_init(x) do {} while(0) | ||
498 | #define mbxfb_debugfs_remove(x) do {} while(0) | ||
499 | #endif | ||
500 | |||
501 | #define res_size(_r) (((_r)->end - (_r)->start) + 1) | ||
502 | |||
503 | static int __devinit mbxfb_probe(struct platform_device *dev) | ||
504 | { | ||
505 | int ret; | ||
506 | struct fb_info *fbi; | ||
507 | struct mbxfb_info *mfbi; | ||
508 | struct mbxfb_platform_data *pdata; | ||
509 | |||
510 | dev_dbg(dev, "mbxfb_probe\n"); | ||
511 | |||
512 | fbi = framebuffer_alloc(sizeof(struct mbxfb_info), &dev->dev); | ||
513 | if (fbi == NULL) { | ||
514 | dev_err(&dev->dev, "framebuffer_alloc failed\n"); | ||
515 | return -ENOMEM; | ||
516 | } | ||
517 | |||
518 | mfbi = fbi->par; | ||
519 | fbi->pseudo_palette = mfbi->pseudo_palette; | ||
520 | pdata = dev->dev.platform_data; | ||
521 | if (pdata->probe) | ||
522 | mfbi->platform_probe = pdata->probe; | ||
523 | if (pdata->remove) | ||
524 | mfbi->platform_remove = pdata->remove; | ||
525 | |||
526 | mfbi->fb_res = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
527 | mfbi->reg_res = platform_get_resource(dev, IORESOURCE_MEM, 1); | ||
528 | |||
529 | if (!mfbi->fb_res || !mfbi->reg_res) { | ||
530 | dev_err(&dev->dev, "no resources found\n"); | ||
531 | ret = -ENODEV; | ||
532 | goto err1; | ||
533 | } | ||
534 | |||
535 | mfbi->fb_req = request_mem_region(mfbi->fb_res->start, | ||
536 | res_size(mfbi->fb_res), dev->name); | ||
537 | if (mfbi->fb_req == NULL) { | ||
538 | dev_err(&dev->dev, "failed to claim framebuffer memory\n"); | ||
539 | ret = -EINVAL; | ||
540 | goto err1; | ||
541 | } | ||
542 | mfbi->fb_phys_addr = mfbi->fb_res->start; | ||
543 | |||
544 | mfbi->reg_req = request_mem_region(mfbi->reg_res->start, | ||
545 | res_size(mfbi->reg_res), dev->name); | ||
546 | if (mfbi->reg_req == NULL) { | ||
547 | dev_err(&dev->dev, "failed to claim Marathon registers\n"); | ||
548 | ret = -EINVAL; | ||
549 | goto err2; | ||
550 | } | ||
551 | mfbi->reg_phys_addr = mfbi->reg_res->start; | ||
552 | |||
553 | mfbi->reg_virt_addr = ioremap_nocache(mfbi->reg_phys_addr, | ||
554 | res_size(mfbi->reg_req)); | ||
555 | if (!mfbi->reg_virt_addr) { | ||
556 | dev_err(&dev->dev, "failed to ioremap Marathon registers\n"); | ||
557 | ret = -EINVAL; | ||
558 | goto err3; | ||
559 | } | ||
560 | virt_base_2700 = (unsigned long)mfbi->reg_virt_addr; | ||
561 | |||
562 | mfbi->fb_virt_addr = ioremap_nocache(mfbi->fb_phys_addr, | ||
563 | res_size(mfbi->fb_req)); | ||
564 | if (!mfbi->reg_virt_addr) { | ||
565 | dev_err(&dev->dev, "failed to ioremap frame buffer\n"); | ||
566 | ret = -EINVAL; | ||
567 | goto err4; | ||
568 | } | ||
569 | |||
570 | /* FIXME: get from platform */ | ||
571 | fbi->screen_base = (char __iomem *)(mfbi->fb_virt_addr + 0x60000); | ||
572 | fbi->screen_size = 8 * 1024 * 1024; /* 8 Megs */ | ||
573 | fbi->fbops = &mbxfb_ops; | ||
574 | |||
575 | fbi->var = mbxfb_default; | ||
576 | fbi->fix = mbxfb_fix; | ||
577 | fbi->fix.smem_start = mfbi->fb_phys_addr + 0x60000; | ||
578 | fbi->fix.smem_len = 8 * 1024 * 1024; | ||
579 | fbi->fix.line_length = 640 * 2; | ||
580 | |||
581 | ret = fb_alloc_cmap(&fbi->cmap, 256, 0); | ||
582 | if (ret < 0) { | ||
583 | dev_err(&dev->dev, "fb_alloc_cmap failed\n"); | ||
584 | ret = -EINVAL; | ||
585 | goto err5; | ||
586 | } | ||
587 | |||
588 | platform_set_drvdata(dev, fbi); | ||
589 | |||
590 | printk(KERN_INFO "fb%d: mbx frame buffer device\n", fbi->node); | ||
591 | |||
592 | if (mfbi->platform_probe) | ||
593 | mfbi->platform_probe(fbi); | ||
594 | |||
595 | enable_controller(fbi); | ||
596 | |||
597 | mbxfb_debugfs_init(fbi); | ||
598 | |||
599 | ret = register_framebuffer(fbi); | ||
600 | if (ret < 0) { | ||
601 | dev_err(&dev->dev, "register_framebuffer failed\n"); | ||
602 | ret = -EINVAL; | ||
603 | goto err6; | ||
604 | } | ||
605 | |||
606 | return 0; | ||
607 | |||
608 | err6: | ||
609 | fb_dealloc_cmap(&fbi->cmap); | ||
610 | err5: | ||
611 | iounmap(mfbi->fb_virt_addr); | ||
612 | err4: | ||
613 | iounmap(mfbi->reg_virt_addr); | ||
614 | err3: | ||
615 | release_mem_region(mfbi->reg_res->start, res_size(mfbi->reg_res)); | ||
616 | err2: | ||
617 | release_mem_region(mfbi->fb_res->start, res_size(mfbi->fb_res)); | ||
618 | err1: | ||
619 | framebuffer_release(fbi); | ||
620 | |||
621 | return ret; | ||
622 | } | ||
623 | |||
624 | static int __devexit mbxfb_remove(struct platform_device *dev) | ||
625 | { | ||
626 | struct fb_info *fbi = platform_get_drvdata(dev); | ||
627 | |||
628 | writel(SYSRST_RST, SYSRST); | ||
629 | udelay(1000); | ||
630 | |||
631 | mbxfb_debugfs_remove(fbi); | ||
632 | |||
633 | if (fbi) { | ||
634 | struct mbxfb_info *mfbi = fbi->par; | ||
635 | |||
636 | unregister_framebuffer(fbi); | ||
637 | if (mfbi) { | ||
638 | if (mfbi->platform_remove) | ||
639 | mfbi->platform_remove(fbi); | ||
640 | |||
641 | if (mfbi->fb_virt_addr) | ||
642 | iounmap(mfbi->fb_virt_addr); | ||
643 | if (mfbi->reg_virt_addr) | ||
644 | iounmap(mfbi->reg_virt_addr); | ||
645 | if (mfbi->reg_req) | ||
646 | release_mem_region(mfbi->reg_req->start, | ||
647 | res_size(mfbi->reg_req)); | ||
648 | if (mfbi->fb_req) | ||
649 | release_mem_region(mfbi->fb_req->start, | ||
650 | res_size(mfbi->fb_req)); | ||
651 | } | ||
652 | framebuffer_release(fbi); | ||
653 | } | ||
654 | |||
655 | return 0; | ||
656 | } | ||
657 | |||
658 | static struct platform_driver mbxfb_driver = { | ||
659 | .probe = mbxfb_probe, | ||
660 | .remove = mbxfb_remove, | ||
661 | .suspend = mbxfb_suspend, | ||
662 | .resume = mbxfb_resume, | ||
663 | .driver = { | ||
664 | .name = "mbx-fb", | ||
665 | }, | ||
666 | }; | ||
667 | |||
668 | int __devinit mbxfb_init(void) | ||
669 | { | ||
670 | return platform_driver_register(&mbxfb_driver); | ||
671 | } | ||
672 | |||
673 | static void __devexit mbxfb_exit(void) | ||
674 | { | ||
675 | platform_driver_unregister(&mbxfb_driver); | ||
676 | } | ||
677 | |||
678 | module_init(mbxfb_init); | ||
679 | module_exit(mbxfb_exit); | ||
680 | |||
681 | MODULE_DESCRIPTION("loadable framebuffer driver for Marathon device"); | ||
682 | MODULE_AUTHOR("Mike Rapoport, Compulab"); | ||
683 | MODULE_LICENSE("GPL"); | ||