Commit | Line | Data |
---|---|---|
f045f77b JC |
1 | /* |
2 | * Copyright 1998-2009 VIA Technologies, Inc. All Rights Reserved. | |
3 | * Copyright 2001-2008 S3 Graphics, Inc. All Rights Reserved. | |
4 | * Copyright 2009 Jonathan Corbet <corbet@lwn.net> | |
5 | */ | |
6 | ||
7 | /* | |
8 | * Core code for the Via multifunction framebuffer device. | |
9 | */ | |
24b4d82e JC |
10 | #include "via-core.h" |
11 | #include "via_i2c.h" | |
12 | #include "global.h" | |
13 | ||
f045f77b JC |
14 | #include <linux/module.h> |
15 | #include <linux/platform_device.h> | |
f045f77b JC |
16 | |
17 | /* | |
18 | * The default port config. | |
19 | */ | |
20 | static struct via_port_cfg adap_configs[] = { | |
21 | [VIA_PORT_26] = { VIA_PORT_I2C, VIA_MODE_OFF, VIASR, 0x26 }, | |
22 | [VIA_PORT_31] = { VIA_PORT_I2C, VIA_MODE_I2C, VIASR, 0x31 }, | |
23 | [VIA_PORT_25] = { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x25 }, | |
24 | [VIA_PORT_2C] = { VIA_PORT_GPIO, VIA_MODE_I2C, VIASR, 0x2c }, | |
25 | [VIA_PORT_3D] = { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x3d }, | |
26 | { 0, 0, 0, 0 } | |
27 | }; | |
28 | ||
24b4d82e JC |
29 | /* |
30 | * We currently only support one viafb device (will there ever be | |
31 | * more than one?), so just declare it globally here. | |
32 | */ | |
33 | static struct viafb_dev global_dev; | |
34 | ||
35 | ||
36 | /* | |
37 | * Figure out how big our framebuffer memory is. Kind of ugly, | |
38 | * but evidently we can't trust the information found in the | |
39 | * fbdev configuration area. | |
40 | */ | |
41 | static u16 via_function3[] = { | |
42 | CLE266_FUNCTION3, KM400_FUNCTION3, CN400_FUNCTION3, CN700_FUNCTION3, | |
43 | CX700_FUNCTION3, KM800_FUNCTION3, KM890_FUNCTION3, P4M890_FUNCTION3, | |
44 | P4M900_FUNCTION3, VX800_FUNCTION3, VX855_FUNCTION3, | |
45 | }; | |
46 | ||
47 | /* Get the BIOS-configured framebuffer size from PCI configuration space | |
48 | * of function 3 in the respective chipset */ | |
49 | static int viafb_get_fb_size_from_pci(int chip_type) | |
50 | { | |
51 | int i; | |
52 | u8 offset = 0; | |
53 | u32 FBSize; | |
54 | u32 VideoMemSize; | |
55 | ||
56 | /* search for the "FUNCTION3" device in this chipset */ | |
57 | for (i = 0; i < ARRAY_SIZE(via_function3); i++) { | |
58 | struct pci_dev *pdev; | |
59 | ||
60 | pdev = pci_get_device(PCI_VENDOR_ID_VIA, via_function3[i], | |
61 | NULL); | |
62 | if (!pdev) | |
63 | continue; | |
64 | ||
65 | DEBUG_MSG(KERN_INFO "Device ID = %x\n", pdev->device); | |
66 | ||
67 | switch (pdev->device) { | |
68 | case CLE266_FUNCTION3: | |
69 | case KM400_FUNCTION3: | |
70 | offset = 0xE0; | |
71 | break; | |
72 | case CN400_FUNCTION3: | |
73 | case CN700_FUNCTION3: | |
74 | case CX700_FUNCTION3: | |
75 | case KM800_FUNCTION3: | |
76 | case KM890_FUNCTION3: | |
77 | case P4M890_FUNCTION3: | |
78 | case P4M900_FUNCTION3: | |
79 | case VX800_FUNCTION3: | |
80 | case VX855_FUNCTION3: | |
81 | /*case CN750_FUNCTION3: */ | |
82 | offset = 0xA0; | |
83 | break; | |
84 | } | |
85 | ||
86 | if (!offset) | |
87 | break; | |
88 | ||
89 | pci_read_config_dword(pdev, offset, &FBSize); | |
90 | pci_dev_put(pdev); | |
91 | } | |
92 | ||
93 | if (!offset) { | |
94 | printk(KERN_ERR "cannot determine framebuffer size\n"); | |
95 | return -EIO; | |
96 | } | |
97 | ||
98 | FBSize = FBSize & 0x00007000; | |
99 | DEBUG_MSG(KERN_INFO "FB Size = %x\n", FBSize); | |
100 | ||
101 | if (chip_type < UNICHROME_CX700) { | |
102 | switch (FBSize) { | |
103 | case 0x00004000: | |
104 | VideoMemSize = (16 << 20); /*16M */ | |
105 | break; | |
106 | ||
107 | case 0x00005000: | |
108 | VideoMemSize = (32 << 20); /*32M */ | |
109 | break; | |
110 | ||
111 | case 0x00006000: | |
112 | VideoMemSize = (64 << 20); /*64M */ | |
113 | break; | |
114 | ||
115 | default: | |
116 | VideoMemSize = (32 << 20); /*32M */ | |
117 | break; | |
118 | } | |
119 | } else { | |
120 | switch (FBSize) { | |
121 | case 0x00001000: | |
122 | VideoMemSize = (8 << 20); /*8M */ | |
123 | break; | |
124 | ||
125 | case 0x00002000: | |
126 | VideoMemSize = (16 << 20); /*16M */ | |
127 | break; | |
128 | ||
129 | case 0x00003000: | |
130 | VideoMemSize = (32 << 20); /*32M */ | |
131 | break; | |
132 | ||
133 | case 0x00004000: | |
134 | VideoMemSize = (64 << 20); /*64M */ | |
135 | break; | |
136 | ||
137 | case 0x00005000: | |
138 | VideoMemSize = (128 << 20); /*128M */ | |
139 | break; | |
140 | ||
141 | case 0x00006000: | |
142 | VideoMemSize = (256 << 20); /*256M */ | |
143 | break; | |
144 | ||
145 | case 0x00007000: /* Only on VX855/875 */ | |
146 | VideoMemSize = (512 << 20); /*512M */ | |
147 | break; | |
148 | ||
149 | default: | |
150 | VideoMemSize = (32 << 20); /*32M */ | |
151 | break; | |
152 | } | |
153 | } | |
154 | ||
155 | return VideoMemSize; | |
156 | } | |
157 | ||
158 | ||
159 | /* | |
160 | * Figure out and map our MMIO regions. | |
161 | */ | |
162 | static int __devinit via_pci_setup_mmio(struct viafb_dev *vdev) | |
163 | { | |
164 | /* | |
165 | * Hook up to the device registers. | |
166 | */ | |
167 | vdev->engine_start = pci_resource_start(vdev->pdev, 1); | |
168 | vdev->engine_len = pci_resource_len(vdev->pdev, 1); | |
169 | /* If this fails, others will notice later */ | |
170 | vdev->engine_mmio = ioremap_nocache(vdev->engine_start, | |
171 | vdev->engine_len); | |
172 | ||
173 | /* | |
174 | * Likewise with I/O memory. | |
175 | */ | |
176 | vdev->fbmem_start = pci_resource_start(vdev->pdev, 0); | |
177 | vdev->fbmem_len = viafb_get_fb_size_from_pci(vdev->chip_type); | |
178 | if (vdev->fbmem_len < 0) | |
179 | return vdev->fbmem_len; | |
180 | vdev->fbmem = ioremap_nocache(vdev->fbmem_start, vdev->fbmem_len); | |
181 | if (vdev->fbmem == NULL) | |
182 | return -ENOMEM; | |
183 | return 0; | |
184 | } | |
185 | ||
186 | static void __devexit via_pci_teardown_mmio(struct viafb_dev *vdev) | |
187 | { | |
188 | iounmap(vdev->fbmem); | |
189 | iounmap(vdev->engine_mmio); | |
190 | } | |
191 | ||
f045f77b JC |
192 | |
193 | static int __devinit via_pci_probe(struct pci_dev *pdev, | |
194 | const struct pci_device_id *ent) | |
195 | { | |
196 | int ret; | |
197 | ||
198 | ret = pci_enable_device(pdev); | |
199 | if (ret) | |
200 | return ret; | |
24b4d82e JC |
201 | /* |
202 | * Global device initialization. | |
203 | */ | |
204 | memset(&global_dev, 0, sizeof(global_dev)); | |
205 | global_dev.pdev = pdev; | |
206 | global_dev.chip_type = ent->driver_data; | |
207 | spin_lock_init(&global_dev.reg_lock); | |
208 | ret = via_pci_setup_mmio(&global_dev); | |
209 | if (ret) | |
210 | goto out_disable; | |
f045f77b JC |
211 | /* |
212 | * Create the I2C busses. Bailing out on failure seems extreme, | |
213 | * but that's what the code did before. | |
214 | */ | |
215 | ret = viafb_create_i2c_busses(adap_configs); | |
216 | if (ret) | |
24b4d82e | 217 | goto out_teardown; |
f045f77b JC |
218 | /* |
219 | * Set up the framebuffer. | |
220 | */ | |
24b4d82e | 221 | ret = via_fb_pci_probe(&global_dev); |
f045f77b JC |
222 | if (ret) |
223 | goto out_i2c; | |
224 | return 0; | |
225 | ||
226 | out_i2c: | |
227 | viafb_delete_i2c_busses(); | |
24b4d82e JC |
228 | out_teardown: |
229 | via_pci_teardown_mmio(&global_dev); | |
f045f77b JC |
230 | out_disable: |
231 | pci_disable_device(pdev); | |
232 | return ret; | |
233 | } | |
234 | ||
235 | static void __devexit via_pci_remove(struct pci_dev *pdev) | |
236 | { | |
237 | viafb_delete_i2c_busses(); | |
238 | via_fb_pci_remove(pdev); | |
24b4d82e | 239 | via_pci_teardown_mmio(&global_dev); |
f045f77b JC |
240 | pci_disable_device(pdev); |
241 | } | |
242 | ||
243 | ||
244 | static struct pci_device_id via_pci_table[] __devinitdata = { | |
245 | { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CLE266_DID), | |
246 | .driver_data = UNICHROME_CLE266 }, | |
247 | { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_PM800_DID), | |
248 | .driver_data = UNICHROME_PM800 }, | |
249 | { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_K400_DID), | |
250 | .driver_data = UNICHROME_K400 }, | |
251 | { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_K800_DID), | |
252 | .driver_data = UNICHROME_K800 }, | |
253 | { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_P4M890_DID), | |
254 | .driver_data = UNICHROME_CN700 }, | |
255 | { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_K8M890_DID), | |
256 | .driver_data = UNICHROME_K8M890 }, | |
257 | { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CX700_DID), | |
258 | .driver_data = UNICHROME_CX700 }, | |
259 | { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_P4M900_DID), | |
260 | .driver_data = UNICHROME_P4M900 }, | |
261 | { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CN750_DID), | |
262 | .driver_data = UNICHROME_CN750 }, | |
263 | { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_VX800_DID), | |
264 | .driver_data = UNICHROME_VX800 }, | |
265 | { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_VX855_DID), | |
266 | .driver_data = UNICHROME_VX855 }, | |
267 | { } | |
268 | }; | |
269 | MODULE_DEVICE_TABLE(pci, via_pci_table); | |
270 | ||
271 | static struct pci_driver via_driver = { | |
272 | .name = "viafb", | |
273 | .id_table = via_pci_table, | |
274 | .probe = via_pci_probe, | |
275 | .remove = __devexit_p(via_pci_remove), | |
276 | }; | |
277 | ||
278 | static int __init via_core_init(void) | |
279 | { | |
280 | int ret; | |
281 | ||
282 | ret = viafb_init(); | |
283 | if (ret) | |
284 | return ret; | |
285 | return pci_register_driver(&via_driver); | |
286 | } | |
287 | ||
288 | static void __exit via_core_exit(void) | |
289 | { | |
290 | pci_unregister_driver(&via_driver); | |
291 | viafb_exit(); | |
292 | } | |
293 | ||
294 | module_init(via_core_init); | |
295 | module_exit(via_core_exit); |