1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | |
3 | #include <linux/of_address.h> |
4 | #include <linux/pci.h> |
5 | #include <linux/platform_device.h> |
6 | |
7 | #include <drm/drm_aperture.h> |
8 | #include <drm/drm_atomic.h> |
9 | #include <drm/drm_atomic_state_helper.h> |
10 | #include <drm/drm_connector.h> |
11 | #include <drm/drm_damage_helper.h> |
12 | #include <drm/drm_device.h> |
13 | #include <drm/drm_drv.h> |
14 | #include <drm/drm_fbdev_generic.h> |
15 | #include <drm/drm_format_helper.h> |
16 | #include <drm/drm_framebuffer.h> |
17 | #include <drm/drm_gem_atomic_helper.h> |
18 | #include <drm/drm_gem_framebuffer_helper.h> |
19 | #include <drm/drm_gem_shmem_helper.h> |
20 | #include <drm/drm_managed.h> |
21 | #include <drm/drm_modeset_helper_vtables.h> |
22 | #include <drm/drm_probe_helper.h> |
23 | #include <drm/drm_simple_kms_helper.h> |
24 | |
25 | #define DRIVER_NAME "ofdrm" |
26 | #define DRIVER_DESC "DRM driver for OF platform devices" |
27 | #define DRIVER_DATE "20220501" |
28 | #define DRIVER_MAJOR 1 |
29 | #define DRIVER_MINOR 0 |
30 | |
31 | #define PCI_VENDOR_ID_ATI_R520 0x7100 |
32 | #define PCI_VENDOR_ID_ATI_R600 0x9400 |
33 | |
34 | #define OFDRM_GAMMA_LUT_SIZE 256 |
35 | |
36 | /* Definitions used by the Avivo palette */ |
37 | #define AVIVO_DC_LUT_RW_SELECT 0x6480 |
38 | #define AVIVO_DC_LUT_RW_MODE 0x6484 |
39 | #define AVIVO_DC_LUT_RW_INDEX 0x6488 |
40 | #define AVIVO_DC_LUT_SEQ_COLOR 0x648c |
41 | #define AVIVO_DC_LUT_PWL_DATA 0x6490 |
42 | #define AVIVO_DC_LUT_30_COLOR 0x6494 |
43 | #define AVIVO_DC_LUT_READ_PIPE_SELECT 0x6498 |
44 | #define AVIVO_DC_LUT_WRITE_EN_MASK 0x649c |
45 | #define AVIVO_DC_LUT_AUTOFILL 0x64a0 |
46 | #define AVIVO_DC_LUTA_CONTROL 0x64c0 |
47 | #define AVIVO_DC_LUTA_BLACK_OFFSET_BLUE 0x64c4 |
48 | #define AVIVO_DC_LUTA_BLACK_OFFSET_GREEN 0x64c8 |
49 | #define AVIVO_DC_LUTA_BLACK_OFFSET_RED 0x64cc |
50 | #define AVIVO_DC_LUTA_WHITE_OFFSET_BLUE 0x64d0 |
51 | #define AVIVO_DC_LUTA_WHITE_OFFSET_GREEN 0x64d4 |
52 | #define AVIVO_DC_LUTA_WHITE_OFFSET_RED 0x64d8 |
53 | #define AVIVO_DC_LUTB_CONTROL 0x6cc0 |
54 | #define AVIVO_DC_LUTB_BLACK_OFFSET_BLUE 0x6cc4 |
55 | #define AVIVO_DC_LUTB_BLACK_OFFSET_GREEN 0x6cc8 |
56 | #define AVIVO_DC_LUTB_BLACK_OFFSET_RED 0x6ccc |
57 | #define AVIVO_DC_LUTB_WHITE_OFFSET_BLUE 0x6cd0 |
58 | #define AVIVO_DC_LUTB_WHITE_OFFSET_GREEN 0x6cd4 |
59 | #define AVIVO_DC_LUTB_WHITE_OFFSET_RED 0x6cd8 |
60 | |
61 | enum ofdrm_model { |
62 | OFDRM_MODEL_UNKNOWN, |
63 | OFDRM_MODEL_MACH64, /* ATI Mach64 */ |
64 | OFDRM_MODEL_RAGE128, /* ATI Rage128 */ |
65 | OFDRM_MODEL_RAGE_M3A, /* ATI Rage Mobility M3 Head A */ |
66 | OFDRM_MODEL_RAGE_M3B, /* ATI Rage Mobility M3 Head B */ |
67 | OFDRM_MODEL_RADEON, /* ATI Radeon */ |
68 | OFDRM_MODEL_GXT2000, /* IBM GXT2000 */ |
69 | OFDRM_MODEL_AVIVO, /* ATI R5xx */ |
70 | OFDRM_MODEL_QEMU, /* QEMU VGA */ |
71 | }; |
72 | |
73 | /* |
74 | * Helpers for display nodes |
75 | */ |
76 | |
77 | static int display_get_validated_int(struct drm_device *dev, const char *name, uint32_t value) |
78 | { |
79 | if (value > INT_MAX) { |
80 | drm_err(dev, "invalid framebuffer %s of %u\n" , name, value); |
81 | return -EINVAL; |
82 | } |
83 | return (int)value; |
84 | } |
85 | |
86 | static int display_get_validated_int0(struct drm_device *dev, const char *name, uint32_t value) |
87 | { |
88 | if (!value) { |
89 | drm_err(dev, "invalid framebuffer %s of %u\n" , name, value); |
90 | return -EINVAL; |
91 | } |
92 | return display_get_validated_int(dev, name, value); |
93 | } |
94 | |
95 | static const struct drm_format_info *display_get_validated_format(struct drm_device *dev, |
96 | u32 depth, bool big_endian) |
97 | { |
98 | const struct drm_format_info *info; |
99 | u32 format; |
100 | |
101 | switch (depth) { |
102 | case 8: |
103 | format = drm_mode_legacy_fb_format(bpp: 8, depth: 8); |
104 | break; |
105 | case 15: |
106 | case 16: |
107 | format = drm_mode_legacy_fb_format(bpp: 16, depth); |
108 | break; |
109 | case 32: |
110 | format = drm_mode_legacy_fb_format(bpp: 32, depth: 24); |
111 | break; |
112 | default: |
113 | drm_err(dev, "unsupported framebuffer depth %u\n" , depth); |
114 | return ERR_PTR(error: -EINVAL); |
115 | } |
116 | |
117 | /* |
118 | * DRM formats assume little-endian byte order. Update the format |
119 | * if the scanout buffer uses big-endian ordering. |
120 | */ |
121 | if (big_endian) { |
122 | switch (format) { |
123 | case DRM_FORMAT_XRGB8888: |
124 | format = DRM_FORMAT_BGRX8888; |
125 | break; |
126 | case DRM_FORMAT_ARGB8888: |
127 | format = DRM_FORMAT_BGRA8888; |
128 | break; |
129 | case DRM_FORMAT_RGB565: |
130 | format = DRM_FORMAT_RGB565 | DRM_FORMAT_BIG_ENDIAN; |
131 | break; |
132 | case DRM_FORMAT_XRGB1555: |
133 | format = DRM_FORMAT_XRGB1555 | DRM_FORMAT_BIG_ENDIAN; |
134 | break; |
135 | default: |
136 | break; |
137 | } |
138 | } |
139 | |
140 | info = drm_format_info(format); |
141 | if (!info) { |
142 | drm_err(dev, "cannot find framebuffer format for depth %u\n" , depth); |
143 | return ERR_PTR(error: -EINVAL); |
144 | } |
145 | |
146 | return info; |
147 | } |
148 | |
149 | static int display_read_u32_of(struct drm_device *dev, struct device_node *of_node, |
150 | const char *name, u32 *value) |
151 | { |
152 | int ret = of_property_read_u32(np: of_node, propname: name, out_value: value); |
153 | |
154 | if (ret) |
155 | drm_err(dev, "cannot parse framebuffer %s: error %d\n" , name, ret); |
156 | return ret; |
157 | } |
158 | |
159 | static bool display_get_big_endian_of(struct drm_device *dev, struct device_node *of_node) |
160 | { |
161 | bool big_endian; |
162 | |
163 | #ifdef __BIG_ENDIAN |
164 | big_endian = !of_property_read_bool(of_node, "little-endian" ); |
165 | #else |
166 | big_endian = of_property_read_bool(np: of_node, propname: "big-endian" ); |
167 | #endif |
168 | |
169 | return big_endian; |
170 | } |
171 | |
172 | static int display_get_width_of(struct drm_device *dev, struct device_node *of_node) |
173 | { |
174 | u32 width; |
175 | int ret = display_read_u32_of(dev, of_node, name: "width" , value: &width); |
176 | |
177 | if (ret) |
178 | return ret; |
179 | return display_get_validated_int0(dev, name: "width" , value: width); |
180 | } |
181 | |
182 | static int display_get_height_of(struct drm_device *dev, struct device_node *of_node) |
183 | { |
184 | u32 height; |
185 | int ret = display_read_u32_of(dev, of_node, name: "height" , value: &height); |
186 | |
187 | if (ret) |
188 | return ret; |
189 | return display_get_validated_int0(dev, name: "height" , value: height); |
190 | } |
191 | |
192 | static int display_get_depth_of(struct drm_device *dev, struct device_node *of_node) |
193 | { |
194 | u32 depth; |
195 | int ret = display_read_u32_of(dev, of_node, name: "depth" , value: &depth); |
196 | |
197 | if (ret) |
198 | return ret; |
199 | return display_get_validated_int0(dev, name: "depth" , value: depth); |
200 | } |
201 | |
202 | static int display_get_linebytes_of(struct drm_device *dev, struct device_node *of_node) |
203 | { |
204 | u32 linebytes; |
205 | int ret = display_read_u32_of(dev, of_node, name: "linebytes" , value: &linebytes); |
206 | |
207 | if (ret) |
208 | return ret; |
209 | return display_get_validated_int(dev, name: "linebytes" , value: linebytes); |
210 | } |
211 | |
212 | static u64 display_get_address_of(struct drm_device *dev, struct device_node *of_node) |
213 | { |
214 | u32 address; |
215 | int ret; |
216 | |
217 | /* |
218 | * Not all devices provide an address property, it's not |
219 | * a bug if this fails. The driver will try to find the |
220 | * framebuffer base address from the device's memory regions. |
221 | */ |
222 | ret = of_property_read_u32(np: of_node, propname: "address" , out_value: &address); |
223 | if (ret) |
224 | return OF_BAD_ADDR; |
225 | |
226 | return address; |
227 | } |
228 | |
229 | static bool is_avivo(u32 vendor, u32 device) |
230 | { |
231 | /* This will match most R5xx */ |
232 | return (vendor == PCI_VENDOR_ID_ATI) && |
233 | ((device >= PCI_VENDOR_ID_ATI_R520 && device < 0x7800) || |
234 | (PCI_VENDOR_ID_ATI_R600 >= 0x9400)); |
235 | } |
236 | |
237 | static enum ofdrm_model display_get_model_of(struct drm_device *dev, struct device_node *of_node) |
238 | { |
239 | enum ofdrm_model model = OFDRM_MODEL_UNKNOWN; |
240 | |
241 | if (of_node_name_prefix(np: of_node, prefix: "ATY,Rage128" )) { |
242 | model = OFDRM_MODEL_RAGE128; |
243 | } else if (of_node_name_prefix(np: of_node, prefix: "ATY,RageM3pA" ) || |
244 | of_node_name_prefix(np: of_node, prefix: "ATY,RageM3p12A" )) { |
245 | model = OFDRM_MODEL_RAGE_M3A; |
246 | } else if (of_node_name_prefix(np: of_node, prefix: "ATY,RageM3pB" )) { |
247 | model = OFDRM_MODEL_RAGE_M3B; |
248 | } else if (of_node_name_prefix(np: of_node, prefix: "ATY,Rage6" )) { |
249 | model = OFDRM_MODEL_RADEON; |
250 | } else if (of_node_name_prefix(np: of_node, prefix: "ATY," )) { |
251 | return OFDRM_MODEL_MACH64; |
252 | } else if (of_device_is_compatible(device: of_node, "pci1014,b7" ) || |
253 | of_device_is_compatible(device: of_node, "pci1014,21c" )) { |
254 | model = OFDRM_MODEL_GXT2000; |
255 | } else if (of_node_name_prefix(np: of_node, prefix: "vga,Display-" )) { |
256 | struct device_node *of_parent; |
257 | const __be32 *vendor_p, *device_p; |
258 | |
259 | /* Look for AVIVO initialized by SLOF */ |
260 | of_parent = of_get_parent(node: of_node); |
261 | vendor_p = of_get_property(node: of_parent, name: "vendor-id" , NULL); |
262 | device_p = of_get_property(node: of_parent, name: "device-id" , NULL); |
263 | if (vendor_p && device_p) { |
264 | u32 vendor = be32_to_cpup(p: vendor_p); |
265 | u32 device = be32_to_cpup(p: device_p); |
266 | |
267 | if (is_avivo(vendor, device)) |
268 | model = OFDRM_MODEL_AVIVO; |
269 | } |
270 | of_node_put(node: of_parent); |
271 | } else if (of_device_is_compatible(device: of_node, "qemu,std-vga" )) { |
272 | model = OFDRM_MODEL_QEMU; |
273 | } |
274 | |
275 | return model; |
276 | } |
277 | |
278 | /* |
279 | * Open Firmware display device |
280 | */ |
281 | |
282 | struct ofdrm_device; |
283 | |
284 | struct ofdrm_device_funcs { |
285 | void __iomem *(*cmap_ioremap)(struct ofdrm_device *odev, |
286 | struct device_node *of_node, |
287 | u64 fb_bas); |
288 | void (*cmap_write)(struct ofdrm_device *odev, unsigned char index, |
289 | unsigned char r, unsigned char g, unsigned char b); |
290 | }; |
291 | |
292 | struct ofdrm_device { |
293 | struct drm_device dev; |
294 | struct platform_device *pdev; |
295 | |
296 | const struct ofdrm_device_funcs *funcs; |
297 | |
298 | /* firmware-buffer settings */ |
299 | struct iosys_map screen_base; |
300 | struct drm_display_mode mode; |
301 | const struct drm_format_info *format; |
302 | unsigned int pitch; |
303 | |
304 | /* colormap */ |
305 | void __iomem *cmap_base; |
306 | |
307 | /* modesetting */ |
308 | uint32_t formats[8]; |
309 | struct drm_plane primary_plane; |
310 | struct drm_crtc crtc; |
311 | struct drm_encoder encoder; |
312 | struct drm_connector connector; |
313 | }; |
314 | |
315 | static struct ofdrm_device *ofdrm_device_of_dev(struct drm_device *dev) |
316 | { |
317 | return container_of(dev, struct ofdrm_device, dev); |
318 | } |
319 | |
320 | /* |
321 | * Hardware |
322 | */ |
323 | |
324 | #if defined(CONFIG_PCI) |
325 | static struct pci_dev *display_get_pci_dev_of(struct drm_device *dev, struct device_node *of_node) |
326 | { |
327 | const __be32 *vendor_p, *device_p; |
328 | u32 vendor, device; |
329 | struct pci_dev *pcidev; |
330 | |
331 | vendor_p = of_get_property(node: of_node, name: "vendor-id" , NULL); |
332 | if (!vendor_p) |
333 | return ERR_PTR(error: -ENODEV); |
334 | vendor = be32_to_cpup(p: vendor_p); |
335 | |
336 | device_p = of_get_property(node: of_node, name: "device-id" , NULL); |
337 | if (!device_p) |
338 | return ERR_PTR(error: -ENODEV); |
339 | device = be32_to_cpup(p: device_p); |
340 | |
341 | pcidev = pci_get_device(vendor, device, NULL); |
342 | if (!pcidev) |
343 | return ERR_PTR(error: -ENODEV); |
344 | |
345 | return pcidev; |
346 | } |
347 | |
348 | static void ofdrm_pci_release(void *data) |
349 | { |
350 | struct pci_dev *pcidev = data; |
351 | |
352 | pci_disable_device(dev: pcidev); |
353 | } |
354 | |
355 | static int ofdrm_device_init_pci(struct ofdrm_device *odev) |
356 | { |
357 | struct drm_device *dev = &odev->dev; |
358 | struct platform_device *pdev = to_platform_device(dev->dev); |
359 | struct device_node *of_node = pdev->dev.of_node; |
360 | struct pci_dev *pcidev; |
361 | int ret; |
362 | |
363 | /* |
364 | * Never use pcim_ or other managed helpers on the returned PCI |
365 | * device. Otherwise, probing the native driver will fail for |
366 | * resource conflicts. PCI-device management has to be tied to |
367 | * the lifetime of the platform device until the native driver |
368 | * takes over. |
369 | */ |
370 | pcidev = display_get_pci_dev_of(dev, of_node); |
371 | if (IS_ERR(ptr: pcidev)) |
372 | return 0; /* no PCI device found; ignore the error */ |
373 | |
374 | ret = pci_enable_device(dev: pcidev); |
375 | if (ret) { |
376 | drm_err(dev, "pci_enable_device(%s) failed: %d\n" , |
377 | dev_name(&pcidev->dev), ret); |
378 | return ret; |
379 | } |
380 | ret = devm_add_action_or_reset(&pdev->dev, ofdrm_pci_release, pcidev); |
381 | if (ret) |
382 | return ret; |
383 | |
384 | return 0; |
385 | } |
386 | #else |
387 | static int ofdrm_device_init_pci(struct ofdrm_device *odev) |
388 | { |
389 | return 0; |
390 | } |
391 | #endif |
392 | |
393 | /* |
394 | * OF display settings |
395 | */ |
396 | |
397 | static struct resource *ofdrm_find_fb_resource(struct ofdrm_device *odev, |
398 | struct resource *fb_res) |
399 | { |
400 | struct platform_device *pdev = to_platform_device(odev->dev.dev); |
401 | struct resource *res, *max_res = NULL; |
402 | u32 i; |
403 | |
404 | for (i = 0; pdev->num_resources; ++i) { |
405 | res = platform_get_resource(pdev, IORESOURCE_MEM, i); |
406 | if (!res) |
407 | break; /* all resources processed */ |
408 | if (resource_size(res) < resource_size(res: fb_res)) |
409 | continue; /* resource too small */ |
410 | if (fb_res->start && resource_contains(r1: res, r2: fb_res)) |
411 | return res; /* resource contains framebuffer */ |
412 | if (!max_res || resource_size(res) > resource_size(res: max_res)) |
413 | max_res = res; /* store largest resource as fallback */ |
414 | } |
415 | |
416 | return max_res; |
417 | } |
418 | |
419 | /* |
420 | * Colormap / Palette |
421 | */ |
422 | |
423 | static void __iomem *get_cmap_address_of(struct ofdrm_device *odev, struct device_node *of_node, |
424 | int bar_no, unsigned long offset, unsigned long size) |
425 | { |
426 | struct drm_device *dev = &odev->dev; |
427 | const __be32 *addr_p; |
428 | u64 max_size, address; |
429 | unsigned int flags; |
430 | void __iomem *mem; |
431 | |
432 | addr_p = of_get_pci_address(dev: of_node, bar_no, size: &max_size, flags: &flags); |
433 | if (!addr_p) |
434 | addr_p = of_get_address(dev: of_node, index: bar_no, size: &max_size, flags: &flags); |
435 | if (!addr_p) |
436 | return IOMEM_ERR_PTR(-ENODEV); |
437 | |
438 | if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0) |
439 | return IOMEM_ERR_PTR(-ENODEV); |
440 | |
441 | if ((offset + size) >= max_size) |
442 | return IOMEM_ERR_PTR(-ENODEV); |
443 | |
444 | address = of_translate_address(np: of_node, addr: addr_p); |
445 | if (address == OF_BAD_ADDR) |
446 | return IOMEM_ERR_PTR(-ENODEV); |
447 | |
448 | mem = devm_ioremap(dev: dev->dev, offset: address + offset, size); |
449 | if (!mem) |
450 | return IOMEM_ERR_PTR(-ENOMEM); |
451 | |
452 | return mem; |
453 | } |
454 | |
455 | static void __iomem *ofdrm_mach64_cmap_ioremap(struct ofdrm_device *odev, |
456 | struct device_node *of_node, |
457 | u64 fb_base) |
458 | { |
459 | struct drm_device *dev = &odev->dev; |
460 | u64 address; |
461 | void __iomem *cmap_base; |
462 | |
463 | address = fb_base & 0xff000000ul; |
464 | address += 0x7ff000; |
465 | |
466 | cmap_base = devm_ioremap(dev: dev->dev, offset: address, size: 0x1000); |
467 | if (!cmap_base) |
468 | return IOMEM_ERR_PTR(-ENOMEM); |
469 | |
470 | return cmap_base; |
471 | } |
472 | |
473 | static void ofdrm_mach64_cmap_write(struct ofdrm_device *odev, unsigned char index, |
474 | unsigned char r, unsigned char g, unsigned char b) |
475 | { |
476 | void __iomem *addr = odev->cmap_base + 0xcc0; |
477 | void __iomem *data = odev->cmap_base + 0xcc0 + 1; |
478 | |
479 | writeb(val: index, addr); |
480 | writeb(val: r, addr: data); |
481 | writeb(val: g, addr: data); |
482 | writeb(val: b, addr: data); |
483 | } |
484 | |
485 | static void __iomem *ofdrm_rage128_cmap_ioremap(struct ofdrm_device *odev, |
486 | struct device_node *of_node, |
487 | u64 fb_base) |
488 | { |
489 | return get_cmap_address_of(odev, of_node, bar_no: 2, offset: 0, size: 0x1fff); |
490 | } |
491 | |
492 | static void ofdrm_rage128_cmap_write(struct ofdrm_device *odev, unsigned char index, |
493 | unsigned char r, unsigned char g, unsigned char b) |
494 | { |
495 | void __iomem *addr = odev->cmap_base + 0xb0; |
496 | void __iomem *data = odev->cmap_base + 0xb4; |
497 | u32 color = (r << 16) | (g << 8) | b; |
498 | |
499 | writeb(val: index, addr); |
500 | writel(val: color, addr: data); |
501 | } |
502 | |
503 | static void __iomem *ofdrm_rage_m3a_cmap_ioremap(struct ofdrm_device *odev, |
504 | struct device_node *of_node, |
505 | u64 fb_base) |
506 | { |
507 | return get_cmap_address_of(odev, of_node, bar_no: 2, offset: 0, size: 0x1fff); |
508 | } |
509 | |
510 | static void ofdrm_rage_m3a_cmap_write(struct ofdrm_device *odev, unsigned char index, |
511 | unsigned char r, unsigned char g, unsigned char b) |
512 | { |
513 | void __iomem *dac_ctl = odev->cmap_base + 0x58; |
514 | void __iomem *addr = odev->cmap_base + 0xb0; |
515 | void __iomem *data = odev->cmap_base + 0xb4; |
516 | u32 color = (r << 16) | (g << 8) | b; |
517 | u32 val; |
518 | |
519 | /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */ |
520 | val = readl(addr: dac_ctl); |
521 | val &= ~0x20; |
522 | writel(val, addr: dac_ctl); |
523 | |
524 | /* Set color at palette index */ |
525 | writeb(val: index, addr); |
526 | writel(val: color, addr: data); |
527 | } |
528 | |
529 | static void __iomem *ofdrm_rage_m3b_cmap_ioremap(struct ofdrm_device *odev, |
530 | struct device_node *of_node, |
531 | u64 fb_base) |
532 | { |
533 | return get_cmap_address_of(odev, of_node, bar_no: 2, offset: 0, size: 0x1fff); |
534 | } |
535 | |
536 | static void ofdrm_rage_m3b_cmap_write(struct ofdrm_device *odev, unsigned char index, |
537 | unsigned char r, unsigned char g, unsigned char b) |
538 | { |
539 | void __iomem *dac_ctl = odev->cmap_base + 0x58; |
540 | void __iomem *addr = odev->cmap_base + 0xb0; |
541 | void __iomem *data = odev->cmap_base + 0xb4; |
542 | u32 color = (r << 16) | (g << 8) | b; |
543 | u32 val; |
544 | |
545 | /* Set PALETTE_ACCESS_CNTL in DAC_CNTL */ |
546 | val = readl(addr: dac_ctl); |
547 | val |= 0x20; |
548 | writel(val, addr: dac_ctl); |
549 | |
550 | /* Set color at palette index */ |
551 | writeb(val: index, addr); |
552 | writel(val: color, addr: data); |
553 | } |
554 | |
555 | static void __iomem *ofdrm_radeon_cmap_ioremap(struct ofdrm_device *odev, |
556 | struct device_node *of_node, |
557 | u64 fb_base) |
558 | { |
559 | return get_cmap_address_of(odev, of_node, bar_no: 1, offset: 0, size: 0x1fff); |
560 | } |
561 | |
562 | static void __iomem *ofdrm_gxt2000_cmap_ioremap(struct ofdrm_device *odev, |
563 | struct device_node *of_node, |
564 | u64 fb_base) |
565 | { |
566 | return get_cmap_address_of(odev, of_node, bar_no: 0, offset: 0x6000, size: 0x1000); |
567 | } |
568 | |
569 | static void ofdrm_gxt2000_cmap_write(struct ofdrm_device *odev, unsigned char index, |
570 | unsigned char r, unsigned char g, unsigned char b) |
571 | { |
572 | void __iomem *data = ((unsigned int __iomem *)odev->cmap_base) + index; |
573 | u32 color = (r << 16) | (g << 8) | b; |
574 | |
575 | writel(val: color, addr: data); |
576 | } |
577 | |
578 | static void __iomem *ofdrm_avivo_cmap_ioremap(struct ofdrm_device *odev, |
579 | struct device_node *of_node, |
580 | u64 fb_base) |
581 | { |
582 | struct device_node *of_parent; |
583 | void __iomem *cmap_base; |
584 | |
585 | of_parent = of_get_parent(node: of_node); |
586 | cmap_base = get_cmap_address_of(odev, of_node: of_parent, bar_no: 0, offset: 0, size: 0x10000); |
587 | of_node_put(node: of_parent); |
588 | |
589 | return cmap_base; |
590 | } |
591 | |
592 | static void ofdrm_avivo_cmap_write(struct ofdrm_device *odev, unsigned char index, |
593 | unsigned char r, unsigned char g, unsigned char b) |
594 | { |
595 | void __iomem *lutsel = odev->cmap_base + AVIVO_DC_LUT_RW_SELECT; |
596 | void __iomem *addr = odev->cmap_base + AVIVO_DC_LUT_RW_INDEX; |
597 | void __iomem *data = odev->cmap_base + AVIVO_DC_LUT_30_COLOR; |
598 | u32 color = (r << 22) | (g << 12) | (b << 2); |
599 | |
600 | /* Write to both LUTs for now */ |
601 | |
602 | writel(val: 1, addr: lutsel); |
603 | writeb(val: index, addr); |
604 | writel(val: color, addr: data); |
605 | |
606 | writel(val: 0, addr: lutsel); |
607 | writeb(val: index, addr); |
608 | writel(val: color, addr: data); |
609 | } |
610 | |
611 | static void __iomem *ofdrm_qemu_cmap_ioremap(struct ofdrm_device *odev, |
612 | struct device_node *of_node, |
613 | u64 fb_base) |
614 | { |
615 | static const __be32 io_of_addr[3] = { |
616 | cpu_to_be32(0x01000000), |
617 | cpu_to_be32(0x00), |
618 | cpu_to_be32(0x00), |
619 | }; |
620 | |
621 | struct drm_device *dev = &odev->dev; |
622 | u64 address; |
623 | void __iomem *cmap_base; |
624 | |
625 | address = of_translate_address(np: of_node, addr: io_of_addr); |
626 | if (address == OF_BAD_ADDR) |
627 | return IOMEM_ERR_PTR(-ENODEV); |
628 | |
629 | cmap_base = devm_ioremap(dev: dev->dev, offset: address + 0x3c8, size: 2); |
630 | if (!cmap_base) |
631 | return IOMEM_ERR_PTR(-ENOMEM); |
632 | |
633 | return cmap_base; |
634 | } |
635 | |
636 | static void ofdrm_qemu_cmap_write(struct ofdrm_device *odev, unsigned char index, |
637 | unsigned char r, unsigned char g, unsigned char b) |
638 | { |
639 | void __iomem *addr = odev->cmap_base; |
640 | void __iomem *data = odev->cmap_base + 1; |
641 | |
642 | writeb(val: index, addr); |
643 | writeb(val: r, addr: data); |
644 | writeb(val: g, addr: data); |
645 | writeb(val: b, addr: data); |
646 | } |
647 | |
648 | static void ofdrm_device_set_gamma_linear(struct ofdrm_device *odev, |
649 | const struct drm_format_info *format) |
650 | { |
651 | struct drm_device *dev = &odev->dev; |
652 | int i; |
653 | |
654 | switch (format->format) { |
655 | case DRM_FORMAT_RGB565: |
656 | case DRM_FORMAT_RGB565 | DRM_FORMAT_BIG_ENDIAN: |
657 | /* Use better interpolation, to take 32 values from 0 to 255 */ |
658 | for (i = 0; i < OFDRM_GAMMA_LUT_SIZE / 8; i++) { |
659 | unsigned char r = i * 8 + i / 4; |
660 | unsigned char g = i * 4 + i / 16; |
661 | unsigned char b = i * 8 + i / 4; |
662 | |
663 | odev->funcs->cmap_write(odev, i, r, g, b); |
664 | } |
665 | /* Green has one more bit, so add padding with 0 for red and blue. */ |
666 | for (i = OFDRM_GAMMA_LUT_SIZE / 8; i < OFDRM_GAMMA_LUT_SIZE / 4; i++) { |
667 | unsigned char r = 0; |
668 | unsigned char g = i * 4 + i / 16; |
669 | unsigned char b = 0; |
670 | |
671 | odev->funcs->cmap_write(odev, i, r, g, b); |
672 | } |
673 | break; |
674 | case DRM_FORMAT_XRGB8888: |
675 | case DRM_FORMAT_BGRX8888: |
676 | for (i = 0; i < OFDRM_GAMMA_LUT_SIZE; i++) |
677 | odev->funcs->cmap_write(odev, i, i, i, i); |
678 | break; |
679 | default: |
680 | drm_warn_once(dev, "Unsupported format %p4cc for gamma correction\n" , |
681 | &format->format); |
682 | break; |
683 | } |
684 | } |
685 | |
686 | static void ofdrm_device_set_gamma(struct ofdrm_device *odev, |
687 | const struct drm_format_info *format, |
688 | struct drm_color_lut *lut) |
689 | { |
690 | struct drm_device *dev = &odev->dev; |
691 | int i; |
692 | |
693 | switch (format->format) { |
694 | case DRM_FORMAT_RGB565: |
695 | case DRM_FORMAT_RGB565 | DRM_FORMAT_BIG_ENDIAN: |
696 | /* Use better interpolation, to take 32 values from lut[0] to lut[255] */ |
697 | for (i = 0; i < OFDRM_GAMMA_LUT_SIZE / 8; i++) { |
698 | unsigned char r = lut[i * 8 + i / 4].red >> 8; |
699 | unsigned char g = lut[i * 4 + i / 16].green >> 8; |
700 | unsigned char b = lut[i * 8 + i / 4].blue >> 8; |
701 | |
702 | odev->funcs->cmap_write(odev, i, r, g, b); |
703 | } |
704 | /* Green has one more bit, so add padding with 0 for red and blue. */ |
705 | for (i = OFDRM_GAMMA_LUT_SIZE / 8; i < OFDRM_GAMMA_LUT_SIZE / 4; i++) { |
706 | unsigned char r = 0; |
707 | unsigned char g = lut[i * 4 + i / 16].green >> 8; |
708 | unsigned char b = 0; |
709 | |
710 | odev->funcs->cmap_write(odev, i, r, g, b); |
711 | } |
712 | break; |
713 | case DRM_FORMAT_XRGB8888: |
714 | case DRM_FORMAT_BGRX8888: |
715 | for (i = 0; i < OFDRM_GAMMA_LUT_SIZE; i++) { |
716 | unsigned char r = lut[i].red >> 8; |
717 | unsigned char g = lut[i].green >> 8; |
718 | unsigned char b = lut[i].blue >> 8; |
719 | |
720 | odev->funcs->cmap_write(odev, i, r, g, b); |
721 | } |
722 | break; |
723 | default: |
724 | drm_warn_once(dev, "Unsupported format %p4cc for gamma correction\n" , |
725 | &format->format); |
726 | break; |
727 | } |
728 | } |
729 | |
730 | /* |
731 | * Modesetting |
732 | */ |
733 | |
734 | struct ofdrm_crtc_state { |
735 | struct drm_crtc_state base; |
736 | |
737 | /* Primary-plane format; required for color mgmt. */ |
738 | const struct drm_format_info *format; |
739 | }; |
740 | |
741 | static struct ofdrm_crtc_state *to_ofdrm_crtc_state(struct drm_crtc_state *base) |
742 | { |
743 | return container_of(base, struct ofdrm_crtc_state, base); |
744 | } |
745 | |
746 | static void ofdrm_crtc_state_destroy(struct ofdrm_crtc_state *ofdrm_crtc_state) |
747 | { |
748 | __drm_atomic_helper_crtc_destroy_state(state: &ofdrm_crtc_state->base); |
749 | kfree(objp: ofdrm_crtc_state); |
750 | } |
751 | |
752 | static const uint64_t ofdrm_primary_plane_format_modifiers[] = { |
753 | DRM_FORMAT_MOD_LINEAR, |
754 | DRM_FORMAT_MOD_INVALID |
755 | }; |
756 | |
757 | static int ofdrm_primary_plane_helper_atomic_check(struct drm_plane *plane, |
758 | struct drm_atomic_state *new_state) |
759 | { |
760 | struct drm_device *dev = plane->dev; |
761 | struct ofdrm_device *odev = ofdrm_device_of_dev(dev); |
762 | struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state: new_state, plane); |
763 | struct drm_shadow_plane_state *new_shadow_plane_state = |
764 | to_drm_shadow_plane_state(state: new_plane_state); |
765 | struct drm_framebuffer *new_fb = new_plane_state->fb; |
766 | struct drm_crtc *new_crtc = new_plane_state->crtc; |
767 | struct drm_crtc_state *new_crtc_state = NULL; |
768 | struct ofdrm_crtc_state *new_ofdrm_crtc_state; |
769 | int ret; |
770 | |
771 | if (new_crtc) |
772 | new_crtc_state = drm_atomic_get_new_crtc_state(state: new_state, crtc: new_plane_state->crtc); |
773 | |
774 | ret = drm_atomic_helper_check_plane_state(plane_state: new_plane_state, crtc_state: new_crtc_state, |
775 | DRM_PLANE_NO_SCALING, |
776 | DRM_PLANE_NO_SCALING, |
777 | can_position: false, can_update_disabled: false); |
778 | if (ret) |
779 | return ret; |
780 | else if (!new_plane_state->visible) |
781 | return 0; |
782 | |
783 | if (new_fb->format != odev->format) { |
784 | void *buf; |
785 | |
786 | /* format conversion necessary; reserve buffer */ |
787 | buf = drm_format_conv_state_reserve(state: &new_shadow_plane_state->fmtcnv_state, |
788 | new_size: odev->pitch, GFP_KERNEL); |
789 | if (!buf) |
790 | return -ENOMEM; |
791 | } |
792 | |
793 | new_crtc_state = drm_atomic_get_new_crtc_state(state: new_state, crtc: new_plane_state->crtc); |
794 | |
795 | new_ofdrm_crtc_state = to_ofdrm_crtc_state(base: new_crtc_state); |
796 | new_ofdrm_crtc_state->format = new_fb->format; |
797 | |
798 | return 0; |
799 | } |
800 | |
801 | static void ofdrm_primary_plane_helper_atomic_update(struct drm_plane *plane, |
802 | struct drm_atomic_state *state) |
803 | { |
804 | struct drm_device *dev = plane->dev; |
805 | struct ofdrm_device *odev = ofdrm_device_of_dev(dev); |
806 | struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); |
807 | struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); |
808 | struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state: plane_state); |
809 | struct drm_framebuffer *fb = plane_state->fb; |
810 | unsigned int dst_pitch = odev->pitch; |
811 | const struct drm_format_info *dst_format = odev->format; |
812 | struct drm_atomic_helper_damage_iter iter; |
813 | struct drm_rect damage; |
814 | int ret, idx; |
815 | |
816 | ret = drm_gem_fb_begin_cpu_access(fb, dir: DMA_FROM_DEVICE); |
817 | if (ret) |
818 | return; |
819 | |
820 | if (!drm_dev_enter(dev, idx: &idx)) |
821 | goto out_drm_gem_fb_end_cpu_access; |
822 | |
823 | drm_atomic_helper_damage_iter_init(iter: &iter, old_state: old_plane_state, new_state: plane_state); |
824 | drm_atomic_for_each_plane_damage(&iter, &damage) { |
825 | struct iosys_map dst = odev->screen_base; |
826 | struct drm_rect dst_clip = plane_state->dst; |
827 | |
828 | if (!drm_rect_intersect(r: &dst_clip, clip: &damage)) |
829 | continue; |
830 | |
831 | iosys_map_incr(map: &dst, incr: drm_fb_clip_offset(pitch: dst_pitch, format: dst_format, clip: &dst_clip)); |
832 | drm_fb_blit(dst: &dst, dst_pitch: &dst_pitch, dst_format: dst_format->format, src: shadow_plane_state->data, fb, |
833 | clip: &damage, state: &shadow_plane_state->fmtcnv_state); |
834 | } |
835 | |
836 | drm_dev_exit(idx); |
837 | out_drm_gem_fb_end_cpu_access: |
838 | drm_gem_fb_end_cpu_access(fb, dir: DMA_FROM_DEVICE); |
839 | } |
840 | |
841 | static void ofdrm_primary_plane_helper_atomic_disable(struct drm_plane *plane, |
842 | struct drm_atomic_state *state) |
843 | { |
844 | struct drm_device *dev = plane->dev; |
845 | struct ofdrm_device *odev = ofdrm_device_of_dev(dev); |
846 | struct iosys_map dst = odev->screen_base; |
847 | struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); |
848 | void __iomem *dst_vmap = dst.vaddr_iomem; /* TODO: Use mapping abstraction */ |
849 | unsigned int dst_pitch = odev->pitch; |
850 | const struct drm_format_info *dst_format = odev->format; |
851 | struct drm_rect dst_clip; |
852 | unsigned long lines, linepixels, i; |
853 | int idx; |
854 | |
855 | drm_rect_init(r: &dst_clip, |
856 | x: plane_state->src_x >> 16, y: plane_state->src_y >> 16, |
857 | width: plane_state->src_w >> 16, height: plane_state->src_h >> 16); |
858 | |
859 | lines = drm_rect_height(r: &dst_clip); |
860 | linepixels = drm_rect_width(r: &dst_clip); |
861 | |
862 | if (!drm_dev_enter(dev, idx: &idx)) |
863 | return; |
864 | |
865 | /* Clear buffer to black if disabled */ |
866 | dst_vmap += drm_fb_clip_offset(pitch: dst_pitch, format: dst_format, clip: &dst_clip); |
867 | for (i = 0; i < lines; ++i) { |
868 | memset_io(dst_vmap, 0, linepixels * dst_format->cpp[0]); |
869 | dst_vmap += dst_pitch; |
870 | } |
871 | |
872 | drm_dev_exit(idx); |
873 | } |
874 | |
875 | static const struct drm_plane_helper_funcs ofdrm_primary_plane_helper_funcs = { |
876 | DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, |
877 | .atomic_check = ofdrm_primary_plane_helper_atomic_check, |
878 | .atomic_update = ofdrm_primary_plane_helper_atomic_update, |
879 | .atomic_disable = ofdrm_primary_plane_helper_atomic_disable, |
880 | }; |
881 | |
882 | static const struct drm_plane_funcs ofdrm_primary_plane_funcs = { |
883 | .update_plane = drm_atomic_helper_update_plane, |
884 | .disable_plane = drm_atomic_helper_disable_plane, |
885 | .destroy = drm_plane_cleanup, |
886 | DRM_GEM_SHADOW_PLANE_FUNCS, |
887 | }; |
888 | |
889 | static enum drm_mode_status ofdrm_crtc_helper_mode_valid(struct drm_crtc *crtc, |
890 | const struct drm_display_mode *mode) |
891 | { |
892 | struct ofdrm_device *odev = ofdrm_device_of_dev(dev: crtc->dev); |
893 | |
894 | return drm_crtc_helper_mode_valid_fixed(crtc, mode, fixed_mode: &odev->mode); |
895 | } |
896 | |
897 | static int ofdrm_crtc_helper_atomic_check(struct drm_crtc *crtc, |
898 | struct drm_atomic_state *new_state) |
899 | { |
900 | static const size_t gamma_lut_length = OFDRM_GAMMA_LUT_SIZE * sizeof(struct drm_color_lut); |
901 | |
902 | struct drm_device *dev = crtc->dev; |
903 | struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(state: new_state, crtc); |
904 | int ret; |
905 | |
906 | if (!new_crtc_state->enable) |
907 | return 0; |
908 | |
909 | ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state: new_crtc_state); |
910 | if (ret) |
911 | return ret; |
912 | |
913 | if (new_crtc_state->color_mgmt_changed) { |
914 | struct drm_property_blob *gamma_lut = new_crtc_state->gamma_lut; |
915 | |
916 | if (gamma_lut && (gamma_lut->length != gamma_lut_length)) { |
917 | drm_dbg(dev, "Incorrect gamma_lut length %zu\n" , gamma_lut->length); |
918 | return -EINVAL; |
919 | } |
920 | } |
921 | |
922 | return 0; |
923 | } |
924 | |
925 | static void ofdrm_crtc_helper_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) |
926 | { |
927 | struct ofdrm_device *odev = ofdrm_device_of_dev(dev: crtc->dev); |
928 | struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); |
929 | struct ofdrm_crtc_state *ofdrm_crtc_state = to_ofdrm_crtc_state(base: crtc_state); |
930 | |
931 | if (crtc_state->enable && crtc_state->color_mgmt_changed) { |
932 | const struct drm_format_info *format = ofdrm_crtc_state->format; |
933 | |
934 | if (crtc_state->gamma_lut) |
935 | ofdrm_device_set_gamma(odev, format, lut: crtc_state->gamma_lut->data); |
936 | else |
937 | ofdrm_device_set_gamma_linear(odev, format); |
938 | } |
939 | } |
940 | |
941 | /* |
942 | * The CRTC is always enabled. Screen updates are performed by |
943 | * the primary plane's atomic_update function. Disabling clears |
944 | * the screen in the primary plane's atomic_disable function. |
945 | */ |
946 | static const struct drm_crtc_helper_funcs ofdrm_crtc_helper_funcs = { |
947 | .mode_valid = ofdrm_crtc_helper_mode_valid, |
948 | .atomic_check = ofdrm_crtc_helper_atomic_check, |
949 | .atomic_flush = ofdrm_crtc_helper_atomic_flush, |
950 | }; |
951 | |
952 | static void ofdrm_crtc_reset(struct drm_crtc *crtc) |
953 | { |
954 | struct ofdrm_crtc_state *ofdrm_crtc_state = |
955 | kzalloc(size: sizeof(*ofdrm_crtc_state), GFP_KERNEL); |
956 | |
957 | if (crtc->state) |
958 | ofdrm_crtc_state_destroy(ofdrm_crtc_state: to_ofdrm_crtc_state(base: crtc->state)); |
959 | |
960 | if (ofdrm_crtc_state) |
961 | __drm_atomic_helper_crtc_reset(crtc, state: &ofdrm_crtc_state->base); |
962 | else |
963 | __drm_atomic_helper_crtc_reset(crtc, NULL); |
964 | } |
965 | |
966 | static struct drm_crtc_state *ofdrm_crtc_atomic_duplicate_state(struct drm_crtc *crtc) |
967 | { |
968 | struct drm_device *dev = crtc->dev; |
969 | struct drm_crtc_state *crtc_state = crtc->state; |
970 | struct ofdrm_crtc_state *new_ofdrm_crtc_state; |
971 | struct ofdrm_crtc_state *ofdrm_crtc_state; |
972 | |
973 | if (drm_WARN_ON(dev, !crtc_state)) |
974 | return NULL; |
975 | |
976 | new_ofdrm_crtc_state = kzalloc(size: sizeof(*new_ofdrm_crtc_state), GFP_KERNEL); |
977 | if (!new_ofdrm_crtc_state) |
978 | return NULL; |
979 | |
980 | ofdrm_crtc_state = to_ofdrm_crtc_state(base: crtc_state); |
981 | |
982 | __drm_atomic_helper_crtc_duplicate_state(crtc, state: &new_ofdrm_crtc_state->base); |
983 | new_ofdrm_crtc_state->format = ofdrm_crtc_state->format; |
984 | |
985 | return &new_ofdrm_crtc_state->base; |
986 | } |
987 | |
988 | static void ofdrm_crtc_atomic_destroy_state(struct drm_crtc *crtc, |
989 | struct drm_crtc_state *crtc_state) |
990 | { |
991 | ofdrm_crtc_state_destroy(ofdrm_crtc_state: to_ofdrm_crtc_state(base: crtc_state)); |
992 | } |
993 | |
994 | static const struct drm_crtc_funcs ofdrm_crtc_funcs = { |
995 | .reset = ofdrm_crtc_reset, |
996 | .destroy = drm_crtc_cleanup, |
997 | .set_config = drm_atomic_helper_set_config, |
998 | .page_flip = drm_atomic_helper_page_flip, |
999 | .atomic_duplicate_state = ofdrm_crtc_atomic_duplicate_state, |
1000 | .atomic_destroy_state = ofdrm_crtc_atomic_destroy_state, |
1001 | }; |
1002 | |
1003 | static int ofdrm_connector_helper_get_modes(struct drm_connector *connector) |
1004 | { |
1005 | struct ofdrm_device *odev = ofdrm_device_of_dev(dev: connector->dev); |
1006 | |
1007 | return drm_connector_helper_get_modes_fixed(connector, fixed_mode: &odev->mode); |
1008 | } |
1009 | |
1010 | static const struct drm_connector_helper_funcs ofdrm_connector_helper_funcs = { |
1011 | .get_modes = ofdrm_connector_helper_get_modes, |
1012 | }; |
1013 | |
1014 | static const struct drm_connector_funcs ofdrm_connector_funcs = { |
1015 | .reset = drm_atomic_helper_connector_reset, |
1016 | .fill_modes = drm_helper_probe_single_connector_modes, |
1017 | .destroy = drm_connector_cleanup, |
1018 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, |
1019 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, |
1020 | }; |
1021 | |
1022 | static const struct drm_mode_config_funcs ofdrm_mode_config_funcs = { |
1023 | .fb_create = drm_gem_fb_create_with_dirty, |
1024 | .atomic_check = drm_atomic_helper_check, |
1025 | .atomic_commit = drm_atomic_helper_commit, |
1026 | }; |
1027 | |
1028 | /* |
1029 | * Init / Cleanup |
1030 | */ |
1031 | |
1032 | static const struct ofdrm_device_funcs ofdrm_unknown_device_funcs = { |
1033 | }; |
1034 | |
1035 | static const struct ofdrm_device_funcs ofdrm_mach64_device_funcs = { |
1036 | .cmap_ioremap = ofdrm_mach64_cmap_ioremap, |
1037 | .cmap_write = ofdrm_mach64_cmap_write, |
1038 | }; |
1039 | |
1040 | static const struct ofdrm_device_funcs ofdrm_rage128_device_funcs = { |
1041 | .cmap_ioremap = ofdrm_rage128_cmap_ioremap, |
1042 | .cmap_write = ofdrm_rage128_cmap_write, |
1043 | }; |
1044 | |
1045 | static const struct ofdrm_device_funcs ofdrm_rage_m3a_device_funcs = { |
1046 | .cmap_ioremap = ofdrm_rage_m3a_cmap_ioremap, |
1047 | .cmap_write = ofdrm_rage_m3a_cmap_write, |
1048 | }; |
1049 | |
1050 | static const struct ofdrm_device_funcs ofdrm_rage_m3b_device_funcs = { |
1051 | .cmap_ioremap = ofdrm_rage_m3b_cmap_ioremap, |
1052 | .cmap_write = ofdrm_rage_m3b_cmap_write, |
1053 | }; |
1054 | |
1055 | static const struct ofdrm_device_funcs ofdrm_radeon_device_funcs = { |
1056 | .cmap_ioremap = ofdrm_radeon_cmap_ioremap, |
1057 | .cmap_write = ofdrm_rage128_cmap_write, /* same as Rage128 */ |
1058 | }; |
1059 | |
1060 | static const struct ofdrm_device_funcs ofdrm_gxt2000_device_funcs = { |
1061 | .cmap_ioremap = ofdrm_gxt2000_cmap_ioremap, |
1062 | .cmap_write = ofdrm_gxt2000_cmap_write, |
1063 | }; |
1064 | |
1065 | static const struct ofdrm_device_funcs ofdrm_avivo_device_funcs = { |
1066 | .cmap_ioremap = ofdrm_avivo_cmap_ioremap, |
1067 | .cmap_write = ofdrm_avivo_cmap_write, |
1068 | }; |
1069 | |
1070 | static const struct ofdrm_device_funcs ofdrm_qemu_device_funcs = { |
1071 | .cmap_ioremap = ofdrm_qemu_cmap_ioremap, |
1072 | .cmap_write = ofdrm_qemu_cmap_write, |
1073 | }; |
1074 | |
1075 | static struct drm_display_mode ofdrm_mode(unsigned int width, unsigned int height) |
1076 | { |
1077 | /* |
1078 | * Assume a monitor resolution of 96 dpi to |
1079 | * get a somewhat reasonable screen size. |
1080 | */ |
1081 | const struct drm_display_mode mode = { |
1082 | DRM_MODE_INIT(60, width, height, |
1083 | DRM_MODE_RES_MM(width, 96ul), |
1084 | DRM_MODE_RES_MM(height, 96ul)) |
1085 | }; |
1086 | |
1087 | return mode; |
1088 | } |
1089 | |
1090 | static struct ofdrm_device *ofdrm_device_create(struct drm_driver *drv, |
1091 | struct platform_device *pdev) |
1092 | { |
1093 | struct device_node *of_node = pdev->dev.of_node; |
1094 | struct ofdrm_device *odev; |
1095 | struct drm_device *dev; |
1096 | enum ofdrm_model model; |
1097 | bool big_endian; |
1098 | int width, height, depth, linebytes; |
1099 | const struct drm_format_info *format; |
1100 | u64 address; |
1101 | resource_size_t fb_size, fb_base, fb_pgbase, fb_pgsize; |
1102 | struct resource *res, *mem; |
1103 | void __iomem *screen_base; |
1104 | struct drm_plane *primary_plane; |
1105 | struct drm_crtc *crtc; |
1106 | struct drm_encoder *encoder; |
1107 | struct drm_connector *connector; |
1108 | unsigned long max_width, max_height; |
1109 | size_t nformats; |
1110 | int ret; |
1111 | |
1112 | odev = devm_drm_dev_alloc(&pdev->dev, drv, struct ofdrm_device, dev); |
1113 | if (IS_ERR(ptr: odev)) |
1114 | return ERR_CAST(ptr: odev); |
1115 | dev = &odev->dev; |
1116 | platform_set_drvdata(pdev, data: dev); |
1117 | |
1118 | ret = ofdrm_device_init_pci(odev); |
1119 | if (ret) |
1120 | return ERR_PTR(error: ret); |
1121 | |
1122 | /* |
1123 | * OF display-node settings |
1124 | */ |
1125 | |
1126 | model = display_get_model_of(dev, of_node); |
1127 | drm_dbg(dev, "detected model %d\n" , model); |
1128 | |
1129 | switch (model) { |
1130 | case OFDRM_MODEL_UNKNOWN: |
1131 | odev->funcs = &ofdrm_unknown_device_funcs; |
1132 | break; |
1133 | case OFDRM_MODEL_MACH64: |
1134 | odev->funcs = &ofdrm_mach64_device_funcs; |
1135 | break; |
1136 | case OFDRM_MODEL_RAGE128: |
1137 | odev->funcs = &ofdrm_rage128_device_funcs; |
1138 | break; |
1139 | case OFDRM_MODEL_RAGE_M3A: |
1140 | odev->funcs = &ofdrm_rage_m3a_device_funcs; |
1141 | break; |
1142 | case OFDRM_MODEL_RAGE_M3B: |
1143 | odev->funcs = &ofdrm_rage_m3b_device_funcs; |
1144 | break; |
1145 | case OFDRM_MODEL_RADEON: |
1146 | odev->funcs = &ofdrm_radeon_device_funcs; |
1147 | break; |
1148 | case OFDRM_MODEL_GXT2000: |
1149 | odev->funcs = &ofdrm_gxt2000_device_funcs; |
1150 | break; |
1151 | case OFDRM_MODEL_AVIVO: |
1152 | odev->funcs = &ofdrm_avivo_device_funcs; |
1153 | break; |
1154 | case OFDRM_MODEL_QEMU: |
1155 | odev->funcs = &ofdrm_qemu_device_funcs; |
1156 | break; |
1157 | } |
1158 | |
1159 | big_endian = display_get_big_endian_of(dev, of_node); |
1160 | |
1161 | width = display_get_width_of(dev, of_node); |
1162 | if (width < 0) |
1163 | return ERR_PTR(error: width); |
1164 | height = display_get_height_of(dev, of_node); |
1165 | if (height < 0) |
1166 | return ERR_PTR(error: height); |
1167 | depth = display_get_depth_of(dev, of_node); |
1168 | if (depth < 0) |
1169 | return ERR_PTR(error: depth); |
1170 | linebytes = display_get_linebytes_of(dev, of_node); |
1171 | if (linebytes < 0) |
1172 | return ERR_PTR(error: linebytes); |
1173 | |
1174 | format = display_get_validated_format(dev, depth, big_endian); |
1175 | if (IS_ERR(ptr: format)) |
1176 | return ERR_CAST(ptr: format); |
1177 | if (!linebytes) { |
1178 | linebytes = drm_format_info_min_pitch(info: format, plane: 0, buffer_width: width); |
1179 | if (drm_WARN_ON(dev, !linebytes)) |
1180 | return ERR_PTR(error: -EINVAL); |
1181 | } |
1182 | |
1183 | fb_size = linebytes * height; |
1184 | |
1185 | /* |
1186 | * Try to figure out the address of the framebuffer. Unfortunately, Open |
1187 | * Firmware doesn't provide a standard way to do so. All we can do is a |
1188 | * dodgy heuristic that happens to work in practice. |
1189 | * |
1190 | * On most machines, the "address" property contains what we need, though |
1191 | * not on Matrox cards found in IBM machines. What appears to give good |
1192 | * results is to go through the PCI ranges and pick one that encloses the |
1193 | * "address" property. If none match, we pick the largest. |
1194 | */ |
1195 | address = display_get_address_of(dev, of_node); |
1196 | if (address != OF_BAD_ADDR) { |
1197 | struct resource fb_res = DEFINE_RES_MEM(address, fb_size); |
1198 | |
1199 | res = ofdrm_find_fb_resource(odev, fb_res: &fb_res); |
1200 | if (!res) |
1201 | return ERR_PTR(error: -EINVAL); |
1202 | if (resource_contains(r1: res, r2: &fb_res)) |
1203 | fb_base = address; |
1204 | else |
1205 | fb_base = res->start; |
1206 | } else { |
1207 | struct resource fb_res = DEFINE_RES_MEM(0u, fb_size); |
1208 | |
1209 | res = ofdrm_find_fb_resource(odev, fb_res: &fb_res); |
1210 | if (!res) |
1211 | return ERR_PTR(error: -EINVAL); |
1212 | fb_base = res->start; |
1213 | } |
1214 | |
1215 | /* |
1216 | * I/O resources |
1217 | */ |
1218 | |
1219 | fb_pgbase = round_down(fb_base, PAGE_SIZE); |
1220 | fb_pgsize = fb_base - fb_pgbase + round_up(fb_size, PAGE_SIZE); |
1221 | |
1222 | ret = devm_aperture_acquire_from_firmware(dev, base: fb_pgbase, size: fb_pgsize); |
1223 | if (ret) { |
1224 | drm_err(dev, "could not acquire memory range %pr: error %d\n" , &res, ret); |
1225 | return ERR_PTR(error: ret); |
1226 | } |
1227 | |
1228 | mem = devm_request_mem_region(&pdev->dev, fb_pgbase, fb_pgsize, drv->name); |
1229 | if (!mem) { |
1230 | drm_warn(dev, "could not acquire memory region %pr\n" , &res); |
1231 | return ERR_PTR(error: -ENOMEM); |
1232 | } |
1233 | |
1234 | screen_base = devm_ioremap(dev: &pdev->dev, offset: mem->start, size: resource_size(res: mem)); |
1235 | if (!screen_base) |
1236 | return ERR_PTR(error: -ENOMEM); |
1237 | |
1238 | if (odev->funcs->cmap_ioremap) { |
1239 | void __iomem *cmap_base = odev->funcs->cmap_ioremap(odev, of_node, fb_base); |
1240 | |
1241 | if (IS_ERR(ptr: cmap_base)) { |
1242 | /* Don't fail; continue without colormap */ |
1243 | drm_warn(dev, "could not find colormap: error %ld\n" , PTR_ERR(cmap_base)); |
1244 | } else { |
1245 | odev->cmap_base = cmap_base; |
1246 | } |
1247 | } |
1248 | |
1249 | /* |
1250 | * Firmware framebuffer |
1251 | */ |
1252 | |
1253 | iosys_map_set_vaddr_iomem(map: &odev->screen_base, vaddr_iomem: screen_base); |
1254 | odev->mode = ofdrm_mode(width, height); |
1255 | odev->format = format; |
1256 | odev->pitch = linebytes; |
1257 | |
1258 | drm_dbg(dev, "display mode={" DRM_MODE_FMT "}\n" , DRM_MODE_ARG(&odev->mode)); |
1259 | drm_dbg(dev, "framebuffer format=%p4cc, size=%dx%d, linebytes=%d byte\n" , |
1260 | &format->format, width, height, linebytes); |
1261 | |
1262 | /* |
1263 | * Mode-setting pipeline |
1264 | */ |
1265 | |
1266 | ret = drmm_mode_config_init(dev); |
1267 | if (ret) |
1268 | return ERR_PTR(error: ret); |
1269 | |
1270 | max_width = max_t(unsigned long, width, DRM_SHADOW_PLANE_MAX_WIDTH); |
1271 | max_height = max_t(unsigned long, height, DRM_SHADOW_PLANE_MAX_HEIGHT); |
1272 | |
1273 | dev->mode_config.min_width = width; |
1274 | dev->mode_config.max_width = max_width; |
1275 | dev->mode_config.min_height = height; |
1276 | dev->mode_config.max_height = max_height; |
1277 | dev->mode_config.funcs = &ofdrm_mode_config_funcs; |
1278 | dev->mode_config.preferred_depth = format->depth; |
1279 | dev->mode_config.quirk_addfb_prefer_host_byte_order = true; |
1280 | |
1281 | /* Primary plane */ |
1282 | |
1283 | nformats = drm_fb_build_fourcc_list(dev, native_fourccs: &format->format, native_nfourccs: 1, |
1284 | fourccs_out: odev->formats, ARRAY_SIZE(odev->formats)); |
1285 | |
1286 | primary_plane = &odev->primary_plane; |
1287 | ret = drm_universal_plane_init(dev, plane: primary_plane, possible_crtcs: 0, funcs: &ofdrm_primary_plane_funcs, |
1288 | formats: odev->formats, format_count: nformats, |
1289 | format_modifiers: ofdrm_primary_plane_format_modifiers, |
1290 | type: DRM_PLANE_TYPE_PRIMARY, NULL); |
1291 | if (ret) |
1292 | return ERR_PTR(error: ret); |
1293 | drm_plane_helper_add(plane: primary_plane, funcs: &ofdrm_primary_plane_helper_funcs); |
1294 | drm_plane_enable_fb_damage_clips(plane: primary_plane); |
1295 | |
1296 | /* CRTC */ |
1297 | |
1298 | crtc = &odev->crtc; |
1299 | ret = drm_crtc_init_with_planes(dev, crtc, primary: primary_plane, NULL, |
1300 | funcs: &ofdrm_crtc_funcs, NULL); |
1301 | if (ret) |
1302 | return ERR_PTR(error: ret); |
1303 | drm_crtc_helper_add(crtc, funcs: &ofdrm_crtc_helper_funcs); |
1304 | |
1305 | if (odev->cmap_base) { |
1306 | drm_mode_crtc_set_gamma_size(crtc, OFDRM_GAMMA_LUT_SIZE); |
1307 | drm_crtc_enable_color_mgmt(crtc, degamma_lut_size: 0, has_ctm: false, OFDRM_GAMMA_LUT_SIZE); |
1308 | } |
1309 | |
1310 | /* Encoder */ |
1311 | |
1312 | encoder = &odev->encoder; |
1313 | ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_NONE); |
1314 | if (ret) |
1315 | return ERR_PTR(error: ret); |
1316 | encoder->possible_crtcs = drm_crtc_mask(crtc); |
1317 | |
1318 | /* Connector */ |
1319 | |
1320 | connector = &odev->connector; |
1321 | ret = drm_connector_init(dev, connector, funcs: &ofdrm_connector_funcs, |
1322 | DRM_MODE_CONNECTOR_Unknown); |
1323 | if (ret) |
1324 | return ERR_PTR(error: ret); |
1325 | drm_connector_helper_add(connector, funcs: &ofdrm_connector_helper_funcs); |
1326 | drm_connector_set_panel_orientation_with_quirk(connector, |
1327 | panel_orientation: DRM_MODE_PANEL_ORIENTATION_UNKNOWN, |
1328 | width, height); |
1329 | |
1330 | ret = drm_connector_attach_encoder(connector, encoder); |
1331 | if (ret) |
1332 | return ERR_PTR(error: ret); |
1333 | |
1334 | drm_mode_config_reset(dev); |
1335 | |
1336 | return odev; |
1337 | } |
1338 | |
1339 | /* |
1340 | * DRM driver |
1341 | */ |
1342 | |
1343 | DEFINE_DRM_GEM_FOPS(ofdrm_fops); |
1344 | |
1345 | static struct drm_driver ofdrm_driver = { |
1346 | DRM_GEM_SHMEM_DRIVER_OPS, |
1347 | .name = DRIVER_NAME, |
1348 | .desc = DRIVER_DESC, |
1349 | .date = DRIVER_DATE, |
1350 | .major = DRIVER_MAJOR, |
1351 | .minor = DRIVER_MINOR, |
1352 | .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET, |
1353 | .fops = &ofdrm_fops, |
1354 | }; |
1355 | |
1356 | /* |
1357 | * Platform driver |
1358 | */ |
1359 | |
1360 | static int ofdrm_probe(struct platform_device *pdev) |
1361 | { |
1362 | struct ofdrm_device *odev; |
1363 | struct drm_device *dev; |
1364 | unsigned int color_mode; |
1365 | int ret; |
1366 | |
1367 | odev = ofdrm_device_create(drv: &ofdrm_driver, pdev); |
1368 | if (IS_ERR(ptr: odev)) |
1369 | return PTR_ERR(ptr: odev); |
1370 | dev = &odev->dev; |
1371 | |
1372 | ret = drm_dev_register(dev, flags: 0); |
1373 | if (ret) |
1374 | return ret; |
1375 | |
1376 | color_mode = drm_format_info_bpp(info: odev->format, plane: 0); |
1377 | if (color_mode == 16) |
1378 | color_mode = odev->format->depth; // can be 15 or 16 |
1379 | |
1380 | drm_fbdev_generic_setup(dev, preferred_bpp: color_mode); |
1381 | |
1382 | return 0; |
1383 | } |
1384 | |
1385 | static void ofdrm_remove(struct platform_device *pdev) |
1386 | { |
1387 | struct drm_device *dev = platform_get_drvdata(pdev); |
1388 | |
1389 | drm_dev_unplug(dev); |
1390 | } |
1391 | |
1392 | static const struct of_device_id ofdrm_of_match_display[] = { |
1393 | { .compatible = "display" , }, |
1394 | { }, |
1395 | }; |
1396 | MODULE_DEVICE_TABLE(of, ofdrm_of_match_display); |
1397 | |
1398 | static struct platform_driver ofdrm_platform_driver = { |
1399 | .driver = { |
1400 | .name = "of-display" , |
1401 | .of_match_table = ofdrm_of_match_display, |
1402 | }, |
1403 | .probe = ofdrm_probe, |
1404 | .remove_new = ofdrm_remove, |
1405 | }; |
1406 | |
1407 | module_platform_driver(ofdrm_platform_driver); |
1408 | |
1409 | MODULE_DESCRIPTION(DRIVER_DESC); |
1410 | MODULE_LICENSE("GPL" ); |
1411 | |