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.
This commit is contained in:
Saleem Abdulrasool
2011-01-16 13:26:13 -08:00
parent b8276b737b
commit b9b3689853
2 changed files with 68 additions and 167 deletions

View File

@@ -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,

View File

@@ -34,120 +34,66 @@
/* Header file for EIA CEA 861-E structures and definitions */
#if defined(__LITTLE_ENDIAN_BITFIELD)
#include <linux/edid.h>
#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;