From: Raymond Yau Date: Tue, 17 Jan 2012 03:32:17 +0000 (+0800) Subject: ALSA: Au88x0 - Implement subdevice volume controls X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=bb92b7c4ed4f7d5102bb1623cc8a1a9960ddfc08;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git ALSA: Au88x0 - Implement subdevice volume controls - 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 Signed-off-by: Takashi Iwai --- diff --git a/sound/pci/au88x0/au88x0.h b/sound/pci/au88x0/au88x0.h index bb938153a964..466a5c8e8354 100644 --- a/sound/pci/au88x0/au88x0.h +++ b/sound/pci/au88x0/au88x0.h @@ -26,7 +26,7 @@ #include #include #include - +#include #endif #ifndef CHIP_AU8820 @@ -107,6 +107,14 @@ #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 diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c index 6933a27a5d76..1181c5ec2d4f 100644 --- a/sound/pci/au88x0/au88x0_core.c +++ b/sound/pci/au88x0/au88x0_core.c @@ -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 { diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c index 0ef2f9712208..e59f120742a4 100644 --- a/sound/pci/au88x0/au88x0_pcm.c +++ b/sound/pci/au88x0/au88x0_pcm.c @@ -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; }