aboutsummaryrefslogtreecommitdiff
path: root/sound/i2c/other
diff options
context:
space:
mode:
Diffstat (limited to 'sound/i2c/other')
-rw-r--r--sound/i2c/other/Makefile9
-rw-r--r--sound/i2c/other/ak4113.c639
-rw-r--r--sound/i2c/other/ak4114.c195
-rw-r--r--sound/i2c/other/ak4117.c25
-rw-r--r--sound/i2c/other/ak4xxx-adda.c324
-rw-r--r--sound/i2c/other/pt2258.c227
-rw-r--r--sound/i2c/other/tea575x-tuner.c233
7 files changed, 1259 insertions, 393 deletions
diff --git a/sound/i2c/other/Makefile b/sound/i2c/other/Makefile
index 2fe023ef00a..5526b03b95a 100644
--- a/sound/i2c/other/Makefile
+++ b/sound/i2c/other/Makefile
@@ -1,16 +1,15 @@
#
# Makefile for ALSA
-# Copyright (c) 2003 by Jaroslav Kysela <perex@suse.cz>
+# Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz>
#
snd-ak4114-objs := ak4114.o
snd-ak4117-objs := ak4117.o
+snd-ak4113-objs := ak4113.o
snd-ak4xxx-adda-objs := ak4xxx-adda.o
-snd-tea575x-tuner-objs := tea575x-tuner.o
+snd-pt2258-objs := pt2258.o
# Module Dependency
obj-$(CONFIG_SND_PDAUDIOCF) += snd-ak4117.o
obj-$(CONFIG_SND_ICE1712) += snd-ak4xxx-adda.o
-obj-$(CONFIG_SND_ICE1724) += snd-ak4xxx-adda.o
-obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o
-obj-$(CONFIG_SND_FM801_TEA575X) += snd-tea575x-tuner.o
+obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o snd-ak4113.o snd-ak4xxx-adda.o snd-pt2258.o
diff --git a/sound/i2c/other/ak4113.c b/sound/i2c/other/ak4113.c
new file mode 100644
index 00000000000..1a3a6fa2715
--- /dev/null
+++ b/sound/i2c/other/ak4113.c
@@ -0,0 +1,639 @@
+/*
+ * Routines for control of the AK4113 via I2C/4-wire serial interface
+ * IEC958 (S/PDIF) receiver by Asahi Kasei
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ * Copyright (c) by Pavel Hofman <pavel.hofman@ivitera.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/ak4113.h>
+#include <sound/asoundef.h>
+#include <sound/info.h>
+
+MODULE_AUTHOR("Pavel Hofman <pavel.hofman@ivitera.com>");
+MODULE_DESCRIPTION("AK4113 IEC958 (S/PDIF) receiver by Asahi Kasei");
+MODULE_LICENSE("GPL");
+
+#define AK4113_ADDR 0x00 /* fixed address */
+
+static void ak4113_stats(struct work_struct *work);
+static void ak4113_init_regs(struct ak4113 *chip);
+
+
+static void reg_write(struct ak4113 *ak4113, unsigned char reg,
+ unsigned char val)
+{
+ ak4113->write(ak4113->private_data, reg, val);
+ if (reg < sizeof(ak4113->regmap))
+ ak4113->regmap[reg] = val;
+}
+
+static inline unsigned char reg_read(struct ak4113 *ak4113, unsigned char reg)
+{
+ return ak4113->read(ak4113->private_data, reg);
+}
+
+static void snd_ak4113_free(struct ak4113 *chip)
+{
+ chip->init = 1; /* don't schedule new work */
+ mb();
+ cancel_delayed_work_sync(&chip->work);
+ kfree(chip);
+}
+
+static int snd_ak4113_dev_free(struct snd_device *device)
+{
+ struct ak4113 *chip = device->device_data;
+ snd_ak4113_free(chip);
+ return 0;
+}
+
+int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read,
+ ak4113_write_t *write, const unsigned char *pgm,
+ void *private_data, struct ak4113 **r_ak4113)
+{
+ struct ak4113 *chip;
+ int err = 0;
+ unsigned char reg;
+ static struct snd_device_ops ops = {
+ .dev_free = snd_ak4113_dev_free,
+ };
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL)
+ return -ENOMEM;
+ spin_lock_init(&chip->lock);
+ chip->card = card;
+ chip->read = read;
+ chip->write = write;
+ chip->private_data = private_data;
+ INIT_DELAYED_WORK(&chip->work, ak4113_stats);
+
+ for (reg = 0; reg < AK4113_WRITABLE_REGS ; reg++)
+ chip->regmap[reg] = pgm[reg];
+ ak4113_init_regs(chip);
+
+ chip->rcs0 = reg_read(chip, AK4113_REG_RCS0) & ~(AK4113_QINT |
+ AK4113_CINT | AK4113_STC);
+ chip->rcs1 = reg_read(chip, AK4113_REG_RCS1);
+ chip->rcs2 = reg_read(chip, AK4113_REG_RCS2);
+ err = snd_device_new(card, SNDRV_DEV_CODEC, chip, &ops);
+ if (err < 0)
+ goto __fail;
+
+ if (r_ak4113)
+ *r_ak4113 = chip;
+ return 0;
+
+__fail:
+ snd_ak4113_free(chip);
+ return err < 0 ? err : -EIO;
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_create);
+
+void snd_ak4113_reg_write(struct ak4113 *chip, unsigned char reg,
+ unsigned char mask, unsigned char val)
+{
+ if (reg >= AK4113_WRITABLE_REGS)
+ return;
+ reg_write(chip, reg, (chip->regmap[reg] & ~mask) | val);
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_reg_write);
+
+static void ak4113_init_regs(struct ak4113 *chip)
+{
+ unsigned char old = chip->regmap[AK4113_REG_PWRDN], reg;
+
+ /* bring the chip to reset state and powerdown state */
+ reg_write(chip, AK4113_REG_PWRDN, old & ~(AK4113_RST|AK4113_PWN));
+ udelay(200);
+ /* release reset, but leave powerdown */
+ reg_write(chip, AK4113_REG_PWRDN, (old | AK4113_RST) & ~AK4113_PWN);
+ udelay(200);
+ for (reg = 1; reg < AK4113_WRITABLE_REGS; reg++)
+ reg_write(chip, reg, chip->regmap[reg]);
+ /* release powerdown, everything is initialized now */
+ reg_write(chip, AK4113_REG_PWRDN, old | AK4113_RST | AK4113_PWN);
+}
+
+void snd_ak4113_reinit(struct ak4113 *chip)
+{
+ chip->init = 1;
+ mb();
+ flush_delayed_work(&chip->work);
+ ak4113_init_regs(chip);
+ /* bring up statistics / event queing */
+ chip->init = 0;
+ if (chip->kctls[0])
+ schedule_delayed_work(&chip->work, HZ / 10);
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_reinit);
+
+static unsigned int external_rate(unsigned char rcs1)
+{
+ switch (rcs1 & (AK4113_FS0|AK4113_FS1|AK4113_FS2|AK4113_FS3)) {
+ case AK4113_FS_8000HZ:
+ return 8000;
+ case AK4113_FS_11025HZ:
+ return 11025;
+ case AK4113_FS_16000HZ:
+ return 16000;
+ case AK4113_FS_22050HZ:
+ return 22050;
+ case AK4113_FS_24000HZ:
+ return 24000;
+ case AK4113_FS_32000HZ:
+ return 32000;
+ case AK4113_FS_44100HZ:
+ return 44100;
+ case AK4113_FS_48000HZ:
+ return 48000;
+ case AK4113_FS_64000HZ:
+ return 64000;
+ case AK4113_FS_88200HZ:
+ return 88200;
+ case AK4113_FS_96000HZ:
+ return 96000;
+ case AK4113_FS_176400HZ:
+ return 176400;
+ case AK4113_FS_192000HZ:
+ return 192000;
+ default:
+ return 0;
+ }
+}
+
+static int snd_ak4113_in_error_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = LONG_MAX;
+ return 0;
+}
+
+static int snd_ak4113_in_error_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+ long *ptr;
+
+ spin_lock_irq(&chip->lock);
+ ptr = (long *)(((char *)chip) + kcontrol->private_value);
+ ucontrol->value.integer.value[0] = *ptr;
+ *ptr = 0;
+ spin_unlock_irq(&chip->lock);
+ return 0;
+}
+
+#define snd_ak4113_in_bit_info snd_ctl_boolean_mono_info
+
+static int snd_ak4113_in_bit_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+ unsigned char reg = kcontrol->private_value & 0xff;
+ unsigned char bit = (kcontrol->private_value >> 8) & 0xff;
+ unsigned char inv = (kcontrol->private_value >> 31) & 1;
+
+ ucontrol->value.integer.value[0] =
+ ((reg_read(chip, reg) & (1 << bit)) ? 1 : 0) ^ inv;
+ return 0;
+}
+
+static int snd_ak4113_rx_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 5;
+ return 0;
+}
+
+static int snd_ak4113_rx_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] =
+ (AK4113_IPS(chip->regmap[AK4113_REG_IO1]));
+ return 0;
+}
+
+static int snd_ak4113_rx_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+ int change;
+ u8 old_val;
+
+ spin_lock_irq(&chip->lock);
+ old_val = chip->regmap[AK4113_REG_IO1];
+ change = ucontrol->value.integer.value[0] != AK4113_IPS(old_val);
+ if (change)
+ reg_write(chip, AK4113_REG_IO1,
+ (old_val & (~AK4113_IPS(0xff))) |
+ (AK4113_IPS(ucontrol->value.integer.value[0])));
+ spin_unlock_irq(&chip->lock);
+ return change;
+}
+
+static int snd_ak4113_rate_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 192000;
+ return 0;
+}
+
+static int snd_ak4113_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = external_rate(reg_read(chip,
+ AK4113_REG_RCS1));
+ return 0;
+}
+
+static int snd_ak4113_spdif_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_ak4113_spdif_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+ unsigned i;
+
+ for (i = 0; i < AK4113_REG_RXCSB_SIZE; i++)
+ ucontrol->value.iec958.status[i] = reg_read(chip,
+ AK4113_REG_RXCSB0 + i);
+ return 0;
+}
+
+static int snd_ak4113_spdif_mask_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+ return 0;
+}
+
+static int snd_ak4113_spdif_mask_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ memset(ucontrol->value.iec958.status, 0xff, AK4113_REG_RXCSB_SIZE);
+ return 0;
+}
+
+static int snd_ak4113_spdif_pinfo(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 0xffff;
+ uinfo->count = 4;
+ return 0;
+}
+
+static int snd_ak4113_spdif_pget(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+ unsigned short tmp;
+
+ ucontrol->value.integer.value[0] = 0xf8f2;
+ ucontrol->value.integer.value[1] = 0x4e1f;
+ tmp = reg_read(chip, AK4113_REG_Pc0) |
+ (reg_read(chip, AK4113_REG_Pc1) << 8);
+ ucontrol->value.integer.value[2] = tmp;
+ tmp = reg_read(chip, AK4113_REG_Pd0) |
+ (reg_read(chip, AK4113_REG_Pd1) << 8);
+ ucontrol->value.integer.value[3] = tmp;
+ return 0;
+}
+
+static int snd_ak4113_spdif_qinfo(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = AK4113_REG_QSUB_SIZE;
+ return 0;
+}
+
+static int snd_ak4113_spdif_qget(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+ unsigned i;
+
+ for (i = 0; i < AK4113_REG_QSUB_SIZE; i++)
+ ucontrol->value.bytes.data[i] = reg_read(chip,
+ AK4113_REG_QSUB_ADDR + i);
+ return 0;
+}
+
+/* Don't forget to change AK4113_CONTROLS define!!! */
+static struct snd_kcontrol_new snd_ak4113_iec958_controls[] = {
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Parity Errors",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_in_error_info,
+ .get = snd_ak4113_in_error_get,
+ .private_value = offsetof(struct ak4113, parity_errors),
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 V-Bit Errors",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_in_error_info,
+ .get = snd_ak4113_in_error_get,
+ .private_value = offsetof(struct ak4113, v_bit_errors),
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 C-CRC Errors",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_in_error_info,
+ .get = snd_ak4113_in_error_get,
+ .private_value = offsetof(struct ak4113, ccrc_errors),
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Q-CRC Errors",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_in_error_info,
+ .get = snd_ak4113_in_error_get,
+ .private_value = offsetof(struct ak4113, qcrc_errors),
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 External Rate",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_rate_info,
+ .get = snd_ak4113_rate_get,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK),
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .info = snd_ak4113_spdif_mask_info,
+ .get = snd_ak4113_spdif_mask_get,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_spdif_info,
+ .get = snd_ak4113_spdif_get,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Preamble Capture Default",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_spdif_pinfo,
+ .get = snd_ak4113_spdif_pget,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Q-subcode Capture Default",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_spdif_qinfo,
+ .get = snd_ak4113_spdif_qget,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Audio",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_in_bit_info,
+ .get = snd_ak4113_in_bit_get,
+ .private_value = (1<<31) | (1<<8) | AK4113_REG_RCS0,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Non-PCM Bitstream",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_in_bit_info,
+ .get = snd_ak4113_in_bit_get,
+ .private_value = (0<<8) | AK4113_REG_RCS1,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 DTS Bitstream",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4113_in_bit_info,
+ .get = snd_ak4113_in_bit_get,
+ .private_value = (1<<8) | AK4113_REG_RCS1,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "AK4113 Input Select",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_WRITE,
+ .info = snd_ak4113_rx_info,
+ .get = snd_ak4113_rx_get,
+ .put = snd_ak4113_rx_put,
+}
+};
+
+static void snd_ak4113_proc_regs_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct ak4113 *ak4113 = entry->private_data;
+ int reg, val;
+ /* all ak4113 registers 0x00 - 0x1c */
+ for (reg = 0; reg < 0x1d; reg++) {
+ val = reg_read(ak4113, reg);
+ snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val);
+ }
+}
+
+static void snd_ak4113_proc_init(struct ak4113 *ak4113)
+{
+ struct snd_info_entry *entry;
+ if (!snd_card_proc_new(ak4113->card, "ak4113", &entry))
+ snd_info_set_text_ops(entry, ak4113, snd_ak4113_proc_regs_read);
+}
+
+int snd_ak4113_build(struct ak4113 *ak4113,
+ struct snd_pcm_substream *cap_substream)
+{
+ struct snd_kcontrol *kctl;
+ unsigned int idx;
+ int err;
+
+ if (snd_BUG_ON(!cap_substream))
+ return -EINVAL;
+ ak4113->substream = cap_substream;
+ for (idx = 0; idx < AK4113_CONTROLS; idx++) {
+ kctl = snd_ctl_new1(&snd_ak4113_iec958_controls[idx], ak4113);
+ if (kctl == NULL)
+ return -ENOMEM;
+ kctl->id.device = cap_substream->pcm->device;
+ kctl->id.subdevice = cap_substream->number;
+ err = snd_ctl_add(ak4113->card, kctl);
+ if (err < 0)
+ return err;
+ ak4113->kctls[idx] = kctl;
+ }
+ snd_ak4113_proc_init(ak4113);
+ /* trigger workq */
+ schedule_delayed_work(&ak4113->work, HZ / 10);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_build);
+
+int snd_ak4113_external_rate(struct ak4113 *ak4113)
+{
+ unsigned char rcs1;
+
+ rcs1 = reg_read(ak4113, AK4113_REG_RCS1);
+ return external_rate(rcs1);
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_external_rate);
+
+int snd_ak4113_check_rate_and_errors(struct ak4113 *ak4113, unsigned int flags)
+{
+ struct snd_pcm_runtime *runtime =
+ ak4113->substream ? ak4113->substream->runtime : NULL;
+ unsigned long _flags;
+ int res = 0;
+ unsigned char rcs0, rcs1, rcs2;
+ unsigned char c0, c1;
+
+ rcs1 = reg_read(ak4113, AK4113_REG_RCS1);
+ if (flags & AK4113_CHECK_NO_STAT)
+ goto __rate;
+ rcs0 = reg_read(ak4113, AK4113_REG_RCS0);
+ rcs2 = reg_read(ak4113, AK4113_REG_RCS2);
+ spin_lock_irqsave(&ak4113->lock, _flags);
+ if (rcs0 & AK4113_PAR)
+ ak4113->parity_errors++;
+ if (rcs0 & AK4113_V)
+ ak4113->v_bit_errors++;
+ if (rcs2 & AK4113_CCRC)
+ ak4113->ccrc_errors++;
+ if (rcs2 & AK4113_QCRC)
+ ak4113->qcrc_errors++;
+ c0 = (ak4113->rcs0 & (AK4113_QINT | AK4113_CINT | AK4113_STC |
+ AK4113_AUDION | AK4113_AUTO | AK4113_UNLCK)) ^
+ (rcs0 & (AK4113_QINT | AK4113_CINT | AK4113_STC |
+ AK4113_AUDION | AK4113_AUTO | AK4113_UNLCK));
+ c1 = (ak4113->rcs1 & (AK4113_DTSCD | AK4113_NPCM | AK4113_PEM |
+ AK4113_DAT | 0xf0)) ^
+ (rcs1 & (AK4113_DTSCD | AK4113_NPCM | AK4113_PEM |
+ AK4113_DAT | 0xf0));
+ ak4113->rcs0 = rcs0 & ~(AK4113_QINT | AK4113_CINT | AK4113_STC);
+ ak4113->rcs1 = rcs1;
+ ak4113->rcs2 = rcs2;
+ spin_unlock_irqrestore(&ak4113->lock, _flags);
+
+ if (rcs0 & AK4113_PAR)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[0]->id);
+ if (rcs0 & AK4113_V)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[1]->id);
+ if (rcs2 & AK4113_CCRC)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[2]->id);
+ if (rcs2 & AK4113_QCRC)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[3]->id);
+
+ /* rate change */
+ if (c1 & 0xf0)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[4]->id);
+
+ if ((c1 & AK4113_PEM) | (c0 & AK4113_CINT))
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[6]->id);
+ if (c0 & AK4113_QINT)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[8]->id);
+
+ if (c0 & AK4113_AUDION)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[9]->id);
+ if (c1 & AK4113_NPCM)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[10]->id);
+ if (c1 & AK4113_DTSCD)
+ snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4113->kctls[11]->id);
+
+ if (ak4113->change_callback && (c0 | c1) != 0)
+ ak4113->change_callback(ak4113, c0, c1);
+
+__rate:
+ /* compare rate */
+ res = external_rate(rcs1);
+ if (!(flags & AK4113_CHECK_NO_RATE) && runtime &&
+ (runtime->rate != res)) {
+ snd_pcm_stream_lock_irqsave(ak4113->substream, _flags);
+ if (snd_pcm_running(ak4113->substream)) {
+ /*printk(KERN_DEBUG "rate changed (%i <- %i)\n",
+ * runtime->rate, res); */
+ snd_pcm_stop(ak4113->substream,
+ SNDRV_PCM_STATE_DRAINING);
+ wake_up(&runtime->sleep);
+ res = 1;
+ }
+ snd_pcm_stream_unlock_irqrestore(ak4113->substream, _flags);
+ }
+ return res;
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_check_rate_and_errors);
+
+static void ak4113_stats(struct work_struct *work)
+{
+ struct ak4113 *chip = container_of(work, struct ak4113, work.work);
+
+ if (!chip->init)
+ snd_ak4113_check_rate_and_errors(chip, chip->check_flags);
+
+ schedule_delayed_work(&chip->work, HZ / 10);
+}
diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c
index 12ffffc9e81..c7f56339415 100644
--- a/sound/i2c/other/ak4114.c
+++ b/sound/i2c/other/ak4114.c
@@ -1,7 +1,7 @@
/*
* Routines for control of the AK4114 via I2C and 4-wire serial interface
* IEC958 (S/PDIF) receiver by Asahi Kasei
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -20,30 +20,32 @@
*
*/
-#include <sound/driver.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
#include <sound/ak4114.h>
#include <sound/asoundef.h>
+#include <sound/info.h>
-MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("AK4114 IEC958 (S/PDIF) receiver by Asahi Kasei");
MODULE_LICENSE("GPL");
#define AK4114_ADDR 0x00 /* fixed address */
-static void ak4114_stats(void *);
+static void ak4114_stats(struct work_struct *work);
+static void ak4114_init_regs(struct ak4114 *chip);
static void reg_write(struct ak4114 *ak4114, unsigned char reg, unsigned char val)
{
ak4114->write(ak4114->private_data, reg, val);
if (reg <= AK4114_REG_INT1_MASK)
ak4114->regmap[reg] = val;
- else if (reg >= AK4114_REG_RXCSB0 && reg <= AK4114_REG_TXCSB4)
- ak4114->txcsb[reg-AK4114_REG_RXCSB0] = val;
+ else if (reg >= AK4114_REG_TXCSB0 && reg <= AK4114_REG_TXCSB4)
+ ak4114->txcsb[reg-AK4114_REG_TXCSB0] = val;
}
static inline unsigned char reg_read(struct ak4114 *ak4114, unsigned char reg)
@@ -58,7 +60,7 @@ static void reg_dump(struct ak4114 *ak4114)
printk(KERN_DEBUG "AK4114 REG DUMP:\n");
for (i = 0; i < 0x20; i++)
- printk(KERN_DEBUG "reg[%02x] = %02x (%02x)\n", i, reg_read(ak4114, i), i < sizeof(ak4114->regmap) ? ak4114->regmap[i] : 0);
+ printk(KERN_DEBUG "reg[%02x] = %02x (%02x)\n", i, reg_read(ak4114, i), i < ARRAY_SIZE(ak4114->regmap) ? ak4114->regmap[i] : 0);
}
#endif
@@ -66,10 +68,7 @@ static void snd_ak4114_free(struct ak4114 *chip)
{
chip->init = 1; /* don't schedule new work */
mb();
- if (chip->workqueue != NULL) {
- flush_workqueue(chip->workqueue);
- destroy_workqueue(chip->workqueue);
- }
+ cancel_delayed_work_sync(&chip->work);
kfree(chip);
}
@@ -82,7 +81,7 @@ static int snd_ak4114_dev_free(struct snd_device *device)
int snd_ak4114_create(struct snd_card *card,
ak4114_read_t *read, ak4114_write_t *write,
- unsigned char pgm[7], unsigned char txcsb[5],
+ const unsigned char pgm[6], const unsigned char txcsb[5],
void *private_data, struct ak4114 **r_ak4114)
{
struct ak4114 *chip;
@@ -100,24 +99,19 @@ int snd_ak4114_create(struct snd_card *card,
chip->read = read;
chip->write = write;
chip->private_data = private_data;
+ INIT_DELAYED_WORK(&chip->work, ak4114_stats);
- for (reg = 0; reg < 7; reg++)
+ for (reg = 0; reg < 6; reg++)
chip->regmap[reg] = pgm[reg];
for (reg = 0; reg < 5; reg++)
chip->txcsb[reg] = txcsb[reg];
- chip->workqueue = create_workqueue("snd-ak4114");
- if (chip->workqueue == NULL) {
- kfree(chip);
- return -ENOMEM;
- }
-
- snd_ak4114_reinit(chip);
+ ak4114_init_regs(chip);
chip->rcs0 = reg_read(chip, AK4114_REG_RCS0) & ~(AK4114_QINT | AK4114_CINT);
chip->rcs1 = reg_read(chip, AK4114_REG_RCS1);
- if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0)
+ if ((err = snd_device_new(card, SNDRV_DEV_CODEC, chip, &ops)) < 0)
goto __fail;
if (r_ak4114)
@@ -134,32 +128,38 @@ void snd_ak4114_reg_write(struct ak4114 *chip, unsigned char reg, unsigned char
if (reg <= AK4114_REG_INT1_MASK)
reg_write(chip, reg, (chip->regmap[reg] & ~mask) | val);
else if (reg >= AK4114_REG_TXCSB0 && reg <= AK4114_REG_TXCSB4)
- reg_write(chip, reg, (chip->txcsb[reg] & ~mask) | val);
+ reg_write(chip, reg,
+ (chip->txcsb[reg-AK4114_REG_TXCSB0] & ~mask) | val);
}
-void snd_ak4114_reinit(struct ak4114 *chip)
+static void ak4114_init_regs(struct ak4114 *chip)
{
unsigned char old = chip->regmap[AK4114_REG_PWRDN], reg;
- chip->init = 1;
- mb();
- flush_workqueue(chip->workqueue);
/* bring the chip to reset state and powerdown state */
reg_write(chip, AK4114_REG_PWRDN, old & ~(AK4114_RST|AK4114_PWN));
udelay(200);
/* release reset, but leave powerdown */
reg_write(chip, AK4114_REG_PWRDN, (old | AK4114_RST) & ~AK4114_PWN);
udelay(200);
- for (reg = 1; reg < 7; reg++)
+ for (reg = 1; reg < 6; reg++)
reg_write(chip, reg, chip->regmap[reg]);
for (reg = 0; reg < 5; reg++)
reg_write(chip, reg + AK4114_REG_TXCSB0, chip->txcsb[reg]);
/* release powerdown, everything is initialized now */
reg_write(chip, AK4114_REG_PWRDN, old | AK4114_RST | AK4114_PWN);
+}
+
+void snd_ak4114_reinit(struct ak4114 *chip)
+{
+ chip->init = 1;
+ mb();
+ flush_delayed_work(&chip->work);
+ ak4114_init_regs(chip);
/* bring up statistics / event queing */
chip->init = 0;
- INIT_WORK(&chip->work, ak4114_stats, chip);
- queue_delayed_work(chip->workqueue, &chip->work, HZ / 10);
+ if (chip->kctls[0])
+ schedule_delayed_work(&chip->work, HZ / 10);
}
static unsigned int external_rate(unsigned char rcs1)
@@ -200,15 +200,7 @@ static int snd_ak4114_in_error_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static int snd_ak4114_in_bit_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;
-}
+#define snd_ak4114_in_bit_info snd_ctl_boolean_mono_info
static int snd_ak4114_in_bit_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -409,7 +401,7 @@ static struct snd_kcontrol_new snd_ak4114_iec958_controls[] = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = "IEC958 Preample Capture Default",
+ .name = "IEC958 Preamble Capture Default",
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4114_spdif_pinfo,
.get = snd_ak4114_spdif_pget,
@@ -435,7 +427,7 @@ static struct snd_kcontrol_new snd_ak4114_iec958_controls[] = {
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4114_in_bit_info,
.get = snd_ak4114_in_bit_get,
- .private_value = (6<<8) | AK4114_REG_RCS1,
+ .private_value = (6<<8) | AK4114_REG_RCS0,
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -443,10 +435,38 @@ static struct snd_kcontrol_new snd_ak4114_iec958_controls[] = {
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4114_in_bit_info,
.get = snd_ak4114_in_bit_get,
- .private_value = (3<<8) | AK4114_REG_RCS1,
+ .private_value = (3<<8) | AK4114_REG_RCS0,
+},
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 PPL Lock Status",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = snd_ak4114_in_bit_info,
+ .get = snd_ak4114_in_bit_get,
+ .private_value = (1<<31) | (4<<8) | AK4114_REG_RCS0,
}
};
+
+static void snd_ak4114_proc_regs_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct ak4114 *ak4114 = entry->private_data;
+ int reg, val;
+ /* all ak4114 registers 0x00 - 0x1f */
+ for (reg = 0; reg < 0x20; reg++) {
+ val = reg_read(ak4114, reg);
+ snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val);
+ }
+}
+
+static void snd_ak4114_proc_init(struct ak4114 *ak4114)
+{
+ struct snd_info_entry *entry;
+ if (!snd_card_proc_new(ak4114->card, "ak4114", &entry))
+ snd_info_set_text_ops(entry, ak4114, snd_ak4114_proc_regs_read);
+}
+
int snd_ak4114_build(struct ak4114 *ak4114,
struct snd_pcm_substream *ply_substream,
struct snd_pcm_substream *cap_substream)
@@ -455,14 +475,15 @@ int snd_ak4114_build(struct ak4114 *ak4114,
unsigned int idx;
int err;
- snd_assert(cap_substream, return -EINVAL);
+ if (snd_BUG_ON(!cap_substream))
+ return -EINVAL;
ak4114->playback_substream = ply_substream;
ak4114->capture_substream = cap_substream;
for (idx = 0; idx < AK4114_CONTROLS; idx++) {
kctl = snd_ctl_new1(&snd_ak4114_iec958_controls[idx], ak4114);
if (kctl == NULL)
return -ENOMEM;
- if (!strstr(kctl->id.name, "Playback")) {
+ if (strstr(kctl->id.name, "Playback")) {
if (ply_substream == NULL) {
snd_ctl_free_one(kctl);
ak4114->kctls[idx] = NULL;
@@ -479,9 +500,59 @@ int snd_ak4114_build(struct ak4114 *ak4114,
return err;
ak4114->kctls[idx] = kctl;
}
+ snd_ak4114_proc_init(ak4114);
+ /* trigger workq */
+ schedule_delayed_work(&ak4114->work, HZ / 10);
return 0;
}
+/* notify kcontrols if any parameters are changed */
+static void ak4114_notify(struct ak4114 *ak4114,
+ unsigned char rcs0, unsigned char rcs1,
+ unsigned char c0, unsigned char c1)
+{
+ if (!ak4114->kctls[0])
+ return;
+
+ if (rcs0 & AK4114_PAR)
+ snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4114->kctls[0]->id);
+ if (rcs0 & AK4114_V)
+ snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4114->kctls[1]->id);
+ if (rcs1 & AK4114_CCRC)
+ snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4114->kctls[2]->id);
+ if (rcs1 & AK4114_QCRC)
+ snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4114->kctls[3]->id);
+
+ /* rate change */
+ if (c1 & 0xf0)
+ snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4114->kctls[4]->id);
+
+ if ((c0 & AK4114_PEM) | (c0 & AK4114_CINT))
+ snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4114->kctls[9]->id);
+ if (c0 & AK4114_QINT)
+ snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4114->kctls[10]->id);
+
+ if (c0 & AK4114_AUDION)
+ snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4114->kctls[11]->id);
+ if (c0 & AK4114_AUTO)
+ snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4114->kctls[12]->id);
+ if (c0 & AK4114_DTSCD)
+ snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4114->kctls[13]->id);
+ if (c0 & AK4114_UNLCK)
+ snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &ak4114->kctls[14]->id);
+}
+
int snd_ak4114_external_rate(struct ak4114 *ak4114)
{
unsigned char rcs1;
@@ -518,31 +589,7 @@ int snd_ak4114_check_rate_and_errors(struct ak4114 *ak4114, unsigned int flags)
ak4114->rcs1 = rcs1;
spin_unlock_irqrestore(&ak4114->lock, _flags);
- if (rcs0 & AK4114_PAR)
- snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[0]->id);
- if (rcs0 & AK4114_V)
- snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[1]->id);
- if (rcs1 & AK4114_CCRC)
- snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[2]->id);
- if (rcs1 & AK4114_QCRC)
- snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[3]->id);
-
- /* rate change */
- if (c1 & 0xf0)
- snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[4]->id);
-
- if ((c0 & AK4114_PEM) | (c0 & AK4114_CINT))
- snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[9]->id);
- if (c0 & AK4114_QINT)
- snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[10]->id);
-
- if (c0 & AK4114_AUDION)
- snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[11]->id);
- if (c0 & AK4114_AUTO)
- snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[12]->id);
- if (c0 & AK4114_DTSCD)
- snd_ctl_notify(ak4114->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4114->kctls[13]->id);
-
+ ak4114_notify(ak4114, rcs0, rcs1, c0, c1);
if (ak4114->change_callback && (c0 | c1) != 0)
ak4114->change_callback(ak4114, c0, c1);
@@ -561,14 +608,14 @@ int snd_ak4114_check_rate_and_errors(struct ak4114 *ak4114, unsigned int flags)
return res;
}
-static void ak4114_stats(void *data)
+static void ak4114_stats(struct work_struct *work)
{
- struct ak4114 *chip = (struct ak4114 *)data;
+ struct ak4114 *chip = container_of(work, struct ak4114, work.work);
- if (chip->init)
- return;
- snd_ak4114_check_rate_and_errors(chip, 0);
- queue_delayed_work(chip->workqueue, &chip->work, HZ / 10);
+ if (!chip->init)
+ snd_ak4114_check_rate_and_errors(chip, chip->check_flags);
+
+ schedule_delayed_work(&chip->work, HZ / 10);
}
EXPORT_SYMBOL(snd_ak4114_create);
diff --git a/sound/i2c/other/ak4117.c b/sound/i2c/other/ak4117.c
index 4e45952dd95..88452e899bd 100644
--- a/sound/i2c/other/ak4117.c
+++ b/sound/i2c/other/ak4117.c
@@ -1,7 +1,7 @@
/*
* Routines for control of the AK4117 via 4-wire serial interface
* IEC958 (S/PDIF) receiver by Asahi Kasei
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -20,16 +20,16 @@
*
*/
-#include <sound/driver.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
#include <sound/ak4117.h>
#include <sound/asoundef.h>
-MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("AK4117 IEC958 (S/PDIF) receiver by Asahi Kasei");
MODULE_LICENSE("GPL");
@@ -62,7 +62,7 @@ static void reg_dump(struct ak4117 *ak4117)
static void snd_ak4117_free(struct ak4117 *chip)
{
- del_timer(&chip->timer);
+ del_timer_sync(&chip->timer);
kfree(chip);
}
@@ -74,7 +74,7 @@ static int snd_ak4117_dev_free(struct snd_device *device)
}
int snd_ak4117_create(struct snd_card *card, ak4117_read_t *read, ak4117_write_t *write,
- unsigned char pgm[5], void *private_data, struct ak4117 **r_ak4117)
+ const unsigned char pgm[5], void *private_data, struct ak4117 **r_ak4117)
{
struct ak4117 *chip;
int err = 0;
@@ -181,15 +181,7 @@ static int snd_ak4117_in_error_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static int snd_ak4117_in_bit_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;
-}
+#define snd_ak4117_in_bit_info snd_ctl_boolean_mono_info
static int snd_ak4117_in_bit_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -388,7 +380,7 @@ static struct snd_kcontrol_new snd_ak4117_iec958_controls[] = {
},
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = "IEC958 Preample Capture Default",
+ .name = "IEC958 Preamble Capture Default",
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.info = snd_ak4117_spdif_pinfo,
.get = snd_ak4117_spdif_pget,
@@ -440,7 +432,8 @@ int snd_ak4117_build(struct ak4117 *ak4117, struct snd_pcm_substream *cap_substr
unsigned int idx;
int err;
- snd_assert(cap_substream, return -EINVAL);
+ if (snd_BUG_ON(!cap_substream))
+ return -EINVAL;
ak4117->substream = cap_substream;
for (idx = 0; idx < AK4117_CONTROLS; idx++) {
kctl = snd_ctl_new1(&snd_ak4117_iec958_controls[idx], ak4117);
diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c
index 5da49e2eb35..f3735e64791 100644
--- a/sound/i2c/other/ak4xxx-adda.c
+++ b/sound/i2c/other/ak4xxx-adda.c
@@ -2,7 +2,7 @@
* ALSA driver for AK4524 / AK4528 / AK4529 / AK4355 / AK4358 / AK4381
* AD and DA converters
*
- * Copyright (c) 2000-2004 Jaroslav Kysela <perex@suse.cz>,
+ * Copyright (c) 2000-2004 Jaroslav Kysela <perex@perex.cz>,
* Takashi Iwai <tiwai@suse.de>
*
* This program is free software; you can redistribute it and/or modify
@@ -19,19 +19,20 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
- */
+ */
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/init.h>
+#include <linux/module.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/tlv.h>
#include <sound/ak4xxx-adda.h>
+#include <sound/info.h>
-MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Takashi Iwai <tiwai@suse.de>");
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("Routines for control of AK452x / AK43xx AD/DA converters");
MODULE_LICENSE("GPL");
@@ -53,25 +54,21 @@ EXPORT_SYMBOL(snd_akm4xxx_write);
static void ak4524_reset(struct snd_akm4xxx *ak, int state)
{
unsigned int chip;
- unsigned char reg, maxreg;
+ unsigned char reg;
- if (ak->type == SND_AK4528)
- maxreg = 0x06;
- else
- maxreg = 0x08;
for (chip = 0; chip < ak->num_dacs/2; chip++) {
snd_akm4xxx_write(ak, chip, 0x01, state ? 0x00 : 0x03);
if (state)
continue;
/* DAC volumes */
- for (reg = 0x04; reg < maxreg; reg++)
+ for (reg = 0x04; reg < ak->total_regs; reg++)
snd_akm4xxx_write(ak, chip, reg,
snd_akm4xxx_get(ak, chip, reg));
}
}
/* reset procedure for AK4355 and AK4358 */
-static void ak4355_reset(struct snd_akm4xxx *ak, int state)
+static void ak435X_reset(struct snd_akm4xxx *ak, int state)
{
unsigned char reg;
@@ -79,7 +76,7 @@ static void ak4355_reset(struct snd_akm4xxx *ak, int state)
snd_akm4xxx_write(ak, 0, 0x01, 0x02); /* reset and soft-mute */
return;
}
- for (reg = 0x00; reg < 0x0b; reg++)
+ for (reg = 0x00; reg < ak->total_regs; reg++)
if (reg != 0x01)
snd_akm4xxx_write(ak, 0, reg,
snd_akm4xxx_get(ak, 0, reg));
@@ -91,12 +88,11 @@ static void ak4381_reset(struct snd_akm4xxx *ak, int state)
{
unsigned int chip;
unsigned char reg;
-
for (chip = 0; chip < ak->num_dacs/2; chip++) {
snd_akm4xxx_write(ak, chip, 0x00, state ? 0x0c : 0x0f);
if (state)
continue;
- for (reg = 0x01; reg < 0x05; reg++)
+ for (reg = 0x01; reg < ak->total_regs; reg++)
snd_akm4xxx_write(ak, chip, reg,
snd_akm4xxx_get(ak, chip, reg));
}
@@ -113,14 +109,17 @@ void snd_akm4xxx_reset(struct snd_akm4xxx *ak, int state)
switch (ak->type) {
case SND_AK4524:
case SND_AK4528:
+ case SND_AK4620:
ak4524_reset(ak, state);
break;
case SND_AK4529:
/* FIXME: needed for ak4529? */
break;
case SND_AK4355:
+ ak435X_reset(ak, state);
+ break;
case SND_AK4358:
- ak4355_reset(ak, state);
+ ak435X_reset(ak, state);
break;
case SND_AK4381:
ak4381_reset(ak, state);
@@ -137,10 +136,10 @@ EXPORT_SYMBOL(snd_akm4xxx_reset);
* Volume conversion table for non-linear volumes
* from -63.5dB (mute) to 0dB step 0.5dB
*
- * Used for AK4524 input/ouput attenuation, AK4528, and
+ * Used for AK4524/AK4620 input/ouput attenuation, AK4528, and
* AK5365 input attenuation
*/
-static unsigned char vol_cvt_datt[128] = {
+static const unsigned char vol_cvt_datt[128] = {
0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06,
0x06, 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x0a,
@@ -162,17 +161,17 @@ static unsigned char vol_cvt_datt[128] = {
/*
* dB tables
*/
-static DECLARE_TLV_DB_SCALE(db_scale_vol_datt, -6350, 50, 1);
-static DECLARE_TLV_DB_SCALE(db_scale_8bit, -12750, 50, 1);
-static DECLARE_TLV_DB_SCALE(db_scale_7bit, -6350, 50, 1);
-static DECLARE_TLV_DB_LINEAR(db_scale_linear, TLV_DB_GAIN_MUTE, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_vol_datt, -6350, 50, 1);
+static const DECLARE_TLV_DB_SCALE(db_scale_8bit, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(db_scale_7bit, -6350, 50, 1);
+static const DECLARE_TLV_DB_LINEAR(db_scale_linear, TLV_DB_GAIN_MUTE, 0);
/*
* initialize all the ak4xxx chips
*/
void snd_akm4xxx_init(struct snd_akm4xxx *ak)
{
- static unsigned char inits_ak4524[] = {
+ static const unsigned char inits_ak4524[] = {
0x00, 0x07, /* 0: all power up */
0x01, 0x00, /* 1: ADC/DAC reset */
0x02, 0x60, /* 2: 24bit I2S */
@@ -184,7 +183,7 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
0x07, 0x00, /* 7: DAC right muted */
0xff, 0xff
};
- static unsigned char inits_ak4528[] = {
+ static const unsigned char inits_ak4528[] = {
0x00, 0x07, /* 0: all power up */
0x01, 0x00, /* 1: ADC/DAC reset */
0x02, 0x60, /* 2: 24bit I2S */
@@ -194,7 +193,7 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
0x05, 0x00, /* 5: ADC right muted */
0xff, 0xff
};
- static unsigned char inits_ak4529[] = {
+ static const unsigned char inits_ak4529[] = {
0x09, 0x01, /* 9: ATS=0, RSTN=1 */
0x0a, 0x3f, /* A: all power up, no zero/overflow detection */
0x00, 0x0c, /* 0: TDM=0, 24bit I2S, SMUTE=0 */
@@ -210,7 +209,7 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
0x08, 0x55, /* 8: deemphasis all off */
0xff, 0xff
};
- static unsigned char inits_ak4355[] = {
+ static const unsigned char inits_ak4355[] = {
0x01, 0x02, /* 1: reset and soft-mute */
0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect,
* disable DZF, sharp roll-off, RSTN#=0 */
@@ -227,12 +226,12 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
0x01, 0x01, /* 1: un-reset, unmute */
0xff, 0xff
};
- static unsigned char inits_ak4358[] = {
+ static const unsigned char inits_ak4358[] = {
0x01, 0x02, /* 1: reset and soft-mute */
0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect,
* disable DZF, sharp roll-off, RSTN#=0 */
- 0x02, 0x0e, /* 2: DA's power up, normal speed, RSTN#=0 */
- // 0x02, 0x2e, /* quad speed */
+ 0x02, 0x4e, /* 2: DA's power up, normal speed, RSTN#=0 */
+ /* 0x02, 0x6e,*/ /* quad speed */
0x03, 0x01, /* 3: de-emphasis off */
0x04, 0x00, /* 4: LOUT1 volume muted */
0x05, 0x00, /* 5: ROUT1 volume muted */
@@ -246,7 +245,7 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
0x01, 0x01, /* 1: un-reset, unmute */
0xff, 0xff
};
- static unsigned char inits_ak4381[] = {
+ static const unsigned char inits_ak4381[] = {
0x00, 0x0c, /* 0: mode3(i2s), disable auto-clock detect */
0x01, 0x02, /* 1: de-emphasis off, normal speed,
* sharp roll-off, DZF off */
@@ -257,9 +256,24 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
0x00, 0x0f, /* 0: power-up, un-reset */
0xff, 0xff
};
+ static const unsigned char inits_ak4620[] = {
+ 0x00, 0x07, /* 0: normal */
+ 0x01, 0x00, /* 0: reset */
+ 0x01, 0x02, /* 1: RSTAD */
+ 0x01, 0x03, /* 1: RSTDA */
+ 0x01, 0x0f, /* 1: normal */
+ 0x02, 0x60, /* 2: 24bit I2S */
+ 0x03, 0x01, /* 3: deemphasis off */
+ 0x04, 0x00, /* 4: LIN muted */
+ 0x05, 0x00, /* 5: RIN muted */
+ 0x06, 0x00, /* 6: LOUT muted */
+ 0x07, 0x00, /* 7: ROUT muted */
+ 0xff, 0xff
+ };
- int chip, num_chips;
- unsigned char *ptr, reg, data, *inits;
+ int chip;
+ const unsigned char *ptr, *inits;
+ unsigned char reg, data;
memset(ak->images, 0, sizeof(ak->images));
memset(ak->volumes, 0, sizeof(ak->volumes));
@@ -267,42 +281,64 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
switch (ak->type) {
case SND_AK4524:
inits = inits_ak4524;
- num_chips = ak->num_dacs / 2;
+ ak->num_chips = ak->num_dacs / 2;
+ ak->name = "ak4524";
+ ak->total_regs = 0x08;
break;
case SND_AK4528:
inits = inits_ak4528;
- num_chips = ak->num_dacs / 2;
+ ak->num_chips = ak->num_dacs / 2;
+ ak->name = "ak4528";
+ ak->total_regs = 0x06;
break;
case SND_AK4529:
inits = inits_ak4529;
- num_chips = 1;
+ ak->num_chips = 1;
+ ak->name = "ak4529";
+ ak->total_regs = 0x0d;
break;
case SND_AK4355:
inits = inits_ak4355;
- num_chips = 1;
+ ak->num_chips = 1;
+ ak->name = "ak4355";
+ ak->total_regs = 0x0b;
break;
case SND_AK4358:
inits = inits_ak4358;
- num_chips = 1;
+ ak->num_chips = 1;
+ ak->name = "ak4358";
+ ak->total_regs = 0x10;
break;
case SND_AK4381:
inits = inits_ak4381;
- num_chips = ak->num_dacs / 2;
+ ak->num_chips = ak->num_dacs / 2;
+ ak->name = "ak4381";
+ ak->total_regs = 0x05;
break;
case SND_AK5365:
/* FIXME: any init sequence? */
+ ak->num_chips = 1;
+ ak->name = "ak5365";
+ ak->total_regs = 0x08;
return;
+ case SND_AK4620:
+ inits = inits_ak4620;
+ ak->num_chips = ak->num_dacs / 2;
+ ak->name = "ak4620";
+ ak->total_regs = 0x08;
+ break;
default:
snd_BUG();
return;
}
- for (chip = 0; chip < num_chips; chip++) {
+ for (chip = 0; chip < ak->num_chips; chip++) {
ptr = inits;
while (*ptr != 0xff) {
reg = *ptr++;
data = *ptr++;
snd_akm4xxx_write(ak, chip, reg, data);
+ udelay(10);
}
}
}
@@ -369,6 +405,8 @@ static int put_ak_reg(struct snd_kcontrol *kcontrol, int addr,
nval = mask - nval;
if (AK_GET_NEEDSMSB(kcontrol->private_value))
nval |= 0x80;
+ /* printk(KERN_DEBUG "DEBUG - AK writing reg: chip %x addr %x,
+ nval %x\n", chip, addr, nval); */
snd_akm4xxx_write(ak, chip, addr, nval);
return 1;
}
@@ -376,8 +414,11 @@ static int put_ak_reg(struct snd_kcontrol *kcontrol, int addr,
static int snd_akm4xxx_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- return put_ak_reg(kcontrol, AK_GET_ADDR(kcontrol->private_value),
- ucontrol->value.integer.value[0]);
+ unsigned int mask = AK_GET_MASK(kcontrol->private_value);
+ unsigned int val = ucontrol->value.integer.value[0];
+ if (val > mask)
+ return -EINVAL;
+ return put_ak_reg(kcontrol, AK_GET_ADDR(kcontrol->private_value), val);
}
static int snd_akm4xxx_stereo_volume_info(struct snd_kcontrol *kcontrol,
@@ -408,11 +449,16 @@ static int snd_akm4xxx_stereo_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
int addr = AK_GET_ADDR(kcontrol->private_value);
+ unsigned int mask = AK_GET_MASK(kcontrol->private_value);
+ unsigned int val[2];
int change;
- change = put_ak_reg(kcontrol, addr, ucontrol->value.integer.value[0]);
- change |= put_ak_reg(kcontrol, addr + 1,
- ucontrol->value.integer.value[1]);
+ val[0] = ucontrol->value.integer.value[0];
+ val[1] = ucontrol->value.integer.value[1];
+ if (val[0] > mask || val[1] > mask)
+ return -EINVAL;
+ change = put_ak_reg(kcontrol, addr, val[0]);
+ change |= put_ak_reg(kcontrol, addr + 1, val[1]);
return change;
}
@@ -462,15 +508,7 @@ static int snd_akm4xxx_deemphasis_put(struct snd_kcontrol *kcontrol,
return change;
}
-static int ak4xxx_switch_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;
-}
+#define ak4xxx_switch_info snd_ctl_boolean_mono_info
static int ak4xxx_switch_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -480,8 +518,8 @@ static int ak4xxx_switch_get(struct snd_kcontrol *kcontrol,
int addr = AK_GET_ADDR(kcontrol->private_value);
int shift = AK_GET_SHIFT(kcontrol->private_value);
int invert = AK_GET_INVERT(kcontrol->private_value);
- unsigned char val = snd_akm4xxx_get(ak, chip, addr);
-
+ /* we observe the (1<<shift) bit only */
+ unsigned char val = snd_akm4xxx_get(ak, chip, addr) & (1<<shift);
if (invert)
val = ! val;
ucontrol->value.integer.value[0] = (val & (1<<shift)) != 0;
@@ -513,6 +551,81 @@ static int ak4xxx_switch_put(struct snd_kcontrol *kcontrol,
return change;
}
+#define AK5365_NUM_INPUTS 5
+
+static int ak4xxx_capture_num_inputs(struct snd_akm4xxx *ak, int mixer_ch)
+{
+ int num_names;
+ const char **input_names;
+
+ input_names = ak->adc_info[mixer_ch].input_names;
+ num_names = 0;
+ while (num_names < AK5365_NUM_INPUTS && input_names[num_names])
+ ++num_names;
+ return num_names;
+}
+
+static int ak4xxx_capture_source_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
+ int mixer_ch = AK_GET_SHIFT(kcontrol->private_value);
+ const char **input_names;
+ unsigned int num_names, idx;
+
+ num_names = ak4xxx_capture_num_inputs(ak, mixer_ch);
+ if (!num_names)
+ return -EINVAL;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = num_names;
+ idx = uinfo->value.enumerated.item;
+ if (idx >= num_names)
+ return -EINVAL;
+ input_names = ak->adc_info[mixer_ch].input_names;
+ strlcpy(uinfo->value.enumerated.name, input_names[idx],
+ sizeof(uinfo->value.enumerated.name));
+ return 0;
+}
+
+static int ak4xxx_capture_source_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
+ int chip = AK_GET_CHIP(kcontrol->private_value);
+ int addr = AK_GET_ADDR(kcontrol->private_value);
+ int mask = AK_GET_MASK(kcontrol->private_value);
+ unsigned char val;
+
+ val = snd_akm4xxx_get(ak, chip, addr) & mask;
+ ucontrol->value.enumerated.item[0] = val;
+ return 0;
+}
+
+static int ak4xxx_capture_source_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
+ int mixer_ch = AK_GET_SHIFT(kcontrol->private_value);
+ int chip = AK_GET_CHIP(kcontrol->private_value);
+ int addr = AK_GET_ADDR(kcontrol->private_value);
+ int mask = AK_GET_MASK(kcontrol->private_value);
+ unsigned char oval, val;
+ int num_names = ak4xxx_capture_num_inputs(ak, mixer_ch);
+
+ if (ucontrol->value.enumerated.item[0] >= num_names)
+ return -EINVAL;
+
+ oval = snd_akm4xxx_get(ak, chip, addr);
+ val = oval & ~mask;
+ val |= ucontrol->value.enumerated.item[0] & mask;
+ if (val != oval) {
+ snd_akm4xxx_write(ak, chip, addr, val);
+ return 1;
+ }
+ return 0;
+}
+
/*
* build AK4xxx controls
*/
@@ -524,6 +637,26 @@ static int build_dac_controls(struct snd_akm4xxx *ak)
mixer_ch = 0;
for (idx = 0; idx < ak->num_dacs; ) {
+ /* mute control for Revolution 7.1 - AK4381 */
+ if (ak->type == SND_AK4381
+ && ak->dac_info[mixer_ch].switch_name) {
+ memset(&knew, 0, sizeof(knew));
+ knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ knew.count = 1;
+ knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ knew.name = ak->dac_info[mixer_ch].switch_name;
+ knew.info = ak4xxx_switch_info;
+ knew.get = ak4xxx_switch_get;
+ knew.put = ak4xxx_switch_put;
+ knew.access = 0;
+ /* register 1, bit 0 (SMUTE): 0 = normal operation,
+ 1 = mute */
+ knew.private_value =
+ AK_COMPOSE(idx/2, 1, 0, 0) | AK_INVERT;
+ err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
+ if (err < 0)
+ return err;
+ }
memset(&knew, 0, sizeof(knew));
if (! ak->dac_info || ! ak->dac_info[mixer_ch].name) {
knew.name = "DAC Volume";
@@ -588,6 +721,12 @@ static int build_dac_controls(struct snd_akm4xxx *ak)
AK_COMPOSE(idx/2, (idx%2) + 3, 0, 255);
knew.tlv.p = db_scale_linear;
break;
+ case SND_AK4620:
+ /* register 6 & 7 */
+ knew.private_value =
+ AK_COMPOSE(idx/2, (idx%2) + 6, 0, 255);
+ knew.tlv.p = db_scale_linear;
+ break;
default:
return -EINVAL;
}
@@ -604,10 +743,12 @@ static int build_dac_controls(struct snd_akm4xxx *ak)
static int build_adc_controls(struct snd_akm4xxx *ak)
{
- int idx, err, mixer_ch, num_stereo;
+ int idx, err, mixer_ch, num_stereo, max_steps;
struct snd_kcontrol_new knew;
mixer_ch = 0;
+ if (ak->type == SND_AK4528)
+ return 0; /* no controls */
for (idx = 0; idx < ak->num_adcs;) {
memset(&knew, 0, sizeof(knew));
if (! ak->adc_info || ! ak->adc_info[mixer_ch].name) {
@@ -633,13 +774,12 @@ static int build_adc_controls(struct snd_akm4xxx *ak)
}
/* register 4 & 5 */
if (ak->type == SND_AK5365)
- knew.private_value =
- AK_COMPOSE(idx/2, (idx%2) + 4, 0, 151) |
- AK_VOL_CVT | AK_IPGA;
+ max_steps = 152;
else
- knew.private_value =
- AK_COMPOSE(idx/2, (idx%2) + 4, 0, 163) |
- AK_VOL_CVT | AK_IPGA;
+ max_steps = 164;
+ knew.private_value =
+ AK_COMPOSE(idx/2, (idx%2) + 4, 0, max_steps) |
+ AK_VOL_CVT | AK_IPGA;
knew.tlv.p = db_scale_vol_datt;
err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
if (err < 0)
@@ -647,9 +787,10 @@ static int build_adc_controls(struct snd_akm4xxx *ak)
if (ak->type == SND_AK5365 && (idx % 2) == 0) {
if (! ak->adc_info ||
- ! ak->adc_info[mixer_ch].switch_name)
+ ! ak->adc_info[mixer_ch].switch_name) {
knew.name = "Capture Switch";
- else
+ knew.index = mixer_ch + ak->idx_offset * 2;
+ } else
knew.name = ak->adc_info[mixer_ch].switch_name;
knew.info = ak4xxx_switch_info;
knew.get = ak4xxx_switch_get;
@@ -662,6 +803,26 @@ static int build_adc_controls(struct snd_akm4xxx *ak)
err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
if (err < 0)
return err;
+
+ memset(&knew, 0, sizeof(knew));
+ knew.name = ak->adc_info[mixer_ch].selector_name;
+ if (!knew.name) {
+ knew.name = "Capture Channel";
+ knew.index = mixer_ch + ak->idx_offset * 2;
+ }
+
+ knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ knew.info = ak4xxx_capture_source_info;
+ knew.get = ak4xxx_capture_source_get;
+ knew.put = ak4xxx_capture_source_put;
+ knew.access = 0;
+ /* input selector control: reg. 1, bits 0-2.
+ * mis-use 'shift' to pass mixer_ch */
+ knew.private_value
+ = AK_COMPOSE(idx/2, 1, mixer_ch, 0x07);
+ err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
+ if (err < 0)
+ return err;
}
idx += num_stereo;
@@ -687,6 +848,7 @@ static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs)
switch (ak->type) {
case SND_AK4524:
case SND_AK4528:
+ case SND_AK4620:
/* register 3 */
knew.private_value = AK_COMPOSE(idx, 3, 0, 0);
break;
@@ -713,6 +875,35 @@ static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs)
return 0;
}
+#ifdef CONFIG_PROC_FS
+static void proc_regs_read(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_akm4xxx *ak = entry->private_data;
+ int reg, val, chip;
+ for (chip = 0; chip < ak->num_chips; chip++) {
+ for (reg = 0; reg < ak->total_regs; reg++) {
+ val = snd_akm4xxx_get(ak, chip, reg);
+ snd_iprintf(buffer, "chip %d: 0x%02x = 0x%02x\n", chip,
+ reg, val);
+ }
+ }
+}
+
+static int proc_init(struct snd_akm4xxx *ak)
+{
+ struct snd_info_entry *entry;
+ int err;
+ err = snd_card_proc_new(ak->card, ak->name, &entry);
+ if (err < 0)
+ return err;
+ snd_info_set_text_ops(entry, ak, proc_regs_read);
+ return 0;
+}
+#else /* !CONFIG_PROC_FS */
+static int proc_init(struct snd_akm4xxx *ak) { return 0; }
+#endif
+
int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
{
int err, num_emphs;
@@ -724,18 +915,21 @@ int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
err = build_adc_controls(ak);
if (err < 0)
return err;
-
if (ak->type == SND_AK4355 || ak->type == SND_AK4358)
num_emphs = 1;
+ else if (ak->type == SND_AK4620)
+ num_emphs = 0;
else
num_emphs = ak->num_dacs / 2;
err = build_deemphasis(ak, num_emphs);
if (err < 0)
return err;
+ err = proc_init(ak);
+ if (err < 0)
+ return err;
return 0;
}
-
EXPORT_SYMBOL(snd_akm4xxx_build_controls);
static int __init alsa_akm4xxx_module_init(void)
diff --git a/sound/i2c/other/pt2258.c b/sound/i2c/other/pt2258.c
new file mode 100644
index 00000000000..9fa390ba171
--- /dev/null
+++ b/sound/i2c/other/pt2258.c
@@ -0,0 +1,227 @@
+/*
+ * ALSA Driver for the PT2258 volume controller.
+ *
+ * Copyright (c) 2006 Jochen Voss <voss@seehuhn.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include <sound/i2c.h>
+#include <sound/pt2258.h>
+#include <linux/module.h>
+
+MODULE_AUTHOR("Jochen Voss <voss@seehuhn.de>");
+MODULE_DESCRIPTION("PT2258 volume controller (Princeton Technology Corp.)");
+MODULE_LICENSE("GPL");
+
+#define PT2258_CMD_RESET 0xc0
+#define PT2258_CMD_UNMUTE 0xf8
+#define PT2258_CMD_MUTE 0xf9
+
+static const unsigned char pt2258_channel_code[12] = {
+ 0x80, 0x90, /* channel 1: -10dB, -1dB */
+ 0x40, 0x50, /* channel 2: -10dB, -1dB */
+ 0x00, 0x10, /* channel 3: -10dB, -1dB */
+ 0x20, 0x30, /* channel 4: -10dB, -1dB */
+ 0x60, 0x70, /* channel 5: -10dB, -1dB */
+ 0xa0, 0xb0 /* channel 6: -10dB, -1dB */
+};
+
+int snd_pt2258_reset(struct snd_pt2258 *pt)
+{
+ unsigned char bytes[2];
+ int i;
+
+ /* reset chip */
+ bytes[0] = PT2258_CMD_RESET;
+ snd_i2c_lock(pt->i2c_bus);
+ if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 1) != 1)
+ goto __error;
+ snd_i2c_unlock(pt->i2c_bus);
+
+ /* mute all channels */
+ pt->mute = 1;
+ bytes[0] = PT2258_CMD_MUTE;
+ snd_i2c_lock(pt->i2c_bus);
+ if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 1) != 1)
+ goto __error;
+ snd_i2c_unlock(pt->i2c_bus);
+
+ /* set all channels to 0dB */
+ for (i = 0; i < 6; ++i)
+ pt->volume[i] = 0;
+ bytes[0] = 0xd0;
+ bytes[1] = 0xe0;
+ snd_i2c_lock(pt->i2c_bus);
+ if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 2) != 2)
+ goto __error;
+ snd_i2c_unlock(pt->i2c_bus);
+
+ return 0;
+
+ __error:
+ snd_i2c_unlock(pt->i2c_bus);
+ snd_printk(KERN_ERR "PT2258 reset failed\n");
+ return -EIO;
+}
+
+static int pt2258_stereo_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 79;
+ return 0;
+}
+
+static int pt2258_stereo_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pt2258 *pt = kcontrol->private_data;
+ int base = kcontrol->private_value;
+
+ /* chip does not support register reads */
+ ucontrol->value.integer.value[0] = 79 - pt->volume[base];
+ ucontrol->value.integer.value[1] = 79 - pt->volume[base + 1];
+ return 0;
+}
+
+static int pt2258_stereo_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pt2258 *pt = kcontrol->private_data;
+ int base = kcontrol->private_value;
+ unsigned char bytes[2];
+ int val0, val1;
+
+ val0 = 79 - ucontrol->value.integer.value[0];
+ val1 = 79 - ucontrol->value.integer.value[1];
+ if (val0 < 0 || val0 > 79 || val1 < 0 || val1 > 79)
+ return -EINVAL;
+ if (val0 == pt->volume[base] && val1 == pt->volume[base + 1])
+ return 0;
+
+ pt->volume[base] = val0;
+ bytes[0] = pt2258_channel_code[2 * base] | (val0 / 10);
+ bytes[1] = pt2258_channel_code[2 * base + 1] | (val0 % 10);
+ snd_i2c_lock(pt->i2c_bus);
+ if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 2) != 2)
+ goto __error;
+ snd_i2c_unlock(pt->i2c_bus);
+
+ pt->volume[base + 1] = val1;
+ bytes[0] = pt2258_channel_code[2 * base + 2] | (val1 / 10);
+ bytes[1] = pt2258_channel_code[2 * base + 3] | (val1 % 10);
+ snd_i2c_lock(pt->i2c_bus);
+ if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 2) != 2)
+ goto __error;
+ snd_i2c_unlock(pt->i2c_bus);
+
+ return 1;
+
+ __error:
+ snd_i2c_unlock(pt->i2c_bus);
+ snd_printk(KERN_ERR "PT2258 access failed\n");
+ return -EIO;
+}
+
+#define pt2258_switch_info snd_ctl_boolean_mono_info
+
+static int pt2258_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pt2258 *pt = kcontrol->private_data;
+
+ ucontrol->value.integer.value[0] = !pt->mute;
+ return 0;
+}
+
+static int pt2258_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_pt2258 *pt = kcontrol->private_data;
+ unsigned char bytes[2];
+ int val;
+
+ val = !ucontrol->value.integer.value[0];
+ if (pt->mute == val)
+ return 0;
+
+ pt->mute = val;
+ bytes[0] = val ? PT2258_CMD_MUTE : PT2258_CMD_UNMUTE;
+ snd_i2c_lock(pt->i2c_bus);
+ if (snd_i2c_sendbytes(pt->i2c_dev, bytes, 1) != 1)
+ goto __error;
+ snd_i2c_unlock(pt->i2c_bus);
+
+ return 1;
+
+ __error:
+ snd_i2c_unlock(pt->i2c_bus);
+ snd_printk(KERN_ERR "PT2258 access failed 2\n");
+ return -EIO;
+}
+
+static const DECLARE_TLV_DB_SCALE(pt2258_db_scale, -7900, 100, 0);
+
+int snd_pt2258_build_controls(struct snd_pt2258 *pt)
+{
+ struct snd_kcontrol_new knew;
+ char *names[3] = {
+ "Mic Loopback Playback Volume",
+ "Line Loopback Playback Volume",
+ "CD Loopback Playback Volume"
+ };
+ int i, err;
+
+ for (i = 0; i < 3; ++i) {
+ memset(&knew, 0, sizeof(knew));
+ knew.name = names[i];
+ knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ knew.count = 1;
+ knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ knew.private_value = 2 * i;
+ knew.info = pt2258_stereo_volume_info;
+ knew.get = pt2258_stereo_volume_get;
+ knew.put = pt2258_stereo_volume_put;
+ knew.tlv.p = pt2258_db_scale;
+
+ err = snd_ctl_add(pt->card, snd_ctl_new1(&knew, pt));
+ if (err < 0)
+ return err;
+ }
+
+ memset(&knew, 0, sizeof(knew));
+ knew.name = "Loopback Switch";
+ knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ knew.info = pt2258_switch_info;
+ knew.get = pt2258_switch_get;
+ knew.put = pt2258_switch_put;
+ knew.access = 0;
+ err = snd_ctl_add(pt->card, snd_ctl_new1(&knew, pt));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+EXPORT_SYMBOL(snd_pt2258_reset);
+EXPORT_SYMBOL(snd_pt2258_build_controls);
diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c
deleted file mode 100644
index 4c2fd14c105..00000000000
--- a/sound/i2c/other/tea575x-tuner.c
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * ALSA driver for TEA5757/5759 Philips AM/FM radio tuner chips
- *
- * Copyright (c) 2004 Jaroslav Kysela <perex@suse.cz>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <sound/driver.h>
-#include <asm/io.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <sound/core.h>
-#include <sound/tea575x-tuner.h>
-
-MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
-MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips");
-MODULE_LICENSE("GPL");
-
-/*
- * definitions
- */
-
-#define TEA575X_BIT_SEARCH (1<<24) /* 1 = search action, 0 = tuned */
-#define TEA575X_BIT_UPDOWN (1<<23) /* 0 = search down, 1 = search up */
-#define TEA575X_BIT_MONO (1<<22) /* 0 = stereo, 1 = mono */
-#define TEA575X_BIT_BAND_MASK (3<<20)
-#define TEA575X_BIT_BAND_FM (0<<20)
-#define TEA575X_BIT_BAND_MW (1<<20)
-#define TEA575X_BIT_BAND_LW (1<<21)
-#define TEA575X_BIT_BAND_SW (1<<22)
-#define TEA575X_BIT_PORT_0 (1<<19) /* user bit */
-#define TEA575X_BIT_PORT_1 (1<<18) /* user bit */
-#define TEA575X_BIT_SEARCH_MASK (3<<16) /* search level */
-#define TEA575X_BIT_SEARCH_5_28 (0<<16) /* FM >5uV, AM >28uV */
-#define TEA575X_BIT_SEARCH_10_40 (1<<16) /* FM >10uV, AM > 40uV */
-#define TEA575X_BIT_SEARCH_30_63 (2<<16) /* FM >30uV, AM > 63uV */
-#define TEA575X_BIT_SEARCH_150_1000 (3<<16) /* FM > 150uV, AM > 1000uV */
-#define TEA575X_BIT_DUMMY (1<<15) /* buffer */
-#define TEA575X_BIT_FREQ_MASK 0x7fff
-
-/*
- * lowlevel part
- */
-
-static void snd_tea575x_set_freq(struct snd_tea575x *tea)
-{
- unsigned long freq;
-
- freq = tea->freq / 16; /* to kHz */
- if (freq > 108000)
- freq = 108000;
- if (freq < 87000)
- freq = 87000;
- /* crystal fixup */
- if (tea->tea5759)
- freq -= tea->freq_fixup;
- else
- freq += tea->freq_fixup;
- /* freq /= 12.5 */
- freq *= 10;
- freq /= 125;
-
- tea->val &= ~TEA575X_BIT_FREQ_MASK;
- tea->val |= freq & TEA575X_BIT_FREQ_MASK;
- tea->ops->write(tea, tea->val);
-}
-
-/*
- * Linux Video interface
- */
-
-static int snd_tea575x_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long data)
-{
- struct video_device *dev = video_devdata(file);
- struct snd_tea575x *tea = video_get_drvdata(dev);
- void __user *arg = (void __user *)data;
-
- switch(cmd) {
- case VIDIOCGCAP:
- {
- struct video_capability v;
- v.type = VID_TYPE_TUNER;
- v.channels = 1;
- v.audios = 1;
- /* No we don't do pictures */
- v.maxwidth = 0;
- v.maxheight = 0;
- v.minwidth = 0;
- v.minheight = 0;
- strcpy(v.name, tea->tea5759 ? "TEA5759" : "TEA5757");
- if (copy_to_user(arg,&v,sizeof(v)))
- return -EFAULT;
- return 0;
- }
- case VIDIOCGTUNER:
- {
- struct video_tuner v;
- if (copy_from_user(&v, arg,sizeof(v))!=0)
- return -EFAULT;
- if (v.tuner) /* Only 1 tuner */
- return -EINVAL;
- v.rangelow = (87*16000);
- v.rangehigh = (108*16000);
- v.flags = VIDEO_TUNER_LOW;
- v.mode = VIDEO_MODE_AUTO;
- strcpy(v.name, "FM");
- v.signal = 0xFFFF;
- if (copy_to_user(arg, &v, sizeof(v)))
- return -EFAULT;
- return 0;
- }
- case VIDIOCSTUNER:
- {
- struct video_tuner v;
- if(copy_from_user(&v, arg, sizeof(v)))
- return -EFAULT;
- if(v.tuner!=0)
- return -EINVAL;
- /* Only 1 tuner so no setting needed ! */
- return 0;
- }
- case VIDIOCGFREQ:
- if(copy_to_user(arg, &tea->freq, sizeof(tea->freq)))
- return -EFAULT;
- return 0;
- case VIDIOCSFREQ:
- if(copy_from_user(&tea->freq, arg, sizeof(tea->freq)))
- return -EFAULT;
- snd_tea575x_set_freq(tea);
- return 0;
- case VIDIOCGAUDIO:
- {
- struct video_audio v;
- memset(&v, 0, sizeof(v));
- strcpy(v.name, "Radio");
- if(copy_to_user(arg,&v, sizeof(v)))
- return -EFAULT;
- return 0;
- }
- case VIDIOCSAUDIO:
- {
- struct video_audio v;
- if(copy_from_user(&v, arg, sizeof(v)))
- return -EFAULT;
- if(v.audio)
- return -EINVAL;
- return 0;
- }
- default:
- return -ENOIOCTLCMD;
- }
-}
-
-static void snd_tea575x_release(struct video_device *vfd)
-{
-}
-
-/*
- * initialize all the tea575x chips
- */
-void snd_tea575x_init(struct snd_tea575x *tea)
-{
- unsigned int val;
-
- val = tea->ops->read(tea);
- if (val == 0x1ffffff || val == 0) {
- snd_printk(KERN_ERR "Cannot find TEA575x chip\n");
- return;
- }
-
- memset(&tea->vd, 0, sizeof(tea->vd));
- tea->vd.owner = tea->card->module;
- strcpy(tea->vd.name, tea->tea5759 ? "TEA5759 radio" : "TEA5757 radio");
- tea->vd.type = VID_TYPE_TUNER;
- tea->vd.hardware = VID_HARDWARE_RTRACK; /* FIXME: assign new number */
- tea->vd.release = snd_tea575x_release;
- video_set_drvdata(&tea->vd, tea);
- tea->vd.fops = &tea->fops;
- tea->fops.owner = tea->card->module;
- tea->fops.open = video_exclusive_open;
- tea->fops.release = video_exclusive_release;
- tea->fops.ioctl = snd_tea575x_ioctl;
- if (video_register_device(&tea->vd, VFL_TYPE_RADIO, tea->dev_nr - 1) < 0) {
- snd_printk(KERN_ERR "unable to register tea575x tuner\n");
- return;
- }
- tea->vd_registered = 1;
-
- tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
- tea->freq = 90500 * 16; /* 90.5Mhz default */
-
- snd_tea575x_set_freq(tea);
-}
-
-void snd_tea575x_exit(struct snd_tea575x *tea)
-{
- if (tea->vd_registered) {
- video_unregister_device(&tea->vd);
- tea->vd_registered = 0;
- }
-}
-
-static int __init alsa_tea575x_module_init(void)
-{
- return 0;
-}
-
-static void __exit alsa_tea575x_module_exit(void)
-{
-}
-
-module_init(alsa_tea575x_module_init)
-module_exit(alsa_tea575x_module_exit)
-
-EXPORT_SYMBOL(snd_tea575x_init);
-EXPORT_SYMBOL(snd_tea575x_exit);