Commit | Line | Data |
---|---|---|
22caf042 MR |
1 | /* |
2 | * linux/drivers/video/mbx/mbxfb.c | |
3 | * | |
ea465250 RA |
4 | * Copyright (C) 2006 8D Technologies inc |
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 | { | |
337 | u32 vsctrl, vbbase, vscadr, vsadr; | |
338 | u32 sssize, spoctrl, svctrl, shctrl; | |
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); | |
352 | ||
353 | spoctrl = readl(SPOCTRL); | |
354 | sssize = readl(SSSIZE); | |
355 | ||
356 | ||
357 | vbbase = Vbbase_Glalpha(set->alpha); | |
358 | ||
359 | vsctrl &= ~( FMsk(VSCTRL_VSWIDTH) | | |
360 | FMsk(VSCTRL_VSHEIGHT) | | |
361 | FMsk(VSCTRL_VPIXFMT) | | |
362 | VSCTRL_GAMMA_EN | VSCTRL_CSC_EN | | |
363 | VSCTRL_COSITED ); | |
364 | vsctrl |= Vsctrl_Width(set->width) | Vsctrl_Height(set->height) | | |
365 | VSCTRL_CSC_EN; | |
366 | ||
367 | vscadr &= ~(VSCADR_STR_EN | VSCADR_COLKEY_EN | VSCADR_COLKEYSRC | | |
368 | FMsk(VSCADR_BLEND_M) | FMsk(VSCADR_BLEND_POS) | | |
369 | FMsk(VSCADR_VBASE_ADR) ); | |
370 | vubase &= ~(VUBASE_UVHALFSTR | FMsk(VUBASE_UBASE_ADR)); | |
371 | vvbase &= ~(FMsk(VVBASE_VBASE_ADR)); | |
372 | ||
373 | switch (set->fmt) | |
374 | { | |
375 | case MBXFB_FMT_YUV12: | |
376 | vsctrl |= VSCTRL_VPIXFMT_YUV12; | |
377 | ||
378 | set->Y_stride = ((set->width) + 0xf ) & ~0xf; | |
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; | |
396 | break; | |
397 | default: | |
398 | return -EINVAL; | |
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 | ||
420 | vscadr |= VSCADR_BLEND_VID | VSCADR_BLEND_GLOB | | |
421 | Vscadr_Vbase_Adr((0x60000 + set->mem_offset)>>4); | |
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 | | |
436 | FMsk(SPOCTRL_VORDER) | FMsk(SPOCTRL_VPITCH)); | |
437 | spoctrl = Spoctrl_Vpitch((set->height<<11)/set->scaled_height) | |
438 | | SPOCTRL_VORDER_2TAP; | |
439 | ||
440 | /* Bypass horiz/vert scaler when same size */ | |
441 | if (set->scaled_width == set->width) | |
442 | spoctrl |= SPOCTRL_H_SC_BP; | |
443 | if (set->scaled_height == set->height) | |
444 | spoctrl |= SPOCTRL_V_SC_BP; | |
445 | ||
446 | svctrl = Svctrl_Initial1(1<<10) | Svctrl_Initial2(1<<10); | |
447 | ||
448 | shctrl = Shctrl_Hinitial(4<<11) | |
449 | | Shctrl_Hpitch((set->width<<11)/set->scaled_width); | |
450 | ||
451 | /* Video plane registers */ | |
452 | write_reg(vsctrl, VSCTRL); | |
453 | write_reg(vbbase, VBBASE); | |
454 | write_reg(vscadr, VSCADR); | |
455 | write_reg(vubase, VUBASE); | |
456 | write_reg(vvbase, VVBASE); | |
457 | write_reg(vsadr, VSADR); | |
458 | ||
459 | /* Video scaler registers */ | |
460 | write_reg(sssize, SSSIZE); | |
461 | write_reg(spoctrl, SPOCTRL); | |
462 | write_reg(svctrl, SVCTRL); | |
463 | write_reg(shctrl, SHCTRL); | |
464 | ||
465 | /* RAPH: Using those coefficients, the scaled | |
466 | * image is quite blurry. I dont know how | |
467 | * to improve them ; The chip documentation | |
468 | * was not helpful.. */ | |
469 | write_reg(0x21212121, VSCOEFF0); | |
470 | write_reg(0x21212121, VSCOEFF1); | |
471 | write_reg(0x21212121, VSCOEFF2); | |
472 | write_reg(0x21212121, VSCOEFF3); | |
473 | write_reg(0x21212121, VSCOEFF4); | |
474 | write_reg(0x00000000, HSCOEFF0); | |
475 | write_reg(0x00000000, HSCOEFF1); | |
476 | write_reg(0x00000000, HSCOEFF2); | |
477 | write_reg(0x03020201, HSCOEFF3); | |
478 | write_reg(0x09070604, HSCOEFF4); | |
479 | write_reg(0x0f0e0c0a, HSCOEFF5); | |
480 | write_reg(0x15141211, HSCOEFF6); | |
481 | write_reg(0x19181716, HSCOEFF7); | |
482 | write_reg(0x00000019, HSCOEFF8); | |
483 | ||
484 | /* Clock */ | |
485 | if (set->enable) | |
486 | vovrclk |= 1; | |
487 | else | |
488 | vovrclk &= ~1; | |
489 | ||
490 | write_reg(vovrclk, VOVRCLK); | |
491 | ||
492 | return 0; | |
493 | } | |
494 | ||
495 | static int mbxfb_ioctl(struct fb_info *info, unsigned int cmd, | |
496 | unsigned long arg) | |
497 | { | |
498 | struct mbxfb_overlaySetup setup; | |
499 | int res; | |
500 | ||
501 | if (cmd == MBXFB_IOCX_OVERLAY) | |
502 | { | |
503 | if (copy_from_user(&setup, (void __user*)arg, | |
504 | sizeof(struct mbxfb_overlaySetup))) | |
505 | return -EFAULT; | |
506 | ||
507 | res = mbxfb_setupOverlay(&setup); | |
508 | if (res) | |
509 | return res; | |
510 | ||
511 | if (copy_to_user((void __user*)arg, &setup, | |
512 | sizeof(struct mbxfb_overlaySetup))) | |
513 | return -EFAULT; | |
514 | ||
515 | return 0; | |
516 | } | |
517 | return -EINVAL; | |
518 | } | |
519 | ||
22caf042 MR |
520 | static struct fb_ops mbxfb_ops = { |
521 | .owner = THIS_MODULE, | |
522 | .fb_check_var = mbxfb_check_var, | |
523 | .fb_set_par = mbxfb_set_par, | |
524 | .fb_setcolreg = mbxfb_setcolreg, | |
525 | .fb_fillrect = cfb_fillrect, | |
526 | .fb_copyarea = cfb_copyarea, | |
527 | .fb_imageblit = cfb_imageblit, | |
528 | .fb_blank = mbxfb_blank, | |
ea465250 | 529 | .fb_ioctl = mbxfb_ioctl, |
22caf042 MR |
530 | }; |
531 | ||
532 | /* | |
533 | Enable external SDRAM controller. Assume that all clocks are active | |
534 | by now. | |
535 | */ | |
536 | static void __devinit setup_memc(struct fb_info *fbi) | |
537 | { | |
22caf042 MR |
538 | unsigned long tmp; |
539 | int i; | |
540 | ||
541 | /* FIXME: use platfrom specific parameters */ | |
542 | /* setup SDRAM controller */ | |
ea465250 | 543 | write_reg_dly((LMCFG_LMC_DS | LMCFG_LMC_TS | LMCFG_LMD_TS | |
22caf042 MR |
544 | LMCFG_LMA_TS), |
545 | LMCFG); | |
22caf042 | 546 | |
ea465250 | 547 | write_reg_dly(LMPWR_MC_PWR_ACT, LMPWR); |
22caf042 MR |
548 | |
549 | /* setup SDRAM timings */ | |
ea465250 | 550 | write_reg_dly((Lmtim_Tras(7) | Lmtim_Trp(3) | Lmtim_Trcd(3) | |
22caf042 MR |
551 | Lmtim_Trc(9) | Lmtim_Tdpl(2)), |
552 | LMTIM); | |
22caf042 | 553 | /* setup SDRAM refresh rate */ |
ea465250 | 554 | write_reg_dly(0xc2b, LMREFRESH); |
22caf042 | 555 | /* setup SDRAM type parameters */ |
ea465250 | 556 | write_reg_dly((LMTYPE_CASLAT_3 | LMTYPE_BKSZ_2 | LMTYPE_ROWSZ_11 | |
22caf042 MR |
557 | LMTYPE_COLSZ_8), |
558 | LMTYPE); | |
22caf042 | 559 | /* enable memory controller */ |
ea465250 | 560 | write_reg_dly(LMPWR_MC_PWR_ACT, LMPWR); |
22caf042 MR |
561 | |
562 | /* perform dummy reads */ | |
563 | for ( i = 0; i < 16; i++ ) { | |
564 | tmp = readl(fbi->screen_base); | |
565 | } | |
566 | } | |
567 | ||
568 | static void enable_clocks(struct fb_info *fbi) | |
569 | { | |
570 | /* enable clocks */ | |
ea465250 RA |
571 | write_reg_dly(SYSCLKSRC_PLL_2, SYSCLKSRC); |
572 | write_reg_dly(PIXCLKSRC_PLL_1, PIXCLKSRC); | |
573 | write_reg_dly(0x00000000, CLKSLEEP); | |
574 | ||
575 | /* PLL output = (Frefclk * M) / (N * 2^P ) | |
576 | * | |
577 | * M: 0x17, N: 0x3, P: 0x0 == 100 Mhz! | |
578 | * M: 0xb, N: 0x1, P: 0x1 == 71 Mhz | |
579 | * */ | |
580 | write_reg_dly((Core_Pll_M(0xb) | Core_Pll_N(0x1) | Core_Pll_P(0x1) | | |
22caf042 MR |
581 | CORE_PLL_EN), |
582 | COREPLL); | |
ea465250 RA |
583 | |
584 | write_reg_dly((Disp_Pll_M(0x1b) | Disp_Pll_N(0x7) | Disp_Pll_P(0x1) | | |
22caf042 MR |
585 | DISP_PLL_EN), |
586 | DISPPLL); | |
587 | ||
ea465250 RA |
588 | write_reg_dly(0x00000000, VOVRCLK); |
589 | write_reg_dly(PIXCLK_EN, PIXCLK); | |
590 | write_reg_dly(MEMCLK_EN, MEMCLK); | |
591 | write_reg_dly(0x00000006, M24CLK); | |
592 | write_reg_dly(0x00000006, MBXCLK); | |
593 | write_reg_dly(SDCLK_EN, SDCLK); | |
594 | write_reg_dly(0x00000001, PIXCLKDIV); | |
22caf042 MR |
595 | } |
596 | ||
597 | static void __devinit setup_graphics(struct fb_info *fbi) | |
598 | { | |
599 | unsigned long gsctrl; | |
600 | ||
5c60b118 RA |
601 | gsctrl = GSCTRL_GAMMA_EN | Gsctrl_Width(fbi->var.xres) | |
602 | Gsctrl_Height(fbi->var.yres); | |
22caf042 MR |
603 | switch (fbi->var.bits_per_pixel) { |
604 | case 16: | |
605 | if (fbi->var.green.length == 5) | |
606 | gsctrl |= GSCTRL_GPIXFMT_ARGB1555; | |
607 | else | |
608 | gsctrl |= GSCTRL_GPIXFMT_RGB565; | |
609 | break; | |
610 | case 24: | |
611 | gsctrl |= GSCTRL_GPIXFMT_RGB888; | |
612 | break; | |
613 | case 32: | |
614 | gsctrl |= GSCTRL_GPIXFMT_ARGB8888; | |
615 | break; | |
616 | } | |
617 | ||
ea465250 RA |
618 | write_reg_dly(gsctrl, GSCTRL); |
619 | write_reg_dly(0x00000000, GBBASE); | |
620 | write_reg_dly(0x00ffffff, GDRCTRL); | |
621 | write_reg_dly((GSCADR_STR_EN | Gscadr_Gbase_Adr(0x6000)), GSCADR); | |
622 | write_reg_dly(0x00000000, GPLUT); | |
22caf042 MR |
623 | } |
624 | ||
625 | static void __devinit setup_display(struct fb_info *fbi) | |
626 | { | |
627 | unsigned long dsctrl = 0; | |
628 | ||
629 | dsctrl = DSCTRL_BLNK_POL; | |
630 | if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT) | |
631 | dsctrl |= DSCTRL_HS_POL; | |
632 | if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT) | |
633 | dsctrl |= DSCTRL_VS_POL; | |
ea465250 RA |
634 | write_reg_dly(dsctrl, DSCTRL); |
635 | write_reg_dly(0xd0303010, DMCTRL); | |
636 | write_reg_dly((readl(DSCTRL) | DSCTRL_SYNCGEN_EN), DSCTRL); | |
22caf042 MR |
637 | } |
638 | ||
639 | static void __devinit enable_controller(struct fb_info *fbi) | |
640 | { | |
ea465250 | 641 | write_reg_dly(SYSRST_RST, SYSRST); |
22caf042 MR |
642 | |
643 | ||
644 | enable_clocks(fbi); | |
645 | setup_memc(fbi); | |
646 | setup_graphics(fbi); | |
647 | setup_display(fbi); | |
648 | } | |
649 | ||
650 | #ifdef CONFIG_PM | |
651 | /* | |
652 | * Power management hooks. Note that we won't be called from IRQ context, | |
653 | * unlike the blank functions above, so we may sleep. | |
654 | */ | |
655 | static int mbxfb_suspend(struct platform_device *dev, pm_message_t state) | |
656 | { | |
657 | /* make frame buffer memory enter self-refresh mode */ | |
ea465250 | 658 | write_reg_dly(LMPWR_MC_PWR_SRM, LMPWR); |
22caf042 MR |
659 | while (LMPWRSTAT != LMPWRSTAT_MC_PWR_SRM) |
660 | ; /* empty statement */ | |
661 | ||
662 | /* reset the device, since it's initial state is 'mostly sleeping' */ | |
ea465250 | 663 | write_reg_dly(SYSRST_RST, SYSRST); |
22caf042 MR |
664 | return 0; |
665 | } | |
666 | ||
667 | static int mbxfb_resume(struct platform_device *dev) | |
668 | { | |
669 | struct fb_info *fbi = platform_get_drvdata(dev); | |
670 | ||
671 | enable_clocks(fbi); | |
672 | /* setup_graphics(fbi); */ | |
673 | /* setup_display(fbi); */ | |
674 | ||
ea465250 | 675 | write_reg_dly((readl(DSCTRL) | DSCTRL_SYNCGEN_EN), DSCTRL); |
22caf042 MR |
676 | return 0; |
677 | } | |
678 | #else | |
679 | #define mbxfb_suspend NULL | |
680 | #define mbxfb_resume NULL | |
681 | #endif | |
682 | ||
683 | /* debugfs entries */ | |
684 | #ifndef CONFIG_FB_MBX_DEBUG | |
685 | #define mbxfb_debugfs_init(x) do {} while(0) | |
686 | #define mbxfb_debugfs_remove(x) do {} while(0) | |
687 | #endif | |
688 | ||
689 | #define res_size(_r) (((_r)->end - (_r)->start) + 1) | |
690 | ||
691 | static int __devinit mbxfb_probe(struct platform_device *dev) | |
692 | { | |
693 | int ret; | |
694 | struct fb_info *fbi; | |
695 | struct mbxfb_info *mfbi; | |
696 | struct mbxfb_platform_data *pdata; | |
697 | ||
698 | dev_dbg(dev, "mbxfb_probe\n"); | |
699 | ||
ea465250 RA |
700 | pdata = dev->dev.platform_data; |
701 | if (!pdata) { | |
702 | dev_err(&dev->dev, "platform data is required\n"); | |
703 | return -EINVAL; | |
704 | } | |
705 | ||
22caf042 MR |
706 | fbi = framebuffer_alloc(sizeof(struct mbxfb_info), &dev->dev); |
707 | if (fbi == NULL) { | |
708 | dev_err(&dev->dev, "framebuffer_alloc failed\n"); | |
709 | return -ENOMEM; | |
710 | } | |
711 | ||
712 | mfbi = fbi->par; | |
713 | fbi->pseudo_palette = mfbi->pseudo_palette; | |
ea465250 RA |
714 | |
715 | ||
22caf042 MR |
716 | if (pdata->probe) |
717 | mfbi->platform_probe = pdata->probe; | |
718 | if (pdata->remove) | |
719 | mfbi->platform_remove = pdata->remove; | |
720 | ||
721 | mfbi->fb_res = platform_get_resource(dev, IORESOURCE_MEM, 0); | |
722 | mfbi->reg_res = platform_get_resource(dev, IORESOURCE_MEM, 1); | |
723 | ||
724 | if (!mfbi->fb_res || !mfbi->reg_res) { | |
725 | dev_err(&dev->dev, "no resources found\n"); | |
726 | ret = -ENODEV; | |
727 | goto err1; | |
728 | } | |
729 | ||
730 | mfbi->fb_req = request_mem_region(mfbi->fb_res->start, | |
731 | res_size(mfbi->fb_res), dev->name); | |
732 | if (mfbi->fb_req == NULL) { | |
733 | dev_err(&dev->dev, "failed to claim framebuffer memory\n"); | |
734 | ret = -EINVAL; | |
735 | goto err1; | |
736 | } | |
737 | mfbi->fb_phys_addr = mfbi->fb_res->start; | |
738 | ||
739 | mfbi->reg_req = request_mem_region(mfbi->reg_res->start, | |
740 | res_size(mfbi->reg_res), dev->name); | |
741 | if (mfbi->reg_req == NULL) { | |
742 | dev_err(&dev->dev, "failed to claim Marathon registers\n"); | |
743 | ret = -EINVAL; | |
744 | goto err2; | |
745 | } | |
746 | mfbi->reg_phys_addr = mfbi->reg_res->start; | |
747 | ||
748 | mfbi->reg_virt_addr = ioremap_nocache(mfbi->reg_phys_addr, | |
749 | res_size(mfbi->reg_req)); | |
750 | if (!mfbi->reg_virt_addr) { | |
751 | dev_err(&dev->dev, "failed to ioremap Marathon registers\n"); | |
752 | ret = -EINVAL; | |
753 | goto err3; | |
754 | } | |
755 | virt_base_2700 = (unsigned long)mfbi->reg_virt_addr; | |
756 | ||
757 | mfbi->fb_virt_addr = ioremap_nocache(mfbi->fb_phys_addr, | |
758 | res_size(mfbi->fb_req)); | |
759 | if (!mfbi->reg_virt_addr) { | |
760 | dev_err(&dev->dev, "failed to ioremap frame buffer\n"); | |
761 | ret = -EINVAL; | |
762 | goto err4; | |
763 | } | |
764 | ||
22caf042 | 765 | fbi->screen_base = (char __iomem *)(mfbi->fb_virt_addr + 0x60000); |
ea465250 | 766 | fbi->screen_size = pdata->memsize; |
22caf042 MR |
767 | fbi->fbops = &mbxfb_ops; |
768 | ||
769 | fbi->var = mbxfb_default; | |
770 | fbi->fix = mbxfb_fix; | |
771 | fbi->fix.smem_start = mfbi->fb_phys_addr + 0x60000; | |
ea465250 RA |
772 | fbi->fix.smem_len = pdata->memsize; |
773 | fbi->fix.line_length = mbxfb_default.xres_virtual * | |
774 | mbxfb_default.bits_per_pixel / 8; | |
22caf042 MR |
775 | |
776 | ret = fb_alloc_cmap(&fbi->cmap, 256, 0); | |
777 | if (ret < 0) { | |
778 | dev_err(&dev->dev, "fb_alloc_cmap failed\n"); | |
779 | ret = -EINVAL; | |
780 | goto err5; | |
781 | } | |
782 | ||
783 | platform_set_drvdata(dev, fbi); | |
784 | ||
785 | printk(KERN_INFO "fb%d: mbx frame buffer device\n", fbi->node); | |
786 | ||
787 | if (mfbi->platform_probe) | |
788 | mfbi->platform_probe(fbi); | |
789 | ||
790 | enable_controller(fbi); | |
791 | ||
792 | mbxfb_debugfs_init(fbi); | |
793 | ||
794 | ret = register_framebuffer(fbi); | |
795 | if (ret < 0) { | |
796 | dev_err(&dev->dev, "register_framebuffer failed\n"); | |
797 | ret = -EINVAL; | |
798 | goto err6; | |
799 | } | |
800 | ||
801 | return 0; | |
802 | ||
803 | err6: | |
804 | fb_dealloc_cmap(&fbi->cmap); | |
805 | err5: | |
806 | iounmap(mfbi->fb_virt_addr); | |
807 | err4: | |
808 | iounmap(mfbi->reg_virt_addr); | |
809 | err3: | |
810 | release_mem_region(mfbi->reg_res->start, res_size(mfbi->reg_res)); | |
811 | err2: | |
812 | release_mem_region(mfbi->fb_res->start, res_size(mfbi->fb_res)); | |
813 | err1: | |
814 | framebuffer_release(fbi); | |
815 | ||
816 | return ret; | |
817 | } | |
818 | ||
819 | static int __devexit mbxfb_remove(struct platform_device *dev) | |
820 | { | |
821 | struct fb_info *fbi = platform_get_drvdata(dev); | |
822 | ||
ea465250 | 823 | write_reg_dly(SYSRST_RST, SYSRST); |
22caf042 MR |
824 | |
825 | mbxfb_debugfs_remove(fbi); | |
826 | ||
827 | if (fbi) { | |
828 | struct mbxfb_info *mfbi = fbi->par; | |
829 | ||
830 | unregister_framebuffer(fbi); | |
831 | if (mfbi) { | |
832 | if (mfbi->platform_remove) | |
833 | mfbi->platform_remove(fbi); | |
834 | ||
835 | if (mfbi->fb_virt_addr) | |
836 | iounmap(mfbi->fb_virt_addr); | |
837 | if (mfbi->reg_virt_addr) | |
838 | iounmap(mfbi->reg_virt_addr); | |
839 | if (mfbi->reg_req) | |
840 | release_mem_region(mfbi->reg_req->start, | |
841 | res_size(mfbi->reg_req)); | |
842 | if (mfbi->fb_req) | |
843 | release_mem_region(mfbi->fb_req->start, | |
844 | res_size(mfbi->fb_req)); | |
845 | } | |
846 | framebuffer_release(fbi); | |
847 | } | |
848 | ||
849 | return 0; | |
850 | } | |
851 | ||
852 | static struct platform_driver mbxfb_driver = { | |
853 | .probe = mbxfb_probe, | |
854 | .remove = mbxfb_remove, | |
855 | .suspend = mbxfb_suspend, | |
856 | .resume = mbxfb_resume, | |
857 | .driver = { | |
858 | .name = "mbx-fb", | |
859 | }, | |
860 | }; | |
861 | ||
862 | int __devinit mbxfb_init(void) | |
863 | { | |
864 | return platform_driver_register(&mbxfb_driver); | |
865 | } | |
866 | ||
867 | static void __devexit mbxfb_exit(void) | |
868 | { | |
869 | platform_driver_unregister(&mbxfb_driver); | |
870 | } | |
871 | ||
872 | module_init(mbxfb_init); | |
873 | module_exit(mbxfb_exit); | |
874 | ||
875 | MODULE_DESCRIPTION("loadable framebuffer driver for Marathon device"); | |
876 | MODULE_AUTHOR("Mike Rapoport, Compulab"); | |
877 | MODULE_LICENSE("GPL"); |