diff options
author | Alan Hourihane <alanh@fairlite.demon.co.uk> | 2007-05-08 03:39:25 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-05-08 14:15:32 -0400 |
commit | dbe7e429fedb3fbc93b496cc1c3eb4fc28333ac0 (patch) | |
tree | 9f88a999af677f65beb7041604c3a5d63bfc58db /drivers | |
parent | 249bdbbf0dbab5554a4bfe55639e324d4758da96 (diff) |
vmlfb: framebuffer driver for Intel Vermilion Range
Add the Intel Vermilion Range framebuffer support.
Signed-off-by: Alan Hourihane <alanh@tungstengraphics.com>
Signed-off-by: Antonino Daplas <adaplas@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/video/Kconfig | 16 | ||||
-rw-r--r-- | drivers/video/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/backlight/Kconfig | 8 | ||||
-rw-r--r-- | drivers/video/backlight/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/backlight/cr_bllcd.c | 287 | ||||
-rw-r--r-- | drivers/video/vermilion/Makefile | 5 | ||||
-rw-r--r-- | drivers/video/vermilion/cr_pll.c | 208 | ||||
-rw-r--r-- | drivers/video/vermilion/vermilion.c | 1195 | ||||
-rw-r--r-- | drivers/video/vermilion/vermilion.h | 260 |
9 files changed, 1981 insertions, 0 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 863c5983ee60..74d764e2e1fa 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig | |||
@@ -882,6 +882,22 @@ config FB_I810_I2C | |||
882 | select FB_DDC | 882 | select FB_DDC |
883 | help | 883 | help |
884 | 884 | ||
885 | config FB_LE80578 | ||
886 | tristate "Intel LE80578 (Vermilion) support" | ||
887 | depends on FB && PCI && X86 | ||
888 | select FB_MODE_HELPERS | ||
889 | select FB_CFB_FILLRECT | ||
890 | select FB_CFB_COPYAREA | ||
891 | select FB_CFB_IMAGEBLIT | ||
892 | help | ||
893 | This driver supports the LE80578 (Vermilion Range) chipset | ||
894 | |||
895 | config FB_CARILLO_RANCH | ||
896 | tristate "Intel Carillo Ranch support" | ||
897 | depends on FB_LE80578 && FB && PCI && X86 | ||
898 | help | ||
899 | This driver supports the LE80578 (Carillo Ranch) board | ||
900 | |||
885 | config FB_INTEL | 901 | config FB_INTEL |
886 | tristate "Intel 830M/845G/852GM/855GM/865G/915G/945G support (EXPERIMENTAL)" | 902 | tristate "Intel 830M/845G/852GM/855GM/865G/915G/945G support (EXPERIMENTAL)" |
887 | depends on FB && EXPERIMENTAL && PCI && X86 | 903 | depends on FB && EXPERIMENTAL && PCI && X86 |
diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 6c7b26e81fc2..a59395d6189d 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile | |||
@@ -56,6 +56,7 @@ obj-$(CONFIG_FB_IMSTT) += imsttfb.o | |||
56 | obj-$(CONFIG_FB_FM2) += fm2fb.o | 56 | obj-$(CONFIG_FB_FM2) += fm2fb.o |
57 | obj-$(CONFIG_FB_CYBLA) += cyblafb.o | 57 | obj-$(CONFIG_FB_CYBLA) += cyblafb.o |
58 | obj-$(CONFIG_FB_TRIDENT) += tridentfb.o | 58 | obj-$(CONFIG_FB_TRIDENT) += tridentfb.o |
59 | obj-$(CONFIG_FB_LE80578) += vermilion/ | ||
59 | obj-$(CONFIG_FB_S3) += s3fb.o | 60 | obj-$(CONFIG_FB_S3) += s3fb.o |
60 | obj-$(CONFIG_FB_STI) += stifb.o | 61 | obj-$(CONFIG_FB_STI) += stifb.o |
61 | obj-$(CONFIG_FB_FFB) += ffb.o sbuslib.o | 62 | obj-$(CONFIG_FB_FFB) += ffb.o sbuslib.o |
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 47d15b5d985a..fbef663fc057 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig | |||
@@ -63,3 +63,11 @@ config BACKLIGHT_PROGEAR | |||
63 | help | 63 | help |
64 | If you have a Frontpath ProGear say Y to enable the | 64 | If you have a Frontpath ProGear say Y to enable the |
65 | backlight driver. | 65 | backlight driver. |
66 | |||
67 | config BACKLIGHT_CARILLO_RANCH | ||
68 | tristate "Intel Carillo Ranch Backlight Driver" | ||
69 | depends on BACKLIGHT_CLASS_DEVICE && LCD_CLASS_DEVICE && PCI && X86 && FB_LE80578 | ||
70 | default n | ||
71 | help | ||
72 | If you have a Intel LE80578 (Carillo Ranch) say Y to enable the | ||
73 | backlight driver. | ||
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 0c3ce46f5094..c6e2266f63e2 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile | |||
@@ -6,3 +6,4 @@ obj-$(CONFIG_BACKLIGHT_CORGI) += corgi_bl.o | |||
6 | obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o | 6 | obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o |
7 | obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o | 7 | obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o |
8 | obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o | 8 | obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o |
9 | obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o | ||
diff --git a/drivers/video/backlight/cr_bllcd.c b/drivers/video/backlight/cr_bllcd.c new file mode 100644 index 000000000000..e9bbc3455c94 --- /dev/null +++ b/drivers/video/backlight/cr_bllcd.c | |||
@@ -0,0 +1,287 @@ | |||
1 | /* | ||
2 | * Copyright (c) Intel Corp. 2007. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to | ||
6 | * develop this driver. | ||
7 | * | ||
8 | * This file is part of the Carillo Ranch video subsystem driver. | ||
9 | * The Carillo Ranch video subsystem driver is free software; | ||
10 | * you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * The Carillo Ranch video subsystem driver is distributed | ||
16 | * in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this driver; if not, write to the Free Software | ||
23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
24 | * | ||
25 | * Authors: | ||
26 | * Thomas Hellstrom <thomas-at-tungstengraphics-dot-com> | ||
27 | * Alan Hourihane <alanh-at-tungstengraphics-dot-com> | ||
28 | */ | ||
29 | |||
30 | #include <linux/module.h> | ||
31 | #include <linux/kernel.h> | ||
32 | #include <linux/init.h> | ||
33 | #include <linux/platform_device.h> | ||
34 | #include <linux/mutex.h> | ||
35 | #include <linux/fb.h> | ||
36 | #include <linux/backlight.h> | ||
37 | #include <linux/lcd.h> | ||
38 | #include <linux/pci.h> | ||
39 | #include <asm/uaccess.h> | ||
40 | |||
41 | /* The LVDS- and panel power controls sits on the | ||
42 | * GPIO port of the ISA bridge. | ||
43 | */ | ||
44 | |||
45 | #define CRVML_DEVICE_LPC 0x27B8 | ||
46 | #define CRVML_REG_GPIOBAR 0x48 | ||
47 | #define CRVML_REG_GPIOEN 0x4C | ||
48 | #define CRVML_GPIOEN_BIT (1 << 4) | ||
49 | #define CRVML_PANEL_PORT 0x38 | ||
50 | #define CRVML_LVDS_ON 0x00000001 | ||
51 | #define CRVML_PANEL_ON 0x00000002 | ||
52 | #define CRVML_BACKLIGHT_OFF 0x00000004 | ||
53 | |||
54 | /* The PLL Clock register sits on Host bridge */ | ||
55 | #define CRVML_DEVICE_MCH 0x5001 | ||
56 | #define CRVML_REG_MCHBAR 0x44 | ||
57 | #define CRVML_REG_MCHEN 0x54 | ||
58 | #define CRVML_MCHEN_BIT (1 << 28) | ||
59 | #define CRVML_MCHMAP_SIZE 4096 | ||
60 | #define CRVML_REG_CLOCK 0xc3c | ||
61 | #define CRVML_CLOCK_SHIFT 8 | ||
62 | #define CRVML_CLOCK_MASK 0x00000f00 | ||
63 | |||
64 | static struct pci_dev *lpc_dev; | ||
65 | static u32 gpio_bar; | ||
66 | |||
67 | struct cr_panel { | ||
68 | struct backlight_device *cr_backlight_device; | ||
69 | struct lcd_device *cr_lcd_device; | ||
70 | }; | ||
71 | |||
72 | static int cr_backlight_set_intensity(struct backlight_device *bd) | ||
73 | { | ||
74 | int intensity = bd->props.brightness; | ||
75 | u32 addr = gpio_bar + CRVML_PANEL_PORT; | ||
76 | u32 cur = inl(addr); | ||
77 | |||
78 | if (bd->props.power == FB_BLANK_UNBLANK) | ||
79 | intensity = FB_BLANK_UNBLANK; | ||
80 | if (bd->props.fb_blank == FB_BLANK_UNBLANK) | ||
81 | intensity = FB_BLANK_UNBLANK; | ||
82 | if (bd->props.power == FB_BLANK_POWERDOWN) | ||
83 | intensity = FB_BLANK_POWERDOWN; | ||
84 | if (bd->props.fb_blank == FB_BLANK_POWERDOWN) | ||
85 | intensity = FB_BLANK_POWERDOWN; | ||
86 | |||
87 | if (intensity == FB_BLANK_UNBLANK) { /* FULL ON */ | ||
88 | cur &= ~CRVML_BACKLIGHT_OFF; | ||
89 | outl(cur, addr); | ||
90 | } else if (intensity == FB_BLANK_POWERDOWN) { /* OFF */ | ||
91 | cur |= CRVML_BACKLIGHT_OFF; | ||
92 | outl(cur, addr); | ||
93 | } /* anything else, don't bother */ | ||
94 | |||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | static int cr_backlight_get_intensity(struct backlight_device *bd) | ||
99 | { | ||
100 | u32 addr = gpio_bar + CRVML_PANEL_PORT; | ||
101 | u32 cur = inl(addr); | ||
102 | u8 intensity; | ||
103 | |||
104 | if (cur & CRVML_BACKLIGHT_OFF) | ||
105 | intensity = FB_BLANK_POWERDOWN; | ||
106 | else | ||
107 | intensity = FB_BLANK_UNBLANK; | ||
108 | |||
109 | return intensity; | ||
110 | } | ||
111 | |||
112 | static struct backlight_ops cr_backlight_ops = { | ||
113 | .get_brightness = cr_backlight_get_intensity, | ||
114 | .update_status = cr_backlight_set_intensity, | ||
115 | }; | ||
116 | |||
117 | static void cr_panel_on(void) | ||
118 | { | ||
119 | u32 addr = gpio_bar + CRVML_PANEL_PORT; | ||
120 | u32 cur = inl(addr); | ||
121 | |||
122 | if (!(cur & CRVML_PANEL_ON)) { | ||
123 | /* Make sure LVDS controller is down. */ | ||
124 | if (cur & 0x00000001) { | ||
125 | cur &= ~CRVML_LVDS_ON; | ||
126 | outl(cur, addr); | ||
127 | } | ||
128 | /* Power up Panel */ | ||
129 | schedule_timeout(HZ / 10); | ||
130 | cur |= CRVML_PANEL_ON; | ||
131 | outl(cur, addr); | ||
132 | } | ||
133 | |||
134 | /* Power up LVDS controller */ | ||
135 | |||
136 | if (!(cur & CRVML_LVDS_ON)) { | ||
137 | schedule_timeout(HZ / 10); | ||
138 | outl(cur | CRVML_LVDS_ON, addr); | ||
139 | } | ||
140 | } | ||
141 | |||
142 | static void cr_panel_off(void) | ||
143 | { | ||
144 | u32 addr = gpio_bar + CRVML_PANEL_PORT; | ||
145 | u32 cur = inl(addr); | ||
146 | |||
147 | /* Power down LVDS controller first to avoid high currents */ | ||
148 | if (cur & CRVML_LVDS_ON) { | ||
149 | cur &= ~CRVML_LVDS_ON; | ||
150 | outl(cur, addr); | ||
151 | } | ||
152 | if (cur & CRVML_PANEL_ON) { | ||
153 | schedule_timeout(HZ / 10); | ||
154 | outl(cur & ~CRVML_PANEL_ON, addr); | ||
155 | } | ||
156 | } | ||
157 | |||
158 | static int cr_lcd_set_power(struct lcd_device *ld, int power) | ||
159 | { | ||
160 | if (power == FB_BLANK_UNBLANK) | ||
161 | cr_panel_on(); | ||
162 | if (power == FB_BLANK_POWERDOWN) | ||
163 | cr_panel_off(); | ||
164 | |||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | static struct lcd_ops cr_lcd_ops = { | ||
169 | .set_power = cr_lcd_set_power, | ||
170 | }; | ||
171 | |||
172 | static int cr_backlight_probe(struct platform_device *pdev) | ||
173 | { | ||
174 | struct cr_panel *crp; | ||
175 | u8 dev_en; | ||
176 | |||
177 | crp = kzalloc(sizeof(crp), GFP_KERNEL); | ||
178 | if (crp == NULL) | ||
179 | return -ENOMEM; | ||
180 | |||
181 | lpc_dev = pci_get_device(PCI_VENDOR_ID_INTEL, | ||
182 | CRVML_DEVICE_LPC, NULL); | ||
183 | if (!lpc_dev) { | ||
184 | printk("INTEL CARILLO RANCH LPC not found.\n"); | ||
185 | return -ENODEV; | ||
186 | } | ||
187 | |||
188 | pci_read_config_byte(lpc_dev, CRVML_REG_GPIOEN, &dev_en); | ||
189 | if (!(dev_en & CRVML_GPIOEN_BIT)) { | ||
190 | printk(KERN_ERR | ||
191 | "Carillo Ranch GPIO device was not enabled.\n"); | ||
192 | pci_dev_put(lpc_dev); | ||
193 | return -ENODEV; | ||
194 | } | ||
195 | |||
196 | crp->cr_backlight_device = backlight_device_register("cr-backlight", | ||
197 | &pdev->dev, NULL, | ||
198 | &cr_backlight_ops); | ||
199 | if (IS_ERR(crp->cr_backlight_device)) { | ||
200 | pci_dev_put(lpc_dev); | ||
201 | return PTR_ERR(crp->cr_backlight_device); | ||
202 | } | ||
203 | |||
204 | crp->cr_lcd_device = lcd_device_register("cr-lcd", | ||
205 | &pdev->dev, | ||
206 | &cr_lcd_ops); | ||
207 | |||
208 | if (IS_ERR(crp->cr_lcd_device)) { | ||
209 | pci_dev_put(lpc_dev); | ||
210 | return PTR_ERR(crp->cr_backlight_device); | ||
211 | } | ||
212 | |||
213 | pci_read_config_dword(lpc_dev, CRVML_REG_GPIOBAR, | ||
214 | &gpio_bar); | ||
215 | gpio_bar &= ~0x3F; | ||
216 | |||
217 | crp->cr_backlight_device->props.power = FB_BLANK_UNBLANK; | ||
218 | crp->cr_backlight_device->props.brightness = 0; | ||
219 | crp->cr_backlight_device->props.max_brightness = 0; | ||
220 | cr_backlight_set_intensity(crp->cr_backlight_device); | ||
221 | |||
222 | cr_lcd_set_power(crp->cr_lcd_device, FB_BLANK_UNBLANK); | ||
223 | |||
224 | platform_set_drvdata(pdev, crp); | ||
225 | |||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | static int cr_backlight_remove(struct platform_device *pdev) | ||
230 | { | ||
231 | struct cr_panel *crp = platform_get_drvdata(pdev); | ||
232 | crp->cr_backlight_device->props.power = FB_BLANK_POWERDOWN; | ||
233 | crp->cr_backlight_device->props.brightness = 0; | ||
234 | crp->cr_backlight_device->props.max_brightness = 0; | ||
235 | cr_backlight_set_intensity(crp->cr_backlight_device); | ||
236 | cr_lcd_set_power(crp->cr_lcd_device, FB_BLANK_POWERDOWN); | ||
237 | backlight_device_unregister(crp->cr_backlight_device); | ||
238 | lcd_device_unregister(crp->cr_lcd_device); | ||
239 | pci_dev_put(lpc_dev); | ||
240 | |||
241 | return 0; | ||
242 | } | ||
243 | |||
244 | static struct platform_driver cr_backlight_driver = { | ||
245 | .probe = cr_backlight_probe, | ||
246 | .remove = cr_backlight_remove, | ||
247 | .driver = { | ||
248 | .name = "cr_backlight", | ||
249 | }, | ||
250 | }; | ||
251 | |||
252 | static struct platform_device *crp; | ||
253 | |||
254 | static int __init cr_backlight_init(void) | ||
255 | { | ||
256 | int ret = platform_driver_register(&cr_backlight_driver); | ||
257 | |||
258 | if (!ret) { | ||
259 | crp = platform_device_alloc("cr_backlight", -1); | ||
260 | if (!crp) | ||
261 | return -ENOMEM; | ||
262 | |||
263 | ret = platform_device_add(crp); | ||
264 | |||
265 | if (ret) { | ||
266 | platform_device_put(crp); | ||
267 | platform_driver_unregister(&cr_backlight_driver); | ||
268 | } | ||
269 | } | ||
270 | |||
271 | printk("Carillo Ranch Backlight Driver Initialized.\n"); | ||
272 | |||
273 | return ret; | ||
274 | } | ||
275 | |||
276 | static void __exit cr_backlight_exit(void) | ||
277 | { | ||
278 | platform_device_unregister(crp); | ||
279 | platform_driver_unregister(&cr_backlight_driver); | ||
280 | } | ||
281 | |||
282 | module_init(cr_backlight_init); | ||
283 | module_exit(cr_backlight_exit); | ||
284 | |||
285 | MODULE_AUTHOR("Tungsten Graphics Inc."); | ||
286 | MODULE_DESCRIPTION("Carillo Ranch Backlight Driver"); | ||
287 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/vermilion/Makefile b/drivers/video/vermilion/Makefile new file mode 100644 index 000000000000..cc21a656153d --- /dev/null +++ b/drivers/video/vermilion/Makefile | |||
@@ -0,0 +1,5 @@ | |||
1 | obj-$(CONFIG_FB_LE80578) += vmlfb.o | ||
2 | obj-$(CONFIG_FB_CARILLO_RANCH) += crvml.o | ||
3 | |||
4 | vmlfb-objs := vermilion.o | ||
5 | crvml-objs := cr_pll.o | ||
diff --git a/drivers/video/vermilion/cr_pll.c b/drivers/video/vermilion/cr_pll.c new file mode 100644 index 000000000000..ebc6e6e0dd0f --- /dev/null +++ b/drivers/video/vermilion/cr_pll.c | |||
@@ -0,0 +1,208 @@ | |||
1 | /* | ||
2 | * Copyright (c) Intel Corp. 2007. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to | ||
6 | * develop this driver. | ||
7 | * | ||
8 | * This file is part of the Carillo Ranch video subsystem driver. | ||
9 | * The Carillo Ranch video subsystem driver is free software; | ||
10 | * you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * The Carillo Ranch video subsystem driver is distributed | ||
16 | * in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this driver; if not, write to the Free Software | ||
23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
24 | * | ||
25 | * Authors: | ||
26 | * Thomas Hellstrom <thomas-at-tungstengraphics-dot-com> | ||
27 | * Alan Hourihane <alanh-at-tungstengraphics-dot-com> | ||
28 | */ | ||
29 | |||
30 | #include <linux/module.h> | ||
31 | #include <linux/kernel.h> | ||
32 | #include <linux/pci.h> | ||
33 | #include <linux/errno.h> | ||
34 | #include <linux/fb.h> | ||
35 | #include "vermilion.h" | ||
36 | |||
37 | /* The PLL Clock register sits on Host bridge */ | ||
38 | #define CRVML_DEVICE_MCH 0x5001 | ||
39 | #define CRVML_REG_MCHBAR 0x44 | ||
40 | #define CRVML_REG_MCHEN 0x54 | ||
41 | #define CRVML_MCHEN_BIT (1 << 28) | ||
42 | #define CRVML_MCHMAP_SIZE 4096 | ||
43 | #define CRVML_REG_CLOCK 0xc3c | ||
44 | #define CRVML_CLOCK_SHIFT 8 | ||
45 | #define CRVML_CLOCK_MASK 0x00000f00 | ||
46 | |||
47 | static struct pci_dev *mch_dev; | ||
48 | static u32 mch_bar; | ||
49 | static void __iomem *mch_regs_base; | ||
50 | static u32 saved_clock; | ||
51 | |||
52 | static const unsigned crvml_clocks[] = { | ||
53 | 6750, | ||
54 | 13500, | ||
55 | 27000, | ||
56 | 29700, | ||
57 | 37125, | ||
58 | 54000, | ||
59 | 59400, | ||
60 | 74250, | ||
61 | 120000 | ||
62 | /* | ||
63 | * There are more clocks, but they are disabled on the CR board. | ||
64 | */ | ||
65 | }; | ||
66 | |||
67 | static const u32 crvml_clock_bits[] = { | ||
68 | 0x0a, | ||
69 | 0x09, | ||
70 | 0x08, | ||
71 | 0x07, | ||
72 | 0x06, | ||
73 | 0x05, | ||
74 | 0x04, | ||
75 | 0x03, | ||
76 | 0x0b | ||
77 | }; | ||
78 | |||
79 | static const unsigned crvml_num_clocks = ARRAY_SIZE(crvml_clocks); | ||
80 | |||
81 | static int crvml_sys_restore(struct vml_sys *sys) | ||
82 | { | ||
83 | void __iomem *clock_reg = mch_regs_base + CRVML_REG_CLOCK; | ||
84 | |||
85 | iowrite32(saved_clock, clock_reg); | ||
86 | ioread32(clock_reg); | ||
87 | |||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | static int crvml_sys_save(struct vml_sys *sys) | ||
92 | { | ||
93 | void __iomem *clock_reg = mch_regs_base + CRVML_REG_CLOCK; | ||
94 | |||
95 | saved_clock = ioread32(clock_reg); | ||
96 | |||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | static int crvml_nearest_index(const struct vml_sys *sys, int clock) | ||
101 | { | ||
102 | int i; | ||
103 | int cur_index = 0; | ||
104 | int cur_diff; | ||
105 | int diff; | ||
106 | |||
107 | cur_diff = clock - crvml_clocks[0]; | ||
108 | cur_diff = (cur_diff < 0) ? -cur_diff : cur_diff; | ||
109 | for (i = 1; i < crvml_num_clocks; ++i) { | ||
110 | diff = clock - crvml_clocks[i]; | ||
111 | diff = (diff < 0) ? -diff : diff; | ||
112 | if (diff < cur_diff) { | ||
113 | cur_index = i; | ||
114 | cur_diff = diff; | ||
115 | } | ||
116 | } | ||
117 | return cur_index; | ||
118 | } | ||
119 | |||
120 | static int crvml_nearest_clock(const struct vml_sys *sys, int clock) | ||
121 | { | ||
122 | return crvml_clocks[crvml_nearest_index(sys, clock)]; | ||
123 | } | ||
124 | |||
125 | static int crvml_set_clock(struct vml_sys *sys, int clock) | ||
126 | { | ||
127 | void __iomem *clock_reg = mch_regs_base + CRVML_REG_CLOCK; | ||
128 | int index; | ||
129 | u32 clock_val; | ||
130 | |||
131 | index = crvml_nearest_index(sys, clock); | ||
132 | |||
133 | if (crvml_clocks[index] != clock) | ||
134 | return -EINVAL; | ||
135 | |||
136 | clock_val = ioread32(clock_reg) & ~CRVML_CLOCK_MASK; | ||
137 | clock_val = crvml_clock_bits[index] << CRVML_CLOCK_SHIFT; | ||
138 | iowrite32(clock_val, clock_reg); | ||
139 | ioread32(clock_reg); | ||
140 | |||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | static struct vml_sys cr_pll_ops = { | ||
145 | .name = "Carillo Ranch", | ||
146 | .save = crvml_sys_save, | ||
147 | .restore = crvml_sys_restore, | ||
148 | .set_clock = crvml_set_clock, | ||
149 | .nearest_clock = crvml_nearest_clock, | ||
150 | }; | ||
151 | |||
152 | static int __init cr_pll_init(void) | ||
153 | { | ||
154 | int err; | ||
155 | u32 dev_en; | ||
156 | |||
157 | mch_dev = pci_get_device(PCI_VENDOR_ID_INTEL, | ||
158 | CRVML_DEVICE_MCH, NULL); | ||
159 | if (!mch_dev) { | ||
160 | printk(KERN_ERR | ||
161 | "Could not find Carillo Ranch MCH device.\n"); | ||
162 | return -ENODEV; | ||
163 | } | ||
164 | |||
165 | pci_read_config_dword(mch_dev, CRVML_REG_MCHEN, &dev_en); | ||
166 | if (!(dev_en & CRVML_MCHEN_BIT)) { | ||
167 | printk(KERN_ERR | ||
168 | "Carillo Ranch MCH device was not enabled.\n"); | ||
169 | pci_dev_put(mch_dev); | ||
170 | return -ENODEV; | ||
171 | } | ||
172 | |||
173 | pci_read_config_dword(mch_dev, CRVML_REG_MCHBAR, | ||
174 | &mch_bar); | ||
175 | mch_regs_base = | ||
176 | ioremap_nocache(mch_bar, CRVML_MCHMAP_SIZE); | ||
177 | if (!mch_regs_base) { | ||
178 | printk(KERN_ERR | ||
179 | "Carillo Ranch MCH device was not enabled.\n"); | ||
180 | pci_dev_put(mch_dev); | ||
181 | return -ENODEV; | ||
182 | } | ||
183 | |||
184 | err = vmlfb_register_subsys(&cr_pll_ops); | ||
185 | if (err) { | ||
186 | printk(KERN_ERR | ||
187 | "Carillo Ranch failed to initialize vml_sys.\n"); | ||
188 | pci_dev_put(mch_dev); | ||
189 | return err; | ||
190 | } | ||
191 | |||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | static void __exit cr_pll_exit(void) | ||
196 | { | ||
197 | vmlfb_unregister_subsys(&cr_pll_ops); | ||
198 | |||
199 | iounmap(mch_regs_base); | ||
200 | pci_dev_put(mch_dev); | ||
201 | } | ||
202 | |||
203 | module_init(cr_pll_init); | ||
204 | module_exit(cr_pll_exit); | ||
205 | |||
206 | MODULE_AUTHOR("Tungsten Graphics Inc."); | ||
207 | MODULE_DESCRIPTION("Carillo Ranch PLL Driver"); | ||
208 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/vermilion/vermilion.c b/drivers/video/vermilion/vermilion.c new file mode 100644 index 000000000000..de531c907718 --- /dev/null +++ b/drivers/video/vermilion/vermilion.c | |||
@@ -0,0 +1,1195 @@ | |||
1 | /* | ||
2 | * Copyright (c) Intel Corp. 2007. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to | ||
6 | * develop this driver. | ||
7 | * | ||
8 | * This file is part of the Vermilion Range fb driver. | ||
9 | * The Vermilion Range fb driver is free software; | ||
10 | * you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * The Vermilion Range fb driver is distributed | ||
16 | * in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this driver; if not, write to the Free Software | ||
23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
24 | * | ||
25 | * Authors: | ||
26 | * Thomas Hellström <thomas-at-tungstengraphics-dot-com> | ||
27 | * Michel Dänzer <michel-at-tungstengraphics-dot-com> | ||
28 | * Alan Hourihane <alanh-at-tungstengraphics-dot-com> | ||
29 | */ | ||
30 | |||
31 | #include <linux/module.h> | ||
32 | #include <linux/kernel.h> | ||
33 | #include <linux/errno.h> | ||
34 | #include <linux/string.h> | ||
35 | #include <linux/delay.h> | ||
36 | #include <linux/mm.h> | ||
37 | #include <linux/fb.h> | ||
38 | #include <linux/pci.h> | ||
39 | #include <asm/cacheflush.h> | ||
40 | #include <asm/tlbflush.h> | ||
41 | #include <linux/mmzone.h> | ||
42 | #include <asm/uaccess.h> | ||
43 | |||
44 | /* #define VERMILION_DEBUG */ | ||
45 | |||
46 | #include "vermilion.h" | ||
47 | |||
48 | #define MODULE_NAME "vmlfb" | ||
49 | |||
50 | #define VML_TOHW(_val, _width) ((((_val) << (_width)) + 0x7FFF - (_val)) >> 16) | ||
51 | |||
52 | static struct mutex vml_mutex; | ||
53 | static struct list_head global_no_mode; | ||
54 | static struct list_head global_has_mode; | ||
55 | static struct fb_ops vmlfb_ops; | ||
56 | static struct vml_sys *subsys = NULL; | ||
57 | static char *vml_default_mode = "1024x768@60"; | ||
58 | static struct fb_videomode defaultmode = { | ||
59 | NULL, 60, 1024, 768, 12896, 144, 24, 29, 3, 136, 6, | ||
60 | 0, FB_VMODE_NONINTERLACED | ||
61 | }; | ||
62 | |||
63 | static u32 vml_mem_requested = (10 * 1024 * 1024); | ||
64 | static u32 vml_mem_contig = (4 * 1024 * 1024); | ||
65 | static u32 vml_mem_min = (4 * 1024 * 1024); | ||
66 | |||
67 | static u32 vml_clocks[] = { | ||
68 | 6750, | ||
69 | 13500, | ||
70 | 27000, | ||
71 | 29700, | ||
72 | 37125, | ||
73 | 54000, | ||
74 | 59400, | ||
75 | 74250, | ||
76 | 120000, | ||
77 | 148500 | ||
78 | }; | ||
79 | |||
80 | static u32 vml_num_clocks = ARRAY_SIZE(vml_clocks); | ||
81 | |||
82 | /* | ||
83 | * Allocate a contiguous vram area and make its linear kernel map | ||
84 | * uncached. | ||
85 | */ | ||
86 | |||
87 | static int vmlfb_alloc_vram_area(struct vram_area *va, unsigned max_order, | ||
88 | unsigned min_order) | ||
89 | { | ||
90 | gfp_t flags; | ||
91 | unsigned long i; | ||
92 | pgprot_t wc_pageprot; | ||
93 | |||
94 | wc_pageprot = PAGE_KERNEL_NOCACHE; | ||
95 | max_order++; | ||
96 | do { | ||
97 | /* | ||
98 | * Really try hard to get the needed memory. | ||
99 | * We need memory below the first 32MB, so we | ||
100 | * add the __GFP_DMA flag that guarantees that we are | ||
101 | * below the first 16MB. | ||
102 | */ | ||
103 | |||
104 | flags = __GFP_DMA | __GFP_HIGH; | ||
105 | va->logical = | ||
106 | __get_free_pages(flags, --max_order); | ||
107 | } while (va->logical == 0 && max_order > min_order); | ||
108 | |||
109 | if (!va->logical) | ||
110 | return -ENOMEM; | ||
111 | |||
112 | va->phys = virt_to_phys((void *)va->logical); | ||
113 | va->size = PAGE_SIZE << max_order; | ||
114 | va->order = max_order; | ||
115 | |||
116 | /* | ||
117 | * It seems like __get_free_pages only ups the usage count | ||
118 | * of the first page. This doesn't work with nopage mapping, so | ||
119 | * up the usage count once more. | ||
120 | */ | ||
121 | |||
122 | memset((void *)va->logical, 0x00, va->size); | ||
123 | for (i = va->logical; i < va->logical + va->size; i += PAGE_SIZE) { | ||
124 | get_page(virt_to_page(i)); | ||
125 | } | ||
126 | |||
127 | /* | ||
128 | * Change caching policy of the linear kernel map to avoid | ||
129 | * mapping type conflicts with user-space mappings. | ||
130 | * The first global_flush_tlb() is really only there to do a global | ||
131 | * wbinvd(). | ||
132 | */ | ||
133 | |||
134 | global_flush_tlb(); | ||
135 | change_page_attr(virt_to_page(va->logical), va->size >> PAGE_SHIFT, | ||
136 | wc_pageprot); | ||
137 | global_flush_tlb(); | ||
138 | |||
139 | printk(KERN_DEBUG MODULE_NAME | ||
140 | ": Allocated %ld bytes vram area at 0x%08lx\n", | ||
141 | va->size, va->phys); | ||
142 | |||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | /* | ||
147 | * Free a contiguous vram area and reset its linear kernel map | ||
148 | * mapping type. | ||
149 | */ | ||
150 | |||
151 | static void vmlfb_free_vram_area(struct vram_area *va) | ||
152 | { | ||
153 | unsigned long j; | ||
154 | |||
155 | if (va->logical) { | ||
156 | |||
157 | /* | ||
158 | * Reset the linear kernel map caching policy. | ||
159 | */ | ||
160 | |||
161 | change_page_attr(virt_to_page(va->logical), | ||
162 | va->size >> PAGE_SHIFT, PAGE_KERNEL); | ||
163 | global_flush_tlb(); | ||
164 | |||
165 | /* | ||
166 | * Decrease the usage count on the pages we've used | ||
167 | * to compensate for upping when allocating. | ||
168 | */ | ||
169 | |||
170 | for (j = va->logical; j < va->logical + va->size; | ||
171 | j += PAGE_SIZE) { | ||
172 | (void)put_page_testzero(virt_to_page(j)); | ||
173 | } | ||
174 | |||
175 | printk(KERN_DEBUG MODULE_NAME | ||
176 | ": Freeing %ld bytes vram area at 0x%08lx\n", | ||
177 | va->size, va->phys); | ||
178 | free_pages(va->logical, va->order); | ||
179 | |||
180 | va->logical = 0; | ||
181 | } | ||
182 | } | ||
183 | |||
184 | /* | ||
185 | * Free allocated vram. | ||
186 | */ | ||
187 | |||
188 | static void vmlfb_free_vram(struct vml_info *vinfo) | ||
189 | { | ||
190 | int i; | ||
191 | |||
192 | for (i = 0; i < vinfo->num_areas; ++i) { | ||
193 | vmlfb_free_vram_area(&vinfo->vram[i]); | ||
194 | } | ||
195 | vinfo->num_areas = 0; | ||
196 | } | ||
197 | |||
198 | /* | ||
199 | * Allocate vram. Currently we try to allocate contiguous areas from the | ||
200 | * __GFP_DMA zone and puzzle them together. A better approach would be to | ||
201 | * allocate one contiguous area for scanout and use one-page allocations for | ||
202 | * offscreen areas. This requires user-space and GPU virtual mappings. | ||
203 | */ | ||
204 | |||
205 | static int vmlfb_alloc_vram(struct vml_info *vinfo, | ||
206 | size_t requested, | ||
207 | size_t min_total, size_t min_contig) | ||
208 | { | ||
209 | int i, j; | ||
210 | int order; | ||
211 | int contiguous; | ||
212 | int err; | ||
213 | struct vram_area *va; | ||
214 | struct vram_area *va2; | ||
215 | |||
216 | vinfo->num_areas = 0; | ||
217 | for (i = 0; i < VML_VRAM_AREAS; ++i) { | ||
218 | va = &vinfo->vram[i]; | ||
219 | order = 0; | ||
220 | |||
221 | while (requested > (PAGE_SIZE << order) && order < MAX_ORDER) | ||
222 | order++; | ||
223 | |||
224 | err = vmlfb_alloc_vram_area(va, order, 0); | ||
225 | |||
226 | if (err) | ||
227 | break; | ||
228 | |||
229 | if (i == 0) { | ||
230 | vinfo->vram_start = va->phys; | ||
231 | vinfo->vram_logical = (void __iomem *) va->logical; | ||
232 | vinfo->vram_contig_size = va->size; | ||
233 | vinfo->num_areas = 1; | ||
234 | } else { | ||
235 | contiguous = 0; | ||
236 | |||
237 | for (j = 0; j < i; ++j) { | ||
238 | va2 = &vinfo->vram[j]; | ||
239 | if (va->phys + va->size == va2->phys || | ||
240 | va2->phys + va2->size == va->phys) { | ||
241 | contiguous = 1; | ||
242 | break; | ||
243 | } | ||
244 | } | ||
245 | |||
246 | if (contiguous) { | ||
247 | vinfo->num_areas++; | ||
248 | if (va->phys < vinfo->vram_start) { | ||
249 | vinfo->vram_start = va->phys; | ||
250 | vinfo->vram_logical = | ||
251 | (void __iomem *)va->logical; | ||
252 | } | ||
253 | vinfo->vram_contig_size += va->size; | ||
254 | } else { | ||
255 | vmlfb_free_vram_area(va); | ||
256 | break; | ||
257 | } | ||
258 | } | ||
259 | |||
260 | if (requested < va->size) | ||
261 | break; | ||
262 | else | ||
263 | requested -= va->size; | ||
264 | } | ||
265 | |||
266 | if (vinfo->vram_contig_size > min_total && | ||
267 | vinfo->vram_contig_size > min_contig) { | ||
268 | |||
269 | printk(KERN_DEBUG MODULE_NAME | ||
270 | ": Contiguous vram: %ld bytes at physical 0x%08lx.\n", | ||
271 | (unsigned long)vinfo->vram_contig_size, | ||
272 | (unsigned long)vinfo->vram_start); | ||
273 | |||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | printk(KERN_ERR MODULE_NAME | ||
278 | ": Could not allocate requested minimal amount of vram.\n"); | ||
279 | |||
280 | vmlfb_free_vram(vinfo); | ||
281 | |||
282 | return -ENOMEM; | ||
283 | } | ||
284 | |||
285 | /* | ||
286 | * Find the GPU to use with our display controller. | ||
287 | */ | ||
288 | |||
289 | static int vmlfb_get_gpu(struct vml_par *par) | ||
290 | { | ||
291 | mutex_lock(&vml_mutex); | ||
292 | |||
293 | par->gpu = pci_get_device(PCI_VENDOR_ID_INTEL, VML_DEVICE_GPU, NULL); | ||
294 | |||
295 | if (!par->gpu) { | ||
296 | mutex_unlock(&vml_mutex); | ||
297 | return -ENODEV; | ||
298 | } | ||
299 | |||
300 | mutex_unlock(&vml_mutex); | ||
301 | |||
302 | if (pci_enable_device(par->gpu) < 0) | ||
303 | return -ENODEV; | ||
304 | |||
305 | return 0; | ||
306 | } | ||
307 | |||
308 | /* | ||
309 | * Find a contiguous vram area that contains a given offset from vram start. | ||
310 | */ | ||
311 | static int vmlfb_vram_offset(struct vml_info *vinfo, unsigned long offset) | ||
312 | { | ||
313 | unsigned long aoffset; | ||
314 | unsigned i; | ||
315 | |||
316 | for (i = 0; i < vinfo->num_areas; ++i) { | ||
317 | aoffset = offset - (vinfo->vram[i].phys - vinfo->vram_start); | ||
318 | |||
319 | if (aoffset < vinfo->vram[i].size) { | ||
320 | return 0; | ||
321 | } | ||
322 | } | ||
323 | |||
324 | return -EINVAL; | ||
325 | } | ||
326 | |||
327 | /* | ||
328 | * Remap the MMIO register spaces of the VDC and the GPU. | ||
329 | */ | ||
330 | |||
331 | static int vmlfb_enable_mmio(struct vml_par *par) | ||
332 | { | ||
333 | int err; | ||
334 | |||
335 | par->vdc_mem_base = pci_resource_start(par->vdc, 0); | ||
336 | par->vdc_mem_size = pci_resource_len(par->vdc, 0); | ||
337 | if (!request_mem_region(par->vdc_mem_base, par->vdc_mem_size, "vmlfb")) { | ||
338 | printk(KERN_ERR MODULE_NAME | ||
339 | ": Could not claim display controller MMIO.\n"); | ||
340 | return -EBUSY; | ||
341 | } | ||
342 | par->vdc_mem = ioremap_nocache(par->vdc_mem_base, par->vdc_mem_size); | ||
343 | if (par->vdc_mem == NULL) { | ||
344 | printk(KERN_ERR MODULE_NAME | ||
345 | ": Could not map display controller MMIO.\n"); | ||
346 | err = -ENOMEM; | ||
347 | goto out_err_0; | ||
348 | } | ||
349 | |||
350 | par->gpu_mem_base = pci_resource_start(par->gpu, 0); | ||
351 | par->gpu_mem_size = pci_resource_len(par->gpu, 0); | ||
352 | if (!request_mem_region(par->gpu_mem_base, par->gpu_mem_size, "vmlfb")) { | ||
353 | printk(KERN_ERR MODULE_NAME ": Could not claim GPU MMIO.\n"); | ||
354 | err = -EBUSY; | ||
355 | goto out_err_1; | ||
356 | } | ||
357 | par->gpu_mem = ioremap_nocache(par->gpu_mem_base, par->gpu_mem_size); | ||
358 | if (par->gpu_mem == NULL) { | ||
359 | printk(KERN_ERR MODULE_NAME ": Could not map GPU MMIO.\n"); | ||
360 | err = -ENOMEM; | ||
361 | goto out_err_2; | ||
362 | } | ||
363 | |||
364 | return 0; | ||
365 | |||
366 | out_err_2: | ||
367 | release_mem_region(par->gpu_mem_base, par->gpu_mem_size); | ||
368 | out_err_1: | ||
369 | iounmap(par->vdc_mem); | ||
370 | out_err_0: | ||
371 | release_mem_region(par->vdc_mem_base, par->vdc_mem_size); | ||
372 | return err; | ||
373 | } | ||
374 | |||
375 | /* | ||
376 | * Unmap the VDC and GPU register spaces. | ||
377 | */ | ||
378 | |||
379 | static void vmlfb_disable_mmio(struct vml_par *par) | ||
380 | { | ||
381 | iounmap(par->gpu_mem); | ||
382 | release_mem_region(par->gpu_mem_base, par->gpu_mem_size); | ||
383 | iounmap(par->vdc_mem); | ||
384 | release_mem_region(par->vdc_mem_base, par->vdc_mem_size); | ||
385 | } | ||
386 | |||
387 | /* | ||
388 | * Release and uninit the VDC and GPU. | ||
389 | */ | ||
390 | |||
391 | static void vmlfb_release_devices(struct vml_par *par) | ||
392 | { | ||
393 | if (atomic_dec_and_test(&par->refcount)) { | ||
394 | pci_set_drvdata(par->vdc, NULL); | ||
395 | pci_disable_device(par->gpu); | ||
396 | pci_disable_device(par->vdc); | ||
397 | } | ||
398 | } | ||
399 | |||
400 | /* | ||
401 | * Free up allocated resources for a device. | ||
402 | */ | ||
403 | |||
404 | static void __devexit vml_pci_remove(struct pci_dev *dev) | ||
405 | { | ||
406 | struct fb_info *info; | ||
407 | struct vml_info *vinfo; | ||
408 | struct vml_par *par; | ||
409 | |||
410 | info = pci_get_drvdata(dev); | ||
411 | if (info) { | ||
412 | vinfo = container_of(info, struct vml_info, info); | ||
413 | par = vinfo->par; | ||
414 | mutex_lock(&vml_mutex); | ||
415 | unregister_framebuffer(info); | ||
416 | fb_dealloc_cmap(&info->cmap); | ||
417 | vmlfb_free_vram(vinfo); | ||
418 | vmlfb_disable_mmio(par); | ||
419 | vmlfb_release_devices(par); | ||
420 | kfree(vinfo); | ||
421 | kfree(par); | ||
422 | mutex_unlock(&vml_mutex); | ||
423 | } | ||
424 | } | ||
425 | |||
426 | static void vmlfb_set_pref_pixel_format(struct fb_var_screeninfo *var) | ||
427 | { | ||
428 | switch (var->bits_per_pixel) { | ||
429 | case 16: | ||
430 | var->blue.offset = 0; | ||
431 | var->blue.length = 5; | ||
432 | var->green.offset = 5; | ||
433 | var->green.length = 5; | ||
434 | var->red.offset = 10; | ||
435 | var->red.length = 5; | ||
436 | var->transp.offset = 15; | ||
437 | var->transp.length = 1; | ||
438 | break; | ||
439 | case 32: | ||
440 | var->blue.offset = 0; | ||
441 | var->blue.length = 8; | ||
442 | var->green.offset = 8; | ||
443 | var->green.length = 8; | ||
444 | var->red.offset = 16; | ||
445 | var->red.length = 8; | ||
446 | var->transp.offset = 24; | ||
447 | var->transp.length = 0; | ||
448 | break; | ||
449 | default: | ||
450 | break; | ||
451 | } | ||
452 | |||
453 | var->blue.msb_right = var->green.msb_right = | ||
454 | var->red.msb_right = var->transp.msb_right = 0; | ||
455 | } | ||
456 | |||
457 | /* | ||
458 | * Device initialization. | ||
459 | * We initialize one vml_par struct per device and one vml_info | ||
460 | * struct per pipe. Currently we have only one pipe. | ||
461 | */ | ||
462 | |||
463 | static int __devinit vml_pci_probe(struct pci_dev *dev, | ||
464 | const struct pci_device_id *id) | ||
465 | { | ||
466 | struct vml_info *vinfo; | ||
467 | struct fb_info *info; | ||
468 | struct vml_par *par; | ||
469 | int err = 0; | ||
470 | |||
471 | par = kzalloc(sizeof(*par), GFP_KERNEL); | ||
472 | if (par == NULL) | ||
473 | return -ENOMEM; | ||
474 | |||
475 | vinfo = kzalloc(sizeof(*vinfo), GFP_KERNEL); | ||
476 | if (vinfo == NULL) { | ||
477 | err = -ENOMEM; | ||
478 | goto out_err_0; | ||
479 | } | ||
480 | |||
481 | vinfo->par = par; | ||
482 | par->vdc = dev; | ||
483 | atomic_set(&par->refcount, 1); | ||
484 | |||
485 | switch (id->device) { | ||
486 | case VML_DEVICE_VDC: | ||
487 | if ((err = vmlfb_get_gpu(par))) | ||
488 | goto out_err_1; | ||
489 | pci_set_drvdata(dev, &vinfo->info); | ||
490 | break; | ||
491 | default: | ||
492 | err = -ENODEV; | ||
493 | goto out_err_1; | ||
494 | break; | ||
495 | } | ||
496 | |||
497 | info = &vinfo->info; | ||
498 | info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK; | ||
499 | |||
500 | err = vmlfb_enable_mmio(par); | ||
501 | if (err) | ||
502 | goto out_err_2; | ||
503 | |||
504 | err = vmlfb_alloc_vram(vinfo, vml_mem_requested, | ||
505 | vml_mem_contig, vml_mem_min); | ||
506 | if (err) | ||
507 | goto out_err_3; | ||
508 | |||
509 | strcpy(info->fix.id, "Vermilion Range"); | ||
510 | info->fix.mmio_start = 0; | ||
511 | info->fix.mmio_len = 0; | ||
512 | info->fix.smem_start = vinfo->vram_start; | ||
513 | info->fix.smem_len = vinfo->vram_contig_size; | ||
514 | info->fix.type = FB_TYPE_PACKED_PIXELS; | ||
515 | info->fix.visual = FB_VISUAL_TRUECOLOR; | ||
516 | info->fix.ypanstep = 1; | ||
517 | info->fix.xpanstep = 1; | ||
518 | info->fix.ywrapstep = 0; | ||
519 | info->fix.accel = FB_ACCEL_NONE; | ||
520 | info->screen_base = vinfo->vram_logical; | ||
521 | info->pseudo_palette = vinfo->pseudo_palette; | ||
522 | info->par = par; | ||
523 | info->fbops = &vmlfb_ops; | ||
524 | info->device = &dev->dev; | ||
525 | |||
526 | INIT_LIST_HEAD(&vinfo->head); | ||
527 | vinfo->pipe_disabled = 1; | ||
528 | vinfo->cur_blank_mode = FB_BLANK_UNBLANK; | ||
529 | |||
530 | info->var.grayscale = 0; | ||
531 | info->var.bits_per_pixel = 16; | ||
532 | vmlfb_set_pref_pixel_format(&info->var); | ||
533 | |||
534 | if (!fb_find_mode | ||
535 | (&info->var, info, vml_default_mode, NULL, 0, &defaultmode, 16)) { | ||
536 | printk(KERN_ERR MODULE_NAME ": Could not find initial mode\n"); | ||
537 | } | ||
538 | |||
539 | if (fb_alloc_cmap(&info->cmap, 256, 1) < 0) { | ||
540 | err = -ENOMEM; | ||
541 | goto out_err_4; | ||
542 | } | ||
543 | |||
544 | err = register_framebuffer(info); | ||
545 | if (err) { | ||
546 | printk(KERN_ERR MODULE_NAME ": Register framebuffer error.\n"); | ||
547 | goto out_err_5; | ||
548 | } | ||
549 | |||
550 | printk("Initialized vmlfb\n"); | ||
551 | |||
552 | return 0; | ||
553 | |||
554 | out_err_5: | ||
555 | fb_dealloc_cmap(&info->cmap); | ||
556 | out_err_4: | ||
557 | vmlfb_free_vram(vinfo); | ||
558 | out_err_3: | ||
559 | vmlfb_disable_mmio(par); | ||
560 | out_err_2: | ||
561 | vmlfb_release_devices(par); | ||
562 | out_err_1: | ||
563 | kfree(vinfo); | ||
564 | out_err_0: | ||
565 | kfree(par); | ||
566 | return err; | ||
567 | } | ||
568 | |||
569 | static int vmlfb_open(struct fb_info *info, int user) | ||
570 | { | ||
571 | /* | ||
572 | * Save registers here? | ||
573 | */ | ||
574 | return 0; | ||
575 | } | ||
576 | |||
577 | static int vmlfb_release(struct fb_info *info, int user) | ||
578 | { | ||
579 | /* | ||
580 | * Restore registers here. | ||
581 | */ | ||
582 | |||
583 | return 0; | ||
584 | } | ||
585 | |||
586 | static int vml_nearest_clock(int clock) | ||
587 | { | ||
588 | |||
589 | int i; | ||
590 | int cur_index; | ||
591 | int cur_diff; | ||
592 | int diff; | ||
593 | |||
594 | cur_index = 0; | ||
595 | cur_diff = clock - vml_clocks[0]; | ||
596 | cur_diff = (cur_diff < 0) ? -cur_diff : cur_diff; | ||
597 | for (i = 1; i < vml_num_clocks; ++i) { | ||
598 | diff = clock - vml_clocks[i]; | ||
599 | diff = (diff < 0) ? -diff : diff; | ||
600 | if (diff < cur_diff) { | ||
601 | cur_index = i; | ||
602 | cur_diff = diff; | ||
603 | } | ||
604 | } | ||
605 | return vml_clocks[cur_index]; | ||
606 | } | ||
607 | |||
608 | static int vmlfb_check_var_locked(struct fb_var_screeninfo *var, | ||
609 | struct vml_info *vinfo) | ||
610 | { | ||
611 | u32 pitch; | ||
612 | u64 mem; | ||
613 | int nearest_clock; | ||
614 | int clock; | ||
615 | int clock_diff; | ||
616 | struct fb_var_screeninfo v; | ||
617 | |||
618 | v = *var; | ||
619 | clock = PICOS2KHZ(var->pixclock); | ||
620 | |||
621 | if (subsys && subsys->nearest_clock) { | ||
622 | nearest_clock = subsys->nearest_clock(subsys, clock); | ||
623 | } else { | ||
624 | nearest_clock = vml_nearest_clock(clock); | ||
625 | } | ||
626 | |||
627 | /* | ||
628 | * Accept a 20% diff. | ||
629 | */ | ||
630 | |||
631 | clock_diff = nearest_clock - clock; | ||
632 | clock_diff = (clock_diff < 0) ? -clock_diff : clock_diff; | ||
633 | if (clock_diff > clock / 5) { | ||
634 | #if 0 | ||
635 | printk(KERN_DEBUG MODULE_NAME ": Diff failure. %d %d\n",clock_diff,clock); | ||
636 | #endif | ||
637 | return -EINVAL; | ||
638 | } | ||
639 | |||
640 | v.pixclock = KHZ2PICOS(nearest_clock); | ||
641 | |||
642 | if (var->xres > VML_MAX_XRES || var->yres > VML_MAX_YRES) { | ||
643 | printk(KERN_DEBUG MODULE_NAME ": Resolution failure.\n"); | ||
644 | return -EINVAL; | ||
645 | } | ||
646 | if (var->xres_virtual > VML_MAX_XRES_VIRTUAL) { | ||
647 | printk(KERN_DEBUG MODULE_NAME | ||
648 | ": Virtual resolution failure.\n"); | ||
649 | return -EINVAL; | ||
650 | } | ||
651 | switch (v.bits_per_pixel) { | ||
652 | case 0 ... 16: | ||
653 | v.bits_per_pixel = 16; | ||
654 | break; | ||
655 | case 17 ... 32: | ||
656 | v.bits_per_pixel = 32; | ||
657 | break; | ||
658 | default: | ||
659 | printk(KERN_DEBUG MODULE_NAME ": Invalid bpp: %d.\n", | ||
660 | var->bits_per_pixel); | ||
661 | return -EINVAL; | ||
662 | } | ||
663 | |||
664 | pitch = __ALIGN_MASK((var->xres * var->bits_per_pixel) >> 3, 0x3F); | ||
665 | mem = pitch * var->yres_virtual; | ||
666 | if (mem > vinfo->vram_contig_size) { | ||
667 | return -ENOMEM; | ||
668 | } | ||
669 | |||
670 | switch (v.bits_per_pixel) { | ||
671 | case 16: | ||
672 | if (var->blue.offset != 0 || | ||
673 | var->blue.length != 5 || | ||
674 | var->green.offset != 5 || | ||
675 | var->green.length != 5 || | ||
676 | var->red.offset != 10 || | ||
677 | var->red.length != 5 || | ||
678 | var->transp.offset != 15 || var->transp.length != 1) { | ||
679 | vmlfb_set_pref_pixel_format(&v); | ||
680 | } | ||
681 | break; | ||
682 | case 32: | ||
683 | if (var->blue.offset != 0 || | ||
684 | var->blue.length != 8 || | ||
685 | var->green.offset != 8 || | ||
686 | var->green.length != 8 || | ||
687 | var->red.offset != 16 || | ||
688 | var->red.length != 8 || | ||
689 | (var->transp.length != 0 && var->transp.length != 8) || | ||
690 | (var->transp.length == 8 && var->transp.offset != 24)) { | ||
691 | vmlfb_set_pref_pixel_format(&v); | ||
692 | } | ||
693 | break; | ||
694 | default: | ||
695 | return -EINVAL; | ||
696 | } | ||
697 | |||
698 | *var = v; | ||
699 | |||
700 | return 0; | ||
701 | } | ||
702 | |||
703 | static int vmlfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | ||
704 | { | ||
705 | struct vml_info *vinfo = container_of(info, struct vml_info, info); | ||
706 | int ret; | ||
707 | |||
708 | mutex_lock(&vml_mutex); | ||
709 | ret = vmlfb_check_var_locked(var, vinfo); | ||
710 | mutex_unlock(&vml_mutex); | ||
711 | |||
712 | return ret; | ||
713 | } | ||
714 | |||
715 | static void vml_wait_vblank(struct vml_info *vinfo) | ||
716 | { | ||
717 | /* Wait for vblank. For now, just wait for a 50Hz cycle (20ms)) */ | ||
718 | mdelay(20); | ||
719 | } | ||
720 | |||
721 | static void vmlfb_disable_pipe(struct vml_info *vinfo) | ||
722 | { | ||
723 | struct vml_par *par = vinfo->par; | ||
724 | |||
725 | /* Disable the MDVO pad */ | ||
726 | VML_WRITE32(par, VML_RCOMPSTAT, 0); | ||
727 | while (!(VML_READ32(par, VML_RCOMPSTAT) & VML_MDVO_VDC_I_RCOMP)) ; | ||
728 | |||
729 | /* Disable display planes */ | ||
730 | VML_WRITE32(par, VML_DSPCCNTR, | ||
731 | VML_READ32(par, VML_DSPCCNTR) & ~VML_GFX_ENABLE); | ||
732 | (void)VML_READ32(par, VML_DSPCCNTR); | ||
733 | /* Wait for vblank for the disable to take effect */ | ||
734 | vml_wait_vblank(vinfo); | ||
735 | |||
736 | /* Next, disable display pipes */ | ||
737 | VML_WRITE32(par, VML_PIPEACONF, 0); | ||
738 | (void)VML_READ32(par, VML_PIPEACONF); | ||
739 | |||
740 | vinfo->pipe_disabled = 1; | ||
741 | } | ||
742 | |||
743 | #ifdef VERMILION_DEBUG | ||
744 | static void vml_dump_regs(struct vml_info *vinfo) | ||
745 | { | ||
746 | struct vml_par *par = vinfo->par; | ||
747 | |||
748 | printk(KERN_DEBUG MODULE_NAME ": Modesetting register dump:\n"); | ||
749 | printk(KERN_DEBUG MODULE_NAME ": \tHTOTAL_A : 0x%08x\n", | ||
750 | (unsigned)VML_READ32(par, VML_HTOTAL_A)); | ||
751 | printk(KERN_DEBUG MODULE_NAME ": \tHBLANK_A : 0x%08x\n", | ||
752 | (unsigned)VML_READ32(par, VML_HBLANK_A)); | ||
753 | printk(KERN_DEBUG MODULE_NAME ": \tHSYNC_A : 0x%08x\n", | ||
754 | (unsigned)VML_READ32(par, VML_HSYNC_A)); | ||
755 | printk(KERN_DEBUG MODULE_NAME ": \tVTOTAL_A : 0x%08x\n", | ||
756 | (unsigned)VML_READ32(par, VML_VTOTAL_A)); | ||
757 | printk(KERN_DEBUG MODULE_NAME ": \tVBLANK_A : 0x%08x\n", | ||
758 | (unsigned)VML_READ32(par, VML_VBLANK_A)); | ||
759 | printk(KERN_DEBUG MODULE_NAME ": \tVSYNC_A : 0x%08x\n", | ||
760 | (unsigned)VML_READ32(par, VML_VSYNC_A)); | ||
761 | printk(KERN_DEBUG MODULE_NAME ": \tDSPCSTRIDE : 0x%08x\n", | ||
762 | (unsigned)VML_READ32(par, VML_DSPCSTRIDE)); | ||
763 | printk(KERN_DEBUG MODULE_NAME ": \tDSPCSIZE : 0x%08x\n", | ||
764 | (unsigned)VML_READ32(par, VML_DSPCSIZE)); | ||
765 | printk(KERN_DEBUG MODULE_NAME ": \tDSPCPOS : 0x%08x\n", | ||
766 | (unsigned)VML_READ32(par, VML_DSPCPOS)); | ||
767 | printk(KERN_DEBUG MODULE_NAME ": \tDSPARB : 0x%08x\n", | ||
768 | (unsigned)VML_READ32(par, VML_DSPARB)); | ||
769 | printk(KERN_DEBUG MODULE_NAME ": \tDSPCADDR : 0x%08x\n", | ||
770 | (unsigned)VML_READ32(par, VML_DSPCADDR)); | ||
771 | printk(KERN_DEBUG MODULE_NAME ": \tBCLRPAT_A : 0x%08x\n", | ||
772 | (unsigned)VML_READ32(par, VML_BCLRPAT_A)); | ||
773 | printk(KERN_DEBUG MODULE_NAME ": \tCANVSCLR_A : 0x%08x\n", | ||
774 | (unsigned)VML_READ32(par, VML_CANVSCLR_A)); | ||
775 | printk(KERN_DEBUG MODULE_NAME ": \tPIPEASRC : 0x%08x\n", | ||
776 | (unsigned)VML_READ32(par, VML_PIPEASRC)); | ||
777 | printk(KERN_DEBUG MODULE_NAME ": \tPIPEACONF : 0x%08x\n", | ||
778 | (unsigned)VML_READ32(par, VML_PIPEACONF)); | ||
779 | printk(KERN_DEBUG MODULE_NAME ": \tDSPCCNTR : 0x%08x\n", | ||
780 | (unsigned)VML_READ32(par, VML_DSPCCNTR)); | ||
781 | printk(KERN_DEBUG MODULE_NAME ": \tRCOMPSTAT : 0x%08x\n", | ||
782 | (unsigned)VML_READ32(par, VML_RCOMPSTAT)); | ||
783 | printk(KERN_DEBUG MODULE_NAME ": End of modesetting register dump.\n"); | ||
784 | } | ||
785 | #endif | ||
786 | |||
787 | static int vmlfb_set_par_locked(struct vml_info *vinfo) | ||
788 | { | ||
789 | struct vml_par *par = vinfo->par; | ||
790 | struct fb_info *info = &vinfo->info; | ||
791 | struct fb_var_screeninfo *var = &info->var; | ||
792 | u32 htotal, hactive, hblank_start, hblank_end, hsync_start, hsync_end; | ||
793 | u32 vtotal, vactive, vblank_start, vblank_end, vsync_start, vsync_end; | ||
794 | u32 dspcntr; | ||
795 | int clock; | ||
796 | |||
797 | vinfo->bytes_per_pixel = var->bits_per_pixel >> 3; | ||
798 | vinfo->stride = | ||
799 | __ALIGN_MASK(var->xres_virtual * vinfo->bytes_per_pixel, 0x3F); | ||
800 | info->fix.line_length = vinfo->stride; | ||
801 | |||
802 | if (!subsys) | ||
803 | return 0; | ||
804 | |||
805 | htotal = | ||
806 | var->xres + var->right_margin + var->hsync_len + var->left_margin; | ||
807 | hactive = var->xres; | ||
808 | hblank_start = var->xres; | ||
809 | hblank_end = htotal; | ||
810 | hsync_start = hactive + var->right_margin; | ||
811 | hsync_end = hsync_start + var->hsync_len; | ||
812 | |||
813 | vtotal = | ||
814 | var->yres + var->lower_margin + var->vsync_len + var->upper_margin; | ||
815 | vactive = var->yres; | ||
816 | vblank_start = var->yres; | ||
817 | vblank_end = vtotal; | ||
818 | vsync_start = vactive + var->lower_margin; | ||
819 | vsync_end = vsync_start + var->vsync_len; | ||
820 | |||
821 | dspcntr = VML_GFX_ENABLE | VML_GFX_GAMMABYPASS; | ||
822 | clock = PICOS2KHZ(var->pixclock); | ||
823 | |||
824 | if (subsys->nearest_clock) { | ||
825 | clock = subsys->nearest_clock(subsys, clock); | ||
826 | } else { | ||
827 | clock = vml_nearest_clock(clock); | ||
828 | } | ||
829 | printk(KERN_DEBUG MODULE_NAME | ||
830 | ": Set mode Hfreq : %d kHz, Vfreq : %d Hz.\n", clock / htotal, | ||
831 | ((clock / htotal) * 1000) / vtotal); | ||
832 | |||
833 | switch (var->bits_per_pixel) { | ||
834 | case 16: | ||
835 | dspcntr |= VML_GFX_ARGB1555; | ||
836 | break; | ||
837 | case 32: | ||
838 | if (var->transp.length == 8) | ||
839 | dspcntr |= VML_GFX_ARGB8888 | VML_GFX_ALPHAMULT; | ||
840 | else | ||
841 | dspcntr |= VML_GFX_RGB0888; | ||
842 | break; | ||
843 | default: | ||
844 | return -EINVAL; | ||
845 | } | ||
846 | |||
847 | vmlfb_disable_pipe(vinfo); | ||
848 | mb(); | ||
849 | |||
850 | if (subsys->set_clock) | ||
851 | subsys->set_clock(subsys, clock); | ||
852 | else | ||
853 | return -EINVAL; | ||
854 | |||
855 | VML_WRITE32(par, VML_HTOTAL_A, ((htotal - 1) << 16) | (hactive - 1)); | ||
856 | VML_WRITE32(par, VML_HBLANK_A, | ||
857 | ((hblank_end - 1) << 16) | (hblank_start - 1)); | ||
858 | VML_WRITE32(par, VML_HSYNC_A, | ||
859 | ((hsync_end - 1) << 16) | (hsync_start - 1)); | ||
860 | VML_WRITE32(par, VML_VTOTAL_A, ((vtotal - 1) << 16) | (vactive - 1)); | ||
861 | VML_WRITE32(par, VML_VBLANK_A, | ||
862 | ((vblank_end - 1) << 16) | (vblank_start - 1)); | ||
863 | VML_WRITE32(par, VML_VSYNC_A, | ||
864 | ((vsync_end - 1) << 16) | (vsync_start - 1)); | ||
865 | VML_WRITE32(par, VML_DSPCSTRIDE, vinfo->stride); | ||
866 | VML_WRITE32(par, VML_DSPCSIZE, | ||
867 | ((var->yres - 1) << 16) | (var->xres - 1)); | ||
868 | VML_WRITE32(par, VML_DSPCPOS, 0x00000000); | ||
869 | VML_WRITE32(par, VML_DSPARB, VML_FIFO_DEFAULT); | ||
870 | VML_WRITE32(par, VML_BCLRPAT_A, 0x00000000); | ||
871 | VML_WRITE32(par, VML_CANVSCLR_A, 0x00000000); | ||
872 | VML_WRITE32(par, VML_PIPEASRC, | ||
873 | ((var->xres - 1) << 16) | (var->yres - 1)); | ||
874 | |||
875 | wmb(); | ||
876 | VML_WRITE32(par, VML_PIPEACONF, VML_PIPE_ENABLE); | ||
877 | wmb(); | ||
878 | VML_WRITE32(par, VML_DSPCCNTR, dspcntr); | ||
879 | wmb(); | ||
880 | VML_WRITE32(par, VML_DSPCADDR, (u32) vinfo->vram_start + | ||
881 | var->yoffset * vinfo->stride + | ||
882 | var->xoffset * vinfo->bytes_per_pixel); | ||
883 | |||
884 | VML_WRITE32(par, VML_RCOMPSTAT, VML_MDVO_PAD_ENABLE); | ||
885 | |||
886 | while (!(VML_READ32(par, VML_RCOMPSTAT) & | ||
887 | (VML_MDVO_VDC_I_RCOMP | VML_MDVO_PAD_ENABLE))) ; | ||
888 | |||
889 | vinfo->pipe_disabled = 0; | ||
890 | #ifdef VERMILION_DEBUG | ||
891 | vml_dump_regs(vinfo); | ||
892 | #endif | ||
893 | |||
894 | return 0; | ||
895 | } | ||
896 | |||
897 | static int vmlfb_set_par(struct fb_info *info) | ||
898 | { | ||
899 | struct vml_info *vinfo = container_of(info, struct vml_info, info); | ||
900 | int ret; | ||
901 | |||
902 | mutex_lock(&vml_mutex); | ||
903 | list_del(&vinfo->head); | ||
904 | list_add(&vinfo->head, (subsys) ? &global_has_mode : &global_no_mode); | ||
905 | ret = vmlfb_set_par_locked(vinfo); | ||
906 | |||
907 | mutex_unlock(&vml_mutex); | ||
908 | return ret; | ||
909 | } | ||
910 | |||
911 | static int vmlfb_blank_locked(struct vml_info *vinfo) | ||
912 | { | ||
913 | struct vml_par *par = vinfo->par; | ||
914 | u32 cur = VML_READ32(par, VML_PIPEACONF); | ||
915 | |||
916 | switch (vinfo->cur_blank_mode) { | ||
917 | case FB_BLANK_UNBLANK: | ||
918 | if (vinfo->pipe_disabled) { | ||
919 | vmlfb_set_par_locked(vinfo); | ||
920 | } | ||
921 | VML_WRITE32(par, VML_PIPEACONF, cur & ~VML_PIPE_FORCE_BORDER); | ||
922 | (void)VML_READ32(par, VML_PIPEACONF); | ||
923 | break; | ||
924 | case FB_BLANK_NORMAL: | ||
925 | if (vinfo->pipe_disabled) { | ||
926 | vmlfb_set_par_locked(vinfo); | ||
927 | } | ||
928 | VML_WRITE32(par, VML_PIPEACONF, cur | VML_PIPE_FORCE_BORDER); | ||
929 | (void)VML_READ32(par, VML_PIPEACONF); | ||
930 | break; | ||
931 | case FB_BLANK_VSYNC_SUSPEND: | ||
932 | case FB_BLANK_HSYNC_SUSPEND: | ||
933 | if (!vinfo->pipe_disabled) { | ||
934 | vmlfb_disable_pipe(vinfo); | ||
935 | } | ||
936 | break; | ||
937 | case FB_BLANK_POWERDOWN: | ||
938 | if (!vinfo->pipe_disabled) { | ||
939 | vmlfb_disable_pipe(vinfo); | ||
940 | } | ||
941 | break; | ||
942 | default: | ||
943 | return -EINVAL; | ||
944 | } | ||
945 | |||
946 | return 0; | ||
947 | } | ||
948 | |||
949 | static int vmlfb_blank(int blank_mode, struct fb_info *info) | ||
950 | { | ||
951 | struct vml_info *vinfo = container_of(info, struct vml_info, info); | ||
952 | int ret; | ||
953 | |||
954 | mutex_lock(&vml_mutex); | ||
955 | vinfo->cur_blank_mode = blank_mode; | ||
956 | ret = vmlfb_blank_locked(vinfo); | ||
957 | mutex_unlock(&vml_mutex); | ||
958 | return ret; | ||
959 | } | ||
960 | |||
961 | static int vmlfb_pan_display(struct fb_var_screeninfo *var, | ||
962 | struct fb_info *info) | ||
963 | { | ||
964 | struct vml_info *vinfo = container_of(info, struct vml_info, info); | ||
965 | struct vml_par *par = vinfo->par; | ||
966 | |||
967 | mutex_lock(&vml_mutex); | ||
968 | VML_WRITE32(par, VML_DSPCADDR, (u32) vinfo->vram_start + | ||
969 | var->yoffset * vinfo->stride + | ||
970 | var->xoffset * vinfo->bytes_per_pixel); | ||
971 | (void)VML_READ32(par, VML_DSPCADDR); | ||
972 | mutex_unlock(&vml_mutex); | ||
973 | |||
974 | return 0; | ||
975 | } | ||
976 | |||
977 | static int vmlfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, | ||
978 | u_int transp, struct fb_info *info) | ||
979 | { | ||
980 | u32 v; | ||
981 | |||
982 | if (regno >= 16) | ||
983 | return -EINVAL; | ||
984 | |||
985 | if (info->var.grayscale) { | ||
986 | red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; | ||
987 | } | ||
988 | |||
989 | if (info->fix.visual != FB_VISUAL_TRUECOLOR) | ||
990 | return -EINVAL; | ||
991 | |||
992 | red = VML_TOHW(red, info->var.red.length); | ||
993 | blue = VML_TOHW(blue, info->var.blue.length); | ||
994 | green = VML_TOHW(green, info->var.green.length); | ||
995 | transp = VML_TOHW(transp, info->var.transp.length); | ||
996 | |||
997 | v = (red << info->var.red.offset) | | ||
998 | (green << info->var.green.offset) | | ||
999 | (blue << info->var.blue.offset) | | ||
1000 | (transp << info->var.transp.offset); | ||
1001 | |||
1002 | switch (info->var.bits_per_pixel) { | ||
1003 | case 16: | ||
1004 | ((u32 *) info->pseudo_palette)[regno] = v; | ||
1005 | break; | ||
1006 | case 24: | ||
1007 | case 32: | ||
1008 | ((u32 *) info->pseudo_palette)[regno] = v; | ||
1009 | break; | ||
1010 | } | ||
1011 | return 0; | ||
1012 | } | ||
1013 | |||
1014 | static int vmlfb_mmap(struct fb_info *info, struct vm_area_struct *vma) | ||
1015 | { | ||
1016 | struct vml_info *vinfo = container_of(info, struct vml_info, info); | ||
1017 | unsigned long size = vma->vm_end - vma->vm_start; | ||
1018 | unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; | ||
1019 | int ret; | ||
1020 | |||
1021 | if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) | ||
1022 | return -EINVAL; | ||
1023 | if (offset + size > vinfo->vram_contig_size) | ||
1024 | return -EINVAL; | ||
1025 | ret = vmlfb_vram_offset(vinfo, offset); | ||
1026 | if (ret) | ||
1027 | return -EINVAL; | ||
1028 | offset += vinfo->vram_start; | ||
1029 | pgprot_val(vma->vm_page_prot) |= _PAGE_PCD; | ||
1030 | pgprot_val(vma->vm_page_prot) &= ~_PAGE_PWT; | ||
1031 | vma->vm_flags |= VM_RESERVED | VM_IO; | ||
1032 | if (remap_pfn_range(vma, vma->vm_start, offset >> PAGE_SHIFT, | ||
1033 | size, vma->vm_page_prot)) | ||
1034 | return -EAGAIN; | ||
1035 | return 0; | ||
1036 | } | ||
1037 | |||
1038 | static int vmlfb_sync(struct fb_info *info) | ||
1039 | { | ||
1040 | return 0; | ||
1041 | } | ||
1042 | |||
1043 | static int vmlfb_cursor(struct fb_info *info, struct fb_cursor *cursor) | ||
1044 | { | ||
1045 | return -EINVAL; /* just to force soft_cursor() call */ | ||
1046 | } | ||
1047 | |||
1048 | static struct fb_ops vmlfb_ops = { | ||
1049 | .owner = THIS_MODULE, | ||
1050 | .fb_open = vmlfb_open, | ||
1051 | .fb_release = vmlfb_release, | ||
1052 | .fb_check_var = vmlfb_check_var, | ||
1053 | .fb_set_par = vmlfb_set_par, | ||
1054 | .fb_blank = vmlfb_blank, | ||
1055 | .fb_pan_display = vmlfb_pan_display, | ||
1056 | .fb_fillrect = cfb_fillrect, | ||
1057 | .fb_copyarea = cfb_copyarea, | ||
1058 | .fb_imageblit = cfb_imageblit, | ||
1059 | .fb_cursor = vmlfb_cursor, | ||
1060 | .fb_sync = vmlfb_sync, | ||
1061 | .fb_mmap = vmlfb_mmap, | ||
1062 | .fb_setcolreg = vmlfb_setcolreg | ||
1063 | }; | ||
1064 | |||
1065 | static struct pci_device_id vml_ids[] = { | ||
1066 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, VML_DEVICE_VDC)}, | ||
1067 | {0} | ||
1068 | }; | ||
1069 | |||
1070 | static struct pci_driver vmlfb_pci_driver = { | ||
1071 | .name = "vmlfb", | ||
1072 | .id_table = vml_ids, | ||
1073 | .probe = vml_pci_probe, | ||
1074 | .remove = __devexit_p(vml_pci_remove) | ||
1075 | }; | ||
1076 | |||
1077 | static void __exit vmlfb_cleanup(void) | ||
1078 | { | ||
1079 | pci_unregister_driver(&vmlfb_pci_driver); | ||
1080 | } | ||
1081 | |||
1082 | static int __init vmlfb_init(void) | ||
1083 | { | ||
1084 | |||
1085 | #ifndef MODULE | ||
1086 | char *option = NULL; | ||
1087 | |||
1088 | if (fb_get_options(MODULE_NAME, &option)) | ||
1089 | return -ENODEV; | ||
1090 | #endif | ||
1091 | |||
1092 | printk(KERN_DEBUG MODULE_NAME ": initializing\n"); | ||
1093 | mutex_init(&vml_mutex); | ||
1094 | INIT_LIST_HEAD(&global_no_mode); | ||
1095 | INIT_LIST_HEAD(&global_has_mode); | ||
1096 | |||
1097 | return pci_register_driver(&vmlfb_pci_driver); | ||
1098 | } | ||
1099 | |||
1100 | int vmlfb_register_subsys(struct vml_sys *sys) | ||
1101 | { | ||
1102 | struct vml_info *entry; | ||
1103 | struct list_head *list; | ||
1104 | u32 save_activate; | ||
1105 | |||
1106 | mutex_lock(&vml_mutex); | ||
1107 | if (subsys != NULL) { | ||
1108 | subsys->restore(subsys); | ||
1109 | } | ||
1110 | subsys = sys; | ||
1111 | subsys->save(subsys); | ||
1112 | |||
1113 | /* | ||
1114 | * We need to restart list traversal for each item, since we | ||
1115 | * release the list mutex in the loop. | ||
1116 | */ | ||
1117 | |||
1118 | list = global_no_mode.next; | ||
1119 | while (list != &global_no_mode) { | ||
1120 | list_del_init(list); | ||
1121 | entry = list_entry(list, struct vml_info, head); | ||
1122 | |||
1123 | /* | ||
1124 | * First, try the current mode which might not be | ||
1125 | * completely validated with respect to the pixel clock. | ||
1126 | */ | ||
1127 | |||
1128 | if (!vmlfb_check_var_locked(&entry->info.var, entry)) { | ||
1129 | vmlfb_set_par_locked(entry); | ||
1130 | list_add_tail(list, &global_has_mode); | ||
1131 | } else { | ||
1132 | |||
1133 | /* | ||
1134 | * Didn't work. Try to find another mode, | ||
1135 | * that matches this subsys. | ||
1136 | */ | ||
1137 | |||
1138 | mutex_unlock(&vml_mutex); | ||
1139 | save_activate = entry->info.var.activate; | ||
1140 | entry->info.var.bits_per_pixel = 16; | ||
1141 | vmlfb_set_pref_pixel_format(&entry->info.var); | ||
1142 | if (fb_find_mode(&entry->info.var, | ||
1143 | &entry->info, | ||
1144 | vml_default_mode, NULL, 0, NULL, 16)) { | ||
1145 | entry->info.var.activate |= | ||
1146 | FB_ACTIVATE_FORCE | FB_ACTIVATE_NOW; | ||
1147 | fb_set_var(&entry->info, &entry->info.var); | ||
1148 | } else { | ||
1149 | printk(KERN_ERR MODULE_NAME | ||
1150 | ": Sorry. no mode found for this subsys.\n"); | ||
1151 | } | ||
1152 | entry->info.var.activate = save_activate; | ||
1153 | mutex_lock(&vml_mutex); | ||
1154 | } | ||
1155 | vmlfb_blank_locked(entry); | ||
1156 | list = global_no_mode.next; | ||
1157 | } | ||
1158 | mutex_unlock(&vml_mutex); | ||
1159 | |||
1160 | printk(KERN_DEBUG MODULE_NAME ": Registered %s subsystem.\n", | ||
1161 | subsys->name ? subsys->name : "unknown"); | ||
1162 | return 0; | ||
1163 | } | ||
1164 | |||
1165 | EXPORT_SYMBOL_GPL(vmlfb_register_subsys); | ||
1166 | |||
1167 | void vmlfb_unregister_subsys(struct vml_sys *sys) | ||
1168 | { | ||
1169 | struct vml_info *entry, *next; | ||
1170 | |||
1171 | mutex_lock(&vml_mutex); | ||
1172 | if (subsys != sys) { | ||
1173 | mutex_unlock(&vml_mutex); | ||
1174 | return; | ||
1175 | } | ||
1176 | subsys->restore(subsys); | ||
1177 | subsys = NULL; | ||
1178 | list_for_each_entry_safe(entry, next, &global_has_mode, head) { | ||
1179 | printk(KERN_DEBUG MODULE_NAME ": subsys disable pipe\n"); | ||
1180 | vmlfb_disable_pipe(entry); | ||
1181 | list_del(&entry->head); | ||
1182 | list_add_tail(&entry->head, &global_no_mode); | ||
1183 | } | ||
1184 | mutex_unlock(&vml_mutex); | ||
1185 | } | ||
1186 | |||
1187 | EXPORT_SYMBOL_GPL(vmlfb_unregister_subsys); | ||
1188 | |||
1189 | module_init(vmlfb_init); | ||
1190 | module_exit(vmlfb_cleanup); | ||
1191 | |||
1192 | MODULE_AUTHOR("Tungsten Graphics"); | ||
1193 | MODULE_DESCRIPTION("Initialization of the Vermilion display devices"); | ||
1194 | MODULE_VERSION("1.0.0"); | ||
1195 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/vermilion/vermilion.h b/drivers/video/vermilion/vermilion.h new file mode 100644 index 000000000000..1fc6695a49d2 --- /dev/null +++ b/drivers/video/vermilion/vermilion.h | |||
@@ -0,0 +1,260 @@ | |||
1 | /* | ||
2 | * Copyright (c) Intel Corp. 2007. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to | ||
6 | * develop this driver. | ||
7 | * | ||
8 | * This file is part of the Vermilion Range fb driver. | ||
9 | * The Vermilion Range fb driver is free software; | ||
10 | * you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * The Vermilion Range fb driver is distributed | ||
16 | * in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this driver; if not, write to the Free Software | ||
23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
24 | * | ||
25 | * Authors: | ||
26 | * Thomas Hellström <thomas-at-tungstengraphics-dot-com> | ||
27 | */ | ||
28 | |||
29 | #ifndef _VERMILION_H_ | ||
30 | #define _VERMILION_H_ | ||
31 | |||
32 | #include <linux/kernel.h> | ||
33 | #include <linux/version.h> | ||
34 | #include <linux/pci.h> | ||
35 | #include <asm/atomic.h> | ||
36 | #include <linux/mutex.h> | ||
37 | |||
38 | #define VML_DEVICE_GPU 0x5002 | ||
39 | #define VML_DEVICE_VDC 0x5009 | ||
40 | |||
41 | #define VML_VRAM_AREAS 3 | ||
42 | #define VML_MAX_XRES 1024 | ||
43 | #define VML_MAX_YRES 768 | ||
44 | #define VML_MAX_XRES_VIRTUAL 1040 | ||
45 | |||
46 | /* | ||
47 | * Display controller registers: | ||
48 | */ | ||
49 | |||
50 | /* Display controller 10-bit color representation */ | ||
51 | |||
52 | #define VML_R_MASK 0x3FF00000 | ||
53 | #define VML_R_SHIFT 20 | ||
54 | #define VML_G_MASK 0x000FFC00 | ||
55 | #define VML_G_SHIFT 10 | ||
56 | #define VML_B_MASK 0x000003FF | ||
57 | #define VML_B_SHIFT 0 | ||
58 | |||
59 | /* Graphics plane control */ | ||
60 | #define VML_DSPCCNTR 0x00072180 | ||
61 | #define VML_GFX_ENABLE 0x80000000 | ||
62 | #define VML_GFX_GAMMABYPASS 0x40000000 | ||
63 | #define VML_GFX_ARGB1555 0x0C000000 | ||
64 | #define VML_GFX_RGB0888 0x18000000 | ||
65 | #define VML_GFX_ARGB8888 0x1C000000 | ||
66 | #define VML_GFX_ALPHACONST 0x02000000 | ||
67 | #define VML_GFX_ALPHAMULT 0x01000000 | ||
68 | #define VML_GFX_CONST_ALPHA 0x000000FF | ||
69 | |||
70 | /* Graphics plane start address. Pixel aligned. */ | ||
71 | #define VML_DSPCADDR 0x00072184 | ||
72 | |||
73 | /* Graphics plane stride register. */ | ||
74 | #define VML_DSPCSTRIDE 0x00072188 | ||
75 | |||
76 | /* Graphics plane position register. */ | ||
77 | #define VML_DSPCPOS 0x0007218C | ||
78 | #define VML_POS_YMASK 0x0FFF0000 | ||
79 | #define VML_POS_YSHIFT 16 | ||
80 | #define VML_POS_XMASK 0x00000FFF | ||
81 | #define VML_POS_XSHIFT 0 | ||
82 | |||
83 | /* Graphics plane height and width */ | ||
84 | #define VML_DSPCSIZE 0x00072190 | ||
85 | #define VML_SIZE_HMASK 0x0FFF0000 | ||
86 | #define VML_SIZE_HSHIFT 16 | ||
87 | #define VML_SISE_WMASK 0x00000FFF | ||
88 | #define VML_SIZE_WSHIFT 0 | ||
89 | |||
90 | /* Graphics plane gamma correction lookup table registers (129 * 32 bits) */ | ||
91 | #define VML_DSPCGAMLUT 0x00072200 | ||
92 | |||
93 | /* Pixel video output configuration register */ | ||
94 | #define VML_PVOCONFIG 0x00061140 | ||
95 | #define VML_CONFIG_BASE 0x80000000 | ||
96 | #define VML_CONFIG_PIXEL_SWAP 0x04000000 | ||
97 | #define VML_CONFIG_DE_INV 0x01000000 | ||
98 | #define VML_CONFIG_HREF_INV 0x00400000 | ||
99 | #define VML_CONFIG_VREF_INV 0x00100000 | ||
100 | #define VML_CONFIG_CLK_INV 0x00040000 | ||
101 | #define VML_CONFIG_CLK_DIV2 0x00010000 | ||
102 | #define VML_CONFIG_ESTRB_INV 0x00008000 | ||
103 | |||
104 | /* Pipe A Horizontal total register */ | ||
105 | #define VML_HTOTAL_A 0x00060000 | ||
106 | #define VML_HTOTAL_MASK 0x1FFF0000 | ||
107 | #define VML_HTOTAL_SHIFT 16 | ||
108 | #define VML_HTOTAL_VAL 8192 | ||
109 | #define VML_HACTIVE_MASK 0x000007FF | ||
110 | #define VML_HACTIVE_SHIFT 0 | ||
111 | #define VML_HACTIVE_VAL 4096 | ||
112 | |||
113 | /* Pipe A Horizontal Blank register */ | ||
114 | #define VML_HBLANK_A 0x00060004 | ||
115 | #define VML_HBLANK_END_MASK 0x1FFF0000 | ||
116 | #define VML_HBLANK_END_SHIFT 16 | ||
117 | #define VML_HBLANK_END_VAL 8192 | ||
118 | #define VML_HBLANK_START_MASK 0x00001FFF | ||
119 | #define VML_HBLANK_START_SHIFT 0 | ||
120 | #define VML_HBLANK_START_VAL 8192 | ||
121 | |||
122 | /* Pipe A Horizontal Sync register */ | ||
123 | #define VML_HSYNC_A 0x00060008 | ||
124 | #define VML_HSYNC_END_MASK 0x1FFF0000 | ||
125 | #define VML_HSYNC_END_SHIFT 16 | ||
126 | #define VML_HSYNC_END_VAL 8192 | ||
127 | #define VML_HSYNC_START_MASK 0x00001FFF | ||
128 | #define VML_HSYNC_START_SHIFT 0 | ||
129 | #define VML_HSYNC_START_VAL 8192 | ||
130 | |||
131 | /* Pipe A Vertical total register */ | ||
132 | #define VML_VTOTAL_A 0x0006000C | ||
133 | #define VML_VTOTAL_MASK 0x1FFF0000 | ||
134 | #define VML_VTOTAL_SHIFT 16 | ||
135 | #define VML_VTOTAL_VAL 8192 | ||
136 | #define VML_VACTIVE_MASK 0x000007FF | ||
137 | #define VML_VACTIVE_SHIFT 0 | ||
138 | #define VML_VACTIVE_VAL 4096 | ||
139 | |||
140 | /* Pipe A Vertical Blank register */ | ||
141 | #define VML_VBLANK_A 0x00060010 | ||
142 | #define VML_VBLANK_END_MASK 0x1FFF0000 | ||
143 | #define VML_VBLANK_END_SHIFT 16 | ||
144 | #define VML_VBLANK_END_VAL 8192 | ||
145 | #define VML_VBLANK_START_MASK 0x00001FFF | ||
146 | #define VML_VBLANK_START_SHIFT 0 | ||
147 | #define VML_VBLANK_START_VAL 8192 | ||
148 | |||
149 | /* Pipe A Vertical Sync register */ | ||
150 | #define VML_VSYNC_A 0x00060014 | ||
151 | #define VML_VSYNC_END_MASK 0x1FFF0000 | ||
152 | #define VML_VSYNC_END_SHIFT 16 | ||
153 | #define VML_VSYNC_END_VAL 8192 | ||
154 | #define VML_VSYNC_START_MASK 0x00001FFF | ||
155 | #define VML_VSYNC_START_SHIFT 0 | ||
156 | #define VML_VSYNC_START_VAL 8192 | ||
157 | |||
158 | /* Pipe A Source Image size (minus one - equal to active size) | ||
159 | * Programmable while pipe is enabled. | ||
160 | */ | ||
161 | #define VML_PIPEASRC 0x0006001C | ||
162 | #define VML_PIPEASRC_HMASK 0x0FFF0000 | ||
163 | #define VML_PIPEASRC_HSHIFT 16 | ||
164 | #define VML_PIPEASRC_VMASK 0x00000FFF | ||
165 | #define VML_PIPEASRC_VSHIFT 0 | ||
166 | |||
167 | /* Pipe A Border Color Pattern register (10 bit color) */ | ||
168 | #define VML_BCLRPAT_A 0x00060020 | ||
169 | |||
170 | /* Pipe A Canvas Color register (10 bit color) */ | ||
171 | #define VML_CANVSCLR_A 0x00060024 | ||
172 | |||
173 | /* Pipe A Configuration register */ | ||
174 | #define VML_PIPEACONF 0x00070008 | ||
175 | #define VML_PIPE_BASE 0x00000000 | ||
176 | #define VML_PIPE_ENABLE 0x80000000 | ||
177 | #define VML_PIPE_FORCE_BORDER 0x02000000 | ||
178 | #define VML_PIPE_PLANES_OFF 0x00080000 | ||
179 | #define VML_PIPE_ARGB_OUTPUT_MODE 0x00040000 | ||
180 | |||
181 | /* Pipe A FIFO setting */ | ||
182 | #define VML_DSPARB 0x00070030 | ||
183 | #define VML_FIFO_DEFAULT 0x00001D9C | ||
184 | |||
185 | /* MDVO rcomp status & pads control register */ | ||
186 | #define VML_RCOMPSTAT 0x00070048 | ||
187 | #define VML_MDVO_VDC_I_RCOMP 0x80000000 | ||
188 | #define VML_MDVO_POWERSAVE_OFF 0x00000008 | ||
189 | #define VML_MDVO_PAD_ENABLE 0x00000004 | ||
190 | #define VML_MDVO_PULLDOWN_ENABLE 0x00000001 | ||
191 | |||
192 | struct vml_par { | ||
193 | struct pci_dev *vdc; | ||
194 | u64 vdc_mem_base; | ||
195 | u64 vdc_mem_size; | ||
196 | char __iomem *vdc_mem; | ||
197 | |||
198 | struct pci_dev *gpu; | ||
199 | u64 gpu_mem_base; | ||
200 | u64 gpu_mem_size; | ||
201 | char __iomem *gpu_mem; | ||
202 | |||
203 | atomic_t refcount; | ||
204 | }; | ||
205 | |||
206 | struct vram_area { | ||
207 | unsigned long logical; | ||
208 | unsigned long phys; | ||
209 | unsigned long size; | ||
210 | unsigned order; | ||
211 | }; | ||
212 | |||
213 | struct vml_info { | ||
214 | struct fb_info info; | ||
215 | struct vml_par *par; | ||
216 | struct list_head head; | ||
217 | struct vram_area vram[VML_VRAM_AREAS]; | ||
218 | u64 vram_start; | ||
219 | u64 vram_contig_size; | ||
220 | u32 num_areas; | ||
221 | void __iomem *vram_logical; | ||
222 | u32 pseudo_palette[16]; | ||
223 | u32 stride; | ||
224 | u32 bytes_per_pixel; | ||
225 | atomic_t vmas; | ||
226 | int cur_blank_mode; | ||
227 | int pipe_disabled; | ||
228 | }; | ||
229 | |||
230 | /* | ||
231 | * Subsystem | ||
232 | */ | ||
233 | |||
234 | struct vml_sys { | ||
235 | char *name; | ||
236 | |||
237 | /* | ||
238 | * Save / Restore; | ||
239 | */ | ||
240 | |||
241 | int (*save) (struct vml_sys * sys); | ||
242 | int (*restore) (struct vml_sys * sys); | ||
243 | |||
244 | /* | ||
245 | * PLL programming; | ||
246 | */ | ||
247 | |||
248 | int (*set_clock) (struct vml_sys * sys, int clock); | ||
249 | int (*nearest_clock) (const struct vml_sys * sys, int clock); | ||
250 | }; | ||
251 | |||
252 | extern int vmlfb_register_subsys(struct vml_sys *sys); | ||
253 | extern void vmlfb_unregister_subsys(struct vml_sys *sys); | ||
254 | |||
255 | #define VML_READ32(_par, _offset) \ | ||
256 | (ioread32((_par)->vdc_mem + (_offset))) | ||
257 | #define VML_WRITE32(_par, _offset, _value) \ | ||
258 | iowrite32(_value, (_par)->vdc_mem + (_offset)) | ||
259 | |||
260 | #endif | ||