diff options
Diffstat (limited to 'drivers/gpu/drm/drm_edid.c')
-rw-r--r-- | drivers/gpu/drm/drm_edid.c | 171 |
1 files changed, 171 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 | } |
1348 | EXPORT_SYMBOL(drm_find_cea_extension); | 1349 | EXPORT_SYMBOL(drm_find_cea_extension); |
1349 | 1350 | ||
1351 | static void | ||
1352 | parse_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 | |||
1381 | static void | ||
1382 | monitor_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 | */ | ||
1399 | void 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 | } | ||
1459 | EXPORT_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 | */ | ||
1466 | int 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 | } | ||
1497 | EXPORT_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 | */ | ||
1507 | struct 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 | } | ||
1519 | EXPORT_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 |