]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ENGR00180350-2 HDMI set infoframe information
authorAlan Tull <r80115@freescale.com>
Wed, 18 Apr 2012 16:40:08 +0000 (11:40 -0500)
committerLothar Waßmann <LW@KARO-electronics.de>
Fri, 24 May 2013 06:34:29 +0000 (08:34 +0200)
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 <r80115@freescale.com>
drivers/video/mxc/mxc_edid.c
sound/soc/codecs/mxc_hdmi.c

index e8a2b9761b6523c598bc4ba026fb80c7c93331d8..f86313df7deaf00645608caf5ce4f5bbb38b35b5 100644 (file)
@@ -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 */
index 4d9a5719eeae773b6568c1c46c1d08059390289c..d61de0090b553a65cdab13a908b5e24720db87b1 100644 (file)
@@ -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 <wni@nvidia.com>
  */
 
 /*
@@ -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;