From a232990b62358d6736e0ef03081093006eacfdff Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Wed, 18 Apr 2012 11:40:08 -0500 Subject: [PATCH] ENGR00180350-2 HDMI set infoframe information MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Get speaker allocation data block information from the EDID. Translate the EDID speaker allocation to audio infoframe speaker allocation (different bit mapping) given the number of channels of audio being played. Set channel count information in HDMI_FC_AUDICONF0. Set speaker allocation information in HDMI_FC_AUDICONF2. From CEA-861-D spec: NOTE—HDMI requires the CT, SS and SF fields to be set to 0 (Refer to Stream Header) as these items are carried in the audio stream. Signed-off-by: Alan Tull --- drivers/video/mxc/mxc_edid.c | 10 ++ sound/soc/codecs/mxc_hdmi.c | 212 ++++++++++++++++++++++++++++++++++- 2 files changed, 216 insertions(+), 6 deletions(-) diff --git a/drivers/video/mxc/mxc_edid.c b/drivers/video/mxc/mxc_edid.c index e8a2b9761b65..f86313df7dea 100644 --- a/drivers/video/mxc/mxc_edid.c +++ b/drivers/video/mxc/mxc_edid.c @@ -350,6 +350,16 @@ int mxc_edid_parse_ext_blk(unsigned char *edid, break; } case 0x4: /*Speaker allocation block*/ + { + i = 0; + while (i < blklen) { + cfg->speaker_alloc = edid[index + 1]; + index += 3; + i += 3; + DPRINTK("Speaker Alloc : %02x\n", cfg->speaker_alloc); + } + break; + } case 0x7: /*User extended block*/ default: /* skip */ diff --git a/sound/soc/codecs/mxc_hdmi.c b/sound/soc/codecs/mxc_hdmi.c index 4d9a5719eeae..d61de0090b55 100644 --- a/sound/soc/codecs/mxc_hdmi.c +++ b/sound/soc/codecs/mxc_hdmi.c @@ -2,6 +2,12 @@ * MXC HDMI ALSA Soc Codec Driver * * Copyright (C) 2011-2012 Freescale Semiconductor, Inc. + * + * Some code from patch_hdmi.c + * Copyright (c) 2008-2010 Intel Corporation. All rights reserved. + * Copyright (c) 2006 ATI Technologies Inc. + * Copyright (c) 2008 NVIDIA Corp. All rights reserved. + * Copyright (c) 2008 Wei Ni */ /* @@ -88,6 +94,185 @@ static void dumpregs(void) static void dumpregs(void) {} #endif +enum cea_speaker_placement { + FL = (1 << 0), /* Front Left */ + FC = (1 << 1), /* Front Center */ + FR = (1 << 2), /* Front Right */ + FLC = (1 << 3), /* Front Left Center */ + FRC = (1 << 4), /* Front Right Center */ + RL = (1 << 5), /* Rear Left */ + RC = (1 << 6), /* Rear Center */ + RR = (1 << 7), /* Rear Right */ + RLC = (1 << 8), /* Rear Left Center */ + RRC = (1 << 9), /* Rear Right Center */ + LFE = (1 << 10), /* Low Frequency Effect */ + FLW = (1 << 11), /* Front Left Wide */ + FRW = (1 << 12), /* Front Right Wide */ + FLH = (1 << 13), /* Front Left High */ + FCH = (1 << 14), /* Front Center High */ + FRH = (1 << 15), /* Front Right High */ + TC = (1 << 16), /* Top Center */ +}; + +/* + * EDID SA bits in the CEA Speaker Allocation data block + */ +static int edid_speaker_allocation_bits[] = { + [0] = FL | FR, + [1] = LFE, + [2] = FC, + [3] = RL | RR, + [4] = RC, + [5] = FLC | FRC, + [6] = RLC | RRC, + [7] = FLW | FRW, + [8] = FLH | FRH, + [9] = TC, + [10] = FCH, +}; + +struct cea_channel_speaker_allocation { + int ca_index; + int speakers[8]; + + /* derived values, just for convenience */ + int channels; + int spk_mask; +}; + +/* + * This is an ordered list! + * + * The preceding ones have better chances to be selected by + * hdmi_channel_allocation(). + */ +static struct cea_channel_speaker_allocation channel_allocations[] = { +/* channel: 7 6 5 4 3 2 1 0 */ +{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, + /* 2.1 */ +{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } }, + /* Dolby Surround */ +{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } }, + /* surround40 */ +{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } }, + /* surround41 */ +{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } }, + /* surround50 */ +{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } }, + /* surround51 */ +{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } }, + /* 6.1 */ +{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } }, + /* surround71 */ +{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } }, + +{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } }, +{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } }, +{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } }, +{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } }, +{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } }, +{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } }, +{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } }, +{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } }, +{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } }, +{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } }, +{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } }, +{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } }, +{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } }, +{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } }, +{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } }, +}; + +/* + * Compute derived values in channel_allocations[]. + */ +static void init_channel_allocations(void) +{ + int i, j; + struct cea_channel_speaker_allocation *p; + + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + p = channel_allocations + i; + p->channels = 0; + p->spk_mask = 0; + for (j = 0; j < ARRAY_SIZE(p->speakers); j++) + if (p->speakers[j]) { + p->channels++; + p->spk_mask |= p->speakers[j]; + } + } +} + +/* + * The transformation takes two steps: + * + * speaker_alloc => (edid_speaker_allocation_bits[]) => spk_mask + * spk_mask => (channel_allocations[]) => CA + * + * TODO: it could select the wrong CA from multiple candidates. +*/ +static int hdmi_channel_allocation(int channels) +{ + int i; + int ca = 0; + int spk_mask = 0; + + /* + * CA defaults to 0 for basic stereo audio + */ + if (channels <= 2) + return 0; + + /* + * expand EDID's speaker allocation mask + * + * EDID tells the speaker mask in a compact(paired) form, + * expand EDID's notions to match the ones used by Audio InfoFrame. + */ + for (i = 0; i < ARRAY_SIZE(edid_speaker_allocation_bits); i++) { + if (edid_cfg.speaker_alloc & (1 << i)) + spk_mask |= edid_speaker_allocation_bits[i]; + } + + /* search for the first working match in the CA table */ + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + if (channels == channel_allocations[i].channels && + (spk_mask & channel_allocations[i].spk_mask) == + channel_allocations[i].spk_mask) { + ca = channel_allocations[i].ca_index; + break; + } + } + + return ca; +} + static void hdmi_set_audio_flat(u8 value) { /* Indicates the subpacket represents a flatline sample */ @@ -103,12 +288,25 @@ static void hdmi_set_layout(unsigned int channels) HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_MASK); } -static void hdmi_set_audio_infoframe(void) +static void hdmi_set_audio_infoframe(unsigned int channels) { - /* set to 0: means "refer to stream header" */ - hdmi_writeb(0x00, HDMI_FC_AUDICONF0); - hdmi_writeb(0x00, HDMI_FC_AUDICONF1); - hdmi_writeb(0x00, HDMI_FC_AUDICONF2); + unsigned char audiconf0, audiconf2; + + /* From CEA-861-D spec: + * NOTE: HDMI requires the CT, SS and SF fields to be set to 0 ("Refer + * to Stream Header") as these items are carried in the audio stream. + * + * So we only set the CC and CA fields. + */ + audiconf0 = ((channels - 1) << HDMI_FC_AUDICONF0_CC_OFFSET) & + HDMI_FC_AUDICONF0_CC_MASK; + + audiconf2 = hdmi_channel_allocation(channels); + + hdmi_writeb(audiconf0, HDMI_FC_AUDICONF0); + hdmi_writeb(0, HDMI_FC_AUDICONF1); + hdmi_writeb(audiconf2, HDMI_FC_AUDICONF2); + hdmi_writeb(0, HDMI_FC_AUDICONF3); } static int cea_audio_rates[HDMI_MAX_RATES] = { @@ -245,7 +443,6 @@ static int mxc_hdmi_codec_startup(struct snd_pcm_substream *substream, return ret; hdmi_set_audio_flat(0); - hdmi_set_audio_infoframe(); return 0; } @@ -255,6 +452,7 @@ static int mxc_hdmi_codec_prepare(struct snd_pcm_substream *substream, { struct snd_pcm_runtime *runtime = substream->runtime; + hdmi_set_audio_infoframe(runtime->channels); hdmi_set_layout(runtime->channels); hdmi_set_sample_rate(runtime->rate); dumpregs(); @@ -374,6 +572,8 @@ static int mxc_hdmi_codec_soc_probe(struct snd_soc_codec *codec) if (ret) goto e_add_ctrls; + init_channel_allocations(); + snd_soc_codec_set_drvdata(codec, hdmi_priv); return 0; -- 2.39.5