1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* leo.c: LEO frame buffer driver |
3 | * |
4 | * Copyright (C) 2003, 2006 David S. Miller (davem@davemloft.net) |
5 | * Copyright (C) 1996-1999 Jakub Jelinek (jj@ultra.linux.cz) |
6 | * Copyright (C) 1997 Michal Rehacek (Michal.Rehacek@st.mff.cuni.cz) |
7 | * |
8 | * Driver layout based loosely on tgafb.c, see that file for credits. |
9 | */ |
10 | |
11 | #include <linux/module.h> |
12 | #include <linux/kernel.h> |
13 | #include <linux/errno.h> |
14 | #include <linux/string.h> |
15 | #include <linux/delay.h> |
16 | #include <linux/init.h> |
17 | #include <linux/fb.h> |
18 | #include <linux/mm.h> |
19 | #include <linux/io.h> |
20 | #include <linux/of.h> |
21 | #include <linux/platform_device.h> |
22 | |
23 | #include <asm/fbio.h> |
24 | |
25 | #include "sbuslib.h" |
26 | |
27 | /* |
28 | * Local functions. |
29 | */ |
30 | |
31 | static int leo_setcolreg(unsigned, unsigned, unsigned, unsigned, |
32 | unsigned, struct fb_info *); |
33 | static int leo_blank(int, struct fb_info *); |
34 | static int leo_pan_display(struct fb_var_screeninfo *, struct fb_info *); |
35 | |
36 | static int leo_sbusfb_mmap(struct fb_info *info, struct vm_area_struct *vma); |
37 | static int leo_sbusfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg); |
38 | |
39 | /* |
40 | * Frame buffer operations |
41 | */ |
42 | |
43 | static const struct fb_ops leo_ops = { |
44 | .owner = THIS_MODULE, |
45 | FB_DEFAULT_SBUS_OPS(leo), |
46 | .fb_setcolreg = leo_setcolreg, |
47 | .fb_blank = leo_blank, |
48 | .fb_pan_display = leo_pan_display, |
49 | }; |
50 | |
51 | #define LEO_OFF_LC_SS0_KRN 0x00200000UL |
52 | #define LEO_OFF_LC_SS0_USR 0x00201000UL |
53 | #define LEO_OFF_LC_SS1_KRN 0x01200000UL |
54 | #define LEO_OFF_LC_SS1_USR 0x01201000UL |
55 | #define LEO_OFF_LD_SS0 0x00400000UL |
56 | #define LEO_OFF_LD_SS1 0x01400000UL |
57 | #define LEO_OFF_LD_GBL 0x00401000UL |
58 | #define LEO_OFF_LX_KRN 0x00600000UL |
59 | #define LEO_OFF_LX_CURSOR 0x00601000UL |
60 | #define LEO_OFF_SS0 0x00800000UL |
61 | #define LEO_OFF_SS1 0x01800000UL |
62 | #define LEO_OFF_UNK 0x00602000UL |
63 | #define LEO_OFF_UNK2 0x00000000UL |
64 | |
65 | #define LEO_CUR_ENABLE 0x00000080 |
66 | #define LEO_CUR_UPDATE 0x00000030 |
67 | #define LEO_CUR_PROGRESS 0x00000006 |
68 | #define LEO_CUR_UPDATECMAP 0x00000003 |
69 | |
70 | #define LEO_CUR_TYPE_MASK 0x00000000 |
71 | #define LEO_CUR_TYPE_IMAGE 0x00000020 |
72 | #define LEO_CUR_TYPE_CMAP 0x00000050 |
73 | |
74 | struct leo_cursor { |
75 | u8 xxx0[16]; |
76 | u32 cur_type; |
77 | u32 cur_misc; |
78 | u32 cur_cursxy; |
79 | u32 cur_data; |
80 | }; |
81 | |
82 | #define LEO_KRN_TYPE_CLUT0 0x00001000 |
83 | #define LEO_KRN_TYPE_CLUT1 0x00001001 |
84 | #define LEO_KRN_TYPE_CLUT2 0x00001002 |
85 | #define LEO_KRN_TYPE_WID 0x00001003 |
86 | #define LEO_KRN_TYPE_UNK 0x00001006 |
87 | #define LEO_KRN_TYPE_VIDEO 0x00002003 |
88 | #define LEO_KRN_TYPE_CLUTDATA 0x00004000 |
89 | #define LEO_KRN_CSR_ENABLE 0x00000008 |
90 | #define LEO_KRN_CSR_PROGRESS 0x00000004 |
91 | #define LEO_KRN_CSR_UNK 0x00000002 |
92 | #define LEO_KRN_CSR_UNK2 0x00000001 |
93 | |
94 | struct leo_lx_krn { |
95 | u32 krn_type; |
96 | u32 krn_csr; |
97 | u32 krn_value; |
98 | }; |
99 | |
100 | struct leo_lc_ss0_krn { |
101 | u32 misc; |
102 | u8 xxx0[0x800-4]; |
103 | u32 rev; |
104 | }; |
105 | |
106 | struct leo_lc_ss0_usr { |
107 | u32 csr; |
108 | u32 addrspace; |
109 | u32 fontmsk; |
110 | u32 fontt; |
111 | u32 extent; |
112 | u32 src; |
113 | u32 dst; |
114 | u32 copy; |
115 | u32 fill; |
116 | }; |
117 | |
118 | struct leo_lc_ss1_krn { |
119 | u8 unknown; |
120 | }; |
121 | |
122 | struct leo_lc_ss1_usr { |
123 | u8 unknown; |
124 | }; |
125 | |
126 | struct leo_ld_ss0 { |
127 | u8 xxx0[0xe00]; |
128 | u32 csr; |
129 | u32 wid; |
130 | u32 wmask; |
131 | u32 widclip; |
132 | u32 vclipmin; |
133 | u32 vclipmax; |
134 | u32 pickmin; /* SS1 only */ |
135 | u32 pickmax; /* SS1 only */ |
136 | u32 fg; |
137 | u32 bg; |
138 | u32 src; /* Copy/Scroll (SS0 only) */ |
139 | u32 dst; /* Copy/Scroll/Fill (SS0 only) */ |
140 | u32 extent; /* Copy/Scroll/Fill size (SS0 only) */ |
141 | u32 xxx1[3]; |
142 | u32 setsem; /* SS1 only */ |
143 | u32 clrsem; /* SS1 only */ |
144 | u32 clrpick; /* SS1 only */ |
145 | u32 clrdat; /* SS1 only */ |
146 | u32 alpha; /* SS1 only */ |
147 | u8 xxx2[0x2c]; |
148 | u32 winbg; |
149 | u32 planemask; |
150 | u32 rop; |
151 | u32 z; |
152 | u32 dczf; /* SS1 only */ |
153 | u32 dczb; /* SS1 only */ |
154 | u32 dcs; /* SS1 only */ |
155 | u32 dczs; /* SS1 only */ |
156 | u32 pickfb; /* SS1 only */ |
157 | u32 pickbb; /* SS1 only */ |
158 | u32 dcfc; /* SS1 only */ |
159 | u32 forcecol; /* SS1 only */ |
160 | u32 door[8]; /* SS1 only */ |
161 | u32 pick[5]; /* SS1 only */ |
162 | }; |
163 | |
164 | #define LEO_SS1_MISC_ENABLE 0x00000001 |
165 | #define LEO_SS1_MISC_STEREO 0x00000002 |
166 | struct leo_ld_ss1 { |
167 | u8 xxx0[0xef4]; |
168 | u32 ss1_misc; |
169 | }; |
170 | |
171 | struct leo_ld_gbl { |
172 | u8 unknown; |
173 | }; |
174 | |
175 | struct leo_par { |
176 | spinlock_t lock; |
177 | struct leo_lx_krn __iomem *lx_krn; |
178 | struct leo_lc_ss0_usr __iomem *lc_ss0_usr; |
179 | struct leo_ld_ss0 __iomem *ld_ss0; |
180 | struct leo_ld_ss1 __iomem *ld_ss1; |
181 | struct leo_cursor __iomem *cursor; |
182 | u32 extent; |
183 | u32 clut_data[256]; |
184 | |
185 | u32 flags; |
186 | #define LEO_FLAG_BLANKED 0x00000001 |
187 | |
188 | unsigned long which_io; |
189 | }; |
190 | |
191 | static void leo_wait(struct leo_lx_krn __iomem *lx_krn) |
192 | { |
193 | int i; |
194 | |
195 | for (i = 0; |
196 | (sbus_readl(&lx_krn->krn_csr) & LEO_KRN_CSR_PROGRESS) && |
197 | i < 300000; |
198 | i++) |
199 | udelay(1); /* Busy wait at most 0.3 sec */ |
200 | return; |
201 | } |
202 | |
203 | static void leo_switch_from_graph(struct fb_info *info) |
204 | { |
205 | struct leo_par *par = (struct leo_par *) info->par; |
206 | struct leo_ld_ss0 __iomem *ss = par->ld_ss0; |
207 | struct leo_cursor __iomem *cursor = par->cursor; |
208 | unsigned long flags; |
209 | u32 val; |
210 | |
211 | spin_lock_irqsave(&par->lock, flags); |
212 | |
213 | par->extent = ((info->var.xres - 1) | |
214 | ((info->var.yres - 1) << 16)); |
215 | |
216 | sbus_writel(0xffffffff, &ss->wid); |
217 | sbus_writel(0xffff, &ss->wmask); |
218 | sbus_writel(0, &ss->vclipmin); |
219 | sbus_writel(par->extent, &ss->vclipmax); |
220 | sbus_writel(0, &ss->fg); |
221 | sbus_writel(0xff000000, &ss->planemask); |
222 | sbus_writel(0x310850, &ss->rop); |
223 | sbus_writel(0, &ss->widclip); |
224 | sbus_writel((info->var.xres-1) | ((info->var.yres-1) << 11), |
225 | &par->lc_ss0_usr->extent); |
226 | sbus_writel(4, &par->lc_ss0_usr->addrspace); |
227 | sbus_writel(0x80000000, &par->lc_ss0_usr->fill); |
228 | sbus_writel(0, &par->lc_ss0_usr->fontt); |
229 | do { |
230 | val = sbus_readl(&par->lc_ss0_usr->csr); |
231 | } while (val & 0x20000000); |
232 | |
233 | /* setup screen buffer for cfb_* functions */ |
234 | sbus_writel(1, &ss->wid); |
235 | sbus_writel(0x00ffffff, &ss->planemask); |
236 | sbus_writel(0x310b90, &ss->rop); |
237 | sbus_writel(0, &par->lc_ss0_usr->addrspace); |
238 | |
239 | /* hide cursor */ |
240 | sbus_writel(sbus_readl(&cursor->cur_misc) & ~LEO_CUR_ENABLE, &cursor->cur_misc); |
241 | |
242 | spin_unlock_irqrestore(lock: &par->lock, flags); |
243 | } |
244 | |
245 | static int leo_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) |
246 | { |
247 | /* We just use this to catch switches out of |
248 | * graphics mode. |
249 | */ |
250 | leo_switch_from_graph(info); |
251 | |
252 | if (var->xoffset || var->yoffset || var->vmode) |
253 | return -EINVAL; |
254 | return 0; |
255 | } |
256 | |
257 | /** |
258 | * leo_setcolreg - Optional function. Sets a color register. |
259 | * @regno: boolean, 0 copy local, 1 get_user() function |
260 | * @red: frame buffer colormap structure |
261 | * @green: The green value which can be up to 16 bits wide |
262 | * @blue: The blue value which can be up to 16 bits wide. |
263 | * @transp: If supported the alpha value which can be up to 16 bits wide. |
264 | * @info: frame buffer info structure |
265 | */ |
266 | static int leo_setcolreg(unsigned regno, |
267 | unsigned red, unsigned green, unsigned blue, |
268 | unsigned transp, struct fb_info *info) |
269 | { |
270 | struct leo_par *par = (struct leo_par *) info->par; |
271 | struct leo_lx_krn __iomem *lx_krn = par->lx_krn; |
272 | unsigned long flags; |
273 | u32 val; |
274 | int i; |
275 | |
276 | if (regno >= 256) |
277 | return 1; |
278 | |
279 | red >>= 8; |
280 | green >>= 8; |
281 | blue >>= 8; |
282 | |
283 | par->clut_data[regno] = red | (green << 8) | (blue << 16); |
284 | |
285 | spin_lock_irqsave(&par->lock, flags); |
286 | |
287 | leo_wait(lx_krn); |
288 | |
289 | sbus_writel(LEO_KRN_TYPE_CLUTDATA, &lx_krn->krn_type); |
290 | for (i = 0; i < 256; i++) |
291 | sbus_writel(par->clut_data[i], &lx_krn->krn_value); |
292 | sbus_writel(LEO_KRN_TYPE_CLUT0, &lx_krn->krn_type); |
293 | |
294 | val = sbus_readl(&lx_krn->krn_csr); |
295 | val |= (LEO_KRN_CSR_UNK | LEO_KRN_CSR_UNK2); |
296 | sbus_writel(val, &lx_krn->krn_csr); |
297 | |
298 | spin_unlock_irqrestore(lock: &par->lock, flags); |
299 | |
300 | return 0; |
301 | } |
302 | |
303 | /** |
304 | * leo_blank - Optional function. Blanks the display. |
305 | * @blank: the blank mode we want. |
306 | * @info: frame buffer structure that represents a single frame buffer |
307 | */ |
308 | static int leo_blank(int blank, struct fb_info *info) |
309 | { |
310 | struct leo_par *par = (struct leo_par *) info->par; |
311 | struct leo_lx_krn __iomem *lx_krn = par->lx_krn; |
312 | unsigned long flags; |
313 | u32 val; |
314 | |
315 | spin_lock_irqsave(&par->lock, flags); |
316 | |
317 | switch (blank) { |
318 | case FB_BLANK_UNBLANK: /* Unblanking */ |
319 | val = sbus_readl(&lx_krn->krn_csr); |
320 | val |= LEO_KRN_CSR_ENABLE; |
321 | sbus_writel(val, &lx_krn->krn_csr); |
322 | par->flags &= ~LEO_FLAG_BLANKED; |
323 | break; |
324 | |
325 | case FB_BLANK_NORMAL: /* Normal blanking */ |
326 | case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */ |
327 | case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */ |
328 | case FB_BLANK_POWERDOWN: /* Poweroff */ |
329 | val = sbus_readl(&lx_krn->krn_csr); |
330 | val &= ~LEO_KRN_CSR_ENABLE; |
331 | sbus_writel(val, &lx_krn->krn_csr); |
332 | par->flags |= LEO_FLAG_BLANKED; |
333 | break; |
334 | } |
335 | |
336 | spin_unlock_irqrestore(lock: &par->lock, flags); |
337 | |
338 | return 0; |
339 | } |
340 | |
341 | static struct sbus_mmap_map leo_mmap_map[] = { |
342 | { |
343 | .voff = LEO_SS0_MAP, |
344 | .poff = LEO_OFF_SS0, |
345 | .size = 0x800000 |
346 | }, |
347 | { |
348 | .voff = LEO_LC_SS0_USR_MAP, |
349 | .poff = LEO_OFF_LC_SS0_USR, |
350 | .size = 0x1000 |
351 | }, |
352 | { |
353 | .voff = LEO_LD_SS0_MAP, |
354 | .poff = LEO_OFF_LD_SS0, |
355 | .size = 0x1000 |
356 | }, |
357 | { |
358 | .voff = LEO_LX_CURSOR_MAP, |
359 | .poff = LEO_OFF_LX_CURSOR, |
360 | .size = 0x1000 |
361 | }, |
362 | { |
363 | .voff = LEO_SS1_MAP, |
364 | .poff = LEO_OFF_SS1, |
365 | .size = 0x800000 |
366 | }, |
367 | { |
368 | .voff = LEO_LC_SS1_USR_MAP, |
369 | .poff = LEO_OFF_LC_SS1_USR, |
370 | .size = 0x1000 |
371 | }, |
372 | { |
373 | .voff = LEO_LD_SS1_MAP, |
374 | .poff = LEO_OFF_LD_SS1, |
375 | .size = 0x1000 |
376 | }, |
377 | { |
378 | .voff = LEO_UNK_MAP, |
379 | .poff = LEO_OFF_UNK, |
380 | .size = 0x1000 |
381 | }, |
382 | { |
383 | .voff = LEO_LX_KRN_MAP, |
384 | .poff = LEO_OFF_LX_KRN, |
385 | .size = 0x1000 |
386 | }, |
387 | { |
388 | .voff = LEO_LC_SS0_KRN_MAP, |
389 | .poff = LEO_OFF_LC_SS0_KRN, |
390 | .size = 0x1000 |
391 | }, |
392 | { |
393 | .voff = LEO_LC_SS1_KRN_MAP, |
394 | .poff = LEO_OFF_LC_SS1_KRN, |
395 | .size = 0x1000 |
396 | }, |
397 | { |
398 | .voff = LEO_LD_GBL_MAP, |
399 | .poff = LEO_OFF_LD_GBL, |
400 | .size = 0x1000 |
401 | }, |
402 | { |
403 | .voff = LEO_UNK2_MAP, |
404 | .poff = LEO_OFF_UNK2, |
405 | .size = 0x100000 |
406 | }, |
407 | { .size = 0 } |
408 | }; |
409 | |
410 | static int leo_sbusfb_mmap(struct fb_info *info, struct vm_area_struct *vma) |
411 | { |
412 | struct leo_par *par = (struct leo_par *)info->par; |
413 | |
414 | return sbusfb_mmap_helper(map: leo_mmap_map, |
415 | physbase: info->fix.smem_start, fbsize: info->fix.smem_len, |
416 | iospace: par->which_io, vma); |
417 | } |
418 | |
419 | static int leo_sbusfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) |
420 | { |
421 | return sbusfb_ioctl_helper(cmd, arg, info, |
422 | type: FBTYPE_SUNLEO, fb_depth: 32, fb_size: info->fix.smem_len); |
423 | } |
424 | |
425 | /* |
426 | * Initialisation |
427 | */ |
428 | |
429 | static void |
430 | leo_init_fix(struct fb_info *info, struct device_node *dp) |
431 | { |
432 | snprintf(buf: info->fix.id, size: sizeof(info->fix.id), fmt: "%pOFn" , dp); |
433 | |
434 | info->fix.type = FB_TYPE_PACKED_PIXELS; |
435 | info->fix.visual = FB_VISUAL_TRUECOLOR; |
436 | |
437 | info->fix.line_length = 8192; |
438 | |
439 | info->fix.accel = FB_ACCEL_SUN_LEO; |
440 | } |
441 | |
442 | static void leo_wid_put(struct fb_info *info, struct fb_wid_list *wl) |
443 | { |
444 | struct leo_par *par = (struct leo_par *) info->par; |
445 | struct leo_lx_krn __iomem *lx_krn = par->lx_krn; |
446 | struct fb_wid_item *wi; |
447 | unsigned long flags; |
448 | u32 val; |
449 | int i, j; |
450 | |
451 | spin_lock_irqsave(&par->lock, flags); |
452 | |
453 | leo_wait(lx_krn); |
454 | |
455 | for (i = 0, wi = wl->wl_list; i < wl->wl_count; i++, wi++) { |
456 | switch (wi->wi_type) { |
457 | case FB_WID_DBL_8: |
458 | j = (wi->wi_index & 0xf) + 0x40; |
459 | break; |
460 | |
461 | case FB_WID_DBL_24: |
462 | j = wi->wi_index & 0x3f; |
463 | break; |
464 | |
465 | default: |
466 | continue; |
467 | } |
468 | sbus_writel(0x5800 + j, &lx_krn->krn_type); |
469 | sbus_writel(wi->wi_values[0], &lx_krn->krn_value); |
470 | } |
471 | sbus_writel(LEO_KRN_TYPE_WID, &lx_krn->krn_type); |
472 | |
473 | val = sbus_readl(&lx_krn->krn_csr); |
474 | val |= (LEO_KRN_CSR_UNK | LEO_KRN_CSR_UNK2); |
475 | sbus_writel(val, &lx_krn->krn_csr); |
476 | |
477 | spin_unlock_irqrestore(lock: &par->lock, flags); |
478 | } |
479 | |
480 | static void leo_init_wids(struct fb_info *info) |
481 | { |
482 | struct fb_wid_item wi; |
483 | struct fb_wid_list wl; |
484 | |
485 | wl.wl_count = 1; |
486 | wl.wl_list = &wi; |
487 | wi.wi_type = FB_WID_DBL_8; |
488 | wi.wi_index = 0; |
489 | wi.wi_values [0] = 0x2c0; |
490 | leo_wid_put(info, wl: &wl); |
491 | wi.wi_index = 1; |
492 | wi.wi_values [0] = 0x30; |
493 | leo_wid_put(info, wl: &wl); |
494 | wi.wi_index = 2; |
495 | wi.wi_values [0] = 0x20; |
496 | leo_wid_put(info, wl: &wl); |
497 | wi.wi_type = FB_WID_DBL_24; |
498 | wi.wi_index = 1; |
499 | wi.wi_values [0] = 0x30; |
500 | leo_wid_put(info, wl: &wl); |
501 | } |
502 | |
503 | static void leo_init_hw(struct fb_info *info) |
504 | { |
505 | struct leo_par *par = (struct leo_par *) info->par; |
506 | u32 val; |
507 | |
508 | val = sbus_readl(&par->ld_ss1->ss1_misc); |
509 | val |= LEO_SS1_MISC_ENABLE; |
510 | sbus_writel(val, &par->ld_ss1->ss1_misc); |
511 | |
512 | leo_switch_from_graph(info); |
513 | } |
514 | |
515 | static void leo_fixup_var_rgb(struct fb_var_screeninfo *var) |
516 | { |
517 | var->red.offset = 0; |
518 | var->red.length = 8; |
519 | var->green.offset = 8; |
520 | var->green.length = 8; |
521 | var->blue.offset = 16; |
522 | var->blue.length = 8; |
523 | var->transp.offset = 0; |
524 | var->transp.length = 0; |
525 | } |
526 | |
527 | static void leo_unmap_regs(struct platform_device *op, struct fb_info *info, |
528 | struct leo_par *par) |
529 | { |
530 | if (par->lc_ss0_usr) |
531 | of_iounmap(&op->resource[0], par->lc_ss0_usr, 0x1000); |
532 | if (par->ld_ss0) |
533 | of_iounmap(&op->resource[0], par->ld_ss0, 0x1000); |
534 | if (par->ld_ss1) |
535 | of_iounmap(&op->resource[0], par->ld_ss1, 0x1000); |
536 | if (par->lx_krn) |
537 | of_iounmap(&op->resource[0], par->lx_krn, 0x1000); |
538 | if (par->cursor) |
539 | of_iounmap(&op->resource[0], |
540 | par->cursor, sizeof(struct leo_cursor)); |
541 | if (info->screen_base) |
542 | of_iounmap(&op->resource[0], info->screen_base, 0x800000); |
543 | } |
544 | |
545 | static int leo_probe(struct platform_device *op) |
546 | { |
547 | struct device_node *dp = op->dev.of_node; |
548 | struct fb_info *info; |
549 | struct leo_par *par; |
550 | int linebytes, err; |
551 | |
552 | info = framebuffer_alloc(size: sizeof(struct leo_par), dev: &op->dev); |
553 | |
554 | err = -ENOMEM; |
555 | if (!info) |
556 | goto out_err; |
557 | par = info->par; |
558 | |
559 | spin_lock_init(&par->lock); |
560 | |
561 | info->fix.smem_start = op->resource[0].start; |
562 | par->which_io = op->resource[0].flags & IORESOURCE_BITS; |
563 | |
564 | sbusfb_fill_var(var: &info->var, dp, bpp: 32); |
565 | leo_fixup_var_rgb(var: &info->var); |
566 | |
567 | linebytes = of_getintprop_default(dp, "linebytes" , |
568 | info->var.xres); |
569 | info->fix.smem_len = PAGE_ALIGN(linebytes * info->var.yres); |
570 | |
571 | par->lc_ss0_usr = |
572 | of_ioremap(&op->resource[0], LEO_OFF_LC_SS0_USR, |
573 | 0x1000, "leolc ss0usr" ); |
574 | par->ld_ss0 = |
575 | of_ioremap(&op->resource[0], LEO_OFF_LD_SS0, |
576 | 0x1000, "leold ss0" ); |
577 | par->ld_ss1 = |
578 | of_ioremap(&op->resource[0], LEO_OFF_LD_SS1, |
579 | 0x1000, "leold ss1" ); |
580 | par->lx_krn = |
581 | of_ioremap(&op->resource[0], LEO_OFF_LX_KRN, |
582 | 0x1000, "leolx krn" ); |
583 | par->cursor = |
584 | of_ioremap(&op->resource[0], LEO_OFF_LX_CURSOR, |
585 | sizeof(struct leo_cursor), "leolx cursor" ); |
586 | info->screen_base = |
587 | of_ioremap(&op->resource[0], LEO_OFF_SS0, |
588 | 0x800000, "leo ram" ); |
589 | if (!par->lc_ss0_usr || |
590 | !par->ld_ss0 || |
591 | !par->ld_ss1 || |
592 | !par->lx_krn || |
593 | !par->cursor || |
594 | !info->screen_base) |
595 | goto out_unmap_regs; |
596 | |
597 | info->fbops = &leo_ops; |
598 | info->pseudo_palette = par->clut_data; |
599 | |
600 | leo_init_wids(info); |
601 | leo_init_hw(info); |
602 | |
603 | leo_blank(blank: FB_BLANK_UNBLANK, info); |
604 | |
605 | if (fb_alloc_cmap(cmap: &info->cmap, len: 256, transp: 0)) |
606 | goto out_unmap_regs; |
607 | |
608 | leo_init_fix(info, dp); |
609 | |
610 | err = register_framebuffer(fb_info: info); |
611 | if (err < 0) |
612 | goto out_dealloc_cmap; |
613 | |
614 | dev_set_drvdata(dev: &op->dev, data: info); |
615 | |
616 | printk(KERN_INFO "%pOF: leo at %lx:%lx\n" , |
617 | dp, |
618 | par->which_io, info->fix.smem_start); |
619 | |
620 | return 0; |
621 | |
622 | out_dealloc_cmap: |
623 | fb_dealloc_cmap(cmap: &info->cmap); |
624 | |
625 | out_unmap_regs: |
626 | leo_unmap_regs(op, info, par); |
627 | framebuffer_release(info); |
628 | |
629 | out_err: |
630 | return err; |
631 | } |
632 | |
633 | static void leo_remove(struct platform_device *op) |
634 | { |
635 | struct fb_info *info = dev_get_drvdata(dev: &op->dev); |
636 | struct leo_par *par = info->par; |
637 | |
638 | unregister_framebuffer(fb_info: info); |
639 | fb_dealloc_cmap(cmap: &info->cmap); |
640 | |
641 | leo_unmap_regs(op, info, par); |
642 | |
643 | framebuffer_release(info); |
644 | } |
645 | |
646 | static const struct of_device_id leo_match[] = { |
647 | { |
648 | .name = "SUNW,leo" , |
649 | }, |
650 | {}, |
651 | }; |
652 | MODULE_DEVICE_TABLE(of, leo_match); |
653 | |
654 | static struct platform_driver leo_driver = { |
655 | .driver = { |
656 | .name = "leo" , |
657 | .of_match_table = leo_match, |
658 | }, |
659 | .probe = leo_probe, |
660 | .remove_new = leo_remove, |
661 | }; |
662 | |
663 | static int __init leo_init(void) |
664 | { |
665 | if (fb_get_options(name: "leofb" , NULL)) |
666 | return -ENODEV; |
667 | |
668 | return platform_driver_register(&leo_driver); |
669 | } |
670 | |
671 | static void __exit leo_exit(void) |
672 | { |
673 | platform_driver_unregister(&leo_driver); |
674 | } |
675 | |
676 | module_init(leo_init); |
677 | module_exit(leo_exit); |
678 | |
679 | MODULE_DESCRIPTION("framebuffer driver for LEO chipsets" ); |
680 | MODULE_AUTHOR("David S. Miller <davem@davemloft.net>" ); |
681 | MODULE_VERSION("2.0" ); |
682 | MODULE_LICENSE("GPL" ); |
683 | |