From 931a75fa38a0ff17f072477b69e54a71cd275568 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Thu, 6 Sep 2007 19:02:23 -0700 Subject: cx88-alsa: Make volume control stereo From: Trent Piepho Use the balance control to make the mono volume control stereo. Note that full range isn't supported. The balance control attenuates one channel by 0 to -63 dB, and the volume control provides additional attenuation to both channels by another 0 to -63 dB. So the channel with the most attenuation has a range of 0 to -126 dB, while the other channel only has a range of 0 to -63 dB. ALSA volume controls don't appear to support this concept. I just limited the range to 0 to -63 total. Once you get to -63 dB, you're already at silence, so additional attenuation is pretty much pointless anyway. Signed-off-by: Trent Piepho --- linux/drivers/media/video/cx88/cx88-alsa.c | 61 ++++++++++++++++++------------ 1 file changed, 37 insertions(+), 24 deletions(-) (limited to 'linux/drivers/media') diff --git a/linux/drivers/media/video/cx88/cx88-alsa.c b/linux/drivers/media/video/cx88/cx88-alsa.c index 67d17e753..407754561 100644 --- a/linux/drivers/media/video/cx88/cx88-alsa.c +++ b/linux/drivers/media/video/cx88/cx88-alsa.c @@ -597,7 +597,7 @@ static int snd_cx88_capture_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *info) { info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - info->count = 1; + info->count = 2; info->value.integer.min = 0; info->value.integer.max = 0x3f; @@ -610,8 +610,12 @@ static int snd_cx88_capture_volume_get(struct snd_kcontrol *kcontrol, { snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); struct cx88_core *core=chip->core; + int vol = 0x3f - (cx_read(AUD_VOL_CTL) & 0x3f), + bal = cx_read(AUD_BAL_CTL); - value->value.integer.value[0] = 0x3f - (cx_read(AUD_VOL_CTL) & 0x3f); + value->value.integer.value[(bal & 0x40) ? 0 : 1] = vol; + vol -= (bal & 0x3f); + value->value.integer.value[(bal & 0x40) ? 1 : 0] = vol < 0 ? 0 : vol; return 0; } @@ -622,19 +626,31 @@ static int snd_cx88_capture_volume_put(struct snd_kcontrol *kcontrol, { snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); struct cx88_core *core=chip->core; - int v; - u32 old_control; - + int v, b; + int changed = 0; + u32 old; + + b = value->value.integer.value[1] - value->value.integer.value[0]; + if (b < 0) { + v = 0x3f - value->value.integer.value[0]; + b = (-b) | 0x40; + } else { + v = 0x3f - value->value.integer.value[1]; + } /* Do we really know this will always be called with IRQs on? */ spin_lock_irq(&chip->reg_lock); - - old_control = 0x3f - (cx_read(AUD_VOL_CTL) & 0x3f); - v = 0x3f - (value->value.integer.value[0] & 0x3f); - cx_andor(AUD_VOL_CTL, 0x3f, v); - + old = cx_read(AUD_VOL_CTL); + if (v != (old & 0x3f)) { + cx_write(AUD_VOL_CTL, (old & ~0x3f) | v); + changed = 1; + } + if (cx_read(AUD_BAL_CTL) != b) { + cx_write(AUD_BAL_CTL, b); + changed = 1; + } spin_unlock_irq(&chip->reg_lock); - return v != old_control; + return changed; } static struct snd_kcontrol_new snd_cx88_capture_volume = { @@ -798,17 +814,12 @@ static int __devinit cx88_audio_initdev(struct pci_dev *pci, return (err); err = snd_cx88_pcm(chip, 0, "CX88 Digital"); - - if (err < 0) { - snd_card_free(card); - return (err); - } + if (err < 0) + goto error; err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_capture_volume, chip)); - if (err < 0) { - snd_card_free(card); - return (err); - } + if (err < 0) + goto error; strcpy (card->driver, "CX88x"); sprintf(card->shortname, "Conexant CX%x", pci->device); @@ -820,14 +831,16 @@ static int __devinit cx88_audio_initdev(struct pci_dev *pci, card->driver,devno); err = snd_card_register(card); - if (err < 0) { - snd_card_free(card); - return (err); - } + if (err < 0) + goto error; pci_set_drvdata(pci,card); devno++; return 0; + +error: + snd_card_free(card); + return err; } /* * ALSA destructor -- cgit v1.2.3 From 6ec1e8e15e1811f1e4fd551746e374a922f20811 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Thu, 6 Sep 2007 19:02:23 -0700 Subject: cx88-alsa: Add mute controls, change control names From: Trent Piepho Add two mute controls. One mutes everything, the other just mutes the analog pass-through output. Rename the existing volume control. The controls are now: Playback Volume Playback Switch Capture Switch These names might seem odd, but I believe they are more correct. The previous "Capture Volume" control didn't actually effect the volume of the captured audio. Instead it controls the volume of the analog pass-thought output. It appears that pass-through controls like this are usually considered to be in the playback direction, not capture. For example, "CAPTURE feedback Playback Volume" is the name used for a control that appears to have the same effect in the ca0106 driver. We only have one volume control, so we can omit the "CAPTURE feedback" part. If someone where to add PCM playback support to the driver, then this would be the volume control. Signed-off-by: Trent Piepho --- linux/drivers/media/video/cx88/cx88-alsa.c | 91 +++++++++++++++++++++++++----- 1 file changed, 78 insertions(+), 13 deletions(-) (limited to 'linux/drivers/media') diff --git a/linux/drivers/media/video/cx88/cx88-alsa.c b/linux/drivers/media/video/cx88/cx88-alsa.c index 407754561..5e01a29e5 100644 --- a/linux/drivers/media/video/cx88/cx88-alsa.c +++ b/linux/drivers/media/video/cx88/cx88-alsa.c @@ -106,6 +106,18 @@ typedef struct cx88_audio_dev snd_cx88_card_t; #define chip_t snd_cx88_card_t #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) +static int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} +#endif + /**************************************************************************** Module global static vars ****************************************************************************/ @@ -593,8 +605,8 @@ static int __devinit snd_cx88_pcm(snd_cx88_card_t *chip, int device, char *name) /**************************************************************************** CONTROL INTERFACE ****************************************************************************/ -static int snd_cx88_capture_volume_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *info) +static int snd_cx88_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *info) { info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; info->count = 2; @@ -604,9 +616,8 @@ static int snd_cx88_capture_volume_info(struct snd_kcontrol *kcontrol, return 0; } -/* OK - TODO: test it */ -static int snd_cx88_capture_volume_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *value) +static int snd_cx88_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) { snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); struct cx88_core *core=chip->core; @@ -621,8 +632,8 @@ static int snd_cx88_capture_volume_get(struct snd_kcontrol *kcontrol, } /* OK - TODO: test it */ -static int snd_cx88_capture_volume_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *value) +static int snd_cx88_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) { snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); struct cx88_core *core=chip->core; @@ -653,14 +664,62 @@ static int snd_cx88_capture_volume_put(struct snd_kcontrol *kcontrol, return changed; } -static struct snd_kcontrol_new snd_cx88_capture_volume = { +static struct snd_kcontrol_new snd_cx88_volume = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Volume", - .info = snd_cx88_capture_volume_info, - .get = snd_cx88_capture_volume_get, - .put = snd_cx88_capture_volume_put, + .name = "Playback Volume", + .info = snd_cx88_volume_info, + .get = snd_cx88_volume_get, + .put = snd_cx88_volume_put, }; +static int snd_cx88_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +{ + snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); + struct cx88_core *core = chip->core; + u32 bit = kcontrol->private_value; + + value->value.integer.value[0] = !(cx_read(AUD_VOL_CTL) & bit); + return 0; +} + +static int snd_cx88_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +{ + snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); + struct cx88_core *core = chip->core; + u32 bit = kcontrol->private_value; + int ret = 0; + u32 vol; + + spin_lock_irq(&chip->reg_lock); + vol = cx_read(AUD_VOL_CTL); + if (value->value.integer.value[0] != !(vol & bit)) { + vol ^= bit; + cx_write(AUD_VOL_CTL, vol); + ret = 1; + } + spin_unlock_irq(&chip->reg_lock); + return ret; +} + +static struct snd_kcontrol_new snd_cx88_dac_switch = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Playback Switch", + .info = snd_ctl_boolean_mono_info, + .get = snd_cx88_switch_get, + .put = snd_cx88_switch_put, + .private_value = (1<<8), +}; + +static struct snd_kcontrol_new snd_cx88_source_switch = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Switch", + .info = snd_ctl_boolean_mono_info, + .get = snd_cx88_switch_get, + .put = snd_cx88_switch_put, + .private_value = (1<<6), +}; /**************************************************************************** Basic Flow for Sound Devices @@ -817,7 +876,13 @@ static int __devinit cx88_audio_initdev(struct pci_dev *pci, if (err < 0) goto error; - err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_capture_volume, chip)); + err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_volume, chip)); + if (err < 0) + goto error; + err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_dac_switch, chip)); + if (err < 0) + goto error; + err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_source_switch, chip)); if (err < 0) goto error; -- cgit v1.2.3 From b5642bbab87ea3481e8e93251a76f19cb3121ad2 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Thu, 6 Sep 2007 19:02:24 -0700 Subject: cx88-alsa: Remove some unused fields in card state struct From: Trent Piepho Not sure why they are there, but they don't do anything now. Signed-off-by: Trent Piepho --- linux/drivers/media/video/cx88/cx88-alsa.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'linux/drivers/media') diff --git a/linux/drivers/media/video/cx88/cx88-alsa.c b/linux/drivers/media/video/cx88/cx88-alsa.c index 5e01a29e5..bb6196ea7 100644 --- a/linux/drivers/media/video/cx88/cx88-alsa.c +++ b/linux/drivers/media/video/cx88/cx88-alsa.c @@ -65,13 +65,10 @@ Data type declarations - Can be moded to a header file later ****************************************************************************/ -/* These can be replaced after done */ -#define MIXER_ADDR_LAST MAX_CX88_INPUT - struct cx88_audio_dev { struct cx88_core *core; struct cx88_dmaqueue q; -#if 1 +#if 0 u64 starttime; #endif @@ -92,9 +89,6 @@ struct cx88_audio_dev { struct videobuf_dmabuf dma_risc; - int mixer_volume[MIXER_ADDR_LAST+1][2]; - int capture_source[MIXER_ADDR_LAST+1][2]; - struct cx88_buffer *buf; struct snd_pcm_substream *substream; -- cgit v1.2.3 From b192ede34d629b1a1ac13ea1931e2d859c747f45 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Thu, 6 Sep 2007 19:02:25 -0700 Subject: cx88-alsa: Add TLV support From: Trent Piepho Lets mixer apps display a dB range for the volume control. Signed-off-by: Trent Piepho --- linux/drivers/media/video/cx88/cx88-alsa.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'linux/drivers/media') diff --git a/linux/drivers/media/video/cx88/cx88-alsa.c b/linux/drivers/media/video/cx88/cx88-alsa.c index bb6196ea7..dab6fbc06 100644 --- a/linux/drivers/media/video/cx88/cx88-alsa.c +++ b/linux/drivers/media/video/cx88/cx88-alsa.c @@ -39,6 +39,7 @@ #include #include #include +#include #include "compat.h" #include "cx88.h" @@ -658,12 +659,17 @@ static int snd_cx88_volume_put(struct snd_kcontrol *kcontrol, return changed; } +static const DECLARE_TLV_DB_SCALE(snd_cx88_db_scale, -6300, 100, 0); + static struct snd_kcontrol_new snd_cx88_volume = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, .name = "Playback Volume", .info = snd_cx88_volume_info, .get = snd_cx88_volume_get, .put = snd_cx88_volume_put, + .tlv.p = snd_cx88_db_scale, }; static int snd_cx88_switch_get(struct snd_kcontrol *kcontrol, -- cgit v1.2.3