drm: vc4: FKMS reads the EDID from fw, and supports mode setting
authorDave Stevenson <dave.stevenson@raspberrypi.org>
Tue, 9 Apr 2019 17:23:41 +0000 (18:23 +0100)
committerpopcornmix <popcornmix@gmail.com>
Wed, 1 Jul 2020 15:32:59 +0000 (16:32 +0100)
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
include/soc/bcm2835/raspberrypi-firmware.h

index 97ab24d..e4b978c 100644 (file)
@@ -91,11 +91,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 {
@@ -189,6 +238,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)
@@ -198,6 +248,8 @@ static inline struct vc4_crtc *to_vc4_crtc(struct drm_crtc *crtc)
 
 struct vc4_fkms_encoder {
        struct drm_encoder base;
+       bool hdmi_monitor;
+       bool rgb_range_selectable;
 };
 
 static inline struct vc4_fkms_encoder *
@@ -215,7 +267,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 *
@@ -224,6 +278,26 @@ to_vc4_fkms_connector(struct drm_connector *connector)
        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;
@@ -258,10 +332,15 @@ static int vc4_plane_set_blank(struct drm_plane *plane, bool blank)
                        .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)
@@ -595,13 +674,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, vc4_crtc->connector, mode);
+       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
@@ -619,6 +787,8 @@ static void vc4_crtc_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_st
 {
        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). */
@@ -637,12 +807,20 @@ vc4_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode)
                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;
 }
 
@@ -652,6 +830,8 @@ static void vc4_crtc_atomic_flush(struct drm_crtc *crtc,
        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;
 
@@ -719,6 +899,8 @@ static int vc4_fkms_enable_vblank(struct drm_crtc *crtc)
 {
        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;
@@ -728,6 +910,8 @@ static void vc4_fkms_disable_vblank(struct drm_crtc *crtc)
 {
        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;
 }
 
@@ -762,36 +946,92 @@ static const struct of_device_id vc4_firmware_kms_dt_match[] = {
 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, &wh, sizeof(wh));
+       ret = rpi_firmware_property_list(vc4->firmware, &mb, sizeof(mb));
 
-       if (ret) {
-               DRM_ERROR("Failed to get screen size: %d (0x%08x 0x%08x)\n",
-                         ret, wh.wh[0], wh.wh[1]);
-               return 0;
+       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);
+
+       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;
+
+       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;
 }
 
@@ -800,11 +1040,14 @@ vc4_fkms_connector_best_encoder(struct drm_connector *connector)
 {
        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);
 }
@@ -823,14 +1066,22 @@ static const struct drm_connector_helper_funcs vc4_fkms_connector_helper_funcs =
        .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) {
@@ -840,11 +1091,21 @@ vc4_fkms_connector_init(struct drm_device *dev, struct drm_encoder *encoder,
        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);
@@ -865,6 +1126,7 @@ vc4_fkms_connector_init(struct drm_device *dev, struct drm_encoder *encoder,
 
 static void vc4_fkms_encoder_destroy(struct drm_encoder *encoder)
 {
+       DRM_DEBUG_KMS("Encoder_destroy\n");
        drm_encoder_cleanup(encoder);
 }
 
@@ -874,10 +1136,12 @@ static const struct drm_encoder_funcs vc4_fkms_encoder_funcs = {
 
 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 = {
@@ -909,6 +1173,7 @@ static int vc4_fkms_create_screen(struct device *dev, struct drm_device *drm,
        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));
@@ -952,13 +1217,14 @@ static int vc4_fkms_create_screen(struct device *dev, struct drm_device *drm,
                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;
index 10f5dc5..1a5dd09 100644 (file)
@@ -75,6 +75,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,
@@ -149,6 +150,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,