aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/drm/drm_edid.c171
-rw-r--r--include/drm/drm_crtc.h9
-rw-r--r--include/drm/drm_edid.h9
3 files changed, 189 insertions, 0 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 7425e5c9bd75..fe39c3570538 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -1319,6 +1319,7 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid,
1319#define HDMI_IDENTIFIER 0x000C03 1319#define HDMI_IDENTIFIER 0x000C03
1320#define AUDIO_BLOCK 0x01 1320#define AUDIO_BLOCK 0x01
1321#define VENDOR_BLOCK 0x03 1321#define VENDOR_BLOCK 0x03
1322#define SPEAKER_BLOCK 0x04
1322#define EDID_BASIC_AUDIO (1 << 6) 1323#define EDID_BASIC_AUDIO (1 << 6)
1323 1324
1324/** 1325/**
@@ -1347,6 +1348,176 @@ u8 *drm_find_cea_extension(struct edid *edid)
1347} 1348}
1348EXPORT_SYMBOL(drm_find_cea_extension); 1349EXPORT_SYMBOL(drm_find_cea_extension);
1349 1350
1351static void
1352parse_hdmi_vsdb(struct drm_connector *connector, uint8_t *db)
1353{
1354 connector->eld[5] |= (db[6] >> 7) << 1; /* Supports_AI */
1355
1356 connector->dvi_dual = db[6] & 1;
1357 connector->max_tmds_clock = db[7] * 5;
1358
1359 connector->latency_present[0] = db[8] >> 7;
1360 connector->latency_present[1] = (db[8] >> 6) & 1;
1361 connector->video_latency[0] = db[9];
1362 connector->audio_latency[0] = db[10];
1363 connector->video_latency[1] = db[11];
1364 connector->audio_latency[1] = db[12];
1365
1366 DRM_LOG_KMS("HDMI: DVI dual %d, "
1367 "max TMDS clock %d, "
1368 "latency present %d %d, "
1369 "video latency %d %d, "
1370 "audio latency %d %d\n",
1371 connector->dvi_dual,
1372 connector->max_tmds_clock,
1373 (int) connector->latency_present[0],
1374 (int) connector->latency_present[1],
1375 connector->video_latency[0],
1376 connector->video_latency[1],
1377 connector->audio_latency[0],
1378 connector->audio_latency[1]);
1379}
1380
1381static void
1382monitor_name(struct detailed_timing *t, void *data)
1383{
1384 if (t->data.other_data.type == EDID_DETAIL_MONITOR_NAME)
1385 *(u8 **)data = t->data.other_data.data.str.str;
1386}
1387
1388/**
1389 * drm_edid_to_eld - build ELD from EDID
1390 * @connector: connector corresponding to the HDMI/DP sink
1391 * @edid: EDID to parse
1392 *
1393 * Fill the ELD (EDID-Like Data) buffer for passing to the audio driver.
1394 * Some ELD fields are left to the graphics driver caller:
1395 * - Conn_Type
1396 * - HDCP
1397 * - Port_ID
1398 */
1399void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
1400{
1401 uint8_t *eld = connector->eld;
1402 u8 *cea;
1403 u8 *name;
1404 u8 *db;
1405 int sad_count = 0;
1406 int mnl;
1407 int dbl;
1408
1409 memset(eld, 0, sizeof(connector->eld));
1410
1411 cea = drm_find_cea_extension(edid);
1412 if (!cea) {
1413 DRM_DEBUG_KMS("ELD: no CEA Extension found\n");
1414 return;
1415 }
1416
1417 name = NULL;
1418 drm_for_each_detailed_block((u8 *)edid, monitor_name, &name);
1419 for (mnl = 0; name && mnl < 13; mnl++) {
1420 if (name[mnl] == 0x0a)
1421 break;
1422 eld[20 + mnl] = name[mnl];
1423 }
1424 eld[4] = (cea[1] << 5) | mnl;
1425 DRM_DEBUG_KMS("ELD monitor %s\n", eld + 20);
1426
1427 eld[0] = 2 << 3; /* ELD version: 2 */
1428
1429 eld[16] = edid->mfg_id[0];
1430 eld[17] = edid->mfg_id[1];
1431 eld[18] = edid->prod_code[0];
1432 eld[19] = edid->prod_code[1];
1433
1434 for (db = cea + 4; db < cea + cea[2]; db += dbl + 1) {
1435 dbl = db[0] & 0x1f;
1436
1437 switch ((db[0] & 0xe0) >> 5) {
1438 case AUDIO_BLOCK: /* Audio Data Block, contains SADs */
1439 sad_count = dbl / 3;
1440 memcpy(eld + 20 + mnl, &db[1], dbl);
1441 break;
1442 case SPEAKER_BLOCK: /* Speaker Allocation Data Block */
1443 eld[7] = db[1];
1444 break;
1445 case VENDOR_BLOCK:
1446 /* HDMI Vendor-Specific Data Block */
1447 if (db[1] == 0x03 && db[2] == 0x0c && db[3] == 0)
1448 parse_hdmi_vsdb(connector, db);
1449 break;
1450 default:
1451 break;
1452 }
1453 }
1454 eld[5] |= sad_count << 4;
1455 eld[2] = (20 + mnl + sad_count * 3 + 3) / 4;
1456
1457 DRM_DEBUG_KMS("ELD size %d, SAD count %d\n", (int)eld[2], sad_count);
1458}
1459EXPORT_SYMBOL(drm_edid_to_eld);
1460
1461/**
1462 * drm_av_sync_delay - HDMI/DP sink audio-video sync delay in millisecond
1463 * @connector: connector associated with the HDMI/DP sink
1464 * @mode: the display mode
1465 */
1466int drm_av_sync_delay(struct drm_connector *connector,
1467 struct drm_display_mode *mode)
1468{
1469 int i = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
1470 int a, v;
1471
1472 if (!connector->latency_present[0])
1473 return 0;
1474 if (!connector->latency_present[1])
1475 i = 0;
1476
1477 a = connector->audio_latency[i];
1478 v = connector->video_latency[i];
1479
1480 /*
1481 * HDMI/DP sink doesn't support audio or video?
1482 */
1483 if (a == 255 || v == 255)
1484 return 0;
1485
1486 /*
1487 * Convert raw EDID values to millisecond.
1488 * Treat unknown latency as 0ms.
1489 */
1490 if (a)
1491 a = min(2 * (a - 1), 500);
1492 if (v)
1493 v = min(2 * (v - 1), 500);
1494
1495 return max(v - a, 0);
1496}
1497EXPORT_SYMBOL(drm_av_sync_delay);
1498
1499/**
1500 * drm_select_eld - select one ELD from multiple HDMI/DP sinks
1501 * @encoder: the encoder just changed display mode
1502 * @mode: the adjusted display mode
1503 *
1504 * It's possible for one encoder to be associated with multiple HDMI/DP sinks.
1505 * The policy is now hard coded to simply use the first HDMI/DP sink's ELD.
1506 */
1507struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
1508 struct drm_display_mode *mode)
1509{
1510 struct drm_connector *connector;
1511 struct drm_device *dev = encoder->dev;
1512
1513 list_for_each_entry(connector, &dev->mode_config.connector_list, head)
1514 if (connector->encoder == encoder && connector->eld[0])
1515 return connector;
1516
1517 return NULL;
1518}
1519EXPORT_SYMBOL(drm_select_eld);
1520
1350/** 1521/**
1351 * drm_detect_hdmi_monitor - detect whether monitor is hdmi. 1522 * drm_detect_hdmi_monitor - detect whether monitor is hdmi.
1352 * @edid: monitor EDID information 1523 * @edid: monitor EDID information
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 44335e57eaaa..802079809282 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -466,6 +466,8 @@ enum drm_connector_force {
466/* DACs should rarely do this without a lot of testing */ 466/* DACs should rarely do this without a lot of testing */
467#define DRM_CONNECTOR_POLL_DISCONNECT (1 << 2) 467#define DRM_CONNECTOR_POLL_DISCONNECT (1 << 2)
468 468
469#define MAX_ELD_BYTES 128
470
469/** 471/**
470 * drm_connector - central DRM connector control structure 472 * drm_connector - central DRM connector control structure
471 * @crtc: CRTC this connector is currently connected to, NULL if none 473 * @crtc: CRTC this connector is currently connected to, NULL if none
@@ -523,6 +525,13 @@ struct drm_connector {
523 uint32_t force_encoder_id; 525 uint32_t force_encoder_id;
524 struct drm_encoder *encoder; /* currently active encoder */ 526 struct drm_encoder *encoder; /* currently active encoder */
525 527
528 /* EDID bits */
529 uint8_t eld[MAX_ELD_BYTES];
530 bool dvi_dual;
531 int max_tmds_clock; /* in MHz */
532 bool latency_present[2];
533 int video_latency[2]; /* [0]: progressive, [1]: interlaced */
534 int audio_latency[2];
526 int null_edid_counter; /* needed to workaround some HW bugs where we get all 0s */ 535 int null_edid_counter; /* needed to workaround some HW bugs where we get all 0s */
527}; 536};
528 537
diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
index eacb415b309a..74ce91684629 100644
--- a/include/drm/drm_edid.h
+++ b/include/drm/drm_edid.h
@@ -230,4 +230,13 @@ struct edid {
230 230
231#define EDID_PRODUCT_ID(e) ((e)->prod_code[0] | ((e)->prod_code[1] << 8)) 231#define EDID_PRODUCT_ID(e) ((e)->prod_code[0] | ((e)->prod_code[1] << 8))
232 232
233struct drm_encoder;
234struct drm_connector;
235struct drm_display_mode;
236void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid);
237int drm_av_sync_delay(struct drm_connector *connector,
238 struct drm_display_mode *mode);
239struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
240 struct drm_display_mode *mode);
241
233#endif /* __DRM_EDID_H__ */ 242#endif /* __DRM_EDID_H__ */