diff options
Diffstat (limited to 'drivers/video/fbdev/vermilion/vermilion.c')
-rw-r--r-- | drivers/video/fbdev/vermilion/vermilion.c | 1175 |
1 files changed, 1175 insertions, 0 deletions
diff --git a/drivers/video/fbdev/vermilion/vermilion.c b/drivers/video/fbdev/vermilion/vermilion.c new file mode 100644 index 000000000000..048a66640b03 --- /dev/null +++ b/drivers/video/fbdev/vermilion/vermilion.c | |||
@@ -0,0 +1,1175 @@ | |||
1 | /* | ||
2 | * Copyright (c) Intel Corp. 2007. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to | ||
6 | * develop this driver. | ||
7 | * | ||
8 | * This file is part of the Vermilion Range fb driver. | ||
9 | * The Vermilion Range fb driver is free software; | ||
10 | * you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * The Vermilion Range fb driver is distributed | ||
16 | * in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this driver; if not, write to the Free Software | ||
23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
24 | * | ||
25 | * Authors: | ||
26 | * Thomas Hellström <thomas-at-tungstengraphics-dot-com> | ||
27 | * Michel Dänzer <michel-at-tungstengraphics-dot-com> | ||
28 | * Alan Hourihane <alanh-at-tungstengraphics-dot-com> | ||
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/delay.h> | ||
36 | #include <linux/slab.h> | ||
37 | #include <linux/mm.h> | ||
38 | #include <linux/fb.h> | ||
39 | #include <linux/pci.h> | ||
40 | #include <asm/cacheflush.h> | ||
41 | #include <asm/tlbflush.h> | ||
42 | #include <linux/mmzone.h> | ||
43 | |||
44 | /* #define VERMILION_DEBUG */ | ||
45 | |||
46 | #include "vermilion.h" | ||
47 | |||
48 | #define MODULE_NAME "vmlfb" | ||
49 | |||
50 | #define VML_TOHW(_val, _width) ((((_val) << (_width)) + 0x7FFF - (_val)) >> 16) | ||
51 | |||
52 | static struct mutex vml_mutex; | ||
53 | static struct list_head global_no_mode; | ||
54 | static struct list_head global_has_mode; | ||
55 | static struct fb_ops vmlfb_ops; | ||
56 | static struct vml_sys *subsys = NULL; | ||
57 | static char *vml_default_mode = "1024x768@60"; | ||
58 | static struct fb_videomode defaultmode = { | ||
59 | NULL, 60, 1024, 768, 12896, 144, 24, 29, 3, 136, 6, | ||
60 | 0, FB_VMODE_NONINTERLACED | ||
61 | }; | ||
62 | |||
63 | static u32 vml_mem_requested = (10 * 1024 * 1024); | ||
64 | static u32 vml_mem_contig = (4 * 1024 * 1024); | ||
65 | static u32 vml_mem_min = (4 * 1024 * 1024); | ||
66 | |||
67 | static u32 vml_clocks[] = { | ||
68 | 6750, | ||
69 | 13500, | ||
70 | 27000, | ||
71 | 29700, | ||
72 | 37125, | ||
73 | 54000, | ||
74 | 59400, | ||
75 | 74250, | ||
76 | 120000, | ||
77 | 148500 | ||
78 | }; | ||
79 | |||
80 | static u32 vml_num_clocks = ARRAY_SIZE(vml_clocks); | ||
81 | |||
82 | /* | ||
83 | * Allocate a contiguous vram area and make its linear kernel map | ||
84 | * uncached. | ||
85 | */ | ||
86 | |||
87 | static int vmlfb_alloc_vram_area(struct vram_area *va, unsigned max_order, | ||
88 | unsigned min_order) | ||
89 | { | ||
90 | gfp_t flags; | ||
91 | unsigned long i; | ||
92 | |||
93 | max_order++; | ||
94 | do { | ||
95 | /* | ||
96 | * Really try hard to get the needed memory. | ||
97 | * We need memory below the first 32MB, so we | ||
98 | * add the __GFP_DMA flag that guarantees that we are | ||
99 | * below the first 16MB. | ||
100 | */ | ||
101 | |||
102 | flags = __GFP_DMA | __GFP_HIGH; | ||
103 | va->logical = | ||
104 | __get_free_pages(flags, --max_order); | ||
105 | } while (va->logical == 0 && max_order > min_order); | ||
106 | |||
107 | if (!va->logical) | ||
108 | return -ENOMEM; | ||
109 | |||
110 | va->phys = virt_to_phys((void *)va->logical); | ||
111 | va->size = PAGE_SIZE << max_order; | ||
112 | va->order = max_order; | ||
113 | |||
114 | /* | ||
115 | * It seems like __get_free_pages only ups the usage count | ||
116 | * of the first page. This doesn't work with fault mapping, so | ||
117 | * up the usage count once more (XXX: should use split_page or | ||
118 | * compound page). | ||
119 | */ | ||
120 | |||
121 | memset((void *)va->logical, 0x00, va->size); | ||
122 | for (i = va->logical; i < va->logical + va->size; i += PAGE_SIZE) { | ||
123 | get_page(virt_to_page(i)); | ||
124 | } | ||
125 | |||
126 | /* | ||
127 | * Change caching policy of the linear kernel map to avoid | ||
128 | * mapping type conflicts with user-space mappings. | ||
129 | */ | ||
130 | set_pages_uc(virt_to_page(va->logical), va->size >> PAGE_SHIFT); | ||
131 | |||
132 | printk(KERN_DEBUG MODULE_NAME | ||
133 | ": Allocated %ld bytes vram area at 0x%08lx\n", | ||
134 | va->size, va->phys); | ||
135 | |||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | /* | ||
140 | * Free a contiguous vram area and reset its linear kernel map | ||
141 | * mapping type. | ||
142 | */ | ||
143 | |||
144 | static void vmlfb_free_vram_area(struct vram_area *va) | ||
145 | { | ||
146 | unsigned long j; | ||
147 | |||
148 | if (va->logical) { | ||
149 | |||
150 | /* | ||
151 | * Reset the linear kernel map caching policy. | ||
152 | */ | ||
153 | |||
154 | set_pages_wb(virt_to_page(va->logical), | ||
155 | va->size >> PAGE_SHIFT); | ||
156 | |||
157 | /* | ||
158 | * Decrease the usage count on the pages we've used | ||
159 | * to compensate for upping when allocating. | ||
160 | */ | ||
161 | |||
162 | for (j = va->logical; j < va->logical + va->size; | ||
163 | j += PAGE_SIZE) { | ||
164 | (void)put_page_testzero(virt_to_page(j)); | ||
165 | } | ||
166 | |||
167 | printk(KERN_DEBUG MODULE_NAME | ||
168 | ": Freeing %ld bytes vram area at 0x%08lx\n", | ||
169 | va->size, va->phys); | ||
170 | free_pages(va->logical, va->order); | ||
171 | |||
172 | va->logical = 0; | ||
173 | } | ||
174 | } | ||
175 | |||
176 | /* | ||
177 | * Free allocated vram. | ||
178 | */ | ||
179 | |||
180 | static void vmlfb_free_vram(struct vml_info *vinfo) | ||
181 | { | ||
182 | int i; | ||
183 | |||
184 | for (i = 0; i < vinfo->num_areas; ++i) { | ||
185 | vmlfb_free_vram_area(&vinfo->vram[i]); | ||
186 | } | ||
187 | vinfo->num_areas = 0; | ||
188 | } | ||
189 | |||
190 | /* | ||
191 | * Allocate vram. Currently we try to allocate contiguous areas from the | ||
192 | * __GFP_DMA zone and puzzle them together. A better approach would be to | ||
193 | * allocate one contiguous area for scanout and use one-page allocations for | ||
194 | * offscreen areas. This requires user-space and GPU virtual mappings. | ||
195 | */ | ||
196 | |||
197 | static int vmlfb_alloc_vram(struct vml_info *vinfo, | ||
198 | size_t requested, | ||
199 | size_t min_total, size_t min_contig) | ||
200 | { | ||
201 | int i, j; | ||
202 | int order; | ||
203 | int contiguous; | ||
204 | int err; | ||
205 | struct vram_area *va; | ||
206 | struct vram_area *va2; | ||
207 | |||
208 | vinfo->num_areas = 0; | ||
209 | for (i = 0; i < VML_VRAM_AREAS; ++i) { | ||
210 | va = &vinfo->vram[i]; | ||
211 | order = 0; | ||
212 | |||
213 | while (requested > (PAGE_SIZE << order) && order < MAX_ORDER) | ||
214 | order++; | ||
215 | |||
216 | err = vmlfb_alloc_vram_area(va, order, 0); | ||
217 | |||
218 | if (err) | ||
219 | break; | ||
220 | |||
221 | if (i == 0) { | ||
222 | vinfo->vram_start = va->phys; | ||
223 | vinfo->vram_logical = (void __iomem *) va->logical; | ||
224 | vinfo->vram_contig_size = va->size; | ||
225 | vinfo->num_areas = 1; | ||
226 | } else { | ||
227 | contiguous = 0; | ||
228 | |||
229 | for (j = 0; j < i; ++j) { | ||
230 | va2 = &vinfo->vram[j]; | ||
231 | if (va->phys + va->size == va2->phys || | ||
232 | va2->phys + va2->size == va->phys) { | ||
233 | contiguous = 1; | ||
234 | break; | ||
235 | } | ||
236 | } | ||
237 | |||
238 | if (contiguous) { | ||
239 | vinfo->num_areas++; | ||
240 | if (va->phys < vinfo->vram_start) { | ||
241 | vinfo->vram_start = va->phys; | ||
242 | vinfo->vram_logical = | ||
243 | (void __iomem *)va->logical; | ||
244 | } | ||
245 | vinfo->vram_contig_size += va->size; | ||
246 | } else { | ||
247 | vmlfb_free_vram_area(va); | ||
248 | break; | ||
249 | } | ||
250 | } | ||
251 | |||
252 | if (requested < va->size) | ||
253 | break; | ||
254 | else | ||
255 | requested -= va->size; | ||
256 | } | ||
257 | |||
258 | if (vinfo->vram_contig_size > min_total && | ||
259 | vinfo->vram_contig_size > min_contig) { | ||
260 | |||
261 | printk(KERN_DEBUG MODULE_NAME | ||
262 | ": Contiguous vram: %ld bytes at physical 0x%08lx.\n", | ||
263 | (unsigned long)vinfo->vram_contig_size, | ||
264 | (unsigned long)vinfo->vram_start); | ||
265 | |||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | printk(KERN_ERR MODULE_NAME | ||
270 | ": Could not allocate requested minimal amount of vram.\n"); | ||
271 | |||
272 | vmlfb_free_vram(vinfo); | ||
273 | |||
274 | return -ENOMEM; | ||
275 | } | ||
276 | |||
277 | /* | ||
278 | * Find the GPU to use with our display controller. | ||
279 | */ | ||
280 | |||
281 | static int vmlfb_get_gpu(struct vml_par *par) | ||
282 | { | ||
283 | mutex_lock(&vml_mutex); | ||
284 | |||
285 | par->gpu = pci_get_device(PCI_VENDOR_ID_INTEL, VML_DEVICE_GPU, NULL); | ||
286 | |||
287 | if (!par->gpu) { | ||
288 | mutex_unlock(&vml_mutex); | ||
289 | return -ENODEV; | ||
290 | } | ||
291 | |||
292 | mutex_unlock(&vml_mutex); | ||
293 | |||
294 | if (pci_enable_device(par->gpu) < 0) | ||
295 | return -ENODEV; | ||
296 | |||
297 | return 0; | ||
298 | } | ||
299 | |||
300 | /* | ||
301 | * Find a contiguous vram area that contains a given offset from vram start. | ||
302 | */ | ||
303 | static int vmlfb_vram_offset(struct vml_info *vinfo, unsigned long offset) | ||
304 | { | ||
305 | unsigned long aoffset; | ||
306 | unsigned i; | ||
307 | |||
308 | for (i = 0; i < vinfo->num_areas; ++i) { | ||
309 | aoffset = offset - (vinfo->vram[i].phys - vinfo->vram_start); | ||
310 | |||
311 | if (aoffset < vinfo->vram[i].size) { | ||
312 | return 0; | ||
313 | } | ||
314 | } | ||
315 | |||
316 | return -EINVAL; | ||
317 | } | ||
318 | |||
319 | /* | ||
320 | * Remap the MMIO register spaces of the VDC and the GPU. | ||
321 | */ | ||
322 | |||
323 | static int vmlfb_enable_mmio(struct vml_par *par) | ||
324 | { | ||
325 | int err; | ||
326 | |||
327 | par->vdc_mem_base = pci_resource_start(par->vdc, 0); | ||
328 | par->vdc_mem_size = pci_resource_len(par->vdc, 0); | ||
329 | if (!request_mem_region(par->vdc_mem_base, par->vdc_mem_size, "vmlfb")) { | ||
330 | printk(KERN_ERR MODULE_NAME | ||
331 | ": Could not claim display controller MMIO.\n"); | ||
332 | return -EBUSY; | ||
333 | } | ||
334 | par->vdc_mem = ioremap_nocache(par->vdc_mem_base, par->vdc_mem_size); | ||
335 | if (par->vdc_mem == NULL) { | ||
336 | printk(KERN_ERR MODULE_NAME | ||
337 | ": Could not map display controller MMIO.\n"); | ||
338 | err = -ENOMEM; | ||
339 | goto out_err_0; | ||
340 | } | ||
341 | |||
342 | par->gpu_mem_base = pci_resource_start(par->gpu, 0); | ||
343 | par->gpu_mem_size = pci_resource_len(par->gpu, 0); | ||
344 | if (!request_mem_region(par->gpu_mem_base, par->gpu_mem_size, "vmlfb")) { | ||
345 | printk(KERN_ERR MODULE_NAME ": Could not claim GPU MMIO.\n"); | ||
346 | err = -EBUSY; | ||
347 | goto out_err_1; | ||
348 | } | ||
349 | par->gpu_mem = ioremap_nocache(par->gpu_mem_base, par->gpu_mem_size); | ||
350 | if (par->gpu_mem == NULL) { | ||
351 | printk(KERN_ERR MODULE_NAME ": Could not map GPU MMIO.\n"); | ||
352 | err = -ENOMEM; | ||
353 | goto out_err_2; | ||
354 | } | ||
355 | |||
356 | return 0; | ||
357 | |||
358 | out_err_2: | ||
359 | release_mem_region(par->gpu_mem_base, par->gpu_mem_size); | ||
360 | out_err_1: | ||
361 | iounmap(par->vdc_mem); | ||
362 | out_err_0: | ||
363 | release_mem_region(par->vdc_mem_base, par->vdc_mem_size); | ||
364 | return err; | ||
365 | } | ||
366 | |||
367 | /* | ||
368 | * Unmap the VDC and GPU register spaces. | ||
369 | */ | ||
370 | |||
371 | static void vmlfb_disable_mmio(struct vml_par *par) | ||
372 | { | ||
373 | iounmap(par->gpu_mem); | ||
374 | release_mem_region(par->gpu_mem_base, par->gpu_mem_size); | ||
375 | iounmap(par->vdc_mem); | ||
376 | release_mem_region(par->vdc_mem_base, par->vdc_mem_size); | ||
377 | } | ||
378 | |||
379 | /* | ||
380 | * Release and uninit the VDC and GPU. | ||
381 | */ | ||
382 | |||
383 | static void vmlfb_release_devices(struct vml_par *par) | ||
384 | { | ||
385 | if (atomic_dec_and_test(&par->refcount)) { | ||
386 | pci_disable_device(par->gpu); | ||
387 | pci_disable_device(par->vdc); | ||
388 | } | ||
389 | } | ||
390 | |||
391 | /* | ||
392 | * Free up allocated resources for a device. | ||
393 | */ | ||
394 | |||
395 | static void vml_pci_remove(struct pci_dev *dev) | ||
396 | { | ||
397 | struct fb_info *info; | ||
398 | struct vml_info *vinfo; | ||
399 | struct vml_par *par; | ||
400 | |||
401 | info = pci_get_drvdata(dev); | ||
402 | if (info) { | ||
403 | vinfo = container_of(info, struct vml_info, info); | ||
404 | par = vinfo->par; | ||
405 | mutex_lock(&vml_mutex); | ||
406 | unregister_framebuffer(info); | ||
407 | fb_dealloc_cmap(&info->cmap); | ||
408 | vmlfb_free_vram(vinfo); | ||
409 | vmlfb_disable_mmio(par); | ||
410 | vmlfb_release_devices(par); | ||
411 | kfree(vinfo); | ||
412 | kfree(par); | ||
413 | mutex_unlock(&vml_mutex); | ||
414 | } | ||
415 | } | ||
416 | |||
417 | static void vmlfb_set_pref_pixel_format(struct fb_var_screeninfo *var) | ||
418 | { | ||
419 | switch (var->bits_per_pixel) { | ||
420 | case 16: | ||
421 | var->blue.offset = 0; | ||
422 | var->blue.length = 5; | ||
423 | var->green.offset = 5; | ||
424 | var->green.length = 5; | ||
425 | var->red.offset = 10; | ||
426 | var->red.length = 5; | ||
427 | var->transp.offset = 15; | ||
428 | var->transp.length = 1; | ||
429 | break; | ||
430 | case 32: | ||
431 | var->blue.offset = 0; | ||
432 | var->blue.length = 8; | ||
433 | var->green.offset = 8; | ||
434 | var->green.length = 8; | ||
435 | var->red.offset = 16; | ||
436 | var->red.length = 8; | ||
437 | var->transp.offset = 24; | ||
438 | var->transp.length = 0; | ||
439 | break; | ||
440 | default: | ||
441 | break; | ||
442 | } | ||
443 | |||
444 | var->blue.msb_right = var->green.msb_right = | ||
445 | var->red.msb_right = var->transp.msb_right = 0; | ||
446 | } | ||
447 | |||
448 | /* | ||
449 | * Device initialization. | ||
450 | * We initialize one vml_par struct per device and one vml_info | ||
451 | * struct per pipe. Currently we have only one pipe. | ||
452 | */ | ||
453 | |||
454 | static int vml_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) | ||
455 | { | ||
456 | struct vml_info *vinfo; | ||
457 | struct fb_info *info; | ||
458 | struct vml_par *par; | ||
459 | int err = 0; | ||
460 | |||
461 | par = kzalloc(sizeof(*par), GFP_KERNEL); | ||
462 | if (par == NULL) | ||
463 | return -ENOMEM; | ||
464 | |||
465 | vinfo = kzalloc(sizeof(*vinfo), GFP_KERNEL); | ||
466 | if (vinfo == NULL) { | ||
467 | err = -ENOMEM; | ||
468 | goto out_err_0; | ||
469 | } | ||
470 | |||
471 | vinfo->par = par; | ||
472 | par->vdc = dev; | ||
473 | atomic_set(&par->refcount, 1); | ||
474 | |||
475 | switch (id->device) { | ||
476 | case VML_DEVICE_VDC: | ||
477 | if ((err = vmlfb_get_gpu(par))) | ||
478 | goto out_err_1; | ||
479 | pci_set_drvdata(dev, &vinfo->info); | ||
480 | break; | ||
481 | default: | ||
482 | err = -ENODEV; | ||
483 | goto out_err_1; | ||
484 | break; | ||
485 | } | ||
486 | |||
487 | info = &vinfo->info; | ||
488 | info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK; | ||
489 | |||
490 | err = vmlfb_enable_mmio(par); | ||
491 | if (err) | ||
492 | goto out_err_2; | ||
493 | |||
494 | err = vmlfb_alloc_vram(vinfo, vml_mem_requested, | ||
495 | vml_mem_contig, vml_mem_min); | ||
496 | if (err) | ||
497 | goto out_err_3; | ||
498 | |||
499 | strcpy(info->fix.id, "Vermilion Range"); | ||
500 | info->fix.mmio_start = 0; | ||
501 | info->fix.mmio_len = 0; | ||
502 | info->fix.smem_start = vinfo->vram_start; | ||
503 | info->fix.smem_len = vinfo->vram_contig_size; | ||
504 | info->fix.type = FB_TYPE_PACKED_PIXELS; | ||
505 | info->fix.visual = FB_VISUAL_TRUECOLOR; | ||
506 | info->fix.ypanstep = 1; | ||
507 | info->fix.xpanstep = 1; | ||
508 | info->fix.ywrapstep = 0; | ||
509 | info->fix.accel = FB_ACCEL_NONE; | ||
510 | info->screen_base = vinfo->vram_logical; | ||
511 | info->pseudo_palette = vinfo->pseudo_palette; | ||
512 | info->par = par; | ||
513 | info->fbops = &vmlfb_ops; | ||
514 | info->device = &dev->dev; | ||
515 | |||
516 | INIT_LIST_HEAD(&vinfo->head); | ||
517 | vinfo->pipe_disabled = 1; | ||
518 | vinfo->cur_blank_mode = FB_BLANK_UNBLANK; | ||
519 | |||
520 | info->var.grayscale = 0; | ||
521 | info->var.bits_per_pixel = 16; | ||
522 | vmlfb_set_pref_pixel_format(&info->var); | ||
523 | |||
524 | if (!fb_find_mode | ||
525 | (&info->var, info, vml_default_mode, NULL, 0, &defaultmode, 16)) { | ||
526 | printk(KERN_ERR MODULE_NAME ": Could not find initial mode\n"); | ||
527 | } | ||
528 | |||
529 | if (fb_alloc_cmap(&info->cmap, 256, 1) < 0) { | ||
530 | err = -ENOMEM; | ||
531 | goto out_err_4; | ||
532 | } | ||
533 | |||
534 | err = register_framebuffer(info); | ||
535 | if (err) { | ||
536 | printk(KERN_ERR MODULE_NAME ": Register framebuffer error.\n"); | ||
537 | goto out_err_5; | ||
538 | } | ||
539 | |||
540 | printk("Initialized vmlfb\n"); | ||
541 | |||
542 | return 0; | ||
543 | |||
544 | out_err_5: | ||
545 | fb_dealloc_cmap(&info->cmap); | ||
546 | out_err_4: | ||
547 | vmlfb_free_vram(vinfo); | ||
548 | out_err_3: | ||
549 | vmlfb_disable_mmio(par); | ||
550 | out_err_2: | ||
551 | vmlfb_release_devices(par); | ||
552 | out_err_1: | ||
553 | kfree(vinfo); | ||
554 | out_err_0: | ||
555 | kfree(par); | ||
556 | return err; | ||
557 | } | ||
558 | |||
559 | static int vmlfb_open(struct fb_info *info, int user) | ||
560 | { | ||
561 | /* | ||
562 | * Save registers here? | ||
563 | */ | ||
564 | return 0; | ||
565 | } | ||
566 | |||
567 | static int vmlfb_release(struct fb_info *info, int user) | ||
568 | { | ||
569 | /* | ||
570 | * Restore registers here. | ||
571 | */ | ||
572 | |||
573 | return 0; | ||
574 | } | ||
575 | |||
576 | static int vml_nearest_clock(int clock) | ||
577 | { | ||
578 | |||
579 | int i; | ||
580 | int cur_index; | ||
581 | int cur_diff; | ||
582 | int diff; | ||
583 | |||
584 | cur_index = 0; | ||
585 | cur_diff = clock - vml_clocks[0]; | ||
586 | cur_diff = (cur_diff < 0) ? -cur_diff : cur_diff; | ||
587 | for (i = 1; i < vml_num_clocks; ++i) { | ||
588 | diff = clock - vml_clocks[i]; | ||
589 | diff = (diff < 0) ? -diff : diff; | ||
590 | if (diff < cur_diff) { | ||
591 | cur_index = i; | ||
592 | cur_diff = diff; | ||
593 | } | ||
594 | } | ||
595 | return vml_clocks[cur_index]; | ||
596 | } | ||
597 | |||
598 | static int vmlfb_check_var_locked(struct fb_var_screeninfo *var, | ||
599 | struct vml_info *vinfo) | ||
600 | { | ||
601 | u32 pitch; | ||
602 | u64 mem; | ||
603 | int nearest_clock; | ||
604 | int clock; | ||
605 | int clock_diff; | ||
606 | struct fb_var_screeninfo v; | ||
607 | |||
608 | v = *var; | ||
609 | clock = PICOS2KHZ(var->pixclock); | ||
610 | |||
611 | if (subsys && subsys->nearest_clock) { | ||
612 | nearest_clock = subsys->nearest_clock(subsys, clock); | ||
613 | } else { | ||
614 | nearest_clock = vml_nearest_clock(clock); | ||
615 | } | ||
616 | |||
617 | /* | ||
618 | * Accept a 20% diff. | ||
619 | */ | ||
620 | |||
621 | clock_diff = nearest_clock - clock; | ||
622 | clock_diff = (clock_diff < 0) ? -clock_diff : clock_diff; | ||
623 | if (clock_diff > clock / 5) { | ||
624 | #if 0 | ||
625 | printk(KERN_DEBUG MODULE_NAME ": Diff failure. %d %d\n",clock_diff,clock); | ||
626 | #endif | ||
627 | return -EINVAL; | ||
628 | } | ||
629 | |||
630 | v.pixclock = KHZ2PICOS(nearest_clock); | ||
631 | |||
632 | if (var->xres > VML_MAX_XRES || var->yres > VML_MAX_YRES) { | ||
633 | printk(KERN_DEBUG MODULE_NAME ": Resolution failure.\n"); | ||
634 | return -EINVAL; | ||
635 | } | ||
636 | if (var->xres_virtual > VML_MAX_XRES_VIRTUAL) { | ||
637 | printk(KERN_DEBUG MODULE_NAME | ||
638 | ": Virtual resolution failure.\n"); | ||
639 | return -EINVAL; | ||
640 | } | ||
641 | switch (v.bits_per_pixel) { | ||
642 | case 0 ... 16: | ||
643 | v.bits_per_pixel = 16; | ||
644 | break; | ||
645 | case 17 ... 32: | ||
646 | v.bits_per_pixel = 32; | ||
647 | break; | ||
648 | default: | ||
649 | printk(KERN_DEBUG MODULE_NAME ": Invalid bpp: %d.\n", | ||
650 | var->bits_per_pixel); | ||
651 | return -EINVAL; | ||
652 | } | ||
653 | |||
654 | pitch = ALIGN((var->xres * var->bits_per_pixel) >> 3, 0x40); | ||
655 | mem = pitch * var->yres_virtual; | ||
656 | if (mem > vinfo->vram_contig_size) { | ||
657 | return -ENOMEM; | ||
658 | } | ||
659 | |||
660 | switch (v.bits_per_pixel) { | ||
661 | case 16: | ||
662 | if (var->blue.offset != 0 || | ||
663 | var->blue.length != 5 || | ||
664 | var->green.offset != 5 || | ||
665 | var->green.length != 5 || | ||
666 | var->red.offset != 10 || | ||
667 | var->red.length != 5 || | ||
668 | var->transp.offset != 15 || var->transp.length != 1) { | ||
669 | vmlfb_set_pref_pixel_format(&v); | ||
670 | } | ||
671 | break; | ||
672 | case 32: | ||
673 | if (var->blue.offset != 0 || | ||
674 | var->blue.length != 8 || | ||
675 | var->green.offset != 8 || | ||
676 | var->green.length != 8 || | ||
677 | var->red.offset != 16 || | ||
678 | var->red.length != 8 || | ||
679 | (var->transp.length != 0 && var->transp.length != 8) || | ||
680 | (var->transp.length == 8 && var->transp.offset != 24)) { | ||
681 | vmlfb_set_pref_pixel_format(&v); | ||
682 | } | ||
683 | break; | ||
684 | default: | ||
685 | return -EINVAL; | ||
686 | } | ||
687 | |||
688 | *var = v; | ||
689 | |||
690 | return 0; | ||
691 | } | ||
692 | |||
693 | static int vmlfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | ||
694 | { | ||
695 | struct vml_info *vinfo = container_of(info, struct vml_info, info); | ||
696 | int ret; | ||
697 | |||
698 | mutex_lock(&vml_mutex); | ||
699 | ret = vmlfb_check_var_locked(var, vinfo); | ||
700 | mutex_unlock(&vml_mutex); | ||
701 | |||
702 | return ret; | ||
703 | } | ||
704 | |||
705 | static void vml_wait_vblank(struct vml_info *vinfo) | ||
706 | { | ||
707 | /* Wait for vblank. For now, just wait for a 50Hz cycle (20ms)) */ | ||
708 | mdelay(20); | ||
709 | } | ||
710 | |||
711 | static void vmlfb_disable_pipe(struct vml_info *vinfo) | ||
712 | { | ||
713 | struct vml_par *par = vinfo->par; | ||
714 | |||
715 | /* Disable the MDVO pad */ | ||
716 | VML_WRITE32(par, VML_RCOMPSTAT, 0); | ||
717 | while (!(VML_READ32(par, VML_RCOMPSTAT) & VML_MDVO_VDC_I_RCOMP)) ; | ||
718 | |||
719 | /* Disable display planes */ | ||
720 | VML_WRITE32(par, VML_DSPCCNTR, | ||
721 | VML_READ32(par, VML_DSPCCNTR) & ~VML_GFX_ENABLE); | ||
722 | (void)VML_READ32(par, VML_DSPCCNTR); | ||
723 | /* Wait for vblank for the disable to take effect */ | ||
724 | vml_wait_vblank(vinfo); | ||
725 | |||
726 | /* Next, disable display pipes */ | ||
727 | VML_WRITE32(par, VML_PIPEACONF, 0); | ||
728 | (void)VML_READ32(par, VML_PIPEACONF); | ||
729 | |||
730 | vinfo->pipe_disabled = 1; | ||
731 | } | ||
732 | |||
733 | #ifdef VERMILION_DEBUG | ||
734 | static void vml_dump_regs(struct vml_info *vinfo) | ||
735 | { | ||
736 | struct vml_par *par = vinfo->par; | ||
737 | |||
738 | printk(KERN_DEBUG MODULE_NAME ": Modesetting register dump:\n"); | ||
739 | printk(KERN_DEBUG MODULE_NAME ": \tHTOTAL_A : 0x%08x\n", | ||
740 | (unsigned)VML_READ32(par, VML_HTOTAL_A)); | ||
741 | printk(KERN_DEBUG MODULE_NAME ": \tHBLANK_A : 0x%08x\n", | ||
742 | (unsigned)VML_READ32(par, VML_HBLANK_A)); | ||
743 | printk(KERN_DEBUG MODULE_NAME ": \tHSYNC_A : 0x%08x\n", | ||
744 | (unsigned)VML_READ32(par, VML_HSYNC_A)); | ||
745 | printk(KERN_DEBUG MODULE_NAME ": \tVTOTAL_A : 0x%08x\n", | ||
746 | (unsigned)VML_READ32(par, VML_VTOTAL_A)); | ||
747 | printk(KERN_DEBUG MODULE_NAME ": \tVBLANK_A : 0x%08x\n", | ||
748 | (unsigned)VML_READ32(par, VML_VBLANK_A)); | ||
749 | printk(KERN_DEBUG MODULE_NAME ": \tVSYNC_A : 0x%08x\n", | ||
750 | (unsigned)VML_READ32(par, VML_VSYNC_A)); | ||
751 | printk(KERN_DEBUG MODULE_NAME ": \tDSPCSTRIDE : 0x%08x\n", | ||
752 | (unsigned)VML_READ32(par, VML_DSPCSTRIDE)); | ||
753 | printk(KERN_DEBUG MODULE_NAME ": \tDSPCSIZE : 0x%08x\n", | ||
754 | (unsigned)VML_READ32(par, VML_DSPCSIZE)); | ||
755 | printk(KERN_DEBUG MODULE_NAME ": \tDSPCPOS : 0x%08x\n", | ||
756 | (unsigned)VML_READ32(par, VML_DSPCPOS)); | ||
757 | printk(KERN_DEBUG MODULE_NAME ": \tDSPARB : 0x%08x\n", | ||
758 | (unsigned)VML_READ32(par, VML_DSPARB)); | ||
759 | printk(KERN_DEBUG MODULE_NAME ": \tDSPCADDR : 0x%08x\n", | ||
760 | (unsigned)VML_READ32(par, VML_DSPCADDR)); | ||
761 | printk(KERN_DEBUG MODULE_NAME ": \tBCLRPAT_A : 0x%08x\n", | ||
762 | (unsigned)VML_READ32(par, VML_BCLRPAT_A)); | ||
763 | printk(KERN_DEBUG MODULE_NAME ": \tCANVSCLR_A : 0x%08x\n", | ||
764 | (unsigned)VML_READ32(par, VML_CANVSCLR_A)); | ||
765 | printk(KERN_DEBUG MODULE_NAME ": \tPIPEASRC : 0x%08x\n", | ||
766 | (unsigned)VML_READ32(par, VML_PIPEASRC)); | ||
767 | printk(KERN_DEBUG MODULE_NAME ": \tPIPEACONF : 0x%08x\n", | ||
768 | (unsigned)VML_READ32(par, VML_PIPEACONF)); | ||
769 | printk(KERN_DEBUG MODULE_NAME ": \tDSPCCNTR : 0x%08x\n", | ||
770 | (unsigned)VML_READ32(par, VML_DSPCCNTR)); | ||
771 | printk(KERN_DEBUG MODULE_NAME ": \tRCOMPSTAT : 0x%08x\n", | ||
772 | (unsigned)VML_READ32(par, VML_RCOMPSTAT)); | ||
773 | printk(KERN_DEBUG MODULE_NAME ": End of modesetting register dump.\n"); | ||
774 | } | ||
775 | #endif | ||
776 | |||
777 | static int vmlfb_set_par_locked(struct vml_info *vinfo) | ||
778 | { | ||
779 | struct vml_par *par = vinfo->par; | ||
780 | struct fb_info *info = &vinfo->info; | ||
781 | struct fb_var_screeninfo *var = &info->var; | ||
782 | u32 htotal, hactive, hblank_start, hblank_end, hsync_start, hsync_end; | ||
783 | u32 vtotal, vactive, vblank_start, vblank_end, vsync_start, vsync_end; | ||
784 | u32 dspcntr; | ||
785 | int clock; | ||
786 | |||
787 | vinfo->bytes_per_pixel = var->bits_per_pixel >> 3; | ||
788 | vinfo->stride = ALIGN(var->xres_virtual * vinfo->bytes_per_pixel, 0x40); | ||
789 | info->fix.line_length = vinfo->stride; | ||
790 | |||
791 | if (!subsys) | ||
792 | return 0; | ||
793 | |||
794 | htotal = | ||
795 | var->xres + var->right_margin + var->hsync_len + var->left_margin; | ||
796 | hactive = var->xres; | ||
797 | hblank_start = var->xres; | ||
798 | hblank_end = htotal; | ||
799 | hsync_start = hactive + var->right_margin; | ||
800 | hsync_end = hsync_start + var->hsync_len; | ||
801 | |||
802 | vtotal = | ||
803 | var->yres + var->lower_margin + var->vsync_len + var->upper_margin; | ||
804 | vactive = var->yres; | ||
805 | vblank_start = var->yres; | ||
806 | vblank_end = vtotal; | ||
807 | vsync_start = vactive + var->lower_margin; | ||
808 | vsync_end = vsync_start + var->vsync_len; | ||
809 | |||
810 | dspcntr = VML_GFX_ENABLE | VML_GFX_GAMMABYPASS; | ||
811 | clock = PICOS2KHZ(var->pixclock); | ||
812 | |||
813 | if (subsys->nearest_clock) { | ||
814 | clock = subsys->nearest_clock(subsys, clock); | ||
815 | } else { | ||
816 | clock = vml_nearest_clock(clock); | ||
817 | } | ||
818 | printk(KERN_DEBUG MODULE_NAME | ||
819 | ": Set mode Hfreq : %d kHz, Vfreq : %d Hz.\n", clock / htotal, | ||
820 | ((clock / htotal) * 1000) / vtotal); | ||
821 | |||
822 | switch (var->bits_per_pixel) { | ||
823 | case 16: | ||
824 | dspcntr |= VML_GFX_ARGB1555; | ||
825 | break; | ||
826 | case 32: | ||
827 | if (var->transp.length == 8) | ||
828 | dspcntr |= VML_GFX_ARGB8888 | VML_GFX_ALPHAMULT; | ||
829 | else | ||
830 | dspcntr |= VML_GFX_RGB0888; | ||
831 | break; | ||
832 | default: | ||
833 | return -EINVAL; | ||
834 | } | ||
835 | |||
836 | vmlfb_disable_pipe(vinfo); | ||
837 | mb(); | ||
838 | |||
839 | if (subsys->set_clock) | ||
840 | subsys->set_clock(subsys, clock); | ||
841 | else | ||
842 | return -EINVAL; | ||
843 | |||
844 | VML_WRITE32(par, VML_HTOTAL_A, ((htotal - 1) << 16) | (hactive - 1)); | ||
845 | VML_WRITE32(par, VML_HBLANK_A, | ||
846 | ((hblank_end - 1) << 16) | (hblank_start - 1)); | ||
847 | VML_WRITE32(par, VML_HSYNC_A, | ||
848 | ((hsync_end - 1) << 16) | (hsync_start - 1)); | ||
849 | VML_WRITE32(par, VML_VTOTAL_A, ((vtotal - 1) << 16) | (vactive - 1)); | ||
850 | VML_WRITE32(par, VML_VBLANK_A, | ||
851 | ((vblank_end - 1) << 16) | (vblank_start - 1)); | ||
852 | VML_WRITE32(par, VML_VSYNC_A, | ||
853 | ((vsync_end - 1) << 16) | (vsync_start - 1)); | ||
854 | VML_WRITE32(par, VML_DSPCSTRIDE, vinfo->stride); | ||
855 | VML_WRITE32(par, VML_DSPCSIZE, | ||
856 | ((var->yres - 1) << 16) | (var->xres - 1)); | ||
857 | VML_WRITE32(par, VML_DSPCPOS, 0x00000000); | ||
858 | VML_WRITE32(par, VML_DSPARB, VML_FIFO_DEFAULT); | ||
859 | VML_WRITE32(par, VML_BCLRPAT_A, 0x00000000); | ||
860 | VML_WRITE32(par, VML_CANVSCLR_A, 0x00000000); | ||
861 | VML_WRITE32(par, VML_PIPEASRC, | ||
862 | ((var->xres - 1) << 16) | (var->yres - 1)); | ||
863 | |||
864 | wmb(); | ||
865 | VML_WRITE32(par, VML_PIPEACONF, VML_PIPE_ENABLE); | ||
866 | wmb(); | ||
867 | VML_WRITE32(par, VML_DSPCCNTR, dspcntr); | ||
868 | wmb(); | ||
869 | VML_WRITE32(par, VML_DSPCADDR, (u32) vinfo->vram_start + | ||
870 | var->yoffset * vinfo->stride + | ||
871 | var->xoffset * vinfo->bytes_per_pixel); | ||
872 | |||
873 | VML_WRITE32(par, VML_RCOMPSTAT, VML_MDVO_PAD_ENABLE); | ||
874 | |||
875 | while (!(VML_READ32(par, VML_RCOMPSTAT) & | ||
876 | (VML_MDVO_VDC_I_RCOMP | VML_MDVO_PAD_ENABLE))) ; | ||
877 | |||
878 | vinfo->pipe_disabled = 0; | ||
879 | #ifdef VERMILION_DEBUG | ||
880 | vml_dump_regs(vinfo); | ||
881 | #endif | ||
882 | |||
883 | return 0; | ||
884 | } | ||
885 | |||
886 | static int vmlfb_set_par(struct fb_info *info) | ||
887 | { | ||
888 | struct vml_info *vinfo = container_of(info, struct vml_info, info); | ||
889 | int ret; | ||
890 | |||
891 | mutex_lock(&vml_mutex); | ||
892 | list_move(&vinfo->head, (subsys) ? &global_has_mode : &global_no_mode); | ||
893 | ret = vmlfb_set_par_locked(vinfo); | ||
894 | |||
895 | mutex_unlock(&vml_mutex); | ||
896 | return ret; | ||
897 | } | ||
898 | |||
899 | static int vmlfb_blank_locked(struct vml_info *vinfo) | ||
900 | { | ||
901 | struct vml_par *par = vinfo->par; | ||
902 | u32 cur = VML_READ32(par, VML_PIPEACONF); | ||
903 | |||
904 | switch (vinfo->cur_blank_mode) { | ||
905 | case FB_BLANK_UNBLANK: | ||
906 | if (vinfo->pipe_disabled) { | ||
907 | vmlfb_set_par_locked(vinfo); | ||
908 | } | ||
909 | VML_WRITE32(par, VML_PIPEACONF, cur & ~VML_PIPE_FORCE_BORDER); | ||
910 | (void)VML_READ32(par, VML_PIPEACONF); | ||
911 | break; | ||
912 | case FB_BLANK_NORMAL: | ||
913 | if (vinfo->pipe_disabled) { | ||
914 | vmlfb_set_par_locked(vinfo); | ||
915 | } | ||
916 | VML_WRITE32(par, VML_PIPEACONF, cur | VML_PIPE_FORCE_BORDER); | ||
917 | (void)VML_READ32(par, VML_PIPEACONF); | ||
918 | break; | ||
919 | case FB_BLANK_VSYNC_SUSPEND: | ||
920 | case FB_BLANK_HSYNC_SUSPEND: | ||
921 | if (!vinfo->pipe_disabled) { | ||
922 | vmlfb_disable_pipe(vinfo); | ||
923 | } | ||
924 | break; | ||
925 | case FB_BLANK_POWERDOWN: | ||
926 | if (!vinfo->pipe_disabled) { | ||
927 | vmlfb_disable_pipe(vinfo); | ||
928 | } | ||
929 | break; | ||
930 | default: | ||
931 | return -EINVAL; | ||
932 | } | ||
933 | |||
934 | return 0; | ||
935 | } | ||
936 | |||
937 | static int vmlfb_blank(int blank_mode, struct fb_info *info) | ||
938 | { | ||
939 | struct vml_info *vinfo = container_of(info, struct vml_info, info); | ||
940 | int ret; | ||
941 | |||
942 | mutex_lock(&vml_mutex); | ||
943 | vinfo->cur_blank_mode = blank_mode; | ||
944 | ret = vmlfb_blank_locked(vinfo); | ||
945 | mutex_unlock(&vml_mutex); | ||
946 | return ret; | ||
947 | } | ||
948 | |||
949 | static int vmlfb_pan_display(struct fb_var_screeninfo *var, | ||
950 | struct fb_info *info) | ||
951 | { | ||
952 | struct vml_info *vinfo = container_of(info, struct vml_info, info); | ||
953 | struct vml_par *par = vinfo->par; | ||
954 | |||
955 | mutex_lock(&vml_mutex); | ||
956 | VML_WRITE32(par, VML_DSPCADDR, (u32) vinfo->vram_start + | ||
957 | var->yoffset * vinfo->stride + | ||
958 | var->xoffset * vinfo->bytes_per_pixel); | ||
959 | (void)VML_READ32(par, VML_DSPCADDR); | ||
960 | mutex_unlock(&vml_mutex); | ||
961 | |||
962 | return 0; | ||
963 | } | ||
964 | |||
965 | static int vmlfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, | ||
966 | u_int transp, struct fb_info *info) | ||
967 | { | ||
968 | u32 v; | ||
969 | |||
970 | if (regno >= 16) | ||
971 | return -EINVAL; | ||
972 | |||
973 | if (info->var.grayscale) { | ||
974 | red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; | ||
975 | } | ||
976 | |||
977 | if (info->fix.visual != FB_VISUAL_TRUECOLOR) | ||
978 | return -EINVAL; | ||
979 | |||
980 | red = VML_TOHW(red, info->var.red.length); | ||
981 | blue = VML_TOHW(blue, info->var.blue.length); | ||
982 | green = VML_TOHW(green, info->var.green.length); | ||
983 | transp = VML_TOHW(transp, info->var.transp.length); | ||
984 | |||
985 | v = (red << info->var.red.offset) | | ||
986 | (green << info->var.green.offset) | | ||
987 | (blue << info->var.blue.offset) | | ||
988 | (transp << info->var.transp.offset); | ||
989 | |||
990 | switch (info->var.bits_per_pixel) { | ||
991 | case 16: | ||
992 | ((u32 *) info->pseudo_palette)[regno] = v; | ||
993 | break; | ||
994 | case 24: | ||
995 | case 32: | ||
996 | ((u32 *) info->pseudo_palette)[regno] = v; | ||
997 | break; | ||
998 | } | ||
999 | return 0; | ||
1000 | } | ||
1001 | |||
1002 | static int vmlfb_mmap(struct fb_info *info, struct vm_area_struct *vma) | ||
1003 | { | ||
1004 | struct vml_info *vinfo = container_of(info, struct vml_info, info); | ||
1005 | unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; | ||
1006 | int ret; | ||
1007 | |||
1008 | ret = vmlfb_vram_offset(vinfo, offset); | ||
1009 | if (ret) | ||
1010 | return -EINVAL; | ||
1011 | |||
1012 | pgprot_val(vma->vm_page_prot) |= _PAGE_PCD; | ||
1013 | pgprot_val(vma->vm_page_prot) &= ~_PAGE_PWT; | ||
1014 | |||
1015 | return vm_iomap_memory(vma, vinfo->vram_start, | ||
1016 | vinfo->vram_contig_size); | ||
1017 | } | ||
1018 | |||
1019 | static int vmlfb_sync(struct fb_info *info) | ||
1020 | { | ||
1021 | return 0; | ||
1022 | } | ||
1023 | |||
1024 | static int vmlfb_cursor(struct fb_info *info, struct fb_cursor *cursor) | ||
1025 | { | ||
1026 | return -EINVAL; /* just to force soft_cursor() call */ | ||
1027 | } | ||
1028 | |||
1029 | static struct fb_ops vmlfb_ops = { | ||
1030 | .owner = THIS_MODULE, | ||
1031 | .fb_open = vmlfb_open, | ||
1032 | .fb_release = vmlfb_release, | ||
1033 | .fb_check_var = vmlfb_check_var, | ||
1034 | .fb_set_par = vmlfb_set_par, | ||
1035 | .fb_blank = vmlfb_blank, | ||
1036 | .fb_pan_display = vmlfb_pan_display, | ||
1037 | .fb_fillrect = cfb_fillrect, | ||
1038 | .fb_copyarea = cfb_copyarea, | ||
1039 | .fb_imageblit = cfb_imageblit, | ||
1040 | .fb_cursor = vmlfb_cursor, | ||
1041 | .fb_sync = vmlfb_sync, | ||
1042 | .fb_mmap = vmlfb_mmap, | ||
1043 | .fb_setcolreg = vmlfb_setcolreg | ||
1044 | }; | ||
1045 | |||
1046 | static struct pci_device_id vml_ids[] = { | ||
1047 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, VML_DEVICE_VDC)}, | ||
1048 | {0} | ||
1049 | }; | ||
1050 | |||
1051 | static struct pci_driver vmlfb_pci_driver = { | ||
1052 | .name = "vmlfb", | ||
1053 | .id_table = vml_ids, | ||
1054 | .probe = vml_pci_probe, | ||
1055 | .remove = vml_pci_remove, | ||
1056 | }; | ||
1057 | |||
1058 | static void __exit vmlfb_cleanup(void) | ||
1059 | { | ||
1060 | pci_unregister_driver(&vmlfb_pci_driver); | ||
1061 | } | ||
1062 | |||
1063 | static int __init vmlfb_init(void) | ||
1064 | { | ||
1065 | |||
1066 | #ifndef MODULE | ||
1067 | char *option = NULL; | ||
1068 | |||
1069 | if (fb_get_options(MODULE_NAME, &option)) | ||
1070 | return -ENODEV; | ||
1071 | #endif | ||
1072 | |||
1073 | printk(KERN_DEBUG MODULE_NAME ": initializing\n"); | ||
1074 | mutex_init(&vml_mutex); | ||
1075 | INIT_LIST_HEAD(&global_no_mode); | ||
1076 | INIT_LIST_HEAD(&global_has_mode); | ||
1077 | |||
1078 | return pci_register_driver(&vmlfb_pci_driver); | ||
1079 | } | ||
1080 | |||
1081 | int vmlfb_register_subsys(struct vml_sys *sys) | ||
1082 | { | ||
1083 | struct vml_info *entry; | ||
1084 | struct list_head *list; | ||
1085 | u32 save_activate; | ||
1086 | |||
1087 | mutex_lock(&vml_mutex); | ||
1088 | if (subsys != NULL) { | ||
1089 | subsys->restore(subsys); | ||
1090 | } | ||
1091 | subsys = sys; | ||
1092 | subsys->save(subsys); | ||
1093 | |||
1094 | /* | ||
1095 | * We need to restart list traversal for each item, since we | ||
1096 | * release the list mutex in the loop. | ||
1097 | */ | ||
1098 | |||
1099 | list = global_no_mode.next; | ||
1100 | while (list != &global_no_mode) { | ||
1101 | list_del_init(list); | ||
1102 | entry = list_entry(list, struct vml_info, head); | ||
1103 | |||
1104 | /* | ||
1105 | * First, try the current mode which might not be | ||
1106 | * completely validated with respect to the pixel clock. | ||
1107 | */ | ||
1108 | |||
1109 | if (!vmlfb_check_var_locked(&entry->info.var, entry)) { | ||
1110 | vmlfb_set_par_locked(entry); | ||
1111 | list_add_tail(list, &global_has_mode); | ||
1112 | } else { | ||
1113 | |||
1114 | /* | ||
1115 | * Didn't work. Try to find another mode, | ||
1116 | * that matches this subsys. | ||
1117 | */ | ||
1118 | |||
1119 | mutex_unlock(&vml_mutex); | ||
1120 | save_activate = entry->info.var.activate; | ||
1121 | entry->info.var.bits_per_pixel = 16; | ||
1122 | vmlfb_set_pref_pixel_format(&entry->info.var); | ||
1123 | if (fb_find_mode(&entry->info.var, | ||
1124 | &entry->info, | ||
1125 | vml_default_mode, NULL, 0, NULL, 16)) { | ||
1126 | entry->info.var.activate |= | ||
1127 | FB_ACTIVATE_FORCE | FB_ACTIVATE_NOW; | ||
1128 | fb_set_var(&entry->info, &entry->info.var); | ||
1129 | } else { | ||
1130 | printk(KERN_ERR MODULE_NAME | ||
1131 | ": Sorry. no mode found for this subsys.\n"); | ||
1132 | } | ||
1133 | entry->info.var.activate = save_activate; | ||
1134 | mutex_lock(&vml_mutex); | ||
1135 | } | ||
1136 | vmlfb_blank_locked(entry); | ||
1137 | list = global_no_mode.next; | ||
1138 | } | ||
1139 | mutex_unlock(&vml_mutex); | ||
1140 | |||
1141 | printk(KERN_DEBUG MODULE_NAME ": Registered %s subsystem.\n", | ||
1142 | subsys->name ? subsys->name : "unknown"); | ||
1143 | return 0; | ||
1144 | } | ||
1145 | |||
1146 | EXPORT_SYMBOL_GPL(vmlfb_register_subsys); | ||
1147 | |||
1148 | void vmlfb_unregister_subsys(struct vml_sys *sys) | ||
1149 | { | ||
1150 | struct vml_info *entry, *next; | ||
1151 | |||
1152 | mutex_lock(&vml_mutex); | ||
1153 | if (subsys != sys) { | ||
1154 | mutex_unlock(&vml_mutex); | ||
1155 | return; | ||
1156 | } | ||
1157 | subsys->restore(subsys); | ||
1158 | subsys = NULL; | ||
1159 | list_for_each_entry_safe(entry, next, &global_has_mode, head) { | ||
1160 | printk(KERN_DEBUG MODULE_NAME ": subsys disable pipe\n"); | ||
1161 | vmlfb_disable_pipe(entry); | ||
1162 | list_move_tail(&entry->head, &global_no_mode); | ||
1163 | } | ||
1164 | mutex_unlock(&vml_mutex); | ||
1165 | } | ||
1166 | |||
1167 | EXPORT_SYMBOL_GPL(vmlfb_unregister_subsys); | ||
1168 | |||
1169 | module_init(vmlfb_init); | ||
1170 | module_exit(vmlfb_cleanup); | ||
1171 | |||
1172 | MODULE_AUTHOR("Tungsten Graphics"); | ||
1173 | MODULE_DESCRIPTION("Initialization of the Vermilion display devices"); | ||
1174 | MODULE_VERSION("1.0.0"); | ||
1175 | MODULE_LICENSE("GPL"); | ||