1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* cg14.c: CGFOURTEEN frame buffer driver |
3 | * |
4 | * Copyright (C) 2003, 2006 David S. Miller (davem@davemloft.net) |
5 | * Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz) |
6 | * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx) |
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/uaccess.h> |
20 | #include <linux/of.h> |
21 | #include <linux/platform_device.h> |
22 | |
23 | #include <asm/io.h> |
24 | #include <asm/fbio.h> |
25 | |
26 | #include "sbuslib.h" |
27 | |
28 | /* |
29 | * Local functions. |
30 | */ |
31 | |
32 | static int cg14_setcolreg(unsigned, unsigned, unsigned, unsigned, |
33 | unsigned, struct fb_info *); |
34 | static int cg14_pan_display(struct fb_var_screeninfo *, struct fb_info *); |
35 | |
36 | static int cg14_sbusfb_mmap(struct fb_info *info, struct vm_area_struct *vma); |
37 | static int cg14_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 cg14_ops = { |
44 | .owner = THIS_MODULE, |
45 | FB_DEFAULT_SBUS_OPS(cg14), |
46 | .fb_setcolreg = cg14_setcolreg, |
47 | .fb_pan_display = cg14_pan_display, |
48 | }; |
49 | |
50 | #define CG14_MCR_INTENABLE_SHIFT 7 |
51 | #define CG14_MCR_INTENABLE_MASK 0x80 |
52 | #define CG14_MCR_VIDENABLE_SHIFT 6 |
53 | #define CG14_MCR_VIDENABLE_MASK 0x40 |
54 | #define CG14_MCR_PIXMODE_SHIFT 4 |
55 | #define CG14_MCR_PIXMODE_MASK 0x30 |
56 | #define CG14_MCR_TMR_SHIFT 2 |
57 | #define CG14_MCR_TMR_MASK 0x0c |
58 | #define CG14_MCR_TMENABLE_SHIFT 1 |
59 | #define CG14_MCR_TMENABLE_MASK 0x02 |
60 | #define CG14_MCR_RESET_SHIFT 0 |
61 | #define CG14_MCR_RESET_MASK 0x01 |
62 | #define CG14_REV_REVISION_SHIFT 4 |
63 | #define CG14_REV_REVISION_MASK 0xf0 |
64 | #define CG14_REV_IMPL_SHIFT 0 |
65 | #define CG14_REV_IMPL_MASK 0x0f |
66 | #define CG14_VBR_FRAMEBASE_SHIFT 12 |
67 | #define CG14_VBR_FRAMEBASE_MASK 0x00fff000 |
68 | #define CG14_VMCR1_SETUP_SHIFT 0 |
69 | #define CG14_VMCR1_SETUP_MASK 0x000001ff |
70 | #define CG14_VMCR1_VCONFIG_SHIFT 9 |
71 | #define CG14_VMCR1_VCONFIG_MASK 0x00000e00 |
72 | #define CG14_VMCR2_REFRESH_SHIFT 0 |
73 | #define CG14_VMCR2_REFRESH_MASK 0x00000001 |
74 | #define CG14_VMCR2_TESTROWCNT_SHIFT 1 |
75 | #define CG14_VMCR2_TESTROWCNT_MASK 0x00000002 |
76 | #define CG14_VMCR2_FBCONFIG_SHIFT 2 |
77 | #define CG14_VMCR2_FBCONFIG_MASK 0x0000000c |
78 | #define CG14_VCR_REFRESHREQ_SHIFT 0 |
79 | #define CG14_VCR_REFRESHREQ_MASK 0x000003ff |
80 | #define CG14_VCR1_REFRESHENA_SHIFT 10 |
81 | #define CG14_VCR1_REFRESHENA_MASK 0x00000400 |
82 | #define CG14_VCA_CAD_SHIFT 0 |
83 | #define CG14_VCA_CAD_MASK 0x000003ff |
84 | #define CG14_VCA_VERS_SHIFT 10 |
85 | #define CG14_VCA_VERS_MASK 0x00000c00 |
86 | #define CG14_VCA_RAMSPEED_SHIFT 12 |
87 | #define CG14_VCA_RAMSPEED_MASK 0x00001000 |
88 | #define CG14_VCA_8MB_SHIFT 13 |
89 | #define CG14_VCA_8MB_MASK 0x00002000 |
90 | |
91 | #define CG14_MCR_PIXMODE_8 0 |
92 | #define CG14_MCR_PIXMODE_16 2 |
93 | #define CG14_MCR_PIXMODE_32 3 |
94 | |
95 | struct cg14_regs{ |
96 | u8 mcr; /* Master Control Reg */ |
97 | u8 ppr; /* Packed Pixel Reg */ |
98 | u8 tms[2]; /* Test Mode Status Regs */ |
99 | u8 msr; /* Master Status Reg */ |
100 | u8 fsr; /* Fault Status Reg */ |
101 | u8 rev; /* Revision & Impl */ |
102 | u8 ccr; /* Clock Control Reg */ |
103 | u32 tmr; /* Test Mode Read Back */ |
104 | u8 mod; /* Monitor Operation Data Reg */ |
105 | u8 acr; /* Aux Control */ |
106 | u8 xxx0[6]; |
107 | u16 hct; /* Hor Counter */ |
108 | u16 vct; /* Vert Counter */ |
109 | u16 hbs; /* Hor Blank Start */ |
110 | u16 hbc; /* Hor Blank Clear */ |
111 | u16 hss; /* Hor Sync Start */ |
112 | u16 hsc; /* Hor Sync Clear */ |
113 | u16 csc; /* Composite Sync Clear */ |
114 | u16 vbs; /* Vert Blank Start */ |
115 | u16 vbc; /* Vert Blank Clear */ |
116 | u16 vss; /* Vert Sync Start */ |
117 | u16 vsc; /* Vert Sync Clear */ |
118 | u16 xcs; |
119 | u16 xcc; |
120 | u16 fsa; /* Fault Status Address */ |
121 | u16 adr; /* Address Registers */ |
122 | u8 xxx1[0xce]; |
123 | u8 pcg[0x100]; /* Pixel Clock Generator */ |
124 | u32 vbr; /* Frame Base Row */ |
125 | u32 vmcr; /* VBC Master Control */ |
126 | u32 vcr; /* VBC refresh */ |
127 | u32 vca; /* VBC Config */ |
128 | }; |
129 | |
130 | #define CG14_CCR_ENABLE 0x04 |
131 | #define CG14_CCR_SELECT 0x02 /* HW/Full screen */ |
132 | |
133 | struct cg14_cursor { |
134 | u32 cpl0[32]; /* Enable plane 0 */ |
135 | u32 cpl1[32]; /* Color selection plane */ |
136 | u8 ccr; /* Cursor Control Reg */ |
137 | u8 xxx0[3]; |
138 | u16 cursx; /* Cursor x,y position */ |
139 | u16 cursy; /* Cursor x,y position */ |
140 | u32 color0; |
141 | u32 color1; |
142 | u32 xxx1[0x1bc]; |
143 | u32 cpl0i[32]; /* Enable plane 0 autoinc */ |
144 | u32 cpl1i[32]; /* Color selection autoinc */ |
145 | }; |
146 | |
147 | struct cg14_dac { |
148 | u8 addr; /* Address Register */ |
149 | u8 xxx0[255]; |
150 | u8 glut; /* Gamma table */ |
151 | u8 xxx1[255]; |
152 | u8 select; /* Register Select */ |
153 | u8 xxx2[255]; |
154 | u8 mode; /* Mode Register */ |
155 | }; |
156 | |
157 | struct cg14_xlut{ |
158 | u8 x_xlut [256]; |
159 | u8 x_xlutd [256]; |
160 | u8 xxx0[0x600]; |
161 | u8 x_xlut_inc [256]; |
162 | u8 x_xlutd_inc [256]; |
163 | }; |
164 | |
165 | /* Color look up table (clut) */ |
166 | /* Each one of these arrays hold the color lookup table (for 256 |
167 | * colors) for each MDI page (I assume then there should be 4 MDI |
168 | * pages, I still wonder what they are. I have seen NeXTStep split |
169 | * the screen in four parts, while operating in 24 bits mode. Each |
170 | * integer holds 4 values: alpha value (transparency channel, thanks |
171 | * go to John Stone (johns@umr.edu) from OpenBSD), red, green and blue |
172 | * |
173 | * I currently use the clut instead of the Xlut |
174 | */ |
175 | struct cg14_clut { |
176 | u32 c_clut [256]; |
177 | u32 c_clutd [256]; /* i wonder what the 'd' is for */ |
178 | u32 c_clut_inc [256]; |
179 | u32 c_clutd_inc [256]; |
180 | }; |
181 | |
182 | #define CG14_MMAP_ENTRIES 16 |
183 | |
184 | struct cg14_par { |
185 | spinlock_t lock; |
186 | struct cg14_regs __iomem *regs; |
187 | struct cg14_clut __iomem *clut; |
188 | struct cg14_cursor __iomem *cursor; |
189 | |
190 | u32 flags; |
191 | #define CG14_FLAG_BLANKED 0x00000001 |
192 | |
193 | unsigned long iospace; |
194 | |
195 | struct sbus_mmap_map mmap_map[CG14_MMAP_ENTRIES]; |
196 | |
197 | int mode; |
198 | int ramsize; |
199 | }; |
200 | |
201 | static void __cg14_reset(struct cg14_par *par) |
202 | { |
203 | struct cg14_regs __iomem *regs = par->regs; |
204 | u8 val; |
205 | |
206 | val = sbus_readb(®s->mcr); |
207 | val &= ~(CG14_MCR_PIXMODE_MASK); |
208 | sbus_writeb(val, ®s->mcr); |
209 | } |
210 | |
211 | static int cg14_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) |
212 | { |
213 | struct cg14_par *par = (struct cg14_par *) info->par; |
214 | unsigned long flags; |
215 | |
216 | /* We just use this to catch switches out of |
217 | * graphics mode. |
218 | */ |
219 | spin_lock_irqsave(&par->lock, flags); |
220 | __cg14_reset(par); |
221 | spin_unlock_irqrestore(lock: &par->lock, flags); |
222 | |
223 | if (var->xoffset || var->yoffset || var->vmode) |
224 | return -EINVAL; |
225 | return 0; |
226 | } |
227 | |
228 | /** |
229 | * cg14_setcolreg - Optional function. Sets a color register. |
230 | * @regno: boolean, 0 copy local, 1 get_user() function |
231 | * @red: frame buffer colormap structure |
232 | * @green: The green value which can be up to 16 bits wide |
233 | * @blue: The blue value which can be up to 16 bits wide. |
234 | * @transp: If supported the alpha value which can be up to 16 bits wide. |
235 | * @info: frame buffer info structure |
236 | */ |
237 | static int cg14_setcolreg(unsigned regno, |
238 | unsigned red, unsigned green, unsigned blue, |
239 | unsigned transp, struct fb_info *info) |
240 | { |
241 | struct cg14_par *par = (struct cg14_par *) info->par; |
242 | struct cg14_clut __iomem *clut = par->clut; |
243 | unsigned long flags; |
244 | u32 val; |
245 | |
246 | if (regno >= 256) |
247 | return 1; |
248 | |
249 | red >>= 8; |
250 | green >>= 8; |
251 | blue >>= 8; |
252 | val = (red | (green << 8) | (blue << 16)); |
253 | |
254 | spin_lock_irqsave(&par->lock, flags); |
255 | sbus_writel(val, &clut->c_clut[regno]); |
256 | spin_unlock_irqrestore(lock: &par->lock, flags); |
257 | |
258 | return 0; |
259 | } |
260 | |
261 | static int cg14_sbusfb_mmap(struct fb_info *info, struct vm_area_struct *vma) |
262 | { |
263 | struct cg14_par *par = (struct cg14_par *) info->par; |
264 | |
265 | return sbusfb_mmap_helper(map: par->mmap_map, |
266 | physbase: info->fix.smem_start, fbsize: info->fix.smem_len, |
267 | iospace: par->iospace, vma); |
268 | } |
269 | |
270 | static int cg14_sbusfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) |
271 | { |
272 | struct cg14_par *par = (struct cg14_par *) info->par; |
273 | struct cg14_regs __iomem *regs = par->regs; |
274 | struct mdi_cfginfo kmdi, __user *mdii; |
275 | unsigned long flags; |
276 | int cur_mode, mode, ret = 0; |
277 | |
278 | switch (cmd) { |
279 | case MDI_RESET: |
280 | spin_lock_irqsave(&par->lock, flags); |
281 | __cg14_reset(par); |
282 | spin_unlock_irqrestore(lock: &par->lock, flags); |
283 | break; |
284 | |
285 | case MDI_GET_CFGINFO: |
286 | memset(&kmdi, 0, sizeof(kmdi)); |
287 | |
288 | spin_lock_irqsave(&par->lock, flags); |
289 | kmdi.mdi_type = FBTYPE_MDICOLOR; |
290 | kmdi.mdi_height = info->var.yres; |
291 | kmdi.mdi_width = info->var.xres; |
292 | kmdi.mdi_mode = par->mode; |
293 | kmdi.mdi_pixfreq = 72; /* FIXME */ |
294 | kmdi.mdi_size = par->ramsize; |
295 | spin_unlock_irqrestore(lock: &par->lock, flags); |
296 | |
297 | mdii = (struct mdi_cfginfo __user *) arg; |
298 | if (copy_to_user(to: mdii, from: &kmdi, n: sizeof(kmdi))) |
299 | ret = -EFAULT; |
300 | break; |
301 | |
302 | case MDI_SET_PIXELMODE: |
303 | if (get_user(mode, (int __user *) arg)) { |
304 | ret = -EFAULT; |
305 | break; |
306 | } |
307 | |
308 | spin_lock_irqsave(&par->lock, flags); |
309 | cur_mode = sbus_readb(®s->mcr); |
310 | cur_mode &= ~CG14_MCR_PIXMODE_MASK; |
311 | switch(mode) { |
312 | case MDI_32_PIX: |
313 | cur_mode |= (CG14_MCR_PIXMODE_32 << |
314 | CG14_MCR_PIXMODE_SHIFT); |
315 | break; |
316 | |
317 | case MDI_16_PIX: |
318 | cur_mode |= (CG14_MCR_PIXMODE_16 << |
319 | CG14_MCR_PIXMODE_SHIFT); |
320 | break; |
321 | |
322 | case MDI_8_PIX: |
323 | break; |
324 | |
325 | default: |
326 | ret = -ENOSYS; |
327 | break; |
328 | } |
329 | if (!ret) { |
330 | sbus_writeb(cur_mode, ®s->mcr); |
331 | par->mode = mode; |
332 | } |
333 | spin_unlock_irqrestore(lock: &par->lock, flags); |
334 | break; |
335 | |
336 | default: |
337 | ret = sbusfb_ioctl_helper(cmd, arg, info, |
338 | type: FBTYPE_MDICOLOR, fb_depth: 8, |
339 | fb_size: info->fix.smem_len); |
340 | break; |
341 | } |
342 | |
343 | return ret; |
344 | } |
345 | |
346 | /* |
347 | * Initialisation |
348 | */ |
349 | |
350 | static void cg14_init_fix(struct fb_info *info, int linebytes, |
351 | struct device_node *dp) |
352 | { |
353 | snprintf(buf: info->fix.id, size: sizeof(info->fix.id), fmt: "%pOFn" , dp); |
354 | |
355 | info->fix.type = FB_TYPE_PACKED_PIXELS; |
356 | info->fix.visual = FB_VISUAL_PSEUDOCOLOR; |
357 | |
358 | info->fix.line_length = linebytes; |
359 | |
360 | info->fix.accel = FB_ACCEL_SUN_CG14; |
361 | } |
362 | |
363 | static struct sbus_mmap_map __cg14_mmap_map[CG14_MMAP_ENTRIES] = { |
364 | { |
365 | .voff = CG14_REGS, |
366 | .poff = 0x80000000, |
367 | .size = 0x1000 |
368 | }, |
369 | { |
370 | .voff = CG14_XLUT, |
371 | .poff = 0x80003000, |
372 | .size = 0x1000 |
373 | }, |
374 | { |
375 | .voff = CG14_CLUT1, |
376 | .poff = 0x80004000, |
377 | .size = 0x1000 |
378 | }, |
379 | { |
380 | .voff = CG14_CLUT2, |
381 | .poff = 0x80005000, |
382 | .size = 0x1000 |
383 | }, |
384 | { |
385 | .voff = CG14_CLUT3, |
386 | .poff = 0x80006000, |
387 | .size = 0x1000 |
388 | }, |
389 | { |
390 | .voff = CG3_MMAP_OFFSET - 0x7000, |
391 | .poff = 0x80000000, |
392 | .size = 0x7000 |
393 | }, |
394 | { |
395 | .voff = CG3_MMAP_OFFSET, |
396 | .poff = 0x00000000, |
397 | .size = SBUS_MMAP_FBSIZE(1) |
398 | }, |
399 | { |
400 | .voff = MDI_CURSOR_MAP, |
401 | .poff = 0x80001000, |
402 | .size = 0x1000 |
403 | }, |
404 | { |
405 | .voff = MDI_CHUNKY_BGR_MAP, |
406 | .poff = 0x01000000, |
407 | .size = 0x400000 |
408 | }, |
409 | { |
410 | .voff = MDI_PLANAR_X16_MAP, |
411 | .poff = 0x02000000, |
412 | .size = 0x200000 |
413 | }, |
414 | { |
415 | .voff = MDI_PLANAR_C16_MAP, |
416 | .poff = 0x02800000, |
417 | .size = 0x200000 |
418 | }, |
419 | { |
420 | .voff = MDI_PLANAR_X32_MAP, |
421 | .poff = 0x03000000, |
422 | .size = 0x100000 |
423 | }, |
424 | { |
425 | .voff = MDI_PLANAR_B32_MAP, |
426 | .poff = 0x03400000, |
427 | .size = 0x100000 |
428 | }, |
429 | { |
430 | .voff = MDI_PLANAR_G32_MAP, |
431 | .poff = 0x03800000, |
432 | .size = 0x100000 |
433 | }, |
434 | { |
435 | .voff = MDI_PLANAR_R32_MAP, |
436 | .poff = 0x03c00000, |
437 | .size = 0x100000 |
438 | }, |
439 | { .size = 0 } |
440 | }; |
441 | |
442 | static void cg14_unmap_regs(struct platform_device *op, struct fb_info *info, |
443 | struct cg14_par *par) |
444 | { |
445 | if (par->regs) |
446 | of_iounmap(&op->resource[0], |
447 | par->regs, sizeof(struct cg14_regs)); |
448 | if (par->clut) |
449 | of_iounmap(&op->resource[0], |
450 | par->clut, sizeof(struct cg14_clut)); |
451 | if (par->cursor) |
452 | of_iounmap(&op->resource[0], |
453 | par->cursor, sizeof(struct cg14_cursor)); |
454 | if (info->screen_base) |
455 | of_iounmap(&op->resource[1], |
456 | info->screen_base, info->fix.smem_len); |
457 | } |
458 | |
459 | static int cg14_probe(struct platform_device *op) |
460 | { |
461 | struct device_node *dp = op->dev.of_node; |
462 | struct fb_info *info; |
463 | struct cg14_par *par; |
464 | int is_8mb, linebytes, i, err; |
465 | |
466 | info = framebuffer_alloc(size: sizeof(struct cg14_par), dev: &op->dev); |
467 | |
468 | err = -ENOMEM; |
469 | if (!info) |
470 | goto out_err; |
471 | par = info->par; |
472 | |
473 | spin_lock_init(&par->lock); |
474 | |
475 | sbusfb_fill_var(var: &info->var, dp, bpp: 8); |
476 | info->var.red.length = 8; |
477 | info->var.green.length = 8; |
478 | info->var.blue.length = 8; |
479 | |
480 | linebytes = of_getintprop_default(dp, "linebytes" , |
481 | info->var.xres); |
482 | info->fix.smem_len = PAGE_ALIGN(linebytes * info->var.yres); |
483 | |
484 | if (of_node_name_eq(np: dp->parent, name: "sbus" ) || |
485 | of_node_name_eq(np: dp->parent, name: "sbi" )) { |
486 | info->fix.smem_start = op->resource[0].start; |
487 | par->iospace = op->resource[0].flags & IORESOURCE_BITS; |
488 | } else { |
489 | info->fix.smem_start = op->resource[1].start; |
490 | par->iospace = op->resource[0].flags & IORESOURCE_BITS; |
491 | } |
492 | |
493 | par->regs = of_ioremap(&op->resource[0], 0, |
494 | sizeof(struct cg14_regs), "cg14 regs" ); |
495 | par->clut = of_ioremap(&op->resource[0], CG14_CLUT1, |
496 | sizeof(struct cg14_clut), "cg14 clut" ); |
497 | par->cursor = of_ioremap(&op->resource[0], CG14_CURSORREGS, |
498 | sizeof(struct cg14_cursor), "cg14 cursor" ); |
499 | |
500 | info->screen_base = of_ioremap(&op->resource[1], 0, |
501 | info->fix.smem_len, "cg14 ram" ); |
502 | |
503 | if (!par->regs || !par->clut || !par->cursor || !info->screen_base) |
504 | goto out_unmap_regs; |
505 | |
506 | is_8mb = (resource_size(res: &op->resource[1]) == (8 * 1024 * 1024)); |
507 | |
508 | BUILD_BUG_ON(sizeof(par->mmap_map) != sizeof(__cg14_mmap_map)); |
509 | |
510 | memcpy(&par->mmap_map, &__cg14_mmap_map, sizeof(par->mmap_map)); |
511 | |
512 | for (i = 0; i < CG14_MMAP_ENTRIES; i++) { |
513 | struct sbus_mmap_map *map = &par->mmap_map[i]; |
514 | |
515 | if (!map->size) |
516 | break; |
517 | if (map->poff & 0x80000000) |
518 | map->poff = (map->poff & 0x7fffffff) + |
519 | (op->resource[0].start - |
520 | op->resource[1].start); |
521 | if (is_8mb && |
522 | map->size >= 0x100000 && |
523 | map->size <= 0x400000) |
524 | map->size *= 2; |
525 | } |
526 | |
527 | par->mode = MDI_8_PIX; |
528 | par->ramsize = (is_8mb ? 0x800000 : 0x400000); |
529 | |
530 | info->flags = FBINFO_HWACCEL_YPAN; |
531 | info->fbops = &cg14_ops; |
532 | |
533 | __cg14_reset(par); |
534 | |
535 | if (fb_alloc_cmap(cmap: &info->cmap, len: 256, transp: 0)) |
536 | goto out_unmap_regs; |
537 | |
538 | fb_set_cmap(cmap: &info->cmap, fb_info: info); |
539 | |
540 | cg14_init_fix(info, linebytes, dp); |
541 | |
542 | err = register_framebuffer(fb_info: info); |
543 | if (err < 0) |
544 | goto out_dealloc_cmap; |
545 | |
546 | dev_set_drvdata(dev: &op->dev, data: info); |
547 | |
548 | printk(KERN_INFO "%pOF: cgfourteen at %lx:%lx, %dMB\n" , |
549 | dp, |
550 | par->iospace, info->fix.smem_start, |
551 | par->ramsize >> 20); |
552 | |
553 | return 0; |
554 | |
555 | out_dealloc_cmap: |
556 | fb_dealloc_cmap(cmap: &info->cmap); |
557 | |
558 | out_unmap_regs: |
559 | cg14_unmap_regs(op, info, par); |
560 | framebuffer_release(info); |
561 | |
562 | out_err: |
563 | return err; |
564 | } |
565 | |
566 | static void cg14_remove(struct platform_device *op) |
567 | { |
568 | struct fb_info *info = dev_get_drvdata(dev: &op->dev); |
569 | struct cg14_par *par = info->par; |
570 | |
571 | unregister_framebuffer(fb_info: info); |
572 | fb_dealloc_cmap(cmap: &info->cmap); |
573 | |
574 | cg14_unmap_regs(op, info, par); |
575 | |
576 | framebuffer_release(info); |
577 | } |
578 | |
579 | static const struct of_device_id cg14_match[] = { |
580 | { |
581 | .name = "cgfourteen" , |
582 | }, |
583 | {}, |
584 | }; |
585 | MODULE_DEVICE_TABLE(of, cg14_match); |
586 | |
587 | static struct platform_driver cg14_driver = { |
588 | .driver = { |
589 | .name = "cg14" , |
590 | .of_match_table = cg14_match, |
591 | }, |
592 | .probe = cg14_probe, |
593 | .remove_new = cg14_remove, |
594 | }; |
595 | |
596 | static int __init cg14_init(void) |
597 | { |
598 | if (fb_get_options(name: "cg14fb" , NULL)) |
599 | return -ENODEV; |
600 | |
601 | return platform_driver_register(&cg14_driver); |
602 | } |
603 | |
604 | static void __exit cg14_exit(void) |
605 | { |
606 | platform_driver_unregister(&cg14_driver); |
607 | } |
608 | |
609 | module_init(cg14_init); |
610 | module_exit(cg14_exit); |
611 | |
612 | MODULE_DESCRIPTION("framebuffer driver for CGfourteen chipsets" ); |
613 | MODULE_AUTHOR("David S. Miller <davem@davemloft.net>" ); |
614 | MODULE_VERSION("2.0" ); |
615 | MODULE_LICENSE("GPL" ); |
616 | |