The first two patches are of a janitorial nature, the first converting the drm_num_cea_modes constant to unsigned and the second adding a few missing forward declarations in order to fix build issues.
Patch 3 adds generic helpers to pack a structures that describe HDMI AVI, audio, vendor-specific or SPD infoframes into the binary format specified in the HDMI specification. The resulting binary buffer should be easily programmable into the HDMI controller.
Patch 4 provides a helper to fill an HDMI AVI infoframe with data from a struct drm_display_mode.
Patch 5 adds the DRM EDID helpers (including the new DRM HDMI helper from patch 4) to the DRM DocBook documentation.
Patch 6, 7 and 8 move the Tegra, Intel and Radeon drivers over to the new helpers. Patch 7 was contributed by Intel's Paulo Zanoni.
Finally, patch 9 removes the drm_mode_cea_vic() function which is a duplicate implementation of the older drm_match_cea_mode().
I haven't gotten around to converting the Exynos driver reimplementation because it isn't as trivial as the others. Nouveau also has some code to write infoframes and I'll see if I can convert those next.
One item that I have on my TODO list is the ACR computations requested by Christian König. My schedule has been really busy lately, so I'm not sure how long it will take until I get to it. I'd appreciate it if we could merge this series without the ACR implementation in order to get at least the numerous infoframe reimplementations cleaned up and to prevent others from getting in.
Thierry
Paulo Zanoni (1): drm/i915: Use generic HDMI infoframe helpers
Thierry Reding (8): drm: Make drm_num_cea_modes unsigned drm: Add some missing forward declarations video: Add generic HDMI infoframe helpers drm: Add HDMI infoframe helpers drm: Add EDID helper documentation drm/tegra: Use generic HDMI infoframe helpers drm/radeon: Use generic HDMI infoframe helpers drm: Remove duplicate drm_mode_cea_vic()
Documentation/DocBook/drm.tmpl | 4 + drivers/gpu/drm/Kconfig | 1 + drivers/gpu/drm/drm_edid.c | 45 +++-- drivers/gpu/drm/drm_edid_modes.h | 2 +- drivers/gpu/drm/i915/intel_drv.h | 62 +----- drivers/gpu/drm/i915/intel_hdmi.c | 186 +++++++++--------- drivers/gpu/drm/i915/intel_sdvo.c | 21 +- drivers/gpu/drm/radeon/evergreen_hdmi.c | 85 ++------ drivers/gpu/drm/radeon/r600_hdmi.c | 134 ++++--------- drivers/gpu/drm/tegra/Kconfig | 1 + drivers/gpu/drm/tegra/hdmi.c | 226 +++++++++++----------- drivers/gpu/drm/tegra/hdmi.h | 189 ------------------ drivers/video/Kconfig | 3 + drivers/video/Makefile | 1 + drivers/video/hdmi.c | 332 ++++++++++++++++++++++++++++++++ include/drm/drm_crtc.h | 6 +- include/drm/drm_edid.h | 6 + include/linux/hdmi.h | 221 +++++++++++++++++++++ 18 files changed, 883 insertions(+), 642 deletions(-) create mode 100644 drivers/video/hdmi.c create mode 100644 include/linux/hdmi.h
Since the variable's value is the size of an array, we can assume that it will never be negative.
Signed-off-by: Thierry Reding thierry.reding@avionic-design.de --- drivers/gpu/drm/drm_edid_modes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_edid_modes.h b/drivers/gpu/drm/drm_edid_modes.h index 5dbf7d2..d65d863 100644 --- a/drivers/gpu/drm/drm_edid_modes.h +++ b/drivers/gpu/drm/drm_edid_modes.h @@ -771,4 +771,4 @@ static const struct drm_display_mode edid_cea_modes[] = { 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, }; -static const int drm_num_cea_modes = ARRAY_SIZE(edid_cea_modes); +static const unsigned int drm_num_cea_modes = ARRAY_SIZE(edid_cea_modes);
On Mon, Jan 14, 2013 at 03:30:20PM +0100, Thierry Reding wrote:
Since the variable's value is the size of an array, we can assume that it will never be negative.
Signed-off-by: Thierry Reding thierry.reding@avionic-design.de
drivers/gpu/drm/drm_edid_modes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_edid_modes.h b/drivers/gpu/drm/drm_edid_modes.h index 5dbf7d2..d65d863 100644 --- a/drivers/gpu/drm/drm_edid_modes.h +++ b/drivers/gpu/drm/drm_edid_modes.h @@ -771,4 +771,4 @@ static const struct drm_display_mode edid_cea_modes[] = { 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, }; -static const int drm_num_cea_modes = ARRAY_SIZE(edid_cea_modes); +static const unsigned int drm_num_cea_modes = ARRAY_SIZE(edid_cea_modes);
Why do we even have these num_foo things? I think it would be clearer to use ARRAY_SIZE(foo) directly where appropriate.
On Mon, Jan 14, 2013 at 04:42:39PM +0200, Ville Syrjälä wrote:
On Mon, Jan 14, 2013 at 03:30:20PM +0100, Thierry Reding wrote:
Since the variable's value is the size of an array, we can assume that it will never be negative.
Signed-off-by: Thierry Reding thierry.reding@avionic-design.de
drivers/gpu/drm/drm_edid_modes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_edid_modes.h b/drivers/gpu/drm/drm_edid_modes.h index 5dbf7d2..d65d863 100644 --- a/drivers/gpu/drm/drm_edid_modes.h +++ b/drivers/gpu/drm/drm_edid_modes.h @@ -771,4 +771,4 @@ static const struct drm_display_mode edid_cea_modes[] = { 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, }; -static const int drm_num_cea_modes = ARRAY_SIZE(edid_cea_modes); +static const unsigned int drm_num_cea_modes = ARRAY_SIZE(edid_cea_modes);
Why do we even have these num_foo things? I think it would be clearer to use ARRAY_SIZE(foo) directly where appropriate.
Actually I was thinking about maybe writing a patch to move the table out of the header and into drm_edid.c (or drm_cea.c?) to make it less easy to create duplicates of the table. In that case, using the ARRAY_SIZE macro directly wouldn't be an option.
Thierry
On Mon, Jan 14, 2013 at 04:06:13PM +0100, Thierry Reding wrote:
On Mon, Jan 14, 2013 at 04:42:39PM +0200, Ville Syrjälä wrote:
On Mon, Jan 14, 2013 at 03:30:20PM +0100, Thierry Reding wrote:
Since the variable's value is the size of an array, we can assume that it will never be negative.
Signed-off-by: Thierry Reding thierry.reding@avionic-design.de
drivers/gpu/drm/drm_edid_modes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_edid_modes.h b/drivers/gpu/drm/drm_edid_modes.h index 5dbf7d2..d65d863 100644 --- a/drivers/gpu/drm/drm_edid_modes.h +++ b/drivers/gpu/drm/drm_edid_modes.h @@ -771,4 +771,4 @@ static const struct drm_display_mode edid_cea_modes[] = { 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, }; -static const int drm_num_cea_modes = ARRAY_SIZE(edid_cea_modes); +static const unsigned int drm_num_cea_modes = ARRAY_SIZE(edid_cea_modes);
Why do we even have these num_foo things? I think it would be clearer to use ARRAY_SIZE(foo) directly where appropriate.
Actually I was thinking about maybe writing a patch to move the table out of the header and into drm_edid.c (or drm_cea.c?) to make it less easy to create duplicates of the table. In that case, using the ARRAY_SIZE macro directly wouldn't be an option.
Well drm_edid.c is the only one accessing these tables anyway, so it they would be in drm_edid.c and static, you could still use ARRAY_SIZE().
Not that I really like the idea of polluting drm_edid.c with all these massive tables.
Too bad cpp doesn't support this kind of thing: #if __BASE_FILE__ != "drm_edid.c" #error ... #endif
On Mon, Jan 14, 2013 at 05:47:13PM +0200, Ville Syrjälä wrote:
On Mon, Jan 14, 2013 at 04:06:13PM +0100, Thierry Reding wrote:
On Mon, Jan 14, 2013 at 04:42:39PM +0200, Ville Syrjälä wrote:
On Mon, Jan 14, 2013 at 03:30:20PM +0100, Thierry Reding wrote:
Since the variable's value is the size of an array, we can assume that it will never be negative.
Signed-off-by: Thierry Reding thierry.reding@avionic-design.de
drivers/gpu/drm/drm_edid_modes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_edid_modes.h b/drivers/gpu/drm/drm_edid_modes.h index 5dbf7d2..d65d863 100644 --- a/drivers/gpu/drm/drm_edid_modes.h +++ b/drivers/gpu/drm/drm_edid_modes.h @@ -771,4 +771,4 @@ static const struct drm_display_mode edid_cea_modes[] = { 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, }; -static const int drm_num_cea_modes = ARRAY_SIZE(edid_cea_modes); +static const unsigned int drm_num_cea_modes = ARRAY_SIZE(edid_cea_modes);
Why do we even have these num_foo things? I think it would be clearer to use ARRAY_SIZE(foo) directly where appropriate.
Actually I was thinking about maybe writing a patch to move the table out of the header and into drm_edid.c (or drm_cea.c?) to make it less easy to create duplicates of the table. In that case, using the ARRAY_SIZE macro directly wouldn't be an option.
Well drm_edid.c is the only one accessing these tables anyway, so it they would be in drm_edid.c and static, you could still use ARRAY_SIZE().
Not that I really like the idea of polluting drm_edid.c with all these massive tables.
There's also the possibility to put the tables along with the functions that access them into a drm_edid_modes.c.
Too bad cpp doesn't support this kind of thing: #if __BASE_FILE__ != "drm_edid.c" #error ... #endif
We could fake that by protecting the tables using #ifdef NEED_CEA_MODES and define NEED_CEA_MODES in drm_edid.c before the drm_edid_modes.h file is included.
Thierry
On Fri, Jan 18, 2013 at 11:30:04AM +0100, Thierry Reding wrote:
On Mon, Jan 14, 2013 at 05:47:13PM +0200, Ville Syrjälä wrote:
On Mon, Jan 14, 2013 at 04:06:13PM +0100, Thierry Reding wrote:
On Mon, Jan 14, 2013 at 04:42:39PM +0200, Ville Syrjälä wrote:
On Mon, Jan 14, 2013 at 03:30:20PM +0100, Thierry Reding wrote:
Since the variable's value is the size of an array, we can assume that it will never be negative.
Signed-off-by: Thierry Reding thierry.reding@avionic-design.de
drivers/gpu/drm/drm_edid_modes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_edid_modes.h b/drivers/gpu/drm/drm_edid_modes.h index 5dbf7d2..d65d863 100644 --- a/drivers/gpu/drm/drm_edid_modes.h +++ b/drivers/gpu/drm/drm_edid_modes.h @@ -771,4 +771,4 @@ static const struct drm_display_mode edid_cea_modes[] = { 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, }; -static const int drm_num_cea_modes = ARRAY_SIZE(edid_cea_modes); +static const unsigned int drm_num_cea_modes = ARRAY_SIZE(edid_cea_modes);
Why do we even have these num_foo things? I think it would be clearer to use ARRAY_SIZE(foo) directly where appropriate.
Actually I was thinking about maybe writing a patch to move the table out of the header and into drm_edid.c (or drm_cea.c?) to make it less easy to create duplicates of the table. In that case, using the ARRAY_SIZE macro directly wouldn't be an option.
Well drm_edid.c is the only one accessing these tables anyway, so it they would be in drm_edid.c and static, you could still use ARRAY_SIZE().
Not that I really like the idea of polluting drm_edid.c with all these massive tables.
There's also the possibility to put the tables along with the functions that access them into a drm_edid_modes.c.
Too bad cpp doesn't support this kind of thing: #if __BASE_FILE__ != "drm_edid.c" #error ... #endif
We could fake that by protecting the tables using #ifdef NEED_CEA_MODES and define NEED_CEA_MODES in drm_edid.c before the drm_edid_modes.h file is included.
Yeah, I suppose there's no better way to do that. It's a bit ugly though.
On Fri, Jan 18, 2013 at 12:51:11PM +0200, Ville Syrjälä wrote:
On Fri, Jan 18, 2013 at 11:30:04AM +0100, Thierry Reding wrote:
On Mon, Jan 14, 2013 at 05:47:13PM +0200, Ville Syrjälä wrote:
On Mon, Jan 14, 2013 at 04:06:13PM +0100, Thierry Reding wrote:
On Mon, Jan 14, 2013 at 04:42:39PM +0200, Ville Syrjälä wrote:
On Mon, Jan 14, 2013 at 03:30:20PM +0100, Thierry Reding wrote:
Since the variable's value is the size of an array, we can assume that it will never be negative.
Signed-off-by: Thierry Reding thierry.reding@avionic-design.de
drivers/gpu/drm/drm_edid_modes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_edid_modes.h b/drivers/gpu/drm/drm_edid_modes.h index 5dbf7d2..d65d863 100644 --- a/drivers/gpu/drm/drm_edid_modes.h +++ b/drivers/gpu/drm/drm_edid_modes.h @@ -771,4 +771,4 @@ static const struct drm_display_mode edid_cea_modes[] = { 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, }; -static const int drm_num_cea_modes = ARRAY_SIZE(edid_cea_modes); +static const unsigned int drm_num_cea_modes = ARRAY_SIZE(edid_cea_modes);
Why do we even have these num_foo things? I think it would be clearer to use ARRAY_SIZE(foo) directly where appropriate.
Actually I was thinking about maybe writing a patch to move the table out of the header and into drm_edid.c (or drm_cea.c?) to make it less easy to create duplicates of the table. In that case, using the ARRAY_SIZE macro directly wouldn't be an option.
Well drm_edid.c is the only one accessing these tables anyway, so it they would be in drm_edid.c and static, you could still use ARRAY_SIZE().
Not that I really like the idea of polluting drm_edid.c with all these massive tables.
There's also the possibility to put the tables along with the functions that access them into a drm_edid_modes.c.
Too bad cpp doesn't support this kind of thing: #if __BASE_FILE__ != "drm_edid.c" #error ... #endif
We could fake that by protecting the tables using #ifdef NEED_CEA_MODES and define NEED_CEA_MODES in drm_edid.c before the drm_edid_modes.h file is included.
Yeah, I suppose there's no better way to do that. It's a bit ugly though.
What about moving them to a separate file altogether? It would involve moving the do_cea_modes() as well, but that would need to be called from drm_edid.c, so it isn't very pretty either.
Thierry
The drm_file and drm_clip_rect structures are used throughout the file but they are never declared nor pulled in through an include. Add forward declarations to make them available.
Signed-off-by: Thierry Reding thierry.reding@avionic-design.de --- include/drm/drm_crtc.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 00d78b5..dd62ca0 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -38,7 +38,8 @@ struct drm_device; struct drm_mode_set; struct drm_framebuffer; struct drm_object_properties; - +struct drm_file; +struct drm_clip_rect;
#define DRM_MODE_OBJECT_CRTC 0xcccccccc #define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0
Add generic helpers to pack HDMI infoframes into binary buffers.
Signed-off-by: Thierry Reding thierry.reding@avionic-design.de --- Changes in v2: - add support for audio, vendor-specific and SPD infoframes - add various validity checks on infoframes - factor out checksum computation
Changes in v3: - introduce HDMI_INFOFRAME_HEADER_SIZE - fix SPD infoframe SDI field offset
drivers/video/Kconfig | 3 + drivers/video/Makefile | 1 + drivers/video/hdmi.c | 332 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/hdmi.h | 221 ++++++++++++++++++++++++++++++++ 4 files changed, 557 insertions(+) create mode 100644 drivers/video/hdmi.c create mode 100644 include/linux/hdmi.h
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 807c7fa..baa3f90 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -54,6 +54,9 @@ config OF_VIDEOMODE help helper to get videomodes from the devicetree
+config HDMI + bool + menuconfig FB tristate "Support for frame buffer devices" ---help--- diff --git a/drivers/video/Makefile b/drivers/video/Makefile index f592f3b..0b50082 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -5,6 +5,7 @@ # Each configuration option enables a list of files.
obj-$(CONFIG_VGASTATE) += vgastate.o +obj-$(CONFIG_HDMI) += hdmi.o obj-y += fb_notify.o obj-$(CONFIG_FB) += fb.o fb-y := fbmem.o fbmon.o fbcmap.o fbsysfs.o \ diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c new file mode 100644 index 0000000..a84e606 --- /dev/null +++ b/drivers/video/hdmi.c @@ -0,0 +1,332 @@ +/* + * Copyright (C) 2012 Avionic Design GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/bitops.h> +#include <linux/errno.h> +#include <linux/export.h> +#include <linux/hdmi.h> +#include <linux/string.h> + +static void hdmi_infoframe_checksum(void *buffer, size_t size) +{ + u8 *ptr = buffer; + u8 csum = 0; + size_t i; + + /* compute checksum */ + for (i = 0; i < size; i++) + csum += ptr[i]; + + ptr[3] = 256 - csum; +} + +/** + * hdmi_avi_infoframe_init() - initialize an HDMI AVI infoframe + * @frame: HDMI AVI infoframe + * + * Returns 0 on success or a negative error code on failure. + */ +int hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame) +{ + if (!frame) + return -EINVAL; + + memset(frame, 0, sizeof(*frame)); + + frame->type = HDMI_INFOFRAME_TYPE_AVI; + frame->version = 2; + frame->length = 13; + + return 0; +} +EXPORT_SYMBOL(hdmi_avi_infoframe_init); + +/** + * hdmi_avi_infoframe_pack() - write HDMI AVI infoframe to binary buffer + * @frame: HDMI AVI 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_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer, + size_t size) +{ + u8 *ptr = buffer; + size_t length; + + if (!frame || !buffer) + return -EINVAL; + + length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; + + if (size < length) + return -ENOSPC; + + memset(buffer, 0, length); + + ptr[0] = frame->type; + ptr[1] = frame->version; + ptr[2] = frame->length; + ptr[3] = 0; /* checksum */ + + /* start infoframe payload */ + ptr += HDMI_INFOFRAME_HEADER_SIZE; + + ptr[0] = ((frame->colorspace & 0x3) << 5) | (frame->scan_mode & 0x3); + + if (frame->active_info_valid) + ptr[0] |= BIT(4); + + if (frame->horizontal_bar_valid) + ptr[0] |= BIT(3); + + if (frame->vertical_bar_valid) + ptr[0] |= BIT(2); + + ptr[1] = ((frame->colorimetry & 0x3) << 6) | + ((frame->picture_aspect & 0x3) << 4) | + (frame->active_aspect & 0xf); + + ptr[2] = ((frame->extended_colorimetry & 0x7) << 4) | + ((frame->quantization_range & 0x3) << 2) | + (frame->nups & 0x3); + + if (frame->itc) + ptr[2] |= BIT(7); + + ptr[3] = frame->video_code & 0x7f; + + ptr[4] = ((frame->ycc_quantization_range & 0x3) << 6) | + ((frame->content_type & 0x3) << 4) | + (frame->pixel_repeat & 0xf); + + ptr[5] = frame->top_bar & 0xff; + ptr[6] = (frame->top_bar >> 8) & 0xff; + ptr[7] = frame->bottom_bar & 0xff; + ptr[8] = (frame->bottom_bar >> 8) & 0xff; + ptr[9] = frame->left_bar & 0xff; + ptr[10] = (frame->left_bar >> 8) & 0xff; + ptr[11] = frame->right_bar & 0xff; + ptr[12] = (frame->right_bar >> 8) & 0xff; + + hdmi_infoframe_checksum(buffer, length); + + return length; +} +EXPORT_SYMBOL(hdmi_avi_infoframe_pack); + +/** + * hdmi_spd_infoframe_init() - initialize an HDMI SPD infoframe + * @frame: HDMI SPD infoframe + * @vendor: vendor string + * @product: product string + * + * Returns 0 on success or a negative error code on failure. + */ +int hdmi_spd_infoframe_init(struct hdmi_spd_infoframe *frame, + const char *vendor, const char *product) +{ + if (!frame) + return -EINVAL; + + memset(frame, 0, sizeof(*frame)); + + frame->type = HDMI_INFOFRAME_TYPE_SPD; + frame->version = 1; + frame->length = 25; + + strncpy(frame->vendor, vendor, sizeof(frame->vendor)); + strncpy(frame->product, product, sizeof(frame->product)); + + return 0; +} +EXPORT_SYMBOL(hdmi_spd_infoframe_init); + +/** + * hdmi_spd_infoframe_pack() - write HDMI SPD infoframe to binary buffer + * @frame: HDMI SPD 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_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, void *buffer, + size_t size) +{ + u8 *ptr = buffer; + size_t length, i; + + if (!frame || !buffer) + return -EINVAL; + + length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; + + if (size < length) + return -ENOSPC; + + memset(buffer, 0, length); + + ptr[0] = frame->type; + ptr[1] = frame->version; + ptr[2] = frame->length; + ptr[3] = 0; /* checksum */ + + /* start infoframe payload */ + ptr += HDMI_INFOFRAME_HEADER_SIZE; + + for (i = 0; i < sizeof(frame->vendor); i++) + ptr[i] = frame->vendor[i]; + + for (i = 0; i < sizeof(frame->product); i++) + ptr[8 + i] = frame->product[i]; + + ptr[24] = frame->sdi; + + hdmi_infoframe_checksum(buffer, length); + + return length; +} +EXPORT_SYMBOL(hdmi_spd_infoframe_pack); + +/** + * hdmi_audio_infoframe_init() - initialize an HDMI audio infoframe + * @frame: HDMI audio infoframe + * + * Returns 0 on success or a negative error code on failure. + */ +int hdmi_audio_infoframe_init(struct hdmi_audio_infoframe *frame) +{ + if (!frame) + return -EINVAL; + + memset(frame, 0, sizeof(*frame)); + + frame->type = HDMI_INFOFRAME_TYPE_AUDIO; + frame->version = 1; + frame->length = 10; + + return 0; +} +EXPORT_SYMBOL(hdmi_audio_infoframe_init); + +/** + * hdmi_audio_infoframe_pack() - write HDMI audio infoframe to binary buffer + * @frame: HDMI audio 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_audio_infoframe_pack(struct hdmi_audio_infoframe *frame, + void *buffer, size_t size) +{ + unsigned char channels; + u8 *ptr = buffer; + size_t length; + + if (!frame || !buffer) + return -EINVAL; + + length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; + + if (size < length) + return -ENOSPC; + + memset(buffer, 0, length); + + if (frame->channels >= 2) + channels = frame->channels - 1; + else + channels = 0; + + ptr[0] = frame->type; + ptr[1] = frame->version; + ptr[2] = frame->length; + ptr[3] = 0; /* checksum */ + + /* start infoframe payload */ + ptr += HDMI_INFOFRAME_HEADER_SIZE; + + ptr[0] = ((frame->coding_type & 0xf) << 4) | (channels & 0x7); + ptr[1] = ((frame->sample_frequency & 0x7) << 2) | + (frame->sample_size & 0x3); + ptr[2] = 0; + ptr[3] = frame->channel_allocation; + ptr[4] = (frame->level_shift_value & 0xf) << 3; + + if (frame->downmix_inhibit) + ptr[4] |= BIT(7); + + hdmi_infoframe_checksum(buffer, length); + + return length; +} +EXPORT_SYMBOL(hdmi_audio_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. + */ +ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame, + void *buffer, size_t size) +{ + u8 *ptr = buffer; + size_t length; + + if (!frame || !buffer) + return -EINVAL; + + length = HDMI_INFOFRAME_HEADER_SIZE + frame->length; + + if (size < length) + return -ENOSPC; + + memset(buffer, 0, length); + + 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); + + return length; +} +EXPORT_SYMBOL(hdmi_vendor_infoframe_pack); diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h new file mode 100644 index 0000000..47d59b1 --- /dev/null +++ b/include/linux/hdmi.h @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2012 Avionic Design GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LINUX_HDMI_H_ +#define __LINUX_HDMI_H_ + +#include <linux/types.h> + +enum hdmi_infoframe_type { + HDMI_INFOFRAME_TYPE_VENDOR = 0x81, + HDMI_INFOFRAME_TYPE_AVI = 0x82, + HDMI_INFOFRAME_TYPE_SPD = 0x83, + HDMI_INFOFRAME_TYPE_AUDIO = 0x84, +}; + +#define HDMI_INFOFRAME_HEADER_SIZE 4 +#define HDMI_AVI_INFOFRAME_SIZE 13 +#define HDMI_SPD_INFOFRAME_SIZE 25 +#define HDMI_AUDIO_INFOFRAME_SIZE 10 + +enum hdmi_colorspace { + HDMI_COLORSPACE_RGB, + HDMI_COLORSPACE_YUV422, + HDMI_COLORSPACE_YUV444, +}; + +enum hdmi_scan_mode { + HDMI_SCAN_MODE_NONE, + HDMI_SCAN_MODE_OVERSCAN, + HDMI_SCAN_MODE_UNDERSCAN, +}; + +enum hdmi_colorimetry { + HDMI_COLORIMETRY_NONE, + HDMI_COLORIMETRY_ITU_601, + HDMI_COLORIMETRY_ITU_709, + HDMI_COLORIMETRY_EXTENDED, +}; + +enum hdmi_picture_aspect { + HDMI_PICTURE_ASPECT_NONE, + HDMI_PICTURE_ASPECT_4_3, + HDMI_PICTURE_ASPECT_16_9, +}; + +enum hdmi_active_aspect { + HDMI_ACTIVE_ASPECT_16_9_TOP = 2, + HDMI_ACTIVE_ASPECT_14_9_TOP = 3, + HDMI_ACTIVE_ASPECT_16_9_CENTER = 4, + HDMI_ACTIVE_ASPECT_PICTURE = 8, + HDMI_ACTIVE_ASPECT_4_3 = 9, + HDMI_ACTIVE_ASPECT_16_9 = 10, + HDMI_ACTIVE_ASPECT_14_9 = 11, + HDMI_ACTIVE_ASPECT_4_3_SP_14_9 = 13, + HDMI_ACTIVE_ASPECT_16_9_SP_14_9 = 14, + HDMI_ACTIVE_ASPECT_16_9_SP_4_3 = 15, +}; + +enum hdmi_extended_colorimetry { + HDMI_EXTENDED_COLORIMETRY_XV_YCC_601, + HDMI_EXTENDED_COLORIMETRY_XV_YCC_709, + HDMI_EXTENDED_COLORIMETRY_S_YCC_601, + HDMI_EXTENDED_COLORIMETRY_ADOBE_YCC_601, + HDMI_EXTENDED_COLORIMETRY_ADOBE_RGB, +}; + +enum hdmi_quantization_range { + HDMI_QUANTIZATION_RANGE_DEFAULT, + HDMI_QUANTIZATION_RANGE_LIMITED, + HDMI_QUANTIZATION_RANGE_FULL, +}; + +/* non-uniform picture scaling */ +enum hdmi_nups { + HDMI_NUPS_UNKNOWN, + HDMI_NUPS_HORIZONTAL, + HDMI_NUPS_VERTICAL, + HDMI_NUPS_BOTH, +}; + +enum hdmi_ycc_quantization_range { + HDMI_YCC_QUANTIZATION_RANGE_LIMITED, + HDMI_YCC_QUANTIZATION_RANGE_FULL, +}; + +enum hdmi_content_type { + HDMI_CONTENT_TYPE_NONE, + HDMI_CONTENT_TYPE_PHOTO, + HDMI_CONTENT_TYPE_CINEMA, + HDMI_CONTENT_TYPE_GAME, +}; + +struct hdmi_avi_infoframe { + enum hdmi_infoframe_type type; + 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; + enum hdmi_colorimetry colorimetry; + enum hdmi_picture_aspect picture_aspect; + enum hdmi_active_aspect active_aspect; + bool itc; + enum hdmi_extended_colorimetry extended_colorimetry; + enum hdmi_quantization_range quantization_range; + enum hdmi_nups nups; + unsigned char video_code; + enum hdmi_ycc_quantization_range ycc_quantization_range; + enum hdmi_content_type content_type; + unsigned char pixel_repeat; + unsigned short top_bar; + unsigned short bottom_bar; + unsigned short left_bar; + unsigned short right_bar; +}; + +int hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame); +ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer, + size_t size); + +enum hdmi_spd_sdi { + HDMI_SPD_SDI_UNKNOWN, + HDMI_SPD_SDI_DSTB, + HDMI_SPD_SDI_DVDP, + HDMI_SPD_SDI_DVHS, + HDMI_SPD_SDI_HDDVR, + HDMI_SPD_SDI_DVC, + HDMI_SPD_SDI_DSC, + HDMI_SPD_SDI_VCD, + HDMI_SPD_SDI_GAME, + HDMI_SPD_SDI_PC, + HDMI_SPD_SDI_BD, + HDMI_SPD_SDI_SCD, +}; + +struct hdmi_spd_infoframe { + enum hdmi_infoframe_type type; + unsigned char version; + unsigned char length; + char vendor[8]; + char product[16]; + enum hdmi_spd_sdi sdi; +}; + +int hdmi_spd_infoframe_init(struct hdmi_spd_infoframe *frame, + const char *vendor, const char *product); +ssize_t hdmi_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, void *buffer, + size_t size); + +enum hdmi_audio_coding_type { + HDMI_AUDIO_CODING_TYPE_STREAM, + HDMI_AUDIO_CODING_TYPE_IEC_60958, + HDMI_AUDIO_CODING_TYPE_AC3, + HDMI_AUDIO_CODING_TYPE_MPEG1, + HDMI_AUDIO_CODING_TYPE_MP3, + HDMI_AUDIO_CODING_TYPE_MPEG2, + HDMI_AUDIO_CODING_TYPE_AAC, + HDMI_AUDIO_CODING_TYPE_DTS, + HDMI_AUDIO_CODING_TYPE_ATRAC, + HDMI_AUDIO_CODING_TYPE_ONE_BIT_AUDIO, + HDMI_AUDIO_CODING_TYPE_DOLBY_DIGITAL_PLUS, + HDMI_AUDIO_CODING_TYPE_DTS_HD, + HDMI_AUDIO_CODING_TYPE_MAT_MLP, + HDMI_AUDIO_CODING_TYPE_DST, + HDMI_AUDIO_CODING_TYPE_WMPRO, +}; + +enum hdmi_audio_sample_size { + HDMI_AUDIO_SAMPLE_SIZE_STREAM, + HDMI_AUDIO_SAMPLE_SIZE_16, + HDMI_AUDIO_SAMPLE_SIZE_20, + HDMI_AUDIO_SAMPLE_SIZE_24, +}; + +enum hdmi_audio_sample_frequency { + HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM, + HDMI_AUDIO_SAMPLE_FREQUENCY_32000, + HDMI_AUDIO_SAMPLE_FREQUENCY_44100, + HDMI_AUDIO_SAMPLE_FREQUENCY_48000, + HDMI_AUDIO_SAMPLE_FREQUENCY_88200, + HDMI_AUDIO_SAMPLE_FREQUENCY_96000, + HDMI_AUDIO_SAMPLE_FREQUENCY_176400, + HDMI_AUDIO_SAMPLE_FREQUENCY_192000, +}; + +struct hdmi_audio_infoframe { + enum hdmi_infoframe_type type; + unsigned char version; + unsigned char length; + unsigned char channels; + enum hdmi_audio_coding_type coding_type; + enum hdmi_audio_sample_size sample_size; + enum hdmi_audio_sample_frequency sample_frequency; + unsigned char channel_allocation; + unsigned char level_shift_value; + bool downmix_inhibit; + +}; + +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); + +#endif /* _DRM_HDMI_H */
On Mon, Jan 14, 2013 at 03:30:22PM +0100, Thierry Reding wrote:
Add generic helpers to pack HDMI infoframes into binary buffers.
Signed-off-by: Thierry Reding thierry.reding@avionic-design.de
Changes in v2:
- add support for audio, vendor-specific and SPD infoframes
- add various validity checks on infoframes
- factor out checksum computation
Changes in v3:
- introduce HDMI_INFOFRAME_HEADER_SIZE
- fix SPD infoframe SDI field offset
drivers/video/Kconfig | 3 + drivers/video/Makefile | 1 + drivers/video/hdmi.c | 332 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/hdmi.h | 221 ++++++++++++++++++++++++++++++++ 4 files changed, 557 insertions(+) create mode 100644 drivers/video/hdmi.c create mode 100644 include/linux/hdmi.h
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 807c7fa..baa3f90 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -54,6 +54,9 @@ config OF_VIDEOMODE help helper to get videomodes from the devicetree
+config HDMI
- bool
menuconfig FB tristate "Support for frame buffer devices" ---help--- diff --git a/drivers/video/Makefile b/drivers/video/Makefile index f592f3b..0b50082 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -5,6 +5,7 @@ # Each configuration option enables a list of files.
obj-$(CONFIG_VGASTATE) += vgastate.o +obj-$(CONFIG_HDMI) += hdmi.o obj-y += fb_notify.o obj-$(CONFIG_FB) += fb.o fb-y := fbmem.o fbmon.o fbcmap.o fbsysfs.o \ diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c new file mode 100644 index 0000000..a84e606 --- /dev/null +++ b/drivers/video/hdmi.c @@ -0,0 +1,332 @@ +/*
- Copyright (C) 2012 Avionic Design GmbH
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- */
+#include <linux/bitops.h> +#include <linux/errno.h> +#include <linux/export.h> +#include <linux/hdmi.h> +#include <linux/string.h>
+static void hdmi_infoframe_checksum(void *buffer, size_t size) +{
- u8 *ptr = buffer;
- u8 csum = 0;
- size_t i;
- /* compute checksum */
- for (i = 0; i < size; i++)
csum += ptr[i];
- ptr[3] = 256 - csum;
+}
+/**
- hdmi_avi_infoframe_init() - initialize an HDMI AVI infoframe
- @frame: HDMI AVI infoframe
- Returns 0 on success or a negative error code on failure.
- */
+int hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame) +{
- if (!frame)
return -EINVAL;
There's quite a bit of error checking all around which seems a bit pointless since this is all internal stuff. Any errors are bugs in some driver code, so just letting the thing oops would be fine in my opinion.
- memset(frame, 0, sizeof(*frame));
- frame->type = HDMI_INFOFRAME_TYPE_AVI;
- frame->version = 2;
- frame->length = 13;
- return 0;
+} +EXPORT_SYMBOL(hdmi_avi_infoframe_init);
+/**
- hdmi_avi_infoframe_pack() - write HDMI AVI infoframe to binary buffer
- @frame: HDMI AVI 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_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer,
size_t size)
+{
- u8 *ptr = buffer;
- size_t length;
- if (!frame || !buffer)
return -EINVAL;
- length = HDMI_INFOFRAME_HEADER_SIZE + frame->length;
- if (size < length)
return -ENOSPC;
- memset(buffer, 0, length);
- ptr[0] = frame->type;
- ptr[1] = frame->version;
- ptr[2] = frame->length;
- ptr[3] = 0; /* checksum */
- /* start infoframe payload */
- ptr += HDMI_INFOFRAME_HEADER_SIZE;
- ptr[0] = ((frame->colorspace & 0x3) << 5) | (frame->scan_mode & 0x3);
- if (frame->active_info_valid)
ptr[0] |= BIT(4);
- if (frame->horizontal_bar_valid)
ptr[0] |= BIT(3);
- if (frame->vertical_bar_valid)
ptr[0] |= BIT(2);
- ptr[1] = ((frame->colorimetry & 0x3) << 6) |
((frame->picture_aspect & 0x3) << 4) |
(frame->active_aspect & 0xf);
- ptr[2] = ((frame->extended_colorimetry & 0x7) << 4) |
((frame->quantization_range & 0x3) << 2) |
(frame->nups & 0x3);
- if (frame->itc)
ptr[2] |= BIT(7);
- ptr[3] = frame->video_code & 0x7f;
- ptr[4] = ((frame->ycc_quantization_range & 0x3) << 6) |
((frame->content_type & 0x3) << 4) |
(frame->pixel_repeat & 0xf);
- ptr[5] = frame->top_bar & 0xff;
- ptr[6] = (frame->top_bar >> 8) & 0xff;
- ptr[7] = frame->bottom_bar & 0xff;
- ptr[8] = (frame->bottom_bar >> 8) & 0xff;
- ptr[9] = frame->left_bar & 0xff;
- ptr[10] = (frame->left_bar >> 8) & 0xff;
- ptr[11] = frame->right_bar & 0xff;
- ptr[12] = (frame->right_bar >> 8) & 0xff;
- hdmi_infoframe_checksum(buffer, length);
- return length;
+} +EXPORT_SYMBOL(hdmi_avi_infoframe_pack);
+/**
- hdmi_spd_infoframe_init() - initialize an HDMI SPD infoframe
- @frame: HDMI SPD infoframe
- @vendor: vendor string
- @product: product string
- Returns 0 on success or a negative error code on failure.
- */
+int hdmi_spd_infoframe_init(struct hdmi_spd_infoframe *frame,
const char *vendor, const char *product)
+{
- if (!frame)
return -EINVAL;
- memset(frame, 0, sizeof(*frame));
- frame->type = HDMI_INFOFRAME_TYPE_SPD;
- frame->version = 1;
- frame->length = 25;
- strncpy(frame->vendor, vendor, sizeof(frame->vendor));
- strncpy(frame->product, product, sizeof(frame->product));
- return 0;
+} +EXPORT_SYMBOL(hdmi_spd_infoframe_init);
+/**
- hdmi_spd_infoframe_pack() - write HDMI SPD infoframe to binary buffer
- @frame: HDMI SPD 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_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, void *buffer,
size_t size)
+{
- u8 *ptr = buffer;
- size_t length, i;
- if (!frame || !buffer)
return -EINVAL;
- length = HDMI_INFOFRAME_HEADER_SIZE + frame->length;
- if (size < length)
return -ENOSPC;
- memset(buffer, 0, length);
- ptr[0] = frame->type;
- ptr[1] = frame->version;
- ptr[2] = frame->length;
- ptr[3] = 0; /* checksum */
- /* start infoframe payload */
- ptr += HDMI_INFOFRAME_HEADER_SIZE;
- for (i = 0; i < sizeof(frame->vendor); i++)
ptr[i] = frame->vendor[i];
- for (i = 0; i < sizeof(frame->product); i++)
ptr[8 + i] = frame->product[i];
memcpy()
- ptr[24] = frame->sdi;
- hdmi_infoframe_checksum(buffer, length);
- return length;
+} +EXPORT_SYMBOL(hdmi_spd_infoframe_pack);
+/**
- hdmi_audio_infoframe_init() - initialize an HDMI audio infoframe
- @frame: HDMI audio infoframe
- Returns 0 on success or a negative error code on failure.
- */
+int hdmi_audio_infoframe_init(struct hdmi_audio_infoframe *frame) +{
- if (!frame)
return -EINVAL;
- memset(frame, 0, sizeof(*frame));
- frame->type = HDMI_INFOFRAME_TYPE_AUDIO;
- frame->version = 1;
- frame->length = 10;
- return 0;
+} +EXPORT_SYMBOL(hdmi_audio_infoframe_init);
+/**
- hdmi_audio_infoframe_pack() - write HDMI audio infoframe to binary buffer
- @frame: HDMI audio 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_audio_infoframe_pack(struct hdmi_audio_infoframe *frame,
void *buffer, size_t size)
+{
- unsigned char channels;
- u8 *ptr = buffer;
- size_t length;
- if (!frame || !buffer)
return -EINVAL;
- length = HDMI_INFOFRAME_HEADER_SIZE + frame->length;
- if (size < length)
return -ENOSPC;
- memset(buffer, 0, length);
- if (frame->channels >= 2)
channels = frame->channels - 1;
- else
channels = 0;
- ptr[0] = frame->type;
- ptr[1] = frame->version;
- ptr[2] = frame->length;
- ptr[3] = 0; /* checksum */
- /* start infoframe payload */
- ptr += HDMI_INFOFRAME_HEADER_SIZE;
- ptr[0] = ((frame->coding_type & 0xf) << 4) | (channels & 0x7);
- ptr[1] = ((frame->sample_frequency & 0x7) << 2) |
(frame->sample_size & 0x3);
- ptr[2] = 0;
- ptr[3] = frame->channel_allocation;
- ptr[4] = (frame->level_shift_value & 0xf) << 3;
- if (frame->downmix_inhibit)
ptr[4] |= BIT(7);
- hdmi_infoframe_checksum(buffer, length);
- return length;
+} +EXPORT_SYMBOL(hdmi_audio_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.
- */
+ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame,
void *buffer, size_t size)
+{
- u8 *ptr = buffer;
- size_t length;
- if (!frame || !buffer)
return -EINVAL;
- length = HDMI_INFOFRAME_HEADER_SIZE + frame->length;
- if (size < length)
return -ENOSPC;
- memset(buffer, 0, length);
- 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);
- return length;
+} +EXPORT_SYMBOL(hdmi_vendor_infoframe_pack); diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h new file mode 100644 index 0000000..47d59b1 --- /dev/null +++ b/include/linux/hdmi.h @@ -0,0 +1,221 @@ +/*
- Copyright (C) 2012 Avionic Design GmbH
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- */
+#ifndef __LINUX_HDMI_H_ +#define __LINUX_HDMI_H_
+#include <linux/types.h>
+enum hdmi_infoframe_type {
- HDMI_INFOFRAME_TYPE_VENOR = 0x81,
- HDMI_INFOFRAME_TYPE_AVI = 0x82,
- HDMI_INFOFRAME_TYPE_SPD = 0x83,
- HDMI_INFOFRAME_TYPE_AUDIO = 0x84,
+};
+#define HDMI_INFOFRAME_HEADER_SIZE 4 +#define HDMI_AVI_INFOFRAME_SIZE 13 +#define HDMI_SPD_INFOFRAME_SIZE 25 +#define HDMI_AUDIO_INFOFRAME_SIZE 10
+enum hdmi_colorspace {
- HDMI_COLORSPACE_RGB,
- HDMI_COLORSPACE_YUV422,
- HDMI_COLORSPACE_YUV444,
+};
+enum hdmi_scan_mode {
- HDMI_SCAN_MODE_NONE,
- HDMI_SCAN_MODE_OVERSCAN,
- HDMI_SCAN_MODE_UNDERSCAN,
+};
+enum hdmi_colorimetry {
- HDMI_COLORIMETRY_NONE,
- HDMI_COLORIMETRY_ITU_601,
- HDMI_COLORIMETRY_ITU_709,
- HDMI_COLORIMETRY_EXTENDED,
+};
+enum hdmi_picture_aspect {
- HDMI_PICTURE_ASPECT_NONE,
- HDMI_PICTURE_ASPECT_4_3,
- HDMI_PICTURE_ASPECT_16_9,
+};
+enum hdmi_active_aspect {
- HDMI_ACTIVE_ASPECT_16_9_TOP = 2,
- HDMI_ACTIVE_ASPECT_14_9_TOP = 3,
- HDMI_ACTIVE_ASPECT_16_9_CENTER = 4,
- HDMI_ACTIVE_ASPECT_PICTURE = 8,
- HDMI_ACTIVE_ASPECT_4_3 = 9,
- HDMI_ACTIVE_ASPECT_16_9 = 10,
- HDMI_ACTIVE_ASPECT_14_9 = 11,
- HDMI_ACTIVE_ASPECT_4_3_SP_14_9 = 13,
- HDMI_ACTIVE_ASPECT_16_9_SP_14_9 = 14,
- HDMI_ACTIVE_ASPECT_16_9_SP_4_3 = 15,
+};
+enum hdmi_extended_colorimetry {
- HDMI_EXTENDED_COLORIMETRY_XV_YCC_601,
- HDMI_EXTENDED_COLORIMETRY_XV_YCC_709,
- HDMI_EXTENDED_COLORIMETRY_S_YCC_601,
- HDMI_EXTENDED_COLORIMETRY_ADOBE_YCC_601,
- HDMI_EXTENDED_COLORIMETRY_ADOBE_RGB,
+};
+enum hdmi_quantization_range {
- HDMI_QUANTIZATION_RANGE_DEFAULT,
- HDMI_QUANTIZATION_RANGE_LIMITED,
- HDMI_QUANTIZATION_RANGE_FULL,
+};
+/* non-uniform picture scaling */ +enum hdmi_nups {
- HDMI_NUPS_UNKNOWN,
- HDMI_NUPS_HORIZONTAL,
- HDMI_NUPS_VERTICAL,
- HDMI_NUPS_BOTH,
+};
+enum hdmi_ycc_quantization_range {
- HDMI_YCC_QUANTIZATION_RANGE_LIMITED,
- HDMI_YCC_QUANTIZATION_RANGE_FULL,
+};
+enum hdmi_content_type {
- HDMI_CONTENT_TYPE_NONE,
- HDMI_CONTENT_TYPE_PHOTO,
- HDMI_CONTENT_TYPE_CINEMA,
- HDMI_CONTENT_TYPE_GAME,
+};
+struct hdmi_avi_infoframe {
- enum hdmi_infoframe_type type;
- 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;
- enum hdmi_colorimetry colorimetry;
- enum hdmi_picture_aspect picture_aspect;
- enum hdmi_active_aspect active_aspect;
- bool itc;
- enum hdmi_extended_colorimetry extended_colorimetry;
- enum hdmi_quantization_range quantization_range;
- enum hdmi_nups nups;
- unsigned char video_code;
- enum hdmi_ycc_quantization_range ycc_quantization_range;
- enum hdmi_content_type content_type;
- unsigned char pixel_repeat;
- unsigned short top_bar;
- unsigned short bottom_bar;
- unsigned short left_bar;
- unsigned short right_bar;
+};
+int hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame); +ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer,
size_t size);
+enum hdmi_spd_sdi {
- HDMI_SPD_SDI_UNKNOWN,
- HDMI_SPD_SDI_DSTB,
- HDMI_SPD_SDI_DVDP,
- HDMI_SPD_SDI_DVHS,
- HDMI_SPD_SDI_HDDVR,
- HDMI_SPD_SDI_DVC,
- HDMI_SPD_SDI_DSC,
- HDMI_SPD_SDI_VCD,
- HDMI_SPD_SDI_GAME,
- HDMI_SPD_SDI_PC,
- HDMI_SPD_SDI_BD,
- HDMI_SPD_SDI_SCD,
I believe SACD is a more correct name.
HD DVD and PMP are missing from this list.
+};
+struct hdmi_spd_infoframe {
- enum hdmi_infoframe_type type;
- unsigned char version;
- unsigned char length;
- char vendor[8];
- char product[16];
- enum hdmi_spd_sdi sdi;
+};
+int hdmi_spd_infoframe_init(struct hdmi_spd_infoframe *frame,
const char *vendor, const char *product);
+ssize_t hdmi_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, void *buffer,
size_t size);
+enum hdmi_audio_coding_type {
- HDMI_AUDIO_CODING_TYPE_STREAM,
- HDMI_AUDIO_CODING_TYPE_IEC_60958,
- HDMI_AUDIO_CODING_TYPE_AC3,
- HDMI_AUDIO_CODING_TYPE_MPEG1,
- HDMI_AUDIO_CODING_TYPE_MP3,
- HDMI_AUDIO_CODING_TYPE_MPEG2,
- HDMI_AUDIO_CODING_TYPE_AAC,
- HDMI_AUDIO_CODING_TYPE_DTS,
- HDMI_AUDIO_CODING_TYPE_ATRAC,
- HDMI_AUDIO_CODING_TYPE_ONE_BIT_AUDIO,
- HDMI_AUDIO_CODING_TYPE_DOLBY_DIGITAL_PLUS,
- HDMI_AUDIO_CODING_TYPE_DTS_HD,
- HDMI_AUDIO_CODING_TYPE_MAT_MLP,
- HDMI_AUDIO_CODING_TYPE_DST,
- HDMI_AUDIO_CODING_TYPE_WMPRO,
These don't always quite match the names in CEA-861-E. Wouldn't it be better to use matching names?
Also the audio coding extension type is missing.
+};
+enum hdmi_audio_sample_size {
- HDMI_AUDIO_SAMPLE_SIZE_STREAM,
- HDMI_AUDIO_SAMPLE_SIZE_16,
- HDMI_AUDIO_SAMPLE_SIZE_20,
- HDMI_AUDIO_SAMPLE_SIZE_24,
+};
+enum hdmi_audio_sample_frequency {
- HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM,
- HDMI_AUDIO_SAMPLE_FREQUENCY_32000,
- HDMI_AUDIO_SAMPLE_FREQUENCY_44100,
- HDMI_AUDIO_SAMPLE_FREQUENCY_48000,
- HDMI_AUDIO_SAMPLE_FREQUENCY_88200,
- HDMI_AUDIO_SAMPLE_FREQUENCY_96000,
- HDMI_AUDIO_SAMPLE_FREQUENCY_176400,
- HDMI_AUDIO_SAMPLE_FREQUENCY_192000,
+};
+struct hdmi_audio_infoframe {
- enum hdmi_infoframe_type type;
- unsigned char version;
- unsigned char length;
- unsigned char channels;
- enum hdmi_audio_coding_type coding_type;
- enum hdmi_audio_sample_size sample_size;
- enum hdmi_audio_sample_frequency sample_frequency;
- unsigned char channel_allocation;
- unsigned char level_shift_value;
- bool downmix_inhibit;
+};
+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);
+#endif /* _DRM_HDMI_H */
1.8.1
dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
On Wed, Jan 16, 2013 at 03:27:14PM +0200, Ville Syrjälä wrote:
On Mon, Jan 14, 2013 at 03:30:22PM +0100, Thierry Reding wrote:
Add generic helpers to pack HDMI infoframes into binary buffers.
[...]
+/**
- hdmi_avi_infoframe_init() - initialize an HDMI AVI infoframe
- @frame: HDMI AVI infoframe
- Returns 0 on success or a negative error code on failure.
- */
+int hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame) +{
- if (!frame)
return -EINVAL;
There's quite a bit of error checking all around which seems a bit pointless since this is all internal stuff. Any errors are bugs in some driver code, so just letting the thing oops would be fine in my opinion.
Okay, I can remove some of the more obvious checks. Checking for space requirements might still be useful, so I'll leave that in.
+ssize_t hdmi_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, void *buffer,
size_t size)
[...]
- for (i = 0; i < sizeof(frame->vendor); i++)
ptr[i] = frame->vendor[i];
- for (i = 0; i < sizeof(frame->product); i++)
ptr[8 + i] = frame->product[i];
memcpy()
Done.
+enum hdmi_spd_sdi {
- HDMI_SPD_SDI_UNKNOWN,
- HDMI_SPD_SDI_DSTB,
- HDMI_SPD_SDI_DVDP,
- HDMI_SPD_SDI_DVHS,
- HDMI_SPD_SDI_HDDVR,
- HDMI_SPD_SDI_DVC,
- HDMI_SPD_SDI_DSC,
- HDMI_SPD_SDI_VCD,
- HDMI_SPD_SDI_GAME,
- HDMI_SPD_SDI_PC,
- HDMI_SPD_SDI_BD,
- HDMI_SPD_SDI_SCD,
I believe SACD is a more correct name.
Done.
HD DVD and PMP are missing from this list.
I can't find any reference to those in CEA-861-D. HDMI 1.4 doesn't have them either. Can you provide any pointers?
+enum hdmi_audio_coding_type {
- HDMI_AUDIO_CODING_TYPE_STREAM,
- HDMI_AUDIO_CODING_TYPE_IEC_60958,
- HDMI_AUDIO_CODING_TYPE_AC3,
- HDMI_AUDIO_CODING_TYPE_MPEG1,
- HDMI_AUDIO_CODING_TYPE_MP3,
- HDMI_AUDIO_CODING_TYPE_MPEG2,
- HDMI_AUDIO_CODING_TYPE_AAC,
- HDMI_AUDIO_CODING_TYPE_DTS,
- HDMI_AUDIO_CODING_TYPE_ATRAC,
- HDMI_AUDIO_CODING_TYPE_ONE_BIT_AUDIO,
- HDMI_AUDIO_CODING_TYPE_DOLBY_DIGITAL_PLUS,
- HDMI_AUDIO_CODING_TYPE_DTS_HD,
- HDMI_AUDIO_CODING_TYPE_MAT_MLP,
- HDMI_AUDIO_CODING_TYPE_DST,
- HDMI_AUDIO_CODING_TYPE_WMPRO,
These don't always quite match the names in CEA-861-E. Wouldn't it be better to use matching names?
Are HD-DVD and PMP above listed in CEA-861-E? I'll need to find a copy of it and will go over this again and match the names to those in the document.
Also the audio coding extension type is missing.
I assume this is also part of CEA-861-E as I can't find a reference in -D?
Thanks for reviewing, Thierry
On Fri, Jan 18, 2013 at 09:56:36AM +0100, Thierry Reding wrote:
On Wed, Jan 16, 2013 at 03:27:14PM +0200, Ville Syrjälä wrote:
On Mon, Jan 14, 2013 at 03:30:22PM +0100, Thierry Reding wrote:
Add generic helpers to pack HDMI infoframes into binary buffers.
[...]
+/**
- hdmi_avi_infoframe_init() - initialize an HDMI AVI infoframe
- @frame: HDMI AVI infoframe
- Returns 0 on success or a negative error code on failure.
- */
+int hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame) +{
- if (!frame)
return -EINVAL;
There's quite a bit of error checking all around which seems a bit pointless since this is all internal stuff. Any errors are bugs in some driver code, so just letting the thing oops would be fine in my opinion.
Okay, I can remove some of the more obvious checks. Checking for space requirements might still be useful, so I'll leave that in.
+ssize_t hdmi_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, void *buffer,
size_t size)
[...]
- for (i = 0; i < sizeof(frame->vendor); i++)
ptr[i] = frame->vendor[i];
- for (i = 0; i < sizeof(frame->product); i++)
ptr[8 + i] = frame->product[i];
memcpy()
Done.
+enum hdmi_spd_sdi {
- HDMI_SPD_SDI_UNKNOWN,
- HDMI_SPD_SDI_DSTB,
- HDMI_SPD_SDI_DVDP,
- HDMI_SPD_SDI_DVHS,
- HDMI_SPD_SDI_HDDVR,
- HDMI_SPD_SDI_DVC,
- HDMI_SPD_SDI_DSC,
- HDMI_SPD_SDI_VCD,
- HDMI_SPD_SDI_GAME,
- HDMI_SPD_SDI_PC,
- HDMI_SPD_SDI_BD,
- HDMI_SPD_SDI_SCD,
I believe SACD is a more correct name.
Done.
HD DVD and PMP are missing from this list.
I can't find any reference to those in CEA-861-D. HDMI 1.4 doesn't have them either. Can you provide any pointers?
CEA-861-E has them.
+enum hdmi_audio_coding_type {
- HDMI_AUDIO_CODING_TYPE_STREAM,
- HDMI_AUDIO_CODING_TYPE_IEC_60958,
- HDMI_AUDIO_CODING_TYPE_AC3,
- HDMI_AUDIO_CODING_TYPE_MPEG1,
- HDMI_AUDIO_CODING_TYPE_MP3,
- HDMI_AUDIO_CODING_TYPE_MPEG2,
- HDMI_AUDIO_CODING_TYPE_AAC,
- HDMI_AUDIO_CODING_TYPE_DTS,
- HDMI_AUDIO_CODING_TYPE_ATRAC,
- HDMI_AUDIO_CODING_TYPE_ONE_BIT_AUDIO,
- HDMI_AUDIO_CODING_TYPE_DOLBY_DIGITAL_PLUS,
- HDMI_AUDIO_CODING_TYPE_DTS_HD,
- HDMI_AUDIO_CODING_TYPE_MAT_MLP,
- HDMI_AUDIO_CODING_TYPE_DST,
- HDMI_AUDIO_CODING_TYPE_WMPRO,
These don't always quite match the names in CEA-861-E. Wouldn't it be better to use matching names?
Are HD-DVD and PMP above listed in CEA-861-E? I'll need to find a copy of it and will go over this again and match the names to those in the document.
Great.
Also the audio coding extension type is missing.
I assume this is also part of CEA-861-E as I can't find a reference in -D?
Yeah, I only checked against CEA-861-E.
Add a generic helper to fill in an HDMI AVI infoframe with data extracted from a DRM display mode.
Signed-off-by: Thierry Reding thierry.reding@avionic-design.de --- Changes in v2: - reuse CEA modes defined in drm_edid_modes.h - don't use pixel aspect ratio for picture aspect
Changes in v3: - move implementation into drm_edid.[ch]
drivers/gpu/drm/Kconfig | 1 + drivers/gpu/drm/drm_edid.c | 33 +++++++++++++++++++++++++++++++++ include/drm/drm_edid.h | 6 ++++++ 3 files changed, 40 insertions(+)
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 983201b..92d2294 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -7,6 +7,7 @@ menuconfig DRM tristate "Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)" depends on (AGP || AGP=n) && !EMULATED_CMPXCHG && MMU + select HDMI select I2C select I2C_ALGOBIT select DMA_SHARED_BUFFER diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 5a3770f..1cfafc0 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -29,6 +29,7 @@ */ #include <linux/kernel.h> #include <linux/slab.h> +#include <linux/hdmi.h> #include <linux/i2c.h> #include <linux/module.h> #include <drm/drmP.h> @@ -2098,3 +2099,35 @@ uint8_t drm_mode_cea_vic(const struct drm_display_mode *mode) return 0; } EXPORT_SYMBOL(drm_mode_cea_vic); + +/** + * drm_hdmi_avi_infoframe_from_display_mode() - fill an HDMI AVI infoframe with + * data from a DRM display mode + * @frame: HDMI AVI infoframe + * @mode: DRM display mode + * + * Returns 0 on success or a negative error code on failure. + */ +int +drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame, + const struct drm_display_mode *mode) +{ + int err; + + if (!frame || !mode) + return -EINVAL; + + err = hdmi_avi_infoframe_init(frame); + if (err < 0) + return err; + + frame->video_code = drm_match_cea_mode(mode); + if (!frame->video_code) + return 0; + + frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE; + frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE; + + return 0; +} +EXPORT_SYMBOL(drm_hdmi_avi_infoframe_from_display_mode); diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index 0cac551..5da1b4a 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -247,6 +247,8 @@ struct edid { struct drm_encoder; struct drm_connector; struct drm_display_mode; +struct hdmi_avi_infoframe; + void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid); int drm_av_sync_delay(struct drm_connector *connector, struct drm_display_mode *mode); @@ -254,4 +256,8 @@ struct drm_connector *drm_select_eld(struct drm_encoder *encoder, struct drm_display_mode *mode); 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); + #endif /* __DRM_EDID_H__ */
Add a reference section about the EDID helper functions to the DRM documentation.
Signed-off-by: Thierry Reding thierry.reding@avionic-design.de --- Documentation/DocBook/drm.tmpl | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 4ee2304..34249bb 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -2111,6 +2111,10 @@ void intel_crt_init(struct drm_device *dev) !Iinclude/drm/drm_dp_helper.h !Edrivers/gpu/drm/drm_dp_helper.c </sect2> + <sect2> + <title>EDID Helper Functions Reference</title> +!Edrivers/gpu/drm/drm_edid.c + </sect2> </sect1>
<!-- Internals: vertical blanking -->
Use the generic HDMI infoframe helpers to get rid of the NVIDIA Tegra reimplementation.
Signed-off-by: Thierry Reding thierry.reding@avionic-design.de --- drivers/gpu/drm/tegra/Kconfig | 1 + drivers/gpu/drm/tegra/hdmi.c | 226 ++++++++++++++++++++---------------------- drivers/gpu/drm/tegra/hdmi.h | 189 ----------------------------------- 3 files changed, 110 insertions(+), 306 deletions(-)
diff --git a/drivers/gpu/drm/tegra/Kconfig b/drivers/gpu/drm/tegra/Kconfig index 721483b..50d9c94 100644 --- a/drivers/gpu/drm/tegra/Kconfig +++ b/drivers/gpu/drm/tegra/Kconfig @@ -4,6 +4,7 @@ config DRM_TEGRA select DRM_KMS_HELPER select DRM_GEM_CMA_HELPER select DRM_KMS_CMA_HELPER + select DRM_HDMI select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index e060c7e..0daee8e 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -10,6 +10,7 @@ #include <linux/clk.h> #include <linux/debugfs.h> #include <linux/gpio.h> +#include <linux/hdmi.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> @@ -17,6 +18,8 @@
#include <mach/clk.h>
+#include <drm/drm_edid.h> + #include "hdmi.h" #include "drm.h" #include "dc.h" @@ -401,54 +404,65 @@ static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi, unsigned int pclk) return 0; }
-static void tegra_hdmi_write_infopack(struct tegra_hdmi *hdmi, - unsigned int offset, u8 type, - u8 version, void *data, size_t size) +static inline unsigned long tegra_hdmi_subpack(const u8 *ptr, size_t size) { - unsigned long value; - u8 *ptr = data; - u32 subpack[2]; + unsigned long value = 0; size_t i; - u8 csum;
- /* first byte of data is the checksum */ - csum = type + version + size - 1; + for (i = size; i > 0; i--) + value = (value << 8) | ptr[i - 1];
- for (i = 1; i < size; i++) - csum += ptr[i]; + return value; +}
- ptr[0] = 0x100 - csum; +static void tegra_hdmi_write_infopack(struct tegra_hdmi *hdmi, const void *data, + size_t size) +{ + const u8 *ptr = data; + unsigned long offset; + unsigned long value; + size_t i, j;
- value = INFOFRAME_HEADER_TYPE(type) | - INFOFRAME_HEADER_VERSION(version) | - INFOFRAME_HEADER_LEN(size - 1); - tegra_hdmi_writel(hdmi, value, offset); + switch (ptr[0]) { + case HDMI_INFOFRAME_TYPE_AVI: + offset = HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER; + break;
- /* The audio inforame only has one set of subpack registers. The hdmi - * block pads the rest of the data as per the spec so we have to fixup - * the length before filling in the subpacks. - */ - if (offset == HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER) - size = 6; + case HDMI_INFOFRAME_TYPE_AUDIO: + offset = HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER; + break;
- /* each subpack 7 bytes devided into: - * subpack_low - bytes 0 - 3 - * subpack_high - bytes 4 - 6 (with byte 7 padded to 0x00) - */ - for (i = 0; i < size; i++) { - size_t index = i % 7; + case HDMI_INFOFRAME_TYPE_VENDOR: + offset = HDMI_NV_PDISP_HDMI_GENERIC_HEADER; + break; + + default: + dev_err(hdmi->dev, "unsupported infoframe type: %02x\n", + ptr[0]); + return; + } + + value = INFOFRAME_HEADER_TYPE(ptr[0]) | + INFOFRAME_HEADER_VERSION(ptr[1]) | + INFOFRAME_HEADER_LEN(ptr[2]); + tegra_hdmi_writel(hdmi, value, offset); + offset++;
- if (index == 0) - memset(subpack, 0x0, sizeof(subpack)); + /* + * Each subpack contains 7 bytes, divided into: + * - subpack_low: bytes 0 - 3 + * - subpack_high: bytes 4 - 6 (with byte 7 padded to 0x00) + */ + for (i = 3, j = 0; i < size; i += 7, j += 8) { + size_t rem = size - i, num = min_t(size_t, rem, 4);
- ((u8 *)subpack)[index] = ptr[i]; + value = tegra_hdmi_subpack(&ptr[i], num); + tegra_hdmi_writel(hdmi, value, offset++);
- if (index == 6 || (i + 1 == size)) { - unsigned int reg = offset + 1 + (i / 7) * 2; + num = min_t(size_t, rem - num, 3);
- tegra_hdmi_writel(hdmi, subpack[0], reg); - tegra_hdmi_writel(hdmi, subpack[1], reg + 1); - } + value = tegra_hdmi_subpack(&ptr[i + 4], num); + tegra_hdmi_writel(hdmi, value, offset++); } }
@@ -456,9 +470,8 @@ static void tegra_hdmi_setup_avi_infoframe(struct tegra_hdmi *hdmi, struct drm_display_mode *mode) { struct hdmi_avi_infoframe frame; - unsigned int h_front_porch; - unsigned int hsize = 16; - unsigned int vsize = 9; + u8 buffer[17]; + ssize_t err;
if (hdmi->dvi) { tegra_hdmi_writel(hdmi, 0, @@ -466,69 +479,19 @@ static void tegra_hdmi_setup_avi_infoframe(struct tegra_hdmi *hdmi, return; }
- h_front_porch = mode->hsync_start - mode->hdisplay; - memset(&frame, 0, sizeof(frame)); - frame.r = HDMI_AVI_R_SAME; - - switch (mode->vdisplay) { - case 480: - if (mode->hdisplay == 640) { - frame.m = HDMI_AVI_M_4_3; - frame.vic = 1; - } else { - frame.m = HDMI_AVI_M_16_9; - frame.vic = 3; - } - break; - - case 576: - if (((hsize * 10) / vsize) > 14) { - frame.m = HDMI_AVI_M_16_9; - frame.vic = 18; - } else { - frame.m = HDMI_AVI_M_4_3; - frame.vic = 17; - } - break; - - case 720: - case 1470: /* stereo mode */ - frame.m = HDMI_AVI_M_16_9; - - if (h_front_porch == 110) - frame.vic = 4; - else - frame.vic = 19; - break; - - case 1080: - case 2205: /* stereo mode */ - frame.m = HDMI_AVI_M_16_9; - - switch (h_front_porch) { - case 88: - frame.vic = 16; - break; - - case 528: - frame.vic = 31; - break; - - default: - frame.vic = 32; - break; - } - break; + err = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode); + if (err < 0) { + dev_err(hdmi->dev, "failed to setup AVI infoframe: %zd\n", err); + return; + }
- default: - frame.m = HDMI_AVI_M_16_9; - frame.vic = 0; - break; + err = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer)); + if (err < 0) { + dev_err(hdmi->dev, "failed to pack AVI infoframe: %zd\n", err); + return; }
- tegra_hdmi_write_infopack(hdmi, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER, - HDMI_INFOFRAME_TYPE_AVI, HDMI_AVI_VERSION, - &frame, sizeof(frame)); + tegra_hdmi_write_infopack(hdmi, buffer, err);
tegra_hdmi_writel(hdmi, INFOFRAME_CTRL_ENABLE, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL); @@ -537,6 +500,8 @@ static void tegra_hdmi_setup_avi_infoframe(struct tegra_hdmi *hdmi, static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi) { struct hdmi_audio_infoframe frame; + u8 buffer[14]; + ssize_t err;
if (hdmi->dvi) { tegra_hdmi_writel(hdmi, 0, @@ -544,14 +509,29 @@ static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi) return; }
- memset(&frame, 0, sizeof(frame)); - frame.cc = HDMI_AUDIO_CC_2; + err = hdmi_audio_infoframe_init(&frame); + if (err < 0) { + dev_err(hdmi->dev, "failed to initialize audio infoframe: %d\n", + err); + return; + } + + frame.channels = 2; + + err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); + if (err < 0) { + dev_err(hdmi->dev, "failed to pack audio infoframe: %zd\n", + err); + return; + }
- tegra_hdmi_write_infopack(hdmi, - HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER, - HDMI_INFOFRAME_TYPE_AUDIO, - HDMI_AUDIO_VERSION, - &frame, sizeof(frame)); + /* + * The audio infoframe has only one set of subpack registers, so the + * infoframe needs to be truncated. One set of subpack registers can + * contain 7 bytes. Including the 3 byte header only the first 10 + * bytes can be programmed. + */ + tegra_hdmi_write_infopack(hdmi, buffer, min(10, err));
tegra_hdmi_writel(hdmi, INFOFRAME_CTRL_ENABLE, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL); @@ -559,8 +539,10 @@ static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi)
static void tegra_hdmi_setup_stereo_infoframe(struct tegra_hdmi *hdmi) { - struct hdmi_stereo_infoframe frame; + struct hdmi_vendor_infoframe frame; unsigned long value; + u8 buffer[10]; + ssize_t err;
if (!hdmi->stereo) { value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); @@ -570,22 +552,32 @@ static void tegra_hdmi_setup_stereo_infoframe(struct tegra_hdmi *hdmi) }
memset(&frame, 0, sizeof(frame)); - frame.regid0 = 0x03; - frame.regid1 = 0x0c; - frame.regid2 = 0x00; - frame.hdmi_video_format = 2; + + 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._3d_structure = 0; + frame.data[4] = 0x00 << 4; /* 3D structure */ } else { - frame._3d_structure = 8; - frame._3d_ext_data = 0; + frame.data[4] = 0x08 << 4; /* 3D structure */ + frame.data[5] = 0x00 << 4; /* 3D ext. data */ + } + + err = hdmi_vendor_infoframe_pack(&frame, buffer, sizeof(buffer)); + if (err < 0) { + dev_err(hdmi->dev, "failed to pack vendor infoframe: %zd\n", + err); + return; }
- tegra_hdmi_write_infopack(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_HEADER, - HDMI_INFOFRAME_TYPE_VENDOR, - HDMI_VENDOR_VERSION, &frame, 6); + tegra_hdmi_write_infopack(hdmi, buffer, err);
value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); value |= GENERIC_CTRL_ENABLE; diff --git a/drivers/gpu/drm/tegra/hdmi.h b/drivers/gpu/drm/tegra/hdmi.h index 1477f36..52ac36e 100644 --- a/drivers/gpu/drm/tegra/hdmi.h +++ b/drivers/gpu/drm/tegra/hdmi.h @@ -10,195 +10,6 @@ #ifndef TEGRA_HDMI_H #define TEGRA_HDMI_H 1
-#define HDMI_INFOFRAME_TYPE_VENDOR 0x81 -#define HDMI_INFOFRAME_TYPE_AVI 0x82 -#define HDMI_INFOFRAME_TYPE_SPD 0x83 -#define HDMI_INFOFRAME_TYPE_AUDIO 0x84 -#define HDMI_INFOFRAME_TYPE_MPEG_SRC 0x85 -#define HDMI_INFOFRAME_TYPE_NTSC_VBI 0x86 - -/* all fields little endian */ -struct hdmi_avi_infoframe { - /* PB0 */ - u8 csum; - - /* PB1 */ - unsigned s:2; /* scan information */ - unsigned b:2; /* bar info data valid */ - unsigned a:1; /* active info present */ - unsigned y:2; /* RGB or YCbCr */ - unsigned res1:1; - - /* PB2 */ - unsigned r:4; /* active format aspect ratio */ - unsigned m:2; /* picture aspect ratio */ - unsigned c:2; /* colorimetry */ - - /* PB3 */ - unsigned sc:2; /* scan information */ - unsigned q:2; /* quantization range */ - unsigned ec:3; /* extended colorimetry */ - unsigned itc:1; /* it content */ - - /* PB4 */ - unsigned vic:7; /* video format id code */ - unsigned res4:1; - - /* PB5 */ - unsigned pr:4; /* pixel repetition factor */ - unsigned cn:2; /* it content type*/ - unsigned yq:2; /* ycc quantization range */ - - /* PB6-7 */ - u16 top_bar_end_line; - - /* PB8-9 */ - u16 bot_bar_start_line; - - /* PB10-11 */ - u16 left_bar_end_pixel; - - /* PB12-13 */ - u16 right_bar_start_pixel; -} __packed; - -#define HDMI_AVI_VERSION 0x02 - -#define HDMI_AVI_Y_RGB 0x0 -#define HDMI_AVI_Y_YCBCR_422 0x1 -#define HDMI_AVI_Y_YCBCR_444 0x2 - -#define HDMI_AVI_B_VERT 0x1 -#define HDMI_AVI_B_HORIZ 0x2 - -#define HDMI_AVI_S_NONE 0x0 -#define HDMI_AVI_S_OVERSCAN 0x1 -#define HDMI_AVI_S_UNDERSCAN 0x2 - -#define HDMI_AVI_C_NONE 0x0 -#define HDMI_AVI_C_SMPTE 0x1 -#define HDMI_AVI_C_ITU_R 0x2 -#define HDMI_AVI_C_EXTENDED 0x4 - -#define HDMI_AVI_M_4_3 0x1 -#define HDMI_AVI_M_16_9 0x2 - -#define HDMI_AVI_R_SAME 0x8 -#define HDMI_AVI_R_4_3_CENTER 0x9 -#define HDMI_AVI_R_16_9_CENTER 0xa -#define HDMI_AVI_R_14_9_CENTER 0xb - -/* all fields little endian */ -struct hdmi_audio_infoframe { - /* PB0 */ - u8 csum; - - /* PB1 */ - unsigned cc:3; /* channel count */ - unsigned res1:1; - unsigned ct:4; /* coding type */ - - /* PB2 */ - unsigned ss:2; /* sample size */ - unsigned sf:3; /* sample frequency */ - unsigned res2:3; - - /* PB3 */ - unsigned cxt:5; /* coding extention type */ - unsigned res3:3; - - /* PB4 */ - u8 ca; /* channel/speaker allocation */ - - /* PB5 */ - unsigned res5:3; - unsigned lsv:4; /* level shift value */ - unsigned dm_inh:1; /* downmix inhibit */ - - /* PB6-10 reserved */ - u8 res6; - u8 res7; - u8 res8; - u8 res9; - u8 res10; -} __packed; - -#define HDMI_AUDIO_VERSION 0x01 - -#define HDMI_AUDIO_CC_STREAM 0x0 /* specified by audio stream */ -#define HDMI_AUDIO_CC_2 0x1 -#define HDMI_AUDIO_CC_3 0x2 -#define HDMI_AUDIO_CC_4 0x3 -#define HDMI_AUDIO_CC_5 0x4 -#define HDMI_AUDIO_CC_6 0x5 -#define HDMI_AUDIO_CC_7 0x6 -#define HDMI_AUDIO_CC_8 0x7 - -#define HDMI_AUDIO_CT_STREAM 0x0 /* specified by audio stream */ -#define HDMI_AUDIO_CT_PCM 0x1 -#define HDMI_AUDIO_CT_AC3 0x2 -#define HDMI_AUDIO_CT_MPEG1 0x3 -#define HDMI_AUDIO_CT_MP3 0x4 -#define HDMI_AUDIO_CT_MPEG2 0x5 -#define HDMI_AUDIO_CT_AAC_LC 0x6 -#define HDMI_AUDIO_CT_DTS 0x7 -#define HDMI_AUDIO_CT_ATRAC 0x8 -#define HDMI_AUDIO_CT_DSD 0x9 -#define HDMI_AUDIO_CT_E_AC3 0xa -#define HDMI_AUDIO_CT_DTS_HD 0xb -#define HDMI_AUDIO_CT_MLP 0xc -#define HDMI_AUDIO_CT_DST 0xd -#define HDMI_AUDIO_CT_WMA_PRO 0xe -#define HDMI_AUDIO_CT_CXT 0xf - -#define HDMI_AUDIO_SF_STREAM 0x0 /* specified by audio stream */ -#define HDMI_AUIDO_SF_32K 0x1 -#define HDMI_AUDIO_SF_44_1K 0x2 -#define HDMI_AUDIO_SF_48K 0x3 -#define HDMI_AUDIO_SF_88_2K 0x4 -#define HDMI_AUDIO_SF_96K 0x5 -#define HDMI_AUDIO_SF_176_4K 0x6 -#define HDMI_AUDIO_SF_192K 0x7 - -#define HDMI_AUDIO_SS_STREAM 0x0 /* specified by audio stream */ -#define HDMI_AUDIO_SS_16BIT 0x1 -#define HDMI_AUDIO_SS_20BIT 0x2 -#define HDMI_AUDIO_SS_24BIT 0x3 - -#define HDMI_AUDIO_CXT_CT 0x0 /* refer to coding in CT */ -#define HDMI_AUDIO_CXT_HE_AAC 0x1 -#define HDMI_AUDIO_CXT_HE_AAC_V2 0x2 -#define HDMI_AUDIO_CXT_MPEG_SURROUND 0x3 - -/* all fields little endian */ -struct hdmi_stereo_infoframe { - /* PB0 */ - u8 csum; - - /* PB1 */ - u8 regid0; - - /* PB2 */ - u8 regid1; - - /* PB3 */ - u8 regid2; - - /* PB4 */ - unsigned res1:5; - unsigned hdmi_video_format:3; - - /* PB5 */ - unsigned res2:4; - unsigned _3d_structure:4; - - /* PB6*/ - unsigned res3:4; - unsigned _3d_ext_data:4; -} __packed; - -#define HDMI_VENDOR_VERSION 0x01 - /* register definitions */ #define HDMI_CTXSW 0x00
From: Paulo Zanoni paulo.r.zanoni@intel.com
Use the generic HDMI infoframe helpers to get rid of the duplicate implementation in the i915 driver.
This patch is based on the initial patch by Thierry Reding, but with a different approach.
TODO: - The SDVO part is totally untested. I am not sure if the buffer size on the SDVO code must be a multiple of 4. - The HDMI part was tested only on SNB/CPT. - The patch is forcing pixel_repeat to 1 so I can properly test the patch. Remove this before the final version. - The correctnes of this patch depends on a fix on patch "video: add Generic HDMI infoframe helpers"
Signed-off-by: Paulo Zanoni paulo.r.zanoni@intel.com Signed-off-by: Thierry Reding thierry.reding@avionic-design.de --- drivers/gpu/drm/i915/intel_drv.h | 62 +------------ drivers/gpu/drm/i915/intel_hdmi.c | 186 ++++++++++++++++++++------------------ drivers/gpu/drm/i915/intel_sdvo.c | 21 ++--- 3 files changed, 112 insertions(+), 157 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 8a1bd4a..f7c237d 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -26,6 +26,7 @@ #define __INTEL_DRV_H__
#include <linux/i2c.h> +#include <linux/hdmi.h> #include <drm/i915_drm.h> #include "i915_drv.h" #include <drm/drm_crtc.h> @@ -276,63 +277,6 @@ struct cxsr_latency { #define to_intel_framebuffer(x) container_of(x, struct intel_framebuffer, base) #define to_intel_plane(x) container_of(x, struct intel_plane, base)
-#define DIP_HEADER_SIZE 5 - -#define DIP_TYPE_AVI 0x82 -#define DIP_VERSION_AVI 0x2 -#define DIP_LEN_AVI 13 -#define DIP_AVI_PR_1 0 -#define DIP_AVI_PR_2 1 - -#define DIP_TYPE_SPD 0x83 -#define DIP_VERSION_SPD 0x1 -#define DIP_LEN_SPD 25 -#define DIP_SPD_UNKNOWN 0 -#define DIP_SPD_DSTB 0x1 -#define DIP_SPD_DVDP 0x2 -#define DIP_SPD_DVHS 0x3 -#define DIP_SPD_HDDVR 0x4 -#define DIP_SPD_DVC 0x5 -#define DIP_SPD_DSC 0x6 -#define DIP_SPD_VCD 0x7 -#define DIP_SPD_GAME 0x8 -#define DIP_SPD_PC 0x9 -#define DIP_SPD_BD 0xa -#define DIP_SPD_SCD 0xb - -struct dip_infoframe { - uint8_t type; /* HB0 */ - uint8_t ver; /* HB1 */ - uint8_t len; /* HB2 - body len, not including checksum */ - uint8_t ecc; /* Header ECC */ - uint8_t checksum; /* PB0 */ - union { - struct { - /* PB1 - Y 6:5, A 4:4, B 3:2, S 1:0 */ - uint8_t Y_A_B_S; - /* PB2 - C 7:6, M 5:4, R 3:0 */ - uint8_t C_M_R; - /* PB3 - ITC 7:7, EC 6:4, Q 3:2, SC 1:0 */ - uint8_t ITC_EC_Q_SC; - /* PB4 - VIC 6:0 */ - uint8_t VIC; - /* PB5 - YQ 7:6, CN 5:4, PR 3:0 */ - uint8_t YQ_CN_PR; - /* PB6 to PB13 */ - uint16_t top_bar_end; - uint16_t bottom_bar_start; - uint16_t left_bar_end; - uint16_t right_bar_start; - } __attribute__ ((packed)) avi; - struct { - uint8_t vn[8]; - uint8_t pd[16]; - uint8_t sdi; - } __attribute__ ((packed)) spd; - uint8_t payload[27]; - } __attribute__ ((packed)) body; -} __attribute__((packed)); - struct intel_hdmi { u32 sdvox_reg; int ddc_bus; @@ -341,7 +285,8 @@ struct intel_hdmi { bool has_audio; enum hdmi_force_audio force_audio; void (*write_infoframe)(struct drm_encoder *encoder, - struct dip_infoframe *frame); + uint8_t *frame, ssize_t len, + enum hdmi_infoframe_type type); void (*set_infoframes)(struct drm_encoder *encoder, struct drm_display_mode *adjusted_mode); }; @@ -433,7 +378,6 @@ extern struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder); extern bool intel_hdmi_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); -extern void intel_dip_infoframe_csum(struct dip_infoframe *avi_if); extern bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob); extern void intel_dvo_init(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 2ee9821..b49df1b 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -29,6 +29,7 @@ #include <linux/i2c.h> #include <linux/slab.h> #include <linux/delay.h> +#include <linux/hdmi.h> #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_edid.h> @@ -66,88 +67,75 @@ static struct intel_hdmi *intel_attached_hdmi(struct drm_connector *connector) return enc_to_intel_hdmi(&intel_attached_encoder(connector)->base); }
-void intel_dip_infoframe_csum(struct dip_infoframe *frame) +static u32 g4x_infoframe_index(enum hdmi_infoframe_type type) { - uint8_t *data = (uint8_t *)frame; - uint8_t sum = 0; - unsigned i; - - frame->checksum = 0; - frame->ecc = 0; - - for (i = 0; i < frame->len + DIP_HEADER_SIZE; i++) - sum += data[i]; - - frame->checksum = 0x100 - sum; -} - -static u32 g4x_infoframe_index(struct dip_infoframe *frame) -{ - switch (frame->type) { - case DIP_TYPE_AVI: + switch (type) { + case HDMI_INFOFRAME_TYPE_AVI: return VIDEO_DIP_SELECT_AVI; - case DIP_TYPE_SPD: + case HDMI_INFOFRAME_TYPE_SPD: return VIDEO_DIP_SELECT_SPD; default: - DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); + DRM_DEBUG_DRIVER("unknown info frame type %d\n", type); return 0; } }
-static u32 g4x_infoframe_enable(struct dip_infoframe *frame) +static u32 g4x_infoframe_enable(enum hdmi_infoframe_type type) { - switch (frame->type) { - case DIP_TYPE_AVI: + switch (type) { + case HDMI_INFOFRAME_TYPE_AVI: return VIDEO_DIP_ENABLE_AVI; - case DIP_TYPE_SPD: + case HDMI_INFOFRAME_TYPE_SPD: return VIDEO_DIP_ENABLE_SPD; default: - DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); + DRM_DEBUG_DRIVER("unknown info frame type %d\n", type); return 0; } }
-static u32 hsw_infoframe_enable(struct dip_infoframe *frame) +static u32 hsw_infoframe_enable(enum hdmi_infoframe_type type) { - switch (frame->type) { - case DIP_TYPE_AVI: + switch (type) { + case HDMI_INFOFRAME_TYPE_AVI: return VIDEO_DIP_ENABLE_AVI_HSW; - case DIP_TYPE_SPD: + case HDMI_INFOFRAME_TYPE_SPD: return VIDEO_DIP_ENABLE_SPD_HSW; default: - DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); + DRM_DEBUG_DRIVER("unknown info frame type %d\n", type); return 0; } }
-static u32 hsw_infoframe_data_reg(struct dip_infoframe *frame, enum pipe pipe) +static u32 hsw_infoframe_data_reg(enum hdmi_infoframe_type type, enum pipe pipe) { - switch (frame->type) { - case DIP_TYPE_AVI: + switch (type) { + case HDMI_INFOFRAME_TYPE_AVI: return HSW_TVIDEO_DIP_AVI_DATA(pipe); - case DIP_TYPE_SPD: + case HDMI_INFOFRAME_TYPE_SPD: return HSW_TVIDEO_DIP_SPD_DATA(pipe); default: - DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); + DRM_DEBUG_DRIVER("unknown info frame type %d\n", type); return 0; } }
static void g4x_write_infoframe(struct drm_encoder *encoder, - struct dip_infoframe *frame) + uint8_t *frame, + ssize_t len, + enum hdmi_infoframe_type type) { uint32_t *data = (uint32_t *)frame; struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 val = I915_READ(VIDEO_DIP_CTL); - unsigned i, len = DIP_HEADER_SIZE + frame->len; + unsigned int i;
WARN(!(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n");
val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ - val |= g4x_infoframe_index(frame); + val |= g4x_infoframe_index(type);
- val &= ~g4x_infoframe_enable(frame); + val &= ~g4x_infoframe_enable(type);
I915_WRITE(VIDEO_DIP_CTL, val);
@@ -161,7 +149,7 @@ static void g4x_write_infoframe(struct drm_encoder *encoder, I915_WRITE(VIDEO_DIP_DATA, 0); mmiowb();
- val |= g4x_infoframe_enable(frame); + val |= g4x_infoframe_enable(type); val &= ~VIDEO_DIP_FREQ_MASK; val |= VIDEO_DIP_FREQ_VSYNC;
@@ -170,22 +158,24 @@ static void g4x_write_infoframe(struct drm_encoder *encoder, }
static void ibx_write_infoframe(struct drm_encoder *encoder, - struct dip_infoframe *frame) + uint8_t *frame, + ssize_t len, + enum hdmi_infoframe_type type) { uint32_t *data = (uint32_t *)frame; struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); int reg = TVIDEO_DIP_CTL(intel_crtc->pipe); - unsigned i, len = DIP_HEADER_SIZE + frame->len; + unsigned int i; u32 val = I915_READ(reg);
WARN(!(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n");
val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ - val |= g4x_infoframe_index(frame); + val |= g4x_infoframe_index(type);
- val &= ~g4x_infoframe_enable(frame); + val &= ~g4x_infoframe_enable(type);
I915_WRITE(reg, val);
@@ -199,7 +189,7 @@ static void ibx_write_infoframe(struct drm_encoder *encoder, I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), 0); mmiowb();
- val |= g4x_infoframe_enable(frame); + val |= g4x_infoframe_enable(type); val &= ~VIDEO_DIP_FREQ_MASK; val |= VIDEO_DIP_FREQ_VSYNC;
@@ -208,25 +198,27 @@ static void ibx_write_infoframe(struct drm_encoder *encoder, }
static void cpt_write_infoframe(struct drm_encoder *encoder, - struct dip_infoframe *frame) + uint8_t *frame, + ssize_t len, + enum hdmi_infoframe_type type) { uint32_t *data = (uint32_t *)frame; struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); int reg = TVIDEO_DIP_CTL(intel_crtc->pipe); - unsigned i, len = DIP_HEADER_SIZE + frame->len; + unsigned int i; u32 val = I915_READ(reg);
WARN(!(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n");
val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ - val |= g4x_infoframe_index(frame); + val |= g4x_infoframe_index(type);
/* The DIP control register spec says that we need to update the AVI * infoframe without clearing its enable bit */ - if (frame->type != DIP_TYPE_AVI) - val &= ~g4x_infoframe_enable(frame); + if (type != HDMI_INFOFRAME_TYPE_AVI) + val &= ~g4x_infoframe_enable(type);
I915_WRITE(reg, val);
@@ -240,31 +232,34 @@ static void cpt_write_infoframe(struct drm_encoder *encoder, I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), 0); mmiowb();
- val |= g4x_infoframe_enable(frame); + val |= g4x_infoframe_enable(type); val &= ~VIDEO_DIP_FREQ_MASK; val |= VIDEO_DIP_FREQ_VSYNC;
I915_WRITE(reg, val); POSTING_READ(reg); + }
static void vlv_write_infoframe(struct drm_encoder *encoder, - struct dip_infoframe *frame) + uint8_t *frame, + ssize_t len, + enum hdmi_infoframe_type type) { uint32_t *data = (uint32_t *)frame; struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); int reg = VLV_TVIDEO_DIP_CTL(intel_crtc->pipe); - unsigned i, len = DIP_HEADER_SIZE + frame->len; + unsigned int i; u32 val = I915_READ(reg);
WARN(!(val & VIDEO_DIP_ENABLE), "Writing DIP with CTL reg disabled\n");
val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ - val |= g4x_infoframe_index(frame); + val |= g4x_infoframe_index(type);
- val &= ~g4x_infoframe_enable(frame); + val &= ~g4x_infoframe_enable(type);
I915_WRITE(reg, val);
@@ -278,7 +273,7 @@ static void vlv_write_infoframe(struct drm_encoder *encoder, I915_WRITE(VLV_TVIDEO_DIP_DATA(intel_crtc->pipe), 0); mmiowb();
- val |= g4x_infoframe_enable(frame); + val |= g4x_infoframe_enable(type); val &= ~VIDEO_DIP_FREQ_MASK; val |= VIDEO_DIP_FREQ_VSYNC;
@@ -287,21 +282,23 @@ static void vlv_write_infoframe(struct drm_encoder *encoder, }
static void hsw_write_infoframe(struct drm_encoder *encoder, - struct dip_infoframe *frame) + uint8_t *frame, + ssize_t len, + enum hdmi_infoframe_type type) { uint32_t *data = (uint32_t *)frame; struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); u32 ctl_reg = HSW_TVIDEO_DIP_CTL(intel_crtc->pipe); - u32 data_reg = hsw_infoframe_data_reg(frame, intel_crtc->pipe); - unsigned int i, len = DIP_HEADER_SIZE + frame->len; + u32 data_reg = hsw_infoframe_data_reg(type, intel_crtc->pipe); + unsigned int i; u32 val = I915_READ(ctl_reg);
if (data_reg == 0) return;
- val &= ~hsw_infoframe_enable(frame); + val &= ~hsw_infoframe_enable(type); I915_WRITE(ctl_reg, val);
mmiowb(); @@ -314,50 +311,67 @@ static void hsw_write_infoframe(struct drm_encoder *encoder, I915_WRITE(data_reg + i, 0); mmiowb();
- val |= hsw_infoframe_enable(frame); + val |= hsw_infoframe_enable(type); I915_WRITE(ctl_reg, val); POSTING_READ(ctl_reg); }
-static void intel_set_infoframe(struct drm_encoder *encoder, - struct dip_infoframe *frame) +static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder, + struct drm_display_mode *adjusted_mode) { struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + struct hdmi_avi_infoframe frame; + uint8_t buffer[20]; /* will be read as uin32_t, so 20 not 18 */ + ssize_t len;
- intel_dip_infoframe_csum(frame); - intel_hdmi->write_infoframe(encoder, frame); -} + memset(buffer, 0, sizeof(buffer));
-static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder, - struct drm_display_mode *adjusted_mode) -{ - struct dip_infoframe avi_if = { - .type = DIP_TYPE_AVI, - .ver = DIP_VERSION_AVI, - .len = DIP_LEN_AVI, - }; + len = drm_hdmi_avi_infoframe_from_display_mode(&frame, adjusted_mode); + WARN(len < 0, "drm_hdmi_avi_infoframe_from_display_mode failed\n");
if (adjusted_mode->flags & DRM_MODE_FLAG_DBLCLK) - avi_if.body.avi.YQ_CN_PR |= DIP_AVI_PR_2; + frame.pixel_repeat = 1;
- avi_if.body.avi.VIC = drm_mode_cea_vic(adjusted_mode); + len = hdmi_avi_infoframe_pack(&frame, buffer + 1, sizeof(buffer) - 1); + WARN(len < 0, "hdmi_avi_infoframe_pack failed\n");
- intel_set_infoframe(encoder, &avi_if); + /* Insert the ECC */ + buffer[0] = buffer[1]; + buffer[1] = buffer[2]; + buffer[2] = buffer[3]; + buffer[3] = 0; + len++; + + intel_hdmi->write_infoframe(encoder, buffer, len, + HDMI_INFOFRAME_TYPE_AVI); }
static void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder) { - struct dip_infoframe spd_if; + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + struct hdmi_spd_infoframe frame; + uint8_t buffer[32]; /* will be read as uint32_t, so 32, not 30 */ + ssize_t len; + + memset(buffer, 0, sizeof(buffer)); + + len = hdmi_spd_infoframe_init(&frame, "Intel", "Integrated gfx"); + WARN(len < 0, "hdmi_spd_infoframe_init failed\n"); + + frame.sdi = HDMI_SPD_SDI_PC; + + len = hdmi_spd_infoframe_pack(&frame, buffer + 1, sizeof(buffer) - 1); + WARN(len < 0, "hdmi_spd_infoframe_pack failed\n");
- memset(&spd_if, 0, sizeof(spd_if)); - spd_if.type = DIP_TYPE_SPD; - spd_if.ver = DIP_VERSION_SPD; - spd_if.len = DIP_LEN_SPD; - strcpy(spd_if.body.spd.vn, "Intel"); - strcpy(spd_if.body.spd.pd, "Integrated gfx"); - spd_if.body.spd.sdi = DIP_SPD_PC; + /* Insert the ECC */ + buffer[0] = buffer[1]; + buffer[1] = buffer[2]; + buffer[2] = buffer[3]; + buffer[3] = 0; + len++;
- intel_set_infoframe(encoder, &spd_if); + intel_hdmi->write_infoframe(encoder, buffer, len, + HDMI_INFOFRAME_TYPE_SPD); }
static void g4x_set_infoframes(struct drm_encoder *encoder, diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index c275bf0..e4abb55 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -29,6 +29,7 @@ #include <linux/slab.h> #include <linux/delay.h> #include <linux/export.h> +#include <linux/hdmi.h> #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_edid.h> @@ -948,20 +949,16 @@ static bool intel_sdvo_write_infoframe(struct intel_sdvo *intel_sdvo,
static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo) { - struct dip_infoframe avi_if = { - .type = DIP_TYPE_AVI, - .ver = DIP_VERSION_AVI, - .len = DIP_LEN_AVI, - }; - uint8_t sdvo_data[4 + sizeof(avi_if.body.avi)]; + uint8_t sdvo_data[HDMI_AVI_INFOFRAME_SIZE + + HDMI_INFOFRAME_HEADER_SIZE]; + struct hdmi_avi_infoframe frame; + ssize_t len;
- intel_dip_infoframe_csum(&avi_if); + len = hdmi_avi_infoframe_init(&frame); + WARN(len < 0, "hdmi_avi_infoframe_init failed!\n");
- /* sdvo spec says that the ecc is handled by the hw, and it looks like - * we must not send the ecc field, either. */ - memcpy(sdvo_data, &avi_if, 3); - sdvo_data[3] = avi_if.checksum; - memcpy(&sdvo_data[4], &avi_if.body, sizeof(avi_if.body.avi)); + len = hdmi_avi_infoframe_pack(&frame, sdvo_data, sizeof(sdvo_data)); + WARN(len < 0, "hdmi_avi_infoframe_pack failed!\n");
return intel_sdvo_write_infoframe(intel_sdvo, SDVO_HBUF_INDEX_AVI_IF, SDVO_HBUF_TX_VSYNC,
Use the generic HDMI infoframe helpers to get rid of the duplicate implementation in the radeon driver.
Signed-off-by: Thierry Reding thierry.reding@avionic-design.de --- drivers/gpu/drm/radeon/evergreen_hdmi.c | 85 +++++--------------- drivers/gpu/drm/radeon/r600_hdmi.c | 134 ++++++++++---------------------- 2 files changed, 58 insertions(+), 161 deletions(-)
diff --git a/drivers/gpu/drm/radeon/evergreen_hdmi.c b/drivers/gpu/drm/radeon/evergreen_hdmi.c index 327c08b..4fdecc2 100644 --- a/drivers/gpu/drm/radeon/evergreen_hdmi.c +++ b/drivers/gpu/drm/radeon/evergreen_hdmi.c @@ -24,6 +24,7 @@ * Authors: Christian König * Rafał Miłecki */ +#include <linux/hdmi.h> #include <drm/drmP.h> #include <drm/radeon_drm.h> #include "radeon.h" @@ -54,79 +55,18 @@ static void evergreen_hdmi_update_ACR(struct drm_encoder *encoder, uint32_t cloc }
/* - * calculate the crc for a given info frame - */ -static void evergreen_hdmi_infoframe_checksum(uint8_t packetType, - uint8_t versionNumber, - uint8_t length, - uint8_t *frame) -{ - int i; - frame[0] = packetType + versionNumber + length; - for (i = 1; i <= length; i++) - frame[0] += frame[i]; - frame[0] = 0x100 - frame[0]; -} - -/* * build a HDMI Video Info Frame */ -static void evergreen_hdmi_videoinfoframe( - struct drm_encoder *encoder, - uint8_t color_format, - int active_information_present, - uint8_t active_format_aspect_ratio, - uint8_t scan_information, - uint8_t colorimetry, - uint8_t ex_colorimetry, - uint8_t quantization, - int ITC, - uint8_t picture_aspect_ratio, - uint8_t video_format_identification, - uint8_t pixel_repetition, - uint8_t non_uniform_picture_scaling, - uint8_t bar_info_data_valid, - uint16_t top_bar, - uint16_t bottom_bar, - uint16_t left_bar, - uint16_t right_bar -) +static void evergreen_hdmi_update_avi_infoframe(struct drm_encoder *encoder, + void *buffer, size_t size) { struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; uint32_t offset = dig->afmt->offset; + uint8_t *frame = buffer + 3;
- uint8_t frame[14]; - - frame[0x0] = 0; - frame[0x1] = - (scan_information & 0x3) | - ((bar_info_data_valid & 0x3) << 2) | - ((active_information_present & 0x1) << 4) | - ((color_format & 0x3) << 5); - frame[0x2] = - (active_format_aspect_ratio & 0xF) | - ((picture_aspect_ratio & 0x3) << 4) | - ((colorimetry & 0x3) << 6); - frame[0x3] = - (non_uniform_picture_scaling & 0x3) | - ((quantization & 0x3) << 2) | - ((ex_colorimetry & 0x7) << 4) | - ((ITC & 0x1) << 7); - frame[0x4] = (video_format_identification & 0x7F); - frame[0x5] = (pixel_repetition & 0xF); - frame[0x6] = (top_bar & 0xFF); - frame[0x7] = (top_bar >> 8); - frame[0x8] = (bottom_bar & 0xFF); - frame[0x9] = (bottom_bar >> 8); - frame[0xA] = (left_bar & 0xFF); - frame[0xB] = (left_bar >> 8); - frame[0xC] = (right_bar & 0xFF); - frame[0xD] = (right_bar >> 8); - - evergreen_hdmi_infoframe_checksum(0x82, 0x02, 0x0D, frame); /* Our header values (type, version, length) should be alright, Intel * is using the same. Checksum function also seems to be OK, it works * fine for audio infoframe. However calculated value is always lower @@ -154,7 +94,10 @@ void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode struct radeon_device *rdev = dev->dev_private; struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE]; + struct hdmi_avi_infoframe frame; uint32_t offset; + ssize_t err;
/* Silent, r600_hdmi_enable will raise WARN for us */ if (!dig->afmt->enabled) @@ -200,9 +143,19 @@ void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode
WREG32(HDMI_GC + offset, 0); /* unset HDMI_GC_AVMUTE */
- evergreen_hdmi_videoinfoframe(encoder, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0); + err = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode); + if (err < 0) { + DRM_ERROR("failed to setup AVI infoframe: %zd\n", err); + return; + } + + err = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer)); + if (err < 0) { + DRM_ERROR("failed to pack AVI infoframe: %zd\n", err); + return; + }
+ evergreen_hdmi_update_avi_infoframe(encoder, buffer, sizeof(buffer)); evergreen_hdmi_update_ACR(encoder, mode->clock);
/* it's unknown what these bits do excatly, but it's indeed quite useful for debugging */ diff --git a/drivers/gpu/drm/radeon/r600_hdmi.c b/drivers/gpu/drm/radeon/r600_hdmi.c index ff80efe..f35f148 100644 --- a/drivers/gpu/drm/radeon/r600_hdmi.c +++ b/drivers/gpu/drm/radeon/r600_hdmi.c @@ -23,6 +23,7 @@ * * Authors: Christian König */ +#include <linux/hdmi.h> #include <drm/drmP.h> #include <drm/radeon_drm.h> #include "radeon.h" @@ -121,79 +122,18 @@ static void r600_hdmi_update_ACR(struct drm_encoder *encoder, uint32_t clock) }
/* - * calculate the crc for a given info frame - */ -static void r600_hdmi_infoframe_checksum(uint8_t packetType, - uint8_t versionNumber, - uint8_t length, - uint8_t *frame) -{ - int i; - frame[0] = packetType + versionNumber + length; - for (i = 1; i <= length; i++) - frame[0] += frame[i]; - frame[0] = 0x100 - frame[0]; -} - -/* * build a HDMI Video Info Frame */ -static void r600_hdmi_videoinfoframe( - struct drm_encoder *encoder, - enum r600_hdmi_color_format color_format, - int active_information_present, - uint8_t active_format_aspect_ratio, - uint8_t scan_information, - uint8_t colorimetry, - uint8_t ex_colorimetry, - uint8_t quantization, - int ITC, - uint8_t picture_aspect_ratio, - uint8_t video_format_identification, - uint8_t pixel_repetition, - uint8_t non_uniform_picture_scaling, - uint8_t bar_info_data_valid, - uint16_t top_bar, - uint16_t bottom_bar, - uint16_t left_bar, - uint16_t right_bar -) +static void r600_hdmi_update_avi_infoframe(struct drm_encoder *encoder, + void *buffer, size_t size) { struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; uint32_t offset = dig->afmt->offset; + uint8_t *frame = buffer + 3;
- uint8_t frame[14]; - - frame[0x0] = 0; - frame[0x1] = - (scan_information & 0x3) | - ((bar_info_data_valid & 0x3) << 2) | - ((active_information_present & 0x1) << 4) | - ((color_format & 0x3) << 5); - frame[0x2] = - (active_format_aspect_ratio & 0xF) | - ((picture_aspect_ratio & 0x3) << 4) | - ((colorimetry & 0x3) << 6); - frame[0x3] = - (non_uniform_picture_scaling & 0x3) | - ((quantization & 0x3) << 2) | - ((ex_colorimetry & 0x7) << 4) | - ((ITC & 0x1) << 7); - frame[0x4] = (video_format_identification & 0x7F); - frame[0x5] = (pixel_repetition & 0xF); - frame[0x6] = (top_bar & 0xFF); - frame[0x7] = (top_bar >> 8); - frame[0x8] = (bottom_bar & 0xFF); - frame[0x9] = (bottom_bar >> 8); - frame[0xA] = (left_bar & 0xFF); - frame[0xB] = (left_bar >> 8); - frame[0xC] = (right_bar & 0xFF); - frame[0xD] = (right_bar >> 8); - - r600_hdmi_infoframe_checksum(0x82, 0x02, 0x0D, frame); /* Our header values (type, version, length) should be alright, Intel * is using the same. Checksum function also seems to be OK, it works * fine for audio infoframe. However calculated value is always lower @@ -215,39 +155,15 @@ static void r600_hdmi_videoinfoframe( /* * build a Audio Info Frame */ -static void r600_hdmi_audioinfoframe( - struct drm_encoder *encoder, - uint8_t channel_count, - uint8_t coding_type, - uint8_t sample_size, - uint8_t sample_frequency, - uint8_t format, - uint8_t channel_allocation, - uint8_t level_shift, - int downmix_inhibit -) +static void r600_hdmi_update_audio_infoframe(struct drm_encoder *encoder, + const void *buffer, size_t size) { struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; uint32_t offset = dig->afmt->offset; - - uint8_t frame[11]; - - frame[0x0] = 0; - frame[0x1] = (channel_count & 0x7) | ((coding_type & 0xF) << 4); - frame[0x2] = (sample_size & 0x3) | ((sample_frequency & 0x7) << 2); - frame[0x3] = format; - frame[0x4] = channel_allocation; - frame[0x5] = ((level_shift & 0xF) << 3) | ((downmix_inhibit & 0x1) << 7); - frame[0x6] = 0; - frame[0x7] = 0; - frame[0x8] = 0; - frame[0x9] = 0; - frame[0xA] = 0; - - r600_hdmi_infoframe_checksum(0x84, 0x01, 0x0A, frame); + const u8 *frame = buffer + 3;
WREG32(HDMI0_AUDIO_INFO0 + offset, frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24)); @@ -320,7 +236,10 @@ void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mod struct radeon_device *rdev = dev->dev_private; struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE]; + struct hdmi_avi_infoframe frame; uint32_t offset; + ssize_t err;
/* Silent, r600_hdmi_enable will raise WARN for us */ if (!dig->afmt->enabled) @@ -371,9 +290,19 @@ void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mod
WREG32(HDMI0_GC + offset, 0); /* unset HDMI0_GC_AVMUTE */
- r600_hdmi_videoinfoframe(encoder, RGB, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + err = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode); + if (err < 0) { + DRM_ERROR("failed to setup AVI infoframe: %zd\n", err); + return; + }
+ err = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer)); + if (err < 0) { + DRM_ERROR("failed to pack AVI infoframe: %zd\n", err); + return; + } + + r600_hdmi_update_avi_infoframe(encoder, buffer, sizeof(buffer)); r600_hdmi_update_ACR(encoder, mode->clock);
/* it's unknown what these bits do excatly, but it's indeed quite useful for debugging */ @@ -395,8 +324,11 @@ void r600_hdmi_update_audio_settings(struct drm_encoder *encoder) struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; struct r600_audio audio = r600_audio_status(rdev); + uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE]; + struct hdmi_audio_infoframe frame; uint32_t offset; uint32_t iec; + ssize_t err;
if (!dig->afmt || !dig->afmt->enabled) return; @@ -462,9 +394,21 @@ void r600_hdmi_update_audio_settings(struct drm_encoder *encoder) iec |= 0x5 << 16; WREG32_P(HDMI0_60958_1 + offset, iec, ~0x5000f);
- r600_hdmi_audioinfoframe(encoder, audio.channels - 1, 0, 0, 0, 0, 0, 0, - 0); + err = hdmi_audio_infoframe_init(&frame); + if (err < 0) { + DRM_ERROR("failed to setup audio infoframe\n"); + return; + } + + frame.channels = audio.channels; + + err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer)); + if (err < 0) { + DRM_ERROR("failed to pack audio infoframe\n"); + return; + }
+ r600_hdmi_update_audio_infoframe(encoder, buffer, sizeof(buffer)); r600_hdmi_audio_workaround(encoder); }
The same function had already been merged with a different name. Remove the duplicate one but reuse some of its kerneldoc fragments for the existing implementation.
Signed-off-by: Thierry Reding thierry.reding@avionic-design.de --- drivers/gpu/drm/drm_edid.c | 30 +++++++----------------------- include/drm/drm_crtc.h | 3 +-- 2 files changed, 8 insertions(+), 25 deletions(-)
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 1cfafc0..0acd6f3 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1514,11 +1514,14 @@ u8 *drm_find_cea_extension(struct edid *edid) } EXPORT_SYMBOL(drm_find_cea_extension);
-/* - * Looks for a CEA mode matching given drm_display_mode. - * Returns its CEA Video ID code, or 0 if not found. +/** + * drm_match_cea_mode - look for a CEA mode matching given mode + * @mode: display mode + * + * Returns the CEA Video ID (VIC) of the mode or 0 if it isn't a CEA-861 + * mode. */ -u8 drm_match_cea_mode(struct drm_display_mode *to_match) +u8 drm_match_cea_mode(const struct drm_display_mode *to_match) { struct drm_display_mode *cea_mode; u8 mode; @@ -2082,25 +2085,6 @@ int drm_add_modes_noedid(struct drm_connector *connector, EXPORT_SYMBOL(drm_add_modes_noedid);
/** - * drm_mode_cea_vic - return the CEA-861 VIC of a given mode - * @mode: mode - * - * RETURNS: - * The VIC number, 0 in case it's not a CEA-861 mode. - */ -uint8_t drm_mode_cea_vic(const struct drm_display_mode *mode) -{ - uint8_t i; - - for (i = 0; i < drm_num_cea_modes; i++) - if (drm_mode_equal(mode, &edid_cea_modes[i])) - return i + 1; - - return 0; -} -EXPORT_SYMBOL(drm_mode_cea_vic); - -/** * drm_hdmi_avi_infoframe_from_display_mode() - fill an HDMI AVI infoframe with * data from a DRM display mode * @frame: HDMI AVI infoframe diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index dd62ca0..d0f3927 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1031,7 +1031,7 @@ extern int drm_mode_gamma_get_ioctl(struct drm_device *dev, 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(struct drm_display_mode *to_match); +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); extern int drm_mode_page_flip_ioctl(struct drm_device *dev, @@ -1048,7 +1048,6 @@ extern struct drm_display_mode *drm_gtf_mode_complex(struct drm_device *dev, int GTF_2C, int GTF_K, int GTF_2J); extern int drm_add_modes_noedid(struct drm_connector *connector, int hdisplay, int vdisplay); -extern uint8_t drm_mode_cea_vic(const struct drm_display_mode *mode);
extern int drm_edid_header_is_valid(const u8 *raw_edid); extern bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid);
On Mon, Jan 14, 2013 at 03:30:28PM +0100, Thierry Reding wrote:
The same function had already been merged with a different name. Remove the duplicate one but reuse some of its kerneldoc fragments for the existing implementation.
And I just used that function. Oh well ;)
Personally I would have kept the other function instead since it has a more descriptive name, but I can't be bothered to care enough to complain too much.
The patch could be moved to the beginning of the series, so it can be merged w/o being blocked by the more invasive stuff. Well, assuming that the more invasive stuff still needs discussing.
Signed-off-by: Thierry Reding thierry.reding@avionic-design.de
drivers/gpu/drm/drm_edid.c | 30 +++++++----------------------- include/drm/drm_crtc.h | 3 +-- 2 files changed, 8 insertions(+), 25 deletions(-)
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 1cfafc0..0acd6f3 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1514,11 +1514,14 @@ u8 *drm_find_cea_extension(struct edid *edid) } EXPORT_SYMBOL(drm_find_cea_extension);
-/*
- Looks for a CEA mode matching given drm_display_mode.
- Returns its CEA Video ID code, or 0 if not found.
+/**
- drm_match_cea_mode - look for a CEA mode matching given mode
- @mode: display mode
- Returns the CEA Video ID (VIC) of the mode or 0 if it isn't a CEA-861
*/
- mode.
-u8 drm_match_cea_mode(struct drm_display_mode *to_match) +u8 drm_match_cea_mode(const struct drm_display_mode *to_match) { struct drm_display_mode *cea_mode; u8 mode; @@ -2082,25 +2085,6 @@ int drm_add_modes_noedid(struct drm_connector *connector, EXPORT_SYMBOL(drm_add_modes_noedid);
/**
- drm_mode_cea_vic - return the CEA-861 VIC of a given mode
- @mode: mode
- RETURNS:
- The VIC number, 0 in case it's not a CEA-861 mode.
- */
-uint8_t drm_mode_cea_vic(const struct drm_display_mode *mode) -{
- uint8_t i;
- for (i = 0; i < drm_num_cea_modes; i++)
if (drm_mode_equal(mode, &edid_cea_modes[i]))
return i + 1;
- return 0;
-} -EXPORT_SYMBOL(drm_mode_cea_vic);
-/**
- drm_hdmi_avi_infoframe_from_display_mode() - fill an HDMI AVI infoframe with
data from a DRM display mode
- @frame: HDMI AVI infoframe
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index dd62ca0..d0f3927 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1031,7 +1031,7 @@ extern int drm_mode_gamma_get_ioctl(struct drm_device *dev, 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(struct drm_display_mode *to_match); +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); extern int drm_mode_page_flip_ioctl(struct drm_device *dev, @@ -1048,7 +1048,6 @@ extern struct drm_display_mode *drm_gtf_mode_complex(struct drm_device *dev, int GTF_2C, int GTF_K, int GTF_2J); extern int drm_add_modes_noedid(struct drm_connector *connector, int hdisplay, int vdisplay); -extern uint8_t drm_mode_cea_vic(const struct drm_display_mode *mode);
extern int drm_edid_header_is_valid(const u8 *raw_edid); extern bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid); -- 1.8.1
dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
On Mon, Jan 14, 2013 at 04:57:56PM +0200, Ville Syrjälä wrote:
On Mon, Jan 14, 2013 at 03:30:28PM +0100, Thierry Reding wrote:
The same function had already been merged with a different name. Remove the duplicate one but reuse some of its kerneldoc fragments for the existing implementation.
And I just used that function. Oh well ;)
Personally I would have kept the other function instead since it has a more descriptive name, but I can't be bothered to care enough to complain too much.
drm_mode_cea_vic() was introduced later, so I preferred the older variant.
The patch could be moved to the beginning of the series, so it can be merged w/o being blocked by the more invasive stuff. Well, assuming that the more invasive stuff still needs discussing.
I had it at the beginning of the series, then realized that I hadn't updated the i915 driver properly. So I cheated a bit and just moved the patch instead of updating i915 and then resolving the conflict in the subsequent i915 HDMI helper patch. =)
If the series can't be merged as-is, I can work this in for the next version.
Thierry
dri-devel@lists.freedesktop.org