aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWu Fengguang <fengguang.wu@intel.com>2011-09-05 02:23:20 -0400
committerKeith Packard <keithp@keithp.com>2011-09-21 17:52:41 -0400
commit76adaa34db407f174dd06370cb60f6029c33b465 (patch)
tree0dd9e80ba2a6e24eb3e9c61e94b71ee9d760cdab
parent578393cd1e6ebe2979664f11df630126c3f348a8 (diff)
drm: support routines for HDMI/DP ELD
ELD (EDID-Like Data) describes to the HDMI/DP audio driver the audio capabilities of the plugged monitor. This adds drm_edid_to_eld() for converting EDID to ELD. The converted ELD will be saved in a new drm_connector.eld[128] data field. This is necessary because the graphics driver will need to fixup some of the data fields (eg. HDMI/DP connection type, AV sync delay) before writing to the hardware ELD buffer. drm_av_sync_delay() will help the graphics drivers dynamically compute the AV sync delay for fixing-up the ELD. ELD selection policy: it's possible for one encoder to be associated with multiple connectors (ie. monitors), in which case the first found ELD will be returned by drm_select_eld(). This policy may not be suitable for all users, but let's start it simple first. The impact of ELD selection policy: assume there are two monitors, one supports stereo playback and the other has 8-channel output; cloned display mode is used, so that the two monitors are associated with the same internal encoder. If only the stereo playback capability is reported, the user won't be able to start 8-channel playback; if the 8-channel ELD is reported, then user space applications may send 8-channel samples down, however the user may actually be listening to the 2-channel monitor and not connecting speakers to the 8-channel monitor. According to James, many TVs will either refuse the display anything or pop-up an OSD warning whenever they receive hdmi audio which they cannot handle. Eventually we will require configurability and/or per-monitor audio control even when the video is cloned. CC: Zhao Yakui <yakui.zhao@intel.com> CC: Wang Zhenyu <zhenyu.z.wang@intel.com> CC: Jeremy Bush <contractfrombelow@gmail.com> CC: Christopher White <c.white@pulseforce.com> CC: Pierre-Louis Bossart <pierre-louis.bossart@intel.com> CC: Paul Menzel <paulepanter@users.sourceforge.net> CC: James Cloos <cloos@jhcloos.com> CC: Chris Wilson <chris@chris-wilson.co.uk> Signed-off-by: Ben Skeggs <bskeggs@redhat.com> Signed-off-by: Wu Fengguang <fengguang.wu@intel.com> Signed-off-by: Keith Packard <keithp@keithp.com>
-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__ */