ALSA: Au88x0 - Implement subdevice volume controls
authorRaymond Yau <superquad.vortex2@gmail.com>
Tue, 17 Jan 2012 03:32:17 +0000 (11:32 +0800)
committerTakashi Iwai <tiwai@suse.de>
Tue, 24 Jan 2012 14:25:08 +0000 (15:25 +0100)
- add "PCM Playback Volume" controls for 16 playback subdevices
  This allow application to change the volume of each subdevice
  by using hardware mixer of au88x0 and default is zero gain/attenunation.

Signed-off-by: Raymond Yau <superquad.vortex2@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/au88x0/au88x0.h
sound/pci/au88x0/au88x0_core.c
sound/pci/au88x0/au88x0_pcm.c

index bb938153a964811b41f471f064fbd293fb660e77..466a5c8e8354f2c7eb3b08cda3130405968db759 100644 (file)
@@ -26,7 +26,7 @@
 #include <sound/mpu401.h>
 #include <sound/hwdep.h>
 #include <sound/ac97_codec.h>
-
+#include <sound/tlv.h>
 #endif
 
 #ifndef CHIP_AU8820
 #define NR_WTPB 0x20           /* WT channels per each bank. */
 #define NR_PCM 0x10
 
+struct pcm_vol {
+       struct snd_kcontrol *kctl;
+       int active;
+       int dma;
+       int mixin[4];
+       int vol[4];
+};
+
 /* Structs */
 typedef struct {
        //int this_08;          /* Still unknown */
@@ -168,6 +176,7 @@ struct snd_vortex {
        /* Xtalk canceler */
        int xt_mode;            /* 1: speakers, 0:headphones. */
 #endif
+       struct pcm_vol pcm_vol[NR_PCM];
 
        int isquad;             /* cache of extended ID codec flag. */
 
@@ -239,7 +248,7 @@ static int vortex_alsafmt_aspfmt(int alsafmt);
 /* Connection  stuff. */
 static void vortex_connect_default(vortex_t * vortex, int en);
 static int vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch,
-                                int dir, int type);
+                                int dir, int type, int subdev);
 static char vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out,
                                  int restype);
 #ifndef CHIP_AU8810
index 6933a27a5d76279e1c98b59953200dbd52b84e08..1181c5ec2d4fe44638163237295abd990a98de0f 100644 (file)
@@ -2050,8 +2050,6 @@ vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype)
 }
 
 /* Default Connections  */
-static int
-vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type);
 
 static void vortex_connect_default(vortex_t * vortex, int en)
 {
@@ -2111,15 +2109,13 @@ static void vortex_connect_default(vortex_t * vortex, int en)
   Return: Return allocated DMA or same DMA passed as "dma" when dma >= 0.
 */
 static int
-vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type)
+vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
+                       int type, int subdev)
 {
        stream_t *stream;
        int i, en;
+       struct pcm_vol *p;
        
-       if ((nr_ch == 3)
-           || ((dir == SNDRV_PCM_STREAM_CAPTURE) && (nr_ch > 2)))
-               return -EBUSY;
-
        if (dma >= 0) {
                en = 0;
                vortex_adb_checkinout(vortex,
@@ -2250,6 +2246,14 @@ vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type)
                                                              MIX_DEFIGAIN);
 #endif
                        }
+                       if (stream->type == VORTEX_PCM_ADB && en) {
+                               p = &vortex->pcm_vol[subdev];
+                               p->dma = dma;
+                               for (i = 0; i < nr_ch; i++)
+                                       p->mixin[i] = mix[i];
+                               for (i = 0; i < ch_top; i++)
+                                       p->vol[i] = 0;
+                       }
                }
 #ifndef CHIP_AU8820
                else {
index 0ef2f97122080f206699a01e32b7f1f1ce7b6825..e59f120742a48e4140a54d80c63f6592c325c220 100644 (file)
@@ -122,6 +122,18 @@ static struct snd_pcm_hw_constraint_list hw_constraints_au8830_channels = {
        .mask = 0,
 };
 #endif
+
+static void vortex_notify_pcm_vol_change(struct snd_card *card,
+                       struct snd_kcontrol *kctl, int activate)
+{
+       if (activate)
+               kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+       else
+               kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+       snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE |
+                               SNDRV_CTL_EVENT_MASK_INFO, &(kctl->id));
+}
+
 /* open callback */
 static int snd_vortex_pcm_open(struct snd_pcm_substream *substream)
 {
@@ -230,12 +242,14 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream,
                if (stream != NULL)
                        vortex_adb_allocroute(chip, stream->dma,
                                              stream->nr_ch, stream->dir,
-                                             stream->type);
+                                             stream->type,
+                                             substream->number);
                /* Alloc routes. */
                dma =
                    vortex_adb_allocroute(chip, -1,
                                          params_channels(hw_params),
-                                         substream->stream, type);
+                                         substream->stream, type,
+                                         substream->number);
                if (dma < 0) {
                        spin_unlock_irq(&chip->lock);
                        return dma;
@@ -246,6 +260,11 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream,
                vortex_adbdma_setbuffers(chip, dma,
                                         params_period_bytes(hw_params),
                                         params_periods(hw_params));
+               if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) {
+                       chip->pcm_vol[substream->number].active = 1;
+                       vortex_notify_pcm_vol_change(chip->card,
+                               chip->pcm_vol[substream->number].kctl, 1);
+               }
        }
 #ifndef CHIP_AU8810
        else {
@@ -275,10 +294,18 @@ static int snd_vortex_pcm_hw_free(struct snd_pcm_substream *substream)
        spin_lock_irq(&chip->lock);
        // Delete audio routes.
        if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) {
-               if (stream != NULL)
+               if (stream != NULL) {
+                       if (VORTEX_PCM_TYPE(substream->pcm) == VORTEX_PCM_ADB) {
+                               chip->pcm_vol[substream->number].active = 0;
+                               vortex_notify_pcm_vol_change(chip->card,
+                                       chip->pcm_vol[substream->number].kctl,
+                                       0);
+                       }
                        vortex_adb_allocroute(chip, stream->dma,
                                              stream->nr_ch, stream->dir,
-                                             stream->type);
+                                             stream->type,
+                                             substream->number);
+               }
        }
 #ifndef CHIP_AU8810
        else {
@@ -506,6 +533,83 @@ static struct snd_kcontrol_new snd_vortex_mixer_spdif[] __devinitdata = {
        },
 };
 
+/* subdevice PCM Volume control */
+
+static int snd_vortex_pcm_vol_info(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_info *uinfo)
+{
+       vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
+       uinfo->value.integer.min = -128;
+       uinfo->value.integer.max = 32;
+       return 0;
+}
+
+static int snd_vortex_pcm_vol_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       int i;
+       vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+       int subdev = kcontrol->id.subdevice;
+       struct pcm_vol *p = &vortex->pcm_vol[subdev];
+       int max_chn = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
+       for (i = 0; i < max_chn; i++)
+               ucontrol->value.integer.value[i] = p->vol[i];
+       return 0;
+}
+
+static int snd_vortex_pcm_vol_put(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       int i;
+       int changed = 0;
+       int mixin;
+       unsigned char vol;
+       vortex_t *vortex = snd_kcontrol_chip(kcontrol);
+       int subdev = kcontrol->id.subdevice;
+       struct pcm_vol *p = &vortex->pcm_vol[subdev];
+       int max_chn = (VORTEX_IS_QUAD(vortex) ? 4 : 2);
+       for (i = 0; i < max_chn; i++) {
+               if (p->vol[i] != ucontrol->value.integer.value[i]) {
+                       p->vol[i] = ucontrol->value.integer.value[i];
+                       if (p->active) {
+                               switch (vortex->dma_adb[p->dma].nr_ch) {
+                               case 1:
+                                       mixin = p->mixin[0];
+                                       break;
+                               case 2:
+                               default:
+                                       mixin = p->mixin[(i < 2) ? i : (i - 2)];
+                                       break;
+                               case 4:
+                                       mixin = p->mixin[i];
+                                       break;
+                               };
+                               vol = p->vol[i];
+                               vortex_mix_setinputvolumebyte(vortex,
+                                       vortex->mixplayb[i], mixin, vol);
+                       }
+                       changed = 1;
+               }
+       }
+       return changed;
+}
+
+static const DECLARE_TLV_DB_MINMAX(vortex_pcm_vol_db_scale, -9600, 2400);
+
+static struct snd_kcontrol_new snd_vortex_pcm_vol __devinitdata = {
+       .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+       .name = "PCM Playback Volume",
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+               SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+               SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+       .info = snd_vortex_pcm_vol_info,
+       .get = snd_vortex_pcm_vol_get,
+       .put = snd_vortex_pcm_vol_put,
+       .tlv = { .p = vortex_pcm_vol_db_scale },
+};
+
 /* create a pcm device */
 static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
 {
@@ -555,5 +659,20 @@ static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
                                return err;
                }
        }
+       if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_ADB) {
+               for (i = 0; i < NR_PCM; i++) {
+                       chip->pcm_vol[i].active = 0;
+                       chip->pcm_vol[i].dma = -1;
+                       kctl = snd_ctl_new1(&snd_vortex_pcm_vol, chip);
+                       if (!kctl)
+                               return -ENOMEM;
+                       chip->pcm_vol[i].kctl = kctl;
+                       kctl->id.device = 0;
+                       kctl->id.subdevice = i;
+                       err = snd_ctl_add(chip->card, kctl);
+                       if (err < 0)
+                               return err;
+               }
+       }
        return 0;
 }