Skip to content

Commit

Permalink
[ALSA] ymfpci: add per-voice volume controls
Browse files Browse the repository at this point in the history
YMFPCI driver
Implements mixer controls for the volume of each playback substream of
the main PCM device.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
  • Loading branch information
Clemens Ladisch authored and Jaroslav Kysela committed Aug 30, 2005
1 parent a55bfdc commit 9bcf655
Show file tree
Hide file tree
Showing 2 changed files with 166 additions and 72 deletions.
6 changes: 6 additions & 0 deletions include/sound/ymfpci.h
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ struct _snd_ymfpci_pcm {
unsigned int running: 1;
unsigned int output_front: 1;
unsigned int output_rear: 1;
unsigned int update_pcm_vol;
u32 period_size; /* cached from runtime->period_size */
u32 buffer_size; /* cached from runtime->buffer_size */
u32 period_pos;
Expand Down Expand Up @@ -367,6 +368,11 @@ struct _snd_ymfpci {
int mode_dup4ch;
int rear_opened;
int spdif_opened;
struct {
u16 left;
u16 right;
snd_kcontrol_t *ctl;
} pcm_mixer[32];

spinlock_t reg_lock;
spinlock_t voice_lock;
Expand Down
232 changes: 160 additions & 72 deletions sound/pci/ymfpci/ymfpci_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,26 @@ static void snd_ymfpci_pcm_interrupt(ymfpci_t *chip, ymfpci_voice_t *voice)
snd_pcm_period_elapsed(ypcm->substream);
spin_lock(&chip->reg_lock);
}

if (unlikely(ypcm->update_pcm_vol)) {
unsigned int subs = ypcm->substream->number;
unsigned int next_bank = 1 - chip->active_bank;
snd_ymfpci_playback_bank_t *bank;
u32 volume;

bank = &voice->bank[next_bank];
volume = cpu_to_le32(chip->pcm_mixer[subs].left << 15);
bank->left_gain_end = volume;
if (ypcm->output_rear)
bank->eff2_gain_end = volume;
if (ypcm->voices[1])
bank = &ypcm->voices[1]->bank[next_bank];
volume = cpu_to_le32(chip->pcm_mixer[subs].right << 15);
bank->right_gain_end = volume;
if (ypcm->output_rear)
bank->eff3_gain_end = volume;
ypcm->update_pcm_vol--;
}
}
spin_unlock(&chip->reg_lock);
}
Expand Down Expand Up @@ -451,87 +471,74 @@ static int snd_ymfpci_pcm_voice_alloc(ymfpci_pcm_t *ypcm, int voices)
return 0;
}

static void snd_ymfpci_pcm_init_voice(ymfpci_voice_t *voice, int stereo,
int rate, int w_16, unsigned long addr,
unsigned int end,
int output_front, int output_rear)
static void snd_ymfpci_pcm_init_voice(ymfpci_pcm_t *ypcm, unsigned int voiceidx,
snd_pcm_runtime_t *runtime,
int has_pcm_volume)
{
ymfpci_voice_t *voice = ypcm->voices[voiceidx];
u32 format;
u32 delta = snd_ymfpci_calc_delta(rate);
u32 lpfQ = snd_ymfpci_calc_lpfQ(rate);
u32 lpfK = snd_ymfpci_calc_lpfK(rate);
u32 delta = snd_ymfpci_calc_delta(runtime->rate);
u32 lpfQ = snd_ymfpci_calc_lpfQ(runtime->rate);
u32 lpfK = snd_ymfpci_calc_lpfK(runtime->rate);
snd_ymfpci_playback_bank_t *bank;
unsigned int nbank;
u32 vol_left, vol_right;
u8 use_left, use_right;

snd_assert(voice != NULL, return);
format = (stereo ? 0x00010000 : 0) | (w_16 ? 0 : 0x80000000);
if (runtime->channels == 1) {
use_left = 1;
use_right = 1;
} else {
use_left = (voiceidx & 1) == 0;
use_right = !use_left;
}
if (has_pcm_volume) {
vol_left = cpu_to_le32(ypcm->chip->pcm_mixer
[ypcm->substream->number].left << 15);
vol_right = cpu_to_le32(ypcm->chip->pcm_mixer
[ypcm->substream->number].right << 15);
} else {
vol_left = cpu_to_le32(0x40000000);
vol_right = cpu_to_le32(0x40000000);
}
format = runtime->channels == 2 ? 0x00010000 : 0;
if (snd_pcm_format_width(runtime->format) == 8)
format |= 0x80000000;
if (runtime->channels == 2 && (voiceidx & 1) != 0)
format |= 1;
for (nbank = 0; nbank < 2; nbank++) {
bank = &voice->bank[nbank];
memset(bank, 0, sizeof(*bank));
bank->format = cpu_to_le32(format);
bank->loop_default = 0;
bank->base = cpu_to_le32(addr);
bank->loop_start = 0;
bank->loop_end = cpu_to_le32(end);
bank->loop_frac = 0;
bank->eg_gain_end = cpu_to_le32(0x40000000);
bank->base = cpu_to_le32(runtime->dma_addr);
bank->loop_end = cpu_to_le32(ypcm->buffer_size);
bank->lpfQ = cpu_to_le32(lpfQ);
bank->status = 0;
bank->num_of_frames = 0;
bank->loop_count = 0;
bank->start = 0;
bank->start_frac = 0;
bank->delta =
bank->delta_end = cpu_to_le32(delta);
bank->lpfK =
bank->lpfK_end = cpu_to_le32(lpfK);
bank->eg_gain = cpu_to_le32(0x40000000);
bank->lpfD1 =
bank->lpfD2 = 0;

bank->left_gain =
bank->right_gain =
bank->left_gain_end =
bank->right_gain_end =
bank->eff1_gain =
bank->eff2_gain =
bank->eff3_gain =
bank->eff1_gain_end =
bank->eff2_gain_end =
bank->eff3_gain_end = 0;

if (!stereo) {
if (output_front) {
bank->left_gain =
bank->eg_gain =
bank->eg_gain_end = cpu_to_le32(0x40000000);

if (ypcm->output_front) {
if (use_left) {
bank->left_gain =
bank->left_gain_end = vol_left;
}
if (use_right) {
bank->right_gain =
bank->left_gain_end =
bank->right_gain_end = cpu_to_le32(0x40000000);
bank->right_gain_end = vol_right;
}
if (output_rear) {
}
if (ypcm->output_rear) {
if (use_left) {
bank->eff2_gain =
bank->eff2_gain_end =
bank->eff3_gain =
bank->eff3_gain_end = cpu_to_le32(0x40000000);
}
} else {
if (output_front) {
if ((voice->number & 1) == 0) {
bank->left_gain =
bank->left_gain_end = cpu_to_le32(0x40000000);
} else {
bank->format |= cpu_to_le32(1);
bank->right_gain =
bank->right_gain_end = cpu_to_le32(0x40000000);
}
bank->eff2_gain_end = vol_left;
}
if (output_rear) {
if ((voice->number & 1) == 0) {
bank->eff3_gain =
bank->eff3_gain_end = cpu_to_le32(0x40000000);
} else {
bank->format |= cpu_to_le32(1);
bank->eff2_gain =
bank->eff2_gain_end = cpu_to_le32(0x40000000);
}
if (use_right) {
bank->eff3_gain =
bank->eff3_gain_end = vol_right;
}
}
}
Expand Down Expand Up @@ -613,7 +620,7 @@ static int snd_ymfpci_playback_hw_free(snd_pcm_substream_t * substream)

static int snd_ymfpci_playback_prepare(snd_pcm_substream_t * substream)
{
// ymfpci_t *chip = snd_pcm_substream_chip(substream);
ymfpci_t *chip = snd_pcm_substream_chip(substream);
snd_pcm_runtime_t *runtime = substream->runtime;
ymfpci_pcm_t *ypcm = runtime->private_data;
unsigned int nvoice;
Expand All @@ -623,14 +630,8 @@ static int snd_ymfpci_playback_prepare(snd_pcm_substream_t * substream)
ypcm->period_pos = 0;
ypcm->last_pos = 0;
for (nvoice = 0; nvoice < runtime->channels; nvoice++)
snd_ymfpci_pcm_init_voice(ypcm->voices[nvoice],
runtime->channels == 2,
runtime->rate,
snd_pcm_format_width(runtime->format) == 16,
runtime->dma_addr,
ypcm->buffer_size,
ypcm->output_front,
ypcm->output_rear);
snd_ymfpci_pcm_init_voice(ypcm, nvoice, runtime,
substream->pcm == chip->pcm);
return 0;
}

Expand Down Expand Up @@ -882,6 +883,7 @@ static int snd_ymfpci_playback_open(snd_pcm_substream_t * substream)
ymfpci_t *chip = snd_pcm_substream_chip(substream);
snd_pcm_runtime_t *runtime = substream->runtime;
ymfpci_pcm_t *ypcm;
snd_kcontrol_t *kctl;
int err;

if ((err = snd_ymfpci_playback_open_1(substream)) < 0)
Expand All @@ -895,6 +897,10 @@ static int snd_ymfpci_playback_open(snd_pcm_substream_t * substream)
chip->rear_opened++;
}
spin_unlock_irq(&chip->reg_lock);

kctl = chip->pcm_mixer[substream->number].ctl;
kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_INFO, &kctl->id);
return 0;
}

Expand Down Expand Up @@ -987,13 +993,17 @@ static int snd_ymfpci_playback_close(snd_pcm_substream_t * substream)
{
ymfpci_t *chip = snd_pcm_substream_chip(substream);
ymfpci_pcm_t *ypcm = substream->runtime->private_data;
snd_kcontrol_t *kctl;

spin_lock_irq(&chip->reg_lock);
if (ypcm->output_rear && chip->rear_opened > 0) {
chip->rear_opened--;
ymfpci_close_extension(chip);
}
spin_unlock_irq(&chip->reg_lock);
kctl = chip->pcm_mixer[substream->number].ctl;
kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_INFO, &kctl->id);
return snd_ymfpci_playback_close_1(substream);
}

Expand Down Expand Up @@ -1665,6 +1675,66 @@ static snd_kcontrol_new_t snd_ymfpci_rear_shared __devinitdata = {
.private_value = 2,
};

/*
* PCM voice volume
*/

static int snd_ymfpci_pcm_vol_info(snd_kcontrol_t *kcontrol,
snd_ctl_elem_info_t *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 0x8000;
return 0;
}

static int snd_ymfpci_pcm_vol_get(snd_kcontrol_t *kcontrol,
snd_ctl_elem_value_t *ucontrol)
{
ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
unsigned int subs = kcontrol->id.subdevice;

ucontrol->value.integer.value[0] = chip->pcm_mixer[subs].left;
ucontrol->value.integer.value[1] = chip->pcm_mixer[subs].right;
return 0;
}

static int snd_ymfpci_pcm_vol_put(snd_kcontrol_t *kcontrol,
snd_ctl_elem_value_t *ucontrol)
{
ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
unsigned int subs = kcontrol->id.subdevice;
snd_pcm_substream_t *substream;
unsigned long flags;

if (ucontrol->value.integer.value[0] != chip->pcm_mixer[subs].left ||
ucontrol->value.integer.value[1] != chip->pcm_mixer[subs].right) {
chip->pcm_mixer[subs].left = ucontrol->value.integer.value[0];
chip->pcm_mixer[subs].right = ucontrol->value.integer.value[1];

substream = (snd_pcm_substream_t *)kcontrol->private_value;
spin_lock_irqsave(&chip->voice_lock, flags);
if (substream->runtime && substream->runtime->private_data) {
ymfpci_pcm_t *ypcm = substream->runtime->private_data;
ypcm->update_pcm_vol = 2;
}
spin_unlock_irqrestore(&chip->voice_lock, flags);
return 1;
}
return 0;
}

static snd_kcontrol_new_t snd_ymfpci_pcm_volume __devinitdata = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "PCM Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.info = snd_ymfpci_pcm_vol_info,
.get = snd_ymfpci_pcm_vol_get,
.put = snd_ymfpci_pcm_vol_put,
};


/*
* Mixer routines
Expand All @@ -1686,6 +1756,7 @@ int __devinit snd_ymfpci_mixer(ymfpci_t *chip, int rear_switch)
{
ac97_template_t ac97;
snd_kcontrol_t *kctl;
snd_pcm_substream_t *substream;
unsigned int idx;
int err;
static ac97_bus_ops_t ops = {
Expand Down Expand Up @@ -1739,6 +1810,23 @@ int __devinit snd_ymfpci_mixer(ymfpci_t *chip, int rear_switch)
return err;
}

/* per-voice volume */
substream = chip->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
for (idx = 0; idx < 32; ++idx) {
kctl = snd_ctl_new1(&snd_ymfpci_pcm_volume, chip);
if (!kctl)
return -ENOMEM;
kctl->id.device = chip->pcm->device;
kctl->id.subdevice = idx;
kctl->private_value = (unsigned long)substream;
if ((err = snd_ctl_add(chip->card, kctl)) < 0)
return err;
chip->pcm_mixer[idx].left = 0x8000;
chip->pcm_mixer[idx].right = 0x8000;
chip->pcm_mixer[idx].ctl = kctl;
substream = substream->next;
}

return 0;
}

Expand Down

0 comments on commit 9bcf655

Please sign in to comment.