From b9b36898534e56d8cd74eccf9579793a3afdcc69 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Sun, 16 Jan 2011 13:26:13 -0800 Subject: [PATCH] cea861: improve CEA EDID extension block definition Improve the CEA861 EDID block definition so that we can use it ot properly identify a HDMI sink. --- drivers/video/mxc/mxcfb_siihdmi.c | 76 ++++---------- include/linux/cea861.h | 159 ++++++++++-------------------- 2 files changed, 68 insertions(+), 167 deletions(-) diff --git a/drivers/video/mxc/mxcfb_siihdmi.c b/drivers/video/mxc/mxcfb_siihdmi.c index 1cff5f71102..2c4cc0205fc 100644 --- a/drivers/video/mxc/mxcfb_siihdmi.c +++ b/drivers/video/mxc/mxcfb_siihdmi.c @@ -256,70 +256,19 @@ static int siihdmi_read_edid(struct siihdmi_tx *tx, u8 *edid, size_t size) return 0; } -static void siihdmi_parse_cea_extension(struct siihdmi_tx *tx, - struct cea_timing_block *ctb) -{ - tx->enable_audio = ctb->basic_audio_supported; - - if (ctb->underscan_supported) - tx->pixel_mapping = PIXEL_MAPPING_UNDERSCANNED; - - if (ctb->dtd_start_offset == CTB_DTD_INVALID) - return; - -#if 0 - /* okay DTD data is off in the wild reaches so the next blocks - * will be audio, video, vendor and speaker configuration - */ - u8 length, offset = 0x5; - struct cea_dbc_header *dbc_header = (struct cea_dbc_header *) &ctb->dbc_start_offset; - - while (offset <= 127) { - switch(dbc_header->block_type_tag) { - case CEA_DATA_BLOCK_TAG_AUDIO: - length = dbc_header->length; - siihdmi_parse_audio(tx, (struct cea_dbc_audio *) &edid[EEDID_EXTENSION_DATA_OFFSET + offset]); - offset += length; - break; - case CEA_DATA_BLOCK_TAG_VIDEO: - length = dbc_header->length; - siihdmi_parse_video(tx, (struct cea_dbc_video *) &edid[EEDID_EXTENSION_DATA_OFFSET + offset]); - offset += length; - break; - case CEA_DATA_BLOCK_TAG_SPEAKER: - length = dbc_header->length; - siihdmi_parse_speaker(tx, (struct cea_dbc_speaker *) &edid[EEDID_EXTENSION_DATA_OFFSET + offset]); - offset += length; - break; - case CEA_DATA_BLOCK_TAG_VENDOR: - length = dbc_header->length; - siihdmi_parse_vendor(tx, (struct cea_dbc_vendor *) &edid[EEDID_EXTENSION_DATA_OFFSET + offset]); - offset += length; - break; - } - } -#endif -} - static void siihdmi_detect_sink(struct siihdmi_tx *tx) { - /* - * Sink detection is a fairly simple matter. Assume that we are - * connected to a DVI sink (video only, no audio support). Check the - * EDID for the presence of the CEA extension (which is guaranteed to be - * the first extension blob, if present). If it is present, and the CEA - * timing block data reports support for audio, then the sink is HDMI. - */ - u8 edid[EDID_BLOCK_SIZE << 1]; const struct edid_block0 * const block0 = (struct edid_block0 *) edid; const struct edid_extension * const block1 = (struct edid_extension *) edid + EDID_BLOCK_SIZE; + /* defaults */ tx->connection_type = CONNECTION_TYPE_DVI; tx->pixel_mapping = PIXEL_MAPPING_EXACT; tx->enable_audio = false; + /* use EDID to identify sink characteristics */ if (siihdmi_read_edid(tx, edid, sizeof(edid)) < 0) return; @@ -327,9 +276,20 @@ static void siihdmi_detect_sink(struct siihdmi_tx *tx) return; switch (block1->tag) { - case EDID_EXTENSION_CEA: - siihdmi_parse_cea_extension(tx, - (struct cea_timing_block *) block1); + case EDID_EXTENSION_CEA: { + const struct cea_timing_block *ctb = + (struct cea_timing_block *) block1; + + tx->enable_audio = ctb->basic_audio_supported; + + if (ctb->underscan_supported) + tx->pixel_mapping = PIXEL_MAPPING_UNDERSCANNED; + + if (!memcmp(ctb->ieee_registration, + CEA861_OUI_REGISTRATION_ID_HDMI, + sizeof(ctb->ieee_registration))) + tx->connection_type = CONNECTION_TYPE_HDMI; + } break; default: break; @@ -444,7 +404,7 @@ static int siihdmi_set_avi_info_frame(struct siihdmi_tx *tx, break; } - cea861_checksum_info_frame((u8 *) &avi); + cea861_checksum_hdmi_info_frame((u8 *) &avi); BUILD_BUG_ON(sizeof(avi) - SIIHDMI_AVI_INFO_FRAME_OFFSET != SIIHDMI_TPI_REG_AVI_INFO_FRAME_LENGTH); ret = i2c_smbus_write_i2c_block_data(tx->client, @@ -483,7 +443,7 @@ static int siihdmi_set_spd_info_frame(struct siihdmi_tx *tx, }, }; - cea861_checksum_info_frame((u8 *) &packet.spd); + cea861_checksum_hdmi_info_frame((u8 *) &packet.spd); BUILD_BUG_ON(sizeof(packet) != SIIHDMI_INFO_FRAME_BUFFER_LENGTH); ret = i2c_smbus_write_i2c_block_data(tx->client, diff --git a/include/linux/cea861.h b/include/linux/cea861.h index 96ab5ae05b1..7cad47a1167 100644 --- a/include/linux/cea861.h +++ b/include/linux/cea861.h @@ -34,120 +34,66 @@ /* Header file for EIA CEA 861-E structures and definitions */ -#if defined(__LITTLE_ENDIAN_BITFIELD) +#include + +#define CEA861_DTD_NOT_PRESENT (0x04) + +/* NOTE: This is placed inot the EDID in little-endian order */ +static const u8 CEA861_OUI_REGISTRATION_ID_HDMI[] = { 0x00, 0x0C, 0x03 }; -/* EDID timing extension block */ struct __packed cea_timing_block { - u8 extension_tag; - u8 revision_number; + /* CEA Extension Header */ + u8 tag; + u8 revision; u8 dtd_start_offset; + + /* Global Declarations */ +#if defined(__LITTLE_ENDIAN_BITFIELD) unsigned dtd_block_count : 4; unsigned yuv_422_supported : 1; unsigned yuv_444_supported : 1; unsigned basic_audio_supported : 1; unsigned underscan_supported : 1; - u8 dbc_start_offset; // data block collection offset +#else + unsigned underscan_supported : 1; + unsigned basic_audio_supported : 1; + unsigned yuv_444_supported : 1; + unsigned yuv_422_supported : 1; + unsigned dtd_block_count : 4; +#endif + + /* CEA Data Block Collection */ + /* Video Data Block */ + u8 video_data_start_offset; + u8 short_video_descriptor[7]; + + /* Audio Data Block */ + u8 audio_data_start_offset; + u8 audio_format; + u8 frequency; + u8 bit_rate; + + /* Speaker Allocation Block */ + u8 speaker_allocation_start_offset; + u8 speaker_designation; + u8 reserved[2]; + + /* Vendor Specific Data Block */ + u8 vendor_specific_offset; + u8 ieee_registration[3]; + u8 component_source[2]; + + struct edid_detailed_timing_descriptor dtb[4]; + + u8 padding[29]; + + u8 checksum; }; -struct __packed cea_dbc_header { - unsigned length : 5; - unsigned block_type_tag : 3; -}; -#define CEA_DATA_BLOCK_TAG_AUDIO (0x1) - -enum cea_dbc_audio_format { - CEA_AUDIO_FORMAT_RESERVED0, - CEA_AUDIO_FORMAT_LPCM, - CEA_AUDIO_FORMAT_AC3, - CEA_AUDIO_FORMAT_MPEG1, - CEA_AUDIO_FORMAT_MP3, - CEA_AUDIO_FORMAT_MPEG2, - CEA_AUDIO_FORMAT_AAC, - CEA_AUDIO_FORMAT_DTS, - CEA_AUDIO_FORMAT_ATRAC, - CEA_AUDIO_FORMAT_SACD_1BIT, - CEA_AUDIO_FORMAT_DOLBYDIGITALPLUS, - CEA_AUDIO_FORMAT_DTSHD, - CEA_AUDIO_FORMAT_MLP_DOLBYTRUEHD, - CEA_AUDIO_FORMAT_DST_AUDIO, - CEA_AUDIO_FORMAT_WMA_PRO, - CEA_AUDIO_FORMAT_RESERVED15, -}; - -struct __packed cea_dbc_audio { - unsigned channels : 3; - unsigned audio_format : 4; - unsigned audio_reserved : 1; - - unsigned freq_32kHz : 1; - unsigned freq_44kHz : 1; - unsigned freq_48kHz : 1; - unsigned freq_88kHz : 1; - unsigned freq_96kHz : 1; - unsigned freq_176kHz : 1; - unsigned freq_192kHz : 1; - unsigned freq_reserved : 1; - - union { - struct { - unsigned bits_16 : 1; - unsigned bits_20 : 1; - unsigned bits_24 : 1; - unsigned bits_reserved : 5; - } lpcm_bits; - u8 rate_div_8kbps; - } bitrate; -}; - -#define CEA_DATA_BLOCK_TAG_VIDEO (0x2) - -struct __packed cea_dbc_video { - unsigned cea_mode_index : 7; - unsigned native : 1; -}; - -#define CEA_DATA_BLOCK_TAG_VENDOR (0x3) - -struct __packed cea_dbc_vendor { - u8 ieee_identifier[3]; // 0x03 0x0C 0x00 == HDMI Licensing, LLC. - u8 cec_address[2]; - - unsigned dvi_dual_link : 1; - unsigned reserved : 2; - unsigned dc_y444 : 1; - unsigned dc_30bit : 1; - unsigned dc_36bit : 1; - unsigned dc_48bit : 1; - unsigned supports_ai : 1; - - u8 max_tmds_freqency; // divided by 5Mhz - - unsigned latency_reserved : 6; - unsigned latency_fields_il : 1; - unsigned latency_fields : 1; - - u8 video_latency; - u8 audio_latency; - u8 video_latency_il; - u8 audio_latency_il; -}; - -#define CEA_DATA_BLOCK_TAG_SPEAKER (0x4) - -struct __packed cea_dbc_speaker { - - unsigned front_lr_present : 1; /* 2 channels */ - unsigned lfe_present : 1; /* X.1 channels */ - unsigned front_center_present : 1; /* standard center speaker */ - unsigned rear_lr_present : 1; /* surround */ - unsigned rear_center_present : 1; /* for 6.1 */ - unsigned fcenter_lr_present : 1; /* for 7.1 */ - unsigned rcenter_lr_present : 1; /* for 7.1 */ - unsigned speaker_reserved : 1; - - u8 reserved[2]; -}; +#if !defined(__LITTLE_ENDIAN_BITFIELD) +#warning "structures defined and packed for little-endian byte order" +#endif /* HDMI Constants and Structures */ #define HDMI_PACKET_TYPE_INFO_FRAME (0x80) @@ -337,7 +283,6 @@ enum audio_sample_size { }; enum audio_coding_extended_type { -/* CODING TYPE_REFER_STREAM_HEADER */ CODING_TYPE_HE_AAC = 1, CODING_TYPE_HE_AACv2, CODING_TYPE_MPEG_SURROUND, @@ -392,11 +337,7 @@ struct __packed audio_info_frame { u8 future_byte_6_10[5]; }; -#else /* == !__LITTLE_ENDIAN_BITFIELD */ -#warning "structures defined and packed for little-endian byte order" -#endif - -static inline void cea861_checksum_info_frame(u8 * const info_frame) +static inline void cea861_checksum_hdmi_info_frame(u8 * const info_frame) { struct info_frame_header * const header = (struct info_frame_header *) info_frame;