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