Commit bb92b7c4 authored by Raymond Yau's avatar Raymond Yau Committed by Takashi Iwai

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: default avatarRaymond Yau <superquad.vortex2@gmail.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent dcd6c922
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
#include <sound/mpu401.h> #include <sound/mpu401.h>
#include <sound/hwdep.h> #include <sound/hwdep.h>
#include <sound/ac97_codec.h> #include <sound/ac97_codec.h>
#include <sound/tlv.h>
#endif #endif
#ifndef CHIP_AU8820 #ifndef CHIP_AU8820
...@@ -107,6 +107,14 @@ ...@@ -107,6 +107,14 @@
#define NR_WTPB 0x20 /* WT channels per each bank. */ #define NR_WTPB 0x20 /* WT channels per each bank. */
#define NR_PCM 0x10 #define NR_PCM 0x10
struct pcm_vol {
struct snd_kcontrol *kctl;
int active;
int dma;
int mixin[4];
int vol[4];
};
/* Structs */ /* Structs */
typedef struct { typedef struct {
//int this_08; /* Still unknown */ //int this_08; /* Still unknown */
...@@ -168,6 +176,7 @@ struct snd_vortex { ...@@ -168,6 +176,7 @@ struct snd_vortex {
/* Xtalk canceler */ /* Xtalk canceler */
int xt_mode; /* 1: speakers, 0:headphones. */ int xt_mode; /* 1: speakers, 0:headphones. */
#endif #endif
struct pcm_vol pcm_vol[NR_PCM];
int isquad; /* cache of extended ID codec flag. */ int isquad; /* cache of extended ID codec flag. */
...@@ -239,7 +248,7 @@ static int vortex_alsafmt_aspfmt(int alsafmt); ...@@ -239,7 +248,7 @@ static int vortex_alsafmt_aspfmt(int alsafmt);
/* Connection stuff. */ /* Connection stuff. */
static void vortex_connect_default(vortex_t * vortex, int en); static void vortex_connect_default(vortex_t * vortex, int en);
static int vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, 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, static char vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out,
int restype); int restype);
#ifndef CHIP_AU8810 #ifndef CHIP_AU8810
......
...@@ -2050,8 +2050,6 @@ vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype) ...@@ -2050,8 +2050,6 @@ vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype)
} }
/* Default Connections */ /* 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) static void vortex_connect_default(vortex_t * vortex, int en)
{ {
...@@ -2111,15 +2109,13 @@ 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. Return: Return allocated DMA or same DMA passed as "dma" when dma >= 0.
*/ */
static int 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; stream_t *stream;
int i, en; int i, en;
struct pcm_vol *p;
if ((nr_ch == 3)
|| ((dir == SNDRV_PCM_STREAM_CAPTURE) && (nr_ch > 2)))
return -EBUSY;
if (dma >= 0) { if (dma >= 0) {
en = 0; en = 0;
vortex_adb_checkinout(vortex, vortex_adb_checkinout(vortex,
...@@ -2250,6 +2246,14 @@ vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type) ...@@ -2250,6 +2246,14 @@ vortex_adb_allocroute(vortex_t * vortex, int dma, int nr_ch, int dir, int type)
MIX_DEFIGAIN); MIX_DEFIGAIN);
#endif #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 #ifndef CHIP_AU8820
else { else {
......
...@@ -122,6 +122,18 @@ static struct snd_pcm_hw_constraint_list hw_constraints_au8830_channels = { ...@@ -122,6 +122,18 @@ static struct snd_pcm_hw_constraint_list hw_constraints_au8830_channels = {
.mask = 0, .mask = 0,
}; };
#endif #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 */ /* open callback */
static int snd_vortex_pcm_open(struct snd_pcm_substream *substream) 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, ...@@ -230,12 +242,14 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream,
if (stream != NULL) if (stream != NULL)
vortex_adb_allocroute(chip, stream->dma, vortex_adb_allocroute(chip, stream->dma,
stream->nr_ch, stream->dir, stream->nr_ch, stream->dir,
stream->type); stream->type,
substream->number);
/* Alloc routes. */ /* Alloc routes. */
dma = dma =
vortex_adb_allocroute(chip, -1, vortex_adb_allocroute(chip, -1,
params_channels(hw_params), params_channels(hw_params),
substream->stream, type); substream->stream, type,
substream->number);
if (dma < 0) { if (dma < 0) {
spin_unlock_irq(&chip->lock); spin_unlock_irq(&chip->lock);
return dma; return dma;
...@@ -246,6 +260,11 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream, ...@@ -246,6 +260,11 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream,
vortex_adbdma_setbuffers(chip, dma, vortex_adbdma_setbuffers(chip, dma,
params_period_bytes(hw_params), params_period_bytes(hw_params),
params_periods(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 #ifndef CHIP_AU8810
else { else {
...@@ -275,10 +294,18 @@ static int snd_vortex_pcm_hw_free(struct snd_pcm_substream *substream) ...@@ -275,10 +294,18 @@ static int snd_vortex_pcm_hw_free(struct snd_pcm_substream *substream)
spin_lock_irq(&chip->lock); spin_lock_irq(&chip->lock);
// Delete audio routes. // Delete audio routes.
if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) { 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, vortex_adb_allocroute(chip, stream->dma,
stream->nr_ch, stream->dir, stream->nr_ch, stream->dir,
stream->type); stream->type,
substream->number);
}
} }
#ifndef CHIP_AU8810 #ifndef CHIP_AU8810
else { else {
...@@ -506,6 +533,83 @@ static struct snd_kcontrol_new snd_vortex_mixer_spdif[] __devinitdata = { ...@@ -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 */ /* create a pcm device */
static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr) 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) ...@@ -555,5 +659,20 @@ static int __devinit snd_vortex_new_pcm(vortex_t *chip, int idx, int nr)
return err; 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; return 0;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment