Root/
1 | /* |
2 | * linux/drivers/video/igafb.c -- Frame buffer device for IGA 1682 |
3 | * |
4 | * Copyright (C) 1998 Vladimir Roganov and Gleb Raiko |
5 | * |
6 | * This driver is partly based on the Frame buffer device for ATI Mach64 |
7 | * and partially on VESA-related code. |
8 | * |
9 | * Copyright (C) 1997-1998 Geert Uytterhoeven |
10 | * Copyright (C) 1998 Bernd Harries |
11 | * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) |
12 | * |
13 | * This file is subject to the terms and conditions of the GNU General Public |
14 | * License. See the file COPYING in the main directory of this archive for |
15 | * more details. |
16 | */ |
17 | |
18 | /****************************************************************************** |
19 | |
20 | TODO: |
21 | Despite of IGA Card has advanced graphic acceleration, |
22 | initial version is almost dummy and does not support it. |
23 | Support for video modes and acceleration must be added |
24 | together with accelerated X-Windows driver implementation. |
25 | |
26 | Most important thing at this moment is that we have working |
27 | JavaEngine1 console & X with new console interface. |
28 | |
29 | ******************************************************************************/ |
30 | |
31 | #include <linux/module.h> |
32 | #include <linux/kernel.h> |
33 | #include <linux/errno.h> |
34 | #include <linux/string.h> |
35 | #include <linux/mm.h> |
36 | #include <linux/slab.h> |
37 | #include <linux/vmalloc.h> |
38 | #include <linux/delay.h> |
39 | #include <linux/interrupt.h> |
40 | #include <linux/fb.h> |
41 | #include <linux/init.h> |
42 | #include <linux/pci.h> |
43 | #include <linux/nvram.h> |
44 | |
45 | #include <asm/io.h> |
46 | |
47 | #ifdef CONFIG_SPARC |
48 | #include <asm/prom.h> |
49 | #include <asm/pcic.h> |
50 | #endif |
51 | |
52 | #include <video/iga.h> |
53 | |
54 | struct pci_mmap_map { |
55 | unsigned long voff; |
56 | unsigned long poff; |
57 | unsigned long size; |
58 | unsigned long prot_flag; |
59 | unsigned long prot_mask; |
60 | }; |
61 | |
62 | struct iga_par { |
63 | struct pci_mmap_map *mmap_map; |
64 | unsigned long frame_buffer_phys; |
65 | unsigned long io_base; |
66 | }; |
67 | |
68 | struct fb_info fb_info; |
69 | |
70 | struct fb_fix_screeninfo igafb_fix __initdata = { |
71 | .id = "IGA 1682", |
72 | .type = FB_TYPE_PACKED_PIXELS, |
73 | .mmio_len = 1000 |
74 | }; |
75 | |
76 | struct fb_var_screeninfo default_var = { |
77 | /* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */ |
78 | .xres = 640, |
79 | .yres = 480, |
80 | .xres_virtual = 640, |
81 | .yres_virtual = 480, |
82 | .bits_per_pixel = 8, |
83 | .red = {0, 8, 0 }, |
84 | .green = {0, 8, 0 }, |
85 | .blue = {0, 8, 0 }, |
86 | .height = -1, |
87 | .width = -1, |
88 | .accel_flags = FB_ACCEL_NONE, |
89 | .pixclock = 39722, |
90 | .left_margin = 48, |
91 | .right_margin = 16, |
92 | .upper_margin = 33, |
93 | .lower_margin = 10, |
94 | .hsync_len = 96, |
95 | .vsync_len = 2, |
96 | .vmode = FB_VMODE_NONINTERLACED |
97 | }; |
98 | |
99 | #ifdef CONFIG_SPARC |
100 | struct fb_var_screeninfo default_var_1024x768 __initdata = { |
101 | /* 1024x768, 75 Hz, Non-Interlaced (78.75 MHz dotclock) */ |
102 | .xres = 1024, |
103 | .yres = 768, |
104 | .xres_virtual = 1024, |
105 | .yres_virtual = 768, |
106 | .bits_per_pixel = 8, |
107 | .red = {0, 8, 0 }, |
108 | .green = {0, 8, 0 }, |
109 | .blue = {0, 8, 0 }, |
110 | .height = -1, |
111 | .width = -1, |
112 | .accel_flags = FB_ACCEL_NONE, |
113 | .pixclock = 12699, |
114 | .left_margin = 176, |
115 | .right_margin = 16, |
116 | .upper_margin = 28, |
117 | .lower_margin = 1, |
118 | .hsync_len = 96, |
119 | .vsync_len = 3, |
120 | .vmode = FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED |
121 | }; |
122 | |
123 | struct fb_var_screeninfo default_var_1152x900 __initdata = { |
124 | /* 1152x900, 76 Hz, Non-Interlaced (110.0 MHz dotclock) */ |
125 | .xres = 1152, |
126 | .yres = 900, |
127 | .xres_virtual = 1152, |
128 | .yres_virtual = 900, |
129 | .bits_per_pixel = 8, |
130 | .red = { 0, 8, 0 }, |
131 | .green = { 0, 8, 0 }, |
132 | .blue = { 0, 8, 0 }, |
133 | .height = -1, |
134 | .width = -1, |
135 | .accel_flags = FB_ACCEL_NONE, |
136 | .pixclock = 9091, |
137 | .left_margin = 234, |
138 | .right_margin = 24, |
139 | .upper_margin = 34, |
140 | .lower_margin = 3, |
141 | .hsync_len = 100, |
142 | .vsync_len = 3, |
143 | .vmode = FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED |
144 | }; |
145 | |
146 | struct fb_var_screeninfo default_var_1280x1024 __initdata = { |
147 | /* 1280x1024, 75 Hz, Non-Interlaced (135.00 MHz dotclock) */ |
148 | .xres = 1280, |
149 | .yres = 1024, |
150 | .xres_virtual = 1280, |
151 | .yres_virtual = 1024, |
152 | .bits_per_pixel = 8, |
153 | .red = {0, 8, 0 }, |
154 | .green = {0, 8, 0 }, |
155 | .blue = {0, 8, 0 }, |
156 | .height = -1, |
157 | .width = -1, |
158 | .accel_flags = 0, |
159 | .pixclock = 7408, |
160 | .left_margin = 248, |
161 | .right_margin = 16, |
162 | .upper_margin = 38, |
163 | .lower_margin = 1, |
164 | .hsync_len = 144, |
165 | .vsync_len = 3, |
166 | .vmode = FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED |
167 | }; |
168 | |
169 | /* |
170 | * Memory-mapped I/O functions for Sparc PCI |
171 | * |
172 | * On sparc we happen to access I/O with memory mapped functions too. |
173 | */ |
174 | #define pci_inb(par, reg) readb(par->io_base+(reg)) |
175 | #define pci_outb(par, val, reg) writeb(val, par->io_base+(reg)) |
176 | |
177 | static inline unsigned int iga_inb(struct iga_par *par, unsigned int reg, |
178 | unsigned int idx) |
179 | { |
180 | pci_outb(par, idx, reg); |
181 | return pci_inb(par, reg + 1); |
182 | } |
183 | |
184 | static inline void iga_outb(struct iga_par *par, unsigned char val, |
185 | unsigned int reg, unsigned int idx ) |
186 | { |
187 | pci_outb(par, idx, reg); |
188 | pci_outb(par, val, reg+1); |
189 | } |
190 | |
191 | #endif /* CONFIG_SPARC */ |
192 | |
193 | /* |
194 | * Very important functionality for the JavaEngine1 computer: |
195 | * make screen border black (usign special IGA registers) |
196 | */ |
197 | static void iga_blank_border(struct iga_par *par) |
198 | { |
199 | int i; |
200 | #if 0 |
201 | /* |
202 | * PROM does this for us, so keep this code as a reminder |
203 | * about required read from 0x3DA and writing of 0x20 in the end. |
204 | */ |
205 | (void) pci_inb(par, 0x3DA); /* required for every access */ |
206 | pci_outb(par, IGA_IDX_VGA_OVERSCAN, IGA_ATTR_CTL); |
207 | (void) pci_inb(par, IGA_ATTR_CTL+1); |
208 | pci_outb(par, 0x38, IGA_ATTR_CTL); |
209 | pci_outb(par, 0x20, IGA_ATTR_CTL); /* re-enable visual */ |
210 | #endif |
211 | /* |
212 | * This does not work as it was designed because the overscan |
213 | * color is looked up in the palette. Therefore, under X11 |
214 | * overscan changes color. |
215 | */ |
216 | for (i=0; i < 3; i++) |
217 | iga_outb(par, 0, IGA_EXT_CNTRL, IGA_IDX_OVERSCAN_COLOR + i); |
218 | } |
219 | |
220 | #ifdef CONFIG_SPARC |
221 | static int igafb_mmap(struct fb_info *info, |
222 | struct vm_area_struct *vma) |
223 | { |
224 | struct iga_par *par = (struct iga_par *)info->par; |
225 | unsigned int size, page, map_size = 0; |
226 | unsigned long map_offset = 0; |
227 | int i; |
228 | |
229 | if (!par->mmap_map) |
230 | return -ENXIO; |
231 | |
232 | size = vma->vm_end - vma->vm_start; |
233 | |
234 | /* Each page, see which map applies */ |
235 | for (page = 0; page < size; ) { |
236 | map_size = 0; |
237 | for (i = 0; par->mmap_map[i].size; i++) { |
238 | unsigned long start = par->mmap_map[i].voff; |
239 | unsigned long end = start + par->mmap_map[i].size; |
240 | unsigned long offset = (vma->vm_pgoff << PAGE_SHIFT) + page; |
241 | |
242 | if (start > offset) |
243 | continue; |
244 | if (offset >= end) |
245 | continue; |
246 | |
247 | map_size = par->mmap_map[i].size - (offset - start); |
248 | map_offset = par->mmap_map[i].poff + (offset - start); |
249 | break; |
250 | } |
251 | if (!map_size) { |
252 | page += PAGE_SIZE; |
253 | continue; |
254 | } |
255 | if (page + map_size > size) |
256 | map_size = size - page; |
257 | |
258 | pgprot_val(vma->vm_page_prot) &= ~(par->mmap_map[i].prot_mask); |
259 | pgprot_val(vma->vm_page_prot) |= par->mmap_map[i].prot_flag; |
260 | |
261 | if (remap_pfn_range(vma, vma->vm_start + page, |
262 | map_offset >> PAGE_SHIFT, map_size, vma->vm_page_prot)) |
263 | return -EAGAIN; |
264 | |
265 | page += map_size; |
266 | } |
267 | |
268 | if (!map_size) |
269 | return -EINVAL; |
270 | |
271 | vma->vm_flags |= VM_IO; |
272 | return 0; |
273 | } |
274 | #endif /* CONFIG_SPARC */ |
275 | |
276 | static int igafb_setcolreg(unsigned regno, unsigned red, unsigned green, |
277 | unsigned blue, unsigned transp, |
278 | struct fb_info *info) |
279 | { |
280 | /* |
281 | * Set a single color register. The values supplied are |
282 | * already rounded down to the hardware's capabilities |
283 | * (according to the entries in the `var' structure). Return |
284 | * != 0 for invalid regno. |
285 | */ |
286 | struct iga_par *par = (struct iga_par *)info->par; |
287 | |
288 | if (regno >= info->cmap.len) |
289 | return 1; |
290 | |
291 | pci_outb(par, regno, DAC_W_INDEX); |
292 | pci_outb(par, red, DAC_DATA); |
293 | pci_outb(par, green, DAC_DATA); |
294 | pci_outb(par, blue, DAC_DATA); |
295 | |
296 | if (regno < 16) { |
297 | switch (info->var.bits_per_pixel) { |
298 | case 16: |
299 | ((u16*)(info->pseudo_palette))[regno] = |
300 | (regno << 10) | (regno << 5) | regno; |
301 | break; |
302 | case 24: |
303 | ((u32*)(info->pseudo_palette))[regno] = |
304 | (regno << 16) | (regno << 8) | regno; |
305 | break; |
306 | case 32: |
307 | { int i; |
308 | i = (regno << 8) | regno; |
309 | ((u32*)(info->pseudo_palette))[regno] = (i << 16) | i; |
310 | } |
311 | break; |
312 | } |
313 | } |
314 | return 0; |
315 | } |
316 | |
317 | /* |
318 | * Framebuffer option structure |
319 | */ |
320 | static struct fb_ops igafb_ops = { |
321 | .owner = THIS_MODULE, |
322 | .fb_setcolreg = igafb_setcolreg, |
323 | .fb_fillrect = cfb_fillrect, |
324 | .fb_copyarea = cfb_copyarea, |
325 | .fb_imageblit = cfb_imageblit, |
326 | #ifdef CONFIG_SPARC |
327 | .fb_mmap = igafb_mmap, |
328 | #endif |
329 | }; |
330 | |
331 | static int __init iga_init(struct fb_info *info, struct iga_par *par) |
332 | { |
333 | char vramsz = iga_inb(par, IGA_EXT_CNTRL, IGA_IDX_EXT_BUS_CNTL) |
334 | & MEM_SIZE_ALIAS; |
335 | int video_cmap_len; |
336 | |
337 | switch (vramsz) { |
338 | case MEM_SIZE_1M: |
339 | info->fix.smem_len = 0x100000; |
340 | break; |
341 | case MEM_SIZE_2M: |
342 | info->fix.smem_len = 0x200000; |
343 | break; |
344 | case MEM_SIZE_4M: |
345 | case MEM_SIZE_RESERVED: |
346 | info->fix.smem_len = 0x400000; |
347 | break; |
348 | } |
349 | |
350 | if (info->var.bits_per_pixel > 8) |
351 | video_cmap_len = 16; |
352 | else |
353 | video_cmap_len = 256; |
354 | |
355 | info->fbops = &igafb_ops; |
356 | info->flags = FBINFO_DEFAULT; |
357 | |
358 | fb_alloc_cmap(&info->cmap, video_cmap_len, 0); |
359 | |
360 | if (register_framebuffer(info) < 0) |
361 | return 0; |
362 | |
363 | printk("fb%d: %s frame buffer device at 0x%08lx [%dMB VRAM]\n", |
364 | info->node, info->fix.id, |
365 | par->frame_buffer_phys, info->fix.smem_len >> 20); |
366 | |
367 | iga_blank_border(par); |
368 | return 1; |
369 | } |
370 | |
371 | int __init igafb_init(void) |
372 | { |
373 | struct fb_info *info; |
374 | struct pci_dev *pdev; |
375 | struct iga_par *par; |
376 | unsigned long addr; |
377 | int size, iga2000 = 0; |
378 | |
379 | if (fb_get_options("igafb", NULL)) |
380 | return -ENODEV; |
381 | |
382 | pdev = pci_get_device(PCI_VENDOR_ID_INTERG, |
383 | PCI_DEVICE_ID_INTERG_1682, 0); |
384 | if (pdev == NULL) { |
385 | /* |
386 | * XXX We tried to use cyber2000fb.c for IGS 2000. |
387 | * But it does not initialize the chip in JavaStation-E, alas. |
388 | */ |
389 | pdev = pci_get_device(PCI_VENDOR_ID_INTERG, 0x2000, 0); |
390 | if(pdev == NULL) { |
391 | return -ENXIO; |
392 | } |
393 | iga2000 = 1; |
394 | } |
395 | /* We leak a reference here but as it cannot be unloaded this is |
396 | fine. If you write unload code remember to free it in unload */ |
397 | |
398 | size = sizeof(struct iga_par) + sizeof(u32)*16; |
399 | |
400 | info = framebuffer_alloc(size, &pdev->dev); |
401 | if (!info) { |
402 | printk("igafb_init: can't alloc fb_info\n"); |
403 | pci_dev_put(pdev); |
404 | return -ENOMEM; |
405 | } |
406 | |
407 | par = info->par; |
408 | |
409 | if ((addr = pdev->resource[0].start) == 0) { |
410 | printk("igafb_init: no memory start\n"); |
411 | kfree(info); |
412 | pci_dev_put(pdev); |
413 | return -ENXIO; |
414 | } |
415 | |
416 | if ((info->screen_base = ioremap(addr, 1024*1024*2)) == 0) { |
417 | printk("igafb_init: can't remap %lx[2M]\n", addr); |
418 | kfree(info); |
419 | pci_dev_put(pdev); |
420 | return -ENXIO; |
421 | } |
422 | |
423 | par->frame_buffer_phys = addr & PCI_BASE_ADDRESS_MEM_MASK; |
424 | |
425 | #ifdef CONFIG_SPARC |
426 | /* |
427 | * The following is sparc specific and this is why: |
428 | * |
429 | * IGS2000 has its I/O memory mapped and we want |
430 | * to generate memory cycles on PCI, e.g. do ioremap(), |
431 | * then readb/writeb() as in Documentation/IO-mapping.txt. |
432 | * |
433 | * IGS1682 is more traditional, it responds to PCI I/O |
434 | * cycles, so we want to access it with inb()/outb(). |
435 | * |
436 | * On sparc, PCIC converts CPU memory access within |
437 | * phys window 0x3000xxxx into PCI I/O cycles. Therefore |
438 | * we may use readb/writeb to access them with IGS1682. |
439 | * |
440 | * We do not take io_base_phys from resource[n].start |
441 | * on IGS1682 because that chip is BROKEN. It does not |
442 | * have a base register for I/O. We just "know" what its |
443 | * I/O addresses are. |
444 | */ |
445 | if (iga2000) { |
446 | igafb_fix.mmio_start = par->frame_buffer_phys | 0x00800000; |
447 | } else { |
448 | igafb_fix.mmio_start = 0x30000000; /* XXX */ |
449 | } |
450 | if ((par->io_base = (int) ioremap(igafb_fix.mmio_start, igafb_fix.smem_len)) == 0) { |
451 | printk("igafb_init: can't remap %lx[4K]\n", igafb_fix.mmio_start); |
452 | iounmap((void *)info->screen_base); |
453 | kfree(info); |
454 | pci_dev_put(pdev); |
455 | return -ENXIO; |
456 | } |
457 | |
458 | /* |
459 | * Figure mmap addresses from PCI config space. |
460 | * We need two regions: for video memory and for I/O ports. |
461 | * Later one can add region for video coprocessor registers. |
462 | * However, mmap routine loops until size != 0, so we put |
463 | * one additional region with size == 0. |
464 | */ |
465 | |
466 | par->mmap_map = kzalloc(4 * sizeof(*par->mmap_map), GFP_ATOMIC); |
467 | if (!par->mmap_map) { |
468 | printk("igafb_init: can't alloc mmap_map\n"); |
469 | iounmap((void *)par->io_base); |
470 | iounmap(info->screen_base); |
471 | kfree(info); |
472 | pci_dev_put(pdev); |
473 | return -ENOMEM; |
474 | } |
475 | |
476 | /* |
477 | * Set default vmode and cmode from PROM properties. |
478 | */ |
479 | { |
480 | struct device_node *dp = pci_device_to_OF_node(pdev); |
481 | int node = dp->node; |
482 | int width = prom_getintdefault(node, "width", 1024); |
483 | int height = prom_getintdefault(node, "height", 768); |
484 | int depth = prom_getintdefault(node, "depth", 8); |
485 | switch (width) { |
486 | case 1024: |
487 | if (height == 768) |
488 | default_var = default_var_1024x768; |
489 | break; |
490 | case 1152: |
491 | if (height == 900) |
492 | default_var = default_var_1152x900; |
493 | break; |
494 | case 1280: |
495 | if (height == 1024) |
496 | default_var = default_var_1280x1024; |
497 | break; |
498 | default: |
499 | break; |
500 | } |
501 | |
502 | switch (depth) { |
503 | case 8: |
504 | default_var.bits_per_pixel = 8; |
505 | break; |
506 | case 16: |
507 | default_var.bits_per_pixel = 16; |
508 | break; |
509 | case 24: |
510 | default_var.bits_per_pixel = 24; |
511 | break; |
512 | case 32: |
513 | default_var.bits_per_pixel = 32; |
514 | break; |
515 | default: |
516 | break; |
517 | } |
518 | } |
519 | |
520 | #endif |
521 | igafb_fix.smem_start = (unsigned long) info->screen_base; |
522 | igafb_fix.line_length = default_var.xres*(default_var.bits_per_pixel/8); |
523 | igafb_fix.visual = default_var.bits_per_pixel <= 8 ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR; |
524 | |
525 | info->var = default_var; |
526 | info->fix = igafb_fix; |
527 | info->pseudo_palette = (void *)(par + 1); |
528 | |
529 | if (!iga_init(info, par)) { |
530 | iounmap((void *)par->io_base); |
531 | iounmap(info->screen_base); |
532 | kfree(par->mmap_map); |
533 | kfree(info); |
534 | } |
535 | |
536 | #ifdef CONFIG_SPARC |
537 | /* |
538 | * Add /dev/fb mmap values. |
539 | */ |
540 | |
541 | /* First region is for video memory */ |
542 | par->mmap_map[0].voff = 0x0; |
543 | par->mmap_map[0].poff = par->frame_buffer_phys & PAGE_MASK; |
544 | par->mmap_map[0].size = info->fix.smem_len & PAGE_MASK; |
545 | par->mmap_map[0].prot_mask = SRMMU_CACHE; |
546 | par->mmap_map[0].prot_flag = SRMMU_WRITE; |
547 | |
548 | /* Second region is for I/O ports */ |
549 | par->mmap_map[1].voff = par->frame_buffer_phys & PAGE_MASK; |
550 | par->mmap_map[1].poff = info->fix.smem_start & PAGE_MASK; |
551 | par->mmap_map[1].size = PAGE_SIZE * 2; /* X wants 2 pages */ |
552 | par->mmap_map[1].prot_mask = SRMMU_CACHE; |
553 | par->mmap_map[1].prot_flag = SRMMU_WRITE; |
554 | #endif /* CONFIG_SPARC */ |
555 | |
556 | return 0; |
557 | } |
558 | |
559 | int __init igafb_setup(char *options) |
560 | { |
561 | char *this_opt; |
562 | |
563 | if (!options || !*options) |
564 | return 0; |
565 | |
566 | while ((this_opt = strsep(&options, ",")) != NULL) { |
567 | } |
568 | return 0; |
569 | } |
570 | |
571 | module_init(igafb_init); |
572 | MODULE_LICENSE("GPL"); |
573 | static struct pci_device_id igafb_pci_tbl[] __devinitdata = { |
574 | { PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_1682, |
575 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, |
576 | { } |
577 | }; |
578 | |
579 | MODULE_DEVICE_TABLE(pci, igafb_pci_tbl); |
580 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9