1 | /* |
2 | * Copyright 2012 Red Hat Inc. |
3 | * |
4 | * Permission is hereby granted, free of charge, to any person obtaining a |
5 | * copy of this software and associated documentation files (the |
6 | * "Software"), to deal in the Software without restriction, including |
7 | * without limitation the rights to use, copy, modify, merge, publish, |
8 | * distribute, sub license, and/or sell copies of the Software, and to |
9 | * permit persons to whom the Software is furnished to do so, subject to |
10 | * the following conditions: |
11 | * |
12 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
13 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
14 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL |
15 | * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, |
16 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
17 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
18 | * USE OR OTHER DEALINGS IN THE SOFTWARE. |
19 | * |
20 | * The above copyright notice and this permission notice (including the |
21 | * next paragraph) shall be included in all copies or substantial portions |
22 | * of the Software. |
23 | * |
24 | */ |
25 | /* |
26 | * Authors: Dave Airlie <airlied@redhat.com> |
27 | */ |
28 | |
29 | #include <linux/module.h> |
30 | #include <linux/pci.h> |
31 | |
32 | #include <drm/drm_aperture.h> |
33 | #include <drm/drm_atomic_helper.h> |
34 | #include <drm/drm_drv.h> |
35 | #include <drm/drm_fbdev_generic.h> |
36 | #include <drm/drm_gem_shmem_helper.h> |
37 | #include <drm/drm_module.h> |
38 | #include <drm/drm_probe_helper.h> |
39 | |
40 | #include "ast_drv.h" |
41 | |
42 | static int ast_modeset = -1; |
43 | |
44 | MODULE_PARM_DESC(modeset, "Disable/Enable modesetting" ); |
45 | module_param_named(modeset, ast_modeset, int, 0400); |
46 | |
47 | /* |
48 | * DRM driver |
49 | */ |
50 | |
51 | DEFINE_DRM_GEM_FOPS(ast_fops); |
52 | |
53 | static const struct drm_driver ast_driver = { |
54 | .driver_features = DRIVER_ATOMIC | |
55 | DRIVER_GEM | |
56 | DRIVER_MODESET, |
57 | |
58 | .fops = &ast_fops, |
59 | .name = DRIVER_NAME, |
60 | .desc = DRIVER_DESC, |
61 | .date = DRIVER_DATE, |
62 | .major = DRIVER_MAJOR, |
63 | .minor = DRIVER_MINOR, |
64 | .patchlevel = DRIVER_PATCHLEVEL, |
65 | |
66 | DRM_GEM_SHMEM_DRIVER_OPS |
67 | }; |
68 | |
69 | /* |
70 | * PCI driver |
71 | */ |
72 | |
73 | #define PCI_VENDOR_ASPEED 0x1a03 |
74 | |
75 | #define AST_VGA_DEVICE(id, info) { \ |
76 | .class = PCI_BASE_CLASS_DISPLAY << 16, \ |
77 | .class_mask = 0xff0000, \ |
78 | .vendor = PCI_VENDOR_ASPEED, \ |
79 | .device = id, \ |
80 | .subvendor = PCI_ANY_ID, \ |
81 | .subdevice = PCI_ANY_ID, \ |
82 | .driver_data = (unsigned long) info } |
83 | |
84 | static const struct pci_device_id ast_pciidlist[] = { |
85 | AST_VGA_DEVICE(PCI_CHIP_AST2000, NULL), |
86 | AST_VGA_DEVICE(PCI_CHIP_AST2100, NULL), |
87 | {0, 0, 0}, |
88 | }; |
89 | |
90 | MODULE_DEVICE_TABLE(pci, ast_pciidlist); |
91 | |
92 | static bool ast_is_vga_enabled(void __iomem *ioregs) |
93 | { |
94 | u8 vgaer = __ast_read8(addr: ioregs, AST_IO_VGAER); |
95 | |
96 | return vgaer & AST_IO_VGAER_VGA_ENABLE; |
97 | } |
98 | |
99 | static void ast_enable_vga(void __iomem *ioregs) |
100 | { |
101 | __ast_write8(addr: ioregs, AST_IO_VGAER, AST_IO_VGAER_VGA_ENABLE); |
102 | __ast_write8(addr: ioregs, AST_IO_VGAMR_W, AST_IO_VGAMR_IOSEL); |
103 | } |
104 | |
105 | /* |
106 | * Run this function as part of the HW device cleanup; not |
107 | * when the DRM device gets released. |
108 | */ |
109 | static void ast_enable_mmio_release(void *data) |
110 | { |
111 | void __iomem *ioregs = (void __force __iomem *)data; |
112 | |
113 | /* enable standard VGA decode */ |
114 | __ast_write8_i(addr: ioregs, AST_IO_VGACRI, index: 0xa1, AST_IO_VGACRA1_MMIO_ENABLED); |
115 | } |
116 | |
117 | static int ast_enable_mmio(struct device *dev, void __iomem *ioregs) |
118 | { |
119 | void *data = (void __force *)ioregs; |
120 | |
121 | __ast_write8_i(addr: ioregs, AST_IO_VGACRI, index: 0xa1, |
122 | AST_IO_VGACRA1_MMIO_ENABLED | |
123 | AST_IO_VGACRA1_VGAIO_DISABLED); |
124 | |
125 | return devm_add_action_or_reset(dev, ast_enable_mmio_release, data); |
126 | } |
127 | |
128 | static void ast_open_key(void __iomem *ioregs) |
129 | { |
130 | __ast_write8_i(addr: ioregs, AST_IO_VGACRI, index: 0x80, AST_IO_VGACR80_PASSWORD); |
131 | } |
132 | |
133 | static int ast_detect_chip(struct pci_dev *pdev, |
134 | void __iomem *regs, void __iomem *ioregs, |
135 | enum ast_chip *chip_out, |
136 | enum ast_config_mode *config_mode_out) |
137 | { |
138 | struct device *dev = &pdev->dev; |
139 | struct device_node *np = dev->of_node; |
140 | enum ast_config_mode config_mode = ast_use_defaults; |
141 | uint32_t scu_rev = 0xffffffff; |
142 | enum ast_chip chip; |
143 | u32 data; |
144 | u8 vgacrd0, vgacrd1; |
145 | |
146 | /* |
147 | * Find configuration mode and read SCU revision |
148 | */ |
149 | |
150 | /* Check if we have device-tree properties */ |
151 | if (np && !of_property_read_u32(np, propname: "aspeed,scu-revision-id" , out_value: &data)) { |
152 | /* We do, disable P2A access */ |
153 | config_mode = ast_use_dt; |
154 | scu_rev = data; |
155 | } else if (pdev->device == PCI_CHIP_AST2000) { // Not all families have a P2A bridge |
156 | /* |
157 | * The BMC will set SCU 0x40 D[12] to 1 if the P2 bridge |
158 | * is disabled. We force using P2A if VGA only mode bit |
159 | * is set D[7] |
160 | */ |
161 | vgacrd0 = __ast_read8_i(addr: ioregs, AST_IO_VGACRI, index: 0xd0); |
162 | vgacrd1 = __ast_read8_i(addr: ioregs, AST_IO_VGACRI, index: 0xd1); |
163 | if (!(vgacrd0 & 0x80) || !(vgacrd1 & 0x10)) { |
164 | |
165 | /* |
166 | * We have a P2A bridge and it is enabled. |
167 | */ |
168 | |
169 | /* Patch AST2500/AST2510 */ |
170 | if ((pdev->revision & 0xf0) == 0x40) { |
171 | if (!(vgacrd0 & AST_VRAM_INIT_STATUS_MASK)) |
172 | ast_patch_ahb_2500(regs); |
173 | } |
174 | |
175 | /* Double check that it's actually working */ |
176 | data = __ast_read32(addr: regs, reg: 0xf004); |
177 | if ((data != 0xffffffff) && (data != 0x00)) { |
178 | config_mode = ast_use_p2a; |
179 | |
180 | /* Read SCU7c (silicon revision register) */ |
181 | __ast_write32(addr: regs, reg: 0xf004, val: 0x1e6e0000); |
182 | __ast_write32(addr: regs, reg: 0xf000, val: 0x1); |
183 | scu_rev = __ast_read32(addr: regs, reg: 0x1207c); |
184 | } |
185 | } |
186 | } |
187 | |
188 | switch (config_mode) { |
189 | case ast_use_defaults: |
190 | dev_info(dev, "Using default configuration\n" ); |
191 | break; |
192 | case ast_use_dt: |
193 | dev_info(dev, "Using device-tree for configuration\n" ); |
194 | break; |
195 | case ast_use_p2a: |
196 | dev_info(dev, "Using P2A bridge for configuration\n" ); |
197 | break; |
198 | } |
199 | |
200 | /* |
201 | * Identify chipset |
202 | */ |
203 | |
204 | if (pdev->revision >= 0x50) { |
205 | chip = AST2600; |
206 | dev_info(dev, "AST 2600 detected\n" ); |
207 | } else if (pdev->revision >= 0x40) { |
208 | switch (scu_rev & 0x300) { |
209 | case 0x0100: |
210 | chip = AST2510; |
211 | dev_info(dev, "AST 2510 detected\n" ); |
212 | break; |
213 | default: |
214 | chip = AST2500; |
215 | dev_info(dev, "AST 2500 detected\n" ); |
216 | break; |
217 | } |
218 | } else if (pdev->revision >= 0x30) { |
219 | switch (scu_rev & 0x300) { |
220 | case 0x0100: |
221 | chip = AST1400; |
222 | dev_info(dev, "AST 1400 detected\n" ); |
223 | break; |
224 | default: |
225 | chip = AST2400; |
226 | dev_info(dev, "AST 2400 detected\n" ); |
227 | break; |
228 | } |
229 | } else if (pdev->revision >= 0x20) { |
230 | switch (scu_rev & 0x300) { |
231 | case 0x0000: |
232 | chip = AST1300; |
233 | dev_info(dev, "AST 1300 detected\n" ); |
234 | break; |
235 | default: |
236 | chip = AST2300; |
237 | dev_info(dev, "AST 2300 detected\n" ); |
238 | break; |
239 | } |
240 | } else if (pdev->revision >= 0x10) { |
241 | switch (scu_rev & 0x0300) { |
242 | case 0x0200: |
243 | chip = AST1100; |
244 | dev_info(dev, "AST 1100 detected\n" ); |
245 | break; |
246 | case 0x0100: |
247 | chip = AST2200; |
248 | dev_info(dev, "AST 2200 detected\n" ); |
249 | break; |
250 | case 0x0000: |
251 | chip = AST2150; |
252 | dev_info(dev, "AST 2150 detected\n" ); |
253 | break; |
254 | default: |
255 | chip = AST2100; |
256 | dev_info(dev, "AST 2100 detected\n" ); |
257 | break; |
258 | } |
259 | } else { |
260 | chip = AST2000; |
261 | dev_info(dev, "AST 2000 detected\n" ); |
262 | } |
263 | |
264 | *chip_out = chip; |
265 | *config_mode_out = config_mode; |
266 | |
267 | return 0; |
268 | } |
269 | |
270 | static int ast_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) |
271 | { |
272 | struct device *dev = &pdev->dev; |
273 | int ret; |
274 | void __iomem *regs; |
275 | void __iomem *ioregs; |
276 | enum ast_config_mode config_mode; |
277 | enum ast_chip chip; |
278 | struct drm_device *drm; |
279 | bool need_post = false; |
280 | |
281 | ret = drm_aperture_remove_conflicting_pci_framebuffers(pdev, req_driver: &ast_driver); |
282 | if (ret) |
283 | return ret; |
284 | |
285 | ret = pcim_enable_device(pdev); |
286 | if (ret) |
287 | return ret; |
288 | |
289 | regs = pcim_iomap(pdev, bar: 1, maxlen: 0); |
290 | if (!regs) |
291 | return -EIO; |
292 | |
293 | if (pdev->revision >= 0x40) { |
294 | /* |
295 | * On AST2500 and later models, MMIO is enabled by |
296 | * default. Adopt it to be compatible with ARM. |
297 | */ |
298 | resource_size_t len = pci_resource_len(pdev, 1); |
299 | |
300 | if (len < AST_IO_MM_OFFSET) |
301 | return -EIO; |
302 | if ((len - AST_IO_MM_OFFSET) < AST_IO_MM_LENGTH) |
303 | return -EIO; |
304 | ioregs = regs + AST_IO_MM_OFFSET; |
305 | } else if (pci_resource_flags(pdev, 2) & IORESOURCE_IO) { |
306 | /* |
307 | * Map I/O registers if we have a PCI BAR for I/O. |
308 | */ |
309 | resource_size_t len = pci_resource_len(pdev, 2); |
310 | |
311 | if (len < AST_IO_MM_LENGTH) |
312 | return -EIO; |
313 | ioregs = pcim_iomap(pdev, bar: 2, maxlen: 0); |
314 | if (!ioregs) |
315 | return -EIO; |
316 | } else { |
317 | /* |
318 | * Anything else is best effort. |
319 | */ |
320 | resource_size_t len = pci_resource_len(pdev, 1); |
321 | |
322 | if (len < AST_IO_MM_OFFSET) |
323 | return -EIO; |
324 | if ((len - AST_IO_MM_OFFSET) < AST_IO_MM_LENGTH) |
325 | return -EIO; |
326 | ioregs = regs + AST_IO_MM_OFFSET; |
327 | |
328 | dev_info(dev, "Platform has no I/O space, using MMIO\n" ); |
329 | } |
330 | |
331 | if (!ast_is_vga_enabled(ioregs)) { |
332 | dev_info(dev, "VGA not enabled on entry, requesting chip POST\n" ); |
333 | need_post = true; |
334 | } |
335 | |
336 | /* |
337 | * If VGA isn't enabled, we need to enable now or subsequent |
338 | * access to the scratch registers will fail. |
339 | */ |
340 | if (need_post) |
341 | ast_enable_vga(ioregs); |
342 | /* Enable extended register access */ |
343 | ast_open_key(ioregs); |
344 | |
345 | ret = ast_enable_mmio(dev, ioregs); |
346 | if (ret) |
347 | return ret; |
348 | |
349 | ret = ast_detect_chip(pdev, regs, ioregs, chip_out: &chip, config_mode_out: &config_mode); |
350 | if (ret) |
351 | return ret; |
352 | |
353 | drm = ast_device_create(pdev, drv: &ast_driver, chip, config_mode, regs, ioregs, need_post); |
354 | if (IS_ERR(ptr: drm)) |
355 | return PTR_ERR(ptr: drm); |
356 | pci_set_drvdata(pdev, data: drm); |
357 | |
358 | ret = drm_dev_register(dev: drm, flags: ent->driver_data); |
359 | if (ret) |
360 | return ret; |
361 | |
362 | drm_fbdev_generic_setup(dev: drm, preferred_bpp: 32); |
363 | |
364 | return 0; |
365 | } |
366 | |
367 | static void ast_pci_remove(struct pci_dev *pdev) |
368 | { |
369 | struct drm_device *dev = pci_get_drvdata(pdev); |
370 | |
371 | drm_dev_unregister(dev); |
372 | drm_atomic_helper_shutdown(dev); |
373 | } |
374 | |
375 | static void ast_pci_shutdown(struct pci_dev *pdev) |
376 | { |
377 | drm_atomic_helper_shutdown(dev: pci_get_drvdata(pdev)); |
378 | } |
379 | |
380 | static int ast_drm_freeze(struct drm_device *dev) |
381 | { |
382 | int error; |
383 | |
384 | error = drm_mode_config_helper_suspend(dev); |
385 | if (error) |
386 | return error; |
387 | pci_save_state(to_pci_dev(dev->dev)); |
388 | return 0; |
389 | } |
390 | |
391 | static int ast_drm_thaw(struct drm_device *dev) |
392 | { |
393 | ast_post_gpu(dev); |
394 | |
395 | return drm_mode_config_helper_resume(dev); |
396 | } |
397 | |
398 | static int ast_drm_resume(struct drm_device *dev) |
399 | { |
400 | if (pci_enable_device(to_pci_dev(dev->dev))) |
401 | return -EIO; |
402 | |
403 | return ast_drm_thaw(dev); |
404 | } |
405 | |
406 | static int ast_pm_suspend(struct device *dev) |
407 | { |
408 | struct pci_dev *pdev = to_pci_dev(dev); |
409 | struct drm_device *ddev = pci_get_drvdata(pdev); |
410 | int error; |
411 | |
412 | error = ast_drm_freeze(dev: ddev); |
413 | if (error) |
414 | return error; |
415 | |
416 | pci_disable_device(dev: pdev); |
417 | pci_set_power_state(dev: pdev, PCI_D3hot); |
418 | return 0; |
419 | } |
420 | |
421 | static int ast_pm_resume(struct device *dev) |
422 | { |
423 | struct pci_dev *pdev = to_pci_dev(dev); |
424 | struct drm_device *ddev = pci_get_drvdata(pdev); |
425 | return ast_drm_resume(dev: ddev); |
426 | } |
427 | |
428 | static int ast_pm_freeze(struct device *dev) |
429 | { |
430 | struct pci_dev *pdev = to_pci_dev(dev); |
431 | struct drm_device *ddev = pci_get_drvdata(pdev); |
432 | return ast_drm_freeze(dev: ddev); |
433 | } |
434 | |
435 | static int ast_pm_thaw(struct device *dev) |
436 | { |
437 | struct pci_dev *pdev = to_pci_dev(dev); |
438 | struct drm_device *ddev = pci_get_drvdata(pdev); |
439 | return ast_drm_thaw(dev: ddev); |
440 | } |
441 | |
442 | static int ast_pm_poweroff(struct device *dev) |
443 | { |
444 | struct pci_dev *pdev = to_pci_dev(dev); |
445 | struct drm_device *ddev = pci_get_drvdata(pdev); |
446 | |
447 | return ast_drm_freeze(dev: ddev); |
448 | } |
449 | |
450 | static const struct dev_pm_ops ast_pm_ops = { |
451 | .suspend = ast_pm_suspend, |
452 | .resume = ast_pm_resume, |
453 | .freeze = ast_pm_freeze, |
454 | .thaw = ast_pm_thaw, |
455 | .poweroff = ast_pm_poweroff, |
456 | .restore = ast_pm_resume, |
457 | }; |
458 | |
459 | static struct pci_driver ast_pci_driver = { |
460 | .name = DRIVER_NAME, |
461 | .id_table = ast_pciidlist, |
462 | .probe = ast_pci_probe, |
463 | .remove = ast_pci_remove, |
464 | .shutdown = ast_pci_shutdown, |
465 | .driver.pm = &ast_pm_ops, |
466 | }; |
467 | |
468 | drm_module_pci_driver_if_modeset(ast_pci_driver, ast_modeset); |
469 | |
470 | MODULE_AUTHOR(DRIVER_AUTHOR); |
471 | MODULE_DESCRIPTION(DRIVER_DESC); |
472 | MODULE_LICENSE("GPL and additional rights" ); |
473 | |