xref: /openbmc/linux/drivers/video/fbdev/vermilion/vermilion.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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