1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright 2012 Red Hat |
4 | * |
5 | * Authors: Matthew Garrett |
6 | * Dave Airlie |
7 | */ |
8 | |
9 | #include <linux/module.h> |
10 | #include <linux/pci.h> |
11 | |
12 | #include <drm/drm_aperture.h> |
13 | #include <drm/drm_atomic_helper.h> |
14 | #include <drm/drm_drv.h> |
15 | #include <drm/drm_fbdev_generic.h> |
16 | #include <drm/drm_file.h> |
17 | #include <drm/drm_ioctl.h> |
18 | #include <drm/drm_managed.h> |
19 | #include <drm/drm_module.h> |
20 | #include <drm/drm_pciids.h> |
21 | |
22 | #include "mgag200_drv.h" |
23 | |
24 | static int mgag200_modeset = -1; |
25 | MODULE_PARM_DESC(modeset, "Disable/Enable modesetting" ); |
26 | module_param_named(modeset, mgag200_modeset, int, 0400); |
27 | |
28 | int mgag200_init_pci_options(struct pci_dev *pdev, u32 option, u32 option2) |
29 | { |
30 | struct device *dev = &pdev->dev; |
31 | int err; |
32 | |
33 | err = pci_write_config_dword(dev: pdev, PCI_MGA_OPTION, val: option); |
34 | if (err != PCIBIOS_SUCCESSFUL) { |
35 | dev_err(dev, "pci_write_config_dword(PCI_MGA_OPTION) failed: %d\n" , err); |
36 | return pcibios_err_to_errno(err); |
37 | } |
38 | |
39 | err = pci_write_config_dword(dev: pdev, PCI_MGA_OPTION2, val: option2); |
40 | if (err != PCIBIOS_SUCCESSFUL) { |
41 | dev_err(dev, "pci_write_config_dword(PCI_MGA_OPTION2) failed: %d\n" , err); |
42 | return pcibios_err_to_errno(err); |
43 | } |
44 | |
45 | return 0; |
46 | } |
47 | |
48 | resource_size_t mgag200_probe_vram(void __iomem *mem, resource_size_t size) |
49 | { |
50 | int offset; |
51 | int orig; |
52 | int test1, test2; |
53 | int orig1, orig2; |
54 | size_t vram_size; |
55 | |
56 | /* Probe */ |
57 | orig = ioread16(mem); |
58 | iowrite16(0, mem); |
59 | |
60 | vram_size = size; |
61 | |
62 | for (offset = 0x100000; offset < vram_size; offset += 0x4000) { |
63 | orig1 = ioread8(mem + offset); |
64 | orig2 = ioread8(mem + offset + 0x100); |
65 | |
66 | iowrite16(0xaa55, mem + offset); |
67 | iowrite16(0xaa55, mem + offset + 0x100); |
68 | |
69 | test1 = ioread16(mem + offset); |
70 | test2 = ioread16(mem); |
71 | |
72 | iowrite16(orig1, mem + offset); |
73 | iowrite16(orig2, mem + offset + 0x100); |
74 | |
75 | if (test1 != 0xaa55) |
76 | break; |
77 | |
78 | if (test2) |
79 | break; |
80 | } |
81 | |
82 | iowrite16(orig, mem); |
83 | |
84 | return offset - 65536; |
85 | } |
86 | |
87 | #if defined(CONFIG_DRM_MGAG200_IOBURST_WORKAROUND) |
88 | static struct drm_gem_object *mgag200_create_object(struct drm_device *dev, size_t size) |
89 | { |
90 | struct drm_gem_shmem_object *shmem; |
91 | |
92 | shmem = kzalloc(sizeof(*shmem), GFP_KERNEL); |
93 | if (!shmem) |
94 | return NULL; |
95 | |
96 | shmem->map_wc = true; |
97 | return &shmem->base; |
98 | } |
99 | #endif |
100 | |
101 | /* |
102 | * DRM driver |
103 | */ |
104 | |
105 | DEFINE_DRM_GEM_FOPS(mgag200_driver_fops); |
106 | |
107 | static const struct drm_driver mgag200_driver = { |
108 | .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET, |
109 | .fops = &mgag200_driver_fops, |
110 | .name = DRIVER_NAME, |
111 | .desc = DRIVER_DESC, |
112 | .date = DRIVER_DATE, |
113 | .major = DRIVER_MAJOR, |
114 | .minor = DRIVER_MINOR, |
115 | .patchlevel = DRIVER_PATCHLEVEL, |
116 | #if defined(CONFIG_DRM_MGAG200_IOBURST_WORKAROUND) |
117 | .gem_create_object = mgag200_create_object, |
118 | #endif |
119 | DRM_GEM_SHMEM_DRIVER_OPS, |
120 | }; |
121 | |
122 | /* |
123 | * DRM device |
124 | */ |
125 | |
126 | resource_size_t mgag200_device_probe_vram(struct mga_device *mdev) |
127 | { |
128 | return mgag200_probe_vram(mem: mdev->vram, size: resource_size(res: mdev->vram_res)); |
129 | } |
130 | |
131 | int mgag200_device_preinit(struct mga_device *mdev) |
132 | { |
133 | struct drm_device *dev = &mdev->base; |
134 | struct pci_dev *pdev = to_pci_dev(dev->dev); |
135 | resource_size_t start, len; |
136 | struct resource *res; |
137 | |
138 | /* BAR 1 contains registers */ |
139 | |
140 | start = pci_resource_start(pdev, 1); |
141 | len = pci_resource_len(pdev, 1); |
142 | |
143 | res = devm_request_mem_region(dev->dev, start, len, "mgadrmfb_mmio" ); |
144 | if (!res) { |
145 | drm_err(dev, "devm_request_mem_region(MMIO) failed\n" ); |
146 | return -ENXIO; |
147 | } |
148 | mdev->rmmio_res = res; |
149 | |
150 | mdev->rmmio = pcim_iomap(pdev, bar: 1, maxlen: 0); |
151 | if (!mdev->rmmio) |
152 | return -ENOMEM; |
153 | |
154 | /* BAR 0 is VRAM */ |
155 | |
156 | start = pci_resource_start(pdev, 0); |
157 | len = pci_resource_len(pdev, 0); |
158 | |
159 | res = devm_request_mem_region(dev->dev, start, len, "mgadrmfb_vram" ); |
160 | if (!res) { |
161 | drm_err(dev, "devm_request_mem_region(VRAM) failed\n" ); |
162 | return -ENXIO; |
163 | } |
164 | mdev->vram_res = res; |
165 | |
166 | mdev->vram = devm_ioremap_wc(dev: dev->dev, offset: res->start, size: resource_size(res)); |
167 | if (!mdev->vram) |
168 | return -ENOMEM; |
169 | |
170 | /* Don't fail on errors, but performance might be reduced. */ |
171 | devm_arch_phys_wc_add(dev: dev->dev, base: res->start, size: resource_size(res)); |
172 | |
173 | return 0; |
174 | } |
175 | |
176 | int mgag200_device_init(struct mga_device *mdev, |
177 | const struct mgag200_device_info *info, |
178 | const struct mgag200_device_funcs *funcs) |
179 | { |
180 | struct drm_device *dev = &mdev->base; |
181 | u8 crtcext3, misc; |
182 | int ret; |
183 | |
184 | mdev->info = info; |
185 | mdev->funcs = funcs; |
186 | |
187 | ret = drmm_mutex_init(dev, &mdev->rmmio_lock); |
188 | if (ret) |
189 | return ret; |
190 | |
191 | mutex_lock(&mdev->rmmio_lock); |
192 | |
193 | RREG_ECRT(0x03, crtcext3); |
194 | crtcext3 |= MGAREG_CRTCEXT3_MGAMODE; |
195 | WREG_ECRT(0x03, crtcext3); |
196 | |
197 | WREG_ECRT(0x04, 0x00); |
198 | |
199 | misc = RREG8(MGA_MISC_IN); |
200 | misc |= MGAREG_MISC_RAMMAPEN | |
201 | MGAREG_MISC_HIGH_PG_SEL; |
202 | WREG8(MGA_MISC_OUT, misc); |
203 | |
204 | mutex_unlock(lock: &mdev->rmmio_lock); |
205 | |
206 | return 0; |
207 | } |
208 | |
209 | /* |
210 | * PCI driver |
211 | */ |
212 | |
213 | static const struct pci_device_id mgag200_pciidlist[] = { |
214 | { PCI_VENDOR_ID_MATROX, 0x520, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_PCI }, |
215 | { PCI_VENDOR_ID_MATROX, 0x521, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_AGP }, |
216 | { PCI_VENDOR_ID_MATROX, 0x522, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_A }, |
217 | { PCI_VENDOR_ID_MATROX, 0x524, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_B }, |
218 | { PCI_VENDOR_ID_MATROX, 0x530, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EV }, |
219 | { PCI_VENDOR_ID_MATROX, 0x532, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_WB }, |
220 | { PCI_VENDOR_ID_MATROX, 0x533, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EH }, |
221 | { PCI_VENDOR_ID_MATROX, 0x534, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_ER }, |
222 | { PCI_VENDOR_ID_MATROX, 0x536, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EW3 }, |
223 | { PCI_VENDOR_ID_MATROX, 0x538, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EH3 }, |
224 | {0,} |
225 | }; |
226 | |
227 | MODULE_DEVICE_TABLE(pci, mgag200_pciidlist); |
228 | |
229 | static int |
230 | mgag200_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) |
231 | { |
232 | enum mga_type type = (enum mga_type)ent->driver_data; |
233 | struct mga_device *mdev; |
234 | struct drm_device *dev; |
235 | int ret; |
236 | |
237 | ret = drm_aperture_remove_conflicting_pci_framebuffers(pdev, req_driver: &mgag200_driver); |
238 | if (ret) |
239 | return ret; |
240 | |
241 | ret = pcim_enable_device(pdev); |
242 | if (ret) |
243 | return ret; |
244 | |
245 | switch (type) { |
246 | case G200_PCI: |
247 | case G200_AGP: |
248 | mdev = mgag200_g200_device_create(pdev, drv: &mgag200_driver); |
249 | break; |
250 | case G200_SE_A: |
251 | case G200_SE_B: |
252 | mdev = mgag200_g200se_device_create(pdev, drv: &mgag200_driver, type); |
253 | break; |
254 | case G200_WB: |
255 | mdev = mgag200_g200wb_device_create(pdev, drv: &mgag200_driver); |
256 | break; |
257 | case G200_EV: |
258 | mdev = mgag200_g200ev_device_create(pdev, drv: &mgag200_driver); |
259 | break; |
260 | case G200_EH: |
261 | mdev = mgag200_g200eh_device_create(pdev, drv: &mgag200_driver); |
262 | break; |
263 | case G200_EH3: |
264 | mdev = mgag200_g200eh3_device_create(pdev, drv: &mgag200_driver); |
265 | break; |
266 | case G200_ER: |
267 | mdev = mgag200_g200er_device_create(pdev, drv: &mgag200_driver); |
268 | break; |
269 | case G200_EW3: |
270 | mdev = mgag200_g200ew3_device_create(pdev, drv: &mgag200_driver); |
271 | break; |
272 | default: |
273 | dev_err(&pdev->dev, "Device type %d is unsupported\n" , type); |
274 | return -ENODEV; |
275 | } |
276 | if (IS_ERR(ptr: mdev)) |
277 | return PTR_ERR(ptr: mdev); |
278 | dev = &mdev->base; |
279 | |
280 | ret = drm_dev_register(dev, flags: 0); |
281 | if (ret) |
282 | return ret; |
283 | |
284 | /* |
285 | * FIXME: A 24-bit color depth does not work with 24 bpp on |
286 | * G200ER. Force 32 bpp. |
287 | */ |
288 | drm_fbdev_generic_setup(dev, preferred_bpp: 32); |
289 | |
290 | return 0; |
291 | } |
292 | |
293 | static void mgag200_pci_remove(struct pci_dev *pdev) |
294 | { |
295 | struct drm_device *dev = pci_get_drvdata(pdev); |
296 | |
297 | drm_dev_unregister(dev); |
298 | drm_atomic_helper_shutdown(dev); |
299 | } |
300 | |
301 | static void mgag200_pci_shutdown(struct pci_dev *pdev) |
302 | { |
303 | drm_atomic_helper_shutdown(dev: pci_get_drvdata(pdev)); |
304 | } |
305 | |
306 | static struct pci_driver mgag200_pci_driver = { |
307 | .name = DRIVER_NAME, |
308 | .id_table = mgag200_pciidlist, |
309 | .probe = mgag200_pci_probe, |
310 | .remove = mgag200_pci_remove, |
311 | .shutdown = mgag200_pci_shutdown, |
312 | }; |
313 | |
314 | drm_module_pci_driver_if_modeset(mgag200_pci_driver, mgag200_modeset); |
315 | |
316 | MODULE_AUTHOR(DRIVER_AUTHOR); |
317 | MODULE_DESCRIPTION(DRIVER_DESC); |
318 | MODULE_LICENSE("GPL" ); |
319 | |