[alsa-devel] Intel8x0 8 channel sound

Takashi Iwai tiwai at suse.de
Mon Feb 18 07:59:17 CET 2008


At Sun, 17 Feb 2008 14:31:11 -0500,
Andrew Boettcher wrote:
> 
> [1  <multipart/alternative (7bit)>]
> [1.1  <text/plain; ISO-8859-1 (7bit)>]
> 
> [1.2  <text/html; ISO-8859-1 (7bit)>]
> On Feb 17, 2008 1:53 PM, Andrew Boettcher <a.boettcher at gmail.com> wrote:
> 
>     On Feb 15, 2008 11:01 AM, Takashi Iwai <tiwai at suse.de> wrote:
>    
>         At Thu, 14 Feb 2008 15:02:04 -0500,
>         Andrew Boettcher wrote:
>         >
>         > For some intel 8x0 cards there is 8 channel sound (such as the
>         ALC850 chip
>         > on NFORCE 4 boards), and this has not been patched yet though the
>         patch has
>         > been on the bug tracker.  Here it is again updated for 1.0.16.
>          There is
>         > also a patch for the configuration in alsa-lib for cards/
>         NFORCE.conf.  I
>         > have been patching my sources every time I update my kernel, I
>         figured it
>         > was time for someone to actually submit this somewhere else.  The
>         patch was
>         > written by Martin Ellis.
>        
>         Thanks for submitting this.  I haven't checked that issue.
>        
>         The change is almost OK.  One thing to be fixed is the enablement of
>         chip->multi8.  It's unconditionally enabled together with multi6.
>        
>         I think we should add a new bitflag in ac97_codec->flags to indicate
>         that the codec supports 8 channels.  Also, ac97_channel_mode_info()
>         shouldn't be unconditionally 8-channels, too.  It should check the
>         8-channel capability as well.
>        
>         Do you know which audio controller model supports 7.1 output?  We may
>         need to check the chip model eventualy for 7.1-enablement.
>         Certainly the earlier version of controllers don't support 7.1 output.
>         Well, but these are very unlikely with ALC850 chip, so maybe it
>         doesn't matter... :)
> 
>         The below is a quick-fixed patch.  Could you try it?
> 
>         Takashi
>        
>         ---
>        
>         diff -r 362cee3efa84 include/ac97_codec.h
>         --- a/include/ac97_codec.h      Fri Feb 15 16:43:11 2008 +0100
>         +++ b/include/ac97_codec.h      Fri Feb 15 16:59:51 2008 +0100
>         @@ -397,6 +397,7 @@
>          #define AC97_HAS_NO_TONE       (1<<16) /* no Tone volume */
>          #define AC97_HAS_NO_STD_PCM    (1<<17) /* no standard AC97 PCM volume
>         and mute */
>          #define AC97_HAS_NO_AUX                (1<<18) /* no standard AC97
>         AUX volume and mute */
>         +#define AC97_HAS_8CH           (1<<19) /* supports 8-channel output *
>         /
>        
>          /* rates indexes */
>          #define AC97_RATES_FRONT_DAC   0
>         diff -r 362cee3efa84 pci/ac97/ac97_patch.c
>         --- a/pci/ac97/ac97_patch.c     Fri Feb 15 16:43:11 2008 +0100
>         +++ b/pci/ac97/ac97_patch.c     Fri Feb 15 16:59:51 2008 +0100
>         @@ -114,10 +114,9 @@ static int ac97_surround_jack_mode_put(s
>        
>          static int ac97_channel_mode_info(struct snd_kcontrol *kcontrol,
>         struct snd_ctl_elem_info *uinfo)
>          {
>         -       static const char *texts[] = { "2ch", "4ch", "6ch" };
>         -       if (kcontrol->private_value)
>         -               return ac97_enum_text_info(kcontrol, uinfo, texts, 2);
>         /* 4ch only */
>         -       return ac97_enum_text_info(kcontrol, uinfo, texts, 3);
>         +       static const char *texts[] = { "2ch", "4ch", "6ch", "8ch" };
>         +       return ac97_enum_text_info(kcontrol, uinfo, texts,
>         +                                  kcontrol->private_value);
>          }
>        
>          static int ac97_channel_mode_get(struct snd_kcontrol *kcontrol,
>         struct snd_ctl_elem_value *ucontrol)
>         @@ -133,13 +132,8 @@ static int ac97_channel_mode_put(struct
>                struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol);
>                unsigned char mode = ucontrol->value.enumerated.item[0];
>        
>         -       if (kcontrol->private_value) {
>         -               if (mode >= 2)
>         -                       return -EINVAL;
>         -       } else {
>         -               if (mode >= 3)
>         -                       return -EINVAL;
>         -       }
>         +       if (mode >= kcontrol->private_value)
>         +               return -EINVAL;
>        
>                if (mode != ac97->channel_mode) {
>                        ac97->channel_mode = mode;
>         @@ -158,6 +152,7 @@ static int ac97_channel_mode_put(struct
>                        .get = ac97_surround_jack_mode_get, \
>                        .put = ac97_surround_jack_mode_put, \
>                }
>         +/* 6ch */
>          #define AC97_CHANNEL_MODE_CTL \
>                { \
>                        .iface  = SNDRV_CTL_ELEM_IFACE_MIXER, \
>         @@ -165,7 +160,9 @@ static int ac97_channel_mode_put(struct
>                        .info = ac97_channel_mode_info, \
>                        .get = ac97_channel_mode_get, \
>                        .put = ac97_channel_mode_put, \
>         +               .private_value = 3, \
>                }
>         +/* 4ch */
>          #define AC97_CHANNEL_MODE_4CH_CTL \
>                { \
>                        .iface  = SNDRV_CTL_ELEM_IFACE_MIXER, \
>         @@ -173,7 +170,17 @@ static int ac97_channel_mode_put(struct
>                        .info = ac97_channel_mode_info, \
>                        .get = ac97_channel_mode_get, \
>                        .put = ac97_channel_mode_put, \
>         -               .private_value = 1, \
>         +               .private_value = 2, \
>         +       }
>         +/* 8ch */
>         +#define AC97_CHANNEL_MODE_8CH_CTL \
>         +       { \
>         +               .iface  = SNDRV_CTL_ELEM_IFACE_MIXER, \
>         +               .name   = "Channel Mode", \
>         +               .info = ac97_channel_mode_info, \
>         +               .get = ac97_channel_mode_get, \
>         +               .put = ac97_channel_mode_put, \
>         +               .private_value = 4, \
>                }
>        
>          static inline int is_surround_on(struct snd_ac97 *ac97)
>         @@ -208,6 +215,11 @@ static inline int is_shared_micin(struct
>          static inline int is_shared_micin(struct snd_ac97 *ac97)
>          {
>                return !ac97->indep_surround && !is_clfe_on(ac97);
>         +}
>         +
>         +static inline int alc850_is_aux_back_surround(struct snd_ac97 *ac97)
>         +{
>         +       return is_surround_on(ac97);
>          }
> 
>         @@ -2816,10 +2828,12 @@ static int patch_alc655(struct snd_ac97
>        
>          #define AC97_ALC850_JACK_SELECT        0x76
>          #define AC97_ALC850_MISC1      0x7a
>         +#define AC97_ALC850_MULTICH    0x6a
>        
>          static void alc850_update_jacks(struct snd_ac97 *ac97)
>          {
>                int shared;
>         +       int aux_is_back_surround;
>        
>                /* shared Line-In / Surround Out */
>                shared = is_shared_surrout(ac97);
>         @@ -2837,13 +2851,18 @@ static void alc850_update_jacks(struct s
>                /* MIC-IN = 1, CENTER-LFE = 5 */
>                snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 4,
>                                     shared ? (5<<4) : (1<<4));
>         +
>         +       aux_is_back_surround = alc850_is_aux_back_surround(ac97);
>         +       /* Aux is Back Surround */
>         +       snd_ac97_update_bits(ac97, AC97_ALC850_MULTICH, 1 << 10,
>         +                            aux_is_back_surround ? (1<<10) : (0
>         <<10));
>          }
>        
>          static const struct snd_kcontrol_new snd_ac97_controls_alc850[] = {
>                AC97_PAGE_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1,
>         0, 0),
>                AC97_SINGLE("Mic Front Input Switch", AC97_ALC850_JACK_SELECT,
>         15, 1, 1),
>                AC97_SURROUND_JACK_MODE_CTL,
>         -       AC97_CHANNEL_MODE_CTL,
>         +       AC97_CHANNEL_MODE_8CH_CTL,
>          };
>        
>          static int patch_alc850_specific(struct snd_ac97 *ac97)
>         @@ -2878,6 +2897,7 @@ static int patch_alc850(struct snd_ac97
>                   spdif-in monitor off, spdif-in PCM off
>                   center on mic off, surround on line-in off
>                   duplicate front off
>         +          NB default bit 10=0 = Aux is Capture, not Back Surround
>                */
>                snd_ac97_write_cache(ac97, AC97_ALC650_MULTICH, 1<<15);
>                /* SURR_OUT: on, Surr 1kOhm: on, Surr Amp: off, Front 1kOhm:
>         off
>         diff -r 362cee3efa84 pci/intel8x0.c
>         --- a/pci/intel8x0.c    Fri Feb 15 16:43:11 2008 +0100
>         +++ b/pci/intel8x0.c    Fri Feb 15 16:59:51 2008 +0100
>         @@ -155,7 +155,8 @@ DEFINE_REGSET(SP, 0x60);    /* SPDIF out */
>          #define   ICH_PCM_SPDIF_69     0x80000000      /* s/pdif pcm on slots
>         6&9 */
>          #define   ICH_PCM_SPDIF_1011   0xc0000000      /* s/pdif pcm on slots
>         10&11 */
>          #define   ICH_PCM_20BIT                0x00400000      /* 20-bit
>         samples (ICH4) */
>         -#define   ICH_PCM_246_MASK     0x00300000      /* 6 channels (not all
>         chips) */
>         +#define   ICH_PCM_246_MASK     0x00300000      /* channels (not all
>         chips) */
>         +#define   ICH_PCM_8            0x00300000      /* 8 channels (not all
>         chips) */
>          #define   ICH_PCM_6            0x00200000      /* 6 channels (not all
>         chips) */
>          #define   ICH_PCM_4            0x00100000      /* 4 channels (not all
>         chips) */
>          #define   ICH_PCM_2            0x00000000      /* 2 channels (stereo)
>         */
>         @@ -382,6 +383,7 @@ struct intel8x0 {
>        
>                unsigned multi4: 1,
>                         multi6: 1,
>         +                multi8 :1,
>                         dra: 1,
>                         smp20bit: 1;
>                unsigned in_ac97_init: 1,
>         @@ -995,6 +997,8 @@ static void snd_intel8x0_setup_pcm_out(s
>                                cnt |= ICH_PCM_4;
>                        else if (runtime->channels == 6)
>                                cnt |= ICH_PCM_6;
>         +               else if (runtime->channels == 8)
>         +                       cnt |= ICH_PCM_8;
>                        if (chip->device_type == DEVICE_NFORCE) {
>                                /* reset to 2ch once to keep the 6 channel data
>         in alignment,
>                                 * to start from Front Left always
>         @@ -1104,6 +1108,16 @@ static struct snd_pcm_hw_constraint_list
>                .mask = 0,
>          };
>        
>         +static unsigned int channels8[] = {
>         +       2, 4, 6, 8,
>         +};
>         +
>         +static struct snd_pcm_hw_constraint_list hw_constraints_channels8 = {
>         +       .count = ARRAY_SIZE(channels8),
>         +       .list = channels8,
>         +       .mask = 0,
>         +};
>         +
>          static int snd_intel8x0_pcm_open(struct snd_pcm_substream *substream,
>         struct ichdev *ichdev)
>          {
>                struct intel8x0 *chip = snd_pcm_substream_chip(substream);
>         @@ -1134,7 +1148,12 @@ static int snd_intel8x0_playback_open(st
>                if (err < 0)
>                        return err;
>        
>         -       if (chip->multi6) {
>         +       if (chip->multi8) {
>         +               runtime->hw.channels_max = 8;
>         +               snd_pcm_hw_constraint_list(runtime, 0,
>         +                                        
>          SNDRV_PCM_HW_PARAM_CHANNELS,
>         +                                          &hw_constraints_channels8);
>         +       } else if (chip->multi6) {
>                        runtime->hw.channels_max = 6;
>                        snd_pcm_hw_constraint_list(runtime, 0,
>         SNDRV_PCM_HW_PARAM_CHANNELS,
>                                                   &hw_constraints_channels6);
>         @@ -2195,8 +2214,11 @@ static int __devinit snd_intel8x0_mixer(
>                }
>                if (pbus->pcms[0].r[0].slots & (1 << AC97_SLOT_PCM_SLEFT)) {
>                        chip->multi4 = 1;
>         -               if (pbus->pcms[0].r[0].slots & (1 << AC97_SLOT_LFE))
>         +               if (pbus->pcms[0].r[0].slots & (1 << AC97_SLOT_LFE)) {
>                                chip->multi6 = 1;
>         +                       if (chip->ac97[0]->flags & AC97_HAS_8CH)
>         +                               chip->multi8 = 1;
>         +               }
>                }
>                if (pbus->pcms[0].r[1].rslots[0]) {
>                        chip->dra = 1;
> 
>     It does not work 8 channel.  I found the problem though.  in line 2219 of
>     the patched pci/intel8x0.c it checks for the flag AC97_HAS_8CH
>     This flag is never set anywhere.  where would be the appropriate place?
> 
>                 if (chip->ac97[0]->flags & AC97_HAS_8CH)
>                     chip->multi8 = 1;
>    
>     Andrew Boettcher
> 
> I went ahead and  made a new patch, attached.  I put the flag in patch_alc850
> () in pci/ac97/ac97_patch.c

Yes, that's the right place.

> The only issue I see now is the imbalanced volumes, nothing a .asoundrc can't
> fix.  I wonder if the DAC volumes are maxed for the side and rear DACs?  In
> the code it only maxes 2 of them.  I don't have a data sheet to see how to max
> the others if they are maxed, or what the deal is.

Hm, could you elaborate?  Which DAC volume doesn't get the maximum
volume and which volume can be in the max level (supposed 0dB) via
which AC97 register / ALSA mixer control right now?


Takashi


More information about the Alsa-devel mailing list