diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-03-18 10:46:37 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-03-18 10:46:37 -0700 |
commit | d3e458d78167102cc961237cfceef6fffc80c0b3 (patch) | |
tree | e9195c1294daf053614e63ac52b0b44a28479017 /sound | |
parent | f2e1fbb5f2177227f71c4fc0491e531dd7acd385 (diff) | |
parent | d351cf4603edb2a5bfa9a48d06c425511c63f2a3 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6: (308 commits)
ALSA: sound/pci/asihpi: check adapter index in hpi_ioctl
ALSA: aloop - Fix possible IRQ lock inversion
ALSA: sound/core: merge list_del()/list_add_tail() to list_move_tail()
ALSA: ctxfi - use list_move() instead of list_del()/list_add() combination
ALSA: firewire - msleep needs delay.h
ALSA: firewire-lib, firewire-speakers: handle packet queueing errors
ALSA: firewire-lib: allocate DMA buffer separately
ALSA: firewire-lib: use no-info SYT for packets without SYT sample
ALSA: add LaCie FireWire Speakers/Griffin FireWave Surround driver
ALSA: hda - Remove an unused variable in patch_realtek.c
ALSA: hda - pin-adc-mux-dmic auto-configuration of 92HD8X codecs
ALSA: hda - fix digital mic selection in mixer on 92HD8X codecs
ALSA: hda - Move default input-src selection to init part
ALSA: hda - Initialize special cases for input src in init phase
ALSA: ctxfi - Clear input settings before initialization
ALSA: ctxfi - Fix SPDIF status retrieval
ALSA: ctxfi - Fix incorrect SPDIF status bit mask
ALSA: ctxfi - Fix microphone boost codes/comments
ALSA: atiixp - Fix wrong time-out checks during ac-link reset
ALSA: intel8x0m: append 'm' to "r_intel8x0"
...
Diffstat (limited to 'sound')
230 files changed, 26575 insertions, 9425 deletions
diff --git a/sound/Kconfig b/sound/Kconfig index fcad760f5691..1fef141ef8e7 100644 --- a/sound/Kconfig +++ b/sound/Kconfig @@ -97,6 +97,8 @@ source "sound/sh/Kconfig" # here assuming USB is defined before ALSA source "sound/usb/Kconfig" +source "sound/firewire/Kconfig" + # the following will depend on the order of config. # here assuming PCMCIA is defined before ALSA source "sound/pcmcia/Kconfig" diff --git a/sound/Makefile b/sound/Makefile index ec467decfa79..ce9132b1c395 100644 --- a/sound/Makefile +++ b/sound/Makefile @@ -6,7 +6,7 @@ obj-$(CONFIG_SOUND_PRIME) += sound_firmware.o obj-$(CONFIG_SOUND_PRIME) += oss/ obj-$(CONFIG_DMASOUND) += oss/ obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \ - sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ + firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ obj-$(CONFIG_SND_AOA) += aoa/ # This one must be compilable even if sound is configured out diff --git a/sound/core/control.c b/sound/core/control.c index 9ce00ed20fba..a08ad57c49b6 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -279,33 +279,31 @@ void snd_ctl_free_one(struct snd_kcontrol *kcontrol) EXPORT_SYMBOL(snd_ctl_free_one); -static unsigned int snd_ctl_hole_check(struct snd_card *card, - unsigned int count) +static bool snd_ctl_remove_numid_conflict(struct snd_card *card, + unsigned int count) { struct snd_kcontrol *kctl; list_for_each_entry(kctl, &card->controls, list) { - if ((kctl->id.numid <= card->last_numid && - kctl->id.numid + kctl->count > card->last_numid) || - (kctl->id.numid <= card->last_numid + count - 1 && - kctl->id.numid + kctl->count > card->last_numid + count - 1)) - return card->last_numid = kctl->id.numid + kctl->count - 1; + if (kctl->id.numid < card->last_numid + 1 + count && + kctl->id.numid + kctl->count > card->last_numid + 1) { + card->last_numid = kctl->id.numid + kctl->count - 1; + return true; + } } - return card->last_numid; + return false; } static int snd_ctl_find_hole(struct snd_card *card, unsigned int count) { - unsigned int last_numid, iter = 100000; + unsigned int iter = 100000; - last_numid = card->last_numid; - while (last_numid != snd_ctl_hole_check(card, count)) { + while (snd_ctl_remove_numid_conflict(card, count)) { if (--iter == 0) { /* this situation is very unlikely */ snd_printk(KERN_ERR "unable to allocate new control numid\n"); return -ENOMEM; } - last_numid = card->last_numid; } return 0; } @@ -466,6 +464,52 @@ error: } /** + * snd_ctl_activate_id - activate/inactivate the control of the given id + * @card: the card instance + * @id: the control id to activate/inactivate + * @active: non-zero to activate + * + * Finds the control instance with the given id, and activate or + * inactivate the control together with notification, if changed. + * + * Returns 0 if unchanged, 1 if changed, or a negative error code on failure. + */ +int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id, + int active) +{ + struct snd_kcontrol *kctl; + struct snd_kcontrol_volatile *vd; + unsigned int index_offset; + int ret; + + down_write(&card->controls_rwsem); + kctl = snd_ctl_find_id(card, id); + if (kctl == NULL) { + ret = -ENOENT; + goto unlock; + } + index_offset = snd_ctl_get_ioff(kctl, &kctl->id); + vd = &kctl->vd[index_offset]; + ret = 0; + if (active) { + if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)) + goto unlock; + vd->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + } else { + if (vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE) + goto unlock; + vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + } + ret = 1; + unlock: + up_write(&card->controls_rwsem); + if (ret > 0) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, id); + return ret; +} +EXPORT_SYMBOL_GPL(snd_ctl_activate_id); + +/** * snd_ctl_rename_id - replace the id of a control on the card * @card: the card instance * @src_id: the old id diff --git a/sound/core/device.c b/sound/core/device.c index a67dfac08c03..2d1ad4b0cd65 100644 --- a/sound/core/device.c +++ b/sound/core/device.c @@ -225,15 +225,16 @@ int snd_device_free_all(struct snd_card *card, snd_device_cmd_t cmd) { struct snd_device *dev; int err; - unsigned int range_low, range_high; + unsigned int range_low, range_high, type; if (snd_BUG_ON(!card)) return -ENXIO; - range_low = cmd * SNDRV_DEV_TYPE_RANGE_SIZE; + range_low = (__force unsigned int)cmd * SNDRV_DEV_TYPE_RANGE_SIZE; range_high = range_low + SNDRV_DEV_TYPE_RANGE_SIZE - 1; __again: list_for_each_entry(dev, &card->devices, list) { - if (dev->type >= range_low && dev->type <= range_high) { + type = (__force unsigned int)dev->type; + if (type >= range_low && type <= range_high) { if ((err = snd_device_free(card, dev->device_data)) < 0) return err; goto __again; diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index 9e92441f9b78..16bd9c03679b 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -192,7 +192,8 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, dmab->bytes = 0; switch (type) { case SNDRV_DMA_TYPE_CONTINUOUS: - dmab->area = snd_malloc_pages(size, (unsigned long)device); + dmab->area = snd_malloc_pages(size, + (__force gfp_t)(unsigned long)device); dmab->addr = 0; break; #ifdef CONFIG_HAS_DMA diff --git a/sound/core/oss/linear.c b/sound/core/oss/linear.c index 4c1d16827199..13b3f6f49fae 100644 --- a/sound/core/oss/linear.c +++ b/sound/core/oss/linear.c @@ -114,7 +114,8 @@ static snd_pcm_sframes_t linear_transfer(struct snd_pcm_plugin *plugin, return frames; } -static void init_data(struct linear_priv *data, int src_format, int dst_format) +static void init_data(struct linear_priv *data, + snd_pcm_format_t src_format, snd_pcm_format_t dst_format) { int src_le, dst_le, src_bytes, dst_bytes; @@ -140,9 +141,9 @@ static void init_data(struct linear_priv *data, int src_format, int dst_format) if (snd_pcm_format_signed(src_format) != snd_pcm_format_signed(dst_format)) { if (dst_le) - data->flip = cpu_to_le32(0x80000000); + data->flip = (__force u32)cpu_to_le32(0x80000000); else - data->flip = cpu_to_be32(0x80000000); + data->flip = (__force u32)cpu_to_be32(0x80000000); } } diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 822dd56993ca..d8359cfeca15 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -190,9 +190,10 @@ static int snd_mixer_oss_get_recsrc(struct snd_mixer_oss_file *fmixer) return -EIO; if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */ int err; - if ((err = mixer->get_recsrc(fmixer, &result)) < 0) + unsigned int index; + if ((err = mixer->get_recsrc(fmixer, &index)) < 0) return err; - result = 1 << result; + result = 1 << index; } else { struct snd_mixer_oss_slot *pslot; int chn; @@ -214,6 +215,7 @@ static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsr struct snd_mixer_oss *mixer = fmixer->mixer; struct snd_mixer_oss_slot *pslot; int chn, active; + unsigned int index; int result = 0; if (mixer == NULL) @@ -222,8 +224,8 @@ static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsr if (recsrc & ~mixer->oss_recsrc) recsrc &= ~mixer->oss_recsrc; mixer->put_recsrc(fmixer, ffz(~recsrc)); - mixer->get_recsrc(fmixer, &result); - result = 1 << result; + mixer->get_recsrc(fmixer, &index); + result = 1 << index; } for (chn = 0; chn < 31; chn++) { pslot = &mixer->slots[chn]; diff --git a/sound/core/oss/mulaw.c b/sound/core/oss/mulaw.c index f7649d4d950b..7915564bd394 100644 --- a/sound/core/oss/mulaw.c +++ b/sound/core/oss/mulaw.c @@ -274,7 +274,7 @@ static snd_pcm_sframes_t mulaw_transfer(struct snd_pcm_plugin *plugin, return frames; } -static void init_data(struct mulaw_priv *data, int format) +static void init_data(struct mulaw_priv *data, snd_pcm_format_t format) { #ifdef SNDRV_LITTLE_ENDIAN data->cvt_endian = snd_pcm_format_big_endian(format) > 0; diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index a2e4eb324699..23c34a02894b 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -41,6 +41,7 @@ #include <sound/info.h> #include <linux/soundcard.h> #include <sound/initval.h> +#include <sound/mixer_oss.h> #define OSS_ALSAEMULVER _SIOR ('M', 249, int) @@ -60,7 +61,6 @@ MODULE_PARM_DESC(nonblock_open, "Don't block opening busy PCM devices."); MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM); MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM1); -extern int snd_mixer_oss_ioctl_card(struct snd_card *card, unsigned int cmd, unsigned long arg); static int snd_pcm_oss_get_rate(struct snd_pcm_oss_file *pcm_oss_file); static int snd_pcm_oss_get_channels(struct snd_pcm_oss_file *pcm_oss_file); static int snd_pcm_oss_get_format(struct snd_pcm_oss_file *pcm_oss_file); @@ -656,7 +656,7 @@ snd_pcm_uframes_t get_hw_ptr_period(struct snd_pcm_runtime *runtime) #define AFMT_AC3 0x00000400 #define AFMT_VORBIS 0x00000800 -static int snd_pcm_oss_format_from(int format) +static snd_pcm_format_t snd_pcm_oss_format_from(int format) { switch (format) { case AFMT_MU_LAW: return SNDRV_PCM_FORMAT_MU_LAW; @@ -680,7 +680,7 @@ static int snd_pcm_oss_format_from(int format) } } -static int snd_pcm_oss_format_to(int format) +static int snd_pcm_oss_format_to(snd_pcm_format_t format) { switch (format) { case SNDRV_PCM_FORMAT_MU_LAW: return AFMT_MU_LAW; @@ -843,7 +843,8 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) size_t oss_frame_size; int err; int direct; - int format, sformat, n; + snd_pcm_format_t format, sformat; + int n; struct snd_mask sformat_mask; struct snd_mask mask; @@ -868,11 +869,11 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) _snd_pcm_hw_param_min(sparams, SNDRV_PCM_HW_PARAM_PERIODS, 2, 0); snd_mask_none(&mask); if (atomic_read(&substream->mmap_count)) - snd_mask_set(&mask, SNDRV_PCM_ACCESS_MMAP_INTERLEAVED); + snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_MMAP_INTERLEAVED); else { - snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_INTERLEAVED); + snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED); if (!direct) - snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); + snd_mask_set(&mask, (__force int)SNDRV_PCM_ACCESS_RW_NONINTERLEAVED); } err = snd_pcm_hw_param_mask(substream, sparams, SNDRV_PCM_HW_PARAM_ACCESS, &mask); if (err < 0) { @@ -891,19 +892,22 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) else sformat = snd_pcm_plug_slave_format(format, &sformat_mask); - if (sformat < 0 || !snd_mask_test(&sformat_mask, sformat)) { - for (sformat = 0; sformat <= SNDRV_PCM_FORMAT_LAST; sformat++) { - if (snd_mask_test(&sformat_mask, sformat) && + if ((__force int)sformat < 0 || + !snd_mask_test(&sformat_mask, (__force int)sformat)) { + for (sformat = (__force snd_pcm_format_t)0; + (__force int)sformat <= (__force int)SNDRV_PCM_FORMAT_LAST; + sformat = (__force snd_pcm_format_t)((__force int)sformat + 1)) { + if (snd_mask_test(&sformat_mask, (__force int)sformat) && snd_pcm_oss_format_to(sformat) >= 0) break; } - if (sformat > SNDRV_PCM_FORMAT_LAST) { + if ((__force int)sformat > (__force int)SNDRV_PCM_FORMAT_LAST) { snd_printd("Cannot find a format!!!\n"); err = -EINVAL; goto failure; } } - err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, sformat, 0); + err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, (__force int)sformat, 0); if (err < 0) goto failure; @@ -912,9 +916,9 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) } else { _snd_pcm_hw_params_any(params); _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS, - SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0); + (__force int)SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0); _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT, - snd_pcm_oss_format_from(runtime->oss.format), 0); + (__force int)snd_pcm_oss_format_from(runtime->oss.format), 0); _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS, runtime->oss.channels, 0); _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE, @@ -1185,10 +1189,10 @@ snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream, const if (in_kernel) { mm_segment_t fs; fs = snd_enter_user(); - ret = snd_pcm_lib_write(substream, (void __user *)ptr, frames); + ret = snd_pcm_lib_write(substream, (void __force __user *)ptr, frames); snd_leave_user(fs); } else { - ret = snd_pcm_lib_write(substream, (void __user *)ptr, frames); + ret = snd_pcm_lib_write(substream, (void __force __user *)ptr, frames); } if (ret != -EPIPE && ret != -ESTRPIPE) break; @@ -1230,10 +1234,10 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p if (in_kernel) { mm_segment_t fs; fs = snd_enter_user(); - ret = snd_pcm_lib_read(substream, (void __user *)ptr, frames); + ret = snd_pcm_lib_read(substream, (void __force __user *)ptr, frames); snd_leave_user(fs); } else { - ret = snd_pcm_lib_read(substream, (void __user *)ptr, frames); + ret = snd_pcm_lib_read(substream, (void __force __user *)ptr, frames); } if (ret == -EPIPE) { if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { @@ -1333,7 +1337,7 @@ static ssize_t snd_pcm_oss_write2(struct snd_pcm_substream *substream, const cha struct snd_pcm_plugin_channel *channels; size_t oss_frame_bytes = (runtime->oss.plugin_first->src_width * runtime->oss.plugin_first->src_format.channels) / 8; if (!in_kernel) { - if (copy_from_user(runtime->oss.buffer, (const char __user *)buf, bytes)) + if (copy_from_user(runtime->oss.buffer, (const char __force __user *)buf, bytes)) return -EFAULT; buf = runtime->oss.buffer; } @@ -1429,7 +1433,7 @@ static ssize_t snd_pcm_oss_read2(struct snd_pcm_substream *substream, char *buf, struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_sframes_t frames, frames1; #ifdef CONFIG_SND_PCM_OSS_PLUGINS - char __user *final_dst = (char __user *)buf; + char __user *final_dst = (char __force __user *)buf; if (runtime->oss.plugin_first) { struct snd_pcm_plugin_channel *channels; size_t oss_frame_bytes = (runtime->oss.plugin_last->dst_width * runtime->oss.plugin_last->dst_format.channels) / 8; @@ -1549,6 +1553,7 @@ static int snd_pcm_oss_sync1(struct snd_pcm_substream *substream, size_t size) { struct snd_pcm_runtime *runtime; ssize_t result = 0; + snd_pcm_state_t state; long res; wait_queue_t wait; @@ -1570,9 +1575,9 @@ static int snd_pcm_oss_sync1(struct snd_pcm_substream *substream, size_t size) result = 0; set_current_state(TASK_INTERRUPTIBLE); snd_pcm_stream_lock_irq(substream); - res = runtime->status->state; + state = runtime->status->state; snd_pcm_stream_unlock_irq(substream); - if (res != SNDRV_PCM_STATE_RUNNING) { + if (state != SNDRV_PCM_STATE_RUNNING) { set_current_state(TASK_RUNNING); break; } @@ -1658,7 +1663,7 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file) size1); size1 /= runtime->channels; /* frames */ fs = snd_enter_user(); - snd_pcm_lib_write(substream, (void __user *)runtime->oss.buffer, size1); + snd_pcm_lib_write(substream, (void __force __user *)runtime->oss.buffer, size1); snd_leave_user(fs); } } else if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) { diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c index 6751daa3bb50..71cc3ddf5c15 100644 --- a/sound/core/oss/pcm_plugin.c +++ b/sound/core/oss/pcm_plugin.c @@ -264,7 +264,7 @@ snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pc return frames; } -static int snd_pcm_plug_formats(struct snd_mask *mask, int format) +static int snd_pcm_plug_formats(struct snd_mask *mask, snd_pcm_format_t format) { struct snd_mask formats = *mask; u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | @@ -276,16 +276,16 @@ static int snd_pcm_plug_formats(struct snd_mask *mask, int format) SNDRV_PCM_FMTBIT_U24_3BE | SNDRV_PCM_FMTBIT_S24_3BE | SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE); - snd_mask_set(&formats, SNDRV_PCM_FORMAT_MU_LAW); + snd_mask_set(&formats, (__force int)SNDRV_PCM_FORMAT_MU_LAW); if (formats.bits[0] & (u32)linfmts) formats.bits[0] |= (u32)linfmts; if (formats.bits[1] & (u32)(linfmts >> 32)) formats.bits[1] |= (u32)(linfmts >> 32); - return snd_mask_test(&formats, format); + return snd_mask_test(&formats, (__force int)format); } -static int preferred_formats[] = { +static snd_pcm_format_t preferred_formats[] = { SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_FORMAT_S16_BE, SNDRV_PCM_FORMAT_U16_LE, @@ -306,24 +306,25 @@ static int preferred_formats[] = { SNDRV_PCM_FORMAT_U8 }; -int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask) +snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, + struct snd_mask *format_mask) { int i; - if (snd_mask_test(format_mask, format)) + if (snd_mask_test(format_mask, (__force int)format)) return format; - if (! snd_pcm_plug_formats(format_mask, format)) - return -EINVAL; + if (!snd_pcm_plug_formats(format_mask, format)) + return (__force snd_pcm_format_t)-EINVAL; if (snd_pcm_format_linear(format)) { unsigned int width = snd_pcm_format_width(format); int unsignd = snd_pcm_format_unsigned(format) > 0; int big = snd_pcm_format_big_endian(format) > 0; unsigned int badness, best = -1; - int best_format = -1; + snd_pcm_format_t best_format = (__force snd_pcm_format_t)-1; for (i = 0; i < ARRAY_SIZE(preferred_formats); i++) { - int f = preferred_formats[i]; + snd_pcm_format_t f = preferred_formats[i]; unsigned int w; - if (!snd_mask_test(format_mask, f)) + if (!snd_mask_test(format_mask, (__force int)f)) continue; w = snd_pcm_format_width(f); if (w >= width) @@ -337,17 +338,20 @@ int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask) best = badness; } } - return best_format >= 0 ? best_format : -EINVAL; + if ((__force int)best_format >= 0) + return best_format; + else + return (__force snd_pcm_format_t)-EINVAL; } else { switch (format) { case SNDRV_PCM_FORMAT_MU_LAW: for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) { - int format1 = preferred_formats[i]; - if (snd_mask_test(format_mask, format1)) + snd_pcm_format_t format1 = preferred_formats[i]; + if (snd_mask_test(format_mask, (__force int)format1)) return format1; } default: - return -EINVAL; + return (__force snd_pcm_format_t)-EINVAL; } } } @@ -359,7 +363,7 @@ int snd_pcm_plug_format_plugins(struct snd_pcm_substream *plug, struct snd_pcm_plugin_format tmpformat; struct snd_pcm_plugin_format dstformat; struct snd_pcm_plugin_format srcformat; - int src_access, dst_access; + snd_pcm_access_t src_access, dst_access; struct snd_pcm_plugin *plugin = NULL; int err; int stream = snd_pcm_plug_stream(plug); @@ -641,7 +645,7 @@ snd_pcm_sframes_t snd_pcm_plug_read_transfer(struct snd_pcm_substream *plug, str } int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_area, size_t dst_offset, - size_t samples, int format) + size_t samples, snd_pcm_format_t format) { /* FIXME: sub byte resolution and odd dst_offset */ unsigned char *dst; @@ -688,7 +692,7 @@ int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_area, size_t dst int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_area, size_t src_offset, const struct snd_pcm_channel_area *dst_area, size_t dst_offset, - size_t samples, int format) + size_t samples, snd_pcm_format_t format) { /* FIXME: sub byte resolution and odd dst_offset */ char *src, *dst; diff --git a/sound/core/oss/pcm_plugin.h b/sound/core/oss/pcm_plugin.h index b9afab603711..a5035c2369a6 100644 --- a/sound/core/oss/pcm_plugin.h +++ b/sound/core/oss/pcm_plugin.h @@ -46,7 +46,7 @@ struct snd_pcm_plugin_channel { }; struct snd_pcm_plugin_format { - int format; + snd_pcm_format_t format; unsigned int rate; unsigned int channels; }; @@ -58,7 +58,7 @@ struct snd_pcm_plugin { struct snd_pcm_plugin_format dst_format; /* destination format */ int src_width; /* sample width in bits */ int dst_width; /* sample width in bits */ - int access; + snd_pcm_access_t access; snd_pcm_sframes_t (*src_frames)(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t dst_frames); snd_pcm_sframes_t (*dst_frames)(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t src_frames); snd_pcm_sframes_t (*client_channels)(struct snd_pcm_plugin *plugin, @@ -125,7 +125,8 @@ int snd_pcm_plug_format_plugins(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_pcm_hw_params *slave_params); -int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask); +snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, + struct snd_mask *format_mask); int snd_pcm_plugin_append(struct snd_pcm_plugin *plugin); @@ -146,12 +147,12 @@ snd_pcm_sframes_t snd_pcm_plugin_client_channels(struct snd_pcm_plugin *plugin, int snd_pcm_area_silence(const struct snd_pcm_channel_area *dst_channel, size_t dst_offset, - size_t samples, int format); + size_t samples, snd_pcm_format_t format); int snd_pcm_area_copy(const struct snd_pcm_channel_area *src_channel, size_t src_offset, const struct snd_pcm_channel_area *dst_channel, size_t dst_offset, - size_t samples, int format); + size_t samples, snd_pcm_format_t format); void *snd_pcm_plug_buf_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t size); void snd_pcm_plug_buf_unlock(struct snd_pcm_substream *plug, void *ptr); diff --git a/sound/core/oss/route.c b/sound/core/oss/route.c index bbe25d8c450a..c8171f5783c8 100644 --- a/sound/core/oss/route.c +++ b/sound/core/oss/route.c @@ -25,7 +25,7 @@ #include "pcm_plugin.h" static void zero_areas(struct snd_pcm_plugin_channel *dvp, int ndsts, - snd_pcm_uframes_t frames, int format) + snd_pcm_uframes_t frames, snd_pcm_format_t format) { int dst = 0; for (; dst < ndsts; ++dst) { @@ -38,7 +38,7 @@ static void zero_areas(struct snd_pcm_plugin_channel *dvp, int ndsts, static inline void copy_area(const struct snd_pcm_plugin_channel *src_channel, struct snd_pcm_plugin_channel *dst_channel, - snd_pcm_uframes_t frames, int format) + snd_pcm_uframes_t frames, snd_pcm_format_t format) { dst_channel->enabled = 1; snd_pcm_area_copy(&src_channel->area, 0, &dst_channel->area, 0, frames, format); @@ -51,7 +51,7 @@ static snd_pcm_sframes_t route_transfer(struct snd_pcm_plugin *plugin, { int nsrcs, ndsts, dst; struct snd_pcm_plugin_channel *dvp; - int format; + snd_pcm_format_t format; if (snd_BUG_ON(!plugin || !src_channels || !dst_channels)) return -ENXIO; diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 6b4b1287b314..ee9abb2d9001 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -211,9 +211,9 @@ static char *snd_pcm_format_names[] = { const char *snd_pcm_format_name(snd_pcm_format_t format) { - if (format >= ARRAY_SIZE(snd_pcm_format_names)) + if ((__force unsigned int)format >= ARRAY_SIZE(snd_pcm_format_names)) return "Unknown"; - return snd_pcm_format_names[format]; + return snd_pcm_format_names[(__force unsigned int)format]; } EXPORT_SYMBOL_GPL(snd_pcm_format_name); @@ -269,12 +269,12 @@ static const char *snd_pcm_stream_name(int stream) static const char *snd_pcm_access_name(snd_pcm_access_t access) { - return snd_pcm_access_names[access]; + return snd_pcm_access_names[(__force int)access]; } static const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat) { - return snd_pcm_subformat_names[subformat]; + return snd_pcm_subformat_names[(__force int)subformat]; } static const char *snd_pcm_tstamp_mode_name(int mode) @@ -284,7 +284,7 @@ static const char *snd_pcm_tstamp_mode_name(int mode) static const char *snd_pcm_state_name(snd_pcm_state_t state) { - return snd_pcm_state_names[state]; + return snd_pcm_state_names[(__force int)state]; } #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index 434af3c56d52..88f02e3866e0 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -35,7 +35,10 @@ struct pcm_format_data { unsigned char silence[8]; /* silence data to fill */ }; -static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = { +/* we do lots of calculations on snd_pcm_format_t; shut up sparse */ +#define INT __force int + +static struct pcm_format_data pcm_formats[(INT)SNDRV_PCM_FORMAT_LAST+1] = { [SNDRV_PCM_FORMAT_S8] = { .width = 8, .phys = 8, .le = -1, .signd = 1, .silence = {}, @@ -215,9 +218,9 @@ static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = { int snd_pcm_format_signed(snd_pcm_format_t format) { int val; - if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) + if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) return -EINVAL; - if ((val = pcm_formats[format].signd) < 0) + if ((val = pcm_formats[(INT)format].signd) < 0) return -EINVAL; return val; } @@ -266,9 +269,9 @@ EXPORT_SYMBOL(snd_pcm_format_linear); int snd_pcm_format_little_endian(snd_pcm_format_t format) { int val; - if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) + if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) return -EINVAL; - if ((val = pcm_formats[format].le) < 0) + if ((val = pcm_formats[(INT)format].le) < 0) return -EINVAL; return val; } @@ -304,9 +307,9 @@ EXPORT_SYMBOL(snd_pcm_format_big_endian); int snd_pcm_format_width(snd_pcm_format_t format) { int val; - if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) + if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) return -EINVAL; - if ((val = pcm_formats[format].width) == 0) + if ((val = pcm_formats[(INT)format].width) == 0) return -EINVAL; return val; } @@ -323,9 +326,9 @@ EXPORT_SYMBOL(snd_pcm_format_width); int snd_pcm_format_physical_width(snd_pcm_format_t format) { int val; - if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) + if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) return -EINVAL; - if ((val = pcm_formats[format].phys) == 0) + if ((val = pcm_formats[(INT)format].phys) == 0) return -EINVAL; return val; } @@ -358,11 +361,11 @@ EXPORT_SYMBOL(snd_pcm_format_size); */ const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format) { - if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) + if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) return NULL; - if (! pcm_formats[format].phys) + if (! pcm_formats[(INT)format].phys) return NULL; - return pcm_formats[format].silence; + return pcm_formats[(INT)format].silence; } EXPORT_SYMBOL(snd_pcm_format_silence_64); @@ -382,16 +385,16 @@ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int int width; unsigned char *dst, *pat; - if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) + if ((INT)format < 0 || (INT)format > (INT)SNDRV_PCM_FORMAT_LAST) return -EINVAL; if (samples == 0) return 0; - width = pcm_formats[format].phys; /* physical width */ - pat = pcm_formats[format].silence; + width = pcm_formats[(INT)format].phys; /* physical width */ + pat = pcm_formats[(INT)format].silence; if (! width) return -EINVAL; /* signed or 1 byte data */ - if (pcm_formats[format].signd == 1 || width <= 8) { + if (pcm_formats[(INT)format].signd == 1 || width <= 8) { unsigned int bytes = samples * width / 8; memset(data, *pat, bytes); return 0; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 4be45e7be8ad..ae42b6509ce4 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -941,7 +941,7 @@ static struct action_ops snd_pcm_action_stop = { * * The state of each stream is then changed to the given state unconditionally. */ -int snd_pcm_stop(struct snd_pcm_substream *substream, int state) +int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t state) { return snd_pcm_action(&snd_pcm_action_stop, substream, state); } diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 99a485f13648..f2436d33fbf7 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -1052,7 +1052,7 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf, } else { #ifdef CONFIG_COMPAT if (client->convert32 && snd_seq_ev_is_varusr(&event)) { - void *ptr = compat_ptr(event.data.raw32.d[1]); + void *ptr = (void __force *)compat_ptr(event.data.raw32.d[1]); event.data.ext.ptr = ptr; } #endif @@ -2407,7 +2407,7 @@ int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg) if (client == NULL) return -ENXIO; fs = snd_enter_user(); - result = snd_seq_do_ioctl(client, cmd, (void __user *)arg); + result = snd_seq_do_ioctl(client, cmd, (void __force __user *)arg); snd_leave_user(fs); return result; } @@ -2497,9 +2497,6 @@ static void snd_seq_info_dump_ports(struct snd_info_buffer *buffer, } -void snd_seq_info_pool(struct snd_info_buffer *buffer, - struct snd_seq_pool *pool, char *space); - /* exported to seq_info.c */ void snd_seq_info_clients_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index 7fb55436287f..7f50c1437675 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -86,7 +86,7 @@ int snd_seq_dump_var_event(const struct snd_seq_event *event, if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) { char buf[32]; - char __user *curptr = (char __user *)event->data.ext.ptr; + char __user *curptr = (char __force __user *)event->data.ext.ptr; while (len > 0) { int size = sizeof(buf); if (len < size) @@ -157,7 +157,7 @@ int snd_seq_expand_var_event(const struct snd_seq_event *event, int count, char if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) { if (! in_kernel) return -EINVAL; - if (copy_from_user(buf, (void __user *)event->data.ext.ptr, len)) + if (copy_from_user(buf, (void __force __user *)event->data.ext.ptr, len)) return -EFAULT; return newlen; } @@ -343,7 +343,7 @@ int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event, tmp->event = src->event; src = src->next; } else if (is_usrptr) { - if (copy_from_user(&tmp->event, (char __user *)buf, size)) { + if (copy_from_user(&tmp->event, (char __force __user *)buf, size)) { err = -EFAULT; goto __error; } diff --git a/sound/core/seq/seq_memory.h b/sound/core/seq/seq_memory.h index 63e91431a29f..4a2ec779b8a7 100644 --- a/sound/core/seq/seq_memory.h +++ b/sound/core/seq/seq_memory.h @@ -24,6 +24,8 @@ #include <sound/seq_kernel.h> #include <linux/poll.h> +struct snd_info_buffer; + /* container for sequencer event (internal use) */ struct snd_seq_event_cell { struct snd_seq_event event; @@ -99,5 +101,7 @@ void snd_sequencer_memory_done(void); /* polling */ int snd_seq_pool_poll_wait(struct snd_seq_pool *pool, struct file *file, poll_table *wait); +void snd_seq_info_pool(struct snd_info_buffer *buffer, + struct snd_seq_pool *pool, char *space); #endif diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c index 3bf7d73ac52e..e12bcd94b6db 100644 --- a/sound/core/seq/seq_ports.c +++ b/sound/core/seq/seq_ports.c @@ -412,7 +412,7 @@ int snd_seq_get_port_info(struct snd_seq_client_port * port, * initialization or termination of devices (see seq_midi.c). * * If callback_all option is set, the callback function is invoked - * at each connnection/disconnection. + * at each connection/disconnection. */ static int subscribe_port(struct snd_seq_client *client, diff --git a/sound/core/timer.c b/sound/core/timer.c index ed016329e911..7c1cbf0a0dc4 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -186,9 +186,8 @@ static void snd_timer_check_slave(struct snd_timer_instance *slave) list_for_each_entry(master, &timer->open_list_head, open_list) { if (slave->slave_class == master->slave_class && slave->slave_id == master->slave_id) { - list_del(&slave->open_list); - list_add_tail(&slave->open_list, - &master->slave_list_head); + list_move_tail(&slave->open_list, + &master->slave_list_head); spin_lock_irq(&slave_active_lock); slave->master = master; slave->timer = master->timer; @@ -414,8 +413,7 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event) static int snd_timer_start1(struct snd_timer *timer, struct snd_timer_instance *timeri, unsigned long sticks) { - list_del(&timeri->active_list); - list_add_tail(&timeri->active_list, &timer->active_list_head); + list_move_tail(&timeri->active_list, &timer->active_list_head); if (timer->running) { if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) goto __start_now; diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c index 3b9b550109cb..a89948ae9e8d 100644 --- a/sound/core/vmaster.c +++ b/sound/core/vmaster.c @@ -18,7 +18,7 @@ * a subset of information returned via ctl info callback */ struct link_ctl_info { - int type; /* value type */ + snd_ctl_elem_type_t type; /* value type */ int count; /* item count */ int min_val, max_val; /* min, max values */ }; diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 12b44b0b6777..a0da7755fcea 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -482,8 +482,9 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable) cable->streams[SNDRV_PCM_STREAM_CAPTURE]; unsigned long delta_play = 0, delta_capt = 0; unsigned int running; + unsigned long flags; - spin_lock(&cable->lock); + spin_lock_irqsave(&cable->lock, flags); running = cable->running ^ cable->pause; if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) { delta_play = jiffies - dpcm_play->last_jiffies; @@ -495,10 +496,8 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable) dpcm_capt->last_jiffies += delta_capt; } - if (delta_play == 0 && delta_capt == 0) { - spin_unlock(&cable->lock); - return running; - } + if (delta_play == 0 && delta_capt == 0) + goto unlock; if (delta_play > delta_capt) { loopback_bytepos_update(dpcm_play, delta_play - delta_capt, @@ -510,14 +509,14 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable) delta_capt = delta_play; } - if (delta_play == 0 && delta_capt == 0) { - spin_unlock(&cable->lock); - return running; - } + if (delta_play == 0 && delta_capt == 0) + goto unlock; + /* note delta_capt == delta_play at this moment */ loopback_bytepos_update(dpcm_capt, delta_capt, BYTEPOS_UPDATE_COPY); loopback_bytepos_update(dpcm_play, delta_play, BYTEPOS_UPDATE_POSONLY); - spin_unlock(&cable->lock); + unlock: + spin_unlock_irqrestore(&cable->lock, flags); return running; } diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig new file mode 100644 index 000000000000..e486f48660fb --- /dev/null +++ b/sound/firewire/Kconfig @@ -0,0 +1,25 @@ +menuconfig SND_FIREWIRE + bool "FireWire sound devices" + depends on FIREWIRE + default y + help + Support for IEEE-1394/FireWire/iLink sound devices. + +if SND_FIREWIRE && FIREWIRE + +config SND_FIREWIRE_LIB + tristate + depends on SND_PCM + +config SND_FIREWIRE_SPEAKERS + tristate "FireWire speakers" + select SND_PCM + select SND_FIREWIRE_LIB + help + Say Y here to include support for the Griffin FireWave Surround + and the LaCie FireWire Speakers. + + To compile this driver as a module, choose M here: the module + will be called snd-firewire-speakers. + +endif # SND_FIREWIRE diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile new file mode 100644 index 000000000000..e5b1634d9ad4 --- /dev/null +++ b/sound/firewire/Makefile @@ -0,0 +1,6 @@ +snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \ + fcp.o cmp.o amdtp.o +snd-firewire-speakers-objs := speakers.o + +obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o +obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c new file mode 100644 index 000000000000..b18140ff2b93 --- /dev/null +++ b/sound/firewire/amdtp.c @@ -0,0 +1,562 @@ +/* + * Audio and Music Data Transmission Protocol (IEC 61883-6) streams + * with Common Isochronous Packet (IEC 61883-1) headers + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include <linux/device.h> +#include <linux/err.h> +#include <linux/firewire.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <sound/pcm.h> +#include "amdtp.h" + +#define TICKS_PER_CYCLE 3072 +#define CYCLES_PER_SECOND 8000 +#define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND) + +#define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 µs */ + +#define TAG_CIP 1 + +#define CIP_EOH (1u << 31) +#define CIP_FMT_AM (0x10 << 24) +#define AMDTP_FDF_AM824 (0 << 19) +#define AMDTP_FDF_SFC_SHIFT 16 + +/* TODO: make these configurable */ +#define INTERRUPT_INTERVAL 16 +#define QUEUE_LENGTH 48 + +/** + * amdtp_out_stream_init - initialize an AMDTP output stream structure + * @s: the AMDTP output stream to initialize + * @unit: the target of the stream + * @flags: the packet transmission method to use + */ +int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit, + enum cip_out_flags flags) +{ + if (flags != CIP_NONBLOCKING) + return -EINVAL; + + s->unit = fw_unit_get(unit); + s->flags = flags; + s->context = ERR_PTR(-1); + mutex_init(&s->mutex); + s->packet_index = 0; + + return 0; +} +EXPORT_SYMBOL(amdtp_out_stream_init); + +/** + * amdtp_out_stream_destroy - free stream resources + * @s: the AMDTP output stream to destroy + */ +void amdtp_out_stream_destroy(struct amdtp_out_stream *s) +{ + WARN_ON(!IS_ERR(s->context)); + mutex_destroy(&s->mutex); + fw_unit_put(s->unit); +} +EXPORT_SYMBOL(amdtp_out_stream_destroy); + +/** + * amdtp_out_stream_set_rate - set the sample rate + * @s: the AMDTP output stream to configure + * @rate: the sample rate + * + * The sample rate must be set before the stream is started, and must not be + * changed while the stream is running. + */ +void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate) +{ + static const struct { + unsigned int rate; + unsigned int syt_interval; + } rate_info[] = { + [CIP_SFC_32000] = { 32000, 8, }, + [CIP_SFC_44100] = { 44100, 8, }, + [CIP_SFC_48000] = { 48000, 8, }, + [CIP_SFC_88200] = { 88200, 16, }, + [CIP_SFC_96000] = { 96000, 16, }, + [CIP_SFC_176400] = { 176400, 32, }, + [CIP_SFC_192000] = { 192000, 32, }, + }; + unsigned int sfc; + + if (WARN_ON(!IS_ERR(s->context))) + return; + + for (sfc = 0; sfc < ARRAY_SIZE(rate_info); ++sfc) + if (rate_info[sfc].rate == rate) { + s->sfc = sfc; + s->syt_interval = rate_info[sfc].syt_interval; + return; + } + WARN_ON(1); +} +EXPORT_SYMBOL(amdtp_out_stream_set_rate); + +/** + * amdtp_out_stream_get_max_payload - get the stream's packet size + * @s: the AMDTP output stream + * + * This function must not be called before the stream has been configured + * with amdtp_out_stream_set_hw_params(), amdtp_out_stream_set_pcm(), and + * amdtp_out_stream_set_midi(). + */ +unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s) +{ + static const unsigned int max_data_blocks[] = { + [CIP_SFC_32000] = 4, + [CIP_SFC_44100] = 6, + [CIP_SFC_48000] = 6, + [CIP_SFC_88200] = 12, + [CIP_SFC_96000] = 12, + [CIP_SFC_176400] = 23, + [CIP_SFC_192000] = 24, + }; + + s->data_block_quadlets = s->pcm_channels; + s->data_block_quadlets += DIV_ROUND_UP(s->midi_ports, 8); + + return 8 + max_data_blocks[s->sfc] * 4 * s->data_block_quadlets; +} +EXPORT_SYMBOL(amdtp_out_stream_get_max_payload); + +static void amdtp_write_s16(struct amdtp_out_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); +static void amdtp_write_s32(struct amdtp_out_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); + +/** + * amdtp_out_stream_set_pcm_format - set the PCM format + * @s: the AMDTP output stream to configure + * @format: the format of the ALSA PCM device + * + * The sample format must be set before the stream is started, and must not be + * changed while the stream is running. + */ +void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, + snd_pcm_format_t format) +{ + if (WARN_ON(!IS_ERR(s->context))) + return; + + switch (format) { + default: + WARN_ON(1); + /* fall through */ + case SNDRV_PCM_FORMAT_S16: + s->transfer_samples = amdtp_write_s16; + break; + case SNDRV_PCM_FORMAT_S32: + s->transfer_samples = amdtp_write_s32; + break; + } +} +EXPORT_SYMBOL(amdtp_out_stream_set_pcm_format); + +static unsigned int calculate_data_blocks(struct amdtp_out_stream *s) +{ + unsigned int phase, data_blocks; + + if (!cip_sfc_is_base_44100(s->sfc)) { + /* Sample_rate / 8000 is an integer, and precomputed. */ + data_blocks = s->data_block_state; + } else { + phase = s->data_block_state; + + /* + * This calculates the number of data blocks per packet so that + * 1) the overall rate is correct and exactly synchronized to + * the bus clock, and + * 2) packets with a rounded-up number of blocks occur as early + * as possible in the sequence (to prevent underruns of the + * device's buffer). + */ + if (s->sfc == CIP_SFC_44100) + /* 6 6 5 6 5 6 5 ... */ + data_blocks = 5 + ((phase & 1) ^ + (phase == 0 || phase >= 40)); + else + /* 12 11 11 11 11 ... or 23 22 22 22 22 ... */ + data_blocks = 11 * (s->sfc >> 1) + (phase == 0); + if (++phase >= (80 >> (s->sfc >> 1))) + phase = 0; + s->data_block_state = phase; + } + + return data_blocks; +} + +static unsigned int calculate_syt(struct amdtp_out_stream *s, + unsigned int cycle) +{ + unsigned int syt_offset, phase, index, syt; + + if (s->last_syt_offset < TICKS_PER_CYCLE) { + if (!cip_sfc_is_base_44100(s->sfc)) + syt_offset = s->last_syt_offset + s->syt_offset_state; + else { + /* + * The time, in ticks, of the n'th SYT_INTERVAL sample is: + * n * SYT_INTERVAL * 24576000 / sample_rate + * Modulo TICKS_PER_CYCLE, the difference between successive + * elements is about 1386.23. Rounding the results of this + * formula to the SYT precision results in a sequence of + * differences that begins with: + * 1386 1386 1387 1386 1386 1386 1387 1386 1386 1386 1387 ... + * This code generates _exactly_ the same sequence. + */ + phase = s->syt_offset_state; + index = phase % 13; + syt_offset = s->last_syt_offset; + syt_offset += 1386 + ((index && !(index & 3)) || + phase == 146); + if (++phase >= 147) + phase = 0; + s->syt_offset_state = phase; + } + } else + syt_offset = s->last_syt_offset - TICKS_PER_CYCLE; + s->last_syt_offset = syt_offset; + + if (syt_offset < TICKS_PER_CYCLE) { + syt_offset += TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE; + syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12; + syt += syt_offset % TICKS_PER_CYCLE; + + return syt & 0xffff; + } else { + return 0xffff; /* no info */ + } +} + +static void amdtp_write_s32(struct amdtp_out_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, remaining_frames, frame_step, i, c; + const u32 *src; + + channels = s->pcm_channels; + src = (void *)runtime->dma_area + + s->pcm_buffer_pointer * (runtime->frame_bits / 8); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + frame_step = s->data_block_quadlets - channels; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + *buffer = cpu_to_be32((*src >> 8) | 0x40000000); + src++; + buffer++; + } + buffer += frame_step; + if (--remaining_frames == 0) + src = (void *)runtime->dma_area; + } +} + +static void amdtp_write_s16(struct amdtp_out_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, remaining_frames, frame_step, i, c; + const u16 *src; + + channels = s->pcm_channels; + src = (void *)runtime->dma_area + + s->pcm_buffer_pointer * (runtime->frame_bits / 8); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + frame_step = s->data_block_quadlets - channels; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + *buffer = cpu_to_be32((*src << 8) | 0x40000000); + src++; + buffer++; + } + buffer += frame_step; + if (--remaining_frames == 0) + src = (void *)runtime->dma_area; + } +} + +static void amdtp_fill_pcm_silence(struct amdtp_out_stream *s, + __be32 *buffer, unsigned int frames) +{ + unsigned int i, c; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < s->pcm_channels; ++c) + buffer[c] = cpu_to_be32(0x40000000); + buffer += s->data_block_quadlets; + } +} + +static void amdtp_fill_midi(struct amdtp_out_stream *s, + __be32 *buffer, unsigned int frames) +{ + unsigned int i; + + for (i = 0; i < frames; ++i) + buffer[s->pcm_channels + i * s->data_block_quadlets] = + cpu_to_be32(0x80000000); +} + +static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle) +{ + __be32 *buffer; + unsigned int index, data_blocks, syt, ptr; + struct snd_pcm_substream *pcm; + struct fw_iso_packet packet; + int err; + + if (s->packet_index < 0) + return; + index = s->packet_index; + + data_blocks = calculate_data_blocks(s); + syt = calculate_syt(s, cycle); + + buffer = s->buffer.packets[index].buffer; + buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | + (s->data_block_quadlets << 16) | + s->data_block_counter); + buffer[1] = cpu_to_be32(CIP_EOH | CIP_FMT_AM | AMDTP_FDF_AM824 | + (s->sfc << AMDTP_FDF_SFC_SHIFT) | syt); + buffer += 2; + + pcm = ACCESS_ONCE(s->pcm); + if (pcm) + s->transfer_samples(s, pcm, buffer, data_blocks); + else + amdtp_fill_pcm_silence(s, buffer, data_blocks); + if (s->midi_ports) + amdtp_fill_midi(s, buffer, data_blocks); + + s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff; + + packet.payload_length = 8 + data_blocks * 4 * s->data_block_quadlets; + packet.interrupt = IS_ALIGNED(index + 1, INTERRUPT_INTERVAL); + packet.skip = 0; + packet.tag = TAG_CIP; + packet.sy = 0; + packet.header_length = 0; + + err = fw_iso_context_queue(s->context, &packet, &s->buffer.iso_buffer, + s->buffer.packets[index].offset); + if (err < 0) { + dev_err(&s->unit->device, "queueing error: %d\n", err); + s->packet_index = -1; + amdtp_out_stream_pcm_abort(s); + return; + } + + if (++index >= QUEUE_LENGTH) + index = 0; + s->packet_index = index; + + if (pcm) { + ptr = s->pcm_buffer_pointer + data_blocks; + if (ptr >= pcm->runtime->buffer_size) + ptr -= pcm->runtime->buffer_size; + ACCESS_ONCE(s->pcm_buffer_pointer) = ptr; + + s->pcm_period_pointer += data_blocks; + if (s->pcm_period_pointer >= pcm->runtime->period_size) { + s->pcm_period_pointer -= pcm->runtime->period_size; + snd_pcm_period_elapsed(pcm); + } + } +} + +static void out_packet_callback(struct fw_iso_context *context, u32 cycle, + size_t header_length, void *header, void *data) +{ + struct amdtp_out_stream *s = data; + unsigned int i, packets = header_length / 4; + + /* + * Compute the cycle of the last queued packet. + * (We need only the four lowest bits for the SYT, so we can ignore + * that bits 0-11 must wrap around at 3072.) + */ + cycle += QUEUE_LENGTH - packets; + + for (i = 0; i < packets; ++i) + queue_out_packet(s, ++cycle); +} + +static int queue_initial_skip_packets(struct amdtp_out_stream *s) +{ + struct fw_iso_packet skip_packet = { + .skip = 1, + }; + unsigned int i; + int err; + + for (i = 0; i < QUEUE_LENGTH; ++i) { + skip_packet.interrupt = IS_ALIGNED(s->packet_index + 1, + INTERRUPT_INTERVAL); + err = fw_iso_context_queue(s->context, &skip_packet, NULL, 0); + if (err < 0) + return err; + if (++s->packet_index >= QUEUE_LENGTH) + s->packet_index = 0; + } + + return 0; +} + +/** + * amdtp_out_stream_start - start sending packets + * @s: the AMDTP output stream to start + * @channel: the isochronous channel on the bus + * @speed: firewire speed code + * + * The stream cannot be started until it has been configured with + * amdtp_out_stream_set_hw_params(), amdtp_out_stream_set_pcm(), and + * amdtp_out_stream_set_midi(); and it must be started before any + * PCM or MIDI device can be started. + */ +int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed) +{ + static const struct { + unsigned int data_block; + unsigned int syt_offset; + } initial_state[] = { + [CIP_SFC_32000] = { 4, 3072 }, + [CIP_SFC_48000] = { 6, 1024 }, + [CIP_SFC_96000] = { 12, 1024 }, + [CIP_SFC_192000] = { 24, 1024 }, + [CIP_SFC_44100] = { 0, 67 }, + [CIP_SFC_88200] = { 0, 67 }, + [CIP_SFC_176400] = { 0, 67 }, + }; + int err; + + mutex_lock(&s->mutex); + + if (WARN_ON(!IS_ERR(s->context) || + (!s->pcm_channels && !s->midi_ports))) { + err = -EBADFD; + goto err_unlock; + } + + s->data_block_state = initial_state[s->sfc].data_block; + s->syt_offset_state = initial_state[s->sfc].syt_offset; + s->last_syt_offset = TICKS_PER_CYCLE; + + err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH, + amdtp_out_stream_get_max_payload(s), + DMA_TO_DEVICE); + if (err < 0) + goto err_unlock; + + s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, + FW_ISO_CONTEXT_TRANSMIT, + channel, speed, 0, + out_packet_callback, s); + if (IS_ERR(s->context)) { + err = PTR_ERR(s->context); + if (err == -EBUSY) + dev_err(&s->unit->device, + "no free output stream on this controller\n"); + goto err_buffer; + } + + amdtp_out_stream_update(s); + + s->packet_index = 0; + s->data_block_counter = 0; + err = queue_initial_skip_packets(s); + if (err < 0) + goto err_context; + + err = fw_iso_context_start(s->context, -1, 0, 0); + if (err < 0) + goto err_context; + + mutex_unlock(&s->mutex); + + return 0; + +err_context: + fw_iso_context_destroy(s->context); + s->context = ERR_PTR(-1); +err_buffer: + iso_packets_buffer_destroy(&s->buffer, s->unit); +err_unlock: + mutex_unlock(&s->mutex); + + return err; +} +EXPORT_SYMBOL(amdtp_out_stream_start); + +/** + * amdtp_out_stream_update - update the stream after a bus reset + * @s: the AMDTP output stream + */ +void amdtp_out_stream_update(struct amdtp_out_stream *s) +{ + ACCESS_ONCE(s->source_node_id_field) = + (fw_parent_device(s->unit)->card->node_id & 0x3f) << 24; +} +EXPORT_SYMBOL(amdtp_out_stream_update); + +/** + * amdtp_out_stream_stop - stop sending packets + * @s: the AMDTP output stream to stop + * + * All PCM and MIDI devices of the stream must be stopped before the stream + * itself can be stopped. + */ +void amdtp_out_stream_stop(struct amdtp_out_stream *s) +{ + mutex_lock(&s->mutex); + + if (IS_ERR(s->context)) { + mutex_unlock(&s->mutex); + return; + } + + fw_iso_context_stop(s->context); + fw_iso_context_destroy(s->context); + s->context = ERR_PTR(-1); + iso_packets_buffer_destroy(&s->buffer, s->unit); + + mutex_unlock(&s->mutex); +} +EXPORT_SYMBOL(amdtp_out_stream_stop); + +/** + * amdtp_out_stream_pcm_abort - abort the running PCM device + * @s: the AMDTP stream about to be stopped + * + * If the isochronous stream needs to be stopped asynchronously, call this + * function first to stop the PCM device. + */ +void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s) +{ + struct snd_pcm_substream *pcm; + + pcm = ACCESS_ONCE(s->pcm); + if (pcm) { + snd_pcm_stream_lock_irq(pcm); + if (snd_pcm_running(pcm)) + snd_pcm_stop(pcm, SNDRV_PCM_STATE_XRUN); + snd_pcm_stream_unlock_irq(pcm); + } +} +EXPORT_SYMBOL(amdtp_out_stream_pcm_abort); diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h new file mode 100644 index 000000000000..537a9cb83581 --- /dev/null +++ b/sound/firewire/amdtp.h @@ -0,0 +1,169 @@ +#ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED +#define SOUND_FIREWIRE_AMDTP_H_INCLUDED + +#include <linux/mutex.h> +#include <linux/spinlock.h> +#include "packets-buffer.h" + +/** + * enum cip_out_flags - describes details of the streaming protocol + * @CIP_NONBLOCKING: In non-blocking mode, each packet contains + * sample_rate/8000 samples, with rounding up or down to adjust + * for clock skew and left-over fractional samples. This should + * be used if supported by the device. + */ +enum cip_out_flags { + CIP_NONBLOCKING = 0, +}; + +/** + * enum cip_sfc - a stream's sample rate + */ +enum cip_sfc { + CIP_SFC_32000 = 0, + CIP_SFC_44100 = 1, + CIP_SFC_48000 = 2, + CIP_SFC_88200 = 3, + CIP_SFC_96000 = 4, + CIP_SFC_176400 = 5, + CIP_SFC_192000 = 6, +}; + +#define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ + SNDRV_PCM_FMTBIT_S32) + +struct fw_unit; +struct fw_iso_context; +struct snd_pcm_substream; + +struct amdtp_out_stream { + struct fw_unit *unit; + enum cip_out_flags flags; + struct fw_iso_context *context; + struct mutex mutex; + + enum cip_sfc sfc; + unsigned int data_block_quadlets; + unsigned int pcm_channels; + unsigned int midi_ports; + void (*transfer_samples)(struct amdtp_out_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); + + unsigned int syt_interval; + unsigned int source_node_id_field; + struct iso_packets_buffer buffer; + + struct snd_pcm_substream *pcm; + + int packet_index; + unsigned int data_block_counter; + + unsigned int data_block_state; + + unsigned int last_syt_offset; + unsigned int syt_offset_state; + + unsigned int pcm_buffer_pointer; + unsigned int pcm_period_pointer; +}; + +int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit, + enum cip_out_flags flags); +void amdtp_out_stream_destroy(struct amdtp_out_stream *s); + +void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate); +unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s); + +int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed); +void amdtp_out_stream_update(struct amdtp_out_stream *s); +void amdtp_out_stream_stop(struct amdtp_out_stream *s); + +void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, + snd_pcm_format_t format); +void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s); + +/** + * amdtp_out_stream_set_pcm - configure format of PCM samples + * @s: the AMDTP output stream to be configured + * @pcm_channels: the number of PCM samples in each data block, to be encoded + * as AM824 multi-bit linear audio + * + * This function must not be called while the stream is running. + */ +static inline void amdtp_out_stream_set_pcm(struct amdtp_out_stream *s, + unsigned int pcm_channels) +{ + s->pcm_channels = pcm_channels; +} + +/** + * amdtp_out_stream_set_midi - configure format of MIDI data + * @s: the AMDTP output stream to be configured + * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels) + * + * This function must not be called while the stream is running. + */ +static inline void amdtp_out_stream_set_midi(struct amdtp_out_stream *s, + unsigned int midi_ports) +{ + s->midi_ports = midi_ports; +} + +/** + * amdtp_out_streaming_error - check for streaming error + * @s: the AMDTP output stream + * + * If this function returns true, the stream's packet queue has stopped due to + * an asynchronous error. + */ +static inline bool amdtp_out_streaming_error(struct amdtp_out_stream *s) +{ + return s->packet_index < 0; +} + +/** + * amdtp_out_stream_pcm_prepare - prepare PCM device for running + * @s: the AMDTP output stream + * + * This function should be called from the PCM device's .prepare callback. + */ +static inline void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s) +{ + s->pcm_buffer_pointer = 0; + s->pcm_period_pointer = 0; +} + +/** + * amdtp_out_stream_pcm_trigger - start/stop playback from a PCM device + * @s: the AMDTP output stream + * @pcm: the PCM device to be started, or %NULL to stop the current device + * + * Call this function on a running isochronous stream to enable the actual + * transmission of PCM data. This function should be called from the PCM + * device's .trigger callback. + */ +static inline void amdtp_out_stream_pcm_trigger(struct amdtp_out_stream *s, + struct snd_pcm_substream *pcm) +{ + ACCESS_ONCE(s->pcm) = pcm; +} + +/** + * amdtp_out_stream_pcm_pointer - get the PCM buffer position + * @s: the AMDTP output stream that transports the PCM data + * + * Returns the current buffer position, in frames. + */ +static inline unsigned long +amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s) +{ + return ACCESS_ONCE(s->pcm_buffer_pointer); +} + +static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc) +{ + return sfc & 1; +} + +#endif diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c new file mode 100644 index 000000000000..4a37f3a6fab9 --- /dev/null +++ b/sound/firewire/cmp.c @@ -0,0 +1,308 @@ +/* + * Connection Management Procedures (IEC 61883-1) helper functions + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include <linux/device.h> +#include <linux/firewire.h> +#include <linux/firewire-constants.h> +#include <linux/module.h> +#include <linux/sched.h> +#include "lib.h" +#include "iso-resources.h" +#include "cmp.h" + +#define IMPR_SPEED_MASK 0xc0000000 +#define IMPR_SPEED_SHIFT 30 +#define IMPR_XSPEED_MASK 0x00000060 +#define IMPR_XSPEED_SHIFT 5 +#define IMPR_PLUGS_MASK 0x0000001f + +#define IPCR_ONLINE 0x80000000 +#define IPCR_BCAST_CONN 0x40000000 +#define IPCR_P2P_CONN_MASK 0x3f000000 +#define IPCR_P2P_CONN_SHIFT 24 +#define IPCR_CHANNEL_MASK 0x003f0000 +#define IPCR_CHANNEL_SHIFT 16 + +enum bus_reset_handling { + ABORT_ON_BUS_RESET, + SUCCEED_ON_BUS_RESET, +}; + +static __attribute__((format(printf, 2, 3))) +void cmp_error(struct cmp_connection *c, const char *fmt, ...) +{ + va_list va; + + va_start(va, fmt); + dev_err(&c->resources.unit->device, "%cPCR%u: %pV", + 'i', c->pcr_index, &(struct va_format){ fmt, &va }); + va_end(va); +} + +static int pcr_modify(struct cmp_connection *c, + __be32 (*modify)(struct cmp_connection *c, __be32 old), + int (*check)(struct cmp_connection *c, __be32 pcr), + enum bus_reset_handling bus_reset_handling) +{ + struct fw_device *device = fw_parent_device(c->resources.unit); + __be32 *buffer = c->resources.buffer; + int generation = c->resources.generation; + int rcode, errors = 0; + __be32 old_arg; + int err; + + buffer[0] = c->last_pcr_value; + for (;;) { + old_arg = buffer[0]; + buffer[1] = modify(c, buffer[0]); + + rcode = fw_run_transaction( + device->card, TCODE_LOCK_COMPARE_SWAP, + device->node_id, generation, device->max_speed, + CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index), + buffer, 8); + + if (rcode == RCODE_COMPLETE) { + if (buffer[0] == old_arg) /* success? */ + break; + + if (check) { + err = check(c, buffer[0]); + if (err < 0) + return err; + } + } else if (rcode == RCODE_GENERATION) + goto bus_reset; + else if (rcode_is_permanent_error(rcode) || ++errors >= 3) + goto io_error; + } + c->last_pcr_value = buffer[1]; + + return 0; + +io_error: + cmp_error(c, "transaction failed: %s\n", rcode_string(rcode)); + return -EIO; + +bus_reset: + return bus_reset_handling == ABORT_ON_BUS_RESET ? -EAGAIN : 0; +} + + +/** + * cmp_connection_init - initializes a connection manager + * @c: the connection manager to initialize + * @unit: a unit of the target device + * @ipcr_index: the index of the iPCR on the target device + */ +int cmp_connection_init(struct cmp_connection *c, + struct fw_unit *unit, + unsigned int ipcr_index) +{ + __be32 impr_be; + u32 impr; + int err; + + err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, + CSR_REGISTER_BASE + CSR_IMPR, + &impr_be, 4); + if (err < 0) + return err; + impr = be32_to_cpu(impr_be); + + if (ipcr_index >= (impr & IMPR_PLUGS_MASK)) + return -EINVAL; + + err = fw_iso_resources_init(&c->resources, unit); + if (err < 0) + return err; + + c->connected = false; + mutex_init(&c->mutex); + c->last_pcr_value = cpu_to_be32(0x80000000); + c->pcr_index = ipcr_index; + c->max_speed = (impr & IMPR_SPEED_MASK) >> IMPR_SPEED_SHIFT; + if (c->max_speed == SCODE_BETA) + c->max_speed += (impr & IMPR_XSPEED_MASK) >> IMPR_XSPEED_SHIFT; + + return 0; +} +EXPORT_SYMBOL(cmp_connection_init); + +/** + * cmp_connection_destroy - free connection manager resources + * @c: the connection manager + */ +void cmp_connection_destroy(struct cmp_connection *c) +{ + WARN_ON(c->connected); + mutex_destroy(&c->mutex); + fw_iso_resources_destroy(&c->resources); +} +EXPORT_SYMBOL(cmp_connection_destroy); + + +static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr) +{ + ipcr &= ~cpu_to_be32(IPCR_BCAST_CONN | + IPCR_P2P_CONN_MASK | + IPCR_CHANNEL_MASK); + ipcr |= cpu_to_be32(1 << IPCR_P2P_CONN_SHIFT); + ipcr |= cpu_to_be32(c->resources.channel << IPCR_CHANNEL_SHIFT); + + return ipcr; +} + +static int ipcr_set_check(struct cmp_connection *c, __be32 ipcr) +{ + if (ipcr & cpu_to_be32(IPCR_BCAST_CONN | + IPCR_P2P_CONN_MASK)) { + cmp_error(c, "plug is already in use\n"); + return -EBUSY; + } + if (!(ipcr & cpu_to_be32(IPCR_ONLINE))) { + cmp_error(c, "plug is not on-line\n"); + return -ECONNREFUSED; + } + + return 0; +} + +/** + * cmp_connection_establish - establish a connection to the target + * @c: the connection manager + * @max_payload_bytes: the amount of data (including CIP headers) per packet + * + * This function establishes a point-to-point connection from the local + * computer to the target by allocating isochronous resources (channel and + * bandwidth) and setting the target's input plug control register. When this + * function succeeds, the caller is responsible for starting transmitting + * packets. + */ +int cmp_connection_establish(struct cmp_connection *c, + unsigned int max_payload_bytes) +{ + int err; + + if (WARN_ON(c->connected)) + return -EISCONN; + + c->speed = min(c->max_speed, + fw_parent_device(c->resources.unit)->max_speed); + + mutex_lock(&c->mutex); + +retry_after_bus_reset: + err = fw_iso_resources_allocate(&c->resources, + max_payload_bytes, c->speed); + if (err < 0) + goto err_mutex; + + err = pcr_modify(c, ipcr_set_modify, ipcr_set_check, + ABORT_ON_BUS_RESET); + if (err == -EAGAIN) { + fw_iso_resources_free(&c->resources); + goto retry_after_bus_reset; + } + if (err < 0) + goto err_resources; + + c->connected = true; + + mutex_unlock(&c->mutex); + + return 0; + +err_resources: + fw_iso_resources_free(&c->resources); +err_mutex: + mutex_unlock(&c->mutex); + + return err; +} +EXPORT_SYMBOL(cmp_connection_establish); + +/** + * cmp_connection_update - update the connection after a bus reset + * @c: the connection manager + * + * This function must be called from the driver's .update handler to reestablish + * any connection that might have been active. + * + * Returns zero on success, or a negative error code. On an error, the + * connection is broken and the caller must stop transmitting iso packets. + */ +int cmp_connection_update(struct cmp_connection *c) +{ + int err; + + mutex_lock(&c->mutex); + + if (!c->connected) { + mutex_unlock(&c->mutex); + return 0; + } + + err = fw_iso_resources_update(&c->resources); + if (err < 0) + goto err_unconnect; + + err = pcr_modify(c, ipcr_set_modify, ipcr_set_check, + SUCCEED_ON_BUS_RESET); + if (err < 0) + goto err_resources; + + mutex_unlock(&c->mutex); + + return 0; + +err_resources: + fw_iso_resources_free(&c->resources); +err_unconnect: + c->connected = false; + mutex_unlock(&c->mutex); + + return err; +} +EXPORT_SYMBOL(cmp_connection_update); + + +static __be32 ipcr_break_modify(struct cmp_connection *c, __be32 ipcr) +{ + return ipcr & ~cpu_to_be32(IPCR_BCAST_CONN | IPCR_P2P_CONN_MASK); +} + +/** + * cmp_connection_break - break the connection to the target + * @c: the connection manager + * + * This function deactives the connection in the target's input plug control + * register, and frees the isochronous resources of the connection. Before + * calling this function, the caller should cease transmitting packets. + */ +void cmp_connection_break(struct cmp_connection *c) +{ + int err; + + mutex_lock(&c->mutex); + + if (!c->connected) { + mutex_unlock(&c->mutex); + return; + } + + err = pcr_modify(c, ipcr_break_modify, NULL, SUCCEED_ON_BUS_RESET); + if (err < 0) + cmp_error(c, "plug is still connected\n"); + + fw_iso_resources_free(&c->resources); + + c->connected = false; + + mutex_unlock(&c->mutex); +} +EXPORT_SYMBOL(cmp_connection_break); diff --git a/sound/firewire/cmp.h b/sound/firewire/cmp.h new file mode 100644 index 000000000000..f47de08feb12 --- /dev/null +++ b/sound/firewire/cmp.h @@ -0,0 +1,41 @@ +#ifndef SOUND_FIREWIRE_CMP_H_INCLUDED +#define SOUND_FIREWIRE_CMP_H_INCLUDED + +#include <linux/mutex.h> +#include <linux/types.h> +#include "iso-resources.h" + +struct fw_unit; + +/** + * struct cmp_connection - manages an isochronous connection to a device + * @speed: the connection's actual speed + * + * This structure manages (using CMP) an isochronous stream from the local + * computer to a device's input plug (iPCR). + * + * There is no corresponding oPCR created on the local computer, so it is not + * possible to overlay connections on top of this one. + */ +struct cmp_connection { + int speed; + /* private: */ + bool connected; + struct mutex mutex; + struct fw_iso_resources resources; + __be32 last_pcr_value; + unsigned int pcr_index; + unsigned int max_speed; +}; + +int cmp_connection_init(struct cmp_connection *connection, + struct fw_unit *unit, + unsigned int ipcr_index); +void cmp_connection_destroy(struct cmp_connection *connection); + +int cmp_connection_establish(struct cmp_connection *connection, + unsigned int max_payload); +int cmp_connection_update(struct cmp_connection *connection); +void cmp_connection_break(struct cmp_connection *connection); + +#endif diff --git a/sound/firewire/fcp.c b/sound/firewire/fcp.c new file mode 100644 index 000000000000..ec578b5ad8da --- /dev/null +++ b/sound/firewire/fcp.c @@ -0,0 +1,224 @@ +/* + * Function Control Protocol (IEC 61883-1) helper functions + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include <linux/device.h> +#include <linux/firewire.h> +#include <linux/firewire-constants.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/spinlock.h> +#include <linux/wait.h> +#include <linux/delay.h> +#include "fcp.h" +#include "lib.h" + +#define CTS_AVC 0x00 + +#define ERROR_RETRIES 3 +#define ERROR_DELAY_MS 5 +#define FCP_TIMEOUT_MS 125 + +static DEFINE_SPINLOCK(transactions_lock); +static LIST_HEAD(transactions); + +enum fcp_state { + STATE_PENDING, + STATE_BUS_RESET, + STATE_COMPLETE, +}; + +struct fcp_transaction { + struct list_head list; + struct fw_unit *unit; + void *response_buffer; + unsigned int response_size; + unsigned int response_match_bytes; + enum fcp_state state; + wait_queue_head_t wait; +}; + +/** + * fcp_avc_transaction - send an AV/C command and wait for its response + * @unit: a unit on the target device + * @command: a buffer containing the command frame; must be DMA-able + * @command_size: the size of @command + * @response: a buffer for the response frame + * @response_size: the maximum size of @response + * @response_match_bytes: a bitmap specifying the bytes used to detect the + * correct response frame + * + * This function sends a FCP command frame to the target and waits for the + * corresponding response frame to be returned. + * + * Because it is possible for multiple FCP transactions to be active at the + * same time, the correct response frame is detected by the value of certain + * bytes. These bytes must be set in @response before calling this function, + * and the corresponding bits must be set in @response_match_bytes. + * + * @command and @response can point to the same buffer. + * + * Asynchronous operation (INTERIM, NOTIFY) is not supported at the moment. + * + * Returns the actual size of the response frame, or a negative error code. + */ +int fcp_avc_transaction(struct fw_unit *unit, + const void *command, unsigned int command_size, + void *response, unsigned int response_size, + unsigned int response_match_bytes) +{ + struct fcp_transaction t; + int tcode, ret, tries = 0; + + t.unit = unit; + t.response_buffer = response; + t.response_size = response_size; + t.response_match_bytes = response_match_bytes; + t.state = STATE_PENDING; + init_waitqueue_head(&t.wait); + + spin_lock_irq(&transactions_lock); + list_add_tail(&t.list, &transactions); + spin_unlock_irq(&transactions_lock); + + for (;;) { + tcode = command_size == 4 ? TCODE_WRITE_QUADLET_REQUEST + : TCODE_WRITE_BLOCK_REQUEST; + ret = snd_fw_transaction(t.unit, tcode, + CSR_REGISTER_BASE + CSR_FCP_COMMAND, + (void *)command, command_size); + if (ret < 0) + break; + + wait_event_timeout(t.wait, t.state != STATE_PENDING, + msecs_to_jiffies(FCP_TIMEOUT_MS)); + + if (t.state == STATE_COMPLETE) { + ret = t.response_size; + break; + } else if (t.state == STATE_BUS_RESET) { + msleep(ERROR_DELAY_MS); + } else if (++tries >= ERROR_RETRIES) { + dev_err(&t.unit->device, "FCP command timed out\n"); + ret = -EIO; + break; + } + } + + spin_lock_irq(&transactions_lock); + list_del(&t.list); + spin_unlock_irq(&transactions_lock); + + return ret; +} +EXPORT_SYMBOL(fcp_avc_transaction); + +/** + * fcp_bus_reset - inform the target handler about a bus reset + * @unit: the unit that might be used by fcp_avc_transaction() + * + * This function must be called from the driver's .update handler to inform + * the FCP transaction handler that a bus reset has happened. Any pending FCP + * transactions are retried. + */ +void fcp_bus_reset(struct fw_unit *unit) +{ + struct fcp_transaction *t; + + spin_lock_irq(&transactions_lock); + list_for_each_entry(t, &transactions, list) { + if (t->unit == unit && + t->state == STATE_PENDING) { + t->state = STATE_BUS_RESET; + wake_up(&t->wait); + } + } + spin_unlock_irq(&transactions_lock); +} +EXPORT_SYMBOL(fcp_bus_reset); + +/* checks whether the response matches the masked bytes in response_buffer */ +static bool is_matching_response(struct fcp_transaction *transaction, + const void *response, size_t length) +{ + const u8 *p1, *p2; + unsigned int mask, i; + + p1 = response; + p2 = transaction->response_buffer; + mask = transaction->response_match_bytes; + + for (i = 0; ; ++i) { + if ((mask & 1) && p1[i] != p2[i]) + return false; + mask >>= 1; + if (!mask) + return true; + if (--length == 0) + return false; + } +} + +static void fcp_response(struct fw_card *card, struct fw_request *request, + int tcode, int destination, int source, + int generation, unsigned long long offset, + void *data, size_t length, void *callback_data) +{ + struct fcp_transaction *t; + unsigned long flags; + + if (length < 1 || (*(const u8 *)data & 0xf0) != CTS_AVC) + return; + + spin_lock_irqsave(&transactions_lock, flags); + list_for_each_entry(t, &transactions, list) { + struct fw_device *device = fw_parent_device(t->unit); + if (device->card != card || + device->generation != generation) + continue; + smp_rmb(); /* node_id vs. generation */ + if (device->node_id != source) + continue; + + if (t->state == STATE_PENDING && + is_matching_response(t, data, length)) { + t->state = STATE_COMPLETE; + t->response_size = min((unsigned int)length, + t->response_size); + memcpy(t->response_buffer, data, t->response_size); + wake_up(&t->wait); + } + } + spin_unlock_irqrestore(&transactions_lock, flags); +} + +static struct fw_address_handler response_register_handler = { + .length = 0x200, + .address_callback = fcp_response, +}; + +static int __init fcp_module_init(void) +{ + static const struct fw_address_region response_register_region = { + .start = CSR_REGISTER_BASE + CSR_FCP_RESPONSE, + .end = CSR_REGISTER_BASE + CSR_FCP_END, + }; + + fw_core_add_address_handler(&response_register_handler, + &response_register_region); + + return 0; +} + +static void __exit fcp_module_exit(void) +{ + WARN_ON(!list_empty(&transactions)); + fw_core_remove_address_handler(&response_register_handler); +} + +module_init(fcp_module_init); +module_exit(fcp_module_exit); diff --git a/sound/firewire/fcp.h b/sound/firewire/fcp.h new file mode 100644 index 000000000000..86595688bd91 --- /dev/null +++ b/sound/firewire/fcp.h @@ -0,0 +1,12 @@ +#ifndef SOUND_FIREWIRE_FCP_H_INCLUDED +#define SOUND_FIREWIRE_FCP_H_INCLUDED + +struct fw_unit; + +int fcp_avc_transaction(struct fw_unit *unit, + const void *command, unsigned int command_size, + void *response, unsigned int response_size, + unsigned int response_match_bytes); +void fcp_bus_reset(struct fw_unit *unit); + +#endif diff --git a/sound/firewire/iso-resources.c b/sound/firewire/iso-resources.c new file mode 100644 index 000000000000..775dbd5f3445 --- /dev/null +++ b/sound/firewire/iso-resources.c @@ -0,0 +1,232 @@ +/* + * isochronous resources helper functions + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include <linux/device.h> +#include <linux/firewire.h> +#include <linux/firewire-constants.h> +#include <linux/jiffies.h> +#include <linux/mutex.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include "iso-resources.h" + +/** + * fw_iso_resources_init - initializes a &struct fw_iso_resources + * @r: the resource manager to initialize + * @unit: the device unit for which the resources will be needed + * + * If the device does not support all channel numbers, change @r->channels_mask + * after calling this function. + */ +int fw_iso_resources_init(struct fw_iso_resources *r, struct fw_unit *unit) +{ + r->buffer = kmalloc(2 * 4, GFP_KERNEL); + if (!r->buffer) + return -ENOMEM; + + r->channels_mask = ~0uLL; + r->unit = fw_unit_get(unit); + mutex_init(&r->mutex); + r->allocated = false; + + return 0; +} + +/** + * fw_iso_resources_destroy - destroy a resource manager + * @r: the resource manager that is no longer needed + */ +void fw_iso_resources_destroy(struct fw_iso_resources *r) +{ + WARN_ON(r->allocated); + kfree(r->buffer); + mutex_destroy(&r->mutex); + fw_unit_put(r->unit); +} + +static unsigned int packet_bandwidth(unsigned int max_payload_bytes, int speed) +{ + unsigned int bytes, s400_bytes; + + /* iso packets have three header quadlets and quadlet-aligned payload */ + bytes = 3 * 4 + ALIGN(max_payload_bytes, 4); + + /* convert to bandwidth units (quadlets at S1600 = bytes at S400) */ + if (speed <= SCODE_400) + s400_bytes = bytes * (1 << (SCODE_400 - speed)); + else + s400_bytes = DIV_ROUND_UP(bytes, 1 << (speed - SCODE_400)); + + return s400_bytes; +} + +static int current_bandwidth_overhead(struct fw_card *card) +{ + /* + * Under the usual pessimistic assumption (cable length 4.5 m), the + * isochronous overhead for N cables is 1.797 µs + N * 0.494 µs, or + * 88.3 + N * 24.3 in bandwidth units. + * + * The calculation below tries to deduce N from the current gap count. + * If the gap count has been optimized by measuring the actual packet + * transmission time, this derived overhead should be near the actual + * overhead as well. + */ + return card->gap_count < 63 ? card->gap_count * 97 / 10 + 89 : 512; +} + +static int wait_isoch_resource_delay_after_bus_reset(struct fw_card *card) +{ + for (;;) { + s64 delay = (card->reset_jiffies + HZ) - get_jiffies_64(); + if (delay <= 0) + return 0; + if (schedule_timeout_interruptible(delay) > 0) + return -ERESTARTSYS; + } +} + +/** + * fw_iso_resources_allocate - allocate isochronous channel and bandwidth + * @r: the resource manager + * @max_payload_bytes: the amount of data (including CIP headers) per packet + * @speed: the speed (e.g., SCODE_400) at which the packets will be sent + * + * This function allocates one isochronous channel and enough bandwidth for the + * specified packet size. + * + * Returns the channel number that the caller must use for streaming, or + * a negative error code. Due to potentionally long delays, this function is + * interruptible and can return -ERESTARTSYS. On success, the caller is + * responsible for calling fw_iso_resources_update() on bus resets, and + * fw_iso_resources_free() when the resources are not longer needed. + */ +int fw_iso_resources_allocate(struct fw_iso_resources *r, + unsigned int max_payload_bytes, int speed) +{ + struct fw_card *card = fw_parent_device(r->unit)->card; + int bandwidth, channel, err; + + if (WARN_ON(r->allocated)) + return -EBADFD; + + r->bandwidth = packet_bandwidth(max_payload_bytes, speed); + +retry_after_bus_reset: + spin_lock_irq(&card->lock); + r->generation = card->generation; + r->bandwidth_overhead = current_bandwidth_overhead(card); + spin_unlock_irq(&card->lock); + + err = wait_isoch_resource_delay_after_bus_reset(card); + if (err < 0) + return err; + + mutex_lock(&r->mutex); + + bandwidth = r->bandwidth + r->bandwidth_overhead; + fw_iso_resource_manage(card, r->generation, r->channels_mask, + &channel, &bandwidth, true, r->buffer); + if (channel == -EAGAIN) { + mutex_unlock(&r->mutex); + goto retry_after_bus_reset; + } + if (channel >= 0) { + r->channel = channel; + r->allocated = true; + } else { + if (channel == -EBUSY) + dev_err(&r->unit->device, + "isochronous resources exhausted\n"); + else + dev_err(&r->unit->device, + "isochronous resource allocation failed\n"); + } + + mutex_unlock(&r->mutex); + + return channel; +} + +/** + * fw_iso_resources_update - update resource allocations after a bus reset + * @r: the resource manager + * + * This function must be called from the driver's .update handler to reallocate + * any resources that were allocated before the bus reset. It is safe to call + * this function if no resources are currently allocated. + * + * Returns a negative error code on failure. If this happens, the caller must + * stop streaming. + */ +int fw_iso_resources_update(struct fw_iso_resources *r) +{ + struct fw_card *card = fw_parent_device(r->unit)->card; + int bandwidth, channel; + + mutex_lock(&r->mutex); + + if (!r->allocated) { + mutex_unlock(&r->mutex); + return 0; + } + + spin_lock_irq(&card->lock); + r->generation = card->generation; + r->bandwidth_overhead = current_bandwidth_overhead(card); + spin_unlock_irq(&card->lock); + + bandwidth = r->bandwidth + r->bandwidth_overhead; + + fw_iso_resource_manage(card, r->generation, 1uLL << r->channel, + &channel, &bandwidth, true, r->buffer); + /* + * When another bus reset happens, pretend that the allocation + * succeeded; we will try again for the new generation later. + */ + if (channel < 0 && channel != -EAGAIN) { + r->allocated = false; + if (channel == -EBUSY) + dev_err(&r->unit->device, + "isochronous resources exhausted\n"); + else + dev_err(&r->unit->device, + "isochronous resource allocation failed\n"); + } + + mutex_unlock(&r->mutex); + + return channel; +} + +/** + * fw_iso_resources_free - frees allocated resources + * @r: the resource manager + * + * This function deallocates the channel and bandwidth, if allocated. + */ +void fw_iso_resources_free(struct fw_iso_resources *r) +{ + struct fw_card *card = fw_parent_device(r->unit)->card; + int bandwidth, channel; + + mutex_lock(&r->mutex); + + if (r->allocated) { + bandwidth = r->bandwidth + r->bandwidth_overhead; + fw_iso_resource_manage(card, r->generation, 1uLL << r->channel, + &channel, &bandwidth, false, r->buffer); + if (channel < 0) + dev_err(&r->unit->device, + "isochronous resource deallocation failed\n"); + + r->allocated = false; + } + + mutex_unlock(&r->mutex); +} diff --git a/sound/firewire/iso-resources.h b/sound/firewire/iso-resources.h new file mode 100644 index 000000000000..3f0730e4d841 --- /dev/null +++ b/sound/firewire/iso-resources.h @@ -0,0 +1,39 @@ +#ifndef SOUND_FIREWIRE_ISO_RESOURCES_H_INCLUDED +#define SOUND_FIREWIRE_ISO_RESOURCES_H_INCLUDED + +#include <linux/mutex.h> +#include <linux/types.h> + +struct fw_unit; + +/** + * struct fw_iso_resources - manages channel/bandwidth allocation + * @channels_mask: if the device does not support all channel numbers, set this + * bit mask to something else than the default (all ones) + * + * This structure manages (de)allocation of isochronous resources (channel and + * bandwidth) for one isochronous stream. + */ +struct fw_iso_resources { + u64 channels_mask; + /* private: */ + struct fw_unit *unit; + struct mutex mutex; + unsigned int channel; + unsigned int bandwidth; /* in bandwidth units, without overhead */ + unsigned int bandwidth_overhead; + int generation; /* in which allocation is valid */ + bool allocated; + __be32 *buffer; +}; + +int fw_iso_resources_init(struct fw_iso_resources *r, + struct fw_unit *unit); +void fw_iso_resources_destroy(struct fw_iso_resources *r); + +int fw_iso_resources_allocate(struct fw_iso_resources *r, + unsigned int max_payload_bytes, int speed); +int fw_iso_resources_update(struct fw_iso_resources *r); +void fw_iso_resources_free(struct fw_iso_resources *r); + +#endif diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c new file mode 100644 index 000000000000..4750cea2210e --- /dev/null +++ b/sound/firewire/lib.c @@ -0,0 +1,85 @@ +/* + * miscellaneous helper functions + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/firewire.h> +#include <linux/module.h> +#include "lib.h" + +#define ERROR_RETRY_DELAY_MS 5 + +/** + * rcode_string - convert a firewire result code to a string + * @rcode: the result + */ +const char *rcode_string(unsigned int rcode) +{ + static const char *const names[] = { + [RCODE_COMPLETE] = "complete", + [RCODE_CONFLICT_ERROR] = "conflict error", + [RCODE_DATA_ERROR] = "data error", + [RCODE_TYPE_ERROR] = "type error", + [RCODE_ADDRESS_ERROR] = "address error", + [RCODE_SEND_ERROR] = "send error", + [RCODE_CANCELLED] = "cancelled", + [RCODE_BUSY] = "busy", + [RCODE_GENERATION] = "generation", + [RCODE_NO_ACK] = "no ack", + }; + + if (rcode < ARRAY_SIZE(names) && names[rcode]) + return names[rcode]; + else + return "unknown"; +} +EXPORT_SYMBOL(rcode_string); + +/** + * snd_fw_transaction - send a request and wait for its completion + * @unit: the driver's unit on the target device + * @tcode: the transaction code + * @offset: the address in the target's address space + * @buffer: input/output data + * @length: length of @buffer + * + * Submits an asynchronous request to the target device, and waits for the + * response. The node ID and the current generation are derived from @unit. + * On a bus reset or an error, the transaction is retried a few times. + * Returns zero on success, or a negative error code. + */ +int snd_fw_transaction(struct fw_unit *unit, int tcode, + u64 offset, void *buffer, size_t length) +{ + struct fw_device *device = fw_parent_device(unit); + int generation, rcode, tries = 0; + + for (;;) { + generation = device->generation; + smp_rmb(); /* node_id vs. generation */ + rcode = fw_run_transaction(device->card, tcode, + device->node_id, generation, + device->max_speed, offset, + buffer, length); + + if (rcode == RCODE_COMPLETE) + return 0; + + if (rcode_is_permanent_error(rcode) || ++tries >= 3) { + dev_err(&unit->device, "transaction failed: %s\n", + rcode_string(rcode)); + return -EIO; + } + + msleep(ERROR_RETRY_DELAY_MS); + } +} +EXPORT_SYMBOL(snd_fw_transaction); + +MODULE_DESCRIPTION("FireWire audio helper functions"); +MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h new file mode 100644 index 000000000000..064f3fd9ab06 --- /dev/null +++ b/sound/firewire/lib.h @@ -0,0 +1,19 @@ +#ifndef SOUND_FIREWIRE_LIB_H_INCLUDED +#define SOUND_FIREWIRE_LIB_H_INCLUDED + +#include <linux/firewire-constants.h> +#include <linux/types.h> + +struct fw_unit; + +int snd_fw_transaction(struct fw_unit *unit, int tcode, + u64 offset, void *buffer, size_t length); +const char *rcode_string(unsigned int rcode); + +/* returns true if retrying the transaction would not make sense */ +static inline bool rcode_is_permanent_error(int rcode) +{ + return rcode == RCODE_TYPE_ERROR || rcode == RCODE_ADDRESS_ERROR; +} + +#endif diff --git a/sound/firewire/packets-buffer.c b/sound/firewire/packets-buffer.c new file mode 100644 index 000000000000..1e20e60ba6a6 --- /dev/null +++ b/sound/firewire/packets-buffer.c @@ -0,0 +1,74 @@ +/* + * helpers for managing a buffer for many packets + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include <linux/firewire.h> +#include <linux/slab.h> +#include "packets-buffer.h" + +/** + * iso_packets_buffer_init - allocates the memory for packets + * @b: the buffer structure to initialize + * @unit: the device at the other end of the stream + * @count: the number of packets + * @packet_size: the (maximum) size of a packet, in bytes + * @direction: %DMA_TO_DEVICE or %DMA_FROM_DEVICE + */ +int iso_packets_buffer_init(struct iso_packets_buffer *b, struct fw_unit *unit, + unsigned int count, unsigned int packet_size, + enum dma_data_direction direction) +{ + unsigned int packets_per_page, pages; + unsigned int i, page_index, offset_in_page; + void *p; + int err; + + b->packets = kmalloc(count * sizeof(*b->packets), GFP_KERNEL); + if (!b->packets) { + err = -ENOMEM; + goto error; + } + + packet_size = L1_CACHE_ALIGN(packet_size); + packets_per_page = PAGE_SIZE / packet_size; + if (WARN_ON(!packets_per_page)) { + err = -EINVAL; + goto error; + } + pages = DIV_ROUND_UP(count, packets_per_page); + + err = fw_iso_buffer_init(&b->iso_buffer, fw_parent_device(unit)->card, + pages, direction); + if (err < 0) + goto err_packets; + + for (i = 0; i < count; ++i) { + page_index = i / packets_per_page; + p = page_address(b->iso_buffer.pages[page_index]); + offset_in_page = (i % packets_per_page) * packet_size; + b->packets[i].buffer = p + offset_in_page; + b->packets[i].offset = page_index * PAGE_SIZE + offset_in_page; + } + + return 0; + +err_packets: + kfree(b->packets); +error: + return err; +} + +/** + * iso_packets_buffer_destroy - frees packet buffer resources + * @b: the buffer structure to free + * @unit: the device at the other end of the stream + */ +void iso_packets_buffer_destroy(struct iso_packets_buffer *b, + struct fw_unit *unit) +{ + fw_iso_buffer_destroy(&b->iso_buffer, fw_parent_device(unit)->card); + kfree(b->packets); +} diff --git a/sound/firewire/packets-buffer.h b/sound/firewire/packets-buffer.h new file mode 100644 index 000000000000..6513c5cb6ea9 --- /dev/null +++ b/sound/firewire/packets-buffer.h @@ -0,0 +1,26 @@ +#ifndef SOUND_FIREWIRE_PACKETS_BUFFER_H_INCLUDED +#define SOUND_FIREWIRE_PACKETS_BUFFER_H_INCLUDED + +#include <linux/dma-mapping.h> +#include <linux/firewire.h> + +/** + * struct iso_packets_buffer - manages a buffer for many packets + * @iso_buffer: the memory containing the packets + * @packets: an array, with each element pointing to one packet + */ +struct iso_packets_buffer { + struct fw_iso_buffer iso_buffer; + struct { + void *buffer; + unsigned int offset; + } *packets; +}; + +int iso_packets_buffer_init(struct iso_packets_buffer *b, struct fw_unit *unit, + unsigned int count, unsigned int packet_size, + enum dma_data_direction direction); +void iso_packets_buffer_destroy(struct iso_packets_buffer *b, + struct fw_unit *unit); + +#endif diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c new file mode 100644 index 000000000000..0fce9218abb1 --- /dev/null +++ b/sound/firewire/speakers.c @@ -0,0 +1,858 @@ +/* + * OXFW970-based speakers driver + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include <linux/device.h> +#include <linux/firewire.h> +#include <linux/firewire-constants.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <sound/control.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include "cmp.h" +#include "fcp.h" +#include "amdtp.h" +#include "lib.h" + +#define OXFORD_FIRMWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x50000) +/* 0x970?vvvv or 0x971?vvvv, where vvvv = firmware version */ + +#define OXFORD_HARDWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x90020) +#define OXFORD_HARDWARE_ID_OXFW970 0x39443841 +#define OXFORD_HARDWARE_ID_OXFW971 0x39373100 + +#define VENDOR_GRIFFIN 0x001292 +#define VENDOR_LACIE 0x00d04b + +#define SPECIFIER_1394TA 0x00a02d +#define VERSION_AVC 0x010001 + +struct device_info { + const char *driver_name; + const char *short_name; + const char *long_name; + int (*pcm_constraints)(struct snd_pcm_runtime *runtime); + unsigned int mixer_channels; + u8 mute_fb_id; + u8 volume_fb_id; +}; + +struct fwspk { + struct snd_card *card; + struct fw_unit *unit; + const struct device_info *device_info; + struct snd_pcm_substream *pcm; + struct mutex mutex; + struct cmp_connection connection; + struct amdtp_out_stream stream; + bool stream_running; + bool mute; + s16 volume[6]; + s16 volume_min; + s16 volume_max; +}; + +MODULE_DESCRIPTION("FireWire speakers driver"); +MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); +MODULE_LICENSE("GPL v2"); + +static int firewave_rate_constraint(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + static unsigned int stereo_rates[] = { 48000, 96000 }; + struct snd_interval *channels = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *rate = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + + /* two channels work only at 48/96 kHz */ + if (snd_interval_max(channels) < 6) + return snd_interval_list(rate, 2, stereo_rates, 0); + return 0; +} + +static int firewave_channels_constraint(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + static const struct snd_interval all_channels = { .min = 6, .max = 6 }; + struct snd_interval *rate = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + + /* 32/44.1 kHz work only with all six channels */ + if (snd_interval_max(rate) < 48000) + return snd_interval_refine(channels, &all_channels); + return 0; +} + +static int firewave_constraints(struct snd_pcm_runtime *runtime) +{ + static unsigned int channels_list[] = { 2, 6 }; + static struct snd_pcm_hw_constraint_list channels_list_constraint = { + .count = 2, + .list = channels_list, + }; + int err; + + runtime->hw.rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000; + runtime->hw.channels_max = 6; + + err = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + &channels_list_constraint); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + firewave_rate_constraint, NULL, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + firewave_channels_constraint, NULL, + SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + return err; + + return 0; +} + +static int lacie_speakers_constraints(struct snd_pcm_runtime *runtime) +{ + runtime->hw.rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000; + + return 0; +} + +static int fwspk_open(struct snd_pcm_substream *substream) +{ + static const struct snd_pcm_hardware hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER, + .formats = AMDTP_OUT_PCM_FORMAT_BITS, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 4 * 1024 * 1024, + .period_bytes_min = 1, + .period_bytes_max = UINT_MAX, + .periods_min = 1, + .periods_max = UINT_MAX, + }; + struct fwspk *fwspk = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + + runtime->hw = hardware; + + err = fwspk->device_info->pcm_constraints(runtime); + if (err < 0) + return err; + err = snd_pcm_limit_hw_rates(runtime); + if (err < 0) + return err; + + err = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_PERIOD_TIME, + 5000, 8192000); + if (err < 0) + return err; + + err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + if (err < 0) + return err; + + return 0; +} + +static int fwspk_close(struct snd_pcm_substream *substream) +{ + return 0; +} + +static void fwspk_stop_stream(struct fwspk *fwspk) +{ + if (fwspk->stream_running) { + amdtp_out_stream_stop(&fwspk->stream); + cmp_connection_break(&fwspk->connection); + fwspk->stream_running = false; + } +} + +static int fwspk_set_rate(struct fwspk *fwspk, unsigned int sfc) +{ + u8 *buf; + int err; + + buf = kmalloc(8, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + buf[0] = 0x00; /* AV/C, CONTROL */ + buf[1] = 0xff; /* unit */ + buf[2] = 0x19; /* INPUT PLUG SIGNAL FORMAT */ + buf[3] = 0x00; /* plug 0 */ + buf[4] = 0x90; /* format: audio */ + buf[5] = 0x00 | sfc; /* AM824, frequency */ + buf[6] = 0xff; /* SYT (not used) */ + buf[7] = 0xff; + + err = fcp_avc_transaction(fwspk->unit, buf, 8, buf, 8, + BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5)); + if (err < 0) + goto error; + if (err < 6 || buf[0] != 0x09 /* ACCEPTED */) { + dev_err(&fwspk->unit->device, "failed to set sample rate\n"); + err = -EIO; + goto error; + } + + err = 0; + +error: + kfree(buf); + + return err; +} + +static int fwspk_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct fwspk *fwspk = substream->private_data; + int err; + + mutex_lock(&fwspk->mutex); + fwspk_stop_stream(fwspk); + mutex_unlock(&fwspk->mutex); + + err = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + goto error; + + amdtp_out_stream_set_rate(&fwspk->stream, params_rate(hw_params)); + amdtp_out_stream_set_pcm(&fwspk->stream, params_channels(hw_params)); + + amdtp_out_stream_set_pcm_format(&fwspk->stream, + params_format(hw_params)); + + err = fwspk_set_rate(fwspk, fwspk->stream.sfc); + if (err < 0) + goto err_buffer; + + return 0; + +err_buffer: + snd_pcm_lib_free_vmalloc_buffer(substream); +error: + return err; +} + +static int fwspk_hw_free(struct snd_pcm_substream *substream) +{ + struct fwspk *fwspk = substream->private_data; + + mutex_lock(&fwspk->mutex); + fwspk_stop_stream(fwspk); + mutex_unlock(&fwspk->mutex); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int fwspk_prepare(struct snd_pcm_substream *substream) +{ + struct fwspk *fwspk = substream->private_data; + int err; + + mutex_lock(&fwspk->mutex); + + if (amdtp_out_streaming_error(&fwspk->stream)) + fwspk_stop_stream(fwspk); + + if (!fwspk->stream_running) { + err = cmp_connection_establish(&fwspk->connection, + amdtp_out_stream_get_max_payload(&fwspk->stream)); + if (err < 0) + goto err_mutex; + + err = amdtp_out_stream_start(&fwspk->stream, + fwspk->connection.resources.channel, + fwspk->connection.speed); + if (err < 0) + goto err_connection; + + fwspk->stream_running = true; + } + + mutex_unlock(&fwspk->mutex); + + amdtp_out_stream_pcm_prepare(&fwspk->stream); + + return 0; + +err_connection: + cmp_connection_break(&fwspk->connection); +err_mutex: + mutex_unlock(&fwspk->mutex); + + return err; +} + +static int fwspk_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct fwspk *fwspk = substream->private_data; + struct snd_pcm_substream *pcm; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + pcm = substream; + break; + case SNDRV_PCM_TRIGGER_STOP: + pcm = NULL; + break; + default: + return -EINVAL; + } + amdtp_out_stream_pcm_trigger(&fwspk->stream, pcm); + return 0; +} + +static snd_pcm_uframes_t fwspk_pointer(struct snd_pcm_substream *substream) +{ + struct fwspk *fwspk = substream->private_data; + + return amdtp_out_stream_pcm_pointer(&fwspk->stream); +} + +static int fwspk_create_pcm(struct fwspk *fwspk) +{ + static struct snd_pcm_ops ops = { + .open = fwspk_open, + .close = fwspk_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = fwspk_hw_params, + .hw_free = fwspk_hw_free, + .prepare = fwspk_prepare, + .trigger = fwspk_trigger, + .pointer = fwspk_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; + struct snd_pcm *pcm; + int err; + + err = snd_pcm_new(fwspk->card, "OXFW970", 0, 1, 0, &pcm); + if (err < 0) + return err; + pcm->private_data = fwspk; + strcpy(pcm->name, fwspk->device_info->short_name); + fwspk->pcm = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + fwspk->pcm->ops = &ops; + return 0; +} + +enum control_action { CTL_READ, CTL_WRITE }; +enum control_attribute { + CTL_MIN = 0x02, + CTL_MAX = 0x03, + CTL_CURRENT = 0x10, +}; + +static int fwspk_mute_command(struct fwspk *fwspk, bool *value, + enum control_action action) +{ + u8 *buf; + u8 response_ok; + int err; + + buf = kmalloc(11, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (action == CTL_READ) { + buf[0] = 0x01; /* AV/C, STATUS */ + response_ok = 0x0c; /* STABLE */ + } else { + buf[0] = 0x00; /* AV/C, CONTROL */ + response_ok = 0x09; /* ACCEPTED */ + } + buf[1] = 0x08; /* audio unit 0 */ + buf[2] = 0xb8; /* FUNCTION BLOCK */ + buf[3] = 0x81; /* function block type: feature */ + buf[4] = fwspk->device_info->mute_fb_id; /* function block ID */ + buf[5] = 0x10; /* control attribute: current */ + buf[6] = 0x02; /* selector length */ + buf[7] = 0x00; /* audio channel number */ + buf[8] = 0x01; /* control selector: mute */ + buf[9] = 0x01; /* control data length */ + if (action == CTL_READ) + buf[10] = 0xff; + else + buf[10] = *value ? 0x70 : 0x60; + + err = fcp_avc_transaction(fwspk->unit, buf, 11, buf, 11, 0x3fe); + if (err < 0) + goto error; + if (err < 11) { + dev_err(&fwspk->unit->device, "short FCP response\n"); + err = -EIO; + goto error; + } + if (buf[0] != response_ok) { + dev_err(&fwspk->unit->device, "mute command failed\n"); + err = -EIO; + goto error; + } + if (action == CTL_READ) + *value = buf[10] == 0x70; + + err = 0; + +error: + kfree(buf); + + return err; +} + +static int fwspk_volume_command(struct fwspk *fwspk, s16 *value, + unsigned int channel, + enum control_attribute attribute, + enum control_action action) +{ + u8 *buf; + u8 response_ok; + int err; + + buf = kmalloc(12, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (action == CTL_READ) { + buf[0] = 0x01; /* AV/C, STATUS */ + response_ok = 0x0c; /* STABLE */ + } else { + buf[0] = 0x00; /* AV/C, CONTROL */ + response_ok = 0x09; /* ACCEPTED */ + } + buf[1] = 0x08; /* audio unit 0 */ + buf[2] = 0xb8; /* FUNCTION BLOCK */ + buf[3] = 0x81; /* function block type: feature */ + buf[4] = fwspk->device_info->volume_fb_id; /* function block ID */ + buf[5] = attribute; /* control attribute */ + buf[6] = 0x02; /* selector length */ + buf[7] = channel; /* audio channel number */ + buf[8] = 0x02; /* control selector: volume */ + buf[9] = 0x02; /* control data length */ + if (action == CTL_READ) { + buf[10] = 0xff; + buf[11] = 0xff; + } else { + buf[10] = *value >> 8; + buf[11] = *value; + } + + err = fcp_avc_transaction(fwspk->unit, buf, 12, buf, 12, 0x3fe); + if (err < 0) + goto error; + if (err < 12) { + dev_err(&fwspk->unit->device, "short FCP response\n"); + err = -EIO; + goto error; + } + if (buf[0] != response_ok) { + dev_err(&fwspk->unit->device, "volume command failed\n"); + err = -EIO; + goto error; + } + if (action == CTL_READ) + *value = (buf[10] << 8) | buf[11]; + + err = 0; + +error: + kfree(buf); + + return err; +} + +static int fwspk_mute_get(struct snd_kcontrol *control, + struct snd_ctl_elem_value *value) +{ + struct fwspk *fwspk = control->private_data; + + value->value.integer.value[0] = !fwspk->mute; + + return 0; +} + +static int fwspk_mute_put(struct snd_kcontrol *control, + struct snd_ctl_elem_value *value) +{ + struct fwspk *fwspk = control->private_data; + bool mute; + int err; + + mute = !value->value.integer.value[0]; + + if (mute == fwspk->mute) + return 0; + + err = fwspk_mute_command(fwspk, &mute, CTL_WRITE); + if (err < 0) + return err; + fwspk->mute = mute; + + return 1; +} + +static int fwspk_volume_info(struct snd_kcontrol *control, + struct snd_ctl_elem_info *info) +{ + struct fwspk *fwspk = control->private_data; + + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = fwspk->device_info->mixer_channels; + info->value.integer.min = fwspk->volume_min; + info->value.integer.max = fwspk->volume_max; + + return 0; +} + +static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 }; + +static int fwspk_volume_get(struct snd_kcontrol *control, + struct snd_ctl_elem_value *value) +{ + struct fwspk *fwspk = control->private_data; + unsigned int i; + + for (i = 0; i < fwspk->device_info->mixer_channels; ++i) + value->value.integer.value[channel_map[i]] = fwspk->volume[i]; + + return 0; +} + +static int fwspk_volume_put(struct snd_kcontrol *control, + struct snd_ctl_elem_value *value) +{ + struct fwspk *fwspk = control->private_data; + unsigned int i, changed_channels; + bool equal_values = true; + s16 volume; + int err; + + for (i = 0; i < fwspk->device_info->mixer_channels; ++i) { + if (value->value.integer.value[i] < fwspk->volume_min || + value->value.integer.value[i] > fwspk->volume_max) + return -EINVAL; + if (value->value.integer.value[i] != + value->value.integer.value[0]) + equal_values = false; + } + + changed_channels = 0; + for (i = 0; i < fwspk->device_info->mixer_channels; ++i) + if (value->value.integer.value[channel_map[i]] != + fwspk->volume[i]) + changed_channels |= 1 << (i + 1); + + if (equal_values && changed_channels != 0) + changed_channels = 1 << 0; + + for (i = 0; i <= fwspk->device_info->mixer_channels; ++i) { + volume = value->value.integer.value[channel_map[i ? i - 1 : 0]]; + if (changed_channels & (1 << i)) { + err = fwspk_volume_command(fwspk, &volume, i, + CTL_CURRENT, CTL_WRITE); + if (err < 0) + return err; + } + if (i > 0) + fwspk->volume[i - 1] = volume; + } + + return changed_channels != 0; +} + +static int fwspk_create_mixer(struct fwspk *fwspk) +{ + static const struct snd_kcontrol_new controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Switch", + .info = snd_ctl_boolean_mono_info, + .get = fwspk_mute_get, + .put = fwspk_mute_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Volume", + .info = fwspk_volume_info, + .get = fwspk_volume_get, + .put = fwspk_volume_put, + }, + }; + unsigned int i, first_ch; + int err; + + err = fwspk_volume_command(fwspk, &fwspk->volume_min, + 0, CTL_MIN, CTL_READ); + if (err < 0) + return err; + err = fwspk_volume_command(fwspk, &fwspk->volume_max, + 0, CTL_MAX, CTL_READ); + if (err < 0) + return err; + + err = fwspk_mute_command(fwspk, &fwspk->mute, CTL_READ); + if (err < 0) + return err; + + first_ch = fwspk->device_info->mixer_channels == 1 ? 0 : 1; + for (i = 0; i < fwspk->device_info->mixer_channels; ++i) { + err = fwspk_volume_command(fwspk, &fwspk->volume[i], + first_ch + i, CTL_CURRENT, CTL_READ); + if (err < 0) + return err; + } + + for (i = 0; i < ARRAY_SIZE(controls); ++i) { + err = snd_ctl_add(fwspk->card, + snd_ctl_new1(&controls[i], fwspk)); + if (err < 0) + return err; + } + + return 0; +} + +static u32 fwspk_read_firmware_version(struct fw_unit *unit) +{ + __be32 data; + int err; + + err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, + OXFORD_FIRMWARE_ID_ADDRESS, &data, 4); + return err >= 0 ? be32_to_cpu(data) : 0; +} + +static void fwspk_card_free(struct snd_card *card) +{ + struct fwspk *fwspk = card->private_data; + struct fw_device *dev = fw_parent_device(fwspk->unit); + + amdtp_out_stream_destroy(&fwspk->stream); + cmp_connection_destroy(&fwspk->connection); + fw_unit_put(fwspk->unit); + fw_device_put(dev); + mutex_destroy(&fwspk->mutex); +} + +static const struct device_info *__devinit fwspk_detect(struct fw_device *dev) +{ + static const struct device_info griffin_firewave = { + .driver_name = "FireWave", + .short_name = "FireWave", + .long_name = "Griffin FireWave Surround", + .pcm_constraints = firewave_constraints, + .mixer_channels = 6, + .mute_fb_id = 0x01, + .volume_fb_id = 0x02, + }; + static const struct device_info lacie_speakers = { + .driver_name = "FWSpeakers", + .short_name = "FireWire Speakers", + .long_name = "LaCie FireWire Speakers", + .pcm_constraints = lacie_speakers_constraints, + .mixer_channels = 1, + .mute_fb_id = 0x01, + .volume_fb_id = 0x01, + }; + struct fw_csr_iterator i; + int key, value; + + fw_csr_iterator_init(&i, dev->config_rom); + while (fw_csr_iterator_next(&i, &key, &value)) + if (key == CSR_VENDOR) + switch (value) { + case VENDOR_GRIFFIN: + return &griffin_firewave; + case VENDOR_LACIE: + return &lacie_speakers; + } + + return NULL; +} + +static int __devinit fwspk_probe(struct device *unit_dev) +{ + struct fw_unit *unit = fw_unit(unit_dev); + struct fw_device *fw_dev = fw_parent_device(unit); + struct snd_card *card; + struct fwspk *fwspk; + u32 firmware; + int err; + + err = snd_card_create(-1, NULL, THIS_MODULE, sizeof(*fwspk), &card); + if (err < 0) + return err; + snd_card_set_dev(card, unit_dev); + + fwspk = card->private_data; + fwspk->card = card; + mutex_init(&fwspk->mutex); + fw_device_get(fw_dev); + fwspk->unit = fw_unit_get(unit); + fwspk->device_info = fwspk_detect(fw_dev); + if (!fwspk->device_info) { + err = -ENODEV; + goto err_unit; + } + + err = cmp_connection_init(&fwspk->connection, unit, 0); + if (err < 0) + goto err_unit; + + err = amdtp_out_stream_init(&fwspk->stream, unit, CIP_NONBLOCKING); + if (err < 0) + goto err_connection; + + card->private_free = fwspk_card_free; + + strcpy(card->driver, fwspk->device_info->driver_name); + strcpy(card->shortname, fwspk->device_info->short_name); + firmware = fwspk_read_firmware_version(unit); + snprintf(card->longname, sizeof(card->longname), + "%s (OXFW%x %04x), GUID %08x%08x at %s, S%d", + fwspk->device_info->long_name, + firmware >> 20, firmware & 0xffff, + fw_dev->config_rom[3], fw_dev->config_rom[4], + dev_name(&unit->device), 100 << fw_dev->max_speed); + strcpy(card->mixername, "OXFW970"); + + err = fwspk_create_pcm(fwspk); + if (err < 0) + goto error; + + err = fwspk_create_mixer(fwspk); + if (err < 0) + goto error; + + err = snd_card_register(card); + if (err < 0) + goto error; + + dev_set_drvdata(unit_dev, fwspk); + + return 0; + +err_connection: + cmp_connection_destroy(&fwspk->connection); +err_unit: + fw_unit_put(fwspk->unit); + fw_device_put(fw_dev); + mutex_destroy(&fwspk->mutex); +error: + snd_card_free(card); + return err; +} + +static int __devexit fwspk_remove(struct device *dev) +{ + struct fwspk *fwspk = dev_get_drvdata(dev); + + snd_card_disconnect(fwspk->card); + + mutex_lock(&fwspk->mutex); + amdtp_out_stream_pcm_abort(&fwspk->stream); + fwspk_stop_stream(fwspk); + mutex_unlock(&fwspk->mutex); + + snd_card_free_when_closed(fwspk->card); + + return 0; +} + +static void fwspk_bus_reset(struct fw_unit *unit) +{ + struct fwspk *fwspk = dev_get_drvdata(&unit->device); + + fcp_bus_reset(fwspk->unit); + + if (cmp_connection_update(&fwspk->connection) < 0) { + mutex_lock(&fwspk->mutex); + amdtp_out_stream_pcm_abort(&fwspk->stream); + fwspk_stop_stream(fwspk); + mutex_unlock(&fwspk->mutex); + return; + } + + amdtp_out_stream_update(&fwspk->stream); +} + +static const struct ieee1394_device_id fwspk_id_table[] = { + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID | + IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION, + .vendor_id = VENDOR_GRIFFIN, + .model_id = 0x00f970, + .specifier_id = SPECIFIER_1394TA, + .version = VERSION_AVC, + }, + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID | + IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION, + .vendor_id = VENDOR_LACIE, + .model_id = 0x00f970, + .specifier_id = SPECIFIER_1394TA, + .version = VERSION_AVC, + }, + { } +}; +MODULE_DEVICE_TABLE(ieee1394, fwspk_id_table); + +static struct fw_driver fwspk_driver = { + .driver = { + .owner = THIS_MODULE, + .name = KBUILD_MODNAME, + .bus = &fw_bus_type, + .probe = fwspk_probe, + .remove = __devexit_p(fwspk_remove), + }, + .update = fwspk_bus_reset, + .id_table = fwspk_id_table, +}; + +static int __init alsa_fwspk_init(void) +{ + return driver_register(&fwspk_driver.driver); +} + +static void __exit alsa_fwspk_exit(void) +{ + driver_unregister(&fwspk_driver.driver); +} + +module_init(alsa_fwspk_init); +module_exit(alsa_fwspk_exit); diff --git a/sound/oss/soundcard.c b/sound/oss/soundcard.c index fcb14a099822..7c7793a0eb25 100644 --- a/sound/oss/soundcard.c +++ b/sound/oss/soundcard.c @@ -526,31 +526,21 @@ bad: } -/* These device names follow the official Linux device list, - * Documentation/devices.txt. Let us know if there are other - * common names we should support for compatibility. - * Only those devices not created by the generic code in sound_core.c are - * registered here. - */ -static const struct { - unsigned short minor; - char *name; - umode_t mode; - int *num; -} dev_list[] = { /* list of minor devices */ -/* seems to be some confusion here -- this device is not in the device list */ - {SND_DEV_DSP16, "dspW", S_IWUGO | S_IRUSR | S_IRGRP, - &num_audiodevs}, - {SND_DEV_AUDIO, "audio", S_IWUGO | S_IRUSR | S_IRGRP, - &num_audiodevs}, -}; - static int dmabuf; static int dmabug; module_param(dmabuf, int, 0444); module_param(dmabug, int, 0444); +/* additional minors for compatibility */ +struct oss_minor_dev { + unsigned short minor; + unsigned int enabled; +} dev_list[] = { + { SND_DEV_DSP16 }, + { SND_DEV_AUDIO }, +}; + static int __init oss_init(void) { int err; @@ -571,18 +561,12 @@ static int __init oss_init(void) sound_dmap_flag = (dmabuf > 0 ? 1 : 0); for (i = 0; i < ARRAY_SIZE(dev_list); i++) { - device_create(sound_class, NULL, - MKDEV(SOUND_MAJOR, dev_list[i].minor), NULL, - "%s", dev_list[i].name); - - if (!dev_list[i].num) - continue; - - for (j = 1; j < *dev_list[i].num; j++) - device_create(sound_class, NULL, - MKDEV(SOUND_MAJOR, - dev_list[i].minor + (j*0x10)), - NULL, "%s%d", dev_list[i].name, j); + j = 0; + do { + unsigned short minor = dev_list[i].minor + j * 0x10; + if (!register_sound_special(&oss_sound_fops, minor)) + dev_list[i].enabled = (1 << j); + } while (++j < num_audiodevs); } if (sound_nblocks >= MAX_MEM_BLOCKS - 1) @@ -596,11 +580,11 @@ static void __exit oss_cleanup(void) int i, j; for (i = 0; i < ARRAY_SIZE(dev_list); i++) { - device_destroy(sound_class, MKDEV(SOUND_MAJOR, dev_list[i].minor)); - if (!dev_list[i].num) - continue; - for (j = 1; j < *dev_list[i].num; j++) - device_destroy(sound_class, MKDEV(SOUND_MAJOR, dev_list[i].minor + (j*0x10))); + j = 0; + do { + if (dev_list[i].enabled & (1 << j)) + unregister_sound_special(dev_list[i].minor); + } while (++j < num_audiodevs); } unregister_sound_special(1); diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 9823d59d7ad7..389cd7931668 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -152,10 +152,16 @@ config SND_AZT3328 select SND_MPU401_UART select SND_PCM select SND_RAWMIDI + select SND_AC97_CODEC help Say Y here to include support for Aztech AZF3328 (PCI168) soundcards. + Supported features: AC97-"conformant" mixer, MPU401/OPL3, analog I/O + (16bit/8bit, many sample rates [<= 66.2kHz], NO hardware mixing), + Digital Enhanced Game Port, 1.024MHz multimedia sequencer timer, + ext. codec (I2S port), onboard amp (4W/4Ohms/ch), suspend/resume. + To compile this driver as a module, choose M here: the module will be called snd-azt3328. @@ -572,13 +578,13 @@ comment "Don't forget to add built-in firmwares for HDSP driver" depends on SND_HDSP=y config SND_HDSPM - tristate "RME Hammerfall DSP MADI" + tristate "RME Hammerfall DSP MADI/RayDAT/AIO" select SND_HWDEP select SND_RAWMIDI select SND_PCM help - Say Y here to include support for RME Hammerfall DSP MADI - soundcards. + Say Y here to include support for RME Hammerfall DSP MADI, + RayDAT and AIO soundcards. To compile this driver as a module, choose M here: the module will be called snd-hdspm. diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index cb62d178b3e0..7f4d619f4ddb 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -71,6 +71,12 @@ static const struct ac97_codec_id snd_ac97_codec_id_vendors[] = { { 0x414b4d00, 0xffffff00, "Asahi Kasei", NULL, NULL }, { 0x414c4300, 0xffffff00, "Realtek", NULL, NULL }, { 0x414c4700, 0xffffff00, "Realtek", NULL, NULL }, +/* + * This is an _inofficial_ Aztech Labs entry + * (value might differ from unknown official Aztech ID), + * currently used by the AC97 emulation of the almost-AC97 PCI168 card. + */ +{ 0x415a5400, 0xffffff00, "Aztech Labs (emulated)", NULL, NULL }, { 0x434d4900, 0xffffff00, "C-Media Electronics", NULL, NULL }, { 0x43525900, 0xffffff00, "Cirrus Logic", NULL, NULL }, { 0x43585400, 0xffffff00, "Conexant", NULL, NULL }, @@ -127,6 +133,7 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = { { 0x414c4781, 0xffffffff, "ALC658D", NULL, NULL }, /* already patched */ { 0x414c4780, 0xfffffff0, "ALC658", patch_alc655, NULL }, { 0x414c4790, 0xfffffff0, "ALC850", patch_alc850, NULL }, +{ 0x415a5401, 0xffffffff, "AZF3328", patch_aztech_azf3328, NULL }, { 0x434d4941, 0xffffffff, "CMI9738", patch_cm9738, NULL }, { 0x434d4961, 0xffffffff, "CMI9739", patch_cm9739, NULL }, { 0x434d4969, 0xffffffff, "CMI9780", patch_cm9780, NULL }, @@ -590,9 +597,9 @@ static int snd_ac97_put_volsw(struct snd_kcontrol *kcontrol, snd_ac97_page_restore(ac97, page_save); #ifdef CONFIG_SND_AC97_POWER_SAVE /* check analog mixer power-down */ - if ((val_mask & 0x8000) && + if ((val_mask & AC97_PD_EAPD) && (kcontrol->private_value & (1<<30))) { - if (val & 0x8000) + if (val & AC97_PD_EAPD) ac97->power_up &= ~(1 << (reg>>1)); else ac97->power_up |= 1 << (reg>>1); @@ -1035,20 +1042,20 @@ static int snd_ac97_dev_free(struct snd_device *device) static int snd_ac97_try_volume_mix(struct snd_ac97 * ac97, int reg) { - unsigned short val, mask = 0x8000; + unsigned short val, mask = AC97_MUTE_MASK_MONO; if (! snd_ac97_valid_reg(ac97, reg)) return 0; switch (reg) { case AC97_MASTER_TONE: - return ac97->caps & 0x04 ? 1 : 0; + return ac97->caps & AC97_BC_BASS_TREBLE ? 1 : 0; case AC97_HEADPHONE: - return ac97->caps & 0x10 ? 1 : 0; + return ac97->caps & AC97_BC_HEADPHONE ? 1 : 0; case AC97_REC_GAIN_MIC: - return ac97->caps & 0x01 ? 1 : 0; + return ac97->caps & AC97_BC_DEDICATED_MIC ? 1 : 0; case AC97_3D_CONTROL: - if (ac97->caps & 0x7c00) { + if (ac97->caps & AC97_BC_3D_TECH_ID_MASK) { val = snd_ac97_read(ac97, reg); /* if nonzero - fixed and we can't set it */ return val == 0; @@ -1104,7 +1111,10 @@ static void check_volume_resolution(struct snd_ac97 *ac97, int reg, unsigned cha *lo_max = *hi_max = 0; for (i = 0 ; i < ARRAY_SIZE(cbit); i++) { unsigned short val; - snd_ac97_write(ac97, reg, 0x8080 | cbit[i] | (cbit[i] << 8)); + snd_ac97_write( + ac97, reg, + AC97_MUTE_MASK_STEREO | cbit[i] | (cbit[i] << 8) + ); /* Do the read twice due to buffers on some ac97 codecs. * e.g. The STAC9704 returns exactly what you wrote to the register * if you read it immediately. This causes the detect routine to fail. @@ -1139,14 +1149,14 @@ static void snd_ac97_change_volume_params2(struct snd_ac97 * ac97, int reg, int unsigned short val, val1; *max = 63; - val = 0x8080 | (0x20 << shift); + val = AC97_MUTE_MASK_STEREO | (0x20 << shift); snd_ac97_write(ac97, reg, val); val1 = snd_ac97_read(ac97, reg); if (val != val1) { *max = 31; } /* reset volume to zero */ - snd_ac97_write_cache(ac97, reg, 0x8080); + snd_ac97_write_cache(ac97, reg, AC97_MUTE_MASK_STEREO); } static inline int printable(unsigned int x) @@ -1183,16 +1193,16 @@ static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg, if (! snd_ac97_valid_reg(ac97, reg)) return 0; - mute_mask = 0x8000; + mute_mask = AC97_MUTE_MASK_MONO; val = snd_ac97_read(ac97, reg); if (check_stereo || (ac97->flags & AC97_STEREO_MUTES)) { /* check whether both mute bits work */ - val1 = val | 0x8080; + val1 = val | AC97_MUTE_MASK_STEREO; snd_ac97_write(ac97, reg, val1); if (val1 == snd_ac97_read(ac97, reg)) - mute_mask = 0x8080; + mute_mask = AC97_MUTE_MASK_STEREO; } - if (mute_mask == 0x8080) { + if (mute_mask == AC97_MUTE_MASK_STEREO) { struct snd_kcontrol_new tmp = AC97_DOUBLE(name, reg, 15, 7, 1, 1); if (check_amix) tmp.private_value |= (1 << 30); @@ -1268,9 +1278,11 @@ static int snd_ac97_cvol_new(struct snd_card *card, char *name, int reg, unsigne err = snd_ctl_add(card, kctl); if (err < 0) return err; - snd_ac97_write_cache(ac97, reg, - (snd_ac97_read(ac97, reg) & 0x8080) | - lo_max | (hi_max << 8)); + snd_ac97_write_cache( + ac97, reg, + (snd_ac97_read(ac97, reg) & AC97_MUTE_MASK_STEREO) + | lo_max | (hi_max << 8) + ); return 0; } @@ -1332,7 +1344,7 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) return err; } - ac97->regs[AC97_CENTER_LFE_MASTER] = 0x8080; + ac97->regs[AC97_CENTER_LFE_MASTER] = AC97_MUTE_MASK_STEREO; /* build center controls */ if ((snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER)) @@ -1410,8 +1422,12 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97))) < 0) return err; set_tlv_db_scale(kctl, db_scale_4bit); - snd_ac97_write_cache(ac97, AC97_PC_BEEP, - snd_ac97_read(ac97, AC97_PC_BEEP) | 0x801e); + snd_ac97_write_cache( + ac97, + AC97_PC_BEEP, + (snd_ac97_read(ac97, AC97_PC_BEEP) + | AC97_MUTE_MASK_MONO | 0x001e) + ); } /* build Phone controls */ @@ -1545,7 +1561,7 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) } /* build Simulated Stereo Enhancement control */ - if (ac97->caps & 0x0008) { + if (ac97->caps & AC97_BC_SIM_STEREO) { if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_STEREO_ENHANCEMENT], ac97))) < 0) return err; } @@ -1557,7 +1573,7 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) } /* build Loudness control */ - if (ac97->caps & 0x0020) { + if (ac97->caps & AC97_BC_LOUDNESS) { if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_general[AC97_GENERAL_LOUDNESS], ac97))) < 0) return err; } @@ -2542,8 +2558,8 @@ void snd_ac97_resume(struct snd_ac97 *ac97) schedule_timeout_uninterruptible(1); } while (time_after_eq(end_time, jiffies)); /* FIXME: extra delay */ - ac97->bus->ops->write(ac97, AC97_MASTER, 0x8000); - if (snd_ac97_read(ac97, AC97_MASTER) != 0x8000) + ac97->bus->ops->write(ac97, AC97_MASTER, AC97_MUTE_MASK_MONO); + if (snd_ac97_read(ac97, AC97_MASTER) != AC97_MUTE_MASK_MONO) msleep(250); } else { end_time = jiffies + msecs_to_jiffies(100); @@ -2747,12 +2763,12 @@ static int master_mute_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem int rshift = (kcontrol->private_value >> 12) & 0x0f; unsigned short mask; if (shift != rshift) - mask = 0x8080; + mask = AC97_MUTE_MASK_STEREO; else - mask = 0x8000; - snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, + mask = AC97_MUTE_MASK_MONO; + snd_ac97_update_bits(ac97, AC97_POWERDOWN, AC97_PD_EAPD, (ac97->regs[AC97_MASTER] & mask) == mask ? - 0x8000 : 0); + AC97_PD_EAPD : 0); } return err; } @@ -2765,7 +2781,10 @@ static int tune_mute_led(struct snd_ac97 *ac97) return -ENOENT; msw->put = master_mute_sw_put; snd_ac97_remove_ctl(ac97, "External Amplifier", NULL); - snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, 0x8000); /* mute LED on */ + snd_ac97_update_bits( + ac97, AC97_POWERDOWN, + AC97_PD_EAPD, AC97_PD_EAPD /* mute LED on */ + ); ac97->scaps |= AC97_SCAP_EAPD_LED; return 0; } @@ -2780,12 +2799,12 @@ static int hp_master_mute_sw_put(struct snd_kcontrol *kcontrol, int rshift = (kcontrol->private_value >> 12) & 0x0f; unsigned short mask; if (shift != rshift) - mask = 0x8080; + mask = AC97_MUTE_MASK_STEREO; else - mask = 0x8000; - snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, + mask = AC97_MUTE_MASK_MONO; + snd_ac97_update_bits(ac97, AC97_POWERDOWN, AC97_PD_EAPD, (ac97->regs[AC97_MASTER] & mask) == mask ? - 0x8000 : 0); + AC97_PD_EAPD : 0); } return err; } @@ -2801,7 +2820,10 @@ static int tune_hp_mute_led(struct snd_ac97 *ac97) snd_ac97_remove_ctl(ac97, "External Amplifier", NULL); snd_ac97_remove_ctl(ac97, "Headphone Playback", "Switch"); snd_ac97_remove_ctl(ac97, "Headphone Playback", "Volume"); - snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, 0x8000); /* mute LED on */ + snd_ac97_update_bits( + ac97, AC97_POWERDOWN, + AC97_PD_EAPD, AC97_PD_EAPD /* mute LED on */ + ); return 0; } diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index bf47574ca1f0..200c9a1d48b7 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -27,6 +27,15 @@ #include "ac97_patch.h" /* + * Forward declarations + */ + +static struct snd_kcontrol *snd_ac97_find_mixer_ctl(struct snd_ac97 *ac97, + const char *name); +static int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name, + const unsigned int *tlv, const char **slaves); + +/* * Chip specific initialization */ @@ -2940,6 +2949,49 @@ static int patch_alc850(struct snd_ac97 *ac97) return 0; } +static int patch_aztech_azf3328_specific(struct snd_ac97 *ac97) +{ + struct snd_kcontrol *kctl_3d_center = + snd_ac97_find_mixer_ctl(ac97, "3D Control - Center"); + struct snd_kcontrol *kctl_3d_depth = + snd_ac97_find_mixer_ctl(ac97, "3D Control - Depth"); + + /* + * 3D register is different from AC97 standard layout + * (also do some renaming, to resemble Windows driver naming) + */ + if (kctl_3d_center) { + kctl_3d_center->private_value = + AC97_SINGLE_VALUE(AC97_3D_CONTROL, 1, 0x07, 0); + snd_ac97_rename_vol_ctl(ac97, + "3D Control - Center", "3D Control - Width" + ); + } + if (kctl_3d_depth) + kctl_3d_depth->private_value = + AC97_SINGLE_VALUE(AC97_3D_CONTROL, 8, 0x03, 0); + + /* Aztech Windows driver calls the + equivalent control "Modem Playback", thus rename it: */ + snd_ac97_rename_vol_ctl(ac97, + "Master Mono Playback", "Modem Playback" + ); + snd_ac97_rename_vol_ctl(ac97, + "Headphone Playback", "FM Synth Playback" + ); + + return 0; +} + +static const struct snd_ac97_build_ops patch_aztech_azf3328_ops = { + .build_specific = patch_aztech_azf3328_specific +}; + +static int patch_aztech_azf3328(struct snd_ac97 *ac97) +{ + ac97->build_ops = &patch_aztech_azf3328_ops; + return 0; +} /* * C-Media CM97xx codecs diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index c80b0b863c54..0ac1f98d91a1 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -21,6 +21,7 @@ * would appreciate it if you grant us the right to use those modifications * for any purpose including commercial applications. */ + /* >0: print Hw params, timer vars. >1: print stream write/copy sizes */ #define REALLY_VERBOSE_LOGGING 0 @@ -36,16 +37,12 @@ #define VPRINTK2(...) #endif -#ifndef ASI_STYLE_NAMES -/* not sure how ALSA style name should look */ -#define ASI_STYLE_NAMES 1 -#endif - #include "hpi_internal.h" #include "hpimsginit.h" #include "hpioctl.h" #include <linux/pci.h> +#include <linux/version.h> #include <linux/init.h> #include <linux/jiffies.h> #include <linux/slab.h> @@ -85,11 +82,11 @@ MODULE_PARM_DESC(enable_hpi_hwdep, /* identify driver */ #ifdef KERNEL_ALSA_BUILD -static char *build_info = "built using headers from kernel source"; +static char *build_info = "Built using headers from kernel source"; module_param(build_info, charp, S_IRUGO); MODULE_PARM_DESC(build_info, "built using headers from kernel source"); #else -static char *build_info = "built within ALSA source"; +static char *build_info = "Built within ALSA source"; module_param(build_info, charp, S_IRUGO); MODULE_PARM_DESC(build_info, "built within ALSA source"); #endif @@ -100,13 +97,14 @@ static const int mixer_dump; #define DEFAULT_SAMPLERATE 44100 static int adapter_fs = DEFAULT_SAMPLERATE; -static struct hpi_hsubsys *ss; /* handle to HPI audio subsystem */ - /* defaults */ #define PERIODS_MIN 2 -#define PERIOD_BYTES_MIN 2304 +#define PERIOD_BYTES_MIN 2048 #define BUFFER_BYTES_MAX (512 * 1024) +/* convert stream to character */ +#define SCHR(s) ((s == SNDRV_PCM_STREAM_PLAYBACK) ? 'P' : 'C') + /*#define TIMER_MILLISECONDS 20 #define FORCE_TIMER_JIFFIES ((TIMER_MILLISECONDS * HZ + 999)/1000) */ @@ -152,11 +150,12 @@ struct snd_card_asihpi_pcm { struct timer_list timer; unsigned int respawn_timer; unsigned int hpi_buffer_attached; - unsigned int pcm_size; - unsigned int pcm_count; + unsigned int buffer_bytes; + unsigned int period_bytes; unsigned int bytes_per_sec; - unsigned int pcm_irq_pos; /* IRQ position */ - unsigned int pcm_buf_pos; /* position in buffer */ + unsigned int pcm_buf_host_rw_ofs; /* Host R/W pos */ + unsigned int pcm_buf_dma_ofs; /* DMA R/W offset in buffer */ + unsigned int pcm_buf_elapsed_dma_ofs; /* DMA R/W offset in buffer */ struct snd_pcm_substream *substream; u32 h_stream; struct hpi_format format; @@ -167,7 +166,6 @@ struct snd_card_asihpi_pcm { /* Functions to allow driver to give a buffer to HPI for busmastering */ static u16 hpi_stream_host_buffer_attach( - struct hpi_hsubsys *hS, u32 h_stream, /* handle to outstream. */ u32 size_in_bytes, /* size in bytes of bus mastering buffer */ u32 pci_address @@ -194,10 +192,7 @@ static u16 hpi_stream_host_buffer_attach( return hr.error; } -static u16 hpi_stream_host_buffer_detach( - struct hpi_hsubsys *hS, - u32 h_stream -) +static u16 hpi_stream_host_buffer_detach(u32 h_stream) { struct hpi_message hm; struct hpi_response hr; @@ -218,24 +213,23 @@ static u16 hpi_stream_host_buffer_detach( return hr.error; } -static inline u16 hpi_stream_start(struct hpi_hsubsys *hS, u32 h_stream) +static inline u16 hpi_stream_start(u32 h_stream) { if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM) - return hpi_outstream_start(hS, h_stream); + return hpi_outstream_start(h_stream); else - return hpi_instream_start(hS, h_stream); + return hpi_instream_start(h_stream); } -static inline u16 hpi_stream_stop(struct hpi_hsubsys *hS, u32 h_stream) +static inline u16 hpi_stream_stop(u32 h_stream) { if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM) - return hpi_outstream_stop(hS, h_stream); + return hpi_outstream_stop(h_stream); else - return hpi_instream_stop(hS, h_stream); + return hpi_instream_stop(h_stream); } static inline u16 hpi_stream_get_info_ex( - struct hpi_hsubsys *hS, u32 h_stream, u16 *pw_state, u32 *pbuffer_size, @@ -244,42 +238,43 @@ static inline u16 hpi_stream_get_info_ex( u32 *pauxiliary_data ) { + u16 e; if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM) - return hpi_outstream_get_info_ex(hS, h_stream, pw_state, + e = hpi_outstream_get_info_ex(h_stream, pw_state, pbuffer_size, pdata_in_buffer, psample_count, pauxiliary_data); else - return hpi_instream_get_info_ex(hS, h_stream, pw_state, + e = hpi_instream_get_info_ex(h_stream, pw_state, pbuffer_size, pdata_in_buffer, psample_count, pauxiliary_data); + return e; } -static inline u16 hpi_stream_group_add(struct hpi_hsubsys *hS, +static inline u16 hpi_stream_group_add( u32 h_master, u32 h_stream) { if (hpi_handle_object(h_master) == HPI_OBJ_OSTREAM) - return hpi_outstream_group_add(hS, h_master, h_stream); + return hpi_outstream_group_add(h_master, h_stream); else - return hpi_instream_group_add(hS, h_master, h_stream); + return hpi_instream_group_add(h_master, h_stream); } -static inline u16 hpi_stream_group_reset(struct hpi_hsubsys *hS, - u32 h_stream) +static inline u16 hpi_stream_group_reset(u32 h_stream) { if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM) - return hpi_outstream_group_reset(hS, h_stream); + return hpi_outstream_group_reset(h_stream); else - return hpi_instream_group_reset(hS, h_stream); + return hpi_instream_group_reset(h_stream); } -static inline u16 hpi_stream_group_get_map(struct hpi_hsubsys *hS, +static inline u16 hpi_stream_group_get_map( u32 h_stream, u32 *mo, u32 *mi) { if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM) - return hpi_outstream_group_get_map(hS, h_stream, mo, mi); + return hpi_outstream_group_get_map(h_stream, mo, mi); else - return hpi_instream_group_get_map(hS, h_stream, mo, mi); + return hpi_instream_group_get_map(h_stream, mo, mi); } static u16 handle_error(u16 err, int line, char *filename) @@ -299,11 +294,11 @@ static void print_hwparams(struct snd_pcm_hw_params *p) { snd_printd("HWPARAMS \n"); snd_printd("samplerate %d \n", params_rate(p)); - snd_printd("channels %d \n", params_channels(p)); - snd_printd("format %d \n", params_format(p)); + snd_printd("Channels %d \n", params_channels(p)); + snd_printd("Format %d \n", params_format(p)); snd_printd("subformat %d \n", params_subformat(p)); - snd_printd("buffer bytes %d \n", params_buffer_bytes(p)); - snd_printd("period bytes %d \n", params_period_bytes(p)); + snd_printd("Buffer bytes %d \n", params_buffer_bytes(p)); + snd_printd("Period bytes %d \n", params_period_bytes(p)); snd_printd("access %d \n", params_access(p)); snd_printd("period_size %d \n", params_period_size(p)); snd_printd("periods %d \n", params_periods(p)); @@ -335,7 +330,7 @@ static snd_pcm_format_t hpi_to_alsa_formats[] = { */ -1 #else - /* SNDRV_PCM_FORMAT_S24_3LE */ /* { HPI_FORMAT_PCM24_SIGNED 15 */ + /* SNDRV_PCM_FORMAT_S24_3LE */ /* HPI_FORMAT_PCM24_SIGNED 15 */ #endif }; @@ -378,20 +373,20 @@ static void snd_card_asihpi_pcm_samplerates(struct snd_card_asihpi *asihpi, } else { /* on cards without SRC, valid rates are determined by sampleclock */ - err = hpi_mixer_get_control(ss, asihpi->h_mixer, + err = hpi_mixer_get_control(asihpi->h_mixer, HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, HPI_CONTROL_SAMPLECLOCK, &h_control); if (err) { snd_printk(KERN_ERR - "no local sampleclock, err %d\n", err); + "No local sampleclock, err %d\n", err); } for (idx = 0; idx < 100; idx++) { - if (hpi_sample_clock_query_local_rate(ss, + if (hpi_sample_clock_query_local_rate( h_control, idx, &sample_rate)) { if (!idx) snd_printk(KERN_ERR - "local rate query failed\n"); + "Local rate query failed\n"); break; } @@ -480,10 +475,10 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream, format, params_rate(params), 0, 0)); if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - if (hpi_instream_reset(ss, dpcm->h_stream) != 0) + if (hpi_instream_reset(dpcm->h_stream) != 0) return -EINVAL; - if (hpi_instream_set_format(ss, + if (hpi_instream_set_format( dpcm->h_stream, &dpcm->format) != 0) return -EINVAL; } @@ -491,10 +486,10 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream, dpcm->hpi_buffer_attached = 0; if (card->support_mmap) { - err = hpi_stream_host_buffer_attach(ss, dpcm->h_stream, + err = hpi_stream_host_buffer_attach(dpcm->h_stream, params_buffer_bytes(params), runtime->dma_addr); if (err == 0) { - snd_printd(KERN_INFO + VPRINTK1(KERN_INFO "stream_host_buffer_attach succeeded %u %lu\n", params_buffer_bytes(params), (unsigned long)runtime->dma_addr); @@ -505,11 +500,11 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream, return -ENOMEM; } - err = hpi_stream_get_info_ex(ss, dpcm->h_stream, NULL, + err = hpi_stream_get_info_ex(dpcm->h_stream, NULL, &dpcm->hpi_buffer_attached, NULL, NULL, NULL); - snd_printd(KERN_INFO "stream_host_buffer_attach status 0x%x\n", + VPRINTK1(KERN_INFO "stream_host_buffer_attach status 0x%x\n", dpcm->hpi_buffer_attached); } bytes_per_sec = params_rate(params) * params_channels(params); @@ -520,16 +515,32 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream, return -EINVAL; dpcm->bytes_per_sec = bytes_per_sec; - dpcm->pcm_size = params_buffer_bytes(params); - dpcm->pcm_count = params_period_bytes(params); - snd_printd(KERN_INFO "pcm_size=%d, pcm_count=%d, bps=%d\n", - dpcm->pcm_size, dpcm->pcm_count, bytes_per_sec); + dpcm->buffer_bytes = params_buffer_bytes(params); + dpcm->period_bytes = params_period_bytes(params); + VPRINTK1(KERN_INFO "buffer_bytes=%d, period_bytes=%d, bps=%d\n", + dpcm->buffer_bytes, dpcm->period_bytes, bytes_per_sec); - dpcm->pcm_irq_pos = 0; - dpcm->pcm_buf_pos = 0; return 0; } +static int +snd_card_asihpi_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_card_asihpi_pcm *dpcm = runtime->private_data; + if (dpcm->hpi_buffer_attached) + hpi_stream_host_buffer_detach(dpcm->h_stream); + + snd_pcm_lib_free_pages(substream); + return 0; +} + +static void snd_card_asihpi_runtime_free(struct snd_pcm_runtime *runtime) +{ + struct snd_card_asihpi_pcm *dpcm = runtime->private_data; + kfree(dpcm); +} + static void snd_card_asihpi_pcm_timer_start(struct snd_pcm_substream * substream) { @@ -537,9 +548,9 @@ static void snd_card_asihpi_pcm_timer_start(struct snd_pcm_substream * struct snd_card_asihpi_pcm *dpcm = runtime->private_data; int expiry; - expiry = (dpcm->pcm_count * HZ / dpcm->bytes_per_sec); - /* wait longer the first time, for samples to propagate */ - expiry = max(expiry, 20); + expiry = HZ / 200; + /*? (dpcm->period_bytes * HZ / dpcm->bytes_per_sec); */ + expiry = max(expiry, 1); /* don't let it be zero! */ dpcm->timer.expires = jiffies + expiry; dpcm->respawn_timer = 1; add_timer(&dpcm->timer); @@ -562,37 +573,44 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, struct snd_pcm_substream *s; u16 e; - snd_printd("trigger %dstream %d\n", - substream->stream, substream->number); + VPRINTK1(KERN_INFO "%c%d trigger\n", + SCHR(substream->stream), substream->number); switch (cmd) { case SNDRV_PCM_TRIGGER_START: snd_pcm_group_for_each_entry(s, substream) { - struct snd_card_asihpi_pcm *ds; - ds = s->runtime->private_data; + struct snd_pcm_runtime *runtime = s->runtime; + struct snd_card_asihpi_pcm *ds = runtime->private_data; if (snd_pcm_substream_chip(s) != card) continue; + /* don't link Cap and Play */ + if (substream->stream != s->stream) + continue; + if ((s->stream == SNDRV_PCM_STREAM_PLAYBACK) && (card->support_mmap)) { /* How do I know how much valid data is present - * in buffer? Just guessing 2 periods, but if + * in buffer? Must be at least one period! + * Guessing 2 periods, but if * buffer is bigger it may contain even more * data?? */ - unsigned int preload = ds->pcm_count * 2; - VPRINTK2("preload %d\n", preload); + unsigned int preload = ds->period_bytes * 1; + VPRINTK2(KERN_INFO "%d preload x%x\n", s->number, preload); hpi_handle_error(hpi_outstream_write_buf( - ss, ds->h_stream, - &s->runtime->dma_area[0], + ds->h_stream, + &runtime->dma_area[0], preload, &ds->format)); + ds->pcm_buf_host_rw_ofs = preload; } if (card->support_grouping) { - VPRINTK1("\t_group %dstream %d\n", s->stream, + VPRINTK1(KERN_INFO "\t%c%d group\n", + SCHR(s->stream), s->number); - e = hpi_stream_group_add(ss, + e = hpi_stream_group_add( dpcm->h_stream, ds->h_stream); if (!e) { @@ -604,10 +622,12 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, } else break; } - snd_printd("start\n"); + VPRINTK1(KERN_INFO "start\n"); /* start the master stream */ snd_card_asihpi_pcm_timer_start(substream); - hpi_handle_error(hpi_stream_start(ss, dpcm->h_stream)); + if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) || + !card->support_mmap) + hpi_handle_error(hpi_stream_start(dpcm->h_stream)); break; case SNDRV_PCM_TRIGGER_STOP: @@ -615,88 +635,73 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, snd_pcm_group_for_each_entry(s, substream) { if (snd_pcm_substream_chip(s) != card) continue; + /* don't link Cap and Play */ + if (substream->stream != s->stream) + continue; /*? workaround linked streams don't transition to SETUP 20070706*/ s->runtime->status->state = SNDRV_PCM_STATE_SETUP; if (card->support_grouping) { - VPRINTK1("\t_group %dstream %d\n", s->stream, + VPRINTK1(KERN_INFO "\t%c%d group\n", + SCHR(s->stream), s->number); snd_pcm_trigger_done(s, substream); } else break; } - snd_printd("stop\n"); + VPRINTK1(KERN_INFO "stop\n"); /* _prepare and _hwparams reset the stream */ - hpi_handle_error(hpi_stream_stop(ss, dpcm->h_stream)); + hpi_handle_error(hpi_stream_stop(dpcm->h_stream)); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) hpi_handle_error( - hpi_outstream_reset(ss, dpcm->h_stream)); + hpi_outstream_reset(dpcm->h_stream)); if (card->support_grouping) - hpi_handle_error(hpi_stream_group_reset(ss, - dpcm->h_stream)); + hpi_handle_error(hpi_stream_group_reset(dpcm->h_stream)); break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - snd_printd("pause release\n"); - hpi_handle_error(hpi_stream_start(ss, dpcm->h_stream)); + VPRINTK1(KERN_INFO "pause release\n"); + hpi_handle_error(hpi_stream_start(dpcm->h_stream)); snd_card_asihpi_pcm_timer_start(substream); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - snd_printd("pause\n"); + VPRINTK1(KERN_INFO "pause\n"); snd_card_asihpi_pcm_timer_stop(substream); - hpi_handle_error(hpi_stream_stop(ss, dpcm->h_stream)); + hpi_handle_error(hpi_stream_stop(dpcm->h_stream)); break; default: - snd_printd("\tINVALID\n"); + snd_printd(KERN_ERR "\tINVALID\n"); return -EINVAL; } return 0; } -static int -snd_card_asihpi_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_card_asihpi_pcm *dpcm = runtime->private_data; - if (dpcm->hpi_buffer_attached) - hpi_stream_host_buffer_detach(ss, dpcm->h_stream); - - snd_pcm_lib_free_pages(substream); - return 0; -} - -static void snd_card_asihpi_runtime_free(struct snd_pcm_runtime *runtime) -{ - struct snd_card_asihpi_pcm *dpcm = runtime->private_data; - kfree(dpcm); -} - /*algorithm outline Without linking degenerates to getting single stream pos etc Without mmap 2nd loop degenerates to snd_pcm_period_elapsed */ /* -buf_pos=get_buf_pos(s); +pcm_buf_dma_ofs=get_buf_pos(s); for_each_linked_stream(s) { - buf_pos=get_buf_pos(s); - min_buf_pos = modulo_min(min_buf_pos, buf_pos, pcm_size) - new_data = min(new_data, calc_new_data(buf_pos,irq_pos) + pcm_buf_dma_ofs=get_buf_pos(s); + min_buf_pos = modulo_min(min_buf_pos, pcm_buf_dma_ofs, buffer_bytes) + new_data = min(new_data, calc_new_data(pcm_buf_dma_ofs,irq_pos) } timer.expires = jiffies + predict_next_period_ready(min_buf_pos); for_each_linked_stream(s) { - s->buf_pos = min_buf_pos; - if (new_data > pcm_count) { + s->pcm_buf_dma_ofs = min_buf_pos; + if (new_data > period_bytes) { if (mmap) { - irq_pos = (irq_pos + pcm_count) % pcm_size; + irq_pos = (irq_pos + period_bytes) % buffer_bytes; if (playback) { - write(pcm_count); + write(period_bytes); } else { - read(pcm_count); + read(period_bytes); } } snd_pcm_period_elapsed(s); @@ -724,105 +729,136 @@ static inline unsigned int modulo_min(unsigned int a, unsigned int b, static void snd_card_asihpi_timer_function(unsigned long data) { struct snd_card_asihpi_pcm *dpcm = (struct snd_card_asihpi_pcm *)data; - struct snd_card_asihpi *card = snd_pcm_substream_chip(dpcm->substream); + struct snd_pcm_substream *substream = dpcm->substream; + struct snd_card_asihpi *card = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime; struct snd_pcm_substream *s; unsigned int newdata = 0; - unsigned int buf_pos, min_buf_pos = 0; + unsigned int pcm_buf_dma_ofs, min_buf_pos = 0; unsigned int remdata, xfercount, next_jiffies; int first = 1; + int loops = 0; u16 state; - u32 buffer_size, data_avail, samples_played, aux; + u32 buffer_size, bytes_avail, samples_played, on_card_bytes; + + VPRINTK1(KERN_INFO "%c%d snd_card_asihpi_timer_function\n", + SCHR(substream->stream), substream->number); /* find minimum newdata and buffer pos in group */ - snd_pcm_group_for_each_entry(s, dpcm->substream) { + snd_pcm_group_for_each_entry(s, substream) { struct snd_card_asihpi_pcm *ds = s->runtime->private_data; runtime = s->runtime; if (snd_pcm_substream_chip(s) != card) continue; - hpi_handle_error(hpi_stream_get_info_ex(ss, + /* don't link Cap and Play */ + if (substream->stream != s->stream) + continue; + + hpi_handle_error(hpi_stream_get_info_ex( ds->h_stream, &state, - &buffer_size, &data_avail, - &samples_played, &aux)); + &buffer_size, &bytes_avail, + &samples_played, &on_card_bytes)); /* number of bytes in on-card buffer */ - runtime->delay = aux; - - if (state == HPI_STATE_DRAINED) { - snd_printd(KERN_WARNING "outstream %d drained\n", - s->number); - snd_pcm_stop(s, SNDRV_PCM_STATE_XRUN); - return; - } + runtime->delay = on_card_bytes; if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { - buf_pos = frames_to_bytes(runtime, samples_played); - } else { - buf_pos = data_avail + ds->pcm_irq_pos; - } + pcm_buf_dma_ofs = ds->pcm_buf_host_rw_ofs - bytes_avail; + if (state == HPI_STATE_STOPPED) { + if ((bytes_avail == 0) && + (on_card_bytes < ds->pcm_buf_host_rw_ofs)) { + hpi_handle_error(hpi_stream_start(ds->h_stream)); + VPRINTK1(KERN_INFO "P%d start\n", s->number); + } + } else if (state == HPI_STATE_DRAINED) { + VPRINTK1(KERN_WARNING "P%d drained\n", + s->number); + /*snd_pcm_stop(s, SNDRV_PCM_STATE_XRUN); + continue; */ + } + } else + pcm_buf_dma_ofs = bytes_avail + ds->pcm_buf_host_rw_ofs; if (first) { /* can't statically init min when wrap is involved */ - min_buf_pos = buf_pos; - newdata = (buf_pos - ds->pcm_irq_pos) % ds->pcm_size; + min_buf_pos = pcm_buf_dma_ofs; + newdata = (pcm_buf_dma_ofs - ds->pcm_buf_elapsed_dma_ofs) % ds->buffer_bytes; first = 0; } else { min_buf_pos = - modulo_min(min_buf_pos, buf_pos, UINT_MAX+1L); + modulo_min(min_buf_pos, pcm_buf_dma_ofs, UINT_MAX+1L); newdata = min( - (buf_pos - ds->pcm_irq_pos) % ds->pcm_size, + (pcm_buf_dma_ofs - ds->pcm_buf_elapsed_dma_ofs) % ds->buffer_bytes, newdata); } - VPRINTK1("PB timer hw_ptr x%04lX, appl_ptr x%04lX\n", + VPRINTK1(KERN_INFO "PB timer hw_ptr x%04lX, appl_ptr x%04lX\n", (unsigned long)frames_to_bytes(runtime, runtime->status->hw_ptr), (unsigned long)frames_to_bytes(runtime, runtime->control->appl_ptr)); - VPRINTK1("%d S=%d, irq=%04X, pos=x%04X, left=x%04X," - " aux=x%04X space=x%04X\n", s->number, - state, ds->pcm_irq_pos, buf_pos, (int)data_avail, - (int)aux, buffer_size-data_avail); + + VPRINTK1(KERN_INFO "%d %c%d S=%d, rw=%04X, dma=x%04X, left=x%04X," + " aux=x%04X space=x%04X\n", + loops, SCHR(s->stream), s->number, + state, ds->pcm_buf_host_rw_ofs, pcm_buf_dma_ofs, (int)bytes_avail, + (int)on_card_bytes, buffer_size-bytes_avail); + loops++; } + pcm_buf_dma_ofs = min_buf_pos; - remdata = newdata % dpcm->pcm_count; - xfercount = newdata - remdata; /* a multiple of pcm_count */ - next_jiffies = ((dpcm->pcm_count-remdata) * HZ / dpcm->bytes_per_sec)+1; - next_jiffies = max(next_jiffies, 2U * HZ / 1000U); + remdata = newdata % dpcm->period_bytes; + xfercount = newdata - remdata; /* a multiple of period_bytes */ + /* come back when on_card_bytes has decreased enough to allow + write to happen, or when data has been consumed to make another + period + */ + if (xfercount && (on_card_bytes > dpcm->period_bytes)) + next_jiffies = ((on_card_bytes - dpcm->period_bytes) * HZ / dpcm->bytes_per_sec); + else + next_jiffies = ((dpcm->period_bytes - remdata) * HZ / dpcm->bytes_per_sec); + + next_jiffies = max(next_jiffies, 1U); dpcm->timer.expires = jiffies + next_jiffies; - VPRINTK1("jif %d buf pos x%04X newdata x%04X xc x%04X\n", - next_jiffies, min_buf_pos, newdata, xfercount); + VPRINTK1(KERN_INFO "jif %d buf pos x%04X newdata x%04X xfer x%04X\n", + next_jiffies, pcm_buf_dma_ofs, newdata, xfercount); - snd_pcm_group_for_each_entry(s, dpcm->substream) { + snd_pcm_group_for_each_entry(s, substream) { struct snd_card_asihpi_pcm *ds = s->runtime->private_data; - ds->pcm_buf_pos = min_buf_pos; - if (xfercount) { + /* don't link Cap and Play */ + if (substream->stream != s->stream) + continue; + + ds->pcm_buf_dma_ofs = pcm_buf_dma_ofs; + + if (xfercount && (on_card_bytes <= ds->period_bytes)) { if (card->support_mmap) { - ds->pcm_irq_pos = ds->pcm_irq_pos + xfercount; if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { - VPRINTK2("write OS%d x%04x\n", + VPRINTK2(KERN_INFO "P%d write x%04x\n", s->number, - ds->pcm_count); + ds->period_bytes); hpi_handle_error( hpi_outstream_write_buf( - ss, ds->h_stream, + ds->h_stream, &s->runtime-> dma_area[0], xfercount, &ds->format)); } else { - VPRINTK2("read IS%d x%04x\n", + VPRINTK2(KERN_INFO "C%d read x%04x\n", s->number, - dpcm->pcm_count); + xfercount); hpi_handle_error( hpi_instream_read_buf( - ss, ds->h_stream, + ds->h_stream, NULL, xfercount)); } + ds->pcm_buf_host_rw_ofs = ds->pcm_buf_host_rw_ofs + xfercount; } /* else R/W will be handled by read/write callbacks */ + ds->pcm_buf_elapsed_dma_ofs = pcm_buf_dma_ofs; snd_pcm_period_elapsed(s); } } @@ -845,12 +881,12 @@ static int snd_card_asihpi_playback_prepare(struct snd_pcm_substream * struct snd_pcm_runtime *runtime = substream->runtime; struct snd_card_asihpi_pcm *dpcm = runtime->private_data; - snd_printd(KERN_INFO "playback prepare %d\n", substream->number); - - hpi_handle_error(hpi_outstream_reset(ss, dpcm->h_stream)); - dpcm->pcm_irq_pos = 0; - dpcm->pcm_buf_pos = 0; + VPRINTK1(KERN_INFO "playback prepare %d\n", substream->number); + hpi_handle_error(hpi_outstream_reset(dpcm->h_stream)); + dpcm->pcm_buf_host_rw_ofs = 0; + dpcm->pcm_buf_dma_ofs = 0; + dpcm->pcm_buf_elapsed_dma_ofs = 0; return 0; } @@ -861,27 +897,8 @@ snd_card_asihpi_playback_pointer(struct snd_pcm_substream *substream) struct snd_card_asihpi_pcm *dpcm = runtime->private_data; snd_pcm_uframes_t ptr; - u32 samples_played; - u16 err; - - if (!snd_pcm_stream_linked(substream)) { - /* NOTE, can use samples played for playback position here and - * in timer fn because it LAGS the actual read pointer, and is a - * better representation of actual playout position - */ - err = hpi_outstream_get_info_ex(ss, dpcm->h_stream, NULL, - NULL, NULL, - &samples_played, NULL); - hpi_handle_error(err); - - dpcm->pcm_buf_pos = frames_to_bytes(runtime, samples_played); - } - /* else must return most conservative value found in timer func - * by looping over all streams - */ - - ptr = bytes_to_frames(runtime, dpcm->pcm_buf_pos % dpcm->pcm_size); - VPRINTK2("playback_pointer=%04ld\n", (unsigned long)ptr); + ptr = bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes); + /* VPRINTK2(KERN_INFO "playback_pointer=x%04lx\n", (unsigned long)ptr); */ return ptr; } @@ -898,12 +915,12 @@ static void snd_card_asihpi_playback_format(struct snd_card_asihpi *asihpi, /* on cards without SRC, must query at valid rate, * maybe set by external sync */ - err = hpi_mixer_get_control(ss, asihpi->h_mixer, + err = hpi_mixer_get_control(asihpi->h_mixer, HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, HPI_CONTROL_SAMPLECLOCK, &h_control); if (!err) - err = hpi_sample_clock_get_sample_rate(ss, h_control, + err = hpi_sample_clock_get_sample_rate(h_control, &sample_rate); for (format = HPI_FORMAT_PCM8_UNSIGNED; @@ -911,7 +928,7 @@ static void snd_card_asihpi_playback_format(struct snd_card_asihpi *asihpi, err = hpi_format_create(&hpi_format, 2, format, sample_rate, 128000, 0); if (!err) - err = hpi_outstream_query_format(ss, h_stream, + err = hpi_outstream_query_format(h_stream, &hpi_format); if (!err && (hpi_to_alsa_formats[format] != -1)) pcmhw->formats |= @@ -942,7 +959,7 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream) return -ENOMEM; err = - hpi_outstream_open(ss, card->adapter_index, + hpi_outstream_open(card->adapter_index, substream->number, &dpcm->h_stream); hpi_handle_error(err); if (err) @@ -998,11 +1015,11 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream) snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, card->update_interval_frames); snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, - card->update_interval_frames * 4, UINT_MAX); + card->update_interval_frames * 2, UINT_MAX); snd_pcm_set_sync(substream); - snd_printd(KERN_INFO "playback open\n"); + VPRINTK1(KERN_INFO "playback open\n"); return 0; } @@ -1012,8 +1029,8 @@ static int snd_card_asihpi_playback_close(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct snd_card_asihpi_pcm *dpcm = runtime->private_data; - hpi_handle_error(hpi_outstream_close(ss, dpcm->h_stream)); - snd_printd(KERN_INFO "playback close\n"); + hpi_handle_error(hpi_outstream_close(dpcm->h_stream)); + VPRINTK1(KERN_INFO "playback close\n"); return 0; } @@ -1036,9 +1053,11 @@ static int snd_card_asihpi_playback_copy(struct snd_pcm_substream *substream, VPRINTK2(KERN_DEBUG "playback copy%d %u bytes\n", substream->number, len); - hpi_handle_error(hpi_outstream_write_buf(ss, dpcm->h_stream, + hpi_handle_error(hpi_outstream_write_buf(dpcm->h_stream, runtime->dma_area, len, &dpcm->format)); + dpcm->pcm_buf_host_rw_ofs = dpcm->pcm_buf_host_rw_ofs + len; + return 0; } @@ -1052,10 +1071,10 @@ static int snd_card_asihpi_playback_silence(struct snd_pcm_substream * struct snd_card_asihpi_pcm *dpcm = runtime->private_data; len = frames_to_bytes(runtime, count); - snd_printd(KERN_INFO "playback silence %u bytes\n", len); + VPRINTK1(KERN_INFO "playback silence %u bytes\n", len); memset(runtime->dma_area, 0, len); - hpi_handle_error(hpi_outstream_write_buf(ss, dpcm->h_stream, + hpi_handle_error(hpi_outstream_write_buf(dpcm->h_stream, runtime->dma_area, len, &dpcm->format)); return 0; } @@ -1091,13 +1110,13 @@ snd_card_asihpi_capture_pointer(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct snd_card_asihpi_pcm *dpcm = runtime->private_data; - VPRINTK2("capture pointer %d=%d\n", - substream->number, dpcm->pcm_buf_pos); - /* NOTE Unlike playback can't use actual dwSamplesPlayed + VPRINTK2(KERN_INFO "capture pointer %d=%d\n", + substream->number, dpcm->pcm_buf_dma_ofs); + /* NOTE Unlike playback can't use actual samples_played for the capture position, because those samples aren't yet in the local buffer available for reading. */ - return bytes_to_frames(runtime, dpcm->pcm_buf_pos % dpcm->pcm_size); + return bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes); } static int snd_card_asihpi_capture_ioctl(struct snd_pcm_substream *substream, @@ -1111,11 +1130,12 @@ static int snd_card_asihpi_capture_prepare(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct snd_card_asihpi_pcm *dpcm = runtime->private_data; - hpi_handle_error(hpi_instream_reset(ss, dpcm->h_stream)); - dpcm->pcm_irq_pos = 0; - dpcm->pcm_buf_pos = 0; + hpi_handle_error(hpi_instream_reset(dpcm->h_stream)); + dpcm->pcm_buf_host_rw_ofs = 0; + dpcm->pcm_buf_dma_ofs = 0; + dpcm->pcm_buf_elapsed_dma_ofs = 0; - snd_printd("capture prepare %d\n", substream->number); + VPRINTK1("Capture Prepare %d\n", substream->number); return 0; } @@ -1133,12 +1153,12 @@ static void snd_card_asihpi_capture_format(struct snd_card_asihpi *asihpi, /* on cards without SRC, must query at valid rate, maybe set by external sync */ - err = hpi_mixer_get_control(ss, asihpi->h_mixer, + err = hpi_mixer_get_control(asihpi->h_mixer, HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, HPI_CONTROL_SAMPLECLOCK, &h_control); if (!err) - err = hpi_sample_clock_get_sample_rate(ss, h_control, + err = hpi_sample_clock_get_sample_rate(h_control, &sample_rate); for (format = HPI_FORMAT_PCM8_UNSIGNED; @@ -1147,7 +1167,7 @@ static void snd_card_asihpi_capture_format(struct snd_card_asihpi *asihpi, err = hpi_format_create(&hpi_format, 2, format, sample_rate, 128000, 0); if (!err) - err = hpi_instream_query_format(ss, h_stream, + err = hpi_instream_query_format(h_stream, &hpi_format); if (!err) pcmhw->formats |= @@ -1178,11 +1198,11 @@ static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream) if (dpcm == NULL) return -ENOMEM; - snd_printd("hpi_instream_open adapter %d stream %d\n", + VPRINTK1("hpi_instream_open adapter %d stream %d\n", card->adapter_index, substream->number); err = hpi_handle_error( - hpi_instream_open(ss, card->adapter_index, + hpi_instream_open(card->adapter_index, substream->number, &dpcm->h_stream)); if (err) kfree(dpcm); @@ -1209,6 +1229,9 @@ static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream) snd_card_asihpi_capture.info |= SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID; + if (card->support_grouping) + snd_card_asihpi_capture.info |= SNDRV_PCM_INFO_SYNC_START; + runtime->hw = snd_card_asihpi_capture; if (card->support_mmap) @@ -1231,7 +1254,7 @@ static int snd_card_asihpi_capture_close(struct snd_pcm_substream *substream) { struct snd_card_asihpi_pcm *dpcm = substream->runtime->private_data; - hpi_handle_error(hpi_instream_close(ss, dpcm->h_stream)); + hpi_handle_error(hpi_instream_close(dpcm->h_stream)); return 0; } @@ -1241,18 +1264,17 @@ static int snd_card_asihpi_capture_copy(struct snd_pcm_substream *substream, { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_card_asihpi_pcm *dpcm = runtime->private_data; - u32 data_size; + u32 len; - data_size = frames_to_bytes(runtime, count); + len = frames_to_bytes(runtime, count); - VPRINTK2("capture copy%d %d bytes\n", substream->number, data_size); - hpi_handle_error(hpi_instream_read_buf(ss, dpcm->h_stream, - runtime->dma_area, data_size)); + VPRINTK2(KERN_INFO "capture copy%d %d bytes\n", substream->number, len); + hpi_handle_error(hpi_instream_read_buf(dpcm->h_stream, + runtime->dma_area, len)); - /* Used by capture_pointer */ - dpcm->pcm_irq_pos = dpcm->pcm_irq_pos + data_size; + dpcm->pcm_buf_host_rw_ofs = dpcm->pcm_buf_host_rw_ofs + len; - if (copy_to_user(dst, runtime->dma_area, data_size)) + if (copy_to_user(dst, runtime->dma_area, len)) return -EFAULT; return 0; @@ -1287,7 +1309,7 @@ static int __devinit snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi, struct snd_pcm *pcm; int err; - err = snd_pcm_new(asihpi->card, "asihpi PCM", device, + err = snd_pcm_new(asihpi->card, "Asihpi PCM", device, asihpi->num_outstreams, asihpi->num_instreams, &pcm); if (err < 0) @@ -1307,7 +1329,7 @@ static int __devinit snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi, pcm->private_data = asihpi; pcm->info_flags = 0; - strcpy(pcm->name, "asihpi PCM"); + strcpy(pcm->name, "Asihpi PCM"); /*? do we want to emulate MMAP for non-BBM cards? Jack doesn't work with ALSAs MMAP emulation - WHY NOT? */ @@ -1330,8 +1352,7 @@ struct hpi_control { char name[44]; /* copied to snd_ctl_elem_id.name[44]; */ }; -static char *asihpi_tuner_band_names[] = -{ +static const char * const asihpi_tuner_band_names[] = { "invalid", "AM", "FM mono", @@ -1349,70 +1370,36 @@ compile_time_assert( (HPI_TUNER_BAND_LAST+1)), assert_tuner_band_names_size); -#if ASI_STYLE_NAMES -static char *asihpi_src_names[] = -{ +static const char * const asihpi_src_names[] = { "no source", - "outstream", - "line_in", - "aes_in", - "tuner", + "PCM", + "Line", + "Digital", + "Tuner", "RF", - "clock", - "bitstr", - "mic", - "cobranet", - "analog_in", - "adapter", + "Clock", + "Bitstream", + "Microphone", + "Cobranet", + "Analog", + "Adapter", }; -#else -static char *asihpi_src_names[] = -{ - "no source", - "PCM playback", - "line in", - "digital in", - "tuner", - "RF", - "clock", - "bitstream", - "mic", - "cobranet in", - "analog in", - "adapter", -}; -#endif compile_time_assert( (ARRAY_SIZE(asihpi_src_names) == (HPI_SOURCENODE_LAST_INDEX-HPI_SOURCENODE_NONE+1)), assert_src_names_size); -#if ASI_STYLE_NAMES -static char *asihpi_dst_names[] = -{ - "no destination", - "instream", - "line_out", - "aes_out", - "RF", - "speaker" , - "cobranet", - "analog_out", -}; -#else -static char *asihpi_dst_names[] = -{ +static const char * const asihpi_dst_names[] = { "no destination", - "PCM capture", - "line out", - "digital out", + "PCM", + "Line", + "Digital", "RF", - "speaker", - "cobranet out", - "analog out" + "Speaker", + "Cobranet Out", + "Analog" }; -#endif compile_time_assert( (ARRAY_SIZE(asihpi_dst_names) == @@ -1438,30 +1425,45 @@ static void asihpi_ctl_init(struct snd_kcontrol_new *snd_control, struct hpi_control *hpi_ctl, char *name) { + char *dir = ""; memset(snd_control, 0, sizeof(*snd_control)); snd_control->name = hpi_ctl->name; snd_control->private_value = hpi_ctl->h_control; snd_control->iface = SNDRV_CTL_ELEM_IFACE_MIXER; snd_control->index = 0; + if (hpi_ctl->dst_node_type + HPI_DESTNODE_NONE == HPI_DESTNODE_ISTREAM) + dir = "Capture "; /* On or towards a PCM capture destination*/ + else if ((hpi_ctl->src_node_type + HPI_SOURCENODE_NONE != HPI_SOURCENODE_OSTREAM) && + (!hpi_ctl->dst_node_type)) + dir = "Capture "; /* On a source node that is not PCM playback */ + else if (hpi_ctl->src_node_type && + (hpi_ctl->src_node_type + HPI_SOURCENODE_NONE != HPI_SOURCENODE_OSTREAM) && + (hpi_ctl->dst_node_type)) + dir = "Monitor Playback "; /* Between an input and an output */ + else + dir = "Playback "; /* PCM Playback source, or output node */ + if (hpi_ctl->src_node_type && hpi_ctl->dst_node_type) - sprintf(hpi_ctl->name, "%s%d to %s%d %s", + sprintf(hpi_ctl->name, "%s%d %s%d %s%s", asihpi_src_names[hpi_ctl->src_node_type], hpi_ctl->src_node_index, asihpi_dst_names[hpi_ctl->dst_node_type], hpi_ctl->dst_node_index, - name); + dir, name); else if (hpi_ctl->dst_node_type) { - sprintf(hpi_ctl->name, "%s%d %s", + sprintf(hpi_ctl->name, "%s %d %s%s", asihpi_dst_names[hpi_ctl->dst_node_type], hpi_ctl->dst_node_index, - name); + dir, name); } else { - sprintf(hpi_ctl->name, "%s%d %s", + sprintf(hpi_ctl->name, "%s %d %s%s", asihpi_src_names[hpi_ctl->src_node_type], hpi_ctl->src_node_index, - name); + dir, name); } + /* printk(KERN_INFO "Adding %s %d to %d ", hpi_ctl->name, + hpi_ctl->wSrcNodeType, hpi_ctl->wDstNodeType); */ } /*------------------------------------------------------------ @@ -1478,7 +1480,7 @@ static int snd_asihpi_volume_info(struct snd_kcontrol *kcontrol, short max_gain_mB; short step_gain_mB; - err = hpi_volume_query_range(ss, h_control, + err = hpi_volume_query_range(h_control, &min_gain_mB, &max_gain_mB, &step_gain_mB); if (err) { max_gain_mB = 0; @@ -1500,7 +1502,7 @@ static int snd_asihpi_volume_get(struct snd_kcontrol *kcontrol, u32 h_control = kcontrol->private_value; short an_gain_mB[HPI_MAX_CHANNELS]; - hpi_handle_error(hpi_volume_get_gain(ss, h_control, an_gain_mB)); + hpi_handle_error(hpi_volume_get_gain(h_control, an_gain_mB)); ucontrol->value.integer.value[0] = an_gain_mB[0] / VOL_STEP_mB; ucontrol->value.integer.value[1] = an_gain_mB[1] / VOL_STEP_mB; @@ -1522,7 +1524,7 @@ static int snd_asihpi_volume_put(struct snd_kcontrol *kcontrol, asihpi->mixer_volume[addr][1] != right; */ change = 1; - hpi_handle_error(hpi_volume_set_gain(ss, h_control, an_gain_mB)); + hpi_handle_error(hpi_volume_set_gain(h_control, an_gain_mB)); return change; } @@ -1534,7 +1536,7 @@ static int __devinit snd_asihpi_volume_add(struct snd_card_asihpi *asihpi, struct snd_card *card = asihpi->card; struct snd_kcontrol_new snd_control; - asihpi_ctl_init(&snd_control, hpi_ctl, "volume"); + asihpi_ctl_init(&snd_control, hpi_ctl, "Volume"); snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ; snd_control.info = snd_asihpi_volume_info; @@ -1558,7 +1560,7 @@ static int snd_asihpi_level_info(struct snd_kcontrol *kcontrol, short step_gain_mB; err = - hpi_level_query_range(ss, h_control, &min_gain_mB, + hpi_level_query_range(h_control, &min_gain_mB, &max_gain_mB, &step_gain_mB); if (err) { max_gain_mB = 2400; @@ -1580,7 +1582,7 @@ static int snd_asihpi_level_get(struct snd_kcontrol *kcontrol, u32 h_control = kcontrol->private_value; short an_gain_mB[HPI_MAX_CHANNELS]; - hpi_handle_error(hpi_level_get_gain(ss, h_control, an_gain_mB)); + hpi_handle_error(hpi_level_get_gain(h_control, an_gain_mB)); ucontrol->value.integer.value[0] = an_gain_mB[0] / HPI_UNITS_PER_dB; ucontrol->value.integer.value[1] = @@ -1604,7 +1606,7 @@ static int snd_asihpi_level_put(struct snd_kcontrol *kcontrol, asihpi->mixer_level[addr][1] != right; */ change = 1; - hpi_handle_error(hpi_level_set_gain(ss, h_control, an_gain_mB)); + hpi_handle_error(hpi_level_set_gain(h_control, an_gain_mB)); return change; } @@ -1617,7 +1619,7 @@ static int __devinit snd_asihpi_level_add(struct snd_card_asihpi *asihpi, struct snd_kcontrol_new snd_control; /* can't use 'volume' cos some nodes have volume as well */ - asihpi_ctl_init(&snd_control, hpi_ctl, "level"); + asihpi_ctl_init(&snd_control, hpi_ctl, "Level"); snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ; snd_control.info = snd_asihpi_level_info; @@ -1633,12 +1635,8 @@ static int __devinit snd_asihpi_level_add(struct snd_card_asihpi *asihpi, ------------------------------------------------------------*/ /* AESEBU format */ -static char *asihpi_aesebu_format_names[] = -{ - "N/A", - "S/PDIF", - "AES/EBU", -}; +static const char * const asihpi_aesebu_format_names[] = { + "N/A", "S/PDIF", "AES/EBU" }; static int snd_asihpi_aesebu_format_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -1659,12 +1657,12 @@ static int snd_asihpi_aesebu_format_info(struct snd_kcontrol *kcontrol, static int snd_asihpi_aesebu_format_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol, - u16 (*func)(const struct hpi_hsubsys *, u32, u16 *)) + u16 (*func)(u32, u16 *)) { u32 h_control = kcontrol->private_value; u16 source, err; - err = func(ss, h_control, &source); + err = func(h_control, &source); /* default to N/A */ ucontrol->value.enumerated.item[0] = 0; @@ -1681,7 +1679,7 @@ static int snd_asihpi_aesebu_format_get(struct snd_kcontrol *kcontrol, static int snd_asihpi_aesebu_format_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol, - u16 (*func)(const struct hpi_hsubsys *, u32, u16)) + u16 (*func)(u32, u16)) { u32 h_control = kcontrol->private_value; @@ -1693,7 +1691,7 @@ static int snd_asihpi_aesebu_format_put(struct snd_kcontrol *kcontrol, if (ucontrol->value.enumerated.item[0] == 2) source = HPI_AESEBU_FORMAT_AESEBU; - if (func(ss, h_control, source) != 0) + if (func(h_control, source) != 0) return -EINVAL; return 1; @@ -1702,13 +1700,13 @@ static int snd_asihpi_aesebu_format_put(struct snd_kcontrol *kcontrol, static int snd_asihpi_aesebu_rx_format_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { return snd_asihpi_aesebu_format_get(kcontrol, ucontrol, - HPI_AESEBU__receiver_get_format); + hpi_aesebu_receiver_get_format); } static int snd_asihpi_aesebu_rx_format_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { return snd_asihpi_aesebu_format_put(kcontrol, ucontrol, - HPI_AESEBU__receiver_set_format); + hpi_aesebu_receiver_set_format); } static int snd_asihpi_aesebu_rxstatus_info(struct snd_kcontrol *kcontrol, @@ -1730,8 +1728,8 @@ static int snd_asihpi_aesebu_rxstatus_get(struct snd_kcontrol *kcontrol, u32 h_control = kcontrol->private_value; u16 status; - hpi_handle_error(HPI_AESEBU__receiver_get_error_status( - ss, h_control, &status)); + hpi_handle_error(hpi_aesebu_receiver_get_error_status( + h_control, &status)); ucontrol->value.integer.value[0] = status; return 0; } @@ -1742,7 +1740,7 @@ static int __devinit snd_asihpi_aesebu_rx_add(struct snd_card_asihpi *asihpi, struct snd_card *card = asihpi->card; struct snd_kcontrol_new snd_control; - asihpi_ctl_init(&snd_control, hpi_ctl, "format"); + asihpi_ctl_init(&snd_control, hpi_ctl, "Format"); snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; snd_control.info = snd_asihpi_aesebu_format_info; snd_control.get = snd_asihpi_aesebu_rx_format_get; @@ -1752,7 +1750,7 @@ static int __devinit snd_asihpi_aesebu_rx_add(struct snd_card_asihpi *asihpi, if (ctl_add(card, &snd_control, asihpi) < 0) return -EINVAL; - asihpi_ctl_init(&snd_control, hpi_ctl, "status"); + asihpi_ctl_init(&snd_control, hpi_ctl, "Status"); snd_control.access = SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ; snd_control.info = snd_asihpi_aesebu_rxstatus_info; @@ -1764,13 +1762,13 @@ static int __devinit snd_asihpi_aesebu_rx_add(struct snd_card_asihpi *asihpi, static int snd_asihpi_aesebu_tx_format_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { return snd_asihpi_aesebu_format_get(kcontrol, ucontrol, - HPI_AESEBU__transmitter_get_format); + hpi_aesebu_transmitter_get_format); } static int snd_asihpi_aesebu_tx_format_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { return snd_asihpi_aesebu_format_put(kcontrol, ucontrol, - HPI_AESEBU__transmitter_set_format); + hpi_aesebu_transmitter_set_format); } @@ -1780,7 +1778,7 @@ static int __devinit snd_asihpi_aesebu_tx_add(struct snd_card_asihpi *asihpi, struct snd_card *card = asihpi->card; struct snd_kcontrol_new snd_control; - asihpi_ctl_init(&snd_control, hpi_ctl, "format"); + asihpi_ctl_init(&snd_control, hpi_ctl, "Format"); snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; snd_control.info = snd_asihpi_aesebu_format_info; snd_control.get = snd_asihpi_aesebu_tx_format_get; @@ -1804,7 +1802,7 @@ static int snd_asihpi_tuner_gain_info(struct snd_kcontrol *kcontrol, u16 gain_range[3]; for (idx = 0; idx < 3; idx++) { - err = hpi_tuner_query_gain(ss, h_control, + err = hpi_tuner_query_gain(h_control, idx, &gain_range[idx]); if (err != 0) return err; @@ -1827,7 +1825,7 @@ static int snd_asihpi_tuner_gain_get(struct snd_kcontrol *kcontrol, u32 h_control = kcontrol->private_value; short gain; - hpi_handle_error(hpi_tuner_get_gain(ss, h_control, &gain)); + hpi_handle_error(hpi_tuner_get_gain(h_control, &gain)); ucontrol->value.integer.value[0] = gain / HPI_UNITS_PER_dB; return 0; @@ -1843,7 +1841,7 @@ static int snd_asihpi_tuner_gain_put(struct snd_kcontrol *kcontrol, short gain; gain = (ucontrol->value.integer.value[0]) * HPI_UNITS_PER_dB; - hpi_handle_error(hpi_tuner_set_gain(ss, h_control, gain)); + hpi_handle_error(hpi_tuner_set_gain(h_control, gain)); return 1; } @@ -1857,7 +1855,7 @@ static int asihpi_tuner_band_query(struct snd_kcontrol *kcontrol, u32 i; for (i = 0; i < len; i++) { - err = hpi_tuner_query_band(ss, + err = hpi_tuner_query_band( h_control, i, &band_list[i]); if (err != 0) break; @@ -1913,7 +1911,7 @@ static int snd_asihpi_tuner_band_get(struct snd_kcontrol *kcontrol, num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands, HPI_TUNER_BAND_LAST); - hpi_handle_error(hpi_tuner_get_band(ss, h_control, &band)); + hpi_handle_error(hpi_tuner_get_band(h_control, &band)); ucontrol->value.enumerated.item[0] = -1; for (idx = 0; idx < HPI_TUNER_BAND_LAST; idx++) @@ -1940,7 +1938,7 @@ static int snd_asihpi_tuner_band_put(struct snd_kcontrol *kcontrol, HPI_TUNER_BAND_LAST); band = tuner_bands[ucontrol->value.enumerated.item[0]]; - hpi_handle_error(hpi_tuner_set_band(ss, h_control, band)); + hpi_handle_error(hpi_tuner_set_band(h_control, band)); return 1; } @@ -1965,7 +1963,7 @@ static int snd_asihpi_tuner_freq_info(struct snd_kcontrol *kcontrol, for (band_iter = 0; band_iter < num_bands; band_iter++) { for (idx = 0; idx < 3; idx++) { - err = hpi_tuner_query_frequency(ss, h_control, + err = hpi_tuner_query_frequency(h_control, idx, tuner_bands[band_iter], &temp_freq_range[idx]); if (err != 0) @@ -1998,7 +1996,7 @@ static int snd_asihpi_tuner_freq_get(struct snd_kcontrol *kcontrol, u32 h_control = kcontrol->private_value; u32 freq; - hpi_handle_error(hpi_tuner_get_frequency(ss, h_control, &freq)); + hpi_handle_error(hpi_tuner_get_frequency(h_control, &freq)); ucontrol->value.integer.value[0] = freq; return 0; @@ -2011,7 +2009,7 @@ static int snd_asihpi_tuner_freq_put(struct snd_kcontrol *kcontrol, u32 freq; freq = ucontrol->value.integer.value[0]; - hpi_handle_error(hpi_tuner_set_frequency(ss, h_control, freq)); + hpi_handle_error(hpi_tuner_set_frequency(h_control, freq)); return 1; } @@ -2026,8 +2024,8 @@ static int __devinit snd_asihpi_tuner_add(struct snd_card_asihpi *asihpi, snd_control.private_value = hpi_ctl->h_control; snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; - if (!hpi_tuner_get_gain(ss, hpi_ctl->h_control, NULL)) { - asihpi_ctl_init(&snd_control, hpi_ctl, "gain"); + if (!hpi_tuner_get_gain(hpi_ctl->h_control, NULL)) { + asihpi_ctl_init(&snd_control, hpi_ctl, "Gain"); snd_control.info = snd_asihpi_tuner_gain_info; snd_control.get = snd_asihpi_tuner_gain_get; snd_control.put = snd_asihpi_tuner_gain_put; @@ -2036,7 +2034,7 @@ static int __devinit snd_asihpi_tuner_add(struct snd_card_asihpi *asihpi, return -EINVAL; } - asihpi_ctl_init(&snd_control, hpi_ctl, "band"); + asihpi_ctl_init(&snd_control, hpi_ctl, "Band"); snd_control.info = snd_asihpi_tuner_band_info; snd_control.get = snd_asihpi_tuner_band_get; snd_control.put = snd_asihpi_tuner_band_put; @@ -2044,7 +2042,7 @@ static int __devinit snd_asihpi_tuner_add(struct snd_card_asihpi *asihpi, if (ctl_add(card, &snd_control, asihpi) < 0) return -EINVAL; - asihpi_ctl_init(&snd_control, hpi_ctl, "freq"); + asihpi_ctl_init(&snd_control, hpi_ctl, "Freq"); snd_control.info = snd_asihpi_tuner_freq_info; snd_control.get = snd_asihpi_tuner_freq_get; snd_control.put = snd_asihpi_tuner_freq_put; @@ -2095,7 +2093,7 @@ static int snd_asihpi_meter_get(struct snd_kcontrol *kcontrol, short an_gain_mB[HPI_MAX_CHANNELS], i; u16 err; - err = hpi_meter_get_peak(ss, h_control, an_gain_mB); + err = hpi_meter_get_peak(h_control, an_gain_mB); for (i = 0; i < HPI_MAX_CHANNELS; i++) { if (err) { @@ -2120,7 +2118,7 @@ static int __devinit snd_asihpi_meter_add(struct snd_card_asihpi *asihpi, struct snd_card *card = asihpi->card; struct snd_kcontrol_new snd_control; - asihpi_ctl_init(&snd_control, hpi_ctl, "meter"); + asihpi_ctl_init(&snd_control, hpi_ctl, "Meter"); snd_control.access = SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ; snd_control.info = snd_asihpi_meter_info; @@ -2140,7 +2138,7 @@ static int snd_card_asihpi_mux_count_sources(struct snd_kcontrol *snd_control) struct hpi_control hpi_ctl; int s, err; for (s = 0; s < 32; s++) { - err = hpi_multiplexer_query_source(ss, h_control, s, + err = hpi_multiplexer_query_source(h_control, s, &hpi_ctl. src_node_type, &hpi_ctl. @@ -2168,7 +2166,7 @@ static int snd_asihpi_mux_info(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.items - 1; err = - hpi_multiplexer_query_source(ss, h_control, + hpi_multiplexer_query_source(h_control, uinfo->value.enumerated.item, &src_node_type, &src_node_index); @@ -2186,11 +2184,11 @@ static int snd_asihpi_mux_get(struct snd_kcontrol *kcontrol, u16 src_node_type, src_node_index; int s; - hpi_handle_error(hpi_multiplexer_get_source(ss, h_control, + hpi_handle_error(hpi_multiplexer_get_source(h_control, &source_type, &source_index)); /* Should cache this search result! */ for (s = 0; s < 256; s++) { - if (hpi_multiplexer_query_source(ss, h_control, s, + if (hpi_multiplexer_query_source(h_control, s, &src_node_type, &src_node_index)) break; @@ -2201,7 +2199,7 @@ static int snd_asihpi_mux_get(struct snd_kcontrol *kcontrol, } } snd_printd(KERN_WARNING - "control %x failed to match mux source %hu %hu\n", + "Control %x failed to match mux source %hu %hu\n", h_control, source_type, source_index); ucontrol->value.enumerated.item[0] = 0; return 0; @@ -2217,12 +2215,12 @@ static int snd_asihpi_mux_put(struct snd_kcontrol *kcontrol, change = 1; - e = hpi_multiplexer_query_source(ss, h_control, + e = hpi_multiplexer_query_source(h_control, ucontrol->value.enumerated.item[0], &source_type, &source_index); if (!e) hpi_handle_error( - hpi_multiplexer_set_source(ss, h_control, + hpi_multiplexer_set_source(h_control, source_type, source_index)); return change; } @@ -2234,11 +2232,7 @@ static int __devinit snd_asihpi_mux_add(struct snd_card_asihpi *asihpi, struct snd_card *card = asihpi->card; struct snd_kcontrol_new snd_control; -#if ASI_STYLE_NAMES - asihpi_ctl_init(&snd_control, hpi_ctl, "multiplexer"); -#else - asihpi_ctl_init(&snd_control, hpi_ctl, "route"); -#endif + asihpi_ctl_init(&snd_control, hpi_ctl, "Route"); snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; snd_control.info = snd_asihpi_mux_info; snd_control.get = snd_asihpi_mux_get; @@ -2254,33 +2248,38 @@ static int __devinit snd_asihpi_mux_add(struct snd_card_asihpi *asihpi, static int snd_asihpi_cmode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *mode_names[HPI_CHANNEL_MODE_LAST] = { - "normal", "swap", - "from_left", "from_right", - "to_left", "to_right" + static const char * const mode_names[HPI_CHANNEL_MODE_LAST + 1] = { + "invalid", + "Normal", "Swap", + "From Left", "From Right", + "To Left", "To Right" }; u32 h_control = kcontrol->private_value; u16 mode; int i; + u16 mode_map[6]; + int valid_modes = 0; /* HPI channel mode values can be from 1 to 6 Some adapters only support a contiguous subset */ for (i = 0; i < HPI_CHANNEL_MODE_LAST; i++) - if (hpi_channel_mode_query_mode( - ss, h_control, i, &mode)) - break; + if (!hpi_channel_mode_query_mode( + h_control, i, &mode)) { + mode_map[valid_modes] = mode; + valid_modes++; + } uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; - uinfo->value.enumerated.items = i; + uinfo->value.enumerated.items = valid_modes; - if (uinfo->value.enumerated.item >= i) - uinfo->value.enumerated.item = i - 1; + if (uinfo->value.enumerated.item >= valid_modes) + uinfo->value.enumerated.item = valid_modes - 1; strcpy(uinfo->value.enumerated.name, - mode_names[uinfo->value.enumerated.item]); + mode_names[mode_map[uinfo->value.enumerated.item]]); return 0; } @@ -2291,7 +2290,7 @@ static int snd_asihpi_cmode_get(struct snd_kcontrol *kcontrol, u32 h_control = kcontrol->private_value; u16 mode; - if (hpi_channel_mode_get(ss, h_control, &mode)) + if (hpi_channel_mode_get(h_control, &mode)) mode = 1; ucontrol->value.enumerated.item[0] = mode - 1; @@ -2307,7 +2306,7 @@ static int snd_asihpi_cmode_put(struct snd_kcontrol *kcontrol, change = 1; - hpi_handle_error(hpi_channel_mode_set(ss, h_control, + hpi_handle_error(hpi_channel_mode_set(h_control, ucontrol->value.enumerated.item[0] + 1)); return change; } @@ -2319,7 +2318,7 @@ static int __devinit snd_asihpi_cmode_add(struct snd_card_asihpi *asihpi, struct snd_card *card = asihpi->card; struct snd_kcontrol_new snd_control; - asihpi_ctl_init(&snd_control, hpi_ctl, "channel mode"); + asihpi_ctl_init(&snd_control, hpi_ctl, "Mode"); snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; snd_control.info = snd_asihpi_cmode_info; snd_control.get = snd_asihpi_cmode_get; @@ -2331,15 +2330,12 @@ static int __devinit snd_asihpi_cmode_add(struct snd_card_asihpi *asihpi, /*------------------------------------------------------------ Sampleclock source controls ------------------------------------------------------------*/ - -static char *sampleclock_sources[MAX_CLOCKSOURCES] = - { "N/A", "local PLL", "AES/EBU sync", "word external", "word header", - "SMPTE", "AES/EBU in1", "auto", "network", "invalid", - "prev module", - "AES/EBU in2", "AES/EBU in3", "AES/EBU in4", "AES/EBU in5", - "AES/EBU in6", "AES/EBU in7", "AES/EBU in8"}; - - +static char *sampleclock_sources[MAX_CLOCKSOURCES] = { + "N/A", "Local PLL", "Digital Sync", "Word External", "Word Header", + "SMPTE", "Digital1", "Auto", "Network", "Invalid", + "Prev Module", + "Digital2", "Digital3", "Digital4", "Digital5", + "Digital6", "Digital7", "Digital8"}; static int snd_asihpi_clksrc_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -2371,11 +2367,11 @@ static int snd_asihpi_clksrc_get(struct snd_kcontrol *kcontrol, int i; ucontrol->value.enumerated.item[0] = 0; - if (hpi_sample_clock_get_source(ss, h_control, &source)) + if (hpi_sample_clock_get_source(h_control, &source)) source = 0; if (source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT) - if (hpi_sample_clock_get_source_index(ss, h_control, &srcindex)) + if (hpi_sample_clock_get_source_index(h_control, &srcindex)) srcindex = 0; for (i = 0; i < clkcache->count; i++) @@ -2402,11 +2398,11 @@ static int snd_asihpi_clksrc_put(struct snd_kcontrol *kcontrol, if (item >= clkcache->count) item = clkcache->count-1; - hpi_handle_error(hpi_sample_clock_set_source(ss, + hpi_handle_error(hpi_sample_clock_set_source( h_control, clkcache->s[item].source)); if (clkcache->s[item].source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT) - hpi_handle_error(hpi_sample_clock_set_source_index(ss, + hpi_handle_error(hpi_sample_clock_set_source_index( h_control, clkcache->s[item].index)); return change; } @@ -2434,7 +2430,7 @@ static int snd_asihpi_clklocal_get(struct snd_kcontrol *kcontrol, u32 rate; u16 e; - e = hpi_sample_clock_get_local_rate(ss, h_control, &rate); + e = hpi_sample_clock_get_local_rate(h_control, &rate); if (!e) ucontrol->value.integer.value[0] = rate; else @@ -2452,7 +2448,7 @@ static int snd_asihpi_clklocal_put(struct snd_kcontrol *kcontrol, asihpi->mixer_clkrate[addr][1] != right; */ change = 1; - hpi_handle_error(hpi_sample_clock_set_local_rate(ss, h_control, + hpi_handle_error(hpi_sample_clock_set_local_rate(h_control, ucontrol->value.integer.value[0])); return change; } @@ -2476,7 +2472,7 @@ static int snd_asihpi_clkrate_get(struct snd_kcontrol *kcontrol, u32 rate; u16 e; - e = hpi_sample_clock_get_sample_rate(ss, h_control, &rate); + e = hpi_sample_clock_get_sample_rate(h_control, &rate); if (!e) ucontrol->value.integer.value[0] = rate; else @@ -2501,7 +2497,7 @@ static int __devinit snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi, clkcache->has_local = 0; for (i = 0; i <= HPI_SAMPLECLOCK_SOURCE_LAST; i++) { - if (hpi_sample_clock_query_source(ss, hSC, + if (hpi_sample_clock_query_source(hSC, i, &source)) break; clkcache->s[i].source = source; @@ -2515,7 +2511,7 @@ static int __devinit snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi, if (has_aes_in) /* already will have picked up index 0 above */ for (j = 1; j < 8; j++) { - if (hpi_sample_clock_query_source_index(ss, hSC, + if (hpi_sample_clock_query_source_index(hSC, j, HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT, &source)) break; @@ -2528,7 +2524,7 @@ static int __devinit snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi, } clkcache->count = i; - asihpi_ctl_init(&snd_control, hpi_ctl, "source"); + asihpi_ctl_init(&snd_control, hpi_ctl, "Source"); snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE ; snd_control.info = snd_asihpi_clksrc_info; snd_control.get = snd_asihpi_clksrc_get; @@ -2538,7 +2534,7 @@ static int __devinit snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi, if (clkcache->has_local) { - asihpi_ctl_init(&snd_control, hpi_ctl, "local_rate"); + asihpi_ctl_init(&snd_control, hpi_ctl, "Localrate"); snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE ; snd_control.info = snd_asihpi_clklocal_info; snd_control.get = snd_asihpi_clklocal_get; @@ -2549,7 +2545,7 @@ static int __devinit snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi, return -EINVAL; } - asihpi_ctl_init(&snd_control, hpi_ctl, "rate"); + asihpi_ctl_init(&snd_control, hpi_ctl, "Rate"); snd_control.access = SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ; snd_control.info = snd_asihpi_clkrate_info; @@ -2571,10 +2567,10 @@ static int __devinit snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi) if (snd_BUG_ON(!asihpi)) return -EINVAL; - strcpy(card->mixername, "asihpi mixer"); + strcpy(card->mixername, "Asihpi Mixer"); err = - hpi_mixer_open(ss, asihpi->adapter_index, + hpi_mixer_open(asihpi->adapter_index, &asihpi->h_mixer); hpi_handle_error(err); if (err) @@ -2585,7 +2581,7 @@ static int __devinit snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi) for (idx = 0; idx < 2000; idx++) { err = hpi_mixer_get_control_by_index( - ss, asihpi->h_mixer, + asihpi->h_mixer, idx, &hpi_ctl.src_node_type, &hpi_ctl.src_node_index, @@ -2597,7 +2593,7 @@ static int __devinit snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi) if (err == HPI_ERROR_CONTROL_DISABLED) { if (mixer_dump) snd_printk(KERN_INFO - "disabled HPI control(%d)\n", + "Disabled HPI Control(%d)\n", idx); continue; } else @@ -2662,7 +2658,7 @@ static int __devinit snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi) default: if (mixer_dump) snd_printk(KERN_INFO - "untranslated HPI control" + "Untranslated HPI Control" "(%d) %d %d %d %d %d\n", idx, hpi_ctl.control_type, @@ -2712,14 +2708,14 @@ snd_asihpi_proc_read(struct snd_info_entry *entry, version & 0x7, ((version >> 13) * 100) + ((version >> 7) & 0x3f)); - err = hpi_mixer_get_control(ss, asihpi->h_mixer, + err = hpi_mixer_get_control(asihpi->h_mixer, HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, HPI_CONTROL_SAMPLECLOCK, &h_control); if (!err) { - err = hpi_sample_clock_get_sample_rate(ss, + err = hpi_sample_clock_get_sample_rate( h_control, &rate); - err += hpi_sample_clock_get_source(ss, h_control, &source); + err += hpi_sample_clock_get_source(h_control, &source); if (!err) snd_iprintf(buffer, "sample_clock=%d_hz, source %s\n", @@ -2841,15 +2837,17 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev, if (err < 0) return err; snd_printk(KERN_WARNING - "**** WARNING **** adapter index %d->ALSA index %d\n", + "**** WARNING **** Adapter index %d->ALSA index %d\n", hpi_card->index, card->number); } + snd_card_set_dev(card, &pci_dev->dev); + asihpi = (struct snd_card_asihpi *) card->private_data; asihpi->card = card; - asihpi->pci = hpi_card->pci; + asihpi->pci = pci_dev; asihpi->adapter_index = hpi_card->index; - hpi_handle_error(hpi_adapter_get_info(ss, + hpi_handle_error(hpi_adapter_get_info( asihpi->adapter_index, &asihpi->num_outstreams, &asihpi->num_instreams, @@ -2859,7 +2857,7 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev, version = asihpi->version; snd_printk(KERN_INFO "adapter ID=%4X index=%d num_outstreams=%d " "num_instreams=%d S/N=%d\n" - "hw version %c%d DSP code version %03d\n", + "Hw Version %c%d DSP code version %03d\n", asihpi->type, asihpi->adapter_index, asihpi->num_outstreams, asihpi->num_instreams, asihpi->serial_number, @@ -2871,33 +2869,33 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev, if (pcm_substreams < asihpi->num_instreams) pcm_substreams = asihpi->num_instreams; - err = hpi_adapter_get_property(ss, asihpi->adapter_index, + err = hpi_adapter_get_property(asihpi->adapter_index, HPI_ADAPTER_PROPERTY_CAPS1, NULL, &asihpi->support_grouping); if (err) asihpi->support_grouping = 0; - err = hpi_adapter_get_property(ss, asihpi->adapter_index, + err = hpi_adapter_get_property(asihpi->adapter_index, HPI_ADAPTER_PROPERTY_CAPS2, &asihpi->support_mrx, NULL); if (err) asihpi->support_mrx = 0; - err = hpi_adapter_get_property(ss, asihpi->adapter_index, + err = hpi_adapter_get_property(asihpi->adapter_index, HPI_ADAPTER_PROPERTY_INTERVAL, NULL, &asihpi->update_interval_frames); if (err) asihpi->update_interval_frames = 512; - hpi_handle_error(hpi_instream_open(ss, asihpi->adapter_index, + hpi_handle_error(hpi_instream_open(asihpi->adapter_index, 0, &h_stream)); - err = hpi_instream_host_buffer_free(ss, h_stream); + err = hpi_instream_host_buffer_free(h_stream); asihpi->support_mmap = (!err); - hpi_handle_error(hpi_instream_close(ss, h_stream)); + hpi_handle_error(hpi_instream_close(h_stream)); - err = hpi_adapter_get_property(ss, asihpi->adapter_index, + err = hpi_adapter_get_property(asihpi->adapter_index, HPI_ADAPTER_PROPERTY_CURCHANNELS, &asihpi->in_max_chans, &asihpi->out_max_chans); if (err) { @@ -2923,13 +2921,13 @@ static int __devinit snd_asihpi_probe(struct pci_dev *pci_dev, goto __nodev; } - err = hpi_mixer_get_control(ss, asihpi->h_mixer, + err = hpi_mixer_get_control(asihpi->h_mixer, HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, HPI_CONTROL_SAMPLECLOCK, &h_control); if (!err) err = hpi_sample_clock_set_local_rate( - ss, h_control, adapter_fs); + h_control, adapter_fs); snd_asihpi_proc_init(asihpi); diff --git a/sound/pci/asihpi/hpi.h b/sound/pci/asihpi/hpi.h index 23399d02f666..6fc025c448de 100644 --- a/sound/pci/asihpi/hpi.h +++ b/sound/pci/asihpi/hpi.h @@ -24,17 +24,10 @@ The HPI is a low-level hardware abstraction layer to all AudioScience digital audio adapters -*/ -/* - You must define one operating system that the HPI is to be compiled under - HPI_OS_WIN32_USER 32bit Windows - HPI_OS_DSP_C6000 DSP TI C6000 (automatically set) - HPI_OS_WDM Windows WDM kernel driver - HPI_OS_LINUX Linux userspace - HPI_OS_LINUX_KERNEL Linux kernel (automatically set) (C) Copyright AudioScience Inc. 1998-2010 -******************************************************************************/ +*/ + #ifndef _HPI_H_ #define _HPI_H_ /* HPI Version @@ -50,20 +43,20 @@ i.e 3.05.02 is a development version #define HPI_VER_RELEASE(v) ((int)(v & 0xFF)) /* Use single digits for versions less that 10 to avoid octal. */ -#define HPI_VER HPI_VERSION_CONSTRUCTOR(4L, 4, 1) -#define HPI_VER_STRING "4.04.01" +#define HPI_VER HPI_VERSION_CONSTRUCTOR(4L, 6, 0) +#define HPI_VER_STRING "4.06.00" /* Library version as documented in hpi-api-versions.txt */ #define HPI_LIB_VER HPI_VERSION_CONSTRUCTOR(9, 0, 0) #include <linux/types.h> -#define HPI_EXCLUDE_DEPRECATED +#define HPI_BUILD_EXCLUDE_DEPRECATED +#define HPI_BUILD_KERNEL_MODE /******************************************************************************/ -/******************************************************************************/ /******** HPI API DEFINITIONS *****/ /******************************************************************************/ -/******************************************************************************/ + /*******************************************/ /** Audio format types \ingroup stream @@ -174,7 +167,6 @@ The range is +1.0 to -1.0, which corresponds to digital fullscale. HPI_FORMAT_UNDEFINED = 0xffff }; -/******************************************* in/out Stream states */ /*******************************************/ /** Stream States \ingroup stream @@ -194,7 +186,7 @@ enum HPI_STREAM_STATES { cards to be ready. */ HPI_STATE_WAIT = 6 }; -/******************************************* mixer source node types */ +/*******************************************/ /** Source node types \ingroup mixer */ @@ -224,7 +216,7 @@ enum HPI_SOURCENODES { /* AX6 max sourcenode types = 15 */ }; -/******************************************* mixer dest node types */ +/*******************************************/ /** Destination node types \ingroup mixer */ @@ -262,11 +254,11 @@ enum HPI_CONTROLS { HPI_CONTROL_MUTE = 4, /*mute control - not used at present. */ HPI_CONTROL_MULTIPLEXER = 5, /**< multiplexer control. */ - HPI_CONTROL_AESEBU_TRANSMITTER = 6, /**< AES/EBU transmitter control. */ - HPI_CONTROL_AESEBUTX = HPI_CONTROL_AESEBU_TRANSMITTER, + HPI_CONTROL_AESEBU_TRANSMITTER = 6, /**< AES/EBU transmitter control */ + HPI_CONTROL_AESEBUTX = 6, /* HPI_CONTROL_AESEBU_TRANSMITTER */ HPI_CONTROL_AESEBU_RECEIVER = 7, /**< AES/EBU receiver control. */ - HPI_CONTROL_AESEBURX = HPI_CONTROL_AESEBU_RECEIVER, + HPI_CONTROL_AESEBURX = 7, /* HPI_CONTROL_AESEBU_RECEIVER */ HPI_CONTROL_LEVEL = 8, /**< level/trim control - works in d_bu. */ HPI_CONTROL_TUNER = 9, /**< tuner control. */ @@ -281,7 +273,7 @@ enum HPI_CONTROLS { HPI_CONTROL_SAMPLECLOCK = 17, /**< sample clock control. */ HPI_CONTROL_MICROPHONE = 18, /**< microphone control. */ HPI_CONTROL_PARAMETRIC_EQ = 19, /**< parametric EQ control. */ - HPI_CONTROL_EQUALIZER = HPI_CONTROL_PARAMETRIC_EQ, + HPI_CONTROL_EQUALIZER = 19, /*HPI_CONTROL_PARAMETRIC_EQ */ HPI_CONTROL_COMPANDER = 20, /**< compander control. */ HPI_CONTROL_COBRANET = 21, /**< cobranet control. */ @@ -296,10 +288,7 @@ enum HPI_CONTROLS { /* WARNING types 256 or greater impact bit packing in all AX6 DSP code */ }; -/* Shorthand names that match attribute names */ - -/******************************************* ADAPTER ATTRIBUTES ****/ - +/*******************************************/ /** Adapter properties These are used in HPI_AdapterSetProperty() and HPI_AdapterGetProperty() \ingroup adapter @@ -330,12 +319,21 @@ by the driver and is not passed on to the DSP at all. Indicates the state of the adapter's SSX2 setting. This setting is stored in non-volatile memory on the adapter. A typical call sequence would be to use HPI_ADAPTER_PROPERTY_SSX2_SETTING to set SSX2 on the adapter and then to reload -the driver. The driver would query HPI_ADAPTER_PROPERTY_SSX2_SETTING during startup -and if SSX2 is set, it would then call HPI_ADAPTER_PROPERTY_ENABLE_SSX2 to enable -SSX2 stream mapping within the kernel level of the driver. +the driver. The driver would query HPI_ADAPTER_PROPERTY_SSX2_SETTING during +startup and if SSX2 is set, it would then call HPI_ADAPTER_PROPERTY_ENABLE_SSX2 +to enable SSX2 stream mapping within the kernel level of the driver. */ HPI_ADAPTER_PROPERTY_SSX2_SETTING = 4, +/** Enables/disables PCI(e) IRQ. +A setting of 0 indicates that no interrupts are being generated. A DSP boot +this property is set to 0. Setting to a non-zero value specifies the number +of frames of audio that should be processed between interrupts. This property +should be set to multiple of the mixer interval as read back from the +HPI_ADAPTER_PROPERTY_INTERVAL property. +*/ + HPI_ADAPTER_PROPERTY_IRQ_RATE = 5, + /** Base number for readonly properties */ HPI_ADAPTER_PROPERTY_READONLYBASE = 256, @@ -440,21 +438,30 @@ return value is true (1) or false (0). If the current adapter mode is MONO SSX2 is disabled, even though this property will return true. */ - HPI_ADAPTER_PROPERTY_SUPPORTS_SSX2 = 271 + HPI_ADAPTER_PROPERTY_SUPPORTS_SSX2 = 271, +/** Readonly supports PCI(e) IRQ. +Indicates that the adapter in it's current mode supports interrupts +across the host bus. Note, this does not imply that interrupts are +enabled. Instead it indicates that they can be enabled. +*/ + HPI_ADAPTER_PROPERTY_SUPPORTS_IRQ = 272 }; /** Adapter mode commands -Used in wQueryOrSet field of HPI_AdapterSetModeEx(). +Used in wQueryOrSet parameter of HPI_AdapterSetModeEx(). \ingroup adapter */ enum HPI_ADAPTER_MODE_CMDS { + /** Set the mode to the given parameter */ HPI_ADAPTER_MODE_SET = 0, + /** Return 0 or error depending whether mode is valid, + but don't set the mode */ HPI_ADAPTER_MODE_QUERY = 1 }; /** Adapter Modes - These are used by HPI_AdapterSetModeEx() + These are used by HPI_AdapterSetModeEx() \warning - more than 16 possible modes breaks a bitmask in the Windows WAVE DLL @@ -629,10 +636,13 @@ enum HPI_MIXER_STORE_COMMAND { HPI_MIXER_STORE_SAVE_SINGLE = 6 }; -/************************************* CONTROL ATTRIBUTE VALUES ****/ +/****************************/ +/* CONTROL ATTRIBUTE VALUES */ +/****************************/ + /** Used by mixer plugin enable functions -E.g. HPI_ParametricEQ_SetState() +E.g. HPI_ParametricEq_SetState() \ingroup mixer */ enum HPI_SWITCH_STATES { @@ -641,6 +651,7 @@ enum HPI_SWITCH_STATES { }; /* Volume control special gain values */ + /** volumes units are 100ths of a dB \ingroup volume */ @@ -650,6 +661,11 @@ enum HPI_SWITCH_STATES { */ #define HPI_GAIN_OFF (-100 * HPI_UNITS_PER_dB) +/** channel mask specifying all channels +\ingroup volume +*/ +#define HPI_BITMASK_ALL_CHANNELS (0xFFFFFFFF) + /** value returned for no signal \ingroup meter */ @@ -667,7 +683,7 @@ enum HPI_VOLUME_AUTOFADES { /** The physical encoding format of the AESEBU I/O. -Used in HPI_AESEBU_Transmitter_SetFormat(), HPI_AESEBU_Receiver_SetFormat() +Used in HPI_Aesebu_Transmitter_SetFormat(), HPI_Aesebu_Receiver_SetFormat() along with related Get and Query functions \ingroup aestx */ @@ -680,7 +696,7 @@ enum HPI_AESEBU_FORMATS { /** AES/EBU error status bits -Returned by HPI_AESEBU_Receiver_GetErrorStatus() +Returned by HPI_Aesebu_Receiver_GetErrorStatus() \ingroup aesrx */ enum HPI_AESEBU_ERRORS { @@ -767,14 +783,6 @@ enum HPI_TUNER_MODE_VALUES { HPI_TUNER_MODE_RDS_RBDS = 2 /**< RDS - RBDS mode */ }; -/** Tuner Level settings -\ingroup tuner -*/ -enum HPI_TUNER_LEVEL { - HPI_TUNER_LEVEL_AVERAGE = 0, - HPI_TUNER_LEVEL_RAW = 1 -}; - /** Tuner Status Bits These bitfield values are returned by a call to HPI_Tuner_GetStatus(). @@ -783,13 +791,13 @@ Multiple fields are returned from a single call. */ enum HPI_TUNER_STATUS_BITS { HPI_TUNER_VIDEO_COLOR_PRESENT = 0x0001, /**< video color is present. */ - HPI_TUNER_VIDEO_IS_60HZ = 0x0020, /**< 60 hz video detected. */ - HPI_TUNER_VIDEO_HORZ_SYNC_MISSING = 0x0040, /**< video HSYNC is missing. */ - HPI_TUNER_VIDEO_STATUS_VALID = 0x0100, /**< video status is valid. */ - HPI_TUNER_PLL_LOCKED = 0x1000, /**< the tuner's PLL is locked. */ - HPI_TUNER_FM_STEREO = 0x2000, /**< tuner reports back FM stereo. */ - HPI_TUNER_DIGITAL = 0x0200, /**< tuner reports digital programming. */ - HPI_TUNER_MULTIPROGRAM = 0x0400 /**< tuner reports multiple programs. */ + HPI_TUNER_VIDEO_IS_60HZ = 0x0020, /**< 60 hz video detected. */ + HPI_TUNER_VIDEO_HORZ_SYNC_MISSING = 0x0040, /**< video HSYNC is missing. */ + HPI_TUNER_VIDEO_STATUS_VALID = 0x0100, /**< video status is valid. */ + HPI_TUNER_DIGITAL = 0x0200, /**< tuner reports digital programming. */ + HPI_TUNER_MULTIPROGRAM = 0x0400, /**< tuner reports multiple programs. */ + HPI_TUNER_PLL_LOCKED = 0x1000, /**< the tuner's PLL is locked. */ + HPI_TUNER_FM_STEREO = 0x2000 /**< tuner reports back FM stereo. */ }; /** Channel Modes @@ -839,7 +847,7 @@ enum HPI_SAMPLECLOCK_SOURCES { HPI_SAMPLECLOCK_SOURCE_LAST = 10 }; -/** Equalizer filter types. Used by HPI_ParametricEQ_SetBand() +/** Equalizer filter types. Used by HPI_ParametricEq_SetBand() \ingroup parmeq */ enum HPI_FILTER_TYPE { @@ -882,7 +890,7 @@ enum HPI_ERROR_CODES { HPI_ERROR_INVALID_OBJ = 101, /** Function does not exist. */ HPI_ERROR_INVALID_FUNC = 102, - /** The specified object (adapter/Stream) does not exist. */ + /** The specified object does not exist. */ HPI_ERROR_INVALID_OBJ_INDEX = 103, /** Trying to access an object that has not been opened yet. */ HPI_ERROR_OBJ_NOT_OPEN = 104, @@ -890,8 +898,7 @@ enum HPI_ERROR_CODES { HPI_ERROR_OBJ_ALREADY_OPEN = 105, /** PCI, ISA resource not valid. */ HPI_ERROR_INVALID_RESOURCE = 106, - /** GetInfo call from SubSysFindAdapters failed. */ - HPI_ERROR_SUBSYSFINDADAPTERS_GETINFO = 107, + /* HPI_ERROR_SUBSYSFINDADAPTERS_GETINFO= 107 */ /** Default response was never updated with actual error code. */ HPI_ERROR_INVALID_RESPONSE = 108, /** wSize field of response was not updated, @@ -899,38 +906,44 @@ enum HPI_ERROR_CODES { HPI_ERROR_PROCESSING_MESSAGE = 109, /** The network did not respond in a timely manner. */ HPI_ERROR_NETWORK_TIMEOUT = 110, - /** An HPI handle is invalid (uninitialised?). */ + /* An HPI handle is invalid (uninitialised?). */ HPI_ERROR_INVALID_HANDLE = 111, /** A function or attribute has not been implemented yet. */ HPI_ERROR_UNIMPLEMENTED = 112, - /** There are too many clients attempting to access a network resource. */ + /** There are too many clients attempting + to access a network resource. */ HPI_ERROR_NETWORK_TOO_MANY_CLIENTS = 113, - /** Response buffer passed to HPI_Message was smaller than returned response */ + /** Response buffer passed to HPI_Message + was smaller than returned response. + wSpecificError field of hpi response contains the required size. + */ HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL = 114, /** The returned response did not match the sent message */ HPI_ERROR_RESPONSE_MISMATCH = 115, + /** A control setting that should have been cached was not. */ + HPI_ERROR_CONTROL_CACHING = 116, + /** A message buffer in the path to the adapter was smaller + than the message size. + wSpecificError field of hpi response contains the actual size. + */ + HPI_ERROR_MESSAGE_BUFFER_TOO_SMALL = 117, - /** Too many adapters.*/ - HPI_ERROR_TOO_MANY_ADAPTERS = 200, + /* HPI_ERROR_TOO_MANY_ADAPTERS= 200 */ /** Bad adpater. */ HPI_ERROR_BAD_ADAPTER = 201, /** Adapter number out of range or not set properly. */ HPI_ERROR_BAD_ADAPTER_NUMBER = 202, /** 2 adapters with the same adapter number. */ - HPI_DUPLICATE_ADAPTER_NUMBER = 203, - /** DSP code failed to bootload. */ + HPI_ERROR_DUPLICATE_ADAPTER_NUMBER = 203, + /** DSP code failed to bootload. (unused?) */ HPI_ERROR_DSP_BOOTLOAD = 204, - /** Adapter failed DSP code self test. */ - HPI_ERROR_DSP_SELFTEST = 205, /** Couldn't find or open the DSP code file. */ HPI_ERROR_DSP_FILE_NOT_FOUND = 206, /** Internal DSP hardware error. */ HPI_ERROR_DSP_HARDWARE = 207, - /** Could not allocate memory in DOS. */ - HPI_ERROR_DOS_MEMORY_ALLOC = 208, /** Could not allocate memory */ HPI_ERROR_MEMORY_ALLOC = 208, - /** Failed to correctly load/config PLD .*/ + /** Failed to correctly load/config PLD. (unused) */ HPI_ERROR_PLD_LOAD = 209, /** Unexpected end of file, block length too big etc. */ HPI_ERROR_DSP_FILE_FORMAT = 210, @@ -939,8 +952,7 @@ enum HPI_ERROR_CODES { HPI_ERROR_DSP_FILE_ACCESS_DENIED = 211, /** First DSP code section header not found in DSP file. */ HPI_ERROR_DSP_FILE_NO_HEADER = 212, - /** File read operation on DSP code file failed. */ - HPI_ERROR_DSP_FILE_READ_ERROR = 213, + /* HPI_ERROR_DSP_FILE_READ_ERROR= 213, */ /** DSP code for adapter family not found. */ HPI_ERROR_DSP_SECTION_NOT_FOUND = 214, /** Other OS specific error opening DSP file. */ @@ -950,23 +962,21 @@ enum HPI_ERROR_CODES { /** DSP code section header had size == 0. */ HPI_ERROR_DSP_FILE_NULL_HEADER = 217, - /** Base number for flash errors. */ - HPI_ERROR_FLASH = 220, + /* HPI_ERROR_FLASH = 220, */ /** Flash has bad checksum */ - HPI_ERROR_BAD_CHECKSUM = (HPI_ERROR_FLASH + 1), - HPI_ERROR_BAD_SEQUENCE = (HPI_ERROR_FLASH + 2), - HPI_ERROR_FLASH_ERASE = (HPI_ERROR_FLASH + 3), - HPI_ERROR_FLASH_PROGRAM = (HPI_ERROR_FLASH + 4), - HPI_ERROR_FLASH_VERIFY = (HPI_ERROR_FLASH + 5), - HPI_ERROR_FLASH_TYPE = (HPI_ERROR_FLASH + 6), - HPI_ERROR_FLASH_START = (HPI_ERROR_FLASH + 7), + HPI_ERROR_BAD_CHECKSUM = 221, + HPI_ERROR_BAD_SEQUENCE = 222, + HPI_ERROR_FLASH_ERASE = 223, + HPI_ERROR_FLASH_PROGRAM = 224, + HPI_ERROR_FLASH_VERIFY = 225, + HPI_ERROR_FLASH_TYPE = 226, + HPI_ERROR_FLASH_START = 227, /** Reserved for OEMs. */ HPI_ERROR_RESERVED_1 = 290, - /** Stream does not exist. */ - HPI_ERROR_INVALID_STREAM = 300, + /* HPI_ERROR_INVALID_STREAM = 300 use HPI_ERROR_INVALID_OBJ_INDEX */ /** Invalid compression format. */ HPI_ERROR_INVALID_FORMAT = 301, /** Invalid format samplerate */ @@ -977,21 +987,19 @@ enum HPI_ERROR_CODES { HPI_ERROR_INVALID_BITRATE = 304, /** Invalid datasize used for stream read/write. */ HPI_ERROR_INVALID_DATASIZE = 305, - /** Stream buffer is full during stream write. */ - HPI_ERROR_BUFFER_FULL = 306, - /** Stream buffer is empty during stream read. */ - HPI_ERROR_BUFFER_EMPTY = 307, - /** Invalid datasize used for stream read/write. */ - HPI_ERROR_INVALID_DATA_TRANSFER = 308, + /* HPI_ERROR_BUFFER_FULL = 306 use HPI_ERROR_INVALID_DATASIZE */ + /* HPI_ERROR_BUFFER_EMPTY = 307 use HPI_ERROR_INVALID_DATASIZE */ + /** Null data pointer used for stream read/write. */ + HPI_ERROR_INVALID_DATA_POINTER = 308, /** Packet ordering error for stream read/write. */ HPI_ERROR_INVALID_PACKET_ORDER = 309, /** Object can't do requested operation in its current - state, eg set format, change rec mux state while recording.*/ + state, eg set format, change rec mux state while recording.*/ HPI_ERROR_INVALID_OPERATION = 310, - /** Where an SRG is shared amongst streams, an incompatible samplerate is one - that is different to any currently playing or recording stream. */ + /** Where a SRG is shared amongst streams, an incompatible samplerate + is one that is different to any currently active stream. */ HPI_ERROR_INCOMPATIBLE_SAMPLERATE = 311, /** Adapter mode is illegal.*/ HPI_ERROR_BAD_ADAPTER_MODE = 312, @@ -1004,6 +1012,8 @@ enum HPI_ERROR_CODES { HPI_ERROR_NO_INTERADAPTER_GROUPS = 314, /** Streams on different DSPs cannot be grouped. */ HPI_ERROR_NO_INTERDSP_GROUPS = 315, + /** Stream wait cancelled before threshold reached. */ + HPI_ERROR_WAIT_CANCELLED = 316, /** Invalid mixer node for this adapter. */ HPI_ERROR_INVALID_NODE = 400, @@ -1017,6 +1027,7 @@ enum HPI_ERROR_CODES { HPI_ERROR_CONTROL_DISABLED = 404, /** I2C transaction failed due to a missing ACK. */ HPI_ERROR_CONTROL_I2C_MISSING_ACK = 405, + HPI_ERROR_I2C_MISSING_ACK = 405, /** Control is busy, or coming out of reset and cannot be accessed at this time. */ HPI_ERROR_CONTROL_NOT_READY = 407, @@ -1027,7 +1038,6 @@ enum HPI_ERROR_CODES { HPI_ERROR_NVMEM_FAIL = 452, /** I2C */ - HPI_ERROR_I2C_MISSING_ACK = HPI_ERROR_CONTROL_I2C_MISSING_ACK, HPI_ERROR_I2C_BAD_ADR = 460, /** Entity errors */ @@ -1035,6 +1045,7 @@ enum HPI_ERROR_CODES { HPI_ERROR_ENTITY_ITEM_COUNT = 471, HPI_ERROR_ENTITY_TYPE_INVALID = 472, HPI_ERROR_ENTITY_ROLE_INVALID = 473, + HPI_ERROR_ENTITY_SIZE_MISMATCH = 474, /* AES18 specific errors were 500..507 */ @@ -1044,11 +1055,18 @@ enum HPI_ERROR_CODES { /** hpioct32.c can't obtain mutex */ HPI_ERROR_MUTEX_TIMEOUT = 700, - /** errors from HPI backends have values >= this */ + /** Backend errors used to be greater than this. + \deprecated Now, all backends return only errors defined here in hpi.h + */ HPI_ERROR_BACKEND_BASE = 900, - /** indicates a cached u16 value is invalid. */ - HPI_ERROR_ILLEGAL_CACHE_VALUE = 0xffff + /** Communication with DSP failed */ + HPI_ERROR_DSP_COMMUNICATION = 900 + /* Note that the dsp communication error is set to this value so that + it remains compatible with any software that expects such errors + to be backend errors i.e. >= 900. + Do not define any new error codes with values > 900. + */ }; /** \defgroup maximums HPI maximum values @@ -1075,7 +1093,7 @@ enum HPI_ERROR_CODES { /**\}*/ -/* ////////////////////////////////////////////////////////////////////// */ +/**************/ /* STRUCTURES */ #ifndef DISABLE_PRAGMA_PACK1 #pragma pack(push, 1) @@ -1092,7 +1110,7 @@ struct hpi_format { /**< Stereo/JointStereo/Mono */ u16 mode_legacy; /**< Legacy ancillary mode or idle bit */ - u16 unused; /**< unused */ + u16 unused; /**< Unused */ u16 channels; /**< 1,2..., (or ancillary mode or idle bit */ u16 format; /**< HPI_FORMAT_PCM16, _MPEG etc. see #HPI_FORMATS. */ }; @@ -1106,930 +1124,594 @@ struct hpi_anc_frame { */ struct hpi_async_event { u16 event_type; /**< type of event. \sa async_event */ - u16 sequence; /**< sequence number, allows lost event detection */ - u32 state; /**< new state */ - u32 h_object; /**< handle to the object returning the event. */ + u16 sequence; /**< Sequence number, allows lost event detection */ + u32 state; /**< New state */ + u32 h_object; /**< handle to the object returning the event. */ union { struct { u16 index; /**< GPIO bit index. */ } gpio; struct { u16 node_index; /**< what node is the control on ? */ - u16 node_type; /**< what type of node is the control on ? */ + u16 node_type; /**< what type of node is the control on ? */ } control; } u; }; -/*/////////////////////////////////////////////////////////////////////////// */ -/* Public HPI Entity related definitions */ - -struct hpi_entity; - -enum e_entity_type { - entity_type_null, - entity_type_sequence, /* sequence of potentially heterogeneous TLV entities */ - - entity_type_reference, /* refers to a TLV entity or NULL */ - - entity_type_int, /* 32 bit */ - entity_type_float, /* ieee754 binary 32 bit encoding */ - entity_type_double, - - entity_type_cstring, - entity_type_octet, - entity_type_ip4_address, - entity_type_ip6_address, - entity_type_mac_address, - - LAST_ENTITY_TYPE -}; - -enum e_entity_role { - entity_role_null, - entity_role_value, - entity_role_classname, - - entity_role_units, - entity_role_flags, - entity_role_range, - - entity_role_mapping, - entity_role_enum, - - entity_role_instance_of, - entity_role_depends_on, - entity_role_member_of_group, - entity_role_value_constraint, - entity_role_parameter_port, - - entity_role_block, - entity_role_node_group, - entity_role_audio_port, - entity_role_clock_port, - LAST_ENTITY_ROLE -}; - /* skip host side function declarations for DSP compile and documentation extraction */ -struct hpi_hsubsys { - int not_really_used; -}; - #ifndef DISABLE_PRAGMA_PACK1 #pragma pack(pop) #endif -/*////////////////////////////////////////////////////////////////////////// */ +/*****************/ /* HPI FUNCTIONS */ +/*****************/ -/*/////////////////////////// */ -/* DATA and FORMAT and STREAM */ - +/* Stream */ u16 hpi_stream_estimate_buffer_size(struct hpi_format *pF, u32 host_polling_rate_in_milli_seconds, u32 *recommended_buffer_size); -/*/////////// */ -/* SUB SYSTEM */ -struct hpi_hsubsys *hpi_subsys_create(void - ); - -void hpi_subsys_free(const struct hpi_hsubsys *ph_subsys); - -u16 hpi_subsys_get_version(const struct hpi_hsubsys *ph_subsys, - u32 *pversion); - -u16 hpi_subsys_get_version_ex(const struct hpi_hsubsys *ph_subsys, - u32 *pversion_ex); - -u16 hpi_subsys_get_info(const struct hpi_hsubsys *ph_subsys, u32 *pversion, - u16 *pw_num_adapters, u16 aw_adapter_list[], u16 list_length); - -u16 hpi_subsys_find_adapters(const struct hpi_hsubsys *ph_subsys, - u16 *pw_num_adapters, u16 aw_adapter_list[], u16 list_length); - -u16 hpi_subsys_get_num_adapters(const struct hpi_hsubsys *ph_subsys, - int *pn_num_adapters); - -u16 hpi_subsys_get_adapter(const struct hpi_hsubsys *ph_subsys, int iterator, - u32 *padapter_index, u16 *pw_adapter_type); - -u16 hpi_subsys_ssx2_bypass(const struct hpi_hsubsys *ph_subsys, u16 bypass); +/*************/ +/* SubSystem */ +/*************/ -u16 hpi_subsys_set_host_network_interface(const struct hpi_hsubsys *ph_subsys, - const char *sz_interface); +u16 hpi_subsys_get_version_ex(u32 *pversion_ex); -/*///////// */ -/* ADAPTER */ +u16 hpi_subsys_get_num_adapters(int *pn_num_adapters); -u16 hpi_adapter_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index); +u16 hpi_subsys_get_adapter(int iterator, u32 *padapter_index, + u16 *pw_adapter_type); -u16 hpi_adapter_close(const struct hpi_hsubsys *ph_subsys, u16 adapter_index); +/***********/ +/* Adapter */ +/***********/ -u16 hpi_adapter_get_info(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u16 *pw_num_outstreams, u16 *pw_num_instreams, - u16 *pw_version, u32 *pserial_number, u16 *pw_adapter_type); +u16 hpi_adapter_open(u16 adapter_index); -u16 hpi_adapter_get_module_by_index(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u16 module_index, u16 *pw_num_outputs, - u16 *pw_num_inputs, u16 *pw_version, u32 *pserial_number, - u16 *pw_module_type, u32 *ph_module); +u16 hpi_adapter_close(u16 adapter_index); -u16 hpi_adapter_set_mode(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u32 adapter_mode); +u16 hpi_adapter_get_info(u16 adapter_index, u16 *pw_num_outstreams, + u16 *pw_num_instreams, u16 *pw_version, u32 *pserial_number, + u16 *pw_adapter_type); -u16 hpi_adapter_set_mode_ex(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u32 adapter_mode, u16 query_or_set); - -u16 hpi_adapter_get_mode(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u32 *padapter_mode); - -u16 hpi_adapter_get_assert(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u16 *assert_present, char *psz_assert, - u16 *pw_line_number); - -u16 hpi_adapter_get_assert_ex(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u16 *assert_present, char *psz_assert, - u32 *pline_number, u16 *pw_assert_on_dsp); - -u16 hpi_adapter_test_assert(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u16 assert_id); - -u16 hpi_adapter_enable_capability(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u16 capability, u32 key); - -u16 hpi_adapter_self_test(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index); - -u16 hpi_adapter_debug_read(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u32 dsp_address, char *p_bytes, int *count_bytes); - -u16 hpi_adapter_set_property(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u16 property, u16 paramter1, u16 paramter2); - -u16 hpi_adapter_get_property(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u16 property, u16 *pw_paramter1, - u16 *pw_paramter2); - -u16 hpi_adapter_enumerate_property(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u16 index, u16 what_to_enumerate, - u16 property_index, u32 *psetting); - -/*////////////// */ -/* NonVol Memory */ -u16 hpi_nv_memory_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, - u32 *ph_nv_memory, u16 *pw_size_in_bytes); - -u16 hpi_nv_memory_read_byte(const struct hpi_hsubsys *ph_subsys, - u32 h_nv_memory, u16 index, u16 *pw_data); - -u16 hpi_nv_memory_write_byte(const struct hpi_hsubsys *ph_subsys, - u32 h_nv_memory, u16 index, u16 data); - -/*////////////// */ -/* Digital I/O */ -u16 hpi_gpio_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, - u32 *ph_gpio, u16 *pw_number_input_bits, u16 *pw_number_output_bits); - -u16 hpi_gpio_read_bit(const struct hpi_hsubsys *ph_subsys, u32 h_gpio, - u16 bit_index, u16 *pw_bit_data); - -u16 hpi_gpio_read_all_bits(const struct hpi_hsubsys *ph_subsys, u32 h_gpio, - u16 aw_all_bit_data[4] - ); +u16 hpi_adapter_get_module_by_index(u16 adapter_index, u16 module_index, + u16 *pw_num_outputs, u16 *pw_num_inputs, u16 *pw_version, + u32 *pserial_number, u16 *pw_module_type, u32 *ph_module); -u16 hpi_gpio_write_bit(const struct hpi_hsubsys *ph_subsys, u32 h_gpio, - u16 bit_index, u16 bit_data); +u16 hpi_adapter_set_mode(u16 adapter_index, u32 adapter_mode); -u16 hpi_gpio_write_status(const struct hpi_hsubsys *ph_subsys, u32 h_gpio, - u16 aw_all_bit_data[4] - ); +u16 hpi_adapter_set_mode_ex(u16 adapter_index, u32 adapter_mode, + u16 query_or_set); -/**********************/ -/* Async Event Object */ -/**********************/ -u16 hpi_async_event_open(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u32 *ph_async); +u16 hpi_adapter_get_mode(u16 adapter_index, u32 *padapter_mode); -u16 hpi_async_event_close(const struct hpi_hsubsys *ph_subsys, u32 h_async); +u16 hpi_adapter_get_assert2(u16 adapter_index, u16 *p_assert_count, + char *psz_assert, u32 *p_param1, u32 *p_param2, + u32 *p_dsp_string_addr, u16 *p_processor_id); -u16 hpi_async_event_wait(const struct hpi_hsubsys *ph_subsys, u32 h_async, - u16 maximum_events, struct hpi_async_event *p_events, - u16 *pw_number_returned); +u16 hpi_adapter_test_assert(u16 adapter_index, u16 assert_id); -u16 hpi_async_event_get_count(const struct hpi_hsubsys *ph_subsys, - u32 h_async, u16 *pw_count); +u16 hpi_adapter_enable_capability(u16 adapter_index, u16 capability, u32 key); -u16 hpi_async_event_get(const struct hpi_hsubsys *ph_subsys, u32 h_async, - u16 maximum_events, struct hpi_async_event *p_events, - u16 *pw_number_returned); +u16 hpi_adapter_self_test(u16 adapter_index); -/*/////////// */ -/* WATCH-DOG */ -u16 hpi_watchdog_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, - u32 *ph_watchdog); +u16 hpi_adapter_debug_read(u16 adapter_index, u32 dsp_address, char *p_bytes, + int *count_bytes); -u16 hpi_watchdog_set_time(const struct hpi_hsubsys *ph_subsys, u32 h_watchdog, - u32 time_millisec); +u16 hpi_adapter_set_property(u16 adapter_index, u16 property, u16 paramter1, + u16 paramter2); -u16 hpi_watchdog_ping(const struct hpi_hsubsys *ph_subsys, u32 h_watchdog); +u16 hpi_adapter_get_property(u16 adapter_index, u16 property, + u16 *pw_paramter1, u16 *pw_paramter2); -/**************/ -/* OUT STREAM */ -/**************/ -u16 hpi_outstream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, - u16 outstream_index, u32 *ph_outstream); +u16 hpi_adapter_enumerate_property(u16 adapter_index, u16 index, + u16 what_to_enumerate, u16 property_index, u32 *psetting); +/*************/ +/* OutStream */ +/*************/ +u16 hpi_outstream_open(u16 adapter_index, u16 outstream_index, + u32 *ph_outstream); -u16 hpi_outstream_close(const struct hpi_hsubsys *ph_subsys, u32 h_outstream); +u16 hpi_outstream_close(u32 h_outstream); -u16 hpi_outstream_get_info_ex(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, u16 *pw_state, u32 *pbuffer_size, u32 *pdata_to_play, - u32 *psamples_played, u32 *pauxiliary_data_to_play); +u16 hpi_outstream_get_info_ex(u32 h_outstream, u16 *pw_state, + u32 *pbuffer_size, u32 *pdata_to_play, u32 *psamples_played, + u32 *pauxiliary_data_to_play); -u16 hpi_outstream_write_buf(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, const u8 *pb_write_buf, u32 bytes_to_write, - const struct hpi_format *p_format); +u16 hpi_outstream_write_buf(u32 h_outstream, const u8 *pb_write_buf, + u32 bytes_to_write, const struct hpi_format *p_format); -u16 hpi_outstream_start(const struct hpi_hsubsys *ph_subsys, u32 h_outstream); +u16 hpi_outstream_start(u32 h_outstream); -u16 hpi_outstream_wait_start(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream); +u16 hpi_outstream_wait_start(u32 h_outstream); -u16 hpi_outstream_stop(const struct hpi_hsubsys *ph_subsys, u32 h_outstream); +u16 hpi_outstream_stop(u32 h_outstream); -u16 hpi_outstream_sinegen(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream); +u16 hpi_outstream_sinegen(u32 h_outstream); -u16 hpi_outstream_reset(const struct hpi_hsubsys *ph_subsys, u32 h_outstream); +u16 hpi_outstream_reset(u32 h_outstream); -u16 hpi_outstream_query_format(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, struct hpi_format *p_format); +u16 hpi_outstream_query_format(u32 h_outstream, struct hpi_format *p_format); -u16 hpi_outstream_set_format(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, struct hpi_format *p_format); +u16 hpi_outstream_set_format(u32 h_outstream, struct hpi_format *p_format); -u16 hpi_outstream_set_punch_in_out(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, u32 punch_in_sample, u32 punch_out_sample); +u16 hpi_outstream_set_punch_in_out(u32 h_outstream, u32 punch_in_sample, + u32 punch_out_sample); -u16 hpi_outstream_set_velocity(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, short velocity); +u16 hpi_outstream_set_velocity(u32 h_outstream, short velocity); -u16 hpi_outstream_ancillary_reset(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, u16 mode); +u16 hpi_outstream_ancillary_reset(u32 h_outstream, u16 mode); -u16 hpi_outstream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, u32 *pframes_available); +u16 hpi_outstream_ancillary_get_info(u32 h_outstream, u32 *pframes_available); -u16 hpi_outstream_ancillary_read(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, struct hpi_anc_frame *p_anc_frame_buffer, +u16 hpi_outstream_ancillary_read(u32 h_outstream, + struct hpi_anc_frame *p_anc_frame_buffer, u32 anc_frame_buffer_size_in_bytes, u32 number_of_ancillary_frames_to_read); -u16 hpi_outstream_set_time_scale(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, u32 time_scaleX10000); +u16 hpi_outstream_set_time_scale(u32 h_outstream, u32 time_scaleX10000); -u16 hpi_outstream_host_buffer_allocate(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, u32 size_in_bytes); +u16 hpi_outstream_host_buffer_allocate(u32 h_outstream, u32 size_in_bytes); -u16 hpi_outstream_host_buffer_free(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream); +u16 hpi_outstream_host_buffer_free(u32 h_outstream); -u16 hpi_outstream_group_add(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, u32 h_stream); +u16 hpi_outstream_group_add(u32 h_outstream, u32 h_stream); -u16 hpi_outstream_group_get_map(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, u32 *poutstream_map, u32 *pinstream_map); +u16 hpi_outstream_group_get_map(u32 h_outstream, u32 *poutstream_map, + u32 *pinstream_map); -u16 hpi_outstream_group_reset(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream); +u16 hpi_outstream_group_reset(u32 h_outstream); -/*////////// */ -/* IN_STREAM */ -u16 hpi_instream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, - u16 instream_index, u32 *ph_instream); +/************/ +/* InStream */ +/************/ +u16 hpi_instream_open(u16 adapter_index, u16 instream_index, + u32 *ph_instream); -u16 hpi_instream_close(const struct hpi_hsubsys *ph_subsys, u32 h_instream); +u16 hpi_instream_close(u32 h_instream); -u16 hpi_instream_query_format(const struct hpi_hsubsys *ph_subsys, - u32 h_instream, const struct hpi_format *p_format); +u16 hpi_instream_query_format(u32 h_instream, + const struct hpi_format *p_format); -u16 hpi_instream_set_format(const struct hpi_hsubsys *ph_subsys, - u32 h_instream, const struct hpi_format *p_format); +u16 hpi_instream_set_format(u32 h_instream, + const struct hpi_format *p_format); -u16 hpi_instream_read_buf(const struct hpi_hsubsys *ph_subsys, u32 h_instream, - u8 *pb_read_buf, u32 bytes_to_read); +u16 hpi_instream_read_buf(u32 h_instream, u8 *pb_read_buf, u32 bytes_to_read); -u16 hpi_instream_start(const struct hpi_hsubsys *ph_subsys, u32 h_instream); +u16 hpi_instream_start(u32 h_instream); -u16 hpi_instream_wait_start(const struct hpi_hsubsys *ph_subsys, - u32 h_instream); +u16 hpi_instream_wait_start(u32 h_instream); -u16 hpi_instream_stop(const struct hpi_hsubsys *ph_subsys, u32 h_instream); +u16 hpi_instream_stop(u32 h_instream); -u16 hpi_instream_reset(const struct hpi_hsubsys *ph_subsys, u32 h_instream); +u16 hpi_instream_reset(u32 h_instream); -u16 hpi_instream_get_info_ex(const struct hpi_hsubsys *ph_subsys, - u32 h_instream, u16 *pw_state, u32 *pbuffer_size, u32 *pdata_recorded, - u32 *psamples_recorded, u32 *pauxiliary_data_recorded); +u16 hpi_instream_get_info_ex(u32 h_instream, u16 *pw_state, u32 *pbuffer_size, + u32 *pdata_recorded, u32 *psamples_recorded, + u32 *pauxiliary_data_recorded); -u16 hpi_instream_ancillary_reset(const struct hpi_hsubsys *ph_subsys, - u32 h_instream, u16 bytes_per_frame, u16 mode, u16 alignment, - u16 idle_bit); +u16 hpi_instream_ancillary_reset(u32 h_instream, u16 bytes_per_frame, + u16 mode, u16 alignment, u16 idle_bit); -u16 hpi_instream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys, - u32 h_instream, u32 *pframe_space); +u16 hpi_instream_ancillary_get_info(u32 h_instream, u32 *pframe_space); -u16 hpi_instream_ancillary_write(const struct hpi_hsubsys *ph_subsys, - u32 h_instream, const struct hpi_anc_frame *p_anc_frame_buffer, +u16 hpi_instream_ancillary_write(u32 h_instream, + const struct hpi_anc_frame *p_anc_frame_buffer, u32 anc_frame_buffer_size_in_bytes, u32 number_of_ancillary_frames_to_write); -u16 hpi_instream_host_buffer_allocate(const struct hpi_hsubsys *ph_subsys, - u32 h_instream, u32 size_in_bytes); +u16 hpi_instream_host_buffer_allocate(u32 h_instream, u32 size_in_bytes); -u16 hpi_instream_host_buffer_free(const struct hpi_hsubsys *ph_subsys, - u32 h_instream); +u16 hpi_instream_host_buffer_free(u32 h_instream); -u16 hpi_instream_group_add(const struct hpi_hsubsys *ph_subsys, - u32 h_instream, u32 h_stream); +u16 hpi_instream_group_add(u32 h_instream, u32 h_stream); -u16 hpi_instream_group_get_map(const struct hpi_hsubsys *ph_subsys, - u32 h_instream, u32 *poutstream_map, u32 *pinstream_map); +u16 hpi_instream_group_get_map(u32 h_instream, u32 *poutstream_map, + u32 *pinstream_map); -u16 hpi_instream_group_reset(const struct hpi_hsubsys *ph_subsys, - u32 h_instream); +u16 hpi_instream_group_reset(u32 h_instream); /*********/ -/* MIXER */ +/* Mixer */ /*********/ -u16 hpi_mixer_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, - u32 *ph_mixer); - -u16 hpi_mixer_close(const struct hpi_hsubsys *ph_subsys, u32 h_mixer); - -u16 hpi_mixer_get_control(const struct hpi_hsubsys *ph_subsys, u32 h_mixer, - u16 src_node_type, u16 src_node_type_index, u16 dst_node_type, - u16 dst_node_type_index, u16 control_type, u32 *ph_control); - -u16 hpi_mixer_get_control_by_index(const struct hpi_hsubsys *ph_subsys, - u32 h_mixer, u16 control_index, u16 *pw_src_node_type, - u16 *pw_src_node_index, u16 *pw_dst_node_type, u16 *pw_dst_node_index, - u16 *pw_control_type, u32 *ph_control); - -u16 hpi_mixer_store(const struct hpi_hsubsys *ph_subsys, u32 h_mixer, - enum HPI_MIXER_STORE_COMMAND command, u16 index); -/*************************/ -/* mixer CONTROLS */ -/*************************/ -/*************************/ -/* volume control */ -/*************************/ -u16 hpi_volume_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control, - short an_gain0_01dB[HPI_MAX_CHANNELS] +u16 hpi_mixer_open(u16 adapter_index, u32 *ph_mixer); + +u16 hpi_mixer_close(u32 h_mixer); + +u16 hpi_mixer_get_control(u32 h_mixer, u16 src_node_type, + u16 src_node_type_index, u16 dst_node_type, u16 dst_node_type_index, + u16 control_type, u32 *ph_control); + +u16 hpi_mixer_get_control_by_index(u32 h_mixer, u16 control_index, + u16 *pw_src_node_type, u16 *pw_src_node_index, u16 *pw_dst_node_type, + u16 *pw_dst_node_index, u16 *pw_control_type, u32 *ph_control); + +u16 hpi_mixer_store(u32 h_mixer, enum HPI_MIXER_STORE_COMMAND command, + u16 index); +/************/ +/* Controls */ +/************/ +/******************/ +/* Volume control */ +/******************/ +u16 hpi_volume_set_gain(u32 h_control, short an_gain0_01dB[HPI_MAX_CHANNELS] ); -u16 hpi_volume_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control, +u16 hpi_volume_get_gain(u32 h_control, short an_gain0_01dB_out[HPI_MAX_CHANNELS] ); +u16 hpi_volume_set_mute(u32 h_control, u32 mute); + +u16 hpi_volume_get_mute(u32 h_control, u32 *mute); + #define hpi_volume_get_range hpi_volume_query_range -u16 hpi_volume_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control, - short *min_gain_01dB, short *max_gain_01dB, short *step_gain_01dB); +u16 hpi_volume_query_range(u32 h_control, short *min_gain_01dB, + short *max_gain_01dB, short *step_gain_01dB); -u16 hpi_volume_query_channels(const struct hpi_hsubsys *ph_subsys, - const u32 h_volume, u32 *p_channels); +u16 hpi_volume_query_channels(const u32 h_volume, u32 *p_channels); -u16 hpi_volume_auto_fade(const struct hpi_hsubsys *ph_subsys, u32 h_control, +u16 hpi_volume_auto_fade(u32 h_control, short an_stop_gain0_01dB[HPI_MAX_CHANNELS], u32 duration_ms); -u16 hpi_volume_auto_fade_profile(const struct hpi_hsubsys *ph_subsys, - u32 h_control, short an_stop_gain0_01dB[HPI_MAX_CHANNELS], - u32 duration_ms, u16 profile); +u16 hpi_volume_auto_fade_profile(u32 h_control, + short an_stop_gain0_01dB[HPI_MAX_CHANNELS], u32 duration_ms, + u16 profile); -/*************************/ -/* level control */ -/*************************/ -u16 hpi_level_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control, - short *min_gain_01dB, short *max_gain_01dB, short *step_gain_01dB); +/*****************/ +/* Level control */ +/*****************/ +u16 hpi_level_query_range(u32 h_control, short *min_gain_01dB, + short *max_gain_01dB, short *step_gain_01dB); -u16 hpi_level_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control, - short an_gain0_01dB[HPI_MAX_CHANNELS] +u16 hpi_level_set_gain(u32 h_control, short an_gain0_01dB[HPI_MAX_CHANNELS] ); -u16 hpi_level_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control, +u16 hpi_level_get_gain(u32 h_control, short an_gain0_01dB_out[HPI_MAX_CHANNELS] ); -/*************************/ -/* meter control */ -/*************************/ -u16 hpi_meter_query_channels(const struct hpi_hsubsys *ph_subsys, - const u32 h_meter, u32 *p_channels); +/*****************/ +/* Meter control */ +/*****************/ +u16 hpi_meter_query_channels(const u32 h_meter, u32 *p_channels); -u16 hpi_meter_get_peak(const struct hpi_hsubsys *ph_subsys, u32 h_control, +u16 hpi_meter_get_peak(u32 h_control, short an_peak0_01dB_out[HPI_MAX_CHANNELS] ); -u16 hpi_meter_get_rms(const struct hpi_hsubsys *ph_subsys, u32 h_control, - short an_peak0_01dB_out[HPI_MAX_CHANNELS] +u16 hpi_meter_get_rms(u32 h_control, short an_peak0_01dB_out[HPI_MAX_CHANNELS] ); -u16 hpi_meter_set_peak_ballistics(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 attack, u16 decay); +u16 hpi_meter_set_peak_ballistics(u32 h_control, u16 attack, u16 decay); -u16 hpi_meter_set_rms_ballistics(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 attack, u16 decay); +u16 hpi_meter_set_rms_ballistics(u32 h_control, u16 attack, u16 decay); -u16 hpi_meter_get_peak_ballistics(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 *attack, u16 *decay); +u16 hpi_meter_get_peak_ballistics(u32 h_control, u16 *attack, u16 *decay); -u16 hpi_meter_get_rms_ballistics(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 *attack, u16 *decay); +u16 hpi_meter_get_rms_ballistics(u32 h_control, u16 *attack, u16 *decay); -/*************************/ -/* channel mode control */ -/*************************/ -u16 hpi_channel_mode_query_mode(const struct hpi_hsubsys *ph_subsys, - const u32 h_mode, const u32 index, u16 *pw_mode); +/************************/ +/* ChannelMode control */ +/************************/ +u16 hpi_channel_mode_query_mode(const u32 h_mode, const u32 index, + u16 *pw_mode); -u16 hpi_channel_mode_set(const struct hpi_hsubsys *ph_subsys, u32 h_control, - u16 mode); +u16 hpi_channel_mode_set(u32 h_control, u16 mode); -u16 hpi_channel_mode_get(const struct hpi_hsubsys *ph_subsys, u32 h_control, - u16 *mode); +u16 hpi_channel_mode_get(u32 h_control, u16 *mode); -/*************************/ -/* Tuner control */ -/*************************/ -u16 hpi_tuner_query_band(const struct hpi_hsubsys *ph_subsys, - const u32 h_tuner, const u32 index, u16 *pw_band); +/*****************/ +/* Tuner control */ +/*****************/ +u16 hpi_tuner_query_band(const u32 h_tuner, const u32 index, u16 *pw_band); -u16 hpi_tuner_set_band(const struct hpi_hsubsys *ph_subsys, u32 h_control, - u16 band); +u16 hpi_tuner_set_band(u32 h_control, u16 band); -u16 hpi_tuner_get_band(const struct hpi_hsubsys *ph_subsys, u32 h_control, - u16 *pw_band); +u16 hpi_tuner_get_band(u32 h_control, u16 *pw_band); -u16 hpi_tuner_query_frequency(const struct hpi_hsubsys *ph_subsys, - const u32 h_tuner, const u32 index, const u16 band, u32 *pfreq); +u16 hpi_tuner_query_frequency(const u32 h_tuner, const u32 index, + const u16 band, u32 *pfreq); -u16 hpi_tuner_set_frequency(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 freq_ink_hz); +u16 hpi_tuner_set_frequency(u32 h_control, u32 freq_ink_hz); -u16 hpi_tuner_get_frequency(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *pw_freq_ink_hz); +u16 hpi_tuner_get_frequency(u32 h_control, u32 *pw_freq_ink_hz); -u16 hpi_tuner_getRF_level(const struct hpi_hsubsys *ph_subsys, u32 h_control, - short *pw_level); +u16 hpi_tuner_get_rf_level(u32 h_control, short *pw_level); -u16 hpi_tuner_get_rawRF_level(const struct hpi_hsubsys *ph_subsys, - u32 h_control, short *pw_level); +u16 hpi_tuner_get_raw_rf_level(u32 h_control, short *pw_level); -u16 hpi_tuner_query_gain(const struct hpi_hsubsys *ph_subsys, - const u32 h_tuner, const u32 index, u16 *pw_gain); +u16 hpi_tuner_query_gain(const u32 h_tuner, const u32 index, u16 *pw_gain); -u16 hpi_tuner_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control, - short gain); +u16 hpi_tuner_set_gain(u32 h_control, short gain); -u16 hpi_tuner_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control, - short *pn_gain); +u16 hpi_tuner_get_gain(u32 h_control, short *pn_gain); -u16 hpi_tuner_get_status(const struct hpi_hsubsys *ph_subsys, u32 h_control, - u16 *pw_status_mask, u16 *pw_status); +u16 hpi_tuner_get_status(u32 h_control, u16 *pw_status_mask, u16 *pw_status); -u16 hpi_tuner_set_mode(const struct hpi_hsubsys *ph_subsys, u32 h_control, - u32 mode, u32 value); +u16 hpi_tuner_set_mode(u32 h_control, u32 mode, u32 value); -u16 hpi_tuner_get_mode(const struct hpi_hsubsys *ph_subsys, u32 h_control, - u32 mode, u32 *pn_value); +u16 hpi_tuner_get_mode(u32 h_control, u32 mode, u32 *pn_value); -u16 hpi_tuner_getRDS(const struct hpi_hsubsys *ph_subsys, u32 h_control, - char *p_rds_data); +u16 hpi_tuner_get_rds(u32 h_control, char *p_rds_data); -u16 hpi_tuner_query_deemphasis(const struct hpi_hsubsys *ph_subsys, - const u32 h_tuner, const u32 index, const u16 band, u32 *pdeemphasis); +u16 hpi_tuner_query_deemphasis(const u32 h_tuner, const u32 index, + const u16 band, u32 *pdeemphasis); -u16 hpi_tuner_set_deemphasis(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 deemphasis); -u16 hpi_tuner_get_deemphasis(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *pdeemphasis); +u16 hpi_tuner_set_deemphasis(u32 h_control, u32 deemphasis); +u16 hpi_tuner_get_deemphasis(u32 h_control, u32 *pdeemphasis); -u16 hpi_tuner_query_program(const struct hpi_hsubsys *ph_subsys, - const u32 h_tuner, u32 *pbitmap_program); +u16 hpi_tuner_query_program(const u32 h_tuner, u32 *pbitmap_program); -u16 hpi_tuner_set_program(const struct hpi_hsubsys *ph_subsys, u32 h_control, - u32 program); +u16 hpi_tuner_set_program(u32 h_control, u32 program); -u16 hpi_tuner_get_program(const struct hpi_hsubsys *ph_subsys, u32 h_control, - u32 *pprogram); +u16 hpi_tuner_get_program(u32 h_control, u32 *pprogram); -u16 hpi_tuner_get_hd_radio_dsp_version(const struct hpi_hsubsys *ph_subsys, - u32 h_control, char *psz_dsp_version, const u32 string_size); +u16 hpi_tuner_get_hd_radio_dsp_version(u32 h_control, char *psz_dsp_version, + const u32 string_size); -u16 hpi_tuner_get_hd_radio_sdk_version(const struct hpi_hsubsys *ph_subsys, - u32 h_control, char *psz_sdk_version, const u32 string_size); +u16 hpi_tuner_get_hd_radio_sdk_version(u32 h_control, char *psz_sdk_version, + const u32 string_size); -u16 hpi_tuner_get_hd_radio_signal_quality(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *pquality); +u16 hpi_tuner_get_hd_radio_signal_quality(u32 h_control, u32 *pquality); -u16 hpi_tuner_get_hd_radio_signal_blend(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *pblend); +u16 hpi_tuner_get_hd_radio_signal_blend(u32 h_control, u32 *pblend); -u16 hpi_tuner_set_hd_radio_signal_blend(const struct hpi_hsubsys *ph_subsys, - u32 h_control, const u32 blend); +u16 hpi_tuner_set_hd_radio_signal_blend(u32 h_control, const u32 blend); -/****************************/ -/* PADs control */ -/****************************/ +/***************/ +/* PAD control */ +/***************/ -u16 HPI_PAD__get_channel_name(const struct hpi_hsubsys *ph_subsys, - u32 h_control, char *psz_string, const u32 string_length); +u16 hpi_pad_get_channel_name(u32 h_control, char *psz_string, + const u32 string_length); -u16 HPI_PAD__get_artist(const struct hpi_hsubsys *ph_subsys, u32 h_control, - char *psz_string, const u32 string_length); +u16 hpi_pad_get_artist(u32 h_control, char *psz_string, + const u32 string_length); -u16 HPI_PAD__get_title(const struct hpi_hsubsys *ph_subsys, u32 h_control, - char *psz_string, const u32 string_length); +u16 hpi_pad_get_title(u32 h_control, char *psz_string, + const u32 string_length); -u16 HPI_PAD__get_comment(const struct hpi_hsubsys *ph_subsys, u32 h_control, - char *psz_string, const u32 string_length); +u16 hpi_pad_get_comment(u32 h_control, char *psz_string, + const u32 string_length); -u16 HPI_PAD__get_program_type(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *ppTY); +u16 hpi_pad_get_program_type(u32 h_control, u32 *ppTY); -u16 HPI_PAD__get_rdsPI(const struct hpi_hsubsys *ph_subsys, u32 h_control, - u32 *ppI); +u16 hpi_pad_get_rdsPI(u32 h_control, u32 *ppI); -u16 HPI_PAD__get_program_type_string(const struct hpi_hsubsys *ph_subsys, - u32 h_control, const u32 data_type, const u32 pTY, char *psz_string, - const u32 string_length); +u16 hpi_pad_get_program_type_string(u32 h_control, const u32 data_type, + const u32 pTY, char *psz_string, const u32 string_length); /****************************/ /* AES/EBU Receiver control */ /****************************/ -u16 HPI_AESEBU__receiver_query_format(const struct hpi_hsubsys *ph_subsys, - const u32 h_aes_rx, const u32 index, u16 *pw_format); +u16 hpi_aesebu_receiver_query_format(const u32 h_aes_rx, const u32 index, + u16 *pw_format); -u16 HPI_AESEBU__receiver_set_format(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 source); +u16 hpi_aesebu_receiver_set_format(u32 h_control, u16 source); -u16 HPI_AESEBU__receiver_get_format(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 *pw_source); +u16 hpi_aesebu_receiver_get_format(u32 h_control, u16 *pw_source); -u16 HPI_AESEBU__receiver_get_sample_rate(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *psample_rate); +u16 hpi_aesebu_receiver_get_sample_rate(u32 h_control, u32 *psample_rate); -u16 HPI_AESEBU__receiver_get_user_data(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 index, u16 *pw_data); +u16 hpi_aesebu_receiver_get_user_data(u32 h_control, u16 index, u16 *pw_data); -u16 HPI_AESEBU__receiver_get_channel_status(const struct hpi_hsubsys - *ph_subsys, u32 h_control, u16 index, u16 *pw_data); +u16 hpi_aesebu_receiver_get_channel_status(u32 h_control, u16 index, + u16 *pw_data); -u16 HPI_AESEBU__receiver_get_error_status(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 *pw_error_data); +u16 hpi_aesebu_receiver_get_error_status(u32 h_control, u16 *pw_error_data); /*******************************/ /* AES/EBU Transmitter control */ /*******************************/ -u16 HPI_AESEBU__transmitter_set_sample_rate(const struct hpi_hsubsys - *ph_subsys, u32 h_control, u32 sample_rate); +u16 hpi_aesebu_transmitter_set_sample_rate(u32 h_control, u32 sample_rate); -u16 HPI_AESEBU__transmitter_set_user_data(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 index, u16 data); +u16 hpi_aesebu_transmitter_set_user_data(u32 h_control, u16 index, u16 data); -u16 HPI_AESEBU__transmitter_set_channel_status(const struct hpi_hsubsys - *ph_subsys, u32 h_control, u16 index, u16 data); +u16 hpi_aesebu_transmitter_set_channel_status(u32 h_control, u16 index, + u16 data); -u16 HPI_AESEBU__transmitter_get_channel_status(const struct hpi_hsubsys - *ph_subsys, u32 h_control, u16 index, u16 *pw_data); +u16 hpi_aesebu_transmitter_get_channel_status(u32 h_control, u16 index, + u16 *pw_data); -u16 HPI_AESEBU__transmitter_query_format(const struct hpi_hsubsys *ph_subsys, - const u32 h_aes_tx, const u32 index, u16 *pw_format); +u16 hpi_aesebu_transmitter_query_format(const u32 h_aes_tx, const u32 index, + u16 *pw_format); -u16 HPI_AESEBU__transmitter_set_format(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 output_format); +u16 hpi_aesebu_transmitter_set_format(u32 h_control, u16 output_format); -u16 HPI_AESEBU__transmitter_get_format(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 *pw_output_format); +u16 hpi_aesebu_transmitter_get_format(u32 h_control, u16 *pw_output_format); /***********************/ -/* multiplexer control */ +/* Multiplexer control */ /***********************/ -u16 hpi_multiplexer_set_source(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 source_node_type, u16 source_node_index); - -u16 hpi_multiplexer_get_source(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 *source_node_type, u16 *source_node_index); +u16 hpi_multiplexer_set_source(u32 h_control, u16 source_node_type, + u16 source_node_index); -u16 hpi_multiplexer_query_source(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 index, u16 *source_node_type, +u16 hpi_multiplexer_get_source(u32 h_control, u16 *source_node_type, u16 *source_node_index); +u16 hpi_multiplexer_query_source(u32 h_control, u16 index, + u16 *source_node_type, u16 *source_node_index); + /***************/ -/* VOX control */ +/* Vox control */ /***************/ -u16 hpi_vox_set_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control, - short an_gain0_01dB); +u16 hpi_vox_set_threshold(u32 h_control, short an_gain0_01dB); -u16 hpi_vox_get_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control, - short *an_gain0_01dB); +u16 hpi_vox_get_threshold(u32 h_control, short *an_gain0_01dB); /*********************/ /* Bitstream control */ /*********************/ -u16 hpi_bitstream_set_clock_edge(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 edge_type); +u16 hpi_bitstream_set_clock_edge(u32 h_control, u16 edge_type); -u16 hpi_bitstream_set_data_polarity(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 polarity); +u16 hpi_bitstream_set_data_polarity(u32 h_control, u16 polarity); -u16 hpi_bitstream_get_activity(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 *pw_clk_activity, u16 *pw_data_activity); +u16 hpi_bitstream_get_activity(u32 h_control, u16 *pw_clk_activity, + u16 *pw_data_activity); /***********************/ /* SampleClock control */ /***********************/ -u16 hpi_sample_clock_query_source(const struct hpi_hsubsys *ph_subsys, - const u32 h_clock, const u32 index, u16 *pw_source); +u16 hpi_sample_clock_query_source(const u32 h_clock, const u32 index, + u16 *pw_source); -u16 hpi_sample_clock_set_source(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 source); +u16 hpi_sample_clock_set_source(u32 h_control, u16 source); -u16 hpi_sample_clock_get_source(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 *pw_source); +u16 hpi_sample_clock_get_source(u32 h_control, u16 *pw_source); -u16 hpi_sample_clock_query_source_index(const struct hpi_hsubsys *ph_subsys, - const u32 h_clock, const u32 index, const u32 source, - u16 *pw_source_index); +u16 hpi_sample_clock_query_source_index(const u32 h_clock, const u32 index, + const u32 source, u16 *pw_source_index); -u16 hpi_sample_clock_set_source_index(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 source_index); +u16 hpi_sample_clock_set_source_index(u32 h_control, u16 source_index); -u16 hpi_sample_clock_get_source_index(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 *pw_source_index); +u16 hpi_sample_clock_get_source_index(u32 h_control, u16 *pw_source_index); -u16 hpi_sample_clock_get_sample_rate(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *psample_rate); +u16 hpi_sample_clock_get_sample_rate(u32 h_control, u32 *psample_rate); -u16 hpi_sample_clock_query_local_rate(const struct hpi_hsubsys *ph_subsys, - const u32 h_clock, const u32 index, u32 *psource); +u16 hpi_sample_clock_query_local_rate(const u32 h_clock, const u32 index, + u32 *psource); -u16 hpi_sample_clock_set_local_rate(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 sample_rate); +u16 hpi_sample_clock_set_local_rate(u32 h_control, u32 sample_rate); -u16 hpi_sample_clock_get_local_rate(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *psample_rate); +u16 hpi_sample_clock_get_local_rate(u32 h_control, u32 *psample_rate); -u16 hpi_sample_clock_set_auto(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 enable); +u16 hpi_sample_clock_set_auto(u32 h_control, u32 enable); -u16 hpi_sample_clock_get_auto(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *penable); +u16 hpi_sample_clock_get_auto(u32 h_control, u32 *penable); -u16 hpi_sample_clock_set_local_rate_lock(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 lock); +u16 hpi_sample_clock_set_local_rate_lock(u32 h_control, u32 lock); -u16 hpi_sample_clock_get_local_rate_lock(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *plock); +u16 hpi_sample_clock_get_local_rate_lock(u32 h_control, u32 *plock); /***********************/ /* Microphone control */ /***********************/ -u16 hpi_microphone_set_phantom_power(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 on_off); +u16 hpi_microphone_set_phantom_power(u32 h_control, u16 on_off); -u16 hpi_microphone_get_phantom_power(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 *pw_on_off); +u16 hpi_microphone_get_phantom_power(u32 h_control, u16 *pw_on_off); -/******************************* - Parametric Equalizer control -*******************************/ -u16 hpi_parametricEQ__get_info(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 *pw_number_of_bands, u16 *pw_enabled); +/********************************/ +/* Parametric Equalizer control */ +/********************************/ +u16 hpi_parametric_eq_get_info(u32 h_control, u16 *pw_number_of_bands, + u16 *pw_enabled); -u16 hpi_parametricEQ__set_state(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 on_off); +u16 hpi_parametric_eq_set_state(u32 h_control, u16 on_off); -u16 hpi_parametricEQ__set_band(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 index, u16 type, u32 frequency_hz, short q100, - short gain0_01dB); +u16 hpi_parametric_eq_set_band(u32 h_control, u16 index, u16 type, + u32 frequency_hz, short q100, short gain0_01dB); -u16 hpi_parametricEQ__get_band(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 index, u16 *pn_type, u32 *pfrequency_hz, - short *pnQ100, short *pn_gain0_01dB); +u16 hpi_parametric_eq_get_band(u32 h_control, u16 index, u16 *pn_type, + u32 *pfrequency_hz, short *pnQ100, short *pn_gain0_01dB); -u16 hpi_parametricEQ__get_coeffs(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 index, short coeffs[5] +u16 hpi_parametric_eq_get_coeffs(u32 h_control, u16 index, short coeffs[5] ); -/******************************* - Compressor Expander control -*******************************/ - -u16 hpi_compander_set_enable(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 on); - -u16 hpi_compander_get_enable(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *pon); - -u16 hpi_compander_set_makeup_gain(const struct hpi_hsubsys *ph_subsys, - u32 h_control, short makeup_gain0_01dB); - -u16 hpi_compander_get_makeup_gain(const struct hpi_hsubsys *ph_subsys, - u32 h_control, short *pn_makeup_gain0_01dB); - -u16 hpi_compander_set_attack_time_constant(const struct hpi_hsubsys - *ph_subsys, u32 h_control, u32 index, u32 attack); - -u16 hpi_compander_get_attack_time_constant(const struct hpi_hsubsys - *ph_subsys, u32 h_control, u32 index, u32 *pw_attack); - -u16 hpi_compander_set_decay_time_constant(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 index, u32 decay); - -u16 hpi_compander_get_decay_time_constant(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 index, u32 *pw_decay); - -u16 hpi_compander_set_threshold(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 index, short threshold0_01dB); - -u16 hpi_compander_get_threshold(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 index, short *pn_threshold0_01dB); - -u16 hpi_compander_set_ratio(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 index, u32 ratio100); - -u16 hpi_compander_get_ratio(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 index, u32 *pw_ratio100); - -/******************************* - Cobranet HMI control -*******************************/ -u16 hpi_cobranet_hmi_write(const struct hpi_hsubsys *ph_subsys, u32 h_control, - u32 hmi_address, u32 byte_count, u8 *pb_data); - -u16 hpi_cobranet_hmi_read(const struct hpi_hsubsys *ph_subsys, u32 h_control, - u32 hmi_address, u32 max_byte_count, u32 *pbyte_count, u8 *pb_data); - -u16 hpi_cobranet_hmi_get_status(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *pstatus, u32 *preadable_size, - u32 *pwriteable_size); - -/*Read the current IP address -*/ -u16 hpi_cobranet_getI_paddress(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *pi_paddress); - -/* Write the current IP address -*/ -u16 hpi_cobranet_setI_paddress(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 i_paddress); - -/* Read the static IP address -*/ -u16 hpi_cobranet_get_staticI_paddress(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *pi_paddress); +/*******************************/ +/* Compressor Expander control */ +/*******************************/ -/* Write the static IP address -*/ -u16 hpi_cobranet_set_staticI_paddress(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 i_paddress); +u16 hpi_compander_set_enable(u32 h_control, u32 on); -/* Read the MAC address -*/ -u16 hpi_cobranet_getMA_caddress(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *pmAC_MS_bs, u32 *pmAC_LS_bs); +u16 hpi_compander_get_enable(u32 h_control, u32 *pon); -/******************************* - Tone Detector control -*******************************/ -u16 hpi_tone_detector_get_state(const struct hpi_hsubsys *ph_subsys, u32 hC, - u32 *state); +u16 hpi_compander_set_makeup_gain(u32 h_control, short makeup_gain0_01dB); -u16 hpi_tone_detector_set_enable(const struct hpi_hsubsys *ph_subsys, u32 hC, - u32 enable); +u16 hpi_compander_get_makeup_gain(u32 h_control, short *pn_makeup_gain0_01dB); -u16 hpi_tone_detector_get_enable(const struct hpi_hsubsys *ph_subsys, u32 hC, - u32 *enable); +u16 hpi_compander_set_attack_time_constant(u32 h_control, u32 index, + u32 attack); -u16 hpi_tone_detector_set_event_enable(const struct hpi_hsubsys *ph_subsys, - u32 hC, u32 event_enable); +u16 hpi_compander_get_attack_time_constant(u32 h_control, u32 index, + u32 *pw_attack); -u16 hpi_tone_detector_get_event_enable(const struct hpi_hsubsys *ph_subsys, - u32 hC, u32 *event_enable); +u16 hpi_compander_set_decay_time_constant(u32 h_control, u32 index, + u32 decay); -u16 hpi_tone_detector_set_threshold(const struct hpi_hsubsys *ph_subsys, - u32 hC, int threshold); +u16 hpi_compander_get_decay_time_constant(u32 h_control, u32 index, + u32 *pw_decay); -u16 hpi_tone_detector_get_threshold(const struct hpi_hsubsys *ph_subsys, - u32 hC, int *threshold); +u16 hpi_compander_set_threshold(u32 h_control, u32 index, + short threshold0_01dB); -u16 hpi_tone_detector_get_frequency(const struct hpi_hsubsys *ph_subsys, - u32 hC, u32 index, u32 *frequency); +u16 hpi_compander_get_threshold(u32 h_control, u32 index, + short *pn_threshold0_01dB); -/******************************* - Silence Detector control -*******************************/ -u16 hpi_silence_detector_get_state(const struct hpi_hsubsys *ph_subsys, - u32 hC, u32 *state); +u16 hpi_compander_set_ratio(u32 h_control, u32 index, u32 ratio100); -u16 hpi_silence_detector_set_enable(const struct hpi_hsubsys *ph_subsys, - u32 hC, u32 enable); +u16 hpi_compander_get_ratio(u32 h_control, u32 index, u32 *pw_ratio100); -u16 hpi_silence_detector_get_enable(const struct hpi_hsubsys *ph_subsys, - u32 hC, u32 *enable); +/********************/ +/* Cobranet control */ +/********************/ +u16 hpi_cobranet_hmi_write(u32 h_control, u32 hmi_address, u32 byte_count, + u8 *pb_data); -u16 hpi_silence_detector_set_event_enable(const struct hpi_hsubsys *ph_subsys, - u32 hC, u32 event_enable); +u16 hpi_cobranet_hmi_read(u32 h_control, u32 hmi_address, u32 max_byte_count, + u32 *pbyte_count, u8 *pb_data); -u16 hpi_silence_detector_get_event_enable(const struct hpi_hsubsys *ph_subsys, - u32 hC, u32 *event_enable); +u16 hpi_cobranet_hmi_get_status(u32 h_control, u32 *pstatus, + u32 *preadable_size, u32 *pwriteable_size); -u16 hpi_silence_detector_set_delay(const struct hpi_hsubsys *ph_subsys, - u32 hC, u32 delay); +u16 hpi_cobranet_get_ip_address(u32 h_control, u32 *pdw_ip_address); -u16 hpi_silence_detector_get_delay(const struct hpi_hsubsys *ph_subsys, - u32 hC, u32 *delay); +u16 hpi_cobranet_set_ip_address(u32 h_control, u32 dw_ip_address); -u16 hpi_silence_detector_set_threshold(const struct hpi_hsubsys *ph_subsys, - u32 hC, int threshold); +u16 hpi_cobranet_get_static_ip_address(u32 h_control, u32 *pdw_ip_address); -u16 hpi_silence_detector_get_threshold(const struct hpi_hsubsys *ph_subsys, - u32 hC, int *threshold); +u16 hpi_cobranet_set_static_ip_address(u32 h_control, u32 dw_ip_address); -/******************************* - Universal control -*******************************/ -u16 hpi_entity_find_next(struct hpi_entity *container_entity, - enum e_entity_type type, enum e_entity_role role, int recursive_flag, - struct hpi_entity **current_match); +u16 hpi_cobranet_get_macaddress(u32 h_control, u32 *p_mac_msbs, + u32 *p_mac_lsbs); -u16 hpi_entity_copy_value_from(struct hpi_entity *entity, - enum e_entity_type type, size_t item_count, void *value_dst_p); +/*************************/ +/* Tone Detector control */ +/*************************/ +u16 hpi_tone_detector_get_state(u32 hC, u32 *state); -u16 hpi_entity_unpack(struct hpi_entity *entity, enum e_entity_type *type, - size_t *items, enum e_entity_role *role, void **value); +u16 hpi_tone_detector_set_enable(u32 hC, u32 enable); -u16 hpi_entity_alloc_and_pack(const enum e_entity_type type, - const size_t item_count, const enum e_entity_role role, void *value, - struct hpi_entity **entity); +u16 hpi_tone_detector_get_enable(u32 hC, u32 *enable); -void hpi_entity_free(struct hpi_entity *entity); +u16 hpi_tone_detector_set_event_enable(u32 hC, u32 event_enable); -u16 hpi_universal_info(const struct hpi_hsubsys *ph_subsys, u32 hC, - struct hpi_entity **info); +u16 hpi_tone_detector_get_event_enable(u32 hC, u32 *event_enable); -u16 hpi_universal_get(const struct hpi_hsubsys *ph_subsys, u32 hC, - struct hpi_entity **value); +u16 hpi_tone_detector_set_threshold(u32 hC, int threshold); -u16 hpi_universal_set(const struct hpi_hsubsys *ph_subsys, u32 hC, - struct hpi_entity *value); +u16 hpi_tone_detector_get_threshold(u32 hC, int *threshold); -/*/////////// */ -/* DSP CLOCK */ -/*/////////// */ -u16 hpi_clock_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, - u32 *ph_dsp_clock); +u16 hpi_tone_detector_get_frequency(u32 hC, u32 index, u32 *frequency); -u16 hpi_clock_set_time(const struct hpi_hsubsys *ph_subsys, u32 h_clock, - u16 hour, u16 minute, u16 second, u16 milli_second); +/****************************/ +/* Silence Detector control */ +/****************************/ +u16 hpi_silence_detector_get_state(u32 hC, u32 *state); -u16 hpi_clock_get_time(const struct hpi_hsubsys *ph_subsys, u32 h_clock, - u16 *pw_hour, u16 *pw_minute, u16 *pw_second, u16 *pw_milli_second); +u16 hpi_silence_detector_set_enable(u32 hC, u32 enable); -/*/////////// */ -/* PROFILE */ -/*/////////// */ -u16 hpi_profile_open_all(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u16 profile_index, u32 *ph_profile, - u16 *pw_max_profiles); +u16 hpi_silence_detector_get_enable(u32 hC, u32 *enable); -u16 hpi_profile_get(const struct hpi_hsubsys *ph_subsys, u32 h_profile, - u16 index, u16 *pw_seconds, u32 *pmicro_seconds, u32 *pcall_count, - u32 *pmax_micro_seconds, u32 *pmin_micro_seconds); +u16 hpi_silence_detector_set_event_enable(u32 hC, u32 event_enable); -u16 hpi_profile_start_all(const struct hpi_hsubsys *ph_subsys, u32 h_profile); +u16 hpi_silence_detector_get_event_enable(u32 hC, u32 *event_enable); -u16 hpi_profile_stop_all(const struct hpi_hsubsys *ph_subsys, u32 h_profile); +u16 hpi_silence_detector_set_delay(u32 hC, u32 delay); -u16 hpi_profile_get_name(const struct hpi_hsubsys *ph_subsys, u32 h_profile, - u16 index, char *sz_profile_name, u16 profile_name_length); +u16 hpi_silence_detector_get_delay(u32 hC, u32 *delay); -u16 hpi_profile_get_utilization(const struct hpi_hsubsys *ph_subsys, - u32 h_profile, u32 *putilization); +u16 hpi_silence_detector_set_threshold(u32 hC, int threshold); -/*//////////////////// */ -/* UTILITY functions */ +u16 hpi_silence_detector_get_threshold(u32 hC, int *threshold); +/*********************/ +/* Utility functions */ +/*********************/ u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format, u32 sample_rate, u32 bit_rate, u32 attributes); -/* Until it's verified, this function is for Windows OSs only */ - -#endif /*_H_HPI_ */ -/* -/////////////////////////////////////////////////////////////////////////////// -// See CVS for history. Last complete set in rev 1.146 -//////////////////////////////////////////////////////////////////////////////// -*/ +#endif /*_HPI_H_ */ diff --git a/sound/pci/asihpi/hpi6000.c b/sound/pci/asihpi/hpi6000.c index 1b9bf9395cfe..3e3c2ef6efd8 100644 --- a/sound/pci/asihpi/hpi6000.c +++ b/sound/pci/asihpi/hpi6000.c @@ -43,16 +43,17 @@ #define HPI_HIF_ERROR_MASK 0x4000 /* HPI6000 specific error codes */ +#define HPI6000_ERROR_BASE 900 /* not actually used anywhere */ -#define HPI6000_ERROR_BASE 900 +/* operational/messaging errors */ #define HPI6000_ERROR_MSG_RESP_IDLE_TIMEOUT 901 -#define HPI6000_ERROR_MSG_RESP_SEND_MSG_ACK 902 + #define HPI6000_ERROR_MSG_RESP_GET_RESP_ACK 903 #define HPI6000_ERROR_MSG_GET_ADR 904 #define HPI6000_ERROR_RESP_GET_ADR 905 #define HPI6000_ERROR_MSG_RESP_BLOCKWRITE32 906 #define HPI6000_ERROR_MSG_RESP_BLOCKREAD32 907 -#define HPI6000_ERROR_MSG_INVALID_DSP_INDEX 908 + #define HPI6000_ERROR_CONTROL_CACHE_PARAMS 909 #define HPI6000_ERROR_SEND_DATA_IDLE_TIMEOUT 911 @@ -62,7 +63,6 @@ #define HPI6000_ERROR_SEND_DATA_CMD 915 #define HPI6000_ERROR_SEND_DATA_WRITE 916 #define HPI6000_ERROR_SEND_DATA_IDLECMD 917 -#define HPI6000_ERROR_SEND_DATA_VERIFY 918 #define HPI6000_ERROR_GET_DATA_IDLE_TIMEOUT 921 #define HPI6000_ERROR_GET_DATA_ACK 922 @@ -76,9 +76,8 @@ #define HPI6000_ERROR_MSG_RESP_GETRESPCMD 961 #define HPI6000_ERROR_MSG_RESP_IDLECMD 962 -#define HPI6000_ERROR_MSG_RESP_BLOCKVERIFY32 963 -/* adapter init errors */ +/* Initialisation/bootload errors */ #define HPI6000_ERROR_UNHANDLED_SUBSYS_ID 930 /* can't access PCI2040 */ @@ -210,6 +209,8 @@ static void adapter_get_asserts(struct hpi_adapter_obj *pao, static short create_adapter_obj(struct hpi_adapter_obj *pao, u32 *pos_error_code); +static void delete_adapter_obj(struct hpi_adapter_obj *pao); + /* local globals */ static u16 gw_pci_read_asserts; /* used to count PCI2040 errors */ @@ -217,17 +218,7 @@ static u16 gw_pci_write_asserts; /* used to count PCI2040 errors */ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr) { - switch (phm->function) { - case HPI_SUBSYS_OPEN: - case HPI_SUBSYS_CLOSE: - case HPI_SUBSYS_GET_INFO: - case HPI_SUBSYS_DRIVER_UNLOAD: - case HPI_SUBSYS_DRIVER_LOAD: - case HPI_SUBSYS_FIND_ADAPTERS: - /* messages that should not get here */ - phr->error = HPI_ERROR_UNIMPLEMENTED; - break; case HPI_SUBSYS_CREATE_ADAPTER: subsys_create_adapter(phm, phr); break; @@ -243,7 +234,6 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr) static void control_message(struct hpi_adapter_obj *pao, struct hpi_message *phm, struct hpi_response *phr) { - switch (phm->function) { case HPI_CONTROL_GET_STATE: if (pao->has_control_cache) { @@ -251,7 +241,13 @@ static void control_message(struct hpi_adapter_obj *pao, err = hpi6000_update_control_cache(pao, phm); if (err) { - phr->error = err; + if (err >= HPI_ERROR_BACKEND_BASE) { + phr->error = + HPI_ERROR_CONTROL_CACHING; + phr->specific_error = err; + } else { + phr->error = err; + } break; } @@ -262,16 +258,15 @@ static void control_message(struct hpi_adapter_obj *pao, } hw_message(pao, phm, phr); break; - case HPI_CONTROL_GET_INFO: - hw_message(pao, phm, phr); - break; case HPI_CONTROL_SET_STATE: hw_message(pao, phm, phr); - hpi_sync_control_cache(((struct hpi_hw_obj *)pao->priv)-> - p_cache, phm, phr); + hpi_cmn_control_cache_sync_to_msg(((struct hpi_hw_obj *)pao-> + priv)->p_cache, phm, phr); break; + + case HPI_CONTROL_GET_INFO: default: - phr->error = HPI_ERROR_INVALID_FUNC; + hw_message(pao, phm, phr); break; } } @@ -280,26 +275,12 @@ static void adapter_message(struct hpi_adapter_obj *pao, struct hpi_message *phm, struct hpi_response *phr) { switch (phm->function) { - case HPI_ADAPTER_GET_INFO: - hw_message(pao, phm, phr); - break; case HPI_ADAPTER_GET_ASSERT: adapter_get_asserts(pao, phm, phr); break; - case HPI_ADAPTER_OPEN: - case HPI_ADAPTER_CLOSE: - case HPI_ADAPTER_TEST_ASSERT: - case HPI_ADAPTER_SELFTEST: - case HPI_ADAPTER_GET_MODE: - case HPI_ADAPTER_SET_MODE: - case HPI_ADAPTER_FIND_OBJECT: - case HPI_ADAPTER_GET_PROPERTY: - case HPI_ADAPTER_SET_PROPERTY: - case HPI_ADAPTER_ENUM_PROPERTY: - hw_message(pao, phm, phr); - break; + default: - phr->error = HPI_ERROR_INVALID_FUNC; + hw_message(pao, phm, phr); break; } } @@ -311,7 +292,7 @@ static void outstream_message(struct hpi_adapter_obj *pao, case HPI_OSTREAM_HOSTBUFFER_ALLOC: case HPI_OSTREAM_HOSTBUFFER_FREE: /* Don't let these messages go to the HW function because - * they're called without allocating the spinlock. + * they're called without locking the spinlock. * For the HPI6000 adapters the HW would return * HPI_ERROR_INVALID_FUNC anyway. */ @@ -331,7 +312,7 @@ static void instream_message(struct hpi_adapter_obj *pao, case HPI_ISTREAM_HOSTBUFFER_ALLOC: case HPI_ISTREAM_HOSTBUFFER_FREE: /* Don't let these messages go to the HW function because - * they're called without allocating the spinlock. + * they're called without locking the spinlock. * For the HPI6000 adapters the HW would return * HPI_ERROR_INVALID_FUNC anyway. */ @@ -355,7 +336,7 @@ void HPI_6000(struct hpi_message *phm, struct hpi_response *phr) /* subsytem messages get executed by every HPI. */ /* All other messages are ignored unless the adapter index matches */ /* an adapter in the HPI */ - HPI_DEBUG_LOG(DEBUG, "O %d,F %x\n", phm->object, phm->function); + /*HPI_DEBUG_LOG(DEBUG, "O %d,F %x\n", phm->wObject, phm->wFunction); */ /* if Dsp has crashed then do not communicate with it any more */ if (phm->object != HPI_OBJ_SUBSYSTEM) { @@ -433,21 +414,13 @@ static void subsys_create_adapter(struct hpi_message *phm, struct hpi_adapter_obj ao; struct hpi_adapter_obj *pao; u32 os_error_code; - short error = 0; + u16 err = 0; u32 dsp_index = 0; HPI_DEBUG_LOG(VERBOSE, "subsys_create_adapter\n"); memset(&ao, 0, sizeof(ao)); - /* this HPI only creates adapters for TI/PCI2040 based devices */ - if (phm->u.s.resource.bus_type != HPI_BUS_PCI) - return; - if (phm->u.s.resource.r.pci->vendor_id != HPI_PCI_VENDOR_ID_TI) - return; - if (phm->u.s.resource.r.pci->device_id != HPI_PCI_DEV_ID_PCI2040) - return; - ao.priv = kzalloc(sizeof(struct hpi_hw_obj), GFP_KERNEL); if (!ao.priv) { HPI_DEBUG_LOG(ERROR, "cant get mem for adapter object\n"); @@ -456,16 +429,19 @@ static void subsys_create_adapter(struct hpi_message *phm, } /* create the adapter object based on the resource information */ - /*? memcpy(&ao.Pci,&phm->u.s.Resource.r.Pci,sizeof(ao.Pci)); */ ao.pci = *phm->u.s.resource.r.pci; - error = create_adapter_obj(&ao, &os_error_code); - if (!error) - error = hpi_add_adapter(&ao); - if (error) { + err = create_adapter_obj(&ao, &os_error_code); + if (err) { + delete_adapter_obj(&ao); + if (err >= HPI_ERROR_BACKEND_BASE) { + phr->error = HPI_ERROR_DSP_BOOTLOAD; + phr->specific_error = err; + } else { + phr->error = err; + } + phr->u.s.data = os_error_code; - kfree(ao.priv); - phr->error = error; return; } /* need to update paParentAdapter */ @@ -473,7 +449,7 @@ static void subsys_create_adapter(struct hpi_message *phm, if (!pao) { /* We just added this adapter, why can't we find it!? */ HPI_DEBUG_LOG(ERROR, "lost adapter after boot\n"); - phr->error = 950; + phr->error = HPI_ERROR_BAD_ADAPTER; return; } @@ -482,9 +458,8 @@ static void subsys_create_adapter(struct hpi_message *phm, phw->ado[dsp_index].pa_parent_adapter = pao; } - phr->u.s.aw_adapter_list[ao.index] = ao.adapter_type; + phr->u.s.adapter_type = ao.adapter_type; phr->u.s.adapter_index = ao.index; - phr->u.s.num_adapters++; phr->error = 0; } @@ -492,20 +467,13 @@ static void subsys_delete_adapter(struct hpi_message *phm, struct hpi_response *phr) { struct hpi_adapter_obj *pao = NULL; - struct hpi_hw_obj *phw; - pao = hpi_find_adapter(phm->adapter_index); + pao = hpi_find_adapter(phm->obj_index); if (!pao) return; - phw = (struct hpi_hw_obj *)pao->priv; - - if (pao->has_control_cache) - hpi_free_control_cache(phw->p_cache); - + delete_adapter_obj(pao); hpi_delete_adapter(pao); - kfree(phw); - phr->error = 0; } @@ -519,9 +487,6 @@ static short create_adapter_obj(struct hpi_adapter_obj *pao, u32 control_cache_count = 0; struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv; - /* init error reporting */ - pao->dsp_crashed = 0; - /* The PCI2040 has the following address map */ /* BAR0 - 4K = HPI control and status registers on PCI2040 (HPI CSR) */ /* BAR1 - 32K = HPI registers on DSP */ @@ -575,36 +540,36 @@ static short create_adapter_obj(struct hpi_adapter_obj *pao, /* get info about the adapter by asking the adapter */ /* send a HPI_ADAPTER_GET_INFO message */ { - struct hpi_message hM; - struct hpi_response hR0; /* response from DSP 0 */ - struct hpi_response hR1; /* response from DSP 1 */ + struct hpi_message hm; + struct hpi_response hr0; /* response from DSP 0 */ + struct hpi_response hr1; /* response from DSP 1 */ u16 error = 0; HPI_DEBUG_LOG(VERBOSE, "send ADAPTER_GET_INFO\n"); - memset(&hM, 0, sizeof(hM)); - hM.type = HPI_TYPE_MESSAGE; - hM.size = sizeof(struct hpi_message); - hM.object = HPI_OBJ_ADAPTER; - hM.function = HPI_ADAPTER_GET_INFO; - hM.adapter_index = 0; - memset(&hR0, 0, sizeof(hR0)); - memset(&hR1, 0, sizeof(hR1)); - hR0.size = sizeof(hR0); - hR1.size = sizeof(hR1); - - error = hpi6000_message_response_sequence(pao, 0, &hM, &hR0); - if (hR0.error) { - HPI_DEBUG_LOG(DEBUG, "message error %d\n", hR0.error); - return hR0.error; + memset(&hm, 0, sizeof(hm)); + hm.type = HPI_TYPE_MESSAGE; + hm.size = sizeof(struct hpi_message); + hm.object = HPI_OBJ_ADAPTER; + hm.function = HPI_ADAPTER_GET_INFO; + hm.adapter_index = 0; + memset(&hr0, 0, sizeof(hr0)); + memset(&hr1, 0, sizeof(hr1)); + hr0.size = sizeof(hr0); + hr1.size = sizeof(hr1); + + error = hpi6000_message_response_sequence(pao, 0, &hm, &hr0); + if (hr0.error) { + HPI_DEBUG_LOG(DEBUG, "message error %d\n", hr0.error); + return hr0.error; } if (phw->num_dsp == 2) { - error = hpi6000_message_response_sequence(pao, 1, &hM, - &hR1); + error = hpi6000_message_response_sequence(pao, 1, &hm, + &hr1); if (error) return error; } - pao->adapter_type = hR0.u.a.adapter_type; - pao->index = hR0.u.a.adapter_index; + pao->adapter_type = hr0.u.ax.info.adapter_type; + pao->index = hr0.u.ax.info.adapter_index; } memset(&phw->control_cache[0], 0, @@ -618,22 +583,37 @@ static short create_adapter_obj(struct hpi_adapter_obj *pao, control_cache_count = hpi_read_word(&phw->ado[0], HPI_HIF_ADDR(control_cache_count)); - pao->has_control_cache = 1; phw->p_cache = hpi_alloc_control_cache(control_cache_count, - control_cache_size, (struct hpi_control_cache_info *) + control_cache_size, (unsigned char *) &phw->control_cache[0] ); - if (!phw->p_cache) - pao->has_control_cache = 0; - } else - pao->has_control_cache = 0; + if (phw->p_cache) + pao->has_control_cache = 1; + } HPI_DEBUG_LOG(DEBUG, "get adapter info ASI%04X index %d\n", pao->adapter_type, pao->index); pao->open = 0; /* upon creation the adapter is closed */ - return 0; + + if (phw->p_cache) + phw->p_cache->adap_idx = pao->index; + + return hpi_add_adapter(pao); +} + +static void delete_adapter_obj(struct hpi_adapter_obj *pao) +{ + struct hpi_hw_obj *phw = (struct hpi_hw_obj *)pao->priv; + + if (pao->has_control_cache) + hpi_free_control_cache(phw->p_cache); + + /* reset DSPs on adapter */ + iowrite32(0x0003000F, phw->dw2040_HPICSR + HPI_RESET); + + kfree(phw); } /************************************************************************/ @@ -645,11 +625,13 @@ static void adapter_get_asserts(struct hpi_adapter_obj *pao, #ifndef HIDE_PCI_ASSERTS /* if we have PCI2040 asserts then collect them */ if ((gw_pci_read_asserts > 0) || (gw_pci_write_asserts > 0)) { - phr->u.a.serial_number = + phr->u.ax.assert.p1 = gw_pci_read_asserts * 100 + gw_pci_write_asserts; - phr->u.a.adapter_index = 1; /* assert count */ - phr->u.a.adapter_type = -1; /* "dsp index" */ - strcpy(phr->u.a.sz_adapter_assert, "PCI2040 error"); + phr->u.ax.assert.p2 = 0; + phr->u.ax.assert.count = 1; /* assert count */ + phr->u.ax.assert.dsp_index = -1; /* "dsp index" */ + strcpy(phr->u.ax.assert.sz_message, "PCI2040 error"); + phr->u.ax.assert.dsp_msg_addr = 0; gw_pci_read_asserts = 0; gw_pci_write_asserts = 0; phr->error = 0; @@ -686,10 +668,10 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao, /* NOTE don't use wAdapterType in this routine. It is not setup yet */ - switch (pao->pci.subsys_device_id) { + switch (pao->pci.pci_dev->subsystem_device) { case 0x5100: case 0x5110: /* ASI5100 revB or higher with C6711D */ - case 0x5200: /* ASI5200 PC_ie version of ASI5100 */ + case 0x5200: /* ASI5200 PCIe version of ASI5100 */ case 0x6100: case 0x6200: boot_load_family = HPI_ADAPTER_FAMILY_ASI(0x6200); @@ -709,8 +691,9 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao, * note that bits 4..15 are read-only and so should always return zero, * even though we wrote 1 to them */ - for (i = 0; i < 1000; i++) - delay = ioread32(phw->dw2040_HPICSR + HPI_RESET); + hpios_delay_micro_seconds(1000); + delay = ioread32(phw->dw2040_HPICSR + HPI_RESET); + if (delay != dw2040_reset) { HPI_DEBUG_LOG(ERROR, "INIT_PCI2040 %x %x\n", dw2040_reset, delay); @@ -743,8 +726,7 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao, dw2040_reset = dw2040_reset & (~0x00000008); iowrite32(dw2040_reset, phw->dw2040_HPICSR + HPI_RESET); /*delay to allow DSP to get going */ - for (i = 0; i < 100; i++) - delay = ioread32(phw->dw2040_HPICSR + HPI_RESET); + hpios_delay_micro_seconds(100); /* loop through all DSPs, downloading DSP code */ for (dsp_index = 0; dsp_index < phw->num_dsp; dsp_index++) { @@ -783,27 +765,27 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao, */ /* bypass PLL */ hpi_write_word(pdo, 0x01B7C100, 0x0000); - for (i = 0; i < 100; i++) - delay = ioread32(phw->dw2040_HPICSR + - HPI_RESET); + hpios_delay_micro_seconds(100); /* ** use default of PLL x7 ** */ /* EMIF = 225/3=75MHz */ hpi_write_word(pdo, 0x01B7C120, 0x8002); + hpios_delay_micro_seconds(100); + /* peri = 225/2 */ hpi_write_word(pdo, 0x01B7C11C, 0x8001); + hpios_delay_micro_seconds(100); + /* cpu = 225/1 */ hpi_write_word(pdo, 0x01B7C118, 0x8000); - /* ~200us delay */ - for (i = 0; i < 2000; i++) - delay = ioread32(phw->dw2040_HPICSR + - HPI_RESET); + + /* ~2ms delay */ + hpios_delay_micro_seconds(2000); + /* PLL not bypassed */ hpi_write_word(pdo, 0x01B7C100, 0x0001); - /* ~200us delay */ - for (i = 0; i < 2000; i++) - delay = ioread32(phw->dw2040_HPICSR + - HPI_RESET); + /* ~2ms delay */ + hpios_delay_micro_seconds(2000); } /* test r/w to internal DSP memory @@ -927,9 +909,7 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao, } /* delay a little to allow SDRAM and DSP to "get going" */ - - for (i = 0; i < 1000; i++) - delay = ioread32(phw->dw2040_HPICSR + HPI_RESET); + hpios_delay_micro_seconds(1000); /* test access to SDRAM */ { @@ -976,7 +956,7 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao, /* write the DSP code down into the DSPs memory */ /*HpiDspCode_Open(nBootLoadFamily,&DspCode,pdwOsErrorCode); */ - dsp_code.ps_dev = pao->pci.p_os_data; + dsp_code.ps_dev = pao->pci.pci_dev; error = hpi_dsp_code_open(boot_load_family, &dsp_code, pos_error_code); @@ -1073,8 +1053,7 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao, /* step 3. Start code by sending interrupt */ iowrite32(0x00030003, pdo->prHPI_control); - for (i = 0; i < 10000; i++) - delay = ioread32(phw->dw2040_HPICSR + HPI_RESET); + hpios_delay_micro_seconds(10000); /* wait for a non-zero value in hostcmd - * indicating initialization is complete @@ -1101,7 +1080,7 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao, * locks up with a bluescreen (NOT GPF or pagefault). */ else - hpios_delay_micro_seconds(1000); + hpios_delay_micro_seconds(10000); } if (timeout == 0) return HPI6000_ERROR_INIT_NOACK; @@ -1132,14 +1111,14 @@ static short hpi6000_adapter_boot_load_dsp(struct hpi_adapter_obj *pao, mask = 0xFFFFFF00L; /* ASI5100 uses AX6 code, */ /* but has no PLD r/w register to test */ - if (HPI_ADAPTER_FAMILY_ASI(pao->pci. - subsys_device_id) == + if (HPI_ADAPTER_FAMILY_ASI(pao->pci.pci_dev-> + subsystem_device) == HPI_ADAPTER_FAMILY_ASI(0x5100)) mask = 0x00000000L; /* ASI5200 uses AX6 code, */ /* but has no PLD r/w register to test */ - if (HPI_ADAPTER_FAMILY_ASI(pao->pci. - subsys_device_id) == + if (HPI_ADAPTER_FAMILY_ASI(pao->pci.pci_dev-> + subsystem_device) == HPI_ADAPTER_FAMILY_ASI(0x5200)) mask = 0x00000000L; break; @@ -1204,7 +1183,7 @@ static u32 hpi_read_word(struct dsp_obj *pdo, u32 address) u32 data = 0; if (hpi_set_address(pdo, address)) - return 0; /*? no way to return error */ + return 0; /*? No way to return error */ /* take care of errata in revB DSP (2.0.1) */ data = ioread32(pdo->prHPI_data); @@ -1340,10 +1319,6 @@ static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao, u32 *p_data; u16 error = 0; - /* does the DSP we are referencing exist? */ - if (dsp_index >= phw->num_dsp) - return HPI6000_ERROR_MSG_INVALID_DSP_INDEX; - ack = hpi6000_wait_dsp_ack(pao, dsp_index, HPI_HIF_IDLE); if (ack & HPI_HIF_ERROR_MASK) { pao->dsp_crashed++; @@ -1351,9 +1326,7 @@ static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao, } pao->dsp_crashed = 0; - /* send the message */ - - /* get the address and size */ + /* get the message address and size */ if (phw->message_buffer_address_on_dsp == 0) { timeout = TIMEOUT; do { @@ -1368,10 +1341,9 @@ static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao, } else address = phw->message_buffer_address_on_dsp; - /* dwLength = sizeof(struct hpi_message); */ length = phm->size; - /* send it */ + /* send the message */ p_data = (u32 *)phm; if (hpi6000_dsp_block_write32(pao, dsp_index, address, p_data, (u16)length / 4)) @@ -1385,7 +1357,7 @@ static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao, if (ack & HPI_HIF_ERROR_MASK) return HPI6000_ERROR_MSG_RESP_GET_RESP_ACK; - /* get the address and size */ + /* get the response address */ if (phw->response_buffer_address_on_dsp == 0) { timeout = TIMEOUT; do { @@ -1409,7 +1381,7 @@ static short hpi6000_message_response_sequence(struct hpi_adapter_obj *pao, if (!timeout) length = sizeof(struct hpi_response); - /* get it */ + /* get the response */ p_data = (u32 *)phr; if (hpi6000_dsp_block_read32(pao, dsp_index, address, p_data, (u16)length / 4)) @@ -1805,17 +1777,11 @@ static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm, hpios_dsplock_lock(pao); error = hpi6000_message_response_sequence(pao, dsp_index, phm, phr); - /* maybe an error response */ - if (error) { - /* something failed in the HPI/DSP interface */ - phr->error = error; - /* just the header of the response is valid */ - phr->size = sizeof(struct hpi_response_header); + if (error) /* something failed in the HPI/DSP interface */ goto err; - } - if (phr->error != 0) /* something failed in the DSP */ - goto err; + if (phr->error) /* something failed in the DSP */ + goto out; switch (phm->function) { case HPI_OSTREAM_WRITE: @@ -1827,21 +1793,30 @@ static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm, error = hpi6000_get_data(pao, dsp_index, phm, phr); break; case HPI_ADAPTER_GET_ASSERT: - phr->u.a.adapter_index = 0; /* dsp 0 default */ + phr->u.ax.assert.dsp_index = 0; /* dsp 0 default */ if (num_dsp == 2) { - if (!phr->u.a.adapter_type) { + if (!phr->u.ax.assert.count) { /* no assert from dsp 0, check dsp 1 */ error = hpi6000_message_response_sequence(pao, 1, phm, phr); - phr->u.a.adapter_index = 1; + phr->u.ax.assert.dsp_index = 1; } } } - if (error) - phr->error = error; - err: + if (error) { + if (error >= HPI_ERROR_BACKEND_BASE) { + phr->error = HPI_ERROR_DSP_COMMUNICATION; + phr->specific_error = error; + } else { + phr->error = error; + } + + /* just the header of the response is valid */ + phr->size = sizeof(struct hpi_response_header); + } +out: hpios_dsplock_unlock(pao); return; } diff --git a/sound/pci/asihpi/hpi6205.c b/sound/pci/asihpi/hpi6205.c index 2672f6591ceb..620525bdac59 100644 --- a/sound/pci/asihpi/hpi6205.c +++ b/sound/pci/asihpi/hpi6205.c @@ -38,27 +38,26 @@ /*****************************************************************************/ /* HPI6205 specific error codes */ -#define HPI6205_ERROR_BASE 1000 -/*#define HPI6205_ERROR_MEM_ALLOC 1001 */ +#define HPI6205_ERROR_BASE 1000 /* not actually used anywhere */ + +/* operational/messaging errors */ +#define HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT 1015 +#define HPI6205_ERROR_MSG_RESP_TIMEOUT 1016 + +/* initialization/bootload errors */ #define HPI6205_ERROR_6205_NO_IRQ 1002 #define HPI6205_ERROR_6205_INIT_FAILED 1003 -/*#define HPI6205_ERROR_MISSING_DSPCODE 1004 */ -#define HPI6205_ERROR_UNKNOWN_PCI_DEVICE 1005 #define HPI6205_ERROR_6205_REG 1006 #define HPI6205_ERROR_6205_DSPPAGE 1007 -#define HPI6205_ERROR_BAD_DSPINDEX 1008 #define HPI6205_ERROR_C6713_HPIC 1009 #define HPI6205_ERROR_C6713_HPIA 1010 #define HPI6205_ERROR_C6713_PLL 1011 #define HPI6205_ERROR_DSP_INTMEM 1012 #define HPI6205_ERROR_DSP_EXTMEM 1013 #define HPI6205_ERROR_DSP_PLD 1014 -#define HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT 1015 -#define HPI6205_ERROR_MSG_RESP_TIMEOUT 1016 #define HPI6205_ERROR_6205_EEPROM 1017 #define HPI6205_ERROR_DSP_EMIF 1018 -#define hpi6205_error(dsp_index, err) (err) /*****************************************************************************/ /* for C6205 PCI i/f */ /* Host Status Register (HSR) bitfields */ @@ -128,9 +127,6 @@ struct hpi_hw_obj { u32 outstream_host_buffer_size[HPI_MAX_STREAMS]; struct consistent_dma_area h_control_cache; - struct consistent_dma_area h_async_event_buffer; -/* struct hpi_control_cache_single *pControlCache; */ - struct hpi_async_event *p_async_event_buffer; struct hpi_control_cache *p_cache; }; @@ -208,8 +204,8 @@ static void instream_start(struct hpi_adapter_obj *pao, static u32 boot_loader_read_mem32(struct hpi_adapter_obj *pao, int dsp_index, u32 address); -static u16 boot_loader_write_mem32(struct hpi_adapter_obj *pao, int dsp_index, - u32 address, u32 data); +static void boot_loader_write_mem32(struct hpi_adapter_obj *pao, + int dsp_index, u32 address, u32 data); static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index); @@ -229,17 +225,7 @@ static u16 boot_loader_test_pld(struct hpi_adapter_obj *pao, int dsp_index); static void subsys_message(struct hpi_message *phm, struct hpi_response *phr) { - switch (phm->function) { - case HPI_SUBSYS_OPEN: - case HPI_SUBSYS_CLOSE: - case HPI_SUBSYS_GET_INFO: - case HPI_SUBSYS_DRIVER_UNLOAD: - case HPI_SUBSYS_DRIVER_LOAD: - case HPI_SUBSYS_FIND_ADAPTERS: - /* messages that should not get here */ - phr->error = HPI_ERROR_UNIMPLEMENTED; - break; case HPI_SUBSYS_CREATE_ADAPTER: subsys_create_adapter(phm, phr); break; @@ -257,15 +243,22 @@ static void control_message(struct hpi_adapter_obj *pao, { struct hpi_hw_obj *phw = pao->priv; + u16 pending_cache_error = 0; switch (phm->function) { case HPI_CONTROL_GET_STATE: if (pao->has_control_cache) { - rmb(); /* make sure we see updates DM_aed from DSP */ - if (hpi_check_control_cache(phw->p_cache, phm, phr)) + rmb(); /* make sure we see updates DMAed from DSP */ + if (hpi_check_control_cache(phw->p_cache, phm, phr)) { break; + } else if (phm->u.c.attribute == HPI_METER_PEAK) { + pending_cache_error = + HPI_ERROR_CONTROL_CACHING; + } } hw_message(pao, phm, phr); + if (pending_cache_error && !phr->error) + phr->error = pending_cache_error; break; case HPI_CONTROL_GET_INFO: hw_message(pao, phm, phr); @@ -273,7 +266,8 @@ static void control_message(struct hpi_adapter_obj *pao, case HPI_CONTROL_SET_STATE: hw_message(pao, phm, phr); if (pao->has_control_cache) - hpi_sync_control_cache(phw->p_cache, phm, phr); + hpi_cmn_control_cache_sync_to_msg(phw->p_cache, phm, + phr); break; default: phr->error = HPI_ERROR_INVALID_FUNC; @@ -296,9 +290,9 @@ static void outstream_message(struct hpi_adapter_obj *pao, { if (phm->obj_index >= HPI_MAX_STREAMS) { - phr->error = HPI_ERROR_INVALID_STREAM; + phr->error = HPI_ERROR_INVALID_OBJ_INDEX; HPI_DEBUG_LOG(WARNING, - "message referencing invalid stream %d " + "Message referencing invalid stream %d " "on adapter index %d\n", phm->obj_index, phm->adapter_index); return; @@ -340,9 +334,9 @@ static void instream_message(struct hpi_adapter_obj *pao, { if (phm->obj_index >= HPI_MAX_STREAMS) { - phr->error = HPI_ERROR_INVALID_STREAM; + phr->error = HPI_ERROR_INVALID_OBJ_INDEX; HPI_DEBUG_LOG(WARNING, - "message referencing invalid stream %d " + "Message referencing invalid stream %d " "on adapter index %d\n", phm->obj_index, phm->adapter_index); return; @@ -385,8 +379,8 @@ void HPI_6205(struct hpi_message *phm, struct hpi_response *phr) * All other messages are ignored unless the adapter index matches * an adapter in the HPI */ - HPI_DEBUG_LOG(DEBUG, "HPI obj=%d, func=%d\n", phm->object, - phm->function); + /* HPI_DEBUG_LOG(DEBUG, "HPI Obj=%d, Func=%d\n", phm->wObject, + phm->wFunction); */ /* if Dsp has crashed then do not communicate with it any more */ if (phm->object != HPI_OBJ_SUBSYSTEM) { @@ -411,8 +405,7 @@ void HPI_6205(struct hpi_message *phm, struct hpi_response *phr) /* Init default response */ if (phm->function != HPI_SUBSYS_CREATE_ADAPTER) - hpi_init_response(phr, phm->object, phm->function, - HPI_ERROR_PROCESSING_MESSAGE); + phr->error = HPI_ERROR_PROCESSING_MESSAGE; HPI_DEBUG_LOG(VERBOSE, "start of switch\n"); switch (phm->type) { @@ -423,9 +416,6 @@ void HPI_6205(struct hpi_message *phm, struct hpi_response *phr) break; case HPI_OBJ_ADAPTER: - phr->size = - sizeof(struct hpi_response_header) + - sizeof(struct hpi_adapter_res); adapter_message(pao, phm, phr); break; @@ -474,14 +464,6 @@ static void subsys_create_adapter(struct hpi_message *phm, memset(&ao, 0, sizeof(ao)); - /* this HPI only creates adapters for TI/PCI devices */ - if (phm->u.s.resource.bus_type != HPI_BUS_PCI) - return; - if (phm->u.s.resource.r.pci->vendor_id != HPI_PCI_VENDOR_ID_TI) - return; - if (phm->u.s.resource.r.pci->device_id != HPI_PCI_DEV_ID_DSP6205) - return; - ao.priv = kzalloc(sizeof(struct hpi_hw_obj), GFP_KERNEL); if (!ao.priv) { HPI_DEBUG_LOG(ERROR, "cant get mem for adapter object\n"); @@ -491,18 +473,20 @@ static void subsys_create_adapter(struct hpi_message *phm, ao.pci = *phm->u.s.resource.r.pci; err = create_adapter_obj(&ao, &os_error_code); - if (!err) - err = hpi_add_adapter(&ao); if (err) { - phr->u.s.data = os_error_code; delete_adapter_obj(&ao); - phr->error = err; + if (err >= HPI_ERROR_BACKEND_BASE) { + phr->error = HPI_ERROR_DSP_BOOTLOAD; + phr->specific_error = err; + } else { + phr->error = err; + } + phr->u.s.data = os_error_code; return; } - phr->u.s.aw_adapter_list[ao.index] = ao.adapter_type; + phr->u.s.adapter_type = ao.adapter_type; phr->u.s.adapter_index = ao.index; - phr->u.s.num_adapters++; phr->error = 0; } @@ -513,7 +497,7 @@ static void subsys_delete_adapter(struct hpi_message *phm, struct hpi_adapter_obj *pao; struct hpi_hw_obj *phw; - pao = hpi_find_adapter(phm->adapter_index); + pao = hpi_find_adapter(phm->obj_index); if (!pao) { phr->error = HPI_ERROR_INVALID_OBJ_INDEX; return; @@ -526,6 +510,7 @@ static void subsys_delete_adapter(struct hpi_message *phm, iowrite32(C6205_HDCR_WARMRESET, phw->prHDCR); delete_adapter_obj(pao); + hpi_delete_adapter(pao); phr->error = 0; } @@ -538,10 +523,6 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao, struct hpi_hw_obj *phw = pao->priv; struct bus_master_interface *interface; u32 phys_addr; -#ifndef HPI6205_NO_HSR_POLL - u32 time_out = HPI6205_TIMEOUT; - u32 temp1; -#endif int i; u16 err; @@ -566,7 +547,7 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao, if (hpios_locked_mem_alloc(&phw->h_locked_mem, sizeof(struct bus_master_interface), - pao->pci.p_os_data)) + pao->pci.pci_dev)) phw->p_interface_buffer = NULL; else if (hpios_locked_mem_get_virt_addr(&phw->h_locked_mem, (void *)&phw->p_interface_buffer)) @@ -591,49 +572,29 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao, /* allow boot load even if mem alloc wont work */ if (!phw->p_interface_buffer) - return hpi6205_error(0, HPI_ERROR_MEMORY_ALLOC); + return HPI_ERROR_MEMORY_ALLOC; interface = phw->p_interface_buffer; -#ifndef HPI6205_NO_HSR_POLL - /* wait for first interrupt indicating the DSP init is done */ - time_out = HPI6205_TIMEOUT * 10; - temp1 = 0; - while (((temp1 & C6205_HSR_INTSRC) == 0) && --time_out) - temp1 = ioread32(phw->prHSR); - - if (temp1 & C6205_HSR_INTSRC) - HPI_DEBUG_LOG(INFO, - "interrupt confirming DSP code running OK\n"); - else { - HPI_DEBUG_LOG(ERROR, - "timed out waiting for interrupt " - "confirming DSP code running\n"); - return hpi6205_error(0, HPI6205_ERROR_6205_NO_IRQ); - } - - /* reset the interrupt */ - iowrite32(C6205_HSR_INTSRC, phw->prHSR); -#endif - /* make sure the DSP has started ok */ if (!wait_dsp_ack(phw, H620_HIF_RESET, HPI6205_TIMEOUT * 10)) { HPI_DEBUG_LOG(ERROR, "timed out waiting reset state \n"); - return hpi6205_error(0, HPI6205_ERROR_6205_INIT_FAILED); + return HPI6205_ERROR_6205_INIT_FAILED; } /* Note that *pao, *phw are zeroed after allocation, * so pointers and flags are NULL by default. * Allocate bus mastering control cache buffer and tell the DSP about it */ if (interface->control_cache.number_of_controls) { - void *p_control_cache_virtual; + u8 *p_control_cache_virtual; err = hpios_locked_mem_alloc(&phw->h_control_cache, interface->control_cache.size_in_bytes, - pao->pci.p_os_data); + pao->pci.pci_dev); if (!err) err = hpios_locked_mem_get_virt_addr(&phw-> - h_control_cache, &p_control_cache_virtual); + h_control_cache, + (void *)&p_control_cache_virtual); if (!err) { memset(p_control_cache_virtual, 0, interface->control_cache.size_in_bytes); @@ -642,7 +603,6 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao, hpi_alloc_control_cache(interface-> control_cache.number_of_controls, interface->control_cache.size_in_bytes, - (struct hpi_control_cache_info *) p_control_cache_virtual); if (!phw->p_cache) err = HPI_ERROR_MEMORY_ALLOC; @@ -662,78 +622,56 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao, pao->has_control_cache = 0; } } - /* allocate bus mastering async buffer and tell the DSP about it */ - if (interface->async_buffer.b.size) { - err = hpios_locked_mem_alloc(&phw->h_async_event_buffer, - interface->async_buffer.b.size * - sizeof(struct hpi_async_event), pao->pci.p_os_data); - if (!err) - err = hpios_locked_mem_get_virt_addr - (&phw->h_async_event_buffer, (void *) - &phw->p_async_event_buffer); - if (!err) - memset((void *)phw->p_async_event_buffer, 0, - interface->async_buffer.b.size * - sizeof(struct hpi_async_event)); - if (!err) { - err = hpios_locked_mem_get_phys_addr - (&phw->h_async_event_buffer, &phys_addr); - interface->async_buffer.physical_address32 = - phys_addr; - } - if (err) { - if (hpios_locked_mem_valid(&phw-> - h_async_event_buffer)) { - hpios_locked_mem_free - (&phw->h_async_event_buffer); - phw->p_async_event_buffer = NULL; - } - } - } send_dsp_command(phw, H620_HIF_IDLE); { - struct hpi_message hM; - struct hpi_response hR; + struct hpi_message hm; + struct hpi_response hr; u32 max_streams; HPI_DEBUG_LOG(VERBOSE, "init ADAPTER_GET_INFO\n"); - memset(&hM, 0, sizeof(hM)); - hM.type = HPI_TYPE_MESSAGE; - hM.size = sizeof(hM); - hM.object = HPI_OBJ_ADAPTER; - hM.function = HPI_ADAPTER_GET_INFO; - hM.adapter_index = 0; - memset(&hR, 0, sizeof(hR)); - hR.size = sizeof(hR); - - err = message_response_sequence(pao, &hM, &hR); + memset(&hm, 0, sizeof(hm)); + hm.type = HPI_TYPE_MESSAGE; + hm.size = sizeof(hm); + hm.object = HPI_OBJ_ADAPTER; + hm.function = HPI_ADAPTER_GET_INFO; + hm.adapter_index = 0; + memset(&hr, 0, sizeof(hr)); + hr.size = sizeof(hr); + + err = message_response_sequence(pao, &hm, &hr); if (err) { HPI_DEBUG_LOG(ERROR, "message transport error %d\n", err); return err; } - if (hR.error) - return hR.error; + if (hr.error) + return hr.error; - pao->adapter_type = hR.u.a.adapter_type; - pao->index = hR.u.a.adapter_index; + pao->adapter_type = hr.u.ax.info.adapter_type; + pao->index = hr.u.ax.info.adapter_index; - max_streams = hR.u.a.num_outstreams + hR.u.a.num_instreams; + max_streams = + hr.u.ax.info.num_outstreams + + hr.u.ax.info.num_instreams; hpios_locked_mem_prepare((max_streams * 6) / 10, max_streams, - 65536, pao->pci.p_os_data); + 65536, pao->pci.pci_dev); HPI_DEBUG_LOG(VERBOSE, "got adapter info type %x index %d serial %d\n", - hR.u.a.adapter_type, hR.u.a.adapter_index, - hR.u.a.serial_number); + hr.u.ax.info.adapter_type, hr.u.ax.info.adapter_index, + hr.u.ax.info.serial_number); } pao->open = 0; /* upon creation the adapter is closed */ + if (phw->p_cache) + phw->p_cache->adap_idx = pao->index; + HPI_DEBUG_LOG(INFO, "bootload DSP OK\n"); - return 0; + + return hpi_add_adapter(pao); } /** Free memory areas allocated by adapter @@ -747,11 +685,6 @@ static void delete_adapter_obj(struct hpi_adapter_obj *pao) phw = pao->priv; - if (hpios_locked_mem_valid(&phw->h_async_event_buffer)) { - hpios_locked_mem_free(&phw->h_async_event_buffer); - phw->p_async_event_buffer = NULL; - } - if (hpios_locked_mem_valid(&phw->h_control_cache)) { hpios_locked_mem_free(&phw->h_control_cache); hpi_free_control_cache(phw->p_cache); @@ -776,13 +709,15 @@ static void delete_adapter_obj(struct hpi_adapter_obj *pao) phw->outstream_host_buffer_size[i] = 0; } - hpios_locked_mem_unprepare(pao->pci.p_os_data); + hpios_locked_mem_unprepare(pao->pci.pci_dev); - hpi_delete_adapter(pao); kfree(phw); } /*****************************************************************************/ +/* Adapter functions */ + +/*****************************************************************************/ /* OutStream Host buffer functions */ /** Allocate or attach buffer for busmastering @@ -824,7 +759,7 @@ static void outstream_host_buffer_allocate(struct hpi_adapter_obj *pao, err = hpios_locked_mem_alloc(&phw->outstream_host_buffers [phm->obj_index], phm->u.d.u.buffer.buffer_size, - pao->pci.p_os_data); + pao->pci.pci_dev); if (err) { phr->error = HPI_ERROR_INVALID_DATASIZE; @@ -861,7 +796,7 @@ static void outstream_host_buffer_allocate(struct hpi_adapter_obj *pao, if (phm->u.d.u.buffer.buffer_size & (phm->u.d.u.buffer. buffer_size - 1)) { HPI_DEBUG_LOG(ERROR, - "buffer size must be 2^N not %d\n", + "Buffer size must be 2^N not %d\n", phm->u.d.u.buffer.buffer_size); phr->error = HPI_ERROR_INVALID_DATASIZE; return; @@ -875,6 +810,7 @@ static void outstream_host_buffer_allocate(struct hpi_adapter_obj *pao, status->dSP_index = 0; status->host_index = status->dSP_index; status->size_in_bytes = phm->u.d.u.buffer.buffer_size; + status->auxiliary_data_available = 0; hw_message(pao, phm, phr); @@ -966,51 +902,6 @@ static void outstream_write(struct hpi_adapter_obj *pao, hpi_init_response(phr, phm->object, phm->function, 0); status = &interface->outstream_host_buffer_status[phm->obj_index]; - if (phw->flag_outstream_just_reset[phm->obj_index]) { - /* First OutStremWrite() call following reset will write data to the - adapter's buffers, reducing delay before stream can start. The DSP - takes care of setting the stream data format using format information - embedded in phm. - */ - int partial_write = 0; - unsigned int original_size = 0; - - phw->flag_outstream_just_reset[phm->obj_index] = 0; - - /* Send the first buffer to the DSP the old way. */ - /* Limit size of first transfer - */ - /* expect that this will not usually be triggered. */ - if (phm->u.d.u.data.data_size > HPI6205_SIZEOF_DATA) { - partial_write = 1; - original_size = phm->u.d.u.data.data_size; - phm->u.d.u.data.data_size = HPI6205_SIZEOF_DATA; - } - /* write it */ - phm->function = HPI_OSTREAM_WRITE; - hw_message(pao, phm, phr); - - if (phr->error) - return; - - /* update status information that the DSP would typically - * update (and will update next time the DSP - * buffer update task reads data from the host BBM buffer) - */ - status->auxiliary_data_available = phm->u.d.u.data.data_size; - status->host_index += phm->u.d.u.data.data_size; - status->dSP_index += phm->u.d.u.data.data_size; - - /* if we did a full write, we can return from here. */ - if (!partial_write) - return; - - /* tweak buffer parameters and let the rest of the */ - /* buffer land in internal BBM buffer */ - phm->u.d.u.data.data_size = - original_size - HPI6205_SIZEOF_DATA; - phm->u.d.u.data.pb_data += HPI6205_SIZEOF_DATA; - } - space_available = outstream_get_space_available(status); if (space_available < phm->u.d.u.data.data_size) { phr->error = HPI_ERROR_INVALID_DATASIZE; @@ -1047,6 +938,24 @@ static void outstream_write(struct hpi_adapter_obj *pao, memcpy(p_bbm_data, p_app_data + l_first_write, phm->u.d.u.data.data_size - l_first_write); } + + /* + * This version relies on the DSP code triggering an OStream buffer + * update immediately following a SET_FORMAT call. The host has + * already written data into the BBM buffer, but the DSP won't know + * about it until dwHostIndex is adjusted. + */ + if (phw->flag_outstream_just_reset[phm->obj_index]) { + /* Format can only change after reset. Must tell DSP. */ + u16 function = phm->function; + phw->flag_outstream_just_reset[phm->obj_index] = 0; + phm->function = HPI_OSTREAM_SET_FORMAT; + hw_message(pao, phm, phr); /* send the format to the DSP */ + phm->function = function; + if (phr->error) + return; + } + status->host_index += phm->u.d.u.data.data_size; } @@ -1132,7 +1041,7 @@ static void instream_host_buffer_allocate(struct hpi_adapter_obj *pao, err = hpios_locked_mem_alloc(&phw->instream_host_buffers[phm-> obj_index], phm->u.d.u.buffer.buffer_size, - pao->pci.p_os_data); + pao->pci.pci_dev); if (err) { phr->error = HPI_ERROR_INVALID_DATASIZE; @@ -1163,7 +1072,7 @@ static void instream_host_buffer_allocate(struct hpi_adapter_obj *pao, if (phm->u.d.u.buffer.buffer_size & (phm->u.d.u.buffer. buffer_size - 1)) { HPI_DEBUG_LOG(ERROR, - "buffer size must be 2^N not %d\n", + "Buffer size must be 2^N not %d\n", phm->u.d.u.buffer.buffer_size); phr->error = HPI_ERROR_INVALID_DATASIZE; return; @@ -1178,8 +1087,10 @@ static void instream_host_buffer_allocate(struct hpi_adapter_obj *pao, status->dSP_index = 0; status->host_index = status->dSP_index; status->size_in_bytes = phm->u.d.u.buffer.buffer_size; + status->auxiliary_data_available = 0; hw_message(pao, phm, phr); + if (phr->error && hpios_locked_mem_valid(&phw-> instream_host_buffers[phm->obj_index])) { @@ -1344,33 +1255,36 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao, struct hpi_hw_obj *phw = pao->priv; struct dsp_code dsp_code; u16 boot_code_id[HPI6205_MAX_FILES_TO_LOAD]; - u16 firmware_id = pao->pci.subsys_device_id; u32 temp; int dsp = 0, i = 0; u16 err = 0; boot_code_id[0] = HPI_ADAPTER_ASI(0x6205); - /* special cases where firmware_id != subsys ID */ - switch (firmware_id) { + boot_code_id[1] = pao->pci.pci_dev->subsystem_device; + boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(boot_code_id[1]); + + /* fix up cases where bootcode id[1] != subsys id */ + switch (boot_code_id[1]) { case HPI_ADAPTER_FAMILY_ASI(0x5000): - boot_code_id[0] = firmware_id; - firmware_id = 0; + boot_code_id[0] = boot_code_id[1]; + boot_code_id[1] = 0; break; case HPI_ADAPTER_FAMILY_ASI(0x5300): case HPI_ADAPTER_FAMILY_ASI(0x5400): case HPI_ADAPTER_FAMILY_ASI(0x6300): - firmware_id = HPI_ADAPTER_FAMILY_ASI(0x6400); + boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(0x6400); break; case HPI_ADAPTER_FAMILY_ASI(0x5600): case HPI_ADAPTER_FAMILY_ASI(0x6500): - firmware_id = HPI_ADAPTER_FAMILY_ASI(0x6600); + boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(0x6600); break; case HPI_ADAPTER_FAMILY_ASI(0x8800): - firmware_id = HPI_ADAPTER_FAMILY_ASI(0x8900); + boot_code_id[1] = HPI_ADAPTER_FAMILY_ASI(0x8900); + break; + default: break; } - boot_code_id[1] = firmware_id; /* reset DSP by writing a 1 to the WARMRESET bit */ temp = C6205_HDCR_WARMRESET; @@ -1381,7 +1295,7 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao, temp = ioread32(phw->prHSR); if ((temp & (C6205_HSR_CFGERR | C6205_HSR_EEREAD)) != C6205_HSR_EEREAD) - return hpi6205_error(0, HPI6205_ERROR_6205_EEPROM); + return HPI6205_ERROR_6205_EEPROM; temp |= 0x04; /* disable PINTA interrupt */ iowrite32(temp, phw->prHSR); @@ -1389,27 +1303,27 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao, /* check control register reports PCI boot mode */ temp = ioread32(phw->prHDCR); if (!(temp & C6205_HDCR_PCIBOOT)) - return hpi6205_error(0, HPI6205_ERROR_6205_REG); + return HPI6205_ERROR_6205_REG; - /* try writing a couple of numbers to the DSP page register */ + /* try writing a few numbers to the DSP page register */ /* and reading them back. */ - temp = 1; + temp = 3; iowrite32(temp, phw->prDSPP); if ((temp | C6205_DSPP_MAP1) != ioread32(phw->prDSPP)) - return hpi6205_error(0, HPI6205_ERROR_6205_DSPPAGE); + return HPI6205_ERROR_6205_DSPPAGE; temp = 2; iowrite32(temp, phw->prDSPP); if ((temp | C6205_DSPP_MAP1) != ioread32(phw->prDSPP)) - return hpi6205_error(0, HPI6205_ERROR_6205_DSPPAGE); - temp = 3; + return HPI6205_ERROR_6205_DSPPAGE; + temp = 1; iowrite32(temp, phw->prDSPP); if ((temp | C6205_DSPP_MAP1) != ioread32(phw->prDSPP)) - return hpi6205_error(0, HPI6205_ERROR_6205_DSPPAGE); + return HPI6205_ERROR_6205_DSPPAGE; /* reset DSP page to the correct number */ temp = 0; iowrite32(temp, phw->prDSPP); if ((temp | C6205_DSPP_MAP1) != ioread32(phw->prDSPP)) - return hpi6205_error(0, HPI6205_ERROR_6205_DSPPAGE); + return HPI6205_ERROR_6205_DSPPAGE; phw->dsp_page = 0; /* release 6713 from reset before 6205 is bootloaded. @@ -1455,7 +1369,7 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao, return err; /* write the DSP code down into the DSPs memory */ - dsp_code.ps_dev = pao->pci.p_os_data; + dsp_code.ps_dev = pao->pci.pci_dev; err = hpi_dsp_code_open(boot_code_id[dsp], &dsp_code, pos_error_code); if (err) @@ -1484,10 +1398,8 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao, if (err) break; for (i = 0; i < (int)length; i++) { - err = boot_loader_write_mem32(pao, dsp, - address, *pcode); - if (err) - break; + boot_loader_write_mem32(pao, dsp, address, + *pcode); /* dummy read every 4 words */ /* for 6205 advisory 1.4.4 */ if (i % 4 == 0) @@ -1561,7 +1473,7 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao, host_mailbox_address_on_dsp = 0x80000000; while ((physicalPC_iaddress != physicalPC_iaddress_verify) && time_out--) { - err = boot_loader_write_mem32(pao, 0, + boot_loader_write_mem32(pao, 0, host_mailbox_address_on_dsp, physicalPC_iaddress); physicalPC_iaddress_verify = @@ -1631,11 +1543,10 @@ static u32 boot_loader_read_mem32(struct hpi_adapter_obj *pao, int dsp_index, return data; } -static u16 boot_loader_write_mem32(struct hpi_adapter_obj *pao, int dsp_index, - u32 address, u32 data) +static void boot_loader_write_mem32(struct hpi_adapter_obj *pao, + int dsp_index, u32 address, u32 data) { struct hpi_hw_obj *phw = pao->priv; - u16 err = 0; __iomem u32 *p_data; /* u32 dwVerifyData=0; */ @@ -1675,15 +1586,11 @@ static u16 boot_loader_write_mem32(struct hpi_adapter_obj *pao, int dsp_index, /* dummy read every 4 words for 6205 advisory 1.4.4 */ boot_loader_read_mem32(pao, 0, 0); - } else - err = hpi6205_error(dsp_index, HPI6205_ERROR_BAD_DSPINDEX); - return err; + } } static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index) { - u16 err = 0; - if (dsp_index == 0) { u32 setting; @@ -1711,8 +1618,7 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index) boot_loader_write_mem32(pao, dsp_index, 0x01800008, setting); if (setting != boot_loader_read_mem32(pao, dsp_index, 0x01800008)) - return hpi6205_error(dsp_index, - HPI6205_ERROR_DSP_EMIF); + return HPI6205_ERROR_DSP_EMIF; /* EMIF CE1 setup - 32 bit async. This is 6713 #1 HPI, */ /* which occupies D15..0. 6713 starts at 27MHz, so need */ @@ -1725,8 +1631,7 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index) boot_loader_write_mem32(pao, dsp_index, 0x01800004, setting); if (setting != boot_loader_read_mem32(pao, dsp_index, 0x01800004)) - return hpi6205_error(dsp_index, - HPI6205_ERROR_DSP_EMIF); + return HPI6205_ERROR_DSP_EMIF; /* EMIF CE2 setup - 32 bit async. This is 6713 #2 HPI, */ /* which occupies D15..0. 6713 starts at 27MHz, so need */ @@ -1738,8 +1643,7 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index) boot_loader_write_mem32(pao, dsp_index, 0x01800010, setting); if (setting != boot_loader_read_mem32(pao, dsp_index, 0x01800010)) - return hpi6205_error(dsp_index, - HPI6205_ERROR_DSP_EMIF); + return HPI6205_ERROR_DSP_EMIF; /* EMIF CE3 setup - 32 bit async. */ /* This is the PLD on the ASI5000 cards only */ @@ -1750,8 +1654,7 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index) boot_loader_write_mem32(pao, dsp_index, 0x01800014, setting); if (setting != boot_loader_read_mem32(pao, dsp_index, 0x01800014)) - return hpi6205_error(dsp_index, - HPI6205_ERROR_DSP_EMIF); + return HPI6205_ERROR_DSP_EMIF; /* set EMIF SDRAM control for 2Mx32 SDRAM (512x32x4 bank) */ /* need to use this else DSP code crashes? */ @@ -1775,12 +1678,9 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index) read_data = 0xFFF7 & boot_loader_read_mem32(pao, 0, HPICL_ADDR); if (write_data != read_data) { - err = hpi6205_error(dsp_index, - HPI6205_ERROR_C6713_HPIC); HPI_DEBUG_LOG(ERROR, "HPICL %x %x\n", write_data, read_data); - - return err; + return HPI6205_ERROR_C6713_HPIC; } /* HPIA - walking ones test */ write_data = 1; @@ -1798,11 +1698,9 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index) HPIAH_ADDR)) << 16); if (read_data != write_data) { - err = hpi6205_error(dsp_index, - HPI6205_ERROR_C6713_HPIA); HPI_DEBUG_LOG(ERROR, "HPIA %x %x\n", write_data, read_data); - return err; + return HPI6205_ERROR_C6713_HPIA; } write_data = write_data << 1; } @@ -1847,30 +1745,81 @@ static u16 boot_loader_config_emif(struct hpi_adapter_obj *pao, int dsp_index) /* PLL should not be bypassed! */ if ((boot_loader_read_mem32(pao, dsp_index, 0x01B7C100) & 0xF) != 0x0001) { - err = hpi6205_error(dsp_index, - HPI6205_ERROR_C6713_PLL); - return err; + return HPI6205_ERROR_C6713_PLL; } /* setup C67x EMIF (note this is the only use of BAR1 via BootLoader_WriteMem32) */ boot_loader_write_mem32(pao, dsp_index, C6713_EMIF_GCTL, 0x000034A8); + + /* EMIF CE0 setup - 2Mx32 Sync DRAM + 31..28 Wr setup + 27..22 Wr strobe + 21..20 Wr hold + 19..16 Rd setup + 15..14 - + 13..8 Rd strobe + 7..4 MTYPE 0011 Sync DRAM 32bits + 3 Wr hold MSB + 2..0 Rd hold + */ boot_loader_write_mem32(pao, dsp_index, C6713_EMIF_CE0, 0x00000030); + + /* EMIF SDRAM Extension + 0x00 + 31-21 0000b 0000b 000b + 20 WR2RD = 2cycles-1 = 1b + + 19-18 WR2DEAC = 3cycle-1 = 10b + 17 WR2WR = 2cycle-1 = 1b + 16-15 R2WDQM = 4cycle-1 = 11b + 14-12 RD2WR = 6cycles-1 = 101b + + 11-10 RD2DEAC = 4cycle-1 = 11b + 9 RD2RD = 2cycle-1 = 1b + 8-7 THZP = 3cycle-1 = 10b + 6-5 TWR = 2cycle-1 = 01b (tWR = 17ns) + 4 TRRD = 2cycle = 0b (tRRD = 14ns) + 3-1 TRAS = 5cycle-1 = 100b (Tras=42ns) + 1 CAS latency = 3cyc = 1b + (for Micron 2M32-7 operating at 100MHz) + */ boot_loader_write_mem32(pao, dsp_index, C6713_EMIF_SDRAMEXT, 0x001BDF29); + + /* EMIF SDRAM control - set up for a 2Mx32 SDRAM (512x32x4 bank) + 31 - 0b - + 30 SDBSZ 1b 4 bank + 29..28 SDRSZ 00b 11 row address pins + + 27..26 SDCSZ 01b 8 column address pins + 25 RFEN 1b refersh enabled + 24 INIT 1b init SDRAM! + + 23..20 TRCD 0001b (Trcd/Tcyc)-1 = (20/10)-1 = 1 + + 19..16 TRP 0001b (Trp/Tcyc)-1 = (20/10)-1 = 1 + + 15..12 TRC 0110b (Trc/Tcyc)-1 = (70/10)-1 = 6 + + 11..0 - 0000b 0000b 0000b + */ boot_loader_write_mem32(pao, dsp_index, C6713_EMIF_SDRAMCTL, - 0x47117000); + 0x47116000); + + /* SDRAM refresh timing + Need 4,096 refresh cycles every 64ms = 15.625us = 1562cycles of 100MHz = 0x61A + */ boot_loader_write_mem32(pao, dsp_index, C6713_EMIF_SDRAMTIMING, 0x00000410); hpios_delay_micro_seconds(1000); } else if (dsp_index == 2) { /* DSP 2 is a C6713 */ + } - } else - err = hpi6205_error(dsp_index, HPI6205_ERROR_BAD_DSPINDEX); - return err; + return 0; } static u16 boot_loader_test_memory(struct hpi_adapter_obj *pao, int dsp_index, @@ -1896,7 +1845,7 @@ static u16 boot_loader_test_memory(struct hpi_adapter_obj *pao, int dsp_index, test_addr); if (data != test_data) { HPI_DEBUG_LOG(VERBOSE, - "memtest error details " + "Memtest error details " "%08x %08x %08x %i\n", test_addr, test_data, data, dsp_index); return 1; /* error */ @@ -1916,7 +1865,7 @@ static u16 boot_loader_test_memory(struct hpi_adapter_obj *pao, int dsp_index, data = boot_loader_read_mem32(pao, dsp_index, test_addr); if (data != test_data) { HPI_DEBUG_LOG(VERBOSE, - "memtest error details " + "Memtest error details " "%08x %08x %08x %i\n", test_addr, test_data, data, dsp_index); return 1; /* error */ @@ -1946,8 +1895,8 @@ static u16 boot_loader_test_internal_memory(struct hpi_adapter_obj *pao, /* 64K data mem */ err = boot_loader_test_memory(pao, dsp_index, 0x80000000, 0x10000); - } else if ((dsp_index == 1) || (dsp_index == 2)) { - /* DSP 1&2 are a C6713 */ + } else if (dsp_index == 1) { + /* DSP 1 is a C6713 */ /* 192K internal mem */ err = boot_loader_test_memory(pao, dsp_index, 0x00000000, 0x30000); @@ -1955,11 +1904,10 @@ static u16 boot_loader_test_internal_memory(struct hpi_adapter_obj *pao, /* 64K internal mem / L2 cache */ err = boot_loader_test_memory(pao, dsp_index, 0x00030000, 0x10000); - } else - return hpi6205_error(dsp_index, HPI6205_ERROR_BAD_DSPINDEX); + } if (err) - return hpi6205_error(dsp_index, HPI6205_ERROR_DSP_INTMEM); + return HPI6205_ERROR_DSP_INTMEM; else return 0; } @@ -1972,24 +1920,23 @@ static u16 boot_loader_test_external_memory(struct hpi_adapter_obj *pao, if (dsp_index == 0) { /* only test for SDRAM if an ASI5000 card */ - if (pao->pci.subsys_device_id == 0x5000) { + if (pao->pci.pci_dev->subsystem_device == 0x5000) { /* DSP 0 is always C6205 */ dRAM_start_address = 0x00400000; dRAM_size = 0x200000; /*dwDRAMinc=1024; */ } else return 0; - } else if ((dsp_index == 1) || (dsp_index == 2)) { + } else if (dsp_index == 1) { /* DSP 1 is a C6713 */ dRAM_start_address = 0x80000000; dRAM_size = 0x200000; /*dwDRAMinc=1024; */ - } else - return hpi6205_error(dsp_index, HPI6205_ERROR_BAD_DSPINDEX); + } if (boot_loader_test_memory(pao, dsp_index, dRAM_start_address, dRAM_size)) - return hpi6205_error(dsp_index, HPI6205_ERROR_DSP_EXTMEM); + return HPI6205_ERROR_DSP_EXTMEM; return 0; } @@ -1998,28 +1945,25 @@ static u16 boot_loader_test_pld(struct hpi_adapter_obj *pao, int dsp_index) u32 data = 0; if (dsp_index == 0) { /* only test for DSP0 PLD on ASI5000 card */ - if (pao->pci.subsys_device_id == 0x5000) { + if (pao->pci.pci_dev->subsystem_device == 0x5000) { /* PLD is located at CE3=0x03000000 */ data = boot_loader_read_mem32(pao, dsp_index, 0x03000008); if ((data & 0xF) != 0x5) - return hpi6205_error(dsp_index, - HPI6205_ERROR_DSP_PLD); + return HPI6205_ERROR_DSP_PLD; data = boot_loader_read_mem32(pao, dsp_index, 0x0300000C); if ((data & 0xF) != 0xA) - return hpi6205_error(dsp_index, - HPI6205_ERROR_DSP_PLD); + return HPI6205_ERROR_DSP_PLD; } } else if (dsp_index == 1) { /* DSP 1 is a C6713 */ - if (pao->pci.subsys_device_id == 0x8700) { + if (pao->pci.pci_dev->subsystem_device == 0x8700) { /* PLD is located at CE1=0x90000000 */ data = boot_loader_read_mem32(pao, dsp_index, 0x90000010); if ((data & 0xFF) != 0xAA) - return hpi6205_error(dsp_index, - HPI6205_ERROR_DSP_PLD); + return HPI6205_ERROR_DSP_PLD; /* 8713 - LED on */ boot_loader_write_mem32(pao, dsp_index, 0x90000000, 0x02); @@ -2037,14 +1981,11 @@ static short hpi6205_transfer_data(struct hpi_adapter_obj *pao, u8 *p_data, struct hpi_hw_obj *phw = pao->priv; u32 data_transferred = 0; u16 err = 0; -#ifndef HPI6205_NO_HSR_POLL - u32 time_out; -#endif u32 temp2; struct bus_master_interface *interface = phw->p_interface_buffer; if (!p_data) - return HPI_ERROR_INVALID_DATA_TRANSFER; + return HPI_ERROR_INVALID_DATA_POINTER; data_size &= ~3L; /* round data_size down to nearest 4 bytes */ @@ -2064,14 +2005,10 @@ static short hpi6205_transfer_data(struct hpi_adapter_obj *pao, u8 *p_data, interface->transfer_size_in_bytes = this_copy; -#ifdef HPI6205_NO_HSR_POLL /* DSP must change this back to nOperation */ interface->dsp_ack = H620_HIF_IDLE; -#endif - send_dsp_command(phw, operation); -#ifdef HPI6205_NO_HSR_POLL temp2 = wait_dsp_ack(phw, operation, HPI6205_TIMEOUT); HPI_DEBUG_LOG(DEBUG, "spun %d times for data xfer of %d\n", HPI6205_TIMEOUT - temp2, this_copy); @@ -2079,45 +2016,11 @@ static short hpi6205_transfer_data(struct hpi_adapter_obj *pao, u8 *p_data, if (!temp2) { /* timed out */ HPI_DEBUG_LOG(ERROR, - "timed out waiting for " "state %d got %d\n", + "Timed out waiting for " "state %d got %d\n", operation, interface->dsp_ack); break; } -#else - /* spin waiting on the result */ - time_out = HPI6205_TIMEOUT; - temp2 = 0; - while ((temp2 == 0) && time_out--) { - /* give 16k bus mastering transfer time to happen */ - /*(16k / 132Mbytes/s = 122usec) */ - hpios_delay_micro_seconds(20); - temp2 = ioread32(phw->prHSR); - temp2 &= C6205_HSR_INTSRC; - } - HPI_DEBUG_LOG(DEBUG, "spun %d times for data xfer of %d\n", - HPI6205_TIMEOUT - time_out, this_copy); - if (temp2 == C6205_HSR_INTSRC) { - HPI_DEBUG_LOG(VERBOSE, - "interrupt from HIF <data> OK\n"); - /* - if(interface->dwDspAck != nOperation) { - HPI_DEBUG_LOG(DEBUG("interface->dwDspAck=%d, - expected %d \n", - interface->dwDspAck,nOperation); - } - */ - } -/* need to handle this differently... */ - else { - HPI_DEBUG_LOG(ERROR, - "interrupt from HIF <data> BAD\n"); - err = HPI_ERROR_DSP_HARDWARE; - } - - /* reset the interrupt from the DSP */ - iowrite32(C6205_HSR_INTSRC, phw->prHSR); -#endif if (operation == H620_HIF_GET_DATA) memcpy(&p_data[data_transferred], (void *)&interface->u.b_data[0], this_copy); @@ -2174,31 +2077,39 @@ static unsigned int message_count; static u16 message_response_sequence(struct hpi_adapter_obj *pao, struct hpi_message *phm, struct hpi_response *phr) { -#ifndef HPI6205_NO_HSR_POLL - u32 temp2; -#endif u32 time_out, time_out2; struct hpi_hw_obj *phw = pao->priv; struct bus_master_interface *interface = phw->p_interface_buffer; u16 err = 0; message_count++; + if (phm->size > sizeof(interface->u)) { + phr->error = HPI_ERROR_MESSAGE_BUFFER_TOO_SMALL; + phr->specific_error = sizeof(interface->u); + phr->size = sizeof(struct hpi_response_header); + HPI_DEBUG_LOG(ERROR, + "message len %d too big for buffer %zd \n", phm->size, + sizeof(interface->u)); + return 0; + } + /* Assume buffer of type struct bus_master_interface is allocated "noncacheable" */ if (!wait_dsp_ack(phw, H620_HIF_IDLE, HPI6205_TIMEOUT)) { HPI_DEBUG_LOG(DEBUG, "timeout waiting for idle\n"); - return hpi6205_error(0, HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT); + return HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT; } - interface->u.message_buffer = *phm; + + memcpy(&interface->u.message_buffer, phm, phm->size); /* signal we want a response */ send_dsp_command(phw, H620_HIF_GET_RESP); time_out2 = wait_dsp_ack(phw, H620_HIF_GET_RESP, HPI6205_TIMEOUT); - if (time_out2 == 0) { + if (!time_out2) { HPI_DEBUG_LOG(ERROR, - "(%u) timed out waiting for " "GET_RESP state [%x]\n", + "(%u) Timed out waiting for " "GET_RESP state [%x]\n", message_count, interface->dsp_ack); } else { HPI_DEBUG_LOG(VERBOSE, @@ -2208,58 +2119,38 @@ static u16 message_response_sequence(struct hpi_adapter_obj *pao, /* spin waiting on HIF interrupt flag (end of msg process) */ time_out = HPI6205_TIMEOUT; -#ifndef HPI6205_NO_HSR_POLL - temp2 = 0; - while ((temp2 == 0) && --time_out) { - temp2 = ioread32(phw->prHSR); - temp2 &= C6205_HSR_INTSRC; - hpios_delay_micro_seconds(1); - } - if (temp2 == C6205_HSR_INTSRC) { - rmb(); /* ensure we see latest value for dsp_ack */ - if ((interface->dsp_ack != H620_HIF_GET_RESP)) { - HPI_DEBUG_LOG(DEBUG, - "(%u)interface->dsp_ack(0x%x) != " - "H620_HIF_GET_RESP, t=%u\n", message_count, - interface->dsp_ack, - HPI6205_TIMEOUT - time_out); - } else { - HPI_DEBUG_LOG(VERBOSE, - "(%u)int with GET_RESP after %u\n", - message_count, HPI6205_TIMEOUT - time_out); + /* read the result */ + if (time_out) { + if (interface->u.response_buffer.size <= phr->size) + memcpy(phr, &interface->u.response_buffer, + interface->u.response_buffer.size); + else { + HPI_DEBUG_LOG(ERROR, + "response len %d too big for buffer %d\n", + interface->u.response_buffer.size, phr->size); + memcpy(phr, &interface->u.response_buffer, + sizeof(struct hpi_response_header)); + phr->error = HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL; + phr->specific_error = + interface->u.response_buffer.size; + phr->size = sizeof(struct hpi_response_header); } - - } else { - /* can we do anything else in response to the error ? */ - HPI_DEBUG_LOG(ERROR, - "interrupt from HIF module BAD (function %x)\n", - phm->function); } - - /* reset the interrupt from the DSP */ - iowrite32(C6205_HSR_INTSRC, phw->prHSR); -#endif - - /* read the result */ - if (time_out != 0) - *phr = interface->u.response_buffer; - /* set interface back to idle */ send_dsp_command(phw, H620_HIF_IDLE); - if ((time_out == 0) || (time_out2 == 0)) { + if (!time_out || !time_out2) { HPI_DEBUG_LOG(DEBUG, "something timed out!\n"); - return hpi6205_error(0, HPI6205_ERROR_MSG_RESP_TIMEOUT); + return HPI6205_ERROR_MSG_RESP_TIMEOUT; } /* special case for adapter close - */ /* wait for the DSP to indicate it is idle */ if (phm->function == HPI_ADAPTER_CLOSE) { if (!wait_dsp_ack(phw, H620_HIF_IDLE, HPI6205_TIMEOUT)) { HPI_DEBUG_LOG(DEBUG, - "timeout waiting for idle " + "Timeout waiting for idle " "(on adapter_close)\n"); - return hpi6205_error(0, - HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT); + return HPI6205_ERROR_MSG_RESP_IDLE_TIMEOUT; } } err = hpi_validate_response(phm, phr); @@ -2279,7 +2170,13 @@ static void hw_message(struct hpi_adapter_obj *pao, struct hpi_message *phm, /* maybe an error response */ if (err) { /* something failed in the HPI/DSP interface */ - phr->error = err; + if (err >= HPI_ERROR_BACKEND_BASE) { + phr->error = HPI_ERROR_DSP_COMMUNICATION; + phr->specific_error = err; + } else { + phr->error = err; + } + pao->dsp_crashed++; /* just the header of the response is valid */ diff --git a/sound/pci/asihpi/hpi6205.h b/sound/pci/asihpi/hpi6205.h index 1adae0857cda..df2f02c0c7b4 100644 --- a/sound/pci/asihpi/hpi6205.h +++ b/sound/pci/asihpi/hpi6205.h @@ -25,9 +25,6 @@ Copyright AudioScience, Inc., 2003 #ifndef _HPI6205_H_ #define _HPI6205_H_ -/* transitional conditional compile shared between host and DSP */ -/* #define HPI6205_NO_HSR_POLL */ - #include "hpi_internal.h" /*********************************************************** @@ -78,8 +75,8 @@ struct bus_master_interface { u32 dsp_ack; u32 transfer_size_in_bytes; union { - struct hpi_message message_buffer; - struct hpi_response response_buffer; + struct hpi_message_header message_buffer; + struct hpi_response_header response_buffer; u8 b_data[HPI6205_SIZEOF_DATA]; } u; struct controlcache_6205 control_cache; diff --git a/sound/pci/asihpi/hpi_internal.h b/sound/pci/asihpi/hpi_internal.h index 16f502d459de..af678be0aa15 100644 --- a/sound/pci/asihpi/hpi_internal.h +++ b/sound/pci/asihpi/hpi_internal.h @@ -28,7 +28,7 @@ HPI internal definitions /** maximum number of memory regions mapped to an adapter */ #define HPI_MAX_ADAPTER_MEM_SPACES (2) -/* Each OS needs its own hpios.h, or specific define as above */ +/* Each OS needs its own hpios.h */ #include "hpios.h" /* physical memory allocation */ @@ -49,7 +49,7 @@ HpiOs_LockedMem_GetPyhsAddr() will always succed on the returned handle. */ u16 hpios_locked_mem_alloc(struct consistent_dma_area *p_locked_mem_handle, /**< memory handle */ - u32 size, /**< size in bytes to allocate */ + u32 size, /**< Size in bytes to allocate */ struct pci_dev *p_os_reference /**< OS specific data required for memory allocation */ ); @@ -96,41 +96,6 @@ typedef void hpi_handler_func(struct hpi_message *, struct hpi_response *); #define compile_time_assert(cond, msg) \ typedef char ASSERT_##msg[(cond) ? 1 : -1] -/*/////////////////////////////////////////////////////////////////////////// */ -/* Private HPI Entity related definitions */ - -#define STR_SIZE_FIELD_MAX 65535U -#define STR_TYPE_FIELD_MAX 255U -#define STR_ROLE_FIELD_MAX 255U - -struct hpi_entity_str { - u16 size; - u8 type; - u8 role; -}; - -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable : 4200) -#endif - -struct hpi_entity { - struct hpi_entity_str header; -#if ! defined(HPI_OS_DSP_C6000) || (defined(HPI_OS_DSP_C6000) && (__TI_COMPILER_VERSION__ > 6000008)) - /* DSP C6000 compiler v6.0.8 and lower - do not support flexible array member */ - u8 value[]; -#else - /* NOTE! Using sizeof(struct hpi_entity) will give erroneous results */ -#define HPI_INTERNAL_WARN_ABOUT_ENTITY_VALUE - u8 value[1]; -#endif -}; - -#if defined(_MSC_VER) -#pragma warning(pop) -#endif - /******************************************* bus types */ enum HPI_BUSES { HPI_BUS_ISAPNP = 1, @@ -139,206 +104,155 @@ enum HPI_BUSES { HPI_BUS_NET = 4 }; +enum HPI_SUBSYS_OPTIONS { + /* 0, 256 are invalid, 1..255 reserved for global options */ + HPI_SUBSYS_OPT_NET_ENABLE = 257, + HPI_SUBSYS_OPT_NET_BROADCAST = 258, + HPI_SUBSYS_OPT_NET_UNICAST = 259, + HPI_SUBSYS_OPT_NET_ADDR = 260, + HPI_SUBSYS_OPT_NET_MASK = 261, + HPI_SUBSYS_OPT_NET_ADAPTER_ADDRESS_ADD = 262 +}; + +/** Volume flags +*/ +enum HPI_VOLUME_FLAGS { + /** Set if the volume control is muted */ + HPI_VOLUME_FLAG_MUTED = (1 << 0), + /** Set if the volume control has a mute function */ + HPI_VOLUME_FLAG_HAS_MUTE = (1 << 1), + /** Set if volume control can do autofading */ + HPI_VOLUME_FLAG_HAS_AUTOFADE = (1 << 2) + /* Note Flags >= (1<<8) are for DSP internal use only */ +}; + /******************************************* CONTROL ATTRIBUTES ****/ /* (in order of control type ID */ /* This allows for 255 control types, 256 unique attributes each */ -#define HPI_CTL_ATTR(ctl, ai) (HPI_CONTROL_##ctl * 0x100 + ai) +#define HPI_CTL_ATTR(ctl, ai) ((HPI_CONTROL_##ctl << 8) + ai) /* Get the sub-index of the attribute for a control type */ -#define HPI_CTL_ATTR_INDEX(i) (i&0xff) +#define HPI_CTL_ATTR_INDEX(i) (i & 0xff) /* Extract the control from the control attribute */ -#define HPI_CTL_ATTR_CONTROL(i) (i>>8) - -/* Generic control attributes. */ - -/** Enable a control. -0=disable, 1=enable -\note generic to all mixer plugins? -*/ -#define HPI_GENERIC_ENABLE HPI_CTL_ATTR(GENERIC, 1) +#define HPI_CTL_ATTR_CONTROL(i) (i >> 8) /** Enable event generation for a control. 0=disable, 1=enable \note generic to all controls that can generate events */ -#define HPI_GENERIC_EVENT_ENABLE HPI_CTL_ATTR(GENERIC, 2) - -/* Volume Control attributes */ -#define HPI_VOLUME_GAIN HPI_CTL_ATTR(VOLUME, 1) -#define HPI_VOLUME_AUTOFADE HPI_CTL_ATTR(VOLUME, 2) - -/** For HPI_ControlQuery() to get the number of channels of a volume control*/ -#define HPI_VOLUME_NUM_CHANNELS HPI_CTL_ATTR(VOLUME, 6) -#define HPI_VOLUME_RANGE HPI_CTL_ATTR(VOLUME, 10) - -/** Level Control attributes */ -#define HPI_LEVEL_GAIN HPI_CTL_ATTR(LEVEL, 1) -#define HPI_LEVEL_RANGE HPI_CTL_ATTR(LEVEL, 10) - -/* Meter Control attributes */ -/** return RMS signal level */ -#define HPI_METER_RMS HPI_CTL_ATTR(METER, 1) -/** return peak signal level */ -#define HPI_METER_PEAK HPI_CTL_ATTR(METER, 2) -/** ballistics for ALL rms meters on adapter */ -#define HPI_METER_RMS_BALLISTICS HPI_CTL_ATTR(METER, 3) -/** ballistics for ALL peak meters on adapter */ -#define HPI_METER_PEAK_BALLISTICS HPI_CTL_ATTR(METER, 4) - -/** For HPI_ControlQuery() to get the number of channels of a meter control*/ -#define HPI_METER_NUM_CHANNELS HPI_CTL_ATTR(METER, 5) - -/* Multiplexer control attributes */ -#define HPI_MULTIPLEXER_SOURCE HPI_CTL_ATTR(MULTIPLEXER, 1) -#define HPI_MULTIPLEXER_QUERYSOURCE HPI_CTL_ATTR(MULTIPLEXER, 2) - -/** AES/EBU transmitter control attributes */ -/** AESEBU or SPDIF */ -#define HPI_AESEBUTX_FORMAT HPI_CTL_ATTR(AESEBUTX, 1) -#define HPI_AESEBUTX_SAMPLERATE HPI_CTL_ATTR(AESEBUTX, 3) -#define HPI_AESEBUTX_CHANNELSTATUS HPI_CTL_ATTR(AESEBUTX, 4) -#define HPI_AESEBUTX_USERDATA HPI_CTL_ATTR(AESEBUTX, 5) - -/** AES/EBU receiver control attributes */ -#define HPI_AESEBURX_FORMAT HPI_CTL_ATTR(AESEBURX, 1) -#define HPI_AESEBURX_ERRORSTATUS HPI_CTL_ATTR(AESEBURX, 2) -#define HPI_AESEBURX_SAMPLERATE HPI_CTL_ATTR(AESEBURX, 3) -#define HPI_AESEBURX_CHANNELSTATUS HPI_CTL_ATTR(AESEBURX, 4) -#define HPI_AESEBURX_USERDATA HPI_CTL_ATTR(AESEBURX, 5) - -/** \defgroup tuner_defs Tuners -\{ -*/ -/** \defgroup tuner_attrs Tuner control attributes -\{ -*/ -#define HPI_TUNER_BAND HPI_CTL_ATTR(TUNER, 1) -#define HPI_TUNER_FREQ HPI_CTL_ATTR(TUNER, 2) -#define HPI_TUNER_LEVEL HPI_CTL_ATTR(TUNER, 3) -#define HPI_TUNER_AUDIOMUTE HPI_CTL_ATTR(TUNER, 4) -/* use TUNER_STATUS instead */ -#define HPI_TUNER_VIDEO_STATUS HPI_CTL_ATTR(TUNER, 5) -#define HPI_TUNER_GAIN HPI_CTL_ATTR(TUNER, 6) -#define HPI_TUNER_STATUS HPI_CTL_ATTR(TUNER, 7) -#define HPI_TUNER_MODE HPI_CTL_ATTR(TUNER, 8) -/** RDS data. */ -#define HPI_TUNER_RDS HPI_CTL_ATTR(TUNER, 9) -/** Audio pre-emphasis. */ -#define HPI_TUNER_DEEMPHASIS HPI_CTL_ATTR(TUNER, 10) -/** HD Radio tuner program control. */ -#define HPI_TUNER_PROGRAM HPI_CTL_ATTR(TUNER, 11) -/** HD Radio tuner digital signal quality. */ -#define HPI_TUNER_HDRADIO_SIGNAL_QUALITY HPI_CTL_ATTR(TUNER, 12) -/** HD Radio SDK firmware version. */ -#define HPI_TUNER_HDRADIO_SDK_VERSION HPI_CTL_ATTR(TUNER, 13) -/** HD Radio DSP firmware version. */ -#define HPI_TUNER_HDRADIO_DSP_VERSION HPI_CTL_ATTR(TUNER, 14) -/** HD Radio signal blend (force analog, or automatic). */ -#define HPI_TUNER_HDRADIO_BLEND HPI_CTL_ATTR(TUNER, 15) - -/** \} */ - -/** \defgroup pads_attrs Tuner PADs control attributes -\{ -*/ -/** The text string containing the station/channel combination. */ -#define HPI_PAD_CHANNEL_NAME HPI_CTL_ATTR(PAD, 1) -/** The text string containing the artist. */ -#define HPI_PAD_ARTIST HPI_CTL_ATTR(PAD, 2) -/** The text string containing the title. */ -#define HPI_PAD_TITLE HPI_CTL_ATTR(PAD, 3) -/** The text string containing the comment. */ -#define HPI_PAD_COMMENT HPI_CTL_ATTR(PAD, 4) -/** The integer containing the PTY code. */ -#define HPI_PAD_PROGRAM_TYPE HPI_CTL_ATTR(PAD, 5) -/** The integer containing the program identification. */ -#define HPI_PAD_PROGRAM_ID HPI_CTL_ATTR(PAD, 6) -/** The integer containing whether traffic information is supported. -Contains either 1 or 0. */ -#define HPI_PAD_TA_SUPPORT HPI_CTL_ATTR(PAD, 7) -/** The integer containing whether traffic announcement is in progress. -Contains either 1 or 0. */ -#define HPI_PAD_TA_ACTIVE HPI_CTL_ATTR(PAD, 8) -/** \} */ -/** \} */ - -/* VOX control attributes */ -#define HPI_VOX_THRESHOLD HPI_CTL_ATTR(VOX, 1) - -/*?? channel mode used hpi_multiplexer_source attribute == 1 */ -#define HPI_CHANNEL_MODE_MODE HPI_CTL_ATTR(CHANNEL_MODE, 1) - -/** \defgroup channel_modes Channel Modes -Used for HPI_ChannelModeSet/Get() -\{ + +/** Unique identifiers for every control attribute */ -/** Left channel out = left channel in, Right channel out = right channel in. */ -#define HPI_CHANNEL_MODE_NORMAL 1 -/** Left channel out = right channel in, Right channel out = left channel in. */ -#define HPI_CHANNEL_MODE_SWAP 2 -/** Left channel out = left channel in, Right channel out = left channel in. */ -#define HPI_CHANNEL_MODE_LEFT_TO_STEREO 3 -/** Left channel out = right channel in, Right channel out = right channel in.*/ -#define HPI_CHANNEL_MODE_RIGHT_TO_STEREO 4 -/** Left channel out = (left channel in + right channel in)/2, - Right channel out = mute. */ -#define HPI_CHANNEL_MODE_STEREO_TO_LEFT 5 -/** Left channel out = mute, - Right channel out = (right channel in + left channel in)/2. */ -#define HPI_CHANNEL_MODE_STEREO_TO_RIGHT 6 -#define HPI_CHANNEL_MODE_LAST 6 -/** \} */ - -/* Bitstream control set attributes */ -#define HPI_BITSTREAM_DATA_POLARITY HPI_CTL_ATTR(BITSTREAM, 1) -#define HPI_BITSTREAM_CLOCK_EDGE HPI_CTL_ATTR(BITSTREAM, 2) -#define HPI_BITSTREAM_CLOCK_SOURCE HPI_CTL_ATTR(BITSTREAM, 3) +enum HPI_CONTROL_ATTRIBUTES { + HPI_GENERIC_ENABLE = HPI_CTL_ATTR(GENERIC, 1), + HPI_GENERIC_EVENT_ENABLE = HPI_CTL_ATTR(GENERIC, 2), + + HPI_VOLUME_GAIN = HPI_CTL_ATTR(VOLUME, 1), + HPI_VOLUME_AUTOFADE = HPI_CTL_ATTR(VOLUME, 2), + HPI_VOLUME_MUTE = HPI_CTL_ATTR(VOLUME, 3), + HPI_VOLUME_GAIN_AND_FLAGS = HPI_CTL_ATTR(VOLUME, 4), + HPI_VOLUME_NUM_CHANNELS = HPI_CTL_ATTR(VOLUME, 6), + HPI_VOLUME_RANGE = HPI_CTL_ATTR(VOLUME, 10), + + HPI_METER_RMS = HPI_CTL_ATTR(METER, 1), + HPI_METER_PEAK = HPI_CTL_ATTR(METER, 2), + HPI_METER_RMS_BALLISTICS = HPI_CTL_ATTR(METER, 3), + HPI_METER_PEAK_BALLISTICS = HPI_CTL_ATTR(METER, 4), + HPI_METER_NUM_CHANNELS = HPI_CTL_ATTR(METER, 5), + + HPI_MULTIPLEXER_SOURCE = HPI_CTL_ATTR(MULTIPLEXER, 1), + HPI_MULTIPLEXER_QUERYSOURCE = HPI_CTL_ATTR(MULTIPLEXER, 2), + + HPI_AESEBUTX_FORMAT = HPI_CTL_ATTR(AESEBUTX, 1), + HPI_AESEBUTX_SAMPLERATE = HPI_CTL_ATTR(AESEBUTX, 3), + HPI_AESEBUTX_CHANNELSTATUS = HPI_CTL_ATTR(AESEBUTX, 4), + HPI_AESEBUTX_USERDATA = HPI_CTL_ATTR(AESEBUTX, 5), + + HPI_AESEBURX_FORMAT = HPI_CTL_ATTR(AESEBURX, 1), + HPI_AESEBURX_ERRORSTATUS = HPI_CTL_ATTR(AESEBURX, 2), + HPI_AESEBURX_SAMPLERATE = HPI_CTL_ATTR(AESEBURX, 3), + HPI_AESEBURX_CHANNELSTATUS = HPI_CTL_ATTR(AESEBURX, 4), + HPI_AESEBURX_USERDATA = HPI_CTL_ATTR(AESEBURX, 5), + + HPI_LEVEL_GAIN = HPI_CTL_ATTR(LEVEL, 1), + HPI_LEVEL_RANGE = HPI_CTL_ATTR(LEVEL, 10), + + HPI_TUNER_BAND = HPI_CTL_ATTR(TUNER, 1), + HPI_TUNER_FREQ = HPI_CTL_ATTR(TUNER, 2), + HPI_TUNER_LEVEL_AVG = HPI_CTL_ATTR(TUNER, 3), + HPI_TUNER_LEVEL_RAW = HPI_CTL_ATTR(TUNER, 4), + HPI_TUNER_SNR = HPI_CTL_ATTR(TUNER, 5), + HPI_TUNER_GAIN = HPI_CTL_ATTR(TUNER, 6), + HPI_TUNER_STATUS = HPI_CTL_ATTR(TUNER, 7), + HPI_TUNER_MODE = HPI_CTL_ATTR(TUNER, 8), + HPI_TUNER_RDS = HPI_CTL_ATTR(TUNER, 9), + HPI_TUNER_DEEMPHASIS = HPI_CTL_ATTR(TUNER, 10), + HPI_TUNER_PROGRAM = HPI_CTL_ATTR(TUNER, 11), + HPI_TUNER_HDRADIO_SIGNAL_QUALITY = HPI_CTL_ATTR(TUNER, 12), + HPI_TUNER_HDRADIO_SDK_VERSION = HPI_CTL_ATTR(TUNER, 13), + HPI_TUNER_HDRADIO_DSP_VERSION = HPI_CTL_ATTR(TUNER, 14), + HPI_TUNER_HDRADIO_BLEND = HPI_CTL_ATTR(TUNER, 15), + + HPI_VOX_THRESHOLD = HPI_CTL_ATTR(VOX, 1), + + HPI_CHANNEL_MODE_MODE = HPI_CTL_ATTR(CHANNEL_MODE, 1), + + HPI_BITSTREAM_DATA_POLARITY = HPI_CTL_ATTR(BITSTREAM, 1), + HPI_BITSTREAM_CLOCK_EDGE = HPI_CTL_ATTR(BITSTREAM, 2), + HPI_BITSTREAM_CLOCK_SOURCE = HPI_CTL_ATTR(BITSTREAM, 3), + HPI_BITSTREAM_ACTIVITY = HPI_CTL_ATTR(BITSTREAM, 4), + + HPI_SAMPLECLOCK_SOURCE = HPI_CTL_ATTR(SAMPLECLOCK, 1), + HPI_SAMPLECLOCK_SAMPLERATE = HPI_CTL_ATTR(SAMPLECLOCK, 2), + HPI_SAMPLECLOCK_SOURCE_INDEX = HPI_CTL_ATTR(SAMPLECLOCK, 3), + HPI_SAMPLECLOCK_LOCAL_SAMPLERATE = HPI_CTL_ATTR(SAMPLECLOCK, 4), + HPI_SAMPLECLOCK_AUTO = HPI_CTL_ATTR(SAMPLECLOCK, 5), + HPI_SAMPLECLOCK_LOCAL_LOCK = HPI_CTL_ATTR(SAMPLECLOCK, 6), + + HPI_MICROPHONE_PHANTOM_POWER = HPI_CTL_ATTR(MICROPHONE, 1), + + HPI_EQUALIZER_NUM_FILTERS = HPI_CTL_ATTR(EQUALIZER, 1), + HPI_EQUALIZER_FILTER = HPI_CTL_ATTR(EQUALIZER, 2), + HPI_EQUALIZER_COEFFICIENTS = HPI_CTL_ATTR(EQUALIZER, 3), + + HPI_COMPANDER_PARAMS = HPI_CTL_ATTR(COMPANDER, 1), + HPI_COMPANDER_MAKEUPGAIN = HPI_CTL_ATTR(COMPANDER, 2), + HPI_COMPANDER_THRESHOLD = HPI_CTL_ATTR(COMPANDER, 3), + HPI_COMPANDER_RATIO = HPI_CTL_ATTR(COMPANDER, 4), + HPI_COMPANDER_ATTACK = HPI_CTL_ATTR(COMPANDER, 5), + HPI_COMPANDER_DECAY = HPI_CTL_ATTR(COMPANDER, 6), + + HPI_COBRANET_SET = HPI_CTL_ATTR(COBRANET, 1), + HPI_COBRANET_GET = HPI_CTL_ATTR(COBRANET, 2), + HPI_COBRANET_SET_DATA = HPI_CTL_ATTR(COBRANET, 3), + HPI_COBRANET_GET_DATA = HPI_CTL_ATTR(COBRANET, 4), + HPI_COBRANET_GET_STATUS = HPI_CTL_ATTR(COBRANET, 5), + HPI_COBRANET_SEND_PACKET = HPI_CTL_ATTR(COBRANET, 6), + HPI_COBRANET_GET_PACKET = HPI_CTL_ATTR(COBRANET, 7), + + HPI_TONEDETECTOR_THRESHOLD = HPI_CTL_ATTR(TONEDETECTOR, 1), + HPI_TONEDETECTOR_STATE = HPI_CTL_ATTR(TONEDETECTOR, 2), + HPI_TONEDETECTOR_FREQUENCY = HPI_CTL_ATTR(TONEDETECTOR, 3), + + HPI_SILENCEDETECTOR_THRESHOLD = HPI_CTL_ATTR(SILENCEDETECTOR, 1), + HPI_SILENCEDETECTOR_STATE = HPI_CTL_ATTR(SILENCEDETECTOR, 2), + HPI_SILENCEDETECTOR_DELAY = HPI_CTL_ATTR(SILENCEDETECTOR, 3), + + HPI_PAD_CHANNEL_NAME = HPI_CTL_ATTR(PAD, 1), + HPI_PAD_ARTIST = HPI_CTL_ATTR(PAD, 2), + HPI_PAD_TITLE = HPI_CTL_ATTR(PAD, 3), + HPI_PAD_COMMENT = HPI_CTL_ATTR(PAD, 4), + HPI_PAD_PROGRAM_TYPE = HPI_CTL_ATTR(PAD, 5), + HPI_PAD_PROGRAM_ID = HPI_CTL_ATTR(PAD, 6), + HPI_PAD_TA_SUPPORT = HPI_CTL_ATTR(PAD, 7), + HPI_PAD_TA_ACTIVE = HPI_CTL_ATTR(PAD, 8) +}; #define HPI_POLARITY_POSITIVE 0 #define HPI_POLARITY_NEGATIVE 1 -/* Bitstream control get attributes */ -#define HPI_BITSTREAM_ACTIVITY 1 - -/* SampleClock control attributes */ -#define HPI_SAMPLECLOCK_SOURCE HPI_CTL_ATTR(SAMPLECLOCK, 1) -#define HPI_SAMPLECLOCK_SAMPLERATE HPI_CTL_ATTR(SAMPLECLOCK, 2) -#define HPI_SAMPLECLOCK_SOURCE_INDEX HPI_CTL_ATTR(SAMPLECLOCK, 3) -#define HPI_SAMPLECLOCK_LOCAL_SAMPLERATE\ - HPI_CTL_ATTR(SAMPLECLOCK, 4) -#define HPI_SAMPLECLOCK_AUTO HPI_CTL_ATTR(SAMPLECLOCK, 5) -#define HPI_SAMPLECLOCK_LOCAL_LOCK HPI_CTL_ATTR(SAMPLECLOCK, 6) - -/* Microphone control attributes */ -#define HPI_MICROPHONE_PHANTOM_POWER HPI_CTL_ATTR(MICROPHONE, 1) - -/** Equalizer control attributes */ -/** Used to get number of filters in an EQ. (Can't set) */ -#define HPI_EQUALIZER_NUM_FILTERS HPI_CTL_ATTR(EQUALIZER, 1) -/** Set/get the filter by type, freq, Q, gain */ -#define HPI_EQUALIZER_FILTER HPI_CTL_ATTR(EQUALIZER, 2) -/** Get the biquad coefficients */ -#define HPI_EQUALIZER_COEFFICIENTS HPI_CTL_ATTR(EQUALIZER, 3) - -/* Note compander also uses HPI_GENERIC_ENABLE */ -#define HPI_COMPANDER_PARAMS HPI_CTL_ATTR(COMPANDER, 1) -#define HPI_COMPANDER_MAKEUPGAIN HPI_CTL_ATTR(COMPANDER, 2) -#define HPI_COMPANDER_THRESHOLD HPI_CTL_ATTR(COMPANDER, 3) -#define HPI_COMPANDER_RATIO HPI_CTL_ATTR(COMPANDER, 4) -#define HPI_COMPANDER_ATTACK HPI_CTL_ATTR(COMPANDER, 5) -#define HPI_COMPANDER_DECAY HPI_CTL_ATTR(COMPANDER, 6) - -/* Cobranet control attributes. */ -#define HPI_COBRANET_SET HPI_CTL_ATTR(COBRANET, 1) -#define HPI_COBRANET_GET HPI_CTL_ATTR(COBRANET, 2) -#define HPI_COBRANET_SET_DATA HPI_CTL_ATTR(COBRANET, 3) -#define HPI_COBRANET_GET_DATA HPI_CTL_ATTR(COBRANET, 4) -#define HPI_COBRANET_GET_STATUS HPI_CTL_ATTR(COBRANET, 5) -#define HPI_COBRANET_SEND_PACKET HPI_CTL_ATTR(COBRANET, 6) -#define HPI_COBRANET_GET_PACKET HPI_CTL_ATTR(COBRANET, 7) - /*------------------------------------------------------------ Cobranet Chip Bridge - copied from HMI.H ------------------------------------------------------------*/ @@ -395,69 +309,22 @@ Used for HPI_ChannelModeSet/Get() #define HPI_ETHERNET_UDP_PORT (44600) /*!< UDP messaging port */ -/** Base network time out is set to 100 milli-seconds. */ -#define HPI_ETHERNET_TIMEOUT_MS (100) - -/** \defgroup tonedet_attr Tonedetector attributes -\{ -Used by HPI_ToneDetector_Set() and HPI_ToneDetector_Get() -*/ - -/** Set the threshold level of a tonedetector, -Threshold is a -ve number in units of dB/100, -*/ -#define HPI_TONEDETECTOR_THRESHOLD HPI_CTL_ATTR(TONEDETECTOR, 1) - -/** Get the current state of tonedetection -The result is a bitmap of detected tones. pairs of bits represent the left -and right channels, with left channel in LSB. -The lowest frequency detector state is in the LSB -*/ -#define HPI_TONEDETECTOR_STATE HPI_CTL_ATTR(TONEDETECTOR, 2) - -/** Get the frequency of a tonedetector band. -*/ -#define HPI_TONEDETECTOR_FREQUENCY HPI_CTL_ATTR(TONEDETECTOR, 3) - -/**\}*/ +/** Default network timeout in milli-seconds. */ +#define HPI_ETHERNET_TIMEOUT_MS 500 -/** \defgroup silencedet_attr SilenceDetector attributes -\{ -*/ - -/** Get the current state of tonedetection -The result is a bitmap with 1s for silent channels. Left channel is in LSB -*/ -#define HPI_SILENCEDETECTOR_STATE \ - HPI_CTL_ATTR(SILENCEDETECTOR, 2) - -/** Set the threshold level of a SilenceDetector, -Threshold is a -ve number in units of dB/100, -*/ -#define HPI_SILENCEDETECTOR_THRESHOLD \ - HPI_CTL_ATTR(SILENCEDETECTOR, 1) - -/** get/set the silence time before the detector triggers -*/ -#define HPI_SILENCEDETECTOR_DELAY \ - HPI_CTL_ATTR(SILENCEDETECTOR, 3) - -/**\}*/ - -/* Locked memory buffer alloc/free phases */ -/** use one message to allocate or free physical memory */ -#define HPI_BUFFER_CMD_EXTERNAL 0 -/** alloc physical memory */ -#define HPI_BUFFER_CMD_INTERNAL_ALLOC 1 -/** send physical memory address to adapter */ -#define HPI_BUFFER_CMD_INTERNAL_GRANTADAPTER 2 -/** notify adapter to stop using physical buffer */ -#define HPI_BUFFER_CMD_INTERNAL_REVOKEADAPTER 3 -/** free physical buffer */ -#define HPI_BUFFER_CMD_INTERNAL_FREE 4 - -/******************************************* CONTROLX ATTRIBUTES ****/ -/* NOTE: All controlx attributes must be unique, unlike control attributes */ +/** Locked memory buffer alloc/free phases */ +enum HPI_BUFFER_CMDS { + /** use one message to allocate or free physical memory */ + HPI_BUFFER_CMD_EXTERNAL = 0, + /** alloc physical memory */ + HPI_BUFFER_CMD_INTERNAL_ALLOC = 1, + /** send physical memory address to adapter */ + HPI_BUFFER_CMD_INTERNAL_GRANTADAPTER = 2, + /** notify adapter to stop using physical buffer */ + HPI_BUFFER_CMD_INTERNAL_REVOKEADAPTER = 3, + /** free physical buffer */ + HPI_BUFFER_CMD_INTERNAL_FREE = 4 +}; /*****************************************************************************/ /*****************************************************************************/ @@ -482,6 +349,12 @@ Threshold is a -ve number in units of dB/100, #define HPI_USB_W2K_TAG 0x57495341 /* "ASIW" */ #define HPI_USB_LINUX_TAG 0x4C495341 /* "ASIL" */ +/** Invalid Adapter index +Used in HPI messages that are not addressed to a specific adapter +Used in DLL to indicate device not present +*/ +#define HPI_ADAPTER_INDEX_INVALID 0xFFFF + /** First 2 hex digits define the adapter family */ #define HPI_ADAPTER_FAMILY_MASK 0xff00 #define HPI_MODULE_FAMILY_MASK 0xfff0 @@ -490,178 +363,185 @@ Threshold is a -ve number in units of dB/100, #define HPI_MODULE_FAMILY_ASI(f) (f & HPI_MODULE_FAMILY_MASK) #define HPI_ADAPTER_ASI(f) (f) -/******************************************* message types */ -#define HPI_TYPE_MESSAGE 1 -#define HPI_TYPE_RESPONSE 2 -#define HPI_TYPE_DATA 3 -#define HPI_TYPE_SSX2BYPASS_MESSAGE 4 - -/******************************************* object types */ -#define HPI_OBJ_SUBSYSTEM 1 -#define HPI_OBJ_ADAPTER 2 -#define HPI_OBJ_OSTREAM 3 -#define HPI_OBJ_ISTREAM 4 -#define HPI_OBJ_MIXER 5 -#define HPI_OBJ_NODE 6 -#define HPI_OBJ_CONTROL 7 -#define HPI_OBJ_NVMEMORY 8 -#define HPI_OBJ_GPIO 9 -#define HPI_OBJ_WATCHDOG 10 -#define HPI_OBJ_CLOCK 11 -#define HPI_OBJ_PROFILE 12 -#define HPI_OBJ_CONTROLEX 13 -#define HPI_OBJ_ASYNCEVENT 14 - -#define HPI_OBJ_MAXINDEX 14 - -/******************************************* methods/functions */ - -#define HPI_OBJ_FUNCTION_SPACING 0x100 -#define HPI_MAKE_INDEX(obj, index) (obj * HPI_OBJ_FUNCTION_SPACING + index) +enum HPI_MESSAGE_TYPES { + HPI_TYPE_MESSAGE = 1, + HPI_TYPE_RESPONSE = 2, + HPI_TYPE_DATA = 3, + HPI_TYPE_SSX2BYPASS_MESSAGE = 4 +}; + +enum HPI_OBJECT_TYPES { + HPI_OBJ_SUBSYSTEM = 1, + HPI_OBJ_ADAPTER = 2, + HPI_OBJ_OSTREAM = 3, + HPI_OBJ_ISTREAM = 4, + HPI_OBJ_MIXER = 5, + HPI_OBJ_NODE = 6, + HPI_OBJ_CONTROL = 7, + HPI_OBJ_NVMEMORY = 8, + HPI_OBJ_GPIO = 9, + HPI_OBJ_WATCHDOG = 10, + HPI_OBJ_CLOCK = 11, + HPI_OBJ_PROFILE = 12, + HPI_OBJ_CONTROLEX = 13, + HPI_OBJ_ASYNCEVENT = 14 +#define HPI_OBJ_MAXINDEX 14 +}; + +#define HPI_OBJ_FUNCTION_SPACING 0x100 +#define HPI_FUNC_ID(obj, i) (HPI_OBJ_##obj * HPI_OBJ_FUNCTION_SPACING + i) + #define HPI_EXTRACT_INDEX(fn) (fn & 0xff) -/* SUB-SYSTEM */ -#define HPI_SUBSYS_OPEN HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 1) -#define HPI_SUBSYS_GET_VERSION HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 2) -#define HPI_SUBSYS_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 3) -#define HPI_SUBSYS_FIND_ADAPTERS HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 4) -#define HPI_SUBSYS_CREATE_ADAPTER HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 5) -#define HPI_SUBSYS_CLOSE HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 6) -#define HPI_SUBSYS_DELETE_ADAPTER HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 7) -#define HPI_SUBSYS_DRIVER_LOAD HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 8) -#define HPI_SUBSYS_DRIVER_UNLOAD HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 9) -#define HPI_SUBSYS_READ_PORT_8 HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 10) -#define HPI_SUBSYS_WRITE_PORT_8 HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 11) -#define HPI_SUBSYS_GET_NUM_ADAPTERS HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 12) -#define HPI_SUBSYS_GET_ADAPTER HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 13) -#define HPI_SUBSYS_SET_NETWORK_INTERFACE HPI_MAKE_INDEX(HPI_OBJ_SUBSYSTEM, 14) -#define HPI_SUBSYS_FUNCTION_COUNT 14 -/* ADAPTER */ -#define HPI_ADAPTER_OPEN HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 1) -#define HPI_ADAPTER_CLOSE HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 2) -#define HPI_ADAPTER_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 3) -#define HPI_ADAPTER_GET_ASSERT HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 4) -#define HPI_ADAPTER_TEST_ASSERT HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 5) -#define HPI_ADAPTER_SET_MODE HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 6) -#define HPI_ADAPTER_GET_MODE HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 7) -#define HPI_ADAPTER_ENABLE_CAPABILITY HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 8) -#define HPI_ADAPTER_SELFTEST HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 9) -#define HPI_ADAPTER_FIND_OBJECT HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 10) -#define HPI_ADAPTER_QUERY_FLASH HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 11) -#define HPI_ADAPTER_START_FLASH HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 12) -#define HPI_ADAPTER_PROGRAM_FLASH HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 13) -#define HPI_ADAPTER_SET_PROPERTY HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 14) -#define HPI_ADAPTER_GET_PROPERTY HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 15) -#define HPI_ADAPTER_ENUM_PROPERTY HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 16) -#define HPI_ADAPTER_MODULE_INFO HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 17) -#define HPI_ADAPTER_DEBUG_READ HPI_MAKE_INDEX(HPI_OBJ_ADAPTER, 18) -#define HPI_ADAPTER_FUNCTION_COUNT 18 -/* OUTPUT STREAM */ -#define HPI_OSTREAM_OPEN HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 1) -#define HPI_OSTREAM_CLOSE HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 2) -#define HPI_OSTREAM_WRITE HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 3) -#define HPI_OSTREAM_START HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 4) -#define HPI_OSTREAM_STOP HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 5) -#define HPI_OSTREAM_RESET HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 6) -#define HPI_OSTREAM_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 7) -#define HPI_OSTREAM_QUERY_FORMAT HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 8) -#define HPI_OSTREAM_DATA HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 9) -#define HPI_OSTREAM_SET_VELOCITY HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 10) -#define HPI_OSTREAM_SET_PUNCHINOUT HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 11) -#define HPI_OSTREAM_SINEGEN HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 12) -#define HPI_OSTREAM_ANC_RESET HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 13) -#define HPI_OSTREAM_ANC_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 14) -#define HPI_OSTREAM_ANC_READ HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 15) -#define HPI_OSTREAM_SET_TIMESCALE HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 16) -#define HPI_OSTREAM_SET_FORMAT HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 17) -#define HPI_OSTREAM_HOSTBUFFER_ALLOC HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 18) -#define HPI_OSTREAM_HOSTBUFFER_FREE HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 19) -#define HPI_OSTREAM_GROUP_ADD HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 20) -#define HPI_OSTREAM_GROUP_GETMAP HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 21) -#define HPI_OSTREAM_GROUP_RESET HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 22) -#define HPI_OSTREAM_HOSTBUFFER_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 23) -#define HPI_OSTREAM_WAIT_START HPI_MAKE_INDEX(HPI_OBJ_OSTREAM, 24) -#define HPI_OSTREAM_FUNCTION_COUNT 24 -/* INPUT STREAM */ -#define HPI_ISTREAM_OPEN HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 1) -#define HPI_ISTREAM_CLOSE HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 2) -#define HPI_ISTREAM_SET_FORMAT HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 3) -#define HPI_ISTREAM_READ HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 4) -#define HPI_ISTREAM_START HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 5) -#define HPI_ISTREAM_STOP HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 6) -#define HPI_ISTREAM_RESET HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 7) -#define HPI_ISTREAM_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 8) -#define HPI_ISTREAM_QUERY_FORMAT HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 9) -#define HPI_ISTREAM_ANC_RESET HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 10) -#define HPI_ISTREAM_ANC_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 11) -#define HPI_ISTREAM_ANC_WRITE HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 12) -#define HPI_ISTREAM_HOSTBUFFER_ALLOC HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 13) -#define HPI_ISTREAM_HOSTBUFFER_FREE HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 14) -#define HPI_ISTREAM_GROUP_ADD HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 15) -#define HPI_ISTREAM_GROUP_GETMAP HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 16) -#define HPI_ISTREAM_GROUP_RESET HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 17) -#define HPI_ISTREAM_HOSTBUFFER_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 18) -#define HPI_ISTREAM_WAIT_START HPI_MAKE_INDEX(HPI_OBJ_ISTREAM, 19) -#define HPI_ISTREAM_FUNCTION_COUNT 19 -/* MIXER */ +enum HPI_FUNCTION_IDS { + HPI_SUBSYS_OPEN = HPI_FUNC_ID(SUBSYSTEM, 1), + HPI_SUBSYS_GET_VERSION = HPI_FUNC_ID(SUBSYSTEM, 2), + HPI_SUBSYS_GET_INFO = HPI_FUNC_ID(SUBSYSTEM, 3), + HPI_SUBSYS_FIND_ADAPTERS = HPI_FUNC_ID(SUBSYSTEM, 4), + HPI_SUBSYS_CREATE_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 5), + HPI_SUBSYS_CLOSE = HPI_FUNC_ID(SUBSYSTEM, 6), + HPI_SUBSYS_DELETE_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 7), + HPI_SUBSYS_DRIVER_LOAD = HPI_FUNC_ID(SUBSYSTEM, 8), + HPI_SUBSYS_DRIVER_UNLOAD = HPI_FUNC_ID(SUBSYSTEM, 9), + HPI_SUBSYS_READ_PORT_8 = HPI_FUNC_ID(SUBSYSTEM, 10), + HPI_SUBSYS_WRITE_PORT_8 = HPI_FUNC_ID(SUBSYSTEM, 11), + HPI_SUBSYS_GET_NUM_ADAPTERS = HPI_FUNC_ID(SUBSYSTEM, 12), + HPI_SUBSYS_GET_ADAPTER = HPI_FUNC_ID(SUBSYSTEM, 13), + HPI_SUBSYS_SET_NETWORK_INTERFACE = HPI_FUNC_ID(SUBSYSTEM, 14), + HPI_SUBSYS_OPTION_INFO = HPI_FUNC_ID(SUBSYSTEM, 15), + HPI_SUBSYS_OPTION_GET = HPI_FUNC_ID(SUBSYSTEM, 16), + HPI_SUBSYS_OPTION_SET = HPI_FUNC_ID(SUBSYSTEM, 17), +#define HPI_SUBSYS_FUNCTION_COUNT 17 + + HPI_ADAPTER_OPEN = HPI_FUNC_ID(ADAPTER, 1), + HPI_ADAPTER_CLOSE = HPI_FUNC_ID(ADAPTER, 2), + HPI_ADAPTER_GET_INFO = HPI_FUNC_ID(ADAPTER, 3), + HPI_ADAPTER_GET_ASSERT = HPI_FUNC_ID(ADAPTER, 4), + HPI_ADAPTER_TEST_ASSERT = HPI_FUNC_ID(ADAPTER, 5), + HPI_ADAPTER_SET_MODE = HPI_FUNC_ID(ADAPTER, 6), + HPI_ADAPTER_GET_MODE = HPI_FUNC_ID(ADAPTER, 7), + HPI_ADAPTER_ENABLE_CAPABILITY = HPI_FUNC_ID(ADAPTER, 8), + HPI_ADAPTER_SELFTEST = HPI_FUNC_ID(ADAPTER, 9), + HPI_ADAPTER_FIND_OBJECT = HPI_FUNC_ID(ADAPTER, 10), + HPI_ADAPTER_QUERY_FLASH = HPI_FUNC_ID(ADAPTER, 11), + HPI_ADAPTER_START_FLASH = HPI_FUNC_ID(ADAPTER, 12), + HPI_ADAPTER_PROGRAM_FLASH = HPI_FUNC_ID(ADAPTER, 13), + HPI_ADAPTER_SET_PROPERTY = HPI_FUNC_ID(ADAPTER, 14), + HPI_ADAPTER_GET_PROPERTY = HPI_FUNC_ID(ADAPTER, 15), + HPI_ADAPTER_ENUM_PROPERTY = HPI_FUNC_ID(ADAPTER, 16), + HPI_ADAPTER_MODULE_INFO = HPI_FUNC_ID(ADAPTER, 17), + HPI_ADAPTER_DEBUG_READ = HPI_FUNC_ID(ADAPTER, 18), + HPI_ADAPTER_IRQ_QUERY_AND_CLEAR = HPI_FUNC_ID(ADAPTER, 19), + HPI_ADAPTER_IRQ_CALLBACK = HPI_FUNC_ID(ADAPTER, 20), +#define HPI_ADAPTER_FUNCTION_COUNT 20 + + HPI_OSTREAM_OPEN = HPI_FUNC_ID(OSTREAM, 1), + HPI_OSTREAM_CLOSE = HPI_FUNC_ID(OSTREAM, 2), + HPI_OSTREAM_WRITE = HPI_FUNC_ID(OSTREAM, 3), + HPI_OSTREAM_START = HPI_FUNC_ID(OSTREAM, 4), + HPI_OSTREAM_STOP = HPI_FUNC_ID(OSTREAM, 5), + HPI_OSTREAM_RESET = HPI_FUNC_ID(OSTREAM, 6), + HPI_OSTREAM_GET_INFO = HPI_FUNC_ID(OSTREAM, 7), + HPI_OSTREAM_QUERY_FORMAT = HPI_FUNC_ID(OSTREAM, 8), + HPI_OSTREAM_DATA = HPI_FUNC_ID(OSTREAM, 9), + HPI_OSTREAM_SET_VELOCITY = HPI_FUNC_ID(OSTREAM, 10), + HPI_OSTREAM_SET_PUNCHINOUT = HPI_FUNC_ID(OSTREAM, 11), + HPI_OSTREAM_SINEGEN = HPI_FUNC_ID(OSTREAM, 12), + HPI_OSTREAM_ANC_RESET = HPI_FUNC_ID(OSTREAM, 13), + HPI_OSTREAM_ANC_GET_INFO = HPI_FUNC_ID(OSTREAM, 14), + HPI_OSTREAM_ANC_READ = HPI_FUNC_ID(OSTREAM, 15), + HPI_OSTREAM_SET_TIMESCALE = HPI_FUNC_ID(OSTREAM, 16), + HPI_OSTREAM_SET_FORMAT = HPI_FUNC_ID(OSTREAM, 17), + HPI_OSTREAM_HOSTBUFFER_ALLOC = HPI_FUNC_ID(OSTREAM, 18), + HPI_OSTREAM_HOSTBUFFER_FREE = HPI_FUNC_ID(OSTREAM, 19), + HPI_OSTREAM_GROUP_ADD = HPI_FUNC_ID(OSTREAM, 20), + HPI_OSTREAM_GROUP_GETMAP = HPI_FUNC_ID(OSTREAM, 21), + HPI_OSTREAM_GROUP_RESET = HPI_FUNC_ID(OSTREAM, 22), + HPI_OSTREAM_HOSTBUFFER_GET_INFO = HPI_FUNC_ID(OSTREAM, 23), + HPI_OSTREAM_WAIT_START = HPI_FUNC_ID(OSTREAM, 24), + HPI_OSTREAM_WAIT = HPI_FUNC_ID(OSTREAM, 25), +#define HPI_OSTREAM_FUNCTION_COUNT 25 + + HPI_ISTREAM_OPEN = HPI_FUNC_ID(ISTREAM, 1), + HPI_ISTREAM_CLOSE = HPI_FUNC_ID(ISTREAM, 2), + HPI_ISTREAM_SET_FORMAT = HPI_FUNC_ID(ISTREAM, 3), + HPI_ISTREAM_READ = HPI_FUNC_ID(ISTREAM, 4), + HPI_ISTREAM_START = HPI_FUNC_ID(ISTREAM, 5), + HPI_ISTREAM_STOP = HPI_FUNC_ID(ISTREAM, 6), + HPI_ISTREAM_RESET = HPI_FUNC_ID(ISTREAM, 7), + HPI_ISTREAM_GET_INFO = HPI_FUNC_ID(ISTREAM, 8), + HPI_ISTREAM_QUERY_FORMAT = HPI_FUNC_ID(ISTREAM, 9), + HPI_ISTREAM_ANC_RESET = HPI_FUNC_ID(ISTREAM, 10), + HPI_ISTREAM_ANC_GET_INFO = HPI_FUNC_ID(ISTREAM, 11), + HPI_ISTREAM_ANC_WRITE = HPI_FUNC_ID(ISTREAM, 12), + HPI_ISTREAM_HOSTBUFFER_ALLOC = HPI_FUNC_ID(ISTREAM, 13), + HPI_ISTREAM_HOSTBUFFER_FREE = HPI_FUNC_ID(ISTREAM, 14), + HPI_ISTREAM_GROUP_ADD = HPI_FUNC_ID(ISTREAM, 15), + HPI_ISTREAM_GROUP_GETMAP = HPI_FUNC_ID(ISTREAM, 16), + HPI_ISTREAM_GROUP_RESET = HPI_FUNC_ID(ISTREAM, 17), + HPI_ISTREAM_HOSTBUFFER_GET_INFO = HPI_FUNC_ID(ISTREAM, 18), + HPI_ISTREAM_WAIT_START = HPI_FUNC_ID(ISTREAM, 19), + HPI_ISTREAM_WAIT = HPI_FUNC_ID(ISTREAM, 20), +#define HPI_ISTREAM_FUNCTION_COUNT 20 + /* NOTE: GET_NODE_INFO, SET_CONNECTION, GET_CONNECTIONS are not currently used */ -#define HPI_MIXER_OPEN HPI_MAKE_INDEX(HPI_OBJ_MIXER, 1) -#define HPI_MIXER_CLOSE HPI_MAKE_INDEX(HPI_OBJ_MIXER, 2) -#define HPI_MIXER_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_MIXER, 3) -#define HPI_MIXER_GET_NODE_INFO HPI_MAKE_INDEX(HPI_OBJ_MIXER, 4) -#define HPI_MIXER_GET_CONTROL HPI_MAKE_INDEX(HPI_OBJ_MIXER, 5) -#define HPI_MIXER_SET_CONNECTION HPI_MAKE_INDEX(HPI_OBJ_MIXER, 6) -#define HPI_MIXER_GET_CONNECTIONS HPI_MAKE_INDEX(HPI_OBJ_MIXER, 7) -#define HPI_MIXER_GET_CONTROL_BY_INDEX HPI_MAKE_INDEX(HPI_OBJ_MIXER, 8) -#define HPI_MIXER_GET_CONTROL_ARRAY_BY_INDEX HPI_MAKE_INDEX(HPI_OBJ_MIXER, 9) -#define HPI_MIXER_GET_CONTROL_MULTIPLE_VALUES HPI_MAKE_INDEX(HPI_OBJ_MIXER, 10) -#define HPI_MIXER_STORE HPI_MAKE_INDEX(HPI_OBJ_MIXER, 11) -#define HPI_MIXER_FUNCTION_COUNT 11 -/* MIXER CONTROLS */ -#define HPI_CONTROL_GET_INFO HPI_MAKE_INDEX(HPI_OBJ_CONTROL, 1) -#define HPI_CONTROL_GET_STATE HPI_MAKE_INDEX(HPI_OBJ_CONTROL, 2) -#define HPI_CONTROL_SET_STATE HPI_MAKE_INDEX(HPI_OBJ_CONTROL, 3) + HPI_MIXER_OPEN = HPI_FUNC_ID(MIXER, 1), + HPI_MIXER_CLOSE = HPI_FUNC_ID(MIXER, 2), + HPI_MIXER_GET_INFO = HPI_FUNC_ID(MIXER, 3), + HPI_MIXER_GET_NODE_INFO = HPI_FUNC_ID(MIXER, 4), + HPI_MIXER_GET_CONTROL = HPI_FUNC_ID(MIXER, 5), + HPI_MIXER_SET_CONNECTION = HPI_FUNC_ID(MIXER, 6), + HPI_MIXER_GET_CONNECTIONS = HPI_FUNC_ID(MIXER, 7), + HPI_MIXER_GET_CONTROL_BY_INDEX = HPI_FUNC_ID(MIXER, 8), + HPI_MIXER_GET_CONTROL_ARRAY_BY_INDEX = HPI_FUNC_ID(MIXER, 9), + HPI_MIXER_GET_CONTROL_MULTIPLE_VALUES = HPI_FUNC_ID(MIXER, 10), + HPI_MIXER_STORE = HPI_FUNC_ID(MIXER, 11), + HPI_MIXER_GET_CACHE_INFO = HPI_FUNC_ID(MIXER, 12), +#define HPI_MIXER_FUNCTION_COUNT 12 + + HPI_CONTROL_GET_INFO = HPI_FUNC_ID(CONTROL, 1), + HPI_CONTROL_GET_STATE = HPI_FUNC_ID(CONTROL, 2), + HPI_CONTROL_SET_STATE = HPI_FUNC_ID(CONTROL, 3), #define HPI_CONTROL_FUNCTION_COUNT 3 -/* NONVOL MEMORY */ -#define HPI_NVMEMORY_OPEN HPI_MAKE_INDEX(HPI_OBJ_NVMEMORY, 1) -#define HPI_NVMEMORY_READ_BYTE HPI_MAKE_INDEX(HPI_OBJ_NVMEMORY, 2) -#define HPI_NVMEMORY_WRITE_BYTE HPI_MAKE_INDEX(HPI_OBJ_NVMEMORY, 3) + + HPI_NVMEMORY_OPEN = HPI_FUNC_ID(NVMEMORY, 1), + HPI_NVMEMORY_READ_BYTE = HPI_FUNC_ID(NVMEMORY, 2), + HPI_NVMEMORY_WRITE_BYTE = HPI_FUNC_ID(NVMEMORY, 3), #define HPI_NVMEMORY_FUNCTION_COUNT 3 -/* GPIO */ -#define HPI_GPIO_OPEN HPI_MAKE_INDEX(HPI_OBJ_GPIO, 1) -#define HPI_GPIO_READ_BIT HPI_MAKE_INDEX(HPI_OBJ_GPIO, 2) -#define HPI_GPIO_WRITE_BIT HPI_MAKE_INDEX(HPI_OBJ_GPIO, 3) -#define HPI_GPIO_READ_ALL HPI_MAKE_INDEX(HPI_OBJ_GPIO, 4) -#define HPI_GPIO_WRITE_STATUS HPI_MAKE_INDEX(HPI_OBJ_GPIO, 5) + + HPI_GPIO_OPEN = HPI_FUNC_ID(GPIO, 1), + HPI_GPIO_READ_BIT = HPI_FUNC_ID(GPIO, 2), + HPI_GPIO_WRITE_BIT = HPI_FUNC_ID(GPIO, 3), + HPI_GPIO_READ_ALL = HPI_FUNC_ID(GPIO, 4), + HPI_GPIO_WRITE_STATUS = HPI_FUNC_ID(GPIO, 5), #define HPI_GPIO_FUNCTION_COUNT 5 -/* ASYNC EVENT */ -#define HPI_ASYNCEVENT_OPEN HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 1) -#define HPI_ASYNCEVENT_CLOSE HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 2) -#define HPI_ASYNCEVENT_WAIT HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 3) -#define HPI_ASYNCEVENT_GETCOUNT HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 4) -#define HPI_ASYNCEVENT_GET HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 5) -#define HPI_ASYNCEVENT_SENDEVENTS HPI_MAKE_INDEX(HPI_OBJ_ASYNCEVENT, 6) + + HPI_ASYNCEVENT_OPEN = HPI_FUNC_ID(ASYNCEVENT, 1), + HPI_ASYNCEVENT_CLOSE = HPI_FUNC_ID(ASYNCEVENT, 2), + HPI_ASYNCEVENT_WAIT = HPI_FUNC_ID(ASYNCEVENT, 3), + HPI_ASYNCEVENT_GETCOUNT = HPI_FUNC_ID(ASYNCEVENT, 4), + HPI_ASYNCEVENT_GET = HPI_FUNC_ID(ASYNCEVENT, 5), + HPI_ASYNCEVENT_SENDEVENTS = HPI_FUNC_ID(ASYNCEVENT, 6), #define HPI_ASYNCEVENT_FUNCTION_COUNT 6 -/* WATCH-DOG */ -#define HPI_WATCHDOG_OPEN HPI_MAKE_INDEX(HPI_OBJ_WATCHDOG, 1) -#define HPI_WATCHDOG_SET_TIME HPI_MAKE_INDEX(HPI_OBJ_WATCHDOG, 2) -#define HPI_WATCHDOG_PING HPI_MAKE_INDEX(HPI_OBJ_WATCHDOG, 3) -/* CLOCK */ -#define HPI_CLOCK_OPEN HPI_MAKE_INDEX(HPI_OBJ_CLOCK, 1) -#define HPI_CLOCK_SET_TIME HPI_MAKE_INDEX(HPI_OBJ_CLOCK, 2) -#define HPI_CLOCK_GET_TIME HPI_MAKE_INDEX(HPI_OBJ_CLOCK, 3) -/* PROFILE */ -#define HPI_PROFILE_OPEN_ALL HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 1) -#define HPI_PROFILE_START_ALL HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 2) -#define HPI_PROFILE_STOP_ALL HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 3) -#define HPI_PROFILE_GET HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 4) -#define HPI_PROFILE_GET_IDLECOUNT HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 5) -#define HPI_PROFILE_GET_NAME HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 6) -#define HPI_PROFILE_GET_UTILIZATION HPI_MAKE_INDEX(HPI_OBJ_PROFILE, 7) + + HPI_WATCHDOG_OPEN = HPI_FUNC_ID(WATCHDOG, 1), + HPI_WATCHDOG_SET_TIME = HPI_FUNC_ID(WATCHDOG, 2), + HPI_WATCHDOG_PING = HPI_FUNC_ID(WATCHDOG, 3), + + HPI_CLOCK_OPEN = HPI_FUNC_ID(CLOCK, 1), + HPI_CLOCK_SET_TIME = HPI_FUNC_ID(CLOCK, 2), + HPI_CLOCK_GET_TIME = HPI_FUNC_ID(CLOCK, 3), + + HPI_PROFILE_OPEN_ALL = HPI_FUNC_ID(PROFILE, 1), + HPI_PROFILE_START_ALL = HPI_FUNC_ID(PROFILE, 2), + HPI_PROFILE_STOP_ALL = HPI_FUNC_ID(PROFILE, 3), + HPI_PROFILE_GET = HPI_FUNC_ID(PROFILE, 4), + HPI_PROFILE_GET_IDLECOUNT = HPI_FUNC_ID(PROFILE, 5), + HPI_PROFILE_GET_NAME = HPI_FUNC_ID(PROFILE, 6), + HPI_PROFILE_GET_UTILIZATION = HPI_FUNC_ID(PROFILE, 7) #define HPI_PROFILE_FUNCTION_COUNT 7 -/* ////////////////////////////////////////////////////////////////////// */ -/* PRIVATE ATTRIBUTES */ +}; /* ////////////////////////////////////////////////////////////////////// */ /* STRUCTURES */ @@ -672,18 +552,7 @@ Threshold is a -ve number in units of dB/100, /** PCI bus resource */ struct hpi_pci { u32 __iomem *ap_mem_base[HPI_MAX_ADAPTER_MEM_SPACES]; - struct pci_dev *p_os_data; - -#ifndef HPI64BIT /* keep structure size constant */ - u32 padding[HPI_MAX_ADAPTER_MEM_SPACES + 1]; -#endif - u16 vendor_id; - u16 device_id; - u16 subsys_vendor_id; - u16 subsys_device_id; - u16 bus_number; - u16 device_number; - u32 interrupt; + struct pci_dev *pci_dev; }; struct hpi_resource { @@ -702,12 +571,10 @@ struct hpi_resource { /** Format info used inside struct hpi_message Not the same as public API struct hpi_format */ struct hpi_msg_format { - u32 sample_rate; - /**< 11025, 32000, 44100 ... */ - u32 bit_rate; /**< for MPEG */ - u32 attributes; - /**< Stereo/JointStereo/Mono */ - u16 channels; /**< 1,2..., (or ancillary mode or idle bit */ + u32 sample_rate; /**< 11025, 32000, 44100 etc. */ + u32 bit_rate; /**< for MPEG */ + u32 attributes; /**< stereo/joint_stereo/mono */ + u16 channels; /**< 1,2..., (or ancillary mode or idle bit */ u16 format; /**< HPI_FORMAT_PCM16, _MPEG etc. see \ref HPI_FORMATS. */ }; @@ -742,7 +609,7 @@ struct hpi_data_compat32 { struct hpi_buffer { /** placehoder for backward compatability (see dwBufferSize) */ struct hpi_msg_format reserved; - u32 command; /**< HPI_BUFFER_CMD_xxx*/ + u32 command; /**< HPI_BUFFER_CMD_xxx*/ u32 pci_address; /**< PCI physical address of buffer for DSP DMA */ u32 buffer_size; /**< must line up with data_size of HPI_DATA*/ }; @@ -777,30 +644,25 @@ struct hpi_subsys_msg { struct hpi_subsys_res { u32 version; - u32 data; /* used to return extended version */ - u16 num_adapters; /* number of adapters */ + u32 data; /* extended version */ + u16 num_adapters; u16 adapter_index; - u16 aw_adapter_list[HPI_MAX_ADAPTERS]; -}; - -struct hpi_adapter_msg { - u32 adapter_mode; /* adapter mode */ - u16 assert_id; /* assert number for "test assert" call - object_index for find object call - query_or_set for hpi_adapter_set_mode_ex() */ - u16 object_type; /* for adapter find object call */ + u16 adapter_type; + u16 pad16; }; union hpi_adapterx_msg { - struct hpi_adapter_msg adapter; struct { - u32 offset; - } query_flash; + u32 dsp_address; + u32 count_bytes; + } debug_read; struct { - u32 offset; - u32 length; - u32 key; - } start_flash; + u32 adapter_mode; + u16 query_or_set; + } mode; + struct { + u16 index; + } module_info; struct { u32 checksum; u16 sequence; @@ -809,28 +671,41 @@ union hpi_adapterx_msg { u16 unused; } program_flash; struct { + u16 index; + u16 what; + u16 property_index; + } property_enum; + struct { u16 property; u16 parameter1; u16 parameter2; } property_set; struct { - u16 index; - u16 what; - u16 property_index; - } property_enum; + u32 offset; + } query_flash; struct { - u16 index; - } module_info; + u32 pad32; + u16 key1; + u16 key2; + } restart; struct { - u32 dsp_address; - u32 count_bytes; - } debug_read; + u32 offset; + u32 length; + u32 key; + } start_flash; + struct { + u32 pad32; + u16 value; + } test_assert; + struct { + u32 yes; + } irq_query; }; struct hpi_adapter_res { u32 serial_number; u16 adapter_type; - u16 adapter_index; /* is this needed? also used for dsp_index */ + u16 adapter_index; u16 num_instreams; u16 num_outstreams; u16 num_mixers; @@ -839,12 +714,18 @@ struct hpi_adapter_res { }; union hpi_adapterx_res { - struct hpi_adapter_res adapter; + struct hpi_adapter_res info; struct { - u32 checksum; - u32 length; - u32 version; - } query_flash; + u32 p1; + u16 count; + u16 dsp_index; + u32 p2; + u32 dsp_msg_addr; + char sz_message[HPI_STRING_LEN]; + } assert; + struct { + u32 adapter_mode; + } mode; struct { u16 sequence; } program_flash; @@ -852,6 +733,14 @@ union hpi_adapterx_res { u16 parameter1; u16 parameter2; } property_get; + struct { + u32 checksum; + u32 length; + u32 version; + } query_flash; + struct { + u32 yes; + } irq_query; }; struct hpi_stream_msg { @@ -863,6 +752,7 @@ struct hpi_stream_msg { u32 time_scale; struct hpi_buffer buffer; struct hpi_streamid stream; + u32 threshold_bytes; } u; }; @@ -911,7 +801,7 @@ struct hpi_stream_res { struct hpi_mixer_msg { u16 control_index; u16 control_type; /* = HPI_CONTROL_METER _VOLUME etc */ - u16 padding1; /* maintain alignment of subsequent fields */ + u16 padding1; /* Maintain alignment of subsequent fields */ u16 node_type1; /* = HPI_SOURCENODE_LINEIN etc */ u16 node_index1; /* = 0..N */ u16 node_type2; @@ -949,6 +839,11 @@ union hpi_mixerx_res { u32 p_data; /* pointer to data array */ u16 more_to_do; /* indicates if there is more to do */ } gcabi; + struct { + u32 total_controls; /* count of controls in the mixer */ + u32 cache_controls; /* count of controls in the cac */ + u32 cache_bytes; /* size of cache */ + } cache_info; }; struct hpi_control_msg { @@ -1000,12 +895,16 @@ union hpi_control_union_res { u32 band; u32 frequency; u32 gain; - u32 level; u32 deemphasis; struct { u32 data[2]; u32 bLER; } rds; + short s_level; + struct { + u16 value; + u16 mask; + } status; } tuner; struct { char sz_data[8]; @@ -1178,11 +1077,11 @@ struct hpi_profile_res_open { }; struct hpi_profile_res_time { - u32 micro_seconds; + u32 total_tick_count; u32 call_count; - u32 max_micro_seconds; - u32 min_micro_seconds; - u16 seconds; + u32 max_tick_count; + u32 ticks_per_millisecond; + u16 profile_interval; }; struct hpi_profile_res_name { @@ -1218,7 +1117,6 @@ struct hpi_message { u16 obj_index; /* */ union { struct hpi_subsys_msg s; - struct hpi_adapter_msg a; union hpi_adapterx_msg ax; struct hpi_stream_msg d; struct hpi_mixer_msg m; @@ -1239,7 +1137,7 @@ struct hpi_message { }; #define HPI_MESSAGE_SIZE_BY_OBJECT { \ - sizeof(struct hpi_message_header) , /* default, no object type 0 */ \ + sizeof(struct hpi_message_header) , /* Default, no object type 0 */ \ sizeof(struct hpi_message_header) + sizeof(struct hpi_subsys_msg),\ sizeof(struct hpi_message_header) + sizeof(union hpi_adapterx_msg),\ sizeof(struct hpi_message_header) + sizeof(struct hpi_stream_msg),\ @@ -1256,6 +1154,11 @@ struct hpi_message { sizeof(struct hpi_message_header) + sizeof(struct hpi_async_msg) \ } +/* +Note that the wSpecificError error field should be inspected and potentially +reported whenever HPI_ERROR_DSP_COMMUNICATION or HPI_ERROR_DSP_BOOTLOAD is +returned in wError. +*/ struct hpi_response_header { u16 size; u8 type; /* HPI_TYPE_RESPONSE */ @@ -1277,7 +1180,6 @@ struct hpi_response { u16 specific_error; /* adapter specific error */ union { struct hpi_subsys_res s; - struct hpi_adapter_res a; union hpi_adapterx_res ax; struct hpi_stream_res d; struct hpi_mixer_res m; @@ -1297,7 +1199,7 @@ struct hpi_response { }; #define HPI_RESPONSE_SIZE_BY_OBJECT { \ - sizeof(struct hpi_response_header) ,/* default, no object type 0 */ \ + sizeof(struct hpi_response_header) ,/* Default, no object type 0 */ \ sizeof(struct hpi_response_header) + sizeof(struct hpi_subsys_res),\ sizeof(struct hpi_response_header) + sizeof(union hpi_adapterx_res),\ sizeof(struct hpi_response_header) + sizeof(struct hpi_stream_res),\ @@ -1314,7 +1216,7 @@ struct hpi_response { sizeof(struct hpi_response_header) + sizeof(struct hpi_async_res) \ } -/*********************** version 1 message/response *****************************/ +/*********************** version 1 message/response **************************/ #define HPINET_ETHERNET_DATA_SIZE (1500) #define HPINET_IP_HDR_SIZE (20) #define HPINET_IP_DATA_SIZE (HPINET_ETHERNET_DATA_SIZE - HPINET_IP_HDR_SIZE) @@ -1394,6 +1296,17 @@ struct hpi_res_adapter_program_flash { sizeof(struct hpi_response_header) - sizeof(u16)]; }; +struct hpi_msg_adapter_debug_read { + struct hpi_message_header h; + u32 dsp_address; + u32 count_bytes; +}; + +struct hpi_res_adapter_debug_read { + struct hpi_response_header h; + u8 bytes[256]; +}; + #if 1 #define hpi_message_header_v1 hpi_message_header #define hpi_response_header_v1 hpi_response_header @@ -1414,23 +1327,10 @@ struct hpi_response_header_v1 { }; #endif -/* STRV HPI Packet */ -struct hpi_msg_strv { - struct hpi_message_header h; - struct hpi_entity strv; -}; - -struct hpi_res_strv { - struct hpi_response_header h; - struct hpi_entity strv; -}; -#define MIN_STRV_PACKET_SIZE sizeof(struct hpi_res_strv) - struct hpi_msg_payload_v0 { struct hpi_message_header h; union { struct hpi_subsys_msg s; - struct hpi_adapter_msg a; union hpi_adapterx_msg ax; struct hpi_stream_msg d; struct hpi_mixer_msg m; @@ -1451,7 +1351,6 @@ struct hpi_res_payload_v0 { struct hpi_response_header h; union { struct hpi_subsys_res s; - struct hpi_adapter_res a; union hpi_adapterx_res ax; struct hpi_stream_res d; struct hpi_mixer_res m; @@ -1471,13 +1370,13 @@ struct hpi_res_payload_v0 { union hpi_message_buffer_v1 { struct hpi_message m0; /* version 0 */ struct hpi_message_header_v1 h; - unsigned char buf[HPI_MAX_PAYLOAD_SIZE]; + u8 buf[HPI_MAX_PAYLOAD_SIZE]; }; union hpi_response_buffer_v1 { struct hpi_response r0; /* version 0 */ struct hpi_response_header_v1 h; - unsigned char buf[HPI_MAX_PAYLOAD_SIZE]; + u8 buf[HPI_MAX_PAYLOAD_SIZE]; }; compile_time_assert((sizeof(union hpi_message_buffer_v1) <= @@ -1499,6 +1398,11 @@ struct hpi_control_defn { /*////////////////////////////////////////////////////////////////////////// */ /* declarations for control caching (internal to HPI<->DSP interaction) */ +/** indicates a cached u16 value is invalid. */ +#define HPI_CACHE_INVALID_UINT16 0xFFFF +/** indicates a cached short value is invalid. */ +#define HPI_CACHE_INVALID_SHORT -32768 + /** A compact representation of (part of) a controls state. Used for efficient transfer of the control state between DSP and host or across a network @@ -1512,58 +1416,104 @@ struct hpi_control_cache_info { u16 control_index; }; -struct hpi_control_cache_single { +struct hpi_control_cache_vol { + struct hpi_control_cache_info i; + short an_log[2]; + unsigned short flags; + char padding[2]; +}; + +struct hpi_control_cache_meter { + struct hpi_control_cache_info i; + short an_log_peak[2]; + short an_logRMS[2]; +}; + +struct hpi_control_cache_channelmode { + struct hpi_control_cache_info i; + u16 mode; + char temp_padding[6]; +}; + +struct hpi_control_cache_mux { + struct hpi_control_cache_info i; + u16 source_node_type; + u16 source_node_index; + char temp_padding[4]; +}; + +struct hpi_control_cache_level { struct hpi_control_cache_info i; + short an_log[2]; + char temp_padding[4]; +}; + +struct hpi_control_cache_tuner { + struct hpi_control_cache_info i; + u32 freq_ink_hz; + u16 band; + short s_level_avg; +}; + +struct hpi_control_cache_aes3rx { + struct hpi_control_cache_info i; + u32 error_status; + u32 format; +}; + +struct hpi_control_cache_aes3tx { + struct hpi_control_cache_info i; + u32 format; + char temp_padding[4]; +}; + +struct hpi_control_cache_tonedetector { + struct hpi_control_cache_info i; + u16 state; + char temp_padding[6]; +}; + +struct hpi_control_cache_silencedetector { + struct hpi_control_cache_info i; + u32 state; + char temp_padding[4]; +}; + +struct hpi_control_cache_sampleclock { + struct hpi_control_cache_info i; + u16 source; + u16 source_index; + u32 sample_rate; +}; + +struct hpi_control_cache_microphone { + struct hpi_control_cache_info i; + u16 phantom_state; + char temp_padding[6]; +}; + +struct hpi_control_cache_generic { + struct hpi_control_cache_info i; + u32 dw1; + u32 dw2; +}; + +struct hpi_control_cache_single { union { - struct { /* volume */ - short an_log[2]; - } v; - struct { /* peak meter */ - short an_log_peak[2]; - short an_logRMS[2]; - } p; - struct { /* channel mode */ - u16 mode; - } m; - struct { /* multiplexer */ - u16 source_node_type; - u16 source_node_index; - } x; - struct { /* level/trim */ - short an_log[2]; - } l; - struct { /* tuner - partial caching. - some attributes go to the DSP. */ - u32 freq_ink_hz; - u16 band; - u16 level; - } t; - struct { /* AESEBU rx status */ - u32 error_status; - u32 source; - } aes3rx; - struct { /* AESEBU tx */ - u32 format; - } aes3tx; - struct { /* tone detector */ - u16 state; - } tone; - struct { /* silence detector */ - u32 state; - u32 count; - } silence; - struct { /* sample clock */ - u16 source; - u16 source_index; - u32 sample_rate; - } clk; - struct { /* microphone control */ - u16 state; - } phantom_power; - struct { /* generic control */ - u32 dw1; - u32 dw2; - } g; + struct hpi_control_cache_info i; + struct hpi_control_cache_vol vol; + struct hpi_control_cache_meter meter; + struct hpi_control_cache_channelmode mode; + struct hpi_control_cache_mux mux; + struct hpi_control_cache_level level; + struct hpi_control_cache_tuner tuner; + struct hpi_control_cache_aes3rx aes3rx; + struct hpi_control_cache_aes3tx aes3tx; + struct hpi_control_cache_tonedetector tone; + struct hpi_control_cache_silencedetector silence; + struct hpi_control_cache_sampleclock clk; + struct hpi_control_cache_microphone microphone; + struct hpi_control_cache_generic generic; } u; }; @@ -1580,8 +1530,7 @@ struct hpi_control_cache_pad { u32 traffic_anouncement; }; -/*/////////////////////////////////////////////////////////////////////////// */ -/* declarations for 2^N sized FIFO buffer (internal to HPI<->DSP interaction) */ +/* 2^N sized FIFO buffer (internal to HPI<->DSP interaction) */ struct hpi_fifo_buffer { u32 size; u32 dSP_index; @@ -1606,25 +1555,18 @@ u32 hpi_indexes_to_handle(const char c_object, const u16 adapter_index, /*////////////////////////////////////////////////////////////////////////// */ /* main HPI entry point */ -hpi_handler_func hpi_send_recv; - -/* UDP message */ -void hpi_send_recvUDP(struct hpi_message *phm, struct hpi_response *phr, - const unsigned int timeout); +void hpi_send_recv(struct hpi_message *phm, struct hpi_response *phr); /* used in PnP OS/driver */ -u16 hpi_subsys_create_adapter(const struct hpi_hsubsys *ph_subsys, - const struct hpi_resource *p_resource, u16 *pw_adapter_index); +u16 hpi_subsys_create_adapter(const struct hpi_resource *p_resource, + u16 *pw_adapter_index); -u16 hpi_subsys_delete_adapter(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index); +u16 hpi_subsys_delete_adapter(u16 adapter_index); -u16 hpi_outstream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, u8 **pp_buffer, +u16 hpi_outstream_host_buffer_get_info(u32 h_outstream, u8 **pp_buffer, struct hpi_hostbuffer_status **pp_status); -u16 hpi_instream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys, - u32 h_instream, u8 **pp_buffer, +u16 hpi_instream_host_buffer_get_info(u32 h_instream, u8 **pp_buffer, struct hpi_hostbuffer_status **pp_status); u16 hpi_adapter_restart(u16 adapter_index); diff --git a/sound/pci/asihpi/hpicmn.c b/sound/pci/asihpi/hpicmn.c index d67f4d3db911..3e9c5c289764 100644 --- a/sound/pci/asihpi/hpicmn.c +++ b/sound/pci/asihpi/hpicmn.c @@ -26,6 +26,8 @@ #include "hpi_internal.h" #include "hpidebug.h" +#include "hpimsginit.h" + #include "hpicmn.h" struct hpi_adapters_list { @@ -43,14 +45,24 @@ static struct hpi_adapters_list adapters; **/ u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr) { - u16 error = 0; + if (phr->type != HPI_TYPE_RESPONSE) { + HPI_DEBUG_LOG(ERROR, "header type %d invalid\n", phr->type); + return HPI_ERROR_INVALID_RESPONSE; + } - if ((phr->type != HPI_TYPE_RESPONSE) - || (phr->object != phm->object) - || (phr->function != phm->function)) - error = HPI_ERROR_INVALID_RESPONSE; + if (phr->object != phm->object) { + HPI_DEBUG_LOG(ERROR, "header object %d invalid\n", + phr->object); + return HPI_ERROR_INVALID_RESPONSE; + } + + if (phr->function != phm->function) { + HPI_DEBUG_LOG(ERROR, "header type %d invalid\n", + phr->function); + return HPI_ERROR_INVALID_RESPONSE; + } - return error; + return 0; } u16 hpi_add_adapter(struct hpi_adapter_obj *pao) @@ -66,8 +78,18 @@ u16 hpi_add_adapter(struct hpi_adapter_obj *pao) } if (adapters.adapter[pao->index].adapter_type) { - { - retval = HPI_DUPLICATE_ADAPTER_NUMBER; + int a; + for (a = HPI_MAX_ADAPTERS - 1; a >= 0; a--) { + if (!adapters.adapter[a].adapter_type) { + HPI_DEBUG_LOG(WARNING, + "ASI%X duplicate index %d moved to %d\n", + pao->adapter_type, pao->index, a); + pao->index = a; + break; + } + } + if (a < 0) { + retval = HPI_ERROR_DUPLICATE_ADAPTER_NUMBER; goto unlock; } } @@ -76,17 +98,22 @@ u16 hpi_add_adapter(struct hpi_adapter_obj *pao) adapters.gw_num_adapters++; unlock: - hpios_alistlock_un_lock(&adapters); + hpios_alistlock_unlock(&adapters); return retval; } void hpi_delete_adapter(struct hpi_adapter_obj *pao) { - memset(pao, 0, sizeof(struct hpi_adapter_obj)); + if (!pao->adapter_type) { + HPI_DEBUG_LOG(ERROR, "removing null adapter?\n"); + return; + } hpios_alistlock_lock(&adapters); - adapters.gw_num_adapters--; /* dec the number of adapters */ - hpios_alistlock_un_lock(&adapters); + if (adapters.adapter[pao->index].adapter_type) + adapters.gw_num_adapters--; + memset(&adapters.adapter[pao->index], 0, sizeof(adapters.adapter[0])); + hpios_alistlock_unlock(&adapters); } /** @@ -99,7 +126,7 @@ struct hpi_adapter_obj *hpi_find_adapter(u16 adapter_index) struct hpi_adapter_obj *pao = NULL; if (adapter_index >= HPI_MAX_ADAPTERS) { - HPI_DEBUG_LOG(VERBOSE, "find_adapter invalid index %d ", + HPI_DEBUG_LOG(VERBOSE, "find_adapter invalid index %d\n", adapter_index); return NULL; } @@ -125,51 +152,34 @@ struct hpi_adapter_obj *hpi_find_adapter(u16 adapter_index) * wipe an HPI_ADAPTERS_LIST structure. * **/ -static void wipe_adapter_list(void - ) +static void wipe_adapter_list(void) { memset(&adapters, 0, sizeof(adapters)); } -/** -* SubSysGetAdapters fills awAdapterList in an struct hpi_response structure -* with all adapters in the given HPI_ADAPTERS_LIST. -* -*/ -static void subsys_get_adapters(struct hpi_response *phr) +static void subsys_get_adapter(struct hpi_message *phm, + struct hpi_response *phr) { - /* fill in the response adapter array with the position */ - /* identified by the adapter number/index of the adapters in */ - /* this HPI */ - /* i.e. if we have an A120 with it's jumper set to */ - /* Adapter Number 2 then put an Adapter type A120 in the */ - /* array in position 1 */ - /* NOTE: AdapterNumber is 1..N, Index is 0..N-1 */ - - /* input: NONE */ - /* output: wNumAdapters */ - /* awAdapter[] */ - /* */ - - short i; - struct hpi_adapter_obj *pao = NULL; + int count = phm->obj_index; + u16 index = 0; - HPI_DEBUG_LOG(VERBOSE, "subsys_get_adapters\n"); - - /* for each adapter, place it's type in the position of the array */ - /* corresponding to it's adapter number */ - for (i = 0; i < adapters.gw_num_adapters; i++) { - pao = &adapters.adapter[i]; - if (phr->u.s.aw_adapter_list[pao->index] != 0) { - phr->error = HPI_DUPLICATE_ADAPTER_NUMBER; - phr->specific_error = pao->index; - return; + /* find the nCount'th nonzero adapter in array */ + for (index = 0; index < HPI_MAX_ADAPTERS; index++) { + if (adapters.adapter[index].adapter_type) { + if (!count) + break; + count--; } - phr->u.s.aw_adapter_list[pao->index] = pao->adapter_type; } - phr->u.s.num_adapters = adapters.gw_num_adapters; - phr->error = 0; /* the function completed OK; */ + if (index < HPI_MAX_ADAPTERS) { + phr->u.s.adapter_index = adapters.adapter[index].index; + phr->u.s.adapter_type = adapters.adapter[index].adapter_type; + } else { + phr->u.s.adapter_index = 0; + phr->u.s.adapter_type = 0; + phr->error = HPI_ERROR_BAD_ADAPTER_NUMBER; + } } static unsigned int control_cache_alloc_check(struct hpi_control_cache *pC) @@ -178,67 +188,98 @@ static unsigned int control_cache_alloc_check(struct hpi_control_cache *pC) int cached = 0; if (!pC) return 0; - if ((!pC->init) && (pC->p_cache != NULL) && (pC->control_count) - && (pC->cache_size_in_bytes) - ) { - u32 *p_master_cache; - pC->init = 1; - - p_master_cache = (u32 *)pC->p_cache; - HPI_DEBUG_LOG(VERBOSE, "check %d controls\n", + + if (pC->init) + return pC->init; + + if (!pC->p_cache) + return 0; + + if (pC->control_count && pC->cache_size_in_bytes) { + char *p_master_cache; + unsigned int byte_count = 0; + + p_master_cache = (char *)pC->p_cache; + HPI_DEBUG_LOG(DEBUG, "check %d controls\n", pC->control_count); for (i = 0; i < pC->control_count; i++) { struct hpi_control_cache_info *info = (struct hpi_control_cache_info *) - p_master_cache; + &p_master_cache[byte_count]; + + if (!info->size_in32bit_words) { + if (!i) { + HPI_DEBUG_LOG(INFO, + "adap %d cache not ready?\n", + pC->adap_idx); + return 0; + } + /* The cache is invalid. + * Minimum valid entry size is + * sizeof(struct hpi_control_cache_info) + */ + HPI_DEBUG_LOG(ERROR, + "adap %d zero size cache entry %d\n", + pC->adap_idx, i); + break; + } if (info->control_type) { - pC->p_info[i] = info; + pC->p_info[info->control_index] = info; cached++; - } else - pC->p_info[i] = NULL; + } else /* dummy cache entry */ + pC->p_info[info->control_index] = NULL; - if (info->size_in32bit_words) - p_master_cache += info->size_in32bit_words; - else - p_master_cache += - sizeof(struct - hpi_control_cache_single) / - sizeof(u32); + byte_count += info->size_in32bit_words * 4; HPI_DEBUG_LOG(VERBOSE, - "cached %d, pinfo %p index %d type %d\n", - cached, pC->p_info[i], info->control_index, - info->control_type); + "cached %d, pinfo %p index %d type %d size %d\n", + cached, pC->p_info[info->control_index], + info->control_index, info->control_type, + info->size_in32bit_words); + + /* quit loop early if whole cache has been scanned. + * dwControlCount is the maximum possible entries + * but some may be absent from the cache + */ + if (byte_count >= pC->cache_size_in_bytes) + break; + /* have seen last control index */ + if (info->control_index == pC->control_count - 1) + break; } - /* - We didn't find anything to cache, so try again later ! - */ - if (!cached) - pC->init = 0; + + if (byte_count != pC->cache_size_in_bytes) + HPI_DEBUG_LOG(WARNING, + "adap %d bytecount %d != cache size %d\n", + pC->adap_idx, byte_count, + pC->cache_size_in_bytes); + else + HPI_DEBUG_LOG(DEBUG, + "adap %d cache good, bytecount == cache size = %d\n", + pC->adap_idx, byte_count); + + pC->init = (u16)cached; } return pC->init; } /** Find a control. */ -static short find_control(struct hpi_message *phm, - struct hpi_control_cache *p_cache, struct hpi_control_cache_info **pI, - u16 *pw_control_index) +static short find_control(u16 control_index, + struct hpi_control_cache *p_cache, struct hpi_control_cache_info **pI) { - *pw_control_index = phm->obj_index; - if (!control_cache_alloc_check(p_cache)) { HPI_DEBUG_LOG(VERBOSE, - "control_cache_alloc_check() failed. adap%d ci%d\n", - phm->adapter_index, *pw_control_index); + "control_cache_alloc_check() failed %d\n", + control_index); return 0; } - *pI = p_cache->p_info[*pw_control_index]; + *pI = p_cache->p_info[control_index]; if (!*pI) { - HPI_DEBUG_LOG(VERBOSE, "uncached adap %d, control %d\n", - phm->adapter_index, *pw_control_index); + HPI_DEBUG_LOG(VERBOSE, "Uncached Control %d\n", + control_index); return 0; } else { HPI_DEBUG_LOG(VERBOSE, "find_control() type %d\n", @@ -247,25 +288,6 @@ static short find_control(struct hpi_message *phm, return 1; } -/** Used by the kernel driver to figure out if a buffer needs mapping. - */ -short hpi_check_buffer_mapping(struct hpi_control_cache *p_cache, - struct hpi_message *phm, void **p, unsigned int *pN) -{ - *pN = 0; - *p = NULL; - if ((phm->function == HPI_CONTROL_GET_STATE) - && (phm->object == HPI_OBJ_CONTROLEX) - ) { - u16 control_index; - struct hpi_control_cache_info *pI; - - if (!find_control(phm, p_cache, &pI, &control_index)) - return 0; - } - return 0; -} - /* allow unified treatment of several string fields within struct */ #define HPICMN_PAD_OFS_AND_SIZE(m) {\ offsetof(struct hpi_control_cache_pad, m), \ @@ -290,13 +312,16 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache, struct hpi_message *phm, struct hpi_response *phr) { short found = 1; - u16 control_index; struct hpi_control_cache_info *pI; struct hpi_control_cache_single *pC; struct hpi_control_cache_pad *p_pad; - if (!find_control(phm, p_cache, &pI, &control_index)) + if (!find_control(phm->obj_index, p_cache, &pI)) { + HPI_DEBUG_LOG(VERBOSE, + "HPICMN find_control() failed for adap %d\n", + phm->adapter_index); return 0; + } phr->error = 0; @@ -310,55 +335,79 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache, case HPI_CONTROL_METER: if (phm->u.c.attribute == HPI_METER_PEAK) { - phr->u.c.an_log_value[0] = pC->u.p.an_log_peak[0]; - phr->u.c.an_log_value[1] = pC->u.p.an_log_peak[1]; + phr->u.c.an_log_value[0] = pC->u.meter.an_log_peak[0]; + phr->u.c.an_log_value[1] = pC->u.meter.an_log_peak[1]; } else if (phm->u.c.attribute == HPI_METER_RMS) { - phr->u.c.an_log_value[0] = pC->u.p.an_logRMS[0]; - phr->u.c.an_log_value[1] = pC->u.p.an_logRMS[1]; + if (pC->u.meter.an_logRMS[0] == + HPI_CACHE_INVALID_SHORT) { + phr->error = + HPI_ERROR_INVALID_CONTROL_ATTRIBUTE; + phr->u.c.an_log_value[0] = HPI_METER_MINIMUM; + phr->u.c.an_log_value[1] = HPI_METER_MINIMUM; + } else { + phr->u.c.an_log_value[0] = + pC->u.meter.an_logRMS[0]; + phr->u.c.an_log_value[1] = + pC->u.meter.an_logRMS[1]; + } } else found = 0; break; case HPI_CONTROL_VOLUME: if (phm->u.c.attribute == HPI_VOLUME_GAIN) { - phr->u.c.an_log_value[0] = pC->u.v.an_log[0]; - phr->u.c.an_log_value[1] = pC->u.v.an_log[1]; - } else + phr->u.c.an_log_value[0] = pC->u.vol.an_log[0]; + phr->u.c.an_log_value[1] = pC->u.vol.an_log[1]; + } else if (phm->u.c.attribute == HPI_VOLUME_MUTE) { + if (pC->u.vol.flags & HPI_VOLUME_FLAG_HAS_MUTE) { + if (pC->u.vol.flags & HPI_VOLUME_FLAG_MUTED) + phr->u.c.param1 = + HPI_BITMASK_ALL_CHANNELS; + else + phr->u.c.param1 = 0; + } else { + phr->error = + HPI_ERROR_INVALID_CONTROL_ATTRIBUTE; + phr->u.c.param1 = 0; + } + } else { found = 0; + } break; case HPI_CONTROL_MULTIPLEXER: if (phm->u.c.attribute == HPI_MULTIPLEXER_SOURCE) { - phr->u.c.param1 = pC->u.x.source_node_type; - phr->u.c.param2 = pC->u.x.source_node_index; + phr->u.c.param1 = pC->u.mux.source_node_type; + phr->u.c.param2 = pC->u.mux.source_node_index; } else { found = 0; } break; case HPI_CONTROL_CHANNEL_MODE: if (phm->u.c.attribute == HPI_CHANNEL_MODE_MODE) - phr->u.c.param1 = pC->u.m.mode; + phr->u.c.param1 = pC->u.mode.mode; else found = 0; break; case HPI_CONTROL_LEVEL: if (phm->u.c.attribute == HPI_LEVEL_GAIN) { - phr->u.c.an_log_value[0] = pC->u.l.an_log[0]; - phr->u.c.an_log_value[1] = pC->u.l.an_log[1]; + phr->u.c.an_log_value[0] = pC->u.level.an_log[0]; + phr->u.c.an_log_value[1] = pC->u.level.an_log[1]; } else found = 0; break; case HPI_CONTROL_TUNER: if (phm->u.c.attribute == HPI_TUNER_FREQ) - phr->u.c.param1 = pC->u.t.freq_ink_hz; + phr->u.c.param1 = pC->u.tuner.freq_ink_hz; else if (phm->u.c.attribute == HPI_TUNER_BAND) - phr->u.c.param1 = pC->u.t.band; - else if ((phm->u.c.attribute == HPI_TUNER_LEVEL) - && (phm->u.c.param1 == HPI_TUNER_LEVEL_AVERAGE)) - if (pC->u.t.level == HPI_ERROR_ILLEGAL_CACHE_VALUE) { - phr->u.c.param1 = 0; + phr->u.c.param1 = pC->u.tuner.band; + else if (phm->u.c.attribute == HPI_TUNER_LEVEL_AVG) + if (pC->u.tuner.s_level_avg == + HPI_CACHE_INVALID_SHORT) { + phr->u.cu.tuner.s_level = 0; phr->error = HPI_ERROR_INVALID_CONTROL_ATTRIBUTE; } else - phr->u.c.param1 = pC->u.t.level; + phr->u.cu.tuner.s_level = + pC->u.tuner.s_level_avg; else found = 0; break; @@ -366,7 +415,7 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache, if (phm->u.c.attribute == HPI_AESEBURX_ERRORSTATUS) phr->u.c.param1 = pC->u.aes3rx.error_status; else if (phm->u.c.attribute == HPI_AESEBURX_FORMAT) - phr->u.c.param1 = pC->u.aes3rx.source; + phr->u.c.param1 = pC->u.aes3rx.format; else found = 0; break; @@ -385,13 +434,12 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache, case HPI_CONTROL_SILENCEDETECTOR: if (phm->u.c.attribute == HPI_SILENCEDETECTOR_STATE) { phr->u.c.param1 = pC->u.silence.state; - phr->u.c.param2 = pC->u.silence.count; } else found = 0; break; case HPI_CONTROL_MICROPHONE: if (phm->u.c.attribute == HPI_MICROPHONE_PHANTOM_POWER) - phr->u.c.param1 = pC->u.phantom_power.state; + phr->u.c.param1 = pC->u.microphone.phantom_state; else found = 0; break; @@ -400,7 +448,7 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache, phr->u.c.param1 = pC->u.clk.source; else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE_INDEX) { if (pC->u.clk.source_index == - HPI_ERROR_ILLEGAL_CACHE_VALUE) { + HPI_CACHE_INVALID_UINT16) { phr->u.c.param1 = 0; phr->error = HPI_ERROR_INVALID_CONTROL_ATTRIBUTE; @@ -411,60 +459,63 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache, else found = 0; break; - case HPI_CONTROL_PAD: + case HPI_CONTROL_PAD:{ + struct hpi_control_cache_pad *p_pad; + p_pad = (struct hpi_control_cache_pad *)pI; - if (!(p_pad->field_valid_flags & (1 << - HPI_CTL_ATTR_INDEX(phm->u.c. - attribute)))) { - phr->error = HPI_ERROR_INVALID_CONTROL_ATTRIBUTE; - break; - } - - if (phm->u.c.attribute == HPI_PAD_PROGRAM_ID) - phr->u.c.param1 = p_pad->pI; - else if (phm->u.c.attribute == HPI_PAD_PROGRAM_TYPE) - phr->u.c.param1 = p_pad->pTY; - else { - unsigned int index = - HPI_CTL_ATTR_INDEX(phm->u.c.attribute) - 1; - unsigned int offset = phm->u.c.param1; - unsigned int pad_string_len, field_size; - char *pad_string; - unsigned int tocopy; - - HPI_DEBUG_LOG(VERBOSE, "PADS HPI_PADS_ %d\n", - phm->u.c.attribute); - - if (index > ARRAY_SIZE(pad_desc) - 1) { + if (!(p_pad->field_valid_flags & (1 << + HPI_CTL_ATTR_INDEX(phm->u.c. + attribute)))) { phr->error = HPI_ERROR_INVALID_CONTROL_ATTRIBUTE; break; } - pad_string = ((char *)p_pad) + pad_desc[index].offset; - field_size = pad_desc[index].field_size; - /* Ensure null terminator */ - pad_string[field_size - 1] = 0; - - pad_string_len = strlen(pad_string) + 1; - - if (offset > pad_string_len) { - phr->error = HPI_ERROR_INVALID_CONTROL_VALUE; - break; + if (phm->u.c.attribute == HPI_PAD_PROGRAM_ID) + phr->u.c.param1 = p_pad->pI; + else if (phm->u.c.attribute == HPI_PAD_PROGRAM_TYPE) + phr->u.c.param1 = p_pad->pTY; + else { + unsigned int index = + HPI_CTL_ATTR_INDEX(phm->u.c. + attribute) - 1; + unsigned int offset = phm->u.c.param1; + unsigned int pad_string_len, field_size; + char *pad_string; + unsigned int tocopy; + + if (index > ARRAY_SIZE(pad_desc) - 1) { + phr->error = + HPI_ERROR_INVALID_CONTROL_ATTRIBUTE; + break; + } + + pad_string = + ((char *)p_pad) + + pad_desc[index].offset; + field_size = pad_desc[index].field_size; + /* Ensure null terminator */ + pad_string[field_size - 1] = 0; + + pad_string_len = strlen(pad_string) + 1; + + if (offset > pad_string_len) { + phr->error = + HPI_ERROR_INVALID_CONTROL_VALUE; + break; + } + + tocopy = pad_string_len - offset; + if (tocopy > sizeof(phr->u.cu.chars8.sz_data)) + tocopy = sizeof(phr->u.cu.chars8. + sz_data); + + memcpy(phr->u.cu.chars8.sz_data, + &pad_string[offset], tocopy); + + phr->u.cu.chars8.remaining_chars = + pad_string_len - offset - tocopy; } - - tocopy = pad_string_len - offset; - if (tocopy > sizeof(phr->u.cu.chars8.sz_data)) - tocopy = sizeof(phr->u.cu.chars8.sz_data); - - HPI_DEBUG_LOG(VERBOSE, - "PADS memcpy(%d), offset %d \n", tocopy, - offset); - memcpy(phr->u.cu.chars8.sz_data, &pad_string[offset], - tocopy); - - phr->u.cu.chars8.remaining_chars = - pad_string_len - offset - tocopy; } break; default: @@ -472,16 +523,9 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache, break; } - if (found) - HPI_DEBUG_LOG(VERBOSE, - "cached adap %d, ctl %d, type %d, attr %d\n", - phm->adapter_index, pI->control_index, - pI->control_type, phm->u.c.attribute); - else - HPI_DEBUG_LOG(VERBOSE, - "uncached adap %d, ctl %d, ctl type %d\n", - phm->adapter_index, pI->control_index, - pI->control_type); + HPI_DEBUG_LOG(VERBOSE, "%s Adap %d, Ctl %d, Type %d, Attr %d\n", + found ? "Cached" : "Uncached", phm->adapter_index, + pI->control_index, pI->control_type, phm->u.c.attribute); if (found) phr->size = @@ -497,18 +541,21 @@ Only update if no error. Volume and Level return the limited values in the response, so use these Multiplexer does so use sent values */ -void hpi_sync_control_cache(struct hpi_control_cache *p_cache, +void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *p_cache, struct hpi_message *phm, struct hpi_response *phr) { - u16 control_index; struct hpi_control_cache_single *pC; struct hpi_control_cache_info *pI; if (phr->error) return; - if (!find_control(phm, p_cache, &pI, &control_index)) + if (!find_control(phm->obj_index, p_cache, &pI)) { + HPI_DEBUG_LOG(VERBOSE, + "HPICMN find_control() failed for adap %d\n", + phm->adapter_index); return; + } /* pC is the default cached control strucure. May be cast to something else in the following switch statement. @@ -518,31 +565,36 @@ void hpi_sync_control_cache(struct hpi_control_cache *p_cache, switch (pI->control_type) { case HPI_CONTROL_VOLUME: if (phm->u.c.attribute == HPI_VOLUME_GAIN) { - pC->u.v.an_log[0] = phr->u.c.an_log_value[0]; - pC->u.v.an_log[1] = phr->u.c.an_log_value[1]; + pC->u.vol.an_log[0] = phr->u.c.an_log_value[0]; + pC->u.vol.an_log[1] = phr->u.c.an_log_value[1]; + } else if (phm->u.c.attribute == HPI_VOLUME_MUTE) { + if (phm->u.c.param1) + pC->u.vol.flags |= HPI_VOLUME_FLAG_MUTED; + else + pC->u.vol.flags &= ~HPI_VOLUME_FLAG_MUTED; } break; case HPI_CONTROL_MULTIPLEXER: /* mux does not return its setting on Set command. */ if (phm->u.c.attribute == HPI_MULTIPLEXER_SOURCE) { - pC->u.x.source_node_type = (u16)phm->u.c.param1; - pC->u.x.source_node_index = (u16)phm->u.c.param2; + pC->u.mux.source_node_type = (u16)phm->u.c.param1; + pC->u.mux.source_node_index = (u16)phm->u.c.param2; } break; case HPI_CONTROL_CHANNEL_MODE: /* mode does not return its setting on Set command. */ if (phm->u.c.attribute == HPI_CHANNEL_MODE_MODE) - pC->u.m.mode = (u16)phm->u.c.param1; + pC->u.mode.mode = (u16)phm->u.c.param1; break; case HPI_CONTROL_LEVEL: if (phm->u.c.attribute == HPI_LEVEL_GAIN) { - pC->u.v.an_log[0] = phr->u.c.an_log_value[0]; - pC->u.v.an_log[1] = phr->u.c.an_log_value[1]; + pC->u.vol.an_log[0] = phr->u.c.an_log_value[0]; + pC->u.vol.an_log[1] = phr->u.c.an_log_value[1]; } break; case HPI_CONTROL_MICROPHONE: if (phm->u.c.attribute == HPI_MICROPHONE_PHANTOM_POWER) - pC->u.phantom_power.state = (u16)phm->u.c.param1; + pC->u.microphone.phantom_state = (u16)phm->u.c.param1; break; case HPI_CONTROL_AESEBU_TRANSMITTER: if (phm->u.c.attribute == HPI_AESEBUTX_FORMAT) @@ -550,7 +602,7 @@ void hpi_sync_control_cache(struct hpi_control_cache *p_cache, break; case HPI_CONTROL_AESEBU_RECEIVER: if (phm->u.c.attribute == HPI_AESEBURX_FORMAT) - pC->u.aes3rx.source = phm->u.c.param1; + pC->u.aes3rx.format = phm->u.c.param1; break; case HPI_CONTROL_SAMPLECLOCK: if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE) @@ -565,59 +617,57 @@ void hpi_sync_control_cache(struct hpi_control_cache *p_cache, } } -struct hpi_control_cache *hpi_alloc_control_cache(const u32 - number_of_controls, const u32 size_in_bytes, - struct hpi_control_cache_info *pDSP_control_buffer) +struct hpi_control_cache *hpi_alloc_control_cache(const u32 control_count, + const u32 size_in_bytes, u8 *p_dsp_control_buffer) { struct hpi_control_cache *p_cache = kmalloc(sizeof(*p_cache), GFP_KERNEL); if (!p_cache) return NULL; + p_cache->p_info = - kmalloc(sizeof(*p_cache->p_info) * number_of_controls, - GFP_KERNEL); + kmalloc(sizeof(*p_cache->p_info) * control_count, GFP_KERNEL); if (!p_cache->p_info) { kfree(p_cache); return NULL; } + memset(p_cache->p_info, 0, sizeof(*p_cache->p_info) * control_count); p_cache->cache_size_in_bytes = size_in_bytes; - p_cache->control_count = number_of_controls; - p_cache->p_cache = - (struct hpi_control_cache_single *)pDSP_control_buffer; + p_cache->control_count = control_count; + p_cache->p_cache = p_dsp_control_buffer; p_cache->init = 0; return p_cache; } void hpi_free_control_cache(struct hpi_control_cache *p_cache) { - if (p_cache->init) { + if (p_cache) { kfree(p_cache->p_info); - p_cache->p_info = NULL; - p_cache->init = 0; kfree(p_cache); } } static void subsys_message(struct hpi_message *phm, struct hpi_response *phr) { + hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, phm->function, 0); switch (phm->function) { case HPI_SUBSYS_OPEN: case HPI_SUBSYS_CLOSE: case HPI_SUBSYS_DRIVER_UNLOAD: - phr->error = 0; break; case HPI_SUBSYS_DRIVER_LOAD: wipe_adapter_list(); hpios_alistlock_init(&adapters); - phr->error = 0; break; - case HPI_SUBSYS_GET_INFO: - subsys_get_adapters(phr); + case HPI_SUBSYS_GET_ADAPTER: + subsys_get_adapter(phm, phr); + break; + case HPI_SUBSYS_GET_NUM_ADAPTERS: + phr->u.s.num_adapters = adapters.gw_num_adapters; break; case HPI_SUBSYS_CREATE_ADAPTER: case HPI_SUBSYS_DELETE_ADAPTER: - phr->error = 0; break; default: phr->error = HPI_ERROR_INVALID_FUNC; diff --git a/sound/pci/asihpi/hpicmn.h b/sound/pci/asihpi/hpicmn.h index 6229022f56cb..590f0b69e655 100644 --- a/sound/pci/asihpi/hpicmn.h +++ b/sound/pci/asihpi/hpicmn.h @@ -33,18 +33,19 @@ struct hpi_adapter_obj { }; struct hpi_control_cache { - u32 init; /**< indicates whether the - structures are initialized */ + /** indicates whether the structures are initialized */ + u16 init; + u16 adap_idx; u32 control_count; u32 cache_size_in_bytes; - struct hpi_control_cache_info - **p_info; /**< pointer to allocated memory of - lookup pointers. */ - struct hpi_control_cache_single - *p_cache; /**< pointer to DSP's control cache. */ + /** pointer to allocated memory of lookup pointers. */ + struct hpi_control_cache_info **p_info; + /** pointer to DSP's control cache. */ + u8 *p_cache; }; struct hpi_adapter_obj *hpi_find_adapter(u16 adapter_index); + u16 hpi_add_adapter(struct hpi_adapter_obj *pao); void hpi_delete_adapter(struct hpi_adapter_obj *pao); @@ -52,13 +53,10 @@ void hpi_delete_adapter(struct hpi_adapter_obj *pao); short hpi_check_control_cache(struct hpi_control_cache *pC, struct hpi_message *phm, struct hpi_response *phr); struct hpi_control_cache *hpi_alloc_control_cache(const u32 - number_of_controls, const u32 size_in_bytes, - struct hpi_control_cache_info - *pDSP_control_buffer); + number_of_controls, const u32 size_in_bytes, u8 *pDSP_control_buffer); void hpi_free_control_cache(struct hpi_control_cache *p_cache); -void hpi_sync_control_cache(struct hpi_control_cache *pC, +void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *pC, struct hpi_message *phm, struct hpi_response *phr); + u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr); -short hpi_check_buffer_mapping(struct hpi_control_cache *p_cache, - struct hpi_message *phm, void **p, unsigned int *pN); diff --git a/sound/pci/asihpi/hpidebug.c b/sound/pci/asihpi/hpidebug.c index 949836ec913a..b52baf62791e 100644 --- a/sound/pci/asihpi/hpidebug.c +++ b/sound/pci/asihpi/hpidebug.c @@ -45,161 +45,14 @@ int hpi_debug_level_get(void) return hpi_debug_level; } -#ifdef HPIOS_DEBUG_PRINT -/* implies OS has no printf-like function */ -#include <stdarg.h> - -void hpi_debug_printf(char *fmt, ...) -{ - va_list arglist; - char buffer[128]; - - va_start(arglist, fmt); - - if (buffer[0]) - HPIOS_DEBUG_PRINT(buffer); - va_end(arglist); -} -#endif - -struct treenode { - void *array; - unsigned int num_elements; -}; - -#define make_treenode_from_array(nodename, array) \ -static void *tmp_strarray_##nodename[] = array; \ -static struct treenode nodename = { \ - &tmp_strarray_##nodename, \ - ARRAY_SIZE(tmp_strarray_##nodename) \ -}; - -#define get_treenode_elem(node_ptr, idx, type) \ - (&(*((type *)(node_ptr)->array)[idx])) - -make_treenode_from_array(hpi_control_type_strings, HPI_CONTROL_TYPE_STRINGS) - - make_treenode_from_array(hpi_subsys_strings, HPI_SUBSYS_STRINGS) - make_treenode_from_array(hpi_adapter_strings, HPI_ADAPTER_STRINGS) - make_treenode_from_array(hpi_istream_strings, HPI_ISTREAM_STRINGS) - make_treenode_from_array(hpi_ostream_strings, HPI_OSTREAM_STRINGS) - make_treenode_from_array(hpi_mixer_strings, HPI_MIXER_STRINGS) - make_treenode_from_array(hpi_node_strings, - { - "NODE is invalid object"}) - - make_treenode_from_array(hpi_control_strings, HPI_CONTROL_STRINGS) - make_treenode_from_array(hpi_nvmemory_strings, HPI_OBJ_STRINGS) - make_treenode_from_array(hpi_digitalio_strings, HPI_DIGITALIO_STRINGS) - make_treenode_from_array(hpi_watchdog_strings, HPI_WATCHDOG_STRINGS) - make_treenode_from_array(hpi_clock_strings, HPI_CLOCK_STRINGS) - make_treenode_from_array(hpi_profile_strings, HPI_PROFILE_STRINGS) - make_treenode_from_array(hpi_asyncevent_strings, HPI_ASYNCEVENT_STRINGS) -#define HPI_FUNCTION_STRINGS \ -{ \ - &hpi_subsys_strings,\ - &hpi_adapter_strings,\ - &hpi_ostream_strings,\ - &hpi_istream_strings,\ - &hpi_mixer_strings,\ - &hpi_node_strings,\ - &hpi_control_strings,\ - &hpi_nvmemory_strings,\ - &hpi_digitalio_strings,\ - &hpi_watchdog_strings,\ - &hpi_clock_strings,\ - &hpi_profile_strings,\ - &hpi_control_strings, \ - &hpi_asyncevent_strings \ -} - make_treenode_from_array(hpi_function_strings, HPI_FUNCTION_STRINGS) - - compile_time_assert(HPI_OBJ_MAXINDEX == 14, obj_list_doesnt_match); - -static char *hpi_function_string(unsigned int function) -{ - unsigned int object; - struct treenode *tmp; - - object = function / HPI_OBJ_FUNCTION_SPACING; - function = function - object * HPI_OBJ_FUNCTION_SPACING; - - if (object == 0 || object == HPI_OBJ_NODE - || object > hpi_function_strings.num_elements) - return "invalid object"; - - tmp = get_treenode_elem(&hpi_function_strings, object - 1, - struct treenode *); - - if (function == 0 || function > tmp->num_elements) - return "invalid function"; - - return get_treenode_elem(tmp, function - 1, char *); -} - void hpi_debug_message(struct hpi_message *phm, char *sz_fileline) { if (phm) { - if ((phm->object <= HPI_OBJ_MAXINDEX) && phm->object) { - u16 index = 0; - u16 attrib = 0; - int is_control = 0; - - index = phm->obj_index; - switch (phm->object) { - case HPI_OBJ_ADAPTER: - case HPI_OBJ_PROFILE: - break; - case HPI_OBJ_MIXER: - if (phm->function == - HPI_MIXER_GET_CONTROL_BY_INDEX) - index = phm->u.m.control_index; - break; - case HPI_OBJ_OSTREAM: - case HPI_OBJ_ISTREAM: - break; - - case HPI_OBJ_CONTROLEX: - case HPI_OBJ_CONTROL: - if (phm->version == 1) - attrib = HPI_CTL_ATTR(UNIVERSAL, 1); - else - attrib = phm->u.c.attribute; - is_control = 1; - break; - default: - break; - } - - if (is_control && (attrib & 0xFF00)) { - int control_type = (attrib & 0xFF00) >> 8; - int attr_index = HPI_CTL_ATTR_INDEX(attrib); - /* note the KERN facility level - is in szFileline already */ - printk("%s adapter %d %s " - "ctrl_index x%04x %s %d\n", - sz_fileline, phm->adapter_index, - hpi_function_string(phm->function), - index, - get_treenode_elem - (&hpi_control_type_strings, - control_type, char *), - attr_index); - - } else - printk("%s adapter %d %s " - "idx x%04x attr x%04x \n", - sz_fileline, phm->adapter_index, - hpi_function_string(phm->function), - index, attrib); - } else { - printk("adap=%d, invalid obj=%d, func=0x%x\n", - phm->adapter_index, phm->object, - phm->function); - } - } else - printk(KERN_ERR - "NULL message pointer to hpi_debug_message!\n"); + printk(KERN_DEBUG "HPI_MSG%d,%d,%d,%d,%d\n", phm->version, + phm->adapter_index, phm->obj_index, phm->function, + phm->u.c.attribute); + } + } void hpi_debug_data(u16 *pdata, u32 len) diff --git a/sound/pci/asihpi/hpidebug.h b/sound/pci/asihpi/hpidebug.h index a2f0952a99f0..940f54c3c538 100644 --- a/sound/pci/asihpi/hpidebug.h +++ b/sound/pci/asihpi/hpidebug.h @@ -37,7 +37,7 @@ enum { HPI_DEBUG_LEVEL_ERROR = 0, /* always log errors */ #define HPI_DEBUG_LEVEL_DEFAULT HPI_DEBUG_LEVEL_NOTICE /* an OS can define an extra flag string that is appended to - the start of each message, eg see hpios_linux.h */ + the start of each message, eg see linux kernel hpios.h */ #ifdef SOURCEFILE_NAME #define FILE_LINE SOURCEFILE_NAME ":" __stringify(__LINE__) " " @@ -45,18 +45,11 @@ enum { HPI_DEBUG_LEVEL_ERROR = 0, /* always log errors */ #define FILE_LINE __FILE__ ":" __stringify(__LINE__) " " #endif -#if defined(HPI_DEBUG) && defined(_WINDOWS) -#define HPI_DEBUGBREAK() debug_break() -#else -#define HPI_DEBUGBREAK() -#endif - #define HPI_DEBUG_ASSERT(expression) \ do { \ - if (!(expression)) {\ - printk(KERN_ERR FILE_LINE\ - "ASSERT " __stringify(expression));\ - HPI_DEBUGBREAK();\ + if (!(expression)) { \ + printk(KERN_ERR FILE_LINE \ + "ASSERT " __stringify(expression)); \ } \ } while (0) @@ -78,28 +71,27 @@ void hpi_debug_message(struct hpi_message *phm, char *sz_fileline); void hpi_debug_data(u16 *pdata, u32 len); -#define HPI_DEBUG_DATA(pdata, len) \ - do { \ +#define HPI_DEBUG_DATA(pdata, len) \ + do { \ if (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE) \ hpi_debug_data(pdata, len); \ } while (0) -#define HPI_DEBUG_MESSAGE(level, phm) \ - do { \ - if (hpi_debug_level >= HPI_DEBUG_LEVEL_##level) { \ - hpi_debug_message(phm,HPI_DEBUG_FLAG_##level \ - FILE_LINE __stringify(level));\ - } \ +#define HPI_DEBUG_MESSAGE(level, phm) \ + do { \ + if (hpi_debug_level >= HPI_DEBUG_LEVEL_##level) { \ + hpi_debug_message(phm, HPI_DEBUG_FLAG_##level \ + FILE_LINE __stringify(level)); \ + } \ } while (0) -#define HPI_DEBUG_RESPONSE(phr) \ - do { \ - if ((hpi_debug_level >= HPI_DEBUG_LEVEL_DEBUG) && (phr->error))\ - HPI_DEBUG_LOG(ERROR, \ - "HPI response - error# %d\n", \ - phr->error); \ - else if (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE) \ - HPI_DEBUG_LOG(VERBOSE, "HPI response OK\n");\ +#define HPI_DEBUG_RESPONSE(phr) \ + do { \ + if (((hpi_debug_level >= HPI_DEBUG_LEVEL_DEBUG) && \ + (phr->error)) ||\ + (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE)) \ + printk(KERN_DEBUG "HPI_RES%d,%d,%d\n", \ + phr->version, phr->error, phr->specific_error); \ } while (0) #ifndef compile_time_assert @@ -107,279 +99,4 @@ void hpi_debug_data(u16 *pdata, u32 len); typedef char msg[(cond) ? 1 : -1] #endif - /* check that size is exactly some number */ -#define function_count_check(sym, size) \ - compile_time_assert((sym##_FUNCTION_COUNT) == (size),\ - strings_match_defs_##sym) - -/* These strings should be generated using a macro which defines - the corresponding symbol values. */ -#define HPI_OBJ_STRINGS \ -{ \ - "HPI_OBJ_SUBSYSTEM", \ - "HPI_OBJ_ADAPTER", \ - "HPI_OBJ_OSTREAM", \ - "HPI_OBJ_ISTREAM", \ - "HPI_OBJ_MIXER", \ - "HPI_OBJ_NODE", \ - "HPI_OBJ_CONTROL", \ - "HPI_OBJ_NVMEMORY", \ - "HPI_OBJ_DIGITALIO", \ - "HPI_OBJ_WATCHDOG", \ - "HPI_OBJ_CLOCK", \ - "HPI_OBJ_PROFILE", \ - "HPI_OBJ_CONTROLEX" \ -} - -#define HPI_SUBSYS_STRINGS \ -{ \ - "HPI_SUBSYS_OPEN", \ - "HPI_SUBSYS_GET_VERSION", \ - "HPI_SUBSYS_GET_INFO", \ - "HPI_SUBSYS_FIND_ADAPTERS", \ - "HPI_SUBSYS_CREATE_ADAPTER",\ - "HPI_SUBSYS_CLOSE", \ - "HPI_SUBSYS_DELETE_ADAPTER", \ - "HPI_SUBSYS_DRIVER_LOAD", \ - "HPI_SUBSYS_DRIVER_UNLOAD", \ - "HPI_SUBSYS_READ_PORT_8", \ - "HPI_SUBSYS_WRITE_PORT_8", \ - "HPI_SUBSYS_GET_NUM_ADAPTERS",\ - "HPI_SUBSYS_GET_ADAPTER", \ - "HPI_SUBSYS_SET_NETWORK_INTERFACE"\ -} -function_count_check(HPI_SUBSYS, 14); - -#define HPI_ADAPTER_STRINGS \ -{ \ - "HPI_ADAPTER_OPEN", \ - "HPI_ADAPTER_CLOSE", \ - "HPI_ADAPTER_GET_INFO", \ - "HPI_ADAPTER_GET_ASSERT", \ - "HPI_ADAPTER_TEST_ASSERT", \ - "HPI_ADAPTER_SET_MODE", \ - "HPI_ADAPTER_GET_MODE", \ - "HPI_ADAPTER_ENABLE_CAPABILITY",\ - "HPI_ADAPTER_SELFTEST", \ - "HPI_ADAPTER_FIND_OBJECT", \ - "HPI_ADAPTER_QUERY_FLASH", \ - "HPI_ADAPTER_START_FLASH", \ - "HPI_ADAPTER_PROGRAM_FLASH", \ - "HPI_ADAPTER_SET_PROPERTY", \ - "HPI_ADAPTER_GET_PROPERTY", \ - "HPI_ADAPTER_ENUM_PROPERTY", \ - "HPI_ADAPTER_MODULE_INFO", \ - "HPI_ADAPTER_DEBUG_READ" \ -} - -function_count_check(HPI_ADAPTER, 18); - -#define HPI_OSTREAM_STRINGS \ -{ \ - "HPI_OSTREAM_OPEN", \ - "HPI_OSTREAM_CLOSE", \ - "HPI_OSTREAM_WRITE", \ - "HPI_OSTREAM_START", \ - "HPI_OSTREAM_STOP", \ - "HPI_OSTREAM_RESET", \ - "HPI_OSTREAM_GET_INFO", \ - "HPI_OSTREAM_QUERY_FORMAT", \ - "HPI_OSTREAM_DATA", \ - "HPI_OSTREAM_SET_VELOCITY", \ - "HPI_OSTREAM_SET_PUNCHINOUT", \ - "HPI_OSTREAM_SINEGEN", \ - "HPI_OSTREAM_ANC_RESET", \ - "HPI_OSTREAM_ANC_GET_INFO", \ - "HPI_OSTREAM_ANC_READ", \ - "HPI_OSTREAM_SET_TIMESCALE",\ - "HPI_OSTREAM_SET_FORMAT", \ - "HPI_OSTREAM_HOSTBUFFER_ALLOC", \ - "HPI_OSTREAM_HOSTBUFFER_FREE", \ - "HPI_OSTREAM_GROUP_ADD",\ - "HPI_OSTREAM_GROUP_GETMAP", \ - "HPI_OSTREAM_GROUP_RESET", \ - "HPI_OSTREAM_HOSTBUFFER_GET_INFO", \ - "HPI_OSTREAM_WAIT_START", \ -} -function_count_check(HPI_OSTREAM, 24); - -#define HPI_ISTREAM_STRINGS \ -{ \ - "HPI_ISTREAM_OPEN", \ - "HPI_ISTREAM_CLOSE", \ - "HPI_ISTREAM_SET_FORMAT", \ - "HPI_ISTREAM_READ", \ - "HPI_ISTREAM_START", \ - "HPI_ISTREAM_STOP", \ - "HPI_ISTREAM_RESET", \ - "HPI_ISTREAM_GET_INFO", \ - "HPI_ISTREAM_QUERY_FORMAT", \ - "HPI_ISTREAM_ANC_RESET", \ - "HPI_ISTREAM_ANC_GET_INFO", \ - "HPI_ISTREAM_ANC_WRITE", \ - "HPI_ISTREAM_HOSTBUFFER_ALLOC",\ - "HPI_ISTREAM_HOSTBUFFER_FREE", \ - "HPI_ISTREAM_GROUP_ADD", \ - "HPI_ISTREAM_GROUP_GETMAP", \ - "HPI_ISTREAM_GROUP_RESET", \ - "HPI_ISTREAM_HOSTBUFFER_GET_INFO", \ - "HPI_ISTREAM_WAIT_START", \ -} -function_count_check(HPI_ISTREAM, 19); - -#define HPI_MIXER_STRINGS \ -{ \ - "HPI_MIXER_OPEN", \ - "HPI_MIXER_CLOSE", \ - "HPI_MIXER_GET_INFO", \ - "HPI_MIXER_GET_NODE_INFO", \ - "HPI_MIXER_GET_CONTROL", \ - "HPI_MIXER_SET_CONNECTION", \ - "HPI_MIXER_GET_CONNECTIONS", \ - "HPI_MIXER_GET_CONTROL_BY_INDEX", \ - "HPI_MIXER_GET_CONTROL_ARRAY_BY_INDEX", \ - "HPI_MIXER_GET_CONTROL_MULTIPLE_VALUES", \ - "HPI_MIXER_STORE", \ -} -function_count_check(HPI_MIXER, 11); - -#define HPI_CONTROL_STRINGS \ -{ \ - "HPI_CONTROL_GET_INFO", \ - "HPI_CONTROL_GET_STATE", \ - "HPI_CONTROL_SET_STATE" \ -} -function_count_check(HPI_CONTROL, 3); - -#define HPI_NVMEMORY_STRINGS \ -{ \ - "HPI_NVMEMORY_OPEN", \ - "HPI_NVMEMORY_READ_BYTE", \ - "HPI_NVMEMORY_WRITE_BYTE" \ -} -function_count_check(HPI_NVMEMORY, 3); - -#define HPI_DIGITALIO_STRINGS \ -{ \ - "HPI_GPIO_OPEN", \ - "HPI_GPIO_READ_BIT", \ - "HPI_GPIO_WRITE_BIT", \ - "HPI_GPIO_READ_ALL", \ - "HPI_GPIO_WRITE_STATUS"\ -} -function_count_check(HPI_GPIO, 5); - -#define HPI_WATCHDOG_STRINGS \ -{ \ - "HPI_WATCHDOG_OPEN", \ - "HPI_WATCHDOG_SET_TIME", \ - "HPI_WATCHDOG_PING" \ -} - -#define HPI_CLOCK_STRINGS \ -{ \ - "HPI_CLOCK_OPEN", \ - "HPI_CLOCK_SET_TIME", \ - "HPI_CLOCK_GET_TIME" \ -} - -#define HPI_PROFILE_STRINGS \ -{ \ - "HPI_PROFILE_OPEN_ALL", \ - "HPI_PROFILE_START_ALL", \ - "HPI_PROFILE_STOP_ALL", \ - "HPI_PROFILE_GET", \ - "HPI_PROFILE_GET_IDLECOUNT", \ - "HPI_PROFILE_GET_NAME", \ - "HPI_PROFILE_GET_UTILIZATION" \ -} -function_count_check(HPI_PROFILE, 7); - -#define HPI_ASYNCEVENT_STRINGS \ -{ \ - "HPI_ASYNCEVENT_OPEN",\ - "HPI_ASYNCEVENT_CLOSE ",\ - "HPI_ASYNCEVENT_WAIT",\ - "HPI_ASYNCEVENT_GETCOUNT",\ - "HPI_ASYNCEVENT_GET",\ - "HPI_ASYNCEVENT_SENDEVENTS"\ -} -function_count_check(HPI_ASYNCEVENT, 6); - -#define HPI_CONTROL_TYPE_STRINGS \ -{ \ - "null control", \ - "HPI_CONTROL_CONNECTION", \ - "HPI_CONTROL_VOLUME", \ - "HPI_CONTROL_METER", \ - "HPI_CONTROL_MUTE", \ - "HPI_CONTROL_MULTIPLEXER", \ - "HPI_CONTROL_AESEBU_TRANSMITTER", \ - "HPI_CONTROL_AESEBU_RECEIVER", \ - "HPI_CONTROL_LEVEL", \ - "HPI_CONTROL_TUNER", \ - "HPI_CONTROL_ONOFFSWITCH", \ - "HPI_CONTROL_VOX", \ - "HPI_CONTROL_AES18_TRANSMITTER", \ - "HPI_CONTROL_AES18_RECEIVER", \ - "HPI_CONTROL_AES18_BLOCKGENERATOR", \ - "HPI_CONTROL_CHANNEL_MODE", \ - "HPI_CONTROL_BITSTREAM", \ - "HPI_CONTROL_SAMPLECLOCK", \ - "HPI_CONTROL_MICROPHONE", \ - "HPI_CONTROL_PARAMETRIC_EQ", \ - "HPI_CONTROL_COMPANDER", \ - "HPI_CONTROL_COBRANET", \ - "HPI_CONTROL_TONE_DETECT", \ - "HPI_CONTROL_SILENCE_DETECT", \ - "HPI_CONTROL_PAD", \ - "HPI_CONTROL_SRC" ,\ - "HPI_CONTROL_UNIVERSAL" \ -} - -compile_time_assert((HPI_CONTROL_LAST_INDEX + 1 == 27), - controltype_strings_match_defs); - -#define HPI_SOURCENODE_STRINGS \ -{ \ - "no source", \ - "HPI_SOURCENODE_OSTREAM", \ - "HPI_SOURCENODE_LINEIN", \ - "HPI_SOURCENODE_AESEBU_IN", \ - "HPI_SOURCENODE_TUNER", \ - "HPI_SOURCENODE_RF", \ - "HPI_SOURCENODE_CLOCK_SOURCE", \ - "HPI_SOURCENODE_RAW_BITSTREAM", \ - "HPI_SOURCENODE_MICROPHONE", \ - "HPI_SOURCENODE_COBRANET", \ - "HPI_SOURCENODE_ANALOG", \ - "HPI_SOURCENODE_ADAPTER" \ -} - -compile_time_assert((HPI_SOURCENODE_LAST_INDEX - HPI_SOURCENODE_NONE + 1) == - (12), sourcenode_strings_match_defs); - -#define HPI_DESTNODE_STRINGS \ -{ \ - "no destination", \ - "HPI_DESTNODE_ISTREAM", \ - "HPI_DESTNODE_LINEOUT", \ - "HPI_DESTNODE_AESEBU_OUT", \ - "HPI_DESTNODE_RF", \ - "HPI_DESTNODE_SPEAKER", \ - "HPI_DESTNODE_COBRANET", \ - "HPI_DESTNODE_ANALOG" \ -} -compile_time_assert((HPI_DESTNODE_LAST_INDEX - HPI_DESTNODE_NONE + 1) == (8), - destnode_strings_match_defs); - -#define HPI_CONTROL_CHANNEL_MODE_STRINGS \ -{ \ - "XXX HPI_CHANNEL_MODE_ERROR XXX", \ - "HPI_CHANNEL_MODE_NORMAL", \ - "HPI_CHANNEL_MODE_SWAP", \ - "HPI_CHANNEL_MODE_LEFT_ONLY", \ - "HPI_CHANNEL_MODE_RIGHT_ONLY" \ -} - -#endif /* _HPIDEBUG_H */ +#endif /* _HPIDEBUG_H_ */ diff --git a/sound/pci/asihpi/hpidspcd.c b/sound/pci/asihpi/hpidspcd.c index 9b10d9a5c255..fb311d8c05bf 100644 --- a/sound/pci/asihpi/hpidspcd.c +++ b/sound/pci/asihpi/hpidspcd.c @@ -71,47 +71,50 @@ short hpi_dsp_code_open(u32 adapter, struct dsp_code *ps_dsp_code, int err; sprintf(fw_name, "asihpi/dsp%04x.bin", adapter); - HPI_DEBUG_LOG(INFO, "requesting firmware for %s\n", fw_name); err = request_firmware(&ps_firmware, fw_name, &ps_dsp_code->ps_dev->dev); + if (err != 0) { - HPI_DEBUG_LOG(ERROR, "%d, request_firmware failed for %s\n", - err, fw_name); + dev_printk(KERN_ERR, &ps_dsp_code->ps_dev->dev, + "%d, request_firmware failed for %s\n", err, + fw_name); goto error1; } if (ps_firmware->size < sizeof(header)) { - HPI_DEBUG_LOG(ERROR, "header size too small %s\n", fw_name); + dev_printk(KERN_ERR, &ps_dsp_code->ps_dev->dev, + "Header size too small %s\n", fw_name); goto error2; } memcpy(&header, ps_firmware->data, sizeof(header)); if (header.adapter != adapter) { - HPI_DEBUG_LOG(ERROR, "adapter type incorrect %4x != %4x\n", - header.adapter, adapter); + dev_printk(KERN_ERR, &ps_dsp_code->ps_dev->dev, + "Adapter type incorrect %4x != %4x\n", header.adapter, + adapter); goto error2; } if (header.size != ps_firmware->size) { - HPI_DEBUG_LOG(ERROR, "code size wrong %d != %ld\n", - header.size, (unsigned long)ps_firmware->size); + dev_printk(KERN_ERR, &ps_dsp_code->ps_dev->dev, + "Code size wrong %d != %ld\n", header.size, + (unsigned long)ps_firmware->size); goto error2; } - if (header.version / 10000 != HPI_VER_DECIMAL / 10000) { - HPI_DEBUG_LOG(ERROR, - "firmware major version mismatch " - "DSP image %d != driver %d\n", header.version, + if (header.version / 100 != HPI_VER_DECIMAL / 100) { + dev_printk(KERN_ERR, &ps_dsp_code->ps_dev->dev, + "Incompatible firmware version " + "DSP image %d != Driver %d\n", header.version, HPI_VER_DECIMAL); goto error2; } if (header.version != HPI_VER_DECIMAL) { - HPI_DEBUG_LOG(WARNING, - "version mismatch DSP image %d != driver %d\n", + dev_printk(KERN_WARNING, &ps_dsp_code->ps_dev->dev, + "Firmware: release version mismatch DSP image %d != Driver %d\n", header.version, HPI_VER_DECIMAL); - /* goto error2; still allow driver to load */ } - HPI_DEBUG_LOG(INFO, "dsp code %s opened\n", fw_name); + HPI_DEBUG_LOG(DEBUG, "dsp code %s opened\n", fw_name); ps_dsp_code->ps_firmware = ps_firmware; ps_dsp_code->block_length = header.size / sizeof(u32); ps_dsp_code->word_count = sizeof(header) / sizeof(u32); @@ -148,7 +151,7 @@ void hpi_dsp_code_rewind(struct dsp_code *ps_dsp_code) short hpi_dsp_code_read_word(struct dsp_code *ps_dsp_code, u32 *pword) { if (ps_dsp_code->word_count + 1 > ps_dsp_code->block_length) - return (HPI_ERROR_DSP_FILE_FORMAT); + return HPI_ERROR_DSP_FILE_FORMAT; *pword = ((u32 *)(ps_dsp_code->ps_firmware->data))[ps_dsp_code-> word_count]; diff --git a/sound/pci/asihpi/hpidspcd.h b/sound/pci/asihpi/hpidspcd.h index d7c240398225..65f0ca732704 100644 --- a/sound/pci/asihpi/hpidspcd.h +++ b/sound/pci/asihpi/hpidspcd.h @@ -87,7 +87,7 @@ void hpi_dsp_code_rewind(struct dsp_code *ps_dsp_code); */ short hpi_dsp_code_read_word(struct dsp_code *ps_dsp_code, /**< DSP code descriptor */ - u32 *pword /**< where to store the read word */ + u32 *pword /**< Where to store the read word */ ); /** Get a block of dsp code into an internal buffer, and provide a pointer to diff --git a/sound/pci/asihpi/hpifunc.c b/sound/pci/asihpi/hpifunc.c index 1e92eb6dd509..c38fc9487560 100644 --- a/sound/pci/asihpi/hpifunc.c +++ b/sound/pci/asihpi/hpifunc.c @@ -30,16 +30,25 @@ u32 hpi_indexes_to_handle(const char c_object, const u16 adapter_index, return handle.w; } -void hpi_handle_to_indexes(const u32 handle, u16 *pw_adapter_index, - u16 *pw_object_index) +static u16 hpi_handle_indexes(const u32 h, u16 *p1, u16 *p2) { union handle_word uhandle; - uhandle.w = handle; + if (!h) + return HPI_ERROR_INVALID_HANDLE; + + uhandle.w = h; + + *p1 = (u16)uhandle.h.adapter_index; + if (p2) + *p2 = (u16)uhandle.h.obj_index; - if (pw_adapter_index) - *pw_adapter_index = (u16)uhandle.h.adapter_index; - if (pw_object_index) - *pw_object_index = (u16)uhandle.h.obj_index; + return 0; +} + +void hpi_handle_to_indexes(const u32 handle, u16 *pw_adapter_index, + u16 *pw_object_index) +{ + hpi_handle_indexes(handle, pw_adapter_index, pw_object_index); } char hpi_handle_object(const u32 handle) @@ -49,22 +58,6 @@ char hpi_handle_object(const u32 handle) return (char)uhandle.h.obj_type; } -#define u32TOINDEX(h, i1) \ -do {\ - if (h == 0) \ - return HPI_ERROR_INVALID_OBJ; \ - else \ - hpi_handle_to_indexes(h, i1, NULL); \ -} while (0) - -#define u32TOINDEXES(h, i1, i2) \ -do {\ - if (h == 0) \ - return HPI_ERROR_INVALID_OBJ; \ - else \ - hpi_handle_to_indexes(h, i1, i2);\ -} while (0) - void hpi_format_to_msg(struct hpi_msg_format *pMF, const struct hpi_format *pF) { @@ -94,52 +87,13 @@ void hpi_stream_response_to_legacy(struct hpi_stream_res *pSR) pSR->u.legacy_stream_info.state = pSR->u.stream_info.state; } -static struct hpi_hsubsys gh_subsys; - -struct hpi_hsubsys *hpi_subsys_create(void) +static inline void hpi_send_recvV1(struct hpi_message_header *m, + struct hpi_response_header *r) { - struct hpi_message hm; - struct hpi_response hr; - - memset(&gh_subsys, 0, sizeof(struct hpi_hsubsys)); - - { - hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, - HPI_SUBSYS_OPEN); - hpi_send_recv(&hm, &hr); - - if (hr.error == 0) - return &gh_subsys; - - } - return NULL; -} - -void hpi_subsys_free(const struct hpi_hsubsys *ph_subsys) -{ - struct hpi_message hm; - struct hpi_response hr; - - hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, - HPI_SUBSYS_CLOSE); - hpi_send_recv(&hm, &hr); - + hpi_send_recv((struct hpi_message *)m, (struct hpi_response *)r); } -u16 hpi_subsys_get_version(const struct hpi_hsubsys *ph_subsys, u32 *pversion) -{ - struct hpi_message hm; - struct hpi_response hr; - - hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, - HPI_SUBSYS_GET_VERSION); - hpi_send_recv(&hm, &hr); - *pversion = hr.u.s.version; - return hr.error; -} - -u16 hpi_subsys_get_version_ex(const struct hpi_hsubsys *ph_subsys, - u32 *pversion_ex) +u16 hpi_subsys_get_version_ex(u32 *pversion_ex) { struct hpi_message hm; struct hpi_response hr; @@ -151,51 +105,8 @@ u16 hpi_subsys_get_version_ex(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_subsys_get_info(const struct hpi_hsubsys *ph_subsys, u32 *pversion, - u16 *pw_num_adapters, u16 aw_adapter_list[], u16 list_length) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, - HPI_SUBSYS_GET_INFO); - - hpi_send_recv(&hm, &hr); - - *pversion = hr.u.s.version; - if (list_length > HPI_MAX_ADAPTERS) - memcpy(aw_adapter_list, &hr.u.s.aw_adapter_list, - HPI_MAX_ADAPTERS); - else - memcpy(aw_adapter_list, &hr.u.s.aw_adapter_list, list_length); - *pw_num_adapters = hr.u.s.num_adapters; - return hr.error; -} - -u16 hpi_subsys_find_adapters(const struct hpi_hsubsys *ph_subsys, - u16 *pw_num_adapters, u16 aw_adapter_list[], u16 list_length) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, - HPI_SUBSYS_FIND_ADAPTERS); - - hpi_send_recv(&hm, &hr); - - if (list_length > HPI_MAX_ADAPTERS) { - memcpy(aw_adapter_list, &hr.u.s.aw_adapter_list, - HPI_MAX_ADAPTERS * sizeof(u16)); - memset(&aw_adapter_list[HPI_MAX_ADAPTERS], 0, - (list_length - HPI_MAX_ADAPTERS) * sizeof(u16)); - } else - memcpy(aw_adapter_list, &hr.u.s.aw_adapter_list, - list_length * sizeof(u16)); - *pw_num_adapters = hr.u.s.num_adapters; - - return hr.error; -} - -u16 hpi_subsys_create_adapter(const struct hpi_hsubsys *ph_subsys, - const struct hpi_resource *p_resource, u16 *pw_adapter_index) +u16 hpi_subsys_create_adapter(const struct hpi_resource *p_resource, + u16 *pw_adapter_index) { struct hpi_message hm; struct hpi_response hr; @@ -210,20 +121,18 @@ u16 hpi_subsys_create_adapter(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_subsys_delete_adapter(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index) +u16 hpi_subsys_delete_adapter(u16 adapter_index) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_DELETE_ADAPTER); - hm.adapter_index = adapter_index; + hm.obj_index = adapter_index; hpi_send_recv(&hm, &hr); return hr.error; } -u16 hpi_subsys_get_num_adapters(const struct hpi_hsubsys *ph_subsys, - int *pn_num_adapters) +u16 hpi_subsys_get_num_adapters(int *pn_num_adapters) { struct hpi_message hm; struct hpi_response hr; @@ -234,35 +143,22 @@ u16 hpi_subsys_get_num_adapters(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_subsys_get_adapter(const struct hpi_hsubsys *ph_subsys, int iterator, - u32 *padapter_index, u16 *pw_adapter_type) +u16 hpi_subsys_get_adapter(int iterator, u32 *padapter_index, + u16 *pw_adapter_type) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_GET_ADAPTER); - hm.adapter_index = (u16)iterator; + hm.obj_index = (u16)iterator; hpi_send_recv(&hm, &hr); *padapter_index = (int)hr.u.s.adapter_index; - *pw_adapter_type = hr.u.s.aw_adapter_list[0]; - return hr.error; -} + *pw_adapter_type = hr.u.s.adapter_type; -u16 hpi_subsys_set_host_network_interface(const struct hpi_hsubsys *ph_subsys, - const char *sz_interface) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, - HPI_SUBSYS_SET_NETWORK_INTERFACE); - if (sz_interface == NULL) - return HPI_ERROR_INVALID_RESOURCE; - hm.u.s.resource.r.net_if = sz_interface; - hpi_send_recv(&hm, &hr); return hr.error; } -u16 hpi_adapter_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index) +u16 hpi_adapter_open(u16 adapter_index) { struct hpi_message hm; struct hpi_response hr; @@ -276,7 +172,7 @@ u16 hpi_adapter_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index) } -u16 hpi_adapter_close(const struct hpi_hsubsys *ph_subsys, u16 adapter_index) +u16 hpi_adapter_close(u16 adapter_index) { struct hpi_message hm; struct hpi_response hr; @@ -289,15 +185,14 @@ u16 hpi_adapter_close(const struct hpi_hsubsys *ph_subsys, u16 adapter_index) return hr.error; } -u16 hpi_adapter_set_mode(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u32 adapter_mode) +u16 hpi_adapter_set_mode(u16 adapter_index, u32 adapter_mode) { - return hpi_adapter_set_mode_ex(ph_subsys, adapter_index, adapter_mode, + return hpi_adapter_set_mode_ex(adapter_index, adapter_mode, HPI_ADAPTER_MODE_SET); } -u16 hpi_adapter_set_mode_ex(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u32 adapter_mode, u16 query_or_set) +u16 hpi_adapter_set_mode_ex(u16 adapter_index, u32 adapter_mode, + u16 query_or_set) { struct hpi_message hm; struct hpi_response hr; @@ -305,14 +200,13 @@ u16 hpi_adapter_set_mode_ex(const struct hpi_hsubsys *ph_subsys, hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, HPI_ADAPTER_SET_MODE); hm.adapter_index = adapter_index; - hm.u.a.adapter_mode = adapter_mode; - hm.u.a.assert_id = query_or_set; + hm.u.ax.mode.adapter_mode = adapter_mode; + hm.u.ax.mode.query_or_set = query_or_set; hpi_send_recv(&hm, &hr); return hr.error; } -u16 hpi_adapter_get_mode(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u32 *padapter_mode) +u16 hpi_adapter_get_mode(u16 adapter_index, u32 *padapter_mode) { struct hpi_message hm; struct hpi_response hr; @@ -321,13 +215,13 @@ u16 hpi_adapter_get_mode(const struct hpi_hsubsys *ph_subsys, hm.adapter_index = adapter_index; hpi_send_recv(&hm, &hr); if (padapter_mode) - *padapter_mode = hr.u.a.serial_number; + *padapter_mode = hr.u.ax.mode.adapter_mode; return hr.error; } -u16 hpi_adapter_get_info(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u16 *pw_num_outstreams, u16 *pw_num_instreams, - u16 *pw_version, u32 *pserial_number, u16 *pw_adapter_type) +u16 hpi_adapter_get_info(u16 adapter_index, u16 *pw_num_outstreams, + u16 *pw_num_instreams, u16 *pw_version, u32 *pserial_number, + u16 *pw_adapter_type) { struct hpi_message hm; struct hpi_response hr; @@ -337,18 +231,17 @@ u16 hpi_adapter_get_info(const struct hpi_hsubsys *ph_subsys, hpi_send_recv(&hm, &hr); - *pw_adapter_type = hr.u.a.adapter_type; - *pw_num_outstreams = hr.u.a.num_outstreams; - *pw_num_instreams = hr.u.a.num_instreams; - *pw_version = hr.u.a.version; - *pserial_number = hr.u.a.serial_number; + *pw_adapter_type = hr.u.ax.info.adapter_type; + *pw_num_outstreams = hr.u.ax.info.num_outstreams; + *pw_num_instreams = hr.u.ax.info.num_instreams; + *pw_version = hr.u.ax.info.version; + *pserial_number = hr.u.ax.info.serial_number; return hr.error; } -u16 hpi_adapter_get_module_by_index(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u16 module_index, u16 *pw_num_outputs, - u16 *pw_num_inputs, u16 *pw_version, u32 *pserial_number, - u16 *pw_module_type, u32 *ph_module) +u16 hpi_adapter_get_module_by_index(u16 adapter_index, u16 module_index, + u16 *pw_num_outputs, u16 *pw_num_inputs, u16 *pw_version, + u32 *pserial_number, u16 *pw_module_type, u32 *ph_module) { struct hpi_message hm; struct hpi_response hr; @@ -360,173 +253,18 @@ u16 hpi_adapter_get_module_by_index(const struct hpi_hsubsys *ph_subsys, hpi_send_recv(&hm, &hr); - *pw_module_type = hr.u.a.adapter_type; - *pw_num_outputs = hr.u.a.num_outstreams; - *pw_num_inputs = hr.u.a.num_instreams; - *pw_version = hr.u.a.version; - *pserial_number = hr.u.a.serial_number; + *pw_module_type = hr.u.ax.info.adapter_type; + *pw_num_outputs = hr.u.ax.info.num_outstreams; + *pw_num_inputs = hr.u.ax.info.num_instreams; + *pw_version = hr.u.ax.info.version; + *pserial_number = hr.u.ax.info.serial_number; *ph_module = 0; return hr.error; } -u16 hpi_adapter_get_assert(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u16 *assert_present, char *psz_assert, - u16 *pw_line_number) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, - HPI_ADAPTER_GET_ASSERT); - hm.adapter_index = adapter_index; - hpi_send_recv(&hm, &hr); - - *assert_present = 0; - - if (!hr.error) { - - *pw_line_number = (u16)hr.u.a.serial_number; - if (*pw_line_number) { - - int i; - char *src = (char *)hr.u.a.sz_adapter_assert; - char *dst = psz_assert; - - *assert_present = 1; - - for (i = 0; i < HPI_STRING_LEN; i++) { - char c; - c = *src++; - *dst++ = c; - if (c == 0) - break; - } - - } - } - return hr.error; -} - -u16 hpi_adapter_get_assert_ex(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u16 *assert_present, char *psz_assert, - u32 *pline_number, u16 *pw_assert_on_dsp) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, - HPI_ADAPTER_GET_ASSERT); - hm.adapter_index = adapter_index; - - hpi_send_recv(&hm, &hr); - - *assert_present = 0; - - if (!hr.error) { - - *pline_number = hr.u.a.serial_number; - - *assert_present = hr.u.a.adapter_type; - - *pw_assert_on_dsp = hr.u.a.adapter_index; - - if (!*assert_present && *pline_number) - - *assert_present = 1; - - if (*assert_present) { - - int i; - char *src = (char *)hr.u.a.sz_adapter_assert; - char *dst = psz_assert; - - for (i = 0; i < HPI_STRING_LEN; i++) { - char c; - c = *src++; - *dst++ = c; - if (c == 0) - break; - } - - } else { - *psz_assert = 0; - } - } - return hr.error; -} - -u16 hpi_adapter_test_assert(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u16 assert_id) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, - HPI_ADAPTER_TEST_ASSERT); - hm.adapter_index = adapter_index; - hm.u.a.assert_id = assert_id; - - hpi_send_recv(&hm, &hr); - - return hr.error; -} - -u16 hpi_adapter_enable_capability(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u16 capability, u32 key) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, - HPI_ADAPTER_ENABLE_CAPABILITY); - hm.adapter_index = adapter_index; - hm.u.a.assert_id = capability; - hm.u.a.adapter_mode = key; - - hpi_send_recv(&hm, &hr); - - return hr.error; -} - -u16 hpi_adapter_self_test(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, - HPI_ADAPTER_SELFTEST); - hm.adapter_index = adapter_index; - hpi_send_recv(&hm, &hr); - return hr.error; -} - -u16 hpi_adapter_debug_read(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u32 dsp_address, char *p_buffer, int *count_bytes) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, - HPI_ADAPTER_DEBUG_READ); - - hr.size = sizeof(hr); - - hm.adapter_index = adapter_index; - hm.u.ax.debug_read.dsp_address = dsp_address; - - if (*count_bytes > (int)sizeof(hr.u.bytes)) - *count_bytes = sizeof(hr.u.bytes); - - hm.u.ax.debug_read.count_bytes = *count_bytes; - - hpi_send_recv(&hm, &hr); - - if (!hr.error) { - *count_bytes = hr.size - 12; - memcpy(p_buffer, &hr.u.bytes, *count_bytes); - } else - *count_bytes = 0; - return hr.error; -} - -u16 hpi_adapter_set_property(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u16 property, u16 parameter1, u16 parameter2) +u16 hpi_adapter_set_property(u16 adapter_index, u16 property, u16 parameter1, + u16 parameter2) { struct hpi_message hm; struct hpi_response hr; @@ -542,9 +280,8 @@ u16 hpi_adapter_set_property(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_adapter_get_property(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u16 property, u16 *pw_parameter1, - u16 *pw_parameter2) +u16 hpi_adapter_get_property(u16 adapter_index, u16 property, + u16 *pw_parameter1, u16 *pw_parameter2) { struct hpi_message hm; struct hpi_response hr; @@ -564,9 +301,8 @@ u16 hpi_adapter_get_property(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_adapter_enumerate_property(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u16 index, u16 what_to_enumerate, - u16 property_index, u32 *psetting) +u16 hpi_adapter_enumerate_property(u16 adapter_index, u16 index, + u16 what_to_enumerate, u16 property_index, u32 *psetting) { return 0; } @@ -574,7 +310,7 @@ u16 hpi_adapter_enumerate_property(const struct hpi_hsubsys *ph_subsys, u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format, u32 sample_rate, u32 bit_rate, u32 attributes) { - u16 error = 0; + u16 err = 0; struct hpi_msg_format fmt; switch (channels) { @@ -586,8 +322,8 @@ u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format, case 16: break; default: - error = HPI_ERROR_INVALID_CHANNELS; - return error; + err = HPI_ERROR_INVALID_CHANNELS; + return err; } fmt.channels = channels; @@ -610,17 +346,17 @@ u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format, case HPI_FORMAT_OEM2: break; default: - error = HPI_ERROR_INVALID_FORMAT; - return error; + err = HPI_ERROR_INVALID_FORMAT; + return err; } fmt.format = format; if (sample_rate < 8000L) { - error = HPI_ERROR_INCOMPATIBLE_SAMPLERATE; + err = HPI_ERROR_INCOMPATIBLE_SAMPLERATE; sample_rate = 8000L; } if (sample_rate > 200000L) { - error = HPI_ERROR_INCOMPATIBLE_SAMPLERATE; + err = HPI_ERROR_INCOMPATIBLE_SAMPLERATE; sample_rate = 200000L; } fmt.sample_rate = sample_rate; @@ -651,10 +387,10 @@ u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format, if ((channels == 1) && (attributes != HPI_MPEG_MODE_DEFAULT)) { attributes = HPI_MPEG_MODE_DEFAULT; - error = HPI_ERROR_INVALID_FORMAT; + err = HPI_ERROR_INVALID_FORMAT; } else if (attributes > HPI_MPEG_MODE_DUALCHANNEL) { attributes = HPI_MPEG_MODE_DEFAULT; - error = HPI_ERROR_INVALID_FORMAT; + err = HPI_ERROR_INVALID_FORMAT; } fmt.attributes = attributes; break; @@ -663,7 +399,7 @@ u16 hpi_format_create(struct hpi_format *p_format, u16 channels, u16 format, } hpi_msg_to_format(p_format, &fmt); - return error; + return err; } u16 hpi_stream_estimate_buffer_size(struct hpi_format *p_format, @@ -712,8 +448,8 @@ u16 hpi_stream_estimate_buffer_size(struct hpi_format *p_format, return 0; } -u16 hpi_outstream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, - u16 outstream_index, u32 *ph_outstream) +u16 hpi_outstream_open(u16 adapter_index, u16 outstream_index, + u32 *ph_outstream) { struct hpi_message hm; struct hpi_response hr; @@ -733,38 +469,41 @@ u16 hpi_outstream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, return hr.error; } -u16 hpi_outstream_close(const struct hpi_hsubsys *ph_subsys, u32 h_outstream) +u16 hpi_outstream_close(u32 h_outstream) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_HOSTBUFFER_FREE); - u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; + hpi_send_recv(&hm, &hr); hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_GROUP_RESET); - u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index); hpi_send_recv(&hm, &hr); hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_CLOSE); - u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index); hpi_send_recv(&hm, &hr); return hr.error; } -u16 hpi_outstream_get_info_ex(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, u16 *pw_state, u32 *pbuffer_size, u32 *pdata_to_play, - u32 *psamples_played, u32 *pauxiliary_data_to_play) +u16 hpi_outstream_get_info_ex(u32 h_outstream, u16 *pw_state, + u32 *pbuffer_size, u32 *pdata_to_play, u32 *psamples_played, + u32 *pauxiliary_data_to_play) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_GET_INFO); - u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hpi_send_recv(&hm, &hr); @@ -782,15 +521,15 @@ u16 hpi_outstream_get_info_ex(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_outstream_write_buf(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, const u8 *pb_data, u32 bytes_to_write, - const struct hpi_format *p_format) +u16 hpi_outstream_write_buf(u32 h_outstream, const u8 *pb_data, + u32 bytes_to_write, const struct hpi_format *p_format) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_WRITE); - u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.d.u.data.pb_data = (u8 *)pb_data; hm.u.d.u.data.data_size = bytes_to_write; @@ -801,82 +540,85 @@ u16 hpi_outstream_write_buf(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_outstream_start(const struct hpi_hsubsys *ph_subsys, u32 h_outstream) +u16 hpi_outstream_start(u32 h_outstream) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_START); - u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hpi_send_recv(&hm, &hr); return hr.error; } -u16 hpi_outstream_wait_start(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream) +u16 hpi_outstream_wait_start(u32 h_outstream) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_WAIT_START); - u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hpi_send_recv(&hm, &hr); return hr.error; } -u16 hpi_outstream_stop(const struct hpi_hsubsys *ph_subsys, u32 h_outstream) +u16 hpi_outstream_stop(u32 h_outstream) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_STOP); - u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hpi_send_recv(&hm, &hr); return hr.error; } -u16 hpi_outstream_sinegen(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream) +u16 hpi_outstream_sinegen(u32 h_outstream) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_SINEGEN); - u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hpi_send_recv(&hm, &hr); return hr.error; } -u16 hpi_outstream_reset(const struct hpi_hsubsys *ph_subsys, u32 h_outstream) +u16 hpi_outstream_reset(u32 h_outstream) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_RESET); - u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hpi_send_recv(&hm, &hr); return hr.error; } -u16 hpi_outstream_query_format(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, struct hpi_format *p_format) +u16 hpi_outstream_query_format(u32 h_outstream, struct hpi_format *p_format) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_QUERY_FORMAT); - u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hpi_format_to_msg(&hm.u.d.u.data.format, p_format); @@ -885,15 +627,15 @@ u16 hpi_outstream_query_format(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_outstream_set_format(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, struct hpi_format *p_format) +u16 hpi_outstream_set_format(u32 h_outstream, struct hpi_format *p_format) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_SET_FORMAT); - u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hpi_format_to_msg(&hm.u.d.u.data.format, p_format); @@ -902,15 +644,15 @@ u16 hpi_outstream_set_format(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_outstream_set_velocity(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, short velocity) +u16 hpi_outstream_set_velocity(u32 h_outstream, short velocity) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_SET_VELOCITY); - u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.d.u.velocity = velocity; hpi_send_recv(&hm, &hr); @@ -918,15 +660,16 @@ u16 hpi_outstream_set_velocity(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_outstream_set_punch_in_out(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, u32 punch_in_sample, u32 punch_out_sample) +u16 hpi_outstream_set_punch_in_out(u32 h_outstream, u32 punch_in_sample, + u32 punch_out_sample) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_SET_PUNCHINOUT); - u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.d.u.pio.punch_in_sample = punch_in_sample; hm.u.d.u.pio.punch_out_sample = punch_out_sample; @@ -936,29 +679,29 @@ u16 hpi_outstream_set_punch_in_out(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_outstream_ancillary_reset(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, u16 mode) +u16 hpi_outstream_ancillary_reset(u32 h_outstream, u16 mode) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_ANC_RESET); - u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.d.u.data.format.channels = mode; hpi_send_recv(&hm, &hr); return hr.error; } -u16 hpi_outstream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, u32 *pframes_available) +u16 hpi_outstream_ancillary_get_info(u32 h_outstream, u32 *pframes_available) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_ANC_GET_INFO); - u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hpi_send_recv(&hm, &hr); if (hr.error == 0) { if (pframes_available) @@ -969,8 +712,8 @@ u16 hpi_outstream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_outstream_ancillary_read(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, struct hpi_anc_frame *p_anc_frame_buffer, +u16 hpi_outstream_ancillary_read(u32 h_outstream, + struct hpi_anc_frame *p_anc_frame_buffer, u32 anc_frame_buffer_size_in_bytes, u32 number_of_ancillary_frames_to_read) { @@ -979,7 +722,8 @@ u16 hpi_outstream_ancillary_read(const struct hpi_hsubsys *ph_subsys, hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_ANC_READ); - u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.d.u.data.pb_data = (u8 *)p_anc_frame_buffer; hm.u.d.u.data.data_size = number_of_ancillary_frames_to_read * @@ -987,19 +731,19 @@ u16 hpi_outstream_ancillary_read(const struct hpi_hsubsys *ph_subsys, if (hm.u.d.u.data.data_size <= anc_frame_buffer_size_in_bytes) hpi_send_recv(&hm, &hr); else - hr.error = HPI_ERROR_INVALID_DATA_TRANSFER; + hr.error = HPI_ERROR_INVALID_DATASIZE; return hr.error; } -u16 hpi_outstream_set_time_scale(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, u32 time_scale) +u16 hpi_outstream_set_time_scale(u32 h_outstream, u32 time_scale) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_SET_TIMESCALE); - u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.d.u.time_scale = time_scale; @@ -1008,22 +752,21 @@ u16 hpi_outstream_set_time_scale(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_outstream_host_buffer_allocate(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, u32 size_in_bytes) +u16 hpi_outstream_host_buffer_allocate(u32 h_outstream, u32 size_in_bytes) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_HOSTBUFFER_ALLOC); - u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.d.u.data.data_size = size_in_bytes; hpi_send_recv(&hm, &hr); return hr.error; } -u16 hpi_outstream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, u8 **pp_buffer, +u16 hpi_outstream_host_buffer_get_info(u32 h_outstream, u8 **pp_buffer, struct hpi_hostbuffer_status **pp_status) { struct hpi_message hm; @@ -1031,7 +774,8 @@ u16 hpi_outstream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys, hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_HOSTBUFFER_GET_INFO); - u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hpi_send_recv(&hm, &hr); if (hr.error == 0) { @@ -1043,21 +787,20 @@ u16 hpi_outstream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_outstream_host_buffer_free(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream) +u16 hpi_outstream_host_buffer_free(u32 h_outstream) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_HOSTBUFFER_FREE); - u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hpi_send_recv(&hm, &hr); return hr.error; } -u16 hpi_outstream_group_add(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, u32 h_stream) +u16 hpi_outstream_group_add(u32 h_outstream, u32 h_stream) { struct hpi_message hm; struct hpi_response hr; @@ -1066,22 +809,22 @@ u16 hpi_outstream_group_add(const struct hpi_hsubsys *ph_subsys, hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_GROUP_ADD); - hr.error = 0; - u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + + if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; + + if (hpi_handle_indexes(h_stream, &adapter, + &hm.u.d.u.stream.stream_index)) + return HPI_ERROR_INVALID_HANDLE; + c_obj_type = hpi_handle_object(h_stream); switch (c_obj_type) { case HPI_OBJ_OSTREAM: - hm.u.d.u.stream.object_type = HPI_OBJ_OSTREAM; - u32TOINDEXES(h_stream, &adapter, - &hm.u.d.u.stream.stream_index); - break; case HPI_OBJ_ISTREAM: - hm.u.d.u.stream.object_type = HPI_OBJ_ISTREAM; - u32TOINDEXES(h_stream, &adapter, - &hm.u.d.u.stream.stream_index); + hm.u.d.u.stream.object_type = c_obj_type; break; default: - return HPI_ERROR_INVALID_STREAM; + return HPI_ERROR_INVALID_OBJ; } if (adapter != hm.adapter_index) return HPI_ERROR_NO_INTERADAPTER_GROUPS; @@ -1090,15 +833,16 @@ u16 hpi_outstream_group_add(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_outstream_group_get_map(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream, u32 *poutstream_map, u32 *pinstream_map) +u16 hpi_outstream_group_get_map(u32 h_outstream, u32 *poutstream_map, + u32 *pinstream_map) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_GROUP_GETMAP); - u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hpi_send_recv(&hm, &hr); if (poutstream_map) @@ -1109,21 +853,20 @@ u16 hpi_outstream_group_get_map(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_outstream_group_reset(const struct hpi_hsubsys *ph_subsys, - u32 h_outstream) +u16 hpi_outstream_group_reset(u32 h_outstream) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_GROUP_RESET); - u32TOINDEXES(h_outstream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_outstream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hpi_send_recv(&hm, &hr); return hr.error; } -u16 hpi_instream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, - u16 instream_index, u32 *ph_instream) +u16 hpi_instream_open(u16 adapter_index, u16 instream_index, u32 *ph_instream) { struct hpi_message hm; struct hpi_response hr; @@ -1145,38 +888,40 @@ u16 hpi_instream_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, return hr.error; } -u16 hpi_instream_close(const struct hpi_hsubsys *ph_subsys, u32 h_instream) +u16 hpi_instream_close(u32 h_instream) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_HOSTBUFFER_FREE); - u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hpi_send_recv(&hm, &hr); hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_GROUP_RESET); - u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index); hpi_send_recv(&hm, &hr); hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_CLOSE); - u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index); hpi_send_recv(&hm, &hr); return hr.error; } -u16 hpi_instream_query_format(const struct hpi_hsubsys *ph_subsys, - u32 h_instream, const struct hpi_format *p_format) +u16 hpi_instream_query_format(u32 h_instream, + const struct hpi_format *p_format) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_QUERY_FORMAT); - u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hpi_format_to_msg(&hm.u.d.u.data.format, p_format); hpi_send_recv(&hm, &hr); @@ -1184,15 +929,15 @@ u16 hpi_instream_query_format(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_instream_set_format(const struct hpi_hsubsys *ph_subsys, - u32 h_instream, const struct hpi_format *p_format) +u16 hpi_instream_set_format(u32 h_instream, const struct hpi_format *p_format) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_SET_FORMAT); - u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hpi_format_to_msg(&hm.u.d.u.data.format, p_format); hpi_send_recv(&hm, &hr); @@ -1200,15 +945,15 @@ u16 hpi_instream_set_format(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_instream_read_buf(const struct hpi_hsubsys *ph_subsys, u32 h_instream, - u8 *pb_data, u32 bytes_to_read) +u16 hpi_instream_read_buf(u32 h_instream, u8 *pb_data, u32 bytes_to_read) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_READ); - u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.d.u.data.data_size = bytes_to_read; hm.u.d.u.data.pb_data = pb_data; @@ -1217,72 +962,76 @@ u16 hpi_instream_read_buf(const struct hpi_hsubsys *ph_subsys, u32 h_instream, return hr.error; } -u16 hpi_instream_start(const struct hpi_hsubsys *ph_subsys, u32 h_instream) +u16 hpi_instream_start(u32 h_instream) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_START); - u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hpi_send_recv(&hm, &hr); return hr.error; } -u16 hpi_instream_wait_start(const struct hpi_hsubsys *ph_subsys, - u32 h_instream) +u16 hpi_instream_wait_start(u32 h_instream) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_WAIT_START); - u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hpi_send_recv(&hm, &hr); return hr.error; } -u16 hpi_instream_stop(const struct hpi_hsubsys *ph_subsys, u32 h_instream) +u16 hpi_instream_stop(u32 h_instream) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_STOP); - u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hpi_send_recv(&hm, &hr); return hr.error; } -u16 hpi_instream_reset(const struct hpi_hsubsys *ph_subsys, u32 h_instream) +u16 hpi_instream_reset(u32 h_instream) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_RESET); - u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hpi_send_recv(&hm, &hr); return hr.error; } -u16 hpi_instream_get_info_ex(const struct hpi_hsubsys *ph_subsys, - u32 h_instream, u16 *pw_state, u32 *pbuffer_size, u32 *pdata_recorded, - u32 *psamples_recorded, u32 *pauxiliary_data_recorded) +u16 hpi_instream_get_info_ex(u32 h_instream, u16 *pw_state, u32 *pbuffer_size, + u32 *pdata_recorded, u32 *psamples_recorded, + u32 *pauxiliary_data_recorded) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_GET_INFO); - u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hpi_send_recv(&hm, &hr); @@ -1300,15 +1049,15 @@ u16 hpi_instream_get_info_ex(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_instream_ancillary_reset(const struct hpi_hsubsys *ph_subsys, - u32 h_instream, u16 bytes_per_frame, u16 mode, u16 alignment, - u16 idle_bit) +u16 hpi_instream_ancillary_reset(u32 h_instream, u16 bytes_per_frame, + u16 mode, u16 alignment, u16 idle_bit) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_ANC_RESET); - u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.d.u.data.format.attributes = bytes_per_frame; hm.u.d.u.data.format.format = (mode << 8) | (alignment & 0xff); hm.u.d.u.data.format.channels = idle_bit; @@ -1316,14 +1065,14 @@ u16 hpi_instream_ancillary_reset(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_instream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys, - u32 h_instream, u32 *pframe_space) +u16 hpi_instream_ancillary_get_info(u32 h_instream, u32 *pframe_space) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_ANC_GET_INFO); - u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hpi_send_recv(&hm, &hr); if (pframe_space) *pframe_space = @@ -1333,8 +1082,8 @@ u16 hpi_instream_ancillary_get_info(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_instream_ancillary_write(const struct hpi_hsubsys *ph_subsys, - u32 h_instream, const struct hpi_anc_frame *p_anc_frame_buffer, +u16 hpi_instream_ancillary_write(u32 h_instream, + const struct hpi_anc_frame *p_anc_frame_buffer, u32 anc_frame_buffer_size_in_bytes, u32 number_of_ancillary_frames_to_write) { @@ -1343,7 +1092,8 @@ u16 hpi_instream_ancillary_write(const struct hpi_hsubsys *ph_subsys, hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_ANC_WRITE); - u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.d.u.data.pb_data = (u8 *)p_anc_frame_buffer; hm.u.d.u.data.data_size = number_of_ancillary_frames_to_write * @@ -1351,12 +1101,11 @@ u16 hpi_instream_ancillary_write(const struct hpi_hsubsys *ph_subsys, if (hm.u.d.u.data.data_size <= anc_frame_buffer_size_in_bytes) hpi_send_recv(&hm, &hr); else - hr.error = HPI_ERROR_INVALID_DATA_TRANSFER; + hr.error = HPI_ERROR_INVALID_DATASIZE; return hr.error; } -u16 hpi_instream_host_buffer_allocate(const struct hpi_hsubsys *ph_subsys, - u32 h_instream, u32 size_in_bytes) +u16 hpi_instream_host_buffer_allocate(u32 h_instream, u32 size_in_bytes) { struct hpi_message hm; @@ -1364,14 +1113,14 @@ u16 hpi_instream_host_buffer_allocate(const struct hpi_hsubsys *ph_subsys, hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_HOSTBUFFER_ALLOC); - u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.d.u.data.data_size = size_in_bytes; hpi_send_recv(&hm, &hr); return hr.error; } -u16 hpi_instream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys, - u32 h_instream, u8 **pp_buffer, +u16 hpi_instream_host_buffer_get_info(u32 h_instream, u8 **pp_buffer, struct hpi_hostbuffer_status **pp_status) { struct hpi_message hm; @@ -1379,7 +1128,8 @@ u16 hpi_instream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys, hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_HOSTBUFFER_GET_INFO); - u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hpi_send_recv(&hm, &hr); if (hr.error == 0) { @@ -1391,8 +1141,7 @@ u16 hpi_instream_host_buffer_get_info(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_instream_host_buffer_free(const struct hpi_hsubsys *ph_subsys, - u32 h_instream) +u16 hpi_instream_host_buffer_free(u32 h_instream) { struct hpi_message hm; @@ -1400,13 +1149,13 @@ u16 hpi_instream_host_buffer_free(const struct hpi_hsubsys *ph_subsys, hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_HOSTBUFFER_FREE); - u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hpi_send_recv(&hm, &hr); return hr.error; } -u16 hpi_instream_group_add(const struct hpi_hsubsys *ph_subsys, - u32 h_instream, u32 h_stream) +u16 hpi_instream_group_add(u32 h_instream, u32 h_stream) { struct hpi_message hm; struct hpi_response hr; @@ -1416,22 +1165,23 @@ u16 hpi_instream_group_add(const struct hpi_hsubsys *ph_subsys, hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_GROUP_ADD); hr.error = 0; - u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + + if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; + + if (hpi_handle_indexes(h_stream, &adapter, + &hm.u.d.u.stream.stream_index)) + return HPI_ERROR_INVALID_HANDLE; + c_obj_type = hpi_handle_object(h_stream); switch (c_obj_type) { case HPI_OBJ_OSTREAM: - hm.u.d.u.stream.object_type = HPI_OBJ_OSTREAM; - u32TOINDEXES(h_stream, &adapter, - &hm.u.d.u.stream.stream_index); - break; case HPI_OBJ_ISTREAM: - hm.u.d.u.stream.object_type = HPI_OBJ_ISTREAM; - u32TOINDEXES(h_stream, &adapter, - &hm.u.d.u.stream.stream_index); + hm.u.d.u.stream.object_type = c_obj_type; break; default: - return HPI_ERROR_INVALID_STREAM; + return HPI_ERROR_INVALID_OBJ; } if (adapter != hm.adapter_index) @@ -1441,15 +1191,16 @@ u16 hpi_instream_group_add(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_instream_group_get_map(const struct hpi_hsubsys *ph_subsys, - u32 h_instream, u32 *poutstream_map, u32 *pinstream_map) +u16 hpi_instream_group_get_map(u32 h_instream, u32 *poutstream_map, + u32 *pinstream_map) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_HOSTBUFFER_FREE); - u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hpi_send_recv(&hm, &hr); if (poutstream_map) @@ -1460,21 +1211,20 @@ u16 hpi_instream_group_get_map(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_instream_group_reset(const struct hpi_hsubsys *ph_subsys, - u32 h_instream) +u16 hpi_instream_group_reset(u32 h_instream) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_GROUP_RESET); - u32TOINDEXES(h_instream, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_instream, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hpi_send_recv(&hm, &hr); return hr.error; } -u16 hpi_mixer_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, - u32 *ph_mixer) +u16 hpi_mixer_open(u16 adapter_index, u32 *ph_mixer) { struct hpi_message hm; struct hpi_response hr; @@ -1492,25 +1242,29 @@ u16 hpi_mixer_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, return hr.error; } -u16 hpi_mixer_close(const struct hpi_hsubsys *ph_subsys, u32 h_mixer) +u16 hpi_mixer_close(u32 h_mixer) { struct hpi_message hm; struct hpi_response hr; + hpi_init_message_response(&hm, &hr, HPI_OBJ_MIXER, HPI_MIXER_CLOSE); - u32TOINDEX(h_mixer, &hm.adapter_index); + if (hpi_handle_indexes(h_mixer, &hm.adapter_index, NULL)) + return HPI_ERROR_INVALID_HANDLE; + hpi_send_recv(&hm, &hr); return hr.error; } -u16 hpi_mixer_get_control(const struct hpi_hsubsys *ph_subsys, u32 h_mixer, - u16 src_node_type, u16 src_node_type_index, u16 dst_node_type, - u16 dst_node_type_index, u16 control_type, u32 *ph_control) +u16 hpi_mixer_get_control(u32 h_mixer, u16 src_node_type, + u16 src_node_type_index, u16 dst_node_type, u16 dst_node_type_index, + u16 control_type, u32 *ph_control) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_MIXER, HPI_MIXER_GET_CONTROL); - u32TOINDEX(h_mixer, &hm.adapter_index); + if (hpi_handle_indexes(h_mixer, &hm.adapter_index, NULL)) + return HPI_ERROR_INVALID_HANDLE; hm.u.m.node_type1 = src_node_type; hm.u.m.node_index1 = src_node_type_index; hm.u.m.node_type2 = dst_node_type; @@ -1528,16 +1282,16 @@ u16 hpi_mixer_get_control(const struct hpi_hsubsys *ph_subsys, u32 h_mixer, return hr.error; } -u16 hpi_mixer_get_control_by_index(const struct hpi_hsubsys *ph_subsys, - u32 h_mixer, u16 control_index, u16 *pw_src_node_type, - u16 *pw_src_node_index, u16 *pw_dst_node_type, u16 *pw_dst_node_index, - u16 *pw_control_type, u32 *ph_control) +u16 hpi_mixer_get_control_by_index(u32 h_mixer, u16 control_index, + u16 *pw_src_node_type, u16 *pw_src_node_index, u16 *pw_dst_node_type, + u16 *pw_dst_node_index, u16 *pw_control_type, u32 *ph_control) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_MIXER, HPI_MIXER_GET_CONTROL_BY_INDEX); - u32TOINDEX(h_mixer, &hm.adapter_index); + if (hpi_handle_indexes(h_mixer, &hm.adapter_index, NULL)) + return HPI_ERROR_INVALID_HANDLE; hm.u.m.control_index = control_index; hpi_send_recv(&hm, &hr); @@ -1562,13 +1316,14 @@ u16 hpi_mixer_get_control_by_index(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_mixer_store(const struct hpi_hsubsys *ph_subsys, u32 h_mixer, - enum HPI_MIXER_STORE_COMMAND command, u16 index) +u16 hpi_mixer_store(u32 h_mixer, enum HPI_MIXER_STORE_COMMAND command, + u16 index) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_MIXER, HPI_MIXER_STORE); - u32TOINDEX(h_mixer, &hm.adapter_index); + if (hpi_handle_indexes(h_mixer, &hm.adapter_index, NULL)) + return HPI_ERROR_INVALID_HANDLE; hm.u.mx.store.command = command; hm.u.mx.store.index = index; hpi_send_recv(&hm, &hr); @@ -1576,16 +1331,16 @@ u16 hpi_mixer_store(const struct hpi_hsubsys *ph_subsys, u32 h_mixer, } static -u16 hpi_control_param_set(const struct hpi_hsubsys *ph_subsys, - const u32 h_control, const u16 attrib, const u32 param1, - const u32 param2) +u16 hpi_control_param_set(const u32 h_control, const u16 attrib, + const u32 param1, const u32 param2) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_SET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.c.attribute = attrib; hm.u.c.param1 = param1; hm.u.c.param2 = param2; @@ -1601,7 +1356,8 @@ static u16 hpi_control_log_set2(u32 h_control, u16 attrib, short sv0, hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_SET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.c.attribute = attrib; hm.u.c.an_log_value[0] = sv0; hm.u.c.an_log_value[1] = sv1; @@ -1610,16 +1366,16 @@ static u16 hpi_control_log_set2(u32 h_control, u16 attrib, short sv0, } static -u16 hpi_control_param_get(const struct hpi_hsubsys *ph_subsys, - const u32 h_control, const u16 attrib, u32 param1, u32 param2, - u32 *pparam1, u32 *pparam2) +u16 hpi_control_param_get(const u32 h_control, const u16 attrib, u32 param1, + u32 param2, u32 *pparam1, u32 *pparam2) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.c.attribute = attrib; hm.u.c.param1 = param1; hm.u.c.param2 = param2; @@ -1632,19 +1388,20 @@ u16 hpi_control_param_get(const struct hpi_hsubsys *ph_subsys, return hr.error; } -#define hpi_control_param1_get(s, h, a, p1) \ - hpi_control_param_get(s, h, a, 0, 0, p1, NULL) -#define hpi_control_param2_get(s, h, a, p1, p2) \ - hpi_control_param_get(s, h, a, 0, 0, p1, p2) +#define hpi_control_param1_get(h, a, p1) \ + hpi_control_param_get(h, a, 0, 0, p1, NULL) +#define hpi_control_param2_get(h, a, p1, p2) \ + hpi_control_param_get(h, a, 0, 0, p1, p2) -static u16 hpi_control_log_get2(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 attrib, short *sv0, short *sv1) +static u16 hpi_control_log_get2(u32 h_control, u16 attrib, short *sv0, + short *sv1) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.c.attribute = attrib; hpi_send_recv(&hm, &hr); @@ -1655,8 +1412,7 @@ static u16 hpi_control_log_get2(const struct hpi_hsubsys *ph_subsys, } static -u16 hpi_control_query(const struct hpi_hsubsys *ph_subsys, - const u32 h_control, const u16 attrib, const u32 index, +u16 hpi_control_query(const u32 h_control, const u16 attrib, const u32 index, const u32 param, u32 *psetting) { struct hpi_message hm; @@ -1664,7 +1420,8 @@ u16 hpi_control_query(const struct hpi_hsubsys *ph_subsys, hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_INFO); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.c.attribute = attrib; hm.u.c.param1 = index; @@ -1682,7 +1439,7 @@ static u16 hpi_control_get_string(const u32 h_control, const u16 attribute, unsigned int sub_string_index = 0, j = 0; char c = 0; unsigned int n = 0; - u16 hE = 0; + u16 err = 0; if ((string_length < 1) || (string_length > 256)) return HPI_ERROR_INVALID_CONTROL_VALUE; @@ -1693,7 +1450,9 @@ static u16 hpi_control_get_string(const u32 h_control, const u16 attribute, hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_control, &hm.adapter_index, + &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.c.attribute = attribute; hm.u.c.param1 = sub_string_index; hm.u.c.param2 = 0; @@ -1705,7 +1464,7 @@ static u16 hpi_control_get_string(const u32 h_control, const u16 attribute, return HPI_ERROR_INVALID_CONTROL_VALUE; if (hr.error) { - hE = hr.error; + err = hr.error; break; } for (j = 0; j < 8; j++) { @@ -1714,7 +1473,7 @@ static u16 hpi_control_get_string(const u32 h_control, const u16 attribute, n++; if (n >= string_length) { psz_string[string_length - 1] = 0; - hE = HPI_ERROR_INVALID_CONTROL_VALUE; + err = HPI_ERROR_INVALID_CONTROL_VALUE; break; } if (c == 0) @@ -1730,57 +1489,52 @@ static u16 hpi_control_get_string(const u32 h_control, const u16 attribute, if (c == 0) break; } - return hE; + return err; } -u16 HPI_AESEBU__receiver_query_format(const struct hpi_hsubsys *ph_subsys, - const u32 h_aes_rx, const u32 index, u16 *pw_format) +u16 hpi_aesebu_receiver_query_format(const u32 h_aes_rx, const u32 index, + u16 *pw_format) { u32 qr; u16 err; - err = hpi_control_query(ph_subsys, h_aes_rx, HPI_AESEBURX_FORMAT, - index, 0, &qr); + err = hpi_control_query(h_aes_rx, HPI_AESEBURX_FORMAT, index, 0, &qr); *pw_format = (u16)qr; return err; } -u16 HPI_AESEBU__receiver_set_format(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 format) +u16 hpi_aesebu_receiver_set_format(u32 h_control, u16 format) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_AESEBURX_FORMAT, format, 0); + return hpi_control_param_set(h_control, HPI_AESEBURX_FORMAT, format, + 0); } -u16 HPI_AESEBU__receiver_get_format(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 *pw_format) +u16 hpi_aesebu_receiver_get_format(u32 h_control, u16 *pw_format) { u16 err; u32 param; - err = hpi_control_param1_get(ph_subsys, h_control, - HPI_AESEBURX_FORMAT, ¶m); + err = hpi_control_param1_get(h_control, HPI_AESEBURX_FORMAT, ¶m); if (!err && pw_format) *pw_format = (u16)param; return err; } -u16 HPI_AESEBU__receiver_get_sample_rate(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *psample_rate) +u16 hpi_aesebu_receiver_get_sample_rate(u32 h_control, u32 *psample_rate) { - return hpi_control_param1_get(ph_subsys, h_control, - HPI_AESEBURX_SAMPLERATE, psample_rate); + return hpi_control_param1_get(h_control, HPI_AESEBURX_SAMPLERATE, + psample_rate); } -u16 HPI_AESEBU__receiver_get_user_data(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 index, u16 *pw_data) +u16 hpi_aesebu_receiver_get_user_data(u32 h_control, u16 index, u16 *pw_data) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.c.attribute = HPI_AESEBURX_USERDATA; hm.u.c.param1 = index; @@ -1791,14 +1545,15 @@ u16 HPI_AESEBU__receiver_get_user_data(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 HPI_AESEBU__receiver_get_channel_status(const struct hpi_hsubsys - *ph_subsys, u32 h_control, u16 index, u16 *pw_data) +u16 hpi_aesebu_receiver_get_channel_status(u32 h_control, u16 index, + u16 *pw_data) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.c.attribute = HPI_AESEBURX_CHANNELSTATUS; hm.u.c.param1 = index; @@ -1809,101 +1564,93 @@ u16 HPI_AESEBU__receiver_get_channel_status(const struct hpi_hsubsys return hr.error; } -u16 HPI_AESEBU__receiver_get_error_status(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 *pw_error_data) +u16 hpi_aesebu_receiver_get_error_status(u32 h_control, u16 *pw_error_data) { u32 error_data = 0; - u16 error = 0; + u16 err = 0; - error = hpi_control_param1_get(ph_subsys, h_control, - HPI_AESEBURX_ERRORSTATUS, &error_data); + err = hpi_control_param1_get(h_control, HPI_AESEBURX_ERRORSTATUS, + &error_data); if (pw_error_data) *pw_error_data = (u16)error_data; - return error; + return err; } -u16 HPI_AESEBU__transmitter_set_sample_rate(const struct hpi_hsubsys - *ph_subsys, u32 h_control, u32 sample_rate) +u16 hpi_aesebu_transmitter_set_sample_rate(u32 h_control, u32 sample_rate) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_AESEBUTX_SAMPLERATE, sample_rate, 0); + return hpi_control_param_set(h_control, HPI_AESEBUTX_SAMPLERATE, + sample_rate, 0); } -u16 HPI_AESEBU__transmitter_set_user_data(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 index, u16 data) +u16 hpi_aesebu_transmitter_set_user_data(u32 h_control, u16 index, u16 data) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_AESEBUTX_USERDATA, index, data); + return hpi_control_param_set(h_control, HPI_AESEBUTX_USERDATA, index, + data); } -u16 HPI_AESEBU__transmitter_set_channel_status(const struct hpi_hsubsys - *ph_subsys, u32 h_control, u16 index, u16 data) +u16 hpi_aesebu_transmitter_set_channel_status(u32 h_control, u16 index, + u16 data) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_AESEBUTX_CHANNELSTATUS, index, data); + return hpi_control_param_set(h_control, HPI_AESEBUTX_CHANNELSTATUS, + index, data); } -u16 HPI_AESEBU__transmitter_get_channel_status(const struct hpi_hsubsys - *ph_subsys, u32 h_control, u16 index, u16 *pw_data) +u16 hpi_aesebu_transmitter_get_channel_status(u32 h_control, u16 index, + u16 *pw_data) { return HPI_ERROR_INVALID_OPERATION; } -u16 HPI_AESEBU__transmitter_query_format(const struct hpi_hsubsys *ph_subsys, - const u32 h_aes_tx, const u32 index, u16 *pw_format) +u16 hpi_aesebu_transmitter_query_format(const u32 h_aes_tx, const u32 index, + u16 *pw_format) { u32 qr; u16 err; - err = hpi_control_query(ph_subsys, h_aes_tx, HPI_AESEBUTX_FORMAT, - index, 0, &qr); + err = hpi_control_query(h_aes_tx, HPI_AESEBUTX_FORMAT, index, 0, &qr); *pw_format = (u16)qr; return err; } -u16 HPI_AESEBU__transmitter_set_format(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 output_format) +u16 hpi_aesebu_transmitter_set_format(u32 h_control, u16 output_format) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_AESEBUTX_FORMAT, output_format, 0); + return hpi_control_param_set(h_control, HPI_AESEBUTX_FORMAT, + output_format, 0); } -u16 HPI_AESEBU__transmitter_get_format(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 *pw_output_format) +u16 hpi_aesebu_transmitter_get_format(u32 h_control, u16 *pw_output_format) { u16 err; u32 param; - err = hpi_control_param1_get(ph_subsys, h_control, - HPI_AESEBUTX_FORMAT, ¶m); + err = hpi_control_param1_get(h_control, HPI_AESEBUTX_FORMAT, ¶m); if (!err && pw_output_format) *pw_output_format = (u16)param; return err; } -u16 hpi_bitstream_set_clock_edge(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 edge_type) +u16 hpi_bitstream_set_clock_edge(u32 h_control, u16 edge_type) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_BITSTREAM_CLOCK_EDGE, edge_type, 0); + return hpi_control_param_set(h_control, HPI_BITSTREAM_CLOCK_EDGE, + edge_type, 0); } -u16 hpi_bitstream_set_data_polarity(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 polarity) +u16 hpi_bitstream_set_data_polarity(u32 h_control, u16 polarity) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_BITSTREAM_DATA_POLARITY, polarity, 0); + return hpi_control_param_set(h_control, HPI_BITSTREAM_DATA_POLARITY, + polarity, 0); } -u16 hpi_bitstream_get_activity(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 *pw_clk_activity, u16 *pw_data_activity) +u16 hpi_bitstream_get_activity(u32 h_control, u16 *pw_clk_activity, + u16 *pw_data_activity) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.c.attribute = HPI_BITSTREAM_ACTIVITY; hpi_send_recv(&hm, &hr); if (pw_clk_activity) @@ -1913,45 +1660,43 @@ u16 hpi_bitstream_get_activity(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_channel_mode_query_mode(const struct hpi_hsubsys *ph_subsys, - const u32 h_mode, const u32 index, u16 *pw_mode) +u16 hpi_channel_mode_query_mode(const u32 h_mode, const u32 index, + u16 *pw_mode) { u32 qr; u16 err; - err = hpi_control_query(ph_subsys, h_mode, HPI_CHANNEL_MODE_MODE, - index, 0, &qr); + err = hpi_control_query(h_mode, HPI_CHANNEL_MODE_MODE, index, 0, &qr); *pw_mode = (u16)qr; return err; } -u16 hpi_channel_mode_set(const struct hpi_hsubsys *ph_subsys, u32 h_control, - u16 mode) +u16 hpi_channel_mode_set(u32 h_control, u16 mode) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_CHANNEL_MODE_MODE, mode, 0); + return hpi_control_param_set(h_control, HPI_CHANNEL_MODE_MODE, mode, + 0); } -u16 hpi_channel_mode_get(const struct hpi_hsubsys *ph_subsys, u32 h_control, - u16 *mode) +u16 hpi_channel_mode_get(u32 h_control, u16 *mode) { u32 mode32 = 0; - u16 error = hpi_control_param1_get(ph_subsys, h_control, + u16 err = hpi_control_param1_get(h_control, HPI_CHANNEL_MODE_MODE, &mode32); if (mode) *mode = (u16)mode32; - return error; + return err; } -u16 hpi_cobranet_hmi_write(const struct hpi_hsubsys *ph_subsys, u32 h_control, - u32 hmi_address, u32 byte_count, u8 *pb_data) +u16 hpi_cobranet_hmi_write(u32 h_control, u32 hmi_address, u32 byte_count, + u8 *pb_data) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROLEX, HPI_CONTROL_SET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.cx.u.cobranet_data.byte_count = byte_count; hm.u.cx.u.cobranet_data.hmi_address = hmi_address; @@ -1969,15 +1714,16 @@ u16 hpi_cobranet_hmi_write(const struct hpi_hsubsys *ph_subsys, u32 h_control, return hr.error; } -u16 hpi_cobranet_hmi_read(const struct hpi_hsubsys *ph_subsys, u32 h_control, - u32 hmi_address, u32 max_byte_count, u32 *pbyte_count, u8 *pb_data) +u16 hpi_cobranet_hmi_read(u32 h_control, u32 hmi_address, u32 max_byte_count, + u32 *pbyte_count, u8 *pb_data) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROLEX, HPI_CONTROL_GET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.cx.u.cobranet_data.byte_count = max_byte_count; hm.u.cx.u.cobranet_data.hmi_address = hmi_address; @@ -2008,16 +1754,16 @@ u16 hpi_cobranet_hmi_read(const struct hpi_hsubsys *ph_subsys, u32 h_control, return hr.error; } -u16 hpi_cobranet_hmi_get_status(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *pstatus, u32 *preadable_size, - u32 *pwriteable_size) +u16 hpi_cobranet_hmi_get_status(u32 h_control, u32 *pstatus, + u32 *preadable_size, u32 *pwriteable_size) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROLEX, HPI_CONTROL_GET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.cx.attribute = HPI_COBRANET_GET_STATUS; @@ -2035,177 +1781,176 @@ u16 hpi_cobranet_hmi_get_status(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_cobranet_getI_paddress(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *pi_paddress) +u16 hpi_cobranet_get_ip_address(u32 h_control, u32 *pdw_ip_address) { u32 byte_count; u32 iP; - u16 error; + u16 err; - error = hpi_cobranet_hmi_read(ph_subsys, h_control, + err = hpi_cobranet_hmi_read(h_control, HPI_COBRANET_HMI_cobra_ip_mon_currentIP, 4, &byte_count, (u8 *)&iP); - *pi_paddress = + *pdw_ip_address = ((iP & 0xff000000) >> 8) | ((iP & 0x00ff0000) << 8) | ((iP & 0x0000ff00) >> 8) | ((iP & 0x000000ff) << 8); - if (error) - *pi_paddress = 0; + if (err) + *pdw_ip_address = 0; - return error; + return err; } -u16 hpi_cobranet_setI_paddress(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 i_paddress) +u16 hpi_cobranet_set_ip_address(u32 h_control, u32 dw_ip_address) { u32 iP; - u16 error; + u16 err; - iP = ((i_paddress & 0xff000000) >> 8) | ((i_paddress & 0x00ff0000) << - 8) | ((i_paddress & 0x0000ff00) >> 8) | ((i_paddress & - 0x000000ff) << 8); + iP = ((dw_ip_address & 0xff000000) >> 8) | ((dw_ip_address & + 0x00ff0000) << 8) | ((dw_ip_address & 0x0000ff00) >> + 8) | ((dw_ip_address & 0x000000ff) << 8); - error = hpi_cobranet_hmi_write(ph_subsys, h_control, + err = hpi_cobranet_hmi_write(h_control, HPI_COBRANET_HMI_cobra_ip_mon_currentIP, 4, (u8 *)&iP); - return error; + return err; } -u16 hpi_cobranet_get_staticI_paddress(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *pi_paddress) +u16 hpi_cobranet_get_static_ip_address(u32 h_control, u32 *pdw_ip_address) { u32 byte_count; u32 iP; - u16 error; - error = hpi_cobranet_hmi_read(ph_subsys, h_control, + u16 err; + err = hpi_cobranet_hmi_read(h_control, HPI_COBRANET_HMI_cobra_ip_mon_staticIP, 4, &byte_count, (u8 *)&iP); - *pi_paddress = + *pdw_ip_address = ((iP & 0xff000000) >> 8) | ((iP & 0x00ff0000) << 8) | ((iP & 0x0000ff00) >> 8) | ((iP & 0x000000ff) << 8); - if (error) - *pi_paddress = 0; + if (err) + *pdw_ip_address = 0; - return error; + return err; } -u16 hpi_cobranet_set_staticI_paddress(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 i_paddress) +u16 hpi_cobranet_set_static_ip_address(u32 h_control, u32 dw_ip_address) { u32 iP; - u16 error; + u16 err; - iP = ((i_paddress & 0xff000000) >> 8) | ((i_paddress & 0x00ff0000) << - 8) | ((i_paddress & 0x0000ff00) >> 8) | ((i_paddress & - 0x000000ff) << 8); + iP = ((dw_ip_address & 0xff000000) >> 8) | ((dw_ip_address & + 0x00ff0000) << 8) | ((dw_ip_address & 0x0000ff00) >> + 8) | ((dw_ip_address & 0x000000ff) << 8); - error = hpi_cobranet_hmi_write(ph_subsys, h_control, + err = hpi_cobranet_hmi_write(h_control, HPI_COBRANET_HMI_cobra_ip_mon_staticIP, 4, (u8 *)&iP); - return error; + return err; } -u16 hpi_cobranet_getMA_caddress(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *pmAC_MS_bs, u32 *pmAC_LS_bs) +u16 hpi_cobranet_get_macaddress(u32 h_control, u32 *p_mac_msbs, + u32 *p_mac_lsbs) { u32 byte_count; - u16 error; - u32 mAC; + u16 err; + u32 mac; - error = hpi_cobranet_hmi_read(ph_subsys, h_control, + err = hpi_cobranet_hmi_read(h_control, HPI_COBRANET_HMI_cobra_if_phy_address, 4, &byte_count, - (u8 *)&mAC); - *pmAC_MS_bs = - ((mAC & 0xff000000) >> 8) | ((mAC & 0x00ff0000) << 8) | ((mAC - & 0x0000ff00) >> 8) | ((mAC & 0x000000ff) << 8); - error += hpi_cobranet_hmi_read(ph_subsys, h_control, - HPI_COBRANET_HMI_cobra_if_phy_address + 1, 4, &byte_count, - (u8 *)&mAC); - *pmAC_LS_bs = - ((mAC & 0xff000000) >> 8) | ((mAC & 0x00ff0000) << 8) | ((mAC - & 0x0000ff00) >> 8) | ((mAC & 0x000000ff) << 8); - - if (error) { - *pmAC_MS_bs = 0; - *pmAC_LS_bs = 0; + (u8 *)&mac); + + if (!err) { + *p_mac_msbs = + ((mac & 0xff000000) >> 8) | ((mac & 0x00ff0000) << 8) + | ((mac & 0x0000ff00) >> 8) | ((mac & 0x000000ff) << + 8); + + err = hpi_cobranet_hmi_read(h_control, + HPI_COBRANET_HMI_cobra_if_phy_address + 1, 4, + &byte_count, (u8 *)&mac); } - return error; + if (!err) { + *p_mac_lsbs = + ((mac & 0xff000000) >> 8) | ((mac & 0x00ff0000) << 8) + | ((mac & 0x0000ff00) >> 8) | ((mac & 0x000000ff) << + 8); + } else { + *p_mac_msbs = 0; + *p_mac_lsbs = 0; + } + + return err; } -u16 hpi_compander_set_enable(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 enable) +u16 hpi_compander_set_enable(u32 h_control, u32 enable) { - return hpi_control_param_set(ph_subsys, h_control, HPI_GENERIC_ENABLE, - enable, 0); + return hpi_control_param_set(h_control, HPI_GENERIC_ENABLE, enable, + 0); } -u16 hpi_compander_get_enable(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *enable) +u16 hpi_compander_get_enable(u32 h_control, u32 *enable) { - return hpi_control_param1_get(ph_subsys, h_control, - HPI_GENERIC_ENABLE, enable); + return hpi_control_param1_get(h_control, HPI_GENERIC_ENABLE, enable); } -u16 hpi_compander_set_makeup_gain(const struct hpi_hsubsys *ph_subsys, - u32 h_control, short makeup_gain0_01dB) +u16 hpi_compander_set_makeup_gain(u32 h_control, short makeup_gain0_01dB) { return hpi_control_log_set2(h_control, HPI_COMPANDER_MAKEUPGAIN, makeup_gain0_01dB, 0); } -u16 hpi_compander_get_makeup_gain(const struct hpi_hsubsys *ph_subsys, - u32 h_control, short *makeup_gain0_01dB) +u16 hpi_compander_get_makeup_gain(u32 h_control, short *makeup_gain0_01dB) { - return hpi_control_log_get2(ph_subsys, h_control, - HPI_COMPANDER_MAKEUPGAIN, makeup_gain0_01dB, NULL); + return hpi_control_log_get2(h_control, HPI_COMPANDER_MAKEUPGAIN, + makeup_gain0_01dB, NULL); } -u16 hpi_compander_set_attack_time_constant(const struct hpi_hsubsys - *ph_subsys, u32 h_control, unsigned int index, u32 attack) +u16 hpi_compander_set_attack_time_constant(u32 h_control, unsigned int index, + u32 attack) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_COMPANDER_ATTACK, attack, index); + return hpi_control_param_set(h_control, HPI_COMPANDER_ATTACK, attack, + index); } -u16 hpi_compander_get_attack_time_constant(const struct hpi_hsubsys - *ph_subsys, u32 h_control, unsigned int index, u32 *attack) +u16 hpi_compander_get_attack_time_constant(u32 h_control, unsigned int index, + u32 *attack) { - return hpi_control_param_get(ph_subsys, h_control, - HPI_COMPANDER_ATTACK, 0, index, attack, NULL); + return hpi_control_param_get(h_control, HPI_COMPANDER_ATTACK, 0, + index, attack, NULL); } -u16 hpi_compander_set_decay_time_constant(const struct hpi_hsubsys *ph_subsys, - u32 h_control, unsigned int index, u32 decay) +u16 hpi_compander_set_decay_time_constant(u32 h_control, unsigned int index, + u32 decay) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_COMPANDER_DECAY, decay, index); + return hpi_control_param_set(h_control, HPI_COMPANDER_DECAY, decay, + index); } -u16 hpi_compander_get_decay_time_constant(const struct hpi_hsubsys *ph_subsys, - u32 h_control, unsigned int index, u32 *decay) +u16 hpi_compander_get_decay_time_constant(u32 h_control, unsigned int index, + u32 *decay) { - return hpi_control_param_get(ph_subsys, h_control, - HPI_COMPANDER_DECAY, 0, index, decay, NULL); + return hpi_control_param_get(h_control, HPI_COMPANDER_DECAY, 0, index, + decay, NULL); } -u16 hpi_compander_set_threshold(const struct hpi_hsubsys *ph_subsys, - u32 h_control, unsigned int index, short threshold0_01dB) +u16 hpi_compander_set_threshold(u32 h_control, unsigned int index, + short threshold0_01dB) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_SET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.c.attribute = HPI_COMPANDER_THRESHOLD; hm.u.c.param2 = index; hm.u.c.an_log_value[0] = threshold0_01dB; @@ -2215,15 +1960,16 @@ u16 hpi_compander_set_threshold(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_compander_get_threshold(const struct hpi_hsubsys *ph_subsys, - u32 h_control, unsigned int index, short *threshold0_01dB) +u16 hpi_compander_get_threshold(u32 h_control, unsigned int index, + short *threshold0_01dB) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.c.attribute = HPI_COMPANDER_THRESHOLD; hm.u.c.param2 = index; @@ -2233,29 +1979,28 @@ u16 hpi_compander_get_threshold(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_compander_set_ratio(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 index, u32 ratio100) +u16 hpi_compander_set_ratio(u32 h_control, u32 index, u32 ratio100) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_COMPANDER_RATIO, ratio100, index); + return hpi_control_param_set(h_control, HPI_COMPANDER_RATIO, ratio100, + index); } -u16 hpi_compander_get_ratio(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 index, u32 *ratio100) +u16 hpi_compander_get_ratio(u32 h_control, u32 index, u32 *ratio100) { - return hpi_control_param_get(ph_subsys, h_control, - HPI_COMPANDER_RATIO, 0, index, ratio100, NULL); + return hpi_control_param_get(h_control, HPI_COMPANDER_RATIO, 0, index, + ratio100, NULL); } -u16 hpi_level_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control, - short *min_gain_01dB, short *max_gain_01dB, short *step_gain_01dB) +u16 hpi_level_query_range(u32 h_control, short *min_gain_01dB, + short *max_gain_01dB, short *step_gain_01dB) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.c.attribute = HPI_LEVEL_RANGE; hpi_send_recv(&hm, &hr); @@ -2273,31 +2018,27 @@ u16 hpi_level_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control, return hr.error; } -u16 hpi_level_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control, - short an_gain0_01dB[HPI_MAX_CHANNELS] +u16 hpi_level_set_gain(u32 h_control, short an_gain0_01dB[HPI_MAX_CHANNELS] ) { return hpi_control_log_set2(h_control, HPI_LEVEL_GAIN, an_gain0_01dB[0], an_gain0_01dB[1]); } -u16 hpi_level_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control, - short an_gain0_01dB[HPI_MAX_CHANNELS] +u16 hpi_level_get_gain(u32 h_control, short an_gain0_01dB[HPI_MAX_CHANNELS] ) { - return hpi_control_log_get2(ph_subsys, h_control, HPI_LEVEL_GAIN, + return hpi_control_log_get2(h_control, HPI_LEVEL_GAIN, &an_gain0_01dB[0], &an_gain0_01dB[1]); } -u16 hpi_meter_query_channels(const struct hpi_hsubsys *ph_subsys, - const u32 h_meter, u32 *p_channels) +u16 hpi_meter_query_channels(const u32 h_meter, u32 *p_channels) { - return hpi_control_query(ph_subsys, h_meter, HPI_METER_NUM_CHANNELS, - 0, 0, p_channels); + return hpi_control_query(h_meter, HPI_METER_NUM_CHANNELS, 0, 0, + p_channels); } -u16 hpi_meter_get_peak(const struct hpi_hsubsys *ph_subsys, u32 h_control, - short an_peakdB[HPI_MAX_CHANNELS] +u16 hpi_meter_get_peak(u32 h_control, short an_peakdB[HPI_MAX_CHANNELS] ) { short i = 0; @@ -2307,7 +2048,8 @@ u16 hpi_meter_get_peak(const struct hpi_hsubsys *ph_subsys, u32 h_control, hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.obj_index = hm.obj_index; hm.u.c.attribute = HPI_METER_PEAK; @@ -2322,8 +2064,7 @@ u16 hpi_meter_get_peak(const struct hpi_hsubsys *ph_subsys, u32 h_control, return hr.error; } -u16 hpi_meter_get_rms(const struct hpi_hsubsys *ph_subsys, u32 h_control, - short an_rmsdB[HPI_MAX_CHANNELS] +u16 hpi_meter_get_rms(u32 h_control, short an_rmsdB[HPI_MAX_CHANNELS] ) { short i = 0; @@ -2333,7 +2074,8 @@ u16 hpi_meter_get_rms(const struct hpi_hsubsys *ph_subsys, u32 h_control, hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.c.attribute = HPI_METER_RMS; hpi_send_recv(&hm, &hr); @@ -2348,22 +2090,20 @@ u16 hpi_meter_get_rms(const struct hpi_hsubsys *ph_subsys, u32 h_control, return hr.error; } -u16 hpi_meter_set_rms_ballistics(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 attack, u16 decay) +u16 hpi_meter_set_rms_ballistics(u32 h_control, u16 attack, u16 decay) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_METER_RMS_BALLISTICS, attack, decay); + return hpi_control_param_set(h_control, HPI_METER_RMS_BALLISTICS, + attack, decay); } -u16 hpi_meter_get_rms_ballistics(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 *pn_attack, u16 *pn_decay) +u16 hpi_meter_get_rms_ballistics(u32 h_control, u16 *pn_attack, u16 *pn_decay) { u32 attack; u32 decay; u16 error; - error = hpi_control_param2_get(ph_subsys, h_control, - HPI_METER_RMS_BALLISTICS, &attack, &decay); + error = hpi_control_param2_get(h_control, HPI_METER_RMS_BALLISTICS, + &attack, &decay); if (pn_attack) *pn_attack = (unsigned short)attack; @@ -2373,22 +2113,21 @@ u16 hpi_meter_get_rms_ballistics(const struct hpi_hsubsys *ph_subsys, return error; } -u16 hpi_meter_set_peak_ballistics(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 attack, u16 decay) +u16 hpi_meter_set_peak_ballistics(u32 h_control, u16 attack, u16 decay) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_METER_PEAK_BALLISTICS, attack, decay); + return hpi_control_param_set(h_control, HPI_METER_PEAK_BALLISTICS, + attack, decay); } -u16 hpi_meter_get_peak_ballistics(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 *pn_attack, u16 *pn_decay) +u16 hpi_meter_get_peak_ballistics(u32 h_control, u16 *pn_attack, + u16 *pn_decay) { u32 attack; u32 decay; u16 error; - error = hpi_control_param2_get(ph_subsys, h_control, - HPI_METER_PEAK_BALLISTICS, &attack, &decay); + error = hpi_control_param2_get(h_control, HPI_METER_PEAK_BALLISTICS, + &attack, &decay); if (pn_attack) *pn_attack = (short)attack; @@ -2398,55 +2137,53 @@ u16 hpi_meter_get_peak_ballistics(const struct hpi_hsubsys *ph_subsys, return error; } -u16 hpi_microphone_set_phantom_power(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 on_off) +u16 hpi_microphone_set_phantom_power(u32 h_control, u16 on_off) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_MICROPHONE_PHANTOM_POWER, (u32)on_off, 0); + return hpi_control_param_set(h_control, HPI_MICROPHONE_PHANTOM_POWER, + (u32)on_off, 0); } -u16 hpi_microphone_get_phantom_power(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 *pw_on_off) +u16 hpi_microphone_get_phantom_power(u32 h_control, u16 *pw_on_off) { u16 error = 0; u32 on_off = 0; - error = hpi_control_param1_get(ph_subsys, h_control, + error = hpi_control_param1_get(h_control, HPI_MICROPHONE_PHANTOM_POWER, &on_off); if (pw_on_off) *pw_on_off = (u16)on_off; return error; } -u16 hpi_multiplexer_set_source(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 source_node_type, u16 source_node_index) +u16 hpi_multiplexer_set_source(u32 h_control, u16 source_node_type, + u16 source_node_index) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_MULTIPLEXER_SOURCE, source_node_type, source_node_index); + return hpi_control_param_set(h_control, HPI_MULTIPLEXER_SOURCE, + source_node_type, source_node_index); } -u16 hpi_multiplexer_get_source(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 *source_node_type, u16 *source_node_index) +u16 hpi_multiplexer_get_source(u32 h_control, u16 *source_node_type, + u16 *source_node_index) { u32 node, index; - u16 error = hpi_control_param2_get(ph_subsys, h_control, + u16 err = hpi_control_param2_get(h_control, HPI_MULTIPLEXER_SOURCE, &node, &index); if (source_node_type) *source_node_type = (u16)node; if (source_node_index) *source_node_index = (u16)index; - return error; + return err; } -u16 hpi_multiplexer_query_source(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 index, u16 *source_node_type, - u16 *source_node_index) +u16 hpi_multiplexer_query_source(u32 h_control, u16 index, + u16 *source_node_type, u16 *source_node_index) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.c.attribute = HPI_MULTIPLEXER_QUERYSOURCE; hm.u.c.param1 = index; @@ -2459,15 +2196,15 @@ u16 hpi_multiplexer_query_source(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_parametricEQ__get_info(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 *pw_number_of_bands, u16 *pw_on_off) +u16 hpi_parametric_eq_get_info(u32 h_control, u16 *pw_number_of_bands, + u16 *pw_on_off) { u32 oB = 0; u32 oO = 0; u16 error = 0; - error = hpi_control_param2_get(ph_subsys, h_control, - HPI_EQUALIZER_NUM_FILTERS, &oO, &oB); + error = hpi_control_param2_get(h_control, HPI_EQUALIZER_NUM_FILTERS, + &oO, &oB); if (pw_number_of_bands) *pw_number_of_bands = (u16)oB; if (pw_on_off) @@ -2475,23 +2212,22 @@ u16 hpi_parametricEQ__get_info(const struct hpi_hsubsys *ph_subsys, return error; } -u16 hpi_parametricEQ__set_state(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 on_off) +u16 hpi_parametric_eq_set_state(u32 h_control, u16 on_off) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_EQUALIZER_NUM_FILTERS, on_off, 0); + return hpi_control_param_set(h_control, HPI_EQUALIZER_NUM_FILTERS, + on_off, 0); } -u16 hpi_parametricEQ__get_band(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 index, u16 *pn_type, u32 *pfrequency_hz, - short *pnQ100, short *pn_gain0_01dB) +u16 hpi_parametric_eq_get_band(u32 h_control, u16 index, u16 *pn_type, + u32 *pfrequency_hz, short *pnQ100, short *pn_gain0_01dB) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.c.attribute = HPI_EQUALIZER_FILTER; hm.u.c.param2 = index; @@ -2509,16 +2245,16 @@ u16 hpi_parametricEQ__get_band(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_parametricEQ__set_band(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 index, u16 type, u32 frequency_hz, short q100, - short gain0_01dB) +u16 hpi_parametric_eq_set_band(u32 h_control, u16 index, u16 type, + u32 frequency_hz, short q100, short gain0_01dB) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_SET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.c.param1 = frequency_hz; hm.u.c.param2 = (index & 0xFFFFL) + ((u32)type << 16); @@ -2531,8 +2267,7 @@ u16 hpi_parametricEQ__set_band(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_parametricEQ__get_coeffs(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 index, short coeffs[5] +u16 hpi_parametric_eq_get_coeffs(u32 h_control, u16 index, short coeffs[5] ) { struct hpi_message hm; @@ -2540,7 +2275,8 @@ u16 hpi_parametricEQ__get_coeffs(const struct hpi_hsubsys *ph_subsys, hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.c.attribute = HPI_EQUALIZER_COEFFICIENTS; hm.u.c.param2 = index; @@ -2555,444 +2291,388 @@ u16 hpi_parametricEQ__get_coeffs(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_sample_clock_query_source(const struct hpi_hsubsys *ph_subsys, - const u32 h_clock, const u32 index, u16 *pw_source) +u16 hpi_sample_clock_query_source(const u32 h_clock, const u32 index, + u16 *pw_source) { u32 qr; u16 err; - err = hpi_control_query(ph_subsys, h_clock, HPI_SAMPLECLOCK_SOURCE, - index, 0, &qr); + err = hpi_control_query(h_clock, HPI_SAMPLECLOCK_SOURCE, index, 0, + &qr); *pw_source = (u16)qr; return err; } -u16 hpi_sample_clock_set_source(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 source) +u16 hpi_sample_clock_set_source(u32 h_control, u16 source) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_SAMPLECLOCK_SOURCE, source, 0); + return hpi_control_param_set(h_control, HPI_SAMPLECLOCK_SOURCE, + source, 0); } -u16 hpi_sample_clock_get_source(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 *pw_source) +u16 hpi_sample_clock_get_source(u32 h_control, u16 *pw_source) { - u16 error = 0; + u16 err = 0; u32 source = 0; - error = hpi_control_param1_get(ph_subsys, h_control, - HPI_SAMPLECLOCK_SOURCE, &source); - if (!error) + err = hpi_control_param1_get(h_control, HPI_SAMPLECLOCK_SOURCE, + &source); + if (!err) if (pw_source) *pw_source = (u16)source; - return error; + return err; } -u16 hpi_sample_clock_query_source_index(const struct hpi_hsubsys *ph_subsys, - const u32 h_clock, const u32 index, const u32 source, - u16 *pw_source_index) +u16 hpi_sample_clock_query_source_index(const u32 h_clock, const u32 index, + const u32 source, u16 *pw_source_index) { u32 qr; u16 err; - err = hpi_control_query(ph_subsys, h_clock, - HPI_SAMPLECLOCK_SOURCE_INDEX, index, source, &qr); + err = hpi_control_query(h_clock, HPI_SAMPLECLOCK_SOURCE_INDEX, index, + source, &qr); *pw_source_index = (u16)qr; return err; } -u16 hpi_sample_clock_set_source_index(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 source_index) +u16 hpi_sample_clock_set_source_index(u32 h_control, u16 source_index) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_SAMPLECLOCK_SOURCE_INDEX, source_index, 0); + return hpi_control_param_set(h_control, HPI_SAMPLECLOCK_SOURCE_INDEX, + source_index, 0); } -u16 hpi_sample_clock_get_source_index(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u16 *pw_source_index) +u16 hpi_sample_clock_get_source_index(u32 h_control, u16 *pw_source_index) { - u16 error = 0; + u16 err = 0; u32 source_index = 0; - error = hpi_control_param1_get(ph_subsys, h_control, - HPI_SAMPLECLOCK_SOURCE_INDEX, &source_index); - if (!error) + err = hpi_control_param1_get(h_control, HPI_SAMPLECLOCK_SOURCE_INDEX, + &source_index); + if (!err) if (pw_source_index) *pw_source_index = (u16)source_index; - return error; + return err; } -u16 hpi_sample_clock_query_local_rate(const struct hpi_hsubsys *ph_subsys, - const u32 h_clock, const u32 index, u32 *prate) +u16 hpi_sample_clock_query_local_rate(const u32 h_clock, const u32 index, + u32 *prate) { u16 err; - err = hpi_control_query(ph_subsys, h_clock, - HPI_SAMPLECLOCK_LOCAL_SAMPLERATE, index, 0, prate); + err = hpi_control_query(h_clock, HPI_SAMPLECLOCK_LOCAL_SAMPLERATE, + index, 0, prate); return err; } -u16 hpi_sample_clock_set_local_rate(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 sample_rate) +u16 hpi_sample_clock_set_local_rate(u32 h_control, u32 sample_rate) { - return hpi_control_param_set(ph_subsys, h_control, + return hpi_control_param_set(h_control, HPI_SAMPLECLOCK_LOCAL_SAMPLERATE, sample_rate, 0); } -u16 hpi_sample_clock_get_local_rate(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *psample_rate) +u16 hpi_sample_clock_get_local_rate(u32 h_control, u32 *psample_rate) { - u16 error = 0; + u16 err = 0; u32 sample_rate = 0; - error = hpi_control_param1_get(ph_subsys, h_control, + err = hpi_control_param1_get(h_control, HPI_SAMPLECLOCK_LOCAL_SAMPLERATE, &sample_rate); - if (!error) + if (!err) if (psample_rate) *psample_rate = sample_rate; - return error; + return err; } -u16 hpi_sample_clock_get_sample_rate(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *psample_rate) +u16 hpi_sample_clock_get_sample_rate(u32 h_control, u32 *psample_rate) { - u16 error = 0; + u16 err = 0; u32 sample_rate = 0; - error = hpi_control_param1_get(ph_subsys, h_control, - HPI_SAMPLECLOCK_SAMPLERATE, &sample_rate); - if (!error) + err = hpi_control_param1_get(h_control, HPI_SAMPLECLOCK_SAMPLERATE, + &sample_rate); + if (!err) if (psample_rate) *psample_rate = sample_rate; - return error; + return err; } -u16 hpi_sample_clock_set_auto(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 enable) +u16 hpi_sample_clock_set_auto(u32 h_control, u32 enable) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_SAMPLECLOCK_AUTO, enable, 0); + return hpi_control_param_set(h_control, HPI_SAMPLECLOCK_AUTO, enable, + 0); } -u16 hpi_sample_clock_get_auto(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *penable) +u16 hpi_sample_clock_get_auto(u32 h_control, u32 *penable) { - return hpi_control_param1_get(ph_subsys, h_control, - HPI_SAMPLECLOCK_AUTO, penable); + return hpi_control_param1_get(h_control, HPI_SAMPLECLOCK_AUTO, + penable); } -u16 hpi_sample_clock_set_local_rate_lock(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 lock) +u16 hpi_sample_clock_set_local_rate_lock(u32 h_control, u32 lock) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_SAMPLECLOCK_LOCAL_LOCK, lock, 0); + return hpi_control_param_set(h_control, HPI_SAMPLECLOCK_LOCAL_LOCK, + lock, 0); } -u16 hpi_sample_clock_get_local_rate_lock(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *plock) +u16 hpi_sample_clock_get_local_rate_lock(u32 h_control, u32 *plock) { - return hpi_control_param1_get(ph_subsys, h_control, - HPI_SAMPLECLOCK_LOCAL_LOCK, plock); + return hpi_control_param1_get(h_control, HPI_SAMPLECLOCK_LOCAL_LOCK, + plock); } -u16 hpi_tone_detector_get_frequency(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 index, u32 *frequency) +u16 hpi_tone_detector_get_frequency(u32 h_control, u32 index, u32 *frequency) { - return hpi_control_param_get(ph_subsys, h_control, - HPI_TONEDETECTOR_FREQUENCY, index, 0, frequency, NULL); + return hpi_control_param_get(h_control, HPI_TONEDETECTOR_FREQUENCY, + index, 0, frequency, NULL); } -u16 hpi_tone_detector_get_state(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *state) +u16 hpi_tone_detector_get_state(u32 h_control, u32 *state) { - return hpi_control_param1_get(ph_subsys, h_control, - HPI_TONEDETECTOR_STATE, state); + return hpi_control_param1_get(h_control, HPI_TONEDETECTOR_STATE, + state); } -u16 hpi_tone_detector_set_enable(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 enable) +u16 hpi_tone_detector_set_enable(u32 h_control, u32 enable) { - return hpi_control_param_set(ph_subsys, h_control, HPI_GENERIC_ENABLE, - (u32)enable, 0); + return hpi_control_param_set(h_control, HPI_GENERIC_ENABLE, enable, + 0); } -u16 hpi_tone_detector_get_enable(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *enable) +u16 hpi_tone_detector_get_enable(u32 h_control, u32 *enable) { - return hpi_control_param1_get(ph_subsys, h_control, - HPI_GENERIC_ENABLE, enable); + return hpi_control_param1_get(h_control, HPI_GENERIC_ENABLE, enable); } -u16 hpi_tone_detector_set_event_enable(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 event_enable) +u16 hpi_tone_detector_set_event_enable(u32 h_control, u32 event_enable) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_GENERIC_EVENT_ENABLE, (u32)event_enable, 0); + return hpi_control_param_set(h_control, HPI_GENERIC_EVENT_ENABLE, + (u32)event_enable, 0); } -u16 hpi_tone_detector_get_event_enable(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *event_enable) +u16 hpi_tone_detector_get_event_enable(u32 h_control, u32 *event_enable) { - return hpi_control_param1_get(ph_subsys, h_control, - HPI_GENERIC_EVENT_ENABLE, event_enable); + return hpi_control_param1_get(h_control, HPI_GENERIC_EVENT_ENABLE, + event_enable); } -u16 hpi_tone_detector_set_threshold(const struct hpi_hsubsys *ph_subsys, - u32 h_control, int threshold) +u16 hpi_tone_detector_set_threshold(u32 h_control, int threshold) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_TONEDETECTOR_THRESHOLD, (u32)threshold, 0); + return hpi_control_param_set(h_control, HPI_TONEDETECTOR_THRESHOLD, + (u32)threshold, 0); } -u16 hpi_tone_detector_get_threshold(const struct hpi_hsubsys *ph_subsys, - u32 h_control, int *threshold) +u16 hpi_tone_detector_get_threshold(u32 h_control, int *threshold) { - return hpi_control_param1_get(ph_subsys, h_control, - HPI_TONEDETECTOR_THRESHOLD, (u32 *)threshold); + return hpi_control_param1_get(h_control, HPI_TONEDETECTOR_THRESHOLD, + (u32 *)threshold); } -u16 hpi_silence_detector_get_state(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *state) +u16 hpi_silence_detector_get_state(u32 h_control, u32 *state) { - return hpi_control_param1_get(ph_subsys, h_control, - HPI_SILENCEDETECTOR_STATE, state); + return hpi_control_param1_get(h_control, HPI_SILENCEDETECTOR_STATE, + state); } -u16 hpi_silence_detector_set_enable(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 enable) +u16 hpi_silence_detector_set_enable(u32 h_control, u32 enable) { - return hpi_control_param_set(ph_subsys, h_control, HPI_GENERIC_ENABLE, - (u32)enable, 0); + return hpi_control_param_set(h_control, HPI_GENERIC_ENABLE, enable, + 0); } -u16 hpi_silence_detector_get_enable(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *enable) +u16 hpi_silence_detector_get_enable(u32 h_control, u32 *enable) { - return hpi_control_param1_get(ph_subsys, h_control, - HPI_GENERIC_ENABLE, enable); + return hpi_control_param1_get(h_control, HPI_GENERIC_ENABLE, enable); } -u16 hpi_silence_detector_set_event_enable(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 event_enable) +u16 hpi_silence_detector_set_event_enable(u32 h_control, u32 event_enable) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_GENERIC_EVENT_ENABLE, event_enable, 0); + return hpi_control_param_set(h_control, HPI_GENERIC_EVENT_ENABLE, + event_enable, 0); } -u16 hpi_silence_detector_get_event_enable(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *event_enable) +u16 hpi_silence_detector_get_event_enable(u32 h_control, u32 *event_enable) { - return hpi_control_param1_get(ph_subsys, h_control, - HPI_GENERIC_EVENT_ENABLE, event_enable); + return hpi_control_param1_get(h_control, HPI_GENERIC_EVENT_ENABLE, + event_enable); } -u16 hpi_silence_detector_set_delay(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 delay) +u16 hpi_silence_detector_set_delay(u32 h_control, u32 delay) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_SILENCEDETECTOR_DELAY, delay, 0); + return hpi_control_param_set(h_control, HPI_SILENCEDETECTOR_DELAY, + delay, 0); } -u16 hpi_silence_detector_get_delay(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *delay) +u16 hpi_silence_detector_get_delay(u32 h_control, u32 *delay) { - return hpi_control_param1_get(ph_subsys, h_control, - HPI_SILENCEDETECTOR_DELAY, delay); + return hpi_control_param1_get(h_control, HPI_SILENCEDETECTOR_DELAY, + delay); } -u16 hpi_silence_detector_set_threshold(const struct hpi_hsubsys *ph_subsys, - u32 h_control, int threshold) +u16 hpi_silence_detector_set_threshold(u32 h_control, int threshold) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_SILENCEDETECTOR_THRESHOLD, threshold, 0); + return hpi_control_param_set(h_control, HPI_SILENCEDETECTOR_THRESHOLD, + threshold, 0); } -u16 hpi_silence_detector_get_threshold(const struct hpi_hsubsys *ph_subsys, - u32 h_control, int *threshold) +u16 hpi_silence_detector_get_threshold(u32 h_control, int *threshold) { - return hpi_control_param1_get(ph_subsys, h_control, + return hpi_control_param1_get(h_control, HPI_SILENCEDETECTOR_THRESHOLD, (u32 *)threshold); } -u16 hpi_tuner_query_band(const struct hpi_hsubsys *ph_subsys, - const u32 h_tuner, const u32 index, u16 *pw_band) +u16 hpi_tuner_query_band(const u32 h_tuner, const u32 index, u16 *pw_band) { u32 qr; u16 err; - err = hpi_control_query(ph_subsys, h_tuner, HPI_TUNER_BAND, index, 0, - &qr); + err = hpi_control_query(h_tuner, HPI_TUNER_BAND, index, 0, &qr); *pw_band = (u16)qr; return err; } -u16 hpi_tuner_set_band(const struct hpi_hsubsys *ph_subsys, u32 h_control, - u16 band) +u16 hpi_tuner_set_band(u32 h_control, u16 band) { - return hpi_control_param_set(ph_subsys, h_control, HPI_TUNER_BAND, - band, 0); + return hpi_control_param_set(h_control, HPI_TUNER_BAND, band, 0); } -u16 hpi_tuner_get_band(const struct hpi_hsubsys *ph_subsys, u32 h_control, - u16 *pw_band) +u16 hpi_tuner_get_band(u32 h_control, u16 *pw_band) { u32 band = 0; u16 error = 0; - error = hpi_control_param1_get(ph_subsys, h_control, HPI_TUNER_BAND, - &band); + error = hpi_control_param1_get(h_control, HPI_TUNER_BAND, &band); if (pw_band) *pw_band = (u16)band; return error; } -u16 hpi_tuner_query_frequency(const struct hpi_hsubsys *ph_subsys, - const u32 h_tuner, const u32 index, const u16 band, u32 *pfreq) +u16 hpi_tuner_query_frequency(const u32 h_tuner, const u32 index, + const u16 band, u32 *pfreq) { - return hpi_control_query(ph_subsys, h_tuner, HPI_TUNER_FREQ, index, - band, pfreq); + return hpi_control_query(h_tuner, HPI_TUNER_FREQ, index, band, pfreq); } -u16 hpi_tuner_set_frequency(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 freq_ink_hz) +u16 hpi_tuner_set_frequency(u32 h_control, u32 freq_ink_hz) { - return hpi_control_param_set(ph_subsys, h_control, HPI_TUNER_FREQ, - freq_ink_hz, 0); + return hpi_control_param_set(h_control, HPI_TUNER_FREQ, freq_ink_hz, + 0); } -u16 hpi_tuner_get_frequency(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *pw_freq_ink_hz) +u16 hpi_tuner_get_frequency(u32 h_control, u32 *pw_freq_ink_hz) { - return hpi_control_param1_get(ph_subsys, h_control, HPI_TUNER_FREQ, + return hpi_control_param1_get(h_control, HPI_TUNER_FREQ, pw_freq_ink_hz); } -u16 hpi_tuner_query_gain(const struct hpi_hsubsys *ph_subsys, - const u32 h_tuner, const u32 index, u16 *pw_gain) +u16 hpi_tuner_query_gain(const u32 h_tuner, const u32 index, u16 *pw_gain) { u32 qr; u16 err; - err = hpi_control_query(ph_subsys, h_tuner, HPI_TUNER_BAND, index, 0, - &qr); + err = hpi_control_query(h_tuner, HPI_TUNER_BAND, index, 0, &qr); *pw_gain = (u16)qr; return err; } -u16 hpi_tuner_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control, - short gain) +u16 hpi_tuner_set_gain(u32 h_control, short gain) { - return hpi_control_param_set(ph_subsys, h_control, HPI_TUNER_GAIN, - gain, 0); + return hpi_control_param_set(h_control, HPI_TUNER_GAIN, gain, 0); } -u16 hpi_tuner_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control, - short *pn_gain) +u16 hpi_tuner_get_gain(u32 h_control, short *pn_gain) { u32 gain = 0; u16 error = 0; - error = hpi_control_param1_get(ph_subsys, h_control, HPI_TUNER_GAIN, - &gain); + error = hpi_control_param1_get(h_control, HPI_TUNER_GAIN, &gain); if (pn_gain) *pn_gain = (u16)gain; return error; } -u16 hpi_tuner_getRF_level(const struct hpi_hsubsys *ph_subsys, u32 h_control, - short *pw_level) +u16 hpi_tuner_get_rf_level(u32 h_control, short *pw_level) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); - hm.u.c.attribute = HPI_TUNER_LEVEL; - hm.u.c.param1 = HPI_TUNER_LEVEL_AVERAGE; + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; + hm.u.cu.attribute = HPI_TUNER_LEVEL_AVG; hpi_send_recv(&hm, &hr); if (pw_level) - *pw_level = (short)hr.u.c.param1; + *pw_level = hr.u.cu.tuner.s_level; return hr.error; } -u16 hpi_tuner_get_rawRF_level(const struct hpi_hsubsys *ph_subsys, - u32 h_control, short *pw_level) +u16 hpi_tuner_get_raw_rf_level(u32 h_control, short *pw_level) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); - hm.u.c.attribute = HPI_TUNER_LEVEL; - hm.u.c.param1 = HPI_TUNER_LEVEL_RAW; + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; + hm.u.cu.attribute = HPI_TUNER_LEVEL_RAW; hpi_send_recv(&hm, &hr); if (pw_level) - *pw_level = (short)hr.u.c.param1; + *pw_level = hr.u.cu.tuner.s_level; return hr.error; } -u16 hpi_tuner_query_deemphasis(const struct hpi_hsubsys *ph_subsys, - const u32 h_tuner, const u32 index, const u16 band, u32 *pdeemphasis) +u16 hpi_tuner_query_deemphasis(const u32 h_tuner, const u32 index, + const u16 band, u32 *pdeemphasis) { - return hpi_control_query(ph_subsys, h_tuner, HPI_TUNER_DEEMPHASIS, - index, band, pdeemphasis); + return hpi_control_query(h_tuner, HPI_TUNER_DEEMPHASIS, index, band, + pdeemphasis); } -u16 hpi_tuner_set_deemphasis(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 deemphasis) +u16 hpi_tuner_set_deemphasis(u32 h_control, u32 deemphasis) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_TUNER_DEEMPHASIS, deemphasis, 0); + return hpi_control_param_set(h_control, HPI_TUNER_DEEMPHASIS, + deemphasis, 0); } -u16 hpi_tuner_get_deemphasis(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *pdeemphasis) +u16 hpi_tuner_get_deemphasis(u32 h_control, u32 *pdeemphasis) { - return hpi_control_param1_get(ph_subsys, h_control, - HPI_TUNER_DEEMPHASIS, pdeemphasis); + return hpi_control_param1_get(h_control, HPI_TUNER_DEEMPHASIS, + pdeemphasis); } -u16 hpi_tuner_query_program(const struct hpi_hsubsys *ph_subsys, - const u32 h_tuner, u32 *pbitmap_program) +u16 hpi_tuner_query_program(const u32 h_tuner, u32 *pbitmap_program) { - return hpi_control_query(ph_subsys, h_tuner, HPI_TUNER_PROGRAM, 0, 0, + return hpi_control_query(h_tuner, HPI_TUNER_PROGRAM, 0, 0, pbitmap_program); } -u16 hpi_tuner_set_program(const struct hpi_hsubsys *ph_subsys, u32 h_control, - u32 program) +u16 hpi_tuner_set_program(u32 h_control, u32 program) { - return hpi_control_param_set(ph_subsys, h_control, HPI_TUNER_PROGRAM, - program, 0); + return hpi_control_param_set(h_control, HPI_TUNER_PROGRAM, program, + 0); } -u16 hpi_tuner_get_program(const struct hpi_hsubsys *ph_subsys, u32 h_control, - u32 *pprogram) +u16 hpi_tuner_get_program(u32 h_control, u32 *pprogram) { - return hpi_control_param1_get(ph_subsys, h_control, HPI_TUNER_PROGRAM, - pprogram); + return hpi_control_param1_get(h_control, HPI_TUNER_PROGRAM, pprogram); } -u16 hpi_tuner_get_hd_radio_dsp_version(const struct hpi_hsubsys *ph_subsys, - u32 h_control, char *psz_dsp_version, const u32 string_size) +u16 hpi_tuner_get_hd_radio_dsp_version(u32 h_control, char *psz_dsp_version, + const u32 string_size) { return hpi_control_get_string(h_control, HPI_TUNER_HDRADIO_DSP_VERSION, psz_dsp_version, string_size); } -u16 hpi_tuner_get_hd_radio_sdk_version(const struct hpi_hsubsys *ph_subsys, - u32 h_control, char *psz_sdk_version, const u32 string_size) +u16 hpi_tuner_get_hd_radio_sdk_version(u32 h_control, char *psz_sdk_version, + const u32 string_size) { return hpi_control_get_string(h_control, HPI_TUNER_HDRADIO_SDK_VERSION, psz_sdk_version, string_size); } -u16 hpi_tuner_get_status(const struct hpi_hsubsys *ph_subsys, u32 h_control, - u16 *pw_status_mask, u16 *pw_status) +u16 hpi_tuner_get_status(u32 h_control, u16 *pw_status_mask, u16 *pw_status) { u32 status = 0; u16 error = 0; - error = hpi_control_param1_get(ph_subsys, h_control, HPI_TUNER_STATUS, - &status); + error = hpi_control_param1_get(h_control, HPI_TUNER_STATUS, &status); if (pw_status) { if (!error) { *pw_status_mask = (u16)(status >> 16); @@ -3005,50 +2685,44 @@ u16 hpi_tuner_get_status(const struct hpi_hsubsys *ph_subsys, u32 h_control, return error; } -u16 hpi_tuner_set_mode(const struct hpi_hsubsys *ph_subsys, u32 h_control, - u32 mode, u32 value) +u16 hpi_tuner_set_mode(u32 h_control, u32 mode, u32 value) { - return hpi_control_param_set(ph_subsys, h_control, HPI_TUNER_MODE, - mode, value); + return hpi_control_param_set(h_control, HPI_TUNER_MODE, mode, value); } -u16 hpi_tuner_get_mode(const struct hpi_hsubsys *ph_subsys, u32 h_control, - u32 mode, u32 *pn_value) +u16 hpi_tuner_get_mode(u32 h_control, u32 mode, u32 *pn_value) { - return hpi_control_param_get(ph_subsys, h_control, HPI_TUNER_MODE, - mode, 0, pn_value, NULL); + return hpi_control_param_get(h_control, HPI_TUNER_MODE, mode, 0, + pn_value, NULL); } -u16 hpi_tuner_get_hd_radio_signal_quality(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *pquality) +u16 hpi_tuner_get_hd_radio_signal_quality(u32 h_control, u32 *pquality) { - return hpi_control_param1_get(ph_subsys, h_control, + return hpi_control_param1_get(h_control, HPI_TUNER_HDRADIO_SIGNAL_QUALITY, pquality); } -u16 hpi_tuner_get_hd_radio_signal_blend(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *pblend) +u16 hpi_tuner_get_hd_radio_signal_blend(u32 h_control, u32 *pblend) { - return hpi_control_param1_get(ph_subsys, h_control, - HPI_TUNER_HDRADIO_BLEND, pblend); + return hpi_control_param1_get(h_control, HPI_TUNER_HDRADIO_BLEND, + pblend); } -u16 hpi_tuner_set_hd_radio_signal_blend(const struct hpi_hsubsys *ph_subsys, - u32 h_control, const u32 blend) +u16 hpi_tuner_set_hd_radio_signal_blend(u32 h_control, const u32 blend) { - return hpi_control_param_set(ph_subsys, h_control, - HPI_TUNER_HDRADIO_BLEND, blend, 0); + return hpi_control_param_set(h_control, HPI_TUNER_HDRADIO_BLEND, + blend, 0); } -u16 hpi_tuner_getRDS(const struct hpi_hsubsys *ph_subsys, u32 h_control, - char *p_data) +u16 hpi_tuner_get_rds(u32 h_control, char *p_data) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.c.attribute = HPI_TUNER_RDS; hpi_send_recv(&hm, &hr); if (p_data) { @@ -3059,80 +2733,82 @@ u16 hpi_tuner_getRDS(const struct hpi_hsubsys *ph_subsys, u32 h_control, return hr.error; } -u16 HPI_PAD__get_channel_name(const struct hpi_hsubsys *ph_subsys, - u32 h_control, char *psz_string, const u32 data_length) +u16 hpi_pad_get_channel_name(u32 h_control, char *psz_string, + const u32 data_length) { return hpi_control_get_string(h_control, HPI_PAD_CHANNEL_NAME, psz_string, data_length); } -u16 HPI_PAD__get_artist(const struct hpi_hsubsys *ph_subsys, u32 h_control, - char *psz_string, const u32 data_length) +u16 hpi_pad_get_artist(u32 h_control, char *psz_string, const u32 data_length) { return hpi_control_get_string(h_control, HPI_PAD_ARTIST, psz_string, data_length); } -u16 HPI_PAD__get_title(const struct hpi_hsubsys *ph_subsys, u32 h_control, - char *psz_string, const u32 data_length) +u16 hpi_pad_get_title(u32 h_control, char *psz_string, const u32 data_length) { return hpi_control_get_string(h_control, HPI_PAD_TITLE, psz_string, data_length); } -u16 HPI_PAD__get_comment(const struct hpi_hsubsys *ph_subsys, u32 h_control, - char *psz_string, const u32 data_length) +u16 hpi_pad_get_comment(u32 h_control, char *psz_string, + const u32 data_length) { return hpi_control_get_string(h_control, HPI_PAD_COMMENT, psz_string, data_length); } -u16 HPI_PAD__get_program_type(const struct hpi_hsubsys *ph_subsys, - u32 h_control, u32 *ppTY) +u16 hpi_pad_get_program_type(u32 h_control, u32 *ppTY) { - return hpi_control_param1_get(ph_subsys, h_control, - HPI_PAD_PROGRAM_TYPE, ppTY); + return hpi_control_param1_get(h_control, HPI_PAD_PROGRAM_TYPE, ppTY); } -u16 HPI_PAD__get_rdsPI(const struct hpi_hsubsys *ph_subsys, u32 h_control, - u32 *ppI) +u16 hpi_pad_get_rdsPI(u32 h_control, u32 *ppI) { - return hpi_control_param1_get(ph_subsys, h_control, - HPI_PAD_PROGRAM_ID, ppI); + return hpi_control_param1_get(h_control, HPI_PAD_PROGRAM_ID, ppI); } -u16 hpi_volume_query_channels(const struct hpi_hsubsys *ph_subsys, - const u32 h_volume, u32 *p_channels) +u16 hpi_volume_query_channels(const u32 h_volume, u32 *p_channels) { - return hpi_control_query(ph_subsys, h_volume, HPI_VOLUME_NUM_CHANNELS, - 0, 0, p_channels); + return hpi_control_query(h_volume, HPI_VOLUME_NUM_CHANNELS, 0, 0, + p_channels); } -u16 hpi_volume_set_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control, - short an_log_gain[HPI_MAX_CHANNELS] +u16 hpi_volume_set_gain(u32 h_control, short an_log_gain[HPI_MAX_CHANNELS] ) { return hpi_control_log_set2(h_control, HPI_VOLUME_GAIN, an_log_gain[0], an_log_gain[1]); } -u16 hpi_volume_get_gain(const struct hpi_hsubsys *ph_subsys, u32 h_control, - short an_log_gain[HPI_MAX_CHANNELS] +u16 hpi_volume_get_gain(u32 h_control, short an_log_gain[HPI_MAX_CHANNELS] ) { - return hpi_control_log_get2(ph_subsys, h_control, HPI_VOLUME_GAIN, + return hpi_control_log_get2(h_control, HPI_VOLUME_GAIN, &an_log_gain[0], &an_log_gain[1]); } -u16 hpi_volume_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control, - short *min_gain_01dB, short *max_gain_01dB, short *step_gain_01dB) +u16 hpi_volume_set_mute(u32 h_control, u32 mute) +{ + return hpi_control_param_set(h_control, HPI_VOLUME_MUTE, mute, 0); +} + +u16 hpi_volume_get_mute(u32 h_control, u32 *mute) +{ + return hpi_control_param1_get(h_control, HPI_VOLUME_MUTE, mute); +} + +u16 hpi_volume_query_range(u32 h_control, short *min_gain_01dB, + short *max_gain_01dB, short *step_gain_01dB) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.c.attribute = HPI_VOLUME_RANGE; hpi_send_recv(&hm, &hr); @@ -3150,16 +2826,17 @@ u16 hpi_volume_query_range(const struct hpi_hsubsys *ph_subsys, u32 h_control, return hr.error; } -u16 hpi_volume_auto_fade_profile(const struct hpi_hsubsys *ph_subsys, - u32 h_control, short an_stop_gain0_01dB[HPI_MAX_CHANNELS], - u32 duration_ms, u16 profile) +u16 hpi_volume_auto_fade_profile(u32 h_control, + short an_stop_gain0_01dB[HPI_MAX_CHANNELS], u32 duration_ms, + u16 profile) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_SET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; memcpy(hm.u.c.an_log_value, an_stop_gain0_01dB, sizeof(short) * HPI_MAX_CHANNELS); @@ -3173,21 +2850,21 @@ u16 hpi_volume_auto_fade_profile(const struct hpi_hsubsys *ph_subsys, return hr.error; } -u16 hpi_volume_auto_fade(const struct hpi_hsubsys *ph_subsys, u32 h_control, +u16 hpi_volume_auto_fade(u32 h_control, short an_stop_gain0_01dB[HPI_MAX_CHANNELS], u32 duration_ms) { - return hpi_volume_auto_fade_profile(ph_subsys, h_control, - an_stop_gain0_01dB, duration_ms, HPI_VOLUME_AUTOFADE_LOG); + return hpi_volume_auto_fade_profile(h_control, an_stop_gain0_01dB, + duration_ms, HPI_VOLUME_AUTOFADE_LOG); } -u16 hpi_vox_set_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control, - short an_gain0_01dB) +u16 hpi_vox_set_threshold(u32 h_control, short an_gain0_01dB) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_SET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.c.attribute = HPI_VOX_THRESHOLD; hm.u.c.an_log_value[0] = an_gain0_01dB; @@ -3197,14 +2874,14 @@ u16 hpi_vox_set_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control, return hr.error; } -u16 hpi_vox_get_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control, - short *an_gain0_01dB) +u16 hpi_vox_get_threshold(u32 h_control, short *an_gain0_01dB) { struct hpi_message hm; struct hpi_response hr; hpi_init_message_response(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE); - u32TOINDEXES(h_control, &hm.adapter_index, &hm.obj_index); + if (hpi_handle_indexes(h_control, &hm.adapter_index, &hm.obj_index)) + return HPI_ERROR_INVALID_HANDLE; hm.u.c.attribute = HPI_VOX_THRESHOLD; hpi_send_recv(&hm, &hr); @@ -3213,728 +2890,3 @@ u16 hpi_vox_get_threshold(const struct hpi_hsubsys *ph_subsys, u32 h_control, return hr.error; } - -static size_t strv_packet_size = MIN_STRV_PACKET_SIZE; - -static size_t entity_type_to_size[LAST_ENTITY_TYPE] = { - 0, - sizeof(struct hpi_entity), - sizeof(void *), - - sizeof(int), - sizeof(float), - sizeof(double), - - sizeof(char), - sizeof(char), - - 4 * sizeof(char), - 16 * sizeof(char), - 6 * sizeof(char), -}; - -static inline size_t hpi_entity_size(struct hpi_entity *entity_ptr) -{ - return entity_ptr->header.size; -} - -static inline size_t hpi_entity_header_size(struct hpi_entity *entity_ptr) -{ - return sizeof(entity_ptr->header); -} - -static inline size_t hpi_entity_value_size(struct hpi_entity *entity_ptr) -{ - return hpi_entity_size(entity_ptr) - - hpi_entity_header_size(entity_ptr); -} - -static inline size_t hpi_entity_item_count(struct hpi_entity *entity_ptr) -{ - return hpi_entity_value_size(entity_ptr) / - entity_type_to_size[entity_ptr->header.type]; -} - -static inline struct hpi_entity *hpi_entity_ptr_to_next(struct hpi_entity - *entity_ptr) -{ - return (void *)(((u8 *)entity_ptr) + hpi_entity_size(entity_ptr)); -} - -static inline u16 hpi_entity_check_type(const enum e_entity_type t) -{ - if (t >= 0 && t < STR_TYPE_FIELD_MAX) - return 0; - return HPI_ERROR_ENTITY_TYPE_INVALID; -} - -static inline u16 hpi_entity_check_role(const enum e_entity_role r) -{ - if (r >= 0 && r < STR_ROLE_FIELD_MAX) - return 0; - return HPI_ERROR_ENTITY_ROLE_INVALID; -} - -static u16 hpi_entity_get_next(struct hpi_entity *entity, int recursive_flag, - void *guard_p, struct hpi_entity **next) -{ - HPI_DEBUG_ASSERT(entity != NULL); - HPI_DEBUG_ASSERT(next != NULL); - HPI_DEBUG_ASSERT(hpi_entity_size(entity) != 0); - - if (guard_p <= (void *)entity) { - *next = NULL; - return 0; - } - - if (recursive_flag && entity->header.type == entity_type_sequence) - *next = (struct hpi_entity *)entity->value; - else - *next = (struct hpi_entity *)hpi_entity_ptr_to_next(entity); - - if (guard_p <= (void *)*next) { - *next = NULL; - return 0; - } - - HPI_DEBUG_ASSERT(guard_p >= (void *)hpi_entity_ptr_to_next(*next)); - return 0; -} - -u16 hpi_entity_find_next(struct hpi_entity *container_entity, - enum e_entity_type type, enum e_entity_role role, int recursive_flag, - struct hpi_entity **current_match) -{ - struct hpi_entity *tmp = NULL; - void *guard_p = NULL; - - HPI_DEBUG_ASSERT(container_entity != NULL); - guard_p = hpi_entity_ptr_to_next(container_entity); - - if (*current_match != NULL) - hpi_entity_get_next(*current_match, recursive_flag, guard_p, - &tmp); - else - hpi_entity_get_next(container_entity, 1, guard_p, &tmp); - - while (tmp) { - u16 err; - - HPI_DEBUG_ASSERT((void *)tmp >= (void *)container_entity); - - if ((!type || tmp->header.type == type) && (!role - || tmp->header.role == role)) { - *current_match = tmp; - return 0; - } - - err = hpi_entity_get_next(tmp, recursive_flag, guard_p, - current_match); - if (err) - return err; - - tmp = *current_match; - } - - *current_match = NULL; - return 0; -} - -void hpi_entity_free(struct hpi_entity *entity) -{ - kfree(entity); -} - -static u16 hpi_entity_alloc_and_copy(struct hpi_entity *src, - struct hpi_entity **dst) -{ - size_t buf_size; - HPI_DEBUG_ASSERT(dst != NULL); - HPI_DEBUG_ASSERT(src != NULL); - - buf_size = hpi_entity_size(src); - *dst = kmalloc(buf_size, GFP_KERNEL); - if (*dst == NULL) - return HPI_ERROR_MEMORY_ALLOC; - memcpy(*dst, src, buf_size); - return 0; -} - -u16 hpi_universal_info(const struct hpi_hsubsys *ph_subsys, u32 hC, - struct hpi_entity **info) -{ - struct hpi_msg_strv hm; - struct hpi_res_strv *phr; - u16 hpi_err; - int remaining_attempts = 2; - size_t resp_packet_size = 1024; - - *info = NULL; - - while (remaining_attempts--) { - phr = kmalloc(resp_packet_size, GFP_KERNEL); - HPI_DEBUG_ASSERT(phr != NULL); - - hpi_init_message_responseV1(&hm.h, (u16)sizeof(hm), &phr->h, - (u16)resp_packet_size, HPI_OBJ_CONTROL, - HPI_CONTROL_GET_INFO); - u32TOINDEXES(hC, &hm.h.adapter_index, &hm.h.obj_index); - - hm.strv.header.size = sizeof(hm.strv); - phr->strv.header.size = resp_packet_size - sizeof(phr->h); - - hpi_send_recv((struct hpi_message *)&hm.h, - (struct hpi_response *)&phr->h); - if (phr->h.error == HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL) { - - HPI_DEBUG_ASSERT(phr->h.specific_error > - MIN_STRV_PACKET_SIZE - && phr->h.specific_error < 1500); - resp_packet_size = phr->h.specific_error; - } else { - remaining_attempts = 0; - if (!phr->h.error) - hpi_entity_alloc_and_copy(&phr->strv, info); - } - - hpi_err = phr->h.error; - kfree(phr); - } - - return hpi_err; -} - -u16 hpi_universal_get(const struct hpi_hsubsys *ph_subsys, u32 hC, - struct hpi_entity **value) -{ - struct hpi_msg_strv hm; - struct hpi_res_strv *phr; - u16 hpi_err; - int remaining_attempts = 2; - - *value = NULL; - - while (remaining_attempts--) { - phr = kmalloc(strv_packet_size, GFP_KERNEL); - if (!phr) - return HPI_ERROR_MEMORY_ALLOC; - - hpi_init_message_responseV1(&hm.h, (u16)sizeof(hm), &phr->h, - (u16)strv_packet_size, HPI_OBJ_CONTROL, - HPI_CONTROL_GET_STATE); - u32TOINDEXES(hC, &hm.h.adapter_index, &hm.h.obj_index); - - hm.strv.header.size = sizeof(hm.strv); - phr->strv.header.size = strv_packet_size - sizeof(phr->h); - - hpi_send_recv((struct hpi_message *)&hm.h, - (struct hpi_response *)&phr->h); - if (phr->h.error == HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL) { - - HPI_DEBUG_ASSERT(phr->h.specific_error > - MIN_STRV_PACKET_SIZE - && phr->h.specific_error < 1000); - strv_packet_size = phr->h.specific_error; - } else { - remaining_attempts = 0; - if (!phr->h.error) - hpi_entity_alloc_and_copy(&phr->strv, value); - } - - hpi_err = phr->h.error; - kfree(phr); - } - - return hpi_err; -} - -u16 hpi_universal_set(const struct hpi_hsubsys *ph_subsys, u32 hC, - struct hpi_entity *value) -{ - struct hpi_msg_strv *phm; - struct hpi_res_strv hr; - - phm = kmalloc(sizeof(phm->h) + value->header.size, GFP_KERNEL); - HPI_DEBUG_ASSERT(phm != NULL); - - hpi_init_message_responseV1(&phm->h, - sizeof(phm->h) + value->header.size, &hr.h, sizeof(hr), - HPI_OBJ_CONTROL, HPI_CONTROL_SET_STATE); - u32TOINDEXES(hC, &phm->h.adapter_index, &phm->h.obj_index); - hr.strv.header.size = sizeof(hr.strv); - - memcpy(&phm->strv, value, value->header.size); - hpi_send_recv((struct hpi_message *)&phm->h, - (struct hpi_response *)&hr.h); - - return hr.h.error; -} - -u16 hpi_entity_alloc_and_pack(const enum e_entity_type type, - const size_t item_count, const enum e_entity_role role, void *value, - struct hpi_entity **entity) -{ - size_t bytes_to_copy, total_size; - u16 hE = 0; - *entity = NULL; - - hE = hpi_entity_check_type(type); - if (hE) - return hE; - - HPI_DEBUG_ASSERT(role > entity_role_null && type < LAST_ENTITY_TYPE); - - bytes_to_copy = entity_type_to_size[type] * item_count; - total_size = hpi_entity_header_size(*entity) + bytes_to_copy; - - HPI_DEBUG_ASSERT(total_size >= hpi_entity_header_size(*entity) - && total_size < STR_SIZE_FIELD_MAX); - - *entity = kmalloc(total_size, GFP_KERNEL); - if (*entity == NULL) - return HPI_ERROR_MEMORY_ALLOC; - memcpy((*entity)->value, value, bytes_to_copy); - (*entity)->header.size = - hpi_entity_header_size(*entity) + bytes_to_copy; - (*entity)->header.type = type; - (*entity)->header.role = role; - return 0; -} - -u16 hpi_entity_copy_value_from(struct hpi_entity *entity, - enum e_entity_type type, size_t item_count, void *value_dst_p) -{ - size_t bytes_to_copy; - - if (entity->header.type != type) - return HPI_ERROR_ENTITY_TYPE_MISMATCH; - - if (hpi_entity_item_count(entity) != item_count) - return HPI_ERROR_ENTITY_ITEM_COUNT; - - bytes_to_copy = entity_type_to_size[type] * item_count; - memcpy(value_dst_p, entity->value, bytes_to_copy); - return 0; -} - -u16 hpi_entity_unpack(struct hpi_entity *entity, enum e_entity_type *type, - size_t *item_count, enum e_entity_role *role, void **value) -{ - u16 err = 0; - HPI_DEBUG_ASSERT(entity != NULL); - - if (type) - *type = entity->header.type; - - if (role) - *role = entity->header.role; - - if (value) - *value = entity->value; - - if (item_count != NULL) { - if (entity->header.type == entity_type_sequence) { - void *guard_p = hpi_entity_ptr_to_next(entity); - struct hpi_entity *next = NULL; - void *contents = entity->value; - - *item_count = 0; - while (contents < guard_p) { - (*item_count)++; - err = hpi_entity_get_next(contents, 0, - guard_p, &next); - if (next == NULL || err) - break; - contents = next; - } - } else { - *item_count = hpi_entity_item_count(entity); - } - } - return err; -} - -u16 hpi_gpio_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, - u32 *ph_gpio, u16 *pw_number_input_bits, u16 *pw_number_output_bits) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_GPIO, HPI_GPIO_OPEN); - hm.adapter_index = adapter_index; - - hpi_send_recv(&hm, &hr); - - if (hr.error == 0) { - *ph_gpio = - hpi_indexes_to_handle(HPI_OBJ_GPIO, adapter_index, 0); - if (pw_number_input_bits) - *pw_number_input_bits = hr.u.l.number_input_bits; - if (pw_number_output_bits) - *pw_number_output_bits = hr.u.l.number_output_bits; - } else - *ph_gpio = 0; - return hr.error; -} - -u16 hpi_gpio_read_bit(const struct hpi_hsubsys *ph_subsys, u32 h_gpio, - u16 bit_index, u16 *pw_bit_data) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_GPIO, HPI_GPIO_READ_BIT); - u32TOINDEX(h_gpio, &hm.adapter_index); - hm.u.l.bit_index = bit_index; - - hpi_send_recv(&hm, &hr); - - *pw_bit_data = hr.u.l.bit_data[0]; - return hr.error; -} - -u16 hpi_gpio_read_all_bits(const struct hpi_hsubsys *ph_subsys, u32 h_gpio, - u16 aw_all_bit_data[4] - ) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_GPIO, HPI_GPIO_READ_ALL); - u32TOINDEX(h_gpio, &hm.adapter_index); - - hpi_send_recv(&hm, &hr); - - if (aw_all_bit_data) { - aw_all_bit_data[0] = hr.u.l.bit_data[0]; - aw_all_bit_data[1] = hr.u.l.bit_data[1]; - aw_all_bit_data[2] = hr.u.l.bit_data[2]; - aw_all_bit_data[3] = hr.u.l.bit_data[3]; - } - return hr.error; -} - -u16 hpi_gpio_write_bit(const struct hpi_hsubsys *ph_subsys, u32 h_gpio, - u16 bit_index, u16 bit_data) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_GPIO, HPI_GPIO_WRITE_BIT); - u32TOINDEX(h_gpio, &hm.adapter_index); - hm.u.l.bit_index = bit_index; - hm.u.l.bit_data = bit_data; - - hpi_send_recv(&hm, &hr); - - return hr.error; -} - -u16 hpi_gpio_write_status(const struct hpi_hsubsys *ph_subsys, u32 h_gpio, - u16 aw_all_bit_data[4] - ) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_GPIO, - HPI_GPIO_WRITE_STATUS); - u32TOINDEX(h_gpio, &hm.adapter_index); - - hpi_send_recv(&hm, &hr); - - if (aw_all_bit_data) { - aw_all_bit_data[0] = hr.u.l.bit_data[0]; - aw_all_bit_data[1] = hr.u.l.bit_data[1]; - aw_all_bit_data[2] = hr.u.l.bit_data[2]; - aw_all_bit_data[3] = hr.u.l.bit_data[3]; - } - return hr.error; -} - -u16 hpi_async_event_open(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u32 *ph_async) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_ASYNCEVENT, - HPI_ASYNCEVENT_OPEN); - hm.adapter_index = adapter_index; - - hpi_send_recv(&hm, &hr); - - if (hr.error == 0) - - *ph_async = - hpi_indexes_to_handle(HPI_OBJ_ASYNCEVENT, - adapter_index, 0); - else - *ph_async = 0; - return hr.error; - -} - -u16 hpi_async_event_close(const struct hpi_hsubsys *ph_subsys, u32 h_async) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_ASYNCEVENT, - HPI_ASYNCEVENT_OPEN); - u32TOINDEX(h_async, &hm.adapter_index); - - hpi_send_recv(&hm, &hr); - - return hr.error; -} - -u16 hpi_async_event_wait(const struct hpi_hsubsys *ph_subsys, u32 h_async, - u16 maximum_events, struct hpi_async_event *p_events, - u16 *pw_number_returned) -{ - - return 0; -} - -u16 hpi_async_event_get_count(const struct hpi_hsubsys *ph_subsys, - u32 h_async, u16 *pw_count) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_ASYNCEVENT, - HPI_ASYNCEVENT_GETCOUNT); - u32TOINDEX(h_async, &hm.adapter_index); - - hpi_send_recv(&hm, &hr); - - if (hr.error == 0) - if (pw_count) - *pw_count = hr.u.as.u.count.count; - - return hr.error; -} - -u16 hpi_async_event_get(const struct hpi_hsubsys *ph_subsys, u32 h_async, - u16 maximum_events, struct hpi_async_event *p_events, - u16 *pw_number_returned) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_ASYNCEVENT, - HPI_ASYNCEVENT_GET); - u32TOINDEX(h_async, &hm.adapter_index); - - hpi_send_recv(&hm, &hr); - if (!hr.error) { - memcpy(p_events, &hr.u.as.u.event, - sizeof(struct hpi_async_event)); - *pw_number_returned = 1; - } - - return hr.error; -} - -u16 hpi_nv_memory_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, - u32 *ph_nv_memory, u16 *pw_size_in_bytes) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_NVMEMORY, - HPI_NVMEMORY_OPEN); - hm.adapter_index = adapter_index; - - hpi_send_recv(&hm, &hr); - - if (hr.error == 0) { - *ph_nv_memory = - hpi_indexes_to_handle(HPI_OBJ_NVMEMORY, adapter_index, - 0); - if (pw_size_in_bytes) - *pw_size_in_bytes = hr.u.n.size_in_bytes; - } else - *ph_nv_memory = 0; - return hr.error; -} - -u16 hpi_nv_memory_read_byte(const struct hpi_hsubsys *ph_subsys, - u32 h_nv_memory, u16 index, u16 *pw_data) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_NVMEMORY, - HPI_NVMEMORY_READ_BYTE); - u32TOINDEX(h_nv_memory, &hm.adapter_index); - hm.u.n.address = index; - - hpi_send_recv(&hm, &hr); - - *pw_data = hr.u.n.data; - return hr.error; -} - -u16 hpi_nv_memory_write_byte(const struct hpi_hsubsys *ph_subsys, - u32 h_nv_memory, u16 index, u16 data) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_NVMEMORY, - HPI_NVMEMORY_WRITE_BYTE); - u32TOINDEX(h_nv_memory, &hm.adapter_index); - hm.u.n.address = index; - hm.u.n.data = data; - - hpi_send_recv(&hm, &hr); - - return hr.error; -} - -u16 hpi_profile_open_all(const struct hpi_hsubsys *ph_subsys, - u16 adapter_index, u16 profile_index, u32 *ph_profile, - u16 *pw_max_profiles) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE, - HPI_PROFILE_OPEN_ALL); - hm.adapter_index = adapter_index; - hm.obj_index = profile_index; - hpi_send_recv(&hm, &hr); - - *pw_max_profiles = hr.u.p.u.o.max_profiles; - if (hr.error == 0) - *ph_profile = - hpi_indexes_to_handle(HPI_OBJ_PROFILE, adapter_index, - profile_index); - else - *ph_profile = 0; - return hr.error; -} - -u16 hpi_profile_get(const struct hpi_hsubsys *ph_subsys, u32 h_profile, - u16 bin_index, u16 *pw_seconds, u32 *pmicro_seconds, u32 *pcall_count, - u32 *pmax_micro_seconds, u32 *pmin_micro_seconds) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE, HPI_PROFILE_GET); - u32TOINDEXES(h_profile, &hm.adapter_index, &hm.obj_index); - hm.u.p.bin_index = bin_index; - hpi_send_recv(&hm, &hr); - if (pw_seconds) - *pw_seconds = hr.u.p.u.t.seconds; - if (pmicro_seconds) - *pmicro_seconds = hr.u.p.u.t.micro_seconds; - if (pcall_count) - *pcall_count = hr.u.p.u.t.call_count; - if (pmax_micro_seconds) - *pmax_micro_seconds = hr.u.p.u.t.max_micro_seconds; - if (pmin_micro_seconds) - *pmin_micro_seconds = hr.u.p.u.t.min_micro_seconds; - return hr.error; -} - -u16 hpi_profile_get_utilization(const struct hpi_hsubsys *ph_subsys, - u32 h_profile, u32 *putilization) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE, - HPI_PROFILE_GET_UTILIZATION); - u32TOINDEXES(h_profile, &hm.adapter_index, &hm.obj_index); - hpi_send_recv(&hm, &hr); - if (hr.error) { - if (putilization) - *putilization = 0; - } else { - if (putilization) - *putilization = hr.u.p.u.t.call_count; - } - return hr.error; -} - -u16 hpi_profile_get_name(const struct hpi_hsubsys *ph_subsys, u32 h_profile, - u16 bin_index, char *sz_name, u16 name_length) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE, - HPI_PROFILE_GET_NAME); - u32TOINDEXES(h_profile, &hm.adapter_index, &hm.obj_index); - hm.u.p.bin_index = bin_index; - hpi_send_recv(&hm, &hr); - if (hr.error) { - if (sz_name) - strcpy(sz_name, "??"); - } else { - if (sz_name) - memcpy(sz_name, (char *)hr.u.p.u.n.sz_name, - name_length); - } - return hr.error; -} - -u16 hpi_profile_start_all(const struct hpi_hsubsys *ph_subsys, u32 h_profile) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE, - HPI_PROFILE_START_ALL); - u32TOINDEXES(h_profile, &hm.adapter_index, &hm.obj_index); - hpi_send_recv(&hm, &hr); - - return hr.error; -} - -u16 hpi_profile_stop_all(const struct hpi_hsubsys *ph_subsys, u32 h_profile) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_PROFILE, - HPI_PROFILE_STOP_ALL); - u32TOINDEXES(h_profile, &hm.adapter_index, &hm.obj_index); - hpi_send_recv(&hm, &hr); - - return hr.error; -} - -u16 hpi_watchdog_open(const struct hpi_hsubsys *ph_subsys, u16 adapter_index, - u32 *ph_watchdog) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_WATCHDOG, - HPI_WATCHDOG_OPEN); - hm.adapter_index = adapter_index; - - hpi_send_recv(&hm, &hr); - - if (hr.error == 0) - *ph_watchdog = - hpi_indexes_to_handle(HPI_OBJ_WATCHDOG, adapter_index, - 0); - else - *ph_watchdog = 0; - return hr.error; -} - -u16 hpi_watchdog_set_time(const struct hpi_hsubsys *ph_subsys, u32 h_watchdog, - u32 time_millisec) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_WATCHDOG, - HPI_WATCHDOG_SET_TIME); - u32TOINDEX(h_watchdog, &hm.adapter_index); - hm.u.w.time_ms = time_millisec; - - hpi_send_recv(&hm, &hr); - - return hr.error; -} - -u16 hpi_watchdog_ping(const struct hpi_hsubsys *ph_subsys, u32 h_watchdog) -{ - struct hpi_message hm; - struct hpi_response hr; - hpi_init_message_response(&hm, &hr, HPI_OBJ_WATCHDOG, - HPI_WATCHDOG_PING); - u32TOINDEX(h_watchdog, &hm.adapter_index); - - hpi_send_recv(&hm, &hr); - - return hr.error; -} diff --git a/sound/pci/asihpi/hpimsginit.c b/sound/pci/asihpi/hpimsginit.c index 8e1d099ed7e4..628376ce4a49 100644 --- a/sound/pci/asihpi/hpimsginit.c +++ b/sound/pci/asihpi/hpimsginit.c @@ -32,21 +32,6 @@ static u16 res_size[HPI_OBJ_MAXINDEX + 1] = HPI_RESPONSE_SIZE_BY_OBJECT; static u16 gwSSX2_bypass; /** \internal - * Used by ASIO driver to disable SSX2 for a single process - * \param phSubSys Pointer to HPI subsystem handle. - * \param wBypass New bypass setting 0 = off, nonzero = on - * \return Previous bypass setting. - */ -u16 hpi_subsys_ssx2_bypass(const struct hpi_hsubsys *ph_subsys, u16 bypass) -{ - u16 old_value = gwSSX2_bypass; - - gwSSX2_bypass = bypass; - - return old_value; -} - -/** \internal * initialize the HPI message structure */ static void hpi_init_message(struct hpi_message *phm, u16 object, @@ -65,7 +50,8 @@ static void hpi_init_message(struct hpi_message *phm, u16 object, phm->object = object; phm->function = function; phm->version = 0; - /* Expect adapter index to be set by caller */ + phm->adapter_index = HPI_ADAPTER_INDEX_INVALID; + /* Expect actual adapter index to be set by caller */ } /** \internal diff --git a/sound/pci/asihpi/hpimsginit.h b/sound/pci/asihpi/hpimsginit.h index 864ad020c9b3..bfd330d78b58 100644 --- a/sound/pci/asihpi/hpimsginit.h +++ b/sound/pci/asihpi/hpimsginit.h @@ -21,11 +21,15 @@ (C) Copyright AudioScience Inc. 2007 *******************************************************************************/ /* Initialise response headers, or msg/response pairs. -Note that it is valid to just init a response e.g. when a lower level is preparing -a response to a message. -However, when sending a message, a matching response buffer always must be prepared +Note that it is valid to just init a response e.g. when a lower level is +preparing a response to a message. +However, when sending a message, a matching response buffer must always be +prepared. */ +#ifndef _HPIMSGINIT_H_ +#define _HPIMSGINIT_H_ + void hpi_init_response(struct hpi_response *phr, u16 object, u16 function, u16 error); @@ -38,3 +42,5 @@ void hpi_init_responseV1(struct hpi_response_header *phr, u16 size, void hpi_init_message_responseV1(struct hpi_message_header *phm, u16 msg_size, struct hpi_response_header *phr, u16 res_size, u16 object, u16 function); + +#endif /* _HPIMSGINIT_H_ */ diff --git a/sound/pci/asihpi/hpimsgx.c b/sound/pci/asihpi/hpimsgx.c index f01ab964f602..bcbdf30a6aa0 100644 --- a/sound/pci/asihpi/hpimsgx.c +++ b/sound/pci/asihpi/hpimsgx.c @@ -23,6 +23,7 @@ Extended Message Function With Response Cacheing #define SOURCEFILE_NAME "hpimsgx.c" #include "hpi_internal.h" #include "hpimsginit.h" +#include "hpicmn.h" #include "hpimsgx.h" #include "hpidebug.h" @@ -42,22 +43,24 @@ static hpi_handler_func *hpi_lookup_entry_point_function(const struct hpi_pci for (i = 0; asihpi_pci_tbl[i].vendor != 0; i++) { if (asihpi_pci_tbl[i].vendor != PCI_ANY_ID - && asihpi_pci_tbl[i].vendor != pci_info->vendor_id) + && asihpi_pci_tbl[i].vendor != + pci_info->pci_dev->vendor) continue; if (asihpi_pci_tbl[i].device != PCI_ANY_ID - && asihpi_pci_tbl[i].device != pci_info->device_id) + && asihpi_pci_tbl[i].device != + pci_info->pci_dev->device) continue; if (asihpi_pci_tbl[i].subvendor != PCI_ANY_ID && asihpi_pci_tbl[i].subvendor != - pci_info->subsys_vendor_id) + pci_info->pci_dev->subsystem_vendor) continue; if (asihpi_pci_tbl[i].subdevice != PCI_ANY_ID && asihpi_pci_tbl[i].subdevice != - pci_info->subsys_device_id) + pci_info->pci_dev->subsystem_device) continue; - HPI_DEBUG_LOG(DEBUG, " %x,%lu\n", i, - asihpi_pci_tbl[i].driver_data); + /* HPI_DEBUG_LOG(DEBUG, " %x,%lx\n", i, + asihpi_pci_tbl[i].driver_data); */ return (hpi_handler_func *) asihpi_pci_tbl[i].driver_data; } @@ -67,21 +70,12 @@ static hpi_handler_func *hpi_lookup_entry_point_function(const struct hpi_pci static inline void hw_entry_point(struct hpi_message *phm, struct hpi_response *phr) { - - hpi_handler_func *ep; - - if (phm->adapter_index < HPI_MAX_ADAPTERS) { - ep = (hpi_handler_func *) hpi_entry_points[phm-> - adapter_index]; - if (ep) { - HPI_DEBUG_MESSAGE(DEBUG, phm); - ep(phm, phr); - HPI_DEBUG_RESPONSE(phr); - return; - } - } - hpi_init_response(phr, phm->object, phm->function, - HPI_ERROR_PROCESSING_MESSAGE); + if ((phm->adapter_index < HPI_MAX_ADAPTERS) + && hpi_entry_points[phm->adapter_index]) + hpi_entry_points[phm->adapter_index] (phm, phr); + else + hpi_init_response(phr, phm->object, phm->function, + HPI_ERROR_PROCESSING_MESSAGE); } static void adapter_open(struct hpi_message *phm, struct hpi_response *phr); @@ -100,6 +94,7 @@ static void instream_close(struct hpi_message *phm, struct hpi_response *phr, void *h_owner); static void HPIMSGX__reset(u16 adapter_index); + static u16 HPIMSGX__init(struct hpi_message *phm, struct hpi_response *phr); static void HPIMSGX__cleanup(u16 adapter_index, void *h_owner); @@ -153,8 +148,6 @@ static struct hpi_stream_response static struct hpi_mixer_response rESP_HPI_MIXER_OPEN[HPI_MAX_ADAPTERS]; -static struct hpi_subsys_response gRESP_HPI_SUBSYS_FIND_ADAPTERS; - static struct adapter_info aDAPTER_INFO[HPI_MAX_ADAPTERS]; /* use these to keep track of opens from user mode apps/DLLs */ @@ -167,6 +160,11 @@ static struct asi_open_state static void subsys_message(struct hpi_message *phm, struct hpi_response *phr, void *h_owner) { + if (phm->adapter_index != HPI_ADAPTER_INDEX_INVALID) + HPI_DEBUG_LOG(WARNING, + "suspicious adapter index %d in subsys message 0x%x.\n", + phm->adapter_index, phm->function); + switch (phm->function) { case HPI_SUBSYS_GET_VERSION: hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, @@ -204,85 +202,37 @@ static void subsys_message(struct hpi_message *phm, struct hpi_response *phr, HPI_SUBSYS_DRIVER_UNLOAD, 0); return; - case HPI_SUBSYS_GET_INFO: - HPI_COMMON(phm, phr); - break; - - case HPI_SUBSYS_FIND_ADAPTERS: - memcpy(phr, &gRESP_HPI_SUBSYS_FIND_ADAPTERS, - sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS)); - break; case HPI_SUBSYS_GET_NUM_ADAPTERS: - memcpy(phr, &gRESP_HPI_SUBSYS_FIND_ADAPTERS, - sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS)); - phr->function = HPI_SUBSYS_GET_NUM_ADAPTERS; - break; case HPI_SUBSYS_GET_ADAPTER: - { - int count = phm->adapter_index; - int index = 0; - hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, - HPI_SUBSYS_GET_ADAPTER, 0); - - /* This is complicated by the fact that we want to - * "skip" 0's in the adapter list. - * First, make sure we are pointing to a - * non-zero adapter type. - */ - while (gRESP_HPI_SUBSYS_FIND_ADAPTERS. - s.aw_adapter_list[index] == 0) { - index++; - if (index >= HPI_MAX_ADAPTERS) - break; - } - while (count) { - /* move on to the next adapter */ - index++; - if (index >= HPI_MAX_ADAPTERS) - break; - while (gRESP_HPI_SUBSYS_FIND_ADAPTERS. - s.aw_adapter_list[index] == 0) { - index++; - if (index >= HPI_MAX_ADAPTERS) - break; - } - count--; - } + HPI_COMMON(phm, phr); + break; - if (index < HPI_MAX_ADAPTERS) { - phr->u.s.adapter_index = (u16)index; - phr->u.s.aw_adapter_list[0] = - gRESP_HPI_SUBSYS_FIND_ADAPTERS. - s.aw_adapter_list[index]; - } else { - phr->u.s.adapter_index = 0; - phr->u.s.aw_adapter_list[0] = 0; - phr->error = HPI_ERROR_BAD_ADAPTER_NUMBER; - } - break; - } case HPI_SUBSYS_CREATE_ADAPTER: HPIMSGX__init(phm, phr); break; + case HPI_SUBSYS_DELETE_ADAPTER: - HPIMSGX__cleanup(phm->adapter_index, h_owner); + HPIMSGX__cleanup(phm->obj_index, h_owner); { struct hpi_message hm; struct hpi_response hr; - /* call to HPI_ADAPTER_CLOSE */ hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, HPI_ADAPTER_CLOSE); - hm.adapter_index = phm->adapter_index; + hm.adapter_index = phm->obj_index; hw_entry_point(&hm, &hr); } - hw_entry_point(phm, phr); - gRESP_HPI_SUBSYS_FIND_ADAPTERS.s. - aw_adapter_list[phm->adapter_index] - = 0; - hpi_entry_points[phm->adapter_index] = NULL; + if ((phm->obj_index < HPI_MAX_ADAPTERS) + && hpi_entry_points[phm->obj_index]) { + hpi_entry_points[phm->obj_index] (phm, phr); + hpi_entry_points[phm->obj_index] = NULL; + } else + phr->error = HPI_ERROR_INVALID_OBJ_INDEX; + break; default: - hw_entry_point(phm, phr); + /* Must explicitly handle every subsys message in this switch */ + hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, phm->function, + HPI_ERROR_INVALID_FUNC); break; } } @@ -409,33 +359,7 @@ void hpi_send_recv_ex(struct hpi_message *phm, struct hpi_response *phr, break; } HPI_DEBUG_RESPONSE(phr); -#if 1 - if (phr->error >= HPI_ERROR_BACKEND_BASE) { - void *ep = NULL; - char *ep_name; - - HPI_DEBUG_MESSAGE(ERROR, phm); - - if (phm->adapter_index < HPI_MAX_ADAPTERS) - ep = hpi_entry_points[phm->adapter_index]; - - /* Don't need this? Have adapter index in debug info - Know at driver load time index->backend mapping */ - if (ep == HPI_6000) - ep_name = "HPI_6000"; - else if (ep == HPI_6205) - ep_name = "HPI_6205"; - else - ep_name = "unknown"; - - HPI_DEBUG_LOG(ERROR, "HPI %s response - error# %d\n", ep_name, - phr->error); - - if (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE) - hpi_debug_data((u16 *)phm, - sizeof(*phm) / sizeof(u16)); - } -#endif + } static void adapter_open(struct hpi_message *phm, struct hpi_response *phr) @@ -484,7 +408,7 @@ static void instream_open(struct hpi_message *phm, struct hpi_response *phr, else { instream_user_open[phm->adapter_index][phm-> obj_index].open_flag = 1; - hpios_msgxlock_un_lock(&msgx_lock); + hpios_msgxlock_unlock(&msgx_lock); /* issue a reset */ hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, @@ -509,7 +433,7 @@ static void instream_open(struct hpi_message *phm, struct hpi_response *phr, sizeof(rESP_HPI_ISTREAM_OPEN[0][0])); } } - hpios_msgxlock_un_lock(&msgx_lock); + hpios_msgxlock_unlock(&msgx_lock); } static void instream_close(struct hpi_message *phm, struct hpi_response *phr, @@ -530,7 +454,7 @@ static void instream_close(struct hpi_message *phm, struct hpi_response *phr, phm->wAdapterIndex, phm->wObjIndex, hOwner); */ instream_user_open[phm->adapter_index][phm-> obj_index].h_owner = NULL; - hpios_msgxlock_un_lock(&msgx_lock); + hpios_msgxlock_unlock(&msgx_lock); /* issue a reset */ hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_RESET); @@ -556,7 +480,7 @@ static void instream_close(struct hpi_message *phm, struct hpi_response *phr, obj_index].h_owner); phr->error = HPI_ERROR_OBJ_NOT_OPEN; } - hpios_msgxlock_un_lock(&msgx_lock); + hpios_msgxlock_unlock(&msgx_lock); } static void outstream_open(struct hpi_message *phm, struct hpi_response *phr, @@ -581,7 +505,7 @@ static void outstream_open(struct hpi_message *phm, struct hpi_response *phr, else { outstream_user_open[phm->adapter_index][phm-> obj_index].open_flag = 1; - hpios_msgxlock_un_lock(&msgx_lock); + hpios_msgxlock_unlock(&msgx_lock); /* issue a reset */ hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, @@ -606,7 +530,7 @@ static void outstream_open(struct hpi_message *phm, struct hpi_response *phr, sizeof(rESP_HPI_OSTREAM_OPEN[0][0])); } } - hpios_msgxlock_un_lock(&msgx_lock); + hpios_msgxlock_unlock(&msgx_lock); } static void outstream_close(struct hpi_message *phm, struct hpi_response *phr, @@ -628,7 +552,7 @@ static void outstream_close(struct hpi_message *phm, struct hpi_response *phr, phm->wAdapterIndex, phm->wObjIndex, hOwner); */ outstream_user_open[phm->adapter_index][phm-> obj_index].h_owner = NULL; - hpios_msgxlock_un_lock(&msgx_lock); + hpios_msgxlock_unlock(&msgx_lock); /* issue a reset */ hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_RESET); @@ -654,7 +578,7 @@ static void outstream_close(struct hpi_message *phm, struct hpi_response *phr, obj_index].h_owner); phr->error = HPI_ERROR_OBJ_NOT_OPEN; } - hpios_msgxlock_un_lock(&msgx_lock); + hpios_msgxlock_unlock(&msgx_lock); } static u16 adapter_prepare(u16 adapter) @@ -683,16 +607,9 @@ static u16 adapter_prepare(u16 adapter) if (hr.error) return hr.error; - aDAPTER_INFO[adapter].num_outstreams = hr.u.a.num_outstreams; - aDAPTER_INFO[adapter].num_instreams = hr.u.a.num_instreams; - aDAPTER_INFO[adapter].type = hr.u.a.adapter_type; - - gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.aw_adapter_list[adapter] = - hr.u.a.adapter_type; - gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters++; - if (gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters > HPI_MAX_ADAPTERS) - gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters = - HPI_MAX_ADAPTERS; + aDAPTER_INFO[adapter].num_outstreams = hr.u.ax.info.num_outstreams; + aDAPTER_INFO[adapter].num_instreams = hr.u.ax.info.num_instreams; + aDAPTER_INFO[adapter].type = hr.u.ax.info.adapter_type; /* call to HPI_OSTREAM_OPEN */ for (i = 0; i < aDAPTER_INFO[adapter].num_outstreams; i++) { @@ -727,7 +644,7 @@ static u16 adapter_prepare(u16 adapter) memcpy(&rESP_HPI_MIXER_OPEN[adapter], &hr, sizeof(rESP_HPI_MIXER_OPEN[0])); - return gRESP_HPI_SUBSYS_FIND_ADAPTERS.h.error; + return 0; } static void HPIMSGX__reset(u16 adapter_index) @@ -737,12 +654,6 @@ static void HPIMSGX__reset(u16 adapter_index) struct hpi_response hr; if (adapter_index == HPIMSGX_ALLADAPTERS) { - /* reset all responses to contain errors */ - hpi_init_response(&hr, HPI_OBJ_SUBSYSTEM, - HPI_SUBSYS_FIND_ADAPTERS, 0); - memcpy(&gRESP_HPI_SUBSYS_FIND_ADAPTERS, &hr, - sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS)); - for (adapter = 0; adapter < HPI_MAX_ADAPTERS; adapter++) { hpi_init_response(&hr, HPI_OBJ_ADAPTER, @@ -783,12 +694,6 @@ static void HPIMSGX__reset(u16 adapter_index) rESP_HPI_ISTREAM_OPEN[adapter_index][i].h.error = HPI_ERROR_INVALID_OBJ; } - if (gRESP_HPI_SUBSYS_FIND_ADAPTERS. - s.aw_adapter_list[adapter_index]) { - gRESP_HPI_SUBSYS_FIND_ADAPTERS. - s.aw_adapter_list[adapter_index] = 0; - gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters--; - } } } @@ -802,15 +707,9 @@ static u16 HPIMSGX__init(struct hpi_message *phm, hpi_handler_func *entry_point_func; struct hpi_response hr; - if (gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.num_adapters >= HPI_MAX_ADAPTERS) - return HPI_ERROR_BAD_ADAPTER_NUMBER; - /* Init response here so we can pass in previous adapter list */ hpi_init_response(&hr, phm->object, phm->function, HPI_ERROR_INVALID_OBJ); - memcpy(hr.u.s.aw_adapter_list, - gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.aw_adapter_list, - sizeof(gRESP_HPI_SUBSYS_FIND_ADAPTERS.s.aw_adapter_list)); entry_point_func = hpi_lookup_entry_point_function(phm->u.s.resource.r.pci); @@ -860,7 +759,7 @@ static void HPIMSGX__cleanup(u16 adapter_index, void *h_owner) struct hpi_response hr; HPI_DEBUG_LOG(DEBUG, - "close adapter %d ostream %d\n", + "Close adapter %d ostream %d\n", adapter, i); hpi_init_message_response(&hm, &hr, @@ -884,7 +783,7 @@ static void HPIMSGX__cleanup(u16 adapter_index, void *h_owner) struct hpi_response hr; HPI_DEBUG_LOG(DEBUG, - "close adapter %d istream %d\n", + "Close adapter %d istream %d\n", adapter, i); hpi_init_message_response(&hm, &hr, diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c index 22dbd91811a4..cd624f13ff8e 100644 --- a/sound/pci/asihpi/hpioctl.c +++ b/sound/pci/asihpi/hpioctl.c @@ -30,6 +30,7 @@ Common Linux HPI ioctl and module probe/remove functions #include <linux/slab.h> #include <linux/moduleparam.h> #include <asm/uaccess.h> +#include <linux/pci.h> #include <linux/stringify.h> #ifdef MODULE_FIRMWARE @@ -45,7 +46,7 @@ MODULE_FIRMWARE("asihpi/dsp8900.bin"); static int prealloc_stream_buf; module_param(prealloc_stream_buf, int, S_IRUGO); MODULE_PARM_DESC(prealloc_stream_buf, - "preallocate size for per-adapter stream buffer"); + "Preallocate size for per-adapter stream buffer"); /* Allow the debug level to be changed after module load. E.g. echo 2 > /sys/module/asihpi/parameters/hpiDebugLevel @@ -121,8 +122,8 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) phpi_ioctl_data = (struct hpi_ioctl_linux __user *)arg; /* Read the message and response pointers from user space. */ - if (get_user(puhm, &phpi_ioctl_data->phm) || - get_user(puhr, &phpi_ioctl_data->phr)) { + if (get_user(puhm, &phpi_ioctl_data->phm) + || get_user(puhr, &phpi_ioctl_data->phr)) { err = -EFAULT; goto out; } @@ -135,7 +136,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (hm->h.size > sizeof(*hm)) hm->h.size = sizeof(*hm); - /*printk(KERN_INFO "message size %d\n", hm->h.wSize); */ + /* printk(KERN_INFO "message size %d\n", hm->h.wSize); */ uncopied_bytes = copy_from_user(hm, puhm, hm->h.size); if (uncopied_bytes) { @@ -155,8 +156,13 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) goto out; } + if (hm->h.adapter_index >= HPI_MAX_ADAPTERS) { + err = -EINVAL; + goto out; + } + pa = &adapters[hm->h.adapter_index]; - hr->h.size = 0; + hr->h.size = res_max_size; if (hm->h.object == HPI_OBJ_SUBSYSTEM) { switch (hm->h.function) { case HPI_SUBSYS_CREATE_ADAPTER: @@ -216,7 +222,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) */ if (pa->buffer_size < size) { HPI_DEBUG_LOG(DEBUG, - "realloc adapter %d stream " + "Realloc adapter %d stream " "buffer from %zd to %d\n", hm->h.adapter_index, pa->buffer_size, size); @@ -259,7 +265,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) copy_from_user(pa->p_buffer, ptr, size); if (uncopied_bytes) HPI_DEBUG_LOG(WARNING, - "missed %d of %d " + "Missed %d of %d " "bytes from user\n", uncopied_bytes, size); } @@ -271,7 +277,7 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) copy_to_user(ptr, pa->p_buffer, size); if (uncopied_bytes) HPI_DEBUG_LOG(WARNING, - "missed %d of %d " "bytes to user\n", + "Missed %d of %d " "bytes to user\n", uncopied_bytes, size); } @@ -290,9 +296,9 @@ long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (hr->h.size > res_max_size) { HPI_DEBUG_LOG(ERROR, "response too big %d %d\n", hr->h.size, res_max_size); - /*HPI_DEBUG_MESSAGE(ERROR, hm); */ - err = -EFAULT; - goto out; + hr->h.error = HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL; + hr->h.specific_error = hr->h.size; + hr->h.size = sizeof(hr->h); } uncopied_bytes = copy_to_user(puhr, hr, hr->h.size); @@ -320,18 +326,26 @@ int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev, memset(&adapter, 0, sizeof(adapter)); - printk(KERN_DEBUG "probe PCI device (%04x:%04x,%04x:%04x,%04x)\n", - pci_dev->vendor, pci_dev->device, pci_dev->subsystem_vendor, + dev_printk(KERN_DEBUG, &pci_dev->dev, + "probe %04x:%04x,%04x:%04x,%04x\n", pci_dev->vendor, + pci_dev->device, pci_dev->subsystem_vendor, pci_dev->subsystem_device, pci_dev->devfn); + if (pci_enable_device(pci_dev) < 0) { + dev_printk(KERN_ERR, &pci_dev->dev, + "pci_enable_device failed, disabling device\n"); + return -EIO; + } + + pci_set_master(pci_dev); /* also sets latency timer if < 16 */ + hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_CREATE_ADAPTER); hpi_init_response(&hr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_CREATE_ADAPTER, HPI_ERROR_PROCESSING_MESSAGE); - hm.adapter_index = -1; /* an invalid index */ + hm.adapter_index = HPI_ADAPTER_INDEX_INVALID; - /* fill in HPI_PCI information from kernel provided information */ adapter.pci = pci_dev; nm = HPI_MAX_ADAPTER_MEM_SPACES; @@ -359,19 +373,7 @@ int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev, pci.ap_mem_base[idx] = adapter.ap_remapped_mem_base[idx]; } - /* could replace Pci with direct pointer to pci_dev for linux - Instead wrap accessor functions for IDs etc. - Would it work for windows? - */ - pci.bus_number = pci_dev->bus->number; - pci.vendor_id = (u16)pci_dev->vendor; - pci.device_id = (u16)pci_dev->device; - pci.subsys_vendor_id = (u16)(pci_dev->subsystem_vendor & 0xffff); - pci.subsys_device_id = (u16)(pci_dev->subsystem_device & 0xffff); - pci.device_number = pci_dev->devfn; - pci.interrupt = pci_dev->irq; - pci.p_os_data = pci_dev; - + pci.pci_dev = pci_dev; hm.u.s.resource.bus_type = HPI_BUS_PCI; hm.u.s.resource.r.pci = &pci; @@ -392,10 +394,10 @@ int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev, } adapter.index = hr.u.s.adapter_index; - adapter.type = hr.u.s.aw_adapter_list[adapter.index]; + adapter.type = hr.u.s.adapter_type; hm.adapter_index = adapter.index; - err = hpi_adapter_open(NULL, adapter.index); + err = hpi_adapter_open(adapter.index); if (err) goto err; @@ -407,8 +409,9 @@ int __devinit asihpi_adapter_probe(struct pci_dev *pci_dev, mutex_init(&adapters[adapter.index].mutex); pci_set_drvdata(pci_dev, &adapters[adapter.index]); - printk(KERN_INFO "probe found adapter ASI%04X HPI index #%d.\n", - adapter.type, adapter.index); + dev_printk(KERN_INFO, &pci_dev->dev, + "probe succeeded for ASI%04X HPI index %d\n", adapter.type, + adapter.index); return 0; @@ -439,7 +442,8 @@ void __devexit asihpi_adapter_remove(struct pci_dev *pci_dev) hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_DELETE_ADAPTER); - hm.adapter_index = pa->index; + hm.obj_index = pa->index; + hm.adapter_index = HPI_ADAPTER_INDEX_INVALID; hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); /* unmap PCI memory space, mapped during device init. */ @@ -450,20 +454,18 @@ void __devexit asihpi_adapter_remove(struct pci_dev *pci_dev) } } - if (pa->p_buffer) { - pa->buffer_size = 0; + if (pa->p_buffer) vfree(pa->p_buffer); - } pci_set_drvdata(pci_dev, NULL); - /* - printk(KERN_INFO "PCI device (%04x:%04x,%04x:%04x,%04x)," - " HPI index # %d, removed.\n", - pci_dev->vendor, pci_dev->device, - pci_dev->subsystem_vendor, - pci_dev->subsystem_device, pci_dev->devfn, - pa->index); - */ + if (1) + dev_printk(KERN_INFO, &pci_dev->dev, + "remove %04x:%04x,%04x:%04x,%04x," " HPI index %d.\n", + pci_dev->vendor, pci_dev->device, + pci_dev->subsystem_vendor, pci_dev->subsystem_device, + pci_dev->devfn, pa->index); + + memset(pa, 0, sizeof(*pa)); } void __init asihpi_init(void) diff --git a/sound/pci/asihpi/hpios.h b/sound/pci/asihpi/hpios.h index 370f39b43f85..03273e729f99 100644 --- a/sound/pci/asihpi/hpios.h +++ b/sound/pci/asihpi/hpios.h @@ -27,9 +27,7 @@ HPI Operating System Specific macros for Linux Kernel driver #define HPI_OS_LINUX_KERNEL #define HPI_OS_DEFINED -#define HPI_KERNEL_MODE - -#define HPI_REASSIGN_DUPLICATE_ADAPTER_IDX +#define HPI_BUILD_KERNEL_MODE #include <linux/io.h> #include <asm/system.h> @@ -135,20 +133,20 @@ static inline void cond_unlock(struct hpios_spinlock *l) #define hpios_msgxlock_init(obj) spin_lock_init(&(obj)->lock) #define hpios_msgxlock_lock(obj) cond_lock(obj) -#define hpios_msgxlock_un_lock(obj) cond_unlock(obj) +#define hpios_msgxlock_unlock(obj) cond_unlock(obj) #define hpios_dsplock_init(obj) spin_lock_init(&(obj)->dsp_lock.lock) #define hpios_dsplock_lock(obj) cond_lock(&(obj)->dsp_lock) #define hpios_dsplock_unlock(obj) cond_unlock(&(obj)->dsp_lock) #ifdef CONFIG_SND_DEBUG -#define HPI_DEBUG +#define HPI_BUILD_DEBUG #endif #define HPI_ALIST_LOCKING #define hpios_alistlock_init(obj) spin_lock_init(&((obj)->list_lock.lock)) #define hpios_alistlock_lock(obj) spin_lock(&((obj)->list_lock.lock)) -#define hpios_alistlock_un_lock(obj) spin_unlock(&((obj)->list_lock.lock)) +#define hpios_alistlock_unlock(obj) spin_unlock(&((obj)->list_lock.lock)) struct hpi_adapter { /* mutex prevents contention for one card diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 49d572a7b235..3119cd97a217 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -522,7 +522,7 @@ static int snd_atiixp_aclink_reset(struct atiixp *chip) atiixp_read(chip, CMD); mdelay(1); atiixp_update(chip, CMD, ATI_REG_CMD_AC_RESET, ATI_REG_CMD_AC_RESET); - if (--timeout) { + if (!--timeout) { snd_printk(KERN_ERR "atiixp: codec reset timeout\n"); break; } diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index 91d7036b6411..2f74c2fdf1ea 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -498,7 +498,7 @@ static int snd_atiixp_aclink_reset(struct atiixp_modem *chip) atiixp_read(chip, CMD); msleep(1); atiixp_update(chip, CMD, ATI_REG_CMD_AC_RESET, ATI_REG_CMD_AC_RESET); - if (--timeout) { + if (!--timeout) { snd_printk(KERN_ERR "atiixp-modem: codec reset timeout\n"); break; } diff --git a/sound/pci/au88x0/au88x0_eq.c b/sound/pci/au88x0/au88x0_eq.c index 38602b85874d..278ed8189fca 100644 --- a/sound/pci/au88x0/au88x0_eq.c +++ b/sound/pci/au88x0/au88x0_eq.c @@ -896,7 +896,8 @@ static int __devinit vortex_eq_init(vortex_t * vortex) if ((kcontrol = snd_ctl_new1(&vortex_eq_kcontrol, vortex)) == NULL) return -ENOMEM; - strcpy(kcontrol->id.name, EqBandLabels[i]); + snprintf(kcontrol->id.name, sizeof(kcontrol->id.name), + "%s Playback Volume", EqBandLabels[i]); kcontrol->private_value = i; if ((err = snd_ctl_add(vortex->card, kcontrol)) < 0) return err; diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 573594bf3225..5715c4d05573 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -1,6 +1,5 @@ -/* - * azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168). - * Copyright (C) 2002, 2005 - 2010 by Andreas Mohr <andi AT lisas.de> +/* azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168). + * Copyright (C) 2002, 2005 - 2011 by Andreas Mohr <andi AT lisas.de> * * Framework borrowed from Bart Hartgers's als4000.c. * Driver developed on PCI168 AP(W) version (PCI rev. 10, subsystem ID 1801), @@ -66,6 +65,13 @@ * addresses illegally. So far unfortunately it looks like the very flexible * ALSA AC97 support is still not enough to easily compensate for such a * grave layout violation despite all tweaks and quirks mechanisms it offers. + * Well, not quite: now ac97 layer is much improved (bus-specific ops!), + * thus I was able to implement support - it's actually working quite well. + * An interesting item might be Aztech AMR 2800-W, since it's an AC97 + * modem card which might reveal the Aztech-specific codec ID which + * we might want to pretend, too. Dito PCI168's brother, PCI368, + * where the advertising datasheet says it's AC97-based and has a + * Digital Enhanced Game Port. * - builtin genuine OPL3 - verified to work fine, 20080506 * - full duplex 16bit playback/record at independent sampling rate * - MPU401 (+ legacy address support, claimed by one official spec sheet) @@ -189,6 +195,16 @@ #include <sound/mpu401.h> #include <sound/opl3.h> #include <sound/initval.h> +/* + * Config switch, to use ALSA's AC97 layer instead of old custom mixer crap. + * If the AC97 compatibility parts we needed to implement locally turn out + * to work nicely, then remove the old implementation eventually. + */ +#define AZF_USE_AC97_LAYER 1 + +#ifdef AZF_USE_AC97_LAYER +#include <sound/ac97_codec.h> +#endif #include "azt3328.h" MODULE_AUTHOR("Andreas Mohr <andi AT lisas.de>"); @@ -328,6 +344,10 @@ struct snd_azf3328 { /* playback, recording and I2S out codecs */ struct snd_azf3328_codec_data codecs[3]; +#ifdef AZF_USE_AC97_LAYER + struct snd_ac97 *ac97; +#endif + struct snd_card *card; struct snd_rawmidi *rmidi; @@ -506,7 +526,7 @@ snd_azf3328_mixer_inw(const struct snd_azf3328 *chip, unsigned reg) #define AZF_MUTE_BIT 0x80 static bool -snd_azf3328_mixer_set_mute(const struct snd_azf3328 *chip, +snd_azf3328_mixer_mute_control(const struct snd_azf3328 *chip, unsigned reg, bool do_mute ) { @@ -521,6 +541,323 @@ snd_azf3328_mixer_set_mute(const struct snd_azf3328 *chip, return (do_mute) ? !updated : updated; } +static inline bool +snd_azf3328_mixer_mute_control_master(const struct snd_azf3328 *chip, + bool do_mute +) +{ + return snd_azf3328_mixer_mute_control( + chip, + IDX_MIXER_PLAY_MASTER, + do_mute + ); +} + +static inline bool +snd_azf3328_mixer_mute_control_pcm(const struct snd_azf3328 *chip, + bool do_mute +) +{ + return snd_azf3328_mixer_mute_control( + chip, + IDX_MIXER_WAVEOUT, + do_mute + ); +} + +static inline void +snd_azf3328_mixer_reset(const struct snd_azf3328 *chip) +{ + /* reset (close) mixer: + * first mute master volume, then reset + */ + snd_azf3328_mixer_mute_control_master(chip, 1); + snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000); +} + +#ifdef AZF_USE_AC97_LAYER + +static inline void +snd_azf3328_mixer_ac97_map_unsupported(unsigned short reg, const char *mode) +{ + /* need to add some more or less clever emulation? */ + printk(KERN_WARNING + "azt3328: missing %s emulation for AC97 register 0x%02x!\n", + mode, reg); +} + +/* + * Need to have _special_ AC97 mixer hardware register index mapper, + * to compensate for the issue of a rather AC97-incompatible hardware layout. + */ +#define AZF_REG_MASK 0x3f +#define AZF_AC97_REG_UNSUPPORTED 0x8000 +#define AZF_AC97_REG_REAL_IO_READ 0x4000 +#define AZF_AC97_REG_REAL_IO_WRITE 0x2000 +#define AZF_AC97_REG_REAL_IO_RW \ + (AZF_AC97_REG_REAL_IO_READ | AZF_AC97_REG_REAL_IO_WRITE) +#define AZF_AC97_REG_EMU_IO_READ 0x0400 +#define AZF_AC97_REG_EMU_IO_WRITE 0x0200 +#define AZF_AC97_REG_EMU_IO_RW \ + (AZF_AC97_REG_EMU_IO_READ | AZF_AC97_REG_EMU_IO_WRITE) +static unsigned short +snd_azf3328_mixer_ac97_map_reg_idx(unsigned short reg) +{ + static const struct { + unsigned short azf_reg; + } azf_reg_mapper[] = { + /* Especially when taking into consideration + * mono/stereo-based sequence of azf vs. AC97 control series, + * it's quite obvious that azf simply got rid + * of the AC97_HEADPHONE control at its intended offset, + * thus shifted _all_ controls by one, + * and _then_ simply added it as an FMSYNTH control at the end, + * to make up for the offset. + * This means we'll have to translate indices here as + * needed and then do some tiny AC97 patch action + * (snd_ac97_rename_vol_ctl() etc.) - that's it. + */ + { /* AC97_RESET */ IDX_MIXER_RESET + | AZF_AC97_REG_REAL_IO_WRITE + | AZF_AC97_REG_EMU_IO_READ }, + { /* AC97_MASTER */ IDX_MIXER_PLAY_MASTER }, + /* note large shift: AC97_HEADPHONE to IDX_MIXER_FMSYNTH! */ + { /* AC97_HEADPHONE */ IDX_MIXER_FMSYNTH }, + { /* AC97_MASTER_MONO */ IDX_MIXER_MODEMOUT }, + { /* AC97_MASTER_TONE */ IDX_MIXER_BASSTREBLE }, + { /* AC97_PC_BEEP */ IDX_MIXER_PCBEEP }, + { /* AC97_PHONE */ IDX_MIXER_MODEMIN }, + { /* AC97_MIC */ IDX_MIXER_MIC }, + { /* AC97_LINE */ IDX_MIXER_LINEIN }, + { /* AC97_CD */ IDX_MIXER_CDAUDIO }, + { /* AC97_VIDEO */ IDX_MIXER_VIDEO }, + { /* AC97_AUX */ IDX_MIXER_AUX }, + { /* AC97_PCM */ IDX_MIXER_WAVEOUT }, + { /* AC97_REC_SEL */ IDX_MIXER_REC_SELECT }, + { /* AC97_REC_GAIN */ IDX_MIXER_REC_VOLUME }, + { /* AC97_REC_GAIN_MIC */ AZF_AC97_REG_EMU_IO_RW }, + { /* AC97_GENERAL_PURPOSE */ IDX_MIXER_ADVCTL2 }, + { /* AC97_3D_CONTROL */ IDX_MIXER_ADVCTL1 }, + }; + + unsigned short reg_azf = AZF_AC97_REG_UNSUPPORTED; + + /* azf3328 supports the low-numbered and low-spec:ed range + of AC97 regs only */ + if (reg <= AC97_3D_CONTROL) { + unsigned short reg_idx = reg / 2; + reg_azf = azf_reg_mapper[reg_idx].azf_reg; + /* a translation-only entry means it's real read/write: */ + if (!(reg_azf & ~AZF_REG_MASK)) + reg_azf |= AZF_AC97_REG_REAL_IO_RW; + } else { + switch (reg) { + case AC97_POWERDOWN: + reg_azf = AZF_AC97_REG_EMU_IO_RW; + break; + case AC97_EXTENDED_ID: + reg_azf = AZF_AC97_REG_EMU_IO_READ; + break; + case AC97_EXTENDED_STATUS: + /* I don't know what the h*ll AC97 layer + * would consult this _extended_ register for + * given a base-AC97-advertised card, + * but let's just emulate it anyway :-P + */ + reg_azf = AZF_AC97_REG_EMU_IO_RW; + break; + case AC97_VENDOR_ID1: + case AC97_VENDOR_ID2: + reg_azf = AZF_AC97_REG_EMU_IO_READ; + break; + } + } + return reg_azf; +} + +static const unsigned short +azf_emulated_ac97_caps = + AC97_BC_DEDICATED_MIC | + AC97_BC_BASS_TREBLE | + /* Headphone is an FM Synth control here */ + AC97_BC_HEADPHONE | + /* no AC97_BC_LOUDNESS! */ + /* mask 0x7c00 is + vendor-specific 3D enhancement + vendor indicator. + Since there actually _is_ an + entry for Aztech Labs + (13), make damn sure + to indicate it. */ + (13 << 10); + +static const unsigned short +azf_emulated_ac97_powerdown = + /* pretend everything to be active */ + AC97_PD_ADC_STATUS | + AC97_PD_DAC_STATUS | + AC97_PD_MIXER_STATUS | + AC97_PD_VREF_STATUS; + +/* + * Emulated, _inofficial_ vendor ID + * (there might be some devices such as the MR 2800-W + * which could reveal the real Aztech AC97 ID). + * We choose to use "AZT" prefix, and then use 1 to indicate PCI168 + * (better don't use 0x68 since there's a PCI368 as well). + */ +static const unsigned int +azf_emulated_ac97_vendor_id = 0x415a5401; + +static unsigned short +snd_azf3328_mixer_ac97_read(struct snd_ac97 *ac97, unsigned short reg_ac97) +{ + const struct snd_azf3328 *chip = ac97->private_data; + unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97); + unsigned short reg_val = 0; + bool unsupported = 0; + + snd_azf3328_dbgmixer( + "snd_azf3328_mixer_ac97_read reg_ac97 %u\n", + reg_ac97 + ); + if (reg_azf & AZF_AC97_REG_UNSUPPORTED) + unsupported = 1; + else { + if (reg_azf & AZF_AC97_REG_REAL_IO_READ) + reg_val = snd_azf3328_mixer_inw(chip, + reg_azf & AZF_REG_MASK); + else { + /* + * Proceed with dummy I/O read, + * to ensure compatible timing where this may matter. + * (ALSA AC97 layer usually doesn't call I/O functions + * due to intelligent I/O caching anyway) + * Choose a mixer register that's thoroughly unrelated + * to common audio (try to minimize distortion). + */ + snd_azf3328_mixer_inw(chip, IDX_MIXER_SOMETHING30H); + } + + if (reg_azf & AZF_AC97_REG_EMU_IO_READ) { + switch (reg_ac97) { + case AC97_RESET: + reg_val |= azf_emulated_ac97_caps; + break; + case AC97_POWERDOWN: + reg_val |= azf_emulated_ac97_powerdown; + break; + case AC97_EXTENDED_ID: + case AC97_EXTENDED_STATUS: + /* AFAICS we simply can't support anything: */ + reg_val |= 0; + break; + case AC97_VENDOR_ID1: + reg_val = azf_emulated_ac97_vendor_id >> 16; + break; + case AC97_VENDOR_ID2: + reg_val = azf_emulated_ac97_vendor_id & 0xffff; + break; + default: + unsupported = 1; + break; + } + } + } + if (unsupported) + snd_azf3328_mixer_ac97_map_unsupported(reg_ac97, "read"); + + return reg_val; +} + +static void +snd_azf3328_mixer_ac97_write(struct snd_ac97 *ac97, + unsigned short reg_ac97, unsigned short val) +{ + const struct snd_azf3328 *chip = ac97->private_data; + unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97); + bool unsupported = 0; + + snd_azf3328_dbgmixer( + "snd_azf3328_mixer_ac97_write reg_ac97 %u val %u\n", + reg_ac97, val + ); + if (reg_azf & AZF_AC97_REG_UNSUPPORTED) + unsupported = 1; + else { + if (reg_azf & AZF_AC97_REG_REAL_IO_WRITE) + snd_azf3328_mixer_outw( + chip, + reg_azf & AZF_REG_MASK, + val + ); + else + if (reg_azf & AZF_AC97_REG_EMU_IO_WRITE) { + switch (reg_ac97) { + case AC97_REC_GAIN_MIC: + case AC97_POWERDOWN: + case AC97_EXTENDED_STATUS: + /* + * Silently swallow these writes. + * Since for most registers our card doesn't + * actually support a comparable feature, + * this is exactly what we should do here. + * The AC97 layer's I/O caching probably + * automatically takes care of all the rest... + * (remembers written values etc.) + */ + break; + default: + unsupported = 1; + break; + } + } + } + if (unsupported) + snd_azf3328_mixer_ac97_map_unsupported(reg_ac97, "write"); +} + +static int __devinit +snd_azf3328_mixer_new(struct snd_azf3328 *chip) +{ + struct snd_ac97_bus *bus; + struct snd_ac97_template ac97; + static struct snd_ac97_bus_ops ops = { + .write = snd_azf3328_mixer_ac97_write, + .read = snd_azf3328_mixer_ac97_read, + }; + int rc; + + memset(&ac97, 0, sizeof(ac97)); + ac97.scaps = AC97_SCAP_SKIP_MODEM + | AC97_SCAP_AUDIO /* we support audio! */ + | AC97_SCAP_NO_SPDIF; + ac97.private_data = chip; + ac97.pci = chip->pci; + + /* + * ALSA's AC97 layer has terrible init crackling issues, + * unfortunately, and since it makes use of AC97_RESET, + * there's no use trying to mute Master Playback proactively. + */ + + rc = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus); + if (!rc) + rc = snd_ac97_mixer(bus, &ac97, &chip->ac97); + /* + * Make sure to complain loudly in case of AC97 init failure, + * since failure may happen quite often, + * due to this card being a very quirky AC97 "lookalike". + */ + if (rc) + printk(KERN_ERR "azt3328: AC97 init failed, err %d!\n", rc); + + /* 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; +} +#else /* AZF_USE_AC97_LAYER */ static void snd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip, unsigned reg, @@ -945,6 +1282,7 @@ snd_azf3328_mixer_new(struct snd_azf3328 *chip) snd_azf3328_dbgcallleave(); return 0; } +#endif /* AZF_USE_AC97_LAYER */ static int snd_azf3328_hw_params(struct snd_pcm_substream *substream, @@ -1233,8 +1571,8 @@ snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd) if (is_main_mixer_playback_codec) { /* mute WaveOut (avoid clicking during setup) */ previously_muted = - snd_azf3328_mixer_set_mute( - chip, IDX_MIXER_WAVEOUT, 1 + snd_azf3328_mixer_mute_control_pcm( + chip, 1 ); } @@ -1290,8 +1628,8 @@ snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd) if (is_main_mixer_playback_codec) { /* now unmute WaveOut */ if (!previously_muted) - snd_azf3328_mixer_set_mute( - chip, IDX_MIXER_WAVEOUT, 0 + snd_azf3328_mixer_mute_control_pcm( + chip, 0 ); } @@ -1315,8 +1653,8 @@ snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd) if (is_main_mixer_playback_codec) { /* mute WaveOut (avoid clicking during setup) */ previously_muted = - snd_azf3328_mixer_set_mute( - chip, IDX_MIXER_WAVEOUT, 1 + snd_azf3328_mixer_mute_control_pcm( + chip, 1 ); } @@ -1341,8 +1679,8 @@ snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd) if (is_main_mixer_playback_codec) { /* now unmute WaveOut */ if (!previously_muted) - snd_azf3328_mixer_set_mute( - chip, IDX_MIXER_WAVEOUT, 0 + snd_azf3328_mixer_mute_control_pcm( + chip, 0 ); } @@ -2050,11 +2388,7 @@ snd_azf3328_free(struct snd_azf3328 *chip) if (chip->irq < 0) goto __end_hw; - /* reset (close) mixer: - * first mute master volume, then reset - */ - snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1); - snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000); + snd_azf3328_mixer_reset(chip); snd_azf3328_timer_stop(chip->timer); snd_azf3328_gameport_free(chip); @@ -2407,6 +2741,55 @@ snd_azf3328_suspend_regs(unsigned long io_addr, unsigned count, u32 *saved_regs) } } +static inline void +snd_azf3328_resume_regs(const u32 *saved_regs, + unsigned long io_addr, + unsigned count +) +{ + unsigned reg; + + for (reg = 0; reg < count; ++reg) { + outl(*saved_regs, io_addr); + snd_azf3328_dbgpm("resume: io 0x%04lx: 0x%08x --> 0x%08x\n", + io_addr, *saved_regs, inl(io_addr)); + ++saved_regs; + io_addr += sizeof(*saved_regs); + } +} + +static inline void +snd_azf3328_suspend_ac97(struct snd_azf3328 *chip) +{ +#ifdef AZF_USE_AC97_LAYER + snd_ac97_suspend(chip->ac97); +#else + snd_azf3328_suspend_regs(chip->mixer_io, + ARRAY_SIZE(chip->saved_regs_mixer), chip->saved_regs_mixer); + + /* make sure to disable master volume etc. to prevent looping sound */ + snd_azf3328_mixer_mute_control_master(chip, 1); + snd_azf3328_mixer_mute_control_pcm(chip, 1); +#endif /* AZF_USE_AC97_LAYER */ +} + +static inline void +snd_azf3328_resume_ac97(const struct snd_azf3328 *chip) +{ +#ifdef AZF_USE_AC97_LAYER + snd_ac97_resume(chip->ac97); +#else + snd_azf3328_resume_regs(chip->saved_regs_mixer, chip->mixer_io, + ARRAY_SIZE(chip->saved_regs_mixer)); + + /* unfortunately with 32bit transfers, IDX_MIXER_PLAY_MASTER (0x02) + and IDX_MIXER_RESET (offset 0x00) get touched at the same time, + resulting in a mixer reset condition persisting until _after_ + master vol was restored. Thus master vol needs an extra restore. */ + outw(((u16 *)chip->saved_regs_mixer)[1], chip->mixer_io + 2); +#endif /* AZF_USE_AC97_LAYER */ +} + static int snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state) { @@ -2420,12 +2803,7 @@ snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state) snd_pcm_suspend_all(chip->pcm[AZF_CODEC_PLAYBACK]); snd_pcm_suspend_all(chip->pcm[AZF_CODEC_I2S_OUT]); - snd_azf3328_suspend_regs(chip->mixer_io, - ARRAY_SIZE(chip->saved_regs_mixer), chip->saved_regs_mixer); - - /* make sure to disable master volume etc. to prevent looping sound */ - snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1); - snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1); + snd_azf3328_suspend_ac97(chip); snd_azf3328_suspend_regs(chip->ctrl_io, ARRAY_SIZE(chip->saved_regs_ctrl), chip->saved_regs_ctrl); @@ -2447,23 +2825,6 @@ snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state) return 0; } -static inline void -snd_azf3328_resume_regs(const u32 *saved_regs, - unsigned long io_addr, - unsigned count -) -{ - unsigned reg; - - for (reg = 0; reg < count; ++reg) { - outl(*saved_regs, io_addr); - snd_azf3328_dbgpm("resume: io 0x%04lx: 0x%08x --> 0x%08x\n", - io_addr, *saved_regs, inl(io_addr)); - ++saved_regs; - io_addr += sizeof(*saved_regs); - } -} - static int snd_azf3328_resume(struct pci_dev *pci) { @@ -2487,14 +2848,7 @@ snd_azf3328_resume(struct pci_dev *pci) snd_azf3328_resume_regs(chip->saved_regs_opl3, chip->opl3_io, ARRAY_SIZE(chip->saved_regs_opl3)); - snd_azf3328_resume_regs(chip->saved_regs_mixer, chip->mixer_io, - ARRAY_SIZE(chip->saved_regs_mixer)); - - /* unfortunately with 32bit transfers, IDX_MIXER_PLAY_MASTER (0x02) - and IDX_MIXER_RESET (offset 0x00) get touched at the same time, - resulting in a mixer reset condition persisting until _after_ - master vol was restored. Thus master vol needs an extra restore. */ - outw(((u16 *)chip->saved_regs_mixer)[1], chip->mixer_io + 2); + snd_azf3328_resume_ac97(chip); snd_azf3328_resume_regs(chip->saved_regs_ctrl, chip->ctrl_io, ARRAY_SIZE(chip->saved_regs_ctrl)); diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 1bff80cde0a2..b9321544c31c 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -869,7 +869,7 @@ spdif_passthru_playback_setup(struct ct_atc *atc, struct ct_atc_pcm *apcm) mutex_lock(&atc->atc_mutex); dao->ops->get_spos(dao, &status); if (((status >> 24) & IEC958_AES3_CON_FS) != iec958_con_fs) { - status &= ((~IEC958_AES3_CON_FS) << 24); + status &= ~(IEC958_AES3_CON_FS << 24); status |= (iec958_con_fs << 24); dao->ops->set_spos(dao, status); dao->ops->commit_write(dao); diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c index af56eb949bde..47d9ea97de02 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -176,6 +176,7 @@ static int dao_set_left_input(struct dao *dao, struct rsc *input) if (!entry) return -ENOMEM; + dao->ops->clear_left_input(dao); /* Program master and conjugate resources */ input->ops->master(input); daio->rscl.ops->master(&daio->rscl); @@ -204,6 +205,7 @@ static int dao_set_right_input(struct dao *dao, struct rsc *input) if (!entry) return -ENOMEM; + dao->ops->clear_right_input(dao); /* Program master and conjugate resources */ input->ops->master(input); daio->rscr.ops->master(&daio->rscr); diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c index b6b11bfe7574..5364164674e4 100644 --- a/sound/pci/ctxfi/cthw20k2.c +++ b/sound/pci/ctxfi/cthw20k2.c @@ -1307,10 +1307,10 @@ static int hw_pll_init(struct hw *hw, unsigned int rsr) set_field(&pllctl, PLLCTL_B, 0); if (48000 == rsr) { set_field(&pllctl, PLLCTL_FD, 16 - 2); - set_field(&pllctl, PLLCTL_RD, 1 - 1); + set_field(&pllctl, PLLCTL_RD, 1 - 1); /* 3000*16/1 = 48000 */ } else { /* 44100 */ set_field(&pllctl, PLLCTL_FD, 147 - 2); - set_field(&pllctl, PLLCTL_RD, 10 - 1); + set_field(&pllctl, PLLCTL_RD, 10 - 1); /* 3000*147/10 = 44100 */ } hw_write_20kx(hw, PLL_CTL, pllctl); mdelay(40); @@ -1740,6 +1740,10 @@ static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type) return data; } +#define MIC_BOOST_0DB 0xCF +#define MIC_BOOST_STEPS_PER_DB 2 +#define MIC_BOOST_20DB (MIC_BOOST_0DB + 20 * MIC_BOOST_STEPS_PER_DB) + static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) { u32 data; @@ -1751,10 +1755,12 @@ static int hw_adc_input_select(struct hw *hw, enum ADCSRC type) hw_write_20kx(hw, GPIO_DATA, data); hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101), MAKE_WM8775_DATA(0x101)); /* Mic-in */ - hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7), - MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ - hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7), - MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ + hw20k2_i2c_write(hw, + MAKE_WM8775_ADDR(WM8775_AADCL, MIC_BOOST_20DB), + MAKE_WM8775_DATA(MIC_BOOST_20DB)); /* +20dB */ + hw20k2_i2c_write(hw, + MAKE_WM8775_ADDR(WM8775_AADCR, MIC_BOOST_20DB), + MAKE_WM8775_DATA(MIC_BOOST_20DB)); /* +20dB */ break; case ADC_LINEIN: data &= ~(0x1 << 14); @@ -1827,10 +1833,12 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info) hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101), MAKE_WM8775_DATA(0x101)); /* Mic-in */ - hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7), - MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ - hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7), - MAKE_WM8775_DATA(0xE7)); /* +12dB boost */ + hw20k2_i2c_write(hw, + MAKE_WM8775_ADDR(WM8775_AADCL, MIC_BOOST_20DB), + MAKE_WM8775_DATA(MIC_BOOST_20DB)); /* +20dB */ + hw20k2_i2c_write(hw, + MAKE_WM8775_ADDR(WM8775_AADCR, MIC_BOOST_20DB), + MAKE_WM8775_DATA(MIC_BOOST_20DB)); /* +20dB */ } else if (mux == 2) { /* Configures GPIO data to select Line-in */ data &= ~(0x1 << 14); diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c index 15c1e7271ea8..c3519ff42fbb 100644 --- a/sound/pci/ctxfi/ctmixer.c +++ b/sound/pci/ctxfi/ctmixer.c @@ -566,19 +566,6 @@ static int ct_spdif_get_mask(struct snd_kcontrol *kcontrol, return 0; } -static int ct_spdif_default_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - unsigned int status = SNDRV_PCM_DEFAULT_CON_SPDIF; - - ucontrol->value.iec958.status[0] = (status >> 0) & 0xff; - ucontrol->value.iec958.status[1] = (status >> 8) & 0xff; - ucontrol->value.iec958.status[2] = (status >> 16) & 0xff; - ucontrol->value.iec958.status[3] = (status >> 24) & 0xff; - - return 0; -} - static int ct_spdif_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -586,6 +573,10 @@ static int ct_spdif_get(struct snd_kcontrol *kcontrol, unsigned int status; atc->spdif_out_get_status(atc, &status); + + if (status == 0) + status = SNDRV_PCM_DEFAULT_CON_SPDIF; + ucontrol->value.iec958.status[0] = (status >> 0) & 0xff; ucontrol->value.iec958.status[1] = (status >> 8) & 0xff; ucontrol->value.iec958.status[2] = (status >> 16) & 0xff; @@ -629,7 +620,7 @@ static struct snd_kcontrol_new iec958_default_ctl = { .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), .count = 1, .info = ct_spdif_info, - .get = ct_spdif_default_get, + .get = ct_spdif_get, .put = ct_spdif_put, .private_value = MIXER_IEC958_DEFAULT }; diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c index 65da6e466f80..b78f3fc3c33c 100644 --- a/sound/pci/ctxfi/ctvmem.c +++ b/sound/pci/ctxfi/ctvmem.c @@ -52,8 +52,7 @@ get_vm_block(struct ct_vm *vm, unsigned int size) if (entry->size == size) { /* Move the vm node from unused list to used list directly */ - list_del(&entry->list); - list_add(&entry->list, &vm->used); + list_move(&entry->list, &vm->used); vm->size -= size; block = entry; goto out; diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 66c7fb3ced3e..5e619a84da06 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -926,7 +926,7 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu) snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19); /* Unknown. */ snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c); - /* IRQ Enable: Alll on */ + /* IRQ Enable: All on */ /* snd_emu1010_fpga_write(emu, 0x09, 0x0f ); */ /* IRQ Enable: All off */ snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, 0x00); diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index ae5c5d5e4b7c..2c79e96d0324 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -29,6 +29,7 @@ #include <sound/asoundef.h> #include <sound/tlv.h> #include <sound/initval.h> +#include <sound/jack.h> #include "hda_local.h" #include "hda_beep.h" #include <sound/hda_hwdep.h> @@ -4959,5 +4960,109 @@ void snd_print_pcm_bits(int pcm, char *buf, int buflen) } EXPORT_SYMBOL_HDA(snd_print_pcm_bits); +#ifdef CONFIG_SND_HDA_INPUT_JACK +/* + * Input-jack notification support + */ +struct hda_jack_item { + hda_nid_t nid; + int type; + struct snd_jack *jack; +}; + +static const char *get_jack_default_name(struct hda_codec *codec, hda_nid_t nid, + int type) +{ + switch (type) { + case SND_JACK_HEADPHONE: + return "Headphone"; + case SND_JACK_MICROPHONE: + return "Mic"; + case SND_JACK_LINEOUT: + return "Line-out"; + case SND_JACK_HEADSET: + return "Headset"; + default: + return "Misc"; + } +} + +static void hda_free_jack_priv(struct snd_jack *jack) +{ + struct hda_jack_item *jacks = jack->private_data; + jacks->nid = 0; + jacks->jack = NULL; +} + +int snd_hda_input_jack_add(struct hda_codec *codec, hda_nid_t nid, int type, + const char *name) +{ + struct hda_jack_item *jack; + int err; + + snd_array_init(&codec->jacks, sizeof(*jack), 32); + jack = snd_array_new(&codec->jacks); + if (!jack) + return -ENOMEM; + + jack->nid = nid; + jack->type = type; + if (!name) + name = get_jack_default_name(codec, nid, type); + err = snd_jack_new(codec->bus->card, name, type, &jack->jack); + if (err < 0) { + jack->nid = 0; + return err; + } + jack->jack->private_data = jack; + jack->jack->private_free = hda_free_jack_priv; + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_input_jack_add); + +void snd_hda_input_jack_report(struct hda_codec *codec, hda_nid_t nid) +{ + struct hda_jack_item *jacks = codec->jacks.list; + int i; + + if (!jacks) + return; + + for (i = 0; i < codec->jacks.used; i++, jacks++) { + unsigned int pin_ctl; + unsigned int present; + int type; + + if (jacks->nid != nid) + continue; + present = snd_hda_jack_detect(codec, nid); + type = jacks->type; + if (type == (SND_JACK_HEADPHONE | SND_JACK_LINEOUT)) { + pin_ctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + type = (pin_ctl & AC_PINCTL_HP_EN) ? + SND_JACK_HEADPHONE : SND_JACK_LINEOUT; + } + snd_jack_report(jacks->jack, present ? type : 0); + } +} +EXPORT_SYMBOL_HDA(snd_hda_input_jack_report); + +/* free jack instances manually when clearing/reconfiguring */ +void snd_hda_input_jack_free(struct hda_codec *codec) +{ + if (!codec->bus->shutdown && codec->jacks.list) { + struct hda_jack_item *jacks = codec->jacks.list; + int i; + for (i = 0; i < codec->jacks.used; i++, jacks++) { + if (jacks->jack) + snd_device_free(codec->bus->card, jacks->jack); + } + } + snd_array_free(&codec->jacks); +} +EXPORT_SYMBOL_HDA(snd_hda_input_jack_free); +#endif /* CONFIG_SND_HDA_INPUT_JACK */ + MODULE_DESCRIPTION("HDA codec core"); MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index fdf8d44f8b6b..e46d5420a9f2 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -866,6 +866,11 @@ struct hda_codec { /* codec-specific additional proc output */ void (*proc_widget_hook)(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid); + +#ifdef CONFIG_SND_HDA_INPUT_JACK + /* jack detection */ + struct snd_array jacks; +#endif }; /* direction */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index fcedad9a5fef..70a9d32f0e96 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1052,9 +1052,12 @@ static void azx_init_pci(struct azx *chip) /* Clear bits 0-2 of PCI register TCSEL (at offset 0x44) * TCSEL == Traffic Class Select Register, which sets PCI express QOS * Ensuring these bits are 0 clears playback static on some HD Audio - * codecs + * codecs. + * The PCI register TCSEL is defined in the Intel manuals. */ - update_pci_byte(chip->pci, ICH6_PCIREG_TCSEL, 0x07, 0); + if (chip->driver_type != AZX_DRIVER_ATI && + chip->driver_type != AZX_DRIVER_ATIHDMI) + update_pci_byte(chip->pci, ICH6_PCIREG_TCSEL, 0x07, 0); switch (chip->driver_type) { case AZX_DRIVER_ATI: diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 3ab5e7a303db..ff5e2ac2239a 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -656,4 +656,28 @@ static inline void snd_hda_eld_proc_free(struct hda_codec *codec, #define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80 void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen); +/* + * Input-jack notification support + */ +#ifdef CONFIG_SND_HDA_INPUT_JACK +int snd_hda_input_jack_add(struct hda_codec *codec, hda_nid_t nid, int type, + const char *name); +void snd_hda_input_jack_report(struct hda_codec *codec, hda_nid_t nid); +void snd_hda_input_jack_free(struct hda_codec *codec); +#else /* CONFIG_SND_HDA_INPUT_JACK */ +static inline int snd_hda_input_jack_add(struct hda_codec *codec, + hda_nid_t nid, int type, + const char *name) +{ + return 0; +} +static inline void snd_hda_input_jack_report(struct hda_codec *codec, + hda_nid_t nid) +{ +} +static inline void snd_hda_input_jack_free(struct hda_codec *codec) +{ +} +#endif /* CONFIG_SND_HDA_INPUT_JACK */ + #endif /* __SOUND_HDA_LOCAL_H */ diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 8dabab798689..734c6ee55d8a 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -30,10 +30,10 @@ #include "hda_beep.h" struct ad198x_spec { - struct snd_kcontrol_new *mixers[5]; + struct snd_kcontrol_new *mixers[6]; int num_mixers; unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ - const struct hda_verb *init_verbs[5]; /* initialization verbs + const struct hda_verb *init_verbs[6]; /* initialization verbs * don't forget NULL termination! */ unsigned int num_init_verbs; @@ -331,36 +331,11 @@ static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); } -static int ad198x_alt_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct ad198x_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->alt_dac_nid[0], stream_tag, - 0, format); - return 0; -} - -static int ad198x_alt_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct ad198x_spec *spec = codec->spec; - snd_hda_codec_cleanup_stream(codec, spec->alt_dac_nid[0]); - return 0; -} - static struct hda_pcm_stream ad198x_pcm_analog_alt_playback = { .substreams = 1, .channels_min = 2, .channels_max = 2, /* NID is set in ad198x_build_pcms */ - .ops = { - .prepare = ad198x_alt_playback_pcm_prepare, - .cleanup = ad198x_alt_playback_pcm_cleanup - }, }; /* @@ -2239,29 +2214,6 @@ static struct snd_kcontrol_new ad1988_6stack_mixers2[] = { static struct snd_kcontrol_new ad1988_6stack_fp_mixers[] = { HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT), - HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT), - HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT), - HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT), - HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT), - - HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT), - HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT), - HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT), - HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT), - - HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT), - - HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT), - { } /* end */ }; @@ -2545,11 +2497,6 @@ static struct hda_verb ad1988_6stack_init_verbs[] = { }; static struct hda_verb ad1988_6stack_fp_init_verbs[] = { - /* Front, Surround, CLFE, side DAC; unmute as default */ - {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Headphone; unmute as default */ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Port-A front headphon path */ @@ -2558,50 +2505,6 @@ static struct hda_verb ad1988_6stack_fp_init_verbs[] = { {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - /* Port-D line-out path */ - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* Port-F surround path */ - {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* Port-G CLFE path */ - {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* Port-H side path */ - {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - /* Mono out path */ - {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */ - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, - {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */ - /* Port-B front mic-in path */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - /* Port-C line-in path */ - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, - {0x33, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* Port-E mic-in path */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, - {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}, - /* Analog Mix output amp */ - {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */ { } }; @@ -3316,20 +3219,20 @@ static int patch_ad1988(struct hda_codec *codec) spec->mixers[0] = ad1988_6stack_mixers1_rev2; else spec->mixers[0] = ad1988_6stack_mixers1; + spec->mixers[1] = ad1988_6stack_mixers2; + spec->num_init_verbs = 1; + spec->init_verbs[0] = ad1988_6stack_init_verbs; if (board_config == AD1988_6STACK_DIG_FP) { - spec->mixers[1] = ad1988_6stack_fp_mixers; + spec->num_mixers++; + spec->mixers[2] = ad1988_6stack_fp_mixers; + spec->num_init_verbs++; + spec->init_verbs[1] = ad1988_6stack_fp_init_verbs; spec->slave_vols = ad1988_6stack_fp_slave_vols; spec->slave_sws = ad1988_6stack_fp_slave_sws; spec->alt_dac_nid = ad1988_alt_dac_nid; spec->stream_analog_alt_playback = &ad198x_pcm_analog_alt_playback; - } else - spec->mixers[1] = ad1988_6stack_mixers2; - spec->num_init_verbs = 1; - if (board_config == AD1988_6STACK_DIG_FP) - spec->init_verbs[0] = ad1988_6stack_fp_init_verbs; - else - spec->init_verbs[0] = ad1988_6stack_init_verbs; + } if ((board_config == AD1988_6STACK_DIG) || (board_config == AD1988_6STACK_DIG_FP)) { spec->multiout.dig_out_nid = AD1988_SPDIF_OUT; diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 4d5004e693f0..d08cf31596f3 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -49,14 +49,6 @@ #define AUTO_MIC_PORTB (1 << 1) #define AUTO_MIC_PORTC (1 << 2) -struct conexant_jack { - - hda_nid_t nid; - int type; - struct snd_jack *jack; - -}; - struct pin_dac_pair { hda_nid_t pin; hda_nid_t dac; @@ -111,9 +103,6 @@ struct conexant_spec { unsigned int spdif_route; - /* jack detection */ - struct snd_array jacks; - /* dynamic controls, init_verbs and input_mux */ struct auto_pin_cfg autocfg; struct hda_input_mux private_imux; @@ -393,71 +382,9 @@ static int conexant_mux_enum_put(struct snd_kcontrol *kcontrol, &spec->cur_mux[adc_idx]); } -#ifdef CONFIG_SND_HDA_INPUT_JACK -static void conexant_free_jack_priv(struct snd_jack *jack) -{ - struct conexant_jack *jacks = jack->private_data; - jacks->nid = 0; - jacks->jack = NULL; -} - -static int conexant_add_jack(struct hda_codec *codec, - hda_nid_t nid, int type) -{ - struct conexant_spec *spec; - struct conexant_jack *jack; - const char *name; - int i, err; - - spec = codec->spec; - snd_array_init(&spec->jacks, sizeof(*jack), 32); - - jack = spec->jacks.list; - for (i = 0; i < spec->jacks.used; i++, jack++) - if (jack->nid == nid) - return 0 ; /* already present */ - - jack = snd_array_new(&spec->jacks); - name = (type == SND_JACK_HEADPHONE) ? "Headphone" : "Mic" ; - - if (!jack) - return -ENOMEM; - - jack->nid = nid; - jack->type = type; - - err = snd_jack_new(codec->bus->card, name, type, &jack->jack); - if (err < 0) - return err; - jack->jack->private_data = jack; - jack->jack->private_free = conexant_free_jack_priv; - return 0; -} - -static void conexant_report_jack(struct hda_codec *codec, hda_nid_t nid) -{ - struct conexant_spec *spec = codec->spec; - struct conexant_jack *jacks = spec->jacks.list; - - if (jacks) { - int i; - for (i = 0; i < spec->jacks.used; i++) { - if (jacks->nid == nid) { - unsigned int present; - present = snd_hda_jack_detect(codec, nid); - - present = (present) ? jacks->type : 0 ; - - snd_jack_report(jacks->jack, - present); - } - jacks++; - } - } -} - static int conexant_init_jacks(struct hda_codec *codec) { +#ifdef CONFIG_SND_HDA_INPUT_JACK struct conexant_spec *spec = codec->spec; int i; @@ -469,15 +396,15 @@ static int conexant_init_jacks(struct hda_codec *codec) int err = 0; switch (hv->param ^ AC_USRSP_EN) { case CONEXANT_HP_EVENT: - err = conexant_add_jack(codec, hv->nid, - SND_JACK_HEADPHONE); - conexant_report_jack(codec, hv->nid); + err = snd_hda_input_jack_add(codec, hv->nid, + SND_JACK_HEADPHONE, NULL); + snd_hda_input_jack_report(codec, hv->nid); break; case CXT5051_PORTC_EVENT: case CONEXANT_MIC_EVENT: - err = conexant_add_jack(codec, hv->nid, - SND_JACK_MICROPHONE); - conexant_report_jack(codec, hv->nid); + err = snd_hda_input_jack_add(codec, hv->nid, + SND_JACK_MICROPHONE, NULL); + snd_hda_input_jack_report(codec, hv->nid); break; } if (err < 0) @@ -485,19 +412,9 @@ static int conexant_init_jacks(struct hda_codec *codec) ++hv; } } - return 0; - -} -#else -static inline void conexant_report_jack(struct hda_codec *codec, hda_nid_t nid) -{ -} - -static inline int conexant_init_jacks(struct hda_codec *codec) -{ +#endif /* CONFIG_SND_HDA_INPUT_JACK */ return 0; } -#endif static int conexant_init(struct hda_codec *codec) { @@ -511,18 +428,7 @@ static int conexant_init(struct hda_codec *codec) static void conexant_free(struct hda_codec *codec) { -#ifdef CONFIG_SND_HDA_INPUT_JACK - struct conexant_spec *spec = codec->spec; - if (spec->jacks.list) { - struct conexant_jack *jacks = spec->jacks.list; - int i; - for (i = 0; i < spec->jacks.used; i++, jacks++) { - if (jacks->jack) - snd_device_free(codec->bus->card, jacks->jack); - } - snd_array_free(&spec->jacks); - } -#endif + snd_hda_input_jack_free(codec); snd_hda_detach_beep_device(codec); kfree(codec->spec); } @@ -1787,7 +1693,7 @@ static void cxt5051_hp_unsol_event(struct hda_codec *codec, cxt5051_portc_automic(codec); break; } - conexant_report_jack(codec, nid); + snd_hda_input_jack_report(codec, nid); } static struct snd_kcontrol_new cxt5051_playback_mixers[] = { @@ -1959,10 +1865,8 @@ static void cxt5051_init_mic_port(struct hda_codec *codec, hda_nid_t nid, snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | event); -#ifdef CONFIG_SND_HDA_INPUT_JACK - conexant_add_jack(codec, nid, SND_JACK_MICROPHONE); - conexant_report_jack(codec, nid); -#endif + snd_hda_input_jack_add(codec, nid, SND_JACK_MICROPHONE, NULL); + snd_hda_input_jack_report(codec, nid); } static struct hda_verb cxt5051_ideapad_init_verbs[] = { @@ -3477,11 +3381,11 @@ static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res) switch (res >> 26) { case CONEXANT_HP_EVENT: cx_auto_hp_automute(codec); - conexant_report_jack(codec, nid); + snd_hda_input_jack_report(codec, nid); break; case CONEXANT_MIC_EVENT: cx_auto_automic(codec); - conexant_report_jack(codec, nid); + snd_hda_input_jack_report(codec, nid); break; } } diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index ec0fa2dd0a27..251773e45f61 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -110,6 +110,12 @@ struct dp_audio_infoframe { u8 LFEPBL01_LSV36_DM_INH7; }; +union audio_infoframe { + struct hdmi_audio_infoframe hdmi; + struct dp_audio_infoframe dp; + u8 bytes[0]; +}; + /* * CEA speaker placement: * @@ -620,8 +626,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, int channels = substream->runtime->channels; int ca; int i; - u8 ai[max(sizeof(struct hdmi_audio_infoframe), - sizeof(struct dp_audio_infoframe))]; + union audio_infoframe ai; ca = hdmi_channel_allocation(codec, nid, channels); @@ -633,11 +638,10 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, pin_nid = spec->pin[i]; - memset(ai, 0, sizeof(ai)); + memset(&ai, 0, sizeof(ai)); if (spec->sink_eld[i].conn_type == 0) { /* HDMI */ - struct hdmi_audio_infoframe *hdmi_ai; + struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi; - hdmi_ai = (struct hdmi_audio_infoframe *)ai; hdmi_ai->type = 0x84; hdmi_ai->ver = 0x01; hdmi_ai->len = 0x0a; @@ -645,9 +649,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, hdmi_ai->CA = ca; hdmi_checksum_audio_infoframe(hdmi_ai); } else if (spec->sink_eld[i].conn_type == 1) { /* DisplayPort */ - struct dp_audio_infoframe *dp_ai; + struct dp_audio_infoframe *dp_ai = &ai.dp; - dp_ai = (struct dp_audio_infoframe *)ai; dp_ai->type = 0x84; dp_ai->len = 0x1b; dp_ai->ver = 0x11 << 2; @@ -664,7 +667,8 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, * sizeof(*dp_ai) to avoid partial match/update problems when * the user switches between HDMI/DP monitors. */ - if (!hdmi_infoframe_uptodate(codec, pin_nid, ai, sizeof(ai))) { + if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes, + sizeof(ai))) { snd_printdd("hdmi_setup_audio_infoframe: " "cvt=%d pin=%d channels=%d\n", nid, pin_nid, @@ -672,7 +676,7 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid, hdmi_setup_channel_mapping(codec, pin_nid, ca); hdmi_stop_infoframe_trans(codec, pin_nid); hdmi_fill_audio_infoframe(codec, pin_nid, - ai, sizeof(ai)); + ai.bytes, sizeof(ai)); hdmi_start_infoframe_trans(codec, pin_nid); } } diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 4261bb8eec1d..f1a03f223495 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -282,12 +282,6 @@ struct alc_mic_route { unsigned char amix_idx; }; -struct alc_jack { - hda_nid_t nid; - int type; - struct snd_jack *jack; -}; - #define MUX_IDX_UNDEF ((unsigned char)-1) struct alc_customize_define { @@ -366,9 +360,6 @@ struct alc_spec { /* PCM information */ struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */ - /* jack detection */ - struct snd_array jacks; - /* dynamic controls, init_verbs and input_mux */ struct auto_pin_cfg autocfg; struct alc_customize_define cdefine; @@ -394,6 +385,7 @@ struct alc_spec { /* other flags */ unsigned int no_analog :1; /* digital I/O only */ unsigned int dual_adc_switch:1; /* switch ADCs (for ALC275) */ + unsigned int single_input_src:1; int init_amp; int codec_variant; /* flag for other variants */ @@ -1032,94 +1024,32 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid, alc_fix_pll(codec); } -#ifdef CONFIG_SND_HDA_INPUT_JACK -static void alc_free_jack_priv(struct snd_jack *jack) -{ - struct alc_jack *jacks = jack->private_data; - jacks->nid = 0; - jacks->jack = NULL; -} - -static int alc_add_jack(struct hda_codec *codec, - hda_nid_t nid, int type) -{ - struct alc_spec *spec; - struct alc_jack *jack; - const char *name; - int err; - - spec = codec->spec; - snd_array_init(&spec->jacks, sizeof(*jack), 32); - jack = snd_array_new(&spec->jacks); - if (!jack) - return -ENOMEM; - - jack->nid = nid; - jack->type = type; - name = (type == SND_JACK_HEADPHONE) ? "Headphone" : "Mic" ; - - err = snd_jack_new(codec->bus->card, name, type, &jack->jack); - if (err < 0) - return err; - jack->jack->private_data = jack; - jack->jack->private_free = alc_free_jack_priv; - return 0; -} - -static void alc_report_jack(struct hda_codec *codec, hda_nid_t nid) -{ - struct alc_spec *spec = codec->spec; - struct alc_jack *jacks = spec->jacks.list; - - if (jacks) { - int i; - for (i = 0; i < spec->jacks.used; i++) { - if (jacks->nid == nid) { - unsigned int present; - present = snd_hda_jack_detect(codec, nid); - - present = (present) ? jacks->type : 0; - - snd_jack_report(jacks->jack, present); - } - jacks++; - } - } -} - static int alc_init_jacks(struct hda_codec *codec) { +#ifdef CONFIG_SND_HDA_INPUT_JACK struct alc_spec *spec = codec->spec; int err; unsigned int hp_nid = spec->autocfg.hp_pins[0]; unsigned int mic_nid = spec->ext_mic.pin; if (hp_nid) { - err = alc_add_jack(codec, hp_nid, SND_JACK_HEADPHONE); + err = snd_hda_input_jack_add(codec, hp_nid, + SND_JACK_HEADPHONE, NULL); if (err < 0) return err; - alc_report_jack(codec, hp_nid); + snd_hda_input_jack_report(codec, hp_nid); } if (mic_nid) { - err = alc_add_jack(codec, mic_nid, SND_JACK_MICROPHONE); + err = snd_hda_input_jack_add(codec, mic_nid, + SND_JACK_MICROPHONE, NULL); if (err < 0) return err; - alc_report_jack(codec, mic_nid); + snd_hda_input_jack_report(codec, mic_nid); } - - return 0; -} -#else -static inline void alc_report_jack(struct hda_codec *codec, hda_nid_t nid) -{ -} - -static inline int alc_init_jacks(struct hda_codec *codec) -{ +#endif /* CONFIG_SND_HDA_INPUT_JACK */ return 0; } -#endif static void alc_automute_speaker(struct hda_codec *codec, int pinctl) { @@ -1133,7 +1063,7 @@ static void alc_automute_speaker(struct hda_codec *codec, int pinctl) nid = spec->autocfg.hp_pins[i]; if (!nid) break; - alc_report_jack(codec, nid); + snd_hda_input_jack_report(codec, nid); spec->jack_present |= snd_hda_jack_detect(codec, nid); } @@ -1240,7 +1170,7 @@ static void alc_mic_automute(struct hda_codec *codec) AC_VERB_SET_CONNECT_SEL, alive->mux_idx); } - alc_report_jack(codec, spec->ext_mic.pin); + snd_hda_input_jack_report(codec, spec->ext_mic.pin); /* FIXME: analog mixer */ } @@ -2292,13 +2222,13 @@ static struct snd_kcontrol_new alc888_acer_aspire_4930g_mixer[] = { HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0f, 2, 0x0, + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0f, 2, 2, HDA_INPUT), - HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0f, 1, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0f, 1, 2, HDA_INPUT), - HDA_CODEC_VOLUME("Side Playback Volume", 0x0e, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Side Playback Switch", 0x0e, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Internal LFE Playback Volume", 0x0f, 1, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Internal LFE Playback Switch", 0x0f, 1, 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("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), @@ -2309,7 +2239,6 @@ static struct snd_kcontrol_new alc888_acer_aspire_4930g_mixer[] = { { } /* end */ }; - static struct snd_kcontrol_new alc889_acer_aspire_8930g_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), @@ -3919,6 +3848,8 @@ static struct hda_amp_list alc880_lg_loopbacks[] = { * Common callbacks */ +static void alc_init_special_input_src(struct hda_codec *codec); + static int alc_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -3929,6 +3860,7 @@ static int alc_init(struct hda_codec *codec) for (i = 0; i < spec->num_init_verbs; i++) snd_hda_sequence_write(codec, spec->init_verbs[i]); + alc_init_special_input_src(codec); if (spec->init_hook) spec->init_hook(codec); @@ -4284,6 +4216,7 @@ static void alc_free(struct hda_codec *codec) return; alc_shutup(codec); + snd_hda_input_jack_free(codec); alc_free_kctls(codec); kfree(spec); snd_hda_detach_beep_device(codec); @@ -4702,7 +4635,7 @@ static struct snd_pci_quirk alc880_cfg_tbl[] = { 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, 0x9054, "Uniwill", ALC880_F1734), SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_UNIWILL), SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_UNIWILL_P53), SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810), @@ -5151,7 +5084,9 @@ static const char *alc_get_line_out_pfx(const struct auto_pin_cfg *cfg, switch (cfg->line_out_type) { case AUTO_PIN_SPEAKER_OUT: - return "Speaker"; + if (cfg->line_outs == 1) + return "Speaker"; + break; case AUTO_PIN_HP_OUT: return "Headphone"; default: @@ -5205,16 +5140,19 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec, return err; } else { const char *name = pfx; - if (!name) + int index = i; + if (!name) { name = chname[i]; + index = 0; + } err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, - name, i, + name, index, HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); if (err < 0) return err; err = __add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, - name, i, + name, index, HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT)); if (err < 0) @@ -5585,6 +5523,7 @@ static void fixup_single_adc(struct hda_codec *codec) spec->capsrc_nids += i; spec->adc_nids += i; spec->num_adc_nids = 1; + spec->single_input_src = 1; } } @@ -5596,6 +5535,16 @@ static void fixup_dual_adc_switch(struct hda_codec *codec) init_capsrc_for_pin(codec, spec->int_mic.pin); } +/* initialize some special cases for input sources */ +static void alc_init_special_input_src(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + if (spec->dual_adc_switch) + fixup_dual_adc_switch(codec); + else if (spec->single_input_src) + init_capsrc_for_pin(codec, spec->autocfg.inputs[0].pin); +} + static void set_capture_mixer(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -5611,7 +5560,7 @@ static void set_capture_mixer(struct hda_codec *codec) int mux = 0; int num_adcs = spec->num_adc_nids; if (spec->dual_adc_switch) - fixup_dual_adc_switch(codec); + num_adcs = 1; else if (spec->auto_mic) fixup_automic_adc(codec); else if (spec->input_mux) { @@ -5620,8 +5569,6 @@ static void set_capture_mixer(struct hda_codec *codec) else if (spec->input_mux->num_items == 1) fixup_single_adc(codec); } - if (spec->dual_adc_switch) - num_adcs = 1; spec->cap_mixer = caps[mux][num_adcs - 1]; } } @@ -10748,6 +10695,7 @@ static struct alc_config_preset alc882_presets[] = { */ enum { PINFIX_ABIT_AW9D_MAX, + PINFIX_LENOVO_Y530, PINFIX_PB_M5210, PINFIX_ACER_ASPIRE_7736, }; @@ -10762,6 +10710,14 @@ static const struct alc_fixup alc882_fixups[] = { { } } }, + [PINFIX_LENOVO_Y530] = { + .type = ALC_FIXUP_PINS, + .v.pins = (const struct alc_pincfg[]) { + { 0x15, 0x99130112 }, /* rear int speakers */ + { 0x16, 0x99130111 }, /* subwoofer */ + { } + } + }, [PINFIX_PB_M5210] = { .type = ALC_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { @@ -10777,6 +10733,7 @@ static const struct alc_fixup alc882_fixups[] = { static struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", PINFIX_PB_M5210), + SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", PINFIX_LENOVO_Y530), SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX), SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", PINFIX_ACER_ASPIRE_7736), {} @@ -10829,23 +10786,28 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec) hda_nid_t pin, dac; int i; - for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) { - pin = spec->autocfg.hp_pins[i]; - if (!pin) - break; - dac = spec->multiout.hp_nid; - if (!dac) - dac = spec->multiout.dac_nids[0]; /* to front */ - alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, dac); + if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT) { + for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) { + pin = spec->autocfg.hp_pins[i]; + if (!pin) + break; + dac = spec->multiout.hp_nid; + if (!dac) + dac = spec->multiout.dac_nids[0]; /* to front */ + alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, dac); + } } - for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) { - pin = spec->autocfg.speaker_pins[i]; - if (!pin) - break; - dac = spec->multiout.extra_out_nid[0]; - if (!dac) - dac = spec->multiout.dac_nids[0]; /* to front */ - alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac); + + if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT) { + for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) { + pin = spec->autocfg.speaker_pins[i]; + if (!pin) + break; + dac = spec->multiout.extra_out_nid[0]; + if (!dac) + dac = spec->multiout.dac_nids[0]; /* to front */ + alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac); + } } } @@ -13796,6 +13758,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec) } #define alc268_auto_init_analog_input alc882_auto_init_analog_input +#define alc268_auto_init_input_src alc882_auto_init_input_src /* init callback for auto-configuration model -- overriding the default init */ static void alc268_auto_init(struct hda_codec *codec) @@ -13805,6 +13768,7 @@ static void alc268_auto_init(struct hda_codec *codec) alc268_auto_init_hp_out(codec); alc268_auto_init_mono_speaker_out(codec); alc268_auto_init_analog_input(codec); + alc268_auto_init_input_src(codec); alc_auto_init_digital(codec); if (spec->unsol_event) alc_inithook(codec); @@ -14092,7 +14056,6 @@ static int patch_alc268(struct hda_codec *codec) if (!spec->no_analog && !spec->adc_nids && spec->input_mux) { /* check whether NID 0x07 is valid */ unsigned int wcap = get_wcaps(codec, 0x07); - int i; spec->capsrc_nids = alc268_capsrc_nids; /* get type */ @@ -14112,13 +14075,6 @@ static int patch_alc268(struct hda_codec *codec) spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids); add_mixer(spec, alc268_capture_mixer); } - /* set default input source */ - for (i = 0; i < spec->num_adc_nids; i++) - snd_hda_codec_write_cache(codec, alc268_capsrc_nids[i], - 0, AC_VERB_SET_CONNECT_SEL, - i < spec->num_mux_defs ? - spec->input_mux[i].items[0].index : - spec->input_mux->items[0].index); } spec->vmaster_nid = 0x02; @@ -14495,7 +14451,7 @@ static void alc269_speaker_automute(struct hda_codec *codec) HDA_AMP_MUTE, bits); snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, HDA_AMP_MUTE, bits); - alc_report_jack(codec, nid); + snd_hda_input_jack_report(codec, nid); } /* unsolicited event for HP jack sensing */ @@ -14807,11 +14763,6 @@ static int alc269_parse_auto_config(struct hda_codec *codec) fillup_priv_adc_nids(codec, alc269_adc_candidates, sizeof(alc269_adc_candidates)); - /* set default input source */ - if (!spec->dual_adc_switch) - select_or_unmute_capsrc(codec, spec->capsrc_nids[0], - spec->input_mux->items[0].index); - err = alc_auto_add_mic_boost(codec); if (err < 0) return err; @@ -14825,6 +14776,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec) #define alc269_auto_init_multi_out alc268_auto_init_multi_out #define alc269_auto_init_hp_out alc268_auto_init_hp_out #define alc269_auto_init_analog_input alc882_auto_init_analog_input +#define alc269_auto_init_input_src alc882_auto_init_input_src /* init callback for auto-configuration model -- overriding the default init */ @@ -14834,6 +14786,8 @@ 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); + if (!spec->dual_adc_switch) + alc269_auto_init_input_src(codec); alc_auto_init_digital(codec); if (spec->unsol_event) alc_inithook(codec); diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index bd7b123f6440..05fcd60cc46f 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -180,18 +180,16 @@ struct sigmatel_event { int data; }; -struct sigmatel_jack { - hda_nid_t nid; - int type; - struct snd_jack *jack; -}; - struct sigmatel_mic_route { hda_nid_t pin; signed char mux_idx; signed char dmux_idx; }; +#define MAX_PINS_NUM 16 +#define MAX_ADCS_NUM 4 +#define MAX_DMICS_NUM 4 + struct sigmatel_spec { struct snd_kcontrol_new *mixers[4]; unsigned int num_mixers; @@ -229,9 +227,6 @@ struct sigmatel_spec { hda_nid_t *pwr_nids; hda_nid_t *dac_list; - /* jack detection */ - struct snd_array jacks; - /* events */ struct snd_array events; @@ -309,6 +304,17 @@ struct sigmatel_spec { struct hda_input_mux private_imux; struct hda_input_mux private_smux; struct hda_input_mux private_mono_mux; + + /* auto spec */ + unsigned auto_pin_cnt; + hda_nid_t auto_pin_nids[MAX_PINS_NUM]; + unsigned auto_adc_cnt; + hda_nid_t auto_adc_nids[MAX_ADCS_NUM]; + hda_nid_t auto_mux_nids[MAX_ADCS_NUM]; + hda_nid_t auto_dmux_nids[MAX_ADCS_NUM]; + unsigned long auto_capvols[MAX_ADCS_NUM]; + unsigned auto_dmic_cnt; + hda_nid_t auto_dmic_nids[MAX_DMICS_NUM]; }; static hda_nid_t stac9200_adc_nids[1] = { @@ -364,14 +370,6 @@ static unsigned long stac92hd73xx_capvols[] = { #define STAC92HD83_DAC_COUNT 3 -static hda_nid_t stac92hd83xxx_mux_nids[2] = { - 0x17, 0x18, -}; - -static hda_nid_t stac92hd83xxx_adc_nids[2] = { - 0x15, 0x16, -}; - static hda_nid_t stac92hd83xxx_pwr_nids[4] = { 0xa, 0xb, 0xd, 0xe, }; @@ -384,25 +382,9 @@ static unsigned int stac92hd83xxx_pwr_mapping[4] = { 0x03, 0x0c, 0x20, 0x40, }; -#define STAC92HD83XXX_NUM_DMICS 2 -static hda_nid_t stac92hd83xxx_dmic_nids[STAC92HD83XXX_NUM_DMICS + 1] = { - 0x11, 0x20, 0 -}; - -#define STAC92HD88XXX_NUM_DMICS STAC92HD83XXX_NUM_DMICS -#define stac92hd88xxx_dmic_nids stac92hd83xxx_dmic_nids - -#define STAC92HD87B_NUM_DMICS 1 -static hda_nid_t stac92hd87b_dmic_nids[STAC92HD87B_NUM_DMICS + 1] = { - 0x11, 0 -}; - -#define STAC92HD83XXX_NUM_CAPS 2 -static unsigned long stac92hd83xxx_capvols[] = { - HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT), - HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_OUTPUT), +static hda_nid_t stac92hd83xxx_dmic_nids[] = { + 0x11, 0x20, }; -#define stac92hd83xxx_capsws stac92hd83xxx_capvols static hda_nid_t stac92hd71bxx_pwr_nids[3] = { 0x0a, 0x0d, 0x0f @@ -581,21 +563,6 @@ static hda_nid_t stac92hd73xx_pin_nids[13] = { 0x14, 0x22, 0x23 }; -static hda_nid_t stac92hd83xxx_pin_nids[10] = { - 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0x10, 0x11, 0x1f, 0x20, -}; - -static hda_nid_t stac92hd87xxx_pin_nids[6] = { - 0x0a, 0x0b, 0x0c, 0x0d, - 0x0f, 0x11, -}; - -static hda_nid_t stac92hd88xxx_pin_nids[8] = { - 0x0a, 0x0b, 0x0c, 0x0d, - 0x0f, 0x11, 0x1f, 0x20, -}; - #define STAC92HD71BXX_NUM_PINS 13 static hda_nid_t stac92hd71bxx_pin_nids_4port[STAC92HD71BXX_NUM_PINS] = { 0x0a, 0x0b, 0x0c, 0x0d, 0x00, @@ -757,7 +724,7 @@ static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e struct sigmatel_spec *spec = codec->spec; unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); const struct hda_input_mux *imux = spec->input_mux; - unsigned int idx, prev_idx; + unsigned int idx, prev_idx, didx; idx = ucontrol->value.enumerated.item[0]; if (idx >= imux->num_items) @@ -769,7 +736,8 @@ static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e snd_hda_codec_write_cache(codec, spec->mux_nids[adc_idx], 0, AC_VERB_SET_CONNECT_SEL, imux->items[idx].index); - if (prev_idx >= spec->num_analog_muxes) { + if (prev_idx >= spec->num_analog_muxes && + spec->mux_nids[adc_idx] != spec->dmux_nids[adc_idx]) { imux = spec->dinput_mux; /* 0 = analog */ snd_hda_codec_write_cache(codec, @@ -779,9 +747,13 @@ static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e } } else { imux = spec->dinput_mux; + /* first dimux item is hardcoded to select analog imux, + * so lets skip it + */ + didx = idx - spec->num_analog_muxes + 1; snd_hda_codec_write_cache(codec, spec->dmux_nids[adc_idx], 0, AC_VERB_SET_CONNECT_SEL, - imux->items[idx - 1].index); + imux->items[didx].index); } spec->cur_mux[adc_idx] = idx; return 1; @@ -3419,6 +3391,17 @@ static const char * const stac92xx_dmic_labels[5] = { "Digital Mic 3", "Digital Mic 4" }; +static hda_nid_t get_connected_node(struct hda_codec *codec, hda_nid_t mux, + int idx) +{ + hda_nid_t conn[HDA_MAX_NUM_INPUTS]; + int nums; + nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); + if (idx >= 0 && idx < nums) + return conn[idx]; + return 0; +} + static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, hda_nid_t nid) { @@ -3429,6 +3412,15 @@ static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, for (i = 0; i < nums; i++) if (conn[i] == nid) return i; + + for (i = 0; i < nums; i++) { + unsigned int wid_caps = get_wcaps(codec, conn[i]); + unsigned int wid_type = get_wcaps_type(wid_caps); + + if (wid_type != AC_WID_PIN && wid_type != AC_WID_AUD_MIX) + if (get_connection_index(codec, conn[i], nid) >= 0) + return i; + } return -1; } @@ -3501,6 +3493,16 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, type_idx, HDA_OUTPUT); if (err < 0) return err; + if (!err) { + nid = get_connected_node(codec, + spec->dmux_nids[0], index); + if (nid) + err = create_elem_capture_vol(codec, + nid, label, + type_idx, HDA_INPUT); + if (err < 0) + return err; + } } } @@ -4054,21 +4056,10 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */ } -#ifdef CONFIG_SND_HDA_INPUT_JACK -static void stac92xx_free_jack_priv(struct snd_jack *jack) -{ - struct sigmatel_jack *jacks = jack->private_data; - jacks->nid = 0; - jacks->jack = NULL; -} -#endif - static int stac92xx_add_jack(struct hda_codec *codec, hda_nid_t nid, int type) { #ifdef CONFIG_SND_HDA_INPUT_JACK - struct sigmatel_spec *spec = codec->spec; - struct sigmatel_jack *jack; int def_conf = snd_hda_codec_get_pincfg(codec, nid); int connectivity = get_defcfg_connect(def_conf); char name[32]; @@ -4077,26 +4068,15 @@ static int stac92xx_add_jack(struct hda_codec *codec, if (connectivity && connectivity != AC_JACK_PORT_FIXED) return 0; - snd_array_init(&spec->jacks, sizeof(*jack), 32); - jack = snd_array_new(&spec->jacks); - if (!jack) - return -ENOMEM; - jack->nid = nid; - jack->type = type; - snprintf(name, sizeof(name), "%s at %s %s Jack", snd_hda_get_jack_type(def_conf), snd_hda_get_jack_connectivity(def_conf), snd_hda_get_jack_location(def_conf)); - err = snd_jack_new(codec->bus->card, name, type, &jack->jack); - if (err < 0) { - jack->nid = 0; + err = snd_hda_input_jack_add(codec, nid, type, name); + if (err < 0) return err; - } - jack->jack->private_data = jack; - jack->jack->private_free = stac92xx_free_jack_priv; -#endif +#endif /* CONFIG_SND_HDA_INPUT_JACK */ return 0; } @@ -4399,23 +4379,6 @@ static int stac92xx_init(struct hda_codec *codec) return 0; } -static void stac92xx_free_jacks(struct hda_codec *codec) -{ -#ifdef CONFIG_SND_HDA_INPUT_JACK - /* free jack instances manually when clearing/reconfiguring */ - struct sigmatel_spec *spec = codec->spec; - if (!codec->bus->shutdown && spec->jacks.list) { - struct sigmatel_jack *jacks = spec->jacks.list; - int i; - for (i = 0; i < spec->jacks.used; i++, jacks++) { - if (jacks->jack) - snd_device_free(codec->bus->card, jacks->jack); - } - } - snd_array_free(&spec->jacks); -#endif -} - static void stac92xx_free_kctls(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; @@ -4449,7 +4412,7 @@ static void stac92xx_free(struct hda_codec *codec) return; stac92xx_shutup(codec); - stac92xx_free_jacks(codec); + snd_hda_input_jack_free(codec); snd_array_free(&spec->events); kfree(spec); @@ -4667,33 +4630,6 @@ static void stac92xx_pin_sense(struct hda_codec *codec, hda_nid_t nid) stac_toggle_power_map(codec, nid, get_pin_presence(codec, nid)); } -static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid) -{ - struct sigmatel_spec *spec = codec->spec; - struct sigmatel_jack *jacks = spec->jacks.list; - - if (jacks) { - int i; - for (i = 0; i < spec->jacks.used; i++) { - if (jacks->nid == nid) { - unsigned int pin_ctl = - snd_hda_codec_read(codec, nid, - 0, AC_VERB_GET_PIN_WIDGET_CONTROL, - 0x00); - int type = jacks->type; - if (type == (SND_JACK_LINEOUT - | SND_JACK_HEADPHONE)) - type = (pin_ctl & AC_PINCTL_HP_EN) - ? SND_JACK_HEADPHONE : SND_JACK_LINEOUT; - snd_jack_report(jacks->jack, - get_pin_presence(codec, nid) - ? type : 0); - } - jacks++; - } - } -} - /* get the pin connection (fixed, none, etc) */ static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx) { @@ -4782,7 +4718,7 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res) case STAC_PWR_EVENT: if (spec->num_pwrs > 0) stac92xx_pin_sense(codec, event->nid); - stac92xx_report_jack(codec, event->nid); + snd_hda_input_jack_report(codec, event->nid); switch (codec->subsystem_id) { case 0x103c308f: @@ -5378,6 +5314,105 @@ static int hp_bnb2011_with_dock(struct hda_codec *codec) return 0; } +static void stac92hd8x_add_pin(struct hda_codec *codec, hda_nid_t nid) +{ + struct sigmatel_spec *spec = codec->spec; + unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid); + int i; + + spec->auto_pin_nids[spec->auto_pin_cnt] = nid; + spec->auto_pin_cnt++; + + if (get_defcfg_device(def_conf) == AC_JACK_MIC_IN && + get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE) { + for (i = 0; i < ARRAY_SIZE(stac92hd83xxx_dmic_nids); i++) { + if (nid == stac92hd83xxx_dmic_nids[i]) { + spec->auto_dmic_nids[spec->auto_dmic_cnt] = nid; + spec->auto_dmic_cnt++; + } + } + } +} + +static void stac92hd8x_add_adc(struct hda_codec *codec, hda_nid_t nid) +{ + struct sigmatel_spec *spec = codec->spec; + + spec->auto_adc_nids[spec->auto_adc_cnt] = nid; + spec->auto_adc_cnt++; +} + +static void stac92hd8x_add_mux(struct hda_codec *codec, hda_nid_t nid) +{ + int i, j; + struct sigmatel_spec *spec = codec->spec; + + for (i = 0; i < spec->auto_adc_cnt; i++) { + if (get_connection_index(codec, + spec->auto_adc_nids[i], nid) >= 0) { + /* mux and volume for adc_nids[i] */ + if (!spec->auto_mux_nids[i]) { + spec->auto_mux_nids[i] = nid; + /* 92hd codecs capture volume is in mux */ + spec->auto_capvols[i] = HDA_COMPOSE_AMP_VAL(nid, + 3, 0, HDA_OUTPUT); + } + for (j = 0; j < spec->auto_dmic_cnt; j++) { + if (get_connection_index(codec, nid, + spec->auto_dmic_nids[j]) >= 0) { + /* dmux for adc_nids[i] */ + if (!spec->auto_dmux_nids[i]) + spec->auto_dmux_nids[i] = nid; + break; + } + } + break; + } + } +} + +static void stac92hd8x_fill_auto_spec(struct hda_codec *codec) +{ + hda_nid_t nid, end_nid; + unsigned int wid_caps, wid_type; + struct sigmatel_spec *spec = codec->spec; + + end_nid = codec->start_nid + codec->num_nodes; + + for (nid = codec->start_nid; nid < end_nid; nid++) { + wid_caps = get_wcaps(codec, nid); + wid_type = get_wcaps_type(wid_caps); + + if (wid_type == AC_WID_PIN) + stac92hd8x_add_pin(codec, nid); + + if (wid_type == AC_WID_AUD_IN && !(wid_caps & AC_WCAP_DIGITAL)) + stac92hd8x_add_adc(codec, nid); + } + + for (nid = codec->start_nid; nid < end_nid; nid++) { + wid_caps = get_wcaps(codec, nid); + wid_type = get_wcaps_type(wid_caps); + + if (wid_type == AC_WID_AUD_SEL) + stac92hd8x_add_mux(codec, nid); + } + + spec->pin_nids = spec->auto_pin_nids; + spec->num_pins = spec->auto_pin_cnt; + spec->adc_nids = spec->auto_adc_nids; + spec->num_adcs = spec->auto_adc_cnt; + spec->capvols = spec->auto_capvols; + spec->capsws = spec->auto_capvols; + spec->num_caps = spec->auto_adc_cnt; + spec->mux_nids = spec->auto_mux_nids; + spec->num_muxes = spec->auto_adc_cnt; + spec->dmux_nids = spec->auto_dmux_nids; + spec->num_dmuxes = spec->auto_adc_cnt; + spec->dmic_nids = spec->auto_dmic_nids; + spec->num_dmics = spec->auto_dmic_cnt; +} + static int patch_stac92hd83xxx(struct hda_codec *codec) { struct sigmatel_spec *spec; @@ -5399,26 +5434,17 @@ static int patch_stac92hd83xxx(struct hda_codec *codec) snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7ED, 0); codec->no_trigger_sense = 1; codec->spec = spec; + + stac92hd8x_fill_auto_spec(codec); + spec->linear_tone_beep = 0; codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs; spec->digbeep_nid = 0x21; - spec->dmic_nids = stac92hd83xxx_dmic_nids; - spec->dmux_nids = stac92hd83xxx_mux_nids; - spec->mux_nids = stac92hd83xxx_mux_nids; - spec->num_muxes = ARRAY_SIZE(stac92hd83xxx_mux_nids); - spec->adc_nids = stac92hd83xxx_adc_nids; - spec->num_adcs = ARRAY_SIZE(stac92hd83xxx_adc_nids); spec->pwr_nids = stac92hd83xxx_pwr_nids; spec->pwr_mapping = stac92hd83xxx_pwr_mapping; spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids); spec->multiout.dac_nids = spec->dac_nids; - spec->init = stac92hd83xxx_core_init; - spec->num_pins = ARRAY_SIZE(stac92hd83xxx_pin_nids); - spec->pin_nids = stac92hd83xxx_pin_nids; - spec->num_caps = STAC92HD83XXX_NUM_CAPS; - spec->capvols = stac92hd83xxx_capvols; - spec->capsws = stac92hd83xxx_capsws; spec->board_config = snd_hda_check_board_config(codec, STAC_92HD83XXX_MODELS, @@ -5436,28 +5462,11 @@ again: case 0x111d76d1: case 0x111d76d9: case 0x111d76e5: - spec->dmic_nids = stac92hd87b_dmic_nids; - spec->num_dmics = stac92xx_connected_ports(codec, - stac92hd87b_dmic_nids, - STAC92HD87B_NUM_DMICS); - spec->num_pins = ARRAY_SIZE(stac92hd87xxx_pin_nids); - spec->pin_nids = stac92hd87xxx_pin_nids; - spec->mono_nid = 0; - spec->num_pwrs = 0; - break; case 0x111d7666: case 0x111d7667: case 0x111d7668: case 0x111d7669: case 0x111d76e3: - spec->num_dmics = stac92xx_connected_ports(codec, - stac92hd88xxx_dmic_nids, - STAC92HD88XXX_NUM_DMICS); - spec->num_pins = ARRAY_SIZE(stac92hd88xxx_pin_nids); - spec->pin_nids = stac92hd88xxx_pin_nids; - spec->mono_nid = 0; - spec->num_pwrs = 0; - break; case 0x111d7604: case 0x111d76d4: case 0x111d7605: @@ -5466,9 +5475,6 @@ again: if (spec->board_config == STAC_92HD83XXX_PWR_REF) break; spec->num_pwrs = 0; - spec->num_dmics = stac92xx_connected_ports(codec, - stac92hd83xxx_dmic_nids, - STAC92HD83XXX_NUM_DMICS); break; } diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index 13cec1e5ced9..2ae8d29500a8 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -341,9 +341,9 @@ static int snd_intel8x0m_codec_semaphore(struct intel8x0m *chip, unsigned int co return -EBUSY; } -static void snd_intel8x0_codec_write(struct snd_ac97 *ac97, - unsigned short reg, - unsigned short val) +static void snd_intel8x0m_codec_write(struct snd_ac97 *ac97, + unsigned short reg, + unsigned short val) { struct intel8x0m *chip = ac97->private_data; @@ -354,8 +354,8 @@ static void snd_intel8x0_codec_write(struct snd_ac97 *ac97, iaputword(chip, reg + ac97->num * 0x80, val); } -static unsigned short snd_intel8x0_codec_read(struct snd_ac97 *ac97, - unsigned short reg) +static unsigned short snd_intel8x0m_codec_read(struct snd_ac97 *ac97, + unsigned short reg) { struct intel8x0m *chip = ac97->private_data; unsigned short res; @@ -385,7 +385,7 @@ static unsigned short snd_intel8x0_codec_read(struct snd_ac97 *ac97, /* * DMA I/O */ -static void snd_intel8x0_setup_periods(struct intel8x0m *chip, struct ichdev *ichdev) +static void snd_intel8x0m_setup_periods(struct intel8x0m *chip, struct ichdev *ichdev) { int idx; u32 *bdbar = ichdev->bdbar; @@ -437,7 +437,7 @@ static void snd_intel8x0_setup_periods(struct intel8x0m *chip, struct ichdev *ic * Interrupt handler */ -static inline void snd_intel8x0_update(struct intel8x0m *chip, struct ichdev *ichdev) +static inline void snd_intel8x0m_update(struct intel8x0m *chip, struct ichdev *ichdev) { unsigned long port = ichdev->reg_offset; int civ, i, step; @@ -489,7 +489,7 @@ static inline void snd_intel8x0_update(struct intel8x0m *chip, struct ichdev *ic iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI); } -static irqreturn_t snd_intel8x0_interrupt(int irq, void *dev_id) +static irqreturn_t snd_intel8x0m_interrupt(int irq, void *dev_id) { struct intel8x0m *chip = dev_id; struct ichdev *ichdev; @@ -512,7 +512,7 @@ static irqreturn_t snd_intel8x0_interrupt(int irq, void *dev_id) for (i = 0; i < chip->bdbars_count; i++) { ichdev = &chip->ichd[i]; if (status & ichdev->int_sta_mask) - snd_intel8x0_update(chip, ichdev); + snd_intel8x0m_update(chip, ichdev); } /* ack them */ @@ -526,7 +526,7 @@ static irqreturn_t snd_intel8x0_interrupt(int irq, void *dev_id) * PCM part */ -static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +static int snd_intel8x0m_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct intel8x0m *chip = snd_pcm_substream_chip(substream); struct ichdev *ichdev = get_ichdev(substream); @@ -561,18 +561,18 @@ static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd return 0; } -static int snd_intel8x0_hw_params(struct snd_pcm_substream *substream, +static int snd_intel8x0m_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 snd_intel8x0_hw_free(struct snd_pcm_substream *substream) +static int snd_intel8x0m_hw_free(struct snd_pcm_substream *substream) { return snd_pcm_lib_free_pages(substream); } -static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(struct snd_pcm_substream *substream) +static snd_pcm_uframes_t snd_intel8x0m_pcm_pointer(struct snd_pcm_substream *substream) { struct intel8x0m *chip = snd_pcm_substream_chip(substream); struct ichdev *ichdev = get_ichdev(substream); @@ -600,7 +600,7 @@ static int snd_intel8x0m_pcm_prepare(struct snd_pcm_substream *substream) ichdev->fragsize = snd_pcm_lib_period_bytes(substream); snd_ac97_write(ichdev->ac97, AC97_LINE1_RATE, runtime->rate); snd_ac97_write(ichdev->ac97, AC97_LINE1_LEVEL, 0); - snd_intel8x0_setup_periods(chip, ichdev); + snd_intel8x0m_setup_periods(chip, ichdev); return 0; } @@ -682,22 +682,22 @@ static struct snd_pcm_ops snd_intel8x0m_playback_ops = { .open = snd_intel8x0m_playback_open, .close = snd_intel8x0m_playback_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_intel8x0_hw_params, - .hw_free = snd_intel8x0_hw_free, + .hw_params = snd_intel8x0m_hw_params, + .hw_free = snd_intel8x0m_hw_free, .prepare = snd_intel8x0m_pcm_prepare, - .trigger = snd_intel8x0_pcm_trigger, - .pointer = snd_intel8x0_pcm_pointer, + .trigger = snd_intel8x0m_pcm_trigger, + .pointer = snd_intel8x0m_pcm_pointer, }; static struct snd_pcm_ops snd_intel8x0m_capture_ops = { .open = snd_intel8x0m_capture_open, .close = snd_intel8x0m_capture_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_intel8x0_hw_params, - .hw_free = snd_intel8x0_hw_free, + .hw_params = snd_intel8x0m_hw_params, + .hw_free = snd_intel8x0m_hw_free, .prepare = snd_intel8x0m_pcm_prepare, - .trigger = snd_intel8x0_pcm_trigger, - .pointer = snd_intel8x0_pcm_pointer, + .trigger = snd_intel8x0m_pcm_trigger, + .pointer = snd_intel8x0m_pcm_pointer, }; @@ -710,7 +710,7 @@ struct ich_pcm_table { int ac97_idx; }; -static int __devinit snd_intel8x0_pcm1(struct intel8x0m *chip, int device, +static int __devinit snd_intel8x0m_pcm1(struct intel8x0m *chip, int device, struct ich_pcm_table *rec) { struct snd_pcm *pcm; @@ -759,7 +759,7 @@ static struct ich_pcm_table intel_pcms[] __devinitdata = { }, }; -static int __devinit snd_intel8x0_pcm(struct intel8x0m *chip) +static int __devinit snd_intel8x0m_pcm(struct intel8x0m *chip) { int i, tblsize, device, err; struct ich_pcm_table *tbl, *rec; @@ -791,7 +791,7 @@ static int __devinit snd_intel8x0_pcm(struct intel8x0m *chip) if (! chip->ichd[rec->ac97_idx].ac97) continue; } - err = snd_intel8x0_pcm1(chip, device, rec); + err = snd_intel8x0m_pcm1(chip, device, rec); if (err < 0) return err; device++; @@ -806,20 +806,20 @@ static int __devinit snd_intel8x0_pcm(struct intel8x0m *chip) * Mixer part */ -static void snd_intel8x0_mixer_free_ac97_bus(struct snd_ac97_bus *bus) +static void snd_intel8x0m_mixer_free_ac97_bus(struct snd_ac97_bus *bus) { struct intel8x0m *chip = bus->private_data; chip->ac97_bus = NULL; } -static void snd_intel8x0_mixer_free_ac97(struct snd_ac97 *ac97) +static void snd_intel8x0m_mixer_free_ac97(struct snd_ac97 *ac97) { struct intel8x0m *chip = ac97->private_data; chip->ac97 = NULL; } -static int __devinit snd_intel8x0_mixer(struct intel8x0m *chip, int ac97_clock) +static int __devinit snd_intel8x0m_mixer(struct intel8x0m *chip, int ac97_clock) { struct snd_ac97_bus *pbus; struct snd_ac97_template ac97; @@ -827,22 +827,22 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0m *chip, int ac97_clock) int err; unsigned int glob_sta = 0; static struct snd_ac97_bus_ops ops = { - .write = snd_intel8x0_codec_write, - .read = snd_intel8x0_codec_read, + .write = snd_intel8x0m_codec_write, + .read = snd_intel8x0m_codec_read, }; chip->in_ac97_init = 1; memset(&ac97, 0, sizeof(ac97)); ac97.private_data = chip; - ac97.private_free = snd_intel8x0_mixer_free_ac97; + ac97.private_free = snd_intel8x0m_mixer_free_ac97; ac97.scaps = AC97_SCAP_SKIP_AUDIO | AC97_SCAP_POWER_SAVE; glob_sta = igetdword(chip, ICHREG(GLOB_STA)); if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus)) < 0) goto __err; - pbus->private_free = snd_intel8x0_mixer_free_ac97_bus; + pbus->private_free = snd_intel8x0m_mixer_free_ac97_bus; if (ac97_clock >= 8000 && ac97_clock <= 48000) pbus->clock = ac97_clock; chip->ac97_bus = pbus; @@ -894,7 +894,8 @@ static int snd_intel8x0m_ich_chip_init(struct intel8x0m *chip, int probing) /* finish cold or do warm reset */ cnt |= (cnt & ICH_AC97COLD) == 0 ? ICH_AC97COLD : ICH_AC97WARM; iputdword(chip, ICHREG(GLOB_CNT), cnt); - end_time = (jiffies + (HZ / 4)) + 1; + usleep_range(500, 1000); /* give warm reset some time */ + end_time = jiffies + HZ / 4; do { if ((igetdword(chip, ICHREG(GLOB_CNT)) & ICH_AC97WARM) == 0) goto __ok; @@ -959,7 +960,7 @@ static int snd_intel8x0m_ich_chip_init(struct intel8x0m *chip, int probing) return 0; } -static int snd_intel8x0_chip_init(struct intel8x0m *chip, int probing) +static int snd_intel8x0m_chip_init(struct intel8x0m *chip, int probing) { unsigned int i; int err; @@ -980,7 +981,7 @@ static int snd_intel8x0_chip_init(struct intel8x0m *chip, int probing) return 0; } -static int snd_intel8x0_free(struct intel8x0m *chip) +static int snd_intel8x0m_free(struct intel8x0m *chip) { unsigned int i; @@ -1045,7 +1046,7 @@ static int intel8x0m_resume(struct pci_dev *pci) return -EIO; } pci_set_master(pci); - if (request_irq(pci->irq, snd_intel8x0_interrupt, + if (request_irq(pci->irq, snd_intel8x0m_interrupt, IRQF_SHARED, card->shortname, chip)) { printk(KERN_ERR "intel8x0m: unable to grab IRQ %d, " "disabling device\n", pci->irq); @@ -1053,7 +1054,7 @@ static int intel8x0m_resume(struct pci_dev *pci) return -EIO; } chip->irq = pci->irq; - snd_intel8x0_chip_init(chip, 0); + snd_intel8x0m_chip_init(chip, 0); snd_ac97_resume(chip->ac97); snd_power_change_state(card, SNDRV_CTL_POWER_D0); @@ -1094,10 +1095,10 @@ static void __devinit snd_intel8x0m_proc_init(struct intel8x0m * chip) #endif /* CONFIG_PROC_FS */ -static int snd_intel8x0_dev_free(struct snd_device *device) +static int snd_intel8x0m_dev_free(struct snd_device *device) { struct intel8x0m *chip = device->device_data; - return snd_intel8x0_free(chip); + return snd_intel8x0m_free(chip); } struct ich_reg_info { @@ -1108,7 +1109,7 @@ struct ich_reg_info { static int __devinit snd_intel8x0m_create(struct snd_card *card, struct pci_dev *pci, unsigned long device_type, - struct intel8x0m ** r_intel8x0) + struct intel8x0m **r_intel8x0m) { struct intel8x0m *chip; int err; @@ -1116,7 +1117,7 @@ static int __devinit snd_intel8x0m_create(struct snd_card *card, unsigned int int_sta_masks; struct ichdev *ichdev; static struct snd_device_ops ops = { - .dev_free = snd_intel8x0_dev_free, + .dev_free = snd_intel8x0m_dev_free, }; static struct ich_reg_info intel_regs[2] = { { ICH_MIINT, 0 }, @@ -1124,7 +1125,7 @@ static int __devinit snd_intel8x0m_create(struct snd_card *card, }; struct ich_reg_info *tbl; - *r_intel8x0 = NULL; + *r_intel8x0m = NULL; if ((err = pci_enable_device(pci)) < 0) return err; @@ -1158,7 +1159,7 @@ static int __devinit snd_intel8x0m_create(struct snd_card *card, chip->addr = pci_iomap(pci, 0, 0); if (!chip->addr) { snd_printk(KERN_ERR "AC'97 space ioremap problem\n"); - snd_intel8x0_free(chip); + snd_intel8x0m_free(chip); return -EIO; } if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) /* ICH4 */ @@ -1167,15 +1168,15 @@ static int __devinit snd_intel8x0m_create(struct snd_card *card, chip->bmaddr = pci_iomap(pci, 1, 0); if (!chip->bmaddr) { snd_printk(KERN_ERR "Controller space ioremap problem\n"); - snd_intel8x0_free(chip); + snd_intel8x0m_free(chip); return -EIO; } port_inited: - if (request_irq(pci->irq, snd_intel8x0_interrupt, IRQF_SHARED, + if (request_irq(pci->irq, snd_intel8x0m_interrupt, IRQF_SHARED, card->shortname, chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); - snd_intel8x0_free(chip); + snd_intel8x0m_free(chip); return -EBUSY; } chip->irq = pci->irq; @@ -1210,7 +1211,7 @@ static int __devinit snd_intel8x0m_create(struct snd_card *card, if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2, &chip->bdbars) < 0) { - snd_intel8x0_free(chip); + snd_intel8x0m_free(chip); return -ENOMEM; } /* tables must be aligned to 8 bytes here, but the kernel pages @@ -1225,19 +1226,19 @@ static int __devinit snd_intel8x0m_create(struct snd_card *card, chip->int_sta_reg = ICH_REG_GLOB_STA; chip->int_sta_mask = int_sta_masks; - if ((err = snd_intel8x0_chip_init(chip, 1)) < 0) { - snd_intel8x0_free(chip); + if ((err = snd_intel8x0m_chip_init(chip, 1)) < 0) { + snd_intel8x0m_free(chip); return err; } if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { - snd_intel8x0_free(chip); + snd_intel8x0m_free(chip); return err; } snd_card_set_dev(card, &pci->dev); - *r_intel8x0 = chip; + *r_intel8x0m = chip; return 0; } @@ -1295,11 +1296,11 @@ static int __devinit snd_intel8x0m_probe(struct pci_dev *pci, } card->private_data = chip; - if ((err = snd_intel8x0_mixer(chip, ac97_clock)) < 0) { + if ((err = snd_intel8x0m_mixer(chip, ac97_clock)) < 0) { snd_card_free(card); return err; } - if ((err = snd_intel8x0_pcm(chip)) < 0) { + if ((err = snd_intel8x0m_pcm(chip)) < 0) { snd_card_free(card); return err; } diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index f5eadfc0672a..a323eafb9e03 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -8,6 +8,21 @@ * Modified 2006-06-01 for AES32 support by Remy Bruno * <remy.bruno@trinnov.com> * + * Modified 2009-04-13 for proper metering by Florian Faber + * <faber@faberman.de> + * + * Modified 2009-04-14 for native float support by Florian Faber + * <faber@faberman.de> + * + * Modified 2009-04-26 fixed bug in rms metering by Florian Faber + * <faber@faberman.de> + * + * Modified 2009-04-30 added hw serial number support by Florian Faber + * + * Modified 2011-01-14 added S/PDIF input on RayDATs by Adrian Knoth + * + * Modified 2011-01-25 variable period sizes on RayDAT/AIO by Adrian Knoth + * * 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 @@ -35,6 +50,7 @@ #include <sound/core.h> #include <sound/control.h> #include <sound/pcm.h> +#include <sound/pcm_params.h> #include <sound/info.h> #include <sound/asoundef.h> #include <sound/rawmidi.h> @@ -47,15 +63,6 @@ static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */ -/* Disable precise pointer at start */ -static int precise_ptr[SNDRV_CARDS]; - -/* Send all playback to line outs */ -static int line_outs_monitor[SNDRV_CARDS]; - -/* Enable Analog Outs on Channel 63/64 by default */ -static int enable_monitor[SNDRV_CARDS]; - module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for RME HDSPM interface."); @@ -65,42 +72,39 @@ MODULE_PARM_DESC(id, "ID string for RME HDSPM interface."); module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable/disable specific HDSPM soundcards."); -module_param_array(precise_ptr, bool, NULL, 0444); -MODULE_PARM_DESC(precise_ptr, "Enable or disable precise pointer."); - -module_param_array(line_outs_monitor, bool, NULL, 0444); -MODULE_PARM_DESC(line_outs_monitor, - "Send playback streams to analog outs by default."); - -module_param_array(enable_monitor, bool, NULL, 0444); -MODULE_PARM_DESC(enable_monitor, - "Enable Analog Out on Channel 63/64 by default."); MODULE_AUTHOR - ("Winfried Ritsch <ritsch_AT_iem.at>, " - "Paul Davis <paul@linuxaudiosystems.com>, " - "Marcus Andersson, Thomas Charbonnel <thomas@undata.org>, " - "Remy Bruno <remy.bruno@trinnov.com>"); +( + "Winfried Ritsch <ritsch_AT_iem.at>, " + "Paul Davis <paul@linuxaudiosystems.com>, " + "Marcus Andersson, Thomas Charbonnel <thomas@undata.org>, " + "Remy Bruno <remy.bruno@trinnov.com>, " + "Florian Faber <faberman@linuxproaudio.org>, " + "Adrian Knoth <adi@drcomp.erfurt.thur.de>" +); MODULE_DESCRIPTION("RME HDSPM"); MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); -/* --- Write registers. --- +/* --- Write registers. --- These are defined as byte-offsets from the iobase value. */ +#define HDSPM_WR_SETTINGS 0 +#define HDSPM_outputBufferAddress 32 +#define HDSPM_inputBufferAddress 36 #define HDSPM_controlRegister 64 #define HDSPM_interruptConfirmation 96 #define HDSPM_control2Reg 256 /* not in specs ???????? */ #define HDSPM_freqReg 256 /* for AES32 */ -#define HDSPM_midiDataOut0 352 /* just believe in old code */ -#define HDSPM_midiDataOut1 356 +#define HDSPM_midiDataOut0 352 /* just believe in old code */ +#define HDSPM_midiDataOut1 356 #define HDSPM_eeprom_wr 384 /* for AES32 */ /* DMA enable for 64 channels, only Bit 0 is relevant */ -#define HDSPM_outputEnableBase 512 /* 512-767 input DMA */ +#define HDSPM_outputEnableBase 512 /* 512-767 input DMA */ #define HDSPM_inputEnableBase 768 /* 768-1023 output DMA */ -/* 16 page addresses for each of the 64 channels DMA buffer in and out +/* 16 page addresses for each of the 64 channels DMA buffer in and out (each 64k=16*4k) Buffer must be 4k aligned (which is default i386 ????) */ #define HDSPM_pageAddressBufferOut 8192 #define HDSPM_pageAddressBufferIn (HDSPM_pageAddressBufferOut+64*16*4) @@ -119,22 +123,84 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_statusRegister2 192 #define HDSPM_timecodeRegister 128 +/* AIO, RayDAT */ +#define HDSPM_RD_STATUS_0 0 +#define HDSPM_RD_STATUS_1 64 +#define HDSPM_RD_STATUS_2 128 +#define HDSPM_RD_STATUS_3 192 + +#define HDSPM_RD_TCO 256 +#define HDSPM_RD_PLL_FREQ 512 +#define HDSPM_WR_TCO 128 + +#define HDSPM_TCO1_TCO_lock 0x00000001 +#define HDSPM_TCO1_WCK_Input_Range_LSB 0x00000002 +#define HDSPM_TCO1_WCK_Input_Range_MSB 0x00000004 +#define HDSPM_TCO1_LTC_Input_valid 0x00000008 +#define HDSPM_TCO1_WCK_Input_valid 0x00000010 +#define HDSPM_TCO1_Video_Input_Format_NTSC 0x00000020 +#define HDSPM_TCO1_Video_Input_Format_PAL 0x00000040 + +#define HDSPM_TCO1_set_TC 0x00000100 +#define HDSPM_TCO1_set_drop_frame_flag 0x00000200 +#define HDSPM_TCO1_LTC_Format_LSB 0x00000400 +#define HDSPM_TCO1_LTC_Format_MSB 0x00000800 + +#define HDSPM_TCO2_TC_run 0x00010000 +#define HDSPM_TCO2_WCK_IO_ratio_LSB 0x00020000 +#define HDSPM_TCO2_WCK_IO_ratio_MSB 0x00040000 +#define HDSPM_TCO2_set_num_drop_frames_LSB 0x00080000 +#define HDSPM_TCO2_set_num_drop_frames_MSB 0x00100000 +#define HDSPM_TCO2_set_jam_sync 0x00200000 +#define HDSPM_TCO2_set_flywheel 0x00400000 + +#define HDSPM_TCO2_set_01_4 0x01000000 +#define HDSPM_TCO2_set_pull_down 0x02000000 +#define HDSPM_TCO2_set_pull_up 0x04000000 +#define HDSPM_TCO2_set_freq 0x08000000 +#define HDSPM_TCO2_set_term_75R 0x10000000 +#define HDSPM_TCO2_set_input_LSB 0x20000000 +#define HDSPM_TCO2_set_input_MSB 0x40000000 +#define HDSPM_TCO2_set_freq_from_app 0x80000000 + + +#define HDSPM_midiDataOut0 352 +#define HDSPM_midiDataOut1 356 +#define HDSPM_midiDataOut2 368 + #define HDSPM_midiDataIn0 360 #define HDSPM_midiDataIn1 364 +#define HDSPM_midiDataIn2 372 +#define HDSPM_midiDataIn3 376 /* status is data bytes in MIDI-FIFO (0-128) */ -#define HDSPM_midiStatusOut0 384 -#define HDSPM_midiStatusOut1 388 -#define HDSPM_midiStatusIn0 392 -#define HDSPM_midiStatusIn1 396 +#define HDSPM_midiStatusOut0 384 +#define HDSPM_midiStatusOut1 388 +#define HDSPM_midiStatusOut2 400 + +#define HDSPM_midiStatusIn0 392 +#define HDSPM_midiStatusIn1 396 +#define HDSPM_midiStatusIn2 404 +#define HDSPM_midiStatusIn3 408 /* the meters are regular i/o-mapped registers, but offset considerably from the rest. the peak registers are reset - when read; the least-significant 4 bits are full-scale counters; + when read; the least-significant 4 bits are full-scale counters; the actual peak value is in the most-significant 24 bits. */ -#define HDSPM_MADI_peakrmsbase 4096 /* 4096-8191 2x64x32Bit Meters */ + +#define HDSPM_MADI_INPUT_PEAK 4096 +#define HDSPM_MADI_PLAYBACK_PEAK 4352 +#define HDSPM_MADI_OUTPUT_PEAK 4608 + +#define HDSPM_MADI_INPUT_RMS_L 6144 +#define HDSPM_MADI_PLAYBACK_RMS_L 6400 +#define HDSPM_MADI_OUTPUT_RMS_L 6656 + +#define HDSPM_MADI_INPUT_RMS_H 7168 +#define HDSPM_MADI_PLAYBACK_RMS_H 7424 +#define HDSPM_MADI_OUTPUT_RMS_H 7680 /* --- Control Register bits --------- */ #define HDSPM_Start (1<<0) /* start engine */ @@ -143,7 +209,9 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_Latency1 (1<<2) /* where n is defined */ #define HDSPM_Latency2 (1<<3) /* by Latency{2,1,0} */ -#define HDSPM_ClockModeMaster (1<<4) /* 1=Master, 0=Slave/Autosync */ +#define HDSPM_ClockModeMaster (1<<4) /* 1=Master, 0=Autosync */ +#define HDSPM_c0Master 0x1 /* Master clock bit in settings + register [RayDAT, AIO] */ #define HDSPM_AudioInterruptEnable (1<<5) /* what do you think ? */ @@ -157,7 +225,7 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); 56channelMODE=0 */ /* MADI ONLY*/ #define HDSPM_Emphasis (1<<10) /* Emphasis */ /* AES32 ONLY */ -#define HDSPM_AutoInp (1<<11) /* Auto Input (takeover) == Safe Mode, +#define HDSPM_AutoInp (1<<11) /* Auto Input (takeover) == Safe Mode, 0=off, 1=on */ /* MADI ONLY */ #define HDSPM_Dolby (1<<11) /* Dolby = "NonAudio" ?? */ /* AES32 ONLY */ @@ -166,22 +234,23 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); */ #define HDSPM_InputSelect1 (1<<15) /* should be 0 */ -#define HDSPM_SyncRef0 (1<<16) /* 0=WOrd, 1=MADI */ -#define HDSPM_SyncRef1 (1<<17) /* for AES32: SyncRefN codes the AES # */ #define HDSPM_SyncRef2 (1<<13) #define HDSPM_SyncRef3 (1<<25) #define HDSPM_SMUX (1<<18) /* Frame ??? */ /* MADI ONY */ -#define HDSPM_clr_tms (1<<19) /* clear track marker, do not use +#define HDSPM_clr_tms (1<<19) /* clear track marker, do not use AES additional bits in lower 5 Audiodatabits ??? */ #define HDSPM_taxi_reset (1<<20) /* ??? */ /* MADI ONLY ? */ #define HDSPM_WCK48 (1<<20) /* Frame ??? = HDSPM_SMUX */ /* AES32 ONLY */ -#define HDSPM_Midi0InterruptEnable (1<<22) -#define HDSPM_Midi1InterruptEnable (1<<23) +#define HDSPM_Midi0InterruptEnable 0x0400000 +#define HDSPM_Midi1InterruptEnable 0x0800000 +#define HDSPM_Midi2InterruptEnable 0x0200000 +#define HDSPM_Midi3InterruptEnable 0x4000000 #define HDSPM_LineOut (1<<24) /* Analog Out on channel 63/64 on=1, mute=0 */ +#define HDSPe_FLOAT_FORMAT 0x2000000 #define HDSPM_DS_DoubleWire (1<<26) /* AES32 ONLY */ #define HDSPM_QS_DoubleWire (1<<27) /* AES32 ONLY */ @@ -198,11 +267,18 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_InputCoaxial (HDSPM_InputSelect0) #define HDSPM_SyncRefMask (HDSPM_SyncRef0|HDSPM_SyncRef1|\ HDSPM_SyncRef2|HDSPM_SyncRef3) -#define HDSPM_SyncRef_Word 0 -#define HDSPM_SyncRef_MADI (HDSPM_SyncRef0) -#define HDSPM_SYNC_FROM_WORD 0 /* Preferred sync reference */ -#define HDSPM_SYNC_FROM_MADI 1 /* choices - used by "pref_sync_ref" */ +#define HDSPM_c0_SyncRef0 0x2 +#define HDSPM_c0_SyncRef1 0x4 +#define HDSPM_c0_SyncRef2 0x8 +#define HDSPM_c0_SyncRef3 0x10 +#define HDSPM_c0_SyncRefMask (HDSPM_c0_SyncRef0 | HDSPM_c0_SyncRef1 |\ + HDSPM_c0_SyncRef2 | HDSPM_c0_SyncRef3) + +#define HDSPM_SYNC_FROM_WORD 0 /* Preferred sync reference */ +#define HDSPM_SYNC_FROM_MADI 1 /* choices - used by "pref_sync_ref" */ +#define HDSPM_SYNC_FROM_TCO 2 +#define HDSPM_SYNC_FROM_SYNC_IN 3 #define HDSPM_Frequency32KHz HDSPM_Frequency0 #define HDSPM_Frequency44_1KHz HDSPM_Frequency1 @@ -216,17 +292,6 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_Frequency192KHz (HDSPM_QuadSpeed|HDSPM_Frequency1|\ HDSPM_Frequency0) -/* --- for internal discrimination */ -#define HDSPM_CLOCK_SOURCE_AUTOSYNC 0 /* Sample Clock Sources */ -#define HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ 1 -#define HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ 2 -#define HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ 3 -#define HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ 4 -#define HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ 5 -#define HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ 6 -#define HDSPM_CLOCK_SOURCE_INTERNAL_128KHZ 7 -#define HDSPM_CLOCK_SOURCE_INTERNAL_176_4KHZ 8 -#define HDSPM_CLOCK_SOURCE_INTERNAL_192KHZ 9 /* Synccheck Status */ #define HDSPM_SYNC_CHECK_NO_LOCK 0 @@ -236,14 +301,16 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); /* AutoSync References - used by "autosync_ref" control switch */ #define HDSPM_AUTOSYNC_FROM_WORD 0 #define HDSPM_AUTOSYNC_FROM_MADI 1 -#define HDSPM_AUTOSYNC_FROM_NONE 2 +#define HDSPM_AUTOSYNC_FROM_TCO 2 +#define HDSPM_AUTOSYNC_FROM_SYNC_IN 3 +#define HDSPM_AUTOSYNC_FROM_NONE 4 /* Possible sources of MADI input */ #define HDSPM_OPTICAL 0 /* optical */ #define HDSPM_COAXIAL 1 /* BNC */ #define hdspm_encode_latency(x) (((x)<<1) & HDSPM_LatencyMask) -#define hdspm_decode_latency(x) (((x) & HDSPM_LatencyMask)>>1) +#define hdspm_decode_latency(x) ((((x) & HDSPM_LatencyMask)>>1)) #define hdspm_encode_in(x) (((x)&0x3)<<14) #define hdspm_decode_in(x) (((x)>>14)&0x3) @@ -270,13 +337,21 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_AB_int (1<<2) /* InputChannel Opt=0, Coax=1 * (like inp0) */ + #define HDSPM_madiLock (1<<3) /* MADI Locked =1, no=0 */ +#define HDSPM_madiSync (1<<18) /* MADI is in sync */ + +#define HDSPM_tcoLock 0x00000020 /* Optional TCO locked status FOR HDSPe MADI! */ +#define HDSPM_tcoSync 0x10000000 /* Optional TCO sync status */ + +#define HDSPM_syncInLock 0x00010000 /* Sync In lock status FOR HDSPe MADI! */ +#define HDSPM_syncInSync 0x00020000 /* Sync In sync status FOR HDSPe MADI! */ #define HDSPM_BufferPositionMask 0x000FFC0 /* Bit 6..15 : h/w buffer pointer */ - /* since 64byte accurate last 6 bits - are not used */ + /* since 64byte accurate, last 6 bits are not used */ + + -#define HDSPM_madiSync (1<<18) /* MADI is in sync */ #define HDSPM_DoubleSpeedStatus (1<<19) /* (input) card in double speed */ #define HDSPM_madiFreq0 (1<<22) /* system freq 0=error */ @@ -287,8 +362,19 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_BufferID (1<<26) /* (Double)Buffer ID toggles with * Interrupt */ -#define HDSPM_midi0IRQPending (1<<30) /* MIDI IRQ is pending */ -#define HDSPM_midi1IRQPending (1<<31) /* and aktiv */ +#define HDSPM_tco_detect 0x08000000 +#define HDSPM_tco_lock 0x20000000 + +#define HDSPM_s2_tco_detect 0x00000040 +#define HDSPM_s2_AEBO_D 0x00000080 +#define HDSPM_s2_AEBI_D 0x00000100 + + +#define HDSPM_midi0IRQPending 0x40000000 +#define HDSPM_midi1IRQPending 0x80000000 +#define HDSPM_midi2IRQPending 0x20000000 +#define HDSPM_midi2IRQPendingAES 0x00000020 +#define HDSPM_midi3IRQPending 0x00200000 /* --- status bit helpers */ #define HDSPM_madiFreqMask (HDSPM_madiFreq0|HDSPM_madiFreq1|\ @@ -317,7 +403,10 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_wc_freq2 (1<<7) /* 100=64, 101=88.2, 110=96, */ /* missing Bit for 111=128, 1000=176.4, 1001=192 */ -#define HDSPM_SelSyncRef0 (1<<8) /* Sync Source in slave mode */ +#define HDSPM_SyncRef0 0x10000 /* Sync Reference */ +#define HDSPM_SyncRef1 0x20000 + +#define HDSPM_SelSyncRef0 (1<<8) /* AutoSync Source */ #define HDSPM_SelSyncRef1 (1<<9) /* 000=word, 001=MADI, */ #define HDSPM_SelSyncRef2 (1<<10) /* 111=no valid signal */ @@ -331,11 +420,19 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define HDSPM_wcFreq88_2 (HDSPM_wc_freq0|HDSPM_wc_freq2) #define HDSPM_wcFreq96 (HDSPM_wc_freq1|HDSPM_wc_freq2) +#define HDSPM_status1_F_0 0x0400000 +#define HDSPM_status1_F_1 0x0800000 +#define HDSPM_status1_F_2 0x1000000 +#define HDSPM_status1_F_3 0x2000000 +#define HDSPM_status1_freqMask (HDSPM_status1_F_0|HDSPM_status1_F_1|HDSPM_status1_F_2|HDSPM_status1_F_3) + #define HDSPM_SelSyncRefMask (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|\ HDSPM_SelSyncRef2) #define HDSPM_SelSyncRef_WORD 0 #define HDSPM_SelSyncRef_MADI (HDSPM_SelSyncRef0) +#define HDSPM_SelSyncRef_TCO (HDSPM_SelSyncRef1) +#define HDSPM_SelSyncRef_SyncIn (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1) #define HDSPM_SelSyncRef_NVALID (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|\ HDSPM_SelSyncRef2) @@ -345,7 +442,7 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); /* status */ #define HDSPM_AES32_wcLock 0x0200000 #define HDSPM_AES32_wcFreq_bit 22 -/* (status >> HDSPM_AES32_wcFreq_bit) & 0xF gives WC frequency (cf function +/* (status >> HDSPM_AES32_wcFreq_bit) & 0xF gives WC frequency (cf function HDSPM_bit2freq */ #define HDSPM_AES32_syncref_bit 16 /* (status >> HDSPM_AES32_syncref_bit) & 0xF gives sync source */ @@ -398,28 +495,348 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}"); #define MADI_DS_CHANNELS 32 #define MADI_QS_CHANNELS 16 +#define RAYDAT_SS_CHANNELS 36 +#define RAYDAT_DS_CHANNELS 20 +#define RAYDAT_QS_CHANNELS 12 + +#define AIO_IN_SS_CHANNELS 14 +#define AIO_IN_DS_CHANNELS 10 +#define AIO_IN_QS_CHANNELS 8 +#define AIO_OUT_SS_CHANNELS 16 +#define AIO_OUT_DS_CHANNELS 12 +#define AIO_OUT_QS_CHANNELS 10 + +#define AES32_CHANNELS 16 + /* the size of a substream (1 mono data stream) */ #define HDSPM_CHANNEL_BUFFER_SAMPLES (16*1024) #define HDSPM_CHANNEL_BUFFER_BYTES (4*HDSPM_CHANNEL_BUFFER_SAMPLES) /* the size of the area we need to allocate for DMA transfers. the size is the same regardless of the number of channels, and - also the latency to use. + also the latency to use. for one direction !!! */ #define HDSPM_DMA_AREA_BYTES (HDSPM_MAX_CHANNELS * HDSPM_CHANNEL_BUFFER_BYTES) #define HDSPM_DMA_AREA_KILOBYTES (HDSPM_DMA_AREA_BYTES/1024) /* revisions >= 230 indicate AES32 card */ -#define HDSPM_AESREVISION 230 +#define HDSPM_MADI_REV 210 +#define HDSPM_RAYDAT_REV 211 +#define HDSPM_AIO_REV 212 +#define HDSPM_MADIFACE_REV 213 +#define HDSPM_AES_REV 240 +#define HDSPM_AES32_REV 234 +#define HDSPM_AES32_OLD_REV 233 /* speed factor modes */ #define HDSPM_SPEED_SINGLE 0 #define HDSPM_SPEED_DOUBLE 1 #define HDSPM_SPEED_QUAD 2 + /* names for speed modes */ static char *hdspm_speed_names[] = { "single", "double", "quad" }; +static char *texts_autosync_aes_tco[] = { "Word Clock", + "AES1", "AES2", "AES3", "AES4", + "AES5", "AES6", "AES7", "AES8", + "TCO" }; +static char *texts_autosync_aes[] = { "Word Clock", + "AES1", "AES2", "AES3", "AES4", + "AES5", "AES6", "AES7", "AES8" }; +static char *texts_autosync_madi_tco[] = { "Word Clock", + "MADI", "TCO", "Sync In" }; +static char *texts_autosync_madi[] = { "Word Clock", + "MADI", "Sync In" }; + +static char *texts_autosync_raydat_tco[] = { + "Word Clock", + "ADAT 1", "ADAT 2", "ADAT 3", "ADAT 4", + "AES", "SPDIF", "TCO", "Sync In" +}; +static char *texts_autosync_raydat[] = { + "Word Clock", + "ADAT 1", "ADAT 2", "ADAT 3", "ADAT 4", + "AES", "SPDIF", "Sync In" +}; +static char *texts_autosync_aio_tco[] = { + "Word Clock", + "ADAT", "AES", "SPDIF", "TCO", "Sync In" +}; +static char *texts_autosync_aio[] = { "Word Clock", + "ADAT", "AES", "SPDIF", "Sync In" }; + +static char *texts_freq[] = { + "No Lock", + "32 kHz", + "44.1 kHz", + "48 kHz", + "64 kHz", + "88.2 kHz", + "96 kHz", + "128 kHz", + "176.4 kHz", + "192 kHz" +}; + +static char *texts_ports_madi[] = { + "MADI.1", "MADI.2", "MADI.3", "MADI.4", "MADI.5", "MADI.6", + "MADI.7", "MADI.8", "MADI.9", "MADI.10", "MADI.11", "MADI.12", + "MADI.13", "MADI.14", "MADI.15", "MADI.16", "MADI.17", "MADI.18", + "MADI.19", "MADI.20", "MADI.21", "MADI.22", "MADI.23", "MADI.24", + "MADI.25", "MADI.26", "MADI.27", "MADI.28", "MADI.29", "MADI.30", + "MADI.31", "MADI.32", "MADI.33", "MADI.34", "MADI.35", "MADI.36", + "MADI.37", "MADI.38", "MADI.39", "MADI.40", "MADI.41", "MADI.42", + "MADI.43", "MADI.44", "MADI.45", "MADI.46", "MADI.47", "MADI.48", + "MADI.49", "MADI.50", "MADI.51", "MADI.52", "MADI.53", "MADI.54", + "MADI.55", "MADI.56", "MADI.57", "MADI.58", "MADI.59", "MADI.60", + "MADI.61", "MADI.62", "MADI.63", "MADI.64", +}; + + +static char *texts_ports_raydat_ss[] = { + "ADAT1.1", "ADAT1.2", "ADAT1.3", "ADAT1.4", "ADAT1.5", "ADAT1.6", + "ADAT1.7", "ADAT1.8", "ADAT2.1", "ADAT2.2", "ADAT2.3", "ADAT2.4", + "ADAT2.5", "ADAT2.6", "ADAT2.7", "ADAT2.8", "ADAT3.1", "ADAT3.2", + "ADAT3.3", "ADAT3.4", "ADAT3.5", "ADAT3.6", "ADAT3.7", "ADAT3.8", + "ADAT4.1", "ADAT4.2", "ADAT4.3", "ADAT4.4", "ADAT4.5", "ADAT4.6", + "ADAT4.7", "ADAT4.8", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R" +}; + +static char *texts_ports_raydat_ds[] = { + "ADAT1.1", "ADAT1.2", "ADAT1.3", "ADAT1.4", + "ADAT2.1", "ADAT2.2", "ADAT2.3", "ADAT2.4", + "ADAT3.1", "ADAT3.2", "ADAT3.3", "ADAT3.4", + "ADAT4.1", "ADAT4.2", "ADAT4.3", "ADAT4.4", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R" +}; + +static char *texts_ports_raydat_qs[] = { + "ADAT1.1", "ADAT1.2", + "ADAT2.1", "ADAT2.2", + "ADAT3.1", "ADAT3.2", + "ADAT4.1", "ADAT4.2", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R" +}; + + +static char *texts_ports_aio_in_ss[] = { + "Analogue.L", "Analogue.R", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R", + "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", "ADAT.5", "ADAT.6", + "ADAT.7", "ADAT.8" +}; + +static char *texts_ports_aio_out_ss[] = { + "Analogue.L", "Analogue.R", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R", + "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", "ADAT.5", "ADAT.6", + "ADAT.7", "ADAT.8", + "Phone.L", "Phone.R" +}; + +static char *texts_ports_aio_in_ds[] = { + "Analogue.L", "Analogue.R", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R", + "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4" +}; + +static char *texts_ports_aio_out_ds[] = { + "Analogue.L", "Analogue.R", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R", + "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", + "Phone.L", "Phone.R" +}; + +static char *texts_ports_aio_in_qs[] = { + "Analogue.L", "Analogue.R", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R", + "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4" +}; + +static char *texts_ports_aio_out_qs[] = { + "Analogue.L", "Analogue.R", + "AES.L", "AES.R", + "SPDIF.L", "SPDIF.R", + "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", + "Phone.L", "Phone.R" +}; + +static char *texts_ports_aes32[] = { + "AES.1", "AES.2", "AES.3", "AES.4", "AES.5", "AES.6", "AES.7", + "AES.8", "AES.9.", "AES.10", "AES.11", "AES.12", "AES.13", "AES.14", + "AES.15", "AES.16" +}; + +/* These tables map the ALSA channels 1..N to the channels that we + need to use in order to find the relevant channel buffer. RME + refers to this kind of mapping as between "the ADAT channel and + the DMA channel." We index it using the logical audio channel, + and the value is the DMA channel (i.e. channel buffer number) + where the data for that channel can be read/written from/to. +*/ + +static char channel_map_unity_ss[HDSPM_MAX_CHANNELS] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63 +}; + +static char channel_map_raydat_ss[HDSPM_MAX_CHANNELS] = { + 4, 5, 6, 7, 8, 9, 10, 11, /* ADAT 1 */ + 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT 2 */ + 20, 21, 22, 23, 24, 25, 26, 27, /* ADAT 3 */ + 28, 29, 30, 31, 32, 33, 34, 35, /* ADAT 4 */ + 0, 1, /* AES */ + 2, 3, /* SPDIF */ + -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, -1, -1, +}; + +static char channel_map_raydat_ds[HDSPM_MAX_CHANNELS] = { + 4, 5, 6, 7, /* ADAT 1 */ + 8, 9, 10, 11, /* ADAT 2 */ + 12, 13, 14, 15, /* ADAT 3 */ + 16, 17, 18, 19, /* ADAT 4 */ + 0, 1, /* AES */ + 2, 3, /* SPDIF */ + -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, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static char channel_map_raydat_qs[HDSPM_MAX_CHANNELS] = { + 4, 5, /* ADAT 1 */ + 6, 7, /* ADAT 2 */ + 8, 9, /* ADAT 3 */ + 10, 11, /* ADAT 4 */ + 0, 1, /* AES */ + 2, 3, /* SPDIF */ + -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, -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, +}; + +static char channel_map_aio_in_ss[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line in */ + 8, 9, /* aes in, */ + 10, 11, /* spdif in */ + 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT in */ + -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, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static char channel_map_aio_out_ss[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line out */ + 8, 9, /* aes out */ + 10, 11, /* spdif out */ + 12, 13, 14, 15, 16, 17, 18, 19, /* ADAT out */ + 6, 7, /* phone out */ + -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, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static char channel_map_aio_in_ds[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line in */ + 8, 9, /* aes in */ + 10, 11, /* spdif in */ + 12, 14, 16, 18, /* adat in */ + -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, -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, -1, -1 +}; + +static char channel_map_aio_out_ds[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line out */ + 8, 9, /* aes out */ + 10, 11, /* spdif out */ + 12, 14, 16, 18, /* adat out */ + 6, 7, /* phone out */ + -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, -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 +}; + +static char channel_map_aio_in_qs[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line in */ + 8, 9, /* aes in */ + 10, 11, /* spdif in */ + 12, 16, /* adat in */ + -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, -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, -1, -1, -1, -1 +}; + +static char channel_map_aio_out_qs[HDSPM_MAX_CHANNELS] = { + 0, 1, /* line out */ + 8, 9, /* aes out */ + 10, 11, /* spdif out */ + 12, 16, /* adat out */ + 6, 7, /* phone out */ + -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, -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, -1, -1 +}; + +static char channel_map_aes32[HDSPM_MAX_CHANNELS] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + -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, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 +}; + struct hdspm_midi { struct hdspm *hdspm; int id; @@ -430,6 +847,21 @@ struct hdspm_midi { struct timer_list timer; spinlock_t lock; int pending; + int dataIn; + int statusIn; + int dataOut; + int statusOut; + int ie; + int irq; +}; + +struct hdspm_tco { + int input; + int framerate; + int wordclock; + int samplerate; + int pull; + int term; /* 0 = off, 1 = on */ }; struct hdspm { @@ -441,21 +873,39 @@ struct hdspm { char *card_name; /* for procinfo */ unsigned short firmware_rev; /* dont know if relevant (yes if AES32)*/ - unsigned char is_aes32; /* indicates if card is AES32 */ + uint8_t io_type; - int precise_ptr; /* use precise pointers, to be tested */ int monitor_outs; /* set up monitoring outs init flag */ u32 control_register; /* cached value */ u32 control2_register; /* cached value */ + u32 settings_register; - struct hdspm_midi midi[2]; + struct hdspm_midi midi[4]; struct tasklet_struct midi_tasklet; size_t period_bytes; - unsigned char ss_channels; /* channels of card in single speed */ - unsigned char ds_channels; /* Double Speed */ - unsigned char qs_channels; /* Quad Speed */ + unsigned char ss_in_channels; + unsigned char ds_in_channels; + unsigned char qs_in_channels; + unsigned char ss_out_channels; + unsigned char ds_out_channels; + unsigned char qs_out_channels; + + unsigned char max_channels_in; + unsigned char max_channels_out; + + char *channel_map_in; + char *channel_map_out; + + char *channel_map_in_ss, *channel_map_in_ds, *channel_map_in_qs; + char *channel_map_out_ss, *channel_map_out_ds, *channel_map_out_qs; + + char **port_names_in; + char **port_names_out; + + char **port_names_in_ss, **port_names_in_ds, **port_names_in_qs; + char **port_names_out_ss, **port_names_out_ds, **port_names_out_qs; unsigned char *playback_buffer; /* suitably aligned address */ unsigned char *capture_buffer; /* suitably aligned address */ @@ -468,14 +918,13 @@ struct hdspm { int last_internal_sample_rate; int system_sample_rate; - char *channel_map; /* channel map for DS and Quadspeed */ - int dev; /* Hardware vars... */ int irq; unsigned long port; void __iomem *iobase; int irq_count; /* for debug */ + int midiPorts; struct snd_card *card; /* one card */ struct snd_pcm *pcm; /* has one pcm */ @@ -487,28 +936,17 @@ struct hdspm { struct snd_kcontrol *playback_mixer_ctls[HDSPM_MAX_CHANNELS]; /* but input to much, so not used */ struct snd_kcontrol *input_mixer_ctls[HDSPM_MAX_CHANNELS]; - /* full mixer accessible over mixer ioctl or hwdep-device */ + /* full mixer accessable over mixer ioctl or hwdep-device */ struct hdspm_mixer *mixer; -}; + struct hdspm_tco *tco; /* NULL if no TCO detected */ -/* These tables map the ALSA channels 1..N to the channels that we - need to use in order to find the relevant channel buffer. RME - refer to this kind of mapping as between "the ADAT channel and - the DMA channel." We index it using the logical audio channel, - and the value is the DMA channel (i.e. channel buffer number) - where the data for that channel can be read/written from/to. -*/ + char **texts_autosync; + int texts_autosync_items; + + cycles_t last_interrupt; -static char channel_map_madi_ss[HDSPM_MAX_CHANNELS] = { - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63 + struct hdspm_peak_rms peak_rms; }; @@ -532,11 +970,11 @@ static int __devinit snd_hdspm_create_alsa_devices(struct snd_card *card, static int __devinit snd_hdspm_create_pcm(struct snd_card *card, struct hdspm * hdspm); -static inline void snd_hdspm_initialize_midi_flush(struct hdspm * hdspm); -static int hdspm_update_simple_mixer_controls(struct hdspm * hdspm); -static int hdspm_autosync_ref(struct hdspm * hdspm); -static int snd_hdspm_set_defaults(struct hdspm * hdspm); -static void hdspm_set_sgbuf(struct hdspm * hdspm, +static inline void snd_hdspm_initialize_midi_flush(struct hdspm *hdspm); +static int hdspm_update_simple_mixer_controls(struct hdspm *hdspm); +static int hdspm_autosync_ref(struct hdspm *hdspm); +static int snd_hdspm_set_defaults(struct hdspm *hdspm); +static void hdspm_set_sgbuf(struct hdspm *hdspm, struct snd_pcm_substream *substream, unsigned int reg, int channels); @@ -550,7 +988,7 @@ static inline int HDSPM_bit2freq(int n) return bit2freq_tab[n]; } -/* Write/read to/from HDSPM with Addresses in Bytes +/* Write/read to/from HDSPM with Adresses in Bytes not words but only 32Bit writes are allowed */ static inline void hdspm_write(struct hdspm * hdspm, unsigned int reg, @@ -564,8 +1002,8 @@ static inline unsigned int hdspm_read(struct hdspm * hdspm, unsigned int reg) return readl(hdspm->iobase + reg); } -/* for each output channel (chan) I have an Input (in) and Playback (pb) Fader - mixer is write only on hardware so we have to cache him for read +/* for each output channel (chan) I have an Input (in) and Playback (pb) Fader + mixer is write only on hardware so we have to cache him for read each fader is a u32, but uses only the first 16 bit */ static inline int hdspm_read_in_gain(struct hdspm * hdspm, unsigned int chan, @@ -641,30 +1079,67 @@ static int snd_hdspm_use_is_exclusive(struct hdspm *hdspm) /* check for external sample rate */ static int hdspm_external_sample_rate(struct hdspm *hdspm) { - if (hdspm->is_aes32) { - unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); - unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister); - unsigned int timecode = - hdspm_read(hdspm, HDSPM_timecodeRegister); + unsigned int status, status2, timecode; + int syncref, rate = 0, rate_bits; - int syncref = hdspm_autosync_ref(hdspm); + switch (hdspm->io_type) { + case AES32: + status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + status = hdspm_read(hdspm, HDSPM_statusRegister); + timecode = hdspm_read(hdspm, HDSPM_timecodeRegister); + + syncref = hdspm_autosync_ref(hdspm); if (syncref == HDSPM_AES32_AUTOSYNC_FROM_WORD && status & HDSPM_AES32_wcLock) - return HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) - & 0xF); + return HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) & 0xF); + if (syncref >= HDSPM_AES32_AUTOSYNC_FROM_AES1 && - syncref <= HDSPM_AES32_AUTOSYNC_FROM_AES8 && - status2 & (HDSPM_LockAES >> - (syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1))) - return HDSPM_bit2freq((timecode >> - (4*(syncref-HDSPM_AES32_AUTOSYNC_FROM_AES1))) & 0xF); + syncref <= HDSPM_AES32_AUTOSYNC_FROM_AES8 && + status2 & (HDSPM_LockAES >> + (syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1))) + return HDSPM_bit2freq((timecode >> (4*(syncref-HDSPM_AES32_AUTOSYNC_FROM_AES1))) & 0xF); return 0; - } else { - unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); - unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister); - unsigned int rate_bits; - int rate = 0; + break; + + case MADIface: + status = hdspm_read(hdspm, HDSPM_statusRegister); + + if (!(status & HDSPM_madiLock)) { + rate = 0; /* no lock */ + } else { + switch (status & (HDSPM_status1_freqMask)) { + case HDSPM_status1_F_0*1: + rate = 32000; break; + case HDSPM_status1_F_0*2: + rate = 44100; break; + case HDSPM_status1_F_0*3: + rate = 48000; break; + case HDSPM_status1_F_0*4: + rate = 64000; break; + case HDSPM_status1_F_0*5: + rate = 88200; break; + case HDSPM_status1_F_0*6: + rate = 96000; break; + case HDSPM_status1_F_0*7: + rate = 128000; break; + case HDSPM_status1_F_0*8: + rate = 176400; break; + case HDSPM_status1_F_0*9: + rate = 192000; break; + default: + rate = 0; break; + } + } + + break; + + case MADI: + case AIO: + case RayDAT: + status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + status = hdspm_read(hdspm, HDSPM_statusRegister); + rate = 0; /* if wordclock has synced freq and wordclock is valid */ if ((status2 & HDSPM_wcLock) != 0 && @@ -672,6 +1147,7 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm) rate_bits = status2 & HDSPM_wcFreqMask; + switch (rate_bits) { case HDSPM_wcFreq32: rate = 32000; @@ -691,7 +1167,6 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm) case HDSPM_wcFreq96: rate = 96000; break; - /* Quadspeed Bit missing ???? */ default: rate = 0; break; @@ -702,10 +1177,10 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm) * word has priority to MADI */ if (rate != 0 && - (status2 & HDSPM_SelSyncRefMask) == HDSPM_SelSyncRef_WORD) + (status2 & HDSPM_SelSyncRefMask) == HDSPM_SelSyncRef_WORD) return rate; - /* maby a madi input (which is taken if sel sync is madi) */ + /* maybe a madi input (which is taken if sel sync is madi) */ if (status & HDSPM_madiLock) { rate_bits = status & HDSPM_madiFreqMask; @@ -742,36 +1217,35 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm) break; } } - return rate; + break; } + + return rate; } /* Latency function */ -static inline void hdspm_compute_period_size(struct hdspm * hdspm) +static inline void hdspm_compute_period_size(struct hdspm *hdspm) { - hdspm->period_bytes = - 1 << ((hdspm_decode_latency(hdspm->control_register) + 8)); + hdspm->period_bytes = 1 << ((hdspm_decode_latency(hdspm->control_register) + 8)); } -static snd_pcm_uframes_t hdspm_hw_pointer(struct hdspm * hdspm) + +static snd_pcm_uframes_t hdspm_hw_pointer(struct hdspm *hdspm) { int position; position = hdspm_read(hdspm, HDSPM_statusRegister); - if (!hdspm->precise_ptr) - return (position & HDSPM_BufferID) ? + switch (hdspm->io_type) { + case RayDAT: + case AIO: + position &= HDSPM_BufferPositionMask; + position /= 4; /* Bytes per sample */ + break; + default: + position = (position & HDSPM_BufferID) ? (hdspm->period_bytes / 4) : 0; - - /* hwpointer comes in bytes and is 64Bytes accurate (by docu since - PCI Burst) - i have experimented that it is at most 64 Byte to much for playing - so substraction of 64 byte should be ok for ALSA, but use it only - for application where you know what you do since if you come to - near with record pointer it can be a disaster */ - - position &= HDSPM_BufferPositionMask; - position = ((position - 64) % (2 * hdspm->period_bytes)) / 4; + } return position; } @@ -805,7 +1279,7 @@ static void hdspm_silence_playback(struct hdspm *hdspm) } } -static int hdspm_set_interrupt_interval(struct hdspm * s, unsigned int frames) +static int hdspm_set_interrupt_interval(struct hdspm *s, unsigned int frames) { int n; @@ -829,21 +1303,53 @@ static int hdspm_set_interrupt_interval(struct hdspm * s, unsigned int frames) return 0; } +static u64 hdspm_calc_dds_value(struct hdspm *hdspm, u64 period) +{ + u64 freq_const; + + if (period == 0) + return 0; + + switch (hdspm->io_type) { + case MADI: + case AES32: + freq_const = 110069313433624ULL; + break; + case RayDAT: + case AIO: + freq_const = 104857600000000ULL; + break; + case MADIface: + freq_const = 131072000000000ULL; + } + + return div_u64(freq_const, period); +} + + static void hdspm_set_dds_value(struct hdspm *hdspm, int rate) { u64 n; - + if (rate >= 112000) rate /= 4; else if (rate >= 56000) rate /= 2; - /* RME says n = 104857600000000, but in the windows MADI driver, I see: -// return 104857600000000 / rate; // 100 MHz - return 110100480000000 / rate; // 105 MHz - */ - /* n = 104857600000000ULL; */ /* = 2^20 * 10^8 */ - n = 110100480000000ULL; /* Value checked for AES32 and MADI */ + switch (hdspm->io_type) { + case MADIface: + n = 131072000000000ULL; /* 125 MHz */ + break; + case MADI: + case AES32: + n = 110069313433624ULL; /* 105 MHz */ + break; + case RayDAT: + case AIO: + n = 104857600000000ULL; /* 100 MHz */ + break; + } + n = div_u64(n, rate); /* n should be less than 2^32 for being written to FREQ register */ snd_BUG_ON(n >> 32); @@ -864,13 +1370,13 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally) if (!(hdspm->control_register & HDSPM_ClockModeMaster)) { - /* SLAVE --- */ + /* SLAVE --- */ if (called_internally) { - /* request from ctl or card initialization - just make a warning an remember setting - for future master mode switching */ - + /* request from ctl or card initialization + just make a warning an remember setting + for future master mode switching */ + snd_printk(KERN_WARNING "HDSPM: " "Warning: device is not running " "as a clock master.\n"); @@ -907,7 +1413,7 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally) Note that a similar but essentially insoluble problem exists for externally-driven rate changes. All we can do is to flag rate - changes in the read/write routines. + changes in the read/write routines. */ if (current_rate <= 48000) @@ -975,16 +1481,35 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally) /* For AES32, need to set DDS value in FREQ register For MADI, also apparently */ hdspm_set_dds_value(hdspm, rate); - - if (hdspm->is_aes32 && rate != current_rate) + + if (AES32 == hdspm->io_type && rate != current_rate) hdspm_write(hdspm, HDSPM_eeprom_wr, 0); - - /* For AES32 and for MADI (at least rev 204), channel_map needs to - * always be channel_map_madi_ss, whatever the sample rate */ - hdspm->channel_map = channel_map_madi_ss; hdspm->system_sample_rate = rate; + if (rate <= 48000) { + hdspm->channel_map_in = hdspm->channel_map_in_ss; + hdspm->channel_map_out = hdspm->channel_map_out_ss; + hdspm->max_channels_in = hdspm->ss_in_channels; + hdspm->max_channels_out = hdspm->ss_out_channels; + hdspm->port_names_in = hdspm->port_names_in_ss; + hdspm->port_names_out = hdspm->port_names_out_ss; + } else if (rate <= 96000) { + hdspm->channel_map_in = hdspm->channel_map_in_ds; + hdspm->channel_map_out = hdspm->channel_map_out_ds; + hdspm->max_channels_in = hdspm->ds_in_channels; + hdspm->max_channels_out = hdspm->ds_out_channels; + hdspm->port_names_in = hdspm->port_names_in_ds; + hdspm->port_names_out = hdspm->port_names_out_ds; + } else { + hdspm->channel_map_in = hdspm->channel_map_in_qs; + hdspm->channel_map_out = hdspm->channel_map_out_qs; + hdspm->max_channels_in = hdspm->qs_in_channels; + hdspm->max_channels_out = hdspm->qs_out_channels; + hdspm->port_names_in = hdspm->port_names_in_qs; + hdspm->port_names_out = hdspm->port_names_out_qs; + } + if (not_set != 0) return -1; @@ -1019,39 +1544,26 @@ static inline unsigned char snd_hdspm_midi_read_byte (struct hdspm *hdspm, int id) { /* the hardware already does the relevant bit-mask with 0xff */ - if (id) - return hdspm_read(hdspm, HDSPM_midiDataIn1); - else - return hdspm_read(hdspm, HDSPM_midiDataIn0); + return hdspm_read(hdspm, hdspm->midi[id].dataIn); } static inline void snd_hdspm_midi_write_byte (struct hdspm *hdspm, int id, int val) { /* the hardware already does the relevant bit-mask with 0xff */ - if (id) - hdspm_write(hdspm, HDSPM_midiDataOut1, val); - else - hdspm_write(hdspm, HDSPM_midiDataOut0, val); + return hdspm_write(hdspm, hdspm->midi[id].dataOut, val); } static inline int snd_hdspm_midi_input_available (struct hdspm *hdspm, int id) { - if (id) - return (hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xff); - else - return (hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xff); + return hdspm_read(hdspm, hdspm->midi[id].statusIn) & 0xFF; } static inline int snd_hdspm_midi_output_possible (struct hdspm *hdspm, int id) { int fifo_bytes_used; - if (id) - fifo_bytes_used = hdspm_read(hdspm, HDSPM_midiStatusOut1); - else - fifo_bytes_used = hdspm_read(hdspm, HDSPM_midiStatusOut0); - fifo_bytes_used &= 0xff; + fifo_bytes_used = hdspm_read(hdspm, hdspm->midi[id].statusOut) & 0xFF; if (fifo_bytes_used < 128) return 128 - fifo_bytes_used; @@ -1074,7 +1586,7 @@ static int snd_hdspm_midi_output_write (struct hdspm_midi *hmidi) unsigned char buf[128]; /* Output is not interrupt driven */ - + spin_lock_irqsave (&hmidi->lock, flags); if (hmidi->output && !snd_rawmidi_transmit_empty (hmidi->output)) { @@ -1083,11 +1595,11 @@ static int snd_hdspm_midi_output_write (struct hdspm_midi *hmidi) if (n_pending > 0) { if (n_pending > (int)sizeof (buf)) n_pending = sizeof (buf); - + to_write = snd_rawmidi_transmit (hmidi->output, buf, n_pending); if (to_write > 0) { - for (i = 0; i < to_write; ++i) + for (i = 0; i < to_write; ++i) snd_hdspm_midi_write_byte (hmidi->hdspm, hmidi->id, buf[i]); @@ -1127,12 +1639,11 @@ static int snd_hdspm_midi_input_read (struct hdspm_midi *hmidi) } } hmidi->pending = 0; - if (hmidi->id) - hmidi->hdspm->control_register |= HDSPM_Midi1InterruptEnable; - else - hmidi->hdspm->control_register |= HDSPM_Midi0InterruptEnable; + + hmidi->hdspm->control_register |= hmidi->ie; hdspm_write(hmidi->hdspm, HDSPM_controlRegister, hmidi->hdspm->control_register); + spin_unlock_irqrestore (&hmidi->lock, flags); return snd_hdspm_midi_output_write (hmidi); } @@ -1143,20 +1654,18 @@ snd_hdspm_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) struct hdspm *hdspm; struct hdspm_midi *hmidi; unsigned long flags; - u32 ie; hmidi = substream->rmidi->private_data; hdspm = hmidi->hdspm; - ie = hmidi->id ? - HDSPM_Midi1InterruptEnable : HDSPM_Midi0InterruptEnable; + spin_lock_irqsave (&hdspm->lock, flags); if (up) { - if (!(hdspm->control_register & ie)) { + if (!(hdspm->control_register & hmidi->ie)) { snd_hdspm_flush_midi_input (hdspm, hmidi->id); - hdspm->control_register |= ie; + hdspm->control_register |= hmidi->ie; } } else { - hdspm->control_register &= ~ie; + hdspm->control_register &= ~hmidi->ie; } hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); @@ -1167,14 +1676,14 @@ static void snd_hdspm_midi_output_timer(unsigned long data) { struct hdspm_midi *hmidi = (struct hdspm_midi *) data; unsigned long flags; - + snd_hdspm_midi_output_write(hmidi); spin_lock_irqsave (&hmidi->lock, flags); /* this does not bump hmidi->istimer, because the kernel automatically removed the timer when it expired, and we are now adding it back, thus - leaving istimer wherever it was set before. + leaving istimer wherever it was set before. */ if (hmidi->istimer) { @@ -1288,22 +1797,103 @@ static int __devinit snd_hdspm_create_midi (struct snd_card *card, hdspm->midi[id].hdspm = hdspm; spin_lock_init (&hdspm->midi[id].lock); - sprintf (buf, "%s MIDI %d", card->shortname, id+1); - err = snd_rawmidi_new (card, buf, id, 1, 1, &hdspm->midi[id].rmidi); - if (err < 0) - return err; + if (0 == id) { + if (MADIface == hdspm->io_type) { + /* MIDI-over-MADI on HDSPe MADIface */ + hdspm->midi[0].dataIn = HDSPM_midiDataIn2; + hdspm->midi[0].statusIn = HDSPM_midiStatusIn2; + hdspm->midi[0].dataOut = HDSPM_midiDataOut2; + hdspm->midi[0].statusOut = HDSPM_midiStatusOut2; + hdspm->midi[0].ie = HDSPM_Midi2InterruptEnable; + hdspm->midi[0].irq = HDSPM_midi2IRQPending; + } else { + hdspm->midi[0].dataIn = HDSPM_midiDataIn0; + hdspm->midi[0].statusIn = HDSPM_midiStatusIn0; + hdspm->midi[0].dataOut = HDSPM_midiDataOut0; + hdspm->midi[0].statusOut = HDSPM_midiStatusOut0; + hdspm->midi[0].ie = HDSPM_Midi0InterruptEnable; + hdspm->midi[0].irq = HDSPM_midi0IRQPending; + } + } else if (1 == id) { + hdspm->midi[1].dataIn = HDSPM_midiDataIn1; + hdspm->midi[1].statusIn = HDSPM_midiStatusIn1; + hdspm->midi[1].dataOut = HDSPM_midiDataOut1; + hdspm->midi[1].statusOut = HDSPM_midiStatusOut1; + hdspm->midi[1].ie = HDSPM_Midi1InterruptEnable; + hdspm->midi[1].irq = HDSPM_midi1IRQPending; + } else if ((2 == id) && (MADI == hdspm->io_type)) { + /* MIDI-over-MADI on HDSPe MADI */ + hdspm->midi[2].dataIn = HDSPM_midiDataIn2; + hdspm->midi[2].statusIn = HDSPM_midiStatusIn2; + hdspm->midi[2].dataOut = HDSPM_midiDataOut2; + hdspm->midi[2].statusOut = HDSPM_midiStatusOut2; + hdspm->midi[2].ie = HDSPM_Midi2InterruptEnable; + hdspm->midi[2].irq = HDSPM_midi2IRQPending; + } else if (2 == id) { + /* TCO MTC, read only */ + hdspm->midi[2].dataIn = HDSPM_midiDataIn2; + hdspm->midi[2].statusIn = HDSPM_midiStatusIn2; + hdspm->midi[2].dataOut = -1; + hdspm->midi[2].statusOut = -1; + hdspm->midi[2].ie = HDSPM_Midi2InterruptEnable; + hdspm->midi[2].irq = HDSPM_midi2IRQPendingAES; + } else if (3 == id) { + /* TCO MTC on HDSPe MADI */ + hdspm->midi[3].dataIn = HDSPM_midiDataIn3; + hdspm->midi[3].statusIn = HDSPM_midiStatusIn3; + hdspm->midi[3].dataOut = -1; + hdspm->midi[3].statusOut = -1; + hdspm->midi[3].ie = HDSPM_Midi3InterruptEnable; + hdspm->midi[3].irq = HDSPM_midi3IRQPending; + } - sprintf(hdspm->midi[id].rmidi->name, "HDSPM MIDI %d", id+1); - hdspm->midi[id].rmidi->private_data = &hdspm->midi[id]; + if ((id < 2) || ((2 == id) && ((MADI == hdspm->io_type) || + (MADIface == hdspm->io_type)))) { + if ((id == 0) && (MADIface == hdspm->io_type)) { + sprintf(buf, "%s MIDIoverMADI", card->shortname); + } else if ((id == 2) && (MADI == hdspm->io_type)) { + sprintf(buf, "%s MIDIoverMADI", card->shortname); + } else { + sprintf(buf, "%s MIDI %d", card->shortname, id+1); + } + err = snd_rawmidi_new(card, buf, id, 1, 1, + &hdspm->midi[id].rmidi); + if (err < 0) + return err; - snd_rawmidi_set_ops(hdspm->midi[id].rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, - &snd_hdspm_midi_output); - snd_rawmidi_set_ops(hdspm->midi[id].rmidi, SNDRV_RAWMIDI_STREAM_INPUT, - &snd_hdspm_midi_input); + sprintf(hdspm->midi[id].rmidi->name, "%s MIDI %d", + card->id, id+1); + hdspm->midi[id].rmidi->private_data = &hdspm->midi[id]; + + snd_rawmidi_set_ops(hdspm->midi[id].rmidi, + SNDRV_RAWMIDI_STREAM_OUTPUT, + &snd_hdspm_midi_output); + snd_rawmidi_set_ops(hdspm->midi[id].rmidi, + SNDRV_RAWMIDI_STREAM_INPUT, + &snd_hdspm_midi_input); + + hdspm->midi[id].rmidi->info_flags |= + SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + } else { + /* TCO MTC, read only */ + sprintf(buf, "%s MTC %d", card->shortname, id+1); + err = snd_rawmidi_new(card, buf, id, 1, 1, + &hdspm->midi[id].rmidi); + if (err < 0) + return err; - hdspm->midi[id].rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | - SNDRV_RAWMIDI_INFO_INPUT | - SNDRV_RAWMIDI_INFO_DUPLEX; + sprintf(hdspm->midi[id].rmidi->name, + "%s MTC %d", card->id, id+1); + hdspm->midi[id].rmidi->private_data = &hdspm->midi[id]; + + snd_rawmidi_set_ops(hdspm->midi[id].rmidi, + SNDRV_RAWMIDI_STREAM_INPUT, + &snd_hdspm_midi_input); + + hdspm->midi[id].rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; + } return 0; } @@ -1312,12 +1902,15 @@ static int __devinit snd_hdspm_create_midi (struct snd_card *card, static void hdspm_midi_tasklet(unsigned long arg) { struct hdspm *hdspm = (struct hdspm *)arg; - - if (hdspm->midi[0].pending) - snd_hdspm_midi_input_read (&hdspm->midi[0]); - if (hdspm->midi[1].pending) - snd_hdspm_midi_input_read (&hdspm->midi[1]); -} + int i = 0; + + while (i < hdspm->midiPorts) { + if (hdspm->midi[i].pending) + snd_hdspm_midi_input_read(&hdspm->midi[i]); + + i++; + } +} /*----------------------------------------------------------------------------- @@ -1326,6 +1919,22 @@ static void hdspm_midi_tasklet(unsigned long arg) /* get the system sample rate which is set */ + +/** + * Calculate the real sample rate from the + * current DDS value. + **/ +static int hdspm_get_system_sample_rate(struct hdspm *hdspm) +{ + unsigned int period, rate; + + period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ); + rate = hdspm_calc_dds_value(hdspm, period); + + return rate; +} + + #define HDSPM_SYSTEM_SAMPLE_RATE(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -1340,112 +1949,274 @@ static int snd_hdspm_info_system_sample_rate(struct snd_kcontrol *kcontrol, { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; + uinfo->value.integer.min = 27000; + uinfo->value.integer.max = 207000; + uinfo->value.integer.step = 1; return 0; } + static int snd_hdspm_get_system_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value * ucontrol) { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - ucontrol->value.enumerated.item[0] = hdspm->system_sample_rate; + ucontrol->value.integer.value[0] = hdspm_get_system_sample_rate(hdspm); + return 0; +} + + +/** + * Returns the WordClock sample rate class for the given card. + **/ +static int hdspm_get_wc_sample_rate(struct hdspm *hdspm) +{ + int status; + + switch (hdspm->io_type) { + case RayDAT: + case AIO: + status = hdspm_read(hdspm, HDSPM_RD_STATUS_1); + return (status >> 16) & 0xF; + break; + default: + break; + } + + + return 0; +} + + +/** + * Returns the TCO sample rate class for the given card. + **/ +static int hdspm_get_tco_sample_rate(struct hdspm *hdspm) +{ + int status; + + if (hdspm->tco) { + switch (hdspm->io_type) { + case RayDAT: + case AIO: + status = hdspm_read(hdspm, HDSPM_RD_STATUS_1); + return (status >> 20) & 0xF; + break; + default: + break; + } + } + + return 0; +} + + +/** + * Returns the SYNC_IN sample rate class for the given card. + **/ +static int hdspm_get_sync_in_sample_rate(struct hdspm *hdspm) +{ + int status; + + if (hdspm->tco) { + switch (hdspm->io_type) { + case RayDAT: + case AIO: + status = hdspm_read(hdspm, HDSPM_RD_STATUS_2); + return (status >> 12) & 0xF; + break; + default: + break; + } + } + return 0; } + +/** + * Returns the sample rate class for input source <idx> for + * 'new style' cards like the AIO and RayDAT. + **/ +static int hdspm_get_s1_sample_rate(struct hdspm *hdspm, unsigned int idx) +{ + int status = hdspm_read(hdspm, HDSPM_RD_STATUS_2); + + return (status >> (idx*4)) & 0xF; +} + + + #define HDSPM_AUTOSYNC_SAMPLE_RATE(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = xindex, \ - .access = SNDRV_CTL_ELEM_ACCESS_READ, \ - .info = snd_hdspm_info_autosync_sample_rate, \ - .get = snd_hdspm_get_autosync_sample_rate \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .private_value = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ, \ + .info = snd_hdspm_info_autosync_sample_rate, \ + .get = snd_hdspm_get_autosync_sample_rate \ } + static int snd_hdspm_info_autosync_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "32000", "44100", "48000", - "64000", "88200", "96000", - "128000", "176400", "192000", - "None" - }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = 10; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); + texts_freq[uinfo->value.enumerated.item]); return 0; } + static int snd_hdspm_get_autosync_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value * ucontrol) { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - switch (hdspm_external_sample_rate(hdspm)) { - case 32000: - ucontrol->value.enumerated.item[0] = 0; - break; - case 44100: - ucontrol->value.enumerated.item[0] = 1; - break; - case 48000: - ucontrol->value.enumerated.item[0] = 2; - break; - case 64000: - ucontrol->value.enumerated.item[0] = 3; - break; - case 88200: - ucontrol->value.enumerated.item[0] = 4; - break; - case 96000: - ucontrol->value.enumerated.item[0] = 5; - break; - case 128000: - ucontrol->value.enumerated.item[0] = 6; - break; - case 176400: - ucontrol->value.enumerated.item[0] = 7; - break; - case 192000: - ucontrol->value.enumerated.item[0] = 8; - break; + switch (hdspm->io_type) { + case RayDAT: + switch (kcontrol->private_value) { + case 0: + ucontrol->value.enumerated.item[0] = + hdspm_get_wc_sample_rate(hdspm); + break; + case 7: + ucontrol->value.enumerated.item[0] = + hdspm_get_tco_sample_rate(hdspm); + break; + case 8: + ucontrol->value.enumerated.item[0] = + hdspm_get_sync_in_sample_rate(hdspm); + break; + default: + ucontrol->value.enumerated.item[0] = + hdspm_get_s1_sample_rate(hdspm, + kcontrol->private_value-1); + } + + case AIO: + switch (kcontrol->private_value) { + case 0: /* WC */ + ucontrol->value.enumerated.item[0] = + hdspm_get_wc_sample_rate(hdspm); + break; + case 4: /* TCO */ + ucontrol->value.enumerated.item[0] = + hdspm_get_tco_sample_rate(hdspm); + break; + case 5: /* SYNC_IN */ + ucontrol->value.enumerated.item[0] = + hdspm_get_sync_in_sample_rate(hdspm); + break; + default: + ucontrol->value.enumerated.item[0] = + hdspm_get_s1_sample_rate(hdspm, + ucontrol->id.index-1); + } + + case AES32: + switch (kcontrol->private_value) { + case 0: /* WC */ + ucontrol->value.enumerated.item[0] = + hdspm_get_wc_sample_rate(hdspm); + break; + case 9: /* TCO */ + ucontrol->value.enumerated.item[0] = + hdspm_get_tco_sample_rate(hdspm); + break; + case 10: /* SYNC_IN */ + ucontrol->value.enumerated.item[0] = + hdspm_get_sync_in_sample_rate(hdspm); + break; + default: /* AES1 to AES8 */ + ucontrol->value.enumerated.item[0] = + hdspm_get_s1_sample_rate(hdspm, + kcontrol->private_value-1); + break; + + } default: - ucontrol->value.enumerated.item[0] = 9; + break; } + return 0; } + #define HDSPM_SYSTEM_CLOCK_MODE(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = xindex, \ - .access = SNDRV_CTL_ELEM_ACCESS_READ, \ - .info = snd_hdspm_info_system_clock_mode, \ - .get = snd_hdspm_get_system_clock_mode, \ -} +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_system_clock_mode, \ + .get = snd_hdspm_get_system_clock_mode, \ + .put = snd_hdspm_put_system_clock_mode, \ +} + + +/** + * Returns the system clock mode for the given card. + * @returns 0 - master, 1 - slave + **/ +static int hdspm_system_clock_mode(struct hdspm *hdspm) +{ + switch (hdspm->io_type) { + case AIO: + case RayDAT: + if (hdspm->settings_register & HDSPM_c0Master) + return 0; + break; + + default: + if (hdspm->control_register & HDSPM_ClockModeMaster) + return 0; + } + return 1; +} -static int hdspm_system_clock_mode(struct hdspm * hdspm) +/** + * Sets the system clock mode. + * @param mode 0 - master, 1 - slave + **/ +static void hdspm_set_system_clock_mode(struct hdspm *hdspm, int mode) { - /* Always reflect the hardware info, rme is never wrong !!!! */ + switch (hdspm->io_type) { + case AIO: + case RayDAT: + if (0 == mode) + hdspm->settings_register |= HDSPM_c0Master; + else + hdspm->settings_register &= ~HDSPM_c0Master; - if (hdspm->control_register & HDSPM_ClockModeMaster) - return 0; - return 1; + hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register); + break; + + default: + if (0 == mode) + hdspm->control_register |= HDSPM_ClockModeMaster; + else + hdspm->control_register &= ~HDSPM_ClockModeMaster; + + hdspm_write(hdspm, HDSPM_controlRegister, + hdspm->control_register); + } } + static int snd_hdspm_info_system_clock_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "Master", "Slave" }; + static char *texts[] = { "Master", "AutoSync" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; @@ -1463,96 +2234,83 @@ static int snd_hdspm_get_system_clock_mode(struct snd_kcontrol *kcontrol, { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - ucontrol->value.enumerated.item[0] = - hdspm_system_clock_mode(hdspm); + ucontrol->value.enumerated.item[0] = hdspm_system_clock_mode(hdspm); return 0; } -#define HDSPM_CLOCK_SOURCE(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = xindex, \ - .info = snd_hdspm_info_clock_source, \ - .get = snd_hdspm_get_clock_source, \ - .put = snd_hdspm_put_clock_source \ +static int snd_hdspm_put_system_clock_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + int val; + + if (!snd_hdspm_use_is_exclusive(hdspm)) + return -EBUSY; + + val = ucontrol->value.enumerated.item[0]; + if (val < 0) + val = 0; + else if (val > 1) + val = 1; + + hdspm_set_system_clock_mode(hdspm, val); + + return 0; } + +#define HDSPM_INTERNAL_CLOCK(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .info = snd_hdspm_info_clock_source, \ + .get = snd_hdspm_get_clock_source, \ + .put = snd_hdspm_put_clock_source \ +} + + static int hdspm_clock_source(struct hdspm * hdspm) { - if (hdspm->control_register & HDSPM_ClockModeMaster) { - switch (hdspm->system_sample_rate) { - case 32000: - return 1; - case 44100: - return 2; - case 48000: - return 3; - case 64000: - return 4; - case 88200: - return 5; - case 96000: - return 6; - case 128000: - return 7; - case 176400: - return 8; - case 192000: - return 9; - default: - return 3; - } - } else { - return 0; + switch (hdspm->system_sample_rate) { + case 32000: return 0; + case 44100: return 1; + case 48000: return 2; + case 64000: return 3; + case 88200: return 4; + case 96000: return 5; + case 128000: return 6; + case 176400: return 7; + case 192000: return 8; } + + return -1; } static int hdspm_set_clock_source(struct hdspm * hdspm, int mode) { int rate; switch (mode) { - - case HDSPM_CLOCK_SOURCE_AUTOSYNC: - if (hdspm_external_sample_rate(hdspm) != 0) { - hdspm->control_register &= ~HDSPM_ClockModeMaster; - hdspm_write(hdspm, HDSPM_controlRegister, - hdspm->control_register); - return 0; - } - return -1; - case HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ: - rate = 32000; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ: - rate = 44100; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ: - rate = 48000; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ: - rate = 64000; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ: - rate = 88200; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ: - rate = 96000; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_128KHZ: - rate = 128000; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_176_4KHZ: - rate = 176400; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_192KHZ: - rate = 192000; - break; - + case 0: + rate = 32000; break; + case 1: + rate = 44100; break; + case 2: + rate = 48000; break; + case 3: + rate = 64000; break; + case 4: + rate = 88200; break; + case 5: + rate = 96000; break; + case 6: + rate = 128000; break; + case 7: + rate = 176400; break; + case 8: + rate = 192000; break; default: - rate = 44100; + rate = 48000; } - hdspm->control_register |= HDSPM_ClockModeMaster; - hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); hdspm_set_rate(hdspm, rate, 1); return 0; } @@ -1560,25 +2318,16 @@ static int hdspm_set_clock_source(struct hdspm * hdspm, int mode) static int snd_hdspm_info_clock_source(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "AutoSync", - "Internal 32.0 kHz", "Internal 44.1 kHz", - "Internal 48.0 kHz", - "Internal 64.0 kHz", "Internal 88.2 kHz", - "Internal 96.0 kHz", - "Internal 128.0 kHz", "Internal 176.4 kHz", - "Internal 192.0 kHz" - }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; - uinfo->value.enumerated.items = 10; + uinfo->value.enumerated.items = 9; 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]); + texts_freq[uinfo->value.enumerated.item+1]); return 0; } @@ -1615,134 +2364,301 @@ static int snd_hdspm_put_clock_source(struct snd_kcontrol *kcontrol, return change; } -#define HDSPM_PREF_SYNC_REF(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = xindex, \ - .info = snd_hdspm_info_pref_sync_ref, \ - .get = snd_hdspm_get_pref_sync_ref, \ - .put = snd_hdspm_put_pref_sync_ref \ -} +#define HDSPM_PREF_SYNC_REF(xname, xindex) \ +{.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_pref_sync_ref, \ + .get = snd_hdspm_get_pref_sync_ref, \ + .put = snd_hdspm_put_pref_sync_ref \ +} + + +/** + * Returns the current preferred sync reference setting. + * The semantics of the return value are depending on the + * card, please see the comments for clarification. + **/ static int hdspm_pref_sync_ref(struct hdspm * hdspm) { - /* Notice that this looks at the requested sync source, - not the one actually in use. - */ - if (hdspm->is_aes32) { + switch (hdspm->io_type) { + case AES32: switch (hdspm->control_register & HDSPM_SyncRefMask) { - /* number gives AES index, except for 0 which - corresponds to WordClock */ - case 0: return 0; - case HDSPM_SyncRef0: return 1; - case HDSPM_SyncRef1: return 2; - case HDSPM_SyncRef1+HDSPM_SyncRef0: return 3; - case HDSPM_SyncRef2: return 4; - case HDSPM_SyncRef2+HDSPM_SyncRef0: return 5; - case HDSPM_SyncRef2+HDSPM_SyncRef1: return 6; - case HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0: return 7; - case HDSPM_SyncRef3: return 8; + case 0: return 0; /* WC */ + case HDSPM_SyncRef0: return 1; /* AES 1 */ + case HDSPM_SyncRef1: return 2; /* AES 2 */ + case HDSPM_SyncRef1+HDSPM_SyncRef0: return 3; /* AES 3 */ + case HDSPM_SyncRef2: return 4; /* AES 4 */ + case HDSPM_SyncRef2+HDSPM_SyncRef0: return 5; /* AES 5 */ + case HDSPM_SyncRef2+HDSPM_SyncRef1: return 6; /* AES 6 */ + case HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0: + return 7; /* AES 7 */ + case HDSPM_SyncRef3: return 8; /* AES 8 */ + case HDSPM_SyncRef3+HDSPM_SyncRef0: return 9; /* TCO */ } - } else { - switch (hdspm->control_register & HDSPM_SyncRefMask) { - case HDSPM_SyncRef_Word: - return HDSPM_SYNC_FROM_WORD; - case HDSPM_SyncRef_MADI: - return HDSPM_SYNC_FROM_MADI; + break; + + case MADI: + case MADIface: + if (hdspm->tco) { + switch (hdspm->control_register & HDSPM_SyncRefMask) { + case 0: return 0; /* WC */ + case HDSPM_SyncRef0: return 1; /* MADI */ + case HDSPM_SyncRef1: return 2; /* TCO */ + case HDSPM_SyncRef1+HDSPM_SyncRef0: + return 3; /* SYNC_IN */ + } + } else { + switch (hdspm->control_register & HDSPM_SyncRefMask) { + case 0: return 0; /* WC */ + case HDSPM_SyncRef0: return 1; /* MADI */ + case HDSPM_SyncRef1+HDSPM_SyncRef0: + return 2; /* SYNC_IN */ + } + } + break; + + case RayDAT: + if (hdspm->tco) { + switch ((hdspm->settings_register & + HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) { + case 0: return 0; /* WC */ + case 3: return 1; /* ADAT 1 */ + case 4: return 2; /* ADAT 2 */ + case 5: return 3; /* ADAT 3 */ + case 6: return 4; /* ADAT 4 */ + case 1: return 5; /* AES */ + case 2: return 6; /* SPDIF */ + case 9: return 7; /* TCO */ + case 10: return 8; /* SYNC_IN */ + } + } else { + switch ((hdspm->settings_register & + HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) { + case 0: return 0; /* WC */ + case 3: return 1; /* ADAT 1 */ + case 4: return 2; /* ADAT 2 */ + case 5: return 3; /* ADAT 3 */ + case 6: return 4; /* ADAT 4 */ + case 1: return 5; /* AES */ + case 2: return 6; /* SPDIF */ + case 10: return 7; /* SYNC_IN */ + } } + + break; + + case AIO: + if (hdspm->tco) { + switch ((hdspm->settings_register & + HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) { + case 0: return 0; /* WC */ + case 3: return 1; /* ADAT */ + case 1: return 2; /* AES */ + case 2: return 3; /* SPDIF */ + case 9: return 4; /* TCO */ + case 10: return 5; /* SYNC_IN */ + } + } else { + switch ((hdspm->settings_register & + HDSPM_c0_SyncRefMask) / HDSPM_c0_SyncRef0) { + case 0: return 0; /* WC */ + case 3: return 1; /* ADAT */ + case 1: return 2; /* AES */ + case 2: return 3; /* SPDIF */ + case 10: return 4; /* SYNC_IN */ + } + } + + break; } - return HDSPM_SYNC_FROM_WORD; + return -1; } + +/** + * Set the preferred sync reference to <pref>. The semantics + * of <pref> are depending on the card type, see the comments + * for clarification. + **/ static int hdspm_set_pref_sync_ref(struct hdspm * hdspm, int pref) { - hdspm->control_register &= ~HDSPM_SyncRefMask; + int p = 0; - if (hdspm->is_aes32) { - switch (pref) { - case 0: - hdspm->control_register |= 0; - break; - case 1: - hdspm->control_register |= HDSPM_SyncRef0; - break; - case 2: - hdspm->control_register |= HDSPM_SyncRef1; - break; - case 3: - hdspm->control_register |= HDSPM_SyncRef1+HDSPM_SyncRef0; - break; - case 4: - hdspm->control_register |= HDSPM_SyncRef2; - break; - case 5: - hdspm->control_register |= HDSPM_SyncRef2+HDSPM_SyncRef0; - break; - case 6: - hdspm->control_register |= HDSPM_SyncRef2+HDSPM_SyncRef1; - break; - case 7: - hdspm->control_register |= - HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0; - break; - case 8: - hdspm->control_register |= HDSPM_SyncRef3; - break; - default: - return -1; - } - } else { + switch (hdspm->io_type) { + case AES32: + hdspm->control_register &= ~HDSPM_SyncRefMask; switch (pref) { - case HDSPM_SYNC_FROM_MADI: - hdspm->control_register |= HDSPM_SyncRef_MADI; + case 0: /* WC */ + break; + case 1: /* AES 1 */ + hdspm->control_register |= HDSPM_SyncRef0; + break; + case 2: /* AES 2 */ + hdspm->control_register |= HDSPM_SyncRef1; + break; + case 3: /* AES 3 */ + hdspm->control_register |= + HDSPM_SyncRef1+HDSPM_SyncRef0; + break; + case 4: /* AES 4 */ + hdspm->control_register |= HDSPM_SyncRef2; + break; + case 5: /* AES 5 */ + hdspm->control_register |= + HDSPM_SyncRef2+HDSPM_SyncRef0; break; - case HDSPM_SYNC_FROM_WORD: - hdspm->control_register |= HDSPM_SyncRef_Word; + case 6: /* AES 6 */ + hdspm->control_register |= + HDSPM_SyncRef2+HDSPM_SyncRef1; + break; + case 7: /* AES 7 */ + hdspm->control_register |= + HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0; + break; + case 8: /* AES 8 */ + hdspm->control_register |= HDSPM_SyncRef3; + break; + case 9: /* TCO */ + hdspm->control_register |= + HDSPM_SyncRef3+HDSPM_SyncRef0; break; default: return -1; } + + break; + + case MADI: + case MADIface: + hdspm->control_register &= ~HDSPM_SyncRefMask; + if (hdspm->tco) { + switch (pref) { + case 0: /* WC */ + break; + case 1: /* MADI */ + hdspm->control_register |= HDSPM_SyncRef0; + break; + case 2: /* TCO */ + hdspm->control_register |= HDSPM_SyncRef1; + break; + case 3: /* SYNC_IN */ + hdspm->control_register |= + HDSPM_SyncRef0+HDSPM_SyncRef1; + break; + default: + return -1; + } + } else { + switch (pref) { + case 0: /* WC */ + break; + case 1: /* MADI */ + hdspm->control_register |= HDSPM_SyncRef0; + break; + case 2: /* SYNC_IN */ + hdspm->control_register |= + HDSPM_SyncRef0+HDSPM_SyncRef1; + break; + default: + return -1; + } + } + + break; + + case RayDAT: + if (hdspm->tco) { + switch (pref) { + case 0: p = 0; break; /* WC */ + case 1: p = 3; break; /* ADAT 1 */ + case 2: p = 4; break; /* ADAT 2 */ + case 3: p = 5; break; /* ADAT 3 */ + case 4: p = 6; break; /* ADAT 4 */ + case 5: p = 1; break; /* AES */ + case 6: p = 2; break; /* SPDIF */ + case 7: p = 9; break; /* TCO */ + case 8: p = 10; break; /* SYNC_IN */ + default: return -1; + } + } else { + switch (pref) { + case 0: p = 0; break; /* WC */ + case 1: p = 3; break; /* ADAT 1 */ + case 2: p = 4; break; /* ADAT 2 */ + case 3: p = 5; break; /* ADAT 3 */ + case 4: p = 6; break; /* ADAT 4 */ + case 5: p = 1; break; /* AES */ + case 6: p = 2; break; /* SPDIF */ + case 7: p = 10; break; /* SYNC_IN */ + default: return -1; + } + } + break; + + case AIO: + if (hdspm->tco) { + switch (pref) { + case 0: p = 0; break; /* WC */ + case 1: p = 3; break; /* ADAT */ + case 2: p = 1; break; /* AES */ + case 3: p = 2; break; /* SPDIF */ + case 4: p = 9; break; /* TCO */ + case 5: p = 10; break; /* SYNC_IN */ + default: return -1; + } + } else { + switch (pref) { + case 0: p = 0; break; /* WC */ + case 1: p = 3; break; /* ADAT */ + case 2: p = 1; break; /* AES */ + case 3: p = 2; break; /* SPDIF */ + case 4: p = 10; break; /* SYNC_IN */ + default: return -1; + } + } + break; } - hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); + + switch (hdspm->io_type) { + case RayDAT: + case AIO: + hdspm->settings_register &= ~HDSPM_c0_SyncRefMask; + hdspm->settings_register |= HDSPM_c0_SyncRef0 * p; + hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register); + break; + + case MADI: + case MADIface: + case AES32: + hdspm_write(hdspm, HDSPM_controlRegister, + hdspm->control_register); + } + return 0; } + static int snd_hdspm_info_pref_sync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - if (hdspm->is_aes32) { - static char *texts[] = { "Word", "AES1", "AES2", "AES3", - "AES4", "AES5", "AES6", "AES7", "AES8" }; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - - uinfo->value.enumerated.items = 9; - - 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]); - } else { - static char *texts[] = { "Word", "MADI" }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = hdspm->texts_autosync_items; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; - uinfo->value.enumerated.items = 2; + strcpy(uinfo->value.enumerated.name, + hdspm->texts_autosync[uinfo->value.enumerated.item]); - 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; } @@ -1750,32 +2666,41 @@ static int snd_hdspm_get_pref_sync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + int psf = hdspm_pref_sync_ref(hdspm); - ucontrol->value.enumerated.item[0] = hdspm_pref_sync_ref(hdspm); - return 0; + if (psf >= 0) { + ucontrol->value.enumerated.item[0] = psf; + return 0; + } + + return -1; } static int snd_hdspm_put_pref_sync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - int change, max; - unsigned int val; - - max = hdspm->is_aes32 ? 9 : 2; + int val, change = 0; if (!snd_hdspm_use_is_exclusive(hdspm)) return -EBUSY; - val = ucontrol->value.enumerated.item[0] % max; + val = ucontrol->value.enumerated.item[0]; + + if (val < 0) + val = 0; + else if (val >= hdspm->texts_autosync_items) + val = hdspm->texts_autosync_items-1; spin_lock_irq(&hdspm->lock); - change = (int) val != hdspm_pref_sync_ref(hdspm); - hdspm_set_pref_sync_ref(hdspm, val); + if (val != hdspm_pref_sync_ref(hdspm)) + change = (0 == hdspm_set_pref_sync_ref(hdspm, val)) ? 1 : 0; + spin_unlock_irq(&hdspm->lock); return change; } + #define HDSPM_AUTOSYNC_REF(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -1785,18 +2710,18 @@ static int snd_hdspm_put_pref_sync_ref(struct snd_kcontrol *kcontrol, .get = snd_hdspm_get_autosync_ref, \ } -static int hdspm_autosync_ref(struct hdspm * hdspm) +static int hdspm_autosync_ref(struct hdspm *hdspm) { - if (hdspm->is_aes32) { + if (AES32 == hdspm->io_type) { unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister); - unsigned int syncref = (status >> HDSPM_AES32_syncref_bit) & - 0xF; + unsigned int syncref = + (status >> HDSPM_AES32_syncref_bit) & 0xF; if (syncref == 0) return HDSPM_AES32_AUTOSYNC_FROM_WORD; if (syncref <= 8) return syncref; return HDSPM_AES32_AUTOSYNC_FROM_NONE; - } else { + } else if (MADI == hdspm->io_type) { /* This looks at the autosync selected sync reference */ unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); @@ -1805,22 +2730,27 @@ static int hdspm_autosync_ref(struct hdspm * hdspm) return HDSPM_AUTOSYNC_FROM_WORD; case HDSPM_SelSyncRef_MADI: return HDSPM_AUTOSYNC_FROM_MADI; + case HDSPM_SelSyncRef_TCO: + return HDSPM_AUTOSYNC_FROM_TCO; + case HDSPM_SelSyncRef_SyncIn: + return HDSPM_AUTOSYNC_FROM_SYNC_IN; case HDSPM_SelSyncRef_NVALID: return HDSPM_AUTOSYNC_FROM_NONE; default: return 0; } - return 0; } + return 0; } + static int snd_hdspm_info_autosync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - if (hdspm->is_aes32) { + if (AES32 == hdspm->io_type) { static char *texts[] = { "WordClock", "AES1", "AES2", "AES3", "AES4", "AES5", "AES6", "AES7", "AES8", "None"}; @@ -1833,14 +2763,15 @@ static int snd_hdspm_info_autosync_ref(struct snd_kcontrol *kcontrol, uinfo->value.enumerated.items - 1; strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - } else { - static char *texts[] = { "WordClock", "MADI", "None" }; + } else if (MADI == hdspm->io_type) { + static char *texts[] = {"Word Clock", "MADI", "TCO", + "Sync In", "None" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; - uinfo->value.enumerated.items = 3; + uinfo->value.enumerated.items = 5; if (uinfo->value.enumerated.item >= - uinfo->value.enumerated.items) + uinfo->value.enumerated.items) uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; strcpy(uinfo->value.enumerated.name, @@ -1858,6 +2789,7 @@ static int snd_hdspm_get_autosync_ref(struct snd_kcontrol *kcontrol, return 0; } + #define HDSPM_LINE_OUT(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -1914,6 +2846,7 @@ static int snd_hdspm_put_line_out(struct snd_kcontrol *kcontrol, return change; } + #define HDSPM_TX_64(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -1969,6 +2902,7 @@ static int snd_hdspm_put_tx_64(struct snd_kcontrol *kcontrol, return change; } + #define HDSPM_C_TMS(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -2024,6 +2958,7 @@ static int snd_hdspm_put_c_tms(struct snd_kcontrol *kcontrol, return change; } + #define HDSPM_SAFE_MODE(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -2079,6 +3014,7 @@ static int snd_hdspm_put_safe_mode(struct snd_kcontrol *kcontrol, return change; } + #define HDSPM_EMPHASIS(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -2134,6 +3070,7 @@ static int snd_hdspm_put_emphasis(struct snd_kcontrol *kcontrol, return change; } + #define HDSPM_DOLBY(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -2189,6 +3126,7 @@ static int snd_hdspm_put_dolby(struct snd_kcontrol *kcontrol, return change; } + #define HDSPM_PROFESSIONAL(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -2315,6 +3253,7 @@ static int snd_hdspm_put_input_select(struct snd_kcontrol *kcontrol, return change; } + #define HDSPM_DS_WIRE(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -2386,6 +3325,7 @@ static int snd_hdspm_put_ds_wire(struct snd_kcontrol *kcontrol, return change; } + #define HDSPM_QS_WIRE(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ @@ -2472,15 +3412,6 @@ static int snd_hdspm_put_qs_wire(struct snd_kcontrol *kcontrol, return change; } -/* Simple Mixer - deprecated since to much faders ??? - MIXER interface says output (source, destination, value) - where source > MAX_channels are playback channels - on MADICARD - - playback mixer matrix: [channelout+64] [output] [value] - - input(thru) mixer matrix: [channelin] [output] [value] - (better do 2 kontrols for separation ?) -*/ #define HDSPM_MIXER(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ @@ -2586,7 +3517,7 @@ static int snd_hdspm_put_mixer(struct snd_kcontrol *kcontrol, /* The simple mixer control(s) provide gain control for the basic 1:1 mappings of playback streams to output - streams. + streams. */ #define HDSPM_PLAYBACK_MIXER \ @@ -2604,7 +3535,7 @@ static int snd_hdspm_info_playback_mixer(struct snd_kcontrol *kcontrol, uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 0; - uinfo->value.integer.max = 65536; + uinfo->value.integer.max = 64; uinfo->value.integer.step = 1; return 0; } @@ -2614,28 +3545,17 @@ static int snd_hdspm_get_playback_mixer(struct snd_kcontrol *kcontrol, { struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); int channel; - int mapped_channel; channel = ucontrol->id.index - 1; if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS)) return -EINVAL; - mapped_channel = hdspm->channel_map[channel]; - if (mapped_channel < 0) - return -EINVAL; - spin_lock_irq(&hdspm->lock); ucontrol->value.integer.value[0] = - hdspm_read_pb_gain(hdspm, mapped_channel, mapped_channel); + (hdspm_read_pb_gain(hdspm, channel, channel)*64)/UNITY_GAIN; spin_unlock_irq(&hdspm->lock); - /* - snd_printdd("get pb mixer index %d, channel %d, mapped_channel %d, " - "value %d\n", - ucontrol->id.index, channel, mapped_channel, - ucontrol->value.integer.value[0]); - */ return 0; } @@ -2645,7 +3565,6 @@ static int snd_hdspm_put_playback_mixer(struct snd_kcontrol *kcontrol, struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); int change; int channel; - int mapped_channel; int gain; if (!snd_hdspm_use_is_exclusive(hdspm)) @@ -2656,59 +3575,60 @@ static int snd_hdspm_put_playback_mixer(struct snd_kcontrol *kcontrol, if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS)) return -EINVAL; - mapped_channel = hdspm->channel_map[channel]; - if (mapped_channel < 0) - return -EINVAL; - - gain = ucontrol->value.integer.value[0]; + gain = ucontrol->value.integer.value[0]*UNITY_GAIN/64; spin_lock_irq(&hdspm->lock); change = - gain != hdspm_read_pb_gain(hdspm, mapped_channel, - mapped_channel); + gain != hdspm_read_pb_gain(hdspm, channel, + channel); if (change) - hdspm_write_pb_gain(hdspm, mapped_channel, mapped_channel, + hdspm_write_pb_gain(hdspm, channel, channel, gain); spin_unlock_irq(&hdspm->lock); return change; } -#define HDSPM_WC_SYNC_CHECK(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = xindex, \ - .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ - .info = snd_hdspm_info_sync_check, \ - .get = snd_hdspm_get_wc_sync_check \ +#define HDSPM_SYNC_CHECK(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .private_value = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_sync_check, \ + .get = snd_hdspm_get_sync_check \ } + static int snd_hdspm_info_sync_check(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "No Lock", "Lock", "Sync" }; + static char *texts[] = { "No Lock", "Lock", "Sync", "N/A" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; - uinfo->value.enumerated.items = 3; + uinfo->value.enumerated.items = 4; if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; + uinfo->value.enumerated.items - 1; strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); + texts[uinfo->value.enumerated.item]); return 0; } -static int hdspm_wc_sync_check(struct hdspm * hdspm) +static int hdspm_wc_sync_check(struct hdspm *hdspm) { - if (hdspm->is_aes32) { - int status = hdspm_read(hdspm, HDSPM_statusRegister); - if (status & HDSPM_AES32_wcLock) { - /* I don't know how to differenciate sync from lock. - Doing as if sync for now */ + int status, status2; + + switch (hdspm->io_type) { + case AES32: + status = hdspm_read(hdspm, HDSPM_statusRegister); + if (status & HDSPM_wcSync) return 2; - } + else if (status & HDSPM_wcLock) + return 1; return 0; - } else { - int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + break; + + case MADI: + status2 = hdspm_read(hdspm, HDSPM_statusRegister2); if (status2 & HDSPM_wcLock) { if (status2 & HDSPM_wcSync) return 2; @@ -2716,29 +3636,30 @@ static int hdspm_wc_sync_check(struct hdspm * hdspm) return 1; } return 0; - } -} + break; -static int snd_hdspm_get_wc_sync_check(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + case RayDAT: + case AIO: + status = hdspm_read(hdspm, HDSPM_statusRegister); - ucontrol->value.enumerated.item[0] = hdspm_wc_sync_check(hdspm); - return 0; -} + if (status & 0x2000000) + return 2; + else if (status & 0x1000000) + return 1; + return 0; + break; -#define HDSPM_MADI_SYNC_CHECK(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = xindex, \ - .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ - .info = snd_hdspm_info_sync_check, \ - .get = snd_hdspm_get_madisync_sync_check \ + case MADIface: + break; + } + + + return 3; } -static int hdspm_madisync_sync_check(struct hdspm * hdspm) + +static int hdspm_madi_sync_check(struct hdspm *hdspm) { int status = hdspm_read(hdspm, HDSPM_statusRegister); if (status & HDSPM_madiLock) { @@ -2750,89 +3671,726 @@ static int hdspm_madisync_sync_check(struct hdspm * hdspm) return 0; } -static int snd_hdspm_get_madisync_sync_check(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value * - ucontrol) + +static int hdspm_s1_sync_check(struct hdspm *hdspm, int idx) { - struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + int status, lock, sync; + + status = hdspm_read(hdspm, HDSPM_RD_STATUS_1); + + lock = (status & (0x1<<idx)) ? 1 : 0; + sync = (status & (0x100<<idx)) ? 1 : 0; - ucontrol->value.enumerated.item[0] = - hdspm_madisync_sync_check(hdspm); + if (lock && sync) + return 2; + else if (lock) + return 1; return 0; } -#define HDSPM_AES_SYNC_CHECK(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = xname, \ - .index = xindex, \ - .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ - .info = snd_hdspm_info_sync_check, \ - .get = snd_hdspm_get_aes_sync_check \ +static int hdspm_sync_in_sync_check(struct hdspm *hdspm) +{ + int status, lock = 0, sync = 0; + + switch (hdspm->io_type) { + case RayDAT: + case AIO: + status = hdspm_read(hdspm, HDSPM_RD_STATUS_3); + lock = (status & 0x400) ? 1 : 0; + sync = (status & 0x800) ? 1 : 0; + break; + + case MADI: + case AES32: + status = hdspm_read(hdspm, HDSPM_statusRegister2); + lock = (status & HDSPM_syncInLock) ? 1 : 0; + sync = (status & HDSPM_syncInSync) ? 1 : 0; + break; + + case MADIface: + break; + } + + if (lock && sync) + return 2; + else if (lock) + return 1; + + return 0; } -static int hdspm_aes_sync_check(struct hdspm * hdspm, int idx) +static int hdspm_aes_sync_check(struct hdspm *hdspm, int idx) { - int status2 = hdspm_read(hdspm, HDSPM_statusRegister2); - if (status2 & (HDSPM_LockAES >> idx)) { - /* I don't know how to differenciate sync from lock. - Doing as if sync for now */ + int status2, lock, sync; + status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + + lock = (status2 & (0x0080 >> idx)) ? 1 : 0; + sync = (status2 & (0x8000 >> idx)) ? 1 : 0; + + if (sync) return 2; + else if (lock) + return 1; + return 0; +} + + +static int hdspm_tco_sync_check(struct hdspm *hdspm) +{ + int status; + + if (hdspm->tco) { + switch (hdspm->io_type) { + case MADI: + case AES32: + status = hdspm_read(hdspm, HDSPM_statusRegister); + if (status & HDSPM_tcoLock) { + if (status & HDSPM_tcoSync) + return 2; + else + return 1; + } + return 0; + + break; + + case RayDAT: + case AIO: + status = hdspm_read(hdspm, HDSPM_RD_STATUS_1); + + if (status & 0x8000000) + return 2; /* Sync */ + if (status & 0x4000000) + return 1; /* Lock */ + return 0; /* No signal */ + break; + + default: + break; + } + } + + return 3; /* N/A */ +} + + +static int snd_hdspm_get_sync_check(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + int val = -1; + + switch (hdspm->io_type) { + case RayDAT: + switch (kcontrol->private_value) { + case 0: /* WC */ + val = hdspm_wc_sync_check(hdspm); break; + case 7: /* TCO */ + val = hdspm_tco_sync_check(hdspm); break; + case 8: /* SYNC IN */ + val = hdspm_sync_in_sync_check(hdspm); break; + default: + val = hdspm_s1_sync_check(hdspm, ucontrol->id.index-1); + } + + case AIO: + switch (kcontrol->private_value) { + case 0: /* WC */ + val = hdspm_wc_sync_check(hdspm); break; + case 4: /* TCO */ + val = hdspm_tco_sync_check(hdspm); break; + case 5: /* SYNC IN */ + val = hdspm_sync_in_sync_check(hdspm); break; + default: + val = hdspm_s1_sync_check(hdspm, ucontrol->id.index-1); + } + + case MADI: + switch (kcontrol->private_value) { + case 0: /* WC */ + val = hdspm_wc_sync_check(hdspm); break; + case 1: /* MADI */ + val = hdspm_madi_sync_check(hdspm); break; + case 2: /* TCO */ + val = hdspm_tco_sync_check(hdspm); break; + case 3: /* SYNC_IN */ + val = hdspm_sync_in_sync_check(hdspm); break; + } + + case MADIface: + val = hdspm_madi_sync_check(hdspm); /* MADI */ + break; + + case AES32: + switch (kcontrol->private_value) { + case 0: /* WC */ + val = hdspm_wc_sync_check(hdspm); break; + case 9: /* TCO */ + val = hdspm_tco_sync_check(hdspm); break; + case 10 /* SYNC IN */: + val = hdspm_sync_in_sync_check(hdspm); break; + default: /* AES1 to AES8 */ + val = hdspm_aes_sync_check(hdspm, + kcontrol->private_value-1); + } + } + + if (-1 == val) + val = 3; + + ucontrol->value.enumerated.item[0] = val; return 0; } -static int snd_hdspm_get_aes_sync_check(struct snd_kcontrol *kcontrol, + + +/** + * TCO controls + **/ +static void hdspm_tco_write(struct hdspm *hdspm) +{ + unsigned int tc[4] = { 0, 0, 0, 0}; + + switch (hdspm->tco->input) { + case 0: + tc[2] |= HDSPM_TCO2_set_input_MSB; + break; + case 1: + tc[2] |= HDSPM_TCO2_set_input_LSB; + break; + default: + break; + } + + switch (hdspm->tco->framerate) { + case 1: + tc[1] |= HDSPM_TCO1_LTC_Format_LSB; + break; + case 2: + tc[1] |= HDSPM_TCO1_LTC_Format_MSB; + break; + case 3: + tc[1] |= HDSPM_TCO1_LTC_Format_MSB + + HDSPM_TCO1_set_drop_frame_flag; + break; + case 4: + tc[1] |= HDSPM_TCO1_LTC_Format_LSB + + HDSPM_TCO1_LTC_Format_MSB; + break; + case 5: + tc[1] |= HDSPM_TCO1_LTC_Format_LSB + + HDSPM_TCO1_LTC_Format_MSB + + HDSPM_TCO1_set_drop_frame_flag; + break; + default: + break; + } + + switch (hdspm->tco->wordclock) { + case 1: + tc[2] |= HDSPM_TCO2_WCK_IO_ratio_LSB; + break; + case 2: + tc[2] |= HDSPM_TCO2_WCK_IO_ratio_MSB; + break; + default: + break; + } + + switch (hdspm->tco->samplerate) { + case 1: + tc[2] |= HDSPM_TCO2_set_freq; + break; + case 2: + tc[2] |= HDSPM_TCO2_set_freq_from_app; + break; + default: + break; + } + + switch (hdspm->tco->pull) { + case 1: + tc[2] |= HDSPM_TCO2_set_pull_up; + break; + case 2: + tc[2] |= HDSPM_TCO2_set_pull_down; + break; + case 3: + tc[2] |= HDSPM_TCO2_set_pull_up + HDSPM_TCO2_set_01_4; + break; + case 4: + tc[2] |= HDSPM_TCO2_set_pull_down + HDSPM_TCO2_set_01_4; + break; + default: + break; + } + + if (1 == hdspm->tco->term) { + tc[2] |= HDSPM_TCO2_set_term_75R; + } + + hdspm_write(hdspm, HDSPM_WR_TCO, tc[0]); + hdspm_write(hdspm, HDSPM_WR_TCO+4, tc[1]); + hdspm_write(hdspm, HDSPM_WR_TCO+8, tc[2]); + hdspm_write(hdspm, HDSPM_WR_TCO+12, tc[3]); +} + + +#define HDSPM_TCO_SAMPLE_RATE(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_tco_sample_rate, \ + .get = snd_hdspm_get_tco_sample_rate, \ + .put = snd_hdspm_put_tco_sample_rate \ +} + +static int snd_hdspm_info_tco_sample_rate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[] = { "44.1 kHz", "48 kHz" }; + 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 snd_hdspm_get_tco_sample_rate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = hdspm->tco->samplerate; + + return 0; +} + +static int snd_hdspm_put_tco_sample_rate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + if (hdspm->tco->samplerate != ucontrol->value.enumerated.item[0]) { + hdspm->tco->samplerate = ucontrol->value.enumerated.item[0]; + + hdspm_tco_write(hdspm); + + return 1; + } + + return 0; +} + + +#define HDSPM_TCO_PULL(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_tco_pull, \ + .get = snd_hdspm_get_tco_pull, \ + .put = snd_hdspm_put_tco_pull \ +} + +static int snd_hdspm_info_tco_pull(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[] = { "0", "+ 0.1 %", "- 0.1 %", "+ 4 %", "- 4 %" }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 5; + + 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 snd_hdspm_get_tco_pull(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = hdspm->tco->pull; + + return 0; +} + +static int snd_hdspm_put_tco_pull(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + if (hdspm->tco->pull != ucontrol->value.enumerated.item[0]) { + hdspm->tco->pull = ucontrol->value.enumerated.item[0]; + + hdspm_tco_write(hdspm); + + return 1; + } + + return 0; +} + +#define HDSPM_TCO_WCK_CONVERSION(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_tco_wck_conversion, \ + .get = snd_hdspm_get_tco_wck_conversion, \ + .put = snd_hdspm_put_tco_wck_conversion \ +} + +static int snd_hdspm_info_tco_wck_conversion(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[] = { "1:1", "44.1 -> 48", "48 -> 44.1" }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + + 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 snd_hdspm_get_tco_wck_conversion(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = hdspm->tco->wordclock; + + return 0; +} + +static int snd_hdspm_put_tco_wck_conversion(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + if (hdspm->tco->wordclock != ucontrol->value.enumerated.item[0]) { + hdspm->tco->wordclock = ucontrol->value.enumerated.item[0]; + + hdspm_tco_write(hdspm); + + return 1; + } + + return 0; +} + + +#define HDSPM_TCO_FRAME_RATE(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_tco_frame_rate, \ + .get = snd_hdspm_get_tco_frame_rate, \ + .put = snd_hdspm_put_tco_frame_rate \ +} + +static int snd_hdspm_info_tco_frame_rate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[] = { "24 fps", "25 fps", "29.97fps", + "29.97 dfps", "30 fps", "30 dfps" }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 6; + + 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 snd_hdspm_get_tco_frame_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - int offset; struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); - offset = ucontrol->id.index - 1; - if (offset < 0 || offset >= 8) - return -EINVAL; + ucontrol->value.enumerated.item[0] = hdspm->tco->framerate; - ucontrol->value.enumerated.item[0] = - hdspm_aes_sync_check(hdspm, offset); return 0; } +static int snd_hdspm_put_tco_frame_rate(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + if (hdspm->tco->framerate != ucontrol->value.enumerated.item[0]) { + hdspm->tco->framerate = ucontrol->value.enumerated.item[0]; -static struct snd_kcontrol_new snd_hdspm_controls_madi[] = { + hdspm_tco_write(hdspm); + + return 1; + } + + return 0; +} - HDSPM_MIXER("Mixer", 0), -/* 'Sample Clock Source' complies with the alsa control naming scheme */ - HDSPM_CLOCK_SOURCE("Sample Clock Source", 0), +#define HDSPM_TCO_SYNC_SOURCE(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_tco_sync_source, \ + .get = snd_hdspm_get_tco_sync_source, \ + .put = snd_hdspm_put_tco_sync_source \ +} + +static int snd_hdspm_info_tco_sync_source(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[] = { "LTC", "Video", "WCK" }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + + 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 snd_hdspm_get_tco_sync_source(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = hdspm->tco->input; + + return 0; +} + +static int snd_hdspm_put_tco_sync_source(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + if (hdspm->tco->input != ucontrol->value.enumerated.item[0]) { + hdspm->tco->input = ucontrol->value.enumerated.item[0]; + + hdspm_tco_write(hdspm); + + return 1; + } + + return 0; +} + + +#define HDSPM_TCO_WORD_TERM(xname, xindex) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_hdspm_info_tco_word_term, \ + .get = snd_hdspm_get_tco_word_term, \ + .put = snd_hdspm_put_tco_word_term \ +} + +static int snd_hdspm_info_tco_word_term(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; +} + + +static int snd_hdspm_get_tco_word_term(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = hdspm->tco->term; + + return 0; +} + + +static int snd_hdspm_put_tco_word_term(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hdspm *hdspm = snd_kcontrol_chip(kcontrol); + + if (hdspm->tco->term != ucontrol->value.enumerated.item[0]) { + hdspm->tco->term = ucontrol->value.enumerated.item[0]; + + hdspm_tco_write(hdspm); + + return 1; + } + + return 0; +} + + + + +static struct snd_kcontrol_new snd_hdspm_controls_madi[] = { + HDSPM_MIXER("Mixer", 0), + HDSPM_INTERNAL_CLOCK("Internal Clock", 0), HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0), HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0), HDSPM_AUTOSYNC_REF("AutoSync Reference", 0), HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0), -/* 'External Rate' complies with the alsa control naming scheme */ - HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0), - HDSPM_WC_SYNC_CHECK("Word Clock Lock Status", 0), - HDSPM_MADI_SYNC_CHECK("MADI Sync Lock Status", 0), + HDSPM_SYNC_CHECK("WC SyncCheck", 0), + HDSPM_SYNC_CHECK("MADI SyncCheck", 1), + HDSPM_SYNC_CHECK("TCO SyncCHeck", 2), + HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 3), HDSPM_LINE_OUT("Line Out", 0), HDSPM_TX_64("TX 64 channels mode", 0), HDSPM_C_TMS("Clear Track Marker", 0), HDSPM_SAFE_MODE("Safe Mode", 0), - HDSPM_INPUT_SELECT("Input Select", 0), + HDSPM_INPUT_SELECT("Input Select", 0) }; -static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = { +static struct snd_kcontrol_new snd_hdspm_controls_madiface[] = { HDSPM_MIXER("Mixer", 0), -/* 'Sample Clock Source' complies with the alsa control naming scheme */ - HDSPM_CLOCK_SOURCE("Sample Clock Source", 0), + HDSPM_INTERNAL_CLOCK("Internal Clock", 0), + HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0), + HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0), + HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0), + HDSPM_SYNC_CHECK("MADI SyncCheck", 0), + HDSPM_TX_64("TX 64 channels mode", 0), + HDSPM_C_TMS("Clear Track Marker", 0), + HDSPM_SAFE_MODE("Safe Mode", 0) +}; +static struct snd_kcontrol_new snd_hdspm_controls_aio[] = { + HDSPM_MIXER("Mixer", 0), + HDSPM_INTERNAL_CLOCK("Internal Clock", 0), HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0), HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0), HDSPM_AUTOSYNC_REF("AutoSync Reference", 0), HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0), -/* 'External Rate' complies with the alsa control naming scheme */ HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0), - HDSPM_WC_SYNC_CHECK("Word Clock Lock Status", 0), -/* HDSPM_AES_SYNC_CHECK("AES Lock Status", 0),*/ /* created in snd_hdspm_create_controls() */ + HDSPM_SYNC_CHECK("WC SyncCheck", 0), + HDSPM_SYNC_CHECK("AES SyncCheck", 1), + HDSPM_SYNC_CHECK("SPDIF SyncCheck", 2), + HDSPM_SYNC_CHECK("ADAT SyncCheck", 3), + HDSPM_SYNC_CHECK("TCO SyncCheck", 4), + HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 5), + HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES Frequency", 1), + HDSPM_AUTOSYNC_SAMPLE_RATE("SPDIF Frequency", 2), + HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT Frequency", 3), + HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 4), + HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 5) + + /* + HDSPM_INPUT_SELECT("Input Select", 0), + HDSPM_SPDIF_OPTICAL("SPDIF Out Optical", 0), + HDSPM_PROFESSIONAL("SPDIF Out Professional", 0); + HDSPM_SPDIF_IN("SPDIF In", 0); + HDSPM_BREAKOUT_CABLE("Breakout Cable", 0); + HDSPM_INPUT_LEVEL("Input Level", 0); + HDSPM_OUTPUT_LEVEL("Output Level", 0); + HDSPM_PHONES("Phones", 0); + */ +}; + +static struct snd_kcontrol_new snd_hdspm_controls_raydat[] = { + HDSPM_MIXER("Mixer", 0), + HDSPM_INTERNAL_CLOCK("Internal Clock", 0), + HDSPM_SYSTEM_CLOCK_MODE("Clock Mode", 0), + HDSPM_PREF_SYNC_REF("Pref Sync Ref", 0), + HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0), + HDSPM_SYNC_CHECK("WC SyncCheck", 0), + HDSPM_SYNC_CHECK("AES SyncCheck", 1), + HDSPM_SYNC_CHECK("SPDIF SyncCheck", 2), + HDSPM_SYNC_CHECK("ADAT1 SyncCheck", 3), + HDSPM_SYNC_CHECK("ADAT2 SyncCheck", 4), + HDSPM_SYNC_CHECK("ADAT3 SyncCheck", 5), + HDSPM_SYNC_CHECK("ADAT4 SyncCheck", 6), + HDSPM_SYNC_CHECK("TCO SyncCheck", 7), + HDSPM_SYNC_CHECK("SYNC IN SyncCheck", 8), + HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES Frequency", 1), + HDSPM_AUTOSYNC_SAMPLE_RATE("SPDIF Frequency", 2), + HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT1 Frequency", 3), + HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT2 Frequency", 4), + HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT3 Frequency", 5), + HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT4 Frequency", 6), + HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 7), + HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 8) +}; + +static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = { + HDSPM_MIXER("Mixer", 0), + HDSPM_INTERNAL_CLOCK("Internal Clock", 0), + HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0), + HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0), + HDSPM_AUTOSYNC_REF("AutoSync Reference", 0), + HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0), + HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0), + HDSPM_SYNC_CHECK("WC Sync Check", 0), + HDSPM_SYNC_CHECK("AES1 Sync Check", 1), + HDSPM_SYNC_CHECK("AES2 Sync Check", 2), + HDSPM_SYNC_CHECK("AES3 Sync Check", 3), + HDSPM_SYNC_CHECK("AES4 Sync Check", 4), + HDSPM_SYNC_CHECK("AES5 Sync Check", 5), + HDSPM_SYNC_CHECK("AES6 Sync Check", 6), + HDSPM_SYNC_CHECK("AES7 Sync Check", 7), + HDSPM_SYNC_CHECK("AES8 Sync Check", 8), + HDSPM_SYNC_CHECK("TCO Sync Check", 9), + HDSPM_SYNC_CHECK("SYNC IN Sync Check", 10), + HDSPM_AUTOSYNC_SAMPLE_RATE("WC Frequency", 0), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES1 Frequency", 1), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES2 Frequency", 2), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES3 Frequency", 3), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES4 Frequency", 4), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES5 Frequency", 5), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES6 Frequency", 6), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES7 Frequency", 7), + HDSPM_AUTOSYNC_SAMPLE_RATE("AES8 Frequency", 8), + HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 9), + HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 10), HDSPM_LINE_OUT("Line Out", 0), HDSPM_EMPHASIS("Emphasis", 0), HDSPM_DOLBY("Non Audio", 0), @@ -2842,6 +4400,19 @@ static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = { HDSPM_QS_WIRE("Quad Speed Wire Mode", 0), }; + + +/* Control elements for the optional TCO module */ +static struct snd_kcontrol_new snd_hdspm_controls_tco[] = { + HDSPM_TCO_SAMPLE_RATE("TCO Sample Rate", 0), + HDSPM_TCO_PULL("TCO Pull", 0), + HDSPM_TCO_WCK_CONVERSION("TCO WCK Conversion", 0), + HDSPM_TCO_FRAME_RATE("TCO Frame Rate", 0), + HDSPM_TCO_SYNC_SOURCE("TCO Sync Source", 0), + HDSPM_TCO_WORD_TERM("TCO Word Term", 0) +}; + + static struct snd_kcontrol_new snd_hdspm_playback_mixer = HDSPM_PLAYBACK_MIXER; @@ -2849,78 +4420,76 @@ static int hdspm_update_simple_mixer_controls(struct hdspm * hdspm) { int i; - for (i = hdspm->ds_channels; i < hdspm->ss_channels; ++i) { + for (i = hdspm->ds_out_channels; i < hdspm->ss_out_channels; ++i) { if (hdspm->system_sample_rate > 48000) { hdspm->playback_mixer_ctls[i]->vd[0].access = - SNDRV_CTL_ELEM_ACCESS_INACTIVE | - SNDRV_CTL_ELEM_ACCESS_READ | - SNDRV_CTL_ELEM_ACCESS_VOLATILE; + SNDRV_CTL_ELEM_ACCESS_INACTIVE | + SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE; } else { hdspm->playback_mixer_ctls[i]->vd[0].access = - SNDRV_CTL_ELEM_ACCESS_READWRITE | - SNDRV_CTL_ELEM_ACCESS_VOLATILE; + SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_VOLATILE; } snd_ctl_notify(hdspm->card, SNDRV_CTL_EVENT_MASK_VALUE | - SNDRV_CTL_EVENT_MASK_INFO, - &hdspm->playback_mixer_ctls[i]->id); + SNDRV_CTL_EVENT_MASK_INFO, + &hdspm->playback_mixer_ctls[i]->id); } return 0; } -static int snd_hdspm_create_controls(struct snd_card *card, struct hdspm * hdspm) +static int snd_hdspm_create_controls(struct snd_card *card, + struct hdspm *hdspm) { unsigned int idx, limit; int err; struct snd_kcontrol *kctl; + struct snd_kcontrol_new *list = NULL; - /* add control list first */ - if (hdspm->is_aes32) { - struct snd_kcontrol_new aes_sync_ctl = - HDSPM_AES_SYNC_CHECK("AES Lock Status", 0); + switch (hdspm->io_type) { + case MADI: + list = snd_hdspm_controls_madi; + limit = ARRAY_SIZE(snd_hdspm_controls_madi); + break; + case MADIface: + list = snd_hdspm_controls_madiface; + limit = ARRAY_SIZE(snd_hdspm_controls_madiface); + break; + case AIO: + list = snd_hdspm_controls_aio; + limit = ARRAY_SIZE(snd_hdspm_controls_aio); + break; + case RayDAT: + list = snd_hdspm_controls_raydat; + limit = ARRAY_SIZE(snd_hdspm_controls_raydat); + break; + case AES32: + list = snd_hdspm_controls_aes32; + limit = ARRAY_SIZE(snd_hdspm_controls_aes32); + break; + } - for (idx = 0; idx < ARRAY_SIZE(snd_hdspm_controls_aes32); - idx++) { - err = snd_ctl_add(card, - snd_ctl_new1(&snd_hdspm_controls_aes32[idx], - hdspm)); - if (err < 0) - return err; - } - for (idx = 1; idx <= 8; idx++) { - aes_sync_ctl.index = idx; - err = snd_ctl_add(card, - snd_ctl_new1(&aes_sync_ctl, hdspm)); - if (err < 0) - return err; - } - } else { - for (idx = 0; idx < ARRAY_SIZE(snd_hdspm_controls_madi); - idx++) { + if (NULL != list) { + for (idx = 0; idx < limit; idx++) { err = snd_ctl_add(card, - snd_ctl_new1(&snd_hdspm_controls_madi[idx], - hdspm)); + snd_ctl_new1(&list[idx], hdspm)); if (err < 0) return err; } } - /* Channel playback mixer as default control - Note: the whole matrix would be 128*HDSPM_MIXER_CHANNELS Faders, - thats too * big for any alsamixer they are accessible via special - IOCTL on hwdep and the mixer 2dimensional mixer control - */ + /* create simple 1:1 playback mixer controls */ snd_hdspm_playback_mixer.name = "Chn"; - limit = HDSPM_MAX_CHANNELS; - - /* The index values are one greater than the channel ID so that - * alsamixer will display them correctly. We want to use the index - * for fast lookup of the relevant channel, but if we use it at all, - * most ALSA software does the wrong thing with it ... - */ - + if (hdspm->system_sample_rate >= 128000) { + limit = hdspm->qs_out_channels; + } else if (hdspm->system_sample_rate >= 64000) { + limit = hdspm->ds_out_channels; + } else { + limit = hdspm->ss_out_channels; + } for (idx = 0; idx < limit; ++idx) { snd_hdspm_playback_mixer.index = idx + 1; kctl = snd_ctl_new1(&snd_hdspm_playback_mixer, hdspm); @@ -2930,11 +4499,24 @@ static int snd_hdspm_create_controls(struct snd_card *card, struct hdspm * hdspm hdspm->playback_mixer_ctls[idx] = kctl; } + + if (hdspm->tco) { + /* add tco control elements */ + list = snd_hdspm_controls_tco; + limit = ARRAY_SIZE(snd_hdspm_controls_tco); + for (idx = 0; idx < limit; idx++) { + err = snd_ctl_add(card, + snd_ctl_new1(&list[idx], hdspm)); + if (err < 0) + return err; + } + } + return 0; } /*------------------------------------------------------------ - /proc interface + /proc interface ------------------------------------------------------------*/ static void @@ -2942,72 +4524,178 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry, struct snd_info_buffer *buffer) { struct hdspm *hdspm = entry->private_data; - unsigned int status; - unsigned int status2; + unsigned int status, status2, control, freq; + char *pref_sync_ref; char *autosync_ref; char *system_clock_mode; - char *clock_source; char *insel; - char *syncref; int x, x2; + /* TCO stuff */ + int a, ltc, frames, seconds, minutes, hours; + unsigned int period; + u64 freq_const = 0; + u32 rate; + status = hdspm_read(hdspm, HDSPM_statusRegister); status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + control = hdspm->control_register; + freq = hdspm_read(hdspm, HDSPM_timecodeRegister); snd_iprintf(buffer, "%s (Card #%d) Rev.%x Status2first3bits: %x\n", - hdspm->card_name, hdspm->card->number + 1, - hdspm->firmware_rev, - (status2 & HDSPM_version0) | - (status2 & HDSPM_version1) | (status2 & - HDSPM_version2)); + hdspm->card_name, hdspm->card->number + 1, + hdspm->firmware_rev, + (status2 & HDSPM_version0) | + (status2 & HDSPM_version1) | (status2 & + HDSPM_version2)); + + snd_iprintf(buffer, "HW Serial: 0x%06x%06x\n", + (hdspm_read(hdspm, HDSPM_midiStatusIn1)>>8) & 0xFFFFFF, + (hdspm_read(hdspm, HDSPM_midiStatusIn0)>>8) & 0xFFFFFF); snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n", - hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase); + hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase); snd_iprintf(buffer, "--- System ---\n"); snd_iprintf(buffer, - "IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n", - status & HDSPM_audioIRQPending, - (status & HDSPM_midi0IRQPending) ? 1 : 0, - (status & HDSPM_midi1IRQPending) ? 1 : 0, - hdspm->irq_count); + "IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n", + status & HDSPM_audioIRQPending, + (status & HDSPM_midi0IRQPending) ? 1 : 0, + (status & HDSPM_midi1IRQPending) ? 1 : 0, + hdspm->irq_count); snd_iprintf(buffer, - "HW pointer: id = %d, rawptr = %d (%d->%d) " - "estimated= %ld (bytes)\n", - ((status & HDSPM_BufferID) ? 1 : 0), - (status & HDSPM_BufferPositionMask), - (status & HDSPM_BufferPositionMask) % - (2 * (int)hdspm->period_bytes), - ((status & HDSPM_BufferPositionMask) - 64) % - (2 * (int)hdspm->period_bytes), - (long) hdspm_hw_pointer(hdspm) * 4); + "HW pointer: id = %d, rawptr = %d (%d->%d) " + "estimated= %ld (bytes)\n", + ((status & HDSPM_BufferID) ? 1 : 0), + (status & HDSPM_BufferPositionMask), + (status & HDSPM_BufferPositionMask) % + (2 * (int)hdspm->period_bytes), + ((status & HDSPM_BufferPositionMask) - 64) % + (2 * (int)hdspm->period_bytes), + (long) hdspm_hw_pointer(hdspm) * 4); snd_iprintf(buffer, - "MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n", - hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF, - hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF, - hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF, - hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF); + "MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n", + hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF); snd_iprintf(buffer, - "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, " - "status2=0x%x\n", - hdspm->control_register, hdspm->control2_register, - status, status2); + "MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n", + hdspm_read(hdspm, HDSPM_midiStatusIn2) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusOut2) & 0xFF); + snd_iprintf(buffer, + "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, " + "status2=0x%x\n", + hdspm->control_register, hdspm->control2_register, + status, status2); + if (status & HDSPM_tco_detect) { + snd_iprintf(buffer, "TCO module detected.\n"); + a = hdspm_read(hdspm, HDSPM_RD_TCO+4); + if (a & HDSPM_TCO1_LTC_Input_valid) { + snd_iprintf(buffer, " LTC valid, "); + switch (a & (HDSPM_TCO1_LTC_Format_LSB | + HDSPM_TCO1_LTC_Format_MSB)) { + case 0: + snd_iprintf(buffer, "24 fps, "); + break; + case HDSPM_TCO1_LTC_Format_LSB: + snd_iprintf(buffer, "25 fps, "); + break; + case HDSPM_TCO1_LTC_Format_MSB: + snd_iprintf(buffer, "29.97 fps, "); + break; + default: + snd_iprintf(buffer, "30 fps, "); + break; + } + if (a & HDSPM_TCO1_set_drop_frame_flag) { + snd_iprintf(buffer, "drop frame\n"); + } else { + snd_iprintf(buffer, "full frame\n"); + } + } else { + snd_iprintf(buffer, " no LTC\n"); + } + if (a & HDSPM_TCO1_Video_Input_Format_NTSC) { + snd_iprintf(buffer, " Video: NTSC\n"); + } else if (a & HDSPM_TCO1_Video_Input_Format_PAL) { + snd_iprintf(buffer, " Video: PAL\n"); + } else { + snd_iprintf(buffer, " No video\n"); + } + if (a & HDSPM_TCO1_TCO_lock) { + snd_iprintf(buffer, " Sync: lock\n"); + } else { + snd_iprintf(buffer, " Sync: no lock\n"); + } + + switch (hdspm->io_type) { + case MADI: + case AES32: + freq_const = 110069313433624ULL; + break; + case RayDAT: + case AIO: + freq_const = 104857600000000ULL; + break; + case MADIface: + break; /* no TCO possible */ + } + + period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ); + snd_iprintf(buffer, " period: %u\n", period); + + + /* rate = freq_const/period; */ + rate = div_u64(freq_const, period); + + if (control & HDSPM_QuadSpeed) { + rate *= 4; + } else if (control & HDSPM_DoubleSpeed) { + rate *= 2; + } + + snd_iprintf(buffer, " Frequency: %u Hz\n", + (unsigned int) rate); + + ltc = hdspm_read(hdspm, HDSPM_RD_TCO); + frames = ltc & 0xF; + ltc >>= 4; + frames += (ltc & 0x3) * 10; + ltc >>= 4; + seconds = ltc & 0xF; + ltc >>= 4; + seconds += (ltc & 0x7) * 10; + ltc >>= 4; + minutes = ltc & 0xF; + ltc >>= 4; + minutes += (ltc & 0x7) * 10; + ltc >>= 4; + hours = ltc & 0xF; + ltc >>= 4; + hours += (ltc & 0x3) * 10; + snd_iprintf(buffer, + " LTC In: %02d:%02d:%02d:%02d\n", + hours, minutes, seconds, frames); + + } else { + snd_iprintf(buffer, "No TCO module detected.\n"); + } snd_iprintf(buffer, "--- Settings ---\n"); x = 1 << (6 + hdspm_decode_latency(hdspm->control_register & - HDSPM_LatencyMask)); + HDSPM_LatencyMask)); snd_iprintf(buffer, - "Size (Latency): %d samples (2 periods of %lu bytes)\n", - x, (unsigned long) hdspm->period_bytes); + "Size (Latency): %d samples (2 periods of %lu bytes)\n", + x, (unsigned long) hdspm->period_bytes); - snd_iprintf(buffer, "Line out: %s, Precise Pointer: %s\n", - (hdspm->control_register & HDSPM_LineOut) ? "on " : "off", - (hdspm->precise_ptr) ? "on" : "off"); + snd_iprintf(buffer, "Line out: %s\n", + (hdspm->control_register & HDSPM_LineOut) ? "on " : "off"); switch (hdspm->control_register & HDSPM_InputMask) { case HDSPM_InputOptical: @@ -3017,63 +4705,22 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry, insel = "Coaxial"; break; default: - insel = "Unknown"; - } - - switch (hdspm->control_register & HDSPM_SyncRefMask) { - case HDSPM_SyncRef_Word: - syncref = "WordClock"; - break; - case HDSPM_SyncRef_MADI: - syncref = "MADI"; - break; - default: - syncref = "Unknown"; + insel = "Unkown"; } - snd_iprintf(buffer, "Inputsel = %s, SyncRef = %s\n", insel, - syncref); snd_iprintf(buffer, - "ClearTrackMarker = %s, Transmit in %s Channel Mode, " - "Auto Input %s\n", - (hdspm-> - control_register & HDSPM_clr_tms) ? "on" : "off", - (hdspm-> - control_register & HDSPM_TX_64ch) ? "64" : "56", - (hdspm-> - control_register & HDSPM_AutoInp) ? "on" : "off"); + "ClearTrackMarker = %s, Transmit in %s Channel Mode, " + "Auto Input %s\n", + (hdspm->control_register & HDSPM_clr_tms) ? "on" : "off", + (hdspm->control_register & HDSPM_TX_64ch) ? "64" : "56", + (hdspm->control_register & HDSPM_AutoInp) ? "on" : "off"); + - switch (hdspm_clock_source(hdspm)) { - case HDSPM_CLOCK_SOURCE_AUTOSYNC: - clock_source = "AutoSync"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ: - clock_source = "Internal 32 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ: - clock_source = "Internal 44.1 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ: - clock_source = "Internal 48 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ: - clock_source = "Internal 64 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ: - clock_source = "Internal 88.2 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ: - clock_source = "Internal 96 kHz"; - break; - default: - clock_source = "Error"; - } - snd_iprintf(buffer, "Sample Clock Source: %s\n", clock_source); if (!(hdspm->control_register & HDSPM_ClockModeMaster)) - system_clock_mode = "Slave"; + system_clock_mode = "AutoSync"; else system_clock_mode = "Master"; - snd_iprintf(buffer, "System Clock Mode: %s\n", system_clock_mode); + snd_iprintf(buffer, "AutoSync Reference: %s\n", system_clock_mode); switch (hdspm_pref_sync_ref(hdspm)) { case HDSPM_SYNC_FROM_WORD: @@ -3082,15 +4729,21 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry, case HDSPM_SYNC_FROM_MADI: pref_sync_ref = "MADI Sync"; break; + case HDSPM_SYNC_FROM_TCO: + pref_sync_ref = "TCO"; + break; + case HDSPM_SYNC_FROM_SYNC_IN: + pref_sync_ref = "Sync In"; + break; default: pref_sync_ref = "XXXX Clock"; break; } snd_iprintf(buffer, "Preferred Sync Reference: %s\n", - pref_sync_ref); + pref_sync_ref); snd_iprintf(buffer, "System Clock Frequency: %d\n", - hdspm->system_sample_rate); + hdspm->system_sample_rate); snd_iprintf(buffer, "--- Status:\n"); @@ -3099,12 +4752,18 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry, x2 = status2 & HDSPM_wcSync; snd_iprintf(buffer, "Inputs MADI=%s, WordClock=%s\n", - (status & HDSPM_madiLock) ? (x ? "Sync" : "Lock") : - "NoLock", - (status2 & HDSPM_wcLock) ? (x2 ? "Sync" : "Lock") : - "NoLock"); + (status & HDSPM_madiLock) ? (x ? "Sync" : "Lock") : + "NoLock", + (status2 & HDSPM_wcLock) ? (x2 ? "Sync" : "Lock") : + "NoLock"); switch (hdspm_autosync_ref(hdspm)) { + case HDSPM_AUTOSYNC_FROM_SYNC_IN: + autosync_ref = "Sync In"; + break; + case HDSPM_AUTOSYNC_FROM_TCO: + autosync_ref = "TCO"; + break; case HDSPM_AUTOSYNC_FROM_WORD: autosync_ref = "Word Clock"; break; @@ -3119,15 +4778,15 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry, break; } snd_iprintf(buffer, - "AutoSync: Reference= %s, Freq=%d (MADI = %d, Word = %d)\n", - autosync_ref, hdspm_external_sample_rate(hdspm), - (status & HDSPM_madiFreqMask) >> 22, - (status2 & HDSPM_wcFreqMask) >> 5); + "AutoSync: Reference= %s, Freq=%d (MADI = %d, Word = %d)\n", + autosync_ref, hdspm_external_sample_rate(hdspm), + (status & HDSPM_madiFreqMask) >> 22, + (status2 & HDSPM_wcFreqMask) >> 5); snd_iprintf(buffer, "Input: %s, Mode=%s\n", - (status & HDSPM_AB_int) ? "Coax" : "Optical", - (status & HDSPM_RX_64ch) ? "64 channels" : - "56 channels"); + (status & HDSPM_AB_int) ? "Coax" : "Optical", + (status & HDSPM_RX_64ch) ? "64 channels" : + "56 channels"); snd_iprintf(buffer, "\n"); } @@ -3142,8 +4801,6 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry, unsigned int timecode; int pref_syncref; char *autosync_ref; - char *system_clock_mode; - char *clock_source; int x; status = hdspm_read(hdspm, HDSPM_statusRegister); @@ -3183,24 +4840,27 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry, hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF, hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF); snd_iprintf(buffer, - "Register: ctrl1=0x%x, status1=0x%x, status2=0x%x, " - "timecode=0x%x\n", - hdspm->control_register, - status, status2, timecode); + "MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n", + hdspm_read(hdspm, HDSPM_midiStatusIn2) & 0xFF, + hdspm_read(hdspm, HDSPM_midiStatusOut2) & 0xFF); + snd_iprintf(buffer, + "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, " + "status2=0x%x\n", + hdspm->control_register, hdspm->control2_register, + status, status2); snd_iprintf(buffer, "--- Settings ---\n"); x = 1 << (6 + hdspm_decode_latency(hdspm->control_register & - HDSPM_LatencyMask)); + HDSPM_LatencyMask)); snd_iprintf(buffer, "Size (Latency): %d samples (2 periods of %lu bytes)\n", x, (unsigned long) hdspm->period_bytes); - snd_iprintf(buffer, "Line out: %s, Precise Pointer: %s\n", + snd_iprintf(buffer, "Line out: %s\n", (hdspm-> - control_register & HDSPM_LineOut) ? "on " : "off", - (hdspm->precise_ptr) ? "on" : "off"); + control_register & HDSPM_LineOut) ? "on " : "off"); snd_iprintf(buffer, "ClearTrackMarker %s, Emphasis %s, Dolby %s\n", @@ -3211,46 +4871,6 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry, (hdspm-> control_register & HDSPM_Dolby) ? "on" : "off"); - switch (hdspm_clock_source(hdspm)) { - case HDSPM_CLOCK_SOURCE_AUTOSYNC: - clock_source = "AutoSync"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ: - clock_source = "Internal 32 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ: - clock_source = "Internal 44.1 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ: - clock_source = "Internal 48 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ: - clock_source = "Internal 64 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ: - clock_source = "Internal 88.2 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ: - clock_source = "Internal 96 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_128KHZ: - clock_source = "Internal 128 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_176_4KHZ: - clock_source = "Internal 176.4 kHz"; - break; - case HDSPM_CLOCK_SOURCE_INTERNAL_192KHZ: - clock_source = "Internal 192 kHz"; - break; - default: - clock_source = "Error"; - } - snd_iprintf(buffer, "Sample Clock Source: %s\n", clock_source); - if (!(hdspm->control_register & HDSPM_ClockModeMaster)) - system_clock_mode = "Slave"; - else - system_clock_mode = "Master"; - snd_iprintf(buffer, "System Clock Mode: %s\n", system_clock_mode); pref_syncref = hdspm_pref_sync_ref(hdspm); if (pref_syncref == 0) @@ -3274,38 +4894,108 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry, snd_iprintf(buffer, "--- Status:\n"); snd_iprintf(buffer, "Word: %s Frequency: %d\n", - (status & HDSPM_AES32_wcLock)? "Sync " : "No Lock", + (status & HDSPM_AES32_wcLock) ? "Sync " : "No Lock", HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) & 0xF)); for (x = 0; x < 8; x++) { snd_iprintf(buffer, "AES%d: %s Frequency: %d\n", x+1, (status2 & (HDSPM_LockAES >> x)) ? - "Sync ": "No Lock", + "Sync " : "No Lock", HDSPM_bit2freq((timecode >> (4*x)) & 0xF)); } switch (hdspm_autosync_ref(hdspm)) { - case HDSPM_AES32_AUTOSYNC_FROM_NONE: autosync_ref="None"; break; - case HDSPM_AES32_AUTOSYNC_FROM_WORD: autosync_ref="Word Clock"; break; - case HDSPM_AES32_AUTOSYNC_FROM_AES1: autosync_ref="AES1"; break; - case HDSPM_AES32_AUTOSYNC_FROM_AES2: autosync_ref="AES2"; break; - case HDSPM_AES32_AUTOSYNC_FROM_AES3: autosync_ref="AES3"; break; - case HDSPM_AES32_AUTOSYNC_FROM_AES4: autosync_ref="AES4"; break; - case HDSPM_AES32_AUTOSYNC_FROM_AES5: autosync_ref="AES5"; break; - case HDSPM_AES32_AUTOSYNC_FROM_AES6: autosync_ref="AES6"; break; - case HDSPM_AES32_AUTOSYNC_FROM_AES7: autosync_ref="AES7"; break; - case HDSPM_AES32_AUTOSYNC_FROM_AES8: autosync_ref="AES8"; break; - default: autosync_ref = "---"; break; + case HDSPM_AES32_AUTOSYNC_FROM_NONE: + autosync_ref = "None"; break; + case HDSPM_AES32_AUTOSYNC_FROM_WORD: + autosync_ref = "Word Clock"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES1: + autosync_ref = "AES1"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES2: + autosync_ref = "AES2"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES3: + autosync_ref = "AES3"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES4: + autosync_ref = "AES4"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES5: + autosync_ref = "AES5"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES6: + autosync_ref = "AES6"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES7: + autosync_ref = "AES7"; break; + case HDSPM_AES32_AUTOSYNC_FROM_AES8: + autosync_ref = "AES8"; break; + default: + autosync_ref = "---"; break; } snd_iprintf(buffer, "AutoSync ref = %s\n", autosync_ref); snd_iprintf(buffer, "\n"); } +static void +snd_hdspm_proc_read_raydat(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct hdspm *hdspm = entry->private_data; + unsigned int status1, status2, status3, control, i; + unsigned int lock, sync; + + status1 = hdspm_read(hdspm, HDSPM_RD_STATUS_1); /* s1 */ + status2 = hdspm_read(hdspm, HDSPM_RD_STATUS_2); /* freq */ + status3 = hdspm_read(hdspm, HDSPM_RD_STATUS_3); /* s2 */ + + control = hdspm->control_register; + + snd_iprintf(buffer, "STATUS1: 0x%08x\n", status1); + snd_iprintf(buffer, "STATUS2: 0x%08x\n", status2); + snd_iprintf(buffer, "STATUS3: 0x%08x\n", status3); + + + snd_iprintf(buffer, "\n*** CLOCK MODE\n\n"); + + snd_iprintf(buffer, "Clock mode : %s\n", + (hdspm_system_clock_mode(hdspm) == 0) ? "master" : "slave"); + snd_iprintf(buffer, "System frequency: %d Hz\n", + hdspm_get_system_sample_rate(hdspm)); + + snd_iprintf(buffer, "\n*** INPUT STATUS\n\n"); + + lock = 0x1; + sync = 0x100; + + for (i = 0; i < 8; i++) { + snd_iprintf(buffer, "s1_input %d: Lock %d, Sync %d, Freq %s\n", + i, + (status1 & lock) ? 1 : 0, + (status1 & sync) ? 1 : 0, + texts_freq[(status2 >> (i * 4)) & 0xF]); + + lock = lock<<1; + sync = sync<<1; + } + + snd_iprintf(buffer, "WC input: Lock %d, Sync %d, Freq %s\n", + (status1 & 0x1000000) ? 1 : 0, + (status1 & 0x2000000) ? 1 : 0, + texts_freq[(status1 >> 16) & 0xF]); + + snd_iprintf(buffer, "TCO input: Lock %d, Sync %d, Freq %s\n", + (status1 & 0x4000000) ? 1 : 0, + (status1 & 0x8000000) ? 1 : 0, + texts_freq[(status1 >> 20) & 0xF]); + + snd_iprintf(buffer, "SYNC IN: Lock %d, Sync %d, Freq %s\n", + (status3 & 0x400) ? 1 : 0, + (status3 & 0x800) ? 1 : 0, + texts_freq[(status2 >> 12) & 0xF]); + +} + #ifdef CONFIG_SND_DEBUG static void -snd_hdspm_proc_read_debug(struct snd_info_entry * entry, +snd_hdspm_proc_read_debug(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct hdspm *hdspm = entry->private_data; @@ -3322,16 +5012,68 @@ snd_hdspm_proc_read_debug(struct snd_info_entry * entry, #endif +static void snd_hdspm_proc_ports_in(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct hdspm *hdspm = entry->private_data; + int i; + + snd_iprintf(buffer, "# generated by hdspm\n"); + + for (i = 0; i < hdspm->max_channels_in; i++) { + snd_iprintf(buffer, "%d=%s\n", i+1, hdspm->port_names_in[i]); + } +} -static void __devinit snd_hdspm_proc_init(struct hdspm * hdspm) +static void snd_hdspm_proc_ports_out(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct hdspm *hdspm = entry->private_data; + int i; + + snd_iprintf(buffer, "# generated by hdspm\n"); + + for (i = 0; i < hdspm->max_channels_out; i++) { + snd_iprintf(buffer, "%d=%s\n", i+1, hdspm->port_names_out[i]); + } +} + + +static void __devinit snd_hdspm_proc_init(struct hdspm *hdspm) { struct snd_info_entry *entry; - if (!snd_card_proc_new(hdspm->card, "hdspm", &entry)) - snd_info_set_text_ops(entry, hdspm, - hdspm->is_aes32 ? - snd_hdspm_proc_read_aes32 : - snd_hdspm_proc_read_madi); + if (!snd_card_proc_new(hdspm->card, "hdspm", &entry)) { + switch (hdspm->io_type) { + case AES32: + snd_info_set_text_ops(entry, hdspm, + snd_hdspm_proc_read_aes32); + break; + case MADI: + snd_info_set_text_ops(entry, hdspm, + snd_hdspm_proc_read_madi); + break; + case MADIface: + /* snd_info_set_text_ops(entry, hdspm, + snd_hdspm_proc_read_madiface); */ + break; + case RayDAT: + snd_info_set_text_ops(entry, hdspm, + snd_hdspm_proc_read_raydat); + break; + case AIO: + break; + } + } + + if (!snd_card_proc_new(hdspm->card, "ports.in", &entry)) { + snd_info_set_text_ops(entry, hdspm, snd_hdspm_proc_ports_in); + } + + if (!snd_card_proc_new(hdspm->card, "ports.out", &entry)) { + snd_info_set_text_ops(entry, hdspm, snd_hdspm_proc_ports_out); + } + #ifdef CONFIG_SND_DEBUG /* debug file to read all hdspm registers */ if (!snd_card_proc_new(hdspm->card, "debug", &entry)) @@ -3341,47 +5083,48 @@ static void __devinit snd_hdspm_proc_init(struct hdspm * hdspm) } /*------------------------------------------------------------ - hdspm intitialize + hdspm intitialize ------------------------------------------------------------*/ 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 initialization). - */ + */ /* set defaults: */ - if (hdspm->is_aes32) + hdspm->settings_register = 0; + + switch (hdspm->io_type) { + case MADI: + case MADIface: + hdspm->control_register = + 0x2 + 0x8 + 0x10 + 0x80 + 0x400 + 0x4000 + 0x1000000; + break; + + case RayDAT: + case AIO: + hdspm->settings_register = 0x1 + 0x1000; + /* Magic values are: LAT_0, LAT_2, Master, freq1, tx64ch, inp_0, + * line_out */ + hdspm->control_register = + 0x2 + 0x8 + 0x10 + 0x80 + 0x400 + 0x4000 + 0x1000000; + break; + + case AES32: hdspm->control_register = HDSPM_ClockModeMaster | /* Master Cloack Mode on */ - hdspm_encode_latency(7) | /* latency maximum = - * 8192 samples - */ + hdspm_encode_latency(7) | /* latency max=8192samples */ HDSPM_SyncRef0 | /* AES1 is syncclock */ HDSPM_LineOut | /* Analog output in */ HDSPM_Professional; /* Professional mode */ - else - hdspm->control_register = - HDSPM_ClockModeMaster | /* Master Cloack Mode on */ - hdspm_encode_latency(7) | /* latency maximum = - * 8192 samples - */ - HDSPM_InputCoaxial | /* Input Coax not Optical */ - HDSPM_SyncRef_MADI | /* Madi is syncclock */ - HDSPM_LineOut | /* Analog output in */ - HDSPM_TX_64ch | /* transmit in 64ch mode */ - HDSPM_AutoInp; /* AutoInput chossing (takeover) */ - - /* ! HDSPM_Frequency0|HDSPM_Frequency1 = 44.1khz */ - /* ! HDSPM_DoubleSpeed HDSPM_QuadSpeed = normal speed */ - /* ! HDSPM_clr_tms = do not clear bits in track marks */ + break; + } hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); - if (!hdspm->is_aes32) { + if (AES32 == hdspm->io_type) { /* No control2 register for AES32 */ #ifdef SNDRV_BIG_ENDIAN hdspm->control2_register = HDSPM_BIGENDIAN_MODE; @@ -3397,57 +5140,59 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm) all_in_all_mixer(hdspm, 0 * UNITY_GAIN); - if (line_outs_monitor[hdspm->dev]) { - - snd_printk(KERN_INFO "HDSPM: " - "sending all playback streams to line outs.\n"); - - for (i = 0; i < HDSPM_MIXER_CHANNELS; i++) { - if (hdspm_write_pb_gain(hdspm, i, i, UNITY_GAIN)) - return -EIO; - } + if (hdspm->io_type == AIO || hdspm->io_type == RayDAT) { + hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register); } /* set a default rate so that the channel map is set up. */ - hdspm->channel_map = channel_map_madi_ss; - hdspm_set_rate(hdspm, 44100, 1); + hdspm_set_rate(hdspm, 48000, 1); return 0; } /*------------------------------------------------------------ - interrupt + interrupt ------------------------------------------------------------*/ static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id) { struct hdspm *hdspm = (struct hdspm *) dev_id; unsigned int status; - int audio; - int midi0; - int midi1; - unsigned int midi0status; - unsigned int midi1status; - int schedule = 0; + int i, audio, midi, schedule = 0; + /* cycles_t now; */ status = hdspm_read(hdspm, HDSPM_statusRegister); audio = status & HDSPM_audioIRQPending; - midi0 = status & HDSPM_midi0IRQPending; - midi1 = status & HDSPM_midi1IRQPending; + midi = status & (HDSPM_midi0IRQPending | HDSPM_midi1IRQPending | + HDSPM_midi2IRQPending | HDSPM_midi3IRQPending); + + /* now = get_cycles(); */ + /** + * LAT_2..LAT_0 period counter (win) counter (mac) + * 6 4096 ~256053425 ~514672358 + * 5 2048 ~128024983 ~257373821 + * 4 1024 ~64023706 ~128718089 + * 3 512 ~32005945 ~64385999 + * 2 256 ~16003039 ~32260176 + * 1 128 ~7998738 ~16194507 + * 0 64 ~3998231 ~8191558 + **/ + /* + snd_printk(KERN_INFO "snd_hdspm_interrupt %llu @ %llx\n", + now-hdspm->last_interrupt, status & 0xFFC0); + hdspm->last_interrupt = now; + */ - if (!audio && !midi0 && !midi1) + if (!audio && !midi) return IRQ_NONE; hdspm_write(hdspm, HDSPM_interruptConfirmation, 0); hdspm->irq_count++; - midi0status = hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xff; - midi1status = hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xff; if (audio) { - if (hdspm->capture_substream) snd_pcm_period_elapsed(hdspm->capture_substream); @@ -3455,118 +5200,44 @@ static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id) snd_pcm_period_elapsed(hdspm->playback_substream); } - if (midi0 && midi0status) { - /* we disable interrupts for this input until processing - * is done - */ - hdspm->control_register &= ~HDSPM_Midi0InterruptEnable; - hdspm_write(hdspm, HDSPM_controlRegister, - hdspm->control_register); - hdspm->midi[0].pending = 1; - schedule = 1; - } - if (midi1 && midi1status) { - /* we disable interrupts for this input until processing - * is done - */ - hdspm->control_register &= ~HDSPM_Midi1InterruptEnable; - hdspm_write(hdspm, HDSPM_controlRegister, - hdspm->control_register); - hdspm->midi[1].pending = 1; - schedule = 1; + if (midi) { + i = 0; + while (i < hdspm->midiPorts) { + if ((hdspm_read(hdspm, + hdspm->midi[i].statusIn) & 0xff) && + (status & hdspm->midi[i].irq)) { + /* we disable interrupts for this input until + * processing is done + */ + hdspm->control_register &= ~hdspm->midi[i].ie; + hdspm_write(hdspm, HDSPM_controlRegister, + hdspm->control_register); + hdspm->midi[i].pending = 1; + schedule = 1; + } + + i++; + } + + if (schedule) + tasklet_hi_schedule(&hdspm->midi_tasklet); } - if (schedule) - tasklet_schedule(&hdspm->midi_tasklet); + return IRQ_HANDLED; } /*------------------------------------------------------------ - pcm interface + pcm interface ------------------------------------------------------------*/ -static snd_pcm_uframes_t snd_hdspm_hw_pointer(struct snd_pcm_substream * - substream) +static snd_pcm_uframes_t snd_hdspm_hw_pointer(struct snd_pcm_substream + *substream) { struct hdspm *hdspm = snd_pcm_substream_chip(substream); return hdspm_hw_pointer(hdspm); } -static char *hdspm_channel_buffer_location(struct hdspm * hdspm, - int stream, int channel) -{ - int mapped_channel; - - if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS)) - return NULL; - - mapped_channel = hdspm->channel_map[channel]; - if (mapped_channel < 0) - return NULL; - - if (stream == SNDRV_PCM_STREAM_CAPTURE) - return hdspm->capture_buffer + - mapped_channel * HDSPM_CHANNEL_BUFFER_BYTES; - else - return hdspm->playback_buffer + - mapped_channel * HDSPM_CHANNEL_BUFFER_BYTES; -} - - -/* dont know why need it ??? */ -static int snd_hdspm_playback_copy(struct snd_pcm_substream *substream, - int channel, snd_pcm_uframes_t pos, - void __user *src, snd_pcm_uframes_t count) -{ - struct hdspm *hdspm = snd_pcm_substream_chip(substream); - char *channel_buf; - - if (snd_BUG_ON(pos + count > HDSPM_CHANNEL_BUFFER_BYTES / 4)) - return -EINVAL; - - channel_buf = - hdspm_channel_buffer_location(hdspm, substream->pstr->stream, - channel); - - if (snd_BUG_ON(!channel_buf)) - return -EIO; - - return copy_from_user(channel_buf + pos * 4, src, count * 4); -} - -static int snd_hdspm_capture_copy(struct snd_pcm_substream *substream, - int channel, snd_pcm_uframes_t pos, - void __user *dst, snd_pcm_uframes_t count) -{ - struct hdspm *hdspm = snd_pcm_substream_chip(substream); - char *channel_buf; - - if (snd_BUG_ON(pos + count > HDSPM_CHANNEL_BUFFER_BYTES / 4)) - return -EINVAL; - - channel_buf = - hdspm_channel_buffer_location(hdspm, substream->pstr->stream, - channel); - if (snd_BUG_ON(!channel_buf)) - return -EIO; - return copy_to_user(dst, channel_buf + pos * 4, count * 4); -} - -static int snd_hdspm_hw_silence(struct snd_pcm_substream *substream, - int channel, snd_pcm_uframes_t pos, - snd_pcm_uframes_t count) -{ - struct hdspm *hdspm = snd_pcm_substream_chip(substream); - char *channel_buf; - - channel_buf = - hdspm_channel_buffer_location(hdspm, substream->pstr->stream, - channel); - if (snd_BUG_ON(!channel_buf)) - return -EIO; - memset(channel_buf + pos * 4, 0, count * 4); - return 0; -} static int snd_hdspm_reset(struct snd_pcm_substream *substream) { @@ -3589,7 +5260,7 @@ static int snd_hdspm_reset(struct snd_pcm_substream *substream) snd_pcm_group_for_each_entry(s, substream) { if (s == other) { oruntime->status->hw_ptr = - runtime->status->hw_ptr; + runtime->status->hw_ptr; break; } } @@ -3621,19 +5292,19 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream, /* The other stream is open, and not by the same task as this one. Make sure that the parameters that matter are the same. - */ + */ if (params_rate(params) != hdspm->system_sample_rate) { spin_unlock_irq(&hdspm->lock); _snd_pcm_hw_param_setempty(params, - SNDRV_PCM_HW_PARAM_RATE); + SNDRV_PCM_HW_PARAM_RATE); return -EBUSY; } if (params_period_size(params) != hdspm->period_bytes / 4) { spin_unlock_irq(&hdspm->lock); _snd_pcm_hw_param_setempty(params, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE); + SNDRV_PCM_HW_PARAM_PERIOD_SIZE); return -EBUSY; } @@ -3646,18 +5317,20 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream, spin_lock_irq(&hdspm->lock); err = hdspm_set_rate(hdspm, params_rate(params), 0); if (err < 0) { + snd_printk(KERN_INFO "err on hdspm_set_rate: %d\n", err); spin_unlock_irq(&hdspm->lock); _snd_pcm_hw_param_setempty(params, - SNDRV_PCM_HW_PARAM_RATE); + SNDRV_PCM_HW_PARAM_RATE); return err; } spin_unlock_irq(&hdspm->lock); err = hdspm_set_interrupt_interval(hdspm, - params_period_size(params)); + params_period_size(params)); if (err < 0) { + snd_printk(KERN_INFO "err on hdspm_set_interrupt_interval: %d\n", err); _snd_pcm_hw_param_setempty(params, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE); + SNDRV_PCM_HW_PARAM_PERIOD_SIZE); return err; } @@ -3667,10 +5340,13 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream, /* malloc all buffer even if not enabled to get sure */ /* Update for MADI rev 204: we need to allocate for all channels, * otherwise it doesn't work at 96kHz */ + err = - snd_pcm_lib_malloc_pages(substream, HDSPM_DMA_AREA_BYTES); - if (err < 0) + snd_pcm_lib_malloc_pages(substream, HDSPM_DMA_AREA_BYTES); + if (err < 0) { + snd_printk(KERN_INFO "err on snd_pcm_lib_malloc_pages: %d\n", err); return err; + } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { @@ -3681,7 +5357,7 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream, snd_hdspm_enable_out(hdspm, i, 1); hdspm->playback_buffer = - (unsigned char *) substream->runtime->dma_area; + (unsigned char *) substream->runtime->dma_area; snd_printdd("Allocated sample buffer for playback at %p\n", hdspm->playback_buffer); } else { @@ -3692,23 +5368,40 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream, snd_hdspm_enable_in(hdspm, i, 1); hdspm->capture_buffer = - (unsigned char *) substream->runtime->dma_area; + (unsigned char *) substream->runtime->dma_area; snd_printdd("Allocated sample buffer for capture at %p\n", hdspm->capture_buffer); } + /* snd_printdd("Allocated sample buffer for %s at 0x%08X\n", substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "playback" : "capture", snd_pcm_sgbuf_get_addr(substream, 0)); - */ + */ /* - snd_printdd("set_hwparams: %s %d Hz, %d channels, bs = %d\n", - substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? - "playback" : "capture", - params_rate(params), params_channels(params), - params_buffer_size(params)); - */ + snd_printdd("set_hwparams: %s %d Hz, %d channels, bs = %d\n", + substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + "playback" : "capture", + params_rate(params), params_channels(params), + params_buffer_size(params)); + */ + + + /* Switch to native float format if requested */ + if (SNDRV_PCM_FORMAT_FLOAT_LE == params_format(params)) { + if (!(hdspm->control_register & HDSPe_FLOAT_FORMAT)) + snd_printk(KERN_INFO "hdspm: Switching to native 32bit LE float format.\n"); + + hdspm->control_register |= HDSPe_FLOAT_FORMAT; + } else if (SNDRV_PCM_FORMAT_S32_LE == params_format(params)) { + if (hdspm->control_register & HDSPe_FLOAT_FORMAT) + snd_printk(KERN_INFO "hdspm: Switching to native 32bit LE integer format.\n"); + + hdspm->control_register &= ~HDSPe_FLOAT_FORMAT; + } + hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); + return 0; } @@ -3719,14 +5412,14 @@ static int snd_hdspm_hw_free(struct snd_pcm_substream *substream) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - /* params_channels(params) should be enough, + /* params_channels(params) should be enough, but to get sure in case of error */ - for (i = 0; i < HDSPM_MAX_CHANNELS; ++i) + for (i = 0; i < hdspm->max_channels_out; ++i) snd_hdspm_enable_out(hdspm, i, 0); hdspm->playback_buffer = NULL; } else { - for (i = 0; i < HDSPM_MAX_CHANNELS; ++i) + for (i = 0; i < hdspm->max_channels_in; ++i) snd_hdspm_enable_in(hdspm, i, 0); hdspm->capture_buffer = NULL; @@ -3738,37 +5431,58 @@ static int snd_hdspm_hw_free(struct snd_pcm_substream *substream) return 0; } + static int snd_hdspm_channel_info(struct snd_pcm_substream *substream, - struct snd_pcm_channel_info * info) + struct snd_pcm_channel_info *info) { struct hdspm *hdspm = snd_pcm_substream_chip(substream); - int mapped_channel; - if (snd_BUG_ON(info->channel >= HDSPM_MAX_CHANNELS)) - return -EINVAL; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (snd_BUG_ON(info->channel >= hdspm->max_channels_out)) { + snd_printk(KERN_INFO "snd_hdspm_channel_info: output channel out of range (%d)\n", info->channel); + return -EINVAL; + } - mapped_channel = hdspm->channel_map[info->channel]; - if (mapped_channel < 0) - return -EINVAL; + if (hdspm->channel_map_out[info->channel] < 0) { + snd_printk(KERN_INFO "snd_hdspm_channel_info: output channel %d mapped out\n", info->channel); + return -EINVAL; + } + + info->offset = hdspm->channel_map_out[info->channel] * + HDSPM_CHANNEL_BUFFER_BYTES; + } else { + if (snd_BUG_ON(info->channel >= hdspm->max_channels_in)) { + snd_printk(KERN_INFO "snd_hdspm_channel_info: input channel out of range (%d)\n", info->channel); + return -EINVAL; + } + + if (hdspm->channel_map_in[info->channel] < 0) { + snd_printk(KERN_INFO "snd_hdspm_channel_info: input channel %d mapped out\n", info->channel); + return -EINVAL; + } + + info->offset = hdspm->channel_map_in[info->channel] * + HDSPM_CHANNEL_BUFFER_BYTES; + } - info->offset = mapped_channel * HDSPM_CHANNEL_BUFFER_BYTES; info->first = 0; info->step = 32; return 0; } + static int snd_hdspm_ioctl(struct snd_pcm_substream *substream, - unsigned int cmd, void *arg) + unsigned int cmd, void *arg) { switch (cmd) { case SNDRV_PCM_IOCTL1_RESET: return snd_hdspm_reset(substream); case SNDRV_PCM_IOCTL1_CHANNEL_INFO: - { - struct snd_pcm_channel_info *info = arg; - return snd_hdspm_channel_info(substream, info); - } + { + struct snd_pcm_channel_info *info = arg; + return snd_hdspm_channel_info(substream, info); + } default: break; } @@ -3815,19 +5529,19 @@ static int snd_hdspm_trigger(struct snd_pcm_substream *substream, int cmd) } if (cmd == SNDRV_PCM_TRIGGER_START) { if (!(running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) - && substream->stream == - SNDRV_PCM_STREAM_CAPTURE) + && substream->stream == + SNDRV_PCM_STREAM_CAPTURE) hdspm_silence_playback(hdspm); } else { if (running && - substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + substream->stream == SNDRV_PCM_STREAM_PLAYBACK) hdspm_silence_playback(hdspm); } } else { if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) hdspm_silence_playback(hdspm); } - _ok: +_ok: snd_pcm_trigger_done(substream, substream); if (!hdspm->running && running) hdspm_start_audio(hdspm); @@ -3844,8 +5558,18 @@ static int snd_hdspm_prepare(struct snd_pcm_substream *substream) return 0; } -static unsigned int period_sizes[] = - { 64, 128, 256, 512, 1024, 2048, 4096, 8192 }; +static unsigned int period_sizes_old[] = { + 64, 128, 256, 512, 1024, 2048, 4096 +}; + +static unsigned int period_sizes_new[] = { + 32, 64, 128, 256, 512, 1024, 2048, 4096 +}; + +/* RayDAT and AIO always have a buffer of 16384 samples per channel */ +static unsigned int raydat_aio_buffer_sizes[] = { + 16384 +}; static struct snd_pcm_hardware snd_hdspm_playback_subinfo = { .info = (SNDRV_PCM_INFO_MMAP | @@ -3866,9 +5590,9 @@ static struct snd_pcm_hardware snd_hdspm_playback_subinfo = { .buffer_bytes_max = HDSPM_CHANNEL_BUFFER_BYTES * HDSPM_MAX_CHANNELS, .period_bytes_min = (64 * 4), - .period_bytes_max = (8192 * 4) * HDSPM_MAX_CHANNELS, + .period_bytes_max = (4096 * 4) * HDSPM_MAX_CHANNELS, .periods_min = 2, - .periods_max = 2, + .periods_max = 512, .fifo_size = 0 }; @@ -3891,20 +5615,66 @@ static struct snd_pcm_hardware snd_hdspm_capture_subinfo = { .buffer_bytes_max = HDSPM_CHANNEL_BUFFER_BYTES * HDSPM_MAX_CHANNELS, .period_bytes_min = (64 * 4), - .period_bytes_max = (8192 * 4) * HDSPM_MAX_CHANNELS, + .period_bytes_max = (4096 * 4) * HDSPM_MAX_CHANNELS, .periods_min = 2, - .periods_max = 2, + .periods_max = 512, .fifo_size = 0 }; -static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes = { - .count = ARRAY_SIZE(period_sizes), - .list = period_sizes, +static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes_old = { + .count = ARRAY_SIZE(period_sizes_old), + .list = period_sizes_old, .mask = 0 }; +static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes_new = { + .count = ARRAY_SIZE(period_sizes_new), + .list = period_sizes_new, + .mask = 0 +}; -static int snd_hdspm_hw_rule_channels_rate(struct snd_pcm_hw_params *params, +static struct snd_pcm_hw_constraint_list hw_constraints_raydat_io_buffer = { + .count = ARRAY_SIZE(raydat_aio_buffer_sizes), + .list = raydat_aio_buffer_sizes, + .mask = 0 +}; + +static int snd_hdspm_hw_rule_in_channels_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct hdspm *hdspm = rule->private; + struct snd_interval *c = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *r = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + + if (r->min > 96000 && r->max <= 192000) { + struct snd_interval t = { + .min = hdspm->qs_in_channels, + .max = hdspm->qs_in_channels, + .integer = 1, + }; + return snd_interval_refine(c, &t); + } else if (r->min > 48000 && r->max <= 96000) { + struct snd_interval t = { + .min = hdspm->ds_in_channels, + .max = hdspm->ds_in_channels, + .integer = 1, + }; + return snd_interval_refine(c, &t); + } else if (r->max < 64000) { + struct snd_interval t = { + .min = hdspm->ss_in_channels, + .max = hdspm->ss_in_channels, + .integer = 1, + }; + return snd_interval_refine(c, &t); + } + + return 0; +} + +static int snd_hdspm_hw_rule_out_channels_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule * rule) { struct hdspm *hdspm = rule->private; @@ -3913,25 +5683,33 @@ static int snd_hdspm_hw_rule_channels_rate(struct snd_pcm_hw_params *params, struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - if (r->min > 48000 && r->max <= 96000) { + if (r->min > 96000 && r->max <= 192000) { struct snd_interval t = { - .min = hdspm->ds_channels, - .max = hdspm->ds_channels, + .min = hdspm->qs_out_channels, + .max = hdspm->qs_out_channels, + .integer = 1, + }; + return snd_interval_refine(c, &t); + } else if (r->min > 48000 && r->max <= 96000) { + struct snd_interval t = { + .min = hdspm->ds_out_channels, + .max = hdspm->ds_out_channels, .integer = 1, }; return snd_interval_refine(c, &t); } else if (r->max < 64000) { struct snd_interval t = { - .min = hdspm->ss_channels, - .max = hdspm->ss_channels, + .min = hdspm->ss_out_channels, + .max = hdspm->ss_out_channels, .integer = 1, }; return snd_interval_refine(c, &t); + } else { } return 0; } -static int snd_hdspm_hw_rule_rate_channels(struct snd_pcm_hw_params *params, +static int snd_hdspm_hw_rule_rate_in_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule * rule) { struct hdspm *hdspm = rule->private; @@ -3940,42 +5718,92 @@ static int snd_hdspm_hw_rule_rate_channels(struct snd_pcm_hw_params *params, struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - if (c->min >= hdspm->ss_channels) { + if (c->min >= hdspm->ss_in_channels) { struct snd_interval t = { .min = 32000, .max = 48000, .integer = 1, }; return snd_interval_refine(r, &t); - } else if (c->max <= hdspm->ds_channels) { + } else if (c->max <= hdspm->qs_in_channels) { + struct snd_interval t = { + .min = 128000, + .max = 192000, + .integer = 1, + }; + return snd_interval_refine(r, &t); + } else if (c->max <= hdspm->ds_in_channels) { struct snd_interval t = { .min = 64000, .max = 96000, .integer = 1, }; + return snd_interval_refine(r, &t); + } + return 0; +} +static int snd_hdspm_hw_rule_rate_out_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct hdspm *hdspm = rule->private; + struct snd_interval *c = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *r = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + + if (c->min >= hdspm->ss_out_channels) { + struct snd_interval t = { + .min = 32000, + .max = 48000, + .integer = 1, + }; + return snd_interval_refine(r, &t); + } else if (c->max <= hdspm->qs_out_channels) { + struct snd_interval t = { + .min = 128000, + .max = 192000, + .integer = 1, + }; + return snd_interval_refine(r, &t); + } else if (c->max <= hdspm->ds_out_channels) { + struct snd_interval t = { + .min = 64000, + .max = 96000, + .integer = 1, + }; return snd_interval_refine(r, &t); } + return 0; } -static int snd_hdspm_hw_rule_channels(struct snd_pcm_hw_params *params, +static int snd_hdspm_hw_rule_in_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { unsigned int list[3]; struct hdspm *hdspm = rule->private; struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - if (hdspm->is_aes32) { - list[0] = hdspm->qs_channels; - list[1] = hdspm->ds_channels; - list[2] = hdspm->ss_channels; - return snd_interval_list(c, 3, list, 0); - } else { - list[0] = hdspm->ds_channels; - list[1] = hdspm->ss_channels; - return snd_interval_list(c, 2, list, 0); - } + + list[0] = hdspm->qs_in_channels; + list[1] = hdspm->ds_in_channels; + list[2] = hdspm->ss_in_channels; + return snd_interval_list(c, 3, list, 0); +} + +static int snd_hdspm_hw_rule_out_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + unsigned int list[3]; + struct hdspm *hdspm = rule->private; + struct snd_interval *c = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + list[0] = hdspm->qs_out_channels; + list[1] = hdspm->ds_out_channels; + list[2] = hdspm->ss_out_channels; + return snd_interval_list(c, 3, list, 0); } @@ -3999,6 +5827,7 @@ static int snd_hdspm_playback_open(struct snd_pcm_substream *substream) snd_pcm_set_sync(substream); + runtime->hw = snd_hdspm_playback_subinfo; if (hdspm->capture_substream == NULL) @@ -4011,25 +5840,41 @@ static int snd_hdspm_playback_open(struct snd_pcm_substream *substream) snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); - snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE, - &hw_constraints_period_sizes); + switch (hdspm->io_type) { + case AIO: + case RayDAT: + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + &hw_constraints_period_sizes_new); + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + &hw_constraints_raydat_io_buffer); - if (hdspm->is_aes32) { + break; + + default: + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + &hw_constraints_period_sizes_old); + } + + if (AES32 == hdspm->io_type) { snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hdspm_hw_constraints_aes32_sample_rates); } else { - snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - snd_hdspm_hw_rule_channels, hdspm, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); - snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - snd_hdspm_hw_rule_channels_rate, hdspm, - SNDRV_PCM_HW_PARAM_RATE, -1); - snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - snd_hdspm_hw_rule_rate_channels, hdspm, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); + snd_hdspm_hw_rule_rate_out_channels, hdspm, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); } + + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_hdspm_hw_rule_out_channels, hdspm, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_hdspm_hw_rule_out_channels_rate, hdspm, + SNDRV_PCM_HW_PARAM_RATE, -1); + return 0; } @@ -4066,24 +5911,40 @@ static int snd_hdspm_capture_open(struct snd_pcm_substream *substream) spin_unlock_irq(&hdspm->lock); snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); - snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE, - &hw_constraints_period_sizes); - if (hdspm->is_aes32) { + switch (hdspm->io_type) { + case AIO: + case RayDAT: + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + &hw_constraints_period_sizes_new); + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + &hw_constraints_raydat_io_buffer); + break; + + default: + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + &hw_constraints_period_sizes_old); + } + + if (AES32 == hdspm->io_type) { snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hdspm_hw_constraints_aes32_sample_rates); } else { - snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - snd_hdspm_hw_rule_channels, hdspm, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); - snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - snd_hdspm_hw_rule_channels_rate, hdspm, - SNDRV_PCM_HW_PARAM_RATE, -1); - snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - snd_hdspm_hw_rule_rate_channels, hdspm, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); + snd_hdspm_hw_rule_rate_in_channels, hdspm, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); } + + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_hdspm_hw_rule_in_channels, hdspm, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_hdspm_hw_rule_in_channels_rate, hdspm, + SNDRV_PCM_HW_PARAM_RATE, -1); + return 0; } @@ -4100,32 +5961,129 @@ static int snd_hdspm_capture_release(struct snd_pcm_substream *substream) return 0; } -static int snd_hdspm_hwdep_ioctl(struct snd_hwdep * hw, struct file *file, - unsigned int cmd, unsigned long arg) +static int snd_hdspm_hwdep_dummy_op(struct snd_hwdep *hw, struct file *file) { + /* we have nothing to initialize but the call is required */ + return 0; +} + +static inline int copy_u32_le(void __user *dest, void __iomem *src) +{ + u32 val = readl(src); + return copy_to_user(dest, &val, 4); +} + +static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, + unsigned int cmd, unsigned long __user arg) +{ + void __user *argp = (void __user *)arg; struct hdspm *hdspm = hw->private_data; struct hdspm_mixer_ioctl mixer; - struct hdspm_config_info info; + struct hdspm_config info; + struct hdspm_status status; struct hdspm_version hdspm_version; - struct hdspm_peak_rms_ioctl rms; + struct hdspm_peak_rms *levels; + struct hdspm_ltc ltc; + unsigned int statusregister; + long unsigned int s; + int i = 0; switch (cmd) { case SNDRV_HDSPM_IOCTL_GET_PEAK_RMS: - if (copy_from_user(&rms, (void __user *)arg, sizeof(rms))) + levels = &hdspm->peak_rms; + for (i = 0; i < HDSPM_MAX_CHANNELS; i++) { + levels->input_peaks[i] = + readl(hdspm->iobase + + HDSPM_MADI_INPUT_PEAK + i*4); + levels->playback_peaks[i] = + readl(hdspm->iobase + + HDSPM_MADI_PLAYBACK_PEAK + i*4); + levels->output_peaks[i] = + readl(hdspm->iobase + + HDSPM_MADI_OUTPUT_PEAK + i*4); + + levels->input_rms[i] = + ((uint64_t) readl(hdspm->iobase + + HDSPM_MADI_INPUT_RMS_H + i*4) << 32) | + (uint64_t) readl(hdspm->iobase + + HDSPM_MADI_INPUT_RMS_L + i*4); + levels->playback_rms[i] = + ((uint64_t)readl(hdspm->iobase + + HDSPM_MADI_PLAYBACK_RMS_H+i*4) << 32) | + (uint64_t)readl(hdspm->iobase + + HDSPM_MADI_PLAYBACK_RMS_L + i*4); + levels->output_rms[i] = + ((uint64_t)readl(hdspm->iobase + + HDSPM_MADI_OUTPUT_RMS_H + i*4) << 32) | + (uint64_t)readl(hdspm->iobase + + HDSPM_MADI_OUTPUT_RMS_L + i*4); + } + + if (hdspm->system_sample_rate > 96000) { + levels->speed = qs; + } else if (hdspm->system_sample_rate > 48000) { + levels->speed = ds; + } else { + levels->speed = ss; + } + levels->status2 = hdspm_read(hdspm, HDSPM_statusRegister2); + + s = copy_to_user(argp, levels, sizeof(struct hdspm_peak_rms)); + if (0 != s) { + /* snd_printk(KERN_ERR "copy_to_user(.., .., %lu): %lu + [Levels]\n", sizeof(struct hdspm_peak_rms), s); + */ return -EFAULT; - /* maybe there is a chance to memorymap in future - * so dont touch just copy - */ - if(copy_to_user_fromio((void __user *)rms.peak, - hdspm->iobase+HDSPM_MADI_peakrmsbase, - sizeof(struct hdspm_peak_rms)) != 0 ) + } + break; + + case SNDRV_HDSPM_IOCTL_GET_LTC: + ltc.ltc = hdspm_read(hdspm, HDSPM_RD_TCO); + i = hdspm_read(hdspm, HDSPM_RD_TCO + 4); + if (i & HDSPM_TCO1_LTC_Input_valid) { + switch (i & (HDSPM_TCO1_LTC_Format_LSB | + HDSPM_TCO1_LTC_Format_MSB)) { + case 0: + ltc.format = fps_24; + break; + case HDSPM_TCO1_LTC_Format_LSB: + ltc.format = fps_25; + break; + case HDSPM_TCO1_LTC_Format_MSB: + ltc.format = fps_2997; + break; + default: + ltc.format = 30; + break; + } + if (i & HDSPM_TCO1_set_drop_frame_flag) { + ltc.frame = drop_frame; + } else { + ltc.frame = full_frame; + } + } else { + ltc.format = format_invalid; + ltc.frame = frame_invalid; + } + if (i & HDSPM_TCO1_Video_Input_Format_NTSC) { + ltc.input_format = ntsc; + } else if (i & HDSPM_TCO1_Video_Input_Format_PAL) { + ltc.input_format = pal; + } else { + ltc.input_format = no_video; + } + + s = copy_to_user(argp, <c, sizeof(struct hdspm_ltc)); + if (0 != s) { + /* + snd_printk(KERN_ERR "copy_to_user(.., .., %lu): %lu [LTC]\n", sizeof(struct hdspm_ltc), s); */ return -EFAULT; + } break; - - case SNDRV_HDSPM_IOCTL_GET_CONFIG_INFO: + case SNDRV_HDSPM_IOCTL_GET_CONFIG: memset(&info, 0, sizeof(info)); spin_lock_irq(&hdspm->lock); @@ -4134,7 +6092,7 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep * hw, struct file *file, info.system_sample_rate = hdspm->system_sample_rate; info.autosync_sample_rate = - hdspm_external_sample_rate(hdspm); + hdspm_external_sample_rate(hdspm); info.system_clock_mode = hdspm_system_clock_mode(hdspm); info.clock_source = hdspm_clock_source(hdspm); info.autosync_ref = hdspm_autosync_ref(hdspm); @@ -4145,10 +6103,58 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep * hw, struct file *file, return -EFAULT; break; + case SNDRV_HDSPM_IOCTL_GET_STATUS: + status.card_type = hdspm->io_type; + + status.autosync_source = hdspm_autosync_ref(hdspm); + + status.card_clock = 110069313433624ULL; + status.master_period = hdspm_read(hdspm, HDSPM_RD_PLL_FREQ); + + switch (hdspm->io_type) { + case MADI: + case MADIface: + status.card_specific.madi.sync_wc = + hdspm_wc_sync_check(hdspm); + status.card_specific.madi.sync_madi = + hdspm_madi_sync_check(hdspm); + status.card_specific.madi.sync_tco = + hdspm_tco_sync_check(hdspm); + status.card_specific.madi.sync_in = + hdspm_sync_in_sync_check(hdspm); + + statusregister = + hdspm_read(hdspm, HDSPM_statusRegister); + status.card_specific.madi.madi_input = + (statusregister & HDSPM_AB_int) ? 1 : 0; + status.card_specific.madi.channel_format = + (statusregister & HDSPM_TX_64ch) ? 1 : 0; + /* TODO: Mac driver sets it when f_s>48kHz */ + status.card_specific.madi.frame_format = 0; + + default: + break; + } + + if (copy_to_user((void __user *) arg, &status, sizeof(status))) + return -EFAULT; + + + break; + case SNDRV_HDSPM_IOCTL_GET_VERSION: + hdspm_version.card_type = hdspm->io_type; + strncpy(hdspm_version.cardname, hdspm->card_name, + sizeof(hdspm_version.cardname)); + hdspm_version.serial = (hdspm_read(hdspm, + HDSPM_midiStatusIn0)>>8) & 0xFFFFFF; hdspm_version.firmware_rev = hdspm->firmware_rev; + hdspm_version.addons = 0; + if (hdspm->tco) + hdspm_version.addons |= HDSPM_ADDON_TCO; + if (copy_to_user((void __user *) arg, &hdspm_version, - sizeof(hdspm_version))) + sizeof(hdspm_version))) return -EFAULT; break; @@ -4156,7 +6162,7 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep * hw, struct file *file, if (copy_from_user(&mixer, (void __user *)arg, sizeof(mixer))) return -EFAULT; if (copy_to_user((void __user *)mixer.mixer, hdspm->mixer, - sizeof(struct hdspm_mixer))) + sizeof(struct hdspm_mixer))) return -EFAULT; break; @@ -4175,8 +6181,6 @@ static struct snd_pcm_ops snd_hdspm_playback_ops = { .prepare = snd_hdspm_prepare, .trigger = snd_hdspm_trigger, .pointer = snd_hdspm_hw_pointer, - .copy = snd_hdspm_playback_copy, - .silence = snd_hdspm_hw_silence, .page = snd_pcm_sgbuf_ops_page, }; @@ -4189,7 +6193,6 @@ static struct snd_pcm_ops snd_hdspm_capture_ops = { .prepare = snd_hdspm_prepare, .trigger = snd_hdspm_trigger, .pointer = snd_hdspm_hw_pointer, - .copy = snd_hdspm_capture_copy, .page = snd_pcm_sgbuf_ops_page, }; @@ -4207,16 +6210,18 @@ static int __devinit snd_hdspm_create_hwdep(struct snd_card *card, hw->private_data = hdspm; strcpy(hw->name, "HDSPM hwdep interface"); + hw->ops.open = snd_hdspm_hwdep_dummy_op; hw->ops.ioctl = snd_hdspm_hwdep_ioctl; + hw->ops.release = snd_hdspm_hwdep_dummy_op; return 0; } /*------------------------------------------------------------ - memory interface + memory interface ------------------------------------------------------------*/ -static int __devinit snd_hdspm_preallocate_memory(struct hdspm * hdspm) +static int __devinit snd_hdspm_preallocate_memory(struct hdspm *hdspm) { int err; struct snd_pcm *pcm; @@ -4228,7 +6233,7 @@ static int __devinit snd_hdspm_preallocate_memory(struct hdspm * hdspm) err = snd_pcm_lib_preallocate_pages_for_all(pcm, - SNDRV_DMA_TYPE_DEV_SG, + SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(hdspm->pci), wanted, wanted); @@ -4242,19 +6247,23 @@ static int __devinit snd_hdspm_preallocate_memory(struct hdspm * hdspm) return 0; } -static void hdspm_set_sgbuf(struct hdspm * hdspm, + +static void hdspm_set_sgbuf(struct hdspm *hdspm, struct snd_pcm_substream *substream, unsigned int reg, int channels) { int i; + + /* continuous memory segment */ for (i = 0; i < (channels * 16); i++) hdspm_write(hdspm, reg + 4 * i, - snd_pcm_sgbuf_get_addr(substream, 4096 * i)); + snd_pcm_sgbuf_get_addr(substream, 4096 * i)); } + /* ------------- ALSA Devices ---------------------------- */ static int __devinit snd_hdspm_create_pcm(struct snd_card *card, - struct hdspm * hdspm) + struct hdspm *hdspm) { struct snd_pcm *pcm; int err; @@ -4283,27 +6292,30 @@ static int __devinit snd_hdspm_create_pcm(struct snd_card *card, static inline void snd_hdspm_initialize_midi_flush(struct hdspm * hdspm) { - snd_hdspm_flush_midi_input(hdspm, 0); - snd_hdspm_flush_midi_input(hdspm, 1); + int i; + + for (i = 0; i < hdspm->midiPorts; i++) + snd_hdspm_flush_midi_input(hdspm, i); } static int __devinit snd_hdspm_create_alsa_devices(struct snd_card *card, struct hdspm * hdspm) { - int err; + int err, i; snd_printdd("Create card...\n"); err = snd_hdspm_create_pcm(card, hdspm); if (err < 0) return err; - err = snd_hdspm_create_midi(card, hdspm, 0); - if (err < 0) - return err; - - err = snd_hdspm_create_midi(card, hdspm, 1); - if (err < 0) - return err; + i = 0; + while (i < hdspm->midiPorts) { + err = snd_hdspm_create_midi(card, hdspm, i); + if (err < 0) { + return err; + } + i++; + } err = snd_hdspm_create_controls(card, hdspm); if (err < 0) @@ -4346,37 +6358,55 @@ static int __devinit snd_hdspm_create_alsa_devices(struct snd_card *card, } static int __devinit snd_hdspm_create(struct snd_card *card, - struct hdspm *hdspm, - int precise_ptr, int enable_monitor) -{ + struct hdspm *hdspm) { + struct pci_dev *pci = hdspm->pci; int err; unsigned long io_extent; hdspm->irq = -1; - - spin_lock_init(&hdspm->midi[0].lock); - spin_lock_init(&hdspm->midi[1].lock); - hdspm->card = card; spin_lock_init(&hdspm->lock); - tasklet_init(&hdspm->midi_tasklet, - hdspm_midi_tasklet, (unsigned long) hdspm); - pci_read_config_word(hdspm->pci, - PCI_CLASS_REVISION, &hdspm->firmware_rev); - - hdspm->is_aes32 = (hdspm->firmware_rev >= HDSPM_AESREVISION); + PCI_CLASS_REVISION, &hdspm->firmware_rev); strcpy(card->mixername, "Xilinx FPGA"); - if (hdspm->is_aes32) { - strcpy(card->driver, "HDSPAES32"); - hdspm->card_name = "RME HDSPM AES32"; - } else { - strcpy(card->driver, "HDSPM"); - hdspm->card_name = "RME HDSPM MADI"; + strcpy(card->driver, "HDSPM"); + + switch (hdspm->firmware_rev) { + case HDSPM_MADI_REV: + hdspm->io_type = MADI; + hdspm->card_name = "RME MADI"; + hdspm->midiPorts = 3; + break; + case HDSPM_RAYDAT_REV: + hdspm->io_type = RayDAT; + hdspm->card_name = "RME RayDAT"; + hdspm->midiPorts = 2; + break; + case HDSPM_AIO_REV: + hdspm->io_type = AIO; + hdspm->card_name = "RME AIO"; + hdspm->midiPorts = 1; + break; + case HDSPM_MADIFACE_REV: + hdspm->io_type = MADIface; + hdspm->card_name = "RME MADIface"; + hdspm->midiPorts = 1; + break; + case HDSPM_AES_REV: + case HDSPM_AES32_REV: + case HDSPM_AES32_OLD_REV: + hdspm->io_type = AES32; + hdspm->card_name = "RME AES32"; + hdspm->midiPorts = 2; + break; + default: + snd_printk(KERN_ERR "HDSPM: unknown firmware revision %x\n", + hdspm->firmware_rev); + return -ENODEV; } err = pci_enable_device(pci); @@ -4393,22 +6423,21 @@ static int __devinit snd_hdspm_create(struct snd_card *card, io_extent = pci_resource_len(pci, 0); snd_printdd("grabbed memory region 0x%lx-0x%lx\n", - hdspm->port, hdspm->port + io_extent - 1); - + hdspm->port, hdspm->port + io_extent - 1); hdspm->iobase = ioremap_nocache(hdspm->port, io_extent); if (!hdspm->iobase) { snd_printk(KERN_ERR "HDSPM: " - "unable to remap region 0x%lx-0x%lx\n", - hdspm->port, hdspm->port + io_extent - 1); + "unable to remap region 0x%lx-0x%lx\n", + hdspm->port, hdspm->port + io_extent - 1); return -EBUSY; } snd_printdd("remapped region (0x%lx) 0x%lx-0x%lx\n", - (unsigned long)hdspm->iobase, hdspm->port, - hdspm->port + io_extent - 1); + (unsigned long)hdspm->iobase, hdspm->port, + hdspm->port + io_extent - 1); if (request_irq(pci->irq, snd_hdspm_interrupt, - IRQF_SHARED, "hdspm", hdspm)) { + IRQF_SHARED, "hdspm", hdspm)) { snd_printk(KERN_ERR "HDSPM: unable to use IRQ %d\n", pci->irq); return -EBUSY; } @@ -4416,23 +6445,219 @@ static int __devinit snd_hdspm_create(struct snd_card *card, snd_printdd("use IRQ %d\n", pci->irq); hdspm->irq = pci->irq; - hdspm->precise_ptr = precise_ptr; - - hdspm->monitor_outs = enable_monitor; snd_printdd("kmalloc Mixer memory of %zd Bytes\n", - sizeof(struct hdspm_mixer)); + sizeof(struct hdspm_mixer)); hdspm->mixer = kzalloc(sizeof(struct hdspm_mixer), GFP_KERNEL); if (!hdspm->mixer) { snd_printk(KERN_ERR "HDSPM: " - "unable to kmalloc Mixer memory of %d Bytes\n", - (int)sizeof(struct hdspm_mixer)); + "unable to kmalloc Mixer memory of %d Bytes\n", + (int)sizeof(struct hdspm_mixer)); return err; } - hdspm->ss_channels = MADI_SS_CHANNELS; - hdspm->ds_channels = MADI_DS_CHANNELS; - hdspm->qs_channels = MADI_QS_CHANNELS; + hdspm->port_names_in = NULL; + hdspm->port_names_out = NULL; + + switch (hdspm->io_type) { + case AES32: + hdspm->ss_in_channels = hdspm->ss_out_channels = AES32_CHANNELS; + hdspm->ds_in_channels = hdspm->ds_out_channels = AES32_CHANNELS; + hdspm->qs_in_channels = hdspm->qs_out_channels = AES32_CHANNELS; + + hdspm->channel_map_in_ss = hdspm->channel_map_out_ss = + channel_map_aes32; + hdspm->channel_map_in_ds = hdspm->channel_map_out_ds = + channel_map_aes32; + hdspm->channel_map_in_qs = hdspm->channel_map_out_qs = + channel_map_aes32; + hdspm->port_names_in_ss = hdspm->port_names_out_ss = + texts_ports_aes32; + hdspm->port_names_in_ds = hdspm->port_names_out_ds = + texts_ports_aes32; + hdspm->port_names_in_qs = hdspm->port_names_out_qs = + texts_ports_aes32; + + hdspm->max_channels_out = hdspm->max_channels_in = + AES32_CHANNELS; + hdspm->port_names_in = hdspm->port_names_out = + texts_ports_aes32; + hdspm->channel_map_in = hdspm->channel_map_out = + channel_map_aes32; + + break; + + case MADI: + case MADIface: + hdspm->ss_in_channels = hdspm->ss_out_channels = + MADI_SS_CHANNELS; + hdspm->ds_in_channels = hdspm->ds_out_channels = + MADI_DS_CHANNELS; + hdspm->qs_in_channels = hdspm->qs_out_channels = + MADI_QS_CHANNELS; + + hdspm->channel_map_in_ss = hdspm->channel_map_out_ss = + channel_map_unity_ss; + hdspm->channel_map_in_ds = hdspm->channel_map_out_ds = + channel_map_unity_ss; + hdspm->channel_map_in_qs = hdspm->channel_map_out_qs = + channel_map_unity_ss; + + hdspm->port_names_in_ss = hdspm->port_names_out_ss = + texts_ports_madi; + hdspm->port_names_in_ds = hdspm->port_names_out_ds = + texts_ports_madi; + hdspm->port_names_in_qs = hdspm->port_names_out_qs = + texts_ports_madi; + break; + + case AIO: + if (0 == (hdspm_read(hdspm, HDSPM_statusRegister2) & HDSPM_s2_AEBI_D)) { + snd_printk(KERN_INFO "HDSPM: AEB input board found, but not supported\n"); + } + + hdspm->ss_in_channels = AIO_IN_SS_CHANNELS; + hdspm->ds_in_channels = AIO_IN_DS_CHANNELS; + hdspm->qs_in_channels = AIO_IN_QS_CHANNELS; + hdspm->ss_out_channels = AIO_OUT_SS_CHANNELS; + hdspm->ds_out_channels = AIO_OUT_DS_CHANNELS; + hdspm->qs_out_channels = AIO_OUT_QS_CHANNELS; + + hdspm->channel_map_out_ss = channel_map_aio_out_ss; + hdspm->channel_map_out_ds = channel_map_aio_out_ds; + hdspm->channel_map_out_qs = channel_map_aio_out_qs; + + hdspm->channel_map_in_ss = channel_map_aio_in_ss; + hdspm->channel_map_in_ds = channel_map_aio_in_ds; + hdspm->channel_map_in_qs = channel_map_aio_in_qs; + + hdspm->port_names_in_ss = texts_ports_aio_in_ss; + hdspm->port_names_out_ss = texts_ports_aio_out_ss; + hdspm->port_names_in_ds = texts_ports_aio_in_ds; + hdspm->port_names_out_ds = texts_ports_aio_out_ds; + hdspm->port_names_in_qs = texts_ports_aio_in_qs; + hdspm->port_names_out_qs = texts_ports_aio_out_qs; + + break; + + case RayDAT: + hdspm->ss_in_channels = hdspm->ss_out_channels = + RAYDAT_SS_CHANNELS; + hdspm->ds_in_channels = hdspm->ds_out_channels = + RAYDAT_DS_CHANNELS; + hdspm->qs_in_channels = hdspm->qs_out_channels = + RAYDAT_QS_CHANNELS; + + hdspm->max_channels_in = RAYDAT_SS_CHANNELS; + hdspm->max_channels_out = RAYDAT_SS_CHANNELS; + + hdspm->channel_map_in_ss = hdspm->channel_map_out_ss = + channel_map_raydat_ss; + hdspm->channel_map_in_ds = hdspm->channel_map_out_ds = + channel_map_raydat_ds; + hdspm->channel_map_in_qs = hdspm->channel_map_out_qs = + channel_map_raydat_qs; + hdspm->channel_map_in = hdspm->channel_map_out = + channel_map_raydat_ss; + + hdspm->port_names_in_ss = hdspm->port_names_out_ss = + texts_ports_raydat_ss; + hdspm->port_names_in_ds = hdspm->port_names_out_ds = + texts_ports_raydat_ds; + hdspm->port_names_in_qs = hdspm->port_names_out_qs = + texts_ports_raydat_qs; + + + break; + + } + + /* TCO detection */ + switch (hdspm->io_type) { + case AIO: + case RayDAT: + if (hdspm_read(hdspm, HDSPM_statusRegister2) & + HDSPM_s2_tco_detect) { + hdspm->midiPorts++; + hdspm->tco = kzalloc(sizeof(struct hdspm_tco), + GFP_KERNEL); + if (NULL != hdspm->tco) { + hdspm_tco_write(hdspm); + } + snd_printk(KERN_INFO "HDSPM: AIO/RayDAT TCO module found\n"); + } else { + hdspm->tco = NULL; + } + break; + + case MADI: + if (hdspm_read(hdspm, HDSPM_statusRegister) & HDSPM_tco_detect) { + hdspm->midiPorts++; + hdspm->tco = kzalloc(sizeof(struct hdspm_tco), + GFP_KERNEL); + if (NULL != hdspm->tco) { + hdspm_tco_write(hdspm); + } + snd_printk(KERN_INFO "HDSPM: MADI TCO module found\n"); + } else { + hdspm->tco = NULL; + } + break; + + default: + hdspm->tco = NULL; + } + + /* texts */ + switch (hdspm->io_type) { + case AES32: + if (hdspm->tco) { + hdspm->texts_autosync = texts_autosync_aes_tco; + hdspm->texts_autosync_items = 10; + } else { + hdspm->texts_autosync = texts_autosync_aes; + hdspm->texts_autosync_items = 9; + } + break; + + case MADI: + if (hdspm->tco) { + hdspm->texts_autosync = texts_autosync_madi_tco; + hdspm->texts_autosync_items = 4; + } else { + hdspm->texts_autosync = texts_autosync_madi; + hdspm->texts_autosync_items = 3; + } + break; + + case MADIface: + + break; + + case RayDAT: + if (hdspm->tco) { + hdspm->texts_autosync = texts_autosync_raydat_tco; + hdspm->texts_autosync_items = 9; + } else { + hdspm->texts_autosync = texts_autosync_raydat; + hdspm->texts_autosync_items = 8; + } + break; + + case AIO: + if (hdspm->tco) { + hdspm->texts_autosync = texts_autosync_aio_tco; + hdspm->texts_autosync_items = 6; + } else { + hdspm->texts_autosync = texts_autosync_aio; + hdspm->texts_autosync_items = 5; + } + break; + + } + + tasklet_init(&hdspm->midi_tasklet, + hdspm_midi_tasklet, (unsigned long) hdspm); snd_printdd("create alsa devices.\n"); err = snd_hdspm_create_alsa_devices(card, hdspm); @@ -4444,6 +6669,7 @@ static int __devinit snd_hdspm_create(struct snd_card *card, return 0; } + static int snd_hdspm_free(struct hdspm * hdspm) { @@ -4452,7 +6678,8 @@ static int snd_hdspm_free(struct hdspm * hdspm) /* stop th audio, and cancel all interrupts */ hdspm->control_register &= ~(HDSPM_Start | HDSPM_AudioInterruptEnable | - HDSPM_Midi0InterruptEnable | HDSPM_Midi1InterruptEnable); + HDSPM_Midi0InterruptEnable | HDSPM_Midi1InterruptEnable | + HDSPM_Midi2InterruptEnable | HDSPM_Midi3InterruptEnable); hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); } @@ -4472,6 +6699,7 @@ static int snd_hdspm_free(struct hdspm * hdspm) return 0; } + static void snd_hdspm_card_free(struct snd_card *card) { struct hdspm *hdspm = card->private_data; @@ -4480,6 +6708,7 @@ static void snd_hdspm_card_free(struct snd_card *card) snd_hdspm_free(hdspm); } + static int __devinit snd_hdspm_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { @@ -4496,7 +6725,7 @@ static int __devinit snd_hdspm_probe(struct pci_dev *pci, } err = snd_card_create(index[dev], id[dev], - THIS_MODULE, sizeof(struct hdspm), &card); + THIS_MODULE, sizeof(struct hdspm), &card); if (err < 0) return err; @@ -4507,16 +6736,25 @@ static int __devinit snd_hdspm_probe(struct pci_dev *pci, snd_card_set_dev(card, &pci->dev); - err = snd_hdspm_create(card, hdspm, precise_ptr[dev], - enable_monitor[dev]); + err = snd_hdspm_create(card, hdspm); if (err < 0) { snd_card_free(card); return err; } - strcpy(card->shortname, "HDSPM MADI"); - sprintf(card->longname, "%s at 0x%lx, irq %d", hdspm->card_name, - hdspm->port, hdspm->irq); + if (hdspm->io_type != MADIface) { + sprintf(card->shortname, "%s_%x", + hdspm->card_name, + (hdspm_read(hdspm, HDSPM_midiStatusIn0)>>8) & 0xFFFFFF); + sprintf(card->longname, "%s S/N 0x%x at 0x%lx, irq %d", + hdspm->card_name, + (hdspm_read(hdspm, HDSPM_midiStatusIn0)>>8) & 0xFFFFFF, + hdspm->port, hdspm->irq); + } else { + sprintf(card->shortname, "%s", hdspm->card_name); + sprintf(card->longname, "%s at 0x%lx, irq %d", + hdspm->card_name, hdspm->port, hdspm->irq); + } err = snd_card_register(card); if (err < 0) { diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index b47cfd45b3b9..3ecbd67f88c9 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -1034,7 +1034,11 @@ static int __devinit snd_pmac_detect(struct snd_pmac *chip) if (of_device_is_compatible(sound, "tumbler")) { chip->model = PMAC_TUMBLER; chip->can_capture = of_machine_is_compatible("PowerMac4,2") - || of_machine_is_compatible("PowerBook4,1"); + || of_machine_is_compatible("PowerBook3,2") + || of_machine_is_compatible("PowerBook3,3") + || of_machine_is_compatible("PowerBook4,1") + || of_machine_is_compatible("PowerBook4,2") + || of_machine_is_compatible("PowerBook4,3"); chip->can_duplex = 0; // chip->can_byte_swap = 0; /* FIXME: check this */ chip->num_freqs = ARRAY_SIZE(tumbler_freqs); diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index a3efc52a34da..8224db5f0434 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -50,10 +50,12 @@ source "sound/soc/jz4740/Kconfig" source "sound/soc/nuc900/Kconfig" source "sound/soc/omap/Kconfig" source "sound/soc/kirkwood/Kconfig" +source "sound/soc/mid-x86/Kconfig" source "sound/soc/pxa/Kconfig" source "sound/soc/samsung/Kconfig" source "sound/soc/s6000/Kconfig" source "sound/soc/sh/Kconfig" +source "sound/soc/tegra/Kconfig" source "sound/soc/txx9/Kconfig" # Supported codecs diff --git a/sound/soc/Makefile b/sound/soc/Makefile index ce913bf5213c..1ed61c5df2c5 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_SND_SOC) += ep93xx/ obj-$(CONFIG_SND_SOC) += fsl/ obj-$(CONFIG_SND_SOC) += imx/ obj-$(CONFIG_SND_SOC) += jz4740/ +obj-$(CONFIG_SND_SOC) += mid-x86/ obj-$(CONFIG_SND_SOC) += nuc900/ obj-$(CONFIG_SND_SOC) += omap/ obj-$(CONFIG_SND_SOC) += kirkwood/ @@ -17,4 +18,5 @@ obj-$(CONFIG_SND_SOC) += pxa/ obj-$(CONFIG_SND_SOC) += samsung/ obj-$(CONFIG_SND_SOC) += s6000/ obj-$(CONFIG_SND_SOC) += sh/ +obj-$(CONFIG_SND_SOC) += tegra/ obj-$(CONFIG_SND_SOC) += txx9/ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index c48b23c1d4fc..d63c1754e05f 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -26,17 +26,24 @@ config SND_SOC_ALL_CODECS select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC select SND_SOC_CS42L51 if I2C select SND_SOC_CS4270 if I2C + select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI select SND_SOC_CX20442 select SND_SOC_DA7210 if I2C + select SND_SOC_DFBMCS320 select SND_SOC_JZ4740_CODEC if SOC_JZ4740 + select SND_SOC_LM4857 if I2C select SND_SOC_MAX98088 if I2C + select SND_SOC_MAX9850 if I2C select SND_SOC_MAX9877 if I2C select SND_SOC_PCM3008 + select SND_SOC_SGTL5000 if I2C + select SND_SOC_SN95031 if INTEL_SCU_IPC select SND_SOC_SPDIF select SND_SOC_SSM2602 if I2C select SND_SOC_STAC9766 if SND_SOC_AC97_BUS select SND_SOC_TLV320AIC23 if I2C select SND_SOC_TLV320AIC26 if SPI_MASTER + select SND_SOC_TVL320AIC32X4 if I2C select SND_SOC_TLV320AIC3X if I2C select SND_SOC_TPA6130A2 if I2C select SND_SOC_TLV320DAC33 if I2C @@ -76,6 +83,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8985 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8990 if I2C + select SND_SOC_WM8991 if I2C select SND_SOC_WM8993 if I2C select SND_SOC_WM8994 if MFD_WM8994 select SND_SOC_WM8995 if SND_SOC_I2C_AND_SPI @@ -155,6 +163,9 @@ config SND_SOC_CS4270_VD33_ERRATA bool depends on SND_SOC_CS4270 +config SND_SOC_CS4271 + tristate + config SND_SOC_CX20442 tristate @@ -167,15 +178,28 @@ config SND_SOC_L3 config SND_SOC_DA7210 tristate +config SND_SOC_DFBMCS320 + tristate + config SND_SOC_DMIC tristate config SND_SOC_MAX98088 tristate +config SND_SOC_MAX9850 + tristate + config SND_SOC_PCM3008 tristate +#Freescale sgtl5000 codec +config SND_SOC_SGTL5000 + tristate + +config SND_SOC_SN95031 + tristate + config SND_SOC_SPDIF tristate @@ -192,6 +216,9 @@ config SND_SOC_TLV320AIC26 tristate "TI TLV320AIC26 Codec support" if SND_SOC_OF_SIMPLE depends on SPI +config SND_SOC_TVL320AIC32X4 + tristate + config SND_SOC_TLV320AIC3X tristate @@ -304,6 +331,9 @@ config SND_SOC_WM8988 config SND_SOC_WM8990 tristate +config SND_SOC_WM8991 + tristate + config SND_SOC_WM8993 tristate @@ -326,6 +356,9 @@ config SND_SOC_WM9713 tristate # Amp +config SND_SOC_LM4857 + tristate + config SND_SOC_MAX9877 tristate @@ -337,4 +370,3 @@ config SND_SOC_WM2000 config SND_SOC_WM9090 tristate - diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 579af9c4f128..379bc55f0723 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -12,19 +12,25 @@ snd-soc-ak4671-objs := ak4671.o snd-soc-cq93vc-objs := cq93vc.o snd-soc-cs42l51-objs := cs42l51.o snd-soc-cs4270-objs := cs4270.o +snd-soc-cs4271-objs := cs4271.o snd-soc-cx20442-objs := cx20442.o snd-soc-da7210-objs := da7210.o +snd-soc-dfbmcs320-objs := dfbmcs320.o snd-soc-dmic-objs := dmic.o snd-soc-l3-objs := l3.o snd-soc-max98088-objs := max98088.o +snd-soc-max9850-objs := max9850.o snd-soc-pcm3008-objs := pcm3008.o +snd-soc-sgtl5000-objs := sgtl5000.o snd-soc-alc5623-objs := alc5623.o +snd-soc-sn95031-objs := sn95031.o snd-soc-spdif-objs := spdif_transciever.o snd-soc-ssm2602-objs := ssm2602.o snd-soc-stac9766-objs := stac9766.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic26-objs := tlv320aic26.o snd-soc-tlv320aic3x-objs := tlv320aic3x.o +snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o snd-soc-tlv320dac33-objs := tlv320dac33.o snd-soc-twl4030-objs := twl4030.o snd-soc-twl6040-objs := twl6040.o @@ -61,6 +67,7 @@ snd-soc-wm8978-objs := wm8978.o snd-soc-wm8985-objs := wm8985.o snd-soc-wm8988-objs := wm8988.o snd-soc-wm8990-objs := wm8990.o +snd-soc-wm8991-objs := wm8991.o snd-soc-wm8993-objs := wm8993.o snd-soc-wm8994-objs := wm8994.o wm8994-tables.o snd-soc-wm8995-objs := wm8995.o @@ -72,6 +79,7 @@ snd-soc-wm-hubs-objs := wm_hubs.o snd-soc-jz4740-codec-objs := jz4740.o # Amp +snd-soc-lm4857-objs := lm4857.o snd-soc-max9877-objs := max9877.o snd-soc-tpa6130a2-objs := tpa6130a2.o snd-soc-wm2000-objs := wm2000.o @@ -88,23 +96,29 @@ obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o +obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o +obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o +obj-$(CONFIG_SND_SOC_DFBMCS320) += snd-soc-dfbmcs320.o obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o +obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o -obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o +obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o +obj-$(CONFIG_SND_SOC_SN95031) +=snd-soc-sn95031.o obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif.o obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o +obj-$(CONFIG_SND_SOC_TVL320AIC32X4) += snd-soc-tlv320aic32x4.o obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o @@ -141,6 +155,7 @@ obj-$(CONFIG_SND_SOC_WM8978) += snd-soc-wm8978.o obj-$(CONFIG_SND_SOC_WM8985) += snd-soc-wm8985.o obj-$(CONFIG_SND_SOC_WM8988) += snd-soc-wm8988.o obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o +obj-$(CONFIG_SND_SOC_WM8991) += snd-soc-wm8991.o obj-$(CONFIG_SND_SOC_WM8993) += snd-soc-wm8993.o obj-$(CONFIG_SND_SOC_WM8994) += snd-soc-wm8994.o obj-$(CONFIG_SND_SOC_WM8995) += snd-soc-wm8995.o @@ -151,6 +166,7 @@ obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o # Amp +obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o obj-$(CONFIG_SND_SOC_WM2000) += snd-soc-wm2000.o diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c index c27f8f59dc66..cbf0b6d400b8 100644 --- a/sound/soc/codecs/ak4104.c +++ b/sound/soc/codecs/ak4104.c @@ -294,7 +294,6 @@ static struct spi_driver ak4104_spi_driver = { static int __init ak4104_init(void) { - pr_info("Asahi Kasei AK4104 ALSA SoC Codec Driver\n"); return spi_register_driver(&ak4104_spi_driver); } module_init(ak4104_init); diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index f00eba313dfd..4be0570e3f1f 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -116,6 +116,12 @@ #define BCKO_MASK (1 << 3) #define BCKO_64 BCKO_MASK +#define DIF_MASK (3 << 0) +#define DSP (0 << 0) +#define RIGHT_J (1 << 0) +#define LEFT_J (2 << 0) +#define I2S (3 << 0) + /* MD_CTL2 */ #define FS0 (1 << 0) #define FS1 (1 << 1) @@ -354,6 +360,24 @@ static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) snd_soc_update_bits(codec, PW_MGMT2, MS, data); snd_soc_update_bits(codec, MD_CTL1, BCKO_MASK, bcko); + /* format type */ + data = 0; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_LEFT_J: + data = LEFT_J; + break; + case SND_SOC_DAIFMT_I2S: + data = I2S; + break; + /* FIXME + * Please add RIGHT_J / DSP support here + */ + default: + return -EINVAL; + break; + } + snd_soc_update_bits(codec, MD_CTL1, DIF_MASK, data); + return 0; } diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 8b51245f2318..0206a17d7283 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -193,12 +193,12 @@ static struct cs4270_mode_ratios cs4270_mode_ratios[] = { /* The number of MCLK/LRCK ratios supported by the CS4270 */ #define NUM_MCLK_RATIOS ARRAY_SIZE(cs4270_mode_ratios) -static int cs4270_reg_is_readable(unsigned int reg) +static int cs4270_reg_is_readable(struct snd_soc_codec *codec, unsigned int reg) { return (reg >= CS4270_FIRSTREG) && (reg <= CS4270_LASTREG); } -static int cs4270_reg_is_volatile(unsigned int reg) +static int cs4270_reg_is_volatile(struct snd_soc_codec *codec, unsigned int reg) { /* Unreadable registers are considered volatile */ if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG)) @@ -719,7 +719,7 @@ static int cs4270_i2c_remove(struct i2c_client *i2c_client) /* * cs4270_id - I2C device IDs supported by this driver */ -static struct i2c_device_id cs4270_id[] = { +static const struct i2c_device_id cs4270_id[] = { {"cs4270", 0}, {} }; @@ -743,8 +743,6 @@ static struct i2c_driver cs4270_i2c_driver = { static int __init cs4270_init(void) { - pr_info("Cirrus Logic CS4270 ALSA SoC Codec Driver\n"); - return i2c_add_driver(&cs4270_i2c_driver); } module_init(cs4270_init); diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c new file mode 100644 index 000000000000..083aab96ca80 --- /dev/null +++ b/sound/soc/codecs/cs4271.c @@ -0,0 +1,667 @@ +/* + * CS4271 ASoC codec driver + * + * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru> + * + * 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. + * + * This driver support CS4271 codec being master or slave, working + * in control port mode, connected either via SPI or I2C. + * The data format accepted is I2S or left-justified. + * DAPM support not implemented. + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/spi/spi.h> +#include <sound/cs4271.h> + +#define CS4271_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) +#define CS4271_PCM_RATES SNDRV_PCM_RATE_8000_192000 + +/* + * CS4271 registers + * High byte represents SPI chip address (0x10) + write command (0) + * Low byte - codec register address + */ +#define CS4271_MODE1 0x2001 /* Mode Control 1 */ +#define CS4271_DACCTL 0x2002 /* DAC Control */ +#define CS4271_DACVOL 0x2003 /* DAC Volume & Mixing Control */ +#define CS4271_VOLA 0x2004 /* DAC Channel A Volume Control */ +#define CS4271_VOLB 0x2005 /* DAC Channel B Volume Control */ +#define CS4271_ADCCTL 0x2006 /* ADC Control */ +#define CS4271_MODE2 0x2007 /* Mode Control 2 */ +#define CS4271_CHIPID 0x2008 /* Chip ID */ + +#define CS4271_FIRSTREG CS4271_MODE1 +#define CS4271_LASTREG CS4271_MODE2 +#define CS4271_NR_REGS ((CS4271_LASTREG & 0xFF) + 1) + +/* Bit masks for the CS4271 registers */ +#define CS4271_MODE1_MODE_MASK 0xC0 +#define CS4271_MODE1_MODE_1X 0x00 +#define CS4271_MODE1_MODE_2X 0x80 +#define CS4271_MODE1_MODE_4X 0xC0 + +#define CS4271_MODE1_DIV_MASK 0x30 +#define CS4271_MODE1_DIV_1 0x00 +#define CS4271_MODE1_DIV_15 0x10 +#define CS4271_MODE1_DIV_2 0x20 +#define CS4271_MODE1_DIV_3 0x30 + +#define CS4271_MODE1_MASTER 0x08 + +#define CS4271_MODE1_DAC_DIF_MASK 0x07 +#define CS4271_MODE1_DAC_DIF_LJ 0x00 +#define CS4271_MODE1_DAC_DIF_I2S 0x01 +#define CS4271_MODE1_DAC_DIF_RJ16 0x02 +#define CS4271_MODE1_DAC_DIF_RJ24 0x03 +#define CS4271_MODE1_DAC_DIF_RJ20 0x04 +#define CS4271_MODE1_DAC_DIF_RJ18 0x05 + +#define CS4271_DACCTL_AMUTE 0x80 +#define CS4271_DACCTL_IF_SLOW 0x40 + +#define CS4271_DACCTL_DEM_MASK 0x30 +#define CS4271_DACCTL_DEM_DIS 0x00 +#define CS4271_DACCTL_DEM_441 0x10 +#define CS4271_DACCTL_DEM_48 0x20 +#define CS4271_DACCTL_DEM_32 0x30 + +#define CS4271_DACCTL_SVRU 0x08 +#define CS4271_DACCTL_SRD 0x04 +#define CS4271_DACCTL_INVA 0x02 +#define CS4271_DACCTL_INVB 0x01 + +#define CS4271_DACVOL_BEQUA 0x40 +#define CS4271_DACVOL_SOFT 0x20 +#define CS4271_DACVOL_ZEROC 0x10 + +#define CS4271_DACVOL_ATAPI_MASK 0x0F +#define CS4271_DACVOL_ATAPI_M_M 0x00 +#define CS4271_DACVOL_ATAPI_M_BR 0x01 +#define CS4271_DACVOL_ATAPI_M_BL 0x02 +#define CS4271_DACVOL_ATAPI_M_BLR2 0x03 +#define CS4271_DACVOL_ATAPI_AR_M 0x04 +#define CS4271_DACVOL_ATAPI_AR_BR 0x05 +#define CS4271_DACVOL_ATAPI_AR_BL 0x06 +#define CS4271_DACVOL_ATAPI_AR_BLR2 0x07 +#define CS4271_DACVOL_ATAPI_AL_M 0x08 +#define CS4271_DACVOL_ATAPI_AL_BR 0x09 +#define CS4271_DACVOL_ATAPI_AL_BL 0x0A +#define CS4271_DACVOL_ATAPI_AL_BLR2 0x0B +#define CS4271_DACVOL_ATAPI_ALR2_M 0x0C +#define CS4271_DACVOL_ATAPI_ALR2_BR 0x0D +#define CS4271_DACVOL_ATAPI_ALR2_BL 0x0E +#define CS4271_DACVOL_ATAPI_ALR2_BLR2 0x0F + +#define CS4271_VOLA_MUTE 0x80 +#define CS4271_VOLA_VOL_MASK 0x7F +#define CS4271_VOLB_MUTE 0x80 +#define CS4271_VOLB_VOL_MASK 0x7F + +#define CS4271_ADCCTL_DITHER16 0x20 + +#define CS4271_ADCCTL_ADC_DIF_MASK 0x10 +#define CS4271_ADCCTL_ADC_DIF_LJ 0x00 +#define CS4271_ADCCTL_ADC_DIF_I2S 0x10 + +#define CS4271_ADCCTL_MUTEA 0x08 +#define CS4271_ADCCTL_MUTEB 0x04 +#define CS4271_ADCCTL_HPFDA 0x02 +#define CS4271_ADCCTL_HPFDB 0x01 + +#define CS4271_MODE2_LOOP 0x10 +#define CS4271_MODE2_MUTECAEQUB 0x08 +#define CS4271_MODE2_FREEZE 0x04 +#define CS4271_MODE2_CPEN 0x02 +#define CS4271_MODE2_PDN 0x01 + +#define CS4271_CHIPID_PART_MASK 0xF0 +#define CS4271_CHIPID_REV_MASK 0x0F + +/* + * Default CS4271 power-up configuration + * Array contains non-existing in hw register at address 0 + * Array do not include Chip ID, as codec driver does not use + * registers read operations at all + */ +static const u8 cs4271_dflt_reg[CS4271_NR_REGS] = { + 0, + 0, + CS4271_DACCTL_AMUTE, + CS4271_DACVOL_SOFT | CS4271_DACVOL_ATAPI_AL_BR, + 0, + 0, + 0, + 0, +}; + +struct cs4271_private { + /* SND_SOC_I2C or SND_SOC_SPI */ + enum snd_soc_control_type bus_type; + void *control_data; + unsigned int mclk; + bool master; + bool deemph; + /* Current sample rate for de-emphasis control */ + int rate; + /* GPIO driving Reset pin, if any */ + int gpio_nreset; + /* GPIO that disable serial bus, if any */ + int gpio_disable; +}; + +/* + * @freq is the desired MCLK rate + * MCLK rate should (c) be the sample rate, multiplied by one of the + * ratios listed in cs4271_mclk_fs_ratios table + */ +static int cs4271_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + + cs4271->mclk = freq; + return 0; +} + +static int cs4271_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int format) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + unsigned int val = 0; + int ret; + + switch (format & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + cs4271->master = 0; + break; + case SND_SOC_DAIFMT_CBM_CFM: + cs4271->master = 1; + val |= CS4271_MODE1_MASTER; + break; + default: + dev_err(codec->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_LEFT_J: + val |= CS4271_MODE1_DAC_DIF_LJ; + ret = snd_soc_update_bits(codec, CS4271_ADCCTL, + CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_LJ); + if (ret < 0) + return ret; + break; + case SND_SOC_DAIFMT_I2S: + val |= CS4271_MODE1_DAC_DIF_I2S; + ret = snd_soc_update_bits(codec, CS4271_ADCCTL, + CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_I2S); + if (ret < 0) + return ret; + break; + default: + dev_err(codec->dev, "Invalid DAI format\n"); + return -EINVAL; + } + + ret = snd_soc_update_bits(codec, CS4271_MODE1, + CS4271_MODE1_DAC_DIF_MASK | CS4271_MODE1_MASTER, val); + if (ret < 0) + return ret; + return 0; +} + +static int cs4271_deemph[] = {0, 44100, 48000, 32000}; + +static int cs4271_set_deemph(struct snd_soc_codec *codec) +{ + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + int i, ret; + int val = CS4271_DACCTL_DEM_DIS; + + if (cs4271->deemph) { + /* Find closest de-emphasis freq */ + val = 1; + for (i = 2; i < ARRAY_SIZE(cs4271_deemph); i++) + if (abs(cs4271_deemph[i] - cs4271->rate) < + abs(cs4271_deemph[val] - cs4271->rate)) + val = i; + val <<= 4; + } + + ret = snd_soc_update_bits(codec, CS4271_DACCTL, + CS4271_DACCTL_DEM_MASK, val); + if (ret < 0) + return ret; + return 0; +} + +static int cs4271_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = cs4271->deemph; + return 0; +} + +static int cs4271_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + + cs4271->deemph = ucontrol->value.enumerated.item[0]; + return cs4271_set_deemph(codec); +} + +struct cs4271_clk_cfg { + bool master; /* codec mode */ + u8 speed_mode; /* codec speed mode: 1x, 2x, 4x */ + unsigned short ratio; /* MCLK / sample rate */ + u8 ratio_mask; /* ratio bit mask for Master mode */ +}; + +static struct cs4271_clk_cfg cs4271_clk_tab[] = { + {1, CS4271_MODE1_MODE_1X, 256, CS4271_MODE1_DIV_1}, + {1, CS4271_MODE1_MODE_1X, 384, CS4271_MODE1_DIV_15}, + {1, CS4271_MODE1_MODE_1X, 512, CS4271_MODE1_DIV_2}, + {1, CS4271_MODE1_MODE_1X, 768, CS4271_MODE1_DIV_3}, + {1, CS4271_MODE1_MODE_2X, 128, CS4271_MODE1_DIV_1}, + {1, CS4271_MODE1_MODE_2X, 192, CS4271_MODE1_DIV_15}, + {1, CS4271_MODE1_MODE_2X, 256, CS4271_MODE1_DIV_2}, + {1, CS4271_MODE1_MODE_2X, 384, CS4271_MODE1_DIV_3}, + {1, CS4271_MODE1_MODE_4X, 64, CS4271_MODE1_DIV_1}, + {1, CS4271_MODE1_MODE_4X, 96, CS4271_MODE1_DIV_15}, + {1, CS4271_MODE1_MODE_4X, 128, CS4271_MODE1_DIV_2}, + {1, CS4271_MODE1_MODE_4X, 192, CS4271_MODE1_DIV_3}, + {0, CS4271_MODE1_MODE_1X, 256, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_1X, 384, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_1X, 512, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_1X, 768, CS4271_MODE1_DIV_2}, + {0, CS4271_MODE1_MODE_1X, 1024, CS4271_MODE1_DIV_2}, + {0, CS4271_MODE1_MODE_2X, 128, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_2X, 192, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_2X, 256, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_2X, 384, CS4271_MODE1_DIV_2}, + {0, CS4271_MODE1_MODE_2X, 512, CS4271_MODE1_DIV_2}, + {0, CS4271_MODE1_MODE_4X, 64, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_4X, 96, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_4X, 128, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_4X, 192, CS4271_MODE1_DIV_2}, + {0, CS4271_MODE1_MODE_4X, 256, CS4271_MODE1_DIV_2}, +}; + +#define CS4171_NR_RATIOS ARRAY_SIZE(cs4271_clk_tab) + +static int cs4271_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + int i, ret; + unsigned int ratio, val; + + cs4271->rate = params_rate(params); + + /* Configure DAC */ + if (cs4271->rate < 50000) + val = CS4271_MODE1_MODE_1X; + else if (cs4271->rate < 100000) + val = CS4271_MODE1_MODE_2X; + else + val = CS4271_MODE1_MODE_4X; + + ratio = cs4271->mclk / cs4271->rate; + for (i = 0; i < CS4171_NR_RATIOS; i++) + if ((cs4271_clk_tab[i].master == cs4271->master) && + (cs4271_clk_tab[i].speed_mode == val) && + (cs4271_clk_tab[i].ratio == ratio)) + break; + + if (i == CS4171_NR_RATIOS) { + dev_err(codec->dev, "Invalid sample rate\n"); + return -EINVAL; + } + + val |= cs4271_clk_tab[i].ratio_mask; + + ret = snd_soc_update_bits(codec, CS4271_MODE1, + CS4271_MODE1_MODE_MASK | CS4271_MODE1_DIV_MASK, val); + if (ret < 0) + return ret; + + return cs4271_set_deemph(codec); +} + +static int cs4271_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + int ret; + int val_a = 0; + int val_b = 0; + + if (mute) { + val_a = CS4271_VOLA_MUTE; + val_b = CS4271_VOLB_MUTE; + } + + ret = snd_soc_update_bits(codec, CS4271_VOLA, CS4271_VOLA_MUTE, val_a); + if (ret < 0) + return ret; + ret = snd_soc_update_bits(codec, CS4271_VOLB, CS4271_VOLB_MUTE, val_b); + if (ret < 0) + return ret; + + return 0; +} + +/* CS4271 controls */ +static DECLARE_TLV_DB_SCALE(cs4271_dac_tlv, -12700, 100, 0); + +static const struct snd_kcontrol_new cs4271_snd_controls[] = { + SOC_DOUBLE_R_TLV("Master Playback Volume", CS4271_VOLA, CS4271_VOLB, + 0, 0x7F, 1, cs4271_dac_tlv), + SOC_SINGLE("Digital Loopback Switch", CS4271_MODE2, 4, 1, 0), + SOC_SINGLE("Soft Ramp Switch", CS4271_DACVOL, 5, 1, 0), + SOC_SINGLE("Zero Cross Switch", CS4271_DACVOL, 4, 1, 0), + SOC_SINGLE_BOOL_EXT("De-emphasis Switch", 0, + cs4271_get_deemph, cs4271_put_deemph), + SOC_SINGLE("Auto-Mute Switch", CS4271_DACCTL, 7, 1, 0), + SOC_SINGLE("Slow Roll Off Filter Switch", CS4271_DACCTL, 6, 1, 0), + SOC_SINGLE("Soft Volume Ramp-Up Switch", CS4271_DACCTL, 3, 1, 0), + SOC_SINGLE("Soft Ramp-Down Switch", CS4271_DACCTL, 2, 1, 0), + SOC_SINGLE("Left Channel Inversion Switch", CS4271_DACCTL, 1, 1, 0), + SOC_SINGLE("Right Channel Inversion Switch", CS4271_DACCTL, 0, 1, 0), + SOC_DOUBLE("Master Capture Switch", CS4271_ADCCTL, 3, 2, 1, 1), + SOC_SINGLE("Dither 16-Bit Data Switch", CS4271_ADCCTL, 5, 1, 0), + SOC_DOUBLE("High Pass Filter Switch", CS4271_ADCCTL, 1, 0, 1, 1), + SOC_DOUBLE_R("Master Playback Switch", CS4271_VOLA, CS4271_VOLB, + 7, 1, 1), +}; + +static struct snd_soc_dai_ops cs4271_dai_ops = { + .hw_params = cs4271_hw_params, + .set_sysclk = cs4271_set_dai_sysclk, + .set_fmt = cs4271_set_dai_fmt, + .digital_mute = cs4271_digital_mute, +}; + +static struct snd_soc_dai_driver cs4271_dai = { + .name = "cs4271-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = CS4271_PCM_RATES, + .formats = CS4271_PCM_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = CS4271_PCM_RATES, + .formats = CS4271_PCM_FORMATS, + }, + .ops = &cs4271_dai_ops, + .symmetric_rates = 1, +}; + +#ifdef CONFIG_PM +static int cs4271_soc_suspend(struct snd_soc_codec *codec, pm_message_t mesg) +{ + int ret; + /* Set power-down bit */ + ret = snd_soc_update_bits(codec, CS4271_MODE2, 0, CS4271_MODE2_PDN); + if (ret < 0) + return ret; + return 0; +} + +static int cs4271_soc_resume(struct snd_soc_codec *codec) +{ + int ret; + /* Restore codec state */ + ret = snd_soc_cache_sync(codec); + if (ret < 0) + return ret; + /* then disable the power-down bit */ + ret = snd_soc_update_bits(codec, CS4271_MODE2, CS4271_MODE2_PDN, 0); + if (ret < 0) + return ret; + return 0; +} +#else +#define cs4271_soc_suspend NULL +#define cs4271_soc_resume NULL +#endif /* CONFIG_PM */ + +static int cs4271_probe(struct snd_soc_codec *codec) +{ + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + struct cs4271_platform_data *cs4271plat = codec->dev->platform_data; + int ret; + int gpio_nreset = -EINVAL; + + codec->control_data = cs4271->control_data; + + if (cs4271plat && gpio_is_valid(cs4271plat->gpio_nreset)) + gpio_nreset = cs4271plat->gpio_nreset; + + if (gpio_nreset >= 0) + if (gpio_request(gpio_nreset, "CS4271 Reset")) + gpio_nreset = -EINVAL; + if (gpio_nreset >= 0) { + /* Reset codec */ + gpio_direction_output(gpio_nreset, 0); + udelay(1); + gpio_set_value(gpio_nreset, 1); + /* Give the codec time to wake up */ + udelay(1); + } + + cs4271->gpio_nreset = gpio_nreset; + + /* + * In case of I2C, chip address specified in board data. + * So cache IO operations use 8 bit codec register address. + * In case of SPI, chip address and register address + * passed together as 16 bit value. + * Anyway, register address is masked with 0xFF inside + * soc-cache code. + */ + if (cs4271->bus_type == SND_SOC_SPI) + ret = snd_soc_codec_set_cache_io(codec, 16, 8, + cs4271->bus_type); + else + ret = snd_soc_codec_set_cache_io(codec, 8, 8, + cs4271->bus_type); + if (ret) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + + ret = snd_soc_update_bits(codec, CS4271_MODE2, 0, + CS4271_MODE2_PDN | CS4271_MODE2_CPEN); + if (ret < 0) + return ret; + ret = snd_soc_update_bits(codec, CS4271_MODE2, CS4271_MODE2_PDN, 0); + if (ret < 0) + return ret; + /* Power-up sequence requires 85 uS */ + udelay(85); + + return snd_soc_add_controls(codec, cs4271_snd_controls, + ARRAY_SIZE(cs4271_snd_controls)); +} + +static int cs4271_remove(struct snd_soc_codec *codec) +{ + struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); + int gpio_nreset; + + gpio_nreset = cs4271->gpio_nreset; + + if (gpio_is_valid(gpio_nreset)) { + /* Set codec to the reset state */ + gpio_set_value(gpio_nreset, 0); + gpio_free(gpio_nreset); + } + + return 0; +}; + +static struct snd_soc_codec_driver soc_codec_dev_cs4271 = { + .probe = cs4271_probe, + .remove = cs4271_remove, + .suspend = cs4271_soc_suspend, + .resume = cs4271_soc_resume, + .reg_cache_default = cs4271_dflt_reg, + .reg_cache_size = ARRAY_SIZE(cs4271_dflt_reg), + .reg_word_size = sizeof(cs4271_dflt_reg[0]), + .compress_type = SND_SOC_FLAT_COMPRESSION, +}; + +#if defined(CONFIG_SPI_MASTER) +static int __devinit cs4271_spi_probe(struct spi_device *spi) +{ + struct cs4271_private *cs4271; + + cs4271 = devm_kzalloc(&spi->dev, sizeof(*cs4271), GFP_KERNEL); + if (!cs4271) + return -ENOMEM; + + spi_set_drvdata(spi, cs4271); + cs4271->control_data = spi; + cs4271->bus_type = SND_SOC_SPI; + + return snd_soc_register_codec(&spi->dev, &soc_codec_dev_cs4271, + &cs4271_dai, 1); +} + +static int __devexit cs4271_spi_remove(struct spi_device *spi) +{ + snd_soc_unregister_codec(&spi->dev); + return 0; +} + +static struct spi_driver cs4271_spi_driver = { + .driver = { + .name = "cs4271", + .owner = THIS_MODULE, + }, + .probe = cs4271_spi_probe, + .remove = __devexit_p(cs4271_spi_remove), +}; +#endif /* defined(CONFIG_SPI_MASTER) */ + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static const struct i2c_device_id cs4271_i2c_id[] = { + {"cs4271", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id); + +static int __devinit cs4271_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cs4271_private *cs4271; + + cs4271 = devm_kzalloc(&client->dev, sizeof(*cs4271), GFP_KERNEL); + if (!cs4271) + return -ENOMEM; + + i2c_set_clientdata(client, cs4271); + cs4271->control_data = client; + cs4271->bus_type = SND_SOC_I2C; + + return snd_soc_register_codec(&client->dev, &soc_codec_dev_cs4271, + &cs4271_dai, 1); +} + +static int __devexit cs4271_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static struct i2c_driver cs4271_i2c_driver = { + .driver = { + .name = "cs4271", + .owner = THIS_MODULE, + }, + .id_table = cs4271_i2c_id, + .probe = cs4271_i2c_probe, + .remove = __devexit_p(cs4271_i2c_remove), +}; +#endif /* defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) */ + +/* + * We only register our serial bus driver here without + * assignment to particular chip. So if any of the below + * fails, there is some problem with I2C or SPI subsystem. + * In most cases this module will be compiled with support + * of only one serial bus. + */ +static int __init cs4271_modinit(void) +{ + int ret; + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + ret = i2c_add_driver(&cs4271_i2c_driver); + if (ret) { + pr_err("Failed to register CS4271 I2C driver: %d\n", ret); + return ret; + } +#endif + +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&cs4271_spi_driver); + if (ret) { + pr_err("Failed to register CS4271 SPI driver: %d\n", ret); + return ret; + } +#endif + + return 0; +} +module_init(cs4271_modinit); + +static void __exit cs4271_modexit(void) +{ +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&cs4271_spi_driver); +#endif + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + i2c_del_driver(&cs4271_i2c_driver); +#endif +} +module_exit(cs4271_modexit); + +MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>"); +MODULE_DESCRIPTION("Cirrus Logic CS4271 ALSA SoC Codec Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/dfbmcs320.c b/sound/soc/codecs/dfbmcs320.c new file mode 100644 index 000000000000..704bbde65737 --- /dev/null +++ b/sound/soc/codecs/dfbmcs320.c @@ -0,0 +1,72 @@ +/* + * Driver for the DFBM-CS320 bluetooth module + * Copyright 2011 Lars-Peter Clausen <lars@metafoo.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. + * + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <sound/soc.h> + +static struct snd_soc_dai_driver dfbmcs320_dai = { + .name = "dfbmcs320-pcm", + .playback = { + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}; + +static struct snd_soc_codec_driver soc_codec_dev_dfbmcs320; + +static int __devinit dfbmcs320_probe(struct platform_device *pdev) +{ + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_dfbmcs320, + &dfbmcs320_dai, 1); +} + +static int __devexit dfbmcs320_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + + return 0; +} + +static struct platform_driver dfmcs320_driver = { + .driver = { + .name = "dfbmcs320", + .owner = THIS_MODULE, + }, + .probe = dfbmcs320_probe, + .remove = __devexit_p(dfbmcs320_remove), +}; + +static int __init dfbmcs320_init(void) +{ + return platform_driver_register(&dfmcs320_driver); +} +module_init(dfbmcs320_init); + +static void __exit dfbmcs320_exit(void) +{ + platform_driver_unregister(&dfmcs320_driver); +} +module_exit(dfbmcs320_exit); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("ASoC DFBM-CS320 bluethooth module driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c new file mode 100644 index 000000000000..72de47e5d040 --- /dev/null +++ b/sound/soc/codecs/lm4857.c @@ -0,0 +1,276 @@ +/* + * LM4857 AMP driver + * + * Copyright 2007 Wolfson Microelectronics PLC. + * Author: Graeme Gregory + * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com + * Copyright 2011 Lars-Peter Clausen <lars@metafoo.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. + * + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/slab.h> + +#include <sound/core.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +struct lm4857 { + struct i2c_client *i2c; + uint8_t mode; +}; + +static const uint8_t lm4857_default_regs[] = { + 0x00, 0x00, 0x00, 0x00, +}; + +/* The register offsets in the cache array */ +#define LM4857_MVOL 0 +#define LM4857_LVOL 1 +#define LM4857_RVOL 2 +#define LM4857_CTRL 3 + +/* the shifts required to set these bits */ +#define LM4857_3D 5 +#define LM4857_WAKEUP 5 +#define LM4857_EPGAIN 4 + +static int lm4857_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + uint8_t data; + int ret; + + ret = snd_soc_cache_write(codec, reg, value); + if (ret < 0) + return ret; + + data = (reg << 6) | value; + ret = i2c_master_send(codec->control_data, &data, 1); + if (ret != 1) { + dev_err(codec->dev, "Failed to write register: %d\n", ret); + return ret; + } + + return 0; +} + +static unsigned int lm4857_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + unsigned int val; + int ret; + + ret = snd_soc_cache_read(codec, reg, &val); + if (ret) + return -1; + + return val; +} + +static int lm4857_get_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = lm4857->mode; + + return 0; +} + +static int lm4857_set_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec); + uint8_t value = ucontrol->value.integer.value[0]; + + lm4857->mode = value; + + if (codec->dapm.bias_level == SND_SOC_BIAS_ON) + snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, value + 6); + + return 1; +} + +static int lm4857_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec); + + switch (level) { + case SND_SOC_BIAS_ON: + snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, lm4857->mode + 6); + break; + case SND_SOC_BIAS_STANDBY: + snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, 0); + break; + default: + break; + } + + codec->dapm.bias_level = level; + + return 0; +} + +static const char *lm4857_mode[] = { + "Earpiece", + "Loudspeaker", + "Loudspeaker + Headphone", + "Headphone", +}; + +static const struct soc_enum lm4857_mode_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lm4857_mode), lm4857_mode); + +static const struct snd_soc_dapm_widget lm4857_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("IN"), + + SND_SOC_DAPM_OUTPUT("LS"), + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_OUTPUT("EP"), +}; + +static const DECLARE_TLV_DB_SCALE(stereo_tlv, -4050, 150, 0); +static const DECLARE_TLV_DB_SCALE(mono_tlv, -3450, 150, 0); + +static const struct snd_kcontrol_new lm4857_controls[] = { + SOC_SINGLE_TLV("Left Playback Volume", LM4857_LVOL, 0, 31, 0, + stereo_tlv), + SOC_SINGLE_TLV("Right Playback Volume", LM4857_RVOL, 0, 31, 0, + stereo_tlv), + SOC_SINGLE_TLV("Mono Playback Volume", LM4857_MVOL, 0, 31, 0, + mono_tlv), + SOC_SINGLE("Spk 3D Playback Switch", LM4857_LVOL, LM4857_3D, 1, 0), + SOC_SINGLE("HP 3D Playback Switch", LM4857_RVOL, LM4857_3D, 1, 0), + SOC_SINGLE("Fast Wakeup Playback Switch", LM4857_CTRL, + LM4857_WAKEUP, 1, 0), + SOC_SINGLE("Earpiece 6dB Playback Switch", LM4857_CTRL, + LM4857_EPGAIN, 1, 0), + + SOC_ENUM_EXT("Mode", lm4857_mode_enum, + lm4857_get_mode, lm4857_set_mode), +}; + +/* There is a demux inbetween the the input signal and the output signals. + * Currently there is no easy way to model it in ASoC and since it does not make + * much of a difference in practice simply connect the input direclty to the + * outputs. */ +static const struct snd_soc_dapm_route lm4857_routes[] = { + {"LS", NULL, "IN"}, + {"HP", NULL, "IN"}, + {"EP", NULL, "IN"}, +}; + +static int lm4857_probe(struct snd_soc_codec *codec) +{ + struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = &codec->dapm; + int ret; + + codec->control_data = lm4857->i2c; + + ret = snd_soc_add_controls(codec, lm4857_controls, + ARRAY_SIZE(lm4857_controls)); + if (ret) + return ret; + + ret = snd_soc_dapm_new_controls(dapm, lm4857_dapm_widgets, + ARRAY_SIZE(lm4857_dapm_widgets)); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, lm4857_routes, + ARRAY_SIZE(lm4857_routes)); + if (ret) + return ret; + + snd_soc_dapm_new_widgets(dapm); + + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_lm4857 = { + .write = lm4857_write, + .read = lm4857_read, + .probe = lm4857_probe, + .reg_cache_size = ARRAY_SIZE(lm4857_default_regs), + .reg_word_size = sizeof(uint8_t), + .reg_cache_default = lm4857_default_regs, + .set_bias_level = lm4857_set_bias_level, +}; + +static int __devinit lm4857_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct lm4857 *lm4857; + int ret; + + lm4857 = kzalloc(sizeof(*lm4857), GFP_KERNEL); + if (!lm4857) + return -ENOMEM; + + i2c_set_clientdata(i2c, lm4857); + + lm4857->i2c = i2c; + + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_lm4857, NULL, 0); + + if (ret) { + kfree(lm4857); + return ret; + } + + return 0; +} + +static int __devexit lm4857_i2c_remove(struct i2c_client *i2c) +{ + struct lm4857 *lm4857 = i2c_get_clientdata(i2c); + + snd_soc_unregister_codec(&i2c->dev); + kfree(lm4857); + + return 0; +} + +static const struct i2c_device_id lm4857_i2c_id[] = { + { "lm4857", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm4857_i2c_id); + +static struct i2c_driver lm4857_i2c_driver = { + .driver = { + .name = "lm4857", + .owner = THIS_MODULE, + }, + .probe = lm4857_i2c_probe, + .remove = __devexit_p(lm4857_i2c_remove), + .id_table = lm4857_i2c_id, +}; + +static int __init lm4857_init(void) +{ + return i2c_add_driver(&lm4857_i2c_driver); +} +module_init(lm4857_init); + +static void __exit lm4857_exit(void) +{ + i2c_del_driver(&lm4857_i2c_driver); +} +module_exit(lm4857_exit); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("LM4857 amplifier driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index 89498f9ad2e5..bd0517cb7980 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -608,7 +608,7 @@ static struct { { 0xFF, 0x00, 1 }, /* FF */ }; -static int max98088_volatile_register(unsigned int reg) +static int max98088_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { return max98088_access[reg].vol; } diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c new file mode 100644 index 000000000000..208d2ee61855 --- /dev/null +++ b/sound/soc/codecs/max9850.c @@ -0,0 +1,389 @@ +/* + * max9850.c -- codec driver for max9850 + * + * Copyright (C) 2011 taskit GmbH + * + * Author: Christian Glindkamp <christian.glindkamp@taskit.de> + * + * Initial development of this code was funded by + * MICRONIC Computer Systeme GmbH, http://www.mcsberlin.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. + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#include "max9850.h" + +struct max9850_priv { + unsigned int sysclk; +}; + +/* max9850 register cache */ +static const u8 max9850_reg[MAX9850_CACHEREGNUM] = { + 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* these registers are not used at the moment but provided for the sake of + * completeness */ +static int max9850_volatile_register(struct snd_soc_codec *codec, + unsigned int reg) +{ + switch (reg) { + case MAX9850_STATUSA: + case MAX9850_STATUSB: + return 1; + default: + return 0; + } +} + +static const unsigned int max9850_tlv[] = { + TLV_DB_RANGE_HEAD(4), + 0x18, 0x1f, TLV_DB_SCALE_ITEM(-7450, 400, 0), + 0x20, 0x33, TLV_DB_SCALE_ITEM(-4150, 200, 0), + 0x34, 0x37, TLV_DB_SCALE_ITEM(-150, 100, 0), + 0x38, 0x3f, TLV_DB_SCALE_ITEM(250, 50, 0), +}; + +static const struct snd_kcontrol_new max9850_controls[] = { +SOC_SINGLE_TLV("Headphone Volume", MAX9850_VOLUME, 0, 0x3f, 1, max9850_tlv), +SOC_SINGLE("Headphone Switch", MAX9850_VOLUME, 7, 1, 1), +SOC_SINGLE("Mono Switch", MAX9850_GENERAL_PURPOSE, 2, 1, 0), +}; + +static const struct snd_kcontrol_new max9850_mixer_controls[] = { + SOC_DAPM_SINGLE("Line In Switch", MAX9850_ENABLE, 1, 1, 0), +}; + +static const struct snd_soc_dapm_widget max9850_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("Charge Pump 1", MAX9850_ENABLE, 4, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("Charge Pump 2", MAX9850_ENABLE, 5, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MCLK", MAX9850_ENABLE, 6, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("SHDN", MAX9850_ENABLE, 7, 0, NULL, 0), +SND_SOC_DAPM_MIXER_NAMED_CTL("Output Mixer", MAX9850_ENABLE, 2, 0, + &max9850_mixer_controls[0], + ARRAY_SIZE(max9850_mixer_controls)), +SND_SOC_DAPM_PGA("Headphone Output", MAX9850_ENABLE, 3, 0, NULL, 0), +SND_SOC_DAPM_DAC("DAC", "HiFi Playback", MAX9850_ENABLE, 0, 0), +SND_SOC_DAPM_OUTPUT("OUTL"), +SND_SOC_DAPM_OUTPUT("HPL"), +SND_SOC_DAPM_OUTPUT("OUTR"), +SND_SOC_DAPM_OUTPUT("HPR"), +SND_SOC_DAPM_MIXER("Line Input", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_INPUT("INL"), +SND_SOC_DAPM_INPUT("INR"), +}; + +static const struct snd_soc_dapm_route intercon[] = { + /* output mixer */ + {"Output Mixer", NULL, "DAC"}, + {"Output Mixer", "Line In Switch", "Line Input"}, + + /* outputs */ + {"Headphone Output", NULL, "Output Mixer"}, + {"HPL", NULL, "Headphone Output"}, + {"HPR", NULL, "Headphone Output"}, + {"OUTL", NULL, "Output Mixer"}, + {"OUTR", NULL, "Output Mixer"}, + + /* inputs */ + {"Line Input", NULL, "INL"}, + {"Line Input", NULL, "INR"}, + + /* supplies */ + {"Output Mixer", NULL, "Charge Pump 1"}, + {"Output Mixer", NULL, "Charge Pump 2"}, + {"Output Mixer", NULL, "SHDN"}, + {"DAC", NULL, "MCLK"}, +}; + +static int max9850_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct max9850_priv *max9850 = snd_soc_codec_get_drvdata(codec); + u64 lrclk_div; + u8 sf, da; + + if (!max9850->sysclk) + return -EINVAL; + + /* lrclk_div = 2^22 * rate / iclk with iclk = mclk / sf */ + sf = (snd_soc_read(codec, MAX9850_CLOCK) >> 2) + 1; + lrclk_div = (1 << 22); + lrclk_div *= params_rate(params); + lrclk_div *= sf; + do_div(lrclk_div, max9850->sysclk); + + snd_soc_write(codec, MAX9850_LRCLK_MSB, (lrclk_div >> 8) & 0x7f); + snd_soc_write(codec, MAX9850_LRCLK_LSB, lrclk_div & 0xff); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + da = 0; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + da = 0x2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + da = 0x3; + break; + default: + return -EINVAL; + } + snd_soc_update_bits(codec, MAX9850_DIGITAL_AUDIO, 0x3, da); + + return 0; +} + +static int max9850_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct max9850_priv *max9850 = snd_soc_codec_get_drvdata(codec); + + /* calculate mclk -> iclk divider */ + if (freq <= 13000000) + snd_soc_write(codec, MAX9850_CLOCK, 0x0); + else if (freq <= 26000000) + snd_soc_write(codec, MAX9850_CLOCK, 0x4); + else if (freq <= 40000000) + snd_soc_write(codec, MAX9850_CLOCK, 0x8); + else + return -EINVAL; + + max9850->sysclk = freq; + return 0; +} + +static int max9850_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u8 da = 0; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + da |= MAX9850_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + da |= MAX9850_DLY; + break; + case SND_SOC_DAIFMT_RIGHT_J: + da |= MAX9850_RTJ; + break; + case SND_SOC_DAIFMT_LEFT_J: + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + da |= MAX9850_BCINV | MAX9850_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + da |= MAX9850_BCINV; + break; + case SND_SOC_DAIFMT_NB_IF: + da |= MAX9850_INV; + break; + default: + return -EINVAL; + } + + /* set da */ + snd_soc_write(codec, MAX9850_DIGITAL_AUDIO, da); + + return 0; +} + +static int max9850_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + ret = snd_soc_cache_sync(codec); + if (ret) { + dev_err(codec->dev, + "Failed to sync cache: %d\n", ret); + return ret; + } + } + break; + case SND_SOC_BIAS_OFF: + break; + } + codec->dapm.bias_level = level; + return 0; +} + +#define MAX9850_RATES SNDRV_PCM_RATE_8000_48000 + +#define MAX9850_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_ops max9850_dai_ops = { + .hw_params = max9850_hw_params, + .set_sysclk = max9850_set_dai_sysclk, + .set_fmt = max9850_set_dai_fmt, +}; + +static struct snd_soc_dai_driver max9850_dai = { + .name = "max9850-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX9850_RATES, + .formats = MAX9850_FORMATS + }, + .ops = &max9850_dai_ops, +}; + +#ifdef CONFIG_PM +static int max9850_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ + max9850_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static int max9850_resume(struct snd_soc_codec *codec) +{ + max9850_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} +#else +#define max9850_suspend NULL +#define max9850_resume NULL +#endif + +static int max9850_probe(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = &codec->dapm; + int ret; + + ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C); + if (ret < 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + + /* enable zero-detect */ + snd_soc_update_bits(codec, MAX9850_GENERAL_PURPOSE, 1, 1); + /* enable slew-rate control */ + snd_soc_update_bits(codec, MAX9850_VOLUME, 0x40, 0x40); + /* set slew-rate 125ms */ + snd_soc_update_bits(codec, MAX9850_CHARGE_PUMP, 0xff, 0xc0); + + snd_soc_dapm_new_controls(dapm, max9850_dapm_widgets, + ARRAY_SIZE(max9850_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); + + snd_soc_add_controls(codec, max9850_controls, + ARRAY_SIZE(max9850_controls)); + + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_max9850 = { + .probe = max9850_probe, + .suspend = max9850_suspend, + .resume = max9850_resume, + .set_bias_level = max9850_set_bias_level, + .reg_cache_size = ARRAY_SIZE(max9850_reg), + .reg_word_size = sizeof(u8), + .reg_cache_default = max9850_reg, + .volatile_register = max9850_volatile_register, +}; + +static int __devinit max9850_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct max9850_priv *max9850; + int ret; + + max9850 = kzalloc(sizeof(struct max9850_priv), GFP_KERNEL); + if (max9850 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, max9850); + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_max9850, &max9850_dai, 1); + if (ret < 0) + kfree(max9850); + return ret; +} + +static __devexit int max9850_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + kfree(i2c_get_clientdata(client)); + return 0; +} + +static const struct i2c_device_id max9850_i2c_id[] = { + { "max9850", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max9850_i2c_id); + +static struct i2c_driver max9850_i2c_driver = { + .driver = { + .name = "max9850", + .owner = THIS_MODULE, + }, + .probe = max9850_i2c_probe, + .remove = __devexit_p(max9850_i2c_remove), + .id_table = max9850_i2c_id, +}; + +static int __init max9850_init(void) +{ + return i2c_add_driver(&max9850_i2c_driver); +} +module_init(max9850_init); + +static void __exit max9850_exit(void) +{ + i2c_del_driver(&max9850_i2c_driver); +} +module_exit(max9850_exit); + +MODULE_AUTHOR("Christian Glindkamp <christian.glindkamp@taskit.de>"); +MODULE_DESCRIPTION("ASoC MAX9850 codec driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max9850.h b/sound/soc/codecs/max9850.h new file mode 100644 index 000000000000..72b1ddb04b0d --- /dev/null +++ b/sound/soc/codecs/max9850.h @@ -0,0 +1,38 @@ +/* + * max9850.h -- codec driver for max9850 + * + * Copyright (C) 2011 taskit GmbH + * Author: Christian Glindkamp <christian.glindkamp@taskit.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. + * + */ + +#ifndef _MAX9850_H +#define _MAX9850_H + +#define MAX9850_STATUSA 0x00 +#define MAX9850_STATUSB 0x01 +#define MAX9850_VOLUME 0x02 +#define MAX9850_GENERAL_PURPOSE 0x03 +#define MAX9850_INTERRUPT 0x04 +#define MAX9850_ENABLE 0x05 +#define MAX9850_CLOCK 0x06 +#define MAX9850_CHARGE_PUMP 0x07 +#define MAX9850_LRCLK_MSB 0x08 +#define MAX9850_LRCLK_LSB 0x09 +#define MAX9850_DIGITAL_AUDIO 0x0a + +#define MAX9850_CACHEREGNUM 11 + +/* MAX9850_DIGITAL_AUDIO */ +#define MAX9850_MASTER (1<<7) +#define MAX9850_INV (1<<6) +#define MAX9850_BCINV (1<<5) +#define MAX9850_DLY (1<<3) +#define MAX9850_RTJ (1<<2) + +#endif diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c new file mode 100644 index 000000000000..1f7217f703ee --- /dev/null +++ b/sound/soc/codecs/sgtl5000.c @@ -0,0 +1,1513 @@ +/* + * sgtl5000.c -- SGTL5000 ALSA SoC Audio driver + * + * Copyright 2010-2011 Freescale Semiconductor, Inc. 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 version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/consumer.h> +#include <sound/core.h> +#include <sound/tlv.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> + +#include "sgtl5000.h" + +#define SGTL5000_DAP_REG_OFFSET 0x0100 +#define SGTL5000_MAX_REG_OFFSET 0x013A + +/* default value of sgtl5000 registers except DAP */ +static const u16 sgtl5000_regs[SGTL5000_MAX_REG_OFFSET >> 1] = { + 0xa011, /* 0x0000, CHIP_ID. 11 stand for revison 17 */ + 0x0000, /* 0x0002, CHIP_DIG_POWER. */ + 0x0008, /* 0x0004, CHIP_CKL_CTRL */ + 0x0010, /* 0x0006, CHIP_I2S_CTRL */ + 0x0000, /* 0x0008, reserved */ + 0x0008, /* 0x000A, CHIP_SSS_CTRL */ + 0x0000, /* 0x000C, reserved */ + 0x020c, /* 0x000E, CHIP_ADCDAC_CTRL */ + 0x3c3c, /* 0x0010, CHIP_DAC_VOL */ + 0x0000, /* 0x0012, reserved */ + 0x015f, /* 0x0014, CHIP_PAD_STRENGTH */ + 0x0000, /* 0x0016, reserved */ + 0x0000, /* 0x0018, reserved */ + 0x0000, /* 0x001A, reserved */ + 0x0000, /* 0x001E, reserved */ + 0x0000, /* 0x0020, CHIP_ANA_ADC_CTRL */ + 0x1818, /* 0x0022, CHIP_ANA_HP_CTRL */ + 0x0111, /* 0x0024, CHIP_ANN_CTRL */ + 0x0000, /* 0x0026, CHIP_LINREG_CTRL */ + 0x0000, /* 0x0028, CHIP_REF_CTRL */ + 0x0000, /* 0x002A, CHIP_MIC_CTRL */ + 0x0000, /* 0x002C, CHIP_LINE_OUT_CTRL */ + 0x0404, /* 0x002E, CHIP_LINE_OUT_VOL */ + 0x7060, /* 0x0030, CHIP_ANA_POWER */ + 0x5000, /* 0x0032, CHIP_PLL_CTRL */ + 0x0000, /* 0x0034, CHIP_CLK_TOP_CTRL */ + 0x0000, /* 0x0036, CHIP_ANA_STATUS */ + 0x0000, /* 0x0038, reserved */ + 0x0000, /* 0x003A, CHIP_ANA_TEST2 */ + 0x0000, /* 0x003C, CHIP_SHORT_CTRL */ + 0x0000, /* reserved */ +}; + +/* default value of dap registers */ +static const u16 sgtl5000_dap_regs[] = { + 0x0000, /* 0x0100, DAP_CONTROL */ + 0x0000, /* 0x0102, DAP_PEQ */ + 0x0040, /* 0x0104, DAP_BASS_ENHANCE */ + 0x051f, /* 0x0106, DAP_BASS_ENHANCE_CTRL */ + 0x0000, /* 0x0108, DAP_AUDIO_EQ */ + 0x0040, /* 0x010A, DAP_SGTL_SURROUND */ + 0x0000, /* 0x010C, DAP_FILTER_COEF_ACCESS */ + 0x0000, /* 0x010E, DAP_COEF_WR_B0_MSB */ + 0x0000, /* 0x0110, DAP_COEF_WR_B0_LSB */ + 0x0000, /* 0x0112, reserved */ + 0x0000, /* 0x0114, reserved */ + 0x002f, /* 0x0116, DAP_AUDIO_EQ_BASS_BAND0 */ + 0x002f, /* 0x0118, DAP_AUDIO_EQ_BAND0 */ + 0x002f, /* 0x011A, DAP_AUDIO_EQ_BAND2 */ + 0x002f, /* 0x011C, DAP_AUDIO_EQ_BAND3 */ + 0x002f, /* 0x011E, DAP_AUDIO_EQ_TREBLE_BAND4 */ + 0x8000, /* 0x0120, DAP_MAIN_CHAN */ + 0x0000, /* 0x0122, DAP_MIX_CHAN */ + 0x0510, /* 0x0124, DAP_AVC_CTRL */ + 0x1473, /* 0x0126, DAP_AVC_THRESHOLD */ + 0x0028, /* 0x0128, DAP_AVC_ATTACK */ + 0x0050, /* 0x012A, DAP_AVC_DECAY */ + 0x0000, /* 0x012C, DAP_COEF_WR_B1_MSB */ + 0x0000, /* 0x012E, DAP_COEF_WR_B1_LSB */ + 0x0000, /* 0x0130, DAP_COEF_WR_B2_MSB */ + 0x0000, /* 0x0132, DAP_COEF_WR_B2_LSB */ + 0x0000, /* 0x0134, DAP_COEF_WR_A1_MSB */ + 0x0000, /* 0x0136, DAP_COEF_WR_A1_LSB */ + 0x0000, /* 0x0138, DAP_COEF_WR_A2_MSB */ + 0x0000, /* 0x013A, DAP_COEF_WR_A2_LSB */ +}; + +/* regulator supplies for sgtl5000, VDDD is an optional external supply */ +enum sgtl5000_regulator_supplies { + VDDA, + VDDIO, + VDDD, + SGTL5000_SUPPLY_NUM +}; + +/* vddd is optional supply */ +static const char *supply_names[SGTL5000_SUPPLY_NUM] = { + "VDDA", + "VDDIO", + "VDDD" +}; + +#define LDO_CONSUMER_NAME "VDDD_LDO" +#define LDO_VOLTAGE 1200000 + +static struct regulator_consumer_supply ldo_consumer[] = { + REGULATOR_SUPPLY(LDO_CONSUMER_NAME, NULL), +}; + +static struct regulator_init_data ldo_init_data = { + .constraints = { + .min_uV = 850000, + .max_uV = 1600000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = 1, + .consumer_supplies = &ldo_consumer[0], +}; + +/* + * sgtl5000 internal ldo regulator, + * enabled when VDDD not provided + */ +struct ldo_regulator { + struct regulator_desc desc; + struct regulator_dev *dev; + int voltage; + void *codec_data; + bool enabled; +}; + +/* sgtl5000 private structure in codec */ +struct sgtl5000_priv { + int sysclk; /* sysclk rate */ + int master; /* i2s master or not */ + int fmt; /* i2s data format */ + struct regulator_bulk_data supplies[SGTL5000_SUPPLY_NUM]; + struct ldo_regulator *ldo; +}; + +/* + * mic_bias power on/off share the same register bits with + * output impedance of mic bias, when power on mic bias, we + * need reclaim it to impedance value. + * 0x0 = Powered off + * 0x1 = 2Kohm + * 0x2 = 4Kohm + * 0x3 = 8Kohm + */ +static int mic_bias_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* change mic bias resistor to 4Kohm */ + snd_soc_update_bits(w->codec, SGTL5000_CHIP_MIC_CTRL, + SGTL5000_BIAS_R_4k, SGTL5000_BIAS_R_4k); + break; + + case SND_SOC_DAPM_PRE_PMD: + /* + * SGTL5000_BIAS_R_8k as mask to clean the two bits + * of mic bias and output impedance + */ + snd_soc_update_bits(w->codec, SGTL5000_CHIP_MIC_CTRL, + SGTL5000_BIAS_R_8k, 0); + break; + } + return 0; +} + +/* + * using codec assist to small pop, hp_powerup or lineout_powerup + * should stay setting until vag_powerup is fully ramped down, + * vag fully ramped down require 400ms. + */ +static int small_pop_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_VAG_POWERUP, SGTL5000_VAG_POWERUP); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_VAG_POWERUP, 0); + msleep(400); + break; + default: + break; + } + + return 0; +} + +/* input sources for ADC */ +static const char *adc_mux_text[] = { + "MIC_IN", "LINE_IN" +}; + +static const struct soc_enum adc_enum = +SOC_ENUM_SINGLE(SGTL5000_CHIP_ANA_CTRL, 2, 2, adc_mux_text); + +static const struct snd_kcontrol_new adc_mux = +SOC_DAPM_ENUM("Capture Mux", adc_enum); + +/* input sources for DAC */ +static const char *dac_mux_text[] = { + "DAC", "LINE_IN" +}; + +static const struct soc_enum dac_enum = +SOC_ENUM_SINGLE(SGTL5000_CHIP_ANA_CTRL, 6, 2, dac_mux_text); + +static const struct snd_kcontrol_new dac_mux = +SOC_DAPM_ENUM("Headphone Mux", dac_enum); + +static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("LINE_IN"), + SND_SOC_DAPM_INPUT("MIC_IN"), + + SND_SOC_DAPM_OUTPUT("HP_OUT"), + SND_SOC_DAPM_OUTPUT("LINE_OUT"), + + SND_SOC_DAPM_MICBIAS_E("Mic Bias", SGTL5000_CHIP_MIC_CTRL, 8, 0, + mic_bias_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_PGA_E("HP", SGTL5000_CHIP_ANA_POWER, 4, 0, NULL, 0, + small_pop_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_E("LO", SGTL5000_CHIP_ANA_POWER, 0, 0, NULL, 0, + small_pop_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_MUX("Capture Mux", SND_SOC_NOPM, 0, 0, &adc_mux), + SND_SOC_DAPM_MUX("Headphone Mux", SND_SOC_NOPM, 0, 0, &dac_mux), + + /* aif for i2s input */ + SND_SOC_DAPM_AIF_IN("AIFIN", "Playback", + 0, SGTL5000_CHIP_DIG_POWER, + 0, 0), + + /* aif for i2s output */ + SND_SOC_DAPM_AIF_OUT("AIFOUT", "Capture", + 0, SGTL5000_CHIP_DIG_POWER, + 1, 0), + + SND_SOC_DAPM_ADC("ADC", "Capture", SGTL5000_CHIP_ANA_POWER, 1, 0), + + SND_SOC_DAPM_DAC("DAC", "Playback", SGTL5000_CHIP_ANA_POWER, 3, 0), +}; + +/* routes for sgtl5000 */ +static const struct snd_soc_dapm_route audio_map[] = { + {"Capture Mux", "LINE_IN", "LINE_IN"}, /* line_in --> adc_mux */ + {"Capture Mux", "MIC_IN", "MIC_IN"}, /* mic_in --> adc_mux */ + + {"ADC", NULL, "Capture Mux"}, /* adc_mux --> adc */ + {"AIFOUT", NULL, "ADC"}, /* adc --> i2s_out */ + + {"DAC", NULL, "AIFIN"}, /* i2s-->dac,skip audio mux */ + {"Headphone Mux", "DAC", "DAC"}, /* dac --> hp_mux */ + {"LO", NULL, "DAC"}, /* dac --> line_out */ + + {"Headphone Mux", "LINE_IN", "LINE_IN"},/* line_in --> hp_mux */ + {"HP", NULL, "Headphone Mux"}, /* hp_mux --> hp */ + + {"LINE_OUT", NULL, "LO"}, + {"HP_OUT", NULL, "HP"}, +}; + +/* custom function to fetch info of PCM playback volume */ +static int dac_info_volsw(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 = 0xfc - 0x3c; + return 0; +} + +/* + * custom function to get of PCM playback volume + * + * dac volume register + * 15-------------8-7--------------0 + * | R channel vol | L channel vol | + * ------------------------------- + * + * PCM volume with 0.5017 dB steps from 0 to -90 dB + * + * register values map to dB + * 0x3B and less = Reserved + * 0x3C = 0 dB + * 0x3D = -0.5 dB + * 0xF0 = -90 dB + * 0xFC and greater = Muted + * + * register value map to userspace value + * + * register value 0x3c(0dB) 0xf0(-90dB)0xfc + * ------------------------------ + * userspace value 0xc0 0 + */ +static int dac_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + int reg; + int l; + int r; + + reg = snd_soc_read(codec, SGTL5000_CHIP_DAC_VOL); + + /* get left channel volume */ + l = (reg & SGTL5000_DAC_VOL_LEFT_MASK) >> SGTL5000_DAC_VOL_LEFT_SHIFT; + + /* get right channel volume */ + r = (reg & SGTL5000_DAC_VOL_RIGHT_MASK) >> SGTL5000_DAC_VOL_RIGHT_SHIFT; + + /* make sure value fall in (0x3c,0xfc) */ + l = clamp(l, 0x3c, 0xfc); + r = clamp(r, 0x3c, 0xfc); + + /* invert it and map to userspace value */ + l = 0xfc - l; + r = 0xfc - r; + + ucontrol->value.integer.value[0] = l; + ucontrol->value.integer.value[1] = r; + + return 0; +} + +/* + * custom function to put of PCM playback volume + * + * dac volume register + * 15-------------8-7--------------0 + * | R channel vol | L channel vol | + * ------------------------------- + * + * PCM volume with 0.5017 dB steps from 0 to -90 dB + * + * register values map to dB + * 0x3B and less = Reserved + * 0x3C = 0 dB + * 0x3D = -0.5 dB + * 0xF0 = -90 dB + * 0xFC and greater = Muted + * + * userspace value map to register value + * + * userspace value 0xc0 0 + * ------------------------------ + * register value 0x3c(0dB) 0xf0(-90dB)0xfc + */ +static int dac_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + int reg; + int l; + int r; + + l = ucontrol->value.integer.value[0]; + r = ucontrol->value.integer.value[1]; + + /* make sure userspace volume fall in (0, 0xfc-0x3c) */ + l = clamp(l, 0, 0xfc - 0x3c); + r = clamp(r, 0, 0xfc - 0x3c); + + /* invert it, get the value can be set to register */ + l = 0xfc - l; + r = 0xfc - r; + + /* shift to get the register value */ + reg = l << SGTL5000_DAC_VOL_LEFT_SHIFT | + r << SGTL5000_DAC_VOL_RIGHT_SHIFT; + + snd_soc_write(codec, SGTL5000_CHIP_DAC_VOL, reg); + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(capture_6db_attenuate, -600, 600, 0); + +/* tlv for mic gain, 0db 20db 30db 40db */ +static const unsigned int mic_gain_tlv[] = { + TLV_DB_RANGE_HEAD(4), + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 3, TLV_DB_SCALE_ITEM(2000, 1000, 0), +}; + +/* tlv for hp volume, -51.5db to 12.0db, step .5db */ +static const DECLARE_TLV_DB_SCALE(headphone_volume, -5150, 50, 0); + +static const struct snd_kcontrol_new sgtl5000_snd_controls[] = { + /* SOC_DOUBLE_S8_TLV with invert */ + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = dac_info_volsw, + .get = dac_get_volsw, + .put = dac_put_volsw, + }, + + SOC_DOUBLE("Capture Volume", SGTL5000_CHIP_ANA_ADC_CTRL, 0, 4, 0xf, 0), + SOC_SINGLE_TLV("Capture Attenuate Switch (-6dB)", + SGTL5000_CHIP_ANA_ADC_CTRL, + 8, 2, 0, capture_6db_attenuate), + SOC_SINGLE("Capture ZC Switch", SGTL5000_CHIP_ANA_CTRL, 1, 1, 0), + + SOC_DOUBLE_TLV("Headphone Playback Volume", + SGTL5000_CHIP_ANA_HP_CTRL, + 0, 8, + 0x7f, 1, + headphone_volume), + SOC_SINGLE("Headphone Playback ZC Switch", SGTL5000_CHIP_ANA_CTRL, + 5, 1, 0), + + SOC_SINGLE_TLV("Mic Volume", SGTL5000_CHIP_MIC_CTRL, + 0, 4, 0, mic_gain_tlv), +}; + +/* mute the codec used by alsa core */ +static int sgtl5000_digital_mute(struct snd_soc_dai *codec_dai, int mute) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 adcdac_ctrl = SGTL5000_DAC_MUTE_LEFT | SGTL5000_DAC_MUTE_RIGHT; + + snd_soc_update_bits(codec, SGTL5000_CHIP_ADCDAC_CTRL, + adcdac_ctrl, mute ? adcdac_ctrl : 0); + + return 0; +} + +/* set codec format */ +static int sgtl5000_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + u16 i2sctl = 0; + + sgtl5000->master = 0; + /* + * i2s clock and frame master setting. + * ONLY support: + * - clock and frame slave, + * - clock and frame master + */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBM_CFM: + i2sctl |= SGTL5000_I2S_MASTER; + sgtl5000->master = 1; + break; + default: + return -EINVAL; + } + + /* setting i2s data format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + i2sctl |= SGTL5000_I2S_MODE_PCM; + break; + case SND_SOC_DAIFMT_DSP_B: + i2sctl |= SGTL5000_I2S_MODE_PCM; + i2sctl |= SGTL5000_I2S_LRALIGN; + break; + case SND_SOC_DAIFMT_I2S: + i2sctl |= SGTL5000_I2S_MODE_I2S_LJ; + break; + case SND_SOC_DAIFMT_RIGHT_J: + i2sctl |= SGTL5000_I2S_MODE_RJ; + i2sctl |= SGTL5000_I2S_LRPOL; + break; + case SND_SOC_DAIFMT_LEFT_J: + i2sctl |= SGTL5000_I2S_MODE_I2S_LJ; + i2sctl |= SGTL5000_I2S_LRALIGN; + break; + default: + return -EINVAL; + } + + sgtl5000->fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + + /* Clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + i2sctl |= SGTL5000_I2S_SCLK_INV; + break; + default: + return -EINVAL; + } + + snd_soc_write(codec, SGTL5000_CHIP_I2S_CTRL, i2sctl); + + return 0; +} + +/* set codec sysclk */ +static int sgtl5000_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + + switch (clk_id) { + case SGTL5000_SYSCLK: + sgtl5000->sysclk = freq; + break; + default: + return -EINVAL; + } + + return 0; +} + +/* + * set clock according to i2s frame clock, + * sgtl5000 provide 2 clock sources. + * 1. sys_mclk. sample freq can only configure to + * 1/256, 1/384, 1/512 of sys_mclk. + * 2. pll. can derive any audio clocks. + * + * clock setting rules: + * 1. in slave mode, only sys_mclk can use. + * 2. as constraint by sys_mclk, sample freq should + * set to 32k, 44.1k and above. + * 3. using sys_mclk prefer to pll to save power. + */ +static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate) +{ + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + int clk_ctl = 0; + int sys_fs; /* sample freq */ + + /* + * sample freq should be divided by frame clock, + * if frame clock lower than 44.1khz, sample feq should set to + * 32khz or 44.1khz. + */ + switch (frame_rate) { + case 8000: + case 16000: + sys_fs = 32000; + break; + case 11025: + case 22050: + sys_fs = 44100; + break; + default: + sys_fs = frame_rate; + break; + } + + /* set divided factor of frame clock */ + switch (sys_fs / frame_rate) { + case 4: + clk_ctl |= SGTL5000_RATE_MODE_DIV_4 << SGTL5000_RATE_MODE_SHIFT; + break; + case 2: + clk_ctl |= SGTL5000_RATE_MODE_DIV_2 << SGTL5000_RATE_MODE_SHIFT; + break; + case 1: + clk_ctl |= SGTL5000_RATE_MODE_DIV_1 << SGTL5000_RATE_MODE_SHIFT; + break; + default: + return -EINVAL; + } + + /* set the sys_fs according to frame rate */ + switch (sys_fs) { + case 32000: + clk_ctl |= SGTL5000_SYS_FS_32k << SGTL5000_SYS_FS_SHIFT; + break; + case 44100: + clk_ctl |= SGTL5000_SYS_FS_44_1k << SGTL5000_SYS_FS_SHIFT; + break; + case 48000: + clk_ctl |= SGTL5000_SYS_FS_48k << SGTL5000_SYS_FS_SHIFT; + break; + case 96000: + clk_ctl |= SGTL5000_SYS_FS_96k << SGTL5000_SYS_FS_SHIFT; + break; + default: + dev_err(codec->dev, "frame rate %d not supported\n", + frame_rate); + return -EINVAL; + } + + /* + * calculate the divider of mclk/sample_freq, + * factor of freq =96k can only be 256, since mclk in range (12m,27m) + */ + switch (sgtl5000->sysclk / sys_fs) { + case 256: + clk_ctl |= SGTL5000_MCLK_FREQ_256FS << + SGTL5000_MCLK_FREQ_SHIFT; + break; + case 384: + clk_ctl |= SGTL5000_MCLK_FREQ_384FS << + SGTL5000_MCLK_FREQ_SHIFT; + break; + case 512: + clk_ctl |= SGTL5000_MCLK_FREQ_512FS << + SGTL5000_MCLK_FREQ_SHIFT; + break; + default: + /* if mclk not satisify the divider, use pll */ + if (sgtl5000->master) { + clk_ctl |= SGTL5000_MCLK_FREQ_PLL << + SGTL5000_MCLK_FREQ_SHIFT; + } else { + dev_err(codec->dev, + "PLL not supported in slave mode\n"); + return -EINVAL; + } + } + + /* if using pll, please check manual 6.4.2 for detail */ + if ((clk_ctl & SGTL5000_MCLK_FREQ_MASK) == SGTL5000_MCLK_FREQ_PLL) { + u64 out, t; + int div2; + int pll_ctl; + unsigned int in, int_div, frac_div; + + if (sgtl5000->sysclk > 17000000) { + div2 = 1; + in = sgtl5000->sysclk / 2; + } else { + div2 = 0; + in = sgtl5000->sysclk; + } + if (sys_fs == 44100) + out = 180633600; + else + out = 196608000; + t = do_div(out, in); + int_div = out; + t *= 2048; + do_div(t, in); + frac_div = t; + pll_ctl = int_div << SGTL5000_PLL_INT_DIV_SHIFT | + frac_div << SGTL5000_PLL_FRAC_DIV_SHIFT; + + snd_soc_write(codec, SGTL5000_CHIP_PLL_CTRL, pll_ctl); + if (div2) + snd_soc_update_bits(codec, + SGTL5000_CHIP_CLK_TOP_CTRL, + SGTL5000_INPUT_FREQ_DIV2, + SGTL5000_INPUT_FREQ_DIV2); + else + snd_soc_update_bits(codec, + SGTL5000_CHIP_CLK_TOP_CTRL, + SGTL5000_INPUT_FREQ_DIV2, + 0); + + /* power up pll */ + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP, + SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP); + } else { + /* power down pll */ + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP, + 0); + } + + /* if using pll, clk_ctrl must be set after pll power up */ + snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl); + + return 0; +} + +/* + * Set PCM DAI bit size and sample rate. + * input: params_rate, params_fmt + */ +static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + int channels = params_channels(params); + int i2s_ctl = 0; + int stereo; + int ret; + + /* sysclk should already set */ + if (!sgtl5000->sysclk) { + dev_err(codec->dev, "%s: set sysclk first!\n", __func__); + return -EFAULT; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + stereo = SGTL5000_DAC_STEREO; + else + stereo = SGTL5000_ADC_STEREO; + + /* set mono to save power */ + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, stereo, + channels == 1 ? 0 : stereo); + + /* set codec clock base on lrclk */ + ret = sgtl5000_set_clock(codec, params_rate(params)); + if (ret) + return ret; + + /* set i2s data format */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + if (sgtl5000->fmt == SND_SOC_DAIFMT_RIGHT_J) + return -EINVAL; + i2s_ctl |= SGTL5000_I2S_DLEN_16 << SGTL5000_I2S_DLEN_SHIFT; + i2s_ctl |= SGTL5000_I2S_SCLKFREQ_32FS << + SGTL5000_I2S_SCLKFREQ_SHIFT; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + i2s_ctl |= SGTL5000_I2S_DLEN_20 << SGTL5000_I2S_DLEN_SHIFT; + i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS << + SGTL5000_I2S_SCLKFREQ_SHIFT; + break; + case SNDRV_PCM_FORMAT_S24_LE: + i2s_ctl |= SGTL5000_I2S_DLEN_24 << SGTL5000_I2S_DLEN_SHIFT; + i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS << + SGTL5000_I2S_SCLKFREQ_SHIFT; + break; + case SNDRV_PCM_FORMAT_S32_LE: + if (sgtl5000->fmt == SND_SOC_DAIFMT_RIGHT_J) + return -EINVAL; + i2s_ctl |= SGTL5000_I2S_DLEN_32 << SGTL5000_I2S_DLEN_SHIFT; + i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS << + SGTL5000_I2S_SCLKFREQ_SHIFT; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, SGTL5000_CHIP_I2S_CTRL, i2s_ctl, i2s_ctl); + + return 0; +} + +static int ldo_regulator_is_enabled(struct regulator_dev *dev) +{ + struct ldo_regulator *ldo = rdev_get_drvdata(dev); + + return ldo->enabled; +} + +static int ldo_regulator_enable(struct regulator_dev *dev) +{ + struct ldo_regulator *ldo = rdev_get_drvdata(dev); + struct snd_soc_codec *codec = (struct snd_soc_codec *)ldo->codec_data; + int reg; + + if (ldo_regulator_is_enabled(dev)) + return 0; + + /* set regulator value firstly */ + reg = (1600 - ldo->voltage / 1000) / 50; + reg = clamp(reg, 0x0, 0xf); + + /* amend the voltage value, unit: uV */ + ldo->voltage = (1600 - reg * 50) * 1000; + + /* set voltage to register */ + snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL, + (0x1 << 4) - 1, reg); + + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_LINEREG_D_POWERUP, + SGTL5000_LINEREG_D_POWERUP); + + /* when internal ldo enabled, simple digital power can be disabled */ + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_LINREG_SIMPLE_POWERUP, + 0); + + ldo->enabled = 1; + return 0; +} + +static int ldo_regulator_disable(struct regulator_dev *dev) +{ + struct ldo_regulator *ldo = rdev_get_drvdata(dev); + struct snd_soc_codec *codec = (struct snd_soc_codec *)ldo->codec_data; + + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_LINEREG_D_POWERUP, + 0); + + /* clear voltage info */ + snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL, + (0x1 << 4) - 1, 0); + + ldo->enabled = 0; + + return 0; +} + +static int ldo_regulator_get_voltage(struct regulator_dev *dev) +{ + struct ldo_regulator *ldo = rdev_get_drvdata(dev); + + return ldo->voltage; +} + +static struct regulator_ops ldo_regulator_ops = { + .is_enabled = ldo_regulator_is_enabled, + .enable = ldo_regulator_enable, + .disable = ldo_regulator_disable, + .get_voltage = ldo_regulator_get_voltage, +}; + +static int ldo_regulator_register(struct snd_soc_codec *codec, + struct regulator_init_data *init_data, + int voltage) +{ + struct ldo_regulator *ldo; + + ldo = kzalloc(sizeof(struct ldo_regulator), GFP_KERNEL); + + if (!ldo) { + dev_err(codec->dev, "failed to allocate ldo_regulator\n"); + return -ENOMEM; + } + + ldo->desc.name = kstrdup(dev_name(codec->dev), GFP_KERNEL); + if (!ldo->desc.name) { + kfree(ldo); + dev_err(codec->dev, "failed to allocate decs name memory\n"); + return -ENOMEM; + } + + ldo->desc.type = REGULATOR_VOLTAGE; + ldo->desc.owner = THIS_MODULE; + ldo->desc.ops = &ldo_regulator_ops; + ldo->desc.n_voltages = 1; + + ldo->codec_data = codec; + ldo->voltage = voltage; + + ldo->dev = regulator_register(&ldo->desc, codec->dev, + init_data, ldo); + if (IS_ERR(ldo->dev)) { + int ret = PTR_ERR(ldo->dev); + + dev_err(codec->dev, "failed to register regulator\n"); + kfree(ldo->desc.name); + kfree(ldo); + + return ret; + } + + return 0; +} + +static int ldo_regulator_remove(struct snd_soc_codec *codec) +{ + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + struct ldo_regulator *ldo = sgtl5000->ldo; + + if (!ldo) + return 0; + + regulator_unregister(ldo->dev); + kfree(ldo->desc.name); + kfree(ldo); + + return 0; +} + +/* + * set dac bias + * common state changes: + * startup: + * off --> standby --> prepare --> on + * standby --> prepare --> on + * + * stop: + * on --> prepare --> standby + */ +static int sgtl5000_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + int ret; + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + ret = regulator_bulk_enable( + ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + if (ret) + return ret; + udelay(10); + } + + break; + case SND_SOC_BIAS_OFF: + regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + break; + } + + codec->dapm.bias_level = level; + return 0; +} + +#define SGTL5000_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_ops sgtl5000_ops = { + .hw_params = sgtl5000_pcm_hw_params, + .digital_mute = sgtl5000_digital_mute, + .set_fmt = sgtl5000_set_dai_fmt, + .set_sysclk = sgtl5000_set_dai_sysclk, +}; + +static struct snd_soc_dai_driver sgtl5000_dai = { + .name = "sgtl5000", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + /* + * only support 8~48K + 96K, + * TODO modify hw_param to support more + */ + .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000, + .formats = SGTL5000_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000, + .formats = SGTL5000_FORMATS, + }, + .ops = &sgtl5000_ops, + .symmetric_rates = 1, +}; + +static int sgtl5000_volatile_register(struct snd_soc_codec *codec, + unsigned int reg) +{ + switch (reg) { + case SGTL5000_CHIP_ID: + case SGTL5000_CHIP_ADCDAC_CTRL: + case SGTL5000_CHIP_ANA_STATUS: + return 1; + } + + return 0; +} + +#ifdef CONFIG_SUSPEND +static int sgtl5000_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ + sgtl5000_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +/* + * restore all sgtl5000 registers, + * since a big hole between dap and regular registers, + * we will restore them respectively. + */ +static int sgtl5000_restore_regs(struct snd_soc_codec *codec) +{ + u16 *cache = codec->reg_cache; + int i; + int regular_regs = SGTL5000_CHIP_SHORT_CTRL >> 1; + + /* restore regular registers */ + for (i = 0; i < regular_regs; i++) { + int reg = i << 1; + + /* this regs depends on the others */ + if (reg == SGTL5000_CHIP_ANA_POWER || + reg == SGTL5000_CHIP_CLK_CTRL || + reg == SGTL5000_CHIP_LINREG_CTRL || + reg == SGTL5000_CHIP_LINE_OUT_CTRL || + reg == SGTL5000_CHIP_CLK_CTRL) + continue; + + snd_soc_write(codec, reg, cache[i]); + } + + /* restore dap registers */ + for (i = SGTL5000_DAP_REG_OFFSET >> 1; + i < SGTL5000_MAX_REG_OFFSET >> 1; i++) { + int reg = i << 1; + + snd_soc_write(codec, reg, cache[i]); + } + + /* + * restore power and other regs according + * to set_power() and set_clock() + */ + snd_soc_write(codec, SGTL5000_CHIP_LINREG_CTRL, + cache[SGTL5000_CHIP_LINREG_CTRL >> 1]); + + snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER, + cache[SGTL5000_CHIP_ANA_POWER >> 1]); + + snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, + cache[SGTL5000_CHIP_CLK_CTRL >> 1]); + + snd_soc_write(codec, SGTL5000_CHIP_REF_CTRL, + cache[SGTL5000_CHIP_REF_CTRL >> 1]); + + snd_soc_write(codec, SGTL5000_CHIP_LINE_OUT_CTRL, + cache[SGTL5000_CHIP_LINE_OUT_CTRL >> 1]); + return 0; +} + +static int sgtl5000_resume(struct snd_soc_codec *codec) +{ + /* Bring the codec back up to standby to enable regulators */ + sgtl5000_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + /* Restore registers by cached in memory */ + sgtl5000_restore_regs(codec); + return 0; +} +#else +#define sgtl5000_suspend NULL +#define sgtl5000_resume NULL +#endif /* CONFIG_SUSPEND */ + +/* + * sgtl5000 has 3 internal power supplies: + * 1. VAG, normally set to vdda/2 + * 2. chargepump, set to different value + * according to voltage of vdda and vddio + * 3. line out VAG, normally set to vddio/2 + * + * and should be set according to: + * 1. vddd provided by external or not + * 2. vdda and vddio voltage value. > 3.1v or not + * 3. chip revision >=0x11 or not. If >=0x11, not use external vddd. + */ +static int sgtl5000_set_power_regs(struct snd_soc_codec *codec) +{ + int vddd; + int vdda; + int vddio; + u16 ana_pwr; + u16 lreg_ctrl; + int vag; + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + + vdda = regulator_get_voltage(sgtl5000->supplies[VDDA].consumer); + vddio = regulator_get_voltage(sgtl5000->supplies[VDDIO].consumer); + vddd = regulator_get_voltage(sgtl5000->supplies[VDDD].consumer); + + vdda = vdda / 1000; + vddio = vddio / 1000; + vddd = vddd / 1000; + + if (vdda <= 0 || vddio <= 0 || vddd < 0) { + dev_err(codec->dev, "regulator voltage not set correctly\n"); + + return -EINVAL; + } + + /* according to datasheet, maximum voltage of supplies */ + if (vdda > 3600 || vddio > 3600 || vddd > 1980) { + dev_err(codec->dev, + "exceed max voltage vdda %dmv vddio %dma vddd %dma\n", + vdda, vddio, vddd); + + return -EINVAL; + } + + /* reset value */ + ana_pwr = snd_soc_read(codec, SGTL5000_CHIP_ANA_POWER); + ana_pwr |= SGTL5000_DAC_STEREO | + SGTL5000_ADC_STEREO | + SGTL5000_REFTOP_POWERUP; + lreg_ctrl = snd_soc_read(codec, SGTL5000_CHIP_LINREG_CTRL); + + if (vddio < 3100 && vdda < 3100) { + /* enable internal oscillator used for charge pump */ + snd_soc_update_bits(codec, SGTL5000_CHIP_CLK_TOP_CTRL, + SGTL5000_INT_OSC_EN, + SGTL5000_INT_OSC_EN); + /* Enable VDDC charge pump */ + ana_pwr |= SGTL5000_VDDC_CHRGPMP_POWERUP; + } else if (vddio >= 3100 && vdda >= 3100) { + /* + * if vddio and vddd > 3.1v, + * charge pump should be clean before set ana_pwr + */ + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_VDDC_CHRGPMP_POWERUP, 0); + + /* VDDC use VDDIO rail */ + lreg_ctrl |= SGTL5000_VDDC_ASSN_OVRD; + lreg_ctrl |= SGTL5000_VDDC_MAN_ASSN_VDDIO << + SGTL5000_VDDC_MAN_ASSN_SHIFT; + } + + snd_soc_write(codec, SGTL5000_CHIP_LINREG_CTRL, lreg_ctrl); + + snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER, ana_pwr); + + /* set voltage to register */ + snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL, + (0x1 << 4) - 1, 0x8); + + /* + * if vddd linear reg has been enabled, + * simple digital supply should be clear to get + * proper VDDD voltage. + */ + if (ana_pwr & SGTL5000_LINEREG_D_POWERUP) + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_LINREG_SIMPLE_POWERUP, + 0); + else + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_LINREG_SIMPLE_POWERUP | + SGTL5000_STARTUP_POWERUP, + 0); + + /* + * set ADC/DAC VAG to vdda / 2, + * should stay in range (0.8v, 1.575v) + */ + vag = vdda / 2; + if (vag <= SGTL5000_ANA_GND_BASE) + vag = 0; + else if (vag >= SGTL5000_ANA_GND_BASE + SGTL5000_ANA_GND_STP * + (SGTL5000_ANA_GND_MASK >> SGTL5000_ANA_GND_SHIFT)) + vag = SGTL5000_ANA_GND_MASK >> SGTL5000_ANA_GND_SHIFT; + else + vag = (vag - SGTL5000_ANA_GND_BASE) / SGTL5000_ANA_GND_STP; + + snd_soc_update_bits(codec, SGTL5000_CHIP_REF_CTRL, + vag << SGTL5000_ANA_GND_SHIFT, + vag << SGTL5000_ANA_GND_SHIFT); + + /* set line out VAG to vddio / 2, in range (0.8v, 1.675v) */ + vag = vddio / 2; + if (vag <= SGTL5000_LINE_OUT_GND_BASE) + vag = 0; + else if (vag >= SGTL5000_LINE_OUT_GND_BASE + + SGTL5000_LINE_OUT_GND_STP * SGTL5000_LINE_OUT_GND_MAX) + vag = SGTL5000_LINE_OUT_GND_MAX; + else + vag = (vag - SGTL5000_LINE_OUT_GND_BASE) / + SGTL5000_LINE_OUT_GND_STP; + + snd_soc_update_bits(codec, SGTL5000_CHIP_LINE_OUT_CTRL, + vag << SGTL5000_LINE_OUT_GND_SHIFT | + SGTL5000_LINE_OUT_CURRENT_360u << + SGTL5000_LINE_OUT_CURRENT_SHIFT, + vag << SGTL5000_LINE_OUT_GND_SHIFT | + SGTL5000_LINE_OUT_CURRENT_360u << + SGTL5000_LINE_OUT_CURRENT_SHIFT); + + return 0; +} + +static int sgtl5000_enable_regulators(struct snd_soc_codec *codec) +{ + u16 reg; + int ret; + int rev; + int i; + int external_vddd = 0; + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + + for (i = 0; i < ARRAY_SIZE(sgtl5000->supplies); i++) + sgtl5000->supplies[i].supply = supply_names[i]; + + ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + if (!ret) + external_vddd = 1; + else { + /* set internal ldo to 1.2v */ + int voltage = LDO_VOLTAGE; + + ret = ldo_regulator_register(codec, &ldo_init_data, voltage); + if (ret) { + dev_err(codec->dev, + "Failed to register vddd internal supplies: %d\n", + ret); + return ret; + } + + sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME; + + ret = regulator_bulk_get(codec->dev, + ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + + if (ret) { + ldo_regulator_remove(codec); + dev_err(codec->dev, + "Failed to request supplies: %d\n", ret); + + return ret; + } + } + + ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + if (ret) + goto err_regulator_free; + + /* wait for all power rails bring up */ + udelay(10); + + /* read chip information */ + reg = snd_soc_read(codec, SGTL5000_CHIP_ID); + if (((reg & SGTL5000_PARTID_MASK) >> SGTL5000_PARTID_SHIFT) != + SGTL5000_PARTID_PART_ID) { + dev_err(codec->dev, + "Device with ID register %x is not a sgtl5000\n", reg); + ret = -ENODEV; + goto err_regulator_disable; + } + + rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT; + dev_info(codec->dev, "sgtl5000 revision %d\n", rev); + + /* + * workaround for revision 0x11 and later, + * roll back to use internal LDO + */ + if (external_vddd && rev >= 0x11) { + int voltage = LDO_VOLTAGE; + /* disable all regulator first */ + regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + /* free VDDD regulator */ + regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + + ret = ldo_regulator_register(codec, &ldo_init_data, voltage); + if (ret) + return ret; + + sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME; + + ret = regulator_bulk_get(codec->dev, + ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + if (ret) { + ldo_regulator_remove(codec); + dev_err(codec->dev, + "Failed to request supplies: %d\n", ret); + + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + if (ret) + goto err_regulator_free; + + /* wait for all power rails bring up */ + udelay(10); + } + + return 0; + +err_regulator_disable: + regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); +err_regulator_free: + regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + if (external_vddd) + ldo_regulator_remove(codec); + return ret; + +} + +static int sgtl5000_probe(struct snd_soc_codec *codec) +{ + int ret; + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + + /* setup i2c data ops */ + ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_I2C); + if (ret < 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + + ret = sgtl5000_enable_regulators(codec); + if (ret) + return ret; + + /* power up sgtl5000 */ + ret = sgtl5000_set_power_regs(codec); + if (ret) + goto err; + + /* enable small pop, introduce 400ms delay in turning off */ + snd_soc_update_bits(codec, SGTL5000_CHIP_REF_CTRL, + SGTL5000_SMALL_POP, + SGTL5000_SMALL_POP); + + /* disable short cut detector */ + snd_soc_write(codec, SGTL5000_CHIP_SHORT_CTRL, 0); + + /* + * set i2s as default input of sound switch + * TODO: add sound switch to control and dapm widge. + */ + snd_soc_write(codec, SGTL5000_CHIP_SSS_CTRL, + SGTL5000_DAC_SEL_I2S_IN << SGTL5000_DAC_SEL_SHIFT); + snd_soc_write(codec, SGTL5000_CHIP_DIG_POWER, + SGTL5000_ADC_EN | SGTL5000_DAC_EN); + + /* enable dac volume ramp by default */ + snd_soc_write(codec, SGTL5000_CHIP_ADCDAC_CTRL, + SGTL5000_DAC_VOL_RAMP_EN | + SGTL5000_DAC_MUTE_RIGHT | + SGTL5000_DAC_MUTE_LEFT); + + snd_soc_write(codec, SGTL5000_CHIP_PAD_STRENGTH, 0x015f); + + snd_soc_write(codec, SGTL5000_CHIP_ANA_CTRL, + SGTL5000_HP_ZCD_EN | + SGTL5000_ADC_ZCD_EN); + + snd_soc_write(codec, SGTL5000_CHIP_MIC_CTRL, 0); + + /* + * disable DAP + * TODO: + * Enable DAP in kcontrol and dapm. + */ + snd_soc_write(codec, SGTL5000_DAP_CTRL, 0); + + /* leading to standby state */ + ret = sgtl5000_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + if (ret) + goto err; + + snd_soc_add_controls(codec, sgtl5000_snd_controls, + ARRAY_SIZE(sgtl5000_snd_controls)); + + snd_soc_dapm_new_controls(&codec->dapm, sgtl5000_dapm_widgets, + ARRAY_SIZE(sgtl5000_dapm_widgets)); + + snd_soc_dapm_add_routes(&codec->dapm, audio_map, + ARRAY_SIZE(audio_map)); + + snd_soc_dapm_new_widgets(&codec->dapm); + + return 0; + +err: + regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + ldo_regulator_remove(codec); + + return ret; +} + +static int sgtl5000_remove(struct snd_soc_codec *codec) +{ + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + + sgtl5000_set_bias_level(codec, SND_SOC_BIAS_OFF); + + regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + ldo_regulator_remove(codec); + + return 0; +} + +static struct snd_soc_codec_driver sgtl5000_driver = { + .probe = sgtl5000_probe, + .remove = sgtl5000_remove, + .suspend = sgtl5000_suspend, + .resume = sgtl5000_resume, + .set_bias_level = sgtl5000_set_bias_level, + .reg_cache_size = ARRAY_SIZE(sgtl5000_regs), + .reg_word_size = sizeof(u16), + .reg_cache_step = 2, + .reg_cache_default = sgtl5000_regs, + .volatile_register = sgtl5000_volatile_register, +}; + +static __devinit int sgtl5000_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct sgtl5000_priv *sgtl5000; + int ret; + + sgtl5000 = kzalloc(sizeof(struct sgtl5000_priv), GFP_KERNEL); + if (!sgtl5000) + return -ENOMEM; + + /* + * copy DAP default values to default value array. + * sgtl5000 register space has a big hole, merge it + * at init phase makes life easy. + * FIXME: should we drop 'const' of sgtl5000_regs? + */ + memcpy((void *)(&sgtl5000_regs[0] + (SGTL5000_DAP_REG_OFFSET >> 1)), + sgtl5000_dap_regs, + SGTL5000_MAX_REG_OFFSET - SGTL5000_DAP_REG_OFFSET); + + i2c_set_clientdata(client, sgtl5000); + + ret = snd_soc_register_codec(&client->dev, + &sgtl5000_driver, &sgtl5000_dai, 1); + if (ret) { + dev_err(&client->dev, "Failed to register codec: %d\n", ret); + kfree(sgtl5000); + return ret; + } + + return 0; +} + +static __devexit int sgtl5000_i2c_remove(struct i2c_client *client) +{ + struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client); + + snd_soc_unregister_codec(&client->dev); + + kfree(sgtl5000); + return 0; +} + +static const struct i2c_device_id sgtl5000_id[] = { + {"sgtl5000", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, sgtl5000_id); + +static struct i2c_driver sgtl5000_i2c_driver = { + .driver = { + .name = "sgtl5000", + .owner = THIS_MODULE, + }, + .probe = sgtl5000_i2c_probe, + .remove = __devexit_p(sgtl5000_i2c_remove), + .id_table = sgtl5000_id, +}; + +static int __init sgtl5000_modinit(void) +{ + return i2c_add_driver(&sgtl5000_i2c_driver); +} +module_init(sgtl5000_modinit); + +static void __exit sgtl5000_exit(void) +{ + i2c_del_driver(&sgtl5000_i2c_driver); +} +module_exit(sgtl5000_exit); + +MODULE_DESCRIPTION("Freescale SGTL5000 ALSA SoC Codec Driver"); +MODULE_AUTHOR("Zeng Zhaoming <zhaoming.zeng@freescale.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sgtl5000.h b/sound/soc/codecs/sgtl5000.h new file mode 100644 index 000000000000..eec3ab368f39 --- /dev/null +++ b/sound/soc/codecs/sgtl5000.h @@ -0,0 +1,400 @@ +/* + * sgtl5000.h - SGTL5000 audio codec interface + * + * Copyright 2010-2011 Freescale Semiconductor, Inc. + * + * 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 _SGTL5000_H +#define _SGTL5000_H + +/* + * Register values. + */ +#define SGTL5000_CHIP_ID 0x0000 +#define SGTL5000_CHIP_DIG_POWER 0x0002 +#define SGTL5000_CHIP_CLK_CTRL 0x0004 +#define SGTL5000_CHIP_I2S_CTRL 0x0006 +#define SGTL5000_CHIP_SSS_CTRL 0x000a +#define SGTL5000_CHIP_ADCDAC_CTRL 0x000e +#define SGTL5000_CHIP_DAC_VOL 0x0010 +#define SGTL5000_CHIP_PAD_STRENGTH 0x0014 +#define SGTL5000_CHIP_ANA_ADC_CTRL 0x0020 +#define SGTL5000_CHIP_ANA_HP_CTRL 0x0022 +#define SGTL5000_CHIP_ANA_CTRL 0x0024 +#define SGTL5000_CHIP_LINREG_CTRL 0x0026 +#define SGTL5000_CHIP_REF_CTRL 0x0028 +#define SGTL5000_CHIP_MIC_CTRL 0x002a +#define SGTL5000_CHIP_LINE_OUT_CTRL 0x002c +#define SGTL5000_CHIP_LINE_OUT_VOL 0x002e +#define SGTL5000_CHIP_ANA_POWER 0x0030 +#define SGTL5000_CHIP_PLL_CTRL 0x0032 +#define SGTL5000_CHIP_CLK_TOP_CTRL 0x0034 +#define SGTL5000_CHIP_ANA_STATUS 0x0036 +#define SGTL5000_CHIP_SHORT_CTRL 0x003c +#define SGTL5000_CHIP_ANA_TEST2 0x003a +#define SGTL5000_DAP_CTRL 0x0100 +#define SGTL5000_DAP_PEQ 0x0102 +#define SGTL5000_DAP_BASS_ENHANCE 0x0104 +#define SGTL5000_DAP_BASS_ENHANCE_CTRL 0x0106 +#define SGTL5000_DAP_AUDIO_EQ 0x0108 +#define SGTL5000_DAP_SURROUND 0x010a +#define SGTL5000_DAP_FLT_COEF_ACCESS 0x010c +#define SGTL5000_DAP_COEF_WR_B0_MSB 0x010e +#define SGTL5000_DAP_COEF_WR_B0_LSB 0x0110 +#define SGTL5000_DAP_EQ_BASS_BAND0 0x0116 +#define SGTL5000_DAP_EQ_BASS_BAND1 0x0118 +#define SGTL5000_DAP_EQ_BASS_BAND2 0x011a +#define SGTL5000_DAP_EQ_BASS_BAND3 0x011c +#define SGTL5000_DAP_EQ_BASS_BAND4 0x011e +#define SGTL5000_DAP_MAIN_CHAN 0x0120 +#define SGTL5000_DAP_MIX_CHAN 0x0122 +#define SGTL5000_DAP_AVC_CTRL 0x0124 +#define SGTL5000_DAP_AVC_THRESHOLD 0x0126 +#define SGTL5000_DAP_AVC_ATTACK 0x0128 +#define SGTL5000_DAP_AVC_DECAY 0x012a +#define SGTL5000_DAP_COEF_WR_B1_MSB 0x012c +#define SGTL5000_DAP_COEF_WR_B1_LSB 0x012e +#define SGTL5000_DAP_COEF_WR_B2_MSB 0x0130 +#define SGTL5000_DAP_COEF_WR_B2_LSB 0x0132 +#define SGTL5000_DAP_COEF_WR_A1_MSB 0x0134 +#define SGTL5000_DAP_COEF_WR_A1_LSB 0x0136 +#define SGTL5000_DAP_COEF_WR_A2_MSB 0x0138 +#define SGTL5000_DAP_COEF_WR_A2_LSB 0x013a + +/* + * Field Definitions. + */ + +/* + * SGTL5000_CHIP_ID + */ +#define SGTL5000_PARTID_MASK 0xff00 +#define SGTL5000_PARTID_SHIFT 8 +#define SGTL5000_PARTID_WIDTH 8 +#define SGTL5000_PARTID_PART_ID 0xa0 +#define SGTL5000_REVID_MASK 0x00ff +#define SGTL5000_REVID_SHIFT 0 +#define SGTL5000_REVID_WIDTH 8 + +/* + * SGTL5000_CHIP_DIG_POWER + */ +#define SGTL5000_ADC_EN 0x0040 +#define SGTL5000_DAC_EN 0x0020 +#define SGTL5000_DAP_POWERUP 0x0010 +#define SGTL5000_I2S_OUT_POWERUP 0x0002 +#define SGTL5000_I2S_IN_POWERUP 0x0001 + +/* + * SGTL5000_CHIP_CLK_CTRL + */ +#define SGTL5000_RATE_MODE_MASK 0x0030 +#define SGTL5000_RATE_MODE_SHIFT 4 +#define SGTL5000_RATE_MODE_WIDTH 2 +#define SGTL5000_RATE_MODE_DIV_1 0 +#define SGTL5000_RATE_MODE_DIV_2 1 +#define SGTL5000_RATE_MODE_DIV_4 2 +#define SGTL5000_RATE_MODE_DIV_6 3 +#define SGTL5000_SYS_FS_MASK 0x000c +#define SGTL5000_SYS_FS_SHIFT 2 +#define SGTL5000_SYS_FS_WIDTH 2 +#define SGTL5000_SYS_FS_32k 0x0 +#define SGTL5000_SYS_FS_44_1k 0x1 +#define SGTL5000_SYS_FS_48k 0x2 +#define SGTL5000_SYS_FS_96k 0x3 +#define SGTL5000_MCLK_FREQ_MASK 0x0003 +#define SGTL5000_MCLK_FREQ_SHIFT 0 +#define SGTL5000_MCLK_FREQ_WIDTH 2 +#define SGTL5000_MCLK_FREQ_256FS 0x0 +#define SGTL5000_MCLK_FREQ_384FS 0x1 +#define SGTL5000_MCLK_FREQ_512FS 0x2 +#define SGTL5000_MCLK_FREQ_PLL 0x3 + +/* + * SGTL5000_CHIP_I2S_CTRL + */ +#define SGTL5000_I2S_SCLKFREQ_MASK 0x0100 +#define SGTL5000_I2S_SCLKFREQ_SHIFT 8 +#define SGTL5000_I2S_SCLKFREQ_WIDTH 1 +#define SGTL5000_I2S_SCLKFREQ_64FS 0x0 +#define SGTL5000_I2S_SCLKFREQ_32FS 0x1 /* Not for RJ mode */ +#define SGTL5000_I2S_MASTER 0x0080 +#define SGTL5000_I2S_SCLK_INV 0x0040 +#define SGTL5000_I2S_DLEN_MASK 0x0030 +#define SGTL5000_I2S_DLEN_SHIFT 4 +#define SGTL5000_I2S_DLEN_WIDTH 2 +#define SGTL5000_I2S_DLEN_32 0x0 +#define SGTL5000_I2S_DLEN_24 0x1 +#define SGTL5000_I2S_DLEN_20 0x2 +#define SGTL5000_I2S_DLEN_16 0x3 +#define SGTL5000_I2S_MODE_MASK 0x000c +#define SGTL5000_I2S_MODE_SHIFT 2 +#define SGTL5000_I2S_MODE_WIDTH 2 +#define SGTL5000_I2S_MODE_I2S_LJ 0x0 +#define SGTL5000_I2S_MODE_RJ 0x1 +#define SGTL5000_I2S_MODE_PCM 0x2 +#define SGTL5000_I2S_LRALIGN 0x0002 +#define SGTL5000_I2S_LRPOL 0x0001 /* set for which mode */ + +/* + * SGTL5000_CHIP_SSS_CTRL + */ +#define SGTL5000_DAP_MIX_LRSWAP 0x4000 +#define SGTL5000_DAP_LRSWAP 0x2000 +#define SGTL5000_DAC_LRSWAP 0x1000 +#define SGTL5000_I2S_OUT_LRSWAP 0x0400 +#define SGTL5000_DAP_MIX_SEL_MASK 0x0300 +#define SGTL5000_DAP_MIX_SEL_SHIFT 8 +#define SGTL5000_DAP_MIX_SEL_WIDTH 2 +#define SGTL5000_DAP_MIX_SEL_ADC 0x0 +#define SGTL5000_DAP_MIX_SEL_I2S_IN 0x1 +#define SGTL5000_DAP_SEL_MASK 0x00c0 +#define SGTL5000_DAP_SEL_SHIFT 6 +#define SGTL5000_DAP_SEL_WIDTH 2 +#define SGTL5000_DAP_SEL_ADC 0x0 +#define SGTL5000_DAP_SEL_I2S_IN 0x1 +#define SGTL5000_DAC_SEL_MASK 0x0030 +#define SGTL5000_DAC_SEL_SHIFT 4 +#define SGTL5000_DAC_SEL_WIDTH 2 +#define SGTL5000_DAC_SEL_ADC 0x0 +#define SGTL5000_DAC_SEL_I2S_IN 0x1 +#define SGTL5000_DAC_SEL_DAP 0x3 +#define SGTL5000_I2S_OUT_SEL_MASK 0x0003 +#define SGTL5000_I2S_OUT_SEL_SHIFT 0 +#define SGTL5000_I2S_OUT_SEL_WIDTH 2 +#define SGTL5000_I2S_OUT_SEL_ADC 0x0 +#define SGTL5000_I2S_OUT_SEL_I2S_IN 0x1 +#define SGTL5000_I2S_OUT_SEL_DAP 0x3 + +/* + * SGTL5000_CHIP_ADCDAC_CTRL + */ +#define SGTL5000_VOL_BUSY_DAC_RIGHT 0x2000 +#define SGTL5000_VOL_BUSY_DAC_LEFT 0x1000 +#define SGTL5000_DAC_VOL_RAMP_EN 0x0200 +#define SGTL5000_DAC_VOL_RAMP_EXPO 0x0100 +#define SGTL5000_DAC_MUTE_RIGHT 0x0008 +#define SGTL5000_DAC_MUTE_LEFT 0x0004 +#define SGTL5000_ADC_HPF_FREEZE 0x0002 +#define SGTL5000_ADC_HPF_BYPASS 0x0001 + +/* + * SGTL5000_CHIP_DAC_VOL + */ +#define SGTL5000_DAC_VOL_RIGHT_MASK 0xff00 +#define SGTL5000_DAC_VOL_RIGHT_SHIFT 8 +#define SGTL5000_DAC_VOL_RIGHT_WIDTH 8 +#define SGTL5000_DAC_VOL_LEFT_MASK 0x00ff +#define SGTL5000_DAC_VOL_LEFT_SHIFT 0 +#define SGTL5000_DAC_VOL_LEFT_WIDTH 8 + +/* + * SGTL5000_CHIP_PAD_STRENGTH + */ +#define SGTL5000_PAD_I2S_LRCLK_MASK 0x0300 +#define SGTL5000_PAD_I2S_LRCLK_SHIFT 8 +#define SGTL5000_PAD_I2S_LRCLK_WIDTH 2 +#define SGTL5000_PAD_I2S_SCLK_MASK 0x00c0 +#define SGTL5000_PAD_I2S_SCLK_SHIFT 6 +#define SGTL5000_PAD_I2S_SCLK_WIDTH 2 +#define SGTL5000_PAD_I2S_DOUT_MASK 0x0030 +#define SGTL5000_PAD_I2S_DOUT_SHIFT 4 +#define SGTL5000_PAD_I2S_DOUT_WIDTH 2 +#define SGTL5000_PAD_I2C_SDA_MASK 0x000c +#define SGTL5000_PAD_I2C_SDA_SHIFT 2 +#define SGTL5000_PAD_I2C_SDA_WIDTH 2 +#define SGTL5000_PAD_I2C_SCL_MASK 0x0003 +#define SGTL5000_PAD_I2C_SCL_SHIFT 0 +#define SGTL5000_PAD_I2C_SCL_WIDTH 2 + +/* + * SGTL5000_CHIP_ANA_ADC_CTRL + */ +#define SGTL5000_ADC_VOL_M6DB 0x0100 +#define SGTL5000_ADC_VOL_RIGHT_MASK 0x00f0 +#define SGTL5000_ADC_VOL_RIGHT_SHIFT 4 +#define SGTL5000_ADC_VOL_RIGHT_WIDTH 4 +#define SGTL5000_ADC_VOL_LEFT_MASK 0x000f +#define SGTL5000_ADC_VOL_LEFT_SHIFT 0 +#define SGTL5000_ADC_VOL_LEFT_WIDTH 4 + +/* + * SGTL5000_CHIP_ANA_HP_CTRL + */ +#define SGTL5000_HP_VOL_RIGHT_MASK 0x7f00 +#define SGTL5000_HP_VOL_RIGHT_SHIFT 8 +#define SGTL5000_HP_VOL_RIGHT_WIDTH 7 +#define SGTL5000_HP_VOL_LEFT_MASK 0x007f +#define SGTL5000_HP_VOL_LEFT_SHIFT 0 +#define SGTL5000_HP_VOL_LEFT_WIDTH 7 + +/* + * SGTL5000_CHIP_ANA_CTRL + */ +#define SGTL5000_LINE_OUT_MUTE 0x0100 +#define SGTL5000_HP_SEL_MASK 0x0040 +#define SGTL5000_HP_SEL_SHIFT 6 +#define SGTL5000_HP_SEL_WIDTH 1 +#define SGTL5000_HP_SEL_DAC 0x0 +#define SGTL5000_HP_SEL_LINE_IN 0x1 +#define SGTL5000_HP_ZCD_EN 0x0020 +#define SGTL5000_HP_MUTE 0x0010 +#define SGTL5000_ADC_SEL_MASK 0x0004 +#define SGTL5000_ADC_SEL_SHIFT 2 +#define SGTL5000_ADC_SEL_WIDTH 1 +#define SGTL5000_ADC_SEL_MIC 0x0 +#define SGTL5000_ADC_SEL_LINE_IN 0x1 +#define SGTL5000_ADC_ZCD_EN 0x0002 +#define SGTL5000_ADC_MUTE 0x0001 + +/* + * SGTL5000_CHIP_LINREG_CTRL + */ +#define SGTL5000_VDDC_MAN_ASSN_MASK 0x0040 +#define SGTL5000_VDDC_MAN_ASSN_SHIFT 6 +#define SGTL5000_VDDC_MAN_ASSN_WIDTH 1 +#define SGTL5000_VDDC_MAN_ASSN_VDDA 0x0 +#define SGTL5000_VDDC_MAN_ASSN_VDDIO 0x1 +#define SGTL5000_VDDC_ASSN_OVRD 0x0020 +#define SGTL5000_LINREG_VDDD_MASK 0x000f +#define SGTL5000_LINREG_VDDD_SHIFT 0 +#define SGTL5000_LINREG_VDDD_WIDTH 4 + +/* + * SGTL5000_CHIP_REF_CTRL + */ +#define SGTL5000_ANA_GND_MASK 0x01f0 +#define SGTL5000_ANA_GND_SHIFT 4 +#define SGTL5000_ANA_GND_WIDTH 5 +#define SGTL5000_ANA_GND_BASE 800 /* mv */ +#define SGTL5000_ANA_GND_STP 25 /*mv */ +#define SGTL5000_BIAS_CTRL_MASK 0x000e +#define SGTL5000_BIAS_CTRL_SHIFT 1 +#define SGTL5000_BIAS_CTRL_WIDTH 3 +#define SGTL5000_SMALL_POP 0x0001 + +/* + * SGTL5000_CHIP_MIC_CTRL + */ +#define SGTL5000_BIAS_R_MASK 0x0200 +#define SGTL5000_BIAS_R_SHIFT 8 +#define SGTL5000_BIAS_R_WIDTH 2 +#define SGTL5000_BIAS_R_off 0x0 +#define SGTL5000_BIAS_R_2K 0x1 +#define SGTL5000_BIAS_R_4k 0x2 +#define SGTL5000_BIAS_R_8k 0x3 +#define SGTL5000_BIAS_VOLT_MASK 0x0070 +#define SGTL5000_BIAS_VOLT_SHIFT 4 +#define SGTL5000_BIAS_VOLT_WIDTH 3 +#define SGTL5000_MIC_GAIN_MASK 0x0003 +#define SGTL5000_MIC_GAIN_SHIFT 0 +#define SGTL5000_MIC_GAIN_WIDTH 2 + +/* + * SGTL5000_CHIP_LINE_OUT_CTRL + */ +#define SGTL5000_LINE_OUT_CURRENT_MASK 0x0f00 +#define SGTL5000_LINE_OUT_CURRENT_SHIFT 8 +#define SGTL5000_LINE_OUT_CURRENT_WIDTH 4 +#define SGTL5000_LINE_OUT_CURRENT_180u 0x0 +#define SGTL5000_LINE_OUT_CURRENT_270u 0x1 +#define SGTL5000_LINE_OUT_CURRENT_360u 0x3 +#define SGTL5000_LINE_OUT_CURRENT_450u 0x7 +#define SGTL5000_LINE_OUT_CURRENT_540u 0xf +#define SGTL5000_LINE_OUT_GND_MASK 0x003f +#define SGTL5000_LINE_OUT_GND_SHIFT 0 +#define SGTL5000_LINE_OUT_GND_WIDTH 6 +#define SGTL5000_LINE_OUT_GND_BASE 800 /* mv */ +#define SGTL5000_LINE_OUT_GND_STP 25 +#define SGTL5000_LINE_OUT_GND_MAX 0x23 + +/* + * SGTL5000_CHIP_LINE_OUT_VOL + */ +#define SGTL5000_LINE_OUT_VOL_RIGHT_MASK 0x1f00 +#define SGTL5000_LINE_OUT_VOL_RIGHT_SHIFT 8 +#define SGTL5000_LINE_OUT_VOL_RIGHT_WIDTH 5 +#define SGTL5000_LINE_OUT_VOL_LEFT_MASK 0x001f +#define SGTL5000_LINE_OUT_VOL_LEFT_SHIFT 0 +#define SGTL5000_LINE_OUT_VOL_LEFT_WIDTH 5 + +/* + * SGTL5000_CHIP_ANA_POWER + */ +#define SGTL5000_DAC_STEREO 0x4000 +#define SGTL5000_LINREG_SIMPLE_POWERUP 0x2000 +#define SGTL5000_STARTUP_POWERUP 0x1000 +#define SGTL5000_VDDC_CHRGPMP_POWERUP 0x0800 +#define SGTL5000_PLL_POWERUP 0x0400 +#define SGTL5000_LINEREG_D_POWERUP 0x0200 +#define SGTL5000_VCOAMP_POWERUP 0x0100 +#define SGTL5000_VAG_POWERUP 0x0080 +#define SGTL5000_ADC_STEREO 0x0040 +#define SGTL5000_REFTOP_POWERUP 0x0020 +#define SGTL5000_HP_POWERUP 0x0010 +#define SGTL5000_DAC_POWERUP 0x0008 +#define SGTL5000_CAPLESS_HP_POWERUP 0x0004 +#define SGTL5000_ADC_POWERUP 0x0002 +#define SGTL5000_LINE_OUT_POWERUP 0x0001 + +/* + * SGTL5000_CHIP_PLL_CTRL + */ +#define SGTL5000_PLL_INT_DIV_MASK 0xf800 +#define SGTL5000_PLL_INT_DIV_SHIFT 11 +#define SGTL5000_PLL_INT_DIV_WIDTH 5 +#define SGTL5000_PLL_FRAC_DIV_MASK 0x0700 +#define SGTL5000_PLL_FRAC_DIV_SHIFT 0 +#define SGTL5000_PLL_FRAC_DIV_WIDTH 11 + +/* + * SGTL5000_CHIP_CLK_TOP_CTRL + */ +#define SGTL5000_INT_OSC_EN 0x0800 +#define SGTL5000_INPUT_FREQ_DIV2 0x0008 + +/* + * SGTL5000_CHIP_ANA_STATUS + */ +#define SGTL5000_HP_LRSHORT 0x0200 +#define SGTL5000_CAPLESS_SHORT 0x0100 +#define SGTL5000_PLL_LOCKED 0x0010 + +/* + * SGTL5000_CHIP_SHORT_CTRL + */ +#define SGTL5000_LVLADJR_MASK 0x7000 +#define SGTL5000_LVLADJR_SHIFT 12 +#define SGTL5000_LVLADJR_WIDTH 3 +#define SGTL5000_LVLADJL_MASK 0x0700 +#define SGTL5000_LVLADJL_SHIFT 8 +#define SGTL5000_LVLADJL_WIDTH 3 +#define SGTL5000_LVLADJC_MASK 0x0070 +#define SGTL5000_LVLADJC_SHIFT 4 +#define SGTL5000_LVLADJC_WIDTH 3 +#define SGTL5000_LR_SHORT_MOD_MASK 0x000c +#define SGTL5000_LR_SHORT_MOD_SHIFT 2 +#define SGTL5000_LR_SHORT_MOD_WIDTH 2 +#define SGTL5000_CM_SHORT_MOD_MASK 0x0003 +#define SGTL5000_CM_SHORT_MOD_SHIFT 0 +#define SGTL5000_CM_SHORT_MOD_WIDTH 2 + +/* + *SGTL5000_CHIP_ANA_TEST2 + */ +#define SGTL5000_MONO_DAC 0x1000 + +/* + * SGTL5000_DAP_CTRL + */ +#define SGTL5000_DAP_MIX_EN 0x0010 +#define SGTL5000_DAP_EN 0x0001 + +#define SGTL5000_SYSCLK 0x00 +#define SGTL5000_LRCLK 0x01 + +#endif diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c new file mode 100644 index 000000000000..2a30eae1881c --- /dev/null +++ b/sound/soc/codecs/sn95031.c @@ -0,0 +1,949 @@ +/* + * sn95031.c - TI sn95031 Codec driver + * + * Copyright (C) 2010 Intel Corp + * Author: Vinod Koul <vinod.koul@intel.com> + * Author: Harsha Priya <priya.harsha@intel.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 of the License. + * + * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <asm/intel_scu_ipc.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 <sound/jack.h> +#include "sn95031.h" + +#define SN95031_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100) +#define SN95031_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) + +/* adc helper functions */ + +/* enables mic bias voltage */ +static void sn95031_enable_mic_bias(struct snd_soc_codec *codec) +{ + snd_soc_write(codec, SN95031_VAUD, BIT(2)|BIT(1)|BIT(0)); + snd_soc_update_bits(codec, SN95031_MICBIAS, BIT(2), BIT(2)); +} + +/* Enable/Disable the ADC depending on the argument */ +static void configure_adc(struct snd_soc_codec *sn95031_codec, int val) +{ + int value = snd_soc_read(sn95031_codec, SN95031_ADC1CNTL1); + + if (val) { + /* Enable and start the ADC */ + value |= (SN95031_ADC_ENBL | SN95031_ADC_START); + value &= (~SN95031_ADC_NO_LOOP); + } else { + /* Just stop the ADC */ + value &= (~SN95031_ADC_START); + } + snd_soc_write(sn95031_codec, SN95031_ADC1CNTL1, value); +} + +/* + * finds an empty channel for conversion + * If the ADC is not enabled then start using 0th channel + * itself. Otherwise find an empty channel by looking for a + * channel in which the stopbit is set to 1. returns the index + * of the first free channel if succeeds or an error code. + * + * Context: can sleep + * + */ +static int find_free_channel(struct snd_soc_codec *sn95031_codec) +{ + int ret = 0, i, value; + + /* check whether ADC is enabled */ + value = snd_soc_read(sn95031_codec, SN95031_ADC1CNTL1); + + if ((value & SN95031_ADC_ENBL) == 0) + return 0; + + /* ADC is already enabled; Looking for an empty channel */ + for (i = 0; i < SN95031_ADC_CHANLS_MAX; i++) { + value = snd_soc_read(sn95031_codec, + SN95031_ADC_CHNL_START_ADDR + i); + if (value & SN95031_STOPBIT_MASK) { + ret = i; + break; + } + } + return (ret > SN95031_ADC_LOOP_MAX) ? (-EINVAL) : ret; +} + +/* Initialize the ADC for reading micbias values. Can sleep. */ +static int sn95031_initialize_adc(struct snd_soc_codec *sn95031_codec) +{ + int base_addr, chnl_addr; + int value; + static int channel_index; + + /* Index of the first channel in which the stop bit is set */ + channel_index = find_free_channel(sn95031_codec); + if (channel_index < 0) { + pr_err("No free ADC channels"); + return channel_index; + } + + base_addr = SN95031_ADC_CHNL_START_ADDR + channel_index; + + if (!(channel_index == 0 || channel_index == SN95031_ADC_LOOP_MAX)) { + /* Reset stop bit for channels other than 0 and 12 */ + value = snd_soc_read(sn95031_codec, base_addr); + /* Set the stop bit to zero */ + snd_soc_write(sn95031_codec, base_addr, value & 0xEF); + /* Index of the first free channel */ + base_addr++; + channel_index++; + } + + /* Since this is the last channel, set the stop bit + to 1 by ORing the DIE_SENSOR_CODE with 0x10 */ + snd_soc_write(sn95031_codec, base_addr, + SN95031_AUDIO_DETECT_CODE | 0x10); + + chnl_addr = SN95031_ADC_DATA_START_ADDR + 2 * channel_index; + pr_debug("mid_initialize : %x", chnl_addr); + configure_adc(sn95031_codec, 1); + return chnl_addr; +} + + +/* reads the ADC registers and gets the mic bias value in mV. */ +static unsigned int sn95031_get_mic_bias(struct snd_soc_codec *codec) +{ + u16 adc_adr = sn95031_initialize_adc(codec); + u16 adc_val1, adc_val2; + unsigned int mic_bias; + + sn95031_enable_mic_bias(codec); + + /* Enable the sound card for conversion before reading */ + snd_soc_write(codec, SN95031_ADC1CNTL3, 0x05); + /* Re-toggle the RRDATARD bit */ + snd_soc_write(codec, SN95031_ADC1CNTL3, 0x04); + + /* Read the higher bits of data */ + msleep(1000); + adc_val1 = snd_soc_read(codec, adc_adr); + adc_adr++; + adc_val2 = snd_soc_read(codec, adc_adr); + + /* Adding lower two bits to the higher bits */ + mic_bias = (adc_val1 << 2) + (adc_val2 & 3); + mic_bias = (mic_bias * SN95031_ADC_ONE_LSB_MULTIPLIER) / 1000; + pr_debug("mic bias = %dmV\n", mic_bias); + return mic_bias; +} +EXPORT_SYMBOL_GPL(sn95031_get_mic_bias); +/*end - adc helper functions */ + +static inline unsigned int sn95031_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + u8 value = 0; + int ret; + + ret = intel_scu_ipc_ioread8(reg, &value); + if (ret) + pr_err("read of %x failed, err %d\n", reg, ret); + return value; + +} + +static inline int sn95031_write(struct snd_soc_codec *codec, + unsigned int reg, unsigned int value) +{ + int ret; + + ret = intel_scu_ipc_iowrite8(reg, value); + if (ret) + pr_err("write of %x failed, err %d\n", reg, ret); + return ret; +} + +static int sn95031_set_vaud_bias(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) { + pr_debug("vaud_bias powering up pll\n"); + /* power up the pll */ + snd_soc_write(codec, SN95031_AUDPLLCTRL, BIT(5)); + /* enable pcm 2 */ + snd_soc_update_bits(codec, SN95031_PCM2C2, + BIT(0), BIT(0)); + } + break; + + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + pr_debug("vaud_bias power up rail\n"); + /* power up the rail */ + snd_soc_write(codec, SN95031_VAUD, + BIT(2)|BIT(1)|BIT(0)); + msleep(1); + } else if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) { + /* turn off pcm */ + pr_debug("vaud_bias power dn pcm\n"); + snd_soc_update_bits(codec, SN95031_PCM2C2, BIT(0), 0); + snd_soc_write(codec, SN95031_AUDPLLCTRL, 0); + } + break; + + + case SND_SOC_BIAS_OFF: + pr_debug("vaud_bias _OFF doing rail shutdown\n"); + snd_soc_write(codec, SN95031_VAUD, BIT(3)); + break; + } + + codec->dapm.bias_level = level; + return 0; +} + +static int sn95031_vhs_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) { + pr_debug("VHS SND_SOC_DAPM_EVENT_ON doing rail startup now\n"); + /* power up the rail */ + snd_soc_write(w->codec, SN95031_VHSP, 0x3D); + snd_soc_write(w->codec, SN95031_VHSN, 0x3F); + msleep(1); + } else if (SND_SOC_DAPM_EVENT_OFF(event)) { + pr_debug("VHS SND_SOC_DAPM_EVENT_OFF doing rail shutdown\n"); + snd_soc_write(w->codec, SN95031_VHSP, 0xC4); + snd_soc_write(w->codec, SN95031_VHSN, 0x04); + } + return 0; +} + +static int sn95031_vihf_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) { + pr_debug("VIHF SND_SOC_DAPM_EVENT_ON doing rail startup now\n"); + /* power up the rail */ + snd_soc_write(w->codec, SN95031_VIHF, 0x27); + msleep(1); + } else if (SND_SOC_DAPM_EVENT_OFF(event)) { + pr_debug("VIHF SND_SOC_DAPM_EVENT_OFF doing rail shutdown\n"); + snd_soc_write(w->codec, SN95031_VIHF, 0x24); + } + return 0; +} + +static int sn95031_dmic12_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + unsigned int ldo = 0, clk_dir = 0, data_dir = 0; + + if (SND_SOC_DAPM_EVENT_ON(event)) { + ldo = BIT(5)|BIT(4); + clk_dir = BIT(0); + data_dir = BIT(7); + } + /* program DMIC LDO, clock and set clock */ + snd_soc_update_bits(w->codec, SN95031_MICBIAS, BIT(5)|BIT(4), ldo); + snd_soc_update_bits(w->codec, SN95031_DMICBUF0123, BIT(0), clk_dir); + snd_soc_update_bits(w->codec, SN95031_DMICBUF0123, BIT(7), data_dir); + return 0; +} + +static int sn95031_dmic34_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + unsigned int ldo = 0, clk_dir = 0, data_dir = 0; + + if (SND_SOC_DAPM_EVENT_ON(event)) { + ldo = BIT(5)|BIT(4); + clk_dir = BIT(2); + data_dir = BIT(1); + } + /* program DMIC LDO, clock and set clock */ + snd_soc_update_bits(w->codec, SN95031_MICBIAS, BIT(5)|BIT(4), ldo); + snd_soc_update_bits(w->codec, SN95031_DMICBUF0123, BIT(2), clk_dir); + snd_soc_update_bits(w->codec, SN95031_DMICBUF45, BIT(1), data_dir); + return 0; +} + +static int sn95031_dmic56_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + unsigned int ldo = 0; + + if (SND_SOC_DAPM_EVENT_ON(event)) + ldo = BIT(7)|BIT(6); + + /* program DMIC LDO */ + snd_soc_update_bits(w->codec, SN95031_MICBIAS, BIT(7)|BIT(6), ldo); + return 0; +} + +/* mux controls */ +static const char *sn95031_mic_texts[] = { "AMIC", "LineIn" }; + +static const struct soc_enum sn95031_micl_enum = + SOC_ENUM_SINGLE(SN95031_ADCCONFIG, 1, 2, sn95031_mic_texts); + +static const struct snd_kcontrol_new sn95031_micl_mux_control = + SOC_DAPM_ENUM("Route", sn95031_micl_enum); + +static const struct soc_enum sn95031_micr_enum = + SOC_ENUM_SINGLE(SN95031_ADCCONFIG, 3, 2, sn95031_mic_texts); + +static const struct snd_kcontrol_new sn95031_micr_mux_control = + SOC_DAPM_ENUM("Route", sn95031_micr_enum); + +static const char *sn95031_input_texts[] = { "DMIC1", "DMIC2", "DMIC3", + "DMIC4", "DMIC5", "DMIC6", + "ADC Left", "ADC Right" }; + +static const struct soc_enum sn95031_input1_enum = + SOC_ENUM_SINGLE(SN95031_AUDIOMUX12, 0, 8, sn95031_input_texts); + +static const struct snd_kcontrol_new sn95031_input1_mux_control = + SOC_DAPM_ENUM("Route", sn95031_input1_enum); + +static const struct soc_enum sn95031_input2_enum = + SOC_ENUM_SINGLE(SN95031_AUDIOMUX12, 4, 8, sn95031_input_texts); + +static const struct snd_kcontrol_new sn95031_input2_mux_control = + SOC_DAPM_ENUM("Route", sn95031_input2_enum); + +static const struct soc_enum sn95031_input3_enum = + SOC_ENUM_SINGLE(SN95031_AUDIOMUX34, 0, 8, sn95031_input_texts); + +static const struct snd_kcontrol_new sn95031_input3_mux_control = + SOC_DAPM_ENUM("Route", sn95031_input3_enum); + +static const struct soc_enum sn95031_input4_enum = + SOC_ENUM_SINGLE(SN95031_AUDIOMUX34, 4, 8, sn95031_input_texts); + +static const struct snd_kcontrol_new sn95031_input4_mux_control = + SOC_DAPM_ENUM("Route", sn95031_input4_enum); + +/* capture path controls */ + +static const char *sn95031_micmode_text[] = {"Single Ended", "Differential"}; + +/* 0dB to 30dB in 10dB steps */ +static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 10, 0); + +static const struct soc_enum sn95031_micmode1_enum = + SOC_ENUM_SINGLE(SN95031_MICAMP1, 1, 2, sn95031_micmode_text); +static const struct soc_enum sn95031_micmode2_enum = + SOC_ENUM_SINGLE(SN95031_MICAMP2, 1, 2, sn95031_micmode_text); + +static const char *sn95031_dmic_cfg_text[] = {"GPO", "DMIC"}; + +static const struct soc_enum sn95031_dmic12_cfg_enum = + SOC_ENUM_SINGLE(SN95031_DMICMUX, 0, 2, sn95031_dmic_cfg_text); +static const struct soc_enum sn95031_dmic34_cfg_enum = + SOC_ENUM_SINGLE(SN95031_DMICMUX, 1, 2, sn95031_dmic_cfg_text); +static const struct soc_enum sn95031_dmic56_cfg_enum = + SOC_ENUM_SINGLE(SN95031_DMICMUX, 2, 2, sn95031_dmic_cfg_text); + +static const struct snd_kcontrol_new sn95031_snd_controls[] = { + SOC_ENUM("Mic1Mode Capture Route", sn95031_micmode1_enum), + SOC_ENUM("Mic2Mode Capture Route", sn95031_micmode2_enum), + SOC_ENUM("DMIC12 Capture Route", sn95031_dmic12_cfg_enum), + SOC_ENUM("DMIC34 Capture Route", sn95031_dmic34_cfg_enum), + SOC_ENUM("DMIC56 Capture Route", sn95031_dmic56_cfg_enum), + SOC_SINGLE_TLV("Mic1 Capture Volume", SN95031_MICAMP1, + 2, 4, 0, mic_tlv), + SOC_SINGLE_TLV("Mic2 Capture Volume", SN95031_MICAMP2, + 2, 4, 0, mic_tlv), +}; + +/* DAPM widgets */ +static const struct snd_soc_dapm_widget sn95031_dapm_widgets[] = { + + /* all end points mic, hs etc */ + SND_SOC_DAPM_OUTPUT("HPOUTL"), + SND_SOC_DAPM_OUTPUT("HPOUTR"), + SND_SOC_DAPM_OUTPUT("EPOUT"), + SND_SOC_DAPM_OUTPUT("IHFOUTL"), + SND_SOC_DAPM_OUTPUT("IHFOUTR"), + SND_SOC_DAPM_OUTPUT("LINEOUTL"), + SND_SOC_DAPM_OUTPUT("LINEOUTR"), + SND_SOC_DAPM_OUTPUT("VIB1OUT"), + SND_SOC_DAPM_OUTPUT("VIB2OUT"), + + SND_SOC_DAPM_INPUT("AMIC1"), /* headset mic */ + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + SND_SOC_DAPM_INPUT("DMIC3"), + SND_SOC_DAPM_INPUT("DMIC4"), + SND_SOC_DAPM_INPUT("DMIC5"), + SND_SOC_DAPM_INPUT("DMIC6"), + SND_SOC_DAPM_INPUT("LINEINL"), + SND_SOC_DAPM_INPUT("LINEINR"), + + SND_SOC_DAPM_MICBIAS("AMIC1Bias", SN95031_MICBIAS, 2, 0), + SND_SOC_DAPM_MICBIAS("AMIC2Bias", SN95031_MICBIAS, 3, 0), + SND_SOC_DAPM_MICBIAS("DMIC12Bias", SN95031_DMICMUX, 3, 0), + SND_SOC_DAPM_MICBIAS("DMIC34Bias", SN95031_DMICMUX, 4, 0), + SND_SOC_DAPM_MICBIAS("DMIC56Bias", SN95031_DMICMUX, 5, 0), + + SND_SOC_DAPM_SUPPLY("DMIC12supply", SN95031_DMICLK, 0, 0, + sn95031_dmic12_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("DMIC34supply", SN95031_DMICLK, 1, 0, + sn95031_dmic34_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("DMIC56supply", SN95031_DMICLK, 2, 0, + sn95031_dmic56_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT("PCM_Out", "Capture", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY("Headset Rail", SND_SOC_NOPM, 0, 0, + sn95031_vhs_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("Speaker Rail", SND_SOC_NOPM, 0, 0, + sn95031_vihf_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* playback path driver enables */ + SND_SOC_DAPM_PGA("Headset Left Playback", + SN95031_DRIVEREN, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Headset Right Playback", + SN95031_DRIVEREN, 1, 0, NULL, 0), + SND_SOC_DAPM_PGA("Speaker Left Playback", + SN95031_DRIVEREN, 2, 0, NULL, 0), + SND_SOC_DAPM_PGA("Speaker Right Playback", + SN95031_DRIVEREN, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("Vibra1 Playback", + SN95031_DRIVEREN, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("Vibra2 Playback", + SN95031_DRIVEREN, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("Earpiece Playback", + SN95031_DRIVEREN, 6, 0, NULL, 0), + SND_SOC_DAPM_PGA("Lineout Left Playback", + SN95031_LOCTL, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Lineout Right Playback", + SN95031_LOCTL, 4, 0, NULL, 0), + + /* playback path filter enable */ + SND_SOC_DAPM_PGA("Headset Left Filter", + SN95031_HSEPRXCTRL, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("Headset Right Filter", + SN95031_HSEPRXCTRL, 5, 0, NULL, 0), + SND_SOC_DAPM_PGA("Speaker Left Filter", + SN95031_IHFRXCTRL, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("Speaker Right Filter", + SN95031_IHFRXCTRL, 1, 0, NULL, 0), + + /* DACs */ + SND_SOC_DAPM_DAC("HSDAC Left", "Headset", + SN95031_DACCONFIG, 0, 0), + SND_SOC_DAPM_DAC("HSDAC Right", "Headset", + SN95031_DACCONFIG, 1, 0), + SND_SOC_DAPM_DAC("IHFDAC Left", "Speaker", + SN95031_DACCONFIG, 2, 0), + SND_SOC_DAPM_DAC("IHFDAC Right", "Speaker", + SN95031_DACCONFIG, 3, 0), + SND_SOC_DAPM_DAC("Vibra1 DAC", "Vibra1", + SN95031_VIB1C5, 1, 0), + SND_SOC_DAPM_DAC("Vibra2 DAC", "Vibra2", + SN95031_VIB2C5, 1, 0), + + /* capture widgets */ + SND_SOC_DAPM_PGA("LineIn Enable Left", SN95031_MICAMP1, + 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("LineIn Enable Right", SN95031_MICAMP2, + 7, 0, NULL, 0), + + SND_SOC_DAPM_PGA("MIC1 Enable", SN95031_MICAMP1, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("MIC2 Enable", SN95031_MICAMP2, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("TX1 Enable", SN95031_AUDIOTXEN, 2, 0, NULL, 0), + SND_SOC_DAPM_PGA("TX2 Enable", SN95031_AUDIOTXEN, 3, 0, NULL, 0), + SND_SOC_DAPM_PGA("TX3 Enable", SN95031_AUDIOTXEN, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("TX4 Enable", SN95031_AUDIOTXEN, 5, 0, NULL, 0), + + /* ADC have null stream as they will be turned ON by TX path */ + SND_SOC_DAPM_ADC("ADC Left", NULL, + SN95031_ADCCONFIG, 0, 0), + SND_SOC_DAPM_ADC("ADC Right", NULL, + SN95031_ADCCONFIG, 2, 0), + + SND_SOC_DAPM_MUX("Mic_InputL Capture Route", + SND_SOC_NOPM, 0, 0, &sn95031_micl_mux_control), + SND_SOC_DAPM_MUX("Mic_InputR Capture Route", + SND_SOC_NOPM, 0, 0, &sn95031_micr_mux_control), + + SND_SOC_DAPM_MUX("Txpath1 Capture Route", + SND_SOC_NOPM, 0, 0, &sn95031_input1_mux_control), + SND_SOC_DAPM_MUX("Txpath2 Capture Route", + SND_SOC_NOPM, 0, 0, &sn95031_input2_mux_control), + SND_SOC_DAPM_MUX("Txpath3 Capture Route", + SND_SOC_NOPM, 0, 0, &sn95031_input3_mux_control), + SND_SOC_DAPM_MUX("Txpath4 Capture Route", + SND_SOC_NOPM, 0, 0, &sn95031_input4_mux_control), + +}; + +static const struct snd_soc_dapm_route sn95031_audio_map[] = { + /* headset and earpiece map */ + { "HPOUTL", NULL, "Headset Rail"}, + { "HPOUTR", NULL, "Headset Rail"}, + { "HPOUTL", NULL, "Headset Left Playback" }, + { "HPOUTR", NULL, "Headset Right Playback" }, + { "EPOUT", NULL, "Earpiece Playback" }, + { "Headset Left Playback", NULL, "Headset Left Filter"}, + { "Headset Right Playback", NULL, "Headset Right Filter"}, + { "Earpiece Playback", NULL, "Headset Left Filter"}, + { "Headset Left Filter", NULL, "HSDAC Left"}, + { "Headset Right Filter", NULL, "HSDAC Right"}, + + /* speaker map */ + { "IHFOUTL", NULL, "Speaker Rail"}, + { "IHFOUTR", NULL, "Speaker Rail"}, + { "IHFOUTL", "NULL", "Speaker Left Playback"}, + { "IHFOUTR", "NULL", "Speaker Right Playback"}, + { "Speaker Left Playback", NULL, "Speaker Left Filter"}, + { "Speaker Right Playback", NULL, "Speaker Right Filter"}, + { "Speaker Left Filter", NULL, "IHFDAC Left"}, + { "Speaker Right Filter", NULL, "IHFDAC Right"}, + + /* vibra map */ + { "VIB1OUT", NULL, "Vibra1 Playback"}, + { "Vibra1 Playback", NULL, "Vibra1 DAC"}, + + { "VIB2OUT", NULL, "Vibra2 Playback"}, + { "Vibra2 Playback", NULL, "Vibra2 DAC"}, + + /* lineout */ + { "LINEOUTL", NULL, "Lineout Left Playback"}, + { "LINEOUTR", NULL, "Lineout Right Playback"}, + { "Lineout Left Playback", NULL, "Headset Left Filter"}, + { "Lineout Left Playback", NULL, "Speaker Left Filter"}, + { "Lineout Left Playback", NULL, "Vibra1 DAC"}, + { "Lineout Right Playback", NULL, "Headset Right Filter"}, + { "Lineout Right Playback", NULL, "Speaker Right Filter"}, + { "Lineout Right Playback", NULL, "Vibra2 DAC"}, + + /* Headset (AMIC1) mic */ + { "AMIC1Bias", NULL, "AMIC1"}, + { "MIC1 Enable", NULL, "AMIC1Bias"}, + { "Mic_InputL Capture Route", "AMIC", "MIC1 Enable"}, + + /* AMIC2 */ + { "AMIC2Bias", NULL, "AMIC2"}, + { "MIC2 Enable", NULL, "AMIC2Bias"}, + { "Mic_InputR Capture Route", "AMIC", "MIC2 Enable"}, + + + /* Linein */ + { "LineIn Enable Left", NULL, "LINEINL"}, + { "LineIn Enable Right", NULL, "LINEINR"}, + { "Mic_InputL Capture Route", "LineIn", "LineIn Enable Left"}, + { "Mic_InputR Capture Route", "LineIn", "LineIn Enable Right"}, + + /* ADC connection */ + { "ADC Left", NULL, "Mic_InputL Capture Route"}, + { "ADC Right", NULL, "Mic_InputR Capture Route"}, + + /*DMIC connections */ + { "DMIC1", NULL, "DMIC12supply"}, + { "DMIC2", NULL, "DMIC12supply"}, + { "DMIC3", NULL, "DMIC34supply"}, + { "DMIC4", NULL, "DMIC34supply"}, + { "DMIC5", NULL, "DMIC56supply"}, + { "DMIC6", NULL, "DMIC56supply"}, + + { "DMIC12Bias", NULL, "DMIC1"}, + { "DMIC12Bias", NULL, "DMIC2"}, + { "DMIC34Bias", NULL, "DMIC3"}, + { "DMIC34Bias", NULL, "DMIC4"}, + { "DMIC56Bias", NULL, "DMIC5"}, + { "DMIC56Bias", NULL, "DMIC6"}, + + /*TX path inputs*/ + { "Txpath1 Capture Route", "ADC Left", "ADC Left"}, + { "Txpath2 Capture Route", "ADC Left", "ADC Left"}, + { "Txpath3 Capture Route", "ADC Left", "ADC Left"}, + { "Txpath4 Capture Route", "ADC Left", "ADC Left"}, + { "Txpath1 Capture Route", "ADC Right", "ADC Right"}, + { "Txpath2 Capture Route", "ADC Right", "ADC Right"}, + { "Txpath3 Capture Route", "ADC Right", "ADC Right"}, + { "Txpath4 Capture Route", "ADC Right", "ADC Right"}, + { "Txpath1 Capture Route", "DMIC1", "DMIC1"}, + { "Txpath2 Capture Route", "DMIC1", "DMIC1"}, + { "Txpath3 Capture Route", "DMIC1", "DMIC1"}, + { "Txpath4 Capture Route", "DMIC1", "DMIC1"}, + { "Txpath1 Capture Route", "DMIC2", "DMIC2"}, + { "Txpath2 Capture Route", "DMIC2", "DMIC2"}, + { "Txpath3 Capture Route", "DMIC2", "DMIC2"}, + { "Txpath4 Capture Route", "DMIC2", "DMIC2"}, + { "Txpath1 Capture Route", "DMIC3", "DMIC3"}, + { "Txpath2 Capture Route", "DMIC3", "DMIC3"}, + { "Txpath3 Capture Route", "DMIC3", "DMIC3"}, + { "Txpath4 Capture Route", "DMIC3", "DMIC3"}, + { "Txpath1 Capture Route", "DMIC4", "DMIC4"}, + { "Txpath2 Capture Route", "DMIC4", "DMIC4"}, + { "Txpath3 Capture Route", "DMIC4", "DMIC4"}, + { "Txpath4 Capture Route", "DMIC4", "DMIC4"}, + { "Txpath1 Capture Route", "DMIC5", "DMIC5"}, + { "Txpath2 Capture Route", "DMIC5", "DMIC5"}, + { "Txpath3 Capture Route", "DMIC5", "DMIC5"}, + { "Txpath4 Capture Route", "DMIC5", "DMIC5"}, + { "Txpath1 Capture Route", "DMIC6", "DMIC6"}, + { "Txpath2 Capture Route", "DMIC6", "DMIC6"}, + { "Txpath3 Capture Route", "DMIC6", "DMIC6"}, + { "Txpath4 Capture Route", "DMIC6", "DMIC6"}, + + /* tx path */ + { "TX1 Enable", NULL, "Txpath1 Capture Route"}, + { "TX2 Enable", NULL, "Txpath2 Capture Route"}, + { "TX3 Enable", NULL, "Txpath3 Capture Route"}, + { "TX4 Enable", NULL, "Txpath4 Capture Route"}, + { "PCM_Out", NULL, "TX1 Enable"}, + { "PCM_Out", NULL, "TX2 Enable"}, + { "PCM_Out", NULL, "TX3 Enable"}, + { "PCM_Out", NULL, "TX4 Enable"}, + +}; + +/* speaker and headset mutes, for audio pops and clicks */ +static int sn95031_pcm_hs_mute(struct snd_soc_dai *dai, int mute) +{ + snd_soc_update_bits(dai->codec, + SN95031_HSLVOLCTRL, BIT(7), (!mute << 7)); + snd_soc_update_bits(dai->codec, + SN95031_HSRVOLCTRL, BIT(7), (!mute << 7)); + return 0; +} + +static int sn95031_pcm_spkr_mute(struct snd_soc_dai *dai, int mute) +{ + snd_soc_update_bits(dai->codec, + SN95031_IHFLVOLCTRL, BIT(7), (!mute << 7)); + snd_soc_update_bits(dai->codec, + SN95031_IHFRVOLCTRL, BIT(7), (!mute << 7)); + return 0; +} + +int sn95031_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + unsigned int format, rate; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + format = BIT(4)|BIT(5); + break; + + case SNDRV_PCM_FORMAT_S24_LE: + format = 0; + break; + default: + return -EINVAL; + } + snd_soc_update_bits(dai->codec, SN95031_PCM2C2, + BIT(4)|BIT(5), format); + + switch (params_rate(params)) { + case 48000: + pr_debug("RATE_48000\n"); + rate = 0; + break; + + case 44100: + pr_debug("RATE_44100\n"); + rate = BIT(7); + break; + + default: + pr_err("ERR rate %d\n", params_rate(params)); + return -EINVAL; + } + snd_soc_update_bits(dai->codec, SN95031_PCM1C1, BIT(7), rate); + + return 0; +} + +/* Codec DAI section */ +static struct snd_soc_dai_ops sn95031_headset_dai_ops = { + .digital_mute = sn95031_pcm_hs_mute, + .hw_params = sn95031_pcm_hw_params, +}; + +static struct snd_soc_dai_ops sn95031_speaker_dai_ops = { + .digital_mute = sn95031_pcm_spkr_mute, + .hw_params = sn95031_pcm_hw_params, +}; + +static struct snd_soc_dai_ops sn95031_vib1_dai_ops = { + .hw_params = sn95031_pcm_hw_params, +}; + +static struct snd_soc_dai_ops sn95031_vib2_dai_ops = { + .hw_params = sn95031_pcm_hw_params, +}; + +struct snd_soc_dai_driver sn95031_dais[] = { +{ + .name = "SN95031 Headset", + .playback = { + .stream_name = "Headset", + .channels_min = 2, + .channels_max = 2, + .rates = SN95031_RATES, + .formats = SN95031_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 5, + .rates = SN95031_RATES, + .formats = SN95031_FORMATS, + }, + .ops = &sn95031_headset_dai_ops, +}, +{ .name = "SN95031 Speaker", + .playback = { + .stream_name = "Speaker", + .channels_min = 2, + .channels_max = 2, + .rates = SN95031_RATES, + .formats = SN95031_FORMATS, + }, + .ops = &sn95031_speaker_dai_ops, +}, +{ .name = "SN95031 Vibra1", + .playback = { + .stream_name = "Vibra1", + .channels_min = 1, + .channels_max = 1, + .rates = SN95031_RATES, + .formats = SN95031_FORMATS, + }, + .ops = &sn95031_vib1_dai_ops, +}, +{ .name = "SN95031 Vibra2", + .playback = { + .stream_name = "Vibra2", + .channels_min = 1, + .channels_max = 1, + .rates = SN95031_RATES, + .formats = SN95031_FORMATS, + }, + .ops = &sn95031_vib2_dai_ops, +}, +}; + +static inline void sn95031_disable_jack_btn(struct snd_soc_codec *codec) +{ + snd_soc_write(codec, SN95031_BTNCTRL2, 0x00); +} + +static inline void sn95031_enable_jack_btn(struct snd_soc_codec *codec) +{ + snd_soc_write(codec, SN95031_BTNCTRL1, 0x77); + snd_soc_write(codec, SN95031_BTNCTRL2, 0x01); +} + +static int sn95031_get_headset_state(struct snd_soc_jack *mfld_jack) +{ + int micbias = sn95031_get_mic_bias(mfld_jack->codec); + + int jack_type = snd_soc_jack_get_type(mfld_jack, micbias); + + pr_debug("jack type detected = %d\n", jack_type); + if (jack_type == SND_JACK_HEADSET) + sn95031_enable_jack_btn(mfld_jack->codec); + return jack_type; +} + +void sn95031_jack_detection(struct mfld_jack_data *jack_data) +{ + unsigned int status; + unsigned int mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_HEADSET; + + pr_debug("interrupt id read in sram = 0x%x\n", jack_data->intr_id); + if (jack_data->intr_id & 0x1) { + pr_debug("short_push detected\n"); + status = SND_JACK_HEADSET | SND_JACK_BTN_0; + } else if (jack_data->intr_id & 0x2) { + pr_debug("long_push detected\n"); + status = SND_JACK_HEADSET | SND_JACK_BTN_1; + } else if (jack_data->intr_id & 0x4) { + pr_debug("headset or headphones inserted\n"); + status = sn95031_get_headset_state(jack_data->mfld_jack); + } else if (jack_data->intr_id & 0x8) { + pr_debug("headset or headphones removed\n"); + status = 0; + sn95031_disable_jack_btn(jack_data->mfld_jack->codec); + } else { + pr_err("unidentified interrupt\n"); + return; + } + + snd_soc_jack_report(jack_data->mfld_jack, status, mask); + /*button pressed and released so we send explicit button release */ + if ((status & SND_JACK_BTN_0) | (status & SND_JACK_BTN_1)) + snd_soc_jack_report(jack_data->mfld_jack, + SND_JACK_HEADSET, mask); +} +EXPORT_SYMBOL_GPL(sn95031_jack_detection); + +/* codec registration */ +static int sn95031_codec_probe(struct snd_soc_codec *codec) +{ + int ret; + + pr_debug("codec_probe called\n"); + + codec->dapm.bias_level = SND_SOC_BIAS_OFF; + codec->dapm.idle_bias_off = 1; + + /* PCM interface config + * This sets the pcm rx slot conguration to max 6 slots + * for max 4 dais (2 stereo and 2 mono) + */ + snd_soc_write(codec, SN95031_PCM2RXSLOT01, 0x10); + snd_soc_write(codec, SN95031_PCM2RXSLOT23, 0x32); + snd_soc_write(codec, SN95031_PCM2RXSLOT45, 0x54); + snd_soc_write(codec, SN95031_PCM2TXSLOT01, 0x10); + snd_soc_write(codec, SN95031_PCM2TXSLOT23, 0x32); + /* pcm port setting + * This sets the pcm port to slave and clock at 19.2Mhz which + * can support 6slots, sampling rate set per stream in hw-params + */ + snd_soc_write(codec, SN95031_PCM1C1, 0x00); + snd_soc_write(codec, SN95031_PCM2C1, 0x01); + snd_soc_write(codec, SN95031_PCM2C2, 0x0A); + snd_soc_write(codec, SN95031_HSMIXER, BIT(0)|BIT(4)); + /* vendor vibra workround, the vibras are muted by + * custom register so unmute them + */ + snd_soc_write(codec, SN95031_SSR5, 0x80); + snd_soc_write(codec, SN95031_SSR6, 0x80); + snd_soc_write(codec, SN95031_VIB1C5, 0x00); + snd_soc_write(codec, SN95031_VIB2C5, 0x00); + /* configure vibras for pcm port */ + snd_soc_write(codec, SN95031_VIB1C3, 0x00); + snd_soc_write(codec, SN95031_VIB2C3, 0x00); + + /* soft mute ramp time */ + snd_soc_write(codec, SN95031_SOFTMUTE, 0x3); + /* fix the initial volume at 1dB, + * default in +9dB, + * 1dB give optimal swing on DAC, amps + */ + snd_soc_write(codec, SN95031_HSLVOLCTRL, 0x08); + snd_soc_write(codec, SN95031_HSRVOLCTRL, 0x08); + snd_soc_write(codec, SN95031_IHFLVOLCTRL, 0x08); + snd_soc_write(codec, SN95031_IHFRVOLCTRL, 0x08); + /* dac mode and lineout workaround */ + snd_soc_write(codec, SN95031_SSR2, 0x10); + snd_soc_write(codec, SN95031_SSR3, 0x40); + + snd_soc_add_controls(codec, sn95031_snd_controls, + ARRAY_SIZE(sn95031_snd_controls)); + + ret = snd_soc_dapm_new_controls(&codec->dapm, sn95031_dapm_widgets, + ARRAY_SIZE(sn95031_dapm_widgets)); + if (ret) + pr_err("soc_dapm_new_control failed %d", ret); + ret = snd_soc_dapm_add_routes(&codec->dapm, sn95031_audio_map, + ARRAY_SIZE(sn95031_audio_map)); + if (ret) + pr_err("soc_dapm_add_routes failed %d", ret); + + return ret; +} + +static int sn95031_codec_remove(struct snd_soc_codec *codec) +{ + pr_debug("codec_remove called\n"); + sn95031_set_vaud_bias(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +struct snd_soc_codec_driver sn95031_codec = { + .probe = sn95031_codec_probe, + .remove = sn95031_codec_remove, + .read = sn95031_read, + .write = sn95031_write, + .set_bias_level = sn95031_set_vaud_bias, +}; + +static int __devinit sn95031_device_probe(struct platform_device *pdev) +{ + pr_debug("codec device probe called for %s\n", dev_name(&pdev->dev)); + return snd_soc_register_codec(&pdev->dev, &sn95031_codec, + sn95031_dais, ARRAY_SIZE(sn95031_dais)); +} + +static int __devexit sn95031_device_remove(struct platform_device *pdev) +{ + pr_debug("codec device remove called\n"); + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static struct platform_driver sn95031_codec_driver = { + .driver = { + .name = "sn95031", + .owner = THIS_MODULE, + }, + .probe = sn95031_device_probe, + .remove = sn95031_device_remove, +}; + +static int __init sn95031_init(void) +{ + pr_debug("driver init called\n"); + return platform_driver_register(&sn95031_codec_driver); +} +module_init(sn95031_init); + +static void __exit sn95031_exit(void) +{ + pr_debug("driver exit called\n"); + platform_driver_unregister(&sn95031_codec_driver); +} +module_exit(sn95031_exit); + +MODULE_DESCRIPTION("ASoC TI SN95031 codec driver"); +MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); +MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sn95031"); diff --git a/sound/soc/codecs/sn95031.h b/sound/soc/codecs/sn95031.h new file mode 100644 index 000000000000..20376d234fb8 --- /dev/null +++ b/sound/soc/codecs/sn95031.h @@ -0,0 +1,132 @@ +/* + * sn95031.h - TI sn95031 Codec driver + * + * Copyright (C) 2010 Intel Corp + * Author: Vinod Koul <vinod.koul@intel.com> + * Author: Harsha Priya <priya.harsha@intel.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 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * + */ +#ifndef _SN95031_H +#define _SN95031_H + +/*register map*/ +#define SN95031_VAUD 0xDB +#define SN95031_VHSP 0xDC +#define SN95031_VHSN 0xDD +#define SN95031_VIHF 0xC9 + +#define SN95031_AUDPLLCTRL 0x240 +#define SN95031_DMICBUF0123 0x241 +#define SN95031_DMICBUF45 0x242 +#define SN95031_DMICGPO 0x244 +#define SN95031_DMICMUX 0x245 +#define SN95031_DMICLK 0x246 +#define SN95031_MICBIAS 0x247 +#define SN95031_ADCCONFIG 0x248 +#define SN95031_MICAMP1 0x249 +#define SN95031_MICAMP2 0x24A +#define SN95031_NOISEMUX 0x24B +#define SN95031_AUDIOMUX12 0x24C +#define SN95031_AUDIOMUX34 0x24D +#define SN95031_AUDIOSINC 0x24E +#define SN95031_AUDIOTXEN 0x24F +#define SN95031_HSEPRXCTRL 0x250 +#define SN95031_IHFRXCTRL 0x251 +#define SN95031_HSMIXER 0x256 +#define SN95031_DACCONFIG 0x257 +#define SN95031_SOFTMUTE 0x258 +#define SN95031_HSLVOLCTRL 0x259 +#define SN95031_HSRVOLCTRL 0x25A +#define SN95031_IHFLVOLCTRL 0x25B +#define SN95031_IHFRVOLCTRL 0x25C +#define SN95031_DRIVEREN 0x25D +#define SN95031_LOCTL 0x25E +#define SN95031_VIB1C1 0x25F +#define SN95031_VIB1C2 0x260 +#define SN95031_VIB1C3 0x261 +#define SN95031_VIB1SPIPCM1 0x262 +#define SN95031_VIB1SPIPCM2 0x263 +#define SN95031_VIB1C5 0x264 +#define SN95031_VIB2C1 0x265 +#define SN95031_VIB2C2 0x266 +#define SN95031_VIB2C3 0x267 +#define SN95031_VIB2SPIPCM1 0x268 +#define SN95031_VIB2SPIPCM2 0x269 +#define SN95031_VIB2C5 0x26A +#define SN95031_BTNCTRL1 0x26B +#define SN95031_BTNCTRL2 0x26C +#define SN95031_PCM1TXSLOT01 0x26D +#define SN95031_PCM1TXSLOT23 0x26E +#define SN95031_PCM1TXSLOT45 0x26F +#define SN95031_PCM1RXSLOT0_3 0x270 +#define SN95031_PCM1RXSLOT45 0x271 +#define SN95031_PCM2TXSLOT01 0x272 +#define SN95031_PCM2TXSLOT23 0x273 +#define SN95031_PCM2TXSLOT45 0x274 +#define SN95031_PCM2RXSLOT01 0x275 +#define SN95031_PCM2RXSLOT23 0x276 +#define SN95031_PCM2RXSLOT45 0x277 +#define SN95031_PCM1C1 0x278 +#define SN95031_PCM1C2 0x279 +#define SN95031_PCM1C3 0x27A +#define SN95031_PCM2C1 0x27B +#define SN95031_PCM2C2 0x27C +/*end codec register defn*/ + +/*vendor defn these are not part of avp*/ +#define SN95031_SSR2 0x381 +#define SN95031_SSR3 0x382 +#define SN95031_SSR5 0x384 +#define SN95031_SSR6 0x385 + +/* ADC registers */ + +#define SN95031_ADC1CNTL1 0x1C0 +#define SN95031_ADC_ENBL 0x10 +#define SN95031_ADC_START 0x08 +#define SN95031_ADC1CNTL3 0x1C2 +#define SN95031_ADCTHERM_ENBL 0x04 +#define SN95031_ADCRRDATA_ENBL 0x05 +#define SN95031_STOPBIT_MASK 16 +#define SN95031_ADCTHERM_MASK 4 +#define SN95031_ADC_CHANLS_MAX 15 /* Number of ADC channels */ +#define SN95031_ADC_LOOP_MAX (SN95031_ADC_CHANLS_MAX - 1) +#define SN95031_ADC_NO_LOOP 0x07 +#define SN95031_AUDIO_GPIO_CTRL 0x070 + +/* ADC channel code values */ +#define SN95031_AUDIO_DETECT_CODE 0x06 + +/* ADC base addresses */ +#define SN95031_ADC_CHNL_START_ADDR 0x1C5 /* increments by 1 */ +#define SN95031_ADC_DATA_START_ADDR 0x1D4 /* increments by 2 */ +/* multipier to convert to mV */ +#define SN95031_ADC_ONE_LSB_MULTIPLIER 2346 + + +struct mfld_jack_data { + int intr_id; + int micbias_vol; + struct snd_soc_jack *mfld_jack; +}; + +extern void sn95031_jack_detection(struct mfld_jack_data *jack_data); + +#endif diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c new file mode 100644 index 000000000000..e93b9d1ae1dd --- /dev/null +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -0,0 +1,794 @@ +/* + * linux/sound/soc/codecs/tlv320aic32x4.c + * + * Copyright 2011 Vista Silicon S.L. + * + * Author: Javier Martin <javier.martin@vista-silicon.com> + * + * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#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 <linux/cdev.h> +#include <linux/slab.h> + +#include <sound/tlv320aic32x4.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 "tlv320aic32x4.h" + +struct aic32x4_rate_divs { + u32 mclk; + u32 rate; + u8 p_val; + u8 pll_j; + u16 pll_d; + u16 dosr; + u8 ndac; + u8 mdac; + u8 aosr; + u8 nadc; + u8 madc; + u8 blck_N; +}; + +struct aic32x4_priv { + u32 sysclk; + s32 master; + u8 page_no; + void *control_data; + u32 power_cfg; + u32 micpga_routing; + bool swapdacs; +}; + +/* 0dB min, 1dB steps */ +static DECLARE_TLV_DB_SCALE(tlv_step_1, 0, 100, 0); +/* 0dB min, 0.5dB steps */ +static DECLARE_TLV_DB_SCALE(tlv_step_0_5, 0, 50, 0); + +static const struct snd_kcontrol_new aic32x4_snd_controls[] = { + SOC_DOUBLE_R_TLV("PCM Playback Volume", AIC32X4_LDACVOL, + AIC32X4_RDACVOL, 0, 0x30, 0, tlv_step_0_5), + SOC_DOUBLE_R_TLV("HP Driver Gain Volume", AIC32X4_HPLGAIN, + AIC32X4_HPRGAIN, 0, 0x1D, 0, tlv_step_1), + SOC_DOUBLE_R_TLV("LO Driver Gain Volume", AIC32X4_LOLGAIN, + AIC32X4_LORGAIN, 0, 0x1D, 0, tlv_step_1), + SOC_DOUBLE_R("HP DAC Playback Switch", AIC32X4_HPLGAIN, + AIC32X4_HPRGAIN, 6, 0x01, 1), + SOC_DOUBLE_R("LO DAC Playback Switch", AIC32X4_LOLGAIN, + AIC32X4_LORGAIN, 6, 0x01, 1), + SOC_DOUBLE_R("Mic PGA Switch", AIC32X4_LMICPGAVOL, + AIC32X4_RMICPGAVOL, 7, 0x01, 1), + + SOC_SINGLE("ADCFGA Left Mute Switch", AIC32X4_ADCFGA, 7, 1, 0), + SOC_SINGLE("ADCFGA Right Mute Switch", AIC32X4_ADCFGA, 3, 1, 0), + + SOC_DOUBLE_R_TLV("ADC Level Volume", AIC32X4_LADCVOL, + AIC32X4_RADCVOL, 0, 0x28, 0, tlv_step_0_5), + SOC_DOUBLE_R_TLV("PGA Level Volume", AIC32X4_LMICPGAVOL, + AIC32X4_RMICPGAVOL, 0, 0x5f, 0, tlv_step_0_5), + + SOC_SINGLE("Auto-mute Switch", AIC32X4_DACMUTE, 4, 7, 0), + + SOC_SINGLE("AGC Left Switch", AIC32X4_LAGC1, 7, 1, 0), + SOC_SINGLE("AGC Right Switch", AIC32X4_RAGC1, 7, 1, 0), + SOC_DOUBLE_R("AGC Target Level", AIC32X4_LAGC1, AIC32X4_RAGC1, + 4, 0x07, 0), + SOC_DOUBLE_R("AGC Gain Hysteresis", AIC32X4_LAGC1, AIC32X4_RAGC1, + 0, 0x03, 0), + SOC_DOUBLE_R("AGC Hysteresis", AIC32X4_LAGC2, AIC32X4_RAGC2, + 6, 0x03, 0), + SOC_DOUBLE_R("AGC Noise Threshold", AIC32X4_LAGC2, AIC32X4_RAGC2, + 1, 0x1F, 0), + SOC_DOUBLE_R("AGC Max PGA", AIC32X4_LAGC3, AIC32X4_RAGC3, + 0, 0x7F, 0), + SOC_DOUBLE_R("AGC Attack Time", AIC32X4_LAGC4, AIC32X4_RAGC4, + 3, 0x1F, 0), + SOC_DOUBLE_R("AGC Decay Time", AIC32X4_LAGC5, AIC32X4_RAGC5, + 3, 0x1F, 0), + SOC_DOUBLE_R("AGC Noise Debounce", AIC32X4_LAGC6, AIC32X4_RAGC6, + 0, 0x1F, 0), + SOC_DOUBLE_R("AGC Signal Debounce", AIC32X4_LAGC7, AIC32X4_RAGC7, + 0, 0x0F, 0), +}; + +static const struct aic32x4_rate_divs aic32x4_divs[] = { + /* 8k rate */ + {AIC32X4_FREQ_12000000, 8000, 1, 7, 6800, 768, 5, 3, 128, 5, 18, 24}, + {AIC32X4_FREQ_24000000, 8000, 2, 7, 6800, 768, 15, 1, 64, 45, 4, 24}, + {AIC32X4_FREQ_25000000, 8000, 2, 7, 3728, 768, 15, 1, 64, 45, 4, 24}, + /* 11.025k rate */ + {AIC32X4_FREQ_12000000, 11025, 1, 7, 5264, 512, 8, 2, 128, 8, 8, 16}, + {AIC32X4_FREQ_24000000, 11025, 2, 7, 5264, 512, 16, 1, 64, 32, 4, 16}, + /* 16k rate */ + {AIC32X4_FREQ_12000000, 16000, 1, 7, 6800, 384, 5, 3, 128, 5, 9, 12}, + {AIC32X4_FREQ_24000000, 16000, 2, 7, 6800, 384, 15, 1, 64, 18, 5, 12}, + {AIC32X4_FREQ_25000000, 16000, 2, 7, 3728, 384, 15, 1, 64, 18, 5, 12}, + /* 22.05k rate */ + {AIC32X4_FREQ_12000000, 22050, 1, 7, 5264, 256, 4, 4, 128, 4, 8, 8}, + {AIC32X4_FREQ_24000000, 22050, 2, 7, 5264, 256, 16, 1, 64, 16, 4, 8}, + {AIC32X4_FREQ_25000000, 22050, 2, 7, 2253, 256, 16, 1, 64, 16, 4, 8}, + /* 32k rate */ + {AIC32X4_FREQ_12000000, 32000, 1, 7, 1680, 192, 2, 7, 64, 2, 21, 6}, + {AIC32X4_FREQ_24000000, 32000, 2, 7, 1680, 192, 7, 2, 64, 7, 6, 6}, + /* 44.1k rate */ + {AIC32X4_FREQ_12000000, 44100, 1, 7, 5264, 128, 2, 8, 128, 2, 8, 4}, + {AIC32X4_FREQ_24000000, 44100, 2, 7, 5264, 128, 8, 2, 64, 8, 4, 4}, + {AIC32X4_FREQ_25000000, 44100, 2, 7, 2253, 128, 8, 2, 64, 8, 4, 4}, + /* 48k rate */ + {AIC32X4_FREQ_12000000, 48000, 1, 8, 1920, 128, 2, 8, 128, 2, 8, 4}, + {AIC32X4_FREQ_24000000, 48000, 2, 8, 1920, 128, 8, 2, 64, 8, 4, 4}, + {AIC32X4_FREQ_25000000, 48000, 2, 7, 8643, 128, 8, 2, 64, 8, 4, 4} +}; + +static const struct snd_kcontrol_new hpl_output_mixer_controls[] = { + SOC_DAPM_SINGLE("L_DAC Switch", AIC32X4_HPLROUTE, 3, 1, 0), + SOC_DAPM_SINGLE("IN1_L Switch", AIC32X4_HPLROUTE, 2, 1, 0), +}; + +static const struct snd_kcontrol_new hpr_output_mixer_controls[] = { + SOC_DAPM_SINGLE("R_DAC Switch", AIC32X4_HPRROUTE, 3, 1, 0), + SOC_DAPM_SINGLE("IN1_R Switch", AIC32X4_HPRROUTE, 2, 1, 0), +}; + +static const struct snd_kcontrol_new lol_output_mixer_controls[] = { + SOC_DAPM_SINGLE("L_DAC Switch", AIC32X4_LOLROUTE, 3, 1, 0), +}; + +static const struct snd_kcontrol_new lor_output_mixer_controls[] = { + SOC_DAPM_SINGLE("R_DAC Switch", AIC32X4_LORROUTE, 3, 1, 0), +}; + +static const struct snd_kcontrol_new left_input_mixer_controls[] = { + SOC_DAPM_SINGLE("IN1_L P Switch", AIC32X4_LMICPGAPIN, 6, 1, 0), + SOC_DAPM_SINGLE("IN2_L P Switch", AIC32X4_LMICPGAPIN, 4, 1, 0), + SOC_DAPM_SINGLE("IN3_L P Switch", AIC32X4_LMICPGAPIN, 2, 1, 0), +}; + +static const struct snd_kcontrol_new right_input_mixer_controls[] = { + SOC_DAPM_SINGLE("IN1_R P Switch", AIC32X4_RMICPGAPIN, 6, 1, 0), + SOC_DAPM_SINGLE("IN2_R P Switch", AIC32X4_RMICPGAPIN, 4, 1, 0), + SOC_DAPM_SINGLE("IN3_R P Switch", AIC32X4_RMICPGAPIN, 2, 1, 0), +}; + +static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = { + SND_SOC_DAPM_DAC("Left DAC", "Left Playback", AIC32X4_DACSETUP, 7, 0), + SND_SOC_DAPM_MIXER("HPL Output Mixer", SND_SOC_NOPM, 0, 0, + &hpl_output_mixer_controls[0], + ARRAY_SIZE(hpl_output_mixer_controls)), + SND_SOC_DAPM_PGA("HPL Power", AIC32X4_OUTPWRCTL, 5, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("LOL Output Mixer", SND_SOC_NOPM, 0, 0, + &lol_output_mixer_controls[0], + ARRAY_SIZE(lol_output_mixer_controls)), + SND_SOC_DAPM_PGA("LOL Power", AIC32X4_OUTPWRCTL, 3, 0, NULL, 0), + + SND_SOC_DAPM_DAC("Right DAC", "Right Playback", AIC32X4_DACSETUP, 6, 0), + SND_SOC_DAPM_MIXER("HPR Output Mixer", SND_SOC_NOPM, 0, 0, + &hpr_output_mixer_controls[0], + ARRAY_SIZE(hpr_output_mixer_controls)), + SND_SOC_DAPM_PGA("HPR Power", AIC32X4_OUTPWRCTL, 4, 0, NULL, 0), + SND_SOC_DAPM_MIXER("LOR Output Mixer", SND_SOC_NOPM, 0, 0, + &lor_output_mixer_controls[0], + ARRAY_SIZE(lor_output_mixer_controls)), + SND_SOC_DAPM_PGA("LOR Power", AIC32X4_OUTPWRCTL, 2, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Left Input Mixer", SND_SOC_NOPM, 0, 0, + &left_input_mixer_controls[0], + ARRAY_SIZE(left_input_mixer_controls)), + SND_SOC_DAPM_MIXER("Right Input Mixer", SND_SOC_NOPM, 0, 0, + &right_input_mixer_controls[0], + ARRAY_SIZE(right_input_mixer_controls)), + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", AIC32X4_ADCSETUP, 7, 0), + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", AIC32X4_ADCSETUP, 6, 0), + SND_SOC_DAPM_MICBIAS("Mic Bias", AIC32X4_MICBIAS, 6, 0), + + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("LOL"), + SND_SOC_DAPM_OUTPUT("LOR"), + SND_SOC_DAPM_INPUT("IN1_L"), + SND_SOC_DAPM_INPUT("IN1_R"), + SND_SOC_DAPM_INPUT("IN2_L"), + SND_SOC_DAPM_INPUT("IN2_R"), + SND_SOC_DAPM_INPUT("IN3_L"), + SND_SOC_DAPM_INPUT("IN3_R"), +}; + +static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = { + /* Left Output */ + {"HPL Output Mixer", "L_DAC Switch", "Left DAC"}, + {"HPL Output Mixer", "IN1_L Switch", "IN1_L"}, + + {"HPL Power", NULL, "HPL Output Mixer"}, + {"HPL", NULL, "HPL Power"}, + + {"LOL Output Mixer", "L_DAC Switch", "Left DAC"}, + + {"LOL Power", NULL, "LOL Output Mixer"}, + {"LOL", NULL, "LOL Power"}, + + /* Right Output */ + {"HPR Output Mixer", "R_DAC Switch", "Right DAC"}, + {"HPR Output Mixer", "IN1_R Switch", "IN1_R"}, + + {"HPR Power", NULL, "HPR Output Mixer"}, + {"HPR", NULL, "HPR Power"}, + + {"LOR Output Mixer", "R_DAC Switch", "Right DAC"}, + + {"LOR Power", NULL, "LOR Output Mixer"}, + {"LOR", NULL, "LOR Power"}, + + /* Left input */ + {"Left Input Mixer", "IN1_L P Switch", "IN1_L"}, + {"Left Input Mixer", "IN2_L P Switch", "IN2_L"}, + {"Left Input Mixer", "IN3_L P Switch", "IN3_L"}, + + {"Left ADC", NULL, "Left Input Mixer"}, + + /* Right Input */ + {"Right Input Mixer", "IN1_R P Switch", "IN1_R"}, + {"Right Input Mixer", "IN2_R P Switch", "IN2_R"}, + {"Right Input Mixer", "IN3_R P Switch", "IN3_R"}, + + {"Right ADC", NULL, "Right Input Mixer"}, +}; + +static inline int aic32x4_change_page(struct snd_soc_codec *codec, + unsigned int new_page) +{ + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + u8 data[2]; + int ret; + + data[0] = 0x00; + data[1] = new_page & 0xff; + + ret = codec->hw_write(codec->control_data, data, 2); + if (ret == 2) { + aic32x4->page_no = new_page; + return 0; + } else { + return ret; + } +} + +static int aic32x4_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int val) +{ + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + unsigned int page = reg / 128; + unsigned int fixed_reg = reg % 128; + u8 data[2]; + int ret; + + /* A write to AIC32X4_PSEL is really a non-explicit page change */ + if (reg == AIC32X4_PSEL) + return aic32x4_change_page(codec, val); + + if (aic32x4->page_no != page) { + ret = aic32x4_change_page(codec, page); + if (ret != 0) + return ret; + } + + data[0] = fixed_reg & 0xff; + data[1] = val & 0xff; + + if (codec->hw_write(codec->control_data, data, 2) == 2) + return 0; + else + return -EIO; +} + +static unsigned int aic32x4_read(struct snd_soc_codec *codec, unsigned int reg) +{ + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + unsigned int page = reg / 128; + unsigned int fixed_reg = reg % 128; + int ret; + + if (aic32x4->page_no != page) { + ret = aic32x4_change_page(codec, page); + if (ret != 0) + return ret; + } + return i2c_smbus_read_byte_data(codec->control_data, fixed_reg & 0xff); +} + +static inline int aic32x4_get_divs(int mclk, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(aic32x4_divs); i++) { + if ((aic32x4_divs[i].rate == rate) + && (aic32x4_divs[i].mclk == mclk)) { + return i; + } + } + printk(KERN_ERR "aic32x4: master clock and sample rate is not supported\n"); + return -EINVAL; +} + +static int aic32x4_add_widgets(struct snd_soc_codec *codec) +{ + snd_soc_dapm_new_controls(&codec->dapm, aic32x4_dapm_widgets, + ARRAY_SIZE(aic32x4_dapm_widgets)); + + snd_soc_dapm_add_routes(&codec->dapm, aic32x4_dapm_routes, + ARRAY_SIZE(aic32x4_dapm_routes)); + + snd_soc_dapm_new_widgets(&codec->dapm); + return 0; +} + +static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + + switch (freq) { + case AIC32X4_FREQ_12000000: + case AIC32X4_FREQ_24000000: + case AIC32X4_FREQ_25000000: + aic32x4->sysclk = freq; + return 0; + } + printk(KERN_ERR "aic32x4: invalid frequency to set DAI system clock\n"); + return -EINVAL; +} + +static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + u8 iface_reg_1; + u8 iface_reg_2; + u8 iface_reg_3; + + iface_reg_1 = snd_soc_read(codec, AIC32X4_IFACE1); + iface_reg_1 = iface_reg_1 & ~(3 << 6 | 3 << 2); + iface_reg_2 = snd_soc_read(codec, AIC32X4_IFACE2); + iface_reg_2 = 0; + iface_reg_3 = snd_soc_read(codec, AIC32X4_IFACE3); + iface_reg_3 = iface_reg_3 & ~(1 << 3); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + aic32x4->master = 1; + iface_reg_1 |= AIC32X4_BCLKMASTER | AIC32X4_WCLKMASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + aic32x4->master = 0; + break; + default: + printk(KERN_ERR "aic32x4: invalid DAI master/slave interface\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_DSP_A: + iface_reg_1 |= (AIC32X4_DSP_MODE << AIC32X4_PLLJ_SHIFT); + iface_reg_3 |= (1 << 3); /* invert bit clock */ + iface_reg_2 = 0x01; /* add offset 1 */ + break; + case SND_SOC_DAIFMT_DSP_B: + iface_reg_1 |= (AIC32X4_DSP_MODE << AIC32X4_PLLJ_SHIFT); + iface_reg_3 |= (1 << 3); /* invert bit clock */ + break; + case SND_SOC_DAIFMT_RIGHT_J: + iface_reg_1 |= + (AIC32X4_RIGHT_JUSTIFIED_MODE << AIC32X4_PLLJ_SHIFT); + break; + case SND_SOC_DAIFMT_LEFT_J: + iface_reg_1 |= + (AIC32X4_LEFT_JUSTIFIED_MODE << AIC32X4_PLLJ_SHIFT); + break; + default: + printk(KERN_ERR "aic32x4: invalid DAI interface format\n"); + return -EINVAL; + } + + snd_soc_write(codec, AIC32X4_IFACE1, iface_reg_1); + snd_soc_write(codec, AIC32X4_IFACE2, iface_reg_2); + snd_soc_write(codec, AIC32X4_IFACE3, iface_reg_3); + return 0; +} + +static int aic32x4_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + u8 data; + int i; + + i = aic32x4_get_divs(aic32x4->sysclk, params_rate(params)); + if (i < 0) { + printk(KERN_ERR "aic32x4: sampling rate not supported\n"); + return i; + } + + /* Use PLL as CODEC_CLKIN and DAC_MOD_CLK as BDIV_CLKIN */ + snd_soc_write(codec, AIC32X4_CLKMUX, AIC32X4_PLLCLKIN); + snd_soc_write(codec, AIC32X4_IFACE3, AIC32X4_DACMOD2BCLK); + + /* We will fix R value to 1 and will make P & J=K.D as varialble */ + data = snd_soc_read(codec, AIC32X4_PLLPR); + data &= ~(7 << 4); + snd_soc_write(codec, AIC32X4_PLLPR, + (data | (aic32x4_divs[i].p_val << 4) | 0x01)); + + snd_soc_write(codec, AIC32X4_PLLJ, aic32x4_divs[i].pll_j); + + snd_soc_write(codec, AIC32X4_PLLDMSB, (aic32x4_divs[i].pll_d >> 8)); + snd_soc_write(codec, AIC32X4_PLLDLSB, + (aic32x4_divs[i].pll_d & 0xff)); + + /* NDAC divider value */ + data = snd_soc_read(codec, AIC32X4_NDAC); + data &= ~(0x7f); + snd_soc_write(codec, AIC32X4_NDAC, data | aic32x4_divs[i].ndac); + + /* MDAC divider value */ + data = snd_soc_read(codec, AIC32X4_MDAC); + data &= ~(0x7f); + snd_soc_write(codec, AIC32X4_MDAC, data | aic32x4_divs[i].mdac); + + /* DOSR MSB & LSB values */ + snd_soc_write(codec, AIC32X4_DOSRMSB, aic32x4_divs[i].dosr >> 8); + snd_soc_write(codec, AIC32X4_DOSRLSB, + (aic32x4_divs[i].dosr & 0xff)); + + /* NADC divider value */ + data = snd_soc_read(codec, AIC32X4_NADC); + data &= ~(0x7f); + snd_soc_write(codec, AIC32X4_NADC, data | aic32x4_divs[i].nadc); + + /* MADC divider value */ + data = snd_soc_read(codec, AIC32X4_MADC); + data &= ~(0x7f); + snd_soc_write(codec, AIC32X4_MADC, data | aic32x4_divs[i].madc); + + /* AOSR value */ + snd_soc_write(codec, AIC32X4_AOSR, aic32x4_divs[i].aosr); + + /* BCLK N divider */ + data = snd_soc_read(codec, AIC32X4_BCLKN); + data &= ~(0x7f); + snd_soc_write(codec, AIC32X4_BCLKN, data | aic32x4_divs[i].blck_N); + + data = snd_soc_read(codec, AIC32X4_IFACE1); + data = data & ~(3 << 4); + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + data |= (AIC32X4_WORD_LEN_20BITS << AIC32X4_DOSRMSB_SHIFT); + break; + case SNDRV_PCM_FORMAT_S24_LE: + data |= (AIC32X4_WORD_LEN_24BITS << AIC32X4_DOSRMSB_SHIFT); + break; + case SNDRV_PCM_FORMAT_S32_LE: + data |= (AIC32X4_WORD_LEN_32BITS << AIC32X4_DOSRMSB_SHIFT); + break; + } + snd_soc_write(codec, AIC32X4_IFACE1, data); + + return 0; +} + +static int aic32x4_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + u8 dac_reg; + + dac_reg = snd_soc_read(codec, AIC32X4_DACMUTE) & ~AIC32X4_MUTEON; + if (mute) + snd_soc_write(codec, AIC32X4_DACMUTE, dac_reg | AIC32X4_MUTEON); + else + snd_soc_write(codec, AIC32X4_DACMUTE, dac_reg); + return 0; +} + +static int aic32x4_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + u8 value; + + switch (level) { + case SND_SOC_BIAS_ON: + if (aic32x4->master) { + /* Switch on PLL */ + value = snd_soc_read(codec, AIC32X4_PLLPR); + snd_soc_write(codec, AIC32X4_PLLPR, + (value | AIC32X4_PLLEN)); + + /* Switch on NDAC Divider */ + value = snd_soc_read(codec, AIC32X4_NDAC); + snd_soc_write(codec, AIC32X4_NDAC, + value | AIC32X4_NDACEN); + + /* Switch on MDAC Divider */ + value = snd_soc_read(codec, AIC32X4_MDAC); + snd_soc_write(codec, AIC32X4_MDAC, + value | AIC32X4_MDACEN); + + /* Switch on NADC Divider */ + value = snd_soc_read(codec, AIC32X4_NADC); + snd_soc_write(codec, AIC32X4_NADC, + value | AIC32X4_MDACEN); + + /* Switch on MADC Divider */ + value = snd_soc_read(codec, AIC32X4_MADC); + snd_soc_write(codec, AIC32X4_MADC, + value | AIC32X4_MDACEN); + + /* Switch on BCLK_N Divider */ + value = snd_soc_read(codec, AIC32X4_BCLKN); + snd_soc_write(codec, AIC32X4_BCLKN, + value | AIC32X4_BCLKEN); + } + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (aic32x4->master) { + /* Switch off PLL */ + value = snd_soc_read(codec, AIC32X4_PLLPR); + snd_soc_write(codec, AIC32X4_PLLPR, + (value & ~AIC32X4_PLLEN)); + + /* Switch off NDAC Divider */ + value = snd_soc_read(codec, AIC32X4_NDAC); + snd_soc_write(codec, AIC32X4_NDAC, + value & ~AIC32X4_NDACEN); + + /* Switch off MDAC Divider */ + value = snd_soc_read(codec, AIC32X4_MDAC); + snd_soc_write(codec, AIC32X4_MDAC, + value & ~AIC32X4_MDACEN); + + /* Switch off NADC Divider */ + value = snd_soc_read(codec, AIC32X4_NADC); + snd_soc_write(codec, AIC32X4_NADC, + value & ~AIC32X4_NDACEN); + + /* Switch off MADC Divider */ + value = snd_soc_read(codec, AIC32X4_MADC); + snd_soc_write(codec, AIC32X4_MADC, + value & ~AIC32X4_MDACEN); + value = snd_soc_read(codec, AIC32X4_BCLKN); + + /* Switch off BCLK_N Divider */ + snd_soc_write(codec, AIC32X4_BCLKN, + value & ~AIC32X4_BCLKEN); + } + break; + case SND_SOC_BIAS_OFF: + break; + } + codec->dapm.bias_level = level; + return 0; +} + +#define AIC32X4_RATES SNDRV_PCM_RATE_8000_48000 +#define AIC32X4_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ + | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_ops aic32x4_ops = { + .hw_params = aic32x4_hw_params, + .digital_mute = aic32x4_mute, + .set_fmt = aic32x4_set_dai_fmt, + .set_sysclk = aic32x4_set_dai_sysclk, +}; + +static struct snd_soc_dai_driver aic32x4_dai = { + .name = "tlv320aic32x4-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AIC32X4_RATES, + .formats = AIC32X4_FORMATS,}, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AIC32X4_RATES, + .formats = AIC32X4_FORMATS,}, + .ops = &aic32x4_ops, + .symmetric_rates = 1, +}; + +static int aic32x4_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ + aic32x4_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int aic32x4_resume(struct snd_soc_codec *codec) +{ + aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + return 0; +} + +static int aic32x4_probe(struct snd_soc_codec *codec) +{ + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + u32 tmp_reg; + + codec->hw_write = (hw_write_t) i2c_master_send; + codec->control_data = aic32x4->control_data; + + snd_soc_write(codec, AIC32X4_RESET, 0x01); + + /* Power platform configuration */ + if (aic32x4->power_cfg & AIC32X4_PWR_MICBIAS_2075_LDOIN) { + snd_soc_write(codec, AIC32X4_MICBIAS, AIC32X4_MICBIAS_LDOIN | + AIC32X4_MICBIAS_2075V); + } + if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE) { + snd_soc_write(codec, AIC32X4_PWRCFG, AIC32X4_AVDDWEAKDISABLE); + } + if (aic32x4->power_cfg & AIC32X4_PWR_AIC32X4_LDO_ENABLE) { + snd_soc_write(codec, AIC32X4_LDOCTL, AIC32X4_LDOCTLEN); + } + tmp_reg = snd_soc_read(codec, AIC32X4_CMMODE); + if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36) { + tmp_reg |= AIC32X4_LDOIN_18_36; + } + if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED) { + tmp_reg |= AIC32X4_LDOIN2HP; + } + snd_soc_write(codec, AIC32X4_CMMODE, tmp_reg); + + /* Do DACs need to be swapped? */ + if (aic32x4->swapdacs) { + snd_soc_write(codec, AIC32X4_DACSETUP, AIC32X4_LDAC2RCHN | AIC32X4_RDAC2LCHN); + } else { + snd_soc_write(codec, AIC32X4_DACSETUP, AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN); + } + + /* Mic PGA routing */ + if (aic32x4->micpga_routing | AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K) { + snd_soc_write(codec, AIC32X4_LMICPGANIN, AIC32X4_LMICPGANIN_IN2R_10K); + } + if (aic32x4->micpga_routing | AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K) { + snd_soc_write(codec, AIC32X4_RMICPGANIN, AIC32X4_RMICPGANIN_IN1L_10K); + } + + aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_add_controls(codec, aic32x4_snd_controls, + ARRAY_SIZE(aic32x4_snd_controls)); + aic32x4_add_widgets(codec); + + return 0; +} + +static int aic32x4_remove(struct snd_soc_codec *codec) +{ + aic32x4_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = { + .read = aic32x4_read, + .write = aic32x4_write, + .probe = aic32x4_probe, + .remove = aic32x4_remove, + .suspend = aic32x4_suspend, + .resume = aic32x4_resume, + .set_bias_level = aic32x4_set_bias_level, +}; + +static __devinit int aic32x4_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct aic32x4_pdata *pdata = i2c->dev.platform_data; + struct aic32x4_priv *aic32x4; + int ret; + + aic32x4 = kzalloc(sizeof(struct aic32x4_priv), GFP_KERNEL); + if (aic32x4 == NULL) + return -ENOMEM; + + aic32x4->control_data = i2c; + i2c_set_clientdata(i2c, aic32x4); + + if (pdata) { + aic32x4->power_cfg = pdata->power_cfg; + aic32x4->swapdacs = pdata->swapdacs; + aic32x4->micpga_routing = pdata->micpga_routing; + } else { + aic32x4->power_cfg = 0; + aic32x4->swapdacs = false; + aic32x4->micpga_routing = 0; + } + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_aic32x4, &aic32x4_dai, 1); + if (ret < 0) + kfree(aic32x4); + return ret; +} + +static __devexit int aic32x4_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + kfree(i2c_get_clientdata(client)); + return 0; +} + +static const struct i2c_device_id aic32x4_i2c_id[] = { + { "tlv320aic32x4", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aic32x4_i2c_id); + +static struct i2c_driver aic32x4_i2c_driver = { + .driver = { + .name = "tlv320aic32x4", + .owner = THIS_MODULE, + }, + .probe = aic32x4_i2c_probe, + .remove = __devexit_p(aic32x4_i2c_remove), + .id_table = aic32x4_i2c_id, +}; + +static int __init aic32x4_modinit(void) +{ + int ret = 0; + + ret = i2c_add_driver(&aic32x4_i2c_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register aic32x4 I2C driver: %d\n", + ret); + } + return ret; +} +module_init(aic32x4_modinit); + +static void __exit aic32x4_exit(void) +{ + i2c_del_driver(&aic32x4_i2c_driver); +} +module_exit(aic32x4_exit); + +MODULE_DESCRIPTION("ASoC tlv320aic32x4 codec driver"); +MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h new file mode 100644 index 000000000000..aae2b2440398 --- /dev/null +++ b/sound/soc/codecs/tlv320aic32x4.h @@ -0,0 +1,143 @@ +/* + * tlv320aic32x4.h + * + * 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 _TLV320AIC32X4_H +#define _TLV320AIC32X4_H + +/* tlv320aic32x4 register space (in decimal to match datasheet) */ + +#define AIC32X4_PAGE1 128 + +#define AIC32X4_PSEL 0 +#define AIC32X4_RESET 1 +#define AIC32X4_CLKMUX 4 +#define AIC32X4_PLLPR 5 +#define AIC32X4_PLLJ 6 +#define AIC32X4_PLLDMSB 7 +#define AIC32X4_PLLDLSB 8 +#define AIC32X4_NDAC 11 +#define AIC32X4_MDAC 12 +#define AIC32X4_DOSRMSB 13 +#define AIC32X4_DOSRLSB 14 +#define AIC32X4_NADC 18 +#define AIC32X4_MADC 19 +#define AIC32X4_AOSR 20 +#define AIC32X4_CLKMUX2 25 +#define AIC32X4_CLKOUTM 26 +#define AIC32X4_IFACE1 27 +#define AIC32X4_IFACE2 28 +#define AIC32X4_IFACE3 29 +#define AIC32X4_BCLKN 30 +#define AIC32X4_IFACE4 31 +#define AIC32X4_IFACE5 32 +#define AIC32X4_IFACE6 33 +#define AIC32X4_DOUTCTL 53 +#define AIC32X4_DINCTL 54 +#define AIC32X4_DACSPB 60 +#define AIC32X4_ADCSPB 61 +#define AIC32X4_DACSETUP 63 +#define AIC32X4_DACMUTE 64 +#define AIC32X4_LDACVOL 65 +#define AIC32X4_RDACVOL 66 +#define AIC32X4_ADCSETUP 81 +#define AIC32X4_ADCFGA 82 +#define AIC32X4_LADCVOL 83 +#define AIC32X4_RADCVOL 84 +#define AIC32X4_LAGC1 86 +#define AIC32X4_LAGC2 87 +#define AIC32X4_LAGC3 88 +#define AIC32X4_LAGC4 89 +#define AIC32X4_LAGC5 90 +#define AIC32X4_LAGC6 91 +#define AIC32X4_LAGC7 92 +#define AIC32X4_RAGC1 94 +#define AIC32X4_RAGC2 95 +#define AIC32X4_RAGC3 96 +#define AIC32X4_RAGC4 97 +#define AIC32X4_RAGC5 98 +#define AIC32X4_RAGC6 99 +#define AIC32X4_RAGC7 100 +#define AIC32X4_PWRCFG (AIC32X4_PAGE1 + 1) +#define AIC32X4_LDOCTL (AIC32X4_PAGE1 + 2) +#define AIC32X4_OUTPWRCTL (AIC32X4_PAGE1 + 9) +#define AIC32X4_CMMODE (AIC32X4_PAGE1 + 10) +#define AIC32X4_HPLROUTE (AIC32X4_PAGE1 + 12) +#define AIC32X4_HPRROUTE (AIC32X4_PAGE1 + 13) +#define AIC32X4_LOLROUTE (AIC32X4_PAGE1 + 14) +#define AIC32X4_LORROUTE (AIC32X4_PAGE1 + 15) +#define AIC32X4_HPLGAIN (AIC32X4_PAGE1 + 16) +#define AIC32X4_HPRGAIN (AIC32X4_PAGE1 + 17) +#define AIC32X4_LOLGAIN (AIC32X4_PAGE1 + 18) +#define AIC32X4_LORGAIN (AIC32X4_PAGE1 + 19) +#define AIC32X4_HEADSTART (AIC32X4_PAGE1 + 20) +#define AIC32X4_MICBIAS (AIC32X4_PAGE1 + 51) +#define AIC32X4_LMICPGAPIN (AIC32X4_PAGE1 + 52) +#define AIC32X4_LMICPGANIN (AIC32X4_PAGE1 + 54) +#define AIC32X4_RMICPGAPIN (AIC32X4_PAGE1 + 55) +#define AIC32X4_RMICPGANIN (AIC32X4_PAGE1 + 57) +#define AIC32X4_FLOATINGINPUT (AIC32X4_PAGE1 + 58) +#define AIC32X4_LMICPGAVOL (AIC32X4_PAGE1 + 59) +#define AIC32X4_RMICPGAVOL (AIC32X4_PAGE1 + 60) + +#define AIC32X4_FREQ_12000000 12000000 +#define AIC32X4_FREQ_24000000 24000000 +#define AIC32X4_FREQ_25000000 25000000 + +#define AIC32X4_WORD_LEN_16BITS 0x00 +#define AIC32X4_WORD_LEN_20BITS 0x01 +#define AIC32X4_WORD_LEN_24BITS 0x02 +#define AIC32X4_WORD_LEN_32BITS 0x03 + +#define AIC32X4_I2S_MODE 0x00 +#define AIC32X4_DSP_MODE 0x01 +#define AIC32X4_RIGHT_JUSTIFIED_MODE 0x02 +#define AIC32X4_LEFT_JUSTIFIED_MODE 0x03 + +#define AIC32X4_AVDDWEAKDISABLE 0x08 +#define AIC32X4_LDOCTLEN 0x01 + +#define AIC32X4_LDOIN_18_36 0x01 +#define AIC32X4_LDOIN2HP 0x02 + +#define AIC32X4_DACSPBLOCK_MASK 0x1f +#define AIC32X4_ADCSPBLOCK_MASK 0x1f + +#define AIC32X4_PLLJ_SHIFT 6 +#define AIC32X4_DOSRMSB_SHIFT 4 + +#define AIC32X4_PLLCLKIN 0x03 + +#define AIC32X4_MICBIAS_LDOIN 0x08 +#define AIC32X4_MICBIAS_2075V 0x60 + +#define AIC32X4_LMICPGANIN_IN2R_10K 0x10 +#define AIC32X4_RMICPGANIN_IN1L_10K 0x10 + +#define AIC32X4_LMICPGAVOL_NOGAIN 0x80 +#define AIC32X4_RMICPGAVOL_NOGAIN 0x80 + +#define AIC32X4_BCLKMASTER 0x08 +#define AIC32X4_WCLKMASTER 0x04 +#define AIC32X4_PLLEN (0x01 << 7) +#define AIC32X4_NDACEN (0x01 << 7) +#define AIC32X4_MDACEN (0x01 << 7) +#define AIC32X4_NADCEN (0x01 << 7) +#define AIC32X4_MADCEN (0x01 << 7) +#define AIC32X4_BCLKEN (0x01 << 7) +#define AIC32X4_DACEN (0x03 << 6) +#define AIC32X4_RDAC2LCHN (0x02 << 2) +#define AIC32X4_LDAC2RCHN (0x02 << 4) +#define AIC32X4_LDAC2LCHN (0x01 << 4) +#define AIC32X4_RDAC2RCHN (0x01 << 2) + +#define AIC32X4_SSTEP2WCLK 0x01 +#define AIC32X4_MUTEON 0x0C +#define AIC32X4_DACMOD2BCLK 0x01 + +#endif /* _TLV320AIC32X4_H */ diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 71d7be8ac488..00b6d87e7bdb 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -1615,6 +1615,7 @@ static const struct i2c_device_id tlv320dac33_i2c_id[] = { }, { }, }; +MODULE_DEVICE_TABLE(i2c, tlv320dac33_i2c_id); static struct i2c_driver tlv320dac33_i2c_driver = { .driver = { diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 4bbf1b15a493..482fcdb59bfa 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -724,8 +724,8 @@ static int twl6040_power_mode_event(struct snd_soc_dapm_widget *w, return 0; } -void twl6040_hs_jack_report(struct snd_soc_codec *codec, - struct snd_soc_jack *jack, int report) +static void twl6040_hs_jack_report(struct snd_soc_codec *codec, + struct snd_soc_jack *jack, int report) { struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); int status; diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 80ddf4fd23db..a3b9cbb20ee9 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -836,24 +836,25 @@ static void wm2000_i2c_shutdown(struct i2c_client *i2c) } #ifdef CONFIG_PM -static int wm2000_i2c_suspend(struct i2c_client *i2c, pm_message_t mesg) +static int wm2000_i2c_suspend(struct device *dev) { + struct i2c_client *i2c = to_i2c_client(dev); struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); return wm2000_anc_transition(wm2000, ANC_OFF); } -static int wm2000_i2c_resume(struct i2c_client *i2c) +static int wm2000_i2c_resume(struct device *dev) { + struct i2c_client *i2c = to_i2c_client(dev); struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); return wm2000_anc_set_mode(wm2000); } -#else -#define wm2000_i2c_suspend NULL -#define wm2000_i2c_resume NULL #endif +static SIMPLE_DEV_PM_OPS(wm2000_pm, wm2000_i2c_suspend, wm2000_i2c_resume); + static const struct i2c_device_id wm2000_i2c_id[] = { { "wm2000", 0 }, { } @@ -864,11 +865,10 @@ static struct i2c_driver wm2000_i2c_driver = { .driver = { .name = "wm2000", .owner = THIS_MODULE, + .pm = &wm2000_pm, }, .probe = wm2000_i2c_probe, .remove = __devexit_p(wm2000_i2c_remove), - .suspend = wm2000_i2c_suspend, - .resume = wm2000_i2c_resume, .shutdown = wm2000_i2c_shutdown, .id_table = wm2000_i2c_id, }; diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index 5eb2f501ce32..4fd4d8dca0fc 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c @@ -58,7 +58,7 @@ static const u16 wm8523_reg[WM8523_REGISTER_COUNT] = { 0x0000, /* R8 - ZERO_DETECT */ }; -static int wm8523_volatile_register(unsigned int reg) +static int wm8523_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8523_DEVICE_ID: @@ -414,7 +414,6 @@ static int wm8523_resume(struct snd_soc_codec *codec) static int wm8523_probe(struct snd_soc_codec *codec) { struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec); - u16 *reg_cache = codec->reg_cache; int ret, i; codec->hw_write = (hw_write_t)i2c_master_send; @@ -471,8 +470,9 @@ static int wm8523_probe(struct snd_soc_codec *codec) } /* Change some default settings - latch VU and enable ZC */ - reg_cache[WM8523_DAC_GAINR] |= WM8523_DACR_VU; - reg_cache[WM8523_DAC_CTRL3] |= WM8523_ZC; + snd_soc_update_bits(codec, WM8523_DAC_GAINR, + WM8523_DACR_VU, WM8523_DACR_VU); + snd_soc_update_bits(codec, WM8523_DAC_CTRL3, WM8523_ZC, WM8523_ZC); wm8523_set_bias_level(codec, SND_SOC_BIAS_STANDBY); diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index 494f2d31d75b..25af901fe813 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -421,7 +421,6 @@ static int wm8741_resume(struct snd_soc_codec *codec) static int wm8741_probe(struct snd_soc_codec *codec) { struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); - u16 *reg_cache = codec->reg_cache; int ret = 0; ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8741->control_type); @@ -437,10 +436,14 @@ static int wm8741_probe(struct snd_soc_codec *codec) } /* Change some default settings - latch VU */ - reg_cache[WM8741_DACLLSB_ATTENUATION] |= WM8741_UPDATELL; - reg_cache[WM8741_DACLMSB_ATTENUATION] |= WM8741_UPDATELM; - reg_cache[WM8741_DACRLSB_ATTENUATION] |= WM8741_UPDATERL; - reg_cache[WM8741_DACRLSB_ATTENUATION] |= WM8741_UPDATERM; + snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION, + WM8741_UPDATELL, WM8741_UPDATELL); + snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION, + WM8741_UPDATELM, WM8741_UPDATELM); + snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION, + WM8741_UPDATERL, WM8741_UPDATERL); + snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION, + WM8741_UPDATERM, WM8741_UPDATERM); snd_soc_add_controls(codec, wm8741_snd_controls, ARRAY_SIZE(wm8741_snd_controls)); diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index 79b02ae125c5..3f09deea8d9d 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -55,8 +55,10 @@ static int caps_charge = 2000; module_param(caps_charge, int, 0); MODULE_PARM_DESC(caps_charge, "WM8753 cap charge time (msecs)"); -static void wm8753_set_dai_mode(struct snd_soc_codec *codec, - struct snd_soc_dai *dai, unsigned int hifi); +static int wm8753_hifi_write_dai_fmt(struct snd_soc_codec *codec, + unsigned int fmt); +static int wm8753_voice_write_dai_fmt(struct snd_soc_codec *codec, + unsigned int fmt); /* * wm8753 register cache @@ -87,6 +89,10 @@ struct wm8753_priv { enum snd_soc_control_type control_type; unsigned int sysclk; unsigned int pcmclk; + + unsigned int voice_fmt; + unsigned int hifi_fmt; + int dai_func; }; @@ -170,9 +176,9 @@ static int wm8753_get_dai(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - int mode = snd_soc_read(codec, WM8753_IOCTL); + struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); - ucontrol->value.integer.value[0] = (mode & 0xc) >> 2; + ucontrol->value.integer.value[0] = wm8753->dai_func; return 0; } @@ -180,16 +186,26 @@ static int wm8753_set_dai(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - int mode = snd_soc_read(codec, WM8753_IOCTL); struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); + u16 ioctl; + + if (codec->active) + return -EBUSY; + + ioctl = snd_soc_read(codec, WM8753_IOCTL); + + wm8753->dai_func = ucontrol->value.integer.value[0]; + + if (((ioctl >> 2) & 0x3) == wm8753->dai_func) + return 1; + + ioctl = (ioctl & 0x1f3) | (wm8753->dai_func << 2); + snd_soc_write(codec, WM8753_IOCTL, ioctl); - if (((mode & 0xc) >> 2) == ucontrol->value.integer.value[0]) - return 0; - mode &= 0xfff3; - mode |= (ucontrol->value.integer.value[0] << 2); + wm8753_hifi_write_dai_fmt(codec, wm8753->hifi_fmt); + wm8753_voice_write_dai_fmt(codec, wm8753->voice_fmt); - wm8753->dai_func = ucontrol->value.integer.value[0]; return 1; } @@ -828,10 +844,9 @@ static int wm8753_set_dai_sysclk(struct snd_soc_dai *codec_dai, /* * Set's ADC and Voice DAC format. */ -static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_dai *codec_dai, +static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_codec *codec, unsigned int fmt) { - struct snd_soc_codec *codec = codec_dai->codec; u16 voice = snd_soc_read(codec, WM8753_PCM) & 0x01ec; /* interface format */ @@ -858,13 +873,6 @@ static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_dai *codec_dai, return 0; } -static int wm8753_pcm_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - wm8753_set_dai_mode(dai->codec, dai, 0); - return 0; -} - /* * Set PCM DAI bit size and sample rate. */ @@ -905,10 +913,9 @@ static int wm8753_pcm_hw_params(struct snd_pcm_substream *substream, /* * Set's PCM dai fmt and BCLK. */ -static int wm8753_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai, +static int wm8753_pcm_set_dai_fmt(struct snd_soc_codec *codec, unsigned int fmt) { - struct snd_soc_codec *codec = codec_dai->codec; u16 voice, ioctl; voice = snd_soc_read(codec, WM8753_PCM) & 0x011f; @@ -999,10 +1006,9 @@ static int wm8753_set_dai_clkdiv(struct snd_soc_dai *codec_dai, /* * Set's HiFi DAC format. */ -static int wm8753_hdac_set_dai_fmt(struct snd_soc_dai *codec_dai, +static int wm8753_hdac_set_dai_fmt(struct snd_soc_codec *codec, unsigned int fmt) { - struct snd_soc_codec *codec = codec_dai->codec; u16 hifi = snd_soc_read(codec, WM8753_HIFI) & 0x01e0; /* interface format */ @@ -1032,10 +1038,9 @@ static int wm8753_hdac_set_dai_fmt(struct snd_soc_dai *codec_dai, /* * Set's I2S DAI format. */ -static int wm8753_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai, +static int wm8753_i2s_set_dai_fmt(struct snd_soc_codec *codec, unsigned int fmt) { - struct snd_soc_codec *codec = codec_dai->codec; u16 ioctl, hifi; hifi = snd_soc_read(codec, WM8753_HIFI) & 0x011f; @@ -1098,13 +1103,6 @@ static int wm8753_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai, return 0; } -static int wm8753_i2s_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - wm8753_set_dai_mode(dai->codec, dai, 1); - return 0; -} - /* * Set PCM DAI bit size and sample rate. */ @@ -1147,61 +1145,117 @@ static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream, return 0; } -static int wm8753_mode1v_set_dai_fmt(struct snd_soc_dai *codec_dai, +static int wm8753_mode1v_set_dai_fmt(struct snd_soc_codec *codec, unsigned int fmt) { - struct snd_soc_codec *codec = codec_dai->codec; u16 clock; /* set clk source as pcmclk */ clock = snd_soc_read(codec, WM8753_CLOCK) & 0xfffb; snd_soc_write(codec, WM8753_CLOCK, clock); - if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0) - return -EINVAL; - return wm8753_pcm_set_dai_fmt(codec_dai, fmt); + return wm8753_vdac_adc_set_dai_fmt(codec, fmt); } -static int wm8753_mode1h_set_dai_fmt(struct snd_soc_dai *codec_dai, +static int wm8753_mode1h_set_dai_fmt(struct snd_soc_codec *codec, unsigned int fmt) { - if (wm8753_hdac_set_dai_fmt(codec_dai, fmt) < 0) - return -EINVAL; - return wm8753_i2s_set_dai_fmt(codec_dai, fmt); + return wm8753_hdac_set_dai_fmt(codec, fmt); } -static int wm8753_mode2_set_dai_fmt(struct snd_soc_dai *codec_dai, +static int wm8753_mode2_set_dai_fmt(struct snd_soc_codec *codec, unsigned int fmt) { - struct snd_soc_codec *codec = codec_dai->codec; u16 clock; /* set clk source as pcmclk */ clock = snd_soc_read(codec, WM8753_CLOCK) & 0xfffb; snd_soc_write(codec, WM8753_CLOCK, clock); - if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0) - return -EINVAL; - return wm8753_i2s_set_dai_fmt(codec_dai, fmt); + return wm8753_vdac_adc_set_dai_fmt(codec, fmt); } -static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_dai *codec_dai, +static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_codec *codec, unsigned int fmt) { - struct snd_soc_codec *codec = codec_dai->codec; u16 clock; /* set clk source as mclk */ clock = snd_soc_read(codec, WM8753_CLOCK) & 0xfffb; snd_soc_write(codec, WM8753_CLOCK, clock | 0x4); - if (wm8753_hdac_set_dai_fmt(codec_dai, fmt) < 0) + if (wm8753_hdac_set_dai_fmt(codec, fmt) < 0) return -EINVAL; - if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0) - return -EINVAL; - return wm8753_i2s_set_dai_fmt(codec_dai, fmt); + return wm8753_vdac_adc_set_dai_fmt(codec, fmt); } +static int wm8753_hifi_write_dai_fmt(struct snd_soc_codec *codec, + unsigned int fmt) +{ + struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + switch (wm8753->dai_func) { + case 0: + ret = wm8753_mode1h_set_dai_fmt(codec, fmt); + break; + case 1: + ret = wm8753_mode2_set_dai_fmt(codec, fmt); + break; + case 2: + case 3: + ret = wm8753_mode3_4_set_dai_fmt(codec, fmt); + break; + default: + break; + } + if (ret) + return ret; + + return wm8753_i2s_set_dai_fmt(codec, fmt); +} + +static int wm8753_hifi_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); + + wm8753->hifi_fmt = fmt; + + return wm8753_hifi_write_dai_fmt(codec, fmt); +}; + +static int wm8753_voice_write_dai_fmt(struct snd_soc_codec *codec, + unsigned int fmt) +{ + struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + if (wm8753->dai_func != 0) + return 0; + + ret = wm8753_mode1v_set_dai_fmt(codec, fmt); + if (ret) + return ret; + ret = wm8753_pcm_set_dai_fmt(codec, fmt); + if (ret) + return ret; + + return 0; +}; + +static int wm8753_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); + + wm8753->voice_fmt = fmt; + + return wm8753_voice_write_dai_fmt(codec, fmt); +}; + static int wm8753_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; @@ -1268,57 +1322,25 @@ static int wm8753_set_bias_level(struct snd_soc_codec *codec, * 3. Voice disabled - HIFI over HIFI * 4. Voice disabled - HIFI over HIFI, uses voice DAI LRC for capture */ -static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode1 = { - .startup = wm8753_i2s_startup, +static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode = { .hw_params = wm8753_i2s_hw_params, .digital_mute = wm8753_mute, - .set_fmt = wm8753_mode1h_set_dai_fmt, - .set_clkdiv = wm8753_set_dai_clkdiv, - .set_pll = wm8753_set_dai_pll, - .set_sysclk = wm8753_set_dai_sysclk, -}; - -static struct snd_soc_dai_ops wm8753_dai_ops_voice_mode1 = { - .startup = wm8753_pcm_startup, - .hw_params = wm8753_pcm_hw_params, - .digital_mute = wm8753_mute, - .set_fmt = wm8753_mode1v_set_dai_fmt, + .set_fmt = wm8753_hifi_set_dai_fmt, .set_clkdiv = wm8753_set_dai_clkdiv, .set_pll = wm8753_set_dai_pll, .set_sysclk = wm8753_set_dai_sysclk, }; -static struct snd_soc_dai_ops wm8753_dai_ops_voice_mode2 = { - .startup = wm8753_pcm_startup, +static struct snd_soc_dai_ops wm8753_dai_ops_voice_mode = { .hw_params = wm8753_pcm_hw_params, .digital_mute = wm8753_mute, - .set_fmt = wm8753_mode2_set_dai_fmt, - .set_clkdiv = wm8753_set_dai_clkdiv, - .set_pll = wm8753_set_dai_pll, - .set_sysclk = wm8753_set_dai_sysclk, -}; - -static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode3 = { - .startup = wm8753_i2s_startup, - .hw_params = wm8753_i2s_hw_params, - .digital_mute = wm8753_mute, - .set_fmt = wm8753_mode3_4_set_dai_fmt, - .set_clkdiv = wm8753_set_dai_clkdiv, - .set_pll = wm8753_set_dai_pll, - .set_sysclk = wm8753_set_dai_sysclk, -}; - -static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode4 = { - .startup = wm8753_i2s_startup, - .hw_params = wm8753_i2s_hw_params, - .digital_mute = wm8753_mute, - .set_fmt = wm8753_mode3_4_set_dai_fmt, + .set_fmt = wm8753_voice_set_dai_fmt, .set_clkdiv = wm8753_set_dai_clkdiv, .set_pll = wm8753_set_dai_pll, .set_sysclk = wm8753_set_dai_sysclk, }; -static struct snd_soc_dai_driver wm8753_all_dai[] = { +static struct snd_soc_dai_driver wm8753_dai[] = { /* DAI HiFi mode 1 */ { .name = "wm8753-hifi", .playback = { @@ -1326,14 +1348,16 @@ static struct snd_soc_dai_driver wm8753_all_dai[] = { .channels_min = 1, .channels_max = 2, .rates = WM8753_RATES, - .formats = WM8753_FORMATS}, + .formats = WM8753_FORMATS + }, .capture = { /* dummy for fast DAI switching */ .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = WM8753_RATES, - .formats = WM8753_FORMATS}, - .ops = &wm8753_dai_ops_hifi_mode1, + .formats = WM8753_FORMATS + }, + .ops = &wm8753_dai_ops_hifi_mode, }, /* DAI Voice mode 1 */ { .name = "wm8753-voice", @@ -1342,97 +1366,19 @@ static struct snd_soc_dai_driver wm8753_all_dai[] = { .channels_min = 1, .channels_max = 1, .rates = WM8753_RATES, - .formats = WM8753_FORMATS,}, - .capture = { - .stream_name = "Capture", - .channels_min = 1, - .channels_max = 2, - .rates = WM8753_RATES, - .formats = WM8753_FORMATS,}, - .ops = &wm8753_dai_ops_voice_mode1, -}, -/* DAI HiFi mode 2 - dummy */ -{ .name = "wm8753-hifi", -}, -/* DAI Voice mode 2 */ -{ .name = "wm8753-voice", - .playback = { - .stream_name = "Voice Playback", - .channels_min = 1, - .channels_max = 1, - .rates = WM8753_RATES, - .formats = WM8753_FORMATS,}, - .capture = { - .stream_name = "Capture", - .channels_min = 1, - .channels_max = 2, - .rates = WM8753_RATES, - .formats = WM8753_FORMATS,}, - .ops = &wm8753_dai_ops_voice_mode2, -}, -/* DAI HiFi mode 3 */ -{ .name = "wm8753-hifi", - .playback = { - .stream_name = "HiFi Playback", - .channels_min = 1, - .channels_max = 2, - .rates = WM8753_RATES, - .formats = WM8753_FORMATS,}, - .capture = { - .stream_name = "Capture", - .channels_min = 1, - .channels_max = 2, - .rates = WM8753_RATES, - .formats = WM8753_FORMATS,}, - .ops = &wm8753_dai_ops_hifi_mode3, -}, -/* DAI Voice mode 3 - dummy */ -{ .name = "wm8753-voice", -}, -/* DAI HiFi mode 4 */ -{ .name = "wm8753-hifi", - .playback = { - .stream_name = "HiFi Playback", - .channels_min = 1, - .channels_max = 2, - .rates = WM8753_RATES, - .formats = WM8753_FORMATS,}, + .formats = WM8753_FORMATS, + }, .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = WM8753_RATES, - .formats = WM8753_FORMATS,}, - .ops = &wm8753_dai_ops_hifi_mode4, -}, -/* DAI Voice mode 4 - dummy */ -{ .name = "wm8753-voice", -}, -}; - -static struct snd_soc_dai_driver wm8753_dai[] = { - { - .name = "wm8753-aif0", - }, - { - .name = "wm8753-aif1", + .formats = WM8753_FORMATS, }, + .ops = &wm8753_dai_ops_voice_mode, +}, }; -static void wm8753_set_dai_mode(struct snd_soc_codec *codec, - struct snd_soc_dai *dai, unsigned int hifi) -{ - struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); - - if (wm8753->dai_func < 4) { - if (hifi) - dai->driver = &wm8753_all_dai[wm8753->dai_func << 1]; - else - dai->driver = &wm8753_all_dai[(wm8753->dai_func << 1) + 1]; - } - snd_soc_write(codec, WM8753_IOCTL, wm8753->dai_func); -} - static void wm8753_work(struct work_struct *work) { struct snd_soc_dapm_context *dapm = diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c index 6dae1b40c9f7..6785688f8806 100644 --- a/sound/soc/codecs/wm8804.c +++ b/sound/soc/codecs/wm8804.c @@ -175,7 +175,7 @@ static int txsrc_put(struct snd_kcontrol *kcontrol, return 0; } -static int wm8804_volatile(unsigned int reg) +static int wm8804_volatile(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8804_RST_DEVID1: diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index cd0959926d12..449ea09a193d 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -180,7 +180,7 @@ static const u16 wm8900_reg_defaults[WM8900_MAXREG] = { /* Remaining registers all zero */ }; -static int wm8900_volatile_register(unsigned int reg) +static int wm8900_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8900_REG_ID: diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 017d99ceb42e..ae1cadfae84c 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -2,6 +2,7 @@ * wm8903.c -- WM8903 ALSA SoC Audio driver * * Copyright 2008 Wolfson Microelectronics + * Copyright 2011 NVIDIA, Inc. * * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> * @@ -19,6 +20,7 @@ #include <linux/init.h> #include <linux/completion.h> #include <linux/delay.h> +#include <linux/gpio.h> #include <linux/pm.h> #include <linux/i2c.h> #include <linux/platform_device.h> @@ -213,6 +215,7 @@ static u16 wm8903_reg_defaults[] = { }; struct wm8903_priv { + struct snd_soc_codec *codec; int sysclk; int irq; @@ -220,25 +223,36 @@ struct wm8903_priv { int fs; int deemph; + int dcs_pending; + int dcs_cache[4]; + /* Reference count */ int class_w_users; - struct completion wseq; - struct snd_soc_jack *mic_jack; int mic_det; int mic_short; int mic_last_report; int mic_delay; + +#ifdef CONFIG_GPIOLIB + struct gpio_chip gpio_chip; +#endif }; -static int wm8903_volatile_register(unsigned int reg) +static int wm8903_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8903_SW_RESET_AND_ID: case WM8903_REVISION_NUMBER: case WM8903_INTERRUPT_STATUS_1: case WM8903_WRITE_SEQUENCER_4: + case WM8903_POWER_MANAGEMENT_3: + case WM8903_POWER_MANAGEMENT_2: + case WM8903_DC_SERVO_READBACK_1: + case WM8903_DC_SERVO_READBACK_2: + case WM8903_DC_SERVO_READBACK_3: + case WM8903_DC_SERVO_READBACK_4: return 1; default: @@ -246,50 +260,6 @@ static int wm8903_volatile_register(unsigned int reg) } } -static int wm8903_run_sequence(struct snd_soc_codec *codec, unsigned int start) -{ - u16 reg[5]; - struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); - - BUG_ON(start > 48); - - /* Enable the sequencer if it's not already on */ - reg[0] = snd_soc_read(codec, WM8903_WRITE_SEQUENCER_0); - snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0, - reg[0] | WM8903_WSEQ_ENA); - - dev_dbg(codec->dev, "Starting sequence at %d\n", start); - - snd_soc_write(codec, WM8903_WRITE_SEQUENCER_3, - start | WM8903_WSEQ_START); - - /* Wait for it to complete. If we have the interrupt wired up then - * that will break us out of the poll early. - */ - do { - wait_for_completion_timeout(&wm8903->wseq, - msecs_to_jiffies(10)); - - reg[4] = snd_soc_read(codec, WM8903_WRITE_SEQUENCER_4); - } while (reg[4] & WM8903_WSEQ_BUSY); - - dev_dbg(codec->dev, "Sequence complete\n"); - - /* Disable the sequencer again if we enabled it */ - snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0, reg[0]); - - return 0; -} - -static void wm8903_sync_reg_cache(struct snd_soc_codec *codec, u16 *cache) -{ - int i; - - /* There really ought to be something better we can do here :/ */ - for (i = 0; i < ARRAY_SIZE(wm8903_reg_defaults); i++) - cache[i] = codec->hw_read(codec, i); -} - static void wm8903_reset(struct snd_soc_codec *codec) { snd_soc_write(codec, WM8903_SW_RESET_AND_ID, 0); @@ -297,11 +267,6 @@ static void wm8903_reset(struct snd_soc_codec *codec) sizeof(wm8903_reg_defaults)); } -#define WM8903_OUTPUT_SHORT 0x8 -#define WM8903_OUTPUT_OUT 0x4 -#define WM8903_OUTPUT_INT 0x2 -#define WM8903_OUTPUT_IN 0x1 - static int wm8903_cp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -311,97 +276,101 @@ static int wm8903_cp_event(struct snd_soc_dapm_widget *w, return 0; } -/* - * Event for headphone and line out amplifier power changes. Special - * power up/down sequences are required in order to maximise pop/click - * performance. - */ -static int wm8903_output_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) +static int wm8903_dcs_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = w->codec; - u16 val; - u16 reg; - u16 dcs_reg; - u16 dcs_bit; - int shift; + struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); - switch (w->reg) { - case WM8903_POWER_MANAGEMENT_2: - reg = WM8903_ANALOGUE_HP_0; - dcs_bit = 0 + w->shift; + switch (event) { + case SND_SOC_DAPM_POST_PMU: + wm8903->dcs_pending |= 1 << w->shift; break; - case WM8903_POWER_MANAGEMENT_3: - reg = WM8903_ANALOGUE_LINEOUT_0; - dcs_bit = 2 + w->shift; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, WM8903_DC_SERVO_0, + 1 << w->shift, 0); break; - default: - BUG(); - return -EINVAL; /* Spurious warning from some compilers */ } - switch (w->shift) { - case 0: - shift = 0; - break; - case 1: - shift = 4; - break; - default: - BUG(); - return -EINVAL; /* Spurious warning from some compilers */ - } + return 0; +} - if (event & SND_SOC_DAPM_PRE_PMU) { - val = snd_soc_read(codec, reg); +#define WM8903_DCS_MODE_WRITE_STOP 0 +#define WM8903_DCS_MODE_START_STOP 2 - /* Short the output */ - val &= ~(WM8903_OUTPUT_SHORT << shift); - snd_soc_write(codec, reg, val); - } +static void wm8903_seq_notifier(struct snd_soc_dapm_context *dapm, + enum snd_soc_dapm_type event, int subseq) +{ + struct snd_soc_codec *codec = container_of(dapm, + struct snd_soc_codec, dapm); + struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); + int dcs_mode = WM8903_DCS_MODE_WRITE_STOP; + int i, val; - if (event & SND_SOC_DAPM_POST_PMU) { - val = snd_soc_read(codec, reg); + /* Complete any pending DC servo starts */ + if (wm8903->dcs_pending) { + dev_dbg(codec->dev, "Starting DC servo for %x\n", + wm8903->dcs_pending); - val |= (WM8903_OUTPUT_IN << shift); - snd_soc_write(codec, reg, val); + /* If we've no cached values then we need to do startup */ + for (i = 0; i < ARRAY_SIZE(wm8903->dcs_cache); i++) { + if (!(wm8903->dcs_pending & (1 << i))) + continue; - val |= (WM8903_OUTPUT_INT << shift); - snd_soc_write(codec, reg, val); + if (wm8903->dcs_cache[i]) { + dev_dbg(codec->dev, + "Restore DC servo %d value %x\n", + 3 - i, wm8903->dcs_cache[i]); + + snd_soc_write(codec, WM8903_DC_SERVO_4 + i, + wm8903->dcs_cache[i] & 0xff); + } else { + dev_dbg(codec->dev, + "Calibrate DC servo %d\n", 3 - i); + dcs_mode = WM8903_DCS_MODE_START_STOP; + } + } - /* Turn on the output ENA_OUTP */ - val |= (WM8903_OUTPUT_OUT << shift); - snd_soc_write(codec, reg, val); + /* Don't trust the cache for analogue */ + if (wm8903->class_w_users) + dcs_mode = WM8903_DCS_MODE_START_STOP; - /* Enable the DC servo */ - dcs_reg = snd_soc_read(codec, WM8903_DC_SERVO_0); - dcs_reg |= dcs_bit; - snd_soc_write(codec, WM8903_DC_SERVO_0, dcs_reg); + snd_soc_update_bits(codec, WM8903_DC_SERVO_2, + WM8903_DCS_MODE_MASK, dcs_mode); - /* Remove the short */ - val |= (WM8903_OUTPUT_SHORT << shift); - snd_soc_write(codec, reg, val); - } + snd_soc_update_bits(codec, WM8903_DC_SERVO_0, + WM8903_DCS_ENA_MASK, wm8903->dcs_pending); - if (event & SND_SOC_DAPM_PRE_PMD) { - val = snd_soc_read(codec, reg); + switch (dcs_mode) { + case WM8903_DCS_MODE_WRITE_STOP: + break; - /* Short the output */ - val &= ~(WM8903_OUTPUT_SHORT << shift); - snd_soc_write(codec, reg, val); + case WM8903_DCS_MODE_START_STOP: + msleep(270); - /* Disable the DC servo */ - dcs_reg = snd_soc_read(codec, WM8903_DC_SERVO_0); - dcs_reg &= ~dcs_bit; - snd_soc_write(codec, WM8903_DC_SERVO_0, dcs_reg); + /* Cache the measured offsets for digital */ + if (wm8903->class_w_users) + break; - /* Then disable the intermediate and output stages */ - val &= ~((WM8903_OUTPUT_OUT | WM8903_OUTPUT_INT | - WM8903_OUTPUT_IN) << shift); - snd_soc_write(codec, reg, val); - } + for (i = 0; i < ARRAY_SIZE(wm8903->dcs_cache); i++) { + if (!(wm8903->dcs_pending & (1 << i))) + continue; - return 0; + val = snd_soc_read(codec, + WM8903_DC_SERVO_READBACK_1 + i); + dev_dbg(codec->dev, "DC servo %d: %x\n", + 3 - i, val); + wm8903->dcs_cache[i] = val; + } + break; + + default: + pr_warn("DCS mode %d delay not set\n", dcs_mode); + break; + } + + wm8903->dcs_pending = 0; + } } /* @@ -667,6 +636,22 @@ static const struct soc_enum lsidetone_enum = static const struct soc_enum rsidetone_enum = SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_0, 0, 3, sidetone_text); +static const char *aif_text[] = { + "Left", "Right" +}; + +static const struct soc_enum lcapture_enum = + SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 7, 2, aif_text); + +static const struct soc_enum rcapture_enum = + SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 6, 2, aif_text); + +static const struct soc_enum lplay_enum = + SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 5, 2, aif_text); + +static const struct soc_enum rplay_enum = + SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 4, 2, aif_text); + static const struct snd_kcontrol_new wm8903_snd_controls[] = { /* Input PGAs - No TLV since the scale depends on PGA mode */ @@ -784,6 +769,18 @@ static const struct snd_kcontrol_new lsidetone_mux = static const struct snd_kcontrol_new rsidetone_mux = SOC_DAPM_ENUM("DACR Sidetone Mux", rsidetone_enum); +static const struct snd_kcontrol_new lcapture_mux = + SOC_DAPM_ENUM("Left Capture Mux", lcapture_enum); + +static const struct snd_kcontrol_new rcapture_mux = + SOC_DAPM_ENUM("Right Capture Mux", rcapture_enum); + +static const struct snd_kcontrol_new lplay_mux = + SOC_DAPM_ENUM("Left Playback Mux", lplay_enum); + +static const struct snd_kcontrol_new rplay_mux = + SOC_DAPM_ENUM("Right Playback Mux", rplay_enum); + static const struct snd_kcontrol_new left_output_mixer[] = { SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_LEFT_MIX_0, 3, 1, 0), SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_LEFT_MIX_0, 2, 1, 0), @@ -847,14 +844,26 @@ SND_SOC_DAPM_MUX("Right Input Mode Mux", SND_SOC_NOPM, 0, 0, &rinput_mode_mux), SND_SOC_DAPM_PGA("Left Input PGA", WM8903_POWER_MANAGEMENT_0, 1, 0, NULL, 0), SND_SOC_DAPM_PGA("Right Input PGA", WM8903_POWER_MANAGEMENT_0, 0, 0, NULL, 0), -SND_SOC_DAPM_ADC("ADCL", "Left HiFi Capture", WM8903_POWER_MANAGEMENT_6, 1, 0), -SND_SOC_DAPM_ADC("ADCR", "Right HiFi Capture", WM8903_POWER_MANAGEMENT_6, 0, 0), +SND_SOC_DAPM_ADC("ADCL", NULL, WM8903_POWER_MANAGEMENT_6, 1, 0), +SND_SOC_DAPM_ADC("ADCR", NULL, WM8903_POWER_MANAGEMENT_6, 0, 0), + +SND_SOC_DAPM_MUX("Left Capture Mux", SND_SOC_NOPM, 0, 0, &lcapture_mux), +SND_SOC_DAPM_MUX("Right Capture Mux", SND_SOC_NOPM, 0, 0, &rcapture_mux), + +SND_SOC_DAPM_AIF_OUT("AIFTXL", "Left HiFi Capture", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_OUT("AIFTXR", "Right HiFi Capture", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_MUX("DACL Sidetone", SND_SOC_NOPM, 0, 0, &lsidetone_mux), SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &rsidetone_mux), -SND_SOC_DAPM_DAC("DACL", "Left Playback", WM8903_POWER_MANAGEMENT_6, 3, 0), -SND_SOC_DAPM_DAC("DACR", "Right Playback", WM8903_POWER_MANAGEMENT_6, 2, 0), +SND_SOC_DAPM_AIF_IN("AIFRXL", "Left Playback", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_IN("AIFRXR", "Right Playback", 0, SND_SOC_NOPM, 0, 0), + +SND_SOC_DAPM_MUX("Left Playback Mux", SND_SOC_NOPM, 0, 0, &lplay_mux), +SND_SOC_DAPM_MUX("Right Playback Mux", SND_SOC_NOPM, 0, 0, &rplay_mux), + +SND_SOC_DAPM_DAC("DACL", NULL, WM8903_POWER_MANAGEMENT_6, 3, 0), +SND_SOC_DAPM_DAC("DACR", NULL, WM8903_POWER_MANAGEMENT_6, 2, 0), SND_SOC_DAPM_MIXER("Left Output Mixer", WM8903_POWER_MANAGEMENT_1, 1, 0, left_output_mixer, ARRAY_SIZE(left_output_mixer)), @@ -866,23 +875,45 @@ SND_SOC_DAPM_MIXER("Left Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 1, 0, SND_SOC_DAPM_MIXER("Right Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 0, 0, right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)), -SND_SOC_DAPM_PGA_E("Left Headphone Output PGA", WM8903_POWER_MANAGEMENT_2, - 1, 0, NULL, 0, wm8903_output_event, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD), -SND_SOC_DAPM_PGA_E("Right Headphone Output PGA", WM8903_POWER_MANAGEMENT_2, - 0, 0, NULL, 0, wm8903_output_event, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD), - -SND_SOC_DAPM_PGA_E("Left Line Output PGA", WM8903_POWER_MANAGEMENT_3, 1, 0, - NULL, 0, wm8903_output_event, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD), -SND_SOC_DAPM_PGA_E("Right Line Output PGA", WM8903_POWER_MANAGEMENT_3, 0, 0, - NULL, 0, wm8903_output_event, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA_S("Left Headphone Output PGA", 0, WM8903_ANALOGUE_HP_0, + 4, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("Right Headphone Output PGA", 0, WM8903_ANALOGUE_HP_0, + 0, 0, NULL, 0), + +SND_SOC_DAPM_PGA_S("Left Line Output PGA", 0, WM8903_ANALOGUE_LINEOUT_0, 4, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("Right Line Output PGA", 0, WM8903_ANALOGUE_LINEOUT_0, 0, 0, + NULL, 0), + +SND_SOC_DAPM_PGA_S("HPL_RMV_SHORT", 4, WM8903_ANALOGUE_HP_0, 7, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPL_ENA_OUTP", 3, WM8903_ANALOGUE_HP_0, 6, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPL_ENA_DLY", 1, WM8903_ANALOGUE_HP_0, 5, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPR_RMV_SHORT", 4, WM8903_ANALOGUE_HP_0, 3, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPR_ENA_OUTP", 3, WM8903_ANALOGUE_HP_0, 2, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPR_ENA_DLY", 1, WM8903_ANALOGUE_HP_0, 1, 0, NULL, 0), + +SND_SOC_DAPM_PGA_S("LINEOUTL_RMV_SHORT", 4, WM8903_ANALOGUE_LINEOUT_0, 7, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("LINEOUTL_ENA_OUTP", 3, WM8903_ANALOGUE_LINEOUT_0, 6, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("LINEOUTL_ENA_DLY", 1, WM8903_ANALOGUE_LINEOUT_0, 5, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("LINEOUTR_RMV_SHORT", 4, WM8903_ANALOGUE_LINEOUT_0, 3, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("LINEOUTR_ENA_OUTP", 3, WM8903_ANALOGUE_LINEOUT_0, 2, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("LINEOUTR_ENA_DLY", 1, WM8903_ANALOGUE_LINEOUT_0, 1, 0, + NULL, 0), + +SND_SOC_DAPM_SUPPLY("DCS Master", WM8903_DC_SERVO_0, 4, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPL_DCS", 3, SND_SOC_NOPM, 3, 0, wm8903_dcs_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA_S("HPR_DCS", 3, SND_SOC_NOPM, 2, 0, wm8903_dcs_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA_S("LINEOUTL_DCS", 3, SND_SOC_NOPM, 1, 0, wm8903_dcs_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA_S("LINEOUTR_DCS", 3, SND_SOC_NOPM, 0, 0, wm8903_dcs_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_PGA("Left Speaker PGA", WM8903_POWER_MANAGEMENT_5, 1, 0, NULL, 0), @@ -892,10 +923,18 @@ SND_SOC_DAPM_PGA("Right Speaker PGA", WM8903_POWER_MANAGEMENT_5, 0, 0, SND_SOC_DAPM_SUPPLY("Charge Pump", WM8903_CHARGE_PUMP_0, 0, 0, wm8903_cp_event, SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8903_CLOCK_RATES_2, 1, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("CLK_SYS", WM8903_CLOCK_RATES_2, 2, 0, NULL, 0), }; static const struct snd_soc_dapm_route intercon[] = { + { "CLK_DSP", NULL, "CLK_SYS" }, + { "Mic Bias", NULL, "CLK_SYS" }, + { "HPL_DCS", NULL, "CLK_SYS" }, + { "HPR_DCS", NULL, "CLK_SYS" }, + { "LINEOUTL_DCS", NULL, "CLK_SYS" }, + { "LINEOUTR_DCS", NULL, "CLK_SYS" }, + { "Left Input Mux", "IN1L", "IN1L" }, { "Left Input Mux", "IN2L", "IN2L" }, { "Left Input Mux", "IN3L", "IN3L" }, @@ -936,18 +975,36 @@ static const struct snd_soc_dapm_route intercon[] = { { "Left Input PGA", NULL, "Left Input Mode Mux" }, { "Right Input PGA", NULL, "Right Input Mode Mux" }, + { "Left Capture Mux", "Left", "ADCL" }, + { "Left Capture Mux", "Right", "ADCR" }, + + { "Right Capture Mux", "Left", "ADCL" }, + { "Right Capture Mux", "Right", "ADCR" }, + + { "AIFTXL", NULL, "Left Capture Mux" }, + { "AIFTXR", NULL, "Right Capture Mux" }, + { "ADCL", NULL, "Left Input PGA" }, { "ADCL", NULL, "CLK_DSP" }, { "ADCR", NULL, "Right Input PGA" }, { "ADCR", NULL, "CLK_DSP" }, + { "Left Playback Mux", "Left", "AIFRXL" }, + { "Left Playback Mux", "Right", "AIFRXR" }, + + { "Right Playback Mux", "Left", "AIFRXL" }, + { "Right Playback Mux", "Right", "AIFRXR" }, + { "DACL Sidetone", "Left", "ADCL" }, { "DACL Sidetone", "Right", "ADCR" }, { "DACR Sidetone", "Left", "ADCL" }, { "DACR Sidetone", "Right", "ADCR" }, + { "DACL", NULL, "Left Playback Mux" }, { "DACL", NULL, "DACL Sidetone" }, { "DACL", NULL, "CLK_DSP" }, + + { "DACR", NULL, "Right Playback Mux" }, { "DACR", NULL, "DACR Sidetone" }, { "DACR", NULL, "CLK_DSP" }, @@ -980,11 +1037,35 @@ static const struct snd_soc_dapm_route intercon[] = { { "Left Speaker PGA", NULL, "Left Speaker Mixer" }, { "Right Speaker PGA", NULL, "Right Speaker Mixer" }, - { "HPOUTL", NULL, "Left Headphone Output PGA" }, - { "HPOUTR", NULL, "Right Headphone Output PGA" }, + { "HPL_ENA_DLY", NULL, "Left Headphone Output PGA" }, + { "HPR_ENA_DLY", NULL, "Right Headphone Output PGA" }, + { "LINEOUTL_ENA_DLY", NULL, "Left Line Output PGA" }, + { "LINEOUTR_ENA_DLY", NULL, "Right Line Output PGA" }, + + { "HPL_DCS", NULL, "DCS Master" }, + { "HPR_DCS", NULL, "DCS Master" }, + { "LINEOUTL_DCS", NULL, "DCS Master" }, + { "LINEOUTR_DCS", NULL, "DCS Master" }, + + { "HPL_DCS", NULL, "HPL_ENA_DLY" }, + { "HPR_DCS", NULL, "HPR_ENA_DLY" }, + { "LINEOUTL_DCS", NULL, "LINEOUTL_ENA_DLY" }, + { "LINEOUTR_DCS", NULL, "LINEOUTR_ENA_DLY" }, - { "LINEOUTL", NULL, "Left Line Output PGA" }, - { "LINEOUTR", NULL, "Right Line Output PGA" }, + { "HPL_ENA_OUTP", NULL, "HPL_DCS" }, + { "HPR_ENA_OUTP", NULL, "HPR_DCS" }, + { "LINEOUTL_ENA_OUTP", NULL, "LINEOUTL_DCS" }, + { "LINEOUTR_ENA_OUTP", NULL, "LINEOUTR_DCS" }, + + { "HPL_RMV_SHORT", NULL, "HPL_ENA_OUTP" }, + { "HPR_RMV_SHORT", NULL, "HPR_ENA_OUTP" }, + { "LINEOUTL_RMV_SHORT", NULL, "LINEOUTL_ENA_OUTP" }, + { "LINEOUTR_RMV_SHORT", NULL, "LINEOUTR_ENA_OUTP" }, + + { "HPOUTL", NULL, "HPL_RMV_SHORT" }, + { "HPOUTR", NULL, "HPR_RMV_SHORT" }, + { "LINEOUTL", NULL, "LINEOUTL_RMV_SHORT" }, + { "LINEOUTR", NULL, "LINEOUTR_RMV_SHORT" }, { "LOP", NULL, "Left Speaker PGA" }, { "LON", NULL, "Left Speaker PGA" }, @@ -1012,29 +1093,71 @@ static int wm8903_add_widgets(struct snd_soc_codec *codec) static int wm8903_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - u16 reg; - switch (level) { case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: - reg = snd_soc_read(codec, WM8903_VMID_CONTROL_0); - reg &= ~(WM8903_VMID_RES_MASK); - reg |= WM8903_VMID_RES_50K; - snd_soc_write(codec, WM8903_VMID_CONTROL_0, reg); + snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0, + WM8903_VMID_RES_MASK, + WM8903_VMID_RES_50K); break; case SND_SOC_BIAS_STANDBY: if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { - snd_soc_write(codec, WM8903_CLOCK_RATES_2, - WM8903_CLK_SYS_ENA); - - /* Change DC servo dither level in startup sequence */ - snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0, 0x11); - snd_soc_write(codec, WM8903_WRITE_SEQUENCER_1, 0x1257); - snd_soc_write(codec, WM8903_WRITE_SEQUENCER_2, 0x2); - - wm8903_run_sequence(codec, 0); - wm8903_sync_reg_cache(codec, codec->reg_cache); + snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0, + WM8903_POBCTRL | WM8903_ISEL_MASK | + WM8903_STARTUP_BIAS_ENA | + WM8903_BIAS_ENA, + WM8903_POBCTRL | + (2 << WM8903_ISEL_SHIFT) | + WM8903_STARTUP_BIAS_ENA); + + snd_soc_update_bits(codec, + WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0, + WM8903_SPK_DISCHARGE, + WM8903_SPK_DISCHARGE); + + msleep(33); + + snd_soc_update_bits(codec, WM8903_POWER_MANAGEMENT_5, + WM8903_SPKL_ENA | WM8903_SPKR_ENA, + WM8903_SPKL_ENA | WM8903_SPKR_ENA); + + snd_soc_update_bits(codec, + WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0, + WM8903_SPK_DISCHARGE, 0); + + snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0, + WM8903_VMID_TIE_ENA | + WM8903_BUFIO_ENA | + WM8903_VMID_IO_ENA | + WM8903_VMID_SOFT_MASK | + WM8903_VMID_RES_MASK | + WM8903_VMID_BUF_ENA, + WM8903_VMID_TIE_ENA | + WM8903_BUFIO_ENA | + WM8903_VMID_IO_ENA | + (2 << WM8903_VMID_SOFT_SHIFT) | + WM8903_VMID_RES_250K | + WM8903_VMID_BUF_ENA); + + msleep(129); + + snd_soc_update_bits(codec, WM8903_POWER_MANAGEMENT_5, + WM8903_SPKL_ENA | WM8903_SPKR_ENA, + 0); + + snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0, + WM8903_VMID_SOFT_MASK, 0); + + snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0, + WM8903_VMID_RES_MASK, + WM8903_VMID_RES_50K); + + snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0, + WM8903_BIAS_ENA | WM8903_POBCTRL, + WM8903_BIAS_ENA); /* By default no bypass paths are enabled so * enable Class W support. @@ -1047,17 +1170,32 @@ static int wm8903_set_bias_level(struct snd_soc_codec *codec, WM8903_CP_DYN_V); } - reg = snd_soc_read(codec, WM8903_VMID_CONTROL_0); - reg &= ~(WM8903_VMID_RES_MASK); - reg |= WM8903_VMID_RES_250K; - snd_soc_write(codec, WM8903_VMID_CONTROL_0, reg); + snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0, + WM8903_VMID_RES_MASK, + WM8903_VMID_RES_250K); break; case SND_SOC_BIAS_OFF: - wm8903_run_sequence(codec, 32); - reg = snd_soc_read(codec, WM8903_CLOCK_RATES_2); - reg &= ~WM8903_CLK_SYS_ENA; - snd_soc_write(codec, WM8903_CLOCK_RATES_2, reg); + snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0, + WM8903_BIAS_ENA, 0); + + snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0, + WM8903_VMID_SOFT_MASK, + 2 << WM8903_VMID_SOFT_SHIFT); + + snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0, + WM8903_VMID_BUF_ENA, 0); + + msleep(290); + + snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0, + WM8903_VMID_TIE_ENA | WM8903_BUFIO_ENA | + WM8903_VMID_IO_ENA | WM8903_VMID_RES_MASK | + WM8903_VMID_SOFT_MASK | + WM8903_VMID_BUF_ENA, 0); + + snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0, + WM8903_STARTUP_BIAS_ENA, 0); break; } @@ -1510,8 +1648,7 @@ static irqreturn_t wm8903_irq(int irq, void *data) int_val = snd_soc_read(codec, WM8903_INTERRUPT_STATUS_1) & mask; if (int_val & WM8903_WSEQ_BUSY_EINT) { - dev_dbg(codec->dev, "Write sequencer done\n"); - complete(&wm8903->wseq); + dev_warn(codec->dev, "Write sequencer done\n"); } /* @@ -1635,6 +1772,120 @@ static int wm8903_resume(struct snd_soc_codec *codec) return 0; } +#ifdef CONFIG_GPIOLIB +static inline struct wm8903_priv *gpio_to_wm8903(struct gpio_chip *chip) +{ + return container_of(chip, struct wm8903_priv, gpio_chip); +} + +static int wm8903_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + if (offset >= WM8903_NUM_GPIO) + return -EINVAL; + + return 0; +} + +static int wm8903_gpio_direction_in(struct gpio_chip *chip, unsigned offset) +{ + struct wm8903_priv *wm8903 = gpio_to_wm8903(chip); + struct snd_soc_codec *codec = wm8903->codec; + unsigned int mask, val; + + mask = WM8903_GP1_FN_MASK | WM8903_GP1_DIR_MASK; + val = (WM8903_GPn_FN_GPIO_INPUT << WM8903_GP1_FN_SHIFT) | + WM8903_GP1_DIR; + + return snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset, + mask, val); +} + +static int wm8903_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct wm8903_priv *wm8903 = gpio_to_wm8903(chip); + struct snd_soc_codec *codec = wm8903->codec; + int reg; + + reg = snd_soc_read(codec, WM8903_GPIO_CONTROL_1 + offset); + + return (reg & WM8903_GP1_LVL_MASK) >> WM8903_GP1_LVL_SHIFT; +} + +static int wm8903_gpio_direction_out(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct wm8903_priv *wm8903 = gpio_to_wm8903(chip); + struct snd_soc_codec *codec = wm8903->codec; + unsigned int mask, val; + + mask = WM8903_GP1_FN_MASK | WM8903_GP1_DIR_MASK | WM8903_GP1_LVL_MASK; + val = (WM8903_GPn_FN_GPIO_OUTPUT << WM8903_GP1_FN_SHIFT) | + (value << WM8903_GP2_LVL_SHIFT); + + return snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset, + mask, val); +} + +static void wm8903_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct wm8903_priv *wm8903 = gpio_to_wm8903(chip); + struct snd_soc_codec *codec = wm8903->codec; + + snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset, + WM8903_GP1_LVL_MASK, + !!value << WM8903_GP1_LVL_SHIFT); +} + +static struct gpio_chip wm8903_template_chip = { + .label = "wm8903", + .owner = THIS_MODULE, + .request = wm8903_gpio_request, + .direction_input = wm8903_gpio_direction_in, + .get = wm8903_gpio_get, + .direction_output = wm8903_gpio_direction_out, + .set = wm8903_gpio_set, + .can_sleep = 1, +}; + +static void wm8903_init_gpio(struct snd_soc_codec *codec) +{ + struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); + struct wm8903_platform_data *pdata = dev_get_platdata(codec->dev); + int ret; + + wm8903->gpio_chip = wm8903_template_chip; + wm8903->gpio_chip.ngpio = WM8903_NUM_GPIO; + wm8903->gpio_chip.dev = codec->dev; + + if (pdata && pdata->gpio_base) + wm8903->gpio_chip.base = pdata->gpio_base; + else + wm8903->gpio_chip.base = -1; + + ret = gpiochip_add(&wm8903->gpio_chip); + if (ret != 0) + dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret); +} + +static void wm8903_free_gpio(struct snd_soc_codec *codec) +{ + struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); + int ret; + + ret = gpiochip_remove(&wm8903->gpio_chip); + if (ret != 0) + dev_err(codec->dev, "Failed to remove GPIOs: %d\n", ret); +} +#else +static void wm8903_init_gpio(struct snd_soc_codec *codec) +{ +} + +static void wm8903_free_gpio(struct snd_soc_codec *codec) +{ +} +#endif + static int wm8903_probe(struct snd_soc_codec *codec) { struct wm8903_platform_data *pdata = dev_get_platdata(codec->dev); @@ -1643,7 +1894,7 @@ static int wm8903_probe(struct snd_soc_codec *codec) int trigger, irq_pol; u16 val; - init_completion(&wm8903->wseq); + wm8903->codec = codec; ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C); if (ret != 0) { @@ -1659,19 +1910,33 @@ static int wm8903_probe(struct snd_soc_codec *codec) } val = snd_soc_read(codec, WM8903_REVISION_NUMBER); - dev_info(codec->dev, "WM8903 revision %d\n", - val & WM8903_CHIP_REV_MASK); + dev_info(codec->dev, "WM8903 revision %c\n", + (val & WM8903_CHIP_REV_MASK) + 'A'); wm8903_reset(codec); /* Set up GPIOs and microphone detection */ if (pdata) { + bool mic_gpio = false; + for (i = 0; i < ARRAY_SIZE(pdata->gpio_cfg); i++) { - if (!pdata->gpio_cfg[i]) + if (pdata->gpio_cfg[i] == WM8903_GPIO_NO_CONFIG) continue; snd_soc_write(codec, WM8903_GPIO_CONTROL_1 + i, pdata->gpio_cfg[i] & 0xffff); + + val = (pdata->gpio_cfg[i] & WM8903_GP1_FN_MASK) + >> WM8903_GP1_FN_SHIFT; + + switch (val) { + case WM8903_GPn_FN_MICBIAS_CURRENT_DETECT: + case WM8903_GPn_FN_MICBIAS_SHORT_DETECT: + mic_gpio = true; + break; + default: + break; + } } snd_soc_write(codec, WM8903_MIC_BIAS_CONTROL_0, @@ -1682,6 +1947,14 @@ static int wm8903_probe(struct snd_soc_codec *codec) snd_soc_update_bits(codec, WM8903_WRITE_SEQUENCER_0, WM8903_WSEQ_ENA, WM8903_WSEQ_ENA); + /* If microphone detection is enabled by pdata but + * detected via IRQ then interrupts can be lost before + * the machine driver has set up microphone detection + * IRQs as the IRQs are clear on read. The detection + * will be enabled when the machine driver configures. + */ + WARN_ON(!mic_gpio && (pdata->micdet_cfg & WM8903_MICDET_ENA)); + wm8903->mic_delay = pdata->micdet_delay; } @@ -1741,20 +2014,23 @@ static int wm8903_probe(struct snd_soc_codec *codec) snd_soc_write(codec, WM8903_ANALOGUE_OUT3_RIGHT, val); /* Enable DAC soft mute by default */ - val = snd_soc_read(codec, WM8903_DAC_DIGITAL_1); - val |= WM8903_DAC_MUTEMODE; - snd_soc_write(codec, WM8903_DAC_DIGITAL_1, val); + snd_soc_update_bits(codec, WM8903_DAC_DIGITAL_1, + WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE, + WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE); snd_soc_add_controls(codec, wm8903_snd_controls, ARRAY_SIZE(wm8903_snd_controls)); wm8903_add_widgets(codec); + wm8903_init_gpio(codec); + return ret; } /* power down chip */ static int wm8903_remove(struct snd_soc_codec *codec) { + wm8903_free_gpio(codec); wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } @@ -1769,6 +2045,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8903 = { .reg_word_size = sizeof(u16), .reg_cache_default = wm8903_reg_defaults, .volatile_register = wm8903_volatile_register, + .seq_notifier = wm8903_seq_notifier, }; #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) @@ -1807,7 +2084,7 @@ MODULE_DEVICE_TABLE(i2c, wm8903_i2c_id); static struct i2c_driver wm8903_i2c_driver = { .driver = { - .name = "wm8903-codec", + .name = "wm8903", .owner = THIS_MODULE, }, .probe = wm8903_i2c_probe, diff --git a/sound/soc/codecs/wm8903.h b/sound/soc/codecs/wm8903.h index e3ec2433b215..db949311c0f2 100644 --- a/sound/soc/codecs/wm8903.h +++ b/sound/soc/codecs/wm8903.h @@ -75,6 +75,14 @@ extern int wm8903_mic_detect(struct snd_soc_codec *codec, #define WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0 0x41 #define WM8903_DC_SERVO_0 0x43 #define WM8903_DC_SERVO_2 0x45 +#define WM8903_DC_SERVO_4 0x47 +#define WM8903_DC_SERVO_5 0x48 +#define WM8903_DC_SERVO_6 0x49 +#define WM8903_DC_SERVO_7 0x4A +#define WM8903_DC_SERVO_READBACK_1 0x51 +#define WM8903_DC_SERVO_READBACK_2 0x52 +#define WM8903_DC_SERVO_READBACK_3 0x53 +#define WM8903_DC_SERVO_READBACK_4 0x54 #define WM8903_ANALOGUE_HP_0 0x5A #define WM8903_ANALOGUE_LINEOUT_0 0x5E #define WM8903_CHARGE_PUMP_0 0x62 diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 9de44a4c05c0..443ae580445c 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -596,7 +596,7 @@ static struct { { 0x003F, 0x003F, 0 }, /* R248 - FLL NCO Test 1 */ }; -static int wm8904_volatile_register(unsigned int reg) +static int wm8904_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { return wm8904_access[reg].vol; } @@ -2436,19 +2436,28 @@ static int wm8904_probe(struct snd_soc_codec *codec) } /* Change some default settings - latch VU and enable ZC */ - reg_cache[WM8904_ADC_DIGITAL_VOLUME_LEFT] |= WM8904_ADC_VU; - reg_cache[WM8904_ADC_DIGITAL_VOLUME_RIGHT] |= WM8904_ADC_VU; - reg_cache[WM8904_DAC_DIGITAL_VOLUME_LEFT] |= WM8904_DAC_VU; - reg_cache[WM8904_DAC_DIGITAL_VOLUME_RIGHT] |= WM8904_DAC_VU; - reg_cache[WM8904_ANALOGUE_OUT1_LEFT] |= WM8904_HPOUT_VU | - WM8904_HPOUTLZC; - reg_cache[WM8904_ANALOGUE_OUT1_RIGHT] |= WM8904_HPOUT_VU | - WM8904_HPOUTRZC; - reg_cache[WM8904_ANALOGUE_OUT2_LEFT] |= WM8904_LINEOUT_VU | - WM8904_LINEOUTLZC; - reg_cache[WM8904_ANALOGUE_OUT2_RIGHT] |= WM8904_LINEOUT_VU | - WM8904_LINEOUTRZC; - reg_cache[WM8904_CLOCK_RATES_0] &= ~WM8904_SR_MODE; + snd_soc_update_bits(codec, WM8904_ADC_DIGITAL_VOLUME_LEFT, + WM8904_ADC_VU, WM8904_ADC_VU); + snd_soc_update_bits(codec, WM8904_ADC_DIGITAL_VOLUME_RIGHT, + WM8904_ADC_VU, WM8904_ADC_VU); + snd_soc_update_bits(codec, WM8904_DAC_DIGITAL_VOLUME_LEFT, + WM8904_DAC_VU, WM8904_DAC_VU); + snd_soc_update_bits(codec, WM8904_DAC_DIGITAL_VOLUME_RIGHT, + WM8904_DAC_VU, WM8904_DAC_VU); + snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT1_LEFT, + WM8904_HPOUT_VU | WM8904_HPOUTLZC, + WM8904_HPOUT_VU | WM8904_HPOUTLZC); + snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT1_RIGHT, + WM8904_HPOUT_VU | WM8904_HPOUTRZC, + WM8904_HPOUT_VU | WM8904_HPOUTRZC); + snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT2_LEFT, + WM8904_LINEOUT_VU | WM8904_LINEOUTLZC, + WM8904_LINEOUT_VU | WM8904_LINEOUTLZC); + snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT2_RIGHT, + WM8904_LINEOUT_VU | WM8904_LINEOUTRZC, + WM8904_LINEOUT_VU | WM8904_LINEOUTRZC); + snd_soc_update_bits(codec, WM8904_CLOCK_RATES_0, + WM8904_SR_MODE, 0); /* Apply configuration from the platform data. */ if (wm8904->pdata) { @@ -2469,10 +2478,12 @@ static int wm8904_probe(struct snd_soc_codec *codec) /* Set Class W by default - this will be managed by the Class * G widget at runtime where bypass paths are available. */ - reg_cache[WM8904_CLASS_W_0] |= WM8904_CP_DYN_PWR; + snd_soc_update_bits(codec, WM8904_CLASS_W_0, + WM8904_CP_DYN_PWR, WM8904_CP_DYN_PWR); /* Use normal bias source */ - reg_cache[WM8904_BIAS_CONTROL_0] &= ~WM8904_POBCTRL; + snd_soc_update_bits(codec, WM8904_BIAS_CONTROL_0, + WM8904_POBCTRL, 0); wm8904_set_bias_level(codec, SND_SOC_BIAS_STANDBY); diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c index 7167dfc96aa7..5e0214d6293e 100644 --- a/sound/soc/codecs/wm8955.c +++ b/sound/soc/codecs/wm8955.c @@ -934,16 +934,27 @@ static int wm8955_probe(struct snd_soc_codec *codec) } /* Change some default settings - latch VU and enable ZC */ - reg_cache[WM8955_LEFT_DAC_VOLUME] |= WM8955_LDVU; - reg_cache[WM8955_RIGHT_DAC_VOLUME] |= WM8955_RDVU; - reg_cache[WM8955_LOUT1_VOLUME] |= WM8955_LO1VU | WM8955_LO1ZC; - reg_cache[WM8955_ROUT1_VOLUME] |= WM8955_RO1VU | WM8955_RO1ZC; - reg_cache[WM8955_LOUT2_VOLUME] |= WM8955_LO2VU | WM8955_LO2ZC; - reg_cache[WM8955_ROUT2_VOLUME] |= WM8955_RO2VU | WM8955_RO2ZC; - reg_cache[WM8955_MONOOUT_VOLUME] |= WM8955_MOZC; + snd_soc_update_bits(codec, WM8955_LEFT_DAC_VOLUME, + WM8955_LDVU, WM8955_LDVU); + snd_soc_update_bits(codec, WM8955_RIGHT_DAC_VOLUME, + WM8955_RDVU, WM8955_RDVU); + snd_soc_update_bits(codec, WM8955_LOUT1_VOLUME, + WM8955_LO1VU | WM8955_LO1ZC, + WM8955_LO1VU | WM8955_LO1ZC); + snd_soc_update_bits(codec, WM8955_ROUT1_VOLUME, + WM8955_RO1VU | WM8955_RO1ZC, + WM8955_RO1VU | WM8955_RO1ZC); + snd_soc_update_bits(codec, WM8955_LOUT2_VOLUME, + WM8955_LO2VU | WM8955_LO2ZC, + WM8955_LO2VU | WM8955_LO2ZC); + snd_soc_update_bits(codec, WM8955_ROUT2_VOLUME, + WM8955_RO2VU | WM8955_RO2ZC, + WM8955_RO2VU | WM8955_RO2ZC); + snd_soc_update_bits(codec, WM8955_MONOOUT_VOLUME, + WM8955_MOZC, WM8955_MOZC); /* Also enable adaptive bass boost by default */ - reg_cache[WM8955_BASS_CONTROL] |= WM8955_BB; + snd_soc_update_bits(codec, WM8955_BASS_CONTROL, WM8955_BB, WM8955_BB); /* Set platform data values */ if (pdata) { diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index 55252e7d02c9..cdee8103d09b 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c @@ -291,7 +291,7 @@ struct wm8961_priv { int sysclk; }; -static int wm8961_volatile_register(unsigned int reg) +static int wm8961_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8961_SOFTWARE_RESET: diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index b9cb1fcf8c92..3b71dd65c966 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -1938,7 +1938,7 @@ static const struct wm8962_reg_access { [21139] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21139 - VSS_XTS32_0 */ }; -static int wm8962_volatile_register(unsigned int reg) +static int wm8962_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { if (wm8962_reg_access[reg].vol) return 1; @@ -1946,7 +1946,7 @@ static int wm8962_volatile_register(unsigned int reg) return 0; } -static int wm8962_readable_register(unsigned int reg) +static int wm8962_readable_register(struct snd_soc_codec *codec, unsigned int reg) { if (wm8962_reg_access[reg].read) return 1; @@ -3635,7 +3635,7 @@ static void wm8962_gpio_set(struct gpio_chip *chip, unsigned offset, int value) struct snd_soc_codec *codec = wm8962->codec; snd_soc_update_bits(codec, WM8962_GPIO_BASE + offset, - WM8962_GP2_LVL, value << WM8962_GP2_LVL_SHIFT); + WM8962_GP2_LVL, !!value << WM8962_GP2_LVL_SHIFT); } static int wm8962_gpio_direction_out(struct gpio_chip *chip, @@ -3822,16 +3822,26 @@ static int wm8962_probe(struct snd_soc_codec *codec) } /* Latch volume update bits */ - reg_cache[WM8962_LEFT_INPUT_VOLUME] |= WM8962_IN_VU; - reg_cache[WM8962_RIGHT_INPUT_VOLUME] |= WM8962_IN_VU; - reg_cache[WM8962_LEFT_ADC_VOLUME] |= WM8962_ADC_VU; - reg_cache[WM8962_RIGHT_ADC_VOLUME] |= WM8962_ADC_VU; - reg_cache[WM8962_LEFT_DAC_VOLUME] |= WM8962_DAC_VU; - reg_cache[WM8962_RIGHT_DAC_VOLUME] |= WM8962_DAC_VU; - reg_cache[WM8962_SPKOUTL_VOLUME] |= WM8962_SPKOUT_VU; - reg_cache[WM8962_SPKOUTR_VOLUME] |= WM8962_SPKOUT_VU; - reg_cache[WM8962_HPOUTL_VOLUME] |= WM8962_HPOUT_VU; - reg_cache[WM8962_HPOUTR_VOLUME] |= WM8962_HPOUT_VU; + snd_soc_update_bits(codec, WM8962_LEFT_INPUT_VOLUME, + WM8962_IN_VU, WM8962_IN_VU); + snd_soc_update_bits(codec, WM8962_RIGHT_INPUT_VOLUME, + WM8962_IN_VU, WM8962_IN_VU); + snd_soc_update_bits(codec, WM8962_LEFT_ADC_VOLUME, + WM8962_ADC_VU, WM8962_ADC_VU); + snd_soc_update_bits(codec, WM8962_RIGHT_ADC_VOLUME, + WM8962_ADC_VU, WM8962_ADC_VU); + snd_soc_update_bits(codec, WM8962_LEFT_DAC_VOLUME, + WM8962_DAC_VU, WM8962_DAC_VU); + snd_soc_update_bits(codec, WM8962_RIGHT_DAC_VOLUME, + WM8962_DAC_VU, WM8962_DAC_VU); + snd_soc_update_bits(codec, WM8962_SPKOUTL_VOLUME, + WM8962_SPKOUT_VU, WM8962_SPKOUT_VU); + snd_soc_update_bits(codec, WM8962_SPKOUTR_VOLUME, + WM8962_SPKOUT_VU, WM8962_SPKOUT_VU); + snd_soc_update_bits(codec, WM8962_HPOUTL_VOLUME, + WM8962_HPOUT_VU, WM8962_HPOUT_VU); + snd_soc_update_bits(codec, WM8962_HPOUTR_VOLUME, + WM8962_HPOUT_VU, WM8962_HPOUT_VU); wm8962_add_widgets(codec); diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c index 8dfb0a0da673..85e3e630e763 100644 --- a/sound/soc/codecs/wm8978.c +++ b/sound/soc/codecs/wm8978.c @@ -93,6 +93,7 @@ static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0); static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0); static const DECLARE_TLV_DB_SCALE(boost_tlv, -1500, 300, 1); +static const DECLARE_TLV_DB_SCALE(limiter_tlv, 0, 100, 0); static const struct snd_kcontrol_new wm8978_snd_controls[] = { @@ -144,8 +145,8 @@ static const struct snd_kcontrol_new wm8978_snd_controls[] = { SOC_SINGLE("DAC Playback Limiter Threshold", WM8978_DAC_LIMITER_2, 4, 7, 0), - SOC_SINGLE("DAC Playback Limiter Boost", - WM8978_DAC_LIMITER_2, 0, 12, 0), + SOC_SINGLE_TLV("DAC Playback Limiter Volume", + WM8978_DAC_LIMITER_2, 0, 12, 0, limiter_tlv), SOC_ENUM("ALC Enable Switch", alc1), SOC_SINGLE("ALC Capture Min Gain", WM8978_ALC_CONTROL_1, 0, 7, 0), @@ -967,7 +968,7 @@ static int wm8978_probe(struct snd_soc_codec *codec) * written. */ for (i = 0; i < ARRAY_SIZE(update_reg); i++) - ((u16 *)codec->reg_cache)[update_reg[i]] |= 0x100; + snd_soc_update_bits(codec, update_reg[i], 0x100, 0x100); /* Reset the codec */ ret = snd_soc_write(codec, WM8978_RESET, 0); diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c new file mode 100644 index 000000000000..28fdfd66661d --- /dev/null +++ b/sound/soc/codecs/wm8991.c @@ -0,0 +1,1427 @@ +/* + * wm8991.c -- WM8991 ALSA Soc Audio driver + * + * Copyright 2007-2010 Wolfson Microelectronics PLC. + * Author: Graeme Gregory + * 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 as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/slab.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 "wm8991.h" + +struct wm8991_priv { + enum snd_soc_control_type control_type; + unsigned int pcmclk; +}; + +static const u16 wm8991_reg_defs[] = { + 0x8991, /* R0 - Reset */ + 0x0000, /* R1 - Power Management (1) */ + 0x6000, /* R2 - Power Management (2) */ + 0x0000, /* R3 - Power Management (3) */ + 0x4050, /* R4 - Audio Interface (1) */ + 0x4000, /* R5 - Audio Interface (2) */ + 0x01C8, /* R6 - Clocking (1) */ + 0x0000, /* R7 - Clocking (2) */ + 0x0040, /* R8 - Audio Interface (3) */ + 0x0040, /* R9 - Audio Interface (4) */ + 0x0004, /* R10 - DAC CTRL */ + 0x00C0, /* R11 - Left DAC Digital Volume */ + 0x00C0, /* R12 - Right DAC Digital Volume */ + 0x0000, /* R13 - Digital Side Tone */ + 0x0100, /* R14 - ADC CTRL */ + 0x00C0, /* R15 - Left ADC Digital Volume */ + 0x00C0, /* R16 - Right ADC Digital Volume */ + 0x0000, /* R17 */ + 0x0000, /* R18 - GPIO CTRL 1 */ + 0x1000, /* R19 - GPIO1 & GPIO2 */ + 0x1010, /* R20 - GPIO3 & GPIO4 */ + 0x1010, /* R21 - GPIO5 & GPIO6 */ + 0x8000, /* R22 - GPIOCTRL 2 */ + 0x0800, /* R23 - GPIO_POL */ + 0x008B, /* R24 - Left Line Input 1&2 Volume */ + 0x008B, /* R25 - Left Line Input 3&4 Volume */ + 0x008B, /* R26 - Right Line Input 1&2 Volume */ + 0x008B, /* R27 - Right Line Input 3&4 Volume */ + 0x0000, /* R28 - Left Output Volume */ + 0x0000, /* R29 - Right Output Volume */ + 0x0066, /* R30 - Line Outputs Volume */ + 0x0022, /* R31 - Out3/4 Volume */ + 0x0079, /* R32 - Left OPGA Volume */ + 0x0079, /* R33 - Right OPGA Volume */ + 0x0003, /* R34 - Speaker Volume */ + 0x0003, /* R35 - ClassD1 */ + 0x0000, /* R36 */ + 0x0100, /* R37 - ClassD3 */ + 0x0000, /* R38 */ + 0x0000, /* R39 - Input Mixer1 */ + 0x0000, /* R40 - Input Mixer2 */ + 0x0000, /* R41 - Input Mixer3 */ + 0x0000, /* R42 - Input Mixer4 */ + 0x0000, /* R43 - Input Mixer5 */ + 0x0000, /* R44 - Input Mixer6 */ + 0x0000, /* R45 - Output Mixer1 */ + 0x0000, /* R46 - Output Mixer2 */ + 0x0000, /* R47 - Output Mixer3 */ + 0x0000, /* R48 - Output Mixer4 */ + 0x0000, /* R49 - Output Mixer5 */ + 0x0000, /* R50 - Output Mixer6 */ + 0x0180, /* R51 - Out3/4 Mixer */ + 0x0000, /* R52 - Line Mixer1 */ + 0x0000, /* R53 - Line Mixer2 */ + 0x0000, /* R54 - Speaker Mixer */ + 0x0000, /* R55 - Additional Control */ + 0x0000, /* R56 - AntiPOP1 */ + 0x0000, /* R57 - AntiPOP2 */ + 0x0000, /* R58 - MICBIAS */ + 0x0000, /* R59 */ + 0x0008, /* R60 - PLL1 */ + 0x0031, /* R61 - PLL2 */ + 0x0026, /* R62 - PLL3 */ +}; + +#define wm8991_reset(c) snd_soc_write(c, WM8991_RESET, 0) + +static const unsigned int rec_mix_tlv[] = { + TLV_DB_RANGE_HEAD(1), + 0, 7, TLV_DB_LINEAR_ITEM(-1500, 600), +}; + +static const unsigned int in_pga_tlv[] = { + TLV_DB_RANGE_HEAD(1), + 0, 0x1F, TLV_DB_LINEAR_ITEM(-1650, 3000), +}; + +static const unsigned int out_mix_tlv[] = { + TLV_DB_RANGE_HEAD(1), + 0, 7, TLV_DB_LINEAR_ITEM(0, -2100), +}; + +static const unsigned int out_pga_tlv[] = { + TLV_DB_RANGE_HEAD(1), + 0, 127, TLV_DB_LINEAR_ITEM(-7300, 600), +}; + +static const unsigned int out_omix_tlv[] = { + TLV_DB_RANGE_HEAD(1), + 0, 7, TLV_DB_LINEAR_ITEM(-600, 0), +}; + +static const unsigned int out_dac_tlv[] = { + TLV_DB_RANGE_HEAD(1), + 0, 255, TLV_DB_LINEAR_ITEM(-7163, 0), +}; + +static const unsigned int in_adc_tlv[] = { + TLV_DB_RANGE_HEAD(1), + 0, 255, TLV_DB_LINEAR_ITEM(-7163, 1763), +}; + +static const unsigned int out_sidetone_tlv[] = { + TLV_DB_RANGE_HEAD(1), + 0, 31, TLV_DB_LINEAR_ITEM(-3600, 0), +}; + +static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int ret; + u16 val; + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + if (ret < 0) + return ret; + + /* now hit the volume update bits (always bit 8) */ + val = snd_soc_read(codec, reg); + return snd_soc_write(codec, reg, val | 0x0100); +} + +static const char *wm8991_digital_sidetone[] = +{"None", "Left ADC", "Right ADC", "Reserved"}; + +static const struct soc_enum wm8991_left_digital_sidetone_enum = + SOC_ENUM_SINGLE(WM8991_DIGITAL_SIDE_TONE, + WM8991_ADC_TO_DACL_SHIFT, + WM8991_ADC_TO_DACL_MASK, + wm8991_digital_sidetone); + +static const struct soc_enum wm8991_right_digital_sidetone_enum = + SOC_ENUM_SINGLE(WM8991_DIGITAL_SIDE_TONE, + WM8991_ADC_TO_DACR_SHIFT, + WM8991_ADC_TO_DACR_MASK, + wm8991_digital_sidetone); + +static const char *wm8991_adcmode[] = +{"Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"}; + +static const struct soc_enum wm8991_right_adcmode_enum = + SOC_ENUM_SINGLE(WM8991_ADC_CTRL, + WM8991_ADC_HPF_CUT_SHIFT, + WM8991_ADC_HPF_CUT_MASK, + wm8991_adcmode); + +static const struct snd_kcontrol_new wm8991_snd_controls[] = { + /* INMIXL */ + SOC_SINGLE("LIN12 PGA Boost", WM8991_INPUT_MIXER3, WM8991_L12MNBST_BIT, 1, 0), + SOC_SINGLE("LIN34 PGA Boost", WM8991_INPUT_MIXER3, WM8991_L34MNBST_BIT, 1, 0), + /* INMIXR */ + SOC_SINGLE("RIN12 PGA Boost", WM8991_INPUT_MIXER3, WM8991_R12MNBST_BIT, 1, 0), + SOC_SINGLE("RIN34 PGA Boost", WM8991_INPUT_MIXER3, WM8991_R34MNBST_BIT, 1, 0), + + /* LOMIX */ + SOC_SINGLE_TLV("LOMIX LIN3 Bypass Volume", WM8991_OUTPUT_MIXER3, + WM8991_LLI3LOVOL_SHIFT, WM8991_LLI3LOVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("LOMIX RIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER3, + WM8991_LR12LOVOL_SHIFT, WM8991_LR12LOVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("LOMIX LIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER3, + WM8991_LL12LOVOL_SHIFT, WM8991_LL12LOVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("LOMIX RIN3 Bypass Volume", WM8991_OUTPUT_MIXER5, + WM8991_LRI3LOVOL_SHIFT, WM8991_LRI3LOVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("LOMIX AINRMUX Bypass Volume", WM8991_OUTPUT_MIXER5, + WM8991_LRBLOVOL_SHIFT, WM8991_LRBLOVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("LOMIX AINLMUX Bypass Volume", WM8991_OUTPUT_MIXER5, + WM8991_LRBLOVOL_SHIFT, WM8991_LRBLOVOL_MASK, 1, out_mix_tlv), + + /* ROMIX */ + SOC_SINGLE_TLV("ROMIX RIN3 Bypass Volume", WM8991_OUTPUT_MIXER4, + WM8991_RRI3ROVOL_SHIFT, WM8991_RRI3ROVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("ROMIX LIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER4, + WM8991_RL12ROVOL_SHIFT, WM8991_RL12ROVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("ROMIX RIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER4, + WM8991_RR12ROVOL_SHIFT, WM8991_RR12ROVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("ROMIX LIN3 Bypass Volume", WM8991_OUTPUT_MIXER6, + WM8991_RLI3ROVOL_SHIFT, WM8991_RLI3ROVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("ROMIX AINLMUX Bypass Volume", WM8991_OUTPUT_MIXER6, + WM8991_RLBROVOL_SHIFT, WM8991_RLBROVOL_MASK, 1, out_mix_tlv), + SOC_SINGLE_TLV("ROMIX AINRMUX Bypass Volume", WM8991_OUTPUT_MIXER6, + WM8991_RRBROVOL_SHIFT, WM8991_RRBROVOL_MASK, 1, out_mix_tlv), + + /* LOUT */ + SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOUT Volume", WM8991_LEFT_OUTPUT_VOLUME, + WM8991_LOUTVOL_SHIFT, WM8991_LOUTVOL_MASK, 0, out_pga_tlv), + SOC_SINGLE("LOUT ZC", WM8991_LEFT_OUTPUT_VOLUME, WM8991_LOZC_BIT, 1, 0), + + /* ROUT */ + SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROUT Volume", WM8991_RIGHT_OUTPUT_VOLUME, + WM8991_ROUTVOL_SHIFT, WM8991_ROUTVOL_MASK, 0, out_pga_tlv), + SOC_SINGLE("ROUT ZC", WM8991_RIGHT_OUTPUT_VOLUME, WM8991_ROZC_BIT, 1, 0), + + /* LOPGA */ + SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOPGA Volume", WM8991_LEFT_OPGA_VOLUME, + WM8991_LOPGAVOL_SHIFT, WM8991_LOPGAVOL_MASK, 0, out_pga_tlv), + SOC_SINGLE("LOPGA ZC Switch", WM8991_LEFT_OPGA_VOLUME, + WM8991_LOPGAZC_BIT, 1, 0), + + /* ROPGA */ + SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROPGA Volume", WM8991_RIGHT_OPGA_VOLUME, + WM8991_ROPGAVOL_SHIFT, WM8991_ROPGAVOL_MASK, 0, out_pga_tlv), + SOC_SINGLE("ROPGA ZC Switch", WM8991_RIGHT_OPGA_VOLUME, + WM8991_ROPGAZC_BIT, 1, 0), + + SOC_SINGLE("LON Mute Switch", WM8991_LINE_OUTPUTS_VOLUME, + WM8991_LONMUTE_BIT, 1, 0), + SOC_SINGLE("LOP Mute Switch", WM8991_LINE_OUTPUTS_VOLUME, + WM8991_LOPMUTE_BIT, 1, 0), + SOC_SINGLE("LOP Attenuation Switch", WM8991_LINE_OUTPUTS_VOLUME, + WM8991_LOATTN_BIT, 1, 0), + SOC_SINGLE("RON Mute Switch", WM8991_LINE_OUTPUTS_VOLUME, + WM8991_RONMUTE_BIT, 1, 0), + SOC_SINGLE("ROP Mute Switch", WM8991_LINE_OUTPUTS_VOLUME, + WM8991_ROPMUTE_BIT, 1, 0), + SOC_SINGLE("ROP Attenuation Switch", WM8991_LINE_OUTPUTS_VOLUME, + WM8991_ROATTN_BIT, 1, 0), + + SOC_SINGLE("OUT3 Mute Switch", WM8991_OUT3_4_VOLUME, + WM8991_OUT3MUTE_BIT, 1, 0), + SOC_SINGLE("OUT3 Attenuation Switch", WM8991_OUT3_4_VOLUME, + WM8991_OUT3ATTN_BIT, 1, 0), + + SOC_SINGLE("OUT4 Mute Switch", WM8991_OUT3_4_VOLUME, + WM8991_OUT4MUTE_BIT, 1, 0), + SOC_SINGLE("OUT4 Attenuation Switch", WM8991_OUT3_4_VOLUME, + WM8991_OUT4ATTN_BIT, 1, 0), + + SOC_SINGLE("Speaker Mode Switch", WM8991_CLASSD1, + WM8991_CDMODE_BIT, 1, 0), + + SOC_SINGLE("Speaker Output Attenuation Volume", WM8991_SPEAKER_VOLUME, + WM8991_SPKVOL_SHIFT, WM8991_SPKVOL_MASK, 0), + SOC_SINGLE("Speaker DC Boost Volume", WM8991_CLASSD3, + WM8991_DCGAIN_SHIFT, WM8991_DCGAIN_MASK, 0), + SOC_SINGLE("Speaker AC Boost Volume", WM8991_CLASSD3, + WM8991_ACGAIN_SHIFT, WM8991_ACGAIN_MASK, 0), + + SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left DAC Digital Volume", + WM8991_LEFT_DAC_DIGITAL_VOLUME, + WM8991_DACL_VOL_SHIFT, + WM8991_DACL_VOL_MASK, + 0, + out_dac_tlv), + + SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right DAC Digital Volume", + WM8991_RIGHT_DAC_DIGITAL_VOLUME, + WM8991_DACR_VOL_SHIFT, + WM8991_DACR_VOL_MASK, + 0, + out_dac_tlv), + + SOC_ENUM("Left Digital Sidetone", wm8991_left_digital_sidetone_enum), + SOC_ENUM("Right Digital Sidetone", wm8991_right_digital_sidetone_enum), + + SOC_SINGLE_TLV("Left Digital Sidetone Volume", WM8991_DIGITAL_SIDE_TONE, + WM8991_ADCL_DAC_SVOL_SHIFT, WM8991_ADCL_DAC_SVOL_MASK, 0, + out_sidetone_tlv), + SOC_SINGLE_TLV("Right Digital Sidetone Volume", WM8991_DIGITAL_SIDE_TONE, + WM8991_ADCR_DAC_SVOL_SHIFT, WM8991_ADCR_DAC_SVOL_MASK, 0, + out_sidetone_tlv), + + SOC_SINGLE("ADC Digital High Pass Filter Switch", WM8991_ADC_CTRL, + WM8991_ADC_HPF_ENA_BIT, 1, 0), + + SOC_ENUM("ADC HPF Mode", wm8991_right_adcmode_enum), + + SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left ADC Digital Volume", + WM8991_LEFT_ADC_DIGITAL_VOLUME, + WM8991_ADCL_VOL_SHIFT, + WM8991_ADCL_VOL_MASK, + 0, + in_adc_tlv), + + SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right ADC Digital Volume", + WM8991_RIGHT_ADC_DIGITAL_VOLUME, + WM8991_ADCR_VOL_SHIFT, + WM8991_ADCR_VOL_MASK, + 0, + in_adc_tlv), + + SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN12 Volume", + WM8991_LEFT_LINE_INPUT_1_2_VOLUME, + WM8991_LIN12VOL_SHIFT, + WM8991_LIN12VOL_MASK, + 0, + in_pga_tlv), + + SOC_SINGLE("LIN12 ZC Switch", WM8991_LEFT_LINE_INPUT_1_2_VOLUME, + WM8991_LI12ZC_BIT, 1, 0), + + SOC_SINGLE("LIN12 Mute Switch", WM8991_LEFT_LINE_INPUT_1_2_VOLUME, + WM8991_LI12MUTE_BIT, 1, 0), + + SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN34 Volume", + WM8991_LEFT_LINE_INPUT_3_4_VOLUME, + WM8991_LIN34VOL_SHIFT, + WM8991_LIN34VOL_MASK, + 0, + in_pga_tlv), + + SOC_SINGLE("LIN34 ZC Switch", WM8991_LEFT_LINE_INPUT_3_4_VOLUME, + WM8991_LI34ZC_BIT, 1, 0), + + SOC_SINGLE("LIN34 Mute Switch", WM8991_LEFT_LINE_INPUT_3_4_VOLUME, + WM8991_LI34MUTE_BIT, 1, 0), + + SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN12 Volume", + WM8991_RIGHT_LINE_INPUT_1_2_VOLUME, + WM8991_RIN12VOL_SHIFT, + WM8991_RIN12VOL_MASK, + 0, + in_pga_tlv), + + SOC_SINGLE("RIN12 ZC Switch", WM8991_RIGHT_LINE_INPUT_1_2_VOLUME, + WM8991_RI12ZC_BIT, 1, 0), + + SOC_SINGLE("RIN12 Mute Switch", WM8991_RIGHT_LINE_INPUT_1_2_VOLUME, + WM8991_RI12MUTE_BIT, 1, 0), + + SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN34 Volume", + WM8991_RIGHT_LINE_INPUT_3_4_VOLUME, + WM8991_RIN34VOL_SHIFT, + WM8991_RIN34VOL_MASK, + 0, + in_pga_tlv), + + SOC_SINGLE("RIN34 ZC Switch", WM8991_RIGHT_LINE_INPUT_3_4_VOLUME, + WM8991_RI34ZC_BIT, 1, 0), + + SOC_SINGLE("RIN34 Mute Switch", WM8991_RIGHT_LINE_INPUT_3_4_VOLUME, + WM8991_RI34MUTE_BIT, 1, 0), +}; + +/* + * _DAPM_ Controls + */ +static int inmixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + u16 reg, fakepower; + + reg = snd_soc_read(w->codec, WM8991_POWER_MANAGEMENT_2); + fakepower = snd_soc_read(w->codec, WM8991_INTDRIVBITS); + + if (fakepower & ((1 << WM8991_INMIXL_PWR_BIT) | + (1 << WM8991_AINLMUX_PWR_BIT))) + reg |= WM8991_AINL_ENA; + else + reg &= ~WM8991_AINL_ENA; + + if (fakepower & ((1 << WM8991_INMIXR_PWR_BIT) | + (1 << WM8991_AINRMUX_PWR_BIT))) + reg |= WM8991_AINR_ENA; + else + reg &= ~WM8991_AINL_ENA; + + snd_soc_write(w->codec, WM8991_POWER_MANAGEMENT_2, reg); + return 0; +} + +static int outmixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + u32 reg_shift = kcontrol->private_value & 0xfff; + int ret = 0; + u16 reg; + + switch (reg_shift) { + case WM8991_SPEAKER_MIXER | (WM8991_LDSPK_BIT << 8): + reg = snd_soc_read(w->codec, WM8991_OUTPUT_MIXER1); + if (reg & WM8991_LDLO) { + printk(KERN_WARNING + "Cannot set as Output Mixer 1 LDLO Set\n"); + ret = -1; + } + break; + + case WM8991_SPEAKER_MIXER | (WM8991_RDSPK_BIT << 8): + reg = snd_soc_read(w->codec, WM8991_OUTPUT_MIXER2); + if (reg & WM8991_RDRO) { + printk(KERN_WARNING + "Cannot set as Output Mixer 2 RDRO Set\n"); + ret = -1; + } + break; + + case WM8991_OUTPUT_MIXER1 | (WM8991_LDLO_BIT << 8): + reg = snd_soc_read(w->codec, WM8991_SPEAKER_MIXER); + if (reg & WM8991_LDSPK) { + printk(KERN_WARNING + "Cannot set as Speaker Mixer LDSPK Set\n"); + ret = -1; + } + break; + + case WM8991_OUTPUT_MIXER2 | (WM8991_RDRO_BIT << 8): + reg = snd_soc_read(w->codec, WM8991_SPEAKER_MIXER); + if (reg & WM8991_RDSPK) { + printk(KERN_WARNING + "Cannot set as Speaker Mixer RDSPK Set\n"); + ret = -1; + } + break; + } + + return ret; +} + +/* INMIX dB values */ +static const unsigned int in_mix_tlv[] = { + TLV_DB_RANGE_HEAD(1), + 0, 7, TLV_DB_LINEAR_ITEM(-1200, 600), +}; + +/* Left In PGA Connections */ +static const struct snd_kcontrol_new wm8991_dapm_lin12_pga_controls[] = { + SOC_DAPM_SINGLE("LIN1 Switch", WM8991_INPUT_MIXER2, WM8991_LMN1_BIT, 1, 0), + SOC_DAPM_SINGLE("LIN2 Switch", WM8991_INPUT_MIXER2, WM8991_LMP2_BIT, 1, 0), +}; + +static const struct snd_kcontrol_new wm8991_dapm_lin34_pga_controls[] = { + SOC_DAPM_SINGLE("LIN3 Switch", WM8991_INPUT_MIXER2, WM8991_LMN3_BIT, 1, 0), + SOC_DAPM_SINGLE("LIN4 Switch", WM8991_INPUT_MIXER2, WM8991_LMP4_BIT, 1, 0), +}; + +/* Right In PGA Connections */ +static const struct snd_kcontrol_new wm8991_dapm_rin12_pga_controls[] = { + SOC_DAPM_SINGLE("RIN1 Switch", WM8991_INPUT_MIXER2, WM8991_RMN1_BIT, 1, 0), + SOC_DAPM_SINGLE("RIN2 Switch", WM8991_INPUT_MIXER2, WM8991_RMP2_BIT, 1, 0), +}; + +static const struct snd_kcontrol_new wm8991_dapm_rin34_pga_controls[] = { + SOC_DAPM_SINGLE("RIN3 Switch", WM8991_INPUT_MIXER2, WM8991_RMN3_BIT, 1, 0), + SOC_DAPM_SINGLE("RIN4 Switch", WM8991_INPUT_MIXER2, WM8991_RMP4_BIT, 1, 0), +}; + +/* INMIXL */ +static const struct snd_kcontrol_new wm8991_dapm_inmixl_controls[] = { + SOC_DAPM_SINGLE_TLV("Record Left Volume", WM8991_INPUT_MIXER3, + WM8991_LDBVOL_SHIFT, WM8991_LDBVOL_MASK, 0, in_mix_tlv), + SOC_DAPM_SINGLE_TLV("LIN2 Volume", WM8991_INPUT_MIXER5, WM8991_LI2BVOL_SHIFT, + 7, 0, in_mix_tlv), + SOC_DAPM_SINGLE("LINPGA12 Switch", WM8991_INPUT_MIXER3, WM8991_L12MNB_BIT, + 1, 0), + SOC_DAPM_SINGLE("LINPGA34 Switch", WM8991_INPUT_MIXER3, WM8991_L34MNB_BIT, + 1, 0), +}; + +/* INMIXR */ +static const struct snd_kcontrol_new wm8991_dapm_inmixr_controls[] = { + SOC_DAPM_SINGLE_TLV("Record Right Volume", WM8991_INPUT_MIXER4, + WM8991_RDBVOL_SHIFT, WM8991_RDBVOL_MASK, 0, in_mix_tlv), + SOC_DAPM_SINGLE_TLV("RIN2 Volume", WM8991_INPUT_MIXER6, WM8991_RI2BVOL_SHIFT, + 7, 0, in_mix_tlv), + SOC_DAPM_SINGLE("RINPGA12 Switch", WM8991_INPUT_MIXER3, WM8991_L12MNB_BIT, + 1, 0), + SOC_DAPM_SINGLE("RINPGA34 Switch", WM8991_INPUT_MIXER3, WM8991_L34MNB_BIT, + 1, 0), +}; + +/* AINLMUX */ +static const char *wm8991_ainlmux[] = +{"INMIXL Mix", "RXVOICE Mix", "DIFFINL Mix"}; + +static const struct soc_enum wm8991_ainlmux_enum = + SOC_ENUM_SINGLE(WM8991_INPUT_MIXER1, WM8991_AINLMODE_SHIFT, + ARRAY_SIZE(wm8991_ainlmux), wm8991_ainlmux); + +static const struct snd_kcontrol_new wm8991_dapm_ainlmux_controls = + SOC_DAPM_ENUM("Route", wm8991_ainlmux_enum); + +/* DIFFINL */ + +/* AINRMUX */ +static const char *wm8991_ainrmux[] = +{"INMIXR Mix", "RXVOICE Mix", "DIFFINR Mix"}; + +static const struct soc_enum wm8991_ainrmux_enum = + SOC_ENUM_SINGLE(WM8991_INPUT_MIXER1, WM8991_AINRMODE_SHIFT, + ARRAY_SIZE(wm8991_ainrmux), wm8991_ainrmux); + +static const struct snd_kcontrol_new wm8991_dapm_ainrmux_controls = + SOC_DAPM_ENUM("Route", wm8991_ainrmux_enum); + +/* RXVOICE */ +static const struct snd_kcontrol_new wm8991_dapm_rxvoice_controls[] = { + SOC_DAPM_SINGLE_TLV("LIN4RXN", WM8991_INPUT_MIXER5, WM8991_LR4BVOL_SHIFT, + WM8991_LR4BVOL_MASK, 0, in_mix_tlv), + SOC_DAPM_SINGLE_TLV("RIN4RXP", WM8991_INPUT_MIXER6, WM8991_RL4BVOL_SHIFT, + WM8991_RL4BVOL_MASK, 0, in_mix_tlv), +}; + +/* LOMIX */ +static const struct snd_kcontrol_new wm8991_dapm_lomix_controls[] = { + SOC_DAPM_SINGLE("LOMIX Right ADC Bypass Switch", WM8991_OUTPUT_MIXER1, + WM8991_LRBLO_BIT, 1, 0), + SOC_DAPM_SINGLE("LOMIX Left ADC Bypass Switch", WM8991_OUTPUT_MIXER1, + WM8991_LLBLO_BIT, 1, 0), + SOC_DAPM_SINGLE("LOMIX RIN3 Bypass Switch", WM8991_OUTPUT_MIXER1, + WM8991_LRI3LO_BIT, 1, 0), + SOC_DAPM_SINGLE("LOMIX LIN3 Bypass Switch", WM8991_OUTPUT_MIXER1, + WM8991_LLI3LO_BIT, 1, 0), + SOC_DAPM_SINGLE("LOMIX RIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER1, + WM8991_LR12LO_BIT, 1, 0), + SOC_DAPM_SINGLE("LOMIX LIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER1, + WM8991_LL12LO_BIT, 1, 0), + SOC_DAPM_SINGLE("LOMIX Left DAC Switch", WM8991_OUTPUT_MIXER1, + WM8991_LDLO_BIT, 1, 0), +}; + +/* ROMIX */ +static const struct snd_kcontrol_new wm8991_dapm_romix_controls[] = { + SOC_DAPM_SINGLE("ROMIX Left ADC Bypass Switch", WM8991_OUTPUT_MIXER2, + WM8991_RLBRO_BIT, 1, 0), + SOC_DAPM_SINGLE("ROMIX Right ADC Bypass Switch", WM8991_OUTPUT_MIXER2, + WM8991_RRBRO_BIT, 1, 0), + SOC_DAPM_SINGLE("ROMIX LIN3 Bypass Switch", WM8991_OUTPUT_MIXER2, + WM8991_RLI3RO_BIT, 1, 0), + SOC_DAPM_SINGLE("ROMIX RIN3 Bypass Switch", WM8991_OUTPUT_MIXER2, + WM8991_RRI3RO_BIT, 1, 0), + SOC_DAPM_SINGLE("ROMIX LIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER2, + WM8991_RL12RO_BIT, 1, 0), + SOC_DAPM_SINGLE("ROMIX RIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER2, + WM8991_RR12RO_BIT, 1, 0), + SOC_DAPM_SINGLE("ROMIX Right DAC Switch", WM8991_OUTPUT_MIXER2, + WM8991_RDRO_BIT, 1, 0), +}; + +/* LONMIX */ +static const struct snd_kcontrol_new wm8991_dapm_lonmix_controls[] = { + SOC_DAPM_SINGLE("LONMIX Left Mixer PGA Switch", WM8991_LINE_MIXER1, + WM8991_LLOPGALON_BIT, 1, 0), + SOC_DAPM_SINGLE("LONMIX Right Mixer PGA Switch", WM8991_LINE_MIXER1, + WM8991_LROPGALON_BIT, 1, 0), + SOC_DAPM_SINGLE("LONMIX Inverted LOP Switch", WM8991_LINE_MIXER1, + WM8991_LOPLON_BIT, 1, 0), +}; + +/* LOPMIX */ +static const struct snd_kcontrol_new wm8991_dapm_lopmix_controls[] = { + SOC_DAPM_SINGLE("LOPMIX Right Mic Bypass Switch", WM8991_LINE_MIXER1, + WM8991_LR12LOP_BIT, 1, 0), + SOC_DAPM_SINGLE("LOPMIX Left Mic Bypass Switch", WM8991_LINE_MIXER1, + WM8991_LL12LOP_BIT, 1, 0), + SOC_DAPM_SINGLE("LOPMIX Left Mixer PGA Switch", WM8991_LINE_MIXER1, + WM8991_LLOPGALOP_BIT, 1, 0), +}; + +/* RONMIX */ +static const struct snd_kcontrol_new wm8991_dapm_ronmix_controls[] = { + SOC_DAPM_SINGLE("RONMIX Right Mixer PGA Switch", WM8991_LINE_MIXER2, + WM8991_RROPGARON_BIT, 1, 0), + SOC_DAPM_SINGLE("RONMIX Left Mixer PGA Switch", WM8991_LINE_MIXER2, + WM8991_RLOPGARON_BIT, 1, 0), + SOC_DAPM_SINGLE("RONMIX Inverted ROP Switch", WM8991_LINE_MIXER2, + WM8991_ROPRON_BIT, 1, 0), +}; + +/* ROPMIX */ +static const struct snd_kcontrol_new wm8991_dapm_ropmix_controls[] = { + SOC_DAPM_SINGLE("ROPMIX Left Mic Bypass Switch", WM8991_LINE_MIXER2, + WM8991_RL12ROP_BIT, 1, 0), + SOC_DAPM_SINGLE("ROPMIX Right Mic Bypass Switch", WM8991_LINE_MIXER2, + WM8991_RR12ROP_BIT, 1, 0), + SOC_DAPM_SINGLE("ROPMIX Right Mixer PGA Switch", WM8991_LINE_MIXER2, + WM8991_RROPGAROP_BIT, 1, 0), +}; + +/* OUT3MIX */ +static const struct snd_kcontrol_new wm8991_dapm_out3mix_controls[] = { + SOC_DAPM_SINGLE("OUT3MIX LIN4RXN Bypass Switch", WM8991_OUT3_4_MIXER, + WM8991_LI4O3_BIT, 1, 0), + SOC_DAPM_SINGLE("OUT3MIX Left Out PGA Switch", WM8991_OUT3_4_MIXER, + WM8991_LPGAO3_BIT, 1, 0), +}; + +/* OUT4MIX */ +static const struct snd_kcontrol_new wm8991_dapm_out4mix_controls[] = { + SOC_DAPM_SINGLE("OUT4MIX Right Out PGA Switch", WM8991_OUT3_4_MIXER, + WM8991_RPGAO4_BIT, 1, 0), + SOC_DAPM_SINGLE("OUT4MIX RIN4RXP Bypass Switch", WM8991_OUT3_4_MIXER, + WM8991_RI4O4_BIT, 1, 0), +}; + +/* SPKMIX */ +static const struct snd_kcontrol_new wm8991_dapm_spkmix_controls[] = { + SOC_DAPM_SINGLE("SPKMIX LIN2 Bypass Switch", WM8991_SPEAKER_MIXER, + WM8991_LI2SPK_BIT, 1, 0), + SOC_DAPM_SINGLE("SPKMIX LADC Bypass Switch", WM8991_SPEAKER_MIXER, + WM8991_LB2SPK_BIT, 1, 0), + SOC_DAPM_SINGLE("SPKMIX Left Mixer PGA Switch", WM8991_SPEAKER_MIXER, + WM8991_LOPGASPK_BIT, 1, 0), + SOC_DAPM_SINGLE("SPKMIX Left DAC Switch", WM8991_SPEAKER_MIXER, + WM8991_LDSPK_BIT, 1, 0), + SOC_DAPM_SINGLE("SPKMIX Right DAC Switch", WM8991_SPEAKER_MIXER, + WM8991_RDSPK_BIT, 1, 0), + SOC_DAPM_SINGLE("SPKMIX Right Mixer PGA Switch", WM8991_SPEAKER_MIXER, + WM8991_ROPGASPK_BIT, 1, 0), + SOC_DAPM_SINGLE("SPKMIX RADC Bypass Switch", WM8991_SPEAKER_MIXER, + WM8991_RL12ROP_BIT, 1, 0), + SOC_DAPM_SINGLE("SPKMIX RIN2 Bypass Switch", WM8991_SPEAKER_MIXER, + WM8991_RI2SPK_BIT, 1, 0), +}; + +static const struct snd_soc_dapm_widget wm8991_dapm_widgets[] = { + /* Input Side */ + /* Input Lines */ + SND_SOC_DAPM_INPUT("LIN1"), + SND_SOC_DAPM_INPUT("LIN2"), + SND_SOC_DAPM_INPUT("LIN3"), + SND_SOC_DAPM_INPUT("LIN4RXN"), + SND_SOC_DAPM_INPUT("RIN3"), + SND_SOC_DAPM_INPUT("RIN4RXP"), + SND_SOC_DAPM_INPUT("RIN1"), + SND_SOC_DAPM_INPUT("RIN2"), + SND_SOC_DAPM_INPUT("Internal ADC Source"), + + /* DACs */ + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8991_POWER_MANAGEMENT_2, + WM8991_ADCL_ENA_BIT, 0), + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8991_POWER_MANAGEMENT_2, + WM8991_ADCR_ENA_BIT, 0), + + /* Input PGAs */ + SND_SOC_DAPM_MIXER("LIN12 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_LIN12_ENA_BIT, + 0, &wm8991_dapm_lin12_pga_controls[0], + ARRAY_SIZE(wm8991_dapm_lin12_pga_controls)), + SND_SOC_DAPM_MIXER("LIN34 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_LIN34_ENA_BIT, + 0, &wm8991_dapm_lin34_pga_controls[0], + ARRAY_SIZE(wm8991_dapm_lin34_pga_controls)), + SND_SOC_DAPM_MIXER("RIN12 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_RIN12_ENA_BIT, + 0, &wm8991_dapm_rin12_pga_controls[0], + ARRAY_SIZE(wm8991_dapm_rin12_pga_controls)), + SND_SOC_DAPM_MIXER("RIN34 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_RIN34_ENA_BIT, + 0, &wm8991_dapm_rin34_pga_controls[0], + ARRAY_SIZE(wm8991_dapm_rin34_pga_controls)), + + /* INMIXL */ + SND_SOC_DAPM_MIXER_E("INMIXL", WM8991_INTDRIVBITS, WM8991_INMIXL_PWR_BIT, 0, + &wm8991_dapm_inmixl_controls[0], + ARRAY_SIZE(wm8991_dapm_inmixl_controls), + inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + /* AINLMUX */ + SND_SOC_DAPM_MUX_E("AINLMUX", WM8991_INTDRIVBITS, WM8991_AINLMUX_PWR_BIT, 0, + &wm8991_dapm_ainlmux_controls, inmixer_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + /* INMIXR */ + SND_SOC_DAPM_MIXER_E("INMIXR", WM8991_INTDRIVBITS, WM8991_INMIXR_PWR_BIT, 0, + &wm8991_dapm_inmixr_controls[0], + ARRAY_SIZE(wm8991_dapm_inmixr_controls), + inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + /* AINRMUX */ + SND_SOC_DAPM_MUX_E("AINRMUX", WM8991_INTDRIVBITS, WM8991_AINRMUX_PWR_BIT, 0, + &wm8991_dapm_ainrmux_controls, inmixer_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + /* Output Side */ + /* DACs */ + SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8991_POWER_MANAGEMENT_3, + WM8991_DACL_ENA_BIT, 0), + SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8991_POWER_MANAGEMENT_3, + WM8991_DACR_ENA_BIT, 0), + + /* LOMIX */ + SND_SOC_DAPM_MIXER_E("LOMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LOMIX_ENA_BIT, + 0, &wm8991_dapm_lomix_controls[0], + ARRAY_SIZE(wm8991_dapm_lomix_controls), + outmixer_event, SND_SOC_DAPM_PRE_REG), + + /* LONMIX */ + SND_SOC_DAPM_MIXER("LONMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LON_ENA_BIT, 0, + &wm8991_dapm_lonmix_controls[0], + ARRAY_SIZE(wm8991_dapm_lonmix_controls)), + + /* LOPMIX */ + SND_SOC_DAPM_MIXER("LOPMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LOP_ENA_BIT, 0, + &wm8991_dapm_lopmix_controls[0], + ARRAY_SIZE(wm8991_dapm_lopmix_controls)), + + /* OUT3MIX */ + SND_SOC_DAPM_MIXER("OUT3MIX", WM8991_POWER_MANAGEMENT_1, WM8991_OUT3_ENA_BIT, 0, + &wm8991_dapm_out3mix_controls[0], + ARRAY_SIZE(wm8991_dapm_out3mix_controls)), + + /* SPKMIX */ + SND_SOC_DAPM_MIXER_E("SPKMIX", WM8991_POWER_MANAGEMENT_1, WM8991_SPK_ENA_BIT, 0, + &wm8991_dapm_spkmix_controls[0], + ARRAY_SIZE(wm8991_dapm_spkmix_controls), outmixer_event, + SND_SOC_DAPM_PRE_REG), + + /* OUT4MIX */ + SND_SOC_DAPM_MIXER("OUT4MIX", WM8991_POWER_MANAGEMENT_1, WM8991_OUT4_ENA_BIT, 0, + &wm8991_dapm_out4mix_controls[0], + ARRAY_SIZE(wm8991_dapm_out4mix_controls)), + + /* ROPMIX */ + SND_SOC_DAPM_MIXER("ROPMIX", WM8991_POWER_MANAGEMENT_3, WM8991_ROP_ENA_BIT, 0, + &wm8991_dapm_ropmix_controls[0], + ARRAY_SIZE(wm8991_dapm_ropmix_controls)), + + /* RONMIX */ + SND_SOC_DAPM_MIXER("RONMIX", WM8991_POWER_MANAGEMENT_3, WM8991_RON_ENA_BIT, 0, + &wm8991_dapm_ronmix_controls[0], + ARRAY_SIZE(wm8991_dapm_ronmix_controls)), + + /* ROMIX */ + SND_SOC_DAPM_MIXER_E("ROMIX", WM8991_POWER_MANAGEMENT_3, WM8991_ROMIX_ENA_BIT, + 0, &wm8991_dapm_romix_controls[0], + ARRAY_SIZE(wm8991_dapm_romix_controls), + outmixer_event, SND_SOC_DAPM_PRE_REG), + + /* LOUT PGA */ + SND_SOC_DAPM_PGA("LOUT PGA", WM8991_POWER_MANAGEMENT_1, WM8991_LOUT_ENA_BIT, 0, + NULL, 0), + + /* ROUT PGA */ + SND_SOC_DAPM_PGA("ROUT PGA", WM8991_POWER_MANAGEMENT_1, WM8991_ROUT_ENA_BIT, 0, + NULL, 0), + + /* LOPGA */ + SND_SOC_DAPM_PGA("LOPGA", WM8991_POWER_MANAGEMENT_3, WM8991_LOPGA_ENA_BIT, 0, + NULL, 0), + + /* ROPGA */ + SND_SOC_DAPM_PGA("ROPGA", WM8991_POWER_MANAGEMENT_3, WM8991_ROPGA_ENA_BIT, 0, + NULL, 0), + + /* MICBIAS */ + SND_SOC_DAPM_MICBIAS("MICBIAS", WM8991_POWER_MANAGEMENT_1, + WM8991_MICBIAS_ENA_BIT, 0), + + SND_SOC_DAPM_OUTPUT("LON"), + SND_SOC_DAPM_OUTPUT("LOP"), + SND_SOC_DAPM_OUTPUT("OUT3"), + SND_SOC_DAPM_OUTPUT("LOUT"), + SND_SOC_DAPM_OUTPUT("SPKN"), + SND_SOC_DAPM_OUTPUT("SPKP"), + SND_SOC_DAPM_OUTPUT("ROUT"), + SND_SOC_DAPM_OUTPUT("OUT4"), + SND_SOC_DAPM_OUTPUT("ROP"), + SND_SOC_DAPM_OUTPUT("RON"), + SND_SOC_DAPM_OUTPUT("OUT"), + + SND_SOC_DAPM_OUTPUT("Internal DAC Sink"), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /* Make DACs turn on when playing even if not mixed into any outputs */ + {"Internal DAC Sink", NULL, "Left DAC"}, + {"Internal DAC Sink", NULL, "Right DAC"}, + + /* Make ADCs turn on when recording even if not mixed from any inputs */ + {"Left ADC", NULL, "Internal ADC Source"}, + {"Right ADC", NULL, "Internal ADC Source"}, + + /* Input Side */ + /* LIN12 PGA */ + {"LIN12 PGA", "LIN1 Switch", "LIN1"}, + {"LIN12 PGA", "LIN2 Switch", "LIN2"}, + /* LIN34 PGA */ + {"LIN34 PGA", "LIN3 Switch", "LIN3"}, + {"LIN34 PGA", "LIN4 Switch", "LIN4RXN"}, + /* INMIXL */ + {"INMIXL", "Record Left Volume", "LOMIX"}, + {"INMIXL", "LIN2 Volume", "LIN2"}, + {"INMIXL", "LINPGA12 Switch", "LIN12 PGA"}, + {"INMIXL", "LINPGA34 Switch", "LIN34 PGA"}, + /* AINLMUX */ + {"AINLMUX", "INMIXL Mix", "INMIXL"}, + {"AINLMUX", "DIFFINL Mix", "LIN12 PGA"}, + {"AINLMUX", "DIFFINL Mix", "LIN34 PGA"}, + {"AINLMUX", "RXVOICE Mix", "LIN4RXN"}, + {"AINLMUX", "RXVOICE Mix", "RIN4RXP"}, + /* ADC */ + {"Left ADC", NULL, "AINLMUX"}, + + /* RIN12 PGA */ + {"RIN12 PGA", "RIN1 Switch", "RIN1"}, + {"RIN12 PGA", "RIN2 Switch", "RIN2"}, + /* RIN34 PGA */ + {"RIN34 PGA", "RIN3 Switch", "RIN3"}, + {"RIN34 PGA", "RIN4 Switch", "RIN4RXP"}, + /* INMIXL */ + {"INMIXR", "Record Right Volume", "ROMIX"}, + {"INMIXR", "RIN2 Volume", "RIN2"}, + {"INMIXR", "RINPGA12 Switch", "RIN12 PGA"}, + {"INMIXR", "RINPGA34 Switch", "RIN34 PGA"}, + /* AINRMUX */ + {"AINRMUX", "INMIXR Mix", "INMIXR"}, + {"AINRMUX", "DIFFINR Mix", "RIN12 PGA"}, + {"AINRMUX", "DIFFINR Mix", "RIN34 PGA"}, + {"AINRMUX", "RXVOICE Mix", "LIN4RXN"}, + {"AINRMUX", "RXVOICE Mix", "RIN4RXP"}, + /* ADC */ + {"Right ADC", NULL, "AINRMUX"}, + + /* LOMIX */ + {"LOMIX", "LOMIX RIN3 Bypass Switch", "RIN3"}, + {"LOMIX", "LOMIX LIN3 Bypass Switch", "LIN3"}, + {"LOMIX", "LOMIX LIN12 PGA Bypass Switch", "LIN12 PGA"}, + {"LOMIX", "LOMIX RIN12 PGA Bypass Switch", "RIN12 PGA"}, + {"LOMIX", "LOMIX Right ADC Bypass Switch", "AINRMUX"}, + {"LOMIX", "LOMIX Left ADC Bypass Switch", "AINLMUX"}, + {"LOMIX", "LOMIX Left DAC Switch", "Left DAC"}, + + /* ROMIX */ + {"ROMIX", "ROMIX RIN3 Bypass Switch", "RIN3"}, + {"ROMIX", "ROMIX LIN3 Bypass Switch", "LIN3"}, + {"ROMIX", "ROMIX LIN12 PGA Bypass Switch", "LIN12 PGA"}, + {"ROMIX", "ROMIX RIN12 PGA Bypass Switch", "RIN12 PGA"}, + {"ROMIX", "ROMIX Right ADC Bypass Switch", "AINRMUX"}, + {"ROMIX", "ROMIX Left ADC Bypass Switch", "AINLMUX"}, + {"ROMIX", "ROMIX Right DAC Switch", "Right DAC"}, + + /* SPKMIX */ + {"SPKMIX", "SPKMIX LIN2 Bypass Switch", "LIN2"}, + {"SPKMIX", "SPKMIX RIN2 Bypass Switch", "RIN2"}, + {"SPKMIX", "SPKMIX LADC Bypass Switch", "AINLMUX"}, + {"SPKMIX", "SPKMIX RADC Bypass Switch", "AINRMUX"}, + {"SPKMIX", "SPKMIX Left Mixer PGA Switch", "LOPGA"}, + {"SPKMIX", "SPKMIX Right Mixer PGA Switch", "ROPGA"}, + {"SPKMIX", "SPKMIX Right DAC Switch", "Right DAC"}, + {"SPKMIX", "SPKMIX Left DAC Switch", "Right DAC"}, + + /* LONMIX */ + {"LONMIX", "LONMIX Left Mixer PGA Switch", "LOPGA"}, + {"LONMIX", "LONMIX Right Mixer PGA Switch", "ROPGA"}, + {"LONMIX", "LONMIX Inverted LOP Switch", "LOPMIX"}, + + /* LOPMIX */ + {"LOPMIX", "LOPMIX Right Mic Bypass Switch", "RIN12 PGA"}, + {"LOPMIX", "LOPMIX Left Mic Bypass Switch", "LIN12 PGA"}, + {"LOPMIX", "LOPMIX Left Mixer PGA Switch", "LOPGA"}, + + /* OUT3MIX */ + {"OUT3MIX", "OUT3MIX LIN4RXN Bypass Switch", "LIN4RXN"}, + {"OUT3MIX", "OUT3MIX Left Out PGA Switch", "LOPGA"}, + + /* OUT4MIX */ + {"OUT4MIX", "OUT4MIX Right Out PGA Switch", "ROPGA"}, + {"OUT4MIX", "OUT4MIX RIN4RXP Bypass Switch", "RIN4RXP"}, + + /* RONMIX */ + {"RONMIX", "RONMIX Right Mixer PGA Switch", "ROPGA"}, + {"RONMIX", "RONMIX Left Mixer PGA Switch", "LOPGA"}, + {"RONMIX", "RONMIX Inverted ROP Switch", "ROPMIX"}, + + /* ROPMIX */ + {"ROPMIX", "ROPMIX Left Mic Bypass Switch", "LIN12 PGA"}, + {"ROPMIX", "ROPMIX Right Mic Bypass Switch", "RIN12 PGA"}, + {"ROPMIX", "ROPMIX Right Mixer PGA Switch", "ROPGA"}, + + /* Out Mixer PGAs */ + {"LOPGA", NULL, "LOMIX"}, + {"ROPGA", NULL, "ROMIX"}, + + {"LOUT PGA", NULL, "LOMIX"}, + {"ROUT PGA", NULL, "ROMIX"}, + + /* Output Pins */ + {"LON", NULL, "LONMIX"}, + {"LOP", NULL, "LOPMIX"}, + {"OUT", NULL, "OUT3MIX"}, + {"LOUT", NULL, "LOUT PGA"}, + {"SPKN", NULL, "SPKMIX"}, + {"ROUT", NULL, "ROUT PGA"}, + {"OUT4", NULL, "OUT4MIX"}, + {"ROP", NULL, "ROPMIX"}, + {"RON", NULL, "RONMIX"}, +}; + +/* PLL divisors */ +struct _pll_div { + u32 div2; + u32 n; + u32 k; +}; + +/* The size in bits of the pll divide multiplied by 10 + * to allow rounding later */ +#define FIXED_PLL_SIZE ((1 << 16) * 10) + +static void pll_factors(struct _pll_div *pll_div, unsigned int target, + unsigned int source) +{ + u64 Kpart; + unsigned int K, Ndiv, Nmod; + + + Ndiv = target / source; + if (Ndiv < 6) { + source >>= 1; + pll_div->div2 = 1; + Ndiv = target / source; + } else + pll_div->div2 = 0; + + if ((Ndiv < 6) || (Ndiv > 12)) + printk(KERN_WARNING + "WM8991 N value outwith recommended range! N = %d\n", Ndiv); + + pll_div->n = Ndiv; + Nmod = target % source; + Kpart = FIXED_PLL_SIZE * (long long)Nmod; + + do_div(Kpart, source); + + K = Kpart & 0xFFFFFFFF; + + /* Check if we need to round */ + if ((K % 10) >= 5) + K += 5; + + /* Move down to proper range now rounding is done */ + K /= 10; + + pll_div->k = K; +} + +static int wm8991_set_dai_pll(struct snd_soc_dai *codec_dai, + int pll_id, int src, unsigned int freq_in, unsigned int freq_out) +{ + u16 reg; + struct snd_soc_codec *codec = codec_dai->codec; + struct _pll_div pll_div; + + if (freq_in && freq_out) { + pll_factors(&pll_div, freq_out * 4, freq_in); + + /* Turn on PLL */ + reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_2); + reg |= WM8991_PLL_ENA; + snd_soc_write(codec, WM8991_POWER_MANAGEMENT_2, reg); + + /* sysclk comes from PLL */ + reg = snd_soc_read(codec, WM8991_CLOCKING_2); + snd_soc_write(codec, WM8991_CLOCKING_2, reg | WM8991_SYSCLK_SRC); + + /* set up N , fractional mode and pre-divisor if neccessary */ + snd_soc_write(codec, WM8991_PLL1, pll_div.n | WM8991_SDM | + (pll_div.div2 ? WM8991_PRESCALE : 0)); + snd_soc_write(codec, WM8991_PLL2, (u8)(pll_div.k>>8)); + snd_soc_write(codec, WM8991_PLL3, (u8)(pll_div.k & 0xFF)); + } else { + /* Turn on PLL */ + reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_2); + reg &= ~WM8991_PLL_ENA; + snd_soc_write(codec, WM8991_POWER_MANAGEMENT_2, reg); + } + return 0; +} + +/* + * Set's ADC and Voice DAC format. + */ +static int wm8991_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 audio1, audio3; + + audio1 = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_1); + audio3 = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_3); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + audio3 &= ~WM8991_AIF_MSTR1; + break; + case SND_SOC_DAIFMT_CBM_CFM: + audio3 |= WM8991_AIF_MSTR1; + break; + default: + return -EINVAL; + } + + audio1 &= ~WM8991_AIF_FMT_MASK; + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + audio1 |= WM8991_AIF_TMF_I2S; + audio1 &= ~WM8991_AIF_LRCLK_INV; + break; + case SND_SOC_DAIFMT_RIGHT_J: + audio1 |= WM8991_AIF_TMF_RIGHTJ; + audio1 &= ~WM8991_AIF_LRCLK_INV; + break; + case SND_SOC_DAIFMT_LEFT_J: + audio1 |= WM8991_AIF_TMF_LEFTJ; + audio1 &= ~WM8991_AIF_LRCLK_INV; + break; + case SND_SOC_DAIFMT_DSP_A: + audio1 |= WM8991_AIF_TMF_DSP; + audio1 &= ~WM8991_AIF_LRCLK_INV; + break; + case SND_SOC_DAIFMT_DSP_B: + audio1 |= WM8991_AIF_TMF_DSP | WM8991_AIF_LRCLK_INV; + break; + default: + return -EINVAL; + } + + snd_soc_write(codec, WM8991_AUDIO_INTERFACE_1, audio1); + snd_soc_write(codec, WM8991_AUDIO_INTERFACE_3, audio3); + return 0; +} + +static int wm8991_set_dai_clkdiv(struct snd_soc_dai *codec_dai, + int div_id, int div) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 reg; + + switch (div_id) { + case WM8991_MCLK_DIV: + reg = snd_soc_read(codec, WM8991_CLOCKING_2) & + ~WM8991_MCLK_DIV_MASK; + snd_soc_write(codec, WM8991_CLOCKING_2, reg | div); + break; + case WM8991_DACCLK_DIV: + reg = snd_soc_read(codec, WM8991_CLOCKING_2) & + ~WM8991_DAC_CLKDIV_MASK; + snd_soc_write(codec, WM8991_CLOCKING_2, reg | div); + break; + case WM8991_ADCCLK_DIV: + reg = snd_soc_read(codec, WM8991_CLOCKING_2) & + ~WM8991_ADC_CLKDIV_MASK; + snd_soc_write(codec, WM8991_CLOCKING_2, reg | div); + break; + case WM8991_BCLK_DIV: + reg = snd_soc_read(codec, WM8991_CLOCKING_1) & + ~WM8991_BCLK_DIV_MASK; + snd_soc_write(codec, WM8991_CLOCKING_1, reg | div); + break; + default: + return -EINVAL; + } + + return 0; +} + +/* + * Set PCM DAI bit size and sample rate. + */ +static int wm8991_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + u16 audio1 = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_1); + + audio1 &= ~WM8991_AIF_WL_MASK; + /* bit size */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + audio1 |= WM8991_AIF_WL_20BITS; + break; + case SNDRV_PCM_FORMAT_S24_LE: + audio1 |= WM8991_AIF_WL_24BITS; + break; + case SNDRV_PCM_FORMAT_S32_LE: + audio1 |= WM8991_AIF_WL_32BITS; + break; + } + + snd_soc_write(codec, WM8991_AUDIO_INTERFACE_1, audio1); + return 0; +} + +static int wm8991_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + u16 val; + + val = snd_soc_read(codec, WM8991_DAC_CTRL) & ~WM8991_DAC_MUTE; + if (mute) + snd_soc_write(codec, WM8991_DAC_CTRL, val | WM8991_DAC_MUTE); + else + snd_soc_write(codec, WM8991_DAC_CTRL, val); + return 0; +} + +static int wm8991_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + u16 val; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* VMID=2*50k */ + val = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_1) & + ~WM8991_VMID_MODE_MASK; + snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, val | 0x2); + break; + + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + snd_soc_cache_sync(codec); + /* Enable all output discharge bits */ + snd_soc_write(codec, WM8991_ANTIPOP1, WM8991_DIS_LLINE | + WM8991_DIS_RLINE | WM8991_DIS_OUT3 | + WM8991_DIS_OUT4 | WM8991_DIS_LOUT | + WM8991_DIS_ROUT); + + /* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */ + snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST | + WM8991_BUFDCOPEN | WM8991_POBCTRL | + WM8991_VMIDTOG); + + /* Delay to allow output caps to discharge */ + msleep(300); + + /* Disable VMIDTOG */ + snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST | + WM8991_BUFDCOPEN | WM8991_POBCTRL); + + /* disable all output discharge bits */ + snd_soc_write(codec, WM8991_ANTIPOP1, 0); + + /* Enable outputs */ + snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1b00); + + msleep(50); + + /* Enable VMID at 2x50k */ + snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f02); + + msleep(100); + + /* Enable VREF */ + snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f03); + + msleep(600); + + /* Enable BUFIOEN */ + snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST | + WM8991_BUFDCOPEN | WM8991_POBCTRL | + WM8991_BUFIOEN); + + /* Disable outputs */ + snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x3); + + /* disable POBCTRL, SOFT_ST and BUFDCOPEN */ + snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_BUFIOEN); + } + + /* VMID=2*250k */ + val = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_1) & + ~WM8991_VMID_MODE_MASK; + snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, val | 0x4); + break; + + case SND_SOC_BIAS_OFF: + /* Enable POBCTRL and SOFT_ST */ + snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST | + WM8991_POBCTRL | WM8991_BUFIOEN); + + /* Enable POBCTRL, SOFT_ST and BUFDCOPEN */ + snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST | + WM8991_BUFDCOPEN | WM8991_POBCTRL | + WM8991_BUFIOEN); + + /* mute DAC */ + val = snd_soc_read(codec, WM8991_DAC_CTRL); + snd_soc_write(codec, WM8991_DAC_CTRL, val | WM8991_DAC_MUTE); + + /* Enable any disabled outputs */ + snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f03); + + /* Disable VMID */ + snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f01); + + msleep(300); + + /* Enable all output discharge bits */ + snd_soc_write(codec, WM8991_ANTIPOP1, WM8991_DIS_LLINE | + WM8991_DIS_RLINE | WM8991_DIS_OUT3 | + WM8991_DIS_OUT4 | WM8991_DIS_LOUT | + WM8991_DIS_ROUT); + + /* Disable VREF */ + snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x0); + + /* disable POBCTRL, SOFT_ST and BUFDCOPEN */ + snd_soc_write(codec, WM8991_ANTIPOP2, 0x0); + codec->cache_sync = 1; + break; + } + + codec->dapm.bias_level = level; + return 0; +} + +static int wm8991_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ + wm8991_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int wm8991_resume(struct snd_soc_codec *codec) +{ + wm8991_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + return 0; +} + +/* power down chip */ +static int wm8991_remove(struct snd_soc_codec *codec) +{ + wm8991_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int wm8991_probe(struct snd_soc_codec *codec) +{ + struct wm8991_priv *wm8991; + int ret; + unsigned int reg; + + wm8991 = snd_soc_codec_get_drvdata(codec); + + ret = snd_soc_codec_set_cache_io(codec, 8, 16, wm8991->control_type); + if (ret < 0) { + dev_err(codec->dev, "Failed to set cache i/o: %d\n", ret); + return ret; + } + + ret = wm8991_reset(codec); + if (ret < 0) { + dev_err(codec->dev, "Failed to issue reset\n"); + return ret; + } + + wm8991_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + reg = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_4); + snd_soc_write(codec, WM8991_AUDIO_INTERFACE_4, reg | WM8991_ALRCGPIO1); + + reg = snd_soc_read(codec, WM8991_GPIO1_GPIO2) & + ~WM8991_GPIO1_SEL_MASK; + snd_soc_write(codec, WM8991_GPIO1_GPIO2, reg | 1); + + reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_1); + snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, reg | WM8991_VREF_ENA| + WM8991_VMID_MODE_MASK); + + reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_2); + snd_soc_write(codec, WM8991_POWER_MANAGEMENT_2, reg | WM8991_OPCLK_ENA); + + snd_soc_write(codec, WM8991_DAC_CTRL, 0); + snd_soc_write(codec, WM8991_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8)); + snd_soc_write(codec, WM8991_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8)); + + snd_soc_add_controls(codec, wm8991_snd_controls, + ARRAY_SIZE(wm8991_snd_controls)); + + snd_soc_dapm_new_controls(&codec->dapm, wm8991_dapm_widgets, + ARRAY_SIZE(wm8991_dapm_widgets)); + snd_soc_dapm_add_routes(&codec->dapm, audio_map, + ARRAY_SIZE(audio_map)); + return 0; +} + +#define WM8991_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_ops wm8991_ops = { + .hw_params = wm8991_hw_params, + .digital_mute = wm8991_mute, + .set_fmt = wm8991_set_dai_fmt, + .set_clkdiv = wm8991_set_dai_clkdiv, + .set_pll = wm8991_set_dai_pll +}; + +/* + * The WM8991 supports 2 different and mutually exclusive DAI + * configurations. + * + * 1. ADC/DAC on Primary Interface + * 2. ADC on Primary Interface/DAC on secondary + */ +static struct snd_soc_dai_driver wm8991_dai = { + /* ADC/DAC on primary */ + .name = "wm8991", + .id = 1, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = WM8991_FORMATS + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = WM8991_FORMATS + }, + .ops = &wm8991_ops +}; + +static struct snd_soc_codec_driver soc_codec_dev_wm8991 = { + .probe = wm8991_probe, + .remove = wm8991_remove, + .suspend = wm8991_suspend, + .resume = wm8991_resume, + .set_bias_level = wm8991_set_bias_level, + .reg_cache_size = WM8991_MAX_REGISTER + 1, + .reg_word_size = sizeof(u16), + .reg_cache_default = wm8991_reg_defs +}; + +static __devinit int wm8991_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct wm8991_priv *wm8991; + int ret; + + wm8991 = kzalloc(sizeof *wm8991, GFP_KERNEL); + if (!wm8991) + return -ENOMEM; + + wm8991->control_type = SND_SOC_I2C; + i2c_set_clientdata(i2c, wm8991); + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_wm8991, &wm8991_dai, 1); + if (ret < 0) + kfree(wm8991); + return ret; +} + +static __devexit int wm8991_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + kfree(i2c_get_clientdata(client)); + return 0; +} + +static const struct i2c_device_id wm8991_i2c_id[] = { + { "wm8991", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8991_i2c_id); + +static struct i2c_driver wm8991_i2c_driver = { + .driver = { + .name = "wm8991", + .owner = THIS_MODULE, + }, + .probe = wm8991_i2c_probe, + .remove = __devexit_p(wm8991_i2c_remove), + .id_table = wm8991_i2c_id, +}; + +static int __init wm8991_modinit(void) +{ + int ret; + ret = i2c_add_driver(&wm8991_i2c_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register WM8991 I2C driver: %d\n", + ret); + } + return 0; +} +module_init(wm8991_modinit); + +static void __exit wm8991_exit(void) +{ + i2c_del_driver(&wm8991_i2c_driver); +} +module_exit(wm8991_exit); + +MODULE_DESCRIPTION("ASoC WM8991 driver"); +MODULE_AUTHOR("Graeme Gregory"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8991.h b/sound/soc/codecs/wm8991.h new file mode 100644 index 000000000000..8a942efd18a5 --- /dev/null +++ b/sound/soc/codecs/wm8991.h @@ -0,0 +1,833 @@ +/* + * wm8991.h -- audio driver for WM8991 + * + * 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 as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef _WM8991_H +#define _WM8991_H + +/* + * Register values. + */ +#define WM8991_RESET 0x00 +#define WM8991_POWER_MANAGEMENT_1 0x01 +#define WM8991_POWER_MANAGEMENT_2 0x02 +#define WM8991_POWER_MANAGEMENT_3 0x03 +#define WM8991_AUDIO_INTERFACE_1 0x04 +#define WM8991_AUDIO_INTERFACE_2 0x05 +#define WM8991_CLOCKING_1 0x06 +#define WM8991_CLOCKING_2 0x07 +#define WM8991_AUDIO_INTERFACE_3 0x08 +#define WM8991_AUDIO_INTERFACE_4 0x09 +#define WM8991_DAC_CTRL 0x0A +#define WM8991_LEFT_DAC_DIGITAL_VOLUME 0x0B +#define WM8991_RIGHT_DAC_DIGITAL_VOLUME 0x0C +#define WM8991_DIGITAL_SIDE_TONE 0x0D +#define WM8991_ADC_CTRL 0x0E +#define WM8991_LEFT_ADC_DIGITAL_VOLUME 0x0F +#define WM8991_RIGHT_ADC_DIGITAL_VOLUME 0x10 +#define WM8991_GPIO_CTRL_1 0x12 +#define WM8991_GPIO1_GPIO2 0x13 +#define WM8991_GPIO3_GPIO4 0x14 +#define WM8991_GPIO5_GPIO6 0x15 +#define WM8991_GPIOCTRL_2 0x16 +#define WM8991_GPIO_POL 0x17 +#define WM8991_LEFT_LINE_INPUT_1_2_VOLUME 0x18 +#define WM8991_LEFT_LINE_INPUT_3_4_VOLUME 0x19 +#define WM8991_RIGHT_LINE_INPUT_1_2_VOLUME 0x1A +#define WM8991_RIGHT_LINE_INPUT_3_4_VOLUME 0x1B +#define WM8991_LEFT_OUTPUT_VOLUME 0x1C +#define WM8991_RIGHT_OUTPUT_VOLUME 0x1D +#define WM8991_LINE_OUTPUTS_VOLUME 0x1E +#define WM8991_OUT3_4_VOLUME 0x1F +#define WM8991_LEFT_OPGA_VOLUME 0x20 +#define WM8991_RIGHT_OPGA_VOLUME 0x21 +#define WM8991_SPEAKER_VOLUME 0x22 +#define WM8991_CLASSD1 0x23 +#define WM8991_CLASSD3 0x25 +#define WM8991_INPUT_MIXER1 0x27 +#define WM8991_INPUT_MIXER2 0x28 +#define WM8991_INPUT_MIXER3 0x29 +#define WM8991_INPUT_MIXER4 0x2A +#define WM8991_INPUT_MIXER5 0x2B +#define WM8991_INPUT_MIXER6 0x2C +#define WM8991_OUTPUT_MIXER1 0x2D +#define WM8991_OUTPUT_MIXER2 0x2E +#define WM8991_OUTPUT_MIXER3 0x2F +#define WM8991_OUTPUT_MIXER4 0x30 +#define WM8991_OUTPUT_MIXER5 0x31 +#define WM8991_OUTPUT_MIXER6 0x32 +#define WM8991_OUT3_4_MIXER 0x33 +#define WM8991_LINE_MIXER1 0x34 +#define WM8991_LINE_MIXER2 0x35 +#define WM8991_SPEAKER_MIXER 0x36 +#define WM8991_ADDITIONAL_CONTROL 0x37 +#define WM8991_ANTIPOP1 0x38 +#define WM8991_ANTIPOP2 0x39 +#define WM8991_MICBIAS 0x3A +#define WM8991_PLL1 0x3C +#define WM8991_PLL2 0x3D +#define WM8991_PLL3 0x3E +#define WM8991_INTDRIVBITS 0x3F + +#define WM8991_REGISTER_COUNT 60 +#define WM8991_MAX_REGISTER 0x3F + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - Reset + */ +#define WM8991_SW_RESET_CHIP_ID_MASK 0xFFFF /* SW_RESET_CHIP_ID - [15:0] */ + +/* + * R1 (0x01) - Power Management (1) + */ +#define WM8991_SPK_ENA 0x1000 /* SPK_ENA */ +#define WM8991_SPK_ENA_BIT 12 +#define WM8991_OUT3_ENA 0x0800 /* OUT3_ENA */ +#define WM8991_OUT3_ENA_BIT 11 +#define WM8991_OUT4_ENA 0x0400 /* OUT4_ENA */ +#define WM8991_OUT4_ENA_BIT 10 +#define WM8991_LOUT_ENA 0x0200 /* LOUT_ENA */ +#define WM8991_LOUT_ENA_BIT 9 +#define WM8991_ROUT_ENA 0x0100 /* ROUT_ENA */ +#define WM8991_ROUT_ENA_BIT 8 +#define WM8991_MICBIAS_ENA 0x0010 /* MICBIAS_ENA */ +#define WM8991_MICBIAS_ENA_BIT 4 +#define WM8991_VMID_MODE_MASK 0x0006 /* VMID_MODE - [2:1] */ +#define WM8991_VREF_ENA 0x0001 /* VREF_ENA */ +#define WM8991_VREF_ENA_BIT 0 + +/* + * R2 (0x02) - Power Management (2) + */ +#define WM8991_PLL_ENA 0x8000 /* PLL_ENA */ +#define WM8991_PLL_ENA_BIT 15 +#define WM8991_TSHUT_ENA 0x4000 /* TSHUT_ENA */ +#define WM8991_TSHUT_ENA_BIT 14 +#define WM8991_TSHUT_OPDIS 0x2000 /* TSHUT_OPDIS */ +#define WM8991_TSHUT_OPDIS_BIT 13 +#define WM8991_OPCLK_ENA 0x0800 /* OPCLK_ENA */ +#define WM8991_OPCLK_ENA_BIT 11 +#define WM8991_AINL_ENA 0x0200 /* AINL_ENA */ +#define WM8991_AINL_ENA_BIT 9 +#define WM8991_AINR_ENA 0x0100 /* AINR_ENA */ +#define WM8991_AINR_ENA_BIT 8 +#define WM8991_LIN34_ENA 0x0080 /* LIN34_ENA */ +#define WM8991_LIN34_ENA_BIT 7 +#define WM8991_LIN12_ENA 0x0040 /* LIN12_ENA */ +#define WM8991_LIN12_ENA_BIT 6 +#define WM8991_RIN34_ENA 0x0020 /* RIN34_ENA */ +#define WM8991_RIN34_ENA_BIT 5 +#define WM8991_RIN12_ENA 0x0010 /* RIN12_ENA */ +#define WM8991_RIN12_ENA_BIT 4 +#define WM8991_ADCL_ENA 0x0002 /* ADCL_ENA */ +#define WM8991_ADCL_ENA_BIT 1 +#define WM8991_ADCR_ENA 0x0001 /* ADCR_ENA */ +#define WM8991_ADCR_ENA_BIT 0 + +/* + * R3 (0x03) - Power Management (3) + */ +#define WM8991_LON_ENA 0x2000 /* LON_ENA */ +#define WM8991_LON_ENA_BIT 13 +#define WM8991_LOP_ENA 0x1000 /* LOP_ENA */ +#define WM8991_LOP_ENA_BIT 12 +#define WM8991_RON_ENA 0x0800 /* RON_ENA */ +#define WM8991_RON_ENA_BIT 11 +#define WM8991_ROP_ENA 0x0400 /* ROP_ENA */ +#define WM8991_ROP_ENA_BIT 10 +#define WM8991_LOPGA_ENA 0x0080 /* LOPGA_ENA */ +#define WM8991_LOPGA_ENA_BIT 7 +#define WM8991_ROPGA_ENA 0x0040 /* ROPGA_ENA */ +#define WM8991_ROPGA_ENA_BIT 6 +#define WM8991_LOMIX_ENA 0x0020 /* LOMIX_ENA */ +#define WM8991_LOMIX_ENA_BIT 5 +#define WM8991_ROMIX_ENA 0x0010 /* ROMIX_ENA */ +#define WM8991_ROMIX_ENA_BIT 4 +#define WM8991_DACL_ENA 0x0002 /* DACL_ENA */ +#define WM8991_DACL_ENA_BIT 1 +#define WM8991_DACR_ENA 0x0001 /* DACR_ENA */ +#define WM8991_DACR_ENA_BIT 0 + +/* + * R4 (0x04) - Audio Interface (1) + */ +#define WM8991_AIFADCL_SRC 0x8000 /* AIFADCL_SRC */ +#define WM8991_AIFADCR_SRC 0x4000 /* AIFADCR_SRC */ +#define WM8991_AIFADC_TDM 0x2000 /* AIFADC_TDM */ +#define WM8991_AIFADC_TDM_CHAN 0x1000 /* AIFADC_TDM_CHAN */ +#define WM8991_AIF_BCLK_INV 0x0100 /* AIF_BCLK_INV */ +#define WM8991_AIF_LRCLK_INV 0x0080 /* AIF_LRCLK_INV */ +#define WM8991_AIF_WL_MASK 0x0060 /* AIF_WL - [6:5] */ +#define WM8991_AIF_WL_16BITS (0 << 5) +#define WM8991_AIF_WL_20BITS (1 << 5) +#define WM8991_AIF_WL_24BITS (2 << 5) +#define WM8991_AIF_WL_32BITS (3 << 5) +#define WM8991_AIF_FMT_MASK 0x0018 /* AIF_FMT - [4:3] */ +#define WM8991_AIF_TMF_RIGHTJ (0 << 3) +#define WM8991_AIF_TMF_LEFTJ (1 << 3) +#define WM8991_AIF_TMF_I2S (2 << 3) +#define WM8991_AIF_TMF_DSP (3 << 3) + +/* + * R5 (0x05) - Audio Interface (2) + */ +#define WM8991_DACL_SRC 0x8000 /* DACL_SRC */ +#define WM8991_DACR_SRC 0x4000 /* DACR_SRC */ +#define WM8991_AIFDAC_TDM 0x2000 /* AIFDAC_TDM */ +#define WM8991_AIFDAC_TDM_CHAN 0x1000 /* AIFDAC_TDM_CHAN */ +#define WM8991_DAC_BOOST_MASK 0x0C00 /* DAC_BOOST - [11:10] */ +#define WM8991_DAC_COMP 0x0010 /* DAC_COMP */ +#define WM8991_DAC_COMPMODE 0x0008 /* DAC_COMPMODE */ +#define WM8991_ADC_COMP 0x0004 /* ADC_COMP */ +#define WM8991_ADC_COMPMODE 0x0002 /* ADC_COMPMODE */ +#define WM8991_LOOPBACK 0x0001 /* LOOPBACK */ + +/* + * R6 (0x06) - Clocking (1) + */ +#define WM8991_TOCLK_RATE 0x8000 /* TOCLK_RATE */ +#define WM8991_TOCLK_ENA 0x4000 /* TOCLK_ENA */ +#define WM8991_OPCLKDIV_MASK 0x1E00 /* OPCLKDIV - [12:9] */ +#define WM8991_DCLKDIV_MASK 0x01C0 /* DCLKDIV - [8:6] */ +#define WM8991_BCLK_DIV_MASK 0x001E /* BCLK_DIV - [4:1] */ +#define WM8991_BCLK_DIV_1 (0x0 << 1) +#define WM8991_BCLK_DIV_1_5 (0x1 << 1) +#define WM8991_BCLK_DIV_2 (0x2 << 1) +#define WM8991_BCLK_DIV_3 (0x3 << 1) +#define WM8991_BCLK_DIV_4 (0x4 << 1) +#define WM8991_BCLK_DIV_5_5 (0x5 << 1) +#define WM8991_BCLK_DIV_6 (0x6 << 1) +#define WM8991_BCLK_DIV_8 (0x7 << 1) +#define WM8991_BCLK_DIV_11 (0x8 << 1) +#define WM8991_BCLK_DIV_12 (0x9 << 1) +#define WM8991_BCLK_DIV_16 (0xA << 1) +#define WM8991_BCLK_DIV_22 (0xB << 1) +#define WM8991_BCLK_DIV_24 (0xC << 1) +#define WM8991_BCLK_DIV_32 (0xD << 1) +#define WM8991_BCLK_DIV_44 (0xE << 1) +#define WM8991_BCLK_DIV_48 (0xF << 1) + +/* + * R7 (0x07) - Clocking (2) + */ +#define WM8991_MCLK_SRC 0x8000 /* MCLK_SRC */ +#define WM8991_SYSCLK_SRC 0x4000 /* SYSCLK_SRC */ +#define WM8991_CLK_FORCE 0x2000 /* CLK_FORCE */ +#define WM8991_MCLK_DIV_MASK 0x1800 /* MCLK_DIV - [12:11] */ +#define WM8991_MCLK_DIV_1 (0 << 11) +#define WM8991_MCLK_DIV_2 ( 2 << 11) +#define WM8991_MCLK_INV 0x0400 /* MCLK_INV */ +#define WM8991_ADC_CLKDIV_MASK 0x00E0 /* ADC_CLKDIV - [7:5] */ +#define WM8991_ADC_CLKDIV_1 (0 << 5) +#define WM8991_ADC_CLKDIV_1_5 (1 << 5) +#define WM8991_ADC_CLKDIV_2 (2 << 5) +#define WM8991_ADC_CLKDIV_3 (3 << 5) +#define WM8991_ADC_CLKDIV_4 (4 << 5) +#define WM8991_ADC_CLKDIV_5_5 (5 << 5) +#define WM8991_ADC_CLKDIV_6 (6 << 5) +#define WM8991_DAC_CLKDIV_MASK 0x001C /* DAC_CLKDIV - [4:2] */ +#define WM8991_DAC_CLKDIV_1 (0 << 2) +#define WM8991_DAC_CLKDIV_1_5 (1 << 2) +#define WM8991_DAC_CLKDIV_2 (2 << 2) +#define WM8991_DAC_CLKDIV_3 (3 << 2) +#define WM8991_DAC_CLKDIV_4 (4 << 2) +#define WM8991_DAC_CLKDIV_5_5 (5 << 2) +#define WM8991_DAC_CLKDIV_6 (6 << 2) + +/* + * R8 (0x08) - Audio Interface (3) + */ +#define WM8991_AIF_MSTR1 0x8000 /* AIF_MSTR1 */ +#define WM8991_AIF_MSTR2 0x4000 /* AIF_MSTR2 */ +#define WM8991_AIF_SEL 0x2000 /* AIF_SEL */ +#define WM8991_ADCLRC_DIR 0x0800 /* ADCLRC_DIR */ +#define WM8991_ADCLRC_RATE_MASK 0x07FF /* ADCLRC_RATE - [10:0] */ + +/* + * R9 (0x09) - Audio Interface (4) + */ +#define WM8991_ALRCGPIO1 0x8000 /* ALRCGPIO1 */ +#define WM8991_ALRCBGPIO6 0x4000 /* ALRCBGPIO6 */ +#define WM8991_AIF_TRIS 0x2000 /* AIF_TRIS */ +#define WM8991_DACLRC_DIR 0x0800 /* DACLRC_DIR */ +#define WM8991_DACLRC_RATE_MASK 0x07FF /* DACLRC_RATE - [10:0] */ + +/* + * R10 (0x0A) - DAC CTRL + */ +#define WM8991_AIF_LRCLKRATE 0x0400 /* AIF_LRCLKRATE */ +#define WM8991_DAC_MONO 0x0200 /* DAC_MONO */ +#define WM8991_DAC_SB_FILT 0x0100 /* DAC_SB_FILT */ +#define WM8991_DAC_MUTERATE 0x0080 /* DAC_MUTERATE */ +#define WM8991_DAC_MUTEMODE 0x0040 /* DAC_MUTEMODE */ +#define WM8991_DEEMP_MASK 0x0030 /* DEEMP - [5:4] */ +#define WM8991_DAC_MUTE 0x0004 /* DAC_MUTE */ +#define WM8991_DACL_DATINV 0x0002 /* DACL_DATINV */ +#define WM8991_DACR_DATINV 0x0001 /* DACR_DATINV */ + +/* + * R11 (0x0B) - Left DAC Digital Volume + */ +#define WM8991_DAC_VU 0x0100 /* DAC_VU */ +#define WM8991_DACL_VOL_MASK 0x00FF /* DACL_VOL - [7:0] */ +#define WM8991_DACL_VOL_SHIFT 0 +/* + * R12 (0x0C) - Right DAC Digital Volume + */ +#define WM8991_DAC_VU 0x0100 /* DAC_VU */ +#define WM8991_DACR_VOL_MASK 0x00FF /* DACR_VOL - [7:0] */ +#define WM8991_DACR_VOL_SHIFT 0 +/* + * R13 (0x0D) - Digital Side Tone + */ +#define WM8991_ADCL_DAC_SVOL_MASK 0x0F /* ADCL_DAC_SVOL - [12:9] */ +#define WM8991_ADCL_DAC_SVOL_SHIFT 9 +#define WM8991_ADCR_DAC_SVOL_MASK 0x0F /* ADCR_DAC_SVOL - [8:5] */ +#define WM8991_ADCR_DAC_SVOL_SHIFT 5 +#define WM8991_ADC_TO_DACL_MASK 0x03 /* ADC_TO_DACL - [3:2] */ +#define WM8991_ADC_TO_DACL_SHIFT 2 +#define WM8991_ADC_TO_DACR_MASK 0x03 /* ADC_TO_DACR - [1:0] */ +#define WM8991_ADC_TO_DACR_SHIFT 0 + +/* + * R14 (0x0E) - ADC CTRL + */ +#define WM8991_ADC_HPF_ENA 0x0100 /* ADC_HPF_ENA */ +#define WM8991_ADC_HPF_ENA_BIT 8 +#define WM8991_ADC_HPF_CUT_MASK 0x03 /* ADC_HPF_CUT - [6:5] */ +#define WM8991_ADC_HPF_CUT_SHIFT 5 +#define WM8991_ADCL_DATINV 0x0002 /* ADCL_DATINV */ +#define WM8991_ADCL_DATINV_BIT 1 +#define WM8991_ADCR_DATINV 0x0001 /* ADCR_DATINV */ +#define WM8991_ADCR_DATINV_BIT 0 + +/* + * R15 (0x0F) - Left ADC Digital Volume + */ +#define WM8991_ADC_VU 0x0100 /* ADC_VU */ +#define WM8991_ADCL_VOL_MASK 0x00FF /* ADCL_VOL - [7:0] */ +#define WM8991_ADCL_VOL_SHIFT 0 + +/* + * R16 (0x10) - Right ADC Digital Volume + */ +#define WM8991_ADC_VU 0x0100 /* ADC_VU */ +#define WM8991_ADCR_VOL_MASK 0x00FF /* ADCR_VOL - [7:0] */ +#define WM8991_ADCR_VOL_SHIFT 0 + +/* + * R18 (0x12) - GPIO CTRL 1 + */ +#define WM8991_IRQ 0x1000 /* IRQ */ +#define WM8991_TEMPOK 0x0800 /* TEMPOK */ +#define WM8991_MICSHRT 0x0400 /* MICSHRT */ +#define WM8991_MICDET 0x0200 /* MICDET */ +#define WM8991_PLL_LCK 0x0100 /* PLL_LCK */ +#define WM8991_GPI8_STATUS 0x0080 /* GPI8_STATUS */ +#define WM8991_GPI7_STATUS 0x0040 /* GPI7_STATUS */ +#define WM8991_GPIO6_STATUS 0x0020 /* GPIO6_STATUS */ +#define WM8991_GPIO5_STATUS 0x0010 /* GPIO5_STATUS */ +#define WM8991_GPIO4_STATUS 0x0008 /* GPIO4_STATUS */ +#define WM8991_GPIO3_STATUS 0x0004 /* GPIO3_STATUS */ +#define WM8991_GPIO2_STATUS 0x0002 /* GPIO2_STATUS */ +#define WM8991_GPIO1_STATUS 0x0001 /* GPIO1_STATUS */ + +/* + * R19 (0x13) - GPIO1 & GPIO2 + */ +#define WM8991_GPIO2_DEB_ENA 0x8000 /* GPIO2_DEB_ENA */ +#define WM8991_GPIO2_IRQ_ENA 0x4000 /* GPIO2_IRQ_ENA */ +#define WM8991_GPIO2_PU 0x2000 /* GPIO2_PU */ +#define WM8991_GPIO2_PD 0x1000 /* GPIO2_PD */ +#define WM8991_GPIO2_SEL_MASK 0x0F00 /* GPIO2_SEL - [11:8] */ +#define WM8991_GPIO1_DEB_ENA 0x0080 /* GPIO1_DEB_ENA */ +#define WM8991_GPIO1_IRQ_ENA 0x0040 /* GPIO1_IRQ_ENA */ +#define WM8991_GPIO1_PU 0x0020 /* GPIO1_PU */ +#define WM8991_GPIO1_PD 0x0010 /* GPIO1_PD */ +#define WM8991_GPIO1_SEL_MASK 0x000F /* GPIO1_SEL - [3:0] */ + +/* + * R20 (0x14) - GPIO3 & GPIO4 + */ +#define WM8991_GPIO4_DEB_ENA 0x8000 /* GPIO4_DEB_ENA */ +#define WM8991_GPIO4_IRQ_ENA 0x4000 /* GPIO4_IRQ_ENA */ +#define WM8991_GPIO4_PU 0x2000 /* GPIO4_PU */ +#define WM8991_GPIO4_PD 0x1000 /* GPIO4_PD */ +#define WM8991_GPIO4_SEL_MASK 0x0F00 /* GPIO4_SEL - [11:8] */ +#define WM8991_GPIO3_DEB_ENA 0x0080 /* GPIO3_DEB_ENA */ +#define WM8991_GPIO3_IRQ_ENA 0x0040 /* GPIO3_IRQ_ENA */ +#define WM8991_GPIO3_PU 0x0020 /* GPIO3_PU */ +#define WM8991_GPIO3_PD 0x0010 /* GPIO3_PD */ +#define WM8991_GPIO3_SEL_MASK 0x000F /* GPIO3_SEL - [3:0] */ + +/* + * R21 (0x15) - GPIO5 & GPIO6 + */ +#define WM8991_GPIO6_DEB_ENA 0x8000 /* GPIO6_DEB_ENA */ +#define WM8991_GPIO6_IRQ_ENA 0x4000 /* GPIO6_IRQ_ENA */ +#define WM8991_GPIO6_PU 0x2000 /* GPIO6_PU */ +#define WM8991_GPIO6_PD 0x1000 /* GPIO6_PD */ +#define WM8991_GPIO6_SEL_MASK 0x0F00 /* GPIO6_SEL - [11:8] */ +#define WM8991_GPIO5_DEB_ENA 0x0080 /* GPIO5_DEB_ENA */ +#define WM8991_GPIO5_IRQ_ENA 0x0040 /* GPIO5_IRQ_ENA */ +#define WM8991_GPIO5_PU 0x0020 /* GPIO5_PU */ +#define WM8991_GPIO5_PD 0x0010 /* GPIO5_PD */ +#define WM8991_GPIO5_SEL_MASK 0x000F /* GPIO5_SEL - [3:0] */ + +/* + * R22 (0x16) - GPIOCTRL 2 + */ +#define WM8991_RD_3W_ENA 0x8000 /* RD_3W_ENA */ +#define WM8991_MODE_3W4W 0x4000 /* MODE_3W4W */ +#define WM8991_TEMPOK_IRQ_ENA 0x0800 /* TEMPOK_IRQ_ENA */ +#define WM8991_MICSHRT_IRQ_ENA 0x0400 /* MICSHRT_IRQ_ENA */ +#define WM8991_MICDET_IRQ_ENA 0x0200 /* MICDET_IRQ_ENA */ +#define WM8991_PLL_LCK_IRQ_ENA 0x0100 /* PLL_LCK_IRQ_ENA */ +#define WM8991_GPI8_DEB_ENA 0x0080 /* GPI8_DEB_ENA */ +#define WM8991_GPI8_IRQ_ENA 0x0040 /* GPI8_IRQ_ENA */ +#define WM8991_GPI8_ENA 0x0010 /* GPI8_ENA */ +#define WM8991_GPI7_DEB_ENA 0x0008 /* GPI7_DEB_ENA */ +#define WM8991_GPI7_IRQ_ENA 0x0004 /* GPI7_IRQ_ENA */ +#define WM8991_GPI7_ENA 0x0001 /* GPI7_ENA */ + +/* + * R23 (0x17) - GPIO_POL + */ +#define WM8991_IRQ_INV 0x1000 /* IRQ_INV */ +#define WM8991_TEMPOK_POL 0x0800 /* TEMPOK_POL */ +#define WM8991_MICSHRT_POL 0x0400 /* MICSHRT_POL */ +#define WM8991_MICDET_POL 0x0200 /* MICDET_POL */ +#define WM8991_PLL_LCK_POL 0x0100 /* PLL_LCK_POL */ +#define WM8991_GPI8_POL 0x0080 /* GPI8_POL */ +#define WM8991_GPI7_POL 0x0040 /* GPI7_POL */ +#define WM8991_GPIO6_POL 0x0020 /* GPIO6_POL */ +#define WM8991_GPIO5_POL 0x0010 /* GPIO5_POL */ +#define WM8991_GPIO4_POL 0x0008 /* GPIO4_POL */ +#define WM8991_GPIO3_POL 0x0004 /* GPIO3_POL */ +#define WM8991_GPIO2_POL 0x0002 /* GPIO2_POL */ +#define WM8991_GPIO1_POL 0x0001 /* GPIO1_POL */ + +/* + * R24 (0x18) - Left Line Input 1&2 Volume + */ +#define WM8991_IPVU 0x0100 /* IPVU */ +#define WM8991_LI12MUTE 0x0080 /* LI12MUTE */ +#define WM8991_LI12MUTE_BIT 7 +#define WM8991_LI12ZC 0x0040 /* LI12ZC */ +#define WM8991_LI12ZC_BIT 6 +#define WM8991_LIN12VOL_MASK 0x001F /* LIN12VOL - [4:0] */ +#define WM8991_LIN12VOL_SHIFT 0 +/* + * R25 (0x19) - Left Line Input 3&4 Volume + */ +#define WM8991_IPVU 0x0100 /* IPVU */ +#define WM8991_LI34MUTE 0x0080 /* LI34MUTE */ +#define WM8991_LI34MUTE_BIT 7 +#define WM8991_LI34ZC 0x0040 /* LI34ZC */ +#define WM8991_LI34ZC_BIT 6 +#define WM8991_LIN34VOL_MASK 0x001F /* LIN34VOL - [4:0] */ +#define WM8991_LIN34VOL_SHIFT 0 + +/* + * R26 (0x1A) - Right Line Input 1&2 Volume + */ +#define WM8991_IPVU 0x0100 /* IPVU */ +#define WM8991_RI12MUTE 0x0080 /* RI12MUTE */ +#define WM8991_RI12MUTE_BIT 7 +#define WM8991_RI12ZC 0x0040 /* RI12ZC */ +#define WM8991_RI12ZC_BIT 6 +#define WM8991_RIN12VOL_MASK 0x001F /* RIN12VOL - [4:0] */ +#define WM8991_RIN12VOL_SHIFT 0 + +/* + * R27 (0x1B) - Right Line Input 3&4 Volume + */ +#define WM8991_IPVU 0x0100 /* IPVU */ +#define WM8991_RI34MUTE 0x0080 /* RI34MUTE */ +#define WM8991_RI34MUTE_BIT 7 +#define WM8991_RI34ZC 0x0040 /* RI34ZC */ +#define WM8991_RI34ZC_BIT 6 +#define WM8991_RIN34VOL_MASK 0x001F /* RIN34VOL - [4:0] */ +#define WM8991_RIN34VOL_SHIFT 0 + +/* + * R28 (0x1C) - Left Output Volume + */ +#define WM8991_OPVU 0x0100 /* OPVU */ +#define WM8991_LOZC 0x0080 /* LOZC */ +#define WM8991_LOZC_BIT 7 +#define WM8991_LOUTVOL_MASK 0x007F /* LOUTVOL - [6:0] */ +#define WM8991_LOUTVOL_SHIFT 0 +/* + * R29 (0x1D) - Right Output Volume + */ +#define WM8991_OPVU 0x0100 /* OPVU */ +#define WM8991_ROZC 0x0080 /* ROZC */ +#define WM8991_ROZC_BIT 7 +#define WM8991_ROUTVOL_MASK 0x007F /* ROUTVOL - [6:0] */ +#define WM8991_ROUTVOL_SHIFT 0 +/* + * R30 (0x1E) - Line Outputs Volume + */ +#define WM8991_LONMUTE 0x0040 /* LONMUTE */ +#define WM8991_LONMUTE_BIT 6 +#define WM8991_LOPMUTE 0x0020 /* LOPMUTE */ +#define WM8991_LOPMUTE_BIT 5 +#define WM8991_LOATTN 0x0010 /* LOATTN */ +#define WM8991_LOATTN_BIT 4 +#define WM8991_RONMUTE 0x0004 /* RONMUTE */ +#define WM8991_RONMUTE_BIT 2 +#define WM8991_ROPMUTE 0x0002 /* ROPMUTE */ +#define WM8991_ROPMUTE_BIT 1 +#define WM8991_ROATTN 0x0001 /* ROATTN */ +#define WM8991_ROATTN_BIT 0 + +/* + * R31 (0x1F) - Out3/4 Volume + */ +#define WM8991_OUT3MUTE 0x0020 /* OUT3MUTE */ +#define WM8991_OUT3MUTE_BIT 5 +#define WM8991_OUT3ATTN 0x0010 /* OUT3ATTN */ +#define WM8991_OUT3ATTN_BIT 4 +#define WM8991_OUT4MUTE 0x0002 /* OUT4MUTE */ +#define WM8991_OUT4MUTE_BIT 1 +#define WM8991_OUT4ATTN 0x0001 /* OUT4ATTN */ +#define WM8991_OUT4ATTN_BIT 0 + +/* + * R32 (0x20) - Left OPGA Volume + */ +#define WM8991_OPVU 0x0100 /* OPVU */ +#define WM8991_LOPGAZC 0x0080 /* LOPGAZC */ +#define WM8991_LOPGAZC_BIT 7 +#define WM8991_LOPGAVOL_MASK 0x007F /* LOPGAVOL - [6:0] */ +#define WM8991_LOPGAVOL_SHIFT 0 + +/* + * R33 (0x21) - Right OPGA Volume + */ +#define WM8991_OPVU 0x0100 /* OPVU */ +#define WM8991_ROPGAZC 0x0080 /* ROPGAZC */ +#define WM8991_ROPGAZC_BIT 7 +#define WM8991_ROPGAVOL_MASK 0x007F /* ROPGAVOL - [6:0] */ +#define WM8991_ROPGAVOL_SHIFT 0 +/* + * R34 (0x22) - Speaker Volume + */ +#define WM8991_SPKVOL_MASK 0x0003 /* SPKVOL - [1:0] */ +#define WM8991_SPKVOL_SHIFT 0 + +/* + * R35 (0x23) - ClassD1 + */ +#define WM8991_CDMODE 0x0100 /* CDMODE */ +#define WM8991_CDMODE_BIT 8 + +/* + * R37 (0x25) - ClassD3 + */ +#define WM8991_DCGAIN_MASK 0x0007 /* DCGAIN - [5:3] */ +#define WM8991_DCGAIN_SHIFT 3 +#define WM8991_ACGAIN_MASK 0x0007 /* ACGAIN - [2:0] */ +#define WM8991_ACGAIN_SHIFT 0 +/* + * R39 (0x27) - Input Mixer1 + */ +#define WM8991_AINLMODE_MASK 0x000C /* AINLMODE - [3:2] */ +#define WM8991_AINLMODE_SHIFT 2 +#define WM8991_AINRMODE_MASK 0x0003 /* AINRMODE - [1:0] */ +#define WM8991_AINRMODE_SHIFT 0 + +/* + * R40 (0x28) - Input Mixer2 + */ +#define WM8991_LMP4 0x0080 /* LMP4 */ +#define WM8991_LMP4_BIT 7 /* LMP4 */ +#define WM8991_LMN3 0x0040 /* LMN3 */ +#define WM8991_LMN3_BIT 6 /* LMN3 */ +#define WM8991_LMP2 0x0020 /* LMP2 */ +#define WM8991_LMP2_BIT 5 /* LMP2 */ +#define WM8991_LMN1 0x0010 /* LMN1 */ +#define WM8991_LMN1_BIT 4 /* LMN1 */ +#define WM8991_RMP4 0x0008 /* RMP4 */ +#define WM8991_RMP4_BIT 3 /* RMP4 */ +#define WM8991_RMN3 0x0004 /* RMN3 */ +#define WM8991_RMN3_BIT 2 /* RMN3 */ +#define WM8991_RMP2 0x0002 /* RMP2 */ +#define WM8991_RMP2_BIT 1 /* RMP2 */ +#define WM8991_RMN1 0x0001 /* RMN1 */ +#define WM8991_RMN1_BIT 0 /* RMN1 */ + +/* + * R41 (0x29) - Input Mixer3 + */ +#define WM8991_L34MNB 0x0100 /* L34MNB */ +#define WM8991_L34MNB_BIT 8 +#define WM8991_L34MNBST 0x0080 /* L34MNBST */ +#define WM8991_L34MNBST_BIT 7 +#define WM8991_L12MNB 0x0020 /* L12MNB */ +#define WM8991_L12MNB_BIT 5 +#define WM8991_L12MNBST 0x0010 /* L12MNBST */ +#define WM8991_L12MNBST_BIT 4 +#define WM8991_LDBVOL_MASK 0x0007 /* LDBVOL - [2:0] */ +#define WM8991_LDBVOL_SHIFT 0 + +/* + * R42 (0x2A) - Input Mixer4 + */ +#define WM8991_R34MNB 0x0100 /* R34MNB */ +#define WM8991_R34MNB_BIT 8 +#define WM8991_R34MNBST 0x0080 /* R34MNBST */ +#define WM8991_R34MNBST_BIT 7 +#define WM8991_R12MNB 0x0020 /* R12MNB */ +#define WM8991_R12MNB_BIT 5 +#define WM8991_R12MNBST 0x0010 /* R12MNBST */ +#define WM8991_R12MNBST_BIT 4 +#define WM8991_RDBVOL_MASK 0x0007 /* RDBVOL - [2:0] */ +#define WM8991_RDBVOL_SHIFT 0 + +/* + * R43 (0x2B) - Input Mixer5 + */ +#define WM8991_LI2BVOL_MASK 0x07 /* LI2BVOL - [8:6] */ +#define WM8991_LI2BVOL_SHIFT 6 +#define WM8991_LR4BVOL_MASK 0x07 /* LR4BVOL - [5:3] */ +#define WM8991_LR4BVOL_SHIFT 3 +#define WM8991_LL4BVOL_MASK 0x07 /* LL4BVOL - [2:0] */ +#define WM8991_LL4BVOL_SHIFT 0 + +/* + * R44 (0x2C) - Input Mixer6 + */ +#define WM8991_RI2BVOL_MASK 0x07 /* RI2BVOL - [8:6] */ +#define WM8991_RI2BVOL_SHIFT 6 +#define WM8991_RL4BVOL_MASK 0x07 /* RL4BVOL - [5:3] */ +#define WM8991_RL4BVOL_SHIFT 3 +#define WM8991_RR4BVOL_MASK 0x07 /* RR4BVOL - [2:0] */ +#define WM8991_RR4BVOL_SHIFT 0 + +/* + * R45 (0x2D) - Output Mixer1 + */ +#define WM8991_LRBLO 0x0080 /* LRBLO */ +#define WM8991_LRBLO_BIT 7 +#define WM8991_LLBLO 0x0040 /* LLBLO */ +#define WM8991_LLBLO_BIT 6 +#define WM8991_LRI3LO 0x0020 /* LRI3LO */ +#define WM8991_LRI3LO_BIT 5 +#define WM8991_LLI3LO 0x0010 /* LLI3LO */ +#define WM8991_LLI3LO_BIT 4 +#define WM8991_LR12LO 0x0008 /* LR12LO */ +#define WM8991_LR12LO_BIT 3 +#define WM8991_LL12LO 0x0004 /* LL12LO */ +#define WM8991_LL12LO_BIT 2 +#define WM8991_LDLO 0x0001 /* LDLO */ +#define WM8991_LDLO_BIT 0 + +/* + * R46 (0x2E) - Output Mixer2 + */ +#define WM8991_RLBRO 0x0080 /* RLBRO */ +#define WM8991_RLBRO_BIT 7 +#define WM8991_RRBRO 0x0040 /* RRBRO */ +#define WM8991_RRBRO_BIT 6 +#define WM8991_RLI3RO 0x0020 /* RLI3RO */ +#define WM8991_RLI3RO_BIT 5 +#define WM8991_RRI3RO 0x0010 /* RRI3RO */ +#define WM8991_RRI3RO_BIT 4 +#define WM8991_RL12RO 0x0008 /* RL12RO */ +#define WM8991_RL12RO_BIT 3 +#define WM8991_RR12RO 0x0004 /* RR12RO */ +#define WM8991_RR12RO_BIT 2 +#define WM8991_RDRO 0x0001 /* RDRO */ +#define WM8991_RDRO_BIT 0 + +/* + * R47 (0x2F) - Output Mixer3 + */ +#define WM8991_LLI3LOVOL_MASK 0x07 /* LLI3LOVOL - [8:6] */ +#define WM8991_LLI3LOVOL_SHIFT 6 +#define WM8991_LR12LOVOL_MASK 0x07 /* LR12LOVOL - [5:3] */ +#define WM8991_LR12LOVOL_SHIFT 3 +#define WM8991_LL12LOVOL_MASK 0x07 /* LL12LOVOL - [2:0] */ +#define WM8991_LL12LOVOL_SHIFT 0 + +/* + * R48 (0x30) - Output Mixer4 + */ +#define WM8991_RRI3ROVOL_MASK 0x07 /* RRI3ROVOL - [8:6] */ +#define WM8991_RRI3ROVOL_SHIFT 6 +#define WM8991_RL12ROVOL_MASK 0x07 /* RL12ROVOL - [5:3] */ +#define WM8991_RL12ROVOL_SHIFT 3 +#define WM8991_RR12ROVOL_MASK 0x07 /* RR12ROVOL - [2:0] */ +#define WM8991_RR12ROVOL_SHIFT 0 + +/* + * R49 (0x31) - Output Mixer5 + */ +#define WM8991_LRI3LOVOL_MASK 0x07 /* LRI3LOVOL - [8:6] */ +#define WM8991_LRI3LOVOL_SHIFT 6 +#define WM8991_LRBLOVOL_MASK 0x07 /* LRBLOVOL - [5:3] */ +#define WM8991_LRBLOVOL_SHIFT 3 +#define WM8991_LLBLOVOL_MASK 0x07 /* LLBLOVOL - [2:0] */ +#define WM8991_LLBLOVOL_SHIFT 0 + +/* + * R50 (0x32) - Output Mixer6 + */ +#define WM8991_RLI3ROVOL_MASK 0x07 /* RLI3ROVOL - [8:6] */ +#define WM8991_RLI3ROVOL_SHIFT 6 +#define WM8991_RLBROVOL_MASK 0x07 /* RLBROVOL - [5:3] */ +#define WM8991_RLBROVOL_SHIFT 3 +#define WM8991_RRBROVOL_MASK 0x07 /* RRBROVOL - [2:0] */ +#define WM8991_RRBROVOL_SHIFT 0 + +/* + * R51 (0x33) - Out3/4 Mixer + */ +#define WM8991_VSEL_MASK 0x0180 /* VSEL - [8:7] */ +#define WM8991_LI4O3 0x0020 /* LI4O3 */ +#define WM8991_LI4O3_BIT 5 +#define WM8991_LPGAO3 0x0010 /* LPGAO3 */ +#define WM8991_LPGAO3_BIT 4 +#define WM8991_RI4O4 0x0002 /* RI4O4 */ +#define WM8991_RI4O4_BIT 1 +#define WM8991_RPGAO4 0x0001 /* RPGAO4 */ +#define WM8991_RPGAO4_BIT 0 +/* + * R52 (0x34) - Line Mixer1 + */ +#define WM8991_LLOPGALON 0x0040 /* LLOPGALON */ +#define WM8991_LLOPGALON_BIT 6 +#define WM8991_LROPGALON 0x0020 /* LROPGALON */ +#define WM8991_LROPGALON_BIT 5 +#define WM8991_LOPLON 0x0010 /* LOPLON */ +#define WM8991_LOPLON_BIT 4 +#define WM8991_LR12LOP 0x0004 /* LR12LOP */ +#define WM8991_LR12LOP_BIT 2 +#define WM8991_LL12LOP 0x0002 /* LL12LOP */ +#define WM8991_LL12LOP_BIT 1 +#define WM8991_LLOPGALOP 0x0001 /* LLOPGALOP */ +#define WM8991_LLOPGALOP_BIT 0 +/* + * R53 (0x35) - Line Mixer2 + */ +#define WM8991_RROPGARON 0x0040 /* RROPGARON */ +#define WM8991_RROPGARON_BIT 6 +#define WM8991_RLOPGARON 0x0020 /* RLOPGARON */ +#define WM8991_RLOPGARON_BIT 5 +#define WM8991_ROPRON 0x0010 /* ROPRON */ +#define WM8991_ROPRON_BIT 4 +#define WM8991_RL12ROP 0x0004 /* RL12ROP */ +#define WM8991_RL12ROP_BIT 2 +#define WM8991_RR12ROP 0x0002 /* RR12ROP */ +#define WM8991_RR12ROP_BIT 1 +#define WM8991_RROPGAROP 0x0001 /* RROPGAROP */ +#define WM8991_RROPGAROP_BIT 0 + +/* + * R54 (0x36) - Speaker Mixer + */ +#define WM8991_LB2SPK 0x0080 /* LB2SPK */ +#define WM8991_LB2SPK_BIT 7 +#define WM8991_RB2SPK 0x0040 /* RB2SPK */ +#define WM8991_RB2SPK_BIT 6 +#define WM8991_LI2SPK 0x0020 /* LI2SPK */ +#define WM8991_LI2SPK_BIT 5 +#define WM8991_RI2SPK 0x0010 /* RI2SPK */ +#define WM8991_RI2SPK_BIT 4 +#define WM8991_LOPGASPK 0x0008 /* LOPGASPK */ +#define WM8991_LOPGASPK_BIT 3 +#define WM8991_ROPGASPK 0x0004 /* ROPGASPK */ +#define WM8991_ROPGASPK_BIT 2 +#define WM8991_LDSPK 0x0002 /* LDSPK */ +#define WM8991_LDSPK_BIT 1 +#define WM8991_RDSPK 0x0001 /* RDSPK */ +#define WM8991_RDSPK_BIT 0 + +/* + * R55 (0x37) - Additional Control + */ +#define WM8991_VROI 0x0001 /* VROI */ + +/* + * R56 (0x38) - AntiPOP1 + */ +#define WM8991_DIS_LLINE 0x0020 /* DIS_LLINE */ +#define WM8991_DIS_RLINE 0x0010 /* DIS_RLINE */ +#define WM8991_DIS_OUT3 0x0008 /* DIS_OUT3 */ +#define WM8991_DIS_OUT4 0x0004 /* DIS_OUT4 */ +#define WM8991_DIS_LOUT 0x0002 /* DIS_LOUT */ +#define WM8991_DIS_ROUT 0x0001 /* DIS_ROUT */ + +/* + * R57 (0x39) - AntiPOP2 + */ +#define WM8991_SOFTST 0x0040 /* SOFTST */ +#define WM8991_BUFIOEN 0x0008 /* BUFIOEN */ +#define WM8991_BUFDCOPEN 0x0004 /* BUFDCOPEN */ +#define WM8991_POBCTRL 0x0002 /* POBCTRL */ +#define WM8991_VMIDTOG 0x0001 /* VMIDTOG */ + +/* + * R58 (0x3A) - MICBIAS + */ +#define WM8991_MCDSCTH_MASK 0x00C0 /* MCDSCTH - [7:6] */ +#define WM8991_MCDTHR_MASK 0x0038 /* MCDTHR - [5:3] */ +#define WM8991_MCD 0x0004 /* MCD */ +#define WM8991_MBSEL 0x0001 /* MBSEL */ + +/* + * R60 (0x3C) - PLL1 + */ +#define WM8991_SDM 0x0080 /* SDM */ +#define WM8991_PRESCALE 0x0040 /* PRESCALE */ +#define WM8991_PLLN_MASK 0x000F /* PLLN - [3:0] */ + +/* + * R61 (0x3D) - PLL2 + */ +#define WM8991_PLLK1_MASK 0x00FF /* PLLK1 - [7:0] */ + +/* + * R62 (0x3E) - PLL3 + */ +#define WM8991_PLLK2_MASK 0x00FF /* PLLK2 - [7:0] */ + +/* + * R63 (0x3F) - Internal Driver Bits + */ +#define WM8991_INMIXL_PWR_BIT 0 +#define WM8991_AINLMUX_PWR_BIT 1 +#define WM8991_INMIXR_PWR_BIT 2 +#define WM8991_AINRMUX_PWR_BIT 3 + +#define WM8991_MCLK_DIV 0 +#define WM8991_DACCLK_DIV 1 +#define WM8991_ADCCLK_DIV 2 +#define WM8991_BCLK_DIV 3 + +#define SOC_WM899X_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert,\ + tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ + SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, \ + .get = snd_soc_get_volsw, .put = wm899x_outpga_put_volsw_vu, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } + +#endif /* _WM8991_H */ diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index 18c0d9ce7c32..379fa22c5b6c 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -242,7 +242,7 @@ struct wm8993_priv { int fll_src; }; -static int wm8993_volatile(unsigned int reg) +static int wm8993_volatile(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8993_SOFTWARE_RESET: diff --git a/sound/soc/codecs/wm8994-tables.c b/sound/soc/codecs/wm8994-tables.c index 68e9b024dd48..a87adbd05ee1 100644 --- a/sound/soc/codecs/wm8994-tables.c +++ b/sound/soc/codecs/wm8994-tables.c @@ -62,8 +62,8 @@ const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE] = { { 0x00FF, 0x00FF }, /* R58 - MICBIAS */ { 0x000F, 0x000F }, /* R59 - LDO 1 */ { 0x0007, 0x0007 }, /* R60 - LDO 2 */ - { 0x0000, 0x0000 }, /* R61 */ - { 0x0000, 0x0000 }, /* R62 */ + { 0xFFFF, 0xFFFF }, /* R61 */ + { 0xFFFF, 0xFFFF }, /* R62 */ { 0x0000, 0x0000 }, /* R63 */ { 0x0000, 0x0000 }, /* R64 */ { 0x0000, 0x0000 }, /* R65 */ @@ -209,9 +209,9 @@ const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE] = { { 0x0000, 0x0000 }, /* R205 */ { 0x0000, 0x0000 }, /* R206 */ { 0x0000, 0x0000 }, /* R207 */ - { 0x0000, 0x0000 }, /* R208 */ - { 0x0000, 0x0000 }, /* R209 */ - { 0x0000, 0x0000 }, /* R210 */ + { 0xFFFF, 0xFFFF }, /* R208 */ + { 0xFFFF, 0xFFFF }, /* R209 */ + { 0xFFFF, 0xFFFF }, /* R210 */ { 0x0000, 0x0000 }, /* R211 */ { 0x0000, 0x0000 }, /* R212 */ { 0x0000, 0x0000 }, /* R213 */ @@ -1573,7 +1573,7 @@ const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE] = { { 0x03C3, 0x03C3 }, /* R1569 - Sidetone */ }; -const __devinitdata u16 wm8994_reg_defaults[WM8994_CACHE_SIZE] = { +const u16 wm8994_reg_defaults[WM8994_CACHE_SIZE] = { 0x8994, /* R0 - Software Reset */ 0x0000, /* R1 - Power Management (1) */ 0x6000, /* R2 - Power Management (2) */ diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index c6c958ee5d59..3dc64c8b6a5c 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -102,8 +102,7 @@ struct wm8994_priv { wm8958_micdet_cb jack_cb; void *jack_cb_data; - bool jack_is_mic; - bool jack_is_video; + int micdet_irq; int revision; struct wm8994_pdata *pdata; @@ -115,7 +114,7 @@ struct wm8994_priv { unsigned int aif2clk_disable:1; }; -static int wm8994_readable(unsigned int reg) +static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM8994_GPIO_1: @@ -142,7 +141,7 @@ static int wm8994_readable(unsigned int reg) return wm8994_access_masks[reg].readable != 0; } -static int wm8994_volatile(unsigned int reg) +static int wm8994_volatile(struct snd_soc_codec *codec, unsigned int reg) { if (reg >= WM8994_CACHE_SIZE) return 1; @@ -170,7 +169,7 @@ static int wm8994_write(struct snd_soc_codec *codec, unsigned int reg, BUG_ON(reg > WM8994_MAX_REGISTER); - if (!wm8994_volatile(reg)) { + if (!wm8994_volatile(codec, reg)) { ret = snd_soc_cache_write(codec, reg, value); if (ret != 0) dev_err(codec->dev, "Cache write to %x failed: %d\n", @@ -188,7 +187,7 @@ static unsigned int wm8994_read(struct snd_soc_codec *codec, BUG_ON(reg > WM8994_MAX_REGISTER); - if (!wm8994_volatile(reg) && wm8994_readable(reg) && + if (!wm8994_volatile(codec, reg) && wm8994_readable(codec, reg) && reg < codec->driver->reg_cache_size) { ret = snd_soc_cache_read(codec, reg, &val); if (ret >= 0) @@ -529,7 +528,7 @@ static int wm8994_get_retune_mobile_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct wm8994_priv *wm8994 =snd_soc_codec_get_drvdata(codec); + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); int block = wm8994_get_retune_mobile_block(kcontrol->id.name); ucontrol->value.enumerated.item[0] = wm8994->retune_mobile_cfg[block]; @@ -1103,6 +1102,13 @@ static int adc_mux_ev(struct snd_soc_dapm_widget *w, return 0; } +static int micbias_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + late_enable_ev(w, kcontrol, event); + return 0; +} + static int dac_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -1440,6 +1446,10 @@ SND_SOC_DAPM_INPUT("DMIC1DAT"), SND_SOC_DAPM_INPUT("DMIC2DAT"), SND_SOC_DAPM_INPUT("Clock"), +SND_SOC_DAPM_MICBIAS("MICBIAS", WM8994_MICBIAS, 2, 0), +SND_SOC_DAPM_SUPPLY_S("MICBIAS Supply", 1, SND_SOC_NOPM, 0, 0, micbias_ev, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("CLK_SYS", SND_SOC_NOPM, 0, 0, clk_sys_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), @@ -1755,6 +1765,8 @@ static const struct snd_soc_dapm_route wm8994_revd_intercon[] = { { "AIF2DACDAT", NULL, "AIF1DACDAT" }, { "AIF1ADCDAT", NULL, "AIF2ADCDAT" }, { "AIF2ADCDAT", NULL, "AIF1ADCDAT" }, + { "MICBIAS", NULL, "CLK_SYS" }, + { "MICBIAS", NULL, "MICBIAS Supply" }, }; static const struct snd_soc_dapm_route wm8994_intercon[] = { @@ -2883,6 +2895,13 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994) else snd_soc_add_controls(wm8994->codec, wm8994_eq_controls, ARRAY_SIZE(wm8994_eq_controls)); + + for (i = 0; i < ARRAY_SIZE(pdata->micbias); i++) { + if (pdata->micbias[i]) { + snd_soc_write(codec, WM8958_MICBIAS1 + i, + pdata->micbias[i] & 0xffff); + } + } } /** @@ -2993,46 +3012,18 @@ static void wm8958_default_micdet(u16 status, void *data) int report = 0; /* If nothing present then clear our statuses */ - if (!(status & WM8958_MICD_STS)) { - wm8994->jack_is_video = false; - wm8994->jack_is_mic = false; + if (!(status & WM8958_MICD_STS)) goto done; - } - /* Assume anything over 475 ohms is a microphone and remember - * that we've seen one (since buttons override it) */ - if (status & 0x600) - wm8994->jack_is_mic = true; - if (wm8994->jack_is_mic) - report |= SND_JACK_MICROPHONE; - - /* Video has an impedence of approximately 75 ohms; assume - * this isn't used as a button and remember it since buttons - * override it. */ - if (status & 0x40) - wm8994->jack_is_video = true; - if (wm8994->jack_is_video) - report |= SND_JACK_VIDEOOUT; + report = SND_JACK_MICROPHONE; /* Everything else is buttons; just assign slots */ - if (status & 0x4) + if (status & 0x1c0) report |= SND_JACK_BTN_0; - if (status & 0x8) - report |= SND_JACK_BTN_1; - if (status & 0x10) - report |= SND_JACK_BTN_2; - if (status & 0x20) - report |= SND_JACK_BTN_3; - if (status & 0x80) - report |= SND_JACK_BTN_4; - if (status & 0x100) - report |= SND_JACK_BTN_5; done: snd_soc_jack_report(wm8994->micdet[0].jack, report, - SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | - SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5 | - SND_JACK_MICROPHONE | SND_JACK_VIDEOOUT); + SND_JACK_BTN_0 | SND_JACK_MICROPHONE); } /** @@ -3131,13 +3122,19 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) wm8994->pdata = dev_get_platdata(codec->dev->parent); wm8994->codec = codec; + if (wm8994->pdata && wm8994->pdata->micdet_irq) + wm8994->micdet_irq = wm8994->pdata->micdet_irq; + else if (wm8994->pdata && wm8994->pdata->irq_base) + wm8994->micdet_irq = wm8994->pdata->irq_base + + WM8994_IRQ_MIC1_DET; + pm_runtime_enable(codec->dev); pm_runtime_resume(codec->dev); /* Read our current status back from the chip - we don't want to * reset as this may interfere with the GPIO or LDO operation. */ for (i = 0; i < WM8994_CACHE_SIZE; i++) { - if (!wm8994_readable(i) || wm8994_volatile(i)) + if (!wm8994_readable(codec, i) || wm8994_volatile(codec, i)) continue; ret = wm8994_reg_read(codec->control_data, i); @@ -3179,14 +3176,17 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) switch (control->type) { case WM8994: - ret = wm8994_request_irq(codec->control_data, - WM8994_IRQ_MIC1_DET, - wm8994_mic_irq, "Mic 1 detect", - wm8994); - if (ret != 0) - dev_warn(codec->dev, - "Failed to request Mic1 detect IRQ: %d\n", - ret); + if (wm8994->micdet_irq) { + ret = request_threaded_irq(wm8994->micdet_irq, NULL, + wm8994_mic_irq, + IRQF_TRIGGER_RISING, + "Mic1 detect", + wm8994); + if (ret != 0) + dev_warn(codec->dev, + "Failed to request Mic1 detect IRQ: %d\n", + ret); + } ret = wm8994_request_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, @@ -3217,15 +3217,17 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) break; case WM8958: - ret = wm8994_request_irq(codec->control_data, - WM8994_IRQ_MIC1_DET, - wm8958_mic_irq, "Mic detect", - wm8994); - if (ret != 0) - dev_warn(codec->dev, - "Failed to request Mic detect IRQ: %d\n", - ret); - break; + if (wm8994->micdet_irq) { + ret = request_threaded_irq(wm8994->micdet_irq, NULL, + wm8958_mic_irq, + IRQF_TRIGGER_RISING, + "Mic detect", + wm8994); + if (ret != 0) + dev_warn(codec->dev, + "Failed to request Mic detect IRQ: %d\n", + ret); + } } /* Remember if AIFnLRCLK is configured as a GPIO. This should be @@ -3369,7 +3371,8 @@ err_irq: wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_SHRT, wm8994); wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_DET, wm8994); wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, wm8994); - wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_DET, wm8994); + if (wm8994->micdet_irq) + free_irq(wm8994->micdet_irq, wm8994); err: kfree(wm8994); return ret; @@ -3386,8 +3389,8 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec) switch (control->type) { case WM8994: - wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_SHRT, - wm8994); + if (wm8994->micdet_irq) + free_irq(wm8994->micdet_irq, wm8994); wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_DET, wm8994); wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, @@ -3397,8 +3400,8 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec) break; case WM8958: - wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_DET, - wm8994); + if (wm8994->micdet_irq) + free_irq(wm8994->micdet_irq, wm8994); break; } kfree(wm8994->retune_mobile_texts); diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h index 0c355bfc88f1..999b8851226b 100644 --- a/sound/soc/codecs/wm8994.h +++ b/sound/soc/codecs/wm8994.h @@ -43,6 +43,6 @@ struct wm8994_access_mask { }; extern const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE]; -extern const __devinitdata u16 wm8994_reg_defaults[WM8994_CACHE_SIZE]; +extern const u16 wm8994_reg_defaults[WM8994_CACHE_SIZE]; #endif diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index 608c84c5aa8e..67eaaecbb42e 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -19,6 +19,7 @@ #include <linux/pm.h> #include <linux/i2c.h> #include <linux/spi/spi.h> +#include <linux/regulator/consumer.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> @@ -30,6 +31,18 @@ #include "wm8995.h" +#define WM8995_NUM_SUPPLIES 8 +static const char *wm8995_supply_names[WM8995_NUM_SUPPLIES] = { + "DCVDD", + "DBVDD1", + "DBVDD2", + "DBVDD3", + "AVDD1", + "AVDD2", + "CPVDD", + "MICVDD" +}; + static const u16 wm8995_reg_defs[WM8995_MAX_REGISTER + 1] = { [0] = 0x8995, [5] = 0x0100, [16] = 0x000b, [17] = 0x000b, [24] = 0x02c0, [25] = 0x02c0, [26] = 0x02c0, [27] = 0x02c0, @@ -126,8 +139,37 @@ struct wm8995_priv { int mclk[2]; int aifclk[2]; struct fll_config fll[2], fll_suspend[2]; + struct regulator_bulk_data supplies[WM8995_NUM_SUPPLIES]; + struct notifier_block disable_nb[WM8995_NUM_SUPPLIES]; + struct snd_soc_codec *codec; }; +/* + * We can't use the same notifier block for more than one supply and + * there's no way I can see to get from a callback to the caller + * except container_of(). + */ +#define WM8995_REGULATOR_EVENT(n) \ +static int wm8995_regulator_event_##n(struct notifier_block *nb, \ + unsigned long event, void *data) \ +{ \ + struct wm8995_priv *wm8995 = container_of(nb, struct wm8995_priv, \ + disable_nb[n]); \ + if (event & REGULATOR_EVENT_DISABLE) { \ + wm8995->codec->cache_sync = 1; \ + } \ + return 0; \ +} + +WM8995_REGULATOR_EVENT(0) +WM8995_REGULATOR_EVENT(1) +WM8995_REGULATOR_EVENT(2) +WM8995_REGULATOR_EVENT(3) +WM8995_REGULATOR_EVENT(4) +WM8995_REGULATOR_EVENT(5) +WM8995_REGULATOR_EVENT(6) +WM8995_REGULATOR_EVENT(7) + static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1); static const DECLARE_TLV_DB_SCALE(in1lr_pga_tlv, -1650, 150, 0); static const DECLARE_TLV_DB_SCALE(in1l_boost_tlv, 0, 600, 0); @@ -909,7 +951,7 @@ static const struct snd_soc_dapm_route wm8995_intercon[] = { { "SPK2R", NULL, "SPK2R Driver" } }; -static int wm8995_volatile(unsigned int reg) +static int wm8995_volatile(struct snd_soc_codec *codec, unsigned int reg) { /* out of bounds registers are generally considered * volatile to support register banks that are partially @@ -1483,6 +1525,11 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies), + wm8995->supplies); + if (ret) + return ret; + ret = snd_soc_cache_sync(codec); if (ret) { dev_err(codec->dev, @@ -1492,12 +1539,13 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec, snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1, WM8995_BG_ENA_MASK, WM8995_BG_ENA); - } break; case SND_SOC_BIAS_OFF: snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1, WM8995_BG_ENA_MASK, 0); + regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies), + wm8995->supplies); break; } @@ -1536,10 +1584,12 @@ static int wm8995_remove(struct snd_soc_codec *codec) static int wm8995_probe(struct snd_soc_codec *codec) { struct wm8995_priv *wm8995; + int i; int ret; codec->dapm.idle_bias_off = 1; wm8995 = snd_soc_codec_get_drvdata(codec); + wm8995->codec = codec; ret = snd_soc_codec_set_cache_io(codec, 16, 16, wm8995->control_type); if (ret < 0) { @@ -1547,21 +1597,58 @@ static int wm8995_probe(struct snd_soc_codec *codec) return ret; } + for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++) + wm8995->supplies[i].supply = wm8995_supply_names[i]; + + ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8995->supplies), + wm8995->supplies); + if (ret) { + dev_err(codec->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + wm8995->disable_nb[0].notifier_call = wm8995_regulator_event_0; + wm8995->disable_nb[1].notifier_call = wm8995_regulator_event_1; + wm8995->disable_nb[2].notifier_call = wm8995_regulator_event_2; + wm8995->disable_nb[3].notifier_call = wm8995_regulator_event_3; + wm8995->disable_nb[4].notifier_call = wm8995_regulator_event_4; + wm8995->disable_nb[5].notifier_call = wm8995_regulator_event_5; + wm8995->disable_nb[6].notifier_call = wm8995_regulator_event_6; + wm8995->disable_nb[7].notifier_call = wm8995_regulator_event_7; + + /* This should really be moved into the regulator core */ + for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++) { + ret = regulator_register_notifier(wm8995->supplies[i].consumer, + &wm8995->disable_nb[i]); + if (ret) { + dev_err(codec->dev, + "Failed to register regulator notifier: %d\n", + ret); + } + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies), + wm8995->supplies); + if (ret) { + dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); + goto err_reg_get; + } + ret = snd_soc_read(codec, WM8995_SOFTWARE_RESET); if (ret < 0) { dev_err(codec->dev, "Failed to read device ID: %d\n", ret); - return ret; + goto err_reg_enable; } if (ret != 0x8995) { dev_err(codec->dev, "Invalid device ID: %#x\n", ret); - return -EINVAL; + goto err_reg_enable; } ret = snd_soc_write(codec, WM8995_SOFTWARE_RESET, 0); if (ret < 0) { dev_err(codec->dev, "Failed to issue reset: %d\n", ret); - return ret; + goto err_reg_enable; } wm8995_set_bias_level(codec, SND_SOC_BIAS_STANDBY); @@ -1596,6 +1683,12 @@ static int wm8995_probe(struct snd_soc_codec *codec) ARRAY_SIZE(wm8995_intercon)); return 0; + +err_reg_enable: + regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies), wm8995->supplies); +err_reg_get: + regulator_bulk_free(ARRAY_SIZE(wm8995->supplies), wm8995->supplies); + return ret; } #define WM8995_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index cce704c275c6..55cdf2982020 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -167,10 +167,10 @@ struct wm9081_priv { int fll_fref; int fll_fout; int tdm_width; - struct wm9081_retune_mobile_config *retune; + struct wm9081_pdata pdata; }; -static int wm9081_volatile_register(unsigned int reg) +static int wm9081_volatile_register(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM9081_SOFTWARE_RESET: @@ -389,27 +389,6 @@ SOC_DAPM_SINGLE("IN2 Switch", WM9081_ANALOGUE_MIXER, 2, 1, 0), SOC_DAPM_SINGLE("Playback Switch", WM9081_ANALOGUE_MIXER, 4, 1, 0), }; -static int speaker_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_codec *codec = w->codec; - unsigned int reg = snd_soc_read(codec, WM9081_POWER_MANAGEMENT); - - switch (event) { - case SND_SOC_DAPM_POST_PMU: - reg |= WM9081_SPK_ENA; - break; - - case SND_SOC_DAPM_PRE_PMD: - reg &= ~WM9081_SPK_ENA; - break; - } - - snd_soc_write(codec, WM9081_POWER_MANAGEMENT, reg); - - return 0; -} - struct _fll_div { u16 fll_fratio; u16 fll_outdiv; @@ -747,9 +726,8 @@ SND_SOC_DAPM_MIXER_NAMED_CTL("Mixer", SND_SOC_NOPM, 0, 0, SND_SOC_DAPM_PGA("LINEOUT PGA", WM9081_POWER_MANAGEMENT, 4, 0, NULL, 0), -SND_SOC_DAPM_PGA_E("Speaker PGA", WM9081_POWER_MANAGEMENT, 2, 0, NULL, 0, - speaker_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA("Speaker PGA", WM9081_POWER_MANAGEMENT, 2, 0, NULL, 0), +SND_SOC_DAPM_PGA("Speaker", WM9081_POWER_MANAGEMENT, 1, 0, NULL, 0), SND_SOC_DAPM_OUTPUT("LINEOUT"), SND_SOC_DAPM_OUTPUT("SPKN"), @@ -762,7 +740,7 @@ SND_SOC_DAPM_SUPPLY("TOCLK", WM9081_CLOCK_CONTROL_3, 2, 0, NULL, 0), }; -static const struct snd_soc_dapm_route audio_paths[] = { +static const struct snd_soc_dapm_route wm9081_audio_paths[] = { { "DAC", NULL, "CLK_SYS" }, { "DAC", NULL, "CLK_DSP" }, @@ -780,8 +758,10 @@ static const struct snd_soc_dapm_route audio_paths[] = { { "Speaker PGA", NULL, "TOCLK" }, { "Speaker PGA", NULL, "CLK_SYS" }, - { "SPKN", NULL, "Speaker PGA" }, - { "SPKP", NULL, "Speaker PGA" }, + { "Speaker", NULL, "Speaker PGA" }, + + { "SPKN", NULL, "Speaker" }, + { "SPKP", NULL, "Speaker" }, }; static int wm9081_set_bias_level(struct snd_soc_codec *codec, @@ -1082,21 +1062,22 @@ static int wm9081_hw_params(struct snd_pcm_substream *substream, aif4 |= wm9081->bclk / wm9081->fs; /* Apply a ReTune Mobile configuration if it's in use */ - if (wm9081->retune) { - struct wm9081_retune_mobile_config *retune = wm9081->retune; + if (wm9081->pdata.num_retune_configs) { + struct wm9081_pdata *pdata = &wm9081->pdata; struct wm9081_retune_mobile_setting *s; int eq1; best = 0; - best_val = abs(retune->configs[0].rate - wm9081->fs); - for (i = 0; i < retune->num_configs; i++) { - cur_val = abs(retune->configs[i].rate - wm9081->fs); + best_val = abs(pdata->retune_configs[0].rate - wm9081->fs); + for (i = 0; i < pdata->num_retune_configs; i++) { + cur_val = abs(pdata->retune_configs[i].rate - + wm9081->fs); if (cur_val < best_val) { best_val = cur_val; best = i; } } - s = &retune->configs[best]; + s = &pdata->retune_configs[best]; dev_dbg(codec->dev, "ReTune Mobile %s tuned for %dHz\n", s->name, s->rate); @@ -1139,10 +1120,9 @@ static int wm9081_digital_mute(struct snd_soc_dai *codec_dai, int mute) return 0; } -static int wm9081_set_sysclk(struct snd_soc_dai *codec_dai, +static int wm9081_set_sysclk(struct snd_soc_codec *codec, int clk_id, unsigned int freq, int dir) { - struct snd_soc_codec *codec = codec_dai->codec; struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec); switch (clk_id) { @@ -1207,7 +1187,6 @@ static int wm9081_set_tdm_slot(struct snd_soc_dai *dai, static struct snd_soc_dai_ops wm9081_dai_ops = { .hw_params = wm9081_hw_params, - .set_sysclk = wm9081_set_sysclk, .set_fmt = wm9081_set_dai_fmt, .digital_mute = wm9081_digital_mute, .set_tdm_slot = wm9081_set_tdm_slot, @@ -1231,7 +1210,6 @@ static struct snd_soc_dai_driver wm9081_dai = { static int wm9081_probe(struct snd_soc_codec *codec) { struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; u16 reg; @@ -1255,6 +1233,14 @@ static int wm9081_probe(struct snd_soc_codec *codec) return ret; } + reg = 0; + if (wm9081->pdata.irq_high) + reg |= WM9081_IRQ_POL; + if (!wm9081->pdata.irq_cmos) + reg |= WM9081_IRQ_OP_CTRL; + snd_soc_update_bits(codec, WM9081_INTERRUPT_CONTROL, + WM9081_IRQ_POL | WM9081_IRQ_OP_CTRL, reg); + wm9081_set_bias_level(codec, SND_SOC_BIAS_STANDBY); /* Enable zero cross by default */ @@ -1266,17 +1252,13 @@ static int wm9081_probe(struct snd_soc_codec *codec) snd_soc_add_controls(codec, wm9081_snd_controls, ARRAY_SIZE(wm9081_snd_controls)); - if (!wm9081->retune) { + if (!wm9081->pdata.num_retune_configs) { dev_dbg(codec->dev, "No ReTune Mobile data, using normal EQ\n"); snd_soc_add_controls(codec, wm9081_eq_controls, ARRAY_SIZE(wm9081_eq_controls)); } - snd_soc_dapm_new_controls(dapm, wm9081_dapm_widgets, - ARRAY_SIZE(wm9081_dapm_widgets)); - snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths)); - return ret; } @@ -1320,11 +1302,19 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9081 = { .remove = wm9081_remove, .suspend = wm9081_suspend, .resume = wm9081_resume, + + .set_sysclk = wm9081_set_sysclk, .set_bias_level = wm9081_set_bias_level, + .reg_cache_size = ARRAY_SIZE(wm9081_reg_defaults), .reg_word_size = sizeof(u16), .reg_cache_default = wm9081_reg_defaults, .volatile_register = wm9081_volatile_register, + + .dapm_widgets = wm9081_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm9081_dapm_widgets), + .dapm_routes = wm9081_audio_paths, + .num_dapm_routes = ARRAY_SIZE(wm9081_audio_paths), }; #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) @@ -1343,8 +1333,8 @@ static __devinit int wm9081_i2c_probe(struct i2c_client *i2c, wm9081->control_data = i2c; if (dev_get_platdata(&i2c->dev)) - memcpy(&wm9081->retune, dev_get_platdata(&i2c->dev), - sizeof(wm9081->retune)); + memcpy(&wm9081->pdata, dev_get_platdata(&i2c->dev), + sizeof(wm9081->pdata)); ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm9081, &wm9081_dai, 1); @@ -1368,7 +1358,7 @@ MODULE_DEVICE_TABLE(i2c, wm9081_i2c_id); static struct i2c_driver wm9081_i2c_driver = { .driver = { - .name = "wm9081-codec", + .name = "wm9081", .owner = THIS_MODULE, }, .probe = wm9081_i2c_probe, diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c index a788c4297046..4de12203e611 100644 --- a/sound/soc/codecs/wm9090.c +++ b/sound/soc/codecs/wm9090.c @@ -144,7 +144,7 @@ struct wm9090_priv { void *control_data; }; -static int wm9090_volatile(unsigned int reg) +static int wm9090_volatile(struct snd_soc_codec *codec, unsigned int reg) { switch (reg) { case WM9090_SOFTWARE_RESET: @@ -518,7 +518,7 @@ static int wm9090_set_bias_level(struct snd_soc_codec *codec, for (i = 1; i < codec->driver->reg_cache_size; i++) { if (reg_cache[i] == wm9090_reg_defaults[i]) continue; - if (wm9090_volatile(i)) + if (wm9090_volatile(codec, i)) continue; ret = snd_soc_write(codec, i, reg_cache[i]); @@ -551,7 +551,6 @@ static int wm9090_set_bias_level(struct snd_soc_codec *codec, static int wm9090_probe(struct snd_soc_codec *codec) { struct wm9090_priv *wm9090 = snd_soc_codec_get_drvdata(codec); - u16 *reg_cache = codec->reg_cache; int ret; codec->control_data = wm9090->control_data; @@ -576,22 +575,30 @@ static int wm9090_probe(struct snd_soc_codec *codec) /* Configure some defaults; they will be written out when we * bring the bias up. */ - reg_cache[WM9090_IN1_LINE_INPUT_A_VOLUME] |= WM9090_IN1_VU - | WM9090_IN1A_ZC; - reg_cache[WM9090_IN1_LINE_INPUT_B_VOLUME] |= WM9090_IN1_VU - | WM9090_IN1B_ZC; - reg_cache[WM9090_IN2_LINE_INPUT_A_VOLUME] |= WM9090_IN2_VU - | WM9090_IN2A_ZC; - reg_cache[WM9090_IN2_LINE_INPUT_B_VOLUME] |= WM9090_IN2_VU - | WM9090_IN2B_ZC; - reg_cache[WM9090_SPEAKER_VOLUME_LEFT] |= - WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC; - reg_cache[WM9090_LEFT_OUTPUT_VOLUME] |= - WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC; - reg_cache[WM9090_RIGHT_OUTPUT_VOLUME] |= - WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC; - - reg_cache[WM9090_CLOCKING_1] |= WM9090_TOCLK_ENA; + snd_soc_update_bits(codec, WM9090_IN1_LINE_INPUT_A_VOLUME, + WM9090_IN1_VU | WM9090_IN1A_ZC, + WM9090_IN1_VU | WM9090_IN1A_ZC); + snd_soc_update_bits(codec, WM9090_IN1_LINE_INPUT_B_VOLUME, + WM9090_IN1_VU | WM9090_IN1B_ZC, + WM9090_IN1_VU | WM9090_IN1B_ZC); + snd_soc_update_bits(codec, WM9090_IN2_LINE_INPUT_A_VOLUME, + WM9090_IN2_VU | WM9090_IN2A_ZC, + WM9090_IN2_VU | WM9090_IN2A_ZC); + snd_soc_update_bits(codec, WM9090_IN2_LINE_INPUT_B_VOLUME, + WM9090_IN2_VU | WM9090_IN2B_ZC, + WM9090_IN2_VU | WM9090_IN2B_ZC); + snd_soc_update_bits(codec, WM9090_SPEAKER_VOLUME_LEFT, + WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC, + WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC); + snd_soc_update_bits(codec, WM9090_LEFT_OUTPUT_VOLUME, + WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC, + WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC); + snd_soc_update_bits(codec, WM9090_RIGHT_OUTPUT_VOLUME, + WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC, + WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC); + + snd_soc_update_bits(codec, WM9090_CLOCKING_1, + WM9090_TOCLK_ENA, WM9090_TOCLK_ENA); wm9090_set_bias_level(codec, SND_SOC_BIAS_STANDBY); diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 516892706063..7b6b3c18e299 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -82,7 +82,8 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec, unsigned int op) } while (reg & op && count < 400); if (reg & op) - dev_err(codec->dev, "Timed out waiting for DC Servo\n"); + dev_err(codec->dev, "Timed out waiting for DC Servo %x\n", + op); } /* diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index 9e0e565e6ed9..d0d60b8a54d4 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c @@ -658,7 +658,7 @@ static int davinci_i2s_probe(struct platform_device *pdev) return -ENODEV; } - ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1, + ioarea = request_mem_region(mem->start, resource_size(mem), pdev->name); if (!ioarea) { dev_err(&pdev->dev, "McBSP region already claimed\n"); @@ -694,20 +694,25 @@ static int davinci_i2s_probe(struct platform_device *pdev) } clk_enable(dev->clk); - dev->base = (void __iomem *)IO_ADDRESS(mem->start); + dev->base = ioremap(mem->start, resource_size(mem)); + if (!dev->base) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err_release_clk; + } dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].dma_addr = - (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DXR_REG); + (dma_addr_t)(mem->start + DAVINCI_MCBSP_DXR_REG); dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].dma_addr = - (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DRR_REG); + (dma_addr_t)(mem->start + DAVINCI_MCBSP_DRR_REG); /* first TX, then RX */ res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (!res) { dev_err(&pdev->dev, "no DMA resource\n"); ret = -ENXIO; - goto err_free_mem; + goto err_iounmap; } dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].channel = res->start; @@ -715,7 +720,7 @@ static int davinci_i2s_probe(struct platform_device *pdev) if (!res) { dev_err(&pdev->dev, "no DMA resource\n"); ret = -ENXIO; - goto err_free_mem; + goto err_iounmap; } dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = res->start; dev->dev = &pdev->dev; @@ -724,14 +729,19 @@ static int davinci_i2s_probe(struct platform_device *pdev) ret = snd_soc_register_dai(&pdev->dev, &davinci_i2s_dai); if (ret != 0) - goto err_free_mem; + goto err_iounmap; return 0; +err_iounmap: + iounmap(dev->base); +err_release_clk: + clk_disable(dev->clk); + clk_put(dev->clk); err_free_mem: kfree(dev); err_release_region: - release_mem_region(mem->start, (mem->end - mem->start) + 1); + release_mem_region(mem->start, resource_size(mem)); return ret; } @@ -747,7 +757,7 @@ static int davinci_i2s_remove(struct platform_device *pdev) dev->clk = NULL; kfree(dev); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(mem->start, (mem->end - mem->start) + 1); + release_mem_region(mem->start, resource_size(mem)); return 0; } diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index fb55d2c5d704..a5af834c8ef5 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -868,7 +868,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) } ioarea = request_mem_region(mem->start, - (mem->end - mem->start) + 1, pdev->name); + resource_size(mem), pdev->name); if (!ioarea) { dev_err(&pdev->dev, "Audio region already claimed\n"); ret = -EBUSY; @@ -885,7 +885,13 @@ static int davinci_mcasp_probe(struct platform_device *pdev) clk_enable(dev->clk); dev->clk_active = 1; - dev->base = (void __iomem *)IO_ADDRESS(mem->start); + dev->base = ioremap(mem->start, resource_size(mem)); + if (!dev->base) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err_release_clk; + } + dev->op_mode = pdata->op_mode; dev->tdm_slots = pdata->tdm_slots; dev->num_serializer = pdata->num_serializer; @@ -899,14 +905,14 @@ static int davinci_mcasp_probe(struct platform_device *pdev) dma_data->asp_chan_q = pdata->asp_chan_q; dma_data->ram_chan_q = pdata->ram_chan_q; dma_data->dma_addr = (dma_addr_t) (pdata->tx_dma_offset + - io_v2p(dev->base)); + mem->start); /* first TX, then RX */ res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (!res) { dev_err(&pdev->dev, "no DMA resource\n"); ret = -ENODEV; - goto err_release_region; + goto err_iounmap; } dma_data->channel = res->start; @@ -915,13 +921,13 @@ static int davinci_mcasp_probe(struct platform_device *pdev) dma_data->asp_chan_q = pdata->asp_chan_q; dma_data->ram_chan_q = pdata->ram_chan_q; dma_data->dma_addr = (dma_addr_t)(pdata->rx_dma_offset + - io_v2p(dev->base)); + mem->start); res = platform_get_resource(pdev, IORESOURCE_DMA, 1); if (!res) { dev_err(&pdev->dev, "no DMA resource\n"); ret = -ENODEV; - goto err_release_region; + goto err_iounmap; } dma_data->channel = res->start; @@ -929,11 +935,16 @@ static int davinci_mcasp_probe(struct platform_device *pdev) ret = snd_soc_register_dai(&pdev->dev, &davinci_mcasp_dai[pdata->op_mode]); if (ret != 0) - goto err_release_region; + goto err_iounmap; return 0; +err_iounmap: + iounmap(dev->base); +err_release_clk: + clk_disable(dev->clk); + clk_put(dev->clk); err_release_region: - release_mem_region(mem->start, (mem->end - mem->start) + 1); + release_mem_region(mem->start, resource_size(mem)); err_release_data: kfree(dev); @@ -951,7 +962,7 @@ static int davinci_mcasp_remove(struct platform_device *pdev) dev->clk = NULL; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(mem->start, (mem->end - mem->start) + 1); + release_mem_region(mem->start, resource_size(mem)); kfree(dev); diff --git a/sound/soc/ep93xx/Kconfig b/sound/soc/ep93xx/Kconfig index 57429041189c..91a28de94109 100644 --- a/sound/soc/ep93xx/Kconfig +++ b/sound/soc/ep93xx/Kconfig @@ -30,3 +30,12 @@ config SND_EP93XX_SOC_SIMONE help Say Y or M here if you want to add support for AC97 audio on the Simplemachines Sim.One board. + +config SND_EP93XX_SOC_EDB93XX + tristate "SoC Audio support for Cirrus Logic EDB93xx boards" + depends on SND_EP93XX_SOC && (MACH_EDB9301 || MACH_EDB9302 || MACH_EDB9302A || MACH_EDB9307A || MACH_EDB9315A) + select SND_EP93XX_SOC_I2S + select SND_SOC_CS4271 + help + Say Y or M here if you want to add support for I2S audio on the + Cirrus Logic EDB93xx boards. diff --git a/sound/soc/ep93xx/Makefile b/sound/soc/ep93xx/Makefile index 8e7977fb6b7d..5514146cbdf0 100644 --- a/sound/soc/ep93xx/Makefile +++ b/sound/soc/ep93xx/Makefile @@ -10,6 +10,8 @@ obj-$(CONFIG_SND_EP93XX_SOC_AC97) += snd-soc-ep93xx-ac97.o # EP93XX Machine Support snd-soc-snappercl15-objs := snappercl15.o snd-soc-simone-objs := simone.o +snd-soc-edb93xx-objs := edb93xx.o obj-$(CONFIG_SND_EP93XX_SOC_SNAPPERCL15) += snd-soc-snappercl15.o obj-$(CONFIG_SND_EP93XX_SOC_SIMONE) += snd-soc-simone.o +obj-$(CONFIG_SND_EP93XX_SOC_EDB93XX) += snd-soc-edb93xx.o diff --git a/sound/soc/ep93xx/edb93xx.c b/sound/soc/ep93xx/edb93xx.c new file mode 100644 index 000000000000..d3aa15119d26 --- /dev/null +++ b/sound/soc/ep93xx/edb93xx.c @@ -0,0 +1,142 @@ +/* + * SoC audio for EDB93xx + * + * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru> + * + * 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. + * + * This driver support CS4271 codec being master or slave, working + * in control port mode, connected either via SPI or I2C. + * The data format accepted is I2S or left-justified. + * DAPM support not implemented. + */ + +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <asm/mach-types.h> +#include <mach/hardware.h> +#include "ep93xx-pcm.h" + +#define edb93xx_has_audio() (machine_is_edb9301() || \ + machine_is_edb9302() || \ + machine_is_edb9302a() || \ + machine_is_edb9307a() || \ + machine_is_edb9315a()) + +static int edb93xx_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_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int err; + unsigned int mclk_rate; + unsigned int rate = params_rate(params); + + /* + * According to CS4271 datasheet we use MCLK/LRCK=256 for + * rates below 50kHz and 128 for higher sample rates + */ + if (rate < 50000) + mclk_rate = rate * 64 * 4; + else + mclk_rate = rate * 64 * 2; + + err = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_IF | + SND_SOC_DAIFMT_CBS_CFS); + if (err) + return err; + + err = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_IF | + SND_SOC_DAIFMT_CBS_CFS); + if (err) + return err; + + err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate, + SND_SOC_CLOCK_IN); + if (err) + return err; + + return snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate, + SND_SOC_CLOCK_OUT); +} + +static struct snd_soc_ops edb93xx_ops = { + .hw_params = edb93xx_hw_params, +}; + +static struct snd_soc_dai_link edb93xx_dai = { + .name = "CS4271", + .stream_name = "CS4271 HiFi", + .platform_name = "ep93xx-pcm-audio", + .cpu_dai_name = "ep93xx-i2s", + .codec_name = "spi0.0", + .codec_dai_name = "cs4271-hifi", + .ops = &edb93xx_ops, +}; + +static struct snd_soc_card snd_soc_edb93xx = { + .name = "EDB93XX", + .dai_link = &edb93xx_dai, + .num_links = 1, +}; + +static struct platform_device *edb93xx_snd_device; + +static int __init edb93xx_init(void) +{ + int ret; + + if (!edb93xx_has_audio()) + return -ENODEV; + + ret = ep93xx_i2s_acquire(EP93XX_SYSCON_DEVCFG_I2SONAC97, + EP93XX_SYSCON_I2SCLKDIV_ORIDE | + EP93XX_SYSCON_I2SCLKDIV_SPOL); + if (ret) + return ret; + + edb93xx_snd_device = platform_device_alloc("soc-audio", -1); + if (!edb93xx_snd_device) { + ret = -ENOMEM; + goto free_i2s; + } + + platform_set_drvdata(edb93xx_snd_device, &snd_soc_edb93xx); + ret = platform_device_add(edb93xx_snd_device); + if (ret) + goto device_put; + + return 0; + +device_put: + platform_device_put(edb93xx_snd_device); +free_i2s: + ep93xx_i2s_release(); + return ret; +} +module_init(edb93xx_init); + +static void __exit edb93xx_exit(void) +{ + platform_device_unregister(edb93xx_snd_device); + ep93xx_i2s_release(); +} +module_exit(edb93xx_exit); + +MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>"); +MODULE_DESCRIPTION("ALSA SoC EDB93xx"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/ep93xx/ep93xx-ac97.c b/sound/soc/ep93xx/ep93xx-ac97.c index 68a0bae1208a..104e95cda0ad 100644 --- a/sound/soc/ep93xx/ep93xx-ac97.c +++ b/sound/soc/ep93xx/ep93xx-ac97.c @@ -253,7 +253,6 @@ static int ep93xx_ac97_trigger(struct snd_pcm_substream *substream, struct ep93xx_ac97_info *info = snd_soc_dai_get_drvdata(dai); unsigned v = 0; - switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: diff --git a/sound/soc/ep93xx/ep93xx-i2s.c b/sound/soc/ep93xx/ep93xx-i2s.c index fff579a1c134..042f4e93746f 100644 --- a/sound/soc/ep93xx/ep93xx-i2s.c +++ b/sound/soc/ep93xx/ep93xx-i2s.c @@ -242,7 +242,7 @@ static int ep93xx_i2s_hw_params(struct snd_pcm_substream *substream, { struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai); unsigned word_len, div, sdiv, lrdiv; - int found = 0, err; + int err; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: @@ -275,15 +275,14 @@ static int ep93xx_i2s_hw_params(struct snd_pcm_substream *substream, * the codec uses. */ div = clk_get_rate(info->mclk) / params_rate(params); - for (sdiv = 2; sdiv <= 4; sdiv += 2) - for (lrdiv = 64; lrdiv <= 128; lrdiv <<= 1) - if (sdiv * lrdiv == div) { - found = 1; - goto out; - } -out: - if (!found) - return -EINVAL; + sdiv = 4; + if (div > (256 + 512) / 2) { + lrdiv = 128; + } else { + lrdiv = 64; + if (div < (128 + 256) / 2) + sdiv = 2; + } err = clk_set_rate(info->sclk, clk_get_rate(info->mclk) / sdiv); if (err) @@ -314,10 +313,12 @@ static int ep93xx_i2s_suspend(struct snd_soc_dai *dai) struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai); if (!dai->active) - return; + return 0; ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_PLAYBACK); ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_CAPTURE); + + return 0; } static int ep93xx_i2s_resume(struct snd_soc_dai *dai) @@ -325,10 +326,12 @@ static int ep93xx_i2s_resume(struct snd_soc_dai *dai) struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai); if (!dai->active) - return; + return 0; ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_PLAYBACK); ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_CAPTURE); + + return 0; } #else #define ep93xx_i2s_suspend NULL @@ -352,13 +355,13 @@ static struct snd_soc_dai_driver ep93xx_i2s_dai = { .playback = { .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_96000, + .rates = SNDRV_PCM_RATE_8000_192000, .formats = EP93XX_I2S_FORMATS, }, .capture = { .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_96000, + .rates = SNDRV_PCM_RATE_8000_192000, .formats = EP93XX_I2S_FORMATS, }, .ops = &ep93xx_i2s_dai_ops, diff --git a/sound/soc/ep93xx/ep93xx-pcm.c b/sound/soc/ep93xx/ep93xx-pcm.c index 06670776f649..a456e491155f 100644 --- a/sound/soc/ep93xx/ep93xx-pcm.c +++ b/sound/soc/ep93xx/ep93xx-pcm.c @@ -35,9 +35,9 @@ static const struct snd_pcm_hardware ep93xx_pcm_hardware = { SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER), - .rates = SNDRV_PCM_RATE_8000_96000, + .rates = SNDRV_PCM_RATE_8000_192000, .rate_min = SNDRV_PCM_RATE_8000, - .rate_max = SNDRV_PCM_RATE_96000, + .rate_max = SNDRV_PCM_RATE_192000, .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c index 7d7847a1e66b..c16c6b2eff95 100644 --- a/sound/soc/fsl/mpc8610_hpcd.c +++ b/sound/soc/fsl/mpc8610_hpcd.c @@ -53,9 +53,8 @@ struct mpc8610_hpcd_data { * * Here we program the DMACR and PMUXCR registers. */ -static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device) +static int mpc8610_hpcd_machine_probe(struct snd_soc_card *card) { - struct snd_soc_card *card = platform_get_drvdata(sound_device); struct mpc8610_hpcd_data *machine_data = container_of(card, struct mpc8610_hpcd_data, card); struct ccsr_guts_86xx __iomem *guts; @@ -138,9 +137,8 @@ static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream) * This function is called to remove the sound device for one SSI. We * de-program the DMACR and PMUXCR register. */ -static int mpc8610_hpcd_machine_remove(struct platform_device *sound_device) +static int mpc8610_hpcd_machine_remove(struct snd_soc_card *card) { - struct snd_soc_card *card = platform_get_drvdata(sound_device); struct mpc8610_hpcd_data *machine_data = container_of(card, struct mpc8610_hpcd_data, card); struct ccsr_guts_86xx __iomem *guts; diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c index 026b756961e0..66e0b68af147 100644 --- a/sound/soc/fsl/p1022_ds.c +++ b/sound/soc/fsl/p1022_ds.c @@ -85,9 +85,8 @@ struct machine_data { * * Here we program the DMACR and PMUXCR registers. */ -static int p1022_ds_machine_probe(struct platform_device *sound_device) +static int p1022_ds_machine_probe(struct snd_soc_card *card) { - struct snd_soc_card *card = platform_get_drvdata(sound_device); struct machine_data *mdata = container_of(card, struct machine_data, card); struct ccsr_guts_85xx __iomem *guts; @@ -160,9 +159,8 @@ static int p1022_ds_startup(struct snd_pcm_substream *substream) * This function is called to remove the sound device for one SSI. We * de-program the DMACR and PMUXCR register. */ -static int p1022_ds_machine_remove(struct platform_device *sound_device) +static int p1022_ds_machine_remove(struct snd_soc_card *card) { - struct snd_soc_card *card = platform_get_drvdata(sound_device); struct machine_data *mdata = container_of(card, struct machine_data, card); struct ccsr_guts_85xx __iomem *guts; diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig index 9eeb8f0d67e9..d8f130d39dd9 100644 --- a/sound/soc/imx/Kconfig +++ b/sound/soc/imx/Kconfig @@ -30,6 +30,16 @@ config SND_MXC_SOC_WM1133_EV1 Enable support for audio on the i.MX31ADS with the WM1133-EV1 PMIC board with WM8835x fitted. +config SND_SOC_MX27VIS_AIC32X4 + tristate "SoC audio support for Visstrim M10 boards" + depends on MACH_IMX27_VISSTRIM_M10 + select SND_SOC_TVL320AIC32X4 + select SND_MXC_SOC_SSI + select SND_MXC_SOC_MX2 + help + Say Y if you want to add support for SoC audio on Visstrim SM10 + board with TLV320AIC32X4 codec. + config SND_SOC_PHYCORE_AC97 tristate "SoC Audio support for Phytec phyCORE (and phyCARD) boards" depends on MACH_PCM043 || MACH_PCA100 diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile index b67fc02a4ecc..d6d609ba7e24 100644 --- a/sound/soc/imx/Makefile +++ b/sound/soc/imx/Makefile @@ -10,8 +10,10 @@ obj-$(CONFIG_SND_MXC_SOC_MX2) += snd-soc-imx-mx2.o # i.MX Machine Support snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o snd-soc-phycore-ac97-objs := phycore-ac97.o +snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o snd-soc-wm1133-ev1-objs := wm1133-ev1.o obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o +obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o diff --git a/sound/soc/imx/imx-ssi.c b/sound/soc/imx/imx-ssi.c index 30894ea7f333..bc92ec620004 100644 --- a/sound/soc/imx/imx-ssi.c +++ b/sound/soc/imx/imx-ssi.c @@ -108,7 +108,7 @@ static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) break; case SND_SOC_DAIFMT_DSP_B: /* data on rising edge of bclk, frame high with data */ - strcr |= SSI_STCR_TFSL; + strcr |= SSI_STCR_TFSL | SSI_STCR_TXBIT0; break; case SND_SOC_DAIFMT_DSP_A: /* data on rising edge of bclk, frame high 1clk before data */ @@ -656,6 +656,9 @@ static int imx_ssi_probe(struct platform_device *pdev) ssi->dma_params_rx.dma_addr = res->start + SSI_SRX0; ssi->dma_params_tx.dma_addr = res->start + SSI_STX0; + ssi->dma_params_tx.burstsize = 4; + ssi->dma_params_rx.burstsize = 4; + res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx0"); if (res) ssi->dma_params_tx.dma = res->start; diff --git a/sound/soc/imx/mx27vis-aic32x4.c b/sound/soc/imx/mx27vis-aic32x4.c new file mode 100644 index 000000000000..054110b91d42 --- /dev/null +++ b/sound/soc/imx/mx27vis-aic32x4.c @@ -0,0 +1,137 @@ +/* + * mx27vis-aic32x4.c + * + * Copyright 2011 Vista Silicon S.L. + * + * Author: Javier Martin <javier.martin@vista-silicon.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <asm/mach-types.h> +#include <mach/audmux.h> + +#include "../codecs/tlv320aic32x4.h" +#include "imx-ssi.h" + +static int mx27vis_aic32x4_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_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret; + u32 dai_format; + + dai_format = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM; + + /* set codec DAI configuration */ + snd_soc_dai_set_fmt(codec_dai, dai_format); + + /* set cpu DAI configuration */ + snd_soc_dai_set_fmt(cpu_dai, dai_format); + + ret = snd_soc_dai_set_sysclk(codec_dai, 0, + 25000000, SND_SOC_CLOCK_OUT); + if (ret) { + pr_err("%s: failed setting codec sysclk\n", __func__); + return ret; + } + + ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, + SND_SOC_CLOCK_IN); + if (ret) { + pr_err("can't set CPU system clock IMX_SSP_SYS_CLK\n"); + return ret; + } + + return 0; +} + +static struct snd_soc_ops mx27vis_aic32x4_snd_ops = { + .hw_params = mx27vis_aic32x4_hw_params, +}; + +static struct snd_soc_dai_link mx27vis_aic32x4_dai = { + .name = "tlv320aic32x4", + .stream_name = "TLV320AIC32X4", + .codec_dai_name = "tlv320aic32x4-hifi", + .platform_name = "imx-pcm-audio.0", + .codec_name = "tlv320aic32x4.0-0018", + .cpu_dai_name = "imx-ssi.0", + .ops = &mx27vis_aic32x4_snd_ops, +}; + +static struct snd_soc_card mx27vis_aic32x4 = { + .name = "visstrim_m10-audio", + .dai_link = &mx27vis_aic32x4_dai, + .num_links = 1, +}; + +static struct platform_device *mx27vis_aic32x4_snd_device; + +static int __init mx27vis_aic32x4_init(void) +{ + int ret; + + mx27vis_aic32x4_snd_device = platform_device_alloc("soc-audio", -1); + if (!mx27vis_aic32x4_snd_device) + return -ENOMEM; + + platform_set_drvdata(mx27vis_aic32x4_snd_device, &mx27vis_aic32x4); + ret = platform_device_add(mx27vis_aic32x4_snd_device); + + if (ret) { + printk(KERN_ERR "ASoC: Platform device allocation failed\n"); + platform_device_put(mx27vis_aic32x4_snd_device); + } + + /* Connect SSI0 as clock slave to SSI1 external pins */ + mxc_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0, + MXC_AUDMUX_V1_PCR_SYN | + MXC_AUDMUX_V1_PCR_TFSDIR | + MXC_AUDMUX_V1_PCR_TCLKDIR | + MXC_AUDMUX_V1_PCR_TFCSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1) | + MXC_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1) + ); + mxc_audmux_v1_configure_port(MX27_AUDMUX_PPCR1_SSI_PINS_1, + MXC_AUDMUX_V1_PCR_SYN | + MXC_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR1_SSI0) + ); + + return ret; +} + +static void __exit mx27vis_aic32x4_exit(void) +{ + platform_device_unregister(mx27vis_aic32x4_snd_device); +} + +module_init(mx27vis_aic32x4_init); +module_exit(mx27vis_aic32x4_exit); + +MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>"); +MODULE_DESCRIPTION("ALSA SoC AIC32X4 mx27 visstrim"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/mid-x86/Kconfig b/sound/soc/mid-x86/Kconfig new file mode 100644 index 000000000000..29350428f1c2 --- /dev/null +++ b/sound/soc/mid-x86/Kconfig @@ -0,0 +1,14 @@ +config SND_MFLD_MACHINE + tristate "SOC Machine Audio driver for Intel Medfield MID platform" + depends on INTEL_SCU_IPC + depends on SND_INTEL_SST + select SND_SOC_SN95031 + select SND_SST_PLATFORM + help + This adds support for ASoC machine driver for Intel(R) MID Medfield platform + used as alsa device in audio substem in Intel(R) MID devices + Say Y if you have such a device + If unsure select "N". + +config SND_SST_PLATFORM + tristate diff --git a/sound/soc/mid-x86/Makefile b/sound/soc/mid-x86/Makefile new file mode 100644 index 000000000000..639883339465 --- /dev/null +++ b/sound/soc/mid-x86/Makefile @@ -0,0 +1,5 @@ +snd-soc-sst-platform-objs := sst_platform.o +snd-soc-mfld-machine-objs := mfld_machine.o + +obj-$(CONFIG_SND_SST_PLATFORM) += snd-soc-sst-platform.o +obj-$(CONFIG_SND_MFLD_MACHINE) += snd-soc-mfld-machine.o diff --git a/sound/soc/mid-x86/mfld_machine.c b/sound/soc/mid-x86/mfld_machine.c new file mode 100644 index 000000000000..429aa1be2cff --- /dev/null +++ b/sound/soc/mid-x86/mfld_machine.c @@ -0,0 +1,452 @@ +/* + * mfld_machine.c - ASoc Machine driver for Intel Medfield MID platform + * + * Copyright (C) 2010 Intel Corp + * Author: Vinod Koul <vinod.koul@intel.com> + * Author: Harsha Priya <priya.harsha@intel.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 of the License. + * + * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/init.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include "../codecs/sn95031.h" + +#define MID_MONO 1 +#define MID_STEREO 2 +#define MID_MAX_CAP 5 +#define MFLD_JACK_INSERT 0x04 + +enum soc_mic_bias_zones { + MFLD_MV_START = 0, + /* mic bias volutage range for Headphones*/ + MFLD_MV_HP = 400, + /* mic bias volutage range for American Headset*/ + MFLD_MV_AM_HS = 650, + /* mic bias volutage range for Headset*/ + MFLD_MV_HS = 2000, + MFLD_MV_UNDEFINED, +}; + +static unsigned int hs_switch; +static unsigned int lo_dac; + +struct mfld_mc_private { + struct platform_device *socdev; + void __iomem *int_base; + struct snd_soc_codec *codec; + u8 interrupt_status; +}; + +struct snd_soc_jack mfld_jack; + +/*Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin mfld_jack_pins[] = { + { + .pin = "Headphones", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "AMIC1", + .mask = SND_JACK_MICROPHONE, + }, +}; + +/* jack detection voltage zones */ +static struct snd_soc_jack_zone mfld_zones[] = { + {MFLD_MV_START, MFLD_MV_AM_HS, SND_JACK_HEADPHONE}, + {MFLD_MV_AM_HS, MFLD_MV_HS, SND_JACK_HEADSET}, +}; + +/* sound card controls */ +static const char *headset_switch_text[] = {"Earpiece", "Headset"}; + +static const char *lo_text[] = {"Vibra", "Headset", "IHF", "None"}; + +static const struct soc_enum headset_enum = + SOC_ENUM_SINGLE_EXT(2, headset_switch_text); + +static const struct soc_enum lo_enum = + SOC_ENUM_SINGLE_EXT(4, lo_text); + +static int headset_get_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = hs_switch; + return 0; +} + +static int headset_set_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + + if (ucontrol->value.integer.value[0] == hs_switch) + return 0; + + if (ucontrol->value.integer.value[0]) { + pr_debug("hs_set HS path\n"); + snd_soc_dapm_enable_pin(&codec->dapm, "Headphones"); + snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT"); + } else { + pr_debug("hs_set EP path\n"); + snd_soc_dapm_disable_pin(&codec->dapm, "Headphones"); + snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT"); + } + snd_soc_dapm_sync(&codec->dapm); + hs_switch = ucontrol->value.integer.value[0]; + + return 0; +} + +static void lo_enable_out_pins(struct snd_soc_codec *codec) +{ + snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTL"); + snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTR"); + snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTL"); + snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTR"); + snd_soc_dapm_enable_pin(&codec->dapm, "VIB1OUT"); + snd_soc_dapm_enable_pin(&codec->dapm, "VIB2OUT"); + if (hs_switch) { + snd_soc_dapm_enable_pin(&codec->dapm, "Headphones"); + snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT"); + } else { + snd_soc_dapm_disable_pin(&codec->dapm, "Headphones"); + snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT"); + } +} + +static int lo_get_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = lo_dac; + return 0; +} + +static int lo_set_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + + if (ucontrol->value.integer.value[0] == lo_dac) + return 0; + + /* we dont want to work with last state of lineout so just enable all + * pins and then disable pins not required + */ + lo_enable_out_pins(codec); + switch (ucontrol->value.integer.value[0]) { + case 0: + pr_debug("set vibra path\n"); + snd_soc_dapm_disable_pin(&codec->dapm, "VIB1OUT"); + snd_soc_dapm_disable_pin(&codec->dapm, "VIB2OUT"); + snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0); + break; + + case 1: + pr_debug("set hs path\n"); + snd_soc_dapm_disable_pin(&codec->dapm, "Headphones"); + snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT"); + snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x22); + break; + + case 2: + pr_debug("set spkr path\n"); + snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTL"); + snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTR"); + snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x44); + break; + + case 3: + pr_debug("set null path\n"); + snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTL"); + snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTR"); + snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x66); + break; + } + snd_soc_dapm_sync(&codec->dapm); + lo_dac = ucontrol->value.integer.value[0]; + return 0; +} + +static const struct snd_kcontrol_new mfld_snd_controls[] = { + SOC_ENUM_EXT("Playback Switch", headset_enum, + headset_get_switch, headset_set_switch), + SOC_ENUM_EXT("Lineout Mux", lo_enum, + lo_get_switch, lo_set_switch), +}; + +static const struct snd_soc_dapm_widget mfld_widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_MIC("Mic", NULL), +}; + +static const struct snd_soc_dapm_route mfld_map[] = { + {"Headphones", NULL, "HPOUTR"}, + {"Headphones", NULL, "HPOUTL"}, + {"Mic", NULL, "AMIC1"}, +}; + +static void mfld_jack_check(unsigned int intr_status) +{ + struct mfld_jack_data jack_data; + + jack_data.mfld_jack = &mfld_jack; + jack_data.intr_id = intr_status; + + sn95031_jack_detection(&jack_data); + /* TODO: add american headset detection post gpiolib support */ +} + +static int mfld_init(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_codec *codec = runtime->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + int ret_val; + + /* Add jack sense widgets */ + snd_soc_dapm_new_controls(dapm, mfld_widgets, ARRAY_SIZE(mfld_widgets)); + + /* Set up the map */ + snd_soc_dapm_add_routes(dapm, mfld_map, ARRAY_SIZE(mfld_map)); + + /* always connected */ + snd_soc_dapm_enable_pin(dapm, "Headphones"); + snd_soc_dapm_enable_pin(dapm, "Mic"); + snd_soc_dapm_sync(dapm); + + ret_val = snd_soc_add_controls(codec, mfld_snd_controls, + ARRAY_SIZE(mfld_snd_controls)); + if (ret_val) { + pr_err("soc_add_controls failed %d", ret_val); + return ret_val; + } + /* default is earpiece pin, userspace sets it explcitly */ + snd_soc_dapm_disable_pin(dapm, "Headphones"); + /* default is lineout NC, userspace sets it explcitly */ + snd_soc_dapm_disable_pin(dapm, "LINEOUTL"); + snd_soc_dapm_disable_pin(dapm, "LINEOUTR"); + lo_dac = 3; + hs_switch = 0; + /* we dont use linein in this so set to NC */ + snd_soc_dapm_disable_pin(dapm, "LINEINL"); + snd_soc_dapm_disable_pin(dapm, "LINEINR"); + snd_soc_dapm_sync(dapm); + + /* Headset and button jack detection */ + ret_val = snd_soc_jack_new(codec, "Intel(R) MID Audio Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1, &mfld_jack); + if (ret_val) { + pr_err("jack creation failed\n"); + return ret_val; + } + + ret_val = snd_soc_jack_add_pins(&mfld_jack, + ARRAY_SIZE(mfld_jack_pins), mfld_jack_pins); + if (ret_val) { + pr_err("adding jack pins failed\n"); + return ret_val; + } + ret_val = snd_soc_jack_add_zones(&mfld_jack, + ARRAY_SIZE(mfld_zones), mfld_zones); + if (ret_val) { + pr_err("adding jack zones failed\n"); + return ret_val; + } + + /* we want to check if anything is inserted at boot, + * so send a fake event to codec and it will read adc + * to find if anything is there or not */ + mfld_jack_check(MFLD_JACK_INSERT); + return ret_val; +} + +struct snd_soc_dai_link mfld_msic_dailink[] = { + { + .name = "Medfield Headset", + .stream_name = "Headset", + .cpu_dai_name = "Headset-cpu-dai", + .codec_dai_name = "SN95031 Headset", + .codec_name = "sn95031", + .platform_name = "sst-platform", + .init = mfld_init, + }, + { + .name = "Medfield Speaker", + .stream_name = "Speaker", + .cpu_dai_name = "Speaker-cpu-dai", + .codec_dai_name = "SN95031 Speaker", + .codec_name = "sn95031", + .platform_name = "sst-platform", + .init = NULL, + }, + { + .name = "Medfield Vibra", + .stream_name = "Vibra1", + .cpu_dai_name = "Vibra1-cpu-dai", + .codec_dai_name = "SN95031 Vibra1", + .codec_name = "sn95031", + .platform_name = "sst-platform", + .init = NULL, + }, + { + .name = "Medfield Haptics", + .stream_name = "Vibra2", + .cpu_dai_name = "Vibra2-cpu-dai", + .codec_dai_name = "SN95031 Vibra2", + .codec_name = "sn95031", + .platform_name = "sst-platform", + .init = NULL, + }, +}; + +/* SoC card */ +static struct snd_soc_card snd_soc_card_mfld = { + .name = "medfield_audio", + .dai_link = mfld_msic_dailink, + .num_links = ARRAY_SIZE(mfld_msic_dailink), +}; + +static irqreturn_t snd_mfld_jack_intr_handler(int irq, void *dev) +{ + struct mfld_mc_private *mc_private = (struct mfld_mc_private *) dev; + + memcpy_fromio(&mc_private->interrupt_status, + ((void *)(mc_private->int_base)), + sizeof(u8)); + return IRQ_WAKE_THREAD; +} + +static irqreturn_t snd_mfld_jack_detection(int irq, void *data) +{ + struct mfld_mc_private *mc_drv_ctx = (struct mfld_mc_private *) data; + + if (mfld_jack.codec == NULL) + return IRQ_HANDLED; + mfld_jack_check(mc_drv_ctx->interrupt_status); + + return IRQ_HANDLED; +} + +static int __devinit snd_mfld_mc_probe(struct platform_device *pdev) +{ + int ret_val = 0, irq; + struct mfld_mc_private *mc_drv_ctx; + struct resource *irq_mem; + + pr_debug("snd_mfld_mc_probe called\n"); + + /* retrive the irq number */ + irq = platform_get_irq(pdev, 0); + + /* audio interrupt base of SRAM location where + * interrupts are stored by System FW */ + mc_drv_ctx = kzalloc(sizeof(*mc_drv_ctx), GFP_ATOMIC); + if (!mc_drv_ctx) { + pr_err("allocation failed\n"); + return -ENOMEM; + } + + irq_mem = platform_get_resource_byname( + pdev, IORESOURCE_MEM, "IRQ_BASE"); + if (!irq_mem) { + pr_err("no mem resource given\n"); + ret_val = -ENODEV; + goto unalloc; + } + mc_drv_ctx->int_base = ioremap_nocache(irq_mem->start, + resource_size(irq_mem)); + if (!mc_drv_ctx->int_base) { + pr_err("Mapping of cache failed\n"); + ret_val = -ENOMEM; + goto unalloc; + } + /* register for interrupt */ + ret_val = request_threaded_irq(irq, snd_mfld_jack_intr_handler, + snd_mfld_jack_detection, + IRQF_SHARED, pdev->dev.driver->name, mc_drv_ctx); + if (ret_val) { + pr_err("cannot register IRQ\n"); + goto unalloc; + } + /* register the soc card */ + snd_soc_card_mfld.dev = &pdev->dev; + ret_val = snd_soc_register_card(&snd_soc_card_mfld); + if (ret_val) { + pr_debug("snd_soc_register_card failed %d\n", ret_val); + goto freeirq; + } + platform_set_drvdata(pdev, mc_drv_ctx); + pr_debug("successfully exited probe\n"); + return ret_val; + +freeirq: + free_irq(irq, mc_drv_ctx); +unalloc: + kfree(mc_drv_ctx); + return ret_val; +} + +static int __devexit snd_mfld_mc_remove(struct platform_device *pdev) +{ + struct mfld_mc_private *mc_drv_ctx = platform_get_drvdata(pdev); + + pr_debug("snd_mfld_mc_remove called\n"); + free_irq(platform_get_irq(pdev, 0), mc_drv_ctx); + snd_soc_unregister_card(&snd_soc_card_mfld); + kfree(mc_drv_ctx); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver snd_mfld_mc_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "msic_audio", + }, + .probe = snd_mfld_mc_probe, + .remove = __devexit_p(snd_mfld_mc_remove), +}; + +static int __init snd_mfld_driver_init(void) +{ + pr_debug("snd_mfld_driver_init called\n"); + return platform_driver_register(&snd_mfld_mc_driver); +} +module_init(snd_mfld_driver_init); + +static void __exit snd_mfld_driver_exit(void) +{ + pr_debug("snd_mfld_driver_exit called\n"); + platform_driver_unregister(&snd_mfld_mc_driver); +} +module_exit(snd_mfld_driver_exit); + +MODULE_DESCRIPTION("ASoC Intel(R) MID Machine driver"); +MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); +MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:msic-audio"); diff --git a/sound/soc/mid-x86/sst_platform.c b/sound/soc/mid-x86/sst_platform.c new file mode 100644 index 000000000000..ee2c22475a76 --- /dev/null +++ b/sound/soc/mid-x86/sst_platform.c @@ -0,0 +1,474 @@ +/* + * sst_platform.c - Intel MID Platform driver + * + * Copyright (C) 2010 Intel Corp + * Author: Vinod Koul <vinod.koul@intel.com> + * Author: Harsha Priya <priya.harsha@intel.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 of the License. + * + * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/slab.h> +#include <linux/io.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include "../../../drivers/staging/intel_sst/intel_sst_ioctl.h" +#include "../../../drivers/staging/intel_sst/intel_sst.h" +#include "sst_platform.h" + +static struct snd_pcm_hardware sst_platform_pcm_hw = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_DOUBLE | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_MMAP| + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_SYNC_START), + .formats = (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16 | + SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_U24 | + SNDRV_PCM_FMTBIT_S32 | SNDRV_PCM_FMTBIT_U32), + .rates = (SNDRV_PCM_RATE_8000| + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000), + .rate_min = SST_MIN_RATE, + .rate_max = SST_MAX_RATE, + .channels_min = SST_MIN_CHANNEL, + .channels_max = SST_MAX_CHANNEL, + .buffer_bytes_max = SST_MAX_BUFFER, + .period_bytes_min = SST_MIN_PERIOD_BYTES, + .period_bytes_max = SST_MAX_PERIOD_BYTES, + .periods_min = SST_MIN_PERIODS, + .periods_max = SST_MAX_PERIODS, + .fifo_size = SST_FIFO_SIZE, +}; + +/* MFLD - MSIC */ +struct snd_soc_dai_driver sst_platform_dai[] = { +{ + .name = "Headset-cpu-dai", + .id = 0, + .playback = { + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S24_LE, + }, + .capture = { + .channels_min = 1, + .channels_max = 5, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +{ + .name = "Speaker-cpu-dai", + .id = 1, + .playback = { + .channels_min = SST_MONO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +{ + .name = "Vibra1-cpu-dai", + .id = 2, + .playback = { + .channels_min = SST_MONO, + .channels_max = SST_MONO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +{ + .name = "Vibra2-cpu-dai", + .id = 3, + .playback = { + .channels_min = SST_MONO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +}; + +/* helper functions */ +static inline void sst_set_stream_status(struct sst_runtime_stream *stream, + int state) +{ + spin_lock(&stream->status_lock); + stream->stream_status = state; + spin_unlock(&stream->status_lock); +} + +static inline int sst_get_stream_status(struct sst_runtime_stream *stream) +{ + int state; + + spin_lock(&stream->status_lock); + state = stream->stream_status; + spin_unlock(&stream->status_lock); + return state; +} + +static void sst_fill_pcm_params(struct snd_pcm_substream *substream, + struct snd_sst_stream_params *param) +{ + + param->uc.pcm_params.codec = SST_CODEC_TYPE_PCM; + param->uc.pcm_params.num_chan = (u8) substream->runtime->channels; + param->uc.pcm_params.pcm_wd_sz = substream->runtime->sample_bits; + param->uc.pcm_params.reserved = 0; + param->uc.pcm_params.sfreq = substream->runtime->rate; + param->uc.pcm_params.ring_buffer_size = + snd_pcm_lib_buffer_bytes(substream); + param->uc.pcm_params.period_count = substream->runtime->period_size; + param->uc.pcm_params.ring_buffer_addr = + virt_to_phys(substream->dma_buffer.area); + pr_debug("period_cnt = %d\n", param->uc.pcm_params.period_count); + pr_debug("sfreq= %d, wd_sz = %d\n", + param->uc.pcm_params.sfreq, param->uc.pcm_params.pcm_wd_sz); +} + +static int sst_platform_alloc_stream(struct snd_pcm_substream *substream) +{ + struct sst_runtime_stream *stream = + substream->runtime->private_data; + struct snd_sst_stream_params param = {{{0,},},}; + struct snd_sst_params str_params = {0}; + int ret_val; + + /* set codec params and inform SST driver the same */ + sst_fill_pcm_params(substream, ¶m); + substream->runtime->dma_area = substream->dma_buffer.area; + str_params.sparams = param; + str_params.codec = param.uc.pcm_params.codec; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + str_params.ops = STREAM_OPS_PLAYBACK; + str_params.device_type = substream->pcm->device + 1; + pr_debug("Playbck stream,Device %d\n", + substream->pcm->device); + } else { + str_params.ops = STREAM_OPS_CAPTURE; + str_params.device_type = SND_SST_DEVICE_CAPTURE; + pr_debug("Capture stream,Device %d\n", + substream->pcm->device); + } + ret_val = stream->sstdrv_ops->pcm_control->open(&str_params); + pr_debug("SST_SND_PLAY/CAPTURE ret_val = %x\n", ret_val); + if (ret_val < 0) + return ret_val; + + stream->stream_info.str_id = ret_val; + pr_debug("str id : %d\n", stream->stream_info.str_id); + return ret_val; +} + +static void sst_period_elapsed(void *mad_substream) +{ + struct snd_pcm_substream *substream = mad_substream; + struct sst_runtime_stream *stream; + int status; + + if (!substream || !substream->runtime) + return; + stream = substream->runtime->private_data; + if (!stream) + return; + status = sst_get_stream_status(stream); + if (status != SST_PLATFORM_RUNNING) + return; + snd_pcm_period_elapsed(substream); +} + +static int sst_platform_init_stream(struct snd_pcm_substream *substream) +{ + struct sst_runtime_stream *stream = + substream->runtime->private_data; + int ret_val; + + pr_debug("setting buffer ptr param\n"); + sst_set_stream_status(stream, SST_PLATFORM_INIT); + stream->stream_info.period_elapsed = sst_period_elapsed; + stream->stream_info.mad_substream = substream; + stream->stream_info.buffer_ptr = 0; + stream->stream_info.sfreq = substream->runtime->rate; + ret_val = stream->sstdrv_ops->pcm_control->device_control( + SST_SND_STREAM_INIT, &stream->stream_info); + if (ret_val) + pr_err("control_set ret error %d\n", ret_val); + return ret_val; + +} +/* end -- helper functions */ + +static int sst_platform_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime; + struct sst_runtime_stream *stream; + int ret_val = 0; + + pr_debug("sst_platform_open called\n"); + runtime = substream->runtime; + runtime->hw = sst_platform_pcm_hw; + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; + spin_lock_init(&stream->status_lock); + stream->stream_info.str_id = 0; + sst_set_stream_status(stream, SST_PLATFORM_INIT); + stream->stream_info.mad_substream = substream; + /* allocate memory for SST API set */ + stream->sstdrv_ops = kzalloc(sizeof(*stream->sstdrv_ops), + GFP_KERNEL); + if (!stream->sstdrv_ops) { + pr_err("sst: mem allocation for ops fail\n"); + kfree(stream); + return -ENOMEM; + } + stream->sstdrv_ops->vendor_id = MSIC_VENDOR_ID; + /* registering with SST driver to get access to SST APIs to use */ + ret_val = register_sst_card(stream->sstdrv_ops); + if (ret_val) { + pr_err("sst: sst card registration failed\n"); + return ret_val; + } + runtime->private_data = stream; + return snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); +} + +static int sst_platform_close(struct snd_pcm_substream *substream) +{ + struct sst_runtime_stream *stream; + int ret_val = 0, str_id; + + pr_debug("sst_platform_close called\n"); + stream = substream->runtime->private_data; + str_id = stream->stream_info.str_id; + if (str_id) + ret_val = stream->sstdrv_ops->pcm_control->close(str_id); + kfree(stream->sstdrv_ops); + kfree(stream); + return ret_val; +} + +static int sst_platform_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct sst_runtime_stream *stream; + int ret_val = 0, str_id; + + pr_debug("sst_platform_pcm_prepare called\n"); + stream = substream->runtime->private_data; + str_id = stream->stream_info.str_id; + if (stream->stream_info.str_id) { + ret_val = stream->sstdrv_ops->pcm_control->device_control( + SST_SND_DROP, &str_id); + return ret_val; + } + + ret_val = sst_platform_alloc_stream(substream); + if (ret_val < 0) + return ret_val; + snprintf(substream->pcm->id, sizeof(substream->pcm->id), + "%d", stream->stream_info.str_id); + + ret_val = sst_platform_init_stream(substream); + if (ret_val) + return ret_val; + substream->runtime->hw.info = SNDRV_PCM_INFO_BLOCK_TRANSFER; + return ret_val; +} + +static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + int ret_val = 0, str_id; + struct sst_runtime_stream *stream; + int str_cmd, status; + + pr_debug("sst_platform_pcm_trigger called\n"); + stream = substream->runtime->private_data; + str_id = stream->stream_info.str_id; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + pr_debug("sst: Trigger Start\n"); + str_cmd = SST_SND_START; + status = SST_PLATFORM_RUNNING; + stream->stream_info.mad_substream = substream; + break; + case SNDRV_PCM_TRIGGER_STOP: + pr_debug("sst: in stop\n"); + str_cmd = SST_SND_DROP; + status = SST_PLATFORM_DROPPED; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + pr_debug("sst: in pause\n"); + str_cmd = SST_SND_PAUSE; + status = SST_PLATFORM_PAUSED; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + pr_debug("sst: in pause release\n"); + str_cmd = SST_SND_RESUME; + status = SST_PLATFORM_RUNNING; + break; + default: + return -EINVAL; + } + ret_val = stream->sstdrv_ops->pcm_control->device_control(str_cmd, + &str_id); + if (!ret_val) + sst_set_stream_status(stream, status); + + return ret_val; +} + + +static snd_pcm_uframes_t sst_platform_pcm_pointer + (struct snd_pcm_substream *substream) +{ + struct sst_runtime_stream *stream; + int ret_val, status; + struct pcm_stream_info *str_info; + + stream = substream->runtime->private_data; + status = sst_get_stream_status(stream); + if (status == SST_PLATFORM_INIT) + return 0; + str_info = &stream->stream_info; + ret_val = stream->sstdrv_ops->pcm_control->device_control( + SST_SND_BUFFER_POINTER, str_info); + if (ret_val) { + pr_err("sst: error code = %d\n", ret_val); + return ret_val; + } + return stream->stream_info.buffer_ptr; +} + +static int sst_platform_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + memset(substream->runtime->dma_area, 0, params_buffer_bytes(params)); + + return 0; +} + +static struct snd_pcm_ops sst_platform_ops = { + .open = sst_platform_open, + .close = sst_platform_close, + .ioctl = snd_pcm_lib_ioctl, + .prepare = sst_platform_pcm_prepare, + .trigger = sst_platform_pcm_trigger, + .pointer = sst_platform_pcm_pointer, + .hw_params = sst_platform_pcm_hw_params, +}; + +static void sst_pcm_free(struct snd_pcm *pcm) +{ + pr_debug("sst_pcm_free called\n"); + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int sst_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, + struct snd_pcm *pcm) +{ + int retval = 0; + + pr_debug("sst_pcm_new called\n"); + if (dai->driver->playback.channels_min || + dai->driver->capture.channels_min) { + retval = snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + SST_MIN_BUFFER, SST_MAX_BUFFER); + if (retval) { + pr_err("dma buffer allocationf fail\n"); + return retval; + } + } + return retval; +} +struct snd_soc_platform_driver sst_soc_platform_drv = { + .ops = &sst_platform_ops, + .pcm_new = sst_pcm_new, + .pcm_free = sst_pcm_free, +}; + +static int sst_platform_probe(struct platform_device *pdev) +{ + int ret; + + pr_debug("sst_platform_probe called\n"); + ret = snd_soc_register_platform(&pdev->dev, &sst_soc_platform_drv); + if (ret) { + pr_err("registering soc platform failed\n"); + return ret; + } + + ret = snd_soc_register_dais(&pdev->dev, + sst_platform_dai, ARRAY_SIZE(sst_platform_dai)); + if (ret) { + pr_err("registering cpu dais failed\n"); + snd_soc_unregister_platform(&pdev->dev); + } + return ret; +} + +static int sst_platform_remove(struct platform_device *pdev) +{ + + snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(sst_platform_dai)); + snd_soc_unregister_platform(&pdev->dev); + pr_debug("sst_platform_remove sucess\n"); + return 0; +} + +static struct platform_driver sst_platform_driver = { + .driver = { + .name = "sst-platform", + .owner = THIS_MODULE, + }, + .probe = sst_platform_probe, + .remove = sst_platform_remove, +}; + +static int __init sst_soc_platform_init(void) +{ + pr_debug("sst_soc_platform_init called\n"); + return platform_driver_register(&sst_platform_driver); +} +module_init(sst_soc_platform_init); + +static void __exit sst_soc_platform_exit(void) +{ + platform_driver_unregister(&sst_platform_driver); + pr_debug("sst_soc_platform_exit sucess\n"); +} +module_exit(sst_soc_platform_exit); + +MODULE_DESCRIPTION("ASoC Intel(R) MID Platform driver"); +MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); +MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sst-platform"); diff --git a/sound/soc/mid-x86/sst_platform.h b/sound/soc/mid-x86/sst_platform.h new file mode 100644 index 000000000000..df370286694f --- /dev/null +++ b/sound/soc/mid-x86/sst_platform.h @@ -0,0 +1,63 @@ +/* + * sst_platform.h - Intel MID Platform driver header file + * + * Copyright (C) 2010 Intel Corp + * Author: Vinod Koul <vinod.koul@intel.com> + * Author: Harsha Priya <priya.harsha@intel.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 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * + */ + +#ifndef __SST_PLATFORMDRV_H__ +#define __SST_PLATFORMDRV_H__ + +#define SST_MONO 1 +#define SST_STEREO 2 +#define SST_MAX_CAP 5 + +#define SST_MIN_RATE 8000 +#define SST_MAX_RATE 48000 +#define SST_MIN_CHANNEL 1 +#define SST_MAX_CHANNEL 5 +#define SST_MAX_BUFFER (800*1024) +#define SST_MIN_BUFFER (800*1024) +#define SST_MIN_PERIOD_BYTES 32 +#define SST_MAX_PERIOD_BYTES SST_MAX_BUFFER +#define SST_MIN_PERIODS 2 +#define SST_MAX_PERIODS (1024*2) +#define SST_FIFO_SIZE 0 +#define SST_CARD_NAMES "intel_mid_card" +#define MSIC_VENDOR_ID 3 + +struct sst_runtime_stream { + int stream_status; + struct pcm_stream_info stream_info; + struct intel_sst_card_ops *sstdrv_ops; + spinlock_t status_lock; +}; + +enum sst_drv_status { + SST_PLATFORM_INIT = 1, + SST_PLATFORM_STARTED, + SST_PLATFORM_RUNNING, + SST_PLATFORM_PAUSED, + SST_PLATFORM_DROPPED, +}; + +#endif diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index a088db6d5091..b5922984eac6 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -24,6 +24,7 @@ config SND_OMAP_SOC_RX51 select OMAP_MCBSP select SND_OMAP_SOC_MCBSP select SND_SOC_TLV320AIC3X + select SND_SOC_TPA6130A2 help Say Y if you want to add support for SoC audio on Nokia RX-51 hardware. This is also known as Nokia N900 product. diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c index 09fb0df8d416..d0986220eff9 100644 --- a/sound/soc/omap/rx51.c +++ b/sound/soc/omap/rx51.c @@ -31,6 +31,7 @@ #include <sound/pcm.h> #include <sound/soc.h> #include <plat/mcbsp.h> +#include "../codecs/tpa6130a2.h" #include <asm/mach-types.h> @@ -39,6 +40,7 @@ #define RX51_TVOUT_SEL_GPIO 40 #define RX51_JACK_DETECT_GPIO 177 +#define RX51_ECI_SW_GPIO 182 /* * REVISIT: TWL4030 GPIO base in RX-51. Now statically defined to 192. This * gpio is reserved in arch/arm/mach-omap2/board-rx51-peripherals.c @@ -47,7 +49,9 @@ enum { RX51_JACK_DISABLED, - RX51_JACK_TVOUT, /* tv-out */ + RX51_JACK_TVOUT, /* tv-out with stereo output */ + RX51_JACK_HP, /* headphone: stereo output, no mic */ + RX51_JACK_HS, /* headset: stereo output with mic */ }; static int rx51_spk_func; @@ -57,6 +61,19 @@ static int rx51_jack_func; static void rx51_ext_control(struct snd_soc_codec *codec) { struct snd_soc_dapm_context *dapm = &codec->dapm; + int hp = 0, hs = 0, tvout = 0; + + switch (rx51_jack_func) { + case RX51_JACK_TVOUT: + tvout = 1; + hp = 1; + break; + case RX51_JACK_HS: + hs = 1; + case RX51_JACK_HP: + hp = 1; + break; + } if (rx51_spk_func) snd_soc_dapm_enable_pin(dapm, "Ext Spk"); @@ -66,9 +83,16 @@ static void rx51_ext_control(struct snd_soc_codec *codec) snd_soc_dapm_enable_pin(dapm, "DMic"); else snd_soc_dapm_disable_pin(dapm, "DMic"); + if (hp) + snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); + else + snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); + if (hs) + snd_soc_dapm_enable_pin(dapm, "HS Mic"); + else + snd_soc_dapm_disable_pin(dapm, "HS Mic"); - gpio_set_value(RX51_TVOUT_SEL_GPIO, - rx51_jack_func == RX51_JACK_TVOUT); + gpio_set_value(RX51_TVOUT_SEL_GPIO, tvout); snd_soc_dapm_sync(dapm); } @@ -153,6 +177,19 @@ static int rx51_spk_event(struct snd_soc_dapm_widget *w, return 0; } +static int rx51_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_codec *codec = w->dapm->codec; + + if (SND_SOC_DAPM_EVENT_ON(event)) + tpa6130a2_stereo_enable(codec, 1); + else + tpa6130a2_stereo_enable(codec, 0); + + return 0; +} + static int rx51_get_input(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -203,7 +240,7 @@ static struct snd_soc_jack_gpio rx51_av_jack_gpios[] = { { .gpio = RX51_JACK_DETECT_GPIO, .name = "avdet-gpio", - .report = SND_JACK_VIDEOOUT, + .report = SND_JACK_HEADSET, .invert = 1, .debounce_time = 200, }, @@ -212,19 +249,38 @@ static struct snd_soc_jack_gpio rx51_av_jack_gpios[] = { static const struct snd_soc_dapm_widget aic34_dapm_widgets[] = { SND_SOC_DAPM_SPK("Ext Spk", rx51_spk_event), SND_SOC_DAPM_MIC("DMic", NULL), + SND_SOC_DAPM_HP("Headphone Jack", rx51_hp_event), + SND_SOC_DAPM_MIC("HS Mic", NULL), + SND_SOC_DAPM_LINE("FM Transmitter", NULL), +}; + +static const struct snd_soc_dapm_widget aic34_dapm_widgetsb[] = { + SND_SOC_DAPM_SPK("Earphone", NULL), }; static const struct snd_soc_dapm_route audio_map[] = { {"Ext Spk", NULL, "HPLOUT"}, {"Ext Spk", NULL, "HPROUT"}, + {"Headphone Jack", NULL, "LLOUT"}, + {"Headphone Jack", NULL, "RLOUT"}, + {"FM Transmitter", NULL, "LLOUT"}, + {"FM Transmitter", NULL, "RLOUT"}, {"DMic Rate 64", NULL, "Mic Bias 2V"}, {"Mic Bias 2V", NULL, "DMic"}, }; +static const struct snd_soc_dapm_route audio_mapb[] = { + {"b LINE2R", NULL, "MONO_LOUT"}, + {"Earphone", NULL, "b HPLOUT"}, + + {"LINE1L", NULL, "b Mic Bias 2.5V"}, + {"b Mic Bias 2.5V", NULL, "HS Mic"} +}; + static const char *spk_function[] = {"Off", "On"}; static const char *input_function[] = {"ADC", "Digital Mic"}; -static const char *jack_function[] = {"Off", "TV-OUT"}; +static const char *jack_function[] = {"Off", "TV-OUT", "Headphone", "Headset"}; static const struct soc_enum rx51_enum[] = { SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function), @@ -239,6 +295,11 @@ static const struct snd_kcontrol_new aic34_rx51_controls[] = { rx51_get_input, rx51_set_input), SOC_ENUM_EXT("Jack Function", rx51_enum[2], rx51_get_jack, rx51_set_jack), + SOC_DAPM_PIN_SWITCH("FM Transmitter"), +}; + +static const struct snd_kcontrol_new aic34_rx51_controlsb[] = { + SOC_DAPM_PIN_SWITCH("Earphone"), }; static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd) @@ -265,11 +326,21 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd) /* Set up RX-51 specific audio path audio_map */ snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); + err = tpa6130a2_add_controls(codec); + if (err < 0) + return err; + snd_soc_limit_volume(codec, "TPA6130A2 Headphone Playback Volume", 42); + + err = omap_mcbsp_st_add_controls(codec, 1); + if (err < 0) + return err; + snd_soc_dapm_sync(dapm); /* AV jack detection */ err = snd_soc_jack_new(codec, "AV Jack", - SND_JACK_VIDEOOUT, &rx51_av_jack); + SND_JACK_HEADSET | SND_JACK_VIDEOOUT, + &rx51_av_jack); if (err) return err; err = snd_soc_jack_add_gpios(&rx51_av_jack, @@ -279,6 +350,24 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd) return err; } +static int rx51_aic34b_init(struct snd_soc_dapm_context *dapm) +{ + int err; + + err = snd_soc_add_controls(dapm->codec, aic34_rx51_controlsb, + ARRAY_SIZE(aic34_rx51_controlsb)); + if (err < 0) + return err; + + err = snd_soc_dapm_new_controls(dapm, aic34_dapm_widgetsb, + ARRAY_SIZE(aic34_dapm_widgetsb)); + if (err < 0) + return 0; + + return snd_soc_dapm_add_routes(dapm, audio_mapb, + ARRAY_SIZE(audio_mapb)); +} + /* Digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link rx51_dai[] = { { @@ -293,11 +382,30 @@ static struct snd_soc_dai_link rx51_dai[] = { }, }; +struct snd_soc_aux_dev rx51_aux_dev[] = { + { + .name = "TLV320AIC34b", + .codec_name = "tlv320aic3x-codec.2-0019", + .init = rx51_aic34b_init, + }, +}; + +static struct snd_soc_codec_conf rx51_codec_conf[] = { + { + .dev_name = "tlv320aic3x-codec.2-0019", + .name_prefix = "b", + }, +}; + /* Audio card */ static struct snd_soc_card rx51_sound_card = { .name = "RX-51", .dai_link = rx51_dai, .num_links = ARRAY_SIZE(rx51_dai), + .aux_dev = rx51_aux_dev, + .num_aux_devs = ARRAY_SIZE(rx51_aux_dev), + .codec_conf = rx51_codec_conf, + .num_configs = ARRAY_SIZE(rx51_codec_conf), }; static struct platform_device *rx51_snd_device; @@ -309,10 +417,14 @@ static int __init rx51_soc_init(void) if (!machine_is_nokia_rx51()) return -ENODEV; - err = gpio_request(RX51_TVOUT_SEL_GPIO, "tvout_sel"); + err = gpio_request_one(RX51_TVOUT_SEL_GPIO, + GPIOF_DIR_OUT | GPIOF_INIT_LOW, "tvout_sel"); if (err) goto err_gpio_tvout_sel; - gpio_direction_output(RX51_TVOUT_SEL_GPIO, 0); + err = gpio_request_one(RX51_ECI_SW_GPIO, + GPIOF_DIR_OUT | GPIOF_INIT_HIGH, "eci_sw"); + if (err) + goto err_gpio_eci_sw; rx51_snd_device = platform_device_alloc("soc-audio", -1); if (!rx51_snd_device) { @@ -330,6 +442,8 @@ static int __init rx51_soc_init(void) err2: platform_device_put(rx51_snd_device); err1: + gpio_free(RX51_ECI_SW_GPIO); +err_gpio_eci_sw: gpio_free(RX51_TVOUT_SEL_GPIO); err_gpio_tvout_sel: @@ -342,6 +456,7 @@ static void __exit rx51_soc_exit(void) rx51_av_jack_gpios); platform_device_unregister(rx51_snd_device); + gpio_free(RX51_ECI_SW_GPIO); gpio_free(RX51_TVOUT_SEL_GPIO); } diff --git a/sound/soc/pxa/raumfeld.c b/sound/soc/pxa/raumfeld.c index 0fd60f423036..2afabaf59491 100644 --- a/sound/soc/pxa/raumfeld.c +++ b/sound/soc/pxa/raumfeld.c @@ -151,13 +151,13 @@ static struct snd_soc_ops raumfeld_cs4270_ops = { .hw_params = raumfeld_cs4270_hw_params, }; -static int raumfeld_line_suspend(struct platform_device *pdev, pm_message_t state) +static int raumfeld_line_suspend(struct snd_soc_card *card) { raumfeld_enable_audio(false); return 0; } -static int raumfeld_line_resume(struct platform_device *pdev) +static int raumfeld_line_resume(struct snd_soc_card *card) { raumfeld_enable_audio(true); return 0; @@ -229,19 +229,19 @@ static struct snd_soc_dai_link raumfeld_dai[] = { { .name = "ak4104", .stream_name = "Playback", - .cpu_dai_name = "pxa-ssp-dai.1", - .codec_dai_name = "ak4104-hifi", - .platform_name = "pxa-pcm-audio", + .cpu_dai_name = "pxa-ssp-dai.1", + .codec_dai_name = "ak4104-hifi", + .platform_name = "pxa-pcm-audio", .ops = &raumfeld_ak4104_ops, - .codec_name = "ak4104-codec.0", + .codec_name = "ak4104-codec.0", }, { .name = "CS4270", .stream_name = "CS4270", - .cpu_dai_name = "pxa-ssp-dai.0", - .platform_name = "pxa-pcm-audio", - .codec_dai_name = "cs4270-hifi", - .codec_name = "cs4270-codec.0-0048", + .cpu_dai_name = "pxa-ssp-dai.0", + .platform_name = "pxa-pcm-audio", + .codec_dai_name = "cs4270-hifi", + .codec_name = "cs4270-codec.0-0048", .ops = &raumfeld_cs4270_ops, },}; diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c index 4b6e5d608b42..9a2351366957 100644 --- a/sound/soc/pxa/tosa.c +++ b/sound/soc/pxa/tosa.c @@ -237,7 +237,7 @@ static struct snd_soc_dai_link tosa_dai[] = { }, }; -static int tosa_probe(struct platform_device *dev) +static int tosa_probe(struct snd_soc_card *card) { int ret; @@ -251,7 +251,7 @@ static int tosa_probe(struct platform_device *dev) return ret; } -static int tosa_remove(struct platform_device *dev) +static int tosa_remove(struct snd_soc_card *card) { gpio_free(TOSA_GPIO_L_MUTE); return 0; diff --git a/sound/soc/pxa/z2.c b/sound/soc/pxa/z2.c index 3ceaef68e01d..d69d9fc32233 100644 --- a/sound/soc/pxa/z2.c +++ b/sound/soc/pxa/z2.c @@ -95,6 +95,11 @@ static struct snd_soc_jack_pin hs_jack_pins[] = { .pin = "Headphone Jack", .mask = SND_JACK_HEADPHONE, }, + { + .pin = "Ext Spk", + .mask = SND_JACK_HEADPHONE, + .invert = 1 + }, }; /* Headset jack detection gpios */ @@ -147,7 +152,7 @@ static int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd) snd_soc_dapm_disable_pin(dapm, "LINPUT3"); snd_soc_dapm_disable_pin(dapm, "RINPUT3"); snd_soc_dapm_disable_pin(dapm, "OUT3"); - snd_soc_dapm_disable_pin(dapm, "MONO"); + snd_soc_dapm_disable_pin(dapm, "MONO1"); /* Add z2 specific widgets */ snd_soc_dapm_new_controls(dapm, wm8750_dapm_widgets, diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c index 25bba108fea3..ac577263b3e3 100644 --- a/sound/soc/pxa/zylonite.c +++ b/sound/soc/pxa/zylonite.c @@ -189,7 +189,7 @@ static struct snd_soc_dai_link zylonite_dai[] = { }, }; -static int zylonite_probe(struct platform_device *pdev) +static int zylonite_probe(struct snd_soc_card *card) { int ret; @@ -216,7 +216,7 @@ static int zylonite_probe(struct platform_device *pdev) return 0; } -static int zylonite_remove(struct platform_device *pdev) +static int zylonite_remove(struct snd_soc_card *card) { if (clk_pout) { clk_disable(pout); @@ -226,8 +226,7 @@ static int zylonite_remove(struct platform_device *pdev) return 0; } -static int zylonite_suspend_post(struct platform_device *pdev, - pm_message_t state) +static int zylonite_suspend_post(struct snd_soc_card *card) { if (clk_pout) clk_disable(pout); @@ -235,7 +234,7 @@ static int zylonite_suspend_post(struct platform_device *pdev, return 0; } -static int zylonite_resume_pre(struct platform_device *pdev) +static int zylonite_resume_pre(struct snd_soc_card *card) { int ret = 0; diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index d6713d5a90e7..a3fdfb631469 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -35,23 +35,16 @@ config SND_SAMSUNG_I2S tristate config SND_SOC_SAMSUNG_NEO1973_WM8753 - tristate "SoC I2S Audio support for NEO1973 - WM8753" - depends on SND_SOC_SAMSUNG && MACH_NEO1973_GTA01 + tristate "Audio support for Openmoko Neo1973 Smartphones (GTA01/GTA02)" + depends on SND_SOC_SAMSUNG && (MACH_NEO1973_GTA01 || MACH_NEO1973_GTA02) select SND_S3C24XX_I2S select SND_SOC_WM8753 + select SND_SOC_LM4857 if MACH_NEO1973_GTA01 + select SND_SOC_DFBMCS320 help - Say Y if you want to add support for SoC audio on smdk2440 - with the WM8753. + Say Y here to enable audio support for the Openmoko Neo1973 + Smartphones. -config SND_SOC_SAMSUNG_NEO1973_GTA02_WM8753 - tristate "Audio support for the Openmoko Neo FreeRunner (GTA02)" - depends on SND_SOC_SAMSUNG && MACH_NEO1973_GTA02 - select SND_S3C24XX_I2S - select SND_SOC_WM8753 - help - This driver provides audio support for the Openmoko Neo FreeRunner - smartphone. - config SND_SOC_SAMSUNG_JIVE_WM8750 tristate "SoC I2S Audio support for Jive" depends on SND_SOC_SAMSUNG && MACH_JIVE diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile index 705d4e8a6724..294dec05c26d 100644 --- a/sound/soc/samsung/Makefile +++ b/sound/soc/samsung/Makefile @@ -20,7 +20,6 @@ obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-i2s.o # S3C24XX Machine Support snd-soc-jive-wm8750-objs := jive_wm8750.o snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o -snd-soc-neo1973-gta02-wm8753-objs := neo1973_gta02_wm8753.o snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o @@ -38,7 +37,6 @@ snd-soc-smdk-spdif-objs := smdk_spdif.o obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o -obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_GTA02_WM8753) += snd-soc-neo1973-gta02-wm8753.o obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o obj-$(CONFIG_SND_SOC_SAMSUNG_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o obj-$(CONFIG_SND_SOC_SAMSUNG_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c index 4770a9550341..f97110e72e85 100644 --- a/sound/soc/samsung/ac97.c +++ b/sound/soc/samsung/ac97.c @@ -12,24 +12,24 @@ * published by the Free Software Foundation. */ -#include <linux/init.h> -#include <linux/module.h> #include <linux/io.h> #include <linux/delay.h> #include <linux/clk.h> #include <sound/soc.h> -#include <plat/regs-ac97.h> #include <mach/dma.h> +#include <plat/regs-ac97.h> #include <plat/audio.h> #include "dma.h" -#include "ac97.h" #define AC_CMD_ADDR(x) (x << 16) #define AC_CMD_DATA(x) (x & 0xffff) +#define S3C_AC97_DAI_PCM 0 +#define S3C_AC97_DAI_MIC 1 + struct s3c_ac97_info { struct clk *ac97_clk; void __iomem *regs; diff --git a/sound/soc/samsung/ac97.h b/sound/soc/samsung/ac97.h deleted file mode 100644 index 0d0e1b511457..000000000000 --- a/sound/soc/samsung/ac97.h +++ /dev/null @@ -1,21 +0,0 @@ -/* sound/soc/samsung/ac97.h - * - * ALSA SoC Audio Layer - S3C AC97 Controller driver - * Evolved from s3c2443-ac97.h - * - * Copyright (c) 2010 Samsung Electronics Co. Ltd - * Author: Jaswinder Singh <jassi.brar@samsung.com> - * Credits: Graeme Gregory, Sean Choi - * - * 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 __S3C_AC97_H_ -#define __S3C_AC97_H_ - -#define S3C_AC97_DAI_PCM 0 -#define S3C_AC97_DAI_MIC 1 - -#endif /* __S3C_AC97_H_ */ diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c index 21240198c5d6..5cb3b880f0d5 100644 --- a/sound/soc/samsung/dma.c +++ b/sound/soc/samsung/dma.c @@ -14,17 +14,11 @@ * option) any later version. */ -#include <linux/module.h> -#include <linux/init.h> -#include <linux/io.h> -#include <linux/platform_device.h> #include <linux/slab.h> #include <linux/dma-mapping.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/pcm_params.h> #include <asm/dma.h> #include <mach/hardware.h> @@ -32,6 +26,9 @@ #include "dma.h" +#define ST_RUNNING (1<<0) +#define ST_OPENED (1<<1) + static const struct snd_pcm_hardware dma_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | @@ -313,7 +310,7 @@ dma_pointer(struct snd_pcm_substream *substream) /* we seem to be getting the odd error from the pcm library due * to out-of-bounds pointers. this is maybe due to the dma engine * not having loaded the new values for the channel before being - * callled... (todo - fix ) + * called... (todo - fix ) */ if (res >= snd_pcm_lib_buffer_bytes(substream)) { diff --git a/sound/soc/samsung/dma.h b/sound/soc/samsung/dma.h index f8cd2b4223af..c50659269a40 100644 --- a/sound/soc/samsung/dma.h +++ b/sound/soc/samsung/dma.h @@ -12,9 +12,6 @@ #ifndef _S3C_AUDIO_H #define _S3C_AUDIO_H -#define ST_RUNNING (1<<0) -#define ST_OPENED (1<<1) - struct s3c_dma_params { struct s3c2410_dma_client *client; /* stream identifier */ int channel; /* Channel ID */ @@ -22,9 +19,4 @@ struct s3c_dma_params { int dma_size; /* Size of the DMA transfer */ }; -#define S3C24XX_DAI_I2S 0 - -/* platform data */ -extern struct snd_ac97_bus_ops s3c24xx_ac97_ops; - #endif diff --git a/sound/soc/samsung/goni_wm8994.c b/sound/soc/samsung/goni_wm8994.c index 34dd9ef1b9c0..f6b3a3ce5919 100644 --- a/sound/soc/samsung/goni_wm8994.c +++ b/sound/soc/samsung/goni_wm8994.c @@ -11,21 +11,13 @@ * */ -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/io.h> -#include <linux/platform_device.h> #include <sound/soc.h> #include <sound/jack.h> + #include <asm/mach-types.h> #include <mach/gpio.h> -#include <mach/regs-clock.h> -#include <linux/mfd/wm8994/core.h> -#include <linux/mfd/wm8994/registers.h> #include "../codecs/wm8994.h" -#include "dma.h" -#include "i2s.h" #define MACHINE_NAME 0 #define CPU_VOICE_DAI 1 diff --git a/sound/soc/samsung/h1940_uda1380.c b/sound/soc/samsung/h1940_uda1380.c index c45f7ce14d61..241f55d00660 100644 --- a/sound/soc/samsung/h1940_uda1380.c +++ b/sound/soc/samsung/h1940_uda1380.c @@ -13,25 +13,16 @@ * */ -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/platform_device.h> -#include <linux/i2c.h> #include <linux/gpio.h> #include <sound/soc.h> -#include <sound/uda1380.h> #include <sound/jack.h> #include <plat/regs-iis.h> - #include <mach/h1940-latch.h> - #include <asm/mach-types.h> -#include "dma.h" #include "s3c24xx-i2s.h" -#include "../codecs/uda1380.h" static unsigned int rates[] = { 11025, diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index d00ac3a7102c..ffa09b3b2caa 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -15,9 +15,8 @@ #include <linux/clk.h> #include <linux/io.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/pcm_params.h> #include <plat/audio.h> diff --git a/sound/soc/samsung/jive_wm8750.c b/sound/soc/samsung/jive_wm8750.c index 08802520e014..3b53ad54bc33 100644 --- a/sound/soc/samsung/jive_wm8750.c +++ b/sound/soc/samsung/jive_wm8750.c @@ -11,22 +11,11 @@ * published by the Free Software Foundation. */ -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/timer.h> -#include <linux/interrupt.h> -#include <linux/platform_device.h> -#include <linux/clk.h> - -#include <sound/core.h> -#include <sound/pcm.h> #include <sound/soc.h> #include <asm/mach-types.h> -#include "dma.h" #include "s3c2412-i2s.h" - #include "../codecs/wm8750.h" static const struct snd_soc_dapm_route audio_map[] = { diff --git a/sound/soc/samsung/lm4857.h b/sound/soc/samsung/lm4857.h deleted file mode 100644 index 0cf5b7011d6f..000000000000 --- a/sound/soc/samsung/lm4857.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * lm4857.h -- ALSA Soc Audio Layer - * - * 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 as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * Revision history - * 18th Jun 2007 Initial version. - */ - -#ifndef LM4857_H_ -#define LM4857_H_ - -/* The register offsets in the cache array */ -#define LM4857_MVOL 0 -#define LM4857_LVOL 1 -#define LM4857_RVOL 2 -#define LM4857_CTRL 3 - -/* the shifts required to set these bits */ -#define LM4857_3D 5 -#define LM4857_WAKEUP 5 -#define LM4857_EPGAIN 4 - -#endif /*LM4857_H_*/ - diff --git a/sound/soc/samsung/ln2440sbc_alc650.c b/sound/soc/samsung/ln2440sbc_alc650.c index a2bb34def740..bd91c19a6c08 100644 --- a/sound/soc/samsung/ln2440sbc_alc650.c +++ b/sound/soc/samsung/ln2440sbc_alc650.c @@ -16,15 +16,8 @@ * */ -#include <linux/module.h> -#include <linux/device.h> -#include <sound/core.h> -#include <sound/pcm.h> #include <sound/soc.h> -#include "dma.h" -#include "ac97.h" - static struct snd_soc_card ln2440sbc; static struct snd_soc_dai_link ln2440sbc_dai[] = { diff --git a/sound/soc/samsung/neo1973_gta02_wm8753.c b/sound/soc/samsung/neo1973_gta02_wm8753.c deleted file mode 100644 index 0d0ae2b9eef6..000000000000 --- a/sound/soc/samsung/neo1973_gta02_wm8753.c +++ /dev/null @@ -1,504 +0,0 @@ -/* - * neo1973_gta02_wm8753.c -- SoC audio for Openmoko Freerunner(GTA02) - * - * Copyright 2007 Openmoko Inc - * Author: Graeme Gregory <graeme@openmoko.org> - * Copyright 2007 Wolfson Microelectronics PLC. - * Author: Graeme Gregory <linux@wolfsonmicro.com> - * Copyright 2009 Wolfson Microelectronics - * - * 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/module.h> -#include <linux/moduleparam.h> -#include <linux/timer.h> -#include <linux/interrupt.h> -#include <linux/platform_device.h> -#include <linux/gpio.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/soc.h> - -#include <asm/mach-types.h> - -#include <plat/regs-iis.h> - -#include <mach/regs-clock.h> -#include <asm/io.h> -#include <mach/gta02.h> -#include "../codecs/wm8753.h" -#include "dma.h" -#include "s3c24xx-i2s.h" - -static struct snd_soc_card neo1973_gta02; - -static int neo1973_gta02_hifi_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_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - unsigned int pll_out = 0, bclk = 0; - int ret = 0; - unsigned long iis_clkrate; - - iis_clkrate = s3c24xx_i2s_get_clockrate(); - - switch (params_rate(params)) { - case 8000: - case 16000: - pll_out = 12288000; - break; - case 48000: - bclk = WM8753_BCLK_DIV_4; - pll_out = 12288000; - break; - case 96000: - bclk = WM8753_BCLK_DIV_2; - pll_out = 12288000; - break; - case 11025: - bclk = WM8753_BCLK_DIV_16; - pll_out = 11289600; - break; - case 22050: - bclk = WM8753_BCLK_DIV_8; - pll_out = 11289600; - break; - case 44100: - bclk = WM8753_BCLK_DIV_4; - pll_out = 11289600; - break; - case 88200: - bclk = WM8753_BCLK_DIV_2; - pll_out = 11289600; - break; - } - - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, - SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - - /* set cpu DAI configuration */ - ret = snd_soc_dai_set_fmt(cpu_dai, - SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - - /* set the codec system clock for DAC and ADC */ - ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, pll_out, - SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - - /* set MCLK division for sample rate */ - ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, - S3C2410_IISMOD_32FS); - if (ret < 0) - return ret; - - /* set codec BCLK division for sample rate */ - ret = snd_soc_dai_set_clkdiv(codec_dai, - WM8753_BCLKDIV, bclk); - if (ret < 0) - return ret; - - /* set prescaler division for sample rate */ - ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER, - S3C24XX_PRESCALE(4, 4)); - if (ret < 0) - return ret; - - /* codec PLL input is PCLK/4 */ - ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, - iis_clkrate / 4, pll_out); - if (ret < 0) - return ret; - - return 0; -} - -static int neo1973_gta02_hifi_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - - /* disable the PLL */ - return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0); -} - -/* - * Neo1973 WM8753 HiFi DAI opserations. - */ -static struct snd_soc_ops neo1973_gta02_hifi_ops = { - .hw_params = neo1973_gta02_hifi_hw_params, - .hw_free = neo1973_gta02_hifi_hw_free, -}; - -static int neo1973_gta02_voice_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_dai *codec_dai = rtd->codec_dai; - unsigned int pcmdiv = 0; - int ret = 0; - unsigned long iis_clkrate; - - iis_clkrate = s3c24xx_i2s_get_clockrate(); - - if (params_rate(params) != 8000) - return -EINVAL; - if (params_channels(params) != 1) - return -EINVAL; - - pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */ - - /* todo: gg check mode (DSP_B) against CSR datasheet */ - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - - /* set the codec system clock for DAC and ADC */ - ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_PCMCLK, - 12288000, SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - - /* set codec PCM division for sample rate */ - ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_PCMDIV, - pcmdiv); - if (ret < 0) - return ret; - - /* configure and enable PLL for 12.288MHz output */ - ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, - iis_clkrate / 4, 12288000); - if (ret < 0) - return ret; - - return 0; -} - -static int neo1973_gta02_voice_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - - /* disable the PLL */ - return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0); -} - -static struct snd_soc_ops neo1973_gta02_voice_ops = { - .hw_params = neo1973_gta02_voice_hw_params, - .hw_free = neo1973_gta02_voice_hw_free, -}; - -#define LM4853_AMP 1 -#define LM4853_SPK 2 - -static u8 lm4853_state; - -/* This has no effect, it exists only to maintain compatibility with - * existing ALSA state files. - */ -static int lm4853_set_state(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - int val = ucontrol->value.integer.value[0]; - - if (val) - lm4853_state |= LM4853_AMP; - else - lm4853_state &= ~LM4853_AMP; - - return 0; -} - -static int lm4853_get_state(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.integer.value[0] = lm4853_state & LM4853_AMP; - - return 0; -} - -static int lm4853_set_spk(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - int val = ucontrol->value.integer.value[0]; - - if (val) { - lm4853_state |= LM4853_SPK; - gpio_set_value(GTA02_GPIO_HP_IN, 0); - } else { - lm4853_state &= ~LM4853_SPK; - gpio_set_value(GTA02_GPIO_HP_IN, 1); - } - - return 0; -} - -static int lm4853_get_spk(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.integer.value[0] = (lm4853_state & LM4853_SPK) >> 1; - - return 0; -} - -static int lm4853_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, - int event) -{ - gpio_set_value(GTA02_GPIO_AMP_SHUT, SND_SOC_DAPM_EVENT_OFF(event)); - - return 0; -} - -static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = { - SND_SOC_DAPM_SPK("Stereo Out", lm4853_event), - SND_SOC_DAPM_LINE("GSM Line Out", NULL), - SND_SOC_DAPM_LINE("GSM Line In", NULL), - SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_MIC("Handset Mic", NULL), - SND_SOC_DAPM_SPK("Handset Spk", NULL), -}; - - -/* example machine audio_mapnections */ -static const struct snd_soc_dapm_route audio_map[] = { - - /* Connections to the lm4853 amp */ - {"Stereo Out", NULL, "LOUT1"}, - {"Stereo Out", NULL, "ROUT1"}, - - /* Connections to the GSM Module */ - {"GSM Line Out", NULL, "MONO1"}, - {"GSM Line Out", NULL, "MONO2"}, - {"RXP", NULL, "GSM Line In"}, - {"RXN", NULL, "GSM Line In"}, - - /* Connections to Headset */ - {"MIC1", NULL, "Mic Bias"}, - {"Mic Bias", NULL, "Headset Mic"}, - - /* Call Mic */ - {"MIC2", NULL, "Mic Bias"}, - {"MIC2N", NULL, "Mic Bias"}, - {"Mic Bias", NULL, "Handset Mic"}, - - /* Call Speaker */ - {"Handset Spk", NULL, "LOUT2"}, - {"Handset Spk", NULL, "ROUT2"}, - - /* Connect the ALC pins */ - {"ACIN", NULL, "ACOP"}, -}; - -static const struct snd_kcontrol_new wm8753_neo1973_gta02_controls[] = { - SOC_DAPM_PIN_SWITCH("Stereo Out"), - SOC_DAPM_PIN_SWITCH("GSM Line Out"), - SOC_DAPM_PIN_SWITCH("GSM Line In"), - SOC_DAPM_PIN_SWITCH("Headset Mic"), - SOC_DAPM_PIN_SWITCH("Handset Mic"), - SOC_DAPM_PIN_SWITCH("Handset Spk"), - - /* This has no effect, it exists only to maintain compatibility with - * existing ALSA state files. - */ - SOC_SINGLE_EXT("Amp State Switch", 6, 0, 1, 0, - lm4853_get_state, - lm4853_set_state), - SOC_SINGLE_EXT("Amp Spk Switch", 7, 0, 1, 0, - lm4853_get_spk, - lm4853_set_spk), -}; - -/* - * This is an example machine initialisation for a wm8753 connected to a - * neo1973 GTA02. - */ -static int neo1973_gta02_wm8753_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; - int err; - - /* set up NC codec pins */ - snd_soc_dapm_nc_pin(dapm, "OUT3"); - snd_soc_dapm_nc_pin(dapm, "OUT4"); - snd_soc_dapm_nc_pin(dapm, "LINE1"); - snd_soc_dapm_nc_pin(dapm, "LINE2"); - - /* Add neo1973 gta02 specific widgets */ - snd_soc_dapm_new_controls(dapm, wm8753_dapm_widgets, - ARRAY_SIZE(wm8753_dapm_widgets)); - - /* add neo1973 gta02 specific controls */ - err = snd_soc_add_controls(codec, wm8753_neo1973_gta02_controls, - ARRAY_SIZE(wm8753_neo1973_gta02_controls)); - - if (err < 0) - return err; - - /* set up neo1973 gta02 specific audio path audio_map */ - snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - - /* set endpoints to default off mode */ - snd_soc_dapm_disable_pin(dapm, "Stereo Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line In"); - snd_soc_dapm_disable_pin(dapm, "Headset Mic"); - snd_soc_dapm_disable_pin(dapm, "Handset Mic"); - snd_soc_dapm_disable_pin(dapm, "Handset Spk"); - - /* allow audio paths from the GSM modem to run during suspend */ - snd_soc_dapm_ignore_suspend(dapm, "Stereo Out"); - snd_soc_dapm_ignore_suspend(dapm, "GSM Line Out"); - snd_soc_dapm_ignore_suspend(dapm, "GSM Line In"); - snd_soc_dapm_ignore_suspend(dapm, "Headset Mic"); - snd_soc_dapm_ignore_suspend(dapm, "Handset Mic"); - snd_soc_dapm_ignore_suspend(dapm, "Handset Spk"); - - snd_soc_dapm_sync(dapm); - - return 0; -} - -/* - * BT Codec DAI - */ -static struct snd_soc_dai_driver bt_dai = { - .name = "bluetooth-dai", - .playback = { - .channels_min = 1, - .channels_max = 1, - .rates = SNDRV_PCM_RATE_8000, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, - .capture = { - .channels_min = 1, - .channels_max = 1, - .rates = SNDRV_PCM_RATE_8000, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, -}; - -static struct snd_soc_dai_link neo1973_gta02_dai[] = { -{ /* Hifi Playback - for similatious use with voice below */ - .name = "WM8753", - .stream_name = "WM8753 HiFi", - .cpu_dai_name = "s3c24xx-iis", - .codec_dai_name = "wm8753-hifi", - .init = neo1973_gta02_wm8753_init, - .platform_name = "samsung-audio", - .codec_name = "wm8753-codec.0-001a", - .ops = &neo1973_gta02_hifi_ops, -}, -{ /* Voice via BT */ - .name = "Bluetooth", - .stream_name = "Voice", - .cpu_dai_name = "bluetooth-dai", - .codec_dai_name = "wm8753-voice", - .ops = &neo1973_gta02_voice_ops, - .codec_name = "wm8753-codec.0-001a", - .platform_name = "samsung-audio", -}, -}; - -static struct snd_soc_card neo1973_gta02 = { - .name = "neo1973-gta02", - .dai_link = neo1973_gta02_dai, - .num_links = ARRAY_SIZE(neo1973_gta02_dai), -}; - -static struct platform_device *neo1973_gta02_snd_device; - -static int __init neo1973_gta02_init(void) -{ - int ret; - - if (!machine_is_neo1973_gta02()) { - printk(KERN_INFO - "Only GTA02 is supported by this ASoC driver\n"); - return -ENODEV; - } - - neo1973_gta02_snd_device = platform_device_alloc("soc-audio", -1); - if (!neo1973_gta02_snd_device) - return -ENOMEM; - - /* register bluetooth DAI here */ - ret = snd_soc_register_dai(&neo1973_gta02_snd_device->dev, &bt_dai); - if (ret) - goto err_put_device; - - platform_set_drvdata(neo1973_gta02_snd_device, &neo1973_gta02); - ret = platform_device_add(neo1973_gta02_snd_device); - - if (ret) - goto err_unregister_dai; - - /* Initialise GPIOs used by amp */ - ret = gpio_request(GTA02_GPIO_HP_IN, "GTA02_HP_IN"); - if (ret) { - pr_err("gta02_wm8753: Failed to register GPIO %d\n", GTA02_GPIO_HP_IN); - goto err_del_device; - } - - ret = gpio_direction_output(GTA02_GPIO_HP_IN, 1); - if (ret) { - pr_err("gta02_wm8753: Failed to configure GPIO %d\n", GTA02_GPIO_HP_IN); - goto err_free_gpio_hp_in; - } - - ret = gpio_request(GTA02_GPIO_AMP_SHUT, "GTA02_AMP_SHUT"); - if (ret) { - pr_err("gta02_wm8753: Failed to register GPIO %d\n", GTA02_GPIO_AMP_SHUT); - goto err_free_gpio_hp_in; - } - - ret = gpio_direction_output(GTA02_GPIO_AMP_SHUT, 1); - if (ret) { - pr_err("gta02_wm8753: Failed to configure GPIO %d\n", GTA02_GPIO_AMP_SHUT); - goto err_free_gpio_amp_shut; - } - - return 0; - -err_free_gpio_amp_shut: - gpio_free(GTA02_GPIO_AMP_SHUT); -err_free_gpio_hp_in: - gpio_free(GTA02_GPIO_HP_IN); -err_del_device: - platform_device_del(neo1973_gta02_snd_device); -err_unregister_dai: - snd_soc_unregister_dai(&neo1973_gta02_snd_device->dev); -err_put_device: - platform_device_put(neo1973_gta02_snd_device); - return ret; -} -module_init(neo1973_gta02_init); - -static void __exit neo1973_gta02_exit(void) -{ - snd_soc_unregister_dai(&neo1973_gta02_snd_device->dev); - platform_device_unregister(neo1973_gta02_snd_device); - gpio_free(GTA02_GPIO_HP_IN); - gpio_free(GTA02_GPIO_AMP_SHUT); -} -module_exit(neo1973_gta02_exit); - -/* Module information */ -MODULE_AUTHOR("Graeme Gregory, graeme@openmoko.org"); -MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973 GTA02"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/samsung/neo1973_wm8753.c b/sound/soc/samsung/neo1973_wm8753.c index d20815d5ab2e..78bfdb3f5d7e 100644 --- a/sound/soc/samsung/neo1973_wm8753.c +++ b/sound/soc/samsung/neo1973_wm8753.c @@ -1,57 +1,32 @@ /* - * neo1973_wm8753.c -- SoC audio for Neo1973 + * neo1973_wm8753.c -- SoC audio for Openmoko Neo1973 and Freerunner devices * + * Copyright 2007 Openmoko Inc + * Author: Graeme Gregory <graeme@openmoko.org> * Copyright 2007 Wolfson Microelectronics PLC. * Author: Graeme Gregory * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com + * Copyright 2009 Wolfson Microelectronics * * 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/module.h> -#include <linux/moduleparam.h> -#include <linux/timer.h> -#include <linux/interrupt.h> #include <linux/platform_device.h> -#include <linux/i2c.h> -#include <sound/core.h> -#include <sound/pcm.h> +#include <linux/gpio.h> + #include <sound/soc.h> -#include <sound/tlv.h> #include <asm/mach-types.h> -#include <asm/hardware/scoop.h> -#include <mach/regs-clock.h> -#include <mach/regs-gpio.h> -#include <mach/hardware.h> -#include <linux/io.h> -#include <mach/spi-gpio.h> - #include <plat/regs-iis.h> +#include <mach/gta02.h> #include "../codecs/wm8753.h" -#include "lm4857.h" -#include "dma.h" #include "s3c24xx-i2s.h" -/* define the scenarios */ -#define NEO_AUDIO_OFF 0 -#define NEO_GSM_CALL_AUDIO_HANDSET 1 -#define NEO_GSM_CALL_AUDIO_HEADSET 2 -#define NEO_GSM_CALL_AUDIO_BLUETOOTH 3 -#define NEO_STEREO_TO_SPEAKERS 4 -#define NEO_STEREO_TO_HEADPHONES 5 -#define NEO_CAPTURE_HANDSET 6 -#define NEO_CAPTURE_HEADSET 7 -#define NEO_CAPTURE_BLUETOOTH 8 - -static struct snd_soc_card neo1973; -static struct i2c_client *i2c; - static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -62,8 +37,6 @@ static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream, int ret = 0; unsigned long iis_clkrate; - pr_debug("Entered %s\n", __func__); - iis_clkrate = s3c24xx_i2s_get_clockrate(); switch (params_rate(params)) { @@ -148,8 +121,6 @@ static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->codec_dai; - pr_debug("Entered %s\n", __func__); - /* disable the PLL */ return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0); } @@ -171,8 +142,6 @@ static int neo1973_voice_hw_params(struct snd_pcm_substream *substream, int ret = 0; unsigned long iis_clkrate; - pr_debug("Entered %s\n", __func__); - iis_clkrate = s3c24xx_i2s_get_clockrate(); if (params_rate(params) != 8000) @@ -214,8 +183,6 @@ static int neo1973_voice_hw_free(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->codec_dai; - pr_debug("Entered %s\n", __func__); - /* disable the PLL */ return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0); } @@ -225,335 +192,232 @@ static struct snd_soc_ops neo1973_voice_ops = { .hw_free = neo1973_voice_hw_free, }; -static int neo1973_scenario; - -static int neo1973_get_scenario(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.integer.value[0] = neo1973_scenario; - return 0; -} - -static int set_scenario_endpoints(struct snd_soc_codec *codec, int scenario) -{ - struct snd_soc_dapm_context *dapm = &codec->dapm; - - pr_debug("Entered %s\n", __func__); - - switch (neo1973_scenario) { - case NEO_AUDIO_OFF: - snd_soc_dapm_disable_pin(dapm, "Audio Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line In"); - snd_soc_dapm_disable_pin(dapm, "Headset Mic"); - snd_soc_dapm_disable_pin(dapm, "Call Mic"); - break; - case NEO_GSM_CALL_AUDIO_HANDSET: - snd_soc_dapm_enable_pin(dapm, "Audio Out"); - snd_soc_dapm_enable_pin(dapm, "GSM Line Out"); - snd_soc_dapm_enable_pin(dapm, "GSM Line In"); - snd_soc_dapm_disable_pin(dapm, "Headset Mic"); - snd_soc_dapm_enable_pin(dapm, "Call Mic"); - break; - case NEO_GSM_CALL_AUDIO_HEADSET: - snd_soc_dapm_enable_pin(dapm, "Audio Out"); - snd_soc_dapm_enable_pin(dapm, "GSM Line Out"); - snd_soc_dapm_enable_pin(dapm, "GSM Line In"); - snd_soc_dapm_enable_pin(dapm, "Headset Mic"); - snd_soc_dapm_disable_pin(dapm, "Call Mic"); - break; - case NEO_GSM_CALL_AUDIO_BLUETOOTH: - snd_soc_dapm_disable_pin(dapm, "Audio Out"); - snd_soc_dapm_enable_pin(dapm, "GSM Line Out"); - snd_soc_dapm_enable_pin(dapm, "GSM Line In"); - snd_soc_dapm_disable_pin(dapm, "Headset Mic"); - snd_soc_dapm_disable_pin(dapm, "Call Mic"); - break; - case NEO_STEREO_TO_SPEAKERS: - snd_soc_dapm_enable_pin(dapm, "Audio Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line In"); - snd_soc_dapm_disable_pin(dapm, "Headset Mic"); - snd_soc_dapm_disable_pin(dapm, "Call Mic"); - break; - case NEO_STEREO_TO_HEADPHONES: - snd_soc_dapm_enable_pin(dapm, "Audio Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line In"); - snd_soc_dapm_disable_pin(dapm, "Headset Mic"); - snd_soc_dapm_disable_pin(dapm, "Call Mic"); - break; - case NEO_CAPTURE_HANDSET: - snd_soc_dapm_disable_pin(dapm, "Audio Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line In"); - snd_soc_dapm_disable_pin(dapm, "Headset Mic"); - snd_soc_dapm_enable_pin(dapm, "Call Mic"); - break; - case NEO_CAPTURE_HEADSET: - snd_soc_dapm_disable_pin(dapm, "Audio Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line In"); - snd_soc_dapm_enable_pin(dapm, "Headset Mic"); - snd_soc_dapm_disable_pin(dapm, "Call Mic"); - break; - case NEO_CAPTURE_BLUETOOTH: - snd_soc_dapm_disable_pin(dapm, "Audio Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line In"); - snd_soc_dapm_disable_pin(dapm, "Headset Mic"); - snd_soc_dapm_disable_pin(dapm, "Call Mic"); - break; - default: - snd_soc_dapm_disable_pin(dapm, "Audio Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line In"); - snd_soc_dapm_disable_pin(dapm, "Headset Mic"); - snd_soc_dapm_disable_pin(dapm, "Call Mic"); - } +/* Shared routes and controls */ - snd_soc_dapm_sync(dapm); +static const struct snd_soc_dapm_widget neo1973_wm8753_dapm_widgets[] = { + SND_SOC_DAPM_LINE("GSM Line Out", NULL), + SND_SOC_DAPM_LINE("GSM Line In", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Handset Mic", NULL), +}; - return 0; -} +static const struct snd_soc_dapm_route neo1973_wm8753_routes[] = { + /* Connections to the GSM Module */ + {"GSM Line Out", NULL, "MONO1"}, + {"GSM Line Out", NULL, "MONO2"}, + {"RXP", NULL, "GSM Line In"}, + {"RXN", NULL, "GSM Line In"}, -static int neo1973_set_scenario(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + /* Connections to Headset */ + {"MIC1", NULL, "Mic Bias"}, + {"Mic Bias", NULL, "Headset Mic"}, - pr_debug("Entered %s\n", __func__); + /* Call Mic */ + {"MIC2", NULL, "Mic Bias"}, + {"MIC2N", NULL, "Mic Bias"}, + {"Mic Bias", NULL, "Handset Mic"}, - if (neo1973_scenario == ucontrol->value.integer.value[0]) - return 0; + /* Connect the ALC pins */ + {"ACIN", NULL, "ACOP"}, +}; - neo1973_scenario = ucontrol->value.integer.value[0]; - set_scenario_endpoints(codec, neo1973_scenario); - return 1; -} +static const struct snd_kcontrol_new neo1973_wm8753_controls[] = { + SOC_DAPM_PIN_SWITCH("GSM Line Out"), + SOC_DAPM_PIN_SWITCH("GSM Line In"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Handset Mic"), +}; -static u8 lm4857_regs[4] = {0x00, 0x40, 0x80, 0xC0}; +/* GTA02 specific routes and controlls */ -static void lm4857_write_regs(void) -{ - pr_debug("Entered %s\n", __func__); +#ifdef CONFIG_MACH_NEO1973_GTA02 - if (i2c_master_send(i2c, lm4857_regs, 4) != 4) - printk(KERN_ERR "lm4857: i2c write failed\n"); -} +static int gta02_speaker_enabled; -static int lm4857_get_reg(struct snd_kcontrol *kcontrol, +static int lm4853_set_spk(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int reg = mc->reg; - int shift = mc->shift; - int mask = mc->max; + gta02_speaker_enabled = ucontrol->value.integer.value[0]; - pr_debug("Entered %s\n", __func__); + gpio_set_value(GTA02_GPIO_HP_IN, !gta02_speaker_enabled); - ucontrol->value.integer.value[0] = (lm4857_regs[reg] >> shift) & mask; return 0; } -static int lm4857_set_reg(struct snd_kcontrol *kcontrol, +static int lm4853_get_spk(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int reg = mc->reg; - int shift = mc->shift; - int mask = mc->max; - - if (((lm4857_regs[reg] >> shift) & mask) == - ucontrol->value.integer.value[0]) - return 0; - - lm4857_regs[reg] &= ~(mask << shift); - lm4857_regs[reg] |= ucontrol->value.integer.value[0] << shift; - lm4857_write_regs(); - return 1; + ucontrol->value.integer.value[0] = gta02_speaker_enabled; + return 0; } -static int lm4857_get_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int lm4853_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) { - u8 value = lm4857_regs[LM4857_CTRL] & 0x0F; - - pr_debug("Entered %s\n", __func__); - - if (value) - value -= 5; + gpio_set_value(GTA02_GPIO_AMP_SHUT, SND_SOC_DAPM_EVENT_OFF(event)); - ucontrol->value.integer.value[0] = value; return 0; } -static int lm4857_set_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - u8 value = ucontrol->value.integer.value[0]; - - pr_debug("Entered %s\n", __func__); - - if (value) - value += 5; - - if ((lm4857_regs[LM4857_CTRL] & 0x0F) == value) - return 0; - - lm4857_regs[LM4857_CTRL] &= 0xF0; - lm4857_regs[LM4857_CTRL] |= value; - lm4857_write_regs(); - return 1; -} +static const struct snd_soc_dapm_route neo1973_gta02_routes[] = { + /* Connections to the amp */ + {"Stereo Out", NULL, "LOUT1"}, + {"Stereo Out", NULL, "ROUT1"}, -static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = { - SND_SOC_DAPM_LINE("Audio Out", NULL), - SND_SOC_DAPM_LINE("GSM Line Out", NULL), - SND_SOC_DAPM_LINE("GSM Line In", NULL), - SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_MIC("Call Mic", NULL), + /* Call Speaker */ + {"Handset Spk", NULL, "LOUT2"}, + {"Handset Spk", NULL, "ROUT2"}, }; +static const struct snd_kcontrol_new neo1973_gta02_wm8753_controls[] = { + SOC_DAPM_PIN_SWITCH("Handset Spk"), + SOC_DAPM_PIN_SWITCH("Stereo Out"), -static const struct snd_soc_dapm_route dapm_routes[] = { - - /* Connections to the lm4857 amp */ - {"Audio Out", NULL, "LOUT1"}, - {"Audio Out", NULL, "ROUT1"}, - - /* Connections to the GSM Module */ - {"GSM Line Out", NULL, "MONO1"}, - {"GSM Line Out", NULL, "MONO2"}, - {"RXP", NULL, "GSM Line In"}, - {"RXN", NULL, "GSM Line In"}, + SOC_SINGLE_BOOL_EXT("Amp Spk Switch", 0, + lm4853_get_spk, + lm4853_set_spk), +}; - /* Connections to Headset */ - {"MIC1", NULL, "Mic Bias"}, - {"Mic Bias", NULL, "Headset Mic"}, +static const struct snd_soc_dapm_widget neo1973_gta02_wm8753_dapm_widgets[] = { + SND_SOC_DAPM_SPK("Handset Spk", NULL), + SND_SOC_DAPM_SPK("Stereo Out", lm4853_event), +}; - /* Call Mic */ - {"MIC2", NULL, "Mic Bias"}, - {"MIC2N", NULL, "Mic Bias"}, - {"Mic Bias", NULL, "Call Mic"}, +static int neo1973_gta02_wm8753_init(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = &codec->dapm; + int ret; - /* Connect the ALC pins */ - {"ACIN", NULL, "ACOP"}, -}; + ret = snd_soc_dapm_new_controls(dapm, neo1973_gta02_wm8753_dapm_widgets, + ARRAY_SIZE(neo1973_gta02_wm8753_dapm_widgets)); + if (ret) + return ret; -static const char *lm4857_mode[] = { - "Off", - "Call Speaker", - "Stereo Speakers", - "Stereo Speakers + Headphones", - "Headphones" -}; + ret = snd_soc_dapm_add_routes(dapm, neo1973_gta02_routes, + ARRAY_SIZE(neo1973_gta02_routes)); + if (ret) + return ret; -static const struct soc_enum lm4857_mode_enum[] = { - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lm4857_mode), lm4857_mode), -}; + ret = snd_soc_add_controls(codec, neo1973_gta02_wm8753_controls, + ARRAY_SIZE(neo1973_gta02_wm8753_controls)); + if (ret) + return ret; -static const char *neo_scenarios[] = { - "Off", - "GSM Handset", - "GSM Headset", - "GSM Bluetooth", - "Speakers", - "Headphones", - "Capture Handset", - "Capture Headset", - "Capture Bluetooth" -}; + snd_soc_dapm_disable_pin(dapm, "Stereo Out"); + snd_soc_dapm_disable_pin(dapm, "Handset Spk"); + snd_soc_dapm_ignore_suspend(dapm, "Stereo Out"); + snd_soc_dapm_ignore_suspend(dapm, "Handset Spk"); -static const struct soc_enum neo_scenario_enum[] = { - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(neo_scenarios), neo_scenarios), -}; + return 0; +} -static const DECLARE_TLV_DB_SCALE(stereo_tlv, -4050, 150, 0); -static const DECLARE_TLV_DB_SCALE(mono_tlv, -3450, 150, 0); - -static const struct snd_kcontrol_new wm8753_neo1973_controls[] = { - SOC_SINGLE_EXT_TLV("Amp Left Playback Volume", LM4857_LVOL, 0, 31, 0, - lm4857_get_reg, lm4857_set_reg, stereo_tlv), - SOC_SINGLE_EXT_TLV("Amp Right Playback Volume", LM4857_RVOL, 0, 31, 0, - lm4857_get_reg, lm4857_set_reg, stereo_tlv), - SOC_SINGLE_EXT_TLV("Amp Mono Playback Volume", LM4857_MVOL, 0, 31, 0, - lm4857_get_reg, lm4857_set_reg, mono_tlv), - SOC_ENUM_EXT("Amp Mode", lm4857_mode_enum[0], - lm4857_get_mode, lm4857_set_mode), - SOC_ENUM_EXT("Neo Mode", neo_scenario_enum[0], - neo1973_get_scenario, neo1973_set_scenario), - SOC_SINGLE_EXT("Amp Spk 3D Playback Switch", LM4857_LVOL, 5, 1, 0, - lm4857_get_reg, lm4857_set_reg), - SOC_SINGLE_EXT("Amp HP 3d Playback Switch", LM4857_RVOL, 5, 1, 0, - lm4857_get_reg, lm4857_set_reg), - SOC_SINGLE_EXT("Amp Fast Wakeup Playback Switch", LM4857_CTRL, 5, 1, 0, - lm4857_get_reg, lm4857_set_reg), - SOC_SINGLE_EXT("Amp Earpiece 6dB Playback Switch", LM4857_CTRL, 4, 1, 0, - lm4857_get_reg, lm4857_set_reg), -}; +#else +static int neo1973_gta02_wm8753_init(struct snd_soc_code *codec) { return 0; } +#endif -/* - * This is an example machine initialisation for a wm8753 connected to a - * neo1973 II. It is missing logic to detect hp/mic insertions and logic - * to re-route the audio in such an event. - */ static int neo1973_wm8753_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; struct snd_soc_dapm_context *dapm = &codec->dapm; - int err; - - pr_debug("Entered %s\n", __func__); + int ret; /* set up NC codec pins */ - snd_soc_dapm_nc_pin(dapm, "LOUT2"); - snd_soc_dapm_nc_pin(dapm, "ROUT2"); + if (machine_is_neo1973_gta01()) { + snd_soc_dapm_nc_pin(dapm, "LOUT2"); + snd_soc_dapm_nc_pin(dapm, "ROUT2"); + } snd_soc_dapm_nc_pin(dapm, "OUT3"); snd_soc_dapm_nc_pin(dapm, "OUT4"); snd_soc_dapm_nc_pin(dapm, "LINE1"); snd_soc_dapm_nc_pin(dapm, "LINE2"); /* Add neo1973 specific widgets */ - snd_soc_dapm_new_controls(dapm, wm8753_dapm_widgets, - ARRAY_SIZE(wm8753_dapm_widgets)); - - /* set endpoints to default mode */ - set_scenario_endpoints(codec, NEO_AUDIO_OFF); + ret = snd_soc_dapm_new_controls(dapm, neo1973_wm8753_dapm_widgets, + ARRAY_SIZE(neo1973_wm8753_dapm_widgets)); + if (ret) + return ret; /* add neo1973 specific controls */ - err = snd_soc_add_controls(codec, wm8753_neo1973_controls, - ARRAY_SIZE(8753_neo1973_controls)); - if (err < 0) - return err; + ret = snd_soc_add_controls(codec, neo1973_wm8753_controls, + ARRAY_SIZE(neo1973_wm8753_controls)); + if (ret) + return ret; /* set up neo1973 specific audio routes */ - err = snd_soc_dapm_add_routes(dapm, dapm_routes, - ARRAY_SIZE(dapm_routes)); + ret = snd_soc_dapm_add_routes(dapm, neo1973_wm8753_routes, + ARRAY_SIZE(neo1973_wm8753_routes)); + if (ret) + return ret; + + /* set endpoints to default off mode */ + snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); + snd_soc_dapm_disable_pin(dapm, "GSM Line In"); + snd_soc_dapm_disable_pin(dapm, "Headset Mic"); + snd_soc_dapm_disable_pin(dapm, "Handset Mic"); + + /* allow audio paths from the GSM modem to run during suspend */ + snd_soc_dapm_ignore_suspend(dapm, "GSM Line Out"); + snd_soc_dapm_ignore_suspend(dapm, "GSM Line In"); + snd_soc_dapm_ignore_suspend(dapm, "Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Handset Mic"); + + if (machine_is_neo1973_gta02()) { + ret = neo1973_gta02_wm8753_init(codec); + if (ret) + return ret; + } snd_soc_dapm_sync(dapm); + return 0; } -/* - * BT Codec DAI - */ -static struct snd_soc_dai bt_dai = { - .name = "bluetooth-dai", - .playback = { - .channels_min = 1, - .channels_max = 1, - .rates = SNDRV_PCM_RATE_8000, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, - .capture = { - .channels_min = 1, - .channels_max = 1, - .rates = SNDRV_PCM_RATE_8000, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, +/* GTA01 specific controlls */ + +#ifdef CONFIG_MACH_NEO1973_GTA01 + +static const struct snd_soc_dapm_route neo1973_lm4857_routes[] = { + {"Amp IN", NULL, "ROUT1"}, + {"Amp IN", NULL, "LOUT1"}, + + {"Handset Spk", NULL, "Amp EP"}, + {"Stereo Out", NULL, "Amp LS"}, + {"Headphone", NULL, "Amp HP"}, +}; + +static const struct snd_soc_dapm_widget neo1973_lm4857_dapm_widgets[] = { + SND_SOC_DAPM_SPK("Handset Spk", NULL), + SND_SOC_DAPM_SPK("Stereo Out", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), }; +static int neo1973_lm4857_init(struct snd_soc_dapm_context *dapm) +{ + int ret; + + ret = snd_soc_dapm_new_controls(dapm, neo1973_lm4857_dapm_widgets, + ARRAY_SIZE(neo1973_lm4857_dapm_widgets)); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, neo1973_lm4857_routes, + ARRAY_SIZE(neo1973_lm4857_routes)); + if (ret) + return ret; + + snd_soc_dapm_ignore_suspend(dapm, "Stereo Out"); + snd_soc_dapm_ignore_suspend(dapm, "Handset Spk"); + snd_soc_dapm_ignore_suspend(dapm, "Headphone"); + + snd_soc_dapm_sync(dapm); + + return 0; +} + +#else +static int neo1973_lm4857_init(struct snd_soc_dapm_context *dapm) { return 0; }; +#endif + static struct snd_soc_dai_link neo1973_dai[] = { { /* Hifi Playback - for similatious use with voice below */ .name = "WM8753", @@ -569,90 +433,49 @@ static struct snd_soc_dai_link neo1973_dai[] = { .name = "Bluetooth", .stream_name = "Voice", .platform_name = "samsung-audio", - .cpu_dai_name = "bluetooth-dai", + .cpu_dai_name = "dfbmcs320-pcm", .codec_dai_name = "wm8753-voice", .codec_name = "wm8753-codec.0-001a", .ops = &neo1973_voice_ops, }, }; -static struct snd_soc_card neo1973 = { - .name = "neo1973", - .dai_link = neo1973_dai, - .num_links = ARRAY_SIZE(neo1973_dai), +static struct snd_soc_aux_dev neo1973_aux_devs[] = { + { + .name = "dfbmcs320", + .codec_name = "dfbmcs320.0", + }, + { + .name = "lm4857", + .codec_name = "lm4857.0-007c", + .init = neo1973_lm4857_init, + }, }; -static int lm4857_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - pr_debug("Entered %s\n", __func__); - - i2c = client; - - lm4857_write_regs(); - return 0; -} - -static int lm4857_i2c_remove(struct i2c_client *client) -{ - pr_debug("Entered %s\n", __func__); - - i2c = NULL; - - return 0; -} - -static u8 lm4857_state; - -static int lm4857_suspend(struct i2c_client *dev, pm_message_t state) -{ - pr_debug("Entered %s\n", __func__); - - dev_dbg(&dev->dev, "lm4857_suspend\n"); - lm4857_state = lm4857_regs[LM4857_CTRL] & 0xf; - if (lm4857_state) { - lm4857_regs[LM4857_CTRL] &= 0xf0; - lm4857_write_regs(); - } - return 0; -} - -static int lm4857_resume(struct i2c_client *dev) -{ - pr_debug("Entered %s\n", __func__); - - if (lm4857_state) { - lm4857_regs[LM4857_CTRL] |= (lm4857_state & 0x0f); - lm4857_write_regs(); - } - return 0; -} - -static void lm4857_shutdown(struct i2c_client *dev) -{ - pr_debug("Entered %s\n", __func__); - - dev_dbg(&dev->dev, "lm4857_shutdown\n"); - lm4857_regs[LM4857_CTRL] &= 0xf0; - lm4857_write_regs(); -} +static struct snd_soc_codec_conf neo1973_codec_conf[] = { + { + .dev_name = "lm4857.0-007c", + .name_prefix = "Amp", + }, +}; -static const struct i2c_device_id lm4857_i2c_id[] = { - { "neo1973_lm4857", 0 }, - { } +#ifdef CONFIG_MACH_NEO1973_GTA02 +static const struct gpio neo1973_gta02_gpios[] = { + { GTA02_GPIO_HP_IN, GPIOF_OUT_INIT_HIGH, "GTA02_HP_IN" }, + { GTA02_GPIO_AMP_SHUT, GPIOF_OUT_INIT_HIGH, "GTA02_AMP_SHUT" }, }; +#else +static const struct gpio neo1973_gta02_gpios[] = {}; +#endif -static struct i2c_driver lm4857_i2c_driver = { - .driver = { - .name = "LM4857 I2C Amp", - .owner = THIS_MODULE, - }, - .suspend = lm4857_suspend, - .resume = lm4857_resume, - .shutdown = lm4857_shutdown, - .probe = lm4857_i2c_probe, - .remove = lm4857_i2c_remove, - .id_table = lm4857_i2c_id, +static struct snd_soc_card neo1973 = { + .name = "neo1973", + .dai_link = neo1973_dai, + .num_links = ARRAY_SIZE(neo1973_dai), + .aux_dev = neo1973_aux_devs, + .num_aux_devs = ARRAY_SIZE(neo1973_aux_devs), + .codec_conf = neo1973_codec_conf, + .num_configs = ARRAY_SIZE(neo1973_codec_conf), }; static struct platform_device *neo1973_snd_device; @@ -661,46 +484,56 @@ static int __init neo1973_init(void) { int ret; - pr_debug("Entered %s\n", __func__); - - if (!machine_is_neo1973_gta01()) { - printk(KERN_INFO - "Only GTA01 hardware supported by ASoC driver\n"); + if (!machine_is_neo1973_gta01() && !machine_is_neo1973_gta02()) return -ENODEV; + + if (machine_is_neo1973_gta02()) { + neo1973.name = "neo1973gta02"; + neo1973.num_aux_devs = 1; + + ret = gpio_request_array(neo1973_gta02_gpios, + ARRAY_SIZE(neo1973_gta02_gpios)); + if (ret) + return ret; } neo1973_snd_device = platform_device_alloc("soc-audio", -1); - if (!neo1973_snd_device) - return -ENOMEM; + if (!neo1973_snd_device) { + ret = -ENOMEM; + goto err_gpio_free; + } platform_set_drvdata(neo1973_snd_device, &neo1973); ret = platform_device_add(neo1973_snd_device); - if (ret) { - platform_device_put(neo1973_snd_device); - return ret; - } - - ret = i2c_add_driver(&lm4857_i2c_driver); + if (ret) + goto err_put_device; - if (ret != 0) - platform_device_unregister(neo1973_snd_device); + return 0; +err_put_device: + platform_device_put(neo1973_snd_device); +err_gpio_free: + if (machine_is_neo1973_gta02()) { + gpio_free_array(neo1973_gta02_gpios, + ARRAY_SIZE(neo1973_gta02_gpios)); + } return ret; } +module_init(neo1973_init); static void __exit neo1973_exit(void) { - pr_debug("Entered %s\n", __func__); - - i2c_del_driver(&lm4857_i2c_driver); platform_device_unregister(neo1973_snd_device); -} -module_init(neo1973_init); + if (machine_is_neo1973_gta02()) { + gpio_free_array(neo1973_gta02_gpios, + ARRAY_SIZE(neo1973_gta02_gpios)); + } +} module_exit(neo1973_exit); /* Module information */ MODULE_AUTHOR("Graeme Gregory, graeme@openmoko.org, www.openmoko.org"); -MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973"); +MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973 and Frerunner"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/samsung/pcm.c b/sound/soc/samsung/pcm.c index 48d0b750406b..38aac7d57a59 100644 --- a/sound/soc/samsung/pcm.c +++ b/sound/soc/samsung/pcm.c @@ -11,20 +11,11 @@ * published by the Free Software Foundation. */ -#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 <linux/gpio.h> #include <linux/io.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/initval.h> #include <sound/soc.h> +#include <sound/pcm_params.h> #include <plat/audio.h> #include <plat/dma.h> @@ -32,6 +23,113 @@ #include "dma.h" #include "pcm.h" +/*Register Offsets */ +#define S3C_PCM_CTL 0x00 +#define S3C_PCM_CLKCTL 0x04 +#define S3C_PCM_TXFIFO 0x08 +#define S3C_PCM_RXFIFO 0x0C +#define S3C_PCM_IRQCTL 0x10 +#define S3C_PCM_IRQSTAT 0x14 +#define S3C_PCM_FIFOSTAT 0x18 +#define S3C_PCM_CLRINT 0x20 + +/* PCM_CTL Bit-Fields */ +#define S3C_PCM_CTL_TXDIPSTICK_MASK 0x3f +#define S3C_PCM_CTL_TXDIPSTICK_SHIFT 13 +#define S3C_PCM_CTL_RXDIPSTICK_MASK 0x3f +#define S3C_PCM_CTL_RXDIPSTICK_SHIFT 7 +#define S3C_PCM_CTL_TXDMA_EN (0x1 << 6) +#define S3C_PCM_CTL_RXDMA_EN (0x1 << 5) +#define S3C_PCM_CTL_TXMSB_AFTER_FSYNC (0x1 << 4) +#define S3C_PCM_CTL_RXMSB_AFTER_FSYNC (0x1 << 3) +#define S3C_PCM_CTL_TXFIFO_EN (0x1 << 2) +#define S3C_PCM_CTL_RXFIFO_EN (0x1 << 1) +#define S3C_PCM_CTL_ENABLE (0x1 << 0) + +/* PCM_CLKCTL Bit-Fields */ +#define S3C_PCM_CLKCTL_SERCLK_EN (0x1 << 19) +#define S3C_PCM_CLKCTL_SERCLKSEL_PCLK (0x1 << 18) +#define S3C_PCM_CLKCTL_SCLKDIV_MASK 0x1ff +#define S3C_PCM_CLKCTL_SYNCDIV_MASK 0x1ff +#define S3C_PCM_CLKCTL_SCLKDIV_SHIFT 9 +#define S3C_PCM_CLKCTL_SYNCDIV_SHIFT 0 + +/* PCM_TXFIFO Bit-Fields */ +#define S3C_PCM_TXFIFO_DVALID (0x1 << 16) +#define S3C_PCM_TXFIFO_DATA_MSK (0xffff << 0) + +/* PCM_RXFIFO Bit-Fields */ +#define S3C_PCM_RXFIFO_DVALID (0x1 << 16) +#define S3C_PCM_RXFIFO_DATA_MSK (0xffff << 0) + +/* PCM_IRQCTL Bit-Fields */ +#define S3C_PCM_IRQCTL_IRQEN (0x1 << 14) +#define S3C_PCM_IRQCTL_WRDEN (0x1 << 12) +#define S3C_PCM_IRQCTL_TXEMPTYEN (0x1 << 11) +#define S3C_PCM_IRQCTL_TXALMSTEMPTYEN (0x1 << 10) +#define S3C_PCM_IRQCTL_TXFULLEN (0x1 << 9) +#define S3C_PCM_IRQCTL_TXALMSTFULLEN (0x1 << 8) +#define S3C_PCM_IRQCTL_TXSTARVEN (0x1 << 7) +#define S3C_PCM_IRQCTL_TXERROVRFLEN (0x1 << 6) +#define S3C_PCM_IRQCTL_RXEMPTEN (0x1 << 5) +#define S3C_PCM_IRQCTL_RXALMSTEMPTEN (0x1 << 4) +#define S3C_PCM_IRQCTL_RXFULLEN (0x1 << 3) +#define S3C_PCM_IRQCTL_RXALMSTFULLEN (0x1 << 2) +#define S3C_PCM_IRQCTL_RXSTARVEN (0x1 << 1) +#define S3C_PCM_IRQCTL_RXERROVRFLEN (0x1 << 0) + +/* PCM_IRQSTAT Bit-Fields */ +#define S3C_PCM_IRQSTAT_IRQPND (0x1 << 13) +#define S3C_PCM_IRQSTAT_WRD_XFER (0x1 << 12) +#define S3C_PCM_IRQSTAT_TXEMPTY (0x1 << 11) +#define S3C_PCM_IRQSTAT_TXALMSTEMPTY (0x1 << 10) +#define S3C_PCM_IRQSTAT_TXFULL (0x1 << 9) +#define S3C_PCM_IRQSTAT_TXALMSTFULL (0x1 << 8) +#define S3C_PCM_IRQSTAT_TXSTARV (0x1 << 7) +#define S3C_PCM_IRQSTAT_TXERROVRFL (0x1 << 6) +#define S3C_PCM_IRQSTAT_RXEMPT (0x1 << 5) +#define S3C_PCM_IRQSTAT_RXALMSTEMPT (0x1 << 4) +#define S3C_PCM_IRQSTAT_RXFULL (0x1 << 3) +#define S3C_PCM_IRQSTAT_RXALMSTFULL (0x1 << 2) +#define S3C_PCM_IRQSTAT_RXSTARV (0x1 << 1) +#define S3C_PCM_IRQSTAT_RXERROVRFL (0x1 << 0) + +/* PCM_FIFOSTAT Bit-Fields */ +#define S3C_PCM_FIFOSTAT_TXCNT_MSK (0x3f << 14) +#define S3C_PCM_FIFOSTAT_TXFIFOEMPTY (0x1 << 13) +#define S3C_PCM_FIFOSTAT_TXFIFOALMSTEMPTY (0x1 << 12) +#define S3C_PCM_FIFOSTAT_TXFIFOFULL (0x1 << 11) +#define S3C_PCM_FIFOSTAT_TXFIFOALMSTFULL (0x1 << 10) +#define S3C_PCM_FIFOSTAT_RXCNT_MSK (0x3f << 4) +#define S3C_PCM_FIFOSTAT_RXFIFOEMPTY (0x1 << 3) +#define S3C_PCM_FIFOSTAT_RXFIFOALMSTEMPTY (0x1 << 2) +#define S3C_PCM_FIFOSTAT_RXFIFOFULL (0x1 << 1) +#define S3C_PCM_FIFOSTAT_RXFIFOALMSTFULL (0x1 << 0) + +/** + * struct s3c_pcm_info - S3C PCM Controller information + * @dev: The parent device passed to use from the probe. + * @regs: The pointer to the device register block. + * @dma_playback: DMA information for playback channel. + * @dma_capture: DMA information for capture channel. + */ +struct s3c_pcm_info { + spinlock_t lock; + struct device *dev; + void __iomem *regs; + + unsigned int sclk_per_fs; + + /* Whether to keep PCMSCLK enabled even when idle(no active xfer) */ + unsigned int idleclk; + + struct clk *pclk; + struct clk *cclk; + + struct s3c_dma_params *dma_playback; + struct s3c_dma_params *dma_capture; +}; + static struct s3c2410_dma_client s3c_pcm_dma_client_out = { .name = "PCM Stereo out" }; diff --git a/sound/soc/samsung/pcm.h b/sound/soc/samsung/pcm.h index 03393dcf852d..726baf814613 100644 --- a/sound/soc/samsung/pcm.h +++ b/sound/soc/samsung/pcm.h @@ -9,116 +9,9 @@ #ifndef __S3C_PCM_H #define __S3C_PCM_H __FILE__ -/*Register Offsets */ -#define S3C_PCM_CTL (0x00) -#define S3C_PCM_CLKCTL (0x04) -#define S3C_PCM_TXFIFO (0x08) -#define S3C_PCM_RXFIFO (0x0C) -#define S3C_PCM_IRQCTL (0x10) -#define S3C_PCM_IRQSTAT (0x14) -#define S3C_PCM_FIFOSTAT (0x18) -#define S3C_PCM_CLRINT (0x20) - -/* PCM_CTL Bit-Fields */ -#define S3C_PCM_CTL_TXDIPSTICK_MASK (0x3f) -#define S3C_PCM_CTL_TXDIPSTICK_SHIFT (13) -#define S3C_PCM_CTL_RXDIPSTICK_MASK (0x3f) -#define S3C_PCM_CTL_RXDIPSTICK_SHIFT (7) -#define S3C_PCM_CTL_TXDMA_EN (0x1<<6) -#define S3C_PCM_CTL_RXDMA_EN (0x1<<5) -#define S3C_PCM_CTL_TXMSB_AFTER_FSYNC (0x1<<4) -#define S3C_PCM_CTL_RXMSB_AFTER_FSYNC (0x1<<3) -#define S3C_PCM_CTL_TXFIFO_EN (0x1<<2) -#define S3C_PCM_CTL_RXFIFO_EN (0x1<<1) -#define S3C_PCM_CTL_ENABLE (0x1<<0) - -/* PCM_CLKCTL Bit-Fields */ -#define S3C_PCM_CLKCTL_SERCLK_EN (0x1<<19) -#define S3C_PCM_CLKCTL_SERCLKSEL_PCLK (0x1<<18) -#define S3C_PCM_CLKCTL_SCLKDIV_MASK (0x1ff) -#define S3C_PCM_CLKCTL_SYNCDIV_MASK (0x1ff) -#define S3C_PCM_CLKCTL_SCLKDIV_SHIFT (9) -#define S3C_PCM_CLKCTL_SYNCDIV_SHIFT (0) - -/* PCM_TXFIFO Bit-Fields */ -#define S3C_PCM_TXFIFO_DVALID (0x1<<16) -#define S3C_PCM_TXFIFO_DATA_MSK (0xffff<<0) - -/* PCM_RXFIFO Bit-Fields */ -#define S3C_PCM_RXFIFO_DVALID (0x1<<16) -#define S3C_PCM_RXFIFO_DATA_MSK (0xffff<<0) - -/* PCM_IRQCTL Bit-Fields */ -#define S3C_PCM_IRQCTL_IRQEN (0x1<<14) -#define S3C_PCM_IRQCTL_WRDEN (0x1<<12) -#define S3C_PCM_IRQCTL_TXEMPTYEN (0x1<<11) -#define S3C_PCM_IRQCTL_TXALMSTEMPTYEN (0x1<<10) -#define S3C_PCM_IRQCTL_TXFULLEN (0x1<<9) -#define S3C_PCM_IRQCTL_TXALMSTFULLEN (0x1<<8) -#define S3C_PCM_IRQCTL_TXSTARVEN (0x1<<7) -#define S3C_PCM_IRQCTL_TXERROVRFLEN (0x1<<6) -#define S3C_PCM_IRQCTL_RXEMPTEN (0x1<<5) -#define S3C_PCM_IRQCTL_RXALMSTEMPTEN (0x1<<4) -#define S3C_PCM_IRQCTL_RXFULLEN (0x1<<3) -#define S3C_PCM_IRQCTL_RXALMSTFULLEN (0x1<<2) -#define S3C_PCM_IRQCTL_RXSTARVEN (0x1<<1) -#define S3C_PCM_IRQCTL_RXERROVRFLEN (0x1<<0) - -/* PCM_IRQSTAT Bit-Fields */ -#define S3C_PCM_IRQSTAT_IRQPND (0x1<<13) -#define S3C_PCM_IRQSTAT_WRD_XFER (0x1<<12) -#define S3C_PCM_IRQSTAT_TXEMPTY (0x1<<11) -#define S3C_PCM_IRQSTAT_TXALMSTEMPTY (0x1<<10) -#define S3C_PCM_IRQSTAT_TXFULL (0x1<<9) -#define S3C_PCM_IRQSTAT_TXALMSTFULL (0x1<<8) -#define S3C_PCM_IRQSTAT_TXSTARV (0x1<<7) -#define S3C_PCM_IRQSTAT_TXERROVRFL (0x1<<6) -#define S3C_PCM_IRQSTAT_RXEMPT (0x1<<5) -#define S3C_PCM_IRQSTAT_RXALMSTEMPT (0x1<<4) -#define S3C_PCM_IRQSTAT_RXFULL (0x1<<3) -#define S3C_PCM_IRQSTAT_RXALMSTFULL (0x1<<2) -#define S3C_PCM_IRQSTAT_RXSTARV (0x1<<1) -#define S3C_PCM_IRQSTAT_RXERROVRFL (0x1<<0) - -/* PCM_FIFOSTAT Bit-Fields */ -#define S3C_PCM_FIFOSTAT_TXCNT_MSK (0x3f<<14) -#define S3C_PCM_FIFOSTAT_TXFIFOEMPTY (0x1<<13) -#define S3C_PCM_FIFOSTAT_TXFIFOALMSTEMPTY (0x1<<12) -#define S3C_PCM_FIFOSTAT_TXFIFOFULL (0x1<<11) -#define S3C_PCM_FIFOSTAT_TXFIFOALMSTFULL (0x1<<10) -#define S3C_PCM_FIFOSTAT_RXCNT_MSK (0x3f<<4) -#define S3C_PCM_FIFOSTAT_RXFIFOEMPTY (0x1<<3) -#define S3C_PCM_FIFOSTAT_RXFIFOALMSTEMPTY (0x1<<2) -#define S3C_PCM_FIFOSTAT_RXFIFOFULL (0x1<<1) -#define S3C_PCM_FIFOSTAT_RXFIFOALMSTFULL (0x1<<0) - #define S3C_PCM_CLKSRC_PCLK 0 #define S3C_PCM_CLKSRC_MUX 1 #define S3C_PCM_SCLK_PER_FS 0 -/** - * struct s3c_pcm_info - S3C PCM Controller information - * @dev: The parent device passed to use from the probe. - * @regs: The pointer to the device register block. - * @dma_playback: DMA information for playback channel. - * @dma_capture: DMA information for capture channel. - */ -struct s3c_pcm_info { - spinlock_t lock; - struct device *dev; - void __iomem *regs; - - unsigned int sclk_per_fs; - - /* Whether to keep PCMSCLK enabled even when idle(no active xfer) */ - unsigned int idleclk; - - struct clk *pclk; - struct clk *cclk; - - struct s3c_dma_params *dma_playback; - struct s3c_dma_params *dma_capture; -}; - #endif /* __S3C_PCM_H */ diff --git a/sound/soc/samsung/rx1950_uda1380.c b/sound/soc/samsung/rx1950_uda1380.c index f40027445dda..1e574a5d440d 100644 --- a/sound/soc/samsung/rx1950_uda1380.c +++ b/sound/soc/samsung/rx1950_uda1380.c @@ -17,26 +17,15 @@ * */ -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/platform_device.h> -#include <linux/i2c.h> #include <linux/gpio.h> -#include <linux/clk.h> #include <sound/soc.h> -#include <sound/uda1380.h> #include <sound/jack.h> #include <plat/regs-iis.h> - -#include <mach/regs-clock.h> - #include <asm/mach-types.h> -#include "dma.h" #include "s3c24xx-i2s.h" -#include "../codecs/uda1380.h" static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd); static int rx1950_startup(struct snd_pcm_substream *substream); diff --git a/sound/soc/samsung/s3c-i2s-v2.c b/sound/soc/samsung/s3c-i2s-v2.c index 094f36e41e83..52074a2b0696 100644 --- a/sound/soc/samsung/s3c-i2s-v2.c +++ b/sound/soc/samsung/s3c-i2s-v2.c @@ -20,9 +20,8 @@ #include <linux/clk.h> #include <linux/io.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/pcm_params.h> #include <mach/dma.h> diff --git a/sound/soc/samsung/s3c2412-i2s.c b/sound/soc/samsung/s3c2412-i2s.c index 7ea837867124..841ab14c1100 100644 --- a/sound/soc/samsung/s3c2412-i2s.c +++ b/sound/soc/samsung/s3c2412-i2s.c @@ -16,21 +16,13 @@ * option) any later version. */ -#include <linux/init.h> -#include <linux/module.h> -#include <linux/device.h> #include <linux/delay.h> #include <linux/gpio.h> #include <linux/clk.h> -#include <linux/kernel.h> #include <linux/io.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/initval.h> #include <sound/soc.h> -#include <mach/hardware.h> +#include <sound/pcm_params.h> #include <mach/regs-gpio.h> #include <mach/dma.h> @@ -39,8 +31,6 @@ #include "regs-i2s-v2.h" #include "s3c2412-i2s.h" -#define S3C2412_I2S_DEBUG 0 - static struct s3c2410_dma_client s3c2412_dma_client_out = { .name = "I2S PCM Stereo out" }; diff --git a/sound/soc/samsung/s3c24xx-i2s.c b/sound/soc/samsung/s3c24xx-i2s.c index 13e41ed8e22b..63d8849d80bd 100644 --- a/sound/soc/samsung/s3c24xx-i2s.c +++ b/sound/soc/samsung/s3c24xx-i2s.c @@ -14,28 +14,16 @@ * 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/jiffies.h> #include <linux/io.h> #include <linux/gpio.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/initval.h> #include <sound/soc.h> +#include <sound/pcm_params.h> -#include <mach/hardware.h> #include <mach/regs-gpio.h> -#include <mach/regs-clock.h> - -#include <asm/dma.h> #include <mach/dma.h> - #include <plat/regs-iis.h> #include "dma.h" diff --git a/sound/soc/samsung/s3c24xx_simtec.c b/sound/soc/samsung/s3c24xx_simtec.c index a434032d1832..349566f0686b 100644 --- a/sound/soc/samsung/s3c24xx_simtec.c +++ b/sound/soc/samsung/s3c24xx_simtec.c @@ -7,20 +7,13 @@ * published by the Free Software Foundation. */ -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/platform_device.h> #include <linux/gpio.h> #include <linux/clk.h> -#include <linux/i2c.h> -#include <sound/core.h> -#include <sound/pcm.h> #include <sound/soc.h> #include <plat/audio-simtec.h> -#include "dma.h" #include "s3c24xx-i2s.h" #include "s3c24xx_simtec.h" diff --git a/sound/soc/samsung/s3c24xx_simtec_hermes.c b/sound/soc/samsung/s3c24xx_simtec_hermes.c index 08fcaaa66907..ce6aef604179 100644 --- a/sound/soc/samsung/s3c24xx_simtec_hermes.c +++ b/sound/soc/samsung/s3c24xx_simtec_hermes.c @@ -7,18 +7,8 @@ * published by the Free Software Foundation. */ -#include <linux/module.h> -#include <linux/clk.h> -#include <linux/platform_device.h> - -#include <sound/core.h> -#include <sound/pcm.h> #include <sound/soc.h> -#include <plat/audio-simtec.h> - -#include "dma.h" -#include "s3c24xx-i2s.h" #include "s3c24xx_simtec.h" static const struct snd_soc_dapm_widget dapm_widgets[] = { diff --git a/sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c b/sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c index 116e3e670167..a7ef7db54687 100644 --- a/sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c +++ b/sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c @@ -7,22 +7,10 @@ * published by the Free Software Foundation. */ -#include <linux/module.h> -#include <linux/clk.h> -#include <linux/platform_device.h> - -#include <sound/core.h> -#include <sound/pcm.h> #include <sound/soc.h> -#include <plat/audio-simtec.h> - -#include "dma.h" -#include "s3c24xx-i2s.h" #include "s3c24xx_simtec.h" -#include "../codecs/tlv320aic23.h" - /* supported machines: * * Machine Connections AMP diff --git a/sound/soc/samsung/s3c24xx_uda134x.c b/sound/soc/samsung/s3c24xx_uda134x.c index 2c09e93dd566..3cb700751078 100644 --- a/sound/soc/samsung/s3c24xx_uda134x.c +++ b/sound/soc/samsung/s3c24xx_uda134x.c @@ -11,22 +11,15 @@ * published by the Free Software Foundation. */ -#include <linux/module.h> #include <linux/clk.h> -#include <linux/mutex.h> #include <linux/gpio.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> + #include <sound/soc.h> #include <sound/s3c24xx_uda134x.h> -#include <sound/uda134x.h> #include <plat/regs-iis.h> -#include "dma.h" #include "s3c24xx-i2s.h" -#include "../codecs/uda134x.h" - /* #define ENFORCE_RATES 1 */ /* diff --git a/sound/soc/samsung/smartq_wm8987.c b/sound/soc/samsung/smartq_wm8987.c index 61e2b5219d42..0a2c4f223038 100644 --- a/sound/soc/samsung/smartq_wm8987.c +++ b/sound/soc/samsung/smartq_wm8987.c @@ -13,20 +13,14 @@ * */ -#include <linux/module.h> -#include <linux/platform_device.h> #include <linux/gpio.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/jack.h> #include <asm/mach-types.h> -#include "dma.h" #include "i2s.h" - #include "../codecs/wm8750.h" /* diff --git a/sound/soc/samsung/smdk2443_wm9710.c b/sound/soc/samsung/smdk2443_wm9710.c index 3be7e7e92d6e..3a0dbfc793f0 100644 --- a/sound/soc/samsung/smdk2443_wm9710.c +++ b/sound/soc/samsung/smdk2443_wm9710.c @@ -12,15 +12,8 @@ * */ -#include <linux/module.h> -#include <linux/device.h> -#include <sound/core.h> -#include <sound/pcm.h> #include <sound/soc.h> -#include "dma.h" -#include "ac97.h" - static struct snd_soc_card smdk2443; static struct snd_soc_dai_link smdk2443_dai[] = { diff --git a/sound/soc/samsung/smdk_spdif.c b/sound/soc/samsung/smdk_spdif.c index b5c3fad01bb8..e8ac961c6ba1 100644 --- a/sound/soc/samsung/smdk_spdif.c +++ b/sound/soc/samsung/smdk_spdif.c @@ -10,15 +10,10 @@ * */ -#include <linux/module.h> -#include <linux/device.h> #include <linux/clk.h> -#include <plat/devs.h> - #include <sound/soc.h> -#include "dma.h" #include "spdif.h" /* Audio clock settings are belonged to board specific part. Every diff --git a/sound/soc/samsung/smdk_wm8580.c b/sound/soc/samsung/smdk_wm8580.c index b2cff1a44aed..8aacf23d6f3a 100644 --- a/sound/soc/samsung/smdk_wm8580.c +++ b/sound/soc/samsung/smdk_wm8580.c @@ -10,17 +10,12 @@ * option) any later version. */ -#include <linux/platform_device.h> -#include <linux/clk.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/pcm_params.h> #include <asm/mach-types.h> #include "../codecs/wm8580.h" -#include "dma.h" #include "i2s.h" /* diff --git a/sound/soc/samsung/smdk_wm9713.c b/sound/soc/samsung/smdk_wm9713.c index ae5fed6f772f..fffe3c1dd1bd 100644 --- a/sound/soc/samsung/smdk_wm9713.c +++ b/sound/soc/samsung/smdk_wm9713.c @@ -11,13 +11,8 @@ * */ -#include <linux/module.h> -#include <linux/device.h> #include <sound/soc.h> -#include "dma.h" -#include "ac97.h" - static struct snd_soc_card smdk; /* diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c index f0816404ea3e..28c491dacf7a 100644 --- a/sound/soc/samsung/spdif.c +++ b/sound/soc/samsung/spdif.c @@ -13,9 +13,8 @@ #include <linux/clk.h> #include <linux/io.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/pcm_params.h> #include <plat/audio.h> #include <mach/dma.h> diff --git a/sound/soc/sh/fsi-ak4642.c b/sound/soc/sh/fsi-ak4642.c index a14820ac9665..d6f4703b3c07 100644 --- a/sound/soc/sh/fsi-ak4642.c +++ b/sound/soc/sh/fsi-ak4642.c @@ -18,18 +18,26 @@ struct fsi_ak4642_data { const char *cpu_dai; const char *codec; const char *platform; + int id; }; static int fsi_ak4642_dai_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *codec = rtd->codec_dai; + struct snd_soc_dai *cpu = rtd->cpu_dai; int ret; - ret = snd_soc_dai_set_fmt(dai, SND_SOC_DAIFMT_CBM_CFM); + ret = snd_soc_dai_set_fmt(codec, SND_SOC_DAIFMT_LEFT_J | + SND_SOC_DAIFMT_CBM_CFM); if (ret < 0) return ret; - ret = snd_soc_dai_set_sysclk(dai, 0, 11289600, 0); + ret = snd_soc_dai_set_sysclk(codec, 0, 11289600, 0); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_LEFT_J | + SND_SOC_DAIFMT_CBS_CFS); return ret; } @@ -60,7 +68,7 @@ static int fsi_ak4642_probe(struct platform_device *pdev) pdata = (struct fsi_ak4642_data *)id_entry->driver_data; - fsi_snd_device = platform_device_alloc("soc-audio", FSI_PORT_A); + fsi_snd_device = platform_device_alloc("soc-audio", pdata->id); if (!fsi_snd_device) goto out; @@ -93,6 +101,7 @@ static struct fsi_ak4642_data fsi_a_ak4642 = { .cpu_dai = "fsia-dai", .codec = "ak4642-codec.0-0012", .platform = "sh_fsi.0", + .id = FSI_PORT_A, }; static struct fsi_ak4642_data fsi_b_ak4642 = { @@ -101,6 +110,7 @@ static struct fsi_ak4642_data fsi_b_ak4642 = { .cpu_dai = "fsib-dai", .codec = "ak4642-codec.0-0012", .platform = "sh_fsi.0", + .id = FSI_PORT_B, }; static struct fsi_ak4642_data fsi_a_ak4643 = { @@ -109,6 +119,7 @@ static struct fsi_ak4642_data fsi_a_ak4643 = { .cpu_dai = "fsia-dai", .codec = "ak4642-codec.0-0013", .platform = "sh_fsi.0", + .id = FSI_PORT_A, }; static struct fsi_ak4642_data fsi_b_ak4643 = { @@ -117,6 +128,7 @@ static struct fsi_ak4642_data fsi_b_ak4643 = { .cpu_dai = "fsib-dai", .codec = "ak4642-codec.0-0013", .platform = "sh_fsi.0", + .id = FSI_PORT_B, }; static struct fsi_ak4642_data fsi2_a_ak4642 = { @@ -125,6 +137,7 @@ static struct fsi_ak4642_data fsi2_a_ak4642 = { .cpu_dai = "fsia-dai", .codec = "ak4642-codec.0-0012", .platform = "sh_fsi2", + .id = FSI_PORT_A, }; static struct fsi_ak4642_data fsi2_b_ak4642 = { @@ -133,6 +146,7 @@ static struct fsi_ak4642_data fsi2_b_ak4642 = { .cpu_dai = "fsib-dai", .codec = "ak4642-codec.0-0012", .platform = "sh_fsi2", + .id = FSI_PORT_B, }; static struct fsi_ak4642_data fsi2_a_ak4643 = { @@ -141,6 +155,7 @@ static struct fsi_ak4642_data fsi2_a_ak4643 = { .cpu_dai = "fsia-dai", .codec = "ak4642-codec.0-0013", .platform = "sh_fsi2", + .id = FSI_PORT_A, }; static struct fsi_ak4642_data fsi2_b_ak4643 = { @@ -149,6 +164,7 @@ static struct fsi_ak4642_data fsi2_b_ak4643 = { .cpu_dai = "fsib-dai", .codec = "ak4642-codec.0-0013", .platform = "sh_fsi2", + .id = FSI_PORT_B, }; static struct platform_device_id fsi_id_table[] = { diff --git a/sound/soc/sh/fsi-da7210.c b/sound/soc/sh/fsi-da7210.c index e8df9da92f71..dbafd7ac5590 100644 --- a/sound/soc/sh/fsi-da7210.c +++ b/sound/soc/sh/fsi-da7210.c @@ -15,11 +15,20 @@ static int fsi_da7210_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *dai = rtd->codec_dai; + struct snd_soc_dai *codec = rtd->codec_dai; + struct snd_soc_dai *cpu = rtd->cpu_dai; + int ret; - return snd_soc_dai_set_fmt(dai, + ret = snd_soc_dai_set_fmt(codec, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_CBS_CFS); + + return ret; } static struct snd_soc_dai_link fsi_da7210_dai = { diff --git a/sound/soc/sh/fsi-hdmi.c b/sound/soc/sh/fsi-hdmi.c index a52dd8ec71d3..9719985eb82d 100644 --- a/sound/soc/sh/fsi-hdmi.c +++ b/sound/soc/sh/fsi-hdmi.c @@ -12,31 +12,59 @@ #include <linux/platform_device.h> #include <sound/sh_fsi.h> +struct fsi_hdmi_data { + const char *cpu_dai; + const char *card; + int id; +}; + +static int fsi_hdmi_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *cpu = rtd->cpu_dai; + int ret; + + ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_CBM_CFM); + + return ret; +} + static struct snd_soc_dai_link fsi_dai_link = { .name = "HDMI", .stream_name = "HDMI", - .cpu_dai_name = "fsib-dai", /* fsi B */ .codec_dai_name = "sh_mobile_hdmi-hifi", .platform_name = "sh_fsi2", .codec_name = "sh-mobile-hdmi", + .init = fsi_hdmi_dai_init, }; static struct snd_soc_card fsi_soc_card = { - .name = "FSI (SH MOBILE HDMI)", .dai_link = &fsi_dai_link, .num_links = 1, }; static struct platform_device *fsi_snd_device; -static int __init fsi_hdmi_init(void) +static int fsi_hdmi_probe(struct platform_device *pdev) { int ret = -ENOMEM; + const struct platform_device_id *id_entry; + struct fsi_hdmi_data *pdata; + + id_entry = pdev->id_entry; + if (!id_entry) { + dev_err(&pdev->dev, "unknown fsi hdmi\n"); + return -ENODEV; + } - fsi_snd_device = platform_device_alloc("soc-audio", FSI_PORT_B); + pdata = (struct fsi_hdmi_data *)id_entry->driver_data; + + fsi_snd_device = platform_device_alloc("soc-audio", pdata->id); if (!fsi_snd_device) goto out; + fsi_dai_link.cpu_dai_name = pdata->cpu_dai; + fsi_soc_card.name = pdata->card; + platform_set_drvdata(fsi_snd_device, &fsi_soc_card); ret = platform_device_add(fsi_snd_device); @@ -47,9 +75,48 @@ out: return ret; } -static void __exit fsi_hdmi_exit(void) +static int fsi_hdmi_remove(struct platform_device *pdev) { platform_device_unregister(fsi_snd_device); + return 0; +} + +static struct fsi_hdmi_data fsi2_a_hdmi = { + .cpu_dai = "fsia-dai", + .card = "FSI2A (SH MOBILE HDMI)", + .id = FSI_PORT_A, +}; + +static struct fsi_hdmi_data fsi2_b_hdmi = { + .cpu_dai = "fsib-dai", + .card = "FSI2B (SH MOBILE HDMI)", + .id = FSI_PORT_B, +}; + +static struct platform_device_id fsi_id_table[] = { + /* FSI 2 */ + { "sh_fsi2_a_hdmi", (kernel_ulong_t)&fsi2_a_hdmi }, + { "sh_fsi2_b_hdmi", (kernel_ulong_t)&fsi2_b_hdmi }, + {}, +}; + +static struct platform_driver fsi_hdmi = { + .driver = { + .name = "fsi-hdmi-audio", + }, + .probe = fsi_hdmi_probe, + .remove = fsi_hdmi_remove, + .id_table = fsi_id_table, +}; + +static int __init fsi_hdmi_init(void) +{ + return platform_driver_register(&fsi_hdmi); +} + +static void __exit fsi_hdmi_exit(void) +{ + platform_driver_unregister(&fsi_hdmi); } module_init(fsi_hdmi_init); diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 2b06402801ef..0c9997e2d8c0 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -78,6 +78,8 @@ /* CKG1 */ #define ACKMD_MASK 0x00007000 #define BPFMD_MASK 0x00000700 +#define DIMD (1 << 4) +#define DOMD (1 << 0) /* A/B MST_CTLR */ #define BP (1 << 4) /* Fix the signal of Biphase output */ @@ -111,6 +113,8 @@ #define FSI_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) +typedef int (*set_rate_func)(struct device *dev, int is_porta, int rate, int enable); + /* * FSI driver use below type name for variable * @@ -128,7 +132,6 @@ struct fsi_stream { struct snd_pcm_substream *substream; int fifo_max_num; - int chan_num; int buff_offset; int buff_len; @@ -143,6 +146,7 @@ struct fsi_priv { void __iomem *base; struct fsi_master *master; + int chan_num; struct fsi_stream playback; struct fsi_stream capture; @@ -252,9 +256,8 @@ static struct snd_soc_dai *fsi_get_dai(struct snd_pcm_substream *substream) return rtd->cpu_dai; } -static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream) +static struct fsi_priv *fsi_get_priv_frm_dai(struct snd_soc_dai *dai) { - struct snd_soc_dai *dai = fsi_get_dai(substream); struct fsi_master *master = snd_soc_dai_get_drvdata(dai); if (dai->id == 0) @@ -263,11 +266,27 @@ static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream) return &master->fsib; } +static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream) +{ + return fsi_get_priv_frm_dai(fsi_get_dai(substream)); +} + +static set_rate_func fsi_get_info_set_rate(struct fsi_master *master) +{ + if (!master->info) + return NULL; + + return master->info->set_rate; +} + static u32 fsi_get_info_flags(struct fsi_priv *fsi) { int is_porta = fsi_is_port_a(fsi); struct fsi_master *master = fsi_get_master(fsi); + if (!master->info) + return 0; + return is_porta ? master->info->porta_flags : master->info->portb_flags; } @@ -288,21 +307,6 @@ static inline struct fsi_stream *fsi_get_stream(struct fsi_priv *fsi, return is_play ? &fsi->playback : &fsi->capture; } -static int fsi_is_master_mode(struct fsi_priv *fsi, int is_play) -{ - u32 mode; - u32 flags = fsi_get_info_flags(fsi); - - mode = is_play ? SH_FSI_OUT_SLAVE_MODE : SH_FSI_IN_SLAVE_MODE; - - /* return - * 1 : master mode - * 0 : slave mode - */ - - return (mode & flags) != mode; -} - static u32 fsi_get_port_shift(struct fsi_priv *fsi, int is_play) { int is_porta = fsi_is_port_a(fsi); @@ -357,7 +361,6 @@ static void fsi_stream_pop(struct fsi_priv *fsi, int is_play) static int fsi_get_fifo_data_num(struct fsi_priv *fsi, int is_play) { u32 status; - struct fsi_stream *io = fsi_get_stream(fsi, is_play); int data_num; status = is_play ? @@ -365,7 +368,7 @@ static int fsi_get_fifo_data_num(struct fsi_priv *fsi, int is_play) fsi_reg_read(fsi, DIFF_ST); data_num = 0x1ff & (status >> 8); - data_num *= io->chan_num; + data_num *= fsi->chan_num; return data_num; } @@ -387,7 +390,7 @@ static int fsi_get_frame_width(struct fsi_priv *fsi, int is_play) struct snd_pcm_substream *substream = io->substream; struct snd_pcm_runtime *runtime = substream->runtime; - return frames_to_bytes(runtime, 1) / io->chan_num; + return frames_to_bytes(runtime, 1) / fsi->chan_num; } static void fsi_count_fifo_err(struct fsi_priv *fsi) @@ -580,10 +583,10 @@ static void fsi_fifo_init(struct fsi_priv *fsi, * 7 channels: 32 ( 32 x 7 = 224) * 8 channels: 32 ( 32 x 8 = 256) */ - for (i = 1; i < io->chan_num; i <<= 1) + for (i = 1; i < fsi->chan_num; i <<= 1) io->fifo_max_num >>= 1; dev_dbg(dai->dev, "%d channel %d store\n", - io->chan_num, io->fifo_max_num); + fsi->chan_num, io->fifo_max_num); /* * set interrupt generation factor @@ -659,7 +662,7 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int stream) * data_num_max : number of FSI fifo free space * data_num : number of ALSA residue data */ - data_num_max = io->fifo_max_num * io->chan_num; + data_num_max = io->fifo_max_num * fsi->chan_num; data_num_max -= fsi_get_fifo_data_num(fsi, is_play); data_num = data_residue_num; @@ -754,25 +757,12 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct fsi_priv *fsi = fsi_get_priv(substream); - struct fsi_master *master = fsi_get_master(fsi); - struct fsi_stream *io; u32 flags = fsi_get_info_flags(fsi); - u32 fmt; u32 data; int is_play = fsi_is_play(substream); - int is_master; - - io = fsi_get_stream(fsi, is_play); pm_runtime_get_sync(dai->dev); - /* CKG1 */ - data = is_play ? (1 << 0) : (1 << 4); - is_master = fsi_is_master_mode(fsi, is_play); - if (is_master) - fsi_reg_mask_set(fsi, CKG1, data, data); - else - fsi_reg_mask_set(fsi, CKG1, data, 0); /* clock inversion (CKG2) */ data = 0; @@ -787,54 +777,6 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, fsi_reg_write(fsi, CKG2, data); - /* do fmt, di fmt */ - data = 0; - fmt = is_play ? SH_FSI_GET_OFMT(flags) : SH_FSI_GET_IFMT(flags); - switch (fmt) { - case SH_FSI_FMT_MONO: - data = CR_MONO; - io->chan_num = 1; - break; - case SH_FSI_FMT_MONO_DELAY: - data = CR_MONO_D; - io->chan_num = 1; - break; - case SH_FSI_FMT_PCM: - data = CR_PCM; - io->chan_num = 2; - break; - case SH_FSI_FMT_I2S: - data = CR_I2S; - io->chan_num = 2; - break; - case SH_FSI_FMT_TDM: - io->chan_num = is_play ? - SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags); - data = CR_TDM | (io->chan_num - 1); - break; - case SH_FSI_FMT_TDM_DELAY: - io->chan_num = is_play ? - SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags); - data = CR_TDM_D | (io->chan_num - 1); - break; - case SH_FSI_FMT_SPDIF: - if (master->core->ver < 2) { - dev_err(dai->dev, "This FSI can not use SPDIF\n"); - return -EINVAL; - } - data = CR_BWS_16 | CR_DTMD_SPDIF_PCM | CR_PCM; - io->chan_num = 2; - fsi_spdif_clk_ctrl(fsi, 1); - fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD); - break; - default: - dev_err(dai->dev, "unknown format.\n"); - return -EINVAL; - } - is_play ? - fsi_reg_write(fsi, DO_FMT, data) : - fsi_reg_write(fsi, DI_FMT, data); - /* irq clear */ fsi_irq_disable(fsi, is_play); fsi_irq_clear_status(fsi); @@ -851,12 +793,12 @@ static void fsi_dai_shutdown(struct snd_pcm_substream *substream, struct fsi_priv *fsi = fsi_get_priv(substream); int is_play = fsi_is_play(substream); struct fsi_master *master = fsi_get_master(fsi); - int (*set_rate)(struct device *dev, int is_porta, int rate, int enable); + set_rate_func set_rate; fsi_irq_disable(fsi, is_play); fsi_clk_ctrl(fsi, 0); - set_rate = master->info->set_rate; + set_rate = fsi_get_info_set_rate(master); if (set_rate && fsi->rate) set_rate(dai->dev, fsi_is_port_a(fsi), fsi->rate, 0); fsi->rate = 0; @@ -889,18 +831,100 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } +static int fsi_set_fmt_dai(struct fsi_priv *fsi, unsigned int fmt) +{ + u32 data = 0; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + data = CR_I2S; + fsi->chan_num = 2; + break; + case SND_SOC_DAIFMT_LEFT_J: + data = CR_PCM; + fsi->chan_num = 2; + break; + default: + return -EINVAL; + } + + fsi_reg_write(fsi, DO_FMT, data); + fsi_reg_write(fsi, DI_FMT, data); + + return 0; +} + +static int fsi_set_fmt_spdif(struct fsi_priv *fsi) +{ + struct fsi_master *master = fsi_get_master(fsi); + u32 data = 0; + + if (master->core->ver < 2) + return -EINVAL; + + data = CR_BWS_16 | CR_DTMD_SPDIF_PCM | CR_PCM; + fsi->chan_num = 2; + fsi_spdif_clk_ctrl(fsi, 1); + fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD); + + fsi_reg_write(fsi, DO_FMT, data); + fsi_reg_write(fsi, DI_FMT, data); + + return 0; +} + +static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct fsi_priv *fsi = fsi_get_priv_frm_dai(dai); + u32 flags = fsi_get_info_flags(fsi); + u32 data = 0; + int ret; + + pm_runtime_get_sync(dai->dev); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + data = DIMD | DOMD; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + ret = -EINVAL; + goto set_fmt_exit; + } + fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data); + + /* set format */ + switch (flags & SH_FSI_FMT_MASK) { + case SH_FSI_FMT_DAI: + ret = fsi_set_fmt_dai(fsi, fmt & SND_SOC_DAIFMT_FORMAT_MASK); + break; + case SH_FSI_FMT_SPDIF: + ret = fsi_set_fmt_spdif(fsi); + break; + default: + ret = -EINVAL; + } + +set_fmt_exit: + pm_runtime_put_sync(dai->dev); + + return ret; +} + static int fsi_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct fsi_priv *fsi = fsi_get_priv(substream); struct fsi_master *master = fsi_get_master(fsi); - int (*set_rate)(struct device *dev, int is_porta, int rate, int enable); + set_rate_func set_rate; int fsi_ver = master->core->ver; long rate = params_rate(params); int ret; - set_rate = master->info->set_rate; + set_rate = fsi_get_info_set_rate(master); if (!set_rate) return 0; @@ -975,6 +999,7 @@ static struct snd_soc_dai_ops fsi_dai_ops = { .startup = fsi_dai_startup, .shutdown = fsi_dai_shutdown, .trigger = fsi_dai_trigger, + .set_fmt = fsi_dai_set_fmt, .hw_params = fsi_dai_hw_params, }; diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index 8c2a21a978ac..5d76da43b14c 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -18,6 +18,8 @@ #include <linux/bitmap.h> #include <linux/rbtree.h> +#include <trace/events/asoc.h> + static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec, unsigned int reg) { @@ -25,7 +27,8 @@ static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec, unsigned int val; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { if (codec->cache_only) return -1; @@ -49,7 +52,8 @@ static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg, data[1] = value & 0x00ff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) { + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { ret = snd_soc_cache_write(codec, reg, value); if (ret < 0) return -1; @@ -106,7 +110,8 @@ static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec, unsigned int val; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { if (codec->cache_only) return -1; @@ -130,7 +135,8 @@ static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg, data[1] = value & 0x00ff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) { + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { ret = snd_soc_cache_write(codec, reg, value); if (ret < 0) return -1; @@ -191,7 +197,8 @@ static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg, data[1] = value & 0xff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) { + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { ret = snd_soc_cache_write(codec, reg, value); if (ret < 0) return -1; @@ -216,7 +223,8 @@ static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec, reg &= 0xff; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { if (codec->cache_only) return -1; @@ -271,7 +279,8 @@ static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg, data[2] = value & 0xff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) { + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { ret = snd_soc_cache_write(codec, reg, value); if (ret < 0) return -1; @@ -295,7 +304,8 @@ static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec, unsigned int val; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { if (codec->cache_only) return -1; @@ -450,7 +460,8 @@ static unsigned int snd_soc_16_8_read(struct snd_soc_codec *codec, reg &= 0xff; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { if (codec->cache_only) return -1; @@ -476,7 +487,8 @@ static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg, reg &= 0xff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) { + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { ret = snd_soc_cache_write(codec, reg, value); if (ret < 0) return -1; @@ -568,7 +580,8 @@ static unsigned int snd_soc_16_16_read(struct snd_soc_codec *codec, unsigned int val; if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg)) { + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { if (codec->cache_only) return -1; @@ -595,7 +608,8 @@ static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg, data[3] = value & 0xff; if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size) { + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { ret = snd_soc_cache_write(codec, reg, value); if (ret < 0) return -1; @@ -761,6 +775,49 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, } EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io); +static bool snd_soc_set_cache_val(void *base, unsigned int idx, + unsigned int val, unsigned int word_size) +{ + switch (word_size) { + case 1: { + u8 *cache = base; + if (cache[idx] == val) + return true; + cache[idx] = val; + break; + } + case 2: { + u16 *cache = base; + if (cache[idx] == val) + return true; + cache[idx] = val; + break; + } + default: + BUG(); + } + return false; +} + +static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx, + unsigned int word_size) +{ + switch (word_size) { + case 1: { + const u8 *cache = base; + return cache[idx]; + } + case 2: { + const u16 *cache = base; + return cache[idx]; + } + default: + BUG(); + } + /* unreachable */ + return -1; +} + struct snd_soc_rbtree_node { struct rb_node node; unsigned int reg; @@ -835,7 +892,9 @@ static int snd_soc_rbtree_cache_sync(struct snd_soc_codec *codec) ret = snd_soc_cache_read(codec, rbnode->reg, &val); if (ret) return ret; + codec->cache_bypass = 1; ret = snd_soc_write(codec, rbnode->reg, val); + codec->cache_bypass = 0; if (ret) return ret; dev_dbg(codec->dev, "Synced register %#x, value = %#x\n", @@ -924,7 +983,12 @@ static int snd_soc_rbtree_cache_exit(struct snd_soc_codec *codec) static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec) { + struct snd_soc_rbtree_node *rbtree_node; struct snd_soc_rbtree_ctx *rbtree_ctx; + unsigned int val; + unsigned int word_size; + int i; + int ret; codec->reg_cache = kmalloc(sizeof *rbtree_ctx, GFP_KERNEL); if (!codec->reg_cache) @@ -936,53 +1000,25 @@ static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec) if (!codec->reg_def_copy) return 0; -/* - * populate the rbtree with the initialized registers. All other - * registers will be inserted into the tree when they are first written. - * - * The reasoning behind this, is that we need to step through and - * dereference the cache in u8/u16 increments without sacrificing - * portability. This could also be done using memcpy() but that would - * be slightly more cryptic. - */ -#define snd_soc_rbtree_populate(cache) \ -({ \ - int ret, i; \ - struct snd_soc_rbtree_node *rbtree_node; \ - \ - ret = 0; \ - cache = codec->reg_def_copy; \ - for (i = 0; i < codec->driver->reg_cache_size; ++i) { \ - if (!cache[i]) \ - continue; \ - rbtree_node = kzalloc(sizeof *rbtree_node, GFP_KERNEL); \ - if (!rbtree_node) { \ - ret = -ENOMEM; \ - snd_soc_cache_exit(codec); \ - break; \ - } \ - rbtree_node->reg = i; \ - rbtree_node->value = cache[i]; \ - rbtree_node->defval = cache[i]; \ - snd_soc_rbtree_insert(&rbtree_ctx->root, \ - rbtree_node); \ - } \ - ret; \ -}) - - switch (codec->driver->reg_word_size) { - case 1: { - const u8 *cache; - - return snd_soc_rbtree_populate(cache); - } - case 2: { - const u16 *cache; - - return snd_soc_rbtree_populate(cache); - } - default: - BUG(); + /* + * populate the rbtree with the initialized registers. All other + * registers will be inserted when they are first modified. + */ + word_size = codec->driver->reg_word_size; + for (i = 0; i < codec->driver->reg_cache_size; ++i) { + val = snd_soc_get_cache_val(codec->reg_def_copy, i, word_size); + if (!val) + continue; + rbtree_node = kzalloc(sizeof *rbtree_node, GFP_KERNEL); + if (!rbtree_node) { + ret = -ENOMEM; + snd_soc_cache_exit(codec); + break; + } + rbtree_node->reg = i; + rbtree_node->value = val; + rbtree_node->defval = val; + snd_soc_rbtree_insert(&rbtree_ctx->root, rbtree_node); } return 0; @@ -1080,34 +1116,28 @@ static inline int snd_soc_lzo_get_blkindex(struct snd_soc_codec *codec, unsigned int reg) { const struct snd_soc_codec_driver *codec_drv; - size_t reg_size; codec_drv = codec->driver; - reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; return (reg * codec_drv->reg_word_size) / - DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count()); + DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count()); } static inline int snd_soc_lzo_get_blkpos(struct snd_soc_codec *codec, unsigned int reg) { const struct snd_soc_codec_driver *codec_drv; - size_t reg_size; codec_drv = codec->driver; - reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; - return reg % (DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count()) / + return reg % (DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count()) / codec_drv->reg_word_size); } static inline int snd_soc_lzo_get_blksize(struct snd_soc_codec *codec) { const struct snd_soc_codec_driver *codec_drv; - size_t reg_size; codec_drv = codec->driver; - reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; - return DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count()); + return DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count()); } static int snd_soc_lzo_cache_sync(struct snd_soc_codec *codec) @@ -1122,7 +1152,9 @@ static int snd_soc_lzo_cache_sync(struct snd_soc_codec *codec) ret = snd_soc_cache_read(codec, i, &val); if (ret) return ret; + codec->cache_bypass = 1; ret = snd_soc_write(codec, i, val); + codec->cache_bypass = 0; if (ret) return ret; dev_dbg(codec->dev, "Synced register %#x, value = %#x\n", @@ -1165,29 +1197,10 @@ static int snd_soc_lzo_cache_write(struct snd_soc_codec *codec, } /* write the new value to the cache */ - switch (codec->driver->reg_word_size) { - case 1: { - u8 *cache; - cache = lzo_block->dst; - if (cache[blkpos] == value) { - kfree(lzo_block->dst); - goto out; - } - cache[blkpos] = value; - } - break; - case 2: { - u16 *cache; - cache = lzo_block->dst; - if (cache[blkpos] == value) { - kfree(lzo_block->dst); - goto out; - } - cache[blkpos] = value; - } - break; - default: - BUG(); + if (snd_soc_set_cache_val(lzo_block->dst, blkpos, value, + codec->driver->reg_word_size)) { + kfree(lzo_block->dst); + goto out; } /* prepare the source to be the decompressed block */ @@ -1241,25 +1254,10 @@ static int snd_soc_lzo_cache_read(struct snd_soc_codec *codec, /* decompress the block */ ret = snd_soc_lzo_decompress_cache_block(codec, lzo_block); - if (ret >= 0) { + if (ret >= 0) /* fetch the value from the cache */ - switch (codec->driver->reg_word_size) { - case 1: { - u8 *cache; - cache = lzo_block->dst; - *value = cache[blkpos]; - } - break; - case 2: { - u16 *cache; - cache = lzo_block->dst; - *value = cache[blkpos]; - } - break; - default: - BUG(); - } - } + *value = snd_soc_get_cache_val(lzo_block->dst, blkpos, + codec->driver->reg_word_size); kfree(lzo_block->dst); /* restore the pointer and length of the compressed block */ @@ -1301,7 +1299,7 @@ static int snd_soc_lzo_cache_exit(struct snd_soc_codec *codec) static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec) { struct snd_soc_lzo_ctx **lzo_blocks; - size_t reg_size, bmp_size; + size_t bmp_size; const struct snd_soc_codec_driver *codec_drv; int ret, tofree, i, blksize, blkcount; const char *p, *end; @@ -1309,7 +1307,6 @@ static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec) ret = 0; codec_drv = codec->driver; - reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; /* * If we have not been given a default register cache @@ -1321,8 +1318,7 @@ static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec) tofree = 1; if (!codec->reg_def_copy) { - codec->reg_def_copy = kzalloc(reg_size, - GFP_KERNEL); + codec->reg_def_copy = kzalloc(codec->reg_size, GFP_KERNEL); if (!codec->reg_def_copy) return -ENOMEM; } @@ -1370,7 +1366,7 @@ static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec) blksize = snd_soc_lzo_get_blksize(codec); p = codec->reg_def_copy; - end = codec->reg_def_copy + reg_size; + end = codec->reg_def_copy + codec->reg_size; /* compress the register map and fill the lzo blocks */ for (i = 0; i < blkcount; ++i, p += blksize) { lzo_blocks[i]->src = p; @@ -1414,28 +1410,10 @@ static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec) ret = snd_soc_cache_read(codec, i, &val); if (ret) return ret; - if (codec_drv->reg_cache_default) { - switch (codec_drv->reg_word_size) { - case 1: { - const u8 *cache; - - cache = codec_drv->reg_cache_default; - if (cache[i] == val) - continue; - } - break; - case 2: { - const u16 *cache; - - cache = codec_drv->reg_cache_default; - if (cache[i] == val) - continue; - } - break; - default: - BUG(); - } - } + if (codec->reg_def_copy) + if (snd_soc_get_cache_val(codec->reg_def_copy, + i, codec_drv->reg_word_size) == val) + continue; ret = snd_soc_write(codec, i, val); if (ret) return ret; @@ -1448,50 +1426,16 @@ static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec) static int snd_soc_flat_cache_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { - switch (codec->driver->reg_word_size) { - case 1: { - u8 *cache; - - cache = codec->reg_cache; - cache[reg] = value; - } - break; - case 2: { - u16 *cache; - - cache = codec->reg_cache; - cache[reg] = value; - } - break; - default: - BUG(); - } - + snd_soc_set_cache_val(codec->reg_cache, reg, value, + codec->driver->reg_word_size); return 0; } static int snd_soc_flat_cache_read(struct snd_soc_codec *codec, unsigned int reg, unsigned int *value) { - switch (codec->driver->reg_word_size) { - case 1: { - u8 *cache; - - cache = codec->reg_cache; - *value = cache[reg]; - } - break; - case 2: { - u16 *cache; - - cache = codec->reg_cache; - *value = cache[reg]; - } - break; - default: - BUG(); - } - + *value = snd_soc_get_cache_val(codec->reg_cache, reg, + codec->driver->reg_word_size); return 0; } @@ -1507,24 +1451,14 @@ static int snd_soc_flat_cache_exit(struct snd_soc_codec *codec) static int snd_soc_flat_cache_init(struct snd_soc_codec *codec) { const struct snd_soc_codec_driver *codec_drv; - size_t reg_size; codec_drv = codec->driver; - reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; - /* - * for flat compression, we don't need to keep a copy of the - * original defaults register cache as it will definitely not - * be marked as __devinitconst - */ - kfree(codec->reg_def_copy); - codec->reg_def_copy = NULL; - - if (codec_drv->reg_cache_default) - codec->reg_cache = kmemdup(codec_drv->reg_cache_default, - reg_size, GFP_KERNEL); + if (codec->reg_def_copy) + codec->reg_cache = kmemdup(codec->reg_def_copy, + codec->reg_size, GFP_KERNEL); else - codec->reg_cache = kzalloc(reg_size, GFP_KERNEL); + codec->reg_cache = kzalloc(codec->reg_size, GFP_KERNEL); if (!codec->reg_cache) return -ENOMEM; @@ -1669,21 +1603,77 @@ EXPORT_SYMBOL_GPL(snd_soc_cache_write); int snd_soc_cache_sync(struct snd_soc_codec *codec) { int ret; + const char *name; if (!codec->cache_sync) { return 0; } - if (codec->cache_ops && codec->cache_ops->sync) { - if (codec->cache_ops->name) - dev_dbg(codec->dev, "Syncing %s cache for %s codec\n", - codec->cache_ops->name, codec->name); - ret = codec->cache_ops->sync(codec); - if (!ret) - codec->cache_sync = 0; - return ret; - } + if (!codec->cache_ops || !codec->cache_ops->sync) + return -EINVAL; - return -EINVAL; + if (codec->cache_ops->name) + name = codec->cache_ops->name; + else + name = "unknown"; + + if (codec->cache_ops->name) + dev_dbg(codec->dev, "Syncing %s cache for %s codec\n", + codec->cache_ops->name, codec->name); + trace_snd_soc_cache_sync(codec, name, "start"); + ret = codec->cache_ops->sync(codec); + if (!ret) + codec->cache_sync = 0; + trace_snd_soc_cache_sync(codec, name, "end"); + return ret; } EXPORT_SYMBOL_GPL(snd_soc_cache_sync); + +static int snd_soc_get_reg_access_index(struct snd_soc_codec *codec, + unsigned int reg) +{ + const struct snd_soc_codec_driver *codec_drv; + unsigned int min, max, index; + + codec_drv = codec->driver; + min = 0; + max = codec_drv->reg_access_size - 1; + do { + index = (min + max) / 2; + if (codec_drv->reg_access_default[index].reg == reg) + return index; + if (codec_drv->reg_access_default[index].reg < reg) + min = index + 1; + else + max = index; + } while (min <= max); + return -1; +} + +int snd_soc_default_volatile_register(struct snd_soc_codec *codec, + unsigned int reg) +{ + int index; + + if (reg >= codec->driver->reg_cache_size) + return 1; + index = snd_soc_get_reg_access_index(codec, reg); + if (index < 0) + return 0; + return codec->driver->reg_access_default[index].vol; +} +EXPORT_SYMBOL_GPL(snd_soc_default_volatile_register); + +int snd_soc_default_readable_register(struct snd_soc_codec *codec, + unsigned int reg) +{ + int index; + + if (reg >= codec->driver->reg_cache_size) + return 1; + index = snd_soc_get_reg_access_index(codec, reg); + if (index < 0) + return 0; + return codec->driver->reg_access_default[index].read; +} +EXPORT_SYMBOL_GPL(snd_soc_default_readable_register); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index c3f6f1e72790..17efacdb248a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -48,7 +48,8 @@ static DEFINE_MUTEX(pcm_mutex); static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq); #ifdef CONFIG_DEBUG_FS -static struct dentry *debugfs_root; +struct dentry *snd_soc_debugfs_root; +EXPORT_SYMBOL_GPL(snd_soc_debugfs_root); #endif static DEFINE_MUTEX(client_mutex); @@ -57,8 +58,6 @@ static LIST_HEAD(dai_list); static LIST_HEAD(platform_list); static LIST_HEAD(codec_list); -static int snd_soc_register_card(struct snd_soc_card *card); -static int snd_soc_unregister_card(struct snd_soc_card *card); static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num); /* @@ -70,10 +69,73 @@ static int pmdown_time = 5000; module_param(pmdown_time, int, 0); MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)"); +/* returns the minimum number of bytes needed to represent + * a particular given value */ +static int min_bytes_needed(unsigned long val) +{ + int c = 0; + int i; + + for (i = (sizeof val * 8) - 1; i >= 0; --i, ++c) + if (val & (1UL << i)) + break; + c = (sizeof val * 8) - c; + if (!c || (c % 8)) + c = (c + 8) / 8; + else + c /= 8; + return c; +} + +/* fill buf which is 'len' bytes with a formatted + * string of the form 'reg: value\n' */ +static int format_register_str(struct snd_soc_codec *codec, + unsigned int reg, char *buf, size_t len) +{ + int wordsize = codec->driver->reg_word_size * 2; + int regsize = min_bytes_needed(codec->driver->reg_cache_size) * 2; + int ret; + char tmpbuf[len + 1]; + char regbuf[regsize + 1]; + + /* since tmpbuf is allocated on the stack, warn the callers if they + * try to abuse this function */ + WARN_ON(len > 63); + + /* +2 for ': ' and + 1 for '\n' */ + if (wordsize + regsize + 2 + 1 != len) + return -EINVAL; + + ret = snd_soc_read(codec , reg); + if (ret < 0) { + memset(regbuf, 'X', regsize); + regbuf[regsize] = '\0'; + } else { + snprintf(regbuf, regsize + 1, "%.*x", regsize, ret); + } + + /* prepare the buffer */ + snprintf(tmpbuf, len + 1, "%.*x: %s\n", wordsize, reg, regbuf); + /* copy it back to the caller without the '\0' */ + memcpy(buf, tmpbuf, len); + + return 0; +} + /* codec register dump */ -static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) +static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf, + size_t count, loff_t pos) { - int ret, i, step = 1, count = 0; + int i, step = 1; + int wordsize, regsize; + int len; + size_t total = 0; + loff_t p = 0; + + wordsize = codec->driver->reg_word_size * 2; + regsize = min_bytes_needed(codec->driver->reg_cache_size) * 2; + + len = wordsize + regsize + 2 + 1; if (!codec->driver->reg_cache_size) return 0; @@ -81,55 +143,37 @@ static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) if (codec->driver->reg_cache_step) step = codec->driver->reg_cache_step; - count += sprintf(buf, "%s registers\n", codec->name); for (i = 0; i < codec->driver->reg_cache_size; i += step) { - if (codec->driver->readable_register && !codec->driver->readable_register(i)) + if (codec->readable_register && !codec->readable_register(codec, i)) continue; - - count += sprintf(buf + count, "%2x: ", i); - if (count >= PAGE_SIZE - 1) - break; - if (codec->driver->display_register) { count += codec->driver->display_register(codec, buf + count, PAGE_SIZE - count, i); } else { - /* If the read fails it's almost certainly due to - * the register being volatile and the device being - * powered off. - */ - ret = snd_soc_read(codec, i); - if (ret >= 0) - count += snprintf(buf + count, - PAGE_SIZE - count, - "%4x", ret); - else - count += snprintf(buf + count, - PAGE_SIZE - count, - "<no data: %d>", ret); + /* only support larger than PAGE_SIZE bytes debugfs + * entries for the default case */ + if (p >= pos) { + if (total + len >= count - 1) + break; + format_register_str(codec, i, buf + total, len); + total += len; + } + p += len; } - - if (count >= PAGE_SIZE - 1) - break; - - count += snprintf(buf + count, PAGE_SIZE - count, "\n"); - if (count >= PAGE_SIZE - 1) - break; } - /* Truncate count; min() would cause a warning */ - if (count >= PAGE_SIZE) - count = PAGE_SIZE - 1; + total = min(total, count - 1); - return count; + return total; } + static ssize_t codec_reg_show(struct device *dev, struct device_attribute *attr, char *buf) { struct snd_soc_pcm_runtime *rtd = container_of(dev, struct snd_soc_pcm_runtime, dev); - return soc_codec_reg_show(rtd->codec, buf); + return soc_codec_reg_show(rtd->codec, buf, PAGE_SIZE, 0); } static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL); @@ -168,16 +212,28 @@ static int codec_reg_open_file(struct inode *inode, struct file *file) } static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) + size_t count, loff_t *ppos) { ssize_t ret; struct snd_soc_codec *codec = file->private_data; - char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + char *buf; + + if (*ppos < 0 || !count) + return -EINVAL; + + buf = kmalloc(count, GFP_KERNEL); if (!buf) return -ENOMEM; - ret = soc_codec_reg_show(codec, buf); - if (ret >= 0) - ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); + + ret = soc_codec_reg_show(codec, buf, count, *ppos); + if (ret >= 0) { + if (copy_to_user(user_buf, buf, ret)) { + kfree(buf); + return -EFAULT; + } + *ppos += ret; + } + kfree(buf); return ret; } @@ -209,6 +265,10 @@ static ssize_t codec_reg_write_file(struct file *file, start++; if (strict_strtoul(start, 16, &value)) return -EINVAL; + + /* Userspace has been fiddling around behind the kernel's back */ + add_taint(TAINT_USER); + snd_soc_write(codec, reg, value); return buf_size; } @@ -232,6 +292,11 @@ static void soc_init_codec_debugfs(struct snd_soc_codec *codec) return; } + debugfs_create_bool("cache_sync", 0444, codec->debugfs_codec_root, + &codec->cache_sync); + debugfs_create_bool("cache_only", 0444, codec->debugfs_codec_root, + &codec->cache_only); + codec->debugfs_reg = debugfs_create_file("codec_reg", 0644, codec->debugfs_codec_root, codec, &codec_reg_fops); @@ -356,7 +421,7 @@ static const struct file_operations platform_list_fops = { static void soc_init_card_debugfs(struct snd_soc_card *card) { card->debugfs_card_root = debugfs_create_dir(card->name, - debugfs_root); + snd_soc_debugfs_root); if (!card->debugfs_card_root) { dev_warn(card->dev, "ASoC: Failed to create codec debugfs directory\n"); @@ -435,20 +500,30 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream) struct snd_soc_dai *codec_dai = rtd->codec_dai; int ret; - if (codec_dai->driver->symmetric_rates || cpu_dai->driver->symmetric_rates || - rtd->dai_link->symmetric_rates) { - dev_dbg(&rtd->dev, "Symmetry forces %dHz rate\n", - rtd->rate); + if (!codec_dai->driver->symmetric_rates && + !cpu_dai->driver->symmetric_rates && + !rtd->dai_link->symmetric_rates) + return 0; - ret = snd_pcm_hw_constraint_minmax(substream->runtime, - SNDRV_PCM_HW_PARAM_RATE, - rtd->rate, - rtd->rate); - if (ret < 0) { - dev_err(&rtd->dev, - "Unable to apply rate symmetry constraint: %d\n", ret); - return ret; - } + /* This can happen if multiple streams are starting simultaneously - + * the second can need to get its constraints before the first has + * picked a rate. Complain and allow the application to carry on. + */ + if (!rtd->rate) { + dev_warn(&rtd->dev, + "Not enforcing symmetric_rates due to race\n"); + return 0; + } + + dev_dbg(&rtd->dev, "Symmetry forces %dHz rate\n", rtd->rate); + + ret = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, + rtd->rate, rtd->rate); + if (ret < 0) { + dev_err(&rtd->dev, + "Unable to apply rate symmetry constraint: %d\n", ret); + return ret; } return 0; @@ -962,12 +1037,11 @@ static struct snd_pcm_ops soc_pcm_ops = { .pointer = soc_pcm_pointer, }; -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP /* powers down audio subsystem for suspend */ -static int soc_suspend(struct device *dev) +int snd_soc_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct snd_soc_card *card = platform_get_drvdata(pdev); + struct snd_soc_card *card = dev_get_drvdata(dev); struct snd_soc_codec *codec; int i; @@ -1008,7 +1082,7 @@ static int soc_suspend(struct device *dev) } if (card->suspend_pre) - card->suspend_pre(pdev, PMSG_SUSPEND); + card->suspend_pre(card); for (i = 0; i < card->num_rtd; i++) { struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai; @@ -1075,10 +1149,11 @@ static int soc_suspend(struct device *dev) } if (card->suspend_post) - card->suspend_post(pdev, PMSG_SUSPEND); + card->suspend_post(card); return 0; } +EXPORT_SYMBOL_GPL(snd_soc_suspend); /* deferred resume work, so resume can complete before we finished * setting our codec back up, which can be very slow on I2C @@ -1087,7 +1162,6 @@ static void soc_resume_deferred(struct work_struct *work) { struct snd_soc_card *card = container_of(work, struct snd_soc_card, deferred_resume_work); - struct platform_device *pdev = to_platform_device(card->dev); struct snd_soc_codec *codec; int i; @@ -1101,7 +1175,7 @@ static void soc_resume_deferred(struct work_struct *work) snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D2); if (card->resume_pre) - card->resume_pre(pdev); + card->resume_pre(card); /* resume AC97 DAIs */ for (i = 0; i < card->num_rtd; i++) { @@ -1176,7 +1250,7 @@ static void soc_resume_deferred(struct work_struct *work) } if (card->resume_post) - card->resume_post(pdev); + card->resume_post(card); dev_dbg(card->dev, "resume work completed\n"); @@ -1185,10 +1259,9 @@ static void soc_resume_deferred(struct work_struct *work) } /* powers up audio subsystem after a suspend */ -static int soc_resume(struct device *dev) +int snd_soc_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct snd_soc_card *card = platform_get_drvdata(pdev); + struct snd_soc_card *card = dev_get_drvdata(dev); int i; /* AC97 devices might have other drivers hanging off them so @@ -1210,9 +1283,10 @@ static int soc_resume(struct device *dev) return 0; } +EXPORT_SYMBOL_GPL(snd_soc_resume); #else -#define soc_suspend NULL -#define soc_resume NULL +#define snd_soc_suspend NULL +#define snd_soc_resume NULL #endif static struct snd_soc_dai_ops null_dai_ops = { @@ -1400,31 +1474,44 @@ static int soc_probe_codec(struct snd_soc_card *card, struct snd_soc_codec *codec) { int ret = 0; + const struct snd_soc_codec_driver *driver = codec->driver; codec->card = card; codec->dapm.card = card; soc_set_name_prefix(card, codec); - if (codec->driver->probe) { - ret = codec->driver->probe(codec); + if (!try_module_get(codec->dev->driver->owner)) + return -ENODEV; + + if (driver->probe) { + ret = driver->probe(codec); if (ret < 0) { dev_err(codec->dev, "asoc: failed to probe CODEC %s: %d\n", codec->name, ret); - return ret; + goto err_probe; } } + if (driver->dapm_widgets) + snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets, + driver->num_dapm_widgets); + if (driver->dapm_routes) + snd_soc_dapm_add_routes(&codec->dapm, driver->dapm_routes, + driver->num_dapm_routes); + soc_init_codec_debugfs(codec); /* mark codec as probed and add to card codec list */ - if (!try_module_get(codec->dev->driver->owner)) - return -ENODEV; - codec->probed = 1; list_add(&codec->card_list, &card->codec_dev_list); list_add(&codec->dapm.list, &card->dapm_list); + return 0; + +err_probe: + module_put(codec->dev->driver->owner); + return ret; } @@ -1468,7 +1555,6 @@ static int soc_post_component_init(struct snd_soc_card *card, /* Make sure all DAPM widgets are instantiated */ snd_soc_dapm_new_widgets(&codec->dapm); - snd_soc_dapm_sync(&codec->dapm); /* register the rtd device */ rtd->codec = codec; @@ -1543,19 +1629,19 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num) /* probe the platform */ if (!platform->probed) { + if (!try_module_get(platform->dev->driver->owner)) + return -ENODEV; + if (platform->driver->probe) { ret = platform->driver->probe(platform); if (ret < 0) { printk(KERN_ERR "asoc: failed to probe platform %s\n", platform->name); + module_put(platform->dev->driver->owner); return ret; } } /* mark platform as probed and add to card platform list */ - - if (!try_module_get(platform->dev->driver->owner)) - return -ENODEV; - platform->probed = 1; list_add(&platform->card_list, &card->platform_dev_list); } @@ -1713,7 +1799,6 @@ static int snd_soc_init_codec_cache(struct snd_soc_codec *codec, static void snd_soc_instantiate_card(struct snd_soc_card *card) { - struct platform_device *pdev = to_platform_device(card->dev); struct snd_soc_codec *codec; struct snd_soc_codec_conf *codec_conf; enum snd_soc_compress_type compress_type; @@ -1740,6 +1825,8 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) list_for_each_entry(codec, &codec_list, list) { if (codec->cache_init) continue; + /* by default we don't override the compress_type */ + compress_type = 0; /* check to see if we need to override the compress_type */ for (i = 0; i < card->num_configs; ++i) { codec_conf = &card->codec_conf[i]; @@ -1750,18 +1837,6 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) break; } } - if (i == card->num_configs) { - /* no need to override the compress_type so - * go ahead and do the standard thing */ - ret = snd_soc_init_codec_cache(codec, 0); - if (ret < 0) { - mutex_unlock(&card->mutex); - return; - } - continue; - } - /* override the compress_type with the one supplied in - * the machine driver */ ret = snd_soc_init_codec_cache(codec, compress_type); if (ret < 0) { mutex_unlock(&card->mutex); @@ -1780,14 +1855,19 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) } card->snd_card->dev = card->dev; -#ifdef CONFIG_PM + card->dapm.bias_level = SND_SOC_BIAS_OFF; + card->dapm.dev = card->dev; + card->dapm.card = card; + list_add(&card->dapm.list, &card->dapm_list); + +#ifdef CONFIG_PM_SLEEP /* deferred resume work */ INIT_WORK(&card->deferred_resume_work, soc_resume_deferred); #endif /* initialise the sound card only once */ if (card->probe) { - ret = card->probe(pdev); + ret = card->probe(card); if (ret < 0) goto card_probe_error; } @@ -1810,11 +1890,37 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) } } + if (card->dapm_widgets) + snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets, + card->num_dapm_widgets); + if (card->dapm_routes) + snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes, + card->num_dapm_routes); + +#ifdef CONFIG_DEBUG_FS + card->dapm.debugfs_dapm = debugfs_create_dir("dapm", + card->debugfs_card_root); + if (!card->dapm.debugfs_dapm) + printk(KERN_WARNING + "Failed to create card DAPM debugfs directory\n"); + + snd_soc_dapm_debugfs_init(&card->dapm); +#endif + snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname), "%s", card->name); snprintf(card->snd_card->longname, sizeof(card->snd_card->longname), "%s", card->name); + if (card->late_probe) { + ret = card->late_probe(card); + if (ret < 0) { + dev_err(card->dev, "%s late_probe() failed: %d\n", + card->name, ret); + goto probe_aux_dev_err; + } + } + ret = snd_card_register(card->snd_card); if (ret < 0) { printk(KERN_ERR "asoc: failed to register soundcard for %s\n", card->name); @@ -1848,7 +1954,7 @@ probe_dai_err: card_probe_error: if (card->remove) - card->remove(pdev); + card->remove(card); snd_card_free(card->snd_card); @@ -1872,16 +1978,15 @@ static int soc_probe(struct platform_device *pdev) struct snd_soc_card *card = platform_get_drvdata(pdev); int ret = 0; + /* + * no card, so machine driver should be registering card + * we should not be here in that case so ret error + */ + if (!card) + return -EINVAL; + /* Bodge while we unpick instantiation */ card->dev = &pdev->dev; - INIT_LIST_HEAD(&card->dai_dev_list); - INIT_LIST_HEAD(&card->codec_dev_list); - INIT_LIST_HEAD(&card->platform_dev_list); - INIT_LIST_HEAD(&card->widgets); - INIT_LIST_HEAD(&card->paths); - INIT_LIST_HEAD(&card->dapm_list); - - soc_init_card_debugfs(card); ret = snd_soc_register_card(card); if (ret != 0) { @@ -1892,45 +1997,48 @@ static int soc_probe(struct platform_device *pdev) return 0; } -/* removes a socdev */ -static int soc_remove(struct platform_device *pdev) +static int soc_cleanup_card_resources(struct snd_soc_card *card) { - struct snd_soc_card *card = platform_get_drvdata(pdev); int i; - if (card->instantiated) { + /* make sure any delayed work runs */ + for (i = 0; i < card->num_rtd; i++) { + struct snd_soc_pcm_runtime *rtd = &card->rtd[i]; + flush_delayed_work_sync(&rtd->delayed_work); + } - /* make sure any delayed work runs */ - for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_pcm_runtime *rtd = &card->rtd[i]; - flush_delayed_work_sync(&rtd->delayed_work); - } + /* remove auxiliary devices */ + for (i = 0; i < card->num_aux_devs; i++) + soc_remove_aux_dev(card, i); - /* remove auxiliary devices */ - for (i = 0; i < card->num_aux_devs; i++) - soc_remove_aux_dev(card, i); + /* remove and free each DAI */ + for (i = 0; i < card->num_rtd; i++) + soc_remove_dai_link(card, i); - /* remove and free each DAI */ - for (i = 0; i < card->num_rtd; i++) - soc_remove_dai_link(card, i); + soc_cleanup_card_debugfs(card); - soc_cleanup_card_debugfs(card); + /* remove the card */ + if (card->remove) + card->remove(card); + + kfree(card->rtd); + snd_card_free(card->snd_card); + return 0; - /* remove the card */ - if (card->remove) - card->remove(pdev); +} + +/* removes a socdev */ +static int soc_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); - kfree(card->rtd); - snd_card_free(card->snd_card); - } snd_soc_unregister_card(card); return 0; } -static int soc_poweroff(struct device *dev) +int snd_soc_poweroff(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct snd_soc_card *card = platform_get_drvdata(pdev); + struct snd_soc_card *card = dev_get_drvdata(dev); int i; if (!card->instantiated) @@ -1947,11 +2055,12 @@ static int soc_poweroff(struct device *dev) return 0; } +EXPORT_SYMBOL_GPL(snd_soc_poweroff); -static const struct dev_pm_ops soc_pm_ops = { - .suspend = soc_suspend, - .resume = soc_resume, - .poweroff = soc_poweroff, +const struct dev_pm_ops snd_soc_pm_ops = { + .suspend = snd_soc_suspend, + .resume = snd_soc_resume, + .poweroff = snd_soc_poweroff, }; /* ASoC platform driver */ @@ -1959,7 +2068,7 @@ static struct platform_driver soc_driver = { .driver = { .name = "soc-audio", .owner = THIS_MODULE, - .pm = &soc_pm_ops, + .pm = &snd_soc_pm_ops, }, .probe = soc_probe, .remove = soc_remove, @@ -2029,10 +2138,11 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) * * Boolean function indiciating if a CODEC register is volatile. */ -int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg) +int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, + unsigned int reg) { - if (codec->driver->volatile_register) - return codec->driver->volatile_register(reg); + if (codec->volatile_register) + return codec->volatile_register(codec, reg); else return 0; } @@ -2129,19 +2239,27 @@ EXPORT_SYMBOL_GPL(snd_soc_write); * * Writes new register value. * - * Returns 1 for change else 0. + * Returns 1 for change, 0 for no change, or negative error code. */ int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg, unsigned int mask, unsigned int value) { int change; unsigned int old, new; + int ret; - old = snd_soc_read(codec, reg); + ret = snd_soc_read(codec, reg); + if (ret < 0) + return ret; + + old = ret; new = (old & ~mask) | value; change = old != new; - if (change) - snd_soc_write(codec, reg, new); + if (change) { + ret = snd_soc_write(codec, reg, new); + if (ret < 0) + return ret; + } return change; } @@ -2226,22 +2344,45 @@ EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams); * @_template: control template * @data: control private data * @long_name: control long name + * @prefix: control name prefix * * Create a new mixer control from a template control. * * Returns 0 for success, else error. */ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, - void *data, char *long_name) + void *data, char *long_name, + const char *prefix) { struct snd_kcontrol_new template; + struct snd_kcontrol *kcontrol; + char *name = NULL; + int name_len; memcpy(&template, _template, sizeof(template)); - if (long_name) - template.name = long_name; template.index = 0; - return snd_ctl_new1(&template, data); + if (!long_name) + long_name = template.name; + + if (prefix) { + name_len = strlen(long_name) + strlen(prefix) + 2; + name = kmalloc(name_len, GFP_ATOMIC); + if (!name) + return NULL; + + snprintf(name, name_len, "%s %s", prefix, long_name); + + template.name = name; + } else { + template.name = long_name; + } + + kcontrol = snd_ctl_new1(&template, data); + + kfree(name); + + return kcontrol; } EXPORT_SYMBOL_GPL(snd_soc_cnew); @@ -2260,22 +2401,16 @@ int snd_soc_add_controls(struct snd_soc_codec *codec, const struct snd_kcontrol_new *controls, int num_controls) { struct snd_card *card = codec->card->snd_card; - char prefixed_name[44], *name; int err, i; for (i = 0; i < num_controls; i++) { const struct snd_kcontrol_new *control = &controls[i]; - if (codec->name_prefix) { - snprintf(prefixed_name, sizeof(prefixed_name), "%s %s", - codec->name_prefix, control->name); - name = prefixed_name; - } else { - name = control->name; - } - err = snd_ctl_add(card, snd_soc_cnew(control, codec, name)); + err = snd_ctl_add(card, snd_soc_cnew(control, codec, + control->name, + codec->name_prefix)); if (err < 0) { dev_err(codec->dev, "%s: Failed to add %s: %d\n", - codec->name, name, err); + codec->name, control->name, err); return err; } } @@ -2956,12 +3091,34 @@ int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, { if (dai->driver && dai->driver->ops->set_sysclk) return dai->driver->ops->set_sysclk(dai, clk_id, freq, dir); + else if (dai->codec && dai->codec->driver->set_sysclk) + return dai->codec->driver->set_sysclk(dai->codec, clk_id, + freq, dir); else return -EINVAL; } EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk); /** + * snd_soc_codec_set_sysclk - configure CODEC system or master clock. + * @codec: CODEC + * @clk_id: DAI specific clock ID + * @freq: new clock frequency in Hz + * @dir: new clock direction - input/output. + * + * Configures the CODEC master (MCLK) or system (SYSCLK) clocking. + */ +int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id, + unsigned int freq, int dir) +{ + if (codec->driver->set_sysclk) + return codec->driver->set_sysclk(codec, clk_id, freq, dir); + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_soc_codec_set_sysclk); + +/** * snd_soc_dai_set_clkdiv - configure DAI clock dividers. * @dai: DAI * @div_id: DAI specific clock divider ID @@ -2997,11 +3154,35 @@ int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, if (dai->driver && dai->driver->ops->set_pll) return dai->driver->ops->set_pll(dai, pll_id, source, freq_in, freq_out); + else if (dai->codec && dai->codec->driver->set_pll) + return dai->codec->driver->set_pll(dai->codec, pll_id, source, + freq_in, freq_out); else return -EINVAL; } EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll); +/* + * snd_soc_codec_set_pll - configure codec PLL. + * @codec: CODEC + * @pll_id: DAI specific PLL ID + * @source: DAI specific source for the PLL + * @freq_in: PLL input clock frequency in Hz + * @freq_out: requested PLL output clock frequency in Hz + * + * Configures and enables PLL to generate output clock based on input clock. + */ +int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + if (codec->driver->set_pll) + return codec->driver->set_pll(codec, pll_id, source, + freq_in, freq_out); + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_soc_codec_set_pll); + /** * snd_soc_dai_set_fmt - configure DAI hardware audio format. * @dai: DAI @@ -3101,17 +3282,18 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute); * * @card: Card to register * - * Note that currently this is an internal only function: it will be - * exposed to machine drivers after further backporting of ASoC v2 - * registration APIs. */ -static int snd_soc_register_card(struct snd_soc_card *card) +int snd_soc_register_card(struct snd_soc_card *card) { int i; if (!card->name || !card->dev) return -EINVAL; + snd_soc_initialize_card_lists(card); + + soc_init_card_debugfs(card); + card->rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime) * (card->num_links + card->num_aux_devs), GFP_KERNEL); @@ -3135,18 +3317,18 @@ static int snd_soc_register_card(struct snd_soc_card *card) return 0; } +EXPORT_SYMBOL_GPL(snd_soc_register_card); /** * snd_soc_unregister_card - Unregister a card with the ASoC core * * @card: Card to unregister * - * Note that currently this is an internal only function: it will be - * exposed to machine drivers after further backporting of ASoC v2 - * registration APIs. */ -static int snd_soc_unregister_card(struct snd_soc_card *card) +int snd_soc_unregister_card(struct snd_soc_card *card) { + if (card->instantiated) + soc_cleanup_card_resources(card); mutex_lock(&client_mutex); list_del(&card->list); mutex_unlock(&client_mutex); @@ -3154,6 +3336,7 @@ static int snd_soc_unregister_card(struct snd_soc_card *card) return 0; } +EXPORT_SYMBOL_GPL(snd_soc_unregister_card); /* * Simplify DAI link configuration by removing ".-1" from device names @@ -3483,9 +3666,12 @@ int snd_soc_register_codec(struct device *dev, codec->write = codec_drv->write; codec->read = codec_drv->read; + codec->volatile_register = codec_drv->volatile_register; + codec->readable_register = codec_drv->readable_register; codec->dapm.bias_level = SND_SOC_BIAS_OFF; codec->dapm.dev = dev; codec->dapm.codec = codec; + codec->dapm.seq_notifier = codec_drv->seq_notifier; codec->dev = dev; codec->driver = codec_drv; codec->num_dai = num_dai; @@ -3494,20 +3680,30 @@ int snd_soc_register_codec(struct device *dev, /* allocate CODEC register cache */ if (codec_drv->reg_cache_size && codec_drv->reg_word_size) { reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; + codec->reg_size = reg_size; /* it is necessary to make a copy of the default register cache * because in the case of using a compression type that requires * the default register cache to be marked as __devinitconst the * kernel might have freed the array by the time we initialize * the cache. */ - codec->reg_def_copy = kmemdup(codec_drv->reg_cache_default, - reg_size, GFP_KERNEL); - if (!codec->reg_def_copy) { - ret = -ENOMEM; - goto fail; + if (codec_drv->reg_cache_default) { + codec->reg_def_copy = kmemdup(codec_drv->reg_cache_default, + reg_size, GFP_KERNEL); + if (!codec->reg_def_copy) { + ret = -ENOMEM; + goto fail; + } } } + if (codec_drv->reg_access_size && codec_drv->reg_access_default) { + if (!codec->volatile_register) + codec->volatile_register = snd_soc_default_volatile_register; + if (!codec->readable_register) + codec->readable_register = snd_soc_default_readable_register; + } + for (i = 0; i < num_dai; i++) { fixup_codec_formats(&dai_drv[i].playback); fixup_codec_formats(&dai_drv[i].capture); @@ -3574,22 +3770,22 @@ EXPORT_SYMBOL_GPL(snd_soc_unregister_codec); static int __init snd_soc_init(void) { #ifdef CONFIG_DEBUG_FS - debugfs_root = debugfs_create_dir("asoc", NULL); - if (IS_ERR(debugfs_root) || !debugfs_root) { + snd_soc_debugfs_root = debugfs_create_dir("asoc", NULL); + if (IS_ERR(snd_soc_debugfs_root) || !snd_soc_debugfs_root) { printk(KERN_WARNING "ASoC: Failed to create debugfs directory\n"); - debugfs_root = NULL; + snd_soc_debugfs_root = NULL; } - if (!debugfs_create_file("codecs", 0444, debugfs_root, NULL, + if (!debugfs_create_file("codecs", 0444, snd_soc_debugfs_root, NULL, &codec_list_fops)) pr_warn("ASoC: Failed to create CODEC list debugfs file\n"); - if (!debugfs_create_file("dais", 0444, debugfs_root, NULL, + if (!debugfs_create_file("dais", 0444, snd_soc_debugfs_root, NULL, &dai_list_fops)) pr_warn("ASoC: Failed to create DAI list debugfs file\n"); - if (!debugfs_create_file("platforms", 0444, debugfs_root, NULL, + if (!debugfs_create_file("platforms", 0444, snd_soc_debugfs_root, NULL, &platform_list_fops)) pr_warn("ASoC: Failed to create platform list debugfs file\n"); #endif @@ -3601,7 +3797,7 @@ module_init(snd_soc_init); static void __exit snd_soc_exit(void) { #ifdef CONFIG_DEBUG_FS - debugfs_remove_recursive(debugfs_root); + debugfs_remove_recursive(snd_soc_debugfs_root); #endif platform_driver_unregister(&soc_driver); } diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 1790f83ee665..81c4052c127c 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -32,6 +32,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> +#include <linux/async.h> #include <linux/delay.h> #include <linux/pm.h> #include <linux/bitops.h> @@ -125,17 +126,17 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( /** * snd_soc_dapm_set_bias_level - set the bias level for the system - * @card: audio device + * @dapm: DAPM context * @level: level to configure * * Configure the bias (power) levels for the SoC audio device. * * Returns 0 for success else error. */ -static int snd_soc_dapm_set_bias_level(struct snd_soc_card *card, - struct snd_soc_dapm_context *dapm, +static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) { + struct snd_soc_card *card = dapm->card; int ret = 0; switch (level) { @@ -365,9 +366,20 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_widget *w) { int i, ret = 0; - size_t name_len; + size_t name_len, prefix_len; struct snd_soc_dapm_path *path; - struct snd_card *card = dapm->codec->card->snd_card; + struct snd_card *card = dapm->card->snd_card; + const char *prefix; + + if (dapm->codec) + prefix = dapm->codec->name_prefix; + else + prefix = NULL; + + if (prefix) + prefix_len = strlen(prefix) + 1; + else + prefix_len = 0; /* add kcontrol */ for (i = 0; i < w->num_kcontrols; i++) { @@ -396,8 +408,15 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm, switch (w->id) { default: + /* The control will get a prefix from + * the control creation process but + * we're also using the same prefix + * for widgets so cut the prefix off + * the front of the widget name. + */ snprintf(path->long_name, name_len, "%s %s", - w->name, w->kcontrols[i].name); + w->name + prefix_len, + w->kcontrols[i].name); break; case snd_soc_dapm_mixer_named_ctl: snprintf(path->long_name, name_len, "%s", @@ -408,7 +427,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm, path->long_name[name_len - 1] = '\0'; path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w, - path->long_name); + path->long_name, prefix); ret = snd_ctl_add(card, path->kcontrol); if (ret < 0) { dev_err(dapm->dev, @@ -429,7 +448,9 @@ static int dapm_new_mux(struct snd_soc_dapm_context *dapm, { struct snd_soc_dapm_path *path = NULL; struct snd_kcontrol *kcontrol; - struct snd_card *card = dapm->codec->card->snd_card; + struct snd_card *card = dapm->card->snd_card; + const char *prefix; + size_t prefix_len; int ret = 0; if (!w->num_kcontrols) { @@ -437,7 +458,22 @@ static int dapm_new_mux(struct snd_soc_dapm_context *dapm, return -EINVAL; } - kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name); + if (dapm->codec) + prefix = dapm->codec->name_prefix; + else + prefix = NULL; + + if (prefix) + prefix_len = strlen(prefix) + 1; + else + prefix_len = 0; + + /* The control will get a prefix from the control creation + * process but we're also using the same prefix for widgets so + * cut the prefix off the front of the widget name. + */ + kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name + prefix_len, + prefix); ret = snd_ctl_add(card, kcontrol); if (ret < 0) @@ -479,7 +515,7 @@ static inline void dapm_clear_walk(struct snd_soc_dapm_context *dapm) */ static int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget) { - int level = snd_power_get_state(widget->dapm->codec->card->snd_card); + int level = snd_power_get_state(widget->dapm->card->snd_card); switch (level) { case SNDRV_CTL_POWER_D3hot: @@ -734,10 +770,23 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) static int dapm_seq_compare(struct snd_soc_dapm_widget *a, struct snd_soc_dapm_widget *b, - int sort[]) + bool power_up) { + int *sort; + + if (power_up) + sort = dapm_up_seq; + else + sort = dapm_down_seq; + if (sort[a->id] != sort[b->id]) return sort[a->id] - sort[b->id]; + if (a->subseq != b->subseq) { + if (power_up) + return a->subseq - b->subseq; + else + return b->subseq - a->subseq; + } if (a->reg != b->reg) return a->reg - b->reg; if (a->dapm != b->dapm) @@ -749,12 +798,12 @@ static int dapm_seq_compare(struct snd_soc_dapm_widget *a, /* Insert a widget in order into a DAPM power sequence. */ static void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget, struct list_head *list, - int sort[]) + bool power_up) { struct snd_soc_dapm_widget *w; list_for_each_entry(w, list, power_list) - if (dapm_seq_compare(new_widget, w, sort) < 0) { + if (dapm_seq_compare(new_widget, w, power_up) < 0) { list_add_tail(&new_widget->power_list, &w->power_list); return; } @@ -865,26 +914,42 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm, * handled. */ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, - struct list_head *list, int event, int sort[]) + struct list_head *list, int event, bool power_up) { struct snd_soc_dapm_widget *w, *n; LIST_HEAD(pending); int cur_sort = -1; + int cur_subseq = -1; int cur_reg = SND_SOC_NOPM; struct snd_soc_dapm_context *cur_dapm = NULL; - int ret; + int ret, i; + int *sort; + + if (power_up) + sort = dapm_up_seq; + else + sort = dapm_down_seq; list_for_each_entry_safe(w, n, list, power_list) { ret = 0; /* Do we need to apply any queued changes? */ if (sort[w->id] != cur_sort || w->reg != cur_reg || - w->dapm != cur_dapm) { + w->dapm != cur_dapm || w->subseq != cur_subseq) { if (!list_empty(&pending)) dapm_seq_run_coalesced(cur_dapm, &pending); + if (cur_dapm && cur_dapm->seq_notifier) { + for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) + if (sort[i] == cur_sort) + cur_dapm->seq_notifier(cur_dapm, + i, + cur_subseq); + } + INIT_LIST_HEAD(&pending); cur_sort = -1; + cur_subseq = -1; cur_reg = SND_SOC_NOPM; cur_dapm = NULL; } @@ -929,6 +994,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, default: /* Queue it up for application */ cur_sort = sort[w->id]; + cur_subseq = w->subseq; cur_reg = w->reg; cur_dapm = w->dapm; list_move(&w->power_list, &pending); @@ -942,6 +1008,13 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, if (!list_empty(&pending)) dapm_seq_run_coalesced(cur_dapm, &pending); + + if (cur_dapm && cur_dapm->seq_notifier) { + for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) + if (sort[i] == cur_sort) + cur_dapm->seq_notifier(cur_dapm, + i, cur_subseq); + } } static void dapm_widget_update(struct snd_soc_dapm_context *dapm) @@ -977,7 +1050,62 @@ static void dapm_widget_update(struct snd_soc_dapm_context *dapm) } } +/* Async callback run prior to DAPM sequences - brings to _PREPARE if + * they're changing state. + */ +static void dapm_pre_sequence_async(void *data, async_cookie_t cookie) +{ + struct snd_soc_dapm_context *d = data; + int ret; + if (d->dev_power && d->bias_level == SND_SOC_BIAS_OFF) { + ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY); + if (ret != 0) + dev_err(d->dev, + "Failed to turn on bias: %d\n", ret); + } + + /* If we're changing to all on or all off then prepare */ + if ((d->dev_power && d->bias_level == SND_SOC_BIAS_STANDBY) || + (!d->dev_power && d->bias_level == SND_SOC_BIAS_ON)) { + ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_PREPARE); + if (ret != 0) + dev_err(d->dev, + "Failed to prepare bias: %d\n", ret); + } +} + +/* Async callback run prior to DAPM sequences - brings to their final + * state. + */ +static void dapm_post_sequence_async(void *data, async_cookie_t cookie) +{ + struct snd_soc_dapm_context *d = data; + int ret; + + /* If we just powered the last thing off drop to standby bias */ + if (d->bias_level == SND_SOC_BIAS_PREPARE && !d->dev_power) { + ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY); + if (ret != 0) + dev_err(d->dev, "Failed to apply standby bias: %d\n", + ret); + } + + /* If we're in standby and can support bias off then do that */ + if (d->bias_level == SND_SOC_BIAS_STANDBY && d->idle_bias_off) { + ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_OFF); + if (ret != 0) + dev_err(d->dev, "Failed to turn off bias: %d\n", ret); + } + + /* If we just powered up then move to active bias */ + if (d->bias_level == SND_SOC_BIAS_PREPARE && d->dev_power) { + ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_ON); + if (ret != 0) + dev_err(d->dev, "Failed to apply active bias: %d\n", + ret); + } +} /* * Scan each dapm widget for complete audio path. @@ -990,12 +1118,12 @@ static void dapm_widget_update(struct snd_soc_dapm_context *dapm) */ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) { - struct snd_soc_card *card = dapm->codec->card; + struct snd_soc_card *card = dapm->card; struct snd_soc_dapm_widget *w; struct snd_soc_dapm_context *d; LIST_HEAD(up_list); LIST_HEAD(down_list); - int ret = 0; + LIST_HEAD(async_domain); int power; trace_snd_soc_dapm_start(card); @@ -1010,10 +1138,10 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) list_for_each_entry(w, &card->widgets, list) { switch (w->id) { case snd_soc_dapm_pre: - dapm_seq_insert(w, &down_list, dapm_down_seq); + dapm_seq_insert(w, &down_list, false); break; case snd_soc_dapm_post: - dapm_seq_insert(w, &up_list, dapm_up_seq); + dapm_seq_insert(w, &up_list, true); break; default: @@ -1033,9 +1161,9 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) trace_snd_soc_dapm_widget_power(w, power); if (power) - dapm_seq_insert(w, &up_list, dapm_up_seq); + dapm_seq_insert(w, &up_list, true); else - dapm_seq_insert(w, &down_list, dapm_down_seq); + dapm_seq_insert(w, &down_list, false); w->power = power; break; @@ -1073,65 +1201,25 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) } } - list_for_each_entry(d, &dapm->card->dapm_list, list) { - if (d->dev_power && d->bias_level == SND_SOC_BIAS_OFF) { - ret = snd_soc_dapm_set_bias_level(card, d, - SND_SOC_BIAS_STANDBY); - if (ret != 0) - dev_err(d->dev, - "Failed to turn on bias: %d\n", ret); - } - - /* If we're changing to all on or all off then prepare */ - if ((d->dev_power && d->bias_level == SND_SOC_BIAS_STANDBY) || - (!d->dev_power && d->bias_level == SND_SOC_BIAS_ON)) { - ret = snd_soc_dapm_set_bias_level(card, d, - SND_SOC_BIAS_PREPARE); - if (ret != 0) - dev_err(d->dev, - "Failed to prepare bias: %d\n", ret); - } - } + /* Run all the bias changes in parallel */ + list_for_each_entry(d, &dapm->card->dapm_list, list) + async_schedule_domain(dapm_pre_sequence_async, d, + &async_domain); + async_synchronize_full_domain(&async_domain); /* Power down widgets first; try to avoid amplifying pops. */ - dapm_seq_run(dapm, &down_list, event, dapm_down_seq); + dapm_seq_run(dapm, &down_list, event, false); dapm_widget_update(dapm); /* Now power up. */ - dapm_seq_run(dapm, &up_list, event, dapm_up_seq); - - list_for_each_entry(d, &dapm->card->dapm_list, list) { - /* If we just powered the last thing off drop to standby bias */ - if (d->bias_level == SND_SOC_BIAS_PREPARE && !d->dev_power) { - ret = snd_soc_dapm_set_bias_level(card, d, - SND_SOC_BIAS_STANDBY); - if (ret != 0) - dev_err(d->dev, - "Failed to apply standby bias: %d\n", - ret); - } + dapm_seq_run(dapm, &up_list, event, true); - /* If we're in standby and can support bias off then do that */ - if (d->bias_level == SND_SOC_BIAS_STANDBY && - d->idle_bias_off) { - ret = snd_soc_dapm_set_bias_level(card, d, - SND_SOC_BIAS_OFF); - if (ret != 0) - dev_err(d->dev, - "Failed to turn off bias: %d\n", ret); - } - - /* If we just powered up then move to active bias */ - if (d->bias_level == SND_SOC_BIAS_PREPARE && d->dev_power) { - ret = snd_soc_dapm_set_bias_level(card, d, - SND_SOC_BIAS_ON); - if (ret != 0) - dev_err(d->dev, - "Failed to apply active bias: %d\n", - ret); - } - } + /* Run all the bias changes in parallel */ + list_for_each_entry(d, &dapm->card->dapm_list, list) + async_schedule_domain(dapm_post_sequence_async, d, + &async_domain); + async_synchronize_full_domain(&async_domain); pop_dbg(dapm->dev, card->pop_time, "DAPM sequencing finished, waiting %dms\n", card->pop_time); @@ -1189,7 +1277,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file, if (p->connect) ret += snprintf(buf + ret, PAGE_SIZE - ret, - " in %s %s\n", + " in \"%s\" \"%s\"\n", p->name ? p->name : "static", p->source->name); } @@ -1199,7 +1287,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file, if (p->connect) ret += snprintf(buf + ret, PAGE_SIZE - ret, - " out %s %s\n", + " out \"%s\" \"%s\"\n", p->name ? p->name : "static", p->sink->name); } @@ -1464,7 +1552,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, char prefixed_source[80]; int ret = 0; - if (dapm->codec->name_prefix) { + if (dapm->codec && dapm->codec->name_prefix) { snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s", dapm->codec->name_prefix, route->sink); sink = prefixed_sink; @@ -2114,14 +2202,14 @@ int snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, return -ENOMEM; name_len = strlen(widget->name) + 1; - if (dapm->codec->name_prefix) + if (dapm->codec && dapm->codec->name_prefix) name_len += 1 + strlen(dapm->codec->name_prefix); w->name = kmalloc(name_len, GFP_KERNEL); if (w->name == NULL) { kfree(w); return -ENOMEM; } - if (dapm->codec->name_prefix) + if (dapm->codec && dapm->codec->name_prefix) snprintf(w->name, name_len, "%s %s", dapm->codec->name_prefix, widget->name); else @@ -2226,7 +2314,6 @@ int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, mutex_unlock(&codec->mutex); return 0; } -EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event); /** * snd_soc_dapm_enable_pin - enable pin. @@ -2393,7 +2480,7 @@ static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm) if (w->dapm != dapm) continue; if (w->power) { - dapm_seq_insert(w, &down_list, dapm_down_seq); + dapm_seq_insert(w, &down_list, false); w->power = 0; powerdown = 1; } @@ -2403,9 +2490,9 @@ static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm) * standby. */ if (powerdown) { - snd_soc_dapm_set_bias_level(NULL, dapm, SND_SOC_BIAS_PREPARE); - dapm_seq_run(dapm, &down_list, 0, dapm_down_seq); - snd_soc_dapm_set_bias_level(NULL, dapm, SND_SOC_BIAS_STANDBY); + snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_PREPARE); + dapm_seq_run(dapm, &down_list, 0, false); + snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_STANDBY); } } @@ -2418,7 +2505,7 @@ void snd_soc_dapm_shutdown(struct snd_soc_card *card) list_for_each_entry(codec, &card->codec_dev_list, list) { soc_dapm_shutdown_codec(&codec->dapm); - snd_soc_dapm_set_bias_level(card, &codec->dapm, SND_SOC_BIAS_OFF); + snd_soc_dapm_set_bias_level(&codec->dapm, SND_SOC_BIAS_OFF); } } diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index ac5a5bc7375a..fcab80b36a37 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -37,6 +37,7 @@ int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type, { jack->codec = codec; INIT_LIST_HEAD(&jack->pins); + INIT_LIST_HEAD(&jack->jack_zones); BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier); return snd_jack_new(codec->card->snd_card, id, type, &jack->jack); @@ -100,7 +101,7 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) } /* Report before the DAPM sync to help users updating micbias status */ - blocking_notifier_call_chain(&jack->notifier, status, NULL); + blocking_notifier_call_chain(&jack->notifier, status, jack); snd_soc_dapm_sync(dapm); @@ -112,6 +113,51 @@ out: EXPORT_SYMBOL_GPL(snd_soc_jack_report); /** + * snd_soc_jack_add_zones - Associate voltage zones with jack + * + * @jack: ASoC jack + * @count: Number of zones + * @zone: Array of zones + * + * After this function has been called the zones specified in the + * array will be associated with the jack. + */ +int snd_soc_jack_add_zones(struct snd_soc_jack *jack, int count, + struct snd_soc_jack_zone *zones) +{ + int i; + + for (i = 0; i < count; i++) { + INIT_LIST_HEAD(&zones[i].list); + list_add(&(zones[i].list), &jack->jack_zones); + } + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_jack_add_zones); + +/** + * snd_soc_jack_get_type - Based on the mic bias value, this function returns + * the type of jack from the zones delcared in the jack type + * + * @micbias_voltage: mic bias voltage at adc channel when jack is plugged in + * + * Based on the mic bias value passed, this function helps identify + * the type of jack from the already delcared jack zones + */ +int snd_soc_jack_get_type(struct snd_soc_jack *jack, int micbias_voltage) +{ + struct snd_soc_jack_zone *zone; + + list_for_each_entry(zone, &jack->jack_zones, list) { + if (micbias_voltage >= zone->min_mv && + micbias_voltage < zone->max_mv) + return zone->jack_type; + } + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_jack_get_type); + +/** * snd_soc_jack_add_pins - Associate DAPM pins with an ASoC jack * * @jack: ASoC jack @@ -194,7 +240,7 @@ static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio) int enable; int report; - enable = gpio_get_value(gpio->gpio); + enable = gpio_get_value_cansleep(gpio->gpio); if (gpio->invert) enable = !enable; @@ -284,6 +330,14 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, if (ret) goto err; + if (gpios[i].wake) { + ret = set_irq_wake(gpio_to_irq(gpios[i].gpio), 1); + if (ret != 0) + printk(KERN_ERR + "Failed to mark GPIO %d as wake source: %d\n", + gpios[i].gpio, ret); + } + #ifdef CONFIG_GPIO_SYSFS /* Expose GPIO value over sysfs for diagnostic purposes */ gpio_export(gpios[i].gpio, false); diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index 1d07b931f3d8..3f45e6a439bf 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -28,26 +28,9 @@ int snd_soc_params_to_frame_size(struct snd_pcm_hw_params *params) { int sample_size; - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - case SNDRV_PCM_FORMAT_S16_BE: - sample_size = 16; - break; - case SNDRV_PCM_FORMAT_S20_3LE: - case SNDRV_PCM_FORMAT_S20_3BE: - sample_size = 20; - break; - case SNDRV_PCM_FORMAT_S24_LE: - case SNDRV_PCM_FORMAT_S24_BE: - sample_size = 24; - break; - case SNDRV_PCM_FORMAT_S32_LE: - case SNDRV_PCM_FORMAT_S32_BE: - sample_size = 32; - break; - default: - return -ENOTSUPP; - } + sample_size = snd_pcm_format_width(params_format(params)); + if (sample_size < 0) + return sample_size; return snd_soc_calc_frame_size(sample_size, params_channels(params), 1); diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig new file mode 100644 index 000000000000..66b504f06c23 --- /dev/null +++ b/sound/soc/tegra/Kconfig @@ -0,0 +1,26 @@ +config SND_TEGRA_SOC + tristate "SoC Audio for the Tegra System-on-Chip" + depends on ARCH_TEGRA && TEGRA_SYSTEM_DMA + default m + help + Say Y or M here if you want support for SoC audio on Tegra. + +config SND_TEGRA_SOC_I2S + tristate + depends on SND_TEGRA_SOC + default m + help + Say Y or M if you want to add support for codecs attached to the + Tegra I2S interface. You will also need to select the individual + machine drivers to support below. + +config SND_TEGRA_SOC_HARMONY + tristate "SoC Audio support for Tegra Harmony reference board" + depends on SND_TEGRA_SOC && MACH_HARMONY && I2C + default m + select SND_TEGRA_SOC_I2S + select SND_SOC_WM8903 + help + Say Y or M here if you want to add support for SoC audio on the + Tegra Harmony reference board. + diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile new file mode 100644 index 000000000000..fd183d3ab4f1 --- /dev/null +++ b/sound/soc/tegra/Makefile @@ -0,0 +1,15 @@ +# Tegra platform Support +snd-soc-tegra-das-objs := tegra_das.o +snd-soc-tegra-pcm-objs := tegra_pcm.o +snd-soc-tegra-i2s-objs := tegra_i2s.o +snd-soc-tegra-utils-objs += tegra_asoc_utils.o + +obj-$(CONFIG_SND_TEGRA_SOC) += snd-soc-tegra-utils.o +obj-$(CONFIG_SND_TEGRA_SOC) += snd-soc-tegra-das.o +obj-$(CONFIG_SND_TEGRA_SOC) += snd-soc-tegra-pcm.o +obj-$(CONFIG_SND_TEGRA_SOC_I2S) += snd-soc-tegra-i2s.o + +# Tegra machine Support +snd-soc-tegra-harmony-objs := harmony.o + +obj-$(CONFIG_SND_TEGRA_SOC_HARMONY) += snd-soc-tegra-harmony.o diff --git a/sound/soc/tegra/harmony.c b/sound/soc/tegra/harmony.c new file mode 100644 index 000000000000..8585957477eb --- /dev/null +++ b/sound/soc/tegra/harmony.c @@ -0,0 +1,393 @@ +/* + * harmony.c - Harmony machine ASoC driver + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (C) 2010-2011 - NVIDIA, Inc. + * + * Based on code copyright/by: + * + * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd. + * + * 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. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <asm/mach-types.h> + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/gpio.h> + +#include <mach/harmony_audio.h> + +#include <sound/core.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "../codecs/wm8903.h" + +#include "tegra_das.h" +#include "tegra_i2s.h" +#include "tegra_pcm.h" +#include "tegra_asoc_utils.h" + +#define DRV_NAME "tegra-snd-harmony" + +#define GPIO_SPKR_EN BIT(0) +#define GPIO_INT_MIC_EN BIT(1) +#define GPIO_EXT_MIC_EN BIT(2) + +struct tegra_harmony { + struct tegra_asoc_utils_data util_data; + struct harmony_audio_platform_data *pdata; + int gpio_requested; +}; + +static int harmony_asoc_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_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_card *card = codec->card; + struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card); + int srate, mclk, mclk_change; + int err; + + srate = params_rate(params); + switch (srate) { + case 64000: + case 88200: + case 96000: + mclk = 128 * srate; + break; + default: + mclk = 256 * srate; + break; + } + /* FIXME: Codec only requires >= 3MHz if OSR==0 */ + while (mclk < 6000000) + mclk *= 2; + + err = tegra_asoc_utils_set_rate(&harmony->util_data, srate, mclk, + &mclk_change); + if (err < 0) { + dev_err(card->dev, "Can't configure clocks\n"); + return err; + } + + err = snd_soc_dai_set_fmt(codec_dai, + SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS); + if (err < 0) { + dev_err(card->dev, "codec_dai fmt not set\n"); + return err; + } + + err = snd_soc_dai_set_fmt(cpu_dai, + SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS); + if (err < 0) { + dev_err(card->dev, "cpu_dai fmt not set\n"); + return err; + } + + if (mclk_change) { + err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, + SND_SOC_CLOCK_IN); + if (err < 0) { + dev_err(card->dev, "codec_dai clock not set\n"); + return err; + } + } + + return 0; +} + +static struct snd_soc_ops harmony_asoc_ops = { + .hw_params = harmony_asoc_hw_params, +}; + +static struct snd_soc_jack harmony_hp_jack; + +static struct snd_soc_jack_pin harmony_hp_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, +}; + +static struct snd_soc_jack_gpio harmony_hp_jack_gpios[] = { + { + .name = "headphone detect", + .report = SND_JACK_HEADPHONE, + .debounce_time = 150, + .invert = 1, + } +}; + +static struct snd_soc_jack harmony_mic_jack; + +static struct snd_soc_jack_pin harmony_mic_jack_pins[] = { + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static int harmony_event_int_spk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct snd_soc_card *card = codec->card; + struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card); + struct harmony_audio_platform_data *pdata = harmony->pdata; + + gpio_set_value_cansleep(pdata->gpio_spkr_en, + SND_SOC_DAPM_EVENT_ON(event)); + + return 0; +} + +static const struct snd_soc_dapm_widget harmony_dapm_widgets[] = { + SND_SOC_DAPM_SPK("Int Spk", harmony_event_int_spk), + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), +}; + +static const struct snd_soc_dapm_route harmony_audio_map[] = { + {"Headphone Jack", NULL, "HPOUTR"}, + {"Headphone Jack", NULL, "HPOUTL"}, + {"Int Spk", NULL, "ROP"}, + {"Int Spk", NULL, "RON"}, + {"Int Spk", NULL, "LOP"}, + {"Int Spk", NULL, "LON"}, + {"Mic Bias", NULL, "Mic Jack"}, + {"IN1L", NULL, "Mic Bias"}, +}; + +static const struct snd_kcontrol_new harmony_controls[] = { + SOC_DAPM_PIN_SWITCH("Int Spk"), +}; + +static int harmony_asoc_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_card *card = codec->card; + struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card); + struct harmony_audio_platform_data *pdata = harmony->pdata; + int ret; + + ret = gpio_request(pdata->gpio_spkr_en, "spkr_en"); + if (ret) { + dev_err(card->dev, "cannot get spkr_en gpio\n"); + return ret; + } + harmony->gpio_requested |= GPIO_SPKR_EN; + + gpio_direction_output(pdata->gpio_spkr_en, 0); + + ret = gpio_request(pdata->gpio_int_mic_en, "int_mic_en"); + if (ret) { + dev_err(card->dev, "cannot get int_mic_en gpio\n"); + return ret; + } + harmony->gpio_requested |= GPIO_INT_MIC_EN; + + /* Disable int mic; enable signal is active-high */ + gpio_direction_output(pdata->gpio_int_mic_en, 0); + + ret = gpio_request(pdata->gpio_ext_mic_en, "ext_mic_en"); + if (ret) { + dev_err(card->dev, "cannot get ext_mic_en gpio\n"); + return ret; + } + harmony->gpio_requested |= GPIO_EXT_MIC_EN; + + /* Enable ext mic; enable signal is active-low */ + gpio_direction_output(pdata->gpio_ext_mic_en, 0); + + ret = snd_soc_add_controls(codec, harmony_controls, + ARRAY_SIZE(harmony_controls)); + if (ret < 0) + return ret; + + snd_soc_dapm_new_controls(dapm, harmony_dapm_widgets, + ARRAY_SIZE(harmony_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, harmony_audio_map, + ARRAY_SIZE(harmony_audio_map)); + + harmony_hp_jack_gpios[0].gpio = pdata->gpio_hp_det; + snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE, + &harmony_hp_jack); + snd_soc_jack_add_pins(&harmony_hp_jack, + ARRAY_SIZE(harmony_hp_jack_pins), + harmony_hp_jack_pins); + snd_soc_jack_add_gpios(&harmony_hp_jack, + ARRAY_SIZE(harmony_hp_jack_gpios), + harmony_hp_jack_gpios); + + snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE, + &harmony_mic_jack); + snd_soc_jack_add_pins(&harmony_mic_jack, + ARRAY_SIZE(harmony_mic_jack_pins), + harmony_mic_jack_pins); + wm8903_mic_detect(codec, &harmony_mic_jack, SND_JACK_MICROPHONE, 0); + + snd_soc_dapm_force_enable_pin(dapm, "Mic Bias"); + + snd_soc_dapm_nc_pin(dapm, "IN3L"); + snd_soc_dapm_nc_pin(dapm, "IN3R"); + snd_soc_dapm_nc_pin(dapm, "LINEOUTL"); + snd_soc_dapm_nc_pin(dapm, "LINEOUTR"); + + snd_soc_dapm_sync(dapm); + + return 0; +} + +static struct snd_soc_dai_link harmony_wm8903_dai = { + .name = "WM8903", + .stream_name = "WM8903 PCM", + .codec_name = "wm8903.0-001a", + .platform_name = "tegra-pcm-audio", + .cpu_dai_name = "tegra-i2s.0", + .codec_dai_name = "wm8903-hifi", + .init = harmony_asoc_init, + .ops = &harmony_asoc_ops, +}; + +static struct snd_soc_card snd_soc_harmony = { + .name = "tegra-harmony", + .dai_link = &harmony_wm8903_dai, + .num_links = 1, +}; + +static __devinit int tegra_snd_harmony_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &snd_soc_harmony; + struct tegra_harmony *harmony; + struct harmony_audio_platform_data *pdata; + int ret; + + if (!machine_is_harmony()) { + dev_err(&pdev->dev, "Not running on Tegra Harmony!\n"); + return -ENODEV; + } + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "no platform data supplied\n"); + return -EINVAL; + } + + harmony = kzalloc(sizeof(struct tegra_harmony), GFP_KERNEL); + if (!harmony) { + dev_err(&pdev->dev, "Can't allocate tegra_harmony\n"); + return -ENOMEM; + } + + harmony->pdata = pdata; + + ret = tegra_asoc_utils_init(&harmony->util_data, &pdev->dev); + if (ret) + goto err_free_harmony; + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, harmony); + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto err_clear_drvdata; + } + + return 0; + +err_clear_drvdata: + snd_soc_card_set_drvdata(card, NULL); + platform_set_drvdata(pdev, NULL); + card->dev = NULL; + tegra_asoc_utils_fini(&harmony->util_data); +err_free_harmony: + kfree(harmony); + return ret; +} + +static int __devexit tegra_snd_harmony_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct tegra_harmony *harmony = snd_soc_card_get_drvdata(card); + struct harmony_audio_platform_data *pdata = harmony->pdata; + + snd_soc_unregister_card(card); + + snd_soc_card_set_drvdata(card, NULL); + platform_set_drvdata(pdev, NULL); + card->dev = NULL; + + tegra_asoc_utils_fini(&harmony->util_data); + + if (harmony->gpio_requested & GPIO_EXT_MIC_EN) + gpio_free(pdata->gpio_ext_mic_en); + if (harmony->gpio_requested & GPIO_INT_MIC_EN) + gpio_free(pdata->gpio_int_mic_en); + if (harmony->gpio_requested & GPIO_SPKR_EN) + gpio_free(pdata->gpio_spkr_en); + + kfree(harmony); + + return 0; +} + +static struct platform_driver tegra_snd_harmony_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = tegra_snd_harmony_probe, + .remove = __devexit_p(tegra_snd_harmony_remove), +}; + +static int __init snd_tegra_harmony_init(void) +{ + return platform_driver_register(&tegra_snd_harmony_driver); +} +module_init(snd_tegra_harmony_init); + +static void __exit snd_tegra_harmony_exit(void) +{ + platform_driver_unregister(&tegra_snd_harmony_driver); +} +module_exit(snd_tegra_harmony_exit); + +MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); +MODULE_DESCRIPTION("Harmony machine ASoC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c new file mode 100644 index 000000000000..52f0a3f9ce40 --- /dev/null +++ b/sound/soc/tegra/tegra_asoc_utils.c @@ -0,0 +1,155 @@ +/* + * tegra_asoc_utils.c - Harmony machine ASoC driver + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (C) 2010 - NVIDIA, Inc. + * + * 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. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/kernel.h> + +#include "tegra_asoc_utils.h" + +int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, + int mclk, int *mclk_change) +{ + int new_baseclock; + int err; + + switch (srate) { + case 11025: + case 22050: + case 44100: + case 88200: + new_baseclock = 56448000; + break; + case 8000: + case 16000: + case 32000: + case 48000: + case 64000: + case 96000: + new_baseclock = 73728000; + break; + default: + return -EINVAL; + } + + *mclk_change = ((new_baseclock != data->set_baseclock) || + (mclk != data->set_mclk)); + if (!*mclk_change) + return 0; + + data->set_baseclock = 0; + data->set_mclk = 0; + + clk_disable(data->clk_cdev1); + clk_disable(data->clk_pll_a_out0); + clk_disable(data->clk_pll_a); + + err = clk_set_rate(data->clk_pll_a, new_baseclock); + if (err) { + dev_err(data->dev, "Can't set pll_a rate: %d\n", err); + return err; + } + + err = clk_set_rate(data->clk_pll_a_out0, mclk); + if (err) { + dev_err(data->dev, "Can't set pll_a_out0 rate: %d\n", err); + return err; + } + + /* Don't set cdev1 rate; its locked to pll_a_out0 */ + + err = clk_enable(data->clk_pll_a); + if (err) { + dev_err(data->dev, "Can't enable pll_a: %d\n", err); + return err; + } + + err = clk_enable(data->clk_pll_a_out0); + if (err) { + dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err); + return err; + } + + err = clk_enable(data->clk_cdev1); + if (err) { + dev_err(data->dev, "Can't enable cdev1: %d\n", err); + return err; + } + + data->set_baseclock = new_baseclock; + data->set_mclk = mclk; + + return 0; +} +EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_rate); + +int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, + struct device *dev) +{ + int ret; + + data->dev = dev; + + data->clk_pll_a = clk_get_sys(NULL, "pll_a"); + if (IS_ERR(data->clk_pll_a)) { + dev_err(data->dev, "Can't retrieve clk pll_a\n"); + ret = PTR_ERR(data->clk_pll_a); + goto err; + } + + data->clk_pll_a_out0 = clk_get_sys(NULL, "pll_a_out0"); + if (IS_ERR(data->clk_pll_a_out0)) { + dev_err(data->dev, "Can't retrieve clk pll_a_out0\n"); + ret = PTR_ERR(data->clk_pll_a_out0); + goto err_put_pll_a; + } + + data->clk_cdev1 = clk_get_sys(NULL, "cdev1"); + if (IS_ERR(data->clk_cdev1)) { + dev_err(data->dev, "Can't retrieve clk cdev1\n"); + ret = PTR_ERR(data->clk_cdev1); + goto err_put_pll_a_out0; + } + + return 0; + +err_put_pll_a_out0: + clk_put(data->clk_pll_a_out0); +err_put_pll_a: + clk_put(data->clk_pll_a); +err: + return ret; +} +EXPORT_SYMBOL_GPL(tegra_asoc_utils_init); + +void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data) +{ + clk_put(data->clk_cdev1); + clk_put(data->clk_pll_a_out0); + clk_put(data->clk_pll_a); +} +EXPORT_SYMBOL_GPL(tegra_asoc_utils_fini); + +MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); +MODULE_DESCRIPTION("Tegra ASoC utility code"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/tegra/tegra_asoc_utils.h b/sound/soc/tegra/tegra_asoc_utils.h new file mode 100644 index 000000000000..bbba7afdfc2c --- /dev/null +++ b/sound/soc/tegra/tegra_asoc_utils.h @@ -0,0 +1,45 @@ +/* + * tegra_asoc_utils.h - Definitions for Tegra DAS driver + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (C) 2010 - NVIDIA, Inc. + * + * 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. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __TEGRA_ASOC_UTILS_H__ +#define __TEGRA_ASOC_UTILS_H_ + +struct clk; +struct device; + +struct tegra_asoc_utils_data { + struct device *dev; + struct clk *clk_pll_a; + struct clk *clk_pll_a_out0; + struct clk *clk_cdev1; + int set_baseclock; + int set_mclk; +}; + +int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, + int mclk, int *mclk_change); +int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, + struct device *dev); +void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data); + +#endif + diff --git a/sound/soc/tegra/tegra_das.c b/sound/soc/tegra/tegra_das.c new file mode 100644 index 000000000000..9f24ef73f2cb --- /dev/null +++ b/sound/soc/tegra/tegra_das.c @@ -0,0 +1,265 @@ +/* + * tegra_das.c - Tegra DAS driver + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (C) 2010 - NVIDIA, Inc. + * + * 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. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/module.h> +#include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/seq_file.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <mach/iomap.h> +#include <sound/soc.h> +#include "tegra_das.h" + +#define DRV_NAME "tegra-das" + +static struct tegra_das *das; + +static inline void tegra_das_write(u32 reg, u32 val) +{ + __raw_writel(val, das->regs + reg); +} + +static inline u32 tegra_das_read(u32 reg) +{ + return __raw_readl(das->regs + reg); +} + +int tegra_das_connect_dap_to_dac(int dap, int dac) +{ + u32 addr; + u32 reg; + + if (!das) + return -ENODEV; + + addr = TEGRA_DAS_DAP_CTRL_SEL + + (dap * TEGRA_DAS_DAP_CTRL_SEL_STRIDE); + reg = dac << TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P; + + tegra_das_write(addr, reg); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra_das_connect_dap_to_dac); + +int tegra_das_connect_dap_to_dap(int dap, int otherdap, int master, + int sdata1rx, int sdata2rx) +{ + u32 addr; + u32 reg; + + if (!das) + return -ENODEV; + + addr = TEGRA_DAS_DAP_CTRL_SEL + + (dap * TEGRA_DAS_DAP_CTRL_SEL_STRIDE); + reg = otherdap << TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P | + !!sdata2rx << TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P | + !!sdata1rx << TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P | + !!master << TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P; + + tegra_das_write(addr, reg); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra_das_connect_dap_to_dap); + +int tegra_das_connect_dac_to_dap(int dac, int dap) +{ + u32 addr; + u32 reg; + + if (!das) + return -ENODEV; + + addr = TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL + + (dac * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE); + reg = dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P | + dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P | + dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P; + + tegra_das_write(addr, reg); + + return 0; +} +EXPORT_SYMBOL_GPL(tegra_das_connect_dac_to_dap); + +#ifdef CONFIG_DEBUG_FS +static int tegra_das_show(struct seq_file *s, void *unused) +{ + int i; + u32 addr; + u32 reg; + + for (i = 0; i < TEGRA_DAS_DAP_CTRL_SEL_COUNT; i++) { + addr = TEGRA_DAS_DAP_CTRL_SEL + + (i * TEGRA_DAS_DAP_CTRL_SEL_STRIDE); + reg = tegra_das_read(addr); + seq_printf(s, "TEGRA_DAS_DAP_CTRL_SEL[%d] = %08x\n", i, reg); + } + + for (i = 0; i < TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT; i++) { + addr = TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL + + (i * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE); + reg = tegra_das_read(addr); + seq_printf(s, "TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL[%d] = %08x\n", + i, reg); + } + + return 0; +} + +static int tegra_das_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, tegra_das_show, inode->i_private); +} + +static const struct file_operations tegra_das_debug_fops = { + .open = tegra_das_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void tegra_das_debug_add(struct tegra_das *das) +{ + das->debug = debugfs_create_file(DRV_NAME, S_IRUGO, + snd_soc_debugfs_root, das, + &tegra_das_debug_fops); +} + +static void tegra_das_debug_remove(struct tegra_das *das) +{ + if (das->debug) + debugfs_remove(das->debug); +} +#else +static inline void tegra_das_debug_add(struct tegra_das *das) +{ +} + +static inline void tegra_das_debug_remove(struct tegra_das *das) +{ +} +#endif + +static int __devinit tegra_das_probe(struct platform_device *pdev) +{ + struct resource *res, *region; + int ret = 0; + + if (das) + return -ENODEV; + + das = kzalloc(sizeof(struct tegra_das), GFP_KERNEL); + if (!das) { + dev_err(&pdev->dev, "Can't allocate tegra_das\n"); + ret = -ENOMEM; + goto exit; + } + das->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "No memory resource\n"); + ret = -ENODEV; + goto err_free; + } + + region = request_mem_region(res->start, resource_size(res), + pdev->name); + if (!region) { + dev_err(&pdev->dev, "Memory region already claimed\n"); + ret = -EBUSY; + goto err_free; + } + + das->regs = ioremap(res->start, resource_size(res)); + if (!das->regs) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err_release; + } + + tegra_das_debug_add(das); + + platform_set_drvdata(pdev, das); + + return 0; + +err_release: + release_mem_region(res->start, resource_size(res)); +err_free: + kfree(das); + das = 0; +exit: + return ret; +} + +static int __devexit tegra_das_remove(struct platform_device *pdev) +{ + struct resource *res; + + if (!das) + return -ENODEV; + + platform_set_drvdata(pdev, NULL); + + tegra_das_debug_remove(das); + + iounmap(das->regs); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + kfree(das); + das = 0; + + return 0; +} + +static struct platform_driver tegra_das_driver = { + .probe = tegra_das_probe, + .remove = __devexit_p(tegra_das_remove), + .driver = { + .name = DRV_NAME, + }, +}; + +static int __init tegra_das_modinit(void) +{ + return platform_driver_register(&tegra_das_driver); +} +module_init(tegra_das_modinit); + +static void __exit tegra_das_modexit(void) +{ + platform_driver_unregister(&tegra_das_driver); +} +module_exit(tegra_das_modexit); + +MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); +MODULE_DESCRIPTION("Tegra DAS driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/tegra/tegra_das.h b/sound/soc/tegra/tegra_das.h new file mode 100644 index 000000000000..2c96c7b3c459 --- /dev/null +++ b/sound/soc/tegra/tegra_das.h @@ -0,0 +1,135 @@ +/* + * tegra_das.h - Definitions for Tegra DAS driver + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (C) 2010 - NVIDIA, Inc. + * + * 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. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __TEGRA_DAS_H__ +#define __TEGRA_DAS_H__ + +/* Register TEGRA_DAS_DAP_CTRL_SEL */ +#define TEGRA_DAS_DAP_CTRL_SEL 0x00 +#define TEGRA_DAS_DAP_CTRL_SEL_COUNT 5 +#define TEGRA_DAS_DAP_CTRL_SEL_STRIDE 4 +#define TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P 31 +#define TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_S 1 +#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P 30 +#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_S 1 +#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P 29 +#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_S 1 +#define TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P 0 +#define TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_S 5 + +/* Values for field TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL */ +#define TEGRA_DAS_DAP_SEL_DAC1 0 +#define TEGRA_DAS_DAP_SEL_DAC2 1 +#define TEGRA_DAS_DAP_SEL_DAC3 2 +#define TEGRA_DAS_DAP_SEL_DAP1 16 +#define TEGRA_DAS_DAP_SEL_DAP2 17 +#define TEGRA_DAS_DAP_SEL_DAP3 18 +#define TEGRA_DAS_DAP_SEL_DAP4 19 +#define TEGRA_DAS_DAP_SEL_DAP5 20 + +/* Register TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL */ +#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL 0x40 +#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT 3 +#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE 4 +#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P 28 +#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_S 4 +#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P 24 +#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_S 4 +#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P 0 +#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_S 4 + +/* + * Values for: + * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL + * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL + * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL + */ +#define TEGRA_DAS_DAC_SEL_DAP1 0 +#define TEGRA_DAS_DAC_SEL_DAP2 1 +#define TEGRA_DAS_DAC_SEL_DAP3 2 +#define TEGRA_DAS_DAC_SEL_DAP4 3 +#define TEGRA_DAS_DAC_SEL_DAP5 4 + +/* + * Names/IDs of the DACs/DAPs. + */ + +#define TEGRA_DAS_DAP_ID_1 0 +#define TEGRA_DAS_DAP_ID_2 1 +#define TEGRA_DAS_DAP_ID_3 2 +#define TEGRA_DAS_DAP_ID_4 3 +#define TEGRA_DAS_DAP_ID_5 4 + +#define TEGRA_DAS_DAC_ID_1 0 +#define TEGRA_DAS_DAC_ID_2 1 +#define TEGRA_DAS_DAC_ID_3 2 + +struct tegra_das { + struct device *dev; + void __iomem *regs; + struct dentry *debug; +}; + +/* + * Terminology: + * DAS: Digital audio switch (HW module controlled by this driver) + * DAP: Digital audio port (port/pins on Tegra device) + * DAC: Digital audio controller (e.g. I2S or AC97 controller elsewhere) + * + * The Tegra DAS is a mux/cross-bar which can connect each DAP to a specific + * DAC, or another DAP. When DAPs are connected, one must be the master and + * one the slave. Each DAC allows selection of a specific DAP for input, to + * cater for the case where N DAPs are connected to 1 DAC for broadcast + * output. + * + * This driver is dumb; no attempt is made to ensure that a valid routing + * configuration is programmed. + */ + +/* + * Connect a DAP to to a DAC + * dap_id: DAP to connect: TEGRA_DAS_DAP_ID_* + * dac_sel: DAC to connect to: TEGRA_DAS_DAP_SEL_DAC* + */ +extern int tegra_das_connect_dap_to_dac(int dap_id, int dac_sel); + +/* + * Connect a DAP to to another DAP + * dap_id: DAP to connect: TEGRA_DAS_DAP_ID_* + * other_dap_sel: DAP to connect to: TEGRA_DAS_DAP_SEL_DAP* + * master: Is this DAP the master (1) or slave (0) + * sdata1rx: Is this DAP's SDATA1 pin RX (1) or TX (0) + * sdata2rx: Is this DAP's SDATA2 pin RX (1) or TX (0) + */ +extern int tegra_das_connect_dap_to_dap(int dap_id, int other_dap_sel, + int master, int sdata1rx, + int sdata2rx); + +/* + * Connect a DAC's input to a DAP + * (DAC outputs are selected by the DAP) + * dac_id: DAC ID to connect: TEGRA_DAS_DAC_ID_* + * dap_sel: DAP to receive input from: TEGRA_DAS_DAC_SEL_DAP* + */ +extern int tegra_das_connect_dac_to_dap(int dac_id, int dap_sel); + +#endif diff --git a/sound/soc/tegra/tegra_i2s.c b/sound/soc/tegra/tegra_i2s.c new file mode 100644 index 000000000000..4f5e2c90b020 --- /dev/null +++ b/sound/soc/tegra/tegra_i2s.c @@ -0,0 +1,503 @@ +/* + * tegra_i2s.c - Tegra I2S driver + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (C) 2010 - NVIDIA, Inc. + * + * Based on code copyright/by: + * + * Copyright (c) 2009-2010, NVIDIA Corporation. + * Scott Peterson <speterson@nvidia.com> + * + * Copyright (C) 2010 Google, Inc. + * Iliyan Malchev <malchev@google.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. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/seq_file.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <mach/iomap.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "tegra_das.h" +#include "tegra_i2s.h" + +#define DRV_NAME "tegra-i2s" + +static inline void tegra_i2s_write(struct tegra_i2s *i2s, u32 reg, u32 val) +{ + __raw_writel(val, i2s->regs + reg); +} + +static inline u32 tegra_i2s_read(struct tegra_i2s *i2s, u32 reg) +{ + return __raw_readl(i2s->regs + reg); +} + +#ifdef CONFIG_DEBUG_FS +static int tegra_i2s_show(struct seq_file *s, void *unused) +{ +#define REG(r) { r, #r } + static const struct { + int offset; + const char *name; + } regs[] = { + REG(TEGRA_I2S_CTRL), + REG(TEGRA_I2S_STATUS), + REG(TEGRA_I2S_TIMING), + REG(TEGRA_I2S_FIFO_SCR), + REG(TEGRA_I2S_PCM_CTRL), + REG(TEGRA_I2S_NW_CTRL), + REG(TEGRA_I2S_TDM_CTRL), + REG(TEGRA_I2S_TDM_TX_RX_CTRL), + }; +#undef REG + + struct tegra_i2s *i2s = s->private; + int i; + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + u32 val = tegra_i2s_read(i2s, regs[i].offset); + seq_printf(s, "%s = %08x\n", regs[i].name, val); + } + + return 0; +} + +static int tegra_i2s_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, tegra_i2s_show, inode->i_private); +} + +static const struct file_operations tegra_i2s_debug_fops = { + .open = tegra_i2s_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void tegra_i2s_debug_add(struct tegra_i2s *i2s, int id) +{ + char name[] = DRV_NAME ".0"; + + snprintf(name, sizeof(name), DRV_NAME".%1d", id); + i2s->debug = debugfs_create_file(name, S_IRUGO, snd_soc_debugfs_root, + i2s, &tegra_i2s_debug_fops); +} + +static void tegra_i2s_debug_remove(struct tegra_i2s *i2s) +{ + if (i2s->debug) + debugfs_remove(i2s->debug); +} +#else +static inline void tegra_i2s_debug_add(struct tegra_i2s *i2s) +{ +} + +static inline void tegra_i2s_debug_remove(struct tegra_i2s *i2s) +{ +} +#endif + +static int tegra_i2s_set_fmt(struct snd_soc_dai *dai, + unsigned int fmt) +{ + struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + return -EINVAL; + } + + i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_MASTER_ENABLE; + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + i2s->reg_ctrl |= TEGRA_I2S_CTRL_MASTER_ENABLE; + break; + case SND_SOC_DAIFMT_CBM_CFM: + break; + default: + return -EINVAL; + } + + i2s->reg_ctrl &= ~(TEGRA_I2S_CTRL_BIT_FORMAT_MASK | + TEGRA_I2S_CTRL_LRCK_MASK); + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_DSP; + i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW; + break; + case SND_SOC_DAIFMT_DSP_B: + i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_DSP; + i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_R_LOW; + break; + case SND_SOC_DAIFMT_I2S: + i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_I2S; + i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW; + break; + case SND_SOC_DAIFMT_RIGHT_J: + i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_RJM; + i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW; + break; + case SND_SOC_DAIFMT_LEFT_J: + i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_LJM; + i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int tegra_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct device *dev = substream->pcm->card->dev; + struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai); + u32 reg; + int ret, sample_size, srate, i2sclock, bitcnt; + + i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_BIT_SIZE_MASK; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_16; + sample_size = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_24; + sample_size = 24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_32; + sample_size = 32; + break; + default: + return -EINVAL; + } + + srate = params_rate(params); + + /* Final "* 2" required by Tegra hardware */ + i2sclock = srate * params_channels(params) * sample_size * 2; + + ret = clk_set_rate(i2s->clk_i2s, i2sclock); + if (ret) { + dev_err(dev, "Can't set I2S clock rate: %d\n", ret); + return ret; + } + + bitcnt = (i2sclock / (2 * srate)) - 1; + if (bitcnt < 0 || bitcnt > TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US) + return -EINVAL; + reg = bitcnt << TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT; + + if (i2sclock % (2 * srate)) + reg |= TEGRA_I2S_TIMING_NON_SYM_ENABLE; + + tegra_i2s_write(i2s, TEGRA_I2S_TIMING, reg); + + tegra_i2s_write(i2s, TEGRA_I2S_FIFO_SCR, + TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS | + TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS); + + return 0; +} + +static void tegra_i2s_start_playback(struct tegra_i2s *i2s) +{ + i2s->reg_ctrl |= TEGRA_I2S_CTRL_FIFO1_ENABLE; + tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl); +} + +static void tegra_i2s_stop_playback(struct tegra_i2s *i2s) +{ + i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_FIFO1_ENABLE; + tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl); +} + +static void tegra_i2s_start_capture(struct tegra_i2s *i2s) +{ + i2s->reg_ctrl |= TEGRA_I2S_CTRL_FIFO2_ENABLE; + tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl); +} + +static void tegra_i2s_stop_capture(struct tegra_i2s *i2s) +{ + i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_FIFO2_ENABLE; + tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl); +} + +static int tegra_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + if (!i2s->clk_refs) + clk_enable(i2s->clk_i2s); + i2s->clk_refs++; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + tegra_i2s_start_playback(i2s); + else + tegra_i2s_start_capture(i2s); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + tegra_i2s_stop_playback(i2s); + else + tegra_i2s_stop_capture(i2s); + i2s->clk_refs--; + if (!i2s->clk_refs) + clk_disable(i2s->clk_i2s); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int tegra_i2s_probe(struct snd_soc_dai *dai) +{ + struct tegra_i2s * i2s = snd_soc_dai_get_drvdata(dai); + + dai->capture_dma_data = &i2s->capture_dma_data; + dai->playback_dma_data = &i2s->playback_dma_data; + + return 0; +} + +static struct snd_soc_dai_ops tegra_i2s_dai_ops = { + .set_fmt = tegra_i2s_set_fmt, + .hw_params = tegra_i2s_hw_params, + .trigger = tegra_i2s_trigger, +}; + +struct snd_soc_dai_driver tegra_i2s_dai[] = { + { + .name = DRV_NAME ".0", + .probe = tegra_i2s_probe, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &tegra_i2s_dai_ops, + .symmetric_rates = 1, + }, + { + .name = DRV_NAME ".1", + .probe = tegra_i2s_probe, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &tegra_i2s_dai_ops, + .symmetric_rates = 1, + }, +}; + +static __devinit int tegra_i2s_platform_probe(struct platform_device *pdev) +{ + struct tegra_i2s * i2s; + char clk_name[12]; /* tegra-i2s.0 */ + struct resource *mem, *memregion, *dmareq; + int ret; + + if ((pdev->id < 0) || + (pdev->id >= ARRAY_SIZE(tegra_i2s_dai))) { + dev_err(&pdev->dev, "ID %d out of range\n", pdev->id); + return -EINVAL; + } + + /* + * FIXME: Until a codec driver exists for the tegra DAS, hard-code a + * 1:1 mapping between audio controllers and audio ports. + */ + ret = tegra_das_connect_dap_to_dac(TEGRA_DAS_DAP_ID_1 + pdev->id, + TEGRA_DAS_DAP_SEL_DAC1 + pdev->id); + if (ret) { + dev_err(&pdev->dev, "Can't set up DAP connection\n"); + return ret; + } + ret = tegra_das_connect_dac_to_dap(TEGRA_DAS_DAC_ID_1 + pdev->id, + TEGRA_DAS_DAC_SEL_DAP1 + pdev->id); + if (ret) { + dev_err(&pdev->dev, "Can't set up DAC connection\n"); + return ret; + } + + i2s = kzalloc(sizeof(struct tegra_i2s), GFP_KERNEL); + if (!i2s) { + dev_err(&pdev->dev, "Can't allocate tegra_i2s\n"); + ret = -ENOMEM; + goto exit; + } + dev_set_drvdata(&pdev->dev, i2s); + + snprintf(clk_name, sizeof(clk_name), DRV_NAME ".%d", pdev->id); + i2s->clk_i2s = clk_get_sys(clk_name, NULL); + if (IS_ERR(i2s->clk_i2s)) { + dev_err(&pdev->dev, "Can't retrieve i2s clock\n"); + ret = PTR_ERR(i2s->clk_i2s); + goto err_free; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "No memory resource\n"); + ret = -ENODEV; + goto err_clk_put; + } + + dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!dmareq) { + dev_err(&pdev->dev, "No DMA resource\n"); + ret = -ENODEV; + goto err_clk_put; + } + + memregion = request_mem_region(mem->start, resource_size(mem), + DRV_NAME); + if (!memregion) { + dev_err(&pdev->dev, "Memory region already claimed\n"); + ret = -EBUSY; + goto err_clk_put; + } + + i2s->regs = ioremap(mem->start, resource_size(mem)); + if (!i2s->regs) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err_release; + } + + i2s->capture_dma_data.addr = mem->start + TEGRA_I2S_FIFO2; + i2s->capture_dma_data.wrap = 4; + i2s->capture_dma_data.width = 32; + i2s->capture_dma_data.req_sel = dmareq->start; + + i2s->playback_dma_data.addr = mem->start + TEGRA_I2S_FIFO1; + i2s->playback_dma_data.wrap = 4; + i2s->playback_dma_data.width = 32; + i2s->playback_dma_data.req_sel = dmareq->start; + + i2s->reg_ctrl = TEGRA_I2S_CTRL_FIFO_FORMAT_PACKED; + + ret = snd_soc_register_dai(&pdev->dev, &tegra_i2s_dai[pdev->id]); + if (ret) { + dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); + ret = -ENOMEM; + goto err_unmap; + } + + tegra_i2s_debug_add(i2s, pdev->id); + + return 0; + +err_unmap: + iounmap(i2s->regs); +err_release: + release_mem_region(mem->start, resource_size(mem)); +err_clk_put: + clk_put(i2s->clk_i2s); +err_free: + kfree(i2s); +exit: + return ret; +} + +static int __devexit tegra_i2s_platform_remove(struct platform_device *pdev) +{ + struct tegra_i2s *i2s = dev_get_drvdata(&pdev->dev); + struct resource *res; + + snd_soc_unregister_dai(&pdev->dev); + + tegra_i2s_debug_remove(i2s); + + iounmap(i2s->regs); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + clk_put(i2s->clk_i2s); + + kfree(i2s); + + return 0; +} + +static struct platform_driver tegra_i2s_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = tegra_i2s_platform_probe, + .remove = __devexit_p(tegra_i2s_platform_remove), +}; + +static int __init snd_tegra_i2s_init(void) +{ + return platform_driver_register(&tegra_i2s_driver); +} +module_init(snd_tegra_i2s_init); + +static void __exit snd_tegra_i2s_exit(void) +{ + platform_driver_unregister(&tegra_i2s_driver); +} +module_exit(snd_tegra_i2s_exit); + +MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); +MODULE_DESCRIPTION("Tegra I2S ASoC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/tegra/tegra_i2s.h b/sound/soc/tegra/tegra_i2s.h new file mode 100644 index 000000000000..2b38a096f46c --- /dev/null +++ b/sound/soc/tegra/tegra_i2s.h @@ -0,0 +1,165 @@ +/* + * tegra_i2s.h - Definitions for Tegra I2S driver + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (C) 2010 - NVIDIA, Inc. + * + * Based on code copyright/by: + * + * Copyright (c) 2009-2010, NVIDIA Corporation. + * Scott Peterson <speterson@nvidia.com> + * + * Copyright (C) 2010 Google, Inc. + * Iliyan Malchev <malchev@google.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. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __TEGRA_I2S_H__ +#define __TEGRA_I2S_H__ + +#include "tegra_pcm.h" + +/* Register offsets from TEGRA_I2S1_BASE and TEGRA_I2S2_BASE */ + +#define TEGRA_I2S_CTRL 0x00 +#define TEGRA_I2S_STATUS 0x04 +#define TEGRA_I2S_TIMING 0x08 +#define TEGRA_I2S_FIFO_SCR 0x0c +#define TEGRA_I2S_PCM_CTRL 0x10 +#define TEGRA_I2S_NW_CTRL 0x14 +#define TEGRA_I2S_TDM_CTRL 0x20 +#define TEGRA_I2S_TDM_TX_RX_CTRL 0x24 +#define TEGRA_I2S_FIFO1 0x40 +#define TEGRA_I2S_FIFO2 0x80 + +/* Fields in TEGRA_I2S_CTRL */ + +#define TEGRA_I2S_CTRL_FIFO2_TX_ENABLE (1 << 30) +#define TEGRA_I2S_CTRL_FIFO1_ENABLE (1 << 29) +#define TEGRA_I2S_CTRL_FIFO2_ENABLE (1 << 28) +#define TEGRA_I2S_CTRL_FIFO1_RX_ENABLE (1 << 27) +#define TEGRA_I2S_CTRL_FIFO_LPBK_ENABLE (1 << 26) +#define TEGRA_I2S_CTRL_MASTER_ENABLE (1 << 25) + +#define TEGRA_I2S_LRCK_LEFT_LOW 0 +#define TEGRA_I2S_LRCK_RIGHT_LOW 1 + +#define TEGRA_I2S_CTRL_LRCK_SHIFT 24 +#define TEGRA_I2S_CTRL_LRCK_MASK (1 << TEGRA_I2S_CTRL_LRCK_SHIFT) +#define TEGRA_I2S_CTRL_LRCK_L_LOW (TEGRA_I2S_LRCK_LEFT_LOW << TEGRA_I2S_CTRL_LRCK_SHIFT) +#define TEGRA_I2S_CTRL_LRCK_R_LOW (TEGRA_I2S_LRCK_RIGHT_LOW << TEGRA_I2S_CTRL_LRCK_SHIFT) + +#define TEGRA_I2S_BIT_FORMAT_I2S 0 +#define TEGRA_I2S_BIT_FORMAT_RJM 1 +#define TEGRA_I2S_BIT_FORMAT_LJM 2 +#define TEGRA_I2S_BIT_FORMAT_DSP 3 + +#define TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT 10 +#define TEGRA_I2S_CTRL_BIT_FORMAT_MASK (3 << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT) +#define TEGRA_I2S_CTRL_BIT_FORMAT_I2S (TEGRA_I2S_BIT_FORMAT_I2S << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT) +#define TEGRA_I2S_CTRL_BIT_FORMAT_RJM (TEGRA_I2S_BIT_FORMAT_RJM << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT) +#define TEGRA_I2S_CTRL_BIT_FORMAT_LJM (TEGRA_I2S_BIT_FORMAT_LJM << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT) +#define TEGRA_I2S_CTRL_BIT_FORMAT_DSP (TEGRA_I2S_BIT_FORMAT_DSP << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT) + +#define TEGRA_I2S_BIT_SIZE_16 0 +#define TEGRA_I2S_BIT_SIZE_20 1 +#define TEGRA_I2S_BIT_SIZE_24 2 +#define TEGRA_I2S_BIT_SIZE_32 3 + +#define TEGRA_I2S_CTRL_BIT_SIZE_SHIFT 8 +#define TEGRA_I2S_CTRL_BIT_SIZE_MASK (3 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA_I2S_CTRL_BIT_SIZE_16 (TEGRA_I2S_BIT_SIZE_16 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA_I2S_CTRL_BIT_SIZE_20 (TEGRA_I2S_BIT_SIZE_20 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA_I2S_CTRL_BIT_SIZE_24 (TEGRA_I2S_BIT_SIZE_24 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA_I2S_CTRL_BIT_SIZE_32 (TEGRA_I2S_BIT_SIZE_32 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT) + +#define TEGRA_I2S_FIFO_16_LSB 0 +#define TEGRA_I2S_FIFO_20_LSB 1 +#define TEGRA_I2S_FIFO_24_LSB 2 +#define TEGRA_I2S_FIFO_32 3 +#define TEGRA_I2S_FIFO_PACKED 7 + +#define TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT 4 +#define TEGRA_I2S_CTRL_FIFO_FORMAT_MASK (7 << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT) +#define TEGRA_I2S_CTRL_FIFO_FORMAT_16_LSB (TEGRA_I2S_FIFO_16_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT) +#define TEGRA_I2S_CTRL_FIFO_FORMAT_20_LSB (TEGRA_I2S_FIFO_20_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT) +#define TEGRA_I2S_CTRL_FIFO_FORMAT_24_LSB (TEGRA_I2S_FIFO_24_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT) +#define TEGRA_I2S_CTRL_FIFO_FORMAT_32 (TEGRA_I2S_FIFO_32 << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT) +#define TEGRA_I2S_CTRL_FIFO_FORMAT_PACKED (TEGRA_I2S_FIFO_PACKED << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT) + +#define TEGRA_I2S_CTRL_IE_FIFO1_ERR (1 << 3) +#define TEGRA_I2S_CTRL_IE_FIFO2_ERR (1 << 2) +#define TEGRA_I2S_CTRL_QE_FIFO1 (1 << 1) +#define TEGRA_I2S_CTRL_QE_FIFO2 (1 << 0) + +/* Fields in TEGRA_I2S_STATUS */ + +#define TEGRA_I2S_STATUS_FIFO1_RDY (1 << 31) +#define TEGRA_I2S_STATUS_FIFO2_RDY (1 << 30) +#define TEGRA_I2S_STATUS_FIFO1_BSY (1 << 29) +#define TEGRA_I2S_STATUS_FIFO2_BSY (1 << 28) +#define TEGRA_I2S_STATUS_FIFO1_ERR (1 << 3) +#define TEGRA_I2S_STATUS_FIFO2_ERR (1 << 2) +#define TEGRA_I2S_STATUS_QS_FIFO1 (1 << 1) +#define TEGRA_I2S_STATUS_QS_FIFO2 (1 << 0) + +/* Fields in TEGRA_I2S_TIMING */ + +#define TEGRA_I2S_TIMING_NON_SYM_ENABLE (1 << 12) +#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT 0 +#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US 0x7fff +#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK (TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US << TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT) + +/* Fields in TEGRA_I2S_FIFO_SCR */ + +#define TEGRA_I2S_FIFO_SCR_FIFO2_FULL_EMPTY_COUNT_SHIFT 24 +#define TEGRA_I2S_FIFO_SCR_FIFO1_FULL_EMPTY_COUNT_SHIFT 16 +#define TEGRA_I2S_FIFO_SCR_FIFO_FULL_EMPTY_COUNT_MASK 0x3f + +#define TEGRA_I2S_FIFO_SCR_FIFO2_CLR (1 << 12) +#define TEGRA_I2S_FIFO_SCR_FIFO1_CLR (1 << 8) + +#define TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT 0 +#define TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS 1 +#define TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS 2 +#define TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS 3 + +#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT 4 +#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_MASK (3 << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) +#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_ONE_SLOT (TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) +#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) +#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_EIGHT_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) +#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_TWELVE_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) + +#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT 0 +#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_MASK (3 << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) +#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_ONE_SLOT (TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) +#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) +#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_EIGHT_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) +#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_TWELVE_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) + +struct tegra_i2s { + struct clk *clk_i2s; + int clk_refs; + struct tegra_pcm_dma_params capture_dma_data; + struct tegra_pcm_dma_params playback_dma_data; + void __iomem *regs; + struct dentry *debug; + u32 reg_ctrl; +}; + +#endif diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c new file mode 100644 index 000000000000..3c271f953582 --- /dev/null +++ b/sound/soc/tegra/tegra_pcm.c @@ -0,0 +1,404 @@ +/* + * tegra_pcm.c - Tegra PCM driver + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (C) 2010 - NVIDIA, Inc. + * + * Based on code copyright/by: + * + * Copyright (c) 2009-2010, NVIDIA Corporation. + * Scott Peterson <speterson@nvidia.com> + * Vijay Mali <vmali@nvidia.com> + * + * Copyright (C) 2010 Google, Inc. + * Iliyan Malchev <malchev@google.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. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/module.h> +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "tegra_pcm.h" + +#define DRV_NAME "tegra-pcm-audio" + +static const struct snd_pcm_hardware tegra_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_INTERLEAVED, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels_min = 2, + .channels_max = 2, + .period_bytes_min = 1024, + .period_bytes_max = PAGE_SIZE, + .periods_min = 2, + .periods_max = 8, + .buffer_bytes_max = PAGE_SIZE * 8, + .fifo_size = 4, +}; + +static void tegra_pcm_queue_dma(struct tegra_runtime_data *prtd) +{ + struct snd_pcm_substream *substream = prtd->substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + struct tegra_dma_req *dma_req; + unsigned long addr; + + dma_req = &prtd->dma_req[prtd->dma_req_idx]; + prtd->dma_req_idx = 1 - prtd->dma_req_idx; + + addr = buf->addr + prtd->dma_pos; + prtd->dma_pos += dma_req->size; + if (prtd->dma_pos >= prtd->dma_pos_end) + prtd->dma_pos = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dma_req->source_addr = addr; + else + dma_req->dest_addr = addr; + + tegra_dma_enqueue_req(prtd->dma_chan, dma_req); +} + +static void dma_complete_callback(struct tegra_dma_req *req) +{ + struct tegra_runtime_data *prtd = (struct tegra_runtime_data *)req->dev; + struct snd_pcm_substream *substream = prtd->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + + spin_lock(&prtd->lock); + + if (!prtd->running) { + spin_unlock(&prtd->lock); + return; + } + + if (++prtd->period_index >= runtime->periods) + prtd->period_index = 0; + + tegra_pcm_queue_dma(prtd); + + spin_unlock(&prtd->lock); + + snd_pcm_period_elapsed(substream); +} + +static void setup_dma_tx_request(struct tegra_dma_req *req, + struct tegra_pcm_dma_params * dmap) +{ + req->complete = dma_complete_callback; + req->to_memory = false; + req->dest_addr = dmap->addr; + req->dest_wrap = dmap->wrap; + req->source_bus_width = 32; + req->source_wrap = 0; + req->dest_bus_width = dmap->width; + req->req_sel = dmap->req_sel; +} + +static void setup_dma_rx_request(struct tegra_dma_req *req, + struct tegra_pcm_dma_params * dmap) +{ + req->complete = dma_complete_callback; + req->to_memory = true; + req->source_addr = dmap->addr; + req->dest_wrap = 0; + req->source_bus_width = dmap->width; + req->source_wrap = dmap->wrap; + req->dest_bus_width = 32; + req->req_sel = dmap->req_sel; +} + +static int tegra_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct tegra_runtime_data *prtd; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct tegra_pcm_dma_params * dmap; + int ret = 0; + + prtd = kzalloc(sizeof(struct tegra_runtime_data), GFP_KERNEL); + if (prtd == NULL) + return -ENOMEM; + + runtime->private_data = prtd; + prtd->substream = substream; + + spin_lock_init(&prtd->lock); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + setup_dma_tx_request(&prtd->dma_req[0], dmap); + setup_dma_tx_request(&prtd->dma_req[1], dmap); + } else { + dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + setup_dma_rx_request(&prtd->dma_req[0], dmap); + setup_dma_rx_request(&prtd->dma_req[1], dmap); + } + + prtd->dma_req[0].dev = prtd; + prtd->dma_req[1].dev = prtd; + + prtd->dma_chan = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT); + if (prtd->dma_chan == NULL) { + ret = -ENOMEM; + goto err; + } + + /* Set HW params now that initialization is complete */ + snd_soc_set_runtime_hwparams(substream, &tegra_pcm_hardware); + + /* Ensure that buffer size is a multiple of period size */ + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + goto err; + + return 0; + +err: + if (prtd->dma_chan) { + tegra_dma_free_channel(prtd->dma_chan); + } + + kfree(prtd); + + return ret; +} + +static int tegra_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct tegra_runtime_data *prtd = runtime->private_data; + + tegra_dma_free_channel(prtd->dma_chan); + + kfree(prtd); + + return 0; +} + +static int tegra_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct tegra_runtime_data *prtd = runtime->private_data; + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + prtd->dma_req[0].size = params_period_bytes(params); + prtd->dma_req[1].size = prtd->dma_req[0].size; + + return 0; +} + +static int tegra_pcm_hw_free(struct snd_pcm_substream *substream) +{ + snd_pcm_set_runtime_buffer(substream, NULL); + + return 0; +} + +static int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct tegra_runtime_data *prtd = runtime->private_data; + unsigned long flags; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + prtd->dma_pos = 0; + prtd->dma_pos_end = frames_to_bytes(runtime, runtime->periods * runtime->period_size); + prtd->period_index = 0; + prtd->dma_req_idx = 0; + /* Fall-through */ + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + spin_lock_irqsave(&prtd->lock, flags); + prtd->running = 1; + spin_unlock_irqrestore(&prtd->lock, flags); + tegra_pcm_queue_dma(prtd); + tegra_pcm_queue_dma(prtd); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + spin_lock_irqsave(&prtd->lock, flags); + prtd->running = 0; + spin_unlock_irqrestore(&prtd->lock, flags); + tegra_dma_dequeue_req(prtd->dma_chan, &prtd->dma_req[0]); + tegra_dma_dequeue_req(prtd->dma_chan, &prtd->dma_req[1]); + break; + default: + return -EINVAL; + } + + return 0; +} + +static snd_pcm_uframes_t tegra_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct tegra_runtime_data *prtd = runtime->private_data; + + return prtd->period_index * runtime->period_size; +} + + +static int tegra_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + return dma_mmap_writecombine(substream->pcm->card->dev, vma, + runtime->dma_area, + runtime->dma_addr, + runtime->dma_bytes); +} + +static struct snd_pcm_ops tegra_pcm_ops = { + .open = tegra_pcm_open, + .close = tegra_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = tegra_pcm_hw_params, + .hw_free = tegra_pcm_hw_free, + .trigger = tegra_pcm_trigger, + .pointer = tegra_pcm_pointer, + .mmap = tegra_pcm_mmap, +}; + +static int tegra_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) +{ + struct snd_pcm_substream *substream = pcm->streams[stream].substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + size_t size = tegra_pcm_hardware.buffer_bytes_max; + + buf->area = dma_alloc_writecombine(pcm->card->dev, size, + &buf->addr, GFP_KERNEL); + if (!buf->area) + return -ENOMEM; + + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = pcm->card->dev; + buf->private_data = NULL; + buf->bytes = size; + + return 0; +} + +static void tegra_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream) +{ + struct snd_pcm_substream *substream = pcm->streams[stream].substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + + if (!buf->area) + return; + + dma_free_writecombine(pcm->card->dev, buf->bytes, + buf->area, buf->addr); + buf->area = NULL; +} + +static u64 tegra_dma_mask = DMA_BIT_MASK(32); + +static int tegra_pcm_new(struct snd_card *card, + struct snd_soc_dai *dai, struct snd_pcm *pcm) +{ + int ret = 0; + + if (!card->dev->dma_mask) + card->dev->dma_mask = &tegra_dma_mask; + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = 0xffffffff; + + if (dai->driver->playback.channels_min) { + ret = tegra_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret) + goto err; + } + + if (dai->driver->capture.channels_min) { + ret = tegra_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_CAPTURE); + if (ret) + goto err_free_play; + } + + return 0; + +err_free_play: + tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK); +err: + return ret; +} + +static void tegra_pcm_free(struct snd_pcm *pcm) +{ + tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE); + tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK); +} + +struct snd_soc_platform_driver tegra_pcm_platform = { + .ops = &tegra_pcm_ops, + .pcm_new = tegra_pcm_new, + .pcm_free = tegra_pcm_free, +}; + +static int __devinit tegra_pcm_platform_probe(struct platform_device *pdev) +{ + return snd_soc_register_platform(&pdev->dev, &tegra_pcm_platform); +} + +static int __devexit tegra_pcm_platform_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static struct platform_driver tegra_pcm_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = tegra_pcm_platform_probe, + .remove = __devexit_p(tegra_pcm_platform_remove), +}; + +static int __init snd_tegra_pcm_init(void) +{ + return platform_driver_register(&tegra_pcm_driver); +} +module_init(snd_tegra_pcm_init); + +static void __exit snd_tegra_pcm_exit(void) +{ + platform_driver_unregister(&tegra_pcm_driver); +} +module_exit(snd_tegra_pcm_exit); + +MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); +MODULE_DESCRIPTION("Tegra PCM ASoC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/tegra/tegra_pcm.h b/sound/soc/tegra/tegra_pcm.h new file mode 100644 index 000000000000..dbb90339fe0d --- /dev/null +++ b/sound/soc/tegra/tegra_pcm.h @@ -0,0 +1,55 @@ +/* + * tegra_pcm.h - Definitions for Tegra PCM driver + * + * Author: Stephen Warren <swarren@nvidia.com> + * Copyright (C) 2010 - NVIDIA, Inc. + * + * Based on code copyright/by: + * + * Copyright (c) 2009-2010, NVIDIA Corporation. + * Scott Peterson <speterson@nvidia.com> + * + * Copyright (C) 2010 Google, Inc. + * Iliyan Malchev <malchev@google.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. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __TEGRA_PCM_H__ +#define __TEGRA_PCM_H__ + +#include <mach/dma.h> + +struct tegra_pcm_dma_params { + unsigned long addr; + unsigned long wrap; + unsigned long width; + unsigned long req_sel; +}; + +struct tegra_runtime_data { + struct snd_pcm_substream *substream; + spinlock_t lock; + int running; + int dma_pos; + int dma_pos_end; + int period_index; + int dma_req_idx; + struct tegra_dma_req dma_req[2]; + struct tegra_dma_channel *dma_chan; +}; + +#endif diff --git a/sound/sound_core.c b/sound/sound_core.c index 5580aced8730..6ce277860fd7 100644 --- a/sound/sound_core.c +++ b/sound/sound_core.c @@ -384,6 +384,9 @@ int register_sound_special_device(const struct file_operations *fops, int unit, case 4: name = "audio"; break; + case 5: + name = "dspW"; + break; case 8: name = "sequencer2"; if (unit >= SOUND_STEP) diff --git a/sound/usb/6fire/Makefile b/sound/usb/6fire/Makefile new file mode 100644 index 000000000000..dfce6ec53513 --- /dev/null +++ b/sound/usb/6fire/Makefile @@ -0,0 +1,3 @@ +snd-usb-6fire-objs += chip.o comm.o midi.o control.o firmware.o pcm.o +obj-$(CONFIG_SND_USB_6FIRE) += snd-usb-6fire.o + diff --git a/sound/usb/6fire/chip.c b/sound/usb/6fire/chip.c new file mode 100644 index 000000000000..c7dca7b0b9fe --- /dev/null +++ b/sound/usb/6fire/chip.c @@ -0,0 +1,232 @@ +/* + * Linux driver for TerraTec DMX 6Fire USB + * + * Main routines and module definitions. + * + * Author: Torsten Schenk <torsten.schenk@zoho.com> + * Created: Jan 01, 2011 + * Version: 0.3.0 + * Copyright: (C) Torsten Schenk + * + * 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 "chip.h" +#include "firmware.h" +#include "pcm.h" +#include "control.h" +#include "comm.h" +#include "midi.h" + +#include <linux/moduleparam.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/gfp.h> +#include <sound/initval.h> + +MODULE_AUTHOR("Torsten Schenk <torsten.schenk@zoho.com>"); +MODULE_DESCRIPTION("TerraTec DMX 6Fire USB audio driver, version 0.3.0"); +MODULE_LICENSE("GPL v2"); +MODULE_SUPPORTED_DEVICE("{{TerraTec, DMX 6Fire USB}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable card */ +static struct sfire_chip *chips[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; +static struct usb_device *devices[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for the 6fire sound device"); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for the 6fire sound device."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable the 6fire sound device."); + +static DEFINE_MUTEX(register_mutex); + +static void usb6fire_chip_abort(struct sfire_chip *chip) +{ + if (chip) { + if (chip->pcm) + usb6fire_pcm_abort(chip); + if (chip->midi) + usb6fire_midi_abort(chip); + if (chip->comm) + usb6fire_comm_abort(chip); + if (chip->control) + usb6fire_control_abort(chip); + if (chip->card) { + snd_card_disconnect(chip->card); + snd_card_free_when_closed(chip->card); + chip->card = NULL; + } + } +} + +static void usb6fire_chip_destroy(struct sfire_chip *chip) +{ + if (chip) { + if (chip->pcm) + usb6fire_pcm_destroy(chip); + if (chip->midi) + usb6fire_midi_destroy(chip); + if (chip->comm) + usb6fire_comm_destroy(chip); + if (chip->control) + usb6fire_control_destroy(chip); + if (chip->card) + snd_card_free(chip->card); + } +} + +static int __devinit usb6fire_chip_probe(struct usb_interface *intf, + const struct usb_device_id *usb_id) +{ + int ret; + int i; + struct sfire_chip *chip = NULL; + struct usb_device *device = interface_to_usbdev(intf); + int regidx = -1; /* index in module parameter array */ + struct snd_card *card = NULL; + + /* look if we already serve this card and return if so */ + mutex_lock(®ister_mutex); + for (i = 0; i < SNDRV_CARDS; i++) { + if (devices[i] == device) { + if (chips[i]) + chips[i]->intf_count++; + usb_set_intfdata(intf, chips[i]); + mutex_unlock(®ister_mutex); + return 0; + } else if (regidx < 0) + regidx = i; + } + if (regidx < 0) { + mutex_unlock(®ister_mutex); + snd_printk(KERN_ERR PREFIX "too many cards registered.\n"); + return -ENODEV; + } + devices[regidx] = device; + mutex_unlock(®ister_mutex); + + /* check, if firmware is present on device, upload it if not */ + ret = usb6fire_fw_init(intf); + if (ret < 0) + return ret; + else if (ret == FW_NOT_READY) /* firmware update performed */ + return 0; + + /* if we are here, card can be registered in alsa. */ + if (usb_set_interface(device, 0, 0) != 0) { + snd_printk(KERN_ERR PREFIX "can't set first interface.\n"); + return -EIO; + } + ret = snd_card_create(index[regidx], id[regidx], THIS_MODULE, + sizeof(struct sfire_chip), &card); + if (ret < 0) { + snd_printk(KERN_ERR PREFIX "cannot create alsa card.\n"); + return ret; + } + strcpy(card->driver, "6FireUSB"); + strcpy(card->shortname, "TerraTec DMX6FireUSB"); + sprintf(card->longname, "%s at %d:%d", card->shortname, + device->bus->busnum, device->devnum); + snd_card_set_dev(card, &intf->dev); + + chip = card->private_data; + chips[regidx] = chip; + chip->dev = device; + chip->regidx = regidx; + chip->intf_count = 1; + chip->card = card; + + ret = usb6fire_comm_init(chip); + if (ret < 0) { + usb6fire_chip_destroy(chip); + return ret; + } + + ret = usb6fire_midi_init(chip); + if (ret < 0) { + usb6fire_chip_destroy(chip); + return ret; + } + + ret = usb6fire_pcm_init(chip); + if (ret < 0) { + usb6fire_chip_destroy(chip); + return ret; + } + + ret = usb6fire_control_init(chip); + if (ret < 0) { + usb6fire_chip_destroy(chip); + return ret; + } + + ret = snd_card_register(card); + if (ret < 0) { + snd_printk(KERN_ERR PREFIX "cannot register card."); + usb6fire_chip_destroy(chip); + return ret; + } + usb_set_intfdata(intf, chip); + return 0; +} + +static void usb6fire_chip_disconnect(struct usb_interface *intf) +{ + struct sfire_chip *chip; + struct snd_card *card; + + chip = usb_get_intfdata(intf); + if (chip) { /* if !chip, fw upload has been performed */ + card = chip->card; + chip->intf_count--; + if (!chip->intf_count) { + mutex_lock(®ister_mutex); + devices[chip->regidx] = NULL; + chips[chip->regidx] = NULL; + mutex_unlock(®ister_mutex); + + chip->shutdown = true; + usb6fire_chip_abort(chip); + usb6fire_chip_destroy(chip); + } + } +} + +static struct usb_device_id device_table[] = { + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x0ccd, + .idProduct = 0x0080 + }, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +static struct usb_driver driver = { + .name = "snd-usb-6fire", + .probe = usb6fire_chip_probe, + .disconnect = usb6fire_chip_disconnect, + .id_table = device_table, +}; + +static int __init usb6fire_chip_init(void) +{ + return usb_register(&driver); +} + +static void __exit usb6fire_chip_cleanup(void) +{ + usb_deregister(&driver); +} + +module_init(usb6fire_chip_init); +module_exit(usb6fire_chip_cleanup); diff --git a/sound/usb/6fire/chip.h b/sound/usb/6fire/chip.h new file mode 100644 index 000000000000..d11e5cb520f0 --- /dev/null +++ b/sound/usb/6fire/chip.h @@ -0,0 +1,32 @@ +/* + * Linux driver for TerraTec DMX 6Fire USB + * + * Author: Torsten Schenk <torsten.schenk@zoho.com> + * Created: Jan 01, 2011 + * Version: 0.3.0 + * Copyright: (C) Torsten Schenk + * + * 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 USB6FIRE_CHIP_H +#define USB6FIRE_CHIP_H + +#include "common.h" + +struct sfire_chip { + struct usb_device *dev; + struct snd_card *card; + int intf_count; /* number of registered interfaces */ + int regidx; /* index in module parameter arrays */ + bool shutdown; + + struct midi_runtime *midi; + struct pcm_runtime *pcm; + struct control_runtime *control; + struct comm_runtime *comm; +}; +#endif /* USB6FIRE_CHIP_H */ + diff --git a/sound/usb/6fire/comm.c b/sound/usb/6fire/comm.c new file mode 100644 index 000000000000..c994daa57af2 --- /dev/null +++ b/sound/usb/6fire/comm.c @@ -0,0 +1,176 @@ +/* + * Linux driver for TerraTec DMX 6Fire USB + * + * Device communications + * + * Author: Torsten Schenk <torsten.schenk@zoho.com> + * Created: Jan 01, 2011 + * Version: 0.3.0 + * Copyright: (C) Torsten Schenk + * + * 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 "comm.h" +#include "chip.h" +#include "midi.h" + +enum { + COMM_EP = 1, + COMM_FPGA_EP = 2 +}; + +static void usb6fire_comm_init_urb(struct comm_runtime *rt, struct urb *urb, + u8 *buffer, void *context, void(*handler)(struct urb *urb)) +{ + usb_init_urb(urb); + urb->transfer_buffer = buffer; + urb->pipe = usb_sndintpipe(rt->chip->dev, COMM_EP); + urb->complete = handler; + urb->context = context; + urb->interval = 1; + urb->dev = rt->chip->dev; +} + +static void usb6fire_comm_receiver_handler(struct urb *urb) +{ + struct comm_runtime *rt = urb->context; + struct midi_runtime *midi_rt = rt->chip->midi; + + if (!urb->status) { + if (rt->receiver_buffer[0] == 0x10) /* midi in event */ + if (midi_rt) + midi_rt->in_received(midi_rt, + rt->receiver_buffer + 2, + rt->receiver_buffer[1]); + } + + if (!rt->chip->shutdown) { + urb->status = 0; + urb->actual_length = 0; + if (usb_submit_urb(urb, GFP_ATOMIC) < 0) + snd_printk(KERN_WARNING PREFIX + "comm data receiver aborted.\n"); + } +} + +static void usb6fire_comm_init_buffer(u8 *buffer, u8 id, u8 request, + u8 reg, u8 vl, u8 vh) +{ + buffer[0] = 0x01; + buffer[2] = request; + buffer[3] = id; + switch (request) { + case 0x02: + buffer[1] = 0x05; /* length (starting at buffer[2]) */ + buffer[4] = reg; + buffer[5] = vl; + buffer[6] = vh; + break; + + case 0x12: + buffer[1] = 0x0b; /* length (starting at buffer[2]) */ + buffer[4] = 0x00; + buffer[5] = 0x18; + buffer[6] = 0x05; + buffer[7] = 0x00; + buffer[8] = 0x01; + buffer[9] = 0x00; + buffer[10] = 0x9e; + buffer[11] = reg; + buffer[12] = vl; + break; + + case 0x20: + case 0x21: + case 0x22: + buffer[1] = 0x04; + buffer[4] = reg; + buffer[5] = vl; + break; + } +} + +static int usb6fire_comm_send_buffer(u8 *buffer, struct usb_device *dev) +{ + int ret; + int actual_len; + + ret = usb_interrupt_msg(dev, usb_sndintpipe(dev, COMM_EP), + buffer, buffer[1] + 2, &actual_len, HZ); + if (ret < 0) + return ret; + else if (actual_len != buffer[1] + 2) + return -EIO; + return 0; +} + +static int usb6fire_comm_write8(struct comm_runtime *rt, u8 request, + u8 reg, u8 value) +{ + u8 buffer[13]; /* 13: maximum length of message */ + + usb6fire_comm_init_buffer(buffer, 0x00, request, reg, value, 0x00); + return usb6fire_comm_send_buffer(buffer, rt->chip->dev); +} + +static int usb6fire_comm_write16(struct comm_runtime *rt, u8 request, + u8 reg, u8 vl, u8 vh) +{ + u8 buffer[13]; /* 13: maximum length of message */ + + usb6fire_comm_init_buffer(buffer, 0x00, request, reg, vl, vh); + return usb6fire_comm_send_buffer(buffer, rt->chip->dev); +} + +int __devinit usb6fire_comm_init(struct sfire_chip *chip) +{ + struct comm_runtime *rt = kzalloc(sizeof(struct comm_runtime), + GFP_KERNEL); + struct urb *urb = &rt->receiver; + int ret; + + if (!rt) + return -ENOMEM; + + rt->serial = 1; + rt->chip = chip; + usb_init_urb(urb); + rt->init_urb = usb6fire_comm_init_urb; + rt->write8 = usb6fire_comm_write8; + rt->write16 = usb6fire_comm_write16; + + /* submit an urb that receives communication data from device */ + urb->transfer_buffer = rt->receiver_buffer; + urb->transfer_buffer_length = COMM_RECEIVER_BUFSIZE; + urb->pipe = usb_rcvintpipe(chip->dev, COMM_EP); + urb->dev = chip->dev; + urb->complete = usb6fire_comm_receiver_handler; + urb->context = rt; + urb->interval = 1; + ret = usb_submit_urb(urb, GFP_KERNEL); + if (ret < 0) { + kfree(rt); + snd_printk(KERN_ERR PREFIX "cannot create comm data receiver."); + return ret; + } + chip->comm = rt; + return 0; +} + +void usb6fire_comm_abort(struct sfire_chip *chip) +{ + struct comm_runtime *rt = chip->comm; + + if (rt) + usb_poison_urb(&rt->receiver); +} + +void usb6fire_comm_destroy(struct sfire_chip *chip) +{ + kfree(chip->comm); + chip->comm = NULL; +} diff --git a/sound/usb/6fire/comm.h b/sound/usb/6fire/comm.h new file mode 100644 index 000000000000..edc5dc84b888 --- /dev/null +++ b/sound/usb/6fire/comm.h @@ -0,0 +1,44 @@ +/* + * Linux driver for TerraTec DMX 6Fire USB + * + * Author: Torsten Schenk <torsten.schenk@zoho.com> + * Created: Jan 01, 2011 + * Version: 0.3.0 + * Copyright: (C) Torsten Schenk + * + * 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 USB6FIRE_COMM_H +#define USB6FIRE_COMM_H + +#include "common.h" + +enum /* settings for comm */ +{ + COMM_RECEIVER_BUFSIZE = 64, +}; + +struct comm_runtime { + struct sfire_chip *chip; + + struct urb receiver; + u8 receiver_buffer[COMM_RECEIVER_BUFSIZE]; + + u8 serial; /* urb serial */ + + void (*init_urb)(struct comm_runtime *rt, struct urb *urb, u8 *buffer, + void *context, void(*handler)(struct urb *urb)); + /* writes control data to the device */ + int (*write8)(struct comm_runtime *rt, u8 request, u8 reg, u8 value); + int (*write16)(struct comm_runtime *rt, u8 request, u8 reg, + u8 vh, u8 vl); +}; + +int __devinit usb6fire_comm_init(struct sfire_chip *chip); +void usb6fire_comm_abort(struct sfire_chip *chip); +void usb6fire_comm_destroy(struct sfire_chip *chip); +#endif /* USB6FIRE_COMM_H */ + diff --git a/sound/usb/6fire/common.h b/sound/usb/6fire/common.h new file mode 100644 index 000000000000..7dbeb4a37831 --- /dev/null +++ b/sound/usb/6fire/common.h @@ -0,0 +1,30 @@ +/* + * Linux driver for TerraTec DMX 6Fire USB + * + * Author: Torsten Schenk <torsten.schenk@zoho.com> + * Created: Jan 01, 2011 + * Version: 0.3.0 + * Copyright: (C) Torsten Schenk + * + * 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 USB6FIRE_COMMON_H +#define USB6FIRE_COMMON_H + +#include <linux/slab.h> +#include <linux/usb.h> +#include <sound/core.h> + +#define PREFIX "6fire: " + +struct sfire_chip; +struct midi_runtime; +struct pcm_runtime; +struct control_runtime; +struct comm_runtime; +#endif /* USB6FIRE_COMMON_H */ + diff --git a/sound/usb/6fire/control.c b/sound/usb/6fire/control.c new file mode 100644 index 000000000000..248463511186 --- /dev/null +++ b/sound/usb/6fire/control.c @@ -0,0 +1,275 @@ +/* + * Linux driver for TerraTec DMX 6Fire USB + * + * Mixer control + * + * Author: Torsten Schenk <torsten.schenk@zoho.com> + * Created: Jan 01, 2011 + * Version: 0.3.0 + * Copyright: (C) Torsten Schenk + * + * 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/interrupt.h> +#include <sound/control.h> + +#include "control.h" +#include "comm.h" +#include "chip.h" + +static char *opt_coax_texts[2] = { "Optical", "Coax" }; +static char *line_phono_texts[2] = { "Line", "Phono" }; + +/* + * calculated with $value\[i\] = 128 \cdot sqrt[3]{\frac{i}{128}}$ + * this is done because the linear values cause rapid degredation + * of volume in the uppermost region. + */ +static const u8 log_volume_table[128] = { + 0x00, 0x19, 0x20, 0x24, 0x28, 0x2b, 0x2e, 0x30, 0x32, 0x34, + 0x36, 0x38, 0x3a, 0x3b, 0x3d, 0x3e, 0x40, 0x41, 0x42, 0x43, + 0x44, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x53, 0x54, 0x55, 0x56, + 0x56, 0x57, 0x58, 0x58, 0x59, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, + 0x5d, 0x5e, 0x5e, 0x5f, 0x60, 0x60, 0x61, 0x61, 0x62, 0x62, + 0x63, 0x63, 0x64, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68, + 0x68, 0x69, 0x69, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c, 0x6c, 0x6c, + 0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71, + 0x71, 0x72, 0x72, 0x73, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, + 0x75, 0x76, 0x76, 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, + 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, + 0x7d, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f }; + +/* + * data that needs to be sent to device. sets up card internal stuff. + * values dumped from windows driver and filtered by trial'n'error. + */ +static const struct { + u8 type; + u8 reg; + u8 value; +} +init_data[] = { + { 0x22, 0x00, 0x00 }, { 0x20, 0x00, 0x08 }, { 0x22, 0x01, 0x01 }, + { 0x20, 0x01, 0x08 }, { 0x22, 0x02, 0x00 }, { 0x20, 0x02, 0x08 }, + { 0x22, 0x03, 0x00 }, { 0x20, 0x03, 0x08 }, { 0x22, 0x04, 0x00 }, + { 0x20, 0x04, 0x08 }, { 0x22, 0x05, 0x01 }, { 0x20, 0x05, 0x08 }, + { 0x22, 0x04, 0x01 }, { 0x12, 0x04, 0x00 }, { 0x12, 0x05, 0x00 }, + { 0x12, 0x0d, 0x78 }, { 0x12, 0x21, 0x82 }, { 0x12, 0x22, 0x80 }, + { 0x12, 0x23, 0x00 }, { 0x12, 0x06, 0x02 }, { 0x12, 0x03, 0x00 }, + { 0x12, 0x02, 0x00 }, { 0x22, 0x03, 0x01 }, + { 0 } /* TERMINATING ENTRY */ +}; + +static void usb6fire_control_master_vol_update(struct control_runtime *rt) +{ + struct comm_runtime *comm_rt = rt->chip->comm; + if (comm_rt) { + /* set volume */ + comm_rt->write8(comm_rt, 0x12, 0x0f, 0x7f - + log_volume_table[rt->master_vol]); + /* unmute */ + comm_rt->write8(comm_rt, 0x12, 0x0e, 0x00); + } +} + +static void usb6fire_control_line_phono_update(struct control_runtime *rt) +{ + struct comm_runtime *comm_rt = rt->chip->comm; + if (comm_rt) { + comm_rt->write8(comm_rt, 0x22, 0x02, rt->line_phono_switch); + comm_rt->write8(comm_rt, 0x21, 0x02, rt->line_phono_switch); + } +} + +static void usb6fire_control_opt_coax_update(struct control_runtime *rt) +{ + struct comm_runtime *comm_rt = rt->chip->comm; + if (comm_rt) { + comm_rt->write8(comm_rt, 0x22, 0x00, rt->opt_coax_switch); + comm_rt->write8(comm_rt, 0x21, 0x00, rt->opt_coax_switch); + } +} + +static int usb6fire_control_master_vol_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 = 127; + return 0; +} + +static int usb6fire_control_master_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct control_runtime *rt = snd_kcontrol_chip(kcontrol); + int changed = 0; + if (rt->master_vol != ucontrol->value.integer.value[0]) { + rt->master_vol = ucontrol->value.integer.value[0]; + usb6fire_control_master_vol_update(rt); + changed = 1; + } + return changed; +} + +static int usb6fire_control_master_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct control_runtime *rt = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = rt->master_vol; + return 0; +} + +static int usb6fire_control_line_phono_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item > 1) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, + line_phono_texts[uinfo->value.enumerated.item]); + return 0; +} + +static int usb6fire_control_line_phono_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct control_runtime *rt = snd_kcontrol_chip(kcontrol); + int changed = 0; + if (rt->line_phono_switch != ucontrol->value.integer.value[0]) { + rt->line_phono_switch = ucontrol->value.integer.value[0]; + usb6fire_control_line_phono_update(rt); + changed = 1; + } + return changed; +} + +static int usb6fire_control_line_phono_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct control_runtime *rt = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = rt->line_phono_switch; + return 0; +} + +static int usb6fire_control_opt_coax_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item > 1) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, + opt_coax_texts[uinfo->value.enumerated.item]); + return 0; +} + +static int usb6fire_control_opt_coax_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct control_runtime *rt = snd_kcontrol_chip(kcontrol); + int changed = 0; + + if (rt->opt_coax_switch != ucontrol->value.enumerated.item[0]) { + rt->opt_coax_switch = ucontrol->value.enumerated.item[0]; + usb6fire_control_opt_coax_update(rt); + changed = 1; + } + return changed; +} + +static int usb6fire_control_opt_coax_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct control_runtime *rt = snd_kcontrol_chip(kcontrol); + ucontrol->value.enumerated.item[0] = rt->opt_coax_switch; + return 0; +} + +static struct __devinitdata snd_kcontrol_new elements[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Volume", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = usb6fire_control_master_vol_info, + .get = usb6fire_control_master_vol_get, + .put = usb6fire_control_master_vol_put + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Line/Phono Capture Route", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = usb6fire_control_line_phono_info, + .get = usb6fire_control_line_phono_get, + .put = usb6fire_control_line_phono_put + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Opt/Coax Capture Route", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = usb6fire_control_opt_coax_info, + .get = usb6fire_control_opt_coax_get, + .put = usb6fire_control_opt_coax_put + }, + {} +}; + +int __devinit usb6fire_control_init(struct sfire_chip *chip) +{ + int i; + int ret; + struct control_runtime *rt = kzalloc(sizeof(struct control_runtime), + GFP_KERNEL); + struct comm_runtime *comm_rt = chip->comm; + + if (!rt) + return -ENOMEM; + + rt->chip = chip; + + i = 0; + while (init_data[i].type) { + comm_rt->write8(comm_rt, init_data[i].type, init_data[i].reg, + init_data[i].value); + i++; + } + + usb6fire_control_opt_coax_update(rt); + usb6fire_control_line_phono_update(rt); + usb6fire_control_master_vol_update(rt); + + i = 0; + while (elements[i].name) { + ret = snd_ctl_add(chip->card, snd_ctl_new1(&elements[i], rt)); + if (ret < 0) { + kfree(rt); + snd_printk(KERN_ERR PREFIX "cannot add control.\n"); + return ret; + } + i++; + } + + chip->control = rt; + return 0; +} + +void usb6fire_control_abort(struct sfire_chip *chip) +{} + +void usb6fire_control_destroy(struct sfire_chip *chip) +{ + kfree(chip->control); + chip->control = NULL; +} diff --git a/sound/usb/6fire/control.h b/sound/usb/6fire/control.h new file mode 100644 index 000000000000..b534c777ab02 --- /dev/null +++ b/sound/usb/6fire/control.h @@ -0,0 +1,37 @@ +/* + * Linux driver for TerraTec DMX 6Fire USB + * + * Author: Torsten Schenk <torsten.schenk@zoho.com> + * Created: Jan 01, 2011 + * Version: 0.3.0 + * Copyright: (C) Torsten Schenk + * + * 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 USB6FIRE_CONTROL_H +#define USB6FIRE_CONTROL_H + +#include "common.h" + +enum { + CONTROL_MAX_ELEMENTS = 32 +}; + +struct control_runtime { + struct sfire_chip *chip; + + struct snd_kcontrol *element[CONTROL_MAX_ELEMENTS]; + bool opt_coax_switch; + bool line_phono_switch; + u8 master_vol; +}; + +int __devinit usb6fire_control_init(struct sfire_chip *chip); +void usb6fire_control_abort(struct sfire_chip *chip); +void usb6fire_control_destroy(struct sfire_chip *chip); +#endif /* USB6FIRE_CONTROL_H */ + diff --git a/sound/usb/6fire/firmware.c b/sound/usb/6fire/firmware.c new file mode 100644 index 000000000000..9081a54a9c6c --- /dev/null +++ b/sound/usb/6fire/firmware.c @@ -0,0 +1,426 @@ +/* + * Linux driver for TerraTec DMX 6Fire USB + * + * Firmware loader + * + * Currently not working for all devices. To be able to use the device + * in linux, it is also possible to let the windows driver upload the firmware. + * For that, start the computer in windows and reboot. + * As long as the device is connected to the power supply, no firmware reload + * needs to be performed. + * + * Author: Torsten Schenk <torsten.schenk@zoho.com> + * Created: Jan 01, 2011 + * Version: 0.3.0 + * Copyright: (C) Torsten Schenk + * + * 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/firmware.h> + +#include "firmware.h" +#include "chip.h" + +MODULE_FIRMWARE("6fire/dmx6firel2.ihx"); +MODULE_FIRMWARE("6fire/dmx6fireap.ihx"); +MODULE_FIRMWARE("6fire/dmx6firecf.bin"); + +enum { + FPGA_BUFSIZE = 512, FPGA_EP = 2 +}; + +static const u8 BIT_REVERSE_TABLE[256] = { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, + 0xd0, 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, + 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, + 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, + 0x34, 0xb4, 0x74, 0xf4, 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, + 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, + 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, + 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 0x06, 0x86, 0x46, + 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, + 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, + 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1, + 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, + 0xf1, 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, + 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, + 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, + 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, + 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, 0x0b, + 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, + 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, + 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, 0x0f, 0x8f, + 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, + 0xbf, 0x7f, 0xff }; + +/* + * wMaxPacketSize of pcm endpoints. + * keep synced with rates_in_packet_size and rates_out_packet_size in pcm.c + * fpp: frames per isopacket + * + * CAUTION: keep sizeof <= buffer[] in usb6fire_fw_init + */ +static const u8 ep_w_max_packet_size[] = { + 0xe4, 0x00, 0xe4, 0x00, /* alt 1: 228 EP2 and EP6 (7 fpp) */ + 0xa4, 0x01, 0xa4, 0x01, /* alt 2: 420 EP2 and EP6 (13 fpp)*/ + 0x94, 0x01, 0x5c, 0x02 /* alt 3: 404 EP2 and 604 EP6 (25 fpp) */ +}; + +struct ihex_record { + u16 address; + u8 len; + u8 data[256]; + char error; /* true if an error occured parsing this record */ + + u8 max_len; /* maximum record length in whole ihex */ + + /* private */ + const char *txt_data; + unsigned int txt_length; + unsigned int txt_offset; /* current position in txt_data */ +}; + +static u8 usb6fire_fw_ihex_nibble(const u8 n) +{ + if (n >= '0' && n <= '9') + return n - '0'; + else if (n >= 'A' && n <= 'F') + return n - ('A' - 10); + else if (n >= 'a' && n <= 'f') + return n - ('a' - 10); + return 0; +} + +static u8 usb6fire_fw_ihex_hex(const u8 *data, u8 *crc) +{ + u8 val = (usb6fire_fw_ihex_nibble(data[0]) << 4) | + usb6fire_fw_ihex_nibble(data[1]); + *crc += val; + return val; +} + +/* + * returns true if record is available, false otherwise. + * iff an error occured, false will be returned and record->error will be true. + */ +static bool usb6fire_fw_ihex_next_record(struct ihex_record *record) +{ + u8 crc = 0; + u8 type; + int i; + + record->error = false; + + /* find begin of record (marked by a colon) */ + while (record->txt_offset < record->txt_length + && record->txt_data[record->txt_offset] != ':') + record->txt_offset++; + if (record->txt_offset == record->txt_length) + return false; + + /* number of characters needed for len, addr and type entries */ + record->txt_offset++; + if (record->txt_offset + 8 > record->txt_length) { + record->error = true; + return false; + } + + record->len = usb6fire_fw_ihex_hex(record->txt_data + + record->txt_offset, &crc); + record->txt_offset += 2; + record->address = usb6fire_fw_ihex_hex(record->txt_data + + record->txt_offset, &crc) << 8; + record->txt_offset += 2; + record->address |= usb6fire_fw_ihex_hex(record->txt_data + + record->txt_offset, &crc); + record->txt_offset += 2; + type = usb6fire_fw_ihex_hex(record->txt_data + + record->txt_offset, &crc); + record->txt_offset += 2; + + /* number of characters needed for data and crc entries */ + if (record->txt_offset + 2 * (record->len + 1) > record->txt_length) { + record->error = true; + return false; + } + for (i = 0; i < record->len; i++) { + record->data[i] = usb6fire_fw_ihex_hex(record->txt_data + + record->txt_offset, &crc); + record->txt_offset += 2; + } + usb6fire_fw_ihex_hex(record->txt_data + record->txt_offset, &crc); + if (crc) { + record->error = true; + return false; + } + + if (type == 1 || !record->len) /* eof */ + return false; + else if (type == 0) + return true; + else { + record->error = true; + return false; + } +} + +static int usb6fire_fw_ihex_init(const struct firmware *fw, + struct ihex_record *record) +{ + record->txt_data = fw->data; + record->txt_length = fw->size; + record->txt_offset = 0; + record->max_len = 0; + /* read all records, if loop ends, record->error indicates, + * whether ihex is valid. */ + while (usb6fire_fw_ihex_next_record(record)) + record->max_len = max(record->len, record->max_len); + if (record->error) + return -EINVAL; + record->txt_offset = 0; + return 0; +} + +static int usb6fire_fw_ezusb_write(struct usb_device *device, + int type, int value, char *data, int len) +{ + int ret; + + ret = usb_control_msg(device, usb_sndctrlpipe(device, 0), type, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, 0, data, len, HZ); + if (ret < 0) + return ret; + else if (ret != len) + return -EIO; + return 0; +} + +static int usb6fire_fw_ezusb_read(struct usb_device *device, + int type, int value, char *data, int len) +{ + int ret = usb_control_msg(device, usb_rcvctrlpipe(device, 0), type, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, + 0, data, len, HZ); + if (ret < 0) + return ret; + else if (ret != len) + return -EIO; + return 0; +} + +static int usb6fire_fw_fpga_write(struct usb_device *device, + char *data, int len) +{ + int actual_len; + int ret; + + ret = usb_bulk_msg(device, usb_sndbulkpipe(device, FPGA_EP), data, len, + &actual_len, HZ); + if (ret < 0) + return ret; + else if (actual_len != len) + return -EIO; + return 0; +} + +static int usb6fire_fw_ezusb_upload( + struct usb_interface *intf, const char *fwname, + unsigned int postaddr, u8 *postdata, unsigned int postlen) +{ + int ret; + u8 data; + struct usb_device *device = interface_to_usbdev(intf); + const struct firmware *fw = 0; + struct ihex_record *rec = kmalloc(sizeof(struct ihex_record), + GFP_KERNEL); + + if (!rec) + return -ENOMEM; + + ret = request_firmware(&fw, fwname, &device->dev); + if (ret < 0) { + kfree(rec); + snd_printk(KERN_ERR PREFIX "error requesting ezusb " + "firmware %s.\n", fwname); + return ret; + } + ret = usb6fire_fw_ihex_init(fw, rec); + if (ret < 0) { + kfree(rec); + snd_printk(KERN_ERR PREFIX "error validating ezusb " + "firmware %s.\n", fwname); + return ret; + } + /* upload firmware image */ + data = 0x01; /* stop ezusb cpu */ + ret = usb6fire_fw_ezusb_write(device, 0xa0, 0xe600, &data, 1); + if (ret < 0) { + kfree(rec); + release_firmware(fw); + snd_printk(KERN_ERR PREFIX "unable to upload ezusb " + "firmware %s: begin message.\n", fwname); + return ret; + } + + while (usb6fire_fw_ihex_next_record(rec)) { /* write firmware */ + ret = usb6fire_fw_ezusb_write(device, 0xa0, rec->address, + rec->data, rec->len); + if (ret < 0) { + kfree(rec); + release_firmware(fw); + snd_printk(KERN_ERR PREFIX "unable to upload ezusb " + "firmware %s: data urb.\n", fwname); + return ret; + } + } + + release_firmware(fw); + kfree(rec); + if (postdata) { /* write data after firmware has been uploaded */ + ret = usb6fire_fw_ezusb_write(device, 0xa0, postaddr, + postdata, postlen); + if (ret < 0) { + snd_printk(KERN_ERR PREFIX "unable to upload ezusb " + "firmware %s: post urb.\n", fwname); + return ret; + } + } + + data = 0x00; /* resume ezusb cpu */ + ret = usb6fire_fw_ezusb_write(device, 0xa0, 0xe600, &data, 1); + if (ret < 0) { + release_firmware(fw); + snd_printk(KERN_ERR PREFIX "unable to upload ezusb " + "firmware %s: end message.\n", fwname); + return ret; + } + return 0; +} + +static int usb6fire_fw_fpga_upload( + struct usb_interface *intf, const char *fwname) +{ + int ret; + int i; + struct usb_device *device = interface_to_usbdev(intf); + u8 *buffer = kmalloc(FPGA_BUFSIZE, GFP_KERNEL); + const char *c; + const char *end; + const struct firmware *fw; + + if (!buffer) + return -ENOMEM; + + ret = request_firmware(&fw, fwname, &device->dev); + if (ret < 0) { + snd_printk(KERN_ERR PREFIX "unable to get fpga firmware %s.\n", + fwname); + kfree(buffer); + return -EIO; + } + + c = fw->data; + end = fw->data + fw->size; + + ret = usb6fire_fw_ezusb_write(device, 8, 0, NULL, 0); + if (ret < 0) { + kfree(buffer); + release_firmware(fw); + snd_printk(KERN_ERR PREFIX "unable to upload fpga firmware: " + "begin urb.\n"); + return ret; + } + + while (c != end) { + for (i = 0; c != end && i < FPGA_BUFSIZE; i++, c++) + buffer[i] = BIT_REVERSE_TABLE[(u8) *c]; + + ret = usb6fire_fw_fpga_write(device, buffer, i); + if (ret < 0) { + release_firmware(fw); + kfree(buffer); + snd_printk(KERN_ERR PREFIX "unable to upload fpga " + "firmware: fw urb.\n"); + return ret; + } + } + release_firmware(fw); + kfree(buffer); + + ret = usb6fire_fw_ezusb_write(device, 9, 0, NULL, 0); + if (ret < 0) { + snd_printk(KERN_ERR PREFIX "unable to upload fpga firmware: " + "end urb.\n"); + return ret; + } + return 0; +} + +int usb6fire_fw_init(struct usb_interface *intf) +{ + int i; + int ret; + struct usb_device *device = interface_to_usbdev(intf); + /* buffer: 8 receiving bytes from device and + * sizeof(EP_W_MAX_PACKET_SIZE) bytes for non-const copy */ + u8 buffer[12]; + + ret = usb6fire_fw_ezusb_read(device, 1, 0, buffer, 8); + if (ret < 0) { + snd_printk(KERN_ERR PREFIX "unable to receive device " + "firmware state.\n"); + return ret; + } + if (buffer[0] != 0xeb || buffer[1] != 0xaa || buffer[2] != 0x55 + || buffer[4] != 0x03 || buffer[5] != 0x01 || buffer[7] + != 0x00) { + snd_printk(KERN_ERR PREFIX "unknown device firmware state " + "received from device: "); + for (i = 0; i < 8; i++) + snd_printk("%02x ", buffer[i]); + snd_printk("\n"); + return -EIO; + } + /* do we need fpga loader ezusb firmware? */ + if (buffer[3] == 0x01 && buffer[6] == 0x19) { + ret = usb6fire_fw_ezusb_upload(intf, + "6fire/dmx6firel2.ihx", 0, NULL, 0); + if (ret < 0) + return ret; + return FW_NOT_READY; + } + /* do we need fpga firmware and application ezusb firmware? */ + else if (buffer[3] == 0x02 && buffer[6] == 0x0b) { + ret = usb6fire_fw_fpga_upload(intf, "6fire/dmx6firecf.bin"); + if (ret < 0) + return ret; + memcpy(buffer, ep_w_max_packet_size, + sizeof(ep_w_max_packet_size)); + ret = usb6fire_fw_ezusb_upload(intf, "6fire/dmx6fireap.ihx", + 0x0003, buffer, sizeof(ep_w_max_packet_size)); + if (ret < 0) + return ret; + return FW_NOT_READY; + } + /* all fw loaded? */ + else if (buffer[3] == 0x03 && buffer[6] == 0x0b) + return 0; + /* unknown data? */ + else { + snd_printk(KERN_ERR PREFIX "unknown device firmware state " + "received from device: "); + for (i = 0; i < 8; i++) + snd_printk("%02x ", buffer[i]); + snd_printk("\n"); + return -EIO; + } + return 0; +} + diff --git a/sound/usb/6fire/firmware.h b/sound/usb/6fire/firmware.h new file mode 100644 index 000000000000..008569895381 --- /dev/null +++ b/sound/usb/6fire/firmware.h @@ -0,0 +1,27 @@ +/* + * Linux driver for TerraTec DMX 6Fire USB + * + * Author: Torsten Schenk + * Created: Jan 01, 2011 + * Copyright: (C) Torsten Schenk + * + * 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 USB6FIRE_FIRMWARE_H +#define USB6FIRE_FIRMWARE_H + +#include "common.h" + +enum /* firmware state of device */ +{ + FW_READY = 0, + FW_NOT_READY = 1 +}; + +int __devinit usb6fire_fw_init(struct usb_interface *intf); +#endif /* USB6FIRE_FIRMWARE_H */ + diff --git a/sound/usb/6fire/midi.c b/sound/usb/6fire/midi.c new file mode 100644 index 000000000000..13f4509dce2b --- /dev/null +++ b/sound/usb/6fire/midi.c @@ -0,0 +1,203 @@ +/* + * Linux driver for TerraTec DMX 6Fire USB + * + * Rawmidi driver + * + * Author: Torsten Schenk <torsten.schenk@zoho.com> + * Created: Jan 01, 2011 + * Version: 0.3.0 + * Copyright: (C) Torsten Schenk + * + * 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 <sound/rawmidi.h> + +#include "midi.h" +#include "chip.h" +#include "comm.h" + +static void usb6fire_midi_out_handler(struct urb *urb) +{ + struct midi_runtime *rt = urb->context; + int ret; + unsigned long flags; + + spin_lock_irqsave(&rt->out_lock, flags); + + if (rt->out) { + ret = snd_rawmidi_transmit(rt->out, rt->out_buffer + 4, + MIDI_BUFSIZE - 4); + if (ret > 0) { /* more data available, send next packet */ + rt->out_buffer[1] = ret + 2; + rt->out_buffer[3] = rt->out_serial++; + urb->transfer_buffer_length = ret + 4; + + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret < 0) + snd_printk(KERN_ERR PREFIX "midi out urb " + "submit failed: %d\n", ret); + } else /* no more data to transmit */ + rt->out = NULL; + } + spin_unlock_irqrestore(&rt->out_lock, flags); +} + +static void usb6fire_midi_in_received( + struct midi_runtime *rt, u8 *data, int length) +{ + unsigned long flags; + + spin_lock_irqsave(&rt->in_lock, flags); + if (rt->in) + snd_rawmidi_receive(rt->in, data, length); + spin_unlock_irqrestore(&rt->in_lock, flags); +} + +static int usb6fire_midi_out_open(struct snd_rawmidi_substream *alsa_sub) +{ + return 0; +} + +static int usb6fire_midi_out_close(struct snd_rawmidi_substream *alsa_sub) +{ + return 0; +} + +static void usb6fire_midi_out_trigger( + struct snd_rawmidi_substream *alsa_sub, int up) +{ + struct midi_runtime *rt = alsa_sub->rmidi->private_data; + struct urb *urb = &rt->out_urb; + __s8 ret; + unsigned long flags; + + spin_lock_irqsave(&rt->out_lock, flags); + if (up) { /* start transfer */ + if (rt->out) { /* we are already transmitting so just return */ + spin_unlock_irqrestore(&rt->out_lock, flags); + return; + } + + ret = snd_rawmidi_transmit(alsa_sub, rt->out_buffer + 4, + MIDI_BUFSIZE - 4); + if (ret > 0) { + rt->out_buffer[1] = ret + 2; + rt->out_buffer[3] = rt->out_serial++; + urb->transfer_buffer_length = ret + 4; + + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret < 0) + snd_printk(KERN_ERR PREFIX "midi out urb " + "submit failed: %d\n", ret); + else + rt->out = alsa_sub; + } + } else if (rt->out == alsa_sub) + rt->out = NULL; + spin_unlock_irqrestore(&rt->out_lock, flags); +} + +static void usb6fire_midi_out_drain(struct snd_rawmidi_substream *alsa_sub) +{ + struct midi_runtime *rt = alsa_sub->rmidi->private_data; + int retry = 0; + + while (rt->out && retry++ < 100) + msleep(10); +} + +static int usb6fire_midi_in_open(struct snd_rawmidi_substream *alsa_sub) +{ + return 0; +} + +static int usb6fire_midi_in_close(struct snd_rawmidi_substream *alsa_sub) +{ + return 0; +} + +static void usb6fire_midi_in_trigger( + struct snd_rawmidi_substream *alsa_sub, int up) +{ + struct midi_runtime *rt = alsa_sub->rmidi->private_data; + unsigned long flags; + + spin_lock_irqsave(&rt->in_lock, flags); + if (up) + rt->in = alsa_sub; + else + rt->in = NULL; + spin_unlock_irqrestore(&rt->in_lock, flags); +} + +static struct snd_rawmidi_ops out_ops = { + .open = usb6fire_midi_out_open, + .close = usb6fire_midi_out_close, + .trigger = usb6fire_midi_out_trigger, + .drain = usb6fire_midi_out_drain +}; + +static struct snd_rawmidi_ops in_ops = { + .open = usb6fire_midi_in_open, + .close = usb6fire_midi_in_close, + .trigger = usb6fire_midi_in_trigger +}; + +int __devinit usb6fire_midi_init(struct sfire_chip *chip) +{ + int ret; + struct midi_runtime *rt = kzalloc(sizeof(struct midi_runtime), + GFP_KERNEL); + struct comm_runtime *comm_rt = chip->comm; + + if (!rt) + return -ENOMEM; + + rt->chip = chip; + rt->in_received = usb6fire_midi_in_received; + rt->out_buffer[0] = 0x80; /* 'send midi' command */ + rt->out_buffer[1] = 0x00; /* size of data */ + rt->out_buffer[2] = 0x00; /* always 0 */ + spin_lock_init(&rt->in_lock); + spin_lock_init(&rt->out_lock); + + comm_rt->init_urb(comm_rt, &rt->out_urb, rt->out_buffer, rt, + usb6fire_midi_out_handler); + + ret = snd_rawmidi_new(chip->card, "6FireUSB", 0, 1, 1, &rt->instance); + if (ret < 0) { + kfree(rt); + snd_printk(KERN_ERR PREFIX "unable to create midi.\n"); + return ret; + } + rt->instance->private_data = rt; + strcpy(rt->instance->name, "DMX6FireUSB MIDI"); + rt->instance->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + snd_rawmidi_set_ops(rt->instance, SNDRV_RAWMIDI_STREAM_OUTPUT, + &out_ops); + snd_rawmidi_set_ops(rt->instance, SNDRV_RAWMIDI_STREAM_INPUT, + &in_ops); + + chip->midi = rt; + return 0; +} + +void usb6fire_midi_abort(struct sfire_chip *chip) +{ + struct midi_runtime *rt = chip->midi; + + if (rt) + usb_poison_urb(&rt->out_urb); +} + +void usb6fire_midi_destroy(struct sfire_chip *chip) +{ + kfree(chip->midi); + chip->midi = NULL; +} diff --git a/sound/usb/6fire/midi.h b/sound/usb/6fire/midi.h new file mode 100644 index 000000000000..97a7bf669135 --- /dev/null +++ b/sound/usb/6fire/midi.h @@ -0,0 +1,46 @@ +/* + * Linux driver for TerraTec DMX 6Fire USB + * + * Author: Torsten Schenk <torsten.schenk@zoho.com> + * Created: Jan 01, 2011 + * Version: 0.3.0 + * Copyright: (C) Torsten Schenk + * + * 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 USB6FIRE_MIDI_H +#define USB6FIRE_MIDI_H + +#include "common.h" + +enum { + MIDI_BUFSIZE = 64 +}; + +struct midi_runtime { + struct sfire_chip *chip; + struct snd_rawmidi *instance; + + struct snd_rawmidi_substream *in; + char in_active; + + spinlock_t in_lock; + spinlock_t out_lock; + struct snd_rawmidi_substream *out; + struct urb out_urb; + u8 out_serial; /* serial number of out packet */ + u8 out_buffer[MIDI_BUFSIZE]; + int buffer_offset; + + void (*in_received)(struct midi_runtime *rt, u8 *data, int length); +}; + +int __devinit usb6fire_midi_init(struct sfire_chip *chip); +void usb6fire_midi_abort(struct sfire_chip *chip); +void usb6fire_midi_destroy(struct sfire_chip *chip); +#endif /* USB6FIRE_MIDI_H */ + diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c new file mode 100644 index 000000000000..ba62c7468ba8 --- /dev/null +++ b/sound/usb/6fire/pcm.c @@ -0,0 +1,688 @@ +/* + * Linux driver for TerraTec DMX 6Fire USB + * + * PCM driver + * + * Author: Torsten Schenk <torsten.schenk@zoho.com> + * Created: Jan 01, 2011 + * Version: 0.3.0 + * Copyright: (C) Torsten Schenk + * + * 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 "pcm.h" +#include "chip.h" +#include "comm.h" + +enum { + OUT_N_CHANNELS = 6, IN_N_CHANNELS = 4 +}; + +/* keep next two synced with + * FW_EP_W_MAX_PACKET_SIZE[] and RATES_MAX_PACKET_SIZE */ +static const int rates_in_packet_size[] = { 228, 228, 420, 420, 404, 404 }; +static const int rates_out_packet_size[] = { 228, 228, 420, 420, 604, 604 }; +static const int rates[] = { 44100, 48000, 88200, 96000, 176400, 192000 }; +static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 }; +static const int rates_alsaid[] = { + SNDRV_PCM_RATE_44100, SNDRV_PCM_RATE_48000, + SNDRV_PCM_RATE_88200, SNDRV_PCM_RATE_96000, + SNDRV_PCM_RATE_176400, SNDRV_PCM_RATE_192000 }; + +/* values to write to soundcard register for all samplerates */ +static const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01}; +static const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00}; + +enum { /* settings for pcm */ + OUT_EP = 6, IN_EP = 2, MAX_BUFSIZE = 128 * 1024 +}; + +enum { /* pcm streaming states */ + STREAM_DISABLED, /* no pcm streaming */ + STREAM_STARTING, /* pcm streaming requested, waiting to become ready */ + STREAM_RUNNING, /* pcm streaming running */ + STREAM_STOPPING +}; + +enum { /* pcm sample rates (also index into RATES_XXX[]) */ + RATE_44KHZ, + RATE_48KHZ, + RATE_88KHZ, + RATE_96KHZ, + RATE_176KHZ, + RATE_192KHZ +}; + +static const struct snd_pcm_hardware pcm_hw = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BATCH, + + .formats = SNDRV_PCM_FMTBIT_S24_LE, + + .rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + + .rate_min = 44100, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 0, /* set in pcm_open, depending on capture/playback */ + .buffer_bytes_max = MAX_BUFSIZE, + .period_bytes_min = PCM_N_PACKETS_PER_URB * (PCM_MAX_PACKET_SIZE - 4), + .period_bytes_max = MAX_BUFSIZE, + .periods_min = 2, + .periods_max = 1024 +}; + +static int usb6fire_pcm_set_rate(struct pcm_runtime *rt) +{ + int ret; + struct usb_device *device = rt->chip->dev; + struct comm_runtime *comm_rt = rt->chip->comm; + + if (rt->rate >= ARRAY_SIZE(rates)) + return -EINVAL; + /* disable streaming */ + ret = comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, 0x00); + if (ret < 0) { + snd_printk(KERN_ERR PREFIX "error stopping streaming while " + "setting samplerate %d.\n", rates[rt->rate]); + return ret; + } + + ret = usb_set_interface(device, 1, rates_altsetting[rt->rate]); + if (ret < 0) { + snd_printk(KERN_ERR PREFIX "error setting interface " + "altsetting %d for samplerate %d.\n", + rates_altsetting[rt->rate], rates[rt->rate]); + return ret; + } + + /* set soundcard clock */ + ret = comm_rt->write16(comm_rt, 0x02, 0x01, rates_6fire_vl[rt->rate], + rates_6fire_vh[rt->rate]); + if (ret < 0) { + snd_printk(KERN_ERR PREFIX "error setting samplerate %d.\n", + rates[rt->rate]); + return ret; + } + + /* enable analog inputs and outputs + * (one bit per stereo-channel) */ + ret = comm_rt->write16(comm_rt, 0x02, 0x02, + (1 << (OUT_N_CHANNELS / 2)) - 1, + (1 << (IN_N_CHANNELS / 2)) - 1); + if (ret < 0) { + snd_printk(KERN_ERR PREFIX "error initializing analog channels " + "while setting samplerate %d.\n", + rates[rt->rate]); + return ret; + } + /* disable digital inputs and outputs */ + ret = comm_rt->write16(comm_rt, 0x02, 0x03, 0x00, 0x00); + if (ret < 0) { + snd_printk(KERN_ERR PREFIX "error initializing digital " + "channels while setting samplerate %d.\n", + rates[rt->rate]); + return ret; + } + + ret = comm_rt->write16(comm_rt, 0x02, 0x00, 0x00, 0x01); + if (ret < 0) { + snd_printk(KERN_ERR PREFIX "error starting streaming while " + "setting samplerate %d.\n", rates[rt->rate]); + return ret; + } + + rt->in_n_analog = IN_N_CHANNELS; + rt->out_n_analog = OUT_N_CHANNELS; + rt->in_packet_size = rates_in_packet_size[rt->rate]; + rt->out_packet_size = rates_out_packet_size[rt->rate]; + return 0; +} + +static struct pcm_substream *usb6fire_pcm_get_substream( + struct snd_pcm_substream *alsa_sub) +{ + struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub); + + if (alsa_sub->stream == SNDRV_PCM_STREAM_PLAYBACK) + return &rt->playback; + else if (alsa_sub->stream == SNDRV_PCM_STREAM_CAPTURE) + return &rt->capture; + snd_printk(KERN_ERR PREFIX "error getting pcm substream slot.\n"); + return NULL; +} + +/* call with stream_mutex locked */ +static void usb6fire_pcm_stream_stop(struct pcm_runtime *rt) +{ + int i; + + if (rt->stream_state != STREAM_DISABLED) { + for (i = 0; i < PCM_N_URBS; i++) { + usb_kill_urb(&rt->in_urbs[i].instance); + usb_kill_urb(&rt->out_urbs[i].instance); + } + rt->stream_state = STREAM_DISABLED; + } +} + +/* call with stream_mutex locked */ +static int usb6fire_pcm_stream_start(struct pcm_runtime *rt) +{ + int ret; + int i; + int k; + struct usb_iso_packet_descriptor *packet; + + if (rt->stream_state == STREAM_DISABLED) { + /* submit our in urbs */ + rt->stream_wait_cond = false; + rt->stream_state = STREAM_STARTING; + for (i = 0; i < PCM_N_URBS; i++) { + for (k = 0; k < PCM_N_PACKETS_PER_URB; k++) { + packet = &rt->in_urbs[i].packets[k]; + packet->offset = k * rt->in_packet_size; + packet->length = rt->in_packet_size; + packet->actual_length = 0; + packet->status = 0; + } + ret = usb_submit_urb(&rt->in_urbs[i].instance, + GFP_ATOMIC); + if (ret) { + usb6fire_pcm_stream_stop(rt); + return ret; + } + } + + /* wait for first out urb to return (sent in in urb handler) */ + wait_event_timeout(rt->stream_wait_queue, rt->stream_wait_cond, + HZ); + if (rt->stream_wait_cond) + rt->stream_state = STREAM_RUNNING; + else { + usb6fire_pcm_stream_stop(rt); + return -EIO; + } + } + return 0; +} + +/* call with substream locked */ +static void usb6fire_pcm_capture(struct pcm_substream *sub, struct pcm_urb *urb) +{ + int i; + int frame; + int frame_count; + unsigned int total_length = 0; + struct pcm_runtime *rt = snd_pcm_substream_chip(sub->instance); + struct snd_pcm_runtime *alsa_rt = sub->instance->runtime; + u32 *src = (u32 *) urb->buffer; + u32 *dest = (u32 *) (alsa_rt->dma_area + sub->dma_off + * (alsa_rt->frame_bits >> 3)); + u32 *dest_end = (u32 *) (alsa_rt->dma_area + alsa_rt->buffer_size + * (alsa_rt->frame_bits >> 3)); + int bytes_per_frame = alsa_rt->channels << 2; + + for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) { + /* at least 4 header bytes for valid packet. + * after that: 32 bits per sample for analog channels */ + if (urb->packets[i].actual_length > 4) + frame_count = (urb->packets[i].actual_length - 4) + / (rt->in_n_analog << 2); + else + frame_count = 0; + + src = (u32 *) (urb->buffer + total_length); + src++; /* skip leading 4 bytes of every packet */ + total_length += urb->packets[i].length; + for (frame = 0; frame < frame_count; frame++) { + memcpy(dest, src, bytes_per_frame); + dest += alsa_rt->channels; + src += rt->in_n_analog; + sub->dma_off++; + sub->period_off++; + if (dest == dest_end) { + sub->dma_off = 0; + dest = (u32 *) alsa_rt->dma_area; + } + } + } +} + +/* call with substream locked */ +static void usb6fire_pcm_playback(struct pcm_substream *sub, + struct pcm_urb *urb) +{ + int i; + int frame; + int frame_count; + struct pcm_runtime *rt = snd_pcm_substream_chip(sub->instance); + struct snd_pcm_runtime *alsa_rt = sub->instance->runtime; + u32 *src = (u32 *) (alsa_rt->dma_area + sub->dma_off + * (alsa_rt->frame_bits >> 3)); + u32 *src_end = (u32 *) (alsa_rt->dma_area + alsa_rt->buffer_size + * (alsa_rt->frame_bits >> 3)); + u32 *dest = (u32 *) urb->buffer; + int bytes_per_frame = alsa_rt->channels << 2; + + for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) { + /* at least 4 header bytes for valid packet. + * after that: 32 bits per sample for analog channels */ + if (urb->packets[i].length > 4) + frame_count = (urb->packets[i].length - 4) + / (rt->out_n_analog << 2); + else + frame_count = 0; + dest++; /* skip leading 4 bytes of every frame */ + for (frame = 0; frame < frame_count; frame++) { + memcpy(dest, src, bytes_per_frame); + src += alsa_rt->channels; + dest += rt->out_n_analog; + sub->dma_off++; + sub->period_off++; + if (src == src_end) { + src = (u32 *) alsa_rt->dma_area; + sub->dma_off = 0; + } + } + } +} + +static void usb6fire_pcm_in_urb_handler(struct urb *usb_urb) +{ + struct pcm_urb *in_urb = usb_urb->context; + struct pcm_urb *out_urb = in_urb->peer; + struct pcm_runtime *rt = in_urb->chip->pcm; + struct pcm_substream *sub; + unsigned long flags; + int total_length = 0; + int frame_count; + int frame; + int channel; + int i; + u8 *dest; + + if (usb_urb->status || rt->panic || rt->stream_state == STREAM_STOPPING) + return; + for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) + if (in_urb->packets[i].status) { + rt->panic = true; + return; + } + + if (rt->stream_state == STREAM_DISABLED) { + snd_printk(KERN_ERR PREFIX "internal error: " + "stream disabled in in-urb handler.\n"); + return; + } + + /* receive our capture data */ + sub = &rt->capture; + spin_lock_irqsave(&sub->lock, flags); + if (sub->active) { + usb6fire_pcm_capture(sub, in_urb); + if (sub->period_off >= sub->instance->runtime->period_size) { + sub->period_off %= sub->instance->runtime->period_size; + spin_unlock_irqrestore(&sub->lock, flags); + snd_pcm_period_elapsed(sub->instance); + } else + spin_unlock_irqrestore(&sub->lock, flags); + } else + spin_unlock_irqrestore(&sub->lock, flags); + + /* setup out urb structure */ + for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) { + out_urb->packets[i].offset = total_length; + out_urb->packets[i].length = (in_urb->packets[i].actual_length + - 4) / (rt->in_n_analog << 2) + * (rt->out_n_analog << 2) + 4; + out_urb->packets[i].status = 0; + total_length += out_urb->packets[i].length; + } + memset(out_urb->buffer, 0, total_length); + + /* now send our playback data (if a free out urb was found) */ + sub = &rt->playback; + spin_lock_irqsave(&sub->lock, flags); + if (sub->active) { + usb6fire_pcm_playback(sub, out_urb); + if (sub->period_off >= sub->instance->runtime->period_size) { + sub->period_off %= sub->instance->runtime->period_size; + spin_unlock_irqrestore(&sub->lock, flags); + snd_pcm_period_elapsed(sub->instance); + } else + spin_unlock_irqrestore(&sub->lock, flags); + } else + spin_unlock_irqrestore(&sub->lock, flags); + + /* setup the 4th byte of each sample (0x40 for analog channels) */ + dest = out_urb->buffer; + for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) + if (out_urb->packets[i].length >= 4) { + frame_count = (out_urb->packets[i].length - 4) + / (rt->out_n_analog << 2); + *(dest++) = 0xaa; + *(dest++) = 0xaa; + *(dest++) = frame_count; + *(dest++) = 0x00; + for (frame = 0; frame < frame_count; frame++) + for (channel = 0; + channel < rt->out_n_analog; + channel++) { + dest += 3; /* skip sample data */ + *(dest++) = 0x40; + } + } + usb_submit_urb(&out_urb->instance, GFP_ATOMIC); + usb_submit_urb(&in_urb->instance, GFP_ATOMIC); +} + +static void usb6fire_pcm_out_urb_handler(struct urb *usb_urb) +{ + struct pcm_urb *urb = usb_urb->context; + struct pcm_runtime *rt = urb->chip->pcm; + + if (rt->stream_state == STREAM_STARTING) { + rt->stream_wait_cond = true; + wake_up(&rt->stream_wait_queue); + } +} + +static int usb6fire_pcm_open(struct snd_pcm_substream *alsa_sub) +{ + struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub); + struct pcm_substream *sub = NULL; + struct snd_pcm_runtime *alsa_rt = alsa_sub->runtime; + + if (rt->panic) + return -EPIPE; + + mutex_lock(&rt->stream_mutex); + alsa_rt->hw = pcm_hw; + + if (alsa_sub->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (rt->rate >= 0) + alsa_rt->hw.rates = rates_alsaid[rt->rate]; + alsa_rt->hw.channels_max = OUT_N_CHANNELS; + sub = &rt->playback; + } else if (alsa_sub->stream == SNDRV_PCM_STREAM_CAPTURE) { + if (rt->rate >= 0) + alsa_rt->hw.rates = rates_alsaid[rt->rate]; + alsa_rt->hw.channels_max = IN_N_CHANNELS; + sub = &rt->capture; + } + + if (!sub) { + mutex_unlock(&rt->stream_mutex); + snd_printk(KERN_ERR PREFIX "invalid stream type.\n"); + return -EINVAL; + } + + sub->instance = alsa_sub; + sub->active = false; + mutex_unlock(&rt->stream_mutex); + return 0; +} + +static int usb6fire_pcm_close(struct snd_pcm_substream *alsa_sub) +{ + struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub); + struct pcm_substream *sub = usb6fire_pcm_get_substream(alsa_sub); + unsigned long flags; + + if (rt->panic) + return 0; + + mutex_lock(&rt->stream_mutex); + if (sub) { + /* deactivate substream */ + spin_lock_irqsave(&sub->lock, flags); + sub->instance = NULL; + sub->active = false; + spin_unlock_irqrestore(&sub->lock, flags); + + /* all substreams closed? if so, stop streaming */ + if (!rt->playback.instance && !rt->capture.instance) { + usb6fire_pcm_stream_stop(rt); + rt->rate = -1; + } + } + mutex_unlock(&rt->stream_mutex); + return 0; +} + +static int usb6fire_pcm_hw_params(struct snd_pcm_substream *alsa_sub, + struct snd_pcm_hw_params *hw_params) +{ + return snd_pcm_lib_malloc_pages(alsa_sub, + params_buffer_bytes(hw_params)); +} + +static int usb6fire_pcm_hw_free(struct snd_pcm_substream *alsa_sub) +{ + return snd_pcm_lib_free_pages(alsa_sub); +} + +static int usb6fire_pcm_prepare(struct snd_pcm_substream *alsa_sub) +{ + struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub); + struct pcm_substream *sub = usb6fire_pcm_get_substream(alsa_sub); + struct snd_pcm_runtime *alsa_rt = alsa_sub->runtime; + int i; + int ret; + + if (rt->panic) + return -EPIPE; + if (!sub) + return -ENODEV; + + mutex_lock(&rt->stream_mutex); + sub->dma_off = 0; + sub->period_off = 0; + + if (rt->stream_state == STREAM_DISABLED) { + for (i = 0; i < ARRAY_SIZE(rates); i++) + if (alsa_rt->rate == rates[i]) { + rt->rate = i; + break; + } + if (i == ARRAY_SIZE(rates)) { + mutex_unlock(&rt->stream_mutex); + snd_printk("invalid rate %d in prepare.\n", + alsa_rt->rate); + return -EINVAL; + } + + ret = usb6fire_pcm_set_rate(rt); + if (ret) { + mutex_unlock(&rt->stream_mutex); + return ret; + } + ret = usb6fire_pcm_stream_start(rt); + if (ret) { + mutex_unlock(&rt->stream_mutex); + snd_printk(KERN_ERR PREFIX + "could not start pcm stream.\n"); + return ret; + } + } + mutex_unlock(&rt->stream_mutex); + return 0; +} + +static int usb6fire_pcm_trigger(struct snd_pcm_substream *alsa_sub, int cmd) +{ + struct pcm_substream *sub = usb6fire_pcm_get_substream(alsa_sub); + struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub); + unsigned long flags; + + if (rt->panic) + return -EPIPE; + if (!sub) + return -ENODEV; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + spin_lock_irqsave(&sub->lock, flags); + sub->active = true; + spin_unlock_irqrestore(&sub->lock, flags); + return 0; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + spin_lock_irqsave(&sub->lock, flags); + sub->active = false; + spin_unlock_irqrestore(&sub->lock, flags); + return 0; + + default: + return -EINVAL; + } +} + +static snd_pcm_uframes_t usb6fire_pcm_pointer( + struct snd_pcm_substream *alsa_sub) +{ + struct pcm_substream *sub = usb6fire_pcm_get_substream(alsa_sub); + struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub); + unsigned long flags; + snd_pcm_uframes_t ret; + + if (rt->panic || !sub) + return SNDRV_PCM_STATE_XRUN; + + spin_lock_irqsave(&sub->lock, flags); + ret = sub->dma_off; + spin_unlock_irqrestore(&sub->lock, flags); + return ret; +} + +static struct snd_pcm_ops pcm_ops = { + .open = usb6fire_pcm_open, + .close = usb6fire_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = usb6fire_pcm_hw_params, + .hw_free = usb6fire_pcm_hw_free, + .prepare = usb6fire_pcm_prepare, + .trigger = usb6fire_pcm_trigger, + .pointer = usb6fire_pcm_pointer, +}; + +static void __devinit usb6fire_pcm_init_urb(struct pcm_urb *urb, + struct sfire_chip *chip, bool in, int ep, + void (*handler)(struct urb *)) +{ + urb->chip = chip; + usb_init_urb(&urb->instance); + urb->instance.transfer_buffer = urb->buffer; + urb->instance.transfer_buffer_length = + PCM_N_PACKETS_PER_URB * PCM_MAX_PACKET_SIZE; + urb->instance.dev = chip->dev; + urb->instance.pipe = in ? usb_rcvisocpipe(chip->dev, ep) + : usb_sndisocpipe(chip->dev, ep); + urb->instance.interval = 1; + urb->instance.transfer_flags = URB_ISO_ASAP; + urb->instance.complete = handler; + urb->instance.context = urb; + urb->instance.number_of_packets = PCM_N_PACKETS_PER_URB; +} + +int __devinit usb6fire_pcm_init(struct sfire_chip *chip) +{ + int i; + int ret; + struct snd_pcm *pcm; + struct pcm_runtime *rt = + kzalloc(sizeof(struct pcm_runtime), GFP_KERNEL); + + if (!rt) + return -ENOMEM; + + rt->chip = chip; + rt->stream_state = STREAM_DISABLED; + rt->rate = -1; + init_waitqueue_head(&rt->stream_wait_queue); + mutex_init(&rt->stream_mutex); + + spin_lock_init(&rt->playback.lock); + spin_lock_init(&rt->capture.lock); + + for (i = 0; i < PCM_N_URBS; i++) { + usb6fire_pcm_init_urb(&rt->in_urbs[i], chip, true, IN_EP, + usb6fire_pcm_in_urb_handler); + usb6fire_pcm_init_urb(&rt->out_urbs[i], chip, false, OUT_EP, + usb6fire_pcm_out_urb_handler); + + rt->in_urbs[i].peer = &rt->out_urbs[i]; + rt->out_urbs[i].peer = &rt->in_urbs[i]; + } + + ret = snd_pcm_new(chip->card, "DMX6FireUSB", 0, 1, 1, &pcm); + if (ret < 0) { + kfree(rt); + snd_printk(KERN_ERR PREFIX "cannot create pcm instance.\n"); + return ret; + } + + pcm->private_data = rt; + strcpy(pcm->name, "DMX 6Fire USB"); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_ops); + + ret = snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + MAX_BUFSIZE, MAX_BUFSIZE); + if (ret) { + kfree(rt); + snd_printk(KERN_ERR PREFIX + "error preallocating pcm buffers.\n"); + return ret; + } + rt->instance = pcm; + + chip->pcm = rt; + return 0; +} + +void usb6fire_pcm_abort(struct sfire_chip *chip) +{ + struct pcm_runtime *rt = chip->pcm; + int i; + + if (rt) { + rt->panic = true; + + if (rt->playback.instance) + snd_pcm_stop(rt->playback.instance, + SNDRV_PCM_STATE_XRUN); + if (rt->capture.instance) + snd_pcm_stop(rt->capture.instance, + SNDRV_PCM_STATE_XRUN); + + for (i = 0; i < PCM_N_URBS; i++) { + usb_poison_urb(&rt->in_urbs[i].instance); + usb_poison_urb(&rt->out_urbs[i].instance); + } + + } +} + +void usb6fire_pcm_destroy(struct sfire_chip *chip) +{ + kfree(chip->pcm); + chip->pcm = NULL; +} diff --git a/sound/usb/6fire/pcm.h b/sound/usb/6fire/pcm.h new file mode 100644 index 000000000000..2bee81374002 --- /dev/null +++ b/sound/usb/6fire/pcm.h @@ -0,0 +1,76 @@ +/* + * Linux driver for TerraTec DMX 6Fire USB + * + * Author: Torsten Schenk <torsten.schenk@zoho.com> + * Created: Jan 01, 2011 + * Version: 0.3.0 + * Copyright: (C) Torsten Schenk + * + * 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 USB6FIRE_PCM_H +#define USB6FIRE_PCM_H + +#include <sound/pcm.h> +#include <linux/mutex.h> + +#include "common.h" + +enum /* settings for pcm */ +{ + /* maximum of EP_W_MAX_PACKET_SIZE[] (see firmware.c) */ + PCM_N_URBS = 16, PCM_N_PACKETS_PER_URB = 8, PCM_MAX_PACKET_SIZE = 604 +}; + +struct pcm_urb { + struct sfire_chip *chip; + + /* BEGIN DO NOT SEPARATE */ + struct urb instance; + struct usb_iso_packet_descriptor packets[PCM_N_PACKETS_PER_URB]; + /* END DO NOT SEPARATE */ + u8 buffer[PCM_N_PACKETS_PER_URB * PCM_MAX_PACKET_SIZE]; + + struct pcm_urb *peer; +}; + +struct pcm_substream { + spinlock_t lock; + struct snd_pcm_substream *instance; + + bool active; + + snd_pcm_uframes_t dma_off; /* current position in alsa dma_area */ + snd_pcm_uframes_t period_off; /* current position in current period */ +}; + +struct pcm_runtime { + struct sfire_chip *chip; + struct snd_pcm *instance; + + struct pcm_substream playback; + struct pcm_substream capture; + bool panic; /* if set driver won't do anymore pcm on device */ + + struct pcm_urb in_urbs[PCM_N_URBS]; + struct pcm_urb out_urbs[PCM_N_URBS]; + int in_packet_size; + int out_packet_size; + int in_n_analog; /* number of analog channels soundcard sends */ + int out_n_analog; /* number of analog channels soundcard receives */ + + struct mutex stream_mutex; + u8 stream_state; /* one of STREAM_XXX (pcm.c) */ + u8 rate; /* one of PCM_RATE_XXX */ + wait_queue_head_t stream_wait_queue; + bool stream_wait_cond; +}; + +int __devinit usb6fire_pcm_init(struct sfire_chip *chip); +void usb6fire_pcm_abort(struct sfire_chip *chip); +void usb6fire_pcm_destroy(struct sfire_chip *chip); +#endif /* USB6FIRE_PCM_H */ diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig index 112984f4080f..97724d8fa9f6 100644 --- a/sound/usb/Kconfig +++ b/sound/usb/Kconfig @@ -62,6 +62,7 @@ config SND_USB_CAIAQ * Native Instruments Audio 2 DJ * Native Instruments Audio 4 DJ * Native Instruments Audio 8 DJ + * Native Instruments Traktor Audio 2 * Native Instruments Guitar Rig Session I/O * Native Instruments Guitar Rig mobile * Native Instruments Traktor Kontrol X1 @@ -97,5 +98,21 @@ config SND_USB_US122L To compile this driver as a module, choose M here: the module will be called snd-usb-us122l. +config SND_USB_6FIRE + tristate "TerraTec DMX 6Fire USB" + depends on EXPERIMENTAL + select FW_LOADER + select SND_RAWMIDI + select SND_PCM + help + Say Y here to include support for TerraTec 6fire DMX USB interface. + + You will need firmware files in order to be able to use the device + after it has been coldstarted. This driver currently does not support + firmware loading for all devices. If you own such a device, + you could start windows and let the windows driver upload + the firmware. As long as you do not unplug your device from power, + it should be usable. + endif # SND_USB diff --git a/sound/usb/Makefile b/sound/usb/Makefile index 1e362bf8834f..cf9ed66445fa 100644 --- a/sound/usb/Makefile +++ b/sound/usb/Makefile @@ -23,4 +23,4 @@ obj-$(CONFIG_SND_USB_UA101) += snd-usbmidi-lib.o obj-$(CONFIG_SND_USB_USX2Y) += snd-usbmidi-lib.o obj-$(CONFIG_SND_USB_US122L) += snd-usbmidi-lib.o -obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ +obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ 6fire/ diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c index 66eabafb1c24..d0d493ca28ae 100644 --- a/sound/usb/caiaq/audio.c +++ b/sound/usb/caiaq/audio.c @@ -805,6 +805,7 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev) case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO2DJ): case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ): case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ): + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORAUDIO2): dev->samplerates |= SNDRV_PCM_RATE_88200; break; } diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index 6480c3283c05..45bc4a2dc6f0 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -46,6 +46,7 @@ MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2}," "{Native Instruments, Audio 2 DJ}," "{Native Instruments, Audio 4 DJ}," "{Native Instruments, Audio 8 DJ}," + "{Native Instruments, Traktor Audio 2}," "{Native Instruments, Session I/O}," "{Native Instruments, GuitarRig mobile}" "{Native Instruments, Traktor Kontrol X1}" @@ -140,6 +141,11 @@ static struct usb_device_id snd_usb_id_table[] = { .idVendor = USB_VID_NATIVEINSTRUMENTS, .idProduct = USB_PID_TRAKTORKONTROLS4 }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = USB_VID_NATIVEINSTRUMENTS, + .idProduct = USB_PID_TRAKTORAUDIO2 + }, { /* terminator */ } }; diff --git a/sound/usb/caiaq/device.h b/sound/usb/caiaq/device.h index e3d8a3efb35b..b2b310194ffa 100644 --- a/sound/usb/caiaq/device.h +++ b/sound/usb/caiaq/device.h @@ -17,6 +17,7 @@ #define USB_PID_GUITARRIGMOBILE 0x0d8d #define USB_PID_TRAKTORKONTROLX1 0x2305 #define USB_PID_TRAKTORKONTROLS4 0xbaff +#define USB_PID_TRAKTORAUDIO2 0x041d #define EP1_BUFSIZE 64 #define EP4_BUFSIZE 512 diff --git a/sound/usb/card.c b/sound/usb/card.c index c0f8270bc199..40722f8711ad 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -65,6 +65,7 @@ #include "pcm.h" #include "urb.h" #include "format.h" +#include "power.h" MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); MODULE_DESCRIPTION("USB Audio"); @@ -330,6 +331,7 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx, chip->setup = device_setup[idx]; chip->nrpacks = nrpacks; chip->async_unlink = async_unlink; + chip->probing = 1; chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct)); @@ -451,6 +453,7 @@ static void *snd_usb_audio_probe(struct usb_device *dev, goto __error; } chip = usb_chip[i]; + chip->probing = 1; break; } } @@ -466,6 +469,7 @@ static void *snd_usb_audio_probe(struct usb_device *dev, goto __error; } snd_card_set_dev(chip->card, &intf->dev); + chip->pm_intf = intf; break; } if (!chip) { @@ -505,6 +509,7 @@ static void *snd_usb_audio_probe(struct usb_device *dev, usb_chip[chip->index] = chip; chip->num_interfaces++; + chip->probing = 0; mutex_unlock(®ister_mutex); return chip; @@ -581,29 +586,61 @@ static void usb_audio_disconnect(struct usb_interface *intf) } #ifdef CONFIG_PM + +int snd_usb_autoresume(struct snd_usb_audio *chip) +{ + int err = -ENODEV; + + if (!chip->shutdown && !chip->probing) + err = usb_autopm_get_interface(chip->pm_intf); + + return err; +} + +void snd_usb_autosuspend(struct snd_usb_audio *chip) +{ + if (!chip->shutdown && !chip->probing) + usb_autopm_put_interface(chip->pm_intf); +} + static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message) { struct snd_usb_audio *chip = usb_get_intfdata(intf); struct list_head *p; struct snd_usb_stream *as; + struct usb_mixer_interface *mixer; 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); - } + if (!(message.event & PM_EVENT_AUTO)) { + 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); + } + } + } else { + /* + * otherwise we keep the rest of the system in the dark + * to keep this transparent + */ + if (!chip->num_suspended_intf++) + chip->autosuspended = 1; } + list_for_each_entry(mixer, &chip->mixer_list, list) + snd_usb_mixer_inactivate(mixer); + return 0; } static int usb_audio_resume(struct usb_interface *intf) { struct snd_usb_audio *chip = usb_get_intfdata(intf); + struct usb_mixer_interface *mixer; + int err = 0; if (chip == (void *)-1L) return 0; @@ -611,12 +648,20 @@ static int usb_audio_resume(struct usb_interface *intf) return 0; /* * ALSA leaves material resumption to user space - * we just notify + * we just notify and restart the mixers */ + list_for_each_entry(mixer, &chip->mixer_list, list) { + err = snd_usb_mixer_activate(mixer); + if (err < 0) + goto err_out; + } - snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0); + if (!chip->autosuspended) + snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0); + chip->autosuspended = 0; - return 0; +err_out: + return err; } #else #define usb_audio_suspend NULL @@ -644,6 +689,7 @@ static struct usb_driver usb_audio_driver = { .suspend = usb_audio_suspend, .resume = usb_audio_resume, .id_table = usb_audio_ids, + .supports_autosuspend = 1, }; static int __init snd_usb_audio_init(void) diff --git a/sound/usb/midi.c b/sound/usb/midi.c index db2dc5ffe6dd..b4b39c0b6c9e 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -54,6 +54,7 @@ #include <sound/asequencer.h> #include "usbaudio.h" #include "midi.h" +#include "power.h" #include "helper.h" /* @@ -1044,6 +1045,7 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream) struct snd_usb_midi* umidi = substream->rmidi->private_data; struct usbmidi_out_port* port = NULL; int i, j; + int err; for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) if (umidi->endpoints[i].out) @@ -1056,6 +1058,9 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream) snd_BUG(); return -ENXIO; } + err = usb_autopm_get_interface(umidi->iface); + if (err < 0) + return -EIO; substream->runtime->private_data = port; port->state = STATE_UNKNOWN; substream_open(substream, 1); @@ -1064,7 +1069,10 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream) static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream) { + struct snd_usb_midi* umidi = substream->rmidi->private_data; + substream_open(substream, 0); + usb_autopm_put_interface(umidi->iface); return 0; } diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 85af6051b52d..5e4775716607 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -61,6 +61,7 @@ #include "mixer.h" #include "helper.h" #include "mixer_quirks.h" +#include "power.h" #define MAX_ID_ELEMS 256 @@ -295,16 +296,22 @@ static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, int v unsigned char buf[2]; int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; int timeout = 10; + int err; + err = snd_usb_autoresume(cval->mixer->chip); + if (err < 0) + return -EIO; while (timeout-- > 0) { if (snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), request, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, validx, snd_usb_ctrl_intf(chip) | (cval->id << 8), buf, val_len, 100) >= val_len) { *value_ret = convert_signed_value(cval, snd_usb_combine_bytes(buf, val_len)); + snd_usb_autosuspend(cval->mixer->chip); return 0; } } + snd_usb_autosuspend(cval->mixer->chip); snd_printdd(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n", request, validx, snd_usb_ctrl_intf(chip) | (cval->id << 8), cval->val_type); return -EINVAL; @@ -328,12 +335,18 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int v memset(buf, 0, sizeof(buf)); + ret = snd_usb_autoresume(chip) ? -EIO : 0; + if (ret) + goto error; + ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, validx, snd_usb_ctrl_intf(chip) | (cval->id << 8), buf, size, 1000); + snd_usb_autosuspend(chip); if (ret < 0) { +error: snd_printk(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n", request, validx, snd_usb_ctrl_intf(chip) | (cval->id << 8), cval->val_type); return ret; @@ -413,7 +426,7 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, { struct snd_usb_audio *chip = cval->mixer->chip; unsigned char buf[2]; - int val_len, timeout = 10; + int val_len, err, timeout = 10; if (cval->mixer->protocol == UAC_VERSION_1) { val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; @@ -433,13 +446,19 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, value_set = convert_bytes_value(cval, value_set); buf[0] = value_set & 0xff; buf[1] = (value_set >> 8) & 0xff; + err = snd_usb_autoresume(chip); + if (err < 0) + return -EIO; while (timeout-- > 0) if (snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), request, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, validx, snd_usb_ctrl_intf(chip) | (cval->id << 8), - buf, val_len, 100) >= 0) + buf, val_len, 100) >= 0) { + snd_usb_autosuspend(chip); return 0; + } + snd_usb_autosuspend(chip); snd_printdd(KERN_ERR "cannot set ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d, data = %#x/%#x\n", request, validx, snd_usb_ctrl_intf(chip) | (cval->id << 8), cval->val_type, buf[0], buf[1]); return -EINVAL; @@ -987,6 +1006,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, struct snd_kcontrol *kctl; struct usb_mixer_elem_info *cval; const struct usbmix_name_map *map; + unsigned int range; control++; /* change from zero-based to 1-based value */ @@ -1121,6 +1141,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, } break; + case USB_ID(0x046d, 0x0808): case USB_ID(0x046d, 0x0809): case USB_ID(0x046d, 0x0991): /* Most audio usb devices lie about volume resolution. @@ -1136,6 +1157,21 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, } + range = (cval->max - cval->min) / cval->res; + /* Are there devices with volume range more than 255? I use a bit more + * to be sure. 384 is a resolution magic number found on Logitech + * devices. It will definitively catch all buggy Logitech devices. + */ + if (range > 384) { + snd_printk(KERN_WARNING "usb_audio: Warning! Unlikely big " + "volume range (=%u), cval->res is probably wrong.", + range); + snd_printk(KERN_WARNING "usb_audio: [%d] FU [%s] ch = %d, " + "val = %d/%d/%d", cval->id, + kctl->id.name, cval->channels, + cval->min, cval->max, cval->res); + } + snd_printdd(KERN_INFO "[%d] FU [%s] ch = %d, val = %d/%d/%d\n", cval->id, kctl->id.name, cval->channels, cval->min, cval->max, cval->res); add_control_to_empty(state, kctl); @@ -2058,8 +2094,9 @@ static void snd_usb_mixer_interrupt(struct urb *urb) { struct usb_mixer_interface *mixer = urb->context; int len = urb->actual_length; + int ustatus = urb->status; - if (urb->status != 0) + if (ustatus != 0) goto requeue; if (mixer->protocol == UAC_VERSION_1) { @@ -2100,12 +2137,32 @@ static void snd_usb_mixer_interrupt(struct urb *urb) } requeue: - if (urb->status != -ENOENT && urb->status != -ECONNRESET) { + if (ustatus != -ENOENT && ustatus != -ECONNRESET && ustatus != -ESHUTDOWN) { urb->dev = mixer->chip->dev; usb_submit_urb(urb, GFP_ATOMIC); } } +/* stop any bus activity of a mixer */ +void snd_usb_mixer_inactivate(struct usb_mixer_interface *mixer) +{ + usb_kill_urb(mixer->urb); + usb_kill_urb(mixer->rc_urb); +} + +int snd_usb_mixer_activate(struct usb_mixer_interface *mixer) +{ + int err; + + if (mixer->urb) { + err = usb_submit_urb(mixer->urb, GFP_NOIO); + if (err < 0) + return err; + } + + return 0; +} + /* create the handler for the optional status interrupt endpoint */ static int snd_usb_mixer_status_create(struct usb_mixer_interface *mixer) { diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 26c636c5c93a..b4a2c8165e4b 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -52,5 +52,7 @@ void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid); int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int value_set); +void snd_usb_mixer_inactivate(struct usb_mixer_interface *mixer); +int snd_usb_mixer_activate(struct usb_mixer_interface *mixer); #endif /* __USBMIXER_H */ diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 782f741cd00a..73dcc8256bc0 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -346,6 +346,141 @@ static int snd_xonar_u1_controls_create(struct usb_mixer_interface *mixer) return 0; } +/* Native Instruments device quirks */ + +#define _MAKE_NI_CONTROL(bRequest,wIndex) ((bRequest) << 16 | (wIndex)) + +static int snd_nativeinstruments_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); + struct usb_device *dev = mixer->chip->dev; + u8 bRequest = (kcontrol->private_value >> 16) & 0xff; + u16 wIndex = kcontrol->private_value & 0xffff; + u8 tmp; + + int ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), bRequest, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0, cpu_to_le16(wIndex), + &tmp, sizeof(tmp), 1000); + + if (ret < 0) { + snd_printk(KERN_ERR + "unable to issue vendor read request (ret = %d)", ret); + return ret; + } + + ucontrol->value.integer.value[0] = tmp; + + return 0; +} + +static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); + struct usb_device *dev = mixer->chip->dev; + u8 bRequest = (kcontrol->private_value >> 16) & 0xff; + u16 wIndex = kcontrol->private_value & 0xffff; + u16 wValue = ucontrol->value.integer.value[0]; + + int ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), bRequest, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + cpu_to_le16(wValue), cpu_to_le16(wIndex), + NULL, 0, 1000); + + if (ret < 0) { + snd_printk(KERN_ERR + "unable to issue vendor write request (ret = %d)", ret); + return ret; + } + + return 0; +} + +static struct snd_kcontrol_new snd_nativeinstruments_ta6_mixers[] = { + { + .name = "Direct Thru Channel A", + .private_value = _MAKE_NI_CONTROL(0x01, 0x03), + }, + { + .name = "Direct Thru Channel B", + .private_value = _MAKE_NI_CONTROL(0x01, 0x05), + }, + { + .name = "Phono Input Channel A", + .private_value = _MAKE_NI_CONTROL(0x02, 0x03), + }, + { + .name = "Phono Input Channel B", + .private_value = _MAKE_NI_CONTROL(0x02, 0x05), + }, +}; + +static struct snd_kcontrol_new snd_nativeinstruments_ta10_mixers[] = { + { + .name = "Direct Thru Channel A", + .private_value = _MAKE_NI_CONTROL(0x01, 0x03), + }, + { + .name = "Direct Thru Channel B", + .private_value = _MAKE_NI_CONTROL(0x01, 0x05), + }, + { + .name = "Direct Thru Channel C", + .private_value = _MAKE_NI_CONTROL(0x01, 0x07), + }, + { + .name = "Direct Thru Channel D", + .private_value = _MAKE_NI_CONTROL(0x01, 0x09), + }, + { + .name = "Phono Input Channel A", + .private_value = _MAKE_NI_CONTROL(0x02, 0x03), + }, + { + .name = "Phono Input Channel B", + .private_value = _MAKE_NI_CONTROL(0x02, 0x05), + }, + { + .name = "Phono Input Channel C", + .private_value = _MAKE_NI_CONTROL(0x02, 0x07), + }, + { + .name = "Phono Input Channel D", + .private_value = _MAKE_NI_CONTROL(0x02, 0x09), + }, +}; + +static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer, + const struct snd_kcontrol_new *kc, + unsigned int count) +{ + int i, err = 0; + struct snd_kcontrol_new template = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .get = snd_nativeinstruments_control_get, + .put = snd_nativeinstruments_control_put, + .info = snd_ctl_boolean_mono_info, + }; + + for (i = 0; i < count; i++) { + struct snd_kcontrol *c; + + template.name = kc[i].name; + template.private_value = kc[i].private_value; + + c = snd_ctl_new1(&template, mixer); + err = snd_ctl_add(mixer->chip->card, c); + + if (err < 0) + break; + } + + return err; +} + void snd_emuusb_set_samplerate(struct snd_usb_audio *chip, unsigned char samplerate_id) { @@ -367,31 +502,44 @@ void snd_emuusb_set_samplerate(struct snd_usb_audio *chip, int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) { - int err; + int err = 0; struct snd_info_entry *entry; if ((err = snd_usb_soundblaster_remote_init(mixer)) < 0) return err; - if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020) || - mixer->chip->usb_id == USB_ID(0x041e, 0x3040) || - mixer->chip->usb_id == USB_ID(0x041e, 0x3042) || - mixer->chip->usb_id == USB_ID(0x041e, 0x3048)) { - if ((err = snd_audigy2nx_controls_create(mixer)) < 0) - return err; + switch (mixer->chip->usb_id) { + case USB_ID(0x041e, 0x3020): + case USB_ID(0x041e, 0x3040): + case USB_ID(0x041e, 0x3042): + case USB_ID(0x041e, 0x3048): + err = snd_audigy2nx_controls_create(mixer); + if (err < 0) + break; if (!snd_card_proc_new(mixer->chip->card, "audigy2nx", &entry)) snd_info_set_text_ops(entry, mixer, snd_audigy2nx_proc_read); - } + break; - if (mixer->chip->usb_id == USB_ID(0x0b05, 0x1739) || - mixer->chip->usb_id == USB_ID(0x0b05, 0x1743)) { + case USB_ID(0x0b05, 0x1739): + case USB_ID(0x0b05, 0x1743): err = snd_xonar_u1_controls_create(mixer); - if (err < 0) - return err; + break; + + case USB_ID(0x17cc, 0x1011): /* Traktor Audio 6 */ + err = snd_nativeinstruments_create_mixer(mixer, + snd_nativeinstruments_ta6_mixers, + ARRAY_SIZE(snd_nativeinstruments_ta6_mixers)); + break; + + case USB_ID(0x17cc, 0x1021): /* Traktor Audio 10 */ + err = snd_nativeinstruments_create_mixer(mixer, + snd_nativeinstruments_ta10_mixers, + ARRAY_SIZE(snd_nativeinstruments_ta10_mixers)); + break; } - return 0; + return err; } void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer, diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index e3f680526cb5..b8dcbf407bbb 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -32,6 +32,7 @@ #include "helper.h" #include "pcm.h" #include "clock.h" +#include "power.h" /* * return the current pcm pointer. just based on the hwptr_done value. @@ -739,6 +740,9 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre pt = 125 * (1 << fp->datainterval); ptmin = min(ptmin, pt); } + err = snd_usb_autoresume(subs->stream->chip); + if (err < 0) + return err; param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME; if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) @@ -756,21 +760,21 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre SNDRV_PCM_HW_PARAM_CHANNELS, param_period_time_if_needed, -1)) < 0) - return err; + goto rep_err; if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_channels, subs, SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_RATE, param_period_time_if_needed, -1)) < 0) - return err; + goto rep_err; if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, hw_rule_format, subs, SNDRV_PCM_HW_PARAM_RATE, SNDRV_PCM_HW_PARAM_CHANNELS, param_period_time_if_needed, -1)) < 0) - return err; + goto rep_err; if (param_period_time_if_needed >= 0) { err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_TIME, @@ -780,11 +784,15 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) - return err; + goto rep_err; } if ((err = snd_usb_pcm_check_knot(runtime, subs)) < 0) - return err; + goto rep_err; return 0; + +rep_err: + snd_usb_autosuspend(subs->stream->chip); + return err; } static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction) @@ -798,6 +806,7 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction) runtime->hw = snd_usb_hardware; runtime->private_data = subs; subs->pcm_substream = substream; + /* runtime PM is also done there */ return setup_hw_info(runtime, subs); } @@ -811,6 +820,7 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction) subs->interface = -1; } subs->pcm_substream = NULL; + snd_usb_autosuspend(subs->stream->chip); return 0; } diff --git a/sound/usb/power.h b/sound/usb/power.h new file mode 100644 index 000000000000..48ee51dcb71e --- /dev/null +++ b/sound/usb/power.h @@ -0,0 +1,17 @@ +#ifndef __USBAUDIO_POWER_H +#define __USBAUDIO_POWER_H + +#ifdef CONFIG_PM +int snd_usb_autoresume(struct snd_usb_audio *chip); +void snd_usb_autosuspend(struct snd_usb_audio *chip); +#else +static inline int snd_usb_autoresume(struct snd_usb_audio *chip) +{ + return 0; +} +static inline void snd_usb_autosuspend(struct snd_usb_audio *chip) +{ +} +#endif + +#endif /* __USBAUDIO_POWER_H */ diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 921a86fd9884..c0dcfca9b5b5 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2290,6 +2290,20 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, +/* Native Instruments MK2 series */ +{ + /* Traktor Audio 6 */ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x17cc, + .idProduct = 0x1010, +}, +{ + /* Traktor Audio 10 */ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x17cc, + .idProduct = 0x1020, +}, + /* Miditech devices */ { USB_DEVICE(0x4752, 0x0011), diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index e314cdb85003..355759bad581 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -425,6 +425,34 @@ static int snd_usb_accessmusic_boot_quirk(struct usb_device *dev) } /* + * Some sound cards from Native Instruments are in fact compliant to the USB + * audio standard of version 2 and other approved USB standards, even though + * they come up as vendor-specific device when first connected. + * + * However, they can be told to come up with a new set of descriptors + * upon their next enumeration, and the interfaces announced by the new + * descriptors will then be handled by the kernel's class drivers. As the + * product ID will also change, no further checks are required. + */ + +static int snd_usb_nativeinstruments_boot_quirk(struct usb_device *dev) +{ + int ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0xaf, USB_TYPE_VENDOR | USB_RECIP_DEVICE, + cpu_to_le16(1), 0, NULL, 0, 1000); + + if (ret < 0) + return ret; + + usb_reset_device(dev); + + /* return -EAGAIN, so the creation of an audio interface for this + * temporary device is aborted. The device will reconnect with a + * new product ID */ + return -EAGAIN; +} + +/* * Setup quirks */ #define AUDIOPHILE_SET 0x01 /* if set, parse device_setup */ @@ -489,27 +517,33 @@ int snd_usb_apply_boot_quirk(struct usb_device *dev, u32 id = USB_ID(le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct)); - /* SB Extigy needs special boot-up sequence */ - /* if more models come, this will go to the quirk list. */ - if (id == USB_ID(0x041e, 0x3000)) + switch (id) { + case USB_ID(0x041e, 0x3000): + /* SB Extigy needs special boot-up sequence */ + /* if more models come, this will go to the quirk list. */ return snd_usb_extigy_boot_quirk(dev, intf); - /* SB Audigy 2 NX needs its own boot-up magic, too */ - if (id == USB_ID(0x041e, 0x3020)) + case USB_ID(0x041e, 0x3020): + /* SB Audigy 2 NX needs its own boot-up magic, too */ return snd_usb_audigy2nx_boot_quirk(dev); - /* C-Media CM106 / Turtle Beach Audio Advantage Roadie */ - if (id == USB_ID(0x10f5, 0x0200)) + case USB_ID(0x10f5, 0x0200): + /* C-Media CM106 / Turtle Beach Audio Advantage Roadie */ return snd_usb_cm106_boot_quirk(dev); - /* C-Media CM6206 / CM106-Like Sound Device */ - if (id == USB_ID(0x0d8c, 0x0102)) + case USB_ID(0x0d8c, 0x0102): + /* C-Media CM6206 / CM106-Like Sound Device */ return snd_usb_cm6206_boot_quirk(dev); - /* Access Music VirusTI Desktop */ - if (id == USB_ID(0x133e, 0x0815)) + case USB_ID(0x133e, 0x0815): + /* Access Music VirusTI Desktop */ return snd_usb_accessmusic_boot_quirk(dev); + case USB_ID(0x17cc, 0x1010): /* Traktor Audio 6 */ + case USB_ID(0x17cc, 0x1020): /* Traktor Audio 10 */ + return snd_usb_nativeinstruments_boot_quirk(dev); + } + return 0; } diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 6e66fffe87f5..32f2a97f2f14 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -34,10 +34,14 @@ struct snd_usb_audio { int index; struct usb_device *dev; struct snd_card *card; + struct usb_interface *pm_intf; u32 usb_id; - int shutdown; struct mutex shutdown_mutex; + unsigned int shutdown:1; + unsigned int probing:1; + unsigned int autosuspended:1; unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */ + int num_interfaces; int num_suspended_intf; |