1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * drivers/mb862xx/mb862xxfb.c |
4 | * |
5 | * Fujitsu Carmine/Coral-P(A)/Lime framebuffer driver |
6 | * |
7 | * (C) 2008 Anatolij Gustschin <agust@denx.de> |
8 | * DENX Software Engineering |
9 | */ |
10 | |
11 | #undef DEBUG |
12 | |
13 | #include <linux/aperture.h> |
14 | #include <linux/fb.h> |
15 | #include <linux/delay.h> |
16 | #include <linux/uaccess.h> |
17 | #include <linux/module.h> |
18 | #include <linux/init.h> |
19 | #include <linux/interrupt.h> |
20 | #include <linux/pci.h> |
21 | #include <linux/of.h> |
22 | #include <linux/of_address.h> |
23 | #include <linux/of_irq.h> |
24 | #include <linux/platform_device.h> |
25 | |
26 | #include "mb862xxfb.h" |
27 | #include "mb862xx_reg.h" |
28 | |
29 | #define NR_PALETTE 256 |
30 | #define MB862XX_MEM_SIZE 0x1000000 |
31 | #define CORALP_MEM_SIZE 0x2000000 |
32 | #define CARMINE_MEM_SIZE 0x8000000 |
33 | #define DRV_NAME "mb862xxfb" |
34 | |
35 | /* Helpers */ |
36 | static inline int h_total(struct fb_var_screeninfo *var) |
37 | { |
38 | return var->xres + var->left_margin + |
39 | var->right_margin + var->hsync_len; |
40 | } |
41 | |
42 | static inline int v_total(struct fb_var_screeninfo *var) |
43 | { |
44 | return var->yres + var->upper_margin + |
45 | var->lower_margin + var->vsync_len; |
46 | } |
47 | |
48 | static inline int hsp(struct fb_var_screeninfo *var) |
49 | { |
50 | return var->xres + var->right_margin - 1; |
51 | } |
52 | |
53 | static inline int vsp(struct fb_var_screeninfo *var) |
54 | { |
55 | return var->yres + var->lower_margin - 1; |
56 | } |
57 | |
58 | static inline int d_pitch(struct fb_var_screeninfo *var) |
59 | { |
60 | return var->xres * var->bits_per_pixel / 8; |
61 | } |
62 | |
63 | static inline unsigned int chan_to_field(unsigned int chan, |
64 | struct fb_bitfield *bf) |
65 | { |
66 | chan &= 0xffff; |
67 | chan >>= 16 - bf->length; |
68 | return chan << bf->offset; |
69 | } |
70 | |
71 | static int mb862xxfb_setcolreg(unsigned regno, |
72 | unsigned red, unsigned green, unsigned blue, |
73 | unsigned transp, struct fb_info *info) |
74 | { |
75 | struct mb862xxfb_par *par = info->par; |
76 | unsigned int val; |
77 | |
78 | switch (info->fix.visual) { |
79 | case FB_VISUAL_TRUECOLOR: |
80 | if (regno < 16) { |
81 | val = chan_to_field(chan: red, bf: &info->var.red); |
82 | val |= chan_to_field(chan: green, bf: &info->var.green); |
83 | val |= chan_to_field(chan: blue, bf: &info->var.blue); |
84 | par->pseudo_palette[regno] = val; |
85 | } |
86 | break; |
87 | case FB_VISUAL_PSEUDOCOLOR: |
88 | if (regno < 256) { |
89 | val = (red >> 8) << 16; |
90 | val |= (green >> 8) << 8; |
91 | val |= blue >> 8; |
92 | outreg(disp, GC_L0PAL0 + (regno * 4), val); |
93 | } |
94 | break; |
95 | default: |
96 | return 1; /* unsupported type */ |
97 | } |
98 | return 0; |
99 | } |
100 | |
101 | static int mb862xxfb_check_var(struct fb_var_screeninfo *var, |
102 | struct fb_info *fbi) |
103 | { |
104 | unsigned long tmp; |
105 | |
106 | fb_dbg(fbi, "%s\n" , __func__); |
107 | |
108 | /* check if these values fit into the registers */ |
109 | if (var->hsync_len > 255 || var->vsync_len > 255) |
110 | return -EINVAL; |
111 | |
112 | if ((var->xres + var->right_margin) >= 4096) |
113 | return -EINVAL; |
114 | |
115 | if ((var->yres + var->lower_margin) > 4096) |
116 | return -EINVAL; |
117 | |
118 | if (h_total(var) > 4096 || v_total(var) > 4096) |
119 | return -EINVAL; |
120 | |
121 | if (var->xres_virtual > 4096 || var->yres_virtual > 4096) |
122 | return -EINVAL; |
123 | |
124 | if (var->bits_per_pixel <= 8) |
125 | var->bits_per_pixel = 8; |
126 | else if (var->bits_per_pixel <= 16) |
127 | var->bits_per_pixel = 16; |
128 | else if (var->bits_per_pixel <= 32) |
129 | var->bits_per_pixel = 32; |
130 | |
131 | /* |
132 | * can cope with 8,16 or 24/32bpp if resulting |
133 | * pitch is divisible by 64 without remainder |
134 | */ |
135 | if (d_pitch(var: &fbi->var) % GC_L0M_L0W_UNIT) { |
136 | int r; |
137 | |
138 | var->bits_per_pixel = 0; |
139 | do { |
140 | var->bits_per_pixel += 8; |
141 | r = d_pitch(var: &fbi->var) % GC_L0M_L0W_UNIT; |
142 | } while (r && var->bits_per_pixel <= 32); |
143 | |
144 | if (d_pitch(var: &fbi->var) % GC_L0M_L0W_UNIT) |
145 | return -EINVAL; |
146 | } |
147 | |
148 | /* line length is going to be 128 bit aligned */ |
149 | tmp = (var->xres * var->bits_per_pixel) / 8; |
150 | if ((tmp & 15) != 0) |
151 | return -EINVAL; |
152 | |
153 | /* set r/g/b positions and validate bpp */ |
154 | switch (var->bits_per_pixel) { |
155 | case 8: |
156 | var->red.length = var->bits_per_pixel; |
157 | var->green.length = var->bits_per_pixel; |
158 | var->blue.length = var->bits_per_pixel; |
159 | var->red.offset = 0; |
160 | var->green.offset = 0; |
161 | var->blue.offset = 0; |
162 | var->transp.length = 0; |
163 | break; |
164 | case 16: |
165 | var->red.length = 5; |
166 | var->green.length = 5; |
167 | var->blue.length = 5; |
168 | var->red.offset = 10; |
169 | var->green.offset = 5; |
170 | var->blue.offset = 0; |
171 | var->transp.length = 0; |
172 | break; |
173 | case 24: |
174 | case 32: |
175 | var->transp.length = 8; |
176 | var->red.length = 8; |
177 | var->green.length = 8; |
178 | var->blue.length = 8; |
179 | var->transp.offset = 24; |
180 | var->red.offset = 16; |
181 | var->green.offset = 8; |
182 | var->blue.offset = 0; |
183 | break; |
184 | default: |
185 | return -EINVAL; |
186 | } |
187 | return 0; |
188 | } |
189 | |
190 | static struct fb_ops mb862xxfb_ops; |
191 | |
192 | /* |
193 | * set display parameters |
194 | */ |
195 | static int mb862xxfb_set_par(struct fb_info *fbi) |
196 | { |
197 | struct mb862xxfb_par *par = fbi->par; |
198 | unsigned long reg, sc; |
199 | |
200 | dev_dbg(par->dev, "%s\n" , __func__); |
201 | if (par->type == BT_CORALP) |
202 | mb862xxfb_init_accel(info: fbi, fbops: &mb862xxfb_ops, xres: fbi->var.xres); |
203 | |
204 | if (par->pre_init) |
205 | return 0; |
206 | |
207 | /* disp off */ |
208 | reg = inreg(disp, GC_DCM1); |
209 | reg &= ~GC_DCM01_DEN; |
210 | outreg(disp, GC_DCM1, reg); |
211 | |
212 | /* set display reference clock div. */ |
213 | sc = par->refclk / (1000000 / fbi->var.pixclock) - 1; |
214 | reg = inreg(disp, GC_DCM1); |
215 | reg &= ~(GC_DCM01_CKS | GC_DCM01_RESV | GC_DCM01_SC); |
216 | reg |= sc << 8; |
217 | outreg(disp, GC_DCM1, reg); |
218 | dev_dbg(par->dev, "SC 0x%lx\n" , sc); |
219 | |
220 | /* disp dimension, format */ |
221 | reg = pack(d_pitch(&fbi->var) / GC_L0M_L0W_UNIT, |
222 | (fbi->var.yres - 1)); |
223 | if (fbi->var.bits_per_pixel == 16) |
224 | reg |= GC_L0M_L0C_16; |
225 | outreg(disp, GC_L0M, reg); |
226 | |
227 | if (fbi->var.bits_per_pixel == 32) { |
228 | reg = inreg(disp, GC_L0EM); |
229 | outreg(disp, GC_L0EM, reg | GC_L0EM_L0EC_24); |
230 | } |
231 | outreg(disp, GC_WY_WX, 0); |
232 | reg = pack(fbi->var.yres - 1, fbi->var.xres); |
233 | outreg(disp, GC_WH_WW, reg); |
234 | outreg(disp, GC_L0OA0, 0); |
235 | outreg(disp, GC_L0DA0, 0); |
236 | outreg(disp, GC_L0DY_L0DX, 0); |
237 | outreg(disp, GC_L0WY_L0WX, 0); |
238 | outreg(disp, GC_L0WH_L0WW, reg); |
239 | |
240 | /* both HW-cursors off */ |
241 | reg = inreg(disp, GC_CPM_CUTC); |
242 | reg &= ~(GC_CPM_CEN0 | GC_CPM_CEN1); |
243 | outreg(disp, GC_CPM_CUTC, reg); |
244 | |
245 | /* timings */ |
246 | reg = pack(fbi->var.xres - 1, fbi->var.xres - 1); |
247 | outreg(disp, GC_HDB_HDP, reg); |
248 | reg = pack((fbi->var.yres - 1), vsp(&fbi->var)); |
249 | outreg(disp, GC_VDP_VSP, reg); |
250 | reg = ((fbi->var.vsync_len - 1) << 24) | |
251 | pack((fbi->var.hsync_len - 1), hsp(&fbi->var)); |
252 | outreg(disp, GC_VSW_HSW_HSP, reg); |
253 | outreg(disp, GC_HTP, pack(h_total(&fbi->var) - 1, 0)); |
254 | outreg(disp, GC_VTR, pack(v_total(&fbi->var) - 1, 0)); |
255 | |
256 | /* display on */ |
257 | reg = inreg(disp, GC_DCM1); |
258 | reg |= GC_DCM01_DEN | GC_DCM01_L0E; |
259 | reg &= ~GC_DCM01_ESY; |
260 | outreg(disp, GC_DCM1, reg); |
261 | return 0; |
262 | } |
263 | |
264 | static int mb862xxfb_pan(struct fb_var_screeninfo *var, |
265 | struct fb_info *info) |
266 | { |
267 | struct mb862xxfb_par *par = info->par; |
268 | unsigned long reg; |
269 | |
270 | reg = pack(var->yoffset, var->xoffset); |
271 | outreg(disp, GC_L0WY_L0WX, reg); |
272 | |
273 | reg = pack(info->var.yres_virtual, info->var.xres_virtual); |
274 | outreg(disp, GC_L0WH_L0WW, reg); |
275 | return 0; |
276 | } |
277 | |
278 | static int mb862xxfb_blank(int mode, struct fb_info *fbi) |
279 | { |
280 | struct mb862xxfb_par *par = fbi->par; |
281 | unsigned long reg; |
282 | |
283 | fb_dbg(fbi, "blank mode=%d\n" , mode); |
284 | |
285 | switch (mode) { |
286 | case FB_BLANK_POWERDOWN: |
287 | reg = inreg(disp, GC_DCM1); |
288 | reg &= ~GC_DCM01_DEN; |
289 | outreg(disp, GC_DCM1, reg); |
290 | break; |
291 | case FB_BLANK_UNBLANK: |
292 | reg = inreg(disp, GC_DCM1); |
293 | reg |= GC_DCM01_DEN; |
294 | outreg(disp, GC_DCM1, reg); |
295 | break; |
296 | case FB_BLANK_NORMAL: |
297 | case FB_BLANK_VSYNC_SUSPEND: |
298 | case FB_BLANK_HSYNC_SUSPEND: |
299 | default: |
300 | return 1; |
301 | } |
302 | return 0; |
303 | } |
304 | |
305 | static int mb862xxfb_ioctl(struct fb_info *fbi, unsigned int cmd, |
306 | unsigned long arg) |
307 | { |
308 | struct mb862xxfb_par *par = fbi->par; |
309 | struct mb862xx_l1_cfg *l1_cfg = &par->l1_cfg; |
310 | void __user *argp = (void __user *)arg; |
311 | int *enable; |
312 | u32 l1em = 0; |
313 | |
314 | switch (cmd) { |
315 | case MB862XX_L1_GET_CFG: |
316 | if (copy_to_user(to: argp, from: l1_cfg, n: sizeof(*l1_cfg))) |
317 | return -EFAULT; |
318 | break; |
319 | case MB862XX_L1_SET_CFG: |
320 | if (copy_from_user(to: l1_cfg, from: argp, n: sizeof(*l1_cfg))) |
321 | return -EFAULT; |
322 | if (l1_cfg->dh == 0 || l1_cfg->dw == 0) |
323 | return -EINVAL; |
324 | if ((l1_cfg->sw >= l1_cfg->dw) && (l1_cfg->sh >= l1_cfg->dh)) { |
325 | /* downscaling */ |
326 | outreg(cap, GC_CAP_CSC, |
327 | pack((l1_cfg->sh << 11) / l1_cfg->dh, |
328 | (l1_cfg->sw << 11) / l1_cfg->dw)); |
329 | l1em = inreg(disp, GC_L1EM); |
330 | l1em &= ~GC_L1EM_DM; |
331 | } else if ((l1_cfg->sw <= l1_cfg->dw) && |
332 | (l1_cfg->sh <= l1_cfg->dh)) { |
333 | /* upscaling */ |
334 | outreg(cap, GC_CAP_CSC, |
335 | pack((l1_cfg->sh << 11) / l1_cfg->dh, |
336 | (l1_cfg->sw << 11) / l1_cfg->dw)); |
337 | outreg(cap, GC_CAP_CMSS, |
338 | pack(l1_cfg->sw >> 1, l1_cfg->sh)); |
339 | outreg(cap, GC_CAP_CMDS, |
340 | pack(l1_cfg->dw >> 1, l1_cfg->dh)); |
341 | l1em = inreg(disp, GC_L1EM); |
342 | l1em |= GC_L1EM_DM; |
343 | } |
344 | |
345 | if (l1_cfg->mirror) { |
346 | outreg(cap, GC_CAP_CBM, |
347 | inreg(cap, GC_CAP_CBM) | GC_CBM_HRV); |
348 | l1em |= l1_cfg->dw * 2 - 8; |
349 | } else { |
350 | outreg(cap, GC_CAP_CBM, |
351 | inreg(cap, GC_CAP_CBM) & ~GC_CBM_HRV); |
352 | l1em &= 0xffff0000; |
353 | } |
354 | outreg(disp, GC_L1EM, l1em); |
355 | break; |
356 | case MB862XX_L1_ENABLE: |
357 | enable = (int *)arg; |
358 | if (*enable) { |
359 | outreg(disp, GC_L1DA, par->cap_buf); |
360 | outreg(cap, GC_CAP_IMG_START, |
361 | pack(l1_cfg->sy >> 1, l1_cfg->sx)); |
362 | outreg(cap, GC_CAP_IMG_END, |
363 | pack(l1_cfg->sh, l1_cfg->sw)); |
364 | outreg(disp, GC_L1M, GC_L1M_16 | GC_L1M_YC | GC_L1M_CS | |
365 | (par->l1_stride << 16)); |
366 | outreg(disp, GC_L1WY_L1WX, |
367 | pack(l1_cfg->dy, l1_cfg->dx)); |
368 | outreg(disp, GC_L1WH_L1WW, |
369 | pack(l1_cfg->dh - 1, l1_cfg->dw)); |
370 | outreg(disp, GC_DLS, 1); |
371 | outreg(cap, GC_CAP_VCM, |
372 | GC_VCM_VIE | GC_VCM_CM | GC_VCM_VS_PAL); |
373 | outreg(disp, GC_DCM1, inreg(disp, GC_DCM1) | |
374 | GC_DCM1_DEN | GC_DCM1_L1E); |
375 | } else { |
376 | outreg(cap, GC_CAP_VCM, |
377 | inreg(cap, GC_CAP_VCM) & ~GC_VCM_VIE); |
378 | outreg(disp, GC_DCM1, |
379 | inreg(disp, GC_DCM1) & ~GC_DCM1_L1E); |
380 | } |
381 | break; |
382 | case MB862XX_L1_CAP_CTL: |
383 | enable = (int *)arg; |
384 | if (*enable) { |
385 | outreg(cap, GC_CAP_VCM, |
386 | inreg(cap, GC_CAP_VCM) | GC_VCM_VIE); |
387 | } else { |
388 | outreg(cap, GC_CAP_VCM, |
389 | inreg(cap, GC_CAP_VCM) & ~GC_VCM_VIE); |
390 | } |
391 | break; |
392 | default: |
393 | return -EINVAL; |
394 | } |
395 | return 0; |
396 | } |
397 | |
398 | /* framebuffer ops */ |
399 | static struct fb_ops mb862xxfb_ops = { |
400 | .owner = THIS_MODULE, |
401 | FB_DEFAULT_IOMEM_OPS, |
402 | .fb_check_var = mb862xxfb_check_var, |
403 | .fb_set_par = mb862xxfb_set_par, |
404 | .fb_setcolreg = mb862xxfb_setcolreg, |
405 | .fb_blank = mb862xxfb_blank, |
406 | .fb_pan_display = mb862xxfb_pan, |
407 | .fb_ioctl = mb862xxfb_ioctl, |
408 | }; |
409 | |
410 | /* initialize fb_info data */ |
411 | static int mb862xxfb_init_fbinfo(struct fb_info *fbi) |
412 | { |
413 | struct mb862xxfb_par *par = fbi->par; |
414 | struct mb862xx_gc_mode *mode = par->gc_mode; |
415 | unsigned long reg; |
416 | int stride; |
417 | |
418 | fbi->fbops = &mb862xxfb_ops; |
419 | fbi->pseudo_palette = par->pseudo_palette; |
420 | fbi->screen_base = par->fb_base; |
421 | fbi->screen_size = par->mapped_vram; |
422 | |
423 | strcpy(p: fbi->fix.id, DRV_NAME); |
424 | fbi->fix.smem_start = (unsigned long)par->fb_base_phys; |
425 | fbi->fix.mmio_start = (unsigned long)par->mmio_base_phys; |
426 | fbi->fix.mmio_len = par->mmio_len; |
427 | fbi->fix.accel = FB_ACCEL_NONE; |
428 | fbi->fix.type = FB_TYPE_PACKED_PIXELS; |
429 | fbi->fix.type_aux = 0; |
430 | fbi->fix.xpanstep = 1; |
431 | fbi->fix.ypanstep = 1; |
432 | fbi->fix.ywrapstep = 0; |
433 | |
434 | reg = inreg(disp, GC_DCM1); |
435 | if (reg & GC_DCM01_DEN && reg & GC_DCM01_L0E) { |
436 | /* get the disp mode from active display cfg */ |
437 | unsigned long sc = ((reg & GC_DCM01_SC) >> 8) + 1; |
438 | unsigned long hsp, vsp, ht, vt; |
439 | |
440 | dev_dbg(par->dev, "using bootloader's disp. mode\n" ); |
441 | fbi->var.pixclock = (sc * 1000000) / par->refclk; |
442 | fbi->var.xres = (inreg(disp, GC_HDB_HDP) & 0x0fff) + 1; |
443 | reg = inreg(disp, GC_VDP_VSP); |
444 | fbi->var.yres = ((reg >> 16) & 0x0fff) + 1; |
445 | vsp = (reg & 0x0fff) + 1; |
446 | fbi->var.xres_virtual = fbi->var.xres; |
447 | fbi->var.yres_virtual = fbi->var.yres; |
448 | reg = inreg(disp, GC_L0EM); |
449 | if (reg & GC_L0EM_L0EC_24) { |
450 | fbi->var.bits_per_pixel = 32; |
451 | } else { |
452 | reg = inreg(disp, GC_L0M); |
453 | if (reg & GC_L0M_L0C_16) |
454 | fbi->var.bits_per_pixel = 16; |
455 | else |
456 | fbi->var.bits_per_pixel = 8; |
457 | } |
458 | reg = inreg(disp, GC_VSW_HSW_HSP); |
459 | fbi->var.hsync_len = ((reg & 0xff0000) >> 16) + 1; |
460 | fbi->var.vsync_len = ((reg & 0x3f000000) >> 24) + 1; |
461 | hsp = (reg & 0xffff) + 1; |
462 | ht = ((inreg(disp, GC_HTP) & 0xfff0000) >> 16) + 1; |
463 | fbi->var.right_margin = hsp - fbi->var.xres; |
464 | fbi->var.left_margin = ht - hsp - fbi->var.hsync_len; |
465 | vt = ((inreg(disp, GC_VTR) & 0xfff0000) >> 16) + 1; |
466 | fbi->var.lower_margin = vsp - fbi->var.yres; |
467 | fbi->var.upper_margin = vt - vsp - fbi->var.vsync_len; |
468 | } else if (mode) { |
469 | dev_dbg(par->dev, "using supplied mode\n" ); |
470 | fb_videomode_to_var(var: &fbi->var, mode: (struct fb_videomode *)mode); |
471 | fbi->var.bits_per_pixel = mode->def_bpp ? mode->def_bpp : 8; |
472 | } else { |
473 | int ret; |
474 | |
475 | ret = fb_find_mode(var: &fbi->var, info: fbi, mode_option: "640x480-16@60" , |
476 | NULL, dbsize: 0, NULL, default_bpp: 16); |
477 | if (ret == 0 || ret == 4) { |
478 | dev_err(par->dev, |
479 | "failed to get initial mode\n" ); |
480 | return -EINVAL; |
481 | } |
482 | } |
483 | |
484 | fbi->var.xoffset = 0; |
485 | fbi->var.yoffset = 0; |
486 | fbi->var.grayscale = 0; |
487 | fbi->var.nonstd = 0; |
488 | fbi->var.height = -1; |
489 | fbi->var.width = -1; |
490 | fbi->var.accel_flags = 0; |
491 | fbi->var.vmode = FB_VMODE_NONINTERLACED; |
492 | fbi->var.activate = FB_ACTIVATE_NOW; |
493 | fbi->flags = |
494 | #ifdef __BIG_ENDIAN |
495 | FBINFO_FOREIGN_ENDIAN | |
496 | #endif |
497 | FBINFO_HWACCEL_XPAN | |
498 | FBINFO_HWACCEL_YPAN; |
499 | |
500 | /* check and possibly fix bpp */ |
501 | if ((fbi->fbops->fb_check_var)(&fbi->var, fbi)) |
502 | dev_err(par->dev, "check_var() failed on initial setup?\n" ); |
503 | |
504 | fbi->fix.visual = fbi->var.bits_per_pixel == 8 ? |
505 | FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; |
506 | fbi->fix.line_length = (fbi->var.xres_virtual * |
507 | fbi->var.bits_per_pixel) / 8; |
508 | fbi->fix.smem_len = fbi->fix.line_length * fbi->var.yres_virtual; |
509 | |
510 | /* |
511 | * reserve space for capture buffers and two cursors |
512 | * at the end of vram: 720x576 * 2 * 2.2 + 64x64 * 16. |
513 | */ |
514 | par->cap_buf = par->mapped_vram - 0x1bd800 - 0x10000; |
515 | par->cap_len = 0x1bd800; |
516 | par->l1_cfg.sx = 0; |
517 | par->l1_cfg.sy = 0; |
518 | par->l1_cfg.sw = 720; |
519 | par->l1_cfg.sh = 576; |
520 | par->l1_cfg.dx = 0; |
521 | par->l1_cfg.dy = 0; |
522 | par->l1_cfg.dw = 720; |
523 | par->l1_cfg.dh = 576; |
524 | stride = par->l1_cfg.sw * (fbi->var.bits_per_pixel / 8); |
525 | par->l1_stride = stride / 64 + ((stride % 64) ? 1 : 0); |
526 | outreg(cap, GC_CAP_CBM, GC_CBM_OO | GC_CBM_CBST | |
527 | (par->l1_stride << 16)); |
528 | outreg(cap, GC_CAP_CBOA, par->cap_buf); |
529 | outreg(cap, GC_CAP_CBLA, par->cap_buf + par->cap_len); |
530 | return 0; |
531 | } |
532 | |
533 | /* |
534 | * show some display controller and cursor registers |
535 | */ |
536 | static ssize_t dispregs_show(struct device *dev, |
537 | struct device_attribute *attr, char *buf) |
538 | { |
539 | struct fb_info *fbi = dev_get_drvdata(dev); |
540 | struct mb862xxfb_par *par = fbi->par; |
541 | char *ptr = buf; |
542 | unsigned int reg; |
543 | |
544 | for (reg = GC_DCM0; reg <= GC_L0DY_L0DX; reg += 4) |
545 | ptr += sprintf(buf: ptr, fmt: "%08x = %08x\n" , |
546 | reg, inreg(disp, reg)); |
547 | |
548 | for (reg = GC_CPM_CUTC; reg <= GC_CUY1_CUX1; reg += 4) |
549 | ptr += sprintf(buf: ptr, fmt: "%08x = %08x\n" , |
550 | reg, inreg(disp, reg)); |
551 | |
552 | for (reg = GC_DCM1; reg <= GC_L0WH_L0WW; reg += 4) |
553 | ptr += sprintf(buf: ptr, fmt: "%08x = %08x\n" , |
554 | reg, inreg(disp, reg)); |
555 | |
556 | for (reg = 0x400; reg <= 0x410; reg += 4) |
557 | ptr += sprintf(buf: ptr, fmt: "geo %08x = %08x\n" , |
558 | reg, inreg(geo, reg)); |
559 | |
560 | for (reg = 0x400; reg <= 0x410; reg += 4) |
561 | ptr += sprintf(buf: ptr, fmt: "draw %08x = %08x\n" , |
562 | reg, inreg(draw, reg)); |
563 | |
564 | for (reg = 0x440; reg <= 0x450; reg += 4) |
565 | ptr += sprintf(buf: ptr, fmt: "draw %08x = %08x\n" , |
566 | reg, inreg(draw, reg)); |
567 | |
568 | return ptr - buf; |
569 | } |
570 | |
571 | static DEVICE_ATTR_RO(dispregs); |
572 | |
573 | static irqreturn_t mb862xx_intr(int irq, void *dev_id) |
574 | { |
575 | struct mb862xxfb_par *par = (struct mb862xxfb_par *) dev_id; |
576 | unsigned long reg_ist, mask; |
577 | |
578 | if (!par) |
579 | return IRQ_NONE; |
580 | |
581 | if (par->type == BT_CARMINE) { |
582 | /* Get Interrupt Status */ |
583 | reg_ist = inreg(ctrl, GC_CTRL_STATUS); |
584 | mask = inreg(ctrl, GC_CTRL_INT_MASK); |
585 | if (reg_ist == 0) |
586 | return IRQ_HANDLED; |
587 | |
588 | reg_ist &= mask; |
589 | if (reg_ist == 0) |
590 | return IRQ_HANDLED; |
591 | |
592 | /* Clear interrupt status */ |
593 | outreg(ctrl, 0x0, reg_ist); |
594 | } else { |
595 | /* Get status */ |
596 | reg_ist = inreg(host, GC_IST); |
597 | mask = inreg(host, GC_IMASK); |
598 | |
599 | reg_ist &= mask; |
600 | if (reg_ist == 0) |
601 | return IRQ_HANDLED; |
602 | |
603 | /* Clear status */ |
604 | outreg(host, GC_IST, ~reg_ist); |
605 | } |
606 | return IRQ_HANDLED; |
607 | } |
608 | |
609 | #if defined(CONFIG_FB_MB862XX_LIME) |
610 | /* |
611 | * GDC (Lime, Coral(B/Q), Mint, ...) on host bus |
612 | */ |
613 | static int mb862xx_gdc_init(struct mb862xxfb_par *par) |
614 | { |
615 | unsigned long ccf, mmr; |
616 | unsigned long ver, rev; |
617 | |
618 | if (!par) |
619 | return -ENODEV; |
620 | |
621 | #if defined(CONFIG_FB_PRE_INIT_FB) |
622 | par->pre_init = 1; |
623 | #endif |
624 | par->host = par->mmio_base; |
625 | par->i2c = par->mmio_base + MB862XX_I2C_BASE; |
626 | par->disp = par->mmio_base + MB862XX_DISP_BASE; |
627 | par->cap = par->mmio_base + MB862XX_CAP_BASE; |
628 | par->draw = par->mmio_base + MB862XX_DRAW_BASE; |
629 | par->geo = par->mmio_base + MB862XX_GEO_BASE; |
630 | par->pio = par->mmio_base + MB862XX_PIO_BASE; |
631 | |
632 | par->refclk = GC_DISP_REFCLK_400; |
633 | |
634 | ver = inreg(host, GC_CID); |
635 | rev = inreg(pio, GC_REVISION); |
636 | if ((ver == 0x303) && (rev & 0xffffff00) == 0x20050100) { |
637 | dev_info(par->dev, "Fujitsu Lime v1.%d found\n" , |
638 | (int)rev & 0xff); |
639 | par->type = BT_LIME; |
640 | ccf = par->gc_mode ? par->gc_mode->ccf : GC_CCF_COT_100; |
641 | mmr = par->gc_mode ? par->gc_mode->mmr : 0x414fb7f2; |
642 | } else { |
643 | dev_info(par->dev, "? GDC, CID/Rev.: 0x%lx/0x%lx \n" , ver, rev); |
644 | return -ENODEV; |
645 | } |
646 | |
647 | if (!par->pre_init) { |
648 | outreg(host, GC_CCF, ccf); |
649 | udelay(200); |
650 | outreg(host, GC_MMR, mmr); |
651 | udelay(10); |
652 | } |
653 | |
654 | /* interrupt status */ |
655 | outreg(host, GC_IST, 0); |
656 | outreg(host, GC_IMASK, GC_INT_EN); |
657 | return 0; |
658 | } |
659 | |
660 | #if defined(CONFIG_SOCRATES) |
661 | static struct mb862xx_gc_mode socrates_gc_mode = { |
662 | /* Mode for Prime View PM070WL4 TFT LCD Panel */ |
663 | { "800x480" , 45, 800, 480, 40000, 86, 42, 33, 10, 128, 2, 0, 0, 0 }, |
664 | /* 16 bits/pixel, 16MB, 133MHz, SDRAM memory mode value */ |
665 | 16, 0x1000000, GC_CCF_COT_133, 0x4157ba63 |
666 | }; |
667 | #endif |
668 | |
669 | static int of_platform_mb862xx_probe(struct platform_device *ofdev) |
670 | { |
671 | struct device_node *np = ofdev->dev.of_node; |
672 | struct device *dev = &ofdev->dev; |
673 | struct mb862xxfb_par *par; |
674 | struct fb_info *info; |
675 | struct resource res; |
676 | resource_size_t res_size; |
677 | unsigned long ret = -ENODEV; |
678 | |
679 | if (of_address_to_resource(np, 0, &res)) { |
680 | dev_err(dev, "Invalid address\n" ); |
681 | return -ENXIO; |
682 | } |
683 | |
684 | info = framebuffer_alloc(sizeof(struct mb862xxfb_par), dev); |
685 | if (!info) |
686 | return -ENOMEM; |
687 | |
688 | par = info->par; |
689 | par->info = info; |
690 | par->dev = dev; |
691 | |
692 | par->irq = irq_of_parse_and_map(np, 0); |
693 | if (!par->irq) { |
694 | dev_err(dev, "failed to map irq\n" ); |
695 | ret = -ENODEV; |
696 | goto fbrel; |
697 | } |
698 | |
699 | res_size = resource_size(&res); |
700 | par->res = request_mem_region(res.start, res_size, DRV_NAME); |
701 | if (par->res == NULL) { |
702 | dev_err(dev, "Cannot claim framebuffer/mmio\n" ); |
703 | ret = -ENXIO; |
704 | goto irqdisp; |
705 | } |
706 | |
707 | #if defined(CONFIG_SOCRATES) |
708 | par->gc_mode = &socrates_gc_mode; |
709 | #endif |
710 | |
711 | par->fb_base_phys = res.start; |
712 | par->mmio_base_phys = res.start + MB862XX_MMIO_BASE; |
713 | par->mmio_len = MB862XX_MMIO_SIZE; |
714 | if (par->gc_mode) |
715 | par->mapped_vram = par->gc_mode->max_vram; |
716 | else |
717 | par->mapped_vram = MB862XX_MEM_SIZE; |
718 | |
719 | par->fb_base = ioremap(par->fb_base_phys, par->mapped_vram); |
720 | if (par->fb_base == NULL) { |
721 | dev_err(dev, "Cannot map framebuffer\n" ); |
722 | goto rel_reg; |
723 | } |
724 | |
725 | par->mmio_base = ioremap(par->mmio_base_phys, par->mmio_len); |
726 | if (par->mmio_base == NULL) { |
727 | dev_err(dev, "Cannot map registers\n" ); |
728 | goto fb_unmap; |
729 | } |
730 | |
731 | dev_dbg(dev, "fb phys 0x%llx 0x%lx\n" , |
732 | (u64)par->fb_base_phys, (ulong)par->mapped_vram); |
733 | dev_dbg(dev, "mmio phys 0x%llx 0x%lx, (irq = %d)\n" , |
734 | (u64)par->mmio_base_phys, (ulong)par->mmio_len, par->irq); |
735 | |
736 | if (mb862xx_gdc_init(par)) |
737 | goto io_unmap; |
738 | |
739 | if (request_irq(par->irq, mb862xx_intr, 0, |
740 | DRV_NAME, (void *)par)) { |
741 | dev_err(dev, "Cannot request irq\n" ); |
742 | goto io_unmap; |
743 | } |
744 | |
745 | mb862xxfb_init_fbinfo(info); |
746 | |
747 | if (fb_alloc_cmap(&info->cmap, NR_PALETTE, 0) < 0) { |
748 | dev_err(dev, "Could not allocate cmap for fb_info.\n" ); |
749 | goto free_irq; |
750 | } |
751 | |
752 | if ((info->fbops->fb_set_par)(info)) |
753 | dev_err(dev, "set_var() failed on initial setup?\n" ); |
754 | |
755 | if (register_framebuffer(info)) { |
756 | dev_err(dev, "failed to register framebuffer\n" ); |
757 | goto rel_cmap; |
758 | } |
759 | |
760 | dev_set_drvdata(dev, info); |
761 | |
762 | if (device_create_file(dev, &dev_attr_dispregs)) |
763 | dev_err(dev, "Can't create sysfs regdump file\n" ); |
764 | return 0; |
765 | |
766 | rel_cmap: |
767 | fb_dealloc_cmap(&info->cmap); |
768 | free_irq: |
769 | outreg(host, GC_IMASK, 0); |
770 | free_irq(par->irq, (void *)par); |
771 | io_unmap: |
772 | iounmap(par->mmio_base); |
773 | fb_unmap: |
774 | iounmap(par->fb_base); |
775 | rel_reg: |
776 | release_mem_region(res.start, res_size); |
777 | irqdisp: |
778 | irq_dispose_mapping(par->irq); |
779 | fbrel: |
780 | framebuffer_release(info); |
781 | return ret; |
782 | } |
783 | |
784 | static void of_platform_mb862xx_remove(struct platform_device *ofdev) |
785 | { |
786 | struct fb_info *fbi = dev_get_drvdata(&ofdev->dev); |
787 | struct mb862xxfb_par *par = fbi->par; |
788 | resource_size_t res_size = resource_size(par->res); |
789 | unsigned long reg; |
790 | |
791 | fb_dbg(fbi, "%s release\n" , fbi->fix.id); |
792 | |
793 | /* display off */ |
794 | reg = inreg(disp, GC_DCM1); |
795 | reg &= ~(GC_DCM01_DEN | GC_DCM01_L0E); |
796 | outreg(disp, GC_DCM1, reg); |
797 | |
798 | /* disable interrupts */ |
799 | outreg(host, GC_IMASK, 0); |
800 | |
801 | free_irq(par->irq, (void *)par); |
802 | irq_dispose_mapping(par->irq); |
803 | |
804 | device_remove_file(&ofdev->dev, &dev_attr_dispregs); |
805 | |
806 | unregister_framebuffer(fbi); |
807 | fb_dealloc_cmap(&fbi->cmap); |
808 | |
809 | iounmap(par->mmio_base); |
810 | iounmap(par->fb_base); |
811 | |
812 | release_mem_region(par->res->start, res_size); |
813 | framebuffer_release(fbi); |
814 | } |
815 | |
816 | /* |
817 | * common types |
818 | */ |
819 | static struct of_device_id of_platform_mb862xx_tbl[] = { |
820 | { .compatible = "fujitsu,MB86276" , }, |
821 | { .compatible = "fujitsu,lime" , }, |
822 | { .compatible = "fujitsu,MB86277" , }, |
823 | { .compatible = "fujitsu,mint" , }, |
824 | { .compatible = "fujitsu,MB86293" , }, |
825 | { .compatible = "fujitsu,MB86294" , }, |
826 | { .compatible = "fujitsu,coral" , }, |
827 | { /* end */ } |
828 | }; |
829 | MODULE_DEVICE_TABLE(of, of_platform_mb862xx_tbl); |
830 | |
831 | static struct platform_driver of_platform_mb862xxfb_driver = { |
832 | .driver = { |
833 | .name = DRV_NAME, |
834 | .of_match_table = of_platform_mb862xx_tbl, |
835 | }, |
836 | .probe = of_platform_mb862xx_probe, |
837 | .remove_new = of_platform_mb862xx_remove, |
838 | }; |
839 | #endif |
840 | |
841 | #if defined(CONFIG_FB_MB862XX_PCI_GDC) |
842 | static int coralp_init(struct mb862xxfb_par *par) |
843 | { |
844 | int cn, ver; |
845 | |
846 | par->host = par->mmio_base; |
847 | par->i2c = par->mmio_base + MB862XX_I2C_BASE; |
848 | par->disp = par->mmio_base + MB862XX_DISP_BASE; |
849 | par->cap = par->mmio_base + MB862XX_CAP_BASE; |
850 | par->draw = par->mmio_base + MB862XX_DRAW_BASE; |
851 | par->geo = par->mmio_base + MB862XX_GEO_BASE; |
852 | par->pio = par->mmio_base + MB862XX_PIO_BASE; |
853 | |
854 | par->refclk = GC_DISP_REFCLK_400; |
855 | |
856 | if (par->mapped_vram >= 0x2000000) { |
857 | /* relocate gdc registers space */ |
858 | writel(val: 1, addr: par->fb_base + MB862XX_MMIO_BASE + GC_RSW); |
859 | udelay(1); /* wait at least 20 bus cycles */ |
860 | } |
861 | |
862 | ver = inreg(host, GC_CID); |
863 | cn = (ver & GC_CID_CNAME_MSK) >> 8; |
864 | ver = ver & GC_CID_VERSION_MSK; |
865 | if (cn == 3) { |
866 | unsigned long reg; |
867 | |
868 | dev_info(par->dev, "Fujitsu Coral-%s GDC Rev.%d found\n" ,\ |
869 | (ver == 6) ? "P" : (ver == 8) ? "PA" : "?" , |
870 | par->pdev->revision); |
871 | reg = inreg(disp, GC_DCM1); |
872 | if (reg & GC_DCM01_DEN && reg & GC_DCM01_L0E) |
873 | par->pre_init = 1; |
874 | |
875 | if (!par->pre_init) { |
876 | outreg(host, GC_CCF, GC_CCF_CGE_166 | GC_CCF_COT_133); |
877 | udelay(200); |
878 | outreg(host, GC_MMR, GC_MMR_CORALP_EVB_VAL); |
879 | udelay(10); |
880 | } |
881 | /* Clear interrupt status */ |
882 | outreg(host, GC_IST, 0); |
883 | } else { |
884 | return -ENODEV; |
885 | } |
886 | |
887 | mb862xx_i2c_init(par); |
888 | return 0; |
889 | } |
890 | |
891 | static int init_dram_ctrl(struct mb862xxfb_par *par) |
892 | { |
893 | unsigned long i = 0; |
894 | |
895 | /* |
896 | * Set io mode first! Spec. says IC may be destroyed |
897 | * if not set to SSTL2/LVCMOS before init. |
898 | */ |
899 | outreg(dram_ctrl, GC_DCTL_IOCONT1_IOCONT0, GC_EVB_DCTL_IOCONT1_IOCONT0); |
900 | |
901 | /* DRAM init */ |
902 | outreg(dram_ctrl, GC_DCTL_MODE_ADD, GC_EVB_DCTL_MODE_ADD); |
903 | outreg(dram_ctrl, GC_DCTL_SETTIME1_EMODE, GC_EVB_DCTL_SETTIME1_EMODE); |
904 | outreg(dram_ctrl, GC_DCTL_REFRESH_SETTIME2, |
905 | GC_EVB_DCTL_REFRESH_SETTIME2); |
906 | outreg(dram_ctrl, GC_DCTL_RSV2_RSV1, GC_EVB_DCTL_RSV2_RSV1); |
907 | outreg(dram_ctrl, GC_DCTL_DDRIF2_DDRIF1, GC_EVB_DCTL_DDRIF2_DDRIF1); |
908 | outreg(dram_ctrl, GC_DCTL_RSV0_STATES, GC_EVB_DCTL_RSV0_STATES); |
909 | |
910 | /* DLL reset done? */ |
911 | while ((inreg(dram_ctrl, GC_DCTL_RSV0_STATES) & GC_DCTL_STATES_MSK)) { |
912 | udelay(GC_DCTL_INIT_WAIT_INTERVAL); |
913 | if (i++ > GC_DCTL_INIT_WAIT_CNT) { |
914 | dev_err(par->dev, "VRAM init failed.\n" ); |
915 | return -EINVAL; |
916 | } |
917 | } |
918 | outreg(dram_ctrl, GC_DCTL_MODE_ADD, GC_EVB_DCTL_MODE_ADD_AFT_RST); |
919 | outreg(dram_ctrl, GC_DCTL_RSV0_STATES, GC_EVB_DCTL_RSV0_STATES_AFT_RST); |
920 | return 0; |
921 | } |
922 | |
923 | static int carmine_init(struct mb862xxfb_par *par) |
924 | { |
925 | unsigned long reg; |
926 | |
927 | par->ctrl = par->mmio_base + MB86297_CTRL_BASE; |
928 | par->i2c = par->mmio_base + MB86297_I2C_BASE; |
929 | par->disp = par->mmio_base + MB86297_DISP0_BASE; |
930 | par->disp1 = par->mmio_base + MB86297_DISP1_BASE; |
931 | par->cap = par->mmio_base + MB86297_CAP0_BASE; |
932 | par->cap1 = par->mmio_base + MB86297_CAP1_BASE; |
933 | par->draw = par->mmio_base + MB86297_DRAW_BASE; |
934 | par->dram_ctrl = par->mmio_base + MB86297_DRAMCTRL_BASE; |
935 | par->wrback = par->mmio_base + MB86297_WRBACK_BASE; |
936 | |
937 | par->refclk = GC_DISP_REFCLK_533; |
938 | |
939 | /* warm up */ |
940 | reg = GC_CTRL_CLK_EN_DRAM | GC_CTRL_CLK_EN_2D3D | GC_CTRL_CLK_EN_DISP0; |
941 | outreg(ctrl, GC_CTRL_CLK_ENABLE, reg); |
942 | |
943 | /* check for engine module revision */ |
944 | if (inreg(draw, GC_2D3D_REV) == GC_RE_REVISION) |
945 | dev_info(par->dev, "Fujitsu Carmine GDC Rev.%d found\n" , |
946 | par->pdev->revision); |
947 | else |
948 | goto err_init; |
949 | |
950 | reg &= ~GC_CTRL_CLK_EN_2D3D; |
951 | outreg(ctrl, GC_CTRL_CLK_ENABLE, reg); |
952 | |
953 | /* set up vram */ |
954 | if (init_dram_ctrl(par) < 0) |
955 | goto err_init; |
956 | |
957 | outreg(ctrl, GC_CTRL_INT_MASK, 0); |
958 | return 0; |
959 | |
960 | err_init: |
961 | outreg(ctrl, GC_CTRL_CLK_ENABLE, 0); |
962 | return -EINVAL; |
963 | } |
964 | |
965 | static inline int mb862xx_pci_gdc_init(struct mb862xxfb_par *par) |
966 | { |
967 | switch (par->type) { |
968 | case BT_CORALP: |
969 | return coralp_init(par); |
970 | case BT_CARMINE: |
971 | return carmine_init(par); |
972 | default: |
973 | return -ENODEV; |
974 | } |
975 | } |
976 | |
977 | #define CHIP_ID(id) \ |
978 | { PCI_DEVICE(PCI_VENDOR_ID_FUJITSU_LIMITED, id) } |
979 | |
980 | static const struct pci_device_id mb862xx_pci_tbl[] = { |
981 | /* MB86295/MB86296 */ |
982 | CHIP_ID(PCI_DEVICE_ID_FUJITSU_CORALP), |
983 | CHIP_ID(PCI_DEVICE_ID_FUJITSU_CORALPA), |
984 | /* MB86297 */ |
985 | CHIP_ID(PCI_DEVICE_ID_FUJITSU_CARMINE), |
986 | { 0, } |
987 | }; |
988 | |
989 | MODULE_DEVICE_TABLE(pci, mb862xx_pci_tbl); |
990 | |
991 | static int mb862xx_pci_probe(struct pci_dev *pdev, |
992 | const struct pci_device_id *ent) |
993 | { |
994 | struct mb862xxfb_par *par; |
995 | struct fb_info *info; |
996 | struct device *dev = &pdev->dev; |
997 | int ret; |
998 | |
999 | ret = aperture_remove_conflicting_pci_devices(pdev, name: "mb862xxfb" ); |
1000 | if (ret) |
1001 | return ret; |
1002 | |
1003 | ret = pci_enable_device(dev: pdev); |
1004 | if (ret < 0) { |
1005 | dev_err(dev, "Cannot enable PCI device\n" ); |
1006 | goto out; |
1007 | } |
1008 | |
1009 | info = framebuffer_alloc(size: sizeof(struct mb862xxfb_par), dev); |
1010 | if (!info) { |
1011 | ret = -ENOMEM; |
1012 | goto dis_dev; |
1013 | } |
1014 | |
1015 | par = info->par; |
1016 | par->info = info; |
1017 | par->dev = dev; |
1018 | par->pdev = pdev; |
1019 | par->irq = pdev->irq; |
1020 | |
1021 | ret = pci_request_regions(pdev, DRV_NAME); |
1022 | if (ret < 0) { |
1023 | dev_err(dev, "Cannot reserve region(s) for PCI device\n" ); |
1024 | goto rel_fb; |
1025 | } |
1026 | |
1027 | switch (pdev->device) { |
1028 | case PCI_DEVICE_ID_FUJITSU_CORALP: |
1029 | case PCI_DEVICE_ID_FUJITSU_CORALPA: |
1030 | par->fb_base_phys = pci_resource_start(par->pdev, 0); |
1031 | par->mapped_vram = CORALP_MEM_SIZE; |
1032 | if (par->mapped_vram >= 0x2000000) { |
1033 | par->mmio_base_phys = par->fb_base_phys + |
1034 | MB862XX_MMIO_HIGH_BASE; |
1035 | } else { |
1036 | par->mmio_base_phys = par->fb_base_phys + |
1037 | MB862XX_MMIO_BASE; |
1038 | } |
1039 | par->mmio_len = MB862XX_MMIO_SIZE; |
1040 | par->type = BT_CORALP; |
1041 | break; |
1042 | case PCI_DEVICE_ID_FUJITSU_CARMINE: |
1043 | par->fb_base_phys = pci_resource_start(par->pdev, 2); |
1044 | par->mmio_base_phys = pci_resource_start(par->pdev, 3); |
1045 | par->mmio_len = pci_resource_len(par->pdev, 3); |
1046 | par->mapped_vram = CARMINE_MEM_SIZE; |
1047 | par->type = BT_CARMINE; |
1048 | break; |
1049 | default: |
1050 | /* should never occur */ |
1051 | ret = -EIO; |
1052 | goto rel_reg; |
1053 | } |
1054 | |
1055 | par->fb_base = ioremap(offset: par->fb_base_phys, size: par->mapped_vram); |
1056 | if (par->fb_base == NULL) { |
1057 | dev_err(dev, "Cannot map framebuffer\n" ); |
1058 | ret = -EIO; |
1059 | goto rel_reg; |
1060 | } |
1061 | |
1062 | par->mmio_base = ioremap(offset: par->mmio_base_phys, size: par->mmio_len); |
1063 | if (par->mmio_base == NULL) { |
1064 | dev_err(dev, "Cannot map registers\n" ); |
1065 | ret = -EIO; |
1066 | goto fb_unmap; |
1067 | } |
1068 | |
1069 | dev_dbg(dev, "fb phys 0x%llx 0x%lx\n" , |
1070 | (unsigned long long)par->fb_base_phys, (ulong)par->mapped_vram); |
1071 | dev_dbg(dev, "mmio phys 0x%llx 0x%lx\n" , |
1072 | (unsigned long long)par->mmio_base_phys, (ulong)par->mmio_len); |
1073 | |
1074 | ret = mb862xx_pci_gdc_init(par); |
1075 | if (ret) |
1076 | goto io_unmap; |
1077 | |
1078 | ret = request_irq(irq: par->irq, handler: mb862xx_intr, IRQF_SHARED, |
1079 | DRV_NAME, dev: (void *)par); |
1080 | if (ret) { |
1081 | dev_err(dev, "Cannot request irq\n" ); |
1082 | goto io_unmap; |
1083 | } |
1084 | |
1085 | mb862xxfb_init_fbinfo(fbi: info); |
1086 | |
1087 | if (fb_alloc_cmap(cmap: &info->cmap, NR_PALETTE, transp: 0) < 0) { |
1088 | dev_err(dev, "Could not allocate cmap for fb_info.\n" ); |
1089 | ret = -ENOMEM; |
1090 | goto free_irq; |
1091 | } |
1092 | |
1093 | if ((info->fbops->fb_set_par)(info)) |
1094 | dev_err(dev, "set_var() failed on initial setup?\n" ); |
1095 | |
1096 | ret = register_framebuffer(fb_info: info); |
1097 | if (ret < 0) { |
1098 | dev_err(dev, "failed to register framebuffer\n" ); |
1099 | goto rel_cmap; |
1100 | } |
1101 | |
1102 | pci_set_drvdata(pdev, data: info); |
1103 | |
1104 | if (device_create_file(device: dev, entry: &dev_attr_dispregs)) |
1105 | dev_err(dev, "Can't create sysfs regdump file\n" ); |
1106 | |
1107 | if (par->type == BT_CARMINE) |
1108 | outreg(ctrl, GC_CTRL_INT_MASK, GC_CARMINE_INT_EN); |
1109 | else |
1110 | outreg(host, GC_IMASK, GC_INT_EN); |
1111 | |
1112 | return 0; |
1113 | |
1114 | rel_cmap: |
1115 | fb_dealloc_cmap(cmap: &info->cmap); |
1116 | free_irq: |
1117 | free_irq(par->irq, (void *)par); |
1118 | io_unmap: |
1119 | iounmap(addr: par->mmio_base); |
1120 | fb_unmap: |
1121 | iounmap(addr: par->fb_base); |
1122 | rel_reg: |
1123 | pci_release_regions(pdev); |
1124 | rel_fb: |
1125 | framebuffer_release(info); |
1126 | dis_dev: |
1127 | pci_disable_device(dev: pdev); |
1128 | out: |
1129 | return ret; |
1130 | } |
1131 | |
1132 | static void mb862xx_pci_remove(struct pci_dev *pdev) |
1133 | { |
1134 | struct fb_info *fbi = pci_get_drvdata(pdev); |
1135 | struct mb862xxfb_par *par = fbi->par; |
1136 | unsigned long reg; |
1137 | |
1138 | fb_dbg(fbi, "%s release\n" , fbi->fix.id); |
1139 | |
1140 | /* display off */ |
1141 | reg = inreg(disp, GC_DCM1); |
1142 | reg &= ~(GC_DCM01_DEN | GC_DCM01_L0E); |
1143 | outreg(disp, GC_DCM1, reg); |
1144 | |
1145 | if (par->type == BT_CARMINE) { |
1146 | outreg(ctrl, GC_CTRL_INT_MASK, 0); |
1147 | outreg(ctrl, GC_CTRL_CLK_ENABLE, 0); |
1148 | } else { |
1149 | outreg(host, GC_IMASK, 0); |
1150 | } |
1151 | |
1152 | mb862xx_i2c_exit(par); |
1153 | |
1154 | device_remove_file(dev: &pdev->dev, attr: &dev_attr_dispregs); |
1155 | |
1156 | unregister_framebuffer(fb_info: fbi); |
1157 | fb_dealloc_cmap(cmap: &fbi->cmap); |
1158 | |
1159 | free_irq(par->irq, (void *)par); |
1160 | iounmap(addr: par->mmio_base); |
1161 | iounmap(addr: par->fb_base); |
1162 | |
1163 | pci_release_regions(pdev); |
1164 | framebuffer_release(info: fbi); |
1165 | pci_disable_device(dev: pdev); |
1166 | } |
1167 | |
1168 | static struct pci_driver mb862xxfb_pci_driver = { |
1169 | .name = DRV_NAME, |
1170 | .id_table = mb862xx_pci_tbl, |
1171 | .probe = mb862xx_pci_probe, |
1172 | .remove = mb862xx_pci_remove, |
1173 | }; |
1174 | #endif |
1175 | |
1176 | static int mb862xxfb_init(void) |
1177 | { |
1178 | int ret = -ENODEV; |
1179 | |
1180 | if (fb_modesetting_disabled(DRV_NAME)) |
1181 | return -ENODEV; |
1182 | |
1183 | #if defined(CONFIG_FB_MB862XX_LIME) |
1184 | ret = platform_driver_register(&of_platform_mb862xxfb_driver); |
1185 | #endif |
1186 | #if defined(CONFIG_FB_MB862XX_PCI_GDC) |
1187 | ret = pci_register_driver(&mb862xxfb_pci_driver); |
1188 | #endif |
1189 | return ret; |
1190 | } |
1191 | |
1192 | static void __exit mb862xxfb_exit(void) |
1193 | { |
1194 | #if defined(CONFIG_FB_MB862XX_LIME) |
1195 | platform_driver_unregister(&of_platform_mb862xxfb_driver); |
1196 | #endif |
1197 | #if defined(CONFIG_FB_MB862XX_PCI_GDC) |
1198 | pci_unregister_driver(dev: &mb862xxfb_pci_driver); |
1199 | #endif |
1200 | } |
1201 | |
1202 | module_init(mb862xxfb_init); |
1203 | module_exit(mb862xxfb_exit); |
1204 | |
1205 | MODULE_DESCRIPTION("Fujitsu MB862xx Framebuffer driver" ); |
1206 | MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>" ); |
1207 | MODULE_LICENSE("GPL v2" ); |
1208 | |