Message ID | 1354725944-1862-3-git-send-email-thierry.reding@avionic-design.de (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
2012/12/5 Thierry Reding <thierry.reding@avionic-design.de>: > 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 > > drivers/video/Kconfig | 3 + > drivers/video/Makefile | 1 + > drivers/video/hdmi.c | 322 +++++++++++++++++++++++++++++++++++++++++++++++++ > include/linux/hdmi.h | 220 +++++++++++++++++++++++++++++++++ > 4 files changed, 546 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..6b95270 > --- /dev/null > +++ b/drivers/video/hdmi.c > @@ -0,0 +1,322 @@ > +/* > + * 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 = 4 + 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 */ > + ptr[4] = ((frame->colorspace & 0x3) << 5) | (frame->scan_mode & 0x3); > + > + if (frame->active_info_valid) > + ptr[4] |= BIT(4); > + > + if (frame->horizontal_bar_valid) > + ptr[4] |= BIT(3); > + > + if (frame->vertical_bar_valid) > + ptr[4] |= BIT(2); > + > + ptr[5] = ((frame->colorimetry & 0x3) << 6) | > + ((frame->picture_aspect & 0x3) << 4) | > + (frame->active_aspect & 0xf); > + > + ptr[6] = ((frame->extended_colorimetry & 0x7) << 4) | > + ((frame->quantization_range & 0x3) << 2) | > + (frame->nups & 0x3); > + > + if (frame->itc) > + ptr[6] |= BIT(7); > + > + ptr[7] = frame->video_code & 0x7f; > + > + ptr[8] = ((frame->ycc_quantization_range & 0x3) << 6) | > + ((frame->content_type & 0x3) << 4) | > + (frame->pixel_repeat & 0xf); > + > + ptr[9] = frame->top_bar & 0xff; > + ptr[10] = (frame->top_bar >> 8) & 0xff; > + ptr[11] = frame->bottom_bar & 0xff; > + ptr[12] = (frame->bottom_bar >> 8) & 0xff; > + ptr[13] = frame->left_bar & 0xff; > + ptr[14] = (frame->left_bar >> 8) & 0xff; > + ptr[15] = frame->right_bar & 0xff; > + ptr[16] = (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 = 4 + 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 */ > + > + for (i = 0; i < sizeof(frame->vendor); i++) > + ptr[4 + i] = frame->vendor[i]; > + > + for (i = 0; i < sizeof(frame->product); i++) > + ptr[12 + i] = frame->product[i]; > + > + ptr[26] = frame->sdi; Shouldn't this be ptr[28] ? > + > + 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 = 4 + 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 */ > + > + ptr[4] = ((frame->coding_type & 0xf) << 4) | (channels & 0x7); > + ptr[5] = ((frame->sample_frequency & 0x7) << 2) | > + (frame->sample_size & 0x3); > + ptr[6] = 0; > + ptr[7] = frame->channel_allocation; > + ptr[8] = (frame->level_shift_value & 0xf) << 3; > + > + if (frame->downmix_inhibit) > + ptr[8] |= 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 = 4 + 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[4], 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..18f74c0 > --- /dev/null > +++ b/include/linux/hdmi.h > @@ -0,0 +1,220 @@ > +/* > + * 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_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 */ > -- > 1.8.0.1 > > _______________________________________________ > dri-devel mailing list > dri-devel@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/dri-devel
On Fri, Dec 07, 2012 at 12:32:42PM -0200, Paulo Zanoni wrote: > 2012/12/5 Thierry Reding <thierry.reding@avionic-design.de>: [...] > > +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 = 4 + 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 */ > > + > > + for (i = 0; i < sizeof(frame->vendor); i++) > > + ptr[4 + i] = frame->vendor[i]; > > + > > + for (i = 0; i < sizeof(frame->product); i++) > > + ptr[12 + i] = frame->product[i]; > > + > > + ptr[26] = frame->sdi; > > Shouldn't this be ptr[28] ? Yes, you're absolutely right. By the way, I had to scroll through your reply several times before I found this single line. If you remove unneeded context when replying your comments are much easier to locate. Thierry
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..6b95270 --- /dev/null +++ b/drivers/video/hdmi.c @@ -0,0 +1,322 @@ +/* + * 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 = 4 + 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 */ + ptr[4] = ((frame->colorspace & 0x3) << 5) | (frame->scan_mode & 0x3); + + if (frame->active_info_valid) + ptr[4] |= BIT(4); + + if (frame->horizontal_bar_valid) + ptr[4] |= BIT(3); + + if (frame->vertical_bar_valid) + ptr[4] |= BIT(2); + + ptr[5] = ((frame->colorimetry & 0x3) << 6) | + ((frame->picture_aspect & 0x3) << 4) | + (frame->active_aspect & 0xf); + + ptr[6] = ((frame->extended_colorimetry & 0x7) << 4) | + ((frame->quantization_range & 0x3) << 2) | + (frame->nups & 0x3); + + if (frame->itc) + ptr[6] |= BIT(7); + + ptr[7] = frame->video_code & 0x7f; + + ptr[8] = ((frame->ycc_quantization_range & 0x3) << 6) | + ((frame->content_type & 0x3) << 4) | + (frame->pixel_repeat & 0xf); + + ptr[9] = frame->top_bar & 0xff; + ptr[10] = (frame->top_bar >> 8) & 0xff; + ptr[11] = frame->bottom_bar & 0xff; + ptr[12] = (frame->bottom_bar >> 8) & 0xff; + ptr[13] = frame->left_bar & 0xff; + ptr[14] = (frame->left_bar >> 8) & 0xff; + ptr[15] = frame->right_bar & 0xff; + ptr[16] = (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 = 4 + 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 */ + + for (i = 0; i < sizeof(frame->vendor); i++) + ptr[4 + i] = frame->vendor[i]; + + for (i = 0; i < sizeof(frame->product); i++) + ptr[12 + i] = frame->product[i]; + + ptr[26] = 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 = 4 + 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 */ + + ptr[4] = ((frame->coding_type & 0xf) << 4) | (channels & 0x7); + ptr[5] = ((frame->sample_frequency & 0x7) << 2) | + (frame->sample_size & 0x3); + ptr[6] = 0; + ptr[7] = frame->channel_allocation; + ptr[8] = (frame->level_shift_value & 0xf) << 3; + + if (frame->downmix_inhibit) + ptr[8] |= 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 = 4 + 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[4], 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..18f74c0 --- /dev/null +++ b/include/linux/hdmi.h @@ -0,0 +1,220 @@ +/* + * 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_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 */
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 drivers/video/Kconfig | 3 + drivers/video/Makefile | 1 + drivers/video/hdmi.c | 322 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/hdmi.h | 220 +++++++++++++++++++++++++++++++++ 4 files changed, 546 insertions(+) create mode 100644 drivers/video/hdmi.c create mode 100644 include/linux/hdmi.h