drm/vc4: Query firmware for custom HDMI mode
authorDave Stevenson <dave.stevenson@raspberrypi.org>
Wed, 3 Jul 2019 16:44:53 +0000 (17:44 +0100)
committerpopcornmix <popcornmix@gmail.com>
Wed, 1 Jul 2020 15:33:02 +0000 (16:33 +0100)
Allow custom HDMI modes to be specified from config.txt,
and these then override EDID parsing.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
drivers/gpu/drm/vc4/vc4_firmware_kms.c

index ff96ba0..3ea7974 100644 (file)
@@ -1066,6 +1066,56 @@ vc4_fkms_connector_detect(struct drm_connector *connector, bool force)
        return connector_status_connected;
 }
 
+/* Queries the firmware to populate a drm_mode structure for this display */
+static int vc4_fkms_get_fw_mode(struct vc4_fkms_connector *fkms_connector,
+                               struct drm_display_mode *mode)
+{
+       struct vc4_dev *vc4 = fkms_connector->vc4_dev;
+       struct set_timings timings = { 0 };
+       int ret;
+
+       timings.display = fkms_connector->display_number;
+
+       ret = rpi_firmware_property(vc4->firmware,
+                                   RPI_FIRMWARE_GET_DISPLAY_TIMING, &timings,
+                                   sizeof(timings));
+       if (ret || !timings.clock)
+               /* No mode returned - abort */
+               return -1;
+
+       /* Equivalent to DRM_MODE macro. */
+       memset(mode, 0, sizeof(*mode));
+       strncpy(mode->name, "FIXED_MODE", sizeof(mode->name));
+       mode->status = 0;
+       mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+       mode->clock = timings.clock;
+       mode->hdisplay = timings.hdisplay;
+       mode->hsync_start = timings.hsync_start;
+       mode->hsync_end = timings.hsync_end;
+       mode->htotal = timings.htotal;
+       mode->hskew = 0;
+       mode->vdisplay = timings.vdisplay;
+       mode->vsync_start = timings.vsync_start;
+       mode->vsync_end = timings.vsync_end;
+       mode->vtotal = timings.vtotal;
+       mode->vscan = timings.vscan;
+
+       if (timings.flags & TIMINGS_FLAGS_H_SYNC_POS)
+               mode->flags |= DRM_MODE_FLAG_PHSYNC;
+       else
+               mode->flags |= DRM_MODE_FLAG_NHSYNC;
+
+       if (timings.flags & TIMINGS_FLAGS_V_SYNC_POS)
+               mode->flags |= DRM_MODE_FLAG_PVSYNC;
+       else
+               mode->flags |= DRM_MODE_FLAG_NVSYNC;
+
+       if (timings.flags & TIMINGS_FLAGS_INTERLACE)
+               mode->flags |= DRM_MODE_FLAG_INTERLACE;
+
+       return 0;
+}
+
 static int vc4_fkms_get_edid_block(void *data, u8 *buf, unsigned int block,
                                   size_t len)
 {
@@ -1094,25 +1144,35 @@ static int vc4_fkms_connector_get_modes(struct drm_connector *connector)
                                        to_vc4_fkms_connector(connector);
        struct drm_encoder *encoder = fkms_connector->encoder;
        struct vc4_fkms_encoder *vc4_encoder = to_vc4_fkms_encoder(encoder);
-       int ret = 0;
+       struct drm_display_mode fw_mode;
+       struct drm_display_mode *mode;
        struct edid *edid;
+       int num_modes;
 
-       edid = drm_do_get_edid(connector, vc4_fkms_get_edid_block,
-                              fkms_connector);
+       if (!vc4_fkms_get_fw_mode(fkms_connector, &fw_mode)) {
+               drm_mode_debug_printmodeline(&fw_mode);
+               mode = drm_mode_duplicate(connector->dev,
+                                         &fw_mode);
+               drm_mode_probed_add(connector, mode);
+               num_modes = 1;  /* 1 mode */
+       } else {
+               edid = drm_do_get_edid(connector, vc4_fkms_get_edid_block,
+                                      fkms_connector);
 
-       /* FIXME: Can we do CEC?
-        * cec_s_phys_addr_from_edid(vc4->hdmi->cec_adap, edid);
-        * if (!edid)
-        *      return -ENODEV;
-        */
+               /* FIXME: Can we do CEC?
+                * cec_s_phys_addr_from_edid(vc4->hdmi->cec_adap, edid);
+                * if (!edid)
+                *      return -ENODEV;
+                */
 
-       vc4_encoder->hdmi_monitor = drm_detect_hdmi_monitor(edid);
+               vc4_encoder->hdmi_monitor = drm_detect_hdmi_monitor(edid);
 
-       drm_connector_update_edid_property(connector, edid);
-       ret = drm_add_edid_modes(connector, edid);
-       kfree(edid);
+               drm_connector_update_edid_property(connector, edid);
+               num_modes = drm_add_edid_modes(connector, edid);
+               kfree(edid);
+       }
 
-       return ret;
+       return num_modes;
 }
 
 /* This is the DSI panel resolution. Use this as a default should the firmware
@@ -1130,55 +1190,15 @@ static int vc4_fkms_lcd_connector_get_modes(struct drm_connector *connector)
 {
        struct vc4_fkms_connector *fkms_connector =
                                        to_vc4_fkms_connector(connector);
-       struct vc4_dev *vc4 = fkms_connector->vc4_dev;
        struct drm_display_mode *mode;
-       struct mailbox_set_mode mb = {
-               .tag1 = { RPI_FIRMWARE_GET_DISPLAY_TIMING,
-                         sizeof(struct set_timings), 0},
-               .timings = { .display = fkms_connector->display_number },
-       };
        struct drm_display_mode fw_mode;
-       int ret = 0;
-
-       ret = rpi_firmware_property_list(vc4->firmware, &mb, sizeof(mb));
-       if (!ret) {
-               /* Equivalent to DRM_MODE macro. */
-               memset(&fw_mode, 0, sizeof(fw_mode));
-               strncpy(fw_mode.name, "LCD_MODE", sizeof(fw_mode.name));
-               fw_mode.status = 0;
-               fw_mode.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
-               fw_mode.clock = mb.timings.clock;
-               fw_mode.hdisplay = mb.timings.hdisplay;
-               fw_mode.hsync_start = mb.timings.hsync_start;
-               fw_mode.hsync_end = mb.timings.hsync_end;
-               fw_mode.htotal = mb.timings.htotal;
-               fw_mode.hskew = 0;
-               fw_mode.vdisplay = mb.timings.vdisplay;
-               fw_mode.vsync_start = mb.timings.vsync_start;
-               fw_mode.vsync_end = mb.timings.vsync_end;
-               fw_mode.vtotal = mb.timings.vtotal;
-               fw_mode.vscan = mb.timings.vscan;
-               if (mb.timings.flags & TIMINGS_FLAGS_H_SYNC_POS)
-                       fw_mode.flags |= DRM_MODE_FLAG_PHSYNC;
-               else
-                       fw_mode.flags |= DRM_MODE_FLAG_NHSYNC;
-               if (mb.timings.flags & TIMINGS_FLAGS_V_SYNC_POS)
-                       fw_mode.flags |= DRM_MODE_FLAG_PVSYNC;
-               else
-                       fw_mode.flags |= DRM_MODE_FLAG_NVSYNC;
-               if (mb.timings.flags & TIMINGS_FLAGS_V_SYNC_POS)
-                       fw_mode.flags |= DRM_MODE_FLAG_PVSYNC;
-               else
-                       fw_mode.flags |= DRM_MODE_FLAG_NVSYNC;
-               if (mb.timings.flags & TIMINGS_FLAGS_INTERLACE)
-                       fw_mode.flags |= DRM_MODE_FLAG_INTERLACE;
 
+       if (!vc4_fkms_get_fw_mode(fkms_connector, &fw_mode) && fw_mode.clock)
                mode = drm_mode_duplicate(connector->dev,
                                          &fw_mode);
-       } else {
+       else
                mode = drm_mode_duplicate(connector->dev,
                                          &lcd_mode);
-       }
 
        if (!mode) {
                DRM_ERROR("Failed to create a new display mode\n");