brcm2708: organize kernel patches
[openwrt/openwrt.git] / target / linux / brcm2708 / patches-4.19 / 950-0559-drm-vc4-FKMS-reads-the-EDID-from-fw-and-supports-mod.patch
diff --git a/target/linux/brcm2708/patches-4.19/950-0559-drm-vc4-FKMS-reads-the-EDID-from-fw-and-supports-mod.patch b/target/linux/brcm2708/patches-4.19/950-0559-drm-vc4-FKMS-reads-the-EDID-from-fw-and-supports-mod.patch
new file mode 100644 (file)
index 0000000..db83c12
--- /dev/null
@@ -0,0 +1,557 @@
+From 9536044338d9c341e805e288a58090c49a793638 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.org>
+Date: Tue, 9 Apr 2019 18:23:41 +0100
+Subject: [PATCH] drm: vc4: FKMS reads the EDID from fw, and supports
+ mode setting
+
+This extends FKMS to read the EDID from the display, and support
+requesting a particular mode via KMS.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
+---
+ drivers/gpu/drm/vc4/vc4_firmware_kms.c     | 334 ++++++++++++++++++---
+ include/soc/bcm2835/raspberrypi-firmware.h |   2 +
+ 2 files changed, 302 insertions(+), 34 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_firmware_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c
+@@ -88,11 +88,60 @@ struct mailbox_blank_display {
+       u32 blank;
+ };
+-struct mailbox_get_width_height {
++struct mailbox_get_edid {
+       struct rpi_firmware_property_tag_header tag1;
+-      u32 display;
+-      struct rpi_firmware_property_tag_header tag2;
+-      u32 wh[2];
++      u32 block;
++      u32 display_number;
++      u8 edid[128];
++};
++
++struct set_timings {
++      u8 display;
++      u8 padding;
++      u16 video_id_code;
++
++      u32 clock;              /* in kHz */
++
++      u16 hdisplay;
++      u16 hsync_start;
++
++      u16 hsync_end;
++      u16 htotal;
++
++      u16 hskew;
++      u16 vdisplay;
++
++      u16 vsync_start;
++      u16 vsync_end;
++
++      u16 vtotal;
++      u16 vscan;
++
++      u16 vrefresh;
++      u16 padding2;
++
++      u32 flags;
++#define  TIMINGS_FLAGS_H_SYNC_POS     BIT(0)
++#define  TIMINGS_FLAGS_H_SYNC_NEG     0
++#define  TIMINGS_FLAGS_V_SYNC_POS     BIT(1)
++#define  TIMINGS_FLAGS_V_SYNC_NEG     0
++
++#define TIMINGS_FLAGS_ASPECT_MASK     GENMASK(7, 4)
++#define TIMINGS_FLAGS_ASPECT_NONE     (0 << 4)
++#define TIMINGS_FLAGS_ASPECT_4_3      (1 << 4)
++#define TIMINGS_FLAGS_ASPECT_16_9     (2 << 4)
++#define TIMINGS_FLAGS_ASPECT_64_27    (3 << 4)
++#define TIMINGS_FLAGS_ASPECT_256_135  (4 << 4)
++
++/* Limited range RGB flag. Not set corresponds to full range. */
++#define TIMINGS_FLAGS_RGB_LIMITED     BIT(8)
++/* DVI monitor, therefore disable infoframes. Not set corresponds to HDMI. */
++#define TIMINGS_FLAGS_DVI             BIT(9)
++};
++
++struct mailbox_set_mode {
++      struct rpi_firmware_property_tag_header tag1;
++      struct set_timings timings;
+ };
+ static const struct vc_image_format {
+@@ -186,6 +235,7 @@ struct vc4_crtc {
+       u32 overscan[4];
+       bool vblank_enabled;
+       u32 display_number;
++      u32 display_type;
+ };
+ static inline struct vc4_crtc *to_vc4_crtc(struct drm_crtc *crtc)
+@@ -195,6 +245,8 @@ static inline struct vc4_crtc *to_vc4_cr
+ struct vc4_fkms_encoder {
+       struct drm_encoder base;
++      bool hdmi_monitor;
++      bool rgb_range_selectable;
+ };
+ static inline struct vc4_fkms_encoder *
+@@ -212,7 +264,9 @@ struct vc4_fkms_connector {
+        * hook.
+        */
+       struct drm_encoder *encoder;
+-      u32 display_idx;
++      struct vc4_dev *vc4_dev;
++      u32 display_number;
++      u32 display_type;
+ };
+ static inline struct vc4_fkms_connector *
+@@ -221,6 +275,26 @@ to_vc4_fkms_connector(struct drm_connect
+       return container_of(connector, struct vc4_fkms_connector, base);
+ }
++static u32 vc4_get_display_type(u32 display_number)
++{
++      const u32 display_types[] = {
++              /* The firmware display (DispmanX) IDs map to specific types in
++               * a fixed manner.
++               */
++              DRM_MODE_ENCODER_DSI,   /* MAIN_LCD */
++              DRM_MODE_ENCODER_DSI,   /* AUX_LCD */
++              DRM_MODE_ENCODER_TMDS,  /* HDMI0 */
++              DRM_MODE_ENCODER_TVDAC, /* VEC */
++              DRM_MODE_ENCODER_NONE,  /* FORCE_LCD */
++              DRM_MODE_ENCODER_NONE,  /* FORCE_TV */
++              DRM_MODE_ENCODER_NONE,  /* FORCE_OTHER */
++              DRM_MODE_ENCODER_TMDS,  /* HDMI1 */
++              DRM_MODE_ENCODER_NONE,  /* FORCE_TV2 */
++      };
++      return display_number > ARRAY_SIZE(display_types) - 1 ?
++                      DRM_MODE_ENCODER_NONE : display_types[display_number];
++}
++
+ /* Firmware's structure for making an FB mbox call. */
+ struct fbinfo_s {
+       u32 xres, yres, xres_virtual, yres_virtual;
+@@ -255,10 +329,15 @@ static int vc4_plane_set_blank(struct dr
+                       .plane_id = vc4_plane->mb.plane.plane_id,
+               }
+       };
++      static const char * const plane_types[] = {
++                                                      "overlay",
++                                                      "primary",
++                                                      "cursor"
++                                                };
+       int ret;
+-      DRM_DEBUG_ATOMIC("[PLANE:%d:%s] overlay plane %s",
+-                       plane->base.id, plane->name,
++      DRM_DEBUG_ATOMIC("[PLANE:%d:%s] %s plane %s",
++                       plane->base.id, plane->name, plane_types[plane->type],
+                        blank ? "blank" : "unblank");
+       if (blank)
+@@ -593,13 +672,102 @@ fail:
+ static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
+ {
+-      /* Everyting is handled in the planes. */
++      struct drm_device *dev = crtc->dev;
++      struct vc4_dev *vc4 = to_vc4_dev(dev);
++      struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
++      struct drm_display_mode *mode = &crtc->state->adjusted_mode;
++      struct vc4_fkms_encoder *vc4_encoder =
++                                      to_vc4_fkms_encoder(vc4_crtc->encoder);
++      struct mailbox_set_mode mb = {
++              .tag1 = { RPI_FIRMWARE_SET_TIMING,
++                        sizeof(struct set_timings), 0},
++      };
++      union hdmi_infoframe frame;
++      int ret;
++
++      ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, mode, false);
++      if (ret < 0) {
++              DRM_ERROR("couldn't fill AVI infoframe\n");
++              return;
++      }
++
++      DRM_DEBUG_KMS("Setting mode for display num %u mode name %s, clk %d, h(disp %d, start %d, end %d, total %d, skew %d) v(disp %d, start %d, end %d, total %d, scan %d), vrefresh %d, par %u\n",
++                    vc4_crtc->display_number, mode->name, mode->clock,
++                    mode->hdisplay, mode->hsync_start, mode->hsync_end,
++                    mode->htotal, mode->hskew, mode->vdisplay,
++                    mode->vsync_start, mode->vsync_end, mode->vtotal,
++                    mode->vscan, mode->vrefresh, mode->picture_aspect_ratio);
++      mb.timings.display = vc4_crtc->display_number;
++
++      mb.timings.video_id_code = frame.avi.video_code;
++
++      mb.timings.clock = mode->clock;
++      mb.timings.hdisplay = mode->hdisplay;
++      mb.timings.hsync_start = mode->hsync_start;
++      mb.timings.hsync_end = mode->hsync_end;
++      mb.timings.htotal = mode->htotal;
++      mb.timings.hskew = mode->hskew;
++      mb.timings.vdisplay = mode->vdisplay;
++      mb.timings.vsync_start = mode->vsync_start;
++      mb.timings.vsync_end = mode->vsync_end;
++      mb.timings.vtotal = mode->vtotal;
++      mb.timings.vscan = mode->vscan;
++      mb.timings.vrefresh = 0;
++      mb.timings.flags = 0;
++      if (mode->flags & DRM_MODE_FLAG_PHSYNC)
++              mb.timings.flags |= TIMINGS_FLAGS_H_SYNC_POS;
++      if (mode->flags & DRM_MODE_FLAG_PVSYNC)
++              mb.timings.flags |= TIMINGS_FLAGS_V_SYNC_POS;
++
++      switch (frame.avi.picture_aspect) {
++      default:
++      case HDMI_PICTURE_ASPECT_NONE:
++              mode->flags |= TIMINGS_FLAGS_ASPECT_NONE;
++              break;
++      case HDMI_PICTURE_ASPECT_4_3:
++              mode->flags |= TIMINGS_FLAGS_ASPECT_4_3;
++              break;
++      case HDMI_PICTURE_ASPECT_16_9:
++              mode->flags |= TIMINGS_FLAGS_ASPECT_16_9;
++              break;
++      case HDMI_PICTURE_ASPECT_64_27:
++              mode->flags |= TIMINGS_FLAGS_ASPECT_64_27;
++              break;
++      case HDMI_PICTURE_ASPECT_256_135:
++              mode->flags |= TIMINGS_FLAGS_ASPECT_256_135;
++              break;
++      }
++
++      if (!vc4_encoder->hdmi_monitor)
++              mb.timings.flags |= TIMINGS_FLAGS_DVI;
++      else if (drm_default_rgb_quant_range(mode) ==
++                                      HDMI_QUANTIZATION_RANGE_LIMITED)
++              mb.timings.flags |= TIMINGS_FLAGS_RGB_LIMITED;
++
++      /*
++      FIXME: To implement
++      switch(mode->flag & DRM_MODE_FLAG_3D_MASK) {
++      case DRM_MODE_FLAG_3D_NONE:
++      case DRM_MODE_FLAG_3D_FRAME_PACKING:
++      case DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE:
++      case DRM_MODE_FLAG_3D_LINE_ALTERNATIVE:
++      case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL:
++      case DRM_MODE_FLAG_3D_L_DEPTH:
++      case DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH:
++      case DRM_MODE_FLAG_3D_TOP_AND_BOTTOM:
++      case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF:
++      }
++      */
++
++      ret = rpi_firmware_property_list(vc4->firmware, &mb, sizeof(mb));
+ }
+ static void vc4_crtc_disable(struct drm_crtc *crtc, struct drm_crtc_state *old_state)
+ {
+       struct drm_plane *plane;
++      DRM_DEBUG_KMS("[CRTC:%d] vblanks off.\n",
++                    crtc->base.id);
+       drm_crtc_vblank_off(crtc);
+       /* Always turn the planes off on CRTC disable. In DRM, planes
+@@ -617,6 +785,8 @@ static void vc4_crtc_enable(struct drm_c
+ {
+       struct drm_plane *plane;
++      DRM_DEBUG_KMS("[CRTC:%d] vblanks on.\n",
++                    crtc->base.id);
+       drm_crtc_vblank_on(crtc);
+       /* Unblank the planes (if they're supposed to be displayed). */
+@@ -635,12 +805,20 @@ vc4_crtc_mode_valid(struct drm_crtc *crt
+               return MODE_NO_DBLESCAN;
+       }
++      /* Limit the pixel clock until we can get dynamic HDMI 2.0 scrambling
++       * working.
++       */
++      if (mode->clock > 340000)
++              return MODE_CLOCK_HIGH;
++
+       return MODE_OK;
+ }
+ static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
+                                struct drm_crtc_state *state)
+ {
++      DRM_DEBUG_KMS("[CRTC:%d] crtc_atomic_check.\n",
++                    crtc->base.id);
+       return 0;
+ }
+@@ -650,6 +828,8 @@ static void vc4_crtc_atomic_flush(struct
+       struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+       struct drm_device *dev = crtc->dev;
++      DRM_DEBUG_KMS("[CRTC:%d] crtc_atomic_flush.\n",
++                    crtc->base.id);
+       if (crtc->state->event) {
+               unsigned long flags;
+@@ -717,6 +897,8 @@ static int vc4_fkms_enable_vblank(struct
+ {
+       struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
++      DRM_DEBUG_KMS("[CRTC:%d] enable_vblank.\n",
++                    crtc->base.id);
+       vc4_crtc->vblank_enabled = true;
+       return 0;
+@@ -726,6 +908,8 @@ static void vc4_fkms_disable_vblank(stru
+ {
+       struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
++      DRM_DEBUG_KMS("[CRTC:%d] disable_vblank.\n",
++                    crtc->base.id);
+       vc4_crtc->vblank_enabled = false;
+ }
+@@ -760,36 +944,92 @@ static const struct of_device_id vc4_fir
+ static enum drm_connector_status
+ vc4_fkms_connector_detect(struct drm_connector *connector, bool force)
+ {
++      DRM_DEBUG_KMS("connector detect.\n");
+       return connector_status_connected;
+ }
+-static int vc4_fkms_connector_get_modes(struct drm_connector *connector)
++static int vc4_fkms_get_edid_block(void *data, u8 *buf, unsigned int block,
++                                 size_t len)
+ {
+-      struct drm_device *dev = connector->dev;
+       struct vc4_fkms_connector *fkms_connector =
+-              to_vc4_fkms_connector(connector);
+-      struct vc4_dev *vc4 = to_vc4_dev(dev);
+-      struct drm_display_mode *mode;
+-      struct mailbox_get_width_height wh = {
+-              .tag1 = {RPI_FIRMWARE_FRAMEBUFFER_SET_DISPLAY_NUM, 4, 0, },
+-              .display = fkms_connector->display_idx,
+-              .tag2 = { RPI_FIRMWARE_FRAMEBUFFER_GET_PHYSICAL_WIDTH_HEIGHT,
+-                        8, 0, },
++                                      (struct vc4_fkms_connector *)data;
++      struct vc4_dev *vc4 = fkms_connector->vc4_dev;
++      struct mailbox_get_edid mb = {
++              .tag1 = { RPI_FIRMWARE_GET_EDID_BLOCK_DISPLAY,
++                        128 + 8, 0 },
++              .block = block,
++              .display_number = fkms_connector->display_number,
+       };
+-      int ret;
++      int ret = 0;
++
++      ret = rpi_firmware_property_list(vc4->firmware, &mb, sizeof(mb));
++
++      if (!ret)
++              memcpy(buf, mb.edid, len);
++
++      return ret;
++}
++
++static int vc4_fkms_connector_get_modes(struct drm_connector *connector)
++{
++      struct vc4_fkms_connector *fkms_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 edid *edid;
++
++      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;
++       */
++
++      vc4_encoder->hdmi_monitor = drm_detect_hdmi_monitor(edid);
+-      ret = rpi_firmware_property_list(vc4->firmware, &wh, sizeof(wh));
++      if (edid && edid->input & DRM_EDID_INPUT_DIGITAL) {
++              vc4_encoder->rgb_range_selectable =
++                      drm_rgb_quant_range_selectable(edid);
++      }
++
++      drm_connector_update_edid_property(connector, edid);
++      ret = drm_add_edid_modes(connector, edid);
++      kfree(edid);
++
++      return ret;
++}
++
++/* FIXME: Read LCD mode from the firmware. This is the DSI panel resolution. */
++static const struct drm_display_mode lcd_mode = {
++      DRM_MODE("800x480", DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
++               25979400 / 1000,
++               800, 800 + 1, 800 + 1 + 2, 800 + 1 + 2 + 46, 0,
++               480, 480 + 7, 480 + 7 + 2, 480 + 7 + 2 + 21, 0,
++               DRM_MODE_FLAG_INTERLACE)
++};
++
++static int vc4_fkms_lcd_connector_get_modes(struct drm_connector *connector)
++{
++      //struct vc4_fkms_connector *fkms_connector =
++      //                              to_vc4_fkms_connector(connector);
++      //struct drm_encoder *encoder = fkms_connector->encoder;
++      //struct vc4_fkms_encoder *vc4_encoder = to_vc4_fkms_encoder(encoder);
++      struct drm_display_mode *mode;
++      //int ret = 0;
+-      if (ret) {
+-              DRM_ERROR("Failed to get screen size: %d (0x%08x 0x%08x)\n",
+-                        ret, wh.wh[0], wh.wh[1]);
+-              return 0;
++      mode = drm_mode_duplicate(connector->dev,
++                                &lcd_mode);
++      if (!mode) {
++              DRM_ERROR("Failed to create a new display mode\n");
++              return -ENOMEM;
+       }
+-      mode = drm_cvt_mode(dev, wh.wh[0], wh.wh[1], 60 /* vrefresh */,
+-                          0, 0, false);
+       drm_mode_probed_add(connector, mode);
++      /* We have one mode */
+       return 1;
+ }
+@@ -798,11 +1038,14 @@ vc4_fkms_connector_best_encoder(struct d
+ {
+       struct vc4_fkms_connector *fkms_connector =
+               to_vc4_fkms_connector(connector);
++      DRM_DEBUG_KMS("best_connector.\n");
+       return fkms_connector->encoder;
+ }
+ static void vc4_fkms_connector_destroy(struct drm_connector *connector)
+ {
++      DRM_DEBUG_KMS("[CONNECTOR:%d] destroy.\n",
++                    connector->base.id);
+       drm_connector_unregister(connector);
+       drm_connector_cleanup(connector);
+ }
+@@ -821,14 +1064,22 @@ static const struct drm_connector_helper
+       .best_encoder = vc4_fkms_connector_best_encoder,
+ };
++static const struct drm_connector_helper_funcs vc4_fkms_lcd_conn_helper_funcs = {
++      .get_modes = vc4_fkms_lcd_connector_get_modes,
++      .best_encoder = vc4_fkms_connector_best_encoder,
++};
++
+ static struct drm_connector *
+ vc4_fkms_connector_init(struct drm_device *dev, struct drm_encoder *encoder,
+-                      u32 display_idx)
++                      u32 display_num)
+ {
+       struct drm_connector *connector = NULL;
+       struct vc4_fkms_connector *fkms_connector;
++      struct vc4_dev *vc4_dev = to_vc4_dev(dev);
+       int ret = 0;
++      DRM_DEBUG_KMS("connector_init, display_num %u\n", display_num);
++
+       fkms_connector = devm_kzalloc(dev->dev, sizeof(*fkms_connector),
+                                     GFP_KERNEL);
+       if (!fkms_connector) {
+@@ -838,11 +1089,21 @@ vc4_fkms_connector_init(struct drm_devic
+       connector = &fkms_connector->base;
+       fkms_connector->encoder = encoder;
+-      fkms_connector->display_idx = display_idx;
+-
+-      drm_connector_init(dev, connector, &vc4_fkms_connector_funcs,
+-                         DRM_MODE_CONNECTOR_HDMIA);
+-      drm_connector_helper_add(connector, &vc4_fkms_connector_helper_funcs);
++      fkms_connector->display_number = display_num;
++      fkms_connector->display_type = vc4_get_display_type(display_num);
++      fkms_connector->vc4_dev = vc4_dev;
++
++      if (fkms_connector->display_type == DRM_MODE_ENCODER_DSI) {
++              drm_connector_init(dev, connector, &vc4_fkms_connector_funcs,
++                                 DRM_MODE_CONNECTOR_DSI);
++              drm_connector_helper_add(connector,
++                                       &vc4_fkms_lcd_conn_helper_funcs);
++      } else {
++              drm_connector_init(dev, connector, &vc4_fkms_connector_funcs,
++                                 DRM_MODE_CONNECTOR_HDMIA);
++              drm_connector_helper_add(connector,
++                                       &vc4_fkms_connector_helper_funcs);
++      }
+       connector->polled = (DRM_CONNECTOR_POLL_CONNECT |
+                            DRM_CONNECTOR_POLL_DISCONNECT);
+@@ -863,6 +1124,7 @@ vc4_fkms_connector_init(struct drm_devic
+ static void vc4_fkms_encoder_destroy(struct drm_encoder *encoder)
+ {
++      DRM_DEBUG_KMS("Encoder_destroy\n");
+       drm_encoder_cleanup(encoder);
+ }
+@@ -872,10 +1134,12 @@ static const struct drm_encoder_funcs vc
+ static void vc4_fkms_encoder_enable(struct drm_encoder *encoder)
+ {
++      DRM_DEBUG_KMS("Encoder_enable\n");
+ }
+ static void vc4_fkms_encoder_disable(struct drm_encoder *encoder)
+ {
++      DRM_DEBUG_KMS("Encoder_disable\n");
+ }
+ static const struct drm_encoder_helper_funcs vc4_fkms_encoder_helper_funcs = {
+@@ -907,6 +1171,7 @@ static int vc4_fkms_create_screen(struct
+       crtc = &vc4_crtc->base;
+       vc4_crtc->display_number = display_ref;
++      vc4_crtc->display_type = vc4_get_display_type(display_ref);
+       /* Blank the firmware provided framebuffer */
+       rpi_firmware_property_list(vc4->firmware, &blank, sizeof(blank));
+@@ -950,13 +1215,14 @@ static int vc4_fkms_create_screen(struct
+               return -ENOMEM;
+       vc4_crtc->encoder = &vc4_encoder->base;
+       vc4_encoder->base.possible_crtcs |= drm_crtc_mask(crtc) ;
++
+       drm_encoder_init(drm, &vc4_encoder->base, &vc4_fkms_encoder_funcs,
+-                       DRM_MODE_ENCODER_TMDS, NULL);
++                       vc4_crtc->display_type, NULL);
+       drm_encoder_helper_add(&vc4_encoder->base,
+                              &vc4_fkms_encoder_helper_funcs);
+       vc4_crtc->connector = vc4_fkms_connector_init(drm, &vc4_encoder->base,
+-                                                    display_idx);
++                                                    display_ref);
+       if (IS_ERR(vc4_crtc->connector)) {
+               ret = PTR_ERR(vc4_crtc->connector);
+               goto err_destroy_encoder;
+--- a/include/soc/bcm2835/raspberrypi-firmware.h
++++ b/include/soc/bcm2835/raspberrypi-firmware.h
+@@ -78,6 +78,7 @@ enum rpi_firmware_property_tag {
+       RPI_FIRMWARE_GET_DISPMANX_RESOURCE_MEM_HANDLE =       0x00030014,
+       RPI_FIRMWARE_GET_EDID_BLOCK =                         0x00030020,
+       RPI_FIRMWARE_GET_CUSTOMER_OTP =                       0x00030021,
++      RPI_FIRMWARE_GET_EDID_BLOCK_DISPLAY =                 0x00030023,
+       RPI_FIRMWARE_GET_DOMAIN_STATE =                       0x00030030,
+       RPI_FIRMWARE_GET_THROTTLED =                          0x00030046,
+       RPI_FIRMWARE_GET_CLOCK_MEASURED =                     0x00030047,
+@@ -150,6 +151,7 @@ enum rpi_firmware_property_tag {
+       RPI_FIRMWARE_VCHIQ_INIT =                             0x00048010,
+       RPI_FIRMWARE_SET_PLANE =                              0x00048015,
++      RPI_FIRMWARE_SET_TIMING =                             0x00048017,
+       RPI_FIRMWARE_GET_COMMAND_LINE =                       0x00050001,
+       RPI_FIRMWARE_GET_DMA_CHANNELS =                       0x00060001,