Follow up on v2: http://lists.freedesktop.org/archives/dri-devel/2013-August/043604.html
With the quick and nice reviews from Ville and Simon, it's time for a v3: - Fix embarrassing hmdi typo - Fix the sending of vendor specific infoframes for side-by-side half modes - Smaller changes here and there.
This function is only used inside drm_edid.c.
Signed-off-by: Damien Lespiau damien.lespiau@intel.com Reviewed-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/drm_edid.c | 5 ++--- include/drm/drm_crtc.h | 1 - 2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index dfc7a1b..e014785 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2298,10 +2298,10 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid, #define EDID_CEA_YCRCB422 (1 << 4) #define EDID_CEA_VCDB_QS (1 << 6)
-/** +/* * Search EDID for CEA extension block. */ -u8 *drm_find_cea_extension(struct edid *edid) +static u8 *drm_find_cea_extension(struct edid *edid) { u8 *edid_ext = NULL; int i; @@ -2322,7 +2322,6 @@ u8 *drm_find_cea_extension(struct edid *edid)
return edid_ext; } -EXPORT_SYMBOL(drm_find_cea_extension);
/* * Calculate the alternate clock for the CEA mode diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index fa12a2f..f3ecc6f 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1050,7 +1050,6 @@ extern int drm_mode_gamma_get_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_mode_gamma_set_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern u8 *drm_find_cea_extension(struct edid *edid); extern u8 drm_match_cea_mode(const struct drm_display_mode *to_match); extern bool drm_detect_hdmi_monitor(struct edid *edid); extern bool drm_detect_monitor_audio(struct edid *edid);
A few styles issues have crept in here, fix them before touching this code again.
v2: constify arguments that can be (Ville Syrjälä) v3: constify, but better (Ville Syrjälä)
Signed-off-by: Damien Lespiau damien.lespiau@intel.com --- drivers/gpu/drm/drm_edid.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index e014785..bb25ee2 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2441,10 +2441,11 @@ add_alternate_cea_modes(struct drm_connector *connector, struct edid *edid) }
static int -do_cea_modes (struct drm_connector *connector, u8 *db, u8 len) +do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len) { struct drm_device *dev = connector->dev; - u8 * mode, cea_mode; + const u8 *mode; + u8 cea_mode; int modes = 0;
for (mode = db; mode < db + len; mode++) { @@ -2501,8 +2502,9 @@ cea_db_offsets(const u8 *cea, int *start, int *end) static int add_cea_modes(struct drm_connector *connector, struct edid *edid) { - u8 * cea = drm_find_cea_extension(edid); - u8 * db, dbl; + const u8 *cea = drm_find_cea_extension(edid); + const u8 *db; + u8 dbl; int modes = 0;
if (cea && cea_revision(cea) >= 3) { @@ -2516,7 +2518,7 @@ add_cea_modes(struct drm_connector *connector, struct edid *edid) dbl = cea_db_payload_len(db);
if (cea_db_tag(db) == VIDEO_BLOCK) - modes += do_cea_modes (connector, db+1, dbl); + modes += do_cea_modes(connector, db + 1, dbl); } }
HDMI 1.4 adds 4 "4k x 2k" modes in the the CEA vendor specific block.
With this commit, we now parse this block and expose the 4k modes that we find there.
v2: Fix the "4096x2160" string (nice catch!), add comments about do_hdmi_vsdb_modes() arguments and make it clearer that offset is relative to the end of the required fields of the HDMI VSDB (Ville Syrjälä)
v3: Fix 'Unknow' typo (Simon Farnsworth)
Signed-off-by: Damien Lespiau damien.lespiau@intel.com Tested-by: Cancan Feng cancan.feng@intel.com Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=67030 Reviewed-by: Simon Farnsworth simon.farnsworth@onelan.co.uk --- drivers/gpu/drm/drm_edid.c | 124 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 109 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index bb25ee2..9de573c 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -931,6 +931,36 @@ static const struct drm_display_mode edid_cea_modes[] = { .vrefresh = 100, }, };
+/* + * HDMI 1.4 4k modes. + */ +static const struct drm_display_mode edid_4k_modes[] = { + /* 1 - 3840x2160@30Hz */ + { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, + 3840, 4016, 4104, 4400, 0, + 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 30, }, + /* 2 - 3840x2160@25Hz */ + { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, + 3840, 4896, 4984, 5280, 0, + 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 25, }, + /* 3 - 3840x2160@24Hz */ + { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, + 3840, 5116, 5204, 5500, 0, + 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 24, }, + /* 4 - 4096x2160@24Hz (SMPTE) */ + { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, + 4096, 5116, 5204, 5500, 0, + 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 24, }, +}; + /*** DDC fetch and block validation ***/
static const u8 edid_header[] = { @@ -2465,6 +2495,68 @@ do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len) return modes; }
+/* + * do_hdmi_vsdb_modes - Parse the HDMI Vendor Specific data block + * @connector: connector corresponding to the HDMI sink + * @db: start of the CEA vendor specific block + * @len: length of the CEA block payload, ie. one can access up to db[len] + * + * Parses the HDMI VSDB looking for modes to add to @connector. + */ +static int +do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len) +{ + struct drm_device *dev = connector->dev; + int modes = 0, offset = 0, i; + u8 vic_len; + + if (len < 8) + goto out; + + /* no HDMI_Video_Present */ + if (!(db[8] & (1 << 5))) + goto out; + + /* Latency_Fields_Present */ + if (db[8] & (1 << 7)) + offset += 2; + + /* I_Latency_Fields_Present */ + if (db[8] & (1 << 6)) + offset += 2; + + /* the declared length is not long enough for the 2 first bytes + * of additional video format capabilities */ + offset += 2; + if (len < (8 + offset)) + goto out; + + vic_len = db[8 + offset] >> 5; + + for (i = 0; i < vic_len && len >= (9 + offset + i); i++) { + struct drm_display_mode *newmode; + u8 vic; + + vic = db[9 + offset + i]; + + vic--; /* VICs start at 1 */ + if (vic >= ARRAY_SIZE(edid_4k_modes)) { + DRM_ERROR("Unknown HDMI VIC: %d\n", vic); + continue; + } + + newmode = drm_mode_duplicate(dev, &edid_4k_modes[vic]); + if (!newmode) + continue; + + drm_mode_probed_add(connector, newmode); + modes++; + } + +out: + return modes; +} + static int cea_db_payload_len(const u8 *db) { @@ -2496,6 +2588,21 @@ cea_db_offsets(const u8 *cea, int *start, int *end) return 0; }
+static bool cea_db_is_hdmi_vsdb(const u8 *db) +{ + int hdmi_id; + + if (cea_db_tag(db) != VENDOR_BLOCK) + return false; + + if (cea_db_payload_len(db) < 5) + return false; + + hdmi_id = db[1] | (db[2] << 8) | (db[3] << 16); + + return hdmi_id == HDMI_IDENTIFIER; +} + #define for_each_cea_db(cea, i, start, end) \ for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1)
@@ -2519,6 +2626,8 @@ add_cea_modes(struct drm_connector *connector, struct edid *edid)
if (cea_db_tag(db) == VIDEO_BLOCK) modes += do_cea_modes(connector, db + 1, dbl); + else if (cea_db_is_hdmi_vsdb(db)) + modes += do_hdmi_vsdb_modes(connector, db, dbl); } }
@@ -2571,21 +2680,6 @@ monitor_name(struct detailed_timing *t, void *data) *(u8 **)data = t->data.other_data.data.str.str; }
-static bool cea_db_is_hdmi_vsdb(const u8 *db) -{ - int hdmi_id; - - if (cea_db_tag(db) != VENDOR_BLOCK) - return false; - - if (cea_db_payload_len(db) < 5) - return false; - - hdmi_id = db[1] | (db[2] << 8) | (db[3] << 16); - - return hdmi_id == HDMI_IDENTIFIER; -} - /** * drm_edid_to_eld - build ELD from EDID * @connector: connector corresponding to the HDMI/DP sink
v2: Fix hmdi typo (Simon Farnsworth, Ville Syrjälä)
Suggested-by: Ville Syrjälä ville.syrjala@linux.intel.com Signed-off-by: Damien Lespiau damien.lespiau@intel.com Reviewed-by: Ville Syrjälä ville.syrjala@linux.intel.com Reviewed-by: Simon Farnsworth simon.farnsworth@onelan.co.uk --- drivers/gpu/drm/drm_edid.c | 68 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 9de573c..2381abd 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2409,6 +2409,54 @@ u8 drm_match_cea_mode(const struct drm_display_mode *to_match) } EXPORT_SYMBOL(drm_match_cea_mode);
+/* + * Calculate the alternate clock for HDMI modes (those from the HDMI vendor + * specific block). + * + * It's almost like cea_mode_alternate_clock(), we just need to add an + * exception for the VIC 4 mode (4096x2160@24Hz): no alternate clock for this + * one. + */ +static unsigned int +hdmi_mode_alternate_clock(const struct drm_display_mode *hdmi_mode) +{ + if (hdmi_mode->vdisplay == 4096 && hdmi_mode->hdisplay == 2160) + return hdmi_mode->clock; + + return cea_mode_alternate_clock(hdmi_mode); +} + +/* + * drm_match_hdmi_mode - look for a HDMI mode matching given mode + * @to_match: display mode + * + * An HDMI mode is one defined in the HDMI vendor specific block. + * + * Returns the HDMI Video ID (VIC) of the mode or 0 if it isn't one. + */ +static u8 drm_match_hdmi_mode(const struct drm_display_mode *to_match) +{ + u8 mode; + + if (!to_match->clock) + return 0; + + for (mode = 0; mode < ARRAY_SIZE(edid_4k_modes); mode++) { + const struct drm_display_mode *hdmi_mode = &edid_4k_modes[mode]; + unsigned int clock1, clock2; + + /* Make sure to also match alternate clocks */ + clock1 = hdmi_mode->clock; + clock2 = hdmi_mode_alternate_clock(hdmi_mode); + + if ((KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock1) || + KHZ2PICOS(to_match->clock) == KHZ2PICOS(clock2)) && + drm_mode_equal_no_clocks(to_match, hdmi_mode)) + return mode + 1; + } + return 0; +} + static int add_alternate_cea_modes(struct drm_connector *connector, struct edid *edid) { @@ -2426,18 +2474,26 @@ add_alternate_cea_modes(struct drm_connector *connector, struct edid *edid) * with the alternate clock for certain CEA modes. */ list_for_each_entry(mode, &connector->probed_modes, head) { - const struct drm_display_mode *cea_mode; + const struct drm_display_mode *cea_mode = NULL; struct drm_display_mode *newmode; - u8 cea_mode_idx = drm_match_cea_mode(mode) - 1; + u8 mode_idx = drm_match_cea_mode(mode) - 1; unsigned int clock1, clock2;
- if (cea_mode_idx >= ARRAY_SIZE(edid_cea_modes)) - continue; + if (mode_idx < ARRAY_SIZE(edid_cea_modes)) { + cea_mode = &edid_cea_modes[mode_idx]; + clock2 = cea_mode_alternate_clock(cea_mode); + } else { + mode_idx = drm_match_hdmi_mode(mode) - 1; + if (mode_idx < ARRAY_SIZE(edid_4k_modes)) { + cea_mode = &edid_4k_modes[mode_idx]; + clock2 = hdmi_mode_alternate_clock(cea_mode); + } + }
- cea_mode = &edid_cea_modes[cea_mode_idx]; + if (!cea_mode) + continue;
clock1 = cea_mode->clock; - clock2 = cea_mode_alternate_clock(cea_mode);
if (clock1 == clock2) continue;
To set the active aspect ratio value in the AVI infoframe today, you not only have to set the active_aspect field, but also the active_info_valid bit. Out of the 1 user of this API, we had 100% misuse, forgetting the _valid bit. This was fixed in:
Author: Damien Lespiau damien.lespiau@intel.com Date: Tue Aug 6 20:32:17 2013 +0100
drm: Don't generate invalid AVI infoframes for CEA modes
We can do better and derive the _valid bit from the user wanting to set the active aspect ratio.
Signed-off-by: Damien Lespiau damien.lespiau@intel.com Reviewed-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/drm_edid.c | 1 - drivers/video/hdmi.c | 4 +++- include/linux/hdmi.h | 1 - 3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 2381abd..d76d608 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -3259,7 +3259,6 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame, frame->video_code = drm_match_cea_mode(mode);
frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE; - frame->active_info_valid = 1; frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE;
return 0; diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c index 635d569..e36da36 100644 --- a/drivers/video/hdmi.c +++ b/drivers/video/hdmi.c @@ -96,7 +96,9 @@ ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer,
ptr[0] = ((frame->colorspace & 0x3) << 5) | (frame->scan_mode & 0x3);
- if (frame->active_info_valid) + /* Data byte 1, bit 4 has to be set if we provide the active format + * aspect ratio */ + if (frame->active_aspect & 0xf) ptr[0] |= BIT(4);
if (frame->horizontal_bar_valid) diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h index bc6743e..931474c6 100644 --- a/include/linux/hdmi.h +++ b/include/linux/hdmi.h @@ -109,7 +109,6 @@ struct hdmi_avi_infoframe { unsigned char version; unsigned char length; enum hdmi_colorspace colorspace; - bool active_info_valid; bool horizontal_bar_valid; bool vertical_bar_valid; enum hdmi_scan_mode scan_mode;
On Wed, Aug 14, 2013 at 06:19:08PM +0100, Damien Lespiau wrote:
To set the active aspect ratio value in the AVI infoframe today, you not only have to set the active_aspect field, but also the active_info_valid bit. Out of the 1 user of this API, we had 100% misuse, forgetting the _valid bit. This was fixed in:
Author: Damien Lespiau damien.lespiau@intel.com Date: Tue Aug 6 20:32:17 2013 +0100
drm: Don't generate invalid AVI infoframes for CEA modes
We can do better and derive the _valid bit from the user wanting to set the active aspect ratio.
[...]
diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c
[...]
@@ -96,7 +96,9 @@ ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer,
ptr[0] = ((frame->colorspace & 0x3) << 5) | (frame->scan_mode & 0x3);
- if (frame->active_info_valid)
- /* Data byte 1, bit 4 has to be set if we provide the active format
* aspect ratio */
Nit: According to the coding style rules this should be:
/* * Data byte 1, ... * ... ratio. */
if (frame->active_aspect & 0xf) ptr[0] |= BIT(4);
if (frame->horizontal_bar_valid)
diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h index bc6743e..931474c6 100644 --- a/include/linux/hdmi.h +++ b/include/linux/hdmi.h @@ -109,7 +109,6 @@ struct hdmi_avi_infoframe { unsigned char version; unsigned char length; enum hdmi_colorspace colorspace;
- bool active_info_valid;
When I initially came up with these data structure I wanted them to explicitly expose all the fields that CEA/HDMI specifies. The idea was that a user would actually be allowed to generate invalid infoframes if they chose to by modifying the hdmi_avi_infoframe structure directly.
Instead I'd prefer to see this handled by some higher level function.
Thierry
Just like:
Author: Damien Lespiau damien.lespiau@intel.com Date: Mon Aug 12 11:53:24 2013 +0100
video/hdmi: Don't let the user of this API create invalid infoframes
But this time for the horizontal/vertical bar data present bits.
Signed-off-by: Damien Lespiau damien.lespiau@intel.com Reviewed-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/video/hdmi.c | 5 +++-- include/linux/hdmi.h | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c index e36da36..ac84215 100644 --- a/drivers/video/hdmi.c +++ b/drivers/video/hdmi.c @@ -101,10 +101,11 @@ ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer, if (frame->active_aspect & 0xf) ptr[0] |= BIT(4);
- if (frame->horizontal_bar_valid) + /* Bit 3 and 2 indicate if we transmit horizontal/vertical bar data */ + if (frame->top_bar || frame->bottom_bar) ptr[0] |= BIT(3);
- if (frame->vertical_bar_valid) + if (frame->left_bar || frame->right_bar) ptr[0] |= BIT(2);
ptr[1] = ((frame->colorimetry & 0x3) << 6) | diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h index 931474c6..b98340b 100644 --- a/include/linux/hdmi.h +++ b/include/linux/hdmi.h @@ -109,8 +109,6 @@ struct hdmi_avi_infoframe { unsigned char version; unsigned char length; enum hdmi_colorspace colorspace; - bool horizontal_bar_valid; - bool vertical_bar_valid; enum hdmi_scan_mode scan_mode; enum hdmi_colorimetry colorimetry; enum hdmi_picture_aspect picture_aspect;
On Wed, Aug 14, 2013 at 06:19:09PM +0100, Damien Lespiau wrote:
Just like:
Author: Damien Lespiau damien.lespiau@intel.com Date: Mon Aug 12 11:53:24 2013 +0100
video/hdmi: Don't let the user of this API create invalid infoframes
But this time for the horizontal/vertical bar data present bits.
Signed-off-by: Damien Lespiau damien.lespiau@intel.com Reviewed-by: Ville Syrjälä ville.syrjala@linux.intel.com
drivers/video/hdmi.c | 5 +++-- include/linux/hdmi.h | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-)
Same comments as for patch 5. Although I begin to see some sense in this. Perhaps not exposing these boolean fields is a good idea after all. I wonder if we're excluding some particular use-case by not exposing these fields.
Thierry
On Thu, Aug 15, 2013 at 04:45:56PM +0200, Thierry Reding wrote:
On Wed, Aug 14, 2013 at 06:19:09PM +0100, Damien Lespiau wrote:
Just like:
Author: Damien Lespiau damien.lespiau@intel.com Date: Mon Aug 12 11:53:24 2013 +0100
video/hdmi: Don't let the user of this API create invalid infoframes
But this time for the horizontal/vertical bar data present bits.
Signed-off-by: Damien Lespiau damien.lespiau@intel.com Reviewed-by: Ville Syrjälä ville.syrjala@linux.intel.com
drivers/video/hdmi.c | 5 +++-- include/linux/hdmi.h | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-)
Same comments as for patch 5. Although I begin to see some sense in this. Perhaps not exposing these boolean fields is a good idea after all. I wonder if we're excluding some particular use-case by not exposing these fields.
Right, so I included patch 5/6 to the v4 of the series, putting preventing someone from generating invalid infoframes before the hypothetical use-case where we want the detail of the fields.
We are, de-facto, excluding the case where we'd want to have _unpack() variants decoding infoframes, but that would only be useful if we wanted to check the validity of infoframes in the kernel for instance. I think it's unlikely we'd like to do that. It's also straightforward to revert the 2 patches if/when that happens.
Provide the same programming model than the other infoframe types.
The generic _pack() function can't handle those yet as we need to move the vendor OUI in the generic hdmi_vendor_infoframe structure to know which kind of vendor infoframe we are dealing with.
v2: Fix the value of Side-by-side (half), hmdi typo, pack 3D_Ext_Data (Ville Syrjälä)
Signed-off-by: Damien Lespiau damien.lespiau@intel.com --- drivers/video/hdmi.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/hdmi.h | 26 ++++++++++++++++ 2 files changed, 114 insertions(+)
diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c index ac84215..59c4748 100644 --- a/drivers/video/hdmi.c +++ b/drivers/video/hdmi.c @@ -286,6 +286,94 @@ ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame, EXPORT_SYMBOL(hdmi_audio_infoframe_pack);
/** + * hdmi_hdmi_infoframe_init() - initialize an HDMI vendor infoframe + * @frame: HDMI vendor infoframe + * + * Returns 0 on success or a negative error code on failure. + */ +int hdmi_hdmi_infoframe_init(struct hdmi_hdmi_infoframe *frame) +{ + memset(frame, 0, sizeof(*frame)); + + frame->type = HDMI_INFOFRAME_TYPE_VENDOR; + frame->version = 1; + + /* 0 is a valid value for s3d_struct, so we use a special "not set" + * value */ + frame->s3d_struct = HDMI_3D_STRUCTURE_INVALID; + + return 0; +} +EXPORT_SYMBOL(hdmi_hdmi_infoframe_init); + +/** + * hdmi_hdmi_infoframe_pack() - write a HDMI vendor infoframe to binary buffer + * @frame: HDMI infoframe + * @buffer: destination buffer + * @size: size of buffer + * + * Packs the information contained in the @frame structure into a binary + * representation that can be written into the corresponding controller + * registers. Also computes the checksum as required by section 5.3.5 of + * the HDMI 1.4 specification. + * + * Returns the number of bytes packed into the binary buffer or a negative + * error code on failure. + */ +ssize_t hdmi_hdmi_infoframe_pack(struct hdmi_hdmi_infoframe *frame, + void *buffer, size_t size) +{ + u8 *ptr = buffer; + size_t length; + + /* empty info frame */ + if (frame->vic == 0 && frame->s3d_struct == HDMI_3D_STRUCTURE_INVALID) + return -EINVAL; + + /* only one of those can be supplied */ + if (frame->vic != 0 && frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID) + return -EINVAL; + + /* for side by side (half) we also need to provide 3D_Ext_Data */ + if (frame->s3d_struct == HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF) + frame->length = 6; + else + frame->length = 5; + + length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; + + if (size < length) + return -ENOSPC; + + memset(buffer, 0, size); + + ptr[0] = frame->type; + ptr[1] = frame->version; + ptr[2] = frame->length; + ptr[3] = 0; /* checksum */ + + /* HDMI OUI */ + ptr[4] = 0x03; + ptr[5] = 0x0c; + ptr[6] = 0x00; + + if (frame->vic) { + ptr[7] = 0x1 << 5; /* video format */ + ptr[8] = frame->vic; + } else { + ptr[7] = 0x2 << 5; /* video format */ + ptr[8] = (frame->s3d_struct & 0xf) << 4; + if (frame->s3d_struct == HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF) + ptr[9] = (frame->s3d_ext_data & 0xf) << 4; + } + + hdmi_infoframe_checksum(buffer, length); + + return length; +} +EXPORT_SYMBOL(hdmi_hdmi_infoframe_pack); + +/** * hdmi_vendor_infoframe_pack() - write a HDMI vendor infoframe to binary * buffer * @frame: HDMI vendor infoframe diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h index b98340b..e733252 100644 --- a/include/linux/hdmi.h +++ b/include/linux/hdmi.h @@ -234,11 +234,37 @@ struct hdmi_vendor_infoframe { ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame, void *buffer, size_t size);
+enum hdmi_3d_structure { + HDMI_3D_STRUCTURE_INVALID = -1, + HDMI_3D_STRUCTURE_FRAME_PACKING = 0, + HDMI_3D_STRUCTURE_FIELD_ALTERNATIVE, + HDMI_3D_STRUCTURE_LINE_ALTERNATIVE, + HDMI_3D_STRUCTURE_SIDE_BY_SIDE_FULL, + HDMI_3D_STRUCTURE_L_DEPTH, + HDMI_3D_STRUCTURE_L_DEPTH_GFX_GFX_DEPTH, + HDMI_3D_STRUCTURE_TOP_AND_BOTTOM, + HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF = 8, +}; + +struct hdmi_hdmi_infoframe { + enum hdmi_infoframe_type type; + unsigned char version; + unsigned char length; + u8 vic; + enum hdmi_3d_structure s3d_struct; + unsigned int s3d_ext_data; +}; + +int hdmi_hdmi_infoframe_init(struct hdmi_hdmi_infoframe *frame); +ssize_t hdmi_hdmi_infoframe_pack(struct hdmi_hdmi_infoframe *frame, + void *buffer, size_t size); + union hdmi_infoframe { struct hdmi_any_infoframe any; struct hdmi_avi_infoframe avi; struct hdmi_spd_infoframe spd; struct hdmi_vendor_infoframe vendor; + struct hdmi_hdmi_infoframe hdmi; struct hdmi_audio_infoframe audio; };
On Wed, Aug 14, 2013 at 06:19:10PM +0100, Damien Lespiau wrote:
Provide the same programming model than the other infoframe types.
The generic _pack() function can't handle those yet as we need to move the vendor OUI in the generic hdmi_vendor_infoframe structure to know which kind of vendor infoframe we are dealing with.
v2: Fix the value of Side-by-side (half), hmdi typo, pack 3D_Ext_Data (Ville Syrjälä)
Signed-off-by: Damien Lespiau damien.lespiau@intel.com
drivers/video/hdmi.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/hdmi.h | 26 ++++++++++++++++ 2 files changed, 114 insertions(+)
diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c index ac84215..59c4748 100644 --- a/drivers/video/hdmi.c +++ b/drivers/video/hdmi.c @@ -286,6 +286,94 @@ ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame, EXPORT_SYMBOL(hdmi_audio_infoframe_pack);
/**
- hdmi_hdmi_infoframe_init() - initialize an HDMI vendor infoframe
- @frame: HDMI vendor infoframe
- Returns 0 on success or a negative error code on failure.
- */
+int hdmi_hdmi_infoframe_init(struct hdmi_hdmi_infoframe *frame) +{
- memset(frame, 0, sizeof(*frame));
- frame->type = HDMI_INFOFRAME_TYPE_VENDOR;
- frame->version = 1;
- /* 0 is a valid value for s3d_struct, so we use a special "not set"
* value */
- frame->s3d_struct = HDMI_3D_STRUCTURE_INVALID;
- return 0;
+} +EXPORT_SYMBOL(hdmi_hdmi_infoframe_init);
+/**
- hdmi_hdmi_infoframe_pack() - write a HDMI vendor infoframe to binary buffer
- @frame: HDMI infoframe
- @buffer: destination buffer
- @size: size of buffer
- Packs the information contained in the @frame structure into a binary
- representation that can be written into the corresponding controller
- registers. Also computes the checksum as required by section 5.3.5 of
- the HDMI 1.4 specification.
- Returns the number of bytes packed into the binary buffer or a negative
- error code on failure.
- */
+ssize_t hdmi_hdmi_infoframe_pack(struct hdmi_hdmi_infoframe *frame,
void *buffer, size_t size)
+{
- u8 *ptr = buffer;
- size_t length;
- /* empty info frame */
- if (frame->vic == 0 && frame->s3d_struct == HDMI_3D_STRUCTURE_INVALID)
return -EINVAL;
- /* only one of those can be supplied */
- if (frame->vic != 0 && frame->s3d_struct != HDMI_3D_STRUCTURE_INVALID)
return -EINVAL;
- /* for side by side (half) we also need to provide 3D_Ext_Data */
- if (frame->s3d_struct == HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF)
Could be >= for a bit of future proofing since the spec says we should send 3d_ext_data even for the reserved > 8 values.
frame->length = 6;
- else
frame->length = 5;
- length = HDMI_INFOFRAME_HEADER_SIZE + frame->length;
- if (size < length)
return -ENOSPC;
- memset(buffer, 0, size);
- ptr[0] = frame->type;
- ptr[1] = frame->version;
- ptr[2] = frame->length;
- ptr[3] = 0; /* checksum */
- /* HDMI OUI */
- ptr[4] = 0x03;
- ptr[5] = 0x0c;
- ptr[6] = 0x00;
- if (frame->vic) {
ptr[7] = 0x1 << 5; /* video format */
ptr[8] = frame->vic;
- } else {
ptr[7] = 0x2 << 5; /* video format */
ptr[8] = (frame->s3d_struct & 0xf) << 4;
if (frame->s3d_struct == HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF)
Could be >= here too.
But whether or not you make those changes: Reviewed-by: Ville Syrjälä ville.syrjala@linux.intel.com
ptr[9] = (frame->s3d_ext_data & 0xf) << 4;
- }
- hdmi_infoframe_checksum(buffer, length);
- return length;
+} +EXPORT_SYMBOL(hdmi_hdmi_infoframe_pack);
+/**
- hdmi_vendor_infoframe_pack() - write a HDMI vendor infoframe to binary
buffer
- @frame: HDMI vendor infoframe
diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h index b98340b..e733252 100644 --- a/include/linux/hdmi.h +++ b/include/linux/hdmi.h @@ -234,11 +234,37 @@ struct hdmi_vendor_infoframe { ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame, void *buffer, size_t size);
+enum hdmi_3d_structure {
- HDMI_3D_STRUCTURE_INVALID = -1,
- HDMI_3D_STRUCTURE_FRAME_PACKING = 0,
- HDMI_3D_STRUCTURE_FIELD_ALTERNATIVE,
- HDMI_3D_STRUCTURE_LINE_ALTERNATIVE,
- HDMI_3D_STRUCTURE_SIDE_BY_SIDE_FULL,
- HDMI_3D_STRUCTURE_L_DEPTH,
- HDMI_3D_STRUCTURE_L_DEPTH_GFX_GFX_DEPTH,
- HDMI_3D_STRUCTURE_TOP_AND_BOTTOM,
- HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF = 8,
+};
+struct hdmi_hdmi_infoframe {
- enum hdmi_infoframe_type type;
- unsigned char version;
- unsigned char length;
- u8 vic;
- enum hdmi_3d_structure s3d_struct;
- unsigned int s3d_ext_data;
+};
+int hdmi_hdmi_infoframe_init(struct hdmi_hdmi_infoframe *frame); +ssize_t hdmi_hdmi_infoframe_pack(struct hdmi_hdmi_infoframe *frame,
void *buffer, size_t size);
union hdmi_infoframe { struct hdmi_any_infoframe any; struct hdmi_avi_infoframe avi; struct hdmi_spd_infoframe spd; struct hdmi_vendor_infoframe vendor;
- struct hdmi_hdmi_infoframe hdmi; struct hdmi_audio_infoframe audio;
};
-- 1.8.3.1
Intel-gfx mailing list Intel-gfx@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/intel-gfx
On Wed, Aug 14, 2013 at 06:19:10PM +0100, Damien Lespiau wrote: [...]
diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c index ac84215..59c4748 100644 --- a/drivers/video/hdmi.c +++ b/drivers/video/hdmi.c @@ -286,6 +286,94 @@ ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame, EXPORT_SYMBOL(hdmi_audio_infoframe_pack);
/**
- hdmi_hdmi_infoframe_init() - initialize an HDMI vendor infoframe
- @frame: HDMI vendor infoframe
- Returns 0 on success or a negative error code on failure.
- */
+int hdmi_hdmi_infoframe_init(struct hdmi_hdmi_infoframe *frame)
The hdmi_hdmi_ prefix is weird. Can't we come up with a better prefix? You refer to it as "HDMI vendor infoframe" in the comments, yet we already have struct hdmi_vendor_infoframe. Perhaps hdmi_3d_infoframe or hdmi_vendor_3d_infoframe would be better choices?
+{
- memset(frame, 0, sizeof(*frame));
- frame->type = HDMI_INFOFRAME_TYPE_VENDOR;
- frame->version = 1;
- /* 0 is a valid value for s3d_struct, so we use a special "not set"
* value */
Nit: The block comment style is inconsistent again.
+/**
- hdmi_hdmi_infoframe_pack() - write a HDMI vendor infoframe to binary buffer
- @frame: HDMI infoframe
- @buffer: destination buffer
- @size: size of buffer
- Packs the information contained in the @frame structure into a binary
- representation that can be written into the corresponding controller
- registers. Also computes the checksum as required by section 5.3.5 of
- the HDMI 1.4 specification.
I need to dig up that version of the specification. This infoframe doesn't seem to exist in 1.3.
Thierry
I just wrote the bits to define and pack HDMI vendor specific infoframe. Port the host1x driver to use those so I can refactor the infoframe code a bit more.
This changes the length of the infoframe payload from 6 to 5, which is enough for the "frame packing" stereo format.
v2: Pimp up the commit message with the note about the length (Ville Syrjälä)
Cc: Thierry Reding thierry.reding@gmail.com Cc: Terje Bergström tbergstrom@nvidia.com Cc: linux-tegra@vger.kernel.org
Signed-off-by: Damien Lespiau damien.lespiau@intel.com --- drivers/gpu/host1x/drm/hdmi.c | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-)
diff --git a/drivers/gpu/host1x/drm/hdmi.c b/drivers/gpu/host1x/drm/hdmi.c index 01097da..b548918 100644 --- a/drivers/gpu/host1x/drm/hdmi.c +++ b/drivers/gpu/host1x/drm/hdmi.c @@ -539,7 +539,7 @@ static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi)
static void tegra_hdmi_setup_stereo_infoframe(struct tegra_hdmi *hdmi) { - struct hdmi_vendor_infoframe frame; + struct hdmi_hdmi_infoframe frame; unsigned long value; u8 buffer[10]; ssize_t err; @@ -551,26 +551,10 @@ static void tegra_hdmi_setup_stereo_infoframe(struct tegra_hdmi *hdmi) return; }
- memset(&frame, 0, sizeof(frame)); + hdmi_hdmi_infoframe_init(&frame); + frame.s3d_struct = HDMI_3D_STRUCTURE_FRAME_PACKING;
- frame.type = HDMI_INFOFRAME_TYPE_VENDOR; - frame.version = 0x01; - frame.length = 6; - - frame.data[0] = 0x03; /* regid0 */ - frame.data[1] = 0x0c; /* regid1 */ - frame.data[2] = 0x00; /* regid2 */ - frame.data[3] = 0x02 << 5; /* video format */ - - /* TODO: 74 MHz limit? */ - if (1) { - frame.data[4] = 0x00 << 4; /* 3D structure */ - } else { - frame.data[4] = 0x08 << 4; /* 3D structure */ - frame.data[5] = 0x00 << 4; /* 3D ext. data */ - } - - err = hdmi_vendor_infoframe_pack(&frame, buffer, sizeof(buffer)); + err = hdmi_hdmi_infoframe_pack(&frame, buffer, sizeof(buffer)); if (err < 0) { dev_err(hdmi->dev, "failed to pack vendor infoframe: %zd\n", err);
We'll need the HDMI OUI for the HDMI vendor infoframe data, so let's move the DRM one to hdmi.h, might as well use the hdmi header to store some hdmi defines.
(Note that, in fact, infoframes are part of the CEA-861 standard, and only the HDMI vendor specific infoframe is special to HDMI, but details..)
Signed-off-by: Damien Lespiau damien.lespiau@intel.com Reviewed-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/drm_edid.c | 1 - include/linux/hdmi.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index d76d608..3aa653f 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2317,7 +2317,6 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid, return closure.modes; }
-#define HDMI_IDENTIFIER 0x000C03 #define AUDIO_BLOCK 0x01 #define VIDEO_BLOCK 0x02 #define VENDOR_BLOCK 0x03 diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h index e733252..37e0cd7 100644 --- a/include/linux/hdmi.h +++ b/include/linux/hdmi.h @@ -18,6 +18,7 @@ enum hdmi_infoframe_type { HDMI_INFOFRAME_TYPE_AUDIO = 0x84, };
+#define HDMI_IDENTIFIER 0x000c03 #define HDMI_INFOFRAME_HEADER_SIZE 4 #define HDMI_AVI_INFOFRAME_SIZE 13 #define HDMI_SPD_INFOFRAME_SIZE 25
On Thu, Aug 15, 2013 at 05:12:00PM +0200, Thierry Reding wrote:
On Wed, Aug 14, 2013 at 06:19:12PM +0100, Damien Lespiau wrote: [...]
+#define HDMI_IDENTIFIER 0x000c03
HDMI_IDENTIFIER sounds really generic. Perhaps HDMI_INFOFRAME_OUI_HDMI?
This identifier is not infoframe specific, it's the IEEE OUI:
http://standards.ieee.org/develop/regauth/oui/oui.txt
would HDMI_IEEE_OUI suit you?
On Mon, Aug 19, 2013 at 02:49:50PM +0100, Damien Lespiau wrote:
On Thu, Aug 15, 2013 at 05:12:00PM +0200, Thierry Reding wrote:
On Wed, Aug 14, 2013 at 06:19:12PM +0100, Damien Lespiau wrote: [...]
+#define HDMI_IDENTIFIER 0x000c03
HDMI_IDENTIFIER sounds really generic. Perhaps HDMI_INFOFRAME_OUI_HDMI?
This identifier is not infoframe specific, it's the IEEE OUI:
http://standards.ieee.org/develop/regauth/oui/oui.txt
would HDMI_IEEE_OUI suit you?
Yes, that sounds much better. Or perhaps IEEE_OUI_HDMI? From a quick grep through the kernel code using the OUI as a prefix seems to be slightly more common. If we ever end up adding a header to collect OUIs it'd be useful to namespace them somehow.
Thierry
On Mon, Aug 19, 2013 at 09:31:42PM +0200, Thierry Reding wrote:
On Mon, Aug 19, 2013 at 02:49:50PM +0100, Damien Lespiau wrote:
On Thu, Aug 15, 2013 at 05:12:00PM +0200, Thierry Reding wrote:
On Wed, Aug 14, 2013 at 06:19:12PM +0100, Damien Lespiau wrote: [...]
+#define HDMI_IDENTIFIER 0x000c03
HDMI_IDENTIFIER sounds really generic. Perhaps HDMI_INFOFRAME_OUI_HDMI?
This identifier is not infoframe specific, it's the IEEE OUI:
http://standards.ieee.org/develop/regauth/oui/oui.txt
would HDMI_IEEE_OUI suit you?
Yes, that sounds much better. Or perhaps IEEE_OUI_HDMI? From a quick grep through the kernel code using the OUI as a prefix seems to be slightly more common. If we ever end up adding a header to collect OUIs it'd be useful to namespace them somehow.
Hopefully we can agree that HDMI_IEEE_OUI is fine :) Would you mind having a look at the two patches introduced to address your comments
HDMI 4k support v4 http://lists.freedesktop.org/archives/dri-devel/2013-August/043988.html
Patches 11 and 14.
Thanks,
With this last bit, hdmi_infoframe_pack() is now able to pack any infoframe we support.
At the same time, because it's impractical to make two commits out of this, we get rid of the version that encourages the open coding of the vendor infoframe packing. We can do so because the only user of this API has been ported in:
Author: Damien Lespiau damien.lespiau@intel.com Date: Mon Aug 12 18:08:37 2013 +0100
gpu: host1x: Port the HDMI vendor infoframe code the common helpers
v2: Change oui to be an unsigned int (Ville Syrjälä)
Signed-off-by: Damien Lespiau damien.lespiau@intel.com Reviewed-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/video/hdmi.c | 45 +++++++++------------------------------------ include/linux/hdmi.h | 24 ++++++++++++------------ 2 files changed, 21 insertions(+), 48 deletions(-)
diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c index 59c4748..7ae4f80 100644 --- a/drivers/video/hdmi.c +++ b/drivers/video/hdmi.c @@ -298,6 +298,7 @@ int hdmi_hdmi_infoframe_init(struct hdmi_hdmi_infoframe *frame) frame->type = HDMI_INFOFRAME_TYPE_VENDOR; frame->version = 1;
+ frame->oui = HDMI_IDENTIFIER; /* 0 is a valid value for s3d_struct, so we use a special "not set" * value */ frame->s3d_struct = HDMI_3D_STRUCTURE_INVALID; @@ -373,46 +374,18 @@ ssize_t hdmi_hdmi_infoframe_pack(struct hdmi_hdmi_infoframe *frame, } EXPORT_SYMBOL(hdmi_hdmi_infoframe_pack);
-/** - * hdmi_vendor_infoframe_pack() - write a HDMI vendor infoframe to binary - * buffer - * @frame: HDMI vendor infoframe - * @buffer: destination buffer - * @size: size of buffer - * - * Packs the information contained in the @frame structure into a binary - * representation that can be written into the corresponding controller - * registers. Also computes the checksum as required by section 5.3.5 of - * the HDMI 1.4 specification. - * - * Returns the number of bytes packed into the binary buffer or a negative - * error code on failure. +/* + * hdmi_vendor_infoframe_pack() - write a vendor infoframe to binary buffer */ -ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame, - void *buffer, size_t size) +static ssize_t hdmi_vendor_infoframe_pack(union hdmi_vendor_infoframe *frame, + void *buffer, size_t size) { - u8 *ptr = buffer; - size_t length; - - length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; - - if (size < length) - return -ENOSPC; - - memset(buffer, 0, size); - - ptr[0] = frame->type; - ptr[1] = frame->version; - ptr[2] = frame->length; - ptr[3] = 0; /* checksum */ - - memcpy(&ptr[HDMI_INFOFRAME_HEADER_SIZE], frame->data, frame->length); - - hdmi_infoframe_checksum(buffer, length); + /* we only know about HDMI vendor infoframes */ + if (frame->any.oui != HDMI_IDENTIFIER) + return -EINVAL;
- return length; + return hdmi_hdmi_infoframe_pack(&frame->hdmi, buffer, size); } -EXPORT_SYMBOL(hdmi_vendor_infoframe_pack);
/** * hdmi_infoframe_pack() - write a HDMI infoframe to binary buffer diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h index 37e0cd7..e24d850 100644 --- a/include/linux/hdmi.h +++ b/include/linux/hdmi.h @@ -225,16 +225,6 @@ int hdmi_audio_infoframe_init(struct hdmi_audio_infoframe *frame); ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame, void *buffer, size_t size);
-struct hdmi_vendor_infoframe { - enum hdmi_infoframe_type type; - unsigned char version; - unsigned char length; - u8 data[27]; -}; - -ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame, - void *buffer, size_t size); - enum hdmi_3d_structure { HDMI_3D_STRUCTURE_INVALID = -1, HDMI_3D_STRUCTURE_FRAME_PACKING = 0, @@ -251,6 +241,7 @@ struct hdmi_hdmi_infoframe { enum hdmi_infoframe_type type; unsigned char version; unsigned char length; + unsigned int oui; u8 vic; enum hdmi_3d_structure s3d_struct; unsigned int s3d_ext_data; @@ -260,12 +251,21 @@ int hdmi_hdmi_infoframe_init(struct hdmi_hdmi_infoframe *frame); ssize_t hdmi_hdmi_infoframe_pack(struct hdmi_hdmi_infoframe *frame, void *buffer, size_t size);
+union hdmi_vendor_infoframe { + struct { + enum hdmi_infoframe_type type; + unsigned char version; + unsigned char length; + unsigned int oui; + } any; + struct hdmi_hdmi_infoframe hdmi; +}; + union hdmi_infoframe { struct hdmi_any_infoframe any; struct hdmi_avi_infoframe avi; struct hdmi_spd_infoframe spd; - struct hdmi_vendor_infoframe vendor; - struct hdmi_hdmi_infoframe hdmi; + union hdmi_vendor_infoframe vendor; struct hdmi_audio_infoframe audio; };
On Wed, Aug 14, 2013 at 06:19:13PM +0100, Damien Lespiau wrote: [...]
diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h
[...]
+union hdmi_vendor_infoframe {
- struct {
enum hdmi_infoframe_type type;
unsigned char version;
unsigned char length;
unsigned int oui;
- } any;
- struct hdmi_hdmi_infoframe hdmi;
Given the above, perhaps struct hdmi_vendor_hdmi_infoframe would possibly be a good fit, albeit being rather long. But I'm obsessed with namespaces...
Thierry
This can then be used by DRM drivers to setup their vendor infoframes.
v2: Fix hmdi typo (Simon Farnsworth)
Signed-off-by: Damien Lespiau damien.lespiau@intel.com Reviewed-by: Simon Farnsworth simon.farnsworth@onelan.co.uk Reviewed-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/drm_edid.c | 36 ++++++++++++++++++++++++++++++++++++ include/drm/drm_edid.h | 4 ++++ 2 files changed, 40 insertions(+)
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 3aa653f..42c62d1 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -3263,3 +3263,39 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame, return 0; } EXPORT_SYMBOL(drm_hdmi_avi_infoframe_from_display_mode); + +/** + * drm_hdmi_vendor_infoframe_from_display_mode() - fill an HDMI infoframe with + * data from a DRM display mode + * @frame: HDMI vendor infoframe + * @mode: DRM display mode + * + * Note that there's is a need to send HDMI vendor infoframes only when using a + * 4k or stereoscopic 3D mode. So when giving any other mode as input this + * function will return -EINVAL, error that can be safely ignored. + * + * Returns 0 on success or a negative error code on failure. + */ +int +drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_hdmi_infoframe *frame, + const struct drm_display_mode *mode) +{ + int err; + u8 vic; + + if (!frame || !mode) + return -EINVAL; + + vic = drm_match_hdmi_mode(mode); + if (!vic) + return -EINVAL; + + err = hdmi_hdmi_infoframe_init(frame); + if (err < 0) + return err; + + frame->vic = vic; + + return 0; +} +EXPORT_SYMBOL(drm_hdmi_vendor_infoframe_from_display_mode); diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index fc481fc..a204e31 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -256,6 +256,7 @@ struct drm_encoder; struct drm_connector; struct drm_display_mode; struct hdmi_avi_infoframe; +struct hdmi_hdmi_infoframe;
void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid); int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads); @@ -268,5 +269,8 @@ int drm_load_edid_firmware(struct drm_connector *connector); int drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame, const struct drm_display_mode *mode); +int +drm_hdmi_vendor_infoframe_from_display_mode(struct hdmi_hdmi_infoframe *frame, + const struct drm_display_mode *mode);
#endif /* __DRM_EDID_H__ */
With all the common infoframe bits now in place, we can finally write the vendor specific infoframes in our driver.
Signed-off-by: Damien Lespiau damien.lespiau@intel.com Reviewed-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/i915_reg.h | 2 ++ drivers/gpu/drm/i915/intel_hdmi.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+)
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 17f6252..33427fd1 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -4157,6 +4157,8 @@ _TRANSCODER(trans, HSW_VIDEO_DIP_CTL_A, HSW_VIDEO_DIP_CTL_B) #define HSW_TVIDEO_DIP_AVI_DATA(trans) \ _TRANSCODER(trans, HSW_VIDEO_DIP_AVI_DATA_A, HSW_VIDEO_DIP_AVI_DATA_B) +#define HSW_TVIDEO_DIP_VS_DATA(trans) \ + _TRANSCODER(trans, HSW_VIDEO_DIP_VS_DATA_A, HSW_VIDEO_DIP_VS_DATA_B) #define HSW_TVIDEO_DIP_SPD_DATA(trans) \ _TRANSCODER(trans, HSW_VIDEO_DIP_SPD_DATA_A, HSW_VIDEO_DIP_SPD_DATA_B) #define HSW_TVIDEO_DIP_GCP(trans) \ diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index a619d94..4148cc8 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -74,6 +74,8 @@ static u32 g4x_infoframe_index(enum hdmi_infoframe_type type) return VIDEO_DIP_SELECT_AVI; case HDMI_INFOFRAME_TYPE_SPD: return VIDEO_DIP_SELECT_SPD; + case HDMI_INFOFRAME_TYPE_VENDOR: + return VIDEO_DIP_SELECT_VENDOR; default: DRM_DEBUG_DRIVER("unknown info frame type %d\n", type); return 0; @@ -87,6 +89,8 @@ static u32 g4x_infoframe_enable(enum hdmi_infoframe_type type) return VIDEO_DIP_ENABLE_AVI; case HDMI_INFOFRAME_TYPE_SPD: return VIDEO_DIP_ENABLE_SPD; + case HDMI_INFOFRAME_TYPE_VENDOR: + return VIDEO_DIP_ENABLE_VENDOR; default: DRM_DEBUG_DRIVER("unknown info frame type %d\n", type); return 0; @@ -100,6 +104,8 @@ static u32 hsw_infoframe_enable(enum hdmi_infoframe_type type) return VIDEO_DIP_ENABLE_AVI_HSW; case HDMI_INFOFRAME_TYPE_SPD: return VIDEO_DIP_ENABLE_SPD_HSW; + case HDMI_INFOFRAME_TYPE_VENDOR: + return VIDEO_DIP_ENABLE_VS_HSW; default: DRM_DEBUG_DRIVER("unknown info frame type %d\n", type); return 0; @@ -114,6 +120,8 @@ static u32 hsw_infoframe_data_reg(enum hdmi_infoframe_type type, return HSW_TVIDEO_DIP_AVI_DATA(cpu_transcoder); case HDMI_INFOFRAME_TYPE_SPD: return HSW_TVIDEO_DIP_SPD_DATA(cpu_transcoder); + case HDMI_INFOFRAME_TYPE_VENDOR: + return HSW_TVIDEO_DIP_VS_DATA(cpu_transcoder); default: DRM_DEBUG_DRIVER("unknown info frame type %d\n", type); return 0; @@ -392,6 +400,21 @@ static void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder) intel_write_infoframe(encoder, &frame); }
+static void +intel_hdmi_set_hdmi_infoframe(struct drm_encoder *encoder, + struct drm_display_mode *adjusted_mode) +{ + union hdmi_infoframe frame; + int ret; + + ret = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi, + adjusted_mode); + if (ret < 0) + return; + + intel_write_infoframe(encoder, &frame); +} + static void g4x_set_infoframes(struct drm_encoder *encoder, struct drm_display_mode *adjusted_mode) { @@ -454,6 +477,7 @@ static void g4x_set_infoframes(struct drm_encoder *encoder,
intel_hdmi_set_avi_infoframe(encoder, adjusted_mode); intel_hdmi_set_spd_infoframe(encoder); + intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode); }
static void ibx_set_infoframes(struct drm_encoder *encoder, @@ -515,6 +539,7 @@ static void ibx_set_infoframes(struct drm_encoder *encoder,
intel_hdmi_set_avi_infoframe(encoder, adjusted_mode); intel_hdmi_set_spd_infoframe(encoder); + intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode); }
static void cpt_set_infoframes(struct drm_encoder *encoder, @@ -550,6 +575,7 @@ static void cpt_set_infoframes(struct drm_encoder *encoder,
intel_hdmi_set_avi_infoframe(encoder, adjusted_mode); intel_hdmi_set_spd_infoframe(encoder); + intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode); }
static void vlv_set_infoframes(struct drm_encoder *encoder, @@ -584,6 +610,7 @@ static void vlv_set_infoframes(struct drm_encoder *encoder,
intel_hdmi_set_avi_infoframe(encoder, adjusted_mode); intel_hdmi_set_spd_infoframe(encoder); + intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode); }
static void hsw_set_infoframes(struct drm_encoder *encoder, @@ -611,6 +638,7 @@ static void hsw_set_infoframes(struct drm_encoder *encoder,
intel_hdmi_set_avi_infoframe(encoder, adjusted_mode); intel_hdmi_set_spd_infoframe(encoder); + intel_hdmi_set_hdmi_infoframe(encoder, adjusted_mode); }
static void intel_hdmi_mode_set(struct intel_encoder *encoder)
On Wed, Aug 14, 2013 at 06:19:03PM +0100, Damien Lespiau wrote:
Follow up on v2: http://lists.freedesktop.org/archives/dri-devel/2013-August/043604.html
With the quick and nice reviews from Ville and Simon, it's time for a v3:
- Fix embarrassing hmdi typo
- Fix the sending of vendor specific infoframes for side-by-side half modes
- Smaller changes here and there.
Looking very good.
Reviewed-by: Ville Syrjälä ville.syrjala@linux.intel.com
for the rest as well.
dri-devel@lists.freedesktop.org