Commit | Line | Data |
---|---|---|
22caf042 MR |
1 | /* |
2 | * linux/drivers/video/mbx/mbxfb.c | |
3 | * | |
ba282daa | 4 | * Copyright (C) 2006-2007 8D Technologies inc |
ea465250 RA |
5 | * Raphael Assenat <raph@8d.com> |
6 | * - Added video overlay support | |
7 | * - Various improvements | |
8 | * | |
22caf042 MR |
9 | * Copyright (C) 2006 Compulab, Ltd. |
10 | * Mike Rapoport <mike@compulab.co.il> | |
ea465250 | 11 | * - Creation of driver |
22caf042 MR |
12 | * |
13 | * Based on pxafb.c | |
14 | * | |
15 | * This file is subject to the terms and conditions of the GNU General Public | |
16 | * License. See the file COPYING in the main directory of this archive for | |
17 | * more details. | |
18 | * | |
19 | * Intel 2700G (Marathon) Graphics Accelerator Frame Buffer Driver | |
20 | * | |
21 | */ | |
22 | ||
23 | #include <linux/delay.h> | |
24 | #include <linux/fb.h> | |
25 | #include <linux/init.h> | |
26 | #include <linux/module.h> | |
27 | #include <linux/platform_device.h> | |
ea465250 | 28 | #include <linux/uaccess.h> |
22caf042 MR |
29 | |
30 | #include <asm/io.h> | |
31 | ||
32 | #include <video/mbxfb.h> | |
33 | ||
34 | #include "regs.h" | |
35 | #include "reg_bits.h" | |
36 | ||
37 | static unsigned long virt_base_2700; | |
38 | ||
ea465250 RA |
39 | #define write_reg(val, reg) do { writel((val), (reg)); } while(0) |
40 | ||
41 | /* Without this delay, the graphics appears somehow scaled and | |
42 | * there is a lot of jitter in scanlines. This delay is probably | |
43 | * needed only after setting some specific register(s) somewhere, | |
44 | * not all over the place... */ | |
45 | #define write_reg_dly(val, reg) do { writel((val), reg); udelay(1000); } while(0) | |
46 | ||
22caf042 MR |
47 | #define MIN_XRES 16 |
48 | #define MIN_YRES 16 | |
49 | #define MAX_XRES 2048 | |
50 | #define MAX_YRES 2048 | |
51 | ||
52 | #define MAX_PALETTES 16 | |
53 | ||
54 | /* FIXME: take care of different chip revisions with different sizes | |
55 | of ODFB */ | |
56 | #define MEMORY_OFFSET 0x60000 | |
57 | ||
58 | struct mbxfb_info { | |
59 | struct device *dev; | |
60 | ||
61 | struct resource *fb_res; | |
62 | struct resource *fb_req; | |
63 | ||
64 | struct resource *reg_res; | |
65 | struct resource *reg_req; | |
66 | ||
67 | void __iomem *fb_virt_addr; | |
68 | unsigned long fb_phys_addr; | |
69 | ||
70 | void __iomem *reg_virt_addr; | |
71 | unsigned long reg_phys_addr; | |
72 | ||
73 | int (*platform_probe) (struct fb_info * fb); | |
74 | int (*platform_remove) (struct fb_info * fb); | |
75 | ||
76 | u32 pseudo_palette[MAX_PALETTES]; | |
77 | #ifdef CONFIG_FB_MBX_DEBUG | |
78 | void *debugfs_data; | |
79 | #endif | |
80 | ||
81 | }; | |
82 | ||
83 | static struct fb_var_screeninfo mbxfb_default __devinitdata = { | |
84 | .xres = 640, | |
85 | .yres = 480, | |
86 | .xres_virtual = 640, | |
87 | .yres_virtual = 480, | |
88 | .bits_per_pixel = 16, | |
89 | .red = {11, 5, 0}, | |
90 | .green = {5, 6, 0}, | |
91 | .blue = {0, 5, 0}, | |
92 | .activate = FB_ACTIVATE_TEST, | |
93 | .height = -1, | |
94 | .width = -1, | |
95 | .pixclock = 40000, | |
96 | .left_margin = 48, | |
97 | .right_margin = 16, | |
98 | .upper_margin = 33, | |
99 | .lower_margin = 10, | |
100 | .hsync_len = 96, | |
101 | .vsync_len = 2, | |
102 | .vmode = FB_VMODE_NONINTERLACED, | |
103 | .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, | |
104 | }; | |
105 | ||
106 | static struct fb_fix_screeninfo mbxfb_fix __devinitdata = { | |
107 | .id = "MBX", | |
108 | .type = FB_TYPE_PACKED_PIXELS, | |
109 | .visual = FB_VISUAL_TRUECOLOR, | |
110 | .xpanstep = 0, | |
111 | .ypanstep = 0, | |
112 | .ywrapstep = 0, | |
113 | .accel = FB_ACCEL_NONE, | |
114 | }; | |
115 | ||
116 | struct pixclock_div { | |
117 | u8 m; | |
118 | u8 n; | |
119 | u8 p; | |
120 | }; | |
121 | ||
122 | static unsigned int mbxfb_get_pixclock(unsigned int pixclock_ps, | |
123 | struct pixclock_div *div) | |
124 | { | |
125 | u8 m, n, p; | |
126 | unsigned int err = 0; | |
127 | unsigned int min_err = ~0x0; | |
128 | unsigned int clk; | |
129 | unsigned int best_clk = 0; | |
130 | unsigned int ref_clk = 13000; /* FIXME: take from platform data */ | |
131 | unsigned int pixclock; | |
132 | ||
133 | /* convert pixclock to KHz */ | |
134 | pixclock = PICOS2KHZ(pixclock_ps); | |
135 | ||
8bc21841 RA |
136 | /* PLL output freq = (ref_clk * M) / (N * 2^P) |
137 | * | |
138 | * M: 1 to 63 | |
139 | * N: 1 to 7 | |
140 | * P: 0 to 7 | |
141 | */ | |
142 | ||
143 | /* RAPH: When N==1, the resulting pixel clock appears to | |
144 | * get divided by 2. Preventing N=1 by starting the following | |
145 | * loop at 2 prevents this. Is this a bug with my chip | |
146 | * revision or something I dont understand? */ | |
22caf042 | 147 | for (m = 1; m < 64; m++) { |
8bc21841 | 148 | for (n = 2; n < 8; n++) { |
22caf042 MR |
149 | for (p = 0; p < 8; p++) { |
150 | clk = (ref_clk * m) / (n * (1 << p)); | |
151 | err = (clk > pixclock) ? (clk - pixclock) : | |
152 | (pixclock - clk); | |
153 | if (err < min_err) { | |
154 | min_err = err; | |
155 | best_clk = clk; | |
156 | div->m = m; | |
157 | div->n = n; | |
158 | div->p = p; | |
159 | } | |
160 | } | |
161 | } | |
162 | } | |
163 | return KHZ2PICOS(best_clk); | |
164 | } | |
165 | ||
166 | static int mbxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, | |
167 | u_int trans, struct fb_info *info) | |
168 | { | |
169 | u32 val, ret = 1; | |
170 | ||
171 | if (regno < MAX_PALETTES) { | |
172 | u32 *pal = info->pseudo_palette; | |
173 | ||
174 | val = (red & 0xf800) | ((green & 0xfc00) >> 5) | | |
175 | ((blue & 0xf800) >> 11); | |
176 | pal[regno] = val; | |
177 | ret = 0; | |
178 | } | |
179 | ||
180 | return ret; | |
181 | } | |
182 | ||
183 | static int mbxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | |
184 | { | |
185 | struct pixclock_div div; | |
186 | ||
187 | var->pixclock = mbxfb_get_pixclock(var->pixclock, &div); | |
188 | ||
189 | if (var->xres < MIN_XRES) | |
190 | var->xres = MIN_XRES; | |
191 | if (var->yres < MIN_YRES) | |
192 | var->yres = MIN_YRES; | |
193 | if (var->xres > MAX_XRES) | |
194 | return -EINVAL; | |
195 | if (var->yres > MAX_YRES) | |
196 | return -EINVAL; | |
197 | var->xres_virtual = max(var->xres_virtual, var->xres); | |
198 | var->yres_virtual = max(var->yres_virtual, var->yres); | |
199 | ||
200 | switch (var->bits_per_pixel) { | |
201 | /* 8 bits-per-pixel is not supported yet */ | |
202 | case 8: | |
203 | return -EINVAL; | |
204 | case 16: | |
205 | var->green.length = (var->green.length == 5) ? 5 : 6; | |
206 | var->red.length = 5; | |
207 | var->blue.length = 5; | |
208 | var->transp.length = 6 - var->green.length; | |
209 | var->blue.offset = 0; | |
210 | var->green.offset = 5; | |
211 | var->red.offset = 5 + var->green.length; | |
212 | var->transp.offset = (5 + var->red.offset) & 15; | |
213 | break; | |
214 | case 24: /* RGB 888 */ | |
215 | case 32: /* RGBA 8888 */ | |
216 | var->red.offset = 16; | |
217 | var->red.length = 8; | |
218 | var->green.offset = 8; | |
219 | var->green.length = 8; | |
220 | var->blue.offset = 0; | |
221 | var->blue.length = 8; | |
222 | var->transp.length = var->bits_per_pixel - 24; | |
223 | var->transp.offset = (var->transp.length) ? 24 : 0; | |
224 | break; | |
225 | } | |
226 | var->red.msb_right = 0; | |
227 | var->green.msb_right = 0; | |
228 | var->blue.msb_right = 0; | |
229 | var->transp.msb_right = 0; | |
230 | ||
231 | return 0; | |
232 | } | |
233 | ||
234 | static int mbxfb_set_par(struct fb_info *info) | |
235 | { | |
236 | struct fb_var_screeninfo *var = &info->var; | |
237 | struct pixclock_div div; | |
238 | ushort hbps, ht, hfps, has; | |
239 | ushort vbps, vt, vfps, vas; | |
240 | u32 gsctrl = readl(GSCTRL); | |
241 | u32 gsadr = readl(GSADR); | |
242 | ||
243 | info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8; | |
244 | ||
245 | /* setup color mode */ | |
246 | gsctrl &= ~(FMsk(GSCTRL_GPIXFMT)); | |
247 | /* FIXME: add *WORKING* support for 8-bits per color */ | |
248 | if (info->var.bits_per_pixel == 8) { | |
249 | return -EINVAL; | |
250 | } else { | |
251 | fb_dealloc_cmap(&info->cmap); | |
252 | gsctrl &= ~GSCTRL_LUT_EN; | |
253 | ||
254 | info->fix.visual = FB_VISUAL_TRUECOLOR; | |
255 | switch (info->var.bits_per_pixel) { | |
256 | case 16: | |
257 | if (info->var.green.length == 5) | |
258 | gsctrl |= GSCTRL_GPIXFMT_ARGB1555; | |
259 | else | |
260 | gsctrl |= GSCTRL_GPIXFMT_RGB565; | |
261 | break; | |
262 | case 24: | |
263 | gsctrl |= GSCTRL_GPIXFMT_RGB888; | |
264 | break; | |
265 | case 32: | |
266 | gsctrl |= GSCTRL_GPIXFMT_ARGB8888; | |
267 | break; | |
268 | } | |
269 | } | |
270 | ||
271 | /* setup resolution */ | |
272 | gsctrl &= ~(FMsk(GSCTRL_GSWIDTH) | FMsk(GSCTRL_GSHEIGHT)); | |
5c60b118 RA |
273 | gsctrl |= Gsctrl_Width(info->var.xres) | |
274 | Gsctrl_Height(info->var.yres); | |
ea465250 | 275 | write_reg_dly(gsctrl, GSCTRL); |
22caf042 MR |
276 | |
277 | gsadr &= ~(FMsk(GSADR_SRCSTRIDE)); | |
278 | gsadr |= Gsadr_Srcstride(info->var.xres * info->var.bits_per_pixel / | |
279 | (8 * 16) - 1); | |
ea465250 | 280 | write_reg_dly(gsadr, GSADR); |
22caf042 MR |
281 | |
282 | /* setup timings */ | |
283 | var->pixclock = mbxfb_get_pixclock(info->var.pixclock, &div); | |
284 | ||
ea465250 | 285 | write_reg_dly((Disp_Pll_M(div.m) | Disp_Pll_N(div.n) | |
22caf042 MR |
286 | Disp_Pll_P(div.p) | DISP_PLL_EN), DISPPLL); |
287 | ||
288 | hbps = var->hsync_len; | |
289 | has = hbps + var->left_margin; | |
290 | hfps = has + var->xres; | |
291 | ht = hfps + var->right_margin; | |
292 | ||
293 | vbps = var->vsync_len; | |
294 | vas = vbps + var->upper_margin; | |
295 | vfps = vas + var->yres; | |
296 | vt = vfps + var->lower_margin; | |
297 | ||
ea465250 RA |
298 | write_reg_dly((Dht01_Hbps(hbps) | Dht01_Ht(ht)), DHT01); |
299 | write_reg_dly((Dht02_Hlbs(has) | Dht02_Has(has)), DHT02); | |
300 | write_reg_dly((Dht03_Hfps(hfps) | Dht03_Hrbs(hfps)), DHT03); | |
301 | write_reg_dly((Dhdet_Hdes(has) | Dhdet_Hdef(hfps)), DHDET); | |
22caf042 | 302 | |
ea465250 RA |
303 | write_reg_dly((Dvt01_Vbps(vbps) | Dvt01_Vt(vt)), DVT01); |
304 | write_reg_dly((Dvt02_Vtbs(vas) | Dvt02_Vas(vas)), DVT02); | |
305 | write_reg_dly((Dvt03_Vfps(vfps) | Dvt03_Vbbs(vfps)), DVT03); | |
306 | write_reg_dly((Dvdet_Vdes(vas) | Dvdet_Vdef(vfps)), DVDET); | |
307 | write_reg_dly((Dvectrl_Vevent(vfps) | Dvectrl_Vfetch(vbps)), DVECTRL); | |
22caf042 | 308 | |
ea465250 RA |
309 | write_reg_dly((readl(DSCTRL) | DSCTRL_SYNCGEN_EN), DSCTRL); |
310 | ||
311 | write_reg_dly(DINTRE_VEVENT0_EN, DINTRE); | |
22caf042 MR |
312 | |
313 | return 0; | |
314 | } | |
315 | ||
316 | static int mbxfb_blank(int blank, struct fb_info *info) | |
317 | { | |
318 | switch (blank) { | |
319 | case FB_BLANK_POWERDOWN: | |
320 | case FB_BLANK_VSYNC_SUSPEND: | |
321 | case FB_BLANK_HSYNC_SUSPEND: | |
322 | case FB_BLANK_NORMAL: | |
ea465250 RA |
323 | write_reg_dly((readl(DSCTRL) & ~DSCTRL_SYNCGEN_EN), DSCTRL); |
324 | write_reg_dly((readl(PIXCLK) & ~PIXCLK_EN), PIXCLK); | |
325 | write_reg_dly((readl(VOVRCLK) & ~VOVRCLK_EN), VOVRCLK); | |
22caf042 MR |
326 | break; |
327 | case FB_BLANK_UNBLANK: | |
ea465250 RA |
328 | write_reg_dly((readl(DSCTRL) | DSCTRL_SYNCGEN_EN), DSCTRL); |
329 | write_reg_dly((readl(PIXCLK) | PIXCLK_EN), PIXCLK); | |
22caf042 MR |
330 | break; |
331 | } | |
332 | return 0; | |
333 | } | |
334 | ||
ea465250 RA |
335 | static int mbxfb_setupOverlay(struct mbxfb_overlaySetup *set) |
336 | { | |
ba282daa RA |
337 | u32 vsctrl, vscadr, vsadr; |
338 | u32 sssize, spoctrl, shctrl; | |
ea465250 RA |
339 | u32 vubase, vvbase; |
340 | u32 vovrclk; | |
341 | ||
342 | if (set->scaled_width==0 || set->scaled_height==0) | |
343 | return -EINVAL; | |
344 | ||
345 | /* read registers which have reserved bits | |
346 | * so we can write them back as-is. */ | |
347 | vovrclk = readl(VOVRCLK); | |
348 | vsctrl = readl(VSCTRL); | |
349 | vscadr = readl(VSCADR); | |
350 | vubase = readl(VUBASE); | |
351 | vvbase = readl(VVBASE); | |
ba282daa | 352 | shctrl = readl(SHCTRL); |
ea465250 RA |
353 | |
354 | spoctrl = readl(SPOCTRL); | |
355 | sssize = readl(SSSIZE); | |
356 | ||
ea465250 RA |
357 | vsctrl &= ~( FMsk(VSCTRL_VSWIDTH) | |
358 | FMsk(VSCTRL_VSHEIGHT) | | |
359 | FMsk(VSCTRL_VPIXFMT) | | |
360 | VSCTRL_GAMMA_EN | VSCTRL_CSC_EN | | |
361 | VSCTRL_COSITED ); | |
362 | vsctrl |= Vsctrl_Width(set->width) | Vsctrl_Height(set->height) | | |
363 | VSCTRL_CSC_EN; | |
364 | ||
ba282daa | 365 | vscadr &= ~(VSCADR_STR_EN | FMsk(VSCADR_VBASE_ADR) ); |
ea465250 RA |
366 | vubase &= ~(VUBASE_UVHALFSTR | FMsk(VUBASE_UBASE_ADR)); |
367 | vvbase &= ~(FMsk(VVBASE_VBASE_ADR)); | |
368 | ||
ba282daa RA |
369 | switch (set->fmt) { |
370 | case MBXFB_FMT_YUV16: | |
371 | vsctrl |= VSCTRL_VPIXFMT_YUV12; | |
ea465250 | 372 | |
ba282daa RA |
373 | set->Y_stride = ((set->width) + 0xf ) & ~0xf; |
374 | break; | |
375 | case MBXFB_FMT_YUV12: | |
376 | vsctrl |= VSCTRL_VPIXFMT_YUV12; | |
ea465250 | 377 | |
ba282daa RA |
378 | set->Y_stride = ((set->width) + 0xf ) & ~0xf; |
379 | vubase |= VUBASE_UVHALFSTR; | |
380 | ||
381 | break; | |
382 | case MBXFB_FMT_UY0VY1: | |
383 | vsctrl |= VSCTRL_VPIXFMT_UY0VY1; | |
384 | set->Y_stride = (set->width*2 + 0xf ) & ~0xf; | |
385 | break; | |
386 | case MBXFB_FMT_VY0UY1: | |
387 | vsctrl |= VSCTRL_VPIXFMT_VY0UY1; | |
388 | set->Y_stride = (set->width*2 + 0xf ) & ~0xf; | |
389 | break; | |
390 | case MBXFB_FMT_Y0UY1V: | |
391 | vsctrl |= VSCTRL_VPIXFMT_Y0UY1V; | |
392 | set->Y_stride = (set->width*2 + 0xf ) & ~0xf; | |
393 | break; | |
394 | case MBXFB_FMT_Y0VY1U: | |
395 | vsctrl |= VSCTRL_VPIXFMT_Y0VY1U; | |
396 | set->Y_stride = (set->width*2 + 0xf ) & ~0xf; | |
ea465250 | 397 | break; |
ba282daa RA |
398 | default: |
399 | return -EINVAL; | |
ea465250 RA |
400 | } |
401 | ||
402 | /* VSCTRL has the bits which sets the Video Pixel Format. | |
403 | * When passing from a packed to planar format, | |
404 | * if we write VSCTRL first, VVBASE and VUBASE would | |
405 | * be zero if we would not set them here. (And then, | |
406 | * the chips hangs and only a reset seems to fix it). | |
407 | * | |
408 | * If course, the values calculated here have no meaning | |
409 | * for packed formats. | |
410 | */ | |
411 | set->UV_stride = ((set->width/2) + 0x7 ) & ~0x7; | |
412 | set->U_offset = set->height * set->Y_stride; | |
413 | set->V_offset = set->U_offset + | |
414 | set->height * set->UV_stride; | |
415 | vubase |= Vubase_Ubase_Adr( | |
416 | (0x60000 + set->mem_offset + set->U_offset)>>3); | |
417 | vvbase |= Vvbase_Vbase_Adr( | |
418 | (0x60000 + set->mem_offset + set->V_offset)>>3); | |
419 | ||
420 | ||
ba282daa | 421 | vscadr |= Vscadr_Vbase_Adr((0x60000 + set->mem_offset)>>4); |
ea465250 RA |
422 | |
423 | if (set->enable) | |
424 | vscadr |= VSCADR_STR_EN; | |
425 | ||
426 | ||
427 | vsadr = Vsadr_Srcstride((set->Y_stride)/16-1) | | |
428 | Vsadr_Xstart(set->x) | Vsadr_Ystart(set->y); | |
429 | ||
430 | sssize &= ~(FMsk(SSSIZE_SC_WIDTH) | FMsk(SSSIZE_SC_HEIGHT)); | |
431 | sssize = Sssize_Sc_Width(set->scaled_width-1) | | |
432 | Sssize_Sc_Height(set->scaled_height-1); | |
433 | ||
434 | spoctrl &= ~(SPOCTRL_H_SC_BP | SPOCTRL_V_SC_BP | | |
435 | SPOCTRL_HV_SC_OR | SPOCTRL_VS_UR_C | | |
ba282daa RA |
436 | FMsk(SPOCTRL_VPITCH)); |
437 | spoctrl |= Spoctrl_Vpitch((set->height<<11)/set->scaled_height); | |
ea465250 RA |
438 | |
439 | /* Bypass horiz/vert scaler when same size */ | |
440 | if (set->scaled_width == set->width) | |
441 | spoctrl |= SPOCTRL_H_SC_BP; | |
442 | if (set->scaled_height == set->height) | |
443 | spoctrl |= SPOCTRL_V_SC_BP; | |
444 | ||
ba282daa RA |
445 | shctrl &= ~(FMsk(SHCTRL_HPITCH) | SHCTRL_HDECIM); |
446 | shctrl |= Shctrl_Hpitch((set->width<<11)/set->scaled_width); | |
ea465250 RA |
447 | |
448 | /* Video plane registers */ | |
449 | write_reg(vsctrl, VSCTRL); | |
ea465250 RA |
450 | write_reg(vscadr, VSCADR); |
451 | write_reg(vubase, VUBASE); | |
452 | write_reg(vvbase, VVBASE); | |
453 | write_reg(vsadr, VSADR); | |
454 | ||
455 | /* Video scaler registers */ | |
456 | write_reg(sssize, SSSIZE); | |
457 | write_reg(spoctrl, SPOCTRL); | |
ea465250 RA |
458 | write_reg(shctrl, SHCTRL); |
459 | ||
ea465250 RA |
460 | /* Clock */ |
461 | if (set->enable) | |
462 | vovrclk |= 1; | |
463 | else | |
464 | vovrclk &= ~1; | |
465 | ||
466 | write_reg(vovrclk, VOVRCLK); | |
467 | ||
468 | return 0; | |
469 | } | |
470 | ||
ba282daa RA |
471 | static int mbxfb_ioctl_planeorder(struct mbxfb_planeorder *porder) |
472 | { | |
473 | unsigned long gscadr, vscadr; | |
474 | ||
475 | if (porder->bottom == porder->top) | |
476 | return -EINVAL; | |
477 | ||
478 | gscadr = readl(GSCADR); | |
479 | vscadr = readl(VSCADR); | |
480 | ||
481 | gscadr &= ~(FMsk(GSCADR_BLEND_POS)); | |
482 | vscadr &= ~(FMsk(VSCADR_BLEND_POS)); | |
483 | ||
484 | switch (porder->bottom) { | |
485 | case MBXFB_PLANE_GRAPHICS: | |
486 | gscadr |= GSCADR_BLEND_GFX; | |
487 | break; | |
488 | case MBXFB_PLANE_VIDEO: | |
489 | vscadr |= VSCADR_BLEND_GFX; | |
490 | break; | |
491 | default: | |
492 | return -EINVAL; | |
493 | } | |
494 | ||
495 | switch (porder->top) { | |
496 | case MBXFB_PLANE_GRAPHICS: | |
497 | gscadr |= GSCADR_BLEND_VID; | |
498 | break; | |
499 | case MBXFB_PLANE_VIDEO: | |
500 | vscadr |= GSCADR_BLEND_VID; | |
501 | break; | |
502 | default: | |
503 | return -EINVAL; | |
504 | } | |
505 | ||
506 | write_reg_dly(vscadr, VSCADR); | |
507 | write_reg_dly(gscadr, GSCADR); | |
508 | ||
509 | return 0; | |
510 | ||
511 | } | |
512 | ||
513 | static int mbxfb_ioctl_alphactl(struct mbxfb_alphaCtl *alpha) | |
514 | { | |
515 | unsigned long vscadr, vbbase, vcmsk; | |
516 | unsigned long gscadr, gbbase, gdrctrl; | |
517 | ||
518 | vbbase = Vbbase_Glalpha(alpha->overlay_global_alpha) | | |
519 | Vbbase_Colkey(alpha->overlay_colorkey); | |
520 | ||
521 | gbbase = Gbbase_Glalpha(alpha->graphics_global_alpha) | | |
522 | Gbbase_Colkey(alpha->graphics_colorkey); | |
523 | ||
524 | vcmsk = readl(VCMSK); | |
525 | vcmsk &= ~(FMsk(VCMSK_COLKEY_M)); | |
526 | vcmsk |= Vcmsk_colkey_m(alpha->overlay_colorkey_mask); | |
527 | ||
528 | gdrctrl = readl(GDRCTRL); | |
529 | gdrctrl &= ~(FMsk(GDRCTRL_COLKEYM)); | |
530 | gdrctrl |= Gdrctrl_Colkeym(alpha->graphics_colorkey_mask); | |
531 | ||
532 | vscadr = readl(VSCADR); | |
533 | vscadr &= ~(FMsk(VSCADR_BLEND_M) | VSCADR_COLKEYSRC | VSCADR_COLKEY_EN); | |
534 | ||
535 | gscadr = readl(GSCADR); | |
536 | gscadr &= ~(FMsk(GSCADR_BLEND_M) | GSCADR_COLKEY_EN | GSCADR_COLKEYSRC); | |
537 | ||
538 | switch (alpha->overlay_colorkey_mode) { | |
539 | case MBXFB_COLORKEY_DISABLED: | |
540 | break; | |
541 | case MBXFB_COLORKEY_PREVIOUS: | |
542 | vscadr |= VSCADR_COLKEY_EN; | |
543 | break; | |
544 | case MBXFB_COLORKEY_CURRENT: | |
545 | vscadr |= VSCADR_COLKEY_EN | VSCADR_COLKEYSRC; | |
546 | break; | |
547 | default: | |
548 | return -EINVAL; | |
549 | } | |
550 | ||
551 | switch (alpha->overlay_blend_mode) { | |
552 | case MBXFB_ALPHABLEND_NONE: | |
553 | vscadr |= VSCADR_BLEND_NONE; | |
554 | break; | |
555 | case MBXFB_ALPHABLEND_GLOBAL: | |
556 | vscadr |= VSCADR_BLEND_GLOB; | |
557 | break; | |
558 | case MBXFB_ALPHABLEND_PIXEL: | |
559 | vscadr |= VSCADR_BLEND_PIX; | |
560 | break; | |
561 | default: | |
562 | return -EINVAL; | |
563 | } | |
564 | ||
565 | switch (alpha->graphics_colorkey_mode) { | |
566 | case MBXFB_COLORKEY_DISABLED: | |
567 | break; | |
568 | case MBXFB_COLORKEY_PREVIOUS: | |
569 | gscadr |= GSCADR_COLKEY_EN; | |
570 | break; | |
571 | case MBXFB_COLORKEY_CURRENT: | |
572 | gscadr |= GSCADR_COLKEY_EN | GSCADR_COLKEYSRC; | |
573 | break; | |
574 | default: | |
575 | return -EINVAL; | |
576 | } | |
577 | ||
578 | switch (alpha->graphics_blend_mode) { | |
579 | case MBXFB_ALPHABLEND_NONE: | |
580 | gscadr |= GSCADR_BLEND_NONE; | |
581 | break; | |
582 | case MBXFB_ALPHABLEND_GLOBAL: | |
583 | gscadr |= GSCADR_BLEND_GLOB; | |
584 | break; | |
585 | case MBXFB_ALPHABLEND_PIXEL: | |
586 | gscadr |= GSCADR_BLEND_PIX; | |
587 | break; | |
588 | default: | |
589 | return -EINVAL; | |
590 | } | |
591 | ||
592 | write_reg_dly(vbbase, VBBASE); | |
593 | write_reg_dly(gbbase, GBBASE); | |
594 | write_reg_dly(vcmsk, VCMSK); | |
595 | write_reg_dly(gdrctrl, GDRCTRL); | |
596 | write_reg_dly(gscadr, GSCADR); | |
597 | write_reg_dly(vscadr, VSCADR); | |
598 | ||
599 | return 0; | |
600 | } | |
601 | ||
ea465250 RA |
602 | static int mbxfb_ioctl(struct fb_info *info, unsigned int cmd, |
603 | unsigned long arg) | |
604 | { | |
ba282daa RA |
605 | struct mbxfb_overlaySetup setup; |
606 | struct mbxfb_planeorder porder; | |
607 | struct mbxfb_alphaCtl alpha; | |
608 | struct mbxfb_reg reg; | |
ea465250 | 609 | int res; |
ba282daa | 610 | __u32 tmp; |
ea465250 | 611 | |
ba282daa | 612 | switch (cmd) |
ea465250 | 613 | { |
ba282daa RA |
614 | case MBXFB_IOCX_OVERLAY: |
615 | if (copy_from_user(&setup, (void __user*)arg, | |
616 | sizeof(struct mbxfb_overlaySetup))) | |
617 | return -EFAULT; | |
618 | ||
619 | res = mbxfb_setupOverlay(&setup); | |
620 | if (res) | |
621 | return res; | |
622 | ||
623 | if (copy_to_user((void __user*)arg, &setup, | |
624 | sizeof(struct mbxfb_overlaySetup))) | |
625 | return -EFAULT; | |
626 | ||
627 | return 0; | |
628 | ||
629 | case MBXFB_IOCS_PLANEORDER: | |
630 | if (copy_from_user(&porder, (void __user*)arg, | |
631 | sizeof(struct mbxfb_planeorder))) | |
ea465250 RA |
632 | return -EFAULT; |
633 | ||
ba282daa | 634 | return mbxfb_ioctl_planeorder(&porder); |
ea465250 | 635 | |
ba282daa RA |
636 | case MBXFB_IOCS_ALPHA: |
637 | if (copy_from_user(&alpha, (void __user*)arg, | |
638 | sizeof(struct mbxfb_alphaCtl))) | |
ea465250 RA |
639 | return -EFAULT; |
640 | ||
ba282daa RA |
641 | return mbxfb_ioctl_alphactl(&alpha); |
642 | ||
643 | case MBXFB_IOCS_REG: | |
644 | if (copy_from_user(®, (void __user*)arg, | |
645 | sizeof(struct mbxfb_reg))) | |
646 | return -EFAULT; | |
647 | ||
648 | if (reg.addr >= 0x10000) /* regs are from 0x3fe0000 to 0x3feffff */ | |
649 | return -EINVAL; | |
650 | ||
651 | tmp = readl(virt_base_2700 + reg.addr); | |
652 | tmp &= ~reg.mask; | |
653 | tmp |= reg.val & reg.mask; | |
654 | writel(tmp, virt_base_2700 + reg.addr); | |
655 | ||
656 | return 0; | |
657 | case MBXFB_IOCX_REG: | |
658 | if (copy_from_user(®, (void __user*)arg, | |
659 | sizeof(struct mbxfb_reg))) | |
660 | return -EFAULT; | |
661 | ||
662 | if (reg.addr >= 0x10000) /* regs are from 0x3fe0000 to 0x3feffff */ | |
663 | return -EINVAL; | |
664 | reg.val = readl(virt_base_2700 + reg.addr); | |
665 | ||
666 | if (copy_to_user((void __user*)arg, ®, | |
667 | sizeof(struct mbxfb_reg))) | |
668 | return -EFAULT; | |
669 | ||
670 | return 0; | |
ea465250 RA |
671 | } |
672 | return -EINVAL; | |
673 | } | |
674 | ||
22caf042 MR |
675 | static struct fb_ops mbxfb_ops = { |
676 | .owner = THIS_MODULE, | |
677 | .fb_check_var = mbxfb_check_var, | |
678 | .fb_set_par = mbxfb_set_par, | |
679 | .fb_setcolreg = mbxfb_setcolreg, | |
680 | .fb_fillrect = cfb_fillrect, | |
681 | .fb_copyarea = cfb_copyarea, | |
682 | .fb_imageblit = cfb_imageblit, | |
683 | .fb_blank = mbxfb_blank, | |
ea465250 | 684 | .fb_ioctl = mbxfb_ioctl, |
22caf042 MR |
685 | }; |
686 | ||
687 | /* | |
688 | Enable external SDRAM controller. Assume that all clocks are active | |
689 | by now. | |
690 | */ | |
691 | static void __devinit setup_memc(struct fb_info *fbi) | |
692 | { | |
22caf042 MR |
693 | unsigned long tmp; |
694 | int i; | |
695 | ||
947af294 | 696 | /* FIXME: use platform specific parameters */ |
22caf042 | 697 | /* setup SDRAM controller */ |
ea465250 | 698 | write_reg_dly((LMCFG_LMC_DS | LMCFG_LMC_TS | LMCFG_LMD_TS | |
22caf042 MR |
699 | LMCFG_LMA_TS), |
700 | LMCFG); | |
22caf042 | 701 | |
ea465250 | 702 | write_reg_dly(LMPWR_MC_PWR_ACT, LMPWR); |
22caf042 MR |
703 | |
704 | /* setup SDRAM timings */ | |
ea465250 | 705 | write_reg_dly((Lmtim_Tras(7) | Lmtim_Trp(3) | Lmtim_Trcd(3) | |
22caf042 MR |
706 | Lmtim_Trc(9) | Lmtim_Tdpl(2)), |
707 | LMTIM); | |
22caf042 | 708 | /* setup SDRAM refresh rate */ |
ea465250 | 709 | write_reg_dly(0xc2b, LMREFRESH); |
22caf042 | 710 | /* setup SDRAM type parameters */ |
ea465250 | 711 | write_reg_dly((LMTYPE_CASLAT_3 | LMTYPE_BKSZ_2 | LMTYPE_ROWSZ_11 | |
22caf042 MR |
712 | LMTYPE_COLSZ_8), |
713 | LMTYPE); | |
22caf042 | 714 | /* enable memory controller */ |
ea465250 | 715 | write_reg_dly(LMPWR_MC_PWR_ACT, LMPWR); |
22caf042 MR |
716 | /* perform dummy reads */ |
717 | for ( i = 0; i < 16; i++ ) { | |
718 | tmp = readl(fbi->screen_base); | |
719 | } | |
720 | } | |
721 | ||
722 | static void enable_clocks(struct fb_info *fbi) | |
723 | { | |
724 | /* enable clocks */ | |
ea465250 RA |
725 | write_reg_dly(SYSCLKSRC_PLL_2, SYSCLKSRC); |
726 | write_reg_dly(PIXCLKSRC_PLL_1, PIXCLKSRC); | |
727 | write_reg_dly(0x00000000, CLKSLEEP); | |
728 | ||
729 | /* PLL output = (Frefclk * M) / (N * 2^P ) | |
730 | * | |
731 | * M: 0x17, N: 0x3, P: 0x0 == 100 Mhz! | |
732 | * M: 0xb, N: 0x1, P: 0x1 == 71 Mhz | |
733 | * */ | |
734 | write_reg_dly((Core_Pll_M(0xb) | Core_Pll_N(0x1) | Core_Pll_P(0x1) | | |
22caf042 MR |
735 | CORE_PLL_EN), |
736 | COREPLL); | |
ea465250 RA |
737 | |
738 | write_reg_dly((Disp_Pll_M(0x1b) | Disp_Pll_N(0x7) | Disp_Pll_P(0x1) | | |
22caf042 MR |
739 | DISP_PLL_EN), |
740 | DISPPLL); | |
741 | ||
ea465250 RA |
742 | write_reg_dly(0x00000000, VOVRCLK); |
743 | write_reg_dly(PIXCLK_EN, PIXCLK); | |
744 | write_reg_dly(MEMCLK_EN, MEMCLK); | |
ba282daa RA |
745 | write_reg_dly(0x00000001, M24CLK); |
746 | write_reg_dly(0x00000001, MBXCLK); | |
ea465250 RA |
747 | write_reg_dly(SDCLK_EN, SDCLK); |
748 | write_reg_dly(0x00000001, PIXCLKDIV); | |
22caf042 MR |
749 | } |
750 | ||
751 | static void __devinit setup_graphics(struct fb_info *fbi) | |
752 | { | |
753 | unsigned long gsctrl; | |
ba282daa | 754 | unsigned long vscadr; |
22caf042 | 755 | |
5c60b118 RA |
756 | gsctrl = GSCTRL_GAMMA_EN | Gsctrl_Width(fbi->var.xres) | |
757 | Gsctrl_Height(fbi->var.yres); | |
22caf042 MR |
758 | switch (fbi->var.bits_per_pixel) { |
759 | case 16: | |
760 | if (fbi->var.green.length == 5) | |
761 | gsctrl |= GSCTRL_GPIXFMT_ARGB1555; | |
762 | else | |
763 | gsctrl |= GSCTRL_GPIXFMT_RGB565; | |
764 | break; | |
765 | case 24: | |
766 | gsctrl |= GSCTRL_GPIXFMT_RGB888; | |
767 | break; | |
768 | case 32: | |
769 | gsctrl |= GSCTRL_GPIXFMT_ARGB8888; | |
770 | break; | |
771 | } | |
772 | ||
ea465250 RA |
773 | write_reg_dly(gsctrl, GSCTRL); |
774 | write_reg_dly(0x00000000, GBBASE); | |
775 | write_reg_dly(0x00ffffff, GDRCTRL); | |
776 | write_reg_dly((GSCADR_STR_EN | Gscadr_Gbase_Adr(0x6000)), GSCADR); | |
777 | write_reg_dly(0x00000000, GPLUT); | |
ba282daa RA |
778 | |
779 | vscadr = readl(VSCADR); | |
780 | vscadr &= ~(FMsk(VSCADR_BLEND_POS) | FMsk(VSCADR_BLEND_M)); | |
781 | vscadr |= VSCADR_BLEND_VID | VSCADR_BLEND_NONE; | |
782 | write_reg_dly(vscadr, VSCADR); | |
22caf042 MR |
783 | } |
784 | ||
785 | static void __devinit setup_display(struct fb_info *fbi) | |
786 | { | |
787 | unsigned long dsctrl = 0; | |
788 | ||
789 | dsctrl = DSCTRL_BLNK_POL; | |
790 | if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT) | |
791 | dsctrl |= DSCTRL_HS_POL; | |
792 | if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT) | |
793 | dsctrl |= DSCTRL_VS_POL; | |
ea465250 RA |
794 | write_reg_dly(dsctrl, DSCTRL); |
795 | write_reg_dly(0xd0303010, DMCTRL); | |
796 | write_reg_dly((readl(DSCTRL) | DSCTRL_SYNCGEN_EN), DSCTRL); | |
22caf042 MR |
797 | } |
798 | ||
799 | static void __devinit enable_controller(struct fb_info *fbi) | |
800 | { | |
ba282daa RA |
801 | u32 svctrl, shctrl; |
802 | ||
ea465250 | 803 | write_reg_dly(SYSRST_RST, SYSRST); |
22caf042 | 804 | |
ba282daa RA |
805 | /* setup a timeout, raise drive strength */ |
806 | write_reg_dly(0xffffff0c, SYSCFG); | |
22caf042 MR |
807 | |
808 | enable_clocks(fbi); | |
809 | setup_memc(fbi); | |
810 | setup_graphics(fbi); | |
811 | setup_display(fbi); | |
ba282daa RA |
812 | |
813 | shctrl = readl(SHCTRL); | |
814 | shctrl &= ~(FMsk(SHCTRL_HINITIAL)); | |
815 | shctrl |= Shctrl_Hinitial(4<<11); | |
816 | writel(shctrl, SHCTRL); | |
817 | ||
818 | svctrl = Svctrl_Initial1(1<<10) | Svctrl_Initial2(1<<10); | |
819 | writel(svctrl, SVCTRL); | |
820 | ||
821 | writel(SPOCTRL_H_SC_BP | SPOCTRL_V_SC_BP | SPOCTRL_VORDER_4TAP | |
822 | , SPOCTRL); | |
823 | ||
824 | /* Those coefficients are good for scaling up. For scaling | |
825 | * down, the application has to calculate them. */ | |
826 | write_reg(0xff000100, VSCOEFF0); | |
827 | write_reg(0xfdfcfdfe, VSCOEFF1); | |
828 | write_reg(0x170d0500, VSCOEFF2); | |
829 | write_reg(0x3d372d22, VSCOEFF3); | |
830 | write_reg(0x00000040, VSCOEFF4); | |
831 | ||
832 | write_reg(0xff010100, HSCOEFF0); | |
833 | write_reg(0x00000000, HSCOEFF1); | |
834 | write_reg(0x02010000, HSCOEFF2); | |
835 | write_reg(0x01020302, HSCOEFF3); | |
836 | write_reg(0xf9fbfe00, HSCOEFF4); | |
837 | write_reg(0xfbf7f6f7, HSCOEFF5); | |
838 | write_reg(0x1c110700, HSCOEFF6); | |
839 | write_reg(0x3e393127, HSCOEFF7); | |
840 | write_reg(0x00000040, HSCOEFF8); | |
841 | ||
22caf042 MR |
842 | } |
843 | ||
844 | #ifdef CONFIG_PM | |
845 | /* | |
846 | * Power management hooks. Note that we won't be called from IRQ context, | |
847 | * unlike the blank functions above, so we may sleep. | |
848 | */ | |
849 | static int mbxfb_suspend(struct platform_device *dev, pm_message_t state) | |
850 | { | |
851 | /* make frame buffer memory enter self-refresh mode */ | |
ea465250 | 852 | write_reg_dly(LMPWR_MC_PWR_SRM, LMPWR); |
22caf042 MR |
853 | while (LMPWRSTAT != LMPWRSTAT_MC_PWR_SRM) |
854 | ; /* empty statement */ | |
855 | ||
856 | /* reset the device, since it's initial state is 'mostly sleeping' */ | |
ea465250 | 857 | write_reg_dly(SYSRST_RST, SYSRST); |
22caf042 MR |
858 | return 0; |
859 | } | |
860 | ||
861 | static int mbxfb_resume(struct platform_device *dev) | |
862 | { | |
863 | struct fb_info *fbi = platform_get_drvdata(dev); | |
864 | ||
865 | enable_clocks(fbi); | |
866 | /* setup_graphics(fbi); */ | |
867 | /* setup_display(fbi); */ | |
868 | ||
ea465250 | 869 | write_reg_dly((readl(DSCTRL) | DSCTRL_SYNCGEN_EN), DSCTRL); |
22caf042 MR |
870 | return 0; |
871 | } | |
872 | #else | |
873 | #define mbxfb_suspend NULL | |
874 | #define mbxfb_resume NULL | |
875 | #endif | |
876 | ||
877 | /* debugfs entries */ | |
878 | #ifndef CONFIG_FB_MBX_DEBUG | |
879 | #define mbxfb_debugfs_init(x) do {} while(0) | |
880 | #define mbxfb_debugfs_remove(x) do {} while(0) | |
881 | #endif | |
882 | ||
883 | #define res_size(_r) (((_r)->end - (_r)->start) + 1) | |
884 | ||
885 | static int __devinit mbxfb_probe(struct platform_device *dev) | |
886 | { | |
887 | int ret; | |
888 | struct fb_info *fbi; | |
889 | struct mbxfb_info *mfbi; | |
890 | struct mbxfb_platform_data *pdata; | |
891 | ||
1039edc9 | 892 | dev_dbg(&dev->dev, "mbxfb_probe\n"); |
22caf042 | 893 | |
ea465250 RA |
894 | pdata = dev->dev.platform_data; |
895 | if (!pdata) { | |
896 | dev_err(&dev->dev, "platform data is required\n"); | |
897 | return -EINVAL; | |
898 | } | |
899 | ||
22caf042 MR |
900 | fbi = framebuffer_alloc(sizeof(struct mbxfb_info), &dev->dev); |
901 | if (fbi == NULL) { | |
902 | dev_err(&dev->dev, "framebuffer_alloc failed\n"); | |
903 | return -ENOMEM; | |
904 | } | |
905 | ||
906 | mfbi = fbi->par; | |
907 | fbi->pseudo_palette = mfbi->pseudo_palette; | |
ea465250 RA |
908 | |
909 | ||
22caf042 MR |
910 | if (pdata->probe) |
911 | mfbi->platform_probe = pdata->probe; | |
912 | if (pdata->remove) | |
913 | mfbi->platform_remove = pdata->remove; | |
914 | ||
915 | mfbi->fb_res = platform_get_resource(dev, IORESOURCE_MEM, 0); | |
916 | mfbi->reg_res = platform_get_resource(dev, IORESOURCE_MEM, 1); | |
917 | ||
918 | if (!mfbi->fb_res || !mfbi->reg_res) { | |
919 | dev_err(&dev->dev, "no resources found\n"); | |
920 | ret = -ENODEV; | |
921 | goto err1; | |
922 | } | |
923 | ||
924 | mfbi->fb_req = request_mem_region(mfbi->fb_res->start, | |
925 | res_size(mfbi->fb_res), dev->name); | |
926 | if (mfbi->fb_req == NULL) { | |
927 | dev_err(&dev->dev, "failed to claim framebuffer memory\n"); | |
928 | ret = -EINVAL; | |
929 | goto err1; | |
930 | } | |
931 | mfbi->fb_phys_addr = mfbi->fb_res->start; | |
932 | ||
933 | mfbi->reg_req = request_mem_region(mfbi->reg_res->start, | |
934 | res_size(mfbi->reg_res), dev->name); | |
935 | if (mfbi->reg_req == NULL) { | |
936 | dev_err(&dev->dev, "failed to claim Marathon registers\n"); | |
937 | ret = -EINVAL; | |
938 | goto err2; | |
939 | } | |
940 | mfbi->reg_phys_addr = mfbi->reg_res->start; | |
941 | ||
942 | mfbi->reg_virt_addr = ioremap_nocache(mfbi->reg_phys_addr, | |
943 | res_size(mfbi->reg_req)); | |
944 | if (!mfbi->reg_virt_addr) { | |
945 | dev_err(&dev->dev, "failed to ioremap Marathon registers\n"); | |
946 | ret = -EINVAL; | |
947 | goto err3; | |
948 | } | |
949 | virt_base_2700 = (unsigned long)mfbi->reg_virt_addr; | |
950 | ||
951 | mfbi->fb_virt_addr = ioremap_nocache(mfbi->fb_phys_addr, | |
952 | res_size(mfbi->fb_req)); | |
953 | if (!mfbi->reg_virt_addr) { | |
954 | dev_err(&dev->dev, "failed to ioremap frame buffer\n"); | |
955 | ret = -EINVAL; | |
956 | goto err4; | |
957 | } | |
958 | ||
22caf042 | 959 | fbi->screen_base = (char __iomem *)(mfbi->fb_virt_addr + 0x60000); |
ea465250 | 960 | fbi->screen_size = pdata->memsize; |
22caf042 MR |
961 | fbi->fbops = &mbxfb_ops; |
962 | ||
963 | fbi->var = mbxfb_default; | |
964 | fbi->fix = mbxfb_fix; | |
965 | fbi->fix.smem_start = mfbi->fb_phys_addr + 0x60000; | |
ea465250 RA |
966 | fbi->fix.smem_len = pdata->memsize; |
967 | fbi->fix.line_length = mbxfb_default.xres_virtual * | |
968 | mbxfb_default.bits_per_pixel / 8; | |
22caf042 MR |
969 | |
970 | ret = fb_alloc_cmap(&fbi->cmap, 256, 0); | |
971 | if (ret < 0) { | |
972 | dev_err(&dev->dev, "fb_alloc_cmap failed\n"); | |
973 | ret = -EINVAL; | |
974 | goto err5; | |
975 | } | |
976 | ||
977 | platform_set_drvdata(dev, fbi); | |
978 | ||
979 | printk(KERN_INFO "fb%d: mbx frame buffer device\n", fbi->node); | |
980 | ||
981 | if (mfbi->platform_probe) | |
982 | mfbi->platform_probe(fbi); | |
983 | ||
984 | enable_controller(fbi); | |
985 | ||
986 | mbxfb_debugfs_init(fbi); | |
987 | ||
988 | ret = register_framebuffer(fbi); | |
989 | if (ret < 0) { | |
990 | dev_err(&dev->dev, "register_framebuffer failed\n"); | |
991 | ret = -EINVAL; | |
992 | goto err6; | |
993 | } | |
994 | ||
995 | return 0; | |
996 | ||
997 | err6: | |
998 | fb_dealloc_cmap(&fbi->cmap); | |
999 | err5: | |
1000 | iounmap(mfbi->fb_virt_addr); | |
1001 | err4: | |
1002 | iounmap(mfbi->reg_virt_addr); | |
1003 | err3: | |
1004 | release_mem_region(mfbi->reg_res->start, res_size(mfbi->reg_res)); | |
1005 | err2: | |
1006 | release_mem_region(mfbi->fb_res->start, res_size(mfbi->fb_res)); | |
1007 | err1: | |
1008 | framebuffer_release(fbi); | |
1009 | ||
1010 | return ret; | |
1011 | } | |
1012 | ||
1013 | static int __devexit mbxfb_remove(struct platform_device *dev) | |
1014 | { | |
1015 | struct fb_info *fbi = platform_get_drvdata(dev); | |
1016 | ||
ea465250 | 1017 | write_reg_dly(SYSRST_RST, SYSRST); |
22caf042 MR |
1018 | |
1019 | mbxfb_debugfs_remove(fbi); | |
1020 | ||
1021 | if (fbi) { | |
1022 | struct mbxfb_info *mfbi = fbi->par; | |
1023 | ||
1024 | unregister_framebuffer(fbi); | |
1025 | if (mfbi) { | |
1026 | if (mfbi->platform_remove) | |
1027 | mfbi->platform_remove(fbi); | |
1028 | ||
1029 | if (mfbi->fb_virt_addr) | |
1030 | iounmap(mfbi->fb_virt_addr); | |
1031 | if (mfbi->reg_virt_addr) | |
1032 | iounmap(mfbi->reg_virt_addr); | |
1033 | if (mfbi->reg_req) | |
1034 | release_mem_region(mfbi->reg_req->start, | |
1035 | res_size(mfbi->reg_req)); | |
1036 | if (mfbi->fb_req) | |
1037 | release_mem_region(mfbi->fb_req->start, | |
1038 | res_size(mfbi->fb_req)); | |
1039 | } | |
1040 | framebuffer_release(fbi); | |
1041 | } | |
1042 | ||
1043 | return 0; | |
1044 | } | |
1045 | ||
1046 | static struct platform_driver mbxfb_driver = { | |
1047 | .probe = mbxfb_probe, | |
1048 | .remove = mbxfb_remove, | |
1049 | .suspend = mbxfb_suspend, | |
1050 | .resume = mbxfb_resume, | |
1051 | .driver = { | |
1052 | .name = "mbx-fb", | |
1053 | }, | |
1054 | }; | |
1055 | ||
1056 | int __devinit mbxfb_init(void) | |
1057 | { | |
1058 | return platform_driver_register(&mbxfb_driver); | |
1059 | } | |
1060 | ||
1061 | static void __devexit mbxfb_exit(void) | |
1062 | { | |
1063 | platform_driver_unregister(&mbxfb_driver); | |
1064 | } | |
1065 | ||
1066 | module_init(mbxfb_init); | |
1067 | module_exit(mbxfb_exit); | |
1068 | ||
1069 | MODULE_DESCRIPTION("loadable framebuffer driver for Marathon device"); | |
1070 | MODULE_AUTHOR("Mike Rapoport, Compulab"); | |
1071 | MODULE_LICENSE("GPL"); |