Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/sysfb: ofdrm: Add EDID support

Add EDID support to sysfb connector helpers. Read the EDID property
from the OF node in ofdrm. Without EDID, this does nothing.

Some systems with OF display, such as 32-bit PPC Macintoshs, provide
the system display's EDID data as node property in their DT. Exporting
this information allows compositors to implement correct DPI and
meaningful color management.

v3:
- avoid parser error by clearing EDID extension field
v2:
- return errno codes on errors (Jani)
- simplify EDID read logic (Jani)

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Link: https://lore.kernel.org/r/20250401094056.32904-13-tzimmermann@suse.de

+56
+34
drivers/gpu/drm/sysfb/drm_sysfb_helper.c
··· 9 9 #include <drm/drm_atomic_state_helper.h> 10 10 #include <drm/drm_damage_helper.h> 11 11 #include <drm/drm_drv.h> 12 + #include <drm/drm_edid.h> 12 13 #include <drm/drm_fourcc.h> 13 14 #include <drm/drm_framebuffer.h> 14 15 #include <drm/drm_gem_atomic_helper.h> ··· 282 281 * Connector 283 282 */ 284 283 284 + static int drm_sysfb_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len) 285 + { 286 + struct drm_sysfb_device *sysfb = data; 287 + const u8 *edid = sysfb->edid; 288 + size_t off = block * EDID_LENGTH; 289 + size_t end = off + len; 290 + 291 + if (!edid) 292 + return -EINVAL; 293 + if (end > EDID_LENGTH) 294 + return -EINVAL; 295 + memcpy(buf, &edid[off], len); 296 + 297 + /* 298 + * We don't have EDID extensions available and reporting them 299 + * will upset DRM helpers. Thus clear the extension field and 300 + * update the checksum. Adding the extension flag to the checksum 301 + * does this. 302 + */ 303 + buf[127] += buf[126]; 304 + buf[126] = 0; 305 + 306 + return 0; 307 + } 308 + 285 309 int drm_sysfb_connector_helper_get_modes(struct drm_connector *connector) 286 310 { 287 311 struct drm_sysfb_device *sysfb = to_drm_sysfb_device(connector->dev); 312 + const struct drm_edid *drm_edid; 288 313 314 + if (sysfb->edid) { 315 + drm_edid = drm_edid_read_custom(connector, drm_sysfb_get_edid_block, sysfb); 316 + drm_edid_connector_update(connector, drm_edid); 317 + drm_edid_free(drm_edid); 318 + } 319 + 320 + /* Return the fixed mode even with EDID */ 289 321 return drm_connector_helper_get_modes_fixed(connector, &sysfb->fb_mode); 290 322 } 291 323 EXPORT_SYMBOL(drm_sysfb_connector_helper_get_modes);
+2
drivers/gpu/drm/sysfb/drm_sysfb_helper.h
··· 24 24 struct drm_sysfb_device { 25 25 struct drm_device dev; 26 26 27 + const u8 *edid; /* can be NULL */ 28 + 27 29 /* hardware settings */ 28 30 struct drm_display_mode fb_mode; 29 31 const struct drm_format_info *fb_format;
+20
drivers/gpu/drm/sysfb/ofdrm.c
··· 12 12 #include <drm/drm_damage_helper.h> 13 13 #include <drm/drm_device.h> 14 14 #include <drm/drm_drv.h> 15 + #include <drm/drm_edid.h> 15 16 #include <drm/drm_fbdev_shmem.h> 16 17 #include <drm/drm_format_helper.h> 17 18 #include <drm/drm_framebuffer.h> ··· 228 227 return address; 229 228 } 230 229 230 + static const u8 *display_get_edid_of(struct drm_device *dev, struct device_node *of_node, 231 + u8 buf[EDID_LENGTH]) 232 + { 233 + int ret = of_property_read_u8_array(of_node, "EDID", buf, EDID_LENGTH); 234 + 235 + if (ret) 236 + return NULL; 237 + return buf; 238 + } 239 + 231 240 static bool is_avivo(u32 vendor, u32 device) 232 241 { 233 242 /* This will match most R5xx */ ··· 308 297 309 298 /* colormap */ 310 299 void __iomem *cmap_base; 300 + 301 + u8 edid[EDID_LENGTH]; 311 302 312 303 /* modesetting */ 313 304 u32 formats[DRM_SYSFB_PLANE_NFORMATS(1)]; ··· 853 840 int width, height, depth, linebytes; 854 841 const struct drm_format_info *format; 855 842 u64 address; 843 + const u8 *edid; 856 844 resource_size_t fb_size, fb_base, fb_pgbase, fb_pgsize; 857 845 struct resource *res, *mem; 858 846 void __iomem *screen_base; ··· 1003 989 } 1004 990 } 1005 991 992 + /* EDID is optional */ 993 + edid = display_get_edid_of(dev, of_node, odev->edid); 994 + 1006 995 /* 1007 996 * Firmware framebuffer 1008 997 */ ··· 1016 999 sysfb->fb_pitch = linebytes; 1017 1000 if (odev->cmap_base) 1018 1001 sysfb->fb_gamma_lut_size = OFDRM_GAMMA_LUT_SIZE; 1002 + sysfb->edid = edid; 1019 1003 1020 1004 drm_dbg(dev, "display mode={" DRM_MODE_FMT "}\n", DRM_MODE_ARG(&sysfb->fb_mode)); 1021 1005 drm_dbg(dev, "framebuffer format=%p4cc, size=%dx%d, linebytes=%d byte\n", ··· 1090 1072 drm_connector_set_panel_orientation_with_quirk(connector, 1091 1073 DRM_MODE_PANEL_ORIENTATION_UNKNOWN, 1092 1074 width, height); 1075 + if (edid) 1076 + drm_connector_attach_edid_property(connector); 1093 1077 1094 1078 ret = drm_connector_attach_encoder(connector, encoder); 1095 1079 if (ret)