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