diff options
Diffstat (limited to 'drivers/gpu/drm/i915/intel_lspcon.c')
-rw-r--r-- | drivers/gpu/drm/i915/intel_lspcon.c | 347 |
1 files changed, 333 insertions, 14 deletions
diff --git a/drivers/gpu/drm/i915/intel_lspcon.c b/drivers/gpu/drm/i915/intel_lspcon.c index 3e085c5f2b81..96a8d9524b0c 100644 --- a/drivers/gpu/drm/i915/intel_lspcon.c +++ b/drivers/gpu/drm/i915/intel_lspcon.c | |||
@@ -27,6 +27,22 @@ | |||
27 | #include <drm/drm_dp_dual_mode_helper.h> | 27 | #include <drm/drm_dp_dual_mode_helper.h> |
28 | #include "intel_drv.h" | 28 | #include "intel_drv.h" |
29 | 29 | ||
30 | /* LSPCON OUI Vendor ID(signatures) */ | ||
31 | #define LSPCON_VENDOR_PARADE_OUI 0x001CF8 | ||
32 | #define LSPCON_VENDOR_MCA_OUI 0x0060AD | ||
33 | |||
34 | /* AUX addresses to write MCA AVI IF */ | ||
35 | #define LSPCON_MCA_AVI_IF_WRITE_OFFSET 0x5C0 | ||
36 | #define LSPCON_MCA_AVI_IF_CTRL 0x5DF | ||
37 | #define LSPCON_MCA_AVI_IF_KICKOFF (1 << 0) | ||
38 | #define LSPCON_MCA_AVI_IF_HANDLED (1 << 1) | ||
39 | |||
40 | /* AUX addresses to write Parade AVI IF */ | ||
41 | #define LSPCON_PARADE_AVI_IF_WRITE_OFFSET 0x516 | ||
42 | #define LSPCON_PARADE_AVI_IF_CTRL 0x51E | ||
43 | #define LSPCON_PARADE_AVI_IF_KICKOFF (1 << 7) | ||
44 | #define LSPCON_PARADE_AVI_IF_DATA_SIZE 32 | ||
45 | |||
30 | static struct intel_dp *lspcon_to_intel_dp(struct intel_lspcon *lspcon) | 46 | static struct intel_dp *lspcon_to_intel_dp(struct intel_lspcon *lspcon) |
31 | { | 47 | { |
32 | struct intel_digital_port *dig_port = | 48 | struct intel_digital_port *dig_port = |
@@ -50,6 +66,40 @@ static const char *lspcon_mode_name(enum drm_lspcon_mode mode) | |||
50 | } | 66 | } |
51 | } | 67 | } |
52 | 68 | ||
69 | static bool lspcon_detect_vendor(struct intel_lspcon *lspcon) | ||
70 | { | ||
71 | struct intel_dp *dp = lspcon_to_intel_dp(lspcon); | ||
72 | struct drm_dp_dpcd_ident *ident; | ||
73 | u32 vendor_oui; | ||
74 | |||
75 | if (drm_dp_read_desc(&dp->aux, &dp->desc, drm_dp_is_branch(dp->dpcd))) { | ||
76 | DRM_ERROR("Can't read description\n"); | ||
77 | return false; | ||
78 | } | ||
79 | |||
80 | ident = &dp->desc.ident; | ||
81 | vendor_oui = (ident->oui[0] << 16) | (ident->oui[1] << 8) | | ||
82 | ident->oui[2]; | ||
83 | |||
84 | switch (vendor_oui) { | ||
85 | case LSPCON_VENDOR_MCA_OUI: | ||
86 | lspcon->vendor = LSPCON_VENDOR_MCA; | ||
87 | DRM_DEBUG_KMS("Vendor: Mega Chips\n"); | ||
88 | break; | ||
89 | |||
90 | case LSPCON_VENDOR_PARADE_OUI: | ||
91 | lspcon->vendor = LSPCON_VENDOR_PARADE; | ||
92 | DRM_DEBUG_KMS("Vendor: Parade Tech\n"); | ||
93 | break; | ||
94 | |||
95 | default: | ||
96 | DRM_ERROR("Invalid/Unknown vendor OUI\n"); | ||
97 | return false; | ||
98 | } | ||
99 | |||
100 | return true; | ||
101 | } | ||
102 | |||
53 | static enum drm_lspcon_mode lspcon_get_current_mode(struct intel_lspcon *lspcon) | 103 | static enum drm_lspcon_mode lspcon_get_current_mode(struct intel_lspcon *lspcon) |
54 | { | 104 | { |
55 | enum drm_lspcon_mode current_mode; | 105 | enum drm_lspcon_mode current_mode; |
@@ -130,6 +180,21 @@ static bool lspcon_wake_native_aux_ch(struct intel_lspcon *lspcon) | |||
130 | return true; | 180 | return true; |
131 | } | 181 | } |
132 | 182 | ||
183 | void lspcon_ycbcr420_config(struct drm_connector *connector, | ||
184 | struct intel_crtc_state *crtc_state) | ||
185 | { | ||
186 | const struct drm_display_info *info = &connector->display_info; | ||
187 | const struct drm_display_mode *adjusted_mode = | ||
188 | &crtc_state->base.adjusted_mode; | ||
189 | |||
190 | if (drm_mode_is_420_only(info, adjusted_mode) && | ||
191 | connector->ycbcr_420_allowed) { | ||
192 | crtc_state->port_clock /= 2; | ||
193 | crtc_state->output_format = INTEL_OUTPUT_FORMAT_YCBCR444; | ||
194 | crtc_state->lspcon_downsampling = true; | ||
195 | } | ||
196 | } | ||
197 | |||
133 | static bool lspcon_probe(struct intel_lspcon *lspcon) | 198 | static bool lspcon_probe(struct intel_lspcon *lspcon) |
134 | { | 199 | { |
135 | int retry; | 200 | int retry; |
@@ -159,7 +224,18 @@ static bool lspcon_probe(struct intel_lspcon *lspcon) | |||
159 | /* Yay ... got a LSPCON device */ | 224 | /* Yay ... got a LSPCON device */ |
160 | DRM_DEBUG_KMS("LSPCON detected\n"); | 225 | DRM_DEBUG_KMS("LSPCON detected\n"); |
161 | lspcon->mode = lspcon_wait_mode(lspcon, expected_mode); | 226 | lspcon->mode = lspcon_wait_mode(lspcon, expected_mode); |
162 | lspcon->active = true; | 227 | |
228 | /* | ||
229 | * In the SW state machine, lets Put LSPCON in PCON mode only. | ||
230 | * In this way, it will work with both HDMI 1.4 sinks as well as HDMI | ||
231 | * 2.0 sinks. | ||
232 | */ | ||
233 | if (lspcon->mode != DRM_LSPCON_MODE_PCON) { | ||
234 | if (lspcon_change_mode(lspcon, DRM_LSPCON_MODE_PCON) < 0) { | ||
235 | DRM_ERROR("LSPCON mode change to PCON failed\n"); | ||
236 | return false; | ||
237 | } | ||
238 | } | ||
163 | return true; | 239 | return true; |
164 | } | 240 | } |
165 | 241 | ||
@@ -185,6 +261,255 @@ static void lspcon_resume_in_pcon_wa(struct intel_lspcon *lspcon) | |||
185 | DRM_DEBUG_KMS("LSPCON DP descriptor mismatch after resume\n"); | 261 | DRM_DEBUG_KMS("LSPCON DP descriptor mismatch after resume\n"); |
186 | } | 262 | } |
187 | 263 | ||
264 | static bool lspcon_parade_fw_ready(struct drm_dp_aux *aux) | ||
265 | { | ||
266 | u8 avi_if_ctrl; | ||
267 | u8 retry; | ||
268 | ssize_t ret; | ||
269 | |||
270 | /* Check if LSPCON FW is ready for data */ | ||
271 | for (retry = 0; retry < 5; retry++) { | ||
272 | if (retry) | ||
273 | usleep_range(200, 300); | ||
274 | |||
275 | ret = drm_dp_dpcd_read(aux, LSPCON_PARADE_AVI_IF_CTRL, | ||
276 | &avi_if_ctrl, 1); | ||
277 | if (ret < 0) { | ||
278 | DRM_ERROR("Failed to read AVI IF control\n"); | ||
279 | return false; | ||
280 | } | ||
281 | |||
282 | if ((avi_if_ctrl & LSPCON_PARADE_AVI_IF_KICKOFF) == 0) | ||
283 | return true; | ||
284 | } | ||
285 | |||
286 | DRM_ERROR("Parade FW not ready to accept AVI IF\n"); | ||
287 | return false; | ||
288 | } | ||
289 | |||
290 | static bool _lspcon_parade_write_infoframe_blocks(struct drm_dp_aux *aux, | ||
291 | uint8_t *avi_buf) | ||
292 | { | ||
293 | u8 avi_if_ctrl; | ||
294 | u8 block_count = 0; | ||
295 | u8 *data; | ||
296 | uint16_t reg; | ||
297 | ssize_t ret; | ||
298 | |||
299 | while (block_count < 4) { | ||
300 | if (!lspcon_parade_fw_ready(aux)) { | ||
301 | DRM_DEBUG_KMS("LSPCON FW not ready, block %d\n", | ||
302 | block_count); | ||
303 | return false; | ||
304 | } | ||
305 | |||
306 | reg = LSPCON_PARADE_AVI_IF_WRITE_OFFSET; | ||
307 | data = avi_buf + block_count * 8; | ||
308 | ret = drm_dp_dpcd_write(aux, reg, data, 8); | ||
309 | if (ret < 0) { | ||
310 | DRM_ERROR("Failed to write AVI IF block %d\n", | ||
311 | block_count); | ||
312 | return false; | ||
313 | } | ||
314 | |||
315 | /* | ||
316 | * Once a block of data is written, we have to inform the FW | ||
317 | * about this by writing into avi infoframe control register: | ||
318 | * - set the kickoff bit[7] to 1 | ||
319 | * - write the block no. to bits[1:0] | ||
320 | */ | ||
321 | reg = LSPCON_PARADE_AVI_IF_CTRL; | ||
322 | avi_if_ctrl = LSPCON_PARADE_AVI_IF_KICKOFF | block_count; | ||
323 | ret = drm_dp_dpcd_write(aux, reg, &avi_if_ctrl, 1); | ||
324 | if (ret < 0) { | ||
325 | DRM_ERROR("Failed to update (0x%x), block %d\n", | ||
326 | reg, block_count); | ||
327 | return false; | ||
328 | } | ||
329 | |||
330 | block_count++; | ||
331 | } | ||
332 | |||
333 | DRM_DEBUG_KMS("Wrote AVI IF blocks successfully\n"); | ||
334 | return true; | ||
335 | } | ||
336 | |||
337 | static bool _lspcon_write_avi_infoframe_parade(struct drm_dp_aux *aux, | ||
338 | const uint8_t *frame, | ||
339 | ssize_t len) | ||
340 | { | ||
341 | uint8_t avi_if[LSPCON_PARADE_AVI_IF_DATA_SIZE] = {1, }; | ||
342 | |||
343 | /* | ||
344 | * Parade's frames contains 32 bytes of data, divided | ||
345 | * into 4 frames: | ||
346 | * Token byte (first byte of first frame, must be non-zero) | ||
347 | * HB0 to HB2 from AVI IF (3 bytes header) | ||
348 | * PB0 to PB27 from AVI IF (28 bytes data) | ||
349 | * So it should look like this | ||
350 | * first block: | <token> <HB0-HB2> <DB0-DB3> | | ||
351 | * next 3 blocks: |<DB4-DB11>|<DB12-DB19>|<DB20-DB28>| | ||
352 | */ | ||
353 | |||
354 | if (len > LSPCON_PARADE_AVI_IF_DATA_SIZE - 1) { | ||
355 | DRM_ERROR("Invalid length of infoframes\n"); | ||
356 | return false; | ||
357 | } | ||
358 | |||
359 | memcpy(&avi_if[1], frame, len); | ||
360 | |||
361 | if (!_lspcon_parade_write_infoframe_blocks(aux, avi_if)) { | ||
362 | DRM_DEBUG_KMS("Failed to write infoframe blocks\n"); | ||
363 | return false; | ||
364 | } | ||
365 | |||
366 | return true; | ||
367 | } | ||
368 | |||
369 | static bool _lspcon_write_avi_infoframe_mca(struct drm_dp_aux *aux, | ||
370 | const uint8_t *buffer, ssize_t len) | ||
371 | { | ||
372 | int ret; | ||
373 | uint32_t val = 0; | ||
374 | uint32_t retry; | ||
375 | uint16_t reg; | ||
376 | const uint8_t *data = buffer; | ||
377 | |||
378 | reg = LSPCON_MCA_AVI_IF_WRITE_OFFSET; | ||
379 | while (val < len) { | ||
380 | /* DPCD write for AVI IF can fail on a slow FW day, so retry */ | ||
381 | for (retry = 0; retry < 5; retry++) { | ||
382 | ret = drm_dp_dpcd_write(aux, reg, (void *)data, 1); | ||
383 | if (ret == 1) { | ||
384 | break; | ||
385 | } else if (retry < 4) { | ||
386 | mdelay(50); | ||
387 | continue; | ||
388 | } else { | ||
389 | DRM_ERROR("DPCD write failed at:0x%x\n", reg); | ||
390 | return false; | ||
391 | } | ||
392 | } | ||
393 | val++; reg++; data++; | ||
394 | } | ||
395 | |||
396 | val = 0; | ||
397 | reg = LSPCON_MCA_AVI_IF_CTRL; | ||
398 | ret = drm_dp_dpcd_read(aux, reg, &val, 1); | ||
399 | if (ret < 0) { | ||
400 | DRM_ERROR("DPCD read failed, address 0x%x\n", reg); | ||
401 | return false; | ||
402 | } | ||
403 | |||
404 | /* Indicate LSPCON chip about infoframe, clear bit 1 and set bit 0 */ | ||
405 | val &= ~LSPCON_MCA_AVI_IF_HANDLED; | ||
406 | val |= LSPCON_MCA_AVI_IF_KICKOFF; | ||
407 | |||
408 | ret = drm_dp_dpcd_write(aux, reg, &val, 1); | ||
409 | if (ret < 0) { | ||
410 | DRM_ERROR("DPCD read failed, address 0x%x\n", reg); | ||
411 | return false; | ||
412 | } | ||
413 | |||
414 | val = 0; | ||
415 | ret = drm_dp_dpcd_read(aux, reg, &val, 1); | ||
416 | if (ret < 0) { | ||
417 | DRM_ERROR("DPCD read failed, address 0x%x\n", reg); | ||
418 | return false; | ||
419 | } | ||
420 | |||
421 | if (val == LSPCON_MCA_AVI_IF_HANDLED) | ||
422 | DRM_DEBUG_KMS("AVI IF handled by FW\n"); | ||
423 | |||
424 | return true; | ||
425 | } | ||
426 | |||
427 | void lspcon_write_infoframe(struct intel_encoder *encoder, | ||
428 | const struct intel_crtc_state *crtc_state, | ||
429 | unsigned int type, | ||
430 | const void *frame, ssize_t len) | ||
431 | { | ||
432 | bool ret; | ||
433 | struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); | ||
434 | struct intel_lspcon *lspcon = enc_to_intel_lspcon(&encoder->base); | ||
435 | |||
436 | /* LSPCON only needs AVI IF */ | ||
437 | if (type != HDMI_INFOFRAME_TYPE_AVI) | ||
438 | return; | ||
439 | |||
440 | if (lspcon->vendor == LSPCON_VENDOR_MCA) | ||
441 | ret = _lspcon_write_avi_infoframe_mca(&intel_dp->aux, | ||
442 | frame, len); | ||
443 | else | ||
444 | ret = _lspcon_write_avi_infoframe_parade(&intel_dp->aux, | ||
445 | frame, len); | ||
446 | |||
447 | if (!ret) { | ||
448 | DRM_ERROR("Failed to write AVI infoframes\n"); | ||
449 | return; | ||
450 | } | ||
451 | |||
452 | DRM_DEBUG_DRIVER("AVI infoframes updated successfully\n"); | ||
453 | } | ||
454 | |||
455 | void lspcon_set_infoframes(struct intel_encoder *encoder, | ||
456 | bool enable, | ||
457 | const struct intel_crtc_state *crtc_state, | ||
458 | const struct drm_connector_state *conn_state) | ||
459 | { | ||
460 | ssize_t ret; | ||
461 | union hdmi_infoframe frame; | ||
462 | uint8_t buf[VIDEO_DIP_DATA_SIZE]; | ||
463 | struct intel_digital_port *dig_port = enc_to_dig_port(&encoder->base); | ||
464 | struct intel_lspcon *lspcon = &dig_port->lspcon; | ||
465 | struct intel_dp *intel_dp = &dig_port->dp; | ||
466 | struct drm_connector *connector = &intel_dp->attached_connector->base; | ||
467 | const struct drm_display_mode *mode = &crtc_state->base.adjusted_mode; | ||
468 | bool is_hdmi2_sink = connector->display_info.hdmi.scdc.supported; | ||
469 | |||
470 | if (!lspcon->active) { | ||
471 | DRM_ERROR("Writing infoframes while LSPCON disabled ?\n"); | ||
472 | return; | ||
473 | } | ||
474 | |||
475 | ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, | ||
476 | mode, is_hdmi2_sink); | ||
477 | if (ret < 0) { | ||
478 | DRM_ERROR("couldn't fill AVI infoframe\n"); | ||
479 | return; | ||
480 | } | ||
481 | |||
482 | if (crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR444) { | ||
483 | if (crtc_state->lspcon_downsampling) | ||
484 | frame.avi.colorspace = HDMI_COLORSPACE_YUV420; | ||
485 | else | ||
486 | frame.avi.colorspace = HDMI_COLORSPACE_YUV444; | ||
487 | } else { | ||
488 | frame.avi.colorspace = HDMI_COLORSPACE_RGB; | ||
489 | } | ||
490 | |||
491 | drm_hdmi_avi_infoframe_quant_range(&frame.avi, mode, | ||
492 | crtc_state->limited_color_range ? | ||
493 | HDMI_QUANTIZATION_RANGE_LIMITED : | ||
494 | HDMI_QUANTIZATION_RANGE_FULL, | ||
495 | false, is_hdmi2_sink); | ||
496 | |||
497 | ret = hdmi_infoframe_pack(&frame, buf, sizeof(buf)); | ||
498 | if (ret < 0) { | ||
499 | DRM_ERROR("Failed to pack AVI IF\n"); | ||
500 | return; | ||
501 | } | ||
502 | |||
503 | dig_port->write_infoframe(encoder, crtc_state, HDMI_INFOFRAME_TYPE_AVI, | ||
504 | buf, ret); | ||
505 | } | ||
506 | |||
507 | bool lspcon_infoframe_enabled(struct intel_encoder *encoder, | ||
508 | const struct intel_crtc_state *pipe_config) | ||
509 | { | ||
510 | return enc_to_intel_lspcon(&encoder->base)->active; | ||
511 | } | ||
512 | |||
188 | void lspcon_resume(struct intel_lspcon *lspcon) | 513 | void lspcon_resume(struct intel_lspcon *lspcon) |
189 | { | 514 | { |
190 | enum drm_lspcon_mode expected_mode; | 515 | enum drm_lspcon_mode expected_mode; |
@@ -216,6 +541,7 @@ bool lspcon_init(struct intel_digital_port *intel_dig_port) | |||
216 | struct intel_lspcon *lspcon = &intel_dig_port->lspcon; | 541 | struct intel_lspcon *lspcon = &intel_dig_port->lspcon; |
217 | struct drm_device *dev = intel_dig_port->base.base.dev; | 542 | struct drm_device *dev = intel_dig_port->base.base.dev; |
218 | struct drm_i915_private *dev_priv = to_i915(dev); | 543 | struct drm_i915_private *dev_priv = to_i915(dev); |
544 | struct drm_connector *connector = &dp->attached_connector->base; | ||
219 | 545 | ||
220 | if (!HAS_LSPCON(dev_priv)) { | 546 | if (!HAS_LSPCON(dev_priv)) { |
221 | DRM_ERROR("LSPCON is not supported on this platform\n"); | 547 | DRM_ERROR("LSPCON is not supported on this platform\n"); |
@@ -230,25 +556,18 @@ bool lspcon_init(struct intel_digital_port *intel_dig_port) | |||
230 | return false; | 556 | return false; |
231 | } | 557 | } |
232 | 558 | ||
233 | /* | ||
234 | * In the SW state machine, lets Put LSPCON in PCON mode only. | ||
235 | * In this way, it will work with both HDMI 1.4 sinks as well as HDMI | ||
236 | * 2.0 sinks. | ||
237 | */ | ||
238 | if (lspcon->active && lspcon->mode != DRM_LSPCON_MODE_PCON) { | ||
239 | if (lspcon_change_mode(lspcon, DRM_LSPCON_MODE_PCON) < 0) { | ||
240 | DRM_ERROR("LSPCON mode change to PCON failed\n"); | ||
241 | return false; | ||
242 | } | ||
243 | } | ||
244 | |||
245 | if (!intel_dp_read_dpcd(dp)) { | 559 | if (!intel_dp_read_dpcd(dp)) { |
246 | DRM_ERROR("LSPCON DPCD read failed\n"); | 560 | DRM_ERROR("LSPCON DPCD read failed\n"); |
247 | return false; | 561 | return false; |
248 | } | 562 | } |
249 | 563 | ||
250 | drm_dp_read_desc(&dp->aux, &dp->desc, drm_dp_is_branch(dp->dpcd)); | 564 | if (!lspcon_detect_vendor(lspcon)) { |
565 | DRM_ERROR("LSPCON vendor detection failed\n"); | ||
566 | return false; | ||
567 | } | ||
251 | 568 | ||
569 | connector->ycbcr_420_allowed = true; | ||
570 | lspcon->active = true; | ||
252 | DRM_DEBUG_KMS("Success: LSPCON init\n"); | 571 | DRM_DEBUG_KMS("Success: LSPCON init\n"); |
253 | return true; | 572 | return true; |
254 | } | 573 | } |