1 From 52add140c76c0a211ab340ced8e7e1ea8bef9c79 Mon Sep 17 00:00:00 2001
2 From: Dave Stevenson <dave.stevenson@raspberrypi.org>
3 Date: Tue, 9 Apr 2019 18:23:41 +0100
4 Subject: [PATCH] drm: vc4: FKMS reads the EDID from fw, and supports
7 This extends FKMS to read the EDID from the display, and support
8 requesting a particular mode via KMS.
10 Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
12 drivers/gpu/drm/vc4/vc4_firmware_kms.c | 334 ++++++++++++++++++---
13 include/soc/bcm2835/raspberrypi-firmware.h | 2 +
14 2 files changed, 302 insertions(+), 34 deletions(-)
16 --- a/drivers/gpu/drm/vc4/vc4_firmware_kms.c
17 +++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c
18 @@ -91,11 +91,60 @@ struct mailbox_blank_display {
22 -struct mailbox_get_width_height {
23 +struct mailbox_get_edid {
24 struct rpi_firmware_property_tag_header tag1;
26 - struct rpi_firmware_property_tag_header tag2;
38 + u32 clock; /* in kHz */
59 +#define TIMINGS_FLAGS_H_SYNC_POS BIT(0)
60 +#define TIMINGS_FLAGS_H_SYNC_NEG 0
61 +#define TIMINGS_FLAGS_V_SYNC_POS BIT(1)
62 +#define TIMINGS_FLAGS_V_SYNC_NEG 0
64 +#define TIMINGS_FLAGS_ASPECT_MASK GENMASK(7, 4)
65 +#define TIMINGS_FLAGS_ASPECT_NONE (0 << 4)
66 +#define TIMINGS_FLAGS_ASPECT_4_3 (1 << 4)
67 +#define TIMINGS_FLAGS_ASPECT_16_9 (2 << 4)
68 +#define TIMINGS_FLAGS_ASPECT_64_27 (3 << 4)
69 +#define TIMINGS_FLAGS_ASPECT_256_135 (4 << 4)
71 +/* Limited range RGB flag. Not set corresponds to full range. */
72 +#define TIMINGS_FLAGS_RGB_LIMITED BIT(8)
73 +/* DVI monitor, therefore disable infoframes. Not set corresponds to HDMI. */
74 +#define TIMINGS_FLAGS_DVI BIT(9)
77 +struct mailbox_set_mode {
78 + struct rpi_firmware_property_tag_header tag1;
79 + struct set_timings timings;
82 static const struct vc_image_format {
83 @@ -189,6 +238,7 @@ struct vc4_crtc {
90 static inline struct vc4_crtc *to_vc4_crtc(struct drm_crtc *crtc)
91 @@ -198,6 +248,8 @@ static inline struct vc4_crtc *to_vc4_cr
93 struct vc4_fkms_encoder {
94 struct drm_encoder base;
96 + bool rgb_range_selectable;
99 static inline struct vc4_fkms_encoder *
100 @@ -215,7 +267,9 @@ struct vc4_fkms_connector {
103 struct drm_encoder *encoder;
105 + struct vc4_dev *vc4_dev;
106 + u32 display_number;
110 static inline struct vc4_fkms_connector *
111 @@ -224,6 +278,26 @@ to_vc4_fkms_connector(struct drm_connect
112 return container_of(connector, struct vc4_fkms_connector, base);
115 +static u32 vc4_get_display_type(u32 display_number)
117 + const u32 display_types[] = {
118 + /* The firmware display (DispmanX) IDs map to specific types in
121 + DRM_MODE_ENCODER_DSI, /* MAIN_LCD */
122 + DRM_MODE_ENCODER_DSI, /* AUX_LCD */
123 + DRM_MODE_ENCODER_TMDS, /* HDMI0 */
124 + DRM_MODE_ENCODER_TVDAC, /* VEC */
125 + DRM_MODE_ENCODER_NONE, /* FORCE_LCD */
126 + DRM_MODE_ENCODER_NONE, /* FORCE_TV */
127 + DRM_MODE_ENCODER_NONE, /* FORCE_OTHER */
128 + DRM_MODE_ENCODER_TMDS, /* HDMI1 */
129 + DRM_MODE_ENCODER_NONE, /* FORCE_TV2 */
131 + return display_number > ARRAY_SIZE(display_types) - 1 ?
132 + DRM_MODE_ENCODER_NONE : display_types[display_number];
135 /* Firmware's structure for making an FB mbox call. */
137 u32 xres, yres, xres_virtual, yres_virtual;
138 @@ -258,10 +332,15 @@ static int vc4_plane_set_blank(struct dr
139 .plane_id = vc4_plane->mb.plane.plane_id,
142 + static const char * const plane_types[] = {
149 - DRM_DEBUG_ATOMIC("[PLANE:%d:%s] overlay plane %s",
150 - plane->base.id, plane->name,
151 + DRM_DEBUG_ATOMIC("[PLANE:%d:%s] %s plane %s",
152 + plane->base.id, plane->name, plane_types[plane->type],
153 blank ? "blank" : "unblank");
156 @@ -595,13 +674,102 @@ fail:
158 static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
160 - /* Everyting is handled in the planes. */
161 + struct drm_device *dev = crtc->dev;
162 + struct vc4_dev *vc4 = to_vc4_dev(dev);
163 + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
164 + struct drm_display_mode *mode = &crtc->state->adjusted_mode;
165 + struct vc4_fkms_encoder *vc4_encoder =
166 + to_vc4_fkms_encoder(vc4_crtc->encoder);
167 + struct mailbox_set_mode mb = {
168 + .tag1 = { RPI_FIRMWARE_SET_TIMING,
169 + sizeof(struct set_timings), 0},
171 + union hdmi_infoframe frame;
174 + ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, vc4_crtc->connector, mode);
176 + DRM_ERROR("couldn't fill AVI infoframe\n");
180 + 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",
181 + vc4_crtc->display_number, mode->name, mode->clock,
182 + mode->hdisplay, mode->hsync_start, mode->hsync_end,
183 + mode->htotal, mode->hskew, mode->vdisplay,
184 + mode->vsync_start, mode->vsync_end, mode->vtotal,
185 + mode->vscan, mode->vrefresh, mode->picture_aspect_ratio);
186 + mb.timings.display = vc4_crtc->display_number;
188 + mb.timings.video_id_code = frame.avi.video_code;
190 + mb.timings.clock = mode->clock;
191 + mb.timings.hdisplay = mode->hdisplay;
192 + mb.timings.hsync_start = mode->hsync_start;
193 + mb.timings.hsync_end = mode->hsync_end;
194 + mb.timings.htotal = mode->htotal;
195 + mb.timings.hskew = mode->hskew;
196 + mb.timings.vdisplay = mode->vdisplay;
197 + mb.timings.vsync_start = mode->vsync_start;
198 + mb.timings.vsync_end = mode->vsync_end;
199 + mb.timings.vtotal = mode->vtotal;
200 + mb.timings.vscan = mode->vscan;
201 + mb.timings.vrefresh = 0;
202 + mb.timings.flags = 0;
203 + if (mode->flags & DRM_MODE_FLAG_PHSYNC)
204 + mb.timings.flags |= TIMINGS_FLAGS_H_SYNC_POS;
205 + if (mode->flags & DRM_MODE_FLAG_PVSYNC)
206 + mb.timings.flags |= TIMINGS_FLAGS_V_SYNC_POS;
208 + switch (frame.avi.picture_aspect) {
210 + case HDMI_PICTURE_ASPECT_NONE:
211 + mode->flags |= TIMINGS_FLAGS_ASPECT_NONE;
213 + case HDMI_PICTURE_ASPECT_4_3:
214 + mode->flags |= TIMINGS_FLAGS_ASPECT_4_3;
216 + case HDMI_PICTURE_ASPECT_16_9:
217 + mode->flags |= TIMINGS_FLAGS_ASPECT_16_9;
219 + case HDMI_PICTURE_ASPECT_64_27:
220 + mode->flags |= TIMINGS_FLAGS_ASPECT_64_27;
222 + case HDMI_PICTURE_ASPECT_256_135:
223 + mode->flags |= TIMINGS_FLAGS_ASPECT_256_135;
227 + if (!vc4_encoder->hdmi_monitor)
228 + mb.timings.flags |= TIMINGS_FLAGS_DVI;
229 + else if (drm_default_rgb_quant_range(mode) ==
230 + HDMI_QUANTIZATION_RANGE_LIMITED)
231 + mb.timings.flags |= TIMINGS_FLAGS_RGB_LIMITED;
234 + FIXME: To implement
235 + switch(mode->flag & DRM_MODE_FLAG_3D_MASK) {
236 + case DRM_MODE_FLAG_3D_NONE:
237 + case DRM_MODE_FLAG_3D_FRAME_PACKING:
238 + case DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE:
239 + case DRM_MODE_FLAG_3D_LINE_ALTERNATIVE:
240 + case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL:
241 + case DRM_MODE_FLAG_3D_L_DEPTH:
242 + case DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH:
243 + case DRM_MODE_FLAG_3D_TOP_AND_BOTTOM:
244 + case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF:
248 + ret = rpi_firmware_property_list(vc4->firmware, &mb, sizeof(mb));
251 static void vc4_crtc_disable(struct drm_crtc *crtc, struct drm_crtc_state *old_state)
253 struct drm_plane *plane;
255 + DRM_DEBUG_KMS("[CRTC:%d] vblanks off.\n",
257 drm_crtc_vblank_off(crtc);
259 /* Always turn the planes off on CRTC disable. In DRM, planes
260 @@ -619,6 +787,8 @@ static void vc4_crtc_enable(struct drm_c
262 struct drm_plane *plane;
264 + DRM_DEBUG_KMS("[CRTC:%d] vblanks on.\n",
266 drm_crtc_vblank_on(crtc);
268 /* Unblank the planes (if they're supposed to be displayed). */
269 @@ -637,12 +807,20 @@ vc4_crtc_mode_valid(struct drm_crtc *crt
270 return MODE_NO_DBLESCAN;
273 + /* Limit the pixel clock until we can get dynamic HDMI 2.0 scrambling
276 + if (mode->clock > 340000)
277 + return MODE_CLOCK_HIGH;
282 static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
283 struct drm_crtc_state *state)
285 + DRM_DEBUG_KMS("[CRTC:%d] crtc_atomic_check.\n",
290 @@ -652,6 +830,8 @@ static void vc4_crtc_atomic_flush(struct
291 struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
292 struct drm_device *dev = crtc->dev;
294 + DRM_DEBUG_KMS("[CRTC:%d] crtc_atomic_flush.\n",
296 if (crtc->state->event) {
299 @@ -719,6 +899,8 @@ static int vc4_fkms_enable_vblank(struct
301 struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
303 + DRM_DEBUG_KMS("[CRTC:%d] enable_vblank.\n",
305 vc4_crtc->vblank_enabled = true;
308 @@ -728,6 +910,8 @@ static void vc4_fkms_disable_vblank(stru
310 struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
312 + DRM_DEBUG_KMS("[CRTC:%d] disable_vblank.\n",
314 vc4_crtc->vblank_enabled = false;
317 @@ -762,36 +946,92 @@ static const struct of_device_id vc4_fir
318 static enum drm_connector_status
319 vc4_fkms_connector_detect(struct drm_connector *connector, bool force)
321 + DRM_DEBUG_KMS("connector detect.\n");
322 return connector_status_connected;
325 -static int vc4_fkms_connector_get_modes(struct drm_connector *connector)
326 +static int vc4_fkms_get_edid_block(void *data, u8 *buf, unsigned int block,
329 - struct drm_device *dev = connector->dev;
330 struct vc4_fkms_connector *fkms_connector =
331 - to_vc4_fkms_connector(connector);
332 - struct vc4_dev *vc4 = to_vc4_dev(dev);
333 - struct drm_display_mode *mode;
334 - struct mailbox_get_width_height wh = {
335 - .tag1 = {RPI_FIRMWARE_FRAMEBUFFER_SET_DISPLAY_NUM, 4, 0, },
336 - .display = fkms_connector->display_idx,
337 - .tag2 = { RPI_FIRMWARE_FRAMEBUFFER_GET_PHYSICAL_WIDTH_HEIGHT,
339 + (struct vc4_fkms_connector *)data;
340 + struct vc4_dev *vc4 = fkms_connector->vc4_dev;
341 + struct mailbox_get_edid mb = {
342 + .tag1 = { RPI_FIRMWARE_GET_EDID_BLOCK_DISPLAY,
345 + .display_number = fkms_connector->display_number,
350 + ret = rpi_firmware_property_list(vc4->firmware, &mb, sizeof(mb));
353 + memcpy(buf, mb.edid, len);
358 +static int vc4_fkms_connector_get_modes(struct drm_connector *connector)
360 + struct vc4_fkms_connector *fkms_connector =
361 + to_vc4_fkms_connector(connector);
362 + struct drm_encoder *encoder = fkms_connector->encoder;
363 + struct vc4_fkms_encoder *vc4_encoder = to_vc4_fkms_encoder(encoder);
367 + edid = drm_do_get_edid(connector, vc4_fkms_get_edid_block,
370 + /* FIXME: Can we do CEC?
371 + * cec_s_phys_addr_from_edid(vc4->hdmi->cec_adap, edid);
376 + vc4_encoder->hdmi_monitor = drm_detect_hdmi_monitor(edid);
378 - ret = rpi_firmware_property_list(vc4->firmware, &wh, sizeof(wh));
379 + if (edid && edid->input & DRM_EDID_INPUT_DIGITAL) {
380 + vc4_encoder->rgb_range_selectable =
381 + drm_rgb_quant_range_selectable(edid);
384 + drm_connector_update_edid_property(connector, edid);
385 + ret = drm_add_edid_modes(connector, edid);
391 +/* FIXME: Read LCD mode from the firmware. This is the DSI panel resolution. */
392 +static const struct drm_display_mode lcd_mode = {
393 + DRM_MODE("800x480", DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
395 + 800, 800 + 1, 800 + 1 + 2, 800 + 1 + 2 + 46, 0,
396 + 480, 480 + 7, 480 + 7 + 2, 480 + 7 + 2 + 21, 0,
397 + DRM_MODE_FLAG_INTERLACE)
400 +static int vc4_fkms_lcd_connector_get_modes(struct drm_connector *connector)
402 + //struct vc4_fkms_connector *fkms_connector =
403 + // to_vc4_fkms_connector(connector);
404 + //struct drm_encoder *encoder = fkms_connector->encoder;
405 + //struct vc4_fkms_encoder *vc4_encoder = to_vc4_fkms_encoder(encoder);
406 + struct drm_display_mode *mode;
410 - DRM_ERROR("Failed to get screen size: %d (0x%08x 0x%08x)\n",
411 - ret, wh.wh[0], wh.wh[1]);
413 + mode = drm_mode_duplicate(connector->dev,
416 + DRM_ERROR("Failed to create a new display mode\n");
420 - mode = drm_cvt_mode(dev, wh.wh[0], wh.wh[1], 60 /* vrefresh */,
422 drm_mode_probed_add(connector, mode);
424 + /* We have one mode */
428 @@ -800,11 +1040,14 @@ vc4_fkms_connector_best_encoder(struct d
430 struct vc4_fkms_connector *fkms_connector =
431 to_vc4_fkms_connector(connector);
432 + DRM_DEBUG_KMS("best_connector.\n");
433 return fkms_connector->encoder;
436 static void vc4_fkms_connector_destroy(struct drm_connector *connector)
438 + DRM_DEBUG_KMS("[CONNECTOR:%d] destroy.\n",
439 + connector->base.id);
440 drm_connector_unregister(connector);
441 drm_connector_cleanup(connector);
443 @@ -823,14 +1066,22 @@ static const struct drm_connector_helper
444 .best_encoder = vc4_fkms_connector_best_encoder,
447 +static const struct drm_connector_helper_funcs vc4_fkms_lcd_conn_helper_funcs = {
448 + .get_modes = vc4_fkms_lcd_connector_get_modes,
449 + .best_encoder = vc4_fkms_connector_best_encoder,
452 static struct drm_connector *
453 vc4_fkms_connector_init(struct drm_device *dev, struct drm_encoder *encoder,
457 struct drm_connector *connector = NULL;
458 struct vc4_fkms_connector *fkms_connector;
459 + struct vc4_dev *vc4_dev = to_vc4_dev(dev);
462 + DRM_DEBUG_KMS("connector_init, display_num %u\n", display_num);
464 fkms_connector = devm_kzalloc(dev->dev, sizeof(*fkms_connector),
466 if (!fkms_connector) {
467 @@ -840,11 +1091,21 @@ vc4_fkms_connector_init(struct drm_devic
468 connector = &fkms_connector->base;
470 fkms_connector->encoder = encoder;
471 - fkms_connector->display_idx = display_idx;
473 - drm_connector_init(dev, connector, &vc4_fkms_connector_funcs,
474 - DRM_MODE_CONNECTOR_HDMIA);
475 - drm_connector_helper_add(connector, &vc4_fkms_connector_helper_funcs);
476 + fkms_connector->display_number = display_num;
477 + fkms_connector->display_type = vc4_get_display_type(display_num);
478 + fkms_connector->vc4_dev = vc4_dev;
480 + if (fkms_connector->display_type == DRM_MODE_ENCODER_DSI) {
481 + drm_connector_init(dev, connector, &vc4_fkms_connector_funcs,
482 + DRM_MODE_CONNECTOR_DSI);
483 + drm_connector_helper_add(connector,
484 + &vc4_fkms_lcd_conn_helper_funcs);
486 + drm_connector_init(dev, connector, &vc4_fkms_connector_funcs,
487 + DRM_MODE_CONNECTOR_HDMIA);
488 + drm_connector_helper_add(connector,
489 + &vc4_fkms_connector_helper_funcs);
492 connector->polled = (DRM_CONNECTOR_POLL_CONNECT |
493 DRM_CONNECTOR_POLL_DISCONNECT);
494 @@ -865,6 +1126,7 @@ vc4_fkms_connector_init(struct drm_devic
496 static void vc4_fkms_encoder_destroy(struct drm_encoder *encoder)
498 + DRM_DEBUG_KMS("Encoder_destroy\n");
499 drm_encoder_cleanup(encoder);
502 @@ -874,10 +1136,12 @@ static const struct drm_encoder_funcs vc
504 static void vc4_fkms_encoder_enable(struct drm_encoder *encoder)
506 + DRM_DEBUG_KMS("Encoder_enable\n");
509 static void vc4_fkms_encoder_disable(struct drm_encoder *encoder)
511 + DRM_DEBUG_KMS("Encoder_disable\n");
514 static const struct drm_encoder_helper_funcs vc4_fkms_encoder_helper_funcs = {
515 @@ -909,6 +1173,7 @@ static int vc4_fkms_create_screen(struct
516 crtc = &vc4_crtc->base;
518 vc4_crtc->display_number = display_ref;
519 + vc4_crtc->display_type = vc4_get_display_type(display_ref);
521 /* Blank the firmware provided framebuffer */
522 rpi_firmware_property_list(vc4->firmware, &blank, sizeof(blank));
523 @@ -952,13 +1217,14 @@ static int vc4_fkms_create_screen(struct
525 vc4_crtc->encoder = &vc4_encoder->base;
526 vc4_encoder->base.possible_crtcs |= drm_crtc_mask(crtc) ;
528 drm_encoder_init(drm, &vc4_encoder->base, &vc4_fkms_encoder_funcs,
529 - DRM_MODE_ENCODER_TMDS, NULL);
530 + vc4_crtc->display_type, NULL);
531 drm_encoder_helper_add(&vc4_encoder->base,
532 &vc4_fkms_encoder_helper_funcs);
534 vc4_crtc->connector = vc4_fkms_connector_init(drm, &vc4_encoder->base,
537 if (IS_ERR(vc4_crtc->connector)) {
538 ret = PTR_ERR(vc4_crtc->connector);
539 goto err_destroy_encoder;
540 --- a/include/soc/bcm2835/raspberrypi-firmware.h
541 +++ b/include/soc/bcm2835/raspberrypi-firmware.h
542 @@ -75,6 +75,7 @@ enum rpi_firmware_property_tag {
543 RPI_FIRMWARE_GET_DISPMANX_RESOURCE_MEM_HANDLE = 0x00030014,
544 RPI_FIRMWARE_GET_EDID_BLOCK = 0x00030020,
545 RPI_FIRMWARE_GET_CUSTOMER_OTP = 0x00030021,
546 + RPI_FIRMWARE_GET_EDID_BLOCK_DISPLAY = 0x00030023,
547 RPI_FIRMWARE_GET_DOMAIN_STATE = 0x00030030,
548 RPI_FIRMWARE_GET_THROTTLED = 0x00030046,
549 RPI_FIRMWARE_GET_CLOCK_MEASURED = 0x00030047,
550 @@ -149,6 +150,7 @@ enum rpi_firmware_property_tag {
551 RPI_FIRMWARE_VCHIQ_INIT = 0x00048010,
553 RPI_FIRMWARE_SET_PLANE = 0x00048015,
554 + RPI_FIRMWARE_SET_TIMING = 0x00048017,
556 RPI_FIRMWARE_GET_COMMAND_LINE = 0x00050001,
557 RPI_FIRMWARE_GET_DMA_CHANNELS = 0x00060001,