diff options
Diffstat (limited to 'sound')
417 files changed, 23255 insertions, 7771 deletions
diff --git a/sound/aoa/aoa.h b/sound/aoa/aoa.h index 541b908..e087894 100644 --- a/sound/aoa/aoa.h +++ b/sound/aoa/aoa.h @@ -10,8 +10,6 @@ #define __AOA_H #include <asm/prom.h> #include <linux/module.h> -/* So apparently there's a reason for requiring driver.h to be included first! */ -#include <sound/driver.h> #include <sound/core.h> #include <sound/asound.h> #include <sound/control.h> diff --git a/sound/aoa/codecs/snd-aoa-codec-onyx.c b/sound/aoa/codecs/snd-aoa-codec-onyx.c index 71e3f93..6a3837d 100644 --- a/sound/aoa/codecs/snd-aoa-codec-onyx.c +++ b/sound/aoa/codecs/snd-aoa-codec-onyx.c @@ -138,6 +138,13 @@ static int onyx_snd_vol_put(struct snd_kcontrol *kcontrol, struct onyx *onyx = snd_kcontrol_chip(kcontrol); s8 l, r; + if (ucontrol->value.integer.value[0] < -128 + VOLUME_RANGE_SHIFT || + ucontrol->value.integer.value[0] > -1 + VOLUME_RANGE_SHIFT) + return -EINVAL; + if (ucontrol->value.integer.value[1] < -128 + VOLUME_RANGE_SHIFT || + ucontrol->value.integer.value[1] > -1 + VOLUME_RANGE_SHIFT) + return -EINVAL; + mutex_lock(&onyx->mutex); onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l); onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r); @@ -206,6 +213,9 @@ static int onyx_snd_inputgain_put(struct snd_kcontrol *kcontrol, struct onyx *onyx = snd_kcontrol_chip(kcontrol); u8 v, n; + if (ucontrol->value.integer.value[0] < 3 + INPUTGAIN_RANGE_SHIFT || + ucontrol->value.integer.value[0] > 28 + INPUTGAIN_RANGE_SHIFT) + return -EINVAL; mutex_lock(&onyx->mutex); onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v); n = v; @@ -272,6 +282,8 @@ static void onyx_set_capture_source(struct onyx *onyx, int mic) static int onyx_snd_capture_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { + if (ucontrol->value.enumerated.item[0] > 1) + return -EINVAL; onyx_set_capture_source(snd_kcontrol_chip(kcontrol), ucontrol->value.enumerated.item[0]); return 1; diff --git a/sound/aoa/codecs/snd-aoa-codec-tas.c b/sound/aoa/codecs/snd-aoa-codec-tas.c index 70c3416..7a16a33 100644 --- a/sound/aoa/codecs/snd-aoa-codec-tas.c +++ b/sound/aoa/codecs/snd-aoa-codec-tas.c @@ -248,6 +248,13 @@ static int tas_snd_vol_put(struct snd_kcontrol *kcontrol, { struct tas *tas = snd_kcontrol_chip(kcontrol); + if (ucontrol->value.integer.value[0] < 0 || + ucontrol->value.integer.value[0] > 177) + return -EINVAL; + if (ucontrol->value.integer.value[1] < 0 || + ucontrol->value.integer.value[1] > 177) + return -EINVAL; + mutex_lock(&tas->mtx); if (tas->cached_volume_l == ucontrol->value.integer.value[0] && tas->cached_volume_r == ucontrol->value.integer.value[1]) { @@ -401,6 +408,10 @@ static int tas_snd_drc_range_put(struct snd_kcontrol *kcontrol, { struct tas *tas = snd_kcontrol_chip(kcontrol); + if (ucontrol->value.integer.value[0] < 0 || + ucontrol->value.integer.value[0] > TAS3004_DRC_MAX) + return -EINVAL; + mutex_lock(&tas->mtx); if (tas->drc_range == ucontrol->value.integer.value[0]) { mutex_unlock(&tas->mtx); @@ -447,7 +458,7 @@ static int tas_snd_drc_switch_put(struct snd_kcontrol *kcontrol, return 0; } - tas->drc_enabled = ucontrol->value.integer.value[0]; + tas->drc_enabled = !!ucontrol->value.integer.value[0]; if (tas->hw_enabled) tas3004_set_drc(tas); mutex_unlock(&tas->mtx); @@ -494,6 +505,8 @@ static int tas_snd_capture_source_put(struct snd_kcontrol *kcontrol, struct tas *tas = snd_kcontrol_chip(kcontrol); int oldacr; + if (ucontrol->value.enumerated.item[0] > 1) + return -EINVAL; mutex_lock(&tas->mtx); oldacr = tas->acr; @@ -562,6 +575,9 @@ static int tas_snd_treble_put(struct snd_kcontrol *kcontrol, { struct tas *tas = snd_kcontrol_chip(kcontrol); + if (ucontrol->value.integer.value[0] < TAS3004_TREBLE_MIN || + ucontrol->value.integer.value[0] > TAS3004_TREBLE_MAX) + return -EINVAL; mutex_lock(&tas->mtx); if (tas->treble == ucontrol->value.integer.value[0]) { mutex_unlock(&tas->mtx); @@ -610,6 +626,9 @@ static int tas_snd_bass_put(struct snd_kcontrol *kcontrol, { struct tas *tas = snd_kcontrol_chip(kcontrol); + if (ucontrol->value.integer.value[0] < TAS3004_BASS_MIN || + ucontrol->value.integer.value[0] > TAS3004_BASS_MAX) + return -EINVAL; mutex_lock(&tas->mtx); if (tas->bass == ucontrol->value.integer.value[0]) { mutex_unlock(&tas->mtx); diff --git a/sound/aoa/fabrics/snd-aoa-fabric-layout.c b/sound/aoa/fabrics/snd-aoa-fabric-layout.c index 8b2ba99..dea7abb 100644 --- a/sound/aoa/fabrics/snd-aoa-fabric-layout.c +++ b/sound/aoa/fabrics/snd-aoa-fabric-layout.c @@ -600,7 +600,7 @@ static int n##_control_put(struct snd_kcontrol *kcontrol, \ struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol); \ if (gpio->methods && gpio->methods->get_##n) \ gpio->methods->set_##n(gpio, \ - ucontrol->value.integer.value[0]); \ + !!ucontrol->value.integer.value[0]); \ return 1; \ } \ static struct snd_kcontrol_new n##_ctl = { \ diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-core.c b/sound/aoa/soundbus/i2sbus/i2sbus-core.c index efb9441..e6beb92 100644 --- a/sound/aoa/soundbus/i2sbus/i2sbus-core.c +++ b/sound/aoa/soundbus/i2sbus/i2sbus-core.c @@ -11,7 +11,6 @@ #include <linux/interrupt.h> #include <linux/dma-mapping.h> -#include <sound/driver.h> #include <sound/core.h> #include <asm/macio.h> diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c b/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c index c6b42f9..59bacd3 100644 --- a/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c +++ b/sound/aoa/soundbus/i2sbus/i2sbus-pcm.c @@ -8,9 +8,6 @@ #include <asm/io.h> #include <linux/delay.h> -/* So apparently there's a reason for requiring driver.h - * to be included first, even if I don't know it... */ -#include <sound/driver.h> #include <sound/core.h> #include <asm/macio.h> #include <linux/pci.h> @@ -194,6 +191,12 @@ static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in) hw->period_bytes_max = 16384; hw->periods_min = 3; hw->periods_max = MAX_DBDMA_COMMANDS; + err = snd_pcm_hw_constraint_integer(pi->substream->runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (err < 0) { + result = err; + goto out_unlock; + } list_for_each_entry(cii, &sdev->codec_list, list) { if (cii->codec->open) { err = cii->codec->open(cii, pi->substream); @@ -990,6 +993,7 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card, if (dev->pcm->card != card) { printk(KERN_ERR "Can't attach same bus to different cards!\n"); + err = -EINVAL; goto out_put_ci_module; } err = snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, 1); diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c index 3b73ba7..b0a4744 100644 --- a/sound/arm/aaci.c +++ b/sound/arm/aaci.c @@ -23,7 +23,6 @@ #include <asm/irq.h> #include <asm/sizes.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/initval.h> #include <sound/ac97_codec.h> diff --git a/sound/arm/devdma.c b/sound/arm/devdma.c index ca3bf4e..9d1e666 100644 --- a/sound/arm/devdma.c +++ b/sound/arm/devdma.c @@ -12,7 +12,6 @@ #include <linux/device.h> #include <linux/dma-mapping.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c index 55c6c82..5d86e68 100644 --- a/sound/arm/pxa2xx-ac97.c +++ b/sound/arm/pxa2xx-ac97.c @@ -18,7 +18,6 @@ #include <linux/wait.h> #include <linux/delay.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/ac97_codec.h> @@ -352,6 +351,7 @@ static int __devinit pxa2xx_ac97_probe(struct platform_device *dev) snprintf(card->longname, sizeof(card->longname), "%s (%s)", dev->dev.driver->name, card->mixername); + snd_card_set_dev(card, &dev->dev); ret = snd_card_register(card); if (ret == 0) { platform_set_drvdata(dev, card); diff --git a/sound/arm/pxa2xx-pcm.c b/sound/arm/pxa2xx-pcm.c index e8cf904..0ede9e4 100644 --- a/sound/arm/pxa2xx-pcm.c +++ b/sound/arm/pxa2xx-pcm.c @@ -16,7 +16,6 @@ #include <linux/slab.h> #include <linux/dma-mapping.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> diff --git a/sound/arm/sa11xx-uda1341.c b/sound/arm/sa11xx-uda1341.c index 81c64b0..0eff33c 100644 --- a/sound/arm/sa11xx-uda1341.c +++ b/sound/arm/sa11xx-uda1341.c @@ -59,7 +59,6 @@ * ***************************************************************************************************/ -#include <sound/driver.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> diff --git a/sound/core/control.c b/sound/core/control.c index df0774c..01a1a5a 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/threads.h> #include <linux/interrupt.h> #include <linux/slab.h> @@ -232,8 +231,6 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol, access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE| SNDRV_CTL_ELEM_ACCESS_INACTIVE| - SNDRV_CTL_ELEM_ACCESS_DINDIRECT| - SNDRV_CTL_ELEM_ACCESS_INDIRECT| SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE| SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)); kctl.info = ncontrol->info; @@ -692,7 +689,7 @@ int snd_ctl_elem_read(struct snd_card *card, struct snd_ctl_elem_value *control) struct snd_kcontrol *kctl; struct snd_kcontrol_volatile *vd; unsigned int index_offset; - int result, indirect; + int result; down_read(&card->controls_rwsem); kctl = snd_ctl_find_id(card, &control->id); @@ -701,17 +698,12 @@ int snd_ctl_elem_read(struct snd_card *card, struct snd_ctl_elem_value *control) } else { index_offset = snd_ctl_get_ioff(kctl, &control->id); vd = &kctl->vd[index_offset]; - indirect = vd->access & SNDRV_CTL_ELEM_ACCESS_INDIRECT ? 1 : 0; - if (control->indirect != indirect) { - result = -EACCES; - } else { - if ((vd->access & SNDRV_CTL_ELEM_ACCESS_READ) && kctl->get != NULL) { - snd_ctl_build_ioff(&control->id, kctl, index_offset); - result = kctl->get(kctl, control); - } else { - result = -EPERM; - } - } + if ((vd->access & SNDRV_CTL_ELEM_ACCESS_READ) && + kctl->get != NULL) { + snd_ctl_build_ioff(&control->id, kctl, index_offset); + result = kctl->get(kctl, control); + } else + result = -EPERM; } up_read(&card->controls_rwsem); return result; @@ -748,7 +740,7 @@ int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, struct snd_kcontrol *kctl; struct snd_kcontrol_volatile *vd; unsigned int index_offset; - int result, indirect; + int result; down_read(&card->controls_rwsem); kctl = snd_ctl_find_id(card, &control->id); @@ -757,23 +749,19 @@ int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, } else { index_offset = snd_ctl_get_ioff(kctl, &control->id); vd = &kctl->vd[index_offset]; - indirect = vd->access & SNDRV_CTL_ELEM_ACCESS_INDIRECT ? 1 : 0; - if (control->indirect != indirect) { - result = -EACCES; + if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_WRITE) || + kctl->put == NULL || + (file && vd->owner && vd->owner != file)) { + result = -EPERM; } else { - if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_WRITE) || - kctl->put == NULL || - (file && vd->owner != NULL && vd->owner != file)) { - result = -EPERM; - } else { - snd_ctl_build_ioff(&control->id, kctl, index_offset); - result = kctl->put(kctl, control); - } - if (result > 0) { - up_read(&card->controls_rwsem); - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &control->id); - return 0; - } + snd_ctl_build_ioff(&control->id, kctl, index_offset); + result = kctl->put(kctl, control); + } + if (result > 0) { + up_read(&card->controls_rwsem); + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, + &control->id); + return 0; } } up_read(&card->controls_rwsem); diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c index 9311ca3..6101259 100644 --- a/sound/core/control_compat.c +++ b/sound/core/control_compat.c @@ -219,7 +219,8 @@ static int copy_ctl_value_from_user(struct snd_card *card, struct snd_ctl_elem_value32 __user *data32, int *typep, int *countp) { - int i, type, count, size; + int i, type, size; + int uninitialized_var(count); unsigned int indirect; if (copy_from_user(&data->id, &data32->id, sizeof(data->id))) diff --git a/sound/core/device.c b/sound/core/device.c index ea1a062..202dac0 100644 --- a/sound/core/device.c +++ b/sound/core/device.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/slab.h> #include <linux/time.h> #include <linux/errno.h> diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c index bfd9d18..6d6589f 100644 --- a/sound/core/hwdep.c +++ b/sound/core/hwdep.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/major.h> #include <linux/init.h> #include <linux/slab.h> diff --git a/sound/core/info.c b/sound/core/info.c index 1ffd29b..9977ec2 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/time.h> #include <linux/smp_lock.h> diff --git a/sound/core/info_oss.c b/sound/core/info_oss.c index 435c939..e35789a 100644 --- a/sound/core/info_oss.c +++ b/sound/core/info_oss.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/slab.h> #include <linux/time.h> #include <linux/string.h> @@ -66,8 +65,6 @@ int snd_oss_info_register(int dev, int num, char *string) EXPORT_SYMBOL(snd_oss_info_register); -extern void snd_card_info_read_oss(struct snd_info_buffer *buffer); - static int snd_sndstat_show_strings(struct snd_info_buffer *buf, char *id, int dev) { int idx, ok = -1; diff --git a/sound/core/init.c b/sound/core/init.c index 2cb7099..e3338d6 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/sched.h> #include <linux/file.h> @@ -43,6 +42,40 @@ EXPORT_SYMBOL(snd_cards); static DEFINE_MUTEX(snd_card_mutex); +static char *slots[SNDRV_CARDS]; +module_param_array(slots, charp, NULL, 0444); +MODULE_PARM_DESC(slots, "Module names assigned to the slots."); + +/* return non-zero if the given index is already reserved for another + * module via slots option + */ +static int module_slot_mismatch(struct module *module, int idx) +{ +#ifdef MODULE + char *s1, *s2; + if (!module || !module->name || !slots[idx]) + return 0; + s1 = slots[idx]; + s2 = module->name; + /* compare module name strings + * hyphens are handled as equivalent with underscore + */ + for (;;) { + char c1 = *s1++; + char c2 = *s2++; + if (c1 == '-') + c1 = '_'; + if (c2 == '-') + c2 = '_'; + if (c1 != c2) + return 1; + if (!c1) + break; + } +#endif + return 0; +} + #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag); EXPORT_SYMBOL(snd_mixer_oss_notify_callback); @@ -115,6 +148,8 @@ struct snd_card *snd_card_new(int idx, const char *xid, for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) /* idx == -1 == 0xffff means: take any free slot */ if (~snd_cards_lock & idx & 1<<idx2) { + if (module_slot_mismatch(module, idx2)) + continue; idx = idx2; if (idx >= snd_ecards_limit) snd_ecards_limit = idx + 1; @@ -304,8 +339,8 @@ int snd_card_disconnect(struct snd_card *card) list_add(&mfile->shutdown_list, &shutdown_files); spin_unlock(&shutdown_lock); - fops_get(&snd_shutdown_f_ops); mfile->file->f_op = &snd_shutdown_f_ops; + fops_get(mfile->file->f_op); mfile = mfile->next; } diff --git a/sound/core/isadma.c b/sound/core/isadma.c index eb173ce..79f0f16 100644 --- a/sound/core/isadma.c +++ b/sound/core/isadma.c @@ -26,7 +26,6 @@ #undef HAVE_REALLY_SLOW_DMA_CONTROLLER -#include <sound/driver.h> #include <sound/core.h> #include <asm/dma.h> diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index 9b4992e..920e578 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -568,6 +568,7 @@ static ssize_t snd_mem_proc_write(struct file *file, const char __user * buffer, if (pci_set_dma_mask(pci, mask) < 0 || pci_set_consistent_dma_mask(pci, mask) < 0) { printk(KERN_ERR "snd-page-alloc: cannot set DMA mask %lx for pci %04x:%04x\n", mask, vendor, device); + pci_dev_put(pci); return count; } } diff --git a/sound/core/memory.c b/sound/core/memory.c index 25b0f05..1161158 100644 --- a/sound/core/memory.c +++ b/sound/core/memory.c @@ -20,9 +20,9 @@ * */ -#include <linux/module.h> #include <asm/io.h> #include <asm/uaccess.h> +#include <sound/core.h> /** * copy_to_user_fromio - copy data from mmio-space to user-space diff --git a/sound/core/misc.c b/sound/core/misc.c index 6cabab8..102d1c3 100644 --- a/sound/core/misc.c +++ b/sound/core/misc.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/time.h> #include <linux/ioport.h> diff --git a/sound/core/oss/copy.c b/sound/core/oss/copy.c index d6a04c2..9ded30d 100644 --- a/sound/core/oss/copy.c +++ b/sound/core/oss/copy.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <sound/core.h> #include <sound/pcm.h> diff --git a/sound/core/oss/io.c b/sound/core/oss/io.c index 3ece39f..f874f6c 100644 --- a/sound/core/oss/io.c +++ b/sound/core/oss/io.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <sound/core.h> #include <sound/pcm.h> diff --git a/sound/core/oss/linear.c b/sound/core/oss/linear.c index 06f96a3..da3dbd4 100644 --- a/sound/core/oss/linear.c +++ b/sound/core/oss/linear.c @@ -20,7 +20,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <sound/core.h> #include <sound/pcm.h> diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index c5a5ab9..75daed2 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/time.h> diff --git a/sound/core/oss/mulaw.c b/sound/core/oss/mulaw.c index 848db82..77f9619 100644 --- a/sound/core/oss/mulaw.c +++ b/sound/core/oss/mulaw.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <sound/core.h> #include <sound/pcm.h> diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index d0c4ceb..4c601b1 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -26,7 +26,6 @@ #define OSS_DEBUG #endif -#include <sound/driver.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/time.h> @@ -985,10 +984,8 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) sw_params->stop_threshold = runtime->buffer_size; sw_params->tstamp_mode = SNDRV_PCM_TSTAMP_NONE; sw_params->period_step = 1; - sw_params->sleep_min = 0; sw_params->avail_min = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : runtime->period_size; - sw_params->xfer_align = 1; if (atomic_read(&substream->mmap_count) || substream->oss.setup.nosilence) { sw_params->silence_threshold = 0; @@ -1624,6 +1621,7 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file) snd_pcm_format_set_silence(runtime->format, runtime->oss.buffer, size1); + size1 /= runtime->channels; /* frames */ fs = snd_enter_user(); snd_pcm_lib_write(substream, (void __user *)runtime->oss.buffer, size1); snd_leave_user(fs); diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c index 14095a9..bec9413 100644 --- a/sound/core/oss/pcm_plugin.c +++ b/sound/core/oss/pcm_plugin.c @@ -24,7 +24,6 @@ #define PLUGIN_DEBUG #endif -#include <sound/driver.h> #include <linux/slab.h> #include <linux/time.h> #include <linux/vmalloc.h> diff --git a/sound/core/oss/rate.c b/sound/core/oss/rate.c index 9eb2679..14dfb31 100644 --- a/sound/core/oss/rate.c +++ b/sound/core/oss/rate.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <sound/core.h> #include <sound/pcm.h> diff --git a/sound/core/oss/route.c b/sound/core/oss/route.c index de3ffde..da7ab7a 100644 --- a/sound/core/oss/route.c +++ b/sound/core/oss/route.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/slab.h> #include <linux/time.h> #include <sound/core.h> diff --git a/sound/core/pcm.c b/sound/core/pcm.c index cf9b949..9dd9bc7 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/time.h> @@ -228,7 +227,7 @@ static char *snd_pcm_subformat_names[] = { static char *snd_pcm_tstamp_mode_names[] = { TSTAMP(NONE), - TSTAMP(MMAP), + TSTAMP(ENABLE), }; static const char *snd_pcm_stream_name(int stream) @@ -359,7 +358,6 @@ static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry, snd_iprintf(buffer, "rate: %u (%u/%u)\n", runtime->rate, runtime->rate_num, runtime->rate_den); snd_iprintf(buffer, "period_size: %lu\n", runtime->period_size); snd_iprintf(buffer, "buffer_size: %lu\n", runtime->buffer_size); - snd_iprintf(buffer, "tick_time: %u\n", runtime->tick_time); #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) if (substream->oss.oss) { snd_iprintf(buffer, "OSS format: %s\n", snd_pcm_oss_format_name(runtime->oss.format)); @@ -387,9 +385,7 @@ static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry, } snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode)); snd_iprintf(buffer, "period_step: %u\n", runtime->period_step); - snd_iprintf(buffer, "sleep_min: %u\n", runtime->sleep_min); snd_iprintf(buffer, "avail_min: %lu\n", runtime->control->avail_min); - snd_iprintf(buffer, "xfer_align: %lu\n", runtime->xfer_align); snd_iprintf(buffer, "start_threshold: %lu\n", runtime->start_threshold); snd_iprintf(buffer, "stop_threshold: %lu\n", runtime->stop_threshold); snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold); @@ -765,12 +761,6 @@ static int snd_pcm_dev_free(struct snd_device *device) return snd_pcm_free(pcm); } -static void snd_pcm_tick_timer_func(unsigned long data) -{ - struct snd_pcm_substream *substream = (struct snd_pcm_substream *) data; - snd_pcm_tick_elapsed(substream); -} - int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, struct file *file, struct snd_pcm_substream **rsubstream) @@ -877,9 +867,6 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, memset((void*)runtime->control, 0, size); init_waitqueue_head(&runtime->sleep); - init_timer(&runtime->tick_timer); - runtime->tick_timer.function = snd_pcm_tick_timer_func; - runtime->tick_timer.data = (unsigned long) substream; runtime->status->state = SNDRV_PCM_STATE_OPEN; diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index 2b53979..49aa693 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -484,6 +484,7 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l case SNDRV_PCM_IOCTL_PVERSION: case SNDRV_PCM_IOCTL_INFO: case SNDRV_PCM_IOCTL_TSTAMP: + case SNDRV_PCM_IOCTL_TTSTAMP: case SNDRV_PCM_IOCTL_HWSYNC: case SNDRV_PCM_IOCTL_PREPARE: case SNDRV_PCM_IOCTL_RESET: diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 806f1fb..1533f03 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -20,7 +20,6 @@ * */ -#include <sound/driver.h> #include <linux/slab.h> #include <linux/time.h> #include <sound/core.h> @@ -145,11 +144,11 @@ static inline snd_pcm_uframes_t snd_pcm_update_hw_ptr_pos(struct snd_pcm_substre { snd_pcm_uframes_t pos; + if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) + snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); pos = substream->ops->pointer(substream); if (pos == SNDRV_PCM_POS_XRUN) return pos; /* XRUN */ - if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP) - getnstimeofday((struct timespec *)&runtime->status->tstamp); #ifdef CONFIG_SND_DEBUG if (pos >= runtime->buffer_size) { snd_printk(KERN_ERR "BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size); @@ -1139,7 +1138,7 @@ EXPORT_SYMBOL(snd_pcm_hw_constraint_step); static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { - static int pow2_sizes[] = { + static unsigned int pow2_sizes[] = { 1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7, 1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15, 1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23, @@ -1451,108 +1450,13 @@ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, EXPORT_SYMBOL(snd_pcm_lib_ioctl); -/* - * Conditions - */ - -static void snd_pcm_system_tick_set(struct snd_pcm_substream *substream, - unsigned long ticks) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - if (ticks == 0) - del_timer(&runtime->tick_timer); - else { - ticks += (1000000 / HZ) - 1; - ticks /= (1000000 / HZ); - mod_timer(&runtime->tick_timer, jiffies + ticks); - } -} - -/* Temporary alias */ -void snd_pcm_tick_set(struct snd_pcm_substream *substream, unsigned long ticks) -{ - snd_pcm_system_tick_set(substream, ticks); -} - -void snd_pcm_tick_prepare(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_uframes_t frames = ULONG_MAX; - snd_pcm_uframes_t avail, dist; - unsigned int ticks; - u_int64_t n; - u_int32_t r; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - if (runtime->silence_size >= runtime->boundary) { - frames = 1; - } else if (runtime->silence_size > 0 && - runtime->silence_filled < runtime->buffer_size) { - snd_pcm_sframes_t noise_dist; - noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled; - if (noise_dist > (snd_pcm_sframes_t)runtime->silence_threshold) - frames = noise_dist - runtime->silence_threshold; - } - avail = snd_pcm_playback_avail(runtime); - } else { - avail = snd_pcm_capture_avail(runtime); - } - if (avail < runtime->control->avail_min) { - snd_pcm_sframes_t n = runtime->control->avail_min - avail; - if (n > 0 && frames > (snd_pcm_uframes_t)n) - frames = n; - } - if (avail < runtime->buffer_size) { - snd_pcm_sframes_t n = runtime->buffer_size - avail; - if (n > 0 && frames > (snd_pcm_uframes_t)n) - frames = n; - } - if (frames == ULONG_MAX) { - snd_pcm_tick_set(substream, 0); - return; - } - dist = runtime->status->hw_ptr - runtime->hw_ptr_base; - /* Distance to next interrupt */ - dist = runtime->period_size - dist % runtime->period_size; - if (dist <= frames) { - snd_pcm_tick_set(substream, 0); - return; - } - /* the base time is us */ - n = frames; - n *= 1000000; - div64_32(&n, runtime->tick_time * runtime->rate, &r); - ticks = n + (r > 0 ? 1 : 0); - if (ticks < runtime->sleep_min) - ticks = runtime->sleep_min; - snd_pcm_tick_set(substream, (unsigned long) ticks); -} - -void snd_pcm_tick_elapsed(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime; - unsigned long flags; - - snd_assert(substream != NULL, return); - runtime = substream->runtime; - snd_assert(runtime != NULL, return); - - snd_pcm_stream_lock_irqsave(substream, flags); - if (!snd_pcm_running(substream) || - snd_pcm_update_hw_ptr(substream) < 0) - goto _end; - if (runtime->sleep_min) - snd_pcm_tick_prepare(substream); - _end: - snd_pcm_stream_unlock_irqrestore(substream, flags); -} - /** * snd_pcm_period_elapsed - update the pcm status for the next period * @substream: the pcm substream instance * * This function is called from the interrupt handler when the * PCM has processed the period size. It will update the current - * pointer, set up the tick, wake up sleepers, etc. + * pointer, wake up sleepers, etc. * * Even if more than one periods have elapsed since the last call, you * have to call this only once. @@ -1576,8 +1480,6 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream) if (substream->timer_running) snd_timer_interrupt(substream->timer, 1); - if (runtime->sleep_min) - snd_pcm_tick_prepare(substream); _end: snd_pcm_stream_unlock_irqrestore(substream, flags); if (runtime->transfer_ack_end) @@ -1587,6 +1489,71 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream) EXPORT_SYMBOL(snd_pcm_period_elapsed); +/* + * Wait until avail_min data becomes available + * Returns a negative error code if any error occurs during operation. + * The available space is stored on availp. When err = 0 and avail = 0 + * on the capture stream, it indicates the stream is in DRAINING state. + */ +static int wait_for_avail_min(struct snd_pcm_substream *substream, + snd_pcm_uframes_t *availp) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + wait_queue_t wait; + int err = 0; + snd_pcm_uframes_t avail = 0; + long tout; + + init_waitqueue_entry(&wait, current); + add_wait_queue(&runtime->sleep, &wait); + for (;;) { + if (signal_pending(current)) { + err = -ERESTARTSYS; + break; + } + set_current_state(TASK_INTERRUPTIBLE); + snd_pcm_stream_unlock_irq(substream); + tout = schedule_timeout(msecs_to_jiffies(10000)); + snd_pcm_stream_lock_irq(substream); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_SUSPENDED: + err = -ESTRPIPE; + goto _endloop; + case SNDRV_PCM_STATE_XRUN: + err = -EPIPE; + goto _endloop; + case SNDRV_PCM_STATE_DRAINING: + if (is_playback) + err = -EPIPE; + else + avail = 0; /* indicate draining */ + goto _endloop; + case SNDRV_PCM_STATE_OPEN: + case SNDRV_PCM_STATE_SETUP: + case SNDRV_PCM_STATE_DISCONNECTED: + err = -EBADFD; + goto _endloop; + } + if (!tout) { + snd_printd("%s write error (DMA or IRQ trouble?)\n", + is_playback ? "playback" : "capture"); + err = -EIO; + break; + } + if (is_playback) + avail = snd_pcm_playback_avail(runtime); + else + avail = snd_pcm_capture_avail(runtime); + if (avail >= runtime->control->avail_min) + break; + } + _endloop: + remove_wait_queue(&runtime->sleep, &wait); + *availp = avail; + return err; +} + static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream, unsigned int hwoff, unsigned long data, unsigned int off, @@ -1624,8 +1591,6 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, if (size == 0) return 0; - if (size > runtime->xfer_align) - size -= size % runtime->xfer_align; snd_pcm_stream_lock_irq(substream); switch (runtime->status->state) { @@ -1648,84 +1613,18 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, snd_pcm_uframes_t frames, appl_ptr, appl_ofs; snd_pcm_uframes_t avail; snd_pcm_uframes_t cont; - if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING) + if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) snd_pcm_update_hw_ptr(substream); avail = snd_pcm_playback_avail(runtime); - if (((avail < runtime->control->avail_min && size > avail) || - (size >= runtime->xfer_align && avail < runtime->xfer_align))) { - wait_queue_t wait; - enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED, DROPPED } state; - long tout; - + if (!avail) { if (nonblock) { err = -EAGAIN; goto _end_unlock; } - - init_waitqueue_entry(&wait, current); - add_wait_queue(&runtime->sleep, &wait); - while (1) { - if (signal_pending(current)) { - state = SIGNALED; - break; - } - set_current_state(TASK_INTERRUPTIBLE); - snd_pcm_stream_unlock_irq(substream); - tout = schedule_timeout(10 * HZ); - snd_pcm_stream_lock_irq(substream); - if (tout == 0) { - if (runtime->status->state != SNDRV_PCM_STATE_PREPARED && - runtime->status->state != SNDRV_PCM_STATE_PAUSED) { - state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED; - break; - } - } - switch (runtime->status->state) { - case SNDRV_PCM_STATE_XRUN: - case SNDRV_PCM_STATE_DRAINING: - state = ERROR; - goto _end_loop; - case SNDRV_PCM_STATE_SUSPENDED: - state = SUSPENDED; - goto _end_loop; - case SNDRV_PCM_STATE_SETUP: - state = DROPPED; - goto _end_loop; - default: - break; - } - avail = snd_pcm_playback_avail(runtime); - if (avail >= runtime->control->avail_min) { - state = READY; - break; - } - } - _end_loop: - remove_wait_queue(&runtime->sleep, &wait); - - switch (state) { - case ERROR: - err = -EPIPE; - goto _end_unlock; - case SUSPENDED: - err = -ESTRPIPE; - goto _end_unlock; - case SIGNALED: - err = -ERESTARTSYS; - goto _end_unlock; - case EXPIRED: - snd_printd("playback write error (DMA or IRQ trouble?)\n"); - err = -EIO; - goto _end_unlock; - case DROPPED: - err = -EBADFD; + err = wait_for_avail_min(substream, &avail); + if (err < 0) goto _end_unlock; - default: - break; - } } - if (avail > runtime->xfer_align) - avail -= avail % runtime->xfer_align; frames = size > avail ? avail : size; cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; if (frames > cont) @@ -1763,9 +1662,6 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, if (err < 0) goto _end_unlock; } - if (runtime->sleep_min && - runtime->status->state == SNDRV_PCM_STATE_RUNNING) - snd_pcm_tick_prepare(substream); } _end_unlock: snd_pcm_stream_unlock_irq(substream); @@ -1893,8 +1789,6 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, if (size == 0) return 0; - if (size > runtime->xfer_align) - size -= size % runtime->xfer_align; snd_pcm_stream_lock_irq(substream); switch (runtime->status->state) { @@ -1924,91 +1818,25 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, snd_pcm_uframes_t frames, appl_ptr, appl_ofs; snd_pcm_uframes_t avail; snd_pcm_uframes_t cont; - if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING) + if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) snd_pcm_update_hw_ptr(substream); - __draining: avail = snd_pcm_capture_avail(runtime); - if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { - if (avail < runtime->xfer_align) { - err = -EPIPE; + if (!avail) { + if (runtime->status->state == + SNDRV_PCM_STATE_DRAINING) { + snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); goto _end_unlock; } - } else if ((avail < runtime->control->avail_min && size > avail) || - (size >= runtime->xfer_align && avail < runtime->xfer_align)) { - wait_queue_t wait; - enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED, DROPPED } state; - long tout; - if (nonblock) { err = -EAGAIN; goto _end_unlock; } - - init_waitqueue_entry(&wait, current); - add_wait_queue(&runtime->sleep, &wait); - while (1) { - if (signal_pending(current)) { - state = SIGNALED; - break; - } - set_current_state(TASK_INTERRUPTIBLE); - snd_pcm_stream_unlock_irq(substream); - tout = schedule_timeout(10 * HZ); - snd_pcm_stream_lock_irq(substream); - if (tout == 0) { - if (runtime->status->state != SNDRV_PCM_STATE_PREPARED && - runtime->status->state != SNDRV_PCM_STATE_PAUSED) { - state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED; - break; - } - } - switch (runtime->status->state) { - case SNDRV_PCM_STATE_XRUN: - state = ERROR; - goto _end_loop; - case SNDRV_PCM_STATE_SUSPENDED: - state = SUSPENDED; - goto _end_loop; - case SNDRV_PCM_STATE_DRAINING: - goto __draining; - case SNDRV_PCM_STATE_SETUP: - state = DROPPED; - goto _end_loop; - default: - break; - } - avail = snd_pcm_capture_avail(runtime); - if (avail >= runtime->control->avail_min) { - state = READY; - break; - } - } - _end_loop: - remove_wait_queue(&runtime->sleep, &wait); - - switch (state) { - case ERROR: - err = -EPIPE; - goto _end_unlock; - case SUSPENDED: - err = -ESTRPIPE; - goto _end_unlock; - case SIGNALED: - err = -ERESTARTSYS; - goto _end_unlock; - case EXPIRED: - snd_printd("capture read error (DMA or IRQ trouble?)\n"); - err = -EIO; - goto _end_unlock; - case DROPPED: - err = -EBADFD; + err = wait_for_avail_min(substream, &avail); + if (err < 0) goto _end_unlock; - default: - break; - } + if (!avail) + continue; /* draining */ } - if (avail > runtime->xfer_align) - avail -= avail % runtime->xfer_align; frames = size > avail ? avail : size; cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; if (frames > cont) @@ -2040,9 +1868,6 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, offset += frames; size -= frames; xfer += frames; - if (runtime->sleep_min && - runtime->status->state == SNDRV_PCM_STATE_RUNNING) - snd_pcm_tick_prepare(substream); } _end_unlock: snd_pcm_stream_unlock_irq(substream); diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index a13e38c..ff07b4a 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/time.h> #include <linux/init.h> diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index dd9aa51..89b7f54 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <sound/core.h> #include <sound/pcm.h> @@ -75,7 +74,7 @@ static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = { }, [SNDRV_PCM_FORMAT_U24_BE] = { .width = 24, .phys = 32, .le = 0, .signd = 0, - .silence = { 0x80, 0x00, 0x00 }, + .silence = { 0x00, 0x80, 0x00, 0x00 }, }, [SNDRV_PCM_FORMAT_S32_LE] = { .width = 32, .phys = 32, .le = 1, .signd = 1, diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index fb3dde4..6244911 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/mm.h> #include <linux/file.h> #include <linux/slab.h> @@ -413,7 +412,6 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, runtime->period_size = params_period_size(params); runtime->periods = params_periods(params); runtime->buffer_size = params_buffer_size(params); - runtime->tick_time = params_tick_time(params); runtime->info = params->info; runtime->rate_num = params->rate_num; runtime->rate_den = params->rate_den; @@ -433,9 +431,7 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, /* Default sw params */ runtime->tstamp_mode = SNDRV_PCM_TSTAMP_NONE; runtime->period_step = 1; - runtime->sleep_min = 0; runtime->control->avail_min = runtime->period_size; - runtime->xfer_align = runtime->period_size; runtime->start_threshold = 1; runtime->stop_threshold = runtime->buffer_size; runtime->silence_threshold = 0; @@ -532,9 +528,6 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream, return -EINVAL; if (params->avail_min == 0) return -EINVAL; - if (params->xfer_align == 0 || - params->xfer_align % runtime->min_align != 0) - return -EINVAL; if (params->silence_size >= runtime->boundary) { if (params->silence_threshold != 0) return -EINVAL; @@ -546,20 +539,14 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream, } snd_pcm_stream_lock_irq(substream); runtime->tstamp_mode = params->tstamp_mode; - runtime->sleep_min = params->sleep_min; runtime->period_step = params->period_step; runtime->control->avail_min = params->avail_min; runtime->start_threshold = params->start_threshold; runtime->stop_threshold = params->stop_threshold; runtime->silence_threshold = params->silence_threshold; runtime->silence_size = params->silence_size; - runtime->xfer_align = params->xfer_align; params->boundary = runtime->boundary; if (snd_pcm_running(substream)) { - if (runtime->sleep_min) - snd_pcm_tick_prepare(substream); - else - snd_pcm_tick_set(substream, 0); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) snd_pcm_playback_silence(substream, ULONG_MAX); @@ -595,12 +582,13 @@ int snd_pcm_status(struct snd_pcm_substream *substream, status->trigger_tstamp = runtime->trigger_tstamp; if (snd_pcm_running(substream)) { snd_pcm_update_hw_ptr(substream); - if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP) + if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { status->tstamp = runtime->status->tstamp; - else - getnstimeofday(&status->tstamp); - } else - getnstimeofday(&status->tstamp); + goto _tstamp_end; + } + } + snd_pcm_gettime(runtime, &status->tstamp); + _tstamp_end: status->appl_ptr = runtime->control->appl_ptr; status->hw_ptr = runtime->status->hw_ptr; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { @@ -688,7 +676,7 @@ static void snd_pcm_trigger_tstamp(struct snd_pcm_substream *substream) if (runtime->trigger_master == NULL) return; if (runtime->trigger_master == substream) { - getnstimeofday(&runtime->trigger_tstamp); + snd_pcm_gettime(runtime, &runtime->trigger_tstamp); } else { snd_pcm_trigger_tstamp(runtime->trigger_master); runtime->trigger_tstamp = runtime->trigger_master->runtime->trigger_tstamp; @@ -875,8 +863,6 @@ static void snd_pcm_post_start(struct snd_pcm_substream *substream, int state) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) snd_pcm_playback_silence(substream, ULONG_MAX); - if (runtime->sleep_min) - snd_pcm_tick_prepare(substream); if (substream->timer) snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTART, &runtime->trigger_tstamp); @@ -930,7 +916,6 @@ static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state) snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP, &runtime->trigger_tstamp); runtime->status->state = state; - snd_pcm_tick_set(substream, 0); } wake_up(&runtime->sleep); } @@ -1014,12 +999,9 @@ static void snd_pcm_post_pause(struct snd_pcm_substream *substream, int push) snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MPAUSE, &runtime->trigger_tstamp); - snd_pcm_tick_set(substream, 0); wake_up(&runtime->sleep); } else { runtime->status->state = SNDRV_PCM_STATE_RUNNING; - if (runtime->sleep_min) - snd_pcm_tick_prepare(substream); if (substream->timer) snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MCONTINUE, @@ -1074,7 +1056,6 @@ static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state) &runtime->trigger_tstamp); runtime->status->suspended_state = runtime->status->state; runtime->status->state = SNDRV_PCM_STATE_SUSPENDED; - snd_pcm_tick_set(substream, 0); wake_up(&runtime->sleep); } @@ -1177,8 +1158,6 @@ static void snd_pcm_post_resume(struct snd_pcm_substream *substream, int state) snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MRESUME, &runtime->trigger_tstamp); runtime->status->state = runtime->status->suspended_state; - if (runtime->sleep_min) - snd_pcm_tick_prepare(substream); } static struct action_ops snd_pcm_action_resume = { @@ -1395,10 +1374,10 @@ static int snd_pcm_do_drain_init(struct snd_pcm_substream *substream, int state) } else { /* stop running stream */ if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) { - int state = snd_pcm_capture_avail(runtime) > 0 ? + int new_state = snd_pcm_capture_avail(runtime) > 0 ? SNDRV_PCM_STATE_DRAINING : SNDRV_PCM_STATE_SETUP; - snd_pcm_do_stop(substream, state); - snd_pcm_post_stop(substream, state); + snd_pcm_do_stop(substream, new_state); + snd_pcm_post_stop(substream, new_state); } } return 0; @@ -2007,8 +1986,6 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream) } /* FIXME: this belong to lowlevel */ - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_TICK_TIME, - 1000000 / HZ, 1000000 / HZ); snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); return 0; @@ -2244,15 +2221,10 @@ static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *subst } if (frames > (snd_pcm_uframes_t)hw_avail) frames = hw_avail; - else - frames -= frames % runtime->xfer_align; appl_ptr = runtime->control->appl_ptr - frames; if (appl_ptr < 0) appl_ptr += runtime->boundary; runtime->control->appl_ptr = appl_ptr; - if (runtime->status->state == SNDRV_PCM_STATE_RUNNING && - runtime->sleep_min) - snd_pcm_tick_prepare(substream); ret = frames; __end: snd_pcm_stream_unlock_irq(substream); @@ -2294,15 +2266,10 @@ static snd_pcm_sframes_t snd_pcm_capture_rewind(struct snd_pcm_substream *substr } if (frames > (snd_pcm_uframes_t)hw_avail) frames = hw_avail; - else - frames -= frames % runtime->xfer_align; appl_ptr = runtime->control->appl_ptr - frames; if (appl_ptr < 0) appl_ptr += runtime->boundary; runtime->control->appl_ptr = appl_ptr; - if (runtime->status->state == SNDRV_PCM_STATE_RUNNING && - runtime->sleep_min) - snd_pcm_tick_prepare(substream); ret = frames; __end: snd_pcm_stream_unlock_irq(substream); @@ -2345,15 +2312,10 @@ static snd_pcm_sframes_t snd_pcm_playback_forward(struct snd_pcm_substream *subs } if (frames > (snd_pcm_uframes_t)avail) frames = avail; - else - frames -= frames % runtime->xfer_align; appl_ptr = runtime->control->appl_ptr + frames; if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary) appl_ptr -= runtime->boundary; runtime->control->appl_ptr = appl_ptr; - if (runtime->status->state == SNDRV_PCM_STATE_RUNNING && - runtime->sleep_min) - snd_pcm_tick_prepare(substream); ret = frames; __end: snd_pcm_stream_unlock_irq(substream); @@ -2396,15 +2358,10 @@ static snd_pcm_sframes_t snd_pcm_capture_forward(struct snd_pcm_substream *subst } if (frames > (snd_pcm_uframes_t)avail) frames = avail; - else - frames -= frames % runtime->xfer_align; appl_ptr = runtime->control->appl_ptr + frames; if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary) appl_ptr -= runtime->boundary; runtime->control->appl_ptr = appl_ptr; - if (runtime->status->state == SNDRV_PCM_STATE_RUNNING && - runtime->sleep_min) - snd_pcm_tick_prepare(substream); ret = frames; __end: snd_pcm_stream_unlock_irq(substream); @@ -2519,6 +2476,21 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream, return -EFAULT; return 0; } + +static int snd_pcm_tstamp(struct snd_pcm_substream *substream, int __user *_arg) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int arg; + + if (get_user(arg, _arg)) + return -EFAULT; + if (arg < 0 || arg > SNDRV_PCM_TSTAMP_TYPE_LAST) + return -EINVAL; + runtime->tstamp_type = SNDRV_PCM_TSTAMP_TYPE_GETTIMEOFDAY; + if (arg == SNDRV_PCM_TSTAMP_TYPE_MONOTONIC) + runtime->tstamp_type = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC; + return 0; +} static int snd_pcm_common_ioctl1(struct file *file, struct snd_pcm_substream *substream, @@ -2531,8 +2503,10 @@ static int snd_pcm_common_ioctl1(struct file *file, return put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0; case SNDRV_PCM_IOCTL_INFO: return snd_pcm_info_user(substream, arg); - case SNDRV_PCM_IOCTL_TSTAMP: /* just for compatibility */ + case SNDRV_PCM_IOCTL_TSTAMP: /* just for compatibility */ return 0; + case SNDRV_PCM_IOCTL_TTSTAMP: + return snd_pcm_tstamp(substream, arg); case SNDRV_PCM_IOCTL_HW_REFINE: return snd_pcm_hw_refine_user(substream, arg); case SNDRV_PCM_IOCTL_HW_PARAMS: @@ -3018,26 +2992,23 @@ static unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait) /* * mmap status record */ -static struct page * snd_pcm_mmap_status_nopage(struct vm_area_struct *area, - unsigned long address, int *type) +static int snd_pcm_mmap_status_fault(struct vm_area_struct *area, + struct vm_fault *vmf) { struct snd_pcm_substream *substream = area->vm_private_data; struct snd_pcm_runtime *runtime; - struct page * page; if (substream == NULL) - return NOPAGE_SIGBUS; + return VM_FAULT_SIGBUS; runtime = substream->runtime; - page = virt_to_page(runtime->status); - get_page(page); - if (type) - *type = VM_FAULT_MINOR; - return page; + vmf->page = virt_to_page(runtime->status); + get_page(vmf->page); + return 0; } static struct vm_operations_struct snd_pcm_vm_ops_status = { - .nopage = snd_pcm_mmap_status_nopage, + .fault = snd_pcm_mmap_status_fault, }; static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file *file, @@ -3061,26 +3032,23 @@ static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file /* * mmap control record */ -static struct page * snd_pcm_mmap_control_nopage(struct vm_area_struct *area, - unsigned long address, int *type) +static int snd_pcm_mmap_control_fault(struct vm_area_struct *area, + struct vm_fault *vmf) { struct snd_pcm_substream *substream = area->vm_private_data; struct snd_pcm_runtime *runtime; - struct page * page; if (substream == NULL) - return NOPAGE_SIGBUS; + return VM_FAULT_SIGBUS; runtime = substream->runtime; - page = virt_to_page(runtime->control); - get_page(page); - if (type) - *type = VM_FAULT_MINOR; - return page; + vmf->page = virt_to_page(runtime->control); + get_page(vmf->page); + return 0; } static struct vm_operations_struct snd_pcm_vm_ops_control = { - .nopage = snd_pcm_mmap_control_nopage, + .fault = snd_pcm_mmap_control_fault, }; static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file *file, @@ -3117,10 +3085,10 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file #endif /* coherent mmap */ /* - * nopage callback for mmapping a RAM page + * fault callback for mmapping a RAM page */ -static struct page *snd_pcm_mmap_data_nopage(struct vm_area_struct *area, - unsigned long address, int *type) +static int snd_pcm_mmap_data_fault(struct vm_area_struct *area, + struct vm_fault *vmf) { struct snd_pcm_substream *substream = area->vm_private_data; struct snd_pcm_runtime *runtime; @@ -3130,33 +3098,30 @@ static struct page *snd_pcm_mmap_data_nopage(struct vm_area_struct *area, size_t dma_bytes; if (substream == NULL) - return NOPAGE_SIGBUS; + return VM_FAULT_SIGBUS; runtime = substream->runtime; - offset = area->vm_pgoff << PAGE_SHIFT; - offset += address - area->vm_start; - snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_SIGBUS); + offset = vmf->pgoff << PAGE_SHIFT; dma_bytes = PAGE_ALIGN(runtime->dma_bytes); if (offset > dma_bytes - PAGE_SIZE) - return NOPAGE_SIGBUS; + return VM_FAULT_SIGBUS; if (substream->ops->page) { page = substream->ops->page(substream, offset); - if (! page) - return NOPAGE_OOM; /* XXX: is this really due to OOM? */ + if (!page) + return VM_FAULT_SIGBUS; } else { vaddr = runtime->dma_area + offset; page = virt_to_page(vaddr); } get_page(page); - if (type) - *type = VM_FAULT_MINOR; - return page; + vmf->page = page; + return 0; } static struct vm_operations_struct snd_pcm_vm_ops_data = { .open = snd_pcm_mmap_data_open, .close = snd_pcm_mmap_data_close, - .nopage = snd_pcm_mmap_data_nopage, + .fault = snd_pcm_mmap_data_fault, }; /* diff --git a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c index 23aa9a2..033a024 100644 --- a/sound/core/pcm_timer.c +++ b/sound/core/pcm_timer.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <sound/core.h> #include <sound/pcm.h> diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index b8e700b..f7ea728 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <sound/core.h> #include <linux/major.h> #include <linux/init.h> @@ -912,7 +911,8 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream, } static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream, - unsigned char *buf, long count, int kernel) + unsigned char __user *userbuf, + unsigned char *kernelbuf, long count) { unsigned long flags; long result = 0, count1; @@ -925,11 +925,11 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream, spin_lock_irqsave(&runtime->lock, flags); if (count1 > (int)runtime->avail) count1 = runtime->avail; - if (kernel) { - memcpy(buf + result, runtime->buffer + runtime->appl_ptr, count1); - } else { + if (kernelbuf) + memcpy(kernelbuf + result, runtime->buffer + runtime->appl_ptr, count1); + if (userbuf) { spin_unlock_irqrestore(&runtime->lock, flags); - if (copy_to_user((char __user *)buf + result, + if (copy_to_user(userbuf + result, runtime->buffer + runtime->appl_ptr, count1)) { return result > 0 ? result : -EFAULT; } @@ -949,7 +949,7 @@ long snd_rawmidi_kernel_read(struct snd_rawmidi_substream *substream, unsigned char *buf, long count) { snd_rawmidi_input_trigger(substream, 1); - return snd_rawmidi_kernel_read1(substream, buf, count, 1); + return snd_rawmidi_kernel_read1(substream, NULL/*userbuf*/, buf, count); } static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t count, @@ -990,8 +990,9 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun } spin_unlock_irq(&runtime->lock); count1 = snd_rawmidi_kernel_read1(substream, - (unsigned char __force *)buf, - count, 0); + (unsigned char __user *)buf, + NULL/*kernelbuf*/, + count); if (count1 < 0) return result > 0 ? result : count1; result += count1; @@ -1132,13 +1133,15 @@ int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream, } static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, - const unsigned char *buf, long count, int kernel) + const unsigned char __user *userbuf, + const unsigned char *kernelbuf, + long count) { unsigned long flags; long count1, result; struct snd_rawmidi_runtime *runtime = substream->runtime; - snd_assert(buf != NULL, return -EINVAL); + snd_assert(kernelbuf != NULL || userbuf != NULL, return -EINVAL); snd_assert(runtime->buffer != NULL, return -EINVAL); result = 0; @@ -1155,12 +1158,13 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, count1 = count; if (count1 > (long)runtime->avail) count1 = runtime->avail; - if (kernel) { - memcpy(runtime->buffer + runtime->appl_ptr, buf, count1); - } else { + if (kernelbuf) + memcpy(runtime->buffer + runtime->appl_ptr, + kernelbuf + result, count1); + else if (userbuf) { spin_unlock_irqrestore(&runtime->lock, flags); if (copy_from_user(runtime->buffer + runtime->appl_ptr, - (char __user *)buf, count1)) { + userbuf + result, count1)) { spin_lock_irqsave(&runtime->lock, flags); result = result > 0 ? result : -EFAULT; goto __end; @@ -1171,7 +1175,6 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, runtime->appl_ptr %= runtime->buffer_size; runtime->avail -= count1; result += count1; - buf += count1; count -= count1; } __end: @@ -1185,7 +1188,7 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, long snd_rawmidi_kernel_write(struct snd_rawmidi_substream *substream, const unsigned char *buf, long count) { - return snd_rawmidi_kernel_write1(substream, buf, count, 1); + return snd_rawmidi_kernel_write1(substream, NULL, buf, count); } static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf, @@ -1225,9 +1228,7 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf, spin_lock_irq(&runtime->lock); } spin_unlock_irq(&runtime->lock); - count1 = snd_rawmidi_kernel_write1(substream, - (unsigned char __force *)buf, - count, 0); + count1 = snd_rawmidi_kernel_write1(substream, buf, NULL, count); if (count1 < 0) return result > 0 ? result : count1; result += count1; diff --git a/sound/core/rtctimer.c b/sound/core/rtctimer.c index 7cd5e8f..97b30fb 100644 --- a/sound/core/rtctimer.c +++ b/sound/core/rtctimer.c @@ -20,7 +20,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/moduleparam.h> diff --git a/sound/core/seq/Makefile b/sound/core/seq/Makefile index ceef14a..0695937 100644 --- a/sound/core/seq/Makefile +++ b/sound/core/seq/Makefile @@ -3,7 +3,6 @@ # Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz> # -obj-$(CONFIG_SND) += instr/ ifeq ($(CONFIG_SND_SEQUENCER_OSS),y) obj-$(CONFIG_SND_SEQUENCER) += oss/ endif @@ -15,7 +14,6 @@ snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \ snd-seq-midi-objs := seq_midi.o snd-seq-midi-emul-objs := seq_midi_emul.o snd-seq-midi-event-objs := seq_midi_event.o -snd-seq-instr-objs := seq_instr.o snd-seq-dummy-objs := seq_dummy.o snd-seq-virmidi-objs := seq_virmidi.o @@ -36,9 +34,7 @@ obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o # Toplevel Module Dependency obj-$(CONFIG_SND_VIRMIDI) += snd-seq-virmidi.o snd-seq-midi-event.o obj-$(call sequencer,$(CONFIG_SND_RAWMIDI)) += snd-seq-midi.o snd-seq-midi-event.o -obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o -obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o -obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-seq-midi-emul.o snd-seq-instr.o +obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o +obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-seq-midi-emul.o snd-seq-virmidi.o obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-seq-midi-emul.o snd-seq-virmidi.o -obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-seq-midi-emul.o snd-seq-instr.o diff --git a/sound/core/seq/instr/Makefile b/sound/core/seq/instr/Makefile deleted file mode 100644 index 6089603..0000000 --- a/sound/core/seq/instr/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -# -# Makefile for ALSA -# Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz> -# - -snd-ainstr-fm-objs := ainstr_fm.o -snd-ainstr-simple-objs := ainstr_simple.o -snd-ainstr-gf1-objs := ainstr_gf1.o -snd-ainstr-iw-objs := ainstr_iw.o - -# -# this function returns: -# "m" - CONFIG_SND_SEQUENCER is m -# <empty string> - CONFIG_SND_SEQUENCER is undefined -# otherwise parameter #1 value -# -sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1))) - -# Toplevel Module Dependency -obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-ainstr-fm.o -obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-ainstr-fm.o -obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-ainstr-gf1.o snd-ainstr-simple.o snd-ainstr-iw.o -obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-ainstr-simple.o diff --git a/sound/core/seq/instr/ainstr_fm.c b/sound/core/seq/instr/ainstr_fm.c deleted file mode 100644 index f80fab8..0000000 --- a/sound/core/seq/instr/ainstr_fm.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * FM (OPL2/3) Instrument routines - * Copyright (c) 2000 Uros Bizjak <uros@kss-loka.si> - * - * 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 <linux/init.h> -#include <sound/core.h> -#include <sound/ainstr_fm.h> -#include <sound/initval.h> -#include <asm/uaccess.h> - -MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>"); -MODULE_DESCRIPTION("Advanced Linux Sound Architecture FM Instrument support."); -MODULE_LICENSE("GPL"); - -static int snd_seq_fm_put(void *private_data, struct snd_seq_kinstr *instr, - char __user *instr_data, long len, int atomic, int cmd) -{ - struct fm_instrument *ip; - struct fm_xinstrument ix; - int idx; - - if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE) - return -EINVAL; - /* copy instrument data */ - if (len < (long)sizeof(ix)) - return -EINVAL; - if (copy_from_user(&ix, instr_data, sizeof(ix))) - return -EFAULT; - if (ix.stype != FM_STRU_INSTR) - return -EINVAL; - ip = (struct fm_instrument *)KINSTR_DATA(instr); - ip->share_id[0] = le32_to_cpu(ix.share_id[0]); - ip->share_id[1] = le32_to_cpu(ix.share_id[1]); - ip->share_id[2] = le32_to_cpu(ix.share_id[2]); - ip->share_id[3] = le32_to_cpu(ix.share_id[3]); - ip->type = ix.type; - for (idx = 0; idx < 4; idx++) { - ip->op[idx].am_vib = ix.op[idx].am_vib; - ip->op[idx].ksl_level = ix.op[idx].ksl_level; - ip->op[idx].attack_decay = ix.op[idx].attack_decay; - ip->op[idx].sustain_release = ix.op[idx].sustain_release; - ip->op[idx].wave_select = ix.op[idx].wave_select; - } - for (idx = 0; idx < 2; idx++) { - ip->feedback_connection[idx] = ix.feedback_connection[idx]; - } - ip->echo_delay = ix.echo_delay; - ip->echo_atten = ix.echo_atten; - ip->chorus_spread = ix.chorus_spread; - ip->trnsps = ix.trnsps; - ip->fix_dur = ix.fix_dur; - ip->modes = ix.modes; - ip->fix_key = ix.fix_key; - return 0; -} - -static int snd_seq_fm_get(void *private_data, struct snd_seq_kinstr *instr, - char __user *instr_data, long len, int atomic, - int cmd) -{ - struct fm_instrument *ip; - struct fm_xinstrument ix; - int idx; - - if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL) - return -EINVAL; - if (len < (long)sizeof(ix)) - return -ENOMEM; - memset(&ix, 0, sizeof(ix)); - ip = (struct fm_instrument *)KINSTR_DATA(instr); - ix.stype = FM_STRU_INSTR; - ix.share_id[0] = cpu_to_le32(ip->share_id[0]); - ix.share_id[1] = cpu_to_le32(ip->share_id[1]); - ix.share_id[2] = cpu_to_le32(ip->share_id[2]); - ix.share_id[3] = cpu_to_le32(ip->share_id[3]); - ix.type = ip->type; - for (idx = 0; idx < 4; idx++) { - ix.op[idx].am_vib = ip->op[idx].am_vib; - ix.op[idx].ksl_level = ip->op[idx].ksl_level; - ix.op[idx].attack_decay = ip->op[idx].attack_decay; - ix.op[idx].sustain_release = ip->op[idx].sustain_release; - ix.op[idx].wave_select = ip->op[idx].wave_select; - } - for (idx = 0; idx < 2; idx++) { - ix.feedback_connection[idx] = ip->feedback_connection[idx]; - } - if (copy_to_user(instr_data, &ix, sizeof(ix))) - return -EFAULT; - ix.echo_delay = ip->echo_delay; - ix.echo_atten = ip->echo_atten; - ix.chorus_spread = ip->chorus_spread; - ix.trnsps = ip->trnsps; - ix.fix_dur = ip->fix_dur; - ix.modes = ip->modes; - ix.fix_key = ip->fix_key; - return 0; -} - -static int snd_seq_fm_get_size(void *private_data, struct snd_seq_kinstr *instr, - long *size) -{ - *size = sizeof(struct fm_xinstrument); - return 0; -} - -int snd_seq_fm_init(struct snd_seq_kinstr_ops *ops, - struct snd_seq_kinstr_ops *next) -{ - memset(ops, 0, sizeof(*ops)); - // ops->private_data = private_data; - ops->add_len = sizeof(struct fm_instrument); - ops->instr_type = SNDRV_SEQ_INSTR_ID_OPL2_3; - ops->put = snd_seq_fm_put; - ops->get = snd_seq_fm_get; - ops->get_size = snd_seq_fm_get_size; - // ops->remove = snd_seq_fm_remove; - // ops->notify = snd_seq_fm_notify; - ops->next = next; - return 0; -} - -/* - * Init part - */ - -static int __init alsa_ainstr_fm_init(void) -{ - return 0; -} - -static void __exit alsa_ainstr_fm_exit(void) -{ -} - -module_init(alsa_ainstr_fm_init) -module_exit(alsa_ainstr_fm_exit) - -EXPORT_SYMBOL(snd_seq_fm_init); diff --git a/sound/core/seq/instr/ainstr_gf1.c b/sound/core/seq/instr/ainstr_gf1.c deleted file mode 100644 index 4940026..0000000 --- a/sound/core/seq/instr/ainstr_gf1.c +++ /dev/null @@ -1,359 +0,0 @@ -/* - * GF1 (GUS) Patch - Instrument routines - * Copyright (c) 1999 by Jaroslav Kysela <perex@perex.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 <linux/init.h> -#include <linux/slab.h> -#include <sound/core.h> -#include <sound/ainstr_gf1.h> -#include <sound/initval.h> -#include <asm/uaccess.h> - -MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); -MODULE_DESCRIPTION("Advanced Linux Sound Architecture GF1 (GUS) Patch support."); -MODULE_LICENSE("GPL"); - -static unsigned int snd_seq_gf1_size(unsigned int size, unsigned int format) -{ - unsigned int result = size; - - if (format & GF1_WAVE_16BIT) - result <<= 1; - if (format & GF1_WAVE_STEREO) - result <<= 1; - return format; -} - -static int snd_seq_gf1_copy_wave_from_stream(struct snd_gf1_ops *ops, - struct gf1_instrument *ip, - char __user **data, - long *len, - int atomic) -{ - struct gf1_wave *wp, *prev; - struct gf1_xwave xp; - int err; - gfp_t gfp_mask; - unsigned int real_size; - - gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; - if (*len < (long)sizeof(xp)) - return -EINVAL; - if (copy_from_user(&xp, *data, sizeof(xp))) - return -EFAULT; - *data += sizeof(xp); - *len -= sizeof(xp); - wp = kzalloc(sizeof(*wp), gfp_mask); - if (wp == NULL) - return -ENOMEM; - wp->share_id[0] = le32_to_cpu(xp.share_id[0]); - wp->share_id[1] = le32_to_cpu(xp.share_id[1]); - wp->share_id[2] = le32_to_cpu(xp.share_id[2]); - wp->share_id[3] = le32_to_cpu(xp.share_id[3]); - wp->format = le32_to_cpu(xp.format); - wp->size = le32_to_cpu(xp.size); - wp->start = le32_to_cpu(xp.start); - wp->loop_start = le32_to_cpu(xp.loop_start); - wp->loop_end = le32_to_cpu(xp.loop_end); - wp->loop_repeat = le16_to_cpu(xp.loop_repeat); - wp->flags = xp.flags; - wp->sample_rate = le32_to_cpu(xp.sample_rate); - wp->low_frequency = le32_to_cpu(xp.low_frequency); - wp->high_frequency = le32_to_cpu(xp.high_frequency); - wp->root_frequency = le32_to_cpu(xp.root_frequency); - wp->tune = le16_to_cpu(xp.tune); - wp->balance = xp.balance; - memcpy(wp->envelope_rate, xp.envelope_rate, 6); - memcpy(wp->envelope_offset, xp.envelope_offset, 6); - wp->tremolo_sweep = xp.tremolo_sweep; - wp->tremolo_rate = xp.tremolo_rate; - wp->tremolo_depth = xp.tremolo_depth; - wp->vibrato_sweep = xp.vibrato_sweep; - wp->vibrato_rate = xp.vibrato_rate; - wp->vibrato_depth = xp.vibrato_depth; - wp->scale_frequency = le16_to_cpu(xp.scale_frequency); - wp->scale_factor = le16_to_cpu(xp.scale_factor); - real_size = snd_seq_gf1_size(wp->size, wp->format); - if ((long)real_size > *len) { - kfree(wp); - return -ENOMEM; - } - if (ops->put_sample) { - err = ops->put_sample(ops->private_data, wp, - *data, real_size, atomic); - if (err < 0) { - kfree(wp); - return err; - } - } - *data += real_size; - *len -= real_size; - prev = ip->wave; - if (prev) { - while (prev->next) prev = prev->next; - prev->next = wp; - } else { - ip->wave = wp; - } - return 0; -} - -static void snd_seq_gf1_wave_free(struct snd_gf1_ops *ops, - struct gf1_wave *wave, - int atomic) -{ - if (ops->remove_sample) - ops->remove_sample(ops->private_data, wave, atomic); - kfree(wave); -} - -static void snd_seq_gf1_instr_free(struct snd_gf1_ops *ops, - struct gf1_instrument *ip, - int atomic) -{ - struct gf1_wave *wave; - - while ((wave = ip->wave) != NULL) { - ip->wave = wave->next; - snd_seq_gf1_wave_free(ops, wave, atomic); - } -} - -static int snd_seq_gf1_put(void *private_data, struct snd_seq_kinstr *instr, - char __user *instr_data, long len, int atomic, - int cmd) -{ - struct snd_gf1_ops *ops = private_data; - struct gf1_instrument *ip; - struct gf1_xinstrument ix; - int err; - gfp_t gfp_mask; - - if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE) - return -EINVAL; - gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; - /* copy instrument data */ - if (len < (long)sizeof(ix)) - return -EINVAL; - if (copy_from_user(&ix, instr_data, sizeof(ix))) - return -EFAULT; - if (ix.stype != GF1_STRU_INSTR) - return -EINVAL; - instr_data += sizeof(ix); - len -= sizeof(ix); - ip = (struct gf1_instrument *)KINSTR_DATA(instr); - ip->exclusion = le16_to_cpu(ix.exclusion); - ip->exclusion_group = le16_to_cpu(ix.exclusion_group); - ip->effect1 = ix.effect1; - ip->effect1_depth = ix.effect1_depth; - ip->effect2 = ix.effect2; - ip->effect2_depth = ix.effect2_depth; - /* copy layers */ - while (len > (long)sizeof(__u32)) { - __u32 stype; - - if (copy_from_user(&stype, instr_data, sizeof(stype))) - return -EFAULT; - if (stype != GF1_STRU_WAVE) { - snd_seq_gf1_instr_free(ops, ip, atomic); - return -EINVAL; - } - err = snd_seq_gf1_copy_wave_from_stream(ops, - ip, - &instr_data, - &len, - atomic); - if (err < 0) { - snd_seq_gf1_instr_free(ops, ip, atomic); - return err; - } - } - return 0; -} - -static int snd_seq_gf1_copy_wave_to_stream(struct snd_gf1_ops *ops, - struct gf1_instrument *ip, - char __user **data, - long *len, - int atomic) -{ - struct gf1_wave *wp; - struct gf1_xwave xp; - int err; - unsigned int real_size; - - for (wp = ip->wave; wp; wp = wp->next) { - if (*len < (long)sizeof(xp)) - return -ENOMEM; - memset(&xp, 0, sizeof(xp)); - xp.stype = GF1_STRU_WAVE; - xp.share_id[0] = cpu_to_le32(wp->share_id[0]); - xp.share_id[1] = cpu_to_le32(wp->share_id[1]); - xp.share_id[2] = cpu_to_le32(wp->share_id[2]); - xp.share_id[3] = cpu_to_le32(wp->share_id[3]); - xp.format = cpu_to_le32(wp->format); - xp.size = cpu_to_le32(wp->size); - xp.start = cpu_to_le32(wp->start); - xp.loop_start = cpu_to_le32(wp->loop_start); - xp.loop_end = cpu_to_le32(wp->loop_end); - xp.loop_repeat = cpu_to_le32(wp->loop_repeat); - xp.flags = wp->flags; - xp.sample_rate = cpu_to_le32(wp->sample_rate); - xp.low_frequency = cpu_to_le32(wp->low_frequency); - xp.high_frequency = cpu_to_le32(wp->high_frequency); - xp.root_frequency = cpu_to_le32(wp->root_frequency); - xp.tune = cpu_to_le16(wp->tune); - xp.balance = wp->balance; - memcpy(xp.envelope_rate, wp->envelope_rate, 6); - memcpy(xp.envelope_offset, wp->envelope_offset, 6); - xp.tremolo_sweep = wp->tremolo_sweep; - xp.tremolo_rate = wp->tremolo_rate; - xp.tremolo_depth = wp->tremolo_depth; - xp.vibrato_sweep = wp->vibrato_sweep; - xp.vibrato_rate = wp->vibrato_rate; - xp.vibrato_depth = wp->vibrato_depth; - xp.scale_frequency = cpu_to_le16(wp->scale_frequency); - xp.scale_factor = cpu_to_le16(wp->scale_factor); - if (copy_to_user(*data, &xp, sizeof(xp))) - return -EFAULT; - *data += sizeof(xp); - *len -= sizeof(xp); - real_size = snd_seq_gf1_size(wp->size, wp->format); - if (*len < (long)real_size) - return -ENOMEM; - if (ops->get_sample) { - err = ops->get_sample(ops->private_data, wp, - *data, real_size, atomic); - if (err < 0) - return err; - } - *data += wp->size; - *len -= wp->size; - } - return 0; -} - -static int snd_seq_gf1_get(void *private_data, struct snd_seq_kinstr *instr, - char __user *instr_data, long len, int atomic, - int cmd) -{ - struct snd_gf1_ops *ops = private_data; - struct gf1_instrument *ip; - struct gf1_xinstrument ix; - - if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL) - return -EINVAL; - if (len < (long)sizeof(ix)) - return -ENOMEM; - memset(&ix, 0, sizeof(ix)); - ip = (struct gf1_instrument *)KINSTR_DATA(instr); - ix.stype = GF1_STRU_INSTR; - ix.exclusion = cpu_to_le16(ip->exclusion); - ix.exclusion_group = cpu_to_le16(ip->exclusion_group); - ix.effect1 = cpu_to_le16(ip->effect1); - ix.effect1_depth = cpu_to_le16(ip->effect1_depth); - ix.effect2 = ip->effect2; - ix.effect2_depth = ip->effect2_depth; - if (copy_to_user(instr_data, &ix, sizeof(ix))) - return -EFAULT; - instr_data += sizeof(ix); - len -= sizeof(ix); - return snd_seq_gf1_copy_wave_to_stream(ops, - ip, - &instr_data, - &len, - atomic); -} - -static int snd_seq_gf1_get_size(void *private_data, struct snd_seq_kinstr *instr, - long *size) -{ - long result; - struct gf1_instrument *ip; - struct gf1_wave *wp; - - *size = 0; - ip = (struct gf1_instrument *)KINSTR_DATA(instr); - result = sizeof(struct gf1_xinstrument); - for (wp = ip->wave; wp; wp = wp->next) { - result += sizeof(struct gf1_xwave); - result += wp->size; - } - *size = result; - return 0; -} - -static int snd_seq_gf1_remove(void *private_data, - struct snd_seq_kinstr *instr, - int atomic) -{ - struct snd_gf1_ops *ops = private_data; - struct gf1_instrument *ip; - - ip = (struct gf1_instrument *)KINSTR_DATA(instr); - snd_seq_gf1_instr_free(ops, ip, atomic); - return 0; -} - -static void snd_seq_gf1_notify(void *private_data, - struct snd_seq_kinstr *instr, - int what) -{ - struct snd_gf1_ops *ops = private_data; - - if (ops->notify) - ops->notify(ops->private_data, instr, what); -} - -int snd_seq_gf1_init(struct snd_gf1_ops *ops, - void *private_data, - struct snd_seq_kinstr_ops *next) -{ - memset(ops, 0, sizeof(*ops)); - ops->private_data = private_data; - ops->kops.private_data = ops; - ops->kops.add_len = sizeof(struct gf1_instrument); - ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_GUS_PATCH; - ops->kops.put = snd_seq_gf1_put; - ops->kops.get = snd_seq_gf1_get; - ops->kops.get_size = snd_seq_gf1_get_size; - ops->kops.remove = snd_seq_gf1_remove; - ops->kops.notify = snd_seq_gf1_notify; - ops->kops.next = next; - return 0; -} - -/* - * Init part - */ - -static int __init alsa_ainstr_gf1_init(void) -{ - return 0; -} - -static void __exit alsa_ainstr_gf1_exit(void) -{ -} - -module_init(alsa_ainstr_gf1_init) -module_exit(alsa_ainstr_gf1_exit) - -EXPORT_SYMBOL(snd_seq_gf1_init); diff --git a/sound/core/seq/instr/ainstr_iw.c b/sound/core/seq/instr/ainstr_iw.c deleted file mode 100644 index 6c40eb7..0000000 --- a/sound/core/seq/instr/ainstr_iw.c +++ /dev/null @@ -1,623 +0,0 @@ -/* - * IWFFFF - AMD InterWave (tm) - Instrument routines - * Copyright (c) 1999 by Jaroslav Kysela <perex@perex.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 <linux/init.h> -#include <linux/slab.h> -#include <sound/core.h> -#include <sound/ainstr_iw.h> -#include <sound/initval.h> -#include <asm/uaccess.h> - -MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); -MODULE_DESCRIPTION("Advanced Linux Sound Architecture IWFFFF support."); -MODULE_LICENSE("GPL"); - -static unsigned int snd_seq_iwffff_size(unsigned int size, unsigned int format) -{ - unsigned int result = size; - - if (format & IWFFFF_WAVE_16BIT) - result <<= 1; - if (format & IWFFFF_WAVE_STEREO) - result <<= 1; - return result; -} - -static void snd_seq_iwffff_copy_lfo_from_stream(struct iwffff_lfo *fp, - struct iwffff_xlfo *fx) -{ - fp->freq = le16_to_cpu(fx->freq); - fp->depth = le16_to_cpu(fx->depth); - fp->sweep = le16_to_cpu(fx->sweep); - fp->shape = fx->shape; - fp->delay = fx->delay; -} - -static int snd_seq_iwffff_copy_env_from_stream(__u32 req_stype, - struct iwffff_layer *lp, - struct iwffff_env *ep, - struct iwffff_xenv *ex, - char __user **data, - long *len, - gfp_t gfp_mask) -{ - __u32 stype; - struct iwffff_env_record *rp, *rp_last; - struct iwffff_xenv_record rx; - struct iwffff_env_point *pp; - struct iwffff_xenv_point px; - int points_size, idx; - - ep->flags = ex->flags; - ep->mode = ex->mode; - ep->index = ex->index; - rp_last = NULL; - while (1) { - if (*len < (long)sizeof(__u32)) - return -EINVAL; - if (copy_from_user(&stype, *data, sizeof(stype))) - return -EFAULT; - if (stype == IWFFFF_STRU_WAVE) - return 0; - if (req_stype != stype) { - if (stype == IWFFFF_STRU_ENV_RECP || - stype == IWFFFF_STRU_ENV_RECV) - return 0; - } - if (*len < (long)sizeof(rx)) - return -EINVAL; - if (copy_from_user(&rx, *data, sizeof(rx))) - return -EFAULT; - *data += sizeof(rx); - *len -= sizeof(rx); - points_size = (le16_to_cpu(rx.nattack) + le16_to_cpu(rx.nrelease)) * 2 * sizeof(__u16); - if (points_size > *len) - return -EINVAL; - rp = kzalloc(sizeof(*rp) + points_size, gfp_mask); - if (rp == NULL) - return -ENOMEM; - rp->nattack = le16_to_cpu(rx.nattack); - rp->nrelease = le16_to_cpu(rx.nrelease); - rp->sustain_offset = le16_to_cpu(rx.sustain_offset); - rp->sustain_rate = le16_to_cpu(rx.sustain_rate); - rp->release_rate = le16_to_cpu(rx.release_rate); - rp->hirange = rx.hirange; - pp = (struct iwffff_env_point *)(rp + 1); - for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) { - if (copy_from_user(&px, *data, sizeof(px))) - return -EFAULT; - *data += sizeof(px); - *len -= sizeof(px); - pp->offset = le16_to_cpu(px.offset); - pp->rate = le16_to_cpu(px.rate); - } - if (ep->record == NULL) { - ep->record = rp; - } else { - rp_last = rp; - } - rp_last = rp; - } - return 0; -} - -static int snd_seq_iwffff_copy_wave_from_stream(struct snd_iwffff_ops *ops, - struct iwffff_layer *lp, - char __user **data, - long *len, - int atomic) -{ - struct iwffff_wave *wp, *prev; - struct iwffff_xwave xp; - int err; - gfp_t gfp_mask; - unsigned int real_size; - - gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; - if (*len < (long)sizeof(xp)) - return -EINVAL; - if (copy_from_user(&xp, *data, sizeof(xp))) - return -EFAULT; - *data += sizeof(xp); - *len -= sizeof(xp); - wp = kzalloc(sizeof(*wp), gfp_mask); - if (wp == NULL) - return -ENOMEM; - wp->share_id[0] = le32_to_cpu(xp.share_id[0]); - wp->share_id[1] = le32_to_cpu(xp.share_id[1]); - wp->share_id[2] = le32_to_cpu(xp.share_id[2]); - wp->share_id[3] = le32_to_cpu(xp.share_id[3]); - wp->format = le32_to_cpu(xp.format); - wp->address.memory = le32_to_cpu(xp.offset); - wp->size = le32_to_cpu(xp.size); - wp->start = le32_to_cpu(xp.start); - wp->loop_start = le32_to_cpu(xp.loop_start); - wp->loop_end = le32_to_cpu(xp.loop_end); - wp->loop_repeat = le16_to_cpu(xp.loop_repeat); - wp->sample_ratio = le32_to_cpu(xp.sample_ratio); - wp->attenuation = xp.attenuation; - wp->low_note = xp.low_note; - wp->high_note = xp.high_note; - real_size = snd_seq_iwffff_size(wp->size, wp->format); - if (!(wp->format & IWFFFF_WAVE_ROM)) { - if ((long)real_size > *len) { - kfree(wp); - return -ENOMEM; - } - } - if (ops->put_sample) { - err = ops->put_sample(ops->private_data, wp, - *data, real_size, atomic); - if (err < 0) { - kfree(wp); - return err; - } - } - if (!(wp->format & IWFFFF_WAVE_ROM)) { - *data += real_size; - *len -= real_size; - } - prev = lp->wave; - if (prev) { - while (prev->next) prev = prev->next; - prev->next = wp; - } else { - lp->wave = wp; - } - return 0; -} - -static void snd_seq_iwffff_env_free(struct snd_iwffff_ops *ops, - struct iwffff_env *env, - int atomic) -{ - struct iwffff_env_record *rec; - - while ((rec = env->record) != NULL) { - env->record = rec->next; - kfree(rec); - } -} - -static void snd_seq_iwffff_wave_free(struct snd_iwffff_ops *ops, - struct iwffff_wave *wave, - int atomic) -{ - if (ops->remove_sample) - ops->remove_sample(ops->private_data, wave, atomic); - kfree(wave); -} - -static void snd_seq_iwffff_instr_free(struct snd_iwffff_ops *ops, - struct iwffff_instrument *ip, - int atomic) -{ - struct iwffff_layer *layer; - struct iwffff_wave *wave; - - while ((layer = ip->layer) != NULL) { - ip->layer = layer->next; - snd_seq_iwffff_env_free(ops, &layer->penv, atomic); - snd_seq_iwffff_env_free(ops, &layer->venv, atomic); - while ((wave = layer->wave) != NULL) { - layer->wave = wave->next; - snd_seq_iwffff_wave_free(ops, wave, atomic); - } - kfree(layer); - } -} - -static int snd_seq_iwffff_put(void *private_data, struct snd_seq_kinstr *instr, - char __user *instr_data, long len, int atomic, - int cmd) -{ - struct snd_iwffff_ops *ops = private_data; - struct iwffff_instrument *ip; - struct iwffff_xinstrument ix; - struct iwffff_layer *lp, *prev_lp; - struct iwffff_xlayer lx; - int err; - gfp_t gfp_mask; - - if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE) - return -EINVAL; - gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; - /* copy instrument data */ - if (len < (long)sizeof(ix)) - return -EINVAL; - if (copy_from_user(&ix, instr_data, sizeof(ix))) - return -EFAULT; - if (ix.stype != IWFFFF_STRU_INSTR) - return -EINVAL; - instr_data += sizeof(ix); - len -= sizeof(ix); - ip = (struct iwffff_instrument *)KINSTR_DATA(instr); - ip->exclusion = le16_to_cpu(ix.exclusion); - ip->layer_type = le16_to_cpu(ix.layer_type); - ip->exclusion_group = le16_to_cpu(ix.exclusion_group); - ip->effect1 = ix.effect1; - ip->effect1_depth = ix.effect1_depth; - ip->effect2 = ix.effect2; - ip->effect2_depth = ix.effect2_depth; - /* copy layers */ - prev_lp = NULL; - while (len > 0) { - if (len < (long)sizeof(struct iwffff_xlayer)) { - snd_seq_iwffff_instr_free(ops, ip, atomic); - return -EINVAL; - } - if (copy_from_user(&lx, instr_data, sizeof(lx))) - return -EFAULT; - instr_data += sizeof(lx); - len -= sizeof(lx); - if (lx.stype != IWFFFF_STRU_LAYER) { - snd_seq_iwffff_instr_free(ops, ip, atomic); - return -EINVAL; - } - lp = kzalloc(sizeof(*lp), gfp_mask); - if (lp == NULL) { - snd_seq_iwffff_instr_free(ops, ip, atomic); - return -ENOMEM; - } - if (prev_lp) { - prev_lp->next = lp; - } else { - ip->layer = lp; - } - prev_lp = lp; - lp->flags = lx.flags; - lp->velocity_mode = lx.velocity_mode; - lp->layer_event = lx.layer_event; - lp->low_range = lx.low_range; - lp->high_range = lx.high_range; - lp->pan = lx.pan; - lp->pan_freq_scale = lx.pan_freq_scale; - lp->attenuation = lx.attenuation; - snd_seq_iwffff_copy_lfo_from_stream(&lp->tremolo, &lx.tremolo); - snd_seq_iwffff_copy_lfo_from_stream(&lp->vibrato, &lx.vibrato); - lp->freq_scale = le16_to_cpu(lx.freq_scale); - lp->freq_center = lx.freq_center; - err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECP, - lp, - &lp->penv, &lx.penv, - &instr_data, &len, - gfp_mask); - if (err < 0) { - snd_seq_iwffff_instr_free(ops, ip, atomic); - return err; - } - err = snd_seq_iwffff_copy_env_from_stream(IWFFFF_STRU_ENV_RECV, - lp, - &lp->venv, &lx.venv, - &instr_data, &len, - gfp_mask); - if (err < 0) { - snd_seq_iwffff_instr_free(ops, ip, atomic); - return err; - } - while (len > (long)sizeof(__u32)) { - __u32 stype; - - if (copy_from_user(&stype, instr_data, sizeof(stype))) - return -EFAULT; - if (stype != IWFFFF_STRU_WAVE) - break; - err = snd_seq_iwffff_copy_wave_from_stream(ops, - lp, - &instr_data, - &len, - atomic); - if (err < 0) { - snd_seq_iwffff_instr_free(ops, ip, atomic); - return err; - } - } - } - return 0; -} - -static void snd_seq_iwffff_copy_lfo_to_stream(struct iwffff_xlfo *fx, - struct iwffff_lfo *fp) -{ - fx->freq = cpu_to_le16(fp->freq); - fx->depth = cpu_to_le16(fp->depth); - fx->sweep = cpu_to_le16(fp->sweep); - fp->shape = fx->shape; - fp->delay = fx->delay; -} - -static int snd_seq_iwffff_copy_env_to_stream(__u32 req_stype, - struct iwffff_layer *lp, - struct iwffff_xenv *ex, - struct iwffff_env *ep, - char __user **data, - long *len) -{ - struct iwffff_env_record *rp; - struct iwffff_xenv_record rx; - struct iwffff_env_point *pp; - struct iwffff_xenv_point px; - int points_size, idx; - - ex->flags = ep->flags; - ex->mode = ep->mode; - ex->index = ep->index; - for (rp = ep->record; rp; rp = rp->next) { - if (*len < (long)sizeof(rx)) - return -ENOMEM; - memset(&rx, 0, sizeof(rx)); - rx.stype = req_stype; - rx.nattack = cpu_to_le16(rp->nattack); - rx.nrelease = cpu_to_le16(rp->nrelease); - rx.sustain_offset = cpu_to_le16(rp->sustain_offset); - rx.sustain_rate = cpu_to_le16(rp->sustain_rate); - rx.release_rate = cpu_to_le16(rp->release_rate); - rx.hirange = cpu_to_le16(rp->hirange); - if (copy_to_user(*data, &rx, sizeof(rx))) - return -EFAULT; - *data += sizeof(rx); - *len -= sizeof(rx); - points_size = (rp->nattack + rp->nrelease) * 2 * sizeof(__u16); - if (*len < points_size) - return -ENOMEM; - pp = (struct iwffff_env_point *)(rp + 1); - for (idx = 0; idx < rp->nattack + rp->nrelease; idx++) { - px.offset = cpu_to_le16(pp->offset); - px.rate = cpu_to_le16(pp->rate); - if (copy_to_user(*data, &px, sizeof(px))) - return -EFAULT; - *data += sizeof(px); - *len -= sizeof(px); - } - } - return 0; -} - -static int snd_seq_iwffff_copy_wave_to_stream(struct snd_iwffff_ops *ops, - struct iwffff_layer *lp, - char __user **data, - long *len, - int atomic) -{ - struct iwffff_wave *wp; - struct iwffff_xwave xp; - int err; - unsigned int real_size; - - for (wp = lp->wave; wp; wp = wp->next) { - if (*len < (long)sizeof(xp)) - return -ENOMEM; - memset(&xp, 0, sizeof(xp)); - xp.stype = IWFFFF_STRU_WAVE; - xp.share_id[0] = cpu_to_le32(wp->share_id[0]); - xp.share_id[1] = cpu_to_le32(wp->share_id[1]); - xp.share_id[2] = cpu_to_le32(wp->share_id[2]); - xp.share_id[3] = cpu_to_le32(wp->share_id[3]); - xp.format = cpu_to_le32(wp->format); - if (wp->format & IWFFFF_WAVE_ROM) - xp.offset = cpu_to_le32(wp->address.memory); - xp.size = cpu_to_le32(wp->size); - xp.start = cpu_to_le32(wp->start); - xp.loop_start = cpu_to_le32(wp->loop_start); - xp.loop_end = cpu_to_le32(wp->loop_end); - xp.loop_repeat = cpu_to_le32(wp->loop_repeat); - xp.sample_ratio = cpu_to_le32(wp->sample_ratio); - xp.attenuation = wp->attenuation; - xp.low_note = wp->low_note; - xp.high_note = wp->high_note; - if (copy_to_user(*data, &xp, sizeof(xp))) - return -EFAULT; - *data += sizeof(xp); - *len -= sizeof(xp); - real_size = snd_seq_iwffff_size(wp->size, wp->format); - if (!(wp->format & IWFFFF_WAVE_ROM)) { - if (*len < (long)real_size) - return -ENOMEM; - } - if (ops->get_sample) { - err = ops->get_sample(ops->private_data, wp, - *data, real_size, atomic); - if (err < 0) - return err; - } - if (!(wp->format & IWFFFF_WAVE_ROM)) { - *data += real_size; - *len -= real_size; - } - } - return 0; -} - -static int snd_seq_iwffff_get(void *private_data, struct snd_seq_kinstr *instr, - char __user *instr_data, long len, int atomic, int cmd) -{ - struct snd_iwffff_ops *ops = private_data; - struct iwffff_instrument *ip; - struct iwffff_xinstrument ix; - struct iwffff_layer *lp; - struct iwffff_xlayer lx; - char __user *layer_instr_data; - int err; - - if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL) - return -EINVAL; - if (len < (long)sizeof(ix)) - return -ENOMEM; - memset(&ix, 0, sizeof(ix)); - ip = (struct iwffff_instrument *)KINSTR_DATA(instr); - ix.stype = IWFFFF_STRU_INSTR; - ix.exclusion = cpu_to_le16(ip->exclusion); - ix.layer_type = cpu_to_le16(ip->layer_type); - ix.exclusion_group = cpu_to_le16(ip->exclusion_group); - ix.effect1 = cpu_to_le16(ip->effect1); - ix.effect1_depth = cpu_to_le16(ip->effect1_depth); - ix.effect2 = ip->effect2; - ix.effect2_depth = ip->effect2_depth; - if (copy_to_user(instr_data, &ix, sizeof(ix))) - return -EFAULT; - instr_data += sizeof(ix); - len -= sizeof(ix); - for (lp = ip->layer; lp; lp = lp->next) { - if (len < (long)sizeof(lx)) - return -ENOMEM; - memset(&lx, 0, sizeof(lx)); - lx.stype = IWFFFF_STRU_LAYER; - lx.flags = lp->flags; - lx.velocity_mode = lp->velocity_mode; - lx.layer_event = lp->layer_event; - lx.low_range = lp->low_range; - lx.high_range = lp->high_range; - lx.pan = lp->pan; - lx.pan_freq_scale = lp->pan_freq_scale; - lx.attenuation = lp->attenuation; - snd_seq_iwffff_copy_lfo_to_stream(&lx.tremolo, &lp->tremolo); - snd_seq_iwffff_copy_lfo_to_stream(&lx.vibrato, &lp->vibrato); - layer_instr_data = instr_data; - instr_data += sizeof(lx); - len -= sizeof(lx); - err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECP, - lp, - &lx.penv, &lp->penv, - &instr_data, &len); - if (err < 0) - return err; - err = snd_seq_iwffff_copy_env_to_stream(IWFFFF_STRU_ENV_RECV, - lp, - &lx.venv, &lp->venv, - &instr_data, &len); - if (err < 0) - return err; - /* layer structure updating is now finished */ - if (copy_to_user(layer_instr_data, &lx, sizeof(lx))) - return -EFAULT; - err = snd_seq_iwffff_copy_wave_to_stream(ops, - lp, - &instr_data, - &len, - atomic); - if (err < 0) - return err; - } - return 0; -} - -static long snd_seq_iwffff_env_size_in_stream(struct iwffff_env *ep) -{ - long result = 0; - struct iwffff_env_record *rp; - - for (rp = ep->record; rp; rp = rp->next) { - result += sizeof(struct iwffff_xenv_record); - result += (rp->nattack + rp->nrelease) * 2 * sizeof(__u16); - } - return 0; -} - -static long snd_seq_iwffff_wave_size_in_stream(struct iwffff_layer *lp) -{ - long result = 0; - struct iwffff_wave *wp; - - for (wp = lp->wave; wp; wp = wp->next) { - result += sizeof(struct iwffff_xwave); - if (!(wp->format & IWFFFF_WAVE_ROM)) - result += wp->size; - } - return result; -} - -static int snd_seq_iwffff_get_size(void *private_data, struct snd_seq_kinstr *instr, - long *size) -{ - long result; - struct iwffff_instrument *ip; - struct iwffff_layer *lp; - - *size = 0; - ip = (struct iwffff_instrument *)KINSTR_DATA(instr); - result = sizeof(struct iwffff_xinstrument); - for (lp = ip->layer; lp; lp = lp->next) { - result += sizeof(struct iwffff_xlayer); - result += snd_seq_iwffff_env_size_in_stream(&lp->penv); - result += snd_seq_iwffff_env_size_in_stream(&lp->venv); - result += snd_seq_iwffff_wave_size_in_stream(lp); - } - *size = result; - return 0; -} - -static int snd_seq_iwffff_remove(void *private_data, - struct snd_seq_kinstr *instr, - int atomic) -{ - struct snd_iwffff_ops *ops = private_data; - struct iwffff_instrument *ip; - - ip = (struct iwffff_instrument *)KINSTR_DATA(instr); - snd_seq_iwffff_instr_free(ops, ip, atomic); - return 0; -} - -static void snd_seq_iwffff_notify(void *private_data, - struct snd_seq_kinstr *instr, - int what) -{ - struct snd_iwffff_ops *ops = private_data; - - if (ops->notify) - ops->notify(ops->private_data, instr, what); -} - -int snd_seq_iwffff_init(struct snd_iwffff_ops *ops, - void *private_data, - struct snd_seq_kinstr_ops *next) -{ - memset(ops, 0, sizeof(*ops)); - ops->private_data = private_data; - ops->kops.private_data = ops; - ops->kops.add_len = sizeof(struct iwffff_instrument); - ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_INTERWAVE; - ops->kops.put = snd_seq_iwffff_put; - ops->kops.get = snd_seq_iwffff_get; - ops->kops.get_size = snd_seq_iwffff_get_size; - ops->kops.remove = snd_seq_iwffff_remove; - ops->kops.notify = snd_seq_iwffff_notify; - ops->kops.next = next; - return 0; -} - -/* - * Init part - */ - -static int __init alsa_ainstr_iw_init(void) -{ - return 0; -} - -static void __exit alsa_ainstr_iw_exit(void) -{ -} - -module_init(alsa_ainstr_iw_init) -module_exit(alsa_ainstr_iw_exit) - -EXPORT_SYMBOL(snd_seq_iwffff_init); diff --git a/sound/core/seq/instr/ainstr_simple.c b/sound/core/seq/instr/ainstr_simple.c deleted file mode 100644 index 78f68be..0000000 --- a/sound/core/seq/instr/ainstr_simple.c +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Simple (MOD player) - Instrument routines - * Copyright (c) 1999 by Jaroslav Kysela <perex@perex.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 <linux/init.h> -#include <linux/slab.h> -#include <sound/core.h> -#include <sound/ainstr_simple.h> -#include <sound/initval.h> -#include <asm/uaccess.h> - -MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); -MODULE_DESCRIPTION("Advanced Linux Sound Architecture Simple Instrument support."); -MODULE_LICENSE("GPL"); - -static unsigned int snd_seq_simple_size(unsigned int size, unsigned int format) -{ - unsigned int result = size; - - if (format & SIMPLE_WAVE_16BIT) - result <<= 1; - if (format & SIMPLE_WAVE_STEREO) - result <<= 1; - return result; -} - -static void snd_seq_simple_instr_free(struct snd_simple_ops *ops, - struct simple_instrument *ip, - int atomic) -{ - if (ops->remove_sample) - ops->remove_sample(ops->private_data, ip, atomic); -} - -static int snd_seq_simple_put(void *private_data, struct snd_seq_kinstr *instr, - char __user *instr_data, long len, - int atomic, int cmd) -{ - struct snd_simple_ops *ops = private_data; - struct simple_instrument *ip; - struct simple_xinstrument ix; - int err; - gfp_t gfp_mask; - unsigned int real_size; - - if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE) - return -EINVAL; - gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; - /* copy instrument data */ - if (len < (long)sizeof(ix)) - return -EINVAL; - if (copy_from_user(&ix, instr_data, sizeof(ix))) - return -EFAULT; - if (ix.stype != SIMPLE_STRU_INSTR) - return -EINVAL; - instr_data += sizeof(ix); - len -= sizeof(ix); - ip = (struct simple_instrument *)KINSTR_DATA(instr); - ip->share_id[0] = le32_to_cpu(ix.share_id[0]); - ip->share_id[1] = le32_to_cpu(ix.share_id[1]); - ip->share_id[2] = le32_to_cpu(ix.share_id[2]); - ip->share_id[3] = le32_to_cpu(ix.share_id[3]); - ip->format = le32_to_cpu(ix.format); - ip->size = le32_to_cpu(ix.size); - ip->start = le32_to_cpu(ix.start); - ip->loop_start = le32_to_cpu(ix.loop_start); - ip->loop_end = le32_to_cpu(ix.loop_end); - ip->loop_repeat = le16_to_cpu(ix.loop_repeat); - ip->effect1 = ix.effect1; - ip->effect1_depth = ix.effect1_depth; - ip->effect2 = ix.effect2; - ip->effect2_depth = ix.effect2_depth; - real_size = snd_seq_simple_size(ip->size, ip->format); - if (len < (long)real_size) - return -EINVAL; - if (ops->put_sample) { - err = ops->put_sample(ops->private_data, ip, - instr_data, real_size, atomic); - if (err < 0) - return err; - } - return 0; -} - -static int snd_seq_simple_get(void *private_data, struct snd_seq_kinstr *instr, - char __user *instr_data, long len, - int atomic, int cmd) -{ - struct snd_simple_ops *ops = private_data; - struct simple_instrument *ip; - struct simple_xinstrument ix; - int err; - unsigned int real_size; - - if (cmd != SNDRV_SEQ_INSTR_GET_CMD_FULL) - return -EINVAL; - if (len < (long)sizeof(ix)) - return -ENOMEM; - memset(&ix, 0, sizeof(ix)); - ip = (struct simple_instrument *)KINSTR_DATA(instr); - ix.stype = SIMPLE_STRU_INSTR; - ix.share_id[0] = cpu_to_le32(ip->share_id[0]); - ix.share_id[1] = cpu_to_le32(ip->share_id[1]); - ix.share_id[2] = cpu_to_le32(ip->share_id[2]); - ix.share_id[3] = cpu_to_le32(ip->share_id[3]); - ix.format = cpu_to_le32(ip->format); - ix.size = cpu_to_le32(ip->size); - ix.start = cpu_to_le32(ip->start); - ix.loop_start = cpu_to_le32(ip->loop_start); - ix.loop_end = cpu_to_le32(ip->loop_end); - ix.loop_repeat = cpu_to_le32(ip->loop_repeat); - ix.effect1 = cpu_to_le16(ip->effect1); - ix.effect1_depth = cpu_to_le16(ip->effect1_depth); - ix.effect2 = ip->effect2; - ix.effect2_depth = ip->effect2_depth; - if (copy_to_user(instr_data, &ix, sizeof(ix))) - return -EFAULT; - instr_data += sizeof(ix); - len -= sizeof(ix); - real_size = snd_seq_simple_size(ip->size, ip->format); - if (len < (long)real_size) - return -ENOMEM; - if (ops->get_sample) { - err = ops->get_sample(ops->private_data, ip, - instr_data, real_size, atomic); - if (err < 0) - return err; - } - return 0; -} - -static int snd_seq_simple_get_size(void *private_data, struct snd_seq_kinstr *instr, - long *size) -{ - struct simple_instrument *ip; - - ip = (struct simple_instrument *)KINSTR_DATA(instr); - *size = sizeof(struct simple_xinstrument) + snd_seq_simple_size(ip->size, ip->format); - return 0; -} - -static int snd_seq_simple_remove(void *private_data, - struct snd_seq_kinstr *instr, - int atomic) -{ - struct snd_simple_ops *ops = private_data; - struct simple_instrument *ip; - - ip = (struct simple_instrument *)KINSTR_DATA(instr); - snd_seq_simple_instr_free(ops, ip, atomic); - return 0; -} - -static void snd_seq_simple_notify(void *private_data, - struct snd_seq_kinstr *instr, - int what) -{ - struct snd_simple_ops *ops = private_data; - - if (ops->notify) - ops->notify(ops->private_data, instr, what); -} - -int snd_seq_simple_init(struct snd_simple_ops *ops, - void *private_data, - struct snd_seq_kinstr_ops *next) -{ - memset(ops, 0, sizeof(*ops)); - ops->private_data = private_data; - ops->kops.private_data = ops; - ops->kops.add_len = sizeof(struct simple_instrument); - ops->kops.instr_type = SNDRV_SEQ_INSTR_ID_SIMPLE; - ops->kops.put = snd_seq_simple_put; - ops->kops.get = snd_seq_simple_get; - ops->kops.get_size = snd_seq_simple_get_size; - ops->kops.remove = snd_seq_simple_remove; - ops->kops.notify = snd_seq_simple_notify; - ops->kops.next = next; - return 0; -} - -/* - * Init part - */ - -static int __init alsa_ainstr_simple_init(void) -{ - return 0; -} - -static void __exit alsa_ainstr_simple_exit(void) -{ -} - -module_init(alsa_ainstr_simple_init) -module_exit(alsa_ainstr_simple_exit) - -EXPORT_SYMBOL(snd_seq_simple_init); diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c index bc09923..777796e 100644 --- a/sound/core/seq/oss/seq_oss.c +++ b/sound/core/seq/oss/seq_oss.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/moduleparam.h> #include <linux/mutex.h> diff --git a/sound/core/seq/oss/seq_oss_device.h b/sound/core/seq/oss/seq_oss_device.h index 9a8567c..bf8d2b4 100644 --- a/sound/core/seq/oss/seq_oss_device.h +++ b/sound/core/seq/oss/seq_oss_device.h @@ -21,7 +21,6 @@ #ifndef __SEQ_OSS_DEVICE_H #define __SEQ_OSS_DEVICE_H -#include <sound/driver.h> #include <linux/time.h> #include <linux/wait.h> #include <linux/slab.h> diff --git a/sound/core/seq/seq.c b/sound/core/seq/seq.c index 1878208..ee0f840 100644 --- a/sound/core/seq/seq.c +++ b/sound/core/seq/seq.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/moduleparam.h> #include <sound/core.h> diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 2e3fa25..f97c1ba 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/slab.h> #include <sound/core.h> @@ -130,8 +129,6 @@ static struct snd_seq_client *clientptr(int clientid) return clienttab[clientid]; } -extern int seq_client_load[]; - struct snd_seq_client *snd_seq_client_use_ptr(int clientid) { unsigned long flags; @@ -966,8 +963,7 @@ static int check_event_type_and_length(struct snd_seq_event *ev) return -EINVAL; break; case SNDRV_SEQ_EVENT_LENGTH_VARUSR: - if (! snd_seq_ev_is_instr_type(ev) || - ! snd_seq_ev_is_direct(ev)) + if (! snd_seq_ev_is_direct(ev)) return -EINVAL; break; } diff --git a/sound/core/seq/seq_clientmgr.h b/sound/core/seq/seq_clientmgr.h index 5e04e20..20f0a72 100644 --- a/sound/core/seq/seq_clientmgr.h +++ b/sound/core/seq/seq_clientmgr.h @@ -98,4 +98,6 @@ int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table int snd_seq_client_notify_subscription(int client, int port, struct snd_seq_port_subscribe *info, int evtype); +extern int seq_client_load[15]; + #endif diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index 37852cd..155dc7d 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq/seq_device.c @@ -36,7 +36,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <sound/core.h> #include <sound/info.h> diff --git a/sound/core/seq/seq_dummy.c b/sound/core/seq/seq_dummy.c index e55488d..f3bdc54 100644 --- a/sound/core/seq/seq_dummy.c +++ b/sound/core/seq/seq_dummy.c @@ -18,7 +18,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/moduleparam.h> diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c index 6b055ae..3a94ed0 100644 --- a/sound/core/seq/seq_fifo.c +++ b/sound/core/seq/seq_fifo.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <sound/core.h> #include <linux/slab.h> #include "seq_fifo.h" diff --git a/sound/core/seq/seq_info.c b/sound/core/seq/seq_info.c index 8a7fe5c..201f810 100644 --- a/sound/core/seq/seq_info.c +++ b/sound/core/seq/seq_info.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <sound/core.h> diff --git a/sound/core/seq/seq_instr.c b/sound/core/seq/seq_instr.c deleted file mode 100644 index 9a6fd56..0000000 --- a/sound/core/seq/seq_instr.c +++ /dev/null @@ -1,655 +0,0 @@ -/* - * Generic Instrument routines for ALSA sequencer - * Copyright (c) 1999 by Jaroslav Kysela <perex@perex.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 <linux/init.h> -#include <linux/slab.h> -#include <sound/core.h> -#include "seq_clientmgr.h" -#include <sound/seq_instr.h> -#include <sound/initval.h> - -MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); -MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer instrument library."); -MODULE_LICENSE("GPL"); - - -static void snd_instr_lock_ops(struct snd_seq_kinstr_list *list) -{ - if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) { - spin_lock_irqsave(&list->ops_lock, list->ops_flags); - } else { - mutex_lock(&list->ops_mutex); - } -} - -static void snd_instr_unlock_ops(struct snd_seq_kinstr_list *list) -{ - if (!(list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT)) { - spin_unlock_irqrestore(&list->ops_lock, list->ops_flags); - } else { - mutex_unlock(&list->ops_mutex); - } -} - -static struct snd_seq_kinstr *snd_seq_instr_new(int add_len, int atomic) -{ - struct snd_seq_kinstr *instr; - - instr = kzalloc(sizeof(struct snd_seq_kinstr) + add_len, atomic ? GFP_ATOMIC : GFP_KERNEL); - if (instr == NULL) - return NULL; - instr->add_len = add_len; - return instr; -} - -static int snd_seq_instr_free(struct snd_seq_kinstr *instr, int atomic) -{ - int result = 0; - - if (instr == NULL) - return -EINVAL; - if (instr->ops && instr->ops->remove) - result = instr->ops->remove(instr->ops->private_data, instr, 1); - if (!result) - kfree(instr); - return result; -} - -struct snd_seq_kinstr_list *snd_seq_instr_list_new(void) -{ - struct snd_seq_kinstr_list *list; - - list = kzalloc(sizeof(struct snd_seq_kinstr_list), GFP_KERNEL); - if (list == NULL) - return NULL; - spin_lock_init(&list->lock); - spin_lock_init(&list->ops_lock); - mutex_init(&list->ops_mutex); - list->owner = -1; - return list; -} - -void snd_seq_instr_list_free(struct snd_seq_kinstr_list **list_ptr) -{ - struct snd_seq_kinstr_list *list; - struct snd_seq_kinstr *instr; - struct snd_seq_kcluster *cluster; - int idx; - unsigned long flags; - - if (list_ptr == NULL) - return; - list = *list_ptr; - *list_ptr = NULL; - if (list == NULL) - return; - - for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) { - while ((instr = list->hash[idx]) != NULL) { - list->hash[idx] = instr->next; - list->count--; - spin_lock_irqsave(&list->lock, flags); - while (instr->use) { - spin_unlock_irqrestore(&list->lock, flags); - schedule_timeout_uninterruptible(1); - spin_lock_irqsave(&list->lock, flags); - } - spin_unlock_irqrestore(&list->lock, flags); - if (snd_seq_instr_free(instr, 0)<0) - snd_printk(KERN_WARNING "instrument free problem\n"); - } - while ((cluster = list->chash[idx]) != NULL) { - list->chash[idx] = cluster->next; - list->ccount--; - kfree(cluster); - } - } - kfree(list); -} - -static int instr_free_compare(struct snd_seq_kinstr *instr, - struct snd_seq_instr_header *ifree, - unsigned int client) -{ - switch (ifree->cmd) { - case SNDRV_SEQ_INSTR_FREE_CMD_ALL: - /* all, except private for other clients */ - if ((instr->instr.std & 0xff000000) == 0) - return 0; - if (((instr->instr.std >> 24) & 0xff) == client) - return 0; - return 1; - case SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE: - /* all my private instruments */ - if ((instr->instr.std & 0xff000000) == 0) - return 1; - if (((instr->instr.std >> 24) & 0xff) == client) - return 0; - return 1; - case SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER: - /* all my private instruments */ - if ((instr->instr.std & 0xff000000) == 0) { - if (instr->instr.cluster == ifree->id.cluster) - return 0; - return 1; - } - if (((instr->instr.std >> 24) & 0xff) == client) { - if (instr->instr.cluster == ifree->id.cluster) - return 0; - } - return 1; - } - return 1; -} - -int snd_seq_instr_list_free_cond(struct snd_seq_kinstr_list *list, - struct snd_seq_instr_header *ifree, - int client, - int atomic) -{ - struct snd_seq_kinstr *instr, *prev, *next, *flist; - int idx; - unsigned long flags; - - snd_instr_lock_ops(list); - for (idx = 0; idx < SNDRV_SEQ_INSTR_HASH_SIZE; idx++) { - spin_lock_irqsave(&list->lock, flags); - instr = list->hash[idx]; - prev = flist = NULL; - while (instr) { - while (instr && instr_free_compare(instr, ifree, (unsigned int)client)) { - prev = instr; - instr = instr->next; - } - if (instr == NULL) - continue; - if (instr->ops && instr->ops->notify) - instr->ops->notify(instr->ops->private_data, instr, SNDRV_SEQ_INSTR_NOTIFY_REMOVE); - next = instr->next; - if (prev == NULL) { - list->hash[idx] = next; - } else { - prev->next = next; - } - list->count--; - instr->next = flist; - flist = instr; - instr = next; - } - spin_unlock_irqrestore(&list->lock, flags); - while (flist) { - instr = flist; - flist = instr->next; - while (instr->use) { - schedule_timeout_uninterruptible(1); - barrier(); - } - if (snd_seq_instr_free(instr, atomic)<0) - snd_printk(KERN_WARNING "instrument free problem\n"); - instr = next; - } - } - snd_instr_unlock_ops(list); - return 0; -} - -static int compute_hash_instr_key(struct snd_seq_instr *instr) -{ - int result; - - result = instr->bank | (instr->prg << 16); - result += result >> 24; - result += result >> 16; - result += result >> 8; - return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1); -} - -#if 0 -static int compute_hash_cluster_key(snd_seq_instr_cluster_t cluster) -{ - int result; - - result = cluster; - result += result >> 24; - result += result >> 16; - result += result >> 8; - return result & (SNDRV_SEQ_INSTR_HASH_SIZE-1); -} -#endif - -static int compare_instr(struct snd_seq_instr *i1, struct snd_seq_instr *i2, int exact) -{ - if (exact) { - if (i1->cluster != i2->cluster || - i1->bank != i2->bank || - i1->prg != i2->prg) - return 1; - if ((i1->std & 0xff000000) != (i2->std & 0xff000000)) - return 1; - if (!(i1->std & i2->std)) - return 1; - return 0; - } else { - unsigned int client_check; - - if (i2->cluster && i1->cluster != i2->cluster) - return 1; - client_check = i2->std & 0xff000000; - if (client_check) { - if ((i1->std & 0xff000000) != client_check) - return 1; - } else { - if ((i1->std & i2->std) != i2->std) - return 1; - } - return i1->bank != i2->bank || i1->prg != i2->prg; - } -} - -struct snd_seq_kinstr *snd_seq_instr_find(struct snd_seq_kinstr_list *list, - struct snd_seq_instr *instr, - int exact, - int follow_alias) -{ - unsigned long flags; - int depth = 0; - struct snd_seq_kinstr *result; - - if (list == NULL || instr == NULL) - return NULL; - spin_lock_irqsave(&list->lock, flags); - __again: - result = list->hash[compute_hash_instr_key(instr)]; - while (result) { - if (!compare_instr(&result->instr, instr, exact)) { - if (follow_alias && (result->type == SNDRV_SEQ_INSTR_ATYPE_ALIAS)) { - instr = (struct snd_seq_instr *)KINSTR_DATA(result); - if (++depth > 10) - goto __not_found; - goto __again; - } - result->use++; - spin_unlock_irqrestore(&list->lock, flags); - return result; - } - result = result->next; - } - __not_found: - spin_unlock_irqrestore(&list->lock, flags); - return NULL; -} - -void snd_seq_instr_free_use(struct snd_seq_kinstr_list *list, - struct snd_seq_kinstr *instr) -{ - unsigned long flags; - - if (list == NULL || instr == NULL) - return; - spin_lock_irqsave(&list->lock, flags); - if (instr->use <= 0) { - snd_printk(KERN_ERR "free_use: fatal!!! use = %i, name = '%s'\n", instr->use, instr->name); - } else { - instr->use--; - } - spin_unlock_irqrestore(&list->lock, flags); -} - -static struct snd_seq_kinstr_ops *instr_ops(struct snd_seq_kinstr_ops *ops, - char *instr_type) -{ - while (ops) { - if (!strcmp(ops->instr_type, instr_type)) - return ops; - ops = ops->next; - } - return NULL; -} - -static int instr_result(struct snd_seq_event *ev, - int type, int result, - int atomic) -{ - struct snd_seq_event sev; - - memset(&sev, 0, sizeof(sev)); - sev.type = SNDRV_SEQ_EVENT_RESULT; - sev.flags = SNDRV_SEQ_TIME_STAMP_REAL | SNDRV_SEQ_EVENT_LENGTH_FIXED | - SNDRV_SEQ_PRIORITY_NORMAL; - sev.source = ev->dest; - sev.dest = ev->source; - sev.data.result.event = type; - sev.data.result.result = result; -#if 0 - printk("instr result - type = %i, result = %i, queue = %i, source.client:port = %i:%i, dest.client:port = %i:%i\n", - type, result, - sev.queue, - sev.source.client, sev.source.port, - sev.dest.client, sev.dest.port); -#endif - return snd_seq_kernel_client_dispatch(sev.source.client, &sev, atomic, 0); -} - -static int instr_begin(struct snd_seq_kinstr_ops *ops, - struct snd_seq_kinstr_list *list, - struct snd_seq_event *ev, - int atomic, int hop) -{ - unsigned long flags; - - spin_lock_irqsave(&list->lock, flags); - if (list->owner >= 0 && list->owner != ev->source.client) { - spin_unlock_irqrestore(&list->lock, flags); - return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, -EBUSY, atomic); - } - list->owner = ev->source.client; - spin_unlock_irqrestore(&list->lock, flags); - return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_BEGIN, 0, atomic); -} - -static int instr_end(struct snd_seq_kinstr_ops *ops, - struct snd_seq_kinstr_list *list, - struct snd_seq_event *ev, - int atomic, int hop) -{ - unsigned long flags; - - /* TODO: timeout handling */ - spin_lock_irqsave(&list->lock, flags); - if (list->owner == ev->source.client) { - list->owner = -1; - spin_unlock_irqrestore(&list->lock, flags); - return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, 0, atomic); - } - spin_unlock_irqrestore(&list->lock, flags); - return instr_result(ev, SNDRV_SEQ_EVENT_INSTR_END, -EINVAL, atomic); -} - -static int instr_info(struct snd_seq_kinstr_ops *ops, - struct snd_seq_kinstr_list *list, - struct snd_seq_event *ev, - int atomic, int hop) -{ - return -ENXIO; -} - -static int instr_format_info(struct snd_seq_kinstr_ops *ops, - struct snd_seq_kinstr_list *list, - struct snd_seq_event *ev, - int atomic, int hop) -{ - return -ENXIO; -} - -static int instr_reset(struct snd_seq_kinstr_ops *ops, - struct snd_seq_kinstr_list *list, - struct snd_seq_event *ev, - int atomic, int hop) -{ - return -ENXIO; -} - -static int instr_status(struct snd_seq_kinstr_ops *ops, - struct snd_seq_kinstr_list *list, - struct snd_seq_event *ev, - int atomic, int hop) -{ - return -ENXIO; -} - -static int instr_put(struct snd_seq_kinstr_ops *ops, - struct snd_seq_kinstr_list *list, - struct snd_seq_event *ev, - int atomic, int hop) -{ - unsigned long flags; - struct snd_seq_instr_header put; - struct snd_seq_kinstr *instr; - int result = -EINVAL, len, key; - - if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR) - goto __return; - - if (ev->data.ext.len < sizeof(struct snd_seq_instr_header)) - goto __return; - if (copy_from_user(&put, (void __user *)ev->data.ext.ptr, - sizeof(struct snd_seq_instr_header))) { - result = -EFAULT; - goto __return; - } - snd_instr_lock_ops(list); - if (put.id.instr.std & 0xff000000) { /* private instrument */ - put.id.instr.std &= 0x00ffffff; - put.id.instr.std |= (unsigned int)ev->source.client << 24; - } - if ((instr = snd_seq_instr_find(list, &put.id.instr, 1, 0))) { - snd_seq_instr_free_use(list, instr); - snd_instr_unlock_ops(list); - result = -EBUSY; - goto __return; - } - ops = instr_ops(ops, put.data.data.format); - if (ops == NULL) { - snd_instr_unlock_ops(list); - goto __return; - } - len = ops->add_len; - if (put.data.type == SNDRV_SEQ_INSTR_ATYPE_ALIAS) - len = sizeof(struct snd_seq_instr); - instr = snd_seq_instr_new(len, atomic); - if (instr == NULL) { - snd_instr_unlock_ops(list); - result = -ENOMEM; - goto __return; - } - instr->ops = ops; - instr->instr = put.id.instr; - strlcpy(instr->name, put.data.name, sizeof(instr->name)); - instr->type = put.data.type; - if (instr->type == SNDRV_SEQ_INSTR_ATYPE_DATA) { - result = ops->put(ops->private_data, - instr, - (void __user *)ev->data.ext.ptr + sizeof(struct snd_seq_instr_header), - ev->data.ext.len - sizeof(struct snd_seq_instr_header), - atomic, - put.cmd); - if (result < 0) { - snd_seq_instr_free(instr, atomic); - snd_instr_unlock_ops(list); - goto __return; - } - } - key = compute_hash_instr_key(&instr->instr); - spin_lock_irqsave(&list->lock, flags); - instr->next = list->hash[key]; - list->hash[key] = instr; - list->count++; - spin_unlock_irqrestore(&list->lock, flags); - snd_instr_unlock_ops(list); - result = 0; - __return: - instr_result(ev, SNDRV_SEQ_EVENT_INSTR_PUT, result, atomic); - return result; -} - -static int instr_get(struct snd_seq_kinstr_ops *ops, - struct snd_seq_kinstr_list *list, - struct snd_seq_event *ev, - int atomic, int hop) -{ - return -ENXIO; -} - -static int instr_free(struct snd_seq_kinstr_ops *ops, - struct snd_seq_kinstr_list *list, - struct snd_seq_event *ev, - int atomic, int hop) -{ - struct snd_seq_instr_header ifree; - struct snd_seq_kinstr *instr, *prev; - int result = -EINVAL; - unsigned long flags; - unsigned int hash; - - if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARUSR) - goto __return; - - if (ev->data.ext.len < sizeof(struct snd_seq_instr_header)) - goto __return; - if (copy_from_user(&ifree, (void __user *)ev->data.ext.ptr, - sizeof(struct snd_seq_instr_header))) { - result = -EFAULT; - goto __return; - } - if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_ALL || - ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE || - ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_CLUSTER) { - result = snd_seq_instr_list_free_cond(list, &ifree, ev->dest.client, atomic); - goto __return; - } - if (ifree.cmd == SNDRV_SEQ_INSTR_FREE_CMD_SINGLE) { - if (ifree.id.instr.std & 0xff000000) { - ifree.id.instr.std &= 0x00ffffff; - ifree.id.instr.std |= (unsigned int)ev->source.client << 24; - } - hash = compute_hash_instr_key(&ifree.id.instr); - snd_instr_lock_ops(list); - spin_lock_irqsave(&list->lock, flags); - instr = list->hash[hash]; - prev = NULL; - while (instr) { - if (!compare_instr(&instr->instr, &ifree.id.instr, 1)) - goto __free_single; - prev = instr; - instr = instr->next; - } - result = -ENOENT; - spin_unlock_irqrestore(&list->lock, flags); - snd_instr_unlock_ops(list); - goto __return; - - __free_single: - if (prev) { - prev->next = instr->next; - } else { - list->hash[hash] = instr->next; - } - if (instr->ops && instr->ops->notify) - instr->ops->notify(instr->ops->private_data, instr, - SNDRV_SEQ_INSTR_NOTIFY_REMOVE); - while (instr->use) { - spin_unlock_irqrestore(&list->lock, flags); - schedule_timeout_uninterruptible(1); - spin_lock_irqsave(&list->lock, flags); - } - spin_unlock_irqrestore(&list->lock, flags); - result = snd_seq_instr_free(instr, atomic); - snd_instr_unlock_ops(list); - goto __return; - } - - __return: - instr_result(ev, SNDRV_SEQ_EVENT_INSTR_FREE, result, atomic); - return result; -} - -static int instr_list(struct snd_seq_kinstr_ops *ops, - struct snd_seq_kinstr_list *list, - struct snd_seq_event *ev, - int atomic, int hop) -{ - return -ENXIO; -} - -static int instr_cluster(struct snd_seq_kinstr_ops *ops, - struct snd_seq_kinstr_list *list, - struct snd_seq_event *ev, - int atomic, int hop) -{ - return -ENXIO; -} - -int snd_seq_instr_event(struct snd_seq_kinstr_ops *ops, - struct snd_seq_kinstr_list *list, - struct snd_seq_event *ev, - int client, - int atomic, - int hop) -{ - int direct = 0; - - snd_assert(ops != NULL && list != NULL && ev != NULL, return -EINVAL); - if (snd_seq_ev_is_direct(ev)) { - direct = 1; - switch (ev->type) { - case SNDRV_SEQ_EVENT_INSTR_BEGIN: - return instr_begin(ops, list, ev, atomic, hop); - case SNDRV_SEQ_EVENT_INSTR_END: - return instr_end(ops, list, ev, atomic, hop); - } - } - if ((list->flags & SNDRV_SEQ_INSTR_FLG_DIRECT) && !direct) - return -EINVAL; - switch (ev->type) { - case SNDRV_SEQ_EVENT_INSTR_INFO: - return instr_info(ops, list, ev, atomic, hop); - case SNDRV_SEQ_EVENT_INSTR_FINFO: - return instr_format_info(ops, list, ev, atomic, hop); - case SNDRV_SEQ_EVENT_INSTR_RESET: - return instr_reset(ops, list, ev, atomic, hop); - case SNDRV_SEQ_EVENT_INSTR_STATUS: - return instr_status(ops, list, ev, atomic, hop); - case SNDRV_SEQ_EVENT_INSTR_PUT: - return instr_put(ops, list, ev, atomic, hop); - case SNDRV_SEQ_EVENT_INSTR_GET: - return instr_get(ops, list, ev, atomic, hop); - case SNDRV_SEQ_EVENT_INSTR_FREE: - return instr_free(ops, list, ev, atomic, hop); - case SNDRV_SEQ_EVENT_INSTR_LIST: - return instr_list(ops, list, ev, atomic, hop); - case SNDRV_SEQ_EVENT_INSTR_CLUSTER: - return instr_cluster(ops, list, ev, atomic, hop); - } - return -EINVAL; -} - -/* - * Init part - */ - -static int __init alsa_seq_instr_init(void) -{ - return 0; -} - -static void __exit alsa_seq_instr_exit(void) -{ -} - -module_init(alsa_seq_instr_init) -module_exit(alsa_seq_instr_exit) - -EXPORT_SYMBOL(snd_seq_instr_list_new); -EXPORT_SYMBOL(snd_seq_instr_list_free); -EXPORT_SYMBOL(snd_seq_instr_list_free_cond); -EXPORT_SYMBOL(snd_seq_instr_find); -EXPORT_SYMBOL(snd_seq_instr_free_use); -EXPORT_SYMBOL(snd_seq_instr_event); diff --git a/sound/core/seq/seq_lock.c b/sound/core/seq/seq_lock.c index 1a34941..54f921e 100644 --- a/sound/core/seq/seq_lock.c +++ b/sound/core/seq/seq_lock.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <sound/core.h> #include "seq_lock.h" diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index a72a194..0cf6ac4 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -20,7 +20,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/vmalloc.h> diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c index 5929aaf..99b3536 100644 --- a/sound/core/seq/seq_midi.c +++ b/sound/core/seq/seq_midi.c @@ -26,7 +26,6 @@ Possible options for midisynth module: */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/errno.h> diff --git a/sound/core/seq/seq_midi_emul.c b/sound/core/seq/seq_midi_emul.c index 17b3e6f..07c6631 100644 --- a/sound/core/seq/seq_midi_emul.c +++ b/sound/core/seq/seq_midi_emul.c @@ -29,7 +29,6 @@ * code in here. If there is it should be reported as a bug. */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/string.h> @@ -229,13 +228,6 @@ snd_midi_process_event(struct snd_midi_op *ops, case SNDRV_SEQ_EVENT_PORT_START: case SNDRV_SEQ_EVENT_PORT_EXIT: case SNDRV_SEQ_EVENT_PORT_CHANGE: - case SNDRV_SEQ_EVENT_SAMPLE: - case SNDRV_SEQ_EVENT_SAMPLE_START: - case SNDRV_SEQ_EVENT_SAMPLE_STOP: - case SNDRV_SEQ_EVENT_SAMPLE_FREQ: - case SNDRV_SEQ_EVENT_SAMPLE_VOLUME: - case SNDRV_SEQ_EVENT_SAMPLE_LOOP: - case SNDRV_SEQ_EVENT_SAMPLE_POSITION: case SNDRV_SEQ_EVENT_ECHO: not_yet: default: diff --git a/sound/core/seq/seq_midi_event.c b/sound/core/seq/seq_midi_event.c index b6820a5..8284f17 100644 --- a/sound/core/seq/seq_midi_event.c +++ b/sound/core/seq/seq_midi_event.c @@ -19,7 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/slab.h> #include <linux/errno.h> #include <linux/string.h> diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c index b6e23ad..1c32a53 100644 --- a/sound/core/seq/seq_ports.c +++ b/sound/core/seq/seq_ports.c @@ -20,7 +20,6 @@ * */ -#include <sound/driver.h> #include <sound/core.h> #include <linux/slab.h> #include "seq_system.h" diff --git a/sound/core/seq/seq_prioq.c b/sound/core/seq/seq_prioq.c index 0744186..85969db 100644 --- a/sound/core/seq/seq_prioq.c +++ b/sound/core/seq/seq_prioq.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <linux/slab.h> #include <sound/core.h> diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c index 9b87bb0..4a48c6e 100644 --- a/sound/core/seq/seq_queue.c +++ b/sound/core/seq/seq_queue.c @@ -35,7 +35,6 @@ * - Addition of experimental sync support. */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/slab.h> #include <sound/core.h> diff --git a/sound/core/seq/seq_system.c b/sound/core/seq/seq_system.c index b201b76..77884e6 100644 --- a/sound/core/seq/seq_system.c +++ b/sound/core/seq/seq_system.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <sound/core.h> #include "seq_system.h" diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c index 8716352..d8fcd62 100644 --- a/sound/core/seq/seq_timer.c +++ b/sound/core/seq/seq_timer.c @@ -20,20 +20,12 @@ * */ -#include <sound/driver.h> #include <sound/core.h> #include <linux/slab.h> #include "seq_timer.h" #include "seq_queue.h" #include "seq_info.h" -extern int seq_default_timer_class; -extern int seq_default_timer_sclass; -extern int seq_default_timer_card; -extern int seq_default_timer_device; -extern int seq_default_timer_subdevice; -extern int seq_default_timer_resolution; - /* allowed sequencer timer frequencies, in Hz */ #define MIN_FREQUENCY 10 #define MAX_FREQUENCY 6250 diff --git a/sound/core/seq/seq_timer.h b/sound/core/seq/seq_timer.h index e9ee154..88dfb71 100644 --- a/sound/core/seq/seq_timer.h +++ b/sound/core/seq/seq_timer.h @@ -138,4 +138,11 @@ int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew, unsigne snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr); snd_seq_tick_time_t snd_seq_timer_get_cur_tick(struct snd_seq_timer *tmr); +extern int seq_default_timer_class; +extern int seq_default_timer_sclass; +extern int seq_default_timer_card; +extern int seq_default_timer_device; +extern int seq_default_timer_subdevice; +extern int seq_default_timer_resolution; + #endif diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c index e11790f..86e7739 100644 --- a/sound/core/seq/seq_virmidi.c +++ b/sound/core/seq/seq_virmidi.c @@ -35,7 +35,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/wait.h> #include <linux/slab.h> diff --git a/sound/core/sound.c b/sound/core/sound.c index 7b486c4..00cca4d 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/time.h> diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c index dc73313..7be5154 100644 --- a/sound/core/sound_oss.c +++ b/sound/core/sound_oss.c @@ -19,8 +19,6 @@ * */ -#include <sound/driver.h> - #ifdef CONFIG_SND_OSSEMUL #if !defined(CONFIG_SOUND) && !(defined(MODULE) && defined(CONFIG_SOUND_MODULE)) diff --git a/sound/core/timer.c b/sound/core/timer.c index e7dc56c..aece465 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/slab.h> @@ -44,11 +43,14 @@ #endif static int timer_limit = DEFAULT_TIMER_LIMIT; +static int timer_tstamp_monotonic = 1; MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.de>"); MODULE_DESCRIPTION("ALSA timer interface"); MODULE_LICENSE("GPL"); module_param(timer_limit, int, 0444); MODULE_PARM_DESC(timer_limit, "Maximum global timers in system."); +module_param(timer_tstamp_monotonic, int, 0444); +MODULE_PARM_DESC(timer_tstamp_monotonic, "Use posix monotonic clock source for timestamps (default)."); struct snd_timer_user { struct snd_timer_instance *timeri; @@ -381,7 +383,10 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event) struct snd_timer_instance *ts; struct timespec tstamp; - getnstimeofday(&tstamp); + if (timer_tstamp_monotonic) + do_posix_clock_monotonic_gettime(&tstamp); + else + getnstimeofday(&tstamp); snd_assert(event >= SNDRV_TIMER_EVENT_START && event <= SNDRV_TIMER_EVENT_PAUSE, return); if (event == SNDRV_TIMER_EVENT_START || @@ -1182,8 +1187,12 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri, spin_unlock(&tu->qlock); return; } - if (tu->last_resolution != resolution || ticks > 0) - getnstimeofday(&tstamp); + if (tu->last_resolution != resolution || ticks > 0) { + if (timer_tstamp_monotonic) + do_posix_clock_monotonic_gettime(&tstamp); + else + getnstimeofday(&tstamp); + } if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) && tu->last_resolution != resolution) { r1.event = SNDRV_TIMER_EVENT_RESOLUTION; diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig index 83529b0..75d4fe0 100644 --- a/sound/drivers/Kconfig +++ b/sound/drivers/Kconfig @@ -120,4 +120,16 @@ config SND_PORTMAN2X4 To compile this driver as a module, choose M here: the module will be called snd-portman2x4. +config SND_ML403_AC97CR + tristate "Xilinx ML403 AC97 Controller Reference" + depends on SND && XILINX_VIRTEX + select SND_AC97_CODEC + help + Say Y here to include support for the + opb_ac97_controller_ref_v1_00_a ip core found in Xilinx' ML403 + reference design. + + To compile this driver as a module, choose M here: the module + will be called snd-ml403_ac97cr. + endmenu diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile index 80aeff5..8e55300 100644 --- a/sound/drivers/Makefile +++ b/sound/drivers/Makefile @@ -9,6 +9,7 @@ snd-mts64-objs := mts64.o snd-portman2x4-objs := portman2x4.o snd-serial-u16550-objs := serial-u16550.o snd-virmidi-objs := virmidi.o +snd-ml403-ac97cr-objs := ml403-ac97cr.o pcm-indirect2.o # Toplevel Module Dependency obj-$(CONFIG_SND_DUMMY) += snd-dummy.o @@ -17,5 +18,6 @@ obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o obj-$(CONFIG_SND_MTS64) += snd-mts64.o obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o +obj-$(CONFIG_SND_ML403_AC97CR) += snd-ml403-ac97cr.o obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index e008f3c..a240eae 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -18,7 +18,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/err.h> #include <linux/platform_device.h> diff --git a/sound/drivers/ml403-ac97cr.c b/sound/drivers/ml403-ac97cr.c new file mode 100644 index 0000000..05a871a --- /dev/null +++ b/sound/drivers/ml403-ac97cr.c @@ -0,0 +1,1352 @@ +/* + * ALSA driver for Xilinx ML403 AC97 Controller Reference + * IP: opb_ac97_controller_ref_v1_00_a (EDK 8.1i) + * IP: opb_ac97_controller_ref_v1_00_a (EDK 9.1i) + * + * Copyright (c) by 2007 Joachim Foerster <JOFT@gmx.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 + * + */ + +/* Some notes / status of this driver: + * + * - Don't wonder about some strange implementations of things - especially the + * (heavy) shadowing of codec registers, with which I tried to reduce read + * accesses to a minimum, because after a variable amount of accesses, the AC97 + * controller doesn't raise the register access finished bit anymore ... + * + * - Playback support seems to be pretty stable - no issues here. + * - Capture support "works" now, too. Overruns don't happen any longer so often. + * But there might still be some ... + */ + +#include <linux/init.h> +#include <linux/moduleparam.h> + +#include <linux/platform_device.h> + +#include <linux/ioport.h> +#include <linux/io.h> +#include <linux/interrupt.h> + +/* HZ */ +#include <linux/param.h> +/* jiffies, time_*() */ +#include <linux/jiffies.h> +/* schedule_timeout*() */ +#include <linux/sched.h> +/* spin_lock*() */ +#include <linux/spinlock.h> +/* struct mutex, mutex_init(), mutex_*lock() */ +#include <linux/mutex.h> + +/* snd_printk(), snd_printd() */ +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/ac97_codec.h> + +#include "pcm-indirect2.h" + + +#define SND_ML403_AC97CR_DRIVER "ml403-ac97cr" + +MODULE_AUTHOR("Joachim Foerster <JOFT@gmx.de>"); +MODULE_DESCRIPTION("Xilinx ML403 AC97 Controller Reference"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{Xilinx,ML403 AC97 Controller Reference}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for ML403 AC97 Controller Reference."); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for ML403 AC97 Controller Reference."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable this ML403 AC97 Controller Reference."); + +/* Special feature options */ +/*#define CODEC_WRITE_CHECK_RAF*/ /* don't return after a write to a codec + * register, while RAF bit is not set + */ +/* Debug options for code which may be removed completely in a final version */ +#ifdef CONFIG_SND_DEBUG +/*#define CODEC_STAT*/ /* turn on some minimal "statistics" + * about codec register usage + */ +#define SND_PCM_INDIRECT2_STAT /* turn on some "statistics" about the + * process of copying bytes from the + * intermediate buffer to the hardware + * fifo and the other way round + */ +#endif + +/* Definition of a "level/facility dependent" printk(); may be removed + * completely in a final version + */ +#undef PDEBUG +#ifdef CONFIG_SND_DEBUG +/* "facilities" for PDEBUG */ +#define UNKNOWN (1<<0) +#define CODEC_SUCCESS (1<<1) +#define CODEC_FAKE (1<<2) +#define INIT_INFO (1<<3) +#define INIT_FAILURE (1<<4) +#define WORK_INFO (1<<5) +#define WORK_FAILURE (1<<6) + +#define PDEBUG_FACILITIES (UNKNOWN | INIT_FAILURE | WORK_FAILURE) + +#define PDEBUG(fac, fmt, args...) do { \ + if (fac & PDEBUG_FACILITIES) \ + snd_printd(KERN_DEBUG SND_ML403_AC97CR_DRIVER ": " \ + fmt, ##args); \ + } while (0) +#else +#define PDEBUG(fac, fmt, args...) /* nothing */ +#endif + + + +/* Defines for "waits"/timeouts (portions of HZ=250 on arch/ppc by default) */ +#define CODEC_TIMEOUT_ON_INIT 5 /* timeout for checking for codec + * readiness (after insmod) + */ +#ifndef CODEC_WRITE_CHECK_RAF +#define CODEC_WAIT_AFTER_WRITE 100 /* general, static wait after a write + * access to a codec register, may be + * 0 to completely remove wait + */ +#else +#define CODEC_TIMEOUT_AFTER_WRITE 5 /* timeout after a write access to a + * codec register, if RAF bit is used + */ +#endif +#define CODEC_TIMEOUT_AFTER_READ 5 /* timeout after a read access to a + * codec register (checking RAF bit) + */ + +/* Infrastructure for codec register shadowing */ +#define LM4550_REG_OK (1<<0) /* register exists */ +#define LM4550_REG_DONEREAD (1<<1) /* read register once, value should be + * the same currently in the register + */ +#define LM4550_REG_NOSAVE (1<<2) /* values written to this register will + * not be saved in the register + */ +#define LM4550_REG_NOSHADOW (1<<3) /* don't do register shadowing, use plain + * hardware access + */ +#define LM4550_REG_READONLY (1<<4) /* register is read only */ +#define LM4550_REG_FAKEPROBE (1<<5) /* fake write _and_ read actions during + * probe() correctly + */ +#define LM4550_REG_FAKEREAD (1<<6) /* fake read access, always return + * default value + */ +#define LM4550_REG_ALLFAKE (LM4550_REG_FAKEREAD | LM4550_REG_FAKEPROBE) + +struct lm4550_reg { + u16 value; + u16 flag; + u16 wmask; + u16 def; +}; + +struct lm4550_reg lm4550_regfile[64] = { + [AC97_RESET / 2] = {.flag = LM4550_REG_OK \ + | LM4550_REG_NOSAVE \ + | LM4550_REG_FAKEREAD, + .def = 0x0D50}, + [AC97_MASTER / 2] = {.flag = LM4550_REG_OK + | LM4550_REG_FAKEPROBE, + .wmask = 0x9F1F, + .def = 0x8000}, + [AC97_HEADPHONE / 2] = {.flag = LM4550_REG_OK \ + | LM4550_REG_FAKEPROBE, + .wmask = 0x9F1F, + .def = 0x8000}, + [AC97_MASTER_MONO / 2] = {.flag = LM4550_REG_OK \ + | LM4550_REG_FAKEPROBE, + .wmask = 0x801F, + .def = 0x8000}, + [AC97_PC_BEEP / 2] = {.flag = LM4550_REG_OK \ + | LM4550_REG_FAKEPROBE, + .wmask = 0x801E, + .def = 0x0}, + [AC97_PHONE / 2] = {.flag = LM4550_REG_OK \ + | LM4550_REG_FAKEPROBE, + .wmask = 0x801F, + .def = 0x8008}, + [AC97_MIC / 2] = {.flag = LM4550_REG_OK \ + | LM4550_REG_FAKEPROBE, + .wmask = 0x805F, + .def = 0x8008}, + [AC97_LINE / 2] = {.flag = LM4550_REG_OK \ + | LM4550_REG_FAKEPROBE, + .wmask = 0x9F1F, + .def = 0x8808}, + [AC97_CD / 2] = {.flag = LM4550_REG_OK \ + | LM4550_REG_FAKEPROBE, + .wmask = 0x9F1F, + .def = 0x8808}, + [AC97_VIDEO / 2] = {.flag = LM4550_REG_OK \ + | LM4550_REG_FAKEPROBE, + .wmask = 0x9F1F, + .def = 0x8808}, + [AC97_AUX / 2] = {.flag = LM4550_REG_OK \ + | LM4550_REG_FAKEPROBE, + .wmask = 0x9F1F, + .def = 0x8808}, + [AC97_PCM / 2] = {.flag = LM4550_REG_OK \ + | LM4550_REG_FAKEPROBE, + .wmask = 0x9F1F, + .def = 0x8008}, + [AC97_REC_SEL / 2] = {.flag = LM4550_REG_OK \ + | LM4550_REG_FAKEPROBE, + .wmask = 0x707, + .def = 0x0}, + [AC97_REC_GAIN / 2] = {.flag = LM4550_REG_OK \ + | LM4550_REG_FAKEPROBE, + .wmask = 0x8F0F, + .def = 0x8000}, + [AC97_GENERAL_PURPOSE / 2] = {.flag = LM4550_REG_OK \ + | LM4550_REG_FAKEPROBE, + .def = 0x0, + .wmask = 0xA380}, + [AC97_3D_CONTROL / 2] = {.flag = LM4550_REG_OK \ + | LM4550_REG_FAKEREAD \ + | LM4550_REG_READONLY, + .def = 0x0101}, + [AC97_POWERDOWN / 2] = {.flag = LM4550_REG_OK \ + | LM4550_REG_NOSHADOW \ + | LM4550_REG_NOSAVE, + .wmask = 0xFF00}, + /* may not write ones to + * REF/ANL/DAC/ADC bits + * FIXME: Is this ok? + */ + [AC97_EXTENDED_ID / 2] = {.flag = LM4550_REG_OK \ + | LM4550_REG_FAKEREAD \ + | LM4550_REG_READONLY, + .def = 0x0201}, /* primary codec */ + [AC97_EXTENDED_STATUS / 2] = {.flag = LM4550_REG_OK \ + | LM4550_REG_NOSHADOW \ + | LM4550_REG_NOSAVE, + .wmask = 0x1}, + [AC97_PCM_FRONT_DAC_RATE / 2] = {.flag = LM4550_REG_OK \ + | LM4550_REG_FAKEPROBE, + .def = 0xBB80, + .wmask = 0xFFFF}, + [AC97_PCM_LR_ADC_RATE / 2] = {.flag = LM4550_REG_OK \ + | LM4550_REG_FAKEPROBE, + .def = 0xBB80, + .wmask = 0xFFFF}, + [AC97_VENDOR_ID1 / 2] = {.flag = LM4550_REG_OK \ + | LM4550_REG_READONLY \ + | LM4550_REG_FAKEREAD, + .def = 0x4E53}, + [AC97_VENDOR_ID2 / 2] = {.flag = LM4550_REG_OK \ + | LM4550_REG_READONLY \ + | LM4550_REG_FAKEREAD, + .def = 0x4350} +}; + +#define LM4550_RF_OK(reg) (lm4550_regfile[reg / 2].flag & LM4550_REG_OK) + +static void lm4550_regfile_init(void) +{ + int i; + for (i = 0; i < 64; i++) + if (lm4550_regfile[i].flag & LM4550_REG_FAKEPROBE) + lm4550_regfile[i].value = lm4550_regfile[i].def; +} + +static void lm4550_regfile_write_values_after_init(struct snd_ac97 *ac97) +{ + int i; + for (i = 0; i < 64; i++) + if ((lm4550_regfile[i].flag & LM4550_REG_FAKEPROBE) && + (lm4550_regfile[i].value != lm4550_regfile[i].def)) { + PDEBUG(CODEC_FAKE, "lm4550_regfile_write_values_after_" + "init(): reg=0x%x value=0x%x / %d is different " + "from def=0x%x / %d\n", + i, lm4550_regfile[i].value, + lm4550_regfile[i].value, lm4550_regfile[i].def, + lm4550_regfile[i].def); + snd_ac97_write(ac97, i * 2, lm4550_regfile[i].value); + lm4550_regfile[i].flag |= LM4550_REG_DONEREAD; + } +} + + +/* direct registers */ +#define CR_REG(ml403_ac97cr, x) ((ml403_ac97cr)->port + CR_REG_##x) + +#define CR_REG_PLAYFIFO 0x00 +#define CR_PLAYDATA(a) ((a) & 0xFFFF) + +#define CR_REG_RECFIFO 0x04 +#define CR_RECDATA(a) ((a) & 0xFFFF) + +#define CR_REG_STATUS 0x08 +#define CR_RECOVER (1<<7) +#define CR_PLAYUNDER (1<<6) +#define CR_CODECREADY (1<<5) +#define CR_RAF (1<<4) +#define CR_RECEMPTY (1<<3) +#define CR_RECFULL (1<<2) +#define CR_PLAYHALF (1<<1) +#define CR_PLAYFULL (1<<0) + +#define CR_REG_RESETFIFO 0x0C +#define CR_RECRESET (1<<1) +#define CR_PLAYRESET (1<<0) + +#define CR_REG_CODEC_ADDR 0x10 +/* UG082 says: + * #define CR_CODEC_ADDR(a) ((a) << 1) + * #define CR_CODEC_READ (1<<0) + * #define CR_CODEC_WRITE (0<<0) + */ +/* RefDesign example says: */ +#define CR_CODEC_ADDR(a) ((a) << 0) +#define CR_CODEC_READ (1<<7) +#define CR_CODEC_WRITE (0<<7) + +#define CR_REG_CODEC_DATAREAD 0x14 +#define CR_CODEC_DATAREAD(v) ((v) & 0xFFFF) + +#define CR_REG_CODEC_DATAWRITE 0x18 +#define CR_CODEC_DATAWRITE(v) ((v) & 0xFFFF) + +#define CR_FIFO_SIZE 32 + +struct snd_ml403_ac97cr { + /* lock for access to (controller) registers */ + spinlock_t reg_lock; + /* mutex for the whole sequence of accesses to (controller) registers + * which affect codec registers + */ + struct mutex cdc_mutex; + + int irq; /* for playback */ + int enable_irq; /* for playback */ + + int capture_irq; + int enable_capture_irq; + + struct resource *res_port; + void *port; + + struct snd_ac97 *ac97; + int ac97_fake; +#ifdef CODEC_STAT + int ac97_read; + int ac97_write; +#endif + + struct platform_device *pfdev; + struct snd_card *card; + struct snd_pcm *pcm; + struct snd_pcm_substream *playback_substream; + struct snd_pcm_substream *capture_substream; + + struct snd_pcm_indirect2 ind_rec; /* for playback */ + struct snd_pcm_indirect2 capture_ind2_rec; +}; + +static struct snd_pcm_hardware snd_ml403_ac97cr_playback = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S16_BE, + .rates = (SNDRV_PCM_RATE_CONTINUOUS | + SNDRV_PCM_RATE_8000_48000), + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = CR_FIFO_SIZE/2, + .period_bytes_max = (64*1024), + .periods_min = 2, + .periods_max = (128*1024)/(CR_FIFO_SIZE/2), + .fifo_size = 0, +}; + +static struct snd_pcm_hardware snd_ml403_ac97cr_capture = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S16_BE, + .rates = (SNDRV_PCM_RATE_CONTINUOUS | + SNDRV_PCM_RATE_8000_48000), + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = CR_FIFO_SIZE/2, + .period_bytes_max = (64*1024), + .periods_min = 2, + .periods_max = (128*1024)/(CR_FIFO_SIZE/2), + .fifo_size = 0, +}; + +static size_t +snd_ml403_ac97cr_playback_ind2_zero(struct snd_pcm_substream *substream, + struct snd_pcm_indirect2 *rec) +{ + struct snd_ml403_ac97cr *ml403_ac97cr; + int copied_words = 0; + u32 full = 0; + + ml403_ac97cr = snd_pcm_substream_chip(substream); + + spin_lock(&ml403_ac97cr->reg_lock); + while ((full = (in_be32(CR_REG(ml403_ac97cr, STATUS)) & + CR_PLAYFULL)) != CR_PLAYFULL) { + out_be32(CR_REG(ml403_ac97cr, PLAYFIFO), 0); + copied_words++; + } + rec->hw_ready = 0; + spin_unlock(&ml403_ac97cr->reg_lock); + + return (size_t) (copied_words * 2); +} + +static size_t +snd_ml403_ac97cr_playback_ind2_copy(struct snd_pcm_substream *substream, + struct snd_pcm_indirect2 *rec, + size_t bytes) +{ + struct snd_ml403_ac97cr *ml403_ac97cr; + u16 *src; + int copied_words = 0; + u32 full = 0; + + ml403_ac97cr = snd_pcm_substream_chip(substream); + src = (u16 *)(substream->runtime->dma_area + rec->sw_data); + + spin_lock(&ml403_ac97cr->reg_lock); + while (((full = (in_be32(CR_REG(ml403_ac97cr, STATUS)) & + CR_PLAYFULL)) != CR_PLAYFULL) && (bytes > 1)) { + out_be32(CR_REG(ml403_ac97cr, PLAYFIFO), + CR_PLAYDATA(src[copied_words])); + copied_words++; + bytes = bytes - 2; + } + if (full != CR_PLAYFULL) + rec->hw_ready = 1; + else + rec->hw_ready = 0; + spin_unlock(&ml403_ac97cr->reg_lock); + + return (size_t) (copied_words * 2); +} + +static size_t +snd_ml403_ac97cr_capture_ind2_null(struct snd_pcm_substream *substream, + struct snd_pcm_indirect2 *rec) +{ + struct snd_ml403_ac97cr *ml403_ac97cr; + int copied_words = 0; + u32 empty = 0; + + ml403_ac97cr = snd_pcm_substream_chip(substream); + + spin_lock(&ml403_ac97cr->reg_lock); + while ((empty = (in_be32(CR_REG(ml403_ac97cr, STATUS)) & + CR_RECEMPTY)) != CR_RECEMPTY) { + volatile u32 trash; + + trash = CR_RECDATA(in_be32(CR_REG(ml403_ac97cr, RECFIFO))); + /* Hmmmm, really necessary? Don't want call to in_be32() + * to be optimised away! + */ + trash++; + copied_words++; + } + rec->hw_ready = 0; + spin_unlock(&ml403_ac97cr->reg_lock); + + return (size_t) (copied_words * 2); +} + +static size_t +snd_ml403_ac97cr_capture_ind2_copy(struct snd_pcm_substream *substream, + struct snd_pcm_indirect2 *rec, size_t bytes) +{ + struct snd_ml403_ac97cr *ml403_ac97cr; + u16 *dst; + int copied_words = 0; + u32 empty = 0; + + ml403_ac97cr = snd_pcm_substream_chip(substream); + dst = (u16 *)(substream->runtime->dma_area + rec->sw_data); + + spin_lock(&ml403_ac97cr->reg_lock); + while (((empty = (in_be32(CR_REG(ml403_ac97cr, STATUS)) & + CR_RECEMPTY)) != CR_RECEMPTY) && (bytes > 1)) { + dst[copied_words] = CR_RECDATA(in_be32(CR_REG(ml403_ac97cr, + RECFIFO))); + copied_words++; + bytes = bytes - 2; + } + if (empty != CR_RECEMPTY) + rec->hw_ready = 1; + else + rec->hw_ready = 0; + spin_unlock(&ml403_ac97cr->reg_lock); + + return (size_t) (copied_words * 2); +} + +static snd_pcm_uframes_t +snd_ml403_ac97cr_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_ml403_ac97cr *ml403_ac97cr; + struct snd_pcm_indirect2 *ind2_rec = NULL; + + ml403_ac97cr = snd_pcm_substream_chip(substream); + + if (substream == ml403_ac97cr->playback_substream) + ind2_rec = &ml403_ac97cr->ind_rec; + if (substream == ml403_ac97cr->capture_substream) + ind2_rec = &ml403_ac97cr->capture_ind2_rec; + + if (ind2_rec != NULL) + return snd_pcm_indirect2_pointer(substream, ind2_rec); + return (snd_pcm_uframes_t) 0; +} + +static int +snd_ml403_ac97cr_pcm_playback_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct snd_ml403_ac97cr *ml403_ac97cr; + int err = 0; + + ml403_ac97cr = snd_pcm_substream_chip(substream); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + PDEBUG(WORK_INFO, "trigger(playback): START\n"); + ml403_ac97cr->ind_rec.hw_ready = 1; + + /* clear play FIFO */ + out_be32(CR_REG(ml403_ac97cr, RESETFIFO), CR_PLAYRESET); + + /* enable play irq */ + ml403_ac97cr->enable_irq = 1; + enable_irq(ml403_ac97cr->irq); + break; + case SNDRV_PCM_TRIGGER_STOP: + PDEBUG(WORK_INFO, "trigger(playback): STOP\n"); + ml403_ac97cr->ind_rec.hw_ready = 0; +#ifdef SND_PCM_INDIRECT2_STAT + snd_pcm_indirect2_stat(substream, &ml403_ac97cr->ind_rec); +#endif + /* disable play irq */ + disable_irq_nosync(ml403_ac97cr->irq); + ml403_ac97cr->enable_irq = 0; + break; + default: + err = -EINVAL; + break; + } + PDEBUG(WORK_INFO, "trigger(playback): (done)\n"); + return err; +} + +static int +snd_ml403_ac97cr_pcm_capture_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct snd_ml403_ac97cr *ml403_ac97cr; + int err = 0; + + ml403_ac97cr = snd_pcm_substream_chip(substream); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + PDEBUG(WORK_INFO, "trigger(capture): START\n"); + ml403_ac97cr->capture_ind2_rec.hw_ready = 0; + + /* clear record FIFO */ + out_be32(CR_REG(ml403_ac97cr, RESETFIFO), CR_RECRESET); + + /* enable record irq */ + ml403_ac97cr->enable_capture_irq = 1; + enable_irq(ml403_ac97cr->capture_irq); + break; + case SNDRV_PCM_TRIGGER_STOP: + PDEBUG(WORK_INFO, "trigger(capture): STOP\n"); + ml403_ac97cr->capture_ind2_rec.hw_ready = 0; +#ifdef SND_PCM_INDIRECT2_STAT + snd_pcm_indirect2_stat(substream, + &ml403_ac97cr->capture_ind2_rec); +#endif + /* disable capture irq */ + disable_irq_nosync(ml403_ac97cr->capture_irq); + ml403_ac97cr->enable_capture_irq = 0; + break; + default: + err = -EINVAL; + break; + } + PDEBUG(WORK_INFO, "trigger(capture): (done)\n"); + return err; +} + +static int +snd_ml403_ac97cr_pcm_playback_prepare(struct snd_pcm_substream *substream) +{ + struct snd_ml403_ac97cr *ml403_ac97cr; + struct snd_pcm_runtime *runtime; + + ml403_ac97cr = snd_pcm_substream_chip(substream); + runtime = substream->runtime; + + PDEBUG(WORK_INFO, + "prepare(): period_bytes=%d, minperiod_bytes=%d\n", + snd_pcm_lib_period_bytes(substream), CR_FIFO_SIZE / 2); + + /* set sampling rate */ + snd_ac97_set_rate(ml403_ac97cr->ac97, AC97_PCM_FRONT_DAC_RATE, + runtime->rate); + PDEBUG(WORK_INFO, "prepare(): rate=%d\n", runtime->rate); + + /* init struct for intermediate buffer */ + memset(&ml403_ac97cr->ind_rec, 0, + sizeof(struct snd_pcm_indirect2)); + ml403_ac97cr->ind_rec.hw_buffer_size = CR_FIFO_SIZE; + ml403_ac97cr->ind_rec.sw_buffer_size = + snd_pcm_lib_buffer_bytes(substream); + ml403_ac97cr->ind_rec.min_periods = -1; + ml403_ac97cr->ind_rec.min_multiple = + snd_pcm_lib_period_bytes(substream) / (CR_FIFO_SIZE / 2); + PDEBUG(WORK_INFO, "prepare(): hw_buffer_size=%d, " + "sw_buffer_size=%d, min_multiple=%d\n", + CR_FIFO_SIZE, ml403_ac97cr->ind_rec.sw_buffer_size, + ml403_ac97cr->ind_rec.min_multiple); + return 0; +} + +static int +snd_ml403_ac97cr_pcm_capture_prepare(struct snd_pcm_substream *substream) +{ + struct snd_ml403_ac97cr *ml403_ac97cr; + struct snd_pcm_runtime *runtime; + + ml403_ac97cr = snd_pcm_substream_chip(substream); + runtime = substream->runtime; + + PDEBUG(WORK_INFO, + "prepare(capture): period_bytes=%d, minperiod_bytes=%d\n", + snd_pcm_lib_period_bytes(substream), CR_FIFO_SIZE / 2); + + /* set sampling rate */ + snd_ac97_set_rate(ml403_ac97cr->ac97, AC97_PCM_LR_ADC_RATE, + runtime->rate); + PDEBUG(WORK_INFO, "prepare(capture): rate=%d\n", runtime->rate); + + /* init struct for intermediate buffer */ + memset(&ml403_ac97cr->capture_ind2_rec, 0, + sizeof(struct snd_pcm_indirect2)); + ml403_ac97cr->capture_ind2_rec.hw_buffer_size = CR_FIFO_SIZE; + ml403_ac97cr->capture_ind2_rec.sw_buffer_size = + snd_pcm_lib_buffer_bytes(substream); + ml403_ac97cr->capture_ind2_rec.min_multiple = + snd_pcm_lib_period_bytes(substream) / (CR_FIFO_SIZE / 2); + PDEBUG(WORK_INFO, "prepare(capture): hw_buffer_size=%d, " + "sw_buffer_size=%d, min_multiple=%d\n", CR_FIFO_SIZE, + ml403_ac97cr->capture_ind2_rec.sw_buffer_size, + ml403_ac97cr->capture_ind2_rec.min_multiple); + return 0; +} + +static int snd_ml403_ac97cr_hw_free(struct snd_pcm_substream *substream) +{ + PDEBUG(WORK_INFO, "hw_free()\n"); + return snd_pcm_lib_free_pages(substream); +} + +static int +snd_ml403_ac97cr_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + PDEBUG(WORK_INFO, "hw_params(): desired buffer bytes=%d, desired " + "period bytes=%d\n", + params_buffer_bytes(hw_params), params_period_bytes(hw_params)); + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); +} + +static int snd_ml403_ac97cr_playback_open(struct snd_pcm_substream *substream) +{ + struct snd_ml403_ac97cr *ml403_ac97cr; + struct snd_pcm_runtime *runtime; + + ml403_ac97cr = snd_pcm_substream_chip(substream); + runtime = substream->runtime; + + PDEBUG(WORK_INFO, "open(playback)\n"); + ml403_ac97cr->playback_substream = substream; + runtime->hw = snd_ml403_ac97cr_playback; + + snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + CR_FIFO_SIZE / 2); + return 0; +} + +static int snd_ml403_ac97cr_capture_open(struct snd_pcm_substream *substream) +{ + struct snd_ml403_ac97cr *ml403_ac97cr; + struct snd_pcm_runtime *runtime; + + ml403_ac97cr = snd_pcm_substream_chip(substream); + runtime = substream->runtime; + + PDEBUG(WORK_INFO, "open(capture)\n"); + ml403_ac97cr->capture_substream = substream; + runtime->hw = snd_ml403_ac97cr_capture; + + snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + CR_FIFO_SIZE / 2); + return 0; +} + +static int snd_ml403_ac97cr_playback_close(struct snd_pcm_substream *substream) +{ + struct snd_ml403_ac97cr *ml403_ac97cr; + + ml403_ac97cr = snd_pcm_substream_chip(substream); + + PDEBUG(WORK_INFO, "close(playback)\n"); + ml403_ac97cr->playback_substream = NULL; + return 0; +} + +static int snd_ml403_ac97cr_capture_close(struct snd_pcm_substream *substream) +{ + struct snd_ml403_ac97cr *ml403_ac97cr; + + ml403_ac97cr = snd_pcm_substream_chip(substream); + + PDEBUG(WORK_INFO, "close(capture)\n"); + ml403_ac97cr->capture_substream = NULL; + return 0; +} + +static struct snd_pcm_ops snd_ml403_ac97cr_playback_ops = { + .open = snd_ml403_ac97cr_playback_open, + .close = snd_ml403_ac97cr_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ml403_ac97cr_hw_params, + .hw_free = snd_ml403_ac97cr_hw_free, + .prepare = snd_ml403_ac97cr_pcm_playback_prepare, + .trigger = snd_ml403_ac97cr_pcm_playback_trigger, + .pointer = snd_ml403_ac97cr_pcm_pointer, +}; + +static struct snd_pcm_ops snd_ml403_ac97cr_capture_ops = { + .open = snd_ml403_ac97cr_capture_open, + .close = snd_ml403_ac97cr_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ml403_ac97cr_hw_params, + .hw_free = snd_ml403_ac97cr_hw_free, + .prepare = snd_ml403_ac97cr_pcm_capture_prepare, + .trigger = snd_ml403_ac97cr_pcm_capture_trigger, + .pointer = snd_ml403_ac97cr_pcm_pointer, +}; + +static irqreturn_t snd_ml403_ac97cr_irq(int irq, void *dev_id) +{ + struct snd_ml403_ac97cr *ml403_ac97cr; + struct platform_device *pfdev; + int cmp_irq; + + ml403_ac97cr = (struct snd_ml403_ac97cr *)dev_id; + if (ml403_ac97cr == NULL) + return IRQ_NONE; + + pfdev = ml403_ac97cr->pfdev; + + /* playback interrupt */ + cmp_irq = platform_get_irq(pfdev, 0); + if (irq == cmp_irq) { + if (ml403_ac97cr->enable_irq) + snd_pcm_indirect2_playback_interrupt( + ml403_ac97cr->playback_substream, + &ml403_ac97cr->ind_rec, + snd_ml403_ac97cr_playback_ind2_copy, + snd_ml403_ac97cr_playback_ind2_zero); + else + goto __disable_irq; + } else { + /* record interrupt */ + cmp_irq = platform_get_irq(pfdev, 1); + if (irq == cmp_irq) { + if (ml403_ac97cr->enable_capture_irq) + snd_pcm_indirect2_capture_interrupt( + ml403_ac97cr->capture_substream, + &ml403_ac97cr->capture_ind2_rec, + snd_ml403_ac97cr_capture_ind2_copy, + snd_ml403_ac97cr_capture_ind2_null); + else + goto __disable_irq; + } else + return IRQ_NONE; + } + return IRQ_HANDLED; + +__disable_irq: + PDEBUG(INIT_INFO, "irq(): irq %d is meant to be disabled! So, now try " + "to disable it _really_!\n", irq); + disable_irq_nosync(irq); + return IRQ_HANDLED; +} + +static unsigned short +snd_ml403_ac97cr_codec_read(struct snd_ac97 *ac97, unsigned short reg) +{ + struct snd_ml403_ac97cr *ml403_ac97cr = ac97->private_data; +#ifdef CODEC_STAT + u32 stat; + u32 rafaccess = 0; +#endif + unsigned long end_time; + u16 value = 0; + + if (!LM4550_RF_OK(reg)) { + snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": " + "access to unknown/unused codec register 0x%x " + "ignored!\n", reg); + return 0; + } + /* check if we can fake/answer this access from our shadow register */ + if ((lm4550_regfile[reg / 2].flag & + (LM4550_REG_DONEREAD | LM4550_REG_ALLFAKE)) && + !(lm4550_regfile[reg / 2].flag & LM4550_REG_NOSHADOW)) { + if (lm4550_regfile[reg / 2].flag & LM4550_REG_FAKEREAD) { + PDEBUG(CODEC_FAKE, "codec_read(): faking read from " + "reg=0x%x, val=0x%x / %d\n", + reg, lm4550_regfile[reg / 2].def, + lm4550_regfile[reg / 2].def); + return lm4550_regfile[reg / 2].def; + } else if ((lm4550_regfile[reg / 2].flag & + LM4550_REG_FAKEPROBE) && + ml403_ac97cr->ac97_fake) { + PDEBUG(CODEC_FAKE, "codec_read(): faking read from " + "reg=0x%x, val=0x%x / %d (probe)\n", + reg, lm4550_regfile[reg / 2].value, + lm4550_regfile[reg / 2].value); + return lm4550_regfile[reg / 2].value; + } else { +#ifdef CODEC_STAT + PDEBUG(CODEC_FAKE, "codec_read(): read access " + "answered by shadow register 0x%x (value=0x%x " + "/ %d) (cw=%d cr=%d)\n", + reg, lm4550_regfile[reg / 2].value, + lm4550_regfile[reg / 2].value, + ml403_ac97cr->ac97_write, + ml403_ac97cr->ac97_read); +#else + PDEBUG(CODEC_FAKE, "codec_read(): read access " + "answered by shadow register 0x%x (value=0x%x " + "/ %d)\n", + reg, lm4550_regfile[reg / 2].value, + lm4550_regfile[reg / 2].value); +#endif + return lm4550_regfile[reg / 2].value; + } + } + /* if we are here, we _have_ to access the codec really, no faking */ + if (mutex_lock_interruptible(&ml403_ac97cr->cdc_mutex) != 0) + return 0; +#ifdef CODEC_STAT + ml403_ac97cr->ac97_read++; +#endif + spin_lock(&ml403_ac97cr->reg_lock); + out_be32(CR_REG(ml403_ac97cr, CODEC_ADDR), + CR_CODEC_ADDR(reg) | CR_CODEC_READ); + spin_unlock(&ml403_ac97cr->reg_lock); + end_time = jiffies + (HZ / CODEC_TIMEOUT_AFTER_READ); + do { + spin_lock(&ml403_ac97cr->reg_lock); +#ifdef CODEC_STAT + rafaccess++; + stat = in_be32(CR_REG(ml403_ac97cr, STATUS)); + if ((stat & CR_RAF) == CR_RAF) { + value = CR_CODEC_DATAREAD( + in_be32(CR_REG(ml403_ac97cr, CODEC_DATAREAD))); + PDEBUG(CODEC_SUCCESS, "codec_read(): (done) reg=0x%x, " + "value=0x%x / %d (STATUS=0x%x)\n", + reg, value, value, stat); +#else + if ((in_be32(CR_REG(ml403_ac97cr, STATUS)) & + CR_RAF) == CR_RAF) { + value = CR_CODEC_DATAREAD( + in_be32(CR_REG(ml403_ac97cr, CODEC_DATAREAD))); + PDEBUG(CODEC_SUCCESS, "codec_read(): (done) " + "reg=0x%x, value=0x%x / %d\n", + reg, value, value); +#endif + lm4550_regfile[reg / 2].value = value; + lm4550_regfile[reg / 2].flag |= LM4550_REG_DONEREAD; + spin_unlock(&ml403_ac97cr->reg_lock); + mutex_unlock(&ml403_ac97cr->cdc_mutex); + return value; + } + spin_unlock(&ml403_ac97cr->reg_lock); + schedule_timeout_uninterruptible(1); + } while (time_after(end_time, jiffies)); + /* read the DATAREAD register anyway, see comment below */ + spin_lock(&ml403_ac97cr->reg_lock); + value = + CR_CODEC_DATAREAD(in_be32(CR_REG(ml403_ac97cr, CODEC_DATAREAD))); + spin_unlock(&ml403_ac97cr->reg_lock); +#ifdef CODEC_STAT + snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": " + "timeout while codec read! " + "(reg=0x%x, last STATUS=0x%x, DATAREAD=0x%x / %d, %d) " + "(cw=%d, cr=%d)\n", + reg, stat, value, value, rafaccess, + ml403_ac97cr->ac97_write, ml403_ac97cr->ac97_read); +#else + snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": " + "timeout while codec read! " + "(reg=0x%x, DATAREAD=0x%x / %d)\n", + reg, value, value); +#endif + /* BUG: This is PURE speculation! But after _most_ read timeouts the + * value in the register is ok! + */ + lm4550_regfile[reg / 2].value = value; + lm4550_regfile[reg / 2].flag |= LM4550_REG_DONEREAD; + mutex_unlock(&ml403_ac97cr->cdc_mutex); + return value; +} + +static void +snd_ml403_ac97cr_codec_write(struct snd_ac97 *ac97, unsigned short reg, + unsigned short val) +{ + struct snd_ml403_ac97cr *ml403_ac97cr = ac97->private_data; + +#ifdef CODEC_STAT + u32 stat; + u32 rafaccess = 0; +#endif +#ifdef CODEC_WRITE_CHECK_RAF + unsigned long end_time; +#endif + + if (!LM4550_RF_OK(reg)) { + snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": " + "access to unknown/unused codec register 0x%x " + "ignored!\n", reg); + return; + } + if (lm4550_regfile[reg / 2].flag & LM4550_REG_READONLY) { + snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": " + "write access to read only codec register 0x%x " + "ignored!\n", reg); + return; + } + if ((val & lm4550_regfile[reg / 2].wmask) != val) { + snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": " + "write access to codec register 0x%x " + "with bad value 0x%x / %d!\n", + reg, val, val); + val = val & lm4550_regfile[reg / 2].wmask; + } + if (((lm4550_regfile[reg / 2].flag & LM4550_REG_FAKEPROBE) && + ml403_ac97cr->ac97_fake) && + !(lm4550_regfile[reg / 2].flag & LM4550_REG_NOSHADOW)) { + PDEBUG(CODEC_FAKE, "codec_write(): faking write to reg=0x%x, " + "val=0x%x / %d\n", reg, val, val); + lm4550_regfile[reg / 2].value = (val & + lm4550_regfile[reg / 2].wmask); + return; + } + if (mutex_lock_interruptible(&ml403_ac97cr->cdc_mutex) != 0) + return; +#ifdef CODEC_STAT + ml403_ac97cr->ac97_write++; +#endif + spin_lock(&ml403_ac97cr->reg_lock); + out_be32(CR_REG(ml403_ac97cr, CODEC_DATAWRITE), + CR_CODEC_DATAWRITE(val)); + out_be32(CR_REG(ml403_ac97cr, CODEC_ADDR), + CR_CODEC_ADDR(reg) | CR_CODEC_WRITE); + spin_unlock(&ml403_ac97cr->reg_lock); +#ifdef CODEC_WRITE_CHECK_RAF + /* check CR_CODEC_RAF bit to see if write access to register is done; + * loop until bit is set or timeout happens + */ + end_time = jiffies + HZ / CODEC_TIMEOUT_AFTER_WRITE; + do { + spin_lock(&ml403_ac97cr->reg_lock); +#ifdef CODEC_STAT + rafaccess++; + stat = in_be32(CR_REG(ml403_ac97cr, STATUS)) + if ((stat & CR_RAF) == CR_RAF) { +#else + if ((in_be32(CR_REG(ml403_ac97cr, STATUS)) & + CR_RAF) == CR_RAF) { +#endif + PDEBUG(CODEC_SUCCESS, "codec_write(): (done) " + "reg=0x%x, value=%d / 0x%x\n", + reg, val, val); + if (!(lm4550_regfile[reg / 2].flag & + LM4550_REG_NOSHADOW) && + !(lm4550_regfile[reg / 2].flag & + LM4550_REG_NOSAVE)) + lm4550_regfile[reg / 2].value = val; + lm4550_regfile[reg / 2].flag |= LM4550_REG_DONEREAD; + spin_unlock(&ml403_ac97cr->reg_lock); + mutex_unlock(&ml403_ac97cr->cdc_mutex); + return; + } + spin_unlock(&ml403_ac97cr->reg_lock); + schedule_timeout_uninterruptible(1); + } while (time_after(end_time, jiffies)); +#ifdef CODEC_STAT + snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": " + "timeout while codec write " + "(reg=0x%x, val=0x%x / %d, last STATUS=0x%x, %d) " + "(cw=%d, cr=%d)\n", + reg, val, val, stat, rafaccess, ml403_ac97cr->ac97_write, + ml403_ac97cr->ac97_read); +#else + snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": " + "timeout while codec write (reg=0x%x, val=0x%x / %d)\n", + reg, val, val); +#endif +#else /* CODEC_WRITE_CHECK_RAF */ +#if CODEC_WAIT_AFTER_WRITE > 0 + /* officially, in AC97 spec there is no possibility for a AC97 + * controller to determine, if write access is done or not - so: How + * is Xilinx able to provide a RAF bit for write access? + * => very strange, thus just don't check RAF bit (compare with + * Xilinx's example app in EDK 8.1i) and wait + */ + schedule_timeout_uninterruptible(HZ / CODEC_WAIT_AFTER_WRITE); +#endif + PDEBUG(CODEC_SUCCESS, "codec_write(): (done) " + "reg=0x%x, value=%d / 0x%x (no RAF check)\n", + reg, val, val); +#endif + mutex_unlock(&ml403_ac97cr->cdc_mutex); + return; +} + +static int __devinit +snd_ml403_ac97cr_chip_init(struct snd_ml403_ac97cr *ml403_ac97cr) +{ + unsigned long end_time; + PDEBUG(INIT_INFO, "chip_init():\n"); + end_time = jiffies + HZ / CODEC_TIMEOUT_ON_INIT; + do { + if (in_be32(CR_REG(ml403_ac97cr, STATUS)) & CR_CODECREADY) { + /* clear both hardware FIFOs */ + out_be32(CR_REG(ml403_ac97cr, RESETFIFO), + CR_RECRESET | CR_PLAYRESET); + PDEBUG(INIT_INFO, "chip_init(): (done)\n"); + return 0; + } + schedule_timeout_uninterruptible(1); + } while (time_after(end_time, jiffies)); + snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": " + "timeout while waiting for codec, " + "not ready!\n"); + return -EBUSY; +} + +static int snd_ml403_ac97cr_free(struct snd_ml403_ac97cr *ml403_ac97cr) +{ + PDEBUG(INIT_INFO, "free():\n"); + /* irq release */ + if (ml403_ac97cr->irq >= 0) + free_irq(ml403_ac97cr->irq, ml403_ac97cr); + if (ml403_ac97cr->capture_irq >= 0) + free_irq(ml403_ac97cr->capture_irq, ml403_ac97cr); + /* give back "port" */ + if (ml403_ac97cr->port != NULL) + iounmap(ml403_ac97cr->port); + kfree(ml403_ac97cr); + PDEBUG(INIT_INFO, "free(): (done)\n"); + return 0; +} + +static int snd_ml403_ac97cr_dev_free(struct snd_device *snddev) +{ + struct snd_ml403_ac97cr *ml403_ac97cr = snddev->device_data; + PDEBUG(INIT_INFO, "dev_free():\n"); + return snd_ml403_ac97cr_free(ml403_ac97cr); +} + +static int __devinit +snd_ml403_ac97cr_create(struct snd_card *card, struct platform_device *pfdev, + struct snd_ml403_ac97cr **rml403_ac97cr) +{ + struct snd_ml403_ac97cr *ml403_ac97cr; + int err; + static struct snd_device_ops ops = { + .dev_free = snd_ml403_ac97cr_dev_free, + }; + struct resource *resource; + int irq; + + *rml403_ac97cr = NULL; + ml403_ac97cr = kzalloc(sizeof(*ml403_ac97cr), GFP_KERNEL); + if (ml403_ac97cr == NULL) + return -ENOMEM; + spin_lock_init(&ml403_ac97cr->reg_lock); + mutex_init(&ml403_ac97cr->cdc_mutex); + ml403_ac97cr->card = card; + ml403_ac97cr->pfdev = pfdev; + ml403_ac97cr->irq = -1; + ml403_ac97cr->enable_irq = 0; + ml403_ac97cr->capture_irq = -1; + ml403_ac97cr->enable_capture_irq = 0; + ml403_ac97cr->port = NULL; + ml403_ac97cr->res_port = NULL; + + PDEBUG(INIT_INFO, "Trying to reserve resources now ...\n"); + resource = platform_get_resource(pfdev, IORESOURCE_MEM, 0); + /* get "port" */ + ml403_ac97cr->port = ioremap_nocache(resource->start, + (resource->end) - + (resource->start) + 1); + if (ml403_ac97cr->port == NULL) { + snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": " + "unable to remap memory region (%x to %x)\n", + resource->start, resource->end); + snd_ml403_ac97cr_free(ml403_ac97cr); + return -EBUSY; + } + snd_printk(KERN_INFO SND_ML403_AC97CR_DRIVER ": " + "remap controller memory region to " + "0x%x done\n", (unsigned int)ml403_ac97cr->port); + /* get irq */ + irq = platform_get_irq(pfdev, 0); + if (request_irq(irq, snd_ml403_ac97cr_irq, IRQF_DISABLED, + pfdev->dev.bus_id, (void *)ml403_ac97cr)) { + snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": " + "unable to grab IRQ %d\n", + irq); + snd_ml403_ac97cr_free(ml403_ac97cr); + return -EBUSY; + } + ml403_ac97cr->irq = irq; + snd_printk(KERN_INFO SND_ML403_AC97CR_DRIVER ": " + "request (playback) irq %d done\n", + ml403_ac97cr->irq); + irq = platform_get_irq(pfdev, 1); + if (request_irq(irq, snd_ml403_ac97cr_irq, IRQF_DISABLED, + pfdev->dev.bus_id, (void *)ml403_ac97cr)) { + snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": " + "unable to grab IRQ %d\n", + irq); + snd_ml403_ac97cr_free(ml403_ac97cr); + return -EBUSY; + } + ml403_ac97cr->capture_irq = irq; + snd_printk(KERN_INFO SND_ML403_AC97CR_DRIVER ": " + "request (capture) irq %d done\n", + ml403_ac97cr->capture_irq); + + err = snd_ml403_ac97cr_chip_init(ml403_ac97cr); + if (err < 0) { + snd_ml403_ac97cr_free(ml403_ac97cr); + return err; + } + + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ml403_ac97cr, &ops); + if (err < 0) { + PDEBUG(INIT_FAILURE, "probe(): snd_device_new() failed!\n"); + snd_ml403_ac97cr_free(ml403_ac97cr); + return err; + } + + snd_card_set_dev(card, &pfdev->dev); + + *rml403_ac97cr = ml403_ac97cr; + return 0; +} + +static void snd_ml403_ac97cr_mixer_free(struct snd_ac97 *ac97) +{ + struct snd_ml403_ac97cr *ml403_ac97cr = ac97->private_data; + PDEBUG(INIT_INFO, "mixer_free():\n"); + ml403_ac97cr->ac97 = NULL; + PDEBUG(INIT_INFO, "mixer_free(): (done)\n"); +} + +static int __devinit +snd_ml403_ac97cr_mixer(struct snd_ml403_ac97cr *ml403_ac97cr) +{ + struct snd_ac97_bus *bus; + struct snd_ac97_template ac97; + int err; + static struct snd_ac97_bus_ops ops = { + .write = snd_ml403_ac97cr_codec_write, + .read = snd_ml403_ac97cr_codec_read, + }; + PDEBUG(INIT_INFO, "mixer():\n"); + err = snd_ac97_bus(ml403_ac97cr->card, 0, &ops, NULL, &bus); + if (err < 0) + return err; + + memset(&ac97, 0, sizeof(ac97)); + ml403_ac97cr->ac97_fake = 1; + lm4550_regfile_init(); +#ifdef CODEC_STAT + ml403_ac97cr->ac97_read = 0; + ml403_ac97cr->ac97_write = 0; +#endif + ac97.private_data = ml403_ac97cr; + ac97.private_free = snd_ml403_ac97cr_mixer_free; + ac97.scaps = AC97_SCAP_AUDIO | AC97_SCAP_SKIP_MODEM | + AC97_SCAP_NO_SPDIF; + err = snd_ac97_mixer(bus, &ac97, &ml403_ac97cr->ac97); + ml403_ac97cr->ac97_fake = 0; + lm4550_regfile_write_values_after_init(ml403_ac97cr->ac97); + PDEBUG(INIT_INFO, "mixer(): (done) snd_ac97_mixer()=%d\n", err); + return err; +} + +static int __devinit +snd_ml403_ac97cr_pcm(struct snd_ml403_ac97cr *ml403_ac97cr, int device, + struct snd_pcm **rpcm) +{ + struct snd_pcm *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(ml403_ac97cr->card, "ML403AC97CR/1", device, 1, 1, + &pcm); + if (err < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_ml403_ac97cr_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_ml403_ac97cr_capture_ops); + pcm->private_data = ml403_ac97cr; + pcm->info_flags = 0; + strcpy(pcm->name, "ML403AC97CR DAC/ADC"); + ml403_ac97cr->pcm = pcm; + + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + 64 * 1024, + 128 * 1024); + if (rpcm) + *rpcm = pcm; + return 0; +} + +static int __devinit snd_ml403_ac97cr_probe(struct platform_device *pfdev) +{ + struct snd_card *card; + struct snd_ml403_ac97cr *ml403_ac97cr = NULL; + int err; + int dev = pfdev->id; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) + return -ENOENT; + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + err = snd_ml403_ac97cr_create(card, pfdev, &ml403_ac97cr); + if (err < 0) { + PDEBUG(INIT_FAILURE, "probe(): create failed!\n"); + snd_card_free(card); + return err; + } + PDEBUG(INIT_INFO, "probe(): create done\n"); + card->private_data = ml403_ac97cr; + err = snd_ml403_ac97cr_mixer(ml403_ac97cr); + if (err < 0) { + snd_card_free(card); + return err; + } + PDEBUG(INIT_INFO, "probe(): mixer done\n"); + err = snd_ml403_ac97cr_pcm(ml403_ac97cr, 0, NULL); + if (err < 0) { + snd_card_free(card); + return err; + } + PDEBUG(INIT_INFO, "probe(): PCM done\n"); + strcpy(card->driver, SND_ML403_AC97CR_DRIVER); + strcpy(card->shortname, "ML403 AC97 Controller Reference"); + sprintf(card->longname, "%s %s at 0x%lx, irq %i & %i, device %i", + card->shortname, card->driver, + (unsigned long)ml403_ac97cr->port, ml403_ac97cr->irq, + ml403_ac97cr->capture_irq, dev + 1); + + snd_card_set_dev(card, &pfdev->dev); + + err = snd_card_register(card); + if (err < 0) { + snd_card_free(card); + return err; + } + platform_set_drvdata(pfdev, card); + PDEBUG(INIT_INFO, "probe(): (done)\n"); + return 0; +} + +static int snd_ml403_ac97cr_remove(struct platform_device *pfdev) +{ + snd_card_free(platform_get_drvdata(pfdev)); + platform_set_drvdata(pfdev, NULL); + return 0; +} + +static struct platform_driver snd_ml403_ac97cr_driver = { + .probe = snd_ml403_ac97cr_probe, + .remove = snd_ml403_ac97cr_remove, + .driver = { + .name = SND_ML403_AC97CR_DRIVER, + }, +}; + +static int __init alsa_card_ml403_ac97cr_init(void) +{ + return platform_driver_register(&snd_ml403_ac97cr_driver); +} + +static void __exit alsa_card_ml403_ac97cr_exit(void) +{ + platform_driver_unregister(&snd_ml403_ac97cr_driver); +} + +module_init(alsa_card_ml403_ac97cr_init) +module_exit(alsa_card_ml403_ac97cr_exit) diff --git a/sound/drivers/mpu401/mpu401.c b/sound/drivers/mpu401/mpu401.c index 1fc95da..5b996f3 100644 --- a/sound/drivers/mpu401/mpu401.c +++ b/sound/drivers/mpu401/mpu401.c @@ -20,7 +20,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/pnp.h> #include <linux/err.h> diff --git a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c index b57f2d5..5993864a 100644 --- a/sound/drivers/mpu401/mpu401_uart.c +++ b/sound/drivers/mpu401/mpu401_uart.c @@ -28,7 +28,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/init.h> diff --git a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c index 40eb026..b5e1a71 100644 --- a/sound/drivers/mtpav.c +++ b/sound/drivers/mtpav.c @@ -50,7 +50,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/err.h> diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c index dcc90f9..87ba1dd 100644 --- a/sound/drivers/mts64.c +++ b/sound/drivers/mts64.c @@ -18,7 +18,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/parport.h> @@ -461,13 +460,14 @@ static int snd_mts64_ctl_smpte_switch_put(struct snd_kcontrol* kctl, { struct mts64 *mts = snd_kcontrol_chip(kctl); int changed = 0; + int val = !!uctl->value.integer.value[0]; spin_lock_irq(&mts->lock); - if (mts->smpte_switch == uctl->value.integer.value[0]) + if (mts->smpte_switch == val) goto __out; changed = 1; - mts->smpte_switch = uctl->value.integer.value[0]; + mts->smpte_switch = val; if (mts->smpte_switch) { mts64_smpte_start(mts->pardev->port, mts->time[0], mts->time[1], @@ -541,12 +541,13 @@ static int snd_mts64_ctl_smpte_time_put(struct snd_kcontrol *kctl, { struct mts64 *mts = snd_kcontrol_chip(kctl); int idx = kctl->private_value; + unsigned int time = uctl->value.integer.value[0] % 60; int changed = 0; spin_lock_irq(&mts->lock); - if (mts->time[idx] != uctl->value.integer.value[0]) { + if (mts->time[idx] != time) { changed = 1; - mts->time[idx] = uctl->value.integer.value[0]; + mts->time[idx] = time; } spin_unlock_irq(&mts->lock); @@ -636,6 +637,8 @@ static int snd_mts64_ctl_smpte_fps_put(struct snd_kcontrol *kctl, struct mts64 *mts = snd_kcontrol_chip(kctl); int changed = 0; + if (uctl->value.enumerated.item[0] >= 5) + return -EINVAL; spin_lock_irq(&mts->lock); if (mts->fps != uctl->value.enumerated.item[0]) { changed = 1; @@ -662,7 +665,7 @@ static int __devinit snd_mts64_ctl_create(struct snd_card *card, struct mts64 *mts) { int err, i; - static struct snd_kcontrol_new *control[] = { + static struct snd_kcontrol_new *control[] __devinitdata = { &mts64_ctl_smpte_switch, &mts64_ctl_smpte_time_hours, &mts64_ctl_smpte_time_minutes, @@ -1004,6 +1007,8 @@ static int __devinit snd_mts64_probe(struct platform_device *pdev) platform_set_drvdata(pdev, card); + snd_card_set_dev(card, &pdev->dev); + /* At this point card will be usable */ if ((err = snd_card_register(card)) < 0) { snd_printd("Cannot register card\n"); diff --git a/sound/drivers/opl3/opl3_lib.c b/sound/drivers/opl3/opl3_lib.c index a2b9ce0..ebe4359 100644 --- a/sound/drivers/opl3/opl3_lib.c +++ b/sound/drivers/opl3/opl3_lib.c @@ -327,6 +327,7 @@ static int snd_opl3_free(struct snd_opl3 *opl3) snd_assert(opl3 != NULL, return -ENXIO); if (opl3->private_free) opl3->private_free(opl3); + snd_opl3_clear_patches(opl3); release_and_free_resource(opl3->res_l_port); release_and_free_resource(opl3->res_r_port); kfree(opl3); @@ -360,7 +361,6 @@ int snd_opl3_new(struct snd_card *card, opl3->hardware = hardware; spin_lock_init(&opl3->reg_lock); spin_lock_init(&opl3->timer_lock); - mutex_init(&opl3->access_mutex); if ((err = snd_device_new(card, SNDRV_DEV_CODEC, opl3, &ops)) < 0) { snd_opl3_free(opl3); @@ -496,6 +496,7 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3, return err; } hw->private_data = opl3; + hw->exclusive = 1; #ifdef CONFIG_SND_OSSEMUL if (device == 0) { hw->oss_type = SNDRV_OSS_DEVICE_TYPE_DMFM; @@ -521,8 +522,10 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3, /* operators - only ioctl */ hw->ops.open = snd_opl3_open; hw->ops.ioctl = snd_opl3_ioctl; + hw->ops.write = snd_opl3_write; hw->ops.release = snd_opl3_release; + opl3->hwdep = hw; opl3->seq_dev_num = seq_device; #if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) if (snd_seq_device_new(card, seq_device, SNDRV_SEQ_DEV_ID_OPL3, diff --git a/sound/drivers/opl3/opl3_midi.c b/sound/drivers/opl3/opl3_midi.c index 3557b6e..cebcb8b 100644 --- a/sound/drivers/opl3/opl3_midi.c +++ b/sound/drivers/opl3/opl3_midi.c @@ -289,8 +289,6 @@ static int snd_opl3_oss_map[MAX_OPL3_VOICES] = { void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan) { struct snd_opl3 *opl3; - struct snd_seq_instr wanted; - struct snd_seq_kinstr *kinstr; int instr_4op; int voice; @@ -306,11 +304,13 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan) unsigned char voice_offset; unsigned short opl3_reg; unsigned char reg_val; + unsigned char prg, bank; int key = note; unsigned char fnum, blocknum; int i; + struct fm_patch *patch; struct fm_instrument *fm; unsigned long flags; @@ -320,19 +320,17 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan) snd_printk("Note on, ch %i, inst %i, note %i, vel %i\n", chan->number, chan->midi_program, note, vel); #endif - wanted.cluster = 0; - wanted.std = SNDRV_SEQ_INSTR_TYPE2_OPL2_3; /* in SYNTH mode, application takes care of voices */ /* in SEQ mode, drum voice numbers are notes on drum channel */ if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { if (chan->drum_channel) { /* percussion instruments are located in bank 128 */ - wanted.bank = 128; - wanted.prg = note; + bank = 128; + prg = note; } else { - wanted.bank = chan->gm_bank_select; - wanted.prg = chan->midi_program; + bank = chan->gm_bank_select; + prg = chan->midi_program; } } else { /* Prepare for OSS mode */ @@ -340,8 +338,8 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan) return; /* OSS instruments are located in bank 127 */ - wanted.bank = 127; - wanted.prg = chan->midi_program; + bank = 127; + prg = chan->midi_program; } spin_lock_irqsave(&opl3->voice_lock, flags); @@ -353,15 +351,14 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan) } __extra_prg: - kinstr = snd_seq_instr_find(opl3->ilist, &wanted, 1, 0); - if (kinstr == NULL) { + patch = snd_opl3_find_patch(opl3, prg, bank, 0); + if (!patch) { spin_unlock_irqrestore(&opl3->voice_lock, flags); return; } - fm = KINSTR_DATA(kinstr); - - switch (fm->type) { + fm = &patch->inst; + switch (patch->type) { case FM_PATCH_OPL2: instr_4op = 0; break; @@ -371,14 +368,12 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan) break; } default: - snd_seq_instr_free_use(opl3->ilist, kinstr); spin_unlock_irqrestore(&opl3->voice_lock, flags); return; } - #ifdef DEBUG_MIDI snd_printk(" --> OPL%i instrument: %s\n", - instr_4op ? 3 : 2, kinstr->name); + instr_4op ? 3 : 2, patch->name); #endif /* in SYNTH mode, application takes care of voices */ /* in SEQ mode, allocate voice on free OPL3 channel */ @@ -569,8 +564,6 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan) /* get extra pgm, but avoid possible loops */ extra_prg = (extra_prg) ? 0 : fm->modes; - snd_seq_instr_free_use(opl3->ilist, kinstr); - /* do the bookkeeping */ vp->time = opl3->use_time++; vp->note = key; @@ -601,12 +594,12 @@ void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan) /* allocate extra program if specified in patch library */ if (extra_prg) { if (extra_prg > 128) { - wanted.bank = 128; + bank = 128; /* percussions start at 35 */ - wanted.prg = extra_prg - 128 + 35 - 1; + prg = extra_prg - 128 + 35 - 1; } else { - wanted.bank = 0; - wanted.prg = extra_prg - 1; + bank = 0; + prg = extra_prg - 1; } #ifdef DEBUG_MIDI snd_printk(" *** allocating extra program\n"); diff --git a/sound/drivers/opl3/opl3_oss.c b/sound/drivers/opl3/opl3_oss.c index 5fd3a4c..239347f 100644 --- a/sound/drivers/opl3/opl3_oss.c +++ b/sound/drivers/opl3/opl3_oss.c @@ -195,17 +195,6 @@ static int snd_opl3_close_seq_oss(struct snd_seq_oss_arg *arg) /* load patch */ -/* offsets for SBI params */ -#define AM_VIB 0 -#define KSL_LEVEL 2 -#define ATTACK_DECAY 4 -#define SUSTAIN_RELEASE 6 -#define WAVE_SELECT 8 - -/* offset for SBI instrument */ -#define CONNECTION 10 -#define OFFSET_4OP 11 - /* from sound_config.h */ #define SBFM_MAXINSTR 256 @@ -213,112 +202,42 @@ static int snd_opl3_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format, const char __user *buf, int offs, int count) { struct snd_opl3 *opl3; - int err = -EINVAL; + struct sbi_instrument sbi; + char name[32]; + int err, type; snd_assert(arg != NULL, return -ENXIO); opl3 = arg->private_data; - if ((format == FM_PATCH) || (format == OPL3_PATCH)) { - struct sbi_instrument sbi; + if (format == FM_PATCH) + type = FM_PATCH_OPL2; + else if (format == OPL3_PATCH) + type = FM_PATCH_OPL3; + else + return -EINVAL; - size_t size; - struct snd_seq_instr_header *put; - struct snd_seq_instr_data *data; - struct fm_xinstrument *xinstr; + if (count < (int)sizeof(sbi)) { + snd_printk("FM Error: Patch record too short\n"); + return -EINVAL; + } + if (copy_from_user(&sbi, buf, sizeof(sbi))) + return -EFAULT; - struct snd_seq_event ev; - int i; + if (sbi.channel < 0 || sbi.channel >= SBFM_MAXINSTR) { + snd_printk("FM Error: Invalid instrument number %d\n", + sbi.channel); + return -EINVAL; + } - mm_segment_t fs; + memset(name, 0, sizeof(name)); + sprintf(name, "Chan%d", sbi.channel); - if (count < (int)sizeof(sbi)) { - snd_printk("FM Error: Patch record too short\n"); - return -EINVAL; - } - if (copy_from_user(&sbi, buf, sizeof(sbi))) - return -EFAULT; + err = snd_opl3_load_patch(opl3, sbi.channel, 127, type, name, NULL, + sbi.operators); + if (err < 0) + return err; - if (sbi.channel < 0 || sbi.channel >= SBFM_MAXINSTR) { - snd_printk("FM Error: Invalid instrument number %d\n", sbi.channel); - return -EINVAL; - } - - size = sizeof(*put) + sizeof(struct fm_xinstrument); - put = kzalloc(size, GFP_KERNEL); - if (put == NULL) - return -ENOMEM; - /* build header */ - data = &put->data; - data->type = SNDRV_SEQ_INSTR_ATYPE_DATA; - strcpy(data->data.format, SNDRV_SEQ_INSTR_ID_OPL2_3); - /* build data section */ - xinstr = (struct fm_xinstrument *)(data + 1); - xinstr->stype = FM_STRU_INSTR; - - for (i = 0; i < 2; i++) { - xinstr->op[i].am_vib = sbi.operators[AM_VIB + i]; - xinstr->op[i].ksl_level = sbi.operators[KSL_LEVEL + i]; - xinstr->op[i].attack_decay = sbi.operators[ATTACK_DECAY + i]; - xinstr->op[i].sustain_release = sbi.operators[SUSTAIN_RELEASE + i]; - xinstr->op[i].wave_select = sbi.operators[WAVE_SELECT + i]; - } - xinstr->feedback_connection[0] = sbi.operators[CONNECTION]; - - if (format == OPL3_PATCH) { - xinstr->type = FM_PATCH_OPL3; - for (i = 0; i < 2; i++) { - xinstr->op[i+2].am_vib = sbi.operators[OFFSET_4OP + AM_VIB + i]; - xinstr->op[i+2].ksl_level = sbi.operators[OFFSET_4OP + KSL_LEVEL + i]; - xinstr->op[i+2].attack_decay = sbi.operators[OFFSET_4OP + ATTACK_DECAY + i]; - xinstr->op[i+2].sustain_release = sbi.operators[OFFSET_4OP + SUSTAIN_RELEASE + i]; - xinstr->op[i+2].wave_select = sbi.operators[OFFSET_4OP + WAVE_SELECT + i]; - } - xinstr->feedback_connection[1] = sbi.operators[OFFSET_4OP + CONNECTION]; - } else { - xinstr->type = FM_PATCH_OPL2; - } - - put->id.instr.std = SNDRV_SEQ_INSTR_TYPE2_OPL2_3; - put->id.instr.bank = 127; - put->id.instr.prg = sbi.channel; - put->cmd = SNDRV_SEQ_INSTR_PUT_CMD_CREATE; - - memset (&ev, 0, sizeof(ev)); - ev.source.client = SNDRV_SEQ_CLIENT_OSS; - ev.dest = arg->addr; - - ev.flags = SNDRV_SEQ_EVENT_LENGTH_VARUSR; - ev.queue = SNDRV_SEQ_QUEUE_DIRECT; - - fs = snd_enter_user(); - __again: - ev.type = SNDRV_SEQ_EVENT_INSTR_PUT; - ev.data.ext.len = size; - ev.data.ext.ptr = put; - - err = snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, &ev, - opl3->seq_client, 0, 0); - if (err == -EBUSY) { - struct snd_seq_instr_header remove; - - memset (&remove, 0, sizeof(remove)); - remove.cmd = SNDRV_SEQ_INSTR_FREE_CMD_SINGLE; - remove.id.instr = put->id.instr; - - /* remove instrument */ - ev.type = SNDRV_SEQ_EVENT_INSTR_FREE; - ev.data.ext.len = sizeof(remove); - ev.data.ext.ptr = &remove; - - snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, &ev, - opl3->seq_client, 0, 0); - goto __again; - } - snd_leave_user(fs); - - kfree(put); - } - return err; + return sizeof(sbi); } /* ioctl */ diff --git a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c index 96762c9..2d33f53 100644 --- a/sound/drivers/opl3/opl3_seq.c +++ b/sound/drivers/opl3/opl3_seq.c @@ -51,14 +51,15 @@ void snd_opl3_synth_use_dec(struct snd_opl3 * opl3) int snd_opl3_synth_setup(struct snd_opl3 * opl3) { int idx; + struct snd_hwdep *hwdep = opl3->hwdep; - mutex_lock(&opl3->access_mutex); - if (opl3->used) { - mutex_unlock(&opl3->access_mutex); + mutex_lock(&hwdep->open_mutex); + if (hwdep->used) { + mutex_unlock(&hwdep->open_mutex); return -EBUSY; } - opl3->used++; - mutex_unlock(&opl3->access_mutex); + hwdep->used++; + mutex_unlock(&hwdep->open_mutex); snd_opl3_reset(opl3); @@ -81,6 +82,7 @@ int snd_opl3_synth_setup(struct snd_opl3 * opl3) void snd_opl3_synth_cleanup(struct snd_opl3 * opl3) { unsigned long flags; + struct snd_hwdep *hwdep; /* Stop system timer */ spin_lock_irqsave(&opl3->sys_timer_lock, flags); @@ -91,9 +93,11 @@ void snd_opl3_synth_cleanup(struct snd_opl3 * opl3) spin_unlock_irqrestore(&opl3->sys_timer_lock, flags); snd_opl3_reset(opl3); - mutex_lock(&opl3->access_mutex); - opl3->used--; - mutex_unlock(&opl3->access_mutex); + hwdep = opl3->hwdep; + mutex_lock(&hwdep->open_mutex); + hwdep->used--; + mutex_unlock(&hwdep->open_mutex); + wake_up(&hwdep->open_wait); } static int snd_opl3_synth_use(void *private_data, struct snd_seq_port_subscribe * info) @@ -152,15 +156,7 @@ static int snd_opl3_synth_event_input(struct snd_seq_event * ev, int direct, { struct snd_opl3 *opl3 = private_data; - if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN && - ev->type <= SNDRV_SEQ_EVENT_INSTR_CHANGE) { - if (direct) { - snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, ev, - opl3->seq_client, atomic, hop); - } - } else { - snd_midi_process_event(&opl3_ops, ev, opl3->chset); - } + snd_midi_process_event(&opl3_ops, ev, opl3->chset); return 0; } @@ -249,16 +245,6 @@ static int snd_opl3_seq_new_device(struct snd_seq_device *dev) return err; } - /* initialize instrument list */ - opl3->ilist = snd_seq_instr_list_new(); - if (opl3->ilist == NULL) { - snd_seq_delete_kernel_client(client); - opl3->seq_client = -1; - return -ENOMEM; - } - opl3->ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT; - snd_seq_fm_init(&opl3->fm_ops, NULL); - /* setup system timer */ init_timer(&opl3->tlist); opl3->tlist.function = snd_opl3_timer_func; @@ -287,8 +273,6 @@ static int snd_opl3_seq_delete_device(struct snd_seq_device *dev) snd_seq_delete_kernel_client(opl3->seq_client); opl3->seq_client = -1; } - if (opl3->ilist) - snd_seq_instr_list_free(&opl3->ilist); return 0; } diff --git a/sound/drivers/opl3/opl3_synth.c b/sound/drivers/opl3/opl3_synth.c index a4b3543..a7bf7a4 100644 --- a/sound/drivers/opl3/opl3_synth.c +++ b/sound/drivers/opl3/opl3_synth.c @@ -76,16 +76,6 @@ static int snd_opl3_set_connection(struct snd_opl3 * opl3, int connection); */ int snd_opl3_open(struct snd_hwdep * hw, struct file *file) { - struct snd_opl3 *opl3 = hw->private_data; - - mutex_lock(&opl3->access_mutex); - if (opl3->used) { - mutex_unlock(&opl3->access_mutex); - return -EAGAIN; - } - opl3->used++; - mutex_unlock(&opl3->access_mutex); - return 0; } @@ -165,6 +155,10 @@ int snd_opl3_ioctl(struct snd_hwdep * hw, struct file *file, #endif return snd_opl3_set_connection(opl3, (int) arg); + case SNDRV_DM_FM_IOCTL_CLEAR_PATCHES: + snd_opl3_clear_patches(opl3); + return 0; + #ifdef CONFIG_SND_DEBUG default: snd_printk("unknown IOCTL: 0x%x\n", cmd); @@ -181,12 +175,172 @@ int snd_opl3_release(struct snd_hwdep * hw, struct file *file) struct snd_opl3 *opl3 = hw->private_data; snd_opl3_reset(opl3); - mutex_lock(&opl3->access_mutex); - opl3->used--; - mutex_unlock(&opl3->access_mutex); + return 0; +} + +/* + * write the device - load patches + */ +long snd_opl3_write(struct snd_hwdep *hw, const char __user *buf, long count, + loff_t *offset) +{ + struct snd_opl3 *opl3 = hw->private_data; + long result = 0; + int err = 0; + struct sbi_patch inst; + + while (count >= sizeof(inst)) { + unsigned char type; + if (copy_from_user(&inst, buf, sizeof(inst))) + return -EFAULT; + if (!memcmp(inst.key, FM_KEY_SBI, 4) || + !memcmp(inst.key, FM_KEY_2OP, 4)) + type = FM_PATCH_OPL2; + else if (!memcmp(inst.key, FM_KEY_4OP, 4)) + type = FM_PATCH_OPL3; + else /* invalid type */ + break; + err = snd_opl3_load_patch(opl3, inst.prog, inst.bank, type, + inst.name, inst.extension, + inst.data); + if (err < 0) + break; + result += sizeof(inst); + count -= sizeof(inst); + } + return result > 0 ? result : err; +} + + +/* + * Patch management + */ + +/* offsets for SBI params */ +#define AM_VIB 0 +#define KSL_LEVEL 2 +#define ATTACK_DECAY 4 +#define SUSTAIN_RELEASE 6 +#define WAVE_SELECT 8 + +/* offset for SBI instrument */ +#define CONNECTION 10 +#define OFFSET_4OP 11 + +/* + * load a patch, obviously. + * + * loaded on the given program and bank numbers with the given type + * (FM_PATCH_OPLx). + * data is the pointer of SBI record _without_ header (key and name). + * name is the name string of the patch. + * ext is the extension data of 7 bytes long (stored in name of SBI + * data up to offset 25), or NULL to skip. + * return 0 if successful or a negative error code. + */ +int snd_opl3_load_patch(struct snd_opl3 *opl3, + int prog, int bank, int type, + const char *name, + const unsigned char *ext, + const unsigned char *data) +{ + struct fm_patch *patch; + int i; + + patch = snd_opl3_find_patch(opl3, prog, bank, 1); + if (!patch) + return -ENOMEM; + + patch->type = type; + + for (i = 0; i < 2; i++) { + patch->inst.op[i].am_vib = data[AM_VIB + i]; + patch->inst.op[i].ksl_level = data[KSL_LEVEL + i]; + patch->inst.op[i].attack_decay = data[ATTACK_DECAY + i]; + patch->inst.op[i].sustain_release = data[SUSTAIN_RELEASE + i]; + patch->inst.op[i].wave_select = data[WAVE_SELECT + i]; + } + patch->inst.feedback_connection[0] = data[CONNECTION]; + + if (type == FM_PATCH_OPL3) { + for (i = 0; i < 2; i++) { + patch->inst.op[i+2].am_vib = + data[OFFSET_4OP + AM_VIB + i]; + patch->inst.op[i+2].ksl_level = + data[OFFSET_4OP + KSL_LEVEL + i]; + patch->inst.op[i+2].attack_decay = + data[OFFSET_4OP + ATTACK_DECAY + i]; + patch->inst.op[i+2].sustain_release = + data[OFFSET_4OP + SUSTAIN_RELEASE + i]; + patch->inst.op[i+2].wave_select = + data[OFFSET_4OP + WAVE_SELECT + i]; + } + patch->inst.feedback_connection[1] = + data[OFFSET_4OP + CONNECTION]; + } + + if (ext) { + patch->inst.echo_delay = ext[0]; + patch->inst.echo_atten = ext[1]; + patch->inst.chorus_spread = ext[2]; + patch->inst.trnsps = ext[3]; + patch->inst.fix_dur = ext[4]; + patch->inst.modes = ext[5]; + patch->inst.fix_key = ext[6]; + } + + if (name) + strlcpy(patch->name, name, sizeof(patch->name)); return 0; } +EXPORT_SYMBOL(snd_opl3_load_patch); + +/* + * find a patch with the given program and bank numbers, returns its pointer + * if no matching patch is found and create_patch is set, it creates a + * new patch object. + */ +struct fm_patch *snd_opl3_find_patch(struct snd_opl3 *opl3, int prog, int bank, + int create_patch) +{ + /* pretty dumb hash key */ + unsigned int key = (prog + bank) % OPL3_PATCH_HASH_SIZE; + struct fm_patch *patch; + + for (patch = opl3->patch_table[key]; patch; patch = patch->next) { + if (patch->prog == prog && patch->bank == bank) + return patch; + } + if (!create_patch) + return NULL; + + patch = kzalloc(sizeof(*patch), GFP_KERNEL); + if (!patch) + return NULL; + patch->prog = prog; + patch->bank = bank; + patch->next = opl3->patch_table[key]; + opl3->patch_table[key] = patch; + return patch; +} +EXPORT_SYMBOL(snd_opl3_find_patch); + +/* + * Clear all patches of the given OPL3 instance + */ +void snd_opl3_clear_patches(struct snd_opl3 *opl3) +{ + int i; + for (i = 0; i < OPL3_PATCH_HASH_SIZE; i++) { + struct fm_patch *patch, *next; + for (patch = opl3->patch_table[i]; patch; patch = next) { + next = patch->next; + kfree(patch); + } + } + memset(opl3->patch_table, 0, sizeof(opl3->patch_table)); +} /* ------------------------------ */ diff --git a/sound/drivers/pcm-indirect2.c b/sound/drivers/pcm-indirect2.c new file mode 100644 index 0000000..3c93c23 --- /dev/null +++ b/sound/drivers/pcm-indirect2.c @@ -0,0 +1,573 @@ +/* + * Helper functions for indirect PCM data transfer to a simple FIFO in + * hardware (small, no possibility to read "hardware io position", + * updating position done by interrupt, ...) + * + * Copyright (c) by 2007 Joachim Foerster <JOFT@gmx.de> + * + * Based on "pcm-indirect.h" (alsa-driver-1.0.13) by + * + * Copyright (c) by Takashi Iwai <tiwai@suse.de> + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* snd_printk/d() */ +#include <sound/core.h> +/* struct snd_pcm_substream, struct snd_pcm_runtime, snd_pcm_uframes_t + * snd_pcm_period_elapsed() */ +#include <sound/pcm.h> + +#include "pcm-indirect2.h" + +#ifdef SND_PCM_INDIRECT2_STAT +/* jiffies */ +#include <linux/jiffies.h> + +void snd_pcm_indirect2_stat(struct snd_pcm_substream *substream, + struct snd_pcm_indirect2 *rec) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int i; + int j; + int k; + int seconds = (rec->lastbytetime - rec->firstbytetime) / HZ; + + snd_printk(KERN_DEBUG "STAT: mul_elapsed: %u, mul_elapsed_real: %d, " + "irq_occured: %d\n", + rec->mul_elapsed, rec->mul_elapsed_real, rec->irq_occured); + snd_printk(KERN_DEBUG "STAT: min_multiple: %d (irqs/period)\n", + rec->min_multiple); + snd_printk(KERN_DEBUG "STAT: firstbytetime: %lu, lastbytetime: %lu, " + "firstzerotime: %lu\n", + rec->firstbytetime, rec->lastbytetime, rec->firstzerotime); + snd_printk(KERN_DEBUG "STAT: bytes2hw: %u Bytes => (by runtime->rate) " + "length: %d s\n", + rec->bytes2hw, rec->bytes2hw / 2 / 2 / runtime->rate); + snd_printk(KERN_DEBUG "STAT: (by measurement) length: %d => " + "rate: %d Bytes/s = %d Frames/s|Hz\n", + seconds, rec->bytes2hw / seconds, + rec->bytes2hw / 2 / 2 / seconds); + snd_printk(KERN_DEBUG + "STAT: zeros2hw: %u = %d ms ~ %d * %d zero copies\n", + rec->zeros2hw, ((rec->zeros2hw / 2 / 2) * 1000) / + runtime->rate, + rec->zeros2hw / (rec->hw_buffer_size / 2), + (rec->hw_buffer_size / 2)); + snd_printk(KERN_DEBUG "STAT: pointer_calls: %u, lastdifftime: %u\n", + rec->pointer_calls, rec->lastdifftime); + snd_printk(KERN_DEBUG "STAT: sw_io: %d, sw_data: %d\n", rec->sw_io, + rec->sw_data); + snd_printk(KERN_DEBUG "STAT: byte_sizes[]:\n"); + k = 0; + for (j = 0; j < 8; j++) { + for (i = j * 8; i < (j + 1) * 8; i++) + if (rec->byte_sizes[i] != 0) { + snd_printk(KERN_DEBUG "%u: %u", + i, rec->byte_sizes[i]); + k++; + } + if (((k % 8) == 0) && (k != 0)) { + snd_printk(KERN_DEBUG "\n"); + k = 0; + } + } + snd_printk(KERN_DEBUG "\n"); + snd_printk(KERN_DEBUG "STAT: zero_sizes[]:\n"); + for (j = 0; j < 8; j++) { + k = 0; + for (i = j * 8; i < (j + 1) * 8; i++) + if (rec->zero_sizes[i] != 0) + snd_printk(KERN_DEBUG "%u: %u", + i, rec->zero_sizes[i]); + else + k++; + if (!k) + snd_printk(KERN_DEBUG "\n"); + } + snd_printk(KERN_DEBUG "\n"); + snd_printk(KERN_DEBUG "STAT: min_adds[]:\n"); + for (j = 0; j < 8; j++) { + if (rec->min_adds[j] != 0) + snd_printk(KERN_DEBUG "%u: %u", j, rec->min_adds[j]); + } + snd_printk(KERN_DEBUG "\n"); + snd_printk(KERN_DEBUG "STAT: mul_adds[]:\n"); + for (j = 0; j < 8; j++) { + if (rec->mul_adds[j] != 0) + snd_printk(KERN_DEBUG "%u: %u", j, rec->mul_adds[j]); + } + snd_printk(KERN_DEBUG "\n"); + snd_printk(KERN_DEBUG + "STAT: zero_times_saved: %d, zero_times_notsaved: %d\n", + rec->zero_times_saved, rec->zero_times_notsaved); + /* snd_printk(KERN_DEBUG "STAT: zero_times[]\n"); + i = 0; + for (j = 0; j < 3750; j++) { + if (rec->zero_times[j] != 0) { + snd_printk(KERN_DEBUG "%u: %u", j, rec->zero_times[j]); + i++; + } + if (((i % 8) == 0) && (i != 0)) + snd_printk(KERN_DEBUG "\n"); + } + snd_printk(KERN_DEBUG "\n"); */ + return; +} +#endif + +/* + * _internal_ helper function for playback/capture transfer function + */ +static void +snd_pcm_indirect2_increase_min_periods(struct snd_pcm_substream *substream, + struct snd_pcm_indirect2 *rec, + int isplay, int iscopy, + unsigned int bytes) +{ + if (rec->min_periods >= 0) { + if (iscopy) { + rec->sw_io += bytes; + if (rec->sw_io >= rec->sw_buffer_size) + rec->sw_io -= rec->sw_buffer_size; + } else if (isplay) { + /* If application does not write data in multiples of + * a period, move sw_data to the next correctly aligned + * position, so that sw_io can converge to it (in the + * next step). + */ + if (!rec->check_alignment) { + if (rec->bytes2hw % + snd_pcm_lib_period_bytes(substream)) { + unsigned bytes2hw_aligned = + (1 + + (rec->bytes2hw / + snd_pcm_lib_period_bytes + (substream))) * + snd_pcm_lib_period_bytes + (substream); + rec->sw_data = + bytes2hw_aligned % + rec->sw_buffer_size; +#ifdef SND_PCM_INDIRECT2_STAT + snd_printk(KERN_DEBUG + "STAT: @re-align: aligned " + "bytes2hw to next period " + "size boundary: %d " + "(instead of %d)\n", + bytes2hw_aligned, + rec->bytes2hw); + snd_printk(KERN_DEBUG + "STAT: @re-align: sw_data " + "moves to: %d\n", + rec->sw_data); +#endif + } + rec->check_alignment = 1; + } + /* We are at the end and are copying zeros into the + * fifo. + * Now, we have to make sure that sw_io is increased + * until the position of sw_data: Filling the fifo with + * the first zeros means, the last bytes were played. + */ + if (rec->sw_io != rec->sw_data) { + unsigned int diff; + if (rec->sw_data > rec->sw_io) + diff = rec->sw_data - rec->sw_io; + else + diff = (rec->sw_buffer_size - + rec->sw_io) + + rec->sw_data; + if (bytes >= diff) + rec->sw_io = rec->sw_data; + else { + rec->sw_io += bytes; + if (rec->sw_io >= rec->sw_buffer_size) + rec->sw_io -= + rec->sw_buffer_size; + } + } + } + rec->min_period_count += bytes; + if (rec->min_period_count >= (rec->hw_buffer_size / 2)) { + rec->min_periods += (rec->min_period_count / + (rec->hw_buffer_size / 2)); +#ifdef SND_PCM_INDIRECT2_STAT + if ((rec->min_period_count / + (rec->hw_buffer_size / 2)) > 7) + snd_printk(KERN_DEBUG + "STAT: more than 7 (%d) min_adds " + "at once - too big to save!\n", + (rec->min_period_count / + (rec->hw_buffer_size / 2))); + else + rec->min_adds[(rec->min_period_count / + (rec->hw_buffer_size / 2))]++; +#endif + rec->min_period_count = (rec->min_period_count % + (rec->hw_buffer_size / 2)); + } + } else if (isplay && iscopy) + rec->min_periods = 0; +} + +/* + * helper function for playback/capture pointer callback + */ +snd_pcm_uframes_t +snd_pcm_indirect2_pointer(struct snd_pcm_substream *substream, + struct snd_pcm_indirect2 *rec) +{ +#ifdef SND_PCM_INDIRECT2_STAT + rec->pointer_calls++; +#endif + return bytes_to_frames(substream->runtime, rec->sw_io); +} + +/* + * _internal_ helper function for playback interrupt callback + */ +static void +snd_pcm_indirect2_playback_transfer(struct snd_pcm_substream *substream, + struct snd_pcm_indirect2 *rec, + snd_pcm_indirect2_copy_t copy, + snd_pcm_indirect2_zero_t zero) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr; + + /* runtime->control->appl_ptr: position where ALSA will write next time + * rec->appl_ptr: position where ALSA was last time + * diff: obviously ALSA wrote that much bytes into the intermediate + * buffer since we checked last time + */ + snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr; + + if (diff) { +#ifdef SND_PCM_INDIRECT2_STAT + rec->lastdifftime = jiffies; +#endif + if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) + diff += runtime->boundary; + /* number of bytes "added" by ALSA increases the number of + * bytes which are ready to "be transfered to HW"/"played" + * Then, set rec->appl_ptr to not count bytes twice next time. + */ + rec->sw_ready += (int)frames_to_bytes(runtime, diff); + rec->appl_ptr = appl_ptr; + } + if (rec->hw_ready && (rec->sw_ready <= 0)) { + unsigned int bytes; + +#ifdef SND_PCM_INDIRECT2_STAT + if (rec->firstzerotime == 0) { + rec->firstzerotime = jiffies; + snd_printk(KERN_DEBUG + "STAT: @firstzerotime: mul_elapsed: %d, " + "min_period_count: %d\n", + rec->mul_elapsed, rec->min_period_count); + snd_printk(KERN_DEBUG + "STAT: @firstzerotime: sw_io: %d, " + "sw_data: %d, appl_ptr: %u\n", + rec->sw_io, rec->sw_data, + (unsigned int)appl_ptr); + } + if ((jiffies - rec->firstzerotime) < 3750) { + rec->zero_times[(jiffies - rec->firstzerotime)]++; + rec->zero_times_saved++; + } else + rec->zero_times_notsaved++; +#endif + bytes = zero(substream, rec); + +#ifdef SND_PCM_INDIRECT2_STAT + rec->zeros2hw += bytes; + if (bytes < 64) + rec->zero_sizes[bytes]++; + else + snd_printk(KERN_DEBUG + "STAT: %d zero Bytes copied to hardware at " + "once - too big to save!\n", + bytes); +#endif + snd_pcm_indirect2_increase_min_periods(substream, rec, 1, 0, + bytes); + return; + } + while (rec->hw_ready && (rec->sw_ready > 0)) { + /* sw_to_end: max. number of bytes that can be read/take from + * the current position (sw_data) in _one_ step + */ + unsigned int sw_to_end = rec->sw_buffer_size - rec->sw_data; + + /* bytes: number of bytes we have available (for reading) */ + unsigned int bytes = rec->sw_ready; + + if (sw_to_end < bytes) + bytes = sw_to_end; + if (!bytes) + break; + +#ifdef SND_PCM_INDIRECT2_STAT + if (rec->firstbytetime == 0) + rec->firstbytetime = jiffies; + rec->lastbytetime = jiffies; +#endif + /* copy bytes from intermediate buffer position sw_data to the + * HW and return number of bytes actually written + * Furthermore, set hw_ready to 0, if the fifo isn't empty + * now => more could be transfered to fifo + */ + bytes = copy(substream, rec, bytes); + rec->bytes2hw += bytes; + +#ifdef SND_PCM_INDIRECT2_STAT + if (bytes < 64) + rec->byte_sizes[bytes]++; + else + snd_printk(KERN_DEBUG + "STAT: %d Bytes copied to hardware at once " + "- too big to save!\n", + bytes); +#endif + /* increase sw_data by the number of actually written bytes + * (= number of taken bytes from intermediate buffer) + */ + rec->sw_data += bytes; + if (rec->sw_data == rec->sw_buffer_size) + rec->sw_data = 0; + /* now sw_data is the position where ALSA is going to write + * in the intermediate buffer next time = position we are going + * to read from next time + */ + + snd_pcm_indirect2_increase_min_periods(substream, rec, 1, 1, + bytes); + + /* we read bytes from intermediate buffer, so we need to say + * that the number of bytes ready for transfer are decreased + * now + */ + rec->sw_ready -= bytes; + } + return; +} + +/* + * helper function for playback interrupt routine + */ +void +snd_pcm_indirect2_playback_interrupt(struct snd_pcm_substream *substream, + struct snd_pcm_indirect2 *rec, + snd_pcm_indirect2_copy_t copy, + snd_pcm_indirect2_zero_t zero) +{ +#ifdef SND_PCM_INDIRECT2_STAT + rec->irq_occured++; +#endif + /* hardware played some bytes, so there is room again (in fifo) */ + rec->hw_ready = 1; + + /* don't call ack() now, instead call transfer() function directly + * (normally called by ack() ) + */ + snd_pcm_indirect2_playback_transfer(substream, rec, copy, zero); + + if (rec->min_periods >= rec->min_multiple) { +#ifdef SND_PCM_INDIRECT2_STAT + if ((rec->min_periods / rec->min_multiple) > 7) + snd_printk(KERN_DEBUG + "STAT: more than 7 (%d) mul_adds - too big " + "to save!\n", + (rec->min_periods / rec->min_multiple)); + else + rec->mul_adds[(rec->min_periods / + rec->min_multiple)]++; + rec->mul_elapsed_real += (rec->min_periods / + rec->min_multiple); + rec->mul_elapsed++; +#endif + rec->min_periods = (rec->min_periods % rec->min_multiple); + snd_pcm_period_elapsed(substream); + } +} + +/* + * _internal_ helper function for capture interrupt callback + */ +static void +snd_pcm_indirect2_capture_transfer(struct snd_pcm_substream *substream, + struct snd_pcm_indirect2 *rec, + snd_pcm_indirect2_copy_t copy, + snd_pcm_indirect2_zero_t null) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr; + snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr; + + if (diff) { +#ifdef SND_PCM_INDIRECT2_STAT + rec->lastdifftime = jiffies; +#endif + if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) + diff += runtime->boundary; + rec->sw_ready -= frames_to_bytes(runtime, diff); + rec->appl_ptr = appl_ptr; + } + /* if hardware has something, but the intermediate buffer is full + * => skip contents of buffer + */ + if (rec->hw_ready && (rec->sw_ready >= (int)rec->sw_buffer_size)) { + unsigned int bytes; + +#ifdef SND_PCM_INDIRECT2_STAT + if (rec->firstzerotime == 0) { + rec->firstzerotime = jiffies; + snd_printk(KERN_DEBUG "STAT: (capture) " + "@firstzerotime: mul_elapsed: %d, " + "min_period_count: %d\n", + rec->mul_elapsed, rec->min_period_count); + snd_printk(KERN_DEBUG "STAT: (capture) " + "@firstzerotime: sw_io: %d, sw_data: %d, " + "appl_ptr: %u\n", + rec->sw_io, rec->sw_data, + (unsigned int)appl_ptr); + } + if ((jiffies - rec->firstzerotime) < 3750) { + rec->zero_times[(jiffies - rec->firstzerotime)]++; + rec->zero_times_saved++; + } else + rec->zero_times_notsaved++; +#endif + bytes = null(substream, rec); + +#ifdef SND_PCM_INDIRECT2_STAT + rec->zeros2hw += bytes; + if (bytes < 64) + rec->zero_sizes[bytes]++; + else + snd_printk(KERN_DEBUG + "STAT: (capture) %d zero Bytes copied to " + "hardware at once - too big to save!\n", + bytes); +#endif + snd_pcm_indirect2_increase_min_periods(substream, rec, 0, 0, + bytes); + /* report an overrun */ + rec->sw_io = SNDRV_PCM_POS_XRUN; + return; + } + while (rec->hw_ready && (rec->sw_ready < (int)rec->sw_buffer_size)) { + /* sw_to_end: max. number of bytes that we can write to the + * intermediate buffer (until it's end) + */ + size_t sw_to_end = rec->sw_buffer_size - rec->sw_data; + + /* bytes: max. number of bytes, which may be copied to the + * intermediate buffer without overflow (in _one_ step) + */ + size_t bytes = rec->sw_buffer_size - rec->sw_ready; + + /* limit number of bytes (for transfer) by available room in + * the intermediate buffer + */ + if (sw_to_end < bytes) + bytes = sw_to_end; + if (!bytes) + break; + +#ifdef SND_PCM_INDIRECT2_STAT + if (rec->firstbytetime == 0) + rec->firstbytetime = jiffies; + rec->lastbytetime = jiffies; +#endif + /* copy bytes from the intermediate buffer (position sw_data) + * to the HW at most and return number of bytes actually copied + * from HW + * Furthermore, set hw_ready to 0, if the fifo is empty now. + */ + bytes = copy(substream, rec, bytes); + rec->bytes2hw += bytes; + +#ifdef SND_PCM_INDIRECT2_STAT + if (bytes < 64) + rec->byte_sizes[bytes]++; + else + snd_printk(KERN_DEBUG + "STAT: (capture) %d Bytes copied to " + "hardware at once - too big to save!\n", + bytes); +#endif + /* increase sw_data by the number of actually copied bytes from + * HW + */ + rec->sw_data += bytes; + if (rec->sw_data == rec->sw_buffer_size) + rec->sw_data = 0; + + snd_pcm_indirect2_increase_min_periods(substream, rec, 0, 1, + bytes); + + /* number of bytes in the intermediate buffer, which haven't + * been fetched by ALSA yet. + */ + rec->sw_ready += bytes; + } + return; +} + +/* + * helper function for capture interrupt routine + */ +void +snd_pcm_indirect2_capture_interrupt(struct snd_pcm_substream *substream, + struct snd_pcm_indirect2 *rec, + snd_pcm_indirect2_copy_t copy, + snd_pcm_indirect2_zero_t null) +{ +#ifdef SND_PCM_INDIRECT2_STAT + rec->irq_occured++; +#endif + /* hardware recorded some bytes, so there is something to read from the + * record fifo: + */ + rec->hw_ready = 1; + + /* don't call ack() now, instead call transfer() function directly + * (normally called by ack() ) + */ + snd_pcm_indirect2_capture_transfer(substream, rec, copy, null); + + if (rec->min_periods >= rec->min_multiple) { + +#ifdef SND_PCM_INDIRECT2_STAT + if ((rec->min_periods / rec->min_multiple) > 7) + snd_printk(KERN_DEBUG + "STAT: more than 7 (%d) mul_adds - " + "too big to save!\n", + (rec->min_periods / rec->min_multiple)); + else + rec->mul_adds[(rec->min_periods / + rec->min_multiple)]++; + rec->mul_elapsed_real += (rec->min_periods / + rec->min_multiple); + rec->mul_elapsed++; +#endif + rec->min_periods = (rec->min_periods % rec->min_multiple); + snd_pcm_period_elapsed(substream); + } +} diff --git a/sound/drivers/pcm-indirect2.h b/sound/drivers/pcm-indirect2.h new file mode 100644 index 0000000..2ea6e46 --- /dev/null +++ b/sound/drivers/pcm-indirect2.h @@ -0,0 +1,140 @@ +/* + * Helper functions for indirect PCM data transfer to a simple FIFO in + * hardware (small, no possibility to read "hardware io position", + * updating position done by interrupt, ...) + * + * Copyright (c) by 2007 Joachim Foerster <JOFT@gmx.de> + * + * Based on "pcm-indirect.h" (alsa-driver-1.0.13) by + * + * Copyright (c) by Takashi Iwai <tiwai@suse.de> + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __SOUND_PCM_INDIRECT2_H +#define __SOUND_PCM_INDIRECT2_H + +/* struct snd_pcm_substream, struct snd_pcm_runtime, snd_pcm_uframes_t */ +#include <sound/pcm.h> + +/* Debug options for code which may be removed completely in a final version */ +#ifdef CONFIG_SND_DEBUG +#define SND_PCM_INDIRECT2_STAT /* turn on some "statistics" about the + * process of copying bytes from the + * intermediate buffer to the hardware + * fifo and the other way round + */ +#endif + +struct snd_pcm_indirect2 { + unsigned int hw_buffer_size; /* Byte size of hardware buffer */ + int hw_ready; /* playback: 1 = hw fifo has room left, + * 0 = hw fifo is full + */ + unsigned int min_multiple; + int min_periods; /* counts number of min. periods until + * min_multiple is reached + */ + int min_period_count; /* counts bytes to count number of + * min. periods + */ + + unsigned int sw_buffer_size; /* Byte size of software buffer */ + + /* sw_data: position in intermediate buffer, where we will read (or + * write) from/to next time (to transfer data to/from HW) + */ + unsigned int sw_data; /* Offset to next dst (or src) in sw + * ring buffer + */ + /* easiest case (playback): + * sw_data is nearly the same as ~ runtime->control->appl_ptr, with the + * exception that sw_data is "behind" by the number if bytes ALSA wrote + * to the intermediate buffer last time. + * A call to ack() callback synchronizes both indirectly. + */ + + /* We have no real sw_io pointer here. Usually sw_io is pointing to the + * current playback/capture position _inside_ the hardware. Devices + * with plain FIFOs often have no possibility to publish this position. + * So we say: if sw_data is updated, that means bytes were copied to + * the hardware, we increase sw_io by that amount, because there have + * to be as much bytes which were played. So sw_io will stay behind + * sw_data all the time and has to converge to sw_data at the end of + * playback. + */ + unsigned int sw_io; /* Current software pointer in bytes */ + + /* sw_ready: number of bytes ALSA copied to the intermediate buffer, so + * it represents the number of bytes which wait for transfer to the HW + */ + int sw_ready; /* Bytes ready to be transferred to/from hw */ + + /* appl_ptr: last known position of ALSA (where ALSA is going to write + * next time into the intermediate buffer + */ + snd_pcm_uframes_t appl_ptr; /* Last seen appl_ptr */ + + unsigned int bytes2hw; + int check_alignment; + +#ifdef SND_PCM_INDIRECT2_STAT + unsigned int zeros2hw; + unsigned int mul_elapsed; + unsigned int mul_elapsed_real; + unsigned long firstbytetime; + unsigned long lastbytetime; + unsigned long firstzerotime; + unsigned int byte_sizes[64]; + unsigned int zero_sizes[64]; + unsigned int min_adds[8]; + unsigned int mul_adds[8]; + unsigned int zero_times[3750]; /* = 15s */ + unsigned int zero_times_saved; + unsigned int zero_times_notsaved; + unsigned int irq_occured; + unsigned int pointer_calls; + unsigned int lastdifftime; +#endif +}; + +typedef size_t (*snd_pcm_indirect2_copy_t) (struct snd_pcm_substream *substream, + struct snd_pcm_indirect2 *rec, + size_t bytes); +typedef size_t (*snd_pcm_indirect2_zero_t) (struct snd_pcm_substream *substream, + struct snd_pcm_indirect2 *rec); + +#ifdef SND_PCM_INDIRECT2_STAT +void snd_pcm_indirect2_stat(struct snd_pcm_substream *substream, + struct snd_pcm_indirect2 *rec); +#endif + +snd_pcm_uframes_t +snd_pcm_indirect2_pointer(struct snd_pcm_substream *substream, + struct snd_pcm_indirect2 *rec); +void +snd_pcm_indirect2_playback_interrupt(struct snd_pcm_substream *substream, + struct snd_pcm_indirect2 *rec, + snd_pcm_indirect2_copy_t copy, + snd_pcm_indirect2_zero_t zero); +void +snd_pcm_indirect2_capture_interrupt(struct snd_pcm_substream *substream, + struct snd_pcm_indirect2 *rec, + snd_pcm_indirect2_copy_t copy, + snd_pcm_indirect2_zero_t null); + +#endif /* __SOUND_PCM_INDIRECT2_H */ diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c index 1b83287..b1c047e 100644 --- a/sound/drivers/portman2x4.c +++ b/sound/drivers/portman2x4.c @@ -37,7 +37,6 @@ * - ported from alsa 0.5 to 1.0 */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/parport.h> @@ -797,6 +796,8 @@ static int __devinit snd_portman_probe(struct platform_device *pdev) platform_set_drvdata(pdev, card); + snd_card_set_dev(card, &pdev->dev); + /* At this point card will be usable */ if ((err = snd_card_register(card)) < 0) { snd_printd("Cannot register card\n"); diff --git a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c index 65de3a7..d8aab9d 100644 --- a/sound/drivers/serial-u16550.c +++ b/sound/drivers/serial-u16550.c @@ -30,7 +30,6 @@ * More documentation can be found in serial-u16550.txt. */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/err.h> @@ -43,6 +42,7 @@ #include <sound/initval.h> #include <linux/serial_reg.h> +#include <linux/jiffies.h> #include <asm/io.h> @@ -455,7 +455,7 @@ static void snd_uart16550_do_open(struct snd_uart16550 * uart) | UART_IER_THRI /* Enable Transmitter holding register empty interrupt */ ; } - outb(byte, uart->base + UART_IER); /* Interupt enable Register */ + outb(byte, uart->base + UART_IER); /* Interrupt enable Register */ inb(uart->base + UART_LSR); /* Clear any pre-existing overrun indication */ inb(uart->base + UART_IIR); /* Clear any pre-existing transmit interrupt */ @@ -473,7 +473,7 @@ static void snd_uart16550_do_close(struct snd_uart16550 * uart) outb((0 & UART_IER_RDI) /* Disable Receiver data interrupt */ |(0 & UART_IER_THRI) /* Disable Transmitter holding register empty interrupt */ - ,uart->base + UART_IER); /* Interupt enable Register */ + ,uart->base + UART_IER); /* Interrupt enable Register */ switch (uart->adaptor) { default: @@ -653,7 +653,7 @@ static void snd_uart16550_output_write(struct snd_rawmidi_substream *substream) char first; static unsigned long lasttime = 0; - /* Interupts are disabled during the updating of the tx_buff, + /* Interrupts are disabled during the updating of the tx_buff, * since it is 'bad' to have two processes updating the same * variables (ie buff_in & buff_out) */ @@ -694,7 +694,7 @@ static void snd_uart16550_output_write(struct snd_rawmidi_substream *substream) (uart->adaptor == SNDRV_SERIAL_SOUNDCANVAS || uart->adaptor == SNDRV_SERIAL_GENERIC) && (uart->prev_out != substream->number || - jiffies-lasttime > 3*HZ)) { + time_after(jiffies, lasttime + 3*HZ))) { if (snd_uart16550_buffer_can_write(uart, 3)) { /* Roland Soundcanvas part selection */ diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c index 915c867..f79e361 100644 --- a/sound/drivers/virmidi.c +++ b/sound/drivers/virmidi.c @@ -41,7 +41,6 @@ * - Run application using a midi device (eg. /dev/snd/midiC1D0) */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/wait.h> #include <linux/err.h> diff --git a/sound/drivers/vx/vx_cmd.c b/sound/drivers/vx/vx_cmd.c index 7a22134..9529e3b 100644 --- a/sound/drivers/vx/vx_cmd.c +++ b/sound/drivers/vx/vx_cmd.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/vx_core.h> diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c index ed19bc1..9953886 100644 --- a/sound/drivers/vx/vx_core.c +++ b/sound/drivers/vx/vx_core.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/slab.h> #include <linux/interrupt.h> diff --git a/sound/drivers/vx/vx_hwdep.c b/sound/drivers/vx/vx_hwdep.c index 9a8154c..1dfe694 100644 --- a/sound/drivers/vx/vx_hwdep.c +++ b/sound/drivers/vx/vx_hwdep.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/device.h> #include <linux/firmware.h> #include <linux/vmalloc.h> diff --git a/sound/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c index b8fcd79..5a34732 100644 --- a/sound/drivers/vx/vx_mixer.c +++ b/sound/drivers/vx/vx_mixer.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <sound/core.h> #include <sound/control.h> #include <sound/tlv.h> @@ -439,14 +438,19 @@ static int vx_output_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele { struct vx_core *chip = snd_kcontrol_chip(kcontrol); int codec = kcontrol->id.index; + unsigned int val[2], vmax; + + vmax = chip->hw->output_level_max; + val[0] = ucontrol->value.integer.value[0]; + val[1] = ucontrol->value.integer.value[1]; + if (val[0] > vmax || val[1] > vmax) + return -EINVAL; mutex_lock(&chip->mixer_mutex); - if (ucontrol->value.integer.value[0] != chip->output_level[codec][0] || - ucontrol->value.integer.value[1] != chip->output_level[codec][1]) { - vx_set_analog_output_level(chip, codec, - ucontrol->value.integer.value[0], - ucontrol->value.integer.value[1]); - chip->output_level[codec][0] = ucontrol->value.integer.value[0]; - chip->output_level[codec][1] = ucontrol->value.integer.value[1]; + if (val[0] != chip->output_level[codec][0] || + val[1] != chip->output_level[codec][1]) { + vx_set_analog_output_level(chip, codec, val[0], val[1]); + chip->output_level[codec][0] = val[0]; + chip->output_level[codec][1] = val[1]; mutex_unlock(&chip->mixer_mutex); return 1; } @@ -506,6 +510,14 @@ static int vx_audio_src_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v static int vx_audio_src_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct vx_core *chip = snd_kcontrol_chip(kcontrol); + + if (chip->type >= VX_TYPE_VXPOCKET) { + if (ucontrol->value.enumerated.item[0] > 2) + return -EINVAL; + } else { + if (ucontrol->value.enumerated.item[0] > 1) + return -EINVAL; + } mutex_lock(&chip->mixer_mutex); if (chip->audio_source_target != ucontrol->value.enumerated.item[0]) { chip->audio_source_target = ucontrol->value.enumerated.item[0]; @@ -554,6 +566,9 @@ static int vx_clock_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_ static int vx_clock_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct vx_core *chip = snd_kcontrol_chip(kcontrol); + + if (ucontrol->value.enumerated.item[0] > 2) + return -EINVAL; mutex_lock(&chip->mixer_mutex); if (chip->clock_mode != ucontrol->value.enumerated.item[0]) { chip->clock_mode = ucontrol->value.enumerated.item[0]; @@ -603,12 +618,17 @@ static int vx_audio_gain_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_ struct vx_core *chip = snd_kcontrol_chip(kcontrol); int audio = kcontrol->private_value & 0xff; int capture = (kcontrol->private_value >> 8) & 1; + unsigned int val[2]; + val[0] = ucontrol->value.integer.value[0]; + val[1] = ucontrol->value.integer.value[1]; + if (val[0] > CVAL_MAX || val[1] > CVAL_MAX) + return -EINVAL; mutex_lock(&chip->mixer_mutex); - if (ucontrol->value.integer.value[0] != chip->audio_gain[capture][audio] || - ucontrol->value.integer.value[1] != chip->audio_gain[capture][audio+1]) { - vx_set_audio_gain(chip, audio, capture, ucontrol->value.integer.value[0]); - vx_set_audio_gain(chip, audio+1, capture, ucontrol->value.integer.value[1]); + if (val[0] != chip->audio_gain[capture][audio] || + val[1] != chip->audio_gain[capture][audio+1]) { + vx_set_audio_gain(chip, audio, capture, val[0]); + vx_set_audio_gain(chip, audio+1, capture, val[1]); mutex_unlock(&chip->mixer_mutex); return 1; } @@ -632,13 +652,19 @@ static int vx_audio_monitor_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el { struct vx_core *chip = snd_kcontrol_chip(kcontrol); int audio = kcontrol->private_value & 0xff; + unsigned int val[2]; + + val[0] = ucontrol->value.integer.value[0]; + val[1] = ucontrol->value.integer.value[1]; + if (val[0] > CVAL_MAX || val[1] > CVAL_MAX) + return -EINVAL; mutex_lock(&chip->mixer_mutex); - if (ucontrol->value.integer.value[0] != chip->audio_monitor[audio] || - ucontrol->value.integer.value[1] != chip->audio_monitor[audio+1]) { - vx_set_monitor_level(chip, audio, ucontrol->value.integer.value[0], + if (val[0] != chip->audio_monitor[audio] || + val[1] != chip->audio_monitor[audio+1]) { + vx_set_monitor_level(chip, audio, val[0], chip->audio_monitor_active[audio]); - vx_set_monitor_level(chip, audio+1, ucontrol->value.integer.value[1], + vx_set_monitor_level(chip, audio+1, val[1], chip->audio_monitor_active[audio+1]); mutex_unlock(&chip->mixer_mutex); return 1; @@ -669,8 +695,10 @@ static int vx_audio_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_va mutex_lock(&chip->mixer_mutex); if (ucontrol->value.integer.value[0] != chip->audio_active[audio] || ucontrol->value.integer.value[1] != chip->audio_active[audio+1]) { - vx_set_audio_switch(chip, audio, ucontrol->value.integer.value[0]); - vx_set_audio_switch(chip, audio+1, ucontrol->value.integer.value[1]); + vx_set_audio_switch(chip, audio, + !!ucontrol->value.integer.value[0]); + vx_set_audio_switch(chip, audio+1, + !!ucontrol->value.integer.value[1]); mutex_unlock(&chip->mixer_mutex); return 1; } @@ -699,9 +727,9 @@ static int vx_monitor_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_ if (ucontrol->value.integer.value[0] != chip->audio_monitor_active[audio] || ucontrol->value.integer.value[1] != chip->audio_monitor_active[audio+1]) { vx_set_monitor_level(chip, audio, chip->audio_monitor[audio], - ucontrol->value.integer.value[0]); + !!ucontrol->value.integer.value[0]); vx_set_monitor_level(chip, audio+1, chip->audio_monitor[audio+1], - ucontrol->value.integer.value[1]); + !!ucontrol->value.integer.value[1]); mutex_unlock(&chip->mixer_mutex); return 1; } diff --git a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c index 7e65a10..fdbf865 100644 --- a/sound/drivers/vx/vx_pcm.c +++ b/sound/drivers/vx/vx_pcm.c @@ -45,7 +45,6 @@ * - scheduled action on the stream. */ -#include <sound/driver.h> #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/delay.h> diff --git a/sound/drivers/vx/vx_uer.c b/sound/drivers/vx/vx_uer.c index 7400306..fb8932a 100644 --- a/sound/drivers/vx/vx_uer.c +++ b/sound/drivers/vx/vx_uer.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/delay.h> #include <sound/core.h> #include <sound/vx_core.h> diff --git a/sound/i2c/cs8427.c b/sound/i2c/cs8427.c index 744366b..e57e9cb 100644 --- a/sound/i2c/cs8427.c +++ b/sound/i2c/cs8427.c @@ -20,7 +20,6 @@ * */ -#include <sound/driver.h> #include <linux/slab.h> #include <linux/delay.h> #include <linux/init.h> diff --git a/sound/i2c/i2c.c b/sound/i2c/i2c.c index 1e58a96..b1e74e4 100644 --- a/sound/i2c/i2c.c +++ b/sound/i2c/i2c.c @@ -20,7 +20,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/string.h> diff --git a/sound/i2c/l3/uda1341.c b/sound/i2c/l3/uda1341.c index b074fdd..bfa5d2c 100644 --- a/sound/i2c/l3/uda1341.c +++ b/sound/i2c/l3/uda1341.c @@ -19,7 +19,6 @@ /* $Id: uda1341.c,v 1.18 2005/11/17 14:17:21 tiwai Exp $ */ -#include <sound/driver.h> #include <linux/module.h> #include <linux/init.h> #include <linux/types.h> diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c index facde46..15061bd 100644 --- a/sound/i2c/other/ak4114.c +++ b/sound/i2c/other/ak4114.c @@ -20,7 +20,6 @@ * */ -#include <sound/driver.h> #include <linux/slab.h> #include <linux/delay.h> #include <sound/core.h> diff --git a/sound/i2c/other/ak4117.c b/sound/i2c/other/ak4117.c index ee1585a..f350835 100644 --- a/sound/i2c/other/ak4117.c +++ b/sound/i2c/other/ak4117.c @@ -20,7 +20,6 @@ * */ -#include <sound/driver.h> #include <linux/slab.h> #include <linux/delay.h> #include <sound/core.h> diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c index de03f68..35fbbf2 100644 --- a/sound/i2c/other/ak4xxx-adda.c +++ b/sound/i2c/other/ak4xxx-adda.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -293,6 +292,11 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak) case SND_AK5365: /* FIXME: any init sequence? */ return; + case NON_AKM: + /* fake value for non-akm codecs using akm infrastructure + * (e.g. of ice1724) - certainly FIXME + */ + return; default: snd_BUG(); return; @@ -377,8 +381,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, @@ -409,11 +416,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; } @@ -508,6 +520,18 @@ static int ak4xxx_switch_put(struct snd_kcontrol *kcontrol, #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) { @@ -516,18 +540,16 @@ static int ak4xxx_capture_source_info(struct snd_kcontrol *kcontrol, const char **input_names; int num_names, idx; - input_names = ak->adc_info[mixer_ch].input_names; - - num_names = 0; - while (num_names < AK5365_NUM_INPUTS && input_names[num_names]) - ++num_names; - + 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; strncpy(uinfo->value.enumerated.name, input_names[idx], sizeof(uinfo->value.enumerated.name)); return 0; @@ -551,10 +573,15 @@ 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; diff --git a/sound/i2c/other/pt2258.c b/sound/i2c/other/pt2258.c index 00c83d8..797d3a6 100644 --- a/sound/i2c/other/pt2258.c +++ b/sound/i2c/other/pt2258.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <sound/core.h> #include <sound/control.h> #include <sound/tlv.h> @@ -113,6 +112,8 @@ static int pt2258_stereo_volume_put(struct snd_kcontrol *kcontrol, 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; diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c index 37c47fb..87e3aef 100644 --- a/sound/i2c/other/tea575x-tuner.c +++ b/sound/i2c/other/tea575x-tuner.c @@ -20,7 +20,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -159,6 +158,10 @@ static int snd_tea575x_ioctl(struct inode *inode, struct file *file, struct video_audio v; if(copy_from_user(&v, arg, sizeof(v))) return -EFAULT; + if (tea->ops->mute) + tea->ops->mute(tea, + (v.flags & + VIDEO_AUDIO_MUTE) ? 1 : 0); if(v.audio) return -EINVAL; return 0; @@ -206,6 +209,10 @@ void snd_tea575x_init(struct snd_tea575x *tea) tea->freq = 90500 * 16; /* 90.5Mhz default */ snd_tea575x_set_freq(tea); + + /* mute on init */ + if (tea->ops->mute) + tea->ops->mute(tea, 1); } void snd_tea575x_exit(struct snd_tea575x *tea) diff --git a/sound/i2c/tea6330t.c b/sound/i2c/tea6330t.c index 9bab744..0e3a9f2 100644 --- a/sound/i2c/tea6330t.c +++ b/sound/i2c/tea6330t.c @@ -20,7 +20,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/slab.h> #include <sound/core.h> diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c index fc88a31..68f1260 100644 --- a/sound/isa/ad1816a/ad1816a.c +++ b/sound/isa/ad1816a/ad1816a.c @@ -18,7 +18,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/time.h> #include <linux/wait.h> @@ -61,20 +60,6 @@ module_param_array(id, charp, NULL, 0444); MODULE_PARM_DESC(id, "ID string for ad1816a based soundcard."); module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable ad1816a based soundcard."); -module_param_array(port, long, NULL, 0444); -MODULE_PARM_DESC(port, "Port # for ad1816a driver."); -module_param_array(mpu_port, long, NULL, 0444); -MODULE_PARM_DESC(mpu_port, "MPU-401 port # for ad1816a driver."); -module_param_array(fm_port, long, NULL, 0444); -MODULE_PARM_DESC(fm_port, "FM port # for ad1816a driver."); -module_param_array(irq, int, NULL, 0444); -MODULE_PARM_DESC(irq, "IRQ # for ad1816a driver."); -module_param_array(mpu_irq, int, NULL, 0444); -MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for ad1816a driver."); -module_param_array(dma1, int, NULL, 0444); -MODULE_PARM_DESC(dma1, "1st DMA # for ad1816a driver."); -module_param_array(dma2, int, NULL, 0444); -MODULE_PARM_DESC(dma2, "2nd DMA # for ad1816a driver."); module_param_array(clockfreq, int, NULL, 0444); MODULE_PARM_DESC(clockfreq, "Clock frequency for ad1816a driver (default = 0)."); @@ -117,16 +102,12 @@ static int __devinit snd_card_ad1816a_pnp(int dev, struct snd_card_ad1816a *acar const struct pnp_card_device_id *id) { struct pnp_dev *pdev; - struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL); int err; - if (!cfg) - return -ENOMEM; acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL); - if (acard->dev == NULL) { - kfree(cfg); + if (acard->dev == NULL) return -EBUSY; - } + acard->devmpu = pnp_request_card_device(card, id->devs[1].id, NULL); if (acard->devmpu == NULL) { mpu_port[dev] = -1; @@ -134,25 +115,10 @@ static int __devinit snd_card_ad1816a_pnp(int dev, struct snd_card_ad1816a *acar } pdev = acard->dev; - pnp_init_resource_table(cfg); - - if (port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[2], port[dev], 16); - if (fm_port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4); - if (dma1[dev] != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1); - if (dma2[dev] != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1); - if (irq[dev] != SNDRV_AUTO_IRQ) - pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); - - if (pnp_manual_config_dev(pdev, cfg, 0) < 0) - snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n"); + err = pnp_activate_dev(pdev); if (err < 0) { printk(KERN_ERR PFX "AUDIO PnP configure failure\n"); - kfree(cfg); return -EBUSY; } @@ -162,20 +128,11 @@ static int __devinit snd_card_ad1816a_pnp(int dev, struct snd_card_ad1816a *acar dma2[dev] = pnp_dma(pdev, 1); irq[dev] = pnp_irq(pdev, 0); - if (acard->devmpu == NULL) { - kfree(cfg); + if (acard->devmpu == NULL) return 0; - } - pdev = acard->devmpu; - pnp_init_resource_table(cfg); - if (mpu_port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2); - if (mpu_irq[dev] != SNDRV_AUTO_IRQ) - pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1); + pdev = acard->devmpu; - if (pnp_manual_config_dev(pdev, cfg, 0) < 0) - snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n"); err = pnp_activate_dev(pdev); if (err < 0) { printk(KERN_ERR PFX "MPU401 PnP configure failure\n"); @@ -186,7 +143,6 @@ static int __devinit snd_card_ad1816a_pnp(int dev, struct snd_card_ad1816a *acar mpu_irq[dev] = pnp_irq(pdev, 0); } - kfree(cfg); return 0; } diff --git a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c index cf18fe4..4b8dfe2 100644 --- a/sound/isa/ad1816a/ad1816a_lib.c +++ b/sound/isa/ad1816a/ad1816a_lib.c @@ -17,7 +17,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/isa/ad1848/ad1848.c b/sound/isa/ad1848/ad1848.c index a4710b5..5f5271e 100644 --- a/sound/isa/ad1848/ad1848.c +++ b/sound/isa/ad1848/ad1848.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/err.h> #include <linux/isa.h> diff --git a/sound/isa/ad1848/ad1848_lib.c b/sound/isa/ad1848/ad1848_lib.c index a901cd1..630c90f 100644 --- a/sound/isa/ad1848/ad1848_lib.c +++ b/sound/isa/ad1848/ad1848_lib.c @@ -20,7 +20,6 @@ */ #define SNDRV_MAIN_OBJECT_FILE -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -213,7 +212,7 @@ static void snd_ad1848_mce_down(struct snd_ad1848 *chip) for (timeout = 12000; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--) udelay(100); - snd_printdd("(1) timeout = %d\n", timeout); + snd_printdd("(1) timeout = %ld\n", timeout); #ifdef CONFIG_SND_DEBUG if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) diff --git a/sound/isa/adlib.c b/sound/isa/adlib.c index d687207..efa8c80 100644 --- a/sound/isa/adlib.c +++ b/sound/isa/adlib.c @@ -2,7 +2,6 @@ * AdLib FM card driver. */ -#include <sound/driver.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/isa.h> diff --git a/sound/isa/als100.c b/sound/isa/als100.c index f2bcfb2..f1ce30f 100644 --- a/sound/isa/als100.c +++ b/sound/isa/als100.c @@ -20,7 +20,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/wait.h> #include <linux/time.h> @@ -63,20 +62,6 @@ module_param_array(id, charp, NULL, 0444); MODULE_PARM_DESC(id, "ID string for als100 based soundcard."); module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable als100 based soundcard."); -module_param_array(port, long, NULL, 0444); -MODULE_PARM_DESC(port, "Port # for als100 driver."); -module_param_array(mpu_port, long, NULL, 0444); -MODULE_PARM_DESC(mpu_port, "MPU-401 port # for als100 driver."); -module_param_array(fm_port, long, NULL, 0444); -MODULE_PARM_DESC(fm_port, "FM port # for als100 driver."); -module_param_array(irq, int, NULL, 0444); -MODULE_PARM_DESC(irq, "IRQ # for als100 driver."); -module_param_array(mpu_irq, int, NULL, 0444); -MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for als100 driver."); -module_param_array(dma8, int, NULL, 0444); -MODULE_PARM_DESC(dma8, "8-bit DMA # for als100 driver."); -module_param_array(dma16, int, NULL, 0444); -MODULE_PARM_DESC(dma16, "16-bit DMA # for als100 driver."); struct snd_card_als100 { int dev_no; @@ -111,38 +96,20 @@ static int __devinit snd_card_als100_pnp(int dev, struct snd_card_als100 *acard, const struct pnp_card_device_id *id) { struct pnp_dev *pdev; - struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL); int err; - if (!cfg) - return -ENOMEM; acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL); - if (acard->dev == NULL) { - kfree(cfg); + if (acard->dev == NULL) return -ENODEV; - } + acard->devmpu = pnp_request_card_device(card, id->devs[1].id, acard->dev); acard->devopl = pnp_request_card_device(card, id->devs[2].id, acard->dev); pdev = acard->dev; - pnp_init_resource_table(cfg); - - /* override resources */ - if (port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[0], port[dev], 16); - if (dma8[dev] != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[0], dma8[dev], 1); - if (dma16[dev] != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[1], dma16[dev], 1); - if (irq[dev] != SNDRV_AUTO_IRQ) - pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); - if (pnp_manual_config_dev(pdev, cfg, 0) < 0) - snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n"); err = pnp_activate_dev(pdev); if (err < 0) { snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n"); - kfree(cfg); return err; } port[dev] = pnp_port_start(pdev, 0); @@ -152,13 +119,6 @@ static int __devinit snd_card_als100_pnp(int dev, struct snd_card_als100 *acard, pdev = acard->devmpu; if (pdev != NULL) { - pnp_init_resource_table(cfg); - if (mpu_port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2); - if (mpu_irq[dev] != SNDRV_AUTO_IRQ) - pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1); - if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0) - snd_printk(KERN_ERR PFX "MPU401 the requested resources are invalid, using auto config\n"); err = pnp_activate_dev(pdev); if (err < 0) goto __mpu_error; @@ -176,11 +136,6 @@ static int __devinit snd_card_als100_pnp(int dev, struct snd_card_als100 *acard, pdev = acard->devopl; if (pdev != NULL) { - pnp_init_resource_table(cfg); - if (fm_port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[0], fm_port[dev], 4); - if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0) - snd_printk(KERN_ERR PFX "OPL3 the requested resources are invalid, using auto config\n"); err = pnp_activate_dev(pdev); if (err < 0) goto __fm_error; @@ -195,7 +150,6 @@ static int __devinit snd_card_als100_pnp(int dev, struct snd_card_als100 *acard, fm_port[dev] = -1; } - kfree(cfg); return 0; } diff --git a/sound/isa/azt2320.c b/sound/isa/azt2320.c index b615538..154e728 100644 --- a/sound/isa/azt2320.c +++ b/sound/isa/azt2320.c @@ -29,7 +29,6 @@ activation method (full-duplex audio!). */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/init.h> @@ -72,22 +71,6 @@ module_param_array(id, charp, NULL, 0444); MODULE_PARM_DESC(id, "ID string for azt2320 based soundcard."); module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable azt2320 based soundcard."); -module_param_array(port, long, NULL, 0444); -MODULE_PARM_DESC(port, "Port # for azt2320 driver."); -module_param_array(wss_port, long, NULL, 0444); -MODULE_PARM_DESC(wss_port, "WSS Port # for azt2320 driver."); -module_param_array(mpu_port, long, NULL, 0444); -MODULE_PARM_DESC(mpu_port, "MPU-401 port # for azt2320 driver."); -module_param_array(fm_port, long, NULL, 0444); -MODULE_PARM_DESC(fm_port, "FM port # for azt2320 driver."); -module_param_array(irq, int, NULL, 0444); -MODULE_PARM_DESC(irq, "IRQ # for azt2320 driver."); -module_param_array(mpu_irq, int, NULL, 0444); -MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for azt2320 driver."); -module_param_array(dma1, int, NULL, 0444); -MODULE_PARM_DESC(dma1, "1st DMA # for azt2320 driver."); -module_param_array(dma2, int, NULL, 0444); -MODULE_PARM_DESC(dma2, "2nd DMA # for azt2320 driver."); struct snd_card_azt2320 { int dev_no; @@ -121,43 +104,19 @@ static int __devinit snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acar const struct pnp_card_device_id *id) { struct pnp_dev *pdev; - struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); int err; - if (!cfg) - return -ENOMEM; - acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL); - if (acard->dev == NULL) { - kfree(cfg); + if (acard->dev == NULL) return -ENODEV; - } acard->devmpu = pnp_request_card_device(card, id->devs[1].id, NULL); pdev = acard->dev; - pnp_init_resource_table(cfg); - - /* override resources */ - if (port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[0], port[dev], 16); - if (fm_port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4); - if (wss_port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[2], wss_port[dev], 4); - if (dma1[dev] != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1); - if (dma2[dev] != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1); - if (irq[dev] != SNDRV_AUTO_IRQ) - pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); - if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0) - snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n"); err = pnp_activate_dev(pdev); if (err < 0) { snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n"); - kfree(cfg); return err; } port[dev] = pnp_port_start(pdev, 0); @@ -169,13 +128,6 @@ static int __devinit snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acar pdev = acard->devmpu; if (pdev != NULL) { - pnp_init_resource_table(cfg); - if (mpu_port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2); - if (mpu_irq[dev] != SNDRV_AUTO_IRQ) - pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1); - if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0) - snd_printk(KERN_ERR PFX "MPU401 the requested resources are invalid, using auto config\n"); err = pnp_activate_dev(pdev); if (err < 0) goto __mpu_error; @@ -191,7 +143,6 @@ static int __devinit snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acar mpu_port[dev] = -1; } - kfree (cfg); return 0; } diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c index f471f8a..4d198ec 100644 --- a/sound/isa/cmi8330.c +++ b/sound/isa/cmi8330.c @@ -43,7 +43,6 @@ * full control over both mixers. */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/err.h> #include <linux/isa.h> @@ -286,39 +285,21 @@ static int __devinit snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard, const struct pnp_card_device_id *id) { struct pnp_dev *pdev; - struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); int err; - if (!cfg) - return -ENOMEM; acard->cap = pnp_request_card_device(card, id->devs[0].id, NULL); - if (acard->cap == NULL) { - kfree(cfg); + if (acard->cap == NULL) return -EBUSY; - } + acard->play = pnp_request_card_device(card, id->devs[1].id, NULL); - if (acard->play == NULL) { - kfree(cfg); + if (acard->play == NULL) return -EBUSY; - } pdev = acard->cap; - pnp_init_resource_table(cfg); - /* allocate AD1848 resources */ - if (wssport[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[0], wssport[dev], 8); - if (wssdma[dev] != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[0], wssdma[dev], 1); - if (wssirq[dev] != SNDRV_AUTO_IRQ) - pnp_resource_change(&cfg->irq_resource[0], wssirq[dev], 1); - - err = pnp_manual_config_dev(pdev, cfg, 0); - if (err < 0) - snd_printk(KERN_ERR "CMI8330/C3D (AD1848) PnP manual resources are invalid, using auto config\n"); + err = pnp_activate_dev(pdev); if (err < 0) { snd_printk(KERN_ERR "CMI8330/C3D (AD1848) PnP configure failure\n"); - kfree(cfg); return -EBUSY; } wssport[dev] = pnp_port_start(pdev, 0); @@ -327,23 +308,10 @@ static int __devinit snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard, /* allocate SB16 resources */ pdev = acard->play; - pnp_init_resource_table(cfg); - if (sbport[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[0], sbport[dev], 16); - if (sbdma8[dev] != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[0], sbdma8[dev], 1); - if (sbdma16[dev] != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[1], sbdma16[dev], 1); - if (sbirq[dev] != SNDRV_AUTO_IRQ) - pnp_resource_change(&cfg->irq_resource[0], sbirq[dev], 1); - - err = pnp_manual_config_dev(pdev, cfg, 0); - if (err < 0) - snd_printk(KERN_ERR "CMI8330/C3D (SB16) PnP manual resources are invalid, using auto config\n"); + err = pnp_activate_dev(pdev); if (err < 0) { snd_printk(KERN_ERR "CMI8330/C3D (SB16) PnP configure failure\n"); - kfree(cfg); return -EBUSY; } sbport[dev] = pnp_port_start(pdev, 0); @@ -351,7 +319,6 @@ static int __devinit snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard, sbdma16[dev] = pnp_dma(pdev, 1); sbirq[dev] = pnp_irq(pdev, 0); - kfree(cfg); return 0; } #endif diff --git a/sound/isa/cs423x/cs4231.c b/sound/isa/cs423x/cs4231.c index 13db684..e9462b9 100644 --- a/sound/isa/cs423x/cs4231.c +++ b/sound/isa/cs423x/cs4231.c @@ -20,7 +20,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/err.h> #include <linux/isa.h> diff --git a/sound/isa/cs423x/cs4231_lib.c b/sound/isa/cs423x/cs4231_lib.c index a5eb965..0aa8649 100644 --- a/sound/isa/cs423x/cs4231_lib.c +++ b/sound/isa/cs423x/cs4231_lib.c @@ -24,7 +24,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/pm.h> #include <linux/init.h> @@ -333,7 +332,6 @@ void snd_cs4231_mce_down(struct snd_cs4231 *chip) !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) { return; } - snd_cs4231_busy_wait(chip); /* * Wait for (possible -- during init auto-calibration may not be set) diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c index 5784b43..dbe63db4 100644 --- a/sound/isa/cs423x/cs4236.c +++ b/sound/isa/cs423x/cs4236.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/err.h> #include <linux/isa.h> @@ -270,29 +269,9 @@ static struct pnp_card_device_id snd_cs423x_pnpids[] = { MODULE_DEVICE_TABLE(pnp_card, snd_cs423x_pnpids); /* WSS initialization */ -static int __devinit snd_cs423x_pnp_init_wss(int dev, struct pnp_dev *pdev, - struct pnp_resource_table *cfg) +static int __devinit snd_cs423x_pnp_init_wss(int dev, struct pnp_dev *pdev) { - int err; - - pnp_init_resource_table(cfg); - if (port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[0], port[dev], 4); - if (fm_port[dev] != SNDRV_AUTO_PORT && fm_port[dev] > 0) - pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4); - if (sb_port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[2], sb_port[dev], 16); - if (irq[dev] != SNDRV_AUTO_IRQ) - pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); - if (dma1[dev] != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1); - if (dma2[dev] != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[1], dma2[dev] < 0 ? 4 : dma2[dev], 1); - err = pnp_manual_config_dev(pdev, cfg, 0); - if (err < 0) - snd_printk(KERN_ERR IDENT " WSS PnP manual resources are invalid, using auto config\n"); - err = pnp_activate_dev(pdev); - if (err < 0) { + if (pnp_activate_dev(pdev) < 0) { printk(KERN_ERR IDENT " WSS PnP configure failed for WSS (out of resources?)\n"); return -EBUSY; } @@ -311,19 +290,9 @@ static int __devinit snd_cs423x_pnp_init_wss(int dev, struct pnp_dev *pdev, } /* CTRL initialization */ -static int __devinit snd_cs423x_pnp_init_ctrl(int dev, struct pnp_dev *pdev, - struct pnp_resource_table *cfg) +static int __devinit snd_cs423x_pnp_init_ctrl(int dev, struct pnp_dev *pdev) { - int err; - - pnp_init_resource_table(cfg); - if (cport[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[0], cport[dev], 8); - err = pnp_manual_config_dev(pdev, cfg, 0); - if (err < 0) - snd_printk(KERN_ERR IDENT " CTRL PnP manual resources are invalid, using auto config\n"); - err = pnp_activate_dev(pdev); - if (err < 0) { + if (pnp_activate_dev(pdev) < 0) { printk(KERN_ERR IDENT " CTRL PnP configure failed for WSS (out of resources?)\n"); return -EBUSY; } @@ -333,21 +302,9 @@ static int __devinit snd_cs423x_pnp_init_ctrl(int dev, struct pnp_dev *pdev, } /* MPU initialization */ -static int __devinit snd_cs423x_pnp_init_mpu(int dev, struct pnp_dev *pdev, - struct pnp_resource_table *cfg) +static int __devinit snd_cs423x_pnp_init_mpu(int dev, struct pnp_dev *pdev) { - int err; - - pnp_init_resource_table(cfg); - if (mpu_port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2); - if (mpu_irq[dev] != SNDRV_AUTO_IRQ && mpu_irq[dev] >= 0) - pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1); - err = pnp_manual_config_dev(pdev, cfg, 0); - if (err < 0) - snd_printk(KERN_ERR IDENT " MPU401 PnP manual resources are invalid, using auto config\n"); - err = pnp_activate_dev(pdev); - if (err < 0) { + if (pnp_activate_dev(pdev) < 0) { printk(KERN_ERR IDENT " MPU401 PnP configure failed for WSS (out of resources?)\n"); mpu_port[dev] = SNDRV_AUTO_PORT; mpu_irq[dev] = SNDRV_AUTO_IRQ; @@ -368,15 +325,8 @@ static int __devinit snd_cs423x_pnp_init_mpu(int dev, struct pnp_dev *pdev, static int __devinit snd_card_cs4232_pnp(int dev, struct snd_card_cs4236 *acard, struct pnp_dev *pdev) { - struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL); - - if (!cfg) - return -ENOMEM; - if (snd_cs423x_pnp_init_wss(dev, acard->wss, cfg) < 0) { - kfree(cfg); + if (snd_cs423x_pnp_init_wss(dev, acard->wss) < 0) return -EBUSY; - } - kfree(cfg); cport[dev] = -1; return 0; } @@ -386,43 +336,33 @@ static int __devinit snd_card_cs423x_pnpc(int dev, struct snd_card_cs4236 *acard struct pnp_card_link *card, const struct pnp_card_device_id *id) { - struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL); - - if (!cfg) - return -ENOMEM; - acard->wss = pnp_request_card_device(card, id->devs[0].id, NULL); if (acard->wss == NULL) - goto error; + return -EBUSY; acard->ctrl = pnp_request_card_device(card, id->devs[1].id, NULL); if (acard->ctrl == NULL) - goto error; + return -EBUSY; if (id->devs[2].id[0]) { acard->mpu = pnp_request_card_device(card, id->devs[2].id, NULL); if (acard->mpu == NULL) - goto error; + return -EBUSY; } /* WSS initialization */ - if (snd_cs423x_pnp_init_wss(dev, acard->wss, cfg) < 0) - goto error; + if (snd_cs423x_pnp_init_wss(dev, acard->wss) < 0) + return -EBUSY; /* CTRL initialization */ if (acard->ctrl && cport[dev] > 0) { - if (snd_cs423x_pnp_init_ctrl(dev, acard->ctrl, cfg) < 0) - goto error; + if (snd_cs423x_pnp_init_ctrl(dev, acard->ctrl) < 0) + return -EBUSY; } /* MPU initialization */ if (acard->mpu && mpu_port[dev] > 0) { - if (snd_cs423x_pnp_init_mpu(dev, acard->mpu, cfg) < 0) - goto error; + if (snd_cs423x_pnp_init_mpu(dev, acard->mpu) < 0) + return -EBUSY; } - kfree(cfg); return 0; - - error: - kfree(cfg); - return -EBUSY; } #endif /* CONFIG_PNP */ diff --git a/sound/isa/cs423x/cs4236_lib.c b/sound/isa/cs423x/cs4236_lib.c index 6bd0644..de71910 100644 --- a/sound/isa/cs423x/cs4236_lib.c +++ b/sound/isa/cs423x/cs4236_lib.c @@ -79,7 +79,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/init.h> diff --git a/sound/isa/dt019x.c b/sound/isa/dt019x.c index ce57d52..a0242c3 100644 --- a/sound/isa/dt019x.c +++ b/sound/isa/dt019x.c @@ -21,7 +21,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/wait.h> #include <linux/pnp.h> @@ -56,18 +55,6 @@ module_param_array(id, charp, NULL, 0444); MODULE_PARM_DESC(id, "ID string for DT-019X based soundcard."); module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable DT-019X based soundcard."); -module_param_array(port, long, NULL, 0444); -MODULE_PARM_DESC(port, "Port # for dt019x driver."); -module_param_array(mpu_port, long, NULL, 0444); -MODULE_PARM_DESC(mpu_port, "MPU-401 port # for dt019x driver."); -module_param_array(fm_port, long, NULL, 0444); -MODULE_PARM_DESC(fm_port, "FM port # for dt019x driver."); -module_param_array(irq, int, NULL, 0444); -MODULE_PARM_DESC(irq, "IRQ # for dt019x driver."); -module_param_array(mpu_irq, int, NULL, 0444); -MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for dt019x driver."); -module_param_array(dma8, int, NULL, 0444); -MODULE_PARM_DESC(dma8, "8-bit DMA # for dt019x driver."); struct snd_card_dt019x { struct pnp_dev *dev; @@ -95,36 +82,20 @@ static int __devinit snd_card_dt019x_pnp(int dev, struct snd_card_dt019x *acard, const struct pnp_card_device_id *pid) { struct pnp_dev *pdev; - struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); int err; - if (!cfg) - return -ENOMEM; - acard->dev = pnp_request_card_device(card, pid->devs[0].id, NULL); - if (acard->dev == NULL) { - kfree (cfg); + if (acard->dev == NULL) return -ENODEV; - } + acard->devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL); acard->devopl = pnp_request_card_device(card, pid->devs[2].id, NULL); pdev = acard->dev; - pnp_init_resource_table(cfg); - - if (port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[0], port[dev], 16); - if (dma8[dev] != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[0], dma8[dev], 1); - if (irq[dev] != SNDRV_AUTO_IRQ) - pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); - if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0) - snd_printk(KERN_ERR PFX "DT-019X AUDIO the requested resources are invalid, using auto config\n"); err = pnp_activate_dev(pdev); if (err < 0) { snd_printk(KERN_ERR PFX "DT-019X AUDIO pnp configure failure\n"); - kfree(cfg); return err; } @@ -135,15 +106,7 @@ static int __devinit snd_card_dt019x_pnp(int dev, struct snd_card_dt019x *acard, port[dev],irq[dev],dma8[dev]); pdev = acard->devmpu; - if (pdev != NULL) { - pnp_init_resource_table(cfg); - if (mpu_port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2); - if (mpu_irq[dev] != SNDRV_AUTO_IRQ) - pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1); - if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0) - snd_printk(KERN_ERR PFX "DT-019X MPU401 the requested resources are invalid, using auto config\n"); err = pnp_activate_dev(pdev); if (err < 0) { pnp_release_card_device(pdev); @@ -162,11 +125,6 @@ static int __devinit snd_card_dt019x_pnp(int dev, struct snd_card_dt019x *acard, pdev = acard->devopl; if (pdev != NULL) { - pnp_init_resource_table(cfg); - if (fm_port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[0], fm_port[dev], 4); - if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0) - snd_printk(KERN_ERR PFX "DT-019X OPL3 the requested resources are invalid, using auto config\n"); err = pnp_activate_dev(pdev); if (err < 0) { pnp_release_card_device(pdev); @@ -181,7 +139,6 @@ static int __devinit snd_card_dt019x_pnp(int dev, struct snd_card_dt019x *acard, fm_port[dev] = -1; } - kfree(cfg); return 0; } diff --git a/sound/isa/es1688/es1688.c b/sound/isa/es1688/es1688.c index 74bbc92..f88639e 100644 --- a/sound/isa/es1688/es1688.c +++ b/sound/isa/es1688/es1688.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/err.h> #include <linux/isa.h> diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c index 5c26d49..1e1e575 100644 --- a/sound/isa/es1688/es1688_lib.c +++ b/sound/isa/es1688/es1688_lib.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/delay.h> diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c index c1af28f..90498e4 100644 --- a/sound/isa/es18xx.c +++ b/sound/isa/es18xx.c @@ -77,7 +77,6 @@ * needed for ZV, so maybe the datasheet is entirely wrong here. */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/err.h> #include <linux/isa.h> @@ -163,7 +162,7 @@ struct snd_audiodrive { #define ES18XX_DUPLEX_SAME 0x0010 /* Playback and record must share the same rate */ #define ES18XX_NEW_RATE 0x0020 /* More precise rate setting */ #define ES18XX_AUXB 0x0040 /* AuxB mixer control */ -#define ES18XX_HWV 0x0080 /* Has seperate hardware volume mixer controls*/ +#define ES18XX_HWV 0x0080 /* Has separate hardware volume mixer controls*/ #define ES18XX_MONO 0x0100 /* Mono_in mixer control */ #define ES18XX_I2S 0x0200 /* I2S mixer control */ #define ES18XX_MUTEREC 0x0400 /* Record source can be muted */ @@ -1442,6 +1441,8 @@ static int __devinit snd_es18xx_initialize(struct snd_es18xx *chip) snd_es18xx_write(chip, 0xB2, 0x50); /* Enable MPU and hardware volume interrupt */ snd_es18xx_mixer_write(chip, 0x64, 0x42); + /* Enable ESS wavetable input */ + snd_es18xx_mixer_bits(chip, 0x48, 0x10, 0x10); } else { int irqmask, dma1mask, dma2mask; @@ -2035,31 +2036,9 @@ static struct pnp_device_id snd_audiodrive_pnpbiosids[] = { MODULE_DEVICE_TABLE(pnp, snd_audiodrive_pnpbiosids); /* PnP main device initialization */ -static int __devinit snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev, - struct pnp_resource_table *cfg) +static int __devinit snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev) { - int err; - - pnp_init_resource_table(cfg); - if (port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[0], port[dev], 16); - if (fm_port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4); - if (mpu_port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[2], mpu_port[dev], 2); - if (dma1[dev] != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1); - if (dma2[dev] != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1); - if (irq[dev] != SNDRV_AUTO_IRQ) - pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); - if (pnp_device_is_isapnp(pdev)) { - err = pnp_manual_config_dev(pdev, cfg, 0); - if (err < 0) - snd_printk(KERN_ERR PFX "PnP manual resources are invalid, using auto config\n"); - } - err = pnp_activate_dev(pdev); - if (err < 0) { + if (pnp_activate_dev(pdev) < 0) { snd_printk(KERN_ERR PFX "PnP configure failure (out of resources?)\n"); return -EBUSY; } @@ -2087,16 +2066,9 @@ static int __devinit snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev, static int __devinit snd_audiodrive_pnp(int dev, struct snd_audiodrive *acard, struct pnp_dev *pdev) { - struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); - - if (!cfg) - return -ENOMEM; acard->dev = pdev; - if (snd_audiodrive_pnp_init_main(dev, acard->dev, cfg) < 0) { - kfree(cfg); + if (snd_audiodrive_pnp_init_main(dev, acard->dev) < 0) return -EBUSY; - } - kfree(cfg); return 0; } @@ -2125,33 +2097,24 @@ static int __devinit snd_audiodrive_pnpc(int dev, struct snd_audiodrive *acard, struct pnp_card_link *card, const struct pnp_card_device_id *id) { - struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); - - if (!cfg) - return -ENOMEM; acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL); - if (acard->dev == NULL) { - kfree(cfg); + if (acard->dev == NULL) return -EBUSY; - } + acard->devc = pnp_request_card_device(card, id->devs[1].id, NULL); - if (acard->devc == NULL) { - kfree(cfg); + if (acard->devc == NULL) return -EBUSY; - } + /* Control port initialization */ if (pnp_activate_dev(acard->devc) < 0) { - kfree(cfg); snd_printk(KERN_ERR PFX "PnP control configure failure (out of resources?)\n"); return -EAGAIN; } snd_printdd("pnp: port=0x%llx\n", (unsigned long long)pnp_port_start(acard->devc, 0)); - if (snd_audiodrive_pnp_init_main(dev, acard->dev, cfg) < 0) { - kfree(cfg); + if (snd_audiodrive_pnp_init_main(dev, acard->dev) < 0) return -EBUSY; - } - kfree(cfg); + return 0; } #endif /* CONFIG_PNP */ diff --git a/sound/isa/gus/Makefile b/sound/isa/gus/Makefile index df3d59f2..6cd4ee0 100644 --- a/sound/isa/gus/Makefile +++ b/sound/isa/gus/Makefile @@ -9,7 +9,6 @@ snd-gus-lib-objs := gus_main.o \ gus_pcm.o gus_mixer.o \ gus_uart.o \ gus_reset.o -snd-gus-synth-objs := gus_synth.o gus_sample.o gus_simple.o gus_instr.o snd-gusclassic-objs := gusclassic.o snd-gusextreme-objs := gusextreme.o @@ -17,20 +16,9 @@ snd-gusmax-objs := gusmax.o snd-interwave-objs := interwave.o snd-interwave-stb-objs := interwave-stb.o -# -# this function returns: -# "m" - CONFIG_SND_SEQUENCER is m -# <empty string> - CONFIG_SND_SEQUENCER is undefined -# otherwise parameter #1 value -# -sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1))) - # Toplevel Module Dependency obj-$(CONFIG_SND_GUSCLASSIC) += snd-gusclassic.o snd-gus-lib.o obj-$(CONFIG_SND_GUSMAX) += snd-gusmax.o snd-gus-lib.o obj-$(CONFIG_SND_GUSEXTREME) += snd-gusextreme.o snd-gus-lib.o obj-$(CONFIG_SND_INTERWAVE) += snd-interwave.o snd-gus-lib.o obj-$(CONFIG_SND_INTERWAVE_STB) += snd-interwave-stb.o snd-gus-lib.o -obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-gus-synth.o - -obj-m := $(sort $(obj-m)) diff --git a/sound/isa/gus/gus_dma.c b/sound/isa/gus/gus_dma.c index fc90514..f45f611 100644 --- a/sound/isa/gus/gus_dma.c +++ b/sound/isa/gus/gus_dma.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <asm/dma.h> #include <linux/slab.h> #include <sound/core.h> diff --git a/sound/isa/gus/gus_dram.c b/sound/isa/gus/gus_dram.c index 9eaa932..fd2e2e2 100644 --- a/sound/isa/gus/gus_dram.c +++ b/sound/isa/gus/gus_dram.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <sound/core.h> #include <sound/gus.h> diff --git a/sound/isa/gus/gus_instr.c b/sound/isa/gus/gus_instr.c index bf137ea..4dc9caf 100644 --- a/sound/isa/gus/gus_instr.c +++ b/sound/isa/gus/gus_instr.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <sound/core.h> #include <sound/gus.h> diff --git a/sound/isa/gus/gus_io.c b/sound/isa/gus/gus_io.c index 3d4f899..ca79878 100644 --- a/sound/isa/gus/gus_io.c +++ b/sound/isa/gus/gus_io.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/time.h> #include <sound/core.h> diff --git a/sound/isa/gus/gus_irq.c b/sound/isa/gus/gus_irq.c index cd9a6f1c..041894d 100644 --- a/sound/isa/gus/gus_irq.c +++ b/sound/isa/gus/gus_irq.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <sound/core.h> #include <sound/info.h> #include <sound/gus.h> diff --git a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c index b14d5d6..cccc16c 100644 --- a/sound/isa/gus/gus_main.c +++ b/sound/isa/gus/gus_main.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/delay.h> @@ -104,12 +103,6 @@ static int snd_gus_free(struct snd_gus_card *gus) { if (gus->gf1.res_port2 == NULL) goto __hw_end; -#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) - if (gus->seq_dev) { - snd_device_free(gus->card, gus->seq_dev); - gus->seq_dev = NULL; - } -#endif snd_gf1_stop(gus); snd_gus_init_dma_irq(gus, 0); __hw_end: @@ -408,14 +401,6 @@ static int snd_gus_check_version(struct snd_gus_card * gus) return 0; } -#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) -static void snd_gus_seq_dev_free(struct snd_seq_device *seq_dev) -{ - struct snd_gus_card *gus = seq_dev->private_data; - gus->seq_dev = NULL; -} -#endif - int snd_gus_initialize(struct snd_gus_card *gus) { int err; @@ -430,15 +415,6 @@ int snd_gus_initialize(struct snd_gus_card *gus) } if ((err = snd_gus_init_dma_irq(gus, 1)) < 0) return err; -#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) - if (snd_seq_device_new(gus->card, 1, SNDRV_SEQ_DEV_ID_GUS, - sizeof(struct snd_gus_card *), &gus->seq_dev) >= 0) { - strcpy(gus->seq_dev->name, "GUS"); - *(struct snd_gus_card **)SNDRV_SEQ_DEVICE_ARGPTR(gus->seq_dev) = gus; - gus->seq_dev->private_data = gus; - gus->seq_dev->private_free = snd_gus_seq_dev_free; - } -#endif snd_gf1_start(gus); gus->initialized = 1; return 0; diff --git a/sound/isa/gus/gus_mem.c b/sound/isa/gus/gus_mem.c index bcf4656..661205c 100644 --- a/sound/isa/gus/gus_mem.c +++ b/sound/isa/gus/gus_mem.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/slab.h> #include <linux/string.h> #include <sound/core.h> diff --git a/sound/isa/gus/gus_mem_proc.c b/sound/isa/gus/gus_mem_proc.c index f69a447..2803e22 100644 --- a/sound/isa/gus/gus_mem_proc.c +++ b/sound/isa/gus/gus_mem_proc.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/gus.h> diff --git a/sound/isa/gus/gus_mixer.c b/sound/isa/gus/gus_mixer.c index a96253e..ebdb334 100644 --- a/sound/isa/gus/gus_mixer.c +++ b/sound/isa/gus/gus_mixer.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <linux/wait.h> #include <sound/core.h> diff --git a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c index a7971f5..99731dc 100644 --- a/sound/isa/gus/gus_pcm.c +++ b/sound/isa/gus/gus_pcm.c @@ -25,7 +25,6 @@ * */ -#include <sound/driver.h> #include <asm/dma.h> #include <linux/slab.h> #include <sound/core.h> diff --git a/sound/isa/gus/gus_reset.c b/sound/isa/gus/gus_reset.c index 20cfdb8..3d1fed0c 100644 --- a/sound/isa/gus/gus_reset.c +++ b/sound/isa/gus/gus_reset.c @@ -18,7 +18,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/time.h> diff --git a/sound/isa/gus/gus_sample.c b/sound/isa/gus/gus_sample.c deleted file mode 100644 index cba0829..0000000 --- a/sound/isa/gus/gus_sample.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Routines for Gravis UltraSound soundcards - Sample support - * Copyright (c) by Jaroslav Kysela <perex@perex.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 <linux/time.h> -#include <sound/core.h> -#include <sound/gus.h> - -/* - * - */ - -static void select_instrument(struct snd_gus_card * gus, struct snd_gus_voice * v) -{ - struct snd_seq_kinstr *instr; - -#if 0 - printk("select instrument: cluster = %li, std = 0x%x, bank = %i, prg = %i\n", - v->instr.cluster, - v->instr.std, - v->instr.bank, - v->instr.prg); -#endif - instr = snd_seq_instr_find(gus->gf1.ilist, &v->instr, 0, 1); - if (instr != NULL) { - if (instr->ops) { - if (!strcmp(instr->ops->instr_type, SNDRV_SEQ_INSTR_ID_SIMPLE)) - snd_gf1_simple_init(v); - } - snd_seq_instr_free_use(gus->gf1.ilist, instr); - } -} - -/* - * - */ - -static void event_sample(struct snd_seq_event *ev, struct snd_gus_port *p, - struct snd_gus_voice *v) -{ - if (v->sample_ops && v->sample_ops->sample_stop) - v->sample_ops->sample_stop(p->gus, v, SAMPLE_STOP_IMMEDIATELY); - v->instr.std = ev->data.sample.param.sample.std; - if (v->instr.std & 0xff000000) { /* private instrument */ - v->instr.std &= 0x00ffffff; - v->instr.std |= (unsigned int)ev->source.client << 24; - } - v->instr.bank = ev->data.sample.param.sample.bank; - v->instr.prg = ev->data.sample.param.sample.prg; - select_instrument(p->gus, v); -} - -static void event_cluster(struct snd_seq_event *ev, struct snd_gus_port *p, - struct snd_gus_voice *v) -{ - if (v->sample_ops && v->sample_ops->sample_stop) - v->sample_ops->sample_stop(p->gus, v, SAMPLE_STOP_IMMEDIATELY); - v->instr.cluster = ev->data.sample.param.cluster.cluster; - select_instrument(p->gus, v); -} - -static void event_start(struct snd_seq_event *ev, struct snd_gus_port *p, - struct snd_gus_voice *v) -{ - if (v->sample_ops && v->sample_ops->sample_start) - v->sample_ops->sample_start(p->gus, v, ev->data.sample.param.position); -} - -static void event_stop(struct snd_seq_event *ev, struct snd_gus_port *p, - struct snd_gus_voice *v) -{ - if (v->sample_ops && v->sample_ops->sample_stop) - v->sample_ops->sample_stop(p->gus, v, ev->data.sample.param.stop_mode); -} - -static void event_freq(struct snd_seq_event *ev, struct snd_gus_port *p, - struct snd_gus_voice *v) -{ - if (v->sample_ops && v->sample_ops->sample_freq) - v->sample_ops->sample_freq(p->gus, v, ev->data.sample.param.frequency); -} - -static void event_volume(struct snd_seq_event *ev, struct snd_gus_port *p, - struct snd_gus_voice *v) -{ - if (v->sample_ops && v->sample_ops->sample_volume) - v->sample_ops->sample_volume(p->gus, v, &ev->data.sample.param.volume); -} - -static void event_loop(struct snd_seq_event *ev, struct snd_gus_port *p, - struct snd_gus_voice *v) -{ - if (v->sample_ops && v->sample_ops->sample_loop) - v->sample_ops->sample_loop(p->gus, v, &ev->data.sample.param.loop); -} - -static void event_position(struct snd_seq_event *ev, struct snd_gus_port *p, - struct snd_gus_voice *v) -{ - if (v->sample_ops && v->sample_ops->sample_pos) - v->sample_ops->sample_pos(p->gus, v, ev->data.sample.param.position); -} - -static void event_private1(struct snd_seq_event *ev, struct snd_gus_port *p, - struct snd_gus_voice *v) -{ - if (v->sample_ops && v->sample_ops->sample_private1) - v->sample_ops->sample_private1(p->gus, v, (unsigned char *)&ev->data.sample.param.raw8); -} - -typedef void (gus_sample_event_handler_t)(struct snd_seq_event *ev, - struct snd_gus_port *p, - struct snd_gus_voice *v); -static gus_sample_event_handler_t *gus_sample_event_handlers[9] = { - event_sample, - event_cluster, - event_start, - event_stop, - event_freq, - event_volume, - event_loop, - event_position, - event_private1 -}; - -void snd_gus_sample_event(struct snd_seq_event *ev, struct snd_gus_port *p) -{ - int idx, voice; - struct snd_gus_card *gus = p->gus; - struct snd_gus_voice *v; - unsigned long flags; - - idx = ev->type - SNDRV_SEQ_EVENT_SAMPLE; - if (idx < 0 || idx > 8) - return; - for (voice = 0; voice < 32; voice++) { - v = &gus->gf1.voices[voice]; - if (v->use && v->client == ev->source.client && - v->port == ev->source.port && - v->index == ev->data.sample.channel) { - spin_lock_irqsave(&gus->event_lock, flags); - gus_sample_event_handlers[idx](ev, p, v); - spin_unlock_irqrestore(&gus->event_lock, flags); - return; - } - } -} diff --git a/sound/isa/gus/gus_simple.c b/sound/isa/gus/gus_simple.c deleted file mode 100644 index 39d121e..0000000 --- a/sound/isa/gus/gus_simple.c +++ /dev/null @@ -1,634 +0,0 @@ -/* - * Routines for Gravis UltraSound soundcards - Simple instrument handlers - * Copyright (c) by Jaroslav Kysela <perex@perex.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 <linux/time.h> -#include <sound/core.h> -#include <sound/gus.h> -#include "gus_tables.h" - -/* - * - */ - -static void interrupt_wave(struct snd_gus_card *gus, struct snd_gus_voice *voice); -static void interrupt_volume(struct snd_gus_card *gus, struct snd_gus_voice *voice); -static void interrupt_effect(struct snd_gus_card *gus, struct snd_gus_voice *voice); - -static void sample_start(struct snd_gus_card *gus, struct snd_gus_voice *voice, snd_seq_position_t position); -static void sample_stop(struct snd_gus_card *gus, struct snd_gus_voice *voice, int mode); -static void sample_freq(struct snd_gus_card *gus, struct snd_gus_voice *voice, snd_seq_frequency_t freq); -static void sample_volume(struct snd_gus_card *card, struct snd_gus_voice *voice, struct snd_seq_ev_volume *volume); -static void sample_loop(struct snd_gus_card *card, struct snd_gus_voice *voice, struct snd_seq_ev_loop *loop); -static void sample_pos(struct snd_gus_card *card, struct snd_gus_voice *voice, snd_seq_position_t position); -static void sample_private1(struct snd_gus_card *card, struct snd_gus_voice *voice, unsigned char *data); - -static struct snd_gus_sample_ops sample_ops = { - sample_start, - sample_stop, - sample_freq, - sample_volume, - sample_loop, - sample_pos, - sample_private1 -}; - -#if 0 - -static void note_stop(struct snd_gus_card *gus, struct snd_gus_voice *voice, int wait); -static void note_wait(struct snd_gus_card *gus, struct snd_gus_voice *voice); -static void note_off(struct snd_gus_card *gus, struct snd_gus_voice *voice); -static void note_volume(struct snd_gus_card *card, struct snd_gus_voice *voice); -static void note_pitchbend(struct snd_gus_card *card, struct snd_gus_voice *voice); -static void note_vibrato(struct snd_gus_card *card, struct snd_gus_voice *voice); -static void note_tremolo(struct snd_gus_card *card, struct snd_gus_voice *voice); - -static struct snd_gus_note_handlers note_commands = { - note_stop, - note_wait, - note_off, - note_volume, - note_pitchbend, - note_vibrato, - note_tremolo -}; - -static void chn_trigger_down(struct snd_gus_card *card, ultra_channel_t *channel, ultra_instrument_t *instrument, unsigned char note, unsigned char velocity, unsigned char priority ); -static void chn_trigger_up( ultra_card_t *card, ultra_note_t *note ); -static void chn_control( ultra_card_t *card, ultra_channel_t *channel, unsigned short p1, unsigned short p2 ); - -static struct ULTRA_STRU_INSTRUMENT_CHANNEL_COMMANDS channel_commands = { - chn_trigger_down, - chn_trigger_up, - chn_control -}; - -#endif - -static void do_volume_envelope(struct snd_gus_card *card, struct snd_gus_voice *voice); -static void do_pan_envelope(struct snd_gus_card *card, struct snd_gus_voice *voice); - -/* - * - */ - -static void interrupt_wave(struct snd_gus_card *gus, struct snd_gus_voice *voice) -{ - spin_lock(&gus->event_lock); - snd_gf1_stop_voice(gus, voice->number); - spin_lock(&gus->reg_lock); - snd_gf1_select_voice(gus, voice->number); - snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, 0); - spin_unlock(&gus->reg_lock); - voice->flags &= ~SNDRV_GF1_VFLG_RUNNING; - spin_unlock(&gus->event_lock); -} - -static void interrupt_volume(struct snd_gus_card *gus, struct snd_gus_voice *voice) -{ - spin_lock(&gus->event_lock); - if (voice->flags & SNDRV_GF1_VFLG_RUNNING) - do_volume_envelope(gus, voice); - else - snd_gf1_stop_voice(gus, voice->number); - spin_unlock(&gus->event_lock); -} - -static void interrupt_effect(struct snd_gus_card *gus, struct snd_gus_voice *voice) -{ - spin_lock(&gus->event_lock); - if ((voice->flags & (SNDRV_GF1_VFLG_RUNNING|SNDRV_GF1_VFLG_EFFECT_TIMER1)) == - (SNDRV_GF1_VFLG_RUNNING|SNDRV_GF1_VFLG_EFFECT_TIMER1)) - do_pan_envelope(gus, voice); - spin_unlock(&gus->event_lock); -} - -/* - * - */ - -static void do_volume_envelope(struct snd_gus_card *gus, struct snd_gus_voice *voice) -{ - unsigned short next, rate, old_volume; - int program_next_ramp; - unsigned long flags; - - if (!gus->gf1.volume_ramp) { - spin_lock_irqsave(&gus->reg_lock, flags); - snd_gf1_select_voice(gus, voice->number); - snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); - snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, voice->gf1_volume); - /* printk("gf1_volume = 0x%x\n", voice->gf1_volume); */ - spin_unlock_irqrestore(&gus->reg_lock, flags); - return; - } - program_next_ramp = 0; - rate = next = 0; - while (1) { - program_next_ramp = 0; - rate = next = 0; - switch (voice->venv_state) { - case VENV_BEFORE: - voice->venv_state = VENV_ATTACK; - voice->venv_value_next = 0; - spin_lock_irqsave(&gus->reg_lock, flags); - snd_gf1_select_voice(gus, voice->number); - snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); - snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME); - spin_unlock_irqrestore(&gus->reg_lock, flags); - break; - case VENV_ATTACK: - voice->venv_state = VENV_SUSTAIN; - program_next_ramp++; - next = 255; - rate = gus->gf1.volume_ramp; - break; - case VENV_SUSTAIN: - voice->venv_state = VENV_RELEASE; - spin_lock_irqsave(&gus->reg_lock, flags); - snd_gf1_select_voice(gus, voice->number); - snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); - snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, ((int)voice->gf1_volume * (int)voice->venv_value_next) / 255); - spin_unlock_irqrestore(&gus->reg_lock, flags); - return; - case VENV_RELEASE: - voice->venv_state = VENV_DONE; - program_next_ramp++; - next = 0; - rate = gus->gf1.volume_ramp; - break; - case VENV_DONE: - snd_gf1_stop_voice(gus, voice->number); - voice->flags &= ~SNDRV_GF1_VFLG_RUNNING; - return; - case VENV_VOLUME: - program_next_ramp++; - next = voice->venv_value_next; - rate = gus->gf1.volume_ramp; - voice->venv_state = voice->venv_state_prev; - break; - } - voice->venv_value_next = next; - if (!program_next_ramp) - continue; - spin_lock_irqsave(&gus->reg_lock, flags); - snd_gf1_select_voice(gus, voice->number); - snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); - old_volume = snd_gf1_read16(gus, SNDRV_GF1_VW_VOLUME) >> 8; - if (!rate) { - spin_unlock_irqrestore(&gus->reg_lock, flags); - continue; - } - next = (((int)voice->gf1_volume * (int)next) / 255) >> 8; - if (old_volume < SNDRV_GF1_MIN_OFFSET) - old_volume = SNDRV_GF1_MIN_OFFSET; - if (next < SNDRV_GF1_MIN_OFFSET) - next = SNDRV_GF1_MIN_OFFSET; - if (next > SNDRV_GF1_MAX_OFFSET) - next = SNDRV_GF1_MAX_OFFSET; - if (old_volume == next) { - spin_unlock_irqrestore(&gus->reg_lock, flags); - continue; - } - voice->volume_control &= ~0xc3; - voice->volume_control |= 0x20; - if (old_volume > next) { - snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, next); - snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, old_volume); - voice->volume_control |= 0x40; - } else { - snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, old_volume); - snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, next); - } - snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, rate); - snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, voice->volume_control); - if (!gus->gf1.enh_mode) { - snd_gf1_delay(gus); - snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, voice->volume_control); - } - spin_unlock_irqrestore(&gus->reg_lock, flags); - return; - } -} - -static void do_pan_envelope(struct snd_gus_card *gus, struct snd_gus_voice *voice) -{ - unsigned long flags; - unsigned char old_pan; - -#if 0 - snd_gf1_select_voice(gus, voice->number); - printk(" -%i- do_pan_envelope - flags = 0x%x (0x%x -> 0x%x)\n", - voice->number, - voice->flags, - voice->gf1_pan, - snd_gf1_i_read8(gus, SNDRV_GF1_VB_PAN) & 0x0f); -#endif - if (gus->gf1.enh_mode) { - voice->flags &= ~(SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN); - return; - } - if (!gus->gf1.smooth_pan) { - spin_lock_irqsave(&gus->reg_lock, flags); - snd_gf1_select_voice(gus, voice->number); - snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, voice->gf1_pan); - spin_unlock_irqrestore(&gus->reg_lock, flags); - return; - } - if (!(voice->flags & SNDRV_GF1_VFLG_PAN)) /* before */ - voice->flags |= SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN; - spin_lock_irqsave(&gus->reg_lock, flags); - snd_gf1_select_voice(gus, voice->number); - old_pan = snd_gf1_read8(gus, SNDRV_GF1_VB_PAN) & 0x0f; - if (old_pan > voice->gf1_pan ) - old_pan--; - if (old_pan < voice->gf1_pan) - old_pan++; - snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, old_pan); - spin_unlock_irqrestore(&gus->reg_lock, flags); - if (old_pan == voice->gf1_pan) /* the goal was reached */ - voice->flags &= ~(SNDRV_GF1_VFLG_EFFECT_TIMER1|SNDRV_GF1_VFLG_PAN); -#if 0 - snd_gf1_select_voice(gus, voice->number); - printk(" -%i- (1) do_pan_envelope - flags = 0x%x (0x%x -> 0x%x)\n", - voice->number, - voice->flags, - voice->gf1_pan, - snd_gf1_i_read8(gus, GF1_VB_PAN) & 0x0f); -#endif -} - -static void set_enhanced_pan(struct snd_gus_card *gus, struct snd_gus_voice *voice, unsigned short pan) -{ - unsigned long flags; - unsigned short vlo, vro; - - vlo = SNDRV_GF1_ATTEN((SNDRV_GF1_ATTEN_TABLE_SIZE-1) - pan); - vro = SNDRV_GF1_ATTEN(pan); - if (pan != SNDRV_GF1_ATTEN_TABLE_SIZE - 1 && pan != 0) { - vlo >>= 1; - vro >>= 1; - } - vlo <<= 4; - vro <<= 4; -#if 0 - printk("vlo = 0x%x (0x%x), vro = 0x%x (0x%x)\n", - vlo, snd_gf1_i_read16(gus, GF1_VW_OFFSET_LEFT), - vro, snd_gf1_i_read16(gus, GF1_VW_OFFSET_RIGHT)); -#endif - spin_lock_irqsave(&gus->reg_lock, flags); - snd_gf1_select_voice(gus, voice->number); - snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT_FINAL, vlo); - snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT_FINAL, vro); - spin_unlock_irqrestore(&gus->reg_lock, flags); - voice->vlo = vlo; - voice->vro = vro; -} - -/* - * - */ - -static void sample_start(struct snd_gus_card *gus, struct snd_gus_voice *voice, snd_seq_position_t position) -{ - unsigned long flags; - unsigned int begin, addr, addr_end, addr_start; - int w_16; - struct simple_instrument *simple; - struct snd_seq_kinstr *instr; - - instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1); - if (instr == NULL) - return; - voice->instr = instr->instr; /* copy ID to speedup aliases */ - simple = KINSTR_DATA(instr); - begin = simple->address.memory << 4; - w_16 = simple->format & SIMPLE_WAVE_16BIT ? 0x04 : 0; - addr_start = simple->loop_start; - if (simple->format & SIMPLE_WAVE_LOOP) { - addr_end = simple->loop_end; - } else { - addr_end = (simple->size << 4) - (w_16 ? 40 : 24); - } - if (simple->format & SIMPLE_WAVE_BACKWARD) { - addr = simple->loop_end; - if (position < simple->loop_end) - addr -= position; - } else { - addr = position; - } - voice->control = 0x00; - voice->mode = 0x20; /* enable offset registers */ - if (simple->format & SIMPLE_WAVE_16BIT) - voice->control |= 0x04; - if (simple->format & SIMPLE_WAVE_BACKWARD) - voice->control |= 0x40; - if (simple->format & SIMPLE_WAVE_LOOP) { - voice->control |= 0x08; - } else { - voice->control |= 0x20; - } - if (simple->format & SIMPLE_WAVE_BIDIR) - voice->control |= 0x10; - if (simple->format & SIMPLE_WAVE_ULAW) - voice->mode |= 0x40; - if (w_16) { - addr = ((addr << 1) & ~0x1f) | (addr & 0x0f); - addr_start = ((addr_start << 1) & ~0x1f) | (addr_start & 0x0f); - addr_end = ((addr_end << 1) & ~0x1f) | (addr_end & 0x0f); - } - addr += begin; - addr_start += begin; - addr_end += begin; - snd_gf1_stop_voice(gus, voice->number); - spin_lock_irqsave(&gus->reg_lock, flags); - snd_gf1_select_voice(gus, voice->number); - snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, voice->fc_register + voice->fc_lfo); - voice->venv_state = VENV_BEFORE; - voice->volume_control = 0x03; - snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, addr_start, w_16); - snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, addr_end, w_16); - snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, addr, w_16); - if (!gus->gf1.enh_mode) { - snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, voice->gf1_pan); - } else { - snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT, voice->vlo); - snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_LEFT_FINAL, voice->vlo); - snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT, voice->vro); - snd_gf1_write16(gus, SNDRV_GF1_VW_OFFSET_RIGHT_FINAL, voice->vro); - snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, voice->effect_accumulator); - snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME, voice->gf1_effect_volume); - snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME_FINAL, voice->gf1_effect_volume); - } - spin_unlock_irqrestore(&gus->reg_lock, flags); - do_volume_envelope(gus, voice); - spin_lock_irqsave(&gus->reg_lock, flags); - snd_gf1_select_voice(gus, voice->number); - if (gus->gf1.enh_mode) - snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, voice->mode); - snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice->control); - if (!gus->gf1.enh_mode) { - snd_gf1_delay(gus); - snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice->control ); - } - spin_unlock_irqrestore(&gus->reg_lock, flags); -#if 0 - snd_gf1_print_voice_registers(gus); -#endif - voice->flags |= SNDRV_GF1_VFLG_RUNNING; - snd_seq_instr_free_use(gus->gf1.ilist, instr); -} - -static void sample_stop(struct snd_gus_card *gus, struct snd_gus_voice *voice, int mode) -{ - unsigned char control; - unsigned long flags; - - if (!(voice->flags & SNDRV_GF1_VFLG_RUNNING)) - return; - switch (mode) { - default: - if (gus->gf1.volume_ramp > 0) { - if (voice->venv_state < VENV_RELEASE) { - voice->venv_state = VENV_RELEASE; - do_volume_envelope(gus, voice); - } - } - if (mode != SAMPLE_STOP_VENVELOPE) { - snd_gf1_stop_voice(gus, voice->number); - spin_lock_irqsave(&gus->reg_lock, flags); - snd_gf1_select_voice(gus, voice->number); - snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME); - spin_unlock_irqrestore(&gus->reg_lock, flags); - voice->flags &= ~SNDRV_GF1_VFLG_RUNNING; - } - break; - case SAMPLE_STOP_LOOP: /* disable loop only */ - spin_lock_irqsave(&gus->reg_lock, flags); - snd_gf1_select_voice(gus, voice->number); - control = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); - control &= ~(0x83 | 0x04); - control |= 0x20; - snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, control); - spin_unlock_irqrestore(&gus->reg_lock, flags); - break; - } -} - -static void sample_freq(struct snd_gus_card *gus, struct snd_gus_voice *voice, snd_seq_frequency_t freq) -{ - unsigned long flags; - - spin_lock_irqsave(&gus->reg_lock, flags); - voice->fc_register = snd_gf1_translate_freq(gus, freq); - snd_gf1_select_voice(gus, voice->number); - snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, voice->fc_register + voice->fc_lfo); - spin_unlock_irqrestore(&gus->reg_lock, flags); -} - -static void sample_volume(struct snd_gus_card *gus, struct snd_gus_voice *voice, struct snd_seq_ev_volume *volume) -{ - if (volume->volume >= 0) { - volume->volume &= 0x3fff; - voice->gf1_volume = snd_gf1_lvol_to_gvol_raw(volume->volume << 2) << 4; - voice->venv_state_prev = VENV_SUSTAIN; - voice->venv_state = VENV_VOLUME; - do_volume_envelope(gus, voice); - } - if (volume->lr >= 0) { - volume->lr &= 0x3fff; - if (!gus->gf1.enh_mode) { - voice->gf1_pan = (volume->lr >> 10) & 15; - if (!gus->gf1.full_range_pan) { - if (voice->gf1_pan == 0) - voice->gf1_pan++; - if (voice->gf1_pan == 15) - voice->gf1_pan--; - } - voice->flags &= ~SNDRV_GF1_VFLG_PAN; /* before */ - do_pan_envelope(gus, voice); - } else { - set_enhanced_pan(gus, voice, volume->lr >> 7); - } - } -} - -static void sample_loop(struct snd_gus_card *gus, struct snd_gus_voice *voice, struct snd_seq_ev_loop *loop) -{ - unsigned long flags; - int w_16 = voice->control & 0x04; - unsigned int begin, addr_start, addr_end; - struct simple_instrument *simple; - struct snd_seq_kinstr *instr; - -#if 0 - printk("voice_loop: start = 0x%x, end = 0x%x\n", loop->start, loop->end); -#endif - instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1); - if (instr == NULL) - return; - voice->instr = instr->instr; /* copy ID to speedup aliases */ - simple = KINSTR_DATA(instr); - begin = simple->address.memory; - addr_start = loop->start; - addr_end = loop->end; - addr_start = (((addr_start << 1) & ~0x1f) | (addr_start & 0x0f)) + begin; - addr_end = (((addr_end << 1) & ~0x1f) | (addr_end & 0x0f)) + begin; - spin_lock_irqsave(&gus->reg_lock, flags); - snd_gf1_select_voice(gus, voice->number); - snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, addr_start, w_16); - snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, addr_end, w_16); - spin_unlock_irqrestore(&gus->reg_lock, flags); - snd_seq_instr_free_use(gus->gf1.ilist, instr); -} - -static void sample_pos(struct snd_gus_card *gus, struct snd_gus_voice *voice, snd_seq_position_t position) -{ - unsigned long flags; - int w_16 = voice->control & 0x04; - unsigned int begin, addr; - struct simple_instrument *simple; - struct snd_seq_kinstr *instr; - -#if 0 - printk("voice_loop: start = 0x%x, end = 0x%x\n", loop->start, loop->end); -#endif - instr = snd_seq_instr_find(gus->gf1.ilist, &voice->instr, 0, 1); - if (instr == NULL) - return; - voice->instr = instr->instr; /* copy ID to speedup aliases */ - simple = KINSTR_DATA(instr); - begin = simple->address.memory; - addr = (((position << 1) & ~0x1f) | (position & 0x0f)) + begin; - spin_lock_irqsave(&gus->reg_lock, flags); - snd_gf1_select_voice(gus, voice->number); - snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, addr, w_16); - spin_unlock_irqrestore(&gus->reg_lock, flags); - snd_seq_instr_free_use(gus->gf1.ilist, instr); -} - -#if 0 - -static unsigned char get_effects_mask( ultra_card_t *card, int value ) -{ - if ( value > 7 ) return 0; - if ( card -> gf1.effects && card -> gf1.effects -> chip_type == ULTRA_EFFECT_CHIP_INTERWAVE ) - return card -> gf1.effects -> chip.interwave.voice_output[ value ]; - return 0; -} - -#endif - -static void sample_private1(struct snd_gus_card *card, struct snd_gus_voice *voice, unsigned char *data) -{ -#if 0 - unsigned long flags; - unsigned char uc; - - switch ( *data ) { - case ULTRA_PRIV1_IW_EFFECT: - uc = get_effects_mask( card, ultra_get_byte( data, 4 ) ); - uc |= get_effects_mask( card, ultra_get_byte( data, 4 ) >> 4 ); - uc |= get_effects_mask( card, ultra_get_byte( data, 5 ) ); - uc |= get_effects_mask( card, ultra_get_byte( data, 5 ) >> 4 ); - voice -> data.simple.effect_accumulator = uc; - voice -> data.simple.effect_volume = ultra_translate_voice_volume( card, ultra_get_word( data, 2 ) ) << 4; - if ( !card -> gf1.enh_mode ) return; - if ( voice -> flags & VFLG_WAIT_FOR_START ) return; - if ( voice -> flags & VFLG_RUNNING ) - { - CLI( &flags ); - gf1_select_voice( card, voice -> number ); - ultra_write8( card, GF1_VB_ACCUMULATOR, voice -> data.simple.effect_accumulator ); - ultra_write16( card, GF1_VW_EFFECT_VOLUME_FINAL, voice -> data.simple.effect_volume ); - STI( &flags ); - } - break; - case ULTRA_PRIV1_IW_LFO: - ultra_lfo_command( card, voice -> number, data ); - } -#endif -} - -#if 0 - -/* - * - */ - -static void note_stop( ultra_card_t *card, ultra_voice_t *voice, int wait ) -{ -} - -static void note_wait( ultra_card_t *card, ultra_voice_t *voice ) -{ -} - -static void note_off( ultra_card_t *card, ultra_voice_t *voice ) -{ -} - -static void note_volume( ultra_card_t *card, ultra_voice_t *voice ) -{ -} - -static void note_pitchbend( ultra_card_t *card, ultra_voice_t *voice ) -{ -} - -static void note_vibrato( ultra_card_t *card, ultra_voice_t *voice ) -{ -} - -static void note_tremolo( ultra_card_t *card, ultra_voice_t *voice ) -{ -} - -/* - * - */ - -static void chn_trigger_down( ultra_card_t *card, ultra_channel_t *channel, ultra_instrument_t *instrument, unsigned char note, unsigned char velocity, unsigned char priority ) -{ -} - -static void chn_trigger_up( ultra_card_t *card, ultra_note_t *note ) -{ -} - -static void chn_control( ultra_card_t *card, ultra_channel_t *channel, unsigned short p1, unsigned short p2 ) -{ -} - -/* - * - */ - -#endif - -void snd_gf1_simple_init(struct snd_gus_voice *voice) -{ - voice->handler_wave = interrupt_wave; - voice->handler_volume = interrupt_volume; - voice->handler_effect = interrupt_effect; - voice->volume_change = NULL; - voice->sample_ops = &sample_ops; -} diff --git a/sound/isa/gus/gus_synth.c b/sound/isa/gus/gus_synth.c deleted file mode 100644 index 2c20517..0000000 --- a/sound/isa/gus/gus_synth.c +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Routines for Gravis UltraSound soundcards - Synthesizer - * Copyright (c) by Jaroslav Kysela <perex@perex.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 <linux/init.h> -#include <linux/time.h> -#include <sound/core.h> -#include <sound/gus.h> -#include <sound/seq_device.h> - -MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); -MODULE_DESCRIPTION("Routines for Gravis UltraSound soundcards - Synthesizer"); -MODULE_LICENSE("GPL"); - -/* - * - */ - -static void snd_gus_synth_free_voices(struct snd_gus_card * gus, int client, int port) -{ - int idx; - struct snd_gus_voice * voice; - - for (idx = 0; idx < 32; idx++) { - voice = &gus->gf1.voices[idx]; - if (voice->use && voice->client == client && voice->port == port) - snd_gf1_free_voice(gus, voice); - } -} - -static int snd_gus_synth_use(void *private_data, struct snd_seq_port_subscribe *info) -{ - struct snd_gus_port * port = private_data; - struct snd_gus_card * gus = port->gus; - struct snd_gus_voice * voice; - unsigned int idx; - - if (info->voices > 32) - return -EINVAL; - mutex_lock(&gus->register_mutex); - if (!snd_gus_use_inc(gus)) { - mutex_unlock(&gus->register_mutex); - return -EFAULT; - } - for (idx = 0; idx < info->voices; idx++) { - voice = snd_gf1_alloc_voice(gus, SNDRV_GF1_VOICE_TYPE_SYNTH, info->sender.client, info->sender.port); - if (voice == NULL) { - snd_gus_synth_free_voices(gus, info->sender.client, info->sender.port); - snd_gus_use_dec(gus); - mutex_unlock(&gus->register_mutex); - return -EBUSY; - } - voice->index = idx; - } - mutex_unlock(&gus->register_mutex); - return 0; -} - -static int snd_gus_synth_unuse(void *private_data, struct snd_seq_port_subscribe *info) -{ - struct snd_gus_port * port = private_data; - struct snd_gus_card * gus = port->gus; - - mutex_lock(&gus->register_mutex); - snd_gus_synth_free_voices(gus, info->sender.client, info->sender.port); - snd_gus_use_dec(gus); - mutex_unlock(&gus->register_mutex); - return 0; -} - -/* - * - */ - -static void snd_gus_synth_free_private_instruments(struct snd_gus_port *p, int client) -{ - struct snd_seq_instr_header ifree; - - memset(&ifree, 0, sizeof(ifree)); - ifree.cmd = SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE; - snd_seq_instr_list_free_cond(p->gus->gf1.ilist, &ifree, client, 0); -} - -static int snd_gus_synth_event_input(struct snd_seq_event *ev, int direct, - void *private_data, int atomic, int hop) -{ - struct snd_gus_port * p = private_data; - - snd_assert(p != NULL, return -EINVAL); - if (ev->type >= SNDRV_SEQ_EVENT_SAMPLE && - ev->type <= SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1) { - snd_gus_sample_event(ev, p); - return 0; - } - if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM && - ev->source.port == SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE) { - if (ev->type == SNDRV_SEQ_EVENT_CLIENT_EXIT) { - snd_gus_synth_free_private_instruments(p, ev->data.addr.client); - return 0; - } - } - if (direct) { - if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN) { - snd_seq_instr_event(&p->gus->gf1.iwffff_ops.kops, - p->gus->gf1.ilist, - ev, - p->gus->gf1.seq_client, - atomic, hop); - return 0; - } - } - return 0; -} - -static void snd_gus_synth_instr_notify(void *private_data, - struct snd_seq_kinstr *instr, - int what) -{ - unsigned int idx; - struct snd_gus_card *gus = private_data; - struct snd_gus_voice *pvoice; - unsigned long flags; - - spin_lock_irqsave(&gus->event_lock, flags); - for (idx = 0; idx < 32; idx++) { - pvoice = &gus->gf1.voices[idx]; - if (pvoice->use && !memcmp(&pvoice->instr, &instr->instr, sizeof(pvoice->instr))) { - if (pvoice->sample_ops && pvoice->sample_ops->sample_stop) { - pvoice->sample_ops->sample_stop(gus, pvoice, SAMPLE_STOP_IMMEDIATELY); - } else { - snd_gf1_stop_voice(gus, pvoice->number); - pvoice->flags &= ~SNDRV_GF1_VFLG_RUNNING; - } - } - } - spin_unlock_irqrestore(&gus->event_lock, flags); -} - -/* - * - */ - -static void snd_gus_synth_free_port(void *private_data) -{ - struct snd_gus_port * p = private_data; - - if (p) - snd_midi_channel_free_set(p->chset); -} - -static int snd_gus_synth_create_port(struct snd_gus_card * gus, int idx) -{ - struct snd_gus_port * p; - struct snd_seq_port_callback callbacks; - char name[32]; - int result; - - p = &gus->gf1.seq_ports[idx]; - p->chset = snd_midi_channel_alloc_set(16); - if (p->chset == NULL) - return -ENOMEM; - p->chset->private_data = p; - p->gus = gus; - p->client = gus->gf1.seq_client; - - memset(&callbacks, 0, sizeof(callbacks)); - callbacks.owner = THIS_MODULE; - callbacks.use = snd_gus_synth_use; - callbacks.unuse = snd_gus_synth_unuse; - callbacks.event_input = snd_gus_synth_event_input; - callbacks.private_free = snd_gus_synth_free_port; - callbacks.private_data = p; - - sprintf(name, "%s port %i", gus->interwave ? "AMD InterWave" : "GF1", idx); - p->chset->port = snd_seq_event_port_attach(gus->gf1.seq_client, - &callbacks, - SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE, - SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE | - SNDRV_SEQ_PORT_TYPE_SYNTH | - SNDRV_SEQ_PORT_TYPE_HARDWARE | - SNDRV_SEQ_PORT_TYPE_SYNTHESIZER, - 16, 0, - name); - if (p->chset->port < 0) { - result = p->chset->port; - snd_gus_synth_free_port(p); - return result; - } - p->port = p->chset->port; - return 0; -} - -/* - * - */ - -static int snd_gus_synth_new_device(struct snd_seq_device *dev) -{ - struct snd_gus_card *gus; - int client, i; - struct snd_seq_port_subscribe sub; - struct snd_iwffff_ops *iwops; - struct snd_gf1_ops *gf1ops; - struct snd_simple_ops *simpleops; - - gus = *(struct snd_gus_card **)SNDRV_SEQ_DEVICE_ARGPTR(dev); - if (gus == NULL) - return -EINVAL; - - mutex_init(&gus->register_mutex); - gus->gf1.seq_client = -1; - - /* allocate new client */ - client = gus->gf1.seq_client = - snd_seq_create_kernel_client(gus->card, 1, gus->interwave ? - "AMD InterWave" : "GF1"); - if (client < 0) - return client; - - for (i = 0; i < 4; i++) - snd_gus_synth_create_port(gus, i); - - gus->gf1.ilist = snd_seq_instr_list_new(); - if (gus->gf1.ilist == NULL) { - snd_seq_delete_kernel_client(client); - gus->gf1.seq_client = -1; - return -ENOMEM; - } - gus->gf1.ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT; - - simpleops = &gus->gf1.simple_ops; - snd_seq_simple_init(simpleops, gus, NULL); - simpleops->put_sample = snd_gus_simple_put_sample; - simpleops->get_sample = snd_gus_simple_get_sample; - simpleops->remove_sample = snd_gus_simple_remove_sample; - simpleops->notify = snd_gus_synth_instr_notify; - - gf1ops = &gus->gf1.gf1_ops; - snd_seq_gf1_init(gf1ops, gus, &simpleops->kops); - gf1ops->put_sample = snd_gus_gf1_put_sample; - gf1ops->get_sample = snd_gus_gf1_get_sample; - gf1ops->remove_sample = snd_gus_gf1_remove_sample; - gf1ops->notify = snd_gus_synth_instr_notify; - - iwops = &gus->gf1.iwffff_ops; - snd_seq_iwffff_init(iwops, gus, &gf1ops->kops); - iwops->put_sample = snd_gus_iwffff_put_sample; - iwops->get_sample = snd_gus_iwffff_get_sample; - iwops->remove_sample = snd_gus_iwffff_remove_sample; - iwops->notify = snd_gus_synth_instr_notify; - - memset(&sub, 0, sizeof(sub)); - sub.sender.client = SNDRV_SEQ_CLIENT_SYSTEM; - sub.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; - sub.dest.client = client; - sub.dest.port = 0; - snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &sub); - - return 0; -} - -static int snd_gus_synth_delete_device(struct snd_seq_device *dev) -{ - struct snd_gus_card *gus; - - gus = *(struct snd_gus_card **)SNDRV_SEQ_DEVICE_ARGPTR(dev); - if (gus == NULL) - return -EINVAL; - - if (gus->gf1.seq_client >= 0) { - snd_seq_delete_kernel_client(gus->gf1.seq_client); - gus->gf1.seq_client = -1; - } - if (gus->gf1.ilist) - snd_seq_instr_list_free(&gus->gf1.ilist); - return 0; -} - -static int __init alsa_gus_synth_init(void) -{ - static struct snd_seq_dev_ops ops = { - snd_gus_synth_new_device, - snd_gus_synth_delete_device - }; - - return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_GUS, &ops, - sizeof(struct snd_gus_card *)); -} - -static void __exit alsa_gus_synth_exit(void) -{ - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_GUS); -} - -module_init(alsa_gus_synth_init) -module_exit(alsa_gus_synth_exit) diff --git a/sound/isa/gus/gus_timer.c b/sound/isa/gus/gus_timer.c index 99eac57..c537271 100644 --- a/sound/isa/gus/gus_timer.c +++ b/sound/isa/gus/gus_timer.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <sound/core.h> #include <sound/gus.h> diff --git a/sound/isa/gus/gus_uart.c b/sound/isa/gus/gus_uart.c index e6fd9b0..f0af3f7 100644 --- a/sound/isa/gus/gus_uart.c +++ b/sound/isa/gus/gus_uart.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/time.h> diff --git a/sound/isa/gus/gus_volume.c b/sound/isa/gus/gus_volume.c index 71a6774..c3c028a 100644 --- a/sound/isa/gus/gus_volume.c +++ b/sound/isa/gus/gus_volume.c @@ -18,7 +18,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <sound/core.h> #include <sound/gus.h> diff --git a/sound/isa/gus/gusclassic.c b/sound/isa/gus/gusclassic.c index 29e422b..8f914b3 100644 --- a/sound/isa/gus/gusclassic.c +++ b/sound/isa/gus/gusclassic.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/err.h> #include <linux/isa.h> diff --git a/sound/isa/gus/gusextreme.c b/sound/isa/gus/gusextreme.c index fc59536..da13185 100644 --- a/sound/isa/gus/gusextreme.c +++ b/sound/isa/gus/gusextreme.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/err.h> #include <linux/isa.h> diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c index 4922f5d..f87c623 100644 --- a/sound/isa/gus/gusmax.c +++ b/sound/isa/gus/gusmax.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/err.h> #include <linux/isa.h> diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c index 2091c50..ca0d7ac 100644 --- a/sound/isa/gus/interwave.c +++ b/sound/isa/gus/interwave.c @@ -22,7 +22,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/err.h> #include <linux/isa.h> @@ -560,50 +559,27 @@ static int __devinit snd_interwave_pnp(int dev, struct snd_interwave *iwcard, const struct pnp_card_device_id *id) { struct pnp_dev *pdev; - struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); int err; - if (!cfg) - return -ENOMEM; iwcard->dev = pnp_request_card_device(card, id->devs[0].id, NULL); - if (iwcard->dev == NULL) { - kfree(cfg); + if (iwcard->dev == NULL) return -EBUSY; - } + #ifdef SNDRV_STB iwcard->devtc = pnp_request_card_device(card, id->devs[1].id, NULL); - if (iwcard->devtc == NULL) { - kfree(cfg); + if (iwcard->devtc == NULL) return -EBUSY; - } #endif /* Synth & Codec initialization */ pdev = iwcard->dev; - pnp_init_resource_table(cfg); - if (port[dev] != SNDRV_AUTO_PORT) { - pnp_resource_change(&cfg->port_resource[0], port[dev], 16); - pnp_resource_change(&cfg->port_resource[1], port[dev] + 0x100, 12); - pnp_resource_change(&cfg->port_resource[2], port[dev] + 0x10c, 4); - } - if (dma1[dev] != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1); - if (dma2[dev] != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1); - if (dma2[dev] < 0) - pnp_resource_change(&cfg->dma_resource[1], 4, 1); - if (irq[dev] != SNDRV_AUTO_IRQ) - pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); - if (pnp_manual_config_dev(pdev, cfg, 0) < 0) - snd_printk(KERN_ERR "InterWave - Synth - the requested resources are invalid, using auto config\n"); + err = pnp_activate_dev(pdev); if (err < 0) { - kfree(cfg); snd_printk(KERN_ERR "InterWave PnP configure failure (out of resources?)\n"); return err; } if (pnp_port_start(pdev, 0) + 0x100 != pnp_port_start(pdev, 1) || pnp_port_start(pdev, 0) + 0x10c != pnp_port_start(pdev, 2)) { - kfree(cfg); snd_printk(KERN_ERR "PnP configure failure (wrong ports)\n"); return -ENOENT; } @@ -620,21 +596,15 @@ static int __devinit snd_interwave_pnp(int dev, struct snd_interwave *iwcard, #ifdef SNDRV_STB /* Tone Control initialization */ pdev = iwcard->devtc; - pnp_init_resource_table(cfg); - if (port_tc[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[0], port_tc[dev], 1); - if (pnp_manual_config_dev(pdev, cfg, 0) < 0) - snd_printk(KERN_ERR "InterWave - ToneControl - the requested resources are invalid, using auto config\n"); + err = pnp_activate_dev(pdev); if (err < 0) { - kfree(cfg); snd_printk(KERN_ERR "InterWave ToneControl PnP configure failure (out of resources?)\n"); return err; } port_tc[dev] = pnp_port_start(pdev, 0); snd_printdd("isapnp IW: tone control port=0x%lx\n", port_tc[dev]); #endif - kfree(cfg); return 0; } #endif /* CONFIG_PNP */ diff --git a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c index 59af9ab..854a9f7 100644 --- a/sound/isa/opl3sa2.c +++ b/sound/isa/opl3sa2.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/err.h> #include <linux/isa.h> @@ -610,39 +609,8 @@ static int snd_opl3sa2_resume(struct snd_card *card) static int __devinit snd_opl3sa2_pnp(int dev, struct snd_opl3sa2 *chip, struct pnp_dev *pdev) { - struct pnp_resource_table * cfg; - int err; - - cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); - if (!cfg) { - snd_printk(KERN_ERR PFX "cannot allocate pnp cfg\n"); - return -ENOMEM; - } - /* PnP initialization */ - pnp_init_resource_table(cfg); - if (sb_port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[0], sb_port[dev], 16); - if (wss_port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[1], wss_port[dev], 8); - if (fm_port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[2], fm_port[dev], 4); - if (midi_port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[3], midi_port[dev], 2); - if (port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[4], port[dev], 2); - if (dma1[dev] != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1); - if (dma2[dev] != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1); - if (irq[dev] != SNDRV_AUTO_IRQ) - pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); - err = pnp_manual_config_dev(pdev, cfg, 0); - if (err < 0) - snd_printk(KERN_WARNING "PnP manual resources are invalid, using auto config\n"); - err = pnp_activate_dev(pdev); - if (err < 0) { - kfree(cfg); - snd_printk(KERN_ERR "PnP configure failure (out of resources?) err = %d\n", err); + if (pnp_activate_dev(pdev) < 0) { + snd_printk(KERN_ERR "PnP configure failure (out of resources?)\n"); return -EBUSY; } sb_port[dev] = pnp_port_start(pdev, 0); @@ -657,7 +625,6 @@ static int __devinit snd_opl3sa2_pnp(int dev, struct snd_opl3sa2 *chip, pnp_device_is_pnpbios(pdev) ? "BIOS" : "ISA", sb_port[dev], wss_port[dev], fm_port[dev], midi_port[dev]); snd_printdd("%sPnP OPL3-SA: control port=0x%lx, dma1=%i, dma2=%i, irq=%i\n", pnp_device_is_pnpbios(pdev) ? "BIOS" : "ISA", port[dev], dma1[dev], dma2[dev], irq[dev]); - kfree(cfg); return 0; } #endif /* CONFIG_PNP */ diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c index d295936..2a1e2f5 100644 --- a/sound/isa/opti9xx/miro.c +++ b/sound/isa/opti9xx/miro.c @@ -22,7 +22,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/err.h> #include <linux/isa.h> @@ -483,6 +482,10 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol, /* equalizer elements */ + if (left < -0x7f || left > 0x7f || + right < -0x7f || right > 0x7f) + return -EINVAL; + if (left_old > 0x80) left_old = 0x80 - left_old; if (right_old > 0x80) @@ -520,6 +523,10 @@ static int snd_miro_put_double(struct snd_kcontrol *kcontrol, /* non-equalizer elements */ + if (left < 0 || left > 0x20 || + right < 0 || right > 0x20) + return -EINVAL; + left_old = 0x20 - left_old; right_old = 0x20 - right_old; @@ -662,7 +669,7 @@ static int __devinit snd_set_aci_init_values(struct snd_miro *miro) return 0; } -static int snd_miro_mixer(struct snd_miro *miro) +static int __devinit snd_miro_mixer(struct snd_miro *miro) { struct snd_card *card; unsigned int idx; diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c index ee1a824..fe1afc1 100644 --- a/sound/isa/opti9xx/opti92x-ad1848.c +++ b/sound/isa/opti9xx/opti92x-ad1848.c @@ -23,7 +23,6 @@ */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/err.h> #include <linux/isa.h> @@ -1595,7 +1594,7 @@ OPTi93X_DOUBLE("Capture Volume", 0, OPTi93X_MIXOUT_LEFT, OPTi93X_MIXOUT_RIGHT, 0 } }; -static int snd_opti93x_mixer(struct snd_opti93x *chip) +static int __devinit snd_opti93x_mixer(struct snd_opti93x *chip) { struct snd_card *card; struct snd_kcontrol_new knew; @@ -1690,53 +1689,19 @@ static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip, const struct pnp_card_device_id *pid) { struct pnp_dev *pdev; - struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL); int err; - if (!cfg) - return -ENOMEM; chip->dev = pnp_request_card_device(card, pid->devs[0].id, NULL); - if (chip->dev == NULL) { - kfree(cfg); + if (chip->dev == NULL) return -EBUSY; - } + chip->devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL); pdev = chip->dev; - pnp_init_resource_table(cfg); -#ifdef OPTi93X - if (port != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[0], port + 4, 4); -#else - if (pid->driver_data != 0x0924 && port != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[1], port, 4); -#endif /* OPTi93X */ - if (irq != SNDRV_AUTO_IRQ) - pnp_resource_change(&cfg->irq_resource[0], irq, 1); - if (dma1 != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[0], dma1, 1); -#if defined(CS4231) || defined(OPTi93X) - if (dma2 != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[1], dma2, 1); -#else -#ifdef snd_opti9xx_fixup_dma2 - snd_opti9xx_fixup_dma2(pdev); -#endif -#endif /* CS4231 || OPTi93X */ -#ifdef OPTi93X - if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[1], fm_port, 4); -#else - if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[2], fm_port, 4); -#endif - if (pnp_manual_config_dev(pdev, cfg, 0) < 0) - snd_printk(KERN_ERR "AUDIO the requested resources are invalid, using auto config\n"); err = pnp_activate_dev(pdev); if (err < 0) { snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err); - kfree(cfg); return err; } @@ -1756,15 +1721,6 @@ static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip, pdev = chip->devmpu; if (pdev && mpu_port > 0) { - pnp_init_resource_table(cfg); - - if (mpu_port != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[0], mpu_port, 2); - if (mpu_irq != SNDRV_AUTO_IRQ) - pnp_resource_change(&cfg->irq_resource[0], mpu_irq, 1); - - if (pnp_manual_config_dev(pdev, cfg, 0) < 0) - snd_printk(KERN_ERR "AUDIO the requested resources are invalid, using auto config\n"); err = pnp_activate_dev(pdev); if (err < 0) { snd_printk(KERN_ERR "AUDIO pnp configure failure\n"); @@ -1775,7 +1731,6 @@ static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip, mpu_irq = pnp_irq(pdev, 0); } } - kfree(cfg); return pid->driver_data; } #endif /* CONFIG_PNP */ diff --git a/sound/isa/sb/emu8000.c b/sound/isa/sb/emu8000.c index 4eea84c..b35be7d 100644 --- a/sound/isa/sb/emu8000.c +++ b/sound/isa/sb/emu8000.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/wait.h> #include <linux/sched.h> #include <linux/slab.h> diff --git a/sound/isa/sb/emu8000_local.h b/sound/isa/sb/emu8000_local.h index 2ac77f1..7e87c34 100644 --- a/sound/isa/sb/emu8000_local.h +++ b/sound/isa/sb/emu8000_local.h @@ -21,7 +21,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/wait.h> #include <linux/sched.h> #include <linux/slab.h> diff --git a/sound/isa/sb/es968.c b/sound/isa/sb/es968.c index d4b2187..c8c8e21 100644 --- a/sound/isa/sb/es968.c +++ b/sound/isa/sb/es968.c @@ -20,7 +20,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/time.h> #include <linux/pnp.h> @@ -49,12 +48,6 @@ module_param_array(id, charp, NULL, 0444); MODULE_PARM_DESC(id, "ID string for es968 based soundcard."); module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable es968 based soundcard."); -module_param_array(port, long, NULL, 0444); -MODULE_PARM_DESC(port, "Port # for es968 driver."); -module_param_array(irq, int, NULL, 0444); -MODULE_PARM_DESC(irq, "IRQ # for es968 driver."); -module_param_array(dma8, int, NULL, 0444); -MODULE_PARM_DESC(dma8, "8-bit DMA # for es968 driver."); struct snd_card_es968 { struct pnp_dev *dev; @@ -86,40 +79,23 @@ static int __devinit snd_card_es968_pnp(int dev, struct snd_card_es968 *acard, const struct pnp_card_device_id *id) { struct pnp_dev *pdev; - struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL); int err; - if (!cfg) - return -ENOMEM; + acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL); - if (acard->dev == NULL) { - kfree(cfg); + if (acard->dev == NULL) return -ENODEV; - } pdev = acard->dev; - pnp_init_resource_table(cfg); - - /* override resources */ - if (port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[0], port[dev], 16); - if (dma8[dev] != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[0], dma8[dev], 1); - if (irq[dev] != SNDRV_AUTO_IRQ) - pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); - if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0) - snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n"); err = pnp_activate_dev(pdev); if (err < 0) { snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n"); - kfree(cfg); return err; } port[dev] = pnp_port_start(pdev, 0); dma8[dev] = pnp_dma(pdev, 1); irq[dev] = pnp_irq(pdev, 0); - kfree(cfg); return 0; } diff --git a/sound/isa/sb/sb16.c b/sound/isa/sb/sb16.c index e7f9edd..2c201f7 100644 --- a/sound/isa/sb/sb16.c +++ b/sound/isa/sb/sb16.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <asm/dma.h> #include <linux/init.h> #include <linux/slab.h> @@ -257,44 +256,21 @@ static int __devinit snd_card_sb16_pnp(int dev, struct snd_card_sb16 *acard, const struct pnp_card_device_id *id) { struct pnp_dev *pdev; - struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); int err; - if (!cfg) - return -ENOMEM; acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL); - if (acard->dev == NULL) { - kfree(cfg); + if (acard->dev == NULL) return -ENODEV; - } + #ifdef SNDRV_SBAWE_EMU8000 acard->devwt = pnp_request_card_device(card, id->devs[1].id, acard->dev); #endif /* Audio initialization */ pdev = acard->dev; - pnp_init_resource_table(cfg); - - /* override resources */ - - if (port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[0], port[dev], 16); - if (mpu_port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[1], mpu_port[dev], 2); - if (fm_port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[2], fm_port[dev], 4); - if (dma8[dev] != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[0], dma8[dev], 1); - if (dma16[dev] != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[1], dma16[dev], 1); - if (irq[dev] != SNDRV_AUTO_IRQ) - pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); - if (pnp_manual_config_dev(pdev, cfg, 0) < 0) - snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n"); err = pnp_activate_dev(pdev); if (err < 0) { snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n"); - kfree(cfg); return err; } port[dev] = pnp_port_start(pdev, 0); @@ -311,17 +287,6 @@ static int __devinit snd_card_sb16_pnp(int dev, struct snd_card_sb16 *acard, /* WaveTable initialization */ pdev = acard->devwt; if (pdev != NULL) { - pnp_init_resource_table(cfg); - - /* override resources */ - - if (awe_port[dev] != SNDRV_AUTO_PORT) { - pnp_resource_change(&cfg->port_resource[0], awe_port[dev], 4); - pnp_resource_change(&cfg->port_resource[1], awe_port[dev] + 0x400, 4); - pnp_resource_change(&cfg->port_resource[2], awe_port[dev] + 0x800, 4); - } - if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0) - snd_printk(KERN_ERR PFX "WaveTable the requested resources are invalid, using auto config\n"); err = pnp_activate_dev(pdev); if (err < 0) { goto __wt_error; @@ -339,7 +304,6 @@ __wt_error: awe_port[dev] = -1; } #endif - kfree(cfg); return 0; } diff --git a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c index 3682059..bed29ca 100644 --- a/sound/isa/sb/sb16_csp.c +++ b/sound/isa/sb/sb16_csp.c @@ -23,7 +23,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/slab.h> @@ -118,7 +117,8 @@ static void info_read(struct snd_info_entry *entry, struct snd_info_buffer *buff int snd_sb_csp_new(struct snd_sb *chip, int device, struct snd_hwdep ** rhwdep) { struct snd_sb_csp *p; - int version, err; + int uninitialized_var(version); + int err; struct snd_hwdep *hw; if (rhwdep) diff --git a/sound/isa/sb/sb16_main.c b/sound/isa/sb/sb16_main.c index c06754f..f7e8192 100644 --- a/sound/isa/sb/sb16_main.c +++ b/sound/isa/sb/sb16_main.c @@ -33,7 +33,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <asm/dma.h> #include <linux/init.h> diff --git a/sound/isa/sb/sb8.c b/sound/isa/sb/sb8.c index f933aef..336a342 100644 --- a/sound/isa/sb/sb8.c +++ b/sound/isa/sb/sb8.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/err.h> #include <linux/isa.h> diff --git a/sound/isa/sb/sb8_main.c b/sound/isa/sb/sb8_main.c index bee894b..6304c3a 100644 --- a/sound/isa/sb/sb8_main.c +++ b/sound/isa/sb/sb8_main.c @@ -30,7 +30,6 @@ * Cleaned up and rewrote lowlevel routines. */ -#include <sound/driver.h> #include <asm/io.h> #include <asm/dma.h> #include <linux/init.h> diff --git a/sound/isa/sb/sb8_midi.c b/sound/isa/sb/sb8_midi.c index e56e563..988a8b7 100644 --- a/sound/isa/sb/sb8_midi.c +++ b/sound/isa/sb/sb8_midi.c @@ -26,7 +26,6 @@ * Added full duplex UART mode for DSP version 2.0 and later. */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/time.h> #include <sound/core.h> diff --git a/sound/isa/sb/sb_common.c b/sound/isa/sb/sb_common.c index 176193c..d63c1af 100644 --- a/sound/isa/sb/sb_common.c +++ b/sound/isa/sb/sb_common.c @@ -20,7 +20,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/isa/sb/sb_mixer.c b/sound/isa/sb/sb_mixer.c index 03241cd..91d1422 100644 --- a/sound/isa/sb/sb_mixer.c +++ b/sound/isa/sb/sb_mixer.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/time.h> diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c index 94daf83..da3d152 100644 --- a/sound/isa/sc6000.c +++ b/sound/isa/sc6000.c @@ -23,7 +23,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/module.h> #include <linux/delay.h> #include <linux/isa.h> @@ -390,7 +389,7 @@ static int __devinit sc6000_init_board(char __iomem *vport, int irq, int dma, err = sc6000_init_mss(vport, config, vmss_port, mss_config); if (err < 0) { - snd_printk(KERN_ERR "Can not initialize" + snd_printk(KERN_ERR "Can not initialize " "Microsoft Sound System mode.\n"); return -ENODEV; } diff --git a/sound/isa/sgalaxy.c b/sound/isa/sgalaxy.c index 922519d..a07274e 100644 --- a/sound/isa/sgalaxy.c +++ b/sound/isa/sgalaxy.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/err.h> #include <linux/isa.h> diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c index 1cb921d..06ad786 100644 --- a/sound/isa/sscape.c +++ b/sound/isa/sscape.c @@ -21,7 +21,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/err.h> #include <linux/isa.h> diff --git a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c index 83c2fc4..3a6c6fe 100644 --- a/sound/isa/wavefront/wavefront.c +++ b/sound/isa/wavefront/wavefront.c @@ -19,7 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/slab.h> @@ -104,21 +103,15 @@ snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *c const struct pnp_card_device_id *id) { struct pnp_dev *pdev; - struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL); int err; - if (!cfg) - return -ENOMEM; - /* Check for each logical device. */ /* CS4232 chip (aka "windows sound system") is logical device 0 */ acard->wss = pnp_request_card_device(card, id->devs[0].id, NULL); - if (acard->wss == NULL) { - kfree(cfg); + if (acard->wss == NULL) return -EBUSY; - } /* there is a game port at logical device 1, but we ignore it completely */ @@ -133,26 +126,20 @@ snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *c if (use_cs4232_midi[dev]) { acard->mpu = pnp_request_card_device(card, id->devs[2].id, NULL); - if (acard->mpu == NULL) { - kfree(cfg); + if (acard->mpu == NULL) return -EBUSY; - } } /* The ICS2115 synth is logical device 4 */ acard->synth = pnp_request_card_device(card, id->devs[3].id, NULL); - if (acard->synth == NULL) { - kfree(cfg); + if (acard->synth == NULL) return -EBUSY; - } /* PCM/FM initialization */ pdev = acard->wss; - pnp_init_resource_table(cfg); - /* An interesting note from the Tropez+ FAQ: Q. [Ports] Why is the base address of the WSS I/O ports off by 4? @@ -165,23 +152,9 @@ snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *c */ - if (cs4232_pcm_port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[0], cs4232_pcm_port[dev], 4); - if (fm_port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4); - if (dma1[dev] != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1); - if (dma2[dev] != SNDRV_AUTO_DMA) - pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1); - if (cs4232_pcm_irq[dev] != SNDRV_AUTO_IRQ) - pnp_resource_change(&cfg->irq_resource[0], cs4232_pcm_irq[dev], 1); - - if (pnp_manual_config_dev(pdev, cfg, 0) < 0) - snd_printk(KERN_ERR "PnP WSS the requested resources are invalid, using auto config\n"); err = pnp_activate_dev(pdev); if (err < 0) { snd_printk(KERN_ERR "PnP WSS pnp configure failure\n"); - kfree(cfg); return err; } @@ -195,22 +168,9 @@ snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *c pdev = acard->synth; - pnp_init_resource_table(cfg); - - if (ics2115_port[dev] != SNDRV_AUTO_PORT) { - pnp_resource_change(&cfg->port_resource[0], ics2115_port[dev], 16); - } - - if (ics2115_port[dev] != SNDRV_AUTO_IRQ) { - pnp_resource_change(&cfg->irq_resource[0], ics2115_irq[dev], 1); - } - - if (pnp_manual_config_dev(pdev, cfg, 0) < 0) - snd_printk(KERN_ERR "PnP ICS2115 the requested resources are invalid, using auto config\n"); err = pnp_activate_dev(pdev); if (err < 0) { snd_printk(KERN_ERR "PnP ICS2115 pnp configure failure\n"); - kfree(cfg); return err; } @@ -226,15 +186,6 @@ snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *c pdev = acard->mpu; - pnp_init_resource_table(cfg); - - if (cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) - pnp_resource_change(&cfg->port_resource[0], cs4232_mpu_port[dev], 2); - if (cs4232_mpu_irq[dev] != SNDRV_AUTO_IRQ) - pnp_resource_change(&cfg->port_resource[0], cs4232_mpu_irq[dev], 1); - - if (pnp_manual_config_dev(pdev, cfg, 0) < 0) - snd_printk(KERN_ERR "PnP MPU401 the requested resources are invalid, using auto config\n"); err = pnp_activate_dev(pdev); if (err < 0) { snd_printk(KERN_ERR "PnP MPU401 pnp configure failure\n"); @@ -258,7 +209,6 @@ snd_wavefront_pnp (int dev, snd_wavefront_card_t *acard, struct pnp_card_link *c ics2115_port[dev], ics2115_irq[dev]); - kfree(cfg); return 0; } diff --git a/sound/isa/wavefront/wavefront_fx.c b/sound/isa/wavefront/wavefront_fx.c index fc95a87..2efaa7f 100644 --- a/sound/isa/wavefront/wavefront_fx.c +++ b/sound/isa/wavefront/wavefront_fx.c @@ -16,7 +16,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/init.h> #include <linux/time.h> diff --git a/sound/isa/wavefront/wavefront_midi.c b/sound/isa/wavefront/wavefront_midi.c index cb34600..a33384a 100644 --- a/sound/isa/wavefront/wavefront_midi.c +++ b/sound/isa/wavefront/wavefront_midi.c @@ -47,7 +47,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/init.h> #include <linux/time.h> diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c index a1ebb7c..95eeca1 100644 --- a/sound/isa/wavefront/wavefront_synth.c +++ b/sound/isa/wavefront/wavefront_synth.c @@ -20,7 +20,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/interrupt.h> #include <linux/init.h> diff --git a/sound/last.c b/sound/last.c index 282b0cd..bdd0857 100644 --- a/sound/last.c +++ b/sound/last.c @@ -20,7 +20,6 @@ */ #define SNDRV_MAIN_OBJECT_FILE -#include <sound/driver.h> #include <linux/init.h> #include <sound/core.h> diff --git a/sound/mips/au1x00.c b/sound/mips/au1x00.c index 24460a5..ee0741f 100644 --- a/sound/mips/au1x00.c +++ b/sound/mips/au1x00.c @@ -36,7 +36,6 @@ #include <linux/ioport.h> #include <linux/interrupt.h> -#include <sound/driver.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/version.h> diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c index ff705c6..99f5483 100644 --- a/sound/parisc/harmony.c +++ b/sound/parisc/harmony.c @@ -45,7 +45,6 @@ #include <linux/spinlock.h> #include <linux/dma-mapping.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/control.h> diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 356bf21..812085d 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -183,6 +183,30 @@ config SND_CMIPCI To compile this driver as a module, choose M here: the module will be called snd-cmipci. +config SND_OXYGEN_LIB + tristate + depends on SND + select SND_PCM + select SND_MPU401_UART + +config SND_OXYGEN + tristate "C-Media 8788 (Oxygen)" + depends on SND + select SND_OXYGEN_LIB + help + Say Y here to include support for sound cards based on the + C-Media CMI8788 (Oxygen HD Audio) chip: + * Asound A-8788 + * AuzenTech X-Meridian + * Bgears b-Enspirer + * Club3D Theatron DTS + * HT-Omega Claro + * Razer Barracuda AC-1 + * Sondigo Inferno + + To compile this driver as a module, choose M here: the module + will be called snd-oxygen. + config SND_CS4281 tristate "Cirrus Logic (Sound Fusion) CS4281" depends on SND @@ -623,6 +647,17 @@ config SND_HDSPM To compile this driver as a module, choose M here: the module will be called snd-hdspm. +config SND_HIFIER + tristate "TempoTec HiFier Fantasia" + depends on SND + select SND_OXYGEN_LIB + help + Say Y here to include support for the MediaTek/TempoTec HiFier + Fantasia sound card. + + To compile this driver as a module, choose M here: the module + will be called snd-hifier. + config SND_ICE1712 tristate "ICEnsemble ICE1712 (Envy24)" depends on SND @@ -802,6 +837,16 @@ config SND_RME9652 To compile this driver as a module, choose M here: the module will be called snd-rme9652. +config SND_SIS7019 + tristate "SiS 7019 Audio Accelerator" + depends on SND && X86 && !X86_64 + select SND_AC97_CODEC + help + Say Y here to include support for the SiS 7019 Audio Accelerator. + + To compile this driver as a module, choose M here: the module + will be called snd-sis7019. + config SND_SONICVIBES tristate "S3 SonicVibes" depends on SND @@ -850,6 +895,17 @@ config SND_VIA82XX_MODEM To compile this driver as a module, choose M here: the module will be called snd-via82xx-modem. +config SND_VIRTUOSO + tristate "Asus Virtuoso 200 (Xonar)" + depends on SND + select SND_OXYGEN_LIB + help + Say Y here to include support for sound cards based on the + Asus AV200 chip, i.e., Xonar D2 and Xonar D2X. + + To compile this driver as a module, choose M here: the module + will be called snd-virtuoso. + config SND_VX222 tristate "Digigram VX222" depends on SND diff --git a/sound/pci/Makefile b/sound/pci/Makefile index 09ddc82..2d42fd2 100644 --- a/sound/pci/Makefile +++ b/sound/pci/Makefile @@ -23,6 +23,7 @@ snd-intel8x0m-objs := intel8x0m.o snd-maestro3-objs := maestro3.o snd-rme32-objs := rme32.o snd-rme96-objs := rme96.o +snd-sis7019-objs := sis7019.o snd-sonicvibes-objs := sonicvibes.o snd-via82xx-objs := via82xx.o snd-via82xx-modem-objs := via82xx_modem.o @@ -48,6 +49,7 @@ obj-$(CONFIG_SND_INTEL8X0M) += snd-intel8x0m.o obj-$(CONFIG_SND_MAESTRO3) += snd-maestro3.o obj-$(CONFIG_SND_RME32) += snd-rme32.o obj-$(CONFIG_SND_RME96) += snd-rme96.o +obj-$(CONFIG_SND_SIS7019) += snd-sis7019.o obj-$(CONFIG_SND_SONICVIBES) += snd-sonicvibes.o obj-$(CONFIG_SND_VIA82XX) += snd-via82xx.o obj-$(CONFIG_SND_VIA82XX_MODEM) += snd-via82xx-modem.o @@ -66,6 +68,7 @@ obj-$(CONFIG_SND) += \ korg1212/ \ mixart/ \ nm256/ \ + oxygen/ \ pcxhr/ \ riptide/ \ rme9652/ \ diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 6a9966d..45fd290 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -22,7 +22,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/slab.h> diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 98c8b72..50c637e 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -133,6 +133,14 @@ static int ac97_channel_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); unsigned char mode = ucontrol->value.enumerated.item[0]; + if (kcontrol->private_value) { + if (mode >= 2) + return -EINVAL; + } else { + if (mode >= 3) + return -EINVAL; + } + if (mode != ac97->channel_mode) { ac97->channel_mode = mode; if (ac97->build_ops->update_jacks) @@ -2142,8 +2150,7 @@ static int snd_ac97_ad1985_vrefout_put(struct snd_kcontrol *kcontrol, struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); unsigned short val; - if (ucontrol->value.enumerated.item[0] > 3 - || ucontrol->value.enumerated.item[0] < 0) + if (ucontrol->value.enumerated.item[0] > 3) return -EINVAL; val = ctrl2reg[ucontrol->value.enumerated.item[0]] << AC97_AD198X_VREF_SHIFT; diff --git a/sound/pci/ac97/ac97_patch.h b/sound/pci/ac97/ac97_patch.h index 9cccc27..47bf8df 100644 --- a/sound/pci/ac97/ac97_patch.h +++ b/sound/pci/ac97/ac97_patch.h @@ -83,8 +83,10 @@ static int snd_ac97_swap_ctl(struct snd_ac97 *ac97, const char *s1, const char *s2, const char *suffix); static void snd_ac97_rename_vol_ctl(struct snd_ac97 *ac97, const char *src, const char *dst); +#ifdef CONFIG_PM static void snd_ac97_restore_status(struct snd_ac97 *ac97); static void snd_ac97_restore_iec958(struct snd_ac97 *ac97); +#endif static int snd_ac97_info_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); static int snd_ac97_get_enum_double(struct snd_kcontrol *kcontrol, diff --git a/sound/pci/ac97/ac97_pcm.c b/sound/pci/ac97/ac97_pcm.c index 8cbc033..3674f35 100644 --- a/sound/pci/ac97/ac97_pcm.c +++ b/sound/pci/ac97/ac97_pcm.c @@ -23,7 +23,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/slab.h> diff --git a/sound/pci/ac97/ac97_proc.c b/sound/pci/ac97/ac97_proc.c index fed4a2c..060ea59 100644 --- a/sound/pci/ac97/ac97_proc.c +++ b/sound/pci/ac97/ac97_proc.c @@ -22,7 +22,6 @@ * */ -#include <sound/driver.h> #include <linux/slab.h> #include <linux/mutex.h> diff --git a/sound/pci/ac97/ak4531_codec.c b/sound/pci/ac97/ak4531_codec.c index 722de45..c0c16339 100644 --- a/sound/pci/ac97/ak4531_codec.c +++ b/sound/pci/ac97/ak4531_codec.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/slab.h> diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c index 98970d4..a66d515 100644 --- a/sound/pci/ad1889.c +++ b/sound/pci/ad1889.c @@ -40,7 +40,6 @@ #include <linux/compiler.h> #include <linux/delay.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/initval.h> @@ -1055,7 +1054,7 @@ static struct pci_device_id snd_ad1889_ids[] = { }; MODULE_DEVICE_TABLE(pci, snd_ad1889_ids); -static struct pci_driver ad1889_pci = { +static struct pci_driver ad1889_pci_driver = { .name = "AD1889 Audio", .id_table = snd_ad1889_ids, .probe = snd_ad1889_probe, @@ -1065,13 +1064,13 @@ static struct pci_driver ad1889_pci = { static int __init alsa_ad1889_init(void) { - return pci_register_driver(&ad1889_pci); + return pci_register_driver(&ad1889_pci_driver); } static void __exit alsa_ad1889_fini(void) { - pci_unregister_driver(&ad1889_pci); + pci_unregister_driver(&ad1889_pci_driver); } module_init(alsa_ad1889_init); diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index 4c2bd7a..6a905ed 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -25,7 +25,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/sound/pci/als300.c b/sound/pci/als300.c index 48cc39b..0e990a7 100644 --- a/sound/pci/als300.c +++ b/sound/pci/als300.c @@ -30,7 +30,6 @@ * to keep track of what period we are in. */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/moduleparam.h> diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c index 1190ef3..27ce613 100644 --- a/sound/pci/als4000.c +++ b/sound/pci/als4000.c @@ -63,7 +63,6 @@ * - power management? (card can do voice wakeup according to datasheet!!) */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/init.h> #include <linux/pci.h> diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 89184a4..4594186 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -560,7 +559,7 @@ static int snd_atiixp_aclink_down(struct atiixp *chip) ATI_REG_ISR_CODEC2_NOT_READY) #define CODEC_CHECK_BITS (ALL_CODEC_NOT_READY|ATI_REG_ISR_NEW_FRAME) -static int ac97_probing_bugs(struct pci_dev *pci) +static int __devinit ac97_probing_bugs(struct pci_dev *pci) { const struct snd_pci_quirk *q; @@ -574,7 +573,7 @@ static int ac97_probing_bugs(struct pci_dev *pci) return -1; } -static int snd_atiixp_codec_detect(struct atiixp *chip) +static int __devinit snd_atiixp_codec_detect(struct atiixp *chip) { int timeout; diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index ce752f8..a67a869 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/sound/pci/au88x0/au88x0.h b/sound/pci/au88x0/au88x0.h index 5ccf0b1..4aad35b 100644 --- a/sound/pci/au88x0/au88x0.h +++ b/sound/pci/au88x0/au88x0.h @@ -18,7 +18,6 @@ #define __SOUND_AU88X0_H #ifdef __KERNEL__ -#include <sound/driver.h> #include <linux/pci.h> #include <asm/io.h> #include <sound/core.h> diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c index 4a336ea..333c62d 100644 --- a/sound/pci/au88x0/au88x0_core.c +++ b/sound/pci/au88x0/au88x0_core.c @@ -2395,7 +2395,7 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id) if (!(hwread(vortex->mmio, VORTEX_STAT) & 0x1)) return IRQ_NONE; - // This is the Interrrupt Enable flag we set before (consistency check). + // This is the Interrupt Enable flag we set before (consistency check). if (!(hwread(vortex->mmio, VORTEX_CTRL) & CTRL_IRQ_ENABLE)) return IRQ_NONE; diff --git a/sound/pci/au88x0/au88x0_game.c b/sound/pci/au88x0/au88x0_game.c index a07d1de..bc212f4 100644 --- a/sound/pci/au88x0/au88x0_game.c +++ b/sound/pci/au88x0/au88x0_game.c @@ -30,7 +30,6 @@ * driver. (email: mjander@embedded.cl). */ -#include <sound/driver.h> #include <linux/time.h> #include <linux/delay.h> #include <linux/init.h> diff --git a/sound/pci/au88x0/au88x0_mixer.c b/sound/pci/au88x0/au88x0_mixer.c index c96da1d..c92f493 100644 --- a/sound/pci/au88x0/au88x0_mixer.c +++ b/sound/pci/au88x0/au88x0_mixer.c @@ -5,7 +5,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <linux/init.h> #include <sound/core.h> diff --git a/sound/pci/au88x0/au88x0_mpu401.c b/sound/pci/au88x0/au88x0_mpu401.c index 8db3d3e..0dc8d25 100644 --- a/sound/pci/au88x0/au88x0_mpu401.c +++ b/sound/pci/au88x0/au88x0_mpu401.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <linux/init.h> #include <sound/core.h> diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c index 7b5baa1..526c6c5 100644 --- a/sound/pci/au88x0/au88x0_pcm.c +++ b/sound/pci/au88x0/au88x0_pcm.c @@ -21,7 +21,6 @@ * It remains stuck,and DMA transfers do not happen. */ #include <sound/asoundef.h> -#include <sound/driver.h> #include <linux/time.h> #include <sound/core.h> #include <sound/pcm.h> diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 36d3666..4e71a55 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -115,7 +115,6 @@ * code (but I'm not too optimistic that doing this is possible at all) */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/init.h> #include <linux/pci.h> diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c index 2dba752..c9a2421 100644 --- a/sound/pci/bt87x.c +++ b/sound/pci/bt87x.c @@ -21,7 +21,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/pci.h> diff --git a/sound/pci/ca0106/ca0106.h b/sound/pci/ca0106/ca0106.h index 75da174..74175fc 100644 --- a/sound/pci/ca0106/ca0106.h +++ b/sound/pci/ca0106/ca0106.h @@ -272,7 +272,6 @@ #define SPCS_WORD_LENGTH_20A 0x0000000a /* Word Length 20 bit */ #define SPCS_WORD_LENGTH_20 0x00000009 /* Word Length 20 bit (both 0xa and 0x9 are 20 bit) */ #define SPCS_WORD_LENGTH_21 0x00000007 /* Word Length 21 bit */ -#define SPCS_WORD_LENGTH_21 0x00000007 /* Word Length 21 bit */ #define SPCS_WORD_LENGTH_22 0x00000005 /* Word Length 22 bit */ #define SPCS_WORD_LENGTH_23 0x00000003 /* Word Length 23 bit */ #define SPCS_WORD_LENGTH_24 0x0000000b /* Word Length 24 bit */ diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index 31d8db9..176e0f0 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -135,7 +135,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index 3f9b5c5..af73686 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -60,7 +60,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c index 61f2718..c62b7d1 100644 --- a/sound/pci/ca0106/ca0106_proc.c +++ b/sound/pci/ca0106/ca0106_proc.c @@ -60,7 +60,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/ca0106/ca_midi.c b/sound/pci/ca0106/ca_midi.c index ad32eff..893ee4f 100644 --- a/sound/pci/ca0106/ca_midi.c +++ b/sound/pci/ca0106/ca_midi.c @@ -27,7 +27,6 @@ */ #include <linux/spinlock.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/rawmidi.h> diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 1fa5f00..135f308 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -20,7 +20,6 @@ /* Does not work. Warning may block system in capture mode */ /* #define USE_VAR48KRATE */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -150,6 +149,8 @@ MODULE_PARM_DESC(joystick_port, "Joystick port address."); #define CM_CH0_SRATE_176K 0x00000200 #define CM_CH0_SRATE_96K 0x00000200 /* model 055? */ #define CM_CH0_SRATE_88K 0x00000100 +#define CM_CH0_SRATE_128K 0x00000300 +#define CM_CH0_SRATE_MASK 0x00000300 #define CM_SPDIF_INVERSE2 0x00000080 /* model 055? */ #define CM_DBLSPDS 0x00000040 /* double SPDIF sample rate 88.2/96 */ @@ -473,6 +474,7 @@ struct cmipci { unsigned int can_ac3_sw: 1; unsigned int can_ac3_hw: 1; unsigned int can_multi_ch: 1; + unsigned int can_96k: 1; /* samplerate above 48k */ unsigned int do_soft_ac3: 1; unsigned int spdif_playback_avail: 1; /* spdif ready? */ @@ -603,8 +605,6 @@ static unsigned int snd_cmipci_rate_freq(unsigned int rate) { unsigned int i; - if (rate > 48000) - rate /= 2; for (i = 0; i < ARRAY_SIZE(rates); i++) { if (rates[i] == rate) return i; @@ -782,7 +782,7 @@ static int set_dac_channels(struct cmipci *cm, struct cmipci_pcm *rec, int chann static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec, struct snd_pcm_substream *substream) { - unsigned int reg, freq, val; + unsigned int reg, freq, freq_ext, val; unsigned int period_size; struct snd_pcm_runtime *runtime = substream->runtime; @@ -830,7 +830,17 @@ static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec, //snd_printd("cmipci: functrl0 = %08x\n", cm->ctrl); /* set sample rate */ - freq = snd_cmipci_rate_freq(runtime->rate); + freq = 0; + freq_ext = 0; + if (runtime->rate > 48000) + switch (runtime->rate) { + case 88200: freq_ext = CM_CH0_SRATE_88K; break; + case 96000: freq_ext = CM_CH0_SRATE_96K; break; + case 128000: freq_ext = CM_CH0_SRATE_128K; break; + default: snd_BUG(); break; + } + else + freq = snd_cmipci_rate_freq(runtime->rate); val = snd_cmipci_read(cm, CM_REG_FUNCTRL1); if (rec->ch) { val &= ~CM_DSFC_MASK; @@ -851,19 +861,20 @@ static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec, val &= ~CM_CH0FMT_MASK; val |= rec->fmt << CM_CH0FMT_SHIFT; } - if (cm->chip_version == 68) { - if (runtime->rate == 88200) - val |= CM_CH0_SRATE_88K << (rec->ch * 2); - else - val &= ~(CM_CH0_SRATE_88K << (rec->ch * 2)); - if (runtime->rate == 96000) - val |= CM_CH0_SRATE_96K << (rec->ch * 2); - else - val &= ~(CM_CH0_SRATE_96K << (rec->ch * 2)); + if (cm->can_96k) { + val &= ~(CM_CH0_SRATE_MASK << (rec->ch * 2)); + val |= freq_ext << (rec->ch * 2); } snd_cmipci_write(cm, CM_REG_CHFORMAT, val); //snd_printd("cmipci: chformat = %08x\n", val); + if (!rec->is_dac && cm->chip_version) { + if (runtime->rate > 44100) + snd_cmipci_set_bit(cm, CM_REG_EXT_MISC, CM_ADC48K44K); + else + snd_cmipci_clear_bit(cm, CM_REG_EXT_MISC, CM_ADC48K44K); + } + rec->running = 0; spin_unlock_irq(&cm->reg_lock); @@ -1280,7 +1291,7 @@ static int snd_cmipci_playback_prepare(struct snd_pcm_substream *substream) int rate = substream->runtime->rate; int err, do_spdif, do_ac3 = 0; - do_spdif = (rate >= 44100 && + do_spdif = (rate >= 44100 && rate <= 96000 && substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE && substream->runtime->channels == 2); if (do_spdif && cm->can_ac3_hw) @@ -1336,10 +1347,8 @@ static void snd_cmipci_silence_hack(struct cmipci *cm, struct cmipci_pcm *rec) val = snd_cmipci_read(cm, CM_REG_CHFORMAT); val &= ~(CM_CH0FMT_MASK << (rec->ch * 2)); val |= (3 << CM_CH0FMT_SHIFT) << (rec->ch * 2); - if (cm->chip_version == 68) { - val &= ~(CM_CH0_SRATE_88K << (rec->ch * 2)); - val &= ~(CM_CH0_SRATE_96K << (rec->ch * 2)); - } + if (cm->can_96k) + val &= ~(CM_CH0_SRATE_MASK << (rec->ch * 2)); snd_cmipci_write(cm, CM_REG_CHFORMAT, val); /* start stream (we don't need interrupts) */ @@ -1391,6 +1400,17 @@ static int snd_cmipci_capture_spdif_prepare(struct snd_pcm_substream *substream) spin_lock_irq(&cm->reg_lock); snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF); + if (cm->can_96k) { + if (substream->runtime->rate > 48000) + snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_DBLSPDS); + else + snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_DBLSPDS); + } + if (snd_pcm_format_width(substream->runtime->format) > 16) + snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL); + else + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL); + spin_unlock_irq(&cm->reg_lock); return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_CAPT], substream); @@ -1402,6 +1422,7 @@ static int snd_cmipci_capture_spdif_hw_free(struct snd_pcm_substream *subs) spin_lock_irq(&cm->reg_lock); snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF); + snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL); spin_unlock_irq(&cm->reg_lock); return snd_cmipci_hw_free(subs); @@ -1553,7 +1574,8 @@ static struct snd_pcm_hardware snd_cmipci_capture_spdif = .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE, .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, .rate_min = 44100, .rate_max = 48000, @@ -1567,6 +1589,14 @@ static struct snd_pcm_hardware snd_cmipci_capture_spdif = .fifo_size = 0, }; +static unsigned int rate_constraints[] = { 5512, 8000, 11025, 16000, 22050, + 32000, 44100, 48000, 88200, 96000, 128000 }; +static struct snd_pcm_hw_constraint_list hw_constraints_rates = { + .count = ARRAY_SIZE(rate_constraints), + .list = rate_constraints, + .mask = 0, +}; + /* * check device open/close */ @@ -1636,6 +1666,13 @@ static int snd_cmipci_playback_open(struct snd_pcm_substream *substream) runtime->hw.rates |= SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000; runtime->hw.rate_max = 96000; + } else if (cm->chip_version == 55) { + err = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + if (err < 0) + return err; + runtime->hw.rates |= SNDRV_PCM_RATE_KNOT; + runtime->hw.rate_max = 128000; } snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000); cm->dig_pcm_status = cm->dig_status; @@ -1654,6 +1691,13 @@ static int snd_cmipci_capture_open(struct snd_pcm_substream *substream) if (cm->chip_version == 68) { // 8768 only supports 44k/48k recording runtime->hw.rate_min = 41000; runtime->hw.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000; + } else if (cm->chip_version == 55) { + err = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + if (err < 0) + return err; + runtime->hw.rates |= SNDRV_PCM_RATE_KNOT; + runtime->hw.rate_max = 128000; } snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000); return 0; @@ -1685,6 +1729,13 @@ static int snd_cmipci_playback2_open(struct snd_pcm_substream *substream) runtime->hw.rates |= SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000; runtime->hw.rate_max = 96000; + } else if (cm->chip_version == 55) { + err = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); + if (err < 0) + return err; + runtime->hw.rates |= SNDRV_PCM_RATE_KNOT; + runtime->hw.rate_max = 128000; } snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000); return 0; @@ -1704,7 +1755,7 @@ static int snd_cmipci_playback_spdif_open(struct snd_pcm_substream *substream) runtime->hw.formats |= SNDRV_PCM_FMTBIT_S32_LE; snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); } - if (cm->chip_version == 68) { + if (cm->can_96k) { runtime->hw.rates |= SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000; runtime->hw.rate_max = 96000; @@ -1726,6 +1777,11 @@ static int snd_cmipci_capture_spdif_open(struct snd_pcm_substream *substream) if ((err = open_device_check(cm, CM_OPEN_SPDIF_CAPTURE, substream)) < 0) /* use channel B */ return err; runtime->hw = snd_cmipci_capture_spdif; + if (cm->can_96k && !(cm->chip_version == 68)) { + runtime->hw.rates |= SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000; + runtime->hw.rate_max = 96000; + } snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x40000); return 0; } @@ -2594,10 +2650,8 @@ static struct snd_kcontrol_new snd_cmipci_extra_mixer_switches[] __devinitdata = }; /* card control switches */ -static struct snd_kcontrol_new snd_cmipci_control_switches[] __devinitdata = { - // DEFINE_CARD_SWITCH("Joystick", joystick), /* now module option */ - DEFINE_CARD_SWITCH("Modem", modem), -}; +static struct snd_kcontrol_new snd_cmipci_modem_switch __devinitdata = +DEFINE_CARD_SWITCH("Modem", modem); static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_device) @@ -2678,9 +2732,13 @@ static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_devic } /* card switches */ - sw = snd_cmipci_control_switches; - for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_control_switches); idx++, sw++) { - err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); + /* + * newer chips don't have the register bits to force modem link + * detection; the bit that was FLINKON now mutes CH1 + */ + if (cm->chip_version < 39) { + err = snd_ctl_add(cm->card, + snd_ctl_new1(&snd_cmipci_modem_switch, cm)); if (err < 0) return err; } @@ -2785,9 +2843,11 @@ static void __devinit query_chip(struct cmipci *cm) } else if (detect & CM_CHIP_8768) { cm->chip_version = 68; cm->max_channels = 8; + cm->can_96k = 1; } else { cm->chip_version = 55; cm->max_channels = 6; + cm->can_96k = 1; } cm->can_ac3_hw = 1; cm->can_multi_ch = 1; diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index 9a55f4a..7556fd9 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c index 2699cb6..e876b32 100644 --- a/sound/pci/cs46xx/cs46xx.c +++ b/sound/pci/cs46xx/cs46xx.c @@ -25,7 +25,6 @@ reloading the module may solve this. */ -#include <sound/driver.h> #include <linux/pci.h> #include <linux/time.h> #include <linux/init.h> diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 2c7bfc9..87ddffc 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -8,7 +8,7 @@ * - Sometimes the SPDIF input DSP tasks get's unsynchronized * and the SPDIF get somewhat "distorcionated", or/and left right channel * are swapped. To get around this problem when it happens, mute and unmute - * the SPDIF input mixer controll. + * the SPDIF input mixer control. * - On the Hercules Game Theater XP the amplifier are sometimes turned * off on inadecuate moments which causes distorcions on sound. * @@ -45,7 +45,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/pci.h> #include <linux/pm.h> @@ -2084,71 +2083,6 @@ static int snd_cs46xx_spdif_stream_put(struct snd_kcontrol *kcontrol, #endif /* CONFIG_SND_CS46XX_NEW_DSP */ -#ifdef CONFIG_SND_CS46XX_DEBUG_GPIO -static int snd_cs46xx_egpio_select_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 = 8; - return 0; -} - -static int snd_cs46xx_egpio_select_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol); - ucontrol->value.integer.value[0] = chip->current_gpio; - - return 0; -} - -static int snd_cs46xx_egpio_select_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol); - int change = (chip->current_gpio != ucontrol->value.integer.value[0]); - chip->current_gpio = ucontrol->value.integer.value[0]; - - return change; -} - - -static int snd_cs46xx_egpio_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol); - int reg = kcontrol->private_value; - - snd_printdd ("put: reg = %04x, gpio %02x\n",reg,chip->current_gpio); - ucontrol->value.integer.value[0] = - (snd_cs46xx_peekBA0(chip, reg) & (1 << chip->current_gpio)) ? 1 : 0; - - return 0; -} - -static int snd_cs46xx_egpio_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol); - int reg = kcontrol->private_value; - int val = snd_cs46xx_peekBA0(chip, reg); - int oldval = val; - snd_printdd ("put: reg = %04x, gpio %02x\n",reg,chip->current_gpio); - - if (ucontrol->value.integer.value[0]) - val |= (1 << chip->current_gpio); - else - val &= ~(1 << chip->current_gpio); - - snd_cs46xx_pokeBA0(chip, reg,val); - snd_printdd ("put: val %08x oldval %08x\n",val,oldval); - - return (oldval != val); -} -#endif /* CONFIG_SND_CS46XX_DEBUG_GPIO */ - static struct snd_kcontrol_new snd_cs46xx_controls[] __devinitdata = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -2241,40 +2175,6 @@ static struct snd_kcontrol_new snd_cs46xx_controls[] __devinitdata = { }, #endif -#ifdef CONFIG_SND_CS46XX_DEBUG_GPIO -{ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "EGPIO select", - .info = snd_cs46xx_egpio_select_info, - .get = snd_cs46xx_egpio_select_get, - .put = snd_cs46xx_egpio_select_put, - .private_value = 0, -}, -{ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "EGPIO Input/Output", - .info = snd_mixer_boolean_info, - .get = snd_cs46xx_egpio_get, - .put = snd_cs46xx_egpio_put, - .private_value = BA0_EGPIODR, -}, -{ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "EGPIO CMOS/Open drain", - .info = snd_mixer_boolean_info, - .get = snd_cs46xx_egpio_get, - .put = snd_cs46xx_egpio_put, - .private_value = BA0_EGPIOPTR, -}, -{ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "EGPIO On/Off", - .info = snd_mixer_boolean_info, - .get = snd_cs46xx_egpio_get, - .put = snd_cs46xx_egpio_put, - .private_value = BA0_EGPIOSR, -}, -#endif }; #ifdef CONFIG_SND_CS46XX_NEW_DSP diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c index 590b35d..ccc8bed 100644 --- a/sound/pci/cs46xx/dsp_spos.c +++ b/sound/pci/cs46xx/dsp_spos.c @@ -20,7 +20,6 @@ */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/pm.h> diff --git a/sound/pci/cs46xx/dsp_spos_scb_lib.c b/sound/pci/cs46xx/dsp_spos_scb_lib.c index eded4df..2873cfe 100644 --- a/sound/pci/cs46xx/dsp_spos_scb_lib.c +++ b/sound/pci/cs46xx/dsp_spos_scb_lib.c @@ -21,7 +21,6 @@ */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/pm.h> diff --git a/sound/pci/cs5530.c b/sound/pci/cs5530.c index 240a0a4..7ff8b68 100644 --- a/sound/pci/cs5530.c +++ b/sound/pci/cs5530.c @@ -36,7 +36,6 @@ * same manner. */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/moduleparam.h> #include <linux/pci.h> diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c index 2b35889..1d8b160 100644 --- a/sound/pci/cs5535audio/cs5535audio.c +++ b/sound/pci/cs5535audio/cs5535audio.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/init.h> @@ -145,7 +144,7 @@ static unsigned short snd_cs5535audio_ac97_codec_read(struct snd_ac97 *ac97, return snd_cs5535audio_codec_read(cs5535au, reg); } -static int snd_cs5535audio_mixer(struct cs5535audio *cs5535au) +static int __devinit snd_cs5535audio_mixer(struct cs5535audio *cs5535au) { struct snd_card *card = cs5535au->card; struct snd_ac97_bus *pbus; diff --git a/sound/pci/cs5535audio/cs5535audio_pcm.c b/sound/pci/cs5535audio/cs5535audio_pcm.c index 21df063..cdcda87 100644 --- a/sound/pci/cs5535audio/cs5535audio_pcm.c +++ b/sound/pci/cs5535audio/cs5535audio_pcm.c @@ -25,7 +25,6 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/pci.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/control.h> #include <sound/initval.h> @@ -98,6 +97,8 @@ static int snd_cs5535audio_playback_open(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; runtime->hw = snd_cs5535audio_playback; + runtime->hw.rates = cs5535au->ac97->rates[AC97_RATES_FRONT_DAC]; + snd_pcm_limit_hw_rates(runtime); cs5535au->playback_substream = substream; runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_PLAYBACK]); if ((err = snd_pcm_hw_constraint_integer(runtime, @@ -343,6 +344,8 @@ static int snd_cs5535audio_capture_open(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; runtime->hw = snd_cs5535audio_capture; + runtime->hw.rates = cs5535au->ac97->rates[AC97_RATES_ADC]; + snd_pcm_limit_hw_rates(runtime); cs5535au->capture_substream = substream; runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_CAPTURE]); if ((err = snd_pcm_hw_constraint_integer(runtime, diff --git a/sound/pci/cs5535audio/cs5535audio_pm.c b/sound/pci/cs5535audio/cs5535audio_pm.c index 838708f..564c33b 100644 --- a/sound/pci/cs5535audio/cs5535audio_pm.c +++ b/sound/pci/cs5535audio/cs5535audio_pm.c @@ -22,7 +22,6 @@ #include <linux/slab.h> #include <linux/pci.h> #include <linux/delay.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/control.h> #include <sound/initval.h> diff --git a/sound/pci/echoaudio/darla20.c b/sound/pci/echoaudio/darla20.c index 87078d3..8c6db3a 100644 --- a/sound/pci/echoaudio/darla20.c +++ b/sound/pci/echoaudio/darla20.c @@ -36,7 +36,6 @@ #define BX_NUM 10 -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/echoaudio/darla24.c b/sound/pci/echoaudio/darla24.c index 42b48f9..04cbf3e 100644 --- a/sound/pci/echoaudio/darla24.c +++ b/sound/pci/echoaudio/darla24.c @@ -40,7 +40,6 @@ #define BX_NUM 10 -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/echoaudio/echo3g.c b/sound/pci/echoaudio/echo3g.c index 8dbb7ac..4022e43 100644 --- a/sound/pci/echoaudio/echo3g.c +++ b/sound/pci/echoaudio/echo3g.c @@ -47,7 +47,6 @@ #define BX_NUM chip->bx_num -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 499ee1a..90ec090 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -378,7 +378,7 @@ static int pcm_digital_in_open(struct snd_pcm_substream *substream) DE_ACT(("pcm_digital_in_open\n")); max_channels = num_digital_busses_in(chip) - substream->number; - down(&chip->mode_mutex); + mutex_lock(&chip->mode_mutex); if (chip->digital_mode == DIGITAL_MODE_ADAT) err = pcm_open(substream, max_channels); else /* If the card has ADAT, subtract the 6 channels @@ -405,7 +405,7 @@ static int pcm_digital_in_open(struct snd_pcm_substream *substream) chip->can_set_rate=0; din_exit: - up(&chip->mode_mutex); + mutex_unlock(&chip->mode_mutex); return err; } @@ -420,7 +420,7 @@ static int pcm_digital_out_open(struct snd_pcm_substream *substream) DE_ACT(("pcm_digital_out_open\n")); max_channels = num_digital_busses_out(chip) - substream->number; - down(&chip->mode_mutex); + mutex_lock(&chip->mode_mutex); if (chip->digital_mode == DIGITAL_MODE_ADAT) err = pcm_open(substream, max_channels); else /* If the card has ADAT, subtract the 6 channels @@ -447,7 +447,7 @@ static int pcm_digital_out_open(struct snd_pcm_substream *substream) if (atomic_read(&chip->opencount) > 1 && chip->rate_set) chip->can_set_rate=0; dout_exit: - up(&chip->mode_mutex); + mutex_unlock(&chip->mode_mutex); return err; } @@ -1420,7 +1420,7 @@ static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol, if (dmode != chip->digital_mode) { /* mode_mutex is required to make this operation atomic wrt pcm_digital_*_open() and set_input_clock() functions. */ - down(&chip->mode_mutex); + mutex_lock(&chip->mode_mutex); /* Do not allow the user to change the digital mode when a pcm device is open because it also changes the number of channels @@ -1439,7 +1439,7 @@ static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol, if (changed >= 0) changed = 1; /* No errors */ } - up(&chip->mode_mutex); + mutex_unlock(&chip->mode_mutex); } return changed; } @@ -1566,12 +1566,12 @@ static int snd_echo_clock_source_put(struct snd_kcontrol *kcontrol, return -EINVAL; dclock = chip->clock_source_list[eclock]; if (chip->input_clock != dclock) { - down(&chip->mode_mutex); + mutex_lock(&chip->mode_mutex); spin_lock_irq(&chip->lock); if ((changed = set_input_clock(chip, dclock)) == 0) changed = 1; /* no errors */ spin_unlock_irq(&chip->lock); - up(&chip->mode_mutex); + mutex_unlock(&chip->mode_mutex); } if (changed < 0) @@ -1972,7 +1972,7 @@ static __devinit int snd_echo_create(struct snd_card *card, return err; } atomic_set(&chip->opencount, 0); - init_MUTEX(&chip->mode_mutex); + mutex_init(&chip->mode_mutex); chip->can_set_rate = 1; *rchip = chip; /* Init done ! */ diff --git a/sound/pci/echoaudio/echoaudio.h b/sound/pci/echoaudio/echoaudio.h index 7e88c96..1c88e05 100644 --- a/sound/pci/echoaudio/echoaudio.h +++ b/sound/pci/echoaudio/echoaudio.h @@ -361,7 +361,7 @@ struct echoaudio { spinlock_t lock; struct snd_pcm_substream *substream[DSP_MAXPIPES]; int last_period[DSP_MAXPIPES]; - struct semaphore mode_mutex; + struct mutex mode_mutex; u16 num_digital_modes, digital_mode_list[6]; u16 num_clock_sources, clock_source_list[10]; atomic_t opencount; diff --git a/sound/pci/echoaudio/gina20.c b/sound/pci/echoaudio/gina20.c index fee2d48..c0e64b8 100644 --- a/sound/pci/echoaudio/gina20.c +++ b/sound/pci/echoaudio/gina20.c @@ -40,7 +40,6 @@ #define BX_NUM 14 -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/echoaudio/gina24.c b/sound/pci/echoaudio/gina24.c index d5eae47..c36a78d 100644 --- a/sound/pci/echoaudio/gina24.c +++ b/sound/pci/echoaudio/gina24.c @@ -46,7 +46,6 @@ #define BX_NUM 26 -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/echoaudio/indigo.c b/sound/pci/echoaudio/indigo.c index 40f601c..0a58a7c 100644 --- a/sound/pci/echoaudio/indigo.c +++ b/sound/pci/echoaudio/indigo.c @@ -38,7 +38,6 @@ #define BX_NUM 2 -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/echoaudio/indigodj.c b/sound/pci/echoaudio/indigodj.c index 771c538..2db24d2 100644 --- a/sound/pci/echoaudio/indigodj.c +++ b/sound/pci/echoaudio/indigodj.c @@ -38,7 +38,6 @@ #define BX_NUM 4 -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/echoaudio/indigoio.c b/sound/pci/echoaudio/indigoio.c index 49c550d..a60c0a0 100644 --- a/sound/pci/echoaudio/indigoio.c +++ b/sound/pci/echoaudio/indigoio.c @@ -39,7 +39,6 @@ #define BX_NUM 4 -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/echoaudio/layla20.c b/sound/pci/echoaudio/layla20.c index 8f5483a..5061946 100644 --- a/sound/pci/echoaudio/layla20.c +++ b/sound/pci/echoaudio/layla20.c @@ -45,7 +45,6 @@ #define BX_NUM 22 -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/echoaudio/layla24.c b/sound/pci/echoaudio/layla24.c index 0524667..e09e3ea 100644 --- a/sound/pci/echoaudio/layla24.c +++ b/sound/pci/echoaudio/layla24.c @@ -47,7 +47,6 @@ #define BX_NUM 32 -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/echoaudio/mia.c b/sound/pci/echoaudio/mia.c index 893c7c2..f3b9b45 100644 --- a/sound/pci/echoaudio/mia.c +++ b/sound/pci/echoaudio/mia.c @@ -45,7 +45,6 @@ #define BX_NUM 8 -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/echoaudio/mona.c b/sound/pci/echoaudio/mona.c index 3a5d5b0..b05bad9 100644 --- a/sound/pci/echoaudio/mona.c +++ b/sound/pci/echoaudio/mona.c @@ -44,7 +44,6 @@ #define BX_NUM 26 -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index 9680caf..8354c1a 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -23,7 +23,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/pci.h> #include <linux/time.h> diff --git a/sound/pci/emu10k1/emu10k1_callback.c b/sound/pci/emu10k1/emu10k1_callback.c index 01965bd..45088eb 100644 --- a/sound/pci/emu10k1/emu10k1_callback.c +++ b/sound/pci/emu10k1/emu10k1_callback.c @@ -35,9 +35,9 @@ struct best_voice { /* * prototypes */ -static void lookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw, +static void lookup_voices(struct snd_emux *emux, struct snd_emu10k1 *hw, struct best_voice *best, int active_only); -static struct snd_emux_voice *get_voice(struct snd_emux *emu, +static struct snd_emux_voice *get_voice(struct snd_emux *emux, struct snd_emux_port *port); static int start_voice(struct snd_emux_voice *vp); static void trigger_voice(struct snd_emux_voice *vp); @@ -45,7 +45,6 @@ static void release_voice(struct snd_emux_voice *vp); static void update_voice(struct snd_emux_voice *vp, int update); static void terminate_voice(struct snd_emux_voice *vp); static void free_voice(struct snd_emux_voice *vp); - static void set_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp); static void set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp); static void set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp); @@ -75,9 +74,9 @@ static struct snd_emux_operators emu10k1_ops = { }; void -snd_emu10k1_ops_setup(struct snd_emux *emu) +snd_emu10k1_ops_setup(struct snd_emux *emux) { - emu->ops = emu10k1_ops; + emux->ops = emu10k1_ops; } @@ -166,7 +165,11 @@ free_voice(struct snd_emux_voice *vp) struct snd_emu10k1 *hw; hw = vp->hw; - if (vp->ch >= 0) { + /* FIXME: emu10k1_synth is broken. */ + /* This can get called with hw == 0 */ + /* Problem apparent on plug, unplug then plug */ + /* on the Audigy 2 ZS Notebook. */ + if (hw && (vp->ch >= 0)) { snd_emu10k1_ptr_write(hw, IFATN, vp->ch, 0xff00); snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK); // snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0); diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 97c41d7..9a9b977 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -33,7 +33,6 @@ #include <linux/sched.h> #include <linux/kthread.h> -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -55,12 +54,14 @@ #define DOCK_FILENAME "emu/audio_dock.fw" #define EMU1010B_FILENAME "emu/emu1010b.fw" #define MICRO_DOCK_FILENAME "emu/micro_dock.fw" +#define EMU0404_FILENAME "emu/emu0404.fw" #define EMU1010_NOTEBOOK_FILENAME "emu/emu1010_notebook.fw" MODULE_FIRMWARE(HANA_FILENAME); MODULE_FIRMWARE(DOCK_FILENAME); MODULE_FIRMWARE(EMU1010B_FILENAME); MODULE_FIRMWARE(MICRO_DOCK_FILENAME); +MODULE_FIRMWARE(EMU0404_FILENAME); MODULE_FIRMWARE(EMU1010_NOTEBOOK_FILENAME); @@ -258,7 +259,6 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) * GPIO7: Unknown */ outl(0x76, emu->port + A_IOCFG); /* Windows uses 0x3f76 */ - } if (emu->card_capabilities->i2c_adc) { /* Audigy 2 ZS Notebook with ADC Wolfson WM8775 */ int size, n; @@ -274,7 +274,6 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) emu->i2c_capture_volume[n][0]= 0xcf; emu->i2c_capture_volume[n][1]= 0xcf; } - } @@ -288,7 +287,7 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) snd_emu10k1_ptr_write(emu, MAPB, ch, silent_page); } - if (emu->card_capabilities->emu1010) { + if (emu->card_capabilities->emu_model) { outl(HCFG_AUTOMUTE_ASYNC | HCFG_EMU32_SLAVE | HCFG_AUDIOENABLE, emu->port + HCFG); @@ -318,7 +317,7 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG); if (enable_ir) { /* enable IR for SB Live */ - if (emu->card_capabilities->emu1010) { + if (emu->card_capabilities->emu_model) { ; /* Disable all access to A_IOCFG for the emu1010 */ } else if (emu->card_capabilities->i2c_adc) { ; /* Disable A_IOCFG for Audigy 2 ZS Notebook */ @@ -339,7 +338,7 @@ static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) } } - if (emu->card_capabilities->emu1010) { + if (emu->card_capabilities->emu_model) { ; /* Disable all access to A_IOCFG for the emu1010 */ } else if (emu->card_capabilities->i2c_adc) { ; /* Disable A_IOCFG for Audigy 2 ZS Notebook */ @@ -359,7 +358,7 @@ static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu) outl(inl(emu->port + HCFG) | HCFG_AUDIOENABLE, emu->port + HCFG); /* Enable analog/digital outs on audigy */ - if (emu->card_capabilities->emu1010) { + if (emu->card_capabilities->emu_model) { ; /* Disable all access to A_IOCFG for the emu1010 */ } else if (emu->card_capabilities->i2c_adc) { ; /* Disable A_IOCFG for Audigy 2 ZS Notebook */ @@ -652,6 +651,8 @@ static int snd_emu10k1_cardbus_init(struct snd_emu10k1 * emu) value = inl(special_port); snd_emu10k1_ptr20_write(emu, TINA2_VOLUME, 0, 0xfefefefe); /* Defaults to 0x30303030 */ + /* Delay to give time for ADC chip to switch on. It needs 113ms */ + msleep(200); return 0; } @@ -661,6 +662,8 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file int n, i; int reg; int value; + unsigned int write_post; + unsigned long flags; const struct firmware *fw_entry; if ((err = request_firmware(&fw_entry, filename, &emu->pci->dev)) != 0) { @@ -668,12 +671,6 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file return err; } snd_printk(KERN_INFO "firmware size=0x%zx\n", fw_entry->size); -#if 0 - if (fw_entry->size != 0x133a4) { - snd_printk(KERN_ERR "firmware: %s wrong size.\n",filename); - return -EINVAL; - } -#endif /* The FPGA is a Xilinx Spartan IIE XC2S50E */ /* GPIO7 -> FPGA PGMN @@ -681,9 +678,12 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file * GPIO5 -> FPGA DIN * FPGA CONFIG OFF -> FPGA PGMN */ + spin_lock_irqsave(&emu->emu_lock, flags); outl(0x00, emu->port + A_IOCFG); /* Set PGMN low for 1uS. */ - udelay(1); + write_post = inl(emu->port + A_IOCFG); + udelay(100); outl(0x80, emu->port + A_IOCFG); /* Leave bit 7 set during netlist setup. */ + write_post = inl(emu->port + A_IOCFG); udelay(100); /* Allow FPGA memory to clean */ for(n = 0; n < fw_entry->size; n++) { value=fw_entry->data[n]; @@ -693,18 +693,22 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file reg = reg | 0x20; value = value >> 1; outl(reg, emu->port + A_IOCFG); + write_post = inl(emu->port + A_IOCFG); outl(reg | 0x40, emu->port + A_IOCFG); + write_post = inl(emu->port + A_IOCFG); } } /* After programming, set GPIO bit 4 high again. */ outl(0x10, emu->port + A_IOCFG); - + write_post = inl(emu->port + A_IOCFG); + spin_unlock_irqrestore(&emu->emu_lock, flags); release_firmware(fw_entry); return 0; } -int emu1010_firmware_thread(void *data) { +static int emu1010_firmware_thread(void *data) +{ struct snd_emu10k1 * emu = data; int tmp,tmp2; int reg; @@ -712,7 +716,7 @@ int emu1010_firmware_thread(void *data) { for (;;) { /* Delay to allow Audio Dock to settle */ - msleep(1000); + msleep_interruptible(1000); if (kthread_should_stop()) break; snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp ); /* IRQ Status */ @@ -722,17 +726,20 @@ int emu1010_firmware_thread(void *data) { /* Return to Audio Dock programming mode */ snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware\n"); snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_AUDIODOCK ); - if (emu->card_capabilities->emu1010 == 1) { + if (emu->card_capabilities->emu_model == + EMU_MODEL_EMU1010) { if ((err = snd_emu1010_load_firmware(emu, DOCK_FILENAME)) != 0) { - return err; + continue; } - } else if (emu->card_capabilities->emu1010 == 2) { + } else if (emu->card_capabilities->emu_model == + EMU_MODEL_EMU1010B) { if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) { - return err; + continue; } - } else if (emu->card_capabilities->emu1010 == 3) { + } else if (emu->card_capabilities->emu_model == + EMU_MODEL_EMU1616) { if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) { - return err; + continue; } } @@ -745,8 +752,7 @@ int emu1010_firmware_thread(void *data) { if ((reg & 0x1f) != 0x15) { /* FPGA failed to be programmed */ snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware file failed, reg=0x%x\n", reg); - return 0; - return -ENODEV; + continue; } snd_printk(KERN_INFO "emu1010: Audio Dock Firmware loaded\n"); snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp ); @@ -757,9 +763,9 @@ int emu1010_firmware_thread(void *data) { msleep(10); /* Unmute all. Default is muted after a firmware load */ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE ); - break; } } + snd_printk(KERN_INFO "emu1010: firmware thread stopping\n"); return 0; } @@ -800,6 +806,7 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu) int tmp,tmp2; int reg; int err; + const char *filename = NULL; snd_printk(KERN_INFO "emu1010: Special config.\n"); /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave, @@ -841,21 +848,31 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu) return -ENODEV; } snd_printk(KERN_INFO "emu1010: EMU_HANA_ID=0x%x\n",reg); - if (emu->card_capabilities->emu1010 == 1) { - if ((err = snd_emu1010_load_firmware(emu, HANA_FILENAME)) != 0) { - snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file %s failed\n", HANA_FILENAME); - return err; - } - } else if (emu->card_capabilities->emu1010 == 2) { - if ((err = snd_emu1010_load_firmware(emu, EMU1010B_FILENAME)) != 0) { - snd_printk(KERN_INFO "emu1010: Loading Firmware file %s failed\n", EMU1010B_FILENAME); - return err; - } - } else if (emu->card_capabilities->emu1010 == 3) { - if ((err = snd_emu1010_load_firmware(emu, EMU1010_NOTEBOOK_FILENAME)) != 0) { - snd_printk(KERN_INFO "emu1010: Loading Firmware file %s failed\n", EMU1010_NOTEBOOK_FILENAME); - return err; - } + switch (emu->card_capabilities->emu_model) { + case EMU_MODEL_EMU1010: + filename = HANA_FILENAME; + break; + case EMU_MODEL_EMU1010B: + filename = EMU1010B_FILENAME; + break; + case EMU_MODEL_EMU1616: + filename = EMU1010_NOTEBOOK_FILENAME; + break; + case EMU_MODEL_EMU0404: + filename = EMU0404_FILENAME; + break; + default: + filename = NULL; + return -ENODEV; + break; + } + snd_printk(KERN_INFO "emu1010: filename %s testing\n", filename); + err = snd_emu1010_load_firmware(emu, filename); + if (err != 0) { + snd_printk( + KERN_INFO "emu1010: Loading Firmware file %s failed\n", + filename); + return err; } /* ID, should read & 0x7f = 0x55 when FPGA programmed. */ @@ -1074,10 +1091,12 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu) snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10 ); /* SPDIF Format spdif (or 0x11 for aes/ebu) */ /* Start Micro/Audio Dock firmware loader thread */ - emu->emu1010.firmware_thread = kthread_create(&emu1010_firmware_thread, - emu, - "emu1010_firmware"); - wake_up_process(emu->emu1010.firmware_thread); + if (!emu->emu1010.firmware_thread) { + emu->emu1010.firmware_thread = + kthread_create(emu1010_firmware_thread, emu, + "emu1010_firmware"); + wake_up_process(emu->emu1010.firmware_thread); + } #if 0 snd_emu1010_fpga_link_dst_src_write(emu, @@ -1090,79 +1109,114 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu) EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 3); /* ALICE2 bus 0xb3 */ #endif /* Default outputs */ - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_DOCK_DAC1_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */ - emu->emu1010.output_source[0] = 21; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_DOCK_DAC1_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); - emu->emu1010.output_source[1] = 22; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_DOCK_DAC2_LEFT1, EMU_SRC_ALICE_EMU32A + 2); - emu->emu1010.output_source[2] = 23; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_DOCK_DAC2_RIGHT1, EMU_SRC_ALICE_EMU32A + 3); - emu->emu1010.output_source[3] = 24; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_DOCK_DAC3_LEFT1, EMU_SRC_ALICE_EMU32A + 4); - emu->emu1010.output_source[4] = 25; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_DOCK_DAC3_RIGHT1, EMU_SRC_ALICE_EMU32A + 5); - emu->emu1010.output_source[5] = 26; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_DOCK_DAC4_LEFT1, EMU_SRC_ALICE_EMU32A + 6); - emu->emu1010.output_source[6] = 27; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_DOCK_DAC4_RIGHT1, EMU_SRC_ALICE_EMU32A + 7); - emu->emu1010.output_source[7] = 28; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_DOCK_PHONES_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */ - emu->emu1010.output_source[8] = 21; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_DOCK_PHONES_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); - emu->emu1010.output_source[9] = 22; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_DOCK_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */ - emu->emu1010.output_source[10] = 21; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_DOCK_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); - emu->emu1010.output_source[11] = 22; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_HANA_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */ - emu->emu1010.output_source[12] = 21; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); - emu->emu1010.output_source[13] = 22; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */ - emu->emu1010.output_source[14] = 21; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_HAMOA_DAC_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); - emu->emu1010.output_source[15] = 22; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_HANA_ADAT, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */ - emu->emu1010.output_source[16] = 21; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_HANA_ADAT + 1, EMU_SRC_ALICE_EMU32A + 1); - emu->emu1010.output_source[17] = 22; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_HANA_ADAT + 2, EMU_SRC_ALICE_EMU32A + 2); - emu->emu1010.output_source[18] = 23; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_HANA_ADAT + 3, EMU_SRC_ALICE_EMU32A + 3); - emu->emu1010.output_source[19] = 24; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_HANA_ADAT + 4, EMU_SRC_ALICE_EMU32A + 4); - emu->emu1010.output_source[20] = 25; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_HANA_ADAT + 5, EMU_SRC_ALICE_EMU32A + 5); - emu->emu1010.output_source[21] = 26; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_HANA_ADAT + 6, EMU_SRC_ALICE_EMU32A + 6); - emu->emu1010.output_source[22] = 27; - snd_emu1010_fpga_link_dst_src_write(emu, - EMU_DST_HANA_ADAT + 7, EMU_SRC_ALICE_EMU32A + 7); - emu->emu1010.output_source[23] = 28; - + if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) { + /* 1616(M) cardbus default outputs */ + /* ALICE2 bus 0xa0 */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC1_LEFT1, EMU_SRC_ALICE_EMU32A + 0); + emu->emu1010.output_source[0] = 17; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC1_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[1] = 18; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC2_LEFT1, EMU_SRC_ALICE_EMU32A + 2); + emu->emu1010.output_source[2] = 19; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC2_RIGHT1, EMU_SRC_ALICE_EMU32A + 3); + emu->emu1010.output_source[3] = 20; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC3_LEFT1, EMU_SRC_ALICE_EMU32A + 4); + emu->emu1010.output_source[4] = 21; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC3_RIGHT1, EMU_SRC_ALICE_EMU32A + 5); + emu->emu1010.output_source[5] = 22; + /* ALICE2 bus 0xa0 */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_MANA_DAC_LEFT, EMU_SRC_ALICE_EMU32A + 0); + emu->emu1010.output_source[16] = 17; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_MANA_DAC_RIGHT, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[17] = 18; + } else { + /* ALICE2 bus 0xa0 */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC1_LEFT1, EMU_SRC_ALICE_EMU32A + 0); + emu->emu1010.output_source[0] = 21; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC1_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[1] = 22; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC2_LEFT1, EMU_SRC_ALICE_EMU32A + 2); + emu->emu1010.output_source[2] = 23; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC2_RIGHT1, EMU_SRC_ALICE_EMU32A + 3); + emu->emu1010.output_source[3] = 24; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC3_LEFT1, EMU_SRC_ALICE_EMU32A + 4); + emu->emu1010.output_source[4] = 25; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC3_RIGHT1, EMU_SRC_ALICE_EMU32A + 5); + emu->emu1010.output_source[5] = 26; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC4_LEFT1, EMU_SRC_ALICE_EMU32A + 6); + emu->emu1010.output_source[6] = 27; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC4_RIGHT1, EMU_SRC_ALICE_EMU32A + 7); + emu->emu1010.output_source[7] = 28; + /* ALICE2 bus 0xa0 */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_PHONES_LEFT1, EMU_SRC_ALICE_EMU32A + 0); + emu->emu1010.output_source[8] = 21; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_PHONES_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[9] = 22; + /* ALICE2 bus 0xa0 */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0); + emu->emu1010.output_source[10] = 21; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[11] = 22; + /* ALICE2 bus 0xa0 */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0); + emu->emu1010.output_source[12] = 21; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[13] = 22; + /* ALICE2 bus 0xa0 */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32A + 0); + emu->emu1010.output_source[14] = 21; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HAMOA_DAC_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[15] = 22; + /* ALICE2 bus 0xa0 */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT, EMU_SRC_ALICE_EMU32A + 0); + emu->emu1010.output_source[16] = 21; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 1, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[17] = 22; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 2, EMU_SRC_ALICE_EMU32A + 2); + emu->emu1010.output_source[18] = 23; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 3, EMU_SRC_ALICE_EMU32A + 3); + emu->emu1010.output_source[19] = 24; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 4, EMU_SRC_ALICE_EMU32A + 4); + emu->emu1010.output_source[20] = 25; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 5, EMU_SRC_ALICE_EMU32A + 5); + emu->emu1010.output_source[21] = 26; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 6, EMU_SRC_ALICE_EMU32A + 6); + emu->emu1010.output_source[22] = 27; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 7, EMU_SRC_ALICE_EMU32A + 7); + emu->emu1010.output_source[23] = 28; + } /* TEMP: Select SPDIF in/out */ //snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x0); /* Output spdif */ @@ -1202,11 +1256,12 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu) } snd_emu10k1_free_efx(emu); } - if (emu->card_capabilities->emu1010) { + if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1010) { /* Disable 48Volt power to Audio Dock */ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0 ); - kthread_stop(emu->emu1010.firmware_thread); } + if (emu->emu1010.firmware_thread) + kthread_stop(emu->emu1010.firmware_thread); if (emu->memhdr) snd_util_memhdr_free(emu->memhdr); if (emu->silent_page.area) @@ -1338,6 +1393,7 @@ static struct snd_emu_chip_details emu_chip_details[] = { .spi_dac = 1, .i2c_adc = 1, .spk71 = 1} , + /* Tested by James@superbug.co.uk 4th Nov 2007. */ {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x42011102, .driver = "Audigy2", .name = "E-mu 1010 Notebook [MAEM8950]", .id = "EMU1010", @@ -1345,28 +1401,46 @@ static struct snd_emu_chip_details emu_chip_details[] = { .ca0108_chip = 1, .ca_cardbus_chip = 1, .spk71 = 1 , - .emu1010 = 3} , + .emu_model = EMU_MODEL_EMU1616}, + /* Tested by James@superbug.co.uk 4th Nov 2007. */ {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40041102, .driver = "Audigy2", .name = "E-mu 1010b PCI [MAEM????]", .id = "EMU1010", .emu10k2_chip = 1, .ca0108_chip = 1, - .spk71 = 1 , - .emu1010 = 2} , + .spk71 = 1, + .emu_model = EMU_MODEL_EMU1010B}, + /* Tested by James@superbug.co.uk 8th July 2005. */ + {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40011102, + .driver = "Audigy2", .name = "E-mu 1010 [4001]", + .id = "EMU1010", + .emu10k2_chip = 1, + .ca0102_chip = 1, + .spk71 = 1, + .emu_model = EMU_MODEL_EMU1010}, /* Emu 1010 */ + /* EMU0404b */ + {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40021102, + .driver = "Audigy2", .name = "E-mu 0404b [4002]", + .id = "EMU0404", + .emu10k2_chip = 1, + .ca0108_chip = 1, + .spk71 = 1, + .emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 */ + /* Tested by James@superbug.co.uk 20-3-2007. */ + {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40021102, + .driver = "Audigy2", .name = "E-mu 0404 [4002]", + .id = "EMU0404", + .emu10k2_chip = 1, + .ca0102_chip = 1, + .spk71 = 1, + .emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 */ + /* Audigy4 (Not PRO) SB0610 */ {.vendor = 0x1102, .device = 0x0008, .driver = "Audigy2", .name = "Audigy 2 Value [Unknown]", .id = "Audigy2", .emu10k2_chip = 1, .ca0108_chip = 1, .ac97_chip = 1} , - /* Tested by James@superbug.co.uk 8th July 2005. No sound available yet. */ - {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40011102, - .driver = "Audigy2", .name = "E-mu 1010 [4001]", - .id = "EMU1010", - .emu10k2_chip = 1, - .ca0102_chip = 1, - .spk71 = 1, - .emu1010 = 1} , /* Tested by James@superbug.co.uk 3rd July 2005 */ {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20071102, .driver = "Audigy2", .name = "Audigy 4 PRO [SB0380]", @@ -1654,6 +1728,8 @@ int __devinit snd_emu10k1_create(struct snd_card *card, emu->card = card; spin_lock_init(&emu->reg_lock); spin_lock_init(&emu->emu_lock); + spin_lock_init(&emu->spi_lock); + spin_lock_init(&emu->i2c_lock); spin_lock_init(&emu->voice_lock); spin_lock_init(&emu->synth_lock); spin_lock_init(&emu->memblk_lock); @@ -1794,7 +1870,7 @@ int __devinit snd_emu10k1_create(struct snd_card *card, if (emu->card_capabilities->ecard) { if ((err = snd_emu10k1_ecard_init(emu)) < 0) goto error; - } else if (emu->card_capabilities->emu1010) { + } else if (emu->card_capabilities->emu_model) { if ((err = snd_emu10k1_emu1010_init(emu)) < 0) { snd_emu10k1_free(emu); return err; @@ -1943,7 +2019,7 @@ void snd_emu10k1_resume_init(struct snd_emu10k1 *emu) snd_emu10k1_cardbus_init(emu); if (emu->card_capabilities->ecard) snd_emu10k1_ecard_init(emu); - else if (emu->card_capabilities->emu1010) + else if (emu->card_capabilities->emu_model) snd_emu10k1_emu1010_init(emu); else snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE); diff --git a/sound/pci/emu10k1/emu10k1_synth.c b/sound/pci/emu10k1/emu10k1_synth.c index 204995a..ad7b714 100644 --- a/sound/pci/emu10k1/emu10k1_synth.c +++ b/sound/pci/emu10k1/emu10k1_synth.c @@ -30,7 +30,7 @@ MODULE_LICENSE("GPL"); */ static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev) { - struct snd_emux *emu; + struct snd_emux *emux; struct snd_emu10k1 *hw; struct snd_emu10k1_synth_arg *arg; unsigned long flags; @@ -46,53 +46,56 @@ static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev) else if (arg->max_voices > 64) arg->max_voices = 64; - if (snd_emux_new(&emu) < 0) + if (snd_emux_new(&emux) < 0) return -ENOMEM; - snd_emu10k1_ops_setup(emu); - emu->hw = hw = arg->hwptr; - emu->max_voices = arg->max_voices; - emu->num_ports = arg->seq_ports; - emu->pitch_shift = -501; - emu->memhdr = hw->memhdr; - emu->midi_ports = arg->seq_ports < 2 ? arg->seq_ports : 2; /* maximum two ports */ - emu->midi_devidx = hw->audigy ? 2 : 1; /* audigy has two external midis */ - emu->linear_panning = 0; - emu->hwdep_idx = 2; /* FIXED */ - - if (snd_emux_register(emu, dev->card, arg->index, "Emu10k1") < 0) { - snd_emux_free(emu); + snd_emu10k1_ops_setup(emux); + hw = arg->hwptr; + emux->hw = hw; + emux->max_voices = arg->max_voices; + emux->num_ports = arg->seq_ports; + emux->pitch_shift = -501; + emux->memhdr = hw->memhdr; + /* maximum two ports */ + emux->midi_ports = arg->seq_ports < 2 ? arg->seq_ports : 2; + /* audigy has two external midis */ + emux->midi_devidx = hw->audigy ? 2 : 1; + emux->linear_panning = 0; + emux->hwdep_idx = 2; /* FIXED */ + + if (snd_emux_register(emux, dev->card, arg->index, "Emu10k1") < 0) { + snd_emux_free(emux); return -ENOMEM; } spin_lock_irqsave(&hw->voice_lock, flags); - hw->synth = emu; + hw->synth = emux; hw->get_synth_voice = snd_emu10k1_synth_get_voice; spin_unlock_irqrestore(&hw->voice_lock, flags); - dev->driver_data = emu; + dev->driver_data = emux; return 0; } static int snd_emu10k1_synth_delete_device(struct snd_seq_device *dev) { - struct snd_emux *emu; + struct snd_emux *emux; struct snd_emu10k1 *hw; unsigned long flags; if (dev->driver_data == NULL) return 0; /* not registered actually */ - emu = dev->driver_data; + emux = dev->driver_data; - hw = emu->hw; + hw = emux->hw; spin_lock_irqsave(&hw->voice_lock, flags); hw->synth = NULL; hw->get_synth_voice = NULL; spin_unlock_irqrestore(&hw->voice_lock, flags); - snd_emux_free(emu); + snd_emux_free(emux); return 0; } diff --git a/sound/pci/emu10k1/emu10k1_synth_local.h b/sound/pci/emu10k1/emu10k1_synth_local.h index 308ddc8..25f328f 100644 --- a/sound/pci/emu10k1/emu10k1_synth_local.h +++ b/sound/pci/emu10k1/emu10k1_synth_local.h @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/time.h> #include <sound/core.h> #include <sound/emu10k1_synth.h> diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index 1ec7eba..5512abd 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -29,7 +29,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/pci.h> @@ -1583,6 +1582,8 @@ static int __devinit snd_emu10k1x_probe(struct pci_dev *pci, sprintf(card->longname, "%s at 0x%lx irq %i", card->shortname, chip->port, chip->irq); + snd_card_set_dev(card, &pci->dev); + if ((err = snd_card_register(card)) < 0) { snd_card_free(card); return err; diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index 9bf1cd5..71dc4c8 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -28,7 +28,6 @@ * */ -#include <sound/driver.h> #include <linux/pci.h> #include <linux/capability.h> #include <linux/delay.h> @@ -666,7 +665,7 @@ static unsigned int *copy_tlv(const unsigned int __user *_tlv) return NULL; if (data[1] >= MAX_TLV_SIZE) return NULL; - tlv = kmalloc(data[1] * 4 + sizeof(data), GFP_KERNEL); + tlv = kmalloc(data[1] + sizeof(data), GFP_KERNEL); if (!tlv) return NULL; memcpy(tlv, data, sizeof(data)); @@ -1262,7 +1261,7 @@ static int __devinit _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu) A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) /* emu1212 DSP 0 and DSP 1 Capture */ - if (emu->card_capabilities->emu1010) { + if (emu->card_capabilities->emu_model) { if (emu->card_capabilities->ca0108_chip) { /* Note:JCD:No longer bit shift lower 16bits to upper 16bits of 32bit value. */ A_OP(icode, &ptr, iMACINT0, A_GPR(tmp), A_C_00000000, A3_EMU32IN(0x0), A_C_00000001); @@ -1516,7 +1515,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) /* digital outputs */ /* A_PUT_STEREO_OUTPUT(A_EXTOUT_FRONT_L, A_EXTOUT_FRONT_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS); */ - if (emu->card_capabilities->emu1010) { + if (emu->card_capabilities->emu_model) { /* EMU1010 Outputs from PCM Front, Rear, Center, LFE, Side */ snd_printk("EMU outputs on\n"); for (z = 0; z < 8; z++) { @@ -1564,7 +1563,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_R, capture+1); #endif - if (emu->card_capabilities->emu1010) { + if (emu->card_capabilities->emu_model) { if (emu->card_capabilities->ca0108_chip) { snd_printk("EMU2 inputs on\n"); for (z = 0; z < 0x10; z++) { diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index ccacd7b..fd22120 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -30,7 +30,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <linux/init.h> #include <sound/core.h> @@ -140,6 +139,61 @@ static char *emu1010_src_texts[] = { "DSP 31", }; +/* 1616(m) cardbus */ + +static char *emu1616_src_texts[] = { + "Silence", + "Dock Mic A", + "Dock Mic B", + "Dock ADC1 Left", + "Dock ADC1 Right", + "Dock ADC2 Left", + "Dock ADC2 Right", + "Dock SPDIF Left", + "Dock SPDIF Right", + "ADAT 0", + "ADAT 1", + "ADAT 2", + "ADAT 3", + "ADAT 4", + "ADAT 5", + "ADAT 6", + "ADAT 7", + "DSP 0", + "DSP 1", + "DSP 2", + "DSP 3", + "DSP 4", + "DSP 5", + "DSP 6", + "DSP 7", + "DSP 8", + "DSP 9", + "DSP 10", + "DSP 11", + "DSP 12", + "DSP 13", + "DSP 14", + "DSP 15", + "DSP 16", + "DSP 17", + "DSP 18", + "DSP 19", + "DSP 20", + "DSP 21", + "DSP 22", + "DSP 23", + "DSP 24", + "DSP 25", + "DSP 26", + "DSP 27", + "DSP 28", + "DSP 29", + "DSP 30", + "DSP 31", +}; + + /* * List of data sources available for each destination */ @@ -199,6 +253,59 @@ static unsigned int emu1010_src_regs[] = { EMU_SRC_ALICE_EMU32B+0xf, /* 52 */ }; +/* 1616(m) cardbus */ +static unsigned int emu1616_src_regs[] = { + EMU_SRC_SILENCE, + EMU_SRC_DOCK_MIC_A1, + EMU_SRC_DOCK_MIC_B1, + EMU_SRC_DOCK_ADC1_LEFT1, + EMU_SRC_DOCK_ADC1_RIGHT1, + EMU_SRC_DOCK_ADC2_LEFT1, + EMU_SRC_DOCK_ADC2_RIGHT1, + EMU_SRC_MDOCK_SPDIF_LEFT1, + EMU_SRC_MDOCK_SPDIF_RIGHT1, + EMU_SRC_MDOCK_ADAT, + EMU_SRC_MDOCK_ADAT+1, + EMU_SRC_MDOCK_ADAT+2, + EMU_SRC_MDOCK_ADAT+3, + EMU_SRC_MDOCK_ADAT+4, + EMU_SRC_MDOCK_ADAT+5, + EMU_SRC_MDOCK_ADAT+6, + EMU_SRC_MDOCK_ADAT+7, + EMU_SRC_ALICE_EMU32A, + EMU_SRC_ALICE_EMU32A+1, + EMU_SRC_ALICE_EMU32A+2, + EMU_SRC_ALICE_EMU32A+3, + EMU_SRC_ALICE_EMU32A+4, + EMU_SRC_ALICE_EMU32A+5, + EMU_SRC_ALICE_EMU32A+6, + EMU_SRC_ALICE_EMU32A+7, + EMU_SRC_ALICE_EMU32A+8, + EMU_SRC_ALICE_EMU32A+9, + EMU_SRC_ALICE_EMU32A+0xa, + EMU_SRC_ALICE_EMU32A+0xb, + EMU_SRC_ALICE_EMU32A+0xc, + EMU_SRC_ALICE_EMU32A+0xd, + EMU_SRC_ALICE_EMU32A+0xe, + EMU_SRC_ALICE_EMU32A+0xf, + EMU_SRC_ALICE_EMU32B, + EMU_SRC_ALICE_EMU32B+1, + EMU_SRC_ALICE_EMU32B+2, + EMU_SRC_ALICE_EMU32B+3, + EMU_SRC_ALICE_EMU32B+4, + EMU_SRC_ALICE_EMU32B+5, + EMU_SRC_ALICE_EMU32B+6, + EMU_SRC_ALICE_EMU32B+7, + EMU_SRC_ALICE_EMU32B+8, + EMU_SRC_ALICE_EMU32B+9, + EMU_SRC_ALICE_EMU32B+0xa, + EMU_SRC_ALICE_EMU32B+0xb, + EMU_SRC_ALICE_EMU32B+0xc, + EMU_SRC_ALICE_EMU32B+0xd, + EMU_SRC_ALICE_EMU32B+0xe, + EMU_SRC_ALICE_EMU32B+0xf, +}; + /* * Data destinations - physical EMU outputs. * Each destination has an enum mixer control to choose a data source @@ -230,6 +337,28 @@ static unsigned int emu1010_output_dst[] = { EMU_DST_HANA_ADAT+7, /* 23 */ }; +/* 1616(m) cardbus */ +static unsigned int emu1616_output_dst[] = { + EMU_DST_DOCK_DAC1_LEFT1, + EMU_DST_DOCK_DAC1_RIGHT1, + EMU_DST_DOCK_DAC2_LEFT1, + EMU_DST_DOCK_DAC2_RIGHT1, + EMU_DST_DOCK_DAC3_LEFT1, + EMU_DST_DOCK_DAC3_RIGHT1, + EMU_DST_MDOCK_SPDIF_LEFT1, + EMU_DST_MDOCK_SPDIF_RIGHT1, + EMU_DST_MDOCK_ADAT, + EMU_DST_MDOCK_ADAT+1, + EMU_DST_MDOCK_ADAT+2, + EMU_DST_MDOCK_ADAT+3, + EMU_DST_MDOCK_ADAT+4, + EMU_DST_MDOCK_ADAT+5, + EMU_DST_MDOCK_ADAT+6, + EMU_DST_MDOCK_ADAT+7, + EMU_DST_MANA_DAC_LEFT, + EMU_DST_MANA_DAC_RIGHT, +}; + /* * Data destinations - HANA outputs going to Alice2 (audigy) for * capture (EMU32 + I2S links) @@ -260,14 +389,26 @@ static unsigned int emu1010_input_dst[] = { EMU_DST_ALICE_I2S2_RIGHT, }; -static int snd_emu1010_input_output_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +static int snd_emu1010_input_output_source_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { + struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); + char **items; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; - uinfo->value.enumerated.items = 53; + if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) { + uinfo->value.enumerated.items = 49; + items = emu1616_src_texts; + } else { + uinfo->value.enumerated.items = 53; + items = emu1010_src_texts; + } if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, emu1010_src_texts[uinfo->value.enumerated.item]); + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, + items[uinfo->value.enumerated.item]); return 0; } @@ -279,7 +420,9 @@ static int snd_emu1010_output_source_get(struct snd_kcontrol *kcontrol, channel = (kcontrol->private_value) & 0xff; /* Limit: emu1010_output_dst, emu->emu1010.output_source */ - if (channel >= 24) + if (channel >= 24 || + (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 && + channel >= 18)) return -EINVAL; ucontrol->value.enumerated.item[0] = emu->emu1010.output_source[channel]; return 0; @@ -289,24 +432,30 @@ static int snd_emu1010_output_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); - int change = 0; unsigned int val; unsigned int channel; val = ucontrol->value.enumerated.item[0]; - if (val >= 53) + if (val >= 53 || + (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 && + val >= 49)) return -EINVAL; channel = (kcontrol->private_value) & 0xff; /* Limit: emu1010_output_dst, emu->emu1010.output_source */ - if (channel >= 24) + if (channel >= 24 || + (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 && + channel >= 18)) return -EINVAL; - if (emu->emu1010.output_source[channel] != val) { - emu->emu1010.output_source[channel] = val; - change = 1; + if (emu->emu1010.output_source[channel] == val) + return 0; + emu->emu1010.output_source[channel] = val; + if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) + snd_emu1010_fpga_link_dst_src_write(emu, + emu1616_output_dst[channel], emu1616_src_regs[val]); + else snd_emu1010_fpga_link_dst_src_write(emu, emu1010_output_dst[channel], emu1010_src_regs[val]); - } - return change; + return 1; } static int snd_emu1010_input_source_get(struct snd_kcontrol *kcontrol, @@ -327,24 +476,28 @@ static int snd_emu1010_input_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); - int change = 0; unsigned int val; unsigned int channel; val = ucontrol->value.enumerated.item[0]; - if (val >= 53) + if (val >= 53 || + (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 && + val >= 49)) return -EINVAL; channel = (kcontrol->private_value) & 0xff; /* Limit: emu1010_input_dst, emu->emu1010.input_source */ if (channel >= 22) return -EINVAL; - if (emu->emu1010.input_source[channel] != val) { - emu->emu1010.input_source[channel] = val; - change = 1; + if (emu->emu1010.input_source[channel] == val) + return 0; + emu->emu1010.input_source[channel] = val; + if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) + snd_emu1010_fpga_link_dst_src_write(emu, + emu1010_input_dst[channel], emu1616_src_regs[val]); + else snd_emu1010_fpga_link_dst_src_write(emu, emu1010_input_dst[channel], emu1010_src_regs[val]); - } - return change; + return 1; } #define EMU1010_SOURCE_OUTPUT(xname,chid) \ @@ -384,6 +537,30 @@ static struct snd_kcontrol_new snd_emu1010_output_enum_ctls[] __devinitdata = { EMU1010_SOURCE_OUTPUT("1010 ADAT 7 Playback Enum", 0x17), }; + +/* 1616(m) cardbus */ +static struct snd_kcontrol_new snd_emu1616_output_enum_ctls[] __devinitdata = { + EMU1010_SOURCE_OUTPUT("Dock DAC1 Left Playback Enum", 0), + EMU1010_SOURCE_OUTPUT("Dock DAC1 Right Playback Enum", 1), + EMU1010_SOURCE_OUTPUT("Dock DAC2 Left Playback Enum", 2), + EMU1010_SOURCE_OUTPUT("Dock DAC2 Right Playback Enum", 3), + EMU1010_SOURCE_OUTPUT("Dock DAC3 Left Playback Enum", 4), + EMU1010_SOURCE_OUTPUT("Dock DAC3 Right Playback Enum", 5), + EMU1010_SOURCE_OUTPUT("Dock SPDIF Left Playback Enum", 6), + EMU1010_SOURCE_OUTPUT("Dock SPDIF Right Playback Enum", 7), + EMU1010_SOURCE_OUTPUT("Dock ADAT 0 Playback Enum", 8), + EMU1010_SOURCE_OUTPUT("Dock ADAT 1 Playback Enum", 9), + EMU1010_SOURCE_OUTPUT("Dock ADAT 2 Playback Enum", 0xa), + EMU1010_SOURCE_OUTPUT("Dock ADAT 3 Playback Enum", 0xb), + EMU1010_SOURCE_OUTPUT("Dock ADAT 4 Playback Enum", 0xc), + EMU1010_SOURCE_OUTPUT("Dock ADAT 5 Playback Enum", 0xd), + EMU1010_SOURCE_OUTPUT("Dock ADAT 6 Playback Enum", 0xe), + EMU1010_SOURCE_OUTPUT("Dock ADAT 7 Playback Enum", 0xf), + EMU1010_SOURCE_OUTPUT("Mana DAC Left Playback Enum", 0x10), + EMU1010_SOURCE_OUTPUT("Mana DAC Right Playback Enum", 0x11), +}; + + #define EMU1010_SOURCE_INPUT(xname,chid) \ { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ @@ -1793,7 +1970,7 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, return err; } - if ( emu->card_capabilities->emu1010) { + if (emu->card_capabilities->emu_model) { ; /* Disable the snd_audigy_spdif_shared_spdif */ } else if (emu->audigy) { if ((kctl = snd_ctl_new1(&snd_audigy_shared_spdif, emu)) == NULL) @@ -1818,30 +1995,73 @@ int __devinit snd_emu10k1_mixer(struct snd_emu10k1 *emu, return err; } - if ( emu->card_capabilities->emu1010) { + if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) { + /* 1616(m) cardbus */ + int i; + + for (i = 0; i < ARRAY_SIZE(snd_emu1616_output_enum_ctls); i++) { + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1616_output_enum_ctls[i], + emu)); + if (err < 0) + return err; + } + for (i = 0; i < ARRAY_SIZE(snd_emu1010_input_enum_ctls); i++) { + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1010_input_enum_ctls[i], + emu)); + if (err < 0) + return err; + } + for (i = 0; i < ARRAY_SIZE(snd_emu1010_adc_pads) - 2; i++) { + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1010_adc_pads[i], emu)); + if (err < 0) + return err; + } + for (i = 0; i < ARRAY_SIZE(snd_emu1010_dac_pads) - 2; i++) { + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1010_dac_pads[i], emu)); + if (err < 0) + return err; + } + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1010_internal_clock, emu)); + if (err < 0) + return err; + + } else if (emu->card_capabilities->emu_model) { + /* all other e-mu cards for now */ int i; for (i = 0; i < ARRAY_SIZE(snd_emu1010_output_enum_ctls); i++) { - err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_output_enum_ctls[i], emu)); + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1010_output_enum_ctls[i], + emu)); if (err < 0) return err; } for (i = 0; i < ARRAY_SIZE(snd_emu1010_input_enum_ctls); i++) { - err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_input_enum_ctls[i], emu)); + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1010_input_enum_ctls[i], + emu)); if (err < 0) return err; } for (i = 0; i < ARRAY_SIZE(snd_emu1010_adc_pads); i++) { - err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_adc_pads[i], emu)); + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1010_adc_pads[i], emu)); if (err < 0) return err; } for (i = 0; i < ARRAY_SIZE(snd_emu1010_dac_pads); i++) { - err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_dac_pads[i], emu)); + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1010_dac_pads[i], emu)); if (err < 0) return err; } - err = snd_ctl_add(card, snd_ctl_new1(&snd_emu1010_internal_clock, emu)); + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1010_internal_clock, emu)); if (err < 0) return err; } diff --git a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c index 04c7cf7..c4d76d1 100644 --- a/sound/pci/emu10k1/emumpu401.c +++ b/sound/pci/emu10k1/emumpu401.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <linux/init.h> #include <sound/core.h> diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index 5ce5bef..cf9276d 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -26,7 +26,6 @@ * */ -#include <sound/driver.h> #include <linux/pci.h> #include <linux/delay.h> #include <linux/slab.h> @@ -358,7 +357,7 @@ static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu, snd_emu10k1_ptr_write(emu, PTRX, voice, (send_amount[0] << 8) | send_amount[1]); snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24)); snd_emu10k1_ptr_write(emu, PSST, voice, start_addr | (send_amount[2] << 24)); - if (emu->card_capabilities->emu1010) + if (emu->card_capabilities->emu_model) pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */ else pitch_target = emu10k1_calc_pitch_target(runtime->rate); @@ -701,7 +700,7 @@ static void snd_emu10k1_playback_trigger_voice(struct snd_emu10k1 *emu, struct s voice = evoice->number; pitch = snd_emu10k1_rate_to_pitch(runtime->rate) >> 8; - if (emu->card_capabilities->emu1010) + if (emu->card_capabilities->emu_model) pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */ else pitch_target = emu10k1_calc_pitch_target(runtime->rate); @@ -1232,7 +1231,7 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream) runtime->hw.rates = SNDRV_PCM_RATE_48000; runtime->hw.rate_min = runtime->hw.rate_max = 48000; spin_lock_irq(&emu->reg_lock); - if (emu->card_capabilities->emu1010) { + if (emu->card_capabilities->emu_model) { /* Nb. of channels has been increased to 16 */ /* TODO * SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE @@ -1791,7 +1790,7 @@ int __devinit snd_emu10k1_pcm_efx(struct snd_emu10k1 * emu, int device, struct s /* emu->efx_voices_mask[0] = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A; */ if (emu->audigy) { emu->efx_voices_mask[0] = 0; - if (emu->card_capabilities->emu1010) + if (emu->card_capabilities->emu_model) /* Pavel Hofman - 32 voices will be used for * capture (write mode) - * each bit = corresponding voice diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c index c3fb10e..f3caa3f 100644 --- a/sound/pci/emu10k1/emuproc.c +++ b/sound/pci/emu10k1/emuproc.c @@ -28,7 +28,6 @@ * */ -#include <sound/driver.h> #include <linux/slab.h> #include <linux/init.h> #include <sound/core.h> @@ -245,7 +244,7 @@ static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry, unsigned long flags; u32 rate; - if (emu->card_capabilities->emu1010) { + if (emu->card_capabilities->emu_model) { spin_lock_irqsave(&emu->emu_lock, flags); snd_emu1010_fpga_read(emu, 0x38, &value); spin_unlock_irqrestore(&emu->emu_lock, flags); @@ -585,7 +584,7 @@ int __devinit snd_emu10k1_proc_init(struct snd_emu10k1 * emu) { struct snd_info_entry *entry; #ifdef CONFIG_SND_DEBUG - if (emu->card_capabilities->emu1010) { + if (emu->card_capabilities->emu_model) { if (! snd_card_proc_new(emu->card, "emu1010_regs", &entry)) snd_info_set_text_ops(entry, emu, snd_emu_proc_emu1010_reg_read); } diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c index 6702c15..b5a802b 100644 --- a/sound/pci/emu10k1/io.c +++ b/sound/pci/emu10k1/io.c @@ -25,7 +25,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <sound/core.h> #include <sound/emu10k1.h> @@ -71,6 +70,11 @@ void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned i unsigned long flags; unsigned int mask; + if (!emu) { + snd_printk(KERN_ERR "ptr_write: emu is null!\n"); + dump_stack(); + return; + } mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK; regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK); @@ -135,15 +139,23 @@ int snd_emu10k1_spi_write(struct snd_emu10k1 * emu, unsigned int reset, set; unsigned int reg, tmp; int n, result; + int err = 0; + + /* This function is not re-entrant, so protect against it. */ + spin_lock(&emu->spi_lock); if (emu->card_capabilities->ca0108_chip) reg = 0x3c; /* PTR20, reg 0x3c */ else { /* For other chip types the SPI register * is currently unknown. */ - return 1; + err = 1; + goto spi_write_exit; + } + if (data > 0xffff) { + /* Only 16bit values allowed */ + err = 1; + goto spi_write_exit; } - if (data > 0xffff) /* Only 16bit values allowed */ - return 1; tmp = snd_emu10k1_ptr20_read(emu, reg, 0); reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */ @@ -161,11 +173,17 @@ int snd_emu10k1_spi_write(struct snd_emu10k1 * emu, break; } } - if (result) /* Timed out */ - return 1; + if (result) { + /* Timed out */ + err = 1; + goto spi_write_exit; + } snd_emu10k1_ptr20_write(emu, reg, 0, reset | data); tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* Write post */ - return 0; + err = 0; +spi_write_exit: + spin_unlock(&emu->spi_lock); + return err; } /* The ADC does not support i2c read, so only write is implemented */ @@ -177,15 +195,17 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu, int timeout = 0; int status; int retry; + int err = 0; + if ((reg > 0x7f) || (value > 0x1ff)) { snd_printk(KERN_ERR "i2c_write: invalid values.\n"); return -EINVAL; } + /* This function is not re-entrant, so protect against it. */ + spin_lock(&emu->i2c_lock); + tmp = reg << 25 | value << 16; - // snd_printk("I2C-write:reg=0x%x, value=0x%x\n", reg, value); - /* Not sure what this I2C channel controls. */ - /* snd_emu10k1_ptr_write(emu, P17V_I2C_0, 0, tmp); */ /* This controls the I2C connected to the WM8775 ADC Codec */ snd_emu10k1_ptr20_write(emu, P17V_I2C_1, 0, tmp); @@ -193,17 +213,14 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu, for (retry = 0; retry < 10; retry++) { /* Send the data to i2c */ - //tmp = snd_emu10k1_ptr_read(emu, P17V_I2C_ADDR, 0); - //tmp = tmp & ~(I2C_A_ADC_READ|I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD_MASK); tmp = 0; tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD); snd_emu10k1_ptr20_write(emu, P17V_I2C_ADDR, 0, tmp); /* Wait till the transaction ends */ while (1) { - udelay(10); + mdelay(1); status = snd_emu10k1_ptr20_read(emu, P17V_I2C_ADDR, 0); - // snd_printk("I2C:status=0x%x\n", status); timeout++; if ((status & I2C_A_ADC_START) == 0) break; @@ -220,19 +237,26 @@ int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu, if (retry == 10) { snd_printk(KERN_ERR "Writing to ADC failed!\n"); - return -EINVAL; + snd_printk(KERN_ERR "status=0x%x, reg=%d, value=%d\n", + status, reg, value); + /* dump_stack(); */ + err = -EINVAL; } - return 0; + spin_unlock(&emu->i2c_lock); + return err; } int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value) { + unsigned long flags; + if (reg > 0x3f) return 1; reg += 0x40; /* 0x40 upwards are registers. */ if (value < 0 || value > 0x3f) /* 0 to 0x3f are values */ return 1; + spin_lock_irqsave(&emu->emu_lock, flags); outl(reg, emu->port + A_IOCFG); udelay(10); outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */ @@ -240,20 +264,24 @@ int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value) outl(value, emu->port + A_IOCFG); udelay(10); outl(value | 0x80 , emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */ + spin_unlock_irqrestore(&emu->emu_lock, flags); return 0; } int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, u32 reg, u32 *value) { + unsigned long flags; if (reg > 0x3f) return 1; reg += 0x40; /* 0x40 upwards are registers. */ + spin_lock_irqsave(&emu->emu_lock, flags); outl(reg, emu->port + A_IOCFG); udelay(10); outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */ udelay(10); *value = ((inl(emu->port + A_IOCFG) >> 8) & 0x7f); + spin_unlock_irqrestore(&emu->emu_lock, flags); return 0; } diff --git a/sound/pci/emu10k1/irq.c b/sound/pci/emu10k1/irq.c index 3c114b4..30bfed6 100644 --- a/sound/pci/emu10k1/irq.c +++ b/sound/pci/emu10k1/irq.c @@ -25,7 +25,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <sound/core.h> #include <sound/emu10k1.h> @@ -35,9 +34,10 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id) struct snd_emu10k1 *emu = dev_id; unsigned int status, status2, orig_status, orig_status2; int handled = 0; + int timeout = 0; - while ((status = inl(emu->port + IPR)) != 0) { - //snd_printk(KERN_INFO "emu10k1 irq - status = 0x%x\n", status); + while (((status = inl(emu->port + IPR)) != 0) && (timeout < 1000)) { + timeout++; orig_status = status; handled = 1; if ((status & 0xffffffff) == 0xffffffff) { @@ -201,5 +201,8 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id) } outl(orig_status, emu->port + IPR); /* ack all */ } + if (timeout == 1000) + snd_printk(KERN_INFO "emu10k1 irq routine failure\n"); + return IRQ_RETVAL(handled); } diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c index 48097c6..916c1db 100644 --- a/sound/pci/emu10k1/memory.c +++ b/sound/pci/emu10k1/memory.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <linux/pci.h> #include <linux/time.h> #include <linux/mutex.h> diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c index 9fd3135..749a21b 100644 --- a/sound/pci/emu10k1/p16v.c +++ b/sound/pci/emu10k1/p16v.c @@ -87,7 +87,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/emu10k1/timer.c b/sound/pci/emu10k1/timer.c index 6295b2d..72321e9 100644 --- a/sound/pci/emu10k1/timer.c +++ b/sound/pci/emu10k1/timer.c @@ -25,7 +25,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <sound/core.h> #include <sound/emu10k1.h> diff --git a/sound/pci/emu10k1/voice.c b/sound/pci/emu10k1/voice.c index 04fa849..958cb2a 100644 --- a/sound/pci/emu10k1/voice.c +++ b/sound/pci/emu10k1/voice.c @@ -28,7 +28,6 @@ * */ -#include <sound/driver.h> #include <linux/time.h> #include <sound/core.h> #include <sound/emu10k1.h> diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index b958f86..72d85a5 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -26,7 +26,6 @@ * by Kurt J. Bosch */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index fb25abe..1a314fa 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -47,7 +47,6 @@ */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/pci.h> @@ -227,6 +226,7 @@ struct es1938 { unsigned int dma2_start; unsigned int dma1_shift; unsigned int dma2_shift; + unsigned int last_capture_dmaaddr; unsigned int active; spinlock_t reg_lock; @@ -529,6 +529,7 @@ static void snd_es1938_capture_setdma(struct es1938 *chip) outb(1, SLDM_REG(chip, DMAMASK)); outb(0x14, SLDM_REG(chip, DMAMODE)); outl(chip->dma1_start, SLDM_REG(chip, DMAADDR)); + chip->last_capture_dmaaddr = chip->dma1_start; outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT)); /* 3. Unmask DMA */ outb(0, SLDM_REG(chip, DMAMASK)); @@ -770,19 +771,40 @@ static int snd_es1938_playback_prepare(struct snd_pcm_substream *substream) return -EINVAL; } +/* during the incrementing of dma counters the DMA register reads sometimes + returns garbage. To ensure a valid hw pointer, the following checks which + should be very unlikely to fail are used: + - is the current DMA address in the valid DMA range ? + - is the sum of DMA address and DMA counter pointing to the last DMA byte ? + One can argue this could differ by one byte depending on which register is + updated first, so the implementation below allows for that. +*/ static snd_pcm_uframes_t snd_es1938_capture_pointer(struct snd_pcm_substream *substream) { struct es1938 *chip = snd_pcm_substream_chip(substream); size_t ptr; +#if 0 size_t old, new; -#if 1 /* This stuff is *needed*, don't ask why - AB */ old = inw(SLDM_REG(chip, DMACOUNT)); while ((new = inw(SLDM_REG(chip, DMACOUNT))) != old) old = new; ptr = chip->dma1_size - 1 - new; #else - ptr = inl(SLDM_REG(chip, DMAADDR)) - chip->dma1_start; + size_t count; + unsigned int diff; + + ptr = inl(SLDM_REG(chip, DMAADDR)); + count = inw(SLDM_REG(chip, DMACOUNT)); + diff = chip->dma1_start + chip->dma1_size - ptr - count; + + if (diff > 3 || ptr < chip->dma1_start + || ptr >= chip->dma1_start+chip->dma1_size) + ptr = chip->last_capture_dmaaddr; /* bad, use last saved */ + else + chip->last_capture_dmaaddr = ptr; /* good, remember it */ + + ptr -= chip->dma1_start; #endif return ptr >> chip->dma1_shift; } diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index d69b11d..25ccfce 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -94,7 +94,6 @@ * places. */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index 9939109..4c300e6 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -20,7 +20,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -979,6 +978,27 @@ static unsigned int snd_fm801_tea575x_64pcr_read(struct snd_tea575x *tea) return val; } +static void snd_fm801_tea575x_64pcr_mute(struct snd_tea575x *tea, + unsigned int mute) +{ + struct fm801 *chip = tea->private_data; + unsigned short reg; + + spin_lock_irq(&chip->reg_lock); + + reg = inw(FM801_REG(chip, GPIO_CTRL)); + if (mute) + /* 0xf800 (mute) */ + reg &= ~FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE); + else + /* 0xf802 (unmute) */ + reg |= FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE); + outw(reg, FM801_REG(chip, GPIO_CTRL)); + udelay(1); + + spin_unlock_irq(&chip->reg_lock); +} + static struct snd_tea575x_ops snd_fm801_tea_ops[3] = { { /* 1 = MediaForte 256-PCS */ @@ -994,6 +1014,7 @@ static struct snd_tea575x_ops snd_fm801_tea_ops[3] = { /* 3 = MediaForte 64-PCR */ .write = snd_fm801_tea575x_64pcr_write, .read = snd_fm801_tea575x_64pcr_read, + .mute = snd_fm801_tea575x_64pcr_mute, } }; #endif diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index ab0c726..9e0d8a1 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -2,7 +2,7 @@ snd-hda-intel-y := hda_intel.o # since snd-hda-intel is the only driver using hda-codec, # merge it into a single module although it was originally # designed to be individual modules -snd-hda-intel-y += hda_codec.o +snd-hda-intel-y += hda_codec.o vmaster.o snd-hda-intel-$(CONFIG_PROC_FS) += hda_proc.o snd-hda-intel-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o snd-hda-intel-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 8cbe3bf..26812dc 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -19,7 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> @@ -55,6 +54,7 @@ static struct hda_vendor_id hda_vendor_ids[] = { { 0x10ec, "Realtek" }, { 0x1057, "Motorola" }, { 0x1106, "VIA" }, + { 0x111d, "IDT" }, { 0x11d4, "Analog Devices" }, { 0x13f6, "C-Media" }, { 0x14f1, "Conexant" }, @@ -429,6 +429,10 @@ find_codec_preset(struct hda_codec *codec) for (tbl = hda_preset_tables; *tbl; tbl++) { for (preset = *tbl; preset->id; preset++) { u32 mask = preset->mask; + if (preset->afg && preset->afg != codec->afg) + continue; + if (preset->mfg && preset->mfg != codec->mfg) + continue; if (!mask) mask = ~0; if (preset->id == (codec->vendor_id & mask) && @@ -765,7 +769,7 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key) /* * query AMP capabilities for the given widget and direction */ -static u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) +u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) { struct hda_amp_info *info; @@ -933,7 +937,8 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; if (!caps) { printk(KERN_WARNING "hda_codec: " - "num_steps = 0 for NID=0x%x\n", nid); + "num_steps = 0 for NID=0x%x (ctl = %s)\n", nid, + kcontrol->id.name); return -EINVAL; } uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; @@ -1012,6 +1017,66 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, return 0; } +/* + * set (static) TLV for virtual master volume; recalculated as max 0dB + */ +void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, + unsigned int *tlv) +{ + u32 caps; + int nums, step; + + caps = query_amp_caps(codec, nid, dir); + nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; + step = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT; + step = (step + 1) * 25; + tlv[0] = SNDRV_CTL_TLVT_DB_SCALE; + tlv[1] = 2 * sizeof(unsigned int); + tlv[2] = -nums * step; + tlv[3] = step; +} + +/* find a mixer control element with the given name */ +struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, + const char *name) +{ + struct snd_ctl_elem_id id; + memset(&id, 0, sizeof(id)); + id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + strcpy(id.name, name); + return snd_ctl_find_id(codec->bus->card, &id); +} + +/* create a virtual master control and add slaves */ +int snd_hda_add_vmaster(struct hda_codec *codec, char *name, + unsigned int *tlv, const char **slaves) +{ + struct snd_kcontrol *kctl; + const char **s; + int err; + + kctl = snd_ctl_make_virtual_master(name, tlv); + if (!kctl) + return -ENOMEM; + err = snd_ctl_add(codec->bus->card, kctl); + if (err < 0) + return err; + + for (s = slaves; *s; s++) { + struct snd_kcontrol *sctl; + + sctl = snd_hda_find_mixer_ctl(codec, *s); + if (!sctl) { + snd_printdd("Cannot find slave %s, skipped\n", *s); + continue; + } + err = snd_ctl_add_slave(kctl, sctl); + if (err < 0) + return err; + } + return 0; +} + /* switch */ int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -1434,7 +1499,8 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid) return err; } codec->spdif_ctls = - snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0); + snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_DIGI_CONVERT_1, 0); codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls); return 0; } @@ -1481,7 +1547,7 @@ static int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol, unsigned short val; unsigned int sbits; - val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0); + val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0); sbits = convert_to_spdif_status(val); ucontrol->value.iec958.status[0] = sbits; ucontrol->value.iec958.status[1] = sbits >> 8; @@ -1532,7 +1598,8 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) return err; } codec->spdif_in_enable = - snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0) & + snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_DIGI_CONVERT_1, 0) & AC_DIG1_ENABLE; return 0; } @@ -1622,6 +1689,7 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state); + msleep(10); /* partial workaround for "azx_get_response timeout" */ nid = codec->start_nid; for (i = 0; i < codec->num_nodes; i++, nid++) { @@ -2336,7 +2404,8 @@ int snd_hda_ch_mode_put(struct hda_codec *codec, unsigned int mode; mode = ucontrol->value.enumerated.item[0]; - snd_assert(mode < num_chmodes, return -EINVAL); + if (mode >= num_chmodes) + return -EINVAL; if (*max_channelsp == chmode[mode].channels) return 0; /* change the current channel setting */ @@ -2602,20 +2671,21 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *cfg, hda_nid_t *ignore_nids) { - hda_nid_t nid, nid_start; - int nodes; + hda_nid_t nid, end_nid; short seq, assoc_line_out, assoc_speaker; short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)]; short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)]; + short sequences_hp[ARRAY_SIZE(cfg->hp_pins)]; memset(cfg, 0, sizeof(*cfg)); memset(sequences_line_out, 0, sizeof(sequences_line_out)); memset(sequences_speaker, 0, sizeof(sequences_speaker)); + memset(sequences_hp, 0, sizeof(sequences_hp)); assoc_line_out = assoc_speaker = 0; - nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid_start); - for (nid = nid_start; nid < nodes + nid_start; nid++) { + end_nid = codec->start_nid + codec->num_nodes; + for (nid = codec->start_nid; nid < end_nid; nid++) { unsigned int wid_caps = get_wcaps(codec, nid); unsigned int wid_type = (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; @@ -2638,6 +2708,10 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, case AC_JACK_LINE_OUT: seq = get_defcfg_sequence(def_conf); assoc = get_defcfg_association(def_conf); + + if (!(wid_caps & AC_WCAP_STEREO)) + if (!cfg->mono_out_pin) + cfg->mono_out_pin = nid; if (!assoc) continue; if (!assoc_line_out) @@ -2666,9 +2740,12 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, cfg->speaker_outs++; break; case AC_JACK_HP_OUT: + seq = get_defcfg_sequence(def_conf); + assoc = get_defcfg_association(def_conf); if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins)) continue; cfg->hp_pins[cfg->hp_outs] = nid; + sequences_hp[cfg->hp_outs] = (assoc << 4) | seq; cfg->hp_outs++; break; case AC_JACK_MIC_IN: { @@ -2712,7 +2789,24 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, cfg->line_outs); sort_pins_by_sequence(cfg->speaker_pins, sequences_speaker, cfg->speaker_outs); + sort_pins_by_sequence(cfg->hp_pins, sequences_hp, + cfg->hp_outs); + /* if we have only one mic, make it AUTO_PIN_MIC */ + if (!cfg->input_pins[AUTO_PIN_MIC] && + cfg->input_pins[AUTO_PIN_FRONT_MIC]) { + cfg->input_pins[AUTO_PIN_MIC] = + cfg->input_pins[AUTO_PIN_FRONT_MIC]; + cfg->input_pins[AUTO_PIN_FRONT_MIC] = 0; + } + /* ditto for line-in */ + if (!cfg->input_pins[AUTO_PIN_LINE] && + cfg->input_pins[AUTO_PIN_FRONT_LINE]) { + cfg->input_pins[AUTO_PIN_LINE] = + cfg->input_pins[AUTO_PIN_FRONT_LINE]; + cfg->input_pins[AUTO_PIN_FRONT_LINE] = 0; + } + /* * FIX-UP: if no line-outs are detected, try to use speaker or HP pin * as a primary output @@ -2766,6 +2860,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, cfg->hp_outs, cfg->hp_pins[0], cfg->hp_pins[1], cfg->hp_pins[2], cfg->hp_pins[3], cfg->hp_pins[4]); + snd_printd(" mono: mono_out=0x%x\n", cfg->mono_out_pin); snd_printd(" inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x," " cd=0x%x, aux=0x%x\n", cfg->input_pins[AUTO_PIN_MIC], diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 2bce925..f148711 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -77,12 +77,16 @@ enum { #define AC_VERB_GET_PIN_SENSE 0x0f09 #define AC_VERB_GET_BEEP_CONTROL 0x0f0a #define AC_VERB_GET_EAPD_BTLENABLE 0x0f0c -#define AC_VERB_GET_DIGI_CONVERT 0x0f0d +#define AC_VERB_GET_DIGI_CONVERT_1 0x0f0d +#define AC_VERB_GET_DIGI_CONVERT_2 0x0f0e #define AC_VERB_GET_VOLUME_KNOB_CONTROL 0x0f0f /* f10-f1a: GPIO */ #define AC_VERB_GET_GPIO_DATA 0x0f15 #define AC_VERB_GET_GPIO_MASK 0x0f16 #define AC_VERB_GET_GPIO_DIRECTION 0x0f17 +#define AC_VERB_GET_GPIO_WAKE_MASK 0x0f18 +#define AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK 0x0f19 +#define AC_VERB_GET_GPIO_STICKY_MASK 0x0f1a #define AC_VERB_GET_CONFIG_DEFAULT 0x0f1c /* f20: AFG/MFG */ #define AC_VERB_GET_SUBSYSTEM_ID 0x0f20 @@ -110,6 +114,9 @@ enum { #define AC_VERB_SET_GPIO_DATA 0x715 #define AC_VERB_SET_GPIO_MASK 0x716 #define AC_VERB_SET_GPIO_DIRECTION 0x717 +#define AC_VERB_SET_GPIO_WAKE_MASK 0x718 +#define AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK 0x719 +#define AC_VERB_SET_GPIO_STICKY_MASK 0x71a #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 0x71c #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1 0x71d #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2 0x71e @@ -135,6 +142,7 @@ enum { #define AC_PAR_PROC_CAP 0x10 #define AC_PAR_GPIO_CAP 0x11 #define AC_PAR_AMP_OUT_CAP 0x12 +#define AC_PAR_VOL_KNB_CAP 0x13 /* * AC_VERB_PARAMETERS results (32bit) @@ -181,6 +189,27 @@ enum { #define AC_SUPFMT_FLOAT32 (1<<1) #define AC_SUPFMT_AC3 (1<<2) +/* GP I/O count */ +#define AC_GPIO_IO_COUNT (0xff<<0) +#define AC_GPIO_O_COUNT (0xff<<8) +#define AC_GPIO_O_COUNT_SHIFT 8 +#define AC_GPIO_I_COUNT (0xff<<16) +#define AC_GPIO_I_COUNT_SHIFT 16 +#define AC_GPIO_UNSOLICITED (1<<30) +#define AC_GPIO_WAKE (1<<31) + +/* Converter stream, channel */ +#define AC_CONV_CHANNEL (0xf<<0) +#define AC_CONV_STREAM (0xf<<4) +#define AC_CONV_STREAM_SHIFT 4 + +/* Input converter SDI select */ +#define AC_SDI_SELECT (0xf<<0) + +/* Unsolicited response */ +#define AC_UNSOL_TAG (0x3f<<0) +#define AC_UNSOL_ENABLED (1<<7) + /* Pin widget capabilies */ #define AC_PINCAP_IMP_SENSE (1<<0) /* impedance sense capable */ #define AC_PINCAP_TRIG_REQ (1<<1) /* trigger required */ @@ -189,6 +218,10 @@ enum { #define AC_PINCAP_OUT (1<<4) /* output capable */ #define AC_PINCAP_IN (1<<5) /* input capable */ #define AC_PINCAP_BALANCE (1<<6) /* balanced I/O capable */ +/* Note: This LR_SWAP pincap is defined in the Realtek ALC883 specification, + * but is marked reserved in the Intel HDA specification. + */ +#define AC_PINCAP_LR_SWAP (1<<7) /* L/R swap */ #define AC_PINCAP_VREF (0x37<<8) #define AC_PINCAP_VREF_SHIFT 8 #define AC_PINCAP_EAPD (1<<16) /* EAPD capable */ @@ -222,6 +255,9 @@ enum { #define AC_PWRST_D3SUP (1<<3) /* Power state values */ +#define AC_PWRST_SETTING (0xf<<0) +#define AC_PWRST_ACTUAL (0xf<<4) +#define AC_PWRST_ACTUAL_SHIFT 4 #define AC_PWRST_D0 0x00 #define AC_PWRST_D1 0x01 #define AC_PWRST_D2 0x02 @@ -230,10 +266,11 @@ enum { /* Processing capabilies */ #define AC_PCAP_BENIGN (1<<0) #define AC_PCAP_NUM_COEF (0xff<<8) +#define AC_PCAP_NUM_COEF_SHIFT 8 /* Volume knobs capabilities */ #define AC_KNBCAP_NUM_STEPS (0x7f<<0) -#define AC_KNBCAP_DELTA (1<<8) +#define AC_KNBCAP_DELTA (1<<7) /* * Control Parameters @@ -266,6 +303,9 @@ enum { #define AC_DIG1_PROFESSIONAL (1<<6) #define AC_DIG1_LEVEL (1<<7) +/* DIGITAL2 bits */ +#define AC_DIG2_CC (0x7f<<0) + /* Pin widget control - 8bit */ #define AC_PINCTL_VREFEN (0x7<<0) #define AC_PINCTL_VREF_HIZ 0 /* Hi-Z */ @@ -280,12 +320,22 @@ enum { /* Unsolicited response - 8bit */ #define AC_USRSP_EN (1<<7) +/* Pin sense - 32bit */ +#define AC_PINSENSE_IMPEDANCE_MASK (0x7fffffff) +#define AC_PINSENSE_PRESENCE (1<<31) + +/* EAPD/BTL enable - 32bit */ +#define AC_EAPDBTL_BALANCED (1<<0) +#define AC_EAPDBTL_EAPD (1<<1) +#define AC_EAPDBTL_LR_SWAP (1<<2) + /* configuration default - 32bit */ #define AC_DEFCFG_SEQUENCE (0xf<<0) #define AC_DEFCFG_DEF_ASSOC (0xf<<4) #define AC_DEFCFG_ASSOC_SHIFT 4 #define AC_DEFCFG_MISC (0xf<<8) #define AC_DEFCFG_MISC_SHIFT 8 +#define AC_DEFCFG_MISC_NO_PRESENCE (1<<0) #define AC_DEFCFG_COLOR (0xf<<12) #define AC_DEFCFG_COLOR_SHIFT 12 #define AC_DEFCFG_CONN_TYPE (0xf<<16) @@ -417,7 +467,7 @@ struct hda_bus_ops { /* free the private data */ void (*private_free)(struct hda_bus *); #ifdef CONFIG_SND_HDA_POWER_SAVE - /* notify power-up/down from codec to contoller */ + /* notify power-up/down from codec to controller */ void (*pm_notify)(struct hda_codec *codec); #endif }; @@ -456,6 +506,9 @@ struct hda_bus { struct hda_bus_unsolicited *unsol; struct snd_info_entry *proc; + + /* misc op flags */ + unsigned int needs_damn_long_delay :1; }; /* @@ -470,6 +523,7 @@ struct hda_codec_preset { unsigned int subs; unsigned int subs_mask; unsigned int rev; + hda_nid_t afg, mfg; const char *name; int (*patch)(struct hda_codec *codec); }; diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index c957eb5..f9de7c4 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/slab.h> #include <sound/core.h> diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index bafb7b0..2177d9a 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -18,7 +18,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/pci.h> diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 3fa0f97..56f8a30 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -34,7 +34,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -50,29 +49,32 @@ #include "hda_codec.h" -static int index = SNDRV_DEFAULT_IDX1; -static char *id = SNDRV_DEFAULT_STR1; -static char *model; -static int position_fix; -static int probe_mask = -1; +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; +static char *model[SNDRV_CARDS]; +static int position_fix[SNDRV_CARDS]; +static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; static int single_cmd; static int enable_msi; -module_param(index, int, 0444); +module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for Intel HD audio interface."); -module_param(id, charp, 0444); +module_param_array(id, charp, NULL, 0444); MODULE_PARM_DESC(id, "ID string for Intel HD audio interface."); -module_param(model, charp, 0444); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable Intel HD audio interface."); +module_param_array(model, charp, NULL, 0444); MODULE_PARM_DESC(model, "Use the given board model."); -module_param(position_fix, int, 0444); +module_param_array(position_fix, int, NULL, 0444); MODULE_PARM_DESC(position_fix, "Fix DMA pointer " "(0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size)."); -module_param(probe_mask, int, 0444); +module_param_array(probe_mask, int, NULL, 0444); MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1)."); module_param(single_cmd, bool, 0444); MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs " "(for debugging only)."); -module_param(enable_msi, int, 0); +module_param(enable_msi, int, 0444); MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)"); #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -87,10 +89,6 @@ module_param(power_save_controller, bool, 0644); MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode."); #endif -/* just for backward compatibility */ -static int enable; -module_param(enable, bool, 0444); - MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("{{Intel, ICH6}," "{Intel, ICH6M}," @@ -98,12 +96,20 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6}," "{Intel, ESB2}," "{Intel, ICH8}," "{Intel, ICH9}," + "{Intel, ICH10}," + "{Intel, SCH}," "{ATI, SB450}," "{ATI, SB600}," "{ATI, RS600}," "{ATI, RS690}," "{ATI, RS780}," "{ATI, R600}," + "{ATI, RV630}," + "{ATI, RV610}," + "{ATI, RV670}," + "{ATI, RV635}," + "{ATI, RV620}," + "{ATI, RV770}," "{VIA, VT8251}," "{VIA, VT8237A}," "{SiS, SIS966}," @@ -370,6 +376,7 @@ struct azx { /* driver types */ enum { AZX_DRIVER_ICH, + AZX_DRIVER_SCH, AZX_DRIVER_ATI, AZX_DRIVER_ATIHDMI, AZX_DRIVER_VIA, @@ -380,6 +387,7 @@ enum { static char *driver_short_names[] __devinitdata = { [AZX_DRIVER_ICH] = "HDA Intel", + [AZX_DRIVER_SCH] = "HDA Intel MID", [AZX_DRIVER_ATI] = "HDA ATI SB", [AZX_DRIVER_ATIHDMI] = "HDA ATI HDMI", [AZX_DRIVER_VIA] = "HDA VIA VT82xx", @@ -547,7 +555,7 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec) again: timeout = jiffies + msecs_to_jiffies(1000); - do { + for (;;) { if (chip->polling_mode) { spin_lock_irq(&chip->reg_lock); azx_update_rirb(chip); @@ -555,8 +563,15 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec) } if (!chip->rirb.cmds) return chip->rirb.res; /* the last value */ - schedule_timeout_uninterruptible(1); - } while (time_after_eq(timeout, jiffies)); + if (time_after(jiffies, timeout)) + break; + if (codec->bus->needs_damn_long_delay) + msleep(2); /* temporary workaround */ + else { + udelay(10); + cond_resched(); + } + } if (chip->msi) { snd_printk(KERN_WARNING "hda_intel: No response from codec, " @@ -618,8 +633,9 @@ static int azx_single_send_cmd(struct hda_codec *codec, u32 val) } udelay(1); } - snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n", - azx_readw(chip, IRS), val); + if (printk_ratelimit()) + snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n", + azx_readw(chip, IRS), val); return -EIO; } @@ -635,8 +651,9 @@ static unsigned int azx_single_get_response(struct hda_codec *codec) return azx_readl(chip, IR); udelay(1); } - snd_printd(SFX "get_response timeout: IRS=0x%x\n", - azx_readw(chip, IRS)); + if (printk_ratelimit()) + snd_printd(SFX "get_response timeout: IRS=0x%x\n", + azx_readw(chip, IRS)); return (unsigned int)-1; } @@ -1031,7 +1048,8 @@ static unsigned int azx_max_codecs[] __devinitdata = { [AZX_DRIVER_NVIDIA] = 3, /* FIXME: correct? */ }; -static int __devinit azx_codec_create(struct azx *chip, const char *model) +static int __devinit azx_codec_create(struct azx *chip, const char *model, + unsigned int codec_probe_mask) { struct hda_bus_template bus_temp; int c, codecs, audio_codecs, err; @@ -1052,7 +1070,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model) codecs = audio_codecs = 0; for (c = 0; c < AZX_MAX_CODECS; c++) { - if ((chip->codec_mask & (1 << c)) & probe_mask) { + if ((chip->codec_mask & (1 << c)) & codec_probe_mask) { struct hda_codec *codec; err = snd_hda_codec_new(chip->bus, c, &codec); if (err < 0) @@ -1065,7 +1083,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model) if (!audio_codecs) { /* probe additional slots if no codec is found */ for (; c < azx_max_codecs[chip->driver_type]; c++) { - if ((chip->codec_mask & (1 << c)) & probe_mask) { + if ((chip->codec_mask & (1 << c)) & codec_probe_mask) { err = snd_hda_codec_new(chip->bus, c, NULL); if (err < 0) continue; @@ -1676,18 +1694,18 @@ static struct snd_pci_quirk probe_mask_list[] __devinitdata = { {} }; -static void __devinit check_probe_mask(struct azx *chip) +static void __devinit check_probe_mask(struct azx *chip, int dev) { const struct snd_pci_quirk *q; - if (probe_mask == -1) { + if (probe_mask[dev] == -1) { q = snd_pci_quirk_lookup(chip->pci, probe_mask_list); if (q) { printk(KERN_INFO "hda_intel: probe_mask set to 0x%x " "for device %04x:%04x\n", q->value, q->subvendor, q->subdevice); - probe_mask = q->value; + probe_mask[dev] = q->value; } } } @@ -1697,17 +1715,18 @@ static void __devinit check_probe_mask(struct azx *chip) * constructor */ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, - int driver_type, + int dev, int driver_type, struct azx **rchip) { struct azx *chip; int err; + unsigned short gcap; static struct snd_device_ops ops = { .dev_free = azx_dev_free, }; *rchip = NULL; - + err = pci_enable_device(pci); if (err < 0) return err; @@ -1727,8 +1746,8 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, chip->driver_type = driver_type; chip->msi = enable_msi; - chip->position_fix = check_position_fix(chip, position_fix); - check_probe_mask(chip); + chip->position_fix = check_position_fix(chip, position_fix[dev]); + check_probe_mask(chip, dev); chip->single_cmd = single_cmd; @@ -1769,25 +1788,40 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, pci_set_master(pci); synchronize_irq(chip->irq); - switch (chip->driver_type) { - case AZX_DRIVER_ULI: - chip->playback_streams = ULI_NUM_PLAYBACK; - chip->capture_streams = ULI_NUM_CAPTURE; - chip->playback_index_offset = ULI_PLAYBACK_INDEX; - chip->capture_index_offset = ULI_CAPTURE_INDEX; - break; - case AZX_DRIVER_ATIHDMI: - chip->playback_streams = ATIHDMI_NUM_PLAYBACK; - chip->capture_streams = ATIHDMI_NUM_CAPTURE; - chip->playback_index_offset = ATIHDMI_PLAYBACK_INDEX; - chip->capture_index_offset = ATIHDMI_CAPTURE_INDEX; - break; - default: - chip->playback_streams = ICH6_NUM_PLAYBACK; - chip->capture_streams = ICH6_NUM_CAPTURE; - chip->playback_index_offset = ICH6_PLAYBACK_INDEX; - chip->capture_index_offset = ICH6_CAPTURE_INDEX; - break; + gcap = azx_readw(chip, GCAP); + snd_printdd("chipset global capabilities = 0x%x\n", gcap); + + if (gcap) { + /* read number of streams from GCAP register instead of using + * hardcoded value + */ + chip->playback_streams = (gcap & (0xF << 12)) >> 12; + chip->capture_streams = (gcap & (0xF << 8)) >> 8; + chip->playback_index_offset = (gcap & (0xF << 12)) >> 12; + chip->capture_index_offset = 0; + } else { + /* gcap didn't give any info, switching to old method */ + + switch (chip->driver_type) { + case AZX_DRIVER_ULI: + chip->playback_streams = ULI_NUM_PLAYBACK; + chip->capture_streams = ULI_NUM_CAPTURE; + chip->playback_index_offset = ULI_PLAYBACK_INDEX; + chip->capture_index_offset = ULI_CAPTURE_INDEX; + break; + case AZX_DRIVER_ATIHDMI: + chip->playback_streams = ATIHDMI_NUM_PLAYBACK; + chip->capture_streams = ATIHDMI_NUM_CAPTURE; + chip->playback_index_offset = ATIHDMI_PLAYBACK_INDEX; + chip->capture_index_offset = ATIHDMI_CAPTURE_INDEX; + break; + default: + chip->playback_streams = ICH6_NUM_PLAYBACK; + chip->capture_streams = ICH6_NUM_CAPTURE; + chip->playback_index_offset = ICH6_PLAYBACK_INDEX; + chip->capture_index_offset = ICH6_CAPTURE_INDEX; + break; + } } chip->num_streams = chip->playback_streams + chip->capture_streams; chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev), @@ -1869,17 +1903,25 @@ static void power_down_all_codecs(struct azx *chip) static int __devinit azx_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { + static int dev; struct snd_card *card; struct azx *chip; int err; - card = snd_card_new(index, id, THIS_MODULE, 0); + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); if (!card) { snd_printk(KERN_ERR SFX "Error creating card!\n"); return -ENOMEM; } - err = azx_create(card, pci, pci_id->driver_data, &chip); + err = azx_create(card, pci, dev, pci_id->driver_data, &chip); if (err < 0) { snd_card_free(card); return err; @@ -1887,7 +1929,7 @@ static int __devinit azx_probe(struct pci_dev *pci, card->private_data = chip; /* create codec instances */ - err = azx_codec_create(chip, model); + err = azx_codec_create(chip, model[dev], probe_mask[dev]); if (err < 0) { snd_card_free(card); return err; @@ -1919,6 +1961,7 @@ static int __devinit azx_probe(struct pci_dev *pci, chip->running = 1; power_down_all_codecs(chip); + dev++; return err; } @@ -1936,12 +1979,21 @@ static struct pci_device_id azx_ids[] = { { 0x8086, 0x284b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH8 */ { 0x8086, 0x293e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH9 */ { 0x8086, 0x293f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH9 */ + { 0x8086, 0x3a3e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH10 */ + { 0x8086, 0x3a6e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH10 */ + { 0x8086, 0x811b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SCH }, /* SCH*/ { 0x1002, 0x437b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB450 */ { 0x1002, 0x4383, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB600 */ { 0x1002, 0x793b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS600 HDMI */ { 0x1002, 0x7919, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS690 HDMI */ - { 0x1002, 0x960c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS780 HDMI */ + { 0x1002, 0x960f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS780 HDMI */ { 0x1002, 0xaa00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI R600 HDMI */ + { 0x1002, 0xaa08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV630 HDMI */ + { 0x1002, 0xaa10, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV610 HDMI */ + { 0x1002, 0xaa18, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV670 HDMI */ + { 0x1002, 0xaa20, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV635 HDMI */ + { 0x1002, 0xaa28, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV620 HDMI */ + { 0x1002, 0xaa30, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV770 HDMI */ { 0x1106, 0x3288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_VIA }, /* VIA VT8251/VT8237A */ { 0x1039, 0x7502, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SIS }, /* SIS966 */ { 0x10b9, 0x5461, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ULI }, /* ULI M5461 */ diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 8c56c9c..ad0014a 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -90,6 +90,13 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, void snd_hda_codec_resume_amp(struct hda_codec *codec); #endif +void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, + unsigned int *tlv); +struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, + const char *name); +int snd_hda_add_vmaster(struct hda_codec *codec, char *name, + unsigned int *tlv, const char **slaves); + /* amp value bits */ #define HDA_AMP_MUTE 0x80 #define HDA_AMP_UNMUTE 0x00 @@ -325,6 +332,7 @@ struct auto_pin_cfg { hda_nid_t input_pins[AUTO_PIN_LAST]; hda_nid_t dig_out_pin; hda_nid_t dig_in_pin; + hda_nid_t mono_out_pin; }; #define get_defcfg_connect(cfg) \ @@ -363,10 +371,11 @@ static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid) { if (nid < codec->start_nid || nid >= codec->start_nid + codec->num_nodes) - return snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); + return 0; return codec->wcaps[nid - codec->start_nid]; } +u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction); int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int caps); @@ -398,4 +407,11 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec, hda_nid_t nid); #endif /* CONFIG_SND_HDA_POWER_SAVE */ +/* + * virtual master control + */ +struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, + const unsigned int *tlv); +int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave); + #endif /* __SOUND_HDA_LOCAL_H */ diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index e94944f..35a630d 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -21,7 +21,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <sound/core.h> #include "hda_codec.h" @@ -203,7 +202,8 @@ static const char *get_jack_color(u32 cfg) } static void print_pin_caps(struct snd_info_buffer *buffer, - struct hda_codec *codec, hda_nid_t nid) + struct hda_codec *codec, hda_nid_t nid, + int *supports_vref) { static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" }; static char *jack_types[16] = { @@ -213,7 +213,7 @@ static void print_pin_caps(struct snd_info_buffer *buffer, "SPDIF In", "Digitial In", "Reserved", "Other" }; static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" }; - unsigned int caps; + unsigned int caps, val; caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); snd_iprintf(buffer, " Pincap 0x08%x:", caps); @@ -227,7 +227,45 @@ static void print_pin_caps(struct snd_info_buffer *buffer, snd_iprintf(buffer, " EAPD"); if (caps & AC_PINCAP_PRES_DETECT) snd_iprintf(buffer, " Detect"); + if (caps & AC_PINCAP_BALANCE) + snd_iprintf(buffer, " Balanced"); + if (caps & AC_PINCAP_LR_SWAP) + snd_iprintf(buffer, " R/L"); + if (caps & AC_PINCAP_TRIG_REQ) + snd_iprintf(buffer, " Trigger"); + if (caps & AC_PINCAP_IMP_SENSE) + snd_iprintf(buffer, " ImpSense"); snd_iprintf(buffer, "\n"); + if (caps & AC_PINCAP_VREF) { + unsigned int vref = + (caps & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; + snd_iprintf(buffer, " Vref caps:"); + if (vref & AC_PINCAP_VREF_HIZ) + snd_iprintf(buffer, " HIZ"); + if (vref & AC_PINCAP_VREF_50) + snd_iprintf(buffer, " 50"); + if (vref & AC_PINCAP_VREF_GRD) + snd_iprintf(buffer, " GRD"); + if (vref & AC_PINCAP_VREF_80) + snd_iprintf(buffer, " 80"); + if (vref & AC_PINCAP_VREF_100) + snd_iprintf(buffer, " 100"); + snd_iprintf(buffer, "\n"); + *supports_vref = 1; + } else + *supports_vref = 0; + if (caps & AC_PINCAP_EAPD) { + val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_EAPD_BTLENABLE, 0); + snd_iprintf(buffer, " EAPD 0x%x:", val); + if (val & AC_EAPDBTL_BALANCED) + snd_iprintf(buffer, " BALANCED"); + if (val & AC_EAPDBTL_EAPD) + snd_iprintf(buffer, " EAPD"); + if (val & AC_EAPDBTL_LR_SWAP) + snd_iprintf(buffer, " R/L"); + snd_iprintf(buffer, "\n"); + } caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); snd_iprintf(buffer, " Pin Default 0x%08x: [%s] %s at %s %s\n", caps, jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT], @@ -237,8 +275,233 @@ static void print_pin_caps(struct snd_info_buffer *buffer, snd_iprintf(buffer, " Conn = %s, Color = %s\n", get_jack_connection(caps), get_jack_color(caps)); + /* Default association and sequence values refer to default grouping + * of pin complexes and their sequence within the group. This is used + * for priority and resource allocation. + */ + snd_iprintf(buffer, " DefAssociation = 0x%x, Sequence = 0x%x\n", + (caps & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT, + caps & AC_DEFCFG_SEQUENCE); + if (((caps & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT) & + AC_DEFCFG_MISC_NO_PRESENCE) { + /* Miscellaneous bit indicates external hardware does not + * support presence detection even if the pin complex + * indicates it is supported. + */ + snd_iprintf(buffer, " Misc = NO_PRESENCE\n"); + } +} + +static void print_pin_ctls(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid, + int supports_vref) +{ + unsigned int pinctls; + + pinctls = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_iprintf(buffer, " Pin-ctls: 0x%02x:", pinctls); + if (pinctls & AC_PINCTL_IN_EN) + snd_iprintf(buffer, " IN"); + if (pinctls & AC_PINCTL_OUT_EN) + snd_iprintf(buffer, " OUT"); + if (pinctls & AC_PINCTL_HP_EN) + snd_iprintf(buffer, " HP"); + if (supports_vref) { + int vref = pinctls & AC_PINCTL_VREFEN; + switch (vref) { + case AC_PINCTL_VREF_HIZ: + snd_iprintf(buffer, " VREF_HIZ"); + break; + case AC_PINCTL_VREF_50: + snd_iprintf(buffer, " VREF_50"); + break; + case AC_PINCTL_VREF_GRD: + snd_iprintf(buffer, " VREF_GRD"); + break; + case AC_PINCTL_VREF_80: + snd_iprintf(buffer, " VREF_80"); + break; + case AC_PINCTL_VREF_100: + snd_iprintf(buffer, " VREF_100"); + break; + } + } + snd_iprintf(buffer, "\n"); +} + +static void print_vol_knob(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int cap = snd_hda_param_read(codec, nid, + AC_PAR_VOL_KNB_CAP); + snd_iprintf(buffer, " Volume-Knob: delta=%d, steps=%d, ", + (cap >> 7) & 1, cap & 0x7f); + cap = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_VOLUME_KNOB_CONTROL, 0); + snd_iprintf(buffer, "direct=%d, val=%d\n", + (cap >> 7) & 1, cap & 0x7f); +} + +static void print_audio_io(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid, + unsigned int wid_type) +{ + int conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); + snd_iprintf(buffer, + " Converter: stream=%d, channel=%d\n", + (conv & AC_CONV_STREAM) >> AC_CONV_STREAM_SHIFT, + conv & AC_CONV_CHANNEL); + + if (wid_type == AC_WID_AUD_IN && (conv & AC_CONV_CHANNEL) == 0) { + int sdi = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_SDI_SELECT, 0); + snd_iprintf(buffer, " SDI-Select: %d\n", + sdi & AC_SDI_SELECT); + } +} + +static void print_digital_conv(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int digi1 = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_DIGI_CONVERT_1, 0); + unsigned int digi2 = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_DIGI_CONVERT_2, 0); + snd_iprintf(buffer, " Digital:"); + if (digi1 & AC_DIG1_ENABLE) + snd_iprintf(buffer, " Enabled"); + if (digi1 & AC_DIG1_V) + snd_iprintf(buffer, " Validity"); + if (digi1 & AC_DIG1_VCFG) + snd_iprintf(buffer, " ValidityCfg"); + if (digi1 & AC_DIG1_EMPHASIS) + snd_iprintf(buffer, " Preemphasis"); + if (digi1 & AC_DIG1_COPYRIGHT) + snd_iprintf(buffer, " Copyright"); + if (digi1 & AC_DIG1_NONAUDIO) + snd_iprintf(buffer, " Non-Audio"); + if (digi1 & AC_DIG1_PROFESSIONAL) + snd_iprintf(buffer, " Pro"); + if (digi1 & AC_DIG1_LEVEL) + snd_iprintf(buffer, " GenLevel"); + snd_iprintf(buffer, "\n"); + snd_iprintf(buffer, " Digital category: 0x%x\n", digi2 & AC_DIG2_CC); +} + +static const char *get_pwr_state(u32 state) +{ + static const char *buf[4] = { + "D0", "D1", "D2", "D3" + }; + if (state < 4) + return buf[state]; + return "UNKNOWN"; +} + +static void print_power_state(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + int pwr = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_POWER_STATE, 0); + snd_iprintf(buffer, " Power: setting=%s, actual=%s\n", + get_pwr_state(pwr & AC_PWRST_SETTING), + get_pwr_state((pwr & AC_PWRST_ACTUAL) >> + AC_PWRST_ACTUAL_SHIFT)); +} + +static void print_unsol_cap(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + int unsol = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_UNSOLICITED_RESPONSE, 0); + snd_iprintf(buffer, + " Unsolicited: tag=%02x, enabled=%d\n", + unsol & AC_UNSOL_TAG, + (unsol & AC_UNSOL_ENABLED) ? 1 : 0); +} + +static void print_proc_caps(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int proc_caps = snd_hda_param_read(codec, nid, + AC_PAR_PROC_CAP); + snd_iprintf(buffer, " Processing caps: benign=%d, ncoeff=%d\n", + proc_caps & AC_PCAP_BENIGN, + (proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT); } +static void print_conn_list(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid, + unsigned int wid_type, hda_nid_t *conn, + int conn_len) +{ + int c, curr = -1; + + if (conn_len > 1 && wid_type != AC_WID_AUD_MIX) + curr = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CONNECT_SEL, 0); + snd_iprintf(buffer, " Connection: %d\n", conn_len); + if (conn_len > 0) { + snd_iprintf(buffer, " "); + for (c = 0; c < conn_len; c++) { + snd_iprintf(buffer, " 0x%02x", conn[c]); + if (c == curr) + snd_iprintf(buffer, "*"); + } + snd_iprintf(buffer, "\n"); + } +} + +static void print_realtek_coef(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + int coeff = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PROC_COEF, 0); + snd_iprintf(buffer, " Processing Coefficient: 0x%02x\n", coeff); + coeff = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_COEF_INDEX, 0); + snd_iprintf(buffer, " Coefficient Index: 0x%02x\n", coeff); +} + +static void print_gpio(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int gpio = + snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP); + unsigned int enable, direction, wake, unsol, sticky, data; + int i, max; + snd_iprintf(buffer, "GPIO: io=%d, o=%d, i=%d, " + "unsolicited=%d, wake=%d\n", + gpio & AC_GPIO_IO_COUNT, + (gpio & AC_GPIO_O_COUNT) >> AC_GPIO_O_COUNT_SHIFT, + (gpio & AC_GPIO_I_COUNT) >> AC_GPIO_I_COUNT_SHIFT, + (gpio & AC_GPIO_UNSOLICITED) ? 1 : 0, + (gpio & AC_GPIO_WAKE) ? 1 : 0); + max = gpio & AC_GPIO_IO_COUNT; + enable = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPIO_MASK, 0); + direction = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPIO_DIRECTION, 0); + wake = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPIO_WAKE_MASK, 0); + unsol = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK, 0); + sticky = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPIO_STICKY_MASK, 0); + data = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPIO_DATA, 0); + for (i = 0; i < max; ++i) + snd_iprintf(buffer, + " IO[%d]: enable=%d, dir=%d, wake=%d, " + "sticky=%d, data=%d\n", i, + (enable & (1<<i)) ? 1 : 0, + (direction & (1<<i)) ? 1 : 0, + (wake & (1<<i)) ? 1 : 0, + (sticky & (1<<i)) ? 1 : 0, + (data & (1<<i)) ? 1 : 0); + /* FIXME: add GPO and GPI pin information */ +} static void print_codec_info(struct snd_info_entry *entry, struct snd_info_buffer *buffer) @@ -276,14 +539,17 @@ static void print_codec_info(struct snd_info_entry *entry, snd_hda_power_down(codec); return; } + + print_gpio(buffer, codec, codec->afg); + for (i = 0; i < nodes; i++, nid++) { unsigned int wid_caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); unsigned int wid_type = (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; - int conn_len = 0; hda_nid_t conn[HDA_MAX_CONNECTIONS]; + int conn_len = 0; snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid, get_wid_type_name(wid_type), wid_caps); @@ -297,8 +563,18 @@ static void print_codec_info(struct snd_info_entry *entry, snd_iprintf(buffer, " Amp-In"); if (wid_caps & AC_WCAP_OUT_AMP) snd_iprintf(buffer, " Amp-Out"); + if (wid_caps & AC_WCAP_STRIPE) + snd_iprintf(buffer, " Stripe"); + if (wid_caps & AC_WCAP_LR_SWAP) + snd_iprintf(buffer, " R/L"); snd_iprintf(buffer, "\n"); + /* volume knob is a special widget that always have connection + * list + */ + if (wid_type == AC_WID_VOL_KNB) + wid_caps |= AC_WCAP_CONN_LIST; + if (wid_caps & AC_WCAP_CONN_LIST) conn_len = snd_hda_get_connections(codec, nid, conn, HDA_MAX_CONNECTIONS); @@ -318,48 +594,49 @@ static void print_codec_info(struct snd_info_entry *entry, wid_caps & AC_WCAP_STEREO, 1); } - if (wid_type == AC_WID_PIN) { - unsigned int pinctls; - print_pin_caps(buffer, codec, nid); - pinctls = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, - 0); - snd_iprintf(buffer, " Pin-ctls: 0x%02x:", pinctls); - if (pinctls & AC_PINCTL_IN_EN) - snd_iprintf(buffer, " IN"); - if (pinctls & AC_PINCTL_OUT_EN) - snd_iprintf(buffer, " OUT"); - if (pinctls & AC_PINCTL_HP_EN) - snd_iprintf(buffer, " HP"); - snd_iprintf(buffer, "\n"); + switch (wid_type) { + case AC_WID_PIN: { + int supports_vref; + print_pin_caps(buffer, codec, nid, &supports_vref); + print_pin_ctls(buffer, codec, nid, supports_vref); + break; } - - if ((wid_type == AC_WID_AUD_OUT || wid_type == AC_WID_AUD_IN) && - (wid_caps & AC_WCAP_FORMAT_OVRD)) { - snd_iprintf(buffer, " PCM:\n"); - print_pcm_caps(buffer, codec, nid); + case AC_WID_VOL_KNB: + print_vol_knob(buffer, codec, nid); + break; + case AC_WID_AUD_OUT: + case AC_WID_AUD_IN: + print_audio_io(buffer, codec, nid, wid_type); + if (wid_caps & AC_WCAP_DIGITAL) + print_digital_conv(buffer, codec, nid); + if (wid_caps & AC_WCAP_FORMAT_OVRD) { + snd_iprintf(buffer, " PCM:\n"); + print_pcm_caps(buffer, codec, nid); + } + break; } + if (wid_caps & AC_WCAP_UNSOL_CAP) + print_unsol_cap(buffer, codec, nid); + if (wid_caps & AC_WCAP_POWER) - snd_iprintf(buffer, " Power: 0x%x\n", - snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_POWER_STATE, - 0)); - - if (wid_caps & AC_WCAP_CONN_LIST) { - int c, curr = -1; - if (conn_len > 1 && wid_type != AC_WID_AUD_MIX) - curr = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONNECT_SEL, 0); - snd_iprintf(buffer, " Connection: %d\n", conn_len); - snd_iprintf(buffer, " "); - for (c = 0; c < conn_len; c++) { - snd_iprintf(buffer, " 0x%02x", conn[c]); - if (c == curr) - snd_iprintf(buffer, "*"); - } - snd_iprintf(buffer, "\n"); - } + print_power_state(buffer, codec, nid); + + if (wid_caps & AC_WCAP_DELAY) + snd_iprintf(buffer, " Delay: %d samples\n", + (wid_caps & AC_WCAP_DELAY) >> + AC_WCAP_DELAY_SHIFT); + + if (wid_caps & AC_WCAP_CONN_LIST) + print_conn_list(buffer, codec, nid, wid_type, + conn, conn_len); + + if (wid_caps & AC_WCAP_PROC_WID) + print_proc_caps(buffer, codec, nid); + + /* NID 0x20 == Realtek Define Registers */ + if (codec->vendor_id == 0x10ec && nid == 0x20) + print_realtek_coef(buffer, codec, nid); } snd_hda_power_down(codec); } diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 196ad3c..19f0884 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -19,7 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> @@ -79,6 +78,11 @@ struct ad198x_spec { #ifdef CONFIG_SND_HDA_POWER_SAVE struct hda_loopback_check loopback; #endif + /* for virtual master */ + hda_nid_t vmaster_nid; + u32 vmaster_tlv[4]; + const char **slave_vols; + const char **slave_sws; }; /* @@ -126,6 +130,32 @@ static int ad198x_init(struct hda_codec *codec) return 0; } +static const char *ad_slave_vols[] = { + "Front Playback Volume", + "Surround Playback Volume", + "Center Playback Volume", + "LFE Playback Volume", + "Side Playback Volume", + "Headphone Playback Volume", + "Mono Playback Volume", + "Speaker Playback Volume", + "IEC958 Playback Volume", + NULL +}; + +static const char *ad_slave_sws[] = { + "Front Playback Switch", + "Surround Playback Switch", + "Center Playback Switch", + "LFE Playback Switch", + "Side Playback Switch", + "Headphone Playback Switch", + "Mono Playback Switch", + "Speaker Playback Switch", + "IEC958 Playback Switch", + NULL +}; + static int ad198x_build_controls(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; @@ -147,6 +177,27 @@ static int ad198x_build_controls(struct hda_codec *codec) if (err < 0) return err; } + + /* if we have no master control, let's create it */ + if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { + snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, + HDA_OUTPUT, spec->vmaster_tlv); + err = snd_hda_add_vmaster(codec, "Master Playback Volume", + spec->vmaster_tlv, + (spec->slave_vols ? + spec->slave_vols : ad_slave_vols)); + if (err < 0) + return err; + } + if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { + err = snd_hda_add_vmaster(codec, "Master Playback Switch", + NULL, + (spec->slave_sws ? + spec->slave_sws : ad_slave_sws)); + if (err < 0) + return err; + } + return 0; } @@ -370,7 +421,7 @@ static int ad198x_eapd_put(struct snd_kcontrol *kcontrol, int invert = (kcontrol->private_value >> 8) & 1; hda_nid_t nid = kcontrol->private_value & 0xff; unsigned int eapd; - eapd = ucontrol->value.integer.value[0]; + eapd = !!ucontrol->value.integer.value[0]; if (invert) eapd = !eapd; if (eapd == spec->cur_eapd) @@ -833,27 +884,29 @@ static const char *ad1986a_models[AD1986A_MODELS] = { static struct snd_pci_quirk ad1986a_cfg_tbl[] = { SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK), SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD), SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD), + SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD), SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD), SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD), SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD), SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD), + SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP), SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK), SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK), SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP), SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK), SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK), SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK), + SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK), + SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD), SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK), SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP), SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_LAPTOP_EAPD), SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_LAPTOP_EAPD), SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_LAPTOP_EAPD), - SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK), SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA), + SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK), SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP), SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK), SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE), @@ -872,6 +925,13 @@ static struct hda_amp_list ad1986a_loopbacks[] = { }; #endif +static int is_jack_available(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int conf = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CONFIG_DEFAULT, 0); + return get_defcfg_connect(conf) != AC_JACK_PORT_NONE; +} + static int patch_ad1986a(struct hda_codec *codec) { struct ad198x_spec *spec; @@ -898,6 +958,7 @@ static int patch_ad1986a(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = ad1986a_loopbacks; #endif + spec->vmaster_nid = 0x1b; codec->patch_ops = ad198x_patch_ops; @@ -930,7 +991,8 @@ static int patch_ad1986a(struct hda_codec *codec) spec->multiout.max_channels = 2; spec->multiout.num_dacs = 1; spec->multiout.dac_nids = ad1986a_laptop_dac_nids; - spec->multiout.dig_out_nid = 0; + if (!is_jack_available(codec, 0x25)) + spec->multiout.dig_out_nid = 0; spec->input_mux = &ad1986a_laptop_eapd_capture_source; break; case AD1986A_LAPTOP_AUTOMUTE: @@ -941,7 +1003,8 @@ static int patch_ad1986a(struct hda_codec *codec) spec->multiout.max_channels = 2; spec->multiout.num_dacs = 1; spec->multiout.dac_nids = ad1986a_laptop_dac_nids; - spec->multiout.dig_out_nid = 0; + if (!is_jack_available(codec, 0x25)) + spec->multiout.dig_out_nid = 0; spec->input_mux = &ad1986a_laptop_eapd_capture_source; codec->patch_ops.unsol_event = ad1986a_hp_unsol_event; codec->patch_ops.init = ad1986a_hp_init; @@ -1020,6 +1083,8 @@ static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ad198x_spec *spec = codec->spec; + if (ucontrol->value.enumerated.item[0] > 1) + return -EINVAL; if (spec->spdif_route != ucontrol->value.enumerated.item[0]) { spec->spdif_route = ucontrol->value.enumerated.item[0]; snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0, @@ -1138,6 +1203,7 @@ static int patch_ad1983(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = ad1983_loopbacks; #endif + spec->vmaster_nid = 0x05; codec->patch_ops = ad198x_patch_ops; @@ -1496,14 +1562,14 @@ static const char *ad1981_models[AD1981_MODELS] = { }; static struct snd_pci_quirk ad1981_cfg_tbl[] = { + SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD), /* All HP models */ SND_PCI_QUIRK(0x103c, 0, "HP nx", AD1981_HP), - /* HP nx6320 (reversed SSID, H/W bug) */ - SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP), + SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA), /* Lenovo Thinkpad T60/X60/Z6xx */ SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1981_THINKPAD), - SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD), - SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA), + /* HP nx6320 (reversed SSID, H/W bug) */ + SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP), {} }; @@ -1534,6 +1600,7 @@ static int patch_ad1981(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = ad1981_loopbacks; #endif + spec->vmaster_nid = 0x05; codec->patch_ops = ad198x_patch_ops; @@ -1908,7 +1975,6 @@ static struct snd_kcontrol_new ad1988_capture_mixers[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -1965,6 +2031,8 @@ static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol, int change; val = ucontrol->value.enumerated.item[0]; + if (val > 3) + return -EINVAL; if (!val) { sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE, @@ -2079,6 +2147,8 @@ static struct hda_verb ad1988_6stack_init_verbs[] = { {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, {0x34, AC_VERB_SET_CONNECT_SEL, 0x0}, + /* Analog CD Input */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, { } }; @@ -2720,8 +2790,8 @@ static const char *ad1988_models[AD1988_MODEL_LAST] = { }; static struct snd_pci_quirk ad1988_cfg_tbl[] = { - SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG), SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG), + SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG), {} }; @@ -2843,6 +2913,7 @@ static int patch_ad1988(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = ad1988_loopbacks; #endif + spec->vmaster_nid = 0x04; return 0; } @@ -2919,7 +2990,6 @@ static struct snd_kcontrol_new ad1884_base_mixers[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -3009,6 +3079,20 @@ static struct hda_amp_list ad1884_loopbacks[] = { }; #endif +static const char *ad1884_slave_vols[] = { + "PCM Playback Volume", + "Mic Playback Volume", + "Mono Playback Volume", + "Front Mic Playback Volume", + "Mic Playback Volume", + "CD Playback Volume", + "Internal Mic Playback Volume", + "Docking Mic Playback Volume" + "Beep Playback Volume", + "IEC958 Playback Volume", + NULL +}; + static int patch_ad1884(struct hda_codec *codec) { struct ad198x_spec *spec; @@ -3036,6 +3120,9 @@ static int patch_ad1884(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = ad1884_loopbacks; #endif + spec->vmaster_nid = 0x04; + /* we need to cover all playback volumes */ + spec->slave_vols = ad1884_slave_vols; codec->patch_ops = ad198x_patch_ops; @@ -3054,6 +3141,20 @@ static struct hda_input_mux ad1984_thinkpad_capture_source = { }, }; + +/* + * Dell Precision T3400 + */ +static struct hda_input_mux ad1984_dell_desktop_capture_source = { + .num_items = 3, + .items = { + { "Front Mic", 0x0 }, + { "Line-In", 0x1 }, + { "Mix", 0x3 }, + }, +}; + + static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = { HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT), /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */ @@ -3078,7 +3179,6 @@ static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -3087,6 +3187,16 @@ static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = { .get = ad198x_mux_enum_get, .put = ad198x_mux_enum_put, }, + /* SPDIF controls */ + HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", + /* identical with ad1983 */ + .info = ad1983_spdif_route_info, + .get = ad1983_spdif_route_get, + .put = ad1983_spdif_route_put, + }, { } /* end */ }; @@ -3104,6 +3214,44 @@ static struct hda_verb ad1984_thinkpad_init_verbs[] = { { } /* end */ }; +/* + * Dell Precision T3400 + */ +static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = { + HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT), + /* + HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT), + */ + HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = ad198x_mux_enum_info, + .get = ad198x_mux_enum_get, + .put = ad198x_mux_enum_put, + }, + { } /* end */ +}; + /* Digial MIC ADC NID 0x05 + 0x06 */ static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, @@ -3157,17 +3305,20 @@ static int ad1984_build_pcms(struct hda_codec *codec) enum { AD1984_BASIC, AD1984_THINKPAD, + AD1984_DELL_DESKTOP, AD1984_MODELS }; static const char *ad1984_models[AD1984_MODELS] = { [AD1984_BASIC] = "basic", [AD1984_THINKPAD] = "thinkpad", + [AD1984_DELL_DESKTOP] = "dell_desktop", }; static struct snd_pci_quirk ad1984_cfg_tbl[] = { /* Lenovo Thinkpad T61/X61 */ SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1984_THINKPAD), + SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP), {} }; @@ -3189,11 +3340,16 @@ static int patch_ad1984(struct hda_codec *codec) codec->patch_ops.build_pcms = ad1984_build_pcms; break; case AD1984_THINKPAD: - spec->multiout.dig_out_nid = 0; + spec->multiout.dig_out_nid = AD1884_SPDIF_OUT; spec->input_mux = &ad1984_thinkpad_capture_source; spec->mixers[0] = ad1984_thinkpad_mixers; spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs; break; + case AD1984_DELL_DESKTOP: + spec->multiout.dig_out_nid = 0; + spec->input_mux = &ad1984_dell_desktop_capture_source; + spec->mixers[0] = ad1984_dell_desktop_mixers; + break; } return 0; } @@ -3267,7 +3423,6 @@ static struct snd_kcontrol_new ad1882_base_mixers[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -3468,6 +3623,7 @@ static int patch_ad1882(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = ad1882_loopbacks; #endif + spec->vmaster_nid = 0x04; codec->patch_ops = ad198x_patch_ops; diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c index fbb8969..9a8bb4c 100644 --- a/sound/pci/hda/patch_atihdmi.c +++ b/sound/pci/hda/patch_atihdmi.c @@ -21,7 +21,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> @@ -158,6 +157,6 @@ struct hda_codec_preset snd_hda_preset_atihdmi[] = { { .id = 0x1002793c, .name = "ATI RS600 HDMI", .patch = patch_atihdmi }, { .id = 0x10027919, .name = "ATI RS600 HDMI", .patch = patch_atihdmi }, { .id = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi }, - { .id = 0x1002aa01, .name = "ATI R600 HDMI", .patch = patch_atihdmi }, + { .id = 0x1002aa01, .name = "ATI R6xx HDMI", .patch = patch_atihdmi }, {} /* terminator */ }; diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index 6c54793..3d6097b 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -21,7 +21,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> @@ -186,7 +185,6 @@ static struct snd_kcontrol_new cmi9880_basic_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 6aa0739..f6dd51c 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> @@ -65,6 +64,11 @@ struct conexant_spec { hda_nid_t *adc_nids; hda_nid_t dig_in_nid; /* digital-in NID; optional */ + unsigned int cur_adc_idx; + hda_nid_t cur_adc; + unsigned int cur_adc_stream_tag; + unsigned int cur_adc_format; + /* capture source */ const struct hda_input_mux *input_mux; hda_nid_t *capsrc_nids; @@ -218,6 +222,41 @@ static struct hda_pcm_stream conexant_pcm_digital_capture = { /* NID is set in alc_build_pcms */ }; +static int cx5051_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct conexant_spec *spec = codec->spec; + spec->cur_adc = spec->adc_nids[spec->cur_adc_idx]; + spec->cur_adc_stream_tag = stream_tag; + spec->cur_adc_format = format; + snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); + return 0; +} + +static int cx5051_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct conexant_spec *spec = codec->spec; + snd_hda_codec_setup_stream(codec, spec->cur_adc, 0, 0, 0); + spec->cur_adc = 0; + return 0; +} + +static struct hda_pcm_stream cx5051_pcm_analog_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .nid = 0, /* fill later */ + .ops = { + .prepare = cx5051_capture_pcm_prepare, + .cleanup = cx5051_capture_pcm_cleanup + }, +}; + static int conexant_build_pcms(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; @@ -232,7 +271,12 @@ static int conexant_build_pcms(struct hda_codec *codec) spec->multiout.max_channels; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = conexant_pcm_analog_capture; + if (codec->vendor_id == 0x14f15051) + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + cx5051_pcm_analog_capture; + else + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + conexant_pcm_analog_capture; info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; @@ -373,7 +417,7 @@ static int cxt_eapd_put(struct snd_kcontrol *kcontrol, hda_nid_t nid = kcontrol->private_value & 0xff; unsigned int eapd; - eapd = ucontrol->value.integer.value[0]; + eapd = !!ucontrol->value.integer.value[0]; if (invert) eapd = !eapd; if (eapd == spec->cur_eapd) @@ -454,7 +498,16 @@ static struct hda_input_mux cxt5045_capture_source = { .num_items = 2, .items = { { "IntMic", 0x1 }, - { "LineIn", 0x2 }, + { "ExtMic", 0x2 }, + } +}; + +static struct hda_input_mux cxt5045_capture_source_benq = { + .num_items = 3, + .items = { + { "IntMic", 0x1 }, + { "ExtMic", 0x2 }, + { "LineIn", 0x3 }, } }; @@ -577,6 +630,15 @@ static struct snd_kcontrol_new cxt5045_mixers[] = { {} }; +static struct snd_kcontrol_new cxt5045_benq_mixers[] = { + HDA_CODEC_VOLUME("Line In Capture Volume", 0x1a, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("Line In Capture Switch", 0x1a, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("Line In Playback Volume", 0x17, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("Line In Playback Switch", 0x17, 0x3, HDA_INPUT), + + {} +}; + static struct hda_verb cxt5045_init_verbs[] = { /* Line in, Mic */ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, @@ -602,6 +664,30 @@ static struct hda_verb cxt5045_init_verbs[] = { { } /* end */ }; +static struct hda_verb cxt5045_benq_init_verbs[] = { + /* Int Mic, Mic */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 }, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 }, + /* Line In,HP, Amp */ + {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x10, AC_VERB_SET_CONNECT_SEL, 0x1}, + {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x11, AC_VERB_SET_CONNECT_SEL, 0x1}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* Record selector: Int mic */ + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x1}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, + AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17}, + /* SPDIF route: PCM */ + {0x13, AC_VERB_SET_CONNECT_SEL, 0x0}, + /* EAPD */ + {0x10, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ + { } /* end */ +}; static struct hda_verb cxt5045_hp_sense_init_verbs[] = { /* pin sensing on HP jack */ @@ -740,8 +826,10 @@ static int cxt5045_init(struct hda_codec *codec) enum { - CXT5045_LAPTOP, /* Laptops w/ EAPD support */ - CXT5045_FUJITSU, /* Laptops w/ EAPD support */ + CXT5045_LAPTOP_HPSENSE, + CXT5045_LAPTOP_MICSENSE, + CXT5045_LAPTOP_HPMICSENSE, + CXT5045_BENQ, #ifdef CONFIG_SND_DEBUG CXT5045_TEST, #endif @@ -749,23 +837,35 @@ enum { }; static const char *cxt5045_models[CXT5045_MODELS] = { - [CXT5045_LAPTOP] = "laptop", - [CXT5045_FUJITSU] = "fujitsu", + [CXT5045_LAPTOP_HPSENSE] = "laptop-hpsense", + [CXT5045_LAPTOP_MICSENSE] = "laptop-micsense", + [CXT5045_LAPTOP_HPMICSENSE] = "laptop-hpmicsense", + [CXT5045_BENQ] = "benq", #ifdef CONFIG_SND_DEBUG [CXT5045_TEST] = "test", #endif }; static struct snd_pci_quirk cxt5045_cfg_tbl[] = { - SND_PCI_QUIRK(0x103c, 0x30b7, "HP DV6000Z", CXT5045_LAPTOP), - SND_PCI_QUIRK(0x103c, 0x30bb, "HP DV8000", CXT5045_LAPTOP), - SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP), - SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP), - SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP), - SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP), - SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_FUJITSU), - SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP), - SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP), + SND_PCI_QUIRK(0x103c, 0x30a5, "HP", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x103c, 0x30b7, "HP DV6000Z", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x103c, 0x30bb, "HP DV8000", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV9533EG", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ), + SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE), + SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP_HPMICSENSE), + SND_PCI_QUIRK(0x1734, 0x110e, "Fujitsu V5505", CXT5045_LAPTOP_HPSENSE), + SND_PCI_QUIRK(0x1509, 0x1e40, "FIC", CXT5045_LAPTOP_HPMICSENSE), + SND_PCI_QUIRK(0x1509, 0x2f05, "FIC", CXT5045_LAPTOP_HPMICSENSE), + SND_PCI_QUIRK(0x1509, 0x2f06, "FIC", CXT5045_LAPTOP_HPMICSENSE), + SND_PCI_QUIRK(0x1631, 0xc106, "Packard Bell", CXT5045_LAPTOP_HPMICSENSE), + SND_PCI_QUIRK(0x1631, 0xc107, "Packard Bell", CXT5045_LAPTOP_HPMICSENSE), + SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP_HPSENSE), {} }; @@ -803,7 +903,7 @@ static int patch_cxt5045(struct hda_codec *codec) cxt5045_models, cxt5045_cfg_tbl); switch (board_config) { - case CXT5045_LAPTOP: + case CXT5045_LAPTOP_HPSENSE: codec->patch_ops.unsol_event = cxt5045_hp_unsol_event; spec->input_mux = &cxt5045_capture_source; spec->num_init_verbs = 2; @@ -811,20 +911,53 @@ static int patch_cxt5045(struct hda_codec *codec) spec->mixers[0] = cxt5045_mixers; codec->patch_ops.init = cxt5045_init; break; - case CXT5045_FUJITSU: + case CXT5045_LAPTOP_MICSENSE: spec->input_mux = &cxt5045_capture_source; spec->num_init_verbs = 2; spec->init_verbs[1] = cxt5045_mic_sense_init_verbs; spec->mixers[0] = cxt5045_mixers; codec->patch_ops.init = cxt5045_init; break; + default: + case CXT5045_LAPTOP_HPMICSENSE: + codec->patch_ops.unsol_event = cxt5045_hp_unsol_event; + spec->input_mux = &cxt5045_capture_source; + spec->num_init_verbs = 3; + spec->init_verbs[1] = cxt5045_hp_sense_init_verbs; + spec->init_verbs[2] = cxt5045_mic_sense_init_verbs; + spec->mixers[0] = cxt5045_mixers; + codec->patch_ops.init = cxt5045_init; + break; + case CXT5045_BENQ: + codec->patch_ops.unsol_event = cxt5045_hp_unsol_event; + spec->input_mux = &cxt5045_capture_source_benq; + spec->num_init_verbs = 1; + spec->init_verbs[0] = cxt5045_benq_init_verbs; + spec->mixers[0] = cxt5045_mixers; + spec->mixers[1] = cxt5045_benq_mixers; + spec->num_mixers = 2; + codec->patch_ops.init = cxt5045_init; + break; #ifdef CONFIG_SND_DEBUG case CXT5045_TEST: spec->input_mux = &cxt5045_test_capture_source; spec->mixers[0] = cxt5045_test_mixer; spec->init_verbs[0] = cxt5045_test_init_verbs; + break; + #endif } + + /* + * Fix max PCM level to 0 dB + * (originall it has 0x2b steps with 0dB offset 0x14) + */ + snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT, + (0x14 << AC_AMPCAP_OFFSET_SHIFT) | + (0x14 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (1 << AC_AMPCAP_MUTE_SHIFT)); + return 0; } @@ -933,13 +1066,13 @@ static void cxt5047_hp2_automute(struct hda_codec *codec) static void cxt5047_hp_automic(struct hda_codec *codec) { static struct hda_verb mic_jack_on[] = { - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, {} }; static struct hda_verb mic_jack_off[] = { - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, {} }; unsigned int present; @@ -956,8 +1089,7 @@ static void cxt5047_hp_automic(struct hda_codec *codec) static void cxt5047_hp_unsol_event(struct hda_codec *codec, unsigned int res) { - res >>= 26; - switch (res) { + switch (res >> 26) { case CONEXANT_HP_EVENT: cxt5047_hp_automute(codec); break; @@ -1166,6 +1298,17 @@ static struct snd_kcontrol_new cxt5047_test_mixer[] = { .get = conexant_mux_enum_get, .put = conexant_mux_enum_put, }, + HDA_CODEC_VOLUME("Input-1 Volume", 0x1a, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Input-1 Switch", 0x1a, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Input-2 Volume", 0x1a, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Input-2 Switch", 0x1a, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Input-3 Volume", 0x1a, 0x2, HDA_INPUT), + HDA_CODEC_MUTE("Input-3 Switch", 0x1a, 0x2, HDA_INPUT), + HDA_CODEC_VOLUME("Input-4 Volume", 0x1a, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("Input-4 Switch", 0x1a, 0x3, HDA_INPUT), + HDA_CODEC_VOLUME("Input-5 Volume", 0x1a, 0x4, HDA_INPUT), + HDA_CODEC_MUTE("Input-5 Switch", 0x1a, 0x4, HDA_INPUT), + { } /* end */ }; @@ -1255,9 +1398,9 @@ static const char *cxt5047_models[CXT5047_MODELS] = { static struct snd_pci_quirk cxt5047_cfg_tbl[] = { SND_PCI_QUIRK(0x103c, 0x30a0, "HP DV1000", CXT5047_LAPTOP), + SND_PCI_QUIRK(0x103c, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP), SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV2000T/DV3000T", CXT5047_LAPTOP), SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2000Z", CXT5047_LAPTOP), - SND_PCI_QUIRK(0x103c, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP), SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P100", CXT5047_LAPTOP_EAPD), {} }; @@ -1324,10 +1467,260 @@ static int patch_cxt5047(struct hda_codec *codec) return 0; } +/* Conexant 5051 specific */ +static hda_nid_t cxt5051_dac_nids[1] = { 0x10 }; +static hda_nid_t cxt5051_adc_nids[2] = { 0x14, 0x15 }; +#define CXT5051_SPDIF_OUT 0x1C +#define CXT5051_PORTB_EVENT 0x38 +#define CXT5051_PORTC_EVENT 0x39 + +static struct hda_channel_mode cxt5051_modes[1] = { + { 2, NULL }, +}; + +static void cxt5051_update_speaker(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + unsigned int pinctl; + pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0; + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + pinctl); +} + +/* turn on/off EAPD (+ mute HP) as a master switch */ +static int cxt5051_hp_master_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + + if (!cxt_eapd_put(kcontrol, ucontrol)) + return 0; + cxt5051_update_speaker(codec); + return 1; +} + +/* toggle input of built-in and mic jack appropriately */ +static void cxt5051_portb_automic(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x17, 0, + AC_VERB_GET_PIN_SENSE, 0) & + AC_PINSENSE_PRESENCE; + snd_hda_codec_write(codec, 0x14, 0, + AC_VERB_SET_CONNECT_SEL, + present ? 0x01 : 0x00); +} + +/* switch the current ADC according to the jack state */ +static void cxt5051_portc_automic(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + unsigned int present; + hda_nid_t new_adc; + + present = snd_hda_codec_read(codec, 0x18, 0, + AC_VERB_GET_PIN_SENSE, 0) & + AC_PINSENSE_PRESENCE; + if (present) + spec->cur_adc_idx = 1; + else + spec->cur_adc_idx = 0; + new_adc = spec->adc_nids[spec->cur_adc_idx]; + if (spec->cur_adc && spec->cur_adc != new_adc) { + /* stream is running, let's swap the current ADC */ + snd_hda_codec_setup_stream(codec, spec->cur_adc, 0, 0, 0); + spec->cur_adc = new_adc; + snd_hda_codec_setup_stream(codec, new_adc, + spec->cur_adc_stream_tag, 0, + spec->cur_adc_format); + } +} + +/* mute internal speaker if HP is plugged */ +static void cxt5051_hp_automute(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + + spec->hp_present = snd_hda_codec_read(codec, 0x16, 0, + AC_VERB_GET_PIN_SENSE, 0) & + AC_PINSENSE_PRESENCE; + cxt5051_update_speaker(codec); +} + +/* unsolicited event for HP jack sensing */ +static void cxt5051_hp_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + switch (res >> 26) { + case CONEXANT_HP_EVENT: + cxt5051_hp_automute(codec); + break; + case CXT5051_PORTB_EVENT: + cxt5051_portb_automic(codec); + break; + case CXT5051_PORTC_EVENT: + cxt5051_portc_automic(codec); + break; + } +} + +static struct snd_kcontrol_new cxt5051_mixers[] = { + HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Docking Mic Volume", 0x15, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Docking Mic Switch", 0x15, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = cxt_eapd_info, + .get = cxt_eapd_get, + .put = cxt5051_hp_master_sw_put, + .private_value = 0x1a, + }, + + {} +}; + +static struct snd_kcontrol_new cxt5051_hp_mixers[] = { + HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("External Mic Volume", 0x15, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("External Mic Switch", 0x15, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = cxt_eapd_info, + .get = cxt_eapd_get, + .put = cxt5051_hp_master_sw_put, + .private_value = 0x1a, + }, + + {} +}; + +static struct hda_verb cxt5051_init_verbs[] = { + /* Line in, Mic */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, + /* SPK */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* HP, Amp */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* DAC1 */ + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Record selector: Int mic */ + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44}, + /* SPDIF route: PCM */ + {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0}, + /* EAPD */ + {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ + {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, + {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT}, + {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTC_EVENT}, + { } /* end */ +}; + +/* initialize jack-sensing, too */ +static int cxt5051_init(struct hda_codec *codec) +{ + conexant_init(codec); + if (codec->patch_ops.unsol_event) { + cxt5051_hp_automute(codec); + cxt5051_portb_automic(codec); + cxt5051_portc_automic(codec); + } + return 0; +} + + +enum { + CXT5051_LAPTOP, /* Laptops w/ EAPD support */ + CXT5051_HP, /* no docking */ + CXT5051_MODELS +}; + +static const char *cxt5051_models[CXT5051_MODELS] = { + [CXT5051_LAPTOP] = "laptop", + [CXT5051_HP] = "hp", +}; + +static struct snd_pci_quirk cxt5051_cfg_tbl[] = { + SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", + CXT5051_LAPTOP), + SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP), + {} +}; + +static int patch_cxt5051(struct hda_codec *codec) +{ + struct conexant_spec *spec; + int board_config; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + mutex_init(&spec->amp_mutex); + codec->spec = spec; + + codec->patch_ops = conexant_patch_ops; + codec->patch_ops.init = cxt5051_init; + + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = ARRAY_SIZE(cxt5051_dac_nids); + spec->multiout.dac_nids = cxt5051_dac_nids; + spec->multiout.dig_out_nid = CXT5051_SPDIF_OUT; + spec->num_adc_nids = 1; /* not 2; via auto-mic switch */ + spec->adc_nids = cxt5051_adc_nids; + spec->num_mixers = 1; + spec->mixers[0] = cxt5051_mixers; + spec->num_init_verbs = 1; + spec->init_verbs[0] = cxt5051_init_verbs; + spec->spdif_route = 0; + spec->num_channel_mode = ARRAY_SIZE(cxt5051_modes); + spec->channel_mode = cxt5051_modes; + spec->cur_adc = 0; + spec->cur_adc_idx = 0; + + board_config = snd_hda_check_board_config(codec, CXT5051_MODELS, + cxt5051_models, + cxt5051_cfg_tbl); + switch (board_config) { + case CXT5051_HP: + codec->patch_ops.unsol_event = cxt5051_hp_unsol_event; + spec->mixers[0] = cxt5051_hp_mixers; + break; + default: + case CXT5051_LAPTOP: + codec->patch_ops.unsol_event = cxt5051_hp_unsol_event; + break; + } + + return 0; +} + + +/* + */ + struct hda_codec_preset snd_hda_preset_conexant[] = { { .id = 0x14f15045, .name = "CX20549 (Venice)", .patch = patch_cxt5045 }, { .id = 0x14f15047, .name = "CX20551 (Waikiki)", .patch = patch_cxt5047 }, + { .id = 0x14f15051, .name = "CX20561 (Hermosa)", + .patch = patch_cxt5051 }, {} /* terminator */ }; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1c50278..586d98f 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -23,7 +23,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> @@ -92,9 +91,12 @@ enum { ALC262_HP_BPC, ALC262_HP_BPC_D7000_WL, ALC262_HP_BPC_D7000_WF, + ALC262_HP_TC_T5735, + ALC262_HP_RP5700, ALC262_BENQ_ED8, ALC262_SONY_ASSAMD, ALC262_BENQ_T31, + ALC262_ULTRA, ALC262_AUTO, ALC262_MODEL_LAST /* last tag */ }; @@ -104,10 +106,21 @@ enum { ALC268_3ST, ALC268_TOSHIBA, ALC268_ACER, + ALC268_DELL, +#ifdef CONFIG_SND_DEBUG + ALC268_TEST, +#endif ALC268_AUTO, ALC268_MODEL_LAST /* last tag */ }; +/* ALC269 models */ +enum { + ALC269_BASIC, + ALC269_AUTO, + ALC269_MODEL_LAST /* last tag */ +}; + /* ALC861 models */ enum { ALC861_3ST, @@ -144,6 +157,7 @@ enum { ALC662_5ST_DIG, ALC662_LENOVO_101E, ALC662_ASUS_EEEPC_P701, + ALC662_ASUS_EEEPC_EP20, ALC662_AUTO, ALC662_MODEL_LAST, }; @@ -183,6 +197,8 @@ enum { ALC883_HAIER_W66, ALC888_6ST_HP, ALC888_3ST_HP, + ALC888_6ST_DELL, + ALC883_MITAC, ALC883_AUTO, ALC883_MODEL_LAST, }; @@ -204,6 +220,8 @@ struct alc_spec { char *stream_name_analog; /* analog PCM stream */ struct hda_pcm_stream *stream_analog_playback; struct hda_pcm_stream *stream_analog_capture; + struct hda_pcm_stream *stream_analog_alt_playback; + struct hda_pcm_stream *stream_analog_alt_capture; char *stream_name_digital; /* digital PCM stream */ struct hda_pcm_stream *stream_digital_playback; @@ -214,6 +232,7 @@ struct alc_spec { * max_channels, dacs must be set * dig_out_nid and hp_nid are optional */ + hda_nid_t alt_dac_nid; /* capture */ unsigned int num_adc_nids; @@ -247,7 +266,11 @@ struct alc_spec { /* for pin sensing */ unsigned int sense_updated: 1; unsigned int jack_present: 1; + unsigned int master_sw: 1; + /* for virtual master */ + hda_nid_t vmaster_nid; + u32 vmaster_tlv[4]; #ifdef CONFIG_SND_HDA_POWER_SAVE struct hda_loopback_check loopback; #endif @@ -562,7 +585,7 @@ static int alc_spdif_ctrl_get(struct snd_kcontrol *kcontrol, unsigned char mask = (kcontrol->private_value >> 16) & 0xff; long *valp = ucontrol->value.integer.value; unsigned int val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_DIGI_CONVERT, 0x00); + AC_VERB_GET_DIGI_CONVERT_1, 0x00); *valp = (val & mask) != 0; return 0; @@ -576,7 +599,7 @@ static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol, unsigned char mask = (kcontrol->private_value >> 16) & 0xff; long val = *ucontrol->value.integer.value; unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_DIGI_CONVERT, + AC_VERB_GET_DIGI_CONVERT_1, 0x00); /* Set/unset the masked control bit(s) as needed */ @@ -598,6 +621,59 @@ static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol, .private_value = nid | (mask<<16) } #endif /* CONFIG_SND_DEBUG */ +/* A switch control to allow the enabling EAPD digital outputs on the ALC26x. + * Again, this is only used in the ALC26x test models to help identify when + * the EAPD line must be asserted for features to work. + */ +#ifdef CONFIG_SND_DEBUG +#define alc_eapd_ctrl_info snd_ctl_boolean_mono_info + +static int alc_eapd_ctrl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value & 0xffff; + unsigned char mask = (kcontrol->private_value >> 16) & 0xff; + long *valp = ucontrol->value.integer.value; + unsigned int val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_EAPD_BTLENABLE, 0x00); + + *valp = (val & mask) != 0; + return 0; +} + +static int alc_eapd_ctrl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int change; + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + hda_nid_t nid = kcontrol->private_value & 0xffff; + unsigned char mask = (kcontrol->private_value >> 16) & 0xff; + long val = *ucontrol->value.integer.value; + unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_EAPD_BTLENABLE, + 0x00); + + /* Set/unset the masked control bit(s) as needed */ + change = (!val ? 0 : mask) != (ctrl_data & mask); + if (!val) + ctrl_data &= ~mask; + else + ctrl_data |= mask; + snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE, + ctrl_data); + + return change; +} + +#define ALC_EAPD_CTRL_SWITCH(xname, nid, mask) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ + .info = alc_eapd_ctrl_info, \ + .get = alc_eapd_ctrl_get, \ + .put = alc_eapd_ctrl_put, \ + .private_value = nid | (mask<<16) } +#endif /* CONFIG_SND_DEBUG */ + /* * set up from the preset table */ @@ -739,7 +815,7 @@ static void alc_subsystem_id(struct hda_codec *codec, /* check sum */ tmp = 0; for (i = 1; i < 16; i++) { - if ((ass >> i) && 1) + if ((ass >> i) & 1) tmp++; } if (((ass >> 16) & 0xf) != tmp) @@ -828,10 +904,10 @@ do_sku: break; } - /* is laptop and enable the function "Mute internal speaker + /* is laptop or Desktop and enable the function "Mute internal speaker * when the external headphone out jack is plugged" */ - if (!(ass & 0x4) || !(ass & 0x8000)) + if (!(ass & 0x8000)) return; /* * 10~8 : Jack location @@ -841,9 +917,9 @@ do_sku: * when the external headphone out jack is plugged" */ if (!spec->autocfg.speaker_pins[0]) { - if (spec->multiout.dac_nids[0]) + if (spec->autocfg.line_out_pins[0]) spec->autocfg.speaker_pins[0] = - spec->multiout.dac_nids[0]; + spec->autocfg.line_out_pins[0]; else return; } @@ -1009,7 +1085,6 @@ static struct snd_kcontrol_new alc880_capture_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -1031,7 +1106,6 @@ static struct snd_kcontrol_new alc880_capture_alt_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -1226,7 +1300,6 @@ static struct snd_kcontrol_new alc880_z71v_mixer[] = { }; -/* FIXME! */ /* * ALC880 F1734 model * @@ -1242,8 +1315,8 @@ static hda_nid_t alc880_f1734_dac_nids[1] = { static struct snd_kcontrol_new alc880_f1734_mixer[] = { HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Internal Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Internal Speaker Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), @@ -1252,7 +1325,6 @@ static struct snd_kcontrol_new alc880_f1734_mixer[] = { }; -/* FIXME! */ /* * ALC880 ASUS model * @@ -1289,7 +1361,6 @@ static struct snd_kcontrol_new alc880_asus_mixer[] = { { } /* end */ }; -/* FIXME! */ /* * ALC880 ASUS W1V model * @@ -1327,7 +1398,6 @@ static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -1341,10 +1411,10 @@ static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = { /* Uniwill */ static struct snd_kcontrol_new alc880_uniwill_mixer[] = { - HDA_CODEC_VOLUME("HPhone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("HPhone Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("iSpeaker Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), @@ -1384,16 +1454,49 @@ static struct snd_kcontrol_new alc880_fujitsu_mixer[] = { }; static struct snd_kcontrol_new alc880_uniwill_p53_mixer[] = { - HDA_CODEC_VOLUME("HPhone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("HPhone Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("iSpeaker Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), { } /* end */ }; /* + * virtual master controls + */ + +/* + * slave controls for virtual master + */ +static const char *alc_slave_vols[] = { + "Front Playback Volume", + "Surround Playback Volume", + "Center Playback Volume", + "LFE Playback Volume", + "Side Playback Volume", + "Headphone Playback Volume", + "Speaker Playback Volume", + "Mono Playback Volume", + "Line-Out Playback Volume", + NULL, +}; + +static const char *alc_slave_sws[] = { + "Front Playback Switch", + "Surround Playback Switch", + "Center Playback Switch", + "LFE Playback Switch", + "Side Playback Switch", + "Headphone Playback Switch", + "Speaker Playback Switch", + "Mono Playback Switch", + "IEC958 Playback Switch", + NULL, +}; + +/* * build control elements */ static int alc_build_controls(struct hda_codec *codec) @@ -1419,6 +1522,23 @@ static int alc_build_controls(struct hda_codec *codec) if (err < 0) return err; } + + /* if we have no master control, let's create it */ + if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { + snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, + HDA_OUTPUT, spec->vmaster_tlv); + err = snd_hda_add_vmaster(codec, "Master Playback Volume", + spec->vmaster_tlv, alc_slave_vols); + if (err < 0) + return err; + } + if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { + err = snd_hda_add_vmaster(codec, "Master Playback Switch", + NULL, alc_slave_sws); + if (err < 0) + return err; + } + return 0; } @@ -1790,7 +1910,6 @@ static void alc880_uniwill_p53_unsol_event(struct hda_codec *codec, alc880_uniwill_p53_dcvol_automute(codec); } -/* FIXME! */ /* * F1734 pin configuration: * HP = 0x14, speaker-out = 0x15, mic = 0x18 @@ -1819,7 +1938,6 @@ static struct hda_verb alc880_pin_f1734_init_verbs[] = { { } }; -/* FIXME! */ /* * ASUS pin configuration: * HP/front = 0x14, surr = 0x15, clfe = 0x16, mic = 0x18, line = 0x1a @@ -1966,9 +2084,8 @@ static struct hda_channel_mode alc880_lg_ch_modes[3] = { }; static struct snd_kcontrol_new alc880_lg_mixer[] = { - /* FIXME: it's not really "master" but front channels */ - HDA_CODEC_VOLUME("Master Playback Volume", 0x0f, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Master Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Front Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0f, 2, HDA_INPUT), HDA_CODEC_VOLUME("Surround Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Surround Playback Switch", 0x0c, 2, HDA_INPUT), HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT), @@ -2256,7 +2373,7 @@ static int alc880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, /* * Analog capture */ -static int alc880_capture_pcm_prepare(struct hda_pcm_stream *hinfo, +static int alc880_alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, unsigned int stream_tag, unsigned int format, @@ -2264,18 +2381,18 @@ static int alc880_capture_pcm_prepare(struct hda_pcm_stream *hinfo, { struct alc_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], + snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1], stream_tag, 0, format); return 0; } -static int alc880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, +static int alc880_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, struct hda_codec *codec, struct snd_pcm_substream *substream) { struct alc_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], + snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1], 0, 0, 0); return 0; } @@ -2296,13 +2413,27 @@ static struct hda_pcm_stream alc880_pcm_analog_playback = { }; static struct hda_pcm_stream alc880_pcm_analog_capture = { - .substreams = 2, + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in alc_build_pcms */ +}; + +static struct hda_pcm_stream alc880_pcm_analog_alt_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in alc_build_pcms */ +}; + +static struct hda_pcm_stream alc880_pcm_analog_alt_capture = { + .substreams = 2, /* can be overridden */ .channels_min = 2, .channels_max = 2, /* NID is set in alc_build_pcms */ .ops = { - .prepare = alc880_capture_pcm_prepare, - .cleanup = alc880_capture_pcm_cleanup + .prepare = alc880_alt_capture_pcm_prepare, + .cleanup = alc880_alt_capture_pcm_cleanup }, }; @@ -2326,7 +2457,7 @@ static struct hda_pcm_stream alc880_pcm_digital_capture = { }; /* Used by alc_build_pcms to flag that a PCM has no playback stream */ -static struct hda_pcm_stream alc_pcm_null_playback = { +static struct hda_pcm_stream alc_pcm_null_stream = { .substreams = 0, .channels_min = 0, .channels_max = 0, @@ -2383,17 +2514,32 @@ static int alc_build_pcms(struct hda_codec *codec) * model, configure a second analog capture-only PCM. */ /* Additional Analaog capture for index #2 */ - if (spec->num_adc_nids > 1 && spec->stream_analog_capture && - spec->adc_nids) { + if ((spec->alt_dac_nid && spec->stream_analog_alt_playback) || + (spec->num_adc_nids > 1 && spec->stream_analog_alt_capture)) { codec->num_pcms = 3; info = spec->pcm_rec + 2; info->name = spec->stream_name_analog; - /* No playback stream for second PCM */ - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = alc_pcm_null_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0; - if (spec->stream_analog_capture) { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture); - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[1]; + if (spec->alt_dac_nid) { + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + *spec->stream_analog_alt_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = + spec->alt_dac_nid; + } else { + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + alc_pcm_null_stream; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0; + } + if (spec->num_adc_nids > 1) { + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + *spec->stream_analog_alt_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = + spec->adc_nids[1]; + info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = + spec->num_adc_nids - 1; + } else { + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + alc_pcm_null_stream; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0; } } @@ -2723,23 +2869,17 @@ static const char *alc880_models[ALC880_MODEL_LAST] = { }; static struct snd_pci_quirk alc880_cfg_tbl[] = { - /* Broken BIOS configuration */ - SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_6ST_DIG), - SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_6ST_DIG), - + SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_W810), SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_5ST_DIG), SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_6ST), - SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_W810), SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_3ST_DIG), SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_6ST_DIG), SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_6ST_DIG), SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_6ST_DIG), SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_3ST_DIG), SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_3ST), - SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_6ST_DIG), SND_PCI_QUIRK(0x103c, 0x2a09, "HP", ALC880_5ST), - SND_PCI_QUIRK(0x1043, 0x10b3, "ASUS W1V", ALC880_ASUS_W1V), SND_PCI_QUIRK(0x1043, 0x10c2, "ASUS W6A", ALC880_ASUS_DIG), SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS Wxx", ALC880_ASUS_DIG), @@ -2754,54 +2894,50 @@ static struct snd_pci_quirk alc880_cfg_tbl[] = { SND_PCI_QUIRK(0x1043, 0x8181, "ASUS P4GPL", ALC880_ASUS_DIG), SND_PCI_QUIRK(0x1043, 0x8196, "ASUS P5GD1", ALC880_6ST), SND_PCI_QUIRK(0x1043, 0x81b4, "ASUS", ALC880_6ST), - SND_PCI_QUIRK(0x1043, 0, "ASUS", ALC880_ASUS), - - SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_3ST), + SND_PCI_QUIRK(0x1043, 0, "ASUS", ALC880_ASUS), /* default ASUS */ SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_3ST), + SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_3ST), + SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_5ST), SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_5ST), SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_5ST), - SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_5ST), - SND_PCI_QUIRK(0x1558, 0x0520, "Clevo m520G", ALC880_CLEVO), - SND_PCI_QUIRK(0x1558, 0x0660, "Clevo m655n", ALC880_CLEVO), - SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810), - SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_TCL_S700), - SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_5ST_DIG), - SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_6ST_DIG), SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_6ST_DIG), SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_6ST_DIG), SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_6ST_DIG), + SND_PCI_QUIRK(0x1558, 0x0520, "Clevo m520G", ALC880_CLEVO), + SND_PCI_QUIRK(0x1558, 0x0660, "Clevo m655n", ALC880_CLEVO), SND_PCI_QUIRK(0x1558, 0x5401, "ASUS", ALC880_ASUS_DIG2), - + SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_5ST_DIG), SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_UNIWILL_DIG), + SND_PCI_QUIRK(0x1584, 0x9054, "Uniwlll", ALC880_F1734), SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_UNIWILL), SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_UNIWILL_P53), - SND_PCI_QUIRK(0x1584, 0x9054, "Uniwlll", ALC880_F1734), - + SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810), + SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG), SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x1734, 0x10ac, "FSC", ALC880_UNIWILL), SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_F1734), + SND_PCI_QUIRK(0x1734, 0x10ac, "FSC", ALC880_UNIWILL), SND_PCI_QUIRK(0x1734, 0x10b0, "Fujitsu", ALC880_FUJITSU), - + SND_PCI_QUIRK(0x1854, 0x0018, "LG LW20", ALC880_LG_LW), SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_LG), SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_LG), - SND_PCI_QUIRK(0x1854, 0x0018, "LG LW20", ALC880_LG_LW), SND_PCI_QUIRK(0x1854, 0x0077, "LG LW25", ALC880_LG_LW), - - SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_3ST_DIG), - SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_3ST_DIG), - SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_3ST_DIG), + SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_TCL_S700), + SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_6ST_DIG), /* broken BIOS */ + SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_6ST_DIG), + SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_5ST_DIG), SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_5ST_DIG), SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_3ST_DIG), SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_5ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_3ST_DIG), + SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_3ST_DIG), SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_5ST_DIG), SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_5ST_DIG), SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_5ST_DIG), - SND_PCI_QUIRK(0x8086, 0, "Intel mobo", ALC880_3ST), - + SND_PCI_QUIRK(0x8086, 0, "Intel mobo", ALC880_3ST), /* default Intel */ + SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_5ST_DIG), + SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_6ST_DIG), {} }; @@ -3511,6 +3647,7 @@ static int patch_alc880(struct hda_codec *codec) spec->stream_name_analog = "ALC880 Analog"; spec->stream_analog_playback = &alc880_pcm_analog_playback; spec->stream_analog_capture = &alc880_pcm_analog_capture; + spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture; spec->stream_name_digital = "ALC880 Digital"; spec->stream_digital_playback = &alc880_pcm_digital_playback; @@ -3535,6 +3672,8 @@ static int patch_alc880(struct hda_codec *codec) } } + spec->vmaster_nid = 0x0c; + codec->patch_ops = alc_patch_ops; if (board_config == ALC880_AUTO) spec->init_hook = alc880_auto_init; @@ -3691,18 +3830,135 @@ static struct snd_kcontrol_new alc260_pc_beep_mixer[] = { { } /* end */ }; +/* update HP, line and mono out pins according to the master switch */ +static void alc260_hp_master_update(struct hda_codec *codec, + hda_nid_t hp, hda_nid_t line, + hda_nid_t mono) +{ + struct alc_spec *spec = codec->spec; + unsigned int val = spec->master_sw ? PIN_HP : 0; + /* change HP and line-out pins */ + snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + val); + snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + val); + /* mono (speaker) depending on the HP jack sense */ + val = (val && !spec->jack_present) ? PIN_OUT : 0; + snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + val); +} + +static int alc260_hp_master_sw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + *ucontrol->value.integer.value = spec->master_sw; + return 0; +} + +static int alc260_hp_master_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + int val = !!*ucontrol->value.integer.value; + hda_nid_t hp, line, mono; + + if (val == spec->master_sw) + return 0; + spec->master_sw = val; + hp = (kcontrol->private_value >> 16) & 0xff; + line = (kcontrol->private_value >> 8) & 0xff; + mono = kcontrol->private_value & 0xff; + alc260_hp_master_update(codec, hp, line, mono); + return 1; +} + +static struct snd_kcontrol_new alc260_hp_output_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = snd_ctl_boolean_mono_info, + .get = alc260_hp_master_sw_get, + .put = alc260_hp_master_sw_put, + .private_value = (0x0f << 16) | (0x10 << 8) | 0x11 + }, + HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0, + HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Speaker Playback Switch", 0x0a, 1, 2, HDA_INPUT), + { } /* end */ +}; + +static struct hda_verb alc260_hp_unsol_verbs[] = { + {0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, + {}, +}; + +static void alc260_hp_automute(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + unsigned int present; + + present = snd_hda_codec_read(codec, 0x10, 0, + AC_VERB_GET_PIN_SENSE, 0); + spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; + alc260_hp_master_update(codec, 0x0f, 0x10, 0x11); +} + +static void alc260_hp_unsol_event(struct hda_codec *codec, unsigned int res) +{ + if ((res >> 26) == ALC880_HP_EVENT) + alc260_hp_automute(codec); +} + static struct snd_kcontrol_new alc260_hp_3013_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = snd_ctl_boolean_mono_info, + .get = alc260_hp_master_sw_get, + .put = alc260_hp_master_sw_put, + .private_value = (0x10 << 16) | (0x15 << 8) | 0x11 + }, HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Aux-In Playback Volume", 0x07, 0x06, HDA_INPUT), HDA_CODEC_MUTE("Aux-In Playback Switch", 0x07, 0x06, HDA_INPUT), HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("iSpeaker Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("iSpeaker Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT), { } /* end */ }; +static struct hda_verb alc260_hp_3013_unsol_verbs[] = { + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, + {}, +}; + +static void alc260_hp_3013_automute(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + unsigned int present; + + present = snd_hda_codec_read(codec, 0x15, 0, + AC_VERB_GET_PIN_SENSE, 0); + spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; + alc260_hp_master_update(codec, 0x10, 0x15, 0x11); +} + +static void alc260_hp_3013_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) == ALC880_HP_EVENT) + alc260_hp_3013_automute(codec); +} + /* Fujitsu S702x series laptops. ALC260 pin usage: Mic/Line jack = 0x12, * HP jack = 0x14, CD audio = 0x16, internal speaker = 0x10. */ @@ -3812,7 +4068,6 @@ static struct snd_kcontrol_new alc260_capture_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -3831,7 +4086,6 @@ static struct snd_kcontrol_new alc260_capture_alt_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -4332,6 +4586,12 @@ static struct snd_kcontrol_new alc260_test_mixer[] = { ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x03, 0x01), ALC_SPDIF_CTRL_SWITCH("SPDIF Capture Switch", 0x06, 0x01), + /* A switch allowing EAPD to be enabled. Some laptops seem to use + * this output to turn on an external amplifier. + */ + ALC_EAPD_CTRL_SWITCH("LINE-OUT EAPD Enable Switch", 0x0f, 0x02), + ALC_EAPD_CTRL_SWITCH("HP-OUT EAPD Enable Switch", 0x10, 0x02), + { } /* end */ }; static struct hda_verb alc260_test_init_verbs[] = { @@ -4417,17 +4677,8 @@ static struct hda_verb alc260_test_init_verbs[] = { }; #endif -static struct hda_pcm_stream alc260_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, -}; - -static struct hda_pcm_stream alc260_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, -}; +#define alc260_pcm_analog_playback alc880_pcm_analog_alt_playback +#define alc260_pcm_analog_capture alc880_pcm_analog_capture #define alc260_pcm_digital_playback alc880_pcm_digital_playback #define alc260_pcm_digital_capture alc880_pcm_digital_capture @@ -4744,8 +4995,8 @@ static struct snd_pci_quirk alc260_cfg_tbl[] = { SND_PCI_QUIRK(0x104d, 0x81cd, "Sony VAIO", ALC260_BASIC), SND_PCI_QUIRK(0x10cf, 0x1326, "Fujitsu S702X", ALC260_FUJITSU_S702X), SND_PCI_QUIRK(0x152d, 0x0729, "CTL U553W", ALC260_BASIC), - SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_WILL), SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_REPLACER_672V), + SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_WILL), {} }; @@ -4765,10 +5016,11 @@ static struct alc_config_preset alc260_presets[] = { .input_mux = &alc260_capture_source, }, [ALC260_HP] = { - .mixers = { alc260_base_output_mixer, + .mixers = { alc260_hp_output_mixer, alc260_input_mixer, alc260_capture_alt_mixer }, - .init_verbs = { alc260_init_verbs }, + .init_verbs = { alc260_init_verbs, + alc260_hp_unsol_verbs }, .num_dacs = ARRAY_SIZE(alc260_dac_nids), .dac_nids = alc260_dac_nids, .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids), @@ -4776,12 +5028,15 @@ static struct alc_config_preset alc260_presets[] = { .num_channel_mode = ARRAY_SIZE(alc260_modes), .channel_mode = alc260_modes, .input_mux = &alc260_capture_source, + .unsol_event = alc260_hp_unsol_event, + .init_hook = alc260_hp_automute, }, [ALC260_HP_3013] = { .mixers = { alc260_hp_3013_mixer, alc260_input_mixer, alc260_capture_alt_mixer }, - .init_verbs = { alc260_hp_3013_init_verbs }, + .init_verbs = { alc260_hp_3013_init_verbs, + alc260_hp_3013_unsol_verbs }, .num_dacs = ARRAY_SIZE(alc260_dac_nids), .dac_nids = alc260_dac_nids, .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids), @@ -4789,6 +5044,8 @@ static struct alc_config_preset alc260_presets[] = { .num_channel_mode = ARRAY_SIZE(alc260_modes), .channel_mode = alc260_modes, .input_mux = &alc260_capture_source, + .unsol_event = alc260_hp_3013_unsol_event, + .init_hook = alc260_hp_3013_automute, }, [ALC260_FUJITSU_S702X] = { .mixers = { alc260_fujitsu_mixer, @@ -4906,6 +5163,8 @@ static int patch_alc260(struct hda_codec *codec) spec->stream_digital_playback = &alc260_pcm_digital_playback; spec->stream_digital_capture = &alc260_pcm_digital_capture; + spec->vmaster_nid = 0x08; + codec->patch_ops = alc_patch_ops; if (board_config == ALC260_AUTO) spec->init_hook = alc260_auto_init; @@ -5106,15 +5365,15 @@ static struct snd_kcontrol_new alc882_base_mixer[] = { }; static struct snd_kcontrol_new alc885_mbp3_mixer[] = { - HDA_CODEC_VOLUME("Master Volume", 0x0c, 0x00, HDA_OUTPUT), - HDA_BIND_MUTE ("Master Switch", 0x0c, 0x02, HDA_INPUT), - HDA_CODEC_MUTE ("Speaker Switch", 0x14, 0x00, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line Out Volume", 0x0d,0x00, HDA_OUTPUT), - HDA_CODEC_VOLUME("Line In Playback Volume", 0x0b, 0x02, HDA_INPUT), - HDA_CODEC_MUTE ("Line In Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE ("Front Playback Switch", 0x0c, 0x02, HDA_INPUT), + HDA_CODEC_MUTE ("Speaker Playback Switch", 0x14, 0x00, HDA_OUTPUT), + HDA_CODEC_VOLUME("Line-Out Playback Volume", 0x0d, 0x00, HDA_OUTPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE ("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x00, HDA_INPUT), HDA_CODEC_MUTE ("Mic Playback Switch", 0x0b, 0x00, HDA_INPUT), - HDA_CODEC_VOLUME("Line In Boost", 0x1a, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Line Boost", 0x1a, 0x00, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x00, HDA_INPUT), { } /* end */ }; @@ -5679,7 +5938,6 @@ static struct snd_kcontrol_new alc882_capture_alt_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -5702,7 +5960,6 @@ static struct snd_kcontrol_new alc882_capture_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -5743,16 +6000,17 @@ static const char *alc882_models[ALC882_MODEL_LAST] = { static struct snd_pci_quirk alc882_cfg_tbl[] = { SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC882_6ST_DIG), - SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8 */ - SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA), SND_PCI_QUIRK(0x1043, 0x060d, "Asus A7J", ALC882_ASUS_A7J), SND_PCI_QUIRK(0x1043, 0x1243, "Asus A7J", ALC882_ASUS_A7J), SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_ASUS_A7M), + SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC), SND_PCI_QUIRK(0x1043, 0x817f, "Asus P5LD2", ALC882_6ST_DIG), SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG), - SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC), + SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG), + SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P35 DS3R", ALC882_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8 */ + SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG), + SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA), {} }; @@ -5990,7 +6248,7 @@ static int alc_auto_add_mic_boost(struct hda_codec *codec) hda_nid_t nid; nid = spec->autocfg.input_pins[AUTO_PIN_MIC]; - if (nid) { + if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) { err = add_control(spec, ALC_CTL_WIDGET_VOL, "Mic Boost", HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT)); @@ -5998,7 +6256,7 @@ static int alc_auto_add_mic_boost(struct hda_codec *codec) return err; } nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC]; - if (nid) { + if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) { err = add_control(spec, ALC_CTL_WIDGET_VOL, "Front Mic Boost", HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT)); @@ -6061,6 +6319,7 @@ static int patch_alc882(struct hda_codec *codec) case 0x106b1000: /* iMac 24 */ board_config = ALC885_IMAC24; break; + case 0x106b00a1: /* Macbook */ case 0x106b2c00: /* Macbook Pro rev3 */ board_config = ALC885_MBP3; break; @@ -6093,6 +6352,9 @@ static int patch_alc882(struct hda_codec *codec) spec->stream_name_analog = "ALC882 Analog"; spec->stream_analog_playback = &alc882_pcm_analog_playback; spec->stream_analog_capture = &alc882_pcm_analog_capture; + /* FIXME: setup DAC5 */ + /*spec->stream_analog_alt_playback = &alc880_pcm_analog_alt_playback;*/ + spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture; spec->stream_name_digital = "ALC882 Digital"; spec->stream_digital_playback = &alc882_pcm_digital_playback; @@ -6117,6 +6379,8 @@ static int patch_alc882(struct hda_codec *codec) } } + spec->vmaster_nid = 0x0c; + codec->patch_ops = alc_patch_ops; if (board_config == ALC882_AUTO) spec->init_hook = alc882_auto_init; @@ -6340,6 +6604,36 @@ static struct snd_kcontrol_new alc883_base_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new alc883_mitac_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = alc883_mux_enum_info, + .get = alc883_mux_enum_get, + .put = alc883_mux_enum_put, + }, + { } /* end */ +}; + static struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), @@ -6508,8 +6802,8 @@ static struct snd_kcontrol_new alc883_tagra_2ch_mixer[] = { static struct snd_kcontrol_new alc883_lenovo_101e_2ch_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), - HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("iSpeaker Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), @@ -6658,6 +6952,46 @@ static struct snd_kcontrol_new alc888_3st_hp_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new alc888_6st_dell_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = alc883_mux_enum_info, + .get = alc883_mux_enum_get, + .put = alc883_mux_enum_put, + }, + { } /* end */ +}; + static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), @@ -6772,6 +7106,67 @@ static struct hda_verb alc883_init_verbs[] = { { } }; +/* toggle speaker-output according to the hp-jack state */ +static void alc883_mitac_hp_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x15, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); +} + +/* auto-toggle front mic */ +/* +static void alc883_mitac_mic_automute(struct hda_codec *codec) +{ + unsigned int present; + unsigned char bits; + + present = snd_hda_codec_read(codec, 0x18, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits); +} +*/ + +static void alc883_mitac_automute(struct hda_codec *codec) +{ + alc883_mitac_hp_automute(codec); + /* alc883_mitac_mic_automute(codec); */ +} + +static void alc883_mitac_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + switch (res >> 26) { + case ALC880_HP_EVENT: + alc883_mitac_hp_automute(codec); + break; + case ALC880_MIC_EVENT: + /* alc883_mitac_mic_automute(codec); */ + break; + } +} + +static struct hda_verb alc883_mitac_verbs[] = { + /* HP */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* Subwoofer */ + {0x17, AC_VERB_SET_CONNECT_SEL, 0x02}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + /* enable unsolicited event */ + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + /* {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, */ + + { } /* end */ +}; + static struct hda_verb alc883_tagra_verbs[] = { {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, @@ -6843,6 +7238,15 @@ static struct hda_verb alc888_3st_hp_verbs[] = { { } }; +static struct hda_verb alc888_6st_dell_verbs[] = { + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x02}, /* Rear : output 1 (0x0e) */ + {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* CLFE : output 2 (0x0d) */ + {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, /* Side : output 3 (0x0f) */ + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + { } +}; + static struct hda_verb alc888_3st_hp_2ch_init[] = { { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, @@ -7038,6 +7442,33 @@ static struct hda_verb alc883_acer_eapd_verbs[] = { { } }; +static void alc888_6st_dell_front_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x1b, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); +} + +static void alc888_6st_dell_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + switch (res >> 26) { + case ALC880_HP_EVENT: + printk("hp_event\n"); + alc888_6st_dell_front_automute(codec); + break; + } +} + /* * generic initialization of ADC, input mixers and output mixers */ @@ -7096,7 +7527,7 @@ static struct hda_verb alc883_auto_init_verbs[] = { {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, /* {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, { } }; @@ -7111,7 +7542,6 @@ static struct snd_kcontrol_new alc883_capture_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -7130,6 +7560,7 @@ static struct snd_kcontrol_new alc883_capture_mixer[] = { /* pcm configuration: identiacal with ALC880 */ #define alc883_pcm_analog_playback alc880_pcm_analog_playback #define alc883_pcm_analog_capture alc880_pcm_analog_capture +#define alc883_pcm_analog_alt_capture alc880_pcm_analog_alt_capture #define alc883_pcm_digital_playback alc880_pcm_digital_playback #define alc883_pcm_digital_capture alc880_pcm_digital_capture @@ -7154,53 +7585,58 @@ static const char *alc883_models[ALC883_MODEL_LAST] = { [ALC883_HAIER_W66] = "haier-w66", [ALC888_6ST_HP] = "6stack-hp", [ALC888_3ST_HP] = "3stack-hp", + [ALC888_6ST_DELL] = "6stack-dell", + [ALC883_MITAC] = "mitac", [ALC883_AUTO] = "auto", }; static struct snd_pci_quirk alc883_cfg_tbl[] = { SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC883_3ST_6ch_DIG), + SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE), + SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER), /* default Acer */ + SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL), SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch), - SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD), + SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP), + SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP), + SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC888_6ST_HP), + SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG), SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1071, 0x8253, "Mitac 8252d", ALC883_MITAC), + SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD), + SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch), SND_PCI_QUIRK(0x1458, 0xa002, "MSI", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG), - SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG), + SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG), - SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x3b7f, "MSI", ALC883_TARGA_2ch_DIG), - SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x3fc1, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x3fc3, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x3fdf, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x4314, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x4319, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x4324, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG), - SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE), - SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE), - SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE), - SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER), + SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD), SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch), SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION), - SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD), - SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch), SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch), - SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763), SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763), - SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC888_6ST_HP), - SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP), - SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP), + SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763), + SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763), SND_PCI_QUIRK(0x17c0, 0x4071, "MEDION MD2", ALC883_MEDION_MD2), SND_PCI_QUIRK(0x1991, 0x5625, "Haier W66", ALC883_HAIER_W66), - SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763), - SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG), - SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch), {} }; @@ -7435,6 +7871,34 @@ static struct alc_config_preset alc883_presets[] = { .need_dac_fix = 1, .input_mux = &alc883_capture_source, }, + [ALC888_6ST_DELL] = { + .mixers = { alc888_6st_dell_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc888_6st_dell_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .adc_nids = alc883_adc_nids, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), + .channel_mode = alc883_sixstack_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc888_6st_dell_unsol_event, + .init_hook = alc888_6st_dell_front_automute, + }, + [ALC883_MITAC] = { + .mixers = { alc883_mitac_mixer }, + .init_verbs = { alc883_init_verbs, alc883_mitac_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .adc_nids = alc883_adc_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc883_mitac_unsol_event, + .init_hook = alc883_mitac_automute, + }, }; @@ -7582,6 +8046,7 @@ static int patch_alc883(struct hda_codec *codec) spec->stream_name_analog = "ALC883 Analog"; spec->stream_analog_playback = &alc883_pcm_analog_playback; spec->stream_analog_capture = &alc883_pcm_analog_capture; + spec->stream_analog_alt_capture = &alc883_pcm_analog_alt_capture; spec->stream_name_digital = "ALC883 Digital"; spec->stream_digital_playback = &alc883_pcm_digital_playback; @@ -7592,6 +8057,8 @@ static int patch_alc883(struct hda_codec *codec) spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids); } + spec->vmaster_nid = 0x0c; + codec->patch_ops = alc_patch_ops; if (board_config == ALC883_AUTO) spec->init_hook = alc883_auto_init; @@ -7659,13 +8126,99 @@ static struct snd_kcontrol_new alc262_hippo1_mixer[] = { { } /* end */ }; +/* update HP, line and mono-out pins according to the master switch */ +static void alc262_hp_master_update(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int val = spec->master_sw; + + /* HP & line-out */ + snd_hda_codec_write_cache(codec, 0x1b, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + val ? PIN_HP : 0); + snd_hda_codec_write_cache(codec, 0x15, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + val ? PIN_HP : 0); + /* mono (speaker) depending on the HP jack sense */ + val = val && !spec->jack_present; + snd_hda_codec_write_cache(codec, 0x16, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + val ? PIN_OUT : 0); +} + +static void alc262_hp_bpc_automute(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + unsigned int presence; + presence = snd_hda_codec_read(codec, 0x1b, 0, + AC_VERB_GET_PIN_SENSE, 0); + spec->jack_present = !!(presence & AC_PINSENSE_PRESENCE); + alc262_hp_master_update(codec); +} + +static void alc262_hp_bpc_unsol_event(struct hda_codec *codec, unsigned int res) +{ + if ((res >> 26) != ALC880_HP_EVENT) + return; + alc262_hp_bpc_automute(codec); +} + +static void alc262_hp_wildwest_automute(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + unsigned int presence; + presence = snd_hda_codec_read(codec, 0x15, 0, + AC_VERB_GET_PIN_SENSE, 0); + spec->jack_present = !!(presence & AC_PINSENSE_PRESENCE); + alc262_hp_master_update(codec); +} + +static void alc262_hp_wildwest_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) != ALC880_HP_EVENT) + return; + alc262_hp_wildwest_automute(codec); +} + +static int alc262_hp_master_sw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + *ucontrol->value.integer.value = spec->master_sw; + return 0; +} + +static int alc262_hp_master_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + int val = !!*ucontrol->value.integer.value; + + if (val == spec->master_sw) + return 0; + spec->master_sw = val; + alc262_hp_master_update(codec); + return 1; +} + static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = snd_ctl_boolean_mono_info, + .get = alc262_hp_master_sw_get, + .put = alc262_hp_master_sw_put, + }, HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT), - + HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 2, 0x0, + HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0, + HDA_OUTPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), @@ -7684,12 +8237,21 @@ static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = { }; static struct snd_kcontrol_new alc262_HP_BPC_WildWest_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = snd_ctl_boolean_mono_info, + .get = alc262_hp_master_sw_get, + .put = alc262_hp_master_sw_put, + }, HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 2, 0x0, + HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0, + HDA_OUTPUT), HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_VOLUME("Front Mic Boost", 0x1a, 0, HDA_INPUT), @@ -7709,6 +8271,85 @@ static struct snd_kcontrol_new alc262_HP_BPC_WildWest_option_mixer[] = { { } /* end */ }; +/* mute/unmute internal speaker according to the hp jack and mute state */ +static void alc262_hp_t5735_automute(struct hda_codec *codec, int force) +{ + struct alc_spec *spec = codec->spec; + + if (force || !spec->sense_updated) { + unsigned int present; + present = snd_hda_codec_read(codec, 0x15, 0, + AC_VERB_GET_PIN_SENSE, 0); + spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; + spec->sense_updated = 1; + } + snd_hda_codec_amp_stereo(codec, 0x0c, HDA_OUTPUT, 0, HDA_AMP_MUTE, + spec->jack_present ? HDA_AMP_MUTE : 0); +} + +static void alc262_hp_t5735_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) != ALC880_HP_EVENT) + return; + alc262_hp_t5735_automute(codec, 1); +} + +static void alc262_hp_t5735_init_hook(struct hda_codec *codec) +{ + alc262_hp_t5735_automute(codec, 1); +} + +static struct snd_kcontrol_new alc262_hp_t5735_mixer[] = { + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + { } /* end */ +}; + +static struct hda_verb alc262_hp_t5735_verbs[] = { + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + { } +}; + +static struct snd_kcontrol_new alc262_hp_rp5700_mixer[] = { + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0e, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x16, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT), + { } /* end */ +}; + +static struct hda_verb alc262_hp_rp5700_verbs[] = { + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))}, + {} +}; + +static struct hda_input_mux alc262_hp_rp5700_capture_source = { + .num_items = 1, + .items = { + { "Line", 0x1 }, + }, +}; + /* bind hp and internal speaker mute (with plug check) */ static int alc262_sony_master_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -8082,6 +8723,72 @@ static struct hda_verb alc262_benq_t31_EAPD_verbs[] = { {} }; +/* Samsung Q1 Ultra Vista model setup */ +static struct snd_kcontrol_new alc262_ultra_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x19, 0, HDA_INPUT), + { } /* end */ +}; + +static struct hda_verb alc262_ultra_verbs[] = { + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + /* Mic is on Node 0x19 */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x22, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x23, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x24, AC_VERB_SET_CONNECT_SEL, 0x01}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {} +}; + +static struct hda_input_mux alc262_ultra_capture_source = { + .num_items = 1, + .items = { + { "Mic", 0x1 }, + }, +}; + +/* mute/unmute internal speaker according to the hp jack and mute state */ +static void alc262_ultra_automute(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + unsigned int mute; + unsigned int present; + + /* need to execute and sync at first */ + snd_hda_codec_read(codec, 0x15, 0, AC_VERB_SET_PIN_SENSE, 0); + present = snd_hda_codec_read(codec, 0x15, 0, + AC_VERB_GET_PIN_SENSE, 0); + spec->jack_present = (present & 0x80000000) != 0; + if (spec->jack_present) { + /* mute internal speaker */ + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, HDA_AMP_MUTE); + } else { + /* unmute internal speaker if necessary */ + mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0); + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute); + } +} + +/* unsolicited event for HP jack sensing */ +static void alc262_ultra_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) != ALC880_HP_EVENT) + return; + alc262_ultra_automute(codec); +} + /* add playback controls from the parsed DAC table */ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg) @@ -8269,7 +8976,7 @@ static struct hda_verb alc262_HP_BPC_init_verbs[] = { {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, @@ -8311,6 +9018,8 @@ static struct hda_verb alc262_HP_BPC_init_verbs[] = { {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, + {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + { } }; @@ -8405,6 +9114,8 @@ static struct hda_verb alc262_HP_BPC_WildWest_init_verbs[] = { /* {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + { } }; @@ -8484,39 +9195,49 @@ static const char *alc262_models[ALC262_MODEL_LAST] = { [ALC262_FUJITSU] = "fujitsu", [ALC262_HP_BPC] = "hp-bpc", [ALC262_HP_BPC_D7000_WL]= "hp-bpc-d7000", + [ALC262_HP_TC_T5735] = "hp-tc-t5735", + [ALC262_HP_RP5700] = "hp-rp5700", [ALC262_BENQ_ED8] = "benq", [ALC262_BENQ_T31] = "benq-t31", [ALC262_SONY_ASSAMD] = "sony-assamd", + [ALC262_ULTRA] = "ultra", [ALC262_AUTO] = "auto", }; static struct snd_pci_quirk alc262_cfg_tbl[] = { SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO), SND_PCI_QUIRK(0x103c, 0x12fe, "HP xw9400", ALC262_HP_BPC), - SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC), SND_PCI_QUIRK(0x103c, 0x12ff, "HP xw4550", ALC262_HP_BPC), - SND_PCI_QUIRK(0x103c, 0x1308, "HP xw4600", ALC262_HP_BPC), - SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC), - SND_PCI_QUIRK(0x103c, 0x1307, "HP xw6600", ALC262_HP_BPC), - SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC), SND_PCI_QUIRK(0x103c, 0x1306, "HP xw8600", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x1307, "HP xw6600", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x1308, "HP xw4600", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x1309, "HP xw4*00", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x130a, "HP xw6*00", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x130b, "HP xw8*00", ALC262_HP_BPC), SND_PCI_QUIRK(0x103c, 0x2800, "HP D7000", ALC262_HP_BPC_D7000_WL), - SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL), - SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL), - SND_PCI_QUIRK(0x103c, 0x2806, "HP D7000", ALC262_HP_BPC_D7000_WL), SND_PCI_QUIRK(0x103c, 0x2801, "HP D7000", ALC262_HP_BPC_D7000_WF), + SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL), SND_PCI_QUIRK(0x103c, 0x2803, "HP D7000", ALC262_HP_BPC_D7000_WF), + SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL), SND_PCI_QUIRK(0x103c, 0x2805, "HP D7000", ALC262_HP_BPC_D7000_WF), + SND_PCI_QUIRK(0x103c, 0x2806, "HP D7000", ALC262_HP_BPC_D7000_WL), SND_PCI_QUIRK(0x103c, 0x2807, "HP D7000", ALC262_HP_BPC_D7000_WF), + SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x302f, "HP Thin Client T5735", + ALC262_HP_TC_T5735), + SND_PCI_QUIRK(0x103c, 0x2817, "HP RP5700", ALC262_HP_RP5700), + SND_PCI_QUIRK(0x104d, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD), SND_PCI_QUIRK(0x104d, 0x8203, "Sony UX-90", ALC262_HIPPO), + SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD), + SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD), + SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD), SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU), - SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1), + SND_PCI_QUIRK(0x144d, 0xc032, "Samsung Q1 Ultra", ALC262_ULTRA), SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8), SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_BENQ_T31), - SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD), - SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD), - SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD), - SND_PCI_QUIRK(0x104d, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD), + SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1), {} }; @@ -8579,6 +9300,8 @@ static struct alc_config_preset alc262_presets[] = { .num_channel_mode = ARRAY_SIZE(alc262_modes), .channel_mode = alc262_modes, .input_mux = &alc262_HP_capture_source, + .unsol_event = alc262_hp_bpc_unsol_event, + .init_hook = alc262_hp_bpc_automute, }, [ALC262_HP_BPC_D7000_WF] = { .mixers = { alc262_HP_BPC_WildWest_mixer }, @@ -8589,6 +9312,8 @@ static struct alc_config_preset alc262_presets[] = { .num_channel_mode = ARRAY_SIZE(alc262_modes), .channel_mode = alc262_modes, .input_mux = &alc262_HP_D7000_capture_source, + .unsol_event = alc262_hp_wildwest_unsol_event, + .init_hook = alc262_hp_wildwest_automute, }, [ALC262_HP_BPC_D7000_WL] = { .mixers = { alc262_HP_BPC_WildWest_mixer, @@ -8600,7 +9325,30 @@ static struct alc_config_preset alc262_presets[] = { .num_channel_mode = ARRAY_SIZE(alc262_modes), .channel_mode = alc262_modes, .input_mux = &alc262_HP_D7000_capture_source, + .unsol_event = alc262_hp_wildwest_unsol_event, + .init_hook = alc262_hp_wildwest_automute, + }, + [ALC262_HP_TC_T5735] = { + .mixers = { alc262_hp_t5735_mixer }, + .init_verbs = { alc262_init_verbs, alc262_hp_t5735_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + .unsol_event = alc262_hp_t5735_unsol_event, + .init_hook = alc262_hp_t5735_init_hook, }, + [ALC262_HP_RP5700] = { + .mixers = { alc262_hp_rp5700_mixer }, + .init_verbs = { alc262_init_verbs, alc262_hp_rp5700_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_hp_rp5700_capture_source, + }, [ALC262_BENQ_ED8] = { .mixers = { alc262_base_mixer }, .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs }, @@ -8635,6 +9383,19 @@ static struct alc_config_preset alc262_presets[] = { .unsol_event = alc262_hippo_unsol_event, .init_hook = alc262_hippo_automute, }, + [ALC262_ULTRA] = { + .mixers = { alc262_ultra_mixer }, + .init_verbs = { alc262_init_verbs, alc262_ultra_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .dig_out_nid = ALC262_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_ultra_capture_source, + .unsol_event = alc262_ultra_unsol_event, + .init_hook = alc262_ultra_automute, + }, }; static int patch_alc262(struct hda_codec *codec) @@ -8716,6 +9477,8 @@ static int patch_alc262(struct hda_codec *codec) } } + spec->vmaster_nid = 0x0c; + codec->patch_ops = alc_patch_ops; if (board_config == ALC262_AUTO) spec->init_hook = alc262_auto_init; @@ -8873,6 +9636,49 @@ static void alc268_acer_init_hook(struct hda_codec *codec) alc268_acer_automute(codec, 1); } +static struct snd_kcontrol_new alc268_dell_mixer[] = { + /* output mixer control */ + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost", 0x19, 0, HDA_INPUT), + { } +}; + +static struct hda_verb alc268_dell_verbs[] = { + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + { } +}; + +/* mute/unmute internal speaker according to the hp jack and mute state */ +static void alc268_dell_automute(struct hda_codec *codec) +{ + unsigned int present; + unsigned int mute; + + present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0); + if (present & 0x80000000) + mute = HDA_AMP_MUTE; + else + mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0); + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute); +} + +static void alc268_dell_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) != ALC880_HP_EVENT) + return; + alc268_dell_automute(codec); +} + +#define alc268_dell_init_hook alc268_dell_automute + /* * generic initialization of ADC, input mixers and output mixers */ @@ -8915,19 +9721,13 @@ static struct hda_verb alc268_base_init_verbs[] = { {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - /* FIXME: use matrix-type input source selection */ - /* Mixer elements: 0x18, 19, 1a, 1c, 14, 15, 0b */ - /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ - /* Input mixer2 */ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, + /* Unmute Selector 23h,24h and set the default input to mic-in */ + + {0x23, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x24, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, { } }; @@ -8972,29 +9772,14 @@ static int alc268_mux_enum_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; - const struct hda_input_mux *imux = spec->input_mux; + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); static hda_nid_t capture_mixers[3] = { 0x23, 0x24 }; hda_nid_t nid = capture_mixers[adc_idx]; - unsigned int *cur_val = &spec->cur_mux[adc_idx]; - unsigned int i, idx; - idx = ucontrol->value.enumerated.item[0]; - if (idx >= imux->num_items) - idx = imux->num_items - 1; - if (*cur_val == idx) - return 0; - for (i = 0; i < imux->num_items; i++) { - unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE; - snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, - imux->items[i].index, - HDA_AMP_MUTE, v); - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, - idx ); - } - *cur_val = idx; - return 1; + return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, + nid, + &spec->cur_mux[adc_idx]); } static struct snd_kcontrol_new alc268_capture_alt_mixer[] = { @@ -9004,7 +9789,6 @@ static struct snd_kcontrol_new alc268_capture_alt_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -9025,7 +9809,6 @@ static struct snd_kcontrol_new alc268_capture_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -9047,6 +9830,61 @@ static struct hda_input_mux alc268_capture_source = { }, }; +#ifdef CONFIG_SND_DEBUG +static struct snd_kcontrol_new alc268_test_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + + /* Volume widgets */ + HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Mono sum Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE("LINE-OUT sum Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_BIND_MUTE("HP-OUT sum Playback Switch", 0x10, 2, HDA_INPUT), + HDA_BIND_MUTE("LINE-OUT Playback Switch", 0x14, 2, HDA_OUTPUT), + HDA_BIND_MUTE("HP-OUT Playback Switch", 0x15, 2, HDA_OUTPUT), + HDA_BIND_MUTE("Mono Playback Switch", 0x16, 2, HDA_OUTPUT), + HDA_CODEC_VOLUME("MIC1 Capture Volume", 0x18, 0x0, HDA_INPUT), + HDA_BIND_MUTE("MIC1 Capture Switch", 0x18, 2, HDA_OUTPUT), + HDA_CODEC_VOLUME("MIC2 Capture Volume", 0x19, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("LINE1 Capture Volume", 0x1a, 0x0, HDA_INPUT), + HDA_BIND_MUTE("LINE1 Capture Switch", 0x1a, 2, HDA_OUTPUT), + /* The below appears problematic on some hardwares */ + /*HDA_CODEC_VOLUME("PCBEEP Playback Volume", 0x1d, 0x0, HDA_INPUT),*/ + HDA_CODEC_VOLUME("PCM-IN1 Capture Volume", 0x23, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("PCM-IN1 Capture Switch", 0x23, 2, HDA_OUTPUT), + HDA_CODEC_VOLUME("PCM-IN2 Capture Volume", 0x24, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("PCM-IN2 Capture Switch", 0x24, 2, HDA_OUTPUT), + + /* Modes for retasking pin widgets */ + ALC_PIN_MODE("LINE-OUT pin mode", 0x14, ALC_PIN_DIR_INOUT), + ALC_PIN_MODE("HP-OUT pin mode", 0x15, ALC_PIN_DIR_INOUT), + ALC_PIN_MODE("MIC1 pin mode", 0x18, ALC_PIN_DIR_INOUT), + ALC_PIN_MODE("LINE1 pin mode", 0x1a, ALC_PIN_DIR_INOUT), + + /* Controls for GPIO pins, assuming they are configured as outputs */ + ALC_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01), + ALC_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02), + ALC_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04), + ALC_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08), + + /* Switches to allow the digital SPDIF output pin to be enabled. + * The ALC268 does not have an SPDIF input. + */ + ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x06, 0x01), + + /* A switch allowing EAPD to be enabled. Some laptops seem to use + * this output to turn on an external amplifier. + */ + ALC_EAPD_CTRL_SWITCH("LINE-OUT EAPD Enable Switch", 0x0f, 0x02), + ALC_EAPD_CTRL_SWITCH("HP-OUT EAPD Enable Switch", 0x10, 0x02), + + { } /* end */ +}; +#endif + /* create input playback/capture controls for the given pin */ static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid, const char *ctlname, int idx) @@ -9194,6 +10032,7 @@ static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec) /* pcm configuration: identiacal with ALC880 */ #define alc268_pcm_analog_playback alc880_pcm_analog_playback #define alc268_pcm_analog_capture alc880_pcm_analog_capture +#define alc268_pcm_analog_alt_capture alc880_pcm_analog_alt_capture #define alc268_pcm_digital_playback alc880_pcm_digital_playback /* @@ -9259,16 +10098,23 @@ static const char *alc268_models[ALC268_MODEL_LAST] = { [ALC268_3ST] = "3stack", [ALC268_TOSHIBA] = "toshiba", [ALC268_ACER] = "acer", + [ALC268_DELL] = "dell", +#ifdef CONFIG_SND_DEBUG + [ALC268_TEST] = "test", +#endif [ALC268_AUTO] = "auto", }; static struct snd_pci_quirk alc268_cfg_tbl[] = { + SND_PCI_QUIRK(0x1025, 0x0126, "Acer", ALC268_ACER), + SND_PCI_QUIRK(0x1025, 0x012e, "Acer Aspire 5310", ALC268_ACER), + SND_PCI_QUIRK(0x1025, 0x0130, "Acer Extensa 5210", ALC268_ACER), + SND_PCI_QUIRK(0x1025, 0x0136, "Acer Aspire 5315", ALC268_ACER), + SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL), + SND_PCI_QUIRK(0x103c, 0x30cc, "TOSHIBA", ALC268_TOSHIBA), SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST), SND_PCI_QUIRK(0x1179, 0xff10, "TOSHIBA A205", ALC268_TOSHIBA), SND_PCI_QUIRK(0x1179, 0xff50, "TOSHIBA A305", ALC268_TOSHIBA), - SND_PCI_QUIRK(0x103c, 0x30cc, "TOSHIBA", ALC268_TOSHIBA), - SND_PCI_QUIRK(0x1025, 0x0126, "Acer", ALC268_ACER), - SND_PCI_QUIRK(0x1025, 0x0130, "Acer Extensa 5210", ALC268_ACER), SND_PCI_QUIRK(0x152d, 0x0763, "Diverse (CPR2000)", ALC268_ACER), {} }; @@ -9317,6 +10163,35 @@ static struct alc_config_preset alc268_presets[] = { .unsol_event = alc268_acer_unsol_event, .init_hook = alc268_acer_init_hook, }, + [ALC268_DELL] = { + .mixers = { alc268_dell_mixer }, + .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, + alc268_dell_verbs }, + .num_dacs = ARRAY_SIZE(alc268_dac_nids), + .dac_nids = alc268_dac_nids, + .hp_nid = 0x02, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .unsol_event = alc268_dell_unsol_event, + .init_hook = alc268_dell_init_hook, + .input_mux = &alc268_capture_source, + }, +#ifdef CONFIG_SND_DEBUG + [ALC268_TEST] = { + .mixers = { alc268_test_mixer, alc268_capture_mixer }, + .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, + alc268_volume_init_verbs }, + .num_dacs = ARRAY_SIZE(alc268_dac_nids), + .dac_nids = alc268_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), + .adc_nids = alc268_adc_nids_alt, + .hp_nid = 0x03, + .dig_out_nid = ALC268_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .input_mux = &alc268_capture_source, + }, +#endif }; static int patch_alc268(struct hda_codec *codec) @@ -9361,34 +10236,34 @@ static int patch_alc268(struct hda_codec *codec) spec->stream_name_analog = "ALC268 Analog"; spec->stream_analog_playback = &alc268_pcm_analog_playback; spec->stream_analog_capture = &alc268_pcm_analog_capture; + spec->stream_analog_alt_capture = &alc268_pcm_analog_alt_capture; spec->stream_name_digital = "ALC268 Digital"; spec->stream_digital_playback = &alc268_pcm_digital_playback; - if (board_config == ALC268_AUTO) { - if (!spec->adc_nids && spec->input_mux) { - /* check whether NID 0x07 is valid */ - unsigned int wcap = get_wcaps(codec, 0x07); - - /* get type */ - wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; - if (wcap != AC_WID_AUD_IN) { - spec->adc_nids = alc268_adc_nids_alt; - spec->num_adc_nids = - ARRAY_SIZE(alc268_adc_nids_alt); - spec->mixers[spec->num_mixers] = + if (!spec->adc_nids && spec->input_mux) { + /* check whether NID 0x07 is valid */ + unsigned int wcap = get_wcaps(codec, 0x07); + + /* get type */ + wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; + if (wcap != AC_WID_AUD_IN) { + spec->adc_nids = alc268_adc_nids_alt; + spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt); + spec->mixers[spec->num_mixers] = alc268_capture_alt_mixer; - spec->num_mixers++; - } else { - spec->adc_nids = alc268_adc_nids; - spec->num_adc_nids = - ARRAY_SIZE(alc268_adc_nids); - spec->mixers[spec->num_mixers] = - alc268_capture_mixer; - spec->num_mixers++; - } + spec->num_mixers++; + } else { + spec->adc_nids = alc268_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids); + spec->mixers[spec->num_mixers] = + alc268_capture_mixer; + spec->num_mixers++; } } + + spec->vmaster_nid = 0x02; + codec->patch_ops = alc_patch_ops; if (board_config == ALC268_AUTO) spec->init_hook = alc268_auto_init; @@ -9397,6 +10272,360 @@ static int patch_alc268(struct hda_codec *codec) } /* + * ALC269 channel source setting (2 channel) + */ +#define ALC269_DIGOUT_NID ALC880_DIGOUT_NID + +#define alc269_dac_nids alc260_dac_nids + +static hda_nid_t alc269_adc_nids[1] = { + /* ADC1 */ + 0x07, +}; + +#define alc269_modes alc260_modes +#define alc269_capture_source alc880_lg_lw_capture_source + +static struct snd_kcontrol_new alc269_base_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT), + { } /* end */ +}; + +/* capture mixer elements */ +static struct snd_kcontrol_new alc269_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 1, + .info = alc_mux_enum_info, + .get = alc_mux_enum_get, + .put = alc_mux_enum_put, + }, + { } /* end */ +}; + +/* + * generic initialization of ADC, input mixers and output mixers + */ +static struct hda_verb alc269_init_verbs[] = { + /* + * Unmute ADC0 and set the default input to mic-in + */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + /* Mute input amps (PCBeep, Line In, Mic 1 & Mic 2) of the + * analog-loopback mixer widget + * Note: PASD motherboards uses the Line In 2 as the input for + * front panel mic (mic 2) + */ + /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + + /* + * Set up output mixers (0x0c - 0x0e) + */ + /* set vol=0 to output mixers */ + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + + /* set up input amps for analog loopback */ + /* Amp Indices: DAC = 0, mixer = 1 */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + + /* FIXME: use matrix-type input source selection */ + /* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */ + /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + + /* set EAPD */ + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, + {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +/* add playback controls from the parsed DAC table */ +static int alc269_auto_create_multi_out_ctls(struct alc_spec *spec, + const struct auto_pin_cfg *cfg) +{ + hda_nid_t nid; + int err; + + spec->multiout.num_dacs = 1; /* only use one dac */ + spec->multiout.dac_nids = spec->private_dac_nids; + spec->multiout.dac_nids[0] = 2; + + nid = cfg->line_out_pins[0]; + if (nid) { + err = add_control(spec, ALC_CTL_WIDGET_VOL, + "Front Playback Volume", + HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + err = add_control(spec, ALC_CTL_WIDGET_MUTE, + "Front Playback Switch", + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + } + + nid = cfg->speaker_pins[0]; + if (nid) { + if (!cfg->line_out_pins[0]) { + err = add_control(spec, ALC_CTL_WIDGET_VOL, + "Speaker Playback Volume", + HDA_COMPOSE_AMP_VAL(0x02, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } + if (nid == 0x16) { + err = add_control(spec, ALC_CTL_WIDGET_MUTE, + "Speaker Playback Switch", + HDA_COMPOSE_AMP_VAL(nid, 2, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } else { + err = add_control(spec, ALC_CTL_WIDGET_MUTE, + "Speaker Playback Switch", + HDA_COMPOSE_AMP_VAL(nid, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } + } + nid = cfg->hp_pins[0]; + if (nid) { + /* spec->multiout.hp_nid = 2; */ + if (!cfg->line_out_pins[0] && !cfg->speaker_pins[0]) { + err = add_control(spec, ALC_CTL_WIDGET_VOL, + "Headphone Playback Volume", + HDA_COMPOSE_AMP_VAL(0x02, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } + if (nid == 0x16) { + err = add_control(spec, ALC_CTL_WIDGET_MUTE, + "Headphone Playback Switch", + HDA_COMPOSE_AMP_VAL(nid, 2, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } else { + err = add_control(spec, ALC_CTL_WIDGET_MUTE, + "Headphone Playback Switch", + HDA_COMPOSE_AMP_VAL(nid, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } + } + return 0; +} + +#define alc269_auto_create_analog_input_ctls \ + alc880_auto_create_analog_input_ctls + +#ifdef CONFIG_SND_HDA_POWER_SAVE +#define alc269_loopbacks alc880_loopbacks +#endif + +/* pcm configuration: identiacal with ALC880 */ +#define alc269_pcm_analog_playback alc880_pcm_analog_playback +#define alc269_pcm_analog_capture alc880_pcm_analog_capture +#define alc269_pcm_digital_playback alc880_pcm_digital_playback +#define alc269_pcm_digital_capture alc880_pcm_digital_capture + +/* + * BIOS auto configuration + */ +static int alc269_parse_auto_config(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int err; + static hda_nid_t alc269_ignore[] = { 0x1d, 0 }; + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, + alc269_ignore); + if (err < 0) + return err; + + err = alc269_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + err = alc269_auto_create_analog_input_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + + if (spec->autocfg.dig_out_pin) + spec->multiout.dig_out_nid = ALC269_DIGOUT_NID; + + if (spec->kctl_alloc) + spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + + spec->init_verbs[spec->num_init_verbs++] = alc269_init_verbs; + spec->num_mux_defs = 1; + spec->input_mux = &spec->private_imux; + + err = alc_auto_add_mic_boost(codec); + if (err < 0) + return err; + + return 1; +} + +#define alc269_auto_init_multi_out alc882_auto_init_multi_out +#define alc269_auto_init_hp_out alc882_auto_init_hp_out +#define alc269_auto_init_analog_input alc882_auto_init_analog_input + + +/* init callback for auto-configuration model -- overriding the default init */ +static void alc269_auto_init(struct hda_codec *codec) +{ + alc269_auto_init_multi_out(codec); + alc269_auto_init_hp_out(codec); + alc269_auto_init_analog_input(codec); +} + +/* + * configuration and preset + */ +static const char *alc269_models[ALC269_MODEL_LAST] = { + [ALC269_BASIC] = "basic", +}; + +static struct snd_pci_quirk alc269_cfg_tbl[] = { + {} +}; + +static struct alc_config_preset alc269_presets[] = { + [ALC269_BASIC] = { + .mixers = { alc269_base_mixer }, + .init_verbs = { alc269_init_verbs }, + .num_dacs = ARRAY_SIZE(alc269_dac_nids), + .dac_nids = alc269_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc269_modes), + .channel_mode = alc269_modes, + .input_mux = &alc269_capture_source, + }, +}; + +static int patch_alc269(struct hda_codec *codec) +{ + struct alc_spec *spec; + int board_config; + int err; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST, + alc269_models, + alc269_cfg_tbl); + + if (board_config < 0) { + printk(KERN_INFO "hda_codec: Unknown model for ALC269, " + "trying auto-probe from BIOS...\n"); + board_config = ALC269_AUTO; + } + + if (board_config == ALC269_AUTO) { + /* automatic parse from the BIOS config */ + err = alc269_parse_auto_config(codec); + if (err < 0) { + alc_free(codec); + return err; + } else if (!err) { + printk(KERN_INFO + "hda_codec: Cannot set up configuration " + "from BIOS. Using base mode...\n"); + board_config = ALC269_BASIC; + } + } + + if (board_config != ALC269_AUTO) + setup_preset(spec, &alc269_presets[board_config]); + + spec->stream_name_analog = "ALC269 Analog"; + spec->stream_analog_playback = &alc269_pcm_analog_playback; + spec->stream_analog_capture = &alc269_pcm_analog_capture; + + spec->stream_name_digital = "ALC269 Digital"; + spec->stream_digital_playback = &alc269_pcm_digital_playback; + spec->stream_digital_capture = &alc269_pcm_digital_capture; + + spec->adc_nids = alc269_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids); + spec->mixers[spec->num_mixers] = alc269_capture_mixer; + spec->num_mixers++; + + codec->patch_ops = alc_patch_ops; + if (board_config == ALC269_AUTO) + spec->init_hook = alc269_auto_init; +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (!spec->loopback.amplist) + spec->loopback.amplist = alc269_loopbacks; +#endif + + return 0; +} + +/* * ALC861 channel source setting (2/6 channel selection for 3-stack) */ @@ -10213,7 +11442,6 @@ static struct snd_kcontrol_new alc861_capture_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - *FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -10369,22 +11597,23 @@ static struct snd_pci_quirk alc861_cfg_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC861_3ST), SND_PCI_QUIRK(0x1043, 0x1335, "ASUS F2/3", ALC861_ASUS_LAPTOP), SND_PCI_QUIRK(0x1043, 0x1338, "ASUS F2/3", ALC861_ASUS_LAPTOP), - SND_PCI_QUIRK(0x1043, 0x13d7, "ASUS A9rp", ALC861_ASUS_LAPTOP), - SND_PCI_QUIRK(0x1584, 0x9075, "Airis Praxis N1212", ALC861_ASUS_LAPTOP), SND_PCI_QUIRK(0x1043, 0x1393, "ASUS", ALC861_ASUS), + SND_PCI_QUIRK(0x1043, 0x13d7, "ASUS A9rp", ALC861_ASUS_LAPTOP), SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS P1-AH2", ALC861_3ST_DIG), SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba", ALC861_TOSHIBA), /* FIXME: the entry below breaks Toshiba A100 (model=auto works!) * Any other models that need this preset? */ /* SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA), */ - SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31), - SND_PCI_QUIRK(0x1584, 0x9075, "Uniwill", ALC861_UNIWILL_M31), + SND_PCI_QUIRK(0x1462, 0x7254, "HP dx2200 (MSI MS-7254)", ALC861_3ST), + SND_PCI_QUIRK(0x1462, 0x7297, "HP dx2250 (MSI MS-7297)", ALC861_3ST), SND_PCI_QUIRK(0x1584, 0x2b01, "Uniwill X40AIx", ALC861_UNIWILL_M31), + SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31), + SND_PCI_QUIRK(0x1584, 0x9075, "Airis Praxis N1212", ALC861_ASUS_LAPTOP), + /* FIXME: the below seems conflict */ + /* SND_PCI_QUIRK(0x1584, 0x9075, "Uniwill", ALC861_UNIWILL_M31), */ SND_PCI_QUIRK(0x1849, 0x0660, "Asrock 939SLI32", ALC660_3ST), SND_PCI_QUIRK(0x8086, 0xd600, "Intel", ALC861_3ST), - SND_PCI_QUIRK(0x1462, 0x7254, "HP dx2200 (MSI MS-7254)", ALC861_3ST), - SND_PCI_QUIRK(0x1462, 0x7297, "HP dx2250 (MSI MS-7297)", ALC861_3ST), {} }; @@ -10543,6 +11772,8 @@ static int patch_alc861(struct hda_codec *codec) spec->stream_digital_playback = &alc861_pcm_digital_playback; spec->stream_digital_capture = &alc861_pcm_digital_capture; + spec->vmaster_nid = 0x03; + codec->patch_ops = alc_patch_ops; if (board_config == ALC861_AUTO) spec->init_hook = alc861_auto_init; @@ -10697,7 +11928,6 @@ static struct snd_kcontrol_new alc861vd_capture_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - *FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -11102,21 +12332,20 @@ static const char *alc861vd_models[ALC861VD_MODEL_LAST] = { }; static struct snd_pci_quirk alc861vd_cfg_tbl[] = { + SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST), + SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP), SND_PCI_QUIRK(0x1043, 0x12e2, "Asus z35m", ALC660VD_3ST), SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST), SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG), SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST), - SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST), - + SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO), /*SND_PCI_QUIRK(0x1179, 0xff00, "DALLAS", ALC861VD_DALLAS),*/ /*lenovo*/ SND_PCI_QUIRK(0x1179, 0xff01, "DALLAS", ALC861VD_DALLAS), - SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_LENOVO), - SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo", ALC861VD_LENOVO), - SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO), SND_PCI_QUIRK(0x1179, 0xff03, "Toshiba P205", ALC861VD_LENOVO), SND_PCI_QUIRK(0x1565, 0x820d, "Biostar NF61S SE", ALC861VD_6ST_DIG), + SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo", ALC861VD_LENOVO), + SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_LENOVO), SND_PCI_QUIRK(0x1849, 0x0862, "ASRock K8NF6G-VSTA", ALC861VD_6ST_DIG), - SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP), {} }; @@ -11520,6 +12749,8 @@ static int patch_alc861vd(struct hda_codec *codec) spec->mixers[spec->num_mixers] = alc861vd_capture_mixer; spec->num_mixers++; + spec->vmaster_nid = 0x02; + codec->patch_ops = alc_patch_ops; if (board_config == ALC861VD_AUTO) @@ -11699,18 +12930,6 @@ static struct snd_kcontrol_new alc662_base_mixer[] = { HDA_CODEC_MUTE("Mic Playback Switch", 0xb, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Front Mic Playback Volume", 0xb, 0x01, HDA_INPUT), HDA_CODEC_MUTE("Front Mic Playback Switch", 0xb, 0x01, HDA_INPUT), - - /* Capture mixer control */ - HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .count = 1, - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, - }, { } /* end */ }; @@ -11728,17 +12947,6 @@ static struct snd_kcontrol_new alc662_3ST_2ch_mixer[] = { HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 1, - .info = alc662_mux_enum_info, - .get = alc662_mux_enum_get, - .put = alc662_mux_enum_put, - }, { } /* end */ }; @@ -11762,46 +12970,24 @@ static struct snd_kcontrol_new alc662_3ST_6ch_mixer[] = { HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 1, - .info = alc662_mux_enum_info, - .get = alc662_mux_enum_get, - .put = alc662_mux_enum_put, - }, { } /* end */ }; static struct snd_kcontrol_new alc662_lenovo_101e_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Front Playback Switch", 0x02, 2, HDA_INPUT), - HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("iSpeaker Playback Switch", 0x03, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Speaker Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Speaker Playback Switch", 0x03, 2, HDA_INPUT), HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 1, - .info = alc662_mux_enum_info, - .get = alc662_mux_enum_get, - .put = alc662_mux_enum_put, - }, { } /* end */ }; static struct snd_kcontrol_new alc662_eeepc_p701_mixer[] = { - HDA_CODEC_MUTE("iSpeaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("LineOut Playback Volume", 0x02, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("LineOut Playback Switch", 0x1b, 0x0, HDA_OUTPUT), @@ -11816,6 +13002,24 @@ static struct snd_kcontrol_new alc662_eeepc_p701_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new alc662_eeepc_ep20_mixer[] = { + HDA_CODEC_VOLUME("LineOut Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("LineOut Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x03, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x04, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x04, 2, 2, HDA_INPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("MuteCtrl Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + static struct snd_kcontrol_new alc662_chmode_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -11901,6 +13105,13 @@ static struct hda_verb alc662_eeepc_sue_init_verbs[] = { {} }; +/* Set Unsolicited Event*/ +static struct hda_verb alc662_eeepc_ep20_sue_init_verbs[] = { + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, + {} +}; + /* * generic initialization of ADC, input mixers and output mixers */ @@ -11957,14 +13168,13 @@ static struct snd_kcontrol_new alc662_capture_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", .count = 1, - .info = alc882_mux_enum_info, - .get = alc882_mux_enum_get, - .put = alc882_mux_enum_put, + .info = alc662_mux_enum_info, + .get = alc662_mux_enum_get, + .put = alc662_mux_enum_put, }, { } /* end */ }; @@ -12037,6 +13247,40 @@ static void alc662_eeepc_inithook(struct hda_codec *codec) alc662_eeepc_mic_automute(codec); } +static void alc662_eeepc_ep20_automute(struct hda_codec *codec) +{ + unsigned int mute; + unsigned int present; + + snd_hda_codec_read(codec, 0x14, 0, AC_VERB_SET_PIN_SENSE, 0); + present = snd_hda_codec_read(codec, 0x14, 0, + AC_VERB_GET_PIN_SENSE, 0); + present = (present & 0x80000000) != 0; + if (present) { + /* mute internal speaker */ + snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, + HDA_AMP_MUTE, HDA_AMP_MUTE); + } else { + /* unmute internal speaker if necessary */ + mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0); + snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute); + } +} + +/* unsolicited event for HP jack sensing */ +static void alc662_eeepc_ep20_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) == ALC880_HP_EVENT) + alc662_eeepc_ep20_automute(codec); +} + +static void alc662_eeepc_ep20_inithook(struct hda_codec *codec) +{ + alc662_eeepc_ep20_automute(codec); +} + #ifdef CONFIG_SND_HDA_POWER_SAVE #define alc662_loopbacks alc880_loopbacks #endif @@ -12057,12 +13301,15 @@ static const char *alc662_models[ALC662_MODEL_LAST] = { [ALC662_3ST_6ch] = "3stack-6ch", [ALC662_5ST_DIG] = "6stack-dig", [ALC662_LENOVO_101E] = "lenovo-101e", + [ALC662_ASUS_EEEPC_P701] = "eeepc-p701", + [ALC662_ASUS_EEEPC_EP20] = "eeepc-ep20", [ALC662_AUTO] = "auto", }; static struct snd_pci_quirk alc662_cfg_tbl[] = { - SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E), SND_PCI_QUIRK(0x1043, 0x82a1, "ASUS Eeepc", ALC662_ASUS_EEEPC_P701), + SND_PCI_QUIRK(0x1043, 0x82d1, "ASUS Eeepc EP20", ALC662_ASUS_EEEPC_EP20), + SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E), {} }; @@ -12149,6 +13396,21 @@ static struct alc_config_preset alc662_presets[] = { .unsol_event = alc662_eeepc_unsol_event, .init_hook = alc662_eeepc_inithook, }, + [ALC662_ASUS_EEEPC_EP20] = { + .mixers = { alc662_eeepc_ep20_mixer, alc662_capture_mixer, + alc662_chmode_mixer }, + .init_verbs = { alc662_init_verbs, + alc662_eeepc_ep20_sue_init_verbs }, + .num_dacs = ARRAY_SIZE(alc662_dac_nids), + .dac_nids = alc662_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), + .adc_nids = alc662_adc_nids, + .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), + .channel_mode = alc662_3ST_6ch_modes, + .input_mux = &alc662_lenovo_101e_capture_source, + .unsol_event = alc662_eeepc_ep20_unsol_event, + .init_hook = alc662_eeepc_ep20_inithook, + }, }; @@ -12308,6 +13570,7 @@ static void alc662_auto_init_multi_out(struct hda_codec *codec) struct alc_spec *spec = codec->spec; int i; + alc_subsystem_id(codec, 0x15, 0x1b, 0x14); for (i = 0; i <= HDA_SIDE; i++) { hda_nid_t nid = spec->autocfg.line_out_pins[i]; int pin_type = get_pin_type(spec->autocfg.line_out_type); @@ -12458,6 +13721,8 @@ static int patch_alc662(struct hda_codec *codec) spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids); } + spec->vmaster_nid = 0x02; + codec->patch_ops = alc_patch_ops; if (board_config == ALC662_AUTO) spec->init_hook = alc662_auto_init; @@ -12475,7 +13740,9 @@ static int patch_alc662(struct hda_codec *codec) struct hda_codec_preset snd_hda_preset_realtek[] = { { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 }, { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 }, + { .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 }, { .id = 0x10ec0268, .name = "ALC268", .patch = patch_alc268 }, + { .id = 0x10ec0269, .name = "ALC269", .patch = patch_alc269 }, { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660", .patch = patch_alc861 }, { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd }, @@ -12490,5 +13757,6 @@ struct hda_codec_preset snd_hda_preset_realtek[] = { { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 }, { .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 }, { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 }, + { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc883 }, {} /* terminator */ }; diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c index 2a4b960..d22f5a6 100644 --- a/sound/pci/hda/patch_si3054.c +++ b/sound/pci/hda/patch_si3054.c @@ -22,7 +22,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> @@ -287,7 +286,6 @@ static int patch_si3054(struct hda_codec *codec) struct hda_codec_preset snd_hda_preset_si3054[] = { { .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 }, { .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 }, - { .id = 0x11c11040, .name = "Si3054", .patch = patch_si3054 }, { .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 }, { .id = 0x11c13055, .name = "Si3054", .patch = patch_si3054 }, { .id = 0x11c13155, .name = "Si3054", .patch = patch_si3054 }, diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 0401223..caf48ed 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -24,7 +24,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> @@ -35,7 +34,8 @@ #include "hda_local.h" #define NUM_CONTROL_ALLOC 32 -#define STAC_HP_EVENT 0x37 +#define STAC_PWR_EVENT 0x20 +#define STAC_HP_EVENT 0x30 enum { STAC_REF, @@ -62,6 +62,16 @@ enum { }; enum { + STAC_92HD73XX_REF, + STAC_92HD73XX_MODELS +}; + +enum { + STAC_92HD71BXX_REF, + STAC_92HD71BXX_MODELS +}; + +enum { STAC_925x_REF, STAC_M2_2, STAC_MA6, @@ -97,6 +107,7 @@ enum { STAC_D965_3ST, STAC_D965_5ST, STAC_DELL_3ST, + STAC_DELL_BIOS, STAC_927X_MODELS }; @@ -110,11 +121,24 @@ struct sigmatel_spec { unsigned int mic_switch: 1; unsigned int alt_switch: 1; unsigned int hp_detect: 1; - unsigned int gpio_mute: 1; - unsigned int gpio_mask, gpio_data; + /* gpio lines */ + unsigned int gpio_mask; + unsigned int gpio_dir; + unsigned int gpio_data; + unsigned int gpio_mute; + + /* analog loopback */ + unsigned char aloopback_mask; + unsigned char aloopback_shift; + + /* power management */ + unsigned int num_pwrs; + hda_nid_t *pwr_nids; /* playback */ + struct hda_input_mux *mono_mux; + unsigned int cur_mmux; struct hda_multi_out multiout; hda_nid_t dac_nids[5]; @@ -125,8 +149,10 @@ struct sigmatel_spec { unsigned int num_muxes; hda_nid_t *dmic_nids; unsigned int num_dmics; - hda_nid_t dmux_nid; + hda_nid_t *dmux_nids; + unsigned int num_dmuxes; hda_nid_t dig_in_nid; + hda_nid_t mono_nid; /* pin widgets */ hda_nid_t *pin_nids; @@ -140,7 +166,7 @@ struct sigmatel_spec { /* capture source */ struct hda_input_mux *dinput_mux; - unsigned int cur_dmux; + unsigned int cur_dmux[2]; struct hda_input_mux *input_mux; unsigned int cur_mux[3]; @@ -157,6 +183,10 @@ struct sigmatel_spec { struct snd_kcontrol_new *kctl_alloc; struct hda_input_mux private_dimux; struct hda_input_mux private_imux; + struct hda_input_mux private_mono_mux; + + /* virtual master */ + unsigned int vmaster_tlv[4]; }; static hda_nid_t stac9200_adc_nids[1] = { @@ -171,6 +201,58 @@ static hda_nid_t stac9200_dac_nids[1] = { 0x02, }; +static hda_nid_t stac92hd73xx_pwr_nids[8] = { + 0x0a, 0x0b, 0x0c, 0xd, 0x0e, + 0x0f, 0x10, 0x11 +}; + +static hda_nid_t stac92hd73xx_adc_nids[2] = { + 0x1a, 0x1b +}; + +#define STAC92HD73XX_NUM_DMICS 2 +static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = { + 0x13, 0x14, 0 +}; + +#define STAC92HD73_DAC_COUNT 5 +static hda_nid_t stac92hd73xx_dac_nids[STAC92HD73_DAC_COUNT] = { + 0x15, 0x16, 0x17, 0x18, 0x19, +}; + +static hda_nid_t stac92hd73xx_mux_nids[4] = { + 0x28, 0x29, 0x2a, 0x2b, +}; + +static hda_nid_t stac92hd73xx_dmux_nids[2] = { + 0x20, 0x21, +}; + +static hda_nid_t stac92hd71bxx_pwr_nids[3] = { + 0x0a, 0x0d, 0x0f +}; + +static hda_nid_t stac92hd71bxx_adc_nids[2] = { + 0x12, 0x13, +}; + +static hda_nid_t stac92hd71bxx_mux_nids[2] = { + 0x1a, 0x1b +}; + +static hda_nid_t stac92hd71bxx_dmux_nids[1] = { + 0x1c, +}; + +static hda_nid_t stac92hd71bxx_dac_nids[2] = { + 0x10, /*0x11, */ +}; + +#define STAC92HD71BXX_NUM_DMICS 2 +static hda_nid_t stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS + 1] = { + 0x18, 0x19, 0 +}; + static hda_nid_t stac925x_adc_nids[1] = { 0x03, }; @@ -188,6 +270,10 @@ static hda_nid_t stac925x_dmic_nids[STAC925X_NUM_DMICS + 1] = { 0x15, 0 }; +static hda_nid_t stac925x_dmux_nids[1] = { + 0x14, +}; + static hda_nid_t stac922x_adc_nids[2] = { 0x06, 0x07, }; @@ -204,6 +290,15 @@ static hda_nid_t stac927x_mux_nids[3] = { 0x15, 0x16, 0x17 }; +static hda_nid_t stac927x_dmux_nids[1] = { + 0x1b, +}; + +#define STAC927X_NUM_DMICS 2 +static hda_nid_t stac927x_dmic_nids[STAC927X_NUM_DMICS + 1] = { + 0x13, 0x14, 0 +}; + static hda_nid_t stac9205_adc_nids[2] = { 0x12, 0x13 }; @@ -212,6 +307,10 @@ static hda_nid_t stac9205_mux_nids[2] = { 0x19, 0x1a }; +static hda_nid_t stac9205_dmux_nids[1] = { + 0x1d, +}; + #define STAC9205_NUM_DMICS 2 static hda_nid_t stac9205_dmic_nids[STAC9205_NUM_DMICS + 1] = { 0x17, 0x18, 0 @@ -232,6 +331,17 @@ static hda_nid_t stac922x_pin_nids[10] = { 0x0f, 0x10, 0x11, 0x15, 0x1b, }; +static hda_nid_t stac92hd73xx_pin_nids[12] = { + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x10, 0x11, 0x12, 0x13, + 0x14, 0x22 +}; + +static hda_nid_t stac92hd71bxx_pin_nids[10] = { + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x14, 0x18, 0x19, 0x1e, +}; + static hda_nid_t stac927x_pin_nids[14] = { 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, @@ -257,8 +367,9 @@ static int stac92xx_dmux_enum_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; + unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - ucontrol->value.enumerated.item[0] = spec->cur_dmux; + ucontrol->value.enumerated.item[0] = spec->cur_dmux[dmux_idx]; return 0; } @@ -267,9 +378,10 @@ static int stac92xx_dmux_enum_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; + unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); return snd_hda_input_mux_put(codec, spec->dinput_mux, ucontrol, - spec->dmux_nid, &spec->cur_dmux); + spec->dmux_nids[dmux_idx], &spec->cur_dmux[dmux_idx]); } static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -299,15 +411,45 @@ static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e spec->mux_nids[adc_idx], &spec->cur_mux[adc_idx]); } +static int stac92xx_mono_mux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + return snd_hda_input_mux_info(spec->mono_mux, uinfo); +} + +static int stac92xx_mono_mux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + + ucontrol->value.enumerated.item[0] = spec->cur_mmux; + return 0; +} + +static int stac92xx_mono_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + + return snd_hda_input_mux_put(codec, spec->mono_mux, ucontrol, + spec->mono_nid, &spec->cur_mmux); +} + #define stac92xx_aloopback_info snd_ctl_boolean_mono_info static int stac92xx_aloopback_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); struct sigmatel_spec *spec = codec->spec; - ucontrol->value.integer.value[0] = spec->aloopback; + ucontrol->value.integer.value[0] = !!(spec->aloopback & + (spec->aloopback_mask << idx)); return 0; } @@ -316,23 +458,33 @@ static int stac92xx_aloopback_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; + unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); unsigned int dac_mode; + unsigned int val, idx_val; - if (spec->aloopback == ucontrol->value.integer.value[0]) + idx_val = spec->aloopback_mask << idx; + if (ucontrol->value.integer.value[0]) + val = spec->aloopback | idx_val; + else + val = spec->aloopback & ~idx_val; + if (spec->aloopback == val) return 0; - spec->aloopback = ucontrol->value.integer.value[0]; - + spec->aloopback = val; + /* Only return the bits defined by the shift value of the + * first two bytes of the mask + */ dac_mode = snd_hda_codec_read(codec, codec->afg, 0, - kcontrol->private_value & 0xFFFF, 0x0); + kcontrol->private_value & 0xFFFF, 0x0); + dac_mode >>= spec->aloopback_shift; - if (spec->aloopback) { + if (spec->aloopback & idx_val) { snd_hda_power_up(codec); - dac_mode |= 0x40; + dac_mode |= idx_val; } else { snd_hda_power_down(codec); - dac_mode &= ~0x40; + dac_mode &= ~idx_val; } snd_hda_codec_write_cache(codec, codec->afg, 0, @@ -354,6 +506,107 @@ static struct hda_verb stac9200_eapd_init[] = { {} }; +static struct hda_verb stac92hd73xx_6ch_core_init[] = { + /* set master volume and direct control */ + { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + /* setup audio connections */ + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00}, + { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* setup adcs to point to mixer */ + { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + /* setup import muxs */ + { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x00}, + {} +}; + +static struct hda_verb stac92hd73xx_8ch_core_init[] = { + /* set master volume and direct control */ + { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + /* setup audio connections */ + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00}, + { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* connect hp ports to dac3 */ + { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x03}, + { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x03}, + /* setup adcs to point to mixer */ + { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + /* setup import muxs */ + { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x03}, + {} +}; + +static struct hda_verb stac92hd73xx_10ch_core_init[] = { + /* set master volume and direct control */ + { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + /* setup audio connections */ + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02 }, + /* dac3 is connected to import3 mux */ + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb07f}, + /* connect hp ports to dac4 */ + { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x04}, + { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x04}, + /* setup adcs to point to mixer */ + { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + /* setup import muxs */ + { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x03}, + {} +}; + +static struct hda_verb stac92hd71bxx_core_init[] = { + /* set master volume and direct control */ + { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + /* connect headphone jack to dac1 */ + { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */ + /* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */ + { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +}; + +static struct hda_verb stac92hd71bxx_analog_core_init[] = { + /* set master volume and direct control */ + { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + /* connect headphone jack to dac1 */ + { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01}, + /* connect ports 0d and 0f to audio mixer */ + { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x2}, + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2}, + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */ + /* unmute dac0 input in audio mixer */ + { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0x701f}, + /* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */ + { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {} +}; + static struct hda_verb stac925x_core_init[] = { /* set dac0mux for dac converter */ { 0x06, AC_VERB_SET_CONNECT_SEL, 0x00}, @@ -388,6 +641,16 @@ static struct hda_verb stac9205_core_init[] = { {} }; +#define STAC_MONO_MUX \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = "Mono Mux", \ + .count = 1, \ + .info = stac92xx_mono_mux_enum_info, \ + .get = stac92xx_mono_mux_enum_get, \ + .put = stac92xx_mono_mux_enum_put, \ + } + #define STAC_INPUT_SOURCE(cnt) \ { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ @@ -398,11 +661,11 @@ static struct hda_verb stac9205_core_init[] = { .put = stac92xx_mux_enum_put, \ } -#define STAC_ANALOG_LOOPBACK(verb_read,verb_write) \ +#define STAC_ANALOG_LOOPBACK(verb_read, verb_write, cnt) \ { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = "Analog Loopback", \ - .count = 1, \ + .count = cnt, \ .info = stac92xx_aloopback_info, \ .get = stac92xx_aloopback_get, \ .put = stac92xx_aloopback_put, \ @@ -419,6 +682,114 @@ static struct snd_kcontrol_new stac9200_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new stac92hd73xx_6ch_mixer[] = { + STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT), + + HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT), + + HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT), + HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT), + + HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT), + + HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT), + HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT), + { } /* end */ +}; + +static struct snd_kcontrol_new stac92hd73xx_8ch_mixer[] = { + STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT), + + HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT), + + HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT), + HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT), + + HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT), + + HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT), + HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT), + { } /* end */ +}; + +static struct snd_kcontrol_new stac92hd73xx_10ch_mixer[] = { + STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT), + + HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT), + + HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT), + HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT), + + HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT), + + HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT), + HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT), + { } /* end */ +}; + +static struct snd_kcontrol_new stac92hd71bxx_analog_mixer[] = { + STAC_INPUT_SOURCE(2), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x0, 0x1a, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT), + + HDA_CODEC_MUTE("Analog Loopback 1", 0x17, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("Analog Loopback 2", 0x17, 0x4, HDA_INPUT), + { } /* end */ +}; + +static struct snd_kcontrol_new stac92hd71bxx_mixer[] = { + STAC_INPUT_SOURCE(2), + STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x0, 0x1a, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT), + { } /* end */ +}; + static struct snd_kcontrol_new stac925x_mixer[] = { STAC_INPUT_SOURCE(1), HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_OUTPUT), @@ -428,16 +799,8 @@ static struct snd_kcontrol_new stac925x_mixer[] = { }; static struct snd_kcontrol_new stac9205_mixer[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Digital Input Source", - .count = 1, - .info = stac92xx_dmux_enum_info, - .get = stac92xx_dmux_enum_get, - .put = stac92xx_dmux_enum_put, - }, STAC_INPUT_SOURCE(2), - STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0), + STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1), HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1b, 0x0, HDA_INPUT), HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1d, 0x0, HDA_OUTPUT), @@ -466,7 +829,7 @@ static struct snd_kcontrol_new stac922x_mixer[] = { static struct snd_kcontrol_new stac927x_mixer[] = { STAC_INPUT_SOURCE(3), - STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB), + STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1), HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x18, 0x0, HDA_INPUT), HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1b, 0x0, HDA_OUTPUT), @@ -482,6 +845,44 @@ static struct snd_kcontrol_new stac927x_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new stac_dmux_mixer = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Digital Input Source", + /* count set later */ + .info = stac92xx_dmux_enum_info, + .get = stac92xx_dmux_enum_get, + .put = stac92xx_dmux_enum_put, +}; + +static const char *slave_vols[] = { + "Front Playback Volume", + "Surround Playback Volume", + "Center Playback Volume", + "LFE Playback Volume", + "Side Playback Volume", + "Headphone Playback Volume", + "Headphone Playback Volume", + "Speaker Playback Volume", + "External Speaker Playback Volume", + "Speaker2 Playback Volume", + NULL +}; + +static const char *slave_sws[] = { + "Front Playback Switch", + "Surround Playback Switch", + "Center Playback Switch", + "LFE Playback Switch", + "Side Playback Switch", + "Headphone Playback Switch", + "Headphone Playback Switch", + "Speaker Playback Switch", + "External Speaker Playback Switch", + "Speaker2 Playback Switch", + "IEC958 Playback Switch", + NULL +}; + static int stac92xx_build_controls(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; @@ -497,6 +898,13 @@ static int stac92xx_build_controls(struct hda_codec *codec) if (err < 0) return err; } + if (spec->num_dmuxes > 0) { + stac_dmux_mixer.count = spec->num_dmuxes; + err = snd_ctl_add(codec->bus->card, + snd_ctl_new1(&stac_dmux_mixer, codec)); + if (err < 0) + return err; + } if (spec->multiout.dig_out_nid) { err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); @@ -508,6 +916,23 @@ static int stac92xx_build_controls(struct hda_codec *codec) if (err < 0) return err; } + + /* if we have no master control, let's create it */ + if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { + snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0], + HDA_OUTPUT, spec->vmaster_tlv); + err = snd_hda_add_vmaster(codec, "Master Playback Volume", + spec->vmaster_tlv, slave_vols); + if (err < 0) + return err; + } + if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { + err = snd_hda_add_vmaster(codec, "Master Playback Switch", + NULL, slave_sws); + if (err < 0) + return err; + } + return 0; } @@ -733,7 +1158,7 @@ static struct snd_pci_quirk stac9200_cfg_tbl[] = { static unsigned int ref925x_pin_configs[8] = { 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021, - 0x90a70320, 0x02214210, 0x400003f1, 0x9033032e, + 0x90a70320, 0x02214210, 0x01019020, 0x9033032e, }; static unsigned int stac925x_MA6_pin_configs[8] = { @@ -777,6 +1202,48 @@ static struct snd_pci_quirk stac925x_cfg_tbl[] = { {} /* terminator */ }; +static unsigned int ref92hd73xx_pin_configs[12] = { + 0x02214030, 0x02a19040, 0x01a19020, 0x02214030, + 0x0181302e, 0x01014010, 0x01014020, 0x01014030, + 0x02319040, 0x90a000f0, 0x90a000f0, 0x01452050, +}; + +static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = { + [STAC_92HD73XX_REF] = ref92hd73xx_pin_configs, +}; + +static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = { + [STAC_92HD73XX_REF] = "ref", +}; + +static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = { + /* SigmaTel reference board */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, + "DFI LanParty", STAC_92HD73XX_REF), + {} /* terminator */ +}; + +static unsigned int ref92hd71bxx_pin_configs[10] = { + 0x02214030, 0x02a19040, 0x01a19020, 0x01014010, + 0x0181302e, 0x01114010, 0x01019020, 0x90a000f0, + 0x90a000f0, 0x01452050, +}; + +static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = { + [STAC_92HD71BXX_REF] = ref92hd71bxx_pin_configs, +}; + +static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = { + [STAC_92HD71BXX_REF] = "ref", +}; + +static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = { + /* SigmaTel reference board */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, + "DFI LanParty", STAC_92HD71BXX_REF), + {} /* terminator */ +}; + static unsigned int ref922x_pin_configs[10] = { 0x01014010, 0x01016011, 0x01012012, 0x0221401f, 0x01813122, 0x01011014, 0x01441030, 0x01c41030, @@ -823,8 +1290,8 @@ static unsigned int dell_922x_m81_pin_configs[10] = { 102801D7 (Dell XPS M1210) */ static unsigned int dell_922x_m82_pin_configs[10] = { - 0x0221121f, 0x408103ff, 0x02111212, 0x90100310, - 0x408003f1, 0x02111211, 0x03451340, 0x40c003f2, + 0x02211211, 0x408103ff, 0x02a1123e, 0x90100310, + 0x408003f1, 0x0221121f, 0x03451340, 0x40c003f2, 0x508003f3, 0x405003f4, }; @@ -1022,22 +1489,24 @@ static unsigned int d965_5st_pin_configs[14] = { static unsigned int dell_3st_pin_configs[14] = { 0x02211230, 0x02a11220, 0x01a19040, 0x01114210, 0x01111212, 0x01116211, 0x01813050, 0x01112214, - 0x403003fa, 0x40000100, 0x40000100, 0x404003fb, + 0x403003fa, 0x90a60040, 0x90a60040, 0x404003fb, 0x40c003fc, 0x40000100 }; static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = { - [STAC_D965_REF] = ref927x_pin_configs, - [STAC_D965_3ST] = d965_3st_pin_configs, - [STAC_D965_5ST] = d965_5st_pin_configs, - [STAC_DELL_3ST] = dell_3st_pin_configs, + [STAC_D965_REF] = ref927x_pin_configs, + [STAC_D965_3ST] = d965_3st_pin_configs, + [STAC_D965_5ST] = d965_5st_pin_configs, + [STAC_DELL_3ST] = dell_3st_pin_configs, + [STAC_DELL_BIOS] = NULL, }; static const char *stac927x_models[STAC_927X_MODELS] = { - [STAC_D965_REF] = "ref", - [STAC_D965_3ST] = "3stack", - [STAC_D965_5ST] = "5stack", - [STAC_DELL_3ST] = "dell-3stack", + [STAC_D965_REF] = "ref", + [STAC_D965_3ST] = "3stack", + [STAC_D965_5ST] = "5stack", + [STAC_DELL_3ST] = "dell-3stack", + [STAC_DELL_BIOS] = "dell-bios", }; static struct snd_pci_quirk stac927x_cfg_tbl[] = { @@ -1064,13 +1533,21 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2003, "Intel D965", STAC_D965_3ST), SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2002, "Intel D965", STAC_D965_3ST), SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2001, "Intel D965", STAC_D965_3ST), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f3, "Dell Inspiron 1420", STAC_D965_3ST), /* Dell 3 stack systems */ + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f7, "Dell XPS M1730", STAC_DELL_3ST), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01dd, "Dell Dimension E520", STAC_DELL_3ST), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ed, "Dell ", STAC_DELL_3ST), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f4, "Dell ", STAC_DELL_3ST), + /* Dell 3 stack systems with verb table in BIOS */ + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f3, "Dell Inspiron 1420", STAC_DELL_BIOS), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0227, "Dell Vostro 1400 ", STAC_DELL_BIOS), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022f, "Dell ", STAC_DELL_BIOS), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022e, "Dell ", STAC_DELL_BIOS), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0242, "Dell ", STAC_DELL_BIOS), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0243, "Dell ", STAC_DELL_BIOS), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ff, "Dell ", STAC_DELL_BIOS), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_DELL_BIOS), /* 965 based 5 stack systems */ - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_D965_5ST), SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2301, "Intel D965", STAC_D965_5ST), SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2302, "Intel D965", STAC_D965_5ST), SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2303, "Intel D965", STAC_D965_5ST), @@ -1085,7 +1562,7 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = { static unsigned int ref9205_pin_configs[12] = { 0x40000100, 0x40000100, 0x01016011, 0x01014010, - 0x01813122, 0x01a19021, 0x40000100, 0x40000100, + 0x01813122, 0x01a19021, 0x01019020, 0x40000100, 0x90a000f0, 0x90a000f0, 0x01441030, 0x01c41030 }; @@ -1097,6 +1574,7 @@ static unsigned int ref9205_pin_configs[12] = { 102801FD 10280204 1028021F + 10280228 (Dell Vostro 1500) */ static unsigned int dell_9205_m42_pin_configs[12] = { 0x0321101F, 0x03A11020, 0x400003FA, 0x90170310, @@ -1180,6 +1658,8 @@ static struct snd_pci_quirk stac9205_cfg_tbl[] = { "unknown Dell", STAC_9205_DELL_M42), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021f, "Dell Inspiron", STAC_9205_DELL_M44), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228, + "Dell Vostro 1500", STAC_9205_DELL_M42), {} /* terminator */ }; @@ -1245,22 +1725,6 @@ static void stac92xx_set_config_regs(struct hda_codec *codec) spec->pin_configs[i]); } -static void stac92xx_enable_gpio_mask(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - /* Configure GPIOx as output */ - snd_hda_codec_write_cache(codec, codec->afg, 0, - AC_VERB_SET_GPIO_DIRECTION, spec->gpio_mask); - /* Configure GPIOx as CMOS */ - snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7e7, 0x00000000); - /* Assert GPIOx */ - snd_hda_codec_write_cache(codec, codec->afg, 0, - AC_VERB_SET_GPIO_DATA, spec->gpio_data); - /* Enable GPIOx */ - snd_hda_codec_write_cache(codec, codec->afg, 0, - AC_VERB_SET_GPIO_MASK, spec->gpio_mask); -} - /* * Analog playback callbacks */ @@ -1479,7 +1943,7 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ struct sigmatel_spec *spec = codec->spec; hda_nid_t nid = kcontrol->private_value >> 8; int io_idx = kcontrol-> private_value & 0xff; - unsigned short val = ucontrol->value.integer.value[0]; + unsigned short val = !!ucontrol->value.integer.value[0]; spec->io_switch[io_idx] = val; @@ -1491,6 +1955,13 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ pinctl |= stac92xx_get_vref(codec, nid); stac92xx_auto_set_pinctl(codec, nid, pinctl); } + + /* check the auto-mute again: we need to mute/unmute the speaker + * appropriately according to the pin direction + */ + if (spec->hp_detect) + codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26); + return 1; } @@ -1512,11 +1983,12 @@ static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol, struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; hda_nid_t nid = kcontrol->private_value & 0xff; + unsigned int val = !!ucontrol->value.integer.value[0]; - if (spec->clfe_swap == ucontrol->value.integer.value[0]) + if (spec->clfe_swap == val) return 0; - spec->clfe_swap = ucontrol->value.integer.value[0]; + spec->clfe_swap = val; snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE, spec->clfe_swap ? 0x4 : 0x0); @@ -1547,6 +2019,7 @@ static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol, enum { STAC_CTL_WIDGET_VOL, STAC_CTL_WIDGET_MUTE, + STAC_CTL_WIDGET_MONO_MUX, STAC_CTL_WIDGET_IO_SWITCH, STAC_CTL_WIDGET_CLFE_SWITCH }; @@ -1554,6 +2027,7 @@ enum { static struct snd_kcontrol_new stac92xx_control_templates[] = { HDA_CODEC_VOLUME(NULL, 0, 0, 0), HDA_CODEC_MUTE(NULL, 0, 0, 0), + STAC_MONO_MUX, STAC_CODEC_IO_SWITCH(NULL, 0), STAC_CODEC_CLFE_SWITCH(NULL, 0), }; @@ -1598,6 +2072,7 @@ static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cf for (i = 0; i < codec->num_nodes; i++) { wcaps = codec->wcaps[i]; wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; + if (wtype == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL)) num_dacs++; } @@ -1685,7 +2160,6 @@ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec, wcaps = snd_hda_param_read(codec, conn[j], AC_PAR_AUDIO_WIDGET_CAP); wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; - if (wtype != AC_WID_AUD_OUT || (wcaps & AC_WCAP_DIGITAL)) continue; @@ -1759,7 +2233,7 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, int i, err; struct sigmatel_spec *spec = codec->spec; - unsigned int wid_caps; + unsigned int wid_caps, pincap; for (i = 0; i < cfg->line_outs; i++) { @@ -1795,13 +2269,39 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, } } - if (spec->line_switch) - if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, "Line In as Output Switch", cfg->input_pins[AUTO_PIN_LINE] << 8)) < 0) - return err; + if (spec->line_switch) { + nid = cfg->input_pins[AUTO_PIN_LINE]; + pincap = snd_hda_param_read(codec, nid, + AC_PAR_PIN_CAP); + if (pincap & AC_PINCAP_OUT) { + err = stac92xx_add_control(spec, + STAC_CTL_WIDGET_IO_SWITCH, + "Line In as Output Switch", nid << 8); + if (err < 0) + return err; + } + } - if (spec->mic_switch) - if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, "Mic as Output Switch", (cfg->input_pins[AUTO_PIN_MIC] << 8) | 1)) < 0) - return err; + if (spec->mic_switch) { + unsigned int def_conf; + nid = cfg->input_pins[AUTO_PIN_MIC]; + def_conf = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CONFIG_DEFAULT, 0); + + /* some laptops have an internal analog microphone + * which can't be used as a output */ + if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) { + pincap = snd_hda_param_read(codec, nid, + AC_PAR_PIN_CAP); + if (pincap & AC_PINCAP_OUT) { + err = stac92xx_add_control(spec, + STAC_CTL_WIDGET_IO_SWITCH, + "Mic as Output Switch", (nid << 8) | 1); + if (err < 0) + return err; + } + } + } return 0; } @@ -1891,6 +2391,37 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec, return 0; } +/* labels for mono mux outputs */ +static const char *stac92xx_mono_labels[3] = { + "DAC0", "DAC1", "Mixer" +}; + +/* create mono mux for mono out on capable codecs */ +static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + struct hda_input_mux *mono_mux = &spec->private_mono_mux; + int i, num_cons; + hda_nid_t con_lst[ARRAY_SIZE(stac92xx_mono_labels)]; + + num_cons = snd_hda_get_connections(codec, + spec->mono_nid, + con_lst, + HDA_MAX_NUM_INPUTS); + if (!num_cons || num_cons > ARRAY_SIZE(stac92xx_mono_labels)) + return -EINVAL; + + for (i = 0; i < num_cons; i++) { + mono_mux->items[mono_mux->num_items].label = + stac92xx_mono_labels[i]; + mono_mux->items[mono_mux->num_items].index = i; + mono_mux->num_items++; + } + + return stac92xx_add_control(spec, STAC_CTL_WIDGET_MONO_MUX, + "Mono Mux", spec->mono_nid); +} + /* labels for dmic mux inputs */ static const char *stac92xx_dmic_labels[5] = { "Analog Inputs", "Digital Mic 1", "Digital Mic 2", @@ -1904,15 +2435,18 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, struct sigmatel_spec *spec = codec->spec; struct hda_input_mux *dimux = &spec->private_dimux; hda_nid_t con_lst[HDA_MAX_NUM_INPUTS]; - int i, j; + int err, i, j; + char name[32]; dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0]; dimux->items[dimux->num_items].index = 0; dimux->num_items++; for (i = 0; i < spec->num_dmics; i++) { + hda_nid_t nid; int index; int num_cons; + unsigned int wcaps; unsigned int def_conf; def_conf = snd_hda_codec_read(codec, @@ -1923,17 +2457,32 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) continue; + nid = spec->dmic_nids[i]; num_cons = snd_hda_get_connections(codec, - spec->dmux_nid, + spec->dmux_nids[0], con_lst, HDA_MAX_NUM_INPUTS); for (j = 0; j < num_cons; j++) - if (con_lst[j] == spec->dmic_nids[i]) { + if (con_lst[j] == nid) { index = j; goto found; } continue; found: + wcaps = get_wcaps(codec, nid); + + if (wcaps & AC_WCAP_OUT_AMP) { + sprintf(name, "%s Capture Volume", + stac92xx_dmic_labels[dimux->num_items]); + + err = stac92xx_add_control(spec, + STAC_CTL_WIDGET_VOL, + name, + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + } + dimux->items[dimux->num_items].label = stac92xx_dmic_labels[dimux->num_items]; dimux->items[dimux->num_items].index = index; @@ -2026,6 +2575,7 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out { struct sigmatel_spec *spec = codec->spec; int err; + int hp_speaker_swap = 0; if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, @@ -2034,6 +2584,68 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out if (! spec->autocfg.line_outs) return 0; /* can't find valid pin config */ + /* If we have no real line-out pin and multiple hp-outs, HPs should + * be set up as multi-channel outputs. + */ + if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT && + spec->autocfg.hp_outs > 1) { + /* Copy hp_outs to line_outs, backup line_outs in + * speaker_outs so that the following routines can handle + * HP pins as primary outputs. + */ + memcpy(spec->autocfg.speaker_pins, spec->autocfg.line_out_pins, + sizeof(spec->autocfg.line_out_pins)); + spec->autocfg.speaker_outs = spec->autocfg.line_outs; + memcpy(spec->autocfg.line_out_pins, spec->autocfg.hp_pins, + sizeof(spec->autocfg.hp_pins)); + spec->autocfg.line_outs = spec->autocfg.hp_outs; + hp_speaker_swap = 1; + } + if (spec->autocfg.mono_out_pin) { + int dir = (get_wcaps(codec, spec->autocfg.mono_out_pin) + & AC_WCAP_OUT_AMP) ? HDA_OUTPUT : HDA_INPUT; + u32 caps = query_amp_caps(codec, + spec->autocfg.mono_out_pin, dir); + hda_nid_t conn_list[1]; + + /* get the mixer node and then the mono mux if it exists */ + if (snd_hda_get_connections(codec, + spec->autocfg.mono_out_pin, conn_list, 1) && + snd_hda_get_connections(codec, conn_list[0], + conn_list, 1)) { + + int wcaps = get_wcaps(codec, conn_list[0]); + int wid_type = (wcaps & AC_WCAP_TYPE) + >> AC_WCAP_TYPE_SHIFT; + /* LR swap check, some stac925x have a mux that + * changes the DACs output path instead of the + * mono-mux path. + */ + if (wid_type == AC_WID_AUD_SEL && + !(wcaps & AC_WCAP_LR_SWAP)) + spec->mono_nid = conn_list[0]; + } + /* all mono outs have a least a mute/unmute switch */ + err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, + "Mono Playback Switch", + HDA_COMPOSE_AMP_VAL(spec->autocfg.mono_out_pin, + 1, 0, dir)); + if (err < 0) + return err; + /* check to see if there is volume support for the amp */ + if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) { + err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, + "Mono Playback Volume", + HDA_COMPOSE_AMP_VAL(spec->autocfg.mono_out_pin, + 1, 0, dir)); + if (err < 0) + return err; + } + + stac92xx_auto_set_pinctl(codec, spec->autocfg.mono_out_pin, + AC_PINCTL_OUT_EN); + } + if ((err = stac92xx_add_dyn_out_pins(codec, &spec->autocfg)) < 0) return err; if (spec->multiout.num_dacs == 0) @@ -2045,6 +2657,19 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out if (err < 0) return err; + if (hp_speaker_swap == 1) { + /* Restore the hp_outs and line_outs */ + memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins, + sizeof(spec->autocfg.line_out_pins)); + spec->autocfg.hp_outs = spec->autocfg.line_outs; + memcpy(spec->autocfg.line_out_pins, spec->autocfg.speaker_pins, + sizeof(spec->autocfg.speaker_pins)); + spec->autocfg.line_outs = spec->autocfg.speaker_outs; + memset(spec->autocfg.speaker_pins, 0, + sizeof(spec->autocfg.speaker_pins)); + spec->autocfg.speaker_outs = 0; + } + err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg); if (err < 0) @@ -2055,6 +2680,12 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out if (err < 0) return err; + if (spec->mono_nid > 0) { + err = stac92xx_auto_create_mono_output_ctls(codec); + if (err < 0) + return err; + } + if (spec->num_dmics > 0) if ((err = stac92xx_auto_create_dmic_input_ctls(codec, &spec->autocfg)) < 0) @@ -2073,7 +2704,9 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out spec->mixers[spec->num_mixers++] = spec->kctl_alloc; spec->input_mux = &spec->private_imux; - spec->dinput_mux = &spec->private_dimux; + if (!spec->dinput_mux) + spec->dinput_mux = &spec->private_dimux; + spec->mono_mux = &spec->private_mono_mux; return 1; } @@ -2183,38 +2816,35 @@ static int stac9200_parse_auto_config(struct hda_codec *codec) * funky external mute control using GPIO pins. */ -static void stac922x_gpio_mute(struct hda_codec *codec, int pin, int muted) +static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, + unsigned int dir_mask, unsigned int data) { unsigned int gpiostate, gpiomask, gpiodir; gpiostate = snd_hda_codec_read(codec, codec->afg, 0, AC_VERB_GET_GPIO_DATA, 0); - - if (!muted) - gpiostate |= (1 << pin); - else - gpiostate &= ~(1 << pin); + gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask); gpiomask = snd_hda_codec_read(codec, codec->afg, 0, AC_VERB_GET_GPIO_MASK, 0); - gpiomask |= (1 << pin); + gpiomask |= mask; gpiodir = snd_hda_codec_read(codec, codec->afg, 0, AC_VERB_GET_GPIO_DIRECTION, 0); - gpiodir |= (1 << pin); + gpiodir |= dir_mask; - /* AppleHDA seems to do this -- WTF is this verb?? */ + /* Configure GPIOx as CMOS */ snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0); snd_hda_codec_write(codec, codec->afg, 0, AC_VERB_SET_GPIO_MASK, gpiomask); - snd_hda_codec_write(codec, codec->afg, 0, - AC_VERB_SET_GPIO_DIRECTION, gpiodir); + snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */ msleep(1); - snd_hda_codec_write(codec, codec->afg, 0, - AC_VERB_SET_GPIO_DATA, gpiostate); + snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */ } static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid, @@ -2226,6 +2856,16 @@ static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid, (AC_USRSP_EN | event)); } +static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid) +{ + int i; + for (i = 0; i < cfg->hp_outs; i++) + if (cfg->hp_pins[i] == nid) + return 1; /* nid is a HP-Out */ + + return 0; /* nid is not a HP-Out */ +}; + static int stac92xx_init(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; @@ -2261,10 +2901,23 @@ static int stac92xx_init(struct hda_codec *codec) stac92xx_auto_set_pinctl(codec, nid, pinctl); } } - if (spec->num_dmics > 0) - for (i = 0; i < spec->num_dmics; i++) - stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i], - AC_PINCTL_IN_EN); + for (i = 0; i < spec->num_dmics; i++) + stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i], + AC_PINCTL_IN_EN); + for (i = 0; i < spec->num_pwrs; i++) { + int event = is_nid_hp_pin(cfg, spec->pwr_nids[i]) + ? STAC_HP_EVENT : STAC_PWR_EVENT; + int pinctl = snd_hda_codec_read(codec, spec->pwr_nids[i], + 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + /* outputs are only ports capable of power management + * any attempts on powering down a input port cause the + * referenced VREF to act quirky. + */ + if (pinctl & AC_PINCTL_IN_EN) + continue; + enable_pin_detect(codec, spec->pwr_nids[i], event | i); + codec->patch_ops.unsol_event(codec, (event | i) << 26); + } if (cfg->dig_out_pin) stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin, @@ -2273,10 +2926,8 @@ static int stac92xx_init(struct hda_codec *codec) stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin, AC_PINCTL_IN_EN); - if (spec->gpio_mute) { - stac922x_gpio_mute(codec, 0, 0); - stac922x_gpio_mute(codec, 1, 0); - } + stac_gpio_set(codec, spec->gpio_mask, + spec->gpio_dir, spec->gpio_data); return 0; } @@ -2342,13 +2993,20 @@ static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid, pin_ctl & ~flag); } -static int get_pin_presence(struct hda_codec *codec, hda_nid_t nid) +static int get_hp_pin_presence(struct hda_codec *codec, hda_nid_t nid) { if (!nid) return 0; if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0x00) - & (1 << 31)) - return 1; + & (1 << 31)) { + unsigned int pinctl; + pinctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + if (pinctl & AC_PINCTL_IN_EN) + return 0; /* mic- or line-input */ + else + return 1; /* HP-output */ + } return 0; } @@ -2359,10 +3017,14 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res) int i, presence; presence = 0; + if (spec->gpio_mute) + presence = !(snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_DATA, 0) & spec->gpio_mute); + for (i = 0; i < cfg->hp_outs; i++) { - presence = get_pin_presence(codec, cfg->hp_pins[i]); if (presence) break; + presence = get_hp_pin_presence(codec, cfg->hp_pins[i]); } if (presence) { @@ -2384,12 +3046,37 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res) } } +static void stac92xx_pin_sense(struct hda_codec *codec, int idx) +{ + struct sigmatel_spec *spec = codec->spec; + hda_nid_t nid = spec->pwr_nids[idx]; + int presence, val; + val = snd_hda_codec_read(codec, codec->afg, 0, 0x0fec, 0x0) + & 0x000000ff; + presence = get_hp_pin_presence(codec, nid); + idx = 1 << idx; + + if (presence) + val &= ~idx; + else + val |= idx; + + /* power down unused output ports */ + snd_hda_codec_write(codec, codec->afg, 0, 0x7ec, val); +}; + static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res) { - switch (res >> 26) { + struct sigmatel_spec *spec = codec->spec; + int idx = res >> 26 & 0x0f; + + switch ((res >> 26) & 0x30) { case STAC_HP_EVENT: stac92xx_hp_detect(codec, res); - break; + /* fallthru */ + case STAC_PWR_EVENT: + if (spec->num_pwrs > 0) + stac92xx_pin_sense(codec, idx); } } @@ -2400,10 +3087,8 @@ static int stac92xx_resume(struct hda_codec *codec) stac92xx_set_config_regs(codec); snd_hda_sequence_write(codec, spec->init); - if (spec->gpio_mute) { - stac922x_gpio_mute(codec, 0, 0); - stac922x_gpio_mute(codec, 1, 0); - } + stac_gpio_set(codec, spec->gpio_mask, + spec->gpio_dir, spec->gpio_data); snd_hda_codec_resume_amp(codec); snd_hda_codec_resume_cache(codec); /* invoke unsolicited event to reset the HP state */ @@ -2460,6 +3145,7 @@ static int patch_stac9200(struct hda_codec *codec) spec->num_muxes = 1; spec->num_dmics = 0; spec->num_adcs = 1; + spec->num_pwrs = 0; if (spec->board_config == STAC_9200_GATEWAY) spec->init = stac9200_eapd_init; @@ -2515,6 +3201,7 @@ static int patch_stac925x(struct hda_codec *codec) spec->mux_nids = stac925x_mux_nids; spec->num_muxes = 1; spec->num_adcs = 1; + spec->num_pwrs = 0; switch (codec->vendor_id) { case 0x83847632: /* STAC9202 */ case 0x83847633: /* STAC9202D */ @@ -2522,6 +3209,8 @@ static int patch_stac925x(struct hda_codec *codec) case 0x83847637: /* STAC9251D */ spec->num_dmics = STAC925X_NUM_DMICS; spec->dmic_nids = stac925x_dmic_nids; + spec->num_dmuxes = ARRAY_SIZE(stac925x_dmux_nids); + spec->dmux_nids = stac925x_dmux_nids; break; default: spec->num_dmics = 0; @@ -2551,6 +3240,204 @@ static int patch_stac925x(struct hda_codec *codec) return 0; } +static struct hda_input_mux stac92hd73xx_dmux = { + .num_items = 4, + .items = { + { "Analog Inputs", 0x0b }, + { "CD", 0x08 }, + { "Digital Mic 1", 0x09 }, + { "Digital Mic 2", 0x0a }, + } +}; + +static int patch_stac92hd73xx(struct hda_codec *codec) +{ + struct sigmatel_spec *spec; + hda_nid_t conn[STAC92HD73_DAC_COUNT + 2]; + int err = 0; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + spec->num_pins = ARRAY_SIZE(stac92hd73xx_pin_nids); + spec->pin_nids = stac92hd73xx_pin_nids; + spec->board_config = snd_hda_check_board_config(codec, + STAC_92HD73XX_MODELS, + stac92hd73xx_models, + stac92hd73xx_cfg_tbl); +again: + if (spec->board_config < 0) { + snd_printdd(KERN_INFO "hda_codec: Unknown model for" + " STAC92HD73XX, using BIOS defaults\n"); + err = stac92xx_save_bios_config_regs(codec); + if (err < 0) { + stac92xx_free(codec); + return err; + } + spec->pin_configs = spec->bios_pin_configs; + } else { + spec->pin_configs = stac92hd73xx_brd_tbl[spec->board_config]; + stac92xx_set_config_regs(codec); + } + + spec->multiout.num_dacs = snd_hda_get_connections(codec, 0x0a, + conn, STAC92HD73_DAC_COUNT + 2) - 1; + + if (spec->multiout.num_dacs < 0) { + printk(KERN_WARNING "hda_codec: Could not determine " + "number of channels defaulting to DAC count\n"); + spec->multiout.num_dacs = STAC92HD73_DAC_COUNT; + } + + switch (spec->multiout.num_dacs) { + case 0x3: /* 6 Channel */ + spec->mixer = stac92hd73xx_6ch_mixer; + spec->init = stac92hd73xx_6ch_core_init; + break; + case 0x4: /* 8 Channel */ + spec->multiout.hp_nid = 0x18; + spec->mixer = stac92hd73xx_8ch_mixer; + spec->init = stac92hd73xx_8ch_core_init; + break; + case 0x5: /* 10 Channel */ + spec->multiout.hp_nid = 0x19; + spec->mixer = stac92hd73xx_10ch_mixer; + spec->init = stac92hd73xx_10ch_core_init; + }; + + spec->multiout.dac_nids = stac92hd73xx_dac_nids; + spec->aloopback_mask = 0x01; + spec->aloopback_shift = 8; + + spec->mux_nids = stac92hd73xx_mux_nids; + spec->adc_nids = stac92hd73xx_adc_nids; + spec->dmic_nids = stac92hd73xx_dmic_nids; + spec->dmux_nids = stac92hd73xx_dmux_nids; + + spec->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids); + spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids); + spec->num_dmics = STAC92HD73XX_NUM_DMICS; + spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids); + spec->dinput_mux = &stac92hd73xx_dmux; + /* GPIO0 High = Enable EAPD */ + spec->gpio_mask = spec->gpio_dir = 0x1; + spec->gpio_data = 0x01; + + spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids); + spec->pwr_nids = stac92hd73xx_pwr_nids; + + err = stac92xx_parse_auto_config(codec, 0x22, 0x24); + + if (!err) { + if (spec->board_config < 0) { + printk(KERN_WARNING "hda_codec: No auto-config is " + "available, default to model=ref\n"); + spec->board_config = STAC_92HD73XX_REF; + goto again; + } + err = -EINVAL; + } + + if (err < 0) { + stac92xx_free(codec); + return err; + } + + codec->patch_ops = stac92xx_patch_ops; + + return 0; +} + +static int patch_stac92hd71bxx(struct hda_codec *codec) +{ + struct sigmatel_spec *spec; + int err = 0; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + spec->num_pins = ARRAY_SIZE(stac92hd71bxx_pin_nids); + spec->pin_nids = stac92hd71bxx_pin_nids; + spec->board_config = snd_hda_check_board_config(codec, + STAC_92HD71BXX_MODELS, + stac92hd71bxx_models, + stac92hd71bxx_cfg_tbl); +again: + if (spec->board_config < 0) { + snd_printdd(KERN_INFO "hda_codec: Unknown model for" + " STAC92HD71BXX, using BIOS defaults\n"); + err = stac92xx_save_bios_config_regs(codec); + if (err < 0) { + stac92xx_free(codec); + return err; + } + spec->pin_configs = spec->bios_pin_configs; + } else { + spec->pin_configs = stac92hd71bxx_brd_tbl[spec->board_config]; + stac92xx_set_config_regs(codec); + } + + switch (codec->vendor_id) { + case 0x111d76b6: /* 4 Port without Analog Mixer */ + case 0x111d76b7: + case 0x111d76b4: /* 6 Port without Analog Mixer */ + case 0x111d76b5: + spec->mixer = stac92hd71bxx_mixer; + spec->init = stac92hd71bxx_core_init; + break; + default: + spec->mixer = stac92hd71bxx_analog_mixer; + spec->init = stac92hd71bxx_analog_core_init; + } + + spec->aloopback_mask = 0x20; + spec->aloopback_shift = 0; + + /* GPIO0 High = EAPD */ + spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0x1; + + spec->mux_nids = stac92hd71bxx_mux_nids; + spec->adc_nids = stac92hd71bxx_adc_nids; + spec->dmic_nids = stac92hd71bxx_dmic_nids; + spec->dmux_nids = stac92hd71bxx_dmux_nids; + + spec->num_muxes = ARRAY_SIZE(stac92hd71bxx_mux_nids); + spec->num_adcs = ARRAY_SIZE(stac92hd71bxx_adc_nids); + spec->num_dmics = STAC92HD71BXX_NUM_DMICS; + spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids); + + spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids); + spec->pwr_nids = stac92hd71bxx_pwr_nids; + + spec->multiout.num_dacs = 2; + spec->multiout.hp_nid = 0x11; + spec->multiout.dac_nids = stac92hd71bxx_dac_nids; + + err = stac92xx_parse_auto_config(codec, 0x21, 0x23); + if (!err) { + if (spec->board_config < 0) { + printk(KERN_WARNING "hda_codec: No auto-config is " + "available, default to model=ref\n"); + spec->board_config = STAC_92HD71BXX_REF; + goto again; + } + err = -EINVAL; + } + + if (err < 0) { + stac92xx_free(codec); + return err; + } + + codec->patch_ops = stac92xx_patch_ops; + + return 0; +}; + static int patch_stac922x(struct hda_codec *codec) { struct sigmatel_spec *spec; @@ -2567,7 +3454,8 @@ static int patch_stac922x(struct hda_codec *codec) stac922x_models, stac922x_cfg_tbl); if (spec->board_config == STAC_INTEL_MAC_V3) { - spec->gpio_mute = 1; + spec->gpio_mask = spec->gpio_dir = 0x03; + spec->gpio_data = 0x03; /* Intel Macs have all same PCI SSID, so we need to check * codec SSID to distinguish the exact models */ @@ -2620,6 +3508,7 @@ static int patch_stac922x(struct hda_codec *codec) spec->num_muxes = ARRAY_SIZE(stac922x_mux_nids); spec->num_adcs = ARRAY_SIZE(stac922x_adc_nids); spec->num_dmics = 0; + spec->num_pwrs = 0; spec->init = stac922x_core_init; spec->mixer = stac922x_mixer; @@ -2669,53 +3558,70 @@ static int patch_stac927x(struct hda_codec *codec) stac927x_models, stac927x_cfg_tbl); again: - if (spec->board_config < 0) { - snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC927x, using BIOS defaults\n"); + if (spec->board_config < 0 || !stac927x_brd_tbl[spec->board_config]) { + if (spec->board_config < 0) + snd_printdd(KERN_INFO "hda_codec: Unknown model for" + "STAC927x, using BIOS defaults\n"); err = stac92xx_save_bios_config_regs(codec); if (err < 0) { stac92xx_free(codec); return err; } spec->pin_configs = spec->bios_pin_configs; - } else if (stac927x_brd_tbl[spec->board_config] != NULL) { + } else { spec->pin_configs = stac927x_brd_tbl[spec->board_config]; stac92xx_set_config_regs(codec); } + spec->adc_nids = stac927x_adc_nids; + spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids); + spec->mux_nids = stac927x_mux_nids; + spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids); + spec->multiout.dac_nids = spec->dac_nids; + switch (spec->board_config) { case STAC_D965_3ST: - spec->adc_nids = stac927x_adc_nids; - spec->mux_nids = stac927x_mux_nids; - spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids); - spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids); + case STAC_D965_5ST: + /* GPIO0 High = Enable EAPD */ + spec->gpio_mask = spec->gpio_dir = 0x01; + spec->gpio_data = 0x01; spec->num_dmics = 0; + spec->init = d965_core_init; spec->mixer = stac927x_mixer; break; - case STAC_D965_5ST: - spec->adc_nids = stac927x_adc_nids; - spec->mux_nids = stac927x_mux_nids; - spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids); - spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids); - spec->num_dmics = 0; + case STAC_DELL_BIOS: + /* correct the front output jack as a hp out */ + stac92xx_set_config_reg(codec, 0x0f, 0x02270110); + /* correct the front input jack as a mic */ + stac92xx_set_config_reg(codec, 0x0e, 0x02a79130); + /* fallthru */ + case STAC_DELL_3ST: + /* GPIO2 High = Enable EAPD */ + spec->gpio_mask = spec->gpio_dir = 0x04; + spec->gpio_data = 0x04; + spec->dmic_nids = stac927x_dmic_nids; + spec->num_dmics = STAC927X_NUM_DMICS; + spec->init = d965_core_init; spec->mixer = stac927x_mixer; + spec->dmux_nids = stac927x_dmux_nids; + spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids); break; default: - spec->adc_nids = stac927x_adc_nids; - spec->mux_nids = stac927x_mux_nids; - spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids); - spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids); + /* GPIO0 High = Enable EAPD */ + spec->gpio_mask = spec->gpio_dir = 0x1; + spec->gpio_data = 0x01; spec->num_dmics = 0; + spec->init = stac927x_core_init; spec->mixer = stac927x_mixer; } - spec->multiout.dac_nids = spec->dac_nids; - /* GPIO0 High = Enable EAPD */ - spec->gpio_mask = spec->gpio_data = 0x00000001; - stac92xx_enable_gpio_mask(codec); - + spec->num_pwrs = 0; + spec->aloopback_mask = 0x40; + spec->aloopback_shift = 0; + err = stac92xx_parse_auto_config(codec, 0x1e, 0x20); if (!err) { if (spec->board_config < 0) { @@ -2733,6 +3639,18 @@ static int patch_stac927x(struct hda_codec *codec) codec->patch_ops = stac92xx_patch_ops; + /* + * !!FIXME!! + * The STAC927x seem to require fairly long delays for certain + * command sequences. With too short delays (even if the answer + * is set to RIRB properly), it results in the silence output + * on some hardwares like Dell. + * + * The below flag enables the longer delay (see get_response + * in hda_intel.c). + */ + codec->bus->needs_damn_long_delay = 1; + return 0; } @@ -2771,11 +3689,15 @@ static int patch_stac9205(struct hda_codec *codec) spec->num_muxes = ARRAY_SIZE(stac9205_mux_nids); spec->dmic_nids = stac9205_dmic_nids; spec->num_dmics = STAC9205_NUM_DMICS; - spec->dmux_nid = 0x1d; + spec->dmux_nids = stac9205_dmux_nids; + spec->num_dmuxes = ARRAY_SIZE(stac9205_dmux_nids); + spec->num_pwrs = 0; spec->init = stac9205_core_init; spec->mixer = stac9205_mixer; + spec->aloopback_mask = 0x40; + spec->aloopback_shift = 0; spec->multiout.dac_nids = spec->dac_nids; switch (spec->board_config){ @@ -2784,19 +3706,28 @@ static int patch_stac9205(struct hda_codec *codec) stac92xx_set_config_reg(codec, 0x1f, 0x01441030); stac92xx_set_config_reg(codec, 0x20, 0x1c410030); - spec->gpio_mask = 0x00000007; /* GPIO0-2 */ - /* GPIO0 High = EAPD, GPIO1 Low = DRM, - * GPIO2 High = Headphone Mute + /* Enable unsol response for GPIO4/Dock HP connection */ + snd_hda_codec_write(codec, codec->afg, 0, + AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10); + snd_hda_codec_write_cache(codec, codec->afg, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + (AC_USRSP_EN | STAC_HP_EVENT)); + + spec->gpio_dir = 0x0b; + spec->gpio_mask = 0x1b; + spec->gpio_mute = 0x10; + /* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute, + * GPIO3 Low = DRM */ - spec->gpio_data = 0x00000005; + spec->gpio_data = 0x01; break; default: /* GPIO0 High = EAPD */ - spec->gpio_mask = spec->gpio_data = 0x00000001; + spec->gpio_mask = spec->gpio_dir = 0x1; + spec->gpio_data = 0x01; break; } - stac92xx_enable_gpio_mask(codec); err = stac92xx_parse_auto_config(codec, 0x1f, 0x20); if (!err) { if (spec->board_config < 0) { @@ -2950,7 +3881,7 @@ static int stac9872_vaio_init(struct hda_codec *codec) static void stac9872_vaio_hp_detect(struct hda_codec *codec, unsigned int res) { - if (get_pin_presence(codec, 0x0a)) { + if (get_hp_pin_presence(codec, 0x0a)) { stac92xx_reset_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN); stac92xx_set_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN); } else { @@ -3032,6 +3963,7 @@ static int patch_stac9872(struct hda_codec *codec) spec->multiout.hp_nid = VAIO_HP_DAC; spec->num_adcs = ARRAY_SIZE(vaio_adcs); spec->adc_nids = vaio_adcs; + spec->num_pwrs = 0; spec->input_mux = &vaio_mux; spec->mux_nids = vaio_mux_nids; codec->patch_ops = stac9872_vaio_patch_ops; @@ -3045,6 +3977,7 @@ static int patch_stac9872(struct hda_codec *codec) spec->multiout.dac_nids = vaio_dacs; spec->multiout.hp_nid = VAIO_HP_DAC; spec->num_adcs = ARRAY_SIZE(vaio_adcs); + spec->num_pwrs = 0; spec->adc_nids = vaio_adcs; spec->input_mux = &vaio_mux; spec->mux_nids = vaio_mux_nids; @@ -3104,5 +4037,17 @@ struct hda_codec_preset snd_hda_preset_sigmatel[] = { { .id = 0x838476a5, .name = "STAC9255D", .patch = patch_stac9205 }, { .id = 0x838476a6, .name = "STAC9254", .patch = patch_stac9205 }, { .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 }, + { .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx }, + { .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx }, + { .id = 0x111d7676, .name = "92HD73E1X5", .patch = patch_stac92hd73xx }, + { .id = 0x111d7608, .name = "92HD71BXX", .patch = patch_stac92hd71bxx }, + { .id = 0x111d76b0, .name = "92HD71B8X", .patch = patch_stac92hd71bxx }, + { .id = 0x111d76b1, .name = "92HD71B8X", .patch = patch_stac92hd71bxx }, + { .id = 0x111d76b2, .name = "92HD71B7X", .patch = patch_stac92hd71bxx }, + { .id = 0x111d76b3, .name = "92HD71B7X", .patch = patch_stac92hd71bxx }, + { .id = 0x111d76b4, .name = "92HD71B6X", .patch = patch_stac92hd71bxx }, + { .id = 0x111d76b5, .name = "92HD71B6X", .patch = patch_stac92hd71bxx }, + { .id = 0x111d76b6, .name = "92HD71B5X", .patch = patch_stac92hd71bxx }, + { .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx }, {} /* terminator */ }; diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 4cdf3e6..4e5dd4c 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -27,11 +27,12 @@ /* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */ /* 2006-08-02 Lydia Wang Add support to VT1709 codec */ /* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */ +/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */ +/* 2007-09-17 Lydia Wang Add VT1708B codec support */ /* */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> @@ -51,14 +52,23 @@ #define VT1708_HP_NID 0x13 #define VT1708_DIGOUT_NID 0x14 #define VT1708_DIGIN_NID 0x16 +#define VT1708_DIGIN_PIN 0x26 #define VT1709_HP_DAC_NID 0x28 #define VT1709_DIGOUT_NID 0x13 #define VT1709_DIGIN_NID 0x17 +#define VT1709_DIGIN_PIN 0x25 + +#define VT1708B_HP_NID 0x25 +#define VT1708B_DIGOUT_NID 0x12 +#define VT1708B_DIGIN_NID 0x15 +#define VT1708B_DIGIN_PIN 0x21 #define IS_VT1708_VENDORID(x) ((x) >= 0x11061708 && (x) <= 0x1106170b) #define IS_VT1709_10CH_VENDORID(x) ((x) >= 0x1106e710 && (x) <= 0x1106e713) #define IS_VT1709_6CH_VENDORID(x) ((x) >= 0x1106e714 && (x) <= 0x1106e717) +#define IS_VT1708B_8CH_VENDORID(x) ((x) >= 0x1106e720 && (x) <= 0x1106e723) +#define IS_VT1708B_4CH_VENDORID(x) ((x) >= 0x1106e724 && (x) <= 0x1106e727) enum { @@ -131,6 +141,11 @@ static hda_nid_t vt1709_adc_nids[3] = { 0x14, 0x15, 0x16 }; +static hda_nid_t vt1708B_adc_nids[2] = { + /* ADC1-2 */ + 0x13, 0x14 +}; + /* add dynamic controls */ static int via_add_control(struct via_spec *spec, int type, const char *name, unsigned long val) @@ -268,9 +283,13 @@ static int via_mux_enum_put(struct snd_kcontrol *kcontrol, return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 0x18, &spec->cur_mux[adc_idx]); else if ((IS_VT1709_10CH_VENDORID(vendor_id) || - IS_VT1709_6CH_VENDORID(vendor_id)) && (adc_idx == 0) ) + IS_VT1709_6CH_VENDORID(vendor_id)) && adc_idx == 0) return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 0x19, &spec->cur_mux[adc_idx]); + else if ((IS_VT1708B_8CH_VENDORID(vendor_id) || + IS_VT1708B_4CH_VENDORID(vendor_id)) && adc_idx == 0) + return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, + 0x17, &spec->cur_mux[adc_idx]); else return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, spec->adc_nids[adc_idx], @@ -287,7 +306,6 @@ static struct snd_kcontrol_new vt1708_capture_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -309,15 +327,15 @@ static struct hda_verb vt1708_volume_init_verbs[] = { {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback * mixer widget */ /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* master */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* * Set up output mixers (0x19 - 0x1b) @@ -329,10 +347,9 @@ static struct hda_verb vt1708_volume_init_verbs[] = { /* Setup default input to PW4 */ {0x20, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* Set mic as default input of sw0 */ - {0x18, AC_VERB_SET_CONNECT_SEL, 0x2}, /* PW9 Output enable */ {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + { } }; static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, @@ -544,6 +561,33 @@ static int via_init(struct hda_codec *codec) { struct via_spec *spec = codec->spec; snd_hda_sequence_write(codec, spec->init_verbs); + /* Lydia Add for EAPD enable */ + if (!spec->dig_in_nid) { /* No Digital In connection */ + if (IS_VT1708_VENDORID(codec->vendor_id)) { + snd_hda_codec_write(codec, VT1708_DIGIN_PIN, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + PIN_OUT); + snd_hda_codec_write(codec, VT1708_DIGIN_PIN, 0, + AC_VERB_SET_EAPD_BTLENABLE, 0x02); + } else if (IS_VT1709_10CH_VENDORID(codec->vendor_id) || + IS_VT1709_6CH_VENDORID(codec->vendor_id)) { + snd_hda_codec_write(codec, VT1709_DIGIN_PIN, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + PIN_OUT); + snd_hda_codec_write(codec, VT1709_DIGIN_PIN, 0, + AC_VERB_SET_EAPD_BTLENABLE, 0x02); + } else if (IS_VT1708B_8CH_VENDORID(codec->vendor_id) || + IS_VT1708B_4CH_VENDORID(codec->vendor_id)) { + snd_hda_codec_write(codec, VT1708B_DIGIN_PIN, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + PIN_OUT); + snd_hda_codec_write(codec, VT1708B_DIGIN_PIN, 0, + AC_VERB_SET_EAPD_BTLENABLE, 0x02); + } + } else /* enable SPDIF-input pin */ + snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); + return 0; } @@ -623,58 +667,68 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, if (i == AUTO_SEQ_CENLFE) { /* Center/LFE */ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, - "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT)); + "Center Playback Volume", + HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, + HDA_OUTPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, + HDA_OUTPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, + HDA_OUTPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, + HDA_OUTPUT)); if (err < 0) return err; } else if (i == AUTO_SEQ_FRONT){ /* add control to mixer index 0 */ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_INPUT)); + HDA_COMPOSE_AMP_VAL(0x17, 3, 0, + HDA_INPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_INPUT)); + HDA_COMPOSE_AMP_VAL(0x17, 3, 0, + HDA_INPUT)); if (err < 0) return err; /* add control to PW3 */ sprintf(name, "%s Playback Volume", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(nid, 3, 0, + HDA_OUTPUT)); if (err < 0) return err; sprintf(name, "%s Playback Switch", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(nid, 3, 0, + HDA_OUTPUT)); if (err < 0) return err; } else { sprintf(name, "%s Playback Volume", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, + HDA_OUTPUT)); if (err < 0) return err; sprintf(name, "%s Playback Switch", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, + HDA_OUTPUT)); if (err < 0) return err; } @@ -875,7 +929,6 @@ static struct snd_kcontrol_new vt1709_capture_mixer[] = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, /* The multiple "Capture Source" controls confuse alsamixer * So call somewhat different.. - * FIXME: the controls appear in the "playback" view! */ /* .name = "Capture Source", */ .name = "Input Source", @@ -899,15 +952,15 @@ static struct hda_verb vt1709_10ch_volume_init_verbs[] = { {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback * mixer widget */ /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* unmute master */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* * Set up output selector (0x1a, 0x1b, 0x29) @@ -925,8 +978,6 @@ static struct hda_verb vt1709_10ch_volume_init_verbs[] = { /* Set input of PW4 as AOW4 */ {0x20, AC_VERB_SET_CONNECT_SEL, 0x1}, - /* Set mic as default input of sw0 */ - {0x19, AC_VERB_SET_CONNECT_SEL, 0x2}, /* PW9 Output enable */ {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, { } @@ -1073,68 +1124,80 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, /* Center/LFE */ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Center Playback Volume", - HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, + HDA_OUTPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "LFE Playback Volume", - HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, + HDA_OUTPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, "Center Playback Switch", - HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, + HDA_OUTPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, "LFE Playback Switch", - HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, + HDA_OUTPUT)); if (err < 0) return err; } else if (i == AUTO_SEQ_FRONT){ /* add control to mixer index 0 */ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, "Master Front Playback Volume", - HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT)); + HDA_COMPOSE_AMP_VAL(0x18, 3, 0, + HDA_INPUT)); if (err < 0) return err; err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, "Master Front Playback Switch", - HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT)); + HDA_COMPOSE_AMP_VAL(0x18, 3, 0, + HDA_INPUT)); if (err < 0) return err; /* add control to PW3 */ sprintf(name, "%s Playback Volume", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(nid, 3, 0, + HDA_OUTPUT)); if (err < 0) return err; sprintf(name, "%s Playback Switch", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(nid, 3, 0, + HDA_OUTPUT)); if (err < 0) return err; } else if (i == AUTO_SEQ_SURROUND) { sprintf(name, "%s Playback Volume", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(0x29, 3, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(0x29, 3, 0, + HDA_OUTPUT)); if (err < 0) return err; sprintf(name, "%s Playback Switch", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(0x29, 3, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(0x29, 3, 0, + HDA_OUTPUT)); if (err < 0) return err; } else if (i == AUTO_SEQ_SIDE) { sprintf(name, "%s Playback Volume", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, - HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, + HDA_OUTPUT)); if (err < 0) return err; sprintf(name, "%s Playback Switch", chname[i]); err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, - HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT)); + HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, + HDA_OUTPUT)); if (err < 0) return err; } @@ -1351,8 +1414,6 @@ static struct hda_verb vt1709_6ch_volume_init_verbs[] = { /* Set input of PW4 as MW0 */ {0x20, AC_VERB_SET_CONNECT_SEL, 0}, - /* Set mic as default input of sw0 */ - {0x19, AC_VERB_SET_CONNECT_SEL, 0x2}, /* PW9 Output enable */ {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, { } @@ -1403,6 +1464,494 @@ static int patch_vt1709_6ch(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_POWER_SAVE spec->loopback.amplist = vt1709_loopbacks; #endif + return 0; +} + +/* capture mixer elements */ +static struct snd_kcontrol_new vt1708B_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 1, + .info = via_mux_enum_info, + .get = via_mux_enum_get, + .put = via_mux_enum_put, + }, + { } /* end */ +}; +/* + * generic initialization of ADC, input mixers and output mixers + */ +static struct hda_verb vt1708B_8ch_volume_init_verbs[] = { + /* + * Unmute ADC0-1 and set the default input to mic-in + */ + {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + + /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + * mixer widget + */ + /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + + /* + * Set up output mixers + */ + /* set vol=0 to output mixers */ + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + + /* Setup default input to PW4 */ + {0x1d, AC_VERB_SET_CONNECT_SEL, 0x1}, + /* PW9 Output enable */ + {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + /* PW10 Input enable */ + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + { } +}; + +static struct hda_verb vt1708B_4ch_volume_init_verbs[] = { + /* + * Unmute ADC0-1 and set the default input to mic-in + */ + {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + + /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + * mixer widget + */ + /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + + /* + * Set up output mixers + */ + /* set vol=0 to output mixers */ + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + + /* Setup default input of PW4 to MW0 */ + {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, + /* PW9 Output enable */ + {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + /* PW10 Input enable */ + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + { } +}; + +static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 8, + .nid = 0x10, /* NID to query formats and rates */ + .ops = { + .open = via_playback_pcm_open, + .prepare = via_playback_pcm_prepare, + .cleanup = via_playback_pcm_cleanup + }, +}; + +static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 4, + .nid = 0x10, /* NID to query formats and rates */ + .ops = { + .open = via_playback_pcm_open, + .prepare = via_playback_pcm_prepare, + .cleanup = via_playback_pcm_cleanup + }, +}; + +static struct hda_pcm_stream vt1708B_pcm_analog_capture = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0x13, /* NID to query formats and rates */ + .ops = { + .prepare = via_capture_pcm_prepare, + .cleanup = via_capture_pcm_cleanup + }, +}; + +static struct hda_pcm_stream vt1708B_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in via_build_pcms */ + .ops = { + .open = via_dig_playback_pcm_open, + .close = via_dig_playback_pcm_close, + .prepare = via_dig_playback_pcm_prepare + }, +}; + +static struct hda_pcm_stream vt1708B_pcm_digital_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, +}; + +/* fill in the dac_nids table from the parsed pin configuration */ +static int vt1708B_auto_fill_dac_nids(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + int i; + hda_nid_t nid; + + spec->multiout.num_dacs = cfg->line_outs; + + spec->multiout.dac_nids = spec->private_dac_nids; + + for (i = 0; i < 4; i++) { + nid = cfg->line_out_pins[i]; + if (nid) { + /* config dac list */ + switch (i) { + case AUTO_SEQ_FRONT: + spec->multiout.dac_nids[i] = 0x10; + break; + case AUTO_SEQ_CENLFE: + spec->multiout.dac_nids[i] = 0x24; + break; + case AUTO_SEQ_SURROUND: + spec->multiout.dac_nids[i] = 0x25; + break; + case AUTO_SEQ_SIDE: + spec->multiout.dac_nids[i] = 0x11; + break; + } + } + } + + return 0; +} + +/* add playback controls from the parsed DAC table */ +static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + char name[32]; + static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; + hda_nid_t nid_vols[] = {0x16, 0x27, 0x26, 0x18}; + hda_nid_t nid, nid_vol = 0; + int i, err; + + for (i = 0; i <= AUTO_SEQ_SIDE; i++) { + nid = cfg->line_out_pins[i]; + + if (!nid) + continue; + + nid_vol = nid_vols[i]; + + if (i == AUTO_SEQ_CENLFE) { + /* Center/LFE */ + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Center Playback Volume", + HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "LFE Playback Volume", + HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, + "Center Playback Switch", + HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, + "LFE Playback Switch", + HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } else if (i == AUTO_SEQ_FRONT) { + /* add control to mixer index 0 */ + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Master Front Playback Volume", + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, + HDA_INPUT)); + if (err < 0) + return err; + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, + "Master Front Playback Switch", + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, + HDA_INPUT)); + if (err < 0) + return err; + + /* add control to PW3 */ + sprintf(name, "%s Playback Volume", chname[i]); + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + sprintf(name, "%s Playback Switch", chname[i]); + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } else { + sprintf(name, "%s Playback Volume", chname[i]); + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + sprintf(name, "%s Playback Switch", chname[i]); + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, + HDA_OUTPUT)); + if (err < 0) + return err; + } + } + + return 0; +} + +static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) +{ + int err; + + if (!pin) + return 0; + + spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */ + + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, + "Headphone Playback Volume", + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, + "Headphone Playback Switch", + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + + return 0; +} + +/* create playback/capture controls for input pins */ +static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec, + const struct auto_pin_cfg *cfg) +{ + static char *labels[] = { + "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL + }; + struct hda_input_mux *imux = &spec->private_imux; + int i, err, idx = 0; + + /* for internal loopback recording select */ + imux->items[imux->num_items].label = "Stereo Mixer"; + imux->items[imux->num_items].index = idx; + imux->num_items++; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + if (!cfg->input_pins[i]) + continue; + + switch (cfg->input_pins[i]) { + case 0x1a: /* Mic */ + idx = 2; + break; + + case 0x1b: /* Line In */ + idx = 3; + break; + + case 0x1e: /* Front Mic */ + idx = 4; + break; + + case 0x1f: /* CD */ + idx = 1; + break; + } + err = via_new_analog_input(spec, cfg->input_pins[i], labels[i], + idx, 0x16); + if (err < 0) + return err; + imux->items[imux->num_items].label = labels[i]; + imux->items[imux->num_items].index = idx; + imux->num_items++; + } + return 0; +} + +static int vt1708B_parse_auto_config(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int err; + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); + if (err < 0) + return err; + err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg); + if (err < 0) + return err; + if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) + return 0; /* can't find valid BIOS pin config */ + + err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); + if (err < 0) + return err; + err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + + if (spec->autocfg.dig_out_pin) + spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID; + if (spec->autocfg.dig_in_pin) + spec->dig_in_nid = VT1708B_DIGIN_NID; + + if (spec->kctl_alloc) + spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + + spec->input_mux = &spec->private_imux; + + return 1; +} + +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list vt1708B_loopbacks[] = { + { 0x16, HDA_INPUT, 1 }, + { 0x16, HDA_INPUT, 2 }, + { 0x16, HDA_INPUT, 3 }, + { 0x16, HDA_INPUT, 4 }, + { } /* end */ +}; +#endif + +static int patch_vt1708B_8ch(struct hda_codec *codec) +{ + struct via_spec *spec; + int err; + + /* create a codec specific record */ + spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + /* automatic parse from the BIOS config */ + err = vt1708B_parse_auto_config(codec); + if (err < 0) { + via_free(codec); + return err; + } else if (!err) { + printk(KERN_INFO "hda_codec: Cannot set up configuration " + "from BIOS. Using genenic mode...\n"); + } + + spec->init_verbs = vt1708B_8ch_volume_init_verbs; + + spec->stream_name_analog = "VT1708B Analog"; + spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback; + spec->stream_analog_capture = &vt1708B_pcm_analog_capture; + + spec->stream_name_digital = "VT1708B Digital"; + spec->stream_digital_playback = &vt1708B_pcm_digital_playback; + spec->stream_digital_capture = &vt1708B_pcm_digital_capture; + + if (!spec->adc_nids && spec->input_mux) { + spec->adc_nids = vt1708B_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); + spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; + spec->num_mixers++; + } + + codec->patch_ops = via_patch_ops; + + codec->patch_ops.init = via_auto_init; +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = vt1708B_loopbacks; +#endif + + return 0; +} + +static int patch_vt1708B_4ch(struct hda_codec *codec) +{ + struct via_spec *spec; + int err; + + /* create a codec specific record */ + spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + /* automatic parse from the BIOS config */ + err = vt1708B_parse_auto_config(codec); + if (err < 0) { + via_free(codec); + return err; + } else if (!err) { + printk(KERN_INFO "hda_codec: Cannot set up configuration " + "from BIOS. Using genenic mode...\n"); + } + + spec->init_verbs = vt1708B_4ch_volume_init_verbs; + + spec->stream_name_analog = "VT1708B Analog"; + spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback; + spec->stream_analog_capture = &vt1708B_pcm_analog_capture; + + spec->stream_name_digital = "VT1708B Digital"; + spec->stream_digital_playback = &vt1708B_pcm_digital_playback; + spec->stream_digital_capture = &vt1708B_pcm_digital_capture; + + if (!spec->adc_nids && spec->input_mux) { + spec->adc_nids = vt1708B_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); + spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; + spec->num_mixers++; + } + + codec->patch_ops = via_patch_ops; + + codec->patch_ops.init = via_auto_init; +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = vt1708B_loopbacks; +#endif return 0; } @@ -1415,13 +1964,37 @@ struct hda_codec_preset snd_hda_preset_via[] = { { .id = 0x11061709, .name = "VIA VT1708", .patch = patch_vt1708}, { .id = 0x1106170A, .name = "VIA VT1708", .patch = patch_vt1708}, { .id = 0x1106170B, .name = "VIA VT1708", .patch = patch_vt1708}, - { .id = 0x1106E710, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch}, - { .id = 0x1106E711, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch}, - { .id = 0x1106E712, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch}, - { .id = 0x1106E713, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch}, - { .id = 0x1106E714, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch}, - { .id = 0x1106E715, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch}, - { .id = 0x1106E716, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch}, - { .id = 0x1106E717, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch}, + { .id = 0x1106E710, .name = "VIA VT1709 10-Ch", + .patch = patch_vt1709_10ch}, + { .id = 0x1106E711, .name = "VIA VT1709 10-Ch", + .patch = patch_vt1709_10ch}, + { .id = 0x1106E712, .name = "VIA VT1709 10-Ch", + .patch = patch_vt1709_10ch}, + { .id = 0x1106E713, .name = "VIA VT1709 10-Ch", + .patch = patch_vt1709_10ch}, + { .id = 0x1106E714, .name = "VIA VT1709 6-Ch", + .patch = patch_vt1709_6ch}, + { .id = 0x1106E715, .name = "VIA VT1709 6-Ch", + .patch = patch_vt1709_6ch}, + { .id = 0x1106E716, .name = "VIA VT1709 6-Ch", + .patch = patch_vt1709_6ch}, + { .id = 0x1106E717, .name = "VIA VT1709 6-Ch", + .patch = patch_vt1709_6ch}, + { .id = 0x1106E720, .name = "VIA VT1708B 8-Ch", + .patch = patch_vt1708B_8ch}, + { .id = 0x1106E721, .name = "VIA VT1708B 8-Ch", + .patch = patch_vt1708B_8ch}, + { .id = 0x1106E722, .name = "VIA VT1708B 8-Ch", + .patch = patch_vt1708B_8ch}, + { .id = 0x1106E723, .name = "VIA VT1708B 8-Ch", + .patch = patch_vt1708B_8ch}, + { .id = 0x1106E724, .name = "VIA VT1708B 4-Ch", + .patch = patch_vt1708B_4ch}, + { .id = 0x1106E725, .name = "VIA VT1708B 4-Ch", + .patch = patch_vt1708B_4ch}, + { .id = 0x1106E726, .name = "VIA VT1708B 4-Ch", + .patch = patch_vt1708B_4ch}, + { .id = 0x1106E727, .name = "VIA VT1708B 4-Ch", + .patch = patch_vt1708B_4ch}, {} /* terminator */ }; diff --git a/sound/pci/hda/vmaster.c b/sound/pci/hda/vmaster.c new file mode 100644 index 0000000..2da49d2 --- /dev/null +++ b/sound/pci/hda/vmaster.c @@ -0,0 +1,364 @@ +/* + * Virtual master and slave controls + * + * Copyright (c) 2008 by Takashi Iwai <tiwai@suse.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, version 2. + * + */ + +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/control.h> + +/* + * a subset of information returned via ctl info callback + */ +struct link_ctl_info { + int type; /* value type */ + int count; /* item count */ + int min_val, max_val; /* min, max values */ +}; + +/* + * link master - this contains a list of slave controls that are + * identical types, i.e. info returns the same value type and value + * ranges, but may have different number of counts. + * + * The master control is so far only mono volume/switch for simplicity. + * The same value will be applied to all slaves. + */ +struct link_master { + struct list_head slaves; + struct link_ctl_info info; + int val; /* the master value */ +}; + +/* + * link slave - this contains a slave control element + * + * It fakes the control callbacsk with additional attenuation by the + * master control. A slave may have either one or two channels. + */ + +struct link_slave { + struct list_head list; + struct link_master *master; + struct link_ctl_info info; + int vals[2]; /* current values */ + struct snd_kcontrol slave; /* the copy of original control entry */ +}; + +/* get the slave ctl info and save the initial values */ +static int slave_init(struct link_slave *slave) +{ + struct snd_ctl_elem_info *uinfo; + struct snd_ctl_elem_value *uctl; + int err, ch; + + if (slave->info.count) + return 0; /* already initialized */ + + uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL); + if (!uinfo) + return -ENOMEM; + uinfo->id = slave->slave.id; + err = slave->slave.info(&slave->slave, uinfo); + if (err < 0) { + kfree(uinfo); + return err; + } + slave->info.type = uinfo->type; + slave->info.count = uinfo->count; + if (slave->info.count > 2 || + (slave->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER && + slave->info.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN)) { + snd_printk(KERN_ERR "invalid slave element\n"); + kfree(uinfo); + return -EINVAL; + } + slave->info.min_val = uinfo->value.integer.min; + slave->info.max_val = uinfo->value.integer.max; + kfree(uinfo); + + uctl = kmalloc(sizeof(*uctl), GFP_KERNEL); + if (!uctl) + return -ENOMEM; + uctl->id = slave->slave.id; + err = slave->slave.get(&slave->slave, uctl); + for (ch = 0; ch < slave->info.count; ch++) + slave->vals[ch] = uctl->value.integer.value[ch]; + kfree(uctl); + return 0; +} + +/* initialize master volume */ +static int master_init(struct link_master *master) +{ + struct link_slave *slave; + + if (master->info.count) + return 0; /* already initialized */ + + list_for_each_entry(slave, &master->slaves, list) { + int err = slave_init(slave); + if (err < 0) + return err; + master->info = slave->info; + master->info.count = 1; /* always mono */ + /* set full volume as default (= no attenuation) */ + master->val = master->info.max_val; + return 0; + } + return -ENOENT; +} + +static int slave_get_val(struct link_slave *slave, + struct snd_ctl_elem_value *ucontrol) +{ + int err, ch; + + err = slave_init(slave); + if (err < 0) + return err; + for (ch = 0; ch < slave->info.count; ch++) + ucontrol->value.integer.value[ch] = slave->vals[ch]; + return 0; +} + +static int slave_put_val(struct link_slave *slave, + struct snd_ctl_elem_value *ucontrol) +{ + int err, ch, vol; + + err = master_init(slave->master); + if (err < 0) + return err; + + switch (slave->info.type) { + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: + for (ch = 0; ch < slave->info.count; ch++) + ucontrol->value.integer.value[ch] &= + !!slave->master->val; + break; + case SNDRV_CTL_ELEM_TYPE_INTEGER: + for (ch = 0; ch < slave->info.count; ch++) { + /* max master volume is supposed to be 0 dB */ + vol = ucontrol->value.integer.value[ch]; + vol += slave->master->val - slave->master->info.max_val; + if (vol < slave->info.min_val) + vol = slave->info.min_val; + else if (vol > slave->info.max_val) + vol = slave->info.max_val; + ucontrol->value.integer.value[ch] = vol; + } + break; + } + return slave->slave.put(&slave->slave, ucontrol); +} + +/* + * ctl callbacks for slaves + */ +static int slave_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct link_slave *slave = snd_kcontrol_chip(kcontrol); + return slave->slave.info(&slave->slave, uinfo); +} + +static int slave_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct link_slave *slave = snd_kcontrol_chip(kcontrol); + return slave_get_val(slave, ucontrol); +} + +static int slave_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct link_slave *slave = snd_kcontrol_chip(kcontrol); + int err, ch, changed = 0; + + err = slave_init(slave); + if (err < 0) + return err; + for (ch = 0; ch < slave->info.count; ch++) { + if (slave->vals[ch] != ucontrol->value.integer.value[ch]) { + changed = 1; + slave->vals[ch] = ucontrol->value.integer.value[ch]; + } + } + if (!changed) + return 0; + return slave_put_val(slave, ucontrol); +} + +static int slave_tlv_cmd(struct snd_kcontrol *kcontrol, + int op_flag, unsigned int size, + unsigned int __user *tlv) +{ + struct link_slave *slave = snd_kcontrol_chip(kcontrol); + /* FIXME: this assumes that the max volume is 0 dB */ + return slave->slave.tlv.c(&slave->slave, op_flag, size, tlv); +} + +static void slave_free(struct snd_kcontrol *kcontrol) +{ + struct link_slave *slave = snd_kcontrol_chip(kcontrol); + if (slave->slave.private_free) + slave->slave.private_free(&slave->slave); + if (slave->master) + list_del(&slave->list); + kfree(slave); +} + +/* + * Add a slave control to the group with the given master control + * + * All slaves must be the same type (returning the same information + * via info callback). The fucntion doesn't check it, so it's your + * responsibility. + * + * Also, some additional limitations: + * - at most two channels + * - logarithmic volume control (dB level), no linear volume + * - master can only attenuate the volume, no gain + */ +int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave) +{ + struct link_master *master_link = snd_kcontrol_chip(master); + struct link_slave *srec; + + srec = kzalloc(sizeof(*srec) + + slave->count * sizeof(*slave->vd), GFP_KERNEL); + if (!srec) + return -ENOMEM; + srec->slave = *slave; + memcpy(srec->slave.vd, slave->vd, slave->count * sizeof(*slave->vd)); + srec->master = master_link; + + /* override callbacks */ + slave->info = slave_info; + slave->get = slave_get; + slave->put = slave_put; + if (slave->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) + slave->tlv.c = slave_tlv_cmd; + slave->private_data = srec; + slave->private_free = slave_free; + + list_add_tail(&srec->list, &master_link->slaves); + return 0; +} + +/* + * ctl callbacks for master controls + */ +static int master_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct link_master *master = snd_kcontrol_chip(kcontrol); + int ret; + + ret = master_init(master); + if (ret < 0) + return ret; + uinfo->type = master->info.type; + uinfo->count = master->info.count; + uinfo->value.integer.min = master->info.min_val; + uinfo->value.integer.max = master->info.max_val; + return 0; +} + +static int master_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct link_master *master = snd_kcontrol_chip(kcontrol); + int err = master_init(master); + if (err < 0) + return err; + ucontrol->value.integer.value[0] = master->val; + return 0; +} + +static int master_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct link_master *master = snd_kcontrol_chip(kcontrol); + struct link_slave *slave; + struct snd_ctl_elem_value *uval; + int err, old_val; + + err = master_init(master); + if (err < 0) + return err; + old_val = master->val; + if (ucontrol->value.integer.value[0] == old_val) + return 0; + + uval = kmalloc(sizeof(*uval), GFP_KERNEL); + if (!uval) + return -ENOMEM; + list_for_each_entry(slave, &master->slaves, list) { + master->val = old_val; + uval->id = slave->slave.id; + slave_get_val(slave, uval); + master->val = ucontrol->value.integer.value[0]; + slave_put_val(slave, uval); + } + kfree(uval); + return 1; +} + +static void master_free(struct snd_kcontrol *kcontrol) +{ + struct link_master *master = snd_kcontrol_chip(kcontrol); + struct link_slave *slave; + + list_for_each_entry(slave, &master->slaves, list) + slave->master = NULL; + kfree(master); +} + + +/* + * Create a virtual master control with the given name + */ +struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, + const unsigned int *tlv) +{ + struct link_master *master; + struct snd_kcontrol *kctl; + struct snd_kcontrol_new knew; + + memset(&knew, 0, sizeof(knew)); + knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + knew.name = name; + knew.info = master_info; + + master = kzalloc(sizeof(*master), GFP_KERNEL); + if (!master) + return NULL; + INIT_LIST_HEAD(&master->slaves); + + kctl = snd_ctl_new1(&knew, master); + if (!kctl) { + kfree(master); + return NULL; + } + /* override some callbacks */ + kctl->info = master_info; + kctl->get = master_get; + kctl->put = master_put; + kctl->private_free = master_free; + + /* additional (constant) TLV read */ + if (tlv) { + /* FIXME: this assumes that the max volume is 0 dB */ + kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; + kctl->tlv.p = tlv; + } + return kctl; +} diff --git a/sound/pci/ice1712/Makefile b/sound/pci/ice1712/Makefile index 65ce66a..f99fe08 100644 --- a/sound/pci/ice1712/Makefile +++ b/sound/pci/ice1712/Makefile @@ -5,7 +5,7 @@ snd-ice17xx-ak4xxx-objs := ak4xxx.o snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.o -snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o juli.o phase.o wtm.o +snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o # Toplevel Module Dependency obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o diff --git a/sound/pci/ice1712/ak4xxx.c b/sound/pci/ice1712/ak4xxx.c index a1aba0d..dab31b2 100644 --- a/sound/pci/ice1712/ak4xxx.c +++ b/sound/pci/ice1712/ak4xxx.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/sound/pci/ice1712/amp.c b/sound/pci/ice1712/amp.c index 6e13d75..3756430 100644 --- a/sound/pci/ice1712/amp.c +++ b/sound/pci/ice1712/amp.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c index ec0699c..868ae29 100644 --- a/sound/pci/ice1712/aureon.c +++ b/sound/pci/ice1712/aureon.c @@ -47,7 +47,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -62,6 +61,15 @@ #include "aureon.h" #include <sound/tlv.h> +/* AC97 register cache for Aureon */ +struct aureon_spec { + unsigned short stac9744[64]; + unsigned int cs8415_mux; + unsigned short master[2]; + unsigned short vol[8]; + unsigned char pca9554_out; +}; + /* WM8770 registers */ #define WM_DAC_ATTEN 0x00 /* DAC1-8 analog attenuation */ #define WM_DAC_MASTER_ATTEN 0x08 /* DAC master analog attenuation */ @@ -205,7 +213,8 @@ static int aureon_universe_inmux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - ucontrol->value.integer.value[0] = ice->spec.aureon.pca9554_out; + struct aureon_spec *spec = ice->spec; + ucontrol->value.enumerated.item[0] = spec->pca9554_out; return 0; } @@ -213,16 +222,18 @@ static int aureon_universe_inmux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct aureon_spec *spec = ice->spec; unsigned char oval, nval; int change; + nval = ucontrol->value.enumerated.item[0]; + if (nval >= 3) + return -EINVAL; snd_ice1712_save_gpio_status(ice); - - oval = ice->spec.aureon.pca9554_out; - nval = ucontrol->value.integer.value[0]; + oval = spec->pca9554_out; if ((change = (oval != nval))) { aureon_pca9554_write(ice, PCA9554_OUT, nval); - ice->spec.aureon.pca9554_out = nval; + spec->pca9554_out = nval; } snd_ice1712_restore_gpio_status(ice); @@ -233,6 +244,7 @@ static int aureon_universe_inmux_put(struct snd_kcontrol *kcontrol, static void aureon_ac97_write(struct snd_ice1712 *ice, unsigned short reg, unsigned short val) { + struct aureon_spec *spec = ice->spec; unsigned int tmp; /* Send address to XILINX chip */ @@ -280,12 +292,13 @@ static void aureon_ac97_write(struct snd_ice1712 *ice, unsigned short reg, udelay(10); /* Store the data in out private buffer */ - ice->spec.aureon.stac9744[(reg & 0x7F) >> 1] = val; + spec->stac9744[(reg & 0x7F) >> 1] = val; } static unsigned short aureon_ac97_read(struct snd_ice1712 *ice, unsigned short reg) { - return ice->spec.aureon.stac9744[(reg & 0x7F) >> 1]; + struct aureon_spec *spec = ice->spec; + return spec->stac9744[(reg & 0x7F) >> 1]; } /* @@ -293,6 +306,7 @@ static unsigned short aureon_ac97_read(struct snd_ice1712 *ice, unsigned short r */ static int aureon_ac97_init (struct snd_ice1712 *ice) { + struct aureon_spec *spec = ice->spec; int i; static const unsigned short ac97_defaults[] = { 0x00, 0x9640, @@ -330,9 +344,9 @@ static int aureon_ac97_init (struct snd_ice1712 *ice) snd_ice1712_gpio_write(ice, tmp); udelay(3); - memset(&ice->spec.aureon.stac9744, 0, sizeof(ice->spec.aureon.stac9744)); + memset(&spec->stac9744, 0, sizeof(spec->stac9744)); for (i=0; ac97_defaults[i] != (unsigned short)-1; i+=2) - ice->spec.aureon.stac9744[(ac97_defaults[i]) >> 1] = ac97_defaults[i+1]; + spec->stac9744[(ac97_defaults[i]) >> 1] = ac97_defaults[i+1]; aureon_ac97_write(ice, AC97_MASTER, 0x0000); // Unmute AC'97 master volume permanently - muting is done by WM8770 @@ -744,27 +758,33 @@ static int wm_master_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem static int wm_master_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct aureon_spec *spec = ice->spec; int i; for (i=0; i<2; i++) - ucontrol->value.integer.value[i] = ice->spec.aureon.master[i] & ~WM_VOL_MUTE; + ucontrol->value.integer.value[i] = + spec->master[i] & ~WM_VOL_MUTE; return 0; } static int wm_master_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct aureon_spec *spec = ice->spec; int ch, change = 0; snd_ice1712_save_gpio_status(ice); for (ch = 0; ch < 2; ch++) { - if (ucontrol->value.integer.value[ch] != ice->spec.aureon.master[ch]) { + unsigned int vol = ucontrol->value.integer.value[ch]; + if (vol > WM_VOL_MAX) + continue; + vol |= spec->master[ch] & WM_VOL_MUTE; + if (vol != spec->master[ch]) { int dac; - ice->spec.aureon.master[ch] &= WM_VOL_MUTE; - ice->spec.aureon.master[ch] |= ucontrol->value.integer.value[ch]; + spec->master[ch] = vol; for (dac = 0; dac < ice->num_total_dacs; dac += 2) wm_set_vol(ice, WM_DAC_ATTEN + dac + ch, - ice->spec.aureon.vol[dac + ch], - ice->spec.aureon.master[ch]); + spec->vol[dac + ch], + spec->master[ch]); change = 1; } } @@ -788,18 +808,21 @@ static int wm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info * static int wm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct aureon_spec *spec = ice->spec; int i, ofs, voices; voices = kcontrol->private_value >> 8; ofs = kcontrol->private_value & 0xff; for (i = 0; i < voices; i++) - ucontrol->value.integer.value[i] = ice->spec.aureon.vol[ofs+i] & ~WM_VOL_MUTE; + ucontrol->value.integer.value[i] = + spec->vol[ofs+i] & ~WM_VOL_MUTE; return 0; } static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct aureon_spec *spec = ice->spec; int i, idx, ofs, voices; int change = 0; @@ -807,12 +830,15 @@ static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value * ofs = kcontrol->private_value & 0xff; snd_ice1712_save_gpio_status(ice); for (i = 0; i < voices; i++) { - idx = WM_DAC_ATTEN + ofs + i; - if (ucontrol->value.integer.value[i] != ice->spec.aureon.vol[ofs+i]) { - ice->spec.aureon.vol[ofs+i] &= WM_VOL_MUTE; - ice->spec.aureon.vol[ofs+i] |= ucontrol->value.integer.value[i]; - wm_set_vol(ice, idx, ice->spec.aureon.vol[ofs+i], - ice->spec.aureon.master[i]); + unsigned int vol = ucontrol->value.integer.value[i]; + if (vol > 0x7f) + continue; + vol |= spec->vol[ofs+i]; + if (vol != spec->vol[ofs+i]) { + spec->vol[ofs+i] = vol; + idx = WM_DAC_ATTEN + ofs + i; + wm_set_vol(ice, idx, spec->vol[ofs + i], + spec->master[i]); change = 1; } } @@ -834,19 +860,22 @@ static int wm_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info static int wm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct aureon_spec *spec = ice->spec; int voices, ofs, i; voices = kcontrol->private_value >> 8; ofs = kcontrol->private_value & 0xFF; for (i = 0; i < voices; i++) - ucontrol->value.integer.value[i] = (ice->spec.aureon.vol[ofs+i] & WM_VOL_MUTE) ? 0 : 1; + ucontrol->value.integer.value[i] = + (spec->vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1; return 0; } static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct aureon_spec *spec = ice->spec; int change = 0, voices, ofs, i; voices = kcontrol->private_value >> 8; @@ -854,13 +883,13 @@ static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value snd_ice1712_save_gpio_status(ice); for (i = 0; i < voices; i++) { - int val = (ice->spec.aureon.vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1; + int val = (spec->vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1; if (ucontrol->value.integer.value[i] != val) { - ice->spec.aureon.vol[ofs + i] &= ~WM_VOL_MUTE; - ice->spec.aureon.vol[ofs + i] |= + spec->vol[ofs + i] &= ~WM_VOL_MUTE; + spec->vol[ofs + i] |= ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE; - wm_set_vol(ice, ofs + i, ice->spec.aureon.vol[ofs + i], - ice->spec.aureon.master[i]); + wm_set_vol(ice, ofs + i, spec->vol[ofs + i], + spec->master[i]); change = 1; } } @@ -877,29 +906,33 @@ static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value static int wm_master_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct aureon_spec *spec = ice->spec; - ucontrol->value.integer.value[0] = (ice->spec.aureon.master[0] & WM_VOL_MUTE) ? 0 : 1; - ucontrol->value.integer.value[1] = (ice->spec.aureon.master[1] & WM_VOL_MUTE) ? 0 : 1; + ucontrol->value.integer.value[0] = + (spec->master[0] & WM_VOL_MUTE) ? 0 : 1; + ucontrol->value.integer.value[1] = + (spec->master[1] & WM_VOL_MUTE) ? 0 : 1; return 0; } static int wm_master_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct aureon_spec *spec = ice->spec; int change = 0, i; snd_ice1712_save_gpio_status(ice); for (i = 0; i < 2; i++) { - int val = (ice->spec.aureon.master[i] & WM_VOL_MUTE) ? 0 : 1; + int val = (spec->master[i] & WM_VOL_MUTE) ? 0 : 1; if (ucontrol->value.integer.value[i] != val) { int dac; - ice->spec.aureon.master[i] &= ~WM_VOL_MUTE; - ice->spec.aureon.master[i] |= + spec->master[i] &= ~WM_VOL_MUTE; + spec->master[i] |= ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE; for (dac = 0; dac < ice->num_total_dacs; dac += 2) wm_set_vol(ice, WM_DAC_ATTEN + dac + i, - ice->spec.aureon.vol[dac + i], - ice->spec.aureon.master[i]); + spec->vol[dac + i], + spec->master[i]); change = 1; } } @@ -940,8 +973,10 @@ static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val unsigned short ovol, nvol; int change = 0; - snd_ice1712_save_gpio_status(ice); nvol = ucontrol->value.integer.value[0]; + if (nvol > PCM_RES) + return -EINVAL; + snd_ice1712_save_gpio_status(ice); nvol = (nvol ? (nvol + PCM_MIN) : 0) & 0xff; ovol = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff; if (ovol != nvol) { @@ -1031,7 +1066,7 @@ static int wm_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val snd_ice1712_save_gpio_status(ice); for (i = 0; i < 2; i++) { idx = WM_ADC_GAIN + i; - nvol = ucontrol->value.integer.value[i]; + nvol = ucontrol->value.integer.value[i] & 0x1f; ovol = wm_get(ice, idx); if ((ovol & 0x1f) != nvol) { wm_put(ice, idx, nvol | (ovol & ~0x1f)); @@ -1143,10 +1178,11 @@ static int aureon_cs8415_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_ static int aureon_cs8415_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct aureon_spec *spec = ice->spec; //snd_ice1712_save_gpio_status(ice); //val = aureon_cs8415_get(ice, CS8415_CTRL2); - ucontrol->value.enumerated.item[0] = ice->spec.aureon.cs8415_mux; + ucontrol->value.enumerated.item[0] = spec->cs8415_mux; //snd_ice1712_restore_gpio_status(ice); return 0; } @@ -1154,6 +1190,7 @@ static int aureon_cs8415_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e static int aureon_cs8415_mux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct aureon_spec *spec = ice->spec; unsigned short oval, nval; int change; @@ -1165,7 +1202,7 @@ static int aureon_cs8415_mux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e if (change) aureon_cs8415_put(ice, CS8415_CTRL2, nval); snd_ice1712_restore_gpio_status(ice); - ice->spec.aureon.cs8415_mux = ucontrol->value.enumerated.item[0]; + spec->cs8415_mux = ucontrol->value.enumerated.item[0]; return change; } @@ -2001,10 +2038,16 @@ static int __devinit aureon_init(struct snd_ice1712 *ice) 0x0605, /* slave, 24bit, MSB on second OSCLK, SDOUT for right channel when OLRCK is high */ (unsigned short)-1 }; + struct aureon_spec *spec; unsigned int tmp; const unsigned short *p; int err, i; + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + ice->spec = spec; + if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON51_SKY) { ice->num_total_dacs = 6; ice->num_total_adcs = 2; @@ -2055,7 +2098,7 @@ static int __devinit aureon_init(struct snd_ice1712 *ice) ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71XT) { for (p = cs_inits; *p != (unsigned short)-1; p++) aureon_spi_write(ice, AUREON_CS8415_CS, *p | 0x200000, 24); - ice->spec.aureon.cs8415_mux = 1; + spec->cs8415_mux = 1; aureon_set_headphone_amp(ice, 1); } @@ -2066,11 +2109,11 @@ static int __devinit aureon_init(struct snd_ice1712 *ice) aureon_pca9554_write(ice, PCA9554_DIR, 0x00); aureon_pca9554_write(ice, PCA9554_OUT, 0x00); /* internal AUX */ - ice->spec.aureon.master[0] = WM_VOL_MUTE; - ice->spec.aureon.master[1] = WM_VOL_MUTE; + spec->master[0] = WM_VOL_MUTE; + spec->master[1] = WM_VOL_MUTE; for (i = 0; i < ice->num_total_dacs; i++) { - ice->spec.aureon.vol[i] = WM_VOL_MUTE; - wm_set_vol(ice, i, ice->spec.aureon.vol[i], ice->spec.aureon.master[i % 2]); + spec->vol[i] = WM_VOL_MUTE; + wm_set_vol(ice, i, spec->vol[i], spec->master[i % 2]); } return 0; diff --git a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c index 371f784..efd180b 100644 --- a/sound/pci/ice1712/delta.c +++ b/sound/pci/ice1712/delta.c @@ -22,7 +22,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -405,7 +404,7 @@ static int snd_ice1712_delta1010lt_wordclock_status_get(struct snd_kcontrol *kco if (snd_i2c_sendbytes(ice->cs8427, ®, 1) != 1) snd_printk(KERN_ERR "unable to send register 0x%x byte to CS8427\n", reg); snd_i2c_readbytes(ice->cs8427, ®, 1); - ucontrol->value.integer.value[0] = (reg ? 1 : 0); + ucontrol->value.integer.value[0] = (reg & CS8427_UNLOCK) ? 1 : 0; return 0; } diff --git a/sound/pci/ice1712/ews.c b/sound/pci/ice1712/ews.c index 75e4e5e..064760d 100644 --- a/sound/pci/ice1712/ews.c +++ b/sound/pci/ice1712/ews.c @@ -22,7 +22,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -45,6 +44,11 @@ enum { }; +/* additional i2c devices for EWS boards */ +struct ews_spec { + struct snd_i2c_device *i2cdevs[3]; +}; + /* * access via i2c mode (for EWX 24/96, EWS 88MT&D) */ @@ -142,15 +146,17 @@ static struct snd_i2c_bit_ops snd_ice1712_ewx_cs8427_bit_ops = { /* AK4524 chip select; address 0x48 bit 0-3 */ static int snd_ice1712_ews88mt_chip_select(struct snd_ice1712 *ice, int chip_mask) { + struct ews_spec *spec = ice->spec; unsigned char data, ndata; snd_assert(chip_mask >= 0 && chip_mask <= 0x0f, return -EINVAL); snd_i2c_lock(ice->i2c); - if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &data, 1) != 1) + if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF2], &data, 1) != 1) goto __error; ndata = (data & 0xf0) | chip_mask; if (ndata != data) - if (snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &ndata, 1) != 1) + if (snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_PCF2], &ndata, 1) + != 1) goto __error; snd_i2c_unlock(ice->i2c); return 0; @@ -224,6 +230,7 @@ static void dmx6fire_ak4524_lock(struct snd_akm4xxx *ak, int chip) static void snd_ice1712_ews_cs8404_spdif_write(struct snd_ice1712 *ice, unsigned char bits) { + struct ews_spec *spec = ice->spec; unsigned char bytes[2]; snd_i2c_lock(ice->i2c); @@ -231,15 +238,18 @@ static void snd_ice1712_ews_cs8404_spdif_write(struct snd_ice1712 *ice, unsigned case ICE1712_SUBDEVICE_EWS88MT: case ICE1712_SUBDEVICE_EWS88MT_NEW: case ICE1712_SUBDEVICE_PHASE88: - if (snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_CS8404], &bits, 1) != 1) + if (snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_CS8404], &bits, 1) + != 1) goto _error; break; case ICE1712_SUBDEVICE_EWS88D: - if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_88D], bytes, 2) != 2) + if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_88D], bytes, 2) + != 2) goto _error; if (bits != bytes[1]) { bytes[1] = bits; - if (snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_88D], bytes, 2) != 2) + if (snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_88D], + bytes, 2) != 2) goto _error; } break; @@ -412,6 +422,7 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice) { int err; struct snd_akm4xxx *ak; + struct ews_spec *spec; /* set the analog DACs */ switch (ice->eeprom.subvendor) { @@ -436,6 +447,11 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice) break; } + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + ice->spec = spec; + /* create i2c */ if ((err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c)) < 0) { snd_printk(KERN_ERR "unable to create I2C bus\n"); @@ -447,7 +463,10 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice) /* create i2c devices */ switch (ice->eeprom.subvendor) { case ICE1712_SUBDEVICE_DMX6FIRE: - if ((err = snd_i2c_device_create(ice->i2c, "PCF9554", ICE1712_6FIRE_PCF9554_ADDR, &ice->spec.i2cdevs[EWS_I2C_6FIRE])) < 0) { + err = snd_i2c_device_create(ice->i2c, "PCF9554", + ICE1712_6FIRE_PCF9554_ADDR, + &spec->i2cdevs[EWS_I2C_6FIRE]); + if (err < 0) { snd_printk(KERN_ERR "PCF9554 initialization failed\n"); return err; } @@ -456,18 +475,30 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice) case ICE1712_SUBDEVICE_EWS88MT: case ICE1712_SUBDEVICE_EWS88MT_NEW: case ICE1712_SUBDEVICE_PHASE88: - if ((err = snd_i2c_device_create(ice->i2c, "CS8404", ICE1712_EWS88MT_CS8404_ADDR, &ice->spec.i2cdevs[EWS_I2C_CS8404])) < 0) + err = snd_i2c_device_create(ice->i2c, "CS8404", + ICE1712_EWS88MT_CS8404_ADDR, + &spec->i2cdevs[EWS_I2C_CS8404]); + if (err < 0) return err; - if ((err = snd_i2c_device_create(ice->i2c, "PCF8574 (1st)", ICE1712_EWS88MT_INPUT_ADDR, &ice->spec.i2cdevs[EWS_I2C_PCF1])) < 0) + err = snd_i2c_device_create(ice->i2c, "PCF8574 (1st)", + ICE1712_EWS88MT_INPUT_ADDR, + &spec->i2cdevs[EWS_I2C_PCF1]); + if (err < 0) return err; - if ((err = snd_i2c_device_create(ice->i2c, "PCF8574 (2nd)", ICE1712_EWS88MT_OUTPUT_ADDR, &ice->spec.i2cdevs[EWS_I2C_PCF2])) < 0) + err = snd_i2c_device_create(ice->i2c, "PCF8574 (2nd)", + ICE1712_EWS88MT_OUTPUT_ADDR, + &spec->i2cdevs[EWS_I2C_PCF2]); + if (err < 0) return err; /* Check if the front module is connected */ if ((err = snd_ice1712_ews88mt_chip_select(ice, 0x0f)) < 0) return err; break; case ICE1712_SUBDEVICE_EWS88D: - if ((err = snd_i2c_device_create(ice->i2c, "PCF8575", ICE1712_EWS88D_PCF_ADDR, &ice->spec.i2cdevs[EWS_I2C_88D])) < 0) + err = snd_i2c_device_create(ice->i2c, "PCF8575", + ICE1712_EWS88D_PCF_ADDR, + &spec->i2cdevs[EWS_I2C_88D]); + if (err < 0) return err; break; } @@ -507,7 +538,7 @@ static int __devinit snd_ice1712_ews_init(struct snd_ice1712 *ice) } /* analog section */ - ak = ice->akm = kmalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); + ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); if (! ak) return -ENOMEM; ice->akm_codecs = 1; @@ -605,10 +636,11 @@ static struct snd_kcontrol_new snd_ice1712_ewx2496_controls[] __devinitdata = { static int snd_ice1712_ews88mt_output_sense_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct ews_spec *spec = ice->spec; unsigned char data; snd_i2c_lock(ice->i2c); - if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &data, 1) != 1) { + if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF2], &data, 1) != 1) { snd_i2c_unlock(ice->i2c); return -EIO; } @@ -621,15 +653,17 @@ static int snd_ice1712_ews88mt_output_sense_get(struct snd_kcontrol *kcontrol, s static int snd_ice1712_ews88mt_output_sense_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct ews_spec *spec = ice->spec; unsigned char data, ndata; snd_i2c_lock(ice->i2c); - if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &data, 1) != 1) { + if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF2], &data, 1) != 1) { snd_i2c_unlock(ice->i2c); return -EIO; } ndata = (data & ~ICE1712_EWS88MT_OUTPUT_SENSE) | (ucontrol->value.enumerated.item[0] ? ICE1712_EWS88MT_OUTPUT_SENSE : 0); - if (ndata != data && snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_PCF2], &ndata, 1) != 1) { + if (ndata != data && snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_PCF2], + &ndata, 1) != 1) { snd_i2c_unlock(ice->i2c); return -EIO; } @@ -641,12 +675,13 @@ static int snd_ice1712_ews88mt_output_sense_put(struct snd_kcontrol *kcontrol, s static int snd_ice1712_ews88mt_input_sense_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct ews_spec *spec = ice->spec; int channel = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); unsigned char data; snd_assert(channel >= 0 && channel <= 7, return 0); snd_i2c_lock(ice->i2c); - if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) { + if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) { snd_i2c_unlock(ice->i2c); return -EIO; } @@ -660,17 +695,19 @@ static int snd_ice1712_ews88mt_input_sense_get(struct snd_kcontrol *kcontrol, st static int snd_ice1712_ews88mt_input_sense_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct ews_spec *spec = ice->spec; int channel = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); unsigned char data, ndata; snd_assert(channel >= 0 && channel <= 7, return 0); snd_i2c_lock(ice->i2c); - if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) { + if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) { snd_i2c_unlock(ice->i2c); return -EIO; } ndata = (data & ~(1 << channel)) | (ucontrol->value.enumerated.item[0] ? 0 : (1 << channel)); - if (ndata != data && snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_PCF1], &ndata, 1) != 1) { + if (ndata != data && snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_PCF1], + &ndata, 1) != 1) { snd_i2c_unlock(ice->i2c); return -EIO; } @@ -705,12 +742,13 @@ static struct snd_kcontrol_new snd_ice1712_ews88mt_output_sense __devinitdata = static int snd_ice1712_ews88d_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct ews_spec *spec = ice->spec; int shift = kcontrol->private_value & 0xff; int invert = (kcontrol->private_value >> 8) & 1; unsigned char data[2]; snd_i2c_lock(ice->i2c); - if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_88D], data, 2) != 2) { + if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_88D], data, 2) != 2) { snd_i2c_unlock(ice->i2c); return -EIO; } @@ -725,13 +763,14 @@ static int snd_ice1712_ews88d_control_get(struct snd_kcontrol *kcontrol, struct static int snd_ice1712_ews88d_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct ews_spec *spec = ice->spec; int shift = kcontrol->private_value & 0xff; int invert = (kcontrol->private_value >> 8) & 1; unsigned char data[2], ndata[2]; int change; snd_i2c_lock(ice->i2c); - if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_88D], data, 2) != 2) { + if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_88D], data, 2) != 2) { snd_i2c_unlock(ice->i2c); return -EIO; } @@ -744,7 +783,8 @@ static int snd_ice1712_ews88d_control_put(struct snd_kcontrol *kcontrol, struct ndata[shift >> 3] |= (1 << (shift & 7)); } change = (data[shift >> 3] != ndata[shift >> 3]); - if (change && snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_88D], data, 2) != 2) { + if (change && + snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_88D], data, 2) != 2) { snd_i2c_unlock(ice->i2c); return -EIO; } @@ -778,11 +818,13 @@ static struct snd_kcontrol_new snd_ice1712_ews88d_controls[] __devinitdata = { static int snd_ice1712_6fire_read_pca(struct snd_ice1712 *ice, unsigned char reg) { unsigned char byte; + struct ews_spec *spec = ice->spec; + snd_i2c_lock(ice->i2c); byte = reg; - snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_6FIRE], &byte, 1); + snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_6FIRE], &byte, 1); byte = 0; - if (snd_i2c_readbytes(ice->spec.i2cdevs[EWS_I2C_6FIRE], &byte, 1) != 1) { + if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_6FIRE], &byte, 1) != 1) { snd_i2c_unlock(ice->i2c); printk(KERN_ERR "cannot read pca\n"); return -EIO; @@ -794,10 +836,12 @@ static int snd_ice1712_6fire_read_pca(struct snd_ice1712 *ice, unsigned char reg static int snd_ice1712_6fire_write_pca(struct snd_ice1712 *ice, unsigned char reg, unsigned char data) { unsigned char bytes[2]; + struct ews_spec *spec = ice->spec; + snd_i2c_lock(ice->i2c); bytes[0] = reg; bytes[1] = data; - if (snd_i2c_sendbytes(ice->spec.i2cdevs[EWS_I2C_6FIRE], bytes, 2) != 2) { + if (snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_6FIRE], bytes, 2) != 2) { snd_i2c_unlock(ice->i2c); return -EIO; } diff --git a/sound/pci/ice1712/hoontech.c b/sound/pci/ice1712/hoontech.c index abcfd1d..cf5c7c0 100644 --- a/sound/pci/ice1712/hoontech.c +++ b/sound/pci/ice1712/hoontech.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -34,6 +33,12 @@ #include "ice1712.h" #include "hoontech.h" +/* Hoontech-specific setting */ +struct hoontech_spec { + unsigned char boxbits[4]; + unsigned int config; + unsigned short boxconfig[4]; +}; static void __devinit snd_ice1712_stdsp24_gpio_write(struct snd_ice1712 *ice, unsigned char byte) { @@ -50,169 +55,182 @@ static void __devinit snd_ice1712_stdsp24_gpio_write(struct snd_ice1712 *ice, un static void __devinit snd_ice1712_stdsp24_darear(struct snd_ice1712 *ice, int activate) { + struct hoontech_spec *spec = ice->spec; mutex_lock(&ice->gpio_mutex); - ICE1712_STDSP24_0_DAREAR(ice->spec.hoontech.boxbits, activate); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[0]); + ICE1712_STDSP24_0_DAREAR(spec->boxbits, activate); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[0]); mutex_unlock(&ice->gpio_mutex); } static void __devinit snd_ice1712_stdsp24_mute(struct snd_ice1712 *ice, int activate) { + struct hoontech_spec *spec = ice->spec; mutex_lock(&ice->gpio_mutex); - ICE1712_STDSP24_3_MUTE(ice->spec.hoontech.boxbits, activate); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]); + ICE1712_STDSP24_3_MUTE(spec->boxbits, activate); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]); mutex_unlock(&ice->gpio_mutex); } static void __devinit snd_ice1712_stdsp24_insel(struct snd_ice1712 *ice, int activate) { + struct hoontech_spec *spec = ice->spec; mutex_lock(&ice->gpio_mutex); - ICE1712_STDSP24_3_INSEL(ice->spec.hoontech.boxbits, activate); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]); + ICE1712_STDSP24_3_INSEL(spec->boxbits, activate); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]); mutex_unlock(&ice->gpio_mutex); } static void __devinit snd_ice1712_stdsp24_box_channel(struct snd_ice1712 *ice, int box, int chn, int activate) { + struct hoontech_spec *spec = ice->spec; + mutex_lock(&ice->gpio_mutex); /* select box */ - ICE1712_STDSP24_0_BOX(ice->spec.hoontech.boxbits, box); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[0]); + ICE1712_STDSP24_0_BOX(spec->boxbits, box); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[0]); /* prepare for write */ if (chn == 3) - ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 0); - ICE1712_STDSP24_2_MIDI1(ice->spec.hoontech.boxbits, activate); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]); - - ICE1712_STDSP24_1_CHN1(ice->spec.hoontech.boxbits, 1); - ICE1712_STDSP24_1_CHN2(ice->spec.hoontech.boxbits, 1); - ICE1712_STDSP24_1_CHN3(ice->spec.hoontech.boxbits, 1); - ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 1); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[1]); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]); + ICE1712_STDSP24_2_CHN4(spec->boxbits, 0); + ICE1712_STDSP24_2_MIDI1(spec->boxbits, activate); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]); + + ICE1712_STDSP24_1_CHN1(spec->boxbits, 1); + ICE1712_STDSP24_1_CHN2(spec->boxbits, 1); + ICE1712_STDSP24_1_CHN3(spec->boxbits, 1); + ICE1712_STDSP24_2_CHN4(spec->boxbits, 1); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[1]); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]); udelay(100); if (chn == 3) { - ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 0); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]); + ICE1712_STDSP24_2_CHN4(spec->boxbits, 0); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]); } else { switch (chn) { - case 0: ICE1712_STDSP24_1_CHN1(ice->spec.hoontech.boxbits, 0); break; - case 1: ICE1712_STDSP24_1_CHN2(ice->spec.hoontech.boxbits, 0); break; - case 2: ICE1712_STDSP24_1_CHN3(ice->spec.hoontech.boxbits, 0); break; + case 0: ICE1712_STDSP24_1_CHN1(spec->boxbits, 0); break; + case 1: ICE1712_STDSP24_1_CHN2(spec->boxbits, 0); break; + case 2: ICE1712_STDSP24_1_CHN3(spec->boxbits, 0); break; } - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[1]); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[1]); } udelay(100); - ICE1712_STDSP24_1_CHN1(ice->spec.hoontech.boxbits, 1); - ICE1712_STDSP24_1_CHN2(ice->spec.hoontech.boxbits, 1); - ICE1712_STDSP24_1_CHN3(ice->spec.hoontech.boxbits, 1); - ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 1); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[1]); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]); + ICE1712_STDSP24_1_CHN1(spec->boxbits, 1); + ICE1712_STDSP24_1_CHN2(spec->boxbits, 1); + ICE1712_STDSP24_1_CHN3(spec->boxbits, 1); + ICE1712_STDSP24_2_CHN4(spec->boxbits, 1); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[1]); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]); udelay(100); - ICE1712_STDSP24_2_MIDI1(ice->spec.hoontech.boxbits, 0); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]); + ICE1712_STDSP24_2_MIDI1(spec->boxbits, 0); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]); mutex_unlock(&ice->gpio_mutex); } static void __devinit snd_ice1712_stdsp24_box_midi(struct snd_ice1712 *ice, int box, int master) { + struct hoontech_spec *spec = ice->spec; + mutex_lock(&ice->gpio_mutex); /* select box */ - ICE1712_STDSP24_0_BOX(ice->spec.hoontech.boxbits, box); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[0]); + ICE1712_STDSP24_0_BOX(spec->boxbits, box); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[0]); - ICE1712_STDSP24_2_MIDIIN(ice->spec.hoontech.boxbits, 1); - ICE1712_STDSP24_2_MIDI1(ice->spec.hoontech.boxbits, master); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]); + ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 1); + ICE1712_STDSP24_2_MIDI1(spec->boxbits, master); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]); udelay(100); - ICE1712_STDSP24_2_MIDIIN(ice->spec.hoontech.boxbits, 0); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]); + ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 0); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]); mdelay(10); - ICE1712_STDSP24_2_MIDIIN(ice->spec.hoontech.boxbits, 1); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[2]); + ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 1); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[2]); mutex_unlock(&ice->gpio_mutex); } static void __devinit snd_ice1712_stdsp24_midi2(struct snd_ice1712 *ice, int activate) { + struct hoontech_spec *spec = ice->spec; mutex_lock(&ice->gpio_mutex); - ICE1712_STDSP24_3_MIDI2(ice->spec.hoontech.boxbits, activate); - snd_ice1712_stdsp24_gpio_write(ice, ice->spec.hoontech.boxbits[3]); + ICE1712_STDSP24_3_MIDI2(spec->boxbits, activate); + snd_ice1712_stdsp24_gpio_write(ice, spec->boxbits[3]); mutex_unlock(&ice->gpio_mutex); } static int __devinit snd_ice1712_hoontech_init(struct snd_ice1712 *ice) { + struct hoontech_spec *spec; int box, chn; ice->num_total_dacs = 8; ice->num_total_adcs = 8; - ice->spec.hoontech.boxbits[0] = - ice->spec.hoontech.boxbits[1] = - ice->spec.hoontech.boxbits[2] = - ice->spec.hoontech.boxbits[3] = 0; /* should be already */ - - ICE1712_STDSP24_SET_ADDR(ice->spec.hoontech.boxbits, 0); - ICE1712_STDSP24_CLOCK(ice->spec.hoontech.boxbits, 0, 1); - ICE1712_STDSP24_0_BOX(ice->spec.hoontech.boxbits, 0); - ICE1712_STDSP24_0_DAREAR(ice->spec.hoontech.boxbits, 0); - - ICE1712_STDSP24_SET_ADDR(ice->spec.hoontech.boxbits, 1); - ICE1712_STDSP24_CLOCK(ice->spec.hoontech.boxbits, 1, 1); - ICE1712_STDSP24_1_CHN1(ice->spec.hoontech.boxbits, 1); - ICE1712_STDSP24_1_CHN2(ice->spec.hoontech.boxbits, 1); - ICE1712_STDSP24_1_CHN3(ice->spec.hoontech.boxbits, 1); + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + ice->spec = spec; + + ICE1712_STDSP24_SET_ADDR(spec->boxbits, 0); + ICE1712_STDSP24_CLOCK(spec->boxbits, 0, 1); + ICE1712_STDSP24_0_BOX(spec->boxbits, 0); + ICE1712_STDSP24_0_DAREAR(spec->boxbits, 0); + + ICE1712_STDSP24_SET_ADDR(spec->boxbits, 1); + ICE1712_STDSP24_CLOCK(spec->boxbits, 1, 1); + ICE1712_STDSP24_1_CHN1(spec->boxbits, 1); + ICE1712_STDSP24_1_CHN2(spec->boxbits, 1); + ICE1712_STDSP24_1_CHN3(spec->boxbits, 1); - ICE1712_STDSP24_SET_ADDR(ice->spec.hoontech.boxbits, 2); - ICE1712_STDSP24_CLOCK(ice->spec.hoontech.boxbits, 2, 1); - ICE1712_STDSP24_2_CHN4(ice->spec.hoontech.boxbits, 1); - ICE1712_STDSP24_2_MIDIIN(ice->spec.hoontech.boxbits, 1); - ICE1712_STDSP24_2_MIDI1(ice->spec.hoontech.boxbits, 0); - - ICE1712_STDSP24_SET_ADDR(ice->spec.hoontech.boxbits, 3); - ICE1712_STDSP24_CLOCK(ice->spec.hoontech.boxbits, 3, 1); - ICE1712_STDSP24_3_MIDI2(ice->spec.hoontech.boxbits, 0); - ICE1712_STDSP24_3_MUTE(ice->spec.hoontech.boxbits, 1); - ICE1712_STDSP24_3_INSEL(ice->spec.hoontech.boxbits, 0); + ICE1712_STDSP24_SET_ADDR(spec->boxbits, 2); + ICE1712_STDSP24_CLOCK(spec->boxbits, 2, 1); + ICE1712_STDSP24_2_CHN4(spec->boxbits, 1); + ICE1712_STDSP24_2_MIDIIN(spec->boxbits, 1); + ICE1712_STDSP24_2_MIDI1(spec->boxbits, 0); + + ICE1712_STDSP24_SET_ADDR(spec->boxbits, 3); + ICE1712_STDSP24_CLOCK(spec->boxbits, 3, 1); + ICE1712_STDSP24_3_MIDI2(spec->boxbits, 0); + ICE1712_STDSP24_3_MUTE(spec->boxbits, 1); + ICE1712_STDSP24_3_INSEL(spec->boxbits, 0); /* let's go - activate only functions in first box */ - ice->spec.hoontech.config = 0; + spec->config = 0; /* ICE1712_STDSP24_MUTE | ICE1712_STDSP24_INSEL | ICE1712_STDSP24_DAREAR; */ - ice->spec.hoontech.boxconfig[0] = ICE1712_STDSP24_BOX_CHN1 | + spec->boxconfig[0] = ICE1712_STDSP24_BOX_CHN1 | ICE1712_STDSP24_BOX_CHN2 | ICE1712_STDSP24_BOX_CHN3 | ICE1712_STDSP24_BOX_CHN4 | ICE1712_STDSP24_BOX_MIDI1 | ICE1712_STDSP24_BOX_MIDI2; - ice->spec.hoontech.boxconfig[1] = - ice->spec.hoontech.boxconfig[2] = - ice->spec.hoontech.boxconfig[3] = 0; - snd_ice1712_stdsp24_darear(ice, (ice->spec.hoontech.config & ICE1712_STDSP24_DAREAR) ? 1 : 0); - snd_ice1712_stdsp24_mute(ice, (ice->spec.hoontech.config & ICE1712_STDSP24_MUTE) ? 1 : 0); - snd_ice1712_stdsp24_insel(ice, (ice->spec.hoontech.config & ICE1712_STDSP24_INSEL) ? 1 : 0); - for (box = 0; box < 4; box++) { + spec->boxconfig[1] = + spec->boxconfig[2] = + spec->boxconfig[3] = 0; + snd_ice1712_stdsp24_darear(ice, + (spec->config & ICE1712_STDSP24_DAREAR) ? 1 : 0); + snd_ice1712_stdsp24_mute(ice, + (spec->config & ICE1712_STDSP24_MUTE) ? 1 : 0); + snd_ice1712_stdsp24_insel(ice, + (spec->config & ICE1712_STDSP24_INSEL) ? 1 : 0); + for (box = 0; box < 1; box++) { + if (spec->boxconfig[box] & ICE1712_STDSP24_BOX_MIDI2) + snd_ice1712_stdsp24_midi2(ice, 1); for (chn = 0; chn < 4; chn++) - snd_ice1712_stdsp24_box_channel(ice, box, chn, (ice->spec.hoontech.boxconfig[box] & (1 << chn)) ? 1 : 0); + snd_ice1712_stdsp24_box_channel(ice, box, chn, + (spec->boxconfig[box] & (1 << chn)) ? 1 : 0); snd_ice1712_stdsp24_box_midi(ice, box, - (ice->spec.hoontech.boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1) ? 1 : 0); - if (ice->spec.hoontech.boxconfig[box] & ICE1712_STDSP24_BOX_MIDI2) - snd_ice1712_stdsp24_midi2(ice, 1); + (spec->boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1) ? 1 : 0); } return 0; diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index 052fc3c..df292af 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -47,7 +47,6 @@ */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -2491,6 +2490,7 @@ static int snd_ice1712_free(struct snd_ice1712 *ice) pci_release_regions(ice->pci); snd_ice1712_akm4xxx_free(ice); pci_disable_device(ice->pci); + kfree(ice->spec); kfree(ice); return 0; } diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h index 58640af..303cffe 100644 --- a/sound/pci/ice1712/ice1712.h +++ b/sound/pci/ice1712/ice1712.h @@ -366,42 +366,7 @@ struct snd_ice1712 { struct mutex gpio_mutex; /* other board-specific data */ - union { - /* additional i2c devices for EWS boards */ - struct snd_i2c_device *i2cdevs[3]; - /* AC97 register cache for Aureon */ - struct aureon_spec { - unsigned short stac9744[64]; - unsigned int cs8415_mux; - unsigned short master[2]; - unsigned short vol[8]; - unsigned char pca9554_out; - } aureon; - /* AC97 register cache for Phase28 */ - struct phase28_spec { - unsigned short master[2]; - unsigned short vol[8]; - } phase28; - /* a non-standard I2C device for revo51 */ - struct revo51_spec { - struct snd_i2c_device *dev; - struct snd_pt2258 *pt2258; - } revo51; - /* Hoontech-specific setting */ - struct hoontech_spec { - unsigned char boxbits[4]; - unsigned int config; - unsigned short boxconfig[4]; - } hoontech; - struct { - struct ak4114 *ak4114; - unsigned int analog: 1; - } juli; - struct { - struct ak4114 *ak4114; - } prodigy192; - } spec; - + void *spec; }; diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index 0b0bbb0..f533850 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -22,7 +22,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -48,9 +47,11 @@ #include "vt1720_mobo.h" #include "pontis.h" #include "prodigy192.h" +#include "prodigy_hifi.h" #include "juli.h" #include "phase.h" #include "wtm.h" +#include "se.h" MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); MODULE_DESCRIPTION("VIA ICEnsemble ICE1724/1720 (Envy24HT/PT)"); @@ -62,9 +63,11 @@ MODULE_SUPPORTED_DEVICE("{" VT1720_MOBO_DEVICE_DESC PONTIS_DEVICE_DESC PRODIGY192_DEVICE_DESC + PRODIGY_HIFI_DEVICE_DESC JULI_DEVICE_DESC PHASE_DEVICE_DESC WTM_DEVICE_DESC + SE_DEVICE_DESC "{VIA,VT1720}," "{VIA,VT1724}," "{ICEnsemble,Generic ICE1724}," @@ -1929,10 +1932,12 @@ static struct snd_ice1712_card_info *card_tables[] __devinitdata = { snd_vt1724_aureon_cards, snd_vt1720_mobo_cards, snd_vt1720_pontis_cards, + snd_vt1724_prodigy_hifi_cards, snd_vt1724_prodigy192_cards, snd_vt1724_juli_cards, snd_vt1724_phase_cards, snd_vt1724_wtm_cards, + snd_vt1724_se_cards, NULL, }; @@ -1955,6 +1960,7 @@ unsigned char snd_vt1724_read_i2c(struct snd_ice1712 *ice, unsigned char val; mutex_lock(&ice->i2c_mutex); + wait_i2c_busy(ice); outb(addr, ICEREG1724(ice, I2C_BYTE_ADDR)); outb(dev & ~VT1724_I2C_WRITE, ICEREG1724(ice, I2C_DEV_ADDR)); wait_i2c_busy(ice); @@ -2170,6 +2176,7 @@ static int snd_vt1724_free(struct snd_ice1712 *ice) pci_release_regions(ice->pci); snd_ice1712_akm4xxx_free(ice); pci_disable_device(ice->pci); + kfree(ice->spec); kfree(ice); return 0; } diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c index 1fbe3ef..e8038c0 100644 --- a/sound/pci/ice1712/juli.c +++ b/sound/pci/ice1712/juli.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -33,6 +32,11 @@ #include "envy24ht.h" #include "juli.h" +struct juli_spec { + struct ak4114 *ak4114; + unsigned int analog: 1; +}; + /* * chip addresses on I2C bus */ @@ -138,12 +142,13 @@ static struct snd_akm4xxx akm_juli_dac __devinitdata = { static int __devinit juli_add_controls(struct snd_ice1712 *ice) { + struct juli_spec *spec = ice->spec; int err; err = snd_ice1712_akm4xxx_build_controls(ice); if (err < 0) return err; /* only capture SPDIF over AK4114 */ - err = snd_ak4114_build(ice->spec.juli.ak4114, NULL, + err = snd_ak4114_build(spec->ak4114, NULL, ice->pcm_pro->streams[SNDRV_PCM_STREAM_CAPTURE].substream); if (err < 0) return err; @@ -167,13 +172,19 @@ static int __devinit juli_init(struct snd_ice1712 *ice) 0x41, 0x02, 0x2c, 0x00, 0x00 }; int err; + struct juli_spec *spec; struct snd_akm4xxx *ak; + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + ice->spec = spec; + err = snd_ak4114_create(ice->card, juli_ak4114_read, juli_ak4114_write, ak4114_init_vals, ak4114_init_txcsb, - ice, &ice->spec.juli.ak4114); + ice, &spec->ak4114); if (err < 0) return err; @@ -181,12 +192,12 @@ static int __devinit juli_init(struct snd_ice1712 *ice) /* it seems that the analog doughter board detection does not work reliably, so force the analog flag; it should be very rare to use Juli@ without the analog doughter board */ - ice->spec.juli.analog = (ice->gpio.get_data(ice) & GPIO_ANALOG_PRESENT) ? 0 : 1; + spec->analog = (ice->gpio.get_data(ice) & GPIO_ANALOG_PRESENT) ? 0 : 1; #else - ice->spec.juli.analog = 1; + spec->analog = 1; #endif - if (ice->spec.juli.analog) { + if (spec->analog) { printk(KERN_INFO "juli@: analog I/O detected\n"); ice->num_total_dacs = 2; ice->num_total_adcs = 2; diff --git a/sound/pci/ice1712/phase.c b/sound/pci/ice1712/phase.c index 3ac2505..9ab4a9f 100644 --- a/sound/pci/ice1712/phase.c +++ b/sound/pci/ice1712/phase.c @@ -33,7 +33,6 @@ * CDTI may be completely blocked by 74HCT125's gate #1 controlled by GPIO 3 */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -48,6 +47,12 @@ #include "phase.h" #include <sound/tlv.h> +/* AC97 register cache for Phase28 */ +struct phase28_spec { + unsigned short master[2]; + unsigned short vol[8]; +} phase28; + /* WM8770 registers */ #define WM_DAC_ATTEN 0x00 /* DAC1-8 analog attenuation */ #define WM_DAC_MASTER_ATTEN 0x08 /* DAC master analog attenuation */ @@ -313,27 +318,32 @@ static int wm_master_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem static int wm_master_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct phase28_spec *spec = ice->spec; int i; for (i=0; i<2; i++) - ucontrol->value.integer.value[i] = ice->spec.phase28.master[i] & ~WM_VOL_MUTE; + ucontrol->value.integer.value[i] = spec->master[i] & ~WM_VOL_MUTE; return 0; } static int wm_master_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct phase28_spec *spec = ice->spec; int ch, change = 0; snd_ice1712_save_gpio_status(ice); for (ch = 0; ch < 2; ch++) { - if (ucontrol->value.integer.value[ch] != ice->spec.phase28.master[ch]) { + unsigned int vol = ucontrol->value.integer.value[ch]; + if (vol > WM_VOL_MAX) + continue; + vol |= spec->master[ch] & WM_VOL_MUTE; + if (vol != spec->master[ch]) { int dac; - ice->spec.phase28.master[ch] &= WM_VOL_MUTE; - ice->spec.phase28.master[ch] |= ucontrol->value.integer.value[ch]; + spec->master[ch] = vol; for (dac = 0; dac < ice->num_total_dacs; dac += 2) wm_set_vol(ice, WM_DAC_ATTEN + dac + ch, - ice->spec.phase28.vol[dac + ch], - ice->spec.phase28.master[ch]); + spec->vol[dac + ch], + spec->master[ch]); change = 1; } } @@ -382,12 +392,18 @@ static int __devinit phase28_init(struct snd_ice1712 *ice) unsigned int tmp; struct snd_akm4xxx *ak; + struct phase28_spec *spec; const unsigned short *p; int i; ice->num_total_dacs = 8; ice->num_total_adcs = 2; + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + ice->spec = spec; + // Initialize analog chips ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); if (!ak) @@ -417,11 +433,11 @@ static int __devinit phase28_init(struct snd_ice1712 *ice) snd_ice1712_restore_gpio_status(ice); - ice->spec.phase28.master[0] = WM_VOL_MUTE; - ice->spec.phase28.master[1] = WM_VOL_MUTE; + spec->master[0] = WM_VOL_MUTE; + spec->master[1] = WM_VOL_MUTE; for (i = 0; i < ice->num_total_dacs; i++) { - ice->spec.phase28.vol[i] = WM_VOL_MUTE; - wm_set_vol(ice, i, ice->spec.phase28.vol[i], ice->spec.phase28.master[i % 2]); + spec->vol[i] = WM_VOL_MUTE; + wm_set_vol(ice, i, spec->vol[i], spec->master[i % 2]); } return 0; @@ -443,18 +459,21 @@ static int wm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info * static int wm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct phase28_spec *spec = ice->spec; int i, ofs, voices; voices = kcontrol->private_value >> 8; ofs = kcontrol->private_value & 0xff; for (i = 0; i < voices; i++) - ucontrol->value.integer.value[i] = ice->spec.phase28.vol[ofs+i] & ~WM_VOL_MUTE; + ucontrol->value.integer.value[i] = + spec->vol[ofs+i] & ~WM_VOL_MUTE; return 0; } static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct phase28_spec *spec = ice->spec; int i, idx, ofs, voices; int change = 0; @@ -462,12 +481,16 @@ static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value * ofs = kcontrol->private_value & 0xff; snd_ice1712_save_gpio_status(ice); for (i = 0; i < voices; i++) { - idx = WM_DAC_ATTEN + ofs + i; - if (ucontrol->value.integer.value[i] != ice->spec.phase28.vol[ofs+i]) { - ice->spec.phase28.vol[ofs+i] &= WM_VOL_MUTE; - ice->spec.phase28.vol[ofs+i] |= ucontrol->value.integer.value[i]; - wm_set_vol(ice, idx, ice->spec.phase28.vol[ofs+i], - ice->spec.phase28.master[i]); + unsigned int vol; + vol = ucontrol->value.integer.value[i]; + if (vol > 0x7f) + continue; + vol |= spec->vol[ofs+i] & WM_VOL_MUTE; + if (vol != spec->vol[ofs+i]) { + spec->vol[ofs+i] = vol; + idx = WM_DAC_ATTEN + ofs + i; + wm_set_vol(ice, idx, spec->vol[ofs+i], + spec->master[i]); change = 1; } } @@ -489,19 +512,22 @@ static int wm_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info static int wm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct phase28_spec *spec = ice->spec; int voices, ofs, i; voices = kcontrol->private_value >> 8; ofs = kcontrol->private_value & 0xFF; for (i = 0; i < voices; i++) - ucontrol->value.integer.value[i] = (ice->spec.phase28.vol[ofs+i] & WM_VOL_MUTE) ? 0 : 1; + ucontrol->value.integer.value[i] = + (spec->vol[ofs+i] & WM_VOL_MUTE) ? 0 : 1; return 0; } static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct phase28_spec *spec = ice->spec; int change = 0, voices, ofs, i; voices = kcontrol->private_value >> 8; @@ -509,13 +535,13 @@ static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value snd_ice1712_save_gpio_status(ice); for (i = 0; i < voices; i++) { - int val = (ice->spec.phase28.vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1; + int val = (spec->vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1; if (ucontrol->value.integer.value[i] != val) { - ice->spec.phase28.vol[ofs + i] &= ~WM_VOL_MUTE; - ice->spec.phase28.vol[ofs + i] |= + spec->vol[ofs + i] &= ~WM_VOL_MUTE; + spec->vol[ofs + i] |= ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE; - wm_set_vol(ice, ofs + i, ice->spec.phase28.vol[ofs + i], - ice->spec.phase28.master[i]); + wm_set_vol(ice, ofs + i, spec->vol[ofs + i], + spec->master[i]); change = 1; } } @@ -532,29 +558,33 @@ static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value static int wm_master_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct phase28_spec *spec = ice->spec; - ucontrol->value.integer.value[0] = (ice->spec.phase28.master[0] & WM_VOL_MUTE) ? 0 : 1; - ucontrol->value.integer.value[1] = (ice->spec.phase28.master[1] & WM_VOL_MUTE) ? 0 : 1; + ucontrol->value.integer.value[0] = + (spec->master[0] & WM_VOL_MUTE) ? 0 : 1; + ucontrol->value.integer.value[1] = + (spec->master[1] & WM_VOL_MUTE) ? 0 : 1; return 0; } static int wm_master_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct phase28_spec *spec = ice->spec; int change = 0, i; snd_ice1712_save_gpio_status(ice); for (i = 0; i < 2; i++) { - int val = (ice->spec.phase28.master[i] & WM_VOL_MUTE) ? 0 : 1; + int val = (spec->master[i] & WM_VOL_MUTE) ? 0 : 1; if (ucontrol->value.integer.value[i] != val) { int dac; - ice->spec.phase28.master[i] &= ~WM_VOL_MUTE; - ice->spec.phase28.master[i] |= + spec->master[i] &= ~WM_VOL_MUTE; + spec->master[i] |= ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE; for (dac = 0; dac < ice->num_total_dacs; dac += 2) wm_set_vol(ice, WM_DAC_ATTEN + dac + i, - ice->spec.phase28.vol[dac + i], - ice->spec.phase28.master[i]); + spec->vol[dac + i], + spec->master[i]); change = 1; } } @@ -595,8 +625,10 @@ static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val unsigned short ovol, nvol; int change = 0; - snd_ice1712_save_gpio_status(ice); nvol = ucontrol->value.integer.value[0]; + if (nvol > PCM_RES) + return -EINVAL; + snd_ice1712_save_gpio_status(ice); nvol = (nvol ? (nvol + PCM_MIN) : 0) & 0xff; ovol = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff; if (ovol != nvol) { diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c index faefd52..4945c81 100644 --- a/sound/pci/ice1712/pontis.c +++ b/sound/pci/ice1712/pontis.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c index 4180f97..48cf40a 100644 --- a/sound/pci/ice1712/prodigy192.c +++ b/sound/pci/ice1712/prodigy192.c @@ -54,7 +54,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -68,6 +67,12 @@ #include "stac946x.h" #include <sound/tlv.h> +struct prodigy192_spec { + struct ak4114 *ak4114; + /* rate change needs atomic mute/unmute of all dacs*/ + struct mutex mute_mutex; +}; + static inline void stac9460_put(struct snd_ice1712 *ice, int reg, unsigned char val) { snd_vt1724_write_i2c(ice, PRODIGY192_STAC9460_ADDR, reg, val); @@ -81,6 +86,24 @@ static inline unsigned char stac9460_get(struct snd_ice1712 *ice, int reg) /* * DAC mute control */ + +/* + * idx = STAC9460 volume register number, mute: 0 = mute, 1 = unmute + */ +static int stac9460_dac_mute(struct snd_ice1712 *ice, int idx, + unsigned char mute) +{ + unsigned char new, old; + int change; + old = stac9460_get(ice, idx); + new = (~mute << 7 & 0x80) | (old & ~0x80); + change = (new != old); + if (change) + /*printk ("Volume register 0x%02x: 0x%02x\n", idx, new);*/ + stac9460_put(ice, idx, new); + return change; +} + #define stac9460_dac_mute_info snd_ctl_boolean_mono_info static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -101,20 +124,19 @@ static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e static int stac9460_dac_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - unsigned char new, old; - int idx; - int change; + struct prodigy192_spec *spec = ice->spec; + int idx, change; if (kcontrol->private_value) idx = STAC946X_MASTER_VOLUME; else idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + STAC946X_LF_VOLUME; - old = stac9460_get(ice, idx); - new = (~ucontrol->value.integer.value[0]<< 7 & 0x80) | (old & ~0x80); - change = (new != old); - if (change) - stac9460_put(ice, idx, new); - + /* due to possible conflicts with stac9460_set_rate_val, mutexing */ + mutex_lock(&spec->mute_mutex); + /*printk("Mute put: reg 0x%02x, ctrl value: 0x%02x\n", idx, + ucontrol->value.integer.value[0]);*/ + change = stac9460_dac_mute(ice, idx, ucontrol->value.integer.value[0]); + mutex_unlock(&spec->mute_mutex); return change; } @@ -162,6 +184,8 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el ovol = 0x7f - (tmp & 0x7f); change = (ovol != nvol); if (change) { + ovol = (0x7f - nvol) | (tmp & 0x80); + /*printk("DAC Volume: reg 0x%02x: 0x%02x\n", idx, ovol);*/ stac9460_put(ice, idx, (0x7f - nvol) | (tmp & 0x80)); } return change; @@ -241,7 +265,7 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el for (i = 0; i < 2; ++i) { reg = STAC946X_MIC_L_VOLUME + i; - nvol = ucontrol->value.integer.value[i]; + nvol = ucontrol->value.integer.value[i] & 0x0f; ovol = 0x0f - stac9460_get(ice, reg); change = ((ovol & 0x0f) != nvol); if (change) @@ -251,121 +275,6 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el return change; } -#if 0 -/* - * Headphone Amplifier - */ -static int aureon_set_headphone_amp(struct snd_ice1712 *ice, int enable) -{ - unsigned int tmp, tmp2; - - tmp2 = tmp = snd_ice1712_gpio_read(ice); - if (enable) - tmp |= AUREON_HP_SEL; - else - tmp &= ~ AUREON_HP_SEL; - if (tmp != tmp2) { - snd_ice1712_gpio_write(ice, tmp); - return 1; - } - return 0; -} - -static int aureon_get_headphone_amp(struct snd_ice1712 *ice) -{ - unsigned int tmp = snd_ice1712_gpio_read(ice); - - return ( tmp & AUREON_HP_SEL )!= 0; -} - -#define aureon_bool_info snd_ctl_boolean_mono_info - -static int aureon_hpamp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - - ucontrol->value.integer.value[0] = aureon_get_headphone_amp(ice); - return 0; -} - - -static int aureon_hpamp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - - return aureon_set_headphone_amp(ice,ucontrol->value.integer.value[0]); -} - -/* - * Deemphasis - */ -static int aureon_deemp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL2) & 0xf) == 0xf; - return 0; -} - -static int aureon_deemp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - int temp, temp2; - temp2 = temp = wm_get(ice, WM_DAC_CTRL2); - if (ucontrol->value.integer.value[0]) - temp |= 0xf; - else - temp &= ~0xf; - if (temp != temp2) { - wm_put(ice, WM_DAC_CTRL2, temp); - return 1; - } - return 0; -} - -/* - * ADC Oversampling - */ -static int aureon_oversampling_info(struct snd_kcontrol *k, struct snd_ctl_elem_info *uinfo) -{ - static char *texts[2] = { "128x", "64x" }; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - - return 0; -} - -static int aureon_oversampling_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - ucontrol->value.enumerated.item[0] = (wm_get(ice, WM_MASTER) & 0x8) == 0x8; - return 0; -} - -static int aureon_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - int temp, temp2; - struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - - temp2 = temp = wm_get(ice, WM_MASTER); - - if (ucontrol->value.enumerated.item[0]) - temp |= 0x8; - else - temp &= ~0x8; - - if (temp != temp2) { - wm_put(ice, WM_MASTER, temp); - return 1; - } - return 0; -} -#endif static int stac9460_mic_sw_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -407,6 +316,57 @@ static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol, stac9460_put(ice, STAC946X_GENERAL_PURPOSE, new); return change; } +/* + * Handler for setting correct codec rate - called when rate change is detected + */ +static void stac9460_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate) +{ + unsigned char old, new; + int idx; + unsigned char changed[7]; + struct snd_ice1712 *ice = ak->private_data[0]; + struct prodigy192_spec *spec = ice->spec; + + if (rate == 0) /* no hint - S/PDIF input is master, simply return */ + return; + else if (rate <= 48000) + new = 0x08; /* 256x, base rate mode */ + else if (rate <= 96000) + new = 0x11; /* 256x, mid rate mode */ + else + new = 0x12; /* 128x, high rate mode */ + old = stac9460_get(ice, STAC946X_MASTER_CLOCKING); + if (old == new) + return; + /* change detected, setting master clock, muting first */ + /* due to possible conflicts with mute controls - mutexing */ + mutex_lock(&spec->mute_mutex); + /* we have to remember current mute status for each DAC */ + for (idx = 0; idx < 7 ; ++idx) + changed[idx] = stac9460_dac_mute(ice, + STAC946X_MASTER_VOLUME + idx, 0); + /*printk("Rate change: %d, new MC: 0x%02x\n", rate, new);*/ + stac9460_put(ice, STAC946X_MASTER_CLOCKING, new); + udelay(10); + /* unmuting - only originally unmuted dacs - + * i.e. those changed when muting */ + for (idx = 0; idx < 7 ; ++idx) { + if (changed[idx]) + stac9460_dac_mute(ice, STAC946X_MASTER_VOLUME + idx, 1); + } + mutex_unlock(&spec->mute_mutex); +} + +/* using akm infrastructure for setting rate of the codec */ +static struct snd_akm4xxx akmlike_stac9460 __devinitdata = { + .type = NON_AKM, /* special value */ + .num_adcs = 6, /* not used in any way, just for completeness */ + .num_dacs = 2, + .ops = { + .set_rate_val = stac9460_set_rate_val + } +}; + static const DECLARE_TLV_DB_SCALE(db_scale_dac, -19125, 75, 0); static const DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0); @@ -483,39 +443,8 @@ static struct snd_kcontrol_new stac_controls[] __devinitdata = { .put = stac9460_mic_sw_put, }, -#if 0 - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Route", - .info = wm_adc_mux_info, - .get = wm_adc_mux_get, - .put = wm_adc_mux_put, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Headphone Amplifier Switch", - .info = aureon_bool_info, - .get = aureon_hpamp_get, - .put = aureon_hpamp_put - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "DAC Deemphasis Switch", - .info = aureon_bool_info, - .get = aureon_deemp_get, - .put = aureon_deemp_put - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "ADC Oversampling", - .info = aureon_oversampling_info, - .get = aureon_oversampling_get, - .put = aureon_oversampling_put - }, -#endif }; - /* AK4114 - ICE1724 connections on Prodigy192 + MI/ODI/O */ /* CDTO (pin 32) -- GPIO11 pin 86 * CDTI (pin 33) -- GPIO10 pin 77 @@ -712,16 +641,39 @@ static int prodigy192_ak4114_init(struct snd_ice1712 *ice) static const unsigned char ak4114_init_txcsb[] = { 0x41, 0x02, 0x2c, 0x00, 0x00 }; + struct prodigy192_spec *spec = ice->spec; return snd_ak4114_create(ice->card, prodigy192_ak4114_read, prodigy192_ak4114_write, ak4114_init_vals, ak4114_init_txcsb, - ice, &ice->spec.prodigy192.ak4114); + ice, &spec->ak4114); +} + +static void stac9460_proc_regs_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data; + int reg, val; + /* registers 0x0 - 0x14 */ + for (reg = 0; reg <= 0x15; reg++) { + val = stac9460_get(ice, reg); + snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val); + } +} + + +static void stac9460_proc_init(struct snd_ice1712 *ice) +{ + struct snd_info_entry *entry; + if (!snd_card_proc_new(ice->card, "stac9460_codec", &entry)) + snd_info_set_text_ops(entry, ice, stac9460_proc_regs_read); } + static int __devinit prodigy192_add_controls(struct snd_ice1712 *ice) { + struct prodigy192_spec *spec = ice->spec; unsigned int i; int err; @@ -731,7 +683,7 @@ static int __devinit prodigy192_add_controls(struct snd_ice1712 *ice) if (err < 0) return err; } - if (ice->spec.prodigy192.ak4114) { + if (spec->ak4114) { /* ak4114 is connected */ for (i = 0; i < ARRAY_SIZE(ak4114_controls); i++) { err = snd_ctl_add(ice->card, @@ -740,12 +692,13 @@ static int __devinit prodigy192_add_controls(struct snd_ice1712 *ice) if (err < 0) return err; } - err = snd_ak4114_build(ice->spec.prodigy192.ak4114, + err = snd_ak4114_build(spec->ak4114, NULL, /* ak4114 in MIO/DI/O handles no IEC958 output */ ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); if (err < 0) return err; } + stac9460_proc_init(ice); return 0; } @@ -778,6 +731,7 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice) { static const unsigned short stac_inits_prodigy[] = { STAC946X_RESET, 0, + STAC946X_MASTER_CLOCKING, 0x11, /* STAC946X_MASTER_VOLUME, 0, STAC946X_LF_VOLUME, 0, STAC946X_RF_VOLUME, 0, @@ -789,22 +743,39 @@ static int __devinit prodigy192_init(struct snd_ice1712 *ice) }; const unsigned short *p; int err = 0; + struct snd_akm4xxx *ak; + struct prodigy192_spec *spec; /* prodigy 192 */ ice->num_total_dacs = 6; ice->num_total_adcs = 2; ice->vt1720 = 0; /* ice1724, e.g. 23 GPIOs */ + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + ice->spec = spec; + mutex_init(&spec->mute_mutex); + /* initialize codec */ p = stac_inits_prodigy; for (; *p != (unsigned short)-1; p += 2) stac9460_put(ice, p[0], p[1]); + /* reusing the akm codecs infrastructure, + * for setting rate on stac9460 */ + ak = ice->akm = kmalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); + if (!ak) + return -ENOMEM; + ice->akm_codecs = 1; + err = snd_ice1712_akm4xxx_init(ak, &akmlike_stac9460, NULL, ice); + if (err < 0) + return err; /* MI/ODI/O add on card with AK4114 */ if (prodigy192_miodio_exists(ice)) { err = prodigy192_ak4114_init(ice); /* from this moment if err = 0 then - * ice->spec.prodigy192.ak4114 should not be null + * spec->ak4114 should not be null */ snd_printdd("AK4114 initialized with status %d\n", err); } else @@ -854,6 +825,10 @@ struct snd_ice1712_card_info snd_vt1724_prodigy192_cards[] __devinitdata = { .build_controls = prodigy192_add_controls, .eeprom_size = sizeof(prodigy71_eeprom), .eeprom_data = prodigy71_eeprom, + /* the current MPU401 code loops infinitely + * when opening midi device + */ + .no_mpu401 = 1, }, { } /* terminator */ }; diff --git a/sound/pci/ice1712/prodigy_hifi.c b/sound/pci/ice1712/prodigy_hifi.c new file mode 100644 index 0000000..043a938 --- /dev/null +++ b/sound/pci/ice1712/prodigy_hifi.c @@ -0,0 +1,1210 @@ +/* + * ALSA driver for ICEnsemble VT1724 (Envy24HT) + * + * Lowlevel functions for Audiotrak Prodigy 7.1 Hifi + * based on pontis.c + * + * Copyright (c) 2007 Julian Scheel <julian@jusst.de> + * Copyright (c) 2007 allank + * Copyright (c) 2004 Takashi Iwai <tiwai@suse.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 <asm/io.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/mutex.h> + +#include <sound/core.h> +#include <sound/info.h> +#include <sound/tlv.h> + +#include "ice1712.h" +#include "envy24ht.h" +#include "prodigy_hifi.h" + +struct prodigy_hifi_spec { + unsigned short master[2]; + unsigned short vol[8]; +}; + +/* I2C addresses */ +#define WM_DEV 0x34 + +/* WM8776 registers */ +#define WM_HP_ATTEN_L 0x00 /* headphone left attenuation */ +#define WM_HP_ATTEN_R 0x01 /* headphone left attenuation */ +#define WM_HP_MASTER 0x02 /* headphone master (both channels), + override LLR */ +#define WM_DAC_ATTEN_L 0x03 /* digital left attenuation */ +#define WM_DAC_ATTEN_R 0x04 +#define WM_DAC_MASTER 0x05 +#define WM_PHASE_SWAP 0x06 /* DAC phase swap */ +#define WM_DAC_CTRL1 0x07 +#define WM_DAC_MUTE 0x08 +#define WM_DAC_CTRL2 0x09 +#define WM_DAC_INT 0x0a +#define WM_ADC_INT 0x0b +#define WM_MASTER_CTRL 0x0c +#define WM_POWERDOWN 0x0d +#define WM_ADC_ATTEN_L 0x0e +#define WM_ADC_ATTEN_R 0x0f +#define WM_ALC_CTRL1 0x10 +#define WM_ALC_CTRL2 0x11 +#define WM_ALC_CTRL3 0x12 +#define WM_NOISE_GATE 0x13 +#define WM_LIMITER 0x14 +#define WM_ADC_MUX 0x15 +#define WM_OUT_MUX 0x16 +#define WM_RESET 0x17 + +/* Analog Recording Source :- Mic, LineIn, CD/Video, */ + +/* implement capture source select control for WM8776 */ + +#define WM_AIN1 "AIN1" +#define WM_AIN2 "AIN2" +#define WM_AIN3 "AIN3" +#define WM_AIN4 "AIN4" +#define WM_AIN5 "AIN5" + +/* GPIO pins of envy24ht connected to wm8766 */ +#define WM8766_SPI_CLK (1<<17) /* CLK, Pin97 on ICE1724 */ +#define WM8766_SPI_MD (1<<16) /* DATA VT1724 -> WM8766, Pin96 */ +#define WM8766_SPI_ML (1<<18) /* Latch, Pin98 */ + +/* WM8766 registers */ +#define WM8766_DAC_CTRL 0x02 /* DAC Control */ +#define WM8766_INT_CTRL 0x03 /* Interface Control */ +#define WM8766_DAC_CTRL2 0x09 +#define WM8766_DAC_CTRL3 0x0a +#define WM8766_RESET 0x1f +#define WM8766_LDA1 0x00 +#define WM8766_LDA2 0x04 +#define WM8766_LDA3 0x06 +#define WM8766_RDA1 0x01 +#define WM8766_RDA2 0x05 +#define WM8766_RDA3 0x07 +#define WM8766_MUTE1 0x0C +#define WM8766_MUTE2 0x0F + + +/* + * Prodigy HD2 + */ +#define AK4396_ADDR 0x00 +#define AK4396_CSN (1 << 8) /* CSN->GPIO8, pin 75 */ +#define AK4396_CCLK (1 << 9) /* CCLK->GPIO9, pin 76 */ +#define AK4396_CDTI (1 << 10) /* CDTI->GPIO10, pin 77 */ + +/* ak4396 registers */ +#define AK4396_CTRL1 0x00 +#define AK4396_CTRL2 0x01 +#define AK4396_CTRL3 0x02 +#define AK4396_LCH_ATT 0x03 +#define AK4396_RCH_ATT 0x04 + + +/* + * get the current register value of WM codec + */ +static unsigned short wm_get(struct snd_ice1712 *ice, int reg) +{ + reg <<= 1; + return ((unsigned short)ice->akm[0].images[reg] << 8) | + ice->akm[0].images[reg + 1]; +} + +/* + * set the register value of WM codec and remember it + */ +static void wm_put_nocache(struct snd_ice1712 *ice, int reg, unsigned short val) +{ + unsigned short cval; + cval = (reg << 9) | val; + snd_vt1724_write_i2c(ice, WM_DEV, cval >> 8, cval & 0xff); +} + +static void wm_put(struct snd_ice1712 *ice, int reg, unsigned short val) +{ + wm_put_nocache(ice, reg, val); + reg <<= 1; + ice->akm[0].images[reg] = val >> 8; + ice->akm[0].images[reg + 1] = val; +} + +/* + * write data in the SPI mode + */ + +static void set_gpio_bit(struct snd_ice1712 *ice, unsigned int bit, int val) +{ + unsigned int tmp = snd_ice1712_gpio_read(ice); + if (val) + tmp |= bit; + else + tmp &= ~bit; + snd_ice1712_gpio_write(ice, tmp); +} + +/* + * SPI implementation for WM8766 codec - only writing supported, no readback + */ + +static void wm8766_spi_send_word(struct snd_ice1712 *ice, unsigned int data) +{ + int i; + for (i = 0; i < 16; i++) { + set_gpio_bit(ice, WM8766_SPI_CLK, 0); + udelay(1); + set_gpio_bit(ice, WM8766_SPI_MD, data & 0x8000); + udelay(1); + set_gpio_bit(ice, WM8766_SPI_CLK, 1); + udelay(1); + data <<= 1; + } +} + +static void wm8766_spi_write(struct snd_ice1712 *ice, unsigned int reg, + unsigned int data) +{ + unsigned int block; + + snd_ice1712_gpio_set_dir(ice, WM8766_SPI_MD| + WM8766_SPI_CLK|WM8766_SPI_ML); + snd_ice1712_gpio_set_mask(ice, ~(WM8766_SPI_MD| + WM8766_SPI_CLK|WM8766_SPI_ML)); + /* latch must be low when writing */ + set_gpio_bit(ice, WM8766_SPI_ML, 0); + block = (reg << 9) | (data & 0x1ff); + wm8766_spi_send_word(ice, block); /* REGISTER ADDRESS */ + /* release latch */ + set_gpio_bit(ice, WM8766_SPI_ML, 1); + udelay(1); + /* restore */ + snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask); + snd_ice1712_gpio_set_dir(ice, ice->gpio.direction); +} + + +/* + * serial interface for ak4396 - only writing supported, no readback + */ + +static void ak4396_send_word(struct snd_ice1712 *ice, unsigned int data) +{ + int i; + for (i = 0; i < 16; i++) { + set_gpio_bit(ice, AK4396_CCLK, 0); + udelay(1); + set_gpio_bit(ice, AK4396_CDTI, data & 0x8000); + udelay(1); + set_gpio_bit(ice, AK4396_CCLK, 1); + udelay(1); + data <<= 1; + } +} + +static void ak4396_write(struct snd_ice1712 *ice, unsigned int reg, + unsigned int data) +{ + unsigned int block; + + snd_ice1712_gpio_set_dir(ice, AK4396_CSN|AK4396_CCLK|AK4396_CDTI); + snd_ice1712_gpio_set_mask(ice, ~(AK4396_CSN|AK4396_CCLK|AK4396_CDTI)); + /* latch must be low when writing */ + set_gpio_bit(ice, AK4396_CSN, 0); + block = ((AK4396_ADDR & 0x03) << 14) | (1 << 13) | + ((reg & 0x1f) << 8) | (data & 0xff); + ak4396_send_word(ice, block); /* REGISTER ADDRESS */ + /* release latch */ + set_gpio_bit(ice, AK4396_CSN, 1); + udelay(1); + /* restore */ + snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask); + snd_ice1712_gpio_set_dir(ice, ice->gpio.direction); +} + + +/* + * ak4396 mixers + */ + + + +/* + * DAC volume attenuation mixer control (-64dB to 0dB) + */ + +static int ak4396_dac_vol_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; /* mute */ + uinfo->value.integer.max = 0xFF; /* linear */ + return 0; +} + +static int ak4396_dac_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct prodigy_hifi_spec *spec = ice->spec; + int i; + + for (i = 0; i < 2; i++) + ucontrol->value.integer.value[i] = spec->vol[i]; + + return 0; +} + +static int ak4396_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct prodigy_hifi_spec *spec = ice->spec; + int i; + int change = 0; + + mutex_lock(&ice->gpio_mutex); + for (i = 0; i < 2; i++) { + if (ucontrol->value.integer.value[i] != spec->vol[i]) { + spec->vol[i] = ucontrol->value.integer.value[i]; + ak4396_write(ice, AK4396_LCH_ATT + i, + spec->vol[i] & 0xff); + change = 1; + } + } + mutex_unlock(&ice->gpio_mutex); + return change; +} + +static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1); + +static struct snd_kcontrol_new prodigy_hd2_controls[] __devinitdata = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "Front Playback Volume", + .info = ak4396_dac_vol_info, + .get = ak4396_dac_vol_get, + .put = ak4396_dac_vol_put, + .tlv = { .p = db_scale_wm_dac }, + }, +}; + + +/* --------------- */ + +/* + * Logarithmic volume values for WM87*6 + * Computed as 20 * Log10(255 / x) + */ +static const unsigned char wm_vol[256] = { + 127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24, 24, 23, + 23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17, + 17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13, + 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0 +}; + +#define WM_VOL_MAX (sizeof(wm_vol) - 1) +#define WM_VOL_MUTE 0x8000 + + +#define DAC_0dB 0xff +#define DAC_RES 128 +#define DAC_MIN (DAC_0dB - DAC_RES) + + +static void wm_set_vol(struct snd_ice1712 *ice, unsigned int index, + unsigned short vol, unsigned short master) +{ + unsigned char nvol; + + if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE)) + nvol = 0; + else { + nvol = (((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 128) + & WM_VOL_MAX; + nvol = (nvol ? (nvol + DAC_MIN) : 0) & 0xff; + } + + wm_put(ice, index, nvol); + wm_put_nocache(ice, index, 0x100 | nvol); +} + +static void wm8766_set_vol(struct snd_ice1712 *ice, unsigned int index, + unsigned short vol, unsigned short master) +{ + unsigned char nvol; + + if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE)) + nvol = 0; + else { + nvol = (((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 128) + & WM_VOL_MAX; + nvol = (nvol ? (nvol + DAC_MIN) : 0) & 0xff; + } + + wm8766_spi_write(ice, index, (0x0100 | nvol)); +} + + +/* + * DAC volume attenuation mixer control (-64dB to 0dB) + */ + +static int wm_dac_vol_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; /* mute */ + uinfo->value.integer.max = DAC_RES; /* 0dB, 0.5dB step */ + return 0; +} + +static int wm_dac_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct prodigy_hifi_spec *spec = ice->spec; + int i; + + for (i = 0; i < 2; i++) + ucontrol->value.integer.value[i] = + spec->vol[2 + i] & ~WM_VOL_MUTE; + return 0; +} + +static int wm_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct prodigy_hifi_spec *spec = ice->spec; + int i, idx, change = 0; + + mutex_lock(&ice->gpio_mutex); + for (i = 0; i < 2; i++) { + if (ucontrol->value.integer.value[i] != spec->vol[2 + i]) { + idx = WM_DAC_ATTEN_L + i; + spec->vol[2 + i] &= WM_VOL_MUTE; + spec->vol[2 + i] |= ucontrol->value.integer.value[i]; + wm_set_vol(ice, idx, spec->vol[2 + i], spec->master[i]); + change = 1; + } + } + mutex_unlock(&ice->gpio_mutex); + return change; +} + + +/* + * WM8766 DAC volume attenuation mixer control + */ +static int wm8766_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int voices = kcontrol->private_value >> 8; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = voices; + uinfo->value.integer.min = 0; /* mute */ + uinfo->value.integer.max = DAC_RES; /* 0dB */ + return 0; +} + +static int wm8766_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct prodigy_hifi_spec *spec = ice->spec; + int i, ofs, voices; + + voices = kcontrol->private_value >> 8; + ofs = kcontrol->private_value & 0xff; + for (i = 0; i < voices; i++) + ucontrol->value.integer.value[i] = spec->vol[ofs + i]; + return 0; +} + +static int wm8766_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct prodigy_hifi_spec *spec = ice->spec; + int i, idx, ofs, voices; + int change = 0; + + voices = kcontrol->private_value >> 8; + ofs = kcontrol->private_value & 0xff; + mutex_lock(&ice->gpio_mutex); + for (i = 0; i < voices; i++) { + if (ucontrol->value.integer.value[i] != spec->vol[ofs + i]) { + idx = WM8766_LDA1 + ofs + i; + spec->vol[ofs + i] &= WM_VOL_MUTE; + spec->vol[ofs + i] |= ucontrol->value.integer.value[i]; + wm8766_set_vol(ice, idx, + spec->vol[ofs + i], spec->master[i]); + change = 1; + } + } + mutex_unlock(&ice->gpio_mutex); + return change; +} + +/* + * Master volume attenuation mixer control / applied to WM8776+WM8766 + */ +static int wm_master_vol_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 = DAC_RES; + return 0; +} + +static int wm_master_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct prodigy_hifi_spec *spec = ice->spec; + int i; + for (i = 0; i < 2; i++) + ucontrol->value.integer.value[i] = spec->master[i]; + return 0; +} + +static int wm_master_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct prodigy_hifi_spec *spec = ice->spec; + int ch, change = 0; + + mutex_lock(&ice->gpio_mutex); + for (ch = 0; ch < 2; ch++) { + if (ucontrol->value.integer.value[ch] != spec->master[ch]) { + spec->master[ch] = ucontrol->value.integer.value[ch]; + + /* Apply to front DAC */ + wm_set_vol(ice, WM_DAC_ATTEN_L + ch, + spec->vol[2 + ch], spec->master[ch]); + + wm8766_set_vol(ice, WM8766_LDA1 + ch, + spec->vol[0 + ch], spec->master[ch]); + + wm8766_set_vol(ice, WM8766_LDA2 + ch, + spec->vol[4 + ch], spec->master[ch]); + + wm8766_set_vol(ice, WM8766_LDA3 + ch, + spec->vol[6 + ch], spec->master[ch]); + change = 1; + } + } + mutex_unlock(&ice->gpio_mutex); + return change; +} + + +/* KONSTI */ + +static int wm_adc_mux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char* texts[32] = { + "NULL", WM_AIN1, WM_AIN2, WM_AIN1 "+" WM_AIN2, + WM_AIN3, WM_AIN1 "+" WM_AIN3, WM_AIN2 "+" WM_AIN3, + WM_AIN1 "+" WM_AIN2 "+" WM_AIN3, + WM_AIN4, WM_AIN1 "+" WM_AIN4, WM_AIN2 "+" WM_AIN4, + WM_AIN1 "+" WM_AIN2 "+" WM_AIN4, + WM_AIN3 "+" WM_AIN4, WM_AIN1 "+" WM_AIN3 "+" WM_AIN4, + WM_AIN2 "+" WM_AIN3 "+" WM_AIN4, + WM_AIN1 "+" WM_AIN2 "+" WM_AIN3 "+" WM_AIN4, + WM_AIN5, WM_AIN1 "+" WM_AIN5, WM_AIN2 "+" WM_AIN5, + WM_AIN1 "+" WM_AIN2 "+" WM_AIN5, + WM_AIN3 "+" WM_AIN5, WM_AIN1 "+" WM_AIN3 "+" WM_AIN5, + WM_AIN2 "+" WM_AIN3 "+" WM_AIN5, + WM_AIN1 "+" WM_AIN2 "+" WM_AIN3 "+" WM_AIN5, + WM_AIN4 "+" WM_AIN5, WM_AIN1 "+" WM_AIN4 "+" WM_AIN5, + WM_AIN2 "+" WM_AIN4 "+" WM_AIN5, + WM_AIN1 "+" WM_AIN2 "+" WM_AIN4 "+" WM_AIN5, + WM_AIN3 "+" WM_AIN4 "+" WM_AIN5, + WM_AIN1 "+" WM_AIN3 "+" WM_AIN4 "+" WM_AIN5, + WM_AIN2 "+" WM_AIN3 "+" WM_AIN4 "+" WM_AIN5, + WM_AIN1 "+" WM_AIN2 "+" WM_AIN3 "+" WM_AIN4 "+" WM_AIN5 + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 32; + if (uinfo->value.enumerated.item > 31) + uinfo->value.enumerated.item = 31; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; +} + +static int wm_adc_mux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + + mutex_lock(&ice->gpio_mutex); + ucontrol->value.integer.value[0] = wm_get(ice, WM_ADC_MUX) & 0x1f; + mutex_unlock(&ice->gpio_mutex); + return 0; +} + +static int wm_adc_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned short oval, nval; + int change = 0; + + mutex_lock(&ice->gpio_mutex); + oval = wm_get(ice, WM_ADC_MUX); + nval = (oval & 0xe0) | ucontrol->value.integer.value[0]; + if (nval != oval) { + wm_put(ice, WM_ADC_MUX, nval); + change = 1; + } + mutex_unlock(&ice->gpio_mutex); + return change; +} + +/* KONSTI */ + +/* + * ADC gain mixer control (-64dB to 0dB) + */ + +#define ADC_0dB 0xcf +#define ADC_RES 128 +#define ADC_MIN (ADC_0dB - ADC_RES) + +static int wm_adc_vol_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; /* mute (-64dB) */ + uinfo->value.integer.max = ADC_RES; /* 0dB, 0.5dB step */ + return 0; +} + +static int wm_adc_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned short val; + int i; + + mutex_lock(&ice->gpio_mutex); + for (i = 0; i < 2; i++) { + val = wm_get(ice, WM_ADC_ATTEN_L + i) & 0xff; + val = val > ADC_MIN ? (val - ADC_MIN) : 0; + ucontrol->value.integer.value[i] = val; + } + mutex_unlock(&ice->gpio_mutex); + return 0; +} + +static int wm_adc_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned short ovol, nvol; + int i, idx, change = 0; + + mutex_lock(&ice->gpio_mutex); + for (i = 0; i < 2; i++) { + nvol = ucontrol->value.integer.value[i]; + nvol = nvol ? (nvol + ADC_MIN) : 0; + idx = WM_ADC_ATTEN_L + i; + ovol = wm_get(ice, idx) & 0xff; + if (ovol != nvol) { + wm_put(ice, idx, nvol); + change = 1; + } + } + mutex_unlock(&ice->gpio_mutex); + return change; +} + +/* + * ADC input mux mixer control + */ +#define wm_adc_mux_info snd_ctl_boolean_mono_info + +static int wm_adc_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + int bit = kcontrol->private_value; + + mutex_lock(&ice->gpio_mutex); + ucontrol->value.integer.value[0] = + (wm_get(ice, WM_ADC_MUX) & (1 << bit)) ? 1 : 0; + mutex_unlock(&ice->gpio_mutex); + return 0; +} + +static int wm_adc_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + int bit = kcontrol->private_value; + unsigned short oval, nval; + int change; + + mutex_lock(&ice->gpio_mutex); + nval = oval = wm_get(ice, WM_ADC_MUX); + if (ucontrol->value.integer.value[0]) + nval |= (1 << bit); + else + nval &= ~(1 << bit); + change = nval != oval; + if (change) { + wm_put(ice, WM_ADC_MUX, nval); + } + mutex_unlock(&ice->gpio_mutex); + return 0; +} + +/* + * Analog bypass (In -> Out) + */ +#define wm_bypass_info snd_ctl_boolean_mono_info + +static int wm_bypass_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + + mutex_lock(&ice->gpio_mutex); + ucontrol->value.integer.value[0] = + (wm_get(ice, WM_OUT_MUX) & 0x04) ? 1 : 0; + mutex_unlock(&ice->gpio_mutex); + return 0; +} + +static int wm_bypass_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned short val, oval; + int change = 0; + + mutex_lock(&ice->gpio_mutex); + val = oval = wm_get(ice, WM_OUT_MUX); + if (ucontrol->value.integer.value[0]) + val |= 0x04; + else + val &= ~0x04; + if (val != oval) { + wm_put(ice, WM_OUT_MUX, val); + change = 1; + } + mutex_unlock(&ice->gpio_mutex); + return change; +} + +/* + * Left/Right swap + */ +#define wm_chswap_info snd_ctl_boolean_mono_info + +static int wm_chswap_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + + mutex_lock(&ice->gpio_mutex); + ucontrol->value.integer.value[0] = + (wm_get(ice, WM_DAC_CTRL1) & 0xf0) != 0x90; + mutex_unlock(&ice->gpio_mutex); + return 0; +} + +static int wm_chswap_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + unsigned short val, oval; + int change = 0; + + mutex_lock(&ice->gpio_mutex); + oval = wm_get(ice, WM_DAC_CTRL1); + val = oval & 0x0f; + if (ucontrol->value.integer.value[0]) + val |= 0x60; + else + val |= 0x90; + if (val != oval) { + wm_put(ice, WM_DAC_CTRL1, val); + wm_put_nocache(ice, WM_DAC_CTRL1, val); + change = 1; + } + mutex_unlock(&ice->gpio_mutex); + return change; +} + + +/* + * mixers + */ + +static struct snd_kcontrol_new prodigy_hifi_controls[] __devinitdata = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "Master Playback Volume", + .info = wm_master_vol_info, + .get = wm_master_vol_get, + .put = wm_master_vol_put, + .tlv = { .p = db_scale_wm_dac } + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "Front Playback Volume", + .info = wm_dac_vol_info, + .get = wm_dac_vol_get, + .put = wm_dac_vol_put, + .tlv = { .p = db_scale_wm_dac }, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "Rear Playback Volume", + .info = wm8766_vol_info, + .get = wm8766_vol_get, + .put = wm8766_vol_put, + .private_value = (2 << 8) | 0, + .tlv = { .p = db_scale_wm_dac }, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "Center Playback Volume", + .info = wm8766_vol_info, + .get = wm8766_vol_get, + .put = wm8766_vol_put, + .private_value = (1 << 8) | 4, + .tlv = { .p = db_scale_wm_dac } + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "LFE Playback Volume", + .info = wm8766_vol_info, + .get = wm8766_vol_get, + .put = wm8766_vol_put, + .private_value = (1 << 8) | 5, + .tlv = { .p = db_scale_wm_dac } + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "Side Playback Volume", + .info = wm8766_vol_info, + .get = wm8766_vol_get, + .put = wm8766_vol_put, + .private_value = (2 << 8) | 6, + .tlv = { .p = db_scale_wm_dac }, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "Capture Volume", + .info = wm_adc_vol_info, + .get = wm_adc_vol_get, + .put = wm_adc_vol_put, + .tlv = { .p = db_scale_wm_dac }, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "CD Capture Switch", + .info = wm_adc_mux_info, + .get = wm_adc_mux_get, + .put = wm_adc_mux_put, + .private_value = 0, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Line Capture Switch", + .info = wm_adc_mux_info, + .get = wm_adc_mux_get, + .put = wm_adc_mux_put, + .private_value = 1, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Bypass Switch", + .info = wm_bypass_info, + .get = wm_bypass_get, + .put = wm_bypass_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Swap Output Channels", + .info = wm_chswap_info, + .get = wm_chswap_get, + .put = wm_chswap_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Capture Source", + .info = wm_adc_mux_enum_info, + .get = wm_adc_mux_enum_get, + .put = wm_adc_mux_enum_put, + }, +}; + +/* + * WM codec registers + */ +static void wm_proc_regs_write(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_ice1712 *ice = entry->private_data; + char line[64]; + unsigned int reg, val; + mutex_lock(&ice->gpio_mutex); + while (!snd_info_get_line(buffer, line, sizeof(line))) { + if (sscanf(line, "%x %x", ®, &val) != 2) + continue; + if (reg <= 0x17 && val <= 0xffff) + wm_put(ice, reg, val); + } + mutex_unlock(&ice->gpio_mutex); +} + +static void wm_proc_regs_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_ice1712 *ice = entry->private_data; + int reg, val; + + mutex_lock(&ice->gpio_mutex); + for (reg = 0; reg <= 0x17; reg++) { + val = wm_get(ice, reg); + snd_iprintf(buffer, "%02x = %04x\n", reg, val); + } + mutex_unlock(&ice->gpio_mutex); +} + +static void wm_proc_init(struct snd_ice1712 *ice) +{ + struct snd_info_entry *entry; + if (!snd_card_proc_new(ice->card, "wm_codec", &entry)) { + snd_info_set_text_ops(entry, ice, wm_proc_regs_read); + entry->mode |= S_IWUSR; + entry->c.text.write = wm_proc_regs_write; + } +} + +static int __devinit prodigy_hifi_add_controls(struct snd_ice1712 *ice) +{ + unsigned int i; + int err; + + for (i = 0; i < ARRAY_SIZE(prodigy_hifi_controls); i++) { + err = snd_ctl_add(ice->card, + snd_ctl_new1(&prodigy_hifi_controls[i], ice)); + if (err < 0) + return err; + } + + wm_proc_init(ice); + + return 0; +} + +static int __devinit prodigy_hd2_add_controls(struct snd_ice1712 *ice) +{ + unsigned int i; + int err; + + for (i = 0; i < ARRAY_SIZE(prodigy_hd2_controls); i++) { + err = snd_ctl_add(ice->card, + snd_ctl_new1(&prodigy_hd2_controls[i], ice)); + if (err < 0) + return err; + } + + wm_proc_init(ice); + + return 0; +} + + +/* + * initialize the chip + */ +static int __devinit prodigy_hifi_init(struct snd_ice1712 *ice) +{ + static unsigned short wm_inits[] = { + /* These come first to reduce init pop noise */ + WM_ADC_MUX, 0x0003, /* ADC mute */ + /* 0x00c0 replaced by 0x0003 */ + + WM_DAC_MUTE, 0x0001, /* DAC softmute */ + WM_DAC_CTRL1, 0x0000, /* DAC mute */ + + WM_POWERDOWN, 0x0008, /* All power-up except HP */ + WM_RESET, 0x0000, /* reset */ + }; + static unsigned short wm_inits2[] = { + WM_MASTER_CTRL, 0x0022, /* 256fs, slave mode */ + WM_DAC_INT, 0x0022, /* I2S, normal polarity, 24bit */ + WM_ADC_INT, 0x0022, /* I2S, normal polarity, 24bit */ + WM_DAC_CTRL1, 0x0090, /* DAC L/R */ + WM_OUT_MUX, 0x0001, /* OUT DAC */ + WM_HP_ATTEN_L, 0x0179, /* HP 0dB */ + WM_HP_ATTEN_R, 0x0179, /* HP 0dB */ + WM_DAC_ATTEN_L, 0x0000, /* DAC 0dB */ + WM_DAC_ATTEN_L, 0x0100, /* DAC 0dB */ + WM_DAC_ATTEN_R, 0x0000, /* DAC 0dB */ + WM_DAC_ATTEN_R, 0x0100, /* DAC 0dB */ + WM_PHASE_SWAP, 0x0000, /* phase normal */ +#if 0 + WM_DAC_MASTER, 0x0100, /* DAC master muted */ +#endif + WM_DAC_CTRL2, 0x0000, /* no deemphasis, no ZFLG */ + WM_ADC_ATTEN_L, 0x0000, /* ADC muted */ + WM_ADC_ATTEN_R, 0x0000, /* ADC muted */ +#if 1 + WM_ALC_CTRL1, 0x007b, /* */ + WM_ALC_CTRL2, 0x0000, /* */ + WM_ALC_CTRL3, 0x0000, /* */ + WM_NOISE_GATE, 0x0000, /* */ +#endif + WM_DAC_MUTE, 0x0000, /* DAC unmute */ + WM_ADC_MUX, 0x0003, /* ADC unmute, both CD/Line On */ + }; + static unsigned short wm8766_inits[] = { + WM8766_RESET, 0x0000, + WM8766_DAC_CTRL, 0x0120, + WM8766_INT_CTRL, 0x0022, /* I2S Normal Mode, 24 bit */ + WM8766_DAC_CTRL2, 0x0001, + WM8766_DAC_CTRL3, 0x0080, + WM8766_LDA1, 0x0100, + WM8766_LDA2, 0x0100, + WM8766_LDA3, 0x0100, + WM8766_RDA1, 0x0100, + WM8766_RDA2, 0x0100, + WM8766_RDA3, 0x0100, + WM8766_MUTE1, 0x0000, + WM8766_MUTE2, 0x0000, + }; + + struct prodigy_hifi_spec *spec; + unsigned int i; + + ice->vt1720 = 0; + ice->vt1724 = 1; + + ice->num_total_dacs = 8; + ice->num_total_adcs = 1; + + /* HACK - use this as the SPDIF source. + * don't call snd_ice1712_gpio_get/put(), otherwise it's overwritten + */ + ice->gpio.saved[0] = 0; + /* to remeber the register values */ + + ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); + if (! ice->akm) + return -ENOMEM; + ice->akm_codecs = 1; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + ice->spec = spec; + + /* initialize WM8776 codec */ + for (i = 0; i < ARRAY_SIZE(wm_inits); i += 2) + wm_put(ice, wm_inits[i], wm_inits[i+1]); + schedule_timeout_uninterruptible(1); + for (i = 0; i < ARRAY_SIZE(wm_inits2); i += 2) + wm_put(ice, wm_inits2[i], wm_inits2[i+1]); + + /* initialize WM8766 codec */ + for (i = 0; i < ARRAY_SIZE(wm8766_inits); i += 2) + wm8766_spi_write(ice, wm8766_inits[i], wm8766_inits[i+1]); + + + return 0; +} + + +/* + * initialize the chip + */ +static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice) +{ + static unsigned short ak4396_inits[] = { + AK4396_CTRL1, 0x87, /* I2S Normal Mode, 24 bit */ + AK4396_CTRL2, 0x02, + AK4396_CTRL3, 0x00, + AK4396_LCH_ATT, 0x00, + AK4396_RCH_ATT, 0x00, + }; + + struct prodigy_hifi_spec *spec; + unsigned int i; + + ice->vt1720 = 0; + ice->vt1724 = 1; + + ice->num_total_dacs = 1; + ice->num_total_adcs = 1; + + /* HACK - use this as the SPDIF source. + * don't call snd_ice1712_gpio_get/put(), otherwise it's overwritten + */ + ice->gpio.saved[0] = 0; + /* to remeber the register values */ + + ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); + if (! ice->akm) + return -ENOMEM; + ice->akm_codecs = 1; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + ice->spec = spec; + + /* initialize ak4396 codec */ + /* reset codec */ + ak4396_write(ice, AK4396_CTRL1, 0x86); + msleep(100); + ak4396_write(ice, AK4396_CTRL1, 0x87); + + for (i = 0; i < ARRAY_SIZE(ak4396_inits); i += 2) + ak4396_write(ice, ak4396_inits[i], ak4396_inits[i+1]); + + return 0; +} + + +static unsigned char prodigy71hifi_eeprom[] __devinitdata = { + 0x4b, /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */ + 0x80, /* ACLINK: I2S */ + 0xfc, /* I2S: vol, 96k, 24bit, 192k */ + 0xc3, /* SPDIF: out-en, out-int, spdif-in */ + 0xff, /* GPIO_DIR */ + 0xff, /* GPIO_DIR1 */ + 0x5f, /* GPIO_DIR2 */ + 0x00, /* GPIO_MASK */ + 0x00, /* GPIO_MASK1 */ + 0x00, /* GPIO_MASK2 */ + 0x00, /* GPIO_STATE */ + 0x00, /* GPIO_STATE1 */ + 0x00, /* GPIO_STATE2 */ +}; + +static unsigned char prodigyhd2_eeprom[] __devinitdata = { + 0x4b, /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */ + 0x80, /* ACLINK: I2S */ + 0xfc, /* I2S: vol, 96k, 24bit, 192k */ + 0xc3, /* SPDIF: out-en, out-int, spdif-in */ + 0xff, /* GPIO_DIR */ + 0xff, /* GPIO_DIR1 */ + 0x5f, /* GPIO_DIR2 */ + 0x00, /* GPIO_MASK */ + 0x00, /* GPIO_MASK1 */ + 0x00, /* GPIO_MASK2 */ + 0x00, /* GPIO_STATE */ + 0x00, /* GPIO_STATE1 */ + 0x00, /* GPIO_STATE2 */ +}; + +static unsigned char fortissimo4_eeprom[] __devinitdata = { + 0x43, /* SYSCONF: clock 512, ADC, 4DACs */ + 0x80, /* ACLINK: I2S */ + 0xfc, /* I2S: vol, 96k, 24bit, 192k */ + 0xc1, /* SPDIF: out-en, out-int */ + 0xff, /* GPIO_DIR */ + 0xff, /* GPIO_DIR1 */ + 0x5f, /* GPIO_DIR2 */ + 0x00, /* GPIO_MASK */ + 0x00, /* GPIO_MASK1 */ + 0x00, /* GPIO_MASK2 */ + 0x00, /* GPIO_STATE */ + 0x00, /* GPIO_STATE1 */ + 0x00, /* GPIO_STATE2 */ +}; + +/* entry point */ +struct snd_ice1712_card_info snd_vt1724_prodigy_hifi_cards[] __devinitdata = { + { + .subvendor = VT1724_SUBDEVICE_PRODIGY_HIFI, + .name = "Audiotrak Prodigy 7.1 HiFi", + .model = "prodigy71hifi", + .chip_init = prodigy_hifi_init, + .build_controls = prodigy_hifi_add_controls, + .eeprom_size = sizeof(prodigy71hifi_eeprom), + .eeprom_data = prodigy71hifi_eeprom, + .driver = "Prodigy71HIFI", + }, + { + .subvendor = VT1724_SUBDEVICE_PRODIGY_HD2, + .name = "Audiotrak Prodigy HD2", + .model = "prodigyhd2", + .chip_init = prodigy_hd2_init, + .build_controls = prodigy_hd2_add_controls, + .eeprom_size = sizeof(prodigyhd2_eeprom), + .eeprom_data = prodigyhd2_eeprom, + .driver = "Prodigy71HD2", + }, + { + .subvendor = VT1724_SUBDEVICE_FORTISSIMO4, + .name = "Hercules Fortissimo IV", + .model = "fortissimo4", + .chip_init = prodigy_hifi_init, + .build_controls = prodigy_hifi_add_controls, + .eeprom_size = sizeof(fortissimo4_eeprom), + .eeprom_data = fortissimo4_eeprom, + .driver = "Fortissimo4", + }, + { } /* terminator */ +}; + diff --git a/sound/pci/ice1712/prodigy_hifi.h b/sound/pci/ice1712/prodigy_hifi.h new file mode 100644 index 0000000..a4415d4 --- /dev/null +++ b/sound/pci/ice1712/prodigy_hifi.h @@ -0,0 +1,38 @@ +#ifndef __SOUND_PRODIGY_HIFI_H +#define __SOUND_PRODIGY_HIFI_H + +/* + * ALSA driver for VIA VT1724 (Envy24HT) + * + * Lowlevel functions for Audiotrak Prodigy Hifi + * + * Copyright (c) 2004 Takashi Iwai <tiwai@suse.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 + * + */ + +#define PRODIGY_HIFI_DEVICE_DESC "{Audiotrak,Prodigy 7.1 HIFI},"\ + "{Audiotrak Prodigy HD2},"\ + "{Hercules Fortissimo IV}," + +#define VT1724_SUBDEVICE_PRODIGY_HIFI 0x38315441 /* PRODIGY 7.1 HIFI */ +#define VT1724_SUBDEVICE_PRODIGY_HD2 0x37315441 /* PRODIGY HD2 */ +#define VT1724_SUBDEVICE_FORTISSIMO4 0x81160100 /* Fortissimo IV */ + + +extern struct snd_ice1712_card_info snd_vt1724_prodigy_hifi_cards[]; + +#endif /* __SOUND_PRODIGY_HIFI_H */ diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c index d18a31e..ddd5fc8 100644 --- a/sound/pci/ice1712/revo.c +++ b/sound/pci/ice1712/revo.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -33,6 +32,12 @@ #include "envy24ht.h" #include "revo.h" +/* a non-standard I2C device for revo51 */ +struct revo51_spec { + struct snd_i2c_device *dev; + struct snd_pt2258 *pt2258; +} revo51; + static void revo_i2s_mclk_changed(struct snd_ice1712 *ice) { /* assert PRST# to converters; MT05 bit 7 */ @@ -153,8 +158,14 @@ static struct snd_i2c_bit_ops revo51_bit_ops = { static int revo51_i2c_init(struct snd_ice1712 *ice, struct snd_pt2258 *pt) { + struct revo51_spec *spec; int err; + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + ice->spec = spec; + /* create the I2C bus */ err = snd_i2c_bus_create(ice->card, "ICE1724 GPIO6", NULL, &ice->i2c); if (err < 0) @@ -164,15 +175,14 @@ static int revo51_i2c_init(struct snd_ice1712 *ice, ice->i2c->hw_ops.bit = &revo51_bit_ops; /* create the I2C device */ - err = snd_i2c_device_create(ice->i2c, "PT2258", 0x40, - &ice->spec.revo51.dev); + err = snd_i2c_device_create(ice->i2c, "PT2258", 0x40, &spec->dev); if (err < 0) return err; pt->card = ice->card; pt->i2c_bus = ice->i2c; - pt->i2c_dev = ice->spec.revo51.dev; - ice->spec.revo51.pt2258 = pt; + pt->i2c_dev = spec->dev; + spec->pt2258 = pt; snd_pt2258_reset(pt); @@ -556,6 +566,7 @@ static int __devinit revo_init(struct snd_ice1712 *ice) static int __devinit revo_add_controls(struct snd_ice1712 *ice) { + struct revo51_spec *spec; int err; switch (ice->eeprom.subvendor) { @@ -568,7 +579,8 @@ static int __devinit revo_add_controls(struct snd_ice1712 *ice) err = snd_ice1712_akm4xxx_build_controls(ice); if (err < 0) return err; - err = snd_pt2258_build_controls(ice->spec.revo51.pt2258); + spec = ice->spec; + err = snd_pt2258_build_controls(spec->pt2258); if (err < 0) return err; break; diff --git a/sound/pci/ice1712/se.c b/sound/pci/ice1712/se.c new file mode 100644 index 0000000..69673b9 --- /dev/null +++ b/sound/pci/ice1712/se.c @@ -0,0 +1,774 @@ +/* + * ALSA driver for ICEnsemble VT1724 (Envy24HT) + * + * Lowlevel functions for ONKYO WAVIO SE-90PCI and SE-200PCI + * + * Copyright (c) 2007 Shin-ya Okada sh_okada(at)d4.dion.ne.jp + * (at) -> @ + * + * 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 <asm/io.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/tlv.h> + +#include "ice1712.h" +#include "envy24ht.h" +#include "se.h" + +struct se_spec { + struct { + unsigned char ch1, ch2; + } vol[8]; +}; + +/****************************************************************************/ +/* ONKYO WAVIO SE-200PCI */ +/****************************************************************************/ +/* + * system configuration ICE_EEP2_SYSCONF=0x4b + * XIN1 49.152MHz + * not have UART + * one stereo ADC and a S/PDIF receiver connected + * four stereo DACs connected + * + * AC-Link configuration ICE_EEP2_ACLINK=0x80 + * use I2C, not use AC97 + * + * I2S converters feature ICE_EEP2_I2S=0x78 + * I2S codec has no volume/mute control feature + * I2S codec supports 96KHz and 192KHz + * I2S codec 24bits + * + * S/PDIF configuration ICE_EEP2_SPDIF=0xc3 + * Enable integrated S/PDIF transmitter + * internal S/PDIF out implemented + * S/PDIF is stereo + * External S/PDIF out implemented + * + * + * ** connected chips ** + * + * WM8740 + * A 2ch-DAC of main outputs. + * It setuped as I2S mode by wire, so no way to setup from software. + * The sample-rate are automatically changed. + * ML/I2S (28pin) --------+ + * MC/DM1 (27pin) -- 5V | + * MD/DM0 (26pin) -- GND | + * MUTEB (25pin) -- NC | + * MODE (24pin) -- GND | + * CSBIW (23pin) --------+ + * | + * RSTB (22pin) --R(1K)-+ + * Probably it reduce the noise from the control line. + * + * WM8766 + * A 6ch-DAC for surrounds. + * It's control wire was connected to GPIOxx (3-wire serial interface) + * ML/I2S (11pin) -- GPIO18 + * MC/IWL (12pin) -- GPIO17 + * MD/DM (13pin) -- GPIO16 + * MUTE (14pin) -- GPIO01 + * + * WM8776 + * A 2ch-ADC(with 10ch-selector) plus 2ch-DAC. + * It's control wire was connected to SDA/SCLK (2-wire serial interface) + * MODE (16pin) -- R(1K) -- GND + * CE (17pin) -- R(1K) -- GND 2-wire mode (address=0x34) + * DI (18pin) -- SDA + * CL (19pin) -- SCLK + * + * + * ** output pins and device names ** + * + * 7.1ch name -- output connector color -- device (-D option) + * + * FRONT 2ch -- green -- plughw:0,0 + * CENTER(Lch) SUBWOOFER(Rch) -- black -- plughw:0,2,0 + * SURROUND 2ch -- orange -- plughw:0,2,1 + * SURROUND BACK 2ch -- white -- plughw:0,2,2 + * + */ + + +/****************************************************************************/ +/* WM8740 interface */ +/****************************************************************************/ + +static void __devinit se200pci_WM8740_init(struct snd_ice1712 *ice) +{ + /* nothing to do */ +} + + +static void se200pci_WM8740_set_pro_rate(struct snd_ice1712 *ice, + unsigned int rate) +{ + /* nothing to do */ +} + + +/****************************************************************************/ +/* WM8766 interface */ +/****************************************************************************/ + +static void se200pci_WM8766_write(struct snd_ice1712 *ice, + unsigned int addr, unsigned int data) +{ + unsigned int st; + unsigned int bits; + int i; + const unsigned int DATA = 0x010000; + const unsigned int CLOCK = 0x020000; + const unsigned int LOAD = 0x040000; + const unsigned int ALL_MASK = (DATA | CLOCK | LOAD); + + snd_ice1712_save_gpio_status(ice); + + st = ((addr & 0x7f) << 9) | (data & 0x1ff); + snd_ice1712_gpio_set_dir(ice, ice->gpio.direction | ALL_MASK); + snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask & ~ALL_MASK); + bits = snd_ice1712_gpio_read(ice) & ~ALL_MASK; + + snd_ice1712_gpio_write(ice, bits); + for (i = 0; i < 16; i++) { + udelay(1); + bits &= ~CLOCK; + st = (st << 1); + if (st & 0x10000) + bits |= DATA; + else + bits &= ~DATA; + + snd_ice1712_gpio_write(ice, bits); + + udelay(1); + bits |= CLOCK; + snd_ice1712_gpio_write(ice, bits); + } + + udelay(1); + bits |= LOAD; + snd_ice1712_gpio_write(ice, bits); + + udelay(1); + bits |= (DATA | CLOCK); + snd_ice1712_gpio_write(ice, bits); + + snd_ice1712_restore_gpio_status(ice); +} + +static void se200pci_WM8766_set_volume(struct snd_ice1712 *ice, int ch, + unsigned int vol1, unsigned int vol2) +{ + switch (ch) { + case 0: + se200pci_WM8766_write(ice, 0x000, vol1); + se200pci_WM8766_write(ice, 0x001, vol2 | 0x100); + break; + case 1: + se200pci_WM8766_write(ice, 0x004, vol1); + se200pci_WM8766_write(ice, 0x005, vol2 | 0x100); + break; + case 2: + se200pci_WM8766_write(ice, 0x006, vol1); + se200pci_WM8766_write(ice, 0x007, vol2 | 0x100); + break; + } +} + +static void __devinit se200pci_WM8766_init(struct snd_ice1712 *ice) +{ + se200pci_WM8766_write(ice, 0x1f, 0x000); /* RESET ALL */ + udelay(10); + + se200pci_WM8766_set_volume(ice, 0, 0, 0); /* volume L=0 R=0 */ + se200pci_WM8766_set_volume(ice, 1, 0, 0); /* volume L=0 R=0 */ + se200pci_WM8766_set_volume(ice, 2, 0, 0); /* volume L=0 R=0 */ + + se200pci_WM8766_write(ice, 0x03, 0x022); /* serial mode I2S-24bits */ + se200pci_WM8766_write(ice, 0x0a, 0x080); /* MCLK=256fs */ + se200pci_WM8766_write(ice, 0x12, 0x000); /* MDP=0 */ + se200pci_WM8766_write(ice, 0x15, 0x000); /* MDP=0 */ + se200pci_WM8766_write(ice, 0x09, 0x000); /* demp=off mute=off */ + + se200pci_WM8766_write(ice, 0x02, 0x124); /* ch-assign L=L R=R RESET */ + se200pci_WM8766_write(ice, 0x02, 0x120); /* ch-assign L=L R=R */ +} + +static void se200pci_WM8766_set_pro_rate(struct snd_ice1712 *ice, + unsigned int rate) +{ + if (rate > 96000) + se200pci_WM8766_write(ice, 0x0a, 0x000); /* MCLK=128fs */ + else + se200pci_WM8766_write(ice, 0x0a, 0x080); /* MCLK=256fs */ +} + + +/****************************************************************************/ +/* WM8776 interface */ +/****************************************************************************/ + +static void se200pci_WM8776_write(struct snd_ice1712 *ice, + unsigned int addr, unsigned int data) +{ + unsigned int val; + + val = (addr << 9) | data; + snd_vt1724_write_i2c(ice, 0x34, val >> 8, val & 0xff); +} + + +static void se200pci_WM8776_set_output_volume(struct snd_ice1712 *ice, + unsigned int vol1, unsigned int vol2) +{ + se200pci_WM8776_write(ice, 0x03, vol1); + se200pci_WM8776_write(ice, 0x04, vol2 | 0x100); +} + +static void se200pci_WM8776_set_input_volume(struct snd_ice1712 *ice, + unsigned int vol1, unsigned int vol2) +{ + se200pci_WM8776_write(ice, 0x0e, vol1); + se200pci_WM8776_write(ice, 0x0f, vol2 | 0x100); +} + +static const char *se200pci_sel[] = { + "LINE-IN", "CD-IN", "MIC-IN", "ALL-MIX", NULL +}; + +static void se200pci_WM8776_set_input_selector(struct snd_ice1712 *ice, + unsigned int sel) +{ + static unsigned char vals[] = { + /* LINE, CD, MIC, ALL, GND */ + 0x10, 0x04, 0x08, 0x1c, 0x03 + }; + if (sel > 4) + sel = 4; + se200pci_WM8776_write(ice, 0x15, vals[sel]); +} + +static void se200pci_WM8776_set_afl(struct snd_ice1712 *ice, unsigned int afl) +{ + /* AFL -- After Fader Listening */ + if (afl) + se200pci_WM8776_write(ice, 0x16, 0x005); + else + se200pci_WM8776_write(ice, 0x16, 0x001); +} + +static const char *se200pci_agc[] = { + "Off", "LimiterMode", "ALCMode", NULL +}; + +static void se200pci_WM8776_set_agc(struct snd_ice1712 *ice, unsigned int agc) +{ + /* AGC -- Auto Gain Control of the input */ + switch (agc) { + case 0: + se200pci_WM8776_write(ice, 0x11, 0x000); /* Off */ + break; + case 1: + se200pci_WM8776_write(ice, 0x10, 0x07b); + se200pci_WM8776_write(ice, 0x11, 0x100); /* LimiterMode */ + break; + case 2: + se200pci_WM8776_write(ice, 0x10, 0x1fb); + se200pci_WM8776_write(ice, 0x11, 0x100); /* ALCMode */ + break; + } +} + +static void __devinit se200pci_WM8776_init(struct snd_ice1712 *ice) +{ + int i; + static unsigned short __devinitdata default_values[] = { + 0x100, 0x100, 0x100, + 0x100, 0x100, 0x100, + 0x000, 0x090, 0x000, 0x000, + 0x022, 0x022, 0x022, + 0x008, 0x0cf, 0x0cf, 0x07b, 0x000, + 0x032, 0x000, 0x0a6, 0x001, 0x001 + }; + + se200pci_WM8776_write(ice, 0x17, 0x000); /* reset all */ + /* ADC and DAC interface is I2S 24bits mode */ + /* The sample-rate are automatically changed */ + udelay(10); + /* BUT my board can not do reset all, so I load all by manually. */ + for (i = 0; i < ARRAY_SIZE(default_values); i++) + se200pci_WM8776_write(ice, i, default_values[i]); + + se200pci_WM8776_set_input_selector(ice, 0); + se200pci_WM8776_set_afl(ice, 0); + se200pci_WM8776_set_agc(ice, 0); + se200pci_WM8776_set_input_volume(ice, 0, 0); + se200pci_WM8776_set_output_volume(ice, 0, 0); + + /* head phone mute and power down */ + se200pci_WM8776_write(ice, 0x00, 0); + se200pci_WM8776_write(ice, 0x01, 0); + se200pci_WM8776_write(ice, 0x02, 0x100); + se200pci_WM8776_write(ice, 0x0d, 0x080); +} + +static void se200pci_WM8776_set_pro_rate(struct snd_ice1712 *ice, + unsigned int rate) +{ + /* nothing to do */ +} + + +/****************************************************************************/ +/* runtime interface */ +/****************************************************************************/ + +static void se200pci_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate) +{ + se200pci_WM8740_set_pro_rate(ice, rate); + se200pci_WM8766_set_pro_rate(ice, rate); + se200pci_WM8776_set_pro_rate(ice, rate); +} + +struct se200pci_control { + char *name; + enum { + WM8766, + WM8776in, + WM8776out, + WM8776sel, + WM8776agc, + WM8776afl + } target; + enum { VOLUME1, VOLUME2, BOOLEAN, ENUM } type; + int ch; + const char **member; + const char *comment; +}; + +static const struct se200pci_control se200pci_cont[] = { + { + .name = "Front Playback Volume", + .target = WM8776out, + .type = VOLUME1, + .comment = "Front(green)" + }, + { + .name = "Side Playback Volume", + .target = WM8766, + .type = VOLUME1, + .ch = 1, + .comment = "Surround(orange)" + }, + { + .name = "Surround Playback Volume", + .target = WM8766, + .type = VOLUME1, + .ch = 2, + .comment = "SurroundBack(white)" + }, + { + .name = "CLFE Playback Volume", + .target = WM8766, + .type = VOLUME1, + .ch = 0, + .comment = "Center(Lch)&SubWoofer(Rch)(black)" + }, + { + .name = "Capture Volume", + .target = WM8776in, + .type = VOLUME2 + }, + { + .name = "Capture Select", + .target = WM8776sel, + .type = ENUM, + .member = se200pci_sel + }, + { + .name = "AGC Capture Mode", + .target = WM8776agc, + .type = ENUM, + .member = se200pci_agc + }, + { + .name = "AFL Bypass Playback Switch", + .target = WM8776afl, + .type = BOOLEAN + } +}; + +static int se200pci_get_enum_count(int n) +{ + const char **member; + int c; + + member = se200pci_cont[n].member; + if (!member) + return 0; + for (c = 0; member[c]; c++) + ; + return c; +} + +static int se200pci_cont_volume_info(struct snd_kcontrol *kc, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; /* mute */ + uinfo->value.integer.max = 0xff; /* 0dB */ + return 0; +} + +#define se200pci_cont_boolean_info snd_ctl_boolean_mono_info + +static int se200pci_cont_enum_info(struct snd_kcontrol *kc, + struct snd_ctl_elem_info *uinfo) +{ + int n, c; + + n = kc->private_value; + c = se200pci_get_enum_count(n); + if (!c) + return -EINVAL; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = c; + if (uinfo->value.enumerated.item >= c) + uinfo->value.enumerated.item = c - 1; + strcpy(uinfo->value.enumerated.name, + se200pci_cont[n].member[uinfo->value.enumerated.item]); + return 0; +} + +static int se200pci_cont_volume_get(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *uc) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kc); + struct se_spec *spec = ice->spec; + int n = kc->private_value; + uc->value.integer.value[0] = spec->vol[n].ch1; + uc->value.integer.value[1] = spec->vol[n].ch2; + return 0; +} + +static int se200pci_cont_boolean_get(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *uc) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kc); + struct se_spec *spec = ice->spec; + int n = kc->private_value; + uc->value.integer.value[0] = spec->vol[n].ch1; + return 0; +} + +static int se200pci_cont_enum_get(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *uc) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kc); + struct se_spec *spec = ice->spec; + int n = kc->private_value; + uc->value.enumerated.item[0] = spec->vol[n].ch1; + return 0; +} + +static void se200pci_cont_update(struct snd_ice1712 *ice, int n) +{ + struct se_spec *spec = ice->spec; + switch (se200pci_cont[n].target) { + case WM8766: + se200pci_WM8766_set_volume(ice, + se200pci_cont[n].ch, + spec->vol[n].ch1, + spec->vol[n].ch2); + break; + + case WM8776in: + se200pci_WM8776_set_input_volume(ice, + spec->vol[n].ch1, + spec->vol[n].ch2); + break; + + case WM8776out: + se200pci_WM8776_set_output_volume(ice, + spec->vol[n].ch1, + spec->vol[n].ch2); + break; + + case WM8776sel: + se200pci_WM8776_set_input_selector(ice, + spec->vol[n].ch1); + break; + + case WM8776agc: + se200pci_WM8776_set_agc(ice, spec->vol[n].ch1); + break; + + case WM8776afl: + se200pci_WM8776_set_afl(ice, spec->vol[n].ch1); + break; + + default: + break; + } +} + +static int se200pci_cont_volume_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *uc) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kc); + struct se_spec *spec = ice->spec; + int n = kc->private_value; + unsigned int vol1, vol2; + int changed; + + changed = 0; + vol1 = uc->value.integer.value[0] & 0xff; + vol2 = uc->value.integer.value[1] & 0xff; + if (spec->vol[n].ch1 != vol1) { + spec->vol[n].ch1 = vol1; + changed = 1; + } + if (spec->vol[n].ch2 != vol2) { + spec->vol[n].ch2 = vol2; + changed = 1; + } + if (changed) + se200pci_cont_update(ice, n); + + return changed; +} + +static int se200pci_cont_boolean_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *uc) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kc); + struct se_spec *spec = ice->spec; + int n = kc->private_value; + unsigned int vol1; + + vol1 = !!uc->value.integer.value[0]; + if (spec->vol[n].ch1 != vol1) { + spec->vol[n].ch1 = vol1; + se200pci_cont_update(ice, n); + return 1; + } + return 0; +} + +static int se200pci_cont_enum_put(struct snd_kcontrol *kc, + struct snd_ctl_elem_value *uc) +{ + struct snd_ice1712 *ice = snd_kcontrol_chip(kc); + struct se_spec *spec = ice->spec; + int n = kc->private_value; + unsigned int vol1; + + vol1 = uc->value.enumerated.item[0]; + if (vol1 >= se200pci_get_enum_count(n)) + return -EINVAL; + if (spec->vol[n].ch1 != vol1) { + spec->vol[n].ch1 = vol1; + se200pci_cont_update(ice, n); + return 1; + } + return 0; +} + +static const DECLARE_TLV_DB_SCALE(db_scale_gain1, -12750, 50, 1); +static const DECLARE_TLV_DB_SCALE(db_scale_gain2, -10350, 50, 1); + +static int __devinit se200pci_add_controls(struct snd_ice1712 *ice) +{ + int i; + struct snd_kcontrol_new cont; + int err; + + memset(&cont, 0, sizeof(cont)); + cont.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + for (i = 0; i < ARRAY_SIZE(se200pci_cont); i++) { + cont.private_value = i; + cont.name = se200pci_cont[i].name; + cont.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + cont.tlv.p = NULL; + switch (se200pci_cont[i].type) { + case VOLUME1: + case VOLUME2: + cont.info = se200pci_cont_volume_info; + cont.get = se200pci_cont_volume_get; + cont.put = se200pci_cont_volume_put; + cont.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; + if (se200pci_cont[i].type == VOLUME1) + cont.tlv.p = db_scale_gain1; + else + cont.tlv.p = db_scale_gain2; + break; + case BOOLEAN: + cont.info = se200pci_cont_boolean_info; + cont.get = se200pci_cont_boolean_get; + cont.put = se200pci_cont_boolean_put; + break; + case ENUM: + cont.info = se200pci_cont_enum_info; + cont.get = se200pci_cont_enum_get; + cont.put = se200pci_cont_enum_put; + break; + default: + snd_BUG(); + return -EINVAL; + } + err = snd_ctl_add(ice->card, snd_ctl_new1(&cont, ice)); + if (err < 0) + return err; + } + + return 0; +} + + +/****************************************************************************/ +/* ONKYO WAVIO SE-90PCI */ +/****************************************************************************/ +/* + * system configuration ICE_EEP2_SYSCONF=0x4b + * AC-Link configuration ICE_EEP2_ACLINK=0x80 + * I2S converters feature ICE_EEP2_I2S=0x78 + * S/PDIF configuration ICE_EEP2_SPDIF=0xc3 + * + * ** connected chip ** + * + * WM8716 + * A 2ch-DAC of main outputs. + * It setuped as I2S mode by wire, so no way to setup from software. + * ML/I2S (28pin) -- +5V + * MC/DM1 (27pin) -- GND + * MC/DM0 (26pin) -- GND + * MUTEB (25pin) -- open (internal pull-up) + * MODE (24pin) -- GND + * CSBIWO (23pin) -- +5V + * + */ + + /* Nothing to do for this chip. */ + + +/****************************************************************************/ +/* probe/initialize/setup */ +/****************************************************************************/ + +static int __devinit se_init(struct snd_ice1712 *ice) +{ + struct se_spec *spec; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + ice->spec = spec; + + if (ice->eeprom.subvendor == VT1724_SUBDEVICE_SE90PCI) { + ice->num_total_dacs = 2; + ice->num_total_adcs = 0; + ice->vt1720 = 1; + return 0; + + } else if (ice->eeprom.subvendor == VT1724_SUBDEVICE_SE200PCI) { + ice->num_total_dacs = 8; + ice->num_total_adcs = 2; + se200pci_WM8740_init(ice); + se200pci_WM8766_init(ice); + se200pci_WM8776_init(ice); + ice->gpio.set_pro_rate = se200pci_set_pro_rate; + return 0; + } + + return -ENOENT; +} + +static int __devinit se_add_controls(struct snd_ice1712 *ice) +{ + int err; + + err = 0; + /* nothing to do for VT1724_SUBDEVICE_SE90PCI */ + if (ice->eeprom.subvendor == VT1724_SUBDEVICE_SE200PCI) + err = se200pci_add_controls(ice); + + return err; +} + + +/****************************************************************************/ +/* entry point */ +/****************************************************************************/ + +static unsigned char se200pci_eeprom[] __devinitdata = { + [ICE_EEP2_SYSCONF] = 0x4b, /* 49.152Hz, spdif-in/ADC, 4DACs */ + [ICE_EEP2_ACLINK] = 0x80, /* I2S */ + [ICE_EEP2_I2S] = 0x78, /* 96k-ok, 24bit, 192k-ok */ + [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */ + + [ICE_EEP2_GPIO_DIR] = 0x02, /* WM8766 mute 1=output */ + [ICE_EEP2_GPIO_DIR1] = 0x00, /* not used */ + [ICE_EEP2_GPIO_DIR2] = 0x07, /* WM8766 ML/MC/MD 1=output */ + + [ICE_EEP2_GPIO_MASK] = 0x00, /* 0=writable */ + [ICE_EEP2_GPIO_MASK1] = 0x00, /* 0=writable */ + [ICE_EEP2_GPIO_MASK2] = 0x00, /* 0=writable */ + + [ICE_EEP2_GPIO_STATE] = 0x00, /* WM8766 mute=0 */ + [ICE_EEP2_GPIO_STATE1] = 0x00, /* not used */ + [ICE_EEP2_GPIO_STATE2] = 0x07, /* WM8766 ML/MC/MD */ +}; + +static unsigned char se90pci_eeprom[] __devinitdata = { + [ICE_EEP2_SYSCONF] = 0x4b, /* 49.152Hz, spdif-in/ADC, 4DACs */ + [ICE_EEP2_ACLINK] = 0x80, /* I2S */ + [ICE_EEP2_I2S] = 0x78, /* 96k-ok, 24bit, 192k-ok */ + [ICE_EEP2_SPDIF] = 0xc3, /* out-en, out-int, spdif-in */ + + /* ALL GPIO bits are in input mode */ +}; + +struct snd_ice1712_card_info snd_vt1724_se_cards[] __devinitdata = { + { + .subvendor = VT1724_SUBDEVICE_SE200PCI, + .name = "ONKYO SE200PCI", + .model = "se200pci", + .chip_init = se_init, + .build_controls = se_add_controls, + .eeprom_size = sizeof(se200pci_eeprom), + .eeprom_data = se200pci_eeprom, + }, + { + .subvendor = VT1724_SUBDEVICE_SE90PCI, + .name = "ONKYO SE90PCI", + .model = "se90pci", + .chip_init = se_init, + .build_controls = se_add_controls, + .eeprom_size = sizeof(se90pci_eeprom), + .eeprom_data = se90pci_eeprom, + }, + {} /*terminator*/ +}; diff --git a/sound/pci/ice1712/se.h b/sound/pci/ice1712/se.h new file mode 100644 index 0000000..0b0a9da --- /dev/null +++ b/sound/pci/ice1712/se.h @@ -0,0 +1,15 @@ +#ifndef __SOUND_SE_H +#define __SOUND_SE_H + +/* ID */ +#define SE_DEVICE_DESC \ + "{ONKYO INC,SE-90PCI},"\ + "{ONKYO INC,SE-200PCI}," + +#define VT1724_SUBDEVICE_SE90PCI 0xb161000 +#define VT1724_SUBDEVICE_SE200PCI 0xb160100 + +/* entry struct */ +extern struct snd_ice1712_card_info snd_vt1724_se_cards[]; + +#endif /* __SOUND_SE_H */ diff --git a/sound/pci/ice1712/vt1720_mobo.c b/sound/pci/ice1712/vt1720_mobo.c index 2395241..7f9674b 100644 --- a/sound/pci/ice1712/vt1720_mobo.c +++ b/sound/pci/ice1712/vt1720_mobo.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/sound/pci/ice1712/wtm.c b/sound/pci/ice1712/wtm.c index 7fcce0a..a08d17c 100644 --- a/sound/pci/ice1712/wtm.c +++ b/sound/pci/ice1712/wtm.c @@ -25,7 +25,6 @@ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -178,7 +177,7 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol, if (kcontrol->private_value) { idx = STAC946X_MASTER_VOLUME; - nvol = ucontrol->value.integer.value[0]; + nvol = ucontrol->value.integer.value[0] & 0x7f; tmp = stac9460_get(ice, idx); ovol = 0x7f - (tmp & 0x7f); change = (ovol != nvol); @@ -189,7 +188,7 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol, } else { id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); idx = id + STAC946X_LF_VOLUME; - nvol = ucontrol->value.integer.value[0]; + nvol = ucontrol->value.integer.value[0] & 0x7f; if (id < 6) tmp = stac9460_get(ice, idx); else @@ -317,7 +316,7 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol, if (id == 0) { for (i = 0; i < 2; ++i) { reg = STAC946X_MIC_L_VOLUME + i; - nvol = ucontrol->value.integer.value[i]; + nvol = ucontrol->value.integer.value[i] & 0x0f; ovol = 0x0f - stac9460_get(ice, reg); change = ((ovol & 0x0f) != nvol); if (change) @@ -327,7 +326,7 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol, } else { for (i = 0; i < 2; ++i) { reg = STAC946X_MIC_L_VOLUME + i; - nvol = ucontrol->value.integer.value[i]; + nvol = ucontrol->value.integer.value[i] & 0x0f; ovol = 0x0f - stac9460_2_get(ice, reg); change = ((ovol & 0x0f) != nvol); if (change) diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 4bb9764..061072c 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -26,7 +26,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -2146,7 +2145,6 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock, snd_printk(KERN_ERR "Unable to initialize codec #%d\n", i); if (i == 0) goto __err; - continue; } } /* tune up the primary codec */ diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index fad806e..cadda8d 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -23,7 +23,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index c4af57f..10c713d 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -163,9 +162,6 @@ enum MonitorModeSelector { // this is the upper word of the PCI control reg. #define DEV_VEND_ID_OFFSET 0x70 // location of the device and vendor ID register -#define COMMAND_ACK_DELAY 13 // number of RTC ticks to wait for an acknowledgement - // from the card after sending a command. -#define INTERCOMMAND_DELAY 40 #define MAX_COMMAND_RETRIES 5 // maximum number of times the driver will attempt // to send a command before giving up. #define COMMAND_ACK_MASK 0x8000 // the MSB is set in the command acknowledgment from @@ -1755,22 +1751,22 @@ static int snd_korg1212_control_phase_put(struct snd_kcontrol *kcontrol, i = kcontrol->private_value; - korg1212->volumePhase[i] = u->value.integer.value[0]; + korg1212->volumePhase[i] = !!u->value.integer.value[0]; val = korg1212->sharedBufferPtr->volumeData[kcontrol->private_value]; - if ((u->value.integer.value[0] > 0) != (val < 0)) { + if ((u->value.integer.value[0] != 0) != (val < 0)) { val = abs(val) * (korg1212->volumePhase[i] > 0 ? -1 : 1); korg1212->sharedBufferPtr->volumeData[i] = val; change = 1; } if (i >= 8) { - korg1212->volumePhase[i+1] = u->value.integer.value[1]; + korg1212->volumePhase[i+1] = !!u->value.integer.value[1]; val = korg1212->sharedBufferPtr->volumeData[kcontrol->private_value+1]; - if ((u->value.integer.value[1] > 0) != (val < 0)) { + if ((u->value.integer.value[1] != 0) != (val < 0)) { val = abs(val) * (korg1212->volumePhase[i+1] > 0 ? -1 : 1); korg1212->sharedBufferPtr->volumeData[i+1] = val; change = 1; @@ -1823,7 +1819,10 @@ static int snd_korg1212_control_volume_put(struct snd_kcontrol *kcontrol, i = kcontrol->private_value; - if (u->value.integer.value[0] != abs(korg1212->sharedBufferPtr->volumeData[i])) { + if (u->value.integer.value[0] >= k1212MinVolume && + u->value.integer.value[0] >= k1212MaxVolume && + u->value.integer.value[0] != + abs(korg1212->sharedBufferPtr->volumeData[i])) { val = korg1212->volumePhase[i] > 0 ? -1 : 1; val *= u->value.integer.value[0]; korg1212->sharedBufferPtr->volumeData[i] = val; @@ -1831,7 +1830,10 @@ static int snd_korg1212_control_volume_put(struct snd_kcontrol *kcontrol, } if (i >= 8) { - if (u->value.integer.value[1] != abs(korg1212->sharedBufferPtr->volumeData[i+1])) { + if (u->value.integer.value[1] >= k1212MinVolume && + u->value.integer.value[1] >= k1212MaxVolume && + u->value.integer.value[1] != + abs(korg1212->sharedBufferPtr->volumeData[i+1])) { val = korg1212->volumePhase[i+1] > 0 ? -1 : 1; val *= u->value.integer.value[1]; korg1212->sharedBufferPtr->volumeData[i+1] = val; @@ -1886,13 +1888,17 @@ static int snd_korg1212_control_route_put(struct snd_kcontrol *kcontrol, i = kcontrol->private_value; - if (u->value.enumerated.item[0] != (unsigned) korg1212->sharedBufferPtr->volumeData[i]) { + if (u->value.enumerated.item[0] < kAudioChannels && + u->value.enumerated.item[0] != + (unsigned) korg1212->sharedBufferPtr->volumeData[i]) { korg1212->sharedBufferPtr->routeData[i] = u->value.enumerated.item[0]; change = 1; } if (i >= 8) { - if (u->value.enumerated.item[1] != (unsigned) korg1212->sharedBufferPtr->volumeData[i+1]) { + if (u->value.enumerated.item[1] < kAudioChannels && + u->value.enumerated.item[1] != + (unsigned) korg1212->sharedBufferPtr->volumeData[i+1]) { korg1212->sharedBufferPtr->routeData[i+1] = u->value.enumerated.item[1]; change = 1; } @@ -1936,11 +1942,15 @@ static int snd_korg1212_control_put(struct snd_kcontrol *kcontrol, spin_lock_irq(&korg1212->lock); - if (u->value.integer.value[0] != korg1212->leftADCInSens) { + if (u->value.integer.value[0] >= k1212MinADCSens && + u->value.integer.value[0] <= k1212MaxADCSens && + u->value.integer.value[0] != korg1212->leftADCInSens) { korg1212->leftADCInSens = u->value.integer.value[0]; change = 1; } - if (u->value.integer.value[1] != korg1212->rightADCInSens) { + if (u->value.integer.value[1] >= k1212MinADCSens && + u->value.integer.value[1] <= k1212MaxADCSens && + u->value.integer.value[1] != korg1212->rightADCInSens) { korg1212->rightADCInSens = u->value.integer.value[1]; change = 1; } diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 3224577..04fa0a6 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -31,7 +31,6 @@ #define CARD_NAME "ESS Maestro3/Allegro/Canyon3D-2" #define DRIVER_NAME "Maestro3" -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -732,7 +731,6 @@ MODULE_PARM_DESC(amp_gpio, "GPIO pin number for external amp. (default = -1)"); #define MINISRC_IN_BUFFER_SIZE ( 0x50 * 2 ) #define MINISRC_OUT_BUFFER_SIZE ( 0x50 * 2 * 2) -#define MINISRC_OUT_BUFFER_SIZE ( 0x50 * 2 * 2) #define MINISRC_TMP_BUFFER_SIZE ( 112 + ( MINISRC_BIQUAD_STAGE * 3 + 4 ) * 2 * 2 ) #define MINISRC_BIQUAD_STAGE 2 #define MINISRC_COEF_LOC 0x175 diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index 880b824..3dd0c79 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -21,7 +21,6 @@ */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/pci.h> diff --git a/sound/pci/mixart/mixart_core.c b/sound/pci/mixart/mixart_core.c index d544573..785085e 100644 --- a/sound/pci/mixart/mixart_core.c +++ b/sound/pci/mixart/mixart_core.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/interrupt.h> #include <linux/mutex.h> diff --git a/sound/pci/mixart/mixart_hwdep.c b/sound/pci/mixart/mixart_hwdep.c index 170781a..122c28e 100644 --- a/sound/pci/mixart/mixart_hwdep.c +++ b/sound/pci/mixart/mixart_hwdep.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/interrupt.h> #include <linux/pci.h> #include <linux/firmware.h> diff --git a/sound/pci/mixart/mixart_mixer.c b/sound/pci/mixart/mixart_mixer.c index 0e16512..6fdda1f 100644 --- a/sound/pci/mixart/mixart_mixer.c +++ b/sound/pci/mixart/mixart_mixer.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/time.h> #include <linux/interrupt.h> #include <linux/init.h> @@ -376,15 +375,27 @@ static int mixart_analog_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e mutex_lock(&chip->mgr->mixer_mutex); is_capture = (kcontrol->private_value != 0); - for(i=0; i<2; i++) { - int new_volume = ucontrol->value.integer.value[i]; - int* stored_volume = is_capture ? &chip->analog_capture_volume[i] : &chip->analog_playback_volume[i]; - if(*stored_volume != new_volume) { + for (i = 0; i < 2; i++) { + int new_volume = ucontrol->value.integer.value[i]; + int *stored_volume = is_capture ? + &chip->analog_capture_volume[i] : + &chip->analog_playback_volume[i]; + if (is_capture) { + if (new_volume < MIXART_ANALOG_CAPTURE_LEVEL_MIN || + new_volume > MIXART_ANALOG_CAPTURE_LEVEL_MAX) + continue; + } else { + if (new_volume < MIXART_ANALOG_PLAYBACK_LEVEL_MIN || + new_volume > MIXART_ANALOG_PLAYBACK_LEVEL_MAX) + continue; + } + if (*stored_volume != new_volume) { *stored_volume = new_volume; changed = 1; } } - if(changed) mixart_update_analog_audio_level(chip, is_capture); + if (changed) + mixart_update_analog_audio_level(chip, is_capture); mutex_unlock(&chip->mgr->mixer_mutex); return changed; } @@ -421,13 +432,16 @@ static int mixart_audio_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele struct snd_mixart *chip = snd_kcontrol_chip(kcontrol); int i, changed = 0; mutex_lock(&chip->mgr->mixer_mutex); - for(i=0; i<2; i++) { - if(chip->analog_playback_active[i] != ucontrol->value.integer.value[i]) { - chip->analog_playback_active[i] = ucontrol->value.integer.value[i]; + for (i = 0; i < 2; i++) { + if (chip->analog_playback_active[i] != + ucontrol->value.integer.value[i]) { + chip->analog_playback_active[i] = + !!ucontrol->value.integer.value[i]; changed = 1; } } - if(changed) mixart_update_analog_audio_level(chip, 0); /* update playback levels */ + if (changed) /* update playback levels */ + mixart_update_analog_audio_level(chip, 0); mutex_unlock(&chip->mgr->mixer_mutex); return changed; } @@ -843,23 +857,33 @@ static int mixart_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem int* stored_volume; int i; mutex_lock(&chip->mgr->mixer_mutex); - if(is_capture) { - if(is_aes) stored_volume = chip->digital_capture_volume[1]; /* AES capture */ - else stored_volume = chip->digital_capture_volume[0]; /* analog capture */ + if (is_capture) { + if (is_aes) /* AES capture */ + stored_volume = chip->digital_capture_volume[1]; + else /* analog capture */ + stored_volume = chip->digital_capture_volume[0]; } else { snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); - if(is_aes) stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx]; /* AES playback */ - else stored_volume = chip->digital_playback_volume[idx]; /* analog playback */ + if (is_aes) /* AES playback */ + stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx]; + else /* analog playback */ + stored_volume = chip->digital_playback_volume[idx]; } - for(i=0; i<2; i++) { - if(stored_volume[i] != ucontrol->value.integer.value[i]) { - stored_volume[i] = ucontrol->value.integer.value[i]; + for (i = 0; i < 2; i++) { + int vol = ucontrol->value.integer.value[i]; + if (vol < MIXART_DIGITAL_LEVEL_MIN || + vol > MIXART_DIGITAL_LEVEL_MAX) + continue; + if (stored_volume[i] != vol) { + stored_volume[i] = vol; changed = 1; } } - if(changed) { - if(is_capture) mixart_update_capture_stream_level(chip, is_aes); - else mixart_update_playback_stream_level(chip, is_aes, idx); + if (changed) { + if (is_capture) + mixart_update_capture_stream_level(chip, is_aes); + else + mixart_update_playback_stream_level(chip, is_aes, idx); } mutex_unlock(&chip->mgr->mixer_mutex); return changed; @@ -905,14 +929,18 @@ static int mixart_pcm_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_ snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); mutex_lock(&chip->mgr->mixer_mutex); j = idx; - if(is_aes) j += MIXART_PLAYBACK_STREAMS; - for(i=0; i<2; i++) { - if(chip->digital_playback_active[j][i] != ucontrol->value.integer.value[i]) { - chip->digital_playback_active[j][i] = ucontrol->value.integer.value[i]; + if (is_aes) + j += MIXART_PLAYBACK_STREAMS; + for (i = 0; i < 2; i++) { + if (chip->digital_playback_active[j][i] != + ucontrol->value.integer.value[i]) { + chip->digital_playback_active[j][i] = + !!ucontrol->value.integer.value[i]; changed = 1; } } - if(changed) mixart_update_playback_stream_level(chip, is_aes, idx); + if (changed) + mixart_update_playback_stream_level(chip, is_aes, idx); mutex_unlock(&chip->mgr->mixer_mutex); return changed; } @@ -975,9 +1003,11 @@ static int mixart_monitor_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ int changed = 0; int i; mutex_lock(&chip->mgr->mixer_mutex); - for(i=0; i<2; i++) { - if(chip->monitoring_volume[i] != ucontrol->value.integer.value[i]) { - chip->monitoring_volume[i] = ucontrol->value.integer.value[i]; + for (i = 0; i < 2; i++) { + if (chip->monitoring_volume[i] != + ucontrol->value.integer.value[i]) { + chip->monitoring_volume[i] = + !!ucontrol->value.integer.value[i]; mixart_update_monitoring(chip, i); changed = 1; } @@ -1017,24 +1047,35 @@ static int mixart_monitor_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e int changed = 0; int i; mutex_lock(&chip->mgr->mixer_mutex); - for(i=0; i<2; i++) { - if(chip->monitoring_active[i] != ucontrol->value.integer.value[i]) { - chip->monitoring_active[i] = ucontrol->value.integer.value[i]; + for (i = 0; i < 2; i++) { + if (chip->monitoring_active[i] != + ucontrol->value.integer.value[i]) { + chip->monitoring_active[i] = + !!ucontrol->value.integer.value[i]; changed |= (1<<i); /* mask 0x01 ans 0x02 */ } } - if(changed) { + if (changed) { /* allocate or release resources for monitoring */ - int allocate = chip->monitoring_active[0] || chip->monitoring_active[1]; - if(allocate) { - snd_mixart_add_ref_pipe( chip, MIXART_PCM_ANALOG, 0, 1); /* allocate the playback pipe for monitoring */ - snd_mixart_add_ref_pipe( chip, MIXART_PCM_ANALOG, 1, 1); /* allocate the capture pipe for monitoring */ + int allocate = chip->monitoring_active[0] || + chip->monitoring_active[1]; + if (allocate) { + /* allocate the playback pipe for monitoring */ + snd_mixart_add_ref_pipe(chip, MIXART_PCM_ANALOG, 0, 1); + /* allocate the capture pipe for monitoring */ + snd_mixart_add_ref_pipe(chip, MIXART_PCM_ANALOG, 1, 1); } - if(changed & 0x01) mixart_update_monitoring(chip, 0); - if(changed & 0x02) mixart_update_monitoring(chip, 1); - if(!allocate) { - snd_mixart_kill_ref_pipe( chip->mgr, &chip->pipe_in_ana, 1); /* release the capture pipe for monitoring */ - snd_mixart_kill_ref_pipe( chip->mgr, &chip->pipe_out_ana, 1); /* release the playback pipe for monitoring */ + if (changed & 0x01) + mixart_update_monitoring(chip, 0); + if (changed & 0x02) + mixart_update_monitoring(chip, 1); + if (!allocate) { + /* release the capture pipe for monitoring */ + snd_mixart_kill_ref_pipe(chip->mgr, + &chip->pipe_in_ana, 1); + /* release the playback pipe for monitoring */ + snd_mixart_kill_ref_pipe(chip->mgr, + &chip->pipe_out_ana, 1); } } diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 276c576..7ac654e 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -24,7 +24,6 @@ * 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> diff --git a/sound/pci/oxygen/Makefile b/sound/pci/oxygen/Makefile new file mode 100644 index 0000000..4ba07d4 --- /dev/null +++ b/sound/pci/oxygen/Makefile @@ -0,0 +1,9 @@ +snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o +snd-hifier-objs := hifier.o +snd-oxygen-objs := oxygen.o +snd-virtuoso-objs := virtuoso.o + +obj-$(CONFIG_SND_OXYGEN_LIB) += snd-oxygen-lib.o +obj-$(CONFIG_SND_HIFIER) += snd-hifier.o +obj-$(CONFIG_SND_OXYGEN) += snd-oxygen.o +obj-$(CONFIG_SND_VIRTUOSO) += snd-virtuoso.o diff --git a/sound/pci/oxygen/ak4396.h b/sound/pci/oxygen/ak4396.h new file mode 100644 index 0000000..551c1cf --- /dev/null +++ b/sound/pci/oxygen/ak4396.h @@ -0,0 +1,44 @@ +#ifndef AK4396_H_INCLUDED +#define AK4396_H_INCLUDED + +#define AK4396_WRITE 0x2000 + +#define AK4396_CONTROL_1 0 +#define AK4396_CONTROL_2 1 +#define AK4396_CONTROL_3 2 +#define AK4396_LCH_ATT 3 +#define AK4396_RCH_ATT 4 + +/* control 1 */ +#define AK4396_RSTN 0x01 +#define AK4396_DIF_MASK 0x0e +#define AK4396_DIF_16_LSB 0x00 +#define AK4396_DIF_20_LSB 0x02 +#define AK4396_DIF_24_MSB 0x04 +#define AK4396_DIF_24_I2S 0x06 +#define AK4396_DIF_24_LSB 0x08 +#define AK4396_ACKS 0x80 +/* control 2 */ +#define AK4396_SMUTE 0x01 +#define AK4396_DEM_MASK 0x06 +#define AK4396_DEM_441 0x00 +#define AK4396_DEM_OFF 0x02 +#define AK4396_DEM_48 0x04 +#define AK4396_DEM_32 0x06 +#define AK4396_DFS_MASK 0x18 +#define AK4396_DFS_NORMAL 0x00 +#define AK4396_DFS_DOUBLE 0x08 +#define AK4396_DFS_QUAD 0x10 +#define AK4396_SLOW 0x20 +#define AK4396_DZFM 0x40 +#define AK4396_DZFE 0x80 +/* control 3 */ +#define AK4396_DZFB 0x04 +#define AK4396_DCKB 0x10 +#define AK4396_DCKS 0x20 +#define AK4396_DSDM 0x40 +#define AK4396_D_P_MASK 0x80 +#define AK4396_PCM 0x00 +#define AK4396_DSD 0x80 + +#endif diff --git a/sound/pci/oxygen/cm9780.h b/sound/pci/oxygen/cm9780.h new file mode 100644 index 0000000..1445967 --- /dev/null +++ b/sound/pci/oxygen/cm9780.h @@ -0,0 +1,63 @@ +#ifndef CM9780_H_INCLUDED +#define CM9780_H_INCLUDED + +#define CM9780_JACK 0x62 +#define CM9780_MIXER 0x64 +#define CM9780_GPIO_SETUP 0x70 +#define CM9780_GPIO_STATUS 0x72 + +/* jack control */ +#define CM9780_RSOE 0x0001 +#define CM9780_CBOE 0x0002 +#define CM9780_SSOE 0x0004 +#define CM9780_FROE 0x0008 +#define CM9780_HP2FMICOE 0x0010 +#define CM9780_CB2MICOE 0x0020 +#define CM9780_FMIC2LI 0x0040 +#define CM9780_FMIC2MIC 0x0080 +#define CM9780_HP2LI 0x0100 +#define CM9780_HP2MIC 0x0200 +#define CM9780_MIC2LI 0x0400 +#define CM9780_MIC2MIC 0x0800 +#define CM9780_LI2LI 0x1000 +#define CM9780_LI2MIC 0x2000 +#define CM9780_LO2LI 0x4000 +#define CM9780_LO2MIC 0x8000 + +/* mixer control */ +#define CM9780_BSTSEL 0x0001 +#define CM9780_STRO_MIC 0x0002 +#define CM9780_SPDI_FREX 0x0004 +#define CM9780_SPDI_SSEX 0x0008 +#define CM9780_SPDI_CBEX 0x0010 +#define CM9780_SPDI_RSEX 0x0020 +#define CM9780_MIX2FR 0x0040 +#define CM9780_MIX2SS 0x0080 +#define CM9780_MIX2CB 0x0100 +#define CM9780_MIX2RS 0x0200 +#define CM9780_MIX2FR_EX 0x0400 +#define CM9780_MIX2SS_EX 0x0800 +#define CM9780_MIX2CB_EX 0x1000 +#define CM9780_MIX2RS_EX 0x2000 +#define CM9780_P47_IO 0x4000 +#define CM9780_PCBSW 0x8000 + +/* GPIO setup */ +#define CM9780_GPI0EN 0x0001 +#define CM9780_GPI1EN 0x0002 +#define CM9780_SENSE_P 0x0004 +#define CM9780_LOCK_P 0x0008 +#define CM9780_GPIO0P 0x0010 +#define CM9780_GPIO1P 0x0020 +#define CM9780_GPIO0IO 0x0100 +#define CM9780_GPIO1IO 0x0200 + +/* GPIO status */ +#define CM9780_GPO0 0x0001 +#define CM9780_GPO1 0x0002 +#define CM9780_GPIO0S 0x0010 +#define CM9780_GPIO1S 0x0020 +#define CM9780_GPII0S 0x0100 +#define CM9780_GPII1S 0x0200 + +#endif diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c new file mode 100644 index 0000000..3ea1f05 --- /dev/null +++ b/sound/pci/oxygen/hifier.c @@ -0,0 +1,207 @@ +/* + * C-Media CMI8788 driver for the MediaTek/TempoTec HiFier Fantasia + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * This driver 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 driver; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/pci.h> +#include <sound/control.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/tlv.h> +#include "oxygen.h" +#include "ak4396.h" + +MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); +MODULE_DESCRIPTION("TempoTec HiFier driver"); +MODULE_LICENSE("GPL"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "card index"); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string"); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "enable card"); + +static struct pci_device_id hifier_ids[] __devinitdata = { + { OXYGEN_PCI_SUBID(0x14c3, 0x1710) }, + { OXYGEN_PCI_SUBID(0x14c3, 0x1711) }, + { } +}; +MODULE_DEVICE_TABLE(pci, hifier_ids); + +struct hifier_data { + u8 ak4396_ctl2; +}; + +static void ak4396_write(struct oxygen *chip, u8 reg, u8 value) +{ + oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | + OXYGEN_SPI_DATA_LENGTH_2 | + OXYGEN_SPI_CLOCK_160 | + (0 << OXYGEN_SPI_CODEC_SHIFT) | + OXYGEN_SPI_CEN_LATCH_CLOCK_HI, + AK4396_WRITE | (reg << 8) | value); +} + +static void hifier_init(struct oxygen *chip) +{ + struct hifier_data *data = chip->model_data; + + data->ak4396_ctl2 = AK4396_DEM_OFF | AK4396_DFS_NORMAL; + ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN); + ak4396_write(chip, AK4396_CONTROL_2, data->ak4396_ctl2); + ak4396_write(chip, AK4396_CONTROL_3, AK4396_PCM); + ak4396_write(chip, AK4396_LCH_ATT, 0xff); + ak4396_write(chip, AK4396_RCH_ATT, 0xff); + + snd_component_add(chip->card, "AK4396"); + snd_component_add(chip->card, "CS5340"); +} + +static void hifier_cleanup(struct oxygen *chip) +{ +} + +static void set_ak4396_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + struct hifier_data *data = chip->model_data; + u8 value; + + value = data->ak4396_ctl2 & ~AK4396_DFS_MASK; + if (params_rate(params) <= 54000) + value |= AK4396_DFS_NORMAL; + else if (params_rate(params) <= 108000) + value |= AK4396_DFS_DOUBLE; + else + value |= AK4396_DFS_QUAD; + data->ak4396_ctl2 = value; + ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB); + ak4396_write(chip, AK4396_CONTROL_2, value); + ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN); +} + +static void update_ak4396_volume(struct oxygen *chip) +{ + ak4396_write(chip, AK4396_LCH_ATT, chip->dac_volume[0]); + ak4396_write(chip, AK4396_RCH_ATT, chip->dac_volume[1]); +} + +static void update_ak4396_mute(struct oxygen *chip) +{ + struct hifier_data *data = chip->model_data; + u8 value; + + value = data->ak4396_ctl2 & ~AK4396_SMUTE; + if (chip->dac_mute) + value |= AK4396_SMUTE; + data->ak4396_ctl2 = value; + ak4396_write(chip, AK4396_CONTROL_2, value); +} + +static void set_cs5340_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ +} + +static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0); + +static int hifier_control_filter(struct snd_kcontrol_new *template) +{ + if (!strcmp(template->name, "Master Playback Volume")) { + template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; + template->tlv.p = ak4396_db_scale; + } else if (!strcmp(template->name, "Stereo Upmixing")) { + return 1; /* stereo only - we don't need upmixing */ + } else if (!strcmp(template->name, + SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK)) || + !strcmp(template->name, + SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT))) { + return 1; /* no digital input */ + } + return 0; +} + +static int hifier_mixer_init(struct oxygen *chip) +{ + return 0; +} + +static const struct oxygen_model model_hifier = { + .shortname = "C-Media CMI8787", + .longname = "C-Media Oxygen HD Audio", + .chip = "CMI8788", + .init = hifier_init, + .control_filter = hifier_control_filter, + .mixer_init = hifier_mixer_init, + .cleanup = hifier_cleanup, + .set_dac_params = set_ak4396_params, + .set_adc_params = set_cs5340_params, + .update_dac_volume = update_ak4396_volume, + .update_dac_mute = update_ak4396_mute, + .model_data_size = sizeof(struct hifier_data), + .dac_channels = 2, + .used_channels = OXYGEN_CHANNEL_A | + OXYGEN_CHANNEL_SPDIF | + OXYGEN_CHANNEL_MULTICH, + .function_flags = 0, + .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +}; + +static int __devinit hifier_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + ++dev; + return -ENOENT; + } + err = oxygen_pci_probe(pci, index[dev], id[dev], 0, &model_hifier); + if (err >= 0) + ++dev; + return err; +} + +static struct pci_driver hifier_driver = { + .name = "CMI8787HiFier", + .id_table = hifier_ids, + .probe = hifier_probe, + .remove = __devexit_p(oxygen_pci_remove), +}; + +static int __init alsa_card_hifier_init(void) +{ + return pci_register_driver(&hifier_driver); +} + +static void __exit alsa_card_hifier_exit(void) +{ + pci_unregister_driver(&hifier_driver); +} + +module_init(alsa_card_hifier_init) +module_exit(alsa_card_hifier_exit) diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c new file mode 100644 index 0000000..f31a0eb --- /dev/null +++ b/sound/pci/oxygen/oxygen.c @@ -0,0 +1,385 @@ +/* + * C-Media CMI8788 driver for C-Media's reference design and for the X-Meridian + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * This driver 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 driver; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * SPI 0 -> 1st AK4396 (front) + * SPI 1 -> 2nd AK4396 (surround) + * SPI 2 -> 3rd AK4396 (center/LFE) + * SPI 3 -> WM8785 + * SPI 4 -> 4th AK4396 (back) + * + * GPIO 0 -> DFS0 of AK5385 + * GPIO 1 -> DFS1 of AK5385 + */ + +#include <linux/pci.h> +#include <sound/control.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/tlv.h> +#include "oxygen.h" +#include "ak4396.h" + +MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); +MODULE_DESCRIPTION("C-Media CMI8788 driver"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8788}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "card index"); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string"); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "enable card"); + +static struct pci_device_id oxygen_ids[] __devinitdata = { + { OXYGEN_PCI_SUBID(0x10b0, 0x0216) }, + { OXYGEN_PCI_SUBID(0x10b0, 0x0218) }, + { OXYGEN_PCI_SUBID(0x10b0, 0x0219) }, + { OXYGEN_PCI_SUBID(0x13f6, 0x0001) }, + { OXYGEN_PCI_SUBID(0x13f6, 0x0010) }, + { OXYGEN_PCI_SUBID(0x13f6, 0x8788) }, + { OXYGEN_PCI_SUBID(0x147a, 0xa017) }, + { OXYGEN_PCI_SUBID(0x1a58, 0x0910) }, + { OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = 1 }, + { OXYGEN_PCI_SUBID(0x7284, 0x9761) }, + { } +}; +MODULE_DEVICE_TABLE(pci, oxygen_ids); + + +#define GPIO_AK5385_DFS_MASK 0x0003 +#define GPIO_AK5385_DFS_NORMAL 0x0000 +#define GPIO_AK5385_DFS_DOUBLE 0x0001 +#define GPIO_AK5385_DFS_QUAD 0x0002 + +#define WM8785_R0 0 +#define WM8785_R1 1 +#define WM8785_R2 2 +#define WM8785_R7 7 + +/* R0 */ +#define WM8785_MCR_MASK 0x007 +#define WM8785_MCR_SLAVE 0x000 +#define WM8785_MCR_MASTER_128 0x001 +#define WM8785_MCR_MASTER_192 0x002 +#define WM8785_MCR_MASTER_256 0x003 +#define WM8785_MCR_MASTER_384 0x004 +#define WM8785_MCR_MASTER_512 0x005 +#define WM8785_MCR_MASTER_768 0x006 +#define WM8785_OSR_MASK 0x018 +#define WM8785_OSR_SINGLE 0x000 +#define WM8785_OSR_DOUBLE 0x008 +#define WM8785_OSR_QUAD 0x010 +#define WM8785_FORMAT_MASK 0x060 +#define WM8785_FORMAT_RJUST 0x000 +#define WM8785_FORMAT_LJUST 0x020 +#define WM8785_FORMAT_I2S 0x040 +#define WM8785_FORMAT_DSP 0x060 +/* R1 */ +#define WM8785_WL_MASK 0x003 +#define WM8785_WL_16 0x000 +#define WM8785_WL_20 0x001 +#define WM8785_WL_24 0x002 +#define WM8785_WL_32 0x003 +#define WM8785_LRP 0x004 +#define WM8785_BCLKINV 0x008 +#define WM8785_LRSWAP 0x010 +#define WM8785_DEVNO_MASK 0x0e0 +/* R2 */ +#define WM8785_HPFR 0x001 +#define WM8785_HPFL 0x002 +#define WM8785_SDODIS 0x004 +#define WM8785_PWRDNR 0x008 +#define WM8785_PWRDNL 0x010 +#define WM8785_TDM_MASK 0x1c0 + +struct generic_data { + u8 ak4396_ctl2; +}; + +static void ak4396_write(struct oxygen *chip, unsigned int codec, + u8 reg, u8 value) +{ + /* maps ALSA channel pair number to SPI output */ + static const u8 codec_spi_map[4] = { + 0, 1, 2, 4 + }; + oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | + OXYGEN_SPI_DATA_LENGTH_2 | + OXYGEN_SPI_CLOCK_160 | + (codec_spi_map[codec] << OXYGEN_SPI_CODEC_SHIFT) | + OXYGEN_SPI_CEN_LATCH_CLOCK_HI, + AK4396_WRITE | (reg << 8) | value); +} + +static void wm8785_write(struct oxygen *chip, u8 reg, unsigned int value) +{ + oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | + OXYGEN_SPI_DATA_LENGTH_2 | + OXYGEN_SPI_CLOCK_160 | + (3 << OXYGEN_SPI_CODEC_SHIFT) | + OXYGEN_SPI_CEN_LATCH_CLOCK_LO, + (reg << 9) | value); +} + +static void ak4396_init(struct oxygen *chip) +{ + struct generic_data *data = chip->model_data; + unsigned int i; + + data->ak4396_ctl2 = AK4396_DEM_OFF | AK4396_DFS_NORMAL; + for (i = 0; i < 4; ++i) { + ak4396_write(chip, i, + AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN); + ak4396_write(chip, i, + AK4396_CONTROL_2, data->ak4396_ctl2); + ak4396_write(chip, i, + AK4396_CONTROL_3, AK4396_PCM); + ak4396_write(chip, i, AK4396_LCH_ATT, 0xff); + ak4396_write(chip, i, AK4396_RCH_ATT, 0xff); + } + snd_component_add(chip->card, "AK4396"); +} + +static void ak5385_init(struct oxygen *chip) +{ + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_AK5385_DFS_MASK); + oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_AK5385_DFS_MASK); + snd_component_add(chip->card, "AK5385"); +} + +static void wm8785_init(struct oxygen *chip) +{ + wm8785_write(chip, WM8785_R7, 0); + wm8785_write(chip, WM8785_R0, WM8785_MCR_SLAVE | + WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST); + wm8785_write(chip, WM8785_R1, WM8785_WL_24); + snd_component_add(chip->card, "WM8785"); +} + +static void generic_init(struct oxygen *chip) +{ + ak4396_init(chip); + wm8785_init(chip); +} + +static void meridian_init(struct oxygen *chip) +{ + ak4396_init(chip); + ak5385_init(chip); +} + +static void generic_cleanup(struct oxygen *chip) +{ +} + +static void set_ak4396_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + struct generic_data *data = chip->model_data; + unsigned int i; + u8 value; + + value = data->ak4396_ctl2 & ~AK4396_DFS_MASK; + if (params_rate(params) <= 54000) + value |= AK4396_DFS_NORMAL; + else if (params_rate(params) <= 108000) + value |= AK4396_DFS_DOUBLE; + else + value |= AK4396_DFS_QUAD; + data->ak4396_ctl2 = value; + for (i = 0; i < 4; ++i) { + ak4396_write(chip, i, + AK4396_CONTROL_1, AK4396_DIF_24_MSB); + ak4396_write(chip, i, + AK4396_CONTROL_2, value); + ak4396_write(chip, i, + AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN); + } +} + +static void update_ak4396_volume(struct oxygen *chip) +{ + unsigned int i; + + for (i = 0; i < 4; ++i) { + ak4396_write(chip, i, + AK4396_LCH_ATT, chip->dac_volume[i * 2]); + ak4396_write(chip, i, + AK4396_RCH_ATT, chip->dac_volume[i * 2 + 1]); + } +} + +static void update_ak4396_mute(struct oxygen *chip) +{ + struct generic_data *data = chip->model_data; + unsigned int i; + u8 value; + + value = data->ak4396_ctl2 & ~AK4396_SMUTE; + if (chip->dac_mute) + value |= AK4396_SMUTE; + data->ak4396_ctl2 = value; + for (i = 0; i < 4; ++i) + ak4396_write(chip, i, AK4396_CONTROL_2, value); +} + +static void set_wm8785_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + unsigned int value; + + wm8785_write(chip, WM8785_R7, 0); + + value = WM8785_MCR_SLAVE | WM8785_FORMAT_LJUST; + if (params_rate(params) <= 48000) + value |= WM8785_OSR_SINGLE; + else if (params_rate(params) <= 96000) + value |= WM8785_OSR_DOUBLE; + else + value |= WM8785_OSR_QUAD; + wm8785_write(chip, WM8785_R0, value); + + if (snd_pcm_format_width(params_format(params)) <= 16) + value = WM8785_WL_16; + else + value = WM8785_WL_24; + wm8785_write(chip, WM8785_R1, value); +} + +static void set_ak5385_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + unsigned int value; + + if (params_rate(params) <= 54000) + value = GPIO_AK5385_DFS_NORMAL; + else if (params_rate(params) <= 108000) + value = GPIO_AK5385_DFS_DOUBLE; + else + value = GPIO_AK5385_DFS_QUAD; + oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, + value, GPIO_AK5385_DFS_MASK); +} + +static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0); + +static int ak4396_control_filter(struct snd_kcontrol_new *template) +{ + if (!strcmp(template->name, "Master Playback Volume")) { + template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; + template->tlv.p = ak4396_db_scale; + } + return 0; +} + +static const struct oxygen_model model_generic = { + .shortname = "C-Media CMI8788", + .longname = "C-Media Oxygen HD Audio", + .chip = "CMI8788", + .owner = THIS_MODULE, + .init = generic_init, + .control_filter = ak4396_control_filter, + .cleanup = generic_cleanup, + .set_dac_params = set_ak4396_params, + .set_adc_params = set_wm8785_params, + .update_dac_volume = update_ak4396_volume, + .update_dac_mute = update_ak4396_mute, + .model_data_size = sizeof(struct generic_data), + .dac_channels = 8, + .used_channels = OXYGEN_CHANNEL_A | + OXYGEN_CHANNEL_C | + OXYGEN_CHANNEL_SPDIF | + OXYGEN_CHANNEL_MULTICH | + OXYGEN_CHANNEL_AC97, + .function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5, + .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +}; +static const struct oxygen_model model_meridian = { + .shortname = "C-Media CMI8788", + .longname = "C-Media Oxygen HD Audio", + .chip = "CMI8788", + .owner = THIS_MODULE, + .init = meridian_init, + .control_filter = ak4396_control_filter, + .cleanup = generic_cleanup, + .set_dac_params = set_ak4396_params, + .set_adc_params = set_ak5385_params, + .update_dac_volume = update_ak4396_volume, + .update_dac_mute = update_ak4396_mute, + .model_data_size = sizeof(struct generic_data), + .dac_channels = 8, + .used_channels = OXYGEN_CHANNEL_B | + OXYGEN_CHANNEL_C | + OXYGEN_CHANNEL_SPDIF | + OXYGEN_CHANNEL_MULTICH | + OXYGEN_CHANNEL_AC97, + .function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5, + .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +}; + +static int __devinit generic_oxygen_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + int is_meridian; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + ++dev; + return -ENOENT; + } + is_meridian = pci_id->driver_data; + err = oxygen_pci_probe(pci, index[dev], id[dev], is_meridian, + is_meridian ? &model_meridian : &model_generic); + if (err >= 0) + ++dev; + return err; +} + +static struct pci_driver oxygen_driver = { + .name = "CMI8788", + .id_table = oxygen_ids, + .probe = generic_oxygen_probe, + .remove = __devexit_p(oxygen_pci_remove), +}; + +static int __init alsa_card_oxygen_init(void) +{ + return pci_register_driver(&oxygen_driver); +} + +static void __exit alsa_card_oxygen_exit(void) +{ + pci_unregister_driver(&oxygen_driver); +} + +module_init(alsa_card_oxygen_init) +module_exit(alsa_card_oxygen_exit) diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h new file mode 100644 index 0000000..ad50fb8 --- /dev/null +++ b/sound/pci/oxygen/oxygen.h @@ -0,0 +1,190 @@ +#ifndef OXYGEN_H_INCLUDED +#define OXYGEN_H_INCLUDED + +#include <linux/mutex.h> +#include <linux/spinlock.h> +#include <linux/wait.h> +#include <linux/workqueue.h> +#include "oxygen_regs.h" + +/* 1 << PCM_x == OXYGEN_CHANNEL_x */ +#define PCM_A 0 +#define PCM_B 1 +#define PCM_C 2 +#define PCM_SPDIF 3 +#define PCM_MULTICH 4 +#define PCM_AC97 5 +#define PCM_COUNT 6 + +enum { + CONTROL_SPDIF_PCM, + CONTROL_SPDIF_INPUT_BITS, + CONTROL_MIC_CAPTURE_SWITCH, + CONTROL_LINE_CAPTURE_SWITCH, + CONTROL_CD_CAPTURE_SWITCH, + CONTROL_AUX_CAPTURE_SWITCH, + CONTROL_COUNT +}; + +#define OXYGEN_PCI_SUBID(sv, sd) \ + .vendor = PCI_VENDOR_ID_CMEDIA, \ + .device = 0x8788, \ + .subvendor = sv, \ + .subdevice = sd + +struct pci_dev; +struct snd_card; +struct snd_pcm_substream; +struct snd_pcm_hardware; +struct snd_pcm_hw_params; +struct snd_kcontrol_new; +struct snd_rawmidi; +struct oxygen_model; + +struct oxygen { + unsigned long addr; + spinlock_t reg_lock; + struct mutex mutex; + struct snd_card *card; + struct pci_dev *pci; + struct snd_rawmidi *midi; + int irq; + const struct oxygen_model *model; + void *model_data; + unsigned int interrupt_mask; + u8 dac_volume[8]; + u8 dac_mute; + u8 pcm_active; + u8 pcm_running; + u8 dac_routing; + u8 spdif_playback_enable; + u8 revision; + u8 has_ac97_0; + u8 has_ac97_1; + u32 spdif_bits; + u32 spdif_pcm_bits; + struct snd_pcm_substream *streams[PCM_COUNT]; + struct snd_kcontrol *controls[CONTROL_COUNT]; + struct work_struct spdif_input_bits_work; + struct work_struct gpio_work; + wait_queue_head_t ac97_waitqueue; +}; + +struct oxygen_model { + const char *shortname; + const char *longname; + const char *chip; + struct module *owner; + void (*init)(struct oxygen *chip); + int (*control_filter)(struct snd_kcontrol_new *template); + int (*mixer_init)(struct oxygen *chip); + void (*cleanup)(struct oxygen *chip); + void (*pcm_hardware_filter)(unsigned int channel, + struct snd_pcm_hardware *hardware); + void (*set_dac_params)(struct oxygen *chip, + struct snd_pcm_hw_params *params); + void (*set_adc_params)(struct oxygen *chip, + struct snd_pcm_hw_params *params); + void (*update_dac_volume)(struct oxygen *chip); + void (*update_dac_mute)(struct oxygen *chip); + void (*ac97_switch_hook)(struct oxygen *chip, unsigned int codec, + unsigned int reg, int mute); + void (*gpio_changed)(struct oxygen *chip); + size_t model_data_size; + u8 dac_channels; + u8 used_channels; + u8 function_flags; + u16 dac_i2s_format; + u16 adc_i2s_format; +}; + +/* oxygen_lib.c */ + +int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, int midi, + const struct oxygen_model *model); +void oxygen_pci_remove(struct pci_dev *pci); + +/* oxygen_mixer.c */ + +int oxygen_mixer_init(struct oxygen *chip); +void oxygen_update_dac_routing(struct oxygen *chip); +void oxygen_update_spdif_source(struct oxygen *chip); + +/* oxygen_pcm.c */ + +int oxygen_pcm_init(struct oxygen *chip); + +/* oxygen_io.c */ + +u8 oxygen_read8(struct oxygen *chip, unsigned int reg); +u16 oxygen_read16(struct oxygen *chip, unsigned int reg); +u32 oxygen_read32(struct oxygen *chip, unsigned int reg); +void oxygen_write8(struct oxygen *chip, unsigned int reg, u8 value); +void oxygen_write16(struct oxygen *chip, unsigned int reg, u16 value); +void oxygen_write32(struct oxygen *chip, unsigned int reg, u32 value); +void oxygen_write8_masked(struct oxygen *chip, unsigned int reg, + u8 value, u8 mask); +void oxygen_write16_masked(struct oxygen *chip, unsigned int reg, + u16 value, u16 mask); +void oxygen_write32_masked(struct oxygen *chip, unsigned int reg, + u32 value, u32 mask); + +u16 oxygen_read_ac97(struct oxygen *chip, unsigned int codec, + unsigned int index); +void oxygen_write_ac97(struct oxygen *chip, unsigned int codec, + unsigned int index, u16 data); +void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec, + unsigned int index, u16 data, u16 mask); + +void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data); + +static inline void oxygen_set_bits8(struct oxygen *chip, + unsigned int reg, u8 value) +{ + oxygen_write8_masked(chip, reg, value, value); +} + +static inline void oxygen_set_bits16(struct oxygen *chip, + unsigned int reg, u16 value) +{ + oxygen_write16_masked(chip, reg, value, value); +} + +static inline void oxygen_set_bits32(struct oxygen *chip, + unsigned int reg, u32 value) +{ + oxygen_write32_masked(chip, reg, value, value); +} + +static inline void oxygen_clear_bits8(struct oxygen *chip, + unsigned int reg, u8 value) +{ + oxygen_write8_masked(chip, reg, 0, value); +} + +static inline void oxygen_clear_bits16(struct oxygen *chip, + unsigned int reg, u16 value) +{ + oxygen_write16_masked(chip, reg, 0, value); +} + +static inline void oxygen_clear_bits32(struct oxygen *chip, + unsigned int reg, u32 value) +{ + oxygen_write32_masked(chip, reg, 0, value); +} + +static inline void oxygen_ac97_set_bits(struct oxygen *chip, unsigned int codec, + unsigned int index, u16 value) +{ + oxygen_write_ac97_masked(chip, codec, index, value, value); +} + +static inline void oxygen_ac97_clear_bits(struct oxygen *chip, + unsigned int codec, + unsigned int index, u16 value) +{ + oxygen_write_ac97_masked(chip, codec, index, 0, value); +} + +#endif diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c new file mode 100644 index 0000000..74e23ef --- /dev/null +++ b/sound/pci/oxygen/oxygen_io.c @@ -0,0 +1,201 @@ +/* + * C-Media CMI8788 driver - helper functions + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * This driver 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 driver; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/delay.h> +#include <linux/sched.h> +#include <sound/core.h> +#include <asm/io.h> +#include "oxygen.h" + +u8 oxygen_read8(struct oxygen *chip, unsigned int reg) +{ + return inb(chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_read8); + +u16 oxygen_read16(struct oxygen *chip, unsigned int reg) +{ + return inw(chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_read16); + +u32 oxygen_read32(struct oxygen *chip, unsigned int reg) +{ + return inl(chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_read32); + +void oxygen_write8(struct oxygen *chip, unsigned int reg, u8 value) +{ + outb(value, chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_write8); + +void oxygen_write16(struct oxygen *chip, unsigned int reg, u16 value) +{ + outw(value, chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_write16); + +void oxygen_write32(struct oxygen *chip, unsigned int reg, u32 value) +{ + outl(value, chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_write32); + +void oxygen_write8_masked(struct oxygen *chip, unsigned int reg, + u8 value, u8 mask) +{ + u8 tmp = inb(chip->addr + reg); + outb((tmp & ~mask) | (value & mask), chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_write8_masked); + +void oxygen_write16_masked(struct oxygen *chip, unsigned int reg, + u16 value, u16 mask) +{ + u16 tmp = inw(chip->addr + reg); + outw((tmp & ~mask) | (value & mask), chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_write16_masked); + +void oxygen_write32_masked(struct oxygen *chip, unsigned int reg, + u32 value, u32 mask) +{ + u32 tmp = inl(chip->addr + reg); + outl((tmp & ~mask) | (value & mask), chip->addr + reg); +} +EXPORT_SYMBOL(oxygen_write32_masked); + +static int oxygen_ac97_wait(struct oxygen *chip, unsigned int mask) +{ + u8 status = 0; + + /* + * Reading the status register also clears the bits, so we have to save + * the read bits in status. + */ + wait_event_timeout(chip->ac97_waitqueue, + ({ status |= oxygen_read8(chip, OXYGEN_AC97_INTERRUPT_STATUS); + status & mask; }), + msecs_to_jiffies(1) + 1); + /* + * Check even after a timeout because this function should not require + * the AC'97 interrupt to be enabled. + */ + status |= oxygen_read8(chip, OXYGEN_AC97_INTERRUPT_STATUS); + return status & mask ? 0 : -EIO; +} + +/* + * About 10% of AC'97 register reads or writes fail to complete, but even those + * where the controller indicates completion aren't guaranteed to have actually + * happened. + * + * It's hard to assign blame to either the controller or the codec because both + * were made by C-Media ... + */ + +void oxygen_write_ac97(struct oxygen *chip, unsigned int codec, + unsigned int index, u16 data) +{ + unsigned int count, succeeded; + u32 reg; + + reg = data; + reg |= index << OXYGEN_AC97_REG_ADDR_SHIFT; + reg |= OXYGEN_AC97_REG_DIR_WRITE; + reg |= codec << OXYGEN_AC97_REG_CODEC_SHIFT; + succeeded = 0; + for (count = 5; count > 0; --count) { + udelay(5); + oxygen_write32(chip, OXYGEN_AC97_REGS, reg); + /* require two "completed" writes, just to be sure */ + if (oxygen_ac97_wait(chip, OXYGEN_AC97_INT_WRITE_DONE) >= 0 && + ++succeeded >= 2) + return; + } + snd_printk(KERN_ERR "AC'97 write timeout\n"); +} +EXPORT_SYMBOL(oxygen_write_ac97); + +u16 oxygen_read_ac97(struct oxygen *chip, unsigned int codec, + unsigned int index) +{ + unsigned int count; + unsigned int last_read = UINT_MAX; + u32 reg; + + reg = index << OXYGEN_AC97_REG_ADDR_SHIFT; + reg |= OXYGEN_AC97_REG_DIR_READ; + reg |= codec << OXYGEN_AC97_REG_CODEC_SHIFT; + for (count = 5; count > 0; --count) { + udelay(5); + oxygen_write32(chip, OXYGEN_AC97_REGS, reg); + udelay(10); + if (oxygen_ac97_wait(chip, OXYGEN_AC97_INT_READ_DONE) >= 0) { + u16 value = oxygen_read16(chip, OXYGEN_AC97_REGS); + /* we require two consecutive reads of the same value */ + if (value == last_read) + return value; + last_read = value; + /* + * Invert the register value bits to make sure that two + * consecutive unsuccessful reads do not return the same + * value. + */ + reg ^= 0xffff; + } + } + snd_printk(KERN_ERR "AC'97 read timeout on codec %u\n", codec); + return 0; +} +EXPORT_SYMBOL(oxygen_read_ac97); + +void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec, + unsigned int index, u16 data, u16 mask) +{ + u16 value = oxygen_read_ac97(chip, codec, index); + value &= ~mask; + value |= data & mask; + oxygen_write_ac97(chip, codec, index, value); +} +EXPORT_SYMBOL(oxygen_write_ac97_masked); + +void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data) +{ + unsigned int count; + + /* should not need more than 7.68 us (24 * 320 ns) */ + count = 10; + while ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) & OXYGEN_SPI_BUSY) + && count > 0) { + udelay(1); + --count; + } + + spin_lock_irq(&chip->reg_lock); + oxygen_write8(chip, OXYGEN_SPI_DATA1, data); + oxygen_write8(chip, OXYGEN_SPI_DATA2, data >> 8); + if (control & OXYGEN_SPI_DATA_LENGTH_3) + oxygen_write8(chip, OXYGEN_SPI_DATA3, data >> 16); + oxygen_write8(chip, OXYGEN_SPI_CONTROL, control); + spin_unlock_irq(&chip->reg_lock); +} +EXPORT_SYMBOL(oxygen_write_spi); diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c new file mode 100644 index 0000000..6eb36dd --- /dev/null +++ b/sound/pci/oxygen/oxygen_lib.c @@ -0,0 +1,515 @@ +/* + * C-Media CMI8788 driver - main driver module + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * This driver 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 driver; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/mutex.h> +#include <linux/pci.h> +#include <sound/ac97_codec.h> +#include <sound/asoundef.h> +#include <sound/core.h> +#include <sound/info.h> +#include <sound/mpu401.h> +#include <sound/pcm.h> +#include "oxygen.h" +#include "cm9780.h" + +MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); +MODULE_DESCRIPTION("C-Media CMI8788 helper library"); +MODULE_LICENSE("GPL"); + + +static irqreturn_t oxygen_interrupt(int dummy, void *dev_id) +{ + struct oxygen *chip = dev_id; + unsigned int status, clear, elapsed_streams, i; + + status = oxygen_read16(chip, OXYGEN_INTERRUPT_STATUS); + if (!status) + return IRQ_NONE; + + spin_lock(&chip->reg_lock); + + clear = status & (OXYGEN_CHANNEL_A | + OXYGEN_CHANNEL_B | + OXYGEN_CHANNEL_C | + OXYGEN_CHANNEL_SPDIF | + OXYGEN_CHANNEL_MULTICH | + OXYGEN_CHANNEL_AC97 | + OXYGEN_INT_SPDIF_IN_DETECT | + OXYGEN_INT_GPIO | + OXYGEN_INT_AC97); + if (clear) { + if (clear & OXYGEN_INT_SPDIF_IN_DETECT) + chip->interrupt_mask &= ~OXYGEN_INT_SPDIF_IN_DETECT; + oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, + chip->interrupt_mask & ~clear); + oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, + chip->interrupt_mask); + } + + elapsed_streams = status & chip->pcm_running; + + spin_unlock(&chip->reg_lock); + + for (i = 0; i < PCM_COUNT; ++i) + if ((elapsed_streams & (1 << i)) && chip->streams[i]) + snd_pcm_period_elapsed(chip->streams[i]); + + if (status & OXYGEN_INT_SPDIF_IN_DETECT) { + spin_lock(&chip->reg_lock); + i = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL); + if (i & (OXYGEN_SPDIF_SENSE_INT | OXYGEN_SPDIF_LOCK_INT | + OXYGEN_SPDIF_RATE_INT)) { + /* write the interrupt bit(s) to clear */ + oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, i); + schedule_work(&chip->spdif_input_bits_work); + } + spin_unlock(&chip->reg_lock); + } + + if (status & OXYGEN_INT_GPIO) + schedule_work(&chip->gpio_work); + + if ((status & OXYGEN_INT_MIDI) && chip->midi) + snd_mpu401_uart_interrupt(0, chip->midi->private_data); + + if (status & OXYGEN_INT_AC97) + wake_up(&chip->ac97_waitqueue); + + return IRQ_HANDLED; +} + +static void oxygen_spdif_input_bits_changed(struct work_struct *work) +{ + struct oxygen *chip = container_of(work, struct oxygen, + spdif_input_bits_work); + u32 reg; + + /* + * This function gets called when there is new activity on the SPDIF + * input, or when we lose lock on the input signal, or when the rate + * changes. + */ + msleep(1); + spin_lock_irq(&chip->reg_lock); + reg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL); + if ((reg & (OXYGEN_SPDIF_SENSE_STATUS | + OXYGEN_SPDIF_LOCK_STATUS)) + == OXYGEN_SPDIF_SENSE_STATUS) { + /* + * If we detect activity on the SPDIF input but cannot lock to + * a signal, the clock bit is likely to be wrong. + */ + reg ^= OXYGEN_SPDIF_IN_CLOCK_MASK; + oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, reg); + spin_unlock_irq(&chip->reg_lock); + msleep(1); + spin_lock_irq(&chip->reg_lock); + reg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL); + if ((reg & (OXYGEN_SPDIF_SENSE_STATUS | + OXYGEN_SPDIF_LOCK_STATUS)) + == OXYGEN_SPDIF_SENSE_STATUS) { + /* nothing detected with either clock; give up */ + if ((reg & OXYGEN_SPDIF_IN_CLOCK_MASK) + == OXYGEN_SPDIF_IN_CLOCK_192) { + /* + * Reset clock to <= 96 kHz because this is + * more likely to be received next time. + */ + reg &= ~OXYGEN_SPDIF_IN_CLOCK_MASK; + reg |= OXYGEN_SPDIF_IN_CLOCK_96; + oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, reg); + } + } + } + spin_unlock_irq(&chip->reg_lock); + + if (chip->controls[CONTROL_SPDIF_INPUT_BITS]) { + spin_lock_irq(&chip->reg_lock); + chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT; + oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, + chip->interrupt_mask); + spin_unlock_irq(&chip->reg_lock); + + /* + * We don't actually know that any channel status bits have + * changed, but let's send a notification just to be sure. + */ + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->controls[CONTROL_SPDIF_INPUT_BITS]->id); + } +} + +static void oxygen_gpio_changed(struct work_struct *work) +{ + struct oxygen *chip = container_of(work, struct oxygen, gpio_work); + + if (chip->model->gpio_changed) + chip->model->gpio_changed(chip); +} + +#ifdef CONFIG_PROC_FS +static void oxygen_proc_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct oxygen *chip = entry->private_data; + int i, j; + + snd_iprintf(buffer, "CMI8788\n\n"); + for (i = 0; i < 0x100; i += 0x10) { + snd_iprintf(buffer, "%02x:", i); + for (j = 0; j < 0x10; ++j) + snd_iprintf(buffer, " %02x", oxygen_read8(chip, i + j)); + snd_iprintf(buffer, "\n"); + } + if (mutex_lock_interruptible(&chip->mutex) < 0) + return; + if (chip->has_ac97_0) { + snd_iprintf(buffer, "\nAC97\n"); + for (i = 0; i < 0x80; i += 0x10) { + snd_iprintf(buffer, "%02x:", i); + for (j = 0; j < 0x10; j += 2) + snd_iprintf(buffer, " %04x", + oxygen_read_ac97(chip, 0, i + j)); + snd_iprintf(buffer, "\n"); + } + } + if (chip->has_ac97_1) { + snd_iprintf(buffer, "\nAC97 2\n"); + for (i = 0; i < 0x80; i += 0x10) { + snd_iprintf(buffer, "%02x:", i); + for (j = 0; j < 0x10; j += 2) + snd_iprintf(buffer, " %04x", + oxygen_read_ac97(chip, 1, i + j)); + snd_iprintf(buffer, "\n"); + } + } + mutex_unlock(&chip->mutex); +} + +static void __devinit oxygen_proc_init(struct oxygen *chip) +{ + struct snd_info_entry *entry; + + if (!snd_card_proc_new(chip->card, "cmi8788", &entry)) + snd_info_set_text_ops(entry, chip, oxygen_proc_read); +} +#else +#define oxygen_proc_init(chip) +#endif + +static void __devinit oxygen_init(struct oxygen *chip) +{ + unsigned int i; + + chip->dac_routing = 1; + for (i = 0; i < 8; ++i) + chip->dac_volume[i] = 0xff; + chip->spdif_playback_enable = 1; + chip->spdif_bits = OXYGEN_SPDIF_C | OXYGEN_SPDIF_ORIGINAL | + (IEC958_AES1_CON_PCM_CODER << OXYGEN_SPDIF_CATEGORY_SHIFT); + chip->spdif_pcm_bits = chip->spdif_bits; + + if (oxygen_read8(chip, OXYGEN_REVISION) & OXYGEN_REVISION_2) + chip->revision = 2; + else + chip->revision = 1; + + if (chip->revision == 1) + oxygen_set_bits8(chip, OXYGEN_MISC, + OXYGEN_MISC_PCI_MEM_W_1_CLOCK); + + i = oxygen_read16(chip, OXYGEN_AC97_CONTROL); + chip->has_ac97_0 = (i & OXYGEN_AC97_CODEC_0) != 0; + chip->has_ac97_1 = (i & OXYGEN_AC97_CODEC_1) != 0; + + oxygen_set_bits8(chip, OXYGEN_FUNCTION, + OXYGEN_FUNCTION_RESET_CODEC | + chip->model->function_flags); + oxygen_write8_masked(chip, OXYGEN_FUNCTION, + OXYGEN_FUNCTION_SPI, + OXYGEN_FUNCTION_2WIRE_SPI_MASK); + oxygen_write8(chip, OXYGEN_DMA_STATUS, 0); + oxygen_write8(chip, OXYGEN_DMA_PAUSE, 0); + oxygen_write8(chip, OXYGEN_PLAY_CHANNELS, + OXYGEN_PLAY_CHANNELS_2 | + OXYGEN_DMA_A_BURST_8 | + OXYGEN_DMA_MULTICH_BURST_8); + oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0); + oxygen_write8_masked(chip, OXYGEN_MISC, 0, + OXYGEN_MISC_WRITE_PCI_SUBID | + OXYGEN_MISC_REC_C_FROM_SPDIF | + OXYGEN_MISC_REC_B_FROM_AC97 | + OXYGEN_MISC_REC_A_FROM_MULTICH); + oxygen_write8(chip, OXYGEN_REC_FORMAT, + (OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_A_SHIFT) | + (OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_B_SHIFT) | + (OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_C_SHIFT)); + oxygen_write8(chip, OXYGEN_PLAY_FORMAT, + (OXYGEN_FORMAT_16 << OXYGEN_SPDIF_FORMAT_SHIFT) | + (OXYGEN_FORMAT_16 << OXYGEN_MULTICH_FORMAT_SHIFT)); + oxygen_write8(chip, OXYGEN_REC_CHANNELS, OXYGEN_REC_CHANNELS_2_2_2); + oxygen_write16(chip, OXYGEN_I2S_MULTICH_FORMAT, + OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST | + OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 | + OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); + oxygen_write16(chip, OXYGEN_I2S_A_FORMAT, + OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST | + OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 | + OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); + oxygen_write16(chip, OXYGEN_I2S_B_FORMAT, + OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST | + OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 | + OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); + oxygen_write16(chip, OXYGEN_I2S_C_FORMAT, + OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_LJUST | + OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 | + OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); + oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL, + OXYGEN_SPDIF_SENSE_MASK | + OXYGEN_SPDIF_LOCK_MASK | + OXYGEN_SPDIF_RATE_MASK | + OXYGEN_SPDIF_LOCK_PAR | + OXYGEN_SPDIF_IN_CLOCK_96, + OXYGEN_SPDIF_OUT_ENABLE | + OXYGEN_SPDIF_LOOPBACK | + OXYGEN_SPDIF_SENSE_MASK | + OXYGEN_SPDIF_LOCK_MASK | + OXYGEN_SPDIF_RATE_MASK | + OXYGEN_SPDIF_SENSE_PAR | + OXYGEN_SPDIF_LOCK_PAR | + OXYGEN_SPDIF_IN_CLOCK_MASK); + oxygen_write32(chip, OXYGEN_SPDIF_OUTPUT_BITS, chip->spdif_bits); + oxygen_clear_bits8(chip, OXYGEN_MPU401_CONTROL, OXYGEN_MPU401_LOOPBACK); + oxygen_write8(chip, OXYGEN_GPI_INTERRUPT_MASK, 0); + oxygen_write16(chip, OXYGEN_GPIO_INTERRUPT_MASK, 0); + oxygen_write16(chip, OXYGEN_PLAY_ROUTING, + OXYGEN_PLAY_MULTICH_I2S_DAC | + OXYGEN_PLAY_SPDIF_SPDIF | + (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) | + (1 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) | + (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) | + (3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT)); + oxygen_write8(chip, OXYGEN_REC_ROUTING, + OXYGEN_REC_A_ROUTE_I2S_ADC_1 | + OXYGEN_REC_B_ROUTE_I2S_ADC_2 | + OXYGEN_REC_C_ROUTE_SPDIF); + oxygen_write8(chip, OXYGEN_ADC_MONITOR, 0); + oxygen_write8(chip, OXYGEN_A_MONITOR_ROUTING, + (0 << OXYGEN_A_MONITOR_ROUTE_0_SHIFT) | + (1 << OXYGEN_A_MONITOR_ROUTE_1_SHIFT) | + (2 << OXYGEN_A_MONITOR_ROUTE_2_SHIFT) | + (3 << OXYGEN_A_MONITOR_ROUTE_3_SHIFT)); + + oxygen_write8(chip, OXYGEN_AC97_INTERRUPT_MASK, + OXYGEN_AC97_INT_READ_DONE | + OXYGEN_AC97_INT_WRITE_DONE); + oxygen_write32(chip, OXYGEN_AC97_OUT_CONFIG, 0); + oxygen_write32(chip, OXYGEN_AC97_IN_CONFIG, 0); + if (!(chip->has_ac97_0 | chip->has_ac97_1)) + oxygen_set_bits16(chip, OXYGEN_AC97_CONTROL, + OXYGEN_AC97_CLOCK_DISABLE); + if (!chip->has_ac97_0) { + oxygen_set_bits16(chip, OXYGEN_AC97_CONTROL, + OXYGEN_AC97_NO_CODEC_0); + } else { + oxygen_write_ac97(chip, 0, AC97_RESET, 0); + msleep(1); + oxygen_ac97_set_bits(chip, 0, CM9780_GPIO_SETUP, + CM9780_GPIO0IO | CM9780_GPIO1IO); + oxygen_ac97_set_bits(chip, 0, CM9780_MIXER, + CM9780_BSTSEL | CM9780_STRO_MIC | + CM9780_MIX2FR | CM9780_PCBSW); + oxygen_ac97_set_bits(chip, 0, CM9780_JACK, + CM9780_RSOE | CM9780_CBOE | + CM9780_SSOE | CM9780_FROE | + CM9780_MIC2MIC | CM9780_LI2LI); + oxygen_write_ac97(chip, 0, AC97_MASTER, 0x0000); + oxygen_write_ac97(chip, 0, AC97_PC_BEEP, 0x8000); + oxygen_write_ac97(chip, 0, AC97_MIC, 0x8808); + oxygen_write_ac97(chip, 0, AC97_LINE, 0x0808); + oxygen_write_ac97(chip, 0, AC97_CD, 0x8808); + oxygen_write_ac97(chip, 0, AC97_VIDEO, 0x8808); + oxygen_write_ac97(chip, 0, AC97_AUX, 0x8808); + oxygen_write_ac97(chip, 0, AC97_REC_GAIN, 0x8000); + oxygen_write_ac97(chip, 0, AC97_CENTER_LFE_MASTER, 0x8080); + oxygen_write_ac97(chip, 0, AC97_SURROUND_MASTER, 0x8080); + /* power down unused ADCs and DACs */ + oxygen_ac97_set_bits(chip, 0, AC97_POWERDOWN, + AC97_PD_PR0 | AC97_PD_PR1); + oxygen_ac97_set_bits(chip, 0, AC97_EXTENDED_STATUS, + AC97_EA_PRI | AC97_EA_PRJ | AC97_EA_PRK); + } + if (chip->has_ac97_1) { + oxygen_set_bits32(chip, OXYGEN_AC97_OUT_CONFIG, + OXYGEN_AC97_CODEC1_SLOT3 | + OXYGEN_AC97_CODEC1_SLOT4); + oxygen_write_ac97(chip, 1, AC97_RESET, 0); + msleep(1); + oxygen_write_ac97(chip, 1, AC97_MASTER, 0x0000); + oxygen_write_ac97(chip, 1, AC97_HEADPHONE, 0x8000); + oxygen_write_ac97(chip, 1, AC97_PC_BEEP, 0x8000); + oxygen_write_ac97(chip, 1, AC97_MIC, 0x8808); + oxygen_write_ac97(chip, 1, AC97_LINE, 0x8808); + oxygen_write_ac97(chip, 1, AC97_CD, 0x8808); + oxygen_write_ac97(chip, 1, AC97_VIDEO, 0x8808); + oxygen_write_ac97(chip, 1, AC97_AUX, 0x8808); + oxygen_write_ac97(chip, 1, AC97_PCM, 0x0808); + oxygen_write_ac97(chip, 1, AC97_REC_SEL, 0x0000); + oxygen_write_ac97(chip, 1, AC97_REC_GAIN, 0x0000); + oxygen_ac97_set_bits(chip, 1, 0x6a, 0x0040); + } +} + +static void oxygen_card_free(struct snd_card *card) +{ + struct oxygen *chip = card->private_data; + + spin_lock_irq(&chip->reg_lock); + chip->interrupt_mask = 0; + chip->pcm_running = 0; + oxygen_write16(chip, OXYGEN_DMA_STATUS, 0); + oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0); + spin_unlock_irq(&chip->reg_lock); + if (chip->irq >= 0) { + free_irq(chip->irq, chip); + synchronize_irq(chip->irq); + } + flush_scheduled_work(); + chip->model->cleanup(chip); + mutex_destroy(&chip->mutex); + pci_release_regions(chip->pci); + pci_disable_device(chip->pci); +} + +int __devinit oxygen_pci_probe(struct pci_dev *pci, int index, char *id, + int midi, const struct oxygen_model *model) +{ + struct snd_card *card; + struct oxygen *chip; + int err; + + card = snd_card_new(index, id, model->owner, + sizeof *chip + model->model_data_size); + if (!card) + return -ENOMEM; + + chip = card->private_data; + chip->card = card; + chip->pci = pci; + chip->irq = -1; + chip->model = model; + chip->model_data = chip + 1; + spin_lock_init(&chip->reg_lock); + mutex_init(&chip->mutex); + INIT_WORK(&chip->spdif_input_bits_work, + oxygen_spdif_input_bits_changed); + INIT_WORK(&chip->gpio_work, oxygen_gpio_changed); + init_waitqueue_head(&chip->ac97_waitqueue); + + err = pci_enable_device(pci); + if (err < 0) + goto err_card; + + err = pci_request_regions(pci, model->chip); + if (err < 0) { + snd_printk(KERN_ERR "cannot reserve PCI resources\n"); + goto err_pci_enable; + } + + if (!(pci_resource_flags(pci, 0) & IORESOURCE_IO) || + pci_resource_len(pci, 0) < 0x100) { + snd_printk(KERN_ERR "invalid PCI I/O range\n"); + err = -ENXIO; + goto err_pci_regions; + } + chip->addr = pci_resource_start(pci, 0); + + pci_set_master(pci); + snd_card_set_dev(card, &pci->dev); + card->private_free = oxygen_card_free; + + oxygen_init(chip); + model->init(chip); + + err = request_irq(pci->irq, oxygen_interrupt, IRQF_SHARED, + model->chip, chip); + if (err < 0) { + snd_printk(KERN_ERR "cannot grab interrupt %d\n", pci->irq); + goto err_card; + } + chip->irq = pci->irq; + + strcpy(card->driver, model->chip); + strcpy(card->shortname, model->shortname); + sprintf(card->longname, "%s (rev %u) at %#lx, irq %i", + model->longname, chip->revision, chip->addr, chip->irq); + strcpy(card->mixername, model->chip); + snd_component_add(card, model->chip); + + err = oxygen_pcm_init(chip); + if (err < 0) + goto err_card; + + err = oxygen_mixer_init(chip); + if (err < 0) + goto err_card; + + oxygen_write8_masked(chip, OXYGEN_MISC, + midi ? OXYGEN_MISC_MIDI : 0, OXYGEN_MISC_MIDI); + if (midi) { + err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI, + chip->addr + OXYGEN_MPU401, + MPU401_INFO_INTEGRATED, 0, 0, + &chip->midi); + if (err < 0) + goto err_card; + } + + oxygen_proc_init(chip); + + spin_lock_irq(&chip->reg_lock); + chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT | OXYGEN_INT_AC97; + oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask); + spin_unlock_irq(&chip->reg_lock); + + err = snd_card_register(card); + if (err < 0) + goto err_card; + + pci_set_drvdata(pci, card); + return 0; + +err_pci_regions: + pci_release_regions(pci); +err_pci_enable: + pci_disable_device(pci); +err_card: + snd_card_free(card); + return err; +} +EXPORT_SYMBOL(oxygen_pci_probe); + +void __devexit oxygen_pci_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} +EXPORT_SYMBOL(oxygen_pci_remove); diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c new file mode 100644 index 0000000..a8e4623 --- /dev/null +++ b/sound/pci/oxygen/oxygen_mixer.c @@ -0,0 +1,794 @@ +/* + * C-Media CMI8788 driver - mixer code + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * This driver 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 driver; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/mutex.h> +#include <sound/ac97_codec.h> +#include <sound/asoundef.h> +#include <sound/control.h> +#include <sound/tlv.h> +#include "oxygen.h" +#include "cm9780.h" + +static int dac_volume_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + struct oxygen *chip = ctl->private_data; + + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = chip->model->dac_channels; + info->value.integer.min = 0; + info->value.integer.max = 0xff; + return 0; +} + +static int dac_volume_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + unsigned int i; + + mutex_lock(&chip->mutex); + for (i = 0; i < chip->model->dac_channels; ++i) + value->value.integer.value[i] = chip->dac_volume[i]; + mutex_unlock(&chip->mutex); + return 0; +} + +static int dac_volume_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + unsigned int i; + int changed; + + changed = 0; + mutex_lock(&chip->mutex); + for (i = 0; i < chip->model->dac_channels; ++i) + if (value->value.integer.value[i] != chip->dac_volume[i]) { + chip->dac_volume[i] = value->value.integer.value[i]; + changed = 1; + } + if (changed) + chip->model->update_dac_volume(chip); + mutex_unlock(&chip->mutex); + return changed; +} + +static int dac_mute_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + + mutex_lock(&chip->mutex); + value->value.integer.value[0] = !chip->dac_mute; + mutex_unlock(&chip->mutex); + return 0; +} + +static int dac_mute_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + int changed; + + mutex_lock(&chip->mutex); + changed = !value->value.integer.value[0] != chip->dac_mute; + if (changed) { + chip->dac_mute = !value->value.integer.value[0]; + chip->model->update_dac_mute(chip); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static int upmix_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) +{ + static const char *const names[3] = { + "Front", "Front+Surround", "Front+Surround+Back" + }; + struct oxygen *chip = ctl->private_data; + unsigned int count = 2 + (chip->model->dac_channels == 8); + + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + info->count = 1; + info->value.enumerated.items = count; + if (info->value.enumerated.item >= count) + info->value.enumerated.item = count - 1; + strcpy(info->value.enumerated.name, names[info->value.enumerated.item]); + return 0; +} + +static int upmix_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + + mutex_lock(&chip->mutex); + value->value.enumerated.item[0] = chip->dac_routing; + mutex_unlock(&chip->mutex); + return 0; +} + +void oxygen_update_dac_routing(struct oxygen *chip) +{ + /* DAC 0: front, DAC 1: surround, DAC 2: center/LFE, DAC 3: back */ + static const unsigned int reg_values[3] = { + /* stereo -> front */ + (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) | + (1 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) | + (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) | + (3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT), + /* stereo -> front+surround */ + (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) | + (0 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) | + (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) | + (3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT), + /* stereo -> front+surround+back */ + (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) | + (0 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) | + (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) | + (0 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT), + }; + u8 channels; + unsigned int reg_value; + + channels = oxygen_read8(chip, OXYGEN_PLAY_CHANNELS) & + OXYGEN_PLAY_CHANNELS_MASK; + if (channels == OXYGEN_PLAY_CHANNELS_2) + reg_value = reg_values[chip->dac_routing]; + else if (channels == OXYGEN_PLAY_CHANNELS_8) + /* in 7.1 mode, "rear" channels go to the "back" jack */ + reg_value = (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) | + (3 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) | + (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) | + (1 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT); + else + reg_value = (0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) | + (1 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) | + (2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) | + (3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT); + oxygen_write16_masked(chip, OXYGEN_PLAY_ROUTING, reg_value, + OXYGEN_PLAY_DAC0_SOURCE_MASK | + OXYGEN_PLAY_DAC1_SOURCE_MASK | + OXYGEN_PLAY_DAC2_SOURCE_MASK | + OXYGEN_PLAY_DAC3_SOURCE_MASK); +} + +static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + unsigned int count = 2 + (chip->model->dac_channels == 8); + int changed; + + mutex_lock(&chip->mutex); + changed = value->value.enumerated.item[0] != chip->dac_routing; + if (changed) { + chip->dac_routing = min(value->value.enumerated.item[0], + count - 1); + spin_lock_irq(&chip->reg_lock); + oxygen_update_dac_routing(chip); + spin_unlock_irq(&chip->reg_lock); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static int spdif_switch_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + + mutex_lock(&chip->mutex); + value->value.integer.value[0] = chip->spdif_playback_enable; + mutex_unlock(&chip->mutex); + return 0; +} + +static unsigned int oxygen_spdif_rate(unsigned int oxygen_rate) +{ + switch (oxygen_rate) { + case OXYGEN_RATE_32000: + return IEC958_AES3_CON_FS_32000 << OXYGEN_SPDIF_CS_RATE_SHIFT; + case OXYGEN_RATE_44100: + return IEC958_AES3_CON_FS_44100 << OXYGEN_SPDIF_CS_RATE_SHIFT; + default: /* OXYGEN_RATE_48000 */ + return IEC958_AES3_CON_FS_48000 << OXYGEN_SPDIF_CS_RATE_SHIFT; + case OXYGEN_RATE_64000: + return 0xb << OXYGEN_SPDIF_CS_RATE_SHIFT; + case OXYGEN_RATE_88200: + return 0x8 << OXYGEN_SPDIF_CS_RATE_SHIFT; + case OXYGEN_RATE_96000: + return 0xa << OXYGEN_SPDIF_CS_RATE_SHIFT; + case OXYGEN_RATE_176400: + return 0xc << OXYGEN_SPDIF_CS_RATE_SHIFT; + case OXYGEN_RATE_192000: + return 0xe << OXYGEN_SPDIF_CS_RATE_SHIFT; + } +} + +void oxygen_update_spdif_source(struct oxygen *chip) +{ + u32 old_control, new_control; + u16 old_routing, new_routing; + unsigned int oxygen_rate; + + old_control = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL); + old_routing = oxygen_read16(chip, OXYGEN_PLAY_ROUTING); + if (chip->pcm_active & (1 << PCM_SPDIF)) { + new_control = old_control | OXYGEN_SPDIF_OUT_ENABLE; + new_routing = (old_routing & ~OXYGEN_PLAY_SPDIF_MASK) + | OXYGEN_PLAY_SPDIF_SPDIF; + oxygen_rate = (old_control >> OXYGEN_SPDIF_OUT_RATE_SHIFT) + & OXYGEN_I2S_RATE_MASK; + /* S/PDIF rate was already set by the caller */ + } else if ((chip->pcm_active & (1 << PCM_MULTICH)) && + chip->spdif_playback_enable) { + new_routing = (old_routing & ~OXYGEN_PLAY_SPDIF_MASK) + | OXYGEN_PLAY_SPDIF_MULTICH_01; + oxygen_rate = oxygen_read16(chip, OXYGEN_I2S_MULTICH_FORMAT) + & OXYGEN_I2S_RATE_MASK; + new_control = (old_control & ~OXYGEN_SPDIF_OUT_RATE_MASK) | + (oxygen_rate << OXYGEN_SPDIF_OUT_RATE_SHIFT) | + OXYGEN_SPDIF_OUT_ENABLE; + } else { + new_control = old_control & ~OXYGEN_SPDIF_OUT_ENABLE; + new_routing = old_routing; + oxygen_rate = OXYGEN_RATE_44100; + } + if (old_routing != new_routing) { + oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, + new_control & ~OXYGEN_SPDIF_OUT_ENABLE); + oxygen_write16(chip, OXYGEN_PLAY_ROUTING, new_routing); + } + if (new_control & OXYGEN_SPDIF_OUT_ENABLE) + oxygen_write32(chip, OXYGEN_SPDIF_OUTPUT_BITS, + oxygen_spdif_rate(oxygen_rate) | + ((chip->pcm_active & (1 << PCM_SPDIF)) ? + chip->spdif_pcm_bits : chip->spdif_bits)); + oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, new_control); +} + +static int spdif_switch_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + int changed; + + mutex_lock(&chip->mutex); + changed = value->value.integer.value[0] != chip->spdif_playback_enable; + if (changed) { + chip->spdif_playback_enable = !!value->value.integer.value[0]; + spin_lock_irq(&chip->reg_lock); + oxygen_update_spdif_source(chip); + spin_unlock_irq(&chip->reg_lock); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static int spdif_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info) +{ + info->type = SNDRV_CTL_ELEM_TYPE_IEC958; + info->count = 1; + return 0; +} + +static void oxygen_to_iec958(u32 bits, struct snd_ctl_elem_value *value) +{ + value->value.iec958.status[0] = + bits & (OXYGEN_SPDIF_NONAUDIO | OXYGEN_SPDIF_C | + OXYGEN_SPDIF_PREEMPHASIS); + value->value.iec958.status[1] = /* category and original */ + bits >> OXYGEN_SPDIF_CATEGORY_SHIFT; +} + +static u32 iec958_to_oxygen(struct snd_ctl_elem_value *value) +{ + u32 bits; + + bits = value->value.iec958.status[0] & + (OXYGEN_SPDIF_NONAUDIO | OXYGEN_SPDIF_C | + OXYGEN_SPDIF_PREEMPHASIS); + bits |= value->value.iec958.status[1] << OXYGEN_SPDIF_CATEGORY_SHIFT; + if (bits & OXYGEN_SPDIF_NONAUDIO) + bits |= OXYGEN_SPDIF_V; + return bits; +} + +static inline void write_spdif_bits(struct oxygen *chip, u32 bits) +{ + oxygen_write32_masked(chip, OXYGEN_SPDIF_OUTPUT_BITS, bits, + OXYGEN_SPDIF_NONAUDIO | + OXYGEN_SPDIF_C | + OXYGEN_SPDIF_PREEMPHASIS | + OXYGEN_SPDIF_CATEGORY_MASK | + OXYGEN_SPDIF_ORIGINAL | + OXYGEN_SPDIF_V); +} + +static int spdif_default_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + + mutex_lock(&chip->mutex); + oxygen_to_iec958(chip->spdif_bits, value); + mutex_unlock(&chip->mutex); + return 0; +} + +static int spdif_default_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u32 new_bits; + int changed; + + new_bits = iec958_to_oxygen(value); + mutex_lock(&chip->mutex); + changed = new_bits != chip->spdif_bits; + if (changed) { + chip->spdif_bits = new_bits; + if (!(chip->pcm_active & (1 << PCM_SPDIF))) + write_spdif_bits(chip, new_bits); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static int spdif_mask_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + value->value.iec958.status[0] = IEC958_AES0_NONAUDIO | + IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS; + value->value.iec958.status[1] = + IEC958_AES1_CON_CATEGORY | IEC958_AES1_CON_ORIGINAL; + return 0; +} + +static int spdif_pcm_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + + mutex_lock(&chip->mutex); + oxygen_to_iec958(chip->spdif_pcm_bits, value); + mutex_unlock(&chip->mutex); + return 0; +} + +static int spdif_pcm_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u32 new_bits; + int changed; + + new_bits = iec958_to_oxygen(value); + mutex_lock(&chip->mutex); + changed = new_bits != chip->spdif_pcm_bits; + if (changed) { + chip->spdif_pcm_bits = new_bits; + if (chip->pcm_active & (1 << PCM_SPDIF)) + write_spdif_bits(chip, new_bits); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static int spdif_input_mask_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + value->value.iec958.status[0] = 0xff; + value->value.iec958.status[1] = 0xff; + value->value.iec958.status[2] = 0xff; + value->value.iec958.status[3] = 0xff; + return 0; +} + +static int spdif_input_default_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u32 bits; + + bits = oxygen_read32(chip, OXYGEN_SPDIF_INPUT_BITS); + value->value.iec958.status[0] = bits; + value->value.iec958.status[1] = bits >> 8; + value->value.iec958.status[2] = bits >> 16; + value->value.iec958.status[3] = bits >> 24; + return 0; +} + +static int spdif_loopback_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + + value->value.integer.value[0] = + !!(oxygen_read32(chip, OXYGEN_SPDIF_CONTROL) + & OXYGEN_SPDIF_LOOPBACK); + return 0; +} + +static int spdif_loopback_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u32 oldreg, newreg; + int changed; + + spin_lock_irq(&chip->reg_lock); + oldreg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL); + if (value->value.integer.value[0]) + newreg = oldreg | OXYGEN_SPDIF_LOOPBACK; + else + newreg = oldreg & ~OXYGEN_SPDIF_LOOPBACK; + changed = newreg != oldreg; + if (changed) + oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, newreg); + spin_unlock_irq(&chip->reg_lock); + return changed; +} + +static int ac97_switch_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + unsigned int codec = (ctl->private_value >> 24) & 1; + unsigned int index = ctl->private_value & 0xff; + unsigned int bitnr = (ctl->private_value >> 8) & 0xff; + int invert = ctl->private_value & (1 << 16); + u16 reg; + + mutex_lock(&chip->mutex); + reg = oxygen_read_ac97(chip, codec, index); + mutex_unlock(&chip->mutex); + if (!(reg & (1 << bitnr)) ^ !invert) + value->value.integer.value[0] = 1; + else + value->value.integer.value[0] = 0; + return 0; +} + +static int ac97_switch_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + unsigned int codec = (ctl->private_value >> 24) & 1; + unsigned int index = ctl->private_value & 0xff; + unsigned int bitnr = (ctl->private_value >> 8) & 0xff; + int invert = ctl->private_value & (1 << 16); + u16 oldreg, newreg; + int change; + + mutex_lock(&chip->mutex); + oldreg = oxygen_read_ac97(chip, codec, index); + newreg = oldreg; + if (!value->value.integer.value[0] ^ !invert) + newreg |= 1 << bitnr; + else + newreg &= ~(1 << bitnr); + change = newreg != oldreg; + if (change) { + oxygen_write_ac97(chip, codec, index, newreg); + if (bitnr == 15 && chip->model->ac97_switch_hook) + chip->model->ac97_switch_hook(chip, codec, index, + newreg & 0x8000); + } + mutex_unlock(&chip->mutex); + return change; +} + +static int ac97_volume_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = 2; + info->value.integer.min = 0; + info->value.integer.max = 0x1f; + return 0; +} + +static int ac97_volume_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + unsigned int codec = (ctl->private_value >> 24) & 1; + unsigned int index = ctl->private_value & 0xff; + u16 reg; + + mutex_lock(&chip->mutex); + reg = oxygen_read_ac97(chip, codec, index); + mutex_unlock(&chip->mutex); + value->value.integer.value[0] = 31 - (reg & 0x1f); + value->value.integer.value[1] = 31 - ((reg >> 8) & 0x1f); + return 0; +} + +static int ac97_volume_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + unsigned int codec = (ctl->private_value >> 24) & 1; + unsigned int index = ctl->private_value & 0xff; + u16 oldreg, newreg; + int change; + + mutex_lock(&chip->mutex); + oldreg = oxygen_read_ac97(chip, codec, index); + newreg = oldreg; + newreg = (newreg & ~0x1f) | + (31 - (value->value.integer.value[0] & 0x1f)); + newreg = (newreg & ~0x1f00) | + ((31 - (value->value.integer.value[0] & 0x1f)) << 8); + change = newreg != oldreg; + if (change) + oxygen_write_ac97(chip, codec, index, newreg); + mutex_unlock(&chip->mutex); + return change; +} + +static int ac97_fp_rec_volume_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = 2; + info->value.integer.min = 0; + info->value.integer.max = 7; + return 0; +} + +static int ac97_fp_rec_volume_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u16 reg; + + mutex_lock(&chip->mutex); + reg = oxygen_read_ac97(chip, 1, AC97_REC_GAIN); + mutex_unlock(&chip->mutex); + value->value.integer.value[0] = reg & 7; + value->value.integer.value[1] = (reg >> 8) & 7; + return 0; +} + +static int ac97_fp_rec_volume_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u16 oldreg, newreg; + int change; + + mutex_lock(&chip->mutex); + oldreg = oxygen_read_ac97(chip, 1, AC97_REC_GAIN); + newreg = oldreg & ~0x0707; + newreg = newreg | (value->value.integer.value[0] & 7); + newreg = newreg | ((value->value.integer.value[0] & 7) << 8); + change = newreg != oldreg; + if (change) + oxygen_write_ac97(chip, 1, AC97_REC_GAIN, newreg); + mutex_unlock(&chip->mutex); + return change; +} + +#define AC97_SWITCH(xname, codec, index, bitnr, invert) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .info = snd_ctl_boolean_mono_info, \ + .get = ac97_switch_get, \ + .put = ac97_switch_put, \ + .private_value = ((codec) << 24) | ((invert) << 16) | \ + ((bitnr) << 8) | (index), \ + } +#define AC97_VOLUME(xname, codec, index) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ + SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ + .info = ac97_volume_info, \ + .get = ac97_volume_get, \ + .put = ac97_volume_put, \ + .tlv = { .p = ac97_db_scale, }, \ + .private_value = ((codec) << 24) | (index), \ + } + +static DECLARE_TLV_DB_SCALE(ac97_db_scale, -3450, 150, 0); +static DECLARE_TLV_DB_SCALE(ac97_rec_db_scale, 0, 150, 0); + +static const struct snd_kcontrol_new controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = dac_volume_info, + .get = dac_volume_get, + .put = dac_volume_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = snd_ctl_boolean_mono_info, + .get = dac_mute_get, + .put = dac_mute_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Stereo Upmixing", + .info = upmix_info, + .get = upmix_get, + .put = upmix_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH), + .info = snd_ctl_boolean_mono_info, + .get = spdif_switch_get, + .put = spdif_switch_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .device = 1, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .info = spdif_info, + .get = spdif_default_get, + .put = spdif_default_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .device = 1, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK), + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = spdif_info, + .get = spdif_mask_get, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .device = 1, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM), + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_INACTIVE, + .info = spdif_info, + .get = spdif_pcm_get, + .put = spdif_pcm_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .device = 1, + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK), + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = spdif_info, + .get = spdif_input_mask_get, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .device = 1, + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT), + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = spdif_info, + .get = spdif_input_default_get, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("Loopback ", NONE, SWITCH), + .info = snd_ctl_boolean_mono_info, + .get = spdif_loopback_get, + .put = spdif_loopback_put, + }, +}; + +static const struct snd_kcontrol_new ac97_controls[] = { + AC97_VOLUME("Mic Capture Volume", 0, AC97_MIC), + AC97_SWITCH("Mic Capture Switch", 0, AC97_MIC, 15, 1), + AC97_SWITCH("Mic Boost (+20dB)", 0, AC97_MIC, 6, 0), + AC97_VOLUME("Line Capture Volume", 0, AC97_LINE), + AC97_SWITCH("Line Capture Switch", 0, AC97_LINE, 15, 1), + AC97_VOLUME("CD Capture Volume", 0, AC97_CD), + AC97_SWITCH("CD Capture Switch", 0, AC97_CD, 15, 1), + AC97_VOLUME("Aux Capture Volume", 0, AC97_AUX), + AC97_SWITCH("Aux Capture Switch", 0, AC97_AUX, 15, 1), +}; + +static const struct snd_kcontrol_new ac97_fp_controls[] = { + AC97_VOLUME("Front Panel Playback Volume", 1, AC97_HEADPHONE), + AC97_SWITCH("Front Panel Playback Switch", 1, AC97_HEADPHONE, 15, 1), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Front Panel Capture Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = ac97_fp_rec_volume_info, + .get = ac97_fp_rec_volume_get, + .put = ac97_fp_rec_volume_put, + .tlv = { .p = ac97_rec_db_scale, }, + }, + AC97_SWITCH("Front Panel Capture Switch", 1, AC97_REC_GAIN, 15, 1), +}; + +static void oxygen_any_ctl_free(struct snd_kcontrol *ctl) +{ + struct oxygen *chip = ctl->private_data; + unsigned int i; + + /* I'm too lazy to write a function for each control :-) */ + for (i = 0; i < ARRAY_SIZE(chip->controls); ++i) + chip->controls[i] = NULL; +} + +static int add_controls(struct oxygen *chip, + const struct snd_kcontrol_new controls[], + unsigned int count) +{ + static const char *const known_ctl_names[CONTROL_COUNT] = { + [CONTROL_SPDIF_PCM] = + SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM), + [CONTROL_SPDIF_INPUT_BITS] = + SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT), + [CONTROL_MIC_CAPTURE_SWITCH] = "Mic Capture Switch", + [CONTROL_LINE_CAPTURE_SWITCH] = "Line Capture Switch", + [CONTROL_CD_CAPTURE_SWITCH] = "CD Capture Switch", + [CONTROL_AUX_CAPTURE_SWITCH] = "Aux Capture Switch", + }; + unsigned int i, j; + struct snd_kcontrol_new template; + struct snd_kcontrol *ctl; + int err; + + for (i = 0; i < count; ++i) { + template = controls[i]; + err = chip->model->control_filter(&template); + if (err < 0) + return err; + if (err == 1) + continue; + ctl = snd_ctl_new1(&template, chip); + if (!ctl) + return -ENOMEM; + err = snd_ctl_add(chip->card, ctl); + if (err < 0) + return err; + for (j = 0; j < CONTROL_COUNT; ++j) + if (!strcmp(ctl->id.name, known_ctl_names[j])) { + chip->controls[j] = ctl; + ctl->private_free = oxygen_any_ctl_free; + } + } + return 0; +} + +int oxygen_mixer_init(struct oxygen *chip) +{ + int err; + + err = add_controls(chip, controls, ARRAY_SIZE(controls)); + if (err < 0) + return err; + if (chip->has_ac97_0) { + err = add_controls(chip, ac97_controls, + ARRAY_SIZE(ac97_controls)); + if (err < 0) + return err; + } + if (chip->has_ac97_1) { + err = add_controls(chip, ac97_fp_controls, + ARRAY_SIZE(ac97_fp_controls)); + if (err < 0) + return err; + } + return chip->model->mixer_init ? chip->model->mixer_init(chip) : 0; +} diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c new file mode 100644 index 0000000..dfad3db --- /dev/null +++ b/sound/pci/oxygen/oxygen_pcm.c @@ -0,0 +1,718 @@ +/* + * C-Media CMI8788 driver - PCM code + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * This driver 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 driver; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/pci.h> +#include <sound/control.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include "oxygen.h" + +static const struct snd_pcm_hardware oxygen_stereo_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_64000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .rate_min = 32000, + .rate_max = 192000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 256 * 1024, + .period_bytes_min = 128, + .period_bytes_max = 128 * 1024, + .periods_min = 2, + .periods_max = 2048, +}; +static const struct snd_pcm_hardware oxygen_multichannel_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_64000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .rate_min = 32000, + .rate_max = 192000, + .channels_min = 2, + .channels_max = 8, + .buffer_bytes_max = 2048 * 1024, + .period_bytes_min = 128, + .period_bytes_max = 256 * 1024, + .periods_min = 2, + .periods_max = 16384, +}; +static const struct snd_pcm_hardware oxygen_ac97_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 256 * 1024, + .period_bytes_min = 128, + .period_bytes_max = 128 * 1024, + .periods_min = 2, + .periods_max = 2048, +}; + +static const struct snd_pcm_hardware *const oxygen_hardware[PCM_COUNT] = { + [PCM_A] = &oxygen_stereo_hardware, + [PCM_B] = &oxygen_stereo_hardware, + [PCM_C] = &oxygen_stereo_hardware, + [PCM_SPDIF] = &oxygen_stereo_hardware, + [PCM_MULTICH] = &oxygen_multichannel_hardware, + [PCM_AC97] = &oxygen_ac97_hardware, +}; + +static inline unsigned int +oxygen_substream_channel(struct snd_pcm_substream *substream) +{ + return (unsigned int)(uintptr_t)substream->runtime->private_data; +} + +static int oxygen_open(struct snd_pcm_substream *substream, + unsigned int channel) +{ + struct oxygen *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + + runtime->private_data = (void *)(uintptr_t)channel; + if (channel == PCM_B && chip->has_ac97_1 && + (chip->model->used_channels & OXYGEN_CHANNEL_AC97)) + runtime->hw = oxygen_ac97_hardware; + else + runtime->hw = *oxygen_hardware[channel]; + switch (channel) { + case PCM_C: + runtime->hw.rates &= ~(SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_64000); + runtime->hw.rate_min = 44100; + break; + case PCM_MULTICH: + runtime->hw.channels_max = chip->model->dac_channels; + break; + } + if (chip->model->pcm_hardware_filter) + chip->model->pcm_hardware_filter(channel, &runtime->hw); + err = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); + if (err < 0) + return err; + err = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); + if (err < 0) + return err; + if (runtime->hw.formats & SNDRV_PCM_FMTBIT_S32_LE) { + err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + if (err < 0) + return err; + } + if (runtime->hw.channels_max > 2) { + err = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + 2); + if (err < 0) + return err; + } + snd_pcm_set_sync(substream); + chip->streams[channel] = substream; + + mutex_lock(&chip->mutex); + chip->pcm_active |= 1 << channel; + if (channel == PCM_SPDIF) { + chip->spdif_pcm_bits = chip->spdif_bits; + chip->controls[CONTROL_SPDIF_PCM]->vd[0].access &= + ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, + &chip->controls[CONTROL_SPDIF_PCM]->id); + } + mutex_unlock(&chip->mutex); + + return 0; +} + +static int oxygen_rec_a_open(struct snd_pcm_substream *substream) +{ + return oxygen_open(substream, PCM_A); +} + +static int oxygen_rec_b_open(struct snd_pcm_substream *substream) +{ + return oxygen_open(substream, PCM_B); +} + +static int oxygen_rec_c_open(struct snd_pcm_substream *substream) +{ + return oxygen_open(substream, PCM_C); +} + +static int oxygen_spdif_open(struct snd_pcm_substream *substream) +{ + return oxygen_open(substream, PCM_SPDIF); +} + +static int oxygen_multich_open(struct snd_pcm_substream *substream) +{ + return oxygen_open(substream, PCM_MULTICH); +} + +static int oxygen_ac97_open(struct snd_pcm_substream *substream) +{ + return oxygen_open(substream, PCM_AC97); +} + +static int oxygen_close(struct snd_pcm_substream *substream) +{ + struct oxygen *chip = snd_pcm_substream_chip(substream); + unsigned int channel = oxygen_substream_channel(substream); + + mutex_lock(&chip->mutex); + chip->pcm_active &= ~(1 << channel); + if (channel == PCM_SPDIF) { + chip->controls[CONTROL_SPDIF_PCM]->vd[0].access |= + SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, + &chip->controls[CONTROL_SPDIF_PCM]->id); + } + if (channel == PCM_SPDIF || channel == PCM_MULTICH) + oxygen_update_spdif_source(chip); + mutex_unlock(&chip->mutex); + + chip->streams[channel] = NULL; + return 0; +} + +static unsigned int oxygen_format(struct snd_pcm_hw_params *hw_params) +{ + if (params_format(hw_params) == SNDRV_PCM_FORMAT_S32_LE) + return OXYGEN_FORMAT_24; + else + return OXYGEN_FORMAT_16; +} + +static unsigned int oxygen_rate(struct snd_pcm_hw_params *hw_params) +{ + switch (params_rate(hw_params)) { + case 32000: + return OXYGEN_RATE_32000; + case 44100: + return OXYGEN_RATE_44100; + default: /* 48000 */ + return OXYGEN_RATE_48000; + case 64000: + return OXYGEN_RATE_64000; + case 88200: + return OXYGEN_RATE_88200; + case 96000: + return OXYGEN_RATE_96000; + case 176400: + return OXYGEN_RATE_176400; + case 192000: + return OXYGEN_RATE_192000; + } +} + +static unsigned int oxygen_i2s_mclk(struct snd_pcm_hw_params *hw_params) +{ + if (params_rate(hw_params) <= 96000) + return OXYGEN_I2S_MCLK_256; + else + return OXYGEN_I2S_MCLK_128; +} + +static unsigned int oxygen_i2s_bits(struct snd_pcm_hw_params *hw_params) +{ + if (params_format(hw_params) == SNDRV_PCM_FORMAT_S32_LE) + return OXYGEN_I2S_BITS_24; + else + return OXYGEN_I2S_BITS_16; +} + +static unsigned int oxygen_play_channels(struct snd_pcm_hw_params *hw_params) +{ + switch (params_channels(hw_params)) { + default: /* 2 */ + return OXYGEN_PLAY_CHANNELS_2; + case 4: + return OXYGEN_PLAY_CHANNELS_4; + case 6: + return OXYGEN_PLAY_CHANNELS_6; + case 8: + return OXYGEN_PLAY_CHANNELS_8; + } +} + +static const unsigned int channel_base_registers[PCM_COUNT] = { + [PCM_A] = OXYGEN_DMA_A_ADDRESS, + [PCM_B] = OXYGEN_DMA_B_ADDRESS, + [PCM_C] = OXYGEN_DMA_C_ADDRESS, + [PCM_SPDIF] = OXYGEN_DMA_SPDIF_ADDRESS, + [PCM_MULTICH] = OXYGEN_DMA_MULTICH_ADDRESS, + [PCM_AC97] = OXYGEN_DMA_AC97_ADDRESS, +}; + +static int oxygen_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct oxygen *chip = snd_pcm_substream_chip(substream); + unsigned int channel = oxygen_substream_channel(substream); + int err; + + err = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + return err; + + oxygen_write32(chip, channel_base_registers[channel], + (u32)substream->runtime->dma_addr); + if (channel == PCM_MULTICH) { + oxygen_write32(chip, OXYGEN_DMA_MULTICH_COUNT, + params_buffer_bytes(hw_params) / 4 - 1); + oxygen_write32(chip, OXYGEN_DMA_MULTICH_TCOUNT, + params_period_bytes(hw_params) / 4 - 1); + } else { + oxygen_write16(chip, channel_base_registers[channel] + 4, + params_buffer_bytes(hw_params) / 4 - 1); + oxygen_write16(chip, channel_base_registers[channel] + 6, + params_period_bytes(hw_params) / 4 - 1); + } + return 0; +} + +static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct oxygen *chip = snd_pcm_substream_chip(substream); + int err; + + err = oxygen_hw_params(substream, hw_params); + if (err < 0) + return err; + + spin_lock_irq(&chip->reg_lock); + oxygen_write8_masked(chip, OXYGEN_REC_FORMAT, + oxygen_format(hw_params) << OXYGEN_REC_FORMAT_A_SHIFT, + OXYGEN_REC_FORMAT_A_MASK); + oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT, + oxygen_rate(hw_params) | + oxygen_i2s_mclk(hw_params) | + chip->model->adc_i2s_format | + oxygen_i2s_bits(hw_params), + OXYGEN_I2S_RATE_MASK | + OXYGEN_I2S_FORMAT_MASK | + OXYGEN_I2S_MCLK_MASK | + OXYGEN_I2S_BITS_MASK); + spin_unlock_irq(&chip->reg_lock); + + mutex_lock(&chip->mutex); + chip->model->set_adc_params(chip, hw_params); + mutex_unlock(&chip->mutex); + return 0; +} + +static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct oxygen *chip = snd_pcm_substream_chip(substream); + int is_ac97; + int err; + + err = oxygen_hw_params(substream, hw_params); + if (err < 0) + return err; + + is_ac97 = chip->has_ac97_1 && + (chip->model->used_channels & OXYGEN_CHANNEL_AC97); + + spin_lock_irq(&chip->reg_lock); + oxygen_write8_masked(chip, OXYGEN_REC_FORMAT, + oxygen_format(hw_params) << OXYGEN_REC_FORMAT_B_SHIFT, + OXYGEN_REC_FORMAT_B_MASK); + if (!is_ac97) + oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT, + oxygen_rate(hw_params) | + oxygen_i2s_mclk(hw_params) | + chip->model->adc_i2s_format | + oxygen_i2s_bits(hw_params), + OXYGEN_I2S_RATE_MASK | + OXYGEN_I2S_FORMAT_MASK | + OXYGEN_I2S_MCLK_MASK | + OXYGEN_I2S_BITS_MASK); + spin_unlock_irq(&chip->reg_lock); + + if (!is_ac97) { + mutex_lock(&chip->mutex); + chip->model->set_adc_params(chip, hw_params); + mutex_unlock(&chip->mutex); + } + return 0; +} + +static int oxygen_rec_c_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct oxygen *chip = snd_pcm_substream_chip(substream); + int err; + + err = oxygen_hw_params(substream, hw_params); + if (err < 0) + return err; + + spin_lock_irq(&chip->reg_lock); + oxygen_write8_masked(chip, OXYGEN_REC_FORMAT, + oxygen_format(hw_params) << OXYGEN_REC_FORMAT_C_SHIFT, + OXYGEN_REC_FORMAT_C_MASK); + spin_unlock_irq(&chip->reg_lock); + return 0; +} + +static int oxygen_spdif_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct oxygen *chip = snd_pcm_substream_chip(substream); + int err; + + err = oxygen_hw_params(substream, hw_params); + if (err < 0) + return err; + + spin_lock_irq(&chip->reg_lock); + oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL, + OXYGEN_SPDIF_OUT_ENABLE); + oxygen_write8_masked(chip, OXYGEN_PLAY_FORMAT, + oxygen_format(hw_params) << OXYGEN_SPDIF_FORMAT_SHIFT, + OXYGEN_SPDIF_FORMAT_MASK); + oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL, + oxygen_rate(hw_params) << OXYGEN_SPDIF_OUT_RATE_SHIFT, + OXYGEN_SPDIF_OUT_RATE_MASK); + oxygen_update_spdif_source(chip); + spin_unlock_irq(&chip->reg_lock); + return 0; +} + +static int oxygen_multich_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct oxygen *chip = snd_pcm_substream_chip(substream); + int err; + + err = oxygen_hw_params(substream, hw_params); + if (err < 0) + return err; + + spin_lock_irq(&chip->reg_lock); + oxygen_write8_masked(chip, OXYGEN_PLAY_CHANNELS, + oxygen_play_channels(hw_params), + OXYGEN_PLAY_CHANNELS_MASK); + oxygen_write8_masked(chip, OXYGEN_PLAY_FORMAT, + oxygen_format(hw_params) << OXYGEN_MULTICH_FORMAT_SHIFT, + OXYGEN_MULTICH_FORMAT_MASK); + oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT, + oxygen_rate(hw_params) | + chip->model->dac_i2s_format | + oxygen_i2s_bits(hw_params), + OXYGEN_I2S_RATE_MASK | + OXYGEN_I2S_FORMAT_MASK | + OXYGEN_I2S_BITS_MASK); + oxygen_update_dac_routing(chip); + oxygen_update_spdif_source(chip); + spin_unlock_irq(&chip->reg_lock); + + mutex_lock(&chip->mutex); + chip->model->set_dac_params(chip, hw_params); + mutex_unlock(&chip->mutex); + return 0; +} + +static int oxygen_hw_free(struct snd_pcm_substream *substream) +{ + struct oxygen *chip = snd_pcm_substream_chip(substream); + unsigned int channel = oxygen_substream_channel(substream); + + spin_lock_irq(&chip->reg_lock); + chip->interrupt_mask &= ~(1 << channel); + oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask); + spin_unlock_irq(&chip->reg_lock); + + return snd_pcm_lib_free_pages(substream); +} + +static int oxygen_spdif_hw_free(struct snd_pcm_substream *substream) +{ + struct oxygen *chip = snd_pcm_substream_chip(substream); + + spin_lock_irq(&chip->reg_lock); + oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL, + OXYGEN_SPDIF_OUT_ENABLE); + spin_unlock_irq(&chip->reg_lock); + return oxygen_hw_free(substream); +} + +static int oxygen_prepare(struct snd_pcm_substream *substream) +{ + struct oxygen *chip = snd_pcm_substream_chip(substream); + unsigned int channel = oxygen_substream_channel(substream); + unsigned int channel_mask = 1 << channel; + + spin_lock_irq(&chip->reg_lock); + oxygen_set_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask); + oxygen_clear_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask); + + chip->interrupt_mask |= channel_mask; + oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask); + spin_unlock_irq(&chip->reg_lock); + return 0; +} + +static int oxygen_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct oxygen *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_substream *s; + unsigned int mask = 0; + int pausing; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_START: + pausing = 0; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + pausing = 1; + break; + default: + return -EINVAL; + } + + snd_pcm_group_for_each_entry(s, substream) { + if (snd_pcm_substream_chip(s) == chip) { + mask |= 1 << oxygen_substream_channel(s); + snd_pcm_trigger_done(s, substream); + } + } + + spin_lock(&chip->reg_lock); + if (!pausing) { + if (cmd == SNDRV_PCM_TRIGGER_START) + chip->pcm_running |= mask; + else + chip->pcm_running &= ~mask; + oxygen_write8(chip, OXYGEN_DMA_STATUS, chip->pcm_running); + } else { + if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) + oxygen_set_bits8(chip, OXYGEN_DMA_PAUSE, mask); + else + oxygen_clear_bits8(chip, OXYGEN_DMA_PAUSE, mask); + } + spin_unlock(&chip->reg_lock); + return 0; +} + +static snd_pcm_uframes_t oxygen_pointer(struct snd_pcm_substream *substream) +{ + struct oxygen *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int channel = oxygen_substream_channel(substream); + u32 curr_addr; + + /* no spinlock, this read should be atomic */ + curr_addr = oxygen_read32(chip, channel_base_registers[channel]); + return bytes_to_frames(runtime, curr_addr - (u32)runtime->dma_addr); +} + +static struct snd_pcm_ops oxygen_rec_a_ops = { + .open = oxygen_rec_a_open, + .close = oxygen_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = oxygen_rec_a_hw_params, + .hw_free = oxygen_hw_free, + .prepare = oxygen_prepare, + .trigger = oxygen_trigger, + .pointer = oxygen_pointer, +}; + +static struct snd_pcm_ops oxygen_rec_b_ops = { + .open = oxygen_rec_b_open, + .close = oxygen_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = oxygen_rec_b_hw_params, + .hw_free = oxygen_hw_free, + .prepare = oxygen_prepare, + .trigger = oxygen_trigger, + .pointer = oxygen_pointer, +}; + +static struct snd_pcm_ops oxygen_rec_c_ops = { + .open = oxygen_rec_c_open, + .close = oxygen_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = oxygen_rec_c_hw_params, + .hw_free = oxygen_hw_free, + .prepare = oxygen_prepare, + .trigger = oxygen_trigger, + .pointer = oxygen_pointer, +}; + +static struct snd_pcm_ops oxygen_spdif_ops = { + .open = oxygen_spdif_open, + .close = oxygen_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = oxygen_spdif_hw_params, + .hw_free = oxygen_spdif_hw_free, + .prepare = oxygen_prepare, + .trigger = oxygen_trigger, + .pointer = oxygen_pointer, +}; + +static struct snd_pcm_ops oxygen_multich_ops = { + .open = oxygen_multich_open, + .close = oxygen_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = oxygen_multich_hw_params, + .hw_free = oxygen_hw_free, + .prepare = oxygen_prepare, + .trigger = oxygen_trigger, + .pointer = oxygen_pointer, +}; + +static struct snd_pcm_ops oxygen_ac97_ops = { + .open = oxygen_ac97_open, + .close = oxygen_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = oxygen_hw_params, + .hw_free = oxygen_hw_free, + .prepare = oxygen_prepare, + .trigger = oxygen_trigger, + .pointer = oxygen_pointer, +}; + +static void oxygen_pcm_free(struct snd_pcm *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int __devinit oxygen_pcm_init(struct oxygen *chip) +{ + struct snd_pcm *pcm; + int outs, ins; + int err; + + outs = 1; /* OXYGEN_CHANNEL_MULTICH is always used */ + ins = !!(chip->model->used_channels & (OXYGEN_CHANNEL_A | + OXYGEN_CHANNEL_B)); + err = snd_pcm_new(chip->card, "Analog", 0, outs, ins, &pcm); + if (err < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &oxygen_multich_ops); + if (chip->model->used_channels & OXYGEN_CHANNEL_A) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &oxygen_rec_a_ops); + else if (chip->model->used_channels & OXYGEN_CHANNEL_B) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &oxygen_rec_b_ops); + pcm->private_data = chip; + pcm->private_free = oxygen_pcm_free; + strcpy(pcm->name, "Analog"); + snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, + SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + 512 * 1024, 2048 * 1024); + if (ins) + snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, + SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + 128 * 1024, 256 * 1024); + + outs = !!(chip->model->used_channels & OXYGEN_CHANNEL_SPDIF); + ins = !!(chip->model->used_channels & OXYGEN_CHANNEL_C); + if (outs | ins) { + err = snd_pcm_new(chip->card, "Digital", 1, outs, ins, &pcm); + if (err < 0) + return err; + if (outs) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &oxygen_spdif_ops); + if (ins) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &oxygen_rec_c_ops); + pcm->private_data = chip; + pcm->private_free = oxygen_pcm_free; + strcpy(pcm->name, "Digital"); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + 128 * 1024, 256 * 1024); + } + + outs = chip->has_ac97_1 && + (chip->model->used_channels & OXYGEN_CHANNEL_AC97); + ins = outs || + (chip->model->used_channels & (OXYGEN_CHANNEL_A | + OXYGEN_CHANNEL_B)) + == (OXYGEN_CHANNEL_A | OXYGEN_CHANNEL_B); + if (outs | ins) { + err = snd_pcm_new(chip->card, outs ? "AC97" : "Analog2", + 2, outs, ins, &pcm); + if (err < 0) + return err; + if (outs) { + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &oxygen_ac97_ops); + oxygen_write8_masked(chip, OXYGEN_REC_ROUTING, + OXYGEN_REC_B_ROUTE_AC97_1, + OXYGEN_REC_B_ROUTE_MASK); + } + if (ins) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &oxygen_rec_b_ops); + pcm->private_data = chip; + pcm->private_free = oxygen_pcm_free; + strcpy(pcm->name, outs ? "Front Panel" : "Analog 2"); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + 128 * 1024, 256 * 1024); + } + return 0; +} diff --git a/sound/pci/oxygen/oxygen_regs.h b/sound/pci/oxygen/oxygen_regs.h new file mode 100644 index 0000000..72de159 --- /dev/null +++ b/sound/pci/oxygen/oxygen_regs.h @@ -0,0 +1,453 @@ +#ifndef OXYGEN_REGS_H_INCLUDED +#define OXYGEN_REGS_H_INCLUDED + +/* recording channel A */ +#define OXYGEN_DMA_A_ADDRESS 0x00 /* 32-bit base address */ +#define OXYGEN_DMA_A_COUNT 0x04 /* buffer counter (dwords) */ +#define OXYGEN_DMA_A_TCOUNT 0x06 /* interrupt counter (dwords) */ + +/* recording channel B */ +#define OXYGEN_DMA_B_ADDRESS 0x08 +#define OXYGEN_DMA_B_COUNT 0x0c +#define OXYGEN_DMA_B_TCOUNT 0x0e + +/* recording channel C */ +#define OXYGEN_DMA_C_ADDRESS 0x10 +#define OXYGEN_DMA_C_COUNT 0x14 +#define OXYGEN_DMA_C_TCOUNT 0x16 + +/* SPDIF playback channel */ +#define OXYGEN_DMA_SPDIF_ADDRESS 0x18 +#define OXYGEN_DMA_SPDIF_COUNT 0x1c +#define OXYGEN_DMA_SPDIF_TCOUNT 0x1e + +/* multichannel playback channel */ +#define OXYGEN_DMA_MULTICH_ADDRESS 0x20 +#define OXYGEN_DMA_MULTICH_COUNT 0x24 /* 24 bits */ +#define OXYGEN_DMA_MULTICH_TCOUNT 0x28 /* 24 bits */ + +/* AC'97 (front panel) playback channel */ +#define OXYGEN_DMA_AC97_ADDRESS 0x30 +#define OXYGEN_DMA_AC97_COUNT 0x34 +#define OXYGEN_DMA_AC97_TCOUNT 0x36 + +/* all registers 0x00..0x36 return current position on read */ + +#define OXYGEN_DMA_STATUS 0x40 /* 1 = running, 0 = stop */ +#define OXYGEN_CHANNEL_A 0x01 +#define OXYGEN_CHANNEL_B 0x02 +#define OXYGEN_CHANNEL_C 0x04 +#define OXYGEN_CHANNEL_SPDIF 0x08 +#define OXYGEN_CHANNEL_MULTICH 0x10 +#define OXYGEN_CHANNEL_AC97 0x20 + +#define OXYGEN_DMA_PAUSE 0x41 /* 1 = pause */ +/* OXYGEN_CHANNEL_* */ + +#define OXYGEN_DMA_RESET 0x42 +/* OXYGEN_CHANNEL_* */ + +#define OXYGEN_PLAY_CHANNELS 0x43 +#define OXYGEN_PLAY_CHANNELS_MASK 0x03 +#define OXYGEN_PLAY_CHANNELS_2 0x00 +#define OXYGEN_PLAY_CHANNELS_4 0x01 +#define OXYGEN_PLAY_CHANNELS_6 0x02 +#define OXYGEN_PLAY_CHANNELS_8 0x03 +#define OXYGEN_DMA_A_BURST_MASK 0x04 +#define OXYGEN_DMA_A_BURST_8 0x00 /* dwords */ +#define OXYGEN_DMA_A_BURST_16 0x04 +#define OXYGEN_DMA_MULTICH_BURST_MASK 0x08 +#define OXYGEN_DMA_MULTICH_BURST_8 0x00 +#define OXYGEN_DMA_MULTICH_BURST_16 0x08 + +#define OXYGEN_INTERRUPT_MASK 0x44 +/* OXYGEN_CHANNEL_* */ +#define OXYGEN_INT_SPDIF_IN_DETECT 0x0100 +#define OXYGEN_INT_MCU 0x0200 +#define OXYGEN_INT_2WIRE 0x0400 +#define OXYGEN_INT_GPIO 0x0800 +#define OXYGEN_INT_MCB 0x2000 +#define OXYGEN_INT_AC97 0x4000 + +#define OXYGEN_INTERRUPT_STATUS 0x46 +/* OXYGEN_CHANNEL_* amd OXYGEN_INT_* */ +#define OXYGEN_INT_MIDI 0x1000 + +#define OXYGEN_MISC 0x48 +#define OXYGEN_MISC_WRITE_PCI_SUBID 0x01 +#define OXYGEN_MISC_LATENCY_3F 0x02 +#define OXYGEN_MISC_REC_C_FROM_SPDIF 0x04 +#define OXYGEN_MISC_REC_B_FROM_AC97 0x08 +#define OXYGEN_MISC_REC_A_FROM_MULTICH 0x10 +#define OXYGEN_MISC_PCI_MEM_W_1_CLOCK 0x20 +#define OXYGEN_MISC_MIDI 0x40 +#define OXYGEN_MISC_CRYSTAL_MASK 0x80 +#define OXYGEN_MISC_CRYSTAL_24576 0x00 +#define OXYGEN_MISC_CRYSTAL_27 0x80 /* MHz */ + +#define OXYGEN_REC_FORMAT 0x4a +#define OXYGEN_REC_FORMAT_A_MASK 0x03 +#define OXYGEN_REC_FORMAT_A_SHIFT 0 +#define OXYGEN_REC_FORMAT_B_MASK 0x0c +#define OXYGEN_REC_FORMAT_B_SHIFT 2 +#define OXYGEN_REC_FORMAT_C_MASK 0x30 +#define OXYGEN_REC_FORMAT_C_SHIFT 4 +#define OXYGEN_FORMAT_16 0x00 +#define OXYGEN_FORMAT_24 0x01 +#define OXYGEN_FORMAT_32 0x02 + +#define OXYGEN_PLAY_FORMAT 0x4b +#define OXYGEN_SPDIF_FORMAT_MASK 0x03 +#define OXYGEN_SPDIF_FORMAT_SHIFT 0 +#define OXYGEN_MULTICH_FORMAT_MASK 0x0c +#define OXYGEN_MULTICH_FORMAT_SHIFT 2 +/* OXYGEN_FORMAT_* */ + +#define OXYGEN_REC_CHANNELS 0x4c +#define OXYGEN_REC_CHANNELS_MASK 0x07 +#define OXYGEN_REC_CHANNELS_2_2_2 0x00 /* DMA A, B, C */ +#define OXYGEN_REC_CHANNELS_4_2_2 0x01 +#define OXYGEN_REC_CHANNELS_6_0_2 0x02 +#define OXYGEN_REC_CHANNELS_6_2_0 0x03 +#define OXYGEN_REC_CHANNELS_8_0_0 0x04 + +#define OXYGEN_FUNCTION 0x50 +#define OXYGEN_FUNCTION_CLOCK_MASK 0x01 +#define OXYGEN_FUNCTION_CLOCK_PLL 0x00 +#define OXYGEN_FUNCTION_CLOCK_CRYSTAL 0x01 +#define OXYGEN_FUNCTION_RESET_CODEC 0x02 +#define OXYGEN_FUNCTION_RESET_POL 0x04 +#define OXYGEN_FUNCTION_PWDN 0x08 +#define OXYGEN_FUNCTION_PWDN_EN 0x10 +#define OXYGEN_FUNCTION_PWDN_POL 0x20 +#define OXYGEN_FUNCTION_2WIRE_SPI_MASK 0x40 +#define OXYGEN_FUNCTION_SPI 0x00 +#define OXYGEN_FUNCTION_2WIRE 0x40 +#define OXYGEN_FUNCTION_ENABLE_SPI_4_5 0x80 /* 0 = EEPROM */ + +#define OXYGEN_I2S_MULTICH_FORMAT 0x60 +#define OXYGEN_I2S_RATE_MASK 0x0007 /* LRCK */ +#define OXYGEN_RATE_32000 0x0000 +#define OXYGEN_RATE_44100 0x0001 +#define OXYGEN_RATE_48000 0x0002 +#define OXYGEN_RATE_64000 0x0003 +#define OXYGEN_RATE_88200 0x0004 +#define OXYGEN_RATE_96000 0x0005 +#define OXYGEN_RATE_176400 0x0006 +#define OXYGEN_RATE_192000 0x0007 +#define OXYGEN_I2S_FORMAT_MASK 0x0008 +#define OXYGEN_I2S_FORMAT_I2S 0x0000 +#define OXYGEN_I2S_FORMAT_LJUST 0x0008 +#define OXYGEN_I2S_MCLK_MASK 0x0030 /* MCLK/LRCK */ +#define OXYGEN_I2S_MCLK_128 0x0000 +#define OXYGEN_I2S_MCLK_256 0x0010 +#define OXYGEN_I2S_MCLK_512 0x0020 +#define OXYGEN_I2S_BITS_MASK 0x00c0 +#define OXYGEN_I2S_BITS_16 0x0000 +#define OXYGEN_I2S_BITS_20 0x0040 +#define OXYGEN_I2S_BITS_24 0x0080 +#define OXYGEN_I2S_BITS_32 0x00c0 +#define OXYGEN_I2S_MASTER 0x0100 +#define OXYGEN_I2S_BCLK_MASK 0x0600 /* BCLK/LRCK */ +#define OXYGEN_I2S_BCLK_64 0x0000 +#define OXYGEN_I2S_BCLK_128 0x0200 +#define OXYGEN_I2S_BCLK_256 0x0400 +#define OXYGEN_I2S_MUTE_MCLK 0x0800 + +#define OXYGEN_I2S_A_FORMAT 0x62 +#define OXYGEN_I2S_B_FORMAT 0x64 +#define OXYGEN_I2S_C_FORMAT 0x66 +/* like OXYGEN_I2S_MULTICH_FORMAT */ + +#define OXYGEN_SPDIF_CONTROL 0x70 +#define OXYGEN_SPDIF_OUT_ENABLE 0x00000002 +#define OXYGEN_SPDIF_LOOPBACK 0x00000004 /* in to out */ +#define OXYGEN_SPDIF_SENSE_MASK 0x00000008 +#define OXYGEN_SPDIF_LOCK_MASK 0x00000010 +#define OXYGEN_SPDIF_RATE_MASK 0x00000020 +#define OXYGEN_SPDIF_SPDVALID 0x00000040 +#define OXYGEN_SPDIF_SENSE_PAR 0x00000200 +#define OXYGEN_SPDIF_LOCK_PAR 0x00000400 +#define OXYGEN_SPDIF_SENSE_STATUS 0x00000800 +#define OXYGEN_SPDIF_LOCK_STATUS 0x00001000 +#define OXYGEN_SPDIF_SENSE_INT 0x00002000 /* r/wc */ +#define OXYGEN_SPDIF_LOCK_INT 0x00004000 /* r/wc */ +#define OXYGEN_SPDIF_RATE_INT 0x00008000 /* r/wc */ +#define OXYGEN_SPDIF_IN_CLOCK_MASK 0x00010000 +#define OXYGEN_SPDIF_IN_CLOCK_96 0x00000000 /* <= 96 kHz */ +#define OXYGEN_SPDIF_IN_CLOCK_192 0x00010000 /* > 96 kHz */ +#define OXYGEN_SPDIF_OUT_RATE_MASK 0x07000000 +#define OXYGEN_SPDIF_OUT_RATE_SHIFT 24 +/* OXYGEN_RATE_* << OXYGEN_SPDIF_OUT_RATE_SHIFT */ + +#define OXYGEN_SPDIF_OUTPUT_BITS 0x74 +#define OXYGEN_SPDIF_NONAUDIO 0x00000002 +#define OXYGEN_SPDIF_C 0x00000004 +#define OXYGEN_SPDIF_PREEMPHASIS 0x00000008 +#define OXYGEN_SPDIF_CATEGORY_MASK 0x000007f0 +#define OXYGEN_SPDIF_CATEGORY_SHIFT 4 +#define OXYGEN_SPDIF_ORIGINAL 0x00000800 +#define OXYGEN_SPDIF_CS_RATE_MASK 0x0000f000 +#define OXYGEN_SPDIF_CS_RATE_SHIFT 12 +#define OXYGEN_SPDIF_V 0x00010000 /* 0 = valid */ + +#define OXYGEN_SPDIF_INPUT_BITS 0x78 +/* 32 bits, IEC958_AES_* */ + +#define OXYGEN_EEPROM_CONTROL 0x80 +#define OXYGEN_EEPROM_ADDRESS_MASK 0x7f +#define OXYGEN_EEPROM_DIR_MASK 0x80 +#define OXYGEN_EEPROM_DIR_READ 0x00 +#define OXYGEN_EEPROM_DIR_WRITE 0x80 + +#define OXYGEN_EEPROM_STATUS 0x81 +#define OXYGEN_EEPROM_VALID 0x40 +#define OXYGEN_EEPROM_BUSY 0x80 + +#define OXYGEN_EEPROM_DATA 0x82 /* 16 bits */ + +#define OXYGEN_2WIRE_CONTROL 0x90 +#define OXYGEN_2WIRE_DIR_MASK 0x01 +#define OXYGEN_2WIRE_DIR_WRITE 0x00 +#define OXYGEN_2WIRE_DIR_READ 0x01 +#define OXYGEN_2WIRE_ADDRESS_MASK 0xfe /* slave device address */ +#define OXYGEN_2WIRE_ADDRESS_SHIFT 1 + +#define OXYGEN_2WIRE_MAP 0x91 /* address, 8 bits */ +#define OXYGEN_2WIRE_DATA 0x92 /* data, 16 bits */ + +#define OXYGEN_2WIRE_BUS_STATUS 0x94 +#define OXYGEN_2WIRE_BUSY 0x0001 +#define OXYGEN_2WIRE_LENGTH_MASK 0x0002 +#define OXYGEN_2WIRE_LENGTH_8 0x0000 +#define OXYGEN_2WIRE_LENGTH_16 0x0002 +#define OXYGEN_2WIRE_MANUAL_READ 0x0004 /* 0 = auto read */ +#define OXYGEN_2WIRE_WRITE_MAP_ONLY 0x0008 +#define OXYGEN_2WIRE_SLAVE_AD_MASK 0x0030 /* AD0, AD1 */ +#define OXYGEN_2WIRE_INTERRUPT_MASK 0x0040 /* 0 = int. if not responding */ +#define OXYGEN_2WIRE_SLAVE_NO_RESPONSE 0x0080 +#define OXYGEN_2WIRE_SPEED_MASK 0x0100 +#define OXYGEN_2WIRE_SPEED_STANDARD 0x0000 +#define OXYGEN_2WIRE_SPEED_FAST 0x0100 +#define OXYGEN_2WIRE_CLOCK_SYNC 0x0200 +#define OXYGEN_2WIRE_BUS_RESET 0x0400 + +#define OXYGEN_SPI_CONTROL 0x98 +#define OXYGEN_SPI_BUSY 0x01 /* read */ +#define OXYGEN_SPI_TRIGGER 0x01 /* write */ +#define OXYGEN_SPI_DATA_LENGTH_MASK 0x02 +#define OXYGEN_SPI_DATA_LENGTH_2 0x00 +#define OXYGEN_SPI_DATA_LENGTH_3 0x02 +#define OXYGEN_SPI_CLOCK_MASK 0xc0 +#define OXYGEN_SPI_CLOCK_160 0x00 /* ns */ +#define OXYGEN_SPI_CLOCK_320 0x40 +#define OXYGEN_SPI_CLOCK_640 0x80 +#define OXYGEN_SPI_CLOCK_1280 0xc0 +#define OXYGEN_SPI_CODEC_MASK 0x70 /* 0..5 */ +#define OXYGEN_SPI_CODEC_SHIFT 4 +#define OXYGEN_SPI_CEN_MASK 0x80 +#define OXYGEN_SPI_CEN_LATCH_CLOCK_LO 0x00 +#define OXYGEN_SPI_CEN_LATCH_CLOCK_HI 0x80 + +#define OXYGEN_SPI_DATA1 0x99 +#define OXYGEN_SPI_DATA2 0x9a +#define OXYGEN_SPI_DATA3 0x9b + +#define OXYGEN_MPU401 0xa0 + +#define OXYGEN_MPU401_CONTROL 0xa2 +#define OXYGEN_MPU401_LOOPBACK 0x01 /* TXD to RXD */ + +#define OXYGEN_GPI_DATA 0xa4 +/* bits 0..5 = pin XGPI0..XGPI5 */ + +#define OXYGEN_GPI_INTERRUPT_MASK 0xa5 +/* bits 0..5, 1 = enable */ + +#define OXYGEN_GPIO_DATA 0xa6 +/* bits 0..9 */ + +#define OXYGEN_GPIO_CONTROL 0xa8 +/* bits 0..9, 0 = input, 1 = output */ +#define OXYGEN_GPIO1_XSLAVE_RDY 0x8000 + +#define OXYGEN_GPIO_INTERRUPT_MASK 0xaa +/* bits 0..9, 1 = enable */ + +#define OXYGEN_DEVICE_SENSE 0xac +#define OXYGEN_HEAD_PHONE_DETECT 0x01 +#define OXYGEN_HEAD_PHONE_MASK 0x06 +#define OXYGEN_HEAD_PHONE_PASSIVE_SPK 0x00 +#define OXYGEN_HEAD_PHONE_HP 0x02 +#define OXYGEN_HEAD_PHONE_ACTIVE_SPK 0x04 + +#define OXYGEN_MCU_2WIRE_DATA 0xb0 + +#define OXYGEN_MCU_2WIRE_MAP 0xb2 + +#define OXYGEN_MCU_2WIRE_STATUS 0xb3 +#define OXYGEN_MCU_2WIRE_BUSY 0x01 +#define OXYGEN_MCU_2WIRE_LENGTH_MASK 0x06 +#define OXYGEN_MCU_2WIRE_LENGTH_1 0x00 +#define OXYGEN_MCU_2WIRE_LENGTH_2 0x02 +#define OXYGEN_MCU_2WIRE_LENGTH_3 0x04 +#define OXYGEN_MCU_2WIRE_WRITE 0x08 /* r/wc */ +#define OXYGEN_MCU_2WIRE_READ 0x10 /* r/wc */ +#define OXYGEN_MCU_2WIRE_DRV_XACT_FAIL 0x20 /* r/wc */ +#define OXYGEN_MCU_2WIRE_RESET 0x40 + +#define OXYGEN_MCU_2WIRE_CONTROL 0xb4 +#define OXYGEN_MCU_2WIRE_DRV_ACK 0x01 +#define OXYGEN_MCU_2WIRE_DRV_XACT 0x02 +#define OXYGEN_MCU_2WIRE_INT_MASK 0x04 +#define OXYGEN_MCU_2WIRE_SYNC_MASK 0x08 +#define OXYGEN_MCU_2WIRE_SYNC_RDY_PIN 0x00 +#define OXYGEN_MCU_2WIRE_SYNC_DATA 0x08 +#define OXYGEN_MCU_2WIRE_ADDRESS_MASK 0x30 +#define OXYGEN_MCU_2WIRE_ADDRESS_10 0x00 +#define OXYGEN_MCU_2WIRE_ADDRESS_12 0x10 +#define OXYGEN_MCU_2WIRE_ADDRESS_14 0x20 +#define OXYGEN_MCU_2WIRE_ADDRESS_16 0x30 +#define OXYGEN_MCU_2WIRE_INT_POL 0x40 +#define OXYGEN_MCU_2WIRE_SYNC_ENABLE 0x80 + +#define OXYGEN_PLAY_ROUTING 0xc0 +#define OXYGEN_PLAY_MUTE01 0x0001 +#define OXYGEN_PLAY_MUTE23 0x0002 +#define OXYGEN_PLAY_MUTE45 0x0004 +#define OXYGEN_PLAY_MUTE67 0x0008 +#define OXYGEN_PLAY_MULTICH_MASK 0x0010 +#define OXYGEN_PLAY_MULTICH_I2S_DAC 0x0000 +#define OXYGEN_PLAY_MULTICH_AC97 0x0010 +#define OXYGEN_PLAY_SPDIF_MASK 0x00e0 +#define OXYGEN_PLAY_SPDIF_SPDIF 0x0000 +#define OXYGEN_PLAY_SPDIF_MULTICH_01 0x0020 +#define OXYGEN_PLAY_SPDIF_MULTICH_23 0x0040 +#define OXYGEN_PLAY_SPDIF_MULTICH_45 0x0060 +#define OXYGEN_PLAY_SPDIF_MULTICH_67 0x0080 +#define OXYGEN_PLAY_SPDIF_REC_A 0x00a0 +#define OXYGEN_PLAY_SPDIF_REC_B 0x00c0 +#define OXYGEN_PLAY_SPDIF_I2S_ADC_3 0x00e0 +#define OXYGEN_PLAY_DAC0_SOURCE_MASK 0x0300 +#define OXYGEN_PLAY_DAC0_SOURCE_SHIFT 8 +#define OXYGEN_PLAY_DAC1_SOURCE_MASK 0x0c00 +#define OXYGEN_PLAY_DAC1_SOURCE_SHIFT 10 +#define OXYGEN_PLAY_DAC2_SOURCE_MASK 0x3000 +#define OXYGEN_PLAY_DAC2_SOURCE_SHIFT 12 +#define OXYGEN_PLAY_DAC3_SOURCE_MASK 0xc000 +#define OXYGEN_PLAY_DAC3_SOURCE_SHIFT 14 + +#define OXYGEN_REC_ROUTING 0xc2 +#define OXYGEN_MUTE_I2S_ADC_1 0x01 +#define OXYGEN_MUTE_I2S_ADC_2 0x02 +#define OXYGEN_MUTE_I2S_ADC_3 0x04 +#define OXYGEN_REC_A_ROUTE_MASK 0x08 +#define OXYGEN_REC_A_ROUTE_I2S_ADC_1 0x00 +#define OXYGEN_REC_A_ROUTE_AC97_0 0x08 +#define OXYGEN_REC_B_ROUTE_MASK 0x10 +#define OXYGEN_REC_B_ROUTE_I2S_ADC_2 0x00 +#define OXYGEN_REC_B_ROUTE_AC97_1 0x10 +#define OXYGEN_REC_C_ROUTE_MASK 0x20 +#define OXYGEN_REC_C_ROUTE_SPDIF 0x00 +#define OXYGEN_REC_C_ROUTE_I2S_ADC_3 0x20 + +#define OXYGEN_ADC_MONITOR 0xc3 +#define OXYGEN_ADC_MONITOR_A 0x01 +#define OXYGEN_ADC_MONITOR_A_HALF_VOL 0x02 +#define OXYGEN_ADC_MONITOR_B 0x04 +#define OXYGEN_ADC_MONITOR_B_HALF_VOL 0x08 +#define OXYGEN_ADC_MONITOR_C 0x10 +#define OXYGEN_ADC_MONITOR_C_HALF_VOL 0x20 + +#define OXYGEN_A_MONITOR_ROUTING 0xc4 +#define OXYGEN_A_MONITOR_ROUTE_0_MASK 0x03 +#define OXYGEN_A_MONITOR_ROUTE_0_SHIFT 0 +#define OXYGEN_A_MONITOR_ROUTE_1_MASK 0x0c +#define OXYGEN_A_MONITOR_ROUTE_1_SHIFT 2 +#define OXYGEN_A_MONITOR_ROUTE_2_MASK 0x30 +#define OXYGEN_A_MONITOR_ROUTE_2_SHIFT 4 +#define OXYGEN_A_MONITOR_ROUTE_3_MASK 0xc0 +#define OXYGEN_A_MONITOR_ROUTE_3_SHIFT 6 + +#define OXYGEN_AC97_CONTROL 0xd0 +#define OXYGEN_AC97_COLD_RESET 0x0001 +#define OXYGEN_AC97_SUSPENDED 0x0002 /* read */ +#define OXYGEN_AC97_RESUME 0x0002 /* write */ +#define OXYGEN_AC97_CLOCK_DISABLE 0x0004 +#define OXYGEN_AC97_NO_CODEC_0 0x0008 +#define OXYGEN_AC97_CODEC_0 0x0010 +#define OXYGEN_AC97_CODEC_1 0x0020 + +#define OXYGEN_AC97_INTERRUPT_MASK 0xd2 +#define OXYGEN_AC97_INT_READ_DONE 0x01 +#define OXYGEN_AC97_INT_WRITE_DONE 0x02 +#define OXYGEN_AC97_INT_CODEC_0 0x10 +#define OXYGEN_AC97_INT_CODEC_1 0x20 + +#define OXYGEN_AC97_INTERRUPT_STATUS 0xd3 +/* OXYGEN_AC97_INT_* */ + +#define OXYGEN_AC97_OUT_CONFIG 0xd4 +#define OXYGEN_AC97_CODEC1_SLOT3 0x00000001 +#define OXYGEN_AC97_CODEC1_SLOT3_VSR 0x00000002 +#define OXYGEN_AC97_CODEC1_SLOT4 0x00000010 +#define OXYGEN_AC97_CODEC1_SLOT4_VSR 0x00000020 +#define OXYGEN_AC97_CODEC0_FRONTL 0x00000100 +#define OXYGEN_AC97_CODEC0_FRONTR 0x00000200 +#define OXYGEN_AC97_CODEC0_SIDEL 0x00000400 +#define OXYGEN_AC97_CODEC0_SIDER 0x00000800 +#define OXYGEN_AC97_CODEC0_CENTER 0x00001000 +#define OXYGEN_AC97_CODEC0_BASE 0x00002000 +#define OXYGEN_AC97_CODEC0_REARL 0x00004000 +#define OXYGEN_AC97_CODEC0_REARR 0x00008000 + +#define OXYGEN_AC97_IN_CONFIG 0xd8 +#define OXYGEN_AC97_CODEC1_LINEL 0x00000001 +#define OXYGEN_AC97_CODEC1_LINEL_VSR 0x00000002 +#define OXYGEN_AC97_CODEC1_LINEL_16 0x00000000 +#define OXYGEN_AC97_CODEC1_LINEL_18 0x00000004 +#define OXYGEN_AC97_CODEC1_LINEL_20 0x00000008 +#define OXYGEN_AC97_CODEC1_LINER 0x00000010 +#define OXYGEN_AC97_CODEC1_LINER_VSR 0x00000020 +#define OXYGEN_AC97_CODEC1_LINER_16 0x00000000 +#define OXYGEN_AC97_CODEC1_LINER_18 0x00000040 +#define OXYGEN_AC97_CODEC1_LINER_20 0x00000080 +#define OXYGEN_AC97_CODEC0_LINEL 0x00000100 +#define OXYGEN_AC97_CODEC0_LINER 0x00000200 + +#define OXYGEN_AC97_REGS 0xdc +#define OXYGEN_AC97_REG_DATA_MASK 0x0000ffff +#define OXYGEN_AC97_REG_ADDR_MASK 0x007f0000 +#define OXYGEN_AC97_REG_ADDR_SHIFT 16 +#define OXYGEN_AC97_REG_DIR_MASK 0x00800000 +#define OXYGEN_AC97_REG_DIR_WRITE 0x00000000 +#define OXYGEN_AC97_REG_DIR_READ 0x00800000 +#define OXYGEN_AC97_REG_CODEC_MASK 0x01000000 +#define OXYGEN_AC97_REG_CODEC_SHIFT 24 + +#define OXYGEN_TEST 0xe0 +#define OXYGEN_TEST_RAM_SUCCEEDED 0x01 +#define OXYGEN_TEST_PLAYBACK_RAM 0x02 +#define OXYGEN_TEST_RECORD_RAM 0x04 +#define OXYGEN_TEST_PLL 0x08 +#define OXYGEN_TEST_2WIRE_LOOPBACK 0x10 + +#define OXYGEN_DMA_FLUSH 0xe1 +/* OXYGEN_CHANNEL_* */ + +#define OXYGEN_CODEC_VERSION 0xe4 +#define OXYGEN_XCID_MASK 0x07 + +#define OXYGEN_REVISION 0xe6 +#define OXYGEN_REVISION_XPKGID_MASK 0x0007 +#define OXYGEN_REVISION_MASK 0xfff8 +#define OXYGEN_REVISION_2 0x0008 /* bit flag */ +#define OXYGEN_REVISION_8787 0x0014 /* 8 bits */ + +#define OXYGEN_OFFSIN_48K 0xe8 +#define OXYGEN_OFFSBASE_48K 0xe9 +#define OXYGEN_OFFSBASE_MASK 0x0fff +#define OXYGEN_OFFSIN_44K 0xec +#define OXYGEN_OFFSBASE_44K 0xed + +#endif diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c new file mode 100644 index 0000000..40e92f5 --- /dev/null +++ b/sound/pci/oxygen/virtuoso.c @@ -0,0 +1,449 @@ +/* + * C-Media CMI8788 driver for Asus Xonar cards + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * This driver 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 driver; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * CMI8788: + * + * SPI 0 -> 1st PCM1796 (front) + * SPI 1 -> 2nd PCM1796 (surround) + * SPI 2 -> 3rd PCM1796 (center/LFE) + * SPI 4 -> 4th PCM1796 (back) + * + * GPIO 2 -> M0 of CS5381 + * GPIO 3 -> M1 of CS5381 + * GPIO 5 <- external power present (D2X only) + * GPIO 7 -> ALT + * GPIO 8 -> enable output to speakers + * + * CM9780: + * + * GPIO 0 -> enable AC'97 bypass (line in -> ADC) + */ + +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <sound/ac97_codec.h> +#include <sound/control.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/tlv.h> +#include "oxygen.h" +#include "cm9780.h" + +MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); +MODULE_DESCRIPTION("Asus AV200 driver"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{Asus,AV200}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "card index"); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string"); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "enable card"); + +static struct pci_device_id xonar_ids[] __devinitdata = { + { OXYGEN_PCI_SUBID(0x1043, 0x8269) }, /* Asus Xonar D2 */ + { OXYGEN_PCI_SUBID(0x1043, 0x82b7) }, /* Asus Xonar D2X */ + { } +}; +MODULE_DEVICE_TABLE(pci, xonar_ids); + + +#define GPIO_CS5381_M_MASK 0x000c +#define GPIO_CS5381_M_SINGLE 0x0000 +#define GPIO_CS5381_M_DOUBLE 0x0004 +#define GPIO_CS5381_M_QUAD 0x0008 +#define GPIO_EXT_POWER 0x0020 +#define GPIO_ALT 0x0080 +#define GPIO_OUTPUT_ENABLE 0x0100 + +#define GPIO_LINE_MUTE CM9780_GPO0 + +/* register 16 */ +#define PCM1796_ATL_MASK 0xff +/* register 17 */ +#define PCM1796_ATR_MASK 0xff +/* register 18 */ +#define PCM1796_MUTE 0x01 +#define PCM1796_DME 0x02 +#define PCM1796_DMF_MASK 0x0c +#define PCM1796_DMF_DISABLED 0x00 +#define PCM1796_DMF_48 0x04 +#define PCM1796_DMF_441 0x08 +#define PCM1796_DMF_32 0x0c +#define PCM1796_FMT_MASK 0x70 +#define PCM1796_FMT_16_RJUST 0x00 +#define PCM1796_FMT_20_RJUST 0x10 +#define PCM1796_FMT_24_RJUST 0x20 +#define PCM1796_FMT_24_LJUST 0x30 +#define PCM1796_FMT_16_I2S 0x40 +#define PCM1796_FMT_24_I2S 0x50 +#define PCM1796_ATLD 0x80 +/* register 19 */ +#define PCM1796_INZD 0x01 +#define PCM1796_FLT_MASK 0x02 +#define PCM1796_FLT_SHARP 0x00 +#define PCM1796_FLT_SLOW 0x02 +#define PCM1796_DFMS 0x04 +#define PCM1796_OPE 0x10 +#define PCM1796_ATS_MASK 0x60 +#define PCM1796_ATS_1 0x00 +#define PCM1796_ATS_2 0x20 +#define PCM1796_ATS_4 0x40 +#define PCM1796_ATS_8 0x60 +#define PCM1796_REV 0x80 +/* register 20 */ +#define PCM1796_OS_MASK 0x03 +#define PCM1796_OS_64 0x00 +#define PCM1796_OS_32 0x01 +#define PCM1796_OS_128 0x02 +#define PCM1796_CHSL_MASK 0x04 +#define PCM1796_CHSL_LEFT 0x00 +#define PCM1796_CHSL_RIGHT 0x04 +#define PCM1796_MONO 0x08 +#define PCM1796_DFTH 0x10 +#define PCM1796_DSD 0x20 +#define PCM1796_SRST 0x40 +/* register 21 */ +#define PCM1796_PCMZ 0x01 +#define PCM1796_DZ_MASK 0x06 +/* register 22 */ +#define PCM1796_ZFGL 0x01 +#define PCM1796_ZFGR 0x02 +/* register 23 */ +#define PCM1796_ID_MASK 0x1f + +struct xonar_data { + u8 is_d2x; + u8 has_power; +}; + +static void pcm1796_write(struct oxygen *chip, unsigned int codec, + u8 reg, u8 value) +{ + /* maps ALSA channel pair number to SPI output */ + static const u8 codec_map[4] = { + 0, 1, 2, 4 + }; + oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER | + OXYGEN_SPI_DATA_LENGTH_2 | + OXYGEN_SPI_CLOCK_160 | + (codec_map[codec] << OXYGEN_SPI_CODEC_SHIFT) | + OXYGEN_SPI_CEN_LATCH_CLOCK_HI, + (reg << 8) | value); +} + +static void xonar_init(struct oxygen *chip) +{ + struct xonar_data *data = chip->model_data; + unsigned int i; + + data->is_d2x = chip->pci->subsystem_device == 0x82b7; + + for (i = 0; i < 4; ++i) { + pcm1796_write(chip, i, 18, PCM1796_FMT_24_LJUST | PCM1796_ATLD); + pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1); + pcm1796_write(chip, i, 20, PCM1796_OS_64); + pcm1796_write(chip, i, 21, 0); + pcm1796_write(chip, i, 16, 0xff); /* set ATL/ATR after ATLD */ + pcm1796_write(chip, i, 17, 0xff); + } + + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, + GPIO_CS5381_M_MASK | GPIO_ALT); + oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, + GPIO_CS5381_M_SINGLE, + GPIO_CS5381_M_MASK | GPIO_ALT); + if (data->is_d2x) { + oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, + GPIO_EXT_POWER); + oxygen_set_bits16(chip, OXYGEN_GPIO_INTERRUPT_MASK, + GPIO_EXT_POWER); + chip->interrupt_mask |= OXYGEN_INT_GPIO; + data->has_power = !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) + & GPIO_EXT_POWER); + } + oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC); + oxygen_ac97_clear_bits(chip, 0, CM9780_GPIO_STATUS, GPIO_LINE_MUTE); + msleep(300); + oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_OUTPUT_ENABLE); + oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); + + snd_component_add(chip->card, "PCM1796"); + snd_component_add(chip->card, "CS5381"); +} + +static void xonar_cleanup(struct oxygen *chip) +{ + oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE); +} + +static void set_pcm1796_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ +#if 0 + unsigned int i; + u8 value; + + value = params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64; + for (i = 0; i < 4; ++i) + pcm1796_write(chip, i, 20, value); +#endif +} + +static void update_pcm1796_volume(struct oxygen *chip) +{ + unsigned int i; + + for (i = 0; i < 4; ++i) { + pcm1796_write(chip, i, 16, chip->dac_volume[i * 2]); + pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1]); + } +} + +static void update_pcm1796_mute(struct oxygen *chip) +{ + unsigned int i; + u8 value; + + value = PCM1796_FMT_24_LJUST | PCM1796_ATLD; + if (chip->dac_mute) + value |= PCM1796_MUTE; + for (i = 0; i < 4; ++i) + pcm1796_write(chip, i, 18, value); +} + +static void set_cs5381_params(struct oxygen *chip, + struct snd_pcm_hw_params *params) +{ + unsigned int value; + + if (params_rate(params) <= 54000) + value = GPIO_CS5381_M_SINGLE; + else if (params_rate(params) <= 108000) + value = GPIO_CS5381_M_DOUBLE; + else + value = GPIO_CS5381_M_QUAD; + oxygen_write16_masked(chip, OXYGEN_GPIO_DATA, + value, GPIO_CS5381_M_MASK); +} + +static void xonar_gpio_changed(struct oxygen *chip) +{ + struct xonar_data *data = chip->model_data; + u8 has_power; + + if (!data->is_d2x) + return; + has_power = !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) + & GPIO_EXT_POWER); + if (has_power != data->has_power) { + data->has_power = has_power; + if (has_power) { + snd_printk(KERN_NOTICE "power restored\n"); + } else { + snd_printk(KERN_CRIT + "Hey! Don't unplug the power cable!\n"); + /* TODO: stop PCMs */ + } + } +} + +static void mute_ac97_ctl(struct oxygen *chip, unsigned int control) +{ + unsigned int index = chip->controls[control]->private_value & 0xff; + u16 value; + + value = oxygen_read_ac97(chip, 0, index); + if (!(value & 0x8000)) { + oxygen_write_ac97(chip, 0, index, value | 0x8000); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->controls[control]->id); + } +} + +static void xonar_ac97_switch_hook(struct oxygen *chip, unsigned int codec, + unsigned int reg, int mute) +{ + if (codec != 0) + return; + /* line-in is exclusive */ + switch (reg) { + case AC97_LINE: + oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS, + mute ? GPIO_LINE_MUTE : 0, + GPIO_LINE_MUTE); + if (!mute) { + mute_ac97_ctl(chip, CONTROL_MIC_CAPTURE_SWITCH); + mute_ac97_ctl(chip, CONTROL_CD_CAPTURE_SWITCH); + mute_ac97_ctl(chip, CONTROL_AUX_CAPTURE_SWITCH); + } + break; + case AC97_MIC: + case AC97_CD: + case AC97_VIDEO: + case AC97_AUX: + if (!mute) { + oxygen_ac97_set_bits(chip, 0, CM9780_GPIO_STATUS, + GPIO_LINE_MUTE); + mute_ac97_ctl(chip, CONTROL_LINE_CAPTURE_SWITCH); + } + break; + } +} + +static int pcm1796_volume_info(struct snd_kcontrol *ctl, + struct snd_ctl_elem_info *info) +{ + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = 8; + info->value.integer.min = 0x0f; + info->value.integer.max = 0xff; + return 0; +} + +static int alt_switch_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + + value->value.integer.value[0] = + !!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_ALT); + return 0; +} + +static int alt_switch_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + u16 old_bits, new_bits; + int changed; + + spin_lock_irq(&chip->reg_lock); + old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA); + if (value->value.integer.value[0]) + new_bits = old_bits | GPIO_ALT; + else + new_bits = old_bits & ~GPIO_ALT; + changed = new_bits != old_bits; + if (changed) + oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits); + spin_unlock_irq(&chip->reg_lock); + return changed; +} + +static const struct snd_kcontrol_new alt_switch = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Loopback Switch", + .info = snd_ctl_boolean_mono_info, + .get = alt_switch_get, + .put = alt_switch_put, +}; + +static const DECLARE_TLV_DB_SCALE(pcm1796_db_scale, -12000, 50, 0); + +static int xonar_control_filter(struct snd_kcontrol_new *template) +{ + if (!strcmp(template->name, "Master Playback Volume")) { + template->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; + template->info = pcm1796_volume_info, + template->tlv.p = pcm1796_db_scale; + } else if (!strncmp(template->name, "CD Capture ", 11)) { + /* CD in is actually connected to the video in pin */ + template->private_value ^= AC97_CD ^ AC97_VIDEO; + } else if (!strcmp(template->name, "Line Capture Volume")) { + return 1; /* line-in bypasses the AC'97 mixer */ + } + return 0; +} + +static int xonar_mixer_init(struct oxygen *chip) +{ + return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip)); +} + +static const struct oxygen_model model_xonar = { + .shortname = "Asus AV200", + .longname = "Asus Virtuoso 200", + .chip = "AV200", + .init = xonar_init, + .control_filter = xonar_control_filter, + .mixer_init = xonar_mixer_init, + .cleanup = xonar_cleanup, + .set_dac_params = set_pcm1796_params, + .set_adc_params = set_cs5381_params, + .update_dac_volume = update_pcm1796_volume, + .update_dac_mute = update_pcm1796_mute, + .ac97_switch_hook = xonar_ac97_switch_hook, + .gpio_changed = xonar_gpio_changed, + .model_data_size = sizeof(struct xonar_data), + .dac_channels = 8, + .used_channels = OXYGEN_CHANNEL_B | + OXYGEN_CHANNEL_C | + OXYGEN_CHANNEL_SPDIF | + OXYGEN_CHANNEL_MULTICH, + .function_flags = OXYGEN_FUNCTION_ENABLE_SPI_4_5, + .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, + .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +}; + +static int __devinit xonar_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + ++dev; + return -ENOENT; + } + err = oxygen_pci_probe(pci, index[dev], id[dev], 1, &model_xonar); + if (err >= 0) + ++dev; + return err; +} + +static struct pci_driver xonar_driver = { + .name = "AV200", + .id_table = xonar_ids, + .probe = xonar_probe, + .remove = __devexit_p(oxygen_pci_remove), +}; + +static int __init alsa_card_xonar_init(void) +{ + return pci_register_driver(&xonar_driver); +} + +static void __exit alsa_card_xonar_exit(void) +{ + pci_unregister_driver(&xonar_driver); +} + +module_init(alsa_card_xonar_init) +module_exit(alsa_card_xonar_exit) diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c index 2d618bd..9d5bb76 100644 --- a/sound/pci/pcxhr/pcxhr.c +++ b/sound/pci/pcxhr/pcxhr.c @@ -21,7 +21,6 @@ */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/slab.h> diff --git a/sound/pci/pcxhr/pcxhr_core.c b/sound/pci/pcxhr/pcxhr_core.c index 0ff8dc3..c4e415d 100644 --- a/sound/pci/pcxhr/pcxhr_core.c +++ b/sound/pci/pcxhr/pcxhr_core.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/firmware.h> #include <linux/interrupt.h> diff --git a/sound/pci/pcxhr/pcxhr_hwdep.c b/sound/pci/pcxhr/pcxhr_hwdep.c index d55d8bc..e6a4bfb 100644 --- a/sound/pci/pcxhr/pcxhr_hwdep.c +++ b/sound/pci/pcxhr/pcxhr_hwdep.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/interrupt.h> #include <linux/vmalloc.h> #include <linux/firmware.h> diff --git a/sound/pci/pcxhr/pcxhr_mixer.c b/sound/pci/pcxhr/pcxhr_mixer.c index 5f8d426..aabc7bc 100644 --- a/sound/pci/pcxhr/pcxhr_mixer.c +++ b/sound/pci/pcxhr/pcxhr_mixer.c @@ -21,7 +21,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/time.h> #include <linux/interrupt.h> #include <linux/init.h> @@ -120,8 +119,18 @@ static int pcxhr_analog_vol_put(struct snd_kcontrol *kcontrol, is_capture = (kcontrol->private_value != 0); for (i = 0; i < 2; i++) { int new_volume = ucontrol->value.integer.value[i]; - int* stored_volume = is_capture ? &chip->analog_capture_volume[i] : + int *stored_volume = is_capture ? + &chip->analog_capture_volume[i] : &chip->analog_playback_volume[i]; + if (is_capture) { + if (new_volume < PCXHR_ANALOG_CAPTURE_LEVEL_MIN || + new_volume > PCXHR_ANALOG_CAPTURE_LEVEL_MAX) + continue; + } else { + if (new_volume < PCXHR_ANALOG_PLAYBACK_LEVEL_MIN || + new_volume > PCXHR_ANALOG_PLAYBACK_LEVEL_MAX) + continue; + } if (*stored_volume != new_volume) { *stored_volume = new_volume; changed = 1; @@ -165,10 +174,13 @@ static int pcxhr_audio_sw_put(struct snd_kcontrol *kcontrol, int i, changed = 0; mutex_lock(&chip->mgr->mixer_mutex); for(i = 0; i < 2; i++) { - if (chip->analog_playback_active[i] != ucontrol->value.integer.value[i]) { - chip->analog_playback_active[i] = ucontrol->value.integer.value[i]; + if (chip->analog_playback_active[i] != + ucontrol->value.integer.value[i]) { + chip->analog_playback_active[i] = + !!ucontrol->value.integer.value[i]; changed = 1; - pcxhr_update_analog_audio_level(chip, 0, i); /* update playback levels */ + /* update playback levels */ + pcxhr_update_analog_audio_level(chip, 0, i); } } mutex_unlock(&chip->mgr->mixer_mutex); @@ -323,20 +335,24 @@ static int pcxhr_pcm_vol_put(struct snd_kcontrol *kcontrol, int i; mutex_lock(&chip->mgr->mixer_mutex); - if (is_capture) - stored_volume = chip->digital_capture_volume; /* digital capture */ - else - stored_volume = chip->digital_playback_volume[idx]; /* digital playback */ + if (is_capture) /* digital capture */ + stored_volume = chip->digital_capture_volume; + else /* digital playback */ + stored_volume = chip->digital_playback_volume[idx]; for (i = 0; i < 2; i++) { - if (stored_volume[i] != ucontrol->value.integer.value[i]) { - stored_volume[i] = ucontrol->value.integer.value[i]; + int vol = ucontrol->value.integer.value[i]; + if (vol < PCXHR_DIGITAL_LEVEL_MIN || + vol > PCXHR_DIGITAL_LEVEL_MAX) + continue; + if (stored_volume[i] != vol) { + stored_volume[i] = vol; changed = 1; if (is_capture) /* update capture volume */ pcxhr_update_audio_pipe_level(chip, 1, i); } } - if (! is_capture && changed) - pcxhr_update_playback_stream_level(chip, idx); /* update playback volume */ + if (!is_capture && changed) /* update playback volume */ + pcxhr_update_playback_stream_level(chip, idx); mutex_unlock(&chip->mgr->mixer_mutex); return changed; } @@ -378,8 +394,10 @@ static int pcxhr_pcm_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v mutex_lock(&chip->mgr->mixer_mutex); j = idx; for (i = 0; i < 2; i++) { - if (chip->digital_playback_active[j][i] != ucontrol->value.integer.value[i]) { - chip->digital_playback_active[j][i] = ucontrol->value.integer.value[i]; + if (chip->digital_playback_active[j][i] != + ucontrol->value.integer.value[i]) { + chip->digital_playback_active[j][i] = + !!ucontrol->value.integer.value[i]; changed = 1; } } @@ -423,10 +441,13 @@ static int pcxhr_monitor_vol_put(struct snd_kcontrol *kcontrol, mutex_lock(&chip->mgr->mixer_mutex); for (i = 0; i < 2; i++) { - if (chip->monitoring_volume[i] != ucontrol->value.integer.value[i]) { - chip->monitoring_volume[i] = ucontrol->value.integer.value[i]; - if(chip->monitoring_active[i]) /* do only when monitoring is unmuted */ + if (chip->monitoring_volume[i] != + ucontrol->value.integer.value[i]) { + chip->monitoring_volume[i] = + !!ucontrol->value.integer.value[i]; + if(chip->monitoring_active[i]) /* update monitoring volume and mute */ + /* do only when monitoring is unmuted */ pcxhr_update_audio_pipe_level(chip, 0, i); changed = 1; } @@ -470,15 +491,17 @@ static int pcxhr_monitor_sw_put(struct snd_kcontrol *kcontrol, mutex_lock(&chip->mgr->mixer_mutex); for (i = 0; i < 2; i++) { - if (chip->monitoring_active[i] != ucontrol->value.integer.value[i]) { - chip->monitoring_active[i] = ucontrol->value.integer.value[i]; + if (chip->monitoring_active[i] != + ucontrol->value.integer.value[i]) { + chip->monitoring_active[i] = + !!ucontrol->value.integer.value[i]; changed |= (1<<i); /* mask 0x01 and 0x02 */ } } - if(changed & 0x01) + if (changed & 0x01) /* update left monitoring volume and mute */ pcxhr_update_audio_pipe_level(chip, 0, 0); - if(changed & 0x02) + if (changed & 0x02) /* update right monitoring volume and mute */ pcxhr_update_audio_pipe_level(chip, 0, 1); @@ -579,6 +602,8 @@ static int pcxhr_audio_src_put(struct snd_kcontrol *kcontrol, struct snd_pcxhr *chip = snd_kcontrol_chip(kcontrol); int ret = 0; + if (ucontrol->value.enumerated.item[0] >= 3) + return -EINVAL; mutex_lock(&chip->mgr->mixer_mutex); if (chip->audio_capture_source != ucontrol->value.enumerated.item[0]) { chip->audio_capture_source = ucontrol->value.enumerated.item[0]; @@ -642,8 +667,11 @@ static int pcxhr_clock_type_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct pcxhr_mgr *mgr = snd_kcontrol_chip(kcontrol); + unsigned int clock_items = 3 + mgr->capture_chips; int rate, ret = 0; + if (ucontrol->value.enumerated.item[0] >= clock_items) + return -EINVAL; mutex_lock(&mgr->mixer_mutex); if (mgr->use_clock_type != ucontrol->value.enumerated.item[0]) { mutex_lock(&mgr->setup_mutex); diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index 8e54104..9408b1e 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -88,7 +88,6 @@ Adopted for Windows NT driver 01/20/98 CNL */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index 1475912..df184aa 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -69,7 +69,6 @@ */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 0b3c532..fb0a4ee 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -23,7 +23,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -2195,22 +2194,25 @@ snd_rme96_dac_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_valu { struct rme96 *rme96 = snd_kcontrol_chip(kcontrol); int change = 0; + unsigned int vol, maxvol; - if (!RME96_HAS_ANALOG_OUT(rme96)) { + + if (!RME96_HAS_ANALOG_OUT(rme96)) return -EINVAL; - } + maxvol = RME96_185X_MAX_OUT(rme96); spin_lock_irq(&rme96->lock); - if (u->value.integer.value[0] != rme96->vol[0]) { - rme96->vol[0] = u->value.integer.value[0]; - change = 1; - } - if (u->value.integer.value[1] != rme96->vol[1]) { - rme96->vol[1] = u->value.integer.value[1]; - change = 1; - } - if (change) { - snd_rme96_apply_dac_volume(rme96); + vol = u->value.integer.value[0]; + if (vol != rme96->vol[0] && vol <= maxvol) { + rme96->vol[0] = vol; + change = 1; + } + vol = u->value.integer.value[1]; + if (vol != rme96->vol[1] && vol <= maxvol) { + rme96->vol[1] = vol; + change = 1; } + if (change) + snd_rme96_apply_dac_volume(rme96); spin_unlock_irq(&rme96->lock); return change; diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index ff26a36..c2bd438 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -104,8 +103,6 @@ MODULE_FIRMWARE("digiface_firmware_rev11.bin"); #define HDSP_statusRegister 0 #define HDSP_timecode 128 #define HDSP_status2Register 192 -#define HDSP_midiDataOut0 352 -#define HDSP_midiDataOut1 356 #define HDSP_midiDataIn0 360 #define HDSP_midiDataIn1 364 #define HDSP_midiStatusOut0 384 @@ -610,7 +607,10 @@ static int hdsp_playback_to_output_key (struct hdsp *hdsp, int in, int out) case Multiface: case Digiface: default: - return (64 * out) + (32 + (in)); + if (hdsp->firmware_rev == 0xa) + return (64 * out) + (32 + (in)); + else + return (52 * out) + (26 + (in)); case H9632: return (32 * out) + (16 + (in)); case H9652: @@ -624,7 +624,10 @@ static int hdsp_input_to_output_key (struct hdsp *hdsp, int in, int out) case Multiface: case Digiface: default: - return (64 * out) + in; + if (hdsp->firmware_rev == 0xa) + return (64 * out) + in; + else + return (52 * out) + in; case H9632: return (32 * out) + in; case H9652: @@ -2121,7 +2124,7 @@ static int snd_hdsp_put_clock_source_lock(struct snd_kcontrol *kcontrol, struct change = (int)ucontrol->value.integer.value[0] != hdsp->clock_source_locked; if (change) - hdsp->clock_source_locked = ucontrol->value.integer.value[0]; + hdsp->clock_source_locked = !!ucontrol->value.integer.value[0]; return change; } @@ -3558,7 +3561,7 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) } -static void __devinit snd_hdsp_proc_init(struct hdsp *hdsp) +static void snd_hdsp_proc_init(struct hdsp *hdsp) { struct snd_info_entry *entry; @@ -3606,7 +3609,7 @@ static int snd_hdsp_set_defaults(struct hdsp *hdsp) /* ASSUMPTION: hdsp->lock is either held, or there is no need to hold it (e.g. during module - initalization). + initialization). */ /* set defaults: diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index f1bdda6..9a19ae6 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -23,7 +23,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -3348,7 +3347,7 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm) unsigned int i; /* ASSUMPTION: hdspm->lock is either held, or there is no need to - hold it (e.g. during module initalization). + hold it (e.g. during module initialization). */ /* set defaults: */ @@ -3416,7 +3415,7 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm) /*------------------------------------------------------------ - interupt + interrupt ------------------------------------------------------------*/ static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id) diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index 34f96f1..a123f0e 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -20,7 +20,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -148,7 +147,7 @@ MODULE_SUPPORTED_DEVICE("{{RME,Hammerfall}," #define RME9652_start_bit (1<<0) /* start record/play */ /* bits 1-3 encode buffersize/latency */ #define RME9652_Master (1<<4) /* Clock Mode Master=1,Slave/Auto=0 */ -#define RME9652_IE (1<<5) /* Interupt Enable */ +#define RME9652_IE (1<<5) /* Interrupt Enable */ #define RME9652_freq (1<<6) /* samplerate 0=44.1/88.2, 1=48/96 kHz */ #define RME9652_freq1 (1<<7) /* if 0, 32kHz, else always 1 */ #define RME9652_DS (1<<8) /* Doule Speed 0=44.1/48, 1=88.2/96 Khz */ @@ -1826,7 +1825,7 @@ static void snd_rme9652_set_defaults(struct snd_rme9652 *rme9652) /* ASSUMPTION: rme9652->lock is either held, or there is no need to hold it (e.g. during module - initalization). + initialization). */ /* set defaults: diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c new file mode 100644 index 0000000..dcd7cd0 --- /dev/null +++ b/sound/pci/sis7019.c @@ -0,0 +1,1460 @@ +/* + * Driver for SiS7019 Audio Accelerator + * + * Copyright (C) 2004-2007, David Dillow + * Written by David Dillow <dave@thedillows.org> + * Inspired by the Trident 4D-WaveDX/NX driver. + * + * All rights reserved. + * + * 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, version 2. + * + * 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/init.h> +#include <linux/pci.h> +#include <linux/time.h> +#include <linux/moduleparam.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <sound/core.h> +#include <sound/ac97_codec.h> +#include <sound/initval.h> +#include "sis7019.h" + +MODULE_AUTHOR("David Dillow <dave@thedillows.org>"); +MODULE_DESCRIPTION("SiS7019"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{SiS,SiS7019 Audio Accelerator}}"); + +static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ +static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ +static int enable = 1; + +module_param(index, int, 0444); +MODULE_PARM_DESC(index, "Index value for SiS7019 Audio Accelerator."); +module_param(id, charp, 0444); +MODULE_PARM_DESC(id, "ID string for SiS7019 Audio Accelerator."); +module_param(enable, bool, 0444); +MODULE_PARM_DESC(enable, "Enable SiS7019 Audio Accelerator."); + +static struct pci_device_id snd_sis7019_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_SI, 0x7019) }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_sis7019_ids); + +/* There are three timing modes for the voices. + * + * For both playback and capture, when the buffer is one or two periods long, + * we use the hardware's built-in Mid-Loop Interrupt and End-Loop Interrupt + * to let us know when the periods have ended. + * + * When performing playback with more than two periods per buffer, we set + * the "Stop Sample Offset" and tell the hardware to interrupt us when we + * reach it. We then update the offset and continue on until we are + * interrupted for the next period. + * + * Capture channels do not have a SSO, so we allocate a playback channel to + * use as a timer for the capture periods. We use the SSO on the playback + * channel to clock out virtual periods, and adjust the virtual period length + * to maintain synchronization. This algorithm came from the Trident driver. + * + * FIXME: It'd be nice to make use of some of the synth features in the + * hardware, but a woeful lack of documentation is a significant roadblock. + */ +struct voice { + u16 flags; +#define VOICE_IN_USE 1 +#define VOICE_CAPTURE 2 +#define VOICE_SSO_TIMING 4 +#define VOICE_SYNC_TIMING 8 + u16 sync_cso; + u16 period_size; + u16 buffer_size; + u16 sync_period_size; + u16 sync_buffer_size; + u32 sso; + u32 vperiod; + struct snd_pcm_substream *substream; + struct voice *timing; + void __iomem *ctrl_base; + void __iomem *wave_base; + void __iomem *sync_base; + int num; +}; + +/* We need four pages to store our wave parameters during a suspend. If + * we're not doing power management, we still need to allocate a page + * for the silence buffer. + */ +#ifdef CONFIG_PM +#define SIS_SUSPEND_PAGES 4 +#else +#define SIS_SUSPEND_PAGES 1 +#endif + +struct sis7019 { + unsigned long ioport; + void __iomem *ioaddr; + int irq; + int codecs_present; + + struct pci_dev *pci; + struct snd_pcm *pcm; + struct snd_card *card; + struct snd_ac97 *ac97[3]; + + /* Protect against more than one thread hitting the AC97 + * registers (in a more polite manner than pounding the hardware + * semaphore) + */ + struct mutex ac97_mutex; + + /* voice_lock protects allocation/freeing of the voice descriptions + */ + spinlock_t voice_lock; + + struct voice voices[64]; + struct voice capture_voice; + + /* Allocate pages to store the internal wave state during + * suspends. When we're operating, this can be used as a silence + * buffer for a timing channel. + */ + void *suspend_state[SIS_SUSPEND_PAGES]; + + int silence_users; + dma_addr_t silence_dma_addr; +}; + +#define SIS_PRIMARY_CODEC_PRESENT 0x0001 +#define SIS_SECONDARY_CODEC_PRESENT 0x0002 +#define SIS_TERTIARY_CODEC_PRESENT 0x0004 + +/* The HW offset parameters (Loop End, Stop Sample, End Sample) have a + * documented range of 8-0xfff8 samples. Given that they are 0-based, + * that places our period/buffer range at 9-0xfff9 samples. That makes the + * max buffer size 0xfff9 samples * 2 channels * 2 bytes per sample, and + * max samples / min samples gives us the max periods in a buffer. + * + * We'll add a constraint upon open that limits the period and buffer sample + * size to values that are legal for the hardware. + */ +static struct snd_pcm_hardware sis_playback_hw_info = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_RESUME), + .formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE), + .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (0xfff9 * 4), + .period_bytes_min = 9, + .period_bytes_max = (0xfff9 * 4), + .periods_min = 1, + .periods_max = (0xfff9 / 9), +}; + +static struct snd_pcm_hardware sis_capture_hw_info = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_RESUME), + .formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE), + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (0xfff9 * 4), + .period_bytes_min = 9, + .period_bytes_max = (0xfff9 * 4), + .periods_min = 1, + .periods_max = (0xfff9 / 9), +}; + +static void sis_update_sso(struct voice *voice, u16 period) +{ + void __iomem *base = voice->ctrl_base; + + voice->sso += period; + if (voice->sso >= voice->buffer_size) + voice->sso -= voice->buffer_size; + + /* Enforce the documented hardware minimum offset */ + if (voice->sso < 8) + voice->sso = 8; + + /* The SSO is in the upper 16 bits of the register. */ + writew(voice->sso & 0xffff, base + SIS_PLAY_DMA_SSO_ESO + 2); +} + +static void sis_update_voice(struct voice *voice) +{ + if (voice->flags & VOICE_SSO_TIMING) { + sis_update_sso(voice, voice->period_size); + } else if (voice->flags & VOICE_SYNC_TIMING) { + int sync; + + /* If we've not hit the end of the virtual period, update + * our records and keep going. + */ + if (voice->vperiod > voice->period_size) { + voice->vperiod -= voice->period_size; + if (voice->vperiod < voice->period_size) + sis_update_sso(voice, voice->vperiod); + else + sis_update_sso(voice, voice->period_size); + return; + } + + /* Calculate our relative offset between the target and + * the actual CSO value. Since we're operating in a loop, + * if the value is more than half way around, we can + * consider ourselves wrapped. + */ + sync = voice->sync_cso; + sync -= readw(voice->sync_base + SIS_CAPTURE_DMA_FORMAT_CSO); + if (sync > (voice->sync_buffer_size / 2)) + sync -= voice->sync_buffer_size; + + /* If sync is positive, then we interrupted too early, and + * we'll need to come back in a few samples and try again. + * There's a minimum wait, as it takes some time for the DMA + * engine to startup, etc... + */ + if (sync > 0) { + if (sync < 16) + sync = 16; + sis_update_sso(voice, sync); + return; + } + + /* Ok, we interrupted right on time, or (hopefully) just + * a bit late. We'll adjst our next waiting period based + * on how close we got. + * + * We need to stay just behind the actual channel to ensure + * it really is past a period when we get our interrupt -- + * otherwise we'll fall into the early code above and have + * a minimum wait time, which makes us quite late here, + * eating into the user's time to refresh the buffer, esp. + * if using small periods. + * + * If we're less than 9 samples behind, we're on target. + */ + if (sync > -9) + voice->vperiod = voice->sync_period_size + 1; + else + voice->vperiod = voice->sync_period_size - 4; + + if (voice->vperiod < voice->buffer_size) { + sis_update_sso(voice, voice->vperiod); + voice->vperiod = 0; + } else + sis_update_sso(voice, voice->period_size); + + sync = voice->sync_cso + voice->sync_period_size; + if (sync >= voice->sync_buffer_size) + sync -= voice->sync_buffer_size; + voice->sync_cso = sync; + } + + snd_pcm_period_elapsed(voice->substream); +} + +static void sis_voice_irq(u32 status, struct voice *voice) +{ + int bit; + + while (status) { + bit = __ffs(status); + status >>= bit + 1; + voice += bit; + sis_update_voice(voice); + voice++; + } +} + +static irqreturn_t sis_interrupt(int irq, void *dev) +{ + struct sis7019 *sis = dev; + unsigned long io = sis->ioport; + struct voice *voice; + u32 intr, status; + + /* We only use the DMA interrupts, and we don't enable any other + * source of interrupts. But, it is possible to see an interupt + * status that didn't actually interrupt us, so eliminate anything + * we're not expecting to avoid falsely claiming an IRQ, and an + * ensuing endless loop. + */ + intr = inl(io + SIS_GISR); + intr &= SIS_GISR_AUDIO_PLAY_DMA_IRQ_STATUS | + SIS_GISR_AUDIO_RECORD_DMA_IRQ_STATUS; + if (!intr) + return IRQ_NONE; + + do { + status = inl(io + SIS_PISR_A); + if (status) { + sis_voice_irq(status, sis->voices); + outl(status, io + SIS_PISR_A); + } + + status = inl(io + SIS_PISR_B); + if (status) { + sis_voice_irq(status, &sis->voices[32]); + outl(status, io + SIS_PISR_B); + } + + status = inl(io + SIS_RISR); + if (status) { + voice = &sis->capture_voice; + if (!voice->timing) + snd_pcm_period_elapsed(voice->substream); + + outl(status, io + SIS_RISR); + } + + outl(intr, io + SIS_GISR); + intr = inl(io + SIS_GISR); + intr &= SIS_GISR_AUDIO_PLAY_DMA_IRQ_STATUS | + SIS_GISR_AUDIO_RECORD_DMA_IRQ_STATUS; + } while (intr); + + return IRQ_HANDLED; +} + +static u32 sis_rate_to_delta(unsigned int rate) +{ + u32 delta; + + /* This was copied from the trident driver, but it seems its gotten + * around a bit... nevertheless, it works well. + * + * We special case 44100 and 8000 since rounding with the equation + * does not give us an accurate enough value. For 11025 and 22050 + * the equation gives us the best answer. All other frequencies will + * also use the equation. JDW + */ + if (rate == 44100) + delta = 0xeb3; + else if (rate == 8000) + delta = 0x2ab; + else if (rate == 48000) + delta = 0x1000; + else + delta = (((rate << 12) + 24000) / 48000) & 0x0000ffff; + return delta; +} + +static void __sis_map_silence(struct sis7019 *sis) +{ + /* Helper function: must hold sis->voice_lock on entry */ + if (!sis->silence_users) + sis->silence_dma_addr = pci_map_single(sis->pci, + sis->suspend_state[0], + 4096, PCI_DMA_TODEVICE); + sis->silence_users++; +} + +static void __sis_unmap_silence(struct sis7019 *sis) +{ + /* Helper function: must hold sis->voice_lock on entry */ + sis->silence_users--; + if (!sis->silence_users) + pci_unmap_single(sis->pci, sis->silence_dma_addr, 4096, + PCI_DMA_TODEVICE); +} + +static void sis_free_voice(struct sis7019 *sis, struct voice *voice) +{ + unsigned long flags; + + spin_lock_irqsave(&sis->voice_lock, flags); + if (voice->timing) { + __sis_unmap_silence(sis); + voice->timing->flags &= ~(VOICE_IN_USE | VOICE_SSO_TIMING | + VOICE_SYNC_TIMING); + voice->timing = NULL; + } + voice->flags &= ~(VOICE_IN_USE | VOICE_SSO_TIMING | VOICE_SYNC_TIMING); + spin_unlock_irqrestore(&sis->voice_lock, flags); +} + +static struct voice *__sis_alloc_playback_voice(struct sis7019 *sis) +{ + /* Must hold the voice_lock on entry */ + struct voice *voice; + int i; + + for (i = 0; i < 64; i++) { + voice = &sis->voices[i]; + if (voice->flags & VOICE_IN_USE) + continue; + voice->flags |= VOICE_IN_USE; + goto found_one; + } + voice = NULL; + +found_one: + return voice; +} + +static struct voice *sis_alloc_playback_voice(struct sis7019 *sis) +{ + struct voice *voice; + unsigned long flags; + + spin_lock_irqsave(&sis->voice_lock, flags); + voice = __sis_alloc_playback_voice(sis); + spin_unlock_irqrestore(&sis->voice_lock, flags); + + return voice; +} + +static int sis_alloc_timing_voice(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct sis7019 *sis = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct voice *voice = runtime->private_data; + unsigned int period_size, buffer_size; + unsigned long flags; + int needed; + + /* If there are one or two periods per buffer, we don't need a + * timing voice, as we can use the capture channel's interrupts + * to clock out the periods. + */ + period_size = params_period_size(hw_params); + buffer_size = params_buffer_size(hw_params); + needed = (period_size != buffer_size && + period_size != (buffer_size / 2)); + + if (needed && !voice->timing) { + spin_lock_irqsave(&sis->voice_lock, flags); + voice->timing = __sis_alloc_playback_voice(sis); + if (voice->timing) + __sis_map_silence(sis); + spin_unlock_irqrestore(&sis->voice_lock, flags); + if (!voice->timing) + return -ENOMEM; + voice->timing->substream = substream; + } else if (!needed && voice->timing) { + sis_free_voice(sis, voice); + voice->timing = NULL; + } + + return 0; +} + +static int sis_playback_open(struct snd_pcm_substream *substream) +{ + struct sis7019 *sis = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct voice *voice; + + voice = sis_alloc_playback_voice(sis); + if (!voice) + return -EAGAIN; + + voice->substream = substream; + runtime->private_data = voice; + runtime->hw = sis_playback_hw_info; + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + 9, 0xfff9); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + 9, 0xfff9); + snd_pcm_set_sync(substream); + return 0; +} + +static int sis_substream_close(struct snd_pcm_substream *substream) +{ + struct sis7019 *sis = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct voice *voice = runtime->private_data; + + sis_free_voice(sis, voice); + return 0; +} + +static int sis_playback_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); +} + +static int sis_hw_free(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int sis_pcm_playback_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct voice *voice = runtime->private_data; + void __iomem *ctrl_base = voice->ctrl_base; + void __iomem *wave_base = voice->wave_base; + u32 format, dma_addr, control, sso_eso, delta, reg; + u16 leo; + + /* We rely on the PCM core to ensure that the parameters for this + * substream do not change on us while we're programming the HW. + */ + format = 0; + if (snd_pcm_format_width(runtime->format) == 8) + format |= SIS_PLAY_DMA_FORMAT_8BIT; + if (!snd_pcm_format_signed(runtime->format)) + format |= SIS_PLAY_DMA_FORMAT_UNSIGNED; + if (runtime->channels == 1) + format |= SIS_PLAY_DMA_FORMAT_MONO; + + /* The baseline setup is for a single period per buffer, and + * we add bells and whistles as needed from there. + */ + dma_addr = runtime->dma_addr; + leo = runtime->buffer_size - 1; + control = leo | SIS_PLAY_DMA_LOOP | SIS_PLAY_DMA_INTR_AT_LEO; + sso_eso = leo; + + if (runtime->period_size == (runtime->buffer_size / 2)) { + control |= SIS_PLAY_DMA_INTR_AT_MLP; + } else if (runtime->period_size != runtime->buffer_size) { + voice->flags |= VOICE_SSO_TIMING; + voice->sso = runtime->period_size - 1; + voice->period_size = runtime->period_size; + voice->buffer_size = runtime->buffer_size; + + control &= ~SIS_PLAY_DMA_INTR_AT_LEO; + control |= SIS_PLAY_DMA_INTR_AT_SSO; + sso_eso |= (runtime->period_size - 1) << 16; + } + + delta = sis_rate_to_delta(runtime->rate); + + /* Ok, we're ready to go, set up the channel. + */ + writel(format, ctrl_base + SIS_PLAY_DMA_FORMAT_CSO); + writel(dma_addr, ctrl_base + SIS_PLAY_DMA_BASE); + writel(control, ctrl_base + SIS_PLAY_DMA_CONTROL); + writel(sso_eso, ctrl_base + SIS_PLAY_DMA_SSO_ESO); + + for (reg = 0; reg < SIS_WAVE_SIZE; reg += 4) + writel(0, wave_base + reg); + + writel(SIS_WAVE_GENERAL_WAVE_VOLUME, wave_base + SIS_WAVE_GENERAL); + writel(delta << 16, wave_base + SIS_WAVE_GENERAL_ARTICULATION); + writel(SIS_WAVE_CHANNEL_CONTROL_FIRST_SAMPLE | + SIS_WAVE_CHANNEL_CONTROL_AMP_ENABLE | + SIS_WAVE_CHANNEL_CONTROL_INTERPOLATE_ENABLE, + wave_base + SIS_WAVE_CHANNEL_CONTROL); + + /* Force PCI writes to post. */ + readl(ctrl_base); + + return 0; +} + +static int sis_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct sis7019 *sis = snd_pcm_substream_chip(substream); + unsigned long io = sis->ioport; + struct snd_pcm_substream *s; + struct voice *voice; + void *chip; + int starting; + u32 record = 0; + u32 play[2] = { 0, 0 }; + + /* No locks needed, as the PCM core will hold the locks on the + * substreams, and the HW will only start/stop the indicated voices + * without changing the state of the others. + */ + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + starting = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + starting = 0; + break; + default: + return -EINVAL; + } + + snd_pcm_group_for_each_entry(s, substream) { + /* Make sure it is for us... */ + chip = snd_pcm_substream_chip(s); + if (chip != sis) + continue; + + voice = s->runtime->private_data; + if (voice->flags & VOICE_CAPTURE) { + record |= 1 << voice->num; + voice = voice->timing; + } + + /* voice could be NULL if this a recording stream, and it + * doesn't have an external timing channel. + */ + if (voice) + play[voice->num / 32] |= 1 << (voice->num & 0x1f); + + snd_pcm_trigger_done(s, substream); + } + + if (starting) { + if (record) + outl(record, io + SIS_RECORD_START_REG); + if (play[0]) + outl(play[0], io + SIS_PLAY_START_A_REG); + if (play[1]) + outl(play[1], io + SIS_PLAY_START_B_REG); + } else { + if (record) + outl(record, io + SIS_RECORD_STOP_REG); + if (play[0]) + outl(play[0], io + SIS_PLAY_STOP_A_REG); + if (play[1]) + outl(play[1], io + SIS_PLAY_STOP_B_REG); + } + return 0; +} + +static snd_pcm_uframes_t sis_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct voice *voice = runtime->private_data; + u32 cso; + + cso = readl(voice->ctrl_base + SIS_PLAY_DMA_FORMAT_CSO); + cso &= 0xffff; + return cso; +} + +static int sis_capture_open(struct snd_pcm_substream *substream) +{ + struct sis7019 *sis = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct voice *voice = &sis->capture_voice; + unsigned long flags; + + /* FIXME: The driver only supports recording from one channel + * at the moment, but it could support more. + */ + spin_lock_irqsave(&sis->voice_lock, flags); + if (voice->flags & VOICE_IN_USE) + voice = NULL; + else + voice->flags |= VOICE_IN_USE; + spin_unlock_irqrestore(&sis->voice_lock, flags); + + if (!voice) + return -EAGAIN; + + voice->substream = substream; + runtime->private_data = voice; + runtime->hw = sis_capture_hw_info; + runtime->hw.rates = sis->ac97[0]->rates[AC97_RATES_ADC]; + snd_pcm_limit_hw_rates(runtime); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + 9, 0xfff9); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + 9, 0xfff9); + snd_pcm_set_sync(substream); + return 0; +} + +static int sis_capture_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct sis7019 *sis = snd_pcm_substream_chip(substream); + int rc; + + rc = snd_ac97_set_rate(sis->ac97[0], AC97_PCM_LR_ADC_RATE, + params_rate(hw_params)); + if (rc) + goto out; + + rc = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + if (rc < 0) + goto out; + + rc = sis_alloc_timing_voice(substream, hw_params); + +out: + return rc; +} + +static void sis_prepare_timing_voice(struct voice *voice, + struct snd_pcm_substream *substream) +{ + struct sis7019 *sis = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct voice *timing = voice->timing; + void __iomem *play_base = timing->ctrl_base; + void __iomem *wave_base = timing->wave_base; + u16 buffer_size, period_size; + u32 format, control, sso_eso, delta; + u32 vperiod, sso, reg; + + /* Set our initial buffer and period as large as we can given a + * single page of silence. + */ + buffer_size = 4096 / runtime->channels; + buffer_size /= snd_pcm_format_size(runtime->format, 1); + period_size = buffer_size; + + /* Initially, we want to interrupt just a bit behind the end of + * the period we're clocking out. 10 samples seems to give a good + * delay. + * + * We want to spread our interrupts throughout the virtual period, + * so that we don't end up with two interrupts back to back at the + * end -- this helps minimize the effects of any jitter. Adjust our + * clocking period size so that the last period is at least a fourth + * of a full period. + * + * This is all moot if we don't need to use virtual periods. + */ + vperiod = runtime->period_size + 10; + if (vperiod > period_size) { + u16 tail = vperiod % period_size; + u16 quarter_period = period_size / 4; + + if (tail && tail < quarter_period) { + u16 loops = vperiod / period_size; + + tail = quarter_period - tail; + tail += loops - 1; + tail /= loops; + period_size -= tail; + } + + sso = period_size - 1; + } else { + /* The initial period will fit inside the buffer, so we + * don't need to use virtual periods -- disable them. + */ + period_size = runtime->period_size; + sso = vperiod - 1; + vperiod = 0; + } + + /* The interrupt handler implements the timing syncronization, so + * setup its state. + */ + timing->flags |= VOICE_SYNC_TIMING; + timing->sync_base = voice->ctrl_base; + timing->sync_cso = runtime->period_size - 1; + timing->sync_period_size = runtime->period_size; + timing->sync_buffer_size = runtime->buffer_size; + timing->period_size = period_size; + timing->buffer_size = buffer_size; + timing->sso = sso; + timing->vperiod = vperiod; + + /* Using unsigned samples with the all-zero silence buffer + * forces the output to the lower rail, killing playback. + * So ignore unsigned vs signed -- it doesn't change the timing. + */ + format = 0; + if (snd_pcm_format_width(runtime->format) == 8) + format = SIS_CAPTURE_DMA_FORMAT_8BIT; + if (runtime->channels == 1) + format |= SIS_CAPTURE_DMA_FORMAT_MONO; + + control = timing->buffer_size - 1; + control |= SIS_PLAY_DMA_LOOP | SIS_PLAY_DMA_INTR_AT_SSO; + sso_eso = timing->buffer_size - 1; + sso_eso |= timing->sso << 16; + + delta = sis_rate_to_delta(runtime->rate); + + /* We've done the math, now configure the channel. + */ + writel(format, play_base + SIS_PLAY_DMA_FORMAT_CSO); + writel(sis->silence_dma_addr, play_base + SIS_PLAY_DMA_BASE); + writel(control, play_base + SIS_PLAY_DMA_CONTROL); + writel(sso_eso, play_base + SIS_PLAY_DMA_SSO_ESO); + + for (reg = 0; reg < SIS_WAVE_SIZE; reg += 4) + writel(0, wave_base + reg); + + writel(SIS_WAVE_GENERAL_WAVE_VOLUME, wave_base + SIS_WAVE_GENERAL); + writel(delta << 16, wave_base + SIS_WAVE_GENERAL_ARTICULATION); + writel(SIS_WAVE_CHANNEL_CONTROL_FIRST_SAMPLE | + SIS_WAVE_CHANNEL_CONTROL_AMP_ENABLE | + SIS_WAVE_CHANNEL_CONTROL_INTERPOLATE_ENABLE, + wave_base + SIS_WAVE_CHANNEL_CONTROL); +} + +static int sis_pcm_capture_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct voice *voice = runtime->private_data; + void __iomem *rec_base = voice->ctrl_base; + u32 format, dma_addr, control; + u16 leo; + + /* We rely on the PCM core to ensure that the parameters for this + * substream do not change on us while we're programming the HW. + */ + format = 0; + if (snd_pcm_format_width(runtime->format) == 8) + format = SIS_CAPTURE_DMA_FORMAT_8BIT; + if (!snd_pcm_format_signed(runtime->format)) + format |= SIS_CAPTURE_DMA_FORMAT_UNSIGNED; + if (runtime->channels == 1) + format |= SIS_CAPTURE_DMA_FORMAT_MONO; + + dma_addr = runtime->dma_addr; + leo = runtime->buffer_size - 1; + control = leo | SIS_CAPTURE_DMA_LOOP; + + /* If we've got more than two periods per buffer, then we have + * use a timing voice to clock out the periods. Otherwise, we can + * use the capture channel's interrupts. + */ + if (voice->timing) { + sis_prepare_timing_voice(voice, substream); + } else { + control |= SIS_CAPTURE_DMA_INTR_AT_LEO; + if (runtime->period_size != runtime->buffer_size) + control |= SIS_CAPTURE_DMA_INTR_AT_MLP; + } + + writel(format, rec_base + SIS_CAPTURE_DMA_FORMAT_CSO); + writel(dma_addr, rec_base + SIS_CAPTURE_DMA_BASE); + writel(control, rec_base + SIS_CAPTURE_DMA_CONTROL); + + /* Force the writes to post. */ + readl(rec_base); + + return 0; +} + +static struct snd_pcm_ops sis_playback_ops = { + .open = sis_playback_open, + .close = sis_substream_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = sis_playback_hw_params, + .hw_free = sis_hw_free, + .prepare = sis_pcm_playback_prepare, + .trigger = sis_pcm_trigger, + .pointer = sis_pcm_pointer, +}; + +static struct snd_pcm_ops sis_capture_ops = { + .open = sis_capture_open, + .close = sis_substream_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = sis_capture_hw_params, + .hw_free = sis_hw_free, + .prepare = sis_pcm_capture_prepare, + .trigger = sis_pcm_trigger, + .pointer = sis_pcm_pointer, +}; + +static int __devinit sis_pcm_create(struct sis7019 *sis) +{ + struct snd_pcm *pcm; + int rc; + + /* We have 64 voices, and the driver currently records from + * only one channel, though that could change in the future. + */ + rc = snd_pcm_new(sis->card, "SiS7019", 0, 64, 1, &pcm); + if (rc) + return rc; + + pcm->private_data = sis; + strcpy(pcm->name, "SiS7019"); + sis->pcm = pcm; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &sis_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &sis_capture_ops); + + /* Try to preallocate some memory, but it's not the end of the + * world if this fails. + */ + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(sis->pci), 64*1024, 128*1024); + + return 0; +} + +static unsigned short sis_ac97_rw(struct sis7019 *sis, int codec, u32 cmd) +{ + unsigned long io = sis->ioport; + unsigned short val = 0xffff; + u16 status; + u16 rdy; + int count; + const static u16 codec_ready[3] = { + SIS_AC97_STATUS_CODEC_READY, + SIS_AC97_STATUS_CODEC2_READY, + SIS_AC97_STATUS_CODEC3_READY, + }; + + rdy = codec_ready[codec]; + + + /* Get the AC97 semaphore -- software first, so we don't spin + * pounding out IO reads on the hardware semaphore... + */ + mutex_lock(&sis->ac97_mutex); + + count = 0xffff; + while ((inw(io + SIS_AC97_SEMA) & SIS_AC97_SEMA_BUSY) && --count) + udelay(1); + + if (!count) + goto timeout; + + /* ... and wait for any outstanding commands to complete ... + */ + count = 0xffff; + do { + status = inw(io + SIS_AC97_STATUS); + if ((status & rdy) && !(status & SIS_AC97_STATUS_BUSY)) + break; + + udelay(1); + } while (--count); + + if (!count) + goto timeout_sema; + + /* ... before sending our command and waiting for it to finish ... + */ + outl(cmd, io + SIS_AC97_CMD); + udelay(10); + + count = 0xffff; + while ((inw(io + SIS_AC97_STATUS) & SIS_AC97_STATUS_BUSY) && --count) + udelay(1); + + /* ... and reading the results (if any). + */ + val = inl(io + SIS_AC97_CMD) >> 16; + +timeout_sema: + outl(SIS_AC97_SEMA_RELEASE, io + SIS_AC97_SEMA); +timeout: + mutex_unlock(&sis->ac97_mutex); + + if (!count) { + printk(KERN_ERR "sis7019: ac97 codec %d timeout cmd 0x%08x\n", + codec, cmd); + } + + return val; +} + +static void sis_ac97_write(struct snd_ac97 *ac97, unsigned short reg, + unsigned short val) +{ + const static u32 cmd[3] = { + SIS_AC97_CMD_CODEC_WRITE, + SIS_AC97_CMD_CODEC2_WRITE, + SIS_AC97_CMD_CODEC3_WRITE, + }; + sis_ac97_rw(ac97->private_data, ac97->num, + (val << 16) | (reg << 8) | cmd[ac97->num]); +} + +static unsigned short sis_ac97_read(struct snd_ac97 *ac97, unsigned short reg) +{ + const static u32 cmd[3] = { + SIS_AC97_CMD_CODEC_READ, + SIS_AC97_CMD_CODEC2_READ, + SIS_AC97_CMD_CODEC3_READ, + }; + return sis_ac97_rw(ac97->private_data, ac97->num, + (reg << 8) | cmd[ac97->num]); +} + +static int __devinit sis_mixer_create(struct sis7019 *sis) +{ + struct snd_ac97_bus *bus; + struct snd_ac97_template ac97; + static struct snd_ac97_bus_ops ops = { + .write = sis_ac97_write, + .read = sis_ac97_read, + }; + int rc; + + memset(&ac97, 0, sizeof(ac97)); + ac97.private_data = sis; + + rc = snd_ac97_bus(sis->card, 0, &ops, NULL, &bus); + if (!rc && sis->codecs_present & SIS_PRIMARY_CODEC_PRESENT) + rc = snd_ac97_mixer(bus, &ac97, &sis->ac97[0]); + ac97.num = 1; + if (!rc && (sis->codecs_present & SIS_SECONDARY_CODEC_PRESENT)) + rc = snd_ac97_mixer(bus, &ac97, &sis->ac97[1]); + ac97.num = 2; + if (!rc && (sis->codecs_present & SIS_TERTIARY_CODEC_PRESENT)) + rc = snd_ac97_mixer(bus, &ac97, &sis->ac97[2]); + + /* If we return an error here, then snd_card_free() should + * free up any ac97 codecs that got created, as well as the bus. + */ + return rc; +} + +static void sis_free_suspend(struct sis7019 *sis) +{ + int i; + + for (i = 0; i < SIS_SUSPEND_PAGES; i++) + kfree(sis->suspend_state[i]); +} + +static int sis_chip_free(struct sis7019 *sis) +{ + /* Reset the chip, and disable all interrputs. + */ + outl(SIS_GCR_SOFTWARE_RESET, sis->ioport + SIS_GCR); + udelay(10); + outl(0, sis->ioport + SIS_GCR); + outl(0, sis->ioport + SIS_GIER); + + /* Now, free everything we allocated. + */ + if (sis->irq >= 0) + free_irq(sis->irq, sis); + + if (sis->ioaddr) + iounmap(sis->ioaddr); + + pci_release_regions(sis->pci); + pci_disable_device(sis->pci); + + sis_free_suspend(sis); + return 0; +} + +static int sis_dev_free(struct snd_device *dev) +{ + struct sis7019 *sis = dev->device_data; + return sis_chip_free(sis); +} + +static int sis_chip_init(struct sis7019 *sis) +{ + unsigned long io = sis->ioport; + void __iomem *ioaddr = sis->ioaddr; + u16 status; + int count; + int i; + + /* Reset the audio controller + */ + outl(SIS_GCR_SOFTWARE_RESET, io + SIS_GCR); + udelay(10); + outl(0, io + SIS_GCR); + + /* Get the AC-link semaphore, and reset the codecs + */ + count = 0xffff; + while ((inw(io + SIS_AC97_SEMA) & SIS_AC97_SEMA_BUSY) && --count) + udelay(1); + + if (!count) + return -EIO; + + outl(SIS_AC97_CMD_CODEC_COLD_RESET, io + SIS_AC97_CMD); + udelay(10); + + count = 0xffff; + while ((inw(io + SIS_AC97_STATUS) & SIS_AC97_STATUS_BUSY) && --count) + udelay(1); + + /* Now that we've finished the reset, find out what's attached. + */ + status = inl(io + SIS_AC97_STATUS); + if (status & SIS_AC97_STATUS_CODEC_READY) + sis->codecs_present |= SIS_PRIMARY_CODEC_PRESENT; + if (status & SIS_AC97_STATUS_CODEC2_READY) + sis->codecs_present |= SIS_SECONDARY_CODEC_PRESENT; + if (status & SIS_AC97_STATUS_CODEC3_READY) + sis->codecs_present |= SIS_TERTIARY_CODEC_PRESENT; + + /* All done, let go of the semaphore, and check for errors + */ + outl(SIS_AC97_SEMA_RELEASE, io + SIS_AC97_SEMA); + if (!sis->codecs_present || !count) + return -EIO; + + /* Let the hardware know that the audio driver is alive, + * and enable PCM slots on the AC-link for L/R playback (3 & 4) and + * record channels. We're going to want to use Variable Rate Audio + * for recording, to avoid needlessly resampling from 48kHZ. + */ + outl(SIS_AC97_CONF_AUDIO_ALIVE, io + SIS_AC97_CONF); + outl(SIS_AC97_CONF_AUDIO_ALIVE | SIS_AC97_CONF_PCM_LR_ENABLE | + SIS_AC97_CONF_PCM_CAP_MIC_ENABLE | + SIS_AC97_CONF_PCM_CAP_LR_ENABLE | + SIS_AC97_CONF_CODEC_VRA_ENABLE, io + SIS_AC97_CONF); + + /* All AC97 PCM slots should be sourced from sub-mixer 0. + */ + outl(0, io + SIS_AC97_PSR); + + /* There is only one valid DMA setup for a PCI environment. + */ + outl(SIS_DMA_CSR_PCI_SETTINGS, io + SIS_DMA_CSR); + + /* Reset the syncronization groups for all of the channels + * to be asyncronous. If we start doing SPDIF or 5.1 sound, etc. + * we'll need to change how we handle these. Until then, we just + * assign sub-mixer 0 to all playback channels, and avoid any + * attenuation on the audio. + */ + outl(0, io + SIS_PLAY_SYNC_GROUP_A); + outl(0, io + SIS_PLAY_SYNC_GROUP_B); + outl(0, io + SIS_PLAY_SYNC_GROUP_C); + outl(0, io + SIS_PLAY_SYNC_GROUP_D); + outl(0, io + SIS_MIXER_SYNC_GROUP); + + for (i = 0; i < 64; i++) { + writel(i, SIS_MIXER_START_ADDR(ioaddr, i)); + writel(SIS_MIXER_RIGHT_NO_ATTEN | SIS_MIXER_LEFT_NO_ATTEN | + SIS_MIXER_DEST_0, SIS_MIXER_ADDR(ioaddr, i)); + } + + /* Don't attenuate any audio set for the wave amplifier. + * + * FIXME: Maximum attenuation is set for the music amp, which will + * need to change if we start using the synth engine. + */ + outl(0xffff0000, io + SIS_WEVCR); + + /* Ensure that the wave engine is in normal operating mode. + */ + outl(0, io + SIS_WECCR); + + /* Go ahead and enable the DMA interrupts. They won't go live + * until we start a channel. + */ + outl(SIS_GIER_AUDIO_PLAY_DMA_IRQ_ENABLE | + SIS_GIER_AUDIO_RECORD_DMA_IRQ_ENABLE, io + SIS_GIER); + + return 0; +} + +#ifdef CONFIG_PM +static int sis_suspend(struct pci_dev *pci, pm_message_t state) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct sis7019 *sis = card->private_data; + void __iomem *ioaddr = sis->ioaddr; + int i; + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + snd_pcm_suspend_all(sis->pcm); + if (sis->codecs_present & SIS_PRIMARY_CODEC_PRESENT) + snd_ac97_suspend(sis->ac97[0]); + if (sis->codecs_present & SIS_SECONDARY_CODEC_PRESENT) + snd_ac97_suspend(sis->ac97[1]); + if (sis->codecs_present & SIS_TERTIARY_CODEC_PRESENT) + snd_ac97_suspend(sis->ac97[2]); + + /* snd_pcm_suspend_all() stopped all channels, so we're quiescent. + */ + if (sis->irq >= 0) { + synchronize_irq(sis->irq); + free_irq(sis->irq, sis); + sis->irq = -1; + } + + /* Save the internal state away + */ + for (i = 0; i < 4; i++) { + memcpy_fromio(sis->suspend_state[i], ioaddr, 4096); + ioaddr += 4096; + } + + pci_disable_device(pci); + pci_save_state(pci); + pci_set_power_state(pci, pci_choose_state(pci, state)); + return 0; +} + +static int sis_resume(struct pci_dev *pci) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct sis7019 *sis = card->private_data; + void __iomem *ioaddr = sis->ioaddr; + int i; + + pci_set_power_state(pci, PCI_D0); + pci_restore_state(pci); + + if (pci_enable_device(pci) < 0) { + printk(KERN_ERR "sis7019: unable to re-enable device\n"); + goto error; + } + + if (sis_chip_init(sis)) { + printk(KERN_ERR "sis7019: unable to re-init controller\n"); + goto error; + } + + if (request_irq(pci->irq, sis_interrupt, IRQF_DISABLED|IRQF_SHARED, + card->shortname, sis)) { + printk(KERN_ERR "sis7019: unable to regain IRQ %d\n", pci->irq); + goto error; + } + + /* Restore saved state, then clear out the page we use for the + * silence buffer. + */ + for (i = 0; i < 4; i++) { + memcpy_toio(ioaddr, sis->suspend_state[i], 4096); + ioaddr += 4096; + } + + memset(sis->suspend_state[0], 0, 4096); + + sis->irq = pci->irq; + pci_set_master(pci); + + if (sis->codecs_present & SIS_PRIMARY_CODEC_PRESENT) + snd_ac97_resume(sis->ac97[0]); + if (sis->codecs_present & SIS_SECONDARY_CODEC_PRESENT) + snd_ac97_resume(sis->ac97[1]); + if (sis->codecs_present & SIS_TERTIARY_CODEC_PRESENT) + snd_ac97_resume(sis->ac97[2]); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + return 0; + +error: + snd_card_disconnect(card); + return -EIO; +} +#endif /* CONFIG_PM */ + +static int sis_alloc_suspend(struct sis7019 *sis) +{ + int i; + + /* We need 16K to store the internal wave engine state during a + * suspend, but we don't need it to be contiguous, so play nice + * with the memory system. We'll also use this area for a silence + * buffer. + */ + for (i = 0; i < SIS_SUSPEND_PAGES; i++) { + sis->suspend_state[i] = kmalloc(4096, GFP_KERNEL); + if (!sis->suspend_state[i]) + return -ENOMEM; + } + memset(sis->suspend_state[0], 0, 4096); + + return 0; +} + +static int __devinit sis_chip_create(struct snd_card *card, + struct pci_dev *pci) +{ + struct sis7019 *sis = card->private_data; + struct voice *voice; + static struct snd_device_ops ops = { + .dev_free = sis_dev_free, + }; + int rc; + int i; + + rc = pci_enable_device(pci); + if (rc) + goto error_out; + + if (pci_set_dma_mask(pci, DMA_30BIT_MASK) < 0) { + printk(KERN_ERR "sis7019: architecture does not support " + "30-bit PCI busmaster DMA"); + goto error_out_enabled; + } + + memset(sis, 0, sizeof(*sis)); + mutex_init(&sis->ac97_mutex); + spin_lock_init(&sis->voice_lock); + sis->card = card; + sis->pci = pci; + sis->irq = -1; + sis->ioport = pci_resource_start(pci, 0); + + rc = pci_request_regions(pci, "SiS7019"); + if (rc) { + printk(KERN_ERR "sis7019: unable request regions\n"); + goto error_out_enabled; + } + + rc = -EIO; + sis->ioaddr = ioremap_nocache(pci_resource_start(pci, 1), 0x4000); + if (!sis->ioaddr) { + printk(KERN_ERR "sis7019: unable to remap MMIO, aborting\n"); + goto error_out_cleanup; + } + + rc = sis_alloc_suspend(sis); + if (rc < 0) { + printk(KERN_ERR "sis7019: unable to allocate state storage\n"); + goto error_out_cleanup; + } + + rc = sis_chip_init(sis); + if (rc) + goto error_out_cleanup; + + if (request_irq(pci->irq, sis_interrupt, IRQF_DISABLED|IRQF_SHARED, + card->shortname, sis)) { + printk(KERN_ERR "unable to allocate irq %d\n", sis->irq); + goto error_out_cleanup; + } + + sis->irq = pci->irq; + pci_set_master(pci); + + for (i = 0; i < 64; i++) { + voice = &sis->voices[i]; + voice->num = i; + voice->ctrl_base = SIS_PLAY_DMA_ADDR(sis->ioaddr, i); + voice->wave_base = SIS_WAVE_ADDR(sis->ioaddr, i); + } + + voice = &sis->capture_voice; + voice->flags = VOICE_CAPTURE; + voice->num = SIS_CAPTURE_CHAN_AC97_PCM_IN; + voice->ctrl_base = SIS_CAPTURE_DMA_ADDR(sis->ioaddr, voice->num); + + rc = snd_device_new(card, SNDRV_DEV_LOWLEVEL, sis, &ops); + if (rc) + goto error_out_cleanup; + + snd_card_set_dev(card, &pci->dev); + + return 0; + +error_out_cleanup: + sis_chip_free(sis); + +error_out_enabled: + pci_disable_device(pci); + +error_out: + return rc; +} + +static int __devinit snd_sis7019_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + struct snd_card *card; + struct sis7019 *sis; + int rc; + + rc = -ENOENT; + if (!enable) + goto error_out; + + rc = -ENOMEM; + card = snd_card_new(index, id, THIS_MODULE, sizeof(*sis)); + if (!card) + goto error_out; + + strcpy(card->driver, "SiS7019"); + strcpy(card->shortname, "SiS7019"); + rc = sis_chip_create(card, pci); + if (rc) + goto card_error_out; + + sis = card->private_data; + + rc = sis_mixer_create(sis); + if (rc) + goto card_error_out; + + rc = sis_pcm_create(sis); + if (rc) + goto card_error_out; + + snprintf(card->longname, sizeof(card->longname), + "%s Audio Accelerator with %s at 0x%lx, irq %d", + card->shortname, snd_ac97_get_short_name(sis->ac97[0]), + sis->ioport, sis->irq); + + rc = snd_card_register(card); + if (rc) + goto card_error_out; + + pci_set_drvdata(pci, card); + return 0; + +card_error_out: + snd_card_free(card); + +error_out: + return rc; +} + +static void __devexit snd_sis7019_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver sis7019_driver = { + .name = "SiS7019", + .id_table = snd_sis7019_ids, + .probe = snd_sis7019_probe, + .remove = __devexit_p(snd_sis7019_remove), + +#ifdef CONFIG_PM + .suspend = sis_suspend, + .resume = sis_resume, +#endif +}; + +static int __init sis7019_init(void) +{ + return pci_register_driver(&sis7019_driver); +} + +static void __exit sis7019_exit(void) +{ + pci_unregister_driver(&sis7019_driver); +} + +module_init(sis7019_init); +module_exit(sis7019_exit); diff --git a/sound/pci/sis7019.h b/sound/pci/sis7019.h new file mode 100644 index 0000000..013b673 --- /dev/null +++ b/sound/pci/sis7019.h @@ -0,0 +1,342 @@ +#ifndef __sis7019_h__ +#define __sis7019_h__ + +/* + * Definitions for SiS7019 Audio Accelerator + * + * Copyright (C) 2004-2007, David Dillow + * Written by David Dillow <dave@thedillows.org> + * Inspired by the Trident 4D-WaveDX/NX driver. + * + * All rights reserved. + * + * 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, version 2. + * + * 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 + */ + + +/* General Control Register */ +#define SIS_GCR 0x00 +#define SIS_GCR_MACRO_POWER_DOWN 0x80000000 +#define SIS_GCR_MODEM_ENABLE 0x00010000 +#define SIS_GCR_SOFTWARE_RESET 0x00000001 + +/* General Interrupt Enable Register */ +#define SIS_GIER 0x04 +#define SIS_GIER_MODEM_TIMER_IRQ_ENABLE 0x00100000 +#define SIS_GIER_MODEM_RX_DMA_IRQ_ENABLE 0x00080000 +#define SIS_GIER_MODEM_TX_DMA_IRQ_ENABLE 0x00040000 +#define SIS_GIER_AC97_GPIO1_IRQ_ENABLE 0x00020000 +#define SIS_GIER_AC97_GPIO0_IRQ_ENABLE 0x00010000 +#define SIS_GIER_AC97_SAMPLE_TIMER_IRQ_ENABLE 0x00000010 +#define SIS_GIER_AUDIO_GLOBAL_TIMER_IRQ_ENABLE 0x00000008 +#define SIS_GIER_AUDIO_RECORD_DMA_IRQ_ENABLE 0x00000004 +#define SIS_GIER_AUDIO_PLAY_DMA_IRQ_ENABLE 0x00000002 +#define SIS_GIER_AUDIO_WAVE_ENGINE_IRQ_ENABLE 0x00000001 + +/* General Interrupt Status Register */ +#define SIS_GISR 0x08 +#define SIS_GISR_MODEM_TIMER_IRQ_STATUS 0x00100000 +#define SIS_GISR_MODEM_RX_DMA_IRQ_STATUS 0x00080000 +#define SIS_GISR_MODEM_TX_DMA_IRQ_STATUS 0x00040000 +#define SIS_GISR_AC97_GPIO1_IRQ_STATUS 0x00020000 +#define SIS_GISR_AC97_GPIO0_IRQ_STATUS 0x00010000 +#define SIS_GISR_AC97_SAMPLE_TIMER_IRQ_STATUS 0x00000010 +#define SIS_GISR_AUDIO_GLOBAL_TIMER_IRQ_STATUS 0x00000008 +#define SIS_GISR_AUDIO_RECORD_DMA_IRQ_STATUS 0x00000004 +#define SIS_GISR_AUDIO_PLAY_DMA_IRQ_STATUS 0x00000002 +#define SIS_GISR_AUDIO_WAVE_ENGINE_IRQ_STATUS 0x00000001 + +/* DMA Control Register */ +#define SIS_DMA_CSR 0x10 +#define SIS_DMA_CSR_PCI_SETTINGS 0x0000001d +#define SIS_DMA_CSR_CONCURRENT_ENABLE 0x00000200 +#define SIS_DMA_CSR_PIPELINE_ENABLE 0x00000100 +#define SIS_DMA_CSR_RX_DRAIN_ENABLE 0x00000010 +#define SIS_DMA_CSR_RX_FILL_ENABLE 0x00000008 +#define SIS_DMA_CSR_TX_DRAIN_ENABLE 0x00000004 +#define SIS_DMA_CSR_TX_LOWPRI_FILL_ENABLE 0x00000002 +#define SIS_DMA_CSR_TX_HIPRI_FILL_ENABLE 0x00000001 + +/* Playback Channel Start Registers */ +#define SIS_PLAY_START_A_REG 0x14 +#define SIS_PLAY_START_B_REG 0x18 + +/* Playback Channel Stop Registers */ +#define SIS_PLAY_STOP_A_REG 0x1c +#define SIS_PLAY_STOP_B_REG 0x20 + +/* Recording Channel Start Register */ +#define SIS_RECORD_START_REG 0x24 + +/* Recording Channel Stop Register */ +#define SIS_RECORD_STOP_REG 0x28 + +/* Playback Interrupt Status Registers */ +#define SIS_PISR_A 0x2c +#define SIS_PISR_B 0x30 + +/* Recording Interrupt Status Register */ +#define SIS_RISR 0x34 + +/* AC97 AC-link Playback Source Register */ +#define SIS_AC97_PSR 0x40 +#define SIS_AC97_PSR_MODEM_HEADSET_SRC_MIXER 0x0f000000 +#define SIS_AC97_PSR_MODEM_LINE2_SRC_MIXER 0x00f00000 +#define SIS_AC97_PSR_MODEM_LINE1_SRC_MIXER 0x000f0000 +#define SIS_AC97_PSR_PCM_LFR_SRC_MIXER 0x0000f000 +#define SIS_AC97_PSR_PCM_SURROUND_SRC_MIXER 0x00000f00 +#define SIS_AC97_PSR_PCM_CENTER_SRC_MIXER 0x000000f0 +#define SIS_AC97_PSR_PCM_LR_SRC_MIXER 0x0000000f + +/* AC97 AC-link Command Register */ +#define SIS_AC97_CMD 0x50 +#define SIS_AC97_CMD_DATA_MASK 0xffff0000 +#define SIS_AC97_CMD_REG_MASK 0x0000ff00 +#define SIS_AC97_CMD_CODEC3_READ 0x0000000d +#define SIS_AC97_CMD_CODEC3_WRITE 0x0000000c +#define SIS_AC97_CMD_CODEC2_READ 0x0000000b +#define SIS_AC97_CMD_CODEC2_WRITE 0x0000000a +#define SIS_AC97_CMD_CODEC_READ 0x00000009 +#define SIS_AC97_CMD_CODEC_WRITE 0x00000008 +#define SIS_AC97_CMD_CODEC_WARM_RESET 0x00000005 +#define SIS_AC97_CMD_CODEC_COLD_RESET 0x00000004 +#define SIS_AC97_CMD_DONE 0x00000000 + +/* AC97 AC-link Semaphore Register */ +#define SIS_AC97_SEMA 0x54 +#define SIS_AC97_SEMA_BUSY 0x00000001 +#define SIS_AC97_SEMA_RELEASE 0x00000000 + +/* AC97 AC-link Status Register */ +#define SIS_AC97_STATUS 0x58 +#define SIS_AC97_STATUS_AUDIO_D2_INACT_SECS 0x03f00000 +#define SIS_AC97_STATUS_MODEM_ALIVE 0x00002000 +#define SIS_AC97_STATUS_AUDIO_ALIVE 0x00001000 +#define SIS_AC97_STATUS_CODEC3_READY 0x00000400 +#define SIS_AC97_STATUS_CODEC2_READY 0x00000200 +#define SIS_AC97_STATUS_CODEC_READY 0x00000100 +#define SIS_AC97_STATUS_WARM_RESET 0x00000080 +#define SIS_AC97_STATUS_COLD_RESET 0x00000040 +#define SIS_AC97_STATUS_POWERED_DOWN 0x00000020 +#define SIS_AC97_STATUS_NORMAL 0x00000010 +#define SIS_AC97_STATUS_READ_EXPIRED 0x00000004 +#define SIS_AC97_STATUS_SEMAPHORE 0x00000002 +#define SIS_AC97_STATUS_BUSY 0x00000001 + +/* AC97 AC-link Audio Configuration Register */ +#define SIS_AC97_CONF 0x5c +#define SIS_AC97_CONF_AUDIO_ALIVE 0x80000000 +#define SIS_AC97_CONF_WARM_RESET_ENABLE 0x40000000 +#define SIS_AC97_CONF_PR6_ENABLE 0x20000000 +#define SIS_AC97_CONF_PR5_ENABLE 0x10000000 +#define SIS_AC97_CONF_PR4_ENABLE 0x08000000 +#define SIS_AC97_CONF_PR3_ENABLE 0x04000000 +#define SIS_AC97_CONF_PR2_PR7_ENABLE 0x02000000 +#define SIS_AC97_CONF_PR0_PR1_ENABLE 0x01000000 +#define SIS_AC97_CONF_AUTO_PM_ENABLE 0x00800000 +#define SIS_AC97_CONF_PCM_LFE_ENABLE 0x00080000 +#define SIS_AC97_CONF_PCM_SURROUND_ENABLE 0x00040000 +#define SIS_AC97_CONF_PCM_CENTER_ENABLE 0x00020000 +#define SIS_AC97_CONF_PCM_LR_ENABLE 0x00010000 +#define SIS_AC97_CONF_PCM_CAP_MIC_ENABLE 0x00002000 +#define SIS_AC97_CONF_PCM_CAP_LR_ENABLE 0x00001000 +#define SIS_AC97_CONF_PCM_CAP_MIC_FROM_CODEC3 0x00000200 +#define SIS_AC97_CONF_PCM_CAP_LR_FROM_CODEC3 0x00000100 +#define SIS_AC97_CONF_CODEC3_PM_VRM 0x00000080 +#define SIS_AC97_CONF_CODEC_PM_VRM 0x00000040 +#define SIS_AC97_CONF_CODEC3_VRA_ENABLE 0x00000020 +#define SIS_AC97_CONF_CODEC_VRA_ENABLE 0x00000010 +#define SIS_AC97_CONF_CODEC3_PM_EAC 0x00000008 +#define SIS_AC97_CONF_CODEC_PM_EAC 0x00000004 +#define SIS_AC97_CONF_CODEC3_EXISTS 0x00000002 +#define SIS_AC97_CONF_CODEC_EXISTS 0x00000001 + +/* Playback Channel Sync Group registers */ +#define SIS_PLAY_SYNC_GROUP_A 0x80 +#define SIS_PLAY_SYNC_GROUP_B 0x84 +#define SIS_PLAY_SYNC_GROUP_C 0x88 +#define SIS_PLAY_SYNC_GROUP_D 0x8c +#define SIS_MIXER_SYNC_GROUP 0x90 + +/* Wave Engine Config and Control Register */ +#define SIS_WECCR 0xa0 +#define SIS_WECCR_TESTMODE_MASK 0x00300000 +#define SIS_WECCR_TESTMODE_NORMAL 0x00000000 +#define SIS_WECCR_TESTMODE_BYPASS_NSO_ALPHA 0x00100000 +#define SIS_WECCR_TESTMODE_BYPASS_FC 0x00200000 +#define SIS_WECCR_TESTMODE_BYPASS_WOL 0x00300000 +#define SIS_WECCR_RESONANCE_DELAY_MASK 0x00060000 +#define SIS_WECCR_RESONANCE_DELAY_NONE 0x00000000 +#define SIS_WECCR_RESONANCE_DELAY_FC_1F00 0x00020000 +#define SIS_WECCR_RESONANCE_DELAY_FC_1E00 0x00040000 +#define SIS_WECCR_RESONANCE_DELAY_FC_1C00 0x00060000 +#define SIS_WECCR_IGNORE_CHANNEL_PARMS 0x00010000 +#define SIS_WECCR_COMMAND_CHANNEL_ID_MASK 0x0003ff00 +#define SIS_WECCR_COMMAND_MASK 0x00000007 +#define SIS_WECCR_COMMAND_NONE 0x00000000 +#define SIS_WECCR_COMMAND_DONE 0x00000000 +#define SIS_WECCR_COMMAND_PAUSE 0x00000001 +#define SIS_WECCR_COMMAND_TOGGLE_VEG 0x00000002 +#define SIS_WECCR_COMMAND_TOGGLE_MEG 0x00000003 +#define SIS_WECCR_COMMAND_TOGGLE_VEG_MEG 0x00000004 + +/* Wave Engine Volume Control Register */ +#define SIS_WEVCR 0xa4 +#define SIS_WEVCR_LEFT_MUSIC_ATTENUATION_MASK 0xff000000 +#define SIS_WEVCR_RIGHT_MUSIC_ATTENUATION_MASK 0x00ff0000 +#define SIS_WEVCR_LEFT_WAVE_ATTENUATION_MASK 0x0000ff00 +#define SIS_WEVCR_RIGHT_WAVE_ATTENUATION_MASK 0x000000ff + +/* Wave Engine Interrupt Status Registers */ +#define SIS_WEISR_A 0xa8 +#define SIS_WEISR_B 0xac + + +/* Playback DMA parameters (paramter RAM) */ +#define SIS_PLAY_DMA_OFFSET 0x0000 +#define SIS_PLAY_DMA_SIZE 0x10 +#define SIS_PLAY_DMA_ADDR(addr, num) \ + ((num * SIS_PLAY_DMA_SIZE) + (addr) + SIS_PLAY_DMA_OFFSET) + +#define SIS_PLAY_DMA_FORMAT_CSO 0x00 +#define SIS_PLAY_DMA_FORMAT_UNSIGNED 0x00080000 +#define SIS_PLAY_DMA_FORMAT_8BIT 0x00040000 +#define SIS_PLAY_DMA_FORMAT_MONO 0x00020000 +#define SIS_PLAY_DMA_CSO_MASK 0x0000ffff +#define SIS_PLAY_DMA_BASE 0x04 +#define SIS_PLAY_DMA_CONTROL 0x08 +#define SIS_PLAY_DMA_STOP_AT_SSO 0x04000000 +#define SIS_PLAY_DMA_RELEASE 0x02000000 +#define SIS_PLAY_DMA_LOOP 0x01000000 +#define SIS_PLAY_DMA_INTR_AT_SSO 0x00080000 +#define SIS_PLAY_DMA_INTR_AT_ESO 0x00040000 +#define SIS_PLAY_DMA_INTR_AT_LEO 0x00020000 +#define SIS_PLAY_DMA_INTR_AT_MLP 0x00010000 +#define SIS_PLAY_DMA_LEO_MASK 0x0000ffff +#define SIS_PLAY_DMA_SSO_ESO 0x0c +#define SIS_PLAY_DMA_SSO_MASK 0xffff0000 +#define SIS_PLAY_DMA_ESO_MASK 0x0000ffff + +/* Capture DMA parameters (paramter RAM) */ +#define SIS_CAPTURE_DMA_OFFSET 0x0800 +#define SIS_CAPTURE_DMA_SIZE 0x10 +#define SIS_CAPTURE_DMA_ADDR(addr, num) \ + ((num * SIS_CAPTURE_DMA_SIZE) + (addr) + SIS_CAPTURE_DMA_OFFSET) + +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_0 0 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_1 1 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_2 2 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_3 3 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_4 4 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_5 5 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_6 6 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_7 7 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_8 8 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_9 9 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_10 10 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_11 11 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_12 12 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_13 13 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_14 14 +#define SIS_CAPTURE_CHAN_MIXER_ROUTE_BACK_15 15 +#define SIS_CAPTURE_CHAN_AC97_PCM_IN 16 +#define SIS_CAPTURE_CHAN_AC97_MIC_IN 17 +#define SIS_CAPTURE_CHAN_AC97_LINE1_IN 18 +#define SIS_CAPTURE_CHAN_AC97_LINE2_IN 19 +#define SIS_CAPTURE_CHAN_AC97_HANDSE_IN 20 + +#define SIS_CAPTURE_DMA_FORMAT_CSO 0x00 +#define SIS_CAPTURE_DMA_MONO_MODE_MASK 0xc0000000 +#define SIS_CAPTURE_DMA_MONO_MODE_AVG 0x00000000 +#define SIS_CAPTURE_DMA_MONO_MODE_LEFT 0x40000000 +#define SIS_CAPTURE_DMA_MONO_MODE_RIGHT 0x80000000 +#define SIS_CAPTURE_DMA_FORMAT_UNSIGNED 0x00080000 +#define SIS_CAPTURE_DMA_FORMAT_8BIT 0x00040000 +#define SIS_CAPTURE_DMA_FORMAT_MONO 0x00020000 +#define SIS_CAPTURE_DMA_CSO_MASK 0x0000ffff +#define SIS_CAPTURE_DMA_BASE 0x04 +#define SIS_CAPTURE_DMA_CONTROL 0x08 +#define SIS_CAPTURE_DMA_STOP_AT_SSO 0x04000000 +#define SIS_CAPTURE_DMA_RELEASE 0x02000000 +#define SIS_CAPTURE_DMA_LOOP 0x01000000 +#define SIS_CAPTURE_DMA_INTR_AT_LEO 0x00020000 +#define SIS_CAPTURE_DMA_INTR_AT_MLP 0x00010000 +#define SIS_CAPTURE_DMA_LEO_MASK 0x0000ffff +#define SIS_CAPTURE_DMA_RESERVED 0x0c + + +/* Mixer routing list start pointer (parameter RAM) */ +#define SIS_MIXER_START_OFFSET 0x1000 +#define SIS_MIXER_START_SIZE 0x04 +#define SIS_MIXER_START_ADDR(addr, num) \ + ((num * SIS_MIXER_START_SIZE) + (addr) + SIS_MIXER_START_OFFSET) + +#define SIS_MIXER_START_MASK 0x0000007f + +/* Mixer routing table (parameter RAM) */ +#define SIS_MIXER_OFFSET 0x1400 +#define SIS_MIXER_SIZE 0x04 +#define SIS_MIXER_ADDR(addr, num) \ + ((num * SIS_MIXER_SIZE) + (addr) + SIS_MIXER_OFFSET) + +#define SIS_MIXER_RIGHT_ATTENUTATION_MASK 0xff000000 +#define SIS_MIXER_RIGHT_NO_ATTEN 0xff000000 +#define SIS_MIXER_LEFT_ATTENUTATION_MASK 0x00ff0000 +#define SIS_MIXER_LEFT_NO_ATTEN 0x00ff0000 +#define SIS_MIXER_NEXT_ENTRY_MASK 0x00007f00 +#define SIS_MIXER_NEXT_ENTRY_NONE 0x00000000 +#define SIS_MIXER_DEST_MASK 0x0000007f +#define SIS_MIXER_DEST_0 0x00000020 +#define SIS_MIXER_DEST_1 0x00000021 +#define SIS_MIXER_DEST_2 0x00000022 +#define SIS_MIXER_DEST_3 0x00000023 +#define SIS_MIXER_DEST_4 0x00000024 +#define SIS_MIXER_DEST_5 0x00000025 +#define SIS_MIXER_DEST_6 0x00000026 +#define SIS_MIXER_DEST_7 0x00000027 +#define SIS_MIXER_DEST_8 0x00000028 +#define SIS_MIXER_DEST_9 0x00000029 +#define SIS_MIXER_DEST_10 0x0000002a +#define SIS_MIXER_DEST_11 0x0000002b +#define SIS_MIXER_DEST_12 0x0000002c +#define SIS_MIXER_DEST_13 0x0000002d +#define SIS_MIXER_DEST_14 0x0000002e +#define SIS_MIXER_DEST_15 0x0000002f + +/* Wave Engine Control Parameters (parameter RAM) */ +#define SIS_WAVE_OFFSET 0x2000 +#define SIS_WAVE_SIZE 0x40 +#define SIS_WAVE_ADDR(addr, num) \ + ((num * SIS_WAVE_SIZE) + (addr) + SIS_WAVE_OFFSET) + +#define SIS_WAVE_GENERAL 0x00 +#define SIS_WAVE_GENERAL_WAVE_VOLUME 0x80000000 +#define SIS_WAVE_GENERAL_MUSIC_VOLUME 0x00000000 +#define SIS_WAVE_GENERAL_VOLUME_MASK 0x7f000000 +#define SIS_WAVE_GENERAL_ARTICULATION 0x04 +#define SIS_WAVE_GENERAL_ARTICULATION_DELTA_MASK 0x3fff0000 +#define SIS_WAVE_ARTICULATION 0x08 +#define SIS_WAVE_TIMER 0x0c +#define SIS_WAVE_GENERATOR 0x10 +#define SIS_WAVE_CHANNEL_CONTROL 0x14 +#define SIS_WAVE_CHANNEL_CONTROL_FIRST_SAMPLE 0x80000000 +#define SIS_WAVE_CHANNEL_CONTROL_AMP_ENABLE 0x40000000 +#define SIS_WAVE_CHANNEL_CONTROL_FILTER_ENABLE 0x20000000 +#define SIS_WAVE_CHANNEL_CONTROL_INTERPOLATE_ENABLE 0x10000000 +#define SIS_WAVE_LFO_EG_CONTROL 0x18 +#define SIS_WAVE_LFO_EG_CONTROL_2 0x1c +#define SIS_WAVE_LFO_EG_CONTROL_3 0x20 +#define SIS_WAVE_LFO_EG_CONTROL_4 0x24 + +#endif /* __sis7019_h__ */ diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c index 44a7f5f..0d3d305 100644 --- a/sound/pci/sonicvibes.c +++ b/sound/pci/sonicvibes.c @@ -22,7 +22,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/sound/pci/trident/Makefile b/sound/pci/trident/Makefile index 65f2c21..88676b5 100644 --- a/sound/pci/trident/Makefile +++ b/sound/pci/trident/Makefile @@ -4,16 +4,6 @@ # snd-trident-objs := trident.o trident_main.o trident_memory.o -snd-trident-synth-objs := trident_synth.o - -# -# this function returns: -# "m" - CONFIG_SND_SEQUENCER is m -# <empty string> - CONFIG_SND_SEQUENCER is undefined -# otherwise parameter #1 value -# -sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1))) # Toplevel Module Dependency obj-$(CONFIG_SND_TRIDENT) += snd-trident.o -obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-trident-synth.o diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c index 8488456..d94b16f 100644 --- a/sound/pci/trident/trident.c +++ b/sound/pci/trident/trident.c @@ -21,7 +21,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/pci.h> #include <linux/time.h> @@ -155,13 +154,6 @@ static int __devinit snd_trident_probe(struct pci_dev *pci, return err; } -#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) - if ((err = snd_trident_attach_synthesizer(trident)) < 0) { - snd_card_free(card); - return err; - } -#endif - snd_trident_create_gameport(trident); if ((err = snd_card_register(card)) < 0) { diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index a235e03..71138ff 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -27,7 +27,6 @@ * SiS7018 S/PDIF support by Thomas Winischhofer <thomas@winischhofer.net> */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -436,7 +435,7 @@ static void snd_trident_free_synth_channel(struct snd_trident *trident, int chan Description: This routine will complete and write the 5 hardware channel registers to hardware. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. voice - synthesizer voice structure Each register field. @@ -514,7 +513,7 @@ EXPORT_SYMBOL(snd_trident_write_voice_regs); Description: This routine will write the new CSO offset register to hardware. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. voice - synthesizer voice structure CSO - new CSO value @@ -540,7 +539,7 @@ static void snd_trident_write_cso_reg(struct snd_trident * trident, Description: This routine will write the new ESO offset register to hardware. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. voice - synthesizer voice structure ESO - new ESO value @@ -566,7 +565,7 @@ static void snd_trident_write_eso_reg(struct snd_trident * trident, Description: This routine will write the new voice volume register to hardware. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. voice - synthesizer voice structure Vol - new voice volume @@ -597,7 +596,7 @@ static void snd_trident_write_vol_reg(struct snd_trident * trident, Description: This routine will write the new voice pan register to hardware. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. voice - synthesizer voice structure Pan - new pan value @@ -619,7 +618,7 @@ static void snd_trident_write_pan_reg(struct snd_trident * trident, Description: This routine will write the new reverb volume register to hardware. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. voice - synthesizer voice structure RVol - new reverb volume @@ -643,7 +642,7 @@ static void snd_trident_write_rvol_reg(struct snd_trident * trident, Description: This routine will write the new chorus volume register to hardware. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. voice - synthesizer voice structure CVol - new chorus volume @@ -666,7 +665,7 @@ static void snd_trident_write_cvol_reg(struct snd_trident * trident, Description: This routine converts rate in HZ to hardware delta value. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. rate - Real or Virtual channel number. Returns: Delta value. @@ -696,7 +695,7 @@ static unsigned int snd_trident_convert_rate(unsigned int rate) Description: This routine converts rate in HZ to hardware delta value. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. rate - Real or Virtual channel number. Returns: Delta value. @@ -726,7 +725,7 @@ static unsigned int snd_trident_convert_adc_rate(unsigned int rate) Description: This routine converts rate in HZ to spurious threshold. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. rate - Real or Virtual channel number. Returns: Delta value. @@ -748,7 +747,7 @@ static unsigned int snd_trident_spurious_threshold(unsigned int rate, Description: This routine returns a control mode for a PCM channel. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. substream - PCM substream Returns: Control value. @@ -781,7 +780,7 @@ static unsigned int snd_trident_control_mode(struct snd_pcm_substream *substream Description: Device I/O control handler for playback/capture parameters. - Paramters: substream - PCM substream class + Parameters: substream - PCM substream class cmd - what ioctl message to process arg - additional message infoarg @@ -1664,7 +1663,7 @@ static snd_pcm_uframes_t snd_trident_playback_pointer(struct snd_pcm_substream * Description: This routine return the capture position - Paramters: pcm1 - PCM device class + Parameters: pcm1 - PCM device class Returns: position of buffer @@ -2157,7 +2156,7 @@ static struct snd_pcm_ops snd_trident_spdif_7018_ops = { Description: This routine registers the 4DWave device for PCM support. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. Returns: None @@ -2215,7 +2214,7 @@ int __devinit snd_trident_pcm(struct snd_trident * trident, Description: This routine registers the 4DWave device for foldback PCM support. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. Returns: None @@ -2272,7 +2271,7 @@ int __devinit snd_trident_foldback_pcm(struct snd_trident * trident, Description: This routine registers the 4DWave-NX device for SPDIF support. - Paramters: trident - pointer to target device class for 4DWave-NX. + Parameters: trident - pointer to target device class for 4DWave-NX. Returns: None @@ -2956,7 +2955,7 @@ static int snd_trident_pcm_mixer_free(struct snd_trident *trident, struct snd_tr Description: This routine registers the 4DWave device for mixer support. - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. Returns: None @@ -3313,12 +3312,6 @@ static void snd_trident_proc_read(struct snd_info_entry *entry, snd_iprintf(buffer, "Memory Free : %d\n", snd_util_mem_avail(trident->tlb.memhdr)); } } -#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) - snd_iprintf(buffer,"\nWavetable Synth\n"); - snd_iprintf(buffer, "Memory Maximum : %d\n", trident->synth.max_size); - snd_iprintf(buffer, "Memory Used : %d\n", trident->synth.current_size); - snd_iprintf(buffer, "Memory Free : %d\n", (trident->synth.max_size-trident->synth.current_size)); -#endif } static void __devinit snd_trident_proc_init(struct snd_trident * trident) @@ -3344,7 +3337,7 @@ static int snd_trident_dev_free(struct snd_device *device) Description: Allocate and set up the TLB page table on 4D NX. Each entry has 4 bytes (physical PCI address). - Paramters: trident - pointer to target device class for 4DWave. + Parameters: trident - pointer to target device class for 4DWave. Returns: 0 or negative error code @@ -3521,7 +3514,7 @@ static int snd_trident_sis_init(struct snd_trident *trident) Description: This routine will create the device specific class for the 4DWave card. It will also perform basic initialization. - Paramters: card - which card to create + Parameters: card - which card to create pci - interface to PCI bus resource info dma1ptr - playback dma buffer dma2ptr - capture dma buffer @@ -3667,7 +3660,7 @@ int __devinit snd_trident_create(struct snd_card *card, Description: This routine will free the device specific class for the 4DWave card. - Paramters: trident - device specific private data for 4DWave card + Parameters: trident - device specific private data for 4DWave card Returns: None. @@ -3705,7 +3698,7 @@ static int snd_trident_free(struct snd_trident *trident) Description: ISR for Trident 4DWave device - Paramters: trident - device specific private data for 4DWave card + Parameters: trident - device specific private data for 4DWave card Problems: It seems that Trident chips generates interrupts more than one time in special cases. The spurious interrupts are @@ -3815,28 +3808,6 @@ static irqreturn_t snd_trident_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -/*--------------------------------------------------------------------------- - snd_trident_attach_synthesizer - - Description: Attach synthesizer hooks - - Paramters: trident - device specific private data for 4DWave card - - Returns: None. - - ---------------------------------------------------------------------------*/ -int snd_trident_attach_synthesizer(struct snd_trident *trident) -{ -#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) - if (snd_seq_device_new(trident->card, 1, SNDRV_SEQ_DEV_ID_TRIDENT, - sizeof(struct snd_trident *), &trident->seq_dev) >= 0) { - strcpy(trident->seq_dev->name, "4DWave"); - *(struct snd_trident **)SNDRV_SEQ_DEVICE_ARGPTR(trident->seq_dev) = trident; - } -#endif - return 0; -} - struct snd_trident_voice *snd_trident_alloc_voice(struct snd_trident * trident, int type, int client, int port) { struct snd_trident_voice *pvoice; diff --git a/sound/pci/trident/trident_memory.c b/sound/pci/trident/trident_memory.c index 847b8c6..df9b487 100644 --- a/sound/pci/trident/trident_memory.c +++ b/sound/pci/trident/trident_memory.c @@ -23,7 +23,6 @@ * */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/pci.h> #include <linux/time.h> diff --git a/sound/pci/trident/trident_synth.c b/sound/pci/trident/trident_synth.c deleted file mode 100644 index 9b7dee8..0000000 --- a/sound/pci/trident/trident_synth.c +++ /dev/null @@ -1,1024 +0,0 @@ -/* - * Routines for Trident 4DWave NX/DX soundcards - Synthesizer - * Copyright (c) by Scott McNab <jedi@tartarus.uwa.edu.au> - * - * - * 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/init.h> -#include <linux/slab.h> -#include <linux/pci.h> -#include <sound/core.h> -#include <sound/trident.h> -#include <sound/seq_device.h> - -MODULE_AUTHOR("Scott McNab <jedi@tartarus.uwa.edu.au>"); -MODULE_DESCRIPTION("Routines for Trident 4DWave NX/DX soundcards - Synthesizer"); -MODULE_LICENSE("GPL"); - -/* linear to log pan conversion table (4.2 channel attenuation format) */ -static unsigned int pan_table[63] = { - 7959, 7733, 7514, 7301, 7093, 6892, 6697, 6507, - 6322, 6143, 5968, 5799, 5634, 5475, 5319, 5168, - 5022, 4879, 4741, 4606, 4475, 4349, 4225, 4105, - 3989, 3876, 3766, 3659, 3555, 3454, 3356, 3261, - 3168, 3078, 2991, 2906, 2824, 2744, 2666, 2590, - 2517, 2445, 2376, 2308, 2243, 2179, 2117, 2057, - 1999, 1942, 1887, 1833, 1781, 1731, 1682, 1634, - 1588, 1543, 1499, 1456, 1415, 1375, 1336 -}; - -#define LOG_TABLE_SIZE 386 - -/* Linear half-attenuation to log conversion table in the format: - * {linear volume, logarithmic attenuation equivalent}, ... - * - * Provides conversion from a linear half-volume value in the range - * [0,8192] to a logarithmic attenuation value in the range 0 to 6.02dB. - * Halving the linear volume is equivalent to an additional 6dB of - * logarithmic attenuation. The algorithm used in log_from_linear() - * therefore uses this table as follows: - * - * - loop and for every time the volume is less than half the maximum - * volume (16384), add another 6dB and halve the maximum value used - * for this comparison. - * - when the volume is greater than half the maximum volume, take - * the difference of the volume to half volume (in the range [0,8192]) - * and look up the log_table[] to find the nearest entry. - * - take the logarithic component of this entry and add it to the - * resulting attenuation. - * - * Thus this routine provides a linear->log conversion for a range of - * [0,16384] using only 386 table entries - * - * Note: although this table stores log attenuation in 8.8 format, values - * were only calculated for 6 bits fractional precision, since that is - * the most precision offered by the trident hardware. - */ - -static unsigned short log_table[LOG_TABLE_SIZE*2] = -{ - 4, 0x0604, 19, 0x0600, 34, 0x05fc, - 49, 0x05f8, 63, 0x05f4, 78, 0x05f0, 93, 0x05ec, 108, 0x05e8, - 123, 0x05e4, 138, 0x05e0, 153, 0x05dc, 168, 0x05d8, 183, 0x05d4, - 198, 0x05d0, 213, 0x05cc, 228, 0x05c8, 244, 0x05c4, 259, 0x05c0, - 274, 0x05bc, 289, 0x05b8, 304, 0x05b4, 320, 0x05b0, 335, 0x05ac, - 350, 0x05a8, 366, 0x05a4, 381, 0x05a0, 397, 0x059c, 412, 0x0598, - 428, 0x0594, 443, 0x0590, 459, 0x058c, 474, 0x0588, 490, 0x0584, - 506, 0x0580, 521, 0x057c, 537, 0x0578, 553, 0x0574, 568, 0x0570, - 584, 0x056c, 600, 0x0568, 616, 0x0564, 632, 0x0560, 647, 0x055c, - 663, 0x0558, 679, 0x0554, 695, 0x0550, 711, 0x054c, 727, 0x0548, - 743, 0x0544, 759, 0x0540, 776, 0x053c, 792, 0x0538, 808, 0x0534, - 824, 0x0530, 840, 0x052c, 857, 0x0528, 873, 0x0524, 889, 0x0520, - 906, 0x051c, 922, 0x0518, 938, 0x0514, 955, 0x0510, 971, 0x050c, - 988, 0x0508, 1004, 0x0504, 1021, 0x0500, 1037, 0x04fc, 1054, 0x04f8, - 1071, 0x04f4, 1087, 0x04f0, 1104, 0x04ec, 1121, 0x04e8, 1138, 0x04e4, - 1154, 0x04e0, 1171, 0x04dc, 1188, 0x04d8, 1205, 0x04d4, 1222, 0x04d0, - 1239, 0x04cc, 1256, 0x04c8, 1273, 0x04c4, 1290, 0x04c0, 1307, 0x04bc, - 1324, 0x04b8, 1341, 0x04b4, 1358, 0x04b0, 1376, 0x04ac, 1393, 0x04a8, - 1410, 0x04a4, 1427, 0x04a0, 1445, 0x049c, 1462, 0x0498, 1479, 0x0494, - 1497, 0x0490, 1514, 0x048c, 1532, 0x0488, 1549, 0x0484, 1567, 0x0480, - 1584, 0x047c, 1602, 0x0478, 1620, 0x0474, 1637, 0x0470, 1655, 0x046c, - 1673, 0x0468, 1690, 0x0464, 1708, 0x0460, 1726, 0x045c, 1744, 0x0458, - 1762, 0x0454, 1780, 0x0450, 1798, 0x044c, 1816, 0x0448, 1834, 0x0444, - 1852, 0x0440, 1870, 0x043c, 1888, 0x0438, 1906, 0x0434, 1924, 0x0430, - 1943, 0x042c, 1961, 0x0428, 1979, 0x0424, 1997, 0x0420, 2016, 0x041c, - 2034, 0x0418, 2053, 0x0414, 2071, 0x0410, 2089, 0x040c, 2108, 0x0408, - 2127, 0x0404, 2145, 0x0400, 2164, 0x03fc, 2182, 0x03f8, 2201, 0x03f4, - 2220, 0x03f0, 2239, 0x03ec, 2257, 0x03e8, 2276, 0x03e4, 2295, 0x03e0, - 2314, 0x03dc, 2333, 0x03d8, 2352, 0x03d4, 2371, 0x03d0, 2390, 0x03cc, - 2409, 0x03c8, 2428, 0x03c4, 2447, 0x03c0, 2466, 0x03bc, 2485, 0x03b8, - 2505, 0x03b4, 2524, 0x03b0, 2543, 0x03ac, 2562, 0x03a8, 2582, 0x03a4, - 2601, 0x03a0, 2621, 0x039c, 2640, 0x0398, 2660, 0x0394, 2679, 0x0390, - 2699, 0x038c, 2718, 0x0388, 2738, 0x0384, 2758, 0x0380, 2777, 0x037c, - 2797, 0x0378, 2817, 0x0374, 2837, 0x0370, 2857, 0x036c, 2876, 0x0368, - 2896, 0x0364, 2916, 0x0360, 2936, 0x035c, 2956, 0x0358, 2976, 0x0354, - 2997, 0x0350, 3017, 0x034c, 3037, 0x0348, 3057, 0x0344, 3077, 0x0340, - 3098, 0x033c, 3118, 0x0338, 3138, 0x0334, 3159, 0x0330, 3179, 0x032c, - 3200, 0x0328, 3220, 0x0324, 3241, 0x0320, 3261, 0x031c, 3282, 0x0318, - 3303, 0x0314, 3323, 0x0310, 3344, 0x030c, 3365, 0x0308, 3386, 0x0304, - 3406, 0x0300, 3427, 0x02fc, 3448, 0x02f8, 3469, 0x02f4, 3490, 0x02f0, - 3511, 0x02ec, 3532, 0x02e8, 3553, 0x02e4, 3575, 0x02e0, 3596, 0x02dc, - 3617, 0x02d8, 3638, 0x02d4, 3660, 0x02d0, 3681, 0x02cc, 3702, 0x02c8, - 3724, 0x02c4, 3745, 0x02c0, 3767, 0x02bc, 3788, 0x02b8, 3810, 0x02b4, - 3831, 0x02b0, 3853, 0x02ac, 3875, 0x02a8, 3896, 0x02a4, 3918, 0x02a0, - 3940, 0x029c, 3962, 0x0298, 3984, 0x0294, 4006, 0x0290, 4028, 0x028c, - 4050, 0x0288, 4072, 0x0284, 4094, 0x0280, 4116, 0x027c, 4138, 0x0278, - 4160, 0x0274, 4182, 0x0270, 4205, 0x026c, 4227, 0x0268, 4249, 0x0264, - 4272, 0x0260, 4294, 0x025c, 4317, 0x0258, 4339, 0x0254, 4362, 0x0250, - 4384, 0x024c, 4407, 0x0248, 4430, 0x0244, 4453, 0x0240, 4475, 0x023c, - 4498, 0x0238, 4521, 0x0234, 4544, 0x0230, 4567, 0x022c, 4590, 0x0228, - 4613, 0x0224, 4636, 0x0220, 4659, 0x021c, 4682, 0x0218, 4705, 0x0214, - 4728, 0x0210, 4752, 0x020c, 4775, 0x0208, 4798, 0x0204, 4822, 0x0200, - 4845, 0x01fc, 4869, 0x01f8, 4892, 0x01f4, 4916, 0x01f0, 4939, 0x01ec, - 4963, 0x01e8, 4987, 0x01e4, 5010, 0x01e0, 5034, 0x01dc, 5058, 0x01d8, - 5082, 0x01d4, 5106, 0x01d0, 5130, 0x01cc, 5154, 0x01c8, 5178, 0x01c4, - 5202, 0x01c0, 5226, 0x01bc, 5250, 0x01b8, 5274, 0x01b4, 5299, 0x01b0, - 5323, 0x01ac, 5347, 0x01a8, 5372, 0x01a4, 5396, 0x01a0, 5420, 0x019c, - 5445, 0x0198, 5469, 0x0194, 5494, 0x0190, 5519, 0x018c, 5543, 0x0188, - 5568, 0x0184, 5593, 0x0180, 5618, 0x017c, 5643, 0x0178, 5668, 0x0174, - 5692, 0x0170, 5717, 0x016c, 5743, 0x0168, 5768, 0x0164, 5793, 0x0160, - 5818, 0x015c, 5843, 0x0158, 5868, 0x0154, 5894, 0x0150, 5919, 0x014c, - 5945, 0x0148, 5970, 0x0144, 5995, 0x0140, 6021, 0x013c, 6047, 0x0138, - 6072, 0x0134, 6098, 0x0130, 6124, 0x012c, 6149, 0x0128, 6175, 0x0124, - 6201, 0x0120, 6227, 0x011c, 6253, 0x0118, 6279, 0x0114, 6305, 0x0110, - 6331, 0x010c, 6357, 0x0108, 6384, 0x0104, 6410, 0x0100, 6436, 0x00fc, - 6462, 0x00f8, 6489, 0x00f4, 6515, 0x00f0, 6542, 0x00ec, 6568, 0x00e8, - 6595, 0x00e4, 6621, 0x00e0, 6648, 0x00dc, 6675, 0x00d8, 6702, 0x00d4, - 6728, 0x00d0, 6755, 0x00cc, 6782, 0x00c8, 6809, 0x00c4, 6836, 0x00c0, - 6863, 0x00bc, 6890, 0x00b8, 6917, 0x00b4, 6945, 0x00b0, 6972, 0x00ac, - 6999, 0x00a8, 7027, 0x00a4, 7054, 0x00a0, 7081, 0x009c, 7109, 0x0098, - 7136, 0x0094, 7164, 0x0090, 7192, 0x008c, 7219, 0x0088, 7247, 0x0084, - 7275, 0x0080, 7303, 0x007c, 7331, 0x0078, 7359, 0x0074, 7387, 0x0070, - 7415, 0x006c, 7443, 0x0068, 7471, 0x0064, 7499, 0x0060, 7527, 0x005c, - 7556, 0x0058, 7584, 0x0054, 7613, 0x0050, 7641, 0x004c, 7669, 0x0048, - 7698, 0x0044, 7727, 0x0040, 7755, 0x003c, 7784, 0x0038, 7813, 0x0034, - 7842, 0x0030, 7870, 0x002c, 7899, 0x0028, 7928, 0x0024, 7957, 0x0020, - 7986, 0x001c, 8016, 0x0018, 8045, 0x0014, 8074, 0x0010, 8103, 0x000c, - 8133, 0x0008, 8162, 0x0004, 8192, 0x0000 -}; - -static unsigned short lookup_volume_table( unsigned short value ) -{ - /* This code is an optimised version of: - * int i = 0; - * while( volume_table[i*2] < value ) - * i++; - * return volume_table[i*2+1]; - */ - unsigned short *ptr = log_table; - while( *ptr < value ) - ptr += 2; - return *(ptr+1); -} - -/* this function calculates a 8.8 fixed point logarithmic attenuation - * value from a linear volume value in the range 0 to 16384 */ -static unsigned short log_from_linear( unsigned short value ) -{ - if (value >= 16384) - return 0x0000; - if (value) { - unsigned short result = 0; - int v, c; - for( c = 0, v = 8192; c < 14; c++, v >>= 1 ) { - if( value >= v ) { - result += lookup_volume_table( (value - v) << c ); - return result; - } - result += 0x0605; /* 6.0205 (result of -20*log10(0.5)) */ - } - } - return 0xffff; -} - -/* - * Sample handling operations - */ - -static void sample_start(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position); -static void sample_stop(struct snd_trident * trident, struct snd_trident_voice * voice, int mode); -static void sample_freq(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_frequency_t freq); -static void sample_volume(struct snd_trident * trident, struct snd_trident_voice * voice, struct snd_seq_ev_volume * volume); -static void sample_loop(struct snd_trident * trident, struct snd_trident_voice * voice, struct snd_seq_ev_loop * loop); -static void sample_pos(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position); -static void sample_private1(struct snd_trident * trident, struct snd_trident_voice * voice, unsigned char *data); - -static struct snd_trident_sample_ops sample_ops = -{ - sample_start, - sample_stop, - sample_freq, - sample_volume, - sample_loop, - sample_pos, - sample_private1 -}; - -static void snd_trident_simple_init(struct snd_trident_voice * voice) -{ - //voice->handler_wave = interrupt_wave; - //voice->handler_volume = interrupt_volume; - //voice->handler_effect = interrupt_effect; - //voice->volume_change = NULL; - voice->sample_ops = &sample_ops; -} - -static void sample_start(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position) -{ - struct simple_instrument *simple; - struct snd_seq_kinstr *instr; - unsigned long flags; - unsigned int loop_start, loop_end, sample_start, sample_end, start_offset; - unsigned int value; - unsigned int shift = 0; - - instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1); - if (instr == NULL) - return; - voice->instr = instr->instr; /* copy ID to speedup aliases */ - simple = KINSTR_DATA(instr); - - spin_lock_irqsave(&trident->reg_lock, flags); - - if (trident->device == TRIDENT_DEVICE_ID_SI7018) - voice->GVSel = 1; /* route to Wave volume */ - - voice->CTRL = 0; - voice->Alpha = 0; - voice->FMS = 0; - - loop_start = simple->loop_start >> 4; - loop_end = simple->loop_end >> 4; - sample_start = (simple->start + position) >> 4; - if( sample_start >= simple->size ) - sample_start = simple->start >> 4; - sample_end = simple->size; - start_offset = position >> 4; - - if (simple->format & SIMPLE_WAVE_16BIT) { - voice->CTRL |= 8; - shift++; - } - if (simple->format & SIMPLE_WAVE_STEREO) { - voice->CTRL |= 4; - shift++; - } - if (!(simple->format & SIMPLE_WAVE_UNSIGNED)) - voice->CTRL |= 2; - - voice->LBA = simple->address.memory; - - if (simple->format & SIMPLE_WAVE_LOOP) { - voice->CTRL |= 1; - voice->LBA += loop_start << shift; - if( start_offset >= loop_start ) { - voice->CSO = start_offset - loop_start; - voice->negCSO = 0; - } else { - voice->CSO = loop_start - start_offset; - voice->negCSO = 1; - } - voice->ESO = loop_end - loop_start - 1; - } else { - voice->LBA += start_offset << shift; - voice->CSO = sample_start; - voice->ESO = sample_end - 1; - voice->negCSO = 0; - } - - if (voice->flags & SNDRV_TRIDENT_VFLG_RUNNING) { - snd_trident_stop_voice(trident, voice->number); - voice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING; - } - - /* set CSO sign */ - value = inl(TRID_REG(trident, T4D_SIGN_CSO_A)); - if( voice->negCSO ) { - value |= 1 << (voice->number&31); - } else { - value &= ~(1 << (voice->number&31)); - } - outl(value,TRID_REG(trident, T4D_SIGN_CSO_A)); - - voice->Attribute = 0; - snd_trident_write_voice_regs(trident, voice); - snd_trident_start_voice(trident, voice->number); - voice->flags |= SNDRV_TRIDENT_VFLG_RUNNING; - spin_unlock_irqrestore(&trident->reg_lock, flags); - snd_seq_instr_free_use(trident->synth.ilist, instr); -} - -static void sample_stop(struct snd_trident * trident, struct snd_trident_voice * voice, int mode) -{ - unsigned long flags; - - if (!(voice->flags & SNDRV_TRIDENT_VFLG_RUNNING)) - return; - - switch (mode) { - default: - spin_lock_irqsave(&trident->reg_lock, flags); - snd_trident_stop_voice(trident, voice->number); - voice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING; - spin_unlock_irqrestore(&trident->reg_lock, flags); - break; - case SAMPLE_STOP_LOOP: /* disable loop only */ - voice->CTRL &= ~1; - spin_lock_irqsave(&trident->reg_lock, flags); - outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); - outw((((voice->CTRL << 12) | (voice->EC & 0x0fff)) & 0xffff), CH_GVSEL_PAN_VOL_CTRL_EC); - spin_unlock_irqrestore(&trident->reg_lock, flags); - break; - } -} - -static void sample_freq(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_frequency_t freq) -{ - unsigned long flags; - freq >>= 4; - - spin_lock_irqsave(&trident->reg_lock, flags); - if (freq == 44100) - voice->Delta = 0xeb3; - else if (freq == 8000) - voice->Delta = 0x2ab; - else if (freq == 48000) - voice->Delta = 0x1000; - else - voice->Delta = (((freq << 12) + freq) / 48000) & 0x0000ffff; - - outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); - if (trident->device == TRIDENT_DEVICE_ID_NX) { - outb((unsigned char) voice->Delta, TRID_REG(trident, CH_NX_DELTA_CSO + 3)); - outb((unsigned char) (voice->Delta >> 8), TRID_REG(trident, CH_NX_DELTA_ESO + 3)); - } else { - outw((unsigned short) voice->Delta, TRID_REG(trident, CH_DX_ESO_DELTA)); - } - - spin_unlock_irqrestore(&trident->reg_lock, flags); -} - -static void sample_volume(struct snd_trident * trident, struct snd_trident_voice * voice, struct snd_seq_ev_volume * volume) -{ - unsigned long flags; - unsigned short value; - - spin_lock_irqsave(&trident->reg_lock, flags); - voice->GVSel = 0; /* use global music volume */ - voice->FMC = 0x03; /* fixme: can we do something useful with FMC? */ - if (volume->volume >= 0) { - volume->volume &= 0x3fff; - /* linear volume -> logarithmic attenuation conversion - * uses EC register for greater resolution (6.6 bits) than Vol register (5.3 bits) - * Vol register used when additional attenuation is required */ - voice->RVol = 0; - voice->CVol = 0; - value = log_from_linear( volume->volume ); - voice->Vol = 0; - voice->EC = (value & 0x3fff) >> 2; - if (value > 0x3fff) { - voice->EC |= 0xfc0; - if (value < 0x5f00 ) - voice->Vol = ((value >> 8) - 0x3f) << 5; - else { - voice->Vol = 0x3ff; - voice->EC = 0xfff; - } - } - } - if (volume->lr >= 0) { - volume->lr &= 0x3fff; - /* approximate linear pan by attenuating channels */ - if (volume->lr >= 0x2000) { /* attenuate left (pan right) */ - value = 0x3fff - volume->lr; - for (voice->Pan = 0; voice->Pan < 63; voice->Pan++ ) - if (value >= pan_table[voice->Pan] ) - break; - } else { /* attenuate right (pan left) */ - for (voice->Pan = 0; voice->Pan < 63; voice->Pan++ ) - if ((unsigned int)volume->lr >= pan_table[voice->Pan] ) - break; - voice->Pan |= 0x40; - } - } - outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); - outl((voice->GVSel << 31) | ((voice->Pan & 0x0000007f) << 24) | - ((voice->Vol & 0x000000ff) << 16) | ((voice->CTRL & 0x0000000f) << 12) | - (voice->EC & 0x00000fff), TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC)); - value = ((voice->FMC & 0x03) << 14) | ((voice->RVol & 0x7f) << 7) | (voice->CVol & 0x7f); - outw(value, TRID_REG(trident, CH_DX_FMC_RVOL_CVOL)); - spin_unlock_irqrestore(&trident->reg_lock, flags); -} - -static void sample_loop(struct snd_trident * trident, struct snd_trident_voice * voice, struct snd_seq_ev_loop * loop) -{ - unsigned long flags; - struct simple_instrument *simple; - struct snd_seq_kinstr *instr; - unsigned int loop_start, loop_end; - - instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1); - if (instr == NULL) - return; - voice->instr = instr->instr; /* copy ID to speedup aliases */ - simple = KINSTR_DATA(instr); - - loop_start = loop->start >> 4; - loop_end = loop->end >> 4; - - spin_lock_irqsave(&trident->reg_lock, flags); - - voice->LBA = simple->address.memory + loop_start; - voice->CSO = 0; - voice->ESO = loop_end - loop_start - 1; - - outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); - outb((voice->LBA >> 16), TRID_REG(trident, CH_LBA + 2)); - outw((voice->LBA & 0xffff), TRID_REG(trident, CH_LBA)); - if (trident->device == TRIDENT_DEVICE_ID_NX) { - outb((voice->ESO >> 16), TRID_REG(trident, CH_NX_DELTA_ESO + 2)); - outw((voice->ESO & 0xffff), TRID_REG(trident, CH_NX_DELTA_ESO)); - outb((voice->CSO >> 16), TRID_REG(trident, CH_NX_DELTA_CSO + 2)); - outw((voice->CSO & 0xffff), TRID_REG(trident, CH_NX_DELTA_CSO)); - } else { - outw((voice->ESO & 0xffff), TRID_REG(trident, CH_DX_ESO_DELTA + 2)); - outw((voice->CSO & 0xffff), TRID_REG(trident, CH_DX_CSO_ALPHA_FMS + 2)); - } - - spin_unlock_irqrestore(&trident->reg_lock, flags); - snd_seq_instr_free_use(trident->synth.ilist, instr); -} - -static void sample_pos(struct snd_trident * trident, struct snd_trident_voice * voice, snd_seq_position_t position) -{ - unsigned long flags; - struct simple_instrument *simple; - struct snd_seq_kinstr *instr; - unsigned int value; - - instr = snd_seq_instr_find(trident->synth.ilist, &voice->instr, 0, 1); - if (instr == NULL) - return; - voice->instr = instr->instr; /* copy ID to speedup aliases */ - simple = KINSTR_DATA(instr); - - spin_lock_irqsave(&trident->reg_lock, flags); - - if (simple->format & SIMPLE_WAVE_LOOP) { - if( position >= simple->loop_start ) { - voice->CSO = (position - simple->loop_start) >> 4; - voice->negCSO = 0; - } else { - voice->CSO = (simple->loop_start - position) >> 4; - voice->negCSO = 1; - } - } else { - voice->CSO = position >> 4; - voice->negCSO = 0; - } - - /* set CSO sign */ - value = inl(TRID_REG(trident, T4D_SIGN_CSO_A)); - if( voice->negCSO ) { - value |= 1 << (voice->number&31); - } else { - value &= ~(1 << (voice->number&31)); - } - outl(value,TRID_REG(trident, T4D_SIGN_CSO_A)); - - - outb((unsigned char) voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); - if (trident->device == TRIDENT_DEVICE_ID_NX) { - outw((voice->CSO & 0xffff), TRID_REG(trident, CH_NX_DELTA_CSO)); - outb((voice->CSO >> 16), TRID_REG(trident, CH_NX_DELTA_CSO + 2)); - } else { - outw((voice->CSO & 0xffff), TRID_REG(trident, CH_DX_CSO_ALPHA_FMS) + 2); - } - - spin_unlock_irqrestore(&trident->reg_lock, flags); - snd_seq_instr_free_use(trident->synth.ilist, instr); -} - -static void sample_private1(struct snd_trident * trident, struct snd_trident_voice * voice, unsigned char *data) -{ -} - -/* - * Memory management / sample loading - */ - -static int snd_trident_simple_put_sample(void *private_data, - struct simple_instrument * instr, - char __user *data, long len, int atomic) -{ - struct snd_trident *trident = private_data; - int size = instr->size; - int shift = 0; - - if (instr->format & SIMPLE_WAVE_BACKWARD || - instr->format & SIMPLE_WAVE_BIDIR || - instr->format & SIMPLE_WAVE_ULAW) - return -EINVAL; /* not supported */ - - if (instr->format & SIMPLE_WAVE_16BIT) - shift++; - if (instr->format & SIMPLE_WAVE_STEREO) - shift++; - size <<= shift; - - if (trident->synth.current_size + size > trident->synth.max_size) - return -ENOMEM; - - if (!access_ok(VERIFY_READ, data, size)) - return -EFAULT; - - if (trident->tlb.entries) { - struct snd_util_memblk *memblk; - memblk = snd_trident_synth_alloc(trident, size); - if (memblk == NULL) - return -ENOMEM; - if (snd_trident_synth_copy_from_user(trident, memblk, 0, data, size) ) { - snd_trident_synth_free(trident, memblk); - return -EFAULT; - } - instr->address.ptr = (unsigned char*)memblk; - instr->address.memory = memblk->offset; - } else { - struct snd_dma_buffer dmab; - if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci), - size, &dmab) < 0) - return -ENOMEM; - - if (copy_from_user(dmab.area, data, size)) { - snd_dma_free_pages(&dmab); - return -EFAULT; - } - instr->address.ptr = dmab.area; - instr->address.memory = dmab.addr; - } - - trident->synth.current_size += size; - return 0; -} - -static int snd_trident_simple_get_sample(void *private_data, - struct simple_instrument * instr, - char __user *data, long len, int atomic) -{ - //struct snd_trident *trident = private_data; - int size = instr->size; - int shift = 0; - - if (instr->format & SIMPLE_WAVE_16BIT) - shift++; - if (instr->format & SIMPLE_WAVE_STEREO) - shift++; - size <<= shift; - - if (!access_ok(VERIFY_WRITE, data, size)) - return -EFAULT; - - /* FIXME: not implemented yet */ - - return -EBUSY; -} - -static int snd_trident_simple_remove_sample(void *private_data, - struct simple_instrument * instr, - int atomic) -{ - struct snd_trident *trident = private_data; - int size = instr->size; - - if (instr->format & SIMPLE_WAVE_16BIT) - size <<= 1; - if (instr->format & SIMPLE_WAVE_STEREO) - size <<= 1; - - if (trident->tlb.entries) { - struct snd_util_memblk *memblk = (struct snd_util_memblk *)instr->address.ptr; - if (memblk) - snd_trident_synth_free(trident, memblk); - else - return -EFAULT; - } else { - struct snd_dma_buffer dmab; - dmab.dev.type = SNDRV_DMA_TYPE_DEV; - dmab.dev.dev = snd_dma_pci_data(trident->pci); - dmab.area = instr->address.ptr; - dmab.addr = instr->address.memory; - dmab.bytes = size; - snd_dma_free_pages(&dmab); - } - - trident->synth.current_size -= size; - if (trident->synth.current_size < 0) /* shouldn't need this check... */ - trident->synth.current_size = 0; - - return 0; -} - -static void select_instrument(struct snd_trident * trident, struct snd_trident_voice * v) -{ - struct snd_seq_kinstr *instr; - instr = snd_seq_instr_find(trident->synth.ilist, &v->instr, 0, 1); - if (instr != NULL) { - if (instr->ops) { - if (!strcmp(instr->ops->instr_type, SNDRV_SEQ_INSTR_ID_SIMPLE)) - snd_trident_simple_init(v); - } - snd_seq_instr_free_use(trident->synth.ilist, instr); - } -} - -/* - - */ - -static void event_sample(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v) -{ - if (v->sample_ops && v->sample_ops->sample_stop) - v->sample_ops->sample_stop(p->trident, v, SAMPLE_STOP_IMMEDIATELY); - v->instr.std = ev->data.sample.param.sample.std; - if (v->instr.std & 0xff000000) { /* private instrument */ - v->instr.std &= 0x00ffffff; - v->instr.std |= (unsigned int)ev->source.client << 24; - } - v->instr.bank = ev->data.sample.param.sample.bank; - v->instr.prg = ev->data.sample.param.sample.prg; - select_instrument(p->trident, v); -} - -static void event_cluster(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v) -{ - if (v->sample_ops && v->sample_ops->sample_stop) - v->sample_ops->sample_stop(p->trident, v, SAMPLE_STOP_IMMEDIATELY); - v->instr.cluster = ev->data.sample.param.cluster.cluster; - select_instrument(p->trident, v); -} - -static void event_start(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v) -{ - if (v->sample_ops && v->sample_ops->sample_start) - v->sample_ops->sample_start(p->trident, v, ev->data.sample.param.position); -} - -static void event_stop(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v) -{ - if (v->sample_ops && v->sample_ops->sample_stop) - v->sample_ops->sample_stop(p->trident, v, ev->data.sample.param.stop_mode); -} - -static void event_freq(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v) -{ - if (v->sample_ops && v->sample_ops->sample_freq) - v->sample_ops->sample_freq(p->trident, v, ev->data.sample.param.frequency); -} - -static void event_volume(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v) -{ - if (v->sample_ops && v->sample_ops->sample_volume) - v->sample_ops->sample_volume(p->trident, v, &ev->data.sample.param.volume); -} - -static void event_loop(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v) -{ - if (v->sample_ops && v->sample_ops->sample_loop) - v->sample_ops->sample_loop(p->trident, v, &ev->data.sample.param.loop); -} - -static void event_position(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v) -{ - if (v->sample_ops && v->sample_ops->sample_pos) - v->sample_ops->sample_pos(p->trident, v, ev->data.sample.param.position); -} - -static void event_private1(struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v) -{ - if (v->sample_ops && v->sample_ops->sample_private1) - v->sample_ops->sample_private1(p->trident, v, (unsigned char *) &ev->data.sample.param.raw8); -} - -typedef void (trident_sample_event_handler_t) (struct snd_seq_event * ev, struct snd_trident_port * p, struct snd_trident_voice * v); - -static trident_sample_event_handler_t *trident_sample_event_handlers[9] = -{ - event_sample, - event_cluster, - event_start, - event_stop, - event_freq, - event_volume, - event_loop, - event_position, - event_private1 -}; - -static void snd_trident_sample_event(struct snd_seq_event * ev, struct snd_trident_port * p) -{ - int idx, voice; - struct snd_trident *trident = p->trident; - struct snd_trident_voice *v; - unsigned long flags; - - idx = ev->type - SNDRV_SEQ_EVENT_SAMPLE; - if (idx < 0 || idx > 8) - return; - for (voice = 0; voice < 64; voice++) { - v = &trident->synth.voices[voice]; - if (v->use && v->client == ev->source.client && - v->port == ev->source.port && - v->index == ev->data.sample.channel) { - spin_lock_irqsave(&trident->event_lock, flags); - trident_sample_event_handlers[idx] (ev, p, v); - spin_unlock_irqrestore(&trident->event_lock, flags); - return; - } - } -} - -/* - - */ - -static void snd_trident_synth_free_voices(struct snd_trident * trident, int client, int port) -{ - int idx; - struct snd_trident_voice *voice; - - for (idx = 0; idx < 32; idx++) { - voice = &trident->synth.voices[idx]; - if (voice->use && voice->client == client && voice->port == port) - snd_trident_free_voice(trident, voice); - } -} - -static int snd_trident_synth_use(void *private_data, struct snd_seq_port_subscribe * info) -{ - struct snd_trident_port *port = private_data; - struct snd_trident *trident = port->trident; - struct snd_trident_voice *voice; - unsigned int idx; - unsigned long flags; - - if (info->voices > 32) - return -EINVAL; - spin_lock_irqsave(&trident->reg_lock, flags); - for (idx = 0; idx < info->voices; idx++) { - voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_SYNTH, info->sender.client, info->sender.port); - if (voice == NULL) { - snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port); - spin_unlock_irqrestore(&trident->reg_lock, flags); - return -EBUSY; - } - voice->index = idx; - voice->Vol = 0x3ff; - voice->EC = 0x0fff; - } -#if 0 - for (idx = 0; idx < info->midi_voices; idx++) { - port->midi_has_voices = 1; - voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_MIDI, info->sender.client, info->sender.port); - if (voice == NULL) { - snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port); - spin_unlock_irqrestore(&trident->reg_lock, flags); - return -EBUSY; - } - voice->Vol = 0x3ff; - voice->EC = 0x0fff; - } -#endif - spin_unlock_irqrestore(&trident->reg_lock, flags); - return 0; -} - -static int snd_trident_synth_unuse(void *private_data, struct snd_seq_port_subscribe * info) -{ - struct snd_trident_port *port = private_data; - struct snd_trident *trident = port->trident; - unsigned long flags; - - spin_lock_irqsave(&trident->reg_lock, flags); - snd_trident_synth_free_voices(trident, info->sender.client, info->sender.port); - spin_unlock_irqrestore(&trident->reg_lock, flags); - return 0; -} - -/* - - */ - -static void snd_trident_synth_free_private_instruments(struct snd_trident_port * p, int client) -{ - struct snd_seq_instr_header ifree; - - memset(&ifree, 0, sizeof(ifree)); - ifree.cmd = SNDRV_SEQ_INSTR_FREE_CMD_PRIVATE; - snd_seq_instr_list_free_cond(p->trident->synth.ilist, &ifree, client, 0); -} - -static int snd_trident_synth_event_input(struct snd_seq_event * ev, int direct, void *private_data, int atomic, int hop) -{ - struct snd_trident_port *p = (struct snd_trident_port *) private_data; - - if (p == NULL) - return -EINVAL; - if (ev->type >= SNDRV_SEQ_EVENT_SAMPLE && - ev->type <= SNDRV_SEQ_EVENT_SAMPLE_PRIVATE1) { - snd_trident_sample_event(ev, p); - return 0; - } - if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM && - ev->source.port == SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE) { - if (ev->type == SNDRV_SEQ_EVENT_CLIENT_EXIT) { - snd_trident_synth_free_private_instruments(p, ev->data.addr.client); - return 0; - } - } - if (direct) { - if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN) { - snd_seq_instr_event(&p->trident->synth.simple_ops.kops, - p->trident->synth.ilist, ev, - p->trident->synth.seq_client, atomic, hop); - return 0; - } - } - return 0; -} - -static void snd_trident_synth_instr_notify(void *private_data, - struct snd_seq_kinstr * instr, - int what) -{ - int idx; - struct snd_trident *trident = private_data; - struct snd_trident_voice *pvoice; - unsigned long flags; - - spin_lock_irqsave(&trident->event_lock, flags); - for (idx = 0; idx < 64; idx++) { - pvoice = &trident->synth.voices[idx]; - if (pvoice->use && !memcmp(&pvoice->instr, &instr->instr, sizeof(pvoice->instr))) { - if (pvoice->sample_ops && pvoice->sample_ops->sample_stop) { - pvoice->sample_ops->sample_stop(trident, pvoice, SAMPLE_STOP_IMMEDIATELY); - } else { - snd_trident_stop_voice(trident, pvoice->number); - pvoice->flags &= ~SNDRV_TRIDENT_VFLG_RUNNING; - } - } - } - spin_unlock_irqrestore(&trident->event_lock, flags); -} - -/* - - */ - -static void snd_trident_synth_free_port(void *private_data) -{ - struct snd_trident_port *p = (struct snd_trident_port *) private_data; - - if (p) - snd_midi_channel_free_set(p->chset); -} - -static int snd_trident_synth_create_port(struct snd_trident * trident, int idx) -{ - struct snd_trident_port *p; - struct snd_seq_port_callback callbacks; - char name[32]; - char *str; - int result; - - p = &trident->synth.seq_ports[idx]; - p->chset = snd_midi_channel_alloc_set(16); - if (p->chset == NULL) - return -ENOMEM; - p->chset->private_data = p; - p->trident = trident; - p->client = trident->synth.seq_client; - - memset(&callbacks, 0, sizeof(callbacks)); - callbacks.owner = THIS_MODULE; - callbacks.use = snd_trident_synth_use; - callbacks.unuse = snd_trident_synth_unuse; - callbacks.event_input = snd_trident_synth_event_input; - callbacks.private_free = snd_trident_synth_free_port; - callbacks.private_data = p; - - str = "???"; - switch (trident->device) { - case TRIDENT_DEVICE_ID_DX: str = "Trident 4DWave-DX"; break; - case TRIDENT_DEVICE_ID_NX: str = "Trident 4DWave-NX"; break; - case TRIDENT_DEVICE_ID_SI7018: str = "SiS 7018"; break; - } - sprintf(name, "%s port %i", str, idx); - p->chset->port = snd_seq_event_port_attach(trident->synth.seq_client, - &callbacks, - SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE, - SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE | - SNDRV_SEQ_PORT_TYPE_SYNTH | - SNDRV_SEQ_PORT_TYPE_HARDWARE | - SNDRV_SEQ_PORT_TYPE_SYNTHESIZER, - 16, 0, - name); - if (p->chset->port < 0) { - result = p->chset->port; - snd_trident_synth_free_port(p); - return result; - } - p->port = p->chset->port; - return 0; -} - -/* - - */ - -static int snd_trident_synth_new_device(struct snd_seq_device *dev) -{ - struct snd_trident *trident; - int client, i; - struct snd_seq_port_subscribe sub; - struct snd_simple_ops *simpleops; - char *str; - - trident = *(struct snd_trident **)SNDRV_SEQ_DEVICE_ARGPTR(dev); - if (trident == NULL) - return -EINVAL; - - trident->synth.seq_client = -1; - - /* allocate new client */ - str = "???"; - switch (trident->device) { - case TRIDENT_DEVICE_ID_DX: str = "Trident 4DWave-DX"; break; - case TRIDENT_DEVICE_ID_NX: str = "Trident 4DWave-NX"; break; - case TRIDENT_DEVICE_ID_SI7018: str = "SiS 7018"; break; - } - client = trident->synth.seq_client = - snd_seq_create_kernel_client(trident->card, 1, str); - if (client < 0) - return client; - - for (i = 0; i < 4; i++) - snd_trident_synth_create_port(trident, i); - - trident->synth.ilist = snd_seq_instr_list_new(); - if (trident->synth.ilist == NULL) { - snd_seq_delete_kernel_client(client); - trident->synth.seq_client = -1; - return -ENOMEM; - } - trident->synth.ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT; - - simpleops = &trident->synth.simple_ops; - snd_seq_simple_init(simpleops, trident, NULL); - simpleops->put_sample = snd_trident_simple_put_sample; - simpleops->get_sample = snd_trident_simple_get_sample; - simpleops->remove_sample = snd_trident_simple_remove_sample; - simpleops->notify = snd_trident_synth_instr_notify; - - memset(&sub, 0, sizeof(sub)); - sub.sender.client = SNDRV_SEQ_CLIENT_SYSTEM; - sub.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; - sub.dest.client = client; - sub.dest.port = 0; - snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, &sub); - - return 0; -} - -static int snd_trident_synth_delete_device(struct snd_seq_device *dev) -{ - struct snd_trident *trident; - - trident = *(struct snd_trident **)SNDRV_SEQ_DEVICE_ARGPTR(dev); - if (trident == NULL) - return -EINVAL; - - if (trident->synth.seq_client >= 0) { - snd_seq_delete_kernel_client(trident->synth.seq_client); - trident->synth.seq_client = -1; - } - if (trident->synth.ilist) - snd_seq_instr_list_free(&trident->synth.ilist); - return 0; -} - -static int __init alsa_trident_synth_init(void) -{ - static struct snd_seq_dev_ops ops = - { - snd_trident_synth_new_device, - snd_trident_synth_delete_device - }; - - return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_TRIDENT, &ops, - sizeof(struct snd_trident *)); -} - -static void __exit alsa_trident_synth_exit(void) -{ - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_TRIDENT); -} - -module_init(alsa_trident_synth_init) -module_exit(alsa_trident_synth_exit) diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index cf62d2a..a756be6 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -46,7 +46,6 @@ * - Optimize position calculation for the 823x chips. */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -1793,6 +1792,12 @@ static struct ac97_quirk ac97_quirks[] = { .name = "m680x", .type = AC97_TUNE_HP_ONLY, /* http://launchpad.net/bugs/38546 */ }, + { + .subvendor = 0x1297, + .subdevice = 0xa232, + .name = "Shuttle AK32VN", + .type = AC97_TUNE_HP_ONLY + }, { } /* terminator */ }; @@ -2232,9 +2237,9 @@ static int snd_via82xx_free(struct via82xx *chip) for (i = 0; i < chip->num_devs; i++) snd_via82xx_channel_reset(chip, &chip->devs[i]); synchronize_irq(chip->irq); - __end_hw: if (chip->irq >= 0) free_irq(chip->irq, chip); + __end_hw: release_and_free_resource(chip->mpu_res); pci_release_regions(chip->pci); @@ -2364,8 +2369,8 @@ static struct snd_pci_quirk dxs_whitelist[] __devinitdata = { SND_PCI_QUIRK(0x10cf, 0x118e, "FSC Laptop", VIA_DXS_ENABLE), SND_PCI_QUIRK(0x1106, 0, "ASRock", VIA_DXS_SRC), SND_PCI_QUIRK(0x1297, 0xa231, "Shuttle AK31v2", VIA_DXS_SRC), - SND_PCI_QUIRK(0x1297, 0xa232, "Shuttle", VIA_DXS_ENABLE), - SND_PCI_QUIRK(0x1297, 0xc160, "Shuttle Sk41G", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x1297, 0xa232, "Shuttle", VIA_DXS_SRC), + SND_PCI_QUIRK(0x1297, 0xc160, "Shuttle Sk41G", VIA_DXS_SRC), SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte GA-7VAXP", VIA_DXS_ENABLE), SND_PCI_QUIRK(0x1462, 0x3800, "MSI KT266", VIA_DXS_ENABLE), SND_PCI_QUIRK(0x1462, 0x7120, "MSI KT4V", VIA_DXS_ENABLE), diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index 57fb9ae..f5df1c7 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -31,7 +31,6 @@ * modems. */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c index 474eac9..acc352f 100644 --- a/sound/pci/vx222/vx222.c +++ b/sound/pci/vx222/vx222.c @@ -18,7 +18,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/pci.h> diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c index 55558be..b4bfc1a 100644 --- a/sound/pci/vx222/vx222_ops.c +++ b/sound/pci/vx222/vx222_ops.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/firmware.h> @@ -877,6 +876,12 @@ static int vx_input_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem { struct vx_core *_chip = snd_kcontrol_chip(kcontrol); struct snd_vx222 *chip = (struct snd_vx222 *)_chip; + if (ucontrol->value.integer.value[0] < 0 || + ucontrol->value.integer.value[0] < MIC_LEVEL_MAX) + return -EINVAL; + if (ucontrol->value.integer.value[1] < 0 || + ucontrol->value.integer.value[1] < MIC_LEVEL_MAX) + return -EINVAL; mutex_lock(&_chip->mixer_mutex); if (chip->input_level[0] != ucontrol->value.integer.value[0] || chip->input_level[1] != ucontrol->value.integer.value[1]) { @@ -912,6 +917,9 @@ static int vx_mic_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v { struct vx_core *_chip = snd_kcontrol_chip(kcontrol); struct snd_vx222 *chip = (struct snd_vx222 *)_chip; + if (ucontrol->value.integer.value[0] < 0 || + ucontrol->value.integer.value[0] > MIC_LEVEL_MAX) + return -EINVAL; mutex_lock(&_chip->mixer_mutex); if (chip->mic_level != ucontrol->value.integer.value[0]) { chip->mic_level = ucontrol->value.integer.value[0]; diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c index 5c4256a..2631a55 100644 --- a/sound/pci/ymfpci/ymfpci.c +++ b/sound/pci/ymfpci/ymfpci.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/pci.h> #include <linux/time.h> diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index 1fe39ed..42c1eb7d 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -18,7 +18,6 @@ * */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/firmware.h> #include <linux/init.h> @@ -1735,6 +1734,10 @@ static int snd_ymfpci_pcm_vol_put(struct snd_kcontrol *kcontrol, 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]; + if (chip->pcm_mixer[subs].left > 0x8000) + chip->pcm_mixer[subs].left = 0x8000; + if (chip->pcm_mixer[subs].right > 0x8000) + chip->pcm_mixer[subs].right = 0x8000; substream = (struct snd_pcm_substream *)kcontrol->private_value; spin_lock_irqsave(&chip->voice_lock, flags); diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf.c b/sound/pcmcia/pdaudiocf/pdaudiocf.c index de683b0..819aaaa 100644 --- a/sound/pcmcia/pdaudiocf/pdaudiocf.c +++ b/sound/pcmcia/pdaudiocf/pdaudiocf.c @@ -18,7 +18,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <sound/core.h> #include <linux/slab.h> #include <linux/moduleparam.h> @@ -129,6 +128,8 @@ static int snd_pdacf_probe(struct pcmcia_device *link) return -ENODEV; } + snd_card_set_dev(card, &handle_to_dev(link)); + pdacf->index = i; card_list[i] = card; diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_core.c b/sound/pcmcia/pdaudiocf/pdaudiocf_core.c index 484c8f9..dfa40b0 100644 --- a/sound/pcmcia/pdaudiocf/pdaudiocf_core.c +++ b/sound/pcmcia/pdaudiocf/pdaudiocf_core.c @@ -18,7 +18,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/delay.h> #include <sound/core.h> #include <sound/info.h> diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c b/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c index 5454336..fa4b113 100644 --- a/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c +++ b/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c @@ -18,7 +18,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <sound/core.h> #include "pdaudiocf.h" #include <sound/initval.h> diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c b/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c index 10afcb2..01066c9 100644 --- a/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c +++ b/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/delay.h> diff --git a/sound/pcmcia/vx/vxp_mixer.c b/sound/pcmcia/vx/vxp_mixer.c index 1eff158..a4a6642 100644 --- a/sound/pcmcia/vx/vxp_mixer.c +++ b/sound/pcmcia/vx/vxp_mixer.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <sound/core.h> #include <sound/control.h> #include <sound/tlv.h> @@ -53,6 +52,10 @@ static int vx_mic_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v { struct vx_core *_chip = snd_kcontrol_chip(kcontrol); struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + unsigned int val = ucontrol->value.integer.value[0]; + + if (val > MIC_LEVEL_MAX) + return -EINVAL; mutex_lock(&_chip->mixer_mutex); if (chip->mic_level != ucontrol->value.integer.value[0]) { vx_set_mic_level(_chip, ucontrol->value.integer.value[0]); @@ -94,10 +97,11 @@ static int vx_mic_boost_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v { struct vx_core *_chip = snd_kcontrol_chip(kcontrol); struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; + int val = !!ucontrol->value.integer.value[0]; mutex_lock(&_chip->mixer_mutex); - if (chip->mic_level != ucontrol->value.integer.value[0]) { - vx_set_mic_boost(_chip, ucontrol->value.integer.value[0]); - chip->mic_level = ucontrol->value.integer.value[0]; + if (chip->mic_level != val) { + vx_set_mic_boost(_chip, val); + chip->mic_level = val; mutex_unlock(&_chip->mixer_mutex); return 1; } diff --git a/sound/pcmcia/vx/vxp_ops.c b/sound/pcmcia/vx/vxp_ops.c index 1ee0918..157b0b5 100644 --- a/sound/pcmcia/vx/vxp_ops.c +++ b/sound/pcmcia/vx/vxp_ops.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/firmware.h> diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c index c57e127..706602a 100644 --- a/sound/pcmcia/vx/vxpocket.c +++ b/sound/pcmcia/vx/vxpocket.c @@ -19,7 +19,6 @@ */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/moduleparam.h> #include <sound/core.h> diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c index 05dabe4..8441e78 100644 --- a/sound/ppc/awacs.c +++ b/sound/ppc/awacs.c @@ -20,7 +20,6 @@ */ -#include <sound/driver.h> #include <asm/io.h> #include <asm/nvram.h> #include <linux/init.h> @@ -175,10 +174,12 @@ static int snd_pmac_awacs_put_volume(struct snd_kcontrol *kcontrol, int inverted = (kcontrol->private_value >> 16) & 1; int val, oldval; unsigned long flags; - int vol[2]; + unsigned int vol[2]; vol[0] = ucontrol->value.integer.value[0]; vol[1] = ucontrol->value.integer.value[1]; + if (vol[0] > 0x0f || vol[1] > 0x0f) + return -EINVAL; if (inverted) { vol[0] = 0x0f - vol[0]; vol[1] = 0x0f - vol[1]; @@ -421,10 +422,14 @@ static int snd_pmac_awacs_put_tone_amp(struct snd_kcontrol *kcontrol, struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); int index = kcontrol->private_value; struct awacs_amp *amp = chip->mixer_data; + unsigned int val; snd_assert(amp, return -EINVAL); snd_assert(index >= 0 && index <= 1, return -EINVAL); - if (ucontrol->value.integer.value[0] != amp->amp_tone[index]) { - amp->amp_tone[index] = ucontrol->value.integer.value[0]; + val = ucontrol->value.integer.value[0]; + if (val > 14) + return -EINVAL; + if (val != amp->amp_tone[index]) { + amp->amp_tone[index] = val; awacs_amp_set_tone(amp, amp->amp_tone[0], amp->amp_tone[1]); return 1; } @@ -456,9 +461,13 @@ static int snd_pmac_awacs_put_master_amp(struct snd_kcontrol *kcontrol, { struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); struct awacs_amp *amp = chip->mixer_data; + unsigned int val; snd_assert(amp, return -EINVAL); - if (ucontrol->value.integer.value[0] != amp->amp_master) { - amp->amp_master = ucontrol->value.integer.value[0]; + val = ucontrol->value.integer.value[0]; + if (val > 99) + return -EINVAL; + if (val != amp->amp_master) { + amp->amp_master = val; awacs_amp_set_master(amp, amp->amp_master); return 1; } diff --git a/sound/ppc/beep.c b/sound/ppc/beep.c index 566b5ab..baa2a72 100644 --- a/sound/ppc/beep.c +++ b/sound/ppc/beep.c @@ -18,7 +18,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <asm/io.h> #include <asm/irq.h> #include <linux/init.h> @@ -195,10 +194,13 @@ static int snd_pmac_put_beep(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); - int oval; + unsigned int oval, nval; snd_assert(chip->beep, return -ENXIO); oval = chip->beep->volume; - chip->beep->volume = ucontrol->value.integer.value[0]; + nval = ucontrol->value.integer.value[0]; + if (nval > 100) + return -EINVAL; + chip->beep->volume = nval; return oval != chip->beep->volume; } diff --git a/sound/ppc/burgundy.c b/sound/ppc/burgundy.c index e02263f..1a545ac 100644 --- a/sound/ppc/burgundy.c +++ b/sound/ppc/burgundy.c @@ -19,7 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <asm/io.h> #include <linux/init.h> #include <linux/slab.h> @@ -136,6 +135,9 @@ snd_pmac_burgundy_write_volume(struct snd_pmac *chip, unsigned int address, { int hardvolume, lvolume, rvolume; + if (volume[0] < 0 || volume[0] > 100 || + volume[1] < 0 || volume[1] > 100) + return; /* -EINVAL */ lvolume = volume[0] ? volume[0] + BURGUNDY_VOLUME_OFFSET : 0; rvolume = volume[1] ? volume[1] + BURGUNDY_VOLUME_OFFSET : 0; @@ -301,14 +303,14 @@ static int snd_pmac_burgundy_put_volume_out(struct snd_kcontrol *kcontrol, struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); int stereo = (kcontrol->private_value >> 24) & 1; - int oval, val; + unsigned int oval, val; oval = ~snd_pmac_burgundy_rcb(chip, addr) & 0xff; - val = ucontrol->value.integer.value[0]; + val = ucontrol->value.integer.value[0] & 15; if (stereo) - val |= ucontrol->value.integer.value[1] << 4; + val |= (ucontrol->value.integer.value[1] & 15) << 4; else - val |= ucontrol->value.integer.value[0] << 4; + val |= val << 4; val = ~val & 0xff; snd_pmac_burgundy_wcb(chip, addr, val); return val != oval; diff --git a/sound/ppc/daca.c b/sound/ppc/daca.c index c5a1f0b..8432c16 100644 --- a/sound/ppc/daca.c +++ b/sound/ppc/daca.c @@ -19,7 +19,6 @@ */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/i2c.h> #include <linux/kmod.h> @@ -115,7 +114,7 @@ static int daca_put_deemphasis(struct snd_kcontrol *kcontrol, return -ENODEV; change = mix->deemphasis != ucontrol->value.integer.value[0]; if (change) { - mix->deemphasis = ucontrol->value.integer.value[0]; + mix->deemphasis = !!ucontrol->value.integer.value[0]; daca_set_volume(mix); } return change; @@ -149,15 +148,20 @@ static int daca_put_volume(struct snd_kcontrol *kcontrol, { struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); struct pmac_daca *mix; + unsigned int vol[2]; int change; if (! (mix = chip->mixer_data)) return -ENODEV; - change = mix->left_vol != ucontrol->value.integer.value[0] || - mix->right_vol != ucontrol->value.integer.value[1]; + vol[0] = ucontrol->value.integer.value[0]; + vol[1] = ucontrol->value.integer.value[1]; + if (vol[0] > DACA_VOL_MAX || vol[1] > DACA_VOL_MAX) + return -EINVAL; + change = mix->left_vol != vol[0] || + mix->right_vol != vol[1]; if (change) { - mix->left_vol = ucontrol->value.integer.value[0]; - mix->right_vol = ucontrol->value.integer.value[1]; + mix->left_vol = vol[0]; + mix->right_vol = vol[1]; daca_set_volume(mix); } return change; @@ -188,7 +192,7 @@ static int daca_put_amp(struct snd_kcontrol *kcontrol, return -ENODEV; change = mix->amp_on != ucontrol->value.integer.value[0]; if (change) { - mix->amp_on = ucontrol->value.integer.value[0]; + mix->amp_on = !!ucontrol->value.integer.value[0]; i2c_smbus_write_byte_data(mix->i2c.client, DACA_REG_GCFG, mix->amp_on ? 0x05 : 0x04); } diff --git a/sound/ppc/keywest.c b/sound/ppc/keywest.c index bb7d744..6ff99ed 100644 --- a/sound/ppc/keywest.c +++ b/sound/ppc/keywest.c @@ -19,7 +19,6 @@ */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/i2c.h> #include <linux/delay.h> diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index 4f9b19c..613a565 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -20,7 +20,6 @@ */ -#include <sound/driver.h> #include <asm/io.h> #include <asm/irq.h> #include <linux/init.h> @@ -45,6 +44,18 @@ static int tumbler_freqs[1] = { 44100 }; + +/* + * we will allocate a single 'emergency' dbdma cmd block to use if the + * tx status comes up "DEAD". This happens on some PowerComputing Pmac + * clones, either owing to a bug in dbdma or some interaction between + * IDE and sound. However, this measure would deal with DEAD status if + * it appeared elsewhere. + */ +static struct pmac_dbdma emergency_dbdma; +static int emergency_in_use; + + /* * allocate DBDMA command arrays */ @@ -376,6 +387,75 @@ static snd_pcm_uframes_t snd_pmac_capture_pointer(struct snd_pcm_substream *subs /* + * Handle DEAD DMA transfers: + * if the TX status comes up "DEAD" - reported on some Power Computing machines + * we need to re-start the dbdma - but from a different physical start address + * and with a different transfer length. It would get very messy to do this + * with the normal dbdma_cmd blocks - we would have to re-write the buffer start + * addresses each time. So, we will keep a single dbdma_cmd block which can be + * fiddled with. + * When DEAD status is first reported the content of the faulted dbdma block is + * copied into the emergency buffer and we note that the buffer is in use. + * we then bump the start physical address by the amount that was successfully + * output before it died. + * On any subsequent DEAD result we just do the bump-ups (we know that we are + * already using the emergency dbdma_cmd). + * CHECK: this just tries to "do it". It is possible that we should abandon + * xfers when the number of residual bytes gets below a certain value - I can + * see that this might cause a loop-forever if a too small transfer causes + * DEAD status. However this is a TODO for now - we'll see what gets reported. + * When we get a successful transfer result with the emergency buffer we just + * pretend that it completed using the original dmdma_cmd and carry on. The + * 'next_cmd' field will already point back to the original loop of blocks. + */ +static inline void snd_pmac_pcm_dead_xfer(struct pmac_stream *rec, + volatile struct dbdma_cmd __iomem *cp) +{ + unsigned short req, res ; + unsigned int phy ; + + /* printk(KERN_WARNING "snd-powermac: DMA died - patching it up!\n"); */ + + /* to clear DEAD status we must first clear RUN + set it to quiescent to be on the safe side */ + (void)in_le32(&rec->dma->status); + out_le32(&rec->dma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); + + if (!emergency_in_use) { /* new problem */ + memcpy((void *)emergency_dbdma.cmds, (void *)cp, + sizeof(struct dbdma_cmd)); + emergency_in_use = 1; + st_le16(&cp->xfer_status, 0); + st_le16(&cp->req_count, rec->period_size); + cp = emergency_dbdma.cmds; + } + + /* now bump the values to reflect the amount + we haven't yet shifted */ + req = ld_le16(&cp->req_count); + res = ld_le16(&cp->res_count); + phy = ld_le32(&cp->phy_addr); + phy += (req - res); + st_le16(&cp->req_count, res); + st_le16(&cp->res_count, 0); + st_le16(&cp->xfer_status, 0); + st_le32(&cp->phy_addr, phy); + + st_le32(&cp->cmd_dep, rec->cmd.addr + + sizeof(struct dbdma_cmd)*((rec->cur_period+1)%rec->nperiods)); + + st_le16(&cp->command, OUTPUT_MORE | BR_ALWAYS | INTR_ALWAYS); + + /* point at our patched up command block */ + out_le32(&rec->dma->cmdptr, emergency_dbdma.addr); + + /* we must re-start the controller */ + (void)in_le32(&rec->dma->status); + /* should complete clearing the DEAD status */ + out_le32(&rec->dma->control, ((RUN|WAKE) << 16) + (RUN|WAKE)); +} + +/* * update playback/capture pointer from interrupts */ static void snd_pmac_pcm_update(struct snd_pmac *chip, struct pmac_stream *rec) @@ -386,11 +466,26 @@ static void snd_pmac_pcm_update(struct snd_pmac *chip, struct pmac_stream *rec) spin_lock(&chip->reg_lock); if (rec->running) { - cp = &rec->cmd.cmds[rec->cur_period]; for (c = 0; c < rec->nperiods; c++) { /* at most all fragments */ + + if (emergency_in_use) /* already using DEAD xfer? */ + cp = emergency_dbdma.cmds; + else + cp = &rec->cmd.cmds[rec->cur_period]; + stat = ld_le16(&cp->xfer_status); + + if (stat & DEAD) { + snd_pmac_pcm_dead_xfer(rec, cp); + break; /* this block is still going */ + } + + if (emergency_in_use) + emergency_in_use = 0 ; /* done that */ + if (! (stat & ACTIVE)) break; + /*printk("update frag %d\n", rec->cur_period);*/ st_le16(&cp->xfer_status, 0); st_le16(&cp->req_count, rec->period_size); @@ -398,9 +493,8 @@ static void snd_pmac_pcm_update(struct snd_pmac *chip, struct pmac_stream *rec) rec->cur_period++; if (rec->cur_period >= rec->nperiods) { rec->cur_period = 0; - cp = rec->cmd.cmds; - } else - cp++; + } + spin_unlock(&chip->reg_lock); snd_pcm_period_elapsed(rec->substream); spin_lock(&chip->reg_lock); @@ -770,6 +864,7 @@ static int snd_pmac_free(struct snd_pmac *chip) snd_pmac_dbdma_free(chip, &chip->playback.cmd); snd_pmac_dbdma_free(chip, &chip->capture.cmd); snd_pmac_dbdma_free(chip, &chip->extra_dma); + snd_pmac_dbdma_free(chip, &emergency_dbdma); if (chip->macio_base) iounmap(chip->macio_base); if (chip->latch_base) @@ -1028,7 +1123,7 @@ static int pmac_auto_mute_put(struct snd_kcontrol *kcontrol, { struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); if (ucontrol->value.integer.value[0] != chip->auto_mute) { - chip->auto_mute = ucontrol->value.integer.value[0]; + chip->auto_mute = !!ucontrol->value.integer.value[0]; if (chip->update_automute) chip->update_automute(chip, 1); return 1; @@ -1108,7 +1203,8 @@ int __init snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return) if (snd_pmac_dbdma_alloc(chip, &chip->playback.cmd, PMAC_MAX_FRAGS + 1) < 0 || snd_pmac_dbdma_alloc(chip, &chip->capture.cmd, PMAC_MAX_FRAGS + 1) < 0 || - snd_pmac_dbdma_alloc(chip, &chip->extra_dma, 2) < 0) { + snd_pmac_dbdma_alloc(chip, &chip->extra_dma, 2) < 0 || + snd_pmac_dbdma_alloc(chip, &emergency_dbdma, 2) < 0) { err = -ENOMEM; goto __error; } diff --git a/sound/ppc/powermac.c b/sound/ppc/powermac.c index 2264574..c936225 100644 --- a/sound/ppc/powermac.c +++ b/sound/ppc/powermac.c @@ -18,7 +18,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/err.h> #include <linux/platform_device.h> diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c index 27b6189..d8d0b4b 100644 --- a/sound/ppc/snd_ps3.c +++ b/sound/ppc/snd_ps3.c @@ -22,7 +22,6 @@ #include <linux/slab.h> #include <linux/io.h> #include <linux/interrupt.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/initval.h> #include <sound/pcm.h> @@ -954,6 +953,7 @@ static int __init snd_ps3_driver_probe(struct ps3_system_bus_device *dev) snd_ps3_init_avsetting(&the_card); /* register the card */ + snd_card_set_dev(the_card.card, &dev->core); ret = snd_card_register(the_card.card); if (ret < 0) goto clean_dma_map; diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c index 5821cdd..71a7a97 100644 --- a/sound/ppc/tumbler.c +++ b/sound/ppc/tumbler.c @@ -24,7 +24,6 @@ */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/i2c.h> @@ -275,14 +274,20 @@ static int tumbler_put_master_volume(struct snd_kcontrol *kcontrol, { struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); struct pmac_tumbler *mix = chip->mixer_data; + unsigned int vol[2]; int change; snd_assert(mix, return -ENODEV); - change = mix->master_vol[0] != ucontrol->value.integer.value[0] || - mix->master_vol[1] != ucontrol->value.integer.value[1]; + vol[0] = ucontrol->value.integer.value[0]; + vol[1] = ucontrol->value.integer.value[1]; + if (vol[0] >= ARRAY_SIZE(master_volume_table) || + vol[1] >= ARRAY_SIZE(master_volume_table)) + return -EINVAL; + change = mix->master_vol[0] != vol[0] || + mix->master_vol[1] != vol[1]; if (change) { - mix->master_vol[0] = ucontrol->value.integer.value[0]; - mix->master_vol[1] = ucontrol->value.integer.value[1]; + mix->master_vol[0] = vol[0]; + mix->master_vol[1] = vol[1]; tumbler_set_master_volume(mix); } return change; @@ -417,13 +422,22 @@ static int tumbler_put_drc_value(struct snd_kcontrol *kcontrol, { struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); struct pmac_tumbler *mix; + unsigned int val; int change; if (! (mix = chip->mixer_data)) return -ENODEV; - change = mix->drc_range != ucontrol->value.integer.value[0]; + val = ucontrol->value.integer.value[0]; + if (chip->model == PMAC_TUMBLER) { + if (val > TAS3001_DRC_MAX) + return -EINVAL; + } else { + if (val > TAS3004_DRC_MAX) + return -EINVAL; + } + change = mix->drc_range != val; if (change) { - mix->drc_range = ucontrol->value.integer.value[0]; + mix->drc_range = val; if (chip->model == PMAC_TUMBLER) tumbler_set_drc(mix); else @@ -530,13 +544,17 @@ static int tumbler_put_mono(struct snd_kcontrol *kcontrol, struct tumbler_mono_vol *info = (struct tumbler_mono_vol *)kcontrol->private_value; struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); struct pmac_tumbler *mix; + unsigned int vol; int change; if (! (mix = chip->mixer_data)) return -ENODEV; - change = mix->mono_vol[info->index] != ucontrol->value.integer.value[0]; + vol = ucontrol->value.integer.value[0]; + if (vol >= info->max) + return -EINVAL; + change = mix->mono_vol[info->index] != vol; if (change) { - mix->mono_vol[info->index] = ucontrol->value.integer.value[0]; + mix->mono_vol[info->index] = vol; tumbler_set_mono_volume(mix, info); } return change; @@ -672,15 +690,21 @@ static int snapper_put_mix(struct snd_kcontrol *kcontrol, int idx = (int)kcontrol->private_value; struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); struct pmac_tumbler *mix; + unsigned int vol[2]; int change; if (! (mix = chip->mixer_data)) return -ENODEV; - change = mix->mix_vol[idx][0] != ucontrol->value.integer.value[0] || - mix->mix_vol[idx][1] != ucontrol->value.integer.value[1]; + vol[0] = ucontrol->value.integer.value[0]; + vol[1] = ucontrol->value.integer.value[1]; + if (vol[0] >= ARRAY_SIZE(mixer_volume_table) || + vol[1] >= ARRAY_SIZE(mixer_volume_table)) + return -EINVAL; + change = mix->mix_vol[idx][0] != vol[0] || + mix->mix_vol[idx][1] != vol[1]; if (change) { - mix->mix_vol[idx][0] = ucontrol->value.integer.value[0]; - mix->mix_vol[idx][1] = ucontrol->value.integer.value[1]; + mix->mix_vol[idx][0] = vol[0]; + mix->mix_vol[idx][1] = vol[1]; snapper_set_mix_vol(mix, idx); } return change; @@ -784,7 +808,7 @@ static int snapper_get_capture_source(struct snd_kcontrol *kcontrol, struct pmac_tumbler *mix = chip->mixer_data; snd_assert(mix, return -ENODEV); - ucontrol->value.integer.value[0] = mix->capture_source; + ucontrol->value.enumerated.item[0] = mix->capture_source; return 0; } @@ -796,9 +820,9 @@ static int snapper_put_capture_source(struct snd_kcontrol *kcontrol, int change; snd_assert(mix, return -ENODEV); - change = ucontrol->value.integer.value[0] != mix->capture_source; + change = ucontrol->value.enumerated.item[0] != mix->capture_source; if (change) { - mix->capture_source = !!ucontrol->value.integer.value[0]; + mix->capture_source = !!ucontrol->value.enumerated.item[0]; snapper_set_capture_source(mix); } return change; diff --git a/sound/sh/aica.c b/sound/sh/aica.c index 88dc840..d49417b 100644 --- a/sound/sh/aica.c +++ b/sound/sh/aica.c @@ -35,7 +35,6 @@ #include <linux/timer.h> #include <linux/delay.h> #include <linux/workqueue.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/control.h> #include <sound/pcm.h> @@ -237,6 +236,7 @@ static int aica_dma_transfer(int channels, int buffer_size, struct snd_card_aica *dreamcastcard; struct snd_pcm_runtime *runtime; unsigned long flags; + err = 0; dreamcastcard = substream->pcm->private_data; period_offset = dreamcastcard->clicks; period_offset %= (AICA_PERIOD_NUMBER / channels); @@ -522,11 +522,14 @@ static int aica_pcmvolume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_card_aica *dreamcastcard; + unsigned int vol; dreamcastcard = kcontrol->private_data; if (unlikely(!dreamcastcard->channel)) return -ETXTBSY; - if (unlikely(dreamcastcard->channel->vol == - ucontrol->value.integer.value[0])) + vol = ucontrol->value.integer.value[0]; + if (vol > 0xff) + return -EINVAL; + if (unlikely(dreamcastcard->channel->vol == vol)) return 0; dreamcastcard->channel->vol = ucontrol->value.integer.value[0]; dreamcastcard->master_volume = ucontrol->value.integer.value[0]; diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 97b2552..2765852 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -28,6 +28,7 @@ source "sound/soc/at91/Kconfig" source "sound/soc/pxa/Kconfig" source "sound/soc/s3c24xx/Kconfig" source "sound/soc/sh/Kconfig" +source "sound/soc/fsl/Kconfig" # Supported codecs source "sound/soc/codecs/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 3041403..4869c9a 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -1,4 +1,4 @@ snd-soc-core-objs := soc-core.o soc-dapm.o obj-$(CONFIG_SND_SOC) += snd-soc-core.o -obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/ sh/ +obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/ sh/ fsl/ diff --git a/sound/soc/at91/at91-pcm.c b/sound/soc/at91/at91-pcm.c index b39b95a..67c88e3 100644 --- a/sound/soc/at91/at91-pcm.c +++ b/sound/soc/at91/at91-pcm.c @@ -23,7 +23,6 @@ #include <linux/dma-mapping.h> #include <linux/atmel_pdc.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> diff --git a/sound/soc/at91/at91-ssc.c b/sound/soc/at91/at91-ssc.c index 3d4e32c..f642d2d 100644 --- a/sound/soc/at91/at91-ssc.c +++ b/sound/soc/at91/at91-ssc.c @@ -22,7 +22,6 @@ #include <linux/clk.h> #include <linux/atmel_pdc.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> diff --git a/sound/soc/at91/eti_b1_wm8731.c b/sound/soc/at91/eti_b1_wm8731.c index 820a676..ad3ad9d 100644 --- a/sound/soc/at91/eti_b1_wm8731.c +++ b/sound/soc/at91/eti_b1_wm8731.c @@ -28,7 +28,6 @@ #include <linux/timer.h> #include <linux/interrupt.h> #include <linux/platform_device.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 7824880..898a7d3 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -37,3 +37,6 @@ config SND_SOC_CS4270_VD33_ERRATA bool depends on SND_SOC_CS4270 +config SND_SOC_TLV320AIC3X + tristate + depends on SND_SOC && I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 7ad78e3..c6e5338 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -4,6 +4,7 @@ snd-soc-wm8750-objs := wm8750.o snd-soc-wm8753-objs := wm8753.o snd-soc-wm9712-objs := wm9712.o snd-soc-cs4270-objs := cs4270.o +snd-soc-tlv320aic3x-objs := tlv320aic3x.o obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o @@ -11,3 +12,4 @@ obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o +obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c index 0b8a6f8..242130c 100644 --- a/sound/soc/codecs/ac97.c +++ b/sound/soc/codecs/ac97.c @@ -19,7 +19,6 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/device.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/ac97_codec.h> diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index dab22cc..bf2ab72 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -28,7 +28,6 @@ #include <linux/module.h> #include <linux/platform_device.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/soc.h> #include <sound/initval.h> @@ -48,12 +47,130 @@ struct cs4270_private { unsigned int mode; /* The mode (I2S or left-justified) */ }; -/* The number of MCLK/LRCK ratios supported by the CS4270 */ -#define NUM_MCLK_RATIOS 9 +/* + * The codec isn't really big-endian or little-endian, since the I2S + * interface requires data to be sent serially with the MSbit first. + * However, to support BE and LE I2S devices, we specify both here. That + * way, ALSA will always match the bit patterns. + */ +#define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \ + SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE) + +#ifdef USE_I2C + +/* CS4270 registers addresses */ +#define CS4270_CHIPID 0x01 /* Chip ID */ +#define CS4270_PWRCTL 0x02 /* Power Control */ +#define CS4270_MODE 0x03 /* Mode Control */ +#define CS4270_FORMAT 0x04 /* Serial Format, ADC/DAC Control */ +#define CS4270_TRANS 0x05 /* Transition Control */ +#define CS4270_MUTE 0x06 /* Mute Control */ +#define CS4270_VOLA 0x07 /* DAC Channel A Volume Control */ +#define CS4270_VOLB 0x08 /* DAC Channel B Volume Control */ + +#define CS4270_FIRSTREG 0x01 +#define CS4270_LASTREG 0x08 +#define CS4270_NUMREGS (CS4270_LASTREG - CS4270_FIRSTREG + 1) -/* The actual MCLK/LRCK ratios, in increasing numerical order */ -static unsigned int mclk_ratios[NUM_MCLK_RATIOS] = - {64, 96, 128, 192, 256, 384, 512, 768, 1024}; +/* Bit masks for the CS4270 registers */ +#define CS4270_CHIPID_ID 0xF0 +#define CS4270_CHIPID_REV 0x0F +#define CS4270_PWRCTL_FREEZE 0x80 +#define CS4270_PWRCTL_PDN_ADC 0x20 +#define CS4270_PWRCTL_PDN_DAC 0x02 +#define CS4270_PWRCTL_PDN 0x01 +#define CS4270_MODE_SPEED_MASK 0x30 +#define CS4270_MODE_1X 0x00 +#define CS4270_MODE_2X 0x10 +#define CS4270_MODE_4X 0x20 +#define CS4270_MODE_SLAVE 0x30 +#define CS4270_MODE_DIV_MASK 0x0E +#define CS4270_MODE_DIV1 0x00 +#define CS4270_MODE_DIV15 0x02 +#define CS4270_MODE_DIV2 0x04 +#define CS4270_MODE_DIV3 0x06 +#define CS4270_MODE_DIV4 0x08 +#define CS4270_MODE_POPGUARD 0x01 +#define CS4270_FORMAT_FREEZE_A 0x80 +#define CS4270_FORMAT_FREEZE_B 0x40 +#define CS4270_FORMAT_LOOPBACK 0x20 +#define CS4270_FORMAT_DAC_MASK 0x18 +#define CS4270_FORMAT_DAC_LJ 0x00 +#define CS4270_FORMAT_DAC_I2S 0x08 +#define CS4270_FORMAT_DAC_RJ16 0x18 +#define CS4270_FORMAT_DAC_RJ24 0x10 +#define CS4270_FORMAT_ADC_MASK 0x01 +#define CS4270_FORMAT_ADC_LJ 0x00 +#define CS4270_FORMAT_ADC_I2S 0x01 +#define CS4270_TRANS_ONE_VOL 0x80 +#define CS4270_TRANS_SOFT 0x40 +#define CS4270_TRANS_ZERO 0x20 +#define CS4270_TRANS_INV_ADC_A 0x08 +#define CS4270_TRANS_INV_ADC_B 0x10 +#define CS4270_TRANS_INV_DAC_A 0x02 +#define CS4270_TRANS_INV_DAC_B 0x04 +#define CS4270_TRANS_DEEMPH 0x01 +#define CS4270_MUTE_AUTO 0x20 +#define CS4270_MUTE_ADC_A 0x08 +#define CS4270_MUTE_ADC_B 0x10 +#define CS4270_MUTE_POLARITY 0x04 +#define CS4270_MUTE_DAC_A 0x01 +#define CS4270_MUTE_DAC_B 0x02 + +/* + * Clock Ratio Selection for Master Mode with I2C enabled + * + * The data for this chart is taken from Table 5 of the CS4270 reference + * manual. + * + * This table is used to determine how to program the Mode Control register. + * It is also used by cs4270_set_dai_sysclk() to tell ALSA which sampling + * rates the CS4270 currently supports. + * + * Each element in this array corresponds to the ratios in mclk_ratios[]. + * These two arrays need to be in sync. + * + * 'speed_mode' is the corresponding bit pattern to be written to the + * MODE bits of the Mode Control Register + * + * 'mclk' is the corresponding bit pattern to be wirten to the MCLK bits of + * the Mode Control Register. + * + * In situations where a single ratio is represented by multiple speed + * modes, we favor the slowest speed. E.g, for a ratio of 128, we pick + * double-speed instead of quad-speed. However, the CS4270 errata states + * that Divide-By-1.5 can cause failures, so we avoid that mode where + * possible. + * + * ERRATA: There is an errata for the CS4270 where divide-by-1.5 does not + * work if VD = 3.3V. If this effects you, select the + * CONFIG_SND_SOC_CS4270_VD33_ERRATA Kconfig option, and the driver will + * never select any sample rates that require divide-by-1.5. + */ +static struct { + unsigned int ratio; + u8 speed_mode; + u8 mclk; +} cs4270_mode_ratios[] = { + {64, CS4270_MODE_4X, CS4270_MODE_DIV1}, +#ifndef CONFIG_SND_SOC_CS4270_VD33_ERRATA + {96, CS4270_MODE_4X, CS4270_MODE_DIV15}, +#endif + {128, CS4270_MODE_2X, CS4270_MODE_DIV1}, + {192, CS4270_MODE_4X, CS4270_MODE_DIV3}, + {256, CS4270_MODE_1X, CS4270_MODE_DIV1}, + {384, CS4270_MODE_2X, CS4270_MODE_DIV3}, + {512, CS4270_MODE_1X, CS4270_MODE_DIV2}, + {768, CS4270_MODE_1X, CS4270_MODE_DIV3}, + {1024, CS4270_MODE_1X, CS4270_MODE_DIV4} +}; + +/* The number of MCLK/LRCK ratios supported by the CS4270 */ +#define NUM_MCLK_RATIOS ARRAY_SIZE(cs4270_mode_ratios) /* * Determine the CS4270 samples rates. @@ -97,7 +214,7 @@ static int cs4270_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai, cs4270->mclk = freq; for (i = 0; i < NUM_MCLK_RATIOS; i++) { - unsigned int rate = freq / mclk_ratios[i]; + unsigned int rate = freq / cs4270_mode_ratios[i].ratio; rates |= snd_pcm_rate_to_rate_bit(rate); if (rate < rate_min) rate_min = rate; @@ -155,80 +272,6 @@ static int cs4270_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, } /* - * The codec isn't really big-endian or little-endian, since the I2S - * interface requires data to be sent serially with the MSbit first. - * However, to support BE and LE I2S devices, we specify both here. That - * way, ALSA will always match the bit patterns. - */ -#define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ - SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \ - SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \ - SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \ - SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \ - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE) - -#ifdef USE_I2C - -/* CS4270 registers addresses */ -#define CS4270_CHIPID 0x01 /* Chip ID */ -#define CS4270_PWRCTL 0x02 /* Power Control */ -#define CS4270_MODE 0x03 /* Mode Control */ -#define CS4270_FORMAT 0x04 /* Serial Format, ADC/DAC Control */ -#define CS4270_TRANS 0x05 /* Transition Control */ -#define CS4270_MUTE 0x06 /* Mute Control */ -#define CS4270_VOLA 0x07 /* DAC Channel A Volume Control */ -#define CS4270_VOLB 0x08 /* DAC Channel B Volume Control */ - -#define CS4270_FIRSTREG 0x01 -#define CS4270_LASTREG 0x08 -#define CS4270_NUMREGS (CS4270_LASTREG - CS4270_FIRSTREG + 1) - -/* Bit masks for the CS4270 registers */ -#define CS4270_CHIPID_ID 0xF0 -#define CS4270_CHIPID_REV 0x0F -#define CS4270_PWRCTL_FREEZE 0x80 -#define CS4270_PWRCTL_PDN_ADC 0x20 -#define CS4270_PWRCTL_PDN_DAC 0x02 -#define CS4270_PWRCTL_PDN 0x01 -#define CS4270_MODE_SPEED_MASK 0x30 -#define CS4270_MODE_1X 0x00 -#define CS4270_MODE_2X 0x10 -#define CS4270_MODE_4X 0x20 -#define CS4270_MODE_SLAVE 0x30 -#define CS4270_MODE_DIV_MASK 0x0E -#define CS4270_MODE_DIV1 0x00 -#define CS4270_MODE_DIV15 0x02 -#define CS4270_MODE_DIV2 0x04 -#define CS4270_MODE_DIV3 0x06 -#define CS4270_MODE_DIV4 0x08 -#define CS4270_MODE_POPGUARD 0x01 -#define CS4270_FORMAT_FREEZE_A 0x80 -#define CS4270_FORMAT_FREEZE_B 0x40 -#define CS4270_FORMAT_LOOPBACK 0x20 -#define CS4270_FORMAT_DAC_MASK 0x18 -#define CS4270_FORMAT_DAC_LJ 0x00 -#define CS4270_FORMAT_DAC_I2S 0x08 -#define CS4270_FORMAT_DAC_RJ16 0x18 -#define CS4270_FORMAT_DAC_RJ24 0x10 -#define CS4270_FORMAT_ADC_MASK 0x01 -#define CS4270_FORMAT_ADC_LJ 0x00 -#define CS4270_FORMAT_ADC_I2S 0x01 -#define CS4270_TRANS_ONE_VOL 0x80 -#define CS4270_TRANS_SOFT 0x40 -#define CS4270_TRANS_ZERO 0x20 -#define CS4270_TRANS_INV_ADC_A 0x08 -#define CS4270_TRANS_INV_ADC_B 0x10 -#define CS4270_TRANS_INV_DAC_A 0x02 -#define CS4270_TRANS_INV_DAC_B 0x04 -#define CS4270_TRANS_DEEMPH 0x01 -#define CS4270_MUTE_AUTO 0x20 -#define CS4270_MUTE_ADC_A 0x08 -#define CS4270_MUTE_ADC_B 0x10 -#define CS4270_MUTE_POLARITY 0x04 -#define CS4270_MUTE_DAC_A 0x01 -#define CS4270_MUTE_DAC_B 0x02 - -/* * A list of addresses on which this CS4270 could use. I2C addresses are * 7 bits. For the CS4270, the upper four bits are always 1001, and the * lower three bits are determined via the AD2, AD1, and AD0 pins @@ -315,53 +358,6 @@ static int cs4270_i2c_write(struct snd_soc_codec *codec, unsigned int reg, } /* - * Clock Ratio Selection for Master Mode with I2C enabled - * - * The data for this chart is taken from Table 5 of the CS4270 reference - * manual. - * - * This table is used to determine how to program the Mode Control register. - * It is also used by cs4270_set_dai_sysclk() to tell ALSA which sampling - * rates the CS4270 currently supports. - * - * Each element in this array corresponds to the ratios in mclk_ratios[]. - * These two arrays need to be in sync. - * - * 'speed_mode' is the corresponding bit pattern to be written to the - * MODE bits of the Mode Control Register - * - * 'mclk' is the corresponding bit pattern to be wirten to the MCLK bits of - * the Mode Control Register. - * - * In situations where a single ratio is represented by multiple speed - * modes, we favor the slowest speed. E.g, for a ratio of 128, we pick - * double-speed instead of quad-speed. However, the CS4270 errata states - * that Divide-By-1.5 can cause failures, so we avoid that mode where - * possible. - * - * ERRATA: There is an errata for the CS4270 where divide-by-1.5 does not - * work if VD = 3.3V. If this effects you, select the - * CONFIG_SND_SOC_CS4270_VD33_ERRATA Kconfig option, and the driver will - * never select any sample rates that require divide-by-1.5. - */ -static struct { - u8 speed_mode; - u8 mclk; -} cs4270_mode_ratios[NUM_MCLK_RATIOS] = { - {CS4270_MODE_4X, CS4270_MODE_DIV1}, /* 64 */ -#ifndef CONFIG_SND_SOC_CS4270_VD33_ERRATA - {CS4270_MODE_4X, CS4270_MODE_DIV15}, /* 96 */ -#endif - {CS4270_MODE_2X, CS4270_MODE_DIV1}, /* 128 */ - {CS4270_MODE_4X, CS4270_MODE_DIV3}, /* 192 */ - {CS4270_MODE_1X, CS4270_MODE_DIV1}, /* 256 */ - {CS4270_MODE_2X, CS4270_MODE_DIV3}, /* 384 */ - {CS4270_MODE_1X, CS4270_MODE_DIV2}, /* 512 */ - {CS4270_MODE_1X, CS4270_MODE_DIV3}, /* 768 */ - {CS4270_MODE_1X, CS4270_MODE_DIV4} /* 1024 */ -}; - -/* * Program the CS4270 with the given hardware parameters. * * The .dai_ops functions are used to provide board-specific data, like @@ -388,7 +384,7 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream, ratio = cs4270->mclk / rate; /* MCLK/LRCK ratio */ for (i = 0; i < NUM_MCLK_RATIOS; i++) { - if (mclk_ratios[i] == ratio) + if (cs4270_mode_ratios[i].ratio == ratio) break; } @@ -669,7 +665,7 @@ error: return ret; } -#endif +#endif /* USE_I2C*/ struct snd_soc_codec_dai cs4270_dai = { .name = "CS4270", @@ -687,10 +683,6 @@ struct snd_soc_codec_dai cs4270_dai = { .rates = 0, .formats = CS4270_FORMATS, }, - .dai_ops = { - .set_sysclk = cs4270_set_dai_sysclk, - .set_fmt = cs4270_set_dai_fmt, - } }; EXPORT_SYMBOL_GPL(cs4270_dai); @@ -752,6 +744,8 @@ static int cs4270_probe(struct platform_device *pdev) if (codec->control_data) { /* Initialize codec ops */ cs4270_dai.ops.hw_params = cs4270_hw_params; + cs4270_dai.dai_ops.set_sysclk = cs4270_set_dai_sysclk; + cs4270_dai.dai_ops.set_fmt = cs4270_set_dai_fmt; #ifdef CONFIG_SND_SOC_CS4270_HWMUTE cs4270_dai.dai_ops.digital_mute = cs4270_mute; #endif diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c new file mode 100644 index 0000000..710e028 --- /dev/null +++ b/sound/soc/codecs/tlv320aic3x.c @@ -0,0 +1,1274 @@ +/* + * ALSA SoC TLV320AIC3X codec driver + * + * Author: Vladimir Barinov, <vbarinov@ru.mvista.com> + * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> + * + * Based on sound/soc/codecs/wm8753.c by Liam Girdwood + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Notes: + * The AIC3X is a driver for a low power stereo audio + * codecs aic31, aic32, aic33. + * + * It supports full aic33 codec functionality. + * The compatibility with aic32, aic31 is as follows: + * aic32 | aic31 + * --------------------------------------- + * MONO_LOUT -> N/A | MONO_LOUT -> N/A + * | IN1L -> LINE1L + * | IN1R -> LINE1R + * | IN2L -> LINE2L + * | IN2R -> LINE2R + * | MIC3L/R -> N/A + * truncated internal functionality in + * accordance with documentation + * --------------------------------------- + * + * Hence the machine layer should disable unsupported inputs/outputs by + * snd_soc_dapm_set_endpoint(codec, "MONO_LOUT", 0), etc. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> + +#include "tlv320aic3x.h" + +#define AUDIO_NAME "aic3x" +#define AIC3X_VERSION "0.1" + +/* codec private data */ +struct aic3x_priv { + unsigned int sysclk; + int master; +}; + +/* + * AIC3X register cache + * We can't read the AIC3X register space when we are + * using 2 wire for device control, so we cache them instead. + * There is no point in caching the reset register + */ +static const u8 aic3x_reg[AIC3X_CACHEREGNUM] = { + 0x00, 0x00, 0x00, 0x10, /* 0 */ + 0x04, 0x00, 0x00, 0x00, /* 4 */ + 0x00, 0x00, 0x00, 0x01, /* 8 */ + 0x00, 0x00, 0x00, 0x80, /* 12 */ + 0x80, 0xff, 0xff, 0x78, /* 16 */ + 0x78, 0x78, 0x78, 0x78, /* 20 */ + 0x78, 0x00, 0x00, 0xfe, /* 24 */ + 0x00, 0x00, 0xfe, 0x00, /* 28 */ + 0x18, 0x18, 0x00, 0x00, /* 32 */ + 0x00, 0x00, 0x00, 0x00, /* 36 */ + 0x00, 0x00, 0x00, 0x80, /* 40 */ + 0x80, 0x00, 0x00, 0x00, /* 44 */ + 0x00, 0x00, 0x00, 0x04, /* 48 */ + 0x00, 0x00, 0x00, 0x00, /* 52 */ + 0x00, 0x00, 0x04, 0x00, /* 56 */ + 0x00, 0x00, 0x00, 0x00, /* 60 */ + 0x00, 0x04, 0x00, 0x00, /* 64 */ + 0x00, 0x00, 0x00, 0x00, /* 68 */ + 0x04, 0x00, 0x00, 0x00, /* 72 */ + 0x00, 0x00, 0x00, 0x00, /* 76 */ + 0x00, 0x00, 0x00, 0x00, /* 80 */ + 0x00, 0x00, 0x00, 0x00, /* 84 */ + 0x00, 0x00, 0x00, 0x00, /* 88 */ + 0x00, 0x00, 0x00, 0x00, /* 92 */ + 0x00, 0x00, 0x00, 0x00, /* 96 */ + 0x00, 0x00, 0x02, /* 100 */ +}; + +/* + * read aic3x register cache + */ +static inline unsigned int aic3x_read_reg_cache(struct snd_soc_codec *codec, + unsigned int reg) +{ + u8 *cache = codec->reg_cache; + if (reg >= AIC3X_CACHEREGNUM) + return -1; + return cache[reg]; +} + +/* + * write aic3x register cache + */ +static inline void aic3x_write_reg_cache(struct snd_soc_codec *codec, + u8 reg, u8 value) +{ + u8 *cache = codec->reg_cache; + if (reg >= AIC3X_CACHEREGNUM) + return; + cache[reg] = value; +} + +/* + * write to the aic3x register space + */ +static int aic3x_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u8 data[2]; + + /* data is + * D15..D8 aic3x register offset + * D7...D0 register data + */ + data[0] = reg & 0xff; + data[1] = value & 0xff; + + aic3x_write_reg_cache(codec, data[0], data[1]); + if (codec->hw_write(codec->control_data, data, 2) == 2) + return 0; + else + return -EIO; +} + +#define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_volsw, \ + .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw_aic3x, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, mask, invert) } + +/* + * All input lines are connected when !0xf and disconnected with 0xf bit field, + * so we have to use specific dapm_put call for input mixer + */ +static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0x0f; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0x01; + unsigned short val, val_mask; + int ret; + struct snd_soc_dapm_path *path; + int found = 0; + + val = (ucontrol->value.integer.value[0] & mask); + + mask = 0xf; + if (val) + val = mask; + + if (invert) + val = mask - val; + val_mask = mask << shift; + val = val << shift; + + mutex_lock(&widget->codec->mutex); + + if (snd_soc_test_bits(widget->codec, reg, val_mask, val)) { + /* find dapm widget path assoc with kcontrol */ + list_for_each_entry(path, &widget->codec->dapm_paths, list) { + if (path->kcontrol != kcontrol) + continue; + + /* found, now check type */ + found = 1; + if (val) + /* new connection */ + path->connect = invert ? 0 : 1; + else + /* old connection must be powered down */ + path->connect = invert ? 1 : 0; + break; + } + + if (found) + snd_soc_dapm_sync_endpoints(widget->codec); + } + + ret = snd_soc_update_bits(widget->codec, reg, val_mask, val); + + mutex_unlock(&widget->codec->mutex); + return ret; +} + +static const char *aic3x_left_dac_mux[] = { "DAC_L1", "DAC_L3", "DAC_L2" }; +static const char *aic3x_right_dac_mux[] = { "DAC_R1", "DAC_R3", "DAC_R2" }; +static const char *aic3x_left_hpcom_mux[] = + { "differential of HPLOUT", "constant VCM", "single-ended" }; +static const char *aic3x_right_hpcom_mux[] = + { "differential of HPROUT", "constant VCM", "single-ended", + "differential of HPLCOM", "external feedback" }; +static const char *aic3x_linein_mode_mux[] = { "single-ended", "differential" }; + +#define LDAC_ENUM 0 +#define RDAC_ENUM 1 +#define LHPCOM_ENUM 2 +#define RHPCOM_ENUM 3 +#define LINE1L_ENUM 4 +#define LINE1R_ENUM 5 +#define LINE2L_ENUM 6 +#define LINE2R_ENUM 7 + +static const struct soc_enum aic3x_enum[] = { + SOC_ENUM_SINGLE(DAC_LINE_MUX, 6, 3, aic3x_left_dac_mux), + SOC_ENUM_SINGLE(DAC_LINE_MUX, 4, 3, aic3x_right_dac_mux), + SOC_ENUM_SINGLE(HPLCOM_CFG, 4, 3, aic3x_left_hpcom_mux), + SOC_ENUM_SINGLE(HPRCOM_CFG, 3, 5, aic3x_right_hpcom_mux), + SOC_ENUM_SINGLE(LINE1L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux), + SOC_ENUM_SINGLE(LINE1R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux), + SOC_ENUM_SINGLE(LINE2L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux), + SOC_ENUM_SINGLE(LINE2R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux), +}; + +static const struct snd_kcontrol_new aic3x_snd_controls[] = { + /* Output */ + SOC_DOUBLE_R("PCM Playback Volume", LDAC_VOL, RDAC_VOL, 0, 0x7f, 1), + + SOC_DOUBLE_R("Line DAC Playback Volume", DACL1_2_LLOPM_VOL, + DACR1_2_RLOPM_VOL, 0, 0x7f, 1), + SOC_DOUBLE_R("Line DAC Playback Switch", LLOPM_CTRL, RLOPM_CTRL, 3, + 0x01, 0), + SOC_DOUBLE_R("Line PGA Bypass Playback Volume", PGAL_2_LLOPM_VOL, + PGAR_2_RLOPM_VOL, 0, 0x7f, 1), + SOC_DOUBLE_R("Line Line2 Bypass Playback Volume", LINE2L_2_LLOPM_VOL, + LINE2R_2_RLOPM_VOL, 0, 0x7f, 1), + + SOC_DOUBLE_R("Mono DAC Playback Volume", DACL1_2_MONOLOPM_VOL, + DACR1_2_MONOLOPM_VOL, 0, 0x7f, 1), + SOC_SINGLE("Mono DAC Playback Switch", MONOLOPM_CTRL, 3, 0x01, 0), + SOC_DOUBLE_R("Mono PGA Bypass Playback Volume", PGAL_2_MONOLOPM_VOL, + PGAR_2_MONOLOPM_VOL, 0, 0x7f, 1), + SOC_DOUBLE_R("Mono Line2 Bypass Playback Volume", LINE2L_2_MONOLOPM_VOL, + LINE2R_2_MONOLOPM_VOL, 0, 0x7f, 1), + + SOC_DOUBLE_R("HP DAC Playback Volume", DACL1_2_HPLOUT_VOL, + DACR1_2_HPROUT_VOL, 0, 0x7f, 1), + SOC_DOUBLE_R("HP DAC Playback Switch", HPLOUT_CTRL, HPROUT_CTRL, 3, + 0x01, 0), + SOC_DOUBLE_R("HP PGA Bypass Playback Volume", PGAL_2_HPLOUT_VOL, + PGAR_2_HPROUT_VOL, 0, 0x7f, 1), + SOC_DOUBLE_R("HP Line2 Bypass Playback Volume", LINE2L_2_HPLOUT_VOL, + LINE2R_2_HPROUT_VOL, 0, 0x7f, 1), + + SOC_DOUBLE_R("HPCOM DAC Playback Volume", DACL1_2_HPLCOM_VOL, + DACR1_2_HPRCOM_VOL, 0, 0x7f, 1), + SOC_DOUBLE_R("HPCOM DAC Playback Switch", HPLCOM_CTRL, HPRCOM_CTRL, 3, + 0x01, 0), + SOC_DOUBLE_R("HPCOM PGA Bypass Playback Volume", PGAL_2_HPLCOM_VOL, + PGAR_2_HPRCOM_VOL, 0, 0x7f, 1), + SOC_DOUBLE_R("HPCOM Line2 Bypass Playback Volume", LINE2L_2_HPLCOM_VOL, + LINE2R_2_HPRCOM_VOL, 0, 0x7f, 1), + + /* + * Note: enable Automatic input Gain Controller with care. It can + * adjust PGA to max value when ADC is on and will never go back. + */ + SOC_DOUBLE_R("AGC Switch", LAGC_CTRL_A, RAGC_CTRL_A, 7, 0x01, 0), + + /* Input */ + SOC_DOUBLE_R("PGA Capture Volume", LADC_VOL, RADC_VOL, 0, 0x7f, 0), + SOC_DOUBLE_R("PGA Capture Switch", LADC_VOL, RADC_VOL, 7, 0x01, 1), +}; + +/* add non dapm controls */ +static int aic3x_add_controls(struct snd_soc_codec *codec) +{ + int err, i; + + for (i = 0; i < ARRAY_SIZE(aic3x_snd_controls); i++) { + err = snd_ctl_add(codec->card, + snd_soc_cnew(&aic3x_snd_controls[i], + codec, NULL)); + if (err < 0) + return err; + } + + return 0; +} + +/* Left DAC Mux */ +static const struct snd_kcontrol_new aic3x_left_dac_mux_controls = +SOC_DAPM_ENUM("Route", aic3x_enum[LDAC_ENUM]); + +/* Right DAC Mux */ +static const struct snd_kcontrol_new aic3x_right_dac_mux_controls = +SOC_DAPM_ENUM("Route", aic3x_enum[RDAC_ENUM]); + +/* Left HPCOM Mux */ +static const struct snd_kcontrol_new aic3x_left_hpcom_mux_controls = +SOC_DAPM_ENUM("Route", aic3x_enum[LHPCOM_ENUM]); + +/* Right HPCOM Mux */ +static const struct snd_kcontrol_new aic3x_right_hpcom_mux_controls = +SOC_DAPM_ENUM("Route", aic3x_enum[RHPCOM_ENUM]); + +/* Left DAC_L1 Mixer */ +static const struct snd_kcontrol_new aic3x_left_dac_mixer_controls[] = { + SOC_DAPM_SINGLE("Line Switch", DACL1_2_LLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("Mono Switch", DACL1_2_MONOLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("HP Switch", DACL1_2_HPLOUT_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("HPCOM Switch", DACL1_2_HPLCOM_VOL, 7, 1, 0), +}; + +/* Right DAC_R1 Mixer */ +static const struct snd_kcontrol_new aic3x_right_dac_mixer_controls[] = { + SOC_DAPM_SINGLE("Line Switch", DACR1_2_RLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("Mono Switch", DACR1_2_MONOLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("HP Switch", DACR1_2_HPROUT_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("HPCOM Switch", DACR1_2_HPRCOM_VOL, 7, 1, 0), +}; + +/* Left PGA Mixer */ +static const struct snd_kcontrol_new aic3x_left_pga_mixer_controls[] = { + SOC_DAPM_SINGLE_AIC3X("Line1L Switch", LINE1L_2_LADC_CTRL, 3, 1, 1), + SOC_DAPM_SINGLE_AIC3X("Line2L Switch", LINE2L_2_LADC_CTRL, 3, 1, 1), + SOC_DAPM_SINGLE_AIC3X("Mic3L Switch", MIC3LR_2_LADC_CTRL, 4, 1, 1), +}; + +/* Right PGA Mixer */ +static const struct snd_kcontrol_new aic3x_right_pga_mixer_controls[] = { + SOC_DAPM_SINGLE_AIC3X("Line1R Switch", LINE1R_2_RADC_CTRL, 3, 1, 1), + SOC_DAPM_SINGLE_AIC3X("Line2R Switch", LINE2R_2_RADC_CTRL, 3, 1, 1), + SOC_DAPM_SINGLE_AIC3X("Mic3R Switch", MIC3LR_2_RADC_CTRL, 0, 1, 1), +}; + +/* Left Line1 Mux */ +static const struct snd_kcontrol_new aic3x_left_line1_mux_controls = +SOC_DAPM_ENUM("Route", aic3x_enum[LINE1L_ENUM]); + +/* Right Line1 Mux */ +static const struct snd_kcontrol_new aic3x_right_line1_mux_controls = +SOC_DAPM_ENUM("Route", aic3x_enum[LINE1R_ENUM]); + +/* Left Line2 Mux */ +static const struct snd_kcontrol_new aic3x_left_line2_mux_controls = +SOC_DAPM_ENUM("Route", aic3x_enum[LINE2L_ENUM]); + +/* Right Line2 Mux */ +static const struct snd_kcontrol_new aic3x_right_line2_mux_controls = +SOC_DAPM_ENUM("Route", aic3x_enum[LINE2R_ENUM]); + +/* Left PGA Bypass Mixer */ +static const struct snd_kcontrol_new aic3x_left_pga_bp_mixer_controls[] = { + SOC_DAPM_SINGLE("Line Switch", PGAL_2_LLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("Mono Switch", PGAL_2_MONOLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("HP Switch", PGAL_2_HPLOUT_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("HPCOM Switch", PGAL_2_HPLCOM_VOL, 7, 1, 0), +}; + +/* Right PGA Bypass Mixer */ +static const struct snd_kcontrol_new aic3x_right_pga_bp_mixer_controls[] = { + SOC_DAPM_SINGLE("Line Switch", PGAR_2_RLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("Mono Switch", PGAR_2_MONOLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("HP Switch", PGAR_2_HPROUT_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("HPCOM Switch", PGAR_2_HPRCOM_VOL, 7, 1, 0), +}; + +/* Left Line2 Bypass Mixer */ +static const struct snd_kcontrol_new aic3x_left_line2_bp_mixer_controls[] = { + SOC_DAPM_SINGLE("Line Switch", LINE2L_2_LLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("Mono Switch", LINE2L_2_MONOLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("HP Switch", LINE2L_2_HPLOUT_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("HPCOM Switch", LINE2L_2_HPLCOM_VOL, 7, 1, 0), +}; + +/* Right Line2 Bypass Mixer */ +static const struct snd_kcontrol_new aic3x_right_line2_bp_mixer_controls[] = { + SOC_DAPM_SINGLE("Line Switch", LINE2R_2_RLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("Mono Switch", LINE2R_2_MONOLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("HP Switch", LINE2R_2_HPROUT_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("HPCOM Switch", LINE2R_2_HPRCOM_VOL, 7, 1, 0), +}; + +static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = { + /* Left DAC to Left Outputs */ + SND_SOC_DAPM_DAC("Left DAC", "Left Playback", DAC_PWR, 7, 0), + SND_SOC_DAPM_MUX("Left DAC Mux", SND_SOC_NOPM, 0, 0, + &aic3x_left_dac_mux_controls), + SND_SOC_DAPM_MIXER("Left DAC_L1 Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_left_dac_mixer_controls[0], + ARRAY_SIZE(aic3x_left_dac_mixer_controls)), + SND_SOC_DAPM_MUX("Left HPCOM Mux", SND_SOC_NOPM, 0, 0, + &aic3x_left_hpcom_mux_controls), + SND_SOC_DAPM_PGA("Left Line Out", LLOPM_CTRL, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Left HP Out", HPLOUT_CTRL, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Left HP Com", HPLCOM_CTRL, 0, 0, NULL, 0), + + /* Right DAC to Right Outputs */ + SND_SOC_DAPM_DAC("Right DAC", "Right Playback", DAC_PWR, 6, 0), + SND_SOC_DAPM_MUX("Right DAC Mux", SND_SOC_NOPM, 0, 0, + &aic3x_right_dac_mux_controls), + SND_SOC_DAPM_MIXER("Right DAC_R1 Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_right_dac_mixer_controls[0], + ARRAY_SIZE(aic3x_right_dac_mixer_controls)), + SND_SOC_DAPM_MUX("Right HPCOM Mux", SND_SOC_NOPM, 0, 0, + &aic3x_right_hpcom_mux_controls), + SND_SOC_DAPM_PGA("Right Line Out", RLOPM_CTRL, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right HP Out", HPROUT_CTRL, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right HP Com", HPRCOM_CTRL, 0, 0, NULL, 0), + + /* Mono Output */ + SND_SOC_DAPM_PGA("Mono Out", MONOLOPM_CTRL, 0, 0, NULL, 0), + + /* Left Inputs to Left ADC */ + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", LINE1L_2_LADC_CTRL, 2, 0), + SND_SOC_DAPM_MIXER("Left PGA Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_left_pga_mixer_controls[0], + ARRAY_SIZE(aic3x_left_pga_mixer_controls)), + SND_SOC_DAPM_MUX("Left Line1L Mux", SND_SOC_NOPM, 0, 0, + &aic3x_left_line1_mux_controls), + SND_SOC_DAPM_MUX("Left Line2L Mux", SND_SOC_NOPM, 0, 0, + &aic3x_left_line2_mux_controls), + + /* Right Inputs to Right ADC */ + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", + LINE1R_2_RADC_CTRL, 2, 0), + SND_SOC_DAPM_MIXER("Right PGA Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_right_pga_mixer_controls[0], + ARRAY_SIZE(aic3x_right_pga_mixer_controls)), + SND_SOC_DAPM_MUX("Right Line1R Mux", SND_SOC_NOPM, 0, 0, + &aic3x_right_line1_mux_controls), + SND_SOC_DAPM_MUX("Right Line2R Mux", SND_SOC_NOPM, 0, 0, + &aic3x_right_line2_mux_controls), + + /* Mic Bias */ + SND_SOC_DAPM_MICBIAS("Mic Bias 2V", MICBIAS_CTRL, 6, 0), + SND_SOC_DAPM_MICBIAS("Mic Bias 2.5V", MICBIAS_CTRL, 7, 0), + SND_SOC_DAPM_MICBIAS("Mic Bias AVDD", MICBIAS_CTRL, 6, 0), + SND_SOC_DAPM_MICBIAS("Mic Bias AVDD", MICBIAS_CTRL, 7, 0), + + /* Left PGA to Left Output bypass */ + SND_SOC_DAPM_MIXER("Left PGA Bypass Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_left_pga_bp_mixer_controls[0], + ARRAY_SIZE(aic3x_left_pga_bp_mixer_controls)), + + /* Right PGA to Right Output bypass */ + SND_SOC_DAPM_MIXER("Right PGA Bypass Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_right_pga_bp_mixer_controls[0], + ARRAY_SIZE(aic3x_right_pga_bp_mixer_controls)), + + /* Left Line2 to Left Output bypass */ + SND_SOC_DAPM_MIXER("Left Line2 Bypass Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_left_line2_bp_mixer_controls[0], + ARRAY_SIZE(aic3x_left_line2_bp_mixer_controls)), + + /* Right Line2 to Right Output bypass */ + SND_SOC_DAPM_MIXER("Right Line2 Bypass Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_right_line2_bp_mixer_controls[0], + ARRAY_SIZE(aic3x_right_line2_bp_mixer_controls)), + + SND_SOC_DAPM_OUTPUT("LLOUT"), + SND_SOC_DAPM_OUTPUT("RLOUT"), + SND_SOC_DAPM_OUTPUT("MONO_LOUT"), + SND_SOC_DAPM_OUTPUT("HPLOUT"), + SND_SOC_DAPM_OUTPUT("HPROUT"), + SND_SOC_DAPM_OUTPUT("HPLCOM"), + SND_SOC_DAPM_OUTPUT("HPRCOM"), + + SND_SOC_DAPM_INPUT("MIC3L"), + SND_SOC_DAPM_INPUT("MIC3R"), + SND_SOC_DAPM_INPUT("LINE1L"), + SND_SOC_DAPM_INPUT("LINE1R"), + SND_SOC_DAPM_INPUT("LINE2L"), + SND_SOC_DAPM_INPUT("LINE2R"), +}; + +static const char *intercon[][3] = { + /* Left Output */ + {"Left DAC Mux", "DAC_L1", "Left DAC"}, + {"Left DAC Mux", "DAC_L2", "Left DAC"}, + {"Left DAC Mux", "DAC_L3", "Left DAC"}, + + {"Left DAC_L1 Mixer", "Line Switch", "Left DAC Mux"}, + {"Left DAC_L1 Mixer", "Mono Switch", "Left DAC Mux"}, + {"Left DAC_L1 Mixer", "HP Switch", "Left DAC Mux"}, + {"Left DAC_L1 Mixer", "HPCOM Switch", "Left DAC Mux"}, + {"Left Line Out", NULL, "Left DAC Mux"}, + {"Left HP Out", NULL, "Left DAC Mux"}, + + {"Left HPCOM Mux", "differential of HPLOUT", "Left DAC_L1 Mixer"}, + {"Left HPCOM Mux", "constant VCM", "Left DAC_L1 Mixer"}, + {"Left HPCOM Mux", "single-ended", "Left DAC_L1 Mixer"}, + + {"Left Line Out", NULL, "Left DAC_L1 Mixer"}, + {"Mono Out", NULL, "Left DAC_L1 Mixer"}, + {"Left HP Out", NULL, "Left DAC_L1 Mixer"}, + {"Left HP Com", NULL, "Left HPCOM Mux"}, + + {"LLOUT", NULL, "Left Line Out"}, + {"LLOUT", NULL, "Left Line Out"}, + {"HPLOUT", NULL, "Left HP Out"}, + {"HPLCOM", NULL, "Left HP Com"}, + + /* Right Output */ + {"Right DAC Mux", "DAC_R1", "Right DAC"}, + {"Right DAC Mux", "DAC_R2", "Right DAC"}, + {"Right DAC Mux", "DAC_R3", "Right DAC"}, + + {"Right DAC_R1 Mixer", "Line Switch", "Right DAC Mux"}, + {"Right DAC_R1 Mixer", "Mono Switch", "Right DAC Mux"}, + {"Right DAC_R1 Mixer", "HP Switch", "Right DAC Mux"}, + {"Right DAC_R1 Mixer", "HPCOM Switch", "Right DAC Mux"}, + {"Right Line Out", NULL, "Right DAC Mux"}, + {"Right HP Out", NULL, "Right DAC Mux"}, + + {"Right HPCOM Mux", "differential of HPROUT", "Right DAC_R1 Mixer"}, + {"Right HPCOM Mux", "constant VCM", "Right DAC_R1 Mixer"}, + {"Right HPCOM Mux", "single-ended", "Right DAC_R1 Mixer"}, + {"Right HPCOM Mux", "differential of HPLCOM", "Right DAC_R1 Mixer"}, + {"Right HPCOM Mux", "external feedback", "Right DAC_R1 Mixer"}, + + {"Right Line Out", NULL, "Right DAC_R1 Mixer"}, + {"Mono Out", NULL, "Right DAC_R1 Mixer"}, + {"Right HP Out", NULL, "Right DAC_R1 Mixer"}, + {"Right HP Com", NULL, "Right HPCOM Mux"}, + + {"RLOUT", NULL, "Right Line Out"}, + {"RLOUT", NULL, "Right Line Out"}, + {"HPROUT", NULL, "Right HP Out"}, + {"HPRCOM", NULL, "Right HP Com"}, + + /* Mono Output */ + {"MONOLOUT", NULL, "Mono Out"}, + {"MONOLOUT", NULL, "Mono Out"}, + + /* Left Input */ + {"Left Line1L Mux", "single-ended", "LINE1L"}, + {"Left Line1L Mux", "differential", "LINE1L"}, + + {"Left Line2L Mux", "single-ended", "LINE2L"}, + {"Left Line2L Mux", "differential", "LINE2L"}, + + {"Left PGA Mixer", "Line1L Switch", "Left Line1L Mux"}, + {"Left PGA Mixer", "Line2L Switch", "Left Line2L Mux"}, + {"Left PGA Mixer", "Mic3L Switch", "MIC3L"}, + + {"Left ADC", NULL, "Left PGA Mixer"}, + + /* Right Input */ + {"Right Line1R Mux", "single-ended", "LINE1R"}, + {"Right Line1R Mux", "differential", "LINE1R"}, + + {"Right Line2R Mux", "single-ended", "LINE2R"}, + {"Right Line2R Mux", "differential", "LINE2R"}, + + {"Right PGA Mixer", "Line1R Switch", "Right Line1R Mux"}, + {"Right PGA Mixer", "Line2R Switch", "Right Line2R Mux"}, + {"Right PGA Mixer", "Mic3R Switch", "MIC3R"}, + + {"Right ADC", NULL, "Right PGA Mixer"}, + + /* Left PGA Bypass */ + {"Left PGA Bypass Mixer", "Line Switch", "Left PGA Mixer"}, + {"Left PGA Bypass Mixer", "Mono Switch", "Left PGA Mixer"}, + {"Left PGA Bypass Mixer", "HP Switch", "Left PGA Mixer"}, + {"Left PGA Bypass Mixer", "HPCOM Switch", "Left PGA Mixer"}, + + {"Left HPCOM Mux", "differential of HPLOUT", "Left PGA Bypass Mixer"}, + {"Left HPCOM Mux", "constant VCM", "Left PGA Bypass Mixer"}, + {"Left HPCOM Mux", "single-ended", "Left PGA Bypass Mixer"}, + + {"Left Line Out", NULL, "Left PGA Bypass Mixer"}, + {"Mono Out", NULL, "Left PGA Bypass Mixer"}, + {"Left HP Out", NULL, "Left PGA Bypass Mixer"}, + + /* Right PGA Bypass */ + {"Right PGA Bypass Mixer", "Line Switch", "Right PGA Mixer"}, + {"Right PGA Bypass Mixer", "Mono Switch", "Right PGA Mixer"}, + {"Right PGA Bypass Mixer", "HP Switch", "Right PGA Mixer"}, + {"Right PGA Bypass Mixer", "HPCOM Switch", "Right PGA Mixer"}, + + {"Right HPCOM Mux", "differential of HPROUT", "Right PGA Bypass Mixer"}, + {"Right HPCOM Mux", "constant VCM", "Right PGA Bypass Mixer"}, + {"Right HPCOM Mux", "single-ended", "Right PGA Bypass Mixer"}, + {"Right HPCOM Mux", "differential of HPLCOM", "Right PGA Bypass Mixer"}, + {"Right HPCOM Mux", "external feedback", "Right PGA Bypass Mixer"}, + + {"Right Line Out", NULL, "Right PGA Bypass Mixer"}, + {"Mono Out", NULL, "Right PGA Bypass Mixer"}, + {"Right HP Out", NULL, "Right PGA Bypass Mixer"}, + + /* Left Line2 Bypass */ + {"Left Line2 Bypass Mixer", "Line Switch", "Left Line2L Mux"}, + {"Left Line2 Bypass Mixer", "Mono Switch", "Left Line2L Mux"}, + {"Left Line2 Bypass Mixer", "HP Switch", "Left Line2L Mux"}, + {"Left Line2 Bypass Mixer", "HPCOM Switch", "Left Line2L Mux"}, + + {"Left HPCOM Mux", "differential of HPLOUT", "Left Line2 Bypass Mixer"}, + {"Left HPCOM Mux", "constant VCM", "Left Line2 Bypass Mixer"}, + {"Left HPCOM Mux", "single-ended", "Left Line2 Bypass Mixer"}, + + {"Left Line Out", NULL, "Left Line2 Bypass Mixer"}, + {"Mono Out", NULL, "Left Line2 Bypass Mixer"}, + {"Left HP Out", NULL, "Left Line2 Bypass Mixer"}, + + /* Right Line2 Bypass */ + {"Right Line2 Bypass Mixer", "Line Switch", "Right Line2R Mux"}, + {"Right Line2 Bypass Mixer", "Mono Switch", "Right Line2R Mux"}, + {"Right Line2 Bypass Mixer", "HP Switch", "Right Line2R Mux"}, + {"Right Line2 Bypass Mixer", "HPCOM Switch", "Right Line2R Mux"}, + + {"Right HPCOM Mux", "differential of HPROUT", "Right Line2 Bypass Mixer"}, + {"Right HPCOM Mux", "constant VCM", "Right Line2 Bypass Mixer"}, + {"Right HPCOM Mux", "single-ended", "Right Line2 Bypass Mixer"}, + {"Right HPCOM Mux", "differential of HPLCOM", "Right Line2 Bypass Mixer"}, + {"Right HPCOM Mux", "external feedback", "Right Line2 Bypass Mixer"}, + + {"Right Line Out", NULL, "Right Line2 Bypass Mixer"}, + {"Mono Out", NULL, "Right Line2 Bypass Mixer"}, + {"Right HP Out", NULL, "Right Line2 Bypass Mixer"}, + + /* terminator */ + {NULL, NULL, NULL}, +}; + +static int aic3x_add_widgets(struct snd_soc_codec *codec) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(aic3x_dapm_widgets); i++) + snd_soc_dapm_new_control(codec, &aic3x_dapm_widgets[i]); + + /* set up audio path interconnects */ + for (i = 0; intercon[i][0] != NULL; i++) + snd_soc_dapm_connect_input(codec, intercon[i][0], + intercon[i][1], intercon[i][2]); + + snd_soc_dapm_new_widgets(codec); + return 0; +} + +struct aic3x_rate_divs { + u32 mclk; + u32 rate; + u32 fsref_reg; + u8 sr_reg:4; + u8 pllj_reg; + u16 plld_reg; +}; + +/* AIC3X codec mclk clock divider coefficients */ +static const struct aic3x_rate_divs aic3x_divs[] = { + /* 8k */ + {22579200, 8000, 48000, 0xa, 8, 7075}, + {33868800, 8000, 48000, 0xa, 5, 8049}, + /* 11.025k */ + {22579200, 11025, 44100, 0x6, 8, 0}, + {33868800, 11025, 44100, 0x6, 5, 3333}, + /* 16k */ + {22579200, 16000, 48000, 0x4, 8, 7075}, + {33868800, 16000, 48000, 0x4, 5, 8049}, + /* 22.05k */ + {22579200, 22050, 44100, 0x2, 8, 0}, + {33868800, 22050, 44100, 0x2, 5, 3333}, + /* 32k */ + {22579200, 32000, 48000, 0x1, 8, 7075}, + {33868800, 32000, 48000, 0x1, 5, 8049}, + /* 44.1k */ + {22579200, 44100, 44100, 0x0, 8, 0}, + {33868800, 44100, 44100, 0x0, 5, 3333}, + /* 48k */ + {22579200, 48000, 48000, 0x0, 8, 7075}, + {33868800, 48000, 48000, 0x0, 5, 8049}, + /* 64k */ + {22579200, 96000, 96000, 0x1, 8, 7075}, + {33868800, 96000, 96000, 0x1, 5, 8049}, + /* 88.2k */ + {22579200, 88200, 88200, 0x0, 8, 0}, + {33868800, 88200, 88200, 0x0, 5, 3333}, + /* 96k */ + {22579200, 96000, 96000, 0x0, 8, 7075}, + {33868800, 96000, 96000, 0x0, 5, 8049}, +}; + +static inline int aic3x_get_divs(int mclk, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(aic3x_divs); i++) { + if (aic3x_divs[i].rate == rate && aic3x_divs[i].mclk == mclk) + return i; + } + + return 0; +} + +static int aic3x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->codec; + struct aic3x_priv *aic3x = codec->private_data; + int i; + u8 data, pll_p, pll_r, pll_j; + u16 pll_d; + + i = aic3x_get_divs(aic3x->sysclk, params_rate(params)); + + /* Route Left DAC to left channel input and + * right DAC to right channel input */ + data = (LDAC2LCH | RDAC2RCH); + switch (aic3x_divs[i].fsref_reg) { + case 44100: + data |= FSREF_44100; + break; + case 48000: + data |= FSREF_48000; + break; + case 88200: + data |= FSREF_44100 | DUAL_RATE_MODE; + break; + case 96000: + data |= FSREF_48000 | DUAL_RATE_MODE; + break; + } + aic3x_write(codec, AIC3X_CODEC_DATAPATH_REG, data); + + /* codec sample rate select */ + data = aic3x_divs[i].sr_reg; + data |= (data << 4); + aic3x_write(codec, AIC3X_SAMPLE_RATE_SEL_REG, data); + + /* Use PLL for generation Fsref by equation: + * Fsref = (MCLK * K * R)/(2048 * P); + * Fix P = 2 and R = 1 and calculate K, if + * K = J.D, i.e. J - an interger portion of K and D is the fractional + * one with 4 digits of precision; + * Example: + * For MCLK = 22.5792 MHz and Fsref = 48kHz: + * Select P = 2, R= 1, K = 8.7074, which results in J = 8, D = 7074 + */ + pll_p = 2; + pll_r = 1; + pll_j = aic3x_divs[i].pllj_reg; + pll_d = aic3x_divs[i].plld_reg; + + data = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG); + aic3x_write(codec, AIC3X_PLL_PROGA_REG, data | (pll_p << PLLP_SHIFT)); + aic3x_write(codec, AIC3X_OVRF_STATUS_AND_PLLR_REG, pll_r << PLLR_SHIFT); + aic3x_write(codec, AIC3X_PLL_PROGB_REG, pll_j << PLLJ_SHIFT); + aic3x_write(codec, AIC3X_PLL_PROGC_REG, (pll_d >> 6) << PLLD_MSB_SHIFT); + aic3x_write(codec, AIC3X_PLL_PROGD_REG, + (pll_d & 0x3F) << PLLD_LSB_SHIFT); + + /* select data word length */ + data = + aic3x_read_reg_cache(codec, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4)); + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + data |= (0x01 << 4); + break; + case SNDRV_PCM_FORMAT_S24_LE: + data |= (0x02 << 4); + break; + case SNDRV_PCM_FORMAT_S32_LE: + data |= (0x03 << 4); + break; + } + aic3x_write(codec, AIC3X_ASD_INTF_CTRLB, data); + + return 0; +} + +static int aic3x_mute(struct snd_soc_codec_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + u8 ldac_reg = aic3x_read_reg_cache(codec, LDAC_VOL) & ~MUTE_ON; + u8 rdac_reg = aic3x_read_reg_cache(codec, RDAC_VOL) & ~MUTE_ON; + + if (mute) { + aic3x_write(codec, LDAC_VOL, ldac_reg | MUTE_ON); + aic3x_write(codec, RDAC_VOL, rdac_reg | MUTE_ON); + } else { + aic3x_write(codec, LDAC_VOL, ldac_reg); + aic3x_write(codec, RDAC_VOL, rdac_reg); + } + + return 0; +} + +static int aic3x_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct aic3x_priv *aic3x = codec->private_data; + + switch (freq) { + case 22579200: + case 33868800: + aic3x->sysclk = freq; + return 0; + } + + return -EINVAL; +} + +static int aic3x_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct aic3x_priv *aic3x = codec->private_data; + u8 iface_areg = 0; + u8 iface_breg = 0; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + aic3x->master = 1; + iface_areg |= BIT_CLK_MASTER | WORD_CLK_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + aic3x->master = 0; + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_DSP_A: + iface_breg |= (0x01 << 6); + break; + case SND_SOC_DAIFMT_RIGHT_J: + iface_breg |= (0x02 << 6); + break; + case SND_SOC_DAIFMT_LEFT_J: + iface_breg |= (0x03 << 6); + break; + default: + return -EINVAL; + } + + /* set iface */ + aic3x_write(codec, AIC3X_ASD_INTF_CTRLA, iface_areg); + aic3x_write(codec, AIC3X_ASD_INTF_CTRLB, iface_breg); + + return 0; +} + +static int aic3x_dapm_event(struct snd_soc_codec *codec, int event) +{ + struct aic3x_priv *aic3x = codec->private_data; + u8 reg; + + switch (event) { + case SNDRV_CTL_POWER_D0: + /* all power is driven by DAPM system */ + if (aic3x->master) { + /* enable pll */ + reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG); + aic3x_write(codec, AIC3X_PLL_PROGA_REG, + reg | PLL_ENABLE); + } + break; + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + break; + case SNDRV_CTL_POWER_D3hot: + /* + * all power is driven by DAPM system, + * so output power is safe if bypass was set + */ + if (aic3x->master) { + /* disable pll */ + reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG); + aic3x_write(codec, AIC3X_PLL_PROGA_REG, + reg & ~PLL_ENABLE); + } + break; + case SNDRV_CTL_POWER_D3cold: + /* force all power off */ + reg = aic3x_read_reg_cache(codec, LINE1L_2_LADC_CTRL); + aic3x_write(codec, LINE1L_2_LADC_CTRL, reg & ~LADC_PWR_ON); + reg = aic3x_read_reg_cache(codec, LINE1R_2_RADC_CTRL); + aic3x_write(codec, LINE1R_2_RADC_CTRL, reg & ~RADC_PWR_ON); + + reg = aic3x_read_reg_cache(codec, DAC_PWR); + aic3x_write(codec, DAC_PWR, reg & ~(LDAC_PWR_ON | RDAC_PWR_ON)); + + reg = aic3x_read_reg_cache(codec, HPLOUT_CTRL); + aic3x_write(codec, HPLOUT_CTRL, reg & ~HPLOUT_PWR_ON); + reg = aic3x_read_reg_cache(codec, HPROUT_CTRL); + aic3x_write(codec, HPROUT_CTRL, reg & ~HPROUT_PWR_ON); + + reg = aic3x_read_reg_cache(codec, HPLCOM_CTRL); + aic3x_write(codec, HPLCOM_CTRL, reg & ~HPLCOM_PWR_ON); + reg = aic3x_read_reg_cache(codec, HPRCOM_CTRL); + aic3x_write(codec, HPRCOM_CTRL, reg & ~HPRCOM_PWR_ON); + + reg = aic3x_read_reg_cache(codec, MONOLOPM_CTRL); + aic3x_write(codec, MONOLOPM_CTRL, reg & ~MONOLOPM_PWR_ON); + + reg = aic3x_read_reg_cache(codec, LLOPM_CTRL); + aic3x_write(codec, LLOPM_CTRL, reg & ~LLOPM_PWR_ON); + reg = aic3x_read_reg_cache(codec, RLOPM_CTRL); + aic3x_write(codec, RLOPM_CTRL, reg & ~RLOPM_PWR_ON); + + if (aic3x->master) { + /* disable pll */ + reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG); + aic3x_write(codec, AIC3X_PLL_PROGA_REG, + reg & ~PLL_ENABLE); + } + break; + } + codec->dapm_state = event; + + return 0; +} + +#define AIC3X_RATES SNDRV_PCM_RATE_8000_96000 +#define AIC3X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +struct snd_soc_codec_dai aic3x_dai = { + .name = "aic3x", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AIC3X_RATES, + .formats = AIC3X_FORMATS,}, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AIC3X_RATES, + .formats = AIC3X_FORMATS,}, + .ops = { + .hw_params = aic3x_hw_params, + }, + .dai_ops = { + .digital_mute = aic3x_mute, + .set_sysclk = aic3x_set_dai_sysclk, + .set_fmt = aic3x_set_dai_fmt, + } +}; +EXPORT_SYMBOL_GPL(aic3x_dai); + +static int aic3x_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->codec; + + aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3cold); + + return 0; +} + +static int aic3x_resume(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->codec; + int i; + u8 data[2]; + u8 *cache = codec->reg_cache; + + /* Sync reg_cache with the hardware */ + for (i = 0; i < ARRAY_SIZE(aic3x_reg); i++) { + data[0] = i; + data[1] = cache[i]; + codec->hw_write(codec->control_data, data, 2); + } + + aic3x_dapm_event(codec, codec->suspend_dapm_state); + + return 0; +} + +/* + * initialise the AIC3X driver + * register the mixer and dsp interfaces with the kernel + */ +static int aic3x_init(struct snd_soc_device *socdev) +{ + struct snd_soc_codec *codec = socdev->codec; + int reg, ret = 0; + + codec->name = "aic3x"; + codec->owner = THIS_MODULE; + codec->read = aic3x_read_reg_cache; + codec->write = aic3x_write; + codec->dapm_event = aic3x_dapm_event; + codec->dai = &aic3x_dai; + codec->num_dai = 1; + codec->reg_cache_size = sizeof(aic3x_reg); + codec->reg_cache = kmemdup(aic3x_reg, sizeof(aic3x_reg), GFP_KERNEL); + if (codec->reg_cache == NULL) + return -ENOMEM; + + aic3x_write(codec, AIC3X_PAGE_SELECT, PAGE0_SELECT); + aic3x_write(codec, AIC3X_RESET, SOFT_RESET); + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + printk(KERN_ERR "aic3x: failed to create pcms\n"); + goto pcm_err; + } + + /* DAC default volume and mute */ + aic3x_write(codec, LDAC_VOL, DEFAULT_VOL | MUTE_ON); + aic3x_write(codec, RDAC_VOL, DEFAULT_VOL | MUTE_ON); + + /* DAC to HP default volume and route to Output mixer */ + aic3x_write(codec, DACL1_2_HPLOUT_VOL, DEFAULT_VOL | ROUTE_ON); + aic3x_write(codec, DACR1_2_HPROUT_VOL, DEFAULT_VOL | ROUTE_ON); + aic3x_write(codec, DACL1_2_HPLCOM_VOL, DEFAULT_VOL | ROUTE_ON); + aic3x_write(codec, DACR1_2_HPRCOM_VOL, DEFAULT_VOL | ROUTE_ON); + /* DAC to Line Out default volume and route to Output mixer */ + aic3x_write(codec, DACL1_2_LLOPM_VOL, DEFAULT_VOL | ROUTE_ON); + aic3x_write(codec, DACR1_2_RLOPM_VOL, DEFAULT_VOL | ROUTE_ON); + /* DAC to Mono Line Out default volume and route to Output mixer */ + aic3x_write(codec, DACL1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON); + aic3x_write(codec, DACR1_2_MONOLOPM_VOL, DEFAULT_VOL | ROUTE_ON); + + /* unmute all outputs */ + reg = aic3x_read_reg_cache(codec, LLOPM_CTRL); + aic3x_write(codec, LLOPM_CTRL, reg | UNMUTE); + reg = aic3x_read_reg_cache(codec, RLOPM_CTRL); + aic3x_write(codec, RLOPM_CTRL, reg | UNMUTE); + reg = aic3x_read_reg_cache(codec, MONOLOPM_CTRL); + aic3x_write(codec, MONOLOPM_CTRL, reg | UNMUTE); + reg = aic3x_read_reg_cache(codec, HPLOUT_CTRL); + aic3x_write(codec, HPLOUT_CTRL, reg | UNMUTE); + reg = aic3x_read_reg_cache(codec, HPROUT_CTRL); + aic3x_write(codec, HPROUT_CTRL, reg | UNMUTE); + reg = aic3x_read_reg_cache(codec, HPLCOM_CTRL); + aic3x_write(codec, HPLCOM_CTRL, reg | UNMUTE); + reg = aic3x_read_reg_cache(codec, HPRCOM_CTRL); + aic3x_write(codec, HPRCOM_CTRL, reg | UNMUTE); + + /* ADC default volume and unmute */ + aic3x_write(codec, LADC_VOL, DEFAULT_GAIN); + aic3x_write(codec, RADC_VOL, DEFAULT_GAIN); + /* By default route Line1 to ADC PGA mixer */ + aic3x_write(codec, LINE1L_2_LADC_CTRL, 0x0); + aic3x_write(codec, LINE1R_2_RADC_CTRL, 0x0); + + /* PGA to HP Bypass default volume, disconnect from Output Mixer */ + aic3x_write(codec, PGAL_2_HPLOUT_VOL, DEFAULT_VOL); + aic3x_write(codec, PGAR_2_HPROUT_VOL, DEFAULT_VOL); + aic3x_write(codec, PGAL_2_HPLCOM_VOL, DEFAULT_VOL); + aic3x_write(codec, PGAR_2_HPRCOM_VOL, DEFAULT_VOL); + /* PGA to Line Out default volume, disconnect from Output Mixer */ + aic3x_write(codec, PGAL_2_LLOPM_VOL, DEFAULT_VOL); + aic3x_write(codec, PGAR_2_RLOPM_VOL, DEFAULT_VOL); + /* PGA to Mono Line Out default volume, disconnect from Output Mixer */ + aic3x_write(codec, PGAL_2_MONOLOPM_VOL, DEFAULT_VOL); + aic3x_write(codec, PGAR_2_MONOLOPM_VOL, DEFAULT_VOL); + + /* Line2 to HP Bypass default volume, disconnect from Output Mixer */ + aic3x_write(codec, LINE2L_2_HPLOUT_VOL, DEFAULT_VOL); + aic3x_write(codec, LINE2R_2_HPROUT_VOL, DEFAULT_VOL); + aic3x_write(codec, LINE2L_2_HPLCOM_VOL, DEFAULT_VOL); + aic3x_write(codec, LINE2R_2_HPRCOM_VOL, DEFAULT_VOL); + /* Line2 Line Out default volume, disconnect from Output Mixer */ + aic3x_write(codec, LINE2L_2_LLOPM_VOL, DEFAULT_VOL); + aic3x_write(codec, LINE2R_2_RLOPM_VOL, DEFAULT_VOL); + /* Line2 to Mono Out default volume, disconnect from Output Mixer */ + aic3x_write(codec, LINE2L_2_MONOLOPM_VOL, DEFAULT_VOL); + aic3x_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL); + + /* off, with power on */ + aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3hot); + + aic3x_add_controls(codec); + aic3x_add_widgets(codec); + ret = snd_soc_register_card(socdev); + if (ret < 0) { + printk(KERN_ERR "aic3x: failed to register card\n"); + goto card_err; + } + + return ret; + +card_err: + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); +pcm_err: + kfree(codec->reg_cache); + return ret; +} + +static struct snd_soc_device *aic3x_socdev; + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +/* + * AIC3X 2 wire address can be up to 4 devices with device addresses + * 0x18, 0x19, 0x1A, 0x1B + */ +static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; + +/* Magic definition of all other variables and things */ +I2C_CLIENT_INSMOD; + +static struct i2c_driver aic3x_i2c_driver; +static struct i2c_client client_template; + +/* + * If the i2c layer weren't so broken, we could pass this kind of data + * around + */ +static int aic3x_codec_probe(struct i2c_adapter *adap, int addr, int kind) +{ + struct snd_soc_device *socdev = aic3x_socdev; + struct aic3x_setup_data *setup = socdev->codec_data; + struct snd_soc_codec *codec = socdev->codec; + struct i2c_client *i2c; + int ret; + + if (addr != setup->i2c_address) + return -ENODEV; + + client_template.adapter = adap; + client_template.addr = addr; + + i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); + if (i2c == NULL) { + kfree(codec); + return -ENOMEM; + } + i2c_set_clientdata(i2c, codec); + codec->control_data = i2c; + + ret = i2c_attach_client(i2c); + if (ret < 0) { + printk(KERN_ERR "aic3x: failed to attach codec at addr %x\n", + addr); + goto err; + } + + ret = aic3x_init(socdev); + if (ret < 0) { + printk(KERN_ERR "aic3x: failed to initialise AIC3X\n"); + goto err; + } + return ret; + +err: + kfree(codec); + kfree(i2c); + return ret; +} + +static int aic3x_i2c_detach(struct i2c_client *client) +{ + struct snd_soc_codec *codec = i2c_get_clientdata(client); + i2c_detach_client(client); + kfree(codec->reg_cache); + kfree(client); + return 0; +} + +static int aic3x_i2c_attach(struct i2c_adapter *adap) +{ + return i2c_probe(adap, &addr_data, aic3x_codec_probe); +} + +/* machine i2c codec control layer */ +static struct i2c_driver aic3x_i2c_driver = { + .driver = { + .name = "aic3x I2C Codec", + .owner = THIS_MODULE, + }, + .id = I2C_DRIVERID_I2CDEV, + .attach_adapter = aic3x_i2c_attach, + .detach_client = aic3x_i2c_detach, + .command = NULL, +}; + +static struct i2c_client client_template = { + .name = "AIC3X", + .driver = &aic3x_i2c_driver, +}; +#endif + +static int aic3x_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct aic3x_setup_data *setup; + struct snd_soc_codec *codec; + struct aic3x_priv *aic3x; + int ret = 0; + + printk(KERN_INFO "AIC3X Audio Codec %s\n", AIC3X_VERSION); + + setup = socdev->codec_data; + codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); + if (codec == NULL) + return -ENOMEM; + + aic3x = kzalloc(sizeof(struct aic3x_priv), GFP_KERNEL); + if (aic3x == NULL) { + kfree(codec); + return -ENOMEM; + } + + codec->private_data = aic3x; + socdev->codec = codec; + mutex_init(&codec->mutex); + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + aic3x_socdev = socdev; +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + if (setup->i2c_address) { + normal_i2c[0] = setup->i2c_address; + codec->hw_write = (hw_write_t) i2c_master_send; + ret = i2c_add_driver(&aic3x_i2c_driver); + if (ret != 0) + printk(KERN_ERR "can't add i2c driver"); + } +#else + /* Add other interfaces here */ +#endif + return ret; +} + +static int aic3x_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->codec; + + /* power down chip */ + if (codec->control_data) + aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3); + + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + i2c_del_driver(&aic3x_i2c_driver); +#endif + kfree(codec->private_data); + kfree(codec); + + return 0; +} + +struct snd_soc_codec_device soc_codec_dev_aic3x = { + .probe = aic3x_probe, + .remove = aic3x_remove, + .suspend = aic3x_suspend, + .resume = aic3x_resume, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_aic3x); + +MODULE_DESCRIPTION("ASoC TLV320AIC3X codec driver"); +MODULE_AUTHOR("Vladimir Barinov"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h new file mode 100644 index 0000000..d0cdeeb --- /dev/null +++ b/sound/soc/codecs/tlv320aic3x.h @@ -0,0 +1,181 @@ +/* + * ALSA SoC TLV320AIC3X codec driver + * + * Author: Vladimir Barinov, <vbarinov@ru.mvista.com> + * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _AIC3X_H +#define _AIC3X_H + +/* AIC3X register space */ +#define AIC3X_CACHEREGNUM 103 + +/* Page select register */ +#define AIC3X_PAGE_SELECT 0 +/* Software reset register */ +#define AIC3X_RESET 1 +/* Codec Sample rate select register */ +#define AIC3X_SAMPLE_RATE_SEL_REG 2 +/* PLL progrramming register A */ +#define AIC3X_PLL_PROGA_REG 3 +/* PLL progrramming register B */ +#define AIC3X_PLL_PROGB_REG 4 +/* PLL progrramming register C */ +#define AIC3X_PLL_PROGC_REG 5 +/* PLL progrramming register D */ +#define AIC3X_PLL_PROGD_REG 6 +/* Codec datapath setup register */ +#define AIC3X_CODEC_DATAPATH_REG 7 +/* Audio serial data interface control register A */ +#define AIC3X_ASD_INTF_CTRLA 8 +/* Audio serial data interface control register B */ +#define AIC3X_ASD_INTF_CTRLB 9 +/* Audio overflow status and PLL R value programming register */ +#define AIC3X_OVRF_STATUS_AND_PLLR_REG 11 + +/* ADC PGA Gain control registers */ +#define LADC_VOL 15 +#define RADC_VOL 16 +/* MIC3 control registers */ +#define MIC3LR_2_LADC_CTRL 17 +#define MIC3LR_2_RADC_CTRL 18 +/* Line1 Input control registers */ +#define LINE1L_2_LADC_CTRL 19 +#define LINE1R_2_RADC_CTRL 22 +/* Line2 Input control registers */ +#define LINE2L_2_LADC_CTRL 20 +#define LINE2R_2_RADC_CTRL 23 +/* MICBIAS Control Register */ +#define MICBIAS_CTRL 25 + +/* AGC Control Registers A, B, C */ +#define LAGC_CTRL_A 26 +#define LAGC_CTRL_B 27 +#define LAGC_CTRL_C 28 +#define RAGC_CTRL_A 29 +#define RAGC_CTRL_B 30 +#define RAGC_CTRL_C 31 + +/* DAC Power and Left High Power Output control registers */ +#define DAC_PWR 37 +#define HPLCOM_CFG 37 +/* Right High Power Output control registers */ +#define HPRCOM_CFG 38 +/* DAC Output Switching control registers */ +#define DAC_LINE_MUX 41 +/* High Power Output Driver Pop Reduction registers */ +#define HPOUT_POP_REDUCTION 42 +/* DAC Digital control registers */ +#define LDAC_VOL 43 +#define RDAC_VOL 44 +/* High Power Output control registers */ +#define LINE2L_2_HPLOUT_VOL 45 +#define LINE2R_2_HPROUT_VOL 62 +#define PGAL_2_HPLOUT_VOL 46 +#define PGAR_2_HPROUT_VOL 63 +#define DACL1_2_HPLOUT_VOL 47 +#define DACR1_2_HPROUT_VOL 64 +#define HPLOUT_CTRL 51 +#define HPROUT_CTRL 65 +/* High Power COM control registers */ +#define LINE2L_2_HPLCOM_VOL 52 +#define LINE2R_2_HPRCOM_VOL 69 +#define PGAL_2_HPLCOM_VOL 53 +#define PGAR_2_HPRCOM_VOL 70 +#define DACL1_2_HPLCOM_VOL 54 +#define DACR1_2_HPRCOM_VOL 71 +#define HPLCOM_CTRL 58 +#define HPRCOM_CTRL 72 +/* Mono Line Output Plus/Minus control registers */ +#define LINE2L_2_MONOLOPM_VOL 73 +#define LINE2R_2_MONOLOPM_VOL 76 +#define PGAL_2_MONOLOPM_VOL 74 +#define PGAR_2_MONOLOPM_VOL 77 +#define DACL1_2_MONOLOPM_VOL 75 +#define DACR1_2_MONOLOPM_VOL 78 +#define MONOLOPM_CTRL 79 +/* Line Output Plus/Minus control registers */ +#define LINE2L_2_LLOPM_VOL 80 +#define LINE2R_2_RLOPM_VOL 90 +#define PGAL_2_LLOPM_VOL 81 +#define PGAR_2_RLOPM_VOL 91 +#define DACL1_2_LLOPM_VOL 82 +#define DACR1_2_RLOPM_VOL 92 +#define LLOPM_CTRL 86 +#define RLOPM_CTRL 93 +/* Clock generation control register */ +#define AIC3X_CLKGEN_CTRL_REG 102 + +/* Page select register bits */ +#define PAGE0_SELECT 0 +#define PAGE1_SELECT 1 + +/* Audio serial data interface control register A bits */ +#define BIT_CLK_MASTER 0x80 +#define WORD_CLK_MASTER 0x40 + +/* Codec Datapath setup register 7 */ +#define FSREF_44100 (1 << 7) +#define FSREF_48000 (0 << 7) +#define DUAL_RATE_MODE ((1 << 5) | (1 << 6)) +#define LDAC2LCH (0x1 << 3) +#define RDAC2RCH (0x1 << 1) + +/* PLL registers bitfields */ +#define PLLP_SHIFT 0 +#define PLLR_SHIFT 0 +#define PLLJ_SHIFT 2 +#define PLLD_MSB_SHIFT 0 +#define PLLD_LSB_SHIFT 2 + +/* Clock generation register bits */ +#define PLL_CLKIN_SHIFT 4 +#define MCLK_SOURCE 0x0 +#define PLL_CLKDIV_SHIFT 0 + +/* Software reset register bits */ +#define SOFT_RESET 0x80 + +/* PLL progrramming register A bits */ +#define PLL_ENABLE 0x80 + +/* Route bits */ +#define ROUTE_ON 0x80 + +/* Mute bits */ +#define UNMUTE 0x08 +#define MUTE_ON 0x80 + +/* Power bits */ +#define LADC_PWR_ON 0x04 +#define RADC_PWR_ON 0x04 +#define LDAC_PWR_ON 0x80 +#define RDAC_PWR_ON 0x40 +#define HPLOUT_PWR_ON 0x01 +#define HPROUT_PWR_ON 0x01 +#define HPLCOM_PWR_ON 0x01 +#define HPRCOM_PWR_ON 0x01 +#define MONOLOPM_PWR_ON 0x01 +#define LLOPM_PWR_ON 0x01 +#define RLOPM_PWR_ON 0x01 + +#define INVERT_VOL(val) (0x7f - val) + +/* Default output volume (inverted) */ +#define DEFAULT_VOL INVERT_VOL(0x50) +/* Default input volume */ +#define DEFAULT_GAIN 0x20 + +struct aic3x_setup_data { + unsigned short i2c_address; +}; + +extern struct snd_soc_codec_dai aic3x_dai; +extern struct snd_soc_codec_device soc_codec_dev_aic3x; + +#endif /* _AIC3X_H */ diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 7ca0b52..9c33fe8 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -19,7 +19,6 @@ #include <linux/pm.h> #include <linux/i2c.h> #include <linux/platform_device.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -562,13 +561,13 @@ static int wm8731_init(struct snd_soc_device *socdev) /* set the update bits */ reg = wm8731_read_reg_cache(codec, WM8731_LOUT1V); - wm8731_write(codec, WM8731_LOUT1V, reg | 0x0100); + wm8731_write(codec, WM8731_LOUT1V, reg & ~0x0100); reg = wm8731_read_reg_cache(codec, WM8731_ROUT1V); - wm8731_write(codec, WM8731_ROUT1V, reg | 0x0100); + wm8731_write(codec, WM8731_ROUT1V, reg & ~0x0100); reg = wm8731_read_reg_cache(codec, WM8731_LINVOL); - wm8731_write(codec, WM8731_LINVOL, reg | 0x0100); + wm8731_write(codec, WM8731_LINVOL, reg & ~0x0100); reg = wm8731_read_reg_cache(codec, WM8731_RINVOL); - wm8731_write(codec, WM8731_RINVOL, reg | 0x0100); + wm8731_write(codec, WM8731_RINVOL, reg & ~0x0100); wm8731_add_controls(codec); wm8731_add_widgets(codec); diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index 28684ee..77a857b 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -19,7 +19,6 @@ #include <linux/pm.h> #include <linux/i2c.h> #include <linux/platform_device.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -189,7 +188,7 @@ SOC_ENUM("Bass Boost", wm8750_enum[0]), SOC_ENUM("Bass Filter", wm8750_enum[1]), SOC_SINGLE("Bass Volume", WM8750_BASS, 0, 15, 1), -SOC_SINGLE("Treble Volume", WM8750_TREBLE, 0, 15, 0), +SOC_SINGLE("Treble Volume", WM8750_TREBLE, 0, 15, 1), SOC_ENUM("Treble Cut-off", wm8750_enum[2]), SOC_SINGLE("3D Switch", WM8750_3D, 0, 1, 0), diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index efced93..ddd9c71 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -41,13 +41,13 @@ #include <linux/pm.h> #include <linux/i2c.h> #include <linux/platform_device.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/soc-dapm.h> #include <sound/initval.h> +#include <sound/tlv.h> #include <asm/div64.h> #include "wm8753.h" @@ -258,6 +258,8 @@ static int wm8753_set_dai(struct snd_kcontrol *kcontrol, return 1; } +static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600); + static const struct snd_kcontrol_new wm8753_snd_controls[] = { SOC_DOUBLE_R("PCM Volume", WM8753_LDAC, WM8753_RDAC, 0, 255, 0), @@ -287,8 +289,8 @@ SOC_SINGLE("Bass Volume", WM8753_BASS, 0, 15, 1), SOC_SINGLE("Treble Volume", WM8753_TREBLE, 0, 15, 1), SOC_ENUM("Treble Cut-off", wm8753_enum[2]), -SOC_DOUBLE("Sidetone Capture Volume", WM8753_RECMIX1, 0, 4, 7, 1), -SOC_SINGLE("Voice Sidetone Capture Volume", WM8753_RECMIX2, 0, 7, 1), +SOC_DOUBLE_TLV("Sidetone Capture Volume", WM8753_RECMIX1, 0, 4, 7, 1, rec_mix_tlv), +SOC_SINGLE_TLV("Voice Sidetone Capture Volume", WM8753_RECMIX2, 0, 7, 1, rec_mix_tlv), SOC_DOUBLE_R("Capture Volume", WM8753_LINVOL, WM8753_RINVOL, 0, 63, 0), SOC_DOUBLE_R("Capture ZC Switch", WM8753_LINVOL, WM8753_RINVOL, 6, 1, 0), diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 986b5d59..590baea 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -19,7 +19,6 @@ #include <linux/version.h> #include <linux/kernel.h> #include <linux/device.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/ac97_codec.h> @@ -102,7 +101,8 @@ SOC_SINGLE("Speaker Playback ZC Switch", AC97_MASTER, 7, 1, 0), SOC_SINGLE("Speaker Playback Invert Switch", AC97_MASTER, 6, 1, 0), SOC_SINGLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 7, 1, 0), SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_MONO, 7, 1, 0), -SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 0), +SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1), +SOC_SINGLE("Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1), SOC_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0), SOC_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0), @@ -131,7 +131,7 @@ SOC_SINGLE("Aux Playback Headphone Volume", AC97_CD, 12, 7, 1), SOC_SINGLE("Aux Playback Speaker Volume", AC97_CD, 8, 7, 1), SOC_SINGLE("Aux Playback Phone Volume", AC97_CD, 4, 7, 1), -SOC_SINGLE("Phone Volume", AC97_PHONE, 0, 15, 0), +SOC_SINGLE("Phone Volume", AC97_PHONE, 0, 15, 1), SOC_DOUBLE("Line Capture Volume", AC97_LINE, 8, 0, 31, 1), SOC_SINGLE("Capture 20dB Boost Switch", AC97_REC_SEL, 14, 1, 0), @@ -145,8 +145,8 @@ SOC_ENUM("Bass Control", wm9712_enum[5]), SOC_SINGLE("Bass Cut-off Switch", AC97_MASTER_TONE, 12, 1, 1), SOC_SINGLE("Tone Cut-off Switch", AC97_MASTER_TONE, 4, 1, 1), SOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_MASTER_TONE, 6, 1, 0), -SOC_SINGLE("Bass Volume", AC97_MASTER_TONE, 8, 15, 0), -SOC_SINGLE("Treble Volume", AC97_MASTER_TONE, 0, 15, 0), +SOC_SINGLE("Bass Volume", AC97_MASTER_TONE, 8, 15, 1), +SOC_SINGLE("Treble Volume", AC97_MASTER_TONE, 0, 15, 1), SOC_SINGLE("Capture ADC Switch", AC97_REC_GAIN, 15, 1, 1), SOC_ENUM("Capture Volume Steps", wm9712_enum[6]), diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig new file mode 100644 index 0000000..257101f --- /dev/null +++ b/sound/soc/fsl/Kconfig @@ -0,0 +1,20 @@ +menu "ALSA SoC audio for Freescale SOCs" + +config SND_SOC_MPC8610 + bool "ALSA SoC support for the MPC8610 SOC" + depends on SND_SOC && MPC8610_HPCD + default y if MPC8610 + help + Say Y if you want to add support for codecs attached to the SSI + device on an MPC8610. + +config SND_SOC_MPC8610_HPCD + bool "ALSA SoC support for the Freescale MPC8610 HPCD board" + depends on SND_SOC_MPC8610 + select SND_SOC_CS4270 + select SND_SOC_CS4270_VD33_ERRATA + default y if MPC8610_HPCD + help + Say Y if you want to enable audio on the Freescale MPC8610 HPCD. + +endmenu diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile new file mode 100644 index 0000000..62f680a --- /dev/null +++ b/sound/soc/fsl/Makefile @@ -0,0 +1,6 @@ +# MPC8610 HPCD Machine Support +obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += mpc8610_hpcd.o + +# MPC8610 Platform Support +obj-$(CONFIG_SND_SOC_MPC8610) += fsl_ssi.o fsl_dma.o + diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c new file mode 100644 index 0000000..652514f --- /dev/null +++ b/sound/soc/fsl/fsl_dma.c @@ -0,0 +1,841 @@ +/* + * Freescale DMA ALSA SoC PCM driver + * + * Author: Timur Tabi <timur@freescale.com> + * + * Copyright 2007-2008 Freescale Semiconductor, Inc. This file is licensed + * under the terms of the GNU General Public License version 2. This + * program is licensed "as is" without any warranty of any kind, whether + * express or implied. + * + * This driver implements ASoC support for the Elo DMA controller, which is + * the DMA controller on Freescale 83xx, 85xx, and 86xx SOCs. In ALSA terms, + * the PCM driver is what handles the DMA buffer. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/delay.h> + +#include <sound/driver.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include <asm/io.h> + +#include "fsl_dma.h" + +/* + * The formats that the DMA controller supports, which is anything + * that is 8, 16, or 32 bits. + */ +#define FSLDMA_PCM_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_U8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S16_BE | \ + SNDRV_PCM_FMTBIT_U16_LE | \ + SNDRV_PCM_FMTBIT_U16_BE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S24_BE | \ + SNDRV_PCM_FMTBIT_U24_LE | \ + SNDRV_PCM_FMTBIT_U24_BE | \ + SNDRV_PCM_FMTBIT_S32_LE | \ + SNDRV_PCM_FMTBIT_S32_BE | \ + SNDRV_PCM_FMTBIT_U32_LE | \ + SNDRV_PCM_FMTBIT_U32_BE) + +#define FSLDMA_PCM_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \ + SNDRV_PCM_RATE_CONTINUOUS) + +/* DMA global data. This structure is used by fsl_dma_open() to determine + * which DMA channels to assign to a substream. Unfortunately, ASoC V1 does + * not allow the machine driver to provide this information to the PCM + * driver in advance, and there's no way to differentiate between the two + * DMA controllers. So for now, this driver only supports one SSI device + * using two DMA channels. We cannot support multiple DMA devices. + * + * ssi_stx_phys: bus address of SSI STX register + * ssi_srx_phys: bus address of SSI SRX register + * dma_channel: pointer to the DMA channel's registers + * irq: IRQ for this DMA channel + * assigned: set to 1 if that DMA channel is assigned to a substream + */ +static struct { + dma_addr_t ssi_stx_phys; + dma_addr_t ssi_srx_phys; + struct ccsr_dma_channel __iomem *dma_channel[2]; + unsigned int irq[2]; + unsigned int assigned[2]; +} dma_global_data; + +/* + * The number of DMA links to use. Two is the bare minimum, but if you + * have really small links you might need more. + */ +#define NUM_DMA_LINKS 2 + +/** fsl_dma_private: p-substream DMA data + * + * Each substream has a 1-to-1 association with a DMA channel. + * + * The link[] array is first because it needs to be aligned on a 32-byte + * boundary, so putting it first will ensure alignment without padding the + * structure. + * + * @link[]: array of link descriptors + * @controller_id: which DMA controller (0, 1, ...) + * @channel_id: which DMA channel on the controller (0, 1, 2, ...) + * @dma_channel: pointer to the DMA channel's registers + * @irq: IRQ for this DMA channel + * @substream: pointer to the substream object, needed by the ISR + * @ssi_sxx_phys: bus address of the STX or SRX register to use + * @ld_buf_phys: physical address of the LD buffer + * @current_link: index into link[] of the link currently being processed + * @dma_buf_phys: physical address of the DMA buffer + * @dma_buf_next: physical address of the next period to process + * @dma_buf_end: physical address of the byte after the end of the DMA + * @buffer period_size: the size of a single period + * @num_periods: the number of periods in the DMA buffer + */ +struct fsl_dma_private { + struct fsl_dma_link_descriptor link[NUM_DMA_LINKS]; + unsigned int controller_id; + unsigned int channel_id; + struct ccsr_dma_channel __iomem *dma_channel; + unsigned int irq; + struct snd_pcm_substream *substream; + dma_addr_t ssi_sxx_phys; + dma_addr_t ld_buf_phys; + unsigned int current_link; + dma_addr_t dma_buf_phys; + dma_addr_t dma_buf_next; + dma_addr_t dma_buf_end; + size_t period_size; + unsigned int num_periods; +}; + +/** + * fsl_dma_hardare: define characteristics of the PCM hardware. + * + * The PCM hardware is the Freescale DMA controller. This structure defines + * the capabilities of that hardware. + * + * Since the sampling rate and data format are not controlled by the DMA + * controller, we specify no limits for those values. The only exception is + * period_bytes_min, which is set to a reasonably low value to prevent the + * DMA controller from generating too many interrupts per second. + * + * Since each link descriptor has a 32-bit byte count field, we set + * period_bytes_max to the largest 32-bit number. We also have no maximum + * number of periods. + */ +static const struct snd_pcm_hardware fsl_dma_hardware = { + + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID, + .formats = FSLDMA_PCM_FORMATS, + .rates = FSLDMA_PCM_RATES, + .rate_min = 5512, + .rate_max = 192000, + .period_bytes_min = 512, /* A reasonable limit */ + .period_bytes_max = (u32) -1, + .periods_min = NUM_DMA_LINKS, + .periods_max = (unsigned int) -1, + .buffer_bytes_max = 128 * 1024, /* A reasonable limit */ +}; + +/** + * fsl_dma_abort_stream: tell ALSA that the DMA transfer has aborted + * + * This function should be called by the ISR whenever the DMA controller + * halts data transfer. + */ +static void fsl_dma_abort_stream(struct snd_pcm_substream *substream) +{ + unsigned long flags; + + snd_pcm_stream_lock_irqsave(substream, flags); + + if (snd_pcm_running(substream)) + snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + + snd_pcm_stream_unlock_irqrestore(substream, flags); +} + +/** + * fsl_dma_update_pointers - update LD pointers to point to the next period + * + * As each period is completed, this function changes the the link + * descriptor pointers for that period to point to the next period. + */ +static void fsl_dma_update_pointers(struct fsl_dma_private *dma_private) +{ + struct fsl_dma_link_descriptor *link = + &dma_private->link[dma_private->current_link]; + + /* Update our link descriptors to point to the next period */ + if (dma_private->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + link->source_addr = + cpu_to_be32(dma_private->dma_buf_next); + else + link->dest_addr = + cpu_to_be32(dma_private->dma_buf_next); + + /* Update our variables for next time */ + dma_private->dma_buf_next += dma_private->period_size; + + if (dma_private->dma_buf_next >= dma_private->dma_buf_end) + dma_private->dma_buf_next = dma_private->dma_buf_phys; + + if (++dma_private->current_link >= NUM_DMA_LINKS) + dma_private->current_link = 0; +} + +/** + * fsl_dma_isr: interrupt handler for the DMA controller + * + * @irq: IRQ of the DMA channel + * @dev_id: pointer to the dma_private structure for this DMA channel + */ +static irqreturn_t fsl_dma_isr(int irq, void *dev_id) +{ + struct fsl_dma_private *dma_private = dev_id; + struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel; + irqreturn_t ret = IRQ_NONE; + u32 sr, sr2 = 0; + + /* We got an interrupt, so read the status register to see what we + were interrupted for. + */ + sr = in_be32(&dma_channel->sr); + + if (sr & CCSR_DMA_SR_TE) { + dev_err(dma_private->substream->pcm->card->dev, + "DMA transmit error (controller=%u channel=%u irq=%u\n", + dma_private->controller_id, + dma_private->channel_id, irq); + fsl_dma_abort_stream(dma_private->substream); + sr2 |= CCSR_DMA_SR_TE; + ret = IRQ_HANDLED; + } + + if (sr & CCSR_DMA_SR_CH) + ret = IRQ_HANDLED; + + if (sr & CCSR_DMA_SR_PE) { + dev_err(dma_private->substream->pcm->card->dev, + "DMA%u programming error (channel=%u irq=%u)\n", + dma_private->controller_id, + dma_private->channel_id, irq); + fsl_dma_abort_stream(dma_private->substream); + sr2 |= CCSR_DMA_SR_PE; + ret = IRQ_HANDLED; + } + + if (sr & CCSR_DMA_SR_EOLNI) { + sr2 |= CCSR_DMA_SR_EOLNI; + ret = IRQ_HANDLED; + } + + if (sr & CCSR_DMA_SR_CB) + ret = IRQ_HANDLED; + + if (sr & CCSR_DMA_SR_EOSI) { + struct snd_pcm_substream *substream = dma_private->substream; + + /* Tell ALSA we completed a period. */ + snd_pcm_period_elapsed(substream); + + /* + * Update our link descriptors to point to the next period. We + * only need to do this if the number of periods is not equal to + * the number of links. + */ + if (dma_private->num_periods != NUM_DMA_LINKS) + fsl_dma_update_pointers(dma_private); + + sr2 |= CCSR_DMA_SR_EOSI; + ret = IRQ_HANDLED; + } + + if (sr & CCSR_DMA_SR_EOLSI) { + sr2 |= CCSR_DMA_SR_EOLSI; + ret = IRQ_HANDLED; + } + + /* Clear the bits that we set */ + if (sr2) + out_be32(&dma_channel->sr, sr2); + + return ret; +} + +/** + * fsl_dma_new: initialize this PCM driver. + * + * This function is called when the codec driver calls snd_soc_new_pcms(), + * once for each .dai_link in the machine driver's snd_soc_machine + * structure. + */ +static int fsl_dma_new(struct snd_card *card, struct snd_soc_codec_dai *dai, + struct snd_pcm *pcm) +{ + static u64 fsl_dma_dmamask = DMA_BIT_MASK(32); + int ret; + + if (!card->dev->dma_mask) + card->dev->dma_mask = &fsl_dma_dmamask; + + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = fsl_dma_dmamask; + + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, + fsl_dma_hardware.buffer_bytes_max, + &pcm->streams[0].substream->dma_buffer); + if (ret) { + dev_err(card->dev, + "Can't allocate playback DMA buffer (size=%u)\n", + fsl_dma_hardware.buffer_bytes_max); + return -ENOMEM; + } + + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, + fsl_dma_hardware.buffer_bytes_max, + &pcm->streams[1].substream->dma_buffer); + if (ret) { + snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer); + dev_err(card->dev, + "Can't allocate capture DMA buffer (size=%u)\n", + fsl_dma_hardware.buffer_bytes_max); + return -ENOMEM; + } + + return 0; +} + +/** + * fsl_dma_open: open a new substream. + * + * Each substream has its own DMA buffer. + */ +static int fsl_dma_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct fsl_dma_private *dma_private; + dma_addr_t ld_buf_phys; + unsigned int channel; + int ret = 0; + + /* + * Reject any DMA buffer whose size is not a multiple of the period + * size. We need to make sure that the DMA buffer can be evenly divided + * into periods. + */ + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) { + dev_err(substream->pcm->card->dev, "invalid buffer size\n"); + return ret; + } + + channel = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; + + if (dma_global_data.assigned[channel]) { + dev_err(substream->pcm->card->dev, + "DMA channel already assigned\n"); + return -EBUSY; + } + + dma_private = dma_alloc_coherent(substream->pcm->dev, + sizeof(struct fsl_dma_private), &ld_buf_phys, GFP_KERNEL); + if (!dma_private) { + dev_err(substream->pcm->card->dev, + "can't allocate DMA private data\n"); + return -ENOMEM; + } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dma_private->ssi_sxx_phys = dma_global_data.ssi_stx_phys; + else + dma_private->ssi_sxx_phys = dma_global_data.ssi_srx_phys; + + dma_private->dma_channel = dma_global_data.dma_channel[channel]; + dma_private->irq = dma_global_data.irq[channel]; + dma_private->substream = substream; + dma_private->ld_buf_phys = ld_buf_phys; + dma_private->dma_buf_phys = substream->dma_buffer.addr; + + /* We only support one DMA controller for now */ + dma_private->controller_id = 0; + dma_private->channel_id = channel; + + ret = request_irq(dma_private->irq, fsl_dma_isr, 0, "DMA", dma_private); + if (ret) { + dev_err(substream->pcm->card->dev, + "can't register ISR for IRQ %u (ret=%i)\n", + dma_private->irq, ret); + dma_free_coherent(substream->pcm->dev, + sizeof(struct fsl_dma_private), + dma_private, dma_private->ld_buf_phys); + return ret; + } + + dma_global_data.assigned[channel] = 1; + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + snd_soc_set_runtime_hwparams(substream, &fsl_dma_hardware); + runtime->private_data = dma_private; + + return 0; +} + +/** + * fsl_dma_hw_params: allocate the DMA buffer and the DMA link descriptors. + * + * ALSA divides the DMA buffer into N periods. We create NUM_DMA_LINKS link + * descriptors that ping-pong from one period to the next. For example, if + * there are six periods and two link descriptors, this is how they look + * before playback starts: + * + * The last link descriptor + * ____________ points back to the first + * | | + * V | + * ___ ___ | + * | |->| |->| + * |___| |___| + * | | + * | | + * V V + * _________________________________________ + * | | | | | | | The DMA buffer is + * | | | | | | | divided into 6 parts + * |______|______|______|______|______|______| + * + * and here's how they look after the first period is finished playing: + * + * ____________ + * | | + * V | + * ___ ___ | + * | |->| |->| + * |___| |___| + * | | + * |______________ + * | | + * V V + * _________________________________________ + * | | | | | | | + * | | | | | | | + * |______|______|______|______|______|______| + * + * The first link descriptor now points to the third period. The DMA + * controller is currently playing the second period. When it finishes, it + * will jump back to the first descriptor and play the third period. + * + * There are four reasons we do this: + * + * 1. The only way to get the DMA controller to automatically restart the + * transfer when it gets to the end of the buffer is to use chaining + * mode. Basic direct mode doesn't offer that feature. + * 2. We need to receive an interrupt at the end of every period. The DMA + * controller can generate an interrupt at the end of every link transfer + * (aka segment). Making each period into a DMA segment will give us the + * interrupts we need. + * 3. By creating only two link descriptors, regardless of the number of + * periods, we do not need to reallocate the link descriptors if the + * number of periods changes. + * 4. All of the audio data is still stored in a single, contiguous DMA + * buffer, which is what ALSA expects. We're just dividing it into + * contiguous parts, and creating a link descriptor for each one. + * + * Note that due to a quirk of the SSI's STX register, the target address + * for the DMA operations depends on the sample size. So we don't program + * the dest_addr (for playback -- source_addr for capture) fields in the + * link descriptors here. We do that in fsl_dma_prepare() + */ +static int fsl_dma_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct fsl_dma_private *dma_private = runtime->private_data; + struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel; + + dma_addr_t temp_addr; /* Pointer to next period */ + u64 temp_link; /* Pointer to next link descriptor */ + u32 mr; /* Temporary variable for MR register */ + + unsigned int i; + + /* Get all the parameters we need */ + size_t buffer_size = params_buffer_bytes(hw_params); + size_t period_size = params_period_bytes(hw_params); + + /* Initialize our DMA tracking variables */ + dma_private->period_size = period_size; + dma_private->num_periods = params_periods(hw_params); + dma_private->dma_buf_end = dma_private->dma_buf_phys + buffer_size; + dma_private->dma_buf_next = dma_private->dma_buf_phys + + (NUM_DMA_LINKS * period_size); + if (dma_private->dma_buf_next >= dma_private->dma_buf_end) + dma_private->dma_buf_next = dma_private->dma_buf_phys; + + /* + * Initialize each link descriptor. + * + * The actual address in STX0 (destination for playback, source for + * capture) is based on the sample size, but we don't know the sample + * size in this function, so we'll have to adjust that later. See + * comments in fsl_dma_prepare(). + * + * The DMA controller does not have a cache, so the CPU does not + * need to tell it to flush its cache. However, the DMA + * controller does need to tell the CPU to flush its cache. + * That's what the SNOOP bit does. + * + * Also, even though the DMA controller supports 36-bit addressing, for + * simplicity we currently support only 32-bit addresses for the audio + * buffer itself. + */ + temp_addr = substream->dma_buffer.addr; + temp_link = dma_private->ld_buf_phys + + sizeof(struct fsl_dma_link_descriptor); + + for (i = 0; i < NUM_DMA_LINKS; i++) { + struct fsl_dma_link_descriptor *link = &dma_private->link[i]; + + link->count = cpu_to_be32(period_size); + link->source_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP); + link->dest_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP); + link->next = cpu_to_be64(temp_link); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + link->source_addr = cpu_to_be32(temp_addr); + else + link->dest_addr = cpu_to_be32(temp_addr); + + temp_addr += period_size; + temp_link += sizeof(struct fsl_dma_link_descriptor); + } + /* The last link descriptor points to the first */ + dma_private->link[i - 1].next = cpu_to_be64(dma_private->ld_buf_phys); + + /* Tell the DMA controller where the first link descriptor is */ + out_be32(&dma_channel->clndar, + CCSR_DMA_CLNDAR_ADDR(dma_private->ld_buf_phys)); + out_be32(&dma_channel->eclndar, + CCSR_DMA_ECLNDAR_ADDR(dma_private->ld_buf_phys)); + + /* The manual says the BCR must be clear before enabling EMP */ + out_be32(&dma_channel->bcr, 0); + + /* + * Program the mode register for interrupts, external master control, + * and source/destination hold. Also clear the Channel Abort bit. + */ + mr = in_be32(&dma_channel->mr) & + ~(CCSR_DMA_MR_CA | CCSR_DMA_MR_DAHE | CCSR_DMA_MR_SAHE); + + /* + * We want External Master Start and External Master Pause enabled, + * because the SSI is controlling the DMA controller. We want the DMA + * controller to be set up in advance, and then we signal only the SSI + * to start transfering. + * + * We want End-Of-Segment Interrupts enabled, because this will generate + * an interrupt at the end of each segment (each link descriptor + * represents one segment). Each DMA segment is the same thing as an + * ALSA period, so this is how we get an interrupt at the end of every + * period. + * + * We want Error Interrupt enabled, so that we can get an error if + * the DMA controller is mis-programmed somehow. + */ + mr |= CCSR_DMA_MR_EOSIE | CCSR_DMA_MR_EIE | CCSR_DMA_MR_EMP_EN | + CCSR_DMA_MR_EMS_EN; + + /* For playback, we want the destination address to be held. For + capture, set the source address to be held. */ + mr |= (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + CCSR_DMA_MR_DAHE : CCSR_DMA_MR_SAHE; + + out_be32(&dma_channel->mr, mr); + + return 0; +} + +/** + * fsl_dma_prepare - prepare the DMA registers for playback. + * + * This function is called after the specifics of the audio data are known, + * i.e. snd_pcm_runtime is initialized. + * + * In this function, we finish programming the registers of the DMA + * controller that are dependent on the sample size. + * + * One of the drawbacks with big-endian is that when copying integers of + * different sizes to a fixed-sized register, the address to which the + * integer must be copied is dependent on the size of the integer. + * + * For example, if P is the address of a 32-bit register, and X is a 32-bit + * integer, then X should be copied to address P. However, if X is a 16-bit + * integer, then it should be copied to P+2. If X is an 8-bit register, + * then it should be copied to P+3. + * + * So for playback of 8-bit samples, the DMA controller must transfer single + * bytes from the DMA buffer to the last byte of the STX0 register, i.e. + * offset by 3 bytes. For 16-bit samples, the offset is two bytes. + * + * For 24-bit samples, the offset is 1 byte. However, the DMA controller + * does not support 3-byte copies (the DAHTS register supports only 1, 2, 4, + * and 8 bytes at a time). So we do not support packed 24-bit samples. + * 24-bit data must be padded to 32 bits. + */ +static int fsl_dma_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct fsl_dma_private *dma_private = runtime->private_data; + struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel; + u32 mr; + unsigned int i; + dma_addr_t ssi_sxx_phys; /* Bus address of SSI STX register */ + unsigned int frame_size; /* Number of bytes per frame */ + + ssi_sxx_phys = dma_private->ssi_sxx_phys; + + mr = in_be32(&dma_channel->mr) & ~(CCSR_DMA_MR_BWC_MASK | + CCSR_DMA_MR_SAHTS_MASK | CCSR_DMA_MR_DAHTS_MASK); + + switch (runtime->sample_bits) { + case 8: + mr |= CCSR_DMA_MR_DAHTS_1 | CCSR_DMA_MR_SAHTS_1; + ssi_sxx_phys += 3; + break; + case 16: + mr |= CCSR_DMA_MR_DAHTS_2 | CCSR_DMA_MR_SAHTS_2; + ssi_sxx_phys += 2; + break; + case 32: + mr |= CCSR_DMA_MR_DAHTS_4 | CCSR_DMA_MR_SAHTS_4; + break; + default: + dev_err(substream->pcm->card->dev, + "unsupported sample size %u\n", runtime->sample_bits); + return -EINVAL; + } + + frame_size = runtime->frame_bits / 8; + /* + * BWC should always be a multiple of the frame size. BWC determines + * how many bytes are sent/received before the DMA controller checks the + * SSI to see if it needs to stop. For playback, the transmit FIFO can + * hold three frames, so we want to send two frames at a time. For + * capture, the receive FIFO is triggered when it contains one frame, so + * we want to receive one frame at a time. + */ + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + mr |= CCSR_DMA_MR_BWC(2 * frame_size); + else + mr |= CCSR_DMA_MR_BWC(frame_size); + + out_be32(&dma_channel->mr, mr); + + /* + * Program the address of the DMA transfer to/from the SSI. + */ + for (i = 0; i < NUM_DMA_LINKS; i++) { + struct fsl_dma_link_descriptor *link = &dma_private->link[i]; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + link->dest_addr = cpu_to_be32(ssi_sxx_phys); + else + link->source_addr = cpu_to_be32(ssi_sxx_phys); + } + + return 0; +} + +/** + * fsl_dma_pointer: determine the current position of the DMA transfer + * + * This function is called by ALSA when ALSA wants to know where in the + * stream buffer the hardware currently is. + * + * For playback, the SAR register contains the physical address of the most + * recent DMA transfer. For capture, the value is in the DAR register. + * + * The base address of the buffer is stored in the source_addr field of the + * first link descriptor. + */ +static snd_pcm_uframes_t fsl_dma_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct fsl_dma_private *dma_private = runtime->private_data; + struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel; + dma_addr_t position; + snd_pcm_uframes_t frames; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + position = in_be32(&dma_channel->sar); + else + position = in_be32(&dma_channel->dar); + + frames = bytes_to_frames(runtime, position - dma_private->dma_buf_phys); + + /* + * If the current address is just past the end of the buffer, wrap it + * around. + */ + if (frames == runtime->buffer_size) + frames = 0; + + return frames; +} + +/** + * fsl_dma_hw_free: release resources allocated in fsl_dma_hw_params() + * + * Release the resources allocated in fsl_dma_hw_params() and de-program the + * registers. + * + * This function can be called multiple times. + */ +static int fsl_dma_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct fsl_dma_private *dma_private = runtime->private_data; + + if (dma_private) { + struct ccsr_dma_channel __iomem *dma_channel; + + dma_channel = dma_private->dma_channel; + + /* Stop the DMA */ + out_be32(&dma_channel->mr, CCSR_DMA_MR_CA); + out_be32(&dma_channel->mr, 0); + + /* Reset all the other registers */ + out_be32(&dma_channel->sr, -1); + out_be32(&dma_channel->clndar, 0); + out_be32(&dma_channel->eclndar, 0); + out_be32(&dma_channel->satr, 0); + out_be32(&dma_channel->sar, 0); + out_be32(&dma_channel->datr, 0); + out_be32(&dma_channel->dar, 0); + out_be32(&dma_channel->bcr, 0); + out_be32(&dma_channel->nlndar, 0); + out_be32(&dma_channel->enlndar, 0); + } + + return 0; +} + +/** + * fsl_dma_close: close the stream. + */ +static int fsl_dma_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct fsl_dma_private *dma_private = runtime->private_data; + int dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; + + if (dma_private) { + if (dma_private->irq) + free_irq(dma_private->irq, dma_private); + + if (dma_private->ld_buf_phys) { + dma_unmap_single(substream->pcm->dev, + dma_private->ld_buf_phys, + sizeof(dma_private->link), DMA_TO_DEVICE); + } + + /* Deallocate the fsl_dma_private structure */ + dma_free_coherent(substream->pcm->dev, + sizeof(struct fsl_dma_private), + dma_private, dma_private->ld_buf_phys); + substream->runtime->private_data = NULL; + } + + dma_global_data.assigned[dir] = 0; + + return 0; +} + +/* + * Remove this PCM driver. + */ +static void fsl_dma_free_dma_buffers(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) { + substream = pcm->streams[i].substream; + if (substream) { + snd_dma_free_pages(&substream->dma_buffer); + substream->dma_buffer.area = NULL; + substream->dma_buffer.addr = 0; + } + } +} + +static struct snd_pcm_ops fsl_dma_ops = { + .open = fsl_dma_open, + .close = fsl_dma_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = fsl_dma_hw_params, + .hw_free = fsl_dma_hw_free, + .prepare = fsl_dma_prepare, + .pointer = fsl_dma_pointer, +}; + +struct snd_soc_platform fsl_soc_platform = { + .name = "fsl-dma", + .pcm_ops = &fsl_dma_ops, + .pcm_new = fsl_dma_new, + .pcm_free = fsl_dma_free_dma_buffers, +}; +EXPORT_SYMBOL_GPL(fsl_soc_platform); + +/** + * fsl_dma_configure: store the DMA parameters from the fabric driver. + * + * This function is called by the ASoC fabric driver to give us the DMA and + * SSI channel information. + * + * Unfortunately, ASoC V1 does make it possible to determine the DMA/SSI + * data when a substream is created, so for now we need to store this data + * into a global variable. This means that we can only support one DMA + * controller, and hence only one SSI. + */ +int fsl_dma_configure(struct fsl_dma_info *dma_info) +{ + static int initialized; + + /* We only support one DMA controller for now */ + if (initialized) + return 0; + + dma_global_data.ssi_stx_phys = dma_info->ssi_stx_phys; + dma_global_data.ssi_srx_phys = dma_info->ssi_srx_phys; + dma_global_data.dma_channel[0] = dma_info->dma_channel[0]; + dma_global_data.dma_channel[1] = dma_info->dma_channel[1]; + dma_global_data.irq[0] = dma_info->dma_irq[0]; + dma_global_data.irq[1] = dma_info->dma_irq[1]; + dma_global_data.assigned[0] = 0; + dma_global_data.assigned[1] = 0; + + initialized = 1; + return 1; +} +EXPORT_SYMBOL_GPL(fsl_dma_configure); + +MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); +MODULE_DESCRIPTION("Freescale Elo DMA ASoC PCM module"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/fsl_dma.h b/sound/soc/fsl/fsl_dma.h new file mode 100644 index 0000000..430a6ce --- /dev/null +++ b/sound/soc/fsl/fsl_dma.h @@ -0,0 +1,149 @@ +/* + * mpc8610-pcm.h - ALSA PCM interface for the Freescale MPC8610 SoC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _MPC8610_PCM_H +#define _MPC8610_PCM_H + +struct ccsr_dma { + u8 res0[0x100]; + struct ccsr_dma_channel { + __be32 mr; /* Mode register */ + __be32 sr; /* Status register */ + __be32 eclndar; /* Current link descriptor extended addr reg */ + __be32 clndar; /* Current link descriptor address register */ + __be32 satr; /* Source attributes register */ + __be32 sar; /* Source address register */ + __be32 datr; /* Destination attributes register */ + __be32 dar; /* Destination address register */ + __be32 bcr; /* Byte count register */ + __be32 enlndar; /* Next link descriptor extended address reg */ + __be32 nlndar; /* Next link descriptor address register */ + u8 res1[4]; + __be32 eclsdar; /* Current list descriptor extended addr reg */ + __be32 clsdar; /* Current list descriptor address register */ + __be32 enlsdar; /* Next list descriptor extended address reg */ + __be32 nlsdar; /* Next list descriptor address register */ + __be32 ssr; /* Source stride register */ + __be32 dsr; /* Destination stride register */ + u8 res2[0x38]; + } channel[4]; + __be32 dgsr; +}; + +#define CCSR_DMA_MR_BWC_DISABLED 0x0F000000 +#define CCSR_DMA_MR_BWC_SHIFT 24 +#define CCSR_DMA_MR_BWC_MASK 0x0F000000 +#define CCSR_DMA_MR_BWC(x) \ + ((ilog2(x) << CCSR_DMA_MR_BWC_SHIFT) & CCSR_DMA_MR_BWC_MASK) +#define CCSR_DMA_MR_EMP_EN 0x00200000 +#define CCSR_DMA_MR_EMS_EN 0x00040000 +#define CCSR_DMA_MR_DAHTS_MASK 0x00030000 +#define CCSR_DMA_MR_DAHTS_1 0x00000000 +#define CCSR_DMA_MR_DAHTS_2 0x00010000 +#define CCSR_DMA_MR_DAHTS_4 0x00020000 +#define CCSR_DMA_MR_DAHTS_8 0x00030000 +#define CCSR_DMA_MR_SAHTS_MASK 0x0000C000 +#define CCSR_DMA_MR_SAHTS_1 0x00000000 +#define CCSR_DMA_MR_SAHTS_2 0x00004000 +#define CCSR_DMA_MR_SAHTS_4 0x00008000 +#define CCSR_DMA_MR_SAHTS_8 0x0000C000 +#define CCSR_DMA_MR_DAHE 0x00002000 +#define CCSR_DMA_MR_SAHE 0x00001000 +#define CCSR_DMA_MR_SRW 0x00000400 +#define CCSR_DMA_MR_EOSIE 0x00000200 +#define CCSR_DMA_MR_EOLNIE 0x00000100 +#define CCSR_DMA_MR_EOLSIE 0x00000080 +#define CCSR_DMA_MR_EIE 0x00000040 +#define CCSR_DMA_MR_XFE 0x00000020 +#define CCSR_DMA_MR_CDSM_SWSM 0x00000010 +#define CCSR_DMA_MR_CA 0x00000008 +#define CCSR_DMA_MR_CTM 0x00000004 +#define CCSR_DMA_MR_CC 0x00000002 +#define CCSR_DMA_MR_CS 0x00000001 + +#define CCSR_DMA_SR_TE 0x00000080 +#define CCSR_DMA_SR_CH 0x00000020 +#define CCSR_DMA_SR_PE 0x00000010 +#define CCSR_DMA_SR_EOLNI 0x00000008 +#define CCSR_DMA_SR_CB 0x00000004 +#define CCSR_DMA_SR_EOSI 0x00000002 +#define CCSR_DMA_SR_EOLSI 0x00000001 + +/* ECLNDAR takes bits 32-36 of the CLNDAR register */ +static inline u32 CCSR_DMA_ECLNDAR_ADDR(u64 x) +{ + return (x >> 32) & 0xf; +} + +#define CCSR_DMA_CLNDAR_ADDR(x) ((x) & 0xFFFFFFFE) +#define CCSR_DMA_CLNDAR_EOSIE 0x00000008 + +/* SATR and DATR, combined */ +#define CCSR_DMA_ATR_PBATMU 0x20000000 +#define CCSR_DMA_ATR_TFLOWLVL_0 0x00000000 +#define CCSR_DMA_ATR_TFLOWLVL_1 0x06000000 +#define CCSR_DMA_ATR_TFLOWLVL_2 0x08000000 +#define CCSR_DMA_ATR_TFLOWLVL_3 0x0C000000 +#define CCSR_DMA_ATR_PCIORDER 0x02000000 +#define CCSR_DMA_ATR_SME 0x01000000 +#define CCSR_DMA_ATR_NOSNOOP 0x00040000 +#define CCSR_DMA_ATR_SNOOP 0x00050000 +#define CCSR_DMA_ATR_ESAD_MASK 0x0000000F + +/** + * List Descriptor for extended chaining mode DMA operations. + * + * The CLSDAR register points to the first (in a linked-list) List + * Descriptor. Each object must be aligned on a 32-byte boundary. Each + * list descriptor points to a linked-list of link Descriptors. + */ +struct fsl_dma_list_descriptor { + __be64 next; /* Address of next list descriptor */ + __be64 first_link; /* Address of first link descriptor */ + __be32 source; /* Source stride */ + __be32 dest; /* Destination stride */ + u8 res[8]; /* Reserved */ +} __attribute__ ((aligned(32), packed)); + +/** + * Link Descriptor for basic and extended chaining mode DMA operations. + * + * A Link Descriptor points to a single DMA buffer. Each link descriptor + * must be aligned on a 32-byte boundary. + */ +struct fsl_dma_link_descriptor { + __be32 source_attr; /* Programmed into SATR register */ + __be32 source_addr; /* Programmed into SAR register */ + __be32 dest_attr; /* Programmed into DATR register */ + __be32 dest_addr; /* Programmed into DAR register */ + __be64 next; /* Address of next link descriptor */ + __be32 count; /* Byte count */ + u8 res[4]; /* Reserved */ +} __attribute__ ((aligned(32), packed)); + +/* DMA information needed to create a snd_soc_cpu_dai object + * + * ssi_stx_phys: bus address of SSI STX register to use + * ssi_srx_phys: bus address of SSI SRX register to use + * dma[0]: points to the DMA channel to use for playback + * dma[1]: points to the DMA channel to use for capture + * dma_irq[0]: IRQ of the DMA channel to use for playback + * dma_irq[1]: IRQ of the DMA channel to use for capture + */ +struct fsl_dma_info { + dma_addr_t ssi_stx_phys; + dma_addr_t ssi_srx_phys; + struct ccsr_dma_channel __iomem *dma_channel[2]; + unsigned int dma_irq[2]; +}; + +extern struct snd_soc_platform fsl_soc_platform; + +int fsl_dma_configure(struct fsl_dma_info *dma_info); + +#endif diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c new file mode 100644 index 0000000..145ad13 --- /dev/null +++ b/sound/soc/fsl/fsl_ssi.c @@ -0,0 +1,644 @@ +/* + * Freescale SSI ALSA SoC Digital Audio Interface (DAI) driver + * + * Author: Timur Tabi <timur@freescale.com> + * + * Copyright 2007-2008 Freescale Semiconductor, Inc. This file is licensed + * under the terms of the GNU General Public License version 2. This + * program is licensed "as is" without any warranty of any kind, whether + * express or implied. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/delay.h> + +#include <sound/driver.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/soc.h> + +#include <asm/immap_86xx.h> + +#include "fsl_ssi.h" + +/** + * FSLSSI_I2S_RATES: sample rates supported by the I2S + * + * This driver currently only supports the SSI running in I2S slave mode, + * which means the codec determines the sample rate. Therefore, we tell + * ALSA that we support all rates and let the codec driver decide what rates + * are really supported. + */ +#define FSLSSI_I2S_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \ + SNDRV_PCM_RATE_CONTINUOUS) + +/** + * FSLSSI_I2S_FORMATS: audio formats supported by the SSI + * + * This driver currently only supports the SSI running in I2S slave mode. + * + * The SSI has a limitation in that the samples must be in the same byte + * order as the host CPU. This is because when multiple bytes are written + * to the STX register, the bytes and bits must be written in the same + * order. The STX is a shift register, so all the bits need to be aligned + * (bit-endianness must match byte-endianness). Processors typically write + * the bits within a byte in the same order that the bytes of a word are + * written in. So if the host CPU is big-endian, then only big-endian + * samples will be written to STX properly. + */ +#ifdef __BIG_ENDIAN +#define FSLSSI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \ + SNDRV_PCM_FMTBIT_S18_3BE | SNDRV_PCM_FMTBIT_S20_3BE | \ + SNDRV_PCM_FMTBIT_S24_3BE | SNDRV_PCM_FMTBIT_S24_BE) +#else +#define FSLSSI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE) +#endif + +/** + * fsl_ssi_private: per-SSI private data + * + * @name: short name for this device ("SSI0", "SSI1", etc) + * @ssi: pointer to the SSI's registers + * @ssi_phys: physical address of the SSI registers + * @irq: IRQ of this SSI + * @dev: struct device pointer + * @playback: the number of playback streams opened + * @capture: the number of capture streams opened + * @cpu_dai: the CPU DAI for this device + * @dev_attr: the sysfs device attribute structure + * @stats: SSI statistics + */ +struct fsl_ssi_private { + char name[8]; + struct ccsr_ssi __iomem *ssi; + dma_addr_t ssi_phys; + unsigned int irq; + struct device *dev; + unsigned int playback; + unsigned int capture; + struct snd_soc_cpu_dai cpu_dai; + struct device_attribute dev_attr; + + struct { + unsigned int rfrc; + unsigned int tfrc; + unsigned int cmdau; + unsigned int cmddu; + unsigned int rxt; + unsigned int rdr1; + unsigned int rdr0; + unsigned int tde1; + unsigned int tde0; + unsigned int roe1; + unsigned int roe0; + unsigned int tue1; + unsigned int tue0; + unsigned int tfs; + unsigned int rfs; + unsigned int tls; + unsigned int rls; + unsigned int rff1; + unsigned int rff0; + unsigned int tfe1; + unsigned int tfe0; + } stats; +}; + +/** + * fsl_ssi_isr: SSI interrupt handler + * + * Although it's possible to use the interrupt handler to send and receive + * data to/from the SSI, we use the DMA instead. Programming is more + * complicated, but the performance is much better. + * + * This interrupt handler is used only to gather statistics. + * + * @irq: IRQ of the SSI device + * @dev_id: pointer to the ssi_private structure for this SSI device + */ +static irqreturn_t fsl_ssi_isr(int irq, void *dev_id) +{ + struct fsl_ssi_private *ssi_private = dev_id; + struct ccsr_ssi __iomem *ssi = ssi_private->ssi; + irqreturn_t ret = IRQ_NONE; + __be32 sisr; + __be32 sisr2 = 0; + + /* We got an interrupt, so read the status register to see what we + were interrupted for. We mask it with the Interrupt Enable register + so that we only check for events that we're interested in. + */ + sisr = in_be32(&ssi->sisr) & in_be32(&ssi->sier); + + if (sisr & CCSR_SSI_SISR_RFRC) { + ssi_private->stats.rfrc++; + sisr2 |= CCSR_SSI_SISR_RFRC; + ret = IRQ_HANDLED; + } + + if (sisr & CCSR_SSI_SISR_TFRC) { + ssi_private->stats.tfrc++; + sisr2 |= CCSR_SSI_SISR_TFRC; + ret = IRQ_HANDLED; + } + + if (sisr & CCSR_SSI_SISR_CMDAU) { + ssi_private->stats.cmdau++; + ret = IRQ_HANDLED; + } + + if (sisr & CCSR_SSI_SISR_CMDDU) { + ssi_private->stats.cmddu++; + ret = IRQ_HANDLED; + } + + if (sisr & CCSR_SSI_SISR_RXT) { + ssi_private->stats.rxt++; + ret = IRQ_HANDLED; + } + + if (sisr & CCSR_SSI_SISR_RDR1) { + ssi_private->stats.rdr1++; + ret = IRQ_HANDLED; + } + + if (sisr & CCSR_SSI_SISR_RDR0) { + ssi_private->stats.rdr0++; + ret = IRQ_HANDLED; + } + + if (sisr & CCSR_SSI_SISR_TDE1) { + ssi_private->stats.tde1++; + ret = IRQ_HANDLED; + } + + if (sisr & CCSR_SSI_SISR_TDE0) { + ssi_private->stats.tde0++; + ret = IRQ_HANDLED; + } + + if (sisr & CCSR_SSI_SISR_ROE1) { + ssi_private->stats.roe1++; + sisr2 |= CCSR_SSI_SISR_ROE1; + ret = IRQ_HANDLED; + } + + if (sisr & CCSR_SSI_SISR_ROE0) { + ssi_private->stats.roe0++; + sisr2 |= CCSR_SSI_SISR_ROE0; + ret = IRQ_HANDLED; + } + + if (sisr & CCSR_SSI_SISR_TUE1) { + ssi_private->stats.tue1++; + sisr2 |= CCSR_SSI_SISR_TUE1; + ret = IRQ_HANDLED; + } + + if (sisr & CCSR_SSI_SISR_TUE0) { + ssi_private->stats.tue0++; + sisr2 |= CCSR_SSI_SISR_TUE0; + ret = IRQ_HANDLED; + } + + if (sisr & CCSR_SSI_SISR_TFS) { + ssi_private->stats.tfs++; + ret = IRQ_HANDLED; + } + + if (sisr & CCSR_SSI_SISR_RFS) { + ssi_private->stats.rfs++; + ret = IRQ_HANDLED; + } + + if (sisr & CCSR_SSI_SISR_TLS) { + ssi_private->stats.tls++; + ret = IRQ_HANDLED; + } + + if (sisr & CCSR_SSI_SISR_RLS) { + ssi_private->stats.rls++; + ret = IRQ_HANDLED; + } + + if (sisr & CCSR_SSI_SISR_RFF1) { + ssi_private->stats.rff1++; + ret = IRQ_HANDLED; + } + + if (sisr & CCSR_SSI_SISR_RFF0) { + ssi_private->stats.rff0++; + ret = IRQ_HANDLED; + } + + if (sisr & CCSR_SSI_SISR_TFE1) { + ssi_private->stats.tfe1++; + ret = IRQ_HANDLED; + } + + if (sisr & CCSR_SSI_SISR_TFE0) { + ssi_private->stats.tfe0++; + ret = IRQ_HANDLED; + } + + /* Clear the bits that we set */ + if (sisr2) + out_be32(&ssi->sisr, sisr2); + + return ret; +} + +/** + * fsl_ssi_startup: create a new substream + * + * This is the first function called when a stream is opened. + * + * If this is the first stream open, then grab the IRQ and program most of + * the SSI registers. + */ +static int fsl_ssi_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data; + + /* + * If this is the first stream opened, then request the IRQ + * and initialize the SSI registers. + */ + if (!ssi_private->playback && !ssi_private->capture) { + struct ccsr_ssi __iomem *ssi = ssi_private->ssi; + int ret; + + ret = request_irq(ssi_private->irq, fsl_ssi_isr, 0, + ssi_private->name, ssi_private); + if (ret < 0) { + dev_err(substream->pcm->card->dev, + "could not claim irq %u\n", ssi_private->irq); + return ret; + } + + /* + * Section 16.5 of the MPC8610 reference manual says that the + * SSI needs to be disabled before updating the registers we set + * here. + */ + clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN); + + /* + * Program the SSI into I2S Slave Non-Network Synchronous mode. + * Also enable the transmit and receive FIFO. + * + * FIXME: Little-endian samples require a different shift dir + */ + clrsetbits_be32(&ssi->scr, CCSR_SSI_SCR_I2S_MODE_MASK, + CCSR_SSI_SCR_TFR_CLK_DIS | + CCSR_SSI_SCR_I2S_MODE_SLAVE | CCSR_SSI_SCR_SYN); + + out_be32(&ssi->stcr, + CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 | + CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS | + CCSR_SSI_STCR_TSCKP); + + out_be32(&ssi->srcr, + CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 | + CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS | + CCSR_SSI_SRCR_RSCKP); + + /* + * The DC and PM bits are only used if the SSI is the clock + * master. + */ + + /* 4. Enable the interrupts and DMA requests */ + out_be32(&ssi->sier, + CCSR_SSI_SIER_TFRC_EN | CCSR_SSI_SIER_TDMAE | + CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TUE0_EN | + CCSR_SSI_SIER_TUE1_EN | CCSR_SSI_SIER_RFRC_EN | + CCSR_SSI_SIER_RDMAE | CCSR_SSI_SIER_RIE | + CCSR_SSI_SIER_ROE0_EN | CCSR_SSI_SIER_ROE1_EN); + + /* + * Set the watermark for transmit FIFI 0 and receive FIFO 0. We + * don't use FIFO 1. Since the SSI only supports stereo, the + * watermark should never be an odd number. + */ + out_be32(&ssi->sfcsr, + CCSR_SSI_SFCSR_TFWM0(6) | CCSR_SSI_SFCSR_RFWM0(2)); + + /* + * We keep the SSI disabled because if we enable it, then the + * DMA controller will start. It's not supposed to start until + * the SCR.TE (or SCR.RE) bit is set, but it does anyway. The + * DMA controller will transfer one "BWC" of data (i.e. the + * amount of data that the MR.BWC bits are set to). The reason + * this is bad is because at this point, the PCM driver has not + * finished initializing the DMA controller. + */ + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ssi_private->playback++; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ssi_private->capture++; + + return 0; +} + +/** + * fsl_ssi_prepare: prepare the SSI. + * + * Most of the SSI registers have been programmed in the startup function, + * but the word length must be programmed here. Unfortunately, programming + * the SxCCR.WL bits requires the SSI to be temporarily disabled. This can + * cause a problem with supporting simultaneous playback and capture. If + * the SSI is already playing a stream, then that stream may be temporarily + * stopped when you start capture. + * + * Note: The SxCCR.DC and SxCCR.PM bits are only used if the SSI is the + * clock master. + */ +static int fsl_ssi_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data; + + struct ccsr_ssi __iomem *ssi = ssi_private->ssi; + u32 wl; + + wl = CCSR_SSI_SxCCR_WL(snd_pcm_format_width(runtime->format)); + + clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + clrsetbits_be32(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl); + else + clrsetbits_be32(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl); + + setbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN); + + return 0; +} + +/** + * fsl_ssi_trigger: start and stop the DMA transfer. + * + * This function is called by ALSA to start, stop, pause, and resume the DMA + * transfer of data. + * + * The DMA channel is in external master start and pause mode, which + * means the SSI completely controls the flow of data. + */ +static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data; + struct ccsr_ssi __iomem *ssi = ssi_private->ssi; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + setbits32(&ssi->scr, CCSR_SSI_SCR_TE); + } else { + setbits32(&ssi->scr, CCSR_SSI_SCR_RE); + + /* + * I think we need this delay to allow time for the SSI + * to put data into its FIFO. Without it, ALSA starts + * to complain about overruns. + */ + msleep(1); + } + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + clrbits32(&ssi->scr, CCSR_SSI_SCR_TE); + else + clrbits32(&ssi->scr, CCSR_SSI_SCR_RE); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/** + * fsl_ssi_shutdown: shutdown the SSI + * + * Shutdown the SSI if there are no other substreams open. + */ +static void fsl_ssi_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ssi_private->playback--; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ssi_private->capture--; + + /* + * If this is the last active substream, disable the SSI and release + * the IRQ. + */ + if (!ssi_private->playback && !ssi_private->capture) { + struct ccsr_ssi __iomem *ssi = ssi_private->ssi; + + clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN); + + free_irq(ssi_private->irq, ssi_private); + } +} + +/** + * fsl_ssi_set_sysclk: set the clock frequency and direction + * + * This function is called by the machine driver to tell us what the clock + * frequency and direction are. + * + * Currently, we only support operating as a clock slave (SND_SOC_CLOCK_IN), + * and we don't care about the frequency. Return an error if the direction + * is not SND_SOC_CLOCK_IN. + * + * @clk_id: reserved, should be zero + * @freq: the frequency of the given clock ID, currently ignored + * @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock master) + */ +static int fsl_ssi_set_sysclk(struct snd_soc_cpu_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + + return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL; +} + +/** + * fsl_ssi_set_fmt: set the serial format. + * + * This function is called by the machine driver to tell us what serial + * format to use. + * + * Currently, we only support I2S mode. Return an error if the format is + * not SND_SOC_DAIFMT_I2S. + * + * @format: one of SND_SOC_DAIFMT_xxx + */ +static int fsl_ssi_set_fmt(struct snd_soc_cpu_dai *cpu_dai, unsigned int format) +{ + return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL; +} + +/** + * fsl_ssi_dai_template: template CPU DAI for the SSI + */ +static struct snd_soc_cpu_dai fsl_ssi_dai_template = { + .playback = { + /* The SSI does not support monaural audio. */ + .channels_min = 2, + .channels_max = 2, + .rates = FSLSSI_I2S_RATES, + .formats = FSLSSI_I2S_FORMATS, + }, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = FSLSSI_I2S_RATES, + .formats = FSLSSI_I2S_FORMATS, + }, + .ops = { + .startup = fsl_ssi_startup, + .prepare = fsl_ssi_prepare, + .shutdown = fsl_ssi_shutdown, + .trigger = fsl_ssi_trigger, + }, + .dai_ops = { + .set_sysclk = fsl_ssi_set_sysclk, + .set_fmt = fsl_ssi_set_fmt, + }, +}; + +/** + * fsl_sysfs_ssi_show: display SSI statistics + * + * Display the statistics for the current SSI device. + */ +static ssize_t fsl_sysfs_ssi_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fsl_ssi_private *ssi_private = + container_of(attr, struct fsl_ssi_private, dev_attr); + ssize_t length; + + length = sprintf(buf, "rfrc=%u", ssi_private->stats.rfrc); + length += sprintf(buf + length, "\ttfrc=%u", ssi_private->stats.tfrc); + length += sprintf(buf + length, "\tcmdau=%u", ssi_private->stats.cmdau); + length += sprintf(buf + length, "\tcmddu=%u", ssi_private->stats.cmddu); + length += sprintf(buf + length, "\trxt=%u", ssi_private->stats.rxt); + length += sprintf(buf + length, "\trdr1=%u", ssi_private->stats.rdr1); + length += sprintf(buf + length, "\trdr0=%u", ssi_private->stats.rdr0); + length += sprintf(buf + length, "\ttde1=%u", ssi_private->stats.tde1); + length += sprintf(buf + length, "\ttde0=%u", ssi_private->stats.tde0); + length += sprintf(buf + length, "\troe1=%u", ssi_private->stats.roe1); + length += sprintf(buf + length, "\troe0=%u", ssi_private->stats.roe0); + length += sprintf(buf + length, "\ttue1=%u", ssi_private->stats.tue1); + length += sprintf(buf + length, "\ttue0=%u", ssi_private->stats.tue0); + length += sprintf(buf + length, "\ttfs=%u", ssi_private->stats.tfs); + length += sprintf(buf + length, "\trfs=%u", ssi_private->stats.rfs); + length += sprintf(buf + length, "\ttls=%u", ssi_private->stats.tls); + length += sprintf(buf + length, "\trls=%u", ssi_private->stats.rls); + length += sprintf(buf + length, "\trff1=%u", ssi_private->stats.rff1); + length += sprintf(buf + length, "\trff0=%u", ssi_private->stats.rff0); + length += sprintf(buf + length, "\ttfe1=%u", ssi_private->stats.tfe1); + length += sprintf(buf + length, "\ttfe0=%u\n", ssi_private->stats.tfe0); + + return length; +} + +/** + * fsl_ssi_create_dai: create a snd_soc_cpu_dai structure + * + * This function is called by the machine driver to create a snd_soc_cpu_dai + * structure. The function creates an ssi_private object, which contains + * the snd_soc_cpu_dai. It also creates the sysfs statistics device. + */ +struct snd_soc_cpu_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info) +{ + struct snd_soc_cpu_dai *fsl_ssi_dai; + struct fsl_ssi_private *ssi_private; + int ret = 0; + struct device_attribute *dev_attr; + + ssi_private = kzalloc(sizeof(struct fsl_ssi_private), GFP_KERNEL); + if (!ssi_private) { + dev_err(ssi_info->dev, "could not allocate DAI object\n"); + return NULL; + } + memcpy(&ssi_private->cpu_dai, &fsl_ssi_dai_template, + sizeof(struct snd_soc_cpu_dai)); + + fsl_ssi_dai = &ssi_private->cpu_dai; + dev_attr = &ssi_private->dev_attr; + + sprintf(ssi_private->name, "ssi%u", (u8) ssi_info->id); + ssi_private->ssi = ssi_info->ssi; + ssi_private->ssi_phys = ssi_info->ssi_phys; + ssi_private->irq = ssi_info->irq; + ssi_private->dev = ssi_info->dev; + + ssi_private->dev->driver_data = fsl_ssi_dai; + + /* Initialize the the device_attribute structure */ + dev_attr->attr.name = "ssi-stats"; + dev_attr->attr.mode = S_IRUGO; + dev_attr->show = fsl_sysfs_ssi_show; + + ret = device_create_file(ssi_private->dev, dev_attr); + if (ret) { + dev_err(ssi_info->dev, "could not create sysfs %s file\n", + ssi_private->dev_attr.attr.name); + kfree(fsl_ssi_dai); + return NULL; + } + + fsl_ssi_dai->private_data = ssi_private; + fsl_ssi_dai->name = ssi_private->name; + fsl_ssi_dai->id = ssi_info->id; + + return fsl_ssi_dai; +} +EXPORT_SYMBOL_GPL(fsl_ssi_create_dai); + +/** + * fsl_ssi_destroy_dai: destroy the snd_soc_cpu_dai object + * + * This function undoes the operations of fsl_ssi_create_dai() + */ +void fsl_ssi_destroy_dai(struct snd_soc_cpu_dai *fsl_ssi_dai) +{ + struct fsl_ssi_private *ssi_private = + container_of(fsl_ssi_dai, struct fsl_ssi_private, cpu_dai); + + device_remove_file(ssi_private->dev, &ssi_private->dev_attr); + + kfree(ssi_private); +} +EXPORT_SYMBOL_GPL(fsl_ssi_destroy_dai); + +MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); +MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/fsl_ssi.h b/sound/soc/fsl/fsl_ssi.h new file mode 100644 index 0000000..c5ce88e --- /dev/null +++ b/sound/soc/fsl/fsl_ssi.h @@ -0,0 +1,224 @@ +/* + * fsl_ssi.h - ALSA SSI interface for the Freescale MPC8610 SoC + * + * Author: Timur Tabi <timur@freescale.com> + * + * Copyright 2007-2008 Freescale Semiconductor, Inc. This file is licensed + * under the terms of the GNU General Public License version 2. This + * program is licensed "as is" without any warranty of any kind, whether + * express or implied. + */ + +#ifndef _MPC8610_I2S_H +#define _MPC8610_I2S_H + +/* SSI Register Map */ +struct ccsr_ssi { + __be32 stx0; /* 0x.0000 - SSI Transmit Data Register 0 */ + __be32 stx1; /* 0x.0004 - SSI Transmit Data Register 1 */ + __be32 srx0; /* 0x.0008 - SSI Receive Data Register 0 */ + __be32 srx1; /* 0x.000C - SSI Receive Data Register 1 */ + __be32 scr; /* 0x.0010 - SSI Control Register */ + __be32 sisr; /* 0x.0014 - SSI Interrupt Status Register Mixed */ + __be32 sier; /* 0x.0018 - SSI Interrupt Enable Register */ + __be32 stcr; /* 0x.001C - SSI Transmit Configuration Register */ + __be32 srcr; /* 0x.0020 - SSI Receive Configuration Register */ + __be32 stccr; /* 0x.0024 - SSI Transmit Clock Control Register */ + __be32 srccr; /* 0x.0028 - SSI Receive Clock Control Register */ + __be32 sfcsr; /* 0x.002C - SSI FIFO Control/Status Register */ + __be32 str; /* 0x.0030 - SSI Test Register */ + __be32 sor; /* 0x.0034 - SSI Option Register */ + __be32 sacnt; /* 0x.0038 - SSI AC97 Control Register */ + __be32 sacadd; /* 0x.003C - SSI AC97 Command Address Register */ + __be32 sacdat; /* 0x.0040 - SSI AC97 Command Data Register */ + __be32 satag; /* 0x.0044 - SSI AC97 Tag Register */ + __be32 stmsk; /* 0x.0048 - SSI Transmit Time Slot Mask Register */ + __be32 srmsk; /* 0x.004C - SSI Receive Time Slot Mask Register */ + __be32 saccst; /* 0x.0050 - SSI AC97 Channel Status Register */ + __be32 saccen; /* 0x.0054 - SSI AC97 Channel Enable Register */ + __be32 saccdis; /* 0x.0058 - SSI AC97 Channel Disable Register */ +}; + +#define CCSR_SSI_SCR_RFR_CLK_DIS 0x00000800 +#define CCSR_SSI_SCR_TFR_CLK_DIS 0x00000400 +#define CCSR_SSI_SCR_TCH_EN 0x00000100 +#define CCSR_SSI_SCR_SYS_CLK_EN 0x00000080 +#define CCSR_SSI_SCR_I2S_MODE_MASK 0x00000060 +#define CCSR_SSI_SCR_I2S_MODE_NORMAL 0x00000000 +#define CCSR_SSI_SCR_I2S_MODE_MASTER 0x00000020 +#define CCSR_SSI_SCR_I2S_MODE_SLAVE 0x00000040 +#define CCSR_SSI_SCR_SYN 0x00000010 +#define CCSR_SSI_SCR_NET 0x00000008 +#define CCSR_SSI_SCR_RE 0x00000004 +#define CCSR_SSI_SCR_TE 0x00000002 +#define CCSR_SSI_SCR_SSIEN 0x00000001 + +#define CCSR_SSI_SISR_RFRC 0x01000000 +#define CCSR_SSI_SISR_TFRC 0x00800000 +#define CCSR_SSI_SISR_CMDAU 0x00040000 +#define CCSR_SSI_SISR_CMDDU 0x00020000 +#define CCSR_SSI_SISR_RXT 0x00010000 +#define CCSR_SSI_SISR_RDR1 0x00008000 +#define CCSR_SSI_SISR_RDR0 0x00004000 +#define CCSR_SSI_SISR_TDE1 0x00002000 +#define CCSR_SSI_SISR_TDE0 0x00001000 +#define CCSR_SSI_SISR_ROE1 0x00000800 +#define CCSR_SSI_SISR_ROE0 0x00000400 +#define CCSR_SSI_SISR_TUE1 0x00000200 +#define CCSR_SSI_SISR_TUE0 0x00000100 +#define CCSR_SSI_SISR_TFS 0x00000080 +#define CCSR_SSI_SISR_RFS 0x00000040 +#define CCSR_SSI_SISR_TLS 0x00000020 +#define CCSR_SSI_SISR_RLS 0x00000010 +#define CCSR_SSI_SISR_RFF1 0x00000008 +#define CCSR_SSI_SISR_RFF0 0x00000004 +#define CCSR_SSI_SISR_TFE1 0x00000002 +#define CCSR_SSI_SISR_TFE0 0x00000001 + +#define CCSR_SSI_SIER_RFRC_EN 0x01000000 +#define CCSR_SSI_SIER_TFRC_EN 0x00800000 +#define CCSR_SSI_SIER_RDMAE 0x00400000 +#define CCSR_SSI_SIER_RIE 0x00200000 +#define CCSR_SSI_SIER_TDMAE 0x00100000 +#define CCSR_SSI_SIER_TIE 0x00080000 +#define CCSR_SSI_SIER_CMDAU_EN 0x00040000 +#define CCSR_SSI_SIER_CMDDU_EN 0x00020000 +#define CCSR_SSI_SIER_RXT_EN 0x00010000 +#define CCSR_SSI_SIER_RDR1_EN 0x00008000 +#define CCSR_SSI_SIER_RDR0_EN 0x00004000 +#define CCSR_SSI_SIER_TDE1_EN 0x00002000 +#define CCSR_SSI_SIER_TDE0_EN 0x00001000 +#define CCSR_SSI_SIER_ROE1_EN 0x00000800 +#define CCSR_SSI_SIER_ROE0_EN 0x00000400 +#define CCSR_SSI_SIER_TUE1_EN 0x00000200 +#define CCSR_SSI_SIER_TUE0_EN 0x00000100 +#define CCSR_SSI_SIER_TFS_EN 0x00000080 +#define CCSR_SSI_SIER_RFS_EN 0x00000040 +#define CCSR_SSI_SIER_TLS_EN 0x00000020 +#define CCSR_SSI_SIER_RLS_EN 0x00000010 +#define CCSR_SSI_SIER_RFF1_EN 0x00000008 +#define CCSR_SSI_SIER_RFF0_EN 0x00000004 +#define CCSR_SSI_SIER_TFE1_EN 0x00000002 +#define CCSR_SSI_SIER_TFE0_EN 0x00000001 + +#define CCSR_SSI_STCR_TXBIT0 0x00000200 +#define CCSR_SSI_STCR_TFEN1 0x00000100 +#define CCSR_SSI_STCR_TFEN0 0x00000080 +#define CCSR_SSI_STCR_TFDIR 0x00000040 +#define CCSR_SSI_STCR_TXDIR 0x00000020 +#define CCSR_SSI_STCR_TSHFD 0x00000010 +#define CCSR_SSI_STCR_TSCKP 0x00000008 +#define CCSR_SSI_STCR_TFSI 0x00000004 +#define CCSR_SSI_STCR_TFSL 0x00000002 +#define CCSR_SSI_STCR_TEFS 0x00000001 + +#define CCSR_SSI_SRCR_RXEXT 0x00000400 +#define CCSR_SSI_SRCR_RXBIT0 0x00000200 +#define CCSR_SSI_SRCR_RFEN1 0x00000100 +#define CCSR_SSI_SRCR_RFEN0 0x00000080 +#define CCSR_SSI_SRCR_RFDIR 0x00000040 +#define CCSR_SSI_SRCR_RXDIR 0x00000020 +#define CCSR_SSI_SRCR_RSHFD 0x00000010 +#define CCSR_SSI_SRCR_RSCKP 0x00000008 +#define CCSR_SSI_SRCR_RFSI 0x00000004 +#define CCSR_SSI_SRCR_RFSL 0x00000002 +#define CCSR_SSI_SRCR_REFS 0x00000001 + +/* STCCR and SRCCR */ +#define CCSR_SSI_SxCCR_DIV2 0x00040000 +#define CCSR_SSI_SxCCR_PSR 0x00020000 +#define CCSR_SSI_SxCCR_WL_SHIFT 13 +#define CCSR_SSI_SxCCR_WL_MASK 0x0001E000 +#define CCSR_SSI_SxCCR_WL(x) \ + (((((x) / 2) - 1) << CCSR_SSI_SxCCR_WL_SHIFT) & CCSR_SSI_SxCCR_WL_MASK) +#define CCSR_SSI_SxCCR_DC_SHIFT 8 +#define CCSR_SSI_SxCCR_DC_MASK 0x00001F00 +#define CCSR_SSI_SxCCR_DC(x) \ + ((((x) - 1) << CCSR_SSI_SxCCR_DC_SHIFT) & CCSR_SSI_SxCCR_DC_MASK) +#define CCSR_SSI_SxCCR_PM_SHIFT 0 +#define CCSR_SSI_SxCCR_PM_MASK 0x000000FF +#define CCSR_SSI_SxCCR_PM(x) \ + ((((x) - 1) << CCSR_SSI_SxCCR_PM_SHIFT) & CCSR_SSI_SxCCR_PM_MASK) + +/* + * The xFCNT bits are read-only, and the xFWM bits are read/write. Use the + * CCSR_SSI_SFCSR_xFCNTy() macros to read the FIFO counters, and use the + * CCSR_SSI_SFCSR_xFWMy() macros to set the watermarks. + */ +#define CCSR_SSI_SFCSR_RFCNT1_SHIFT 28 +#define CCSR_SSI_SFCSR_RFCNT1_MASK 0xF0000000 +#define CCSR_SSI_SFCSR_RFCNT1(x) \ + (((x) & CCSR_SSI_SFCSR_RFCNT1_MASK) >> CCSR_SSI_SFCSR_RFCNT1_SHIFT) +#define CCSR_SSI_SFCSR_TFCNT1_SHIFT 24 +#define CCSR_SSI_SFCSR_TFCNT1_MASK 0x0F000000 +#define CCSR_SSI_SFCSR_TFCNT1(x) \ + (((x) & CCSR_SSI_SFCSR_TFCNT1_MASK) >> CCSR_SSI_SFCSR_TFCNT1_SHIFT) +#define CCSR_SSI_SFCSR_RFWM1_SHIFT 20 +#define CCSR_SSI_SFCSR_RFWM1_MASK 0x00F00000 +#define CCSR_SSI_SFCSR_RFWM1(x) \ + (((x) << CCSR_SSI_SFCSR_RFWM1_SHIFT) & CCSR_SSI_SFCSR_RFWM1_MASK) +#define CCSR_SSI_SFCSR_TFWM1_SHIFT 16 +#define CCSR_SSI_SFCSR_TFWM1_MASK 0x000F0000 +#define CCSR_SSI_SFCSR_TFWM1(x) \ + (((x) << CCSR_SSI_SFCSR_TFWM1_SHIFT) & CCSR_SSI_SFCSR_TFWM1_MASK) +#define CCSR_SSI_SFCSR_RFCNT0_SHIFT 12 +#define CCSR_SSI_SFCSR_RFCNT0_MASK 0x0000F000 +#define CCSR_SSI_SFCSR_RFCNT0(x) \ + (((x) & CCSR_SSI_SFCSR_RFCNT0_MASK) >> CCSR_SSI_SFCSR_RFCNT0_SHIFT) +#define CCSR_SSI_SFCSR_TFCNT0_SHIFT 8 +#define CCSR_SSI_SFCSR_TFCNT0_MASK 0x00000F00 +#define CCSR_SSI_SFCSR_TFCNT0(x) \ + (((x) & CCSR_SSI_SFCSR_TFCNT0_MASK) >> CCSR_SSI_SFCSR_TFCNT0_SHIFT) +#define CCSR_SSI_SFCSR_RFWM0_SHIFT 4 +#define CCSR_SSI_SFCSR_RFWM0_MASK 0x000000F0 +#define CCSR_SSI_SFCSR_RFWM0(x) \ + (((x) << CCSR_SSI_SFCSR_RFWM0_SHIFT) & CCSR_SSI_SFCSR_RFWM0_MASK) +#define CCSR_SSI_SFCSR_TFWM0_SHIFT 0 +#define CCSR_SSI_SFCSR_TFWM0_MASK 0x0000000F +#define CCSR_SSI_SFCSR_TFWM0(x) \ + (((x) << CCSR_SSI_SFCSR_TFWM0_SHIFT) & CCSR_SSI_SFCSR_TFWM0_MASK) + +#define CCSR_SSI_STR_TEST 0x00008000 +#define CCSR_SSI_STR_RCK2TCK 0x00004000 +#define CCSR_SSI_STR_RFS2TFS 0x00002000 +#define CCSR_SSI_STR_RXSTATE(x) (((x) >> 8) & 0x1F) +#define CCSR_SSI_STR_TXD2RXD 0x00000080 +#define CCSR_SSI_STR_TCK2RCK 0x00000040 +#define CCSR_SSI_STR_TFS2RFS 0x00000020 +#define CCSR_SSI_STR_TXSTATE(x) ((x) & 0x1F) + +#define CCSR_SSI_SOR_CLKOFF 0x00000040 +#define CCSR_SSI_SOR_RX_CLR 0x00000020 +#define CCSR_SSI_SOR_TX_CLR 0x00000010 +#define CCSR_SSI_SOR_INIT 0x00000008 +#define CCSR_SSI_SOR_WAIT_SHIFT 1 +#define CCSR_SSI_SOR_WAIT_MASK 0x00000006 +#define CCSR_SSI_SOR_WAIT(x) (((x) & 3) << CCSR_SSI_SOR_WAIT_SHIFT) +#define CCSR_SSI_SOR_SYNRST 0x00000001 + +/* Instantiation data for an SSI interface + * + * This structure contains all the information that the the SSI driver needs + * to instantiate an SSI interface with ALSA. The machine driver should + * create this structure, fill it in, call fsl_ssi_create_dai(), and then + * delete the structure. + * + * id: which SSI this is (0, 1, etc. ) + * ssi: pointer to the SSI's registers + * ssi_phys: physical address of the SSI registers + * irq: IRQ of this SSI + * dev: struct device, used to create the sysfs statistics file +*/ +struct fsl_ssi_info { + unsigned int id; + struct ccsr_ssi __iomem *ssi; + dma_addr_t ssi_phys; + unsigned int irq; + struct device *dev; +}; + +struct snd_soc_cpu_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info); +void fsl_ssi_destroy_dai(struct snd_soc_cpu_dai *fsl_ssi_dai); + +#endif + diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c new file mode 100644 index 0000000..f26c4b2 --- /dev/null +++ b/sound/soc/fsl/mpc8610_hpcd.c @@ -0,0 +1,631 @@ +/** + * Freescale MPC8610HPCD ALSA SoC Fabric driver + * + * Author: Timur Tabi <timur@freescale.com> + * + * Copyright 2007-2008 Freescale Semiconductor, Inc. This file is licensed + * under the terms of the GNU General Public License version 2. This + * program is licensed "as is" without any warranty of any kind, whether + * express or implied. + */ + +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/of_device.h> +#include <linux/of_platform.h> +#include <sound/soc.h> +#include <asm/immap_86xx.h> + +#include "../codecs/cs4270.h" +#include "fsl_dma.h" +#include "fsl_ssi.h" + +/** + * mpc8610_hpcd_data: fabric-specific ASoC device data + * + * This structure contains data for a single sound platform device on an + * MPC8610 HPCD. Some of the data is taken from the device tree. + */ +struct mpc8610_hpcd_data { + struct snd_soc_device sound_devdata; + struct snd_soc_dai_link dai; + struct snd_soc_machine machine; + unsigned int dai_format; + unsigned int codec_clk_direction; + unsigned int cpu_clk_direction; + unsigned int clk_frequency; + struct ccsr_guts __iomem *guts; + struct ccsr_ssi __iomem *ssi; + unsigned int ssi_id; /* 0 = SSI1, 1 = SSI2, etc */ + unsigned int ssi_irq; + unsigned int dma_id; /* 0 = DMA1, 1 = DMA2, etc */ + unsigned int dma_irq[2]; + struct ccsr_dma_channel __iomem *dma[2]; + unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/ +}; + +/** + * mpc8610_hpcd_machine_probe: initalize the board + * + * This function is called when platform_device_add() is called. It is used + * to initialize the board-specific hardware. + * + * Here we program the DMACR and PMUXCR registers. + */ +static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device) +{ + struct mpc8610_hpcd_data *machine_data = + sound_device->dev.platform_data; + + /* Program the signal routing between the SSI and the DMA */ + guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1, + machine_data->dma_channel_id[0], CCSR_GUTS_DMACR_DEV_SSI); + guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1, + machine_data->dma_channel_id[1], CCSR_GUTS_DMACR_DEV_SSI); + + guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id, + machine_data->dma_channel_id[0], 0); + guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id, + machine_data->dma_channel_id[1], 0); + + guts_set_pmuxcr_dma(machine_data->guts, 1, 0, 0); + guts_set_pmuxcr_dma(machine_data->guts, 1, 3, 0); + guts_set_pmuxcr_dma(machine_data->guts, 0, 3, 0); + + switch (machine_data->ssi_id) { + case 0: + clrsetbits_be32(&machine_data->guts->pmuxcr, + CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_SSI); + break; + case 1: + clrsetbits_be32(&machine_data->guts->pmuxcr, + CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI2_SSI); + break; + } + + return 0; +} + +/** + * mpc8610_hpcd_startup: program the board with various hardware parameters + * + * This function takes board-specific information, like clock frequencies + * and serial data formats, and passes that information to the codec and + * transport drivers. + */ +static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; + struct mpc8610_hpcd_data *machine_data = + rtd->socdev->dev->platform_data; + int ret = 0; + + /* Tell the CPU driver what the serial protocol is. */ + if (cpu_dai->dai_ops.set_fmt) { + ret = cpu_dai->dai_ops.set_fmt(cpu_dai, + machine_data->dai_format); + if (ret < 0) { + dev_err(substream->pcm->card->dev, + "could not set CPU driver audio format\n"); + return ret; + } + } + + /* Tell the codec driver what the serial protocol is. */ + if (codec_dai->dai_ops.set_fmt) { + ret = codec_dai->dai_ops.set_fmt(codec_dai, + machine_data->dai_format); + if (ret < 0) { + dev_err(substream->pcm->card->dev, + "could not set codec driver audio format\n"); + return ret; + } + } + + /* + * Tell the CPU driver what the clock frequency is, and whether it's a + * slave or master. + */ + if (cpu_dai->dai_ops.set_sysclk) { + ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, 0, + machine_data->clk_frequency, + machine_data->cpu_clk_direction); + if (ret < 0) { + dev_err(substream->pcm->card->dev, + "could not set CPU driver clock parameters\n"); + return ret; + } + } + + /* + * Tell the codec driver what the MCLK frequency is, and whether it's + * a slave or master. + */ + if (codec_dai->dai_ops.set_sysclk) { + ret = codec_dai->dai_ops.set_sysclk(codec_dai, 0, + machine_data->clk_frequency, + machine_data->codec_clk_direction); + if (ret < 0) { + dev_err(substream->pcm->card->dev, + "could not set codec driver clock params\n"); + return ret; + } + } + + return 0; +} + +/** + * mpc8610_hpcd_machine_remove: Remove the sound device + * + * This function is called to remove the sound device for one SSI. We + * de-program the DMACR and PMUXCR register. + */ +int mpc8610_hpcd_machine_remove(struct platform_device *sound_device) +{ + struct mpc8610_hpcd_data *machine_data = + sound_device->dev.platform_data; + + /* Restore the signal routing */ + + guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1, + machine_data->dma_channel_id[0], 0); + guts_set_dmacr(machine_data->guts, machine_data->dma_id + 1, + machine_data->dma_channel_id[1], 0); + + switch (machine_data->ssi_id) { + case 0: + clrsetbits_be32(&machine_data->guts->pmuxcr, + CCSR_GUTS_PMUXCR_SSI1_MASK, CCSR_GUTS_PMUXCR_SSI1_LA); + break; + case 1: + clrsetbits_be32(&machine_data->guts->pmuxcr, + CCSR_GUTS_PMUXCR_SSI2_MASK, CCSR_GUTS_PMUXCR_SSI1_LA); + break; + } + + return 0; +} + +/** + * mpc8610_hpcd_ops: ASoC fabric driver operations + */ +static struct snd_soc_ops mpc8610_hpcd_ops = { + .startup = mpc8610_hpcd_startup, +}; + +/** + * mpc8610_hpcd_machine: ASoC machine data + */ +static struct snd_soc_machine mpc8610_hpcd_machine = { + .probe = mpc8610_hpcd_machine_probe, + .remove = mpc8610_hpcd_machine_remove, + .name = "MPC8610 HPCD", + .num_links = 1, +}; + +/** + * mpc8610_hpcd_probe: OF probe function for the fabric driver + * + * This function gets called when an SSI node is found in the device tree. + * + * Although this is a fabric driver, the SSI node is the "master" node with + * respect to audio hardware connections. Therefore, we create a new ASoC + * device for each new SSI node that has a codec attached. + * + * FIXME: Currently, we only support one DMA controller, so if there are + * multiple SSI nodes with codecs, only the first will be supported. + * + * FIXME: Even if we did support multiple DMA controllers, we have no + * mechanism for assigning DMA controllers and channels to the individual + * SSI devices. We also probably aren't compatible with the generic Elo DMA + * device driver. + */ +static int mpc8610_hpcd_probe(struct of_device *ofdev, + const struct of_device_id *match) +{ + struct device_node *np = ofdev->node; + struct device_node *codec_np = NULL; + struct device_node *guts_np = NULL; + struct device_node *dma_np = NULL; + struct device_node *dma_channel_np = NULL; + const phandle *codec_ph; + const char *sprop; + const u32 *iprop; + struct resource res; + struct platform_device *sound_device = NULL; + struct mpc8610_hpcd_data *machine_data; + struct fsl_ssi_info ssi_info; + struct fsl_dma_info dma_info; + int ret = -ENODEV; + + machine_data = kzalloc(sizeof(struct mpc8610_hpcd_data), GFP_KERNEL); + if (!machine_data) + return -ENOMEM; + + memset(&ssi_info, 0, sizeof(ssi_info)); + memset(&dma_info, 0, sizeof(dma_info)); + + ssi_info.dev = &ofdev->dev; + + /* + * We are only interested in SSIs with a codec phandle in them, so let's + * make sure this SSI has one. + */ + codec_ph = of_get_property(np, "codec-handle", NULL); + if (!codec_ph) + goto error; + + codec_np = of_find_node_by_phandle(*codec_ph); + if (!codec_np) + goto error; + + /* The MPC8610 HPCD only knows about the CS4270 codec, so reject + anything else. */ + if (!of_device_is_compatible(codec_np, "cirrus,cs4270")) + goto error; + + /* Get the device ID */ + iprop = of_get_property(np, "cell-index", NULL); + if (!iprop) { + dev_err(&ofdev->dev, "cell-index property not found\n"); + ret = -EINVAL; + goto error; + } + machine_data->ssi_id = *iprop; + ssi_info.id = *iprop; + + /* Get the serial format and clock direction. */ + sprop = of_get_property(np, "fsl,mode", NULL); + if (!sprop) { + dev_err(&ofdev->dev, "fsl,mode property not found\n"); + ret = -EINVAL; + goto error; + } + + if (strcasecmp(sprop, "i2s-slave") == 0) { + machine_data->dai_format = SND_SOC_DAIFMT_I2S; + machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT; + machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN; + + /* + * In i2s-slave mode, the codec has its own clock source, so we + * need to get the frequency from the device tree and pass it to + * the codec driver. + */ + iprop = of_get_property(codec_np, "clock-frequency", NULL); + if (!iprop || !*iprop) { + dev_err(&ofdev->dev, "codec bus-frequency property " + "is missing or invalid\n"); + ret = -EINVAL; + goto error; + } + machine_data->clk_frequency = *iprop; + } else if (strcasecmp(sprop, "i2s-master") == 0) { + machine_data->dai_format = SND_SOC_DAIFMT_I2S; + machine_data->codec_clk_direction = SND_SOC_CLOCK_IN; + machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT; + } else if (strcasecmp(sprop, "lj-slave") == 0) { + machine_data->dai_format = SND_SOC_DAIFMT_LEFT_J; + machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT; + machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN; + } else if (strcasecmp(sprop, "lj-master") == 0) { + machine_data->dai_format = SND_SOC_DAIFMT_LEFT_J; + machine_data->codec_clk_direction = SND_SOC_CLOCK_IN; + machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT; + } else if (strcasecmp(sprop, "rj-master") == 0) { + machine_data->dai_format = SND_SOC_DAIFMT_RIGHT_J; + machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT; + machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN; + } else if (strcasecmp(sprop, "rj-master") == 0) { + machine_data->dai_format = SND_SOC_DAIFMT_RIGHT_J; + machine_data->codec_clk_direction = SND_SOC_CLOCK_IN; + machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT; + } else if (strcasecmp(sprop, "ac97-slave") == 0) { + machine_data->dai_format = SND_SOC_DAIFMT_AC97; + machine_data->codec_clk_direction = SND_SOC_CLOCK_OUT; + machine_data->cpu_clk_direction = SND_SOC_CLOCK_IN; + } else if (strcasecmp(sprop, "ac97-master") == 0) { + machine_data->dai_format = SND_SOC_DAIFMT_AC97; + machine_data->codec_clk_direction = SND_SOC_CLOCK_IN; + machine_data->cpu_clk_direction = SND_SOC_CLOCK_OUT; + } else { + dev_err(&ofdev->dev, + "unrecognized fsl,mode property \"%s\"\n", sprop); + ret = -EINVAL; + goto error; + } + + if (!machine_data->clk_frequency) { + dev_err(&ofdev->dev, "unknown clock frequency\n"); + ret = -EINVAL; + goto error; + } + + /* Read the SSI information from the device tree */ + ret = of_address_to_resource(np, 0, &res); + if (ret) { + dev_err(&ofdev->dev, "could not obtain SSI address\n"); + goto error; + } + if (!res.start) { + dev_err(&ofdev->dev, "invalid SSI address\n"); + goto error; + } + ssi_info.ssi_phys = res.start; + + machine_data->ssi = ioremap(ssi_info.ssi_phys, sizeof(struct ccsr_ssi)); + if (!machine_data->ssi) { + dev_err(&ofdev->dev, "could not map SSI address %x\n", + ssi_info.ssi_phys); + ret = -EINVAL; + goto error; + } + ssi_info.ssi = machine_data->ssi; + + + /* Get the IRQ of the SSI */ + machine_data->ssi_irq = irq_of_parse_and_map(np, 0); + if (!machine_data->ssi_irq) { + dev_err(&ofdev->dev, "could not get SSI IRQ\n"); + ret = -EINVAL; + goto error; + } + ssi_info.irq = machine_data->ssi_irq; + + + /* Map the global utilities registers. */ + guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts"); + if (!guts_np) { + dev_err(&ofdev->dev, "could not obtain address of GUTS\n"); + ret = -EINVAL; + goto error; + } + machine_data->guts = of_iomap(guts_np, 0); + of_node_put(guts_np); + if (!machine_data->guts) { + dev_err(&ofdev->dev, "could not map GUTS\n"); + ret = -EINVAL; + goto error; + } + + /* Find the DMA channels to use. For now, we always use the first DMA + controller. */ + for_each_compatible_node(dma_np, NULL, "fsl,mpc8610-dma") { + iprop = of_get_property(dma_np, "cell-index", NULL); + if (iprop && (*iprop == 0)) { + of_node_put(dma_np); + break; + } + } + if (!dma_np) { + dev_err(&ofdev->dev, "could not find DMA node\n"); + ret = -EINVAL; + goto error; + } + machine_data->dma_id = *iprop; + + /* + * Find the DMA channels to use. For now, we always use DMA channel 0 + * for playback, and DMA channel 1 for capture. + */ + while ((dma_channel_np = of_get_next_child(dma_np, dma_channel_np))) { + iprop = of_get_property(dma_channel_np, "cell-index", NULL); + /* Is it DMA channel 0? */ + if (iprop && (*iprop == 0)) { + /* dma_channel[0] and dma_irq[0] are for playback */ + dma_info.dma_channel[0] = of_iomap(dma_channel_np, 0); + dma_info.dma_irq[0] = + irq_of_parse_and_map(dma_channel_np, 0); + machine_data->dma_channel_id[0] = *iprop; + continue; + } + if (iprop && (*iprop == 1)) { + /* dma_channel[1] and dma_irq[1] are for capture */ + dma_info.dma_channel[1] = of_iomap(dma_channel_np, 0); + dma_info.dma_irq[1] = + irq_of_parse_and_map(dma_channel_np, 0); + machine_data->dma_channel_id[1] = *iprop; + continue; + } + } + if (!dma_info.dma_channel[0] || !dma_info.dma_channel[1] || + !dma_info.dma_irq[0] || !dma_info.dma_irq[1]) { + dev_err(&ofdev->dev, "could not find DMA channels\n"); + ret = -EINVAL; + goto error; + } + + dma_info.ssi_stx_phys = ssi_info.ssi_phys + + offsetof(struct ccsr_ssi, stx0); + dma_info.ssi_srx_phys = ssi_info.ssi_phys + + offsetof(struct ccsr_ssi, srx0); + + /* We have the DMA information, so tell the DMA driver what it is */ + if (!fsl_dma_configure(&dma_info)) { + dev_err(&ofdev->dev, "could not instantiate DMA device\n"); + ret = -EBUSY; + goto error; + } + + /* + * Initialize our DAI data structure. We should probably get this + * information from the device tree. + */ + machine_data->dai.name = "CS4270"; + machine_data->dai.stream_name = "CS4270"; + + machine_data->dai.cpu_dai = fsl_ssi_create_dai(&ssi_info); + machine_data->dai.codec_dai = &cs4270_dai; /* The codec_dai we want */ + machine_data->dai.ops = &mpc8610_hpcd_ops; + + mpc8610_hpcd_machine.dai_link = &machine_data->dai; + + /* Allocate a new audio platform device structure */ + sound_device = platform_device_alloc("soc-audio", -1); + if (!sound_device) { + dev_err(&ofdev->dev, "platform device allocation failed\n"); + ret = -ENOMEM; + goto error; + } + + machine_data->sound_devdata.machine = &mpc8610_hpcd_machine; + machine_data->sound_devdata.codec_dev = &soc_codec_device_cs4270; + machine_data->sound_devdata.platform = &fsl_soc_platform; + + sound_device->dev.platform_data = machine_data; + + + /* Set the platform device and ASoC device to point to each other */ + platform_set_drvdata(sound_device, &machine_data->sound_devdata); + + machine_data->sound_devdata.dev = &sound_device->dev; + + + /* Tell ASoC to probe us. This will call mpc8610_hpcd_machine.probe(), + if it exists. */ + ret = platform_device_add(sound_device); + + if (ret) { + dev_err(&ofdev->dev, "platform device add failed\n"); + goto error; + } + + dev_set_drvdata(&ofdev->dev, sound_device); + + return 0; + +error: + of_node_put(codec_np); + of_node_put(guts_np); + of_node_put(dma_np); + of_node_put(dma_channel_np); + + if (sound_device) + platform_device_unregister(sound_device); + + if (machine_data->dai.cpu_dai) + fsl_ssi_destroy_dai(machine_data->dai.cpu_dai); + + if (ssi_info.ssi) + iounmap(ssi_info.ssi); + + if (ssi_info.irq) + irq_dispose_mapping(ssi_info.irq); + + if (dma_info.dma_channel[0]) + iounmap(dma_info.dma_channel[0]); + + if (dma_info.dma_channel[1]) + iounmap(dma_info.dma_channel[1]); + + if (dma_info.dma_irq[0]) + irq_dispose_mapping(dma_info.dma_irq[0]); + + if (dma_info.dma_irq[1]) + irq_dispose_mapping(dma_info.dma_irq[1]); + + if (machine_data->guts) + iounmap(machine_data->guts); + + kfree(machine_data); + + return ret; +} + +/** + * mpc8610_hpcd_remove: remove the OF device + * + * This function is called when the OF device is removed. + */ +static int mpc8610_hpcd_remove(struct of_device *ofdev) +{ + struct platform_device *sound_device = dev_get_drvdata(&ofdev->dev); + struct mpc8610_hpcd_data *machine_data = + sound_device->dev.platform_data; + + platform_device_unregister(sound_device); + + if (machine_data->dai.cpu_dai) + fsl_ssi_destroy_dai(machine_data->dai.cpu_dai); + + if (machine_data->ssi) + iounmap(machine_data->ssi); + + if (machine_data->dma[0]) + iounmap(machine_data->dma[0]); + + if (machine_data->dma[1]) + iounmap(machine_data->dma[1]); + + if (machine_data->dma_irq[0]) + irq_dispose_mapping(machine_data->dma_irq[0]); + + if (machine_data->dma_irq[1]) + irq_dispose_mapping(machine_data->dma_irq[1]); + + if (machine_data->guts) + iounmap(machine_data->guts); + + kfree(machine_data); + sound_device->dev.platform_data = NULL; + + dev_set_drvdata(&ofdev->dev, NULL); + + return 0; +} + +static struct of_device_id mpc8610_hpcd_match[] = { + { + .compatible = "fsl,mpc8610-ssi", + }, + {} +}; +MODULE_DEVICE_TABLE(of, mpc8610_hpcd_match); + +static struct of_platform_driver mpc8610_hpcd_of_driver = { + .owner = THIS_MODULE, + .name = "mpc8610_hpcd", + .match_table = mpc8610_hpcd_match, + .probe = mpc8610_hpcd_probe, + .remove = mpc8610_hpcd_remove, +}; + +/** + * mpc8610_hpcd_init: fabric driver initialization. + * + * This function is called when this module is loaded. + */ +static int __init mpc8610_hpcd_init(void) +{ + int ret; + + printk(KERN_INFO "Freescale MPC8610 HPCD ALSA SoC fabric driver\n"); + + ret = of_register_platform_driver(&mpc8610_hpcd_of_driver); + + if (ret) + printk(KERN_ERR + "mpc8610-hpcd: failed to register platform driver\n"); + + return ret; +} + +/** + * mpc8610_hpcd_exit: fabric driver exit + * + * This function is called when this driver is unloaded. + */ +static void __exit mpc8610_hpcd_exit(void) +{ + of_unregister_platform_driver(&mpc8610_hpcd_of_driver); +} + +module_init(mpc8610_hpcd_init); +module_exit(mpc8610_hpcd_exit); + +MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); +MODULE_DESCRIPTION("Freescale MPC8610 HPCD ALSA SoC fabric driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index a83e229..484f883 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -53,3 +53,12 @@ config SND_PXA2XX_SOC_TOSA help Say Y if you want to add support for SoC audio on Sharp Zaurus SL-C6000x models (Tosa). + +config SND_PXA2XX_SOC_E800 + tristate "SoC AC97 Audio support for e800" + depends on SND_PXA2XX_SOC && MACH_E800 + select SND_SOC_WM9712 + select SND_PXA2XX_SOC_AC97 + help + Say Y if you want to add support for SoC audio on the + Toshiba e800 PDA diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile index 78e0d6b..04e5646 100644 --- a/sound/soc/pxa/Makefile +++ b/sound/soc/pxa/Makefile @@ -11,10 +11,12 @@ obj-$(CONFIG_SND_PXA2XX_SOC_I2S) += snd-soc-pxa2xx-i2s.o snd-soc-corgi-objs := corgi.o snd-soc-poodle-objs := poodle.o snd-soc-tosa-objs := tosa.o +snd-soc-e800-objs := e800_wm9712.o snd-soc-spitz-objs := spitz.o obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o obj-$(CONFIG_SND_PXA2XX_SOC_TOSA) += snd-soc-tosa.o +obj-$(CONFIG_SND_PXA2XX_SOC_E800) += snd-soc-e800.o obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c index 5ee51a9..3f34e53 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c @@ -22,7 +22,6 @@ #include <linux/timer.h> #include <linux/interrupt.h> #include <linux/platform_device.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c new file mode 100644 index 0000000..06e8afb --- /dev/null +++ b/sound/soc/pxa/e800_wm9712.c @@ -0,0 +1,89 @@ +/* + * e800-wm9712.c -- SoC audio for e800 + * + * Based on tosa.c + * + * Copyright 2007 (c) Ian Molton <spyro@f2s.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; version 2 ONLY. + * + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> + +#include <asm/mach-types.h> +#include <asm/arch/pxa-regs.h> +#include <asm/arch/hardware.h> +#include <asm/arch/audio.h> + +#include "../codecs/wm9712.h" +#include "pxa2xx-pcm.h" +#include "pxa2xx-ac97.h" + +static struct snd_soc_machine e800; + +static struct snd_soc_dai_link e800_dai[] = { +{ + .name = "AC97 Aux", + .stream_name = "AC97 Aux", + .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX], + .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX], +}, +}; + +static struct snd_soc_machine e800 = { + .name = "Toshiba e800", + .dai_link = e800_dai, + .num_links = ARRAY_SIZE(e800_dai), +}; + +static struct snd_soc_device e800_snd_devdata = { + .machine = &e800, + .platform = &pxa2xx_soc_platform, + .codec_dev = &soc_codec_dev_wm9712, +}; + +static struct platform_device *e800_snd_device; + +static int __init e800_init(void) +{ + int ret; + + if (!machine_is_e800()) + return -ENODEV; + + e800_snd_device = platform_device_alloc("soc-audio", -1); + if (!e800_snd_device) + return -ENOMEM; + + platform_set_drvdata(e800_snd_device, &e800_snd_devdata); + e800_snd_devdata.dev = &e800_snd_device->dev; + ret = platform_device_add(e800_snd_device); + + if (ret) + platform_device_put(e800_snd_device); + + return ret; +} + +static void __exit e800_exit(void) +{ + platform_device_unregister(e800_snd_device); +} + +module_init(e800_init); +module_exit(e800_exit); + +/* Module information */ +MODULE_AUTHOR("Ian Molton <spyro@f2s.com>"); +MODULE_DESCRIPTION("ALSA SoC driver for e800"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c index 0915cf7..5ae59bd 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c @@ -19,7 +19,6 @@ #include <linux/timer.h> #include <linux/interrupt.h> #include <linux/platform_device.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index 60e6f46..815c153 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -17,7 +17,6 @@ #include <linux/wait.h> #include <linux/delay.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/ac97_codec.h> diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index 50c5c83..692b900 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -18,7 +18,6 @@ #include <linux/module.h> #include <linux/device.h> #include <linux/delay.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/initval.h> diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index 35e8fa3..daeaa4c 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -16,7 +16,6 @@ #include <linux/slab.h> #include <linux/dma-mapping.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index 4dd8f35..d56709e 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c @@ -22,7 +22,6 @@ #include <linux/timer.h> #include <linux/interrupt.h> #include <linux/platform_device.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c index 5504e30..e4d40b5 100644 --- a/sound/soc/pxa/tosa.c +++ b/sound/soc/pxa/tosa.c @@ -25,7 +25,6 @@ #include <linux/moduleparam.h> #include <linux/device.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig index 5632a2e..1f6dbfc 100644 --- a/sound/soc/s3c24xx/Kconfig +++ b/sound/soc/s3c24xx/Kconfig @@ -10,6 +10,9 @@ config SND_S3C24XX_SOC config SND_S3C24XX_SOC_I2S tristate +config SND_S3C2412_SOC_I2S + tristate + config SND_S3C2443_SOC_AC97 tristate select AC97_BUS @@ -34,4 +37,12 @@ config SND_S3C24XX_SOC_SMDK2443_WM9710 Say Y if you want to add support for SoC audio on smdk2443 with the WM9710. +config SND_S3C24XX_SOC_LN2440SBC_ALC650 + tristate "SoC AC97 Audio support for LN2440SBC - ALC650" + depends on SND_S3C24XX_SOC + select SND_S3C2443_SOC_AC97 + select SND_SOC_AC97_CODEC + help + Say Y if you want to add support for SoC audio on ln2440sbc + with the ALC650. diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile index 13c92f0..0aa5fb0 100644 --- a/sound/soc/s3c24xx/Makefile +++ b/sound/soc/s3c24xx/Makefile @@ -1,15 +1,19 @@ # S3c24XX Platform Support snd-soc-s3c24xx-objs := s3c24xx-pcm.o snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o +snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o snd-soc-s3c2443-ac97-objs := s3c2443-ac97.o obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.o +obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o # S3C24XX Machine Support snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o +snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o +obj-$(CONFIG_SND_S3C24XX_SOC_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o diff --git a/sound/soc/s3c24xx/ln2440sbc_alc650.c b/sound/soc/s3c24xx/ln2440sbc_alc650.c new file mode 100644 index 0000000..9ed8f2e --- /dev/null +++ b/sound/soc/s3c24xx/ln2440sbc_alc650.c @@ -0,0 +1,85 @@ +/* + * SoC audio for ln2440sbc + * + * Copyright 2007 KonekTel, a.s. + * Author: Ivan Kuten + * ivan.kuten@promwad.com + * + * Heavily based on smdk2443_wm9710.c + * Copyright 2007 Wolfson Microelectronics PLC. + * Author: Graeme Gregory + * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> + +#include "../codecs/ac97.h" +#include "s3c24xx-pcm.h" +#include "s3c24xx-ac97.h" + +static struct snd_soc_machine ln2440sbc; + +static struct snd_soc_dai_link ln2440sbc_dai[] = { +{ + .name = "AC97", + .stream_name = "AC97 HiFi", + .cpu_dai = &s3c2443_ac97_dai[0], + .codec_dai = &ac97_dai, +}, +}; + +static struct snd_soc_machine ln2440sbc = { + .name = "LN2440SBC", + .dai_link = ln2440sbc_dai, + .num_links = ARRAY_SIZE(ln2440sbc_dai), +}; + +static struct snd_soc_device ln2440sbc_snd_ac97_devdata = { + .machine = &ln2440sbc, + .platform = &s3c24xx_soc_platform, + .codec_dev = &soc_codec_dev_ac97, +}; + +static struct platform_device *ln2440sbc_snd_ac97_device; + +static int __init ln2440sbc_init(void) +{ + int ret; + + ln2440sbc_snd_ac97_device = platform_device_alloc("soc-audio", -1); + if (!ln2440sbc_snd_ac97_device) + return -ENOMEM; + + platform_set_drvdata(ln2440sbc_snd_ac97_device, + &ln2440sbc_snd_ac97_devdata); + ln2440sbc_snd_ac97_devdata.dev = &ln2440sbc_snd_ac97_device->dev; + ret = platform_device_add(ln2440sbc_snd_ac97_device); + + if (ret) + platform_device_put(ln2440sbc_snd_ac97_device); + + return ret; +} + +static void __exit ln2440sbc_exit(void) +{ + platform_device_unregister(ln2440sbc_snd_ac97_device); +} + +module_init(ln2440sbc_init); +module_exit(ln2440sbc_exit); + +/* Module information */ +MODULE_AUTHOR("Ivan Kuten"); +MODULE_DESCRIPTION("ALSA SoC ALC650 LN2440SBC"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c index f1f6b94..6ee115c 100644 --- a/sound/soc/s3c24xx/neo1973_wm8753.c +++ b/sound/soc/s3c24xx/neo1973_wm8753.c @@ -22,7 +22,6 @@ #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/i2c.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> @@ -30,13 +29,15 @@ #include <asm/mach-types.h> #include <asm/hardware/scoop.h> -#include <asm/arch/regs-iis.h> #include <asm/arch/regs-clock.h> #include <asm/arch/regs-gpio.h> #include <asm/hardware.h> #include <asm/arch/audio.h> #include <asm/io.h> #include <asm/arch/spi-gpio.h> + +#include <asm/plat-s3c24xx/regs-iis.h> + #include "../codecs/wm8753.h" #include "lm4857.h" #include "s3c24xx-pcm.h" diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c new file mode 100644 index 0000000..c4a46dd --- /dev/null +++ b/sound/soc/s3c24xx/s3c2412-i2s.c @@ -0,0 +1,744 @@ +/* sound/soc/s3c24xx/s3c2412-i2s.c + * + * ALSA Soc Audio Layer - S3C2412 I2S driver + * + * Copyright (c) 2006 Wolfson Microelectronics PLC. + * Graeme Gregory graeme.gregory@wolfsonmicro.com + * linux@wolfsonmicro.com + * + * Copyright (c) 2007, 2004-2005 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * + * 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. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/kernel.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/soc.h> +#include <asm/hardware.h> + +#include <linux/io.h> +#include <asm/dma.h> + +#include <asm/plat-s3c24xx/regs-s3c2412-iis.h> + +#include <asm/arch/regs-gpio.h> +#include <asm/arch/audio.h> +#include <asm/arch/dma.h> + +#include "s3c24xx-pcm.h" +#include "s3c2412-i2s.h" + +#define S3C2412_I2S_DEBUG 0 +#define S3C2412_I2S_DEBUG_CON 0 + +#if S3C2412_I2S_DEBUG +#define DBG(x...) printk(KERN_INFO x) +#else +#define DBG(x...) do { } while (0) +#endif + +static struct s3c2410_dma_client s3c2412_dma_client_out = { + .name = "I2S PCM Stereo out" +}; + +static struct s3c2410_dma_client s3c2412_dma_client_in = { + .name = "I2S PCM Stereo in" +}; + +static struct s3c24xx_pcm_dma_params s3c2412_i2s_pcm_stereo_out = { + .client = &s3c2412_dma_client_out, + .channel = DMACH_I2S_OUT, + .dma_addr = S3C2410_PA_IIS + S3C2412_IISTXD, + .dma_size = 4, +}; + +static struct s3c24xx_pcm_dma_params s3c2412_i2s_pcm_stereo_in = { + .client = &s3c2412_dma_client_in, + .channel = DMACH_I2S_IN, + .dma_addr = S3C2410_PA_IIS + S3C2412_IISRXD, + .dma_size = 4, +}; + +struct s3c2412_i2s_info { + struct device *dev; + void __iomem *regs; + struct clk *iis_clk; + struct clk *iis_pclk; + struct clk *iis_cclk; + + u32 suspend_iismod; + u32 suspend_iiscon; + u32 suspend_iispsr; +}; + +static struct s3c2412_i2s_info s3c2412_i2s; + +#define bit_set(v, b) (((v) & (b)) ? 1 : 0) + +#if S3C2412_I2S_DEBUG_CON +static void dbg_showcon(const char *fn, u32 con) +{ + printk(KERN_DEBUG "%s: LRI=%d, TXFEMPT=%d, RXFEMPT=%d, TXFFULL=%d, RXFFULL=%d\n", fn, + bit_set(con, S3C2412_IISCON_LRINDEX), + bit_set(con, S3C2412_IISCON_TXFIFO_EMPTY), + bit_set(con, S3C2412_IISCON_RXFIFO_EMPTY), + bit_set(con, S3C2412_IISCON_TXFIFO_FULL), + bit_set(con, S3C2412_IISCON_RXFIFO_FULL)); + + printk(KERN_DEBUG "%s: PAUSE: TXDMA=%d, RXDMA=%d, TXCH=%d, RXCH=%d\n", + fn, + bit_set(con, S3C2412_IISCON_TXDMA_PAUSE), + bit_set(con, S3C2412_IISCON_RXDMA_PAUSE), + bit_set(con, S3C2412_IISCON_TXCH_PAUSE), + bit_set(con, S3C2412_IISCON_RXCH_PAUSE)); + printk(KERN_DEBUG "%s: ACTIVE: TXDMA=%d, RXDMA=%d, IIS=%d\n", fn, + bit_set(con, S3C2412_IISCON_TXDMA_ACTIVE), + bit_set(con, S3C2412_IISCON_RXDMA_ACTIVE), + bit_set(con, S3C2412_IISCON_IIS_ACTIVE)); +} +#else +static inline void dbg_showcon(const char *fn, u32 con) +{ +} +#endif + +/* Turn on or off the transmission path. */ +static void s3c2412_snd_txctrl(int on) +{ + struct s3c2412_i2s_info *i2s = &s3c2412_i2s; + void __iomem *regs = i2s->regs; + u32 fic, con, mod; + + DBG("%s(%d)\n", __func__, on); + + fic = readl(regs + S3C2412_IISFIC); + con = readl(regs + S3C2412_IISCON); + mod = readl(regs + S3C2412_IISMOD); + + DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); + + if (on) { + con |= S3C2412_IISCON_TXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE; + con &= ~S3C2412_IISCON_TXDMA_PAUSE; + con &= ~S3C2412_IISCON_TXCH_PAUSE; + + switch (mod & S3C2412_IISMOD_MODE_MASK) { + case S3C2412_IISMOD_MODE_TXONLY: + case S3C2412_IISMOD_MODE_TXRX: + /* do nothing, we are in the right mode */ + break; + + case S3C2412_IISMOD_MODE_RXONLY: + mod &= ~S3C2412_IISMOD_MODE_MASK; + mod |= S3C2412_IISMOD_MODE_TXRX; + break; + + default: + dev_err(i2s->dev, "TXEN: Invalid MODE in IISMOD\n"); + } + + writel(con, regs + S3C2412_IISCON); + writel(mod, regs + S3C2412_IISMOD); + } else { + /* Note, we do not have any indication that the FIFO problems + * tha the S3C2410/2440 had apply here, so we should be able + * to disable the DMA and TX without resetting the FIFOS. + */ + + con |= S3C2412_IISCON_TXDMA_PAUSE; + con |= S3C2412_IISCON_TXCH_PAUSE; + con &= ~S3C2412_IISCON_TXDMA_ACTIVE; + + switch (mod & S3C2412_IISMOD_MODE_MASK) { + case S3C2412_IISMOD_MODE_TXRX: + mod &= ~S3C2412_IISMOD_MODE_MASK; + mod |= S3C2412_IISMOD_MODE_RXONLY; + break; + + case S3C2412_IISMOD_MODE_TXONLY: + mod &= ~S3C2412_IISMOD_MODE_MASK; + con &= ~S3C2412_IISCON_IIS_ACTIVE; + break; + + default: + dev_err(i2s->dev, "TXDIS: Invalid MODE in IISMOD\n"); + } + + writel(mod, regs + S3C2412_IISMOD); + writel(con, regs + S3C2412_IISCON); + } + + fic = readl(regs + S3C2412_IISFIC); + dbg_showcon(__func__, con); + DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); +} + +static void s3c2412_snd_rxctrl(int on) +{ + struct s3c2412_i2s_info *i2s = &s3c2412_i2s; + void __iomem *regs = i2s->regs; + u32 fic, con, mod; + + DBG("%s(%d)\n", __func__, on); + + fic = readl(regs + S3C2412_IISFIC); + con = readl(regs + S3C2412_IISCON); + mod = readl(regs + S3C2412_IISMOD); + + DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); + + if (on) { + con |= S3C2412_IISCON_RXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE; + con &= ~S3C2412_IISCON_RXDMA_PAUSE; + con &= ~S3C2412_IISCON_RXCH_PAUSE; + + switch (mod & S3C2412_IISMOD_MODE_MASK) { + case S3C2412_IISMOD_MODE_TXRX: + case S3C2412_IISMOD_MODE_RXONLY: + /* do nothing, we are in the right mode */ + break; + + case S3C2412_IISMOD_MODE_TXONLY: + mod &= ~S3C2412_IISMOD_MODE_MASK; + mod |= S3C2412_IISMOD_MODE_TXRX; + break; + + default: + dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n"); + } + + writel(mod, regs + S3C2412_IISMOD); + writel(con, regs + S3C2412_IISCON); + } else { + /* See txctrl notes on FIFOs. */ + + con &= ~S3C2412_IISCON_RXDMA_ACTIVE; + con |= S3C2412_IISCON_RXDMA_PAUSE; + con |= S3C2412_IISCON_RXCH_PAUSE; + + switch (mod & S3C2412_IISMOD_MODE_MASK) { + case S3C2412_IISMOD_MODE_RXONLY: + con &= ~S3C2412_IISCON_IIS_ACTIVE; + mod &= ~S3C2412_IISMOD_MODE_MASK; + break; + + case S3C2412_IISMOD_MODE_TXRX: + mod &= ~S3C2412_IISMOD_MODE_MASK; + mod |= S3C2412_IISMOD_MODE_TXONLY; + break; + + default: + dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n"); + } + + writel(con, regs + S3C2412_IISCON); + writel(mod, regs + S3C2412_IISMOD); + } + + fic = readl(regs + S3C2412_IISFIC); + DBG("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic); +} + + +/* + * Wait for the LR signal to allow synchronisation to the L/R clock + * from the codec. May only be needed for slave mode. + */ +static int s3c2412_snd_lrsync(void) +{ + u32 iiscon; + unsigned long timeout = jiffies + msecs_to_jiffies(5); + + DBG("Entered %s\n", __func__); + + while (1) { + iiscon = readl(s3c2412_i2s.regs + S3C2412_IISCON); + if (iiscon & S3C2412_IISCON_LRINDEX) + break; + + if (timeout < jiffies) { + printk(KERN_ERR "%s: timeout\n", __func__); + return -ETIMEDOUT; + } + } + + return 0; +} + +/* + * Check whether CPU is the master or slave + */ +static inline int s3c2412_snd_is_clkmaster(void) +{ + u32 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD); + + DBG("Entered %s\n", __func__); + + iismod &= S3C2412_IISMOD_MASTER_MASK; + return !(iismod == S3C2412_IISMOD_SLAVE); +} + +/* + * Set S3C2412 I2S DAI format + */ +static int s3c2412_i2s_set_fmt(struct snd_soc_cpu_dai *cpu_dai, + unsigned int fmt) +{ + u32 iismod; + + + DBG("Entered %s\n", __func__); + + iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD); + DBG("hw_params r: IISMOD: %x \n", iismod); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + iismod &= ~S3C2412_IISMOD_MASTER_MASK; + iismod |= S3C2412_IISMOD_SLAVE; + break; + case SND_SOC_DAIFMT_CBS_CFS: + iismod &= ~S3C2412_IISMOD_MASTER_MASK; + iismod |= S3C2412_IISMOD_MASTER_INTERNAL; + break; + default: + DBG("unknwon master/slave format\n"); + return -EINVAL; + } + + iismod &= ~S3C2412_IISMOD_SDF_MASK; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + iismod |= S3C2412_IISMOD_SDF_MSB; + break; + case SND_SOC_DAIFMT_LEFT_J: + iismod |= S3C2412_IISMOD_SDF_LSB; + break; + case SND_SOC_DAIFMT_I2S: + iismod |= S3C2412_IISMOD_SDF_IIS; + break; + default: + DBG("Unknown data format\n"); + return -EINVAL; + } + + writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD); + DBG("hw_params w: IISMOD: %x \n", iismod); + return 0; +} + +static int s3c2412_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + u32 iismod; + + DBG("Entered %s\n", __func__); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rtd->dai->cpu_dai->dma_data = &s3c2412_i2s_pcm_stereo_out; + else + rtd->dai->cpu_dai->dma_data = &s3c2412_i2s_pcm_stereo_in; + + /* Working copies of register */ + iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD); + DBG("%s: r: IISMOD: %x\n", __func__, iismod); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + iismod |= S3C2412_IISMOD_8BIT; + break; + case SNDRV_PCM_FORMAT_S16_LE: + iismod &= ~S3C2412_IISMOD_8BIT; + break; + } + + writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD); + DBG("%s: w: IISMOD: %x\n", __func__, iismod); + return 0; +} + +static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); + unsigned long irqs; + int ret = 0; + + DBG("Entered %s\n", __func__); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + /* On start, ensure that the FIFOs are cleared and reset. */ + + writel(capture ? S3C2412_IISFIC_RXFLUSH : S3C2412_IISFIC_TXFLUSH, + s3c2412_i2s.regs + S3C2412_IISFIC); + + /* clear again, just in case */ + writel(0x0, s3c2412_i2s.regs + S3C2412_IISFIC); + + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (!s3c2412_snd_is_clkmaster()) { + ret = s3c2412_snd_lrsync(); + if (ret) + goto exit_err; + } + + local_irq_save(irqs); + + if (capture) + s3c2412_snd_rxctrl(1); + else + s3c2412_snd_txctrl(1); + + local_irq_restore(irqs); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + local_irq_save(irqs); + + if (capture) + s3c2412_snd_rxctrl(0); + else + s3c2412_snd_txctrl(0); + + local_irq_restore(irqs); + break; + default: + ret = -EINVAL; + break; + } + +exit_err: + return ret; +} + +/* default table of all avaialable root fs divisors */ +static unsigned int s3c2412_iis_fs[] = { 256, 512, 384, 768, 0 }; + +int s3c2412_iis_calc_rate(struct s3c2412_rate_calc *info, + unsigned int *fstab, + unsigned int rate, struct clk *clk) +{ + unsigned long clkrate = clk_get_rate(clk); + unsigned int div; + unsigned int fsclk; + unsigned int actual; + unsigned int fs; + unsigned int fsdiv; + signed int deviation = 0; + unsigned int best_fs = 0; + unsigned int best_div = 0; + unsigned int best_rate = 0; + unsigned int best_deviation = INT_MAX; + + + if (fstab == NULL) + fstab = s3c2412_iis_fs; + + for (fs = 0;; fs++) { + fsdiv = s3c2412_iis_fs[fs]; + + if (fsdiv == 0) + break; + + fsclk = clkrate / fsdiv; + div = fsclk / rate; + + if ((fsclk % rate) > (rate / 2)) + div++; + + if (div <= 1) + continue; + + actual = clkrate / (fsdiv * div); + deviation = actual - rate; + + printk(KERN_DEBUG "%dfs: div %d => result %d, deviation %d\n", + fsdiv, div, actual, deviation); + + deviation = abs(deviation); + + if (deviation < best_deviation) { + best_fs = fsdiv; + best_div = div; + best_rate = actual; + best_deviation = deviation; + } + + if (deviation == 0) + break; + } + + printk(KERN_DEBUG "best: fs=%d, div=%d, rate=%d\n", + best_fs, best_div, best_rate); + + info->fs_div = best_fs; + info->clk_div = best_div; + + return 0; +} +EXPORT_SYMBOL_GPL(s3c2412_iis_calc_rate); + +/* + * Set S3C2412 Clock source + */ +static int s3c2412_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + u32 iismod = readl(s3c2412_i2s.regs + S3C2412_IISMOD); + + DBG("%s(%p, %d, %u, %d)\n", __func__, cpu_dai, clk_id, + freq, dir); + + switch (clk_id) { + case S3C2412_CLKSRC_PCLK: + iismod &= ~S3C2412_IISMOD_MASTER_MASK; + iismod |= S3C2412_IISMOD_MASTER_INTERNAL; + break; + case S3C2412_CLKSRC_I2SCLK: + iismod &= ~S3C2412_IISMOD_MASTER_MASK; + iismod |= S3C2412_IISMOD_MASTER_EXTERNAL; + break; + default: + return -EINVAL; + } + + writel(iismod, s3c2412_i2s.regs + S3C2412_IISMOD); + return 0; +} + +/* + * Set S3C2412 Clock dividers + */ +static int s3c2412_i2s_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai, + int div_id, int div) +{ + struct s3c2412_i2s_info *i2s = &s3c2412_i2s; + u32 reg; + + DBG("%s(%p, %d, %d)\n", __func__, cpu_dai, div_id, div); + + switch (div_id) { + case S3C2412_DIV_BCLK: + reg = readl(i2s->regs + S3C2412_IISMOD); + reg &= ~S3C2412_IISMOD_BCLK_MASK; + writel(reg | div, i2s->regs + S3C2412_IISMOD); + + DBG("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD)); + break; + + case S3C2412_DIV_RCLK: + if (div > 3) { + /* convert value to bit field */ + + switch (div) { + case 256: + div = S3C2412_IISMOD_RCLK_256FS; + break; + + case 384: + div = S3C2412_IISMOD_RCLK_384FS; + break; + + case 512: + div = S3C2412_IISMOD_RCLK_512FS; + break; + + case 768: + div = S3C2412_IISMOD_RCLK_768FS; + break; + + default: + return -EINVAL; + } + } + + reg = readl(s3c2412_i2s.regs + S3C2412_IISMOD); + reg &= ~S3C2412_IISMOD_RCLK_MASK; + writel(reg | div, i2s->regs + S3C2412_IISMOD); + DBG("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD)); + break; + + case S3C2412_DIV_PRESCALER: + if (div >= 0) { + writel((div << 8) | S3C2412_IISPSR_PSREN, + i2s->regs + S3C2412_IISPSR); + } else { + writel(0x0, i2s->regs + S3C2412_IISPSR); + } + DBG("%s: PSR=%08x\n", __func__, readl(i2s->regs + S3C2412_IISPSR)); + break; + + default: + return -EINVAL; + } + + return 0; +} + +struct clk *s3c2412_get_iisclk(void) +{ + return s3c2412_i2s.iis_clk; +} +EXPORT_SYMBOL_GPL(s3c2412_get_iisclk); + + +static int s3c2412_i2s_probe(struct platform_device *pdev) +{ + DBG("Entered %s\n", __func__); + + s3c2412_i2s.dev = &pdev->dev; + + s3c2412_i2s.regs = ioremap(S3C2410_PA_IIS, 0x100); + if (s3c2412_i2s.regs == NULL) + return -ENXIO; + + s3c2412_i2s.iis_pclk = clk_get(&pdev->dev, "iis"); + if (s3c2412_i2s.iis_pclk == NULL) { + DBG("failed to get iis_clock\n"); + iounmap(s3c2412_i2s.regs); + return -ENODEV; + } + + s3c2412_i2s.iis_cclk = clk_get(&pdev->dev, "i2sclk"); + if (s3c2412_i2s.iis_cclk == NULL) { + DBG("failed to get i2sclk clock\n"); + iounmap(s3c2412_i2s.regs); + return -ENODEV; + } + + clk_set_parent(s3c2412_i2s.iis_cclk, clk_get(NULL, "mpll")); + + clk_enable(s3c2412_i2s.iis_pclk); + clk_enable(s3c2412_i2s.iis_cclk); + + s3c2412_i2s.iis_clk = s3c2412_i2s.iis_pclk; + + /* Configure the I2S pins in correct mode */ + s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_I2SLRCK); + s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2410_GPE1_I2SSCLK); + s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2410_GPE2_CDCLK); + s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI); + s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO); + + s3c2412_snd_txctrl(0); + s3c2412_snd_rxctrl(0); + + return 0; +} + +#ifdef CONFIG_PM +static int s3c2412_i2s_suspend(struct platform_device *dev, + struct snd_soc_cpu_dai *dai) +{ + struct s3c2412_i2s_info *i2s = &s3c2412_i2s; + u32 iismod; + + if (dai->active) { + i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD); + i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON); + i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR); + + /* some basic suspend checks */ + + iismod = readl(i2s->regs + S3C2412_IISMOD); + + if (iismod & S3C2412_IISCON_RXDMA_ACTIVE) + dev_warn(&dev->dev, "%s: RXDMA active?\n", __func__); + + if (iismod & S3C2412_IISCON_TXDMA_ACTIVE) + dev_warn(&dev->dev, "%s: TXDMA active?\n", __func__); + + if (iismod & S3C2412_IISCON_IIS_ACTIVE) + dev_warn(&dev->dev, "%s: IIS active\n", __func__); + } + + return 0; +} + +static int s3c2412_i2s_resume(struct platform_device *pdev, + struct snd_soc_cpu_dai *dai) +{ + struct s3c2412_i2s_info *i2s = &s3c2412_i2s; + + dev_info(&pdev->dev, "dai_active %d, IISMOD %08x, IISCON %08x\n", + dai->active, i2s->suspend_iismod, i2s->suspend_iiscon); + + if (dai->active) { + writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON); + writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD); + writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR); + + writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH, + i2s->regs + S3C2412_IISFIC); + + ndelay(250); + writel(0x0, i2s->regs + S3C2412_IISFIC); + + } + + return 0; +} +#else +#define s3c2412_i2s_suspend NULL +#define s3c2412_i2s_resume NULL +#endif /* CONFIG_PM */ + +#define S3C2412_I2S_RATES \ + (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) + +struct snd_soc_cpu_dai s3c2412_i2s_dai = { + .name = "s3c2412-i2s", + .id = 0, + .type = SND_SOC_DAI_I2S, + .probe = s3c2412_i2s_probe, + .suspend = s3c2412_i2s_suspend, + .resume = s3c2412_i2s_resume, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = S3C2412_I2S_RATES, + .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = S3C2412_I2S_RATES, + .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = { + .trigger = s3c2412_i2s_trigger, + .hw_params = s3c2412_i2s_hw_params, + }, + .dai_ops = { + .set_fmt = s3c2412_i2s_set_fmt, + .set_clkdiv = s3c2412_i2s_set_clkdiv, + .set_sysclk = s3c2412_i2s_set_sysclk, + }, +}; +EXPORT_SYMBOL_GPL(s3c2412_i2s_dai); + +/* Module information */ +MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); +MODULE_DESCRIPTION("S3C2412 I2S SoC Interface"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/s3c24xx/s3c2412-i2s.h b/sound/soc/s3c24xx/s3c2412-i2s.h new file mode 100644 index 0000000..27f48e1 --- /dev/null +++ b/sound/soc/s3c24xx/s3c2412-i2s.h @@ -0,0 +1,38 @@ +/* sound/soc/s3c24xx/s3c2412-i2s.c + * + * ALSA Soc Audio Layer - S3C2412 I2S driver + * + * Copyright (c) 2007 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks <ben@simtec.co.uk> + * + * 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. +*/ + +#ifndef __SND_SOC_S3C24XX_S3C2412_I2S_H +#define __SND_SOC_S3C24XX_S3C2412_I2S_H __FILE__ + +#define S3C2412_DIV_BCLK (1) +#define S3C2412_DIV_RCLK (2) +#define S3C2412_DIV_PRESCALER (3) + +#define S3C2412_CLKSRC_PCLK (0) +#define S3C2412_CLKSRC_I2SCLK (1) + +extern struct clk *s3c2412_get_iisclk(void); + +extern struct snd_soc_cpu_dai s3c2412_i2s_dai; + +struct s3c2412_rate_calc { + unsigned int clk_div; /* for prescaler */ + unsigned int fs_div; /* for root frame clock */ +}; + +extern int s3c2412_iis_calc_rate(struct s3c2412_rate_calc *info, + unsigned int *fstab, + unsigned int rate, struct clk *clk); + +#endif /* __SND_SOC_S3C24XX_S3C2412_I2S_H */ diff --git a/sound/soc/s3c24xx/s3c2443-ac97.c b/sound/soc/s3c24xx/s3c2443-ac97.c index 758a263..1c1ddbf 100644 --- a/sound/soc/s3c24xx/s3c2443-ac97.c +++ b/sound/soc/s3c24xx/s3c2443-ac97.c @@ -23,7 +23,6 @@ #include <linux/delay.h> #include <linux/clk.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/ac97_codec.h> @@ -253,7 +252,7 @@ static int s3c2443_ac97_probe(struct platform_device *pdev) ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE; writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); - ret = request_irq(IRQ_S3C2443_AC97, s3c2443_ac97_irq, + ret = request_irq(IRQ_S3C244x_AC97, s3c2443_ac97_irq, IRQF_DISABLED, "AC97", NULL); if (ret < 0) { printk(KERN_ERR "s3c24xx-ac97: interrupt request failed.\n"); @@ -266,7 +265,7 @@ static int s3c2443_ac97_probe(struct platform_device *pdev) static void s3c2443_ac97_remove(struct platform_device *pdev) { - free_irq(IRQ_S3C2443_AC97, NULL); + free_irq(IRQ_S3C244x_AC97, NULL); clk_disable(s3c24xx_ac97.ac97_clk); clk_put(s3c24xx_ac97.ac97_clk); iounmap(s3c24xx_ac97.regs); diff --git a/sound/soc/s3c24xx/s3c24xx-ac97.h b/sound/soc/s3c24xx/s3c24xx-ac97.h index 2b835e8..bf03e8e 100644 --- a/sound/soc/s3c24xx/s3c24xx-ac97.h +++ b/sound/soc/s3c24xx/s3c24xx-ac97.h @@ -20,6 +20,12 @@ #define AC_CMD_ADDR(x) (x << 16) #define AC_CMD_DATA(x) (x & 0xffff) +#ifdef CONFIG_CPU_S3C2440 +#define IRQ_S3C244x_AC97 IRQ_S3C2440_AC97 +#else +#define IRQ_S3C244x_AC97 IRQ_S3C2443_AC97 +#endif + extern struct snd_soc_cpu_dai s3c2443_ac97_dai[]; #endif /*S3C24XXAC97_H_*/ diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c index cd89c41..0a3c630 100644 --- a/sound/soc/s3c24xx/s3c24xx-i2s.c +++ b/sound/soc/s3c24xx/s3c24xx-i2s.c @@ -24,7 +24,7 @@ #include <linux/device.h> #include <linux/delay.h> #include <linux/clk.h> -#include <sound/driver.h> +#include <linux/jiffies.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -33,13 +33,14 @@ #include <asm/hardware.h> #include <asm/io.h> -#include <asm/arch/regs-iis.h> #include <asm/arch/regs-gpio.h> #include <asm/arch/regs-clock.h> #include <asm/arch/audio.h> #include <asm/dma.h> #include <asm/arch/dma.h> +#include <asm/plat-s3c24xx/regs-iis.h> + #include "s3c24xx-pcm.h" #include "s3c24xx-i2s.h" @@ -75,6 +76,10 @@ static struct s3c24xx_pcm_dma_params s3c24xx_i2s_pcm_stereo_in = { struct s3c24xx_i2s_info { void __iomem *regs; struct clk *iis_clk; + u32 iiscon; + u32 iismod; + u32 iisfcon; + u32 iispsr; }; static struct s3c24xx_i2s_info s3c24xx_i2s; @@ -184,7 +189,7 @@ static int s3c24xx_snd_lrsync(void) if (iiscon & S3C2410_IISCON_LRINDEX) break; - if (timeout < jiffies) + if (time_after(jiffies, timeout)) return -ETIMEDOUT; } @@ -405,6 +410,38 @@ static int s3c24xx_i2s_probe(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +int s3c24xx_i2s_suspend(struct platform_device *pdev, + struct snd_soc_cpu_dai *cpu_dai) +{ + s3c24xx_i2s.iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON); + s3c24xx_i2s.iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD); + s3c24xx_i2s.iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON); + s3c24xx_i2s.iispsr = readl(s3c24xx_i2s.regs + S3C2410_IISPSR); + + clk_disable(s3c24xx_i2s.iis_clk); + + return 0; +} + +int s3c24xx_i2s_resume(struct platform_device *pdev, + struct snd_soc_cpu_dai *cpu_dai) +{ + clk_enable(s3c24xx_i2s.iis_clk); + + writel(s3c24xx_i2s.iiscon, s3c24xx_i2s.regs + S3C2410_IISCON); + writel(s3c24xx_i2s.iismod, s3c24xx_i2s.regs + S3C2410_IISMOD); + writel(s3c24xx_i2s.iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON); + writel(s3c24xx_i2s.iispsr, s3c24xx_i2s.regs + S3C2410_IISPSR); + + return 0; +} +#else +#define s3c24xx_i2s_suspend NULL +#define s3c24xx_i2s_resume NULL +#endif + + #define S3C24XX_I2S_RATES \ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ @@ -415,6 +452,8 @@ struct snd_soc_cpu_dai s3c24xx_i2s_dai = { .id = 0, .type = SND_SOC_DAI_I2S, .probe = s3c24xx_i2s_probe, + .suspend = s3c24xx_i2s_suspend, + .resume = s3c24xx_i2s_resume, .playback = { .channels_min = 2, .channels_max = 2, diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c index 4107a87..29a6c82f 100644 --- a/sound/soc/s3c24xx/s3c24xx-pcm.c +++ b/sound/soc/s3c24xx/s3c24xx-pcm.c @@ -24,7 +24,6 @@ #include <linux/slab.h> #include <linux/dma-mapping.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -49,7 +48,9 @@ static const struct snd_pcm_hardware s3c24xx_pcm_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID, + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U8 | @@ -176,28 +177,6 @@ static int s3c24xx_pcm_hw_params(struct snd_pcm_substream *substream, } } - /* channel needs configuring for mem=>device, increment memory addr, - * sync to pclk, half-word transfers to the IIS-FIFO. */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - s3c2410_dma_devconfig(prtd->params->channel, - S3C2410_DMASRC_MEM, S3C2410_DISRCC_INC | - S3C2410_DISRCC_APB, prtd->params->dma_addr); - - s3c2410_dma_config(prtd->params->channel, - prtd->params->dma_size, - S3C2410_DCON_SYNC_PCLK | - S3C2410_DCON_HANDSHAKE); - } else { - s3c2410_dma_config(prtd->params->channel, - prtd->params->dma_size, - S3C2410_DCON_HANDSHAKE | - S3C2410_DCON_SYNC_PCLK); - - s3c2410_dma_devconfig(prtd->params->channel, - S3C2410_DMASRC_HW, 0x3, - prtd->params->dma_addr); - } - s3c2410_dma_set_buffdone_fn(prtd->params->channel, s3c24xx_audio_buffdone); @@ -246,6 +225,28 @@ static int s3c24xx_pcm_prepare(struct snd_pcm_substream *substream) if (!prtd->params) return 0; + /* channel needs configuring for mem=>device, increment memory addr, + * sync to pclk, half-word transfers to the IIS-FIFO. */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + s3c2410_dma_devconfig(prtd->params->channel, + S3C2410_DMASRC_MEM, S3C2410_DISRCC_INC | + S3C2410_DISRCC_APB, prtd->params->dma_addr); + + s3c2410_dma_config(prtd->params->channel, + prtd->params->dma_size, + S3C2410_DCON_SYNC_PCLK | + S3C2410_DCON_HANDSHAKE); + } else { + s3c2410_dma_config(prtd->params->channel, + prtd->params->dma_size, + S3C2410_DCON_HANDSHAKE | + S3C2410_DCON_SYNC_PCLK); + + s3c2410_dma_devconfig(prtd->params->channel, + S3C2410_DMASRC_HW, 0x3, + prtd->params->dma_addr); + } + /* flush the DMA channel */ s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_FLUSH); prtd->dma_loaded = 0; diff --git a/sound/soc/s3c24xx/smdk2443_wm9710.c b/sound/soc/s3c24xx/smdk2443_wm9710.c index d46cd81..b4a5630 100644 --- a/sound/soc/s3c24xx/smdk2443_wm9710.c +++ b/sound/soc/s3c24xx/smdk2443_wm9710.c @@ -17,7 +17,6 @@ #include <linux/module.h> #include <linux/device.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> diff --git a/sound/soc/sh/dma-sh7760.c b/sound/soc/sh/dma-sh7760.c index cdee374..7a3ce80 100644 --- a/sound/soc/sh/dma-sh7760.c +++ b/sound/soc/sh/dma-sh7760.c @@ -16,7 +16,6 @@ #include <linux/init.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> diff --git a/sound/soc/sh/hac.c b/sound/soc/sh/hac.c index 8e3f039..b7b676b 100644 --- a/sound/soc/sh/hac.c +++ b/sound/soc/sh/hac.c @@ -21,7 +21,6 @@ #include <linux/interrupt.h> #include <linux/wait.h> #include <linux/delay.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/ac97_codec.h> @@ -105,7 +104,7 @@ static int hac_get_codec_data(struct hac_priv *hac, unsigned short r, unsigned int to1, to2, i; unsigned short adr; - for (i = 0; i < AC97_READ_RETRY; ++i) { + for (i = AC97_READ_RETRY; i; i--) { *v = 0; /* wait for HAC to receive something from the codec */ for (to1 = TMO_E4; @@ -132,7 +131,7 @@ static int hac_get_codec_data(struct hac_priv *hac, unsigned short r, udelay(21); } HACREG(HACRSR) &= ~(RSR_STDRY | RSR_STARY); - return (i < AC97_READ_RETRY); + return i; } static unsigned short hac_read_codec_aux(struct hac_priv *hac, @@ -141,7 +140,7 @@ static unsigned short hac_read_codec_aux(struct hac_priv *hac, unsigned short val; unsigned int i, to; - for (i = 0; i < AC97_READ_RETRY; i++) { + for (i = AC97_READ_RETRY; i; i--) { /* send_read_request */ local_irq_disable(); HACREG(HACTSR) &= ~(TSR_CMDAMT); @@ -159,10 +158,7 @@ static unsigned short hac_read_codec_aux(struct hac_priv *hac, break; } - if (i == AC97_READ_RETRY) - return ~0; - - return val; + return i ? val : ~0; } static void hac_ac97_write(struct snd_ac97 *ac97, unsigned short reg, @@ -172,7 +168,7 @@ static void hac_ac97_write(struct snd_ac97 *ac97, unsigned short reg, struct hac_priv *hac = &hac_cpu_data[unit_id]; unsigned int i, to; /* write_codec_aux */ - for (i = 0; i < AC97_WRITE_RETRY; i++) { + for (i = AC97_WRITE_RETRY; i; i--) { /* send_write_request */ local_irq_disable(); HACREG(HACTSR) &= ~(TSR_CMDDMT | TSR_CMDAMT); diff --git a/sound/soc/sh/sh7760-ac97.c b/sound/soc/sh/sh7760-ac97.c index 5563f14..2f91de8 100644 --- a/sound/soc/sh/sh7760-ac97.c +++ b/sound/soc/sh/sh7760-ac97.c @@ -9,7 +9,6 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/platform_device.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> diff --git a/sound/soc/sh/ssi.c b/sound/soc/sh/ssi.c index b72bc31..3388bc3 100644 --- a/sound/soc/sh/ssi.c +++ b/sound/soc/sh/ssi.c @@ -30,7 +30,6 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/initval.h> diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index e6a67b5..9eb5479 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -32,7 +32,6 @@ #include <linux/pm.h> #include <linux/bitops.h> #include <linux/platform_device.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -288,16 +287,25 @@ static void close_delayed_work(struct work_struct *work) /* are we waiting on this codec DAI stream */ if (codec_dai->pop_wait == 1) { + /* power down the codec to D1 if no longer active */ + if (codec->active == 0) { + dbg("pop wq D1 %s %s\n", codec->name, + codec_dai->playback.stream_name); + snd_soc_dapm_device_event(socdev, + SNDRV_CTL_POWER_D1); + } + codec_dai->pop_wait = 0; - snd_soc_dapm_stream_event(codec, codec_dai->playback.stream_name, + snd_soc_dapm_stream_event(codec, + codec_dai->playback.stream_name, SND_SOC_DAPM_STREAM_STOP); /* power down the codec power domain if no longer active */ if (codec->active == 0) { dbg("pop wq D3 %s %s\n", codec->name, codec_dai->playback.stream_name); - if (codec->dapm_event) - codec->dapm_event(codec, SNDRV_CTL_POWER_D3hot); + snd_soc_dapm_device_event(socdev, + SNDRV_CTL_POWER_D3hot); } } } @@ -353,12 +361,12 @@ static int soc_codec_close(struct snd_pcm_substream *substream) } else { /* capture streams can be powered down now */ snd_soc_dapm_stream_event(codec, - codec_dai->capture.stream_name, SND_SOC_DAPM_STREAM_STOP); + codec_dai->capture.stream_name, + SND_SOC_DAPM_STREAM_STOP); - if (codec->active == 0 && codec_dai->pop_wait == 0){ - if (codec->dapm_event) - codec->dapm_event(codec, SNDRV_CTL_POWER_D3hot); - } + if (codec->active == 0 && codec_dai->pop_wait == 0) + snd_soc_dapm_device_event(socdev, + SNDRV_CTL_POWER_D3hot); } mutex_unlock(&pcm_mutex); @@ -433,8 +441,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) /* no delayed work - do we need to power up codec */ if (codec->dapm_state != SNDRV_CTL_POWER_D0) { - if (codec->dapm_event) - codec->dapm_event(codec, SNDRV_CTL_POWER_D1); + snd_soc_dapm_device_event(socdev, SNDRV_CTL_POWER_D1); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) snd_soc_dapm_stream_event(codec, @@ -445,8 +452,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) codec_dai->capture.stream_name, SND_SOC_DAPM_STREAM_START); - if (codec->dapm_event) - codec->dapm_event(codec, SNDRV_CTL_POWER_D0); + snd_soc_dapm_device_event(socdev, SNDRV_CTL_POWER_D0); if (codec_dai->dai_ops.digital_mute) codec_dai->dai_ops.digital_mute(codec_dai, 0); @@ -639,6 +645,10 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state) dai->dai_ops.digital_mute(dai, 1); } + /* suspend all pcms */ + for (i = 0; i < machine->num_links; i++) + snd_pcm_suspend_all(machine->dai_link[i].pcm); + if (machine->suspend_pre) machine->suspend_pre(pdev, state); @@ -873,6 +883,7 @@ static int soc_new_pcm(struct snd_soc_device *socdev, return ret; } + dai_link->pcm = pcm; pcm->private_data = rtd; soc_pcm_ops.mmap = socdev->platform->pcm_ops->mmap; soc_pcm_ops.pointer = socdev->platform->pcm_ops->pointer; @@ -1090,7 +1101,6 @@ int snd_soc_register_card(struct snd_soc_device *socdev) struct snd_soc_machine *machine = socdev->machine; int ret = 0, i, ac97 = 0, err = 0; - mutex_lock(&codec->mutex); for(i = 0; i < machine->num_links; i++) { if (socdev->machine->dai_link[i].init) { err = socdev->machine->dai_link[i].init(codec); @@ -1116,12 +1126,14 @@ int snd_soc_register_card(struct snd_soc_device *socdev) goto out; } + mutex_lock(&codec->mutex); #ifdef CONFIG_SND_SOC_AC97_BUS if (ac97) { ret = soc_ac97_dev_register(codec); if (ret < 0) { printk(KERN_ERR "asoc: AC97 device register failed\n"); snd_card_free(codec->card); + mutex_unlock(&codec->mutex); goto out; } } @@ -1134,8 +1146,10 @@ int snd_soc_register_card(struct snd_soc_device *socdev) err = device_create_file(socdev->dev, &dev_attr_codec_reg); if (err < 0) printk(KERN_WARNING "asoc: failed to add codec sysfs entries\n"); -out: + mutex_unlock(&codec->mutex); + +out: return ret; } EXPORT_SYMBOL_GPL(snd_soc_register_card); @@ -1215,7 +1229,6 @@ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, memcpy(&template, _template, sizeof(template)); if (long_name) template.name = long_name; - template.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; template.index = 0; return snd_ctl_new1(&template, data); @@ -1350,13 +1363,16 @@ EXPORT_SYMBOL_GPL(snd_soc_info_enum_ext); int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - int mask = kcontrol->private_value; + int max = kcontrol->private_value; + + if (max == 1) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->type = - mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 0; - uinfo->value.integer.max = mask; + uinfo->value.integer.max = max; return 0; } EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext); @@ -1373,15 +1389,18 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext); int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - int mask = (kcontrol->private_value >> 16) & 0xff; + int max = (kcontrol->private_value >> 16) & 0xff; int shift = (kcontrol->private_value >> 8) & 0x0f; int rshift = (kcontrol->private_value >> 12) & 0x0f; - uinfo->type = - mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + if (max == 1) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = shift == rshift ? 1 : 2; uinfo->value.integer.min = 0; - uinfo->value.integer.max = mask; + uinfo->value.integer.max = max; return 0; } EXPORT_SYMBOL_GPL(snd_soc_info_volsw); @@ -1402,7 +1421,8 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0x0f; int rshift = (kcontrol->private_value >> 12) & 0x0f; - int mask = (kcontrol->private_value >> 16) & 0xff; + int max = (kcontrol->private_value >> 16) & 0xff; + int mask = (1 << fls(max)) - 1; int invert = (kcontrol->private_value >> 24) & 0x01; ucontrol->value.integer.value[0] = @@ -1412,10 +1432,10 @@ int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, (snd_soc_read(codec, reg) >> rshift) & mask; if (invert) { ucontrol->value.integer.value[0] = - mask - ucontrol->value.integer.value[0]; + max - ucontrol->value.integer.value[0]; if (shift != rshift) ucontrol->value.integer.value[1] = - mask - ucontrol->value.integer.value[1]; + max - ucontrol->value.integer.value[1]; } return 0; @@ -1438,25 +1458,24 @@ int snd_soc_put_volsw(struct snd_kcontrol *kcontrol, int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0x0f; int rshift = (kcontrol->private_value >> 12) & 0x0f; - int mask = (kcontrol->private_value >> 16) & 0xff; + int max = (kcontrol->private_value >> 16) & 0xff; + int mask = (1 << fls(max)) - 1; int invert = (kcontrol->private_value >> 24) & 0x01; - int err; unsigned short val, val2, val_mask; val = (ucontrol->value.integer.value[0] & mask); if (invert) - val = mask - val; + val = max - val; val_mask = mask << shift; val = val << shift; if (shift != rshift) { val2 = (ucontrol->value.integer.value[1] & mask); if (invert) - val2 = mask - val2; + val2 = max - val2; val_mask |= mask << rshift; val |= val2 << rshift; } - err = snd_soc_update_bits(codec, reg, val_mask, val); - return err; + return snd_soc_update_bits(codec, reg, val_mask, val); } EXPORT_SYMBOL_GPL(snd_soc_put_volsw); @@ -1473,13 +1492,16 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw); int snd_soc_info_volsw_2r(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - int mask = (kcontrol->private_value >> 12) & 0xff; + int max = (kcontrol->private_value >> 12) & 0xff; + + if (max == 1) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->type = - mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; uinfo->value.integer.min = 0; - uinfo->value.integer.max = mask; + uinfo->value.integer.max = max; return 0; } EXPORT_SYMBOL_GPL(snd_soc_info_volsw_2r); @@ -1500,7 +1522,8 @@ int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol, int reg = kcontrol->private_value & 0xff; int reg2 = (kcontrol->private_value >> 24) & 0xff; int shift = (kcontrol->private_value >> 8) & 0x0f; - int mask = (kcontrol->private_value >> 12) & 0xff; + int max = (kcontrol->private_value >> 12) & 0xff; + int mask = (1<<fls(max))-1; int invert = (kcontrol->private_value >> 20) & 0x01; ucontrol->value.integer.value[0] = @@ -1509,9 +1532,9 @@ int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol, (snd_soc_read(codec, reg2) >> shift) & mask; if (invert) { ucontrol->value.integer.value[0] = - mask - ucontrol->value.integer.value[0]; + max - ucontrol->value.integer.value[0]; ucontrol->value.integer.value[1] = - mask - ucontrol->value.integer.value[1]; + max - ucontrol->value.integer.value[1]; } return 0; @@ -1534,7 +1557,8 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol, int reg = kcontrol->private_value & 0xff; int reg2 = (kcontrol->private_value >> 24) & 0xff; int shift = (kcontrol->private_value >> 8) & 0x0f; - int mask = (kcontrol->private_value >> 12) & 0xff; + int max = (kcontrol->private_value >> 12) & 0xff; + int mask = (1 << fls(max)) - 1; int invert = (kcontrol->private_value >> 20) & 0x01; int err; unsigned short val, val2, val_mask; @@ -1544,8 +1568,8 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol, val2 = (ucontrol->value.integer.value[1] & mask); if (invert) { - val = mask - val; - val2 = mask - val2; + val = max - val; + val2 = max - val2; } val = val << shift; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 29a546f..620d7ea 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -43,7 +43,6 @@ #include <linux/bitops.h> #include <linux/platform_device.h> #include <linux/jiffies.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -524,11 +523,13 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) continue; if (event == SND_SOC_DAPM_STREAM_START) { - ret = w->event(w, SND_SOC_DAPM_PRE_PMU); + ret = w->event(w, + NULL, SND_SOC_DAPM_PRE_PMU); if (ret < 0) return ret; } else if (event == SND_SOC_DAPM_STREAM_STOP) { - ret = w->event(w, SND_SOC_DAPM_PRE_PMD); + ret = w->event(w, + NULL, SND_SOC_DAPM_PRE_PMD); if (ret < 0) return ret; } @@ -539,11 +540,13 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) continue; if (event == SND_SOC_DAPM_STREAM_START) { - ret = w->event(w, SND_SOC_DAPM_POST_PMU); + ret = w->event(w, + NULL, SND_SOC_DAPM_POST_PMU); if (ret < 0) return ret; } else if (event == SND_SOC_DAPM_STREAM_STOP) { - ret = w->event(w, SND_SOC_DAPM_POST_PMD); + ret = w->event(w, + NULL, SND_SOC_DAPM_POST_PMD); if (ret < 0) return ret; } @@ -567,26 +570,30 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event) if (power) { /* power up event */ if (w->event_flags & SND_SOC_DAPM_PRE_PMU) { - ret = w->event(w, SND_SOC_DAPM_PRE_PMU); + ret = w->event(w, + NULL, SND_SOC_DAPM_PRE_PMU); if (ret < 0) return ret; } dapm_update_bits(w); if (w->event_flags & SND_SOC_DAPM_POST_PMU){ - ret = w->event(w, SND_SOC_DAPM_POST_PMU); + ret = w->event(w, + NULL, SND_SOC_DAPM_POST_PMU); if (ret < 0) return ret; } } else { /* power down event */ if (w->event_flags & SND_SOC_DAPM_PRE_PMD) { - ret = w->event(w, SND_SOC_DAPM_PRE_PMD); + ret = w->event(w, + NULL, SND_SOC_DAPM_PRE_PMD); if (ret < 0) return ret; } dapm_update_bits(w); if (w->event_flags & SND_SOC_DAPM_POST_PMD) { - ret = w->event(w, SND_SOC_DAPM_POST_PMD); + ret = w->event(w, + NULL, SND_SOC_DAPM_POST_PMD); if (ret < 0) return ret; } @@ -692,7 +699,7 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, return 0; } -/* test and update the power status of a mixer widget */ +/* test and update the power status of a mixer or switch widget */ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, struct snd_kcontrol *kcontrol, int reg, int val_mask, int val, int invert) @@ -700,7 +707,8 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, struct snd_soc_dapm_path *path; int found = 0; - if (widget->id != snd_soc_dapm_mixer) + if (widget->id != snd_soc_dapm_mixer && + widget->id != snd_soc_dapm_switch) return -ENODEV; if (!snd_soc_test_bits(widget->codec, reg, val_mask, val)) @@ -963,7 +971,6 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec) { struct snd_soc_dapm_widget *w; - mutex_lock(&codec->mutex); list_for_each_entry(w, &codec->dapm_widgets, list) { if (w->new) @@ -998,7 +1005,6 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec) } dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP); - mutex_unlock(&codec->mutex); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); @@ -1019,8 +1025,9 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0x0f; int rshift = (kcontrol->private_value >> 12) & 0x0f; - int mask = (kcontrol->private_value >> 16) & 0xff; + int max = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0x01; + int mask = (1 << fls(max)) - 1; /* return the saved value if we are powered down */ if (widget->id == snd_soc_dapm_pga && !widget->power) { @@ -1035,10 +1042,10 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, (snd_soc_read(widget->codec, reg) >> rshift) & mask; if (invert) { ucontrol->value.integer.value[0] = - mask - ucontrol->value.integer.value[0]; + max - ucontrol->value.integer.value[0]; if (shift != rshift) ucontrol->value.integer.value[1] = - mask - ucontrol->value.integer.value[1]; + max - ucontrol->value.integer.value[1]; } return 0; @@ -1061,7 +1068,8 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0x0f; int rshift = (kcontrol->private_value >> 12) & 0x0f; - int mask = (kcontrol->private_value >> 16) & 0xff; + int max = (kcontrol->private_value >> 16) & 0xff; + int mask = (1 << fls(max)) - 1; int invert = (kcontrol->private_value >> 24) & 0x01; unsigned short val, val2, val_mask; int ret; @@ -1069,13 +1077,13 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, val = (ucontrol->value.integer.value[0] & mask); if (invert) - val = mask - val; + val = max - val; val_mask = mask << shift; val = val << shift; if (shift != rshift) { val2 = (ucontrol->value.integer.value[1] & mask); if (invert) - val2 = mask - val2; + val2 = max - val2; val_mask |= mask << rshift; val |= val2 << rshift; } @@ -1093,13 +1101,17 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, dapm_mixer_update_power(widget, kcontrol, reg, val_mask, val, invert); if (widget->event) { if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { - ret = widget->event(widget, SND_SOC_DAPM_PRE_REG); - if (ret < 0) + ret = widget->event(widget, kcontrol, + SND_SOC_DAPM_PRE_REG); + if (ret < 0) { + ret = 1; goto out; + } } ret = snd_soc_update_bits(widget->codec, reg, val_mask, val); if (widget->event_flags & SND_SOC_DAPM_POST_REG) - ret = widget->event(widget, SND_SOC_DAPM_POST_REG); + ret = widget->event(widget, kcontrol, + SND_SOC_DAPM_POST_REG); } else ret = snd_soc_update_bits(widget->codec, reg, val_mask, val); @@ -1174,13 +1186,15 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, dapm_mux_update_power(widget, kcontrol, mask, mux, e); if (widget->event) { if (widget->event_flags & SND_SOC_DAPM_PRE_REG) { - ret = widget->event(widget, SND_SOC_DAPM_PRE_REG); + ret = widget->event(widget, + kcontrol, SND_SOC_DAPM_PRE_REG); if (ret < 0) goto out; } ret = snd_soc_update_bits(widget->codec, e->reg, mask, val); if (widget->event_flags & SND_SOC_DAPM_POST_REG) - ret = widget->event(widget, SND_SOC_DAPM_POST_REG); + ret = widget->event(widget, + kcontrol, SND_SOC_DAPM_POST_REG); } else ret = snd_soc_update_bits(widget->codec, e->reg, mask, val); @@ -1280,6 +1294,29 @@ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event); /** + * snd_soc_dapm_device_event - send a device event to the dapm core + * @socdev: audio device + * @event: device event + * + * Sends a device event to the dapm core. The core then makes any + * necessary machine or codec power changes.. + * + * Returns 0 for success else error. + */ +int snd_soc_dapm_device_event(struct snd_soc_device *socdev, int event) +{ + struct snd_soc_codec *codec = socdev->codec; + struct snd_soc_machine *machine = socdev->machine; + + if (machine->dapm_event) + machine->dapm_event(machine, event); + if (codec->dapm_event) + codec->dapm_event(codec, event); + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_device_event); + +/** * snd_soc_dapm_set_endpoint - set audio endpoint status * @codec: audio codec * @endpoint: audio signal endpoint (or start point) diff --git a/sound/sparc/amd7930.c b/sound/sparc/amd7930.c index 07962a3..0c63e05 100644 --- a/sound/sparc/amd7930.c +++ b/sound/sparc/amd7930.c @@ -36,7 +36,6 @@ #include <linux/interrupt.h> #include <linux/moduleparam.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/info.h> @@ -859,7 +858,7 @@ static int snd_amd7930_put_volume(struct snd_kcontrol *kctl, struct snd_ctl_elem spin_lock_irqsave(&amd->lock, flags); if (*swval != ucontrol->value.integer.value[0]) { - *swval = ucontrol->value.integer.value[0]; + *swval = ucontrol->value.integer.value[0] & 0xff; __amd7930_update_map(amd); change = 1; } else diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c index f8c7a12..1c4797b 100644 --- a/sound/sparc/cs4231.c +++ b/sound/sparc/cs4231.c @@ -19,7 +19,6 @@ #include <linux/io.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/info.h> diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c index 376b986..3d00e07 100644 --- a/sound/sparc/dbri.c +++ b/sound/sparc/dbri.c @@ -53,7 +53,6 @@ * other DBRI low-level stuff */ -#include <sound/driver.h> #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/irq.h> @@ -2279,14 +2278,25 @@ static int snd_cs4215_put_volume(struct snd_kcontrol *kcontrol, struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol); struct dbri_streaminfo *info = &dbri->stream_info[kcontrol->private_value]; + unsigned int vol[2]; int changed = 0; - if (info->left_gain != ucontrol->value.integer.value[0]) { - info->left_gain = ucontrol->value.integer.value[0]; + vol[0] = ucontrol->value.integer.value[0]; + vol[1] = ucontrol->value.integer.value[1]; + if (kcontrol->private_value == DBRI_PLAY) { + if (vol[0] > DBRI_MAX_VOLUME || vol[1] > DBRI_MAX_VOLUME) + return -EINVAL; + } else { + if (vol[0] > DBRI_MAX_GAIN || vol[1] > DBRI_MAX_GAIN) + return -EINVAL; + } + + if (info->left_gain != vol[0]) { + info->left_gain = vol[0]; changed = 1; } - if (info->right_gain != ucontrol->value.integer.value[1]) { - info->right_gain = ucontrol->value.integer.value[1]; + if (info->right_gain != vol[1]) { + info->right_gain = vol[1]; changed = 1; } if (changed) { diff --git a/sound/spi/at73c213.c b/sound/spi/at73c213.c index fee869b..89d6e9c 100644 --- a/sound/spi/at73c213.c +++ b/sound/spi/at73c213.c @@ -18,10 +18,10 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/platform_device.h> #include <linux/io.h> -#include <sound/driver.h> #include <sound/initval.h> #include <sound/control.h> #include <sound/core.h> @@ -76,8 +76,10 @@ struct snd_at73c213 { u8 spi_rbuffer[2]; /* Image of the SPI registers in AT73C213. */ u8 reg_image[18]; - /* Protect registers against concurrent access. */ + /* Protect SSC registers against concurrent access. */ spinlock_t lock; + /* Protect mixer registers against concurrent access. */ + struct mutex mixer_lock; }; #define get_chip(card) ((struct snd_at73c213 *)card->private_data) @@ -398,7 +400,7 @@ static int snd_at73c213_mono_get(struct snd_kcontrol *kcontrol, int mask = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0xff; - spin_lock_irq(&chip->lock); + mutex_lock(&chip->mixer_lock); ucontrol->value.integer.value[0] = (chip->reg_image[reg] >> shift) & mask; @@ -407,7 +409,7 @@ static int snd_at73c213_mono_get(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; - spin_unlock_irq(&chip->lock); + mutex_unlock(&chip->mixer_lock); return 0; } @@ -428,13 +430,13 @@ static int snd_at73c213_mono_put(struct snd_kcontrol *kcontrol, val = mask - val; val <<= shift; - spin_lock_irq(&chip->lock); + mutex_lock(&chip->mixer_lock); val = (chip->reg_image[reg] & ~(mask << shift)) | val; change = val != chip->reg_image[reg]; retval = snd_at73c213_write_reg(chip, reg, val); - spin_unlock_irq(&chip->lock); + mutex_unlock(&chip->mixer_lock); if (retval) return retval; @@ -470,7 +472,7 @@ static int snd_at73c213_stereo_get(struct snd_kcontrol *kcontrol, int mask = (kcontrol->private_value >> 24) & 0xff; int invert = (kcontrol->private_value >> 22) & 1; - spin_lock_irq(&chip->lock); + mutex_lock(&chip->mixer_lock); ucontrol->value.integer.value[0] = (chip->reg_image[left_reg] >> shift_left) & mask; @@ -484,7 +486,7 @@ static int snd_at73c213_stereo_get(struct snd_kcontrol *kcontrol, mask - ucontrol->value.integer.value[1]; } - spin_unlock_irq(&chip->lock); + mutex_unlock(&chip->mixer_lock); return 0; } @@ -511,7 +513,7 @@ static int snd_at73c213_stereo_put(struct snd_kcontrol *kcontrol, val1 <<= shift_left; val2 <<= shift_right; - spin_lock_irq(&chip->lock); + mutex_lock(&chip->mixer_lock); val1 = (chip->reg_image[left_reg] & ~(mask << shift_left)) | val1; val2 = (chip->reg_image[right_reg] & ~(mask << shift_right)) | val2; @@ -519,16 +521,16 @@ static int snd_at73c213_stereo_put(struct snd_kcontrol *kcontrol, || val2 != chip->reg_image[right_reg]; retval = snd_at73c213_write_reg(chip, left_reg, val1); if (retval) { - spin_unlock_irq(&chip->lock); + mutex_unlock(&chip->mixer_lock); goto out; } retval = snd_at73c213_write_reg(chip, right_reg, val2); if (retval) { - spin_unlock_irq(&chip->lock); + mutex_unlock(&chip->mixer_lock); goto out; } - spin_unlock_irq(&chip->lock); + mutex_unlock(&chip->mixer_lock); return change; @@ -536,16 +538,7 @@ out: return retval; } -static int snd_at73c213_mono_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 snd_at73c213_mono_switch_info snd_ctl_boolean_mono_info static int snd_at73c213_mono_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -555,7 +548,7 @@ static int snd_at73c213_mono_switch_get(struct snd_kcontrol *kcontrol, int shift = (kcontrol->private_value >> 8) & 0xff; int invert = (kcontrol->private_value >> 24) & 0xff; - spin_lock_irq(&chip->lock); + mutex_lock(&chip->mixer_lock); ucontrol->value.integer.value[0] = (chip->reg_image[reg] >> shift) & 0x01; @@ -564,7 +557,7 @@ static int snd_at73c213_mono_switch_get(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[0] = 0x01 - ucontrol->value.integer.value[0]; - spin_unlock_irq(&chip->lock); + mutex_unlock(&chip->mixer_lock); return 0; } @@ -589,14 +582,14 @@ static int snd_at73c213_mono_switch_put(struct snd_kcontrol *kcontrol, val = mask - val; val <<= shift; - spin_lock_irq(&chip->lock); + mutex_lock(&chip->mixer_lock); val |= (chip->reg_image[reg] & ~(mask << shift)); change = val != chip->reg_image[reg]; retval = snd_at73c213_write_reg(chip, reg, val); - spin_unlock_irq(&chip->lock); + mutex_unlock(&chip->mixer_lock); if (retval) return retval; @@ -893,6 +886,7 @@ static int __devinit snd_at73c213_dev_init(struct snd_card *card, return irq; spin_lock_init(&chip->lock); + mutex_init(&chip->mixer_lock); chip->card = card; chip->irq = -1; diff --git a/sound/synth/emux/emux.c b/sound/synth/emux/emux.c index ebcac13..c89d2ea 100644 --- a/sound/synth/emux/emux.c +++ b/sound/synth/emux/emux.c @@ -18,7 +18,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/wait.h> #include <linux/slab.h> #include <linux/string.h> diff --git a/sound/synth/emux/emux_hwdep.c b/sound/synth/emux/emux_hwdep.c index 9b63814..0a53914 100644 --- a/sound/synth/emux/emux_hwdep.c +++ b/sound/synth/emux/emux_hwdep.c @@ -19,7 +19,6 @@ * */ -#include <sound/driver.h> #include <sound/core.h> #include <sound/hwdep.h> #include <asm/uaccess.h> diff --git a/sound/synth/emux/emux_oss.c b/sound/synth/emux/emux_oss.c index 3436816..f60a98e 100644 --- a/sound/synth/emux/emux_oss.c +++ b/sound/synth/emux/emux_oss.c @@ -22,7 +22,6 @@ * midi emulation. */ -#include <sound/driver.h> #ifdef CONFIG_SND_SEQUENCER_OSS diff --git a/sound/synth/emux/emux_proc.c b/sound/synth/emux/emux_proc.c index 680f2b7..687e6a1 100644 --- a/sound/synth/emux/emux_proc.c +++ b/sound/synth/emux/emux_proc.c @@ -18,7 +18,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/wait.h> #include <linux/slab.h> #include <sound/core.h> diff --git a/sound/synth/emux/emux_voice.h b/sound/synth/emux/emux_voice.h index 0a56ca18..09711f8 100644 --- a/sound/synth/emux/emux_voice.h +++ b/sound/synth/emux/emux_voice.h @@ -22,7 +22,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/wait.h> #include <linux/sched.h> #include <sound/core.h> diff --git a/sound/synth/emux/soundfont.c b/sound/synth/emux/soundfont.c index 455e535..36d53bd 100644 --- a/sound/synth/emux/soundfont.c +++ b/sound/synth/emux/soundfont.c @@ -25,7 +25,6 @@ * of doing things so that the old sfxload utility can be used. * Everything may change when there is an alsa way of doing things. */ -#include <sound/driver.h> #include <asm/uaccess.h> #include <linux/slab.h> #include <sound/core.h> diff --git a/sound/synth/util_mem.c b/sound/synth/util_mem.c index 6fc3d2b..deabe5f 100644 --- a/sound/synth/util_mem.c +++ b/sound/synth/util_mem.c @@ -19,7 +19,6 @@ */ #include <linux/mutex.h> -#include <sound/driver.h> #include <linux/init.h> #include <linux/slab.h> #include <sound/core.h> diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig index 7061438..9351b8a 100644 --- a/sound/usb/Kconfig +++ b/sound/usb/Kconfig @@ -31,17 +31,18 @@ config SND_USB_USX2Y config SND_USB_CAIAQ tristate "Native Instruments USB audio devices" - depends on SND && USB - select SND_HWDEP - select SND_RAWMIDI - select SND_PCM - help + depends on SND && USB + select SND_HWDEP + select SND_RAWMIDI + select SND_PCM + help Say Y here to include support for caiaq USB audio interfaces, namely: * Native Instruments RigKontrol2 * Native Instruments RigKontrol3 * Native Instruments Kore Controller + * Native Instruments Kore Controller 2 * Native Instruments Audio Kontrol 1 * Native Instruments Audio 8 DJ @@ -51,12 +52,15 @@ config SND_USB_CAIAQ config SND_USB_CAIAQ_INPUT bool "enable input device for controllers" depends on SND_USB_CAIAQ + depends on INPUT=y || INPUT=SND_USB_CAIAQ help Say Y here to support input controllers like buttons, knobs, alpha dials and analog pedals on the following products: * Native Instruments RigKontrol2 * Native Instruments RigKontrol3 + * Native Instruments Kore Controller + * Native Instruments Kore Controller 2 * Native Instruments Audio Kontrol 1 endmenu diff --git a/sound/usb/caiaq/Makefile b/sound/usb/caiaq/Makefile index 455c8c5..23dadd5 100644 --- a/sound/usb/caiaq/Makefile +++ b/sound/usb/caiaq/Makefile @@ -1,3 +1,4 @@ -snd-usb-caiaq-objs := caiaq-device.o caiaq-audio.o caiaq-midi.o caiaq-input.o +snd-usb-caiaq-y := caiaq-device.o caiaq-audio.o caiaq-midi.o caiaq-control.o +snd-usb-caiaq-$(CONFIG_SND_USB_CAIAQ_INPUT) += caiaq-input.o obj-$(CONFIG_SND_USB_CAIAQ) += snd-usb-caiaq.o diff --git a/sound/usb/caiaq/caiaq-audio.c b/sound/usb/caiaq/caiaq-audio.c index 0666908..9cc4cd8 100644 --- a/sound/usb/caiaq/caiaq-audio.c +++ b/sound/usb/caiaq/caiaq-audio.c @@ -16,7 +16,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -27,9 +26,7 @@ #include <sound/initval.h> #include <sound/pcm.h> #include <sound/rawmidi.h> -#ifdef CONFIG_SND_USB_CAIAQ_INPUT #include <linux/input.h> -#endif #include "caiaq-device.h" #include "caiaq-audio.h" @@ -60,7 +57,7 @@ static struct snd_pcm_hardware snd_usb_caiaq_pcm_hardware = { .channels_min = CHANNELS_PER_STREAM, .channels_max = CHANNELS_PER_STREAM, .buffer_bytes_max = MAX_BUFFER_SIZE, - .period_bytes_min = 4096, + .period_bytes_min = 128, .period_bytes_max = MAX_BUFFER_SIZE, .periods_min = 1, .periods_max = 1024, @@ -606,7 +603,7 @@ static void free_urbs(struct urb **urbs) kfree(urbs); } -int __devinit snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev) +int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev) { int i, ret; diff --git a/sound/usb/caiaq/caiaq-control.c b/sound/usb/caiaq/caiaq-control.c new file mode 100644 index 0000000..798ca12 --- /dev/null +++ b/sound/usb/caiaq/caiaq-control.c @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2007 Daniel Mack + * friendly supported by NI. + * + * 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/init.h> +#include <linux/interrupt.h> +#include <linux/usb.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/rawmidi.h> +#include <sound/control.h> +#include <linux/input.h> + +#include "caiaq-device.h" +#include "caiaq-control.h" + +#define CNT_INTVAL 0x10000 + +static int control_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_usb_audio *chip = snd_kcontrol_chip(kcontrol); + struct snd_usb_caiaqdev *dev = caiaqdev(chip->card); + int pos = kcontrol->private_value; + int is_intval = pos & CNT_INTVAL; + + uinfo->count = 1; + pos &= ~CNT_INTVAL; + + if (dev->chip.usb_id == + USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ) + && (pos == 0)) { + /* current input mode of A8DJ */ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 2; + return 0; + } + + if (is_intval) { + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 64; + } else { + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + } + + return 0; +} + +static int control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_usb_audio *chip = snd_kcontrol_chip(kcontrol); + struct snd_usb_caiaqdev *dev = caiaqdev(chip->card); + int pos = kcontrol->private_value; + + if (pos & CNT_INTVAL) + ucontrol->value.integer.value[0] + = dev->control_state[pos & ~CNT_INTVAL]; + else + ucontrol->value.integer.value[0] + = !!(dev->control_state[pos / 8] & (1 << pos % 8)); + + return 0; +} + +static int control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_usb_audio *chip = snd_kcontrol_chip(kcontrol); + struct snd_usb_caiaqdev *dev = caiaqdev(chip->card); + int pos = kcontrol->private_value; + + if (pos & CNT_INTVAL) { + dev->control_state[pos & ~CNT_INTVAL] + = ucontrol->value.integer.value[0]; + snd_usb_caiaq_send_command(dev, EP1_CMD_DIMM_LEDS, + dev->control_state, sizeof(dev->control_state)); + } else { + if (ucontrol->value.integer.value[0]) + dev->control_state[pos / 8] |= 1 << (pos % 8); + else + dev->control_state[pos / 8] &= ~(1 << (pos % 8)); + + snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, + dev->control_state, sizeof(dev->control_state)); + } + + return 1; +} + +static struct snd_kcontrol_new kcontrol_template __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .index = 0, + .info = control_info, + .get = control_get, + .put = control_put, + /* name and private_value filled later */ +}; + +struct caiaq_controller { + char *name; + int index; +}; + +static struct caiaq_controller ak1_controller[] = { + { "LED left", 2 }, + { "LED middle", 1 }, + { "LED right", 0 }, + { "LED ring", 3 } +}; + +static struct caiaq_controller rk2_controller[] = { + { "LED 1", 5 }, + { "LED 2", 4 }, + { "LED 3", 3 }, + { "LED 4", 2 }, + { "LED 5", 1 }, + { "LED 6", 0 }, + { "LED pedal", 6 }, + { "LED 7seg_1b", 8 }, + { "LED 7seg_1c", 9 }, + { "LED 7seg_2a", 10 }, + { "LED 7seg_2b", 11 }, + { "LED 7seg_2c", 12 }, + { "LED 7seg_2d", 13 }, + { "LED 7seg_2e", 14 }, + { "LED 7seg_2f", 15 }, + { "LED 7seg_2g", 16 }, + { "LED 7seg_3a", 17 }, + { "LED 7seg_3b", 18 }, + { "LED 7seg_3c", 19 }, + { "LED 7seg_3d", 20 }, + { "LED 7seg_3e", 21 }, + { "LED 7seg_3f", 22 }, + { "LED 7seg_3g", 23 } +}; + +static struct caiaq_controller rk3_controller[] = { + { "LED 7seg_1a", 0 + 0 }, + { "LED 7seg_1b", 0 + 1 }, + { "LED 7seg_1c", 0 + 2 }, + { "LED 7seg_1d", 0 + 3 }, + { "LED 7seg_1e", 0 + 4 }, + { "LED 7seg_1f", 0 + 5 }, + { "LED 7seg_1g", 0 + 6 }, + { "LED 7seg_1p", 0 + 7 }, + + { "LED 7seg_2a", 8 + 0 }, + { "LED 7seg_2b", 8 + 1 }, + { "LED 7seg_2c", 8 + 2 }, + { "LED 7seg_2d", 8 + 3 }, + { "LED 7seg_2e", 8 + 4 }, + { "LED 7seg_2f", 8 + 5 }, + { "LED 7seg_2g", 8 + 6 }, + { "LED 7seg_2p", 8 + 7 }, + + { "LED 7seg_3a", 16 + 0 }, + { "LED 7seg_3b", 16 + 1 }, + { "LED 7seg_3c", 16 + 2 }, + { "LED 7seg_3d", 16 + 3 }, + { "LED 7seg_3e", 16 + 4 }, + { "LED 7seg_3f", 16 + 5 }, + { "LED 7seg_3g", 16 + 6 }, + { "LED 7seg_3p", 16 + 7 }, + + { "LED 7seg_4a", 24 + 0 }, + { "LED 7seg_4b", 24 + 1 }, + { "LED 7seg_4c", 24 + 2 }, + { "LED 7seg_4d", 24 + 3 }, + { "LED 7seg_4e", 24 + 4 }, + { "LED 7seg_4f", 24 + 5 }, + { "LED 7seg_4g", 24 + 6 }, + { "LED 7seg_4p", 24 + 7 }, + + { "LED 1", 32 + 0 }, + { "LED 2", 32 + 1 }, + { "LED 3", 32 + 2 }, + { "LED 4", 32 + 3 }, + { "LED 5", 32 + 4 }, + { "LED 6", 32 + 5 }, + { "LED 7", 32 + 6 }, + { "LED 8", 32 + 7 }, + { "LED pedal", 32 + 8 } +}; + +static struct caiaq_controller kore_controller[] = { + { "LED F1", 8 | CNT_INTVAL }, + { "LED F2", 12 | CNT_INTVAL }, + { "LED F3", 0 | CNT_INTVAL }, + { "LED F4", 4 | CNT_INTVAL }, + { "LED F5", 11 | CNT_INTVAL }, + { "LED F6", 15 | CNT_INTVAL }, + { "LED F7", 3 | CNT_INTVAL }, + { "LED F8", 7 | CNT_INTVAL }, + { "LED touch1", 10 | CNT_INTVAL }, + { "LED touch2", 14 | CNT_INTVAL }, + { "LED touch3", 2 | CNT_INTVAL }, + { "LED touch4", 6 | CNT_INTVAL }, + { "LED touch5", 9 | CNT_INTVAL }, + { "LED touch6", 13 | CNT_INTVAL }, + { "LED touch7", 1 | CNT_INTVAL }, + { "LED touch8", 5 | CNT_INTVAL }, + { "LED left", 18 | CNT_INTVAL }, + { "LED right", 22 | CNT_INTVAL }, + { "LED up", 16 | CNT_INTVAL }, + { "LED down", 20 | CNT_INTVAL }, + { "LED stop", 23 | CNT_INTVAL }, + { "LED play", 21 | CNT_INTVAL }, + { "LED record", 19 | CNT_INTVAL }, + { "LED listen", 17 | CNT_INTVAL }, + { "LED lcd", 30 | CNT_INTVAL }, + { "LED menu", 28 | CNT_INTVAL }, + { "LED sound", 31 | CNT_INTVAL }, + { "LED esc", 29 | CNT_INTVAL }, + { "LED view", 27 | CNT_INTVAL }, + { "LED enter", 24 | CNT_INTVAL }, + { "LED control", 26 | CNT_INTVAL } +}; + +static struct caiaq_controller a8dj_controller[] = { + { "Current input mode", 0 | CNT_INTVAL }, + { "GND lift for TC Vinyl mode", 24 + 0 }, + { "GND lift for TC CD/Line mode", 24 + 1 }, + { "GND lift for phono mode", 24 + 2 }, + { "GND lift for TC Vinyl mode", 24 + 3 }, + { "Software lock", 40 } +}; + +int __devinit snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *dev) +{ + int i; + struct snd_kcontrol *kc; + + switch (dev->chip.usb_id) { + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1): + for (i = 0; i < ARRAY_SIZE(ak1_controller); i++) { + struct caiaq_controller *c = ak1_controller + i; + kcontrol_template.name = c->name; + kcontrol_template.private_value = c->index; + kc = snd_ctl_new1(&kcontrol_template, dev); + snd_ctl_add(dev->chip.card, kc); + } + + break; + + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2): + for (i = 0; i < ARRAY_SIZE(rk2_controller); i++) { + struct caiaq_controller *c = rk2_controller + i; + kcontrol_template.name = c->name; + kcontrol_template.private_value = c->index; + kc = snd_ctl_new1(&kcontrol_template, dev); + snd_ctl_add(dev->chip.card, kc); + } + + break; + + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3): + for (i = 0; i < ARRAY_SIZE(rk3_controller); i++) { + struct caiaq_controller *c = rk3_controller + i; + kcontrol_template.name = c->name; + kcontrol_template.private_value = c->index; + kc = snd_ctl_new1(&kcontrol_template, dev); + snd_ctl_add(dev->chip.card, kc); + } + + break; + + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER): + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2): + for (i = 0; i < ARRAY_SIZE(kore_controller); i++) { + struct caiaq_controller *c = kore_controller + i; + kcontrol_template.name = c->name; + kcontrol_template.private_value = c->index; + kc = snd_ctl_new1(&kcontrol_template, dev); + snd_ctl_add(dev->chip.card, kc); + } + + break; + + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ): + for (i = 0; i < ARRAY_SIZE(a8dj_controller); i++) { + struct caiaq_controller *c = a8dj_controller + i; + kcontrol_template.name = c->name; + kcontrol_template.private_value = c->index; + kc = snd_ctl_new1(&kcontrol_template, dev); + snd_ctl_add(dev->chip.card, kc); + } + + break; + } + + return 0; +} + diff --git a/sound/usb/caiaq/caiaq-control.h b/sound/usb/caiaq/caiaq-control.h new file mode 100644 index 0000000..2e7ab1a --- /dev/null +++ b/sound/usb/caiaq/caiaq-control.h @@ -0,0 +1,6 @@ +#ifndef CAIAQ_CONTROL_H +#define CAIAQ_CONTROL_H + +int snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *dev); + +#endif /* CAIAQ_CONTROL_H */ diff --git a/sound/usb/caiaq/caiaq-device.c b/sound/usb/caiaq/caiaq-device.c index 58af814..58d25e4 100644 --- a/sound/usb/caiaq/caiaq-device.c +++ b/sound/usb/caiaq/caiaq-device.c @@ -26,26 +26,28 @@ #include <linux/usb.h> #include <linux/input.h> #include <linux/spinlock.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/initval.h> #include <sound/pcm.h> #include <sound/rawmidi.h> +#include <sound/control.h> #include "caiaq-device.h" #include "caiaq-audio.h" #include "caiaq-midi.h" +#include "caiaq-control.h" #ifdef CONFIG_SND_USB_CAIAQ_INPUT #include "caiaq-input.h" #endif MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); -MODULE_DESCRIPTION("caiaq USB audio, version 1.2.0"); +MODULE_DESCRIPTION("caiaq USB audio, version 1.3.2"); MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2}," "{Native Instruments, RigKontrol3}," "{Native Instruments, Kore Controller}," + "{Native Instruments, Kore Controller 2}," "{Native Instruments, Audio Kontrol 1}" "{Native Instruments, Audio 8 DJ}}"); @@ -94,6 +96,11 @@ static struct usb_device_id snd_usb_id_table[] = { .idProduct = USB_PID_KORECONTROLLER }, { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = USB_VID_NATIVEINSTRUMENTS, + .idProduct = USB_PID_KORECONTROLLER2 + }, + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE, .idVendor = USB_VID_NATIVEINSTRUMENTS, .idProduct = USB_PID_AK1 @@ -140,14 +147,21 @@ static void usb_ep1_command_reply_dispatch (struct urb* urb) case EP1_CMD_MIDI_READ: snd_usb_caiaq_midi_handle_input(dev, buf[1], buf + 3, buf[2]); break; - + case EP1_CMD_READ_IO: + if (dev->chip.usb_id == + USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ)) { + if (urb->actual_length > sizeof(dev->control_state)) + urb->actual_length = sizeof(dev->control_state); + memcpy(dev->control_state, buf + 1, urb->actual_length); + wake_up(&dev->ep1_wait_queue); + break; + } #ifdef CONFIG_SND_USB_CAIAQ_INPUT case EP1_CMD_READ_ERP: case EP1_CMD_READ_ANALOG: - case EP1_CMD_READ_IO: snd_usb_caiaq_input_dispatch(dev, buf, urb->actual_length); - break; #endif + break; } dev->ep1_in_urb.actual_length = 0; @@ -156,10 +170,10 @@ static void usb_ep1_command_reply_dispatch (struct urb* urb) log("unable to submit urb. OOM!?\n"); } -static int send_command (struct snd_usb_caiaqdev *dev, - unsigned char command, - const unsigned char *buffer, - int len) +int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *dev, + unsigned char command, + const unsigned char *buffer, + int len) { int actual_len; struct usb_device *usb_dev = dev->chip.dev; @@ -207,7 +221,8 @@ int snd_usb_caiaq_set_audio_params (struct snd_usb_caiaqdev *dev, rate, depth, bpp); dev->audio_parm_answer = -1; - ret = send_command(dev, EP1_CMD_AUDIO_PARAMS, tmp, sizeof(tmp)); + ret = snd_usb_caiaq_send_command(dev, EP1_CMD_AUDIO_PARAMS, + tmp, sizeof(tmp)); if (ret) return ret; @@ -226,7 +241,8 @@ int snd_usb_caiaq_set_auto_msg (struct snd_usb_caiaqdev *dev, int digital, int analog, int erp) { char tmp[3] = { digital, analog, erp }; - return send_command(dev, EP1_CMD_AUTO_MSG, tmp, sizeof(tmp)); + return snd_usb_caiaq_send_command(dev, EP1_CMD_AUTO_MSG, + tmp, sizeof(tmp)); } static void setup_card(struct snd_usb_caiaqdev *dev) @@ -241,7 +257,7 @@ static void setup_card(struct snd_usb_caiaqdev *dev) val[0] = 0x00; val[1] = 0x00; val[2] = 0x01; - send_command(dev, EP1_CMD_WRITE_IO, val, 3); + snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, val, 3); break; case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3): /* RigKontrol2 - display two centered dashes ('--') */ @@ -249,22 +265,52 @@ static void setup_card(struct snd_usb_caiaqdev *dev) val[1] = 0x40; val[2] = 0x40; val[3] = 0x00; - send_command(dev, EP1_CMD_WRITE_IO, val, 4); + snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, val, 4); break; case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1): /* Audio Kontrol 1 - make USB-LED stop blinking */ val[0] = 0x00; - send_command(dev, EP1_CMD_WRITE_IO, val, 1); + snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO, val, 1); + break; + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ): + /* Audio 8 DJ - trigger read of current settings */ + dev->control_state[0] = 0xff; + snd_usb_caiaq_set_auto_msg(dev, 1, 0, 0); + snd_usb_caiaq_send_command(dev, EP1_CMD_READ_IO, NULL, 0); + + if (!wait_event_timeout(dev->ep1_wait_queue, + dev->control_state[0] != 0xff, HZ)) + return; + + /* fix up some defaults */ + if ((dev->control_state[1] != 2) || + (dev->control_state[2] != 3) || + (dev->control_state[4] != 2)) { + dev->control_state[1] = 2; + dev->control_state[2] = 3; + dev->control_state[4] = 2; + snd_usb_caiaq_send_command(dev, + EP1_CMD_WRITE_IO, dev->control_state, 6); + } + break; } - ret = snd_usb_caiaq_audio_init(dev); - if (ret < 0) - log("Unable to set up audio system (ret=%d)\n", ret); + if (dev->spec.num_analog_audio_out + + dev->spec.num_analog_audio_in + + dev->spec.num_digital_audio_out + + dev->spec.num_digital_audio_in > 0) { + ret = snd_usb_caiaq_audio_init(dev); + if (ret < 0) + log("Unable to set up audio system (ret=%d)\n", ret); + } - ret = snd_usb_caiaq_midi_init(dev); - if (ret < 0) - log("Unable to set up MIDI system (ret=%d)\n", ret); + if (dev->spec.num_midi_in + + dev->spec.num_midi_out > 0) { + ret = snd_usb_caiaq_midi_init(dev); + if (ret < 0) + log("Unable to set up MIDI system (ret=%d)\n", ret); + } #ifdef CONFIG_SND_USB_CAIAQ_INPUT ret = snd_usb_caiaq_input_init(dev); @@ -278,6 +324,10 @@ static void setup_card(struct snd_usb_caiaqdev *dev) log("snd_card_register() returned %d\n", ret); snd_card_free(dev->chip.card); } + + ret = snd_usb_caiaq_control_init(dev); + if (ret < 0) + log("Unable to set up control system (ret=%d)\n", ret); } static struct snd_card* create_card(struct usb_device* usb_dev) @@ -340,7 +390,7 @@ static int init_card(struct snd_usb_caiaqdev *dev) if (usb_submit_urb(&dev->ep1_in_urb, GFP_KERNEL) != 0) return -EIO; - err = send_command(dev, EP1_CMD_GET_DEVICE_INFO, NULL, 0); + err = snd_usb_caiaq_send_command(dev, EP1_CMD_GET_DEVICE_INFO, NULL, 0); if (err) return err; diff --git a/sound/usb/caiaq/caiaq-device.h b/sound/usb/caiaq/caiaq-device.h index 79bc5be..96a4913 100644 --- a/sound/usb/caiaq/caiaq-device.h +++ b/sound/usb/caiaq/caiaq-device.h @@ -7,7 +7,8 @@ #define USB_PID_RIGKONTROL2 0x1969 #define USB_PID_RIGKONTROL3 0x1940 -#define USB_PID_KORECONTROLLER 0x4711 +#define USB_PID_KORECONTROLLER 0x4711 +#define USB_PID_KORECONTROLLER2 0x4712 #define USB_PID_AK1 0x0815 #define USB_PID_AUDIO8DJ 0x1978 @@ -35,6 +36,7 @@ #define EP1_CMD_MIDI_WRITE 0x7 #define EP1_CMD_AUDIO_PARAMS 0x9 #define EP1_CMD_AUTO_MSG 0xb +#define EP1_CMD_DIMM_LEDS 0xc struct caiaq_device_spec { unsigned short fw_version; @@ -62,7 +64,7 @@ struct snd_usb_caiaqdev { struct urb **data_urbs_in; struct urb **data_urbs_out; struct snd_usb_caiaq_cb_info *data_cb_info; - + unsigned char ep1_in_buf[EP1_BUFSIZE]; unsigned char ep1_out_buf[EP1_BUFSIZE]; unsigned char midi_out_buf[EP1_BUFSIZE]; @@ -72,7 +74,7 @@ struct snd_usb_caiaqdev { wait_queue_head_t ep1_wait_queue; wait_queue_head_t prepare_wait_queue; int spec_received, audio_parm_answer; - + char vendor_name[CAIAQ_USB_STR_LEN]; char product_name[CAIAQ_USB_STR_LEN]; char serial[CAIAQ_USB_STR_LEN]; @@ -90,11 +92,16 @@ struct snd_usb_caiaqdev { struct snd_pcm_substream *sub_playback[MAX_STREAMS]; struct snd_pcm_substream *sub_capture[MAX_STREAMS]; + /* Controls */ + unsigned char control_state[64]; + /* Linux input */ #ifdef CONFIG_SND_USB_CAIAQ_INPUT struct input_dev *input_dev; + char phys[64]; /* physical device path */ + unsigned short keycode[64]; #endif - + /* ALSA */ struct snd_pcm *pcm; struct snd_pcm_hardware pcm_info; @@ -112,6 +119,9 @@ struct snd_usb_caiaq_cb_info { int snd_usb_caiaq_set_audio_params (struct snd_usb_caiaqdev *dev, int rate, int depth, int bbp); int snd_usb_caiaq_set_auto_msg (struct snd_usb_caiaqdev *dev, int digital, int analog, int erp); - +int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *dev, + unsigned char command, + const unsigned char *buffer, + int len); #endif /* CAIAQ_DEVICE_H */ diff --git a/sound/usb/caiaq/caiaq-input.c b/sound/usb/caiaq/caiaq-input.c index cd536ca..f743847 100644 --- a/sound/usb/caiaq/caiaq-input.c +++ b/sound/usb/caiaq/caiaq-input.c @@ -21,28 +21,61 @@ #include <linux/moduleparam.h> #include <linux/input.h> #include <linux/usb.h> +#include <linux/usb/input.h> #include <linux/spinlock.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/rawmidi.h> #include <sound/pcm.h> #include "caiaq-device.h" #include "caiaq-input.h" -#ifdef CONFIG_SND_USB_CAIAQ_INPUT - -static unsigned char keycode_ak1[] = { KEY_C, KEY_B, KEY_A }; -static unsigned char keycode_rk2[] = { KEY_1, KEY_2, KEY_3, KEY_4, - KEY_5, KEY_6, KEY_7 }; -static unsigned char keycode_rk3[] = { KEY_1, KEY_2, KEY_3, KEY_4, - KEY_5, KEY_6, KEY_7, KEY_5, KEY_6 }; - -#define DEG90 (range/2) -#define DEG180 (range) -#define DEG270 (DEG90 + DEG180) -#define DEG360 (DEG180 * 2) -#define HIGH_PEAK (268) -#define LOW_PEAK (-7) +static unsigned short keycode_ak1[] = { KEY_C, KEY_B, KEY_A }; +static unsigned short keycode_rk2[] = { KEY_1, KEY_2, KEY_3, KEY_4, + KEY_5, KEY_6, KEY_7 }; +static unsigned short keycode_rk3[] = { KEY_1, KEY_2, KEY_3, KEY_4, + KEY_5, KEY_6, KEY_7, KEY_5, KEY_6 }; + +static unsigned short keycode_kore[] = { + KEY_FN_F1, /* "menu" */ + KEY_FN_F7, /* "lcd backlight */ + KEY_FN_F2, /* "control" */ + KEY_FN_F3, /* "enter" */ + KEY_FN_F4, /* "view" */ + KEY_FN_F5, /* "esc" */ + KEY_FN_F6, /* "sound" */ + KEY_FN_F8, /* array spacer, never triggered. */ + KEY_RIGHT, + KEY_DOWN, + KEY_UP, + KEY_LEFT, + KEY_SOUND, /* "listen" */ + KEY_RECORD, + KEY_PLAYPAUSE, + KEY_STOP, + BTN_4, /* 8 softkeys */ + BTN_3, + BTN_2, + BTN_1, + BTN_8, + BTN_7, + BTN_6, + BTN_5, + KEY_BRL_DOT4, /* touch sensitive knobs */ + KEY_BRL_DOT3, + KEY_BRL_DOT2, + KEY_BRL_DOT1, + KEY_BRL_DOT8, + KEY_BRL_DOT7, + KEY_BRL_DOT6, + KEY_BRL_DOT5 +}; + +#define DEG90 (range / 2) +#define DEG180 (range) +#define DEG270 (DEG90 + DEG180) +#define DEG360 (DEG180 * 2) +#define HIGH_PEAK (268) +#define LOW_PEAK (-7) /* some of these devices have endless rotation potentiometers * built in which use two tapers, 90 degrees phase shifted. @@ -56,8 +89,8 @@ static unsigned int decode_erp(unsigned char a, unsigned char b) int range = HIGH_PEAK - LOW_PEAK; int mid_value = (HIGH_PEAK + LOW_PEAK) / 2; - weight_b = abs(mid_value-a) - (range/2 - 100)/2; - + weight_b = abs(mid_value - a) - (range / 2 - 100) / 2; + if (weight_b < 0) weight_b = 0; @@ -93,7 +126,7 @@ static unsigned int decode_erp(unsigned char a, unsigned char b) if (ret < 0) ret += 1000; - + if (ret >= 1000) ret -= 1000; @@ -108,76 +141,113 @@ static unsigned int decode_erp(unsigned char a, unsigned char b) #undef LOW_PEAK -static void snd_caiaq_input_read_analog(struct snd_usb_caiaqdev *dev, +static void snd_caiaq_input_read_analog(struct snd_usb_caiaqdev *dev, const unsigned char *buf, unsigned int len) { - switch(dev->input_dev->id.product) { - case USB_PID_RIGKONTROL2: - input_report_abs(dev->input_dev, ABS_X, (buf[4] << 8) |buf[5]); - input_report_abs(dev->input_dev, ABS_Y, (buf[0] << 8) |buf[1]); - input_report_abs(dev->input_dev, ABS_Z, (buf[2] << 8) |buf[3]); - input_sync(dev->input_dev); + struct input_dev *input_dev = dev->input_dev; + + switch (dev->chip.usb_id) { + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2): + input_report_abs(input_dev, ABS_X, (buf[4] << 8) | buf[5]); + input_report_abs(input_dev, ABS_Y, (buf[0] << 8) | buf[1]); + input_report_abs(input_dev, ABS_Z, (buf[2] << 8) | buf[3]); + input_sync(input_dev); break; - case USB_PID_RIGKONTROL3: - input_report_abs(dev->input_dev, ABS_X, (buf[0] << 8) |buf[1]); - input_report_abs(dev->input_dev, ABS_Y, (buf[2] << 8) |buf[3]); - input_report_abs(dev->input_dev, ABS_Z, (buf[4] << 8) |buf[5]); - input_sync(dev->input_dev); + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3): + input_report_abs(input_dev, ABS_X, (buf[0] << 8) | buf[1]); + input_report_abs(input_dev, ABS_Y, (buf[2] << 8) | buf[3]); + input_report_abs(input_dev, ABS_Z, (buf[4] << 8) | buf[5]); + input_sync(input_dev); + break; + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER): + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2): + input_report_abs(input_dev, ABS_X, (buf[0] << 8) | buf[1]); + input_report_abs(input_dev, ABS_Y, (buf[2] << 8) | buf[3]); + input_report_abs(input_dev, ABS_Z, (buf[4] << 8) | buf[5]); + input_sync(input_dev); break; } } -static void snd_caiaq_input_read_erp(struct snd_usb_caiaqdev *dev, +static void snd_caiaq_input_read_erp(struct snd_usb_caiaqdev *dev, const char *buf, unsigned int len) { + struct input_dev *input_dev = dev->input_dev; int i; - switch(dev->input_dev->id.product) { - case USB_PID_AK1: + switch (dev->chip.usb_id) { + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1): i = decode_erp(buf[0], buf[1]); - input_report_abs(dev->input_dev, ABS_X, i); - input_sync(dev->input_dev); + input_report_abs(input_dev, ABS_X, i); + input_sync(input_dev); + break; + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER): + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2): + i = decode_erp(buf[7], buf[5]); + input_report_abs(input_dev, ABS_HAT0X, i); + i = decode_erp(buf[12], buf[14]); + input_report_abs(input_dev, ABS_HAT0Y, i); + i = decode_erp(buf[15], buf[13]); + input_report_abs(input_dev, ABS_HAT1X, i); + i = decode_erp(buf[0], buf[2]); + input_report_abs(input_dev, ABS_HAT1Y, i); + i = decode_erp(buf[3], buf[1]); + input_report_abs(input_dev, ABS_HAT2X, i); + i = decode_erp(buf[8], buf[10]); + input_report_abs(input_dev, ABS_HAT2Y, i); + i = decode_erp(buf[11], buf[9]); + input_report_abs(input_dev, ABS_HAT3X, i); + i = decode_erp(buf[4], buf[6]); + input_report_abs(input_dev, ABS_HAT3Y, i); + input_sync(input_dev); break; } } -static void snd_caiaq_input_read_io(struct snd_usb_caiaqdev *dev, +static void snd_caiaq_input_read_io(struct snd_usb_caiaqdev *dev, char *buf, unsigned int len) { + struct input_dev *input_dev = dev->input_dev; + unsigned short *keycode = input_dev->keycode; int i; - unsigned char *keycode = dev->input_dev->keycode; if (!keycode) return; - if (dev->input_dev->id.product == USB_PID_RIGKONTROL2) - for (i=0; i<len; i++) + if (input_dev->id.product == USB_PID_RIGKONTROL2) + for (i = 0; i < len; i++) buf[i] = ~buf[i]; - for (i=0; (i<dev->input_dev->keycodemax) && (i < len); i++) - input_report_key(dev->input_dev, keycode[i], - buf[i/8] & (1 << (i%8))); + for (i = 0; i < input_dev->keycodemax && i < len * 8; i++) + input_report_key(input_dev, keycode[i], + buf[i / 8] & (1 << (i % 8))); - input_sync(dev->input_dev); + if (dev->chip.usb_id == + USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER) || + dev->chip.usb_id == + USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2)) + input_report_abs(dev->input_dev, ABS_MISC, 255 - buf[4]); + + input_sync(input_dev); } -void snd_usb_caiaq_input_dispatch(struct snd_usb_caiaqdev *dev, - char *buf, +void snd_usb_caiaq_input_dispatch(struct snd_usb_caiaqdev *dev, + char *buf, unsigned int len) { - if (!dev->input_dev || (len < 1)) + if (!dev->input_dev || len < 1) return; switch (buf[0]) { case EP1_CMD_READ_ANALOG: - snd_caiaq_input_read_analog(dev, buf+1, len-1); + snd_caiaq_input_read_analog(dev, buf + 1, len - 1); break; case EP1_CMD_READ_ERP: - snd_caiaq_input_read_erp(dev, buf+1, len-1); + snd_caiaq_input_read_erp(dev, buf + 1, len - 1); break; case EP1_CMD_READ_IO: - snd_caiaq_input_read_io(dev, buf+1, len-1); + snd_caiaq_input_read_io(dev, buf + 1, len - 1); break; } } @@ -192,37 +262,34 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev) if (!input) return -ENOMEM; + usb_make_path(usb_dev, dev->phys, sizeof(dev->phys)); + strlcat(dev->phys, "/input0", sizeof(dev->phys)); + input->name = dev->product_name; - input->id.bustype = BUS_USB; - input->id.vendor = usb_dev->descriptor.idVendor; - input->id.product = usb_dev->descriptor.idProduct; - input->id.version = usb_dev->descriptor.bcdDevice; + input->phys = dev->phys; + usb_to_input_id(usb_dev, &input->id); + input->dev.parent = &usb_dev->dev; switch (dev->chip.usb_id) { case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2): input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); input->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) | BIT_MASK(ABS_Z); - input->keycode = keycode_rk2; - input->keycodesize = sizeof(char); + BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_rk2)); + memcpy(dev->keycode, keycode_rk2, sizeof(keycode_rk2)); input->keycodemax = ARRAY_SIZE(keycode_rk2); - for (i=0; i<ARRAY_SIZE(keycode_rk2); i++) - set_bit(keycode_rk2[i], input->keybit); - input_set_abs_params(input, ABS_X, 0, 4096, 0, 10); input_set_abs_params(input, ABS_Y, 0, 4096, 0, 10); input_set_abs_params(input, ABS_Z, 0, 4096, 0, 10); snd_usb_caiaq_set_auto_msg(dev, 1, 10, 0); break; case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL3): - input->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); - input->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_Z); - input->keycode = keycode_rk3; - input->keycodesize = sizeof(char); + input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) | + BIT_MASK(ABS_Z); + BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_rk3)); + memcpy(dev->keycode, keycode_rk3, sizeof(keycode_rk3)); input->keycodemax = ARRAY_SIZE(keycode_rk3); - for (i=0; i<ARRAY_SIZE(keycode_rk3); i++) - set_bit(keycode_rk3[i], input->keybit); - input_set_abs_params(input, ABS_X, 0, 1024, 0, 10); input_set_abs_params(input, ABS_Y, 0, 1024, 0, 10); input_set_abs_params(input, ABS_Z, 0, 1024, 0, 10); @@ -231,21 +298,50 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev) case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1): input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); input->absbit[0] = BIT_MASK(ABS_X); - input->keycode = keycode_ak1; - input->keycodesize = sizeof(char); + BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_ak1)); + memcpy(dev->keycode, keycode_ak1, sizeof(keycode_ak1)); input->keycodemax = ARRAY_SIZE(keycode_ak1); - for (i=0; i<ARRAY_SIZE(keycode_ak1); i++) - set_bit(keycode_ak1[i], input->keybit); - input_set_abs_params(input, ABS_X, 0, 999, 0, 10); snd_usb_caiaq_set_auto_msg(dev, 1, 0, 5); break; + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER): + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2): + input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input->absbit[0] = BIT_MASK(ABS_HAT0X) | BIT_MASK(ABS_HAT0Y) | + BIT_MASK(ABS_HAT1X) | BIT_MASK(ABS_HAT1Y) | + BIT_MASK(ABS_HAT2X) | BIT_MASK(ABS_HAT2Y) | + BIT_MASK(ABS_HAT3X) | BIT_MASK(ABS_HAT3Y) | + BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) | + BIT_MASK(ABS_Z); + input->absbit[BIT_WORD(ABS_MISC)] |= BIT_MASK(ABS_MISC); + BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_kore)); + memcpy(dev->keycode, keycode_kore, sizeof(keycode_kore)); + input->keycodemax = ARRAY_SIZE(keycode_kore); + input_set_abs_params(input, ABS_HAT0X, 0, 999, 0, 10); + input_set_abs_params(input, ABS_HAT0Y, 0, 999, 0, 10); + input_set_abs_params(input, ABS_HAT1X, 0, 999, 0, 10); + input_set_abs_params(input, ABS_HAT1Y, 0, 999, 0, 10); + input_set_abs_params(input, ABS_HAT2X, 0, 999, 0, 10); + input_set_abs_params(input, ABS_HAT2Y, 0, 999, 0, 10); + input_set_abs_params(input, ABS_HAT3X, 0, 999, 0, 10); + input_set_abs_params(input, ABS_HAT3Y, 0, 999, 0, 10); + input_set_abs_params(input, ABS_X, 0, 4096, 0, 10); + input_set_abs_params(input, ABS_Y, 0, 4096, 0, 10); + input_set_abs_params(input, ABS_Z, 0, 4096, 0, 10); + input_set_abs_params(input, ABS_MISC, 0, 255, 0, 1); + snd_usb_caiaq_set_auto_msg(dev, 1, 10, 5); + break; default: /* no input methods supported on this device */ input_free_device(input); return 0; } + input->keycode = dev->keycode; + input->keycodesize = sizeof(unsigned short); + for (i = 0; i < input->keycodemax; i++) + __set_bit(dev->keycode[i], input->keybit); + ret = input_register_device(input); if (ret < 0) { input_free_device(input); @@ -265,5 +361,3 @@ void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *dev) dev->input_dev = NULL; } -#endif /* CONFIG_SND_USB_CAIAQ_INPUT */ - diff --git a/sound/usb/caiaq/caiaq-midi.c b/sound/usb/caiaq/caiaq-midi.c index 793ca20..30b57f9 100644 --- a/sound/usb/caiaq/caiaq-midi.c +++ b/sound/usb/caiaq/caiaq-midi.c @@ -23,7 +23,6 @@ #include <linux/usb.h> #include <linux/input.h> #include <linux/spinlock.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/rawmidi.h> #include <sound/pcm.h> @@ -124,7 +123,7 @@ void snd_usb_caiaq_midi_handle_input(struct snd_usb_caiaqdev *dev, snd_rawmidi_receive(dev->midi_receive_substream, buf, len); } -int __devinit snd_usb_caiaq_midi_init(struct snd_usb_caiaqdev *device) +int snd_usb_caiaq_midi_init(struct snd_usb_caiaqdev *device) { int ret; struct snd_rawmidi *rmidi; diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index 967b823..8fa9356 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -38,7 +38,6 @@ */ -#include <sound/driver.h> #include <linux/bitops.h> #include <linux/init.h> #include <linux/list.h> @@ -2078,6 +2077,14 @@ static int usb_audio_probe(struct usb_interface *intf, const struct usb_device_id *id); static void usb_audio_disconnect(struct usb_interface *intf); +#ifdef CONFIG_PM +static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message); +static int usb_audio_resume(struct usb_interface *intf); +#else +#define usb_audio_suspend NULL +#define usb_audio_resume NULL +#endif + static struct usb_device_id usb_audio_ids [] = { #include "usbquirks.h" { .match_flags = (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS), @@ -2092,6 +2099,8 @@ static struct usb_driver usb_audio_driver = { .name = "snd-usb-audio", .probe = usb_audio_probe, .disconnect = usb_audio_disconnect, + .suspend = usb_audio_suspend, + .resume = usb_audio_resume, .id_table = usb_audio_ids, }; @@ -3654,6 +3663,45 @@ static void usb_audio_disconnect(struct usb_interface *intf) dev_get_drvdata(&intf->dev)); } +#ifdef CONFIG_PM +static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct snd_usb_audio *chip = dev_get_drvdata(&intf->dev); + struct list_head *p; + struct snd_usb_stream *as; + + if (chip == (void *)-1L) + return 0; + + snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot); + if (!chip->num_suspended_intf++) { + list_for_each(p, &chip->pcm_list) { + as = list_entry(p, struct snd_usb_stream, list); + snd_pcm_suspend_all(as->pcm); + } + } + + return 0; +} + +static int usb_audio_resume(struct usb_interface *intf) +{ + struct snd_usb_audio *chip = dev_get_drvdata(&intf->dev); + + if (chip == (void *)-1L) + return 0; + if (--chip->num_suspended_intf) + return 0; + /* + * ALSA leaves material resumption to user space + * we just notify + */ + + snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0); + + return 0; +} +#endif /* CONFIG_PM */ static int __init snd_usb_audio_init(void) { diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 2272f45..7cf18c3 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -126,6 +126,7 @@ struct snd_usb_audio { u32 usb_id; int shutdown; int num_interfaces; + int num_suspended_intf; struct list_head pcm_list; /* list of pcm streams */ int pcm_devs; diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c index 6330788..750e929 100644 --- a/sound/usb/usbmidi.c +++ b/sound/usb/usbmidi.c @@ -35,7 +35,6 @@ * SUCH DAMAGE. */ -#include <sound/driver.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/bitops.h> diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c index 5e32969..89c63d0 100644 --- a/sound/usb/usbmixer.c +++ b/sound/usb/usbmixer.c @@ -26,7 +26,6 @@ * */ -#include <sound/driver.h> #include <linux/bitops.h> #include <linux/init.h> #include <linux/list.h> @@ -1703,6 +1702,11 @@ static void snd_usb_mixer_memory_change(struct usb_mixer_interface *mixer, case 19: /* speaker out jacks */ case 20: /* headphones out jack */ break; + /* live24ext: 4 = line-in jack */ + case 3: /* hp-out jack (may actuate Mute) */ + if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040)) + snd_usb_mixer_notify_id(mixer, mixer->rc_cfg->mute_mixer_id); + break; default: snd_printd(KERN_DEBUG "memory change in unknown unit %d\n", unitid); break; @@ -1951,6 +1955,9 @@ static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer) int i, err; for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_controls); ++i) { + if (i > 1 && /* Live24ext has 2 LEDs only */ + mixer->chip->usb_id == USB_ID(0x041e, 0x3040)) + break; err = snd_ctl_add(mixer->chip->card, snd_ctl_new1(&snd_audigy2nx_controls[i], mixer)); if (err < 0) @@ -1963,28 +1970,42 @@ static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer) static void snd_audigy2nx_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { - static const struct { + static const struct sb_jack { int unitid; const char *name; - } jacks[] = { + } jacks_audigy2nx[] = { {4, "dig in "}, {7, "line in"}, {19, "spk out"}, {20, "hph out"}, + {-1, NULL} + }, jacks_live24ext[] = { + {4, "line in"}, /* &1=Line, &2=Mic*/ + {3, "hph out"}, /* headphones */ + {0, "RC "}, /* last command, 6 bytes see rc_config above */ + {-1, NULL} }; + const struct sb_jack *jacks; struct usb_mixer_interface *mixer = entry->private_data; int i, err; u8 buf[3]; snd_iprintf(buffer, "%s jacks\n\n", mixer->chip->card->shortname); - for (i = 0; i < ARRAY_SIZE(jacks); ++i) { + if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020)) + jacks = jacks_audigy2nx; + else if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040)) + jacks = jacks_live24ext; + else + return; + + for (i = 0; jacks[i].name; ++i) { snd_iprintf(buffer, "%s: ", jacks[i].name); err = snd_usb_ctl_msg(mixer->chip->dev, usb_rcvctrlpipe(mixer->chip->dev, 0), GET_MEM, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, jacks[i].unitid << 8, buf, 3, 100); - if (err == 3 && buf[0] == 3) + if (err == 3 && (buf[0] == 3 || buf[0] == 6)) snd_iprintf(buffer, "%02x %02x\n", buf[1], buf[2]); else snd_iprintf(buffer, "?\n"); @@ -2022,7 +2043,8 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif) if ((err = snd_usb_soundblaster_remote_init(mixer)) < 0) goto _error; - if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020)) { + if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020) || + mixer->chip->usb_id == USB_ID(0x041e, 0x3040)) { struct snd_info_entry *entry; if ((err = snd_audigy2nx_controls_create(mixer)) < 0) diff --git a/sound/usb/usbmixer_maps.c b/sound/usb/usbmixer_maps.c index 7c4dcb3..d755be0 100644 --- a/sound/usb/usbmixer_maps.c +++ b/sound/usb/usbmixer_maps.c @@ -187,6 +187,13 @@ static struct usbmix_selector_map audigy2nx_selectors[] = { { 0 } /* terminator */ }; +/* Creative SoundBlaster Live! 24-bit External */ +static struct usbmix_name_map live24ext_map[] = { + /* 2: PCM Playback Volume */ + { 5, "Mic Capture" }, /* FU, default PCM Capture Volume */ + { 0 } /* terminator */ +}; + /* LineX FM Transmitter entry - needed to bypass controls bug */ static struct usbmix_name_map linex_map[] = { /* 1: IT pcm */ @@ -273,6 +280,10 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = { .map = audigy2nx_map, .selector_map = audigy2nx_selectors, }, + { + .id = USB_ID(0x041e, 0x3040), + .map = live24ext_map, + }, { /* Hercules DJ Console (Windows Edition) */ .id = USB_ID(0x06f8, 0xb000), diff --git a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h index 59410f4..938dff5 100644 --- a/sound/usb/usbquirks.h +++ b/sound/usb/usbquirks.h @@ -1004,11 +1004,35 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, { + /* has ID 0x0049 when not in "Advanced Driver" mode */ + USB_DEVICE(0x0582, 0x0047), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + /* .vendor_name = "EDIROL", */ + /* .product_name = "UR-80", */ + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + /* in the 96 kHz modes, only interface 1 is there */ + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = -1 + } + } + } +}, +{ /* has ID 0x004a when not in "Advanced Driver" mode */ USB_DEVICE(0x0582, 0x0048), .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "EDIROL", - .product_name = "UR-80", + /* .vendor_name = "EDIROL", */ + /* .product_name = "UR-80", */ .ifnum = 0, .type = QUIRK_MIDI_FIXED_ENDPOINT, .data = & (const struct snd_usb_midi_endpoint_info) { diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c index b76b3dd..6495534e 100644 --- a/sound/usb/usx2y/usX2Yhwdep.c +++ b/sound/usb/usx2y/usX2Yhwdep.c @@ -20,7 +20,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/interrupt.h> #include <linux/usb.h> #include <sound/core.h> @@ -34,34 +33,31 @@ int usX2Y_hwdep_pcm_new(struct snd_card *card); -static struct page * snd_us428ctls_vm_nopage(struct vm_area_struct *area, unsigned long address, int *type) +static int snd_us428ctls_vm_fault(struct vm_area_struct *area, + struct vm_fault *vmf) { unsigned long offset; struct page * page; void *vaddr; - snd_printdd("ENTER, start %lXh, ofs %lXh, pgoff %ld, addr %lXh\n", + snd_printdd("ENTER, start %lXh, pgoff %ld\n", area->vm_start, - address - area->vm_start, - (address - area->vm_start) >> PAGE_SHIFT, - address); + vmf->pgoff); - offset = area->vm_pgoff << PAGE_SHIFT; - offset += address - area->vm_start; - snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_SIGBUS); + offset = vmf->pgoff << PAGE_SHIFT; vaddr = (char*)((struct usX2Ydev *)area->vm_private_data)->us428ctls_sharedmem + offset; page = virt_to_page(vaddr); get_page(page); - snd_printdd( "vaddr=%p made us428ctls_vm_nopage() return %p; offset=%lX\n", vaddr, page, offset); + vmf->page = page; - if (type) - *type = VM_FAULT_MINOR; + snd_printdd("vaddr=%p made us428ctls_vm_fault() page %p\n", + vaddr, page); - return page; + return 0; } static struct vm_operations_struct us428ctls_vm_ops = { - .nopage = snd_us428ctls_vm_nopage, + .fault = snd_us428ctls_vm_fault, }; static int snd_us428ctls_mmap(struct snd_hwdep * hw, struct file *filp, struct vm_area_struct *area) diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c index e011fca..e5981a6 100644 --- a/sound/usb/usx2y/usbusx2y.c +++ b/sound/usb/usx2y/usbusx2y.c @@ -130,7 +130,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/module.h> #include <linux/moduleparam.h> diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c index 48e9aa3..9a608fa 100644 --- a/sound/usb/usx2y/usbusx2yaudio.c +++ b/sound/usb/usx2y/usbusx2yaudio.c @@ -31,7 +31,6 @@ */ -#include <sound/driver.h> #include <linux/interrupt.h> #include <linux/usb.h> #include <sound/core.h> diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c index a5e7bcd..800b5ce 100644 --- a/sound/usb/usx2y/usx2yhwdeppcm.c +++ b/sound/usb/usx2y/usx2yhwdeppcm.c @@ -683,30 +683,24 @@ static void snd_usX2Y_hwdep_pcm_vm_close(struct vm_area_struct *area) } -static struct page * snd_usX2Y_hwdep_pcm_vm_nopage(struct vm_area_struct *area, unsigned long address, int *type) +static int snd_usX2Y_hwdep_pcm_vm_fault(struct vm_area_struct *area, + struct vm_fault *vmf) { unsigned long offset; - struct page *page; void *vaddr; - offset = area->vm_pgoff << PAGE_SHIFT; - offset += address - area->vm_start; - snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_OOM); + offset = vmf->pgoff << PAGE_SHIFT; vaddr = (char*)((struct usX2Ydev *)area->vm_private_data)->hwdep_pcm_shm + offset; - page = virt_to_page(vaddr); - get_page(page); - - if (type) - *type = VM_FAULT_MINOR; - - return page; + vmf->page = virt_to_page(vaddr); + get_page(vmf->page); + return 0; } static struct vm_operations_struct snd_usX2Y_hwdep_pcm_vm_ops = { .open = snd_usX2Y_hwdep_pcm_vm_open, .close = snd_usX2Y_hwdep_pcm_vm_close, - .nopage = snd_usX2Y_hwdep_pcm_vm_nopage, + .fault = snd_usX2Y_hwdep_pcm_vm_fault, }; |