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