2012/12/5 Thierry Reding <thierry.reding@xxxxxxxxxxxxxxxxx>: > Add generic helpers to pack HDMI infoframes into binary buffers. > > Signed-off-by: Thierry Reding <thierry.reding@xxxxxxxxxxxxxxxxx> > --- > 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@xxxxxxxxxxxxxxxxxxxxx > http://lists.freedesktop.org/mailman/listinfo/dri-devel -- Paulo Zanoni _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel