diff -Nur sound/Kconfig sound/Kconfig --- sound/Kconfig 2007-07-09 01:32:17.000000000 +0200 +++ sound/Kconfig 2007-07-24 02:00:09.000000000 +0200 @@ -63,8 +63,14 @@ source "sound/arm/Kconfig" +if SPI +source "sound/spi/Kconfig" +endif + source "sound/mips/Kconfig" +source "sound/sh/Kconfig" + # the following will depend on the order of config. # here assuming USB is defined before ALSA source "sound/usb/Kconfig" diff -Nur sound/Makefile sound/Makefile --- sound/Makefile 2007-07-09 01:32:17.000000000 +0200 +++ sound/Makefile 2007-07-24 02:00:09.000000000 +0200 @@ -5,7 +5,8 @@ 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/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/ soc/ +obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \ + sparc/ spi/ parisc/ pcmcia/ mips/ soc/ obj-$(CONFIG_SND_AOA) += aoa/ # This one must be compilable even if sound is configured out diff -Nur sound/aoa/codecs/snd-aoa-codec-onyx.c sound/aoa/codecs/snd-aoa-codec-onyx.c --- sound/aoa/codecs/snd-aoa-codec-onyx.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/aoa/codecs/snd-aoa-codec-onyx.c 2007-07-24 02:00:09.000000000 +0200 @@ -297,15 +297,7 @@ .put = onyx_snd_capture_source_put, }; -static int onyx_snd_mute_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define onyx_snd_mute_info snd_ctl_boolean_stereo_info static int onyx_snd_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -359,15 +351,7 @@ }; -static int onyx_snd_single_bit_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define onyx_snd_single_bit_info snd_ctl_boolean_mono_info #define FLAG_POLARITY_INVERT 1 #define FLAG_SPDIFLOCK 2 @@ -661,7 +645,7 @@ .tag = 2, }, #ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE -Once alsa gets supports for this kind of thing we can add it... + /* Once alsa gets supports for this kind of thing we can add it... */ { /* digital compressed output */ .formats = SNDRV_PCM_FMTBIT_COMPRESSED_16BE, @@ -713,7 +697,7 @@ if (substream->runtime->format == SNDRV_PCM_FMTBIT_COMPRESSED_16BE) { /* mute and lock analog output */ onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v); - if (onyx_write_register(onyx + if (onyx_write_register(onyx, ONYX_REG_DAC_CONTROL, v | ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT)) goto out_unlock; diff -Nur sound/aoa/codecs/snd-aoa-codec-tas.c sound/aoa/codecs/snd-aoa-codec-tas.c --- sound/aoa/codecs/snd-aoa-codec-tas.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/aoa/codecs/snd-aoa-codec-tas.c 2007-07-24 02:00:09.000000000 +0200 @@ -272,15 +272,7 @@ .put = tas_snd_vol_put, }; -static int tas_snd_mute_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define tas_snd_mute_info snd_ctl_boolean_stereo_info static int tas_snd_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -431,15 +423,7 @@ .put = tas_snd_drc_range_put, }; -static int tas_snd_drc_switch_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define tas_snd_drc_switch_info snd_ctl_boolean_mono_info static int tas_snd_drc_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -743,6 +727,7 @@ return 0; } +#ifdef CONFIG_PM /* we are controlled via i2c and assume that is always up * If that wasn't the case, we'd have to suspend once * our i2c device is suspended, and then take note of that! */ @@ -768,7 +753,6 @@ return 0; } -#ifdef CONFIG_PM static int _tas_suspend(struct codec_info_item *cii, pm_message_t state) { return tas_suspend(cii->codec_data); @@ -778,7 +762,10 @@ { return tas_resume(cii->codec_data); } -#endif +#else /* CONFIG_PM */ +#define _tas_suspend NULL +#define _tas_resume NULL +#endif /* CONFIG_PM */ static struct codec_info tas_codec_info = { .transfers = tas_transfers, @@ -791,10 +778,8 @@ .owner = THIS_MODULE, .usable = tas_usable, .switch_clock = tas_switch_clock, -#ifdef CONFIG_PM .suspend = _tas_suspend, .resume = _tas_resume, -#endif }; static int tas_init_codec(struct aoa_codec *codec) diff -Nur sound/aoa/fabrics/snd-aoa-fabric-layout.c sound/aoa/fabrics/snd-aoa-fabric-layout.c --- sound/aoa/fabrics/snd-aoa-fabric-layout.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/aoa/fabrics/snd-aoa-fabric-layout.c 2007-07-24 02:00:09.000000000 +0200 @@ -582,15 +582,7 @@ * make the fabric handle all the card stuff, etc... */ static struct layout_dev *layout_device; -static int control_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define control_info snd_ctl_boolean_mono_info #define AMP_CONTROL(n, description) \ static int n##_control_get(struct snd_kcontrol *kcontrol, \ diff -Nur sound/arm/sa11xx-uda1341.c sound/arm/sa11xx-uda1341.c --- sound/arm/sa11xx-uda1341.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/arm/sa11xx-uda1341.c 2007-08-03 02:00:09.000000000 +0200 @@ -79,12 +79,6 @@ #include #include -#ifdef CONFIG_H3600_HAL -#include -#include -#include -#endif - #include #include #include @@ -100,9 +94,6 @@ * We use DMA stuff from 2.4.18-rmk3-hh24 here to be able to compile this * module for Familiar 0.6.1 */ -#ifdef CONFIG_H3600_HAL -#define HH_VERSION 1 -#endif /* {{{ Type definitions */ @@ -238,11 +229,8 @@ rate = 8000; /* Set the external clock generator */ -#ifdef CONFIG_H3600_HAL - h3600_audio_clock(rate); -#else + sa11xx_uda1341_set_audio_clock(rate); -#endif /* Select the clock divisor */ switch (rate) { @@ -307,13 +295,10 @@ local_irq_restore(flags); /* Enable the audio power */ -#ifdef CONFIG_H3600_HAL - h3600_audio_power(AUDIO_RATE_DEFAULT); -#else + clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_CODEC_NRESET); set_sa11xx_uda1341_egpio(IPAQ_EGPIO_AUDIO_ON); set_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE); -#endif /* Wait for the UDA1341 to wake up */ mdelay(1); //FIXME - was removed by Perex - Why? @@ -331,21 +316,13 @@ /* make the left and right channels unswapped (flip the WS latch) */ Ser4SSDR = 0; -#ifdef CONFIG_H3600_HAL - h3600_audio_mute(0); -#else - clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE); -#endif + clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE); } static void sa11xx_uda1341_audio_shutdown(struct sa11xx_uda1341 *sa11xx_uda1341) { /* mute on */ -#ifdef CONFIG_H3600_HAL - h3600_audio_mute(1); -#else set_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE); -#endif /* disable the audio power and all signals leading to the audio chip */ l3_close(sa11xx_uda1341->uda1341); @@ -354,13 +331,9 @@ /* power off and mute off */ /* FIXME - is muting off necesary??? */ -#ifdef CONFIG_H3600_HAL - h3600_audio_power(0); - h3600_audio_mute(0); -#else + clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_AUDIO_ON); clr_sa11xx_uda1341_egpio(IPAQ_EGPIO_QMUTE); -#endif } /* }}} */ diff -Nur sound/core/Makefile sound/core/Makefile --- sound/core/Makefile 2007-07-09 01:32:17.000000000 +0200 +++ sound/core/Makefile 2007-07-28 02:00:08.000000000 +0200 @@ -3,18 +3,15 @@ # Copyright (c) 1999,2001 by Jaroslav Kysela # -snd-objs := sound.o init.o memory.o info.o control.o misc.o device.o -ifeq ($(CONFIG_ISA_DMA_API),y) -snd-objs += isadma.o -endif -ifeq ($(CONFIG_SND_OSSEMUL),y) -snd-objs += sound_oss.o info_oss.o -endif +snd-y := sound.o init.o memory.o info.o control.o misc.o device.o +snd-$(CONFIG_ISA_DMA_API) += isadma.o +snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o info_oss.o snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ pcm_memory.o -snd-page-alloc-objs := memalloc.o sgbuf.o +snd-page-alloc-y := memalloc.o +snd-page-alloc-$(CONFIG_HAS_DMA) += sgbuf.o snd-rawmidi-objs := rawmidi.o snd-timer-objs := timer.o diff -Nur sound/core/control.c sound/core/control.c --- sound/core/control.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/core/control.c 2007-07-24 02:00:09.000000000 +0200 @@ -1486,3 +1486,30 @@ snd_assert(card != NULL, return -ENXIO); return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops); } + +/* + * Frequently used control callbacks + */ +int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +EXPORT_SYMBOL(snd_ctl_boolean_mono_info); + +int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +EXPORT_SYMBOL(snd_ctl_boolean_stereo_info); diff -Nur sound/core/memalloc.c sound/core/memalloc.c --- sound/core/memalloc.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/core/memalloc.c 2007-07-28 02:00:08.000000000 +0200 @@ -205,6 +205,7 @@ * */ +#ifdef CONFIG_HAS_DMA /* allocate the coherent DMA pages */ static void *snd_malloc_dev_pages(struct device *dev, size_t size, dma_addr_t *dma) { @@ -238,6 +239,7 @@ dec_snd_pages(pg); dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma); } +#endif /* CONFIG_HAS_DMA */ #ifdef CONFIG_SBUS @@ -311,12 +313,14 @@ dmab->area = snd_malloc_sbus_pages(device, size, &dmab->addr); break; #endif +#ifdef CONFIG_HAS_DMA case SNDRV_DMA_TYPE_DEV: dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr); break; case SNDRV_DMA_TYPE_DEV_SG: snd_malloc_sgbuf_pages(device, size, dmab, NULL); break; +#endif default: printk(KERN_ERR "snd-malloc: invalid device type %d\n", type); dmab->area = NULL; @@ -382,12 +386,14 @@ snd_free_sbus_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr); break; #endif +#ifdef CONFIG_HAS_DMA case SNDRV_DMA_TYPE_DEV: snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr); break; case SNDRV_DMA_TYPE_DEV_SG: snd_free_sgbuf_pages(dmab); break; +#endif default: printk(KERN_ERR "snd-malloc: invalid device type %d\n", dmab->dev.type); } diff -Nur sound/core/oss/Makefile sound/core/oss/Makefile --- sound/core/oss/Makefile 2007-07-09 01:32:17.000000000 +0200 +++ sound/core/oss/Makefile 2007-08-09 02:00:06.000000000 +0200 @@ -5,8 +5,9 @@ snd-mixer-oss-objs := mixer_oss.o -snd-pcm-oss-objs := pcm_oss.o pcm_plugin.o \ - io.o copy.o linear.o mulaw.o route.o rate.o +snd-pcm-oss-y := pcm_oss.o +snd-pcm-oss-$(CONFIG_SND_PCM_OSS_PLUGINS) += pcm_plugin.o \ + io.o copy.o linear.o mulaw.o route.o rate.o obj-$(CONFIG_SND_MIXER_OSS) += snd-mixer-oss.o obj-$(CONFIG_SND_PCM_OSS) += snd-pcm-oss.o diff -Nur sound/core/oss/copy.c sound/core/oss/copy.c --- sound/core/oss/copy.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/core/oss/copy.c 2007-08-09 02:00:06.000000000 +0200 @@ -20,9 +20,6 @@ */ #include - -#ifdef CONFIG_SND_PCM_OSS_PLUGINS - #include #include #include @@ -88,5 +85,3 @@ *r_plugin = plugin; return 0; } - -#endif diff -Nur sound/core/oss/io.c sound/core/oss/io.c --- sound/core/oss/io.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/core/oss/io.c 2007-08-09 02:00:06.000000000 +0200 @@ -20,9 +20,6 @@ */ #include - -#ifdef CONFIG_SND_PCM_OSS_PLUGINS - #include #include #include @@ -135,5 +132,3 @@ *r_plugin = plugin; return 0; } - -#endif diff -Nur sound/core/oss/linear.c sound/core/oss/linear.c --- sound/core/oss/linear.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/core/oss/linear.c 2007-08-09 02:00:06.000000000 +0200 @@ -21,9 +21,6 @@ */ #include - -#ifdef CONFIG_SND_PCM_OSS_PLUGINS - #include #include #include @@ -34,19 +31,34 @@ */ struct linear_priv { - int conv; + int cvt_endian; /* need endian conversion? */ + unsigned int src_ofs; /* byte offset in source format */ + unsigned int dst_ofs; /* byte soffset in destination format */ + unsigned int copy_ofs; /* byte offset in temporary u32 data */ + unsigned int dst_bytes; /* byte size of destination format */ + unsigned int copy_bytes; /* bytes to copy per conversion */ + unsigned int flip; /* MSB flip for signeness, done after endian conv */ }; +static inline void do_convert(struct linear_priv *data, + unsigned char *dst, unsigned char *src) +{ + unsigned int tmp = 0; + unsigned char *p = (unsigned char *)&tmp; + + memcpy(p + data->copy_ofs, src + data->src_ofs, data->copy_bytes); + if (data->cvt_endian) + tmp = swab32(tmp); + tmp ^= data->flip; + memcpy(dst, p + data->dst_ofs, data->dst_bytes); +} + static void convert(struct snd_pcm_plugin *plugin, const struct snd_pcm_plugin_channel *src_channels, struct snd_pcm_plugin_channel *dst_channels, snd_pcm_uframes_t frames) { -#define CONV_LABELS -#include "plugin_ops.h" -#undef CONV_LABELS struct linear_priv *data = (struct linear_priv *)plugin->extra_data; - void *conv = conv_labels[data->conv]; int channel; int nchannels = plugin->src_format.channels; for (channel = 0; channel < nchannels; ++channel) { @@ -67,11 +79,7 @@ dst_step = dst_channels[channel].area.step / 8; frames1 = frames; while (frames1-- > 0) { - goto *conv; -#define CONV_END after -#include "plugin_ops.h" -#undef CONV_END - after: + do_convert(data, dst, src); src += src_step; dst += dst_step; } @@ -106,29 +114,36 @@ return frames; } -static int conv_index(int src_format, int dst_format) +static void init_data(struct linear_priv *data, int src_format, int dst_format) { - int src_endian, dst_endian, sign, src_width, dst_width; + int src_le, dst_le, src_bytes, dst_bytes; - sign = (snd_pcm_format_signed(src_format) != - snd_pcm_format_signed(dst_format)); -#ifdef SNDRV_LITTLE_ENDIAN - src_endian = snd_pcm_format_big_endian(src_format); - dst_endian = snd_pcm_format_big_endian(dst_format); -#else - src_endian = snd_pcm_format_little_endian(src_format); - dst_endian = snd_pcm_format_little_endian(dst_format); -#endif - - if (src_endian < 0) - src_endian = 0; - if (dst_endian < 0) - dst_endian = 0; - - src_width = snd_pcm_format_width(src_format) / 8 - 1; - dst_width = snd_pcm_format_width(dst_format) / 8 - 1; - - return src_width * 32 + src_endian * 16 + sign * 8 + dst_width * 2 + dst_endian; + src_bytes = snd_pcm_format_width(src_format) / 8; + dst_bytes = snd_pcm_format_width(dst_format) / 8; + src_le = snd_pcm_format_little_endian(src_format) > 0; + dst_le = snd_pcm_format_little_endian(dst_format) > 0; + + data->dst_bytes = dst_bytes; + data->cvt_endian = src_le != dst_le; + data->copy_bytes = src_bytes < dst_bytes ? src_bytes : dst_bytes; + if (src_le) { + data->copy_ofs = 4 - data->copy_bytes; + data->src_ofs = src_bytes - data->copy_bytes; + } else + data->src_ofs = snd_pcm_format_physical_width(src_format) / 8 - + src_bytes; + if (dst_le) + data->dst_ofs = 4 - data->dst_bytes; + else + data->dst_ofs = snd_pcm_format_physical_width(dst_format) / 8 - + dst_bytes; + if (snd_pcm_format_signed(src_format) != + snd_pcm_format_signed(dst_format)) { + if (dst_le) + data->flip = cpu_to_le32(0x80000000); + else + data->flip = cpu_to_be32(0x80000000); + } } int snd_pcm_plugin_build_linear(struct snd_pcm_substream *plug, @@ -154,10 +169,8 @@ if (err < 0) return err; data = (struct linear_priv *)plugin->extra_data; - data->conv = conv_index(src_format->format, dst_format->format); + init_data(data, src_format->format, dst_format->format); plugin->transfer = linear_transfer; *r_plugin = plugin; return 0; } - -#endif diff -Nur sound/core/oss/mulaw.c sound/core/oss/mulaw.c --- sound/core/oss/mulaw.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/core/oss/mulaw.c 2007-08-09 02:00:06.000000000 +0200 @@ -22,9 +22,6 @@ */ #include - -#ifdef CONFIG_SND_PCM_OSS_PLUGINS - #include #include #include @@ -149,19 +146,32 @@ struct mulaw_priv { mulaw_f func; - int conv; + int cvt_endian; /* need endian conversion? */ + unsigned int native_ofs; /* byte offset in native format */ + unsigned int copy_ofs; /* byte offset in s16 format */ + unsigned int native_bytes; /* byte size of the native format */ + unsigned int copy_bytes; /* bytes to copy per conversion */ + u16 flip; /* MSB flip for signedness, done after endian conversion */ }; +static inline void cvt_s16_to_native(struct mulaw_priv *data, + unsigned char *dst, u16 sample) +{ + sample ^= data->flip; + if (data->cvt_endian) + sample = swab16(sample); + if (data->native_bytes > data->copy_bytes) + memset(dst, 0, data->native_bytes); + memcpy(dst + data->native_ofs, (char *)&sample + data->copy_ofs, + data->copy_bytes); +} + static void mulaw_decode(struct snd_pcm_plugin *plugin, const struct snd_pcm_plugin_channel *src_channels, struct snd_pcm_plugin_channel *dst_channels, snd_pcm_uframes_t frames) { -#define PUT_S16_LABELS -#include "plugin_ops.h" -#undef PUT_S16_LABELS struct mulaw_priv *data = (struct mulaw_priv *)plugin->extra_data; - void *put = put_s16_labels[data->conv]; int channel; int nchannels = plugin->src_format.channels; for (channel = 0; channel < nchannels; ++channel) { @@ -183,30 +193,33 @@ frames1 = frames; while (frames1-- > 0) { signed short sample = ulaw2linear(*src); - goto *put; -#define PUT_S16_END after -#include "plugin_ops.h" -#undef PUT_S16_END - after: + cvt_s16_to_native(data, dst, sample); src += src_step; dst += dst_step; } } } +static inline signed short cvt_native_to_s16(struct mulaw_priv *data, + unsigned char *src) +{ + u16 sample = 0; + memcpy((char *)&sample + data->copy_ofs, src + data->native_ofs, + data->copy_bytes); + if (data->cvt_endian) + sample = swab16(sample); + sample ^= data->flip; + return (signed short)sample; +} + static void mulaw_encode(struct snd_pcm_plugin *plugin, const struct snd_pcm_plugin_channel *src_channels, struct snd_pcm_plugin_channel *dst_channels, snd_pcm_uframes_t frames) { -#define GET_S16_LABELS -#include "plugin_ops.h" -#undef GET_S16_LABELS struct mulaw_priv *data = (struct mulaw_priv *)plugin->extra_data; - void *get = get_s16_labels[data->conv]; int channel; int nchannels = plugin->src_format.channels; - signed short sample = 0; for (channel = 0; channel < nchannels; ++channel) { char *src; char *dst; @@ -225,11 +238,7 @@ dst_step = dst_channels[channel].area.step / 8; frames1 = frames; while (frames1-- > 0) { - goto *get; -#define GET_S16_END after -#include "plugin_ops.h" -#undef GET_S16_END - after: + signed short sample = cvt_native_to_s16(data, src); *dst = linear2ulaw(sample); src += src_step; dst += dst_step; @@ -265,23 +274,25 @@ return frames; } -static int getput_index(int format) +static void init_data(struct mulaw_priv *data, int format) { - int sign, width, endian; - sign = !snd_pcm_format_signed(format); - width = snd_pcm_format_width(format) / 8 - 1; - if (width < 0 || width > 3) { - snd_printk(KERN_ERR "snd-pcm-oss: invalid format %d\n", format); - width = 0; - } #ifdef SNDRV_LITTLE_ENDIAN - endian = snd_pcm_format_big_endian(format); + data->cvt_endian = snd_pcm_format_big_endian(format) > 0; #else - endian = snd_pcm_format_little_endian(format); + data->cvt_endian = snd_pcm_format_little_endian(format) > 0; #endif - if (endian < 0) - endian = 0; - return width * 4 + endian * 2 + sign; + if (!snd_pcm_format_signed(format)) + data->flip = 0x8000; + data->native_bytes = snd_pcm_format_physical_width(format) / 8; + data->copy_bytes = data->native_bytes < 2 ? 1 : 2; + if (snd_pcm_format_little_endian(format)) { + data->native_ofs = data->native_bytes - data->copy_bytes; + data->copy_ofs = 2 - data->copy_bytes; + } else { + /* S24 in 4bytes need an 1 byte offset */ + data->native_ofs = data->native_bytes - + snd_pcm_format_width(format) / 8; + } } int snd_pcm_plugin_build_mulaw(struct snd_pcm_substream *plug, @@ -322,11 +333,8 @@ return err; data = (struct mulaw_priv *)plugin->extra_data; data->func = func; - data->conv = getput_index(format->format); - snd_assert(data->conv >= 0 && data->conv < 4*2*2, return -EINVAL); + init_data(data, format->format); plugin->transfer = mulaw_transfer; *r_plugin = plugin; return 0; } - -#endif diff -Nur sound/core/oss/pcm_oss.c sound/core/oss/pcm_oss.c --- sound/core/oss/pcm_oss.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/core/oss/pcm_oss.c 2007-08-09 02:00:06.000000000 +0200 @@ -633,6 +633,22 @@ return bytes_to_frames(runtime, (buffer_size * bytes) / runtime->oss.buffer_bytes); } +/* define extended formats in the recent OSS versions (if any) */ +/* linear formats */ +#define AFMT_S32_LE 0x00001000 +#define AFMT_S32_BE 0x00002000 +#define AFMT_S24_LE 0x00008000 +#define AFMT_S24_BE 0x00010000 +#define AFMT_S24_PACKED 0x00040000 + +/* other supported formats */ +#define AFMT_FLOAT 0x00004000 +#define AFMT_SPDIF_RAW 0x00020000 + +/* unsupported formats */ +#define AFMT_AC3 0x00000400 +#define AFMT_VORBIS 0x00000800 + static int snd_pcm_oss_format_from(int format) { switch (format) { @@ -646,6 +662,13 @@ case AFMT_U16_LE: return SNDRV_PCM_FORMAT_U16_LE; case AFMT_U16_BE: return SNDRV_PCM_FORMAT_U16_BE; case AFMT_MPEG: return SNDRV_PCM_FORMAT_MPEG; + case AFMT_S32_LE: return SNDRV_PCM_FORMAT_S32_LE; + case AFMT_S32_BE: return SNDRV_PCM_FORMAT_S32_BE; + case AFMT_S24_LE: return SNDRV_PCM_FORMAT_S24_LE; + case AFMT_S24_BE: return SNDRV_PCM_FORMAT_S24_BE; + case AFMT_S24_PACKED: return SNDRV_PCM_FORMAT_S24_3LE; + case AFMT_FLOAT: return SNDRV_PCM_FORMAT_FLOAT; + case AFMT_SPDIF_RAW: return SNDRV_PCM_FORMAT_IEC958_SUBFRAME; default: return SNDRV_PCM_FORMAT_U8; } } @@ -663,6 +686,13 @@ case SNDRV_PCM_FORMAT_U16_LE: return AFMT_U16_LE; case SNDRV_PCM_FORMAT_U16_BE: return AFMT_U16_BE; case SNDRV_PCM_FORMAT_MPEG: return AFMT_MPEG; + case SNDRV_PCM_FORMAT_S32_LE: return AFMT_S32_LE; + case SNDRV_PCM_FORMAT_S32_BE: return AFMT_S32_BE; + case SNDRV_PCM_FORMAT_S24_LE: return AFMT_S24_LE; + case SNDRV_PCM_FORMAT_S24_BE: return AFMT_S24_BE; + case SNDRV_PCM_FORMAT_S24_3LE: return AFMT_S24_PACKED; + case SNDRV_PCM_FORMAT_FLOAT: return AFMT_FLOAT; + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME: return AFMT_SPDIF_RAW; default: return -EINVAL; } } @@ -1725,7 +1755,10 @@ return AFMT_MU_LAW | AFMT_U8 | AFMT_S16_LE | AFMT_S16_BE | AFMT_S8 | AFMT_U16_LE | - AFMT_U16_BE; + AFMT_U16_BE | + AFMT_S32_LE | AFMT_S32_BE | + AFMT_S24_LE | AFMT_S24_LE | + AFMT_S24_PACKED; params = kmalloc(sizeof(*params), GFP_KERNEL); if (!params) return -ENOMEM; diff -Nur sound/core/oss/pcm_plugin.c sound/core/oss/pcm_plugin.c --- sound/core/oss/pcm_plugin.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/core/oss/pcm_plugin.c 2007-08-09 02:00:06.000000000 +0200 @@ -25,9 +25,6 @@ #endif #include - -#ifdef CONFIG_SND_PCM_OSS_PLUGINS - #include #include #include @@ -267,6 +264,8 @@ SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_BE | SNDRV_PCM_FMTBIT_S24_BE | + SNDRV_PCM_FMTBIT_U24_3LE | SNDRV_PCM_FMTBIT_S24_3LE | + 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); @@ -283,6 +282,10 @@ SNDRV_PCM_FORMAT_S16_BE, SNDRV_PCM_FORMAT_U16_LE, SNDRV_PCM_FORMAT_U16_BE, + SNDRV_PCM_FORMAT_S24_3LE, + SNDRV_PCM_FORMAT_S24_3BE, + SNDRV_PCM_FORMAT_U24_3LE, + SNDRV_PCM_FORMAT_U24_3BE, SNDRV_PCM_FORMAT_S24_LE, SNDRV_PCM_FORMAT_S24_BE, SNDRV_PCM_FORMAT_U24_LE, @@ -297,41 +300,37 @@ int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask) { + int i; + if (snd_mask_test(format_mask, format)) return format; if (! snd_pcm_plug_formats(format_mask, format)) return -EINVAL; if (snd_pcm_format_linear(format)) { - int width = snd_pcm_format_width(format); - int unsignd = snd_pcm_format_unsigned(format); - int big = snd_pcm_format_big_endian(format); - int format1; - int wid, width1=width; - int dwidth1 = 8; - for (wid = 0; wid < 4; ++wid) { - int end, big1 = big; - for (end = 0; end < 2; ++end) { - int sgn, unsignd1 = unsignd; - for (sgn = 0; sgn < 2; ++sgn) { - format1 = snd_pcm_build_linear_format(width1, unsignd1, big1); - if (format1 >= 0 && - snd_mask_test(format_mask, format1)) - goto _found; - unsignd1 = !unsignd1; - } - big1 = !big1; - } - if (width1 == 32) { - dwidth1 = -dwidth1; - width1 = width; + 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; + for (i = 0; i < ARRAY_SIZE(preferred_formats); i++) { + int f = preferred_formats[i]; + unsigned int w; + if (!snd_mask_test(format_mask, f)) + continue; + w = snd_pcm_format_width(f); + if (w >= width) + badness = w - width; + else + badness = width - w + 32; + badness += snd_pcm_format_unsigned(f) != unsignd; + badness += snd_pcm_format_big_endian(f) != big; + if (badness < best) { + best_format = f; + best = badness; } - width1 += dwidth1; } - return -EINVAL; - _found: - return format1; + return best_format >= 0 ? best_format : -EINVAL; } else { - unsigned int i; switch (format) { case SNDRV_PCM_FORMAT_MU_LAW: for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) { @@ -740,5 +739,3 @@ } return 0; } - -#endif diff -Nur sound/core/oss/plugin_ops.h sound/core/oss/plugin_ops.h --- sound/core/oss/plugin_ops.h 2007-07-09 01:32:17.000000000 +0200 +++ sound/core/oss/plugin_ops.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,370 +0,0 @@ -/* - * Plugin sample operators with fast switch - * Copyright (c) 2000 by Jaroslav Kysela - * - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library 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 Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - - -#define as_u8(ptr) (*(u_int8_t*)(ptr)) -#define as_u16(ptr) (*(u_int16_t*)(ptr)) -#define as_u32(ptr) (*(u_int32_t*)(ptr)) -#define as_u64(ptr) (*(u_int64_t*)(ptr)) -#define as_s8(ptr) (*(int8_t*)(ptr)) -#define as_s16(ptr) (*(int16_t*)(ptr)) -#define as_s32(ptr) (*(int32_t*)(ptr)) -#define as_s64(ptr) (*(int64_t*)(ptr)) - -#ifdef COPY_LABELS -static void *copy_labels[4] = { - &©_8, - &©_16, - &©_32, - &©_64 -}; -#endif - -#ifdef COPY_END -while(0) { -copy_8: as_s8(dst) = as_s8(src); goto COPY_END; -copy_16: as_s16(dst) = as_s16(src); goto COPY_END; -copy_32: as_s32(dst) = as_s32(src); goto COPY_END; -copy_64: as_s64(dst) = as_s64(src); goto COPY_END; -} -#endif - -#ifdef CONV_LABELS -/* src_wid src_endswap sign_toggle dst_wid dst_endswap */ -static void *conv_labels[4 * 2 * 2 * 4 * 2] = { - &&conv_xxx1_xxx1, /* 8h -> 8h */ - &&conv_xxx1_xxx1, /* 8h -> 8s */ - &&conv_xxx1_xx10, /* 8h -> 16h */ - &&conv_xxx1_xx01, /* 8h -> 16s */ - &&conv_xxx1_x100, /* 8h -> 24h */ - &&conv_xxx1_001x, /* 8h -> 24s */ - &&conv_xxx1_1000, /* 8h -> 32h */ - &&conv_xxx1_0001, /* 8h -> 32s */ - &&conv_xxx1_xxx9, /* 8h ^> 8h */ - &&conv_xxx1_xxx9, /* 8h ^> 8s */ - &&conv_xxx1_xx90, /* 8h ^> 16h */ - &&conv_xxx1_xx09, /* 8h ^> 16s */ - &&conv_xxx1_x900, /* 8h ^> 24h */ - &&conv_xxx1_009x, /* 8h ^> 24s */ - &&conv_xxx1_9000, /* 8h ^> 32h */ - &&conv_xxx1_0009, /* 8h ^> 32s */ - &&conv_xxx1_xxx1, /* 8s -> 8h */ - &&conv_xxx1_xxx1, /* 8s -> 8s */ - &&conv_xxx1_xx10, /* 8s -> 16h */ - &&conv_xxx1_xx01, /* 8s -> 16s */ - &&conv_xxx1_x100, /* 8s -> 24h */ - &&conv_xxx1_001x, /* 8s -> 24s */ - &&conv_xxx1_1000, /* 8s -> 32h */ - &&conv_xxx1_0001, /* 8s -> 32s */ - &&conv_xxx1_xxx9, /* 8s ^> 8h */ - &&conv_xxx1_xxx9, /* 8s ^> 8s */ - &&conv_xxx1_xx90, /* 8s ^> 16h */ - &&conv_xxx1_xx09, /* 8s ^> 16s */ - &&conv_xxx1_x900, /* 8s ^> 24h */ - &&conv_xxx1_009x, /* 8s ^> 24s */ - &&conv_xxx1_9000, /* 8s ^> 32h */ - &&conv_xxx1_0009, /* 8s ^> 32s */ - &&conv_xx12_xxx1, /* 16h -> 8h */ - &&conv_xx12_xxx1, /* 16h -> 8s */ - &&conv_xx12_xx12, /* 16h -> 16h */ - &&conv_xx12_xx21, /* 16h -> 16s */ - &&conv_xx12_x120, /* 16h -> 24h */ - &&conv_xx12_021x, /* 16h -> 24s */ - &&conv_xx12_1200, /* 16h -> 32h */ - &&conv_xx12_0021, /* 16h -> 32s */ - &&conv_xx12_xxx9, /* 16h ^> 8h */ - &&conv_xx12_xxx9, /* 16h ^> 8s */ - &&conv_xx12_xx92, /* 16h ^> 16h */ - &&conv_xx12_xx29, /* 16h ^> 16s */ - &&conv_xx12_x920, /* 16h ^> 24h */ - &&conv_xx12_029x, /* 16h ^> 24s */ - &&conv_xx12_9200, /* 16h ^> 32h */ - &&conv_xx12_0029, /* 16h ^> 32s */ - &&conv_xx12_xxx2, /* 16s -> 8h */ - &&conv_xx12_xxx2, /* 16s -> 8s */ - &&conv_xx12_xx21, /* 16s -> 16h */ - &&conv_xx12_xx12, /* 16s -> 16s */ - &&conv_xx12_x210, /* 16s -> 24h */ - &&conv_xx12_012x, /* 16s -> 24s */ - &&conv_xx12_2100, /* 16s -> 32h */ - &&conv_xx12_0012, /* 16s -> 32s */ - &&conv_xx12_xxxA, /* 16s ^> 8h */ - &&conv_xx12_xxxA, /* 16s ^> 8s */ - &&conv_xx12_xxA1, /* 16s ^> 16h */ - &&conv_xx12_xx1A, /* 16s ^> 16s */ - &&conv_xx12_xA10, /* 16s ^> 24h */ - &&conv_xx12_01Ax, /* 16s ^> 24s */ - &&conv_xx12_A100, /* 16s ^> 32h */ - &&conv_xx12_001A, /* 16s ^> 32s */ - &&conv_x123_xxx1, /* 24h -> 8h */ - &&conv_x123_xxx1, /* 24h -> 8s */ - &&conv_x123_xx12, /* 24h -> 16h */ - &&conv_x123_xx21, /* 24h -> 16s */ - &&conv_x123_x123, /* 24h -> 24h */ - &&conv_x123_321x, /* 24h -> 24s */ - &&conv_x123_1230, /* 24h -> 32h */ - &&conv_x123_0321, /* 24h -> 32s */ - &&conv_x123_xxx9, /* 24h ^> 8h */ - &&conv_x123_xxx9, /* 24h ^> 8s */ - &&conv_x123_xx92, /* 24h ^> 16h */ - &&conv_x123_xx29, /* 24h ^> 16s */ - &&conv_x123_x923, /* 24h ^> 24h */ - &&conv_x123_329x, /* 24h ^> 24s */ - &&conv_x123_9230, /* 24h ^> 32h */ - &&conv_x123_0329, /* 24h ^> 32s */ - &&conv_123x_xxx3, /* 24s -> 8h */ - &&conv_123x_xxx3, /* 24s -> 8s */ - &&conv_123x_xx32, /* 24s -> 16h */ - &&conv_123x_xx23, /* 24s -> 16s */ - &&conv_123x_x321, /* 24s -> 24h */ - &&conv_123x_123x, /* 24s -> 24s */ - &&conv_123x_3210, /* 24s -> 32h */ - &&conv_123x_0123, /* 24s -> 32s */ - &&conv_123x_xxxB, /* 24s ^> 8h */ - &&conv_123x_xxxB, /* 24s ^> 8s */ - &&conv_123x_xxB2, /* 24s ^> 16h */ - &&conv_123x_xx2B, /* 24s ^> 16s */ - &&conv_123x_xB21, /* 24s ^> 24h */ - &&conv_123x_12Bx, /* 24s ^> 24s */ - &&conv_123x_B210, /* 24s ^> 32h */ - &&conv_123x_012B, /* 24s ^> 32s */ - &&conv_1234_xxx1, /* 32h -> 8h */ - &&conv_1234_xxx1, /* 32h -> 8s */ - &&conv_1234_xx12, /* 32h -> 16h */ - &&conv_1234_xx21, /* 32h -> 16s */ - &&conv_1234_x123, /* 32h -> 24h */ - &&conv_1234_321x, /* 32h -> 24s */ - &&conv_1234_1234, /* 32h -> 32h */ - &&conv_1234_4321, /* 32h -> 32s */ - &&conv_1234_xxx9, /* 32h ^> 8h */ - &&conv_1234_xxx9, /* 32h ^> 8s */ - &&conv_1234_xx92, /* 32h ^> 16h */ - &&conv_1234_xx29, /* 32h ^> 16s */ - &&conv_1234_x923, /* 32h ^> 24h */ - &&conv_1234_329x, /* 32h ^> 24s */ - &&conv_1234_9234, /* 32h ^> 32h */ - &&conv_1234_4329, /* 32h ^> 32s */ - &&conv_1234_xxx4, /* 32s -> 8h */ - &&conv_1234_xxx4, /* 32s -> 8s */ - &&conv_1234_xx43, /* 32s -> 16h */ - &&conv_1234_xx34, /* 32s -> 16s */ - &&conv_1234_x432, /* 32s -> 24h */ - &&conv_1234_234x, /* 32s -> 24s */ - &&conv_1234_4321, /* 32s -> 32h */ - &&conv_1234_1234, /* 32s -> 32s */ - &&conv_1234_xxxC, /* 32s ^> 8h */ - &&conv_1234_xxxC, /* 32s ^> 8s */ - &&conv_1234_xxC3, /* 32s ^> 16h */ - &&conv_1234_xx3C, /* 32s ^> 16s */ - &&conv_1234_xC32, /* 32s ^> 24h */ - &&conv_1234_23Cx, /* 32s ^> 24s */ - &&conv_1234_C321, /* 32s ^> 32h */ - &&conv_1234_123C, /* 32s ^> 32s */ -}; -#endif - -#ifdef CONV_END -while(0) { -conv_xxx1_xxx1: as_u8(dst) = as_u8(src); goto CONV_END; -conv_xxx1_xx10: as_u16(dst) = (u_int16_t)as_u8(src) << 8; goto CONV_END; -conv_xxx1_xx01: as_u16(dst) = (u_int16_t)as_u8(src); goto CONV_END; -conv_xxx1_x100: as_u32(dst) = (u_int32_t)as_u8(src) << 16; goto CONV_END; -conv_xxx1_001x: as_u32(dst) = (u_int32_t)as_u8(src) << 8; goto CONV_END; -conv_xxx1_1000: as_u32(dst) = (u_int32_t)as_u8(src) << 24; goto CONV_END; -conv_xxx1_0001: as_u32(dst) = (u_int32_t)as_u8(src); goto CONV_END; -conv_xxx1_xxx9: as_u8(dst) = as_u8(src) ^ 0x80; goto CONV_END; -conv_xxx1_xx90: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80) << 8; goto CONV_END; -conv_xxx1_xx09: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80); goto CONV_END; -conv_xxx1_x900: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 16; goto CONV_END; -conv_xxx1_009x: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 8; goto CONV_END; -conv_xxx1_9000: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 24; goto CONV_END; -conv_xxx1_0009: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80); goto CONV_END; -conv_xx12_xxx1: as_u8(dst) = as_u16(src) >> 8; goto CONV_END; -conv_xx12_xx12: as_u16(dst) = as_u16(src); goto CONV_END; -conv_xx12_xx21: as_u16(dst) = swab16(as_u16(src)); goto CONV_END; -conv_xx12_x120: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto CONV_END; -conv_xx12_021x: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 8; goto CONV_END; -conv_xx12_1200: as_u32(dst) = (u_int32_t)as_u16(src) << 16; goto CONV_END; -conv_xx12_0021: as_u32(dst) = (u_int32_t)swab16(as_u16(src)); goto CONV_END; -conv_xx12_xxx9: as_u8(dst) = (as_u16(src) >> 8) ^ 0x80; goto CONV_END; -conv_xx12_xx92: as_u16(dst) = as_u16(src) ^ 0x8000; goto CONV_END; -conv_xx12_xx29: as_u16(dst) = swab16(as_u16(src)) ^ 0x80; goto CONV_END; -conv_xx12_x920: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 8; goto CONV_END; -conv_xx12_029x: as_u32(dst) = (u_int32_t)(swab16(as_u16(src)) ^ 0x80) << 8; goto CONV_END; -conv_xx12_9200: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 16; goto CONV_END; -conv_xx12_0029: as_u32(dst) = (u_int32_t)(swab16(as_u16(src)) ^ 0x80); goto CONV_END; -conv_xx12_xxx2: as_u8(dst) = as_u16(src) & 0xff; goto CONV_END; -conv_xx12_x210: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 8; goto CONV_END; -conv_xx12_012x: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto CONV_END; -conv_xx12_2100: as_u32(dst) = (u_int32_t)swab16(as_u16(src)) << 16; goto CONV_END; -conv_xx12_0012: as_u32(dst) = (u_int32_t)as_u16(src); goto CONV_END; -conv_xx12_xxxA: as_u8(dst) = (as_u16(src) ^ 0x80) & 0xff; goto CONV_END; -conv_xx12_xxA1: as_u16(dst) = swab16(as_u16(src) ^ 0x80); goto CONV_END; -conv_xx12_xx1A: as_u16(dst) = as_u16(src) ^ 0x80; goto CONV_END; -conv_xx12_xA10: as_u32(dst) = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 8; goto CONV_END; -conv_xx12_01Ax: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80) << 8; goto CONV_END; -conv_xx12_A100: as_u32(dst) = (u_int32_t)swab16(as_u16(src) ^ 0x80) << 16; goto CONV_END; -conv_xx12_001A: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80); goto CONV_END; -conv_x123_xxx1: as_u8(dst) = as_u32(src) >> 16; goto CONV_END; -conv_x123_xx12: as_u16(dst) = as_u32(src) >> 8; goto CONV_END; -conv_x123_xx21: as_u16(dst) = swab16(as_u32(src) >> 8); goto CONV_END; -conv_x123_x123: as_u32(dst) = as_u32(src); goto CONV_END; -conv_x123_321x: as_u32(dst) = swab32(as_u32(src)); goto CONV_END; -conv_x123_1230: as_u32(dst) = as_u32(src) << 8; goto CONV_END; -conv_x123_0321: as_u32(dst) = swab32(as_u32(src)) >> 8; goto CONV_END; -conv_x123_xxx9: as_u8(dst) = (as_u32(src) >> 16) ^ 0x80; goto CONV_END; -conv_x123_xx92: as_u16(dst) = (as_u32(src) >> 8) ^ 0x8000; goto CONV_END; -conv_x123_xx29: as_u16(dst) = swab16(as_u32(src) >> 8) ^ 0x80; goto CONV_END; -conv_x123_x923: as_u32(dst) = as_u32(src) ^ 0x800000; goto CONV_END; -conv_x123_329x: as_u32(dst) = swab32(as_u32(src)) ^ 0x8000; goto CONV_END; -conv_x123_9230: as_u32(dst) = (as_u32(src) ^ 0x800000) << 8; goto CONV_END; -conv_x123_0329: as_u32(dst) = (swab32(as_u32(src)) >> 8) ^ 0x80; goto CONV_END; -conv_123x_xxx3: as_u8(dst) = (as_u32(src) >> 8) & 0xff; goto CONV_END; -conv_123x_xx32: as_u16(dst) = swab16(as_u32(src) >> 8); goto CONV_END; -conv_123x_xx23: as_u16(dst) = (as_u32(src) >> 8) & 0xffff; goto CONV_END; -conv_123x_x321: as_u32(dst) = swab32(as_u32(src)); goto CONV_END; -conv_123x_123x: as_u32(dst) = as_u32(src); goto CONV_END; -conv_123x_3210: as_u32(dst) = swab32(as_u32(src)) << 8; goto CONV_END; -conv_123x_0123: as_u32(dst) = as_u32(src) >> 8; goto CONV_END; -conv_123x_xxxB: as_u8(dst) = ((as_u32(src) >> 8) & 0xff) ^ 0x80; goto CONV_END; -conv_123x_xxB2: as_u16(dst) = swab16((as_u32(src) >> 8) ^ 0x80); goto CONV_END; -conv_123x_xx2B: as_u16(dst) = ((as_u32(src) >> 8) & 0xffff) ^ 0x80; goto CONV_END; -conv_123x_xB21: as_u32(dst) = swab32(as_u32(src)) ^ 0x800000; goto CONV_END; -conv_123x_12Bx: as_u32(dst) = as_u32(src) ^ 0x8000; goto CONV_END; -conv_123x_B210: as_u32(dst) = swab32(as_u32(src) ^ 0x8000) << 8; goto CONV_END; -conv_123x_012B: as_u32(dst) = (as_u32(src) >> 8) ^ 0x80; goto CONV_END; -conv_1234_xxx1: as_u8(dst) = as_u32(src) >> 24; goto CONV_END; -conv_1234_xx12: as_u16(dst) = as_u32(src) >> 16; goto CONV_END; -conv_1234_xx21: as_u16(dst) = swab16(as_u32(src) >> 16); goto CONV_END; -conv_1234_x123: as_u32(dst) = as_u32(src) >> 8; goto CONV_END; -conv_1234_321x: as_u32(dst) = swab32(as_u32(src)) << 8; goto CONV_END; -conv_1234_1234: as_u32(dst) = as_u32(src); goto CONV_END; -conv_1234_4321: as_u32(dst) = swab32(as_u32(src)); goto CONV_END; -conv_1234_xxx9: as_u8(dst) = (as_u32(src) >> 24) ^ 0x80; goto CONV_END; -conv_1234_xx92: as_u16(dst) = (as_u32(src) >> 16) ^ 0x8000; goto CONV_END; -conv_1234_xx29: as_u16(dst) = swab16(as_u32(src) >> 16) ^ 0x80; goto CONV_END; -conv_1234_x923: as_u32(dst) = (as_u32(src) >> 8) ^ 0x800000; goto CONV_END; -conv_1234_329x: as_u32(dst) = (swab32(as_u32(src)) ^ 0x80) << 8; goto CONV_END; -conv_1234_9234: as_u32(dst) = as_u32(src) ^ 0x80000000; goto CONV_END; -conv_1234_4329: as_u32(dst) = swab32(as_u32(src)) ^ 0x80; goto CONV_END; -conv_1234_xxx4: as_u8(dst) = as_u32(src) & 0xff; goto CONV_END; -conv_1234_xx43: as_u16(dst) = swab16(as_u32(src)); goto CONV_END; -conv_1234_xx34: as_u16(dst) = as_u32(src) & 0xffff; goto CONV_END; -conv_1234_x432: as_u32(dst) = swab32(as_u32(src)) >> 8; goto CONV_END; -conv_1234_234x: as_u32(dst) = as_u32(src) << 8; goto CONV_END; -conv_1234_xxxC: as_u8(dst) = (as_u32(src) & 0xff) ^ 0x80; goto CONV_END; -conv_1234_xxC3: as_u16(dst) = swab16(as_u32(src) ^ 0x80); goto CONV_END; -conv_1234_xx3C: as_u16(dst) = (as_u32(src) & 0xffff) ^ 0x80; goto CONV_END; -conv_1234_xC32: as_u32(dst) = (swab32(as_u32(src)) >> 8) ^ 0x800000; goto CONV_END; -conv_1234_23Cx: as_u32(dst) = (as_u32(src) ^ 0x80) << 8; goto CONV_END; -conv_1234_C321: as_u32(dst) = swab32(as_u32(src) ^ 0x80); goto CONV_END; -conv_1234_123C: as_u32(dst) = as_u32(src) ^ 0x80; goto CONV_END; -} -#endif - -#ifdef GET_S16_LABELS -/* src_wid src_endswap unsigned */ -static void *get_s16_labels[4 * 2 * 2] = { - &&get_s16_xxx1_xx10, /* 8h -> 16h */ - &&get_s16_xxx1_xx90, /* 8h ^> 16h */ - &&get_s16_xxx1_xx10, /* 8s -> 16h */ - &&get_s16_xxx1_xx90, /* 8s ^> 16h */ - &&get_s16_xx12_xx12, /* 16h -> 16h */ - &&get_s16_xx12_xx92, /* 16h ^> 16h */ - &&get_s16_xx12_xx21, /* 16s -> 16h */ - &&get_s16_xx12_xxA1, /* 16s ^> 16h */ - &&get_s16_x123_xx12, /* 24h -> 16h */ - &&get_s16_x123_xx92, /* 24h ^> 16h */ - &&get_s16_123x_xx32, /* 24s -> 16h */ - &&get_s16_123x_xxB2, /* 24s ^> 16h */ - &&get_s16_1234_xx12, /* 32h -> 16h */ - &&get_s16_1234_xx92, /* 32h ^> 16h */ - &&get_s16_1234_xx43, /* 32s -> 16h */ - &&get_s16_1234_xxC3, /* 32s ^> 16h */ -}; -#endif - -#ifdef GET_S16_END -while(0) { -get_s16_xxx1_xx10: sample = (u_int16_t)as_u8(src) << 8; goto GET_S16_END; -get_s16_xxx1_xx90: sample = (u_int16_t)(as_u8(src) ^ 0x80) << 8; goto GET_S16_END; -get_s16_xx12_xx12: sample = as_u16(src); goto GET_S16_END; -get_s16_xx12_xx92: sample = as_u16(src) ^ 0x8000; goto GET_S16_END; -get_s16_xx12_xx21: sample = swab16(as_u16(src)); goto GET_S16_END; -get_s16_xx12_xxA1: sample = swab16(as_u16(src) ^ 0x80); goto GET_S16_END; -get_s16_x123_xx12: sample = as_u32(src) >> 8; goto GET_S16_END; -get_s16_x123_xx92: sample = (as_u32(src) >> 8) ^ 0x8000; goto GET_S16_END; -get_s16_123x_xx32: sample = swab16(as_u32(src) >> 8); goto GET_S16_END; -get_s16_123x_xxB2: sample = swab16((as_u32(src) >> 8) ^ 0x8000); goto GET_S16_END; -get_s16_1234_xx12: sample = as_u32(src) >> 16; goto GET_S16_END; -get_s16_1234_xx92: sample = (as_u32(src) >> 16) ^ 0x8000; goto GET_S16_END; -get_s16_1234_xx43: sample = swab16(as_u32(src)); goto GET_S16_END; -get_s16_1234_xxC3: sample = swab16(as_u32(src) ^ 0x80); goto GET_S16_END; -} -#endif - -#ifdef PUT_S16_LABELS -/* dst_wid dst_endswap unsigned */ -static void *put_s16_labels[4 * 2 * 2] = { - &&put_s16_xx12_xxx1, /* 16h -> 8h */ - &&put_s16_xx12_xxx9, /* 16h ^> 8h */ - &&put_s16_xx12_xxx1, /* 16h -> 8s */ - &&put_s16_xx12_xxx9, /* 16h ^> 8s */ - &&put_s16_xx12_xx12, /* 16h -> 16h */ - &&put_s16_xx12_xx92, /* 16h ^> 16h */ - &&put_s16_xx12_xx21, /* 16h -> 16s */ - &&put_s16_xx12_xx29, /* 16h ^> 16s */ - &&put_s16_xx12_x120, /* 16h -> 24h */ - &&put_s16_xx12_x920, /* 16h ^> 24h */ - &&put_s16_xx12_021x, /* 16h -> 24s */ - &&put_s16_xx12_029x, /* 16h ^> 24s */ - &&put_s16_xx12_1200, /* 16h -> 32h */ - &&put_s16_xx12_9200, /* 16h ^> 32h */ - &&put_s16_xx12_0021, /* 16h -> 32s */ - &&put_s16_xx12_0029, /* 16h ^> 32s */ -}; -#endif - -#ifdef PUT_S16_END -while (0) { -put_s16_xx12_xxx1: as_u8(dst) = sample >> 8; goto PUT_S16_END; -put_s16_xx12_xxx9: as_u8(dst) = (sample >> 8) ^ 0x80; goto PUT_S16_END; -put_s16_xx12_xx12: as_u16(dst) = sample; goto PUT_S16_END; -put_s16_xx12_xx92: as_u16(dst) = sample ^ 0x8000; goto PUT_S16_END; -put_s16_xx12_xx21: as_u16(dst) = swab16(sample); goto PUT_S16_END; -put_s16_xx12_xx29: as_u16(dst) = swab16(sample) ^ 0x80; goto PUT_S16_END; -put_s16_xx12_x120: as_u32(dst) = (u_int32_t)sample << 8; goto PUT_S16_END; -put_s16_xx12_x920: as_u32(dst) = (u_int32_t)(sample ^ 0x8000) << 8; goto PUT_S16_END; -put_s16_xx12_021x: as_u32(dst) = (u_int32_t)swab16(sample) << 8; goto PUT_S16_END; -put_s16_xx12_029x: as_u32(dst) = (u_int32_t)(swab16(sample) ^ 0x80) << 8; goto PUT_S16_END; -put_s16_xx12_1200: as_u32(dst) = (u_int32_t)sample << 16; goto PUT_S16_END; -put_s16_xx12_9200: as_u32(dst) = (u_int32_t)(sample ^ 0x8000) << 16; goto PUT_S16_END; -put_s16_xx12_0021: as_u32(dst) = (u_int32_t)swab16(sample); goto PUT_S16_END; -put_s16_xx12_0029: as_u32(dst) = (u_int32_t)swab16(sample) ^ 0x80; goto PUT_S16_END; -} -#endif - -#undef as_u8 -#undef as_u16 -#undef as_u32 -#undef as_s8 -#undef as_s16 -#undef as_s32 diff -Nur sound/core/oss/rate.c sound/core/oss/rate.c --- sound/core/oss/rate.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/core/oss/rate.c 2007-08-09 02:00:06.000000000 +0200 @@ -20,9 +20,6 @@ */ #include - -#ifdef CONFIG_SND_PCM_OSS_PLUGINS - #include #include #include @@ -340,5 +337,3 @@ *r_plugin = plugin; return 0; } - -#endif diff -Nur sound/core/oss/route.c sound/core/oss/route.c --- sound/core/oss/route.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/core/oss/route.c 2007-08-09 02:00:06.000000000 +0200 @@ -20,9 +20,6 @@ */ #include - -#ifdef CONFIG_SND_PCM_OSS_PLUGINS - #include #include #include @@ -108,5 +105,3 @@ *r_plugin = plugin; return 0; } - -#endif diff -Nur sound/core/pcm_misc.c sound/core/pcm_misc.c --- sound/core/pcm_misc.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/core/pcm_misc.c 2007-08-14 02:00:08.000000000 +0200 @@ -422,38 +422,6 @@ EXPORT_SYMBOL(snd_pcm_format_set_silence); -/* [width][unsigned][bigendian] */ -static int linear_formats[4][2][2] = { - {{ SNDRV_PCM_FORMAT_S8, SNDRV_PCM_FORMAT_S8}, - { SNDRV_PCM_FORMAT_U8, SNDRV_PCM_FORMAT_U8}}, - {{SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_FORMAT_S16_BE}, - {SNDRV_PCM_FORMAT_U16_LE, SNDRV_PCM_FORMAT_U16_BE}}, - {{SNDRV_PCM_FORMAT_S24_LE, SNDRV_PCM_FORMAT_S24_BE}, - {SNDRV_PCM_FORMAT_U24_LE, SNDRV_PCM_FORMAT_U24_BE}}, - {{SNDRV_PCM_FORMAT_S32_LE, SNDRV_PCM_FORMAT_S32_BE}, - {SNDRV_PCM_FORMAT_U32_LE, SNDRV_PCM_FORMAT_U32_BE}} -}; - -/** - * snd_pcm_build_linear_format - return the suitable linear format for the given condition - * @width: the bit-width - * @unsignd: 1 if unsigned, 0 if signed. - * @big_endian: 1 if big-endian, 0 if little-endian - * - * Returns the suitable linear format for the given condition. - */ -snd_pcm_format_t snd_pcm_build_linear_format(int width, int unsignd, int big_endian) -{ - if (width & 7) - return SND_PCM_FORMAT_UNKNOWN; - width = (width / 8) - 1; - if (width < 0 || width >= 4) - return SND_PCM_FORMAT_UNKNOWN; - return linear_formats[width][!!unsignd][!!big_endian]; -} - -EXPORT_SYMBOL(snd_pcm_build_linear_format); - /** * snd_pcm_limit_hw_rates - determine rate_min/rate_max fields * @runtime: the runtime instance @@ -465,21 +433,16 @@ */ int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime) { - static unsigned rates[] = { - /* ATTENTION: these values depend on the definition in pcm.h! */ - 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, - 64000, 88200, 96000, 176400, 192000 - }; int i; - for (i = 0; i < (int)ARRAY_SIZE(rates); i++) { + for (i = 0; i < (int)snd_pcm_known_rates.count; i++) { if (runtime->hw.rates & (1 << i)) { - runtime->hw.rate_min = rates[i]; + runtime->hw.rate_min = snd_pcm_known_rates.list[i]; break; } } - for (i = (int)ARRAY_SIZE(rates) - 1; i >= 0; i--) { + for (i = (int)snd_pcm_known_rates.count - 1; i >= 0; i--) { if (runtime->hw.rates & (1 << i)) { - runtime->hw.rate_max = rates[i]; + runtime->hw.rate_max = snd_pcm_known_rates.list[i]; break; } } @@ -487,3 +450,21 @@ } EXPORT_SYMBOL(snd_pcm_limit_hw_rates); + +/** + * snd_pcm_rate_to_rate_bit - converts sample rate to SNDRV_PCM_RATE_xxx bit + * @rate: the sample rate to convert + * + * Returns the SNDRV_PCM_RATE_xxx flag that corresponds to the given rate, or + * SNDRV_PCM_RATE_KNOT for an unknown rate. + */ +unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate) +{ + unsigned int i; + + for (i = 0; i < snd_pcm_known_rates.count; i++) + if (snd_pcm_known_rates.list[i] == rate) + return 1u << i; + return SNDRV_PCM_RATE_KNOT; +} +EXPORT_SYMBOL(snd_pcm_rate_to_rate_bit); diff -Nur sound/core/pcm_native.c sound/core/pcm_native.c --- sound/core/pcm_native.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/core/pcm_native.c 2007-08-14 02:00:08.000000000 +0200 @@ -1487,7 +1487,7 @@ snd_pcm_stream_lock_irq(substream); /* resume pause */ - if (runtime->status->state == SNDRV_PCM_STATE_PAUSED) + if (substream->runtime->status->state == SNDRV_PCM_STATE_PAUSED) snd_pcm_pause(substream, 0); /* pre-start/stop - all running streams are changed to DRAINING state */ @@ -1787,12 +1787,18 @@ static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000 }; +const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, +}; + static int snd_pcm_hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_pcm_hardware *hw = rule->private; return snd_interval_list(hw_param_interval(params, rule->var), - ARRAY_SIZE(rates), rates, hw->rates); + snd_pcm_known_rates.count, + snd_pcm_known_rates.list, hw->rates); } static int snd_pcm_hw_rule_buffer_bytes_max(struct snd_pcm_hw_params *params, diff -Nur sound/core/rawmidi.c sound/core/rawmidi.c --- sound/core/rawmidi.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/core/rawmidi.c 2007-07-24 02:00:09.000000000 +0200 @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include diff -Nur sound/core/seq/oss/seq_oss_init.c sound/core/seq/oss/seq_oss_init.c --- sound/core/seq/oss/seq_oss_init.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/core/seq/oss/seq_oss_init.c 2007-08-08 02:00:06.000000000 +0200 @@ -176,29 +176,29 @@ int i, rc; struct seq_oss_devinfo *dp; - if ((dp = kzalloc(sizeof(*dp), GFP_KERNEL)) == NULL) { + dp = kzalloc(sizeof(*dp), GFP_KERNEL); + if (!dp) { snd_printk(KERN_ERR "can't malloc device info\n"); return -ENOMEM; } debug_printk(("oss_open: dp = %p\n", dp)); + dp->cseq = system_client; + dp->port = -1; + dp->queue = -1; + for (i = 0; i < SNDRV_SEQ_OSS_MAX_CLIENTS; i++) { if (client_table[i] == NULL) break; } + + dp->index = i; if (i >= SNDRV_SEQ_OSS_MAX_CLIENTS) { snd_printk(KERN_ERR "too many applications\n"); - kfree(dp); - return -ENOMEM; + rc = -ENOMEM; + goto _error; } - dp->index = i; - dp->cseq = system_client; - dp->port = -1; - dp->queue = -1; - dp->readq = NULL; - dp->writeq = NULL; - /* look up synth and midi devices */ snd_seq_oss_synth_setup(dp); snd_seq_oss_midi_setup(dp); @@ -211,14 +211,16 @@ /* create port */ debug_printk(("create new port\n")); - if ((rc = create_port(dp)) < 0) { + rc = create_port(dp); + if (rc < 0) { snd_printk(KERN_ERR "can't create port\n"); goto _error; } /* allocate queue */ debug_printk(("allocate queue\n")); - if ((rc = alloc_seq_queue(dp)) < 0) + rc = alloc_seq_queue(dp); + if (rc < 0) goto _error; /* set address */ @@ -235,7 +237,8 @@ /* initialize read queue */ debug_printk(("initialize read queue\n")); if (is_read_mode(dp->file_mode)) { - if ((dp->readq = snd_seq_oss_readq_new(dp, maxqlen)) == NULL) { + dp->readq = snd_seq_oss_readq_new(dp, maxqlen); + if (!dp->readq) { rc = -ENOMEM; goto _error; } @@ -245,7 +248,7 @@ debug_printk(("initialize write queue\n")); if (is_write_mode(dp->file_mode)) { dp->writeq = snd_seq_oss_writeq_new(dp, maxqlen); - if (dp->writeq == NULL) { + if (!dp->writeq) { rc = -ENOMEM; goto _error; } @@ -253,7 +256,8 @@ /* initialize timer */ debug_printk(("initialize timer\n")); - if ((dp->timer = snd_seq_oss_timer_new(dp)) == NULL) { + dp->timer = snd_seq_oss_timer_new(dp); + if (!dp->timer) { snd_printk(KERN_ERR "can't alloc timer\n"); rc = -ENOMEM; goto _error; @@ -276,11 +280,13 @@ return 0; _error: + snd_seq_oss_writeq_delete(dp->writeq); + snd_seq_oss_readq_delete(dp->readq); snd_seq_oss_synth_cleanup(dp); snd_seq_oss_midi_cleanup(dp); - i = dp->queue; delete_port(dp); - delete_seq_queue(i); + delete_seq_queue(dp->queue); + kfree(dp); return rc; } diff -Nur sound/core/seq/oss/seq_oss_writeq.c sound/core/seq/oss/seq_oss_writeq.c --- sound/core/seq/oss/seq_oss_writeq.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/core/seq/oss/seq_oss_writeq.c 2007-08-08 02:00:06.000000000 +0200 @@ -63,8 +63,10 @@ void snd_seq_oss_writeq_delete(struct seq_oss_writeq *q) { - snd_seq_oss_writeq_clear(q); /* to be sure */ - kfree(q); + if (q) { + snd_seq_oss_writeq_clear(q); /* to be sure */ + kfree(q); + } } diff -Nur sound/core/seq/seq_instr.c sound/core/seq/seq_instr.c --- sound/core/seq/seq_instr.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/core/seq/seq_instr.c 2007-06-26 02:00:08.000000000 +0200 @@ -109,7 +109,7 @@ spin_lock_irqsave(&list->lock, flags); while (instr->use) { spin_unlock_irqrestore(&list->lock, flags); - schedule_timeout_interruptible(1); + schedule_timeout(1); spin_lock_irqsave(&list->lock, flags); } spin_unlock_irqrestore(&list->lock, flags); @@ -199,7 +199,7 @@ instr = flist; flist = instr->next; while (instr->use) - schedule_timeout_interruptible(1); + schedule_timeout(1); if (snd_seq_instr_free(instr, atomic)<0) snd_printk(KERN_WARNING "instrument free problem\n"); instr = next; @@ -555,7 +555,7 @@ SNDRV_SEQ_INSTR_NOTIFY_REMOVE); while (instr->use) { spin_unlock_irqrestore(&list->lock, flags); - schedule_timeout_interruptible(1); + schedule_timeout(1); spin_lock_irqsave(&list->lock, flags); } spin_unlock_irqrestore(&list->lock, flags); diff -Nur sound/core/seq/seq_midi_event.c sound/core/seq/seq_midi_event.c --- sound/core/seq/seq_midi_event.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/core/seq/seq_midi_event.c 2007-08-11 02:00:08.000000000 +0200 @@ -32,10 +32,9 @@ MODULE_DESCRIPTION("MIDI byte <-> sequencer event coder"); MODULE_LICENSE("GPL"); -/* queue type */ -/* from 0 to 7 are normal commands (note off, on, etc.) */ -#define ST_NOTEOFF 0 -#define ST_NOTEON 1 +/* event type, index into status_event[] */ +/* from 0 to 6 are normal commands (note off, on, etc.) for 0x9?-0xe? */ +#define ST_INVALID 7 #define ST_SPECIAL 8 #define ST_SYSEX ST_SPECIAL /* from 8 to 15 are events for 0xf0-0xf7 */ @@ -65,32 +64,33 @@ void (*encode)(struct snd_midi_event *dev, struct snd_seq_event *ev); void (*decode)(struct snd_seq_event *ev, unsigned char *buf); } status_event[] = { - /* 0x80 - 0xf0 */ - {SNDRV_SEQ_EVENT_NOTEOFF, 2, note_event, note_decode}, - {SNDRV_SEQ_EVENT_NOTEON, 2, note_event, note_decode}, - {SNDRV_SEQ_EVENT_KEYPRESS, 2, note_event, note_decode}, - {SNDRV_SEQ_EVENT_CONTROLLER, 2, two_param_ctrl_event, two_param_decode}, - {SNDRV_SEQ_EVENT_PGMCHANGE, 1, one_param_ctrl_event, one_param_decode}, - {SNDRV_SEQ_EVENT_CHANPRESS, 1, one_param_ctrl_event, one_param_decode}, - {SNDRV_SEQ_EVENT_PITCHBEND, 2, pitchbend_ctrl_event, pitchbend_decode}, - {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf0 */ + /* 0x80 - 0xef */ + {SNDRV_SEQ_EVENT_NOTEOFF, 2, note_event, note_decode}, + {SNDRV_SEQ_EVENT_NOTEON, 2, note_event, note_decode}, + {SNDRV_SEQ_EVENT_KEYPRESS, 2, note_event, note_decode}, + {SNDRV_SEQ_EVENT_CONTROLLER, 2, two_param_ctrl_event, two_param_decode}, + {SNDRV_SEQ_EVENT_PGMCHANGE, 1, one_param_ctrl_event, one_param_decode}, + {SNDRV_SEQ_EVENT_CHANPRESS, 1, one_param_ctrl_event, one_param_decode}, + {SNDRV_SEQ_EVENT_PITCHBEND, 2, pitchbend_ctrl_event, pitchbend_decode}, + /* invalid */ + {SNDRV_SEQ_EVENT_NONE, -1, NULL, NULL}, /* 0xf0 - 0xff */ - {SNDRV_SEQ_EVENT_SYSEX, 1, NULL, NULL}, /* sysex: 0xf0 */ - {SNDRV_SEQ_EVENT_QFRAME, 1, one_param_event, one_param_decode}, /* 0xf1 */ - {SNDRV_SEQ_EVENT_SONGPOS, 2, songpos_event, songpos_decode}, /* 0xf2 */ - {SNDRV_SEQ_EVENT_SONGSEL, 1, one_param_event, one_param_decode}, /* 0xf3 */ - {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf4 */ - {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf5 */ - {SNDRV_SEQ_EVENT_TUNE_REQUEST, 0, NULL, NULL}, /* 0xf6 */ - {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf7 */ - {SNDRV_SEQ_EVENT_CLOCK, 0, NULL, NULL}, /* 0xf8 */ - {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xf9 */ - {SNDRV_SEQ_EVENT_START, 0, NULL, NULL}, /* 0xfa */ - {SNDRV_SEQ_EVENT_CONTINUE, 0, NULL, NULL}, /* 0xfb */ - {SNDRV_SEQ_EVENT_STOP, 0, NULL, NULL}, /* 0xfc */ - {SNDRV_SEQ_EVENT_NONE, 0, NULL, NULL}, /* 0xfd */ - {SNDRV_SEQ_EVENT_SENSING, 0, NULL, NULL}, /* 0xfe */ - {SNDRV_SEQ_EVENT_RESET, 0, NULL, NULL}, /* 0xff */ + {SNDRV_SEQ_EVENT_SYSEX, 1, NULL, NULL}, /* sysex: 0xf0 */ + {SNDRV_SEQ_EVENT_QFRAME, 1, one_param_event, one_param_decode}, /* 0xf1 */ + {SNDRV_SEQ_EVENT_SONGPOS, 2, songpos_event, songpos_decode}, /* 0xf2 */ + {SNDRV_SEQ_EVENT_SONGSEL, 1, one_param_event, one_param_decode}, /* 0xf3 */ + {SNDRV_SEQ_EVENT_NONE, -1, NULL, NULL}, /* 0xf4 */ + {SNDRV_SEQ_EVENT_NONE, -1, NULL, NULL}, /* 0xf5 */ + {SNDRV_SEQ_EVENT_TUNE_REQUEST, 0, NULL, NULL}, /* 0xf6 */ + {SNDRV_SEQ_EVENT_NONE, -1, NULL, NULL}, /* 0xf7 */ + {SNDRV_SEQ_EVENT_CLOCK, 0, NULL, NULL}, /* 0xf8 */ + {SNDRV_SEQ_EVENT_NONE, -1, NULL, NULL}, /* 0xf9 */ + {SNDRV_SEQ_EVENT_START, 0, NULL, NULL}, /* 0xfa */ + {SNDRV_SEQ_EVENT_CONTINUE, 0, NULL, NULL}, /* 0xfb */ + {SNDRV_SEQ_EVENT_STOP, 0, NULL, NULL}, /* 0xfc */ + {SNDRV_SEQ_EVENT_NONE, -1, NULL, NULL}, /* 0xfd */ + {SNDRV_SEQ_EVENT_SENSING, 0, NULL, NULL}, /* 0xfe */ + {SNDRV_SEQ_EVENT_RESET, 0, NULL, NULL}, /* 0xff */ }; static int extra_decode_ctrl14(struct snd_midi_event *dev, unsigned char *buf, int len, @@ -129,6 +129,7 @@ } dev->bufsize = bufsize; dev->lastcmd = 0xff; + dev->type = ST_INVALID; spin_lock_init(&dev->lock); *rdev = dev; return 0; @@ -149,7 +150,7 @@ { dev->read = 0; dev->qlen = 0; - dev->type = 0; + dev->type = ST_INVALID; } void snd_midi_event_reset_encode(struct snd_midi_event *dev) @@ -251,29 +252,31 @@ ev->type = status_event[ST_SPECIAL + c - 0xf0].event; ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK; ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED; - return 1; + return ev->type != SNDRV_SEQ_EVENT_NONE; } spin_lock_irqsave(&dev->lock, flags); - if (dev->qlen > 0) { - /* rest of command */ - dev->buf[dev->read++] = c; - if (dev->type != ST_SYSEX) - dev->qlen--; - } else { + if ((c & 0x80) && + (c != MIDI_CMD_COMMON_SYSEX_END || dev->type != ST_SYSEX)) { /* new command */ + dev->buf[0] = c; + if ((c & 0xf0) == 0xf0) /* system messages */ + dev->type = (c & 0x0f) + ST_SPECIAL; + else + dev->type = (c >> 4) & 0x07; dev->read = 1; - if (c & 0x80) { - dev->buf[0] = c; - if ((c & 0xf0) == 0xf0) /* special events */ - dev->type = (c & 0x0f) + ST_SPECIAL; - else - dev->type = (c >> 4) & 0x07; - dev->qlen = status_event[dev->type].qlen; - } else { - /* process this byte as argument */ + dev->qlen = status_event[dev->type].qlen; + } else { + if (dev->qlen > 0) { + /* rest of command */ dev->buf[dev->read++] = c; + if (dev->type != ST_SYSEX) + dev->qlen--; + } else { + /* running status */ + dev->buf[1] = c; dev->qlen = status_event[dev->type].qlen - 1; + dev->read = 2; } } if (dev->qlen == 0) { @@ -282,6 +285,8 @@ ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED; if (status_event[dev->type].encode) /* set data values */ status_event[dev->type].encode(dev, ev); + if (dev->type >= ST_SPECIAL) + dev->type = ST_INVALID; rc = 1; } else if (dev->type == ST_SYSEX) { if (c == MIDI_CMD_COMMON_SYSEX_END || diff -Nur sound/core/seq/seq_virmidi.c sound/core/seq/seq_virmidi.c --- sound/core/seq/seq_virmidi.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/core/seq/seq_virmidi.c 2007-07-21 02:00:08.000000000 +0200 @@ -363,7 +363,7 @@ if (rdev->client >= 0) return 0; - pinfo = kmalloc(sizeof(*pinfo), GFP_KERNEL); + pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL); if (!pinfo) { err = -ENOMEM; goto __error; @@ -380,7 +380,6 @@ rdev->client = client; /* create a port */ - memset(pinfo, 0, sizeof(*pinfo)); pinfo->addr.client = client; sprintf(pinfo->name, "VirMIDI %d-%d", rdev->card->number, rdev->device); /* set all capabilities */ diff -Nur sound/core/sound.c sound/core/sound.c --- sound/core/sound.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/core/sound.c 2007-07-21 02:00:08.000000000 +0200 @@ -446,8 +446,7 @@ { snd_info_minor_unregister(); snd_info_done(); - if (unregister_chrdev(major, "alsa") != 0) - snd_printk(KERN_ERR "unable to unregister major device number %d\n", major); + unregister_chrdev(major, "alsa"); } module_init(alsa_sound_init) diff -Nur sound/core/timer.c sound/core/timer.c --- sound/core/timer.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/core/timer.c 2007-07-17 02:00:06.000000000 +0200 @@ -1549,9 +1549,11 @@ int err = 0; tu = file->private_data; - snd_assert(tu->timeri != NULL, return -ENXIO); + if (!tu->timeri) + return -EBADFD; t = tu->timeri->timer; - snd_assert(t != NULL, return -ENXIO); + if (!t) + return -EBADFD; info = kzalloc(sizeof(*info), GFP_KERNEL); if (! info) @@ -1579,9 +1581,11 @@ int err; tu = file->private_data; - snd_assert(tu->timeri != NULL, return -ENXIO); + if (!tu->timeri) + return -EBADFD; t = tu->timeri->timer; - snd_assert(t != NULL, return -ENXIO); + if (!t) + return -EBADFD; if (copy_from_user(¶ms, _params, sizeof(params))) return -EFAULT; if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE) && params.ticks < 1) { @@ -1675,7 +1679,8 @@ struct snd_timer_status status; tu = file->private_data; - snd_assert(tu->timeri != NULL, return -ENXIO); + if (!tu->timeri) + return -EBADFD; memset(&status, 0, sizeof(status)); status.tstamp = tu->tstamp; status.resolution = snd_timer_resolution(tu->timeri); @@ -1695,7 +1700,8 @@ struct snd_timer_user *tu; tu = file->private_data; - snd_assert(tu->timeri != NULL, return -ENXIO); + if (!tu->timeri) + return -EBADFD; snd_timer_stop(tu->timeri); tu->timeri->lost = 0; tu->last_resolution = 0; @@ -1708,7 +1714,8 @@ struct snd_timer_user *tu; tu = file->private_data; - snd_assert(tu->timeri != NULL, return -ENXIO); + if (!tu->timeri) + return -EBADFD; return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0; } @@ -1718,7 +1725,8 @@ struct snd_timer_user *tu; tu = file->private_data; - snd_assert(tu->timeri != NULL, return -ENXIO); + if (!tu->timeri) + return -EBADFD; tu->timeri->lost = 0; return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0; } @@ -1729,7 +1737,8 @@ struct snd_timer_user *tu; tu = file->private_data; - snd_assert(tu->timeri != NULL, return -ENXIO); + if (!tu->timeri) + return -EBADFD; return (err = snd_timer_pause(tu->timeri)) < 0 ? err : 0; } diff -Nur sound/drivers/dummy.c sound/drivers/dummy.c --- sound/drivers/dummy.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/drivers/dummy.c 2007-07-24 02:00:09.000000000 +0200 @@ -510,15 +510,7 @@ .get = snd_dummy_capsrc_get, .put = snd_dummy_capsrc_put, \ .private_value = addr } -static int snd_dummy_capsrc_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_dummy_capsrc_info snd_ctl_boolean_stereo_info static int snd_dummy_capsrc_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -659,7 +651,7 @@ }, }; -static void __init_or_module snd_dummy_unregister_all(void) +static void snd_dummy_unregister_all(void) { int i; diff -Nur sound/drivers/mpu401/mpu401.c sound/drivers/mpu401/mpu401.c --- sound/drivers/mpu401/mpu401.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/drivers/mpu401/mpu401.c 2007-06-26 02:00:08.000000000 +0200 @@ -228,7 +228,7 @@ static struct pnp_driver snd_mpu401_pnp_driver; #endif -static void __init_or_module snd_mpu401_unregister_all(void) +static void snd_mpu401_unregister_all(void) { int i; diff -Nur sound/drivers/mts64.c sound/drivers/mts64.c --- sound/drivers/mts64.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/drivers/mts64.c 2007-07-24 02:00:09.000000000 +0200 @@ -440,15 +440,7 @@ *********************************************************************/ /* SMPTE Switch */ -static int snd_mts64_ctl_smpte_switch_info(struct snd_kcontrol *kctl, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_mts64_ctl_smpte_switch_info snd_ctl_boolean_mono_info static int snd_mts64_ctl_smpte_switch_get(struct snd_kcontrol* kctl, struct snd_ctl_elem_value *uctl) diff -Nur sound/drivers/opl3/Makefile sound/drivers/opl3/Makefile --- sound/drivers/opl3/Makefile 2007-07-09 01:32:17.000000000 +0200 +++ sound/drivers/opl3/Makefile 2007-07-28 02:00:08.000000000 +0200 @@ -4,10 +4,8 @@ # snd-opl3-lib-objs := opl3_lib.o opl3_synth.o -snd-opl3-synth-objs := opl3_seq.o opl3_midi.o opl3_drums.o -ifeq ($(CONFIG_SND_SEQUENCER_OSS),y) -snd-opl3-synth-objs += opl3_oss.o -endif +snd-opl3-synth-y := opl3_seq.o opl3_midi.o opl3_drums.o +snd-opl3-synth-$(CONFIG_SND_SEQUENCER_OSS) += opl3_oss.o # # this function returns: diff -Nur sound/drivers/portman2x4.c sound/drivers/portman2x4.c --- sound/drivers/portman2x4.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/drivers/portman2x4.c 2007-06-28 02:00:23.000000000 +0200 @@ -833,7 +833,7 @@ /********************************************************************* * module init stuff *********************************************************************/ -static void __init_or_module snd_portman_unregister_all(void) +static void snd_portman_unregister_all(void) { int i; diff -Nur sound/drivers/serial-u16550.c sound/drivers/serial-u16550.c --- sound/drivers/serial-u16550.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/drivers/serial-u16550.c 2007-06-26 02:00:08.000000000 +0200 @@ -998,7 +998,7 @@ }, }; -static void __init_or_module snd_serial_unregister_all(void) +static void snd_serial_unregister_all(void) { int i; diff -Nur sound/drivers/virmidi.c sound/drivers/virmidi.c --- sound/drivers/virmidi.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/drivers/virmidi.c 2007-06-26 02:00:08.000000000 +0200 @@ -145,7 +145,7 @@ }, }; -static void __init_or_module snd_virmidi_unregister_all(void) +static void snd_virmidi_unregister_all(void) { int i; diff -Nur sound/drivers/vx/vx_mixer.c sound/drivers/vx/vx_mixer.c --- sound/drivers/vx/vx_mixer.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/drivers/vx/vx_mixer.c 2007-07-24 02:00:09.000000000 +0200 @@ -647,14 +647,7 @@ return 0; } -static int vx_audio_sw_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define vx_audio_sw_info snd_ctl_boolean_stereo_info static int vx_audio_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -865,14 +858,7 @@ return 0; } -static int vx_saturation_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define vx_saturation_info snd_ctl_boolean_stereo_info static int vx_saturation_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { diff -Nur sound/i2c/Makefile sound/i2c/Makefile --- sound/i2c/Makefile 2007-07-09 01:32:17.000000000 +0200 +++ sound/i2c/Makefile 2007-07-28 02:00:08.000000000 +0200 @@ -7,9 +7,7 @@ snd-cs8427-objs := cs8427.o snd-tea6330t-objs := tea6330t.o -ifeq ($(subst m,y,$(CONFIG_L3)),y) - obj-$(CONFIG_L3) += l3/ -endif +obj-$(CONFIG_L3) += l3/ obj-$(CONFIG_SND) += other/ diff -Nur sound/i2c/other/ak4114.c sound/i2c/other/ak4114.c --- sound/i2c/other/ak4114.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/i2c/other/ak4114.c 2007-07-24 02:00:09.000000000 +0200 @@ -200,15 +200,7 @@ return 0; } -static int snd_ak4114_in_bit_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_ak4114_in_bit_info snd_ctl_boolean_mono_info static int snd_ak4114_in_bit_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff -Nur sound/i2c/other/ak4117.c sound/i2c/other/ak4117.c --- sound/i2c/other/ak4117.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/i2c/other/ak4117.c 2007-07-24 02:00:10.000000000 +0200 @@ -181,15 +181,7 @@ return 0; } -static int snd_ak4117_in_bit_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_ak4117_in_bit_info snd_ctl_boolean_mono_info static int snd_ak4117_in_bit_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff -Nur sound/i2c/other/ak4xxx-adda.c sound/i2c/other/ak4xxx-adda.c --- sound/i2c/other/ak4xxx-adda.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/i2c/other/ak4xxx-adda.c 2007-07-24 02:00:10.000000000 +0200 @@ -463,15 +463,7 @@ return change; } -static int ak4xxx_switch_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define ak4xxx_switch_info snd_ctl_boolean_mono_info static int ak4xxx_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -481,8 +473,8 @@ int addr = AK_GET_ADDR(kcontrol->private_value); int shift = AK_GET_SHIFT(kcontrol->private_value); int invert = AK_GET_INVERT(kcontrol->private_value); - unsigned char val = snd_akm4xxx_get(ak, chip, addr); - + /* we observe the (1<value.integer.value[0] = (val & (1<num_dacs; ) { + /* mute control for Revolution 7.1 - AK4381 */ + if (ak->type == SND_AK4381 + && ak->dac_info[mixer_ch].switch_name) { + memset(&knew, 0, sizeof(knew)); + knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + knew.count = 1; + knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + knew.name = ak->dac_info[mixer_ch].switch_name; + knew.info = ak4xxx_switch_info; + knew.get = ak4xxx_switch_get; + knew.put = ak4xxx_switch_put; + knew.access = 0; + /* register 1, bit 0 (SMUTE): 0 = normal operation, + 1 = mute */ + knew.private_value = + AK_COMPOSE(idx/2, 1, 0, 0) | AK_INVERT; + err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak)); + if (err < 0) + return err; + } memset(&knew, 0, sizeof(knew)); if (! ak->dac_info || ! ak->dac_info[mixer_ch].name) { knew.name = "DAC Volume"; diff -Nur sound/i2c/other/pt2258.c sound/i2c/other/pt2258.c --- sound/i2c/other/pt2258.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/i2c/other/pt2258.c 2007-07-24 02:00:10.000000000 +0200 @@ -140,15 +140,7 @@ return -EIO; } -static int pt2258_switch_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define pt2258_switch_info snd_ctl_boolean_mono_info static int pt2258_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff -Nur sound/i2c/tea6330t.c sound/i2c/tea6330t.c --- sound/i2c/tea6330t.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/i2c/tea6330t.c 2007-07-24 02:00:10.000000000 +0200 @@ -142,15 +142,7 @@ .info = snd_tea6330t_info_master_switch, \ .get = snd_tea6330t_get_master_switch, .put = snd_tea6330t_put_master_switch } -static int snd_tea6330t_info_master_switch(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_tea6330t_info_master_switch snd_ctl_boolean_stereo_info static int snd_tea6330t_get_master_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff -Nur sound/isa/Kconfig sound/isa/Kconfig --- sound/isa/Kconfig 2007-07-09 01:32:17.000000000 +0200 +++ sound/isa/Kconfig 2007-07-11 02:00:05.000000000 +0200 @@ -1,8 +1,5 @@ # ALSA ISA drivers -menu "ISA devices" - depends on SND!=n && ISA && ISA_DMA_API - config SND_AD1848_LIB tristate select SND_PCM @@ -11,6 +8,22 @@ tristate select SND_PCM +config SND_SB_COMMON + tristate + +config SND_SB8_DSP + tristate + select SND_PCM + select SND_SB_COMMON + +config SND_SB16_DSP + tristate + select SND_PCM + select SND_SB_COMMON + +menu "ISA devices" + depends on SND!=n && ISA && ISA_DMA_API + config SND_ADLIB tristate "AdLib FM card" depends on SND @@ -55,7 +68,7 @@ select ISAPNP select SND_OPL3_LIB select SND_MPU401_UART - select SND_PCM + select SND_SB16_DSP help Say Y here to include support for soundcards based on Avance Logic ALS100, ALS110, ALS120 and ALS200 chips. @@ -81,6 +94,7 @@ tristate "C-Media CMI8330" depends on SND select SND_AD1848_LIB + select SND_SB16_DSP help Say Y here to include support for soundcards based on the C-Media CMI8330 chip. @@ -132,7 +146,7 @@ select ISAPNP select SND_OPL3_LIB select SND_MPU401_UART - select SND_PCM + select SND_SB16_DSP help Say Y here to include support for soundcards based on the Diamond Technologies DT-019X or Avance Logic ALS-007 chips. @@ -145,7 +159,7 @@ depends on SND && PNP && ISA select ISAPNP select SND_MPU401_UART - select SND_PCM + select SND_SB8_DSP help Say Y here to include support for ESS AudioDrive ES968 chips. @@ -321,7 +335,7 @@ depends on SND select SND_OPL3_LIB select SND_RAWMIDI - select SND_PCM + select SND_SB8_DSP help Say Y here to include support for Creative Sound Blaster 1.0/ 2.0/Pro (8-bit) or 100% compatible soundcards. @@ -334,7 +348,7 @@ depends on SND select SND_OPL3_LIB select SND_MPU401_UART - select SND_PCM + select SND_SB16_DSP help Say Y here to include support for Sound Blaster 16 soundcards (including the Plug and Play version). @@ -347,7 +361,7 @@ depends on SND select SND_OPL3_LIB select SND_MPU401_UART - select SND_PCM + select SND_SB16_DSP help Say Y here to include support for Sound Blaster AWE soundcards (including the Plug and Play version). diff -Nur sound/isa/ad1816a/ad1816a_lib.c sound/isa/ad1816a/ad1816a_lib.c --- sound/isa/ad1816a/ad1816a_lib.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/isa/ad1816a/ad1816a_lib.c 2007-08-14 02:00:08.000000000 +0200 @@ -453,7 +453,6 @@ if ((error = snd_ad1816a_open(chip, AD1816A_MODE_PLAYBACK)) < 0) return error; - snd_pcm_set_sync(substream); runtime->hw = snd_ad1816a_playback; snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max); snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max); @@ -469,7 +468,6 @@ if ((error = snd_ad1816a_open(chip, AD1816A_MODE_CAPTURE)) < 0) return error; - snd_pcm_set_sync(substream); runtime->hw = snd_ad1816a_capture; snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max); snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max); diff -Nur sound/isa/ad1848/Makefile sound/isa/ad1848/Makefile --- sound/isa/ad1848/Makefile 2007-07-09 01:32:17.000000000 +0200 +++ sound/isa/ad1848/Makefile 2007-08-11 02:00:08.000000000 +0200 @@ -7,9 +7,6 @@ snd-ad1848-objs := ad1848.o # Toplevel Module Dependency -obj-$(CONFIG_SND_CMI8330) += snd-ad1848-lib.o -obj-$(CONFIG_SND_SGALAXY) += snd-ad1848-lib.o -obj-$(CONFIG_SND_AD1848) += snd-ad1848.o snd-ad1848-lib.o -obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-ad1848-lib.o +obj-$(CONFIG_SND_AD1848) += snd-ad1848.o +obj-$(CONFIG_SND_AD1848_LIB) += snd-ad1848-lib.o -obj-m := $(sort $(obj-m)) diff -Nur sound/isa/ad1848/ad1848_lib.c sound/isa/ad1848/ad1848_lib.c --- sound/isa/ad1848/ad1848_lib.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/isa/ad1848/ad1848_lib.c 2007-06-26 02:00:08.000000000 +0200 @@ -245,7 +245,7 @@ snd_printk(KERN_ERR "mce_down - auto calibration time out (2)\n"); return; } - time = schedule_timeout_interruptible(time); + time = schedule_timeout(time); spin_lock_irqsave(&chip->reg_lock, flags); } #if 0 @@ -258,7 +258,7 @@ snd_printk(KERN_ERR "mce_down - auto calibration time out (3)\n"); return; } - time = schedule_timeout_interruptible(time); + time = schedule_timeout(time); spin_lock_irqsave(&chip->reg_lock, flags); } spin_unlock_irqrestore(&chip->reg_lock, flags); diff -Nur sound/isa/cs423x/Makefile sound/isa/cs423x/Makefile --- sound/isa/cs423x/Makefile 2007-07-09 01:32:17.000000000 +0200 +++ sound/isa/cs423x/Makefile 2007-08-11 02:00:08.000000000 +0200 @@ -10,17 +10,8 @@ snd-cs4236-objs := cs4236.o # Toplevel Module Dependency -obj-$(CONFIG_SND_AZT2320) += snd-cs4231-lib.o -obj-$(CONFIG_SND_MIRO) += snd-cs4231-lib.o -obj-$(CONFIG_SND_OPL3SA2) += snd-cs4231-lib.o -obj-$(CONFIG_SND_CS4231) += snd-cs4231.o snd-cs4231-lib.o -obj-$(CONFIG_SND_CS4232) += snd-cs4232.o snd-cs4231-lib.o -obj-$(CONFIG_SND_CS4236) += snd-cs4236.o snd-cs4236-lib.o snd-cs4231-lib.o -obj-$(CONFIG_SND_GUSMAX) += snd-cs4231-lib.o -obj-$(CONFIG_SND_INTERWAVE) += snd-cs4231-lib.o -obj-$(CONFIG_SND_INTERWAVE_STB) += snd-cs4231-lib.o -obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-cs4231-lib.o -obj-$(CONFIG_SND_WAVEFRONT) += snd-cs4231-lib.o -obj-$(CONFIG_SND_SSCAPE) += snd-cs4231-lib.o +obj-$(CONFIG_SND_CS4231_LIB) += snd-cs4231-lib.o +obj-$(CONFIG_SND_CS4231) += snd-cs4231.o +obj-$(CONFIG_SND_CS4232) += snd-cs4232.o +obj-$(CONFIG_SND_CS4236) += snd-cs4236.o snd-cs4236-lib.o -obj-m := $(sort $(obj-m)) diff -Nur sound/isa/cs423x/cs4231_lib.c sound/isa/cs423x/cs4231_lib.c --- sound/isa/cs423x/cs4231_lib.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/isa/cs423x/cs4231_lib.c 2007-07-28 02:00:08.000000000 +0200 @@ -555,6 +555,8 @@ snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT] = pdfr); } spin_unlock_irqrestore(&chip->reg_lock, flags); + if (chip->hardware == CS4231_HW_OPL3SA2) + udelay(100); /* this seems to help */ snd_cs4231_mce_down(chip); } snd_cs4231_calibrate_mute(chip, 0); diff -Nur sound/isa/es18xx.c sound/isa/es18xx.c --- sound/isa/es18xx.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/isa/es18xx.c 2007-08-02 02:00:06.000000000 +0200 @@ -1071,14 +1071,7 @@ return (snd_es18xx_mixer_bits(chip, 0x1c, 0x07, val) != val) || retVal; } -static int snd_es18xx_info_spatializer_enable(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_es18xx_info_spatializer_enable snd_ctl_boolean_mono_info static int snd_es18xx_get_spatializer_enable(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1120,14 +1113,7 @@ return 0; } -static int snd_es18xx_info_hw_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_es18xx_info_hw_switch snd_ctl_boolean_stereo_info static int snd_es18xx_get_hw_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2042,6 +2028,7 @@ static struct pnp_device_id snd_audiodrive_pnpbiosids[] = { { .id = "ESS1869" }, + { .id = "ESS1879" }, { .id = "" } /* end */ }; diff -Nur sound/isa/gus/gus_mixer.c sound/isa/gus/gus_mixer.c --- sound/isa/gus/gus_mixer.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/isa/gus/gus_mixer.c 2007-07-24 02:00:10.000000000 +0200 @@ -36,14 +36,7 @@ .get = snd_gf1_get_single, .put = snd_gf1_put_single, \ .private_value = shift | (invert << 8) } -static int snd_gf1_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_gf1_info_single snd_ctl_boolean_mono_info static int snd_gf1_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { diff -Nur sound/isa/opl3sa2.c sound/isa/opl3sa2.c --- sound/isa/opl3sa2.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/isa/opl3sa2.c 2007-07-28 02:00:08.000000000 +0200 @@ -164,6 +164,8 @@ { .id = "YMH0801", .devs = { { "YMH0021" } } }, /* NeoMagic MagicWave 3DX */ { .id = "NMX2200", .devs = { { "YMH2210" } } }, + /* NeoMagic MagicWave 3D */ + { .id = "NMX2200", .devs = { { "NMX2210" } } }, /* --- */ { .id = "" } /* end */ }; @@ -251,6 +253,7 @@ /* 0x03 - YM715B */ /* 0x04 - YM719 - OPL-SA4? */ /* 0x05 - OPL3-SA3 - Libretto 100 */ + /* 0x07 - unknown - Neomagic MagicWave 3D */ break; } str[0] = chip->version + '0'; diff -Nur sound/isa/opti9xx/miro.c sound/isa/opti9xx/miro.c --- sound/isa/opti9xx/miro.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/isa/opti9xx/miro.c 2007-07-24 02:00:10.000000000 +0200 @@ -242,14 +242,7 @@ * MIXER part */ -static int snd_miro_info_capture(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - - return 0; -} +#define snd_miro_info_capture snd_ctl_boolean_mono_info static int snd_miro_get_capture(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -344,14 +337,7 @@ return change; } -static int snd_miro_info_amp(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - - return 0; -} +#define snd_miro_info_amp snd_ctl_boolean_mono_info static int snd_miro_get_amp(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff -Nur sound/isa/opti9xx/opti92x-ad1848.c sound/isa/opti9xx/opti92x-ad1848.c --- sound/isa/opti9xx/opti92x-ad1848.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/isa/opti9xx/opti92x-ad1848.c 2007-06-21 02:00:09.000000000 +0200 @@ -1927,10 +1927,12 @@ static int __devinit snd_opti9xx_isa_match(struct device *devptr, unsigned int dev) { +#ifdef CONFIG_PNP if (snd_opti9xx_pnp_is_probed) return 0; if (isapnp) return 0; +#endif return 1; } @@ -2096,6 +2098,7 @@ pnp_register_card_driver(&opti9xx_pnpc_driver); if (snd_opti9xx_pnp_is_probed) return 0; + pnp_unregister_card_driver(&opti9xx_pnpc_driver); #endif return isa_register_driver(&snd_opti9xx_driver, 1); } diff -Nur sound/isa/sb/Makefile sound/isa/sb/Makefile --- sound/isa/sb/Makefile 2007-07-09 01:32:17.000000000 +0200 +++ sound/isa/sb/Makefile 2007-05-26 02:00:06.000000000 +0200 @@ -22,14 +22,13 @@ sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1))) # Toplevel Module Dependency -obj-$(CONFIG_SND_ALS100) += snd-sb16-dsp.o snd-sb-common.o -obj-$(CONFIG_SND_CMI8330) += snd-sb16-dsp.o snd-sb-common.o -obj-$(CONFIG_SND_DT019X) += snd-sb16-dsp.o snd-sb-common.o -obj-$(CONFIG_SND_SB8) += snd-sb8.o snd-sb8-dsp.o snd-sb-common.o -obj-$(CONFIG_SND_SB16) += snd-sb16.o snd-sb16-dsp.o snd-sb-common.o -obj-$(CONFIG_SND_SBAWE) += snd-sbawe.o snd-sb16-dsp.o snd-sb-common.o -obj-$(CONFIG_SND_ES968) += snd-es968.o snd-sb8-dsp.o snd-sb-common.o -obj-$(CONFIG_SND_ALS4000) += snd-sb-common.o +obj-$(CONFIG_SND_SB_COMMON) += snd-sb-common.o +obj-$(CONFIG_SND_SB16_DSP) += snd-sb16-dsp.o +obj-$(CONFIG_SND_SB8_DSP) += snd-sb8-dsp.o +obj-$(CONFIG_SND_SB8) += snd-sb8.o +obj-$(CONFIG_SND_SB16) += snd-sb16.o +obj-$(CONFIG_SND_SBAWE) += snd-sbawe.o +obj-$(CONFIG_SND_ES968) += snd-es968.o ifeq ($(CONFIG_SND_SB16_CSP),y) obj-$(CONFIG_SND_SB16) += snd-sb16-csp.o obj-$(CONFIG_SND_SBAWE) += snd-sb16-csp.o diff -Nur sound/isa/sb/sb16_csp.c sound/isa/sb/sb16_csp.c --- sound/isa/sb/sb16_csp.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/isa/sb/sb16_csp.c 2007-07-24 02:00:10.000000000 +0200 @@ -979,14 +979,7 @@ * QSound mixer control for PCM */ -static int snd_sb_qsound_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_sb_qsound_switch_info snd_ctl_boolean_mono_info static int snd_sb_qsound_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { diff -Nur sound/isa/sb/sb16_main.c sound/isa/sb/sb16_main.c --- sound/isa/sb/sb16_main.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/isa/sb/sb16_main.c 2007-05-25 02:00:06.000000000 +0200 @@ -563,6 +563,11 @@ __open_ok: if (chip->hardware == SB_HW_ALS100) runtime->hw.rate_max = 48000; + if (chip->hardware == SB_HW_CS5530) { + runtime->hw.buffer_bytes_max = 32 * 1024; + runtime->hw.periods_min = 2; + runtime->hw.rate_min = 44100; + } if (chip->mode & SB_RATE_LOCK) runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate; chip->playback_substream = substream; @@ -633,6 +638,11 @@ __open_ok: if (chip->hardware == SB_HW_ALS100) runtime->hw.rate_max = 48000; + if (chip->hardware == SB_HW_CS5530) { + runtime->hw.buffer_bytes_max = 32 * 1024; + runtime->hw.periods_min = 2; + runtime->hw.rate_min = 44100; + } if (chip->mode & SB_RATE_LOCK) runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate; chip->capture_substream = substream; diff -Nur sound/isa/sb/sb_common.c sound/isa/sb/sb_common.c --- sound/isa/sb/sb_common.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/isa/sb/sb_common.c 2007-05-25 02:00:06.000000000 +0200 @@ -128,7 +128,7 @@ minor = version & 0xff; snd_printdd("SB [0x%lx]: DSP chip found, version = %i.%i\n", chip->port, major, minor); - + switch (chip->hardware) { case SB_HW_AUTO: switch (major) { @@ -168,6 +168,9 @@ case SB_HW_DT019X: str = "(DT019X/ALS007)"; break; + case SB_HW_CS5530: + str = "16 (CS5530)"; + break; default: return -ENODEV; } diff -Nur sound/isa/sb/sb_mixer.c sound/isa/sb/sb_mixer.c --- sound/isa/sb/sb_mixer.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/isa/sb/sb_mixer.c 2007-05-25 02:00:06.000000000 +0200 @@ -821,6 +821,7 @@ break; case SB_HW_16: case SB_HW_ALS100: + case SB_HW_CS5530: if ((err = snd_sbmixer_init(chip, snd_sb16_controls, ARRAY_SIZE(snd_sb16_controls), @@ -950,6 +951,7 @@ break; case SB_HW_16: case SB_HW_ALS100: + case SB_HW_CS5530: save_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs)); break; case SB_HW_ALS4000: @@ -975,6 +977,7 @@ break; case SB_HW_16: case SB_HW_ALS100: + case SB_HW_CS5530: restore_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs)); break; case SB_HW_ALS4000: diff -Nur sound/isa/sscape.c sound/isa/sscape.c --- sound/isa/sscape.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/isa/sscape.c 2007-06-26 02:00:08.000000000 +0200 @@ -382,7 +382,7 @@ unsigned long flags; unsigned char x; - schedule_timeout_interruptible(1); + schedule_timeout(1); spin_lock_irqsave(&s->lock, flags); x = inb(HOST_DATA_IO(s->io_base)); @@ -409,7 +409,7 @@ unsigned long flags; unsigned char x; - schedule_timeout_interruptible(1); + schedule_timeout(1); spin_lock_irqsave(&s->lock, flags); x = inb(HOST_DATA_IO(s->io_base)); diff -Nur sound/isa/wavefront/wavefront_synth.c sound/isa/wavefront/wavefront_synth.c --- sound/isa/wavefront/wavefront_synth.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/isa/wavefront/wavefront_synth.c 2007-06-26 02:00:08.000000000 +0200 @@ -1780,7 +1780,7 @@ outb (val,port); spin_unlock_irq(&dev->irq_lock); while (1) { - if ((timeout = schedule_timeout_interruptible(timeout)) == 0) + if ((timeout = schedule_timeout(timeout)) == 0) return; if (dev->irq_ok) return; diff -Nur sound/pci/Kconfig sound/pci/Kconfig --- sound/pci/Kconfig 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/Kconfig 2007-08-11 02:00:08.000000000 +0200 @@ -33,6 +33,7 @@ select SND_OPL3_LIB select SND_MPU401_UART select SND_PCM + select SND_SB_COMMON help Say Y here to include support for soundcards based on Avance Logic ALS4000 chips. @@ -215,6 +216,16 @@ This works better than the old code, so say Y. +config SND_CS5530 + tristate "CS5530 Audio" + depends on SND && ISA_DMA_API + select SND_SB16_DSP + help + Say Y here to include support for audio on Cyrix/NatSemi CS5530 chips. + + To compile this driver as a module, choose M here: the module + will be called snd-cs5530. + config SND_CS5535AUDIO tristate "CS5535/CS5536 Audio" depends on SND && X86 && !X86_64 @@ -489,6 +500,95 @@ To compile this driver as a module, choose M here: the module will be called snd-hda-intel. +config SND_HDA_HWDEP + bool "Build hwdep interface for HD-audio driver" + depends on SND_HDA_INTEL + select SND_HWDEP + help + Say Y here to build a hwdep interface for HD-audio driver. + This interface can be used for out-of-bound communication + with codecs for debugging purposes. + +config SND_HDA_CODEC_REALTEK + bool "Build Realtek HD-audio codec support" + depends on SND_HDA_INTEL + default y + help + Say Y here to include Realtek HD-audio codec support in + snd-hda-intel driver, such as ALC880. + +config SND_HDA_CODEC_ANALOG + bool "Build Analog Device HD-audio codec support" + depends on SND_HDA_INTEL + default y + help + Say Y here to include Analog Device HD-audio codec support in + snd-hda-intel driver, such as AD1986A. + +config SND_HDA_CODEC_SIGMATEL + bool "Build IDT/Sigmatel HD-audio codec support" + depends on SND_HDA_INTEL + default y + help + Say Y here to include IDT (Sigmatel) HD-audio codec support in + snd-hda-intel driver, such as STAC9200. + +config SND_HDA_CODEC_VIA + bool "Build VIA HD-audio codec support" + depends on SND_HDA_INTEL + default y + help + Say Y here to include VIA HD-audio codec support in + snd-hda-intel driver, such as VT1708. + +config SND_HDA_CODEC_ATIHDMI + bool "Build ATI HDMI HD-audio codec support" + depends on SND_HDA_INTEL + default y + help + Say Y here to include ATI HDMI HD-audio codec support in + snd-hda-intel driver, such as ATI RS600 HDMI. + +config SND_HDA_CODEC_CONEXANT + bool "Build Conexant HD-audio codec support" + depends on SND_HDA_INTEL + default y + help + Say Y here to include Conexant HD-audio codec support in + snd-hda-intel driver, such as CX20549. + +config SND_HDA_CODEC_CMEDIA + bool "Build C-Media HD-audio codec support" + depends on SND_HDA_INTEL + default y + help + Say Y here to include C-Media HD-audio codec support in + snd-hda-intel driver, such as CMI9880. + +config SND_HDA_CODEC_SI3054 + bool "Build Silicon Labs 3054 HD-modem codec support" + depends on SND_HDA_INTEL + default y + help + Say Y here to include Silicon Labs 3054 HD-modem codec + (and compatibles) support in snd-hda-intel driver. + +config SND_HDA_GENERIC + bool "Enable generic HD-audio codec parser" + depends on SND_HDA_INTEL + default y + help + Say Y here to enable the generic HD-audio codec parser + in snd-hda-intel driver. + +config SND_HDA_POWER_SAVE + bool "Aggressive power-saving on HD-audio" + depends on SND_HDA_INTEL && EXPERIMENTAL + help + Say Y here to enable more aggressive power-saving mode on + HD-audio driver. The power-saving timeout can be configured + via power_save option or over sysfs on-the-fly. + config SND_HDSP tristate "RME Hammerfall DSP Audio" depends on SND diff -Nur sound/pci/Makefile sound/pci/Makefile --- sound/pci/Makefile 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/Makefile 2007-05-25 02:00:06.000000000 +0200 @@ -12,6 +12,7 @@ snd-bt87x-objs := bt87x.o snd-cmipci-objs := cmipci.o snd-cs4281-objs := cs4281.o +snd-cs5530-objs := cs5530.o snd-ens1370-objs := ens1370.o snd-ens1371-objs := ens1371.o snd-es1938-objs := es1938.o @@ -36,6 +37,7 @@ obj-$(CONFIG_SND_BT87X) += snd-bt87x.o obj-$(CONFIG_SND_CMIPCI) += snd-cmipci.o obj-$(CONFIG_SND_CS4281) += snd-cs4281.o +obj-$(CONFIG_SND_CS5530) += snd-cs5530.o obj-$(CONFIG_SND_ENS1370) += snd-ens1370.o obj-$(CONFIG_SND_ENS1371) += snd-ens1371.o obj-$(CONFIG_SND_ES1938) += snd-es1938.o diff -Nur sound/pci/ac97/ac97_codec.c sound/pci/ac97/ac97_codec.c --- sound/pci/ac97/ac97_codec.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/ac97/ac97_codec.c 2007-08-11 02:00:08.000000000 +0200 @@ -2036,11 +2036,12 @@ else { udelay(50); if (ac97->scaps & AC97_SCAP_SKIP_AUDIO) - err = ac97_reset_wait(ac97, HZ/2, 1); + err = ac97_reset_wait(ac97, msecs_to_jiffies(500), 1); else { - err = ac97_reset_wait(ac97, HZ/2, 0); + err = ac97_reset_wait(ac97, msecs_to_jiffies(500), 0); if (err < 0) - err = ac97_reset_wait(ac97, HZ/2, 1); + err = ac97_reset_wait(ac97, + msecs_to_jiffies(500), 1); } if (err < 0) { snd_printk(KERN_WARNING "AC'97 %d does not respond - RESET\n", ac97->num); @@ -2104,7 +2105,7 @@ } /* nothing should be in powerdown mode */ snd_ac97_write_cache(ac97, AC97_GENERAL_PURPOSE, 0); - end_time = jiffies + (HZ / 10); + end_time = jiffies + msecs_to_jiffies(100); do { if ((snd_ac97_read(ac97, AC97_POWERDOWN) & 0x0f) == 0x0f) goto __ready_ok; @@ -2136,7 +2137,7 @@ udelay(100); /* nothing should be in powerdown mode */ snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0); - end_time = jiffies + (HZ / 10); + end_time = jiffies + msecs_to_jiffies(100); do { if ((snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS) & tmp) == tmp) goto __ready_ok; @@ -2354,7 +2355,8 @@ * (for avoiding loud click noises for many (OSS) apps * that open/close frequently) */ - schedule_delayed_work(&ac97->power_work, HZ*2); + schedule_delayed_work(&ac97->power_work, + msecs_to_jiffies(2000)); else { cancel_delayed_work(&ac97->power_work); update_power_regs(ac97); @@ -2436,7 +2438,7 @@ /* * restore ac97 status */ -void snd_ac97_restore_status(struct snd_ac97 *ac97) +static void snd_ac97_restore_status(struct snd_ac97 *ac97) { int i; @@ -2457,7 +2459,7 @@ /* * restore IEC958 status */ -void snd_ac97_restore_iec958(struct snd_ac97 *ac97) +static void snd_ac97_restore_iec958(struct snd_ac97 *ac97) { if (ac97->ext_id & AC97_EI_SPDIF) { if (ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_SPDIF) { diff -Nur sound/pci/ac97/ac97_patch.c sound/pci/ac97/ac97_patch.c --- sound/pci/ac97/ac97_patch.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/ac97/ac97_patch.c 2007-07-24 02:00:10.000000000 +0200 @@ -1880,14 +1880,7 @@ return 0; } -static int snd_ac97_ad1888_lohpsel_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_ac97_ad1888_lohpsel_info snd_ctl_boolean_mono_info static int snd_ac97_ad1888_lohpsel_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2186,15 +2179,7 @@ return 0; } -static int snd_ac97_ad1986_bool_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_ac97_ad1986_bool_info snd_ctl_boolean_mono_info static int snd_ac97_ad1986_lososel_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff -Nur sound/pci/ali5451/ali5451.c sound/pci/ali5451/ali5451.c --- sound/pci/ali5451/ali5451.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/ali5451/ali5451.c 2007-08-21 17:37:19.000000000 +0200 @@ -239,7 +239,7 @@ struct snd_ali { - unsigned long irq; + int irq; unsigned long port; unsigned char revision; @@ -731,8 +731,7 @@ return; } - count = 0; - while (count++ <= 50000) { + for (count = 0; count <= 50000; count++) { snd_ali_delay(codec, 6); bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1)); R2 = bval & 0x1F; @@ -1805,15 +1804,7 @@ .info = snd_ali5451_spdif_info, .get = snd_ali5451_spdif_get, \ .put = snd_ali5451_spdif_put, .private_value = value} -static int snd_ali5451_spdif_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_ali5451_spdif_info snd_ctl_boolean_mono_info static int snd_ali5451_spdif_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2343,7 +2334,7 @@ strcpy(card->driver, "ALI5451"); strcpy(card->shortname, "ALI 5451"); - sprintf(card->longname, "%s at 0x%lx, irq %li", + sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, codec->port, codec->irq); snd_ali_printk("register card.\n"); diff -Nur sound/pci/ali5451/ali5451.c~ sound/pci/ali5451/ali5451.c~ --- sound/pci/ali5451/ali5451.c~ 1970-01-01 01:00:00.000000000 +0100 +++ sound/pci/ali5451/ali5451.c~ 2007-07-24 02:00:10.000000000 +0200 @@ -0,0 +1,2381 @@ +/* + * Matt Wu + * Apr 26, 2001 + * Routines for control of ALi pci audio M5451 + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public Lcodecnse as published by + * the Free Software Foundation; either version 2 of the Lcodecnse, 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 Lcodecnse for more details. + * + * You should have received a copy of the GNU General Public Lcodecnse + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Matt Wu "); +MODULE_DESCRIPTION("ALI M5451"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{ALI,M5451,pci},{ALI,M5451}}"); + +static int index = SNDRV_DEFAULT_IDX1; /* Index */ +static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ +static int pcm_channels = 32; +static int spdif; + +module_param(index, int, 0444); +MODULE_PARM_DESC(index, "Index value for ALI M5451 PCI Audio."); +module_param(id, charp, 0444); +MODULE_PARM_DESC(id, "ID string for ALI M5451 PCI Audio."); +module_param(pcm_channels, int, 0444); +MODULE_PARM_DESC(pcm_channels, "PCM Channels"); +module_param(spdif, bool, 0444); +MODULE_PARM_DESC(spdif, "Support SPDIF I/O"); + +/* just for backward compatibility */ +static int enable; +module_param(enable, bool, 0444); + + +/* + * Debug part definitions + */ + +/* #define ALI_DEBUG */ + +#ifdef ALI_DEBUG +#define snd_ali_printk(format, args...) printk(KERN_DEBUG format, ##args); +#else +#define snd_ali_printk(format, args...) +#endif + +/* + * Constants definition + */ + +#define DEVICE_ID_ALI5451 ((PCI_VENDOR_ID_AL<<16)|PCI_DEVICE_ID_AL_M5451) + + +#define ALI_CHANNELS 32 + +#define ALI_PCM_IN_CHANNEL 31 +#define ALI_SPDIF_IN_CHANNEL 19 +#define ALI_SPDIF_OUT_CHANNEL 15 +#define ALI_CENTER_CHANNEL 24 +#define ALI_LEF_CHANNEL 23 +#define ALI_SURR_LEFT_CHANNEL 26 +#define ALI_SURR_RIGHT_CHANNEL 25 +#define ALI_MODEM_IN_CHANNEL 21 +#define ALI_MODEM_OUT_CHANNEL 20 + +#define SNDRV_ALI_VOICE_TYPE_PCM 01 +#define SNDRV_ALI_VOICE_TYPE_OTH 02 + +#define ALI_5451_V02 0x02 + +/* + * Direct Registers + */ + +#define ALI_LEGACY_DMAR0 0x00 /* ADR0 */ +#define ALI_LEGACY_DMAR4 0x04 /* CNT0 */ +#define ALI_LEGACY_DMAR11 0x0b /* MOD */ +#define ALI_LEGACY_DMAR15 0x0f /* MMR */ +#define ALI_MPUR0 0x20 +#define ALI_MPUR1 0x21 +#define ALI_MPUR2 0x22 +#define ALI_MPUR3 0x23 + +#define ALI_AC97_WRITE 0x40 +#define ALI_AC97_READ 0x44 + +#define ALI_SCTRL 0x48 +#define ALI_SPDIF_OUT_ENABLE 0x20 +#define ALI_SCTRL_LINE_IN2 (1 << 9) +#define ALI_SCTRL_GPIO_IN2 (1 << 13) +#define ALI_SCTRL_LINE_OUT_EN (1 << 20) +#define ALI_SCTRL_GPIO_OUT_EN (1 << 23) +#define ALI_SCTRL_CODEC1_READY (1 << 24) +#define ALI_SCTRL_CODEC2_READY (1 << 25) +#define ALI_AC97_GPIO 0x4c +#define ALI_AC97_GPIO_ENABLE 0x8000 +#define ALI_AC97_GPIO_DATA_SHIFT 16 +#define ALI_SPDIF_CS 0x70 +#define ALI_SPDIF_CTRL 0x74 +#define ALI_SPDIF_IN_FUNC_ENABLE 0x02 +#define ALI_SPDIF_IN_CH_STATUS 0x40 +#define ALI_SPDIF_OUT_CH_STATUS 0xbf +#define ALI_START 0x80 +#define ALI_STOP 0x84 +#define ALI_CSPF 0x90 +#define ALI_AINT 0x98 +#define ALI_GC_CIR 0xa0 + #define ENDLP_IE 0x00001000 + #define MIDLP_IE 0x00002000 +#define ALI_AINTEN 0xa4 +#define ALI_VOLUME 0xa8 +#define ALI_SBDELTA_DELTA_R 0xac +#define ALI_MISCINT 0xb0 + #define ADDRESS_IRQ 0x00000020 + #define TARGET_REACHED 0x00008000 + #define MIXER_OVERFLOW 0x00000800 + #define MIXER_UNDERFLOW 0x00000400 + #define GPIO_IRQ 0x01000000 +#define ALI_SBBL_SBCL 0xc0 +#define ALI_SBCTRL_SBE2R_SBDD 0xc4 +#define ALI_STIMER 0xc8 +#define ALI_GLOBAL_CONTROL 0xd4 +#define ALI_SPDIF_OUT_SEL_PCM 0x00000400 /* bit 10 */ +#define ALI_SPDIF_IN_SUPPORT 0x00000800 /* bit 11 */ +#define ALI_SPDIF_OUT_CH_ENABLE 0x00008000 /* bit 15 */ +#define ALI_SPDIF_IN_CH_ENABLE 0x00080000 /* bit 19 */ +#define ALI_PCM_IN_ENABLE 0x80000000 /* bit 31 */ + +#define ALI_CSO_ALPHA_FMS 0xe0 +#define ALI_LBA 0xe4 +#define ALI_ESO_DELTA 0xe8 +#define ALI_GVSEL_PAN_VOC_CTRL_EC 0xf0 +#define ALI_EBUF1 0xf4 +#define ALI_EBUF2 0xf8 + +#define ALI_REG(codec, x) ((codec)->port + x) + +#define MAX_CODECS 2 + + +struct snd_ali; +struct snd_ali_voice; + +struct snd_ali_channel_control { + /* register data */ + struct REGDATA { + unsigned int start; + unsigned int stop; + unsigned int aint; + unsigned int ainten; + } data; + + /* register addresses */ + struct REGS { + unsigned int start; + unsigned int stop; + unsigned int aint; + unsigned int ainten; + unsigned int ac97read; + unsigned int ac97write; + } regs; + +}; + +struct snd_ali_voice { + unsigned int number; + unsigned int use :1, + pcm :1, + midi :1, + mode :1, + synth :1, + running :1; + + /* PCM data */ + struct snd_ali *codec; + struct snd_pcm_substream *substream; + struct snd_ali_voice *extra; + + int eso; /* final ESO value for channel */ + int count; /* runtime->period_size */ + + /* --- */ + + void *private_data; + void (*private_free)(void *private_data); +}; + + +struct snd_alidev { + + struct snd_ali_voice voices[ALI_CHANNELS]; + + unsigned int chcnt; /* num of opened channels */ + unsigned int chmap; /* bitmap for opened channels */ + unsigned int synthcount; + +}; + + +#define ALI_GLOBAL_REGS 56 +#define ALI_CHANNEL_REGS 8 +struct snd_ali_image { + u32 regs[ALI_GLOBAL_REGS]; + u32 channel_regs[ALI_CHANNELS][ALI_CHANNEL_REGS]; +}; + + +struct snd_ali { + int irq; + unsigned long port; + unsigned char revision; + + unsigned int hw_initialized :1; + unsigned int spdif_support :1; + + struct pci_dev *pci; + struct pci_dev *pci_m1533; + struct pci_dev *pci_m7101; + + struct snd_card *card; + struct snd_pcm *pcm[MAX_CODECS]; + struct snd_alidev synth; + struct snd_ali_channel_control chregs; + + /* S/PDIF Mask */ + unsigned int spdif_mask; + + unsigned int spurious_irq_count; + unsigned int spurious_irq_max_delta; + + unsigned int num_of_codecs; + + struct snd_ac97_bus *ac97_bus; + struct snd_ac97 *ac97[MAX_CODECS]; + unsigned short ac97_ext_id; + unsigned short ac97_ext_status; + + spinlock_t reg_lock; + spinlock_t voice_alloc; + +#ifdef CONFIG_PM + struct snd_ali_image *image; +#endif +}; + +static struct pci_device_id snd_ali_ids[] = { + {PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5451), 0, 0, 0}, + {0, } +}; +MODULE_DEVICE_TABLE(pci, snd_ali_ids); + +static void snd_ali_clear_voices(struct snd_ali *, unsigned int, unsigned int); +static unsigned short snd_ali_codec_peek(struct snd_ali *, int, unsigned short); +static void snd_ali_codec_poke(struct snd_ali *, int, unsigned short, + unsigned short); + +/* + * AC97 ACCESS + */ + +static inline unsigned int snd_ali_5451_peek(struct snd_ali *codec, + unsigned int port) +{ + return (unsigned int)inl(ALI_REG(codec, port)); +} + +static inline void snd_ali_5451_poke(struct snd_ali *codec, + unsigned int port, + unsigned int val) +{ + outl((unsigned int)val, ALI_REG(codec, port)); +} + +static int snd_ali_codec_ready(struct snd_ali *codec, + unsigned int port) +{ + unsigned long end_time; + unsigned int res; + + end_time = jiffies + msecs_to_jiffies(250); + do { + res = snd_ali_5451_peek(codec,port); + if (!(res & 0x8000)) + return 0; + schedule_timeout_uninterruptible(1); + } while (time_after_eq(end_time, jiffies)); + snd_ali_5451_poke(codec, port, res & ~0x8000); + snd_printdd("ali_codec_ready: codec is not ready.\n "); + return -EIO; +} + +static int snd_ali_stimer_ready(struct snd_ali *codec) +{ + unsigned long end_time; + unsigned long dwChk1,dwChk2; + + dwChk1 = snd_ali_5451_peek(codec, ALI_STIMER); + dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER); + + end_time = jiffies + msecs_to_jiffies(250); + do { + dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER); + if (dwChk2 != dwChk1) + return 0; + schedule_timeout_uninterruptible(1); + } while (time_after_eq(end_time, jiffies)); + snd_printk(KERN_ERR "ali_stimer_read: stimer is not ready.\n"); + return -EIO; +} + +static void snd_ali_codec_poke(struct snd_ali *codec,int secondary, + unsigned short reg, + unsigned short val) +{ + unsigned int dwVal; + unsigned int port; + + if (reg >= 0x80) { + snd_printk(KERN_ERR "ali_codec_poke: reg(%xh) invalid.\n", reg); + return; + } + + port = codec->chregs.regs.ac97write; + + if (snd_ali_codec_ready(codec, port) < 0) + return; + if (snd_ali_stimer_ready(codec) < 0) + return; + + dwVal = (unsigned int) (reg & 0xff); + dwVal |= 0x8000 | (val << 16); + if (secondary) + dwVal |= 0x0080; + if (codec->revision == ALI_5451_V02) + dwVal |= 0x0100; + + snd_ali_5451_poke(codec, port, dwVal); + + return ; +} + +static unsigned short snd_ali_codec_peek(struct snd_ali *codec, + int secondary, + unsigned short reg) +{ + unsigned int dwVal; + unsigned int port; + + if (reg >= 0x80) { + snd_printk(KERN_ERR "ali_codec_peek: reg(%xh) invalid.\n", reg); + return ~0; + } + + port = codec->chregs.regs.ac97read; + + if (snd_ali_codec_ready(codec, port) < 0) + return ~0; + if (snd_ali_stimer_ready(codec) < 0) + return ~0; + + dwVal = (unsigned int) (reg & 0xff); + dwVal |= 0x8000; /* bit 15*/ + if (secondary) + dwVal |= 0x0080; + + snd_ali_5451_poke(codec, port, dwVal); + + if (snd_ali_stimer_ready(codec) < 0) + return ~0; + if (snd_ali_codec_ready(codec, port) < 0) + return ~0; + + return (snd_ali_5451_peek(codec, port) & 0xffff0000) >> 16; +} + +static void snd_ali_codec_write(struct snd_ac97 *ac97, + unsigned short reg, + unsigned short val ) +{ + struct snd_ali *codec = ac97->private_data; + + snd_ali_printk("codec_write: reg=%xh data=%xh.\n", reg, val); + if (reg == AC97_GPIO_STATUS) { + outl((val << ALI_AC97_GPIO_DATA_SHIFT) | ALI_AC97_GPIO_ENABLE, + ALI_REG(codec, ALI_AC97_GPIO)); + return; + } + snd_ali_codec_poke(codec, ac97->num, reg, val); + return ; +} + + +static unsigned short snd_ali_codec_read(struct snd_ac97 *ac97, + unsigned short reg) +{ + struct snd_ali *codec = ac97->private_data; + + snd_ali_printk("codec_read reg=%xh.\n", reg); + return snd_ali_codec_peek(codec, ac97->num, reg); +} + +/* + * AC97 Reset + */ + +static int snd_ali_reset_5451(struct snd_ali *codec) +{ + struct pci_dev *pci_dev; + unsigned short wCount, wReg; + unsigned int dwVal; + + pci_dev = codec->pci_m1533; + if (pci_dev) { + pci_read_config_dword(pci_dev, 0x7c, &dwVal); + pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000); + udelay(5000); + pci_read_config_dword(pci_dev, 0x7c, &dwVal); + pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff); + udelay(5000); + } + + pci_dev = codec->pci; + pci_read_config_dword(pci_dev, 0x44, &dwVal); + pci_write_config_dword(pci_dev, 0x44, dwVal | 0x000c0000); + udelay(500); + pci_read_config_dword(pci_dev, 0x44, &dwVal); + pci_write_config_dword(pci_dev, 0x44, dwVal & 0xfffbffff); + udelay(5000); + + wCount = 200; + while(wCount--) { + wReg = snd_ali_codec_peek(codec, 0, AC97_POWERDOWN); + if ((wReg & 0x000f) == 0x000f) + return 0; + udelay(5000); + } + + /* non-fatal if you have a non PM capable codec */ + /* snd_printk(KERN_WARNING "ali5451: reset time out\n"); */ + return 0; +} + +#ifdef CODEC_RESET + +static int snd_ali_reset_codec(struct snd_ali *codec) +{ + struct pci_dev *pci_dev; + unsigned char bVal; + unsigned int dwVal; + unsigned short wCount, wReg; + + pci_dev = codec->pci_m1533; + + pci_read_config_dword(pci_dev, 0x7c, &dwVal); + pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000); + udelay(5000); + pci_read_config_dword(pci_dev, 0x7c, &dwVal); + pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff); + udelay(5000); + + bVal = inb(ALI_REG(codec,ALI_SCTRL)); + bVal |= 0x02; + outb(ALI_REG(codec,ALI_SCTRL),bVal); + udelay(5000); + bVal = inb(ALI_REG(codec,ALI_SCTRL)); + bVal &= 0xfd; + outb(ALI_REG(codec,ALI_SCTRL),bVal); + udelay(15000); + + wCount = 200; + while (wCount--) { + wReg = snd_ali_codec_read(codec->ac97, AC97_POWERDOWN); + if ((wReg & 0x000f) == 0x000f) + return 0; + udelay(5000); + } + return -1; +} + +#endif + +/* + * ALI 5451 Controller + */ + +static void snd_ali_enable_special_channel(struct snd_ali *codec, + unsigned int channel) +{ + unsigned long dwVal; + + dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)); + dwVal |= 1 << (channel & 0x0000001f); + outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); +} + +static void snd_ali_disable_special_channel(struct snd_ali *codec, + unsigned int channel) +{ + unsigned long dwVal; + + dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)); + dwVal &= ~(1 << (channel & 0x0000001f)); + outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); +} + +static void snd_ali_enable_address_interrupt(struct snd_ali *codec) +{ + unsigned int gc; + + gc = inl(ALI_REG(codec, ALI_GC_CIR)); + gc |= ENDLP_IE; + gc |= MIDLP_IE; + outl( gc, ALI_REG(codec, ALI_GC_CIR)); +} + +static void snd_ali_disable_address_interrupt(struct snd_ali *codec) +{ + unsigned int gc; + + gc = inl(ALI_REG(codec, ALI_GC_CIR)); + gc &= ~ENDLP_IE; + gc &= ~MIDLP_IE; + outl(gc, ALI_REG(codec, ALI_GC_CIR)); +} + +#if 0 /* not used */ +static void snd_ali_enable_voice_irq(struct snd_ali *codec, + unsigned int channel) +{ + unsigned int mask; + struct snd_ali_channel_control *pchregs = &(codec->chregs); + + snd_ali_printk("enable_voice_irq channel=%d\n",channel); + + mask = 1 << (channel & 0x1f); + pchregs->data.ainten = inl(ALI_REG(codec, pchregs->regs.ainten)); + pchregs->data.ainten |= mask; + outl(pchregs->data.ainten, ALI_REG(codec, pchregs->regs.ainten)); +} +#endif + +static void snd_ali_disable_voice_irq(struct snd_ali *codec, + unsigned int channel) +{ + unsigned int mask; + struct snd_ali_channel_control *pchregs = &(codec->chregs); + + snd_ali_printk("disable_voice_irq channel=%d\n",channel); + + mask = 1 << (channel & 0x1f); + pchregs->data.ainten = inl(ALI_REG(codec, pchregs->regs.ainten)); + pchregs->data.ainten &= ~mask; + outl(pchregs->data.ainten, ALI_REG(codec, pchregs->regs.ainten)); +} + +static int snd_ali_alloc_pcm_channel(struct snd_ali *codec, int channel) +{ + unsigned int idx = channel & 0x1f; + + if (codec->synth.chcnt >= ALI_CHANNELS){ + snd_printk(KERN_ERR + "ali_alloc_pcm_channel: no free channels.\n"); + return -1; + } + + if (!(codec->synth.chmap & (1 << idx))) { + codec->synth.chmap |= 1 << idx; + codec->synth.chcnt++; + snd_ali_printk("alloc_pcm_channel no. %d.\n",idx); + return idx; + } + return -1; +} + +static int snd_ali_find_free_channel(struct snd_ali * codec, int rec) +{ + int idx; + int result = -1; + + snd_ali_printk("find_free_channel: for %s\n",rec ? "rec" : "pcm"); + + /* recording */ + if (rec) { + if (codec->spdif_support && + (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & + ALI_SPDIF_IN_SUPPORT)) + idx = ALI_SPDIF_IN_CHANNEL; + else + idx = ALI_PCM_IN_CHANNEL; + + result = snd_ali_alloc_pcm_channel(codec, idx); + if (result >= 0) + return result; + else { + snd_printk(KERN_ERR "ali_find_free_channel: " + "record channel is busy now.\n"); + return -1; + } + } + + /* playback... */ + if (codec->spdif_support && + (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & + ALI_SPDIF_OUT_CH_ENABLE)) { + idx = ALI_SPDIF_OUT_CHANNEL; + result = snd_ali_alloc_pcm_channel(codec, idx); + if (result >= 0) + return result; + else + snd_printk(KERN_ERR "ali_find_free_channel: " + "S/PDIF out channel is in busy now.\n"); + } + + for (idx = 0; idx < ALI_CHANNELS; idx++) { + result = snd_ali_alloc_pcm_channel(codec, idx); + if (result >= 0) + return result; + } + snd_printk(KERN_ERR "ali_find_free_channel: no free channels.\n"); + return -1; +} + +static void snd_ali_free_channel_pcm(struct snd_ali *codec, int channel) +{ + unsigned int idx = channel & 0x0000001f; + + snd_ali_printk("free_channel_pcm channel=%d\n",channel); + + if (channel < 0 || channel >= ALI_CHANNELS) + return; + + if (!(codec->synth.chmap & (1 << idx))) { + snd_printk(KERN_ERR "ali_free_channel_pcm: " + "channel %d is not in use.\n", channel); + return; + } else { + codec->synth.chmap &= ~(1 << idx); + codec->synth.chcnt--; + } +} + +#if 0 /* not used */ +static void snd_ali_start_voice(struct snd_ali *codec, unsigned int channel) +{ + unsigned int mask = 1 << (channel & 0x1f); + + snd_ali_printk("start_voice: channel=%d\n",channel); + outl(mask, ALI_REG(codec,codec->chregs.regs.start)); +} +#endif + +static void snd_ali_stop_voice(struct snd_ali *codec, unsigned int channel) +{ + unsigned int mask = 1 << (channel & 0x1f); + + snd_ali_printk("stop_voice: channel=%d\n",channel); + outl(mask, ALI_REG(codec, codec->chregs.regs.stop)); +} + +/* + * S/PDIF Part + */ + +static void snd_ali_delay(struct snd_ali *codec,int interval) +{ + unsigned long begintimer,currenttimer; + + begintimer = inl(ALI_REG(codec, ALI_STIMER)); + currenttimer = inl(ALI_REG(codec, ALI_STIMER)); + + while (currenttimer < begintimer + interval) { + if (snd_ali_stimer_ready(codec) < 0) + break; + currenttimer = inl(ALI_REG(codec, ALI_STIMER)); + cpu_relax(); + } +} + +static void snd_ali_detect_spdif_rate(struct snd_ali *codec) +{ + u16 wval; + u16 count = 0; + u8 bval, R1 = 0, R2; + + bval = inb(ALI_REG(codec, ALI_SPDIF_CTRL + 1)); + bval |= 0x1F; + outb(bval, ALI_REG(codec, ALI_SPDIF_CTRL + 1)); + + while ((R1 < 0x0b || R1 > 0x0e) && R1 != 0x12 && count <= 50000) { + count ++; + snd_ali_delay(codec, 6); + bval = inb(ALI_REG(codec, ALI_SPDIF_CTRL + 1)); + R1 = bval & 0x1F; + } + + if (count > 50000) { + snd_printk(KERN_ERR "ali_detect_spdif_rate: timeout!\n"); + return; + } + + for (count = 0; count <= 50000; count++) { + snd_ali_delay(codec, 6); + bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1)); + R2 = bval & 0x1F; + if (R2 != R1) + R1 = R2; + else + break; + } + + if (count > 50000) { + snd_printk(KERN_ERR "ali_detect_spdif_rate: timeout!\n"); + return; + } + + if (R2 >= 0x0b && R2 <= 0x0e) { + wval = inw(ALI_REG(codec, ALI_SPDIF_CTRL + 2)); + wval &= 0xe0f0; + wval |= (0x09 << 8) | 0x05; + outw(wval, ALI_REG(codec, ALI_SPDIF_CTRL + 2)); + + bval = inb(ALI_REG(codec, ALI_SPDIF_CS + 3)) & 0xf0; + outb(bval | 0x02, ALI_REG(codec, ALI_SPDIF_CS + 3)); + } else if (R2 == 0x12) { + wval = inw(ALI_REG(codec, ALI_SPDIF_CTRL + 2)); + wval &= 0xe0f0; + wval |= (0x0e << 8) | 0x08; + outw(wval, ALI_REG(codec, ALI_SPDIF_CTRL + 2)); + + bval = inb(ALI_REG(codec,ALI_SPDIF_CS + 3)) & 0xf0; + outb(bval | 0x03, ALI_REG(codec, ALI_SPDIF_CS + 3)); + } +} + +static unsigned int snd_ali_get_spdif_in_rate(struct snd_ali *codec) +{ + u32 dwRate; + u8 bval; + + bval = inb(ALI_REG(codec, ALI_SPDIF_CTRL)); + bval &= 0x7f; + bval |= 0x40; + outb(bval, ALI_REG(codec, ALI_SPDIF_CTRL)); + + snd_ali_detect_spdif_rate(codec); + + bval = inb(ALI_REG(codec, ALI_SPDIF_CS + 3)); + bval &= 0x0f; + + switch (bval) { + case 0: dwRate = 44100; break; + case 1: dwRate = 48000; break; + case 2: dwRate = 32000; break; + default: dwRate = 0; break; + } + + return dwRate; +} + +static void snd_ali_enable_spdif_in(struct snd_ali *codec) +{ + unsigned int dwVal; + + dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)); + dwVal |= ALI_SPDIF_IN_SUPPORT; + outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); + + dwVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL)); + dwVal |= 0x02; + outb(dwVal, ALI_REG(codec, ALI_SPDIF_CTRL)); + + snd_ali_enable_special_channel(codec, ALI_SPDIF_IN_CHANNEL); +} + +static void snd_ali_disable_spdif_in(struct snd_ali *codec) +{ + unsigned int dwVal; + + dwVal = inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)); + dwVal &= ~ALI_SPDIF_IN_SUPPORT; + outl(dwVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); + + snd_ali_disable_special_channel(codec, ALI_SPDIF_IN_CHANNEL); +} + + +static void snd_ali_set_spdif_out_rate(struct snd_ali *codec, unsigned int rate) +{ + unsigned char bVal; + unsigned int dwRate; + + switch (rate) { + case 32000: dwRate = 0x300; break; + case 48000: dwRate = 0x200; break; + default: dwRate = 0; break; + } + + bVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL)); + bVal &= (unsigned char)(~(1<<6)); + + bVal |= 0x80; /* select right */ + outb(bVal, ALI_REG(codec, ALI_SPDIF_CTRL)); + outb(dwRate | 0x20, ALI_REG(codec, ALI_SPDIF_CS + 2)); + + bVal &= ~0x80; /* select left */ + outb(bVal, ALI_REG(codec, ALI_SPDIF_CTRL)); + outw(rate | 0x10, ALI_REG(codec, ALI_SPDIF_CS + 2)); +} + +static void snd_ali_enable_spdif_out(struct snd_ali *codec) +{ + unsigned short wVal; + unsigned char bVal; + struct pci_dev *pci_dev; + + pci_dev = codec->pci_m1533; + if (pci_dev == NULL) + return; + pci_read_config_byte(pci_dev, 0x61, &bVal); + bVal |= 0x40; + pci_write_config_byte(pci_dev, 0x61, bVal); + pci_read_config_byte(pci_dev, 0x7d, &bVal); + bVal |= 0x01; + pci_write_config_byte(pci_dev, 0x7d, bVal); + + pci_read_config_byte(pci_dev, 0x7e, &bVal); + bVal &= (~0x20); + bVal |= 0x10; + pci_write_config_byte(pci_dev, 0x7e, bVal); + + bVal = inb(ALI_REG(codec, ALI_SCTRL)); + outb(bVal | ALI_SPDIF_OUT_ENABLE, ALI_REG(codec, ALI_SCTRL)); + + bVal = inb(ALI_REG(codec, ALI_SPDIF_CTRL)); + outb(bVal & ALI_SPDIF_OUT_CH_STATUS, ALI_REG(codec, ALI_SPDIF_CTRL)); + + wVal = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL)); + wVal |= ALI_SPDIF_OUT_SEL_PCM; + outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); + snd_ali_disable_special_channel(codec, ALI_SPDIF_OUT_CHANNEL); +} + +static void snd_ali_enable_spdif_chnout(struct snd_ali *codec) +{ + unsigned short wVal; + + wVal = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL)); + wVal &= ~ALI_SPDIF_OUT_SEL_PCM; + outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); +/* + wVal = inw(ALI_REG(codec, ALI_SPDIF_CS)); + if (flag & ALI_SPDIF_OUT_NON_PCM) + wVal |= 0x0002; + else + wVal &= (~0x0002); + outw(wVal, ALI_REG(codec, ALI_SPDIF_CS)); +*/ + snd_ali_enable_special_channel(codec, ALI_SPDIF_OUT_CHANNEL); +} + +static void snd_ali_disable_spdif_chnout(struct snd_ali *codec) +{ + unsigned short wVal; + + wVal = inw(ALI_REG(codec, ALI_GLOBAL_CONTROL)); + wVal |= ALI_SPDIF_OUT_SEL_PCM; + outw(wVal, ALI_REG(codec, ALI_GLOBAL_CONTROL)); + + snd_ali_enable_special_channel(codec, ALI_SPDIF_OUT_CHANNEL); +} + +static void snd_ali_disable_spdif_out(struct snd_ali *codec) +{ + unsigned char bVal; + + bVal = inb(ALI_REG(codec, ALI_SCTRL)); + outb(bVal & ~ALI_SPDIF_OUT_ENABLE, ALI_REG(codec, ALI_SCTRL)); + + snd_ali_disable_spdif_chnout(codec); +} + +static void snd_ali_update_ptr(struct snd_ali *codec, int channel) +{ + struct snd_ali_voice *pvoice; + struct snd_pcm_runtime *runtime; + struct snd_ali_channel_control *pchregs; + unsigned int old, mask; +#ifdef ALI_DEBUG + unsigned int temp, cspf; +#endif + + pchregs = &(codec->chregs); + + /* check if interrupt occurred for channel */ + old = pchregs->data.aint; + mask = 1U << (channel & 0x1f); + + if (!(old & mask)) + return; + + pvoice = &codec->synth.voices[channel]; + runtime = pvoice->substream->runtime; + + udelay(100); + spin_lock(&codec->reg_lock); + + if (pvoice->pcm && pvoice->substream) { + /* pcm interrupt */ +#ifdef ALI_DEBUG + outb((u8)(pvoice->number), ALI_REG(codec, ALI_GC_CIR)); + temp = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2)); + cspf = (inl(ALI_REG(codec, ALI_CSPF)) & mask) == mask; +#endif + if (pvoice->running) { + snd_ali_printk("update_ptr: cso=%4.4x cspf=%d.\n", + (u16)temp, cspf); + spin_unlock(&codec->reg_lock); + snd_pcm_period_elapsed(pvoice->substream); + spin_lock(&codec->reg_lock); + } else { + snd_ali_stop_voice(codec, channel); + snd_ali_disable_voice_irq(codec, channel); + } + } else if (codec->synth.voices[channel].synth) { + /* synth interrupt */ + } else if (codec->synth.voices[channel].midi) { + /* midi interrupt */ + } else { + /* unknown interrupt */ + snd_ali_stop_voice(codec, channel); + snd_ali_disable_voice_irq(codec, channel); + } + spin_unlock(&codec->reg_lock); + outl(mask,ALI_REG(codec,pchregs->regs.aint)); + pchregs->data.aint = old & (~mask); +} + +static irqreturn_t snd_ali_card_interrupt(int irq, void *dev_id) +{ + struct snd_ali *codec = dev_id; + int channel; + unsigned int audio_int; + struct snd_ali_channel_control *pchregs; + + if (codec == NULL || !codec->hw_initialized) + return IRQ_NONE; + + audio_int = inl(ALI_REG(codec, ALI_MISCINT)); + if (!audio_int) + return IRQ_NONE; + + pchregs = &(codec->chregs); + if (audio_int & ADDRESS_IRQ) { + /* get interrupt status for all channels */ + pchregs->data.aint = inl(ALI_REG(codec, pchregs->regs.aint)); + for (channel = 0; channel < ALI_CHANNELS; channel++) + snd_ali_update_ptr(codec, channel); + } + outl((TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), + ALI_REG(codec, ALI_MISCINT)); + + return IRQ_HANDLED; +} + + +static struct snd_ali_voice *snd_ali_alloc_voice(struct snd_ali * codec, + int type, int rec, int channel) +{ + struct snd_ali_voice *pvoice; + int idx; + + snd_ali_printk("alloc_voice: type=%d rec=%d\n", type, rec); + + spin_lock_irq(&codec->voice_alloc); + if (type == SNDRV_ALI_VOICE_TYPE_PCM) { + idx = channel > 0 ? snd_ali_alloc_pcm_channel(codec, channel) : + snd_ali_find_free_channel(codec,rec); + if (idx < 0) { + snd_printk(KERN_ERR "ali_alloc_voice: err.\n"); + spin_unlock_irq(&codec->voice_alloc); + return NULL; + } + pvoice = &(codec->synth.voices[idx]); + pvoice->codec = codec; + pvoice->use = 1; + pvoice->pcm = 1; + pvoice->mode = rec; + spin_unlock_irq(&codec->voice_alloc); + return pvoice; + } + spin_unlock_irq(&codec->voice_alloc); + return NULL; +} + + +static void snd_ali_free_voice(struct snd_ali * codec, + struct snd_ali_voice *pvoice) +{ + void (*private_free)(void *); + void *private_data; + + snd_ali_printk("free_voice: channel=%d\n",pvoice->number); + if (pvoice == NULL || !pvoice->use) + return; + snd_ali_clear_voices(codec, pvoice->number, pvoice->number); + spin_lock_irq(&codec->voice_alloc); + private_free = pvoice->private_free; + private_data = pvoice->private_data; + pvoice->private_free = NULL; + pvoice->private_data = NULL; + if (pvoice->pcm) + snd_ali_free_channel_pcm(codec, pvoice->number); + pvoice->use = pvoice->pcm = pvoice->synth = 0; + pvoice->substream = NULL; + spin_unlock_irq(&codec->voice_alloc); + if (private_free) + private_free(private_data); +} + + +static void snd_ali_clear_voices(struct snd_ali *codec, + unsigned int v_min, + unsigned int v_max) +{ + unsigned int i; + + for (i = v_min; i <= v_max; i++) { + snd_ali_stop_voice(codec, i); + snd_ali_disable_voice_irq(codec, i); + } +} + +static void snd_ali_write_voice_regs(struct snd_ali *codec, + unsigned int Channel, + unsigned int LBA, + unsigned int CSO, + unsigned int ESO, + unsigned int DELTA, + unsigned int ALPHA_FMS, + unsigned int GVSEL, + unsigned int PAN, + unsigned int VOL, + unsigned int CTRL, + unsigned int EC) +{ + unsigned int ctlcmds[4]; + + outb((unsigned char)(Channel & 0x001f), ALI_REG(codec, ALI_GC_CIR)); + + ctlcmds[0] = (CSO << 16) | (ALPHA_FMS & 0x0000ffff); + ctlcmds[1] = LBA; + ctlcmds[2] = (ESO << 16) | (DELTA & 0x0ffff); + ctlcmds[3] = (GVSEL << 31) | + ((PAN & 0x0000007f) << 24) | + ((VOL & 0x000000ff) << 16) | + ((CTRL & 0x0000000f) << 12) | + (EC & 0x00000fff); + + outb(Channel, ALI_REG(codec, ALI_GC_CIR)); + + outl(ctlcmds[0], ALI_REG(codec, ALI_CSO_ALPHA_FMS)); + outl(ctlcmds[1], ALI_REG(codec, ALI_LBA)); + outl(ctlcmds[2], ALI_REG(codec, ALI_ESO_DELTA)); + outl(ctlcmds[3], ALI_REG(codec, ALI_GVSEL_PAN_VOC_CTRL_EC)); + + outl(0x30000000, ALI_REG(codec, ALI_EBUF1)); /* Still Mode */ + outl(0x30000000, ALI_REG(codec, ALI_EBUF2)); /* Still Mode */ +} + +static unsigned int snd_ali_convert_rate(unsigned int rate, int rec) +{ + unsigned int delta; + + if (rate < 4000) + rate = 4000; + if (rate > 48000) + rate = 48000; + + if (rec) { + if (rate == 44100) + delta = 0x116a; + else if (rate == 8000) + delta = 0x6000; + else if (rate == 48000) + delta = 0x1000; + else + delta = ((48000 << 12) / rate) & 0x0000ffff; + } else { + if (rate == 44100) + delta = 0xeb3; + else if (rate == 8000) + delta = 0x2ab; + else if (rate == 48000) + delta = 0x1000; + else + delta = (((rate << 12) + rate) / 48000) & 0x0000ffff; + } + + return delta; +} + +static unsigned int snd_ali_control_mode(struct snd_pcm_substream *substream) +{ + unsigned int CTRL; + struct snd_pcm_runtime *runtime = substream->runtime; + + /* set ctrl mode + CTRL default: 8-bit (unsigned) mono, loop mode enabled + */ + CTRL = 0x00000001; + if (snd_pcm_format_width(runtime->format) == 16) + CTRL |= 0x00000008; /* 16-bit data */ + if (!snd_pcm_format_unsigned(runtime->format)) + CTRL |= 0x00000002; /* signed data */ + if (runtime->channels > 1) + CTRL |= 0x00000004; /* stereo data */ + return CTRL; +} + +/* + * PCM part + */ + +static int snd_ali_trigger(struct snd_pcm_substream *substream, + int cmd) + +{ + struct snd_ali *codec = snd_pcm_substream_chip(substream); + struct snd_pcm_substream *s; + unsigned int what, whati, capture_flag; + struct snd_ali_voice *pvoice, *evoice; + unsigned int val; + int do_start; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + do_start = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + do_start = 0; + break; + default: + return -EINVAL; + } + + what = whati = capture_flag = 0; + snd_pcm_group_for_each_entry(s, substream) { + if ((struct snd_ali *) snd_pcm_substream_chip(s) == codec) { + pvoice = s->runtime->private_data; + evoice = pvoice->extra; + what |= 1 << (pvoice->number & 0x1f); + if (evoice == NULL) + whati |= 1 << (pvoice->number & 0x1f); + else { + whati |= 1 << (evoice->number & 0x1f); + what |= 1 << (evoice->number & 0x1f); + } + if (do_start) { + pvoice->running = 1; + if (evoice != NULL) + evoice->running = 1; + } else { + pvoice->running = 0; + if (evoice != NULL) + evoice->running = 0; + } + snd_pcm_trigger_done(s, substream); + if (pvoice->mode) + capture_flag = 1; + } + } + spin_lock(&codec->reg_lock); + if (!do_start) + outl(what, ALI_REG(codec, ALI_STOP)); + val = inl(ALI_REG(codec, ALI_AINTEN)); + if (do_start) + val |= whati; + else + val &= ~whati; + outl(val, ALI_REG(codec, ALI_AINTEN)); + if (do_start) + outl(what, ALI_REG(codec, ALI_START)); + snd_ali_printk("trigger: what=%xh whati=%xh\n", what, whati); + spin_unlock(&codec->reg_lock); + + return 0; +} + +static int snd_ali_playback_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_ali *codec = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ali_voice *pvoice = runtime->private_data; + struct snd_ali_voice *evoice = pvoice->extra; + int err; + + err = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + return err; + + /* voice management */ + + if (params_buffer_size(hw_params) / 2 != + params_period_size(hw_params)) { + if (!evoice) { + evoice = snd_ali_alloc_voice(codec, + SNDRV_ALI_VOICE_TYPE_PCM, + 0, -1); + if (!evoice) + return -ENOMEM; + pvoice->extra = evoice; + evoice->substream = substream; + } + } else { + if (evoice) { + snd_ali_free_voice(codec, evoice); + pvoice->extra = evoice = NULL; + } + } + + return 0; +} + +static int snd_ali_playback_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_ali *codec = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ali_voice *pvoice = runtime->private_data; + struct snd_ali_voice *evoice = pvoice ? pvoice->extra : NULL; + + snd_pcm_lib_free_pages(substream); + if (evoice) { + snd_ali_free_voice(codec, evoice); + pvoice->extra = NULL; + } + return 0; +} + +static int snd_ali_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_ali_hw_free(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_ali_playback_prepare(struct snd_pcm_substream *substream) +{ + struct snd_ali *codec = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ali_voice *pvoice = runtime->private_data; + struct snd_ali_voice *evoice = pvoice->extra; + + unsigned int LBA; + unsigned int Delta; + unsigned int ESO; + unsigned int CTRL; + unsigned int GVSEL; + unsigned int PAN; + unsigned int VOL; + unsigned int EC; + + snd_ali_printk("playback_prepare ...\n"); + + spin_lock_irq(&codec->reg_lock); + + /* set Delta (rate) value */ + Delta = snd_ali_convert_rate(runtime->rate, 0); + + if (pvoice->number == ALI_SPDIF_IN_CHANNEL || + pvoice->number == ALI_PCM_IN_CHANNEL) + snd_ali_disable_special_channel(codec, pvoice->number); + else if (codec->spdif_support && + (inl(ALI_REG(codec, ALI_GLOBAL_CONTROL)) & + ALI_SPDIF_OUT_CH_ENABLE) + && pvoice->number == ALI_SPDIF_OUT_CHANNEL) { + snd_ali_set_spdif_out_rate(codec, runtime->rate); + Delta = 0x1000; + } + + /* set Loop Back Address */ + LBA = runtime->dma_addr; + + /* set interrupt count size */ + pvoice->count = runtime->period_size; + + /* set target ESO for channel */ + pvoice->eso = runtime->buffer_size; + + snd_ali_printk("playback_prepare: eso=%xh count=%xh\n", + pvoice->eso, pvoice->count); + + /* set ESO to capture first MIDLP interrupt */ + ESO = pvoice->eso -1; + /* set ctrl mode */ + CTRL = snd_ali_control_mode(substream); + + GVSEL = 1; + PAN = 0; + VOL = 0; + EC = 0; + snd_ali_printk("playback_prepare:\n"); + snd_ali_printk("ch=%d, Rate=%d Delta=%xh,GVSEL=%xh,PAN=%xh,CTRL=%xh\n", + pvoice->number,runtime->rate,Delta,GVSEL,PAN,CTRL); + snd_ali_write_voice_regs(codec, + pvoice->number, + LBA, + 0, /* cso */ + ESO, + Delta, + 0, /* alpha */ + GVSEL, + PAN, + VOL, + CTRL, + EC); + if (evoice) { + evoice->count = pvoice->count; + evoice->eso = pvoice->count << 1; + ESO = evoice->eso - 1; + snd_ali_write_voice_regs(codec, + evoice->number, + LBA, + 0, /* cso */ + ESO, + Delta, + 0, /* alpha */ + GVSEL, + 0x7f, + 0x3ff, + CTRL, + EC); + } + spin_unlock_irq(&codec->reg_lock); + return 0; +} + + +static int snd_ali_prepare(struct snd_pcm_substream *substream) +{ + struct snd_ali *codec = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ali_voice *pvoice = runtime->private_data; + unsigned int LBA; + unsigned int Delta; + unsigned int ESO; + unsigned int CTRL; + unsigned int GVSEL; + unsigned int PAN; + unsigned int VOL; + unsigned int EC; + u8 bValue; + + spin_lock_irq(&codec->reg_lock); + + snd_ali_printk("ali_prepare...\n"); + + snd_ali_enable_special_channel(codec,pvoice->number); + + Delta = (pvoice->number == ALI_MODEM_IN_CHANNEL || + pvoice->number == ALI_MODEM_OUT_CHANNEL) ? + 0x1000 : snd_ali_convert_rate(runtime->rate, pvoice->mode); + + /* Prepare capture intr channel */ + if (pvoice->number == ALI_SPDIF_IN_CHANNEL) { + + unsigned int rate; + + spin_unlock_irq(&codec->reg_lock); + if (codec->revision != ALI_5451_V02) + return -1; + + rate = snd_ali_get_spdif_in_rate(codec); + if (rate == 0) { + snd_printk(KERN_WARNING "ali_capture_preapre: " + "spdif rate detect err!\n"); + rate = 48000; + } + spin_lock_irq(&codec->reg_lock); + bValue = inb(ALI_REG(codec,ALI_SPDIF_CTRL)); + if (bValue & 0x10) { + outb(bValue,ALI_REG(codec,ALI_SPDIF_CTRL)); + printk(KERN_WARNING "clear SPDIF parity error flag.\n"); + } + + if (rate != 48000) + Delta = ((rate << 12) / runtime->rate) & 0x00ffff; + } + + /* set target ESO for channel */ + pvoice->eso = runtime->buffer_size; + + /* set interrupt count size */ + pvoice->count = runtime->period_size; + + /* set Loop Back Address */ + LBA = runtime->dma_addr; + + /* set ESO to capture first MIDLP interrupt */ + ESO = pvoice->eso - 1; + CTRL = snd_ali_control_mode(substream); + GVSEL = 0; + PAN = 0x00; + VOL = 0x00; + EC = 0; + + snd_ali_write_voice_regs( codec, + pvoice->number, + LBA, + 0, /* cso */ + ESO, + Delta, + 0, /* alpha */ + GVSEL, + PAN, + VOL, + CTRL, + EC); + + spin_unlock_irq(&codec->reg_lock); + + return 0; +} + + +static snd_pcm_uframes_t +snd_ali_playback_pointer(struct snd_pcm_substream *substream) +{ + struct snd_ali *codec = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ali_voice *pvoice = runtime->private_data; + unsigned int cso; + + spin_lock(&codec->reg_lock); + if (!pvoice->running) { + spin_unlock(&codec->reg_lock); + return 0; + } + outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR)); + cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2)); + spin_unlock(&codec->reg_lock); + snd_ali_printk("playback pointer returned cso=%xh.\n", cso); + + return cso; +} + + +static snd_pcm_uframes_t snd_ali_pointer(struct snd_pcm_substream *substream) +{ + struct snd_ali *codec = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ali_voice *pvoice = runtime->private_data; + unsigned int cso; + + spin_lock(&codec->reg_lock); + if (!pvoice->running) { + spin_unlock_irq(&codec->reg_lock); + return 0; + } + outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR)); + cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2)); + spin_unlock(&codec->reg_lock); + + return cso; +} + +static struct snd_pcm_hardware snd_ali_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_SYNC_START), + .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE), + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (256*1024), + .period_bytes_min = 64, + .period_bytes_max = (256*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +/* + * Capture support device description + */ + +static struct snd_pcm_hardware snd_ali_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_SYNC_START), + .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE), + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static void snd_ali_pcm_free_substream(struct snd_pcm_runtime *runtime) +{ + struct snd_ali_voice *pvoice = runtime->private_data; + struct snd_ali *codec; + + if (pvoice) { + codec = pvoice->codec; + snd_ali_free_voice(pvoice->codec, pvoice); + } +} + +static int snd_ali_open(struct snd_pcm_substream *substream, int rec, + int channel, struct snd_pcm_hardware *phw) +{ + struct snd_ali *codec = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ali_voice *pvoice; + + pvoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, rec, + channel); + if (!pvoice) + return -EAGAIN; + + pvoice->substream = substream; + runtime->private_data = pvoice; + runtime->private_free = snd_ali_pcm_free_substream; + + runtime->hw = *phw; + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + 0, 64*1024); + return 0; +} + +static int snd_ali_playback_open(struct snd_pcm_substream *substream) +{ + return snd_ali_open(substream, 0, -1, &snd_ali_playback); +} + +static int snd_ali_capture_open(struct snd_pcm_substream *substream) +{ + return snd_ali_open(substream, 1, -1, &snd_ali_capture); +} + +static int snd_ali_playback_close(struct snd_pcm_substream *substream) +{ + return 0; +} + +static int snd_ali_close(struct snd_pcm_substream *substream) +{ + struct snd_ali *codec = snd_pcm_substream_chip(substream); + struct snd_ali_voice *pvoice = substream->runtime->private_data; + + snd_ali_disable_special_channel(codec,pvoice->number); + + return 0; +} + +static struct snd_pcm_ops snd_ali_playback_ops = { + .open = snd_ali_playback_open, + .close = snd_ali_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ali_playback_hw_params, + .hw_free = snd_ali_playback_hw_free, + .prepare = snd_ali_playback_prepare, + .trigger = snd_ali_trigger, + .pointer = snd_ali_playback_pointer, +}; + +static struct snd_pcm_ops snd_ali_capture_ops = { + .open = snd_ali_capture_open, + .close = snd_ali_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ali_hw_params, + .hw_free = snd_ali_hw_free, + .prepare = snd_ali_prepare, + .trigger = snd_ali_trigger, + .pointer = snd_ali_pointer, +}; + +/* + * Modem PCM + */ + +static int snd_ali_modem_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_ali *chip = snd_pcm_substream_chip(substream); + unsigned int modem_num = chip->num_of_codecs - 1; + snd_ac97_write(chip->ac97[modem_num], AC97_LINE1_RATE, + params_rate(hw_params)); + snd_ac97_write(chip->ac97[modem_num], AC97_LINE1_LEVEL, 0); + return snd_ali_hw_params(substream, hw_params); +} + +static struct snd_pcm_hardware snd_ali_modem = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000), + .rate_min = 8000, + .rate_max = 16000, + .channels_min = 1, + .channels_max = 1, + .buffer_bytes_max = (256*1024), + .period_bytes_min = 64, + .period_bytes_max = (256*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static int snd_ali_modem_open(struct snd_pcm_substream *substream, int rec, + int channel) +{ + static unsigned int rates[] = {8000, 9600, 12000, 16000}; + static struct snd_pcm_hw_constraint_list hw_constraint_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, + }; + int err = snd_ali_open(substream, rec, channel, &snd_ali_modem); + + if (err) + return err; + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &hw_constraint_rates); +} + +static int snd_ali_modem_playback_open(struct snd_pcm_substream *substream) +{ + return snd_ali_modem_open(substream, 0, ALI_MODEM_OUT_CHANNEL); +} + +static int snd_ali_modem_capture_open(struct snd_pcm_substream *substream) +{ + return snd_ali_modem_open(substream, 1, ALI_MODEM_IN_CHANNEL); +} + +static struct snd_pcm_ops snd_ali_modem_playback_ops = { + .open = snd_ali_modem_playback_open, + .close = snd_ali_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ali_modem_hw_params, + .hw_free = snd_ali_hw_free, + .prepare = snd_ali_prepare, + .trigger = snd_ali_trigger, + .pointer = snd_ali_pointer, +}; + +static struct snd_pcm_ops snd_ali_modem_capture_ops = { + .open = snd_ali_modem_capture_open, + .close = snd_ali_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ali_modem_hw_params, + .hw_free = snd_ali_hw_free, + .prepare = snd_ali_prepare, + .trigger = snd_ali_trigger, + .pointer = snd_ali_pointer, +}; + + +struct ali_pcm_description { + char *name; + unsigned int playback_num; + unsigned int capture_num; + struct snd_pcm_ops *playback_ops; + struct snd_pcm_ops *capture_ops; + unsigned short class; +}; + + +static void snd_ali_pcm_free(struct snd_pcm *pcm) +{ + struct snd_ali *codec = pcm->private_data; + codec->pcm[pcm->device] = NULL; +} + + +static int __devinit snd_ali_pcm(struct snd_ali * codec, int device, + struct ali_pcm_description *desc) +{ + struct snd_pcm *pcm; + int err; + + err = snd_pcm_new(codec->card, desc->name, device, + desc->playback_num, desc->capture_num, &pcm); + if (err < 0) { + snd_printk(KERN_ERR "snd_ali_pcm: err called snd_pcm_new.\n"); + return err; + } + pcm->private_data = codec; + pcm->private_free = snd_ali_pcm_free; + if (desc->playback_ops) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + desc->playback_ops); + if (desc->capture_ops) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + desc->capture_ops); + + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(codec->pci), + 64*1024, 128*1024); + + pcm->info_flags = 0; + pcm->dev_class = desc->class; + pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; + strcpy(pcm->name, desc->name); + codec->pcm[0] = pcm; + return 0; +} + +static struct ali_pcm_description ali_pcms[] = { + { .name = "ALI 5451", + .playback_num = ALI_CHANNELS, + .capture_num = 1, + .playback_ops = &snd_ali_playback_ops, + .capture_ops = &snd_ali_capture_ops + }, + { .name = "ALI 5451 modem", + .playback_num = 1, + .capture_num = 1, + .playback_ops = &snd_ali_modem_playback_ops, + .capture_ops = &snd_ali_modem_capture_ops, + .class = SNDRV_PCM_CLASS_MODEM + } +}; + +static int __devinit snd_ali_build_pcms(struct snd_ali *codec) +{ + int i, err; + for (i = 0; i < codec->num_of_codecs && i < ARRAY_SIZE(ali_pcms); i++) { + err = snd_ali_pcm(codec, i, &ali_pcms[i]); + if (err < 0) + return err; + } + return 0; +} + + +#define ALI5451_SPDIF(xname, xindex, value) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ +.info = snd_ali5451_spdif_info, .get = snd_ali5451_spdif_get, \ +.put = snd_ali5451_spdif_put, .private_value = value} + +#define snd_ali5451_spdif_info snd_ctl_boolean_mono_info + +static int snd_ali5451_spdif_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ali *codec = kcontrol->private_data; + unsigned int enable; + + enable = ucontrol->value.integer.value[0] ? 1 : 0; + + spin_lock_irq(&codec->reg_lock); + switch (kcontrol->private_value) { + case 0: + enable = (codec->spdif_mask & 0x02) ? 1 : 0; + break; + case 1: + enable = ((codec->spdif_mask & 0x02) && + (codec->spdif_mask & 0x04)) ? 1 : 0; + break; + case 2: + enable = (codec->spdif_mask & 0x01) ? 1 : 0; + break; + default: + break; + } + ucontrol->value.integer.value[0] = enable; + spin_unlock_irq(&codec->reg_lock); + return 0; +} + +static int snd_ali5451_spdif_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ali *codec = kcontrol->private_data; + unsigned int change = 0, enable = 0; + + enable = ucontrol->value.integer.value[0] ? 1 : 0; + + spin_lock_irq(&codec->reg_lock); + switch (kcontrol->private_value) { + case 0: + change = (codec->spdif_mask & 0x02) ? 1 : 0; + change = change ^ enable; + if (change) { + if (enable) { + codec->spdif_mask |= 0x02; + snd_ali_enable_spdif_out(codec); + } else { + codec->spdif_mask &= ~(0x02); + codec->spdif_mask &= ~(0x04); + snd_ali_disable_spdif_out(codec); + } + } + break; + case 1: + change = (codec->spdif_mask & 0x04) ? 1 : 0; + change = change ^ enable; + if (change && (codec->spdif_mask & 0x02)) { + if (enable) { + codec->spdif_mask |= 0x04; + snd_ali_enable_spdif_chnout(codec); + } else { + codec->spdif_mask &= ~(0x04); + snd_ali_disable_spdif_chnout(codec); + } + } + break; + case 2: + change = (codec->spdif_mask & 0x01) ? 1 : 0; + change = change ^ enable; + if (change) { + if (enable) { + codec->spdif_mask |= 0x01; + snd_ali_enable_spdif_in(codec); + } else { + codec->spdif_mask &= ~(0x01); + snd_ali_disable_spdif_in(codec); + } + } + break; + default: + break; + } + spin_unlock_irq(&codec->reg_lock); + + return change; +} + +static struct snd_kcontrol_new snd_ali5451_mixer_spdif[] __devinitdata = { + /* spdif aplayback switch */ + /* FIXME: "IEC958 Playback Switch" may conflict with one on ac97_codec */ + ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("Output ",NONE,SWITCH), 0, 0), + /* spdif out to spdif channel */ + ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("Channel Output ",NONE,SWITCH), 0, 1), + /* spdif in from spdif channel */ + ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0, 2) +}; + +static int __devinit snd_ali_mixer(struct snd_ali * codec) +{ + struct snd_ac97_template ac97; + unsigned int idx; + int i, err; + static struct snd_ac97_bus_ops ops = { + .write = snd_ali_codec_write, + .read = snd_ali_codec_read, + }; + + err = snd_ac97_bus(codec->card, 0, &ops, codec, &codec->ac97_bus); + if (err < 0) + return err; + + memset(&ac97, 0, sizeof(ac97)); + ac97.private_data = codec; + + for (i = 0; i < codec->num_of_codecs; i++) { + ac97.num = i; + err = snd_ac97_mixer(codec->ac97_bus, &ac97, &codec->ac97[i]); + if (err < 0) { + snd_printk(KERN_ERR + "ali mixer %d creating error.\n", i); + if (i == 0) + return err; + codec->num_of_codecs = 1; + break; + } + } + + if (codec->spdif_support) { + for (idx = 0; idx < ARRAY_SIZE(snd_ali5451_mixer_spdif); idx++) { + err = snd_ctl_add(codec->card, + snd_ctl_new1(&snd_ali5451_mixer_spdif[idx], codec)); + if (err < 0) + return err; + } + } + return 0; +} + +#ifdef CONFIG_PM +static int ali_suspend(struct pci_dev *pci, pm_message_t state) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct snd_ali *chip = card->private_data; + struct snd_ali_image *im; + int i, j; + + im = chip->image; + if (!im) + return 0; + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + for (i = 0; i < chip->num_of_codecs; i++) { + snd_pcm_suspend_all(chip->pcm[i]); + snd_ac97_suspend(chip->ac97[i]); + } + + spin_lock_irq(&chip->reg_lock); + + im->regs[ALI_MISCINT >> 2] = inl(ALI_REG(chip, ALI_MISCINT)); + /* im->regs[ALI_START >> 2] = inl(ALI_REG(chip, ALI_START)); */ + im->regs[ALI_STOP >> 2] = inl(ALI_REG(chip, ALI_STOP)); + + /* disable all IRQ bits */ + outl(0, ALI_REG(chip, ALI_MISCINT)); + + for (i = 0; i < ALI_GLOBAL_REGS; i++) { + if ((i*4 == ALI_MISCINT) || (i*4 == ALI_STOP)) + continue; + im->regs[i] = inl(ALI_REG(chip, i*4)); + } + + for (i = 0; i < ALI_CHANNELS; i++) { + outb(i, ALI_REG(chip, ALI_GC_CIR)); + for (j = 0; j < ALI_CHANNEL_REGS; j++) + im->channel_regs[i][j] = inl(ALI_REG(chip, j*4 + 0xe0)); + } + + /* stop all HW channel */ + outl(0xffffffff, ALI_REG(chip, ALI_STOP)); + + spin_unlock_irq(&chip->reg_lock); + + pci_disable_device(pci); + pci_save_state(pci); + pci_set_power_state(pci, pci_choose_state(pci, state)); + return 0; +} + +static int ali_resume(struct pci_dev *pci) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct snd_ali *chip = card->private_data; + struct snd_ali_image *im; + int i, j; + + im = chip->image; + if (!im) + return 0; + + pci_set_power_state(pci, PCI_D0); + pci_restore_state(pci); + if (pci_enable_device(pci) < 0) { + printk(KERN_ERR "ali5451: pci_enable_device failed, " + "disabling device\n"); + snd_card_disconnect(card); + return -EIO; + } + pci_set_master(pci); + + spin_lock_irq(&chip->reg_lock); + + for (i = 0; i < ALI_CHANNELS; i++) { + outb(i, ALI_REG(chip, ALI_GC_CIR)); + for (j = 0; j < ALI_CHANNEL_REGS; j++) + outl(im->channel_regs[i][j], ALI_REG(chip, j*4 + 0xe0)); + } + + for (i = 0; i < ALI_GLOBAL_REGS; i++) { + if ((i*4 == ALI_MISCINT) || (i*4 == ALI_STOP) || + (i*4 == ALI_START)) + continue; + outl(im->regs[i], ALI_REG(chip, i*4)); + } + + /* start HW channel */ + outl(im->regs[ALI_START >> 2], ALI_REG(chip, ALI_START)); + /* restore IRQ enable bits */ + outl(im->regs[ALI_MISCINT >> 2], ALI_REG(chip, ALI_MISCINT)); + + spin_unlock_irq(&chip->reg_lock); + + for (i = 0 ; i < chip->num_of_codecs; i++) + snd_ac97_resume(chip->ac97[i]); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + return 0; +} +#endif /* CONFIG_PM */ + +static int snd_ali_free(struct snd_ali * codec) +{ + if (codec->hw_initialized) + snd_ali_disable_address_interrupt(codec); + if (codec->irq >= 0) { + synchronize_irq(codec->irq); + free_irq(codec->irq, codec); + } + if (codec->port) + pci_release_regions(codec->pci); + pci_disable_device(codec->pci); +#ifdef CONFIG_PM + kfree(codec->image); +#endif + pci_dev_put(codec->pci_m1533); + pci_dev_put(codec->pci_m7101); + kfree(codec); + return 0; +} + +static int snd_ali_chip_init(struct snd_ali *codec) +{ + unsigned int legacy; + unsigned char temp; + struct pci_dev *pci_dev; + + snd_ali_printk("chip initializing ... \n"); + + if (snd_ali_reset_5451(codec)) { + snd_printk(KERN_ERR "ali_chip_init: reset 5451 error.\n"); + return -1; + } + + if (codec->revision == ALI_5451_V02) { + pci_dev = codec->pci_m1533; + pci_read_config_byte(pci_dev, 0x59, &temp); + temp |= 0x80; + pci_write_config_byte(pci_dev, 0x59, temp); + + pci_dev = codec->pci_m7101; + pci_read_config_byte(pci_dev, 0xb8, &temp); + temp |= 0x20; + pci_write_config_byte(pci_dev, 0xB8, temp); + } + + pci_read_config_dword(codec->pci, 0x44, &legacy); + legacy &= 0xff00ff00; + legacy |= 0x000800aa; + pci_write_config_dword(codec->pci, 0x44, legacy); + + outl(0x80000001, ALI_REG(codec, ALI_GLOBAL_CONTROL)); + outl(0x00000000, ALI_REG(codec, ALI_AINTEN)); + outl(0xffffffff, ALI_REG(codec, ALI_AINT)); + outl(0x00000000, ALI_REG(codec, ALI_VOLUME)); + outb(0x10, ALI_REG(codec, ALI_MPUR2)); + + codec->ac97_ext_id = snd_ali_codec_peek(codec, 0, AC97_EXTENDED_ID); + codec->ac97_ext_status = snd_ali_codec_peek(codec, 0, + AC97_EXTENDED_STATUS); + if (codec->spdif_support) { + snd_ali_enable_spdif_out(codec); + codec->spdif_mask = 0x00000002; + } + + codec->num_of_codecs = 1; + + /* secondary codec - modem */ + if (inl(ALI_REG(codec, ALI_SCTRL)) & ALI_SCTRL_CODEC2_READY) { + codec->num_of_codecs++; + outl(inl(ALI_REG(codec, ALI_SCTRL)) | + (ALI_SCTRL_LINE_IN2 | ALI_SCTRL_GPIO_IN2 | + ALI_SCTRL_LINE_OUT_EN), + ALI_REG(codec, ALI_SCTRL)); + } + + snd_ali_printk("chip initialize succeed.\n"); + return 0; + +} + +/* proc for register dump */ +static void snd_ali_proc_read(struct snd_info_entry *entry, + struct snd_info_buffer *buf) +{ + struct snd_ali *codec = entry->private_data; + int i; + for (i = 0; i < 256 ; i+= 4) + snd_iprintf(buf, "%02x: %08x\n", i, inl(ALI_REG(codec, i))); +} + +static void __devinit snd_ali_proc_init(struct snd_ali *codec) +{ + struct snd_info_entry *entry; + if (!snd_card_proc_new(codec->card, "ali5451", &entry)) + snd_info_set_text_ops(entry, codec, snd_ali_proc_read); +} + +static int __devinit snd_ali_resources(struct snd_ali *codec) +{ + int err; + + snd_ali_printk("resouces allocation ...\n"); + err = pci_request_regions(codec->pci, "ALI 5451"); + if (err < 0) + return err; + codec->port = pci_resource_start(codec->pci, 0); + + if (request_irq(codec->pci->irq, snd_ali_card_interrupt, + IRQF_SHARED, "ALI 5451", codec)) { + snd_printk(KERN_ERR "Unable to request irq.\n"); + return -EBUSY; + } + codec->irq = codec->pci->irq; + snd_ali_printk("resouces allocated.\n"); + return 0; +} +static int snd_ali_dev_free(struct snd_device *device) +{ + struct snd_ali *codec = device->device_data; + snd_ali_free(codec); + return 0; +} + +static int __devinit snd_ali_create(struct snd_card *card, + struct pci_dev *pci, + int pcm_streams, + int spdif_support, + struct snd_ali ** r_ali) +{ + struct snd_ali *codec; + int i, err; + unsigned short cmdw; + static struct snd_device_ops ops = { + .dev_free = snd_ali_dev_free, + }; + + *r_ali = NULL; + + snd_ali_printk("creating ...\n"); + + /* enable PCI device */ + err = pci_enable_device(pci); + if (err < 0) + return err; + /* check, if we can restrict PCI DMA transfers to 31 bits */ + if (pci_set_dma_mask(pci, DMA_31BIT_MASK) < 0 || + pci_set_consistent_dma_mask(pci, DMA_31BIT_MASK) < 0) { + snd_printk(KERN_ERR "architecture does not support " + "31bit PCI busmaster DMA\n"); + pci_disable_device(pci); + return -ENXIO; + } + + codec = kzalloc(sizeof(*codec), GFP_KERNEL); + if (!codec) { + pci_disable_device(pci); + return -ENOMEM; + } + + spin_lock_init(&codec->reg_lock); + spin_lock_init(&codec->voice_alloc); + + codec->card = card; + codec->pci = pci; + codec->irq = -1; + codec->revision = pci->revision; + codec->spdif_support = spdif_support; + + if (pcm_streams < 1) + pcm_streams = 1; + if (pcm_streams > 32) + pcm_streams = 32; + + pci_set_master(pci); + pci_read_config_word(pci, PCI_COMMAND, &cmdw); + if ((cmdw & PCI_COMMAND_IO) != PCI_COMMAND_IO) { + cmdw |= PCI_COMMAND_IO; + pci_write_config_word(pci, PCI_COMMAND, cmdw); + } + pci_set_master(pci); + + if (snd_ali_resources(codec)) { + snd_ali_free(codec); + return -EBUSY; + } + + synchronize_irq(pci->irq); + + codec->synth.chmap = 0; + codec->synth.chcnt = 0; + codec->spdif_mask = 0; + codec->synth.synthcount = 0; + + if (codec->revision == ALI_5451_V02) + codec->chregs.regs.ac97read = ALI_AC97_WRITE; + else + codec->chregs.regs.ac97read = ALI_AC97_READ; + codec->chregs.regs.ac97write = ALI_AC97_WRITE; + + codec->chregs.regs.start = ALI_START; + codec->chregs.regs.stop = ALI_STOP; + codec->chregs.regs.aint = ALI_AINT; + codec->chregs.regs.ainten = ALI_AINTEN; + + codec->chregs.data.start = 0x00; + codec->chregs.data.stop = 0x00; + codec->chregs.data.aint = 0x00; + codec->chregs.data.ainten = 0x00; + + /* M1533: southbridge */ + codec->pci_m1533 = pci_get_device(0x10b9, 0x1533, NULL); + if (!codec->pci_m1533) { + snd_printk(KERN_ERR "ali5451: cannot find ALi 1533 chip.\n"); + snd_ali_free(codec); + return -ENODEV; + } + /* M7101: power management */ + codec->pci_m7101 = pci_get_device(0x10b9, 0x7101, NULL); + if (!codec->pci_m7101 && codec->revision == ALI_5451_V02) { + snd_printk(KERN_ERR "ali5451: cannot find ALi 7101 chip.\n"); + snd_ali_free(codec); + return -ENODEV; + } + + snd_ali_printk("snd_device_new is called.\n"); + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, codec, &ops); + if (err < 0) { + snd_ali_free(codec); + return err; + } + + snd_card_set_dev(card, &pci->dev); + + /* initialise synth voices*/ + for (i = 0; i < ALI_CHANNELS; i++) + codec->synth.voices[i].number = i; + + err = snd_ali_chip_init(codec); + if (err < 0) { + snd_printk(KERN_ERR "ali create: chip init error.\n"); + return err; + } + +#ifdef CONFIG_PM + codec->image = kmalloc(sizeof(*codec->image), GFP_KERNEL); + if (!codec->image) + snd_printk(KERN_WARNING "can't allocate apm buffer\n"); +#endif + + snd_ali_enable_address_interrupt(codec); + codec->hw_initialized = 1; + + *r_ali = codec; + snd_ali_printk("created.\n"); + return 0; +} + +static int __devinit snd_ali_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + struct snd_card *card; + struct snd_ali *codec; + int err; + + snd_ali_printk("probe ...\n"); + + card = snd_card_new(index, id, THIS_MODULE, 0); + if (!card) + return -ENOMEM; + + err = snd_ali_create(card, pci, pcm_channels, spdif, &codec); + if (err < 0) + goto error; + card->private_data = codec; + + snd_ali_printk("mixer building ...\n"); + err = snd_ali_mixer(codec); + if (err < 0) + goto error; + + snd_ali_printk("pcm building ...\n"); + err = snd_ali_build_pcms(codec); + if (err < 0) + goto error; + + snd_ali_proc_init(codec); + + strcpy(card->driver, "ALI5451"); + strcpy(card->shortname, "ALI 5451"); + + sprintf(card->longname, "%s at 0x%lx, irq %i", + card->shortname, codec->port, codec->irq); + + snd_ali_printk("register card.\n"); + err = snd_card_register(card); + if (err < 0) + goto error; + + pci_set_drvdata(pci, card); + return 0; + + error: + snd_card_free(card); + return err; +} + +static void __devexit snd_ali_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "ALI 5451", + .id_table = snd_ali_ids, + .probe = snd_ali_probe, + .remove = __devexit_p(snd_ali_remove), +#ifdef CONFIG_PM + .suspend = ali_suspend, + .resume = ali_resume, +#endif +}; + +static int __init alsa_card_ali_init(void) +{ + return pci_register_driver(&driver); +} + +static void __exit alsa_card_ali_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_ali_init) +module_exit(alsa_card_ali_exit) diff -Nur sound/pci/als300.c sound/pci/als300.c --- sound/pci/als300.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/als300.c 2007-05-30 02:00:10.000000000 +0200 @@ -88,8 +88,8 @@ #define PLAYBACK_BLOCK_COUNTER 0x9A #define RECORD_BLOCK_COUNTER 0x9B -#define DEBUG_CALLS 1 -#define DEBUG_PLAY_REC 1 +#define DEBUG_CALLS 0 +#define DEBUG_PLAY_REC 0 #if DEBUG_CALLS #define snd_als300_dbgcalls(format, args...) printk(format, ##args) @@ -733,7 +733,8 @@ snd_als300_init(chip); - if (snd_als300_ac97(chip) < 0) { + err = snd_als300_ac97(chip); + if (err < 0) { snd_printk(KERN_WARNING "Could not create ac97\n"); snd_als300_free(chip); return err; diff -Nur sound/pci/au88x0/au88x0.c sound/pci/au88x0/au88x0.c --- sound/pci/au88x0/au88x0.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/au88x0/au88x0.c 2007-08-21 17:37:19.000000000 +0200 @@ -232,6 +232,7 @@ pci_disable_device(chip->pci_dev); //FIXME: this not the right place to unregister the gameport vortex_gameport_unregister(chip); + kfree(chip); return err; } diff -Nur sound/pci/au88x0/au88x0.c~ sound/pci/au88x0/au88x0.c~ --- sound/pci/au88x0/au88x0.c~ 1970-01-01 01:00:00.000000000 +0100 +++ sound/pci/au88x0/au88x0.c~ 2007-08-07 02:00:07.000000000 +0200 @@ -0,0 +1,400 @@ +/* + * ALSA driver for the Aureal Vortex family of soundprocessors. + * Author: Manuel Jander (mjander@embedded.cl) + * + * This driver is the result of the OpenVortex Project from Savannah + * (savannah.nongnu.org/projects/openvortex). I would like to thank + * the developers of OpenVortex, Jeff Muizelaar and Kester Maddock, from + * whom i got plenty of help, and their codebase was invaluable. + * Thanks to the ALSA developers, they helped a lot working out + * the ALSA part. + * Thanks also to Sourceforge for maintaining the old binary drivers, + * and the forum, where developers could comunicate. + * + * Now at least i can play Legacy DOOM with MIDI music :-) + */ + +#include "au88x0.h" +#include +#include +#include +#include +#include +#include +#include + +// module parameters (see "Module Parameters") +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; +static int pcifix[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 255 }; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); +module_param_array(pcifix, int, NULL, 0444); +MODULE_PARM_DESC(pcifix, "Enable VIA-workaround for " CARD_NAME " soundcard."); + +MODULE_DESCRIPTION("Aureal vortex"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{Aureal Semiconductor Inc., Aureal Vortex Sound Processor}}"); + +MODULE_DEVICE_TABLE(pci, snd_vortex_ids); + +static void vortex_fix_latency(struct pci_dev *vortex) +{ + int rc; + if (!(rc = pci_write_config_byte(vortex, 0x40, 0xff))) { + printk(KERN_INFO CARD_NAME + ": vortex latency is 0xff\n"); + } else { + printk(KERN_WARNING CARD_NAME + ": could not set vortex latency: pci error 0x%x\n", rc); + } +} + +static void vortex_fix_agp_bridge(struct pci_dev *via) +{ + int rc; + u8 value; + + /* + * only set the bit (Extend PCI#2 Internal Master for + * Efficient Handling of Dummy Requests) if the can + * read the config and it is not already set + */ + + if (!(rc = pci_read_config_byte(via, 0x42, &value)) + && ((value & 0x10) + || !(rc = pci_write_config_byte(via, 0x42, value | 0x10)))) { + printk(KERN_INFO CARD_NAME + ": bridge config is 0x%x\n", value | 0x10); + } else { + printk(KERN_WARNING CARD_NAME + ": could not set vortex latency: pci error 0x%x\n", rc); + } +} + +static void __devinit snd_vortex_workaround(struct pci_dev *vortex, int fix) +{ + struct pci_dev *via = NULL; + + /* autodetect if workarounds are required */ + if (fix == 255) { + /* VIA KT133 */ + via = pci_get_device(PCI_VENDOR_ID_VIA, + PCI_DEVICE_ID_VIA_8365_1, NULL); + /* VIA Apollo */ + if (via == NULL) { + via = pci_get_device(PCI_VENDOR_ID_VIA, + PCI_DEVICE_ID_VIA_82C598_1, NULL); + /* AMD Irongate */ + if (via == NULL) + via = pci_get_device(PCI_VENDOR_ID_AMD, + PCI_DEVICE_ID_AMD_FE_GATE_7007, NULL); + } + if (via) { + printk(KERN_INFO CARD_NAME ": Activating latency workaround...\n"); + vortex_fix_latency(vortex); + vortex_fix_agp_bridge(via); + } + } else { + if (fix & 0x1) + vortex_fix_latency(vortex); + if ((fix & 0x2) && (via = pci_get_device(PCI_VENDOR_ID_VIA, + PCI_DEVICE_ID_VIA_8365_1, NULL))) + vortex_fix_agp_bridge(via); + if ((fix & 0x4) && (via = pci_get_device(PCI_VENDOR_ID_VIA, + PCI_DEVICE_ID_VIA_82C598_1, NULL))) + vortex_fix_agp_bridge(via); + if ((fix & 0x8) && (via = pci_get_device(PCI_VENDOR_ID_AMD, + PCI_DEVICE_ID_AMD_FE_GATE_7007, NULL))) + vortex_fix_agp_bridge(via); + } + pci_dev_put(via); +} + +// component-destructor +// (see "Management of Cards and Components") +static int snd_vortex_dev_free(struct snd_device *device) +{ + vortex_t *vortex = device->device_data; + + vortex_gameport_unregister(vortex); + vortex_core_shutdown(vortex); + // Take down PCI interface. + synchronize_irq(vortex->irq); + free_irq(vortex->irq, vortex); + iounmap(vortex->mmio); + pci_release_regions(vortex->pci_dev); + pci_disable_device(vortex->pci_dev); + kfree(vortex); + + return 0; +} + +// chip-specific constructor +// (see "Management of Cards and Components") +static int __devinit +snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip) +{ + vortex_t *chip; + int err; + static struct snd_device_ops ops = { + .dev_free = snd_vortex_dev_free, + }; + + *rchip = NULL; + + // check PCI availability (DMA). + if ((err = pci_enable_device(pci)) < 0) + return err; + if (pci_set_dma_mask(pci, DMA_32BIT_MASK) < 0 || + pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK) < 0) { + printk(KERN_ERR "error to set DMA mask\n"); + pci_disable_device(pci); + return -ENXIO; + } + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (chip == NULL) { + pci_disable_device(pci); + return -ENOMEM; + } + + chip->card = card; + + // initialize the stuff + chip->pci_dev = pci; + chip->io = pci_resource_start(pci, 0); + chip->vendor = pci->vendor; + chip->device = pci->device; + chip->card = card; + chip->irq = -1; + + // (1) PCI resource allocation + // Get MMIO area + // + if ((err = pci_request_regions(pci, CARD_NAME_SHORT)) != 0) + goto regions_out; + + chip->mmio = ioremap_nocache(pci_resource_start(pci, 0), + pci_resource_len(pci, 0)); + if (!chip->mmio) { + printk(KERN_ERR "MMIO area remap failed.\n"); + err = -ENOMEM; + goto ioremap_out; + } + + /* Init audio core. + * This must be done before we do request_irq otherwise we can get spurious + * interupts that we do not handle properly and make a mess of things */ + if ((err = vortex_core_init(chip)) != 0) { + printk(KERN_ERR "hw core init failed\n"); + goto core_out; + } + + if ((err = request_irq(pci->irq, vortex_interrupt, + IRQF_SHARED, CARD_NAME_SHORT, + chip)) != 0) { + printk(KERN_ERR "cannot grab irq\n"); + goto irq_out; + } + chip->irq = pci->irq; + + pci_set_master(pci); + // End of PCI setup. + + // Register alsa root device. + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + goto alloc_out; + } + + snd_card_set_dev(card, &pci->dev); + + *rchip = chip; + + return 0; + + alloc_out: + synchronize_irq(chip->irq); + free_irq(chip->irq, chip); + irq_out: + vortex_core_shutdown(chip); + core_out: + iounmap(chip->mmio); + ioremap_out: + pci_release_regions(chip->pci_dev); + regions_out: + pci_disable_device(chip->pci_dev); + //FIXME: this not the right place to unregister the gameport + vortex_gameport_unregister(chip); + kfree(chip); + return err; +} + +// constructor -- see "Constructor" sub-section +static int __devinit +snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) +{ + static int dev; + struct snd_card *card; + vortex_t *chip; + int err; + + // (1) + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + // (2) + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + // (3) + if ((err = snd_vortex_create(card, pci, &chip)) < 0) { + snd_card_free(card); + return err; + } + snd_vortex_workaround(pci, pcifix[dev]); + + // Card details needed in snd_vortex_midi + strcpy(card->driver, CARD_NAME_SHORT); + sprintf(card->shortname, "Aureal Vortex %s", CARD_NAME_SHORT); + sprintf(card->longname, "%s at 0x%lx irq %i", + card->shortname, chip->io, chip->irq); + + // (4) Alloc components. + // ADB pcm. + if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_ADB, NR_ADB)) < 0) { + snd_card_free(card); + return err; + } +#ifndef CHIP_AU8820 + // ADB SPDIF + if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_SPDIF, 1)) < 0) { + snd_card_free(card); + return err; + } + // A3D + if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_A3D, NR_A3D)) < 0) { + snd_card_free(card); + return err; + } +#endif + /* + // ADB I2S + if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_I2S, 1)) < 0) { + snd_card_free(card); + return err; + } + */ +#ifndef CHIP_AU8810 + // WT pcm. + if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_WT, NR_WT)) < 0) { + snd_card_free(card); + return err; + } +#endif + // snd_ac97_mixer and Vortex mixer. + if ((err = snd_vortex_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_vortex_midi(chip)) < 0) { + snd_card_free(card); + return err; + } + + vortex_gameport_register(chip); + +#if 0 + if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_VORTEX_SYNTH, + sizeof(snd_vortex_synth_arg_t), &wave) < 0 + || wave == NULL) { + snd_printk(KERN_ERR "Can't initialize Aureal wavetable synth\n"); + } else { + snd_vortex_synth_arg_t *arg; + + arg = SNDRV_SEQ_DEVICE_ARGPTR(wave); + strcpy(wave->name, "Aureal Synth"); + arg->hwptr = vortex; + arg->index = 1; + arg->seq_ports = seq_ports[dev]; + arg->max_voices = max_synth_voices[dev]; + } +#endif + + // (5) + if ((err = pci_read_config_word(pci, PCI_DEVICE_ID, + &(chip->device))) < 0) { + snd_card_free(card); + return err; + } + if ((err = pci_read_config_word(pci, PCI_VENDOR_ID, + &(chip->vendor))) < 0) { + snd_card_free(card); + return err; + } + chip->rev = pci->revision; +#ifdef CHIP_AU8830 + if ((chip->rev) != 0xfe && (chip->rev) != 0xfa) { + printk(KERN_ALERT + "vortex: The revision (%x) of your card has not been seen before.\n", + chip->rev); + printk(KERN_ALERT + "vortex: Please email the results of 'lspci -vv' to openvortex-dev@nongnu.org.\n"); + snd_card_free(card); + err = -ENODEV; + return err; + } +#endif + + // (6) + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + // (7) + pci_set_drvdata(pci, card); + dev++; + vortex_connect_default(chip, 1); + vortex_enable_int(chip); + return 0; +} + +// destructor -- see "Destructor" sub-section +static void __devexit snd_vortex_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +// pci_driver definition +static struct pci_driver driver = { + .name = CARD_NAME_SHORT, + .id_table = snd_vortex_ids, + .probe = snd_vortex_probe, + .remove = __devexit_p(snd_vortex_remove), +}; + +// initialization of the module +static int __init alsa_card_vortex_init(void) +{ + return pci_register_driver(&driver); +} + +// clean up the module +static void __exit alsa_card_vortex_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_vortex_init) +module_exit(alsa_card_vortex_exit) diff -Nur sound/pci/au88x0/au88x0_eq.c sound/pci/au88x0/au88x0_eq.c --- sound/pci/au88x0/au88x0_eq.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/au88x0/au88x0_eq.c 2007-07-24 02:00:10.000000000 +0200 @@ -728,15 +728,7 @@ /* ALSA interface */ /* Control interface */ -static int -snd_vortex_eqtoggle_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_vortex_eqtoggle_info snd_ctl_boolean_mono_info static int snd_vortex_eqtoggle_get(struct snd_kcontrol *kcontrol, diff -Nur sound/pci/bt87x.c sound/pci/bt87x.c --- sound/pci/bt87x.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/bt87x.c 2007-08-14 02:00:08.000000000 +0200 @@ -340,28 +340,9 @@ static int snd_bt87x_set_digital_hw(struct snd_bt87x *chip, struct snd_pcm_runtime *runtime) { - static struct { - int rate; - unsigned int bit; - } ratebits[] = { - {8000, SNDRV_PCM_RATE_8000}, - {11025, SNDRV_PCM_RATE_11025}, - {16000, SNDRV_PCM_RATE_16000}, - {22050, SNDRV_PCM_RATE_22050}, - {32000, SNDRV_PCM_RATE_32000}, - {44100, SNDRV_PCM_RATE_44100}, - {48000, SNDRV_PCM_RATE_48000} - }; - int i; - chip->reg_control |= CTL_DA_IOM_DA; runtime->hw = snd_bt87x_digital_hw; - runtime->hw.rates = SNDRV_PCM_RATE_KNOT; - for (i = 0; i < ARRAY_SIZE(ratebits); ++i) - if (chip->dig_rate == ratebits[i].rate) { - runtime->hw.rates = ratebits[i].bit; - break; - } + runtime->hw.rates = snd_pcm_rate_to_rate_bit(chip->dig_rate); runtime->hw.rate_min = chip->dig_rate; runtime->hw.rate_max = chip->dig_rate; return 0; @@ -569,15 +550,7 @@ .put = snd_bt87x_capture_volume_put, }; -static int snd_bt87x_capture_boost_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *info) -{ - info->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - info->count = 1; - info->value.integer.min = 0; - info->value.integer.max = 1; - return 0; -} +#define snd_bt87x_capture_boost_info snd_ctl_boolean_mono_info static int snd_bt87x_capture_boost_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *value) diff -Nur sound/pci/ca0106/ca0106.h sound/pci/ca0106/ca0106.h --- sound/pci/ca0106/ca0106.h 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/ca0106/ca0106.h 2007-08-21 17:37:19.000000000 +0200 @@ -1,7 +1,7 @@ /* * Copyright (c) 2004 James Courtier-Dutton * Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit - * Version: 0.0.21 + * Version: 0.0.22 * * FEATURES currently supported: * See ca0106_main.c for features. @@ -47,6 +47,8 @@ * Added GPIO info for SB Live 24bit. * 0.0.21 * Implement support for Line-in capture on SB Live 24bit. + * 0.0.22 + * Add support for mute control on SB Live 24bit (cards w/ SPI DAC) * * * This code was initally based on code from ALSA's emu10k1x.c which is: @@ -552,6 +554,95 @@ #define CONTROL_CENTER_LFE_CHANNEL 1 #define CONTROL_UNKNOWN_CHANNEL 2 + +/* Based on WM8768 Datasheet Rev 4.2 page 32 */ +#define SPI_REG_MASK 0x1ff /* 16-bit SPI writes have a 7-bit address */ +#define SPI_REG_SHIFT 9 /* followed by 9 bits of data */ + +#define SPI_LDA1_REG 0 /* digital attenuation */ +#define SPI_RDA1_REG 1 +#define SPI_LDA2_REG 4 +#define SPI_RDA2_REG 5 +#define SPI_LDA3_REG 6 +#define SPI_RDA3_REG 7 +#define SPI_LDA4_REG 13 +#define SPI_RDA4_REG 14 +#define SPI_MASTDA_REG 8 + +#define SPI_DA_BIT_UPDATE (1<<8) /* update attenuation values */ +#define SPI_DA_BIT_0dB 0xff /* 0 dB */ +#define SPI_DA_BIT_infdB 0x00 /* inf dB attenuation (mute) */ + +#define SPI_PL_REG 2 +#define SPI_PL_BIT_L_M (0<<5) /* left channel = mute */ +#define SPI_PL_BIT_L_L (1<<5) /* left channel = left */ +#define SPI_PL_BIT_L_R (2<<5) /* left channel = right */ +#define SPI_PL_BIT_L_C (3<<5) /* left channel = (L+R)/2 */ +#define SPI_PL_BIT_R_M (0<<7) /* right channel = mute */ +#define SPI_PL_BIT_R_L (1<<7) /* right channel = left */ +#define SPI_PL_BIT_R_R (2<<7) /* right channel = right */ +#define SPI_PL_BIT_R_C (3<<7) /* right channel = (L+R)/2 */ +#define SPI_IZD_REG 2 +#define SPI_IZD_BIT (1<<4) /* infinite zero detect */ + +#define SPI_FMT_REG 3 +#define SPI_FMT_BIT_RJ (0<<0) /* right justified mode */ +#define SPI_FMT_BIT_LJ (1<<0) /* left justified mode */ +#define SPI_FMT_BIT_I2S (2<<0) /* I2S mode */ +#define SPI_FMT_BIT_DSP (3<<0) /* DSP Modes A or B */ +#define SPI_LRP_REG 3 +#define SPI_LRP_BIT (1<<2) /* invert LRCLK polarity */ +#define SPI_BCP_REG 3 +#define SPI_BCP_BIT (1<<3) /* invert BCLK polarity */ +#define SPI_IWL_REG 3 +#define SPI_IWL_BIT_16 (0<<4) /* 16-bit world length */ +#define SPI_IWL_BIT_20 (1<<4) /* 20-bit world length */ +#define SPI_IWL_BIT_24 (2<<4) /* 24-bit world length */ +#define SPI_IWL_BIT_32 (3<<4) /* 32-bit world length */ + +#define SPI_MS_REG 10 +#define SPI_MS_BIT (1<<5) /* master mode */ +#define SPI_RATE_REG 10 /* only applies in master mode */ +#define SPI_RATE_BIT_128 (0<<6) /* MCLK = LRCLK * 128 */ +#define SPI_RATE_BIT_192 (1<<6) +#define SPI_RATE_BIT_256 (2<<6) +#define SPI_RATE_BIT_384 (3<<6) +#define SPI_RATE_BIT_512 (4<<6) +#define SPI_RATE_BIT_768 (5<<6) + +/* They really do label the bit for the 4th channel "4" and not "3" */ +#define SPI_DMUTE0_REG 9 +#define SPI_DMUTE1_REG 9 +#define SPI_DMUTE2_REG 9 +#define SPI_DMUTE4_REG 15 +#define SPI_DMUTE0_BIT (1<<3) +#define SPI_DMUTE1_BIT (1<<4) +#define SPI_DMUTE2_BIT (1<<5) +#define SPI_DMUTE4_BIT (1<<2) + +#define SPI_PHASE0_REG 3 +#define SPI_PHASE1_REG 3 +#define SPI_PHASE2_REG 3 +#define SPI_PHASE4_REG 15 +#define SPI_PHASE0_BIT (1<<6) +#define SPI_PHASE1_BIT (1<<7) +#define SPI_PHASE2_BIT (1<<8) +#define SPI_PHASE4_BIT (1<<3) + +#define SPI_PDWN_REG 2 /* power down all DACs */ +#define SPI_PDWN_BIT (1<<2) +#define SPI_DACD0_REG 10 /* power down individual DACs */ +#define SPI_DACD1_REG 10 +#define SPI_DACD2_REG 10 +#define SPI_DACD4_REG 15 +#define SPI_DACD0_BIT (1<<1) +#define SPI_DACD1_BIT (1<<2) +#define SPI_DACD2_BIT (1<<3) +#define SPI_DACD4_BIT (1<<0) /* datasheet error says it's 1 */ + +#define SPI_PWRDNALL_REG 10 /* power down everything */ +#define SPI_PWRDNALL_BIT (1<<4) + #include "ca_midi.h" struct snd_ca0106; @@ -612,6 +703,8 @@ struct snd_ca_midi midi; struct snd_ca_midi midi2; + + u16 spi_dac_reg[16]; }; int snd_ca0106_mixer(struct snd_ca0106 *emu); @@ -628,4 +721,5 @@ int snd_ca0106_i2c_write(struct snd_ca0106 *emu, u32 reg, u32 value); - +int snd_ca0106_spi_write(struct snd_ca0106 * emu, + unsigned int data); diff -Nur sound/pci/ca0106/ca0106.h~ sound/pci/ca0106/ca0106.h~ --- sound/pci/ca0106/ca0106.h~ 1970-01-01 01:00:00.000000000 +0100 +++ sound/pci/ca0106/ca0106.h~ 2007-07-26 02:00:06.000000000 +0200 @@ -0,0 +1,724 @@ +/* + * Copyright (c) 2004 James Courtier-Dutton + * Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit + * Version: 0.0.22 + * + * FEATURES currently supported: + * See ca0106_main.c for features. + * + * Changelog: + * Support interrupts per period. + * Removed noise from Center/LFE channel when in Analog mode. + * Rename and remove mixer controls. + * 0.0.6 + * Use separate card based DMA buffer for periods table list. + * 0.0.7 + * Change remove and rename ctrls into lists. + * 0.0.8 + * Try to fix capture sources. + * 0.0.9 + * Fix AC3 output. + * Enable S32_LE format support. + * 0.0.10 + * Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".) + * 0.0.11 + * Add Model name recognition. + * 0.0.12 + * Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period. + * Remove redundent "voice" handling. + * 0.0.13 + * Single trigger call for multi channels. + * 0.0.14 + * Set limits based on what the sound card hardware can do. + * playback periods_min=2, periods_max=8 + * capture hw constraints require period_size = n * 64 bytes. + * playback hw constraints require period_size = n * 64 bytes. + * 0.0.15 + * Separated ca0106.c into separate functional .c files. + * 0.0.16 + * Implement 192000 sample rate. + * 0.0.17 + * Add support for SB0410 and SB0413. + * 0.0.18 + * Modified Copyright message. + * 0.0.19 + * Added I2C and SPI registers. Filled in interrupt enable. + * 0.0.20 + * Added GPIO info for SB Live 24bit. + * 0.0.21 + * Implement support for Line-in capture on SB Live 24bit. + * 0.0.22 + * Add support for mute control on SB Live 24bit (cards w/ SPI DAC) + * + * + * This code was initally based on code from ALSA's emu10k1x.c which is: + * Copyright (c) by Francisco Moraes + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/************************************************************************************************/ +/* PCI function 0 registers, address = + PCIBASE0 */ +/************************************************************************************************/ + +#define PTR 0x00 /* Indexed register set pointer register */ + /* NOTE: The CHANNELNUM and ADDRESS words can */ + /* be modified independently of each other. */ + /* CNL[1:0], ADDR[27:16] */ + +#define DATA 0x04 /* Indexed register set data register */ + /* DATA[31:0] */ + +#define IPR 0x08 /* Global interrupt pending register */ + /* Clear pending interrupts by writing a 1 to */ + /* the relevant bits and zero to the other bits */ +#define IPR_MIDI_RX_B 0x00020000 /* MIDI UART-B Receive buffer non-empty */ +#define IPR_MIDI_TX_B 0x00010000 /* MIDI UART-B Transmit buffer empty */ +#define IPR_SPDIF_IN_USER 0x00004000 /* SPDIF input user data has 16 more bits */ +#define IPR_SPDIF_OUT_USER 0x00002000 /* SPDIF output user data needs 16 more bits */ +#define IPR_SPDIF_OUT_FRAME 0x00001000 /* SPDIF frame about to start */ +#define IPR_SPI 0x00000800 /* SPI transaction completed */ +#define IPR_I2C_EEPROM 0x00000400 /* I2C EEPROM transaction completed */ +#define IPR_I2C_DAC 0x00000200 /* I2C DAC transaction completed */ +#define IPR_AI 0x00000100 /* Audio pending register changed. See PTR reg 0x76 */ +#define IPR_GPI 0x00000080 /* General Purpose input changed */ +#define IPR_SRC_LOCKED 0x00000040 /* SRC lock status changed */ +#define IPR_SPDIF_STATUS 0x00000020 /* SPDIF status changed */ +#define IPR_TIMER2 0x00000010 /* 192000Hz Timer */ +#define IPR_TIMER1 0x00000008 /* 44100Hz Timer */ +#define IPR_MIDI_RX_A 0x00000004 /* MIDI UART-A Receive buffer non-empty */ +#define IPR_MIDI_TX_A 0x00000002 /* MIDI UART-A Transmit buffer empty */ +#define IPR_PCI 0x00000001 /* PCI Bus error */ + +#define INTE 0x0c /* Interrupt enable register */ + +#define INTE_MIDI_RX_B 0x00020000 /* MIDI UART-B Receive buffer non-empty */ +#define INTE_MIDI_TX_B 0x00010000 /* MIDI UART-B Transmit buffer empty */ +#define INTE_SPDIF_IN_USER 0x00004000 /* SPDIF input user data has 16 more bits */ +#define INTE_SPDIF_OUT_USER 0x00002000 /* SPDIF output user data needs 16 more bits */ +#define INTE_SPDIF_OUT_FRAME 0x00001000 /* SPDIF frame about to start */ +#define INTE_SPI 0x00000800 /* SPI transaction completed */ +#define INTE_I2C_EEPROM 0x00000400 /* I2C EEPROM transaction completed */ +#define INTE_I2C_DAC 0x00000200 /* I2C DAC transaction completed */ +#define INTE_AI 0x00000100 /* Audio pending register changed. See PTR reg 0x75 */ +#define INTE_GPI 0x00000080 /* General Purpose input changed */ +#define INTE_SRC_LOCKED 0x00000040 /* SRC lock status changed */ +#define INTE_SPDIF_STATUS 0x00000020 /* SPDIF status changed */ +#define INTE_TIMER2 0x00000010 /* 192000Hz Timer */ +#define INTE_TIMER1 0x00000008 /* 44100Hz Timer */ +#define INTE_MIDI_RX_A 0x00000004 /* MIDI UART-A Receive buffer non-empty */ +#define INTE_MIDI_TX_A 0x00000002 /* MIDI UART-A Transmit buffer empty */ +#define INTE_PCI 0x00000001 /* PCI Bus error */ + +#define UNKNOWN10 0x10 /* Unknown ??. Defaults to 0 */ +#define HCFG 0x14 /* Hardware config register */ + /* 0x1000 causes AC3 to fails. It adds a dither bit. */ + +#define HCFG_STAC 0x10000000 /* Special mode for STAC9460 Codec. */ +#define HCFG_CAPTURE_I2S_BYPASS 0x08000000 /* 1 = bypass I2S input async SRC. */ +#define HCFG_CAPTURE_SPDIF_BYPASS 0x04000000 /* 1 = bypass SPDIF input async SRC. */ +#define HCFG_PLAYBACK_I2S_BYPASS 0x02000000 /* 0 = I2S IN mixer output, 1 = I2S IN1. */ +#define HCFG_FORCE_LOCK 0x01000000 /* For test only. Force input SRC tracker to lock. */ +#define HCFG_PLAYBACK_ATTENUATION 0x00006000 /* Playback attenuation mask. 0 = 0dB, 1 = 6dB, 2 = 12dB, 3 = Mute. */ +#define HCFG_PLAYBACK_DITHER 0x00001000 /* 1 = Add dither bit to all playback channels. */ +#define HCFG_PLAYBACK_S32_LE 0x00000800 /* 1 = S32_LE, 0 = S16_LE */ +#define HCFG_CAPTURE_S32_LE 0x00000400 /* 1 = S32_LE, 0 = S16_LE (S32_LE current not working) */ +#define HCFG_8_CHANNEL_PLAY 0x00000200 /* 1 = 8 channels, 0 = 2 channels per substream.*/ +#define HCFG_8_CHANNEL_CAPTURE 0x00000100 /* 1 = 8 channels, 0 = 2 channels per substream.*/ +#define HCFG_MONO 0x00000080 /* 1 = I2S Input mono */ +#define HCFG_I2S_OUTPUT 0x00000010 /* 1 = I2S Output disabled */ +#define HCFG_AC97 0x00000008 /* 0 = AC97 1.0, 1 = AC97 2.0 */ +#define HCFG_LOCK_PLAYBACK_CACHE 0x00000004 /* 1 = Cancel bustmaster accesses to soundcache */ + /* NOTE: This should generally never be used. */ +#define HCFG_LOCK_CAPTURE_CACHE 0x00000002 /* 1 = Cancel bustmaster accesses to soundcache */ + /* NOTE: This should generally never be used. */ +#define HCFG_AUDIOENABLE 0x00000001 /* 0 = CODECs transmit zero-valued samples */ + /* Should be set to 1 when the EMU10K1 is */ + /* completely initialized. */ +#define GPIO 0x18 /* Defaults: 005f03a3-Analog, 005f02a2-SPDIF. */ + /* Here pins 0,1,2,3,4,,6 are output. 5,7 are input */ + /* For the Audigy LS, pin 0 (or bit 8) controls the SPDIF/Analog jack. */ + /* SB Live 24bit: + * bit 8 0 = SPDIF in and out / 1 = Analog (Mic or Line)-in. + * bit 9 0 = Mute / 1 = Analog out. + * bit 10 0 = Line-in / 1 = Mic-in. + * bit 11 0 = ? / 1 = ? + * bit 12 0 = 48 Khz / 1 = 96 Khz Analog out on SB Live 24bit. + * bit 13 0 = ? / 1 = ? + * bit 14 0 = Mute / 1 = Analog out + * bit 15 0 = ? / 1 = ? + * Both bit 9 and bit 14 have to be set for analog sound to work on the SB Live 24bit. + */ + /* 8 general purpose programmable In/Out pins. + * GPI [8:0] Read only. Default 0. + * GPO [15:8] Default 0x9. (Default to SPDIF jack enabled for SPDIF) + * GPO Enable [23:16] Default 0x0f. Setting a bit to 1, causes the pin to be an output pin. + */ +#define AC97DATA 0x1c /* AC97 register set data register (16 bit) */ + +#define AC97ADDRESS 0x1e /* AC97 register set address register (8 bit) */ + +/********************************************************************************************************/ +/* CA0106 pointer-offset register set, accessed through the PTR and DATA registers */ +/********************************************************************************************************/ + +/* Initally all registers from 0x00 to 0x3f have zero contents. */ +#define PLAYBACK_LIST_ADDR 0x00 /* Base DMA address of a list of pointers to each period/size */ + /* One list entry: 4 bytes for DMA address, + * 4 bytes for period_size << 16. + * One list entry is 8 bytes long. + * One list entry for each period in the buffer. + */ + /* ADDR[31:0], Default: 0x0 */ +#define PLAYBACK_LIST_SIZE 0x01 /* Size of list in bytes << 16. E.g. 8 periods -> 0x00380000 */ + /* SIZE[21:16], Default: 0x8 */ +#define PLAYBACK_LIST_PTR 0x02 /* Pointer to the current period being played */ + /* PTR[5:0], Default: 0x0 */ +#define PLAYBACK_UNKNOWN3 0x03 /* Not used ?? */ +#define PLAYBACK_DMA_ADDR 0x04 /* Playback DMA addresss */ + /* DMA[31:0], Default: 0x0 */ +#define PLAYBACK_PERIOD_SIZE 0x05 /* Playback period size. win2000 uses 0x04000000 */ + /* SIZE[31:16], Default: 0x0 */ +#define PLAYBACK_POINTER 0x06 /* Playback period pointer. Used with PLAYBACK_LIST_PTR to determine buffer position currently in DAC */ + /* POINTER[15:0], Default: 0x0 */ +#define PLAYBACK_PERIOD_END_ADDR 0x07 /* Playback fifo end address */ + /* END_ADDR[15:0], FLAG[16] 0 = don't stop, 1 = stop */ +#define PLAYBACK_FIFO_OFFSET_ADDRESS 0x08 /* Current fifo offset address [21:16] */ + /* Cache size valid [5:0] */ +#define PLAYBACK_UNKNOWN9 0x09 /* 0x9 to 0xf Unused */ +#define CAPTURE_DMA_ADDR 0x10 /* Capture DMA address */ + /* DMA[31:0], Default: 0x0 */ +#define CAPTURE_BUFFER_SIZE 0x11 /* Capture buffer size */ + /* SIZE[31:16], Default: 0x0 */ +#define CAPTURE_POINTER 0x12 /* Capture buffer pointer. Sample currently in ADC */ + /* POINTER[15:0], Default: 0x0 */ +#define CAPTURE_FIFO_OFFSET_ADDRESS 0x13 /* Current fifo offset address [21:16] */ + /* Cache size valid [5:0] */ +#define PLAYBACK_LAST_SAMPLE 0x20 /* The sample currently being played */ +/* 0x21 - 0x3f unused */ +#define BASIC_INTERRUPT 0x40 /* Used by both playback and capture interrupt handler */ + /* Playback (0x1< Center Speaker, 2 -> Sub Woofer, 3 -> Ground, 4 -> Ground + * For Digital: 1 -> Front SPDIF, 2 -> Rear SPDIF, 3 -> Center/Subwoofer SPDIF, 4 -> Ground. + * Standard 4 pole Video A/V cable with RCA outputs: 1 -> White, 2 -> Yellow, 3 -> Sheild on all three, 4 -> Red. + * So, from this you can see that you cannot use a Standard 4 pole Video A/V cable with the SB Audigy LS card. + */ +/* The Front SPDIF PCM gets mixed with samples from the AC97 codec, so can only work for Stereo PCM and not AC3/DTS + * The Rear SPDIF can be used for Stereo PCM and also AC3/DTS + * The Center/LFE SPDIF cannot be used for AC3/DTS, but can be used for Stereo PCM. + * Summary: For ALSA we use the Rear channel for SPDIF Digital AC3/DTS output + */ +/* A standard 2 pole mono mini-jack to RCA plug can be used for SPDIF Stereo PCM output from the Front channel. + * A standard 3 pole stereo mini-jack to 2 RCA plugs can be used for SPDIF AC3/DTS and Stereo PCM output utilising the Rear channel and just one of the RCA plugs. + */ +#define SPCS0 0x41 /* SPDIF output Channel Status 0 register. For Rear. default=0x02108004, non-audio=0x02108006 */ +#define SPCS1 0x42 /* SPDIF output Channel Status 1 register. For Front */ +#define SPCS2 0x43 /* SPDIF output Channel Status 2 register. For Center/LFE */ +#define SPCS3 0x44 /* SPDIF output Channel Status 3 register. Unknown */ + /* When Channel set to 0: */ +#define SPCS_CLKACCYMASK 0x30000000 /* Clock accuracy */ +#define SPCS_CLKACCY_1000PPM 0x00000000 /* 1000 parts per million */ +#define SPCS_CLKACCY_50PPM 0x10000000 /* 50 parts per million */ +#define SPCS_CLKACCY_VARIABLE 0x20000000 /* Variable accuracy */ +#define SPCS_SAMPLERATEMASK 0x0f000000 /* Sample rate */ +#define SPCS_SAMPLERATE_44 0x00000000 /* 44.1kHz sample rate */ +#define SPCS_SAMPLERATE_48 0x02000000 /* 48kHz sample rate */ +#define SPCS_SAMPLERATE_32 0x03000000 /* 32kHz sample rate */ +#define SPCS_CHANNELNUMMASK 0x00f00000 /* Channel number */ +#define SPCS_CHANNELNUM_UNSPEC 0x00000000 /* Unspecified channel number */ +#define SPCS_CHANNELNUM_LEFT 0x00100000 /* Left channel */ +#define SPCS_CHANNELNUM_RIGHT 0x00200000 /* Right channel */ +#define SPCS_SOURCENUMMASK 0x000f0000 /* Source number */ +#define SPCS_SOURCENUM_UNSPEC 0x00000000 /* Unspecified source number */ +#define SPCS_GENERATIONSTATUS 0x00008000 /* Originality flag (see IEC-958 spec) */ +#define SPCS_CATEGORYCODEMASK 0x00007f00 /* Category code (see IEC-958 spec) */ +#define SPCS_MODEMASK 0x000000c0 /* Mode (see IEC-958 spec) */ +#define SPCS_EMPHASISMASK 0x00000038 /* Emphasis */ +#define SPCS_EMPHASIS_NONE 0x00000000 /* No emphasis */ +#define SPCS_EMPHASIS_50_15 0x00000008 /* 50/15 usec 2 channel */ +#define SPCS_COPYRIGHT 0x00000004 /* Copyright asserted flag -- do not modify */ +#define SPCS_NOTAUDIODATA 0x00000002 /* 0 = Digital audio, 1 = not audio */ +#define SPCS_PROFESSIONAL 0x00000001 /* 0 = Consumer (IEC-958), 1 = pro (AES3-1992) */ + + /* When Channel set to 1: */ +#define SPCS_WORD_LENGTH_MASK 0x0000000f /* Word Length Mask */ +#define SPCS_WORD_LENGTH_16 0x00000008 /* Word Length 16 bit */ +#define SPCS_WORD_LENGTH_17 0x00000006 /* Word Length 17 bit */ +#define SPCS_WORD_LENGTH_18 0x00000004 /* Word Length 18 bit */ +#define SPCS_WORD_LENGTH_19 0x00000002 /* Word Length 19 bit */ +#define SPCS_WORD_LENGTH_20A 0x0000000a /* Word Length 20 bit */ +#define SPCS_WORD_LENGTH_20 0x00000009 /* Word Length 20 bit (both 0xa and 0x9 are 20 bit) */ +#define SPCS_WORD_LENGTH_21 0x00000007 /* Word Length 21 bit */ +#define SPCS_WORD_LENGTH_21 0x00000007 /* Word Length 21 bit */ +#define SPCS_WORD_LENGTH_22 0x00000005 /* Word Length 22 bit */ +#define SPCS_WORD_LENGTH_23 0x00000003 /* Word Length 23 bit */ +#define SPCS_WORD_LENGTH_24 0x0000000b /* Word Length 24 bit */ +#define SPCS_ORIGINAL_SAMPLE_RATE_MASK 0x000000f0 /* Original Sample rate */ +#define SPCS_ORIGINAL_SAMPLE_RATE_NONE 0x00000000 /* Original Sample rate not indicated */ +#define SPCS_ORIGINAL_SAMPLE_RATE_16000 0x00000010 /* Original Sample rate */ +#define SPCS_ORIGINAL_SAMPLE_RATE_RES1 0x00000020 /* Original Sample rate */ +#define SPCS_ORIGINAL_SAMPLE_RATE_32000 0x00000030 /* Original Sample rate */ +#define SPCS_ORIGINAL_SAMPLE_RATE_12000 0x00000040 /* Original Sample rate */ +#define SPCS_ORIGINAL_SAMPLE_RATE_11025 0x00000050 /* Original Sample rate */ +#define SPCS_ORIGINAL_SAMPLE_RATE_8000 0x00000060 /* Original Sample rate */ +#define SPCS_ORIGINAL_SAMPLE_RATE_RES2 0x00000070 /* Original Sample rate */ +#define SPCS_ORIGINAL_SAMPLE_RATE_192000 0x00000080 /* Original Sample rate */ +#define SPCS_ORIGINAL_SAMPLE_RATE_24000 0x00000090 /* Original Sample rate */ +#define SPCS_ORIGINAL_SAMPLE_RATE_96000 0x000000a0 /* Original Sample rate */ +#define SPCS_ORIGINAL_SAMPLE_RATE_48000 0x000000b0 /* Original Sample rate */ +#define SPCS_ORIGINAL_SAMPLE_RATE_176400 0x000000c0 /* Original Sample rate */ +#define SPCS_ORIGINAL_SAMPLE_RATE_22050 0x000000d0 /* Original Sample rate */ +#define SPCS_ORIGINAL_SAMPLE_RATE_88200 0x000000e0 /* Original Sample rate */ +#define SPCS_ORIGINAL_SAMPLE_RATE_44100 0x000000f0 /* Original Sample rate */ + +#define SPDIF_SELECT1 0x45 /* Enables SPDIF or Analogue outputs 0-SPDIF, 0xf00-Analogue */ + /* 0x100 - Front, 0x800 - Rear, 0x200 - Center/LFE. + * But as the jack is shared, use 0xf00. + * The Windows2000 driver uses 0x0000000f for both digital and analog. + * 0xf00 introduces interesting noises onto the Center/LFE. + * If you turn the volume up, you hear computer noise, + * e.g. mouse moving, changing between app windows etc. + * So, I am going to set this to 0x0000000f all the time now, + * same as the windows driver does. + * Use register SPDIF_SELECT2(0x72) to switch between SPDIF and Analog. + */ + /* When Channel = 0: + * Wide SPDIF format [3:0] (one bit for each channel) (0=20bit, 1=24bit) + * Tristate SPDIF Output [11:8] (one bit for each channel) (0=Not tristate, 1=Tristate) + * SPDIF Bypass enable [19:16] (one bit for each channel) (0=Not bypass, 1=Bypass) + */ + /* When Channel = 1: + * SPDIF 0 User data [7:0] + * SPDIF 1 User data [15:8] + * SPDIF 0 User data [23:16] + * SPDIF 0 User data [31:24] + * User data can be sent by using the SPDIF output frame pending and SPDIF output user bit interrupts. + */ +#define WATERMARK 0x46 /* Test bit to indicate cache usage level */ +#define SPDIF_INPUT_STATUS 0x49 /* SPDIF Input status register. Bits the same as SPCS. + * When Channel = 0: Bits the same as SPCS channel 0. + * When Channel = 1: Bits the same as SPCS channel 1. + * When Channel = 2: + * SPDIF Input User data [16:0] + * SPDIF Input Frame count [21:16] + */ +#define CAPTURE_CACHE_DATA 0x50 /* 0x50-0x5f Recorded samples. */ +#define CAPTURE_SOURCE 0x60 /* Capture Source 0 = MIC */ +#define CAPTURE_SOURCE_CHANNEL0 0xf0000000 /* Mask for selecting the Capture sources */ +#define CAPTURE_SOURCE_CHANNEL1 0x0f000000 /* 0 - SPDIF mixer output. */ +#define CAPTURE_SOURCE_CHANNEL2 0x00f00000 /* 1 - What you hear or . 2 - ?? */ +#define CAPTURE_SOURCE_CHANNEL3 0x000f0000 /* 3 - Mic in, Line in, TAD in, Aux in. */ +#define CAPTURE_SOURCE_RECORD_MAP 0x0000ffff /* Default 0x00e4 */ + /* Record Map [7:0] (2 bits per channel) 0=mapped to channel 0, 1=mapped to channel 1, 2=mapped to channel2, 3=mapped to channel3 + * Record source select for channel 0 [18:16] + * Record source select for channel 1 [22:20] + * Record source select for channel 2 [26:24] + * Record source select for channel 3 [30:28] + * 0 - SPDIF mixer output. + * 1 - i2s mixer output. + * 2 - SPDIF input. + * 3 - i2s input. + * 4 - AC97 capture. + * 5 - SRC output. + */ +#define CAPTURE_VOLUME1 0x61 /* Capture volume per channel 0-3 */ +#define CAPTURE_VOLUME2 0x62 /* Capture volume per channel 4-7 */ + +#define PLAYBACK_ROUTING1 0x63 /* Playback routing of channels 0-7. Effects AC3 output. Default 0x32765410 */ +#define ROUTING1_REAR 0x77000000 /* Channel_id 0 sends to 10, Channel_id 1 sends to 32 */ +#define ROUTING1_NULL 0x00770000 /* Channel_id 2 sends to 54, Channel_id 3 sends to 76 */ +#define ROUTING1_CENTER_LFE 0x00007700 /* 0x32765410 means, send Channel_id 0 to FRONT, Channel_id 1 to REAR */ +#define ROUTING1_FRONT 0x00000077 /* Channel_id 2 to CENTER_LFE, Channel_id 3 to NULL. */ + /* Channel_id's handle stereo channels. Channel X is a single mono channel */ + /* Host is input from the PCI bus. */ + /* Host channel 0 [2:0] -> SPDIF Mixer/Router channel 0-7. + * Host channel 1 [6:4] -> SPDIF Mixer/Router channel 0-7. + * Host channel 2 [10:8] -> SPDIF Mixer/Router channel 0-7. + * Host channel 3 [14:12] -> SPDIF Mixer/Router channel 0-7. + * Host channel 4 [18:16] -> SPDIF Mixer/Router channel 0-7. + * Host channel 5 [22:20] -> SPDIF Mixer/Router channel 0-7. + * Host channel 6 [26:24] -> SPDIF Mixer/Router channel 0-7. + * Host channel 7 [30:28] -> SPDIF Mixer/Router channel 0-7. + */ + +#define PLAYBACK_ROUTING2 0x64 /* Playback Routing . Feeding Capture channels back into Playback. Effects AC3 output. Default 0x76767676 */ + /* SRC is input from the capture inputs. */ + /* SRC channel 0 [2:0] -> SPDIF Mixer/Router channel 0-7. + * SRC channel 1 [6:4] -> SPDIF Mixer/Router channel 0-7. + * SRC channel 2 [10:8] -> SPDIF Mixer/Router channel 0-7. + * SRC channel 3 [14:12] -> SPDIF Mixer/Router channel 0-7. + * SRC channel 4 [18:16] -> SPDIF Mixer/Router channel 0-7. + * SRC channel 5 [22:20] -> SPDIF Mixer/Router channel 0-7. + * SRC channel 6 [26:24] -> SPDIF Mixer/Router channel 0-7. + * SRC channel 7 [30:28] -> SPDIF Mixer/Router channel 0-7. + */ + +#define PLAYBACK_MUTE 0x65 /* Unknown. While playing 0x0, while silent 0x00fc0000 */ + /* SPDIF Mixer input control: + * Invert SRC to SPDIF Mixer [7-0] (One bit per channel) + * Invert Host to SPDIF Mixer [15:8] (One bit per channel) + * SRC to SPDIF Mixer disable [23:16] (One bit per channel) + * Host to SPDIF Mixer disable [31:24] (One bit per channel) + */ +#define PLAYBACK_VOLUME1 0x66 /* Playback SPDIF volume per channel. Set to the same PLAYBACK_VOLUME(0x6a) */ + /* PLAYBACK_VOLUME1 must be set to 30303030 for SPDIF AC3 Playback */ + /* SPDIF mixer input volume. 0=12dB, 0x30=0dB, 0xFE=-51.5dB, 0xff=Mute */ + /* One register for each of the 4 stereo streams. */ + /* SRC Right volume [7:0] + * SRC Left volume [15:8] + * Host Right volume [23:16] + * Host Left volume [31:24] + */ +#define CAPTURE_ROUTING1 0x67 /* Capture Routing. Default 0x32765410 */ + /* Similar to register 0x63, except that the destination is the I2S mixer instead of the SPDIF mixer. I.E. Outputs to the Analog outputs instead of SPDIF. */ +#define CAPTURE_ROUTING2 0x68 /* Unknown Routing. Default 0x76767676 */ + /* Similar to register 0x64, except that the destination is the I2S mixer instead of the SPDIF mixer. I.E. Outputs to the Analog outputs instead of SPDIF. */ +#define CAPTURE_MUTE 0x69 /* Unknown. While capturing 0x0, while silent 0x00fc0000 */ + /* Similar to register 0x65, except that the destination is the I2S mixer instead of the SPDIF mixer. I.E. Outputs to the Analog outputs instead of SPDIF. */ +#define PLAYBACK_VOLUME2 0x6a /* Playback Analog volume per channel. Does not effect AC3 output */ + /* Similar to register 0x66, except that the destination is the I2S mixer instead of the SPDIF mixer. I.E. Outputs to the Analog outputs instead of SPDIF. */ +#define UNKNOWN6b 0x6b /* Unknown. Readonly. Default 00400000 00400000 00400000 00400000 */ +#define MIDI_UART_A_DATA 0x6c /* Midi Uart A Data */ +#define MIDI_UART_A_CMD 0x6d /* Midi Uart A Command/Status */ +#define MIDI_UART_B_DATA 0x6e /* Midi Uart B Data (currently unused) */ +#define MIDI_UART_B_CMD 0x6f /* Midi Uart B Command/Status (currently unused) */ + +/* unique channel identifier for midi->channel */ + +#define CA0106_MIDI_CHAN_A 0x1 +#define CA0106_MIDI_CHAN_B 0x2 + +/* from mpu401 */ + +#define CA0106_MIDI_INPUT_AVAIL 0x80 +#define CA0106_MIDI_OUTPUT_READY 0x40 +#define CA0106_MPU401_RESET 0xff +#define CA0106_MPU401_ENTER_UART 0x3f +#define CA0106_MPU401_ACK 0xfe + +#define SAMPLE_RATE_TRACKER_STATUS 0x70 /* Readonly. Default 00108000 00108000 00500000 00500000 */ + /* Estimated sample rate [19:0] Relative to 48kHz. 0x8000 = 1.0 + * Rate Locked [20] + * SPDIF Locked [21] For SPDIF channel only. + * Valid Audio [22] For SPDIF channel only. + */ +#define CAPTURE_CONTROL 0x71 /* Some sort of routing. default = 40c81000 30303030 30300000 00700000 */ + /* Channel_id 0: 0x40c81000 must be changed to 0x40c80000 for SPDIF AC3 input or output. */ + /* Channel_id 1: 0xffffffff(mute) 0x30303030(max) controls CAPTURE feedback into PLAYBACK. */ + /* Sample rate output control register Channel=0 + * Sample output rate [1:0] (0=48kHz, 1=44.1kHz, 2=96kHz, 3=192Khz) + * Sample input rate [3:2] (0=48kHz, 1=Not available, 2=96kHz, 3=192Khz) + * SRC input source select [4] 0=Audio from digital mixer, 1=Audio from analog source. + * Record rate [9:8] (0=48kHz, 1=Not available, 2=96kHz, 3=192Khz) + * Record mixer output enable [12:10] + * I2S input rate master mode [15:14] (0=48kHz, 1=44.1kHz, 2=96kHz, 3=192Khz) + * I2S output rate [17:16] (0=48kHz, 1=44.1kHz, 2=96kHz, 3=192Khz) + * I2S output source select [18] (0=Audio from host, 1=Audio from SRC) + * Record mixer I2S enable [20:19] (enable/disable i2sin1 and i2sin0) + * I2S output master clock select [21] (0=256*I2S output rate, 1=512*I2S output rate.) + * I2S input master clock select [22] (0=256*I2S input rate, 1=512*I2S input rate.) + * I2S input mode [23] (0=Slave, 1=Master) + * SPDIF output rate [25:24] (0=48kHz, 1=44.1kHz, 2=96kHz, 3=192Khz) + * SPDIF output source select [26] (0=host, 1=SRC) + * Not used [27] + * Record Source 0 input [29:28] (0=SPDIF in, 1=I2S in, 2=AC97 Mic, 3=AC97 PCM) + * Record Source 1 input [31:30] (0=SPDIF in, 1=I2S in, 2=AC97 Mic, 3=AC97 PCM) + */ + /* Sample rate output control register Channel=1 + * I2S Input 0 volume Right [7:0] + * I2S Input 0 volume Left [15:8] + * I2S Input 1 volume Right [23:16] + * I2S Input 1 volume Left [31:24] + */ + /* Sample rate output control register Channel=2 + * SPDIF Input volume Right [23:16] + * SPDIF Input volume Left [31:24] + */ + /* Sample rate output control register Channel=3 + * No used + */ +#define SPDIF_SELECT2 0x72 /* Some sort of routing. Channel_id 0 only. default = 0x0f0f003f. Analog 0x000b0000, Digital 0x0b000000 */ +#define ROUTING2_FRONT_MASK 0x00010000 /* Enable for Front speakers. */ +#define ROUTING2_CENTER_LFE_MASK 0x00020000 /* Enable for Center/LFE speakers. */ +#define ROUTING2_REAR_MASK 0x00080000 /* Enable for Rear speakers. */ + /* Audio output control + * AC97 output enable [5:0] + * I2S output enable [19:16] + * SPDIF output enable [27:24] + */ +#define UNKNOWN73 0x73 /* Unknown. Readonly. Default 0x0 */ +#define CHIP_VERSION 0x74 /* P17 Chip version. Channel_id 0 only. Default 00000071 */ +#define EXTENDED_INT_MASK 0x75 /* Used by both playback and capture interrupt handler */ + /* Sets which Interrupts are enabled. */ + /* 0x00000001 = Half period. Playback. + * 0x00000010 = Full period. Playback. + * 0x00000100 = Half buffer. Playback. + * 0x00001000 = Full buffer. Playback. + * 0x00010000 = Half buffer. Capture. + * 0x00100000 = Full buffer. Capture. + * Capture can only do 2 periods. + * 0x01000000 = End audio. Playback. + * 0x40000000 = Half buffer Playback,Caputre xrun. + * 0x80000000 = Full buffer Playback,Caputre xrun. + */ +#define EXTENDED_INT 0x76 /* Used by both playback and capture interrupt handler */ + /* Shows which interrupts are active at the moment. */ + /* Same bit layout as EXTENDED_INT_MASK */ +#define COUNTER77 0x77 /* Counter range 0 to 0x3fffff, 192000 counts per second. */ +#define COUNTER78 0x78 /* Counter range 0 to 0x3fffff, 44100 counts per second. */ +#define EXTENDED_INT_TIMER 0x79 /* Channel_id 0 only. Used by both playback and capture interrupt handler */ + /* Causes interrupts based on timer intervals. */ +#define SPI 0x7a /* SPI: Serial Interface Register */ +#define I2C_A 0x7b /* I2C Address. 32 bit */ +#define I2C_D0 0x7c /* I2C Data Port 0. 32 bit */ +#define I2C_D1 0x7d /* I2C Data Port 1. 32 bit */ +//I2C values +#define I2C_A_ADC_ADD_MASK 0x000000fe //The address is a 7 bit address +#define I2C_A_ADC_RW_MASK 0x00000001 //bit mask for R/W +#define I2C_A_ADC_TRANS_MASK 0x00000010 //Bit mask for I2c address DAC value +#define I2C_A_ADC_ABORT_MASK 0x00000020 //Bit mask for I2C transaction abort flag +#define I2C_A_ADC_LAST_MASK 0x00000040 //Bit mask for Last word transaction +#define I2C_A_ADC_BYTE_MASK 0x00000080 //Bit mask for Byte Mode + +#define I2C_A_ADC_ADD 0x00000034 //This is the Device address for ADC +#define I2C_A_ADC_READ 0x00000001 //To perform a read operation +#define I2C_A_ADC_START 0x00000100 //Start I2C transaction +#define I2C_A_ADC_ABORT 0x00000200 //I2C transaction abort +#define I2C_A_ADC_LAST 0x00000400 //I2C last transaction +#define I2C_A_ADC_BYTE 0x00000800 //I2C one byte mode + +#define I2C_D_ADC_REG_MASK 0xfe000000 //ADC address register +#define I2C_D_ADC_DAT_MASK 0x01ff0000 //ADC data register + +#define ADC_TIMEOUT 0x00000007 //ADC Timeout Clock Disable +#define ADC_IFC_CTRL 0x0000000b //ADC Interface Control +#define ADC_MASTER 0x0000000c //ADC Master Mode Control +#define ADC_POWER 0x0000000d //ADC PowerDown Control +#define ADC_ATTEN_ADCL 0x0000000e //ADC Attenuation ADCL +#define ADC_ATTEN_ADCR 0x0000000f //ADC Attenuation ADCR +#define ADC_ALC_CTRL1 0x00000010 //ADC ALC Control 1 +#define ADC_ALC_CTRL2 0x00000011 //ADC ALC Control 2 +#define ADC_ALC_CTRL3 0x00000012 //ADC ALC Control 3 +#define ADC_NOISE_CTRL 0x00000013 //ADC Noise Gate Control +#define ADC_LIMIT_CTRL 0x00000014 //ADC Limiter Control +#define ADC_MUX 0x00000015 //ADC Mux offset + +#if 0 +/* FIXME: Not tested yet. */ +#define ADC_GAIN_MASK 0x000000ff //Mask for ADC Gain +#define ADC_ZERODB 0x000000cf //Value to set ADC to 0dB +#define ADC_MUTE_MASK 0x000000c0 //Mask for ADC mute +#define ADC_MUTE 0x000000c0 //Value to mute ADC +#define ADC_OSR 0x00000008 //Mask for ADC oversample rate select +#define ADC_TIMEOUT_DISABLE 0x00000008 //Value and mask to disable Timeout clock +#define ADC_HPF_DISABLE 0x00000100 //Value and mask to disable High pass filter +#define ADC_TRANWIN_MASK 0x00000070 //Mask for Length of Transient Window +#endif + +#define ADC_MUX_MASK 0x0000000f //Mask for ADC Mux +#define ADC_MUX_PHONE 0x00000001 //Value to select TAD at ADC Mux (Not used) +#define ADC_MUX_MIC 0x00000002 //Value to select Mic at ADC Mux +#define ADC_MUX_LINEIN 0x00000004 //Value to select LineIn at ADC Mux +#define ADC_MUX_AUX 0x00000008 //Value to select Aux at ADC Mux + +#define SET_CHANNEL 0 /* Testing channel outputs 0=Front, 1=Center/LFE, 2=Unknown, 3=Rear */ +#define PCM_FRONT_CHANNEL 0 +#define PCM_REAR_CHANNEL 1 +#define PCM_CENTER_LFE_CHANNEL 2 +#define PCM_UNKNOWN_CHANNEL 3 +#define CONTROL_FRONT_CHANNEL 0 +#define CONTROL_REAR_CHANNEL 3 +#define CONTROL_CENTER_LFE_CHANNEL 1 +#define CONTROL_UNKNOWN_CHANNEL 2 + + +/* Based on WM8768 Datasheet Rev 4.2 page 32 */ +#define SPI_REG_MASK 0x1ff /* 16-bit SPI writes have a 7-bit address */ +#define SPI_REG_SHIFT 9 /* followed by 9 bits of data */ + +#define SPI_LDA1_REG 0 /* digital attenuation */ +#define SPI_RDA1_REG 1 +#define SPI_LDA2_REG 4 +#define SPI_RDA2_REG 5 +#define SPI_LDA3_REG 6 +#define SPI_RDA3_REG 7 +#define SPI_LDA4_REG 13 +#define SPI_RDA4_REG 14 +#define SPI_MASTDA_REG 8 + +#define SPI_DA_BIT_UPDATE (1<<8) /* update attenuation values */ +#define SPI_DA_BIT_0dB 0xff /* 0 dB */ +#define SPI_DA_BIT_infdB 0x00 /* inf dB attenuation (mute) */ + +#define SPI_PL_REG 2 +#define SPI_PL_BIT_L_M (0<<5) /* left channel = mute */ +#define SPI_PL_BIT_L_L (1<<5) /* left channel = left */ +#define SPI_PL_BIT_L_R (2<<5) /* left channel = right */ +#define SPI_PL_BIT_L_C (3<<5) /* left channel = (L+R)/2 */ +#define SPI_PL_BIT_R_M (0<<7) /* right channel = mute */ +#define SPI_PL_BIT_R_L (1<<7) /* right channel = left */ +#define SPI_PL_BIT_R_R (2<<7) /* right channel = right */ +#define SPI_PL_BIT_R_C (3<<7) /* right channel = (L+R)/2 */ +#define SPI_IZD_REG 2 +#define SPI_IZD_BIT (1<<4) /* infinite zero detect */ + +#define SPI_FMT_REG 3 +#define SPI_FMT_BIT_RJ (0<<0) /* right justified mode */ +#define SPI_FMT_BIT_LJ (1<<0) /* left justified mode */ +#define SPI_FMT_BIT_I2S (2<<0) /* I2S mode */ +#define SPI_FMT_BIT_DSP (3<<0) /* DSP Modes A or B */ +#define SPI_LRP_REG 3 +#define SPI_LRP_BIT (1<<2) /* invert LRCLK polarity */ +#define SPI_BCP_REG 3 +#define SPI_BCP_BIT (1<<3) /* invert BCLK polarity */ +#define SPI_IWL_REG 3 +#define SPI_IWL_BIT_16 (0<<4) /* 16-bit world length */ +#define SPI_IWL_BIT_20 (1<<4) /* 20-bit world length */ +#define SPI_IWL_BIT_24 (2<<4) /* 24-bit world length */ +#define SPI_IWL_BIT_32 (3<<4) /* 32-bit world length */ + +#define SPI_MS_REG 10 +#define SPI_MS_BIT (1<<5) /* master mode */ +#define SPI_RATE_REG 10 /* only applies in master mode */ +#define SPI_RATE_BIT_128 (0<<6) /* MCLK = LRCLK * 128 */ +#define SPI_RATE_BIT_192 (1<<6) +#define SPI_RATE_BIT_256 (2<<6) +#define SPI_RATE_BIT_384 (3<<6) +#define SPI_RATE_BIT_512 (4<<6) +#define SPI_RATE_BIT_768 (5<<6) + +/* They really do label the bit for the 4th channel "4" and not "3" */ +#define SPI_DMUTE0_REG 9 +#define SPI_DMUTE1_REG 9 +#define SPI_DMUTE2_REG 9 +#define SPI_DMUTE4_REG 15 +#define SPI_DMUTE0_BIT (1<<3) +#define SPI_DMUTE1_BIT (1<<4) +#define SPI_DMUTE2_BIT (1<<5) +#define SPI_DMUTE4_BIT (1<<2) + +#define SPI_PHASE0_REG 3 +#define SPI_PHASE1_REG 3 +#define SPI_PHASE2_REG 3 +#define SPI_PHASE4_REG 15 +#define SPI_PHASE0_BIT (1<<6) +#define SPI_PHASE1_BIT (1<<7) +#define SPI_PHASE2_BIT (1<<8) +#define SPI_PHASE4_BIT (1<<3) + +#define SPI_PDWN_REG 2 /* power down all DACs */ +#define SPI_PDWN_BIT (1<<2) +#define SPI_DACD0_REG 10 /* power down individual DACs */ +#define SPI_DACD1_REG 10 +#define SPI_DACD2_REG 10 +#define SPI_DACD4_REG 15 +#define SPI_DACD0_BIT (1<<1) +#define SPI_DACD1_BIT (1<<2) +#define SPI_DACD2_BIT (1<<3) +#define SPI_DACD4_BIT (1<<0) /* datasheet error says it's 1 */ + +#define SPI_PWRDNALL_REG 10 /* power down everything */ +#define SPI_PWRDNALL_BIT (1<<4) + +#include "ca_midi.h" + +struct snd_ca0106; + +struct snd_ca0106_channel { + struct snd_ca0106 *emu; + int number; + int use; + void (*interrupt)(struct snd_ca0106 *emu, struct snd_ca0106_channel *channel); + struct snd_ca0106_pcm *epcm; +}; + +struct snd_ca0106_pcm { + struct snd_ca0106 *emu; + struct snd_pcm_substream *substream; + int channel_id; + unsigned short running; +}; + +struct snd_ca0106_details { + u32 serial; + char * name; + int ac97; + int gpio_type; + int i2c_adc; + int spi_dac; +}; + +// definition of the chip-specific record +struct snd_ca0106 { + struct snd_card *card; + struct snd_ca0106_details *details; + struct pci_dev *pci; + + unsigned long port; + struct resource *res_port; + int irq; + + unsigned int serial; /* serial number */ + unsigned short model; /* subsystem id */ + + spinlock_t emu_lock; + + struct snd_ac97 *ac97; + struct snd_pcm *pcm; + + struct snd_ca0106_channel playback_channels[4]; + struct snd_ca0106_channel capture_channels[4]; + u32 spdif_bits[4]; /* s/pdif out setup */ + int spdif_enable; + int capture_source; + int i2c_capture_source; + u8 i2c_capture_volume[4][2]; + int capture_mic_line_in; + + struct snd_dma_buffer buffer; + + struct snd_ca_midi midi; + struct snd_ca_midi midi2; + + u16 spi_dac_reg[16]; +}; + +int snd_ca0106_mixer(struct snd_ca0106 *emu); +int snd_ca0106_proc_init(struct snd_ca0106 * emu); + +unsigned int snd_ca0106_ptr_read(struct snd_ca0106 * emu, + unsigned int reg, + unsigned int chn); + +void snd_ca0106_ptr_write(struct snd_ca0106 *emu, + unsigned int reg, + unsigned int chn, + unsigned int data); + +int snd_ca0106_i2c_write(struct snd_ca0106 *emu, u32 reg, u32 value); + +int snd_ca0106_spi_write(struct snd_ca0106 * emu, + unsigned int data); diff -Nur sound/pci/ca0106/ca0106_main.c sound/pci/ca0106/ca0106_main.c --- sound/pci/ca0106/ca0106_main.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/ca0106/ca0106_main.c 2007-08-21 17:37:19.000000000 +0200 @@ -1,7 +1,7 @@ /* * Copyright (c) 2004 James Courtier-Dutton * Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit - * Version: 0.0.23 + * Version: 0.0.25 * * FEATURES currently supported: * Front, Rear and Center/LFE. @@ -79,6 +79,10 @@ * Add support for MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97. From kiksen, bug #901 * 0.0.23 * Implement support for Line-in capture on SB Live 24bit. + * 0.0.24 + * Add support for mute control on SB Live 24bit (cards w/ SPI DAC) + * 0.0.25 + * Powerdown SPI DAC channels when not in use * * BUGS: * Some stability problems when unloading the snd-ca0106 kernel module. @@ -168,6 +172,34 @@ #include "ca0106.h" static struct snd_ca0106_details ca0106_chip_details[] = { + /* Sound Blaster X-Fi Extreme Audio. This does not have an AC97. 53SB079000000 */ + /* It is really just a normal SB Live 24bit. */ + /* Tested: + * See ALSA bug#3251 + */ + { .serial = 0x10131102, + .name = "X-Fi Extreme Audio [SBxxxx]", + .gpio_type = 1, + .i2c_adc = 1 } , + /* Sound Blaster X-Fi Extreme Audio. This does not have an AC97. 53SB079000000 */ + /* It is really just a normal SB Live 24bit. */ + /* + * CTRL:CA0111-WTLF + * ADC: WM8775SEDS + * DAC: CS4382-KQZ + */ + /* Tested: + * Playback on front, rear, center/lfe speakers + * Capture from Mic in. + * Not-Tested: + * Capture from Line in. + * Playback to digital out. + */ + { .serial = 0x10121102, + .name = "X-Fi Extreme Audio [SB0790]", + .gpio_type = 1, + .i2c_adc = 1 } , + /* New Dell Sound Blaster Live! 7.1 24bit. This does not have an AC97. */ /* AudigyLS[SB0310] */ { .serial = 0x10021102, .name = "AudigyLS [SB0310]", @@ -242,10 +274,11 @@ /* hardware definition */ static struct snd_pcm_hardware snd_ca0106_playback_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID), + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_SYNC_START, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, .rates = (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000), @@ -428,6 +461,19 @@ kfree(runtime->private_data); } +static const int spi_dacd_reg[] = { + [PCM_FRONT_CHANNEL] = SPI_DACD4_REG, + [PCM_REAR_CHANNEL] = SPI_DACD0_REG, + [PCM_CENTER_LFE_CHANNEL]= SPI_DACD2_REG, + [PCM_UNKNOWN_CHANNEL] = SPI_DACD1_REG, +}; +static const int spi_dacd_bit[] = { + [PCM_FRONT_CHANNEL] = SPI_DACD4_BIT, + [PCM_REAR_CHANNEL] = SPI_DACD0_BIT, + [PCM_CENTER_LFE_CHANNEL]= SPI_DACD2_BIT, + [PCM_UNKNOWN_CHANNEL] = SPI_DACD1_BIT, +}; + /* open_playback callback */ static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substream, int channel_id) @@ -462,6 +508,17 @@ return err; if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0) return err; + snd_pcm_set_sync(substream); + + if (chip->details->spi_dac && channel_id != PCM_FRONT_CHANNEL) { + const int reg = spi_dacd_reg[channel_id]; + + /* Power up dac */ + chip->spi_dac_reg[reg] &= ~spi_dacd_bit[channel_id]; + err = snd_ca0106_spi_write(chip, chip->spi_dac_reg[reg]); + if (err < 0) + return err; + } return 0; } @@ -472,6 +529,14 @@ struct snd_pcm_runtime *runtime = substream->runtime; struct snd_ca0106_pcm *epcm = runtime->private_data; chip->playback_channels[epcm->channel_id].use = 0; + + if (chip->details->spi_dac && epcm->channel_id != PCM_FRONT_CHANNEL) { + const int reg = spi_dacd_reg[epcm->channel_id]; + + /* Power down DAC */ + chip->spi_dac_reg[reg] |= spi_dacd_bit[epcm->channel_id]; + snd_ca0106_spi_write(chip, chip->spi_dac_reg[reg]); + } /* FIXME: maybe zero others */ return 0; } @@ -790,6 +855,9 @@ break; } snd_pcm_group_for_each_entry(s, substream) { + if (snd_pcm_substream_chip(s) != emu || + s->stream != SNDRV_PCM_STREAM_PLAYBACK) + continue; runtime = s->runtime; epcm = runtime->private_data; channel = epcm->channel_id; @@ -1195,28 +1263,23 @@ return 0; } +#define SPI_REG(reg, value) (((reg) << SPI_REG_SHIFT) | (value)) static unsigned int spi_dac_init[] = { - 0x00ff, - 0x02ff, - 0x0400, - 0x0520, - 0x0620, /* Set 24 bit. Was 0x0600 */ - 0x08ff, - 0x0aff, - 0x0cff, - 0x0eff, - 0x10ff, - 0x1200, - 0x1400, - 0x1480, - 0x1800, - 0x1aff, - 0x1cff, - 0x1e00, - 0x0530, - 0x0602, - 0x0622, - 0x1400, + SPI_REG(SPI_LDA1_REG, SPI_DA_BIT_0dB), /* 0dB dig. attenuation */ + SPI_REG(SPI_RDA1_REG, SPI_DA_BIT_0dB), + SPI_REG(SPI_PL_REG, SPI_PL_BIT_L_L | SPI_PL_BIT_R_R | SPI_IZD_BIT), + SPI_REG(SPI_FMT_REG, SPI_FMT_BIT_I2S | SPI_IWL_BIT_24), + SPI_REG(SPI_LDA2_REG, SPI_DA_BIT_0dB), + SPI_REG(SPI_RDA2_REG, SPI_DA_BIT_0dB), + SPI_REG(SPI_LDA3_REG, SPI_DA_BIT_0dB), + SPI_REG(SPI_RDA3_REG, SPI_DA_BIT_0dB), + SPI_REG(SPI_MASTDA_REG, SPI_DA_BIT_0dB), + SPI_REG(9, 0x00), + SPI_REG(SPI_MS_REG, SPI_DACD0_BIT | SPI_DACD1_BIT | SPI_DACD2_BIT), + SPI_REG(12, 0x00), + SPI_REG(SPI_LDA4_REG, SPI_DA_BIT_0dB), + SPI_REG(SPI_RDA4_REG, SPI_DA_BIT_0dB | SPI_DA_BIT_UPDATE), + SPI_REG(SPI_DACD4_REG, 0x00), }; static unsigned int i2c_adc_init[][2] = { @@ -1457,8 +1520,13 @@ int size, n; size = ARRAY_SIZE(spi_dac_init); - for (n=0; n < size; n++) + for (n = 0; n < size; n++) { + int reg = spi_dac_init[n] >> SPI_REG_SHIFT; + snd_ca0106_spi_write(chip, spi_dac_init[n]); + if (reg < ARRAY_SIZE(chip->spi_dac_reg)) + chip->spi_dac_reg[reg] = spi_dac_init[n]; + } } if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, diff -Nur sound/pci/ca0106/ca0106_main.c~ sound/pci/ca0106/ca0106_main.c~ --- sound/pci/ca0106/ca0106_main.c~ 1970-01-01 01:00:00.000000000 +0100 +++ sound/pci/ca0106/ca0106_main.c~ 2007-08-14 02:00:08.000000000 +0200 @@ -0,0 +1,1732 @@ +/* + * Copyright (c) 2004 James Courtier-Dutton + * Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit + * Version: 0.0.25 + * + * FEATURES currently supported: + * Front, Rear and Center/LFE. + * Surround40 and Surround51. + * Capture from MIC an LINE IN input. + * SPDIF digital playback of PCM stereo and AC3/DTS works. + * (One can use a standard mono mini-jack to one RCA plugs cable. + * or one can use a standard stereo mini-jack to two RCA plugs cable. + * Plug one of the RCA plugs into the Coax input of the external decoder/receiver.) + * ( In theory one could output 3 different AC3 streams at once, to 3 different SPDIF outputs. ) + * Notes on how to capture sound: + * The AC97 is used in the PLAYBACK direction. + * The output from the AC97 chip, instead of reaching the speakers, is fed into the Philips 1361T ADC. + * So, to record from the MIC, set the MIC Playback volume to max, + * unmute the MIC and turn up the MASTER Playback volume. + * So, to prevent feedback when capturing, minimise the "Capture feedback into Playback" volume. + * + * The only playback controls that currently do anything are: - + * Analog Front + * Analog Rear + * Analog Center/LFE + * SPDIF Front + * SPDIF Rear + * SPDIF Center/LFE + * + * For capture from Mic in or Line in. + * Digital/Analog ( switch must be in Analog mode for CAPTURE. ) + * + * CAPTURE feedback into PLAYBACK + * + * Changelog: + * Support interrupts per period. + * Removed noise from Center/LFE channel when in Analog mode. + * Rename and remove mixer controls. + * 0.0.6 + * Use separate card based DMA buffer for periods table list. + * 0.0.7 + * Change remove and rename ctrls into lists. + * 0.0.8 + * Try to fix capture sources. + * 0.0.9 + * Fix AC3 output. + * Enable S32_LE format support. + * 0.0.10 + * Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".) + * 0.0.11 + * Add Model name recognition. + * 0.0.12 + * Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period. + * Remove redundent "voice" handling. + * 0.0.13 + * Single trigger call for multi channels. + * 0.0.14 + * Set limits based on what the sound card hardware can do. + * playback periods_min=2, periods_max=8 + * capture hw constraints require period_size = n * 64 bytes. + * playback hw constraints require period_size = n * 64 bytes. + * 0.0.15 + * Minor updates. + * 0.0.16 + * Implement 192000 sample rate. + * 0.0.17 + * Add support for SB0410 and SB0413. + * 0.0.18 + * Modified Copyright message. + * 0.0.19 + * Finally fix support for SB Live 24 bit. SB0410 and SB0413. + * The output codec needs resetting, otherwise all output is muted. + * 0.0.20 + * Merge "pci_disable_device(pci);" fixes. + * 0.0.21 + * Add 4 capture channels. (SPDIF only comes in on channel 0. ) + * Add SPDIF capture using optional digital I/O module for SB Live 24bit. (Analog capture does not yet work.) + * 0.0.22 + * Add support for MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97. From kiksen, bug #901 + * 0.0.23 + * Implement support for Line-in capture on SB Live 24bit. + * 0.0.24 + * Add support for mute control on SB Live 24bit (cards w/ SPI DAC) + * 0.0.25 + * Powerdown SPI DAC channels when not in use + * + * BUGS: + * Some stability problems when unloading the snd-ca0106 kernel module. + * -- + * + * TODO: + * 4 Capture channels, only one implemented so far. + * Other capture rates apart from 48khz not implemented. + * MIDI + * -- + * GENERAL INFO: + * Model: SB0310 + * P17 Chip: CA0106-DAT + * AC97 Codec: STAC 9721 + * ADC: Philips 1361T (Stereo 24bit) + * DAC: WM8746EDS (6-channel, 24bit, 192Khz) + * + * GENERAL INFO: + * Model: SB0410 + * P17 Chip: CA0106-DAT + * AC97 Codec: None + * ADC: WM8775EDS (4 Channel) + * DAC: CS4382 (114 dB, 24-Bit, 192 kHz, 8-Channel D/A Converter with DSD Support) + * SPDIF Out control switches between Mic in and SPDIF out. + * No sound out or mic input working yet. + * + * GENERAL INFO: + * Model: SB0413 + * P17 Chip: CA0106-DAT + * AC97 Codec: None. + * ADC: Unknown + * DAC: Unknown + * Trying to handle it like the SB0410. + * + * This code was initally based on code from ALSA's emu10k1x.c which is: + * Copyright (c) by Francisco Moraes + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("James Courtier-Dutton "); +MODULE_DESCRIPTION("CA0106"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{Creative,SB CA0106 chip}}"); + +// module parameters (see "Module Parameters") +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; +static uint subsystem[SNDRV_CARDS]; /* Force card subsystem model */ + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for the CA0106 soundcard."); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for the CA0106 soundcard."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable the CA0106 soundcard."); +module_param_array(subsystem, uint, NULL, 0444); +MODULE_PARM_DESC(subsystem, "Force card subsystem model."); + +#include "ca0106.h" + +static struct snd_ca0106_details ca0106_chip_details[] = { + /* Sound Blaster X-Fi Extreme Audio. This does not have an AC97. 53SB079000000 */ + /* It is really just a normal SB Live 24bit. */ + /* Tested: + * See ALSA bug#3251 + */ + { .serial = 0x10131102, + .name = "X-Fi Extreme Audio [SBxxxx]", + .gpio_type = 1, + .i2c_adc = 1 } , + /* Sound Blaster X-Fi Extreme Audio. This does not have an AC97. 53SB079000000 */ + /* It is really just a normal SB Live 24bit. */ + /* + * CTRL:CA0111-WTLF + * ADC: WM8775SEDS + * DAC: CS4382-KQZ + */ + /* Tested: + * Playback on front, rear, center/lfe speakers + * Capture from Mic in. + * Not-Tested: + * Capture from Line in. + * Playback to digital out. + */ + { .serial = 0x10121102, + .name = "X-Fi Extreme Audio [SB0790]", + .gpio_type = 1, + .i2c_adc = 1 } , + /* New Dell Sound Blaster Live! 7.1 24bit. This does not have an AC97. */ + /* AudigyLS[SB0310] */ + { .serial = 0x10021102, + .name = "AudigyLS [SB0310]", + .ac97 = 1 } , + /* Unknown AudigyLS that also says SB0310 on it */ + { .serial = 0x10051102, + .name = "AudigyLS [SB0310b]", + .ac97 = 1 } , + /* New Sound Blaster Live! 7.1 24bit. This does not have an AC97. 53SB041000001 */ + { .serial = 0x10061102, + .name = "Live! 7.1 24bit [SB0410]", + .gpio_type = 1, + .i2c_adc = 1 } , + /* New Dell Sound Blaster Live! 7.1 24bit. This does not have an AC97. */ + { .serial = 0x10071102, + .name = "Live! 7.1 24bit [SB0413]", + .gpio_type = 1, + .i2c_adc = 1 } , + /* New Audigy SE. Has a different DAC. */ + /* SB0570: + * CTRL:CA0106-DAT + * ADC: WM8775EDS + * DAC: WM8768GEDS + */ + { .serial = 0x100a1102, + .name = "Audigy SE [SB0570]", + .gpio_type = 1, + .i2c_adc = 1, + .spi_dac = 1 } , + /* New Audigy LS. Has a different DAC. */ + /* SB0570: + * CTRL:CA0106-DAT + * ADC: WM8775EDS + * DAC: WM8768GEDS + */ + { .serial = 0x10111102, + .name = "Audigy SE OEM [SB0570a]", + .gpio_type = 1, + .i2c_adc = 1, + .spi_dac = 1 } , + /* MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97 */ + /* SB0438 + * CTRL:CA0106-DAT + * ADC: WM8775SEDS + * DAC: CS4382-KQZ + */ + { .serial = 0x10091462, + .name = "MSI K8N Diamond MB [SB0438]", + .gpio_type = 2, + .i2c_adc = 1 } , + /* Shuttle XPC SD31P which has an onboard Creative Labs + * Sound Blaster Live! 24-bit EAX + * high-definition 7.1 audio processor". + * Added using info from andrewvegan in alsa bug #1298 + */ + { .serial = 0x30381297, + .name = "Shuttle XPC SD31P [SD31P]", + .gpio_type = 1, + .i2c_adc = 1 } , + /* Shuttle XPC SD11G5 which has an onboard Creative Labs + * Sound Blaster Live! 24-bit EAX + * high-definition 7.1 audio processor". + * Fixes ALSA bug#1600 + */ + { .serial = 0x30411297, + .name = "Shuttle XPC SD11G5 [SD11G5]", + .gpio_type = 1, + .i2c_adc = 1 } , + { .serial = 0, + .name = "AudigyLS [Unknown]" } +}; + +/* hardware definition */ +static struct snd_pcm_hardware snd_ca0106_playback_hw = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, + .rates = (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000), + .rate_min = 48000, + .rate_max = 192000, + .channels_min = 2, //1, + .channels_max = 2, //6, + .buffer_bytes_max = ((65536 - 64) * 8), + .period_bytes_min = 64, + .period_bytes_max = (65536 - 64), + .periods_min = 2, + .periods_max = 8, + .fifo_size = 0, +}; + +static struct snd_pcm_hardware snd_ca0106_capture_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, + .rates = (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000), + .rate_min = 44100, + .rate_max = 192000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = ((65536 - 64) * 8), + .period_bytes_min = 64, + .period_bytes_max = (65536 - 64), + .periods_min = 2, + .periods_max = 2, + .fifo_size = 0, +}; + +unsigned int snd_ca0106_ptr_read(struct snd_ca0106 * emu, + unsigned int reg, + unsigned int chn) +{ + unsigned long flags; + unsigned int regptr, val; + + regptr = (reg << 16) | chn; + + spin_lock_irqsave(&emu->emu_lock, flags); + outl(regptr, emu->port + PTR); + val = inl(emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); + return val; +} + +void snd_ca0106_ptr_write(struct snd_ca0106 *emu, + unsigned int reg, + unsigned int chn, + unsigned int data) +{ + unsigned int regptr; + unsigned long flags; + + regptr = (reg << 16) | chn; + + spin_lock_irqsave(&emu->emu_lock, flags); + outl(regptr, emu->port + PTR); + outl(data, emu->port + DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +int snd_ca0106_spi_write(struct snd_ca0106 * emu, + unsigned int data) +{ + unsigned int reset, set; + unsigned int reg, tmp; + int n, result; + reg = SPI; + if (data > 0xffff) /* Only 16bit values allowed */ + return 1; + tmp = snd_ca0106_ptr_read(emu, reg, 0); + reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */ + set = reset | 0x10000; /* Set xxx1xxxx */ + snd_ca0106_ptr_write(emu, reg, 0, reset | data); + tmp = snd_ca0106_ptr_read(emu, reg, 0); /* write post */ + snd_ca0106_ptr_write(emu, reg, 0, set | data); + result = 1; + /* Wait for status bit to return to 0 */ + for (n = 0; n < 100; n++) { + udelay(10); + tmp = snd_ca0106_ptr_read(emu, reg, 0); + if (!(tmp & 0x10000)) { + result = 0; + break; + } + } + if (result) /* Timed out */ + return 1; + snd_ca0106_ptr_write(emu, reg, 0, reset | data); + tmp = snd_ca0106_ptr_read(emu, reg, 0); /* Write post */ + return 0; +} + +/* The ADC does not support i2c read, so only write is implemented */ +int snd_ca0106_i2c_write(struct snd_ca0106 *emu, + u32 reg, + u32 value) +{ + u32 tmp; + int timeout = 0; + int status; + int retry; + if ((reg > 0x7f) || (value > 0x1ff)) { + snd_printk(KERN_ERR "i2c_write: invalid values.\n"); + return -EINVAL; + } + + tmp = reg << 25 | value << 16; + // snd_printk("I2C-write:reg=0x%x, value=0x%x\n", reg, value); + /* Not sure what this I2C channel controls. */ + /* snd_ca0106_ptr_write(emu, I2C_D0, 0, tmp); */ + + /* This controls the I2C connected to the WM8775 ADC Codec */ + snd_ca0106_ptr_write(emu, I2C_D1, 0, tmp); + + for (retry = 0; retry < 10; retry++) { + /* Send the data to i2c */ + //tmp = snd_ca0106_ptr_read(emu, I2C_A, 0); + //tmp = tmp & ~(I2C_A_ADC_READ|I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD_MASK); + tmp = 0; + tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD); + snd_ca0106_ptr_write(emu, I2C_A, 0, tmp); + + /* Wait till the transaction ends */ + while (1) { + status = snd_ca0106_ptr_read(emu, I2C_A, 0); + //snd_printk("I2C:status=0x%x\n", status); + timeout++; + if ((status & I2C_A_ADC_START) == 0) + break; + + if (timeout > 1000) + break; + } + //Read back and see if the transaction is successful + if ((status & I2C_A_ADC_ABORT) == 0) + break; + } + + if (retry == 10) { + snd_printk(KERN_ERR "Writing to ADC failed!\n"); + return -EINVAL; + } + + return 0; +} + + +static void snd_ca0106_intr_enable(struct snd_ca0106 *emu, unsigned int intrenb) +{ + unsigned long flags; + unsigned int enable; + + spin_lock_irqsave(&emu->emu_lock, flags); + enable = inl(emu->port + INTE) | intrenb; + outl(enable, emu->port + INTE); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +static void snd_ca0106_intr_disable(struct snd_ca0106 *emu, unsigned int intrenb) +{ + unsigned long flags; + unsigned int enable; + + spin_lock_irqsave(&emu->emu_lock, flags); + enable = inl(emu->port + INTE) & ~intrenb; + outl(enable, emu->port + INTE); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + + +static void snd_ca0106_pcm_free_substream(struct snd_pcm_runtime *runtime) +{ + kfree(runtime->private_data); +} + +static const int spi_dacd_reg[] = { + [PCM_FRONT_CHANNEL] = SPI_DACD4_REG, + [PCM_REAR_CHANNEL] = SPI_DACD0_REG, + [PCM_CENTER_LFE_CHANNEL]= SPI_DACD2_REG, + [PCM_UNKNOWN_CHANNEL] = SPI_DACD1_REG, +}; +static const int spi_dacd_bit[] = { + [PCM_FRONT_CHANNEL] = SPI_DACD4_BIT, + [PCM_REAR_CHANNEL] = SPI_DACD0_BIT, + [PCM_CENTER_LFE_CHANNEL]= SPI_DACD2_BIT, + [PCM_UNKNOWN_CHANNEL] = SPI_DACD1_BIT, +}; + +/* open_playback callback */ +static int snd_ca0106_pcm_open_playback_channel(struct snd_pcm_substream *substream, + int channel_id) +{ + struct snd_ca0106 *chip = snd_pcm_substream_chip(substream); + struct snd_ca0106_channel *channel = &(chip->playback_channels[channel_id]); + struct snd_ca0106_pcm *epcm; + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + + epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); + + if (epcm == NULL) + return -ENOMEM; + epcm->emu = chip; + epcm->substream = substream; + epcm->channel_id=channel_id; + + runtime->private_data = epcm; + runtime->private_free = snd_ca0106_pcm_free_substream; + + runtime->hw = snd_ca0106_playback_hw; + + channel->emu = chip; + channel->number = channel_id; + + channel->use = 1; + //printk("open:channel_id=%d, chip=%p, channel=%p\n",channel_id, chip, channel); + //channel->interrupt = snd_ca0106_pcm_channel_interrupt; + channel->epcm = epcm; + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0) + return err; + snd_pcm_set_sync(substream); + + if (chip->details->spi_dac && channel_id != PCM_FRONT_CHANNEL) { + const int reg = spi_dacd_reg[channel_id]; + + /* Power up dac */ + chip->spi_dac_reg[reg] &= ~spi_dacd_bit[channel_id]; + err = snd_ca0106_spi_write(chip, chip->spi_dac_reg[reg]); + if (err < 0) + return err; + } + return 0; +} + +/* close callback */ +static int snd_ca0106_pcm_close_playback(struct snd_pcm_substream *substream) +{ + struct snd_ca0106 *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ca0106_pcm *epcm = runtime->private_data; + chip->playback_channels[epcm->channel_id].use = 0; + + if (chip->details->spi_dac && epcm->channel_id != PCM_FRONT_CHANNEL) { + const int reg = spi_dacd_reg[epcm->channel_id]; + + /* Power down DAC */ + chip->spi_dac_reg[reg] |= spi_dacd_bit[epcm->channel_id]; + snd_ca0106_spi_write(chip, chip->spi_dac_reg[reg]); + } + /* FIXME: maybe zero others */ + return 0; +} + +static int snd_ca0106_pcm_open_playback_front(struct snd_pcm_substream *substream) +{ + return snd_ca0106_pcm_open_playback_channel(substream, PCM_FRONT_CHANNEL); +} + +static int snd_ca0106_pcm_open_playback_center_lfe(struct snd_pcm_substream *substream) +{ + return snd_ca0106_pcm_open_playback_channel(substream, PCM_CENTER_LFE_CHANNEL); +} + +static int snd_ca0106_pcm_open_playback_unknown(struct snd_pcm_substream *substream) +{ + return snd_ca0106_pcm_open_playback_channel(substream, PCM_UNKNOWN_CHANNEL); +} + +static int snd_ca0106_pcm_open_playback_rear(struct snd_pcm_substream *substream) +{ + return snd_ca0106_pcm_open_playback_channel(substream, PCM_REAR_CHANNEL); +} + +/* open_capture callback */ +static int snd_ca0106_pcm_open_capture_channel(struct snd_pcm_substream *substream, + int channel_id) +{ + struct snd_ca0106 *chip = snd_pcm_substream_chip(substream); + struct snd_ca0106_channel *channel = &(chip->capture_channels[channel_id]); + struct snd_ca0106_pcm *epcm; + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + + epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); + if (epcm == NULL) { + snd_printk(KERN_ERR "open_capture_channel: failed epcm alloc\n"); + return -ENOMEM; + } + epcm->emu = chip; + epcm->substream = substream; + epcm->channel_id=channel_id; + + runtime->private_data = epcm; + runtime->private_free = snd_ca0106_pcm_free_substream; + + runtime->hw = snd_ca0106_capture_hw; + + channel->emu = chip; + channel->number = channel_id; + + channel->use = 1; + //printk("open:channel_id=%d, chip=%p, channel=%p\n",channel_id, chip, channel); + //channel->interrupt = snd_ca0106_pcm_channel_interrupt; + channel->epcm = epcm; + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + //snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_capture_period_sizes); + if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0) + return err; + return 0; +} + +/* close callback */ +static int snd_ca0106_pcm_close_capture(struct snd_pcm_substream *substream) +{ + struct snd_ca0106 *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ca0106_pcm *epcm = runtime->private_data; + chip->capture_channels[epcm->channel_id].use = 0; + /* FIXME: maybe zero others */ + return 0; +} + +static int snd_ca0106_pcm_open_0_capture(struct snd_pcm_substream *substream) +{ + return snd_ca0106_pcm_open_capture_channel(substream, 0); +} + +static int snd_ca0106_pcm_open_1_capture(struct snd_pcm_substream *substream) +{ + return snd_ca0106_pcm_open_capture_channel(substream, 1); +} + +static int snd_ca0106_pcm_open_2_capture(struct snd_pcm_substream *substream) +{ + return snd_ca0106_pcm_open_capture_channel(substream, 2); +} + +static int snd_ca0106_pcm_open_3_capture(struct snd_pcm_substream *substream) +{ + return snd_ca0106_pcm_open_capture_channel(substream, 3); +} + +/* hw_params callback */ +static int snd_ca0106_pcm_hw_params_playback(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); +} + +/* hw_free callback */ +static int snd_ca0106_pcm_hw_free_playback(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +/* hw_params callback */ +static int snd_ca0106_pcm_hw_params_capture(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); +} + +/* hw_free callback */ +static int snd_ca0106_pcm_hw_free_capture(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +/* prepare playback callback */ +static int snd_ca0106_pcm_prepare_playback(struct snd_pcm_substream *substream) +{ + struct snd_ca0106 *emu = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ca0106_pcm *epcm = runtime->private_data; + int channel = epcm->channel_id; + u32 *table_base = (u32 *)(emu->buffer.area+(8*16*channel)); + u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size); + u32 hcfg_mask = HCFG_PLAYBACK_S32_LE; + u32 hcfg_set = 0x00000000; + u32 hcfg; + u32 reg40_mask = 0x30000 << (channel<<1); + u32 reg40_set = 0; + u32 reg40; + /* FIXME: Depending on mixer selection of SPDIF out or not, select the spdif rate or the DAC rate. */ + u32 reg71_mask = 0x03030000 ; /* Global. Set SPDIF rate. We only support 44100 to spdif, not to DAC. */ + u32 reg71_set = 0; + u32 reg71; + int i; + + //snd_printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, periods=%u, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, runtime->periods, frames_to_bytes(runtime, 1)); + //snd_printk("dma_addr=%x, dma_area=%p, table_base=%p\n",runtime->dma_addr, runtime->dma_area, table_base); + //snd_printk("dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",emu->buffer.addr, emu->buffer.area, emu->buffer.bytes); + /* Rate can be set per channel. */ + /* reg40 control host to fifo */ + /* reg71 controls DAC rate. */ + switch (runtime->rate) { + case 44100: + reg40_set = 0x10000 << (channel<<1); + reg71_set = 0x01010000; + break; + case 48000: + reg40_set = 0; + reg71_set = 0; + break; + case 96000: + reg40_set = 0x20000 << (channel<<1); + reg71_set = 0x02020000; + break; + case 192000: + reg40_set = 0x30000 << (channel<<1); + reg71_set = 0x03030000; + break; + default: + reg40_set = 0; + reg71_set = 0; + break; + } + /* Format is a global setting */ + /* FIXME: Only let the first channel accessed set this. */ + switch (runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + hcfg_set = 0; + break; + case SNDRV_PCM_FORMAT_S32_LE: + hcfg_set = HCFG_PLAYBACK_S32_LE; + break; + default: + hcfg_set = 0; + break; + } + hcfg = inl(emu->port + HCFG) ; + hcfg = (hcfg & ~hcfg_mask) | hcfg_set; + outl(hcfg, emu->port + HCFG); + reg40 = snd_ca0106_ptr_read(emu, 0x40, 0); + reg40 = (reg40 & ~reg40_mask) | reg40_set; + snd_ca0106_ptr_write(emu, 0x40, 0, reg40); + reg71 = snd_ca0106_ptr_read(emu, 0x71, 0); + reg71 = (reg71 & ~reg71_mask) | reg71_set; + snd_ca0106_ptr_write(emu, 0x71, 0, reg71); + + /* FIXME: Check emu->buffer.size before actually writing to it. */ + for(i=0; i < runtime->periods; i++) { + table_base[i*2] = runtime->dma_addr + (i * period_size_bytes); + table_base[i*2+1] = period_size_bytes << 16; + } + + snd_ca0106_ptr_write(emu, PLAYBACK_LIST_ADDR, channel, emu->buffer.addr+(8*16*channel)); + snd_ca0106_ptr_write(emu, PLAYBACK_LIST_SIZE, channel, (runtime->periods - 1) << 19); + snd_ca0106_ptr_write(emu, PLAYBACK_LIST_PTR, channel, 0); + snd_ca0106_ptr_write(emu, PLAYBACK_DMA_ADDR, channel, runtime->dma_addr); + snd_ca0106_ptr_write(emu, PLAYBACK_PERIOD_SIZE, channel, frames_to_bytes(runtime, runtime->period_size)<<16); // buffer size in bytes + /* FIXME test what 0 bytes does. */ + snd_ca0106_ptr_write(emu, PLAYBACK_PERIOD_SIZE, channel, 0); // buffer size in bytes + snd_ca0106_ptr_write(emu, PLAYBACK_POINTER, channel, 0); + snd_ca0106_ptr_write(emu, 0x07, channel, 0x0); + snd_ca0106_ptr_write(emu, 0x08, channel, 0); + snd_ca0106_ptr_write(emu, PLAYBACK_MUTE, 0x0, 0x0); /* Unmute output */ +#if 0 + snd_ca0106_ptr_write(emu, SPCS0, 0, + SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | + SPCS_GENERATIONSTATUS | 0x00001200 | + 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT ); + } +#endif + + return 0; +} + +/* prepare capture callback */ +static int snd_ca0106_pcm_prepare_capture(struct snd_pcm_substream *substream) +{ + struct snd_ca0106 *emu = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ca0106_pcm *epcm = runtime->private_data; + int channel = epcm->channel_id; + u32 hcfg_mask = HCFG_CAPTURE_S32_LE; + u32 hcfg_set = 0x00000000; + u32 hcfg; + u32 over_sampling=0x2; + u32 reg71_mask = 0x0000c000 ; /* Global. Set ADC rate. */ + u32 reg71_set = 0; + u32 reg71; + + //snd_printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, periods=%u, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, runtime->periods, frames_to_bytes(runtime, 1)); + //snd_printk("dma_addr=%x, dma_area=%p, table_base=%p\n",runtime->dma_addr, runtime->dma_area, table_base); + //snd_printk("dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",emu->buffer.addr, emu->buffer.area, emu->buffer.bytes); + /* reg71 controls ADC rate. */ + switch (runtime->rate) { + case 44100: + reg71_set = 0x00004000; + break; + case 48000: + reg71_set = 0; + break; + case 96000: + reg71_set = 0x00008000; + over_sampling=0xa; + break; + case 192000: + reg71_set = 0x0000c000; + over_sampling=0xa; + break; + default: + reg71_set = 0; + break; + } + /* Format is a global setting */ + /* FIXME: Only let the first channel accessed set this. */ + switch (runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + hcfg_set = 0; + break; + case SNDRV_PCM_FORMAT_S32_LE: + hcfg_set = HCFG_CAPTURE_S32_LE; + break; + default: + hcfg_set = 0; + break; + } + hcfg = inl(emu->port + HCFG) ; + hcfg = (hcfg & ~hcfg_mask) | hcfg_set; + outl(hcfg, emu->port + HCFG); + reg71 = snd_ca0106_ptr_read(emu, 0x71, 0); + reg71 = (reg71 & ~reg71_mask) | reg71_set; + snd_ca0106_ptr_write(emu, 0x71, 0, reg71); + if (emu->details->i2c_adc == 1) { /* The SB0410 and SB0413 use I2C to control ADC. */ + snd_ca0106_i2c_write(emu, ADC_MASTER, over_sampling); /* Adjust the over sampler to better suit the capture rate. */ + } + + + //printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, frames_to_bytes(runtime, 1)); + snd_ca0106_ptr_write(emu, 0x13, channel, 0); + snd_ca0106_ptr_write(emu, CAPTURE_DMA_ADDR, channel, runtime->dma_addr); + snd_ca0106_ptr_write(emu, CAPTURE_BUFFER_SIZE, channel, frames_to_bytes(runtime, runtime->buffer_size)<<16); // buffer size in bytes + snd_ca0106_ptr_write(emu, CAPTURE_POINTER, channel, 0); + + return 0; +} + +/* trigger_playback callback */ +static int snd_ca0106_pcm_trigger_playback(struct snd_pcm_substream *substream, + int cmd) +{ + struct snd_ca0106 *emu = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime; + struct snd_ca0106_pcm *epcm; + int channel; + int result = 0; + struct snd_pcm_substream *s; + u32 basic = 0; + u32 extended = 0; + int running=0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + running=1; + break; + case SNDRV_PCM_TRIGGER_STOP: + default: + running=0; + break; + } + snd_pcm_group_for_each_entry(s, substream) { + if (snd_pcm_substream_chip(s) != emu || + s->stream != SNDRV_PCM_STREAM_PLAYBACK) + continue; + runtime = s->runtime; + epcm = runtime->private_data; + channel = epcm->channel_id; + //snd_printk("channel=%d\n",channel); + epcm->running = running; + basic |= (0x1<runtime; + struct snd_ca0106_pcm *epcm = runtime->private_data; + int channel = epcm->channel_id; + int result = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) | (0x110000<running = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0) & ~(0x100<running = 0; + break; + default: + result = -EINVAL; + break; + } + return result; +} + +/* pointer_playback callback */ +static snd_pcm_uframes_t +snd_ca0106_pcm_pointer_playback(struct snd_pcm_substream *substream) +{ + struct snd_ca0106 *emu = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ca0106_pcm *epcm = runtime->private_data; + snd_pcm_uframes_t ptr, ptr1, ptr2,ptr3,ptr4 = 0; + int channel = epcm->channel_id; + + if (!epcm->running) + return 0; + + ptr3 = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel); + ptr1 = snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel); + ptr4 = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel); + if (ptr3 != ptr4) ptr1 = snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel); + ptr2 = bytes_to_frames(runtime, ptr1); + ptr2+= (ptr4 >> 3) * runtime->period_size; + ptr=ptr2; + if (ptr >= runtime->buffer_size) + ptr -= runtime->buffer_size; + //printk("ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n", ptr1, ptr2, ptr, (int)runtime->buffer_size, (int)runtime->period_size, (int)runtime->frame_bits, (int)runtime->rate); + + return ptr; +} + +/* pointer_capture callback */ +static snd_pcm_uframes_t +snd_ca0106_pcm_pointer_capture(struct snd_pcm_substream *substream) +{ + struct snd_ca0106 *emu = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ca0106_pcm *epcm = runtime->private_data; + snd_pcm_uframes_t ptr, ptr1, ptr2 = 0; + int channel = channel=epcm->channel_id; + + if (!epcm->running) + return 0; + + ptr1 = snd_ca0106_ptr_read(emu, CAPTURE_POINTER, channel); + ptr2 = bytes_to_frames(runtime, ptr1); + ptr=ptr2; + if (ptr >= runtime->buffer_size) + ptr -= runtime->buffer_size; + //printk("ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n", ptr1, ptr2, ptr, (int)runtime->buffer_size, (int)runtime->period_size, (int)runtime->frame_bits, (int)runtime->rate); + + return ptr; +} + +/* operators */ +static struct snd_pcm_ops snd_ca0106_playback_front_ops = { + .open = snd_ca0106_pcm_open_playback_front, + .close = snd_ca0106_pcm_close_playback, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ca0106_pcm_hw_params_playback, + .hw_free = snd_ca0106_pcm_hw_free_playback, + .prepare = snd_ca0106_pcm_prepare_playback, + .trigger = snd_ca0106_pcm_trigger_playback, + .pointer = snd_ca0106_pcm_pointer_playback, +}; + +static struct snd_pcm_ops snd_ca0106_capture_0_ops = { + .open = snd_ca0106_pcm_open_0_capture, + .close = snd_ca0106_pcm_close_capture, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ca0106_pcm_hw_params_capture, + .hw_free = snd_ca0106_pcm_hw_free_capture, + .prepare = snd_ca0106_pcm_prepare_capture, + .trigger = snd_ca0106_pcm_trigger_capture, + .pointer = snd_ca0106_pcm_pointer_capture, +}; + +static struct snd_pcm_ops snd_ca0106_capture_1_ops = { + .open = snd_ca0106_pcm_open_1_capture, + .close = snd_ca0106_pcm_close_capture, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ca0106_pcm_hw_params_capture, + .hw_free = snd_ca0106_pcm_hw_free_capture, + .prepare = snd_ca0106_pcm_prepare_capture, + .trigger = snd_ca0106_pcm_trigger_capture, + .pointer = snd_ca0106_pcm_pointer_capture, +}; + +static struct snd_pcm_ops snd_ca0106_capture_2_ops = { + .open = snd_ca0106_pcm_open_2_capture, + .close = snd_ca0106_pcm_close_capture, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ca0106_pcm_hw_params_capture, + .hw_free = snd_ca0106_pcm_hw_free_capture, + .prepare = snd_ca0106_pcm_prepare_capture, + .trigger = snd_ca0106_pcm_trigger_capture, + .pointer = snd_ca0106_pcm_pointer_capture, +}; + +static struct snd_pcm_ops snd_ca0106_capture_3_ops = { + .open = snd_ca0106_pcm_open_3_capture, + .close = snd_ca0106_pcm_close_capture, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ca0106_pcm_hw_params_capture, + .hw_free = snd_ca0106_pcm_hw_free_capture, + .prepare = snd_ca0106_pcm_prepare_capture, + .trigger = snd_ca0106_pcm_trigger_capture, + .pointer = snd_ca0106_pcm_pointer_capture, +}; + +static struct snd_pcm_ops snd_ca0106_playback_center_lfe_ops = { + .open = snd_ca0106_pcm_open_playback_center_lfe, + .close = snd_ca0106_pcm_close_playback, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ca0106_pcm_hw_params_playback, + .hw_free = snd_ca0106_pcm_hw_free_playback, + .prepare = snd_ca0106_pcm_prepare_playback, + .trigger = snd_ca0106_pcm_trigger_playback, + .pointer = snd_ca0106_pcm_pointer_playback, +}; + +static struct snd_pcm_ops snd_ca0106_playback_unknown_ops = { + .open = snd_ca0106_pcm_open_playback_unknown, + .close = snd_ca0106_pcm_close_playback, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ca0106_pcm_hw_params_playback, + .hw_free = snd_ca0106_pcm_hw_free_playback, + .prepare = snd_ca0106_pcm_prepare_playback, + .trigger = snd_ca0106_pcm_trigger_playback, + .pointer = snd_ca0106_pcm_pointer_playback, +}; + +static struct snd_pcm_ops snd_ca0106_playback_rear_ops = { + .open = snd_ca0106_pcm_open_playback_rear, + .close = snd_ca0106_pcm_close_playback, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ca0106_pcm_hw_params_playback, + .hw_free = snd_ca0106_pcm_hw_free_playback, + .prepare = snd_ca0106_pcm_prepare_playback, + .trigger = snd_ca0106_pcm_trigger_playback, + .pointer = snd_ca0106_pcm_pointer_playback, +}; + + +static unsigned short snd_ca0106_ac97_read(struct snd_ac97 *ac97, + unsigned short reg) +{ + struct snd_ca0106 *emu = ac97->private_data; + unsigned long flags; + unsigned short val; + + spin_lock_irqsave(&emu->emu_lock, flags); + outb(reg, emu->port + AC97ADDRESS); + val = inw(emu->port + AC97DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); + return val; +} + +static void snd_ca0106_ac97_write(struct snd_ac97 *ac97, + unsigned short reg, unsigned short val) +{ + struct snd_ca0106 *emu = ac97->private_data; + unsigned long flags; + + spin_lock_irqsave(&emu->emu_lock, flags); + outb(reg, emu->port + AC97ADDRESS); + outw(val, emu->port + AC97DATA); + spin_unlock_irqrestore(&emu->emu_lock, flags); +} + +static int snd_ca0106_ac97(struct snd_ca0106 *chip) +{ + struct snd_ac97_bus *pbus; + struct snd_ac97_template ac97; + int err; + static struct snd_ac97_bus_ops ops = { + .write = snd_ca0106_ac97_write, + .read = snd_ca0106_ac97_read, + }; + + if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0) + return err; + pbus->no_vra = 1; /* we don't need VRA */ + + memset(&ac97, 0, sizeof(ac97)); + ac97.private_data = chip; + ac97.scaps = AC97_SCAP_NO_SPDIF; + return snd_ac97_mixer(pbus, &ac97, &chip->ac97); +} + +static int snd_ca0106_free(struct snd_ca0106 *chip) +{ + if (chip->res_port != NULL) { /* avoid access to already used hardware */ + // disable interrupts + snd_ca0106_ptr_write(chip, BASIC_INTERRUPT, 0, 0); + outl(0, chip->port + INTE); + snd_ca0106_ptr_write(chip, EXTENDED_INT_MASK, 0, 0); + udelay(1000); + // disable audio + //outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG); + outl(0, chip->port + HCFG); + /* FIXME: We need to stop and DMA transfers here. + * But as I am not sure how yet, we cannot from the dma pages. + * So we can fix: snd-malloc: Memory leak? pages not freed = 8 + */ + } + // release the data +#if 1 + if (chip->buffer.area) + snd_dma_free_pages(&chip->buffer); +#endif + + // release the i/o port + release_and_free_resource(chip->res_port); + + // release the irq + if (chip->irq >= 0) + free_irq(chip->irq, chip); + pci_disable_device(chip->pci); + kfree(chip); + return 0; +} + +static int snd_ca0106_dev_free(struct snd_device *device) +{ + struct snd_ca0106 *chip = device->device_data; + return snd_ca0106_free(chip); +} + +static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id) +{ + unsigned int status; + + struct snd_ca0106 *chip = dev_id; + int i; + int mask; + unsigned int stat76; + struct snd_ca0106_channel *pchannel; + + status = inl(chip->port + IPR); + if (! status) + return IRQ_NONE; + + stat76 = snd_ca0106_ptr_read(chip, EXTENDED_INT, 0); + //snd_printk("interrupt status = 0x%08x, stat76=0x%08x\n", status, stat76); + //snd_printk("ptr=0x%08x\n",snd_ca0106_ptr_read(chip, PLAYBACK_POINTER, 0)); + mask = 0x11; /* 0x1 for one half, 0x10 for the other half period. */ + for(i = 0; i < 4; i++) { + pchannel = &(chip->playback_channels[i]); + if (stat76 & mask) { +/* FIXME: Select the correct substream for period elapsed */ + if(pchannel->use) { + snd_pcm_period_elapsed(pchannel->epcm->substream); + //printk(KERN_INFO "interrupt [%d] used\n", i); + } + } + //printk(KERN_INFO "channel=%p\n",pchannel); + //printk(KERN_INFO "interrupt stat76[%d] = %08x, use=%d, channel=%d\n", i, stat76, pchannel->use, pchannel->number); + mask <<= 1; + } + mask = 0x110000; /* 0x1 for one half, 0x10 for the other half period. */ + for(i = 0; i < 4; i++) { + pchannel = &(chip->capture_channels[i]); + if (stat76 & mask) { +/* FIXME: Select the correct substream for period elapsed */ + if(pchannel->use) { + snd_pcm_period_elapsed(pchannel->epcm->substream); + //printk(KERN_INFO "interrupt [%d] used\n", i); + } + } + //printk(KERN_INFO "channel=%p\n",pchannel); + //printk(KERN_INFO "interrupt stat76[%d] = %08x, use=%d, channel=%d\n", i, stat76, pchannel->use, pchannel->number); + mask <<= 1; + } + + snd_ca0106_ptr_write(chip, EXTENDED_INT, 0, stat76); + + if (chip->midi.dev_id && + (status & (chip->midi.ipr_tx|chip->midi.ipr_rx))) { + if (chip->midi.interrupt) + chip->midi.interrupt(&chip->midi, status); + else + chip->midi.interrupt_disable(&chip->midi, chip->midi.tx_enable | chip->midi.rx_enable); + } + + // acknowledge the interrupt if necessary + outl(status, chip->port+IPR); + + return IRQ_HANDLED; +} + +static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device, struct snd_pcm **rpcm) +{ + struct snd_pcm *pcm; + struct snd_pcm_substream *substream; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(emu->card, "ca0106", device, 1, 1, &pcm)) < 0) + return err; + + pcm->private_data = emu; + + switch (device) { + case 0: + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_front_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_0_ops); + break; + case 1: + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_rear_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_1_ops); + break; + case 2: + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_center_lfe_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_2_ops); + break; + case 3: + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_unknown_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_3_ops); + break; + } + + pcm->info_flags = 0; + pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; + strcpy(pcm->name, "CA0106"); + emu->pcm = pcm; + + for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + substream; + substream = substream->next) { + if ((err = snd_pcm_lib_preallocate_pages(substream, + SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(emu->pci), + 64*1024, 64*1024)) < 0) /* FIXME: 32*1024 for sound buffer, between 32and64 for Periods table. */ + return err; + } + + for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + substream; + substream = substream->next) { + if ((err = snd_pcm_lib_preallocate_pages(substream, + SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(emu->pci), + 64*1024, 64*1024)) < 0) + return err; + } + + if (rpcm) + *rpcm = pcm; + + return 0; +} + +#define SPI_REG(reg, value) (((reg) << SPI_REG_SHIFT) | (value)) +static unsigned int spi_dac_init[] = { + SPI_REG(SPI_LDA1_REG, SPI_DA_BIT_0dB), /* 0dB dig. attenuation */ + SPI_REG(SPI_RDA1_REG, SPI_DA_BIT_0dB), + SPI_REG(SPI_PL_REG, SPI_PL_BIT_L_L | SPI_PL_BIT_R_R | SPI_IZD_BIT), + SPI_REG(SPI_FMT_REG, SPI_FMT_BIT_I2S | SPI_IWL_BIT_24), + SPI_REG(SPI_LDA2_REG, SPI_DA_BIT_0dB), + SPI_REG(SPI_RDA2_REG, SPI_DA_BIT_0dB), + SPI_REG(SPI_LDA3_REG, SPI_DA_BIT_0dB), + SPI_REG(SPI_RDA3_REG, SPI_DA_BIT_0dB), + SPI_REG(SPI_MASTDA_REG, SPI_DA_BIT_0dB), + SPI_REG(9, 0x00), + SPI_REG(SPI_MS_REG, SPI_DACD0_BIT | SPI_DACD1_BIT | SPI_DACD2_BIT), + SPI_REG(12, 0x00), + SPI_REG(SPI_LDA4_REG, SPI_DA_BIT_0dB), + SPI_REG(SPI_RDA4_REG, SPI_DA_BIT_0dB | SPI_DA_BIT_UPDATE), + SPI_REG(SPI_DACD4_REG, 0x00), +}; + +static unsigned int i2c_adc_init[][2] = { + { 0x17, 0x00 }, /* Reset */ + { 0x07, 0x00 }, /* Timeout */ + { 0x0b, 0x22 }, /* Interface control */ + { 0x0c, 0x22 }, /* Master mode control */ + { 0x0d, 0x08 }, /* Powerdown control */ + { 0x0e, 0xcf }, /* Attenuation Left 0x01 = -103dB, 0xff = 24dB */ + { 0x0f, 0xcf }, /* Attenuation Right 0.5dB steps */ + { 0x10, 0x7b }, /* ALC Control 1 */ + { 0x11, 0x00 }, /* ALC Control 2 */ + { 0x12, 0x32 }, /* ALC Control 3 */ + { 0x13, 0x00 }, /* Noise gate control */ + { 0x14, 0xa6 }, /* Limiter control */ + { 0x15, ADC_MUX_LINEIN }, /* ADC Mixer control */ +}; + +static int __devinit snd_ca0106_create(int dev, struct snd_card *card, + struct pci_dev *pci, + struct snd_ca0106 **rchip) +{ + struct snd_ca0106 *chip; + struct snd_ca0106_details *c; + int err; + int ch; + static struct snd_device_ops ops = { + .dev_free = snd_ca0106_dev_free, + }; + + *rchip = NULL; + + if ((err = pci_enable_device(pci)) < 0) + return err; + if (pci_set_dma_mask(pci, DMA_32BIT_MASK) < 0 || + pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK) < 0) { + printk(KERN_ERR "error to set 32bit mask DMA\n"); + pci_disable_device(pci); + return -ENXIO; + } + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (chip == NULL) { + pci_disable_device(pci); + return -ENOMEM; + } + + chip->card = card; + chip->pci = pci; + chip->irq = -1; + + spin_lock_init(&chip->emu_lock); + + chip->port = pci_resource_start(pci, 0); + if ((chip->res_port = request_region(chip->port, 0x20, + "snd_ca0106")) == NULL) { + snd_ca0106_free(chip); + printk(KERN_ERR "cannot allocate the port\n"); + return -EBUSY; + } + + if (request_irq(pci->irq, snd_ca0106_interrupt, + IRQF_SHARED, "snd_ca0106", chip)) { + snd_ca0106_free(chip); + printk(KERN_ERR "cannot grab irq\n"); + return -EBUSY; + } + chip->irq = pci->irq; + + /* This stores the periods table. */ + if(snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), 1024, &chip->buffer) < 0) { + snd_ca0106_free(chip); + return -ENOMEM; + } + + pci_set_master(pci); + /* read serial */ + pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial); + pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model); +#if 1 + printk(KERN_INFO "snd-ca0106: Model %04x Rev %08x Serial %08x\n", chip->model, + pci->revision, chip->serial); +#endif + strcpy(card->driver, "CA0106"); + strcpy(card->shortname, "CA0106"); + + for (c = ca0106_chip_details; c->serial; c++) { + if (subsystem[dev]) { + if (c->serial == subsystem[dev]) + break; + } else if (c->serial == chip->serial) + break; + } + chip->details = c; + if (subsystem[dev]) { + printk(KERN_INFO "snd-ca0106: Sound card name=%s, subsystem=0x%x. Forced to subsystem=0x%x\n", + c->name, chip->serial, subsystem[dev]); + } + + sprintf(card->longname, "%s at 0x%lx irq %i", + c->name, chip->port, chip->irq); + + outl(0, chip->port + INTE); + + /* + * Init to 0x02109204 : + * Clock accuracy = 0 (1000ppm) + * Sample Rate = 2 (48kHz) + * Audio Channel = 1 (Left of 2) + * Source Number = 0 (Unspecified) + * Generation Status = 1 (Original for Cat Code 12) + * Cat Code = 12 (Digital Signal Mixer) + * Mode = 0 (Mode 0) + * Emphasis = 0 (None) + * CP = 1 (Copyright unasserted) + * AN = 0 (Audio data) + * P = 0 (Consumer) + */ + snd_ca0106_ptr_write(chip, SPCS0, 0, + chip->spdif_bits[0] = + SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | + SPCS_GENERATIONSTATUS | 0x00001200 | + 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); + /* Only SPCS1 has been tested */ + snd_ca0106_ptr_write(chip, SPCS1, 0, + chip->spdif_bits[1] = + SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | + SPCS_GENERATIONSTATUS | 0x00001200 | + 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); + snd_ca0106_ptr_write(chip, SPCS2, 0, + chip->spdif_bits[2] = + SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | + SPCS_GENERATIONSTATUS | 0x00001200 | + 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); + snd_ca0106_ptr_write(chip, SPCS3, 0, + chip->spdif_bits[3] = + SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | + SPCS_GENERATIONSTATUS | 0x00001200 | + 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); + + snd_ca0106_ptr_write(chip, PLAYBACK_MUTE, 0, 0x00fc0000); + snd_ca0106_ptr_write(chip, CAPTURE_MUTE, 0, 0x00fc0000); + + /* Write 0x8000 to AC97_REC_GAIN to mute it. */ + outb(AC97_REC_GAIN, chip->port + AC97ADDRESS); + outw(0x8000, chip->port + AC97DATA); +#if 0 + snd_ca0106_ptr_write(chip, SPCS0, 0, 0x2108006); + snd_ca0106_ptr_write(chip, 0x42, 0, 0x2108006); + snd_ca0106_ptr_write(chip, 0x43, 0, 0x2108006); + snd_ca0106_ptr_write(chip, 0x44, 0, 0x2108006); +#endif + + //snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0xf0f003f); /* OSS drivers set this. */ + /* Analog or Digital output */ + snd_ca0106_ptr_write(chip, SPDIF_SELECT1, 0, 0xf); + snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0x000f0000); /* 0x0b000000 for digital, 0x000b0000 for analog, from win2000 drivers. Use 0x000f0000 for surround71 */ + chip->spdif_enable = 0; /* Set digital SPDIF output off */ + //snd_ca0106_ptr_write(chip, 0x45, 0, 0); /* Analogue out */ + //snd_ca0106_ptr_write(chip, 0x45, 0, 0xf00); /* Digital out */ + + snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 0, 0x40c81000); /* goes to 0x40c80000 when doing SPDIF IN/OUT */ + snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 1, 0xffffffff); /* (Mute) CAPTURE feedback into PLAYBACK volume. Only lower 16 bits matter. */ + snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 2, 0x30300000); /* SPDIF IN Volume */ + snd_ca0106_ptr_write(chip, CAPTURE_CONTROL, 3, 0x00700000); /* SPDIF IN Volume, 0x70 = (vol & 0x3f) | 0x40 */ + snd_ca0106_ptr_write(chip, PLAYBACK_ROUTING1, 0, 0x32765410); + snd_ca0106_ptr_write(chip, PLAYBACK_ROUTING2, 0, 0x76767676); + snd_ca0106_ptr_write(chip, CAPTURE_ROUTING1, 0, 0x32765410); + snd_ca0106_ptr_write(chip, CAPTURE_ROUTING2, 0, 0x76767676); + for(ch = 0; ch < 4; ch++) { + snd_ca0106_ptr_write(chip, CAPTURE_VOLUME1, ch, 0x30303030); /* Only high 16 bits matter */ + snd_ca0106_ptr_write(chip, CAPTURE_VOLUME2, ch, 0x30303030); + //snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0x40404040); /* Mute */ + //snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0x40404040); /* Mute */ + snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME1, ch, 0xffffffff); /* Mute */ + snd_ca0106_ptr_write(chip, PLAYBACK_VOLUME2, ch, 0xffffffff); /* Mute */ + } + if (chip->details->i2c_adc == 1) { + /* Select MIC, Line in, TAD in, AUX in */ + snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4); + /* Default to CAPTURE_SOURCE to i2s in */ + chip->capture_source = 3; + } else if (chip->details->ac97 == 1) { + /* Default to AC97 in */ + snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x444400e4); + /* Default to CAPTURE_SOURCE to AC97 in */ + chip->capture_source = 4; + } else { + /* Select MIC, Line in, TAD in, AUX in */ + snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4); + /* Default to Set CAPTURE_SOURCE to i2s in */ + chip->capture_source = 3; + } + + if (chip->details->gpio_type == 2) { /* The SB0438 use GPIO differently. */ + /* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */ + outl(0x0, chip->port+GPIO); + //outl(0x00f0e000, chip->port+GPIO); /* Analog */ + outl(0x005f5301, chip->port+GPIO); /* Analog */ + } else if (chip->details->gpio_type == 1) { /* The SB0410 and SB0413 use GPIO differently. */ + /* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */ + outl(0x0, chip->port+GPIO); + //outl(0x00f0e000, chip->port+GPIO); /* Analog */ + outl(0x005f5301, chip->port+GPIO); /* Analog */ + } else { + outl(0x0, chip->port+GPIO); + outl(0x005f03a3, chip->port+GPIO); /* Analog */ + //outl(0x005f02a2, chip->port+GPIO); /* SPDIF */ + } + snd_ca0106_intr_enable(chip, 0x105); /* Win2000 uses 0x1e0 */ + + //outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG); + //outl(0x00001409, chip->port+HCFG); /* 0x1000 causes AC3 to fails. Maybe it effects 24 bit output. */ + //outl(0x00000009, chip->port+HCFG); + outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG); /* AC97 2.0, Enable outputs. */ + + if (chip->details->i2c_adc == 1) { /* The SB0410 and SB0413 use I2C to control ADC. */ + int size, n; + + size = ARRAY_SIZE(i2c_adc_init); + //snd_printk("I2C:array size=0x%x\n", size); + for (n=0; n < size; n++) { + snd_ca0106_i2c_write(chip, i2c_adc_init[n][0], i2c_adc_init[n][1]); + } + for (n=0; n < 4; n++) { + chip->i2c_capture_volume[n][0]= 0xcf; + chip->i2c_capture_volume[n][1]= 0xcf; + } + chip->i2c_capture_source=2; /* Line in */ + //snd_ca0106_i2c_write(chip, ADC_MUX, ADC_MUX_LINEIN); /* Enable Line-in capture. MIC in currently untested. */ + } + if (chip->details->spi_dac == 1) { /* The SB0570 use SPI to control DAC. */ + int size, n; + + size = ARRAY_SIZE(spi_dac_init); + for (n = 0; n < size; n++) { + int reg = spi_dac_init[n] >> SPI_REG_SHIFT; + + snd_ca0106_spi_write(chip, spi_dac_init[n]); + if (reg < ARRAY_SIZE(chip->spi_dac_reg)) + chip->spi_dac_reg[reg] = spi_dac_init[n]; + } + } + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, + chip, &ops)) < 0) { + snd_ca0106_free(chip); + return err; + } + *rchip = chip; + return 0; +} + + +static void ca0106_midi_interrupt_enable(struct snd_ca_midi *midi, int intr) +{ + snd_ca0106_intr_enable((struct snd_ca0106 *)(midi->dev_id), intr); +} + +static void ca0106_midi_interrupt_disable(struct snd_ca_midi *midi, int intr) +{ + snd_ca0106_intr_disable((struct snd_ca0106 *)(midi->dev_id), intr); +} + +static unsigned char ca0106_midi_read(struct snd_ca_midi *midi, int idx) +{ + return (unsigned char)snd_ca0106_ptr_read((struct snd_ca0106 *)(midi->dev_id), + midi->port + idx, 0); +} + +static void ca0106_midi_write(struct snd_ca_midi *midi, int data, int idx) +{ + snd_ca0106_ptr_write((struct snd_ca0106 *)(midi->dev_id), midi->port + idx, 0, data); +} + +static struct snd_card *ca0106_dev_id_card(void *dev_id) +{ + return ((struct snd_ca0106 *)dev_id)->card; +} + +static int ca0106_dev_id_port(void *dev_id) +{ + return ((struct snd_ca0106 *)dev_id)->port; +} + +static int __devinit snd_ca0106_midi(struct snd_ca0106 *chip, unsigned int channel) +{ + struct snd_ca_midi *midi; + char *name; + int err; + + if (channel == CA0106_MIDI_CHAN_B) { + name = "CA0106 MPU-401 (UART) B"; + midi = &chip->midi2; + midi->tx_enable = INTE_MIDI_TX_B; + midi->rx_enable = INTE_MIDI_RX_B; + midi->ipr_tx = IPR_MIDI_TX_B; + midi->ipr_rx = IPR_MIDI_RX_B; + midi->port = MIDI_UART_B_DATA; + } else { + name = "CA0106 MPU-401 (UART)"; + midi = &chip->midi; + midi->tx_enable = INTE_MIDI_TX_A; + midi->rx_enable = INTE_MIDI_TX_B; + midi->ipr_tx = IPR_MIDI_TX_A; + midi->ipr_rx = IPR_MIDI_RX_A; + midi->port = MIDI_UART_A_DATA; + } + + midi->reset = CA0106_MPU401_RESET; + midi->enter_uart = CA0106_MPU401_ENTER_UART; + midi->ack = CA0106_MPU401_ACK; + + midi->input_avail = CA0106_MIDI_INPUT_AVAIL; + midi->output_ready = CA0106_MIDI_OUTPUT_READY; + + midi->channel = channel; + + midi->interrupt_enable = ca0106_midi_interrupt_enable; + midi->interrupt_disable = ca0106_midi_interrupt_disable; + + midi->read = ca0106_midi_read; + midi->write = ca0106_midi_write; + + midi->get_dev_id_card = ca0106_dev_id_card; + midi->get_dev_id_port = ca0106_dev_id_port; + + midi->dev_id = chip; + + if ((err = ca_midi_init(chip, midi, 0, name)) < 0) + return err; + + return 0; +} + + +static int __devinit snd_ca0106_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + struct snd_card *card; + struct snd_ca0106 *chip; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_ca0106_create(dev, card, pci, &chip)) < 0) { + snd_card_free(card); + return err; + } + + if ((err = snd_ca0106_pcm(chip, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ca0106_pcm(chip, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ca0106_pcm(chip, 2, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ca0106_pcm(chip, 3, NULL)) < 0) { + snd_card_free(card); + return err; + } + if (chip->details->ac97 == 1) { /* The SB0410 and SB0413 do not have an AC97 chip. */ + if ((err = snd_ca0106_ac97(chip)) < 0) { + snd_card_free(card); + return err; + } + } + if ((err = snd_ca0106_mixer(chip)) < 0) { + snd_card_free(card); + return err; + } + + snd_printdd("ca0106: probe for MIDI channel A ..."); + if ((err = snd_ca0106_midi(chip,CA0106_MIDI_CHAN_A)) < 0) { + snd_card_free(card); + snd_printdd(" failed, err=0x%x\n",err); + return err; + } + snd_printdd(" done.\n"); + +#ifdef CONFIG_PROC_FS + snd_ca0106_proc_init(chip); +#endif + + snd_card_set_dev(card, &pci->dev); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_ca0106_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +// PCI IDs +static struct pci_device_id snd_ca0106_ids[] = { + { 0x1102, 0x0007, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Audigy LS or Live 24bit */ + { 0, } +}; +MODULE_DEVICE_TABLE(pci, snd_ca0106_ids); + +// pci_driver definition +static struct pci_driver driver = { + .name = "CA0106", + .id_table = snd_ca0106_ids, + .probe = snd_ca0106_probe, + .remove = __devexit_p(snd_ca0106_remove), +}; + +// initialization of the module +static int __init alsa_card_ca0106_init(void) +{ + return pci_register_driver(&driver); +} + +// clean up the module +static void __exit alsa_card_ca0106_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_ca0106_init) +module_exit(alsa_card_ca0106_exit) diff -Nur sound/pci/ca0106/ca0106_mixer.c sound/pci/ca0106/ca0106_mixer.c --- sound/pci/ca0106/ca0106_mixer.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/ca0106/ca0106_mixer.c 2007-07-26 02:00:06.000000000 +0200 @@ -1,7 +1,7 @@ /* * Copyright (c) 2004 James Courtier-Dutton * Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit - * Version: 0.0.17 + * Version: 0.0.18 * * FEATURES currently supported: * See ca0106_main.c for features. @@ -39,6 +39,8 @@ * Modified Copyright message. * 0.0.17 * Implement Mic and Line in Capture. + * 0.0.18 + * Add support for mute control on SB Live 24bit (cards w/ SPI DAC) * * This code was initally based on code from ALSA's emu10k1x.c which is: * Copyright (c) by Francisco Moraes @@ -77,15 +79,7 @@ static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1); static const DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1); -static int snd_ca0106_shared_spdif_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_ca0106_shared_spdif_info snd_ctl_boolean_mono_info static int snd_ca0106_shared_spdif_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -470,6 +464,42 @@ return change; } +#define spi_mute_info snd_ctl_boolean_mono_info + +static int spi_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); + unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT; + unsigned int bit = kcontrol->private_value & SPI_REG_MASK; + + ucontrol->value.integer.value[0] = !(emu->spi_dac_reg[reg] & bit); + return 0; +} + +static int spi_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); + unsigned int reg = kcontrol->private_value >> SPI_REG_SHIFT; + unsigned int bit = kcontrol->private_value & SPI_REG_MASK; + int ret; + + ret = emu->spi_dac_reg[reg] & bit; + if (ucontrol->value.integer.value[0]) { + if (!ret) /* bit already cleared, do nothing */ + return 0; + emu->spi_dac_reg[reg] &= ~bit; + } else { + if (ret) /* bit already set, do nothing */ + return 0; + emu->spi_dac_reg[reg] |= bit; + } + + ret = snd_ca0106_spi_write(emu, emu->spi_dac_reg[reg]); + return ret ? -1 : 1; +} + #define CA_VOLUME(xname,chid,reg) \ { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ @@ -562,6 +592,28 @@ I2C_VOLUME("Aux Capture Volume", 3), }; +#define SPI_SWITCH(xname,reg,bit) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .info = spi_mute_info, \ + .get = spi_mute_get, \ + .put = spi_mute_put, \ + .private_value = (reg<card; char **c; static char *ca0106_remove_ctls[] = { @@ -640,17 +702,9 @@ rename_ctl(card, c[0], c[1]); #endif - for (i = 0; i < ARRAY_SIZE(snd_ca0106_volume_ctls); i++) { - err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_volume_ctls[i], emu)); - if (err < 0) - return err; - } + ADD_CTLS(emu, snd_ca0106_volume_ctls); if (emu->details->i2c_adc == 1) { - for (i = 0; i < ARRAY_SIZE(snd_ca0106_volume_i2c_adc_ctls); i++) { - err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_volume_i2c_adc_ctls[i], emu)); - if (err < 0) - return err; - } + ADD_CTLS(emu, snd_ca0106_volume_i2c_adc_ctls); if (emu->details->gpio_type == 1) err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu)); else /* gpio_type == 2 */ @@ -658,6 +712,8 @@ if (err < 0) return err; } + if (emu->details->spi_dac == 1) + ADD_CTLS(emu, snd_ca0106_volume_spi_dac_ctls); return 0; } diff -Nur sound/pci/ca0106/ca_midi.h sound/pci/ca0106/ca_midi.h --- sound/pci/ca0106/ca_midi.h 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/ca0106/ca_midi.h 2007-08-03 02:00:09.000000000 +0200 @@ -22,9 +22,9 @@ * */ -#include -#include -#include +#include +#include +#include #define CA_MIDI_MODE_INPUT MPU401_MODE_INPUT #define CA_MIDI_MODE_OUTPUT MPU401_MODE_OUTPUT diff -Nur sound/pci/cmipci.c sound/pci/cmipci.c --- sound/pci/cmipci.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/cmipci.c 2007-07-24 02:00:10.000000000 +0200 @@ -2139,15 +2139,7 @@ */ }; -static int snd_cmipci_uswitch_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_cmipci_uswitch_info snd_ctl_boolean_mono_info static int _snd_cmipci_uswitch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol, diff -Nur sound/pci/cs4281.c sound/pci/cs4281.c --- sound/pci/cs4281.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/cs4281.c 2007-08-14 02:00:08.000000000 +0200 @@ -842,12 +842,11 @@ static struct snd_pcm_hardware snd_cs4281_playback = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_RESUME | - SNDRV_PCM_INFO_SYNC_START), + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE | @@ -868,12 +867,11 @@ static struct snd_pcm_hardware snd_cs4281_capture = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_RESUME | - SNDRV_PCM_INFO_SYNC_START), + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE | @@ -904,7 +902,6 @@ dma->right_slot = 1; runtime->private_data = dma; runtime->hw = snd_cs4281_playback; - snd_pcm_set_sync(substream); /* should be detected from the AC'97 layer, but it seems that although CS4297A rev B reports 18-bit ADC resolution, samples are 20-bit */ @@ -924,7 +921,6 @@ dma->right_slot = 11; runtime->private_data = dma; runtime->hw = snd_cs4281_capture; - snd_pcm_set_sync(substream); /* should be detected from the AC'97 layer, but it seems that although CS4297A rev B reports 18-bit ADC resolution, samples are 20-bit */ diff -Nur sound/pci/cs46xx/Makefile sound/pci/cs46xx/Makefile --- sound/pci/cs46xx/Makefile 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/cs46xx/Makefile 2007-07-28 02:00:08.000000000 +0200 @@ -3,10 +3,8 @@ # Copyright (c) 2001 by Jaroslav Kysela # -snd-cs46xx-objs := cs46xx.o cs46xx_lib.o -ifeq ($(CONFIG_SND_CS46XX_NEW_DSP),y) - snd-cs46xx-objs += dsp_spos.o dsp_spos_scb_lib.o -endif +snd-cs46xx-y := cs46xx.o cs46xx_lib.o +snd-cs46xx-$(CONFIG_SND_CS46XX_NEW_DSP) += dsp_spos.o dsp_spos_scb_lib.o # Toplevel Module Dependency obj-$(CONFIG_SND_CS46XX) += snd-cs46xx.o diff -Nur sound/pci/cs46xx/cs46xx_lib.c sound/pci/cs46xx/cs46xx_lib.c --- sound/pci/cs46xx/cs46xx_lib.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/cs46xx/cs46xx_lib.c 2007-07-24 02:00:10.000000000 +0200 @@ -1818,15 +1818,7 @@ } #endif -static int snd_mixer_boolean_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_mixer_boolean_info snd_ctl_boolean_mono_info static int snd_cs46xx_iec958_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2897,6 +2889,10 @@ } #endif +#ifdef CONFIG_PM + kfree(chip->saved_regs); +#endif + pci_disable_device(chip->pci); kfree(chip); return 0; @@ -3140,6 +3136,23 @@ /* * start and load DSP */ + +static void cs46xx_enable_stream_irqs(struct snd_cs46xx *chip) +{ + unsigned int tmp; + + snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_IEV | HICR_CHGM); + + tmp = snd_cs46xx_peek(chip, BA1_PFIE); + tmp &= ~0x0000f03f; + snd_cs46xx_poke(chip, BA1_PFIE, tmp); /* playback interrupt enable */ + + tmp = snd_cs46xx_peek(chip, BA1_CIE); + tmp &= ~0x0000003f; + tmp |= 0x00000001; + snd_cs46xx_poke(chip, BA1_CIE, tmp); /* capture interrupt enable */ +} + int __devinit snd_cs46xx_start_dsp(struct snd_cs46xx *chip) { unsigned int tmp; @@ -3214,19 +3227,7 @@ snd_cs46xx_proc_start(chip); - /* - * Enable interrupts on the part. - */ - snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_IEV | HICR_CHGM); - - tmp = snd_cs46xx_peek(chip, BA1_PFIE); - tmp &= ~0x0000f03f; - snd_cs46xx_poke(chip, BA1_PFIE, tmp); /* playback interrupt enable */ - - tmp = snd_cs46xx_peek(chip, BA1_CIE); - tmp &= ~0x0000003f; - tmp |= 0x00000001; - snd_cs46xx_poke(chip, BA1_CIE, tmp); /* capture interrupt enable */ + cs46xx_enable_stream_irqs(chip); #ifndef CONFIG_SND_CS46XX_NEW_DSP /* set the attenuation to 0dB */ @@ -3665,11 +3666,19 @@ * APM support */ #ifdef CONFIG_PM +static unsigned int saved_regs[] = { + BA0_ACOSV, + BA0_ASER_FADDR, + BA0_ASER_MASTER, + BA1_PVOL, + BA1_CVOL, +}; + int snd_cs46xx_suspend(struct pci_dev *pci, pm_message_t state) { struct snd_card *card = pci_get_drvdata(pci); struct snd_cs46xx *chip = card->private_data; - int amp_saved; + int i, amp_saved; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); chip->in_suspend = 1; @@ -3680,6 +3689,10 @@ snd_ac97_suspend(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]); snd_ac97_suspend(chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]); + /* save some registers */ + for (i = 0; i < ARRAY_SIZE(saved_regs); i++) + chip->saved_regs[i] = snd_cs46xx_peekBA0(chip, saved_regs[i]); + amp_saved = chip->amplifier; /* turn off amp */ chip->amplifier_ctrl(chip, -chip->amplifier); @@ -3698,7 +3711,7 @@ { struct snd_card *card = pci_get_drvdata(pci); struct snd_cs46xx *chip = card->private_data; - int amp_saved; + int i, amp_saved; pci_set_power_state(pci, PCI_D0); pci_restore_state(pci); @@ -3716,6 +3729,16 @@ snd_cs46xx_chip_init(chip); + snd_cs46xx_reset(chip); +#ifdef CONFIG_SND_CS46XX_NEW_DSP + cs46xx_dsp_resume(chip); + /* restore some registers */ + for (i = 0; i < ARRAY_SIZE(saved_regs); i++) + snd_cs46xx_pokeBA0(chip, saved_regs[i], chip->saved_regs[i]); +#else + snd_cs46xx_download_image(chip); +#endif + #if 0 snd_cs46xx_codec_write(chip, BA0_AC97_GENERAL_PURPOSE, chip->ac97_general_purpose); @@ -3730,6 +3753,13 @@ snd_ac97_resume(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]); snd_ac97_resume(chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]); + /* reset playback/capture */ + snd_cs46xx_set_play_sample_rate(chip, 8000); + snd_cs46xx_set_capture_sample_rate(chip, 8000); + snd_cs46xx_proc_start(chip); + + cs46xx_enable_stream_irqs(chip); + if (amp_saved) chip->amplifier_ctrl(chip, 1); /* turn amp on */ else @@ -3896,6 +3926,15 @@ snd_cs46xx_proc_init(card, chip); +#ifdef CONFIG_PM + chip->saved_regs = kmalloc(sizeof(*chip->saved_regs) * + ARRAY_SIZE(saved_regs), GFP_KERNEL); + if (!chip->saved_regs) { + snd_cs46xx_free(chip); + return -ENOMEM; + } +#endif + chip->active_ctrl(chip, -1); /* disable CLKRUN */ snd_card_set_dev(card, &pci->dev); diff -Nur sound/pci/cs46xx/cs46xx_lib.h sound/pci/cs46xx/cs46xx_lib.h --- sound/pci/cs46xx/cs46xx_lib.h 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/cs46xx/cs46xx_lib.h 2007-07-20 02:00:08.000000000 +0200 @@ -86,6 +86,9 @@ struct dsp_spos_instance *cs46xx_dsp_spos_create (struct snd_cs46xx * chip); void cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip); int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * module); +#ifdef CONFIG_PM +int cs46xx_dsp_resume(struct snd_cs46xx * chip); +#endif struct dsp_symbol_entry *cs46xx_dsp_lookup_symbol (struct snd_cs46xx * chip, char * symbol_name, int symbol_type); #ifdef CONFIG_PROC_FS diff -Nur sound/pci/cs46xx/dsp_spos.c sound/pci/cs46xx/dsp_spos.c --- sound/pci/cs46xx/dsp_spos.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/cs46xx/dsp_spos.c 2007-07-20 02:00:08.000000000 +0200 @@ -306,13 +306,59 @@ mutex_unlock(&chip->spos_mutex); } +static int dsp_load_parameter(struct snd_cs46xx *chip, + struct dsp_segment_desc *parameter) +{ + u32 doffset, dsize; + + if (!parameter) { + snd_printdd("dsp_spos: module got no parameter segment\n"); + return 0; + } + + doffset = (parameter->offset * 4 + DSP_PARAMETER_BYTE_OFFSET); + dsize = parameter->size * 4; + + snd_printdd("dsp_spos: " + "downloading parameter data to chip (%08x-%08x)\n", + doffset,doffset + dsize); + if (snd_cs46xx_download (chip, parameter->data, doffset, dsize)) { + snd_printk(KERN_ERR "dsp_spos: " + "failed to download parameter data to DSP\n"); + return -EINVAL; + } + return 0; +} + +static int dsp_load_sample(struct snd_cs46xx *chip, + struct dsp_segment_desc *sample) +{ + u32 doffset, dsize; + + if (!sample) { + snd_printdd("dsp_spos: module got no sample segment\n"); + return 0; + } + + doffset = (sample->offset * 4 + DSP_SAMPLE_BYTE_OFFSET); + dsize = sample->size * 4; + + snd_printdd("dsp_spos: downloading sample data to chip (%08x-%08x)\n", + doffset,doffset + dsize); + + if (snd_cs46xx_download (chip,sample->data,doffset,dsize)) { + snd_printk(KERN_ERR "dsp_spos: failed to sample data to DSP\n"); + return -EINVAL; + } + return 0; +} + int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * module) { struct dsp_spos_instance * ins = chip->dsp_spos_instance; struct dsp_segment_desc * code = get_segment_desc (module,SEGTYPE_SP_PROGRAM); - struct dsp_segment_desc * parameter = get_segment_desc (module,SEGTYPE_SP_PARAMETER); - struct dsp_segment_desc * sample = get_segment_desc (module,SEGTYPE_SP_SAMPLE); u32 doffset, dsize; + int err; if (ins->nmodules == DSP_MAX_MODULES - 1) { snd_printk(KERN_ERR "dsp_spos: to many modules loaded into DSP\n"); @@ -326,49 +372,20 @@ snd_cs46xx_clear_BA1(chip, DSP_PARAMETER_BYTE_OFFSET, DSP_PARAMETER_BYTE_SIZE); } - if (parameter == NULL) { - snd_printdd("dsp_spos: module got no parameter segment\n"); - } else { - if (ins->nmodules > 0) { - snd_printk(KERN_WARNING "dsp_spos: WARNING current parameter data may be overwriten!\n"); - } - - doffset = (parameter->offset * 4 + DSP_PARAMETER_BYTE_OFFSET); - dsize = parameter->size * 4; - - snd_printdd("dsp_spos: downloading parameter data to chip (%08x-%08x)\n", - doffset,doffset + dsize); - - if (snd_cs46xx_download (chip, parameter->data, doffset, dsize)) { - snd_printk(KERN_ERR "dsp_spos: failed to download parameter data to DSP\n"); - return -EINVAL; - } - } + err = dsp_load_parameter(chip, get_segment_desc(module, + SEGTYPE_SP_PARAMETER)); + if (err < 0) + return err; if (ins->nmodules == 0) { snd_printdd("dsp_spos: clearing sample area\n"); snd_cs46xx_clear_BA1(chip, DSP_SAMPLE_BYTE_OFFSET, DSP_SAMPLE_BYTE_SIZE); } - if (sample == NULL) { - snd_printdd("dsp_spos: module got no sample segment\n"); - } else { - if (ins->nmodules > 0) { - snd_printk(KERN_WARNING "dsp_spos: WARNING current sample data may be overwriten\n"); - } - - doffset = (sample->offset * 4 + DSP_SAMPLE_BYTE_OFFSET); - dsize = sample->size * 4; - - snd_printdd("dsp_spos: downloading sample data to chip (%08x-%08x)\n", - doffset,doffset + dsize); - - if (snd_cs46xx_download (chip,sample->data,doffset,dsize)) { - snd_printk(KERN_ERR "dsp_spos: failed to sample data to DSP\n"); - return -EINVAL; - } - } - + err = dsp_load_sample(chip, get_segment_desc(module, + SEGTYPE_SP_SAMPLE)); + if (err < 0) + return err; if (ins->nmodules == 0) { snd_printdd("dsp_spos: clearing code area\n"); @@ -986,7 +1003,10 @@ return NULL; } - strcpy(ins->tasks[ins->ntask].task_name,name); + if (name) + strcpy(ins->tasks[ins->ntask].task_name, name); + else + strcpy(ins->tasks[ins->ntask].task_name, "(NULL)"); ins->tasks[ins->ntask].address = dest; ins->tasks[ins->ntask].size = size; @@ -995,7 +1015,8 @@ desc = (ins->tasks + ins->ntask); ins->ntask++; - add_symbol (chip,name,dest,SYMBOL_PARAMETER); + if (name) + add_symbol (chip,name,dest,SYMBOL_PARAMETER); return desc; } @@ -1006,6 +1027,7 @@ desc = _map_scb (chip,name,dest); if (desc) { + desc->data = scb_data; _dsp_create_scb(chip,scb_data,dest); } else { snd_printk(KERN_ERR "dsp_spos: failed to map SCB\n"); @@ -1023,6 +1045,7 @@ desc = _map_task_tree (chip,name,dest,size); if (desc) { + desc->data = task_data; _dsp_create_task_tree(chip,task_data,dest,size); } else { snd_printk(KERN_ERR "dsp_spos: failed to map TASK\n"); @@ -1320,8 +1343,10 @@ 0x0000ffff }; - /* dirty hack ... */ - _dsp_create_task_tree (chip,(u32 *)&mix2_ostream_spb,WRITE_BACK_SPB,2); + if (!cs46xx_dsp_create_task_tree(chip, NULL, + (u32 *)&mix2_ostream_spb, + WRITE_BACK_SPB, 2)) + goto _fail_end; } /* input sample converter */ @@ -1622,7 +1647,6 @@ return 0; } - static void cs46xx_dsp_disable_spdif_hw (struct snd_cs46xx *chip) { struct dsp_spos_instance * ins = chip->dsp_spos_instance; @@ -1894,3 +1918,61 @@ return 0; } + +#ifdef CONFIG_PM +int cs46xx_dsp_resume(struct snd_cs46xx * chip) +{ + struct dsp_spos_instance * ins = chip->dsp_spos_instance; + int i, err; + + /* clear parameter, sample and code areas */ + snd_cs46xx_clear_BA1(chip, DSP_PARAMETER_BYTE_OFFSET, + DSP_PARAMETER_BYTE_SIZE); + snd_cs46xx_clear_BA1(chip, DSP_SAMPLE_BYTE_OFFSET, + DSP_SAMPLE_BYTE_SIZE); + snd_cs46xx_clear_BA1(chip, DSP_CODE_BYTE_OFFSET, DSP_CODE_BYTE_SIZE); + + for (i = 0; i < ins->nmodules; i++) { + struct dsp_module_desc *module = &ins->modules[i]; + struct dsp_segment_desc *seg; + u32 doffset, dsize; + + seg = get_segment_desc(module, SEGTYPE_SP_PARAMETER); + err = dsp_load_parameter(chip, seg); + if (err < 0) + return err; + + seg = get_segment_desc(module, SEGTYPE_SP_SAMPLE); + err = dsp_load_sample(chip, seg); + if (err < 0) + return err; + + seg = get_segment_desc(module, SEGTYPE_SP_PROGRAM); + if (!seg) + continue; + + doffset = seg->offset * 4 + module->load_address * 4 + + DSP_CODE_BYTE_OFFSET; + dsize = seg->size * 4; + err = snd_cs46xx_download(chip, + ins->code.data + module->load_address, + doffset, dsize); + if (err < 0) + return err; + } + + for (i = 0; i < ins->ntask; i++) { + struct dsp_task_descriptor *t = &ins->tasks[i]; + _dsp_create_task_tree(chip, t->data, t->address, t->size); + } + + for (i = 0; i < ins->nscb; i++) { + struct dsp_scb_descriptor *s = &ins->scbs[i]; + if (s->deleted) + continue; + _dsp_create_scb(chip, s->data, s->address); + } + + return 0; +} +#endif diff -Nur sound/pci/cs46xx/dsp_spos_scb_lib.c sound/pci/cs46xx/dsp_spos_scb_lib.c --- sound/pci/cs46xx/dsp_spos_scb_lib.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/cs46xx/dsp_spos_scb_lib.c 2007-07-25 02:00:06.000000000 +0200 @@ -1480,7 +1480,7 @@ if (!pcm_channel->src_scb->ref_count) { cs46xx_dsp_remove_scb(chip,pcm_channel->src_scb); - snd_assert (pcm_channel->src_slot >= 0 && pcm_channel->src_slot <= DSP_MAX_SRC_NR, + snd_assert (pcm_channel->src_slot >= 0 && pcm_channel->src_slot < DSP_MAX_SRC_NR, return ); ins->src_scb_slots[pcm_channel->src_slot] = 0; diff -Nur sound/pci/cs5530.c sound/pci/cs5530.c --- sound/pci/cs5530.c 1970-01-01 01:00:00.000000000 +0100 +++ sound/pci/cs5530.c 2007-05-25 02:00:06.000000000 +0200 @@ -0,0 +1,306 @@ +/* + * cs5530.c - Initialisation code for Cyrix/NatSemi VSA1 softaudio + * + * (C) Copyright 2007 Ash Willis + * (C) Copyright 2003 Red Hat Inc + * + * This driver was ported (shamelessly ripped ;) from oss/kahlua.c but I did + * mess with it a bit. The chip seems to have to have trouble with full duplex + * mode. If we're recording in 8bit 8000kHz, say, and we then attempt to + * simultaneously play back audio at 16bit 44100kHz, the device actually plays + * back in the same format in which it is capturing. By forcing the chip to + * always play/capture in 16/44100, we can let alsa-lib convert the samples and + * that way we can hack up some full duplex audio. + * + * XpressAudio(tm) is used on the Cyrix MediaGX (now NatSemi Geode) systems. + * The older version (VSA1) provides fairly good soundblaster emulation + * although there are a couple of bugs: large DMA buffers break record, + * and the MPU event handling seems suspect. VSA2 allows the native driver + * to control the AC97 audio engine directly and requires a different driver. + * + * Thanks to National Semiconductor for providing the needed information + * on the XpressAudio(tm) internals. + * + * 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, 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. + * + * TO DO: + * Investigate whether we can portably support Cognac (5520) in the + * same manner. + */ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Ash Willis"); +MODULE_DESCRIPTION("CS5530 Audio"); +MODULE_LICENSE("GPL"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; + +struct snd_cs5530 { + struct snd_card *card; + struct pci_dev *pci; + struct snd_sb *sb; + unsigned long pci_base; +}; + +static struct pci_device_id snd_cs5530_ids[] = { + {PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_AUDIO, PCI_ANY_ID, + PCI_ANY_ID, 0, 0}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, snd_cs5530_ids); + +static int snd_cs5530_free(struct snd_cs5530 *chip) +{ + pci_release_regions(chip->pci); + pci_disable_device(chip->pci); + kfree(chip); + return 0; +} + +static int snd_cs5530_dev_free(struct snd_device *device) +{ + struct snd_cs5530 *chip = device->device_data; + return snd_cs5530_free(chip); +} + +static void __devexit snd_cs5530_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static u8 __devinit snd_cs5530_mixer_read(unsigned long io, u8 reg) +{ + outb(reg, io + 4); + udelay(20); + reg = inb(io + 5); + udelay(20); + return reg; +} + +static int __devinit snd_cs5530_create(struct snd_card *card, + struct pci_dev *pci, + struct snd_cs5530 **rchip) +{ + struct snd_cs5530 *chip; + unsigned long sb_base; + u8 irq, dma8, dma16 = 0; + u16 map; + void __iomem *mem; + int err; + + static struct snd_device_ops ops = { + .dev_free = snd_cs5530_dev_free, + }; + *rchip = NULL; + + err = pci_enable_device(pci); + if (err < 0) + return err; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (chip == NULL) { + pci_disable_device(pci); + return -ENOMEM; + } + + chip->card = card; + chip->pci = pci; + + err = pci_request_regions(pci, "CS5530"); + if (err < 0) { + kfree(chip); + pci_disable_device(pci); + return err; + } + chip->pci_base = pci_resource_start(pci, 0); + + mem = ioremap_nocache(chip->pci_base, pci_resource_len(pci, 0)); + if (mem == NULL) { + kfree(chip); + pci_disable_device(pci); + return -EBUSY; + } + + map = readw(mem + 0x18); + iounmap(mem); + + /* Map bits + 0:1 * 0x20 + 0x200 = sb base + 2 sb enable + 3 adlib enable + 5 MPU enable 0x330 + 6 MPU enable 0x300 + + The other bits may be used internally so must be masked */ + + sb_base = 0x220 + 0x20 * (map & 3); + + if (map & (1<<2)) + printk(KERN_INFO "CS5530: XpressAudio at 0x%lx\n", sb_base); + else { + printk(KERN_ERR "Could not find XpressAudio!\n"); + snd_cs5530_free(chip); + return -ENODEV; + } + + if (map & (1<<5)) + printk(KERN_INFO "CS5530: MPU at 0x300\n"); + else if (map & (1<<6)) + printk(KERN_INFO "CS5530: MPU at 0x330\n"); + + irq = snd_cs5530_mixer_read(sb_base, 0x80) & 0x0F; + dma8 = snd_cs5530_mixer_read(sb_base, 0x81); + + if (dma8 & 0x20) + dma16 = 5; + else if (dma8 & 0x40) + dma16 = 6; + else if (dma8 & 0x80) + dma16 = 7; + else { + printk(KERN_ERR "CS5530: No 16bit DMA enabled\n"); + snd_cs5530_free(chip); + return -ENODEV; + } + + if (dma8 & 0x01) + dma8 = 0; + else if (dma8 & 02) + dma8 = 1; + else if (dma8 & 0x08) + dma8 = 3; + else { + printk(KERN_ERR "CS5530: No 8bit DMA enabled\n"); + snd_cs5530_free(chip); + return -ENODEV; + } + + if (irq & 1) + irq = 9; + else if (irq & 2) + irq = 5; + else if (irq & 4) + irq = 7; + else if (irq & 8) + irq = 10; + else { + printk(KERN_ERR "CS5530: SoundBlaster IRQ not set\n"); + snd_cs5530_free(chip); + return -ENODEV; + } + + printk(KERN_INFO "CS5530: IRQ: %d DMA8: %d DMA16: %d\n", irq, dma8, + dma16); + + err = snd_sbdsp_create(card, sb_base, irq, snd_sb16dsp_interrupt, dma8, + dma16, SB_HW_CS5530, &chip->sb); + if (err < 0) { + printk(KERN_ERR "CS5530: Could not create SoundBlaster\n"); + snd_cs5530_free(chip); + return err; + } + + err = snd_sb16dsp_pcm(chip->sb, 0, &chip->sb->pcm); + if (err < 0) { + printk(KERN_ERR "CS5530: Could not create PCM\n"); + snd_cs5530_free(chip); + return err; + } + + err = snd_sbmixer_new(chip->sb); + if (err < 0) { + printk(KERN_ERR "CS5530: Could not create Mixer\n"); + snd_cs5530_free(chip); + return err; + } + + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); + if (err < 0) { + snd_cs5530_free(chip); + return err; + } + + snd_card_set_dev(card, &pci->dev); + *rchip = chip; + return 0; +} + +static int __devinit snd_cs5530_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + struct snd_card *card; + struct snd_cs5530 *chip = NULL; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + + if (card == NULL) + return -ENOMEM; + + err = snd_cs5530_create(card, pci, &chip); + if (err < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, "CS5530"); + strcpy(card->shortname, "CS5530 Audio"); + sprintf(card->longname, "%s at 0x%lx", card->shortname, chip->pci_base); + + err = snd_card_register(card); + if (err < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static struct pci_driver driver = { + .name = "CS5530_Audio", + .id_table = snd_cs5530_ids, + .probe = snd_cs5530_probe, + .remove = __devexit_p(snd_cs5530_remove), +}; + +static int __init alsa_card_cs5530_init(void) +{ + return pci_register_driver(&driver); +} + +static void __exit alsa_card_cs5530_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_cs5530_init) +module_exit(alsa_card_cs5530_exit) + diff -Nur sound/pci/cs5535audio/Makefile sound/pci/cs5535audio/Makefile --- sound/pci/cs5535audio/Makefile 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/cs5535audio/Makefile 2007-07-28 02:00:08.000000000 +0200 @@ -2,11 +2,8 @@ # Makefile for cs5535audio # -snd-cs5535audio-objs := cs5535audio.o cs5535audio_pcm.o - -ifeq ($(CONFIG_PM),y) -snd-cs5535audio-objs += cs5535audio_pm.o -endif +snd-cs5535audio-y := cs5535audio.o cs5535audio_pcm.o +snd-cs5535audio-$(CONFIG_PM) += cs5535audio_pm.o # Toplevel Module Dependency obj-$(CONFIG_SND_CS5535AUDIO) += snd-cs5535audio.o diff -Nur sound/pci/cs5535audio/cs5535audio_pcm.c sound/pci/cs5535audio/cs5535audio_pcm.c --- sound/pci/cs5535audio/cs5535audio_pcm.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/cs5535audio/cs5535audio_pcm.c 2007-08-14 02:00:08.000000000 +0200 @@ -43,7 +43,6 @@ SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_SYNC_START | SNDRV_PCM_INFO_RESUME ), .formats = ( @@ -71,8 +70,7 @@ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_SYNC_START + SNDRV_PCM_INFO_MMAP_VALID ), .formats = ( SNDRV_PCM_FMTBIT_S16_LE @@ -102,7 +100,6 @@ runtime->hw = snd_cs5535audio_playback; cs5535au->playback_substream = substream; runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_PLAYBACK]); - snd_pcm_set_sync(substream); if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) return err; @@ -348,7 +345,6 @@ runtime->hw = snd_cs5535audio_capture; cs5535au->capture_substream = substream; runtime->private_data = &(cs5535au->dmas[CS5535AUDIO_DMA_CAPTURE]); - snd_pcm_set_sync(substream); if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) return err; diff -Nur sound/pci/echoaudio/echoaudio.c sound/pci/echoaudio/echoaudio.c --- sound/pci/echoaudio/echoaudio.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/echoaudio/echoaudio.c 2007-07-24 02:00:10.000000000 +0200 @@ -1595,15 +1595,7 @@ #ifdef ECHOCARD_HAS_PHANTOM_POWER /******************* Phantom power switch *******************/ -static int snd_echo_phantom_power_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_echo_phantom_power_info snd_ctl_boolean_mono_info static int snd_echo_phantom_power_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1646,15 +1638,7 @@ #ifdef ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE /******************* Digital input automute switch *******************/ -static int snd_echo_automute_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_echo_automute_info snd_ctl_boolean_mono_info static int snd_echo_automute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1695,18 +1679,7 @@ /******************* VU-meters switch *******************/ -static int snd_echo_vumeters_switch_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct echoaudio *chip; - - chip = snd_kcontrol_chip(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_echo_vumeters_switch_info snd_ctl_boolean_mono_info static int snd_echo_vumeters_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff -Nur sound/pci/emu10k1/emu10k1_main.c sound/pci/emu10k1/emu10k1_main.c --- sound/pci/emu10k1/emu10k1_main.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/emu10k1/emu10k1_main.c 2007-08-21 17:37:19.000000000 +0200 @@ -31,6 +31,8 @@ * */ +#include +#include #include #include #include @@ -51,9 +53,15 @@ #define HANA_FILENAME "emu/hana.fw" #define DOCK_FILENAME "emu/audio_dock.fw" +#define EMU1010B_FILENAME "emu/emu1010b.fw" +#define MICRO_DOCK_FILENAME "emu/micro_dock.fw" +#define EMU1010_NOTEBOOK_FILENAME "emu/emu1010_notebook.fw" MODULE_FIRMWARE(HANA_FILENAME); MODULE_FIRMWARE(DOCK_FILENAME); +MODULE_FIRMWARE(EMU1010B_FILENAME); +MODULE_FIRMWARE(MICRO_DOCK_FILENAME); +MODULE_FIRMWARE(EMU1010_NOTEBOOK_FILENAME); /************************************************************************* @@ -660,10 +668,12 @@ return err; } snd_printk(KERN_INFO "firmware size=0x%zx\n", fw_entry->size); +#if 0 if (fw_entry->size != 0x133a4) { snd_printk(KERN_ERR "firmware: %s wrong size.\n",filename); return -EINVAL; } +#endif /* The FPGA is a Xilinx Spartan IIE XC2S50E */ /* GPIO7 -> FPGA PGMN @@ -694,6 +704,96 @@ return 0; } +int emu1010_firmware_thread(void *data) { + struct snd_emu10k1 * emu = data; + int tmp,tmp2; + int reg; + int err; + + for (;;) { + /* Delay to allow Audio Dock to settle */ + msleep(1000); + if (kthread_should_stop()) + break; + snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp ); /* IRQ Status */ + snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, ® ); /* OPTIONS: Which cards are attached to the EMU */ + if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) { + /* Audio Dock attached */ + /* Return to Audio Dock programming mode */ + snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware\n"); + snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_AUDIODOCK ); + if (emu->card_capabilities->emu1010 == 1) { + if ((err = snd_emu1010_load_firmware(emu, DOCK_FILENAME)) != 0) { + return err; + } + } else if (emu->card_capabilities->emu1010 == 2) { + if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) { + return err; + } + } else if (emu->card_capabilities->emu1010 == 3) { + if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) { + return err; + } + } + + snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0 ); + snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, ® ); + snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_IRQ_STATUS=0x%x\n",reg); + /* ID, should read & 0x7f = 0x55 when FPGA programmed. */ + snd_emu1010_fpga_read(emu, EMU_HANA_ID, ® ); + snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_ID=0x%x\n",reg); + if ((reg & 0x1f) != 0x15) { + /* FPGA failed to be programmed */ + snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware file failed, reg=0x%x\n", reg); + return 0; + return -ENODEV; + } + snd_printk(KERN_INFO "emu1010: Audio Dock Firmware loaded\n"); + snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp ); + snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2 ); + snd_printk("Audio Dock ver:%d.%d\n",tmp ,tmp2); + /* Sync clocking between 1010 and Dock */ + /* Allow DLL to settle */ + msleep(10); + /* Unmute all. Default is muted after a firmware load */ + snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE ); + break; + } + } + return 0; +} + +/* + * EMU-1010 - details found out from this driver, official MS Win drivers, + * testing the card: + * + * Audigy2 (aka Alice2): + * --------------------- + * * communication over PCI + * * conversion of 32-bit data coming over EMU32 links from HANA FPGA + * to 2 x 16-bit, using internal DSP instructions + * * slave mode, clock supplied by HANA + * * linked to HANA using: + * 32 x 32-bit serial EMU32 output channels + * 16 x EMU32 input channels + * (?) x I2S I/O channels (?) + * + * FPGA (aka HANA): + * --------------- + * * provides all (?) physical inputs and outputs of the card + * (ADC, DAC, SPDIF I/O, ADAT I/O, etc.) + * * provides clock signal for the card and Alice2 + * * two crystals - for 44.1kHz and 48kHz multiples + * * provides internal routing of signal sources to signal destinations + * * inputs/outputs to Alice2 - see above + * + * Current status of the driver: + * ---------------------------- + * * only 44.1/48kHz supported (the MS Win driver supports up to 192 kHz) + * * PCM device nb. 2: + * 16 x 16-bit playback - snd_emu10k1_fx8010_playback_ops + * 16 x 32-bit capture - snd_emu10k1_capture_efx_ops + */ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu) { unsigned int i; @@ -727,7 +827,7 @@ /* ID, should read & 0x7f = 0x55. (Bit 7 is the IRQ bit) */ snd_emu1010_fpga_read(emu, EMU_HANA_ID, ® ); snd_printdd("reg1=0x%x\n",reg); - if (reg == 0x55) { + if ((reg & 0x3f) == 0x15) { /* FPGA netlist already present so clear it */ /* Return to programming mode */ @@ -735,19 +835,32 @@ } snd_emu1010_fpga_read(emu, EMU_HANA_ID, ® ); snd_printdd("reg2=0x%x\n",reg); - if (reg == 0x55) { + if ((reg & 0x3f) == 0x15) { /* FPGA failed to return to programming mode */ + snd_printk(KERN_INFO "emu1010: FPGA failed to return to programming mode\n"); return -ENODEV; } snd_printk(KERN_INFO "emu1010: EMU_HANA_ID=0x%x\n",reg); - if ((err = snd_emu1010_load_firmware(emu, HANA_FILENAME)) != 0) { - snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file %s failed\n", HANA_FILENAME); - return err; + if (emu->card_capabilities->emu1010 == 1) { + if ((err = snd_emu1010_load_firmware(emu, HANA_FILENAME)) != 0) { + snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file %s failed\n", HANA_FILENAME); + return err; + } + } else if (emu->card_capabilities->emu1010 == 2) { + if ((err = snd_emu1010_load_firmware(emu, EMU1010B_FILENAME)) != 0) { + snd_printk(KERN_INFO "emu1010: Loading Firmware file %s failed\n", EMU1010B_FILENAME); + return err; + } + } else if (emu->card_capabilities->emu1010 == 3) { + if ((err = snd_emu1010_load_firmware(emu, EMU1010_NOTEBOOK_FILENAME)) != 0) { + snd_printk(KERN_INFO "emu1010: Loading Firmware file %s failed\n", EMU1010_NOTEBOOK_FILENAME); + return err; + } } /* ID, should read & 0x7f = 0x55 when FPGA programmed. */ snd_emu1010_fpga_read(emu, EMU_HANA_ID, ® ); - if (reg != 0x55) { + if ((reg & 0x3f) != 0x15) { /* FPGA failed to be programmed */ snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file failed, reg=0x%x\n", reg); return -ENODEV; @@ -765,8 +878,16 @@ snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, ® ); snd_printk(KERN_INFO "emu1010: Card options=0x%x\n",reg); snd_emu1010_fpga_read(emu, EMU_HANA_OPTICAL_TYPE, &tmp ); - /* ADAT input. */ - snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x01 ); + /* Optical -> ADAT I/O */ + /* 0 : SPDIF + * 1 : ADAT + */ + emu->emu1010.optical_in = 1; /* IN_ADAT */ + emu->emu1010.optical_out = 1; /* IN_ADAT */ + tmp = 0; + tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : 0) | + (emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : 0); + snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp ); snd_emu1010_fpga_read(emu, EMU_HANA_ADC_PADS, &tmp ); /* Set no attenuation on Audio Dock pads. */ snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, 0x00 ); @@ -850,6 +971,27 @@ EMU_DST_ALICE2_EMU32_6, EMU_SRC_DOCK_ADC2_LEFT1); snd_emu1010_fpga_link_dst_src_write(emu, EMU_DST_ALICE2_EMU32_7, EMU_SRC_DOCK_ADC2_RIGHT1); + /* Pavel Hofman - setting defaults for 8 more capture channels + * Defaults only, users will set their own values anyways, let's + * just copy/paste. + */ + + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_8, EMU_SRC_DOCK_MIC_A1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_9, EMU_SRC_DOCK_MIC_B1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_A, EMU_SRC_HAMOA_ADC_LEFT2); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_B, EMU_SRC_HAMOA_ADC_LEFT2); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_C, EMU_SRC_DOCK_ADC1_LEFT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_D, EMU_SRC_DOCK_ADC1_RIGHT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_E, EMU_SRC_DOCK_ADC2_LEFT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_F, EMU_SRC_DOCK_ADC2_RIGHT1); #endif #if 0 /* Original */ @@ -931,38 +1073,12 @@ snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp ); snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10 ); /* SPDIF Format spdif (or 0x11 for aes/ebu) */ - /* Delay to allow Audio Dock to settle */ - msleep(100); - snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp ); /* IRQ Status */ - snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, ® ); /* OPTIONS: Which cards are attached to the EMU */ - /* FIXME: The loading of this should be able to happen any time, - * as the user can plug/unplug it at any time - */ - if (reg & (EMU_HANA_OPTION_DOCK_ONLINE | EMU_HANA_OPTION_DOCK_OFFLINE) ) { - /* Audio Dock attached */ - /* Return to Audio Dock programming mode */ - snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware\n"); - snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_AUDIODOCK ); - if ((err = snd_emu1010_load_firmware(emu, DOCK_FILENAME)) != 0) { - return err; - } - snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0 ); - snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, ® ); - snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_IRQ_STATUS=0x%x\n",reg); - /* ID, should read & 0x7f = 0x55 when FPGA programmed. */ - snd_emu1010_fpga_read(emu, EMU_HANA_ID, ® ); - snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_ID=0x%x\n",reg); - if (reg != 0x55) { - /* FPGA failed to be programmed */ - snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware file failed, reg=0x%x\n", reg); - return 0; - return -ENODEV; - } - snd_printk(KERN_INFO "emu1010: Audio Dock Firmware loaded\n"); - snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp ); - snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2 ); - snd_printk("Audio Dock ver:%d.%d\n",tmp ,tmp2); - } + /* Start Micro/Audio Dock firmware loader thread */ + emu->emu1010.firmware_thread = kthread_create(&emu1010_firmware_thread, + emu, + "emu1010_firmware"); + wake_up_process(emu->emu1010.firmware_thread); + #if 0 snd_emu1010_fpga_link_dst_src_write(emu, EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32B + 2); /* ALICE2 bus 0xa2 */ @@ -1048,7 +1164,7 @@ emu->emu1010.output_source[23] = 28; /* TEMP: Select SPDIF in/out */ - snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x0); /* Output spdif */ + //snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x0); /* Output spdif */ /* TEMP: Select 48kHz SPDIF out */ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x0); /* Mute all */ @@ -1089,6 +1205,7 @@ if (emu->card_capabilities->emu1010) { /* Disable 48Volt power to Audio Dock */ snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0 ); + kthread_stop(emu->emu1010.firmware_thread); } if (emu->memhdr) snd_util_memhdr_free(emu->memhdr); @@ -1227,9 +1344,15 @@ .emu10k2_chip = 1, .ca0108_chip = 1, .ca_cardbus_chip = 1, - .spi_dac = 1, - .i2c_adc = 1, - .spk71 = 1} , + .spk71 = 1 , + .emu1010 = 3} , + {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40041102, + .driver = "Audigy2", .name = "E-mu 1010b PCI [MAEM????]", + .id = "EMU1010", + .emu10k2_chip = 1, + .ca0108_chip = 1, + .spk71 = 1 , + .emu1010 = 2} , {.vendor = 0x1102, .device = 0x0008, .driver = "Audigy2", .name = "Audigy 2 Value [Unknown]", .id = "Audigy2", @@ -1665,12 +1788,13 @@ emu->fx8010.extout_mask = extout_mask; emu->enable_ir = enable_ir; + if (emu->card_capabilities->ca_cardbus_chip) { + if ((err = snd_emu10k1_cardbus_init(emu)) < 0) + goto error; + } if (emu->card_capabilities->ecard) { if ((err = snd_emu10k1_ecard_init(emu)) < 0) goto error; - } else if (emu->card_capabilities->ca_cardbus_chip) { - if ((err = snd_emu10k1_cardbus_init(emu)) < 0) - goto error; } else if (emu->card_capabilities->emu1010) { if ((err = snd_emu10k1_emu1010_init(emu)) < 0) { snd_emu10k1_free(emu); @@ -1816,10 +1940,10 @@ void snd_emu10k1_resume_init(struct snd_emu10k1 *emu) { + if (emu->card_capabilities->ca_cardbus_chip) + snd_emu10k1_cardbus_init(emu); if (emu->card_capabilities->ecard) snd_emu10k1_ecard_init(emu); - else if (emu->card_capabilities->ca_cardbus_chip) - snd_emu10k1_cardbus_init(emu); else if (emu->card_capabilities->emu1010) snd_emu10k1_emu1010_init(emu); else diff -Nur sound/pci/emu10k1/emu10k1_main.c~ sound/pci/emu10k1/emu10k1_main.c~ --- sound/pci/emu10k1/emu10k1_main.c~ 1970-01-01 01:00:00.000000000 +0100 +++ sound/pci/emu10k1/emu10k1_main.c~ 2007-07-27 02:00:07.000000000 +0200 @@ -0,0 +1,1975 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Creative Labs, Inc. + * Routines for control of EMU10K1 chips + * + * Copyright (c) by James Courtier-Dutton + * Added support for Audigy 2 Value. + * Added EMU 1010 support. + * General bug fixes and enhancements. + * + * + * BUGS: + * -- + * + * TODO: + * -- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include "p16v.h" +#include "tina2.h" +#include "p17v.h" + + +#define HANA_FILENAME "emu/hana.fw" +#define DOCK_FILENAME "emu/audio_dock.fw" +#define EMU1010B_FILENAME "emu/emu1010b.fw" +#define MICRO_DOCK_FILENAME "emu/micro_dock.fw" +#define EMU1010_NOTEBOOK_FILENAME "emu/emu1010_notebook.fw" + +MODULE_FIRMWARE(HANA_FILENAME); +MODULE_FIRMWARE(DOCK_FILENAME); +MODULE_FIRMWARE(EMU1010B_FILENAME); +MODULE_FIRMWARE(MICRO_DOCK_FILENAME); +MODULE_FIRMWARE(EMU1010_NOTEBOOK_FILENAME); + + +/************************************************************************* + * EMU10K1 init / done + *************************************************************************/ + +void snd_emu10k1_voice_init(struct snd_emu10k1 * emu, int ch) +{ + snd_emu10k1_ptr_write(emu, DCYSUSV, ch, 0); + snd_emu10k1_ptr_write(emu, IP, ch, 0); + snd_emu10k1_ptr_write(emu, VTFT, ch, 0xffff); + snd_emu10k1_ptr_write(emu, CVCF, ch, 0xffff); + snd_emu10k1_ptr_write(emu, PTRX, ch, 0); + snd_emu10k1_ptr_write(emu, CPF, ch, 0); + snd_emu10k1_ptr_write(emu, CCR, ch, 0); + + snd_emu10k1_ptr_write(emu, PSST, ch, 0); + snd_emu10k1_ptr_write(emu, DSL, ch, 0x10); + snd_emu10k1_ptr_write(emu, CCCA, ch, 0); + snd_emu10k1_ptr_write(emu, Z1, ch, 0); + snd_emu10k1_ptr_write(emu, Z2, ch, 0); + snd_emu10k1_ptr_write(emu, FXRT, ch, 0x32100000); + + snd_emu10k1_ptr_write(emu, ATKHLDM, ch, 0); + snd_emu10k1_ptr_write(emu, DCYSUSM, ch, 0); + snd_emu10k1_ptr_write(emu, IFATN, ch, 0xffff); + snd_emu10k1_ptr_write(emu, PEFE, ch, 0); + snd_emu10k1_ptr_write(emu, FMMOD, ch, 0); + snd_emu10k1_ptr_write(emu, TREMFRQ, ch, 24); /* 1 Hz */ + snd_emu10k1_ptr_write(emu, FM2FRQ2, ch, 24); /* 1 Hz */ + snd_emu10k1_ptr_write(emu, TEMPENV, ch, 0); + + /*** these are last so OFF prevents writing ***/ + snd_emu10k1_ptr_write(emu, LFOVAL2, ch, 0); + snd_emu10k1_ptr_write(emu, LFOVAL1, ch, 0); + snd_emu10k1_ptr_write(emu, ATKHLDV, ch, 0); + snd_emu10k1_ptr_write(emu, ENVVOL, ch, 0); + snd_emu10k1_ptr_write(emu, ENVVAL, ch, 0); + + /* Audigy extra stuffs */ + if (emu->audigy) { + snd_emu10k1_ptr_write(emu, 0x4c, ch, 0); /* ?? */ + snd_emu10k1_ptr_write(emu, 0x4d, ch, 0); /* ?? */ + snd_emu10k1_ptr_write(emu, 0x4e, ch, 0); /* ?? */ + snd_emu10k1_ptr_write(emu, 0x4f, ch, 0); /* ?? */ + snd_emu10k1_ptr_write(emu, A_FXRT1, ch, 0x03020100); + snd_emu10k1_ptr_write(emu, A_FXRT2, ch, 0x3f3f3f3f); + snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, ch, 0); + } +} + +static unsigned int spi_dac_init[] = { + 0x00ff, + 0x02ff, + 0x0400, + 0x0520, + 0x0600, + 0x08ff, + 0x0aff, + 0x0cff, + 0x0eff, + 0x10ff, + 0x1200, + 0x1400, + 0x1480, + 0x1800, + 0x1aff, + 0x1cff, + 0x1e00, + 0x0530, + 0x0602, + 0x0622, + 0x1400, +}; + +static unsigned int i2c_adc_init[][2] = { + { 0x17, 0x00 }, /* Reset */ + { 0x07, 0x00 }, /* Timeout */ + { 0x0b, 0x22 }, /* Interface control */ + { 0x0c, 0x22 }, /* Master mode control */ + { 0x0d, 0x08 }, /* Powerdown control */ + { 0x0e, 0xcf }, /* Attenuation Left 0x01 = -103dB, 0xff = 24dB */ + { 0x0f, 0xcf }, /* Attenuation Right 0.5dB steps */ + { 0x10, 0x7b }, /* ALC Control 1 */ + { 0x11, 0x00 }, /* ALC Control 2 */ + { 0x12, 0x32 }, /* ALC Control 3 */ + { 0x13, 0x00 }, /* Noise gate control */ + { 0x14, 0xa6 }, /* Limiter control */ + { 0x15, ADC_MUX_2 }, /* ADC Mixer control. Mic for Audigy 2 ZS Notebook */ +}; + +static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume) +{ + unsigned int silent_page; + int ch; + u32 tmp; + + /* disable audio and lock cache */ + outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, + emu->port + HCFG); + + /* reset recording buffers */ + snd_emu10k1_ptr_write(emu, MICBS, 0, ADCBS_BUFSIZE_NONE); + snd_emu10k1_ptr_write(emu, MICBA, 0, 0); + snd_emu10k1_ptr_write(emu, FXBS, 0, ADCBS_BUFSIZE_NONE); + snd_emu10k1_ptr_write(emu, FXBA, 0, 0); + snd_emu10k1_ptr_write(emu, ADCBS, 0, ADCBS_BUFSIZE_NONE); + snd_emu10k1_ptr_write(emu, ADCBA, 0, 0); + + /* disable channel interrupt */ + outl(0, emu->port + INTE); + snd_emu10k1_ptr_write(emu, CLIEL, 0, 0); + snd_emu10k1_ptr_write(emu, CLIEH, 0, 0); + snd_emu10k1_ptr_write(emu, SOLEL, 0, 0); + snd_emu10k1_ptr_write(emu, SOLEH, 0, 0); + + if (emu->audigy){ + /* set SPDIF bypass mode */ + snd_emu10k1_ptr_write(emu, SPBYPASS, 0, SPBYPASS_FORMAT); + /* enable rear left + rear right AC97 slots */ + snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_REAR_RIGHT | + AC97SLOT_REAR_LEFT); + } + + /* init envelope engine */ + for (ch = 0; ch < NUM_G; ch++) + snd_emu10k1_voice_init(emu, ch); + + snd_emu10k1_ptr_write(emu, SPCS0, 0, emu->spdif_bits[0]); + snd_emu10k1_ptr_write(emu, SPCS1, 0, emu->spdif_bits[1]); + snd_emu10k1_ptr_write(emu, SPCS2, 0, emu->spdif_bits[2]); + + if (emu->card_capabilities->ca0151_chip) { /* audigy2 */ + /* Hacks for Alice3 to work independent of haP16V driver */ + //Setup SRCMulti_I2S SamplingRate + tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0); + tmp &= 0xfffff1ff; + tmp |= (0x2<<9); + snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp); + + /* Setup SRCSel (Enable Spdif,I2S SRCMulti) */ + snd_emu10k1_ptr20_write(emu, SRCSel, 0, 0x14); + /* Setup SRCMulti Input Audio Enable */ + /* Use 0xFFFFFFFF to enable P16V sounds. */ + snd_emu10k1_ptr20_write(emu, SRCMULTI_ENABLE, 0, 0xFFFFFFFF); + + /* Enabled Phased (8-channel) P16V playback */ + outl(0x0201, emu->port + HCFG2); + /* Set playback routing. */ + snd_emu10k1_ptr20_write(emu, CAPTURE_P16V_SOURCE, 0, 0x78e4); + } + if (emu->card_capabilities->ca0108_chip) { /* audigy2 Value */ + /* Hacks for Alice3 to work independent of haP16V driver */ + snd_printk(KERN_INFO "Audigy2 value: Special config.\n"); + //Setup SRCMulti_I2S SamplingRate + tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0); + tmp &= 0xfffff1ff; + tmp |= (0x2<<9); + snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp); + + /* Setup SRCSel (Enable Spdif,I2S SRCMulti) */ + outl(0x600000, emu->port + 0x20); + outl(0x14, emu->port + 0x24); + + /* Setup SRCMulti Input Audio Enable */ + outl(0x7b0000, emu->port + 0x20); + outl(0xFF000000, emu->port + 0x24); + + /* Setup SPDIF Out Audio Enable */ + /* The Audigy 2 Value has a separate SPDIF out, + * so no need for a mixer switch + */ + outl(0x7a0000, emu->port + 0x20); + outl(0xFF000000, emu->port + 0x24); + tmp = inl(emu->port + A_IOCFG) & ~0x8; /* Clear bit 3 */ + outl(tmp, emu->port + A_IOCFG); + } + if (emu->card_capabilities->spi_dac) { /* Audigy 2 ZS Notebook with DAC Wolfson WM8768/WM8568 */ + int size, n; + + size = ARRAY_SIZE(spi_dac_init); + for (n = 0; n < size; n++) + snd_emu10k1_spi_write(emu, spi_dac_init[n]); + + snd_emu10k1_ptr20_write(emu, 0x60, 0, 0x10); + /* Enable GPIOs + * GPIO0: Unknown + * GPIO1: Speakers-enabled. + * GPIO2: Unknown + * GPIO3: Unknown + * GPIO4: IEC958 Output on. + * GPIO5: Unknown + * GPIO6: Unknown + * GPIO7: Unknown + */ + outl(0x76, emu->port + A_IOCFG); /* Windows uses 0x3f76 */ + + } + if (emu->card_capabilities->i2c_adc) { /* Audigy 2 ZS Notebook with ADC Wolfson WM8775 */ + int size, n; + + snd_emu10k1_ptr20_write(emu, P17V_I2S_SRC_SEL, 0, 0x2020205f); + tmp = inl(emu->port + A_IOCFG); + outl(tmp | 0x4, emu->port + A_IOCFG); /* Set bit 2 for mic input */ + tmp = inl(emu->port + A_IOCFG); + size = ARRAY_SIZE(i2c_adc_init); + for (n = 0; n < size; n++) + snd_emu10k1_i2c_write(emu, i2c_adc_init[n][0], i2c_adc_init[n][1]); + for (n=0; n < 4; n++) { + emu->i2c_capture_volume[n][0]= 0xcf; + emu->i2c_capture_volume[n][1]= 0xcf; + } + + } + + + snd_emu10k1_ptr_write(emu, PTB, 0, emu->ptb_pages.addr); + snd_emu10k1_ptr_write(emu, TCB, 0, 0); /* taken from original driver */ + snd_emu10k1_ptr_write(emu, TCBS, 0, 4); /* taken from original driver */ + + silent_page = (emu->silent_page.addr << 1) | MAP_PTI_MASK; + for (ch = 0; ch < NUM_G; ch++) { + snd_emu10k1_ptr_write(emu, MAPA, ch, silent_page); + snd_emu10k1_ptr_write(emu, MAPB, ch, silent_page); + } + + if (emu->card_capabilities->emu1010) { + outl(HCFG_AUTOMUTE_ASYNC | + HCFG_EMU32_SLAVE | + HCFG_AUDIOENABLE, emu->port + HCFG); + /* + * Hokay, setup HCFG + * Mute Disable Audio = 0 + * Lock Tank Memory = 1 + * Lock Sound Memory = 0 + * Auto Mute = 1 + */ + } else if (emu->audigy) { + if (emu->revision == 4) /* audigy2 */ + outl(HCFG_AUDIOENABLE | + HCFG_AC3ENABLE_CDSPDIF | + HCFG_AC3ENABLE_GPSPDIF | + HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG); + else + outl(HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG); + /* FIXME: Remove all these emu->model and replace it with a card recognition parameter, + * e.g. card_capabilities->joystick */ + } else if (emu->model == 0x20 || + emu->model == 0xc400 || + (emu->model == 0x21 && emu->revision < 6)) + outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE, emu->port + HCFG); + else + // With on-chip joystick + outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG); + + if (enable_ir) { /* enable IR for SB Live */ + if (emu->card_capabilities->emu1010) { + ; /* Disable all access to A_IOCFG for the emu1010 */ + } else if (emu->card_capabilities->i2c_adc) { + ; /* Disable A_IOCFG for Audigy 2 ZS Notebook */ + } else if (emu->audigy) { + unsigned int reg = inl(emu->port + A_IOCFG); + outl(reg | A_IOCFG_GPOUT2, emu->port + A_IOCFG); + udelay(500); + outl(reg | A_IOCFG_GPOUT1 | A_IOCFG_GPOUT2, emu->port + A_IOCFG); + udelay(100); + outl(reg, emu->port + A_IOCFG); + } else { + unsigned int reg = inl(emu->port + HCFG); + outl(reg | HCFG_GPOUT2, emu->port + HCFG); + udelay(500); + outl(reg | HCFG_GPOUT1 | HCFG_GPOUT2, emu->port + HCFG); + udelay(100); + outl(reg, emu->port + HCFG); + } + } + + if (emu->card_capabilities->emu1010) { + ; /* Disable all access to A_IOCFG for the emu1010 */ + } else if (emu->card_capabilities->i2c_adc) { + ; /* Disable A_IOCFG for Audigy 2 ZS Notebook */ + } else if (emu->audigy) { /* enable analog output */ + unsigned int reg = inl(emu->port + A_IOCFG); + outl(reg | A_IOCFG_GPOUT0, emu->port + A_IOCFG); + } + + return 0; +} + +static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu) +{ + /* + * Enable the audio bit + */ + outl(inl(emu->port + HCFG) | HCFG_AUDIOENABLE, emu->port + HCFG); + + /* Enable analog/digital outs on audigy */ + if (emu->card_capabilities->emu1010) { + ; /* Disable all access to A_IOCFG for the emu1010 */ + } else if (emu->card_capabilities->i2c_adc) { + ; /* Disable A_IOCFG for Audigy 2 ZS Notebook */ + } else if (emu->audigy) { + outl(inl(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG); + + if (emu->card_capabilities->ca0151_chip) { /* audigy2 */ + /* Unmute Analog now. Set GPO6 to 1 for Apollo. + * This has to be done after init ALice3 I2SOut beyond 48KHz. + * So, sequence is important. */ + outl(inl(emu->port + A_IOCFG) | 0x0040, emu->port + A_IOCFG); + } else if (emu->card_capabilities->ca0108_chip) { /* audigy2 value */ + /* Unmute Analog now. */ + outl(inl(emu->port + A_IOCFG) | 0x0060, emu->port + A_IOCFG); + } else { + /* Disable routing from AC97 line out to Front speakers */ + outl(inl(emu->port + A_IOCFG) | 0x0080, emu->port + A_IOCFG); + } + } + +#if 0 + { + unsigned int tmp; + /* FIXME: the following routine disables LiveDrive-II !! */ + // TOSLink detection + emu->tos_link = 0; + tmp = inl(emu->port + HCFG); + if (tmp & (HCFG_GPINPUT0 | HCFG_GPINPUT1)) { + outl(tmp|0x800, emu->port + HCFG); + udelay(50); + if (tmp != (inl(emu->port + HCFG) & ~0x800)) { + emu->tos_link = 1; + outl(tmp, emu->port + HCFG); + } + } + } +#endif + + snd_emu10k1_intr_enable(emu, INTE_PCIERRORENABLE); +} + +int snd_emu10k1_done(struct snd_emu10k1 * emu) +{ + int ch; + + outl(0, emu->port + INTE); + + /* + * Shutdown the chip + */ + for (ch = 0; ch < NUM_G; ch++) + snd_emu10k1_ptr_write(emu, DCYSUSV, ch, 0); + for (ch = 0; ch < NUM_G; ch++) { + snd_emu10k1_ptr_write(emu, VTFT, ch, 0); + snd_emu10k1_ptr_write(emu, CVCF, ch, 0); + snd_emu10k1_ptr_write(emu, PTRX, ch, 0); + snd_emu10k1_ptr_write(emu, CPF, ch, 0); + } + + /* reset recording buffers */ + snd_emu10k1_ptr_write(emu, MICBS, 0, 0); + snd_emu10k1_ptr_write(emu, MICBA, 0, 0); + snd_emu10k1_ptr_write(emu, FXBS, 0, 0); + snd_emu10k1_ptr_write(emu, FXBA, 0, 0); + snd_emu10k1_ptr_write(emu, FXWC, 0, 0); + snd_emu10k1_ptr_write(emu, ADCBS, 0, ADCBS_BUFSIZE_NONE); + snd_emu10k1_ptr_write(emu, ADCBA, 0, 0); + snd_emu10k1_ptr_write(emu, TCBS, 0, TCBS_BUFFSIZE_16K); + snd_emu10k1_ptr_write(emu, TCB, 0, 0); + if (emu->audigy) + snd_emu10k1_ptr_write(emu, A_DBG, 0, A_DBG_SINGLE_STEP); + else + snd_emu10k1_ptr_write(emu, DBG, 0, EMU10K1_DBG_SINGLE_STEP); + + /* disable channel interrupt */ + snd_emu10k1_ptr_write(emu, CLIEL, 0, 0); + snd_emu10k1_ptr_write(emu, CLIEH, 0, 0); + snd_emu10k1_ptr_write(emu, SOLEL, 0, 0); + snd_emu10k1_ptr_write(emu, SOLEH, 0, 0); + + /* disable audio and lock cache */ + outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, emu->port + HCFG); + snd_emu10k1_ptr_write(emu, PTB, 0, 0); + + return 0; +} + +/************************************************************************* + * ECARD functional implementation + *************************************************************************/ + +/* In A1 Silicon, these bits are in the HC register */ +#define HOOKN_BIT (1L << 12) +#define HANDN_BIT (1L << 11) +#define PULSEN_BIT (1L << 10) + +#define EC_GDI1 (1 << 13) +#define EC_GDI0 (1 << 14) + +#define EC_NUM_CONTROL_BITS 20 + +#define EC_AC3_DATA_SELN 0x0001L +#define EC_EE_DATA_SEL 0x0002L +#define EC_EE_CNTRL_SELN 0x0004L +#define EC_EECLK 0x0008L +#define EC_EECS 0x0010L +#define EC_EESDO 0x0020L +#define EC_TRIM_CSN 0x0040L +#define EC_TRIM_SCLK 0x0080L +#define EC_TRIM_SDATA 0x0100L +#define EC_TRIM_MUTEN 0x0200L +#define EC_ADCCAL 0x0400L +#define EC_ADCRSTN 0x0800L +#define EC_DACCAL 0x1000L +#define EC_DACMUTEN 0x2000L +#define EC_LEDN 0x4000L + +#define EC_SPDIF0_SEL_SHIFT 15 +#define EC_SPDIF1_SEL_SHIFT 17 +#define EC_SPDIF0_SEL_MASK (0x3L << EC_SPDIF0_SEL_SHIFT) +#define EC_SPDIF1_SEL_MASK (0x7L << EC_SPDIF1_SEL_SHIFT) +#define EC_SPDIF0_SELECT(_x) (((_x) << EC_SPDIF0_SEL_SHIFT) & EC_SPDIF0_SEL_MASK) +#define EC_SPDIF1_SELECT(_x) (((_x) << EC_SPDIF1_SEL_SHIFT) & EC_SPDIF1_SEL_MASK) +#define EC_CURRENT_PROM_VERSION 0x01 /* Self-explanatory. This should + * be incremented any time the EEPROM's + * format is changed. */ + +#define EC_EEPROM_SIZE 0x40 /* ECARD EEPROM has 64 16-bit words */ + +/* Addresses for special values stored in to EEPROM */ +#define EC_PROM_VERSION_ADDR 0x20 /* Address of the current prom version */ +#define EC_BOARDREV0_ADDR 0x21 /* LSW of board rev */ +#define EC_BOARDREV1_ADDR 0x22 /* MSW of board rev */ + +#define EC_LAST_PROMFILE_ADDR 0x2f + +#define EC_SERIALNUM_ADDR 0x30 /* First word of serial number. The + * can be up to 30 characters in length + * and is stored as a NULL-terminated + * ASCII string. Any unused bytes must be + * filled with zeros */ +#define EC_CHECKSUM_ADDR 0x3f /* Location at which checksum is stored */ + + +/* Most of this stuff is pretty self-evident. According to the hardware + * dudes, we need to leave the ADCCAL bit low in order to avoid a DC + * offset problem. Weird. + */ +#define EC_RAW_RUN_MODE (EC_DACMUTEN | EC_ADCRSTN | EC_TRIM_MUTEN | \ + EC_TRIM_CSN) + + +#define EC_DEFAULT_ADC_GAIN 0xC4C4 +#define EC_DEFAULT_SPDIF0_SEL 0x0 +#define EC_DEFAULT_SPDIF1_SEL 0x4 + +/************************************************************************** + * @func Clock bits into the Ecard's control latch. The Ecard uses a + * control latch will is loaded bit-serially by toggling the Modem control + * lines from function 2 on the E8010. This function hides these details + * and presents the illusion that we are actually writing to a distinct + * register. + */ + +static void snd_emu10k1_ecard_write(struct snd_emu10k1 * emu, unsigned int value) +{ + unsigned short count; + unsigned int data; + unsigned long hc_port; + unsigned int hc_value; + + hc_port = emu->port + HCFG; + hc_value = inl(hc_port) & ~(HOOKN_BIT | HANDN_BIT | PULSEN_BIT); + outl(hc_value, hc_port); + + for (count = 0; count < EC_NUM_CONTROL_BITS; count++) { + + /* Set up the value */ + data = ((value & 0x1) ? PULSEN_BIT : 0); + value >>= 1; + + outl(hc_value | data, hc_port); + + /* Clock the shift register */ + outl(hc_value | data | HANDN_BIT, hc_port); + outl(hc_value | data, hc_port); + } + + /* Latch the bits */ + outl(hc_value | HOOKN_BIT, hc_port); + outl(hc_value, hc_port); +} + +/************************************************************************** + * @func Set the gain of the ECARD's CS3310 Trim/gain controller. The + * trim value consists of a 16bit value which is composed of two + * 8 bit gain/trim values, one for the left channel and one for the + * right channel. The following table maps from the Gain/Attenuation + * value in decibels into the corresponding bit pattern for a single + * channel. + */ + +static void snd_emu10k1_ecard_setadcgain(struct snd_emu10k1 * emu, + unsigned short gain) +{ + unsigned int bit; + + /* Enable writing to the TRIM registers */ + snd_emu10k1_ecard_write(emu, emu->ecard_ctrl & ~EC_TRIM_CSN); + + /* Do it again to insure that we meet hold time requirements */ + snd_emu10k1_ecard_write(emu, emu->ecard_ctrl & ~EC_TRIM_CSN); + + for (bit = (1 << 15); bit; bit >>= 1) { + unsigned int value; + + value = emu->ecard_ctrl & ~(EC_TRIM_CSN | EC_TRIM_SDATA); + + if (gain & bit) + value |= EC_TRIM_SDATA; + + /* Clock the bit */ + snd_emu10k1_ecard_write(emu, value); + snd_emu10k1_ecard_write(emu, value | EC_TRIM_SCLK); + snd_emu10k1_ecard_write(emu, value); + } + + snd_emu10k1_ecard_write(emu, emu->ecard_ctrl); +} + +static int snd_emu10k1_ecard_init(struct snd_emu10k1 * emu) +{ + unsigned int hc_value; + + /* Set up the initial settings */ + emu->ecard_ctrl = EC_RAW_RUN_MODE | + EC_SPDIF0_SELECT(EC_DEFAULT_SPDIF0_SEL) | + EC_SPDIF1_SELECT(EC_DEFAULT_SPDIF1_SEL); + + /* Step 0: Set the codec type in the hardware control register + * and enable audio output */ + hc_value = inl(emu->port + HCFG); + outl(hc_value | HCFG_AUDIOENABLE | HCFG_CODECFORMAT_I2S, emu->port + HCFG); + inl(emu->port + HCFG); + + /* Step 1: Turn off the led and deassert TRIM_CS */ + snd_emu10k1_ecard_write(emu, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN); + + /* Step 2: Calibrate the ADC and DAC */ + snd_emu10k1_ecard_write(emu, EC_DACCAL | EC_LEDN | EC_TRIM_CSN); + + /* Step 3: Wait for awhile; XXX We can't get away with this + * under a real operating system; we'll need to block and wait that + * way. */ + snd_emu10k1_wait(emu, 48000); + + /* Step 4: Switch off the DAC and ADC calibration. Note + * That ADC_CAL is actually an inverted signal, so we assert + * it here to stop calibration. */ + snd_emu10k1_ecard_write(emu, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN); + + /* Step 4: Switch into run mode */ + snd_emu10k1_ecard_write(emu, emu->ecard_ctrl); + + /* Step 5: Set the analog input gain */ + snd_emu10k1_ecard_setadcgain(emu, EC_DEFAULT_ADC_GAIN); + + return 0; +} + +static int snd_emu10k1_cardbus_init(struct snd_emu10k1 * emu) +{ + unsigned long special_port; + unsigned int value; + + /* Special initialisation routine + * before the rest of the IO-Ports become active. + */ + special_port = emu->port + 0x38; + value = inl(special_port); + outl(0x00d00000, special_port); + value = inl(special_port); + outl(0x00d00001, special_port); + value = inl(special_port); + outl(0x00d0005f, special_port); + value = inl(special_port); + outl(0x00d0007f, special_port); + value = inl(special_port); + outl(0x0090007f, special_port); + value = inl(special_port); + + snd_emu10k1_ptr20_write(emu, TINA2_VOLUME, 0, 0xfefefefe); /* Defaults to 0x30303030 */ + return 0; +} + +static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * filename) +{ + int err; + int n, i; + int reg; + int value; + const struct firmware *fw_entry; + + if ((err = request_firmware(&fw_entry, filename, &emu->pci->dev)) != 0) { + snd_printk(KERN_ERR "firmware: %s not found. Err=%d\n",filename, err); + return err; + } + snd_printk(KERN_INFO "firmware size=0x%zx\n", fw_entry->size); +#if 0 + if (fw_entry->size != 0x133a4) { + snd_printk(KERN_ERR "firmware: %s wrong size.\n",filename); + return -EINVAL; + } +#endif + + /* The FPGA is a Xilinx Spartan IIE XC2S50E */ + /* GPIO7 -> FPGA PGMN + * GPIO6 -> FPGA CCLK + * GPIO5 -> FPGA DIN + * FPGA CONFIG OFF -> FPGA PGMN + */ + outl(0x00, emu->port + A_IOCFG); /* Set PGMN low for 1uS. */ + udelay(1); + outl(0x80, emu->port + A_IOCFG); /* Leave bit 7 set during netlist setup. */ + udelay(100); /* Allow FPGA memory to clean */ + for(n = 0; n < fw_entry->size; n++) { + value=fw_entry->data[n]; + for(i = 0; i < 8; i++) { + reg = 0x80; + if (value & 0x1) + reg = reg | 0x20; + value = value >> 1; + outl(reg, emu->port + A_IOCFG); + outl(reg | 0x40, emu->port + A_IOCFG); + } + } + /* After programming, set GPIO bit 4 high again. */ + outl(0x10, emu->port + A_IOCFG); + + + release_firmware(fw_entry); + return 0; +} + +int emu1010_firmware_thread(void *data) { + struct snd_emu10k1 * emu = data; + int tmp,tmp2; + int reg; + int err; + + for (;;) { + /* Delay to allow Audio Dock to settle */ + msleep(1000); + if (kthread_should_stop()) + break; + snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp ); /* IRQ Status */ + snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, ® ); /* OPTIONS: Which cards are attached to the EMU */ + if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) { + /* Audio Dock attached */ + /* Return to Audio Dock programming mode */ + snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware\n"); + snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_AUDIODOCK ); + if (emu->card_capabilities->emu1010 == 1) { + if ((err = snd_emu1010_load_firmware(emu, DOCK_FILENAME)) != 0) { + return err; + } + } else if (emu->card_capabilities->emu1010 == 2) { + if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) { + return err; + } + } else if (emu->card_capabilities->emu1010 == 3) { + if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) { + return err; + } + } + + snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0 ); + snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, ® ); + snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_IRQ_STATUS=0x%x\n",reg); + /* ID, should read & 0x7f = 0x55 when FPGA programmed. */ + snd_emu1010_fpga_read(emu, EMU_HANA_ID, ® ); + snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_ID=0x%x\n",reg); + if ((reg & 0x1f) != 0x15) { + /* FPGA failed to be programmed */ + snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware file failed, reg=0x%x\n", reg); + return 0; + return -ENODEV; + } + snd_printk(KERN_INFO "emu1010: Audio Dock Firmware loaded\n"); + snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp ); + snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2 ); + snd_printk("Audio Dock ver:%d.%d\n",tmp ,tmp2); + /* Sync clocking between 1010 and Dock */ + /* Allow DLL to settle */ + msleep(10); + /* Unmute all. Default is muted after a firmware load */ + snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE ); + break; + } + } + return 0; +} + +/* + * EMU-1010 - details found out from this driver, official MS Win drivers, + * testing the card: + * + * Audigy2 (aka Alice2): + * --------------------- + * * communication over PCI + * * conversion of 32-bit data coming over EMU32 links from HANA FPGA + * to 2 x 16-bit, using internal DSP instructions + * * slave mode, clock supplied by HANA + * * linked to HANA using: + * 32 x 32-bit serial EMU32 output channels + * 16 x EMU32 input channels + * (?) x I2S I/O channels (?) + * + * FPGA (aka HANA): + * --------------- + * * provides all (?) physical inputs and outputs of the card + * (ADC, DAC, SPDIF I/O, ADAT I/O, etc.) + * * provides clock signal for the card and Alice2 + * * two crystals - for 44.1kHz and 48kHz multiples + * * provides internal routing of signal sources to signal destinations + * * inputs/outputs to Alice2 - see above + * + * Current status of the driver: + * ---------------------------- + * * only 44.1/48kHz supported (the MS Win driver supports up to 192 kHz) + * * PCM device nb. 2: + * 16 x 16-bit playback - snd_emu10k1_fx8010_playback_ops + * 16 x 32-bit capture - snd_emu10k1_capture_efx_ops + */ +static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu) +{ + unsigned int i; + int tmp,tmp2; + int reg; + int err; + + snd_printk(KERN_INFO "emu1010: Special config.\n"); + /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave, + * Lock Sound Memory Cache, Lock Tank Memory Cache, + * Mute all codecs. + */ + outl(0x0005a00c, emu->port + HCFG); + /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave, + * Lock Tank Memory Cache, + * Mute all codecs. + */ + outl(0x0005a004, emu->port + HCFG); + /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave, + * Mute all codecs. + */ + outl(0x0005a000, emu->port + HCFG); + /* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave, + * Mute all codecs. + */ + outl(0x0005a000, emu->port + HCFG); + + /* Disable 48Volt power to Audio Dock */ + snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0 ); + + /* ID, should read & 0x7f = 0x55. (Bit 7 is the IRQ bit) */ + snd_emu1010_fpga_read(emu, EMU_HANA_ID, ® ); + snd_printdd("reg1=0x%x\n",reg); + if ((reg & 0x3f) == 0x15) { + /* FPGA netlist already present so clear it */ + /* Return to programming mode */ + + snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0x02 ); + } + snd_emu1010_fpga_read(emu, EMU_HANA_ID, ® ); + snd_printdd("reg2=0x%x\n",reg); + if ((reg & 0x3f) == 0x15) { + /* FPGA failed to return to programming mode */ + snd_printk(KERN_INFO "emu1010: FPGA failed to return to programming mode\n"); + return -ENODEV; + } + snd_printk(KERN_INFO "emu1010: EMU_HANA_ID=0x%x\n",reg); + if (emu->card_capabilities->emu1010 == 1) { + if ((err = snd_emu1010_load_firmware(emu, HANA_FILENAME)) != 0) { + snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file %s failed\n", HANA_FILENAME); + return err; + } + } else if (emu->card_capabilities->emu1010 == 2) { + if ((err = snd_emu1010_load_firmware(emu, EMU1010B_FILENAME)) != 0) { + snd_printk(KERN_INFO "emu1010: Loading Firmware file %s failed\n", EMU1010B_FILENAME); + return err; + } + } else if (emu->card_capabilities->emu1010 == 3) { + if ((err = snd_emu1010_load_firmware(emu, EMU1010_NOTEBOOK_FILENAME)) != 0) { + snd_printk(KERN_INFO "emu1010: Loading Firmware file %s failed\n", EMU1010_NOTEBOOK_FILENAME); + return err; + } + } + + /* ID, should read & 0x7f = 0x55 when FPGA programmed. */ + snd_emu1010_fpga_read(emu, EMU_HANA_ID, ® ); + if ((reg & 0x3f) != 0x15) { + /* FPGA failed to be programmed */ + snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file failed, reg=0x%x\n", reg); + return -ENODEV; + } + + snd_printk(KERN_INFO "emu1010: Hana Firmware loaded\n"); + snd_emu1010_fpga_read(emu, EMU_HANA_MAJOR_REV, &tmp ); + snd_emu1010_fpga_read(emu, EMU_HANA_MINOR_REV, &tmp2 ); + snd_printk("Hana ver:%d.%d\n",tmp ,tmp2); + /* Enable 48Volt power to Audio Dock */ + snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, EMU_HANA_DOCK_PWR_ON ); + + snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, ® ); + snd_printk(KERN_INFO "emu1010: Card options=0x%x\n",reg); + snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, ® ); + snd_printk(KERN_INFO "emu1010: Card options=0x%x\n",reg); + snd_emu1010_fpga_read(emu, EMU_HANA_OPTICAL_TYPE, &tmp ); + /* Optical -> ADAT I/O */ + /* 0 : SPDIF + * 1 : ADAT + */ + emu->emu1010.optical_in = 1; /* IN_ADAT */ + emu->emu1010.optical_out = 1; /* IN_ADAT */ + tmp = 0; + tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : 0) | + (emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : 0); + snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp ); + snd_emu1010_fpga_read(emu, EMU_HANA_ADC_PADS, &tmp ); + /* Set no attenuation on Audio Dock pads. */ + snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, 0x00 ); + emu->emu1010.adc_pads = 0x00; + snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp ); + /* Unmute Audio dock DACs, Headphone source DAC-4. */ + snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30 ); + snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12 ); + snd_emu1010_fpga_read(emu, EMU_HANA_DAC_PADS, &tmp ); + /* DAC PADs. */ + snd_emu1010_fpga_write(emu, EMU_HANA_DAC_PADS, 0x0f ); + emu->emu1010.dac_pads = 0x0f; + snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp ); + snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30 ); + snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp ); + /* SPDIF Format. Set Consumer mode, 24bit, copy enable */ + snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10 ); + /* MIDI routing */ + snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19 ); + /* Unknown. */ + snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c ); + /* snd_emu1010_fpga_write(emu, 0x09, 0x0f ); // IRQ Enable: All on */ + /* IRQ Enable: All off */ + snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, 0x00 ); + + snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, ® ); + snd_printk(KERN_INFO "emu1010: Card options3=0x%x\n",reg); + /* Default WCLK set to 48kHz. */ + snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 0x00 ); + /* Word Clock source, Internal 48kHz x1 */ + snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K ); + //snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X ); + /* Audio Dock LEDs. */ + snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12 ); + +#if 0 + /* For 96kHz */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_0, EMU_SRC_HAMOA_ADC_LEFT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_1, EMU_SRC_HAMOA_ADC_RIGHT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_4, EMU_SRC_HAMOA_ADC_LEFT2); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_5, EMU_SRC_HAMOA_ADC_RIGHT2); +#endif +#if 0 + /* For 192kHz */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_0, EMU_SRC_HAMOA_ADC_LEFT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_1, EMU_SRC_HAMOA_ADC_RIGHT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_2, EMU_SRC_HAMOA_ADC_LEFT2); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_3, EMU_SRC_HAMOA_ADC_RIGHT2); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_4, EMU_SRC_HAMOA_ADC_LEFT3); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_5, EMU_SRC_HAMOA_ADC_RIGHT3); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_6, EMU_SRC_HAMOA_ADC_LEFT4); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_7, EMU_SRC_HAMOA_ADC_RIGHT4); +#endif +#if 1 + /* For 48kHz */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_0, EMU_SRC_DOCK_MIC_A1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_1, EMU_SRC_DOCK_MIC_B1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_2, EMU_SRC_HAMOA_ADC_LEFT2); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_3, EMU_SRC_HAMOA_ADC_LEFT2); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_4, EMU_SRC_DOCK_ADC1_LEFT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_5, EMU_SRC_DOCK_ADC1_RIGHT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_6, EMU_SRC_DOCK_ADC2_LEFT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_7, EMU_SRC_DOCK_ADC2_RIGHT1); + /* Pavel Hofman - setting defaults for 8 more capture channels + * Defaults only, users will set their own values anyways, let's + * just copy/paste. + */ + + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_8, EMU_SRC_DOCK_MIC_A1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_9, EMU_SRC_DOCK_MIC_B1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_A, EMU_SRC_HAMOA_ADC_LEFT2); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_B, EMU_SRC_HAMOA_ADC_LEFT2); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_C, EMU_SRC_DOCK_ADC1_LEFT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_D, EMU_SRC_DOCK_ADC1_RIGHT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_E, EMU_SRC_DOCK_ADC2_LEFT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_F, EMU_SRC_DOCK_ADC2_RIGHT1); +#endif +#if 0 + /* Original */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_4, EMU_SRC_HANA_ADAT); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_5, EMU_SRC_HANA_ADAT + 1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_6, EMU_SRC_HANA_ADAT + 2); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_7, EMU_SRC_HANA_ADAT + 3); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_8, EMU_SRC_HANA_ADAT + 4); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_9, EMU_SRC_HANA_ADAT + 5); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_A, EMU_SRC_HANA_ADAT + 6); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_B, EMU_SRC_HANA_ADAT + 7); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_C, EMU_SRC_DOCK_MIC_A1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_D, EMU_SRC_DOCK_MIC_B1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_E, EMU_SRC_HAMOA_ADC_LEFT2); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE2_EMU32_F, EMU_SRC_HAMOA_ADC_LEFT2); +#endif + for (i = 0;i < 0x20; i++ ) { + /* AudioDock Elink <- Silence */ + snd_emu1010_fpga_link_dst_src_write(emu, 0x0100+i, EMU_SRC_SILENCE); + } + for (i = 0;i < 4; i++) { + /* Hana SPDIF Out <- Silence */ + snd_emu1010_fpga_link_dst_src_write(emu, 0x0200+i, EMU_SRC_SILENCE); + } + for (i = 0;i < 7; i++) { + /* Hamoa DAC <- Silence */ + snd_emu1010_fpga_link_dst_src_write(emu, 0x0300+i, EMU_SRC_SILENCE); + } + for (i = 0;i < 7; i++) { + /* Hana ADAT Out <- Silence */ + snd_emu1010_fpga_link_dst_src_write(emu, EMU_DST_HANA_ADAT + i, EMU_SRC_SILENCE); + } + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE_I2S0_LEFT, EMU_SRC_DOCK_ADC1_LEFT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE_I2S0_RIGHT, EMU_SRC_DOCK_ADC1_RIGHT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE_I2S1_LEFT, EMU_SRC_DOCK_ADC2_LEFT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE_I2S1_RIGHT, EMU_SRC_DOCK_ADC2_RIGHT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE_I2S2_LEFT, EMU_SRC_DOCK_ADC3_LEFT1); + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_ALICE_I2S2_RIGHT, EMU_SRC_DOCK_ADC3_RIGHT1); + snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x01 ); // Unmute all + + snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp ); + + /* AC97 1.03, Any 32Meg of 2Gig address, Auto-Mute, EMU32 Slave, + * Lock Sound Memory Cache, Lock Tank Memory Cache, + * Mute all codecs. + */ + outl(0x0000a000, emu->port + HCFG); + /* AC97 1.03, Any 32Meg of 2Gig address, Auto-Mute, EMU32 Slave, + * Lock Sound Memory Cache, Lock Tank Memory Cache, + * Un-Mute all codecs. + */ + outl(0x0000a001, emu->port + HCFG); + + /* Initial boot complete. Now patches */ + + snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp ); + snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19 ); /* MIDI Route */ + snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c ); /* Unknown */ + snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19 ); /* MIDI Route */ + snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c ); /* Unknown */ + snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp ); + snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10 ); /* SPDIF Format spdif (or 0x11 for aes/ebu) */ + + /* Start Micro/Audio Dock firmware loader thread */ + emu->emu1010.firmware_thread = kthread_create(&emu1010_firmware_thread, + emu, + "emu1010_firmware"); + wake_up_process(emu->emu1010.firmware_thread); + +#if 0 + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32B + 2); /* ALICE2 bus 0xa2 */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HAMOA_DAC_RIGHT1, EMU_SRC_ALICE_EMU32B + 3); /* ALICE2 bus 0xa3 */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 2); /* ALICE2 bus 0xb2 */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 3); /* ALICE2 bus 0xb3 */ +#endif + /* Default outputs */ + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC1_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */ + emu->emu1010.output_source[0] = 21; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC1_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[1] = 22; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC2_LEFT1, EMU_SRC_ALICE_EMU32A + 2); + emu->emu1010.output_source[2] = 23; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC2_RIGHT1, EMU_SRC_ALICE_EMU32A + 3); + emu->emu1010.output_source[3] = 24; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC3_LEFT1, EMU_SRC_ALICE_EMU32A + 4); + emu->emu1010.output_source[4] = 25; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC3_RIGHT1, EMU_SRC_ALICE_EMU32A + 5); + emu->emu1010.output_source[5] = 26; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC4_LEFT1, EMU_SRC_ALICE_EMU32A + 6); + emu->emu1010.output_source[6] = 27; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_DAC4_RIGHT1, EMU_SRC_ALICE_EMU32A + 7); + emu->emu1010.output_source[7] = 28; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_PHONES_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */ + emu->emu1010.output_source[8] = 21; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_PHONES_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[9] = 22; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */ + emu->emu1010.output_source[10] = 21; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_DOCK_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[11] = 22; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */ + emu->emu1010.output_source[12] = 21; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[13] = 22; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */ + emu->emu1010.output_source[14] = 21; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HAMOA_DAC_RIGHT1, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[15] = 22; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT, EMU_SRC_ALICE_EMU32A + 0); /* ALICE2 bus 0xa0 */ + emu->emu1010.output_source[16] = 21; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 1, EMU_SRC_ALICE_EMU32A + 1); + emu->emu1010.output_source[17] = 22; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 2, EMU_SRC_ALICE_EMU32A + 2); + emu->emu1010.output_source[18] = 23; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 3, EMU_SRC_ALICE_EMU32A + 3); + emu->emu1010.output_source[19] = 24; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 4, EMU_SRC_ALICE_EMU32A + 4); + emu->emu1010.output_source[20] = 25; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 5, EMU_SRC_ALICE_EMU32A + 5); + emu->emu1010.output_source[21] = 26; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 6, EMU_SRC_ALICE_EMU32A + 6); + emu->emu1010.output_source[22] = 27; + snd_emu1010_fpga_link_dst_src_write(emu, + EMU_DST_HANA_ADAT + 7, EMU_SRC_ALICE_EMU32A + 7); + emu->emu1010.output_source[23] = 28; + + /* TEMP: Select SPDIF in/out */ + //snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x0); /* Output spdif */ + + /* TEMP: Select 48kHz SPDIF out */ + snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x0); /* Mute all */ + snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 0x0); /* Default fallback clock 48kHz */ + /* Word Clock source, Internal 48kHz x1 */ + snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K ); + //snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X ); + emu->emu1010.internal_clock = 1; /* 48000 */ + snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12);/* Set LEDs on Audio Dock */ + snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x1); /* Unmute all */ + //snd_emu1010_fpga_write(emu, 0x7, 0x0); /* Mute all */ + //snd_emu1010_fpga_write(emu, 0x7, 0x1); /* Unmute all */ + //snd_emu1010_fpga_write(emu, 0xe, 0x12); /* Set LEDs on Audio Dock */ + + return 0; +} +/* + * Create the EMU10K1 instance + */ + +#ifdef CONFIG_PM +static int alloc_pm_buffer(struct snd_emu10k1 *emu); +static void free_pm_buffer(struct snd_emu10k1 *emu); +#endif + +static int snd_emu10k1_free(struct snd_emu10k1 *emu) +{ + if (emu->port) { /* avoid access to already used hardware */ + snd_emu10k1_fx8010_tram_setup(emu, 0); + snd_emu10k1_done(emu); + /* remove reserved page */ + if (emu->reserved_page) { + snd_emu10k1_synth_free(emu, (struct snd_util_memblk *)emu->reserved_page); + emu->reserved_page = NULL; + } + snd_emu10k1_free_efx(emu); + } + if (emu->card_capabilities->emu1010) { + /* Disable 48Volt power to Audio Dock */ + snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0 ); + kthread_stop(emu->emu1010.firmware_thread); + } + if (emu->memhdr) + snd_util_memhdr_free(emu->memhdr); + if (emu->silent_page.area) + snd_dma_free_pages(&emu->silent_page); + if (emu->ptb_pages.area) + snd_dma_free_pages(&emu->ptb_pages); + vfree(emu->page_ptr_table); + vfree(emu->page_addr_table); +#ifdef CONFIG_PM + free_pm_buffer(emu); +#endif + if (emu->irq >= 0) + free_irq(emu->irq, emu); + if (emu->port) + pci_release_regions(emu->pci); + if (emu->card_capabilities->ca0151_chip) /* P16V */ + snd_p16v_free(emu); + pci_disable_device(emu->pci); + kfree(emu); + return 0; +} + +static int snd_emu10k1_dev_free(struct snd_device *device) +{ + struct snd_emu10k1 *emu = device->device_data; + return snd_emu10k1_free(emu); +} + +static struct snd_emu_chip_details emu_chip_details[] = { + /* Audigy 2 Value AC3 out does not work yet. Need to find out how to turn off interpolators.*/ + /* Tested by James@superbug.co.uk 3rd July 2005 */ + /* DSP: CA0108-IAT + * DAC: CS4382-KQ + * ADC: Philips 1361T + * AC97: STAC9750 + * CA0151: None + */ + {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10011102, + .driver = "Audigy2", .name = "Audigy 2 Value [SB0400]", + .id = "Audigy2", + .emu10k2_chip = 1, + .ca0108_chip = 1, + .spk71 = 1, + .ac97_chip = 1} , + /* Audigy4 (Not PRO) SB0610 */ + /* Tested by James@superbug.co.uk 4th April 2006 */ + /* A_IOCFG bits + * Output + * 0: ? + * 1: ? + * 2: ? + * 3: 0 - Digital Out, 1 - Line in + * 4: ? + * 5: ? + * 6: ? + * 7: ? + * Input + * 8: ? + * 9: ? + * A: Green jack sense (Front) + * B: ? + * C: Black jack sense (Rear/Side Right) + * D: Yellow jack sense (Center/LFE/Side Left) + * E: ? + * F: ? + * + * Digital Out/Line in switch using A_IOCFG bit 3 (0x08) + * 0 - Digital Out + * 1 - Line in + */ + /* Mic input not tested. + * Analog CD input not tested + * Digital Out not tested. + * Line in working. + * Audio output 5.1 working. Side outputs not working. + */ + /* DSP: CA10300-IAT LF + * DAC: Cirrus Logic CS4382-KQZ + * ADC: Philips 1361T + * AC97: Sigmatel STAC9750 + * CA0151: None + */ + {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10211102, + .driver = "Audigy2", .name = "Audigy 4 [SB0610]", + .id = "Audigy2", + .emu10k2_chip = 1, + .ca0108_chip = 1, + .spk71 = 1, + .adc_1361t = 1, /* 24 bit capture instead of 16bit */ + .ac97_chip = 1} , + /* Audigy 2 ZS Notebook Cardbus card.*/ + /* Tested by James@superbug.co.uk 6th November 2006 */ + /* Audio output 7.1/Headphones working. + * Digital output working. (AC3 not checked, only PCM) + * Audio Mic/Line inputs working. + * Digital input not tested. + */ + /* DSP: Tina2 + * DAC: Wolfson WM8768/WM8568 + * ADC: Wolfson WM8775 + * AC97: None + * CA0151: None + */ + /* Tested by James@superbug.co.uk 4th April 2006 */ + /* A_IOCFG bits + * Output + * 0: Not Used + * 1: 0 = Mute all the 7.1 channel out. 1 = unmute. + * 2: Analog input 0 = line in, 1 = mic in + * 3: Not Used + * 4: Digital output 0 = off, 1 = on. + * 5: Not Used + * 6: Not Used + * 7: Not Used + * Input + * All bits 1 (0x3fxx) means nothing plugged in. + * 8-9: 0 = Line in/Mic, 2 = Optical in, 3 = Nothing. + * A-B: 0 = Headphones, 2 = Optical out, 3 = Nothing. + * C-D: 2 = Front/Rear/etc, 3 = nothing. + * E-F: Always 0 + * + */ + {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x20011102, + .driver = "Audigy2", .name = "Audigy 2 ZS Notebook [SB0530]", + .id = "Audigy2", + .emu10k2_chip = 1, + .ca0108_chip = 1, + .ca_cardbus_chip = 1, + .spi_dac = 1, + .i2c_adc = 1, + .spk71 = 1} , + {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x42011102, + .driver = "Audigy2", .name = "E-mu 1010 Notebook [MAEM8950]", + .id = "EMU1010", + .emu10k2_chip = 1, + .ca0108_chip = 1, + .ca_cardbus_chip = 1, + .spk71 = 1 , + .emu1010 = 3} , + {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40041102, + .driver = "Audigy2", .name = "E-mu 1010b PCI [MAEM????]", + .id = "EMU1010", + .emu10k2_chip = 1, + .ca0108_chip = 1, + .spk71 = 1 , + .emu1010 = 2} , + {.vendor = 0x1102, .device = 0x0008, + .driver = "Audigy2", .name = "Audigy 2 Value [Unknown]", + .id = "Audigy2", + .emu10k2_chip = 1, + .ca0108_chip = 1, + .ac97_chip = 1} , + /* Tested by James@superbug.co.uk 8th July 2005. No sound available yet. */ + {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40011102, + .driver = "Audigy2", .name = "E-mu 1010 [4001]", + .id = "EMU1010", + .emu10k2_chip = 1, + .ca0102_chip = 1, + .spk71 = 1, + .emu1010 = 1} , + /* Tested by James@superbug.co.uk 3rd July 2005 */ + {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20071102, + .driver = "Audigy2", .name = "Audigy 4 PRO [SB0380]", + .id = "Audigy2", + .emu10k2_chip = 1, + .ca0102_chip = 1, + .ca0151_chip = 1, + .spk71 = 1, + .spdif_bug = 1, + .ac97_chip = 1} , + /* Tested by shane-alsa@cm.nu 5th Nov 2005 */ + /* The 0x20061102 does have SB0350 written on it + * Just like 0x20021102 + */ + {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20061102, + .driver = "Audigy2", .name = "Audigy 2 [SB0350b]", + .id = "Audigy2", + .emu10k2_chip = 1, + .ca0102_chip = 1, + .ca0151_chip = 1, + .spk71 = 1, + .spdif_bug = 1, + .ac97_chip = 1} , + {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20021102, + .driver = "Audigy2", .name = "Audigy 2 ZS [SB0350]", + .id = "Audigy2", + .emu10k2_chip = 1, + .ca0102_chip = 1, + .ca0151_chip = 1, + .spk71 = 1, + .spdif_bug = 1, + .ac97_chip = 1} , + {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20011102, + .driver = "Audigy2", .name = "Audigy 2 ZS [2001]", + .id = "Audigy2", + .emu10k2_chip = 1, + .ca0102_chip = 1, + .ca0151_chip = 1, + .spk71 = 1, + .spdif_bug = 1, + .ac97_chip = 1} , + /* Audigy 2 */ + /* Tested by James@superbug.co.uk 3rd July 2005 */ + /* DSP: CA0102-IAT + * DAC: CS4382-KQ + * ADC: Philips 1361T + * AC97: STAC9721 + * CA0151: Yes + */ + {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10071102, + .driver = "Audigy2", .name = "Audigy 2 [SB0240]", + .id = "Audigy2", + .emu10k2_chip = 1, + .ca0102_chip = 1, + .ca0151_chip = 1, + .spk71 = 1, + .spdif_bug = 1, + .adc_1361t = 1, /* 24 bit capture instead of 16bit */ + .ac97_chip = 1} , + {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10051102, + .driver = "Audigy2", .name = "Audigy 2 EX [1005]", + .id = "Audigy2", + .emu10k2_chip = 1, + .ca0102_chip = 1, + .ca0151_chip = 1, + .spk71 = 1, + .spdif_bug = 1} , + /* Dell OEM/Creative Labs Audigy 2 ZS */ + /* See ALSA bug#1365 */ + {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10031102, + .driver = "Audigy2", .name = "Audigy 2 ZS [SB0353]", + .id = "Audigy2", + .emu10k2_chip = 1, + .ca0102_chip = 1, + .ca0151_chip = 1, + .spk71 = 1, + .spdif_bug = 1, + .ac97_chip = 1} , + {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10021102, + .driver = "Audigy2", .name = "Audigy 2 Platinum [SB0240P]", + .id = "Audigy2", + .emu10k2_chip = 1, + .ca0102_chip = 1, + .ca0151_chip = 1, + .spk71 = 1, + .spdif_bug = 1, + .adc_1361t = 1, /* 24 bit capture instead of 16bit. Fixes ALSA bug#324 */ + .ac97_chip = 1} , + {.vendor = 0x1102, .device = 0x0004, .revision = 0x04, + .driver = "Audigy2", .name = "Audigy 2 [Unknown]", + .id = "Audigy2", + .emu10k2_chip = 1, + .ca0102_chip = 1, + .ca0151_chip = 1, + .spdif_bug = 1, + .ac97_chip = 1} , + {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00531102, + .driver = "Audigy", .name = "Audigy 1 [SB0090]", + .id = "Audigy", + .emu10k2_chip = 1, + .ca0102_chip = 1, + .ac97_chip = 1} , + {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00521102, + .driver = "Audigy", .name = "Audigy 1 ES [SB0160]", + .id = "Audigy", + .emu10k2_chip = 1, + .ca0102_chip = 1, + .spdif_bug = 1, + .ac97_chip = 1} , + {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00511102, + .driver = "Audigy", .name = "Audigy 1 [SB0090]", + .id = "Audigy", + .emu10k2_chip = 1, + .ca0102_chip = 1, + .ac97_chip = 1} , + {.vendor = 0x1102, .device = 0x0004, + .driver = "Audigy", .name = "Audigy 1 [Unknown]", + .id = "Audigy", + .emu10k2_chip = 1, + .ca0102_chip = 1, + .ac97_chip = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x806B1102, + .driver = "EMU10K1", .name = "SBLive! [SB0105]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x806A1102, + .driver = "EMU10K1", .name = "SBLive! Value [SB0103]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80691102, + .driver = "EMU10K1", .name = "SBLive! Value [SB0101]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + /* Tested by ALSA bug#1680 26th December 2005 */ + /* note: It really has SB0220 written on the card. */ + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80661102, + .driver = "EMU10K1", .name = "SB Live 5.1 Dell OEM [SB0220]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + /* Tested by Thomas Zehetbauer 27th Aug 2005 */ + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80651102, + .driver = "EMU10K1", .name = "SB Live 5.1 [SB0220]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x100a1102, + .driver = "EMU10K1", .name = "SB Live 5.1 [SB0220]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80641102, + .driver = "EMU10K1", .name = "SB Live 5.1", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + /* Tested by alsa bugtrack user "hus" bug #1297 12th Aug 2005 */ + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80611102, + .driver = "EMU10K1", .name = "SBLive 5.1 [SB0060]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 2, /* ac97 is optional; both SBLive 5.1 and platinum + * share the same IDs! + */ + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80511102, + .driver = "EMU10K1", .name = "SBLive! Value [CT4850]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80401102, + .driver = "EMU10K1", .name = "SBLive! Platinum [CT4760P]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80321102, + .driver = "EMU10K1", .name = "SBLive! Value [CT4871]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80311102, + .driver = "EMU10K1", .name = "SBLive! Value [CT4831]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80281102, + .driver = "EMU10K1", .name = "SBLive! Value [CT4870]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + /* Tested by James@superbug.co.uk 3rd July 2005 */ + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80271102, + .driver = "EMU10K1", .name = "SBLive! Value [CT4832]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80261102, + .driver = "EMU10K1", .name = "SBLive! Value [CT4830]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80231102, + .driver = "EMU10K1", .name = "SB PCI512 [CT4790]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80221102, + .driver = "EMU10K1", .name = "SBLive! Value [CT4780]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x40011102, + .driver = "EMU10K1", .name = "E-mu APS [4001]", + .id = "APS", + .emu10k1_chip = 1, + .ecard = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x00211102, + .driver = "EMU10K1", .name = "SBLive! [CT4620]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x00201102, + .driver = "EMU10K1", .name = "SBLive! Value [CT4670]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + {.vendor = 0x1102, .device = 0x0002, + .driver = "EMU10K1", .name = "SB Live [Unknown]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1, + .sblive51 = 1} , + { } /* terminator */ +}; + +int __devinit snd_emu10k1_create(struct snd_card *card, + struct pci_dev * pci, + unsigned short extin_mask, + unsigned short extout_mask, + long max_cache_bytes, + int enable_ir, + uint subsystem, + struct snd_emu10k1 ** remu) +{ + struct snd_emu10k1 *emu; + int idx, err; + int is_audigy; + unsigned int silent_page; + const struct snd_emu_chip_details *c; + static struct snd_device_ops ops = { + .dev_free = snd_emu10k1_dev_free, + }; + + *remu = NULL; + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + + emu = kzalloc(sizeof(*emu), GFP_KERNEL); + if (emu == NULL) { + pci_disable_device(pci); + return -ENOMEM; + } + emu->card = card; + spin_lock_init(&emu->reg_lock); + spin_lock_init(&emu->emu_lock); + spin_lock_init(&emu->voice_lock); + spin_lock_init(&emu->synth_lock); + spin_lock_init(&emu->memblk_lock); + mutex_init(&emu->fx8010.lock); + INIT_LIST_HEAD(&emu->mapped_link_head); + INIT_LIST_HEAD(&emu->mapped_order_link_head); + emu->pci = pci; + emu->irq = -1; + emu->synth = NULL; + emu->get_synth_voice = NULL; + /* read revision & serial */ + emu->revision = pci->revision; + pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial); + pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &emu->model); + snd_printdd("vendor=0x%x, device=0x%x, subsystem_vendor_id=0x%x, subsystem_id=0x%x\n",pci->vendor, pci->device, emu->serial, emu->model); + + for (c = emu_chip_details; c->vendor; c++) { + if (c->vendor == pci->vendor && c->device == pci->device) { + if (subsystem) { + if (c->subsystem && (c->subsystem == subsystem) ) { + break; + } else continue; + } else { + if (c->subsystem && (c->subsystem != emu->serial) ) + continue; + if (c->revision && c->revision != emu->revision) + continue; + } + break; + } + } + if (c->vendor == 0) { + snd_printk(KERN_ERR "emu10k1: Card not recognised\n"); + kfree(emu); + pci_disable_device(pci); + return -ENOENT; + } + emu->card_capabilities = c; + if (c->subsystem && !subsystem) + snd_printdd("Sound card name=%s\n", c->name); + else if (subsystem) + snd_printdd("Sound card name=%s, vendor=0x%x, device=0x%x, subsystem=0x%x. Forced to subsytem=0x%x\n", + c->name, pci->vendor, pci->device, emu->serial, c->subsystem); + else + snd_printdd("Sound card name=%s, vendor=0x%x, device=0x%x, subsystem=0x%x.\n", + c->name, pci->vendor, pci->device, emu->serial); + + if (!*card->id && c->id) { + int i, n = 0; + strlcpy(card->id, c->id, sizeof(card->id)); + for (;;) { + for (i = 0; i < snd_ecards_limit; i++) { + if (snd_cards[i] && !strcmp(snd_cards[i]->id, card->id)) + break; + } + if (i >= snd_ecards_limit) + break; + n++; + if (n >= SNDRV_CARDS) + break; + snprintf(card->id, sizeof(card->id), "%s_%d", c->id, n); + } + } + + is_audigy = emu->audigy = c->emu10k2_chip; + + /* set the DMA transfer mask */ + emu->dma_mask = is_audigy ? AUDIGY_DMA_MASK : EMU10K1_DMA_MASK; + if (pci_set_dma_mask(pci, emu->dma_mask) < 0 || + pci_set_consistent_dma_mask(pci, emu->dma_mask) < 0) { + snd_printk(KERN_ERR "architecture does not support PCI busmaster DMA with mask 0x%lx\n", emu->dma_mask); + kfree(emu); + pci_disable_device(pci); + return -ENXIO; + } + if (is_audigy) + emu->gpr_base = A_FXGPREGBASE; + else + emu->gpr_base = FXGPREGBASE; + + if ((err = pci_request_regions(pci, "EMU10K1")) < 0) { + kfree(emu); + pci_disable_device(pci); + return err; + } + emu->port = pci_resource_start(pci, 0); + + if (request_irq(pci->irq, snd_emu10k1_interrupt, IRQF_SHARED, + "EMU10K1", emu)) { + err = -EBUSY; + goto error; + } + emu->irq = pci->irq; + + emu->max_cache_pages = max_cache_bytes >> PAGE_SHIFT; + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), + 32 * 1024, &emu->ptb_pages) < 0) { + err = -ENOMEM; + goto error; + } + + emu->page_ptr_table = (void **)vmalloc(emu->max_cache_pages * sizeof(void*)); + emu->page_addr_table = (unsigned long*)vmalloc(emu->max_cache_pages * sizeof(unsigned long)); + if (emu->page_ptr_table == NULL || emu->page_addr_table == NULL) { + err = -ENOMEM; + goto error; + } + + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), + EMUPAGESIZE, &emu->silent_page) < 0) { + err = -ENOMEM; + goto error; + } + emu->memhdr = snd_util_memhdr_new(emu->max_cache_pages * PAGE_SIZE); + if (emu->memhdr == NULL) { + err = -ENOMEM; + goto error; + } + emu->memhdr->block_extra_size = sizeof(struct snd_emu10k1_memblk) - + sizeof(struct snd_util_memblk); + + pci_set_master(pci); + + emu->fx8010.fxbus_mask = 0x303f; + if (extin_mask == 0) + extin_mask = 0x3fcf; + if (extout_mask == 0) + extout_mask = 0x7fff; + emu->fx8010.extin_mask = extin_mask; + emu->fx8010.extout_mask = extout_mask; + emu->enable_ir = enable_ir; + + if (emu->card_capabilities->ca_cardbus_chip) { + if ((err = snd_emu10k1_cardbus_init(emu)) < 0) + goto error; + } + if (emu->card_capabilities->ecard) { + if ((err = snd_emu10k1_ecard_init(emu)) < 0) + goto error; + } else if (emu->card_capabilities->emu1010) { + if ((err = snd_emu10k1_emu1010_init(emu)) < 0) { + snd_emu10k1_free(emu); + return err; + } + } else { + /* 5.1: Enable the additional AC97 Slots. If the emu10k1 version + does not support this, it shouldn't do any harm */ + snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE); + } + + /* initialize TRAM setup */ + emu->fx8010.itram_size = (16 * 1024)/2; + emu->fx8010.etram_pages.area = NULL; + emu->fx8010.etram_pages.bytes = 0; + + /* + * Init to 0x02109204 : + * Clock accuracy = 0 (1000ppm) + * Sample Rate = 2 (48kHz) + * Audio Channel = 1 (Left of 2) + * Source Number = 0 (Unspecified) + * Generation Status = 1 (Original for Cat Code 12) + * Cat Code = 12 (Digital Signal Mixer) + * Mode = 0 (Mode 0) + * Emphasis = 0 (None) + * CP = 1 (Copyright unasserted) + * AN = 0 (Audio data) + * P = 0 (Consumer) + */ + emu->spdif_bits[0] = emu->spdif_bits[1] = + emu->spdif_bits[2] = SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | + SPCS_GENERATIONSTATUS | 0x00001200 | + 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT; + + emu->reserved_page = (struct snd_emu10k1_memblk *) + snd_emu10k1_synth_alloc(emu, 4096); + if (emu->reserved_page) + emu->reserved_page->map_locked = 1; + + /* Clear silent pages and set up pointers */ + memset(emu->silent_page.area, 0, PAGE_SIZE); + silent_page = emu->silent_page.addr << 1; + for (idx = 0; idx < MAXPAGES; idx++) + ((u32 *)emu->ptb_pages.area)[idx] = cpu_to_le32(silent_page | idx); + + /* set up voice indices */ + for (idx = 0; idx < NUM_G; idx++) { + emu->voices[idx].emu = emu; + emu->voices[idx].number = idx; + } + + if ((err = snd_emu10k1_init(emu, enable_ir, 0)) < 0) + goto error; +#ifdef CONFIG_PM + if ((err = alloc_pm_buffer(emu)) < 0) + goto error; +#endif + + /* Initialize the effect engine */ + if ((err = snd_emu10k1_init_efx(emu)) < 0) + goto error; + snd_emu10k1_audio_enable(emu); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, emu, &ops)) < 0) + goto error; + +#ifdef CONFIG_PROC_FS + snd_emu10k1_proc_init(emu); +#endif + + snd_card_set_dev(card, &pci->dev); + *remu = emu; + return 0; + + error: + snd_emu10k1_free(emu); + return err; +} + +#ifdef CONFIG_PM +static unsigned char saved_regs[] = { + CPF, PTRX, CVCF, VTFT, Z1, Z2, PSST, DSL, CCCA, CCR, CLP, + FXRT, MAPA, MAPB, ENVVOL, ATKHLDV, DCYSUSV, LFOVAL1, ENVVAL, + ATKHLDM, DCYSUSM, LFOVAL2, IP, IFATN, PEFE, FMMOD, TREMFRQ, FM2FRQ2, + TEMPENV, ADCCR, FXWC, MICBA, ADCBA, FXBA, + MICBS, ADCBS, FXBS, CDCS, GPSCS, SPCS0, SPCS1, SPCS2, + SPBYPASS, AC97SLOT, CDSRCS, GPSRCS, ZVSRCS, MICIDX, ADCIDX, FXIDX, + 0xff /* end */ +}; +static unsigned char saved_regs_audigy[] = { + A_ADCIDX, A_MICIDX, A_FXWC1, A_FXWC2, A_SAMPLE_RATE, + A_FXRT2, A_SENDAMOUNTS, A_FXRT1, + 0xff /* end */ +}; + +static int __devinit alloc_pm_buffer(struct snd_emu10k1 *emu) +{ + int size; + + size = ARRAY_SIZE(saved_regs); + if (emu->audigy) + size += ARRAY_SIZE(saved_regs_audigy); + emu->saved_ptr = vmalloc(4 * NUM_G * size); + if (! emu->saved_ptr) + return -ENOMEM; + if (snd_emu10k1_efx_alloc_pm_buffer(emu) < 0) + return -ENOMEM; + if (emu->card_capabilities->ca0151_chip && + snd_p16v_alloc_pm_buffer(emu) < 0) + return -ENOMEM; + return 0; +} + +static void free_pm_buffer(struct snd_emu10k1 *emu) +{ + vfree(emu->saved_ptr); + snd_emu10k1_efx_free_pm_buffer(emu); + if (emu->card_capabilities->ca0151_chip) + snd_p16v_free_pm_buffer(emu); +} + +void snd_emu10k1_suspend_regs(struct snd_emu10k1 *emu) +{ + int i; + unsigned char *reg; + unsigned int *val; + + val = emu->saved_ptr; + for (reg = saved_regs; *reg != 0xff; reg++) + for (i = 0; i < NUM_G; i++, val++) + *val = snd_emu10k1_ptr_read(emu, *reg, i); + if (emu->audigy) { + for (reg = saved_regs_audigy; *reg != 0xff; reg++) + for (i = 0; i < NUM_G; i++, val++) + *val = snd_emu10k1_ptr_read(emu, *reg, i); + } + if (emu->audigy) + emu->saved_a_iocfg = inl(emu->port + A_IOCFG); + emu->saved_hcfg = inl(emu->port + HCFG); +} + +void snd_emu10k1_resume_init(struct snd_emu10k1 *emu) +{ + if (emu->card_capabilities->ca_cardbus_chip) + snd_emu10k1_cardbus_init(emu); + if (emu->card_capabilities->ecard) + snd_emu10k1_ecard_init(emu); + else if (emu->card_capabilities->emu1010) + snd_emu10k1_emu1010_init(emu); + else + snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE); + snd_emu10k1_init(emu, emu->enable_ir, 1); +} + +void snd_emu10k1_resume_regs(struct snd_emu10k1 *emu) +{ + int i; + unsigned char *reg; + unsigned int *val; + + snd_emu10k1_audio_enable(emu); + + /* resore for spdif */ + if (emu->audigy) + outl(emu->saved_a_iocfg, emu->port + A_IOCFG); + outl(emu->saved_hcfg, emu->port + HCFG); + + val = emu->saved_ptr; + for (reg = saved_regs; *reg != 0xff; reg++) + for (i = 0; i < NUM_G; i++, val++) + snd_emu10k1_ptr_write(emu, *reg, i, *val); + if (emu->audigy) { + for (reg = saved_regs_audigy; *reg != 0xff; reg++) + for (i = 0; i < NUM_G; i++, val++) + snd_emu10k1_ptr_write(emu, *reg, i, *val); + } +} +#endif diff -Nur sound/pci/emu10k1/emu10k1x.c sound/pci/emu10k1/emu10k1x.c --- sound/pci/emu10k1/emu10k1x.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/emu10k1/emu10k1x.c 2007-08-21 17:37:19.000000000 +0200 @@ -1062,14 +1062,7 @@ return 0; } -static int snd_emu10k1x_shared_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_emu10k1x_shared_spdif_info snd_ctl_boolean_mono_info static int snd_emu10k1x_shared_spdif_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff -Nur sound/pci/emu10k1/emufx.c sound/pci/emu10k1/emufx.c --- sound/pci/emu10k1/emufx.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/emu10k1/emufx.c 2007-08-12 02:00:07.000000000 +0200 @@ -1123,6 +1123,11 @@ ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF; } +/* + * Used for emu1010 - conversion from 32-bit capture inputs from HANA + * to 2 x 16-bit registers in audigy - their values are read via DMA. + * Conversion is performed by Audigy DSP instructions of FX8010. + */ static int snd_emu10k1_audigy_dsp_convert_32_to_2x16( struct snd_emu10k1_fx8010_code *icode, u32 *ptr, int tmp, int bit_shifter16, @@ -1193,12 +1198,16 @@ snd_emu10k1_ptr_write(emu, A_DBG, 0, (emu->fx8010.dbg = 0) | A_DBG_SINGLE_STEP); #if 1 - /* PCM front Playback Volume (independent from stereo mix) */ + /* PCM front Playback Volume (independent from stereo mix) + * playback = 0 + ( gpr * FXBUS_PCM_LEFT_FRONT >> 31) + * where gpr contains attenuation from corresponding mixer control + * (snd_emu10k1_init_stereo_control) + */ A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_FRONT)); A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_FRONT)); snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Front Playback Volume", gpr, 100); gpr += 2; - + /* PCM Surround Playback (independent from stereo mix) */ A_OP(icode, &ptr, iMAC0, A_GPR(playback+2), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_REAR)); A_OP(icode, &ptr, iMAC0, A_GPR(playback+3), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_REAR)); @@ -1258,8 +1267,16 @@ /* emu1212 DSP 0 and DSP 1 Capture */ if (emu->card_capabilities->emu1010) { - A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_P16VIN(0x0)); - A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_P16VIN(0x1)); + if (emu->card_capabilities->ca0108_chip) { + /* Note:JCD:No longer bit shift lower 16bits to upper 16bits of 32bit value. */ + A_OP(icode, &ptr, iMACINT0, A_GPR(tmp), A_C_00000000, A3_EMU32IN(0x0), A_C_00000001); + A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_GPR(tmp)); + A_OP(icode, &ptr, iMACINT0, A_GPR(tmp), A_C_00000000, A3_EMU32IN(0x1), A_C_00000001); + A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr), A_GPR(tmp)); + } else { + A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_P16VIN(0x0)); + A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_P16VIN(0x1)); + } snd_emu10k1_init_stereo_control(&controls[nctl++], "EMU Capture Volume", gpr, 0); gpr += 2; } @@ -1507,7 +1524,11 @@ /* EMU1010 Outputs from PCM Front, Rear, Center, LFE, Side */ snd_printk("EMU outputs on\n"); for (z = 0; z < 8; z++) { - A_OP(icode, &ptr, iACC3, A_EMU32OUTL(z), A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), A_C_00000000, A_C_00000000); + if (emu->card_capabilities->ca0108_chip) { + A_OP(icode, &ptr, iACC3, A3_EMU32OUT(z), A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), A_C_00000000, A_C_00000000); + } else { + A_OP(icode, &ptr, iACC3, A_EMU32OUTL(z), A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), A_C_00000000, A_C_00000000); + } } } @@ -1548,41 +1569,116 @@ #endif if (emu->card_capabilities->emu1010) { - snd_printk("EMU inputs on\n"); - /* Capture 8 channels of S32_LE sound */ - - /* printk("emufx.c: gpr=0x%x, tmp=0x%x\n",gpr, tmp); */ - /* For the EMU1010: How to get 32bit values from the DSP. High 16bits into L, low 16bits into R. */ - /* A_P16VIN(0) is delayed by one sample, - * so all other A_P16VIN channels will need to also be delayed - */ - /* Left ADC in. 1 of 2 */ - snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_P16VIN(0x0), A_FXBUS2(0) ); - /* Right ADC in 1 of 2 */ - gpr_map[gpr++] = 0x00000000; - snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(2) ); - A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x1), A_C_00000000, A_C_00000000); - gpr_map[gpr++] = 0x00000000; - snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(4) ); - A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x2), A_C_00000000, A_C_00000000); - gpr_map[gpr++] = 0x00000000; - snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(6) ); - A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x3), A_C_00000000, A_C_00000000); - /* For 96kHz mode */ - /* Left ADC in. 2 of 2 */ - gpr_map[gpr++] = 0x00000000; - snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0x8) ); - A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x4), A_C_00000000, A_C_00000000); - /* Right ADC in 2 of 2 */ - gpr_map[gpr++] = 0x00000000; - snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xa) ); - A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x5), A_C_00000000, A_C_00000000); - gpr_map[gpr++] = 0x00000000; - snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xc) ); - A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x6), A_C_00000000, A_C_00000000); - gpr_map[gpr++] = 0x00000000; - snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xe) ); - A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x7), A_C_00000000, A_C_00000000); + if (emu->card_capabilities->ca0108_chip) { + snd_printk("EMU2 inputs on\n"); + for (z = 0; z < 0x10; z++) { + snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, + bit_shifter16, + A3_EMU32IN(z), + A_FXBUS2(z*2) ); + } + } else { + snd_printk("EMU inputs on\n"); + /* Capture 16 (originally 8) channels of S32_LE sound */ + + /* printk("emufx.c: gpr=0x%x, tmp=0x%x\n",gpr, tmp); */ + /* For the EMU1010: How to get 32bit values from the DSP. High 16bits into L, low 16bits into R. */ + /* A_P16VIN(0) is delayed by one sample, + * so all other A_P16VIN channels will need to also be delayed + */ + /* Left ADC in. 1 of 2 */ + snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_P16VIN(0x0), A_FXBUS2(0) ); + /* Right ADC in 1 of 2 */ + gpr_map[gpr++] = 0x00000000; + /* Delaying by one sample: instead of copying the input + * value A_P16VIN to output A_FXBUS2 as in the first channel, + * we use an auxiliary register, delaying the value by one + * sample + */ + snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(2) ); + A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x1), A_C_00000000, A_C_00000000); + gpr_map[gpr++] = 0x00000000; + snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(4) ); + A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x2), A_C_00000000, A_C_00000000); + gpr_map[gpr++] = 0x00000000; + snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(6) ); + A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x3), A_C_00000000, A_C_00000000); + /* For 96kHz mode */ + /* Left ADC in. 2 of 2 */ + gpr_map[gpr++] = 0x00000000; + snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0x8) ); + A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x4), A_C_00000000, A_C_00000000); + /* Right ADC in 2 of 2 */ + gpr_map[gpr++] = 0x00000000; + snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xa) ); + A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x5), A_C_00000000, A_C_00000000); + gpr_map[gpr++] = 0x00000000; + snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xc) ); + A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x6), A_C_00000000, A_C_00000000); + gpr_map[gpr++] = 0x00000000; + snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xe) ); + A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x7), A_C_00000000, A_C_00000000); + /* Pavel Hofman - we still have voices, A_FXBUS2s, and + * A_P16VINs available - + * let's add 8 more capture channels - total of 16 + */ + gpr_map[gpr++] = 0x00000000; + snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp, + bit_shifter16, + A_GPR(gpr - 1), + A_FXBUS2(0x10)); + A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x8), + A_C_00000000, A_C_00000000); + gpr_map[gpr++] = 0x00000000; + snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp, + bit_shifter16, + A_GPR(gpr - 1), + A_FXBUS2(0x12)); + A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x9), + A_C_00000000, A_C_00000000); + gpr_map[gpr++] = 0x00000000; + snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp, + bit_shifter16, + A_GPR(gpr - 1), + A_FXBUS2(0x14)); + A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xa), + A_C_00000000, A_C_00000000); + gpr_map[gpr++] = 0x00000000; + snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp, + bit_shifter16, + A_GPR(gpr - 1), + A_FXBUS2(0x16)); + A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xb), + A_C_00000000, A_C_00000000); + gpr_map[gpr++] = 0x00000000; + snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp, + bit_shifter16, + A_GPR(gpr - 1), + A_FXBUS2(0x18)); + A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xc), + A_C_00000000, A_C_00000000); + gpr_map[gpr++] = 0x00000000; + snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp, + bit_shifter16, + A_GPR(gpr - 1), + A_FXBUS2(0x1a)); + A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xd), + A_C_00000000, A_C_00000000); + gpr_map[gpr++] = 0x00000000; + snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp, + bit_shifter16, + A_GPR(gpr - 1), + A_FXBUS2(0x1c)); + A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xe), + A_C_00000000, A_C_00000000); + gpr_map[gpr++] = 0x00000000; + snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp, + bit_shifter16, + A_GPR(gpr - 1), + A_FXBUS2(0x1e)); + A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xf), + A_C_00000000, A_C_00000000); + } #if 0 for (z = 4; z < 8; z++) { @@ -2344,14 +2440,13 @@ strcpy(dst, src); } -static int snd_emu10k1_fx8010_info(struct snd_emu10k1 *emu, +static void snd_emu10k1_fx8010_info(struct snd_emu10k1 *emu, struct snd_emu10k1_fx8010_info *info) { char **fxbus, **extin, **extout; unsigned short fxbus_mask, extin_mask, extout_mask; int res; - memset(info, 0, sizeof(info)); info->internal_tram_size = emu->fx8010.itram_size; info->external_tram_size = emu->fx8010.etram_pages.bytes / 2; fxbus = fxbuses; @@ -2368,7 +2463,6 @@ for (res = 16; res < 32; res++, extout++) copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res); info->gpr_controls = emu->fx8010.gpr_count; - return 0; } static int snd_emu10k1_fx8010_ioctl(struct snd_hwdep * hw, struct file *file, unsigned int cmd, unsigned long arg) @@ -2389,10 +2483,7 @@ info = kmalloc(sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; - if ((res = snd_emu10k1_fx8010_info(emu, info)) < 0) { - kfree(info); - return res; - } + snd_emu10k1_fx8010_info(emu, info); if (copy_to_user(argp, info, sizeof(*info))) { kfree(info); return -EFAULT; diff -Nur sound/pci/emu10k1/emumixer.c sound/pci/emu10k1/emumixer.c --- sound/pci/emu10k1/emumixer.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/emu10k1/emumixer.c 2007-08-17 02:00:07.000000000 +0200 @@ -77,6 +77,10 @@ return 0; } +/* + * Items labels in enum mixer controls assigning source data to + * each destination + */ static char *emu1010_src_texts[] = { "Silence", "Dock Mic A", @@ -133,6 +137,9 @@ "DSP 31", }; +/* + * List of data sources available for each destination + */ static unsigned int emu1010_src_regs[] = { EMU_SRC_SILENCE,/* 0 */ EMU_SRC_DOCK_MIC_A1, /* 1 */ @@ -189,6 +196,10 @@ EMU_SRC_ALICE_EMU32B+0xf, /* 52 */ }; +/* + * Data destinations - physical EMU outputs. + * Each destination has an enum mixer control to choose a data source + */ static unsigned int emu1010_output_dst[] = { EMU_DST_DOCK_DAC1_LEFT1, /* 0 */ EMU_DST_DOCK_DAC1_RIGHT1, /* 1 */ @@ -216,6 +227,11 @@ EMU_DST_HANA_ADAT+7, /* 23 */ }; +/* + * Data destinations - HANA outputs going to Alice2 (audigy) for + * capture (EMU32 + I2S links) + * Each destination has an enum mixer control to choose a data source + */ static unsigned int emu1010_input_dst[] = { EMU_DST_ALICE2_EMU32_0, EMU_DST_ALICE2_EMU32_1, @@ -384,15 +400,7 @@ - -static int snd_emu1010_adc_pads_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_emu1010_adc_pads_info snd_ctl_boolean_mono_info static int snd_emu1010_adc_pads_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -440,14 +448,7 @@ EMU1010_ADC_PADS("ADC1 14dB PAD 0202 Capture Switch", EMU_HANA_0202_ADC_PAD1), }; -static int snd_emu1010_dac_pads_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_emu1010_dac_pads_info snd_ctl_boolean_mono_info static int snd_emu1010_dac_pads_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -500,17 +501,19 @@ static int snd_emu1010_internal_clock_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[2] = { - "44100", "48000" + static char *texts[4] = { + "44100", "48000", "SPDIF", "ADAT" }; - + 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; + uinfo->value.enumerated.items = 4; + 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_emu1010_internal_clock_get(struct snd_kcontrol *kcontrol, @@ -568,6 +571,44 @@ /* Unmute all */ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE ); break; + + case 2: /* Take clock from S/PDIF IN */ + /* Mute all */ + snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE ); + /* Default fallback clock 48kHz */ + snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_48K ); + /* Word Clock source, sync to S/PDIF input */ + snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, + EMU_HANA_WCLOCK_HANA_SPDIF_IN | EMU_HANA_WCLOCK_1X ); + /* Set LEDs on Audio Dock */ + snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, + EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_LOCK ); + /* FIXME: We should set EMU_HANA_DOCK_LEDS_2_LOCK only when clock signal is present and valid */ + /* Allow DLL to settle */ + msleep(10); + /* Unmute all */ + snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE ); + break; + + case 3: + /* Take clock from ADAT IN */ + /* Mute all */ + snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE ); + /* Default fallback clock 48kHz */ + snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_48K ); + /* Word Clock source, sync to ADAT input */ + snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, + EMU_HANA_WCLOCK_HANA_ADAT_IN | EMU_HANA_WCLOCK_1X ); + /* Set LEDs on Audio Dock */ + snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_LOCK ); + /* FIXME: We should set EMU_HANA_DOCK_LEDS_2_LOCK only when clock signal is present and valid */ + /* Allow DLL to settle */ + msleep(10); + /* Unmute all */ + snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE ); + + + break; } } return change; @@ -855,7 +896,7 @@ .access = SNDRV_CTL_ELEM_ACCESS_READ, .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), - .count = 4, + .count = 3, .info = snd_emu10k1_spdif_info, .get = snd_emu10k1_spdif_get_mask }; @@ -864,7 +905,7 @@ { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), - .count = 4, + .count = 3, .info = snd_emu10k1_spdif_info, .get = snd_emu10k1_spdif_get, .put = snd_emu10k1_spdif_put @@ -1310,14 +1351,7 @@ .put = snd_emu10k1_efx_attn_put }; -static int snd_emu10k1_shared_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_emu10k1_shared_spdif_info snd_ctl_boolean_mono_info static int snd_emu10k1_shared_spdif_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff -Nur sound/pci/emu10k1/emupcm.c sound/pci/emu10k1/emupcm.c --- sound/pci/emu10k1/emupcm.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/emu10k1/emupcm.c 2007-06-12 02:00:08.000000000 +0200 @@ -1233,24 +1233,26 @@ runtime->hw.rate_min = runtime->hw.rate_max = 48000; spin_lock_irq(&emu->reg_lock); if (emu->card_capabilities->emu1010) { - /* TODO + /* Nb. of channels has been increased to 16 */ + /* TODO * SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE * 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 = 8, - * channels_max = 8, + * channels_min = 16, + * channels_max = 16, * Need to add mixer control to fix sample rate * - * There are 16 mono channels of 16bits each. + * There are 32 mono channels of 16bits each. * 24bit Audio uses 2x channels over 16bit * 96kHz uses 2x channels over 48kHz * 192kHz uses 4x channels over 48kHz - * So, for 48kHz 24bit, one has 8 channels - * for 96kHz 24bit, one has 4 channels - * for 192kHz 24bit, one has 2 channels + * So, for 48kHz 24bit, one has 16 channels + * for 96kHz 24bit, one has 8 channels + * for 192kHz 24bit, one has 4 channels + * */ #if 1 switch (emu->emu1010.internal_clock) { @@ -1258,13 +1260,15 @@ /* For 44.1kHz */ runtime->hw.rates = SNDRV_PCM_RATE_44100; runtime->hw.rate_min = runtime->hw.rate_max = 44100; - runtime->hw.channels_min = runtime->hw.channels_max = 8; + runtime->hw.channels_min = + runtime->hw.channels_max = 16; break; case 1: /* For 48kHz */ runtime->hw.rates = SNDRV_PCM_RATE_48000; runtime->hw.rate_min = runtime->hw.rate_max = 48000; - runtime->hw.channels_min = runtime->hw.channels_max = 8; + runtime->hw.channels_min = + runtime->hw.channels_max = 16; break; }; #endif @@ -1282,7 +1286,7 @@ #endif runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE; /* efx_voices_mask[0] is expected to be zero - * efx_voices_mask[1] is expected to have 16bits set + * efx_voices_mask[1] is expected to have 32bits set */ } else { runtime->hw.channels_min = runtime->hw.channels_max = 0; @@ -1787,11 +1791,24 @@ /* emu->efx_voices_mask[0] = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A; */ if (emu->audigy) { emu->efx_voices_mask[0] = 0; - emu->efx_voices_mask[1] = 0xffff; + if (emu->card_capabilities->emu1010) + /* Pavel Hofman - 32 voices will be used for + * capture (write mode) - + * each bit = corresponding voice + */ + emu->efx_voices_mask[1] = 0xffffffff; + else + emu->efx_voices_mask[1] = 0xffff; } else { emu->efx_voices_mask[0] = 0xffff0000; emu->efx_voices_mask[1] = 0; } + /* For emu1010, the control has to set 32 upper bits (voices) + * out of the 64 bits (voices) to true for the 16-channels capture + * to work correctly. Correct A_FXWC2 initial value (0xffffffff) + * is already defined but the snd_emu10k1_pcm_efx_voices_mask + * control can override this register's value. + */ kctl = snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu); if (!kctl) return -ENOMEM; diff -Nur sound/pci/emu10k1/emuproc.c sound/pci/emu10k1/emuproc.c --- sound/pci/emu10k1/emuproc.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/emu10k1/emuproc.c 2007-07-27 02:00:07.000000000 +0200 @@ -240,8 +240,42 @@ struct snd_info_buffer *buffer) { struct snd_emu10k1 *emu = entry->private_data; - snd_emu10k1_proc_spdif_status(emu, buffer, "CD-ROM S/PDIF In", CDCS, CDSRCS); - snd_emu10k1_proc_spdif_status(emu, buffer, "Optical or Coax S/PDIF In", GPSCS, GPSRCS); + u32 value; + u32 value2; + unsigned long flags; + u32 rate; + + if (emu->card_capabilities->emu1010) { + spin_lock_irqsave(&emu->emu_lock, flags); + snd_emu1010_fpga_read(emu, 0x38, &value); + spin_unlock_irqrestore(&emu->emu_lock, flags); + if ((value & 0x1) == 0) { + spin_lock_irqsave(&emu->emu_lock, flags); + snd_emu1010_fpga_read(emu, 0x2a, &value); + snd_emu1010_fpga_read(emu, 0x2b, &value2); + spin_unlock_irqrestore(&emu->emu_lock, flags); + rate = 0x1770000 / (((value << 5) | value2)+1); + snd_iprintf(buffer, "ADAT Locked : %u\n", rate); + } else { + snd_iprintf(buffer, "ADAT Unlocked\n"); + } + spin_lock_irqsave(&emu->emu_lock, flags); + snd_emu1010_fpga_read(emu, 0x20, &value); + spin_unlock_irqrestore(&emu->emu_lock, flags); + if ((value & 0x4) == 0) { + spin_lock_irqsave(&emu->emu_lock, flags); + snd_emu1010_fpga_read(emu, 0x28, &value); + snd_emu1010_fpga_read(emu, 0x29, &value2); + spin_unlock_irqrestore(&emu->emu_lock, flags); + rate = 0x1770000 / (((value << 5) | value2)+1); + snd_iprintf(buffer, "SPDIF Locked : %d\n", rate); + } else { + snd_iprintf(buffer, "SPDIF Unlocked\n"); + } + } else { + snd_emu10k1_proc_spdif_status(emu, buffer, "CD-ROM S/PDIF In", CDCS, CDSRCS); + snd_emu10k1_proc_spdif_status(emu, buffer, "Optical or Coax S/PDIF In", GPSCS, GPSRCS); + } #if 0 val = snd_emu10k1_ptr_read(emu, ZVSRCS, 0); snd_iprintf(buffer, "\nZoomed Video\n"); @@ -379,20 +413,16 @@ struct snd_info_buffer *buffer) { struct snd_emu10k1 *emu = entry->private_data; - unsigned long value; + int value; unsigned long flags; - unsigned long regs; int i; snd_iprintf(buffer, "EMU1010 Registers:\n\n"); - for(i = 0; i < 0x30; i+=1) { + for(i = 0; i < 0x40; i+=1) { spin_lock_irqsave(&emu->emu_lock, flags); - regs=i+0x40; /* 0x40 upwards are registers. */ - outl(regs, emu->port + A_IOCFG); - outl(regs | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */ - value = inl(emu->port + A_IOCFG); + snd_emu1010_fpga_read(emu, i, &value); spin_unlock_irqrestore(&emu->emu_lock, flags); - snd_iprintf(buffer, "%02X: %08lX, %02lX\n", i, value, (value >> 8) & 0x7f); + snd_iprintf(buffer, "%02X: %08X, %02X\n", i, value, (value >> 8) & 0x7f); } } @@ -555,9 +585,9 @@ { struct snd_info_entry *entry; #ifdef CONFIG_SND_DEBUG - if ((emu->card_capabilities->emu1010) && - snd_card_proc_new(emu->card, "emu1010_regs", &entry)) { - snd_info_set_text_ops(entry, emu, snd_emu_proc_emu1010_reg_read); + if (emu->card_capabilities->emu1010) { + if (! snd_card_proc_new(emu->card, "emu1010_regs", &entry)) + snd_info_set_text_ops(entry, emu, snd_emu_proc_emu1010_reg_read); } if (! snd_card_proc_new(emu->card, "io_regs", &entry)) { snd_info_set_text_ops(entry, emu, snd_emu_proc_io_reg_read); diff -Nur sound/pci/emu10k1/io.c sound/pci/emu10k1/io.c --- sound/pci/emu10k1/io.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/emu10k1/io.c 2007-07-27 02:00:07.000000000 +0200 @@ -226,9 +226,9 @@ return 0; } -int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, int reg, int value) +int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value) { - if (reg < 0 || reg > 0x3f) + if (reg > 0x3f) return 1; reg += 0x40; /* 0x40 upwards are registers. */ if (value < 0 || value > 0x3f) /* 0 to 0x3f are values */ @@ -244,9 +244,9 @@ return 0; } -int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, int reg, int *value) +int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, u32 reg, u32 *value) { - if (reg < 0 || reg > 0x3f) + if (reg > 0x3f) return 1; reg += 0x40; /* 0x40 upwards are registers. */ outl(reg, emu->port + A_IOCFG); @@ -261,7 +261,7 @@ /* Each Destination has one and only one Source, * but one Source can feed any number of Destinations simultaneously. */ -int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, int dst, int src) +int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, u32 dst, u32 src) { snd_emu1010_fpga_write(emu, 0x00, ((dst >> 8) & 0x3f) ); snd_emu1010_fpga_write(emu, 0x01, (dst & 0x3f) ); diff -Nur sound/pci/emu10k1/p16v.c sound/pci/emu10k1/p16v.c --- sound/pci/emu10k1/p16v.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/emu10k1/p16v.c 2007-08-14 02:00:08.000000000 +0200 @@ -124,11 +124,12 @@ /* hardware definition */ static struct snd_pcm_hardware snd_p16v_playback_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_RESUME | - SNDRV_PCM_INFO_MMAP_VALID), + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_SYNC_START, .formats = SNDRV_PCM_FMTBIT_S32_LE, /* Only supports 24-bit samples padded to 32 bits. */ .rates = SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100, .rate_min = 44100, @@ -207,6 +208,11 @@ if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) return err; + runtime->sync.id32[0] = substream->pcm->card->number; + runtime->sync.id32[1] = 'P'; + runtime->sync.id32[2] = 16; + runtime->sync.id32[3] = 'V'; + return 0; } /* open_capture callback */ @@ -448,6 +454,9 @@ break; } snd_pcm_group_for_each_entry(s, substream) { + if (snd_pcm_substream_chip(s) != emu || + s->stream != SNDRV_PCM_STREAM_PLAYBACK) + continue; runtime = s->runtime; epcm = runtime->private_data; channel = substream->pcm->device-emu->p16v_device_offset; diff -Nur sound/pci/ens1370.c sound/pci/ens1370.c --- sound/pci/ens1370.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/ens1370.c 2007-08-21 17:37:19.000000000 +0200 @@ -1419,15 +1419,7 @@ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_es1371_spdif_info, \ .get = snd_es1371_spdif_get, .put = snd_es1371_spdif_put } -static int snd_es1371_spdif_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_es1371_spdif_info snd_ctl_boolean_mono_info static int snd_es1371_spdif_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1489,15 +1481,7 @@ }; -static int snd_es1373_rear_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_es1373_rear_info snd_ctl_boolean_mono_info static int snd_es1373_rear_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1542,15 +1526,7 @@ .put = snd_es1373_rear_put, }; -static int snd_es1373_line_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_es1373_line_info snd_ctl_boolean_mono_info static int snd_es1373_line_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1607,8 +1583,8 @@ unsigned char rev; /* revision */ }; -static int __devinit es1371_quirk_lookup(struct ensoniq *ensoniq, - struct es1371_quirk *list) +static int es1371_quirk_lookup(struct ensoniq *ensoniq, + struct es1371_quirk *list) { while (list->vid != (unsigned short)PCI_ANY_ID) { if (ensoniq->pci->vendor == list->vid && @@ -1707,15 +1683,7 @@ .get = snd_ensoniq_control_get, .put = snd_ensoniq_control_put, \ .private_value = mask } -static int snd_ensoniq_control_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_ensoniq_control_info snd_ctl_boolean_mono_info static int snd_ensoniq_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff -Nur sound/pci/ens1370.c~ sound/pci/ens1370.c~ --- sound/pci/ens1370.c~ 1970-01-01 01:00:00.000000000 +0100 +++ sound/pci/ens1370.c~ 2007-07-24 02:00:10.000000000 +0200 @@ -0,0 +1,2496 @@ +/* + * Driver for Ensoniq ES1370/ES1371 AudioPCI soundcard + * Copyright (c) by Jaroslav Kysela , + * Thomas Sailer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* Power-Management-Code ( CONFIG_PM ) + * for ens1371 only ( FIXME ) + * derived from cs4281.c, atiixp.c and via82xx.c + * using http://www.alsa-project.org/~iwai/writing-an-alsa-driver/c1540.htm + * by Kurt J. Bosch + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#ifdef CHIP1371 +#include +#else +#include +#endif +#include +#include + +#ifndef CHIP1371 +#undef CHIP1370 +#define CHIP1370 +#endif + +#ifdef CHIP1370 +#define DRIVER_NAME "ENS1370" +#else +#define DRIVER_NAME "ENS1371" +#endif + + +MODULE_AUTHOR("Jaroslav Kysela , Thomas Sailer "); +MODULE_LICENSE("GPL"); +#ifdef CHIP1370 +MODULE_DESCRIPTION("Ensoniq AudioPCI ES1370"); +MODULE_SUPPORTED_DEVICE("{{Ensoniq,AudioPCI-97 ES1370}," + "{Creative Labs,SB PCI64/128 (ES1370)}}"); +#endif +#ifdef CHIP1371 +MODULE_DESCRIPTION("Ensoniq/Creative AudioPCI ES1371+"); +MODULE_SUPPORTED_DEVICE("{{Ensoniq,AudioPCI ES1371/73}," + "{Ensoniq,AudioPCI ES1373}," + "{Creative Labs,Ectiva EV1938}," + "{Creative Labs,SB PCI64/128 (ES1371/73)}," + "{Creative Labs,Vibra PCI128}," + "{Ectiva,EV1938}}"); +#endif + +#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#define SUPPORT_JOYSTICK +#endif + +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 switches */ +#ifdef SUPPORT_JOYSTICK +#ifdef CHIP1371 +static int joystick_port[SNDRV_CARDS]; +#else +static int joystick[SNDRV_CARDS]; +#endif +#endif +#ifdef CHIP1371 +static int spdif[SNDRV_CARDS]; +static int lineio[SNDRV_CARDS]; +#endif + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for Ensoniq AudioPCI soundcard."); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for Ensoniq AudioPCI soundcard."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable Ensoniq AudioPCI soundcard."); +#ifdef SUPPORT_JOYSTICK +#ifdef CHIP1371 +module_param_array(joystick_port, int, NULL, 0444); +MODULE_PARM_DESC(joystick_port, "Joystick port address."); +#else +module_param_array(joystick, bool, NULL, 0444); +MODULE_PARM_DESC(joystick, "Enable joystick."); +#endif +#endif /* SUPPORT_JOYSTICK */ +#ifdef CHIP1371 +module_param_array(spdif, int, NULL, 0444); +MODULE_PARM_DESC(spdif, "S/PDIF output (-1 = none, 0 = auto, 1 = force)."); +module_param_array(lineio, int, NULL, 0444); +MODULE_PARM_DESC(lineio, "Line In to Rear Out (0 = auto, 1 = force)."); +#endif + +/* ES1371 chip ID */ +/* This is a little confusing because all ES1371 compatible chips have the + same DEVICE_ID, the only thing differentiating them is the REV_ID field. + This is only significant if you want to enable features on the later parts. + Yes, I know it's stupid and why didn't we use the sub IDs? +*/ +#define ES1371REV_ES1373_A 0x04 +#define ES1371REV_ES1373_B 0x06 +#define ES1371REV_CT5880_A 0x07 +#define CT5880REV_CT5880_C 0x02 +#define CT5880REV_CT5880_D 0x03 /* ??? -jk */ +#define CT5880REV_CT5880_E 0x04 /* mw */ +#define ES1371REV_ES1371_B 0x09 +#define EV1938REV_EV1938_A 0x00 +#define ES1371REV_ES1373_8 0x08 + +/* + * Direct registers + */ + +#define ES_REG(ensoniq, x) ((ensoniq)->port + ES_REG_##x) + +#define ES_REG_CONTROL 0x00 /* R/W: Interrupt/Chip select control register */ +#define ES_1370_ADC_STOP (1<<31) /* disable capture buffer transfers */ +#define ES_1370_XCTL1 (1<<30) /* general purpose output bit */ +#define ES_1373_BYPASS_P1 (1<<31) /* bypass SRC for PB1 */ +#define ES_1373_BYPASS_P2 (1<<30) /* bypass SRC for PB2 */ +#define ES_1373_BYPASS_R (1<<29) /* bypass SRC for REC */ +#define ES_1373_TEST_BIT (1<<28) /* should be set to 0 for normal operation */ +#define ES_1373_RECEN_B (1<<27) /* mix record with playback for I2S/SPDIF out */ +#define ES_1373_SPDIF_THRU (1<<26) /* 0 = SPDIF thru mode, 1 = SPDIF == dig out */ +#define ES_1371_JOY_ASEL(o) (((o)&0x03)<<24)/* joystick port mapping */ +#define ES_1371_JOY_ASELM (0x03<<24) /* mask for above */ +#define ES_1371_JOY_ASELI(i) (((i)>>24)&0x03) +#define ES_1371_GPIO_IN(i) (((i)>>20)&0x0f)/* GPIO in [3:0] pins - R/O */ +#define ES_1370_PCLKDIVO(o) (((o)&0x1fff)<<16)/* clock divide ratio for DAC2 */ +#define ES_1370_PCLKDIVM ((0x1fff)<<16) /* mask for above */ +#define ES_1370_PCLKDIVI(i) (((i)>>16)&0x1fff)/* clock divide ratio for DAC2 */ +#define ES_1371_GPIO_OUT(o) (((o)&0x0f)<<16)/* GPIO out [3:0] pins - W/R */ +#define ES_1371_GPIO_OUTM (0x0f<<16) /* mask for above */ +#define ES_MSFMTSEL (1<<15) /* MPEG serial data format; 0 = SONY, 1 = I2S */ +#define ES_1370_M_SBB (1<<14) /* clock source for DAC - 0 = clock generator; 1 = MPEG clocks */ +#define ES_1371_SYNC_RES (1<<14) /* Warm AC97 reset */ +#define ES_1370_WTSRSEL(o) (((o)&0x03)<<12)/* fixed frequency clock for DAC1 */ +#define ES_1370_WTSRSELM (0x03<<12) /* mask for above */ +#define ES_1371_ADC_STOP (1<<13) /* disable CCB transfer capture information */ +#define ES_1371_PWR_INTRM (1<<12) /* power level change interrupts enable */ +#define ES_1370_DAC_SYNC (1<<11) /* DAC's are synchronous */ +#define ES_1371_M_CB (1<<11) /* capture clock source; 0 = AC'97 ADC; 1 = I2S */ +#define ES_CCB_INTRM (1<<10) /* CCB voice interrupts enable */ +#define ES_1370_M_CB (1<<9) /* capture clock source; 0 = ADC; 1 = MPEG */ +#define ES_1370_XCTL0 (1<<8) /* generap purpose output bit */ +#define ES_1371_PDLEV(o) (((o)&0x03)<<8) /* current power down level */ +#define ES_1371_PDLEVM (0x03<<8) /* mask for above */ +#define ES_BREQ (1<<7) /* memory bus request enable */ +#define ES_DAC1_EN (1<<6) /* DAC1 playback channel enable */ +#define ES_DAC2_EN (1<<5) /* DAC2 playback channel enable */ +#define ES_ADC_EN (1<<4) /* ADC capture channel enable */ +#define ES_UART_EN (1<<3) /* UART enable */ +#define ES_JYSTK_EN (1<<2) /* Joystick module enable */ +#define ES_1370_CDC_EN (1<<1) /* Codec interface enable */ +#define ES_1371_XTALCKDIS (1<<1) /* Xtal clock disable */ +#define ES_1370_SERR_DISABLE (1<<0) /* PCI serr signal disable */ +#define ES_1371_PCICLKDIS (1<<0) /* PCI clock disable */ +#define ES_REG_STATUS 0x04 /* R/O: Interrupt/Chip select status register */ +#define ES_INTR (1<<31) /* Interrupt is pending */ +#define ES_1371_ST_AC97_RST (1<<29) /* CT5880 AC'97 Reset bit */ +#define ES_1373_REAR_BIT27 (1<<27) /* rear bits: 000 - front, 010 - mirror, 101 - separate */ +#define ES_1373_REAR_BIT26 (1<<26) +#define ES_1373_REAR_BIT24 (1<<24) +#define ES_1373_GPIO_INT_EN(o)(((o)&0x0f)<<20)/* GPIO [3:0] pins - interrupt enable */ +#define ES_1373_SPDIF_EN (1<<18) /* SPDIF enable */ +#define ES_1373_SPDIF_TEST (1<<17) /* SPDIF test */ +#define ES_1371_TEST (1<<16) /* test ASIC */ +#define ES_1373_GPIO_INT(i) (((i)&0x0f)>>12)/* GPIO [3:0] pins - interrupt pending */ +#define ES_1370_CSTAT (1<<10) /* CODEC is busy or register write in progress */ +#define ES_1370_CBUSY (1<<9) /* CODEC is busy */ +#define ES_1370_CWRIP (1<<8) /* CODEC register write in progress */ +#define ES_1371_SYNC_ERR (1<<8) /* CODEC synchronization error occurred */ +#define ES_1371_VC(i) (((i)>>6)&0x03) /* voice code from CCB module */ +#define ES_1370_VC(i) (((i)>>5)&0x03) /* voice code from CCB module */ +#define ES_1371_MPWR (1<<5) /* power level interrupt pending */ +#define ES_MCCB (1<<4) /* CCB interrupt pending */ +#define ES_UART (1<<3) /* UART interrupt pending */ +#define ES_DAC1 (1<<2) /* DAC1 channel interrupt pending */ +#define ES_DAC2 (1<<1) /* DAC2 channel interrupt pending */ +#define ES_ADC (1<<0) /* ADC channel interrupt pending */ +#define ES_REG_UART_DATA 0x08 /* R/W: UART data register */ +#define ES_REG_UART_STATUS 0x09 /* R/O: UART status register */ +#define ES_RXINT (1<<7) /* RX interrupt occurred */ +#define ES_TXINT (1<<2) /* TX interrupt occurred */ +#define ES_TXRDY (1<<1) /* transmitter ready */ +#define ES_RXRDY (1<<0) /* receiver ready */ +#define ES_REG_UART_CONTROL 0x09 /* W/O: UART control register */ +#define ES_RXINTEN (1<<7) /* RX interrupt enable */ +#define ES_TXINTENO(o) (((o)&0x03)<<5) /* TX interrupt enable */ +#define ES_TXINTENM (0x03<<5) /* mask for above */ +#define ES_TXINTENI(i) (((i)>>5)&0x03) +#define ES_CNTRL(o) (((o)&0x03)<<0) /* control */ +#define ES_CNTRLM (0x03<<0) /* mask for above */ +#define ES_REG_UART_RES 0x0a /* R/W: UART reserver register */ +#define ES_TEST_MODE (1<<0) /* test mode enabled */ +#define ES_REG_MEM_PAGE 0x0c /* R/W: Memory page register */ +#define ES_MEM_PAGEO(o) (((o)&0x0f)<<0) /* memory page select - out */ +#define ES_MEM_PAGEM (0x0f<<0) /* mask for above */ +#define ES_MEM_PAGEI(i) (((i)>>0)&0x0f) /* memory page select - in */ +#define ES_REG_1370_CODEC 0x10 /* W/O: Codec write register address */ +#define ES_1370_CODEC_WRITE(a,d) ((((a)&0xff)<<8)|(((d)&0xff)<<0)) +#define ES_REG_1371_CODEC 0x14 /* W/R: Codec Read/Write register address */ +#define ES_1371_CODEC_RDY (1<<31) /* codec ready */ +#define ES_1371_CODEC_WIP (1<<30) /* codec register access in progress */ +#define ES_1371_CODEC_PIRD (1<<23) /* codec read/write select register */ +#define ES_1371_CODEC_WRITE(a,d) ((((a)&0x7f)<<16)|(((d)&0xffff)<<0)) +#define ES_1371_CODEC_READS(a) ((((a)&0x7f)<<16)|ES_1371_CODEC_PIRD) +#define ES_1371_CODEC_READ(i) (((i)>>0)&0xffff) + +#define ES_REG_1371_SMPRATE 0x10 /* W/R: Codec rate converter interface register */ +#define ES_1371_SRC_RAM_ADDRO(o) (((o)&0x7f)<<25)/* address of the sample rate converter */ +#define ES_1371_SRC_RAM_ADDRM (0x7f<<25) /* mask for above */ +#define ES_1371_SRC_RAM_ADDRI(i) (((i)>>25)&0x7f)/* address of the sample rate converter */ +#define ES_1371_SRC_RAM_WE (1<<24) /* R/W: read/write control for sample rate converter */ +#define ES_1371_SRC_RAM_BUSY (1<<23) /* R/O: sample rate memory is busy */ +#define ES_1371_SRC_DISABLE (1<<22) /* sample rate converter disable */ +#define ES_1371_DIS_P1 (1<<21) /* playback channel 1 accumulator update disable */ +#define ES_1371_DIS_P2 (1<<20) /* playback channel 1 accumulator update disable */ +#define ES_1371_DIS_R1 (1<<19) /* capture channel accumulator update disable */ +#define ES_1371_SRC_RAM_DATAO(o) (((o)&0xffff)<<0)/* current value of the sample rate converter */ +#define ES_1371_SRC_RAM_DATAM (0xffff<<0) /* mask for above */ +#define ES_1371_SRC_RAM_DATAI(i) (((i)>>0)&0xffff)/* current value of the sample rate converter */ + +#define ES_REG_1371_LEGACY 0x18 /* W/R: Legacy control/status register */ +#define ES_1371_JFAST (1<<31) /* fast joystick timing */ +#define ES_1371_HIB (1<<30) /* host interrupt blocking enable */ +#define ES_1371_VSB (1<<29) /* SB; 0 = addr 0x220xH, 1 = 0x22FxH */ +#define ES_1371_VMPUO(o) (((o)&0x03)<<27)/* base register address; 0 = 0x320xH; 1 = 0x330xH; 2 = 0x340xH; 3 = 0x350xH */ +#define ES_1371_VMPUM (0x03<<27) /* mask for above */ +#define ES_1371_VMPUI(i) (((i)>>27)&0x03)/* base register address */ +#define ES_1371_VCDCO(o) (((o)&0x03)<<25)/* CODEC; 0 = 0x530xH; 1 = undefined; 2 = 0xe80xH; 3 = 0xF40xH */ +#define ES_1371_VCDCM (0x03<<25) /* mask for above */ +#define ES_1371_VCDCI(i) (((i)>>25)&0x03)/* CODEC address */ +#define ES_1371_FIRQ (1<<24) /* force an interrupt */ +#define ES_1371_SDMACAP (1<<23) /* enable event capture for slave DMA controller */ +#define ES_1371_SPICAP (1<<22) /* enable event capture for slave IRQ controller */ +#define ES_1371_MDMACAP (1<<21) /* enable event capture for master DMA controller */ +#define ES_1371_MPICAP (1<<20) /* enable event capture for master IRQ controller */ +#define ES_1371_ADCAP (1<<19) /* enable event capture for ADLIB register; 0x388xH */ +#define ES_1371_SVCAP (1<<18) /* enable event capture for SB registers */ +#define ES_1371_CDCCAP (1<<17) /* enable event capture for CODEC registers */ +#define ES_1371_BACAP (1<<16) /* enable event capture for SoundScape base address */ +#define ES_1371_EXI(i) (((i)>>8)&0x07) /* event number */ +#define ES_1371_AI(i) (((i)>>3)&0x1f) /* event significant I/O address */ +#define ES_1371_WR (1<<2) /* event capture; 0 = read; 1 = write */ +#define ES_1371_LEGINT (1<<0) /* interrupt for legacy events; 0 = interrupt did occur */ + +#define ES_REG_CHANNEL_STATUS 0x1c /* R/W: first 32-bits from S/PDIF channel status block, es1373 */ + +#define ES_REG_SERIAL 0x20 /* R/W: Serial interface control register */ +#define ES_1371_DAC_TEST (1<<22) /* DAC test mode enable */ +#define ES_P2_END_INCO(o) (((o)&0x07)<<19)/* binary offset value to increment / loop end */ +#define ES_P2_END_INCM (0x07<<19) /* mask for above */ +#define ES_P2_END_INCI(i) (((i)>>16)&0x07)/* binary offset value to increment / loop end */ +#define ES_P2_ST_INCO(o) (((o)&0x07)<<16)/* binary offset value to increment / start */ +#define ES_P2_ST_INCM (0x07<<16) /* mask for above */ +#define ES_P2_ST_INCI(i) (((i)<<16)&0x07)/* binary offset value to increment / start */ +#define ES_R1_LOOP_SEL (1<<15) /* ADC; 0 - loop mode; 1 = stop mode */ +#define ES_P2_LOOP_SEL (1<<14) /* DAC2; 0 - loop mode; 1 = stop mode */ +#define ES_P1_LOOP_SEL (1<<13) /* DAC1; 0 - loop mode; 1 = stop mode */ +#define ES_P2_PAUSE (1<<12) /* DAC2; 0 - play mode; 1 = pause mode */ +#define ES_P1_PAUSE (1<<11) /* DAC1; 0 - play mode; 1 = pause mode */ +#define ES_R1_INT_EN (1<<10) /* ADC interrupt enable */ +#define ES_P2_INT_EN (1<<9) /* DAC2 interrupt enable */ +#define ES_P1_INT_EN (1<<8) /* DAC1 interrupt enable */ +#define ES_P1_SCT_RLD (1<<7) /* force sample counter reload for DAC1 */ +#define ES_P2_DAC_SEN (1<<6) /* when stop mode: 0 - DAC2 play back zeros; 1 = DAC2 play back last sample */ +#define ES_R1_MODEO(o) (((o)&0x03)<<4) /* ADC mode; 0 = 8-bit mono; 1 = 8-bit stereo; 2 = 16-bit mono; 3 = 16-bit stereo */ +#define ES_R1_MODEM (0x03<<4) /* mask for above */ +#define ES_R1_MODEI(i) (((i)>>4)&0x03) +#define ES_P2_MODEO(o) (((o)&0x03)<<2) /* DAC2 mode; -- '' -- */ +#define ES_P2_MODEM (0x03<<2) /* mask for above */ +#define ES_P2_MODEI(i) (((i)>>2)&0x03) +#define ES_P1_MODEO(o) (((o)&0x03)<<0) /* DAC1 mode; -- '' -- */ +#define ES_P1_MODEM (0x03<<0) /* mask for above */ +#define ES_P1_MODEI(i) (((i)>>0)&0x03) + +#define ES_REG_DAC1_COUNT 0x24 /* R/W: DAC1 sample count register */ +#define ES_REG_DAC2_COUNT 0x28 /* R/W: DAC2 sample count register */ +#define ES_REG_ADC_COUNT 0x2c /* R/W: ADC sample count register */ +#define ES_REG_CURR_COUNT(i) (((i)>>16)&0xffff) +#define ES_REG_COUNTO(o) (((o)&0xffff)<<0) +#define ES_REG_COUNTM (0xffff<<0) +#define ES_REG_COUNTI(i) (((i)>>0)&0xffff) + +#define ES_REG_DAC1_FRAME 0x30 /* R/W: PAGE 0x0c; DAC1 frame address */ +#define ES_REG_DAC1_SIZE 0x34 /* R/W: PAGE 0x0c; DAC1 frame size */ +#define ES_REG_DAC2_FRAME 0x38 /* R/W: PAGE 0x0c; DAC2 frame address */ +#define ES_REG_DAC2_SIZE 0x3c /* R/W: PAGE 0x0c; DAC2 frame size */ +#define ES_REG_ADC_FRAME 0x30 /* R/W: PAGE 0x0d; ADC frame address */ +#define ES_REG_ADC_SIZE 0x34 /* R/W: PAGE 0x0d; ADC frame size */ +#define ES_REG_FCURR_COUNTO(o) (((o)&0xffff)<<16) +#define ES_REG_FCURR_COUNTM (0xffff<<16) +#define ES_REG_FCURR_COUNTI(i) (((i)>>14)&0x3fffc) +#define ES_REG_FSIZEO(o) (((o)&0xffff)<<0) +#define ES_REG_FSIZEM (0xffff<<0) +#define ES_REG_FSIZEI(i) (((i)>>0)&0xffff) +#define ES_REG_PHANTOM_FRAME 0x38 /* R/W: PAGE 0x0d: phantom frame address */ +#define ES_REG_PHANTOM_COUNT 0x3c /* R/W: PAGE 0x0d: phantom frame count */ + +#define ES_REG_UART_FIFO 0x30 /* R/W: PAGE 0x0e; UART FIFO register */ +#define ES_REG_UF_VALID (1<<8) +#define ES_REG_UF_BYTEO(o) (((o)&0xff)<<0) +#define ES_REG_UF_BYTEM (0xff<<0) +#define ES_REG_UF_BYTEI(i) (((i)>>0)&0xff) + + +/* + * Pages + */ + +#define ES_PAGE_DAC 0x0c +#define ES_PAGE_ADC 0x0d +#define ES_PAGE_UART 0x0e +#define ES_PAGE_UART1 0x0f + +/* + * Sample rate converter addresses + */ + +#define ES_SMPREG_DAC1 0x70 +#define ES_SMPREG_DAC2 0x74 +#define ES_SMPREG_ADC 0x78 +#define ES_SMPREG_VOL_ADC 0x6c +#define ES_SMPREG_VOL_DAC1 0x7c +#define ES_SMPREG_VOL_DAC2 0x7e +#define ES_SMPREG_TRUNC_N 0x00 +#define ES_SMPREG_INT_REGS 0x01 +#define ES_SMPREG_ACCUM_FRAC 0x02 +#define ES_SMPREG_VFREQ_FRAC 0x03 + +/* + * Some contants + */ + +#define ES_1370_SRCLOCK 1411200 +#define ES_1370_SRTODIV(x) (ES_1370_SRCLOCK/(x)-2) + +/* + * Open modes + */ + +#define ES_MODE_PLAY1 0x0001 +#define ES_MODE_PLAY2 0x0002 +#define ES_MODE_CAPTURE 0x0004 + +#define ES_MODE_OUTPUT 0x0001 /* for MIDI */ +#define ES_MODE_INPUT 0x0002 /* for MIDI */ + +/* + + */ + +struct ensoniq { + spinlock_t reg_lock; + struct mutex src_mutex; + + int irq; + + unsigned long playback1size; + unsigned long playback2size; + unsigned long capture3size; + + unsigned long port; + unsigned int mode; + unsigned int uartm; /* UART mode */ + + unsigned int ctrl; /* control register */ + unsigned int sctrl; /* serial control register */ + unsigned int cssr; /* control status register */ + unsigned int uartc; /* uart control register */ + unsigned int rev; /* chip revision */ + + union { +#ifdef CHIP1371 + struct { + struct snd_ac97 *ac97; + } es1371; +#else + struct { + int pclkdiv_lock; + struct snd_ak4531 *ak4531; + } es1370; +#endif + } u; + + struct pci_dev *pci; + struct snd_card *card; + struct snd_pcm *pcm1; /* DAC1/ADC PCM */ + struct snd_pcm *pcm2; /* DAC2 PCM */ + struct snd_pcm_substream *playback1_substream; + struct snd_pcm_substream *playback2_substream; + struct snd_pcm_substream *capture_substream; + unsigned int p1_dma_size; + unsigned int p2_dma_size; + unsigned int c_dma_size; + unsigned int p1_period_size; + unsigned int p2_period_size; + unsigned int c_period_size; + struct snd_rawmidi *rmidi; + struct snd_rawmidi_substream *midi_input; + struct snd_rawmidi_substream *midi_output; + + unsigned int spdif; + unsigned int spdif_default; + unsigned int spdif_stream; + +#ifdef CHIP1370 + struct snd_dma_buffer dma_bug; +#endif + +#ifdef SUPPORT_JOYSTICK + struct gameport *gameport; +#endif +}; + +static irqreturn_t snd_audiopci_interrupt(int irq, void *dev_id); + +static struct pci_device_id snd_audiopci_ids[] = { +#ifdef CHIP1370 + { 0x1274, 0x5000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ES1370 */ +#endif +#ifdef CHIP1371 + { 0x1274, 0x1371, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ES1371 */ + { 0x1274, 0x5880, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* ES1373 - CT5880 */ + { 0x1102, 0x8938, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Ectiva EV1938 */ +#endif + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_audiopci_ids); + +/* + * constants + */ + +#define POLL_COUNT 0xa000 + +#ifdef CHIP1370 +static unsigned int snd_es1370_fixed_rates[] = + {5512, 11025, 22050, 44100}; +static struct snd_pcm_hw_constraint_list snd_es1370_hw_constraints_rates = { + .count = 4, + .list = snd_es1370_fixed_rates, + .mask = 0, +}; +static struct snd_ratnum es1370_clock = { + .num = ES_1370_SRCLOCK, + .den_min = 29, + .den_max = 353, + .den_step = 1, +}; +static struct snd_pcm_hw_constraint_ratnums snd_es1370_hw_constraints_clock = { + .nrats = 1, + .rats = &es1370_clock, +}; +#else +static struct snd_ratden es1371_dac_clock = { + .num_min = 3000 * (1 << 15), + .num_max = 48000 * (1 << 15), + .num_step = 3000, + .den = 1 << 15, +}; +static struct snd_pcm_hw_constraint_ratdens snd_es1371_hw_constraints_dac_clock = { + .nrats = 1, + .rats = &es1371_dac_clock, +}; +static struct snd_ratnum es1371_adc_clock = { + .num = 48000 << 15, + .den_min = 32768, + .den_max = 393216, + .den_step = 1, +}; +static struct snd_pcm_hw_constraint_ratnums snd_es1371_hw_constraints_adc_clock = { + .nrats = 1, + .rats = &es1371_adc_clock, +}; +#endif +static const unsigned int snd_ensoniq_sample_shift[] = + {0, 1, 1, 2}; + +/* + * common I/O routines + */ + +#ifdef CHIP1371 + +static unsigned int snd_es1371_wait_src_ready(struct ensoniq * ensoniq) +{ + unsigned int t, r = 0; + + for (t = 0; t < POLL_COUNT; t++) { + r = inl(ES_REG(ensoniq, 1371_SMPRATE)); + if ((r & ES_1371_SRC_RAM_BUSY) == 0) + return r; + cond_resched(); + } + snd_printk(KERN_ERR "wait source ready timeout 0x%lx [0x%x]\n", + ES_REG(ensoniq, 1371_SMPRATE), r); + return 0; +} + +static unsigned int snd_es1371_src_read(struct ensoniq * ensoniq, unsigned short reg) +{ + unsigned int temp, i, orig, r; + + /* wait for ready */ + temp = orig = snd_es1371_wait_src_ready(ensoniq); + + /* expose the SRC state bits */ + r = temp & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | + ES_1371_DIS_P2 | ES_1371_DIS_R1); + r |= ES_1371_SRC_RAM_ADDRO(reg) | 0x10000; + outl(r, ES_REG(ensoniq, 1371_SMPRATE)); + + /* now, wait for busy and the correct time to read */ + temp = snd_es1371_wait_src_ready(ensoniq); + + if ((temp & 0x00870000) != 0x00010000) { + /* wait for the right state */ + for (i = 0; i < POLL_COUNT; i++) { + temp = inl(ES_REG(ensoniq, 1371_SMPRATE)); + if ((temp & 0x00870000) == 0x00010000) + break; + } + } + + /* hide the state bits */ + r = orig & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | + ES_1371_DIS_P2 | ES_1371_DIS_R1); + r |= ES_1371_SRC_RAM_ADDRO(reg); + outl(r, ES_REG(ensoniq, 1371_SMPRATE)); + + return temp; +} + +static void snd_es1371_src_write(struct ensoniq * ensoniq, + unsigned short reg, unsigned short data) +{ + unsigned int r; + + r = snd_es1371_wait_src_ready(ensoniq) & + (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | + ES_1371_DIS_P2 | ES_1371_DIS_R1); + r |= ES_1371_SRC_RAM_ADDRO(reg) | ES_1371_SRC_RAM_DATAO(data); + outl(r | ES_1371_SRC_RAM_WE, ES_REG(ensoniq, 1371_SMPRATE)); +} + +#endif /* CHIP1371 */ + +#ifdef CHIP1370 + +static void snd_es1370_codec_write(struct snd_ak4531 *ak4531, + unsigned short reg, unsigned short val) +{ + struct ensoniq *ensoniq = ak4531->private_data; + unsigned long end_time = jiffies + HZ / 10; + +#if 0 + printk("CODEC WRITE: reg = 0x%x, val = 0x%x (0x%x), creg = 0x%x\n", + reg, val, ES_1370_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1370_CODEC)); +#endif + do { + if (!(inl(ES_REG(ensoniq, STATUS)) & ES_1370_CSTAT)) { + outw(ES_1370_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1370_CODEC)); + return; + } + schedule_timeout_uninterruptible(1); + } while (time_after(end_time, jiffies)); + snd_printk(KERN_ERR "codec write timeout, status = 0x%x\n", + inl(ES_REG(ensoniq, STATUS))); +} + +#endif /* CHIP1370 */ + +#ifdef CHIP1371 + +static void snd_es1371_codec_write(struct snd_ac97 *ac97, + unsigned short reg, unsigned short val) +{ + struct ensoniq *ensoniq = ac97->private_data; + unsigned int t, x; + + mutex_lock(&ensoniq->src_mutex); + for (t = 0; t < POLL_COUNT; t++) { + if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) { + /* save the current state for latter */ + x = snd_es1371_wait_src_ready(ensoniq); + outl((x & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | + ES_1371_DIS_P2 | ES_1371_DIS_R1)) | 0x00010000, + ES_REG(ensoniq, 1371_SMPRATE)); + /* wait for not busy (state 0) first to avoid + transition states */ + for (t = 0; t < POLL_COUNT; t++) { + if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == + 0x00000000) + break; + } + /* wait for a SAFE time to write addr/data and then do it, dammit */ + for (t = 0; t < POLL_COUNT; t++) { + if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == + 0x00010000) + break; + } + outl(ES_1371_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1371_CODEC)); + /* restore SRC reg */ + snd_es1371_wait_src_ready(ensoniq); + outl(x, ES_REG(ensoniq, 1371_SMPRATE)); + mutex_unlock(&ensoniq->src_mutex); + return; + } + } + mutex_unlock(&ensoniq->src_mutex); + snd_printk(KERN_ERR "codec write timeout at 0x%lx [0x%x]\n", + ES_REG(ensoniq, 1371_CODEC), inl(ES_REG(ensoniq, 1371_CODEC))); +} + +static unsigned short snd_es1371_codec_read(struct snd_ac97 *ac97, + unsigned short reg) +{ + struct ensoniq *ensoniq = ac97->private_data; + unsigned int t, x, fail = 0; + + __again: + mutex_lock(&ensoniq->src_mutex); + for (t = 0; t < POLL_COUNT; t++) { + if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) { + /* save the current state for latter */ + x = snd_es1371_wait_src_ready(ensoniq); + outl((x & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | + ES_1371_DIS_P2 | ES_1371_DIS_R1)) | 0x00010000, + ES_REG(ensoniq, 1371_SMPRATE)); + /* wait for not busy (state 0) first to avoid + transition states */ + for (t = 0; t < POLL_COUNT; t++) { + if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == + 0x00000000) + break; + } + /* wait for a SAFE time to write addr/data and then do it, dammit */ + for (t = 0; t < POLL_COUNT; t++) { + if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == + 0x00010000) + break; + } + outl(ES_1371_CODEC_READS(reg), ES_REG(ensoniq, 1371_CODEC)); + /* restore SRC reg */ + snd_es1371_wait_src_ready(ensoniq); + outl(x, ES_REG(ensoniq, 1371_SMPRATE)); + /* wait for WIP again */ + for (t = 0; t < POLL_COUNT; t++) { + if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) + break; + } + /* now wait for the stinkin' data (RDY) */ + for (t = 0; t < POLL_COUNT; t++) { + if ((x = inl(ES_REG(ensoniq, 1371_CODEC))) & ES_1371_CODEC_RDY) { + mutex_unlock(&ensoniq->src_mutex); + return ES_1371_CODEC_READ(x); + } + } + mutex_unlock(&ensoniq->src_mutex); + if (++fail > 10) { + snd_printk(KERN_ERR "codec read timeout (final) " + "at 0x%lx, reg = 0x%x [0x%x]\n", + ES_REG(ensoniq, 1371_CODEC), reg, + inl(ES_REG(ensoniq, 1371_CODEC))); + return 0; + } + goto __again; + } + } + mutex_unlock(&ensoniq->src_mutex); + snd_printk(KERN_ERR "es1371: codec read timeout at 0x%lx [0x%x]\n", + ES_REG(ensoniq, 1371_CODEC), inl(ES_REG(ensoniq, 1371_CODEC))); + return 0; +} + +static void snd_es1371_codec_wait(struct snd_ac97 *ac97) +{ + msleep(750); + snd_es1371_codec_read(ac97, AC97_RESET); + snd_es1371_codec_read(ac97, AC97_VENDOR_ID1); + snd_es1371_codec_read(ac97, AC97_VENDOR_ID2); + msleep(50); +} + +static void snd_es1371_adc_rate(struct ensoniq * ensoniq, unsigned int rate) +{ + unsigned int n, truncm, freq, result; + + mutex_lock(&ensoniq->src_mutex); + n = rate / 3000; + if ((1 << n) & ((1 << 15) | (1 << 13) | (1 << 11) | (1 << 9))) + n--; + truncm = (21 * n - 1) | 1; + freq = ((48000UL << 15) / rate) * n; + result = (48000UL << 15) / (freq / n); + if (rate >= 24000) { + if (truncm > 239) + truncm = 239; + snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N, + (((239 - truncm) >> 1) << 9) | (n << 4)); + } else { + if (truncm > 119) + truncm = 119; + snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N, + 0x8000 | (((119 - truncm) >> 1) << 9) | (n << 4)); + } + snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_INT_REGS, + (snd_es1371_src_read(ensoniq, ES_SMPREG_ADC + + ES_SMPREG_INT_REGS) & 0x00ff) | + ((freq >> 5) & 0xfc00)); + snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC, n << 8); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC + 1, n << 8); + mutex_unlock(&ensoniq->src_mutex); +} + +static void snd_es1371_dac1_rate(struct ensoniq * ensoniq, unsigned int rate) +{ + unsigned int freq, r; + + mutex_lock(&ensoniq->src_mutex); + freq = ((rate << 15) + 1500) / 3000; + r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | + ES_1371_DIS_P2 | ES_1371_DIS_R1)) | + ES_1371_DIS_P1; + outl(r, ES_REG(ensoniq, 1371_SMPRATE)); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS, + (snd_es1371_src_read(ensoniq, ES_SMPREG_DAC1 + + ES_SMPREG_INT_REGS) & 0x00ff) | + ((freq >> 5) & 0xfc00)); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff); + r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | + ES_1371_DIS_P2 | ES_1371_DIS_R1)); + outl(r, ES_REG(ensoniq, 1371_SMPRATE)); + mutex_unlock(&ensoniq->src_mutex); +} + +static void snd_es1371_dac2_rate(struct ensoniq * ensoniq, unsigned int rate) +{ + unsigned int freq, r; + + mutex_lock(&ensoniq->src_mutex); + freq = ((rate << 15) + 1500) / 3000; + r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | + ES_1371_DIS_P1 | ES_1371_DIS_R1)) | + ES_1371_DIS_P2; + outl(r, ES_REG(ensoniq, 1371_SMPRATE)); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS, + (snd_es1371_src_read(ensoniq, ES_SMPREG_DAC2 + + ES_SMPREG_INT_REGS) & 0x00ff) | + ((freq >> 5) & 0xfc00)); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_VFREQ_FRAC, + freq & 0x7fff); + r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | + ES_1371_DIS_P1 | ES_1371_DIS_R1)); + outl(r, ES_REG(ensoniq, 1371_SMPRATE)); + mutex_unlock(&ensoniq->src_mutex); +} + +#endif /* CHIP1371 */ + +static int snd_ensoniq_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct ensoniq *ensoniq = snd_pcm_substream_chip(substream); + switch (cmd) { + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + { + unsigned int what = 0; + struct snd_pcm_substream *s; + snd_pcm_group_for_each_entry(s, substream) { + if (s == ensoniq->playback1_substream) { + what |= ES_P1_PAUSE; + snd_pcm_trigger_done(s, substream); + } else if (s == ensoniq->playback2_substream) { + what |= ES_P2_PAUSE; + snd_pcm_trigger_done(s, substream); + } else if (s == ensoniq->capture_substream) + return -EINVAL; + } + spin_lock(&ensoniq->reg_lock); + if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) + ensoniq->sctrl |= what; + else + ensoniq->sctrl &= ~what; + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + spin_unlock(&ensoniq->reg_lock); + break; + } + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_STOP: + { + unsigned int what = 0; + struct snd_pcm_substream *s; + snd_pcm_group_for_each_entry(s, substream) { + if (s == ensoniq->playback1_substream) { + what |= ES_DAC1_EN; + snd_pcm_trigger_done(s, substream); + } else if (s == ensoniq->playback2_substream) { + what |= ES_DAC2_EN; + snd_pcm_trigger_done(s, substream); + } else if (s == ensoniq->capture_substream) { + what |= ES_ADC_EN; + snd_pcm_trigger_done(s, substream); + } + } + spin_lock(&ensoniq->reg_lock); + if (cmd == SNDRV_PCM_TRIGGER_START) + ensoniq->ctrl |= what; + else + ensoniq->ctrl &= ~what; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + spin_unlock(&ensoniq->reg_lock); + break; + } + default: + return -EINVAL; + } + return 0; +} + +/* + * PCM part + */ + +static int snd_ensoniq_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_ensoniq_hw_free(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_ensoniq_playback1_prepare(struct snd_pcm_substream *substream) +{ + struct ensoniq *ensoniq = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int mode = 0; + + ensoniq->p1_dma_size = snd_pcm_lib_buffer_bytes(substream); + ensoniq->p1_period_size = snd_pcm_lib_period_bytes(substream); + if (snd_pcm_format_width(runtime->format) == 16) + mode |= 0x02; + if (runtime->channels > 1) + mode |= 0x01; + spin_lock_irq(&ensoniq->reg_lock); + ensoniq->ctrl &= ~ES_DAC1_EN; +#ifdef CHIP1371 + /* 48k doesn't need SRC (it breaks AC3-passthru) */ + if (runtime->rate == 48000) + ensoniq->ctrl |= ES_1373_BYPASS_P1; + else + ensoniq->ctrl &= ~ES_1373_BYPASS_P1; +#endif + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE)); + outl(runtime->dma_addr, ES_REG(ensoniq, DAC1_FRAME)); + outl((ensoniq->p1_dma_size >> 2) - 1, ES_REG(ensoniq, DAC1_SIZE)); + ensoniq->sctrl &= ~(ES_P1_LOOP_SEL | ES_P1_PAUSE | ES_P1_SCT_RLD | ES_P1_MODEM); + ensoniq->sctrl |= ES_P1_INT_EN | ES_P1_MODEO(mode); + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + outl((ensoniq->p1_period_size >> snd_ensoniq_sample_shift[mode]) - 1, + ES_REG(ensoniq, DAC1_COUNT)); +#ifdef CHIP1370 + ensoniq->ctrl &= ~ES_1370_WTSRSELM; + switch (runtime->rate) { + case 5512: ensoniq->ctrl |= ES_1370_WTSRSEL(0); break; + case 11025: ensoniq->ctrl |= ES_1370_WTSRSEL(1); break; + case 22050: ensoniq->ctrl |= ES_1370_WTSRSEL(2); break; + case 44100: ensoniq->ctrl |= ES_1370_WTSRSEL(3); break; + default: snd_BUG(); + } +#endif + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + spin_unlock_irq(&ensoniq->reg_lock); +#ifndef CHIP1370 + snd_es1371_dac1_rate(ensoniq, runtime->rate); +#endif + return 0; +} + +static int snd_ensoniq_playback2_prepare(struct snd_pcm_substream *substream) +{ + struct ensoniq *ensoniq = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int mode = 0; + + ensoniq->p2_dma_size = snd_pcm_lib_buffer_bytes(substream); + ensoniq->p2_period_size = snd_pcm_lib_period_bytes(substream); + if (snd_pcm_format_width(runtime->format) == 16) + mode |= 0x02; + if (runtime->channels > 1) + mode |= 0x01; + spin_lock_irq(&ensoniq->reg_lock); + ensoniq->ctrl &= ~ES_DAC2_EN; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE)); + outl(runtime->dma_addr, ES_REG(ensoniq, DAC2_FRAME)); + outl((ensoniq->p2_dma_size >> 2) - 1, ES_REG(ensoniq, DAC2_SIZE)); + ensoniq->sctrl &= ~(ES_P2_LOOP_SEL | ES_P2_PAUSE | ES_P2_DAC_SEN | + ES_P2_END_INCM | ES_P2_ST_INCM | ES_P2_MODEM); + ensoniq->sctrl |= ES_P2_INT_EN | ES_P2_MODEO(mode) | + ES_P2_END_INCO(mode & 2 ? 2 : 1) | ES_P2_ST_INCO(0); + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + outl((ensoniq->p2_period_size >> snd_ensoniq_sample_shift[mode]) - 1, + ES_REG(ensoniq, DAC2_COUNT)); +#ifdef CHIP1370 + if (!(ensoniq->u.es1370.pclkdiv_lock & ES_MODE_CAPTURE)) { + ensoniq->ctrl &= ~ES_1370_PCLKDIVM; + ensoniq->ctrl |= ES_1370_PCLKDIVO(ES_1370_SRTODIV(runtime->rate)); + ensoniq->u.es1370.pclkdiv_lock |= ES_MODE_PLAY2; + } +#endif + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + spin_unlock_irq(&ensoniq->reg_lock); +#ifndef CHIP1370 + snd_es1371_dac2_rate(ensoniq, runtime->rate); +#endif + return 0; +} + +static int snd_ensoniq_capture_prepare(struct snd_pcm_substream *substream) +{ + struct ensoniq *ensoniq = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int mode = 0; + + ensoniq->c_dma_size = snd_pcm_lib_buffer_bytes(substream); + ensoniq->c_period_size = snd_pcm_lib_period_bytes(substream); + if (snd_pcm_format_width(runtime->format) == 16) + mode |= 0x02; + if (runtime->channels > 1) + mode |= 0x01; + spin_lock_irq(&ensoniq->reg_lock); + ensoniq->ctrl &= ~ES_ADC_EN; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE)); + outl(runtime->dma_addr, ES_REG(ensoniq, ADC_FRAME)); + outl((ensoniq->c_dma_size >> 2) - 1, ES_REG(ensoniq, ADC_SIZE)); + ensoniq->sctrl &= ~(ES_R1_LOOP_SEL | ES_R1_MODEM); + ensoniq->sctrl |= ES_R1_INT_EN | ES_R1_MODEO(mode); + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + outl((ensoniq->c_period_size >> snd_ensoniq_sample_shift[mode]) - 1, + ES_REG(ensoniq, ADC_COUNT)); +#ifdef CHIP1370 + if (!(ensoniq->u.es1370.pclkdiv_lock & ES_MODE_PLAY2)) { + ensoniq->ctrl &= ~ES_1370_PCLKDIVM; + ensoniq->ctrl |= ES_1370_PCLKDIVO(ES_1370_SRTODIV(runtime->rate)); + ensoniq->u.es1370.pclkdiv_lock |= ES_MODE_CAPTURE; + } +#endif + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + spin_unlock_irq(&ensoniq->reg_lock); +#ifndef CHIP1370 + snd_es1371_adc_rate(ensoniq, runtime->rate); +#endif + return 0; +} + +static snd_pcm_uframes_t snd_ensoniq_playback1_pointer(struct snd_pcm_substream *substream) +{ + struct ensoniq *ensoniq = snd_pcm_substream_chip(substream); + size_t ptr; + + spin_lock(&ensoniq->reg_lock); + if (inl(ES_REG(ensoniq, CONTROL)) & ES_DAC1_EN) { + outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE)); + ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, DAC1_SIZE))); + ptr = bytes_to_frames(substream->runtime, ptr); + } else { + ptr = 0; + } + spin_unlock(&ensoniq->reg_lock); + return ptr; +} + +static snd_pcm_uframes_t snd_ensoniq_playback2_pointer(struct snd_pcm_substream *substream) +{ + struct ensoniq *ensoniq = snd_pcm_substream_chip(substream); + size_t ptr; + + spin_lock(&ensoniq->reg_lock); + if (inl(ES_REG(ensoniq, CONTROL)) & ES_DAC2_EN) { + outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE)); + ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, DAC2_SIZE))); + ptr = bytes_to_frames(substream->runtime, ptr); + } else { + ptr = 0; + } + spin_unlock(&ensoniq->reg_lock); + return ptr; +} + +static snd_pcm_uframes_t snd_ensoniq_capture_pointer(struct snd_pcm_substream *substream) +{ + struct ensoniq *ensoniq = snd_pcm_substream_chip(substream); + size_t ptr; + + spin_lock(&ensoniq->reg_lock); + if (inl(ES_REG(ensoniq, CONTROL)) & ES_ADC_EN) { + outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE)); + ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, ADC_SIZE))); + ptr = bytes_to_frames(substream->runtime, ptr); + } else { + ptr = 0; + } + spin_unlock(&ensoniq->reg_lock); + return ptr; +} + +static struct snd_pcm_hardware snd_ensoniq_playback1 = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = +#ifndef CHIP1370 + SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, +#else + (SNDRV_PCM_RATE_KNOT | /* 5512Hz rate */ + SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050 | + SNDRV_PCM_RATE_44100), +#endif + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static struct snd_pcm_hardware snd_ensoniq_playback2 = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static struct snd_pcm_hardware snd_ensoniq_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static int snd_ensoniq_playback1_open(struct snd_pcm_substream *substream) +{ + struct ensoniq *ensoniq = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + + ensoniq->mode |= ES_MODE_PLAY1; + ensoniq->playback1_substream = substream; + runtime->hw = snd_ensoniq_playback1; + snd_pcm_set_sync(substream); + spin_lock_irq(&ensoniq->reg_lock); + if (ensoniq->spdif && ensoniq->playback2_substream == NULL) + ensoniq->spdif_stream = ensoniq->spdif_default; + spin_unlock_irq(&ensoniq->reg_lock); +#ifdef CHIP1370 + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_es1370_hw_constraints_rates); +#else + snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_es1371_hw_constraints_dac_clock); +#endif + return 0; +} + +static int snd_ensoniq_playback2_open(struct snd_pcm_substream *substream) +{ + struct ensoniq *ensoniq = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + + ensoniq->mode |= ES_MODE_PLAY2; + ensoniq->playback2_substream = substream; + runtime->hw = snd_ensoniq_playback2; + snd_pcm_set_sync(substream); + spin_lock_irq(&ensoniq->reg_lock); + if (ensoniq->spdif && ensoniq->playback1_substream == NULL) + ensoniq->spdif_stream = ensoniq->spdif_default; + spin_unlock_irq(&ensoniq->reg_lock); +#ifdef CHIP1370 + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_es1370_hw_constraints_clock); +#else + snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_es1371_hw_constraints_dac_clock); +#endif + return 0; +} + +static int snd_ensoniq_capture_open(struct snd_pcm_substream *substream) +{ + struct ensoniq *ensoniq = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + + ensoniq->mode |= ES_MODE_CAPTURE; + ensoniq->capture_substream = substream; + runtime->hw = snd_ensoniq_capture; + snd_pcm_set_sync(substream); +#ifdef CHIP1370 + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_es1370_hw_constraints_clock); +#else + snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &snd_es1371_hw_constraints_adc_clock); +#endif + return 0; +} + +static int snd_ensoniq_playback1_close(struct snd_pcm_substream *substream) +{ + struct ensoniq *ensoniq = snd_pcm_substream_chip(substream); + + ensoniq->playback1_substream = NULL; + ensoniq->mode &= ~ES_MODE_PLAY1; + return 0; +} + +static int snd_ensoniq_playback2_close(struct snd_pcm_substream *substream) +{ + struct ensoniq *ensoniq = snd_pcm_substream_chip(substream); + + ensoniq->playback2_substream = NULL; + spin_lock_irq(&ensoniq->reg_lock); +#ifdef CHIP1370 + ensoniq->u.es1370.pclkdiv_lock &= ~ES_MODE_PLAY2; +#endif + ensoniq->mode &= ~ES_MODE_PLAY2; + spin_unlock_irq(&ensoniq->reg_lock); + return 0; +} + +static int snd_ensoniq_capture_close(struct snd_pcm_substream *substream) +{ + struct ensoniq *ensoniq = snd_pcm_substream_chip(substream); + + ensoniq->capture_substream = NULL; + spin_lock_irq(&ensoniq->reg_lock); +#ifdef CHIP1370 + ensoniq->u.es1370.pclkdiv_lock &= ~ES_MODE_CAPTURE; +#endif + ensoniq->mode &= ~ES_MODE_CAPTURE; + spin_unlock_irq(&ensoniq->reg_lock); + return 0; +} + +static struct snd_pcm_ops snd_ensoniq_playback1_ops = { + .open = snd_ensoniq_playback1_open, + .close = snd_ensoniq_playback1_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ensoniq_hw_params, + .hw_free = snd_ensoniq_hw_free, + .prepare = snd_ensoniq_playback1_prepare, + .trigger = snd_ensoniq_trigger, + .pointer = snd_ensoniq_playback1_pointer, +}; + +static struct snd_pcm_ops snd_ensoniq_playback2_ops = { + .open = snd_ensoniq_playback2_open, + .close = snd_ensoniq_playback2_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ensoniq_hw_params, + .hw_free = snd_ensoniq_hw_free, + .prepare = snd_ensoniq_playback2_prepare, + .trigger = snd_ensoniq_trigger, + .pointer = snd_ensoniq_playback2_pointer, +}; + +static struct snd_pcm_ops snd_ensoniq_capture_ops = { + .open = snd_ensoniq_capture_open, + .close = snd_ensoniq_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ensoniq_hw_params, + .hw_free = snd_ensoniq_hw_free, + .prepare = snd_ensoniq_capture_prepare, + .trigger = snd_ensoniq_trigger, + .pointer = snd_ensoniq_capture_pointer, +}; + +static int __devinit snd_ensoniq_pcm(struct ensoniq * ensoniq, int device, + struct snd_pcm ** rpcm) +{ + struct snd_pcm *pcm; + int err; + + if (rpcm) + *rpcm = NULL; +#ifdef CHIP1370 + err = snd_pcm_new(ensoniq->card, "ES1370/1", device, 1, 1, &pcm); +#else + err = snd_pcm_new(ensoniq->card, "ES1371/1", device, 1, 1, &pcm); +#endif + if (err < 0) + return err; + +#ifdef CHIP1370 + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ensoniq_playback2_ops); +#else + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ensoniq_playback1_ops); +#endif + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ensoniq_capture_ops); + + pcm->private_data = ensoniq; + pcm->info_flags = 0; +#ifdef CHIP1370 + strcpy(pcm->name, "ES1370 DAC2/ADC"); +#else + strcpy(pcm->name, "ES1371 DAC2/ADC"); +#endif + ensoniq->pcm1 = pcm; + + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(ensoniq->pci), 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +static int __devinit snd_ensoniq_pcm2(struct ensoniq * ensoniq, int device, + struct snd_pcm ** rpcm) +{ + struct snd_pcm *pcm; + int err; + + if (rpcm) + *rpcm = NULL; +#ifdef CHIP1370 + err = snd_pcm_new(ensoniq->card, "ES1370/2", device, 1, 0, &pcm); +#else + err = snd_pcm_new(ensoniq->card, "ES1371/2", device, 1, 0, &pcm); +#endif + if (err < 0) + return err; + +#ifdef CHIP1370 + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ensoniq_playback1_ops); +#else + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ensoniq_playback2_ops); +#endif + pcm->private_data = ensoniq; + pcm->info_flags = 0; +#ifdef CHIP1370 + strcpy(pcm->name, "ES1370 DAC1"); +#else + strcpy(pcm->name, "ES1371 DAC1"); +#endif + ensoniq->pcm2 = pcm; + + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(ensoniq->pci), 64*1024, 128*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +/* + * Mixer section + */ + +/* + * ENS1371 mixer (including SPDIF interface) + */ +#ifdef CHIP1371 +static int snd_ens1373_spdif_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ens1373_spdif_default_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol); + spin_lock_irq(&ensoniq->reg_lock); + ucontrol->value.iec958.status[0] = (ensoniq->spdif_default >> 0) & 0xff; + ucontrol->value.iec958.status[1] = (ensoniq->spdif_default >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (ensoniq->spdif_default >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (ensoniq->spdif_default >> 24) & 0xff; + spin_unlock_irq(&ensoniq->reg_lock); + return 0; +} + +static int snd_ens1373_spdif_default_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol); + unsigned int val; + int change; + + val = ((u32)ucontrol->value.iec958.status[0] << 0) | + ((u32)ucontrol->value.iec958.status[1] << 8) | + ((u32)ucontrol->value.iec958.status[2] << 16) | + ((u32)ucontrol->value.iec958.status[3] << 24); + spin_lock_irq(&ensoniq->reg_lock); + change = ensoniq->spdif_default != val; + ensoniq->spdif_default = val; + if (change && ensoniq->playback1_substream == NULL && + ensoniq->playback2_substream == NULL) + outl(val, ES_REG(ensoniq, CHANNEL_STATUS)); + spin_unlock_irq(&ensoniq->reg_lock); + return change; +} + +static int snd_ens1373_spdif_mask_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.iec958.status[0] = 0xff; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0xff; + ucontrol->value.iec958.status[3] = 0xff; + return 0; +} + +static int snd_ens1373_spdif_stream_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol); + spin_lock_irq(&ensoniq->reg_lock); + ucontrol->value.iec958.status[0] = (ensoniq->spdif_stream >> 0) & 0xff; + ucontrol->value.iec958.status[1] = (ensoniq->spdif_stream >> 8) & 0xff; + ucontrol->value.iec958.status[2] = (ensoniq->spdif_stream >> 16) & 0xff; + ucontrol->value.iec958.status[3] = (ensoniq->spdif_stream >> 24) & 0xff; + spin_unlock_irq(&ensoniq->reg_lock); + return 0; +} + +static int snd_ens1373_spdif_stream_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol); + unsigned int val; + int change; + + val = ((u32)ucontrol->value.iec958.status[0] << 0) | + ((u32)ucontrol->value.iec958.status[1] << 8) | + ((u32)ucontrol->value.iec958.status[2] << 16) | + ((u32)ucontrol->value.iec958.status[3] << 24); + spin_lock_irq(&ensoniq->reg_lock); + change = ensoniq->spdif_stream != val; + ensoniq->spdif_stream = val; + if (change && (ensoniq->playback1_substream != NULL || + ensoniq->playback2_substream != NULL)) + outl(val, ES_REG(ensoniq, CHANNEL_STATUS)); + spin_unlock_irq(&ensoniq->reg_lock); + return change; +} + +#define ES1371_SPDIF(xname) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_es1371_spdif_info, \ + .get = snd_es1371_spdif_get, .put = snd_es1371_spdif_put } + +#define snd_es1371_spdif_info snd_ctl_boolean_mono_info + +static int snd_es1371_spdif_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol); + + spin_lock_irq(&ensoniq->reg_lock); + ucontrol->value.integer.value[0] = ensoniq->ctrl & ES_1373_SPDIF_THRU ? 1 : 0; + spin_unlock_irq(&ensoniq->reg_lock); + return 0; +} + +static int snd_es1371_spdif_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol); + unsigned int nval1, nval2; + int change; + + nval1 = ucontrol->value.integer.value[0] ? ES_1373_SPDIF_THRU : 0; + nval2 = ucontrol->value.integer.value[0] ? ES_1373_SPDIF_EN : 0; + spin_lock_irq(&ensoniq->reg_lock); + change = (ensoniq->ctrl & ES_1373_SPDIF_THRU) != nval1; + ensoniq->ctrl &= ~ES_1373_SPDIF_THRU; + ensoniq->ctrl |= nval1; + ensoniq->cssr &= ~ES_1373_SPDIF_EN; + ensoniq->cssr |= nval2; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + outl(ensoniq->cssr, ES_REG(ensoniq, STATUS)); + spin_unlock_irq(&ensoniq->reg_lock); + return change; +} + + +/* spdif controls */ +static struct snd_kcontrol_new snd_es1371_mixer_spdif[] __devinitdata = { + ES1371_SPDIF(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH)), + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + .info = snd_ens1373_spdif_info, + .get = snd_ens1373_spdif_default_get, + .put = snd_ens1373_spdif_default_put, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), + .info = snd_ens1373_spdif_info, + .get = snd_ens1373_spdif_mask_get + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + .info = snd_ens1373_spdif_info, + .get = snd_ens1373_spdif_stream_get, + .put = snd_ens1373_spdif_stream_put + }, +}; + + +#define snd_es1373_rear_info snd_ctl_boolean_mono_info + +static int snd_es1373_rear_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol); + int val = 0; + + spin_lock_irq(&ensoniq->reg_lock); + if ((ensoniq->cssr & (ES_1373_REAR_BIT27|ES_1373_REAR_BIT26| + ES_1373_REAR_BIT24)) == ES_1373_REAR_BIT26) + val = 1; + ucontrol->value.integer.value[0] = val; + spin_unlock_irq(&ensoniq->reg_lock); + return 0; +} + +static int snd_es1373_rear_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol); + unsigned int nval1; + int change; + + nval1 = ucontrol->value.integer.value[0] ? + ES_1373_REAR_BIT26 : (ES_1373_REAR_BIT27|ES_1373_REAR_BIT24); + spin_lock_irq(&ensoniq->reg_lock); + change = (ensoniq->cssr & (ES_1373_REAR_BIT27| + ES_1373_REAR_BIT26|ES_1373_REAR_BIT24)) != nval1; + ensoniq->cssr &= ~(ES_1373_REAR_BIT27|ES_1373_REAR_BIT26|ES_1373_REAR_BIT24); + ensoniq->cssr |= nval1; + outl(ensoniq->cssr, ES_REG(ensoniq, STATUS)); + spin_unlock_irq(&ensoniq->reg_lock); + return change; +} + +static struct snd_kcontrol_new snd_ens1373_rear __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "AC97 2ch->4ch Copy Switch", + .info = snd_es1373_rear_info, + .get = snd_es1373_rear_get, + .put = snd_es1373_rear_put, +}; + +#define snd_es1373_line_info snd_ctl_boolean_mono_info + +static int snd_es1373_line_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol); + int val = 0; + + spin_lock_irq(&ensoniq->reg_lock); + if ((ensoniq->ctrl & ES_1371_GPIO_OUTM) >= 4) + val = 1; + ucontrol->value.integer.value[0] = val; + spin_unlock_irq(&ensoniq->reg_lock); + return 0; +} + +static int snd_es1373_line_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol); + int changed; + unsigned int ctrl; + + spin_lock_irq(&ensoniq->reg_lock); + ctrl = ensoniq->ctrl; + if (ucontrol->value.integer.value[0]) + ensoniq->ctrl |= ES_1371_GPIO_OUT(4); /* switch line-in -> rear out */ + else + ensoniq->ctrl &= ~ES_1371_GPIO_OUT(4); + changed = (ctrl != ensoniq->ctrl); + if (changed) + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + spin_unlock_irq(&ensoniq->reg_lock); + return changed; +} + +static struct snd_kcontrol_new snd_ens1373_line __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Line In->Rear Out Switch", + .info = snd_es1373_line_info, + .get = snd_es1373_line_get, + .put = snd_es1373_line_put, +}; + +static void snd_ensoniq_mixer_free_ac97(struct snd_ac97 *ac97) +{ + struct ensoniq *ensoniq = ac97->private_data; + ensoniq->u.es1371.ac97 = NULL; +} + +struct es1371_quirk { + unsigned short vid; /* vendor ID */ + unsigned short did; /* device ID */ + unsigned char rev; /* revision */ +}; + +static int es1371_quirk_lookup(struct ensoniq *ensoniq, + struct es1371_quirk *list) +{ + while (list->vid != (unsigned short)PCI_ANY_ID) { + if (ensoniq->pci->vendor == list->vid && + ensoniq->pci->device == list->did && + ensoniq->rev == list->rev) + return 1; + list++; + } + return 0; +} + +static struct es1371_quirk es1371_spdif_present[] __devinitdata = { + { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_C }, + { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_D }, + { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_E }, + { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_ES1371, .rev = ES1371REV_CT5880_A }, + { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_ES1371, .rev = ES1371REV_ES1373_8 }, + { .vid = PCI_ANY_ID, .did = PCI_ANY_ID } +}; + +static struct snd_pci_quirk ens1373_line_quirk[] __devinitdata = { + SND_PCI_QUIRK_ID(0x1274, 0x2000), /* GA-7DXR */ + SND_PCI_QUIRK_ID(0x1458, 0xa000), /* GA-8IEXP */ + { } /* end */ +}; + +static int __devinit snd_ensoniq_1371_mixer(struct ensoniq *ensoniq, + int has_spdif, int has_line) +{ + struct snd_card *card = ensoniq->card; + struct snd_ac97_bus *pbus; + struct snd_ac97_template ac97; + int err; + static struct snd_ac97_bus_ops ops = { + .write = snd_es1371_codec_write, + .read = snd_es1371_codec_read, + .wait = snd_es1371_codec_wait, + }; + + if ((err = snd_ac97_bus(card, 0, &ops, NULL, &pbus)) < 0) + return err; + + memset(&ac97, 0, sizeof(ac97)); + ac97.private_data = ensoniq; + ac97.private_free = snd_ensoniq_mixer_free_ac97; + ac97.scaps = AC97_SCAP_AUDIO; + if ((err = snd_ac97_mixer(pbus, &ac97, &ensoniq->u.es1371.ac97)) < 0) + return err; + if (has_spdif > 0 || + (!has_spdif && es1371_quirk_lookup(ensoniq, es1371_spdif_present))) { + struct snd_kcontrol *kctl; + int i, index = 0; + + ensoniq->spdif_default = ensoniq->spdif_stream = + SNDRV_PCM_DEFAULT_CON_SPDIF; + outl(ensoniq->spdif_default, ES_REG(ensoniq, CHANNEL_STATUS)); + + if (ensoniq->u.es1371.ac97->ext_id & AC97_EI_SPDIF) + index++; + + for (i = 0; i < ARRAY_SIZE(snd_es1371_mixer_spdif); i++) { + kctl = snd_ctl_new1(&snd_es1371_mixer_spdif[i], ensoniq); + if (!kctl) + return -ENOMEM; + kctl->id.index = index; + err = snd_ctl_add(card, kctl); + if (err < 0) + return err; + } + } + if (ensoniq->u.es1371.ac97->ext_id & AC97_EI_SDAC) { + /* mirror rear to front speakers */ + ensoniq->cssr &= ~(ES_1373_REAR_BIT27|ES_1373_REAR_BIT24); + ensoniq->cssr |= ES_1373_REAR_BIT26; + err = snd_ctl_add(card, snd_ctl_new1(&snd_ens1373_rear, ensoniq)); + if (err < 0) + return err; + } + if (has_line > 0 || + snd_pci_quirk_lookup(ensoniq->pci, ens1373_line_quirk)) { + err = snd_ctl_add(card, snd_ctl_new1(&snd_ens1373_line, + ensoniq)); + if (err < 0) + return err; + } + + return 0; +} + +#endif /* CHIP1371 */ + +/* generic control callbacks for ens1370 */ +#ifdef CHIP1370 +#define ENSONIQ_CONTROL(xname, mask) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = xname, .info = snd_ensoniq_control_info, \ + .get = snd_ensoniq_control_get, .put = snd_ensoniq_control_put, \ + .private_value = mask } + +#define snd_ensoniq_control_info snd_ctl_boolean_mono_info + +static int snd_ensoniq_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol); + int mask = kcontrol->private_value; + + spin_lock_irq(&ensoniq->reg_lock); + ucontrol->value.integer.value[0] = ensoniq->ctrl & mask ? 1 : 0; + spin_unlock_irq(&ensoniq->reg_lock); + return 0; +} + +static int snd_ensoniq_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct ensoniq *ensoniq = snd_kcontrol_chip(kcontrol); + int mask = kcontrol->private_value; + unsigned int nval; + int change; + + nval = ucontrol->value.integer.value[0] ? mask : 0; + spin_lock_irq(&ensoniq->reg_lock); + change = (ensoniq->ctrl & mask) != nval; + ensoniq->ctrl &= ~mask; + ensoniq->ctrl |= nval; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + spin_unlock_irq(&ensoniq->reg_lock); + return change; +} + +/* + * ENS1370 mixer + */ + +static struct snd_kcontrol_new snd_es1370_controls[2] __devinitdata = { +ENSONIQ_CONTROL("PCM 0 Output also on Line-In Jack", ES_1370_XCTL0), +ENSONIQ_CONTROL("Mic +5V bias", ES_1370_XCTL1) +}; + +#define ES1370_CONTROLS ARRAY_SIZE(snd_es1370_controls) + +static void snd_ensoniq_mixer_free_ak4531(struct snd_ak4531 *ak4531) +{ + struct ensoniq *ensoniq = ak4531->private_data; + ensoniq->u.es1370.ak4531 = NULL; +} + +static int __devinit snd_ensoniq_1370_mixer(struct ensoniq * ensoniq) +{ + struct snd_card *card = ensoniq->card; + struct snd_ak4531 ak4531; + unsigned int idx; + int err; + + /* try reset AK4531 */ + outw(ES_1370_CODEC_WRITE(AK4531_RESET, 0x02), ES_REG(ensoniq, 1370_CODEC)); + inw(ES_REG(ensoniq, 1370_CODEC)); + udelay(100); + outw(ES_1370_CODEC_WRITE(AK4531_RESET, 0x03), ES_REG(ensoniq, 1370_CODEC)); + inw(ES_REG(ensoniq, 1370_CODEC)); + udelay(100); + + memset(&ak4531, 0, sizeof(ak4531)); + ak4531.write = snd_es1370_codec_write; + ak4531.private_data = ensoniq; + ak4531.private_free = snd_ensoniq_mixer_free_ak4531; + if ((err = snd_ak4531_mixer(card, &ak4531, &ensoniq->u.es1370.ak4531)) < 0) + return err; + for (idx = 0; idx < ES1370_CONTROLS; idx++) { + err = snd_ctl_add(card, snd_ctl_new1(&snd_es1370_controls[idx], ensoniq)); + if (err < 0) + return err; + } + return 0; +} + +#endif /* CHIP1370 */ + +#ifdef SUPPORT_JOYSTICK + +#ifdef CHIP1371 +static int __devinit snd_ensoniq_get_joystick_port(int dev) +{ + switch (joystick_port[dev]) { + case 0: /* disabled */ + case 1: /* auto-detect */ + case 0x200: + case 0x208: + case 0x210: + case 0x218: + return joystick_port[dev]; + + default: + printk(KERN_ERR "ens1371: invalid joystick port %#x", joystick_port[dev]); + return 0; + } +} +#else +static inline int snd_ensoniq_get_joystick_port(int dev) +{ + return joystick[dev] ? 0x200 : 0; +} +#endif + +static int __devinit snd_ensoniq_create_gameport(struct ensoniq *ensoniq, int dev) +{ + struct gameport *gp; + int io_port; + + io_port = snd_ensoniq_get_joystick_port(dev); + + switch (io_port) { + case 0: + return -ENOSYS; + + case 1: /* auto_detect */ + for (io_port = 0x200; io_port <= 0x218; io_port += 8) + if (request_region(io_port, 8, "ens137x: gameport")) + break; + if (io_port > 0x218) { + printk(KERN_WARNING "ens137x: no gameport ports available\n"); + return -EBUSY; + } + break; + + default: + if (!request_region(io_port, 8, "ens137x: gameport")) { + printk(KERN_WARNING "ens137x: gameport io port 0x%#x in use\n", + io_port); + return -EBUSY; + } + break; + } + + ensoniq->gameport = gp = gameport_allocate_port(); + if (!gp) { + printk(KERN_ERR "ens137x: cannot allocate memory for gameport\n"); + release_region(io_port, 8); + return -ENOMEM; + } + + gameport_set_name(gp, "ES137x"); + gameport_set_phys(gp, "pci%s/gameport0", pci_name(ensoniq->pci)); + gameport_set_dev_parent(gp, &ensoniq->pci->dev); + gp->io = io_port; + + ensoniq->ctrl |= ES_JYSTK_EN; +#ifdef CHIP1371 + ensoniq->ctrl &= ~ES_1371_JOY_ASELM; + ensoniq->ctrl |= ES_1371_JOY_ASEL((io_port - 0x200) / 8); +#endif + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + + gameport_register_port(ensoniq->gameport); + + return 0; +} + +static void snd_ensoniq_free_gameport(struct ensoniq *ensoniq) +{ + if (ensoniq->gameport) { + int port = ensoniq->gameport->io; + + gameport_unregister_port(ensoniq->gameport); + ensoniq->gameport = NULL; + ensoniq->ctrl &= ~ES_JYSTK_EN; + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + release_region(port, 8); + } +} +#else +static inline int snd_ensoniq_create_gameport(struct ensoniq *ensoniq, long port) { return -ENOSYS; } +static inline void snd_ensoniq_free_gameport(struct ensoniq *ensoniq) { } +#endif /* SUPPORT_JOYSTICK */ + +/* + + */ + +static void snd_ensoniq_proc_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct ensoniq *ensoniq = entry->private_data; + +#ifdef CHIP1370 + snd_iprintf(buffer, "Ensoniq AudioPCI ES1370\n\n"); +#else + snd_iprintf(buffer, "Ensoniq AudioPCI ES1371\n\n"); +#endif + snd_iprintf(buffer, "Joystick enable : %s\n", + ensoniq->ctrl & ES_JYSTK_EN ? "on" : "off"); +#ifdef CHIP1370 + snd_iprintf(buffer, "MIC +5V bias : %s\n", + ensoniq->ctrl & ES_1370_XCTL1 ? "on" : "off"); + snd_iprintf(buffer, "Line In to AOUT : %s\n", + ensoniq->ctrl & ES_1370_XCTL0 ? "on" : "off"); +#else + snd_iprintf(buffer, "Joystick port : 0x%x\n", + (ES_1371_JOY_ASELI(ensoniq->ctrl) * 8) + 0x200); +#endif +} + +static void __devinit snd_ensoniq_proc_init(struct ensoniq * ensoniq) +{ + struct snd_info_entry *entry; + + if (! snd_card_proc_new(ensoniq->card, "audiopci", &entry)) + snd_info_set_text_ops(entry, ensoniq, snd_ensoniq_proc_read); +} + +/* + + */ + +static int snd_ensoniq_free(struct ensoniq *ensoniq) +{ + snd_ensoniq_free_gameport(ensoniq); + if (ensoniq->irq < 0) + goto __hw_end; +#ifdef CHIP1370 + outl(ES_1370_SERR_DISABLE, ES_REG(ensoniq, CONTROL)); /* switch everything off */ + outl(0, ES_REG(ensoniq, SERIAL)); /* clear serial interface */ +#else + outl(0, ES_REG(ensoniq, CONTROL)); /* switch everything off */ + outl(0, ES_REG(ensoniq, SERIAL)); /* clear serial interface */ +#endif + synchronize_irq(ensoniq->irq); + pci_set_power_state(ensoniq->pci, 3); + __hw_end: +#ifdef CHIP1370 + if (ensoniq->dma_bug.area) + snd_dma_free_pages(&ensoniq->dma_bug); +#endif + if (ensoniq->irq >= 0) + free_irq(ensoniq->irq, ensoniq); + pci_release_regions(ensoniq->pci); + pci_disable_device(ensoniq->pci); + kfree(ensoniq); + return 0; +} + +static int snd_ensoniq_dev_free(struct snd_device *device) +{ + struct ensoniq *ensoniq = device->device_data; + return snd_ensoniq_free(ensoniq); +} + +#ifdef CHIP1371 +static struct snd_pci_quirk es1371_amplifier_hack[] __devinitdata = { + SND_PCI_QUIRK_ID(0x107b, 0x2150), /* Gateway Solo 2150 */ + SND_PCI_QUIRK_ID(0x13bd, 0x100c), /* EV1938 on Mebius PC-MJ100V */ + SND_PCI_QUIRK_ID(0x1102, 0x5938), /* Targa Xtender300 */ + SND_PCI_QUIRK_ID(0x1102, 0x8938), /* IPC Topnote G notebook */ + { } /* end */ +}; + +static struct es1371_quirk es1371_ac97_reset_hack[] = { + { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_C }, + { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_D }, + { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_CT5880, .rev = CT5880REV_CT5880_E }, + { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_ES1371, .rev = ES1371REV_CT5880_A }, + { .vid = PCI_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_ES1371, .rev = ES1371REV_ES1373_8 }, + { .vid = PCI_ANY_ID, .did = PCI_ANY_ID } +}; +#endif + +static void snd_ensoniq_chip_init(struct ensoniq *ensoniq) +{ +#ifdef CHIP1371 + int idx; +#endif + /* this code was part of snd_ensoniq_create before intruduction + * of suspend/resume + */ +#ifdef CHIP1370 + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE)); + outl(ensoniq->dma_bug.addr, ES_REG(ensoniq, PHANTOM_FRAME)); + outl(0, ES_REG(ensoniq, PHANTOM_COUNT)); +#else + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + outl(0, ES_REG(ensoniq, 1371_LEGACY)); + if (es1371_quirk_lookup(ensoniq, es1371_ac97_reset_hack)) { + outl(ensoniq->cssr, ES_REG(ensoniq, STATUS)); + /* need to delay around 20ms(bleech) to give + some CODECs enough time to wakeup */ + msleep(20); + } + /* AC'97 warm reset to start the bitclk */ + outl(ensoniq->ctrl | ES_1371_SYNC_RES, ES_REG(ensoniq, CONTROL)); + inl(ES_REG(ensoniq, CONTROL)); + udelay(20); + outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); + /* Init the sample rate converter */ + snd_es1371_wait_src_ready(ensoniq); + outl(ES_1371_SRC_DISABLE, ES_REG(ensoniq, 1371_SMPRATE)); + for (idx = 0; idx < 0x80; idx++) + snd_es1371_src_write(ensoniq, idx, 0); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_TRUNC_N, 16 << 4); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS, 16 << 10); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_TRUNC_N, 16 << 4); + snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS, 16 << 10); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC, 1 << 12); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC + 1, 1 << 12); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC1, 1 << 12); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC1 + 1, 1 << 12); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC2, 1 << 12); + snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC2 + 1, 1 << 12); + snd_es1371_adc_rate(ensoniq, 22050); + snd_es1371_dac1_rate(ensoniq, 22050); + snd_es1371_dac2_rate(ensoniq, 22050); + /* WARNING: + * enabling the sample rate converter without properly programming + * its parameters causes the chip to lock up (the SRC busy bit will + * be stuck high, and I've found no way to rectify this other than + * power cycle) - Thomas Sailer + */ + snd_es1371_wait_src_ready(ensoniq); + outl(0, ES_REG(ensoniq, 1371_SMPRATE)); + /* try reset codec directly */ + outl(ES_1371_CODEC_WRITE(0, 0), ES_REG(ensoniq, 1371_CODEC)); +#endif + outb(ensoniq->uartc = 0x00, ES_REG(ensoniq, UART_CONTROL)); + outb(0x00, ES_REG(ensoniq, UART_RES)); + outl(ensoniq->cssr, ES_REG(ensoniq, STATUS)); + synchronize_irq(ensoniq->irq); +} + +#ifdef CONFIG_PM +static int snd_ensoniq_suspend(struct pci_dev *pci, pm_message_t state) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct ensoniq *ensoniq = card->private_data; + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + + snd_pcm_suspend_all(ensoniq->pcm1); + snd_pcm_suspend_all(ensoniq->pcm2); + +#ifdef CHIP1371 + snd_ac97_suspend(ensoniq->u.es1371.ac97); +#else + /* try to reset AK4531 */ + outw(ES_1370_CODEC_WRITE(AK4531_RESET, 0x02), ES_REG(ensoniq, 1370_CODEC)); + inw(ES_REG(ensoniq, 1370_CODEC)); + udelay(100); + outw(ES_1370_CODEC_WRITE(AK4531_RESET, 0x03), ES_REG(ensoniq, 1370_CODEC)); + inw(ES_REG(ensoniq, 1370_CODEC)); + udelay(100); + snd_ak4531_suspend(ensoniq->u.es1370.ak4531); +#endif + + pci_disable_device(pci); + pci_save_state(pci); + pci_set_power_state(pci, pci_choose_state(pci, state)); + return 0; +} + +static int snd_ensoniq_resume(struct pci_dev *pci) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct ensoniq *ensoniq = card->private_data; + + pci_set_power_state(pci, PCI_D0); + pci_restore_state(pci); + if (pci_enable_device(pci) < 0) { + printk(KERN_ERR DRIVER_NAME ": pci_enable_device failed, " + "disabling device\n"); + snd_card_disconnect(card); + return -EIO; + } + pci_set_master(pci); + + snd_ensoniq_chip_init(ensoniq); + +#ifdef CHIP1371 + snd_ac97_resume(ensoniq->u.es1371.ac97); +#else + snd_ak4531_resume(ensoniq->u.es1370.ak4531); +#endif + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + return 0; +} +#endif /* CONFIG_PM */ + + +static int __devinit snd_ensoniq_create(struct snd_card *card, + struct pci_dev *pci, + struct ensoniq ** rensoniq) +{ + struct ensoniq *ensoniq; + int err; + static struct snd_device_ops ops = { + .dev_free = snd_ensoniq_dev_free, + }; + + *rensoniq = NULL; + if ((err = pci_enable_device(pci)) < 0) + return err; + ensoniq = kzalloc(sizeof(*ensoniq), GFP_KERNEL); + if (ensoniq == NULL) { + pci_disable_device(pci); + return -ENOMEM; + } + spin_lock_init(&ensoniq->reg_lock); + mutex_init(&ensoniq->src_mutex); + ensoniq->card = card; + ensoniq->pci = pci; + ensoniq->irq = -1; + if ((err = pci_request_regions(pci, "Ensoniq AudioPCI")) < 0) { + kfree(ensoniq); + pci_disable_device(pci); + return err; + } + ensoniq->port = pci_resource_start(pci, 0); + if (request_irq(pci->irq, snd_audiopci_interrupt, IRQF_SHARED, + "Ensoniq AudioPCI", ensoniq)) { + snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); + snd_ensoniq_free(ensoniq); + return -EBUSY; + } + ensoniq->irq = pci->irq; +#ifdef CHIP1370 + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), + 16, &ensoniq->dma_bug) < 0) { + snd_printk(KERN_ERR "unable to allocate space for phantom area - dma_bug\n"); + snd_ensoniq_free(ensoniq); + return -EBUSY; + } +#endif + pci_set_master(pci); + ensoniq->rev = pci->revision; +#ifdef CHIP1370 +#if 0 + ensoniq->ctrl = ES_1370_CDC_EN | ES_1370_SERR_DISABLE | + ES_1370_PCLKDIVO(ES_1370_SRTODIV(8000)); +#else /* get microphone working */ + ensoniq->ctrl = ES_1370_CDC_EN | ES_1370_PCLKDIVO(ES_1370_SRTODIV(8000)); +#endif + ensoniq->sctrl = 0; +#else + ensoniq->ctrl = 0; + ensoniq->sctrl = 0; + ensoniq->cssr = 0; + if (snd_pci_quirk_lookup(pci, es1371_amplifier_hack)) + ensoniq->ctrl |= ES_1371_GPIO_OUT(1); /* turn amplifier on */ + + if (es1371_quirk_lookup(ensoniq, es1371_ac97_reset_hack)) + ensoniq->cssr |= ES_1371_ST_AC97_RST; +#endif + + snd_ensoniq_chip_init(ensoniq); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ensoniq, &ops)) < 0) { + snd_ensoniq_free(ensoniq); + return err; + } + + snd_ensoniq_proc_init(ensoniq); + + snd_card_set_dev(card, &pci->dev); + + *rensoniq = ensoniq; + return 0; +} + +/* + * MIDI section + */ + +static void snd_ensoniq_midi_interrupt(struct ensoniq * ensoniq) +{ + struct snd_rawmidi *rmidi = ensoniq->rmidi; + unsigned char status, mask, byte; + + if (rmidi == NULL) + return; + /* do Rx at first */ + spin_lock(&ensoniq->reg_lock); + mask = ensoniq->uartm & ES_MODE_INPUT ? ES_RXRDY : 0; + while (mask) { + status = inb(ES_REG(ensoniq, UART_STATUS)); + if ((status & mask) == 0) + break; + byte = inb(ES_REG(ensoniq, UART_DATA)); + snd_rawmidi_receive(ensoniq->midi_input, &byte, 1); + } + spin_unlock(&ensoniq->reg_lock); + + /* do Tx at second */ + spin_lock(&ensoniq->reg_lock); + mask = ensoniq->uartm & ES_MODE_OUTPUT ? ES_TXRDY : 0; + while (mask) { + status = inb(ES_REG(ensoniq, UART_STATUS)); + if ((status & mask) == 0) + break; + if (snd_rawmidi_transmit(ensoniq->midi_output, &byte, 1) != 1) { + ensoniq->uartc &= ~ES_TXINTENM; + outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL)); + mask &= ~ES_TXRDY; + } else { + outb(byte, ES_REG(ensoniq, UART_DATA)); + } + } + spin_unlock(&ensoniq->reg_lock); +} + +static int snd_ensoniq_midi_input_open(struct snd_rawmidi_substream *substream) +{ + struct ensoniq *ensoniq = substream->rmidi->private_data; + + spin_lock_irq(&ensoniq->reg_lock); + ensoniq->uartm |= ES_MODE_INPUT; + ensoniq->midi_input = substream; + if (!(ensoniq->uartm & ES_MODE_OUTPUT)) { + outb(ES_CNTRL(3), ES_REG(ensoniq, UART_CONTROL)); + outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL)); + outl(ensoniq->ctrl |= ES_UART_EN, ES_REG(ensoniq, CONTROL)); + } + spin_unlock_irq(&ensoniq->reg_lock); + return 0; +} + +static int snd_ensoniq_midi_input_close(struct snd_rawmidi_substream *substream) +{ + struct ensoniq *ensoniq = substream->rmidi->private_data; + + spin_lock_irq(&ensoniq->reg_lock); + if (!(ensoniq->uartm & ES_MODE_OUTPUT)) { + outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL)); + outl(ensoniq->ctrl &= ~ES_UART_EN, ES_REG(ensoniq, CONTROL)); + } else { + outb(ensoniq->uartc &= ~ES_RXINTEN, ES_REG(ensoniq, UART_CONTROL)); + } + ensoniq->midi_input = NULL; + ensoniq->uartm &= ~ES_MODE_INPUT; + spin_unlock_irq(&ensoniq->reg_lock); + return 0; +} + +static int snd_ensoniq_midi_output_open(struct snd_rawmidi_substream *substream) +{ + struct ensoniq *ensoniq = substream->rmidi->private_data; + + spin_lock_irq(&ensoniq->reg_lock); + ensoniq->uartm |= ES_MODE_OUTPUT; + ensoniq->midi_output = substream; + if (!(ensoniq->uartm & ES_MODE_INPUT)) { + outb(ES_CNTRL(3), ES_REG(ensoniq, UART_CONTROL)); + outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL)); + outl(ensoniq->ctrl |= ES_UART_EN, ES_REG(ensoniq, CONTROL)); + } + spin_unlock_irq(&ensoniq->reg_lock); + return 0; +} + +static int snd_ensoniq_midi_output_close(struct snd_rawmidi_substream *substream) +{ + struct ensoniq *ensoniq = substream->rmidi->private_data; + + spin_lock_irq(&ensoniq->reg_lock); + if (!(ensoniq->uartm & ES_MODE_INPUT)) { + outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL)); + outl(ensoniq->ctrl &= ~ES_UART_EN, ES_REG(ensoniq, CONTROL)); + } else { + outb(ensoniq->uartc &= ~ES_TXINTENM, ES_REG(ensoniq, UART_CONTROL)); + } + ensoniq->midi_output = NULL; + ensoniq->uartm &= ~ES_MODE_OUTPUT; + spin_unlock_irq(&ensoniq->reg_lock); + return 0; +} + +static void snd_ensoniq_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) +{ + unsigned long flags; + struct ensoniq *ensoniq = substream->rmidi->private_data; + int idx; + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + if (up) { + if ((ensoniq->uartc & ES_RXINTEN) == 0) { + /* empty input FIFO */ + for (idx = 0; idx < 32; idx++) + inb(ES_REG(ensoniq, UART_DATA)); + ensoniq->uartc |= ES_RXINTEN; + outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL)); + } + } else { + if (ensoniq->uartc & ES_RXINTEN) { + ensoniq->uartc &= ~ES_RXINTEN; + outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL)); + } + } + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); +} + +static void snd_ensoniq_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) +{ + unsigned long flags; + struct ensoniq *ensoniq = substream->rmidi->private_data; + unsigned char byte; + + spin_lock_irqsave(&ensoniq->reg_lock, flags); + if (up) { + if (ES_TXINTENI(ensoniq->uartc) == 0) { + ensoniq->uartc |= ES_TXINTENO(1); + /* fill UART FIFO buffer at first, and turn Tx interrupts only if necessary */ + while (ES_TXINTENI(ensoniq->uartc) == 1 && + (inb(ES_REG(ensoniq, UART_STATUS)) & ES_TXRDY)) { + if (snd_rawmidi_transmit(substream, &byte, 1) != 1) { + ensoniq->uartc &= ~ES_TXINTENM; + } else { + outb(byte, ES_REG(ensoniq, UART_DATA)); + } + } + outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL)); + } + } else { + if (ES_TXINTENI(ensoniq->uartc) == 1) { + ensoniq->uartc &= ~ES_TXINTENM; + outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL)); + } + } + spin_unlock_irqrestore(&ensoniq->reg_lock, flags); +} + +static struct snd_rawmidi_ops snd_ensoniq_midi_output = +{ + .open = snd_ensoniq_midi_output_open, + .close = snd_ensoniq_midi_output_close, + .trigger = snd_ensoniq_midi_output_trigger, +}; + +static struct snd_rawmidi_ops snd_ensoniq_midi_input = +{ + .open = snd_ensoniq_midi_input_open, + .close = snd_ensoniq_midi_input_close, + .trigger = snd_ensoniq_midi_input_trigger, +}; + +static int __devinit snd_ensoniq_midi(struct ensoniq * ensoniq, int device, + struct snd_rawmidi **rrawmidi) +{ + struct snd_rawmidi *rmidi; + int err; + + if (rrawmidi) + *rrawmidi = NULL; + if ((err = snd_rawmidi_new(ensoniq->card, "ES1370/1", device, 1, 1, &rmidi)) < 0) + return err; +#ifdef CHIP1370 + strcpy(rmidi->name, "ES1370"); +#else + strcpy(rmidi->name, "ES1371"); +#endif + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_ensoniq_midi_output); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_ensoniq_midi_input); + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->private_data = ensoniq; + ensoniq->rmidi = rmidi; + if (rrawmidi) + *rrawmidi = rmidi; + return 0; +} + +/* + * Interrupt handler + */ + +static irqreturn_t snd_audiopci_interrupt(int irq, void *dev_id) +{ + struct ensoniq *ensoniq = dev_id; + unsigned int status, sctrl; + + if (ensoniq == NULL) + return IRQ_NONE; + + status = inl(ES_REG(ensoniq, STATUS)); + if (!(status & ES_INTR)) + return IRQ_NONE; + + spin_lock(&ensoniq->reg_lock); + sctrl = ensoniq->sctrl; + if (status & ES_DAC1) + sctrl &= ~ES_P1_INT_EN; + if (status & ES_DAC2) + sctrl &= ~ES_P2_INT_EN; + if (status & ES_ADC) + sctrl &= ~ES_R1_INT_EN; + outl(sctrl, ES_REG(ensoniq, SERIAL)); + outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); + spin_unlock(&ensoniq->reg_lock); + + if (status & ES_UART) + snd_ensoniq_midi_interrupt(ensoniq); + if ((status & ES_DAC2) && ensoniq->playback2_substream) + snd_pcm_period_elapsed(ensoniq->playback2_substream); + if ((status & ES_ADC) && ensoniq->capture_substream) + snd_pcm_period_elapsed(ensoniq->capture_substream); + if ((status & ES_DAC1) && ensoniq->playback1_substream) + snd_pcm_period_elapsed(ensoniq->playback1_substream); + return IRQ_HANDLED; +} + +static int __devinit snd_audiopci_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + struct snd_card *card; + struct ensoniq *ensoniq; + int err, pcm_devs[2]; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_ensoniq_create(card, pci, &ensoniq)) < 0) { + snd_card_free(card); + return err; + } + card->private_data = ensoniq; + + pcm_devs[0] = 0; pcm_devs[1] = 1; +#ifdef CHIP1370 + if ((err = snd_ensoniq_1370_mixer(ensoniq)) < 0) { + snd_card_free(card); + return err; + } +#endif +#ifdef CHIP1371 + if ((err = snd_ensoniq_1371_mixer(ensoniq, spdif[dev], lineio[dev])) < 0) { + snd_card_free(card); + return err; + } +#endif + if ((err = snd_ensoniq_pcm(ensoniq, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ensoniq_pcm2(ensoniq, 1, NULL)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_ensoniq_midi(ensoniq, 0, NULL)) < 0) { + snd_card_free(card); + return err; + } + + snd_ensoniq_create_gameport(ensoniq, dev); + + strcpy(card->driver, DRIVER_NAME); + + strcpy(card->shortname, "Ensoniq AudioPCI"); + sprintf(card->longname, "%s %s at 0x%lx, irq %i", + card->shortname, + card->driver, + ensoniq->port, + ensoniq->irq); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_audiopci_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = DRIVER_NAME, + .id_table = snd_audiopci_ids, + .probe = snd_audiopci_probe, + .remove = __devexit_p(snd_audiopci_remove), +#ifdef CONFIG_PM + .suspend = snd_ensoniq_suspend, + .resume = snd_ensoniq_resume, +#endif +}; + +static int __init alsa_card_ens137x_init(void) +{ + return pci_register_driver(&driver); +} + +static void __exit alsa_card_ens137x_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_ens137x_init) +module_exit(alsa_card_ens137x_exit) diff -Nur sound/pci/es1938.c sound/pci/es1938.c --- sound/pci/es1938.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/es1938.c 2007-07-24 02:00:10.000000000 +0200 @@ -1066,15 +1066,7 @@ return snd_es1938_mixer_bits(chip, 0x1c, 0x07, val) != val; } -static int snd_es1938_info_spatializer_enable(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_es1938_info_spatializer_enable snd_ctl_boolean_mono_info static int snd_es1938_get_spatializer_enable(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1120,15 +1112,7 @@ return 0; } -static int snd_es1938_info_hw_switch(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_es1938_info_hw_switch snd_ctl_boolean_stereo_info static int snd_es1938_get_hw_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff -Nur sound/pci/hda/Makefile sound/pci/hda/Makefile --- sound/pci/hda/Makefile 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/hda/Makefile 2007-07-31 11:13:52.000000000 +0200 @@ -1,19 +1,18 @@ -snd-hda-intel-objs := hda_intel.o +snd-hda-intel-y := hda_intel.o # since snd-hda-intel is the only driver using hda-codec, # merge it into a single module although it was originally # designed to be individual modules -snd-hda-intel-objs += hda_codec.o \ - hda_generic.o \ - patch_realtek.o \ - patch_cmedia.o \ - patch_analog.o \ - patch_sigmatel.o \ - patch_si3054.o \ - patch_atihdmi.o \ - patch_conexant.o \ - patch_via.o -ifdef CONFIG_PROC_FS -snd-hda-intel-objs += hda_proc.o -endif +snd-hda-intel-y += hda_codec.o +snd-hda-intel-$(CONFIG_PROC_FS) += hda_proc.o +snd-hda-intel-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o +snd-hda-intel-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o +snd-hda-intel-$(CONFIG_SND_HDA_CODEC_REALTEK) += patch_realtek.o +snd-hda-intel-$(CONFIG_SND_HDA_CODEC_CMEDIA) += patch_cmedia.o +snd-hda-intel-$(CONFIG_SND_HDA_CODEC_ANALOG) += patch_analog.o +snd-hda-intel-$(CONFIG_SND_HDA_CODEC_SIGMATEL) += patch_sigmatel.o +snd-hda-intel-$(CONFIG_SND_HDA_CODEC_SI3054) += patch_si3054.o +snd-hda-intel-$(CONFIG_SND_HDA_CODEC_ATIHDMI) += patch_atihdmi.o +snd-hda-intel-$(CONFIG_SND_HDA_CODEC_CONEXANT) += patch_conexant.o +snd-hda-intel-$(CONFIG_SND_HDA_CODEC_VIA) += patch_via.o obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o diff -Nur sound/pci/hda/hda_codec.c sound/pci/hda/hda_codec.c --- sound/pci/hda/hda_codec.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/hda/hda_codec.c 2007-08-17 02:00:07.000000000 +0200 @@ -31,7 +31,15 @@ #include #include #include "hda_local.h" +#include +#ifdef CONFIG_SND_HDA_POWER_SAVE +/* define this option here to hide as static */ +static int power_save = 10; +module_param(power_save, int, 0644); +MODULE_PARM_DESC(power_save, "Automatic power-saving timeout " + "(in second, 0 = disable)."); +#endif /* * vendor / preset table @@ -59,6 +67,13 @@ #include "hda_patch.h" +#ifdef CONFIG_SND_HDA_POWER_SAVE +static void hda_power_work(struct work_struct *work); +static void hda_keep_power_on(struct hda_codec *codec); +#else +static inline void hda_keep_power_on(struct hda_codec *codec) {} +#endif + /** * snd_hda_codec_read - send a command and get the response * @codec: the HDA codec @@ -76,12 +91,14 @@ unsigned int verb, unsigned int parm) { unsigned int res; + snd_hda_power_up(codec); mutex_lock(&codec->bus->cmd_mutex); if (!codec->bus->ops.command(codec, nid, direct, verb, parm)) res = codec->bus->ops.get_response(codec); else res = (unsigned int)-1; mutex_unlock(&codec->bus->cmd_mutex); + snd_hda_power_down(codec); return res; } @@ -101,9 +118,11 @@ unsigned int verb, unsigned int parm) { int err; + snd_hda_power_up(codec); mutex_lock(&codec->bus->cmd_mutex); err = codec->bus->ops.command(codec, nid, direct, verb, parm); mutex_unlock(&codec->bus->cmd_mutex); + snd_hda_power_down(codec); return err; } @@ -387,6 +406,13 @@ return 0; } +#ifdef CONFIG_SND_HDA_GENERIC +#define is_generic_config(codec) \ + (codec->bus->modelname && !strcmp(codec->bus->modelname, "generic")) +#else +#define is_generic_config(codec) 0 +#endif + /* * find a matching codec preset */ @@ -395,7 +421,7 @@ { const struct hda_codec_preset **tbl, *preset; - if (codec->bus->modelname && !strcmp(codec->bus->modelname, "generic")) + if (is_generic_config(codec)) return NULL; /* use the generic parser */ for (tbl = hda_preset_tables; *tbl; tbl++) { @@ -486,6 +512,10 @@ } +static void init_hda_cache(struct hda_cache_rec *cache, + unsigned int record_size); +static inline void free_hda_cache(struct hda_cache_rec *cache); + /* * codec destructor */ @@ -493,17 +523,20 @@ { if (!codec) return; +#ifdef CONFIG_SND_HDA_POWER_SAVE + cancel_delayed_work(&codec->power_work); + flush_scheduled_work(); +#endif list_del(&codec->list); codec->bus->caddr_tbl[codec->addr] = NULL; if (codec->patch_ops.free) codec->patch_ops.free(codec); - kfree(codec->amp_info); + free_hda_cache(&codec->amp_cache); + free_hda_cache(&codec->cmd_cache); kfree(codec->wcaps); kfree(codec); } -static void init_amp_hash(struct hda_codec *codec); - /** * snd_hda_codec_new - create a HDA codec * @bus: the bus to assign @@ -537,7 +570,17 @@ codec->bus = bus; codec->addr = codec_addr; mutex_init(&codec->spdif_mutex); - init_amp_hash(codec); + init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); + init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); + +#ifdef CONFIG_SND_HDA_POWER_SAVE + INIT_DELAYED_WORK(&codec->power_work, hda_power_work); + /* snd_hda_codec_new() marks the codec as power-up, and leave it as is. + * the caller has to power down appropriatley after initialization + * phase. + */ + hda_keep_power_on(codec); +#endif list_add_tail(&codec->list, &bus->codec_list); bus->caddr_tbl[codec_addr] = codec; @@ -581,10 +624,26 @@ snd_hda_get_codec_name(codec, bus->card->mixername, sizeof(bus->card->mixername)); - if (codec->preset && codec->preset->patch) - err = codec->preset->patch(codec); - else +#ifdef CONFIG_SND_HDA_GENERIC + if (is_generic_config(codec)) { err = snd_hda_parse_generic_codec(codec); + goto patched; + } +#endif + if (codec->preset && codec->preset->patch) { + err = codec->preset->patch(codec); + goto patched; + } + + /* call the default parser */ +#ifdef CONFIG_SND_HDA_GENERIC + err = snd_hda_parse_generic_codec(codec); +#else + printk(KERN_ERR "hda-codec: No codec parser is available\n"); + err = -ENODEV; +#endif + + patched: if (err < 0) { snd_hda_codec_free(codec); return err; @@ -594,6 +653,9 @@ init_unsol_queue(bus); snd_hda_codec_proc_new(codec); +#ifdef CONFIG_SND_HDA_HWDEP + snd_hda_create_hwdep(codec); +#endif sprintf(component, "HDA:%08x", codec->vendor_id); snd_component_add(codec->bus->card, component); @@ -637,59 +699,72 @@ #define INFO_AMP_VOL(ch) (1 << (1 + (ch))) /* initialize the hash table */ -static void __devinit init_amp_hash(struct hda_codec *codec) +static void __devinit init_hda_cache(struct hda_cache_rec *cache, + unsigned int record_size) +{ + memset(cache, 0, sizeof(*cache)); + memset(cache->hash, 0xff, sizeof(cache->hash)); + cache->record_size = record_size; +} + +static inline void free_hda_cache(struct hda_cache_rec *cache) { - memset(codec->amp_hash, 0xff, sizeof(codec->amp_hash)); - codec->num_amp_entries = 0; - codec->amp_info_size = 0; - codec->amp_info = NULL; + kfree(cache->buffer); } /* query the hash. allocate an entry if not found. */ -static struct hda_amp_info *get_alloc_amp_hash(struct hda_codec *codec, u32 key) +static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache, + u32 key) { - u16 idx = key % (u16)ARRAY_SIZE(codec->amp_hash); - u16 cur = codec->amp_hash[idx]; - struct hda_amp_info *info; + u16 idx = key % (u16)ARRAY_SIZE(cache->hash); + u16 cur = cache->hash[idx]; + struct hda_cache_head *info; while (cur != 0xffff) { - info = &codec->amp_info[cur]; + info = (struct hda_cache_head *)(cache->buffer + + cur * cache->record_size); if (info->key == key) return info; cur = info->next; } /* add a new hash entry */ - if (codec->num_amp_entries >= codec->amp_info_size) { + if (cache->num_entries >= cache->size) { /* reallocate the array */ - int new_size = codec->amp_info_size + 64; - struct hda_amp_info *new_info; - new_info = kcalloc(new_size, sizeof(struct hda_amp_info), - GFP_KERNEL); - if (!new_info) { + unsigned int new_size = cache->size + 64; + void *new_buffer; + new_buffer = kcalloc(new_size, cache->record_size, GFP_KERNEL); + if (!new_buffer) { snd_printk(KERN_ERR "hda_codec: " "can't malloc amp_info\n"); return NULL; } - if (codec->amp_info) { - memcpy(new_info, codec->amp_info, - codec->amp_info_size * - sizeof(struct hda_amp_info)); - kfree(codec->amp_info); - } - codec->amp_info_size = new_size; - codec->amp_info = new_info; - } - cur = codec->num_amp_entries++; - info = &codec->amp_info[cur]; + if (cache->buffer) { + memcpy(new_buffer, cache->buffer, + cache->size * cache->record_size); + kfree(cache->buffer); + } + cache->size = new_size; + cache->buffer = new_buffer; + } + cur = cache->num_entries++; + info = (struct hda_cache_head *)(cache->buffer + + cur * cache->record_size); info->key = key; - info->status = 0; /* not initialized yet */ - info->next = codec->amp_hash[idx]; - codec->amp_hash[idx] = cur; + info->val = 0; + info->next = cache->hash[idx]; + cache->hash[idx] = cur; return info; } +/* query and allocate an amp hash entry */ +static inline struct hda_amp_info * +get_alloc_amp_hash(struct hda_codec *codec, u32 key) +{ + return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key); +} + /* * query AMP capabilities for the given widget and direction */ @@ -700,7 +775,7 @@ info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, 0)); if (!info) return 0; - if (!(info->status & INFO_AMP_CAPS)) { + if (!(info->head.val & INFO_AMP_CAPS)) { if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD)) nid = codec->afg; info->amp_caps = snd_hda_param_read(codec, nid, @@ -708,7 +783,7 @@ AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); if (info->amp_caps) - info->status |= INFO_AMP_CAPS; + info->head.val |= INFO_AMP_CAPS; } return info->amp_caps; } @@ -722,7 +797,7 @@ if (!info) return -EINVAL; info->amp_caps = caps; - info->status |= INFO_AMP_CAPS; + info->head.val |= INFO_AMP_CAPS; return 0; } @@ -736,7 +811,7 @@ { u32 val, parm; - if (info->status & INFO_AMP_VOL(ch)) + if (info->head.val & INFO_AMP_VOL(ch)) return info->vol[ch]; parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT; @@ -745,7 +820,7 @@ val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, parm); info->vol[ch] = val & 0xff; - info->status |= INFO_AMP_VOL(ch); + info->head.val |= INFO_AMP_VOL(ch); return info->vol[ch]; } @@ -792,12 +867,50 @@ return 0; val &= mask; val |= get_vol_mute(codec, info, nid, ch, direction, idx) & ~mask; - if (info->vol[ch] == val && !codec->in_resume) + if (info->vol[ch] == val) return 0; put_vol_mute(codec, info, nid, ch, direction, idx, val); return 1; } +/* + * update the AMP stereo with the same mask and value + */ +int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, + int direction, int idx, int mask, int val) +{ + int ch, ret = 0; + for (ch = 0; ch < 2; ch++) + ret |= snd_hda_codec_amp_update(codec, nid, ch, direction, + idx, mask, val); + return ret; +} + +#ifdef SND_HDA_NEEDS_RESUME +/* resume the all amp commands from the cache */ +void snd_hda_codec_resume_amp(struct hda_codec *codec) +{ + struct hda_amp_info *buffer = codec->amp_cache.buffer; + int i; + + for (i = 0; i < codec->amp_cache.size; i++, buffer++) { + u32 key = buffer->head.key; + hda_nid_t nid; + unsigned int idx, dir, ch; + if (!key) + continue; + nid = key & 0xff; + idx = (key >> 16) & 0xff; + dir = (key >> 24) & 0xff; + for (ch = 0; ch < 2; ch++) { + if (!(buffer->head.val & INFO_AMP_VOL(ch))) + continue; + put_vol_mute(codec, buffer, nid, ch, dir, idx, + buffer->vol[ch]); + } + } +} +#endif /* SND_HDA_NEEDS_RESUME */ /* * AMP control callbacks @@ -844,9 +957,11 @@ long *valp = ucontrol->value.integer.value; if (chs & 1) - *valp++ = snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & 0x7f; + *valp++ = snd_hda_codec_amp_read(codec, nid, 0, dir, idx) + & HDA_AMP_VOLMASK; if (chs & 2) - *valp = snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & 0x7f; + *valp = snd_hda_codec_amp_read(codec, nid, 1, dir, idx) + & HDA_AMP_VOLMASK; return 0; } @@ -861,6 +976,7 @@ long *valp = ucontrol->value.integer.value; int change = 0; + snd_hda_power_up(codec); if (chs & 1) { change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx, 0x7f, *valp); @@ -869,6 +985,7 @@ if (chs & 2) change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx, 0x7f, *valp); + snd_hda_power_down(codec); return change; } @@ -923,10 +1040,10 @@ if (chs & 1) *valp++ = (snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & - 0x80) ? 0 : 1; + HDA_AMP_MUTE) ? 0 : 1; if (chs & 2) *valp = (snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & - 0x80) ? 0 : 1; + HDA_AMP_MUTE) ? 0 : 1; return 0; } @@ -941,15 +1058,22 @@ long *valp = ucontrol->value.integer.value; int change = 0; + snd_hda_power_up(codec); if (chs & 1) { change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx, - 0x80, *valp ? 0 : 0x80); + HDA_AMP_MUTE, + *valp ? 0 : HDA_AMP_MUTE); valp++; } if (chs & 2) change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx, - 0x80, *valp ? 0 : 0x80); - + HDA_AMP_MUTE, + *valp ? 0 : HDA_AMP_MUTE); +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (codec->patch_ops.check_power_status) + codec->patch_ops.check_power_status(codec, nid); +#endif + snd_hda_power_down(codec); return change; } @@ -1002,6 +1126,93 @@ } /* + * generic bound volume/swtich controls + */ +int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_bind_ctls *c; + int err; + + c = (struct hda_bind_ctls *)kcontrol->private_value; + mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */ + kcontrol->private_value = *c->values; + err = c->ops->info(kcontrol, uinfo); + kcontrol->private_value = (long)c; + mutex_unlock(&codec->spdif_mutex); + return err; +} + +int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_bind_ctls *c; + int err; + + c = (struct hda_bind_ctls *)kcontrol->private_value; + mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */ + kcontrol->private_value = *c->values; + err = c->ops->get(kcontrol, ucontrol); + kcontrol->private_value = (long)c; + mutex_unlock(&codec->spdif_mutex); + return err; +} + +int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_bind_ctls *c; + unsigned long *vals; + int err = 0, change = 0; + + c = (struct hda_bind_ctls *)kcontrol->private_value; + mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */ + for (vals = c->values; *vals; vals++) { + kcontrol->private_value = *vals; + err = c->ops->put(kcontrol, ucontrol); + if (err < 0) + break; + change |= err; + } + kcontrol->private_value = (long)c; + mutex_unlock(&codec->spdif_mutex); + return err < 0 ? err : change; +} + +int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *tlv) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_bind_ctls *c; + int err; + + c = (struct hda_bind_ctls *)kcontrol->private_value; + mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */ + kcontrol->private_value = *c->values; + err = c->ops->tlv(kcontrol, op_flag, size, tlv); + kcontrol->private_value = (long)c; + mutex_unlock(&codec->spdif_mutex); + return err; +} + +struct hda_ctl_ops snd_hda_bind_vol = { + .info = snd_hda_mixer_amp_volume_info, + .get = snd_hda_mixer_amp_volume_get, + .put = snd_hda_mixer_amp_volume_put, + .tlv = snd_hda_mixer_amp_tlv +}; + +struct hda_ctl_ops snd_hda_bind_sw = { + .info = snd_hda_mixer_amp_switch_info, + .get = snd_hda_mixer_amp_switch_get, + .put = snd_hda_mixer_amp_switch_put, + .tlv = snd_hda_mixer_amp_tlv +}; + +/* * SPDIF out controls */ @@ -1118,26 +1329,20 @@ change = codec->spdif_ctls != val; codec->spdif_ctls = val; - if (change || codec->in_resume) { - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, - val & 0xff); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_2, - val >> 8); + if (change) { + snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_DIGI_CONVERT_1, + val & 0xff); + snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_DIGI_CONVERT_2, + val >> 8); } mutex_unlock(&codec->spdif_mutex); return change; } -static int snd_hda_spdif_out_switch_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_hda_spdif_out_switch_info snd_ctl_boolean_mono_info static int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1161,17 +1366,16 @@ if (ucontrol->value.integer.value[0]) val |= AC_DIG1_ENABLE; change = codec->spdif_ctls != val; - if (change || codec->in_resume) { + if (change) { codec->spdif_ctls = val; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, - val & 0xff); + snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_DIGI_CONVERT_1, + val & 0xff); /* unmute amp switch (if any) */ if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) && (val & AC_DIG1_ENABLE)) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | - AC_AMP_SET_OUTPUT); + snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, + HDA_AMP_MUTE, 0); } mutex_unlock(&codec->spdif_mutex); return change; @@ -1219,8 +1423,7 @@ * * Returns 0 if successful, or a negative error code. */ -int __devinit snd_hda_create_spdif_out_ctls(struct hda_codec *codec, - hda_nid_t nid) +int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid) { int err; struct snd_kcontrol *kctl; @@ -1264,10 +1467,10 @@ mutex_lock(&codec->spdif_mutex); change = codec->spdif_in_enable != val; - if (change || codec->in_resume) { + if (change) { codec->spdif_in_enable = val; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, - val); + snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_DIGI_CONVERT_1, val); } mutex_unlock(&codec->spdif_mutex); return change; @@ -1318,8 +1521,7 @@ * * Returns 0 if successful, or a negative error code. */ -int __devinit snd_hda_create_spdif_in_ctls(struct hda_codec *codec, - hda_nid_t nid) +int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) { int err; struct snd_kcontrol *kctl; @@ -1338,6 +1540,79 @@ return 0; } +#ifdef SND_HDA_NEEDS_RESUME +/* + * command cache + */ + +/* build a 32bit cache key with the widget id and the command parameter */ +#define build_cmd_cache_key(nid, verb) ((verb << 8) | nid) +#define get_cmd_cache_nid(key) ((key) & 0xff) +#define get_cmd_cache_cmd(key) (((key) >> 8) & 0xffff) + +/** + * snd_hda_codec_write_cache - send a single command with caching + * @codec: the HDA codec + * @nid: NID to send the command + * @direct: direct flag + * @verb: the verb to send + * @parm: the parameter for the verb + * + * Send a single command without waiting for response. + * + * Returns 0 if successful, or a negative error code. + */ +int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, + int direct, unsigned int verb, unsigned int parm) +{ + int err; + snd_hda_power_up(codec); + mutex_lock(&codec->bus->cmd_mutex); + err = codec->bus->ops.command(codec, nid, direct, verb, parm); + if (!err) { + struct hda_cache_head *c; + u32 key = build_cmd_cache_key(nid, verb); + c = get_alloc_hash(&codec->cmd_cache, key); + if (c) + c->val = parm; + } + mutex_unlock(&codec->bus->cmd_mutex); + snd_hda_power_down(codec); + return err; +} + +/* resume the all commands from the cache */ +void snd_hda_codec_resume_cache(struct hda_codec *codec) +{ + struct hda_cache_head *buffer = codec->cmd_cache.buffer; + int i; + + for (i = 0; i < codec->cmd_cache.size; i++, buffer++) { + u32 key = buffer->key; + if (!key) + continue; + snd_hda_codec_write(codec, get_cmd_cache_nid(key), 0, + get_cmd_cache_cmd(key), buffer->val); + } +} + +/** + * snd_hda_sequence_write_cache - sequence writes with caching + * @codec: the HDA codec + * @seq: VERB array to send + * + * Send the commands sequentially from the given array. + * Thte commands are recorded on cache for power-save and resume. + * The array must be terminated with NID=0. + */ +void snd_hda_sequence_write_cache(struct hda_codec *codec, + const struct hda_verb *seq) +{ + for (; seq->nid; seq++) + snd_hda_codec_write_cache(codec, seq->nid, 0, seq->verb, + seq->param); +} +#endif /* SND_HDA_NEEDS_RESUME */ /* * set power state of the codec @@ -1345,23 +1620,72 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, unsigned int power_state) { - hda_nid_t nid, nid_start; - int nodes; + hda_nid_t nid; + int i; snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state); - nodes = snd_hda_get_sub_nodes(codec, fg, &nid_start); - for (nid = nid_start; nid < nodes + nid_start; nid++) { + nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, nid++) { if (get_wcaps(codec, nid) & AC_WCAP_POWER) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, power_state); } - if (power_state == AC_PWRST_D0) + if (power_state == AC_PWRST_D0) { + unsigned long end_time; + int state; msleep(10); + /* wait until the codec reachs to D0 */ + end_time = jiffies + msecs_to_jiffies(500); + do { + state = snd_hda_codec_read(codec, fg, 0, + AC_VERB_GET_POWER_STATE, 0); + if (state == power_state) + break; + msleep(1); + } while (time_after_eq(end_time, jiffies)); + } +} + +#ifdef SND_HDA_NEEDS_RESUME +/* + * call suspend and power-down; used both from PM and power-save + */ +static void hda_call_codec_suspend(struct hda_codec *codec) +{ + if (codec->patch_ops.suspend) + codec->patch_ops.suspend(codec, PMSG_SUSPEND); + hda_set_power_state(codec, + codec->afg ? codec->afg : codec->mfg, + AC_PWRST_D3); +#ifdef CONFIG_SND_HDA_POWER_SAVE + cancel_delayed_work(&codec->power_work); + codec->power_on = 0; + codec->power_transition = 0; +#endif +} + +/* + * kick up codec; used both from PM and power-save + */ +static void hda_call_codec_resume(struct hda_codec *codec) +{ + hda_set_power_state(codec, + codec->afg ? codec->afg : codec->mfg, + AC_PWRST_D0); + if (codec->patch_ops.resume) + codec->patch_ops.resume(codec); + else { + if (codec->patch_ops.init) + codec->patch_ops.init(codec); + snd_hda_codec_resume_amp(codec); + snd_hda_codec_resume_cache(codec); + } } +#endif /* SND_HDA_NEEDS_RESUME */ /** @@ -1376,28 +1700,24 @@ { struct hda_codec *codec; - /* build controls */ - list_for_each_entry(codec, &bus->codec_list, list) { - int err; - if (!codec->patch_ops.build_controls) - continue; - err = codec->patch_ops.build_controls(codec); - if (err < 0) - return err; - } - - /* initialize */ list_for_each_entry(codec, &bus->codec_list, list) { - int err; + int err = 0; + /* fake as if already powered-on */ + hda_keep_power_on(codec); + /* then fire up */ hda_set_power_state(codec, codec->afg ? codec->afg : codec->mfg, AC_PWRST_D0); - if (!codec->patch_ops.init) - continue; - err = codec->patch_ops.init(codec); + /* continue to initialize... */ + if (codec->patch_ops.init) + err = codec->patch_ops.init(codec); + if (!err && codec->patch_ops.build_controls) + err = codec->patch_ops.build_controls(codec); + snd_hda_power_down(codec); if (err < 0) return err; } + return 0; } @@ -1789,9 +2109,9 @@ * * If no entries are matching, the function returns a negative value. */ -int __devinit snd_hda_check_board_config(struct hda_codec *codec, - int num_configs, const char **models, - const struct snd_pci_quirk *tbl) +int snd_hda_check_board_config(struct hda_codec *codec, + int num_configs, const char **models, + const struct snd_pci_quirk *tbl) { if (codec->bus->modelname && models) { int i; @@ -1841,10 +2161,9 @@ * * Returns 0 if successful, or a negative error code. */ -int __devinit snd_hda_add_new_ctls(struct hda_codec *codec, - struct snd_kcontrol_new *knew) +int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew) { - int err; + int err; for (; knew->name; knew++) { struct snd_kcontrol *kctl; @@ -1867,6 +2186,91 @@ return 0; } +#ifdef CONFIG_SND_HDA_POWER_SAVE +static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, + unsigned int power_state); + +static void hda_power_work(struct work_struct *work) +{ + struct hda_codec *codec = + container_of(work, struct hda_codec, power_work.work); + + if (!codec->power_on || codec->power_count) + return; + + hda_call_codec_suspend(codec); + if (codec->bus->ops.pm_notify) + codec->bus->ops.pm_notify(codec); +} + +static void hda_keep_power_on(struct hda_codec *codec) +{ + codec->power_count++; + codec->power_on = 1; +} + +void snd_hda_power_up(struct hda_codec *codec) +{ + codec->power_count++; + if (codec->power_on || codec->power_transition) + return; + + codec->power_on = 1; + if (codec->bus->ops.pm_notify) + codec->bus->ops.pm_notify(codec); + hda_call_codec_resume(codec); + cancel_delayed_work(&codec->power_work); + codec->power_transition = 0; +} + +void snd_hda_power_down(struct hda_codec *codec) +{ + --codec->power_count; + if (!codec->power_on || codec->power_count || codec->power_transition) + return; + if (power_save) { + codec->power_transition = 1; /* avoid reentrance */ + schedule_delayed_work(&codec->power_work, + msecs_to_jiffies(power_save * 1000)); + } +} + +int snd_hda_check_amp_list_power(struct hda_codec *codec, + struct hda_loopback_check *check, + hda_nid_t nid) +{ + struct hda_amp_list *p; + int ch, v; + + if (!check->amplist) + return 0; + for (p = check->amplist; p->nid; p++) { + if (p->nid == nid) + break; + } + if (!p->nid) + return 0; /* nothing changed */ + + for (p = check->amplist; p->nid; p++) { + for (ch = 0; ch < 2; ch++) { + v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, + p->idx); + if (!(v & HDA_AMP_MUTE) && v > 0) { + if (!check->power_on) { + check->power_on = 1; + snd_hda_power_up(codec); + } + return 1; + } + } + } + if (check->power_on) { + check->power_on = 0; + snd_hda_power_down(codec); + } + return 0; +} +#endif /* * Channel mode helper @@ -1913,12 +2317,12 @@ mode = ucontrol->value.enumerated.item[0]; snd_assert(mode < num_chmodes, return -EINVAL); - if (*max_channelsp == chmode[mode].channels && !codec->in_resume) + if (*max_channelsp == chmode[mode].channels) return 0; /* change the current channel setting */ *max_channelsp = chmode[mode].channels; if (chmode[mode].sequence) - snd_hda_sequence_write(codec, chmode[mode].sequence); + snd_hda_sequence_write_cache(codec, chmode[mode].sequence); return 1; } @@ -1951,10 +2355,10 @@ idx = ucontrol->value.enumerated.item[0]; if (idx >= imux->num_items) idx = imux->num_items - 1; - if (*cur_val == idx && !codec->in_resume) + if (*cur_val == idx) return 0; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, - imux->items[idx].index); + snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, + imux->items[idx].index); *cur_val = idx; return 1; } @@ -2118,7 +2522,7 @@ * Helper for automatic ping configuration */ -static int __devinit is_in_nid_list(hda_nid_t nid, hda_nid_t *list) +static int is_in_nid_list(hda_nid_t nid, hda_nid_t *list) { for (; *list; list++) if (*list == nid) @@ -2169,9 +2573,9 @@ * The digital input/output pins are assigned to dig_in_pin and dig_out_pin, * respectively. */ -int __devinit snd_hda_parse_pin_def_config(struct hda_codec *codec, - struct auto_pin_cfg *cfg, - hda_nid_t *ignore_nids) +int snd_hda_parse_pin_def_config(struct hda_codec *codec, + struct auto_pin_cfg *cfg, + hda_nid_t *ignore_nids) { hda_nid_t nid, nid_start; int nodes; @@ -2371,93 +2775,36 @@ { struct hda_codec *codec; - /* FIXME: should handle power widget capabilities */ list_for_each_entry(codec, &bus->codec_list, list) { - if (codec->patch_ops.suspend) - codec->patch_ops.suspend(codec, state); - hda_set_power_state(codec, - codec->afg ? codec->afg : codec->mfg, - AC_PWRST_D3); +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (!codec->power_on) + continue; +#endif + hda_call_codec_suspend(codec); } return 0; } +#ifndef CONFIG_SND_HDA_POWER_SAVE /** * snd_hda_resume - resume the codecs * @bus: the HDA bus * @state: resume state * * Returns 0 if successful. + * + * This fucntion is defined only when POWER_SAVE isn't set. + * In the power-save mode, the codec is resumed dynamically. */ int snd_hda_resume(struct hda_bus *bus) { struct hda_codec *codec; list_for_each_entry(codec, &bus->codec_list, list) { - hda_set_power_state(codec, - codec->afg ? codec->afg : codec->mfg, - AC_PWRST_D0); - if (codec->patch_ops.resume) - codec->patch_ops.resume(codec); - } - return 0; -} - -/** - * snd_hda_resume_ctls - resume controls in the new control list - * @codec: the HDA codec - * @knew: the array of struct snd_kcontrol_new - * - * This function resumes the mixer controls in the struct snd_kcontrol_new array, - * originally for snd_hda_add_new_ctls(). - * The array must be terminated with an empty entry as terminator. - */ -int snd_hda_resume_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew) -{ - struct snd_ctl_elem_value *val; - - val = kmalloc(sizeof(*val), GFP_KERNEL); - if (!val) - return -ENOMEM; - codec->in_resume = 1; - for (; knew->name; knew++) { - int i, count; - count = knew->count ? knew->count : 1; - for (i = 0; i < count; i++) { - memset(val, 0, sizeof(*val)); - val->id.iface = knew->iface; - val->id.device = knew->device; - val->id.subdevice = knew->subdevice; - strcpy(val->id.name, knew->name); - val->id.index = knew->index ? knew->index : i; - /* Assume that get callback reads only from cache, - * not accessing to the real hardware - */ - if (snd_ctl_elem_read(codec->bus->card, val) < 0) - continue; - snd_ctl_elem_write(codec->bus->card, NULL, val); - } + hda_call_codec_resume(codec); } - codec->in_resume = 0; - kfree(val); return 0; } +#endif /* !CONFIG_SND_HDA_POWER_SAVE */ -/** - * snd_hda_resume_spdif_out - resume the digital out - * @codec: the HDA codec - */ -int snd_hda_resume_spdif_out(struct hda_codec *codec) -{ - return snd_hda_resume_ctls(codec, dig_mixes); -} - -/** - * snd_hda_resume_spdif_in - resume the digital in - * @codec: the HDA codec - */ -int snd_hda_resume_spdif_in(struct hda_codec *codec) -{ - return snd_hda_resume_ctls(codec, dig_in_ctls); -} #endif diff -Nur sound/pci/hda/hda_codec.h sound/pci/hda/hda_codec.h --- sound/pci/hda/hda_codec.h 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/hda/hda_codec.h 2007-08-17 02:00:07.000000000 +0200 @@ -24,6 +24,11 @@ #include #include #include +#include + +#if defined(CONFIG_PM) || defined(CONFIG_SND_HDA_POWER_SAVE) +#define SND_HDA_NEEDS_RESUME /* resume control code is required */ +#endif /* * nodes @@ -199,7 +204,9 @@ #define AC_AMPCAP_OFFSET_SHIFT 0 #define AC_AMPCAP_NUM_STEPS (0x7f<<8) /* number of steps */ #define AC_AMPCAP_NUM_STEPS_SHIFT 8 -#define AC_AMPCAP_STEP_SIZE (0x7f<<16) /* step size 0-32dB in 0.25dB */ +#define AC_AMPCAP_STEP_SIZE (0x7f<<16) /* step size 0-32dB + * in 0.25dB + */ #define AC_AMPCAP_STEP_SIZE_SHIFT 16 #define AC_AMPCAP_MUTE (1<<31) /* mute capable */ #define AC_AMPCAP_MUTE_SHIFT 31 @@ -409,6 +416,10 @@ unsigned int (*get_response)(struct hda_codec *codec); /* free the private data */ void (*private_free)(struct hda_bus *); +#ifdef CONFIG_SND_HDA_POWER_SAVE + /* notify power-up/down from codec to contoller */ + void (*pm_notify)(struct hda_codec *codec); +#endif }; /* template to pass to the bus constructor */ @@ -436,7 +447,8 @@ /* codec linked list */ struct list_head codec_list; - struct hda_codec *caddr_tbl[HDA_MAX_CODEC_ADDRESS + 1]; /* caddr -> codec */ + /* link caddr -> codec */ + struct hda_codec *caddr_tbl[HDA_MAX_CODEC_ADDRESS + 1]; struct mutex cmd_mutex; @@ -469,19 +481,34 @@ int (*init)(struct hda_codec *codec); void (*free)(struct hda_codec *codec); void (*unsol_event)(struct hda_codec *codec, unsigned int res); -#ifdef CONFIG_PM +#ifdef SND_HDA_NEEDS_RESUME int (*suspend)(struct hda_codec *codec, pm_message_t state); int (*resume)(struct hda_codec *codec); #endif +#ifdef CONFIG_SND_HDA_POWER_SAVE + int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid); +#endif }; /* record for amp information cache */ -struct hda_amp_info { +struct hda_cache_head { u32 key; /* hash key */ + u16 val; /* assigned value */ + u16 next; /* next link; -1 = terminal */ +}; + +struct hda_amp_info { + struct hda_cache_head head; u32 amp_caps; /* amp capabilities */ u16 vol[2]; /* current volume & mute */ - u16 status; /* update flag */ - u16 next; /* next link */ +}; + +struct hda_cache_rec { + u16 hash[64]; /* hash table for index */ + unsigned int num_entries; /* number of assigned entries */ + unsigned int size; /* allocated size */ + unsigned int record_size; /* record size (including header) */ + void *buffer; /* hash table entries */ }; /* PCM callbacks */ @@ -499,7 +526,7 @@ /* PCM information for each substream */ struct hda_pcm_stream { - unsigned int substreams; /* number of substreams, 0 = not exist */ + unsigned int substreams; /* number of substreams, 0 = not exist*/ unsigned int channels_min; /* min. number of channels */ unsigned int channels_max; /* max. number of channels */ hda_nid_t nid; /* default NID to query rates/formats/bps, or set up */ @@ -536,11 +563,6 @@ /* set by patch */ struct hda_codec_ops patch_ops; - /* resume phase - all controls should update even if - * the values are not changed - */ - unsigned int in_resume; - /* PCM to create, set by patch_ops.build_pcms callback */ unsigned int num_pcms; struct hda_pcm *pcm_info; @@ -553,16 +575,22 @@ hda_nid_t start_nid; u32 *wcaps; - /* hash for amp access */ - u16 amp_hash[32]; - int num_amp_entries; - int amp_info_size; - struct hda_amp_info *amp_info; + struct hda_cache_rec amp_cache; /* cache for amp access */ + struct hda_cache_rec cmd_cache; /* cache for other commands */ struct mutex spdif_mutex; unsigned int spdif_status; /* IEC958 status bits */ unsigned short spdif_ctls; /* SPDIF control bits */ unsigned int spdif_in_enable; /* SPDIF input enable? */ + + struct snd_hwdep *hwdep; /* assigned hwdep device */ + +#ifdef CONFIG_SND_HDA_POWER_SAVE + unsigned int power_on :1; /* current (global) power-state */ + unsigned int power_transition :1; /* power-state in transition */ + int power_count; /* current (global) power refcount */ + struct delayed_work power_work; /* delayed task for powerdown */ +#endif }; /* direction */ @@ -582,13 +610,17 @@ /* * low level functions */ -unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, int direct, +unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, + int direct, unsigned int verb, unsigned int parm); int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm); -#define snd_hda_param_read(codec, nid, param) snd_hda_codec_read(codec, nid, 0, AC_VERB_PARAMETERS, param) -int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *start_id); -int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns); +#define snd_hda_param_read(codec, nid, param) \ + snd_hda_codec_read(codec, nid, 0, AC_VERB_PARAMETERS, param) +int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, + hda_nid_t *start_id); +int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, + hda_nid_t *conn_list, int max_conns); struct hda_verb { hda_nid_t nid; @@ -596,11 +628,24 @@ u32 param; }; -void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq); +void snd_hda_sequence_write(struct hda_codec *codec, + const struct hda_verb *seq); /* unsolicited event */ int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex); +/* cached write */ +#ifdef SND_HDA_NEEDS_RESUME +int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, + int direct, unsigned int verb, unsigned int parm); +void snd_hda_sequence_write_cache(struct hda_codec *codec, + const struct hda_verb *seq); +void snd_hda_codec_resume_cache(struct hda_codec *codec); +#else +#define snd_hda_codec_write_cache snd_hda_codec_write +#define snd_hda_sequence_write_cache snd_hda_sequence_write +#endif + /* * Mixer */ @@ -610,10 +655,13 @@ * PCM */ int snd_hda_build_pcms(struct hda_bus *bus); -void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stream_tag, +void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, + u32 stream_tag, int channel_id, int format); -unsigned int snd_hda_calc_stream_format(unsigned int rate, unsigned int channels, - unsigned int format, unsigned int maxbps); +unsigned int snd_hda_calc_stream_format(unsigned int rate, + unsigned int channels, + unsigned int format, + unsigned int maxbps); int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, u32 *ratesp, u64 *formatsp, unsigned int *bpsp); int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, @@ -632,4 +680,15 @@ int snd_hda_resume(struct hda_bus *bus); #endif +/* + * power saving + */ +#ifdef CONFIG_SND_HDA_POWER_SAVE +void snd_hda_power_up(struct hda_codec *codec); +void snd_hda_power_down(struct hda_codec *codec); +#else +static inline void snd_hda_power_up(struct hda_codec *codec) {} +static inline void snd_hda_power_down(struct hda_codec *codec) {} +#endif + #endif /* __SOUND_HDA_CODEC_H */ diff -Nur sound/pci/hda/hda_generic.c sound/pci/hda/hda_generic.c --- sound/pci/hda/hda_generic.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/hda/hda_generic.c 2007-08-11 02:00:08.000000000 +0200 @@ -70,6 +70,13 @@ struct hda_pcm pcm_rec; /* PCM information */ struct list_head nid_list; /* list of widgets */ + +#ifdef CONFIG_SND_HDA_POWER_SAVE +#define MAX_LOOPBACK_AMPS 7 + struct hda_loopback_check loopback; + int num_loopbacks; + struct hda_amp_list loopback_list[MAX_LOOPBACK_AMPS + 1]; +#endif }; /* @@ -218,9 +225,8 @@ ofs = (node->amp_out_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; if (val >= ofs) val -= ofs; - val |= AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT; - val |= AC_AMP_SET_OUTPUT; - return snd_hda_codec_write(codec, node->nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, val); + snd_hda_codec_amp_stereo(codec, node->nid, HDA_OUTPUT, 0, 0xff, val); + return 0; } /* @@ -234,11 +240,8 @@ ofs = (node->amp_in_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; if (val >= ofs) val -= ofs; - val |= AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT; - val |= AC_AMP_SET_INPUT; - // awk added - fixed to allow unmuting of indexed amps - val |= index << AC_AMP_SET_INDEX_SHIFT; - return snd_hda_codec_write(codec, node->nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, val); + snd_hda_codec_amp_stereo(codec, node->nid, HDA_INPUT, index, 0xff, val); + return 0; } /* @@ -248,7 +251,8 @@ unsigned int index) { snd_printdd("CONNECT: NID=0x%x IDX=0x%x\n", node->nid, index); - return snd_hda_codec_write(codec, node->nid, 0, AC_VERB_SET_CONNECT_SEL, index); + return snd_hda_codec_write_cache(codec, node->nid, 0, + AC_VERB_SET_CONNECT_SEL, index); } /* @@ -379,7 +383,7 @@ /* unmute the PIN output */ unmute_output(codec, node); /* set PIN-Out enable */ - snd_hda_codec_write(codec, node->nid, 0, + snd_hda_codec_write_cache(codec, node->nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN | ((node->pin_caps & AC_PINCAP_HP_DRV) ? @@ -570,7 +574,8 @@ /* unmute the PIN external input */ unmute_input(codec, node, 0); /* index = 0? */ /* set PIN-In enable */ - snd_hda_codec_write(codec, node->nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl); + snd_hda_codec_write_cache(codec, node->nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl); return 1; /* found */ } @@ -684,11 +689,33 @@ return 0; } +#ifdef CONFIG_SND_HDA_POWER_SAVE +static void add_input_loopback(struct hda_codec *codec, hda_nid_t nid, + int dir, int idx) +{ + struct hda_gspec *spec = codec->spec; + struct hda_amp_list *p; + + if (spec->num_loopbacks >= MAX_LOOPBACK_AMPS) { + snd_printk(KERN_ERR "hda_generic: Too many loopback ctls\n"); + return; + } + p = &spec->loopback_list[spec->num_loopbacks++]; + p->nid = nid; + p->dir = dir; + p->idx = idx; + spec->loopback.amplist = spec->loopback_list; +} +#else +#define add_input_loopback(codec,nid,dir,idx) +#endif + /* * create mixer controls if possible */ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node, - unsigned int index, const char *type, const char *dir_sfx) + unsigned int index, const char *type, + const char *dir_sfx, int is_loopback) { char name[32]; int err; @@ -702,6 +729,8 @@ if ((node->wid_caps & AC_WCAP_IN_AMP) && (node->amp_in_caps & AC_AMPCAP_MUTE)) { knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, index, HDA_INPUT); + if (is_loopback) + add_input_loopback(codec, node->nid, HDA_INPUT, index); snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index); if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0) return err; @@ -709,6 +738,8 @@ } else if ((node->wid_caps & AC_WCAP_OUT_AMP) && (node->amp_out_caps & AC_AMPCAP_MUTE)) { knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, 0, HDA_OUTPUT); + if (is_loopback) + add_input_loopback(codec, node->nid, HDA_OUTPUT, 0); snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid); if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0) return err; @@ -767,7 +798,7 @@ for (i = 0; i < spec->pcm_vol_nodes; i++) { err = create_mixer(codec, spec->pcm_vol[i].node, spec->pcm_vol[i].index, - names[i], "Playback"); + names[i], "Playback", 0); if (err < 0) return err; } @@ -784,7 +815,7 @@ case 1: return create_mixer(codec, spec->pcm_vol[0].node, spec->pcm_vol[0].index, - "Master", "Playback"); + "Master", "Playback", 0); case 2: if (defcfg_type(spec->out_pin_node[0]) == AC_JACK_SPEAKER) return create_output_mixers(codec, types_speaker); @@ -820,7 +851,7 @@ if (spec->input_mux.num_items == 1) { err = create_mixer(codec, adc_node, spec->input_mux.items[0].index, - NULL, "Capture"); + NULL, "Capture", 0); if (err < 0) return err; return 0; @@ -886,7 +917,8 @@ return err; else if (err >= 1) { if (err == 1) { - err = create_mixer(codec, node, i, type, "Playback"); + err = create_mixer(codec, node, i, type, + "Playback", 1); if (err < 0) return err; if (err > 0) @@ -1022,6 +1054,14 @@ return 0; } +#ifdef CONFIG_SND_HDA_POWER_SAVE +static int generic_check_power_status(struct hda_codec *codec, hda_nid_t nid) +{ + struct hda_gspec *spec = codec->spec; + return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); +} +#endif + /* */ @@ -1029,6 +1069,9 @@ .build_controls = build_generic_controls, .build_pcms = build_generic_pcms, .free = snd_hda_generic_free, +#ifdef CONFIG_SND_HDA_POWER_SAVE + .check_power_status = generic_check_power_status, +#endif }; /* diff -Nur sound/pci/hda/hda_hwdep.c sound/pci/hda/hda_hwdep.c --- sound/pci/hda/hda_hwdep.c 1970-01-01 01:00:00.000000000 +0100 +++ sound/pci/hda/hda_hwdep.c 2007-07-31 11:13:52.000000000 +0200 @@ -0,0 +1,122 @@ +/* + * HWDEP Interface for HD-audio codec + * + * Copyright (c) 2007 Takashi Iwai + * + * This driver 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 driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include "hda_codec.h" +#include "hda_local.h" +#include + +/* + * write/read an out-of-bound verb + */ +static int verb_write_ioctl(struct hda_codec *codec, + struct hda_verb_ioctl __user *arg) +{ + u32 verb, res; + + if (get_user(verb, &arg->verb)) + return -EFAULT; + res = snd_hda_codec_read(codec, verb >> 24, 0, + (verb >> 8) & 0xffff, verb & 0xff); + if (put_user(res, &arg->res)) + return -EFAULT; + return 0; +} + +static int get_wcap_ioctl(struct hda_codec *codec, + struct hda_verb_ioctl __user *arg) +{ + u32 verb, res; + + if (get_user(verb, &arg->verb)) + return -EFAULT; + res = get_wcaps(codec, verb >> 24); + if (put_user(res, &arg->res)) + return -EFAULT; + return 0; +} + + +/* + */ +static int hda_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct hda_codec *codec = hw->private_data; + void __user *argp = (void __user *)arg; + + switch (cmd) { + case HDA_IOCTL_PVERSION: + return put_user(HDA_HWDEP_VERSION, (int __user *)argp); + case HDA_IOCTL_VERB_WRITE: + return verb_write_ioctl(codec, argp); + case HDA_IOCTL_GET_WCAP: + return get_wcap_ioctl(codec, argp); + } + return -ENOIOCTLCMD; +} + +#ifdef CONFIG_COMPAT +static int hda_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return hda_hwdep_ioctl(hw, file, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + +static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file) +{ +#ifndef CONFIG_SND_DEBUG_DETECT + if (!capable(CAP_SYS_RAWIO)) + return -EACCES; +#endif + return 0; +} + +int __devinit snd_hda_create_hwdep(struct hda_codec *codec) +{ + char hwname[16]; + struct snd_hwdep *hwdep; + int err; + + sprintf(hwname, "HDA Codec %d", codec->addr); + err = snd_hwdep_new(codec->bus->card, hwname, codec->addr, &hwdep); + if (err < 0) + return err; + codec->hwdep = hwdep; + sprintf(hwdep->name, "HDA Codec %d", codec->addr); + hwdep->iface = SNDRV_HWDEP_IFACE_HDA; + hwdep->private_data = codec; + hwdep->exclusive = 1; + + hwdep->ops.open = hda_hwdep_open; + hwdep->ops.ioctl = hda_hwdep_ioctl; +#ifdef CONFIG_COMPAT + hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat; +#endif + + return 0; +} diff -Nur sound/pci/hda/hda_intel.c sound/pci/hda/hda_intel.c --- sound/pci/hda/hda_intel.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/hda/hda_intel.c 2007-08-18 02:00:07.000000000 +0200 @@ -1,6 +1,7 @@ /* * - * hda_intel.c - Implementation of primary alsa driver code base for Intel HD Audio. + * hda_intel.c - Implementation of primary alsa driver code base + * for Intel HD Audio. * * Copyright(c) 2004 Intel Corporation. All rights reserved. * @@ -64,14 +65,27 @@ module_param(model, charp, 0444); MODULE_PARM_DESC(model, "Use the given board model."); module_param(position_fix, int, 0444); -MODULE_PARM_DESC(position_fix, "Fix DMA pointer (0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size)."); +MODULE_PARM_DESC(position_fix, "Fix DMA pointer " + "(0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size)."); module_param(probe_mask, int, 0444); MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1)."); module_param(single_cmd, bool, 0444); -MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs (for debugging only)."); +MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs " + "(for debugging only)."); module_param(enable_msi, int, 0); MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)"); +#ifdef CONFIG_SND_HDA_POWER_SAVE +/* power_save option is defined in hda_codec.c */ + +/* reset the HD-audio controller in power save mode. + * this may give more power-saving, but will take longer time to + * wake up. + */ +static int power_save_controller = 1; +module_param(power_save_controller, bool, 0644); +MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode."); +#endif /* just for backward compatibility */ static int enable; @@ -98,6 +112,7 @@ #define SFX "hda-intel: " + /* * registers */ @@ -213,15 +228,16 @@ #define SD_INT_DESC_ERR 0x10 /* descriptor error interrupt */ #define SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */ #define SD_INT_COMPLETE 0x04 /* completion interrupt */ -#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|SD_INT_COMPLETE) +#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|\ + SD_INT_COMPLETE) /* SD_STS */ #define SD_STS_FIFO_READY 0x20 /* FIFO ready */ /* INTCTL and INTSTS */ -#define ICH6_INT_ALL_STREAM 0xff /* all stream interrupts */ -#define ICH6_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */ -#define ICH6_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */ +#define ICH6_INT_ALL_STREAM 0xff /* all stream interrupts */ +#define ICH6_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */ +#define ICH6_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */ /* GCTL unsolicited response enable bit */ #define ICH6_GCTL_UREN (1<<8) @@ -257,22 +273,26 @@ */ struct azx_dev { - u32 *bdl; /* virtual address of the BDL */ - dma_addr_t bdl_addr; /* physical address of the BDL */ - u32 *posbuf; /* position buffer pointer */ - - unsigned int bufsize; /* size of the play buffer in bytes */ - unsigned int fragsize; /* size of each period in bytes */ - unsigned int frags; /* number for period in the play buffer */ - unsigned int fifo_size; /* FIFO size */ + u32 *bdl; /* virtual address of the BDL */ + dma_addr_t bdl_addr; /* physical address of the BDL */ + u32 *posbuf; /* position buffer pointer */ + + unsigned int bufsize; /* size of the play buffer in bytes */ + unsigned int fragsize; /* size of each period in bytes */ + unsigned int frags; /* number for period in the play buffer */ + unsigned int fifo_size; /* FIFO size */ - void __iomem *sd_addr; /* stream descriptor pointer */ + void __iomem *sd_addr; /* stream descriptor pointer */ - u32 sd_int_sta_mask; /* stream int status mask */ + u32 sd_int_sta_mask; /* stream int status mask */ /* pcm support */ - struct snd_pcm_substream *substream; /* assigned substream, set in PCM open */ - unsigned int format_val; /* format value to be set in the controller and the codec */ + struct snd_pcm_substream *substream; /* assigned substream, + * set in PCM open + */ + unsigned int format_val; /* format value to be set in the + * controller and the codec + */ unsigned char stream_tag; /* assigned stream */ unsigned char index; /* stream index */ /* for sanity check of position buffer */ @@ -337,10 +357,14 @@ /* flags */ int position_fix; + unsigned int running :1; unsigned int initialized :1; unsigned int single_cmd :1; unsigned int polling_mode :1; unsigned int msi :1; + + /* for debugging */ + unsigned int last_cmd; /* last issued command (to sync) */ }; /* driver types */ @@ -415,7 +439,8 @@ int err; /* single page (at least 4096 bytes) must suffice for both ringbuffes */ - err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), + err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), PAGE_SIZE, &chip->rb); if (err < 0) { snd_printk(KERN_ERR SFX "cannot allocate CORB/RIRB\n"); @@ -466,18 +491,10 @@ } /* send a command */ -static int azx_corb_send_cmd(struct hda_codec *codec, hda_nid_t nid, int direct, - unsigned int verb, unsigned int para) +static int azx_corb_send_cmd(struct hda_codec *codec, u32 val) { struct azx *chip = codec->bus->private_data; unsigned int wp; - u32 val; - - val = (u32)(codec->addr & 0x0f) << 28; - val |= (u32)direct << 27; - val |= (u32)nid << 20; - val |= verb << 8; - val |= para; /* add command to corb */ wp = azx_readb(chip, CORBWP); @@ -536,14 +553,14 @@ azx_update_rirb(chip); spin_unlock_irq(&chip->reg_lock); } - if (! chip->rirb.cmds) + if (!chip->rirb.cmds) return chip->rirb.res; /* the last value */ - schedule_timeout_interruptible(1); + schedule_timeout(1); } while (time_after_eq(timeout, jiffies)); if (chip->msi) { snd_printk(KERN_WARNING "hda_intel: No response from codec, " - "disabling MSI...\n"); + "disabling MSI: last cmd=0x%08x\n", chip->last_cmd); free_irq(chip->irq, chip); chip->irq = -1; pci_disable_msi(chip->pci); @@ -555,13 +572,15 @@ if (!chip->polling_mode) { snd_printk(KERN_WARNING "hda_intel: azx_get_response timeout, " - "switching to polling mode...\n"); + "switching to polling mode: last cmd=0x%08x\n", + chip->last_cmd); chip->polling_mode = 1; goto again; } snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, " - "switching to single_cmd mode...\n"); + "switching to single_cmd mode: last cmd=0x%08x\n", + chip->last_cmd); chip->rirb.rp = azx_readb(chip, RIRBWP); chip->rirb.cmds = 0; /* switch to single_cmd mode */ @@ -581,32 +600,26 @@ */ /* send a command */ -static int azx_single_send_cmd(struct hda_codec *codec, hda_nid_t nid, - int direct, unsigned int verb, - unsigned int para) +static int azx_single_send_cmd(struct hda_codec *codec, u32 val) { struct azx *chip = codec->bus->private_data; - u32 val; int timeout = 50; - val = (u32)(codec->addr & 0x0f) << 28; - val |= (u32)direct << 27; - val |= (u32)nid << 20; - val |= verb << 8; - val |= para; - while (timeout--) { /* check ICB busy bit */ - if (! (azx_readw(chip, IRS) & ICH6_IRS_BUSY)) { + if (!((azx_readw(chip, IRS) & ICH6_IRS_BUSY))) { /* Clear IRV valid bit */ - azx_writew(chip, IRS, azx_readw(chip, IRS) | ICH6_IRS_VALID); + azx_writew(chip, IRS, azx_readw(chip, IRS) | + ICH6_IRS_VALID); azx_writel(chip, IC, val); - azx_writew(chip, IRS, azx_readw(chip, IRS) | ICH6_IRS_BUSY); + azx_writew(chip, IRS, azx_readw(chip, IRS) | + ICH6_IRS_BUSY); return 0; } udelay(1); } - snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n", azx_readw(chip, IRS), val); + snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n", + azx_readw(chip, IRS), val); return -EIO; } @@ -622,7 +635,8 @@ return azx_readl(chip, IR); udelay(1); } - snd_printd(SFX "get_response timeout: IRS=0x%x\n", azx_readw(chip, IRS)); + snd_printd(SFX "get_response timeout: IRS=0x%x\n", + azx_readw(chip, IRS)); return (unsigned int)-1; } @@ -639,10 +653,19 @@ unsigned int para) { struct azx *chip = codec->bus->private_data; + u32 val; + + val = (u32)(codec->addr & 0x0f) << 28; + val |= (u32)direct << 27; + val |= (u32)nid << 20; + val |= verb << 8; + val |= para; + chip->last_cmd = val; + if (chip->single_cmd) - return azx_single_send_cmd(codec, nid, direct, verb, para); + return azx_single_send_cmd(codec, val); else - return azx_corb_send_cmd(codec, nid, direct, verb, para); + return azx_corb_send_cmd(codec, val); } /* get a response */ @@ -655,6 +678,9 @@ return azx_rirb_get_response(codec); } +#ifdef CONFIG_SND_HDA_POWER_SAVE +static void azx_power_notify(struct hda_codec *codec); +#endif /* reset codec link */ static int azx_reset(struct azx *chip) @@ -780,18 +806,12 @@ /* - * initialize the chip + * reset and start the controller registers */ static void azx_init_chip(struct azx *chip) { - unsigned char reg; - - /* 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 - */ - pci_read_config_byte (chip->pci, ICH6_PCIREG_TCSEL, ®); - pci_write_config_byte(chip->pci, ICH6_PCIREG_TCSEL, reg & 0xf8); + if (chip->initialized) + return; /* reset controller */ azx_reset(chip); @@ -808,19 +828,45 @@ azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr); azx_writel(chip, DPUBASE, upper_32bit(chip->posbuf.addr)); + chip->initialized = 1; +} + +/* + * initialize the PCI registers + */ +/* update bits in a PCI register byte */ +static void update_pci_byte(struct pci_dev *pci, unsigned int reg, + unsigned char mask, unsigned char val) +{ + unsigned char data; + + pci_read_config_byte(pci, reg, &data); + data &= ~mask; + data |= (val & mask); + pci_write_config_byte(pci, reg, data); +} + +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 + */ + update_pci_byte(chip->pci, ICH6_PCIREG_TCSEL, 0x07, 0); + switch (chip->driver_type) { case AZX_DRIVER_ATI: /* For ATI SB450 azalia HD audio, we need to enable snoop */ - pci_read_config_byte(chip->pci, ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR, - ®); - pci_write_config_byte(chip->pci, ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR, - (reg & 0xf8) | ATI_SB450_HDAUDIO_ENABLE_SNOOP); + update_pci_byte(chip->pci, + ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR, + 0x07, ATI_SB450_HDAUDIO_ENABLE_SNOOP); break; case AZX_DRIVER_NVIDIA: /* For NVIDIA HDA, enable snoop */ - pci_read_config_byte(chip->pci,NVIDIA_HDA_TRANSREG_ADDR, ®); - pci_write_config_byte(chip->pci,NVIDIA_HDA_TRANSREG_ADDR, - (reg & 0xf0) | NVIDIA_HDA_ENABLE_COHBITS); + update_pci_byte(chip->pci, + NVIDIA_HDA_TRANSREG_ADDR, + 0x0f, NVIDIA_HDA_ENABLE_COHBITS); break; } } @@ -860,7 +906,7 @@ /* clear rirb int */ status = azx_readb(chip, RIRBSTS); if (status & RIRB_INT_MASK) { - if (! chip->single_cmd && (status & RIRB_INT_RESPONSE)) + if (!chip->single_cmd && (status & RIRB_INT_RESPONSE)) azx_update_rirb(chip); azx_writeb(chip, RIRBSTS, RIRB_INT_MASK); } @@ -914,9 +960,11 @@ int timeout; /* make sure the run bit is zero for SD */ - azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) & ~SD_CTL_DMA_START); + azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) & + ~SD_CTL_DMA_START); /* reset stream */ - azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) | SD_CTL_STREAM_RESET); + azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) | + SD_CTL_STREAM_RESET); udelay(3); timeout = 300; while (!((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) && @@ -934,7 +982,7 @@ /* program the stream_tag */ azx_sd_writel(azx_dev, SD_CTL, - (azx_sd_readl(azx_dev, SD_CTL) & ~SD_CTL_STREAM_TAG_MASK) | + (azx_sd_readl(azx_dev, SD_CTL) & ~SD_CTL_STREAM_TAG_MASK)| (azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT)); /* program the length of samples in cyclic buffer */ @@ -954,11 +1002,13 @@ azx_sd_writel(azx_dev, SD_BDLPU, upper_32bit(azx_dev->bdl_addr)); /* enable the position buffer */ - if (! (azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE)) - azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE); + if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE)) + azx_writel(chip, DPLBASE, + (u32)chip->posbuf.addr |ICH6_DPLBASE_ENABLE); /* set the interrupt enable bits in the descriptor control register */ - azx_sd_writel(azx_dev, SD_CTL, azx_sd_readl(azx_dev, SD_CTL) | SD_INT_MASK); + azx_sd_writel(azx_dev, SD_CTL, + azx_sd_readl(azx_dev, SD_CTL) | SD_INT_MASK); return 0; } @@ -989,8 +1039,12 @@ bus_temp.pci = chip->pci; bus_temp.ops.command = azx_send_cmd; bus_temp.ops.get_response = azx_get_response; +#ifdef CONFIG_SND_HDA_POWER_SAVE + bus_temp.ops.pm_notify = azx_power_notify; +#endif - if ((err = snd_hda_bus_new(chip->card, &bus_temp, &chip->bus)) < 0) + err = snd_hda_bus_new(chip->card, &bus_temp, &chip->bus); + if (err < 0) return err; codecs = audio_codecs = 0; @@ -1041,7 +1095,7 @@ nums = chip->capture_streams; } for (i = 0; i < nums; i++, dev++) - if (! chip->azx_dev[dev].opened) { + if (!chip->azx_dev[dev].opened) { chip->azx_dev[dev].opened = 1; return &chip->azx_dev[dev]; } @@ -1055,7 +1109,8 @@ } static struct snd_pcm_hardware azx_pcm_hw = { - .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | /* No full-resume yet implemented */ @@ -1108,8 +1163,11 @@ 128); snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128); - if ((err = hinfo->ops.open(hinfo, apcm->codec, substream)) < 0) { + snd_hda_power_up(apcm->codec); + err = hinfo->ops.open(hinfo, apcm->codec, substream); + if (err < 0) { azx_release_device(azx_dev); + snd_hda_power_down(apcm->codec); mutex_unlock(&chip->open_mutex); return err; } @@ -1138,13 +1196,16 @@ spin_unlock_irqrestore(&chip->reg_lock, flags); azx_release_device(azx_dev); hinfo->ops.close(hinfo, apcm->codec, substream); + snd_hda_power_down(apcm->codec); mutex_unlock(&chip->open_mutex); return 0; } -static int azx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) +static int azx_pcm_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)); + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); } static int azx_pcm_hw_free(struct snd_pcm_substream *substream) @@ -1178,13 +1239,15 @@ runtime->channels, runtime->format, hinfo->maxbps); - if (! azx_dev->format_val) { - snd_printk(KERN_ERR SFX "invalid format_val, rate=%d, ch=%d, format=%d\n", + if (!azx_dev->format_val) { + snd_printk(KERN_ERR SFX + "invalid format_val, rate=%d, ch=%d, format=%d\n", runtime->rate, runtime->channels, runtime->format); return -EINVAL; } - snd_printdd("azx_pcm_prepare: bufsize=0x%x, fragsize=0x%x, format=0x%x\n", + snd_printdd("azx_pcm_prepare: bufsize=0x%x, fragsize=0x%x, " + "format=0x%x\n", azx_dev->bufsize, azx_dev->fragsize, azx_dev->format_val); azx_setup_periods(azx_dev); azx_setup_controller(chip, azx_dev); @@ -1226,7 +1289,8 @@ cmd == SNDRV_PCM_TRIGGER_SUSPEND || cmd == SNDRV_PCM_TRIGGER_STOP) { int timeout = 5000; - while (azx_sd_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START && --timeout) + while ((azx_sd_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START) && + --timeout) ; } return err; @@ -1244,7 +1308,7 @@ /* use the position buffer */ pos = le32_to_cpu(*azx_dev->posbuf); if (chip->position_fix == POS_FIX_AUTO && - azx_dev->period_intr == 1 && ! pos) { + azx_dev->period_intr == 1 && !pos) { printk(KERN_WARNING "hda-intel: Invalid position buffer, " "using LPIB read method instead.\n"); @@ -1295,7 +1359,8 @@ snd_assert(cpcm->name, return -EINVAL); err = snd_pcm_new(chip->card, cpcm->name, pcm_dev, - cpcm->stream[0].substreams, cpcm->stream[1].substreams, + cpcm->stream[0].substreams, + cpcm->stream[1].substreams, &pcm); if (err < 0) return err; @@ -1330,7 +1395,8 @@ int c, err; int pcm_dev; - if ((err = snd_hda_build_pcms(chip->bus)) < 0) + err = snd_hda_build_pcms(chip->bus); + if (err < 0) return err; /* create audio PCMs */ @@ -1341,10 +1407,12 @@ if (codec->pcm_info[c].is_modem) continue; /* create later */ if (pcm_dev >= AZX_MAX_AUDIO_PCMS) { - snd_printk(KERN_ERR SFX "Too many audio PCMs\n"); + snd_printk(KERN_ERR SFX + "Too many audio PCMs\n"); return -EINVAL; } - err = create_codec_pcm(chip, codec, &codec->pcm_info[c], pcm_dev); + err = create_codec_pcm(chip, codec, + &codec->pcm_info[c], pcm_dev); if (err < 0) return err; pcm_dev++; @@ -1356,13 +1424,15 @@ list_for_each(p, &chip->bus->codec_list) { codec = list_entry(p, struct hda_codec, list); for (c = 0; c < codec->num_pcms; c++) { - if (! codec->pcm_info[c].is_modem) + if (!codec->pcm_info[c].is_modem) continue; /* already created */ if (pcm_dev >= AZX_MAX_PCMS) { - snd_printk(KERN_ERR SFX "Too many modem PCMs\n"); + snd_printk(KERN_ERR SFX + "Too many modem PCMs\n"); return -EINVAL; } - err = create_codec_pcm(chip, codec, &codec->pcm_info[c], pcm_dev); + err = create_codec_pcm(chip, codec, + &codec->pcm_info[c], pcm_dev); if (err < 0) return err; chip->pcm[pcm_dev]->dev_class = SNDRV_PCM_CLASS_MODEM; @@ -1389,7 +1459,8 @@ int i; /* initialize each stream (aka device) - * assign the starting bdl address to each stream (device) and initialize + * assign the starting bdl address to each stream (device) + * and initialize */ for (i = 0; i < chip->num_streams; i++) { unsigned int off = sizeof(u32) * (i * AZX_MAX_FRAG * 4); @@ -1426,6 +1497,46 @@ } +static void azx_stop_chip(struct azx *chip) +{ + if (!chip->initialized) + return; + + /* disable interrupts */ + azx_int_disable(chip); + azx_int_clear(chip); + + /* disable CORB/RIRB */ + azx_free_cmd_io(chip); + + /* disable position buffer */ + azx_writel(chip, DPLBASE, 0); + azx_writel(chip, DPUBASE, 0); + + chip->initialized = 0; +} + +#ifdef CONFIG_SND_HDA_POWER_SAVE +/* power-up/down the controller */ +static void azx_power_notify(struct hda_codec *codec) +{ + struct azx *chip = codec->bus->private_data; + struct hda_codec *c; + int power_on = 0; + + list_for_each_entry(c, &codec->bus->codec_list, list) { + if (c->power_on) { + power_on = 1; + break; + } + } + if (power_on) + azx_init_chip(chip); + else if (chip->running && power_save_controller) + azx_stop_chip(chip); +} +#endif /* CONFIG_SND_HDA_POWER_SAVE */ + #ifdef CONFIG_PM /* * power management @@ -1439,8 +1550,9 @@ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); for (i = 0; i < chip->pcm_devs; i++) snd_pcm_suspend_all(chip->pcm[i]); - snd_hda_suspend(chip->bus, state); - azx_free_cmd_io(chip); + if (chip->initialized) + snd_hda_suspend(chip->bus, state); + azx_stop_chip(chip); if (chip->irq >= 0) { synchronize_irq(chip->irq); free_irq(chip->irq, chip); @@ -1473,8 +1585,12 @@ chip->msi = 0; if (azx_acquire_irq(chip, 1) < 0) return -EIO; + azx_init_pci(chip); +#ifndef CONFIG_SND_HDA_POWER_SAVE + /* the explicit resume is needed only when POWER_SAVE isn't set */ azx_init_chip(chip); snd_hda_resume(chip->bus); +#endif snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } @@ -1488,20 +1604,9 @@ { if (chip->initialized) { int i; - for (i = 0; i < chip->num_streams; i++) azx_stream_stop(chip, &chip->azx_dev[i]); - - /* disable interrupts */ - azx_int_disable(chip); - azx_int_clear(chip); - - /* disable CORB/RIRB */ - azx_free_cmd_io(chip); - - /* disable position buffer */ - azx_writel(chip, DPLBASE, 0); - azx_writel(chip, DPUBASE, 0); + azx_stop_chip(chip); } if (chip->irq >= 0) { @@ -1537,6 +1642,7 @@ */ static struct snd_pci_quirk position_fix_list[] __devinitdata = { SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_NONE), + SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_NONE), {} }; @@ -1547,7 +1653,7 @@ if (fix == POS_FIX_AUTO) { q = snd_pci_quirk_lookup(chip->pci, position_fix_list); if (q) { - snd_printdd(KERN_INFO + printk(KERN_INFO "hda_intel: position_fix set to %d " "for device %04x:%04x\n", q->value, q->subvendor, q->subdevice); @@ -1558,6 +1664,36 @@ } /* + * black-lists for probe_mask + */ +static struct snd_pci_quirk probe_mask_list[] __devinitdata = { + /* Thinkpad often breaks the controller communication when accessing + * to the non-working (or non-existing) modem codec slot. + */ + SND_PCI_QUIRK(0x1014, 0x05b7, "Thinkpad Z60", 0x01), + SND_PCI_QUIRK(0x17aa, 0x2010, "Thinkpad X/T/R60", 0x01), + SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X/T/R61", 0x01), + {} +}; + +static void __devinit check_probe_mask(struct azx *chip) +{ + const struct snd_pci_quirk *q; + + if (probe_mask == -1) { + q = snd_pci_quirk_lookup(chip->pci, probe_mask_list); + if (q) { + printk(KERN_INFO + "hda_intel: probe_mask set to 0x%x " + "for device %04x:%04x\n", + q->value, q->subvendor, q->subdevice); + probe_mask = q->value; + } + } +} + + +/* * constructor */ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci, @@ -1592,6 +1728,7 @@ chip->msi = enable_msi; chip->position_fix = check_position_fix(chip, position_fix); + check_probe_mask(chip); chip->single_cmd = single_cmd; @@ -1653,37 +1790,43 @@ break; } chip->num_streams = chip->playback_streams + chip->capture_streams; - chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev), GFP_KERNEL); + chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev), + GFP_KERNEL); if (!chip->azx_dev) { snd_printk(KERN_ERR "cannot malloc azx_dev\n"); goto errout; } /* allocate memory for the BDL for each stream */ - if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), - BDL_SIZE, &chip->bdl)) < 0) { + err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + BDL_SIZE, &chip->bdl); + if (err < 0) { snd_printk(KERN_ERR SFX "cannot allocate BDL\n"); goto errout; } /* allocate memory for the position buffer */ - if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), - chip->num_streams * 8, &chip->posbuf)) < 0) { + err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + chip->num_streams * 8, &chip->posbuf); + if (err < 0) { snd_printk(KERN_ERR SFX "cannot allocate posbuf\n"); goto errout; } /* allocate CORB/RIRB */ - if (! chip->single_cmd) - if ((err = azx_alloc_cmd_io(chip)) < 0) + if (!chip->single_cmd) { + err = azx_alloc_cmd_io(chip); + if (err < 0) goto errout; + } /* initialize streams */ azx_init_stream(chip); /* initialize chip */ + azx_init_pci(chip); azx_init_chip(chip); - chip->initialized = 1; - /* codec detection */ if (!chip->codec_mask) { snd_printk(KERN_ERR SFX "no codecs found!\n"); @@ -1691,14 +1834,16 @@ goto errout; } - if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) <0) { + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); + if (err <0) { snd_printk(KERN_ERR SFX "Error creating device [card]!\n"); goto errout; } strcpy(card->driver, "HDA-Intel"); strcpy(card->shortname, driver_short_names[chip->driver_type]); - sprintf(card->longname, "%s at 0x%lx irq %i", card->shortname, chip->addr, chip->irq); + sprintf(card->longname, "%s at 0x%lx irq %i", + card->shortname, chip->addr, chip->irq); *rchip = chip; return 0; @@ -1708,7 +1853,21 @@ return err; } -static int __devinit azx_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) +static void power_down_all_codecs(struct azx *chip) +{ +#ifdef CONFIG_SND_HDA_POWER_SAVE + /* The codecs were powered up in snd_hda_codec_new(). + * Now all initialization done, so turn them down if possible + */ + struct hda_codec *codec; + list_for_each_entry(codec, &chip->bus->codec_list, list) { + snd_hda_power_down(codec); + } +#endif +} + +static int __devinit azx_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) { struct snd_card *card; struct azx *chip; @@ -1728,31 +1887,37 @@ card->private_data = chip; /* create codec instances */ - if ((err = azx_codec_create(chip, model)) < 0) { + err = azx_codec_create(chip, model); + if (err < 0) { snd_card_free(card); return err; } /* create PCM streams */ - if ((err = azx_pcm_create(chip)) < 0) { + err = azx_pcm_create(chip); + if (err < 0) { snd_card_free(card); return err; } /* create mixer controls */ - if ((err = azx_mixer_create(chip)) < 0) { + err = azx_mixer_create(chip); + if (err < 0) { snd_card_free(card); return err; } snd_card_set_dev(card, &pci->dev); - if ((err = snd_card_register(card)) < 0) { + err = snd_card_register(card); + if (err < 0) { snd_card_free(card); return err; } pci_set_drvdata(pci, card); + chip->running = 1; + power_down_all_codecs(chip); return err; } @@ -1788,6 +1953,12 @@ { 0x10de, 0x044b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP65 */ { 0x10de, 0x055c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP67 */ { 0x10de, 0x055d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP67 */ + { 0x10de, 0x07fc, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP73 */ + { 0x10de, 0x07fd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP73 */ + { 0x10de, 0x0774, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */ + { 0x10de, 0x0775, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */ + { 0x10de, 0x0776, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */ + { 0x10de, 0x0777, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */ { 0, } }; MODULE_DEVICE_TABLE(pci, azx_ids); diff -Nur sound/pci/hda/hda_local.h sound/pci/hda/hda_local.h --- sound/pci/hda/hda_local.h 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/hda/hda_local.h 2007-08-11 02:00:08.000000000 +0200 @@ -26,7 +26,8 @@ /* * for mixer controls */ -#define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) ((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19)) +#define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) \ + ((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19)) /* mono volume with index (index=0,1,...) (channel=1,2) */ #define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \ @@ -64,18 +65,35 @@ #define HDA_CODEC_MUTE(xname, nid, xindex, direction) \ HDA_CODEC_MUTE_MONO(xname, nid, 3, xindex, direction) -int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); -int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); -int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); -int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *tlv); -int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); -int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); -int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *tlv); +int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); /* lowlevel accessor with caching; use carefully */ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index); int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int mask, int val); +int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, + int dir, int idx, int mask, int val); +#ifdef SND_HDA_NEEDS_RESUME +void snd_hda_codec_resume_amp(struct hda_codec *codec); +#endif + +/* amp value bits */ +#define HDA_AMP_MUTE 0x80 +#define HDA_AMP_UNMUTE 0x00 +#define HDA_AMP_VOLMASK 0x7f /* mono switch binding multiple inputs */ #define HDA_BIND_MUTE_MONO(xname, nid, channel, indices, direction) \ @@ -86,11 +104,61 @@ .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, indices, direction) } /* stereo switch binding multiple inputs */ -#define HDA_BIND_MUTE(xname,nid,indices,dir) HDA_BIND_MUTE_MONO(xname,nid,3,indices,dir) +#define HDA_BIND_MUTE(xname,nid,indices,dir) \ + HDA_BIND_MUTE_MONO(xname,nid,3,indices,dir) + +int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +/* more generic bound controls */ +struct hda_ctl_ops { + snd_kcontrol_info_t *info; + snd_kcontrol_get_t *get; + snd_kcontrol_put_t *put; + snd_kcontrol_tlv_rw_t *tlv; +}; + +extern struct hda_ctl_ops snd_hda_bind_vol; /* for bind-volume with TLV */ +extern struct hda_ctl_ops snd_hda_bind_sw; /* for bind-switch */ + +struct hda_bind_ctls { + struct hda_ctl_ops *ops; + long values[]; +}; -int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); -int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *tlv); + +#define HDA_BIND_VOL(xname, bindrec) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,\ + .info = snd_hda_mixer_bind_ctls_info,\ + .get = snd_hda_mixer_bind_ctls_get,\ + .put = snd_hda_mixer_bind_ctls_put,\ + .tlv = { .c = snd_hda_mixer_bind_tlv },\ + .private_value = (long) (bindrec) } +#define HDA_BIND_SW(xname, bindrec) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER,\ + .name = xname, \ + .info = snd_hda_mixer_bind_ctls_info,\ + .get = snd_hda_mixer_bind_ctls_get,\ + .put = snd_hda_mixer_bind_ctls_put,\ + .private_value = (long) (bindrec) } +/* + * SPDIF I/O + */ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid); int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid); @@ -107,8 +175,10 @@ struct hda_input_mux_item items[HDA_MAX_NUM_INPUTS]; }; -int snd_hda_input_mux_info(const struct hda_input_mux *imux, struct snd_ctl_elem_info *uinfo); -int snd_hda_input_mux_put(struct hda_codec *codec, const struct hda_input_mux *imux, +int snd_hda_input_mux_info(const struct hda_input_mux *imux, + struct snd_ctl_elem_info *uinfo); +int snd_hda_input_mux_put(struct hda_codec *codec, + const struct hda_input_mux *imux, struct snd_ctl_elem_value *ucontrol, hda_nid_t nid, unsigned int *cur_val); @@ -120,13 +190,19 @@ const struct hda_verb *sequence; }; -int snd_hda_ch_mode_info(struct hda_codec *codec, struct snd_ctl_elem_info *uinfo, - const struct hda_channel_mode *chmode, int num_chmodes); -int snd_hda_ch_mode_get(struct hda_codec *codec, struct snd_ctl_elem_value *ucontrol, - const struct hda_channel_mode *chmode, int num_chmodes, +int snd_hda_ch_mode_info(struct hda_codec *codec, + struct snd_ctl_elem_info *uinfo, + const struct hda_channel_mode *chmode, + int num_chmodes); +int snd_hda_ch_mode_get(struct hda_codec *codec, + struct snd_ctl_elem_value *ucontrol, + const struct hda_channel_mode *chmode, + int num_chmodes, int max_channels); -int snd_hda_ch_mode_put(struct hda_codec *codec, struct snd_ctl_elem_value *ucontrol, - const struct hda_channel_mode *chmode, int num_chmodes, +int snd_hda_ch_mode_put(struct hda_codec *codec, + struct snd_ctl_elem_value *ucontrol, + const struct hda_channel_mode *chmode, + int num_chmodes, int *max_channelsp); /* @@ -146,20 +222,25 @@ int dig_out_used; /* current usage of digital out (HDA_DIG_XXX) */ }; -int snd_hda_multi_out_dig_open(struct hda_codec *codec, struct hda_multi_out *mout); -int snd_hda_multi_out_dig_close(struct hda_codec *codec, struct hda_multi_out *mout); +int snd_hda_multi_out_dig_open(struct hda_codec *codec, + struct hda_multi_out *mout); +int snd_hda_multi_out_dig_close(struct hda_codec *codec, + struct hda_multi_out *mout); int snd_hda_multi_out_dig_prepare(struct hda_codec *codec, struct hda_multi_out *mout, unsigned int stream_tag, unsigned int format, struct snd_pcm_substream *substream); -int snd_hda_multi_out_analog_open(struct hda_codec *codec, struct hda_multi_out *mout, +int snd_hda_multi_out_analog_open(struct hda_codec *codec, + struct hda_multi_out *mout, struct snd_pcm_substream *substream); -int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_out *mout, +int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, + struct hda_multi_out *mout, unsigned int stream_tag, unsigned int format, struct snd_pcm_substream *substream); -int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, struct hda_multi_out *mout); +int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, + struct hda_multi_out *mout); /* * generic codec parser @@ -181,16 +262,8 @@ int snd_hda_check_board_config(struct hda_codec *codec, int num_configs, const char **modelnames, const struct snd_pci_quirk *pci_list); -int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew); - -/* - * power management - */ -#ifdef CONFIG_PM -int snd_hda_resume_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew); -int snd_hda_resume_spdif_out(struct hda_codec *codec); -int snd_hda_resume_spdif_in(struct hda_codec *codec); -#endif +int snd_hda_add_new_ctls(struct hda_codec *codec, + struct snd_kcontrol_new *knew); /* * unsolicited event handler @@ -232,7 +305,9 @@ struct auto_pin_cfg { int line_outs; - hda_nid_t line_out_pins[5]; /* sorted in the order of Front/Surr/CLFE/Side */ + hda_nid_t line_out_pins[5]; /* sorted in the order of + * Front/Surr/CLFE/Side + */ int speaker_outs; hda_nid_t speaker_pins[5]; int hp_outs; @@ -243,13 +318,19 @@ hda_nid_t dig_in_pin; }; -#define get_defcfg_connect(cfg) ((cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT) -#define get_defcfg_association(cfg) ((cfg & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT) -#define get_defcfg_location(cfg) ((cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT) -#define get_defcfg_sequence(cfg) (cfg & AC_DEFCFG_SEQUENCE) -#define get_defcfg_device(cfg) ((cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT) +#define get_defcfg_connect(cfg) \ + ((cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT) +#define get_defcfg_association(cfg) \ + ((cfg & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT) +#define get_defcfg_location(cfg) \ + ((cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT) +#define get_defcfg_sequence(cfg) \ + (cfg & AC_DEFCFG_SEQUENCE) +#define get_defcfg_device(cfg) \ + ((cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT) -int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *cfg, +int snd_hda_parse_pin_def_config(struct hda_codec *codec, + struct auto_pin_cfg *cfg, hda_nid_t *ignore_nids); /* amp values */ @@ -280,4 +361,32 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int caps); +/* + * hwdep interface + */ +int snd_hda_create_hwdep(struct hda_codec *codec); + +/* + * power-management + */ + +#ifdef CONFIG_SND_HDA_POWER_SAVE +void snd_hda_schedule_power_save(struct hda_codec *codec); + +struct hda_amp_list { + hda_nid_t nid; + unsigned char dir; + unsigned char idx; +}; + +struct hda_loopback_check { + struct hda_amp_list *amplist; + int power_on; +}; + +int snd_hda_check_amp_list_power(struct hda_codec *codec, + struct hda_loopback_check *check, + hda_nid_t nid); +#endif /* CONFIG_SND_HDA_POWER_SAVE */ + #endif /* __SOUND_HDA_LOCAL_H */ diff -Nur sound/pci/hda/hda_patch.h sound/pci/hda/hda_patch.h --- sound/pci/hda/hda_patch.h 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/hda/hda_patch.h 2007-07-28 02:00:08.000000000 +0200 @@ -20,13 +20,29 @@ extern struct hda_codec_preset snd_hda_preset_via[]; static const struct hda_codec_preset *hda_preset_tables[] = { +#ifdef CONFIG_SND_HDA_CODEC_REALTEK snd_hda_preset_realtek, +#endif +#ifdef CONFIG_SND_HDA_CODEC_CMEDIA snd_hda_preset_cmedia, +#endif +#ifdef CONFIG_SND_HDA_CODEC_ANALOG snd_hda_preset_analog, +#endif +#ifdef CONFIG_SND_HDA_CODEC_SIGMATEL snd_hda_preset_sigmatel, +#endif +#ifdef CONFIG_SND_HDA_CODEC_SI3054 snd_hda_preset_si3054, +#endif +#ifdef CONFIG_SND_HDA_CODEC_ATIHDMI snd_hda_preset_atihdmi, +#endif +#ifdef CONFIG_SND_HDA_CODEC_CONEXANT snd_hda_preset_conexant, +#endif +#ifdef CONFIG_SND_HDA_CODEC_VIA snd_hda_preset_via, +#endif NULL }; diff -Nur sound/pci/hda/hda_proc.c sound/pci/hda/hda_proc.c --- sound/pci/hda/hda_proc.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/hda/hda_proc.c 2007-08-11 02:00:08.000000000 +0200 @@ -58,7 +58,8 @@ snd_iprintf(buffer, "N/A\n"); return; } - snd_iprintf(buffer, "ofs=0x%02x, nsteps=0x%02x, stepsize=0x%02x, mute=%x\n", + snd_iprintf(buffer, "ofs=0x%02x, nsteps=0x%02x, stepsize=0x%02x, " + "mute=%x\n", caps & AC_AMPCAP_OFFSET, (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT, (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT, @@ -76,11 +77,13 @@ for (i = 0; i < indices; i++) { snd_iprintf(buffer, " ["); if (stereo) { - val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, + val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_AMP_GAIN_MUTE, AC_AMP_GET_LEFT | dir | i); snd_iprintf(buffer, "0x%02x ", val); } - val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, + val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_AMP_GAIN_MUTE, AC_AMP_GET_RIGHT | dir | i); snd_iprintf(buffer, "0x%02x]", val); } @@ -237,7 +240,8 @@ } -static void print_codec_info(struct snd_info_entry *entry, struct snd_info_buffer *buffer) +static void print_codec_info(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) { struct hda_codec *codec = entry->private_data; char buf[32]; @@ -250,8 +254,15 @@ snd_iprintf(buffer, "Vendor Id: 0x%x\n", codec->vendor_id); snd_iprintf(buffer, "Subsystem Id: 0x%x\n", codec->subsystem_id); snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id); + + if (codec->mfg) + snd_iprintf(buffer, "Modem Function Group: 0x%x\n", codec->mfg); + else + snd_iprintf(buffer, "No Modem Function Group found\n"); + if (! codec->afg) return; + snd_hda_power_up(codec); snd_iprintf(buffer, "Default PCM:\n"); print_pcm_caps(buffer, codec, codec->afg); snd_iprintf(buffer, "Default Amp-In caps: "); @@ -262,12 +273,15 @@ nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); if (! nid || nodes < 0) { snd_iprintf(buffer, "Invalid AFG subtree\n"); + snd_hda_power_down(codec); return; } for (i = 0; i < nodes; i++, nid++) { - unsigned int wid_caps = snd_hda_param_read(codec, nid, - AC_PAR_AUDIO_WIDGET_CAP); - unsigned int wid_type = (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; + unsigned int wid_caps = + snd_hda_param_read(codec, nid, + AC_PAR_AUDIO_WIDGET_CAP); + unsigned int wid_type = + (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; int conn_len = 0; hda_nid_t conn[HDA_MAX_CONNECTIONS]; @@ -307,7 +321,9 @@ if (wid_type == AC_WID_PIN) { unsigned int pinctls; print_pin_caps(buffer, codec, nid); - pinctls = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + pinctls = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, + 0); snd_iprintf(buffer, " Pin-ctls: 0x%02x:", pinctls); if (pinctls & AC_PINCTL_IN_EN) snd_iprintf(buffer, " IN"); @@ -327,7 +343,8 @@ if (wid_caps & AC_WCAP_POWER) snd_iprintf(buffer, " Power: 0x%x\n", snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_POWER_STATE, 0)); + AC_VERB_GET_POWER_STATE, + 0)); if (wid_caps & AC_WCAP_CONN_LIST) { int c, curr = -1; @@ -344,6 +361,7 @@ snd_iprintf(buffer, "\n"); } } + snd_hda_power_down(codec); } /* diff -Nur sound/pci/hda/patch_analog.c sound/pci/hda/patch_analog.c --- sound/pci/hda/patch_analog.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/hda/patch_analog.c 2007-08-16 02:00:06.000000000 +0200 @@ -1,7 +1,8 @@ /* - * HD audio interface patch for AD1981HD, AD1983, AD1986A, AD1988 + * HD audio interface patch for AD1882, AD1884, AD1981HD, AD1983, AD1984, + * AD1986A, AD1988 * - * Copyright (c) 2005 Takashi Iwai + * Copyright (c) 2005-2007 Takashi Iwai * * This driver is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -61,7 +62,7 @@ int num_channel_mode; /* PCM information */ - struct hda_pcm pcm_rec[2]; /* used in alc_build_pcms() */ + struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */ struct mutex amp_mutex; /* PCM volume/mute control mutex */ unsigned int spdif_route; @@ -72,6 +73,10 @@ struct snd_kcontrol_new *kctl_alloc; struct hda_input_mux private_imux; hda_nid_t private_dac_nids[4]; + +#ifdef CONFIG_SND_HDA_POWER_SAVE + struct hda_loopback_check loopback; +#endif }; /* @@ -143,6 +148,14 @@ return 0; } +#ifdef CONFIG_SND_HDA_POWER_SAVE +static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid) +{ + struct ad198x_spec *spec = codec->spec; + return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); +} +#endif + /* * Analog playback callbacks */ @@ -317,30 +330,13 @@ kfree(codec->spec); } -#ifdef CONFIG_PM -static int ad198x_resume(struct hda_codec *codec) -{ - struct ad198x_spec *spec = codec->spec; - int i; - - codec->patch_ops.init(codec); - for (i = 0; i < spec->num_mixers; i++) - snd_hda_resume_ctls(codec, spec->mixers[i]); - if (spec->multiout.dig_out_nid) - snd_hda_resume_spdif_out(codec); - if (spec->dig_in_nid) - snd_hda_resume_spdif_in(codec); - return 0; -} -#endif - static struct hda_codec_ops ad198x_patch_ops = { .build_controls = ad198x_build_controls, .build_pcms = ad198x_build_pcms, .init = ad198x_init, .free = ad198x_free, -#ifdef CONFIG_PM - .resume = ad198x_resume, +#ifdef CONFIG_SND_HDA_POWER_SAVE + .check_power_status = ad198x_check_power_status, #endif }; @@ -349,15 +345,7 @@ * EAPD control * the private value = nid | (invert << 8) */ -static int ad198x_eapd_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define ad198x_eapd_info snd_ctl_boolean_mono_info static int ad198x_eapd_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -383,12 +371,12 @@ eapd = ucontrol->value.integer.value[0]; if (invert) eapd = !eapd; - if (eapd == spec->cur_eapd && ! codec->in_resume) + if (eapd == spec->cur_eapd) return 0; spec->cur_eapd = eapd; - snd_hda_codec_write(codec, nid, - 0, AC_VERB_SET_EAPD_BTLENABLE, - eapd ? 0x02 : 0x00); + snd_hda_codec_write_cache(codec, nid, + 0, AC_VERB_SET_EAPD_BTLENABLE, + eapd ? 0x02 : 0x00); return 1; } @@ -429,94 +417,36 @@ }, }; -/* - * PCM control - * - * bind volumes/mutes of 3 DACs as a single PCM control for simplicity - */ - -#define ad1986a_pcm_amp_vol_info snd_hda_mixer_amp_volume_info - -static int ad1986a_pcm_amp_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *ad = codec->spec; - - mutex_lock(&ad->amp_mutex); - snd_hda_mixer_amp_volume_get(kcontrol, ucontrol); - mutex_unlock(&ad->amp_mutex); - return 0; -} - -static int ad1986a_pcm_amp_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *ad = codec->spec; - int i, change = 0; - - mutex_lock(&ad->amp_mutex); - for (i = 0; i < ARRAY_SIZE(ad1986a_dac_nids); i++) { - kcontrol->private_value = HDA_COMPOSE_AMP_VAL(ad1986a_dac_nids[i], 3, 0, HDA_OUTPUT); - change |= snd_hda_mixer_amp_volume_put(kcontrol, ucontrol); - } - kcontrol->private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT); - mutex_unlock(&ad->amp_mutex); - return change; -} - -#define ad1986a_pcm_amp_sw_info snd_hda_mixer_amp_switch_info - -static int ad1986a_pcm_amp_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *ad = codec->spec; - - mutex_lock(&ad->amp_mutex); - snd_hda_mixer_amp_switch_get(kcontrol, ucontrol); - mutex_unlock(&ad->amp_mutex); - return 0; -} -static int ad1986a_pcm_amp_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct ad198x_spec *ad = codec->spec; - int i, change = 0; +static struct hda_bind_ctls ad1986a_bind_pcm_vol = { + .ops = &snd_hda_bind_vol, + .values = { + HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT), + 0 + }, +}; - mutex_lock(&ad->amp_mutex); - for (i = 0; i < ARRAY_SIZE(ad1986a_dac_nids); i++) { - kcontrol->private_value = HDA_COMPOSE_AMP_VAL(ad1986a_dac_nids[i], 3, 0, HDA_OUTPUT); - change |= snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); - } - kcontrol->private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT); - mutex_unlock(&ad->amp_mutex); - return change; -} +static struct hda_bind_ctls ad1986a_bind_pcm_sw = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT), + 0 + }, +}; /* * mixers */ static struct snd_kcontrol_new ad1986a_mixers[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PCM Playback Volume", - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | - SNDRV_CTL_ELEM_ACCESS_TLV_READ | - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, - .info = ad1986a_pcm_amp_vol_info, - .get = ad1986a_pcm_amp_vol_get, - .put = ad1986a_pcm_amp_vol_put, - .tlv = { .c = snd_hda_mixer_amp_tlv }, - .private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT) - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PCM Playback Switch", - .info = ad1986a_pcm_amp_sw_info, - .get = ad1986a_pcm_amp_sw_get, - .put = ad1986a_pcm_amp_sw_put, - .private_value = HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT) - }, + /* + * bind volumes/mutes of 3 DACs as a single PCM control for simplicity + */ + HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol), + HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw), HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT), @@ -568,13 +498,30 @@ /* laptop model - 2ch only */ static hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC }; +/* master controls both pins 0x1a and 0x1b */ +static struct hda_bind_ctls ad1986a_laptop_master_vol = { + .ops = &snd_hda_bind_vol, + .values = { + HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), + 0, + }, +}; + +static struct hda_bind_ctls ad1986a_laptop_master_sw = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), + 0, + }, +}; + static struct snd_kcontrol_new ad1986a_laptop_mixers[] = { HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Master Playback Volume", 0x1b, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Master Playback Switch", 0x1b, 0x0, HDA_OUTPUT), - /* HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT), */ + HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol), + HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw), HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT), @@ -602,43 +549,6 @@ /* laptop-eapd model - 2ch only */ -/* master controls both pins 0x1a and 0x1b */ -static int ad1986a_laptop_master_vol_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - long *valp = ucontrol->value.integer.value; - int change; - - change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0, - 0x7f, valp[0] & 0x7f); - change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0, - 0x7f, valp[1] & 0x7f); - snd_hda_codec_amp_update(codec, 0x1b, 0, HDA_OUTPUT, 0, - 0x7f, valp[0] & 0x7f); - snd_hda_codec_amp_update(codec, 0x1b, 1, HDA_OUTPUT, 0, - 0x7f, valp[1] & 0x7f); - return change; -} - -static int ad1986a_laptop_master_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - long *valp = ucontrol->value.integer.value; - int change; - - change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0, - 0x80, valp[0] ? 0 : 0x80); - change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0, - 0x80, valp[1] ? 0 : 0x80); - snd_hda_codec_amp_update(codec, 0x1b, 0, HDA_OUTPUT, 0, - 0x80, valp[0] ? 0 : 0x80); - snd_hda_codec_amp_update(codec, 0x1b, 1, HDA_OUTPUT, 0, - 0x80, valp[1] ? 0 : 0x80); - return change; -} - static struct hda_input_mux ad1986a_laptop_eapd_capture_source = { .num_items = 3, .items = { @@ -649,23 +559,8 @@ }; static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Volume", - .info = snd_hda_mixer_amp_volume_info, - .get = snd_hda_mixer_amp_volume_get, - .put = ad1986a_laptop_master_vol_put, - .tlv = { .c = snd_hda_mixer_amp_tlv }, - .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT), - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = ad1986a_laptop_master_sw_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT), - }, + HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol), + HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw), HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x0, HDA_OUTPUT), @@ -854,6 +749,17 @@ {} }; +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list ad1986a_loopbacks[] = { + { 0x13, HDA_OUTPUT, 0 }, /* Mic */ + { 0x14, HDA_OUTPUT, 0 }, /* Phone */ + { 0x15, HDA_OUTPUT, 0 }, /* CD */ + { 0x16, HDA_OUTPUT, 0 }, /* Aux */ + { 0x17, HDA_OUTPUT, 0 }, /* Line */ + { } /* end */ +}; +#endif + static int patch_ad1986a(struct hda_codec *codec) { struct ad198x_spec *spec; @@ -863,7 +769,6 @@ if (spec == NULL) return -ENOMEM; - mutex_init(&spec->amp_mutex); codec->spec = spec; spec->multiout.max_channels = 6; @@ -878,6 +783,9 @@ spec->mixers[0] = ad1986a_mixers; spec->num_init_verbs = 1; spec->init_verbs[0] = ad1986a_init_verbs; +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = ad1986a_loopbacks; +#endif codec->patch_ops = ad198x_patch_ops; @@ -981,8 +889,9 @@ if (spec->spdif_route != ucontrol->value.enumerated.item[0]) { spec->spdif_route = ucontrol->value.enumerated.item[0]; - snd_hda_codec_write(codec, spec->multiout.dig_out_nid, 0, - AC_VERB_SET_CONNECT_SEL, spec->spdif_route); + snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0, + AC_VERB_SET_CONNECT_SEL, + spec->spdif_route); return 1; } return 0; @@ -1062,6 +971,13 @@ { } /* end */ }; +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list ad1983_loopbacks[] = { + { 0x12, HDA_OUTPUT, 0 }, /* Mic */ + { 0x13, HDA_OUTPUT, 0 }, /* Line */ + { } /* end */ +}; +#endif static int patch_ad1983(struct hda_codec *codec) { @@ -1071,7 +987,6 @@ if (spec == NULL) return -ENOMEM; - mutex_init(&spec->amp_mutex); codec->spec = spec; spec->multiout.max_channels = 2; @@ -1087,6 +1002,9 @@ spec->num_init_verbs = 1; spec->init_verbs[0] = ad1983_init_verbs; spec->spdif_route = 0; +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = ad1983_loopbacks; +#endif codec->patch_ops = ad198x_patch_ops; @@ -1210,6 +1128,17 @@ { } /* end */ }; +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list ad1981_loopbacks[] = { + { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */ + { 0x13, HDA_OUTPUT, 0 }, /* Line */ + { 0x1b, HDA_OUTPUT, 0 }, /* Aux */ + { 0x1c, HDA_OUTPUT, 0 }, /* Mic */ + { 0x1d, HDA_OUTPUT, 0 }, /* CD */ + { } /* end */ +}; +#endif + /* * Patch for HP nx6320 * @@ -1239,31 +1168,21 @@ return 0; /* toggle HP mute appropriately */ - snd_hda_codec_amp_update(codec, 0x06, 0, HDA_OUTPUT, 0, - 0x80, spec->cur_eapd ? 0 : 0x80); - snd_hda_codec_amp_update(codec, 0x06, 1, HDA_OUTPUT, 0, - 0x80, spec->cur_eapd ? 0 : 0x80); + snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0, + HDA_AMP_MUTE, + spec->cur_eapd ? 0 : HDA_AMP_MUTE); return 1; } /* bind volumes of both NID 0x05 and 0x06 */ -static int ad1981_hp_master_vol_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - long *valp = ucontrol->value.integer.value; - int change; - - change = snd_hda_codec_amp_update(codec, 0x05, 0, HDA_OUTPUT, 0, - 0x7f, valp[0] & 0x7f); - change |= snd_hda_codec_amp_update(codec, 0x05, 1, HDA_OUTPUT, 0, - 0x7f, valp[1] & 0x7f); - snd_hda_codec_amp_update(codec, 0x06, 0, HDA_OUTPUT, 0, - 0x7f, valp[0] & 0x7f); - snd_hda_codec_amp_update(codec, 0x06, 1, HDA_OUTPUT, 0, - 0x7f, valp[1] & 0x7f); - return change; -} +static struct hda_bind_ctls ad1981_hp_bind_master_vol = { + .ops = &snd_hda_bind_vol, + .values = { + HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT), + 0 + }, +}; /* mute internal speaker if HP is plugged */ static void ad1981_hp_automute(struct hda_codec *codec) @@ -1272,10 +1191,8 @@ present = snd_hda_codec_read(codec, 0x06, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_amp_update(codec, 0x05, 0, HDA_OUTPUT, 0, - 0x80, present ? 0x80 : 0); - snd_hda_codec_amp_update(codec, 0x05, 1, HDA_OUTPUT, 0, - 0x80, present ? 0x80 : 0); + snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); } /* toggle input of built-in and mic jack appropriately */ @@ -1326,14 +1243,7 @@ }; static struct snd_kcontrol_new ad1981_hp_mixers[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Volume", - .info = snd_hda_mixer_amp_volume_info, - .get = snd_hda_mixer_amp_volume_get, - .put = ad1981_hp_master_vol_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT), - }, + HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", @@ -1473,7 +1383,6 @@ if (spec == NULL) return -ENOMEM; - mutex_init(&spec->amp_mutex); codec->spec = spec; spec->multiout.max_channels = 2; @@ -1489,6 +1398,9 @@ spec->num_init_verbs = 1; spec->init_verbs[0] = ad1981_init_verbs; spec->spdif_route = 0; +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = ad1981_loopbacks; +#endif codec->patch_ops = ad198x_patch_ops; @@ -1896,16 +1808,19 @@ struct hda_codec *codec = snd_kcontrol_chip(kcontrol); unsigned int sel; - sel = snd_hda_codec_read(codec, 0x02, 0, AC_VERB_GET_CONNECT_SEL, 0); - if (sel > 0) { + sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE, + AC_AMP_GET_INPUT); + if (!(sel & 0x80)) + ucontrol->value.enumerated.item[0] = 0; + else { sel = snd_hda_codec_read(codec, 0x0b, 0, AC_VERB_GET_CONNECT_SEL, 0); if (sel < 3) sel++; else sel = 0; + ucontrol->value.enumerated.item[0] = sel; } - ucontrol->value.enumerated.item[0] = sel; return 0; } @@ -1917,23 +1832,39 @@ int change; val = ucontrol->value.enumerated.item[0]; - sel = snd_hda_codec_read(codec, 0x02, 0, AC_VERB_GET_CONNECT_SEL, 0); if (!val) { - change = sel != 0; - if (change || codec->in_resume) - snd_hda_codec_write(codec, 0x02, 0, - AC_VERB_SET_CONNECT_SEL, 0); + sel = snd_hda_codec_read(codec, 0x1d, 0, + AC_VERB_GET_AMP_GAIN_MUTE, + AC_AMP_GET_INPUT); + change = sel & 0x80; + if (change) { + snd_hda_codec_write_cache(codec, 0x1d, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); + snd_hda_codec_write_cache(codec, 0x1d, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(1)); + } } else { - change = sel == 0; - if (change || codec->in_resume) - snd_hda_codec_write(codec, 0x02, 0, - AC_VERB_SET_CONNECT_SEL, 1); + sel = snd_hda_codec_read(codec, 0x1d, 0, + AC_VERB_GET_AMP_GAIN_MUTE, + AC_AMP_GET_INPUT | 0x01); + change = sel & 0x80; + if (change) { + snd_hda_codec_write_cache(codec, 0x1d, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(0)); + snd_hda_codec_write_cache(codec, 0x1d, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(1)); + } sel = snd_hda_codec_read(codec, 0x0b, 0, AC_VERB_GET_CONNECT_SEL, 0) + 1; change |= sel != val; - if (change || codec->in_resume) - snd_hda_codec_write(codec, 0x0b, 0, - AC_VERB_SET_CONNECT_SEL, val - 1); + if (change) + snd_hda_codec_write_cache(codec, 0x0b, 0, + AC_VERB_SET_CONNECT_SEL, + val - 1); } return change; } @@ -2046,10 +1977,9 @@ {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */ {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */ {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, /* SPDIF out pin */ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ - {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x17}, /* 0dB */ { } }; @@ -2224,6 +2154,15 @@ snd_hda_sequence_write(codec, ad1988_laptop_hp_off); } +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list ad1988_loopbacks[] = { + { 0x20, HDA_INPUT, 0 }, /* Front Mic */ + { 0x20, HDA_INPUT, 1 }, /* Line */ + { 0x20, HDA_INPUT, 4 }, /* Mic */ + { 0x20, HDA_INPUT, 6 }, /* CD */ + { } /* end */ +}; +#endif /* * Automatic parse of I/O pins from the BIOS configuration @@ -2662,7 +2601,6 @@ if (spec == NULL) return -ENOMEM; - mutex_init(&spec->amp_mutex); codec->spec = spec; if (is_rev2(codec)) @@ -2769,7 +2707,656 @@ codec->patch_ops.unsol_event = ad1988_laptop_unsol_event; break; } +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = ad1988_loopbacks; +#endif + + return 0; +} + + +/* + * AD1884 / AD1984 + * + * port-B - front line/mic-in + * port-E - aux in/out + * port-F - aux in/out + * port-C - rear line/mic-in + * port-D - rear line/hp-out + * port-A - front line/hp-out + * + * AD1984 = AD1884 + two digital mic-ins + * + * FIXME: + * For simplicity, we share the single DAC for both HP and line-outs + * right now. The inidividual playbacks could be easily implemented, + * but no build-up framework is given, so far. + */ + +static hda_nid_t ad1884_dac_nids[1] = { + 0x04, +}; + +static hda_nid_t ad1884_adc_nids[2] = { + 0x08, 0x09, +}; + +static hda_nid_t ad1884_capsrc_nids[2] = { + 0x0c, 0x0d, +}; +#define AD1884_SPDIF_OUT 0x02 + +static struct hda_input_mux ad1884_capture_source = { + .num_items = 4, + .items = { + { "Front Mic", 0x0 }, + { "Mic", 0x1 }, + { "CD", 0x2 }, + { "Mix", 0x3 }, + }, +}; + +static struct snd_kcontrol_new ad1884_base_mixers[] = { + HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT), + /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */ + HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT), + /* + HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Digital Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT), + */ + HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + * FIXME: the controls appear in the "playback" view! + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = ad198x_mux_enum_info, + .get = ad198x_mux_enum_get, + .put = ad198x_mux_enum_put, + }, + /* SPDIF controls */ + HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", + /* identical with ad1983 */ + .info = ad1983_spdif_route_info, + .get = ad1983_spdif_route_get, + .put = ad1983_spdif_route_put, + }, + { } /* end */ +}; + +static struct snd_kcontrol_new ad1984_dmic_mixers[] = { + HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0, + HDA_INPUT), + HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0, + HDA_INPUT), + { } /* end */ +}; + +/* + * initialization verbs + */ +static struct hda_verb ad1884_init_verbs[] = { + /* DACs; mute as default */ + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + /* Port-A (HP) mixer */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* Port-A pin */ + {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* HP selector - select DAC2 */ + {0x22, AC_VERB_SET_CONNECT_SEL, 0x1}, + /* Port-D (Line-out) mixer */ + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* Port-D pin */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Mono-out mixer */ + {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* Mono-out pin */ + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Mono selector */ + {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1}, + /* Port-B (front mic) pin */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Port-C (rear mic) pin */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Analog mixer; mute as default */ + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + /* Analog Mix output amp */ + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */ + /* SPDIF output selector */ + {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */ + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ + { } /* end */ +}; + +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list ad1884_loopbacks[] = { + { 0x20, HDA_INPUT, 0 }, /* Front Mic */ + { 0x20, HDA_INPUT, 1 }, /* Mic */ + { 0x20, HDA_INPUT, 2 }, /* CD */ + { 0x20, HDA_INPUT, 4 }, /* Docking */ + { } /* end */ +}; +#endif + +static int patch_ad1884(struct hda_codec *codec) +{ + struct ad198x_spec *spec; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + mutex_init(&spec->amp_mutex); + codec->spec = spec; + + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids); + spec->multiout.dac_nids = ad1884_dac_nids; + spec->multiout.dig_out_nid = AD1884_SPDIF_OUT; + spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids); + spec->adc_nids = ad1884_adc_nids; + spec->capsrc_nids = ad1884_capsrc_nids; + spec->input_mux = &ad1884_capture_source; + spec->num_mixers = 1; + spec->mixers[0] = ad1884_base_mixers; + spec->num_init_verbs = 1; + spec->init_verbs[0] = ad1884_init_verbs; + spec->spdif_route = 0; +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = ad1884_loopbacks; +#endif + + codec->patch_ops = ad198x_patch_ops; + + return 0; +} + +/* + * Lenovo Thinkpad T61/X61 + */ +static struct hda_input_mux ad1984_thinkpad_capture_source = { + .num_items = 3, + .items = { + { "Mic", 0x0 }, + { "Internal Mic", 0x1 }, + { "Mix", 0x3 }, + }, +}; + +static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = { + HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT), + /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */ + HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + * FIXME: the controls appear in the "playback" view! + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = ad198x_mux_enum_info, + .get = ad198x_mux_enum_get, + .put = ad198x_mux_enum_put, + }, + { } /* end */ +}; + +/* additional verbs */ +static struct hda_verb ad1984_thinkpad_init_verbs[] = { + /* Port-E (docking station mic) pin */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* docking mic boost */ + {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Analog mixer - docking mic; mute as default */ + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* enable EAPD bit */ + {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, + { } /* end */ +}; + +/* Digial MIC ADC NID 0x05 + 0x06 */ +static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + snd_hda_codec_setup_stream(codec, 0x05 + substream->number, + stream_tag, 0, format); + return 0; +} + +static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + snd_hda_codec_setup_stream(codec, 0x05 + substream->number, + 0, 0, 0); + return 0; +} + +static struct hda_pcm_stream ad1984_pcm_dmic_capture = { + .substreams = 2, + .channels_min = 2, + .channels_max = 2, + .nid = 0x05, + .ops = { + .prepare = ad1984_pcm_dmic_prepare, + .cleanup = ad1984_pcm_dmic_cleanup + }, +}; + +static int ad1984_build_pcms(struct hda_codec *codec) +{ + struct ad198x_spec *spec = codec->spec; + struct hda_pcm *info; + int err; + + err = ad198x_build_pcms(codec); + if (err < 0) + return err; + + info = spec->pcm_rec + codec->num_pcms; + codec->num_pcms++; + info->name = "AD1984 Digital Mic"; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture; + return 0; +} + +/* models */ +enum { + AD1984_BASIC, + AD1984_THINKPAD, + AD1984_MODELS +}; + +static const char *ad1984_models[AD1984_MODELS] = { + [AD1984_BASIC] = "basic", + [AD1984_THINKPAD] = "thinkpad", +}; + +static struct snd_pci_quirk ad1984_cfg_tbl[] = { + /* Lenovo Thinkpad T61/X61 */ + SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1984_THINKPAD), + {} +}; + +static int patch_ad1984(struct hda_codec *codec) +{ + struct ad198x_spec *spec; + int board_config, err; + + err = patch_ad1884(codec); + if (err < 0) + return err; + spec = codec->spec; + board_config = snd_hda_check_board_config(codec, AD1984_MODELS, + ad1984_models, ad1984_cfg_tbl); + switch (board_config) { + case AD1984_BASIC: + /* additional digital mics */ + spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers; + codec->patch_ops.build_pcms = ad1984_build_pcms; + break; + case AD1984_THINKPAD: + spec->multiout.dig_out_nid = 0; + spec->input_mux = &ad1984_thinkpad_capture_source; + spec->mixers[0] = ad1984_thinkpad_mixers; + spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs; + break; + } + return 0; +} + + +/* + * AD1882 + * + * port-A - front hp-out + * port-B - front mic-in + * port-C - rear line-in, shared surr-out (3stack) + * port-D - rear line-out + * port-E - rear mic-in, shared clfe-out (3stack) + * port-F - rear surr-out (6stack) + * port-G - rear clfe-out (6stack) + */ + +static hda_nid_t ad1882_dac_nids[3] = { + 0x04, 0x03, 0x05 +}; + +static hda_nid_t ad1882_adc_nids[2] = { + 0x08, 0x09, +}; + +static hda_nid_t ad1882_capsrc_nids[2] = { + 0x0c, 0x0d, +}; + +#define AD1882_SPDIF_OUT 0x02 + +/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */ +static struct hda_input_mux ad1882_capture_source = { + .num_items = 5, + .items = { + { "Front Mic", 0x1 }, + { "Mic", 0x4 }, + { "Line", 0x2 }, + { "CD", 0x3 }, + { "Mix", 0x7 }, + }, +}; + +static struct snd_kcontrol_new ad1882_base_mixers[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT), + HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT), + HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Line-In Boost", 0x3a, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + * FIXME: the controls appear in the "playback" view! + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = ad198x_mux_enum_info, + .get = ad198x_mux_enum_get, + .put = ad198x_mux_enum_put, + }, + /* SPDIF controls */ + HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", + /* identical with ad1983 */ + .info = ad1983_spdif_route_info, + .get = ad1983_spdif_route_get, + .put = ad1983_spdif_route_put, + }, + { } /* end */ +}; + +static struct snd_kcontrol_new ad1882_3stack_mixers[] = { + HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = ad198x_ch_mode_info, + .get = ad198x_ch_mode_get, + .put = ad198x_ch_mode_put, + }, + { } /* end */ +}; + +static struct snd_kcontrol_new ad1882_6stack_mixers[] = { + HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT), + { } /* end */ +}; + +static struct hda_verb ad1882_ch2_init[] = { + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + { } /* end */ +}; + +static struct hda_verb ad1882_ch4_init[] = { + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + { } /* end */ +}; + +static struct hda_verb ad1882_ch6_init[] = { + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + { } /* end */ +}; + +static struct hda_channel_mode ad1882_modes[3] = { + { 2, ad1882_ch2_init }, + { 4, ad1882_ch4_init }, + { 6, ad1882_ch6_init }, +}; + +/* + * initialization verbs + */ +static struct hda_verb ad1882_init_verbs[] = { + /* DACs; mute as default */ + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + /* Port-A (HP) mixer */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* Port-A pin */ + {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* HP selector - select DAC2 */ + {0x37, AC_VERB_SET_CONNECT_SEL, 0x1}, + /* Port-D (Line-out) mixer */ + {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* Port-D pin */ + {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Mono-out mixer */ + {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* Mono-out pin */ + {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Port-B (front mic) pin */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */ + /* Port-C (line-in) pin */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */ + /* Port-C mixer - mute as input */ + {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* Port-E (mic-in) pin */ + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */ + /* Port-E mixer - mute as input */ + {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* Port-F (surround) */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Port-G (CLFE) */ + {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Analog mixer; mute as default */ + /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */ + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, + /* Analog Mix output amp */ + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */ + /* SPDIF output selector */ + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ + {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */ + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ + { } /* end */ +}; + +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list ad1882_loopbacks[] = { + { 0x20, HDA_INPUT, 0 }, /* Front Mic */ + { 0x20, HDA_INPUT, 1 }, /* Mic */ + { 0x20, HDA_INPUT, 4 }, /* Line */ + { 0x20, HDA_INPUT, 6 }, /* CD */ + { } /* end */ +}; +#endif + +/* models */ +enum { + AD1882_3STACK, + AD1882_6STACK, + AD1882_MODELS +}; + +static const char *ad1882_models[AD1986A_MODELS] = { + [AD1882_3STACK] = "3stack", + [AD1882_6STACK] = "6stack", +}; + + +static int patch_ad1882(struct hda_codec *codec) +{ + struct ad198x_spec *spec; + int board_config; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + mutex_init(&spec->amp_mutex); + codec->spec = spec; + + spec->multiout.max_channels = 6; + spec->multiout.num_dacs = 3; + spec->multiout.dac_nids = ad1882_dac_nids; + spec->multiout.dig_out_nid = AD1882_SPDIF_OUT; + spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids); + spec->adc_nids = ad1882_adc_nids; + spec->capsrc_nids = ad1882_capsrc_nids; + spec->input_mux = &ad1882_capture_source; + spec->num_mixers = 1; + spec->mixers[0] = ad1882_base_mixers; + spec->num_init_verbs = 1; + spec->init_verbs[0] = ad1882_init_verbs; + spec->spdif_route = 0; +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = ad1882_loopbacks; +#endif + + codec->patch_ops = ad198x_patch_ops; + + /* override some parameters */ + board_config = snd_hda_check_board_config(codec, AD1882_MODELS, + ad1882_models, NULL); + switch (board_config) { + default: + case AD1882_3STACK: + spec->num_mixers = 2; + spec->mixers[1] = ad1882_3stack_mixers; + spec->channel_mode = ad1882_modes; + spec->num_channel_mode = ARRAY_SIZE(ad1882_modes); + spec->need_dac_fix = 1; + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = 1; + break; + case AD1882_6STACK: + spec->num_mixers = 2; + spec->mixers[1] = ad1882_6stack_mixers; + break; + } return 0; } @@ -2778,8 +3365,11 @@ * patch entries */ struct hda_codec_preset snd_hda_preset_analog[] = { + { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 }, + { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 }, { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 }, { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 }, + { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 }, { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a }, { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 }, { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 }, diff -Nur sound/pci/hda/patch_atihdmi.c sound/pci/hda/patch_atihdmi.c --- sound/pci/hda/patch_atihdmi.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/hda/patch_atihdmi.c 2007-08-11 02:00:08.000000000 +0200 @@ -62,19 +62,6 @@ return 0; } -#ifdef CONFIG_PM -/* - * resume - */ -static int atihdmi_resume(struct hda_codec *codec) -{ - atihdmi_init(codec); - snd_hda_resume_spdif_out(codec); - - return 0; -} -#endif - /* * Digital out */ @@ -141,9 +128,6 @@ .build_pcms = atihdmi_build_pcms, .init = atihdmi_init, .free = atihdmi_free, -#ifdef CONFIG_PM - .resume = atihdmi_resume, -#endif }; static int patch_atihdmi(struct hda_codec *codec) @@ -172,6 +156,7 @@ */ struct hda_codec_preset snd_hda_preset_atihdmi[] = { { .id = 0x1002793c, .name = "ATI RS600 HDMI", .patch = patch_atihdmi }, + { .id = 0x10027919, .name = "ATI RS600 HDMI", .patch = patch_atihdmi }, { .id = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi }, { .id = 0x1002aa01, .name = "ATI R600 HDMI", .patch = patch_atihdmi }, {} /* terminator */ diff -Nur sound/pci/hda/patch_cmedia.c sound/pci/hda/patch_cmedia.c --- sound/pci/hda/patch_cmedia.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/hda/patch_cmedia.c 2007-08-11 02:00:08.000000000 +0200 @@ -427,27 +427,6 @@ return 0; } -#ifdef CONFIG_PM -/* - * resume - */ -static int cmi9880_resume(struct hda_codec *codec) -{ - struct cmi_spec *spec = codec->spec; - - cmi9880_init(codec); - snd_hda_resume_ctls(codec, cmi9880_basic_mixer); - if (spec->channel_modes) - snd_hda_resume_ctls(codec, cmi9880_ch_mode_mixer); - if (spec->multiout.dig_out_nid) - snd_hda_resume_spdif_out(codec); - if (spec->dig_in_nid) - snd_hda_resume_spdif_in(codec); - - return 0; -} -#endif - /* * Analog playback callbacks */ @@ -635,9 +614,6 @@ .build_pcms = cmi9880_build_pcms, .init = cmi9880_init, .free = cmi9880_free, -#ifdef CONFIG_PM - .resume = cmi9880_resume, -#endif }; static int patch_cmi9880(struct hda_codec *codec) diff -Nur sound/pci/hda/patch_conexant.c sound/pci/hda/patch_conexant.c --- sound/pci/hda/patch_conexant.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/hda/patch_conexant.c 2007-08-11 02:00:08.000000000 +0200 @@ -311,23 +311,6 @@ kfree(codec->spec); } -#ifdef CONFIG_PM -static int conexant_resume(struct hda_codec *codec) -{ - struct conexant_spec *spec = codec->spec; - int i; - - codec->patch_ops.init(codec); - for (i = 0; i < spec->num_mixers; i++) - snd_hda_resume_ctls(codec, spec->mixers[i]); - if (spec->multiout.dig_out_nid) - snd_hda_resume_spdif_out(codec); - if (spec->dig_in_nid) - snd_hda_resume_spdif_in(codec); - return 0; -} -#endif - static int conexant_build_controls(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; @@ -358,9 +341,6 @@ .build_pcms = conexant_build_pcms, .init = conexant_init, .free = conexant_free, -#ifdef CONFIG_PM - .resume = conexant_resume, -#endif }; /* @@ -368,15 +348,7 @@ * the private value = nid | (invert << 8) */ -static int cxt_eapd_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define cxt_eapd_info snd_ctl_boolean_mono_info static int cxt_eapd_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -404,13 +376,13 @@ eapd = ucontrol->value.integer.value[0]; if (invert) eapd = !eapd; - if (eapd == spec->cur_eapd && !codec->in_resume) + if (eapd == spec->cur_eapd) return 0; spec->cur_eapd = eapd; - snd_hda_codec_write(codec, nid, - 0, AC_VERB_SET_EAPD_BTLENABLE, - eapd ? 0x02 : 0x00); + snd_hda_codec_write_cache(codec, nid, + 0, AC_VERB_SET_EAPD_BTLENABLE, + eapd ? 0x02 : 0x00); return 1; } @@ -500,34 +472,25 @@ /* toggle internal speakers mute depending of presence of * the headphone jack */ - bits = (!spec->hp_present && spec->cur_eapd) ? 0 : 0x80; - snd_hda_codec_amp_update(codec, 0x10, 0, HDA_OUTPUT, 0, 0x80, bits); - snd_hda_codec_amp_update(codec, 0x10, 1, HDA_OUTPUT, 0, 0x80, bits); - - bits = spec->cur_eapd ? 0 : 0x80; - snd_hda_codec_amp_update(codec, 0x11, 0, HDA_OUTPUT, 0, 0x80, bits); - snd_hda_codec_amp_update(codec, 0x11, 1, HDA_OUTPUT, 0, 0x80, bits); + bits = (!spec->hp_present && spec->cur_eapd) ? 0 : HDA_AMP_MUTE; + snd_hda_codec_amp_stereo(codec, 0x10, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); + + bits = spec->cur_eapd ? 0 : HDA_AMP_MUTE; + snd_hda_codec_amp_stereo(codec, 0x11, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); return 1; } /* bind volumes of both NID 0x10 and 0x11 */ -static int cxt5045_hp_master_vol_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - long *valp = ucontrol->value.integer.value; - int change; - - change = snd_hda_codec_amp_update(codec, 0x10, 0, HDA_OUTPUT, 0, - 0x7f, valp[0] & 0x7f); - change |= snd_hda_codec_amp_update(codec, 0x10, 1, HDA_OUTPUT, 0, - 0x7f, valp[1] & 0x7f); - snd_hda_codec_amp_update(codec, 0x11, 0, HDA_OUTPUT, 0, - 0x7f, valp[0] & 0x7f); - snd_hda_codec_amp_update(codec, 0x11, 1, HDA_OUTPUT, 0, - 0x7f, valp[1] & 0x7f); - return change; -} +static struct hda_bind_ctls cxt5045_hp_bind_master_vol = { + .ops = &snd_hda_bind_vol, + .values = { + HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x11, 3, 0, HDA_OUTPUT), + 0 + }, +}; /* toggle input of built-in and mic jack appropriately */ static void cxt5045_hp_automic(struct hda_codec *codec) @@ -562,9 +525,9 @@ spec->hp_present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = (spec->hp_present || !spec->cur_eapd) ? 0x80 : 0; - snd_hda_codec_amp_update(codec, 0x10, 0, HDA_OUTPUT, 0, 0x80, bits); - snd_hda_codec_amp_update(codec, 0x10, 1, HDA_OUTPUT, 0, 0x80, bits); + bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x10, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); } /* unsolicited event for HP jack sensing */ @@ -595,14 +558,7 @@ HDA_CODEC_MUTE("Int Mic Switch", 0x1a, 0x01, HDA_INPUT), HDA_CODEC_VOLUME("Ext Mic Volume", 0x1a, 0x02, HDA_INPUT), HDA_CODEC_MUTE("Ext Mic Switch", 0x1a, 0x02, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Volume", - .info = snd_hda_mixer_amp_volume_info, - .get = snd_hda_mixer_amp_volume_get, - .put = cxt5045_hp_master_vol_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT), - }, + HDA_BIND_VOL("Master Playback Volume", &cxt5045_hp_bind_master_vol), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", @@ -801,7 +757,9 @@ SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP), SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP), SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP), + SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP), SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_FUJITSU), + SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP), SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP), {} }; @@ -913,33 +871,24 @@ /* toggle internal speakers mute depending of presence of * the headphone jack */ - bits = (!spec->hp_present && spec->cur_eapd) ? 0 : 0x80; - snd_hda_codec_amp_update(codec, 0x1d, 0, HDA_OUTPUT, 0, 0x80, bits); - snd_hda_codec_amp_update(codec, 0x1d, 1, HDA_OUTPUT, 0, 0x80, bits); - bits = spec->cur_eapd ? 0 : 0x80; - snd_hda_codec_amp_update(codec, 0x13, 0, HDA_OUTPUT, 0, 0x80, bits); - snd_hda_codec_amp_update(codec, 0x13, 1, HDA_OUTPUT, 0, 0x80, bits); + bits = (!spec->hp_present && spec->cur_eapd) ? 0 : HDA_AMP_MUTE; + snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); + bits = spec->cur_eapd ? 0 : HDA_AMP_MUTE; + snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); return 1; } /* bind volumes of both NID 0x13 (Headphones) and 0x1d (Speakers) */ -static int cxt5047_hp_master_vol_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - long *valp = ucontrol->value.integer.value; - int change; - - change = snd_hda_codec_amp_update(codec, 0x1d, 0, HDA_OUTPUT, 0, - 0x7f, valp[0] & 0x7f); - change |= snd_hda_codec_amp_update(codec, 0x1d, 1, HDA_OUTPUT, 0, - 0x7f, valp[1] & 0x7f); - snd_hda_codec_amp_update(codec, 0x13, 0, HDA_OUTPUT, 0, - 0x7f, valp[0] & 0x7f); - snd_hda_codec_amp_update(codec, 0x13, 1, HDA_OUTPUT, 0, - 0x7f, valp[1] & 0x7f); - return change; -} +static struct hda_bind_ctls cxt5047_bind_master_vol = { + .ops = &snd_hda_bind_vol, + .values = { + HDA_COMPOSE_AMP_VAL(0x13, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT), + 0 + }, +}; /* mute internal speaker if HP is plugged */ static void cxt5047_hp_automute(struct hda_codec *codec) @@ -950,12 +899,12 @@ spec->hp_present = snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = (spec->hp_present || !spec->cur_eapd) ? 0x80 : 0; - snd_hda_codec_amp_update(codec, 0x1d, 0, HDA_OUTPUT, 0, 0x80, bits); - snd_hda_codec_amp_update(codec, 0x1d, 1, HDA_OUTPUT, 0, 0x80, bits); + bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); /* Mute/Unmute PCM 2 for good measure - some systems need this */ - snd_hda_codec_amp_update(codec, 0x1c, 0, HDA_OUTPUT, 0, 0x80, bits); - snd_hda_codec_amp_update(codec, 0x1c, 1, HDA_OUTPUT, 0, 0x80, bits); + snd_hda_codec_amp_stereo(codec, 0x1c, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); } /* mute internal speaker if HP is plugged */ @@ -967,12 +916,12 @@ spec->hp_present = snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = spec->hp_present ? 0x80 : 0; - snd_hda_codec_amp_update(codec, 0x1d, 0, HDA_OUTPUT, 0, 0x80, bits); - snd_hda_codec_amp_update(codec, 0x1d, 1, HDA_OUTPUT, 0, 0x80, bits); + bits = spec->hp_present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x1d, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); /* Mute/Unmute PCM 2 for good measure - some systems need this */ - snd_hda_codec_amp_update(codec, 0x1c, 0, HDA_OUTPUT, 0, 0x80, bits); - snd_hda_codec_amp_update(codec, 0x1c, 1, HDA_OUTPUT, 0, 0x80, bits); + snd_hda_codec_amp_stereo(codec, 0x1c, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); } /* toggle input of built-in and mic jack appropriately */ @@ -1061,14 +1010,7 @@ HDA_CODEC_MUTE("Capture Switch", 0x12, 0x03, HDA_INPUT), HDA_CODEC_VOLUME("PCM Volume", 0x10, 0x00, HDA_OUTPUT), HDA_CODEC_MUTE("PCM Switch", 0x10, 0x00, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Volume", - .info = snd_hda_mixer_amp_volume_info, - .get = snd_hda_mixer_amp_volume_get, - .put = cxt5047_hp_master_vol_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x13, 3, 0, HDA_OUTPUT), - }, + HDA_BIND_VOL("Master Playback Volume", &cxt5047_bind_master_vol), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", diff -Nur sound/pci/hda/patch_realtek.c sound/pci/hda/patch_realtek.c --- sound/pci/hda/patch_realtek.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/hda/patch_realtek.c 2007-08-18 02:00:07.000000000 +0200 @@ -94,10 +94,20 @@ ALC262_HP_BPC_D7000_WF, ALC262_BENQ_ED8, ALC262_SONY_ASSAMD, + ALC262_BENQ_T31, ALC262_AUTO, ALC262_MODEL_LAST /* last tag */ }; +/* ALC268 models */ +enum { + ALC268_3ST, + ALC268_TOSHIBA, + ALC268_ACER, + ALC268_AUTO, + ALC268_MODEL_LAST /* last tag */ +}; + /* ALC861 models */ enum { ALC861_3ST, @@ -115,11 +125,13 @@ /* ALC861-VD models */ enum { ALC660VD_3ST, + ALC660VD_3ST_DIG, ALC861VD_3ST, ALC861VD_3ST_DIG, ALC861VD_6ST_DIG, ALC861VD_LENOVO, ALC861VD_DALLAS, + ALC861VD_HP, ALC861VD_AUTO, ALC861VD_MODEL_LAST, }; @@ -144,6 +156,8 @@ ALC882_TARGA, ALC882_ASUS_A7J, ALC885_MACPRO, + ALC885_MBP3, + ALC885_IMAC24, ALC882_AUTO, ALC882_MODEL_LAST, }; @@ -157,12 +171,15 @@ ALC883_TARGA_DIG, ALC883_TARGA_2ch_DIG, ALC883_ACER, + ALC883_ACER_ASPIRE, ALC883_MEDION, ALC883_MEDION_MD2, ALC883_LAPTOP_EAPD, ALC883_LENOVO_101E_2ch, ALC883_LENOVO_NB0763, ALC888_LENOVO_MS7195_DIG, + ALC888_6ST_HP, + ALC888_3ST_HP, ALC883_AUTO, ALC883_MODEL_LAST, }; @@ -227,6 +244,10 @@ /* for pin sensing */ unsigned int sense_updated: 1; unsigned int jack_present: 1; + +#ifdef CONFIG_SND_HDA_POWER_SAVE + struct hda_loopback_check loopback; +#endif }; /* @@ -251,6 +272,9 @@ const struct hda_input_mux *input_mux; void (*unsol_event)(struct hda_codec *, unsigned int); void (*init_hook)(struct hda_codec *); +#ifdef CONFIG_SND_HDA_POWER_SAVE + struct hda_amp_list *loopbacks; +#endif }; @@ -429,8 +453,9 @@ change = pinctl != alc_pin_mode_values[val]; if (change) { /* Set pin mode to that requested */ - snd_hda_codec_write(codec,nid,0,AC_VERB_SET_PIN_WIDGET_CONTROL, - alc_pin_mode_values[val]); + snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + alc_pin_mode_values[val]); /* Also enable the retasking pin's input/output as required * for the requested pin mode. Enum values of 2 or less are @@ -443,19 +468,15 @@ * this turns out to be necessary in the future. */ if (val <= 2) { - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); + snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, + HDA_AMP_MUTE, HDA_AMP_MUTE); + snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0, + HDA_AMP_MUTE, 0); } else { - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(0)); - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); + snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0, + HDA_AMP_MUTE, HDA_AMP_MUTE); + snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, + HDA_AMP_MUTE, 0); } } return change; @@ -474,15 +495,7 @@ * needed for any "production" models. */ #ifdef CONFIG_SND_DEBUG -static int alc_gpio_data_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define alc_gpio_data_info snd_ctl_boolean_mono_info static int alc_gpio_data_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -515,7 +528,8 @@ gpio_data &= ~mask; else gpio_data |= mask; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_GPIO_DATA, gpio_data); + snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_GPIO_DATA, gpio_data); return change; } @@ -535,15 +549,7 @@ * necessary. */ #ifdef CONFIG_SND_DEBUG -static int alc_spdif_ctrl_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define alc_spdif_ctrl_info snd_ctl_boolean_mono_info static int alc_spdif_ctrl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -576,8 +582,8 @@ ctrl_data &= ~mask; else ctrl_data |= mask; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, - ctrl_data); + snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, + ctrl_data); return change; } @@ -626,6 +632,9 @@ spec->unsol_event = preset->unsol_event; spec->init_hook = preset->init_hook; +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = preset->loopbacks; +#endif } /* Enable GPIO mask and set output */ @@ -713,6 +722,38 @@ } /* + * Fix-up pin default configurations + */ + +struct alc_pincfg { + hda_nid_t nid; + u32 val; +}; + +static void alc_fix_pincfg(struct hda_codec *codec, + const struct snd_pci_quirk *quirk, + const struct alc_pincfg **pinfix) +{ + const struct alc_pincfg *cfg; + + quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk); + if (!quirk) + return; + + cfg = pinfix[quirk->value]; + for (; cfg->nid; cfg++) { + int i; + u32 val = cfg->val; + for (i = 0; i < 4; i++) { + snd_hda_codec_write(codec, cfg->nid, 0, + AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 + i, + val & 0xff); + val >>= 8; + } + } +} + +/* * ALC880 3-stack model * * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0e) @@ -1260,11 +1301,13 @@ * panel mic (mic 2) */ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* * Set up output mixers (0x0c - 0x0f) @@ -1524,15 +1567,11 @@ present = snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? 0x80 : 0; - snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0, - 0x80, bits); - snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0, - 0x80, bits); - snd_hda_codec_amp_update(codec, 0x16, 0, HDA_OUTPUT, 0, - 0x80, bits); - snd_hda_codec_amp_update(codec, 0x16, 1, HDA_OUTPUT, 0, - 0x80, bits); + bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); + snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); } /* auto-toggle front mic */ @@ -1543,11 +1582,8 @@ present = snd_hda_codec_read(codec, 0x18, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? 0x80 : 0; - snd_hda_codec_amp_update(codec, 0x0b, 0, HDA_INPUT, 1, - 0x80, bits); - snd_hda_codec_amp_update(codec, 0x0b, 1, HDA_INPUT, 1, - 0x80, bits); + bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits); } static void alc880_uniwill_automute(struct hda_codec *codec) @@ -1579,11 +1615,8 @@ present = snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? 0x80 : 0; - snd_hda_codec_amp_update(codec, 0x15, 0, HDA_INPUT, 0, - 0x80, bits); - snd_hda_codec_amp_update(codec, 0x15, 1, HDA_INPUT, 0, - 0x80, bits); + bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x15, HDA_INPUT, 0, HDA_AMP_MUTE, bits); } static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec) @@ -1591,19 +1624,14 @@ unsigned int present; present = snd_hda_codec_read(codec, 0x21, 0, - AC_VERB_GET_VOLUME_KNOB_CONTROL, 0) & 0x7f; - - snd_hda_codec_amp_update(codec, 0x0c, 0, HDA_OUTPUT, 0, - 0x7f, present); - snd_hda_codec_amp_update(codec, 0x0c, 1, HDA_OUTPUT, 0, - 0x7f, present); - - snd_hda_codec_amp_update(codec, 0x0d, 0, HDA_OUTPUT, 0, - 0x7f, present); - snd_hda_codec_amp_update(codec, 0x0d, 1, HDA_OUTPUT, 0, - 0x7f, present); - + AC_VERB_GET_VOLUME_KNOB_CONTROL, 0); + present &= HDA_AMP_VOLMASK; + snd_hda_codec_amp_stereo(codec, 0x0c, HDA_OUTPUT, 0, + HDA_AMP_VOLMASK, present); + snd_hda_codec_amp_stereo(codec, 0x0d, HDA_OUTPUT, 0, + HDA_AMP_VOLMASK, present); } + static void alc880_uniwill_p53_unsol_event(struct hda_codec *codec, unsigned int res) { @@ -1824,8 +1852,8 @@ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, /* mute all amp mixer inputs */ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(6)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(7)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* line-in to input */ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, @@ -1856,11 +1884,9 @@ present = snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? 0x80 : 0; - snd_hda_codec_amp_update(codec, 0x17, 0, HDA_OUTPUT, 0, - 0x80, bits); - snd_hda_codec_amp_update(codec, 0x17, 1, HDA_OUTPUT, 0, - 0x80, bits); + bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); } static void alc880_lg_unsol_event(struct hda_codec *codec, unsigned int res) @@ -1878,41 +1904,62 @@ * Pin assignment: * Speaker-out: 0x14 * Mic-In: 0x18 - * Built-in Mic-In: 0x19 (?) - * HP-Out: 0x1b + * Built-in Mic-In: 0x19 + * Line-In: 0x1b + * HP-Out: 0x1a * SPDIF-Out: 0x1e */ -/* seems analog CD is not working */ static struct hda_input_mux alc880_lg_lw_capture_source = { - .num_items = 2, + .num_items = 3, .items = { { "Mic", 0x0 }, { "Internal Mic", 0x1 }, + { "Line In", 0x2 }, }, }; +#define alc880_lg_lw_modes alc880_threestack_modes + static struct snd_kcontrol_new alc880_lg_lw_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = alc_ch_mode_info, + .get = alc_ch_mode_get, + .put = alc_ch_mode_put, + }, { } /* end */ }; static struct hda_verb alc880_lg_lw_init_verbs[] = { + {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ + {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */ + {0x12, AC_VERB_SET_CONNECT_SEL, 0x03}, /* line/surround */ + /* set capture source to mic-in */ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(7)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* speaker-out */ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* HP-out */ - {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* mic-in to input */ @@ -1934,11 +1981,9 @@ present = snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? 0x80 : 0; - snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0, - 0x80, bits); - snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0, - 0x80, bits); + bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); } static void alc880_lg_lw_unsol_event(struct hda_codec *codec, unsigned int res) @@ -1950,6 +1995,24 @@ alc880_lg_lw_automute(codec); } +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list alc880_loopbacks[] = { + { 0x0b, HDA_INPUT, 0 }, + { 0x0b, HDA_INPUT, 1 }, + { 0x0b, HDA_INPUT, 2 }, + { 0x0b, HDA_INPUT, 3 }, + { 0x0b, HDA_INPUT, 4 }, + { } /* end */ +}; + +static struct hda_amp_list alc880_lg_loopbacks[] = { + { 0x0b, HDA_INPUT, 1 }, + { 0x0b, HDA_INPUT, 6 }, + { 0x0b, HDA_INPUT, 7 }, + { } /* end */ +}; +#endif + /* * Common callbacks */ @@ -1976,24 +2039,11 @@ spec->unsol_event(codec, res); } -#ifdef CONFIG_PM -/* - * resume - */ -static int alc_resume(struct hda_codec *codec) +#ifdef CONFIG_SND_HDA_POWER_SAVE +static int alc_check_power_status(struct hda_codec *codec, hda_nid_t nid) { struct alc_spec *spec = codec->spec; - int i; - - alc_init(codec); - for (i = 0; i < spec->num_mixers; i++) - snd_hda_resume_ctls(codec, spec->mixers[i]); - if (spec->multiout.dig_out_nid) - snd_hda_resume_spdif_out(codec); - if (spec->dig_in_nid) - snd_hda_resume_spdif_in(codec); - - return 0; + return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); } #endif @@ -2228,8 +2278,8 @@ .init = alc_init, .free = alc_free, .unsol_event = alc_unsol_event, -#ifdef CONFIG_PM - .resume = alc_resume, +#ifdef CONFIG_SND_HDA_POWER_SAVE + .check_power_status = alc_check_power_status, #endif }; @@ -2327,11 +2377,14 @@ AC_VERB_GET_PIN_WIDGET_CONTROL, 0); new_ctl = ctls[ucontrol->value.enumerated.item[0]]; if (old_ctl != new_ctl) { - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, new_ctl); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - (ucontrol->value.enumerated.item[0] >= 3 ? - 0xb080 : 0xb000)); + int val; + snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + new_ctl); + val = ucontrol->value.enumerated.item[0] >= 3 ? + HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, + HDA_AMP_MUTE, val); return 1; } return 0; @@ -2374,7 +2427,8 @@ sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0) & 3; if (ucontrol->value.enumerated.item[0] != sel) { sel = ucontrol->value.enumerated.item[0] & 3; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, sel); + snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_CONNECT_SEL, sel); return 1; } return 0; @@ -2851,16 +2905,19 @@ .input_mux = &alc880_lg_capture_source, .unsol_event = alc880_lg_unsol_event, .init_hook = alc880_lg_automute, +#ifdef CONFIG_SND_HDA_POWER_SAVE + .loopbacks = alc880_lg_loopbacks, +#endif }, [ALC880_LG_LW] = { .mixers = { alc880_lg_lw_mixer }, .init_verbs = { alc880_volume_init_verbs, alc880_lg_lw_init_verbs }, - .num_dacs = 1, + .num_dacs = ARRAY_SIZE(alc880_dac_nids), .dac_nids = alc880_dac_nids, .dig_out_nid = ALC880_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes), - .channel_mode = alc880_2_jack_modes, + .num_channel_mode = ARRAY_SIZE(alc880_lg_lw_modes), + .channel_mode = alc880_lg_lw_modes, .input_mux = &alc880_lg_lw_capture_source, .unsol_event = alc880_lg_lw_unsol_event, .init_hook = alc880_lg_lw_automute, @@ -3334,6 +3391,10 @@ codec->patch_ops = alc_patch_ops; if (board_config == ALC880_AUTO) spec->init_hook = alc880_auto_init; +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (!spec->loopback.amplist) + spec->loopback.amplist = alc880_loopbacks; +#endif return 0; } @@ -3682,12 +3743,12 @@ /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & * Line In 2 = 0x03 */ - /* mute CD */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - /* mute Line In */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - /* mute Mic */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + /* mute analog inputs */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */ /* mute Front out path */ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, @@ -3732,12 +3793,12 @@ /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & * Line In 2 = 0x03 */ - /* unmute CD */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, - /* unmute Line In */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, - /* unmute Mic */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + /* mute analog inputs */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */ /* Unmute Front out path */ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, @@ -3782,12 +3843,12 @@ /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & * Line In 2 = 0x03 */ - /* unmute CD */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, - /* unmute Line In */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, - /* unmute Mic */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + /* mute analog inputs */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */ /* Unmute Front out path */ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, @@ -4004,13 +4065,17 @@ present = snd_hda_codec_read(codec, 0x0f, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; if (present) { - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 1); - snd_hda_codec_write(codec, 0x0f, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); + snd_hda_codec_write_cache(codec, 0x01, 0, + AC_VERB_SET_GPIO_DATA, 1); + snd_hda_codec_write_cache(codec, 0x0f, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + PIN_HP); } else { - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0); - snd_hda_codec_write(codec, 0x0f, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + snd_hda_codec_write_cache(codec, 0x01, 0, + AC_VERB_SET_GPIO_DATA, 0); + snd_hda_codec_write_cache(codec, 0x0f, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + PIN_OUT); } } @@ -4405,11 +4470,12 @@ * front panel mic (mic 2) */ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + /* mute analog inputs */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* * Set up output mixers (0x08 - 0x0a) @@ -4486,6 +4552,17 @@ alc260_auto_init_analog_input(codec); } +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list alc260_loopbacks[] = { + { 0x07, HDA_INPUT, 0 }, + { 0x07, HDA_INPUT, 1 }, + { 0x07, HDA_INPUT, 2 }, + { 0x07, HDA_INPUT, 3 }, + { 0x07, HDA_INPUT, 4 }, + { } /* end */ +}; +#endif + /* * ALC260 configurations */ @@ -4685,6 +4762,10 @@ codec->patch_ops = alc_patch_ops; if (board_config == ALC260_AUTO) spec->init_hook = alc260_auto_init; +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (!spec->loopback.amplist) + spec->loopback.amplist = alc260_loopbacks; +#endif return 0; } @@ -4747,12 +4828,13 @@ idx = ucontrol->value.enumerated.item[0]; if (idx >= imux->num_items) idx = imux->num_items - 1; - if (*cur_val == idx && !codec->in_resume) + if (*cur_val == idx) return 0; for (i = 0; i < imux->num_items; i++) { - unsigned int v = (i == idx) ? 0x7000 : 0x7080; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - v | (imux->items[i].index << 8)); + unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE; + snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, + imux->items[i].index, + HDA_AMP_MUTE, v); } *cur_val = idx; return 1; @@ -4814,6 +4896,38 @@ { 8, alc882_sixstack_ch8_init }, }; +/* + * macbook pro ALC885 can switch LineIn to LineOut without loosing Mic + */ + +/* + * 2ch mode + */ +static struct hda_verb alc885_mbp_ch2_init[] = { + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + { } /* end */ +}; + +/* + * 6ch mode + */ +static struct hda_verb alc885_mbp_ch6_init[] = { + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + { } /* end */ +}; + +static struct hda_channel_mode alc885_mbp_6ch_modes[2] = { + { 2, alc885_mbp_ch2_init }, + { 6, alc885_mbp_ch6_init }, +}; + + /* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b */ @@ -4844,6 +4958,19 @@ { } /* end */ }; +static struct snd_kcontrol_new alc885_mbp3_mixer[] = { + HDA_CODEC_VOLUME("Master Volume", 0x0c, 0x00, HDA_OUTPUT), + HDA_BIND_MUTE ("Master Switch", 0x0c, 0x02, HDA_INPUT), + HDA_CODEC_MUTE ("Speaker Switch", 0x14, 0x00, HDA_OUTPUT), + HDA_CODEC_VOLUME("Line Out Volume", 0x0d,0x00, HDA_OUTPUT), + HDA_CODEC_VOLUME("Line In Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE ("Line In Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x00, HDA_INPUT), + HDA_CODEC_MUTE ("Mic Playback Switch", 0x0b, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Line In Boost", 0x1a, 0x00, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x00, HDA_INPUT), + { } /* end */ +}; static struct snd_kcontrol_new alc882_w2jc_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), @@ -5054,6 +5181,137 @@ { } }; +/* Macbook Pro rev3 */ +static struct hda_verb alc885_mbp3_init_verbs[] = { + /* Front mixer: unmute input/output amp left and right (volume = 0) */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* Rear mixer */ + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* Front Pin: output 0 (0x0c) */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* HP Pin: output 0 (0x0d) */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc4}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + /* Mic (rear) pin: input vref at 80% */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Front Mic pin: input vref at 80% */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line In pin: use output 1 when in LineOut mode */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, + + /* FIXME: use matrix-type input source selection */ + /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ + /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* Input mixer2 */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* Input mixer3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + /* ADC1: mute amp left and right */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* ADC2: mute amp left and right */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* ADC3: mute amp left and right */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + + { } +}; + +/* iMac 24 mixer. */ +static struct snd_kcontrol_new alc885_imac24_mixer[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x00, HDA_OUTPUT), + HDA_CODEC_MUTE("Master Playback Switch", 0x0c, 0x00, HDA_INPUT), + { } /* end */ +}; + +/* iMac 24 init verbs. */ +static struct hda_verb alc885_imac24_init_verbs[] = { + /* Internal speakers: output 0 (0x0c) */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x18, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* Internal speakers: output 0 (0x0c) */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* Headphone: output 0 (0x0c) */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + /* Front Mic: input vref at 80% */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + { } +}; + +/* Toggle speaker-output according to the hp-jack state */ +static void alc885_imac24_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x14, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + snd_hda_codec_amp_stereo(codec, 0x18, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + snd_hda_codec_amp_stereo(codec, 0x1a, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); +} + +/* Processes unsolicited events. */ +static void alc885_imac24_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + /* Headphone insertion or removal. */ + if ((res >> 26) == ALC880_HP_EVENT) + alc885_imac24_automute(codec); +} + +static void alc885_mbp3_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x15, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? 0 : HDA_AMP_MUTE); + +} +static void alc885_mbp3_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + /* Headphone insertion or removal. */ + if ((res >> 26) == ALC880_HP_EVENT) + alc885_mbp3_automute(codec); +} + + static struct hda_verb alc882_targa_verbs[] = { {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, @@ -5079,11 +5337,10 @@ present = snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_amp_update(codec, 0x1b, 0, HDA_OUTPUT, 0, - 0x80, present ? 0x80 : 0); - snd_hda_codec_amp_update(codec, 0x1b, 1, HDA_OUTPUT, 0, - 0x80, present ? 0x80 : 0); - snd_hda_codec_write(codec, 1, 0, AC_VERB_SET_GPIO_DATA, present ? 1 : 3); + snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + snd_hda_codec_write_cache(codec, 1, 0, AC_VERB_SET_GPIO_DATA, + present ? 1 : 3); } static void alc882_targa_unsol_event(struct hda_codec *codec, unsigned int res) @@ -5146,6 +5403,20 @@ AC_VERB_SET_GPIO_DATA, gpiostate); } +/* set up GPIO at initialization */ +static void alc885_macpro_init_hook(struct hda_codec *codec) +{ + alc882_gpio_mute(codec, 0, 0); + alc882_gpio_mute(codec, 1, 0); +} + +/* set up GPIO and update auto-muting at initialization */ +static void alc885_imac24_init_hook(struct hda_codec *codec) +{ + alc885_macpro_init_hook(codec); + alc885_imac24_automute(codec); +} + /* * generic initialization of ADC, input mixers and output mixers */ @@ -5160,17 +5431,17 @@ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback * mixer widget * Note: PASD motherboards uses the Line In 2 as the input for * front panel mic (mic 2) */ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* * Set up output mixers (0x0c - 0x0f) @@ -5259,6 +5530,10 @@ { } /* end */ }; +#ifdef CONFIG_SND_HDA_POWER_SAVE +#define alc882_loopbacks alc880_loopbacks +#endif + /* pcm configuration: identiacal with ALC880 */ #define alc882_pcm_analog_playback alc880_pcm_analog_playback #define alc882_pcm_analog_capture alc880_pcm_analog_capture @@ -5274,6 +5549,8 @@ [ALC882_ARIMA] = "arima", [ALC882_W2JC] = "w2jc", [ALC885_MACPRO] = "macpro", + [ALC885_MBP3] = "mbp3", + [ALC885_IMAC24] = "imac24", [ALC882_AUTO] = "auto", }; @@ -5284,6 +5561,7 @@ SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8 */ SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA), SND_PCI_QUIRK(0x1043, 0x060d, "Asus A7J", ALC882_ASUS_A7J), + SND_PCI_QUIRK(0x1043, 0x817f, "Asus P5LD2", ALC882_6ST_DIG), SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG), SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC), {} @@ -5334,6 +5612,20 @@ .input_mux = &alc882_capture_source, .dig_out_nid = ALC882_DIGOUT_NID, }, + [ALC885_MBP3] = { + .mixers = { alc885_mbp3_mixer, alc882_chmode_mixer }, + .init_verbs = { alc885_mbp3_init_verbs, + alc880_gpio1_init_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .channel_mode = alc885_mbp_6ch_modes, + .num_channel_mode = ARRAY_SIZE(alc885_mbp_6ch_modes), + .input_mux = &alc882_capture_source, + .dig_out_nid = ALC882_DIGOUT_NID, + .dig_in_nid = ALC882_DIGIN_NID, + .unsol_event = alc885_mbp3_unsol_event, + .init_hook = alc885_mbp3_automute, + }, [ALC885_MACPRO] = { .mixers = { alc882_macpro_mixer }, .init_verbs = { alc882_macpro_init_verbs }, @@ -5344,6 +5636,20 @@ .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), .channel_mode = alc882_ch_modes, .input_mux = &alc882_capture_source, + .init_hook = alc885_macpro_init_hook, + }, + [ALC885_IMAC24] = { + .mixers = { alc885_imac24_mixer }, + .init_verbs = { alc885_imac24_init_verbs }, + .num_dacs = ARRAY_SIZE(alc882_dac_nids), + .dac_nids = alc882_dac_nids, + .dig_out_nid = ALC882_DIGOUT_NID, + .dig_in_nid = ALC882_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), + .channel_mode = alc882_ch_modes, + .input_mux = &alc882_capture_source, + .unsol_event = alc885_imac24_unsol_event, + .init_hook = alc885_imac24_init_hook, }, [ALC882_TARGA] = { .mixers = { alc882_targa_mixer, alc882_chmode_mixer, @@ -5379,6 +5685,29 @@ /* + * Pin config fixes + */ +enum { + PINFIX_ABIT_AW9D_MAX +}; + +static struct alc_pincfg alc882_abit_aw9d_pinfix[] = { + { 0x15, 0x01080104 }, /* side */ + { 0x16, 0x01011012 }, /* rear */ + { 0x17, 0x01016011 }, /* clfe */ + { } +}; + +static const struct alc_pincfg *alc882_pin_fixes[] = { + [PINFIX_ABIT_AW9D_MAX] = alc882_abit_aw9d_pinfix, +}; + +static struct snd_pci_quirk alc882_pinfix_tbl[] = { + SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX), + {} +}; + +/* * BIOS auto configuration */ static void alc882_auto_set_output_and_unmute(struct hda_codec *codec, @@ -5494,6 +5823,12 @@ case 0x106b0c00: /* Mac Pro */ board_config = ALC885_MACPRO; break; + case 0x106b1000: /* iMac 24 */ + board_config = ALC885_IMAC24; + break; + case 0x106b2c00: /* Macbook Pro rev3 */ + board_config = ALC885_MBP3; + break; default: printk(KERN_INFO "hda_codec: Unknown model for ALC882, " "trying auto-probe from BIOS...\n"); @@ -5501,6 +5836,8 @@ } } + alc_fix_pincfg(codec, alc882_pinfix_tbl, alc882_pin_fixes); + if (board_config == ALC882_AUTO) { /* automatic parse from the BIOS config */ err = alc882_parse_auto_config(codec); @@ -5518,11 +5855,6 @@ if (board_config != ALC882_AUTO) setup_preset(spec, &alc882_presets[board_config]); - if (board_config == ALC885_MACPRO) { - alc882_gpio_mute(codec, 0, 0); - alc882_gpio_mute(codec, 1, 0); - } - spec->stream_name_analog = "ALC882 Analog"; spec->stream_analog_playback = &alc882_pcm_analog_playback; spec->stream_analog_capture = &alc882_pcm_analog_capture; @@ -5553,6 +5885,10 @@ codec->patch_ops = alc_patch_ops; if (board_config == ALC882_AUTO) spec->init_hook = alc882_auto_init; +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (!spec->loopback.amplist) + spec->loopback.amplist = alc882_loopbacks; +#endif return 0; } @@ -5630,12 +5966,13 @@ idx = ucontrol->value.enumerated.item[0]; if (idx >= imux->num_items) idx = imux->num_items - 1; - if (*cur_val == idx && !codec->in_resume) + if (*cur_val == idx) return 0; for (i = 0; i < imux->num_items; i++) { - unsigned int v = (i == idx) ? 0x7000 : 0x7080; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - v | (imux->items[i].index << 8)); + unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE; + snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, + imux->items[i].index, + HDA_AMP_MUTE, v); } *cur_val = idx; return 1; @@ -5995,6 +6332,109 @@ { } /* end */ }; +static struct snd_kcontrol_new alc888_6st_hp_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = alc883_mux_enum_info, + .get = alc883_mux_enum_get, + .put = alc883_mux_enum_put, + }, + { } /* end */ +}; + +static struct snd_kcontrol_new alc888_3st_hp_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = alc883_mux_enum_info, + .get = alc883_mux_enum_get, + .put = alc883_mux_enum_put, + }, + { } /* end */ +}; + +static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = alc883_mux_enum_info, + .get = alc883_mux_enum_get, + .put = alc883_mux_enum_put, + }, + { } /* end */ +}; + static struct snd_kcontrol_new alc883_chmode_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -6030,11 +6470,12 @@ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + /* mute analog input loopbacks */ + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* Front Pin: output 0 (0x0c) */ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, @@ -6126,6 +6567,42 @@ { } /* end */ }; +static struct hda_verb alc888_6st_hp_verbs[] = { + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x02}, /* Rear : output 2 (0x0e) */ + {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* CLFE : output 1 (0x0d) */ + {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, /* Side : output 3 (0x0f) */ + { } +}; + +static struct hda_verb alc888_3st_hp_verbs[] = { + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */ + {0x18, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Rear : output 1 (0x0d) */ + {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, /* CLFE : output 2 (0x0e) */ + { } +}; + +static struct hda_verb alc888_3st_hp_2ch_init[] = { + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { } +}; + +static struct hda_verb alc888_3st_hp_6ch_init[] = { + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { } +}; + +static struct hda_channel_mode alc888_3st_hp_modes[2] = { + { 2, alc888_3st_hp_2ch_init }, + { 6, alc888_3st_hp_6ch_init }, +}; + /* toggle front-jack and RCA according to the hp-jack state */ static void alc888_lenovo_ms7195_front_automute(struct hda_codec *codec) { @@ -6133,15 +6610,10 @@ present = snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0, - 0x80, present ? 0x80 : 0); - snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0, - 0x80, present ? 0x80 : 0); - snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0, - 0x80, present ? 0x80 : 0); - snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0, - 0x80, present ? 0x80 : 0); - + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); } /* toggle RCA according to the front-jack state */ @@ -6151,12 +6623,10 @@ present = snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0, - 0x80, present ? 0x80 : 0); - snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0, - 0x80, present ? 0x80 : 0); - + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); } + static void alc883_lenovo_ms7195_unsol_event(struct hda_codec *codec, unsigned int res) { @@ -6183,10 +6653,8 @@ present = snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0, - 0x80, present ? 0x80 : 0); - snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0, - 0x80, present ? 0x80 : 0); + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); } static void alc883_medion_md2_unsol_event(struct hda_codec *codec, @@ -6204,13 +6672,11 @@ present = snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? 0x80 : 0; - snd_hda_codec_amp_update(codec, 0x1b, 0, HDA_OUTPUT, 0, - 0x80, bits); - snd_hda_codec_amp_update(codec, 0x1b, 1, HDA_OUTPUT, 0, - 0x80, bits); - snd_hda_codec_write(codec, 1, 0, AC_VERB_SET_GPIO_DATA, - present ? 1 : 3); + bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); + snd_hda_codec_write_cache(codec, 1, 0, AC_VERB_SET_GPIO_DATA, + present ? 1 : 3); } static void alc883_tagra_unsol_event(struct hda_codec *codec, unsigned int res) @@ -6226,11 +6692,9 @@ present = snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? 0x80 : 0; - snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0, - 0x80, bits); - snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0, - 0x80, bits); + bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); } static void alc883_lenovo_101e_all_automute(struct hda_codec *codec) @@ -6240,15 +6704,11 @@ present = snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? 0x80 : 0; - snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0, - 0x80, bits); - snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0, - 0x80, bits); - snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0, - 0x80, bits); - snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0, - 0x80, bits); + bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); } static void alc883_lenovo_101e_unsol_event(struct hda_codec *codec, @@ -6260,6 +6720,44 @@ alc883_lenovo_101e_ispeaker_automute(codec); } +/* toggle speaker-output according to the hp-jack state */ +static void alc883_acer_aspire_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_codec_read(codec, 0x14, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); +} + +static void alc883_acer_aspire_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) == ALC880_HP_EVENT) + alc883_acer_aspire_automute(codec); +} + +static struct hda_verb alc883_acer_eapd_verbs[] = { + /* HP Pin: output 0 (0x0c) */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* Front Pin: output 0 (0x0c) */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* eanable EAPD on medion laptop */ + {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, + {0x20, AC_VERB_SET_PROC_COEF, 0x3050}, + /* enable unsolicited event */ + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + { } +}; + /* * generic initialization of ADC, input mixers and output mixers */ @@ -6272,17 +6770,17 @@ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback * mixer widget * Note: PASD motherboards uses the Line In 2 as the input for * front panel mic (mic 2) */ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* * Set up output mixers (0x0c - 0x0f) @@ -6345,6 +6843,10 @@ { } /* end */ }; +#ifdef CONFIG_SND_HDA_POWER_SAVE +#define alc883_loopbacks alc880_loopbacks +#endif + /* pcm configuration: identiacal with ALC880 */ #define alc883_pcm_analog_playback alc880_pcm_analog_playback #define alc883_pcm_analog_capture alc880_pcm_analog_capture @@ -6362,17 +6864,21 @@ [ALC883_TARGA_DIG] = "targa-dig", [ALC883_TARGA_2ch_DIG] = "targa-2ch-dig", [ALC883_ACER] = "acer", + [ALC883_ACER_ASPIRE] = "acer-aspire", [ALC883_MEDION] = "medion", [ALC883_MEDION_MD2] = "medion-md2", [ALC883_LAPTOP_EAPD] = "laptop-eapd", [ALC883_LENOVO_101E_2ch] = "lenovo-101e", [ALC883_LENOVO_NB0763] = "lenovo-nb0763", [ALC888_LENOVO_MS7195_DIG] = "lenovo-ms7195-dig", + [ALC888_6ST_HP] = "6stack-hp", + [ALC888_3ST_HP] = "3stack-hp", [ALC883_AUTO] = "auto", }; static struct snd_pci_quirk alc883_cfg_tbl[] = { SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC883_3ST_6ch_DIG), + SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG), SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch), SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD), SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG), @@ -6381,6 +6887,8 @@ SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG), SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG), SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG), @@ -6388,10 +6896,13 @@ SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x3fc1, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x3fc3, "MSI", ALC883_TARGA_DIG), + SND_PCI_QUIRK(0x1462, 0x3fdf, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x4314, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x4319, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0x4324, "MSI", ALC883_TARGA_DIG), SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG), + SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE), + SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE), SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER), SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch), SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION), @@ -6400,6 +6911,9 @@ SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch), SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763), SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763), + SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC888_6ST_HP), + SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP), + SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP), SND_PCI_QUIRK(0x17c0, 0x4071, "MEDION MD2", ALC883_MEDION_MD2), {} }; @@ -6487,8 +7001,7 @@ .init_hook = alc883_tagra_automute, }, [ALC883_ACER] = { - .mixers = { alc883_base_mixer, - alc883_chmode_mixer }, + .mixers = { alc883_base_mixer }, /* On TravelMate laptops, GPIO 0 enables the internal speaker * and the headphone jack. Turn this on and rely on the * standard mute methods whenever the user wants to turn @@ -6503,6 +7016,20 @@ .channel_mode = alc883_3ST_2ch_modes, .input_mux = &alc883_capture_source, }, + [ALC883_ACER_ASPIRE] = { + .mixers = { alc883_acer_aspire_mixer }, + .init_verbs = { alc883_init_verbs, alc883_acer_eapd_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .adc_nids = alc883_adc_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + .unsol_event = alc883_acer_aspire_unsol_event, + .init_hook = alc883_acer_aspire_automute, + }, [ALC883_MEDION] = { .mixers = { alc883_fivestack_mixer, alc883_chmode_mixer }, @@ -6531,8 +7058,7 @@ .init_hook = alc883_medion_md2_automute, }, [ALC883_LAPTOP_EAPD] = { - .mixers = { alc883_base_mixer, - alc883_chmode_mixer }, + .mixers = { alc883_base_mixer }, .init_verbs = { alc883_init_verbs, alc882_eapd_verbs }, .num_dacs = ARRAY_SIZE(alc883_dac_nids), .dac_nids = alc883_dac_nids, @@ -6584,6 +7110,31 @@ .unsol_event = alc883_lenovo_ms7195_unsol_event, .init_hook = alc888_lenovo_ms7195_front_automute, }, + [ALC888_6ST_HP] = { + .mixers = { alc888_6st_hp_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc888_6st_hp_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .adc_nids = alc883_adc_nids, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), + .channel_mode = alc883_sixstack_modes, + .input_mux = &alc883_capture_source, + }, + [ALC888_3ST_HP] = { + .mixers = { alc888_3st_hp_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs, alc888_3st_hp_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .adc_nids = alc883_adc_nids, + .num_channel_mode = ARRAY_SIZE(alc888_3st_hp_modes), + .channel_mode = alc888_3st_hp_modes, + .need_dac_fix = 1, + .input_mux = &alc883_capture_source, + }, }; @@ -6737,6 +7288,10 @@ codec->patch_ops = alc_patch_ops; if (board_config == ALC883_AUTO) spec->init_hook = alc883_auto_init; +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (!spec->loopback.amplist) + spec->loopback.amplist = alc883_loopbacks; +#endif return 0; } @@ -6847,9 +7402,18 @@ { } /* end */ }; +static struct hda_bind_ctls alc262_sony_bind_sw = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), + 0, + }, +}; + static struct snd_kcontrol_new alc262_sony_mixer[] = { HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_BIND_SW("Front Playback Switch", &alc262_sony_bind_sw), HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), @@ -6857,7 +7421,16 @@ { } /* end */ }; - +static struct snd_kcontrol_new alc262_benq_t31_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + { } /* end */ +}; #define alc262_capture_mixer alc882_capture_mixer #define alc262_capture_alt_mixer alc882_capture_alt_mixer @@ -6876,17 +7449,17 @@ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback * mixer widget * Note: PASD motherboards uses the Line In 2 as the input for * front panel mic (mic 2) */ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* * Set up output mixers (0x0c - 0x0e) @@ -6967,34 +7540,26 @@ }; /* mute/unmute internal speaker according to the hp jack and mute state */ -static void alc262_hippo_automute(struct hda_codec *codec, int force) +static void alc262_hippo_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; unsigned int mute; + unsigned int present; - if (force || !spec->sense_updated) { - unsigned int present; - /* need to execute and sync at first */ - snd_hda_codec_read(codec, 0x15, 0, AC_VERB_SET_PIN_SENSE, 0); - present = snd_hda_codec_read(codec, 0x15, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = (present & 0x80000000) != 0; - spec->sense_updated = 1; - } + /* need to execute and sync at first */ + snd_hda_codec_read(codec, 0x15, 0, AC_VERB_SET_PIN_SENSE, 0); + present = snd_hda_codec_read(codec, 0x15, 0, + AC_VERB_GET_PIN_SENSE, 0); + spec->jack_present = (present & 0x80000000) != 0; if (spec->jack_present) { /* mute internal speaker */ - snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0, - 0x80, 0x80); - snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0, - 0x80, 0x80); + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, HDA_AMP_MUTE); } else { /* unmute internal speaker if necessary */ mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0); - snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0, - 0x80, mute & 0x80); - mute = snd_hda_codec_amp_read(codec, 0x15, 1, HDA_OUTPUT, 0); - snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0, - 0x80, mute & 0x80); + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute); } } @@ -7004,37 +7569,27 @@ { if ((res >> 26) != ALC880_HP_EVENT) return; - alc262_hippo_automute(codec, 1); + alc262_hippo_automute(codec); } -static void alc262_hippo1_automute(struct hda_codec *codec, int force) +static void alc262_hippo1_automute(struct hda_codec *codec) { - struct alc_spec *spec = codec->spec; unsigned int mute; + unsigned int present; - if (force || !spec->sense_updated) { - unsigned int present; - /* need to execute and sync at first */ - snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0); - present = snd_hda_codec_read(codec, 0x1b, 0, - AC_VERB_GET_PIN_SENSE, 0); - spec->jack_present = (present & 0x80000000) != 0; - spec->sense_updated = 1; - } - if (spec->jack_present) { + snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0); + present = snd_hda_codec_read(codec, 0x1b, 0, + AC_VERB_GET_PIN_SENSE, 0); + present = (present & 0x80000000) != 0; + if (present) { /* mute internal speaker */ - snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0, - 0x80, 0x80); - snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0, - 0x80, 0x80); + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, HDA_AMP_MUTE); } else { /* unmute internal speaker if necessary */ mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0); - snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0, - 0x80, mute & 0x80); - mute = snd_hda_codec_amp_read(codec, 0x1b, 1, HDA_OUTPUT, 0); - snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0, - 0x80, mute & 0x80); + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute); } } @@ -7044,7 +7599,7 @@ { if ((res >> 26) != ALC880_HP_EVENT) return; - alc262_hippo1_automute(codec, 1); + alc262_hippo1_automute(codec); } /* @@ -7096,18 +7651,13 @@ } if (spec->jack_present) { /* mute internal speaker */ - snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0, - 0x80, 0x80); - snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0, - 0x80, 0x80); + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, + HDA_AMP_MUTE, HDA_AMP_MUTE); } else { /* unmute internal speaker if necessary */ mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0); - snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0, - 0x80, mute & 0x80); - mute = snd_hda_codec_amp_read(codec, 0x14, 1, HDA_OUTPUT, 0); - snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0, - 0x80, mute & 0x80); + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, + HDA_AMP_MUTE, mute); } } @@ -7121,23 +7671,14 @@ } /* bind volumes of both NID 0x0c and 0x0d */ -static int alc262_fujitsu_master_vol_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - long *valp = ucontrol->value.integer.value; - int change; - - change = snd_hda_codec_amp_update(codec, 0x0c, 0, HDA_OUTPUT, 0, - 0x7f, valp[0] & 0x7f); - change |= snd_hda_codec_amp_update(codec, 0x0c, 1, HDA_OUTPUT, 0, - 0x7f, valp[1] & 0x7f); - snd_hda_codec_amp_update(codec, 0x0d, 0, HDA_OUTPUT, 0, - 0x7f, valp[0] & 0x7f); - snd_hda_codec_amp_update(codec, 0x0d, 1, HDA_OUTPUT, 0, - 0x7f, valp[1] & 0x7f); - return change; -} +static struct hda_bind_ctls alc262_fujitsu_bind_master_vol = { + .ops = &snd_hda_bind_vol, + .values = { + HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x0d, 3, 0, HDA_OUTPUT), + 0 + }, +}; /* bind hp and internal speaker mute (with plug check) */ static int alc262_fujitsu_master_sw_put(struct snd_kcontrol *kcontrol, @@ -7148,24 +7689,18 @@ int change; change = snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0, - 0x80, valp[0] ? 0 : 0x80); + HDA_AMP_MUTE, + valp[0] ? 0 : HDA_AMP_MUTE); change |= snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0, - 0x80, valp[1] ? 0 : 0x80); - if (change || codec->in_resume) - alc262_fujitsu_automute(codec, codec->in_resume); + HDA_AMP_MUTE, + valp[1] ? 0 : HDA_AMP_MUTE); + if (change) + alc262_fujitsu_automute(codec, 0); return change; } static struct snd_kcontrol_new alc262_fujitsu_mixer[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Volume", - .info = snd_hda_mixer_amp_volume_info, - .get = snd_hda_mixer_amp_volume_get, - .put = alc262_fujitsu_master_vol_put, - .tlv = { .c = snd_hda_mixer_amp_tlv }, - .private_value = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT), - }, + HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", @@ -7189,6 +7724,15 @@ {} }; +static struct hda_verb alc262_benq_t31_EAPD_verbs[] = { + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + + {0x20, AC_VERB_SET_COEF_INDEX, 0x07}, + {0x20, AC_VERB_SET_PROC_COEF, 0x3050}, + {} +}; + /* add playback controls from the parsed DAC table */ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg) @@ -7284,17 +7828,17 @@ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback * mixer widget * Note: PASD motherboards uses the Line In 2 as the input for * front panel mic (mic 2) */ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* * Set up output mixers (0x0c - 0x0f) @@ -7345,19 +7889,19 @@ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback * mixer widget * Note: PASD motherboards uses the Line In 2 as the input for * front panel mic (mic 2) */ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(6)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, /* * Set up output mixers (0x0c - 0x0e) @@ -7432,20 +7976,20 @@ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback * mixer widget * Note: PASD motherboards uses the Line In 2 as the input for front * panel mic (mic 2) */ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(6)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(7)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)}, /* * Set up output mixers (0x0c - 0x0e) */ @@ -7515,6 +8059,10 @@ { } }; +#ifdef CONFIG_SND_HDA_POWER_SAVE +#define alc262_loopbacks alc880_loopbacks +#endif + /* pcm configuration: identiacal with ALC880 */ #define alc262_pcm_analog_playback alc880_pcm_analog_playback #define alc262_pcm_analog_capture alc880_pcm_analog_capture @@ -7584,7 +8132,8 @@ [ALC262_HP_BPC] = "hp-bpc", [ALC262_HP_BPC_D7000_WL]= "hp-bpc-d7000", [ALC262_BENQ_ED8] = "benq", - [ALC262_BENQ_ED8] = "sony-assamd", + [ALC262_BENQ_T31] = "benq-t31", + [ALC262_SONY_ASSAMD] = "sony-assamd", [ALC262_AUTO] = "auto", }; @@ -7592,8 +8141,12 @@ SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO), SND_PCI_QUIRK(0x103c, 0x12fe, "HP xw9400", ALC262_HP_BPC), SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x12ff, "HP xw4550", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x1308, "HP xw4600", ALC262_HP_BPC), SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x1307, "HP xw6600", ALC262_HP_BPC), SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC), + SND_PCI_QUIRK(0x103c, 0x1306, "HP xw8600", ALC262_HP_BPC), SND_PCI_QUIRK(0x103c, 0x2800, "HP D7000", ALC262_HP_BPC_D7000_WL), SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL), SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL), @@ -7606,150 +8159,781 @@ SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU), SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1), SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8), + SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_BENQ_T31), + SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD), SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD), SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD), SND_PCI_QUIRK(0x104d, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD), {} }; -static struct alc_config_preset alc262_presets[] = { - [ALC262_BASIC] = { - .mixers = { alc262_base_mixer }, - .init_verbs = { alc262_init_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, +static struct alc_config_preset alc262_presets[] = { + [ALC262_BASIC] = { + .mixers = { alc262_base_mixer }, + .init_verbs = { alc262_init_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + }, + [ALC262_HIPPO] = { + .mixers = { alc262_base_mixer }, + .init_verbs = { alc262_init_verbs, alc262_hippo_unsol_verbs}, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .dig_out_nid = ALC262_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + .unsol_event = alc262_hippo_unsol_event, + .init_hook = alc262_hippo_automute, + }, + [ALC262_HIPPO_1] = { + .mixers = { alc262_hippo1_mixer }, + .init_verbs = { alc262_init_verbs, alc262_hippo1_unsol_verbs}, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x02, + .dig_out_nid = ALC262_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + .unsol_event = alc262_hippo1_unsol_event, + .init_hook = alc262_hippo1_automute, + }, + [ALC262_FUJITSU] = { + .mixers = { alc262_fujitsu_mixer }, + .init_verbs = { alc262_init_verbs, alc262_fujitsu_unsol_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .dig_out_nid = ALC262_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_fujitsu_capture_source, + .unsol_event = alc262_fujitsu_unsol_event, + }, + [ALC262_HP_BPC] = { + .mixers = { alc262_HP_BPC_mixer }, + .init_verbs = { alc262_HP_BPC_init_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_HP_capture_source, + }, + [ALC262_HP_BPC_D7000_WF] = { + .mixers = { alc262_HP_BPC_WildWest_mixer }, + .init_verbs = { alc262_HP_BPC_WildWest_init_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_HP_capture_source, + }, + [ALC262_HP_BPC_D7000_WL] = { + .mixers = { alc262_HP_BPC_WildWest_mixer, + alc262_HP_BPC_WildWest_option_mixer }, + .init_verbs = { alc262_HP_BPC_WildWest_init_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_HP_capture_source, + }, + [ALC262_BENQ_ED8] = { + .mixers = { alc262_base_mixer }, + .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + }, + [ALC262_SONY_ASSAMD] = { + .mixers = { alc262_sony_mixer }, + .init_verbs = { alc262_init_verbs, alc262_sony_unsol_verbs}, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x02, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + .unsol_event = alc262_hippo_unsol_event, + .init_hook = alc262_hippo_automute, + }, + [ALC262_BENQ_T31] = { + .mixers = { alc262_benq_t31_mixer }, + .init_verbs = { alc262_init_verbs, alc262_benq_t31_EAPD_verbs, alc262_hippo_unsol_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_capture_source, + .unsol_event = alc262_hippo_unsol_event, + .init_hook = alc262_hippo_automute, + }, +}; + +static int patch_alc262(struct hda_codec *codec) +{ + struct alc_spec *spec; + int board_config; + int err; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; +#if 0 + /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is + * under-run + */ + { + int tmp; + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7); + tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0); + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7); + snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PROC_COEF, tmp | 0x80); + } +#endif + + board_config = snd_hda_check_board_config(codec, ALC262_MODEL_LAST, + alc262_models, + alc262_cfg_tbl); + + if (board_config < 0) { + printk(KERN_INFO "hda_codec: Unknown model for ALC262, " + "trying auto-probe from BIOS...\n"); + board_config = ALC262_AUTO; + } + + if (board_config == ALC262_AUTO) { + /* automatic parse from the BIOS config */ + err = alc262_parse_auto_config(codec); + if (err < 0) { + alc_free(codec); + return err; + } else if (!err) { + printk(KERN_INFO + "hda_codec: Cannot set up configuration " + "from BIOS. Using base mode...\n"); + board_config = ALC262_BASIC; + } + } + + if (board_config != ALC262_AUTO) + setup_preset(spec, &alc262_presets[board_config]); + + spec->stream_name_analog = "ALC262 Analog"; + spec->stream_analog_playback = &alc262_pcm_analog_playback; + spec->stream_analog_capture = &alc262_pcm_analog_capture; + + spec->stream_name_digital = "ALC262 Digital"; + spec->stream_digital_playback = &alc262_pcm_digital_playback; + spec->stream_digital_capture = &alc262_pcm_digital_capture; + + if (!spec->adc_nids && spec->input_mux) { + /* check whether NID 0x07 is valid */ + unsigned int wcap = get_wcaps(codec, 0x07); + + /* get type */ + wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; + if (wcap != AC_WID_AUD_IN) { + spec->adc_nids = alc262_adc_nids_alt; + spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids_alt); + spec->mixers[spec->num_mixers] = + alc262_capture_alt_mixer; + spec->num_mixers++; + } else { + spec->adc_nids = alc262_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids); + spec->mixers[spec->num_mixers] = alc262_capture_mixer; + spec->num_mixers++; + } + } + + codec->patch_ops = alc_patch_ops; + if (board_config == ALC262_AUTO) + spec->init_hook = alc262_auto_init; +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (!spec->loopback.amplist) + spec->loopback.amplist = alc262_loopbacks; +#endif + + return 0; +} + +/* + * ALC268 channel source setting (2 channel) + */ +#define ALC268_DIGOUT_NID ALC880_DIGOUT_NID +#define alc268_modes alc260_modes + +static hda_nid_t alc268_dac_nids[2] = { + /* front, hp */ + 0x02, 0x03 +}; + +static hda_nid_t alc268_adc_nids[2] = { + /* ADC0-1 */ + 0x08, 0x07 +}; + +static hda_nid_t alc268_adc_nids_alt[1] = { + /* ADC0 */ + 0x08 +}; + +static struct snd_kcontrol_new alc268_base_mixer[] = { + /* output mixer control */ + HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), + { } +}; + +static struct hda_verb alc268_eapd_verbs[] = { + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, + {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, + { } +}; + +/* Toshiba specific */ +#define alc268_toshiba_automute alc262_hippo_automute + +static struct hda_verb alc268_toshiba_verbs[] = { + {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + { } /* end */ +}; + +/* Acer specific */ +/* bind volumes of both NID 0x0c and 0x0d */ +static struct hda_bind_ctls alc268_acer_bind_master_vol = { + .ops = &snd_hda_bind_vol, + .values = { + HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x03, 3, 0, HDA_OUTPUT), + 0 + }, +}; + +#define alc268_acer_master_sw_put alc262_fujitsu_master_sw_put +#define alc268_acer_automute alc262_fujitsu_automute + +static struct snd_kcontrol_new alc268_acer_mixer[] = { + /* output mixer control */ + HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = snd_hda_mixer_amp_switch_info, + .get = snd_hda_mixer_amp_switch_get, + .put = alc268_acer_master_sw_put, + .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), + }, + { } +}; + +static struct hda_verb alc268_acer_verbs[] = { + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, + { } +}; + +/* unsolicited event for HP jack sensing */ +static void alc268_toshiba_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 28) != ALC880_HP_EVENT) + return; + alc268_toshiba_automute(codec); +} + +static void alc268_acer_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 28) != ALC880_HP_EVENT) + return; + alc268_acer_automute(codec, 1); +} + +/* + * generic initialization of ADC, input mixers and output mixers + */ +static struct hda_verb alc268_base_init_verbs[] = { + /* Unmute DAC0-1 and set vol = 0 */ + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + /* + * Set up output mixers (0x0c - 0x0e) + */ + /* set vol=0 to output mixers */ + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0e, AC_VERB_SET_CONNECT_SEL, 0x00}, + + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + + /* FIXME: use matrix-type input source selection */ + /* Mixer elements: 0x18, 19, 1a, 1c, 14, 15, 0b */ + /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ + /* Input mixer2 */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, + + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, + { } +}; + +/* + * generic initialization of ADC, input mixers and output mixers + */ +static struct hda_verb alc268_volume_init_verbs[] = { + /* set output DAC */ + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + + /* set PCBEEP vol = 0 */ + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, (0xb000 | (0x00 << 8))}, + + { } +}; + +#define alc268_mux_enum_info alc_mux_enum_info +#define alc268_mux_enum_get alc_mux_enum_get + +static int alc268_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + const struct hda_input_mux *imux = spec->input_mux; + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + static hda_nid_t capture_mixers[3] = { 0x23, 0x24 }; + hda_nid_t nid = capture_mixers[adc_idx]; + unsigned int *cur_val = &spec->cur_mux[adc_idx]; + unsigned int i, idx; + + idx = ucontrol->value.enumerated.item[0]; + if (idx >= imux->num_items) + idx = imux->num_items - 1; + if (*cur_val == idx) + return 0; + for (i = 0; i < imux->num_items; i++) { + unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE; + snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, + imux->items[i].index, + HDA_AMP_MUTE, v); + snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_CONNECT_SEL, + idx ); + } + *cur_val = idx; + return 1; +} + +static struct snd_kcontrol_new alc268_capture_alt_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + * FIXME: the controls appear in the "playback" view! + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 1, + .info = alc268_mux_enum_info, + .get = alc268_mux_enum_get, + .put = alc268_mux_enum_put, + }, + { } /* end */ +}; + +static struct snd_kcontrol_new alc268_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x24, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x24, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + * FIXME: the controls appear in the "playback" view! + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = alc268_mux_enum_info, + .get = alc268_mux_enum_get, + .put = alc268_mux_enum_put, + }, + { } /* end */ +}; + +static struct hda_input_mux alc268_capture_source = { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x1 }, + { "Line", 0x2 }, + { "CD", 0x3 }, + }, +}; + +/* create input playback/capture controls for the given pin */ +static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid, + const char *ctlname, int idx) +{ + char name[32]; + int err; + + sprintf(name, "%s Playback Volume", ctlname); + if (nid == 0x14) { + err = add_control(spec, ALC_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(0x02, 3, idx, + HDA_OUTPUT)); + if (err < 0) + return err; + } else if (nid == 0x15) { + err = add_control(spec, ALC_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(0x03, 3, idx, + HDA_OUTPUT)); + if (err < 0) + return err; + } else + return -1; + sprintf(name, "%s Playback Switch", ctlname); + err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT)); + if (err < 0) + return err; + return 0; +} + +/* add playback controls from the parsed DAC table */ +static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec, + const struct auto_pin_cfg *cfg) +{ + hda_nid_t nid; + int err; + + spec->multiout.num_dacs = 2; /* only use one dac */ + spec->multiout.dac_nids = spec->private_dac_nids; + spec->multiout.dac_nids[0] = 2; + spec->multiout.dac_nids[1] = 3; + + nid = cfg->line_out_pins[0]; + if (nid) + alc268_new_analog_output(spec, nid, "Front", 0); + + nid = cfg->speaker_pins[0]; + if (nid == 0x1d) { + err = add_control(spec, ALC_CTL_WIDGET_VOL, + "Speaker Playback Volume", + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT)); + if (err < 0) + return err; + } + nid = cfg->hp_pins[0]; + if (nid) + alc268_new_analog_output(spec, nid, "Headphone", 0); + + nid = cfg->line_out_pins[1] | cfg->line_out_pins[2]; + if (nid == 0x16) { + err = add_control(spec, ALC_CTL_WIDGET_MUTE, + "Mono Playback Switch", + HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_INPUT)); + if (err < 0) + return err; + } + return 0; +} + +/* create playback/capture controls for input pins */ +static int alc268_auto_create_analog_input_ctls(struct alc_spec *spec, + const struct auto_pin_cfg *cfg) +{ + struct hda_input_mux *imux = &spec->private_imux; + int i, idx1; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + switch(cfg->input_pins[i]) { + case 0x18: + idx1 = 0; /* Mic 1 */ + break; + case 0x19: + idx1 = 1; /* Mic 2 */ + break; + case 0x1a: + idx1 = 2; /* Line In */ + break; + case 0x1c: + idx1 = 3; /* CD */ + break; + default: + continue; + } + imux->items[imux->num_items].label = auto_pin_cfg_labels[i]; + imux->items[imux->num_items].index = idx1; + imux->num_items++; + } + return 0; +} + +static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t speaker_nid = spec->autocfg.speaker_pins[0]; + hda_nid_t hp_nid = spec->autocfg.hp_pins[0]; + hda_nid_t line_nid = spec->autocfg.line_out_pins[0]; + unsigned int dac_vol1, dac_vol2; + + if (speaker_nid) { + snd_hda_codec_write(codec, speaker_nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); + snd_hda_codec_write(codec, 0x0f, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(1)); + snd_hda_codec_write(codec, 0x10, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(1)); + } else { + snd_hda_codec_write(codec, 0x0f, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)); + snd_hda_codec_write(codec, 0x10, 0, + AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)); + } + + dac_vol1 = dac_vol2 = 0xb000 | 0x40; /* set max volume */ + if (line_nid == 0x14) + dac_vol2 = AMP_OUT_ZERO; + else if (line_nid == 0x15) + dac_vol1 = AMP_OUT_ZERO; + if (hp_nid == 0x14) + dac_vol2 = AMP_OUT_ZERO; + else if (hp_nid == 0x15) + dac_vol1 = AMP_OUT_ZERO; + if (line_nid != 0x16 || hp_nid != 0x16 || + spec->autocfg.line_out_pins[1] != 0x16 || + spec->autocfg.line_out_pins[2] != 0x16) + dac_vol1 = dac_vol2 = AMP_OUT_ZERO; + + snd_hda_codec_write(codec, 0x02, 0, + AC_VERB_SET_AMP_GAIN_MUTE, dac_vol1); + snd_hda_codec_write(codec, 0x03, 0, + AC_VERB_SET_AMP_GAIN_MUTE, dac_vol2); +} + +/* pcm configuration: identiacal with ALC880 */ +#define alc268_pcm_analog_playback alc880_pcm_analog_playback +#define alc268_pcm_analog_capture alc880_pcm_analog_capture +#define alc268_pcm_digital_playback alc880_pcm_digital_playback + +/* + * BIOS auto configuration + */ +static int alc268_parse_auto_config(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int err; + static hda_nid_t alc268_ignore[] = { 0 }; + + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, + alc268_ignore); + if (err < 0) + return err; + if (!spec->autocfg.line_outs) + return 0; /* can't find valid BIOS pin config */ + + err = alc268_auto_create_multi_out_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + err = alc268_auto_create_analog_input_ctls(spec, &spec->autocfg); + if (err < 0) + return err; + + spec->multiout.max_channels = 2; + + /* digital only support output */ + if (spec->autocfg.dig_out_pin) + spec->multiout.dig_out_nid = ALC268_DIGOUT_NID; + + if (spec->kctl_alloc) + spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + + spec->init_verbs[spec->num_init_verbs++] = alc268_volume_init_verbs; + spec->num_mux_defs = 1; + spec->input_mux = &spec->private_imux; + + return 1; +} + +#define alc268_auto_init_multi_out alc882_auto_init_multi_out +#define alc268_auto_init_hp_out alc882_auto_init_hp_out +#define alc268_auto_init_analog_input alc882_auto_init_analog_input + +/* init callback for auto-configuration model -- overriding the default init */ +static void alc268_auto_init(struct hda_codec *codec) +{ + alc268_auto_init_multi_out(codec); + alc268_auto_init_hp_out(codec); + alc268_auto_init_mono_speaker_out(codec); + alc268_auto_init_analog_input(codec); +} + +/* + * configuration and preset + */ +static const char *alc268_models[ALC268_MODEL_LAST] = { + [ALC268_3ST] = "3stack", + [ALC268_TOSHIBA] = "toshiba", + [ALC268_ACER] = "acer", + [ALC268_AUTO] = "auto", +}; + +static struct snd_pci_quirk alc268_cfg_tbl[] = { + SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST), + SND_PCI_QUIRK(0x1179, 0xff10, "TOSHIBA A205", ALC268_TOSHIBA), + SND_PCI_QUIRK(0x103c, 0x30cc, "TOSHIBA", ALC268_TOSHIBA), + SND_PCI_QUIRK(0x1025, 0x0126, "Acer", ALC268_ACER), + {} +}; + +static struct alc_config_preset alc268_presets[] = { + [ALC268_3ST] = { + .mixers = { alc268_base_mixer, alc268_capture_alt_mixer }, + .init_verbs = { alc268_base_init_verbs }, + .num_dacs = ARRAY_SIZE(alc268_dac_nids), + .dac_nids = alc268_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), + .adc_nids = alc268_adc_nids_alt, .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_capture_source, - }, - [ALC262_HIPPO] = { - .mixers = { alc262_base_mixer }, - .init_verbs = { alc262_init_verbs, alc262_hippo_unsol_verbs}, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, + .dig_out_nid = ALC268_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .input_mux = &alc268_capture_source, + }, + [ALC268_TOSHIBA] = { + .mixers = { alc268_base_mixer, alc268_capture_alt_mixer }, + .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, + alc268_toshiba_verbs }, + .num_dacs = ARRAY_SIZE(alc268_dac_nids), + .dac_nids = alc268_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), + .adc_nids = alc268_adc_nids_alt, .hp_nid = 0x03, - .dig_out_nid = ALC262_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_capture_source, - .unsol_event = alc262_hippo_unsol_event, - }, - [ALC262_HIPPO_1] = { - .mixers = { alc262_hippo1_mixer }, - .init_verbs = { alc262_init_verbs, alc262_hippo1_unsol_verbs}, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .input_mux = &alc268_capture_source, + .input_mux = &alc268_capture_source, + .unsol_event = alc268_toshiba_unsol_event, + .init_hook = alc268_toshiba_automute, + }, + [ALC268_ACER] = { + .mixers = { alc268_acer_mixer, alc268_capture_alt_mixer }, + .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, + alc268_acer_verbs }, + .num_dacs = ARRAY_SIZE(alc268_dac_nids), + .dac_nids = alc268_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), + .adc_nids = alc268_adc_nids_alt, .hp_nid = 0x02, - .dig_out_nid = ALC262_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_capture_source, - .unsol_event = alc262_hippo1_unsol_event, - }, - [ALC262_FUJITSU] = { - .mixers = { alc262_fujitsu_mixer }, - .init_verbs = { alc262_init_verbs, alc262_fujitsu_unsol_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .dig_out_nid = ALC262_DIGOUT_NID, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_fujitsu_capture_source, - .unsol_event = alc262_fujitsu_unsol_event, - }, - [ALC262_HP_BPC] = { - .mixers = { alc262_HP_BPC_mixer }, - .init_verbs = { alc262_HP_BPC_init_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_HP_capture_source, - }, - [ALC262_HP_BPC_D7000_WF] = { - .mixers = { alc262_HP_BPC_WildWest_mixer }, - .init_verbs = { alc262_HP_BPC_WildWest_init_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_HP_capture_source, - }, - [ALC262_HP_BPC_D7000_WL] = { - .mixers = { alc262_HP_BPC_WildWest_mixer, - alc262_HP_BPC_WildWest_option_mixer }, - .init_verbs = { alc262_HP_BPC_WildWest_init_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_HP_capture_source, - }, - [ALC262_BENQ_ED8] = { - .mixers = { alc262_base_mixer }, - .init_verbs = { alc262_init_verbs, alc262_EAPD_verbs }, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x03, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_capture_source, + .num_channel_mode = ARRAY_SIZE(alc268_modes), + .channel_mode = alc268_modes, + .input_mux = &alc268_capture_source, + .unsol_event = alc268_acer_unsol_event, }, - [ALC262_SONY_ASSAMD] = { - .mixers = { alc262_sony_mixer }, - .init_verbs = { alc262_init_verbs, alc262_sony_unsol_verbs}, - .num_dacs = ARRAY_SIZE(alc262_dac_nids), - .dac_nids = alc262_dac_nids, - .hp_nid = 0x02, - .num_channel_mode = ARRAY_SIZE(alc262_modes), - .channel_mode = alc262_modes, - .input_mux = &alc262_capture_source, - .unsol_event = alc262_hippo_unsol_event, - }, }; -static int patch_alc262(struct hda_codec *codec) +static int patch_alc268(struct hda_codec *codec) { struct alc_spec *spec; int board_config; int err; - spec = kzalloc(sizeof(*spec), GFP_KERNEL); + spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); if (spec == NULL) return -ENOMEM; codec->spec = spec; -#if 0 - /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is - * under-run - */ - { - int tmp; - snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7); - tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0); - snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7); - snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PROC_COEF, tmp | 0x80); - } -#endif - board_config = snd_hda_check_board_config(codec, ALC262_MODEL_LAST, - alc262_models, - alc262_cfg_tbl); + board_config = snd_hda_check_board_config(codec, ALC268_MODEL_LAST, + alc268_models, + alc268_cfg_tbl); - if (board_config < 0) { - printk(KERN_INFO "hda_codec: Unknown model for ALC262, " + if (board_config < 0 || board_config >= ALC268_MODEL_LAST) { + printk(KERN_INFO "hda_codec: Unknown model for ALC268, " "trying auto-probe from BIOS...\n"); - board_config = ALC262_AUTO; + board_config = ALC268_AUTO; } - if (board_config == ALC262_AUTO) { + if (board_config == ALC268_AUTO) { /* automatic parse from the BIOS config */ - err = alc262_parse_auto_config(codec); + err = alc268_parse_auto_config(codec); if (err < 0) { alc_free(codec); return err; @@ -7757,44 +8941,47 @@ printk(KERN_INFO "hda_codec: Cannot set up configuration " "from BIOS. Using base mode...\n"); - board_config = ALC262_BASIC; + board_config = ALC268_3ST; } } - if (board_config != ALC262_AUTO) - setup_preset(spec, &alc262_presets[board_config]); - - spec->stream_name_analog = "ALC262 Analog"; - spec->stream_analog_playback = &alc262_pcm_analog_playback; - spec->stream_analog_capture = &alc262_pcm_analog_capture; - - spec->stream_name_digital = "ALC262 Digital"; - spec->stream_digital_playback = &alc262_pcm_digital_playback; - spec->stream_digital_capture = &alc262_pcm_digital_capture; - - if (!spec->adc_nids && spec->input_mux) { - /* check whether NID 0x07 is valid */ - unsigned int wcap = get_wcaps(codec, 0x07); + if (board_config != ALC268_AUTO) + setup_preset(spec, &alc268_presets[board_config]); - /* get type */ - wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; - if (wcap != AC_WID_AUD_IN) { - spec->adc_nids = alc262_adc_nids_alt; - spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids_alt); - spec->mixers[spec->num_mixers] = - alc262_capture_alt_mixer; - spec->num_mixers++; - } else { - spec->adc_nids = alc262_adc_nids; - spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids); - spec->mixers[spec->num_mixers] = alc262_capture_mixer; - spec->num_mixers++; + spec->stream_name_analog = "ALC268 Analog"; + spec->stream_analog_playback = &alc268_pcm_analog_playback; + spec->stream_analog_capture = &alc268_pcm_analog_capture; + + spec->stream_name_digital = "ALC268 Digital"; + spec->stream_digital_playback = &alc268_pcm_digital_playback; + + if (board_config == ALC268_AUTO) { + if (!spec->adc_nids && spec->input_mux) { + /* check whether NID 0x07 is valid */ + unsigned int wcap = get_wcaps(codec, 0x07); + + /* get type */ + wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; + if (wcap != AC_WID_AUD_IN) { + spec->adc_nids = alc268_adc_nids_alt; + spec->num_adc_nids = + ARRAY_SIZE(alc268_adc_nids_alt); + spec->mixers[spec->num_mixers] = + alc268_capture_alt_mixer; + spec->num_mixers++; + } else { + spec->adc_nids = alc268_adc_nids; + spec->num_adc_nids = + ARRAY_SIZE(alc268_adc_nids); + spec->mixers[spec->num_mixers] = + alc268_capture_mixer; + spec->num_mixers++; + } } } - codec->patch_ops = alc_patch_ops; - if (board_config == ALC262_AUTO) - spec->init_hook = alc262_auto_init; + if (board_config == ALC268_AUTO) + spec->init_hook = alc268_auto_init; return 0; } @@ -8426,14 +9613,10 @@ present = snd_hda_codec_read(codec, 0x0f, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_amp_update(codec, 0x16, 0, HDA_INPUT, 0, - 0x80, present ? 0x80 : 0); - snd_hda_codec_amp_update(codec, 0x16, 1, HDA_INPUT, 0, - 0x80, present ? 0x80 : 0); - snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_INPUT, 3, - 0x80, present ? 0 : 0x80); - snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_INPUT, 3, - 0x80, present ? 0 : 0x80); + snd_hda_codec_amp_stereo(codec, 0x16, HDA_INPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + snd_hda_codec_amp_stereo(codec, 0x1a, HDA_INPUT, 3, + HDA_AMP_MUTE, present ? 0 : HDA_AMP_MUTE); } static void alc861_toshiba_unsol_event(struct hda_codec *codec, @@ -8746,6 +9929,16 @@ alc861_auto_init_analog_input(codec); } +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list alc861_loopbacks[] = { + { 0x15, HDA_INPUT, 0 }, + { 0x15, HDA_INPUT, 1 }, + { 0x15, HDA_INPUT, 2 }, + { 0x15, HDA_INPUT, 3 }, + { } /* end */ +}; +#endif + /* * configuration and preset @@ -8767,13 +9960,21 @@ SND_PCI_QUIRK(0x1043, 0x1335, "ASUS F2/3", ALC861_ASUS_LAPTOP), SND_PCI_QUIRK(0x1043, 0x1338, "ASUS F2/3", ALC861_ASUS_LAPTOP), SND_PCI_QUIRK(0x1043, 0x13d7, "ASUS A9rp", ALC861_ASUS_LAPTOP), + SND_PCI_QUIRK(0x1584, 0x9075, "Airis Praxis N1212", ALC861_ASUS_LAPTOP), SND_PCI_QUIRK(0x1043, 0x1393, "ASUS", ALC861_ASUS), + SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS P1-AH2", ALC861_3ST_DIG), SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba", ALC861_TOSHIBA), - SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA), + /* FIXME: the entry below breaks Toshiba A100 (model=auto works!) + * Any other models that need this preset? + */ + /* SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA), */ SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31), + SND_PCI_QUIRK(0x1584, 0x9075, "Uniwill", ALC861_UNIWILL_M31), SND_PCI_QUIRK(0x1584, 0x2b01, "Uniwill X40AIx", ALC861_UNIWILL_M31), SND_PCI_QUIRK(0x1849, 0x0660, "Asrock 939SLI32", ALC660_3ST), SND_PCI_QUIRK(0x8086, 0xd600, "Intel", ALC861_3ST), + SND_PCI_QUIRK(0x1462, 0x7254, "HP dx2200 (MSI MS-7254)", ALC861_3ST), + SND_PCI_QUIRK(0x1462, 0x7297, "HP dx2250 (MSI MS-7297)", ALC861_3ST), {} }; @@ -8935,6 +10136,10 @@ codec->patch_ops = alc_patch_ops; if (board_config == ALC861_AUTO) spec->init_hook = alc861_auto_init; +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (!spec->loopback.amplist) + spec->loopback.amplist = alc861_loopbacks; +#endif return 0; } @@ -8991,6 +10196,14 @@ }, }; +static struct hda_input_mux alc861vd_hp_capture_source = { + .num_items = 2, + .items = { + { "Front Mic", 0x0 }, + { "ATAPI Mic", 0x1 }, + }, +}; + #define alc861vd_mux_enum_info alc_mux_enum_info #define alc861vd_mux_enum_get alc_mux_enum_get @@ -9009,12 +10222,13 @@ idx = ucontrol->value.enumerated.item[0]; if (idx >= imux->num_items) idx = imux->num_items - 1; - if (*cur_val == idx && !codec->in_resume) + if (*cur_val == idx) return 0; for (i = 0; i < imux->num_items; i++) { - unsigned int v = (i == idx) ? 0x7000 : 0x7080; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - v | (imux->items[i].index << 8)); + unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE; + snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, + imux->items[i].index, + HDA_AMP_MUTE, v); } *cur_val = idx; return 1; @@ -9188,17 +10402,22 @@ HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x05, HDA_INPUT), HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 1, - .info = alc882_mux_enum_info, - .get = alc882_mux_enum_get, - .put = alc882_mux_enum_put, - }, + { } /* end */ +}; + +/* Pin assignment: Speaker=0x14, Line-out = 0x15, + * Front Mic=0x18, ATAPI Mic = 0x19, + */ +static struct snd_kcontrol_new alc861vd_hp_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + { } /* end */ }; @@ -9216,11 +10435,11 @@ * the analog-loopback mixer widget */ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* Capture mixer: unmute Mic, F-Mic, Line, CD inputs */ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, @@ -9349,11 +10568,9 @@ present = snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? 0x80 : 0; - snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0, - 0x80, bits); - snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0, - 0x80, bits); + bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); } static void alc861vd_lenovo_mic_automute(struct hda_codec *codec) @@ -9363,11 +10580,9 @@ present = snd_hda_codec_read(codec, 0x18, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? 0x80 : 0; - snd_hda_codec_amp_update(codec, 0x0b, 0, HDA_INPUT, 1, - 0x80, bits); - snd_hda_codec_amp_update(codec, 0x0b, 1, HDA_INPUT, 1, - 0x80, bits); + bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, + HDA_AMP_MUTE, bits); } static void alc861vd_lenovo_automute(struct hda_codec *codec) @@ -9441,10 +10656,8 @@ present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0, - 0x80, present ? 0x80 : 0); - snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0, - 0x80, present ? 0x80 : 0); + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); } static void alc861vd_dallas_unsol_event(struct hda_codec *codec, unsigned int res) @@ -9453,6 +10666,10 @@ alc861vd_dallas_automute(codec); } +#ifdef CONFIG_SND_HDA_POWER_SAVE +#define alc861vd_loopbacks alc880_loopbacks +#endif + /* pcm configuration: identiacal with ALC880 */ #define alc861vd_pcm_analog_playback alc880_pcm_analog_playback #define alc861vd_pcm_analog_capture alc880_pcm_analog_capture @@ -9464,25 +10681,32 @@ */ static const char *alc861vd_models[ALC861VD_MODEL_LAST] = { [ALC660VD_3ST] = "3stack-660", + [ALC660VD_3ST_DIG] = "3stack-660-digout", [ALC861VD_3ST] = "3stack", [ALC861VD_3ST_DIG] = "3stack-digout", [ALC861VD_6ST_DIG] = "6stack-digout", [ALC861VD_LENOVO] = "lenovo", [ALC861VD_DALLAS] = "dallas", + [ALC861VD_HP] = "hp", [ALC861VD_AUTO] = "auto", }; static struct snd_pci_quirk alc861vd_cfg_tbl[] = { SND_PCI_QUIRK(0x1043, 0x12e2, "Asus z35m", ALC660VD_3ST), SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST), - SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST), + SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG), SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST), SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST), - SND_PCI_QUIRK(0x1179, 0xff00, "DALLAS", ALC861VD_DALLAS), + /*SND_PCI_QUIRK(0x1179, 0xff00, "DALLAS", ALC861VD_DALLAS),*/ /*lenovo*/ SND_PCI_QUIRK(0x1179, 0xff01, "DALLAS", ALC861VD_DALLAS), SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_LENOVO), SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo", ALC861VD_LENOVO), + SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO), + SND_PCI_QUIRK(0x1179, 0xff03, "Toshiba P205", ALC861VD_LENOVO), + SND_PCI_QUIRK(0x1565, 0x820d, "Biostar NF61S SE", ALC861VD_6ST_DIG), + SND_PCI_QUIRK(0x1849, 0x0862, "ASRock K8NF6G-VSTA", ALC861VD_6ST_DIG), + SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP), {} }; @@ -9499,6 +10723,19 @@ .channel_mode = alc861vd_3stack_2ch_modes, .input_mux = &alc861vd_capture_source, }, + [ALC660VD_3ST_DIG] = { + .mixers = { alc861vd_3st_mixer }, + .init_verbs = { alc861vd_volume_init_verbs, + alc861vd_3stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc660vd_dac_nids), + .dac_nids = alc660vd_dac_nids, + .dig_out_nid = ALC861VD_DIGOUT_NID, + .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids), + .adc_nids = alc861vd_adc_nids, + .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), + .channel_mode = alc861vd_3stack_2ch_modes, + .input_mux = &alc861vd_capture_source, + }, [ALC861VD_3ST] = { .mixers = { alc861vd_3st_mixer }, .init_verbs = { alc861vd_volume_init_verbs, @@ -9559,7 +10796,21 @@ .input_mux = &alc861vd_dallas_capture_source, .unsol_event = alc861vd_dallas_unsol_event, .init_hook = alc861vd_dallas_automute, - }, + }, + [ALC861VD_HP] = { + .mixers = { alc861vd_hp_mixer }, + .init_verbs = { alc861vd_dallas_verbs, alc861vd_eapd_verbs }, + .num_dacs = ARRAY_SIZE(alc861vd_dac_nids), + .dac_nids = alc861vd_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids), + .dig_out_nid = ALC861VD_DIGOUT_NID, + .adc_nids = alc861vd_adc_nids, + .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes), + .channel_mode = alc861vd_3stack_2ch_modes, + .input_mux = &alc861vd_hp_capture_source, + .unsol_event = alc861vd_dallas_unsol_event, + .init_hook = alc861vd_dallas_automute, + }, }; /* @@ -9859,6 +11110,10 @@ if (board_config == ALC861VD_AUTO) spec->init_hook = alc861vd_auto_init; +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (!spec->loopback.amplist) + spec->loopback.amplist = alc861vd_loopbacks; +#endif return 0; } @@ -9916,7 +11171,7 @@ struct alc_spec *spec = codec->spec; const struct hda_input_mux *imux = spec->input_mux; unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - static hda_nid_t capture_mixers[3] = { 0x24, 0x23, 0x22 }; + static hda_nid_t capture_mixers[2] = { 0x23, 0x22 }; hda_nid_t nid = capture_mixers[adc_idx]; unsigned int *cur_val = &spec->cur_mux[adc_idx]; unsigned int i, idx; @@ -9924,12 +11179,13 @@ idx = ucontrol->value.enumerated.item[0]; if (idx >= imux->num_items) idx = imux->num_items - 1; - if (*cur_val == idx && !codec->in_resume) + if (*cur_val == idx) return 0; for (i = 0; i < imux->num_items; i++) { - unsigned int v = (i == idx) ? 0x7000 : 0x7080; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - v | (imux->items[i].index << 8)); + unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE; + snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, + imux->items[i].index, + HDA_AMP_MUTE, v); } *cur_val = idx; return 1; @@ -10138,11 +11394,11 @@ {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front mixer: unmute input/output amp left and right (volume = 0) */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, @@ -10211,11 +11467,11 @@ * panel mic (mic 2) */ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* * Set up output mixers (0x0c - 0x0f) @@ -10239,11 +11495,7 @@ /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ /* Input mixer */ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - /*{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},*/ - {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, - + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, { } }; @@ -10274,11 +11526,9 @@ present = snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? 0x80 : 0; - snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0, - 0x80, bits); - snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0, - 0x80, bits); + bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); } static void alc662_lenovo_101e_all_automute(struct hda_codec *codec) @@ -10288,15 +11538,11 @@ present = snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; - bits = present ? 0x80 : 0; - snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0, - 0x80, bits); - snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0, - 0x80, bits); - snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0, - 0x80, bits); - snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0, - 0x80, bits); + bits = present ? HDA_AMP_MUTE : 0; + snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); + snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, + HDA_AMP_MUTE, bits); } static void alc662_lenovo_101e_unsol_event(struct hda_codec *codec, @@ -10308,6 +11554,10 @@ alc662_lenovo_101e_ispeaker_automute(codec); } +#ifdef CONFIG_SND_HDA_POWER_SAVE +#define alc662_loopbacks alc880_loopbacks +#endif + /* pcm configuration: identiacal with ALC880 */ #define alc662_pcm_analog_playback alc880_pcm_analog_playback @@ -10420,7 +11670,7 @@ for (i = 0; i < cfg->line_outs; i++) { if (!spec->multiout.dac_nids[i]) continue; - nid = alc880_idx_to_dac(i); + nid = alc880_idx_to_mixer(i); if (i == 2) { /* Center/LFE */ err = add_control(spec, ALC_CTL_WIDGET_VOL, @@ -10643,14 +11893,10 @@ spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux; - if (err < 0) - return err; - else if (err > 0) - /* hack - override the init verbs */ - spec->init_verbs[0] = alc662_auto_init_verbs; + spec->init_verbs[spec->num_init_verbs++] = alc662_auto_init_verbs; spec->mixers[spec->num_mixers] = alc662_capture_mixer; spec->num_mixers++; - return err; + return 1; } /* additional initialization for auto-configuration model */ @@ -10687,7 +11933,7 @@ if (err < 0) { alc_free(codec); return err; - } else if (err) { + } else if (!err) { printk(KERN_INFO "hda_codec: Cannot set up configuration " "from BIOS. Using base mode...\n"); @@ -10714,6 +11960,10 @@ codec->patch_ops = alc_patch_ops; if (board_config == ALC662_AUTO) spec->init_hook = alc662_auto_init; +#ifdef CONFIG_SND_HDA_POWER_SAVE + if (!spec->loopback.amplist) + spec->loopback.amplist = alc662_loopbacks; +#endif return 0; } @@ -10724,6 +11974,7 @@ struct hda_codec_preset snd_hda_preset_realtek[] = { { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 }, { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 }, + { .id = 0x10ec0268, .name = "ALC268", .patch = patch_alc268 }, { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660", .patch = patch_alc861 }, { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd }, diff -Nur sound/pci/hda/patch_si3054.c sound/pci/hda/patch_si3054.c --- sound/pci/hda/patch_si3054.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/hda/patch_si3054.c 2007-08-11 02:00:08.000000000 +0200 @@ -78,6 +78,8 @@ /* si3054 codec registers (nodes) access macros */ #define GET_REG(codec,reg) (snd_hda_codec_read(codec,reg,0,SI3054_VERB_READ_NODE,0)) #define SET_REG(codec,reg,val) (snd_hda_codec_write(codec,reg,0,SI3054_VERB_WRITE_NODE,val)) +#define SET_REG_CACHE(codec,reg,val) \ + snd_hda_codec_write_cache(codec,reg,0,SI3054_VERB_WRITE_NODE,val) struct si3054_spec { @@ -94,15 +96,7 @@ #define PRIVATE_REG(val) ((val>>16)&0xffff) #define PRIVATE_MASK(val) (val&0xffff) -static int si3054_switch_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define si3054_switch_info snd_ctl_boolean_mono_info static int si3054_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *uvalue) @@ -121,9 +115,9 @@ u16 reg = PRIVATE_REG(kcontrol->private_value); u16 mask = PRIVATE_MASK(kcontrol->private_value); if (uvalue->value.integer.value[0]) - SET_REG(codec, reg, (GET_REG(codec, reg)) | mask); + SET_REG_CACHE(codec, reg, (GET_REG(codec, reg)) | mask); else - SET_REG(codec, reg, (GET_REG(codec, reg)) & ~mask); + SET_REG_CACHE(codec, reg, (GET_REG(codec, reg)) & ~mask); return 0; } @@ -275,10 +269,6 @@ .build_pcms = si3054_build_pcms, .init = si3054_init, .free = si3054_free, -#ifdef CONFIG_PM - //.suspend = si3054_suspend, - .resume = si3054_init, -#endif }; static int patch_si3054(struct hda_codec *codec) @@ -304,8 +294,12 @@ { .id = 0x10573055, .name = "Si3054", .patch = patch_si3054 }, { .id = 0x10573057, .name = "Si3054", .patch = patch_si3054 }, { .id = 0x10573155, .name = "Si3054", .patch = patch_si3054 }, + /* VIA HDA on Clevo m540 */ + { .id = 0x11063288, .name = "Si3054", .patch = patch_si3054 }, /* Asus A8J Modem (SM56) */ { .id = 0x15433155, .name = "Si3054", .patch = patch_si3054 }, + /* LG LW20 modem */ + { .id = 0x18540018, .name = "Si3054", .patch = patch_si3054 }, {} }; diff -Nur sound/pci/hda/patch_sigmatel.c sound/pci/hda/patch_sigmatel.c --- sound/pci/hda/patch_sigmatel.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/hda/patch_sigmatel.c 2007-08-17 02:00:07.000000000 +0200 @@ -44,6 +44,9 @@ enum { STAC_9205_REF, + STAC_9205_DELL_M43, + STAC_9205_DELL_M44, + STAC_9205_M43xx, STAC_9205_MODELS }; @@ -59,11 +62,19 @@ STAC_D945_REF, STAC_D945GTP3, STAC_D945GTP5, + STAC_922X_DELL, + STAC_INTEL_MAC_V1, + STAC_INTEL_MAC_V2, + STAC_INTEL_MAC_V3, + STAC_INTEL_MAC_V4, + STAC_INTEL_MAC_V5, + /* for backward compitability */ STAC_MACMINI, STAC_MACBOOK, STAC_MACBOOK_PRO_V1, STAC_MACBOOK_PRO_V2, STAC_IMAC_INTEL, + STAC_IMAC_INTEL_20, STAC_922X_MODELS }; @@ -71,6 +82,7 @@ STAC_D965_REF, STAC_D965_3ST, STAC_D965_5ST, + STAC_DELL_3ST, STAC_927X_MODELS }; @@ -86,6 +98,8 @@ unsigned int hp_detect: 1; unsigned int gpio_mute: 1; + unsigned int gpio_mask, gpio_data; + /* playback */ struct hda_multi_out multiout; hda_nid_t dac_nids[5]; @@ -210,7 +224,6 @@ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x14, 0x16, 0x17, 0x18, 0x21, 0x22, - }; static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol, @@ -326,8 +339,6 @@ }; static struct snd_kcontrol_new stac925x_mixer[] = { - HDA_CODEC_VOLUME("Master Playback Volume", 0xe, 0, HDA_OUTPUT), - HDA_CODEC_MUTE("Master Playback Switch", 0xe, 0, HDA_OUTPUT), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Input Source", @@ -549,44 +560,78 @@ 0x02a19320, 0x40000100, }; -static unsigned int macbook_pro_v1_pin_configs[10] = { - 0x0321e230, 0x03a1e020, 0x9017e110, 0x01014010, - 0x01a19021, 0x0381e021, 0x1345e240, 0x13c5e22e, - 0x02a19320, 0x400000fb +static unsigned int intel_mac_v1_pin_configs[10] = { + 0x0121e21f, 0x400000ff, 0x9017e110, 0x400000fd, + 0x400000fe, 0x0181e020, 0x1145e030, 0x11c5e240, + 0x400000fc, 0x400000fb, +}; + +static unsigned int intel_mac_v2_pin_configs[10] = { + 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd, + 0x400000fe, 0x0181e020, 0x1145e230, 0x500000fa, + 0x400000fc, 0x400000fb, +}; + +static unsigned int intel_mac_v3_pin_configs[10] = { + 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd, + 0x400000fe, 0x0181e020, 0x1145e230, 0x11c5e240, + 0x400000fc, 0x400000fb, }; -static unsigned int macbook_pro_v2_pin_configs[10] = { - 0x0221401f, 0x90a70120, 0x01813024, 0x01014010, - 0x400000fd, 0x01016011, 0x1345e240, 0x13c5e22e, +static unsigned int intel_mac_v4_pin_configs[10] = { + 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f, + 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240, 0x400000fc, 0x400000fb, }; -static unsigned int imac_intel_pin_configs[10] = { - 0x0121e230, 0x90a70120, 0x9017e110, 0x400000fe, - 0x400000fd, 0x0181e021, 0x1145e040, 0x400000fa, +static unsigned int intel_mac_v5_pin_configs[10] = { + 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f, + 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240, 0x400000fc, 0x400000fb, }; +static unsigned int stac922x_dell_pin_configs[10] = { + 0x0221121e, 0x408103ff, 0x02a1123e, 0x90100310, + 0x408003f1, 0x0221122f, 0x03451340, 0x40c003f2, + 0x50a003f3, 0x405003f4 +}; + static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = { [STAC_D945_REF] = ref922x_pin_configs, [STAC_D945GTP3] = d945gtp3_pin_configs, [STAC_D945GTP5] = d945gtp5_pin_configs, - [STAC_MACMINI] = macbook_pro_v1_pin_configs, - [STAC_MACBOOK] = macbook_pro_v1_pin_configs, - [STAC_MACBOOK_PRO_V1] = macbook_pro_v1_pin_configs, - [STAC_MACBOOK_PRO_V2] = macbook_pro_v2_pin_configs, - [STAC_IMAC_INTEL] = imac_intel_pin_configs, + [STAC_922X_DELL] = stac922x_dell_pin_configs, + [STAC_INTEL_MAC_V1] = intel_mac_v1_pin_configs, + [STAC_INTEL_MAC_V2] = intel_mac_v2_pin_configs, + [STAC_INTEL_MAC_V3] = intel_mac_v3_pin_configs, + [STAC_INTEL_MAC_V4] = intel_mac_v4_pin_configs, + [STAC_INTEL_MAC_V5] = intel_mac_v5_pin_configs, + /* for backward compitability */ + [STAC_MACMINI] = intel_mac_v3_pin_configs, + [STAC_MACBOOK] = intel_mac_v5_pin_configs, + [STAC_MACBOOK_PRO_V1] = intel_mac_v3_pin_configs, + [STAC_MACBOOK_PRO_V2] = intel_mac_v3_pin_configs, + [STAC_IMAC_INTEL] = intel_mac_v2_pin_configs, + [STAC_IMAC_INTEL_20] = intel_mac_v3_pin_configs, }; static const char *stac922x_models[STAC_922X_MODELS] = { [STAC_D945_REF] = "ref", [STAC_D945GTP5] = "5stack", [STAC_D945GTP3] = "3stack", + [STAC_922X_DELL] = "dell", + [STAC_INTEL_MAC_V1] = "intel-mac-v1", + [STAC_INTEL_MAC_V2] = "intel-mac-v2", + [STAC_INTEL_MAC_V3] = "intel-mac-v3", + [STAC_INTEL_MAC_V4] = "intel-mac-v4", + [STAC_INTEL_MAC_V5] = "intel-mac-v5", + /* for backward compitability */ [STAC_MACMINI] = "macmini", [STAC_MACBOOK] = "macbook", [STAC_MACBOOK_PRO_V1] = "macbook-pro-v1", [STAC_MACBOOK_PRO_V2] = "macbook-pro", [STAC_IMAC_INTEL] = "imac-intel", + [STAC_IMAC_INTEL_20] = "imac-intel-20", }; static struct snd_pci_quirk stac922x_cfg_tbl[] = { @@ -649,7 +694,10 @@ /* other systems */ /* Apple Mac Mini (early 2006) */ SND_PCI_QUIRK(0x8384, 0x7680, - "Mac Mini", STAC_MACMINI), + "Mac Mini", STAC_INTEL_MAC_V3), + /* Dell */ + SND_PCI_QUIRK(0x1028, 0x01d7, "Dell XPS M1210", STAC_922X_DELL), + {} /* terminator */ }; @@ -674,16 +722,25 @@ 0x40000100, 0x40000100 }; +static unsigned int dell_3st_pin_configs[14] = { + 0x02211230, 0x02a11220, 0x01a19040, 0x01114210, + 0x01111212, 0x01116211, 0x01813050, 0x01112214, + 0x403003fa, 0x40000100, 0x40000100, 0x404003fb, + 0x40c003fc, 0x40000100 +}; + static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = { [STAC_D965_REF] = ref927x_pin_configs, [STAC_D965_3ST] = d965_3st_pin_configs, [STAC_D965_5ST] = d965_5st_pin_configs, + [STAC_DELL_3ST] = dell_3st_pin_configs, }; static const char *stac927x_models[STAC_927X_MODELS] = { [STAC_D965_REF] = "ref", [STAC_D965_3ST] = "3stack", [STAC_D965_5ST] = "5stack", + [STAC_DELL_3ST] = "dell-3stack", }; static struct snd_pci_quirk stac927x_cfg_tbl[] = { @@ -710,6 +767,10 @@ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2003, "Intel D965", STAC_D965_3ST), SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2002, "Intel D965", STAC_D965_3ST), SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2001, "Intel D965", STAC_D965_3ST), + /* Dell 3 stack systems */ + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01dd, "Dell E520", STAC_DELL_3ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ed, "Dell ", STAC_DELL_3ST), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f4, "Dell ", STAC_DELL_3ST), /* 965 based 5 stack systems */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2301, "Intel D965", STAC_D965_5ST), SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2302, "Intel D965", STAC_D965_5ST), @@ -729,18 +790,58 @@ 0x90a000f0, 0x90a000f0, 0x01441030, 0x01c41030 }; +static unsigned int dell_m43_9205_pin_configs[12] = { + 0x0321101f, 0x03a11020, 0x90a70330, 0x90170310, + 0x400000fe, 0x400000ff, 0x400000fd, 0x40f000f9, + 0x400000fa, 0x400000fc, 0x0144131f, 0x40c003f8, +}; + +static unsigned int dell_m44_9205_pin_configs[12] = { + 0x0421101f, 0x04a11020, 0x400003fa, 0x90170310, + 0x400003fb, 0x400003fc, 0x400003fd, 0x400003f9, + 0x90a60330, 0x400003ff, 0x01441340, 0x40c003fe, +}; + + static unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = { - ref9205_pin_configs, + [STAC_9205_REF] = ref9205_pin_configs, + [STAC_9205_DELL_M43] = dell_m43_9205_pin_configs, + [STAC_9205_DELL_M44] = dell_m44_9205_pin_configs, + [STAC_9205_M43xx] = NULL, }; static const char *stac9205_models[STAC_9205_MODELS] = { [STAC_9205_REF] = "ref", + [STAC_9205_DELL_M43] = "dell-m43", + [STAC_9205_DELL_M44] = "dell-m44", }; static struct snd_pci_quirk stac9205_cfg_tbl[] = { /* SigmaTel reference board */ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, "DFI LanParty", STAC_9205_REF), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f8, + "Dell Precision", STAC_9205_M43xx), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f9, + "Dell Precision", STAC_9205_DELL_M43), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fa, + "Dell Precision", STAC_9205_DELL_M43), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fe, + "Dell Precision", STAC_9205_DELL_M43), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ff, + "Dell Precision", STAC_9205_DELL_M43), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0206, + "Dell Precision", STAC_9205_DELL_M43), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1, + "Dell Inspiron", STAC_9205_DELL_M44), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f2, + "Dell Inspiron", STAC_9205_DELL_M44), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fc, + "Dell Inspiron", STAC_9205_DELL_M44), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fd, + "Dell Inspiron", STAC_9205_DELL_M44), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021f, + "Dell Inspiron", STAC_9205_DELL_M44), {} /* terminator */ }; @@ -770,33 +871,56 @@ return 0; } +static void stac92xx_set_config_reg(struct hda_codec *codec, + hda_nid_t pin_nid, unsigned int pin_config) +{ + int i; + snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_CONFIG_DEFAULT_BYTES_0, + pin_config & 0x000000ff); + snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_CONFIG_DEFAULT_BYTES_1, + (pin_config & 0x0000ff00) >> 8); + snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_CONFIG_DEFAULT_BYTES_2, + (pin_config & 0x00ff0000) >> 16); + snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, + pin_config >> 24); + i = snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_CONFIG_DEFAULT, + 0x00); + snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x pin config %8.8x\n", + pin_nid, i); +} + static void stac92xx_set_config_regs(struct hda_codec *codec) { int i; struct sigmatel_spec *spec = codec->spec; - unsigned int pin_cfg; - if (! spec->pin_nids || ! spec->pin_configs) - return; + if (!spec->pin_configs) + return; - for (i = 0; i < spec->num_pins; i++) { - snd_hda_codec_write(codec, spec->pin_nids[i], 0, - AC_VERB_SET_CONFIG_DEFAULT_BYTES_0, - spec->pin_configs[i] & 0x000000ff); - snd_hda_codec_write(codec, spec->pin_nids[i], 0, - AC_VERB_SET_CONFIG_DEFAULT_BYTES_1, - (spec->pin_configs[i] & 0x0000ff00) >> 8); - snd_hda_codec_write(codec, spec->pin_nids[i], 0, - AC_VERB_SET_CONFIG_DEFAULT_BYTES_2, - (spec->pin_configs[i] & 0x00ff0000) >> 16); - snd_hda_codec_write(codec, spec->pin_nids[i], 0, - AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, - spec->pin_configs[i] >> 24); - pin_cfg = snd_hda_codec_read(codec, spec->pin_nids[i], 0, - AC_VERB_GET_CONFIG_DEFAULT, - 0x00); - snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x pin config %8.8x\n", spec->pin_nids[i], pin_cfg); - } + for (i = 0; i < spec->num_pins; i++) + stac92xx_set_config_reg(codec, spec->pin_nids[i], + spec->pin_configs[i]); +} + +static void stac92xx_enable_gpio_mask(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + /* Configure GPIOx as output */ + snd_hda_codec_write_cache(codec, codec->afg, 0, + AC_VERB_SET_GPIO_DIRECTION, spec->gpio_mask); + /* Configure GPIOx as CMOS */ + snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7e7, 0x00000000); + /* Assert GPIOx */ + snd_hda_codec_write_cache(codec, codec->afg, 0, + AC_VERB_SET_GPIO_DATA, spec->gpio_data); + /* Enable GPIOx */ + snd_hda_codec_write_cache(codec, codec->afg, 0, + AC_VERB_SET_GPIO_MASK, spec->gpio_mask); } /* @@ -995,17 +1119,11 @@ static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type) { - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); + snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); } -static int stac92xx_io_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define stac92xx_io_switch_info snd_ctl_boolean_mono_info static int stac92xx_io_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1168,7 +1286,7 @@ * and 9202/925x. For those, dac_nids[] must be hard-coded. */ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) + struct auto_pin_cfg *cfg) { struct sigmatel_spec *spec = codec->spec; int i, j, conn_len = 0; @@ -1193,6 +1311,13 @@ } if (j == conn_len) { + if (spec->multiout.num_dacs > 0) { + /* we have already working output pins, + * so let's drop the broken ones again + */ + cfg->line_outs = spec->multiout.num_dacs; + break; + } /* error out, no available DAC found */ snd_printk(KERN_ERR "%s: No available DAC for pin 0x%x\n", @@ -1204,8 +1329,8 @@ spec->multiout.num_dacs++; if (conn_len > 1) { /* select this DAC in the pin's input mux */ - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, j); + snd_hda_codec_write_cache(codec, nid, 0, + AC_VERB_SET_CONNECT_SEL, j); } } @@ -1334,7 +1459,15 @@ continue; add_spec_dacs(spec, nid); } - + for (i = 0; i < cfg->line_outs; i++) { + nid = snd_hda_codec_read(codec, cfg->line_out_pins[i], 0, + AC_VERB_GET_CONNECT_LIST, 0) & 0xff; + if (check_in_dac_nids(spec, nid)) + nid = 0; + if (! nid) + continue; + add_spec_dacs(spec, nid); + } for (i = old_num_dacs; i < spec->multiout.num_dacs; i++) { static const char *pfxs[] = { "Speaker", "External Speaker", "Speaker2", @@ -1450,9 +1583,9 @@ * NID lists. Hopefully this won't get confused. */ for (i = 0; i < spec->num_muxes; i++) { - snd_hda_codec_write(codec, spec->mux_nids[i], 0, - AC_VERB_SET_CONNECT_SEL, - imux->items[0].index); + snd_hda_codec_write_cache(codec, spec->mux_nids[i], 0, + AC_VERB_SET_CONNECT_SEL, + imux->items[0].index); } } @@ -1784,7 +1917,7 @@ if (flag & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN)) pin_ctl &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); - snd_hda_codec_write(codec, nid, 0, + snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_ctl | flag); } @@ -1794,7 +1927,7 @@ { unsigned int pin_ctl = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00); - snd_hda_codec_write(codec, nid, 0, + snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_ctl & ~flag); } @@ -1850,22 +1983,13 @@ } } -#ifdef CONFIG_PM +#ifdef SND_HDA_NEEDS_RESUME static int stac92xx_resume(struct hda_codec *codec) { - struct sigmatel_spec *spec = codec->spec; - int i; - - stac92xx_init(codec); stac92xx_set_config_regs(codec); - snd_hda_resume_ctls(codec, spec->mixer); - for (i = 0; i < spec->num_mixers; i++) - snd_hda_resume_ctls(codec, spec->mixers[i]); - if (spec->multiout.dig_out_nid) - snd_hda_resume_spdif_out(codec); - if (spec->dig_in_nid) - snd_hda_resume_spdif_in(codec); - + stac92xx_init(codec); + snd_hda_codec_resume_amp(codec); + snd_hda_codec_resume_cache(codec); return 0; } #endif @@ -1876,7 +2000,7 @@ .init = stac92xx_init, .free = stac92xx_free, .unsol_event = stac92xx_unsol_event, -#ifdef CONFIG_PM +#ifdef SND_HDA_NEEDS_RESUME .resume = stac92xx_resume, #endif }; @@ -1891,7 +2015,7 @@ return -ENOMEM; codec->spec = spec; - spec->num_pins = 8; + spec->num_pins = ARRAY_SIZE(stac9200_pin_nids); spec->pin_nids = stac9200_pin_nids; spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS, stac9200_models, @@ -1941,7 +2065,7 @@ return -ENOMEM; codec->spec = spec; - spec->num_pins = 8; + spec->num_pins = ARRAY_SIZE(stac925x_pin_nids); spec->pin_nids = stac925x_pin_nids; spec->board_config = snd_hda_check_board_config(codec, STAC_925x_MODELS, stac925x_models, @@ -2013,29 +2137,41 @@ return -ENOMEM; codec->spec = spec; - spec->num_pins = 10; + spec->num_pins = ARRAY_SIZE(stac922x_pin_nids); spec->pin_nids = stac922x_pin_nids; spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS, stac922x_models, stac922x_cfg_tbl); - if (spec->board_config == STAC_MACMINI) { + if (spec->board_config == STAC_INTEL_MAC_V3) { spec->gpio_mute = 1; /* Intel Macs have all same PCI SSID, so we need to check * codec SSID to distinguish the exact models */ printk(KERN_INFO "hda_codec: STAC922x, Apple subsys_id=%x\n", codec->subsystem_id); switch (codec->subsystem_id) { - case 0x106b0a00: /* MacBook First generatoin */ - spec->board_config = STAC_MACBOOK; + + case 0x106b0800: + spec->board_config = STAC_INTEL_MAC_V1; break; - case 0x106b0200: /* MacBook Pro first generation */ - spec->board_config = STAC_MACBOOK_PRO_V1; + case 0x106b0600: + case 0x106b0700: + spec->board_config = STAC_INTEL_MAC_V2; break; - case 0x106b1e00: /* MacBook Pro second generation */ - spec->board_config = STAC_MACBOOK_PRO_V2; + case 0x106b0e00: + case 0x106b0f00: + case 0x106b1600: + case 0x106b1700: + case 0x106b0200: + case 0x106b1e00: + spec->board_config = STAC_INTEL_MAC_V3; break; - case 0x106b0700: /* Intel-based iMac */ - spec->board_config = STAC_IMAC_INTEL; + case 0x106b1a00: + case 0x00000100: + spec->board_config = STAC_INTEL_MAC_V4; + break; + case 0x106b0a00: + case 0x106b2200: + spec->board_config = STAC_INTEL_MAC_V5; break; } } @@ -2082,6 +2218,13 @@ codec->patch_ops = stac92xx_patch_ops; + /* Fix Mux capture level; max to 2 */ + snd_hda_override_amp_caps(codec, 0x12, HDA_OUTPUT, + (0 << AC_AMPCAP_OFFSET_SHIFT) | + (2 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (0 << AC_AMPCAP_MUTE_SHIFT)); + return 0; } @@ -2095,7 +2238,7 @@ return -ENOMEM; codec->spec = spec; - spec->num_pins = 14; + spec->num_pins = ARRAY_SIZE(stac927x_pin_nids); spec->pin_nids = stac927x_pin_nids; spec->board_config = snd_hda_check_board_config(codec, STAC_927X_MODELS, stac927x_models, @@ -2141,7 +2284,10 @@ } spec->multiout.dac_nids = spec->dac_nids; - + /* GPIO0 High = Enable EAPD */ + spec->gpio_mask = spec->gpio_data = 0x00000001; + stac92xx_enable_gpio_mask(codec); + err = stac92xx_parse_auto_config(codec, 0x1e, 0x20); if (!err) { if (spec->board_config < 0) { @@ -2159,13 +2305,6 @@ codec->patch_ops = stac92xx_patch_ops; - /* Fix Mux capture level; max to 2 */ - snd_hda_override_amp_caps(codec, 0x12, HDA_OUTPUT, - (0 << AC_AMPCAP_OFFSET_SHIFT) | - (2 << AC_AMPCAP_NUM_STEPS_SHIFT) | - (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) | - (0 << AC_AMPCAP_MUTE_SHIFT)); - return 0; } @@ -2179,7 +2318,7 @@ return -ENOMEM; codec->spec = spec; - spec->num_pins = 14; + spec->num_pins = ARRAY_SIZE(stac9205_pin_nids); spec->pin_nids = stac9205_pin_nids; spec->board_config = snd_hda_check_board_config(codec, STAC_9205_MODELS, stac9205_models, @@ -2209,19 +2348,27 @@ spec->mixer = stac9205_mixer; spec->multiout.dac_nids = spec->dac_nids; + + switch (spec->board_config){ + case STAC_9205_M43xx: + case STAC_9205_DELL_M43: + /* Enable SPDIF in/out */ + stac92xx_set_config_reg(codec, 0x1f, 0x01441030); + stac92xx_set_config_reg(codec, 0x20, 0x1c410030); + + spec->gpio_mask = 0x00000007; /* GPIO0-2 */ + /* GPIO0 High = EAPD, GPIO1 Low = DRM, + * GPIO2 High = Headphone Mute + */ + spec->gpio_data = 0x00000005; + break; + default: + /* GPIO0 High = EAPD */ + spec->gpio_mask = spec->gpio_data = 0x00000001; + break; + } - /* Configure GPIO0 as EAPD output */ - snd_hda_codec_write(codec, codec->afg, 0, - AC_VERB_SET_GPIO_DIRECTION, 0x00000001); - /* Configure GPIO0 as CMOS */ - snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0x00000000); - /* Assert GPIO0 high */ - snd_hda_codec_write(codec, codec->afg, 0, - AC_VERB_SET_GPIO_DATA, 0x00000001); - /* Enable GPIO0 */ - snd_hda_codec_write(codec, codec->afg, 0, - AC_VERB_SET_GPIO_MASK, 0x00000001); - + stac92xx_enable_gpio_mask(codec); err = stac92xx_parse_auto_config(codec, 0x1f, 0x20); if (!err) { if (spec->board_config < 0) { @@ -2256,19 +2403,20 @@ .num_items = 2, .items = { /* { "HP", 0x0 }, */ - { "Line", 0x1 }, - { "Mic", 0x2 }, + { "Mic Jack", 0x1 }, + { "Internal Mic", 0x2 }, { "PCM", 0x3 }, } }; static struct hda_verb vaio_init[] = { {0x0a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP }, /* HP <- 0x2 */ + {0x0a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | STAC_HP_EVENT}, {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, /* Speaker <- 0x5 */ {0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? (<- 0x2) */ {0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x2}, /* mic-sel: 0a,0d,14,02 */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* capture sw/vol -> 0x8 */ @@ -2284,7 +2432,7 @@ {0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */ /* {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },*/ /* Optical Out */ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */ - {0x15, AC_VERB_SET_CONNECT_SEL, 0x2}, /* mic-sel: 0a,0d,14,02 */ + {0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */ /* {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},*/ /* Optical Out */ @@ -2295,61 +2443,28 @@ }; /* bind volumes of both NID 0x02 and 0x05 */ -static int vaio_master_vol_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - long *valp = ucontrol->value.integer.value; - int change; - - change = snd_hda_codec_amp_update(codec, 0x02, 0, HDA_OUTPUT, 0, - 0x7f, valp[0] & 0x7f); - change |= snd_hda_codec_amp_update(codec, 0x02, 1, HDA_OUTPUT, 0, - 0x7f, valp[1] & 0x7f); - snd_hda_codec_amp_update(codec, 0x05, 0, HDA_OUTPUT, 0, - 0x7f, valp[0] & 0x7f); - snd_hda_codec_amp_update(codec, 0x05, 1, HDA_OUTPUT, 0, - 0x7f, valp[1] & 0x7f); - return change; -} +static struct hda_bind_ctls vaio_bind_master_vol = { + .ops = &snd_hda_bind_vol, + .values = { + HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT), + 0 + }, +}; /* bind volumes of both NID 0x02 and 0x05 */ -static int vaio_master_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - long *valp = ucontrol->value.integer.value; - int change; - - change = snd_hda_codec_amp_update(codec, 0x02, 0, HDA_OUTPUT, 0, - 0x80, (valp[0] ? 0 : 0x80)); - change |= snd_hda_codec_amp_update(codec, 0x02, 1, HDA_OUTPUT, 0, - 0x80, (valp[1] ? 0 : 0x80)); - snd_hda_codec_amp_update(codec, 0x05, 0, HDA_OUTPUT, 0, - 0x80, (valp[0] ? 0 : 0x80)); - snd_hda_codec_amp_update(codec, 0x05, 1, HDA_OUTPUT, 0, - 0x80, (valp[1] ? 0 : 0x80)); - return change; -} +static struct hda_bind_ctls vaio_bind_master_sw = { + .ops = &snd_hda_bind_sw, + .values = { + HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), + HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT), + 0, + }, +}; static struct snd_kcontrol_new vaio_mixer[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Volume", - .info = snd_hda_mixer_amp_volume_info, - .get = snd_hda_mixer_amp_volume_get, - .put = vaio_master_vol_put, - .tlv = { .c = snd_hda_mixer_amp_tlv }, - .private_value = HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = vaio_master_sw_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), - }, + HDA_BIND_VOL("Master Playback Volume", &vaio_bind_master_vol), + HDA_BIND_SW("Master Playback Switch", &vaio_bind_master_sw), /* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */ HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT), HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT), @@ -2365,22 +2480,8 @@ }; static struct snd_kcontrol_new vaio_ar_mixer[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Volume", - .info = snd_hda_mixer_amp_volume_info, - .get = snd_hda_mixer_amp_volume_get, - .put = vaio_master_vol_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .info = snd_hda_mixer_amp_switch_info, - .get = snd_hda_mixer_amp_switch_get, - .put = vaio_master_sw_put, - .private_value = HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), - }, + HDA_BIND_VOL("Master Playback Volume", &vaio_bind_master_vol), + HDA_BIND_SW("Master Playback Switch", &vaio_bind_master_sw), /* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */ HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT), HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT), @@ -2402,6 +2503,49 @@ .build_pcms = stac92xx_build_pcms, .init = stac92xx_init, .free = stac92xx_free, +#ifdef SND_HDA_NEEDS_RESUME + .resume = stac92xx_resume, +#endif +}; + +static int stac9872_vaio_init(struct hda_codec *codec) +{ + int err; + + err = stac92xx_init(codec); + if (err < 0) + return err; + if (codec->patch_ops.unsol_event) + codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26); + return 0; +} + +static void stac9872_vaio_hp_detect(struct hda_codec *codec, unsigned int res) +{ + if (get_pin_presence(codec, 0x0a)) { + stac92xx_reset_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN); + stac92xx_set_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN); + } else { + stac92xx_reset_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN); + stac92xx_set_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN); + } +} + +static void stac9872_vaio_unsol_event(struct hda_codec *codec, unsigned int res) +{ + switch (res >> 26) { + case STAC_HP_EVENT: + stac9872_vaio_hp_detect(codec, res); + break; + } +} + +static struct hda_codec_ops stac9872_vaio_patch_ops = { + .build_controls = stac92xx_build_controls, + .build_pcms = stac92xx_build_pcms, + .init = stac9872_vaio_init, + .free = stac92xx_free, + .unsol_event = stac9872_vaio_unsol_event, #ifdef CONFIG_PM .resume = stac92xx_resume, #endif @@ -2462,6 +2606,7 @@ spec->adc_nids = vaio_adcs; spec->input_mux = &vaio_mux; spec->mux_nids = vaio_mux_nids; + codec->patch_ops = stac9872_vaio_patch_ops; break; case CXD9872AKD_VAIO: @@ -2475,10 +2620,10 @@ spec->adc_nids = vaio_adcs; spec->input_mux = &vaio_mux; spec->mux_nids = vaio_mux_nids; + codec->patch_ops = stac9872_patch_ops; break; } - codec->patch_ops = stac9872_patch_ops; return 0; } diff -Nur sound/pci/hda/patch_via.c sound/pci/hda/patch_via.c --- sound/pci/hda/patch_via.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/hda/patch_via.c 2007-08-11 02:00:08.000000000 +0200 @@ -115,6 +115,10 @@ struct snd_kcontrol_new *kctl_alloc; struct hda_input_mux private_imux; hda_nid_t private_dac_nids[4]; + +#ifdef CONFIG_SND_HDA_POWER_SAVE + struct hda_loopback_check loopback; +#endif }; static hda_nid_t vt1708_adc_nids[2] = { @@ -305,15 +309,15 @@ {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback * mixer widget */ /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* master */ + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* * Set up output mixers (0x19 - 0x1b) @@ -543,24 +547,11 @@ return 0; } -#ifdef CONFIG_PM -/* - * resume - */ -static int via_resume(struct hda_codec *codec) +#ifdef CONFIG_SND_HDA_POWER_SAVE +static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) { struct via_spec *spec = codec->spec; - int i; - - via_init(codec); - for (i = 0; i < spec->num_mixers; i++) - snd_hda_resume_ctls(codec, spec->mixers[i]); - if (spec->multiout.dig_out_nid) - snd_hda_resume_spdif_out(codec); - if (spec->dig_in_nid) - snd_hda_resume_spdif_in(codec); - - return 0; + return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); } #endif @@ -571,8 +562,8 @@ .build_pcms = via_build_pcms, .init = via_init, .free = via_free, -#ifdef CONFIG_PM - .resume = via_resume, +#ifdef CONFIG_SND_HDA_POWER_SAVE + .check_power_status = via_check_power_status, #endif }; @@ -762,6 +753,16 @@ return 0; } +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list vt1708_loopbacks[] = { + { 0x17, HDA_INPUT, 1 }, + { 0x17, HDA_INPUT, 2 }, + { 0x17, HDA_INPUT, 3 }, + { 0x17, HDA_INPUT, 4 }, + { } /* end */ +}; +#endif + static int vt1708_parse_auto_config(struct hda_codec *codec) { struct via_spec *spec = codec->spec; @@ -855,6 +856,9 @@ codec->patch_ops = via_patch_ops; codec->patch_ops.init = via_auto_init; +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = vt1708_loopbacks; +#endif return 0; } @@ -895,15 +899,15 @@ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback * mixer widget */ /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* unmute master */ + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* * Set up output selector (0x1a, 0x1b, 0x29) @@ -1251,6 +1255,16 @@ return 1; } +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list vt1709_loopbacks[] = { + { 0x18, HDA_INPUT, 1 }, + { 0x18, HDA_INPUT, 2 }, + { 0x18, HDA_INPUT, 3 }, + { 0x18, HDA_INPUT, 4 }, + { } /* end */ +}; +#endif + static int patch_vt1709_10ch(struct hda_codec *codec) { struct via_spec *spec; @@ -1293,6 +1307,9 @@ codec->patch_ops = via_patch_ops; codec->patch_ops.init = via_auto_init; +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = vt1709_loopbacks; +#endif return 0; } @@ -1383,6 +1400,9 @@ codec->patch_ops = via_patch_ops; codec->patch_ops.init = via_auto_init; +#ifdef CONFIG_SND_HDA_POWER_SAVE + spec->loopback.amplist = vt1709_loopbacks; +#endif return 0; } diff -Nur sound/pci/ice1712/aureon.c sound/pci/ice1712/aureon.c --- sound/pci/ice1712/aureon.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/ice1712/aureon.c 2007-07-24 02:00:10.000000000 +0200 @@ -394,7 +394,7 @@ /* * AC'97 mute controls */ -#define aureon_ac97_mute_info aureon_mono_bool_info +#define aureon_ac97_mute_info snd_ctl_boolean_mono_info static int aureon_ac97_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -430,7 +430,7 @@ /* * AC'97 mute controls */ -#define aureon_ac97_micboost_info aureon_mono_bool_info +#define aureon_ac97_micboost_info snd_ctl_boolean_mono_info static int aureon_ac97_micboost_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -621,19 +621,12 @@ /* */ -static int aureon_mono_bool_info(struct snd_kcontrol *k, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define aureon_mono_bool_info snd_ctl_boolean_mono_info /* * AC'97 master playback mute controls (Mute on WM8770 chip) */ -#define aureon_ac97_mmute_info aureon_mono_bool_info +#define aureon_ac97_mmute_info snd_ctl_boolean_mono_info static int aureon_ac97_mmute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -708,7 +701,7 @@ /* * DAC mute control */ -#define wm_pcm_mute_info aureon_mono_bool_info +#define wm_pcm_mute_info snd_ctl_boolean_mono_info static int wm_pcm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -879,13 +872,7 @@ /* * WM8770 master mute control */ -static int wm_master_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define wm_master_mute_info snd_ctl_boolean_stereo_info static int wm_master_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -969,14 +956,7 @@ /* * ADC mute control */ -static int wm_adc_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define wm_adc_mute_info snd_ctl_boolean_stereo_info static int wm_adc_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1210,12 +1190,7 @@ /* * CS8415A Mute */ -static int aureon_cs8415_mute_info (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - return 0; -} +#define aureon_cs8415_mute_info snd_ctl_boolean_mono_info static int aureon_cs8415_mute_get (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1316,7 +1291,7 @@ return ( tmp & AUREON_HP_SEL )!= 0; } -#define aureon_hpamp_info aureon_mono_bool_info +#define aureon_hpamp_info snd_ctl_boolean_mono_info static int aureon_hpamp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1338,7 +1313,7 @@ * Deemphasis */ -#define aureon_deemp_info aureon_mono_bool_info +#define aureon_deemp_info snd_ctl_boolean_mono_info static int aureon_deemp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { diff -Nur sound/pci/ice1712/delta.c sound/pci/ice1712/delta.c --- sound/pci/ice1712/delta.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/ice1712/delta.c 2007-07-24 02:00:10.000000000 +0200 @@ -393,15 +393,8 @@ snd_ice1712_delta_cs8403_spdif_write(ice, tmp); } -static int snd_ice1712_delta1010lt_wordclock_status_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_ice1712_delta1010lt_wordclock_status_info \ + snd_ctl_boolean_mono_info static int snd_ice1712_delta1010lt_wordclock_status_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff -Nur sound/pci/ice1712/ews.c sound/pci/ice1712/ews.c --- sound/pci/ice1712/ews.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/ice1712/ews.c 2007-07-24 02:00:10.000000000 +0200 @@ -700,14 +700,7 @@ * EWS88D specific controls */ -static int snd_ice1712_ews88d_control_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_ice1712_ews88d_control_info snd_ctl_boolean_mono_info static int snd_ice1712_ews88d_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -812,14 +805,7 @@ return 0; } -static int snd_ice1712_6fire_control_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_ice1712_6fire_control_info snd_ctl_boolean_mono_info static int snd_ice1712_6fire_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { diff -Nur sound/pci/ice1712/ice1712.c sound/pci/ice1712/ice1712.c --- sound/pci/ice1712/ice1712.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/ice1712/ice1712.c 2007-07-25 02:00:06.000000000 +0200 @@ -256,14 +256,7 @@ /* * consumer ac97 digital mix */ -static int snd_ice1712_digmix_route_ac97_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_ice1712_digmix_route_ac97_info snd_ctl_boolean_mono_info static int snd_ice1712_digmix_route_ac97_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1300,14 +1293,7 @@ outw(val, ICEMT(ice, MONITOR_VOLUME)); } -static int snd_ice1712_pro_mixer_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_ice1712_pro_mixer_switch_info snd_ctl_boolean_stereo_info static int snd_ice1712_pro_mixer_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1759,16 +1745,6 @@ .put = snd_ice1712_spdif_stream_put }; -int snd_ice1712_gpio_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} - int snd_ice1712_gpio_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1968,15 +1944,7 @@ .put = snd_ice1712_pro_internal_clock_default_put }; -static int snd_ice1712_pro_rate_locking_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_ice1712_pro_rate_locking_info snd_ctl_boolean_mono_info static int snd_ice1712_pro_rate_locking_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2007,15 +1975,7 @@ .put = snd_ice1712_pro_rate_locking_put }; -static int snd_ice1712_pro_rate_reset_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_ice1712_pro_rate_reset_info snd_ctl_boolean_mono_info static int snd_ice1712_pro_rate_reset_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff -Nur sound/pci/ice1712/ice1712.h sound/pci/ice1712/ice1712.h --- sound/pci/ice1712/ice1712.h 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/ice1712/ice1712.h 2007-07-25 02:00:06.000000000 +0200 @@ -451,11 +451,10 @@ /* for bit controls */ #define ICE1712_GPIO(xiface, xname, xindex, mask, invert, xaccess) \ -{ .iface = xiface, .name = xname, .access = xaccess, .info = snd_ice1712_gpio_info, \ +{ .iface = xiface, .name = xname, .access = xaccess, .info = snd_ctl_boolean_mono_info, \ .get = snd_ice1712_gpio_get, .put = snd_ice1712_gpio_put, \ .private_value = mask | (invert << 24) } -int snd_ice1712_gpio_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); int snd_ice1712_gpio_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_ice1712_gpio_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); diff -Nur sound/pci/ice1712/ice1724.c sound/pci/ice1712/ice1724.c --- sound/pci/ice1712/ice1724.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/ice1712/ice1724.c 2007-07-31 02:00:08.000000000 +0200 @@ -341,10 +341,12 @@ what = 0; snd_pcm_group_for_each_entry(s, substream) { - const struct vt1724_pcm_reg *reg; - reg = s->runtime->private_data; - what |= reg->start; - snd_pcm_trigger_done(s, substream); + if (snd_pcm_substream_chip(s) == ice) { + const struct vt1724_pcm_reg *reg; + reg = s->runtime->private_data; + what |= reg->start; + snd_pcm_trigger_done(s, substream); + } } switch (cmd) { @@ -1479,15 +1481,7 @@ .get = snd_vt1724_spdif_maskp_get, }; -static int snd_vt1724_spdif_sw_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_vt1724_spdif_sw_info snd_ctl_boolean_mono_info static int snd_vt1724_spdif_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1532,15 +1526,7 @@ * GPIO access from extern */ -int snd_vt1724_gpio_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_vt1724_gpio_info snd_ctl_boolean_mono_info int snd_vt1724_gpio_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1706,15 +1692,7 @@ .put = snd_vt1724_pro_internal_clock_put }; -static int snd_vt1724_pro_rate_locking_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_vt1724_pro_rate_locking_info snd_ctl_boolean_mono_info static int snd_vt1724_pro_rate_locking_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1745,15 +1723,7 @@ .put = snd_vt1724_pro_rate_locking_put }; -static int snd_vt1724_pro_rate_reset_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_vt1724_pro_rate_reset_info snd_ctl_boolean_mono_info static int snd_vt1724_pro_rate_reset_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff -Nur sound/pci/ice1712/phase.c sound/pci/ice1712/phase.c --- sound/pci/ice1712/phase.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/ice1712/phase.c 2007-07-24 02:00:10.000000000 +0200 @@ -270,7 +270,7 @@ /* * DAC mute control */ -#define wm_pcm_mute_info phase28_mono_bool_info +#define wm_pcm_mute_info snd_ctl_boolean_mono_info static int wm_pcm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -527,13 +527,7 @@ /* * WM8770 master mute control */ -static int wm_master_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define wm_master_mute_info snd_ctl_boolean_stereo_info static int wm_master_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -615,20 +609,9 @@ } /* - */ -static int phase28_mono_bool_info(struct snd_kcontrol *k, 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; -} - -/* * Deemphasis */ -#define phase28_deemp_info phase28_mono_bool_info +#define phase28_deemp_info snd_ctl_boolean_mono_info static int phase28_deemp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { diff -Nur sound/pci/ice1712/pontis.c sound/pci/ice1712/pontis.c --- sound/pci/ice1712/pontis.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/ice1712/pontis.c 2007-07-24 02:00:10.000000000 +0200 @@ -216,14 +216,7 @@ /* * ADC input mux mixer control */ -static int wm_adc_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define wm_adc_mux_info snd_ctl_boolean_mono_info static int wm_adc_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -260,14 +253,7 @@ /* * Analog bypass (In -> Out) */ -static int wm_bypass_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define wm_bypass_info snd_ctl_boolean_mono_info static int wm_bypass_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -302,14 +288,7 @@ /* * Left/Right swap */ -static int wm_chswap_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define wm_chswap_info snd_ctl_boolean_mono_info static int wm_chswap_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { diff -Nur sound/pci/ice1712/prodigy192.c sound/pci/ice1712/prodigy192.c --- sound/pci/ice1712/prodigy192.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/ice1712/prodigy192.c 2007-07-24 02:00:10.000000000 +0200 @@ -81,14 +81,7 @@ /* * DAC mute control */ -static int stac9460_dac_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define stac9460_dac_mute_info snd_ctl_boolean_mono_info static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -177,14 +170,7 @@ /* * ADC mute control */ -static int stac9460_adc_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define stac9460_adc_mute_info snd_ctl_boolean_stereo_info static int stac9460_adc_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -292,14 +278,7 @@ return ( tmp & AUREON_HP_SEL )!= 0; } -static int aureon_bool_info(struct snd_kcontrol *k, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define aureon_bool_info snd_ctl_boolean_mono_info static int aureon_hpamp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { diff -Nur sound/pci/ice1712/revo.c sound/pci/ice1712/revo.c --- sound/pci/ice1712/revo.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/ice1712/revo.c 2007-05-20 02:00:11.000000000 +0200 @@ -186,7 +186,12 @@ #define AK_DAC(xname,xch) { .name = xname, .num_channels = xch } static const struct snd_akm4xxx_dac_channel revo71_front[] = { - AK_DAC("PCM Playback Volume", 2) + { + .name = "PCM Playback Volume", + .num_channels = 2, + /* front channels DAC supports muting */ + .switch_name = "PCM Playback Switch", + }, }; static const struct snd_akm4xxx_dac_channel revo71_surround[] = { diff -Nur sound/pci/ice1712/wtm.c sound/pci/ice1712/wtm.c --- sound/pci/ice1712/wtm.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/ice1712/wtm.c 2007-07-24 02:00:10.000000000 +0200 @@ -71,14 +71,7 @@ /* * DAC mute control */ -static int stac9460_dac_mute_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - return 0; -} +#define stac9460_dac_mute_info snd_ctl_boolean_mono_info static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -218,15 +211,7 @@ /* * ADC mute control */ -static int stac9460_adc_mute_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define stac9460_adc_mute_info snd_ctl_boolean_stereo_info static int stac9460_adc_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -357,15 +342,7 @@ * MIC / LINE switch fonction */ -static int stac9460_mic_sw_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define stac9460_mic_sw_info snd_ctl_boolean_mono_info static int stac9460_mic_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff -Nur sound/pci/korg1212/korg1212.c sound/pci/korg1212/korg1212.c --- sound/pci/korg1212/korg1212.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/korg1212/korg1212.c 2007-08-14 02:00:08.000000000 +0200 @@ -1391,8 +1391,6 @@ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_playback_open [%s]\n", stateName[korg1212->cardState]); - snd_pcm_set_sync(substream); // ??? - snd_korg1212_OpenCard(korg1212); runtime->hw = snd_korg1212_playback_info; @@ -1422,8 +1420,6 @@ K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_capture_open [%s]\n", stateName[korg1212->cardState]); - snd_pcm_set_sync(substream); - snd_korg1212_OpenCard(korg1212); runtime->hw = snd_korg1212_capture_info; diff -Nur sound/pci/maestro3.c sound/pci/maestro3.c --- sound/pci/maestro3.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/maestro3.c 2007-08-14 02:00:08.000000000 +0200 @@ -1821,7 +1821,6 @@ return err; runtime->hw = snd_m3_playback; - snd_pcm_set_sync(subs); return 0; } @@ -1846,7 +1845,6 @@ return err; runtime->hw = snd_m3_capture; - snd_pcm_set_sync(subs); return 0; } diff -Nur sound/pci/mixart/mixart.c sound/pci/mixart/mixart.c --- sound/pci/mixart/mixart.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/mixart/mixart.c 2007-08-14 02:00:08.000000000 +0200 @@ -652,7 +652,7 @@ static struct snd_pcm_hardware snd_mixart_analog_caps = { .info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), .formats = ( SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | @@ -673,7 +673,7 @@ static struct snd_pcm_hardware snd_mixart_digital_caps = { .info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), .formats = ( SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | @@ -1317,6 +1317,12 @@ mgr->mem[i].phys = pci_resource_start(pci, i); mgr->mem[i].virt = ioremap_nocache(mgr->mem[i].phys, pci_resource_len(pci, i)); + if (!mgr->mem[i].virt) { + printk(KERN_ERR "unable to remap resource 0x%lx\n", + mgr->mem[i].phys); + snd_mixart_free(mgr); + return -EBUSY; + } } if (request_irq(pci->irq, snd_mixart_interrupt, IRQF_SHARED, diff -Nur sound/pci/mixart/mixart_hwdep.c sound/pci/mixart/mixart_hwdep.c --- sound/pci/mixart/mixart_hwdep.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/mixart/mixart_hwdep.c 2007-07-20 02:00:08.000000000 +0200 @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include "mixart.h" diff -Nur sound/pci/mixart/mixart_mixer.c sound/pci/mixart/mixart_mixer.c --- sound/pci/mixart/mixart_mixer.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/mixart/mixart_mixer.c 2007-07-24 02:00:10.000000000 +0200 @@ -403,14 +403,7 @@ }; /* shared */ -static int mixart_sw_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define mixart_sw_info snd_ctl_boolean_stereo_info static int mixart_audio_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { diff -Nur sound/pci/nm256/nm256.c sound/pci/nm256/nm256.c --- sound/pci/nm256/nm256.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/nm256/nm256.c 2007-08-14 02:00:08.000000000 +0200 @@ -842,7 +842,6 @@ runtime->private_data = s; s->substream = substream; - snd_pcm_set_sync(substream); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); } @@ -1533,7 +1532,8 @@ printk(KERN_ERR " force the driver to load by " "passing in the module parameter\n"); printk(KERN_ERR " force_ac97=1\n"); - printk(KERN_ERR " or try sb16 or cs423x drivers instead.\n"); + printk(KERN_ERR " or try sb16, opl3sa2, or " + "cs423x drivers instead.\n"); err = -ENXIO; goto __error; } diff -Nur sound/pci/pcxhr/pcxhr.c sound/pci/pcxhr/pcxhr.c --- sound/pci/pcxhr/pcxhr.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/pcxhr/pcxhr.c 2007-08-14 02:00:08.000000000 +0200 @@ -646,6 +646,8 @@ if (snd_pcm_stream_linked(subs)) { struct snd_pcxhr *chip = snd_pcm_substream_chip(subs); snd_pcm_group_for_each_entry(s, subs) { + if (snd_pcm_substream_chip(s) != chip) + continue; stream = s->runtime->private_data; stream->status = PCXHR_STREAM_STATUS_SCHEDULE_RUN; @@ -902,6 +904,8 @@ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 4); snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 4); + snd_pcm_set_sync(subs); + mgr->ref_count_rate++; mutex_unlock(&mgr->setup_mutex); diff -Nur sound/pci/pcxhr/pcxhr_mixer.c sound/pci/pcxhr/pcxhr_mixer.c --- sound/pci/pcxhr/pcxhr_mixer.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/pcxhr/pcxhr_mixer.c 2007-07-24 02:00:10.000000000 +0200 @@ -144,14 +144,7 @@ }; /* shared */ -static int pcxhr_sw_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define pcxhr_sw_info snd_ctl_boolean_stereo_info static int pcxhr_audio_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff -Nur sound/pci/rme32.c sound/pci/rme32.c --- sound/pci/rme32.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/rme32.c 2007-08-14 02:00:08.000000000 +0200 @@ -258,19 +258,6 @@ & RME32_RCR_AUDIO_ADDR_MASK); } -static int snd_rme32_ratecode(int rate) -{ - switch (rate) { - case 32000: return SNDRV_PCM_RATE_32000; - case 44100: return SNDRV_PCM_RATE_44100; - case 48000: return SNDRV_PCM_RATE_48000; - case 64000: return SNDRV_PCM_RATE_64000; - case 88200: return SNDRV_PCM_RATE_88200; - case 96000: return SNDRV_PCM_RATE_96000; - } - return 0; -} - /* silence callback for halfduplex mode */ static int snd_rme32_playback_silence(struct snd_pcm_substream *substream, int channel, /* not used (interleaved data) */ snd_pcm_uframes_t pos, @@ -887,7 +874,7 @@ if ((rme32->rcreg & RME32_RCR_KMODE) && (rate = snd_rme32_capture_getrate(rme32, &dummy)) > 0) { /* AutoSync */ - runtime->hw.rates = snd_rme32_ratecode(rate); + runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate); runtime->hw.rate_min = rate; runtime->hw.rate_max = rate; } @@ -929,7 +916,7 @@ if (isadat) { return -EIO; } - runtime->hw.rates = snd_rme32_ratecode(rate); + runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate); runtime->hw.rate_min = rate; runtime->hw.rate_max = rate; } @@ -965,7 +952,7 @@ if ((rme32->rcreg & RME32_RCR_KMODE) && (rate = snd_rme32_capture_getrate(rme32, &dummy)) > 0) { /* AutoSync */ - runtime->hw.rates = snd_rme32_ratecode(rate); + runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate); runtime->hw.rate_min = rate; runtime->hw.rate_max = rate; } @@ -989,7 +976,7 @@ if (!isadat) { return -EIO; } - runtime->hw.rates = snd_rme32_ratecode(rate); + runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate); runtime->hw.rate_min = rate; runtime->hw.rate_max = rate; } @@ -1582,16 +1569,8 @@ * control interface */ -static int -snd_rme32_info_loopback_control(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_rme32_info_loopback_control snd_ctl_boolean_mono_info + static int snd_rme32_get_loopback_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff -Nur sound/pci/rme96.c sound/pci/rme96.c --- sound/pci/rme96.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/rme96.c 2007-08-14 02:00:08.000000000 +0200 @@ -301,20 +301,6 @@ } static int -snd_rme96_ratecode(int rate) -{ - switch (rate) { - case 32000: return SNDRV_PCM_RATE_32000; - case 44100: return SNDRV_PCM_RATE_44100; - case 48000: return SNDRV_PCM_RATE_48000; - case 64000: return SNDRV_PCM_RATE_64000; - case 88200: return SNDRV_PCM_RATE_88200; - case 96000: return SNDRV_PCM_RATE_96000; - } - return 0; -} - -static int snd_rme96_playback_silence(struct snd_pcm_substream *substream, int channel, /* not used (interleaved data) */ snd_pcm_uframes_t pos, @@ -1176,8 +1162,6 @@ struct rme96 *rme96 = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_set_sync(substream); - spin_lock_irq(&rme96->lock); if (rme96->playback_substream != NULL) { spin_unlock_irq(&rme96->lock); @@ -1194,7 +1178,7 @@ (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0) { /* slave clock */ - runtime->hw.rates = snd_rme96_ratecode(rate); + runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate); runtime->hw.rate_min = rate; runtime->hw.rate_max = rate; } @@ -1214,8 +1198,6 @@ struct rme96 *rme96 = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_set_sync(substream); - runtime->hw = snd_rme96_capture_spdif_info; if (snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG && (rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0) @@ -1223,7 +1205,7 @@ if (isadat) { return -EIO; } - runtime->hw.rates = snd_rme96_ratecode(rate); + runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate); runtime->hw.rate_min = rate; runtime->hw.rate_max = rate; } @@ -1247,8 +1229,6 @@ struct rme96 *rme96 = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_set_sync(substream); - spin_lock_irq(&rme96->lock); if (rme96->playback_substream != NULL) { spin_unlock_irq(&rme96->lock); @@ -1265,7 +1245,7 @@ (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0) { /* slave clock */ - runtime->hw.rates = snd_rme96_ratecode(rate); + runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate); runtime->hw.rate_min = rate; runtime->hw.rate_max = rate; } @@ -1280,8 +1260,6 @@ struct rme96 *rme96 = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_set_sync(substream); - runtime->hw = snd_rme96_capture_adat_info; if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) { /* makes no sense to use analog input. Note that analog @@ -1292,7 +1270,7 @@ if (!isadat) { return -EIO; } - runtime->hw.rates = snd_rme96_ratecode(rate); + runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate); runtime->hw.rate_min = rate; runtime->hw.rate_max = rate; } @@ -1826,15 +1804,8 @@ * control interface */ -static int -snd_rme96_info_loopback_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_rme96_info_loopback_control snd_ctl_boolean_mono_info + static int snd_rme96_get_loopback_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { diff -Nur sound/pci/rme9652/hdsp.c sound/pci/rme9652/hdsp.c --- sound/pci/rme9652/hdsp.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/rme9652/hdsp.c 2007-07-24 02:00:10.000000000 +0200 @@ -1623,14 +1623,7 @@ return 0; } -static int snd_hdsp_info_spdif_bits(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_hdsp_info_spdif_bits snd_ctl_boolean_mono_info static int snd_hdsp_get_spdif_out(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2111,14 +2104,7 @@ return change; } -static int snd_hdsp_info_clock_source_lock(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_hdsp_info_clock_source_lock snd_ctl_boolean_mono_info static int snd_hdsp_get_clock_source_lock(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2420,14 +2406,7 @@ return 0; } -static int snd_hdsp_info_xlr_breakout_cable(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_hdsp_info_xlr_breakout_cable snd_ctl_boolean_mono_info static int snd_hdsp_get_xlr_breakout_cable(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2483,14 +2462,7 @@ return 0; } -static int snd_hdsp_info_aeb(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_hdsp_info_aeb snd_ctl_boolean_mono_info static int snd_hdsp_get_aeb(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2729,14 +2701,7 @@ return 0; } -static int snd_hdsp_info_line_out(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_hdsp_info_line_out snd_ctl_boolean_mono_info static int snd_hdsp_get_line_out(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2782,14 +2747,7 @@ return 0; } -static int snd_hdsp_info_precise_pointer(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_hdsp_info_precise_pointer snd_ctl_boolean_mono_info static int snd_hdsp_get_precise_pointer(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2835,14 +2793,7 @@ return 0; } -static int snd_hdsp_info_use_midi_tasklet(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_hdsp_info_use_midi_tasklet snd_ctl_boolean_mono_info static int snd_hdsp_get_use_midi_tasklet(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { diff -Nur sound/pci/rme9652/hdspm.c sound/pci/rme9652/hdspm.c --- sound/pci/rme9652/hdspm.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/rme9652/hdspm.c 2007-07-28 02:00:08.000000000 +0200 @@ -1,5 +1,4 @@ -/* -*- linux-c -*- - * +/* * ALSA driver for RME Hammerfall DSP MADI audio interface(s) * * Copyright (c) 2003 Winfried Ritsch (IEM) @@ -78,7 +77,8 @@ "Enable Analog Out on Channel 63/64 by default."); MODULE_AUTHOR - ("Winfried Ritsch , Paul Davis , " + ("Winfried Ritsch , " + "Paul Davis , " "Marcus Andersson, Thomas Charbonnel , " "Remy Bruno "); MODULE_DESCRIPTION("RME HDSPM"); @@ -161,7 +161,9 @@ 0=off, 1=on */ /* MADI ONLY */ #define HDSPM_Dolby (1<<11) /* Dolby = "NonAudio" ?? */ /* AES32 ONLY */ -#define HDSPM_InputSelect0 (1<<14) /* Input select 0= optical, 1=coax */ /* MADI ONLY*/ +#define HDSPM_InputSelect0 (1<<14) /* Input select 0= optical, 1=coax + * -- MADI ONLY + */ #define HDSPM_InputSelect1 (1<<15) /* should be 0 */ #define HDSPM_SyncRef0 (1<<16) /* 0=WOrd, 1=MADI */ @@ -189,11 +191,13 @@ /* --- bit helper defines */ #define HDSPM_LatencyMask (HDSPM_Latency0|HDSPM_Latency1|HDSPM_Latency2) -#define HDSPM_FrequencyMask (HDSPM_Frequency0|HDSPM_Frequency1|HDSPM_DoubleSpeed|HDSPM_QuadSpeed) +#define HDSPM_FrequencyMask (HDSPM_Frequency0|HDSPM_Frequency1|\ + HDSPM_DoubleSpeed|HDSPM_QuadSpeed) #define HDSPM_InputMask (HDSPM_InputSelect0|HDSPM_InputSelect1) #define HDSPM_InputOptical 0 #define HDSPM_InputCoaxial (HDSPM_InputSelect0) -#define HDSPM_SyncRefMask (HDSPM_SyncRef0|HDSPM_SyncRef1|HDSPM_SyncRef2|HDSPM_SyncRef3) +#define HDSPM_SyncRefMask (HDSPM_SyncRef0|HDSPM_SyncRef1|\ + HDSPM_SyncRef2|HDSPM_SyncRef3) #define HDSPM_SyncRef_Word 0 #define HDSPM_SyncRef_MADI (HDSPM_SyncRef0) @@ -205,10 +209,12 @@ #define HDSPM_Frequency48KHz (HDSPM_Frequency1|HDSPM_Frequency0) #define HDSPM_Frequency64KHz (HDSPM_DoubleSpeed|HDSPM_Frequency0) #define HDSPM_Frequency88_2KHz (HDSPM_DoubleSpeed|HDSPM_Frequency1) -#define HDSPM_Frequency96KHz (HDSPM_DoubleSpeed|HDSPM_Frequency1|HDSPM_Frequency0) +#define HDSPM_Frequency96KHz (HDSPM_DoubleSpeed|HDSPM_Frequency1|\ + HDSPM_Frequency0) #define HDSPM_Frequency128KHz (HDSPM_QuadSpeed|HDSPM_Frequency0) #define HDSPM_Frequency176_4KHz (HDSPM_QuadSpeed|HDSPM_Frequency1) -#define HDSPM_Frequency192KHz (HDSPM_QuadSpeed|HDSPM_Frequency1|HDSPM_Frequency0) +#define HDSPM_Frequency192KHz (HDSPM_QuadSpeed|HDSPM_Frequency1|\ + HDSPM_Frequency0) /* --- for internal discrimination */ #define HDSPM_CLOCK_SOURCE_AUTOSYNC 0 /* Sample Clock Sources */ @@ -256,10 +262,14 @@ #define HDSPM_RD_MULTIPLE (1<<10) /* --- Status Register bits --- */ /* MADI ONLY */ /* Bits defined here and - that do not conflict with specific bits for AES32 seem to be valid also for the AES32 */ + that do not conflict with specific bits for AES32 seem to be valid also + for the AES32 + */ #define HDSPM_audioIRQPending (1<<0) /* IRQ is high and pending */ -#define HDSPM_RX_64ch (1<<1) /* Input 64chan. MODE=1, 56chn. MODE=0 */ -#define HDSPM_AB_int (1<<2) /* InputChannel Opt=0, Coax=1 (like inp0) */ +#define HDSPM_RX_64ch (1<<1) /* Input 64chan. MODE=1, 56chn MODE=0 */ +#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_BufferPositionMask 0x000FFC0 /* Bit 6..15 : h/w buffer pointer */ @@ -274,12 +284,15 @@ #define HDSPM_madiFreq2 (1<<24) /* 4=64, 5=88.2 6=96 */ #define HDSPM_madiFreq3 (1<<25) /* 7=128, 8=176.4 9=192 */ -#define HDSPM_BufferID (1<<26) /* (Double)Buffer ID toggles with Interrupt */ +#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 */ /* --- status bit helpers */ -#define HDSPM_madiFreqMask (HDSPM_madiFreq0|HDSPM_madiFreq1|HDSPM_madiFreq2|HDSPM_madiFreq3) +#define HDSPM_madiFreqMask (HDSPM_madiFreq0|HDSPM_madiFreq1|\ + HDSPM_madiFreq2|HDSPM_madiFreq3) #define HDSPM_madiFreq32 (HDSPM_madiFreq0) #define HDSPM_madiFreq44_1 (HDSPM_madiFreq1) #define HDSPM_madiFreq48 (HDSPM_madiFreq0|HDSPM_madiFreq1) @@ -319,10 +332,12 @@ #define HDSPM_wcFreq96 (HDSPM_wc_freq1|HDSPM_wc_freq2) -#define HDSPM_SelSyncRefMask (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|HDSPM_SelSyncRef2) +#define HDSPM_SelSyncRefMask (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|\ + HDSPM_SelSyncRef2) #define HDSPM_SelSyncRef_WORD 0 #define HDSPM_SelSyncRef_MADI (HDSPM_SelSyncRef0) -#define HDSPM_SelSyncRef_NVALID (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|HDSPM_SelSyncRef2) +#define HDSPM_SelSyncRef_NVALID (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|\ + HDSPM_SelSyncRef2) /* For AES32, bits for status, status2 and timecode are different @@ -412,8 +427,9 @@ struct hdspm { spinlock_t lock; - struct snd_pcm_substream *capture_substream; /* only one playback */ - struct snd_pcm_substream *playback_substream; /* and/or capture stream */ + /* only one playback and/or capture stream */ + struct snd_pcm_substream *capture_substream; + struct snd_pcm_substream *playback_substream; char *card_name; /* for procinfo */ unsigned short firmware_rev; /* dont know if relevant (yes if AES32)*/ @@ -460,9 +476,12 @@ struct pci_dev *pci; /* and an pci info */ /* Mixer vars */ - struct snd_kcontrol *playback_mixer_ctls[HDSPM_MAX_CHANNELS]; /* fast alsa mixer */ - struct snd_kcontrol *input_mixer_ctls[HDSPM_MAX_CHANNELS]; /* but input to much, so not used */ - struct hdspm_mixer *mixer; /* full mixer accessable over mixer ioctl or hwdep-device */ + /* fast alsa mixer */ + 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 accessable over mixer ioctl or hwdep-device */ + struct hdspm_mixer *mixer; }; @@ -616,13 +635,15 @@ 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 timecode = + hdspm_read(hdspm, HDSPM_timecodeRegister); int 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 >> @@ -668,7 +689,9 @@ } } - /* if rate detected and Syncref is Word than have it, word has priority to MADI */ + /* if rate detected and Syncref is Word than have it, + * word has priority to MADI + */ if (rate != 0 && (status2 & HDSPM_SelSyncRefMask) == HDSPM_SelSyncRef_WORD) return rate; @@ -727,12 +750,12 @@ position = hdspm_read(hdspm, HDSPM_statusRegister); - if (!hdspm->precise_ptr) { - return (position & HDSPM_BufferID) ? (hdspm->period_bytes / - 4) : 0; - } + if (!hdspm->precise_ptr) + return (position & HDSPM_BufferID) ? + (hdspm->period_bytes / 4) : 0; - /* hwpointer comes in bytes and is 64Bytes accurate (by docu since PCI Burst) + /* 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 @@ -808,10 +831,10 @@ rate /= 2; /* RME says n = 104857600000000, but in the windows MADI driver, I see: -// return 104857600000000 / rate; // 100 MHz + return 104857600000000 / rate; // 100 MHz return 110100480000000 / rate; // 105 MHz */ - //n = 104857600000000ULL; /* = 2^20 * 10^8 */ + /* n = 104857600000000ULL; */ /* = 2^20 * 10^8 */ n = 110100480000000ULL; /* Value checked for AES32 and MADI */ div64_32(&n, rate, &r); /* n should be less than 2^32 for being written to FREQ register */ @@ -841,8 +864,9 @@ 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"); + snd_printk(KERN_WARNING "HDSPM: " + "Warning: device is not running " + "as a clock master.\n"); not_set = 1; } else { @@ -850,16 +874,18 @@ int external_freq = hdspm_external_sample_rate(hdspm); - if ((hdspm_autosync_ref(hdspm) == - HDSPM_AUTOSYNC_FROM_NONE)) { + if (hdspm_autosync_ref(hdspm) == + HDSPM_AUTOSYNC_FROM_NONE) { - snd_printk(KERN_WARNING "HDSPM: Detected no Externel Sync \n"); + snd_printk(KERN_WARNING "HDSPM: " + "Detected no Externel Sync \n"); not_set = 1; } else if (rate != external_freq) { - snd_printk - (KERN_WARNING "HDSPM: Warning: No AutoSync source for requested rate\n"); + snd_printk(KERN_WARNING "HDSPM: " + "Warning: No AutoSync source for " + "requested rate\n"); not_set = 1; } } @@ -934,7 +960,9 @@ if (reject_if_open && (hdspm->capture_pid >= 0 || hdspm->playback_pid >= 0)) { snd_printk - (KERN_ERR "HDSPM: cannot change between single- and double-speed mode (capture PID = %d, playback PID = %d)\n", + (KERN_ERR "HDSPM: " + "cannot change between single- and double-speed mode " + "(capture PID = %d, playback PID = %d)\n", hdspm->capture_pid, hdspm->playback_pid); return -EBUSY; } @@ -966,8 +994,14 @@ static void all_in_all_mixer(struct hdspm * hdspm, int sgain) { int i, j; - unsigned int gain = - (sgain > UNITY_GAIN) ? UNITY_GAIN : (sgain < 0) ? 0 : sgain; + unsigned int gain; + + if (sgain > UNITY_GAIN) + gain = UNITY_GAIN; + else if (sgain < 0) + gain = 0; + else + gain = sgain; for (i = 0; i < HDSPM_MIXER_CHANNELS; i++) for (j = 0; j < HDSPM_MIXER_CHANNELS; j++) { @@ -980,7 +1014,8 @@ MIDI ----------------------------------------------------------------------------*/ -static inline unsigned char snd_hdspm_midi_read_byte (struct hdspm *hdspm, int id) +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) @@ -989,7 +1024,8 @@ return hdspm_read(hdspm, HDSPM_midiDataIn0); } -static inline void snd_hdspm_midi_write_byte (struct hdspm *hdspm, int id, int val) +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) @@ -1011,9 +1047,10 @@ int fifo_bytes_used; if (id) - fifo_bytes_used = hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xff; + fifo_bytes_used = hdspm_read(hdspm, HDSPM_midiStatusOut1); else - fifo_bytes_used = hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xff; + fifo_bytes_used = hdspm_read(hdspm, HDSPM_midiStatusOut0); + fifo_bytes_used &= 0xff; if (fifo_bytes_used < 128) return 128 - fifo_bytes_used; @@ -1038,16 +1075,21 @@ /* Output is not interrupt driven */ spin_lock_irqsave (&hmidi->lock, flags); - if (hmidi->output) { - if (!snd_rawmidi_transmit_empty (hmidi->output)) { - if ((n_pending = snd_hdspm_midi_output_possible (hmidi->hdspm, hmidi->id)) > 0) { - if (n_pending > (int)sizeof (buf)) - n_pending = sizeof (buf); - - if ((to_write = snd_rawmidi_transmit (hmidi->output, buf, n_pending)) > 0) { - for (i = 0; i < to_write; ++i) - snd_hdspm_midi_write_byte (hmidi->hdspm, hmidi->id, buf[i]); - } + if (hmidi->output && + !snd_rawmidi_transmit_empty (hmidi->output)) { + n_pending = snd_hdspm_midi_output_possible (hmidi->hdspm, + hmidi->id); + 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) + snd_hdspm_midi_write_byte (hmidi->hdspm, + hmidi->id, + buf[i]); } } } @@ -1057,51 +1099,55 @@ static int snd_hdspm_midi_input_read (struct hdspm_midi *hmidi) { - unsigned char buf[128]; /* this buffer is designed to match the MIDI input FIFO size */ + unsigned char buf[128]; /* this buffer is designed to match the MIDI + * input FIFO size + */ unsigned long flags; int n_pending; int i; spin_lock_irqsave (&hmidi->lock, flags); - if ((n_pending = snd_hdspm_midi_input_available (hmidi->hdspm, hmidi->id)) > 0) { + n_pending = snd_hdspm_midi_input_available (hmidi->hdspm, hmidi->id); + if (n_pending > 0) { if (hmidi->input) { - if (n_pending > (int)sizeof (buf)) { + if (n_pending > (int)sizeof (buf)) n_pending = sizeof (buf); - } - for (i = 0; i < n_pending; ++i) { - buf[i] = snd_hdspm_midi_read_byte (hmidi->hdspm, hmidi->id); - } - if (n_pending) { - snd_rawmidi_receive (hmidi->input, buf, n_pending); - } + for (i = 0; i < n_pending; ++i) + buf[i] = snd_hdspm_midi_read_byte (hmidi->hdspm, + hmidi->id); + if (n_pending) + snd_rawmidi_receive (hmidi->input, buf, + n_pending); } else { /* flush the MIDI input FIFO */ - while (n_pending--) { - snd_hdspm_midi_read_byte (hmidi->hdspm, hmidi->id); - } + while (n_pending--) + snd_hdspm_midi_read_byte (hmidi->hdspm, + hmidi->id); } } hmidi->pending = 0; - if (hmidi->id) { + if (hmidi->id) hmidi->hdspm->control_register |= HDSPM_Midi1InterruptEnable; - } else { + else hmidi->hdspm->control_register |= HDSPM_Midi0InterruptEnable; - } - hdspm_write(hmidi->hdspm, HDSPM_controlRegister, hmidi->hdspm->control_register); + hdspm_write(hmidi->hdspm, HDSPM_controlRegister, + hmidi->hdspm->control_register); spin_unlock_irqrestore (&hmidi->lock, flags); return snd_hdspm_midi_output_write (hmidi); } -static void snd_hdspm_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) +static void +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 = (struct hdspm_midi *) substream->rmidi->private_data; + hmidi = substream->rmidi->private_data; hdspm = hmidi->hdspm; - ie = hmidi->id ? HDSPM_Midi1InterruptEnable : HDSPM_Midi0InterruptEnable; + ie = hmidi->id ? + HDSPM_Midi1InterruptEnable : HDSPM_Midi0InterruptEnable; spin_lock_irqsave (&hdspm->lock, flags); if (up) { if (!(hdspm->control_register & ie)) { @@ -1138,12 +1184,13 @@ spin_unlock_irqrestore (&hmidi->lock, flags); } -static void snd_hdspm_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) +static void +snd_hdspm_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) { struct hdspm_midi *hmidi; unsigned long flags; - hmidi = (struct hdspm_midi *) substream->rmidi->private_data; + hmidi = substream->rmidi->private_data; spin_lock_irqsave (&hmidi->lock, flags); if (up) { if (!hmidi->istimer) { @@ -1155,9 +1202,8 @@ hmidi->istimer++; } } else { - if (hmidi->istimer && --hmidi->istimer <= 0) { + if (hmidi->istimer && --hmidi->istimer <= 0) del_timer (&hmidi->timer); - } } spin_unlock_irqrestore (&hmidi->lock, flags); if (up) @@ -1168,7 +1214,7 @@ { struct hdspm_midi *hmidi; - hmidi = (struct hdspm_midi *) substream->rmidi->private_data; + hmidi = substream->rmidi->private_data; spin_lock_irq (&hmidi->lock); snd_hdspm_flush_midi_input (hmidi->hdspm, hmidi->id); hmidi->input = substream; @@ -1181,7 +1227,7 @@ { struct hdspm_midi *hmidi; - hmidi = (struct hdspm_midi *) substream->rmidi->private_data; + hmidi = substream->rmidi->private_data; spin_lock_irq (&hmidi->lock); hmidi->output = substream; spin_unlock_irq (&hmidi->lock); @@ -1195,7 +1241,7 @@ snd_hdspm_midi_input_trigger (substream, 0); - hmidi = (struct hdspm_midi *) substream->rmidi->private_data; + hmidi = substream->rmidi->private_data; spin_lock_irq (&hmidi->lock); hmidi->input = NULL; spin_unlock_irq (&hmidi->lock); @@ -1209,7 +1255,7 @@ snd_hdspm_midi_output_trigger (substream, 0); - hmidi = (struct hdspm_midi *) substream->rmidi->private_data; + hmidi = substream->rmidi->private_data; spin_lock_irq (&hmidi->lock); hmidi->output = NULL; spin_unlock_irq (&hmidi->lock); @@ -1231,29 +1277,28 @@ .trigger = snd_hdspm_midi_input_trigger, }; -static int __devinit snd_hdspm_create_midi (struct snd_card *card, struct hdspm *hdspm, int id) +static int __devinit snd_hdspm_create_midi (struct snd_card *card, + struct hdspm *hdspm, int id) { int err; char buf[32]; hdspm->midi[id].id = id; - hdspm->midi[id].rmidi = NULL; - hdspm->midi[id].input = NULL; - hdspm->midi[id].output = NULL; hdspm->midi[id].hdspm = hdspm; - hdspm->midi[id].istimer = 0; - hdspm->midi[id].pending = 0; spin_lock_init (&hdspm->midi[id].lock); sprintf (buf, "%s MIDI %d", card->shortname, id+1); - if ((err = snd_rawmidi_new (card, buf, id, 1, 1, &hdspm->midi[id].rmidi)) < 0) + err = snd_rawmidi_new (card, buf, id, 1, 1, &hdspm->midi[id].rmidi); + if (err < 0) return err; 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); + 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 | @@ -1637,7 +1682,8 @@ hdspm->control_register |= HDSPM_SyncRef2+HDSPM_SyncRef1; break; case 7: - hdspm->control_register |= HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0; + hdspm->control_register |= + HDSPM_SyncRef2+HDSPM_SyncRef1+HDSPM_SyncRef0; break; case 8: hdspm->control_register |= HDSPM_SyncRef3; @@ -1675,7 +1721,8 @@ uinfo->value.enumerated.items = 9; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + if (uinfo->value.enumerated.item >= + uinfo->value.enumerated.items) uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; strcpy(uinfo->value.enumerated.name, @@ -1688,7 +1735,8 @@ uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + if (uinfo->value.enumerated.item >= + uinfo->value.enumerated.items) uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; strcpy(uinfo->value.enumerated.name, @@ -1740,7 +1788,8 @@ { if (hdspm->is_aes32) { 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) @@ -1777,20 +1826,20 @@ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = 10; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + 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 - { + } else { static char *texts[] = { "WordClock", "MADI", "None" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + if (uinfo->value.enumerated.item >= + uinfo->value.enumerated.items) uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; strcpy(uinfo->value.enumerated.name, @@ -1834,15 +1883,7 @@ return 0; } -static int snd_hdspm_info_line_out(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_hdspm_info_line_out snd_ctl_boolean_mono_info static int snd_hdspm_get_line_out(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1897,15 +1938,7 @@ return 0; } -static int snd_hdspm_info_tx_64(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_hdspm_info_tx_64 snd_ctl_boolean_mono_info static int snd_hdspm_get_tx_64(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1960,15 +1993,7 @@ return 0; } -static int snd_hdspm_info_c_tms(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_hdspm_info_c_tms snd_ctl_boolean_mono_info static int snd_hdspm_get_c_tms(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2023,15 +2048,7 @@ return 0; } -static int snd_hdspm_info_safe_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_hdspm_info_safe_mode snd_ctl_boolean_mono_info static int snd_hdspm_get_safe_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2086,15 +2103,7 @@ return 0; } -static int snd_hdspm_info_emphasis(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_hdspm_info_emphasis snd_ctl_boolean_mono_info static int snd_hdspm_get_emphasis(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2149,15 +2158,7 @@ return 0; } -static int snd_hdspm_info_dolby(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_hdspm_info_dolby snd_ctl_boolean_mono_info static int snd_hdspm_get_dolby(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2212,15 +2213,7 @@ return 0; } -static int snd_hdspm_info_professional(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_hdspm_info_professional snd_ctl_boolean_mono_info static int snd_hdspm_get_professional(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2472,7 +2465,7 @@ if (val > 2) val = 2; spin_lock_irq(&hdspm->lock); - change = (int) val != hdspm_qs_wire(hdspm); + change = val != hdspm_qs_wire(hdspm); hdspm_set_qs_wire(hdspm, val); spin_unlock_irq(&hdspm->lock); return change; @@ -2573,8 +2566,8 @@ source - HDSPM_MAX_CHANNELS); else - change = - gain != hdspm_read_in_gain(hdspm, destination, source); + change = gain != hdspm_read_in_gain(hdspm, destination, + source); if (change) { if (source >= HDSPM_MAX_CHANNELS) @@ -2627,7 +2620,8 @@ snd_assert(channel >= 0 || channel < HDSPM_MAX_CHANNELS, return -EINVAL); - if ((mapped_channel = hdspm->channel_map[channel]) < 0) + mapped_channel = hdspm->channel_map[channel]; + if (mapped_channel < 0) return -EINVAL; spin_lock_irq(&hdspm->lock); @@ -2635,10 +2629,12 @@ hdspm_read_pb_gain(hdspm, mapped_channel, mapped_channel); 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]); - */ - + /* + 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; } @@ -2659,7 +2655,8 @@ snd_assert(channel >= 0 || channel < HDSPM_MAX_CHANNELS, return -EINVAL); - if ((mapped_channel = hdspm->channel_map[channel]) < 0) + mapped_channel = hdspm->channel_map[channel]; + if (mapped_channel < 0) return -EINVAL; gain = ucontrol->value.integer.value[0]; @@ -2909,28 +2906,26 @@ } /* 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 accesible via special IOCTL on hwdep -and the mixer 2dimensional mixer control */ + Note: the whole matrix would be 128*HDSPM_MIXER_CHANNELS Faders, + thats too * big for any alsamixer they are accesible via special + IOCTL on hwdep and the mixer 2dimensional mixer control + */ 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 ... + /* 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 ... */ for (idx = 0; idx < limit; ++idx) { snd_hdspm_playback_mixer.index = idx + 1; - if ((err = snd_ctl_add(card, - kctl = - snd_ctl_new1 - (&snd_hdspm_playback_mixer, - hdspm)))) { + kctl = snd_ctl_new1(&snd_hdspm_playback_mixer, hdspm); + err = snd_ctl_add(card, kctl); + if (err < 0) return err; - } hdspm->playback_mixer_ctls[idx] = kctl; } @@ -2945,7 +2940,7 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry, struct snd_info_buffer *buffer) { - struct hdspm *hdspm = (struct hdspm *) entry->private_data; + struct hdspm *hdspm = entry->private_data; unsigned int status; unsigned int status2; char *pref_sync_ref; @@ -2978,14 +2973,14 @@ (status & HDSPM_midi1IRQPending) ? 1 : 0, hdspm->irq_count); snd_iprintf(buffer, - "HW pointer: id = %d, rawptr = %d (%d->%d) estimated= %ld (bytes)\n", + "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), + (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, @@ -2995,24 +2990,22 @@ 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", + "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)); + x = 1 << (6 + hdspm_decode_latency(hdspm->control_register & + 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", - (hdspm-> - control_register & HDSPM_LineOut) ? "on " : "off", + (hdspm->control_register & HDSPM_LineOut) ? "on " : "off", (hdspm->precise_ptr) ? "on" : "off"); switch (hdspm->control_register & HDSPM_InputMask) { @@ -3040,7 +3033,8 @@ syncref); snd_iprintf(buffer, - "ClearTrackMarker = %s, Transmit in %s Channel Mode, Auto Input %s\n", + "ClearTrackMarker = %s, Transmit in %s Channel Mode, " + "Auto Input %s\n", (hdspm-> control_register & HDSPM_clr_tms) ? "on" : "off", (hdspm-> @@ -3141,7 +3135,7 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry, struct snd_info_buffer *buffer) { - struct hdspm *hdspm = (struct hdspm *) entry->private_data; + struct hdspm *hdspm = entry->private_data; unsigned int status; unsigned int status2; unsigned int timecode; @@ -3171,14 +3165,14 @@ (status & HDSPM_midi1IRQPending) ? 1 : 0, hdspm->irq_count); snd_iprintf(buffer, - "HW pointer: id = %d, rawptr = %d (%d->%d) estimated= %ld (bytes)\n", + "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), + (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, @@ -3188,16 +3182,15 @@ 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", + "Register: ctrl1=0x%x, status1=0x%x, status2=0x%x, " + "timecode=0x%x\n", hdspm->control_register, status, status2, timecode); snd_iprintf(buffer, "--- Settings ---\n"); - x = 1 << (6 + - hdspm_decode_latency(hdspm-> - control_register & - HDSPM_LatencyMask)); + x = 1 << (6 + hdspm_decode_latency(hdspm->control_register & + HDSPM_LatencyMask)); snd_iprintf(buffer, "Size (Latency): %d samples (2 periods of %lu bytes)\n", @@ -3280,14 +3273,15 @@ snd_iprintf(buffer, "--- Status:\n"); snd_iprintf(buffer, "Word: %s Frequency: %d\n", - (status & HDSPM_AES32_wcLock)? "Sync " : "No Lock", - HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) & 0xF)); + (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", - HDSPM_bit2freq((timecode >> (4*x)) & 0xF)); + x+1, + (status2 & (HDSPM_LockAES >> x)) ? + "Sync ": "No Lock", + HDSPM_bit2freq((timecode >> (4*x)) & 0xF)); } switch (hdspm_autosync_ref(hdspm)) { @@ -3313,12 +3307,11 @@ snd_hdspm_proc_read_debug(struct snd_info_entry * entry, struct snd_info_buffer *buffer) { - struct hdspm *hdspm = (struct hdspm *)entry->private_data; + struct hdspm *hdspm = entry->private_data; int j,i; - for (i = 0; i < 256 /* 1024*64 */; i += j) - { + for (i = 0; i < 256 /* 1024*64 */; i += j) { snd_iprintf(buffer, "0x%08X: ", i); for (j = 0; j < 16; j += 4) snd_iprintf(buffer, "%08X ", hdspm_read(hdspm, i + j)); @@ -3361,14 +3354,20 @@ /* set defaults: */ if (hdspm->is_aes32) - hdspm->control_register = HDSPM_ClockModeMaster | /* Master Cloack Mode on */ - hdspm_encode_latency(7) | /* latency maximum = 8192 samples */ + hdspm->control_register = + HDSPM_ClockModeMaster | /* Master Cloack Mode on */ + hdspm_encode_latency(7) | /* latency maximum = + * 8192 samples + */ 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->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 */ @@ -3399,7 +3398,8 @@ if (line_outs_monitor[hdspm->dev]) { - snd_printk(KERN_INFO "HDSPM: sending all playback streams to line outs.\n"); + 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)) @@ -3448,20 +3448,16 @@ if (audio) { if (hdspm->capture_substream) - snd_pcm_period_elapsed(hdspm->pcm-> - streams - [SNDRV_PCM_STREAM_CAPTURE]. - substream); + snd_pcm_period_elapsed(hdspm->capture_substream); if (hdspm->playback_substream) - snd_pcm_period_elapsed(hdspm->pcm-> - streams - [SNDRV_PCM_STREAM_PLAYBACK]. - substream); + snd_pcm_period_elapsed(hdspm->playback_substream); } if (midi0 && midi0status) { - /* we disable interrupts for this input until processing is done */ + /* we disable interrupts for this input until processing + * is done + */ hdspm->control_register &= ~HDSPM_Midi0InterruptEnable; hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); @@ -3469,7 +3465,9 @@ schedule = 1; } if (midi1 && midi1status) { - /* we disable interrupts for this input until processing is done */ + /* we disable interrupts for this input until processing + * is done + */ hdspm->control_register &= ~HDSPM_Midi1InterruptEnable; hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); @@ -3501,16 +3499,16 @@ snd_assert(channel >= 0 || channel < HDSPM_MAX_CHANNELS, return NULL); - if ((mapped_channel = hdspm->channel_map[channel]) < 0) + mapped_channel = hdspm->channel_map[channel]; + if (mapped_channel < 0) return NULL; - if (stream == SNDRV_PCM_STREAM_CAPTURE) { + if (stream == SNDRV_PCM_STREAM_CAPTURE) return hdspm->capture_buffer + mapped_channel * HDSPM_CHANNEL_BUFFER_BYTES; - } else { + else return hdspm->playback_buffer + mapped_channel * HDSPM_CHANNEL_BUFFER_BYTES; - } } @@ -3525,9 +3523,9 @@ snd_assert(pos + count <= HDSPM_CHANNEL_BUFFER_BYTES / 4, return -EINVAL); - channel_buf = hdspm_channel_buffer_location(hdspm, - substream->pstr-> - stream, channel); + channel_buf = + hdspm_channel_buffer_location(hdspm, substream->pstr->stream, + channel); snd_assert(channel_buf != NULL, return -EIO); @@ -3544,9 +3542,9 @@ snd_assert(pos + count <= HDSPM_CHANNEL_BUFFER_BYTES / 4, return -EINVAL); - channel_buf = hdspm_channel_buffer_location(hdspm, - substream->pstr-> - stream, channel); + channel_buf = + hdspm_channel_buffer_location(hdspm, substream->pstr->stream, + channel); snd_assert(channel_buf != NULL, return -EIO); return copy_to_user(dst, channel_buf + pos * 4, count * 4); } @@ -3559,8 +3557,8 @@ char *channel_buf; channel_buf = - hdspm_channel_buffer_location(hdspm, substream->pstr->stream, - channel); + hdspm_channel_buffer_location(hdspm, substream->pstr->stream, + channel); snd_assert(channel_buf != NULL, return -EIO); memset(channel_buf + pos * 4, 0, count * 4); return 0; @@ -3616,7 +3614,7 @@ other_pid = hdspm->playback_pid; } - if ((other_pid > 0) && (this_pid != other_pid)) { + if (other_pid > 0 && this_pid != other_pid) { /* The other stream is open, and not by the same task as this one. Make sure that the parameters @@ -3633,7 +3631,7 @@ 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; } @@ -3644,7 +3642,8 @@ /* how to make sure that the rate matches an externally-set one ? */ spin_lock_irq(&hdspm->lock); - if ((err = hdspm_set_rate(hdspm, params_rate(params), 0)) < 0) { + err = hdspm_set_rate(hdspm, params_rate(params), 0); + if (err < 0) { spin_unlock_irq(&hdspm->lock); _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE); @@ -3652,16 +3651,17 @@ } spin_unlock_irq(&hdspm->lock); - if ((err = - hdspm_set_interrupt_interval(hdspm, - params_period_size(params))) < - 0) { + err = hdspm_set_interrupt_interval(hdspm, + params_period_size(params)); + if (err < 0) { _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); return err; } - /* Memory allocation, takashi's method, dont know if we should spinlock */ + /* Memory allocation, takashi's method, dont know if we should + * spinlock + */ /* 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 */ @@ -3746,7 +3746,8 @@ snd_assert(info->channel < HDSPM_MAX_CHANNELS, return -EINVAL); - if ((mapped_channel = hdspm->channel_map[info->channel]) < 0) + mapped_channel = hdspm->channel_map[info->channel]; + if (mapped_channel < 0) return -EINVAL; info->offset = mapped_channel * HDSPM_CHANNEL_BUFFER_BYTES; @@ -3760,15 +3761,13 @@ { switch (cmd) { case SNDRV_PCM_IOCTL1_RESET: - { - return snd_hdspm_reset(substream); - } + 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; } @@ -3979,9 +3978,12 @@ } -static unsigned int hdspm_aes32_sample_rates[] = { 32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000 }; +static unsigned int hdspm_aes32_sample_rates[] = { + 32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000 +}; -static struct snd_pcm_hw_constraint_list hdspm_hw_constraints_aes32_sample_rates = { +static struct snd_pcm_hw_constraint_list +hdspm_hw_constraints_aes32_sample_rates = { .count = ARRAY_SIZE(hdspm_aes32_sample_rates), .list = hdspm_aes32_sample_rates, .mask = 0 @@ -4107,7 +4109,7 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep * hw, struct file *file, unsigned int cmd, unsigned long arg) { - struct hdspm *hdspm = (struct hdspm *) hw->private_data; + struct hdspm *hdspm = hw->private_data; struct hdspm_mixer_ioctl mixer; struct hdspm_config_info info; struct hdspm_version hdspm_version; @@ -4115,11 +4117,12 @@ switch (cmd) { - case SNDRV_HDSPM_IOCTL_GET_PEAK_RMS: if (copy_from_user(&rms, (void __user *)arg, sizeof(rms))) return -EFAULT; - /* maybe there is a chance to memorymap in future so dont touch just copy */ + /* 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 ) @@ -4131,21 +4134,16 @@ case SNDRV_HDSPM_IOCTL_GET_CONFIG_INFO: spin_lock_irq(&hdspm->lock); - info.pref_sync_ref = - (unsigned char) hdspm_pref_sync_ref(hdspm); - info.wordclock_sync_check = - (unsigned char) hdspm_wc_sync_check(hdspm); + info.pref_sync_ref = hdspm_pref_sync_ref(hdspm); + info.wordclock_sync_check = hdspm_wc_sync_check(hdspm); info.system_sample_rate = hdspm->system_sample_rate; info.autosync_sample_rate = hdspm_external_sample_rate(hdspm); - info.system_clock_mode = - (unsigned char) hdspm_system_clock_mode(hdspm); - info.clock_source = - (unsigned char) hdspm_clock_source(hdspm); - info.autosync_ref = - (unsigned char) hdspm_autosync_ref(hdspm); - info.line_out = (unsigned char) hdspm_line_out(hdspm); + info.system_clock_mode = hdspm_system_clock_mode(hdspm); + info.clock_source = hdspm_clock_source(hdspm); + info.autosync_ref = hdspm_autosync_ref(hdspm); + info.line_out = hdspm_line_out(hdspm); info.passthru = 0; spin_unlock_irq(&hdspm->lock); if (copy_to_user((void __user *) arg, &info, sizeof(info))) @@ -4162,8 +4160,8 @@ case SNDRV_HDSPM_IOCTL_GET_MIXER: 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))) + if (copy_to_user((void __user *)mixer.mixer, hdspm->mixer, + sizeof(struct hdspm_mixer))) return -EFAULT; break; @@ -4206,7 +4204,8 @@ struct snd_hwdep *hw; int err; - if ((err = snd_hwdep_new(card, "HDSPM hwdep", 0, &hw)) < 0) + err = snd_hwdep_new(card, "HDSPM hwdep", 0, &hw); + if (err < 0) return err; hdspm->hwdep = hw; @@ -4232,15 +4231,15 @@ pcm = hdspm->pcm; -/* wanted = HDSPM_DMA_AREA_BYTES + 4096;*/ /* dont know why, but it works */ wanted = HDSPM_DMA_AREA_BYTES; - if ((err = + err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(hdspm->pci), wanted, - wanted)) < 0) { + wanted); + if (err < 0) { snd_printdd("Could not preallocate %zd Bytes\n", wanted); return err; @@ -4256,8 +4255,7 @@ int i; for (i = 0; i < (channels * 16); i++) hdspm_write(hdspm, reg + 4 * i, - snd_pcm_sgbuf_get_addr(sgbuf, - (size_t) 4096 * i)); + snd_pcm_sgbuf_get_addr(sgbuf, (size_t) 4096 * i)); } /* ------------- ALSA Devices ---------------------------- */ @@ -4267,7 +4265,8 @@ struct snd_pcm *pcm; int err; - if ((err = snd_pcm_new(card, hdspm->card_name, 0, 1, 1, &pcm)) < 0) + err = snd_pcm_new(card, hdspm->card_name, 0, 1, 1, &pcm); + if (err < 0) return err; hdspm->pcm = pcm; @@ -4281,7 +4280,8 @@ pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; - if ((err = snd_hdspm_preallocate_memory(hdspm)) < 0) + err = snd_hdspm_preallocate_memory(hdspm); + if (err < 0) return err; return 0; @@ -4299,19 +4299,24 @@ int err; snd_printdd("Create card...\n"); - if ((err = snd_hdspm_create_pcm(card, hdspm)) < 0) + err = snd_hdspm_create_pcm(card, hdspm); + if (err < 0) return err; - if ((err = snd_hdspm_create_midi(card, hdspm, 0)) < 0) + err = snd_hdspm_create_midi(card, hdspm, 0); + if (err < 0) return err; - if ((err = snd_hdspm_create_midi(card, hdspm, 1)) < 0) + err = snd_hdspm_create_midi(card, hdspm, 1); + if (err < 0) return err; - if ((err = snd_hdspm_create_controls(card, hdspm)) < 0) + err = snd_hdspm_create_controls(card, hdspm); + if (err < 0) return err; - if ((err = snd_hdspm_create_hwdep(card, hdspm)) < 0) + err = snd_hdspm_create_hwdep(card, hdspm); + if (err < 0) return err; snd_printdd("proc init...\n"); @@ -4326,7 +4331,8 @@ hdspm->playback_substream = NULL; snd_printdd("Set defaults...\n"); - if ((err = snd_hdspm_set_defaults(hdspm)) < 0) + err = snd_hdspm_set_defaults(hdspm); + if (err < 0) return err; snd_printdd("Update mixer controls...\n"); @@ -4334,7 +4340,8 @@ snd_printdd("Initializeing complete ???\n"); - if ((err = snd_card_register(card)) < 0) { + err = snd_card_register(card); + if (err < 0) { snd_printk(KERN_ERR "HDSPM: error registering card\n"); return err; } @@ -4344,36 +4351,18 @@ return 0; } -static int __devinit snd_hdspm_create(struct snd_card *card, struct hdspm * hdspm, +static int __devinit snd_hdspm_create(struct snd_card *card, + struct hdspm *hdspm, int precise_ptr, int enable_monitor) { struct pci_dev *pci = hdspm->pci; int err; - int i; - unsigned long io_extent; hdspm->irq = -1; - hdspm->irq_count = 0; - hdspm->midi[0].rmidi = NULL; - hdspm->midi[1].rmidi = NULL; - hdspm->midi[0].input = NULL; - hdspm->midi[1].input = NULL; - hdspm->midi[0].output = NULL; - hdspm->midi[1].output = NULL; spin_lock_init(&hdspm->midi[0].lock); spin_lock_init(&hdspm->midi[1].lock); - hdspm->iobase = NULL; - hdspm->control_register = 0; - hdspm->control2_register = 0; - - hdspm->playback_buffer = NULL; - hdspm->capture_buffer = NULL; - - for (i = 0; i < HDSPM_MAX_CHANNELS; ++i) - hdspm->playback_mixer_ctls[i] = NULL; - hdspm->mixer = NULL; hdspm->card = card; @@ -4396,12 +4385,14 @@ hdspm->card_name = "RME HDSPM MADI"; } - if ((err = pci_enable_device(pci)) < 0) + err = pci_enable_device(pci); + if (err < 0) return err; pci_set_master(hdspm->pci); - if ((err = pci_request_regions(pci, "hdspm")) < 0) + err = pci_request_regions(pci, "hdspm"); + if (err < 0) return err; hdspm->port = pci_resource_start(pci, 0); @@ -4411,8 +4402,10 @@ hdspm->port, hdspm->port + io_extent - 1); - if ((hdspm->iobase = ioremap_nocache(hdspm->port, io_extent)) == NULL) { - snd_printk(KERN_ERR "HDSPM: unable to remap region 0x%lx-0x%lx\n", + 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); return -EBUSY; } @@ -4435,9 +4428,10 @@ snd_printdd("kmalloc Mixer memory of %zd Bytes\n", sizeof(struct hdspm_mixer)); - if ((hdspm->mixer = kmalloc(sizeof(struct hdspm_mixer), GFP_KERNEL)) - == NULL) { - snd_printk(KERN_ERR "HDSPM: unable to kmalloc Mixer memory of %d Bytes\n", + 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)); return err; } @@ -4447,7 +4441,8 @@ hdspm->qs_channels = MADI_QS_CHANNELS; snd_printdd("create alsa devices.\n"); - if ((err = snd_hdspm_create_alsa_devices(card, hdspm)) < 0) + err = snd_hdspm_create_alsa_devices(card, hdspm); + if (err < 0) return err; snd_hdspm_initialize_midi_flush(hdspm); @@ -4462,9 +4457,8 @@ /* stop th audio, and cancel all interrupts */ hdspm->control_register &= - ~(HDSPM_Start | HDSPM_AudioInterruptEnable - | HDSPM_Midi0InterruptEnable | - HDSPM_Midi1InterruptEnable); + ~(HDSPM_Start | HDSPM_AudioInterruptEnable | + HDSPM_Midi0InterruptEnable | HDSPM_Midi1InterruptEnable); hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register); } @@ -4472,7 +4466,6 @@ if (hdspm->irq >= 0) free_irq(hdspm->irq, (void *) hdspm); - kfree(hdspm->mixer); if (hdspm->iobase) @@ -4487,7 +4480,7 @@ static void snd_hdspm_card_free(struct snd_card *card) { - struct hdspm *hdspm = (struct hdspm *) card->private_data; + struct hdspm *hdspm = card->private_data; if (hdspm) snd_hdspm_free(hdspm); @@ -4508,20 +4501,21 @@ return -ENOENT; } - if (!(card = snd_card_new(index[dev], id[dev], - THIS_MODULE, sizeof(struct hdspm)))) + card = snd_card_new(index[dev], id[dev], + THIS_MODULE, sizeof(struct hdspm)); + if (!card) return -ENOMEM; - hdspm = (struct hdspm *) card->private_data; + hdspm = card->private_data; card->private_free = snd_hdspm_card_free; hdspm->dev = dev; hdspm->pci = pci; snd_card_set_dev(card, &pci->dev); - if ((err = - snd_hdspm_create(card, hdspm, precise_ptr[dev], - enable_monitor[dev])) < 0) { + err = snd_hdspm_create(card, hdspm, precise_ptr[dev], + enable_monitor[dev]); + if (err < 0) { snd_card_free(card); return err; } @@ -4530,7 +4524,8 @@ sprintf(card->longname, "%s at 0x%lx, irq %d", hdspm->card_name, hdspm->port, hdspm->irq); - if ((err = snd_card_register(card)) < 0) { + err = snd_card_register(card); + if (err < 0) { snd_card_free(card); return err; } diff -Nur sound/pci/rme9652/rme9652.c sound/pci/rme9652/rme9652.c --- sound/pci/rme9652/rme9652.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/rme9652/rme9652.c 2007-07-24 02:00:10.000000000 +0200 @@ -406,7 +406,7 @@ } else if (!frag) return 0; offset -= rme9652->max_jitter; - if (offset < 0) + if ((int)offset < 0) offset += period_size * 2; } else { if (offset > period_size + rme9652->max_jitter) { @@ -1067,14 +1067,7 @@ return 0; } -static int snd_rme9652_info_spdif_out(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_rme9652_info_spdif_out snd_ctl_boolean_mono_info static int snd_rme9652_get_spdif_out(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1338,14 +1331,7 @@ .put = snd_rme9652_put_passthru, \ .get = snd_rme9652_get_passthru } -static int snd_rme9652_info_passthru(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_rme9652_info_passthru snd_ctl_boolean_mono_info static int snd_rme9652_get_passthru(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1445,14 +1431,7 @@ .info = snd_rme9652_info_tc_valid, \ .get = snd_rme9652_get_tc_valid } -static int snd_rme9652_info_tc_valid(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_rme9652_info_tc_valid snd_ctl_boolean_mono_info static int snd_rme9652_get_tc_valid(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { diff -Nur sound/pci/trident/trident_main.c sound/pci/trident/trident_main.c --- sound/pci/trident/trident_main.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/trident/trident_main.c 2007-07-24 02:00:10.000000000 +0200 @@ -2317,15 +2317,7 @@ Description: enable/disable S/PDIF out from ac97 mixer ---------------------------------------------------------------------------*/ -static int snd_trident_spdif_control_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_trident_spdif_control_info snd_ctl_boolean_mono_info static int snd_trident_spdif_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2545,15 +2537,7 @@ Description: enable/disable rear path for ac97 ---------------------------------------------------------------------------*/ -static int snd_trident_ac97_control_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_trident_ac97_control_info snd_ctl_boolean_mono_info static int snd_trident_ac97_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff -Nur sound/pci/via82xx.c sound/pci/via82xx.c --- sound/pci/via82xx.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/via82xx.c 2007-08-21 17:37:19.000000000 +0200 @@ -1572,15 +1572,7 @@ .put = snd_via8233_capture_source_put, }; -static int snd_via8233_dxs3_spdif_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_via8233_dxs3_spdif_info snd_ctl_boolean_mono_info static int snd_via8233_dxs3_spdif_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2098,7 +2090,7 @@ pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval); if (pval & VIA_ACLINK_C00_READY) /* primary codec ready */ break; - schedule_timeout_uninterruptible(1); + schedule_timeout(1); } while (time_before(jiffies, end_time)); if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY) @@ -2117,7 +2109,7 @@ chip->ac97_secondary = 1; goto __ac97_ok2; } - schedule_timeout_interruptible(1); + schedule_timeout(1); } while (time_before(jiffies, end_time)); /* This is ok, the most of motherboards have only one codec */ diff -Nur sound/pci/via82xx.c~ sound/pci/via82xx.c~ --- sound/pci/via82xx.c~ 1970-01-01 01:00:00.000000000 +0100 +++ sound/pci/via82xx.c~ 2007-07-24 02:00:10.000000000 +0200 @@ -0,0 +1,2557 @@ +/* + * ALSA driver for VIA VT82xx (South Bridge) + * + * VT82C686A/B/C, VT8233A/C, VT8235 + * + * Copyright (c) 2000 Jaroslav Kysela + * Tjeerd.Mulder + * 2002 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * Changes: + * + * Dec. 19, 2002 Takashi Iwai + * - use the DSX channels for the first pcm playback. + * (on VIA8233, 8233C and 8235 only) + * this will allow you play simultaneously up to 4 streams. + * multi-channel playback is assigned to the second device + * on these chips. + * - support the secondary capture (on VIA8233/C,8235) + * - SPDIF support + * the DSX3 channel can be used for SPDIF output. + * on VIA8233A, this channel is assigned to the second pcm + * playback. + * the card config of alsa-lib will assign the correct + * device for applications. + * - clean up the code, separate low-level initialization + * routines for each chipset. + * + * Sep. 26, 2005 Karsten Wiese + * - Optimize position calculation for the 823x chips. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#define POINTER_DEBUG +#endif + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("VIA VT82xx audio"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{VIA,VT82C686A/B/C,pci},{VIA,VT8233A/C,8235}}"); + +#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#define SUPPORT_JOYSTICK 1 +#endif + +static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ +static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ +static long mpu_port; +#ifdef SUPPORT_JOYSTICK +static int joystick; +#endif +static int ac97_clock = 48000; +static char *ac97_quirk; +static int dxs_support; + +module_param(index, int, 0444); +MODULE_PARM_DESC(index, "Index value for VIA 82xx bridge."); +module_param(id, charp, 0444); +MODULE_PARM_DESC(id, "ID string for VIA 82xx bridge."); +module_param(mpu_port, long, 0444); +MODULE_PARM_DESC(mpu_port, "MPU-401 port. (VT82C686x only)"); +#ifdef SUPPORT_JOYSTICK +module_param(joystick, bool, 0444); +MODULE_PARM_DESC(joystick, "Enable joystick. (VT82C686x only)"); +#endif +module_param(ac97_clock, int, 0444); +MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (default 48000Hz)."); +module_param(ac97_quirk, charp, 0444); +MODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware."); +module_param(dxs_support, int, 0444); +MODULE_PARM_DESC(dxs_support, "Support for DXS channels (0 = auto, 1 = enable, 2 = disable, 3 = 48k only, 4 = no VRA, 5 = enable any sample rate)"); + +/* just for backward compatibility */ +static int enable; +module_param(enable, bool, 0444); + + +/* revision numbers for via686 */ +#define VIA_REV_686_A 0x10 +#define VIA_REV_686_B 0x11 +#define VIA_REV_686_C 0x12 +#define VIA_REV_686_D 0x13 +#define VIA_REV_686_E 0x14 +#define VIA_REV_686_H 0x20 + +/* revision numbers for via8233 */ +#define VIA_REV_PRE_8233 0x10 /* not in market */ +#define VIA_REV_8233C 0x20 /* 2 rec, 4 pb, 1 multi-pb */ +#define VIA_REV_8233 0x30 /* 2 rec, 4 pb, 1 multi-pb, spdif */ +#define VIA_REV_8233A 0x40 /* 1 rec, 1 multi-pb, spdf */ +#define VIA_REV_8235 0x50 /* 2 rec, 4 pb, 1 multi-pb, spdif */ +#define VIA_REV_8237 0x60 +#define VIA_REV_8251 0x70 + +/* + * Direct registers + */ + +#define VIAREG(via, x) ((via)->port + VIA_REG_##x) +#define VIADEV_REG(viadev, x) ((viadev)->port + VIA_REG_##x) + +/* common offsets */ +#define VIA_REG_OFFSET_STATUS 0x00 /* byte - channel status */ +#define VIA_REG_STAT_ACTIVE 0x80 /* RO */ +#define VIA8233_SHADOW_STAT_ACTIVE 0x08 /* RO */ +#define VIA_REG_STAT_PAUSED 0x40 /* RO */ +#define VIA_REG_STAT_TRIGGER_QUEUED 0x08 /* RO */ +#define VIA_REG_STAT_STOPPED 0x04 /* RWC */ +#define VIA_REG_STAT_EOL 0x02 /* RWC */ +#define VIA_REG_STAT_FLAG 0x01 /* RWC */ +#define VIA_REG_OFFSET_CONTROL 0x01 /* byte - channel control */ +#define VIA_REG_CTRL_START 0x80 /* WO */ +#define VIA_REG_CTRL_TERMINATE 0x40 /* WO */ +#define VIA_REG_CTRL_AUTOSTART 0x20 +#define VIA_REG_CTRL_PAUSE 0x08 /* RW */ +#define VIA_REG_CTRL_INT_STOP 0x04 +#define VIA_REG_CTRL_INT_EOL 0x02 +#define VIA_REG_CTRL_INT_FLAG 0x01 +#define VIA_REG_CTRL_RESET 0x01 /* RW - probably reset? undocumented */ +#define VIA_REG_CTRL_INT (VIA_REG_CTRL_INT_FLAG | VIA_REG_CTRL_INT_EOL | VIA_REG_CTRL_AUTOSTART) +#define VIA_REG_OFFSET_TYPE 0x02 /* byte - channel type (686 only) */ +#define VIA_REG_TYPE_AUTOSTART 0x80 /* RW - autostart at EOL */ +#define VIA_REG_TYPE_16BIT 0x20 /* RW */ +#define VIA_REG_TYPE_STEREO 0x10 /* RW */ +#define VIA_REG_TYPE_INT_LLINE 0x00 +#define VIA_REG_TYPE_INT_LSAMPLE 0x04 +#define VIA_REG_TYPE_INT_LESSONE 0x08 +#define VIA_REG_TYPE_INT_MASK 0x0c +#define VIA_REG_TYPE_INT_EOL 0x02 +#define VIA_REG_TYPE_INT_FLAG 0x01 +#define VIA_REG_OFFSET_TABLE_PTR 0x04 /* dword - channel table pointer */ +#define VIA_REG_OFFSET_CURR_PTR 0x04 /* dword - channel current pointer */ +#define VIA_REG_OFFSET_STOP_IDX 0x08 /* dword - stop index, channel type, sample rate */ +#define VIA8233_REG_TYPE_16BIT 0x00200000 /* RW */ +#define VIA8233_REG_TYPE_STEREO 0x00100000 /* RW */ +#define VIA_REG_OFFSET_CURR_COUNT 0x0c /* dword - channel current count (24 bit) */ +#define VIA_REG_OFFSET_CURR_INDEX 0x0f /* byte - channel current index (for via8233 only) */ + +#define DEFINE_VIA_REGSET(name,val) \ +enum {\ + VIA_REG_##name##_STATUS = (val),\ + VIA_REG_##name##_CONTROL = (val) + 0x01,\ + VIA_REG_##name##_TYPE = (val) + 0x02,\ + VIA_REG_##name##_TABLE_PTR = (val) + 0x04,\ + VIA_REG_##name##_CURR_PTR = (val) + 0x04,\ + VIA_REG_##name##_STOP_IDX = (val) + 0x08,\ + VIA_REG_##name##_CURR_COUNT = (val) + 0x0c,\ +} + +/* playback block */ +DEFINE_VIA_REGSET(PLAYBACK, 0x00); +DEFINE_VIA_REGSET(CAPTURE, 0x10); +DEFINE_VIA_REGSET(FM, 0x20); + +/* AC'97 */ +#define VIA_REG_AC97 0x80 /* dword */ +#define VIA_REG_AC97_CODEC_ID_MASK (3<<30) +#define VIA_REG_AC97_CODEC_ID_SHIFT 30 +#define VIA_REG_AC97_CODEC_ID_PRIMARY 0x00 +#define VIA_REG_AC97_CODEC_ID_SECONDARY 0x01 +#define VIA_REG_AC97_SECONDARY_VALID (1<<27) +#define VIA_REG_AC97_PRIMARY_VALID (1<<25) +#define VIA_REG_AC97_BUSY (1<<24) +#define VIA_REG_AC97_READ (1<<23) +#define VIA_REG_AC97_CMD_SHIFT 16 +#define VIA_REG_AC97_CMD_MASK 0x7e +#define VIA_REG_AC97_DATA_SHIFT 0 +#define VIA_REG_AC97_DATA_MASK 0xffff + +#define VIA_REG_SGD_SHADOW 0x84 /* dword */ +/* via686 */ +#define VIA_REG_SGD_STAT_PB_FLAG (1<<0) +#define VIA_REG_SGD_STAT_CP_FLAG (1<<1) +#define VIA_REG_SGD_STAT_FM_FLAG (1<<2) +#define VIA_REG_SGD_STAT_PB_EOL (1<<4) +#define VIA_REG_SGD_STAT_CP_EOL (1<<5) +#define VIA_REG_SGD_STAT_FM_EOL (1<<6) +#define VIA_REG_SGD_STAT_PB_STOP (1<<8) +#define VIA_REG_SGD_STAT_CP_STOP (1<<9) +#define VIA_REG_SGD_STAT_FM_STOP (1<<10) +#define VIA_REG_SGD_STAT_PB_ACTIVE (1<<12) +#define VIA_REG_SGD_STAT_CP_ACTIVE (1<<13) +#define VIA_REG_SGD_STAT_FM_ACTIVE (1<<14) +/* via8233 */ +#define VIA8233_REG_SGD_STAT_FLAG (1<<0) +#define VIA8233_REG_SGD_STAT_EOL (1<<1) +#define VIA8233_REG_SGD_STAT_STOP (1<<2) +#define VIA8233_REG_SGD_STAT_ACTIVE (1<<3) +#define VIA8233_INTR_MASK(chan) ((VIA8233_REG_SGD_STAT_FLAG|VIA8233_REG_SGD_STAT_EOL) << ((chan) * 4)) +#define VIA8233_REG_SGD_CHAN_SDX 0 +#define VIA8233_REG_SGD_CHAN_MULTI 4 +#define VIA8233_REG_SGD_CHAN_REC 6 +#define VIA8233_REG_SGD_CHAN_REC1 7 + +#define VIA_REG_GPI_STATUS 0x88 +#define VIA_REG_GPI_INTR 0x8c + +/* multi-channel and capture registers for via8233 */ +DEFINE_VIA_REGSET(MULTPLAY, 0x40); +DEFINE_VIA_REGSET(CAPTURE_8233, 0x60); + +/* via8233-specific registers */ +#define VIA_REG_OFS_PLAYBACK_VOLUME_L 0x02 /* byte */ +#define VIA_REG_OFS_PLAYBACK_VOLUME_R 0x03 /* byte */ +#define VIA_REG_OFS_MULTPLAY_FORMAT 0x02 /* byte - format and channels */ +#define VIA_REG_MULTPLAY_FMT_8BIT 0x00 +#define VIA_REG_MULTPLAY_FMT_16BIT 0x80 +#define VIA_REG_MULTPLAY_FMT_CH_MASK 0x70 /* # channels << 4 (valid = 1,2,4,6) */ +#define VIA_REG_OFS_CAPTURE_FIFO 0x02 /* byte - bit 6 = fifo enable */ +#define VIA_REG_CAPTURE_FIFO_ENABLE 0x40 + +#define VIA_DXS_MAX_VOLUME 31 /* max. volume (attenuation) of reg 0x32/33 */ + +#define VIA_REG_CAPTURE_CHANNEL 0x63 /* byte - input select */ +#define VIA_REG_CAPTURE_CHANNEL_MIC 0x4 +#define VIA_REG_CAPTURE_CHANNEL_LINE 0 +#define VIA_REG_CAPTURE_SELECT_CODEC 0x03 /* recording source codec (0 = primary) */ + +#define VIA_TBL_BIT_FLAG 0x40000000 +#define VIA_TBL_BIT_EOL 0x80000000 + +/* pci space */ +#define VIA_ACLINK_STAT 0x40 +#define VIA_ACLINK_C11_READY 0x20 +#define VIA_ACLINK_C10_READY 0x10 +#define VIA_ACLINK_C01_READY 0x04 /* secondary codec ready */ +#define VIA_ACLINK_LOWPOWER 0x02 /* low-power state */ +#define VIA_ACLINK_C00_READY 0x01 /* primary codec ready */ +#define VIA_ACLINK_CTRL 0x41 +#define VIA_ACLINK_CTRL_ENABLE 0x80 /* 0: disable, 1: enable */ +#define VIA_ACLINK_CTRL_RESET 0x40 /* 0: assert, 1: de-assert */ +#define VIA_ACLINK_CTRL_SYNC 0x20 /* 0: release SYNC, 1: force SYNC hi */ +#define VIA_ACLINK_CTRL_SDO 0x10 /* 0: release SDO, 1: force SDO hi */ +#define VIA_ACLINK_CTRL_VRA 0x08 /* 0: disable VRA, 1: enable VRA */ +#define VIA_ACLINK_CTRL_PCM 0x04 /* 0: disable PCM, 1: enable PCM */ +#define VIA_ACLINK_CTRL_FM 0x02 /* via686 only */ +#define VIA_ACLINK_CTRL_SB 0x01 /* via686 only */ +#define VIA_ACLINK_CTRL_INIT (VIA_ACLINK_CTRL_ENABLE|\ + VIA_ACLINK_CTRL_RESET|\ + VIA_ACLINK_CTRL_PCM|\ + VIA_ACLINK_CTRL_VRA) +#define VIA_FUNC_ENABLE 0x42 +#define VIA_FUNC_MIDI_PNP 0x80 /* FIXME: it's 0x40 in the datasheet! */ +#define VIA_FUNC_MIDI_IRQMASK 0x40 /* FIXME: not documented! */ +#define VIA_FUNC_RX2C_WRITE 0x20 +#define VIA_FUNC_SB_FIFO_EMPTY 0x10 +#define VIA_FUNC_ENABLE_GAME 0x08 +#define VIA_FUNC_ENABLE_FM 0x04 +#define VIA_FUNC_ENABLE_MIDI 0x02 +#define VIA_FUNC_ENABLE_SB 0x01 +#define VIA_PNP_CONTROL 0x43 +#define VIA_FM_NMI_CTRL 0x48 +#define VIA8233_VOLCHG_CTRL 0x48 +#define VIA8233_SPDIF_CTRL 0x49 +#define VIA8233_SPDIF_DX3 0x08 +#define VIA8233_SPDIF_SLOT_MASK 0x03 +#define VIA8233_SPDIF_SLOT_1011 0x00 +#define VIA8233_SPDIF_SLOT_34 0x01 +#define VIA8233_SPDIF_SLOT_78 0x02 +#define VIA8233_SPDIF_SLOT_69 0x03 + +/* + */ + +#define VIA_DXS_AUTO 0 +#define VIA_DXS_ENABLE 1 +#define VIA_DXS_DISABLE 2 +#define VIA_DXS_48K 3 +#define VIA_DXS_NO_VRA 4 +#define VIA_DXS_SRC 5 + + +/* + * pcm stream + */ + +struct snd_via_sg_table { + unsigned int offset; + unsigned int size; +} ; + +#define VIA_TABLE_SIZE 255 + +struct viadev { + unsigned int reg_offset; + unsigned long port; + int direction; /* playback = 0, capture = 1 */ + struct snd_pcm_substream *substream; + int running; + unsigned int tbl_entries; /* # descriptors */ + struct snd_dma_buffer table; + struct snd_via_sg_table *idx_table; + /* for recovery from the unexpected pointer */ + unsigned int lastpos; + unsigned int fragsize; + unsigned int bufsize; + unsigned int bufsize2; + int hwptr_done; /* processed frame position in the buffer */ + int in_interrupt; + int shadow_shift; +}; + + +enum { TYPE_CARD_VIA686 = 1, TYPE_CARD_VIA8233 }; +enum { TYPE_VIA686, TYPE_VIA8233, TYPE_VIA8233A }; + +#define VIA_MAX_DEVS 7 /* 4 playback, 1 multi, 2 capture */ + +struct via_rate_lock { + spinlock_t lock; + int rate; + int used; +}; + +struct via82xx { + int irq; + + unsigned long port; + struct resource *mpu_res; + int chip_type; + unsigned char revision; + + unsigned char old_legacy; + unsigned char old_legacy_cfg; +#ifdef CONFIG_PM + unsigned char legacy_saved; + unsigned char legacy_cfg_saved; + unsigned char spdif_ctrl_saved; + unsigned char capture_src_saved[2]; + unsigned int mpu_port_saved; +#endif + + unsigned char playback_volume[4][2]; /* for VIA8233/C/8235; default = 0 */ + unsigned char playback_volume_c[2]; /* for VIA8233/C/8235; default = 0 */ + + unsigned int intr_mask; /* SGD_SHADOW mask to check interrupts */ + + struct pci_dev *pci; + struct snd_card *card; + + unsigned int num_devs; + unsigned int playback_devno, multi_devno, capture_devno; + struct viadev devs[VIA_MAX_DEVS]; + struct via_rate_lock rates[2]; /* playback and capture */ + unsigned int dxs_fixed: 1; /* DXS channel accepts only 48kHz */ + unsigned int no_vra: 1; /* no need to set VRA on DXS channels */ + unsigned int dxs_src: 1; /* use full SRC capabilities of DXS */ + unsigned int spdif_on: 1; /* only spdif rates work to external DACs */ + + struct snd_pcm *pcms[2]; + struct snd_rawmidi *rmidi; + + struct snd_ac97_bus *ac97_bus; + struct snd_ac97 *ac97; + unsigned int ac97_clock; + unsigned int ac97_secondary; /* secondary AC'97 codec is present */ + + spinlock_t reg_lock; + struct snd_info_entry *proc_entry; + +#ifdef SUPPORT_JOYSTICK + struct gameport *gameport; +#endif +}; + +static struct pci_device_id snd_via82xx_ids[] = { + /* 0x1106, 0x3058 */ + { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA686, }, /* 686A */ + /* 0x1106, 0x3059 */ + { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8233_5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA8233, }, /* VT8233 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_via82xx_ids); + +/* + */ + +/* + * allocate and initialize the descriptor buffers + * periods = number of periods + * fragsize = period size in bytes + */ +static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substream, + struct pci_dev *pci, + unsigned int periods, unsigned int fragsize) +{ + unsigned int i, idx, ofs, rest; + struct via82xx *chip = snd_pcm_substream_chip(substream); + struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream); + + if (dev->table.area == NULL) { + /* the start of each lists must be aligned to 8 bytes, + * but the kernel pages are much bigger, so we don't care + */ + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), + PAGE_ALIGN(VIA_TABLE_SIZE * 2 * 8), + &dev->table) < 0) + return -ENOMEM; + } + if (! dev->idx_table) { + dev->idx_table = kmalloc(sizeof(*dev->idx_table) * VIA_TABLE_SIZE, GFP_KERNEL); + if (! dev->idx_table) + return -ENOMEM; + } + + /* fill the entries */ + idx = 0; + ofs = 0; + for (i = 0; i < periods; i++) { + rest = fragsize; + /* fill descriptors for a period. + * a period can be split to several descriptors if it's + * over page boundary. + */ + do { + unsigned int r; + unsigned int flag; + + if (idx >= VIA_TABLE_SIZE) { + snd_printk(KERN_ERR "via82xx: too much table size!\n"); + return -EINVAL; + } + ((u32 *)dev->table.area)[idx << 1] = cpu_to_le32((u32)snd_pcm_sgbuf_get_addr(sgbuf, ofs)); + r = PAGE_SIZE - (ofs % PAGE_SIZE); + if (rest < r) + r = rest; + rest -= r; + if (! rest) { + if (i == periods - 1) + flag = VIA_TBL_BIT_EOL; /* buffer boundary */ + else + flag = VIA_TBL_BIT_FLAG; /* period boundary */ + } else + flag = 0; /* period continues to the next */ + // printk("via: tbl %d: at %d size %d (rest %d)\n", idx, ofs, r, rest); + ((u32 *)dev->table.area)[(idx<<1) + 1] = cpu_to_le32(r | flag); + dev->idx_table[idx].offset = ofs; + dev->idx_table[idx].size = r; + ofs += r; + idx++; + } while (rest > 0); + } + dev->tbl_entries = idx; + dev->bufsize = periods * fragsize; + dev->bufsize2 = dev->bufsize / 2; + dev->fragsize = fragsize; + return 0; +} + + +static int clean_via_table(struct viadev *dev, struct snd_pcm_substream *substream, + struct pci_dev *pci) +{ + if (dev->table.area) { + snd_dma_free_pages(&dev->table); + dev->table.area = NULL; + } + kfree(dev->idx_table); + dev->idx_table = NULL; + return 0; +} + +/* + * Basic I/O + */ + +static inline unsigned int snd_via82xx_codec_xread(struct via82xx *chip) +{ + return inl(VIAREG(chip, AC97)); +} + +static inline void snd_via82xx_codec_xwrite(struct via82xx *chip, unsigned int val) +{ + outl(val, VIAREG(chip, AC97)); +} + +static int snd_via82xx_codec_ready(struct via82xx *chip, int secondary) +{ + unsigned int timeout = 1000; /* 1ms */ + unsigned int val; + + while (timeout-- > 0) { + udelay(1); + if (!((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY)) + return val & 0xffff; + } + snd_printk(KERN_ERR "codec_ready: codec %i is not ready [0x%x]\n", + secondary, snd_via82xx_codec_xread(chip)); + return -EIO; +} + +static int snd_via82xx_codec_valid(struct via82xx *chip, int secondary) +{ + unsigned int timeout = 1000; /* 1ms */ + unsigned int val, val1; + unsigned int stat = !secondary ? VIA_REG_AC97_PRIMARY_VALID : + VIA_REG_AC97_SECONDARY_VALID; + + while (timeout-- > 0) { + val = snd_via82xx_codec_xread(chip); + val1 = val & (VIA_REG_AC97_BUSY | stat); + if (val1 == stat) + return val & 0xffff; + udelay(1); + } + return -EIO; +} + +static void snd_via82xx_codec_wait(struct snd_ac97 *ac97) +{ + struct via82xx *chip = ac97->private_data; + int err; + err = snd_via82xx_codec_ready(chip, ac97->num); + /* here we need to wait fairly for long time.. */ + msleep(500); +} + +static void snd_via82xx_codec_write(struct snd_ac97 *ac97, + unsigned short reg, + unsigned short val) +{ + struct via82xx *chip = ac97->private_data; + unsigned int xval; + + xval = !ac97->num ? VIA_REG_AC97_CODEC_ID_PRIMARY : VIA_REG_AC97_CODEC_ID_SECONDARY; + xval <<= VIA_REG_AC97_CODEC_ID_SHIFT; + xval |= reg << VIA_REG_AC97_CMD_SHIFT; + xval |= val << VIA_REG_AC97_DATA_SHIFT; + snd_via82xx_codec_xwrite(chip, xval); + snd_via82xx_codec_ready(chip, ac97->num); +} + +static unsigned short snd_via82xx_codec_read(struct snd_ac97 *ac97, unsigned short reg) +{ + struct via82xx *chip = ac97->private_data; + unsigned int xval, val = 0xffff; + int again = 0; + + xval = ac97->num << VIA_REG_AC97_CODEC_ID_SHIFT; + xval |= ac97->num ? VIA_REG_AC97_SECONDARY_VALID : VIA_REG_AC97_PRIMARY_VALID; + xval |= VIA_REG_AC97_READ; + xval |= (reg & 0x7f) << VIA_REG_AC97_CMD_SHIFT; + while (1) { + if (again++ > 3) { + snd_printk(KERN_ERR "codec_read: codec %i is not valid [0x%x]\n", + ac97->num, snd_via82xx_codec_xread(chip)); + return 0xffff; + } + snd_via82xx_codec_xwrite(chip, xval); + udelay (20); + if (snd_via82xx_codec_valid(chip, ac97->num) >= 0) { + udelay(25); + val = snd_via82xx_codec_xread(chip); + break; + } + } + return val & 0xffff; +} + +static void snd_via82xx_channel_reset(struct via82xx *chip, struct viadev *viadev) +{ + outb(VIA_REG_CTRL_PAUSE | VIA_REG_CTRL_TERMINATE | VIA_REG_CTRL_RESET, + VIADEV_REG(viadev, OFFSET_CONTROL)); + inb(VIADEV_REG(viadev, OFFSET_CONTROL)); + udelay(50); + /* disable interrupts */ + outb(0x00, VIADEV_REG(viadev, OFFSET_CONTROL)); + /* clear interrupts */ + outb(0x03, VIADEV_REG(viadev, OFFSET_STATUS)); + outb(0x00, VIADEV_REG(viadev, OFFSET_TYPE)); /* for via686 */ + // outl(0, VIADEV_REG(viadev, OFFSET_CURR_PTR)); + viadev->lastpos = 0; + viadev->hwptr_done = 0; +} + + +/* + * Interrupt handler + * Used for 686 and 8233A + */ +static irqreturn_t snd_via686_interrupt(int irq, void *dev_id) +{ + struct via82xx *chip = dev_id; + unsigned int status; + unsigned int i; + + status = inl(VIAREG(chip, SGD_SHADOW)); + if (! (status & chip->intr_mask)) { + if (chip->rmidi) + /* check mpu401 interrupt */ + return snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data); + return IRQ_NONE; + } + + /* check status for each stream */ + spin_lock(&chip->reg_lock); + for (i = 0; i < chip->num_devs; i++) { + struct viadev *viadev = &chip->devs[i]; + unsigned char c_status = inb(VIADEV_REG(viadev, OFFSET_STATUS)); + if (! (c_status & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG|VIA_REG_STAT_STOPPED))) + continue; + if (viadev->substream && viadev->running) { + /* + * Update hwptr_done based on 'period elapsed' + * interrupts. We'll use it, when the chip returns 0 + * for OFFSET_CURR_COUNT. + */ + if (c_status & VIA_REG_STAT_EOL) + viadev->hwptr_done = 0; + else + viadev->hwptr_done += viadev->fragsize; + viadev->in_interrupt = c_status; + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(viadev->substream); + spin_lock(&chip->reg_lock); + viadev->in_interrupt = 0; + } + outb(c_status, VIADEV_REG(viadev, OFFSET_STATUS)); /* ack */ + } + spin_unlock(&chip->reg_lock); + return IRQ_HANDLED; +} + +/* + * Interrupt handler + */ +static irqreturn_t snd_via8233_interrupt(int irq, void *dev_id) +{ + struct via82xx *chip = dev_id; + unsigned int status; + unsigned int i; + int irqreturn = 0; + + /* check status for each stream */ + spin_lock(&chip->reg_lock); + status = inl(VIAREG(chip, SGD_SHADOW)); + + for (i = 0; i < chip->num_devs; i++) { + struct viadev *viadev = &chip->devs[i]; + struct snd_pcm_substream *substream; + unsigned char c_status, shadow_status; + + shadow_status = (status >> viadev->shadow_shift) & + (VIA8233_SHADOW_STAT_ACTIVE|VIA_REG_STAT_EOL| + VIA_REG_STAT_FLAG); + c_status = shadow_status & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG); + if (!c_status) + continue; + + substream = viadev->substream; + if (substream && viadev->running) { + /* + * Update hwptr_done based on 'period elapsed' + * interrupts. We'll use it, when the chip returns 0 + * for OFFSET_CURR_COUNT. + */ + if (c_status & VIA_REG_STAT_EOL) + viadev->hwptr_done = 0; + else + viadev->hwptr_done += viadev->fragsize; + viadev->in_interrupt = c_status; + if (shadow_status & VIA8233_SHADOW_STAT_ACTIVE) + viadev->in_interrupt |= VIA_REG_STAT_ACTIVE; + spin_unlock(&chip->reg_lock); + + snd_pcm_period_elapsed(substream); + + spin_lock(&chip->reg_lock); + viadev->in_interrupt = 0; + } + outb(c_status, VIADEV_REG(viadev, OFFSET_STATUS)); /* ack */ + irqreturn = 1; + } + spin_unlock(&chip->reg_lock); + return IRQ_RETVAL(irqreturn); +} + +/* + * PCM callbacks + */ + +/* + * trigger callback + */ +static int snd_via82xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct via82xx *chip = snd_pcm_substream_chip(substream); + struct viadev *viadev = substream->runtime->private_data; + unsigned char val; + + if (chip->chip_type != TYPE_VIA686) + val = VIA_REG_CTRL_INT; + else + val = 0; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + val |= VIA_REG_CTRL_START; + viadev->running = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + val = VIA_REG_CTRL_TERMINATE; + viadev->running = 0; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + val |= VIA_REG_CTRL_PAUSE; + viadev->running = 0; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + viadev->running = 1; + break; + default: + return -EINVAL; + } + outb(val, VIADEV_REG(viadev, OFFSET_CONTROL)); + if (cmd == SNDRV_PCM_TRIGGER_STOP) + snd_via82xx_channel_reset(chip, viadev); + return 0; +} + + +/* + * pointer callbacks + */ + +/* + * calculate the linear position at the given sg-buffer index and the rest count + */ + +#define check_invalid_pos(viadev,pos) \ + ((pos) < viadev->lastpos && ((pos) >= viadev->bufsize2 ||\ + viadev->lastpos < viadev->bufsize2)) + +static inline unsigned int calc_linear_pos(struct viadev *viadev, unsigned int idx, + unsigned int count) +{ + unsigned int size, base, res; + + size = viadev->idx_table[idx].size; + base = viadev->idx_table[idx].offset; + res = base + size - count; + if (res >= viadev->bufsize) + res -= viadev->bufsize; + + /* check the validity of the calculated position */ + if (size < count) { + snd_printd(KERN_ERR "invalid via82xx_cur_ptr (size = %d, count = %d)\n", + (int)size, (int)count); + res = viadev->lastpos; + } else { + if (! count) { + /* Some mobos report count = 0 on the DMA boundary, + * i.e. count = size indeed. + * Let's check whether this step is above the expected size. + */ + int delta = res - viadev->lastpos; + if (delta < 0) + delta += viadev->bufsize; + if ((unsigned int)delta > viadev->fragsize) + res = base; + } + if (check_invalid_pos(viadev, res)) { +#ifdef POINTER_DEBUG + printk(KERN_DEBUG "fail: idx = %i/%i, lastpos = 0x%x, " + "bufsize2 = 0x%x, offsize = 0x%x, size = 0x%x, " + "count = 0x%x\n", idx, viadev->tbl_entries, + viadev->lastpos, viadev->bufsize2, + viadev->idx_table[idx].offset, + viadev->idx_table[idx].size, count); +#endif + /* count register returns full size when end of buffer is reached */ + res = base + size; + if (check_invalid_pos(viadev, res)) { + snd_printd(KERN_ERR "invalid via82xx_cur_ptr (2), " + "using last valid pointer\n"); + res = viadev->lastpos; + } + } + } + return res; +} + +/* + * get the current pointer on via686 + */ +static snd_pcm_uframes_t snd_via686_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct via82xx *chip = snd_pcm_substream_chip(substream); + struct viadev *viadev = substream->runtime->private_data; + unsigned int idx, ptr, count, res; + + snd_assert(viadev->tbl_entries, return 0); + if (!(inb(VIADEV_REG(viadev, OFFSET_STATUS)) & VIA_REG_STAT_ACTIVE)) + return 0; + + spin_lock(&chip->reg_lock); + count = inl(VIADEV_REG(viadev, OFFSET_CURR_COUNT)) & 0xffffff; + /* The via686a does not have the current index register, + * so we need to calculate the index from CURR_PTR. + */ + ptr = inl(VIADEV_REG(viadev, OFFSET_CURR_PTR)); + if (ptr <= (unsigned int)viadev->table.addr) + idx = 0; + else /* CURR_PTR holds the address + 8 */ + idx = ((ptr - (unsigned int)viadev->table.addr) / 8 - 1) % viadev->tbl_entries; + res = calc_linear_pos(viadev, idx, count); + viadev->lastpos = res; /* remember the last position */ + spin_unlock(&chip->reg_lock); + + return bytes_to_frames(substream->runtime, res); +} + +/* + * get the current pointer on via823x + */ +static snd_pcm_uframes_t snd_via8233_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct via82xx *chip = snd_pcm_substream_chip(substream); + struct viadev *viadev = substream->runtime->private_data; + unsigned int idx, count, res; + int status; + + snd_assert(viadev->tbl_entries, return 0); + + spin_lock(&chip->reg_lock); + count = inl(VIADEV_REG(viadev, OFFSET_CURR_COUNT)); + status = viadev->in_interrupt; + if (!status) + status = inb(VIADEV_REG(viadev, OFFSET_STATUS)); + + /* An apparent bug in the 8251 is worked around by sending a + * REG_CTRL_START. */ + if (chip->revision == VIA_REV_8251 && (status & VIA_REG_STAT_EOL)) + snd_via82xx_pcm_trigger(substream, SNDRV_PCM_TRIGGER_START); + + if (!(status & VIA_REG_STAT_ACTIVE)) { + res = 0; + goto unlock; + } + if (count & 0xffffff) { + idx = count >> 24; + if (idx >= viadev->tbl_entries) { +#ifdef POINTER_DEBUG + printk(KERN_DEBUG "fail: invalid idx = %i/%i\n", idx, + viadev->tbl_entries); +#endif + res = viadev->lastpos; + } else { + count &= 0xffffff; + res = calc_linear_pos(viadev, idx, count); + } + } else { + res = viadev->hwptr_done; + if (!viadev->in_interrupt) { + if (status & VIA_REG_STAT_EOL) { + res = 0; + } else + if (status & VIA_REG_STAT_FLAG) { + res += viadev->fragsize; + } + } + } +unlock: + viadev->lastpos = res; + spin_unlock(&chip->reg_lock); + + return bytes_to_frames(substream->runtime, res); +} + + +/* + * hw_params callback: + * allocate the buffer and build up the buffer description table + */ +static int snd_via82xx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct via82xx *chip = snd_pcm_substream_chip(substream); + struct viadev *viadev = substream->runtime->private_data; + int err; + + err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); + if (err < 0) + return err; + err = build_via_table(viadev, substream, chip->pci, + params_periods(hw_params), + params_period_bytes(hw_params)); + if (err < 0) + return err; + + return 0; +} + +/* + * hw_free callback: + * clean up the buffer description table and release the buffer + */ +static int snd_via82xx_hw_free(struct snd_pcm_substream *substream) +{ + struct via82xx *chip = snd_pcm_substream_chip(substream); + struct viadev *viadev = substream->runtime->private_data; + + clean_via_table(viadev, substream, chip->pci); + snd_pcm_lib_free_pages(substream); + return 0; +} + + +/* + * set up the table pointer + */ +static void snd_via82xx_set_table_ptr(struct via82xx *chip, struct viadev *viadev) +{ + snd_via82xx_codec_ready(chip, 0); + outl((u32)viadev->table.addr, VIADEV_REG(viadev, OFFSET_TABLE_PTR)); + udelay(20); + snd_via82xx_codec_ready(chip, 0); +} + +/* + * prepare callback for playback and capture on via686 + */ +static void via686_setup_format(struct via82xx *chip, struct viadev *viadev, + struct snd_pcm_runtime *runtime) +{ + snd_via82xx_channel_reset(chip, viadev); + /* this must be set after channel_reset */ + snd_via82xx_set_table_ptr(chip, viadev); + outb(VIA_REG_TYPE_AUTOSTART | + (runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA_REG_TYPE_16BIT : 0) | + (runtime->channels > 1 ? VIA_REG_TYPE_STEREO : 0) | + ((viadev->reg_offset & 0x10) == 0 ? VIA_REG_TYPE_INT_LSAMPLE : 0) | + VIA_REG_TYPE_INT_EOL | + VIA_REG_TYPE_INT_FLAG, VIADEV_REG(viadev, OFFSET_TYPE)); +} + +static int snd_via686_playback_prepare(struct snd_pcm_substream *substream) +{ + struct via82xx *chip = snd_pcm_substream_chip(substream); + struct viadev *viadev = substream->runtime->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + + snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); + snd_ac97_set_rate(chip->ac97, AC97_SPDIF, runtime->rate); + via686_setup_format(chip, viadev, runtime); + return 0; +} + +static int snd_via686_capture_prepare(struct snd_pcm_substream *substream) +{ + struct via82xx *chip = snd_pcm_substream_chip(substream); + struct viadev *viadev = substream->runtime->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + + snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate); + via686_setup_format(chip, viadev, runtime); + return 0; +} + +/* + * lock the current rate + */ +static int via_lock_rate(struct via_rate_lock *rec, int rate) +{ + int changed = 0; + + spin_lock_irq(&rec->lock); + if (rec->rate != rate) { + if (rec->rate && rec->used > 1) /* already set */ + changed = -EINVAL; + else { + rec->rate = rate; + changed = 1; + } + } + spin_unlock_irq(&rec->lock); + return changed; +} + +/* + * prepare callback for DSX playback on via823x + */ +static int snd_via8233_playback_prepare(struct snd_pcm_substream *substream) +{ + struct via82xx *chip = snd_pcm_substream_chip(substream); + struct viadev *viadev = substream->runtime->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + int ac97_rate = chip->dxs_src ? 48000 : runtime->rate; + int rate_changed; + u32 rbits; + + if ((rate_changed = via_lock_rate(&chip->rates[0], ac97_rate)) < 0) + return rate_changed; + if (rate_changed) + snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, + chip->no_vra ? 48000 : runtime->rate); + if (chip->spdif_on && viadev->reg_offset == 0x30) + snd_ac97_set_rate(chip->ac97, AC97_SPDIF, runtime->rate); + + if (runtime->rate == 48000) + rbits = 0xfffff; + else + rbits = (0x100000 / 48000) * runtime->rate + + ((0x100000 % 48000) * runtime->rate) / 48000; + snd_assert((rbits & ~0xfffff) == 0, return -EINVAL); + snd_via82xx_channel_reset(chip, viadev); + snd_via82xx_set_table_ptr(chip, viadev); + outb(chip->playback_volume[viadev->reg_offset / 0x10][0], + VIADEV_REG(viadev, OFS_PLAYBACK_VOLUME_L)); + outb(chip->playback_volume[viadev->reg_offset / 0x10][1], + VIADEV_REG(viadev, OFS_PLAYBACK_VOLUME_R)); + outl((runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA8233_REG_TYPE_16BIT : 0) | /* format */ + (runtime->channels > 1 ? VIA8233_REG_TYPE_STEREO : 0) | /* stereo */ + rbits | /* rate */ + 0xff000000, /* STOP index is never reached */ + VIADEV_REG(viadev, OFFSET_STOP_IDX)); + udelay(20); + snd_via82xx_codec_ready(chip, 0); + return 0; +} + +/* + * prepare callback for multi-channel playback on via823x + */ +static int snd_via8233_multi_prepare(struct snd_pcm_substream *substream) +{ + struct via82xx *chip = snd_pcm_substream_chip(substream); + struct viadev *viadev = substream->runtime->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int slots; + int fmt; + + if (via_lock_rate(&chip->rates[0], runtime->rate) < 0) + return -EINVAL; + snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); + snd_ac97_set_rate(chip->ac97, AC97_PCM_SURR_DAC_RATE, runtime->rate); + snd_ac97_set_rate(chip->ac97, AC97_PCM_LFE_DAC_RATE, runtime->rate); + snd_ac97_set_rate(chip->ac97, AC97_SPDIF, runtime->rate); + snd_via82xx_channel_reset(chip, viadev); + snd_via82xx_set_table_ptr(chip, viadev); + + fmt = (runtime->format == SNDRV_PCM_FORMAT_S16_LE) ? + VIA_REG_MULTPLAY_FMT_16BIT : VIA_REG_MULTPLAY_FMT_8BIT; + fmt |= runtime->channels << 4; + outb(fmt, VIADEV_REG(viadev, OFS_MULTPLAY_FORMAT)); +#if 0 + if (chip->revision == VIA_REV_8233A) + slots = 0; + else +#endif + { + /* set sample number to slot 3, 4, 7, 8, 6, 9 (for VIA8233/C,8235) */ + /* corresponding to FL, FR, RL, RR, C, LFE ?? */ + switch (runtime->channels) { + case 1: slots = (1<<0) | (1<<4); break; + case 2: slots = (1<<0) | (2<<4); break; + case 3: slots = (1<<0) | (2<<4) | (5<<8); break; + case 4: slots = (1<<0) | (2<<4) | (3<<8) | (4<<12); break; + case 5: slots = (1<<0) | (2<<4) | (3<<8) | (4<<12) | (5<<16); break; + case 6: slots = (1<<0) | (2<<4) | (3<<8) | (4<<12) | (5<<16) | (6<<20); break; + default: slots = 0; break; + } + } + /* STOP index is never reached */ + outl(0xff000000 | slots, VIADEV_REG(viadev, OFFSET_STOP_IDX)); + udelay(20); + snd_via82xx_codec_ready(chip, 0); + return 0; +} + +/* + * prepare callback for capture on via823x + */ +static int snd_via8233_capture_prepare(struct snd_pcm_substream *substream) +{ + struct via82xx *chip = snd_pcm_substream_chip(substream); + struct viadev *viadev = substream->runtime->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + + if (via_lock_rate(&chip->rates[1], runtime->rate) < 0) + return -EINVAL; + snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate); + snd_via82xx_channel_reset(chip, viadev); + snd_via82xx_set_table_ptr(chip, viadev); + outb(VIA_REG_CAPTURE_FIFO_ENABLE, VIADEV_REG(viadev, OFS_CAPTURE_FIFO)); + outl((runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA8233_REG_TYPE_16BIT : 0) | + (runtime->channels > 1 ? VIA8233_REG_TYPE_STEREO : 0) | + 0xff000000, /* STOP index is never reached */ + VIADEV_REG(viadev, OFFSET_STOP_IDX)); + udelay(20); + snd_via82xx_codec_ready(chip, 0); + return 0; +} + + +/* + * pcm hardware definition, identical for both playback and capture + */ +static struct snd_pcm_hardware snd_via82xx_hw = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + /* SNDRV_PCM_INFO_RESUME | */ + SNDRV_PCM_INFO_PAUSE), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 128 * 1024, + .period_bytes_min = 32, + .period_bytes_max = 128 * 1024, + .periods_min = 2, + .periods_max = VIA_TABLE_SIZE / 2, + .fifo_size = 0, +}; + + +/* + * open callback skeleton + */ +static int snd_via82xx_pcm_open(struct via82xx *chip, struct viadev *viadev, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + struct via_rate_lock *ratep; + + runtime->hw = snd_via82xx_hw; + + /* set the hw rate condition */ + ratep = &chip->rates[viadev->direction]; + spin_lock_irq(&ratep->lock); + ratep->used++; + if (chip->spdif_on && viadev->reg_offset == 0x30) { + /* DXS#3 and spdif is on */ + runtime->hw.rates = chip->ac97->rates[AC97_RATES_SPDIF]; + snd_pcm_limit_hw_rates(runtime); + } else if (chip->dxs_fixed && viadev->reg_offset < 0x40) { + /* fixed DXS playback rate */ + runtime->hw.rates = SNDRV_PCM_RATE_48000; + runtime->hw.rate_min = runtime->hw.rate_max = 48000; + } else if (chip->dxs_src && viadev->reg_offset < 0x40) { + /* use full SRC capabilities of DXS */ + runtime->hw.rates = (SNDRV_PCM_RATE_CONTINUOUS | + SNDRV_PCM_RATE_8000_48000); + runtime->hw.rate_min = 8000; + runtime->hw.rate_max = 48000; + } else if (! ratep->rate) { + int idx = viadev->direction ? AC97_RATES_ADC : AC97_RATES_FRONT_DAC; + runtime->hw.rates = chip->ac97->rates[idx]; + snd_pcm_limit_hw_rates(runtime); + } else { + /* a fixed rate */ + runtime->hw.rates = SNDRV_PCM_RATE_KNOT; + runtime->hw.rate_max = runtime->hw.rate_min = ratep->rate; + } + spin_unlock_irq(&ratep->lock); + + /* we may remove following constaint when we modify table entries + in interrupt */ + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + + runtime->private_data = viadev; + viadev->substream = substream; + + return 0; +} + + +/* + * open callback for playback on via686 and via823x DSX + */ +static int snd_via82xx_playback_open(struct snd_pcm_substream *substream) +{ + struct via82xx *chip = snd_pcm_substream_chip(substream); + struct viadev *viadev = &chip->devs[chip->playback_devno + substream->number]; + int err; + + if ((err = snd_via82xx_pcm_open(chip, viadev, substream)) < 0) + return err; + return 0; +} + +/* + * open callback for playback on via823x multi-channel + */ +static int snd_via8233_multi_open(struct snd_pcm_substream *substream) +{ + struct via82xx *chip = snd_pcm_substream_chip(substream); + struct viadev *viadev = &chip->devs[chip->multi_devno]; + int err; + /* channels constraint for VIA8233A + * 3 and 5 channels are not supported + */ + static unsigned int channels[] = { + 1, 2, 4, 6 + }; + static struct snd_pcm_hw_constraint_list hw_constraints_channels = { + .count = ARRAY_SIZE(channels), + .list = channels, + .mask = 0, + }; + + if ((err = snd_via82xx_pcm_open(chip, viadev, substream)) < 0) + return err; + substream->runtime->hw.channels_max = 6; + if (chip->revision == VIA_REV_8233A) + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + &hw_constraints_channels); + return 0; +} + +/* + * open callback for capture on via686 and via823x + */ +static int snd_via82xx_capture_open(struct snd_pcm_substream *substream) +{ + struct via82xx *chip = snd_pcm_substream_chip(substream); + struct viadev *viadev = &chip->devs[chip->capture_devno + substream->pcm->device]; + + return snd_via82xx_pcm_open(chip, viadev, substream); +} + +/* + * close callback + */ +static int snd_via82xx_pcm_close(struct snd_pcm_substream *substream) +{ + struct via82xx *chip = snd_pcm_substream_chip(substream); + struct viadev *viadev = substream->runtime->private_data; + struct via_rate_lock *ratep; + + /* release the rate lock */ + ratep = &chip->rates[viadev->direction]; + spin_lock_irq(&ratep->lock); + ratep->used--; + if (! ratep->used) + ratep->rate = 0; + spin_unlock_irq(&ratep->lock); + if (! ratep->rate) { + if (! viadev->direction) { + snd_ac97_update_power(chip->ac97, + AC97_PCM_FRONT_DAC_RATE, 0); + snd_ac97_update_power(chip->ac97, + AC97_PCM_SURR_DAC_RATE, 0); + snd_ac97_update_power(chip->ac97, + AC97_PCM_LFE_DAC_RATE, 0); + } else + snd_ac97_update_power(chip->ac97, + AC97_PCM_LR_ADC_RATE, 0); + } + viadev->substream = NULL; + return 0; +} + + +/* via686 playback callbacks */ +static struct snd_pcm_ops snd_via686_playback_ops = { + .open = snd_via82xx_playback_open, + .close = snd_via82xx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_via82xx_hw_params, + .hw_free = snd_via82xx_hw_free, + .prepare = snd_via686_playback_prepare, + .trigger = snd_via82xx_pcm_trigger, + .pointer = snd_via686_pcm_pointer, + .page = snd_pcm_sgbuf_ops_page, +}; + +/* via686 capture callbacks */ +static struct snd_pcm_ops snd_via686_capture_ops = { + .open = snd_via82xx_capture_open, + .close = snd_via82xx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_via82xx_hw_params, + .hw_free = snd_via82xx_hw_free, + .prepare = snd_via686_capture_prepare, + .trigger = snd_via82xx_pcm_trigger, + .pointer = snd_via686_pcm_pointer, + .page = snd_pcm_sgbuf_ops_page, +}; + +/* via823x DSX playback callbacks */ +static struct snd_pcm_ops snd_via8233_playback_ops = { + .open = snd_via82xx_playback_open, + .close = snd_via82xx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_via82xx_hw_params, + .hw_free = snd_via82xx_hw_free, + .prepare = snd_via8233_playback_prepare, + .trigger = snd_via82xx_pcm_trigger, + .pointer = snd_via8233_pcm_pointer, + .page = snd_pcm_sgbuf_ops_page, +}; + +/* via823x multi-channel playback callbacks */ +static struct snd_pcm_ops snd_via8233_multi_ops = { + .open = snd_via8233_multi_open, + .close = snd_via82xx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_via82xx_hw_params, + .hw_free = snd_via82xx_hw_free, + .prepare = snd_via8233_multi_prepare, + .trigger = snd_via82xx_pcm_trigger, + .pointer = snd_via8233_pcm_pointer, + .page = snd_pcm_sgbuf_ops_page, +}; + +/* via823x capture callbacks */ +static struct snd_pcm_ops snd_via8233_capture_ops = { + .open = snd_via82xx_capture_open, + .close = snd_via82xx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_via82xx_hw_params, + .hw_free = snd_via82xx_hw_free, + .prepare = snd_via8233_capture_prepare, + .trigger = snd_via82xx_pcm_trigger, + .pointer = snd_via8233_pcm_pointer, + .page = snd_pcm_sgbuf_ops_page, +}; + + +static void init_viadev(struct via82xx *chip, int idx, unsigned int reg_offset, + int shadow_pos, int direction) +{ + chip->devs[idx].reg_offset = reg_offset; + chip->devs[idx].shadow_shift = shadow_pos * 4; + chip->devs[idx].direction = direction; + chip->devs[idx].port = chip->port + reg_offset; +} + +/* + * create pcm instances for VIA8233, 8233C and 8235 (not 8233A) + */ +static int __devinit snd_via8233_pcm_new(struct via82xx *chip) +{ + struct snd_pcm *pcm; + int i, err; + + chip->playback_devno = 0; /* x 4 */ + chip->multi_devno = 4; /* x 1 */ + chip->capture_devno = 5; /* x 2 */ + chip->num_devs = 7; + chip->intr_mask = 0x33033333; /* FLAG|EOL for rec0-1, mc, sdx0-3 */ + + /* PCM #0: 4 DSX playbacks and 1 capture */ + err = snd_pcm_new(chip->card, chip->card->shortname, 0, 4, 1, &pcm); + if (err < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops); + pcm->private_data = chip; + strcpy(pcm->name, chip->card->shortname); + chip->pcms[0] = pcm; + /* set up playbacks */ + for (i = 0; i < 4; i++) + init_viadev(chip, i, 0x10 * i, i, 0); + /* capture */ + init_viadev(chip, chip->capture_devno, VIA_REG_CAPTURE_8233_STATUS, 6, 1); + + if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(chip->pci), + 64*1024, 128*1024)) < 0) + return err; + + /* PCM #1: multi-channel playback and 2nd capture */ + err = snd_pcm_new(chip->card, chip->card->shortname, 1, 1, 1, &pcm); + if (err < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_multi_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops); + pcm->private_data = chip; + strcpy(pcm->name, chip->card->shortname); + chip->pcms[1] = pcm; + /* set up playback */ + init_viadev(chip, chip->multi_devno, VIA_REG_MULTPLAY_STATUS, 4, 0); + /* set up capture */ + init_viadev(chip, chip->capture_devno + 1, VIA_REG_CAPTURE_8233_STATUS + 0x10, 7, 1); + + if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(chip->pci), + 64*1024, 128*1024)) < 0) + return err; + + return 0; +} + +/* + * create pcm instances for VIA8233A + */ +static int __devinit snd_via8233a_pcm_new(struct via82xx *chip) +{ + struct snd_pcm *pcm; + int err; + + chip->multi_devno = 0; + chip->playback_devno = 1; + chip->capture_devno = 2; + chip->num_devs = 3; + chip->intr_mask = 0x03033000; /* FLAG|EOL for rec0, mc, sdx3 */ + + /* PCM #0: multi-channel playback and capture */ + err = snd_pcm_new(chip->card, chip->card->shortname, 0, 1, 1, &pcm); + if (err < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_multi_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops); + pcm->private_data = chip; + strcpy(pcm->name, chip->card->shortname); + chip->pcms[0] = pcm; + /* set up playback */ + init_viadev(chip, chip->multi_devno, VIA_REG_MULTPLAY_STATUS, 4, 0); + /* capture */ + init_viadev(chip, chip->capture_devno, VIA_REG_CAPTURE_8233_STATUS, 6, 1); + + if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(chip->pci), + 64*1024, 128*1024)) < 0) + return err; + + /* SPDIF supported? */ + if (! ac97_can_spdif(chip->ac97)) + return 0; + + /* PCM #1: DXS3 playback (for spdif) */ + err = snd_pcm_new(chip->card, chip->card->shortname, 1, 1, 0, &pcm); + if (err < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_playback_ops); + pcm->private_data = chip; + strcpy(pcm->name, chip->card->shortname); + chip->pcms[1] = pcm; + /* set up playback */ + init_viadev(chip, chip->playback_devno, 0x30, 3, 0); + + if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(chip->pci), + 64*1024, 128*1024)) < 0) + return err; + + return 0; +} + +/* + * create a pcm instance for via686a/b + */ +static int __devinit snd_via686_pcm_new(struct via82xx *chip) +{ + struct snd_pcm *pcm; + int err; + + chip->playback_devno = 0; + chip->capture_devno = 1; + chip->num_devs = 2; + chip->intr_mask = 0x77; /* FLAG | EOL for PB, CP, FM */ + + err = snd_pcm_new(chip->card, chip->card->shortname, 0, 1, 1, &pcm); + if (err < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via686_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via686_capture_ops); + pcm->private_data = chip; + strcpy(pcm->name, chip->card->shortname); + chip->pcms[0] = pcm; + init_viadev(chip, 0, VIA_REG_PLAYBACK_STATUS, 0, 0); + init_viadev(chip, 1, VIA_REG_CAPTURE_STATUS, 0, 1); + + if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(chip->pci), + 64*1024, 128*1024)) < 0) + return err; + + return 0; +} + + +/* + * Mixer part + */ + +static int snd_via8233_capture_source_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + /* formerly they were "Line" and "Mic", but it looks like that they + * have nothing to do with the actual physical connections... + */ + static char *texts[2] = { + "Input1", "Input2" + }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item >= 2) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_via8233_capture_source_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct via82xx *chip = snd_kcontrol_chip(kcontrol); + unsigned long port = chip->port + (kcontrol->id.index ? (VIA_REG_CAPTURE_CHANNEL + 0x10) : VIA_REG_CAPTURE_CHANNEL); + ucontrol->value.enumerated.item[0] = inb(port) & VIA_REG_CAPTURE_CHANNEL_MIC ? 1 : 0; + return 0; +} + +static int snd_via8233_capture_source_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct via82xx *chip = snd_kcontrol_chip(kcontrol); + unsigned long port = chip->port + (kcontrol->id.index ? (VIA_REG_CAPTURE_CHANNEL + 0x10) : VIA_REG_CAPTURE_CHANNEL); + u8 val, oval; + + spin_lock_irq(&chip->reg_lock); + oval = inb(port); + val = oval & ~VIA_REG_CAPTURE_CHANNEL_MIC; + if (ucontrol->value.enumerated.item[0]) + val |= VIA_REG_CAPTURE_CHANNEL_MIC; + if (val != oval) + outb(val, port); + spin_unlock_irq(&chip->reg_lock); + return val != oval; +} + +static struct snd_kcontrol_new snd_via8233_capture_source __devinitdata = { + .name = "Input Source Select", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_via8233_capture_source_info, + .get = snd_via8233_capture_source_get, + .put = snd_via8233_capture_source_put, +}; + +#define snd_via8233_dxs3_spdif_info snd_ctl_boolean_mono_info + +static int snd_via8233_dxs3_spdif_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct via82xx *chip = snd_kcontrol_chip(kcontrol); + u8 val; + + pci_read_config_byte(chip->pci, VIA8233_SPDIF_CTRL, &val); + ucontrol->value.integer.value[0] = (val & VIA8233_SPDIF_DX3) ? 1 : 0; + return 0; +} + +static int snd_via8233_dxs3_spdif_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct via82xx *chip = snd_kcontrol_chip(kcontrol); + u8 val, oval; + + pci_read_config_byte(chip->pci, VIA8233_SPDIF_CTRL, &oval); + val = oval & ~VIA8233_SPDIF_DX3; + if (ucontrol->value.integer.value[0]) + val |= VIA8233_SPDIF_DX3; + /* save the spdif flag for rate filtering */ + chip->spdif_on = ucontrol->value.integer.value[0] ? 1 : 0; + if (val != oval) { + pci_write_config_byte(chip->pci, VIA8233_SPDIF_CTRL, val); + return 1; + } + return 0; +} + +static struct snd_kcontrol_new snd_via8233_dxs3_spdif_control __devinitdata = { + .name = SNDRV_CTL_NAME_IEC958("Output ",NONE,SWITCH), + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_via8233_dxs3_spdif_info, + .get = snd_via8233_dxs3_spdif_get, + .put = snd_via8233_dxs3_spdif_put, +}; + +static int snd_via8233_dxs_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = VIA_DXS_MAX_VOLUME; + return 0; +} + +static int snd_via8233_dxs_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct via82xx *chip = snd_kcontrol_chip(kcontrol); + unsigned int idx = snd_ctl_get_ioff(kcontrol, &ucontrol->id); + + ucontrol->value.integer.value[0] = VIA_DXS_MAX_VOLUME - chip->playback_volume[idx][0]; + ucontrol->value.integer.value[1] = VIA_DXS_MAX_VOLUME - chip->playback_volume[idx][1]; + return 0; +} + +static int snd_via8233_pcmdxs_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct via82xx *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = VIA_DXS_MAX_VOLUME - chip->playback_volume_c[0]; + ucontrol->value.integer.value[1] = VIA_DXS_MAX_VOLUME - chip->playback_volume_c[1]; + return 0; +} + +static int snd_via8233_dxs_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct via82xx *chip = snd_kcontrol_chip(kcontrol); + unsigned int idx = snd_ctl_get_ioff(kcontrol, &ucontrol->id); + unsigned long port = chip->port + 0x10 * idx; + unsigned char val; + int i, change = 0; + + for (i = 0; i < 2; i++) { + val = ucontrol->value.integer.value[i]; + if (val > VIA_DXS_MAX_VOLUME) + val = VIA_DXS_MAX_VOLUME; + val = VIA_DXS_MAX_VOLUME - val; + change |= val != chip->playback_volume[idx][i]; + if (change) { + chip->playback_volume[idx][i] = val; + outb(val, port + VIA_REG_OFS_PLAYBACK_VOLUME_L + i); + } + } + return change; +} + +static int snd_via8233_pcmdxs_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct via82xx *chip = snd_kcontrol_chip(kcontrol); + unsigned int idx; + unsigned char val; + int i, change = 0; + + for (i = 0; i < 2; i++) { + val = ucontrol->value.integer.value[i]; + if (val > VIA_DXS_MAX_VOLUME) + val = VIA_DXS_MAX_VOLUME; + val = VIA_DXS_MAX_VOLUME - val; + if (val != chip->playback_volume_c[i]) { + change = 1; + chip->playback_volume_c[i] = val; + for (idx = 0; idx < 4; idx++) { + unsigned long port = chip->port + 0x10 * idx; + chip->playback_volume[idx][i] = val; + outb(val, port + VIA_REG_OFS_PLAYBACK_VOLUME_L + i); + } + } + } + return change; +} + +static const DECLARE_TLV_DB_SCALE(db_scale_dxs, -9450, 150, 1); + +static struct snd_kcontrol_new snd_via8233_pcmdxs_volume_control __devinitdata = { + .name = "PCM Playback Volume", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .info = snd_via8233_dxs_volume_info, + .get = snd_via8233_pcmdxs_volume_get, + .put = snd_via8233_pcmdxs_volume_put, + .tlv = { .p = db_scale_dxs } +}; + +static struct snd_kcontrol_new snd_via8233_dxs_volume_control __devinitdata = { + .name = "VIA DXS Playback Volume", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .count = 4, + .info = snd_via8233_dxs_volume_info, + .get = snd_via8233_dxs_volume_get, + .put = snd_via8233_dxs_volume_put, + .tlv = { .p = db_scale_dxs } +}; + +/* + */ + +static void snd_via82xx_mixer_free_ac97_bus(struct snd_ac97_bus *bus) +{ + struct via82xx *chip = bus->private_data; + chip->ac97_bus = NULL; +} + +static void snd_via82xx_mixer_free_ac97(struct snd_ac97 *ac97) +{ + struct via82xx *chip = ac97->private_data; + chip->ac97 = NULL; +} + +static struct ac97_quirk ac97_quirks[] = { + { + .subvendor = 0x1106, + .subdevice = 0x4161, + .codec_id = 0x56494161, /* VT1612A */ + .name = "Soltek SL-75DRV5", + .type = AC97_TUNE_NONE + }, + { /* FIXME: which codec? */ + .subvendor = 0x1106, + .subdevice = 0x4161, + .name = "ASRock K7VT2", + .type = AC97_TUNE_HP_ONLY + }, + { + .subvendor = 0x1019, + .subdevice = 0x0a81, + .name = "ECS K7VTA3", + .type = AC97_TUNE_HP_ONLY + }, + { + .subvendor = 0x1019, + .subdevice = 0x0a85, + .name = "ECS L7VMM2", + .type = AC97_TUNE_HP_ONLY + }, + { + .subvendor = 0x1849, + .subdevice = 0x3059, + .name = "ASRock K7VM2", + .type = AC97_TUNE_HP_ONLY /* VT1616 */ + }, + { + .subvendor = 0x14cd, + .subdevice = 0x7002, + .name = "Unknown", + .type = AC97_TUNE_ALC_JACK + }, + { + .subvendor = 0x1071, + .subdevice = 0x8590, + .name = "Mitac Mobo", + .type = AC97_TUNE_ALC_JACK + }, + { + .subvendor = 0x161f, + .subdevice = 0x202b, + .name = "Arima Notebook", + .type = AC97_TUNE_HP_ONLY, + }, + { + .subvendor = 0x161f, + .subdevice = 0x2032, + .name = "Targa Traveller 811", + .type = AC97_TUNE_HP_ONLY, + }, + { + .subvendor = 0x161f, + .subdevice = 0x2032, + .name = "m680x", + .type = AC97_TUNE_HP_ONLY, /* http://launchpad.net/bugs/38546 */ + }, + { } /* terminator */ +}; + +static int __devinit snd_via82xx_mixer_new(struct via82xx *chip, const char *quirk_override) +{ + struct snd_ac97_template ac97; + int err; + static struct snd_ac97_bus_ops ops = { + .write = snd_via82xx_codec_write, + .read = snd_via82xx_codec_read, + .wait = snd_via82xx_codec_wait, + }; + + if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus)) < 0) + return err; + chip->ac97_bus->private_free = snd_via82xx_mixer_free_ac97_bus; + chip->ac97_bus->clock = chip->ac97_clock; + + memset(&ac97, 0, sizeof(ac97)); + ac97.private_data = chip; + ac97.private_free = snd_via82xx_mixer_free_ac97; + ac97.pci = chip->pci; + ac97.scaps = AC97_SCAP_SKIP_MODEM | AC97_SCAP_POWER_SAVE; + if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0) + return err; + + snd_ac97_tune_hardware(chip->ac97, ac97_quirks, quirk_override); + + if (chip->chip_type != TYPE_VIA686) { + /* use slot 10/11 */ + snd_ac97_update_bits(chip->ac97, AC97_EXTENDED_STATUS, 0x03 << 4, 0x03 << 4); + } + + return 0; +} + +#ifdef SUPPORT_JOYSTICK +#define JOYSTICK_ADDR 0x200 +static int __devinit snd_via686_create_gameport(struct via82xx *chip, unsigned char *legacy) +{ + struct gameport *gp; + struct resource *r; + + if (!joystick) + return -ENODEV; + + r = request_region(JOYSTICK_ADDR, 8, "VIA686 gameport"); + if (!r) { + printk(KERN_WARNING "via82xx: cannot reserve joystick port 0x%#x\n", + JOYSTICK_ADDR); + return -EBUSY; + } + + chip->gameport = gp = gameport_allocate_port(); + if (!gp) { + printk(KERN_ERR "via82xx: cannot allocate memory for gameport\n"); + release_and_free_resource(r); + return -ENOMEM; + } + + gameport_set_name(gp, "VIA686 Gameport"); + gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci)); + gameport_set_dev_parent(gp, &chip->pci->dev); + gp->io = JOYSTICK_ADDR; + gameport_set_port_data(gp, r); + + /* Enable legacy joystick port */ + *legacy |= VIA_FUNC_ENABLE_GAME; + pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, *legacy); + + gameport_register_port(chip->gameport); + + return 0; +} + +static void snd_via686_free_gameport(struct via82xx *chip) +{ + if (chip->gameport) { + struct resource *r = gameport_get_port_data(chip->gameport); + + gameport_unregister_port(chip->gameport); + chip->gameport = NULL; + release_and_free_resource(r); + } +} +#else +static inline int snd_via686_create_gameport(struct via82xx *chip, unsigned char *legacy) +{ + return -ENOSYS; +} +static inline void snd_via686_free_gameport(struct via82xx *chip) { } +#endif + + +/* + * + */ + +static int __devinit snd_via8233_init_misc(struct via82xx *chip) +{ + int i, err, caps; + unsigned char val; + + caps = chip->chip_type == TYPE_VIA8233A ? 1 : 2; + for (i = 0; i < caps; i++) { + snd_via8233_capture_source.index = i; + err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_capture_source, chip)); + if (err < 0) + return err; + } + if (ac97_can_spdif(chip->ac97)) { + err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_dxs3_spdif_control, chip)); + if (err < 0) + return err; + } + if (chip->chip_type != TYPE_VIA8233A) { + /* when no h/w PCM volume control is found, use DXS volume control + * as the PCM vol control + */ + struct snd_ctl_elem_id sid; + memset(&sid, 0, sizeof(sid)); + strcpy(sid.name, "PCM Playback Volume"); + sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER; + if (! snd_ctl_find_id(chip->card, &sid)) { + snd_printd(KERN_INFO "Using DXS as PCM Playback\n"); + err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_pcmdxs_volume_control, chip)); + if (err < 0) + return err; + } + else /* Using DXS when PCM emulation is enabled is really weird */ + { + /* Standalone DXS controls */ + err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_dxs_volume_control, chip)); + if (err < 0) + return err; + } + } + /* select spdif data slot 10/11 */ + pci_read_config_byte(chip->pci, VIA8233_SPDIF_CTRL, &val); + val = (val & ~VIA8233_SPDIF_SLOT_MASK) | VIA8233_SPDIF_SLOT_1011; + val &= ~VIA8233_SPDIF_DX3; /* SPDIF off as default */ + pci_write_config_byte(chip->pci, VIA8233_SPDIF_CTRL, val); + + return 0; +} + +static int __devinit snd_via686_init_misc(struct via82xx *chip) +{ + unsigned char legacy, legacy_cfg; + int rev_h = 0; + + legacy = chip->old_legacy; + legacy_cfg = chip->old_legacy_cfg; + legacy |= VIA_FUNC_MIDI_IRQMASK; /* FIXME: correct? (disable MIDI) */ + legacy &= ~VIA_FUNC_ENABLE_GAME; /* disable joystick */ + if (chip->revision >= VIA_REV_686_H) { + rev_h = 1; + if (mpu_port >= 0x200) { /* force MIDI */ + mpu_port &= 0xfffc; + pci_write_config_dword(chip->pci, 0x18, mpu_port | 0x01); +#ifdef CONFIG_PM + chip->mpu_port_saved = mpu_port; +#endif + } else { + mpu_port = pci_resource_start(chip->pci, 2); + } + } else { + switch (mpu_port) { /* force MIDI */ + case 0x300: + case 0x310: + case 0x320: + case 0x330: + legacy_cfg &= ~(3 << 2); + legacy_cfg |= (mpu_port & 0x0030) >> 2; + break; + default: /* no, use BIOS settings */ + if (legacy & VIA_FUNC_ENABLE_MIDI) + mpu_port = 0x300 + ((legacy_cfg & 0x000c) << 2); + break; + } + } + if (mpu_port >= 0x200 && + (chip->mpu_res = request_region(mpu_port, 2, "VIA82xx MPU401")) + != NULL) { + if (rev_h) + legacy |= VIA_FUNC_MIDI_PNP; /* enable PCI I/O 2 */ + legacy |= VIA_FUNC_ENABLE_MIDI; + } else { + if (rev_h) + legacy &= ~VIA_FUNC_MIDI_PNP; /* disable PCI I/O 2 */ + legacy &= ~VIA_FUNC_ENABLE_MIDI; + mpu_port = 0; + } + + pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, legacy); + pci_write_config_byte(chip->pci, VIA_PNP_CONTROL, legacy_cfg); + if (chip->mpu_res) { + if (snd_mpu401_uart_new(chip->card, 0, MPU401_HW_VIA686A, + mpu_port, MPU401_INFO_INTEGRATED, + chip->irq, 0, &chip->rmidi) < 0) { + printk(KERN_WARNING "unable to initialize MPU-401" + " at 0x%lx, skipping\n", mpu_port); + legacy &= ~VIA_FUNC_ENABLE_MIDI; + } else { + legacy &= ~VIA_FUNC_MIDI_IRQMASK; /* enable MIDI interrupt */ + } + pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, legacy); + } + + snd_via686_create_gameport(chip, &legacy); + +#ifdef CONFIG_PM + chip->legacy_saved = legacy; + chip->legacy_cfg_saved = legacy_cfg; +#endif + + return 0; +} + + +/* + * proc interface + */ +static void snd_via82xx_proc_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct via82xx *chip = entry->private_data; + int i; + + snd_iprintf(buffer, "%s\n\n", chip->card->longname); + for (i = 0; i < 0xa0; i += 4) { + snd_iprintf(buffer, "%02x: %08x\n", i, inl(chip->port + i)); + } +} + +static void __devinit snd_via82xx_proc_init(struct via82xx *chip) +{ + struct snd_info_entry *entry; + + if (! snd_card_proc_new(chip->card, "via82xx", &entry)) + snd_info_set_text_ops(entry, chip, snd_via82xx_proc_read); +} + +/* + * + */ + +static int snd_via82xx_chip_init(struct via82xx *chip) +{ + unsigned int val; + unsigned long end_time; + unsigned char pval; + +#if 0 /* broken on K7M? */ + if (chip->chip_type == TYPE_VIA686) + /* disable all legacy ports */ + pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, 0); +#endif + pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval); + if (! (pval & VIA_ACLINK_C00_READY)) { /* codec not ready? */ + /* deassert ACLink reset, force SYNC */ + pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL, + VIA_ACLINK_CTRL_ENABLE | + VIA_ACLINK_CTRL_RESET | + VIA_ACLINK_CTRL_SYNC); + udelay(100); +#if 1 /* FIXME: should we do full reset here for all chip models? */ + pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL, 0x00); + udelay(100); +#else + /* deassert ACLink reset, force SYNC (warm AC'97 reset) */ + pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL, + VIA_ACLINK_CTRL_RESET|VIA_ACLINK_CTRL_SYNC); + udelay(2); +#endif + /* ACLink on, deassert ACLink reset, VSR, SGD data out */ + /* note - FM data out has trouble with non VRA codecs !! */ + pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL, VIA_ACLINK_CTRL_INIT); + udelay(100); + } + + /* Make sure VRA is enabled, in case we didn't do a + * complete codec reset, above */ + pci_read_config_byte(chip->pci, VIA_ACLINK_CTRL, &pval); + if ((pval & VIA_ACLINK_CTRL_INIT) != VIA_ACLINK_CTRL_INIT) { + /* ACLink on, deassert ACLink reset, VSR, SGD data out */ + /* note - FM data out has trouble with non VRA codecs !! */ + pci_write_config_byte(chip->pci, VIA_ACLINK_CTRL, VIA_ACLINK_CTRL_INIT); + udelay(100); + } + + /* wait until codec ready */ + end_time = jiffies + msecs_to_jiffies(750); + do { + pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval); + if (pval & VIA_ACLINK_C00_READY) /* primary codec ready */ + break; + schedule_timeout(1); + } while (time_before(jiffies, end_time)); + + if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY) + snd_printk(KERN_ERR "AC'97 codec is not ready [0x%x]\n", val); + +#if 0 /* FIXME: we don't support the second codec yet so skip the detection now.. */ + snd_via82xx_codec_xwrite(chip, VIA_REG_AC97_READ | + VIA_REG_AC97_SECONDARY_VALID | + (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT)); + end_time = jiffies + msecs_to_jiffies(750); + snd_via82xx_codec_xwrite(chip, VIA_REG_AC97_READ | + VIA_REG_AC97_SECONDARY_VALID | + (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT)); + do { + if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_SECONDARY_VALID) { + chip->ac97_secondary = 1; + goto __ac97_ok2; + } + schedule_timeout(1); + } while (time_before(jiffies, end_time)); + /* This is ok, the most of motherboards have only one codec */ + + __ac97_ok2: +#endif + + if (chip->chip_type == TYPE_VIA686) { + /* route FM trap to IRQ, disable FM trap */ + pci_write_config_byte(chip->pci, VIA_FM_NMI_CTRL, 0); + /* disable all GPI interrupts */ + outl(0, VIAREG(chip, GPI_INTR)); + } + + if (chip->chip_type != TYPE_VIA686) { + /* Workaround for Award BIOS bug: + * DXS channels don't work properly with VRA if MC97 is disabled. + */ + struct pci_dev *pci; + pci = pci_get_device(0x1106, 0x3068, NULL); /* MC97 */ + if (pci) { + unsigned char data; + pci_read_config_byte(pci, 0x44, &data); + pci_write_config_byte(pci, 0x44, data | 0x40); + pci_dev_put(pci); + } + } + + if (chip->chip_type != TYPE_VIA8233A) { + int i, idx; + for (idx = 0; idx < 4; idx++) { + unsigned long port = chip->port + 0x10 * idx; + for (i = 0; i < 2; i++) { + chip->playback_volume[idx][i]=chip->playback_volume_c[i]; + outb(chip->playback_volume_c[i], + port + VIA_REG_OFS_PLAYBACK_VOLUME_L + i); + } + } + } + + return 0; +} + +#ifdef CONFIG_PM +/* + * power management + */ +static int snd_via82xx_suspend(struct pci_dev *pci, pm_message_t state) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct via82xx *chip = card->private_data; + int i; + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + for (i = 0; i < 2; i++) + snd_pcm_suspend_all(chip->pcms[i]); + for (i = 0; i < chip->num_devs; i++) + snd_via82xx_channel_reset(chip, &chip->devs[i]); + synchronize_irq(chip->irq); + snd_ac97_suspend(chip->ac97); + + /* save misc values */ + if (chip->chip_type != TYPE_VIA686) { + pci_read_config_byte(chip->pci, VIA8233_SPDIF_CTRL, &chip->spdif_ctrl_saved); + chip->capture_src_saved[0] = inb(chip->port + VIA_REG_CAPTURE_CHANNEL); + chip->capture_src_saved[1] = inb(chip->port + VIA_REG_CAPTURE_CHANNEL + 0x10); + } + + pci_disable_device(pci); + pci_save_state(pci); + pci_set_power_state(pci, pci_choose_state(pci, state)); + return 0; +} + +static int snd_via82xx_resume(struct pci_dev *pci) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct via82xx *chip = card->private_data; + int i; + + pci_set_power_state(pci, PCI_D0); + pci_restore_state(pci); + if (pci_enable_device(pci) < 0) { + printk(KERN_ERR "via82xx: pci_enable_device failed, " + "disabling device\n"); + snd_card_disconnect(card); + return -EIO; + } + pci_set_master(pci); + + snd_via82xx_chip_init(chip); + + if (chip->chip_type == TYPE_VIA686) { + if (chip->mpu_port_saved) + pci_write_config_dword(chip->pci, 0x18, chip->mpu_port_saved | 0x01); + pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, chip->legacy_saved); + pci_write_config_byte(chip->pci, VIA_PNP_CONTROL, chip->legacy_cfg_saved); + } else { + pci_write_config_byte(chip->pci, VIA8233_SPDIF_CTRL, chip->spdif_ctrl_saved); + outb(chip->capture_src_saved[0], chip->port + VIA_REG_CAPTURE_CHANNEL); + outb(chip->capture_src_saved[1], chip->port + VIA_REG_CAPTURE_CHANNEL + 0x10); + } + + snd_ac97_resume(chip->ac97); + + for (i = 0; i < chip->num_devs; i++) + snd_via82xx_channel_reset(chip, &chip->devs[i]); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + return 0; +} +#endif /* CONFIG_PM */ + +static int snd_via82xx_free(struct via82xx *chip) +{ + unsigned int i; + + if (chip->irq < 0) + goto __end_hw; + /* disable interrupts */ + for (i = 0; i < chip->num_devs; i++) + snd_via82xx_channel_reset(chip, &chip->devs[i]); + synchronize_irq(chip->irq); + __end_hw: + if (chip->irq >= 0) + free_irq(chip->irq, chip); + release_and_free_resource(chip->mpu_res); + pci_release_regions(chip->pci); + + if (chip->chip_type == TYPE_VIA686) { + snd_via686_free_gameport(chip); + pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, chip->old_legacy); + pci_write_config_byte(chip->pci, VIA_PNP_CONTROL, chip->old_legacy_cfg); + } + pci_disable_device(chip->pci); + kfree(chip); + return 0; +} + +static int snd_via82xx_dev_free(struct snd_device *device) +{ + struct via82xx *chip = device->device_data; + return snd_via82xx_free(chip); +} + +static int __devinit snd_via82xx_create(struct snd_card *card, + struct pci_dev *pci, + int chip_type, + int revision, + unsigned int ac97_clock, + struct via82xx ** r_via) +{ + struct via82xx *chip; + int err; + static struct snd_device_ops ops = { + .dev_free = snd_via82xx_dev_free, + }; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + if ((chip = kzalloc(sizeof(*chip), GFP_KERNEL)) == NULL) { + pci_disable_device(pci); + return -ENOMEM; + } + + chip->chip_type = chip_type; + chip->revision = revision; + + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->rates[0].lock); + spin_lock_init(&chip->rates[1].lock); + chip->card = card; + chip->pci = pci; + chip->irq = -1; + + pci_read_config_byte(pci, VIA_FUNC_ENABLE, &chip->old_legacy); + pci_read_config_byte(pci, VIA_PNP_CONTROL, &chip->old_legacy_cfg); + pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, + chip->old_legacy & ~(VIA_FUNC_ENABLE_SB|VIA_FUNC_ENABLE_FM)); + + if ((err = pci_request_regions(pci, card->driver)) < 0) { + kfree(chip); + pci_disable_device(pci); + return err; + } + chip->port = pci_resource_start(pci, 0); + if (request_irq(pci->irq, + chip_type == TYPE_VIA8233 ? + snd_via8233_interrupt : snd_via686_interrupt, + IRQF_SHARED, + card->driver, chip)) { + snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); + snd_via82xx_free(chip); + return -EBUSY; + } + chip->irq = pci->irq; + if (ac97_clock >= 8000 && ac97_clock <= 48000) + chip->ac97_clock = ac97_clock; + synchronize_irq(chip->irq); + + if ((err = snd_via82xx_chip_init(chip)) < 0) { + snd_via82xx_free(chip); + return err; + } + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_via82xx_free(chip); + return err; + } + + /* The 8233 ac97 controller does not implement the master bit + * in the pci command register. IMHO this is a violation of the PCI spec. + * We call pci_set_master here because it does not hurt. */ + pci_set_master(pci); + + snd_card_set_dev(card, &pci->dev); + + *r_via = chip; + return 0; +} + +struct via823x_info { + int revision; + char *name; + int type; +}; +static struct via823x_info via823x_cards[] __devinitdata = { + { VIA_REV_PRE_8233, "VIA 8233-Pre", TYPE_VIA8233 }, + { VIA_REV_8233C, "VIA 8233C", TYPE_VIA8233 }, + { VIA_REV_8233, "VIA 8233", TYPE_VIA8233 }, + { VIA_REV_8233A, "VIA 8233A", TYPE_VIA8233A }, + { VIA_REV_8235, "VIA 8235", TYPE_VIA8233 }, + { VIA_REV_8237, "VIA 8237", TYPE_VIA8233 }, + { VIA_REV_8251, "VIA 8251", TYPE_VIA8233 }, +}; + +/* + * auto detection of DXS channel supports. + */ + +static struct snd_pci_quirk dxs_whitelist[] __devinitdata = { + SND_PCI_QUIRK(0x1005, 0x4710, "Avance Logic Mobo", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x1019, 0x0996, "ESC Mobo", VIA_DXS_48K), + SND_PCI_QUIRK(0x1019, 0x0a81, "ECS K7VTA3 v8.0", VIA_DXS_NO_VRA), + SND_PCI_QUIRK(0x1019, 0x0a85, "ECS L7VMM2", VIA_DXS_NO_VRA), + SND_PCI_QUIRK(0x1019, 0, "ESC K8", VIA_DXS_SRC), + SND_PCI_QUIRK(0x1019, 0xaa01, "ESC K8T890-A", VIA_DXS_SRC), + SND_PCI_QUIRK(0x1025, 0x0033, "Acer Inspire 1353LM", VIA_DXS_NO_VRA), + SND_PCI_QUIRK(0x1025, 0x0046, "Acer Aspire 1524 WLMi", VIA_DXS_SRC), + SND_PCI_QUIRK(0x1043, 0, "ASUS A7/A8", VIA_DXS_NO_VRA), + SND_PCI_QUIRK(0x1071, 0, "Diverse Notebook", VIA_DXS_NO_VRA), + SND_PCI_QUIRK(0x10cf, 0x118e, "FSC Laptop", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x1106, 0, "ASRock", VIA_DXS_SRC), + SND_PCI_QUIRK(0x1297, 0xa232, "Shuttle", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x1297, 0xc160, "Shuttle Sk41G", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte GA-7VAXP", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x1462, 0x3800, "MSI KT266", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x1462, 0x7120, "MSI KT4V", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x1462, 0x7142, "MSI K8MM-V", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x1462, 0, "MSI Mobo", VIA_DXS_SRC), + SND_PCI_QUIRK(0x147b, 0x1401, "ABIT KD7(-RAID)", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x147b, 0x1411, "ABIT VA-20", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x147b, 0x1413, "ABIT KV8 Pro", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x147b, 0x1415, "ABIT AV8", VIA_DXS_NO_VRA), + SND_PCI_QUIRK(0x14ff, 0x0403, "Twinhead mobo", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x14ff, 0x0408, "Twinhead laptop", VIA_DXS_SRC), + SND_PCI_QUIRK(0x1558, 0x4701, "Clevo D470", VIA_DXS_SRC), + SND_PCI_QUIRK(0x1584, 0x8120, "Diverse Laptop", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x1584, 0x8123, "Targa/Uniwill", VIA_DXS_NO_VRA), + SND_PCI_QUIRK(0x161f, 0x202b, "Amira Notebook", VIA_DXS_NO_VRA), + SND_PCI_QUIRK(0x161f, 0x2032, "m680x machines", VIA_DXS_48K), + SND_PCI_QUIRK(0x1631, 0xe004, "PB EasyNote 3174", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x1695, 0x3005, "EPoX EP-8K9A", VIA_DXS_ENABLE), + SND_PCI_QUIRK(0x1695, 0, "EPoX mobo", VIA_DXS_SRC), + SND_PCI_QUIRK(0x16f3, 0, "Jetway K8", VIA_DXS_SRC), + SND_PCI_QUIRK(0x1734, 0, "FSC Laptop", VIA_DXS_SRC), + SND_PCI_QUIRK(0x1849, 0x3059, "ASRock K7VM2", VIA_DXS_NO_VRA), + SND_PCI_QUIRK(0x1849, 0, "ASRock mobo", VIA_DXS_SRC), + SND_PCI_QUIRK(0x1919, 0x200a, "Soltek SL-K8", VIA_DXS_NO_VRA), + SND_PCI_QUIRK(0x4005, 0x4710, "MSI K7T266", VIA_DXS_SRC), + { } /* terminator */ +}; + +static int __devinit check_dxs_list(struct pci_dev *pci, int revision) +{ + const struct snd_pci_quirk *w; + + w = snd_pci_quirk_lookup(pci, dxs_whitelist); + if (w) { + snd_printdd(KERN_INFO "via82xx: DXS white list for %s found\n", + w->name); + return w->value; + } + + /* for newer revision, default to DXS_SRC */ + if (revision >= VIA_REV_8235) + return VIA_DXS_SRC; + + /* + * not detected, try 48k rate only to be sure. + */ + printk(KERN_INFO "via82xx: Assuming DXS channels with 48k fixed sample rate.\n"); + printk(KERN_INFO " Please try dxs_support=5 option\n"); + printk(KERN_INFO " and report if it works on your machine.\n"); + printk(KERN_INFO " For more details, read ALSA-Configuration.txt.\n"); + return VIA_DXS_48K; +}; + +static int __devinit snd_via82xx_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + struct snd_card *card; + struct via82xx *chip; + int chip_type = 0, card_type; + unsigned int i; + int err; + + card = snd_card_new(index, id, THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + card_type = pci_id->driver_data; + switch (card_type) { + case TYPE_CARD_VIA686: + strcpy(card->driver, "VIA686A"); + sprintf(card->shortname, "VIA 82C686A/B rev%x", pci->revision); + chip_type = TYPE_VIA686; + break; + case TYPE_CARD_VIA8233: + chip_type = TYPE_VIA8233; + sprintf(card->shortname, "VIA 823x rev%x", pci->revision); + for (i = 0; i < ARRAY_SIZE(via823x_cards); i++) { + if (pci->revision == via823x_cards[i].revision) { + chip_type = via823x_cards[i].type; + strcpy(card->shortname, via823x_cards[i].name); + break; + } + } + if (chip_type != TYPE_VIA8233A) { + if (dxs_support == VIA_DXS_AUTO) + dxs_support = check_dxs_list(pci, pci->revision); + /* force to use VIA8233 or 8233A model according to + * dxs_support module option + */ + if (dxs_support == VIA_DXS_DISABLE) + chip_type = TYPE_VIA8233A; + else + chip_type = TYPE_VIA8233; + } + if (chip_type == TYPE_VIA8233A) + strcpy(card->driver, "VIA8233A"); + else if (pci->revision >= VIA_REV_8237) + strcpy(card->driver, "VIA8237"); /* no slog assignment */ + else + strcpy(card->driver, "VIA8233"); + break; + default: + snd_printk(KERN_ERR "invalid card type %d\n", card_type); + err = -EINVAL; + goto __error; + } + + if ((err = snd_via82xx_create(card, pci, chip_type, pci->revision, + ac97_clock, &chip)) < 0) + goto __error; + card->private_data = chip; + if ((err = snd_via82xx_mixer_new(chip, ac97_quirk)) < 0) + goto __error; + + if (chip_type == TYPE_VIA686) { + if ((err = snd_via686_pcm_new(chip)) < 0 || + (err = snd_via686_init_misc(chip)) < 0) + goto __error; + } else { + if (chip_type == TYPE_VIA8233A) { + if ((err = snd_via8233a_pcm_new(chip)) < 0) + goto __error; + // chip->dxs_fixed = 1; /* FIXME: use 48k for DXS #3? */ + } else { + if ((err = snd_via8233_pcm_new(chip)) < 0) + goto __error; + if (dxs_support == VIA_DXS_48K) + chip->dxs_fixed = 1; + else if (dxs_support == VIA_DXS_NO_VRA) + chip->no_vra = 1; + else if (dxs_support == VIA_DXS_SRC) { + chip->no_vra = 1; + chip->dxs_src = 1; + } + } + if ((err = snd_via8233_init_misc(chip)) < 0) + goto __error; + } + + /* disable interrupts */ + for (i = 0; i < chip->num_devs; i++) + snd_via82xx_channel_reset(chip, &chip->devs[i]); + + snprintf(card->longname, sizeof(card->longname), + "%s with %s at %#lx, irq %d", card->shortname, + snd_ac97_get_short_name(chip->ac97), chip->port, chip->irq); + + snd_via82xx_proc_init(chip); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + return 0; + + __error: + snd_card_free(card); + return err; +} + +static void __devexit snd_via82xx_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "VIA 82xx Audio", + .id_table = snd_via82xx_ids, + .probe = snd_via82xx_probe, + .remove = __devexit_p(snd_via82xx_remove), +#ifdef CONFIG_PM + .suspend = snd_via82xx_suspend, + .resume = snd_via82xx_resume, +#endif +}; + +static int __init alsa_card_via82xx_init(void) +{ + return pci_register_driver(&driver); +} + +static void __exit alsa_card_via82xx_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_via82xx_init) +module_exit(alsa_card_via82xx_exit) diff -Nur sound/pci/via82xx_modem.c sound/pci/via82xx_modem.c --- sound/pci/via82xx_modem.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/via82xx_modem.c 2007-08-21 17:37:19.000000000 +0200 @@ -983,7 +983,7 @@ pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval); if (pval & VIA_ACLINK_C00_READY) /* primary codec ready */ break; - schedule_timeout_uninterruptible(1); + schedule_timeout(1); } while (time_before(jiffies, end_time)); if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY) @@ -1001,7 +1001,7 @@ chip->ac97_secondary = 1; goto __ac97_ok2; } - schedule_timeout_interruptible(1); + schedule_timeout(1); } while (time_before(jiffies, end_time)); /* This is ok, the most of motherboards have only one codec */ diff -Nur sound/pci/ymfpci/ymfpci_main.c sound/pci/ymfpci/ymfpci_main.c --- sound/pci/ymfpci/ymfpci_main.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pci/ymfpci/ymfpci_main.c 2007-08-21 17:37:19.000000000 +0200 @@ -171,17 +171,6 @@ return val[0]; } -static void snd_ymfpci_pcm_441_volume_set(struct snd_ymfpci_pcm *ypcm) -{ - unsigned int value; - struct snd_ymfpci_pcm_mixer *mixer; - - mixer = &ypcm->chip->pcm_mixer[ypcm->substream->number]; - value = min_t(unsigned int, mixer->left, 0x7fff) >> 1; - value |= (min_t(unsigned int, mixer->right, 0x7fff) >> 1) << 16; - snd_ymfpci_writel(ypcm->chip, YDSXGR_BUF441OUTVOL, value); -} - /* * Hardware start management */ @@ -389,6 +378,7 @@ { struct snd_ymfpci *chip = snd_pcm_substream_chip(substream); struct snd_ymfpci_pcm *ypcm = substream->runtime->private_data; + struct snd_kcontrol *kctl = NULL; int result = 0; spin_lock(&chip->reg_lock); @@ -406,6 +396,11 @@ ypcm->running = 1; break; case SNDRV_PCM_TRIGGER_STOP: + if (substream->pcm == chip->pcm && !ypcm->use_441_slot) { + kctl = chip->pcm_mixer[substream->number].ctl; + kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + } + /* fall through */ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: chip->ctrl_playback[ypcm->voices[0]->number + 1] = 0; @@ -419,6 +414,8 @@ } __unlock: spin_unlock(&chip->reg_lock); + if (kctl) + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_INFO, &kctl->id); return result; } static int snd_ymfpci_capture_trigger(struct snd_pcm_substream *substream, @@ -526,7 +523,6 @@ ypcm->chip->src441_used = voice->number; ypcm->use_441_slot = 1; format |= 0x10000000; - snd_ymfpci_pcm_441_volume_set(ypcm); } if (ypcm->chip->src441_used == voice->number && (format & 0x10000000) == 0) { @@ -667,6 +663,7 @@ struct snd_ymfpci *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct snd_ymfpci_pcm *ypcm = runtime->private_data; + struct snd_kcontrol *kctl; unsigned int nvoice; ypcm->period_size = runtime->period_size; @@ -676,6 +673,12 @@ for (nvoice = 0; nvoice < runtime->channels; nvoice++) snd_ymfpci_pcm_init_voice(ypcm, nvoice, runtime, substream->pcm == chip->pcm); + + if (substream->pcm == chip->pcm && !ypcm->use_441_slot) { + kctl = chip->pcm_mixer[substream->number].ctl; + kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_INFO, &kctl->id); + } return 0; } @@ -926,7 +929,6 @@ struct snd_ymfpci *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct snd_ymfpci_pcm *ypcm; - struct snd_kcontrol *kctl; int err; if ((err = snd_ymfpci_playback_open_1(substream)) < 0) @@ -941,10 +943,6 @@ chip->rear_opened++; } spin_unlock_irq(&chip->reg_lock); - - kctl = chip->pcm_mixer[substream->number].ctl; - kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; - snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_INFO, &kctl->id); return 0; } @@ -1039,7 +1037,6 @@ { struct snd_ymfpci *chip = snd_pcm_substream_chip(substream); struct snd_ymfpci_pcm *ypcm = substream->runtime->private_data; - struct snd_kcontrol *kctl; spin_lock_irq(&chip->reg_lock); if (ypcm->output_rear && chip->rear_opened > 0) { @@ -1047,9 +1044,6 @@ ymfpci_close_extension(chip); } spin_unlock_irq(&chip->reg_lock); - kctl = chip->pcm_mixer[substream->number].ctl; - kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; - snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_INFO, &kctl->id); return snd_ymfpci_playback_close_1(substream); } @@ -1443,22 +1437,7 @@ .get = snd_ymfpci_get_single, .put = snd_ymfpci_put_single, \ .private_value = ((reg) | ((shift) << 16)) } -static int snd_ymfpci_info_single(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - int reg = kcontrol->private_value & 0xffff; - - switch (reg) { - case YDSXGR_SPDIFOUTCTRL: break; - case YDSXGR_SPDIFINCTRL: break; - default: return -EINVAL; - } - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_ymfpci_info_single snd_ctl_boolean_mono_info static int snd_ymfpci_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1567,17 +1546,30 @@ return change; } +static int snd_ymfpci_put_nativedacvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol); + unsigned int reg = YDSXGR_NATIVEDACOUTVOL; + unsigned int reg2 = YDSXGR_BUF441OUTVOL; + int change; + unsigned int value, oval; + + value = ucontrol->value.integer.value[0] & 0x3fff; + value |= (ucontrol->value.integer.value[1] & 0x3fff) << 16; + spin_lock_irq(&chip->reg_lock); + oval = snd_ymfpci_readl(chip, reg); + change = value != oval; + snd_ymfpci_writel(chip, reg, value); + snd_ymfpci_writel(chip, reg2, value); + spin_unlock_irq(&chip->reg_lock); + return change; +} + /* * 4ch duplication */ -static int snd_ymfpci_info_dup4ch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_ymfpci_info_dup4ch snd_ctl_boolean_mono_info static int snd_ymfpci_get_dup4ch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1598,7 +1590,17 @@ static struct snd_kcontrol_new snd_ymfpci_controls[] __devinitdata = { -YMFPCI_DOUBLE("Wave Playback Volume", 0, YDSXGR_NATIVEDACOUTVOL), +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Wave Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = snd_ymfpci_info_double, + .get = snd_ymfpci_get_double, + .put = snd_ymfpci_put_nativedacvol, + .private_value = YDSXGR_NATIVEDACOUTVOL, + .tlv = { .p = db_scale_native }, +}, YMFPCI_DOUBLE("Wave Capture Volume", 0, YDSXGR_NATIVEDACLOOPVOL), YMFPCI_DOUBLE("Digital Capture Volume", 0, YDSXGR_NATIVEDACINVOL), YMFPCI_DOUBLE("Digital Capture Volume", 1, YDSXGR_NATIVEADCINVOL), @@ -1665,14 +1667,7 @@ return 0; } -static int snd_ymfpci_gpio_sw_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_ymfpci_gpio_sw_info snd_ctl_boolean_mono_info static int snd_ymfpci_gpio_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1748,8 +1743,6 @@ struct snd_ymfpci_pcm *ypcm = substream->runtime->private_data; if (!ypcm->use_441_slot) ypcm->update_pcm_vol = 2; - else - snd_ymfpci_pcm_441_volume_set(ypcm); } spin_unlock_irqrestore(&chip->voice_lock, flags); return 1; diff -Nur sound/pci/ymfpci/ymfpci_main.c~ sound/pci/ymfpci/ymfpci_main.c~ --- sound/pci/ymfpci/ymfpci_main.c~ 1970-01-01 01:00:00.000000000 +0100 +++ sound/pci/ymfpci/ymfpci_main.c~ 2007-07-24 02:00:10.000000000 +0200 @@ -0,0 +1,2464 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Routines for control of YMF724/740/744/754 chips + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * common I/O routines + */ + +static void snd_ymfpci_irq_wait(struct snd_ymfpci *chip); + +static inline u8 snd_ymfpci_readb(struct snd_ymfpci *chip, u32 offset) +{ + return readb(chip->reg_area_virt + offset); +} + +static inline void snd_ymfpci_writeb(struct snd_ymfpci *chip, u32 offset, u8 val) +{ + writeb(val, chip->reg_area_virt + offset); +} + +static inline u16 snd_ymfpci_readw(struct snd_ymfpci *chip, u32 offset) +{ + return readw(chip->reg_area_virt + offset); +} + +static inline void snd_ymfpci_writew(struct snd_ymfpci *chip, u32 offset, u16 val) +{ + writew(val, chip->reg_area_virt + offset); +} + +static inline u32 snd_ymfpci_readl(struct snd_ymfpci *chip, u32 offset) +{ + return readl(chip->reg_area_virt + offset); +} + +static inline void snd_ymfpci_writel(struct snd_ymfpci *chip, u32 offset, u32 val) +{ + writel(val, chip->reg_area_virt + offset); +} + +static int snd_ymfpci_codec_ready(struct snd_ymfpci *chip, int secondary) +{ + unsigned long end_time; + u32 reg = secondary ? YDSXGR_SECSTATUSADR : YDSXGR_PRISTATUSADR; + + end_time = jiffies + msecs_to_jiffies(750); + do { + if ((snd_ymfpci_readw(chip, reg) & 0x8000) == 0) + return 0; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout_uninterruptible(1); + } while (time_before(jiffies, end_time)); + snd_printk(KERN_ERR "codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_ymfpci_readw(chip, reg)); + return -EBUSY; +} + +static void snd_ymfpci_codec_write(struct snd_ac97 *ac97, u16 reg, u16 val) +{ + struct snd_ymfpci *chip = ac97->private_data; + u32 cmd; + + snd_ymfpci_codec_ready(chip, 0); + cmd = ((YDSXG_AC97WRITECMD | reg) << 16) | val; + snd_ymfpci_writel(chip, YDSXGR_AC97CMDDATA, cmd); +} + +static u16 snd_ymfpci_codec_read(struct snd_ac97 *ac97, u16 reg) +{ + struct snd_ymfpci *chip = ac97->private_data; + + if (snd_ymfpci_codec_ready(chip, 0)) + return ~0; + snd_ymfpci_writew(chip, YDSXGR_AC97CMDADR, YDSXG_AC97READCMD | reg); + if (snd_ymfpci_codec_ready(chip, 0)) + return ~0; + if (chip->device_id == PCI_DEVICE_ID_YAMAHA_744 && chip->rev < 2) { + int i; + for (i = 0; i < 600; i++) + snd_ymfpci_readw(chip, YDSXGR_PRISTATUSDATA); + } + return snd_ymfpci_readw(chip, YDSXGR_PRISTATUSDATA); +} + +/* + * Misc routines + */ + +static u32 snd_ymfpci_calc_delta(u32 rate) +{ + switch (rate) { + case 8000: return 0x02aaab00; + case 11025: return 0x03accd00; + case 16000: return 0x05555500; + case 22050: return 0x07599a00; + case 32000: return 0x0aaaab00; + case 44100: return 0x0eb33300; + default: return ((rate << 16) / 375) << 5; + } +} + +static u32 def_rate[8] = { + 100, 2000, 8000, 11025, 16000, 22050, 32000, 48000 +}; + +static u32 snd_ymfpci_calc_lpfK(u32 rate) +{ + u32 i; + static u32 val[8] = { + 0x00570000, 0x06AA0000, 0x18B20000, 0x20930000, + 0x2B9A0000, 0x35A10000, 0x3EAA0000, 0x40000000 + }; + + if (rate == 44100) + return 0x40000000; /* FIXME: What's the right value? */ + for (i = 0; i < 8; i++) + if (rate <= def_rate[i]) + return val[i]; + return val[0]; +} + +static u32 snd_ymfpci_calc_lpfQ(u32 rate) +{ + u32 i; + static u32 val[8] = { + 0x35280000, 0x34A70000, 0x32020000, 0x31770000, + 0x31390000, 0x31C90000, 0x33D00000, 0x40000000 + }; + + if (rate == 44100) + return 0x370A0000; + for (i = 0; i < 8; i++) + if (rate <= def_rate[i]) + return val[i]; + return val[0]; +} + +/* + * Hardware start management + */ + +static void snd_ymfpci_hw_start(struct snd_ymfpci *chip) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->start_count++ > 0) + goto __end; + snd_ymfpci_writel(chip, YDSXGR_MODE, + snd_ymfpci_readl(chip, YDSXGR_MODE) | 3); + chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT) & 1; + __end: + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_ymfpci_hw_stop(struct snd_ymfpci *chip) +{ + unsigned long flags; + long timeout = 1000; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (--chip->start_count > 0) + goto __end; + snd_ymfpci_writel(chip, YDSXGR_MODE, + snd_ymfpci_readl(chip, YDSXGR_MODE) & ~3); + while (timeout-- > 0) { + if ((snd_ymfpci_readl(chip, YDSXGR_STATUS) & 2) == 0) + break; + } + if (atomic_read(&chip->interrupt_sleep_count)) { + atomic_set(&chip->interrupt_sleep_count, 0); + wake_up(&chip->interrupt_sleep); + } + __end: + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +/* + * Playback voice management + */ + +static int voice_alloc(struct snd_ymfpci *chip, + enum snd_ymfpci_voice_type type, int pair, + struct snd_ymfpci_voice **rvoice) +{ + struct snd_ymfpci_voice *voice, *voice2; + int idx; + + *rvoice = NULL; + for (idx = 0; idx < YDSXG_PLAYBACK_VOICES; idx += pair ? 2 : 1) { + voice = &chip->voices[idx]; + voice2 = pair ? &chip->voices[idx+1] : NULL; + if (voice->use || (voice2 && voice2->use)) + continue; + voice->use = 1; + if (voice2) + voice2->use = 1; + switch (type) { + case YMFPCI_PCM: + voice->pcm = 1; + if (voice2) + voice2->pcm = 1; + break; + case YMFPCI_SYNTH: + voice->synth = 1; + break; + case YMFPCI_MIDI: + voice->midi = 1; + break; + } + snd_ymfpci_hw_start(chip); + if (voice2) + snd_ymfpci_hw_start(chip); + *rvoice = voice; + return 0; + } + return -ENOMEM; +} + +static int snd_ymfpci_voice_alloc(struct snd_ymfpci *chip, + enum snd_ymfpci_voice_type type, int pair, + struct snd_ymfpci_voice **rvoice) +{ + unsigned long flags; + int result; + + snd_assert(rvoice != NULL, return -EINVAL); + snd_assert(!pair || type == YMFPCI_PCM, return -EINVAL); + + spin_lock_irqsave(&chip->voice_lock, flags); + for (;;) { + result = voice_alloc(chip, type, pair, rvoice); + if (result == 0 || type != YMFPCI_PCM) + break; + /* TODO: synth/midi voice deallocation */ + break; + } + spin_unlock_irqrestore(&chip->voice_lock, flags); + return result; +} + +static int snd_ymfpci_voice_free(struct snd_ymfpci *chip, struct snd_ymfpci_voice *pvoice) +{ + unsigned long flags; + + snd_assert(pvoice != NULL, return -EINVAL); + snd_ymfpci_hw_stop(chip); + spin_lock_irqsave(&chip->voice_lock, flags); + if (pvoice->number == chip->src441_used) { + chip->src441_used = -1; + pvoice->ypcm->use_441_slot = 0; + } + pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0; + pvoice->ypcm = NULL; + pvoice->interrupt = NULL; + spin_unlock_irqrestore(&chip->voice_lock, flags); + return 0; +} + +/* + * PCM part + */ + +static void snd_ymfpci_pcm_interrupt(struct snd_ymfpci *chip, struct snd_ymfpci_voice *voice) +{ + struct snd_ymfpci_pcm *ypcm; + u32 pos, delta; + + if ((ypcm = voice->ypcm) == NULL) + return; + if (ypcm->substream == NULL) + return; + spin_lock(&chip->reg_lock); + if (ypcm->running) { + pos = le32_to_cpu(voice->bank[chip->active_bank].start); + if (pos < ypcm->last_pos) + delta = pos + (ypcm->buffer_size - ypcm->last_pos); + else + delta = pos - ypcm->last_pos; + ypcm->period_pos += delta; + ypcm->last_pos = pos; + if (ypcm->period_pos >= ypcm->period_size) { + // printk("done - active_bank = 0x%x, start = 0x%x\n", chip->active_bank, voice->bank[chip->active_bank].start); + ypcm->period_pos %= ypcm->period_size; + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(ypcm->substream); + spin_lock(&chip->reg_lock); + } + + if (unlikely(ypcm->update_pcm_vol)) { + unsigned int subs = ypcm->substream->number; + unsigned int next_bank = 1 - chip->active_bank; + struct snd_ymfpci_playback_bank *bank; + u32 volume; + + bank = &voice->bank[next_bank]; + volume = cpu_to_le32(chip->pcm_mixer[subs].left << 15); + bank->left_gain_end = volume; + if (ypcm->output_rear) + bank->eff2_gain_end = volume; + if (ypcm->voices[1]) + bank = &ypcm->voices[1]->bank[next_bank]; + volume = cpu_to_le32(chip->pcm_mixer[subs].right << 15); + bank->right_gain_end = volume; + if (ypcm->output_rear) + bank->eff3_gain_end = volume; + ypcm->update_pcm_vol--; + } + } + spin_unlock(&chip->reg_lock); +} + +static void snd_ymfpci_pcm_capture_interrupt(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ymfpci_pcm *ypcm = runtime->private_data; + struct snd_ymfpci *chip = ypcm->chip; + u32 pos, delta; + + spin_lock(&chip->reg_lock); + if (ypcm->running) { + pos = le32_to_cpu(chip->bank_capture[ypcm->capture_bank_number][chip->active_bank]->start) >> ypcm->shift; + if (pos < ypcm->last_pos) + delta = pos + (ypcm->buffer_size - ypcm->last_pos); + else + delta = pos - ypcm->last_pos; + ypcm->period_pos += delta; + ypcm->last_pos = pos; + if (ypcm->period_pos >= ypcm->period_size) { + ypcm->period_pos %= ypcm->period_size; + // printk("done - active_bank = 0x%x, start = 0x%x\n", chip->active_bank, voice->bank[chip->active_bank].start); + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(substream); + spin_lock(&chip->reg_lock); + } + } + spin_unlock(&chip->reg_lock); +} + +static int snd_ymfpci_playback_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct snd_ymfpci *chip = snd_pcm_substream_chip(substream); + struct snd_ymfpci_pcm *ypcm = substream->runtime->private_data; + struct snd_kcontrol *kctl = NULL; + int result = 0; + + spin_lock(&chip->reg_lock); + if (ypcm->voices[0] == NULL) { + result = -EINVAL; + goto __unlock; + } + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + chip->ctrl_playback[ypcm->voices[0]->number + 1] = cpu_to_le32(ypcm->voices[0]->bank_addr); + if (ypcm->voices[1] != NULL && !ypcm->use_441_slot) + chip->ctrl_playback[ypcm->voices[1]->number + 1] = cpu_to_le32(ypcm->voices[1]->bank_addr); + ypcm->running = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + if (substream->pcm == chip->pcm && !ypcm->use_441_slot) { + kctl = chip->pcm_mixer[substream->number].ctl; + kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + } + /* fall through */ + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + chip->ctrl_playback[ypcm->voices[0]->number + 1] = 0; + if (ypcm->voices[1] != NULL && !ypcm->use_441_slot) + chip->ctrl_playback[ypcm->voices[1]->number + 1] = 0; + ypcm->running = 0; + break; + default: + result = -EINVAL; + break; + } + __unlock: + spin_unlock(&chip->reg_lock); + if (kctl) + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_INFO, &kctl->id); + return result; +} +static int snd_ymfpci_capture_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct snd_ymfpci *chip = snd_pcm_substream_chip(substream); + struct snd_ymfpci_pcm *ypcm = substream->runtime->private_data; + int result = 0; + u32 tmp; + + spin_lock(&chip->reg_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + tmp = snd_ymfpci_readl(chip, YDSXGR_MAPOFREC) | (1 << ypcm->capture_bank_number); + snd_ymfpci_writel(chip, YDSXGR_MAPOFREC, tmp); + ypcm->running = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + tmp = snd_ymfpci_readl(chip, YDSXGR_MAPOFREC) & ~(1 << ypcm->capture_bank_number); + snd_ymfpci_writel(chip, YDSXGR_MAPOFREC, tmp); + ypcm->running = 0; + break; + default: + result = -EINVAL; + break; + } + spin_unlock(&chip->reg_lock); + return result; +} + +static int snd_ymfpci_pcm_voice_alloc(struct snd_ymfpci_pcm *ypcm, int voices) +{ + int err; + + if (ypcm->voices[1] != NULL && voices < 2) { + snd_ymfpci_voice_free(ypcm->chip, ypcm->voices[1]); + ypcm->voices[1] = NULL; + } + if (voices == 1 && ypcm->voices[0] != NULL) + return 0; /* already allocated */ + if (voices == 2 && ypcm->voices[0] != NULL && ypcm->voices[1] != NULL) + return 0; /* already allocated */ + if (voices > 1) { + if (ypcm->voices[0] != NULL && ypcm->voices[1] == NULL) { + snd_ymfpci_voice_free(ypcm->chip, ypcm->voices[0]); + ypcm->voices[0] = NULL; + } + } + err = snd_ymfpci_voice_alloc(ypcm->chip, YMFPCI_PCM, voices > 1, &ypcm->voices[0]); + if (err < 0) + return err; + ypcm->voices[0]->ypcm = ypcm; + ypcm->voices[0]->interrupt = snd_ymfpci_pcm_interrupt; + if (voices > 1) { + ypcm->voices[1] = &ypcm->chip->voices[ypcm->voices[0]->number + 1]; + ypcm->voices[1]->ypcm = ypcm; + } + return 0; +} + +static void snd_ymfpci_pcm_init_voice(struct snd_ymfpci_pcm *ypcm, unsigned int voiceidx, + struct snd_pcm_runtime *runtime, + int has_pcm_volume) +{ + struct snd_ymfpci_voice *voice = ypcm->voices[voiceidx]; + u32 format; + u32 delta = snd_ymfpci_calc_delta(runtime->rate); + u32 lpfQ = snd_ymfpci_calc_lpfQ(runtime->rate); + u32 lpfK = snd_ymfpci_calc_lpfK(runtime->rate); + struct snd_ymfpci_playback_bank *bank; + unsigned int nbank; + u32 vol_left, vol_right; + u8 use_left, use_right; + unsigned long flags; + + snd_assert(voice != NULL, return); + if (runtime->channels == 1) { + use_left = 1; + use_right = 1; + } else { + use_left = (voiceidx & 1) == 0; + use_right = !use_left; + } + if (has_pcm_volume) { + vol_left = cpu_to_le32(ypcm->chip->pcm_mixer + [ypcm->substream->number].left << 15); + vol_right = cpu_to_le32(ypcm->chip->pcm_mixer + [ypcm->substream->number].right << 15); + } else { + vol_left = cpu_to_le32(0x40000000); + vol_right = cpu_to_le32(0x40000000); + } + spin_lock_irqsave(&ypcm->chip->voice_lock, flags); + format = runtime->channels == 2 ? 0x00010000 : 0; + if (snd_pcm_format_width(runtime->format) == 8) + format |= 0x80000000; + else if (ypcm->chip->device_id == PCI_DEVICE_ID_YAMAHA_754 && + runtime->rate == 44100 && runtime->channels == 2 && + voiceidx == 0 && (ypcm->chip->src441_used == -1 || + ypcm->chip->src441_used == voice->number)) { + ypcm->chip->src441_used = voice->number; + ypcm->use_441_slot = 1; + format |= 0x10000000; + } + if (ypcm->chip->src441_used == voice->number && + (format & 0x10000000) == 0) { + ypcm->chip->src441_used = -1; + ypcm->use_441_slot = 0; + } + if (runtime->channels == 2 && (voiceidx & 1) != 0) + format |= 1; + spin_unlock_irqrestore(&ypcm->chip->voice_lock, flags); + for (nbank = 0; nbank < 2; nbank++) { + bank = &voice->bank[nbank]; + memset(bank, 0, sizeof(*bank)); + bank->format = cpu_to_le32(format); + bank->base = cpu_to_le32(runtime->dma_addr); + bank->loop_end = cpu_to_le32(ypcm->buffer_size); + bank->lpfQ = cpu_to_le32(lpfQ); + bank->delta = + bank->delta_end = cpu_to_le32(delta); + bank->lpfK = + bank->lpfK_end = cpu_to_le32(lpfK); + bank->eg_gain = + bank->eg_gain_end = cpu_to_le32(0x40000000); + + if (ypcm->output_front) { + if (use_left) { + bank->left_gain = + bank->left_gain_end = vol_left; + } + if (use_right) { + bank->right_gain = + bank->right_gain_end = vol_right; + } + } + if (ypcm->output_rear) { + if (!ypcm->swap_rear) { + if (use_left) { + bank->eff2_gain = + bank->eff2_gain_end = vol_left; + } + if (use_right) { + bank->eff3_gain = + bank->eff3_gain_end = vol_right; + } + } else { + /* The SPDIF out channels seem to be swapped, so we have + * to swap them here, too. The rear analog out channels + * will be wrong, but otherwise AC3 would not work. + */ + if (use_left) { + bank->eff3_gain = + bank->eff3_gain_end = vol_left; + } + if (use_right) { + bank->eff2_gain = + bank->eff2_gain_end = vol_right; + } + } + } + } +} + +static int __devinit snd_ymfpci_ac3_init(struct snd_ymfpci *chip) +{ + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), + 4096, &chip->ac3_tmp_base) < 0) + return -ENOMEM; + + chip->bank_effect[3][0]->base = + chip->bank_effect[3][1]->base = cpu_to_le32(chip->ac3_tmp_base.addr); + chip->bank_effect[3][0]->loop_end = + chip->bank_effect[3][1]->loop_end = cpu_to_le32(1024); + chip->bank_effect[4][0]->base = + chip->bank_effect[4][1]->base = cpu_to_le32(chip->ac3_tmp_base.addr + 2048); + chip->bank_effect[4][0]->loop_end = + chip->bank_effect[4][1]->loop_end = cpu_to_le32(1024); + + spin_lock_irq(&chip->reg_lock); + snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT, + snd_ymfpci_readl(chip, YDSXGR_MAPOFEFFECT) | 3 << 3); + spin_unlock_irq(&chip->reg_lock); + return 0; +} + +static int snd_ymfpci_ac3_done(struct snd_ymfpci *chip) +{ + spin_lock_irq(&chip->reg_lock); + snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT, + snd_ymfpci_readl(chip, YDSXGR_MAPOFEFFECT) & ~(3 << 3)); + spin_unlock_irq(&chip->reg_lock); + // snd_ymfpci_irq_wait(chip); + if (chip->ac3_tmp_base.area) { + snd_dma_free_pages(&chip->ac3_tmp_base); + chip->ac3_tmp_base.area = NULL; + } + return 0; +} + +static int snd_ymfpci_playback_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ymfpci_pcm *ypcm = runtime->private_data; + int err; + + if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) + return err; + if ((err = snd_ymfpci_pcm_voice_alloc(ypcm, params_channels(hw_params))) < 0) + return err; + return 0; +} + +static int snd_ymfpci_playback_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_ymfpci *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ymfpci_pcm *ypcm; + + if (runtime->private_data == NULL) + return 0; + ypcm = runtime->private_data; + + /* wait, until the PCI operations are not finished */ + snd_ymfpci_irq_wait(chip); + snd_pcm_lib_free_pages(substream); + if (ypcm->voices[1]) { + snd_ymfpci_voice_free(chip, ypcm->voices[1]); + ypcm->voices[1] = NULL; + } + if (ypcm->voices[0]) { + snd_ymfpci_voice_free(chip, ypcm->voices[0]); + ypcm->voices[0] = NULL; + } + return 0; +} + +static int snd_ymfpci_playback_prepare(struct snd_pcm_substream *substream) +{ + struct snd_ymfpci *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ymfpci_pcm *ypcm = runtime->private_data; + struct snd_kcontrol *kctl; + unsigned int nvoice; + + ypcm->period_size = runtime->period_size; + ypcm->buffer_size = runtime->buffer_size; + ypcm->period_pos = 0; + ypcm->last_pos = 0; + for (nvoice = 0; nvoice < runtime->channels; nvoice++) + snd_ymfpci_pcm_init_voice(ypcm, nvoice, runtime, + substream->pcm == chip->pcm); + + if (substream->pcm == chip->pcm && !ypcm->use_441_slot) { + kctl = chip->pcm_mixer[substream->number].ctl; + kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_INFO, &kctl->id); + } + return 0; +} + +static int snd_ymfpci_capture_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_ymfpci_capture_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_ymfpci *chip = snd_pcm_substream_chip(substream); + + /* wait, until the PCI operations are not finished */ + snd_ymfpci_irq_wait(chip); + return snd_pcm_lib_free_pages(substream); +} + +static int snd_ymfpci_capture_prepare(struct snd_pcm_substream *substream) +{ + struct snd_ymfpci *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ymfpci_pcm *ypcm = runtime->private_data; + struct snd_ymfpci_capture_bank * bank; + int nbank; + u32 rate, format; + + ypcm->period_size = runtime->period_size; + ypcm->buffer_size = runtime->buffer_size; + ypcm->period_pos = 0; + ypcm->last_pos = 0; + ypcm->shift = 0; + rate = ((48000 * 4096) / runtime->rate) - 1; + format = 0; + if (runtime->channels == 2) { + format |= 2; + ypcm->shift++; + } + if (snd_pcm_format_width(runtime->format) == 8) + format |= 1; + else + ypcm->shift++; + switch (ypcm->capture_bank_number) { + case 0: + snd_ymfpci_writel(chip, YDSXGR_RECFORMAT, format); + snd_ymfpci_writel(chip, YDSXGR_RECSLOTSR, rate); + break; + case 1: + snd_ymfpci_writel(chip, YDSXGR_ADCFORMAT, format); + snd_ymfpci_writel(chip, YDSXGR_ADCSLOTSR, rate); + break; + } + for (nbank = 0; nbank < 2; nbank++) { + bank = chip->bank_capture[ypcm->capture_bank_number][nbank]; + bank->base = cpu_to_le32(runtime->dma_addr); + bank->loop_end = cpu_to_le32(ypcm->buffer_size << ypcm->shift); + bank->start = 0; + bank->num_of_loops = 0; + } + return 0; +} + +static snd_pcm_uframes_t snd_ymfpci_playback_pointer(struct snd_pcm_substream *substream) +{ + struct snd_ymfpci *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ymfpci_pcm *ypcm = runtime->private_data; + struct snd_ymfpci_voice *voice = ypcm->voices[0]; + + if (!(ypcm->running && voice)) + return 0; + return le32_to_cpu(voice->bank[chip->active_bank].start); +} + +static snd_pcm_uframes_t snd_ymfpci_capture_pointer(struct snd_pcm_substream *substream) +{ + struct snd_ymfpci *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ymfpci_pcm *ypcm = runtime->private_data; + + if (!ypcm->running) + return 0; + return le32_to_cpu(chip->bank_capture[ypcm->capture_bank_number][chip->active_bank]->start) >> ypcm->shift; +} + +static void snd_ymfpci_irq_wait(struct snd_ymfpci *chip) +{ + wait_queue_t wait; + int loops = 4; + + while (loops-- > 0) { + if ((snd_ymfpci_readl(chip, YDSXGR_MODE) & 3) == 0) + continue; + init_waitqueue_entry(&wait, current); + add_wait_queue(&chip->interrupt_sleep, &wait); + atomic_inc(&chip->interrupt_sleep_count); + schedule_timeout_uninterruptible(msecs_to_jiffies(50)); + remove_wait_queue(&chip->interrupt_sleep, &wait); + } +} + +static irqreturn_t snd_ymfpci_interrupt(int irq, void *dev_id) +{ + struct snd_ymfpci *chip = dev_id; + u32 status, nvoice, mode; + struct snd_ymfpci_voice *voice; + + status = snd_ymfpci_readl(chip, YDSXGR_STATUS); + if (status & 0x80000000) { + chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT) & 1; + spin_lock(&chip->voice_lock); + for (nvoice = 0; nvoice < YDSXG_PLAYBACK_VOICES; nvoice++) { + voice = &chip->voices[nvoice]; + if (voice->interrupt) + voice->interrupt(chip, voice); + } + for (nvoice = 0; nvoice < YDSXG_CAPTURE_VOICES; nvoice++) { + if (chip->capture_substream[nvoice]) + snd_ymfpci_pcm_capture_interrupt(chip->capture_substream[nvoice]); + } +#if 0 + for (nvoice = 0; nvoice < YDSXG_EFFECT_VOICES; nvoice++) { + if (chip->effect_substream[nvoice]) + snd_ymfpci_pcm_effect_interrupt(chip->effect_substream[nvoice]); + } +#endif + spin_unlock(&chip->voice_lock); + spin_lock(&chip->reg_lock); + snd_ymfpci_writel(chip, YDSXGR_STATUS, 0x80000000); + mode = snd_ymfpci_readl(chip, YDSXGR_MODE) | 2; + snd_ymfpci_writel(chip, YDSXGR_MODE, mode); + spin_unlock(&chip->reg_lock); + + if (atomic_read(&chip->interrupt_sleep_count)) { + atomic_set(&chip->interrupt_sleep_count, 0); + wake_up(&chip->interrupt_sleep); + } + } + + status = snd_ymfpci_readw(chip, YDSXGR_INTFLAG); + if (status & 1) { + if (chip->timer) + snd_timer_interrupt(chip->timer, chip->timer->sticks); + } + snd_ymfpci_writew(chip, YDSXGR_INTFLAG, status); + + if (chip->rawmidi) + snd_mpu401_uart_interrupt(irq, chip->rawmidi->private_data); + return IRQ_HANDLED; +} + +static struct snd_pcm_hardware snd_ymfpci_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 256 * 1024, /* FIXME: enough? */ + .period_bytes_min = 64, + .period_bytes_max = 256 * 1024, /* FIXME: enough? */ + .periods_min = 3, + .periods_max = 1024, + .fifo_size = 0, +}; + +static struct snd_pcm_hardware snd_ymfpci_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 256 * 1024, /* FIXME: enough? */ + .period_bytes_min = 64, + .period_bytes_max = 256 * 1024, /* FIXME: enough? */ + .periods_min = 3, + .periods_max = 1024, + .fifo_size = 0, +}; + +static void snd_ymfpci_pcm_free_substream(struct snd_pcm_runtime *runtime) +{ + kfree(runtime->private_data); +} + +static int snd_ymfpci_playback_open_1(struct snd_pcm_substream *substream) +{ + struct snd_ymfpci *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ymfpci_pcm *ypcm; + + ypcm = kzalloc(sizeof(*ypcm), GFP_KERNEL); + if (ypcm == NULL) + return -ENOMEM; + ypcm->chip = chip; + ypcm->type = PLAYBACK_VOICE; + ypcm->substream = substream; + runtime->hw = snd_ymfpci_playback; + runtime->private_data = ypcm; + runtime->private_free = snd_ymfpci_pcm_free_substream; + /* FIXME? True value is 256/48 = 5.33333 ms */ + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5333, UINT_MAX); + return 0; +} + +/* call with spinlock held */ +static void ymfpci_open_extension(struct snd_ymfpci *chip) +{ + if (! chip->rear_opened) { + if (! chip->spdif_opened) /* set AC3 */ + snd_ymfpci_writel(chip, YDSXGR_MODE, + snd_ymfpci_readl(chip, YDSXGR_MODE) | (1 << 30)); + /* enable second codec (4CHEN) */ + snd_ymfpci_writew(chip, YDSXGR_SECCONFIG, + (snd_ymfpci_readw(chip, YDSXGR_SECCONFIG) & ~0x0330) | 0x0010); + } +} + +/* call with spinlock held */ +static void ymfpci_close_extension(struct snd_ymfpci *chip) +{ + if (! chip->rear_opened) { + if (! chip->spdif_opened) + snd_ymfpci_writel(chip, YDSXGR_MODE, + snd_ymfpci_readl(chip, YDSXGR_MODE) & ~(1 << 30)); + snd_ymfpci_writew(chip, YDSXGR_SECCONFIG, + (snd_ymfpci_readw(chip, YDSXGR_SECCONFIG) & ~0x0330) & ~0x0010); + } +} + +static int snd_ymfpci_playback_open(struct snd_pcm_substream *substream) +{ + struct snd_ymfpci *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ymfpci_pcm *ypcm; + int err; + + if ((err = snd_ymfpci_playback_open_1(substream)) < 0) + return err; + ypcm = runtime->private_data; + ypcm->output_front = 1; + ypcm->output_rear = chip->mode_dup4ch ? 1 : 0; + ypcm->swap_rear = 0; + spin_lock_irq(&chip->reg_lock); + if (ypcm->output_rear) { + ymfpci_open_extension(chip); + chip->rear_opened++; + } + spin_unlock_irq(&chip->reg_lock); + return 0; +} + +static int snd_ymfpci_playback_spdif_open(struct snd_pcm_substream *substream) +{ + struct snd_ymfpci *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ymfpci_pcm *ypcm; + int err; + + if ((err = snd_ymfpci_playback_open_1(substream)) < 0) + return err; + ypcm = runtime->private_data; + ypcm->output_front = 0; + ypcm->output_rear = 1; + ypcm->swap_rear = 1; + spin_lock_irq(&chip->reg_lock); + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL, + snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) | 2); + ymfpci_open_extension(chip); + chip->spdif_pcm_bits = chip->spdif_bits; + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_pcm_bits); + chip->spdif_opened++; + spin_unlock_irq(&chip->reg_lock); + + chip->spdif_pcm_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &chip->spdif_pcm_ctl->id); + return 0; +} + +static int snd_ymfpci_playback_4ch_open(struct snd_pcm_substream *substream) +{ + struct snd_ymfpci *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ymfpci_pcm *ypcm; + int err; + + if ((err = snd_ymfpci_playback_open_1(substream)) < 0) + return err; + ypcm = runtime->private_data; + ypcm->output_front = 0; + ypcm->output_rear = 1; + ypcm->swap_rear = 0; + spin_lock_irq(&chip->reg_lock); + ymfpci_open_extension(chip); + chip->rear_opened++; + spin_unlock_irq(&chip->reg_lock); + return 0; +} + +static int snd_ymfpci_capture_open(struct snd_pcm_substream *substream, + u32 capture_bank_number) +{ + struct snd_ymfpci *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ymfpci_pcm *ypcm; + + ypcm = kzalloc(sizeof(*ypcm), GFP_KERNEL); + if (ypcm == NULL) + return -ENOMEM; + ypcm->chip = chip; + ypcm->type = capture_bank_number + CAPTURE_REC; + ypcm->substream = substream; + ypcm->capture_bank_number = capture_bank_number; + chip->capture_substream[capture_bank_number] = substream; + runtime->hw = snd_ymfpci_capture; + /* FIXME? True value is 256/48 = 5.33333 ms */ + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5333, UINT_MAX); + runtime->private_data = ypcm; + runtime->private_free = snd_ymfpci_pcm_free_substream; + snd_ymfpci_hw_start(chip); + return 0; +} + +static int snd_ymfpci_capture_rec_open(struct snd_pcm_substream *substream) +{ + return snd_ymfpci_capture_open(substream, 0); +} + +static int snd_ymfpci_capture_ac97_open(struct snd_pcm_substream *substream) +{ + return snd_ymfpci_capture_open(substream, 1); +} + +static int snd_ymfpci_playback_close_1(struct snd_pcm_substream *substream) +{ + return 0; +} + +static int snd_ymfpci_playback_close(struct snd_pcm_substream *substream) +{ + struct snd_ymfpci *chip = snd_pcm_substream_chip(substream); + struct snd_ymfpci_pcm *ypcm = substream->runtime->private_data; + + spin_lock_irq(&chip->reg_lock); + if (ypcm->output_rear && chip->rear_opened > 0) { + chip->rear_opened--; + ymfpci_close_extension(chip); + } + spin_unlock_irq(&chip->reg_lock); + return snd_ymfpci_playback_close_1(substream); +} + +static int snd_ymfpci_playback_spdif_close(struct snd_pcm_substream *substream) +{ + struct snd_ymfpci *chip = snd_pcm_substream_chip(substream); + + spin_lock_irq(&chip->reg_lock); + chip->spdif_opened = 0; + ymfpci_close_extension(chip); + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL, + snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & ~2); + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits); + spin_unlock_irq(&chip->reg_lock); + chip->spdif_pcm_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, &chip->spdif_pcm_ctl->id); + return snd_ymfpci_playback_close_1(substream); +} + +static int snd_ymfpci_playback_4ch_close(struct snd_pcm_substream *substream) +{ + struct snd_ymfpci *chip = snd_pcm_substream_chip(substream); + + spin_lock_irq(&chip->reg_lock); + if (chip->rear_opened > 0) { + chip->rear_opened--; + ymfpci_close_extension(chip); + } + spin_unlock_irq(&chip->reg_lock); + return snd_ymfpci_playback_close_1(substream); +} + +static int snd_ymfpci_capture_close(struct snd_pcm_substream *substream) +{ + struct snd_ymfpci *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ymfpci_pcm *ypcm = runtime->private_data; + + if (ypcm != NULL) { + chip->capture_substream[ypcm->capture_bank_number] = NULL; + snd_ymfpci_hw_stop(chip); + } + return 0; +} + +static struct snd_pcm_ops snd_ymfpci_playback_ops = { + .open = snd_ymfpci_playback_open, + .close = snd_ymfpci_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ymfpci_playback_hw_params, + .hw_free = snd_ymfpci_playback_hw_free, + .prepare = snd_ymfpci_playback_prepare, + .trigger = snd_ymfpci_playback_trigger, + .pointer = snd_ymfpci_playback_pointer, +}; + +static struct snd_pcm_ops snd_ymfpci_capture_rec_ops = { + .open = snd_ymfpci_capture_rec_open, + .close = snd_ymfpci_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ymfpci_capture_hw_params, + .hw_free = snd_ymfpci_capture_hw_free, + .prepare = snd_ymfpci_capture_prepare, + .trigger = snd_ymfpci_capture_trigger, + .pointer = snd_ymfpci_capture_pointer, +}; + +int __devinit snd_ymfpci_pcm(struct snd_ymfpci *chip, int device, struct snd_pcm ** rpcm) +{ + struct snd_pcm *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(chip->card, "YMFPCI", device, 32, 1, &pcm)) < 0) + return err; + pcm->private_data = chip; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ymfpci_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ymfpci_capture_rec_ops); + + /* global setup */ + pcm->info_flags = 0; + strcpy(pcm->name, "YMFPCI"); + chip->pcm = pcm; + + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 256*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +static struct snd_pcm_ops snd_ymfpci_capture_ac97_ops = { + .open = snd_ymfpci_capture_ac97_open, + .close = snd_ymfpci_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ymfpci_capture_hw_params, + .hw_free = snd_ymfpci_capture_hw_free, + .prepare = snd_ymfpci_capture_prepare, + .trigger = snd_ymfpci_capture_trigger, + .pointer = snd_ymfpci_capture_pointer, +}; + +int __devinit snd_ymfpci_pcm2(struct snd_ymfpci *chip, int device, struct snd_pcm ** rpcm) +{ + struct snd_pcm *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(chip->card, "YMFPCI - PCM2", device, 0, 1, &pcm)) < 0) + return err; + pcm->private_data = chip; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ymfpci_capture_ac97_ops); + + /* global setup */ + pcm->info_flags = 0; + sprintf(pcm->name, "YMFPCI - %s", + chip->device_id == PCI_DEVICE_ID_YAMAHA_754 ? "Direct Recording" : "AC'97"); + chip->pcm2 = pcm; + + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 256*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +static struct snd_pcm_ops snd_ymfpci_playback_spdif_ops = { + .open = snd_ymfpci_playback_spdif_open, + .close = snd_ymfpci_playback_spdif_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ymfpci_playback_hw_params, + .hw_free = snd_ymfpci_playback_hw_free, + .prepare = snd_ymfpci_playback_prepare, + .trigger = snd_ymfpci_playback_trigger, + .pointer = snd_ymfpci_playback_pointer, +}; + +int __devinit snd_ymfpci_pcm_spdif(struct snd_ymfpci *chip, int device, struct snd_pcm ** rpcm) +{ + struct snd_pcm *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(chip->card, "YMFPCI - IEC958", device, 1, 0, &pcm)) < 0) + return err; + pcm->private_data = chip; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ymfpci_playback_spdif_ops); + + /* global setup */ + pcm->info_flags = 0; + strcpy(pcm->name, "YMFPCI - IEC958"); + chip->pcm_spdif = pcm; + + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 256*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +static struct snd_pcm_ops snd_ymfpci_playback_4ch_ops = { + .open = snd_ymfpci_playback_4ch_open, + .close = snd_ymfpci_playback_4ch_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ymfpci_playback_hw_params, + .hw_free = snd_ymfpci_playback_hw_free, + .prepare = snd_ymfpci_playback_prepare, + .trigger = snd_ymfpci_playback_trigger, + .pointer = snd_ymfpci_playback_pointer, +}; + +int __devinit snd_ymfpci_pcm_4ch(struct snd_ymfpci *chip, int device, struct snd_pcm ** rpcm) +{ + struct snd_pcm *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + if ((err = snd_pcm_new(chip->card, "YMFPCI - Rear", device, 1, 0, &pcm)) < 0) + return err; + pcm->private_data = chip; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ymfpci_playback_4ch_ops); + + /* global setup */ + pcm->info_flags = 0; + strcpy(pcm->name, "YMFPCI - Rear PCM"); + chip->pcm_4ch = pcm; + + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 256*1024); + + if (rpcm) + *rpcm = pcm; + return 0; +} + +static int snd_ymfpci_spdif_default_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ymfpci_spdif_default_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol); + + spin_lock_irq(&chip->reg_lock); + ucontrol->value.iec958.status[0] = (chip->spdif_bits >> 0) & 0xff; + ucontrol->value.iec958.status[1] = (chip->spdif_bits >> 8) & 0xff; + ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS_48000; + spin_unlock_irq(&chip->reg_lock); + return 0; +} + +static int snd_ymfpci_spdif_default_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol); + unsigned int val; + int change; + + val = ((ucontrol->value.iec958.status[0] & 0x3e) << 0) | + (ucontrol->value.iec958.status[1] << 8); + spin_lock_irq(&chip->reg_lock); + change = chip->spdif_bits != val; + chip->spdif_bits = val; + if ((snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & 1) && chip->pcm_spdif == NULL) + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits); + spin_unlock_irq(&chip->reg_lock); + return change; +} + +static struct snd_kcontrol_new snd_ymfpci_spdif_default __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + .info = snd_ymfpci_spdif_default_info, + .get = snd_ymfpci_spdif_default_get, + .put = snd_ymfpci_spdif_default_put +}; + +static int snd_ymfpci_spdif_mask_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ymfpci_spdif_mask_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol); + + spin_lock_irq(&chip->reg_lock); + ucontrol->value.iec958.status[0] = 0x3e; + ucontrol->value.iec958.status[1] = 0xff; + spin_unlock_irq(&chip->reg_lock); + return 0; +} + +static struct snd_kcontrol_new snd_ymfpci_spdif_mask __devinitdata = +{ + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), + .info = snd_ymfpci_spdif_mask_info, + .get = snd_ymfpci_spdif_mask_get, +}; + +static int snd_ymfpci_spdif_stream_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ymfpci_spdif_stream_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol); + + spin_lock_irq(&chip->reg_lock); + ucontrol->value.iec958.status[0] = (chip->spdif_pcm_bits >> 0) & 0xff; + ucontrol->value.iec958.status[1] = (chip->spdif_pcm_bits >> 8) & 0xff; + ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS_48000; + spin_unlock_irq(&chip->reg_lock); + return 0; +} + +static int snd_ymfpci_spdif_stream_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol); + unsigned int val; + int change; + + val = ((ucontrol->value.iec958.status[0] & 0x3e) << 0) | + (ucontrol->value.iec958.status[1] << 8); + spin_lock_irq(&chip->reg_lock); + change = chip->spdif_pcm_bits != val; + chip->spdif_pcm_bits = val; + if ((snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & 2)) + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_pcm_bits); + spin_unlock_irq(&chip->reg_lock); + return change; +} + +static struct snd_kcontrol_new snd_ymfpci_spdif_stream __devinitdata = +{ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), + .info = snd_ymfpci_spdif_stream_info, + .get = snd_ymfpci_spdif_stream_get, + .put = snd_ymfpci_spdif_stream_put +}; + +static int snd_ymfpci_drec_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *info) +{ + static char *texts[3] = {"AC'97", "IEC958", "ZV Port"}; + + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + info->count = 1; + info->value.enumerated.items = 3; + if (info->value.enumerated.item > 2) + info->value.enumerated.item = 2; + strcpy(info->value.enumerated.name, texts[info->value.enumerated.item]); + return 0; +} + +static int snd_ymfpci_drec_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *value) +{ + struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol); + u16 reg; + + spin_lock_irq(&chip->reg_lock); + reg = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL); + spin_unlock_irq(&chip->reg_lock); + if (!(reg & 0x100)) + value->value.enumerated.item[0] = 0; + else + value->value.enumerated.item[0] = 1 + ((reg & 0x200) != 0); + return 0; +} + +static int snd_ymfpci_drec_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *value) +{ + struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol); + u16 reg, old_reg; + + spin_lock_irq(&chip->reg_lock); + old_reg = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL); + if (value->value.enumerated.item[0] == 0) + reg = old_reg & ~0x100; + else + reg = (old_reg & ~0x300) | 0x100 | ((value->value.enumerated.item[0] == 2) << 9); + snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, reg); + spin_unlock_irq(&chip->reg_lock); + return reg != old_reg; +} + +static struct snd_kcontrol_new snd_ymfpci_drec_source __devinitdata = { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Direct Recording Source", + .info = snd_ymfpci_drec_source_info, + .get = snd_ymfpci_drec_source_get, + .put = snd_ymfpci_drec_source_put +}; + +/* + * Mixer controls + */ + +#define YMFPCI_SINGLE(xname, xindex, reg, shift) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_ymfpci_info_single, \ + .get = snd_ymfpci_get_single, .put = snd_ymfpci_put_single, \ + .private_value = ((reg) | ((shift) << 16)) } + +#define snd_ymfpci_info_single snd_ctl_boolean_mono_info + +static int snd_ymfpci_get_single(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xffff; + unsigned int shift = (kcontrol->private_value >> 16) & 0xff; + unsigned int mask = 1; + + switch (reg) { + case YDSXGR_SPDIFOUTCTRL: break; + case YDSXGR_SPDIFINCTRL: break; + default: return -EINVAL; + } + ucontrol->value.integer.value[0] = + (snd_ymfpci_readl(chip, reg) >> shift) & mask; + return 0; +} + +static int snd_ymfpci_put_single(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xffff; + unsigned int shift = (kcontrol->private_value >> 16) & 0xff; + unsigned int mask = 1; + int change; + unsigned int val, oval; + + switch (reg) { + case YDSXGR_SPDIFOUTCTRL: break; + case YDSXGR_SPDIFINCTRL: break; + default: return -EINVAL; + } + val = (ucontrol->value.integer.value[0] & mask); + val <<= shift; + spin_lock_irq(&chip->reg_lock); + oval = snd_ymfpci_readl(chip, reg); + val = (oval & ~(mask << shift)) | val; + change = val != oval; + snd_ymfpci_writel(chip, reg, val); + spin_unlock_irq(&chip->reg_lock); + return change; +} + +static const DECLARE_TLV_DB_LINEAR(db_scale_native, TLV_DB_GAIN_MUTE, 0); + +#define YMFPCI_DOUBLE(xname, xindex, reg) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ + .info = snd_ymfpci_info_double, \ + .get = snd_ymfpci_get_double, .put = snd_ymfpci_put_double, \ + .private_value = reg, \ + .tlv = { .p = db_scale_native } } + +static int snd_ymfpci_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +{ + unsigned int reg = kcontrol->private_value; + + if (reg < 0x80 || reg >= 0xc0) + return -EINVAL; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 16383; + return 0; +} + +static int snd_ymfpci_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol); + unsigned int reg = kcontrol->private_value; + unsigned int shift_left = 0, shift_right = 16, mask = 16383; + unsigned int val; + + if (reg < 0x80 || reg >= 0xc0) + return -EINVAL; + spin_lock_irq(&chip->reg_lock); + val = snd_ymfpci_readl(chip, reg); + spin_unlock_irq(&chip->reg_lock); + ucontrol->value.integer.value[0] = (val >> shift_left) & mask; + ucontrol->value.integer.value[1] = (val >> shift_right) & mask; + return 0; +} + +static int snd_ymfpci_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol); + unsigned int reg = kcontrol->private_value; + unsigned int shift_left = 0, shift_right = 16, mask = 16383; + int change; + unsigned int val1, val2, oval; + + if (reg < 0x80 || reg >= 0xc0) + return -EINVAL; + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + val1 <<= shift_left; + val2 <<= shift_right; + spin_lock_irq(&chip->reg_lock); + oval = snd_ymfpci_readl(chip, reg); + val1 = (oval & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; + change = val1 != oval; + snd_ymfpci_writel(chip, reg, val1); + spin_unlock_irq(&chip->reg_lock); + return change; +} + +static int snd_ymfpci_put_nativedacvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol); + unsigned int reg = YDSXGR_NATIVEDACOUTVOL; + unsigned int reg2 = YDSXGR_BUF441OUTVOL; + int change; + unsigned int value, oval; + + value = ucontrol->value.integer.value[0] & 0x3fff; + value |= (ucontrol->value.integer.value[1] & 0x3fff) << 16; + spin_lock_irq(&chip->reg_lock); + oval = snd_ymfpci_readl(chip, reg); + change = value != oval; + snd_ymfpci_writel(chip, reg, value); + snd_ymfpci_writel(chip, reg2, value); + spin_unlock_irq(&chip->reg_lock); + return change; +} + +/* + * 4ch duplication + */ +#define snd_ymfpci_info_dup4ch snd_ctl_boolean_mono_info + +static int snd_ymfpci_get_dup4ch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = chip->mode_dup4ch; + return 0; +} + +static int snd_ymfpci_put_dup4ch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol); + int change; + change = (ucontrol->value.integer.value[0] != chip->mode_dup4ch); + if (change) + chip->mode_dup4ch = !!ucontrol->value.integer.value[0]; + return change; +} + + +static struct snd_kcontrol_new snd_ymfpci_controls[] __devinitdata = { +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Wave Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = snd_ymfpci_info_double, + .get = snd_ymfpci_get_double, + .put = snd_ymfpci_put_nativedacvol, + .private_value = YDSXGR_NATIVEDACOUTVOL, + .tlv = { .p = db_scale_native }, +}, +YMFPCI_DOUBLE("Wave Capture Volume", 0, YDSXGR_NATIVEDACLOOPVOL), +YMFPCI_DOUBLE("Digital Capture Volume", 0, YDSXGR_NATIVEDACINVOL), +YMFPCI_DOUBLE("Digital Capture Volume", 1, YDSXGR_NATIVEADCINVOL), +YMFPCI_DOUBLE("ADC Playback Volume", 0, YDSXGR_PRIADCOUTVOL), +YMFPCI_DOUBLE("ADC Capture Volume", 0, YDSXGR_PRIADCLOOPVOL), +YMFPCI_DOUBLE("ADC Playback Volume", 1, YDSXGR_SECADCOUTVOL), +YMFPCI_DOUBLE("ADC Capture Volume", 1, YDSXGR_SECADCLOOPVOL), +YMFPCI_DOUBLE("FM Legacy Volume", 0, YDSXGR_LEGACYOUTVOL), +YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("AC97 ", PLAYBACK,VOLUME), 0, YDSXGR_ZVOUTVOL), +YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("", CAPTURE,VOLUME), 0, YDSXGR_ZVLOOPVOL), +YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("AC97 ",PLAYBACK,VOLUME), 1, YDSXGR_SPDIFOUTVOL), +YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,VOLUME), 1, YDSXGR_SPDIFLOOPVOL), +YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), 0, YDSXGR_SPDIFOUTCTRL, 0), +YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0, YDSXGR_SPDIFINCTRL, 0), +YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("Loop",NONE,NONE), 0, YDSXGR_SPDIFINCTRL, 4), +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "4ch Duplication", + .info = snd_ymfpci_info_dup4ch, + .get = snd_ymfpci_get_dup4ch, + .put = snd_ymfpci_put_dup4ch, +}, +}; + + +/* + * GPIO + */ + +static int snd_ymfpci_get_gpio_out(struct snd_ymfpci *chip, int pin) +{ + u16 reg, mode; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + reg = snd_ymfpci_readw(chip, YDSXGR_GPIOFUNCENABLE); + reg &= ~(1 << (pin + 8)); + reg |= (1 << pin); + snd_ymfpci_writew(chip, YDSXGR_GPIOFUNCENABLE, reg); + /* set the level mode for input line */ + mode = snd_ymfpci_readw(chip, YDSXGR_GPIOTYPECONFIG); + mode &= ~(3 << (pin * 2)); + snd_ymfpci_writew(chip, YDSXGR_GPIOTYPECONFIG, mode); + snd_ymfpci_writew(chip, YDSXGR_GPIOFUNCENABLE, reg | (1 << (pin + 8))); + mode = snd_ymfpci_readw(chip, YDSXGR_GPIOINSTATUS); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return (mode >> pin) & 1; +} + +static int snd_ymfpci_set_gpio_out(struct snd_ymfpci *chip, int pin, int enable) +{ + u16 reg; + unsigned long flags; + + spin_lock_irqsave(&chip->reg_lock, flags); + reg = snd_ymfpci_readw(chip, YDSXGR_GPIOFUNCENABLE); + reg &= ~(1 << pin); + reg &= ~(1 << (pin + 8)); + snd_ymfpci_writew(chip, YDSXGR_GPIOFUNCENABLE, reg); + snd_ymfpci_writew(chip, YDSXGR_GPIOOUTCTRL, enable << pin); + snd_ymfpci_writew(chip, YDSXGR_GPIOFUNCENABLE, reg | (1 << (pin + 8))); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + return 0; +} + +#define snd_ymfpci_gpio_sw_info snd_ctl_boolean_mono_info + +static int snd_ymfpci_gpio_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol); + int pin = (int)kcontrol->private_value; + ucontrol->value.integer.value[0] = snd_ymfpci_get_gpio_out(chip, pin); + return 0; +} + +static int snd_ymfpci_gpio_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol); + int pin = (int)kcontrol->private_value; + + if (snd_ymfpci_get_gpio_out(chip, pin) != ucontrol->value.integer.value[0]) { + snd_ymfpci_set_gpio_out(chip, pin, !!ucontrol->value.integer.value[0]); + ucontrol->value.integer.value[0] = snd_ymfpci_get_gpio_out(chip, pin); + return 1; + } + return 0; +} + +static struct snd_kcontrol_new snd_ymfpci_rear_shared __devinitdata = { + .name = "Shared Rear/Line-In Switch", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_ymfpci_gpio_sw_info, + .get = snd_ymfpci_gpio_sw_get, + .put = snd_ymfpci_gpio_sw_put, + .private_value = 2, +}; + +/* + * PCM voice volume + */ + +static int snd_ymfpci_pcm_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0x8000; + return 0; +} + +static int snd_ymfpci_pcm_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol); + unsigned int subs = kcontrol->id.subdevice; + + ucontrol->value.integer.value[0] = chip->pcm_mixer[subs].left; + ucontrol->value.integer.value[1] = chip->pcm_mixer[subs].right; + return 0; +} + +static int snd_ymfpci_pcm_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol); + unsigned int subs = kcontrol->id.subdevice; + struct snd_pcm_substream *substream; + unsigned long flags; + + if (ucontrol->value.integer.value[0] != chip->pcm_mixer[subs].left || + ucontrol->value.integer.value[1] != chip->pcm_mixer[subs].right) { + chip->pcm_mixer[subs].left = ucontrol->value.integer.value[0]; + chip->pcm_mixer[subs].right = ucontrol->value.integer.value[1]; + + substream = (struct snd_pcm_substream *)kcontrol->private_value; + spin_lock_irqsave(&chip->voice_lock, flags); + if (substream->runtime && substream->runtime->private_data) { + struct snd_ymfpci_pcm *ypcm = substream->runtime->private_data; + if (!ypcm->use_441_slot) + ypcm->update_pcm_vol = 2; + } + spin_unlock_irqrestore(&chip->voice_lock, flags); + return 1; + } + return 0; +} + +static struct snd_kcontrol_new snd_ymfpci_pcm_volume __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "PCM Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_INACTIVE, + .info = snd_ymfpci_pcm_vol_info, + .get = snd_ymfpci_pcm_vol_get, + .put = snd_ymfpci_pcm_vol_put, +}; + + +/* + * Mixer routines + */ + +static void snd_ymfpci_mixer_free_ac97_bus(struct snd_ac97_bus *bus) +{ + struct snd_ymfpci *chip = bus->private_data; + chip->ac97_bus = NULL; +} + +static void snd_ymfpci_mixer_free_ac97(struct snd_ac97 *ac97) +{ + struct snd_ymfpci *chip = ac97->private_data; + chip->ac97 = NULL; +} + +int __devinit snd_ymfpci_mixer(struct snd_ymfpci *chip, int rear_switch) +{ + struct snd_ac97_template ac97; + struct snd_kcontrol *kctl; + struct snd_pcm_substream *substream; + unsigned int idx; + int err; + static struct snd_ac97_bus_ops ops = { + .write = snd_ymfpci_codec_write, + .read = snd_ymfpci_codec_read, + }; + + if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus)) < 0) + return err; + chip->ac97_bus->private_free = snd_ymfpci_mixer_free_ac97_bus; + chip->ac97_bus->no_vra = 1; /* YMFPCI doesn't need VRA */ + + memset(&ac97, 0, sizeof(ac97)); + ac97.private_data = chip; + ac97.private_free = snd_ymfpci_mixer_free_ac97; + if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0) + return err; + + /* to be sure */ + snd_ac97_update_bits(chip->ac97, AC97_EXTENDED_STATUS, + AC97_EA_VRA|AC97_EA_VRM, 0); + + for (idx = 0; idx < ARRAY_SIZE(snd_ymfpci_controls); idx++) { + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_controls[idx], chip))) < 0) + return err; + } + + /* add S/PDIF control */ + snd_assert(chip->pcm_spdif != NULL, return -EIO); + if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_default, chip))) < 0) + return err; + kctl->id.device = chip->pcm_spdif->device; + if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_mask, chip))) < 0) + return err; + kctl->id.device = chip->pcm_spdif->device; + if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_stream, chip))) < 0) + return err; + kctl->id.device = chip->pcm_spdif->device; + chip->spdif_pcm_ctl = kctl; + + /* direct recording source */ + if (chip->device_id == PCI_DEVICE_ID_YAMAHA_754 && + (err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_drec_source, chip))) < 0) + return err; + + /* + * shared rear/line-in + */ + if (rear_switch) { + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_rear_shared, chip))) < 0) + return err; + } + + /* per-voice volume */ + substream = chip->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + for (idx = 0; idx < 32; ++idx) { + kctl = snd_ctl_new1(&snd_ymfpci_pcm_volume, chip); + if (!kctl) + return -ENOMEM; + kctl->id.device = chip->pcm->device; + kctl->id.subdevice = idx; + kctl->private_value = (unsigned long)substream; + if ((err = snd_ctl_add(chip->card, kctl)) < 0) + return err; + chip->pcm_mixer[idx].left = 0x8000; + chip->pcm_mixer[idx].right = 0x8000; + chip->pcm_mixer[idx].ctl = kctl; + substream = substream->next; + } + + return 0; +} + + +/* + * timer + */ + +static int snd_ymfpci_timer_start(struct snd_timer *timer) +{ + struct snd_ymfpci *chip; + unsigned long flags; + unsigned int count; + + chip = snd_timer_chip(timer); + count = (timer->sticks << 1) - 1; + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ymfpci_writew(chip, YDSXGR_TIMERCOUNT, count); + snd_ymfpci_writeb(chip, YDSXGR_TIMERCTRL, 0x03); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_ymfpci_timer_stop(struct snd_timer *timer) +{ + struct snd_ymfpci *chip; + unsigned long flags; + + chip = snd_timer_chip(timer); + spin_lock_irqsave(&chip->reg_lock, flags); + snd_ymfpci_writeb(chip, YDSXGR_TIMERCTRL, 0x00); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +static int snd_ymfpci_timer_precise_resolution(struct snd_timer *timer, + unsigned long *num, unsigned long *den) +{ + *num = 1; + *den = 48000; + return 0; +} + +static struct snd_timer_hardware snd_ymfpci_timer_hw = { + .flags = SNDRV_TIMER_HW_AUTO, + .resolution = 20833, /* 1/fs = 20.8333...us */ + .ticks = 0x8000, + .start = snd_ymfpci_timer_start, + .stop = snd_ymfpci_timer_stop, + .precise_resolution = snd_ymfpci_timer_precise_resolution, +}; + +int __devinit snd_ymfpci_timer(struct snd_ymfpci *chip, int device) +{ + struct snd_timer *timer = NULL; + struct snd_timer_id tid; + int err; + + tid.dev_class = SNDRV_TIMER_CLASS_CARD; + tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; + tid.card = chip->card->number; + tid.device = device; + tid.subdevice = 0; + if ((err = snd_timer_new(chip->card, "YMFPCI", &tid, &timer)) >= 0) { + strcpy(timer->name, "YMFPCI timer"); + timer->private_data = chip; + timer->hw = snd_ymfpci_timer_hw; + } + chip->timer = timer; + return err; +} + + +/* + * proc interface + */ + +static void snd_ymfpci_proc_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_ymfpci *chip = entry->private_data; + int i; + + snd_iprintf(buffer, "YMFPCI\n\n"); + for (i = 0; i <= YDSXGR_WORKBASE; i += 4) + snd_iprintf(buffer, "%04x: %04x\n", i, snd_ymfpci_readl(chip, i)); +} + +static int __devinit snd_ymfpci_proc_init(struct snd_card *card, struct snd_ymfpci *chip) +{ + struct snd_info_entry *entry; + + if (! snd_card_proc_new(card, "ymfpci", &entry)) + snd_info_set_text_ops(entry, chip, snd_ymfpci_proc_read); + return 0; +} + +/* + * initialization routines + */ + +static void snd_ymfpci_aclink_reset(struct pci_dev * pci) +{ + u8 cmd; + + pci_read_config_byte(pci, PCIR_DSXG_CTRL, &cmd); +#if 0 // force to reset + if (cmd & 0x03) { +#endif + pci_write_config_byte(pci, PCIR_DSXG_CTRL, cmd & 0xfc); + pci_write_config_byte(pci, PCIR_DSXG_CTRL, cmd | 0x03); + pci_write_config_byte(pci, PCIR_DSXG_CTRL, cmd & 0xfc); + pci_write_config_word(pci, PCIR_DSXG_PWRCTRL1, 0); + pci_write_config_word(pci, PCIR_DSXG_PWRCTRL2, 0); +#if 0 + } +#endif +} + +static void snd_ymfpci_enable_dsp(struct snd_ymfpci *chip) +{ + snd_ymfpci_writel(chip, YDSXGR_CONFIG, 0x00000001); +} + +static void snd_ymfpci_disable_dsp(struct snd_ymfpci *chip) +{ + u32 val; + int timeout = 1000; + + val = snd_ymfpci_readl(chip, YDSXGR_CONFIG); + if (val) + snd_ymfpci_writel(chip, YDSXGR_CONFIG, 0x00000000); + while (timeout-- > 0) { + val = snd_ymfpci_readl(chip, YDSXGR_STATUS); + if ((val & 0x00000002) == 0) + break; + } +} + +#ifdef CONFIG_SND_YMFPCI_FIRMWARE_IN_KERNEL + +#include "ymfpci_image.h" + +static struct firmware snd_ymfpci_dsp_microcode = { + .size = YDSXG_DSPLENGTH, + .data = (u8 *)DspInst, +}; +static struct firmware snd_ymfpci_controller_microcode = { + .size = YDSXG_CTRLLENGTH, + .data = (u8 *)CntrlInst, +}; +static struct firmware snd_ymfpci_controller_1e_microcode = { + .size = YDSXG_CTRLLENGTH, + .data = (u8 *)CntrlInst1E, +}; +#endif + +#ifdef CONFIG_SND_YMFPCI_FIRMWARE_IN_KERNEL +static int snd_ymfpci_request_firmware(struct snd_ymfpci *chip) +{ + chip->dsp_microcode = &snd_ymfpci_dsp_microcode; + if (chip->device_id == PCI_DEVICE_ID_YAMAHA_724F || + chip->device_id == PCI_DEVICE_ID_YAMAHA_740C || + chip->device_id == PCI_DEVICE_ID_YAMAHA_744 || + chip->device_id == PCI_DEVICE_ID_YAMAHA_754) + chip->controller_microcode = + &snd_ymfpci_controller_1e_microcode; + else + chip->controller_microcode = + &snd_ymfpci_controller_microcode; + return 0; +} + +#else /* use fw_loader */ + +#ifdef __LITTLE_ENDIAN +static inline void snd_ymfpci_convert_from_le(const struct firmware *fw) { } +#else +static void snd_ymfpci_convert_from_le(const struct firmware *fw) +{ + int i; + u32 *data = (u32 *)fw->data; + + for (i = 0; i < fw->size / 4; ++i) + le32_to_cpus(&data[i]); +} +#endif + +static int snd_ymfpci_request_firmware(struct snd_ymfpci *chip) +{ + int err, is_1e; + const char *name; + + err = request_firmware(&chip->dsp_microcode, "yamaha/ds1_dsp.fw", + &chip->pci->dev); + if (err >= 0) { + if (chip->dsp_microcode->size == YDSXG_DSPLENGTH) + snd_ymfpci_convert_from_le(chip->dsp_microcode); + else { + snd_printk(KERN_ERR "DSP microcode has wrong size\n"); + err = -EINVAL; + } + } + if (err < 0) + return err; + is_1e = chip->device_id == PCI_DEVICE_ID_YAMAHA_724F || + chip->device_id == PCI_DEVICE_ID_YAMAHA_740C || + chip->device_id == PCI_DEVICE_ID_YAMAHA_744 || + chip->device_id == PCI_DEVICE_ID_YAMAHA_754; + name = is_1e ? "yamaha/ds1e_ctrl.fw" : "yamaha/ds1_ctrl.fw"; + err = request_firmware(&chip->controller_microcode, name, + &chip->pci->dev); + if (err >= 0) { + if (chip->controller_microcode->size == YDSXG_CTRLLENGTH) + snd_ymfpci_convert_from_le(chip->controller_microcode); + else { + snd_printk(KERN_ERR "controller microcode" + " has wrong size\n"); + err = -EINVAL; + } + } + if (err < 0) + return err; + return 0; +} + +MODULE_FIRMWARE("yamaha/ds1_dsp.fw"); +MODULE_FIRMWARE("yamaha/ds1_ctrl.fw"); +MODULE_FIRMWARE("yamaha/ds1e_ctrl.fw"); + +#endif + +static void snd_ymfpci_download_image(struct snd_ymfpci *chip) +{ + int i; + u16 ctrl; + u32 *inst; + + snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0x00000000); + snd_ymfpci_disable_dsp(chip); + snd_ymfpci_writel(chip, YDSXGR_MODE, 0x00010000); + snd_ymfpci_writel(chip, YDSXGR_MODE, 0x00000000); + snd_ymfpci_writel(chip, YDSXGR_MAPOFREC, 0x00000000); + snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT, 0x00000000); + snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, 0x00000000); + snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, 0x00000000); + snd_ymfpci_writel(chip, YDSXGR_EFFCTRLBASE, 0x00000000); + ctrl = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL); + snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, ctrl & ~0x0007); + + /* setup DSP instruction code */ + inst = (u32 *)chip->dsp_microcode->data; + for (i = 0; i < YDSXG_DSPLENGTH / 4; i++) + snd_ymfpci_writel(chip, YDSXGR_DSPINSTRAM + (i << 2), inst[i]); + + /* setup control instruction code */ + inst = (u32 *)chip->controller_microcode->data; + for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++) + snd_ymfpci_writel(chip, YDSXGR_CTRLINSTRAM + (i << 2), inst[i]); + + snd_ymfpci_enable_dsp(chip); +} + +static int __devinit snd_ymfpci_memalloc(struct snd_ymfpci *chip) +{ + long size, playback_ctrl_size; + int voice, bank, reg; + u8 *ptr; + dma_addr_t ptr_addr; + + playback_ctrl_size = 4 + 4 * YDSXG_PLAYBACK_VOICES; + chip->bank_size_playback = snd_ymfpci_readl(chip, YDSXGR_PLAYCTRLSIZE) << 2; + chip->bank_size_capture = snd_ymfpci_readl(chip, YDSXGR_RECCTRLSIZE) << 2; + chip->bank_size_effect = snd_ymfpci_readl(chip, YDSXGR_EFFCTRLSIZE) << 2; + chip->work_size = YDSXG_DEFAULT_WORK_SIZE; + + size = ALIGN(playback_ctrl_size, 0x100) + + ALIGN(chip->bank_size_playback * 2 * YDSXG_PLAYBACK_VOICES, 0x100) + + ALIGN(chip->bank_size_capture * 2 * YDSXG_CAPTURE_VOICES, 0x100) + + ALIGN(chip->bank_size_effect * 2 * YDSXG_EFFECT_VOICES, 0x100) + + chip->work_size; + /* work_ptr must be aligned to 256 bytes, but it's already + covered with the kernel page allocation mechanism */ + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), + size, &chip->work_ptr) < 0) + return -ENOMEM; + ptr = chip->work_ptr.area; + ptr_addr = chip->work_ptr.addr; + memset(ptr, 0, size); /* for sure */ + + chip->bank_base_playback = ptr; + chip->bank_base_playback_addr = ptr_addr; + chip->ctrl_playback = (u32 *)ptr; + chip->ctrl_playback[0] = cpu_to_le32(YDSXG_PLAYBACK_VOICES); + ptr += ALIGN(playback_ctrl_size, 0x100); + ptr_addr += ALIGN(playback_ctrl_size, 0x100); + for (voice = 0; voice < YDSXG_PLAYBACK_VOICES; voice++) { + chip->voices[voice].number = voice; + chip->voices[voice].bank = (struct snd_ymfpci_playback_bank *)ptr; + chip->voices[voice].bank_addr = ptr_addr; + for (bank = 0; bank < 2; bank++) { + chip->bank_playback[voice][bank] = (struct snd_ymfpci_playback_bank *)ptr; + ptr += chip->bank_size_playback; + ptr_addr += chip->bank_size_playback; + } + } + ptr = (char *)ALIGN((unsigned long)ptr, 0x100); + ptr_addr = ALIGN(ptr_addr, 0x100); + chip->bank_base_capture = ptr; + chip->bank_base_capture_addr = ptr_addr; + for (voice = 0; voice < YDSXG_CAPTURE_VOICES; voice++) + for (bank = 0; bank < 2; bank++) { + chip->bank_capture[voice][bank] = (struct snd_ymfpci_capture_bank *)ptr; + ptr += chip->bank_size_capture; + ptr_addr += chip->bank_size_capture; + } + ptr = (char *)ALIGN((unsigned long)ptr, 0x100); + ptr_addr = ALIGN(ptr_addr, 0x100); + chip->bank_base_effect = ptr; + chip->bank_base_effect_addr = ptr_addr; + for (voice = 0; voice < YDSXG_EFFECT_VOICES; voice++) + for (bank = 0; bank < 2; bank++) { + chip->bank_effect[voice][bank] = (struct snd_ymfpci_effect_bank *)ptr; + ptr += chip->bank_size_effect; + ptr_addr += chip->bank_size_effect; + } + ptr = (char *)ALIGN((unsigned long)ptr, 0x100); + ptr_addr = ALIGN(ptr_addr, 0x100); + chip->work_base = ptr; + chip->work_base_addr = ptr_addr; + + snd_assert(ptr + chip->work_size == chip->work_ptr.area + chip->work_ptr.bytes, ); + + snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, chip->bank_base_playback_addr); + snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, chip->bank_base_capture_addr); + snd_ymfpci_writel(chip, YDSXGR_EFFCTRLBASE, chip->bank_base_effect_addr); + snd_ymfpci_writel(chip, YDSXGR_WORKBASE, chip->work_base_addr); + snd_ymfpci_writel(chip, YDSXGR_WORKSIZE, chip->work_size >> 2); + + /* S/PDIF output initialization */ + chip->spdif_bits = chip->spdif_pcm_bits = SNDRV_PCM_DEFAULT_CON_SPDIF & 0xffff; + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL, 0); + snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits); + + /* S/PDIF input initialization */ + snd_ymfpci_writew(chip, YDSXGR_SPDIFINCTRL, 0); + + /* digital mixer setup */ + for (reg = 0x80; reg < 0xc0; reg += 4) + snd_ymfpci_writel(chip, reg, 0); + snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0x3fff3fff); + snd_ymfpci_writel(chip, YDSXGR_ZVOUTVOL, 0x3fff3fff); + snd_ymfpci_writel(chip, YDSXGR_SPDIFOUTVOL, 0x3fff3fff); + snd_ymfpci_writel(chip, YDSXGR_NATIVEADCINVOL, 0x3fff3fff); + snd_ymfpci_writel(chip, YDSXGR_NATIVEDACINVOL, 0x3fff3fff); + snd_ymfpci_writel(chip, YDSXGR_PRIADCLOOPVOL, 0x3fff3fff); + snd_ymfpci_writel(chip, YDSXGR_LEGACYOUTVOL, 0x3fff3fff); + + return 0; +} + +static int snd_ymfpci_free(struct snd_ymfpci *chip) +{ + u16 ctrl; + + snd_assert(chip != NULL, return -EINVAL); + + if (chip->res_reg_area) { /* don't touch busy hardware */ + snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0); + snd_ymfpci_writel(chip, YDSXGR_BUF441OUTVOL, 0); + snd_ymfpci_writel(chip, YDSXGR_LEGACYOUTVOL, 0); + snd_ymfpci_writel(chip, YDSXGR_STATUS, ~0); + snd_ymfpci_disable_dsp(chip); + snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, 0); + snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, 0); + snd_ymfpci_writel(chip, YDSXGR_EFFCTRLBASE, 0); + snd_ymfpci_writel(chip, YDSXGR_WORKBASE, 0); + snd_ymfpci_writel(chip, YDSXGR_WORKSIZE, 0); + ctrl = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL); + snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, ctrl & ~0x0007); + } + + snd_ymfpci_ac3_done(chip); + + /* Set PCI device to D3 state */ +#if 0 + /* FIXME: temporarily disabled, otherwise we cannot fire up + * the chip again unless reboot. ACPI bug? + */ + pci_set_power_state(chip->pci, 3); +#endif + +#ifdef CONFIG_PM + vfree(chip->saved_regs); +#endif + release_and_free_resource(chip->mpu_res); + release_and_free_resource(chip->fm_res); + snd_ymfpci_free_gameport(chip); + if (chip->reg_area_virt) + iounmap(chip->reg_area_virt); + if (chip->work_ptr.area) + snd_dma_free_pages(&chip->work_ptr); + + if (chip->irq >= 0) + free_irq(chip->irq, chip); + release_and_free_resource(chip->res_reg_area); + + pci_write_config_word(chip->pci, 0x40, chip->old_legacy_ctrl); + + pci_disable_device(chip->pci); +#ifndef CONFIG_SND_YMFPCI_FIRMWARE_IN_KERNEL + release_firmware(chip->dsp_microcode); + release_firmware(chip->controller_microcode); +#endif + kfree(chip); + return 0; +} + +static int snd_ymfpci_dev_free(struct snd_device *device) +{ + struct snd_ymfpci *chip = device->device_data; + return snd_ymfpci_free(chip); +} + +#ifdef CONFIG_PM +static int saved_regs_index[] = { + /* spdif */ + YDSXGR_SPDIFOUTCTRL, + YDSXGR_SPDIFOUTSTATUS, + YDSXGR_SPDIFINCTRL, + /* volumes */ + YDSXGR_PRIADCLOOPVOL, + YDSXGR_NATIVEDACINVOL, + YDSXGR_NATIVEDACOUTVOL, + YDSXGR_BUF441OUTVOL, + YDSXGR_NATIVEADCINVOL, + YDSXGR_SPDIFLOOPVOL, + YDSXGR_SPDIFOUTVOL, + YDSXGR_ZVOUTVOL, + YDSXGR_LEGACYOUTVOL, + /* address bases */ + YDSXGR_PLAYCTRLBASE, + YDSXGR_RECCTRLBASE, + YDSXGR_EFFCTRLBASE, + YDSXGR_WORKBASE, + /* capture set up */ + YDSXGR_MAPOFREC, + YDSXGR_RECFORMAT, + YDSXGR_RECSLOTSR, + YDSXGR_ADCFORMAT, + YDSXGR_ADCSLOTSR, +}; +#define YDSXGR_NUM_SAVED_REGS ARRAY_SIZE(saved_regs_index) + +int snd_ymfpci_suspend(struct pci_dev *pci, pm_message_t state) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct snd_ymfpci *chip = card->private_data; + unsigned int i; + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + snd_pcm_suspend_all(chip->pcm); + snd_pcm_suspend_all(chip->pcm2); + snd_pcm_suspend_all(chip->pcm_spdif); + snd_pcm_suspend_all(chip->pcm_4ch); + snd_ac97_suspend(chip->ac97); + for (i = 0; i < YDSXGR_NUM_SAVED_REGS; i++) + chip->saved_regs[i] = snd_ymfpci_readl(chip, saved_regs_index[i]); + chip->saved_ydsxgr_mode = snd_ymfpci_readl(chip, YDSXGR_MODE); + snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0); + snd_ymfpci_disable_dsp(chip); + pci_disable_device(pci); + pci_save_state(pci); + pci_set_power_state(pci, pci_choose_state(pci, state)); + return 0; +} + +int snd_ymfpci_resume(struct pci_dev *pci) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct snd_ymfpci *chip = card->private_data; + unsigned int i; + + pci_set_power_state(pci, PCI_D0); + pci_restore_state(pci); + if (pci_enable_device(pci) < 0) { + printk(KERN_ERR "ymfpci: pci_enable_device failed, " + "disabling device\n"); + snd_card_disconnect(card); + return -EIO; + } + pci_set_master(pci); + snd_ymfpci_aclink_reset(pci); + snd_ymfpci_codec_ready(chip, 0); + snd_ymfpci_download_image(chip); + udelay(100); + + for (i = 0; i < YDSXGR_NUM_SAVED_REGS; i++) + snd_ymfpci_writel(chip, saved_regs_index[i], chip->saved_regs[i]); + + snd_ac97_resume(chip->ac97); + + /* start hw again */ + if (chip->start_count > 0) { + spin_lock_irq(&chip->reg_lock); + snd_ymfpci_writel(chip, YDSXGR_MODE, chip->saved_ydsxgr_mode); + chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT); + spin_unlock_irq(&chip->reg_lock); + } + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + return 0; +} +#endif /* CONFIG_PM */ + +int __devinit snd_ymfpci_create(struct snd_card *card, + struct pci_dev * pci, + unsigned short old_legacy_ctrl, + struct snd_ymfpci ** rchip) +{ + struct snd_ymfpci *chip; + int err; + static struct snd_device_ops ops = { + .dev_free = snd_ymfpci_dev_free, + }; + + *rchip = NULL; + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (chip == NULL) { + pci_disable_device(pci); + return -ENOMEM; + } + chip->old_legacy_ctrl = old_legacy_ctrl; + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->voice_lock); + init_waitqueue_head(&chip->interrupt_sleep); + atomic_set(&chip->interrupt_sleep_count, 0); + chip->card = card; + chip->pci = pci; + chip->irq = -1; + chip->device_id = pci->device; + chip->rev = pci->revision; + chip->reg_area_phys = pci_resource_start(pci, 0); + chip->reg_area_virt = ioremap_nocache(chip->reg_area_phys, 0x8000); + pci_set_master(pci); + chip->src441_used = -1; + + if ((chip->res_reg_area = request_mem_region(chip->reg_area_phys, 0x8000, "YMFPCI")) == NULL) { + snd_printk(KERN_ERR "unable to grab memory region 0x%lx-0x%lx\n", chip->reg_area_phys, chip->reg_area_phys + 0x8000 - 1); + snd_ymfpci_free(chip); + return -EBUSY; + } + if (request_irq(pci->irq, snd_ymfpci_interrupt, IRQF_SHARED, + "YMFPCI", chip)) { + snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); + snd_ymfpci_free(chip); + return -EBUSY; + } + chip->irq = pci->irq; + + snd_ymfpci_aclink_reset(pci); + if (snd_ymfpci_codec_ready(chip, 0) < 0) { + snd_ymfpci_free(chip); + return -EIO; + } + + err = snd_ymfpci_request_firmware(chip); + if (err < 0) { + snd_printk(KERN_ERR "firmware request failed: %d\n", err); + snd_ymfpci_free(chip); + return err; + } + snd_ymfpci_download_image(chip); + + udelay(100); /* seems we need a delay after downloading image.. */ + + if (snd_ymfpci_memalloc(chip) < 0) { + snd_ymfpci_free(chip); + return -EIO; + } + + if ((err = snd_ymfpci_ac3_init(chip)) < 0) { + snd_ymfpci_free(chip); + return err; + } + +#ifdef CONFIG_PM + chip->saved_regs = vmalloc(YDSXGR_NUM_SAVED_REGS * sizeof(u32)); + if (chip->saved_regs == NULL) { + snd_ymfpci_free(chip); + return -ENOMEM; + } +#endif + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_ymfpci_free(chip); + return err; + } + + snd_ymfpci_proc_init(card, chip); + + snd_card_set_dev(card, &pci->dev); + + *rchip = chip; + return 0; +} diff -Nur sound/pcmcia/vx/vxp_mixer.c sound/pcmcia/vx/vxp_mixer.c --- sound/pcmcia/vx/vxp_mixer.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/pcmcia/vx/vxp_mixer.c 2007-07-24 02:00:10.000000000 +0200 @@ -80,14 +80,7 @@ /* * mic boost level control (for VXP440) */ -static int vx_mic_boost_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define vx_mic_boost_info snd_ctl_boolean_mono_info static int vx_mic_boost_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { diff -Nur sound/ppc/Kconfig sound/ppc/Kconfig --- sound/ppc/Kconfig 2007-07-09 01:32:17.000000000 +0200 +++ sound/ppc/Kconfig 2007-07-04 02:00:07.000000000 +0200 @@ -33,3 +33,23 @@ option. endmenu + +menu "ALSA PowerPC devices" + depends on SND!=n && ( PPC64 || PPC32 ) + +config SND_PS3 + tristate "PS3 Audio support" + depends on SND && PS3_PS3AV + select SND_PCM + default m + help + Say Y here to include support for audio on the PS3 + + To compile this driver as a module, choose M here: the module + will be called snd_ps3. + +config SND_PS3_DEFAULT_START_DELAY + int "Startup delay time in ms" + depends on SND_PS3 + default "2000" +endmenu diff -Nur sound/ppc/Makefile sound/ppc/Makefile --- sound/ppc/Makefile 2007-07-09 01:32:17.000000000 +0200 +++ sound/ppc/Makefile 2007-07-04 02:00:07.000000000 +0200 @@ -6,4 +6,5 @@ snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o keywest.o beep.o # Toplevel Module Dependency -obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o +obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o +obj-$(CONFIG_SND_PS3) += snd_ps3.o diff -Nur sound/ppc/beep.c sound/ppc/beep.c --- sound/ppc/beep.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/ppc/beep.c 2007-07-20 02:00:08.000000000 +0200 @@ -118,7 +118,7 @@ default: return -1; } - chip = dev->private; + chip = input_get_drvdata(dev); if (! chip || (beep = chip->beep) == NULL) return -1; @@ -239,8 +239,8 @@ input_dev->evbit[0] = BIT(EV_SND); input_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE); input_dev->event = snd_pmac_beep_event; - input_dev->private = chip; - input_dev->cdev.dev = &chip->pdev->dev; + input_dev->dev.parent = &chip->pdev->dev; + input_set_drvdata(input_dev, chip); beep->dev = input_dev; beep->buf = dmabuf; @@ -251,8 +251,8 @@ err = snd_ctl_add(chip->card, beep_ctl); if (err < 0) goto fail1; - - chip->beep = beep; + + chip->beep = beep; err = input_register_device(beep->dev); if (err) diff -Nur sound/ppc/daca.c sound/ppc/daca.c --- sound/ppc/daca.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/ppc/daca.c 2007-07-24 02:00:10.000000000 +0200 @@ -91,15 +91,7 @@ /* deemphasis switch */ -static int daca_info_deemphasis(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define daca_info_deemphasis snd_ctl_boolean_mono_info static int daca_get_deemphasis(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff -Nur sound/ppc/pmac.c sound/ppc/pmac.c --- sound/ppc/pmac.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/ppc/pmac.c 2007-08-14 02:00:08.000000000 +0200 @@ -490,35 +490,14 @@ struct snd_pcm_substream *subs) { struct snd_pcm_runtime *runtime = subs->runtime; - int i, j, fflags; - static int typical_freqs[] = { - 44100, - 22050, - 11025, - 0, - }; - static int typical_freq_flags[] = { - SNDRV_PCM_RATE_44100, - SNDRV_PCM_RATE_22050, - SNDRV_PCM_RATE_11025, - 0, - }; + int i; /* look up frequency table and fill bit mask */ runtime->hw.rates = 0; - fflags = chip->freqs_ok; - for (i = 0; typical_freqs[i]; i++) { - for (j = 0; j < chip->num_freqs; j++) { - if ((chip->freqs_ok & (1 << j)) && - chip->freq_table[j] == typical_freqs[i]) { - runtime->hw.rates |= typical_freq_flags[i]; - fflags &= ~(1 << j); - break; - } - } - } - if (fflags) /* rest */ - runtime->hw.rates |= SNDRV_PCM_RATE_KNOT; + for (i = 0; i < chip->num_freqs; i++) + if (chip->freqs_ok & (1 << i)) + runtime->hw.rates |= + snd_pcm_rate_to_rate_bit(chip->freq_table[i]); /* check for minimum and maximum rates */ for (i = 0; i < chip->num_freqs; i++) { @@ -551,9 +530,6 @@ runtime->hw.periods_max = rec->cmd.size - 1; - if (chip->can_duplex) - snd_pcm_set_sync(subs); - /* constraints to fix choppy sound */ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); return 0; @@ -1035,29 +1011,6 @@ return 0; } -/* - * exported - boolean info callbacks for ease of programming - */ -int snd_pmac_boolean_stereo_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} - -int snd_pmac_boolean_mono_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} - #ifdef PMAC_SUPPORT_AUTOMUTE /* * auto-mute diff -Nur sound/ppc/pmac.h sound/ppc/pmac.h --- sound/ppc/pmac.h 2007-07-09 01:32:17.000000000 +0200 +++ sound/ppc/pmac.h 2007-07-24 02:00:10.000000000 +0200 @@ -202,8 +202,8 @@ void snd_pmac_keywest_cleanup(struct pmac_keywest *i2c); /* misc */ -int snd_pmac_boolean_stereo_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); -int snd_pmac_boolean_mono_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); +#define snd_pmac_boolean_stereo_info snd_ctl_boolean_stereo_info +#define snd_pmac_boolean_mono_info snd_ctl_boolean_mono_info int snd_pmac_add_automute(struct snd_pmac *chip); diff -Nur sound/ppc/snd_ps3.c sound/ppc/snd_ps3.c --- sound/ppc/snd_ps3.c 1970-01-01 01:00:00.000000000 +0100 +++ sound/ppc/snd_ps3.c 2007-07-24 02:00:10.000000000 +0200 @@ -0,0 +1,1124 @@ +/* + * Audio support for PS3 + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * All rights reserved. + * Copyright 2006, 2007 Sony Corporation + * + * 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 Licence. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "snd_ps3_reg.h" +#include "snd_ps3.h" + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("PS3 sound driver"); +MODULE_AUTHOR("Sony Computer Entertainment Inc."); + +/* module entries */ +static int __init snd_ps3_init(void); +static void __exit snd_ps3_exit(void); + +/* ALSA snd driver ops */ +static int snd_ps3_pcm_open(struct snd_pcm_substream *substream); +static int snd_ps3_pcm_close(struct snd_pcm_substream *substream); +static int snd_ps3_pcm_prepare(struct snd_pcm_substream *substream); +static int snd_ps3_pcm_trigger(struct snd_pcm_substream *substream, + int cmd); +static snd_pcm_uframes_t snd_ps3_pcm_pointer(struct snd_pcm_substream + *substream); +static int snd_ps3_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params); +static int snd_ps3_pcm_hw_free(struct snd_pcm_substream *substream); + + +/* ps3_system_bus_driver entries */ +static int __init snd_ps3_driver_probe(struct ps3_system_bus_device *dev); +static int snd_ps3_driver_remove(struct ps3_system_bus_device *dev); + +/* address setup */ +static int snd_ps3_map_mmio(void); +static void snd_ps3_unmap_mmio(void); +static int snd_ps3_allocate_irq(void); +static void snd_ps3_free_irq(void); +static void snd_ps3_audio_set_base_addr(uint64_t ioaddr_start); + +/* interrupt handler */ +static irqreturn_t snd_ps3_interrupt(int irq, void *dev_id); + + +/* set sampling rate/format */ +static int snd_ps3_set_avsetting(struct snd_pcm_substream *substream); +/* take effect parameter change */ +static int snd_ps3_change_avsetting(struct snd_ps3_card_info *card); +/* initialize avsetting and take it effect */ +static int snd_ps3_init_avsetting(struct snd_ps3_card_info *card); +/* setup dma */ +static int snd_ps3_program_dma(struct snd_ps3_card_info *card, + enum snd_ps3_dma_filltype filltype); +static void snd_ps3_wait_for_dma_stop(struct snd_ps3_card_info *card); + +static dma_addr_t v_to_bus(struct snd_ps3_card_info *, void *vaddr, int ch); + + +module_init(snd_ps3_init); +module_exit(snd_ps3_exit); + +/* + * global + */ +static struct snd_ps3_card_info the_card; + +static int snd_ps3_start_delay = CONFIG_SND_PS3_DEFAULT_START_DELAY; + +module_param_named(start_delay, snd_ps3_start_delay, uint, 0644); +MODULE_PARM_DESC(start_delay, "time to insert silent data in milisec"); + +static int index = SNDRV_DEFAULT_IDX1; +static char *id = SNDRV_DEFAULT_STR1; + +module_param(index, int, 0444); +MODULE_PARM_DESC(index, "Index value for PS3 soundchip."); +module_param(id, charp, 0444); +MODULE_PARM_DESC(id, "ID string for PS3 soundchip."); + + +/* + * PS3 audio register access + */ +static inline u32 read_reg(unsigned int reg) +{ + return in_be32(the_card.mapped_mmio_vaddr + reg); +} +static inline void write_reg(unsigned int reg, u32 val) +{ + out_be32(the_card.mapped_mmio_vaddr + reg, val); +} +static inline void update_reg(unsigned int reg, u32 or_val) +{ + u32 newval = read_reg(reg) | or_val; + write_reg(reg, newval); +} +static inline void update_mask_reg(unsigned int reg, u32 mask, u32 or_val) +{ + u32 newval = (read_reg(reg) & mask) | or_val; + write_reg(reg, newval); +} + +/* + * ALSA defs + */ +const static struct snd_pcm_hardware snd_ps3_pcm_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_NONINTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = (SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S24_BE), + .rates = (SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000), + .rate_min = 44100, + .rate_max = 96000, + + .channels_min = 2, /* stereo only */ + .channels_max = 2, + + .buffer_bytes_max = PS3_AUDIO_FIFO_SIZE * 64, + + /* interrupt by four stages */ + .period_bytes_min = PS3_AUDIO_FIFO_STAGE_SIZE * 4, + .period_bytes_max = PS3_AUDIO_FIFO_STAGE_SIZE * 4, + + .periods_min = 16, + .periods_max = 32, /* buffer_size_max/ period_bytes_max */ + + .fifo_size = PS3_AUDIO_FIFO_SIZE +}; + +static struct snd_pcm_ops snd_ps3_pcm_spdif_ops = +{ + .open = snd_ps3_pcm_open, + .close = snd_ps3_pcm_close, + .prepare = snd_ps3_pcm_prepare, + .ioctl = snd_pcm_lib_ioctl, + .trigger = snd_ps3_pcm_trigger, + .pointer = snd_ps3_pcm_pointer, + .hw_params = snd_ps3_pcm_hw_params, + .hw_free = snd_ps3_pcm_hw_free +}; + +static int snd_ps3_verify_dma_stop(struct snd_ps3_card_info *card, + int count, int force_stop) +{ + int dma_ch, done, retries, stop_forced = 0; + uint32_t status; + + for (dma_ch = 0; dma_ch < 8; dma_ch ++) { + retries = count; + do { + status = read_reg(PS3_AUDIO_KICK(dma_ch)) & + PS3_AUDIO_KICK_STATUS_MASK; + switch (status) { + case PS3_AUDIO_KICK_STATUS_DONE: + case PS3_AUDIO_KICK_STATUS_NOTIFY: + case PS3_AUDIO_KICK_STATUS_CLEAR: + case PS3_AUDIO_KICK_STATUS_ERROR: + done = 1; + break; + default: + done = 0; + udelay(10); + } + } while (!done && --retries); + if (!retries && force_stop) { + pr_info("%s: DMA ch %d is not stopped.", + __func__, dma_ch); + /* last resort. force to stop dma. + * NOTE: this cause DMA done interrupts + */ + update_reg(PS3_AUDIO_CONFIG, PS3_AUDIO_CONFIG_CLEAR); + stop_forced = 1; + } + } + return stop_forced; +} + +/* + * wait for all dma is done. + * NOTE: caller should reset card->running before call. + * If not, the interrupt handler will re-start DMA, + * then DMA is never stopped. + */ +static void snd_ps3_wait_for_dma_stop(struct snd_ps3_card_info *card) +{ + int stop_forced; + /* + * wait for the last dma is done + */ + + /* + * expected maximum DMA done time is 5.7ms + something (DMA itself). + * 5.7ms is from 16bit/sample 2ch 44.1Khz; the time next + * DMA kick event would occur. + */ + stop_forced = snd_ps3_verify_dma_stop(card, 700, 1); + + /* + * clear outstanding interrupts. + */ + update_reg(PS3_AUDIO_INTR_0, 0); + update_reg(PS3_AUDIO_AX_IS, 0); + + /* + *revert CLEAR bit since it will not reset automatically after DMA stop + */ + if (stop_forced) + update_mask_reg(PS3_AUDIO_CONFIG, ~PS3_AUDIO_CONFIG_CLEAR, 0); + /* ensure the hardware sees changes */ + wmb(); +} + +static void snd_ps3_kick_dma(struct snd_ps3_card_info *card) +{ + + update_reg(PS3_AUDIO_KICK(0), PS3_AUDIO_KICK_REQUEST); + /* ensure the hardware sees the change */ + wmb(); +} + +/* + * convert virtual addr to ioif bus addr. + */ +static dma_addr_t v_to_bus(struct snd_ps3_card_info *card, + void * paddr, + int ch) +{ + return card->dma_start_bus_addr[ch] + + (paddr - card->dma_start_vaddr[ch]); +}; + + +/* + * increment ring buffer pointer. + * NOTE: caller must hold write spinlock + */ +static void snd_ps3_bump_buffer(struct snd_ps3_card_info *card, + enum snd_ps3_ch ch, size_t byte_count, + int stage) +{ + if (!stage) + card->dma_last_transfer_vaddr[ch] = + card->dma_next_transfer_vaddr[ch]; + card->dma_next_transfer_vaddr[ch] += byte_count; + if ((card->dma_start_vaddr[ch] + (card->dma_buffer_size / 2)) <= + card->dma_next_transfer_vaddr[ch]) { + card->dma_next_transfer_vaddr[ch] = card->dma_start_vaddr[ch]; + } +} +/* + * setup dmac to send data to audio and attenuate samples on the ring buffer + */ +static int snd_ps3_program_dma(struct snd_ps3_card_info *card, + enum snd_ps3_dma_filltype filltype) +{ + /* this dmac does not support over 4G */ + uint32_t dma_addr; + int fill_stages, dma_ch, stage; + enum snd_ps3_ch ch; + uint32_t ch0_kick_event = 0; /* initialize to mute gcc */ + void *start_vaddr; + unsigned long irqsave; + int silent = 0; + + switch (filltype) { + case SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL: + silent = 1; + /* intentionally fall thru */ + case SND_PS3_DMA_FILLTYPE_FIRSTFILL: + ch0_kick_event = PS3_AUDIO_KICK_EVENT_ALWAYS; + break; + + case SND_PS3_DMA_FILLTYPE_SILENT_RUNNING: + silent = 1; + /* intentionally fall thru */ + case SND_PS3_DMA_FILLTYPE_RUNNING: + ch0_kick_event = PS3_AUDIO_KICK_EVENT_SERIALOUT0_EMPTY; + break; + } + + snd_ps3_verify_dma_stop(card, 700, 0); + fill_stages = 4; + spin_lock_irqsave(&card->dma_lock, irqsave); + for (ch = 0; ch < 2; ch++) { + start_vaddr = card->dma_next_transfer_vaddr[0]; + for (stage = 0; stage < fill_stages; stage ++) { + dma_ch = stage * 2 + ch; + if (silent) + dma_addr = card->null_buffer_start_dma_addr; + else + dma_addr = + v_to_bus(card, + card->dma_next_transfer_vaddr[ch], + ch); + + write_reg(PS3_AUDIO_SOURCE(dma_ch), + (PS3_AUDIO_SOURCE_TARGET_SYSTEM_MEMORY | + dma_addr)); + + /* dst: fixed to 3wire#0 */ + if (ch == 0) + write_reg(PS3_AUDIO_DEST(dma_ch), + (PS3_AUDIO_DEST_TARGET_AUDIOFIFO | + PS3_AUDIO_AO_3W_LDATA(0))); + else + write_reg(PS3_AUDIO_DEST(dma_ch), + (PS3_AUDIO_DEST_TARGET_AUDIOFIFO | + PS3_AUDIO_AO_3W_RDATA(0))); + + /* count always 1 DMA block (1/2 stage = 128 bytes) */ + write_reg(PS3_AUDIO_DMASIZE(dma_ch), 0); + /* bump pointer if needed */ + if (!silent) + snd_ps3_bump_buffer(card, ch, + PS3_AUDIO_DMAC_BLOCK_SIZE, + stage); + + /* kick event */ + if (dma_ch == 0) + write_reg(PS3_AUDIO_KICK(dma_ch), + ch0_kick_event); + else + write_reg(PS3_AUDIO_KICK(dma_ch), + PS3_AUDIO_KICK_EVENT_AUDIO_DMA(dma_ch + - 1) | + PS3_AUDIO_KICK_REQUEST); + } + } + /* ensure the hardware sees the change */ + wmb(); + spin_unlock_irqrestore(&card->dma_lock, irqsave); + + return 0; +} + +/* + * audio mute on/off + * mute_on : 0 output enabled + * 1 mute + */ +static int snd_ps3_mute(int mute_on) +{ + return ps3av_audio_mute(mute_on); +} + +/* + * PCM operators + */ +static int snd_ps3_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); + int pcm_index; + + pcm_index = substream->pcm->device; + /* to retrieve substream/runtime in interrupt handler */ + card->substream = substream; + + runtime->hw = snd_ps3_pcm_hw; + + card->start_delay = snd_ps3_start_delay; + + /* mute off */ + snd_ps3_mute(0); /* this function sleep */ + + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + PS3_AUDIO_FIFO_STAGE_SIZE * 4 * 2); + return 0; +}; + +static int snd_ps3_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + size_t size; + + /* alloc transport buffer */ + size = params_buffer_bytes(hw_params); + snd_pcm_lib_malloc_pages(substream, size); + return 0; +}; + +static int snd_ps3_delay_to_bytes(struct snd_pcm_substream *substream, + unsigned int delay_ms) +{ + int ret; + int rate ; + + rate = substream->runtime->rate; + ret = snd_pcm_format_size(substream->runtime->format, + rate * delay_ms / 1000) + * substream->runtime->channels; + + pr_debug(KERN_ERR "%s: time=%d rate=%d bytes=%ld, frames=%d, ret=%d\n", + __func__, + delay_ms, + rate, + snd_pcm_format_size(substream->runtime->format, rate), + rate * delay_ms / 1000, + ret); + + return ret; +}; + +static int snd_ps3_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); + unsigned long irqsave; + + if (!snd_ps3_set_avsetting(substream)) { + /* some parameter changed */ + write_reg(PS3_AUDIO_AX_IE, + PS3_AUDIO_AX_IE_ASOBEIE(0) | + PS3_AUDIO_AX_IE_ASOBUIE(0)); + /* + * let SPDIF device re-lock with SPDIF signal, + * start with some silence + */ + card->silent = snd_ps3_delay_to_bytes(substream, + card->start_delay) / + (PS3_AUDIO_FIFO_STAGE_SIZE * 4); /* every 4 times */ + } + + /* restart ring buffer pointer */ + spin_lock_irqsave(&card->dma_lock, irqsave); + { + card->dma_buffer_size = runtime->dma_bytes; + + card->dma_last_transfer_vaddr[SND_PS3_CH_L] = + card->dma_next_transfer_vaddr[SND_PS3_CH_L] = + card->dma_start_vaddr[SND_PS3_CH_L] = + runtime->dma_area; + card->dma_start_bus_addr[SND_PS3_CH_L] = runtime->dma_addr; + + card->dma_last_transfer_vaddr[SND_PS3_CH_R] = + card->dma_next_transfer_vaddr[SND_PS3_CH_R] = + card->dma_start_vaddr[SND_PS3_CH_R] = + runtime->dma_area + (runtime->dma_bytes / 2); + card->dma_start_bus_addr[SND_PS3_CH_R] = + runtime->dma_addr + (runtime->dma_bytes / 2); + + pr_debug("%s: vaddr=%p bus=%#lx\n", __func__, + card->dma_start_vaddr[SND_PS3_CH_L], + card->dma_start_bus_addr[SND_PS3_CH_L]); + + } + spin_unlock_irqrestore(&card->dma_lock, irqsave); + + /* ensure the hardware sees the change */ + mb(); + + return 0; +}; + +static int snd_ps3_pcm_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + /* clear outstanding interrupts */ + update_reg(PS3_AUDIO_AX_IS, 0); + + spin_lock(&card->dma_lock); + { + card->running = 1; + } + spin_unlock(&card->dma_lock); + + snd_ps3_program_dma(card, + SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL); + snd_ps3_kick_dma(card); + while (read_reg(PS3_AUDIO_KICK(7)) & + PS3_AUDIO_KICK_STATUS_MASK) { + udelay(1); + } + snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_RUNNING); + snd_ps3_kick_dma(card); + break; + + case SNDRV_PCM_TRIGGER_STOP: + spin_lock(&card->dma_lock); + { + card->running = 0; + } + spin_unlock(&card->dma_lock); + snd_ps3_wait_for_dma_stop(card); + break; + default: + break; + + } + + return ret; +}; + +/* + * report current pointer + */ +static snd_pcm_uframes_t snd_ps3_pcm_pointer( + struct snd_pcm_substream *substream) +{ + struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); + size_t bytes; + snd_pcm_uframes_t ret; + + spin_lock(&card->dma_lock); + { + bytes = (size_t)(card->dma_last_transfer_vaddr[SND_PS3_CH_L] - + card->dma_start_vaddr[SND_PS3_CH_L]); + } + spin_unlock(&card->dma_lock); + + ret = bytes_to_frames(substream->runtime, bytes * 2); + + return ret; +}; + +static int snd_ps3_pcm_hw_free(struct snd_pcm_substream *substream) +{ + int ret; + ret = snd_pcm_lib_free_pages(substream); + return ret; +}; + +static int snd_ps3_pcm_close(struct snd_pcm_substream *substream) +{ + /* mute on */ + snd_ps3_mute(1); + return 0; +}; + +static void snd_ps3_audio_fixup(struct snd_ps3_card_info *card) +{ + /* + * avsetting driver seems to never change the followings + * so, init them here once + */ + + /* no dma interrupt needed */ + write_reg(PS3_AUDIO_INTR_EN_0, 0); + + /* use every 4 buffer empty interrupt */ + update_mask_reg(PS3_AUDIO_AX_IC, + PS3_AUDIO_AX_IC_AASOIMD_MASK, + PS3_AUDIO_AX_IC_AASOIMD_EVERY4); + + /* enable 3wire clocks */ + update_mask_reg(PS3_AUDIO_AO_3WMCTRL, + ~(PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_DISABLED | + PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_DISABLED), + 0); + update_reg(PS3_AUDIO_AO_3WMCTRL, + PS3_AUDIO_AO_3WMCTRL_ASOPLRCK_DEFAULT); +} + +/* + * av setting + * NOTE: calling this function may generate audio interrupt. + */ +static int snd_ps3_change_avsetting(struct snd_ps3_card_info *card) +{ + int ret, retries, i; + pr_debug("%s: start\n", __func__); + + ret = ps3av_set_audio_mode(card->avs.avs_audio_ch, + card->avs.avs_audio_rate, + card->avs.avs_audio_width, + card->avs.avs_audio_format, + card->avs.avs_audio_source); + /* + * Reset the following unwanted settings: + */ + + /* disable all 3wire buffers */ + update_mask_reg(PS3_AUDIO_AO_3WMCTRL, + ~(PS3_AUDIO_AO_3WMCTRL_ASOEN(0) | + PS3_AUDIO_AO_3WMCTRL_ASOEN(1) | + PS3_AUDIO_AO_3WMCTRL_ASOEN(2) | + PS3_AUDIO_AO_3WMCTRL_ASOEN(3)), + 0); + wmb(); /* ensure the hardware sees the change */ + /* wait for actually stopped */ + retries = 1000; + while ((read_reg(PS3_AUDIO_AO_3WMCTRL) & + (PS3_AUDIO_AO_3WMCTRL_ASORUN(0) | + PS3_AUDIO_AO_3WMCTRL_ASORUN(1) | + PS3_AUDIO_AO_3WMCTRL_ASORUN(2) | + PS3_AUDIO_AO_3WMCTRL_ASORUN(3))) && + --retries) { + udelay(1); + } + + /* reset buffer pointer */ + for (i = 0; i < 4; i++) { + update_reg(PS3_AUDIO_AO_3WCTRL(i), + PS3_AUDIO_AO_3WCTRL_ASOBRST_RESET); + udelay(10); + } + wmb(); /* ensure the hardware actually start resetting */ + + /* enable 3wire#0 buffer */ + update_reg(PS3_AUDIO_AO_3WMCTRL, PS3_AUDIO_AO_3WMCTRL_ASOEN(0)); + + + /* In 24bit mode,ALSA inserts a zero byte at first byte of per sample */ + update_mask_reg(PS3_AUDIO_AO_3WCTRL(0), + ~PS3_AUDIO_AO_3WCTRL_ASODF, + PS3_AUDIO_AO_3WCTRL_ASODF_LSB); + update_mask_reg(PS3_AUDIO_AO_SPDCTRL(0), + ~PS3_AUDIO_AO_SPDCTRL_SPODF, + PS3_AUDIO_AO_SPDCTRL_SPODF_LSB); + /* ensure all the setting above is written back to register */ + wmb(); + /* avsetting driver altered AX_IE, caller must reset it if you want */ + pr_debug("%s: end\n", __func__); + return ret; +} + +static int snd_ps3_init_avsetting(struct snd_ps3_card_info *card) +{ + int ret; + pr_debug("%s: start\n", __func__); + card->avs.avs_audio_ch = PS3AV_CMD_AUDIO_NUM_OF_CH_2; + card->avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K; + card->avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16; + card->avs.avs_audio_format = PS3AV_CMD_AUDIO_FORMAT_PCM; + card->avs.avs_audio_source = PS3AV_CMD_AUDIO_SOURCE_SERIAL; + + ret = snd_ps3_change_avsetting(card); + + snd_ps3_audio_fixup(card); + + /* to start to generate SPDIF signal, fill data */ + snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL); + snd_ps3_kick_dma(card); + pr_debug("%s: end\n", __func__); + return ret; +} + +/* + * set sampling rate according to the substream + */ +static int snd_ps3_set_avsetting(struct snd_pcm_substream *substream) +{ + struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); + struct snd_ps3_avsetting_info avs; + + avs = card->avs; + + pr_debug("%s: called freq=%d width=%d\n", __func__, + substream->runtime->rate, + snd_pcm_format_width(substream->runtime->format)); + + pr_debug("%s: before freq=%d width=%d\n", __func__, + card->avs.avs_audio_rate, card->avs.avs_audio_width); + + /* sample rate */ + switch (substream->runtime->rate) { + case 44100: + avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_44K; + break; + case 48000: + avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K; + break; + case 88200: + avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_88K; + break; + case 96000: + avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_96K; + break; + default: + pr_info("%s: invalid rate %d\n", __func__, + substream->runtime->rate); + return 1; + } + + /* width */ + switch (snd_pcm_format_width(substream->runtime->format)) { + case 16: + avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16; + break; + case 24: + avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_24; + break; + default: + pr_info("%s: invalid width %d\n", __func__, + snd_pcm_format_width(substream->runtime->format)); + return 1; + } + + if ((card->avs.avs_audio_width != avs.avs_audio_width) || + (card->avs.avs_audio_rate != avs.avs_audio_rate)) { + card->avs = avs; + snd_ps3_change_avsetting(card); + + pr_debug("%s: after freq=%d width=%d\n", __func__, + card->avs.avs_audio_rate, card->avs.avs_audio_width); + + return 0; + } else + return 1; +} + + + +static int snd_ps3_map_mmio(void) +{ + the_card.mapped_mmio_vaddr = + ioremap(the_card.ps3_dev->m_region->bus_addr, + the_card.ps3_dev->m_region->len); + + if (!the_card.mapped_mmio_vaddr) { + pr_info("%s: ioremap 0 failed p=%#lx l=%#lx \n", + __func__, the_card.ps3_dev->m_region->lpar_addr, + the_card.ps3_dev->m_region->len); + return -ENXIO; + } + + return 0; +}; + +static void snd_ps3_unmap_mmio(void) +{ + iounmap(the_card.mapped_mmio_vaddr); + the_card.mapped_mmio_vaddr = NULL; +} + +static int snd_ps3_allocate_irq(void) +{ + int ret; + u64 lpar_addr, lpar_size; + u64 __iomem *mapped; + + /* FIXME: move this to device_init (H/W probe) */ + + /* get irq outlet */ + ret = lv1_gpu_device_map(1, &lpar_addr, &lpar_size); + if (ret) { + pr_info("%s: device map 1 failed %d\n", __func__, + ret); + return -ENXIO; + } + + mapped = ioremap(lpar_addr, lpar_size); + if (!mapped) { + pr_info("%s: ioremap 1 failed \n", __func__); + return -ENXIO; + } + + the_card.audio_irq_outlet = in_be64(mapped); + + iounmap(mapped); + ret = lv1_gpu_device_unmap(1); + if (ret) + pr_info("%s: unmap 1 failed\n", __func__); + + /* irq */ + ret = ps3_irq_plug_setup(PS3_BINDING_CPU_ANY, + the_card.audio_irq_outlet, + &the_card.irq_no); + if (ret) { + pr_info("%s:ps3_alloc_irq failed (%d)\n", __func__, ret); + return ret; + } + + ret = request_irq(the_card.irq_no, snd_ps3_interrupt, IRQF_DISABLED, + SND_PS3_DRIVER_NAME, &the_card); + if (ret) { + pr_info("%s: request_irq failed (%d)\n", __func__, ret); + goto cleanup_irq; + } + + return 0; + + cleanup_irq: + ps3_irq_plug_destroy(the_card.irq_no); + return ret; +}; + +static void snd_ps3_free_irq(void) +{ + free_irq(the_card.irq_no, &the_card); + ps3_irq_plug_destroy(the_card.irq_no); +} + +static void snd_ps3_audio_set_base_addr(uint64_t ioaddr_start) +{ + uint64_t val; + int ret; + + val = (ioaddr_start & (0x0fUL << 32)) >> (32 - 20) | + (0x03UL << 24) | + (0x0fUL << 12) | + (PS3_AUDIO_IOID); + + ret = lv1_gpu_attribute(0x100, 0x007, val, 0, 0); + if (ret) + pr_info("%s: gpu_attribute failed %d\n", __func__, + ret); +} + +static int __init snd_ps3_driver_probe(struct ps3_system_bus_device *dev) +{ + int ret; + u64 lpar_addr, lpar_size; + + BUG_ON(!firmware_has_feature(FW_FEATURE_PS3_LV1)); + BUG_ON(dev->match_id != PS3_MATCH_ID_SOUND); + + the_card.ps3_dev = dev; + + ret = ps3_open_hv_device(dev); + + if (ret) + return -ENXIO; + + /* setup MMIO */ + ret = lv1_gpu_device_map(2, &lpar_addr, &lpar_size); + if (ret) { + pr_info("%s: device map 2 failed %d\n", __func__, ret); + goto clean_open; + } + ps3_mmio_region_init(dev, dev->m_region, lpar_addr, lpar_size, + PAGE_SHIFT); + + ret = snd_ps3_map_mmio(); + if (ret) + goto clean_dev_map; + + /* setup DMA area */ + ps3_dma_region_init(dev, dev->d_region, + PAGE_SHIFT, /* use system page size */ + 0, /* dma type; not used */ + NULL, + _ALIGN_UP(SND_PS3_DMA_REGION_SIZE, PAGE_SIZE)); + dev->d_region->ioid = PS3_AUDIO_IOID; + + ret = ps3_dma_region_create(dev->d_region); + if (ret) { + pr_info("%s: region_create\n", __func__); + goto clean_mmio; + } + + snd_ps3_audio_set_base_addr(dev->d_region->bus_addr); + + /* CONFIG_SND_PS3_DEFAULT_START_DELAY */ + the_card.start_delay = snd_ps3_start_delay; + + /* irq */ + if (snd_ps3_allocate_irq()) { + ret = -ENXIO; + goto clean_dma_region; + } + + /* create card instance */ + the_card.card = snd_card_new(index, id, THIS_MODULE, 0); + if (!the_card.card) { + ret = -ENXIO; + goto clean_irq; + } + + strcpy(the_card.card->driver, "PS3"); + strcpy(the_card.card->shortname, "PS3"); + strcpy(the_card.card->longname, "PS3 sound"); + /* create PCM devices instance */ + /* NOTE:this driver works assuming pcm:substream = 1:1 */ + ret = snd_pcm_new(the_card.card, + "SPDIF", + 0, /* instance index, will be stored pcm.device*/ + 1, /* output substream */ + 0, /* input substream */ + &(the_card.pcm)); + if (ret) + goto clean_card; + + the_card.pcm->private_data = &the_card; + strcpy(the_card.pcm->name, "SPDIF"); + + /* set pcm ops */ + snd_pcm_set_ops(the_card.pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_ps3_pcm_spdif_ops); + + the_card.pcm->info_flags = SNDRV_PCM_INFO_NONINTERLEAVED; + /* pre-alloc PCM DMA buffer*/ + ret = snd_pcm_lib_preallocate_pages_for_all(the_card.pcm, + SNDRV_DMA_TYPE_DEV, + &dev->core, + SND_PS3_PCM_PREALLOC_SIZE, + SND_PS3_PCM_PREALLOC_SIZE); + if (ret < 0) { + pr_info("%s: prealloc failed\n", __func__); + goto clean_card; + } + + /* + * allocate null buffer + * its size should be lager than PS3_AUDIO_FIFO_STAGE_SIZE * 2 + * PAGE_SIZE is enogh + */ + if (!(the_card.null_buffer_start_vaddr = + dma_alloc_coherent(&the_card.ps3_dev->core, + PAGE_SIZE, + &the_card.null_buffer_start_dma_addr, + GFP_KERNEL))) { + pr_info("%s: nullbuffer alloc failed\n", __func__); + goto clean_preallocate; + } + pr_debug("%s: null vaddr=%p dma=%#lx\n", __func__, + the_card.null_buffer_start_vaddr, + the_card.null_buffer_start_dma_addr); + /* set default sample rate/word width */ + snd_ps3_init_avsetting(&the_card); + + /* register the card */ + ret = snd_card_register(the_card.card); + if (ret < 0) + goto clean_dma_map; + + pr_info("%s started. start_delay=%dms\n", + the_card.card->longname, the_card.start_delay); + return 0; + +clean_dma_map: + dma_free_coherent(&the_card.ps3_dev->core, + PAGE_SIZE, + the_card.null_buffer_start_vaddr, + the_card.null_buffer_start_dma_addr); +clean_preallocate: + snd_pcm_lib_preallocate_free_for_all(the_card.pcm); +clean_card: + snd_card_free(the_card.card); +clean_irq: + snd_ps3_free_irq(); +clean_dma_region: + ps3_dma_region_free(dev->d_region); +clean_mmio: + snd_ps3_unmap_mmio(); +clean_dev_map: + lv1_gpu_device_unmap(2); +clean_open: + ps3_close_hv_device(dev); + /* + * there is no destructor function to pcm. + * midlayer automatically releases if the card removed + */ + return ret; +}; /* snd_ps3_probe */ + +/* called when module removal */ +static int snd_ps3_driver_remove(struct ps3_system_bus_device *dev) +{ + int ret; + pr_info("%s:start id=%d\n", __func__, dev->match_id); + if (dev->match_id != PS3_MATCH_ID_SOUND) + return -ENXIO; + + /* + * ctl and preallocate buffer will be freed in + * snd_card_free + */ + ret = snd_card_free(the_card.card); + if (ret) + pr_info("%s: ctl freecard=%d\n", __func__, ret); + + dma_free_coherent(&dev->core, + PAGE_SIZE, + the_card.null_buffer_start_vaddr, + the_card.null_buffer_start_dma_addr); + + ps3_dma_region_free(dev->d_region); + + snd_ps3_free_irq(); + snd_ps3_unmap_mmio(); + + lv1_gpu_device_unmap(2); + ps3_close_hv_device(dev); + pr_info("%s:end id=%d\n", __func__, dev->match_id); + return 0; +} /* snd_ps3_remove */ + +static struct ps3_system_bus_driver snd_ps3_bus_driver_info = { + .match_id = PS3_MATCH_ID_SOUND, + .probe = snd_ps3_driver_probe, + .remove = snd_ps3_driver_remove, + .shutdown = snd_ps3_driver_remove, + .core = { + .name = SND_PS3_DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + + +/* + * Interrupt handler + */ +static irqreturn_t snd_ps3_interrupt(int irq, void *dev_id) +{ + + uint32_t port_intr; + int underflow_occured = 0; + struct snd_ps3_card_info *card = dev_id; + + if (!card->running) { + update_reg(PS3_AUDIO_AX_IS, 0); + update_reg(PS3_AUDIO_INTR_0, 0); + return IRQ_HANDLED; + } + + port_intr = read_reg(PS3_AUDIO_AX_IS); + /* + *serial buffer empty detected (every 4 times), + *program next dma and kick it + */ + if (port_intr & PS3_AUDIO_AX_IE_ASOBEIE(0)) { + write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBEIE(0)); + if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) { + write_reg(PS3_AUDIO_AX_IS, port_intr); + underflow_occured = 1; + } + if (card->silent) { + /* we are still in silent time */ + snd_ps3_program_dma(card, + (underflow_occured) ? + SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL : + SND_PS3_DMA_FILLTYPE_SILENT_RUNNING); + snd_ps3_kick_dma(card); + card->silent --; + } else { + snd_ps3_program_dma(card, + (underflow_occured) ? + SND_PS3_DMA_FILLTYPE_FIRSTFILL : + SND_PS3_DMA_FILLTYPE_RUNNING); + snd_ps3_kick_dma(card); + snd_pcm_period_elapsed(card->substream); + } + } else if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) { + write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBUIE(0)); + /* + * serial out underflow, but buffer empty not detected. + * in this case, fill fifo with 0 to recover. After + * filling dummy data, serial automatically start to + * consume them and then will generate normal buffer + * empty interrupts. + * If both buffer underflow and buffer empty are occured, + * it is better to do nomal data transfer than empty one + */ + snd_ps3_program_dma(card, + SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL); + snd_ps3_kick_dma(card); + snd_ps3_program_dma(card, + SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL); + snd_ps3_kick_dma(card); + } + /* clear interrupt cause */ + return IRQ_HANDLED; +}; + +/* + * module/subsystem initialize/terminate + */ +static int __init snd_ps3_init(void) +{ + int ret; + + if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) + return -ENXIO; + + memset(&the_card, 0, sizeof(the_card)); + spin_lock_init(&the_card.dma_lock); + + /* register systembus DRIVER, this calls our probe() func */ + ret = ps3_system_bus_driver_register(&snd_ps3_bus_driver_info); + + return ret; +} + +static void __exit snd_ps3_exit(void) +{ + ps3_system_bus_driver_unregister(&snd_ps3_bus_driver_info); +} + +MODULE_ALIAS(PS3_MODULE_ALIAS_SOUND); diff -Nur sound/ppc/snd_ps3.h sound/ppc/snd_ps3.h --- sound/ppc/snd_ps3.h 1970-01-01 01:00:00.000000000 +0100 +++ sound/ppc/snd_ps3.h 2007-07-04 02:00:07.000000000 +0200 @@ -0,0 +1,135 @@ +/* + * Audio support for PS3 + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * All rights reserved. + * Copyright 2006, 2007 Sony Corporation + * + * 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 Licence. + * + * 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 + */ + +#if !defined(_SND_PS3_H_) +#define _SND_PS3_H_ + +#include + +#define SND_PS3_DRIVER_NAME "snd_ps3" + +enum snd_ps3_out_channel { + SND_PS3_OUT_SPDIF_0, + SND_PS3_OUT_SPDIF_1, + SND_PS3_OUT_SERIAL_0, + SND_PS3_OUT_DEVS +}; + +enum snd_ps3_dma_filltype { + SND_PS3_DMA_FILLTYPE_FIRSTFILL, + SND_PS3_DMA_FILLTYPE_RUNNING, + SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL, + SND_PS3_DMA_FILLTYPE_SILENT_RUNNING +}; + +enum snd_ps3_ch { + SND_PS3_CH_L = 0, + SND_PS3_CH_R = 1, + SND_PS3_CH_MAX = 2 +}; + +struct snd_ps3_avsetting_info { + uint32_t avs_audio_ch; /* fixed */ + uint32_t avs_audio_rate; + uint32_t avs_audio_width; + uint32_t avs_audio_format; /* fixed */ + uint32_t avs_audio_source; /* fixed */ +}; +/* + * PS3 audio 'card' instance + * there should be only ONE hardware. + */ +struct snd_ps3_card_info { + struct ps3_system_bus_device *ps3_dev; + struct snd_card *card; + + struct snd_pcm *pcm; + struct snd_pcm_substream *substream; + + /* hvc info */ + u64 audio_lpar_addr; + u64 audio_lpar_size; + + /* registers */ + void __iomem *mapped_mmio_vaddr; + + /* irq */ + u64 audio_irq_outlet; + unsigned int irq_no; + + /* remember avsetting */ + struct snd_ps3_avsetting_info avs; + + /* dma buffer management */ + spinlock_t dma_lock; + /* dma_lock start */ + void * dma_start_vaddr[2]; /* 0 for L, 1 for R */ + dma_addr_t dma_start_bus_addr[2]; + size_t dma_buffer_size; + void * dma_last_transfer_vaddr[2]; + void * dma_next_transfer_vaddr[2]; + int silent; + /* dma_lock end */ + + int running; + + /* null buffer */ + void *null_buffer_start_vaddr; + dma_addr_t null_buffer_start_dma_addr; + + /* start delay */ + unsigned int start_delay; + +}; + + +/* PS3 audio DMAC block size in bytes */ +#define PS3_AUDIO_DMAC_BLOCK_SIZE (128) +/* one stage (stereo) of audio FIFO in bytes */ +#define PS3_AUDIO_FIFO_STAGE_SIZE (256) +/* how many stages the fifo have */ +#define PS3_AUDIO_FIFO_STAGE_COUNT (8) +/* fifo size 128 bytes * 8 stages * stereo (2ch) */ +#define PS3_AUDIO_FIFO_SIZE \ + (PS3_AUDIO_FIFO_STAGE_SIZE * PS3_AUDIO_FIFO_STAGE_COUNT) + +/* PS3 audio DMAC max block count in one dma shot = 128 (0x80) blocks*/ +#define PS3_AUDIO_DMAC_MAX_BLOCKS (PS3_AUDIO_DMASIZE_BLOCKS_MASK + 1) + +#define PS3_AUDIO_NORMAL_DMA_START_CH (0) +#define PS3_AUDIO_NORMAL_DMA_COUNT (8) +#define PS3_AUDIO_NULL_DMA_START_CH \ + (PS3_AUDIO_NORMAL_DMA_START_CH + PS3_AUDIO_NORMAL_DMA_COUNT) +#define PS3_AUDIO_NULL_DMA_COUNT (2) + +#define SND_PS3_MAX_VOL (0x0F) +#define SND_PS3_MIN_VOL (0x00) +#define SND_PS3_MIN_ATT SND_PS3_MIN_VOL +#define SND_PS3_MAX_ATT SND_PS3_MAX_VOL + +#define SND_PS3_PCM_PREALLOC_SIZE \ + (PS3_AUDIO_DMAC_BLOCK_SIZE * PS3_AUDIO_DMAC_MAX_BLOCKS * 4) + +#define SND_PS3_DMA_REGION_SIZE \ + (SND_PS3_PCM_PREALLOC_SIZE + PAGE_SIZE) + +#define PS3_AUDIO_IOID (1UL) + +#endif /* _SND_PS3_H_ */ diff -Nur sound/ppc/snd_ps3_reg.h sound/ppc/snd_ps3_reg.h --- sound/ppc/snd_ps3_reg.h 1970-01-01 01:00:00.000000000 +0100 +++ sound/ppc/snd_ps3_reg.h 2007-07-04 02:00:07.000000000 +0200 @@ -0,0 +1,891 @@ +/* + * Audio support for PS3 + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2006, 2007 Sony Corporation + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 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 + */ + +/* + * interrupt / configure registers + */ + +#define PS3_AUDIO_INTR_0 (0x00000100) +#define PS3_AUDIO_INTR_EN_0 (0x00000140) +#define PS3_AUDIO_CONFIG (0x00000200) + +/* + * DMAC registers + * n:0..9 + */ +#define PS3_AUDIO_DMAC_REGBASE(x) (0x0000210 + 0x20 * (x)) + +#define PS3_AUDIO_KICK(n) (PS3_AUDIO_DMAC_REGBASE(n) + 0x00) +#define PS3_AUDIO_SOURCE(n) (PS3_AUDIO_DMAC_REGBASE(n) + 0x04) +#define PS3_AUDIO_DEST(n) (PS3_AUDIO_DMAC_REGBASE(n) + 0x08) +#define PS3_AUDIO_DMASIZE(n) (PS3_AUDIO_DMAC_REGBASE(n) + 0x0C) + +/* + * mute control + */ +#define PS3_AUDIO_AX_MCTRL (0x00004000) +#define PS3_AUDIO_AX_ISBP (0x00004004) +#define PS3_AUDIO_AX_AOBP (0x00004008) +#define PS3_AUDIO_AX_IC (0x00004010) +#define PS3_AUDIO_AX_IE (0x00004014) +#define PS3_AUDIO_AX_IS (0x00004018) + +/* + * three wire serial + * n:0..3 + */ +#define PS3_AUDIO_AO_MCTRL (0x00006000) +#define PS3_AUDIO_AO_3WMCTRL (0x00006004) + +#define PS3_AUDIO_AO_3WCTRL(n) (0x00006200 + 0x200 * (n)) + +/* + * S/PDIF + * n:0..1 + * x:0..11 + * y:0..5 + */ +#define PS3_AUDIO_AO_SPD_REGBASE(n) (0x00007200 + 0x200 * (n)) + +#define PS3_AUDIO_AO_SPDCTRL(n) \ + (PS3_AUDIO_AO_SPD_REGBASE(n) + 0x00) +#define PS3_AUDIO_AO_SPDUB(n, x) \ + (PS3_AUDIO_AO_SPD_REGBASE(n) + 0x04 + 0x04 * (x)) +#define PS3_AUDIO_AO_SPDCS(n, y) \ + (PS3_AUDIO_AO_SPD_REGBASE(n) + 0x34 + 0x04 * (y)) + + +/* + PS3_AUDIO_INTR_0 register tells an interrupt handler which audio + DMA channel triggered the interrupt. The interrupt status for a channel + can be cleared by writing a '1' to the corresponding bit. A new interrupt + cannot be generated until the previous interrupt has been cleared. + + Note that the status reported by PS3_AUDIO_INTR_0 is independent of the + value of PS3_AUDIO_INTR_EN_0. + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0 0 0 0 0 0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C| INTR_0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ +#define PS3_AUDIO_INTR_0_CHAN(n) (1 << ((n) * 2)) +#define PS3_AUDIO_INTR_0_CHAN9 PS3_AUDIO_INTR_0_CHAN(9) +#define PS3_AUDIO_INTR_0_CHAN8 PS3_AUDIO_INTR_0_CHAN(8) +#define PS3_AUDIO_INTR_0_CHAN7 PS3_AUDIO_INTR_0_CHAN(7) +#define PS3_AUDIO_INTR_0_CHAN6 PS3_AUDIO_INTR_0_CHAN(6) +#define PS3_AUDIO_INTR_0_CHAN5 PS3_AUDIO_INTR_0_CHAN(5) +#define PS3_AUDIO_INTR_0_CHAN4 PS3_AUDIO_INTR_0_CHAN(4) +#define PS3_AUDIO_INTR_0_CHAN3 PS3_AUDIO_INTR_0_CHAN(3) +#define PS3_AUDIO_INTR_0_CHAN2 PS3_AUDIO_INTR_0_CHAN(2) +#define PS3_AUDIO_INTR_0_CHAN1 PS3_AUDIO_INTR_0_CHAN(1) +#define PS3_AUDIO_INTR_0_CHAN0 PS3_AUDIO_INTR_0_CHAN(0) + +/* + The PS3_AUDIO_INTR_EN_0 register specifies which DMA channels can generate + an interrupt to the PU. Each bit of PS3_AUDIO_INTR_EN_0 is ANDed with the + corresponding bit in PS3_AUDIO_INTR_0. The resulting bits are OR'd together + to generate the Audio interrupt. + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0 0 0 0 0 0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C| INTR_EN_0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + + Bit assignments are same as PS3_AUDIO_INTR_0 +*/ + +/* + PS3_AUDIO_CONFIG + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 C|0 0 0 0 0 0 0 0| CONFIG + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + +*/ + +/* The CLEAR field cancels all pending transfers, and stops any running DMA + transfers. Any interrupts associated with the canceled transfers + will occur as if the transfer had finished. + Since this bit is designed to recover from DMA related issues + which are caused by unpredictable situations, it is prefered to wait + for normal DMA transfer end without using this bit. +*/ +#define PS3_AUDIO_CONFIG_CLEAR (1 << 8) /* RWIVF */ + +/* + PS3_AUDIO_AX_MCTRL: Audio Port Mute Control Register + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|A|A|A|0 0 0 0 0 0 0|S|S|A|A|A|A| AX_MCTRL + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ + +/* 3 Wire Audio Serial Output Channel Mutes (0..3) */ +#define PS3_AUDIO_AX_MCTRL_ASOMT(n) (1 << (3 - (n))) /* RWIVF */ +#define PS3_AUDIO_AX_MCTRL_ASO3MT (1 << 0) /* RWIVF */ +#define PS3_AUDIO_AX_MCTRL_ASO2MT (1 << 1) /* RWIVF */ +#define PS3_AUDIO_AX_MCTRL_ASO1MT (1 << 2) /* RWIVF */ +#define PS3_AUDIO_AX_MCTRL_ASO0MT (1 << 3) /* RWIVF */ + +/* S/PDIF mutes (0,1)*/ +#define PS3_AUDIO_AX_MCTRL_SPOMT(n) (1 << (5 - (n))) /* RWIVF */ +#define PS3_AUDIO_AX_MCTRL_SPO1MT (1 << 4) /* RWIVF */ +#define PS3_AUDIO_AX_MCTRL_SPO0MT (1 << 5) /* RWIVF */ + +/* All 3 Wire Serial Outputs Mute */ +#define PS3_AUDIO_AX_MCTRL_AASOMT (1 << 13) /* RWIVF */ + +/* All S/PDIF Mute */ +#define PS3_AUDIO_AX_MCTRL_ASPOMT (1 << 14) /* RWIVF */ + +/* All Audio Outputs Mute */ +#define PS3_AUDIO_AX_MCTRL_AAOMT (1 << 15) /* RWIVF */ + +/* + S/PDIF Outputs Buffer Read/Write Pointer Register + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0|0|SPO0B|0|SPO1B|0 0 0 0 0 0 0 0|0|SPO0B|0|SPO1B| AX_ISBP + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + +*/ +/* + S/PDIF Output Channel Read Buffer Numbers + Buffer number is value of field. + Indicates current read access buffer ID from Audio Data + Transfer controller of S/PDIF Output +*/ + +#define PS3_AUDIO_AX_ISBP_SPOBRN_MASK(n) (0x7 << 4 * (1 - (n))) /* R-IUF */ +#define PS3_AUDIO_AX_ISBP_SPO1BRN_MASK (0x7 << 0) /* R-IUF */ +#define PS3_AUDIO_AX_ISBP_SPO0BRN_MASK (0x7 << 4) /* R-IUF */ + +/* +S/PDIF Output Channel Buffer Write Numbers +Indicates current write access buffer ID from bus master. +*/ +#define PS3_AUDIO_AX_ISBP_SPOBWN_MASK(n) (0x7 << 4 * (5 - (n))) /* R-IUF */ +#define PS3_AUDIO_AX_ISBP_SPO1BWN_MASK (0x7 << 16) /* R-IUF */ +#define PS3_AUDIO_AX_ISBP_SPO0BWN_MASK (0x7 << 20) /* R-IUF */ + +/* + 3 Wire Audio Serial Outputs Buffer Read/Write + Pointer Register + Buffer number is value of field + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0|ASO0B|0|ASO1B|0|ASO2B|0|ASO3B|0|ASO0B|0|ASO1B|0|ASO2B|0|ASO3B| AX_AOBP + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ + +/* +3 Wire Audio Serial Output Channel Buffer Read Numbers +Indicates current read access buffer Id from Audio Data Transfer +Controller of 3 Wire Audio Serial Output Channels +*/ +#define PS3_AUDIO_AX_AOBP_ASOBRN_MASK(n) (0x7 << 4 * (3 - (n))) /* R-IUF */ + +#define PS3_AUDIO_AX_AOBP_ASO3BRN_MASK (0x7 << 0) /* R-IUF */ +#define PS3_AUDIO_AX_AOBP_ASO2BRN_MASK (0x7 << 4) /* R-IUF */ +#define PS3_AUDIO_AX_AOBP_ASO1BRN_MASK (0x7 << 8) /* R-IUF */ +#define PS3_AUDIO_AX_AOBP_ASO0BRN_MASK (0x7 << 12) /* R-IUF */ + +/* +3 Wire Audio Serial Output Channel Buffer Write Numbers +Indicates current write access buffer ID from bus master. +*/ +#define PS3_AUDIO_AX_AOBP_ASOBWN_MASK(n) (0x7 << 4 * (7 - (n))) /* R-IUF */ + +#define PS3_AUDIO_AX_AOBP_ASO3BWN_MASK (0x7 << 16) /* R-IUF */ +#define PS3_AUDIO_AX_AOBP_ASO2BWN_MASK (0x7 << 20) /* R-IUF */ +#define PS3_AUDIO_AX_AOBP_ASO1BWN_MASK (0x7 << 24) /* R-IUF */ +#define PS3_AUDIO_AX_AOBP_ASO0BWN_MASK (0x7 << 28) /* R-IUF */ + + + +/* +Audio Port Interrupt Condition Register +For the fields in this register, the following values apply: +0 = Interrupt is generated every interrupt event. +1 = Interrupt is generated every 2 interrupt events. +2 = Interrupt is generated every 4 interrupt events. +3 = Reserved + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0|0 0|SPO|0 0|SPO|0 0|AAS|0 0 0 0 0 0 0 0 0 0 0 0| AX_IC + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ +/* +All 3-Wire Audio Serial Outputs Interrupt Mode +Configures the Interrupt and Signal Notification +condition of all 3-wire Audio Serial Outputs. +*/ +#define PS3_AUDIO_AX_IC_AASOIMD_MASK (0x3 << 12) /* RWIVF */ +#define PS3_AUDIO_AX_IC_AASOIMD_EVERY1 (0x0 << 12) /* RWI-V */ +#define PS3_AUDIO_AX_IC_AASOIMD_EVERY2 (0x1 << 12) /* RW--V */ +#define PS3_AUDIO_AX_IC_AASOIMD_EVERY4 (0x2 << 12) /* RW--V */ + +/* +S/PDIF Output Channel Interrupt Modes +Configures the Interrupt and signal Notification +conditions of S/PDIF output channels. +*/ +#define PS3_AUDIO_AX_IC_SPO1IMD_MASK (0x3 << 16) /* RWIVF */ +#define PS3_AUDIO_AX_IC_SPO1IMD_EVERY1 (0x0 << 16) /* RWI-V */ +#define PS3_AUDIO_AX_IC_SPO1IMD_EVERY2 (0x1 << 16) /* RW--V */ +#define PS3_AUDIO_AX_IC_SPO1IMD_EVERY4 (0x2 << 16) /* RW--V */ + +#define PS3_AUDIO_AX_IC_SPO0IMD_MASK (0x3 << 20) /* RWIVF */ +#define PS3_AUDIO_AX_IC_SPO0IMD_EVERY1 (0x0 << 20) /* RWI-V */ +#define PS3_AUDIO_AX_IC_SPO0IMD_EVERY2 (0x1 << 20) /* RW--V */ +#define PS3_AUDIO_AX_IC_SPO0IMD_EVERY4 (0x2 << 20) /* RW--V */ + +/* +Audio Port interrupt Enable Register +Configures whether to enable or disable each Interrupt Generation. + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0|S|S|0 0|A|A|A|A|0 0 0 0|S|S|0 0|S|S|0 0|A|A|A|A| AX_IE + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + +*/ + +/* +3 Wire Audio Serial Output Channel Buffer Underflow +Interrupt Enables +Select enable/disable of Buffer Underflow Interrupts for +3-Wire Audio Serial Output Channels +DISABLED=Interrupt generation disabled. +*/ +#define PS3_AUDIO_AX_IE_ASOBUIE(n) (1 << (3 - (n))) /* RWIVF */ +#define PS3_AUDIO_AX_IE_ASO3BUIE (1 << 0) /* RWIVF */ +#define PS3_AUDIO_AX_IE_ASO2BUIE (1 << 1) /* RWIVF */ +#define PS3_AUDIO_AX_IE_ASO1BUIE (1 << 2) /* RWIVF */ +#define PS3_AUDIO_AX_IE_ASO0BUIE (1 << 3) /* RWIVF */ + +/* S/PDIF Output Channel Buffer Underflow Interrupt Enables */ + +#define PS3_AUDIO_AX_IE_SPOBUIE(n) (1 << (7 - (n))) /* RWIVF */ +#define PS3_AUDIO_AX_IE_SPO1BUIE (1 << 6) /* RWIVF */ +#define PS3_AUDIO_AX_IE_SPO0BUIE (1 << 7) /* RWIVF */ + +/* S/PDIF Output Channel One Block Transfer Completion Interrupt Enables */ + +#define PS3_AUDIO_AX_IE_SPOBTCIE(n) (1 << (11 - (n))) /* RWIVF */ +#define PS3_AUDIO_AX_IE_SPO1BTCIE (1 << 10) /* RWIVF */ +#define PS3_AUDIO_AX_IE_SPO0BTCIE (1 << 11) /* RWIVF */ + +/* 3-Wire Audio Serial Output Channel Buffer Empty Interrupt Enables */ + +#define PS3_AUDIO_AX_IE_ASOBEIE(n) (1 << (19 - (n))) /* RWIVF */ +#define PS3_AUDIO_AX_IE_ASO3BEIE (1 << 16) /* RWIVF */ +#define PS3_AUDIO_AX_IE_ASO2BEIE (1 << 17) /* RWIVF */ +#define PS3_AUDIO_AX_IE_ASO1BEIE (1 << 18) /* RWIVF */ +#define PS3_AUDIO_AX_IE_ASO0BEIE (1 << 19) /* RWIVF */ + +/* S/PDIF Output Channel Buffer Empty Interrupt Enables */ + +#define PS3_AUDIO_AX_IE_SPOBEIE(n) (1 << (23 - (n))) /* RWIVF */ +#define PS3_AUDIO_AX_IE_SPO1BEIE (1 << 22) /* RWIVF */ +#define PS3_AUDIO_AX_IE_SPO0BEIE (1 << 23) /* RWIVF */ + +/* +Audio Port Interrupt Status Register +Indicates Interrupt status, which interrupt has occured, and can clear +each interrupt in this register. +Writing 1b to a field containing 1b clears field and de-asserts interrupt. +Writing 0b to a field has no effect. +Field vaules are the following: +0 - Interrupt hasn't occured. +1 - Interrupt has occured. + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0|S|S|0 0|A|A|A|A|0 0 0 0|S|S|0 0|S|S|0 0|A|A|A|A| AX_IS + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + + Bit assignment are same as AX_IE +*/ + +/* +Audio Output Master Control Register +Configures Master Clock and other master Audio Output Settings + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0|SCKSE|0|SCKSE| MR0 | MR1 |MCL|MCL|0 0 0 0|0 0 0 0 0 0 0 0| AO_MCTRL + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ + +/* +MCLK Output Control +Controls mclko[1] output. +0 - Disable output (fixed at High) +1 - Output clock produced by clock selected +with scksel1 by mr1 +2 - Reserved +3 - Reserved +*/ + +#define PS3_AUDIO_AO_MCTRL_MCLKC1_MASK (0x3 << 12) /* RWIVF */ +#define PS3_AUDIO_AO_MCTRL_MCLKC1_DISABLED (0x0 << 12) /* RWI-V */ +#define PS3_AUDIO_AO_MCTRL_MCLKC1_ENABLED (0x1 << 12) /* RW--V */ +#define PS3_AUDIO_AO_MCTRL_MCLKC1_RESVD2 (0x2 << 12) /* RW--V */ +#define PS3_AUDIO_AO_MCTRL_MCLKC1_RESVD3 (0x3 << 12) /* RW--V */ + +/* +MCLK Output Control +Controls mclko[0] output. +0 - Disable output (fixed at High) +1 - Output clock produced by clock selected +with SCKSEL0 by MR0 +2 - Reserved +3 - Reserved +*/ +#define PS3_AUDIO_AO_MCTRL_MCLKC0_MASK (0x3 << 14) /* RWIVF */ +#define PS3_AUDIO_AO_MCTRL_MCLKC0_DISABLED (0x0 << 14) /* RWI-V */ +#define PS3_AUDIO_AO_MCTRL_MCLKC0_ENABLED (0x1 << 14) /* RW--V */ +#define PS3_AUDIO_AO_MCTRL_MCLKC0_RESVD2 (0x2 << 14) /* RW--V */ +#define PS3_AUDIO_AO_MCTRL_MCLKC0_RESVD3 (0x3 << 14) /* RW--V */ +/* +Master Clock Rate 1 +Sets the divide ration of Master Clock1 (clock output from +mclko[1] for the input clock selected by scksel1. +*/ +#define PS3_AUDIO_AO_MCTRL_MR1_MASK (0xf << 16) +#define PS3_AUDIO_AO_MCTRL_MR1_DEFAULT (0x0 << 16) /* RWI-V */ +/* +Master Clock Rate 0 +Sets the divide ratio of Master Clock0 (clock output from +mclko[0] for the input clock selected by scksel0). +*/ +#define PS3_AUDIO_AO_MCTRL_MR0_MASK (0xf << 20) /* RWIVF */ +#define PS3_AUDIO_AO_MCTRL_MR0_DEFAULT (0x0 << 20) /* RWI-V */ +/* +System Clock Select 0/1 +Selects the system clock to be used as Master Clock 0/1 +Input the system clock that is appropriate for the sampling +rate. +*/ +#define PS3_AUDIO_AO_MCTRL_SCKSEL1_MASK (0x7 << 24) /* RWIVF */ +#define PS3_AUDIO_AO_MCTRL_SCKSEL1_DEFAULT (0x2 << 24) /* RWI-V */ + +#define PS3_AUDIO_AO_MCTRL_SCKSEL0_MASK (0x7 << 28) /* RWIVF */ +#define PS3_AUDIO_AO_MCTRL_SCKSEL0_DEFAULT (0x2 << 28) /* RWI-V */ + + +/* +3-Wire Audio Output Master Control Register +Configures clock, 3-Wire Audio Serial Output Enable, and +other 3-Wire Audio Serial Output Master Settings + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |A|A|A|A|0 0 0|A| ASOSR |0 0 0 0|A|A|A|A|A|A|0|1|0 0 0 0 0 0 0 0| AO_3WMCTRL + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ + + +/* +LRCKO Polarity +0 - Reserved +1 - default +*/ +#define PS3_AUDIO_AO_3WMCTRL_ASOPLRCK (1 << 8) /* RWIVF */ +#define PS3_AUDIO_AO_3WMCTRL_ASOPLRCK_DEFAULT (1 << 8) /* RW--V */ + +/* LRCK Output Disable */ + +#define PS3_AUDIO_AO_3WMCTRL_ASOLRCKD (1 << 10) /* RWIVF */ +#define PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_ENABLED (0 << 10) /* RW--V */ +#define PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_DISABLED (1 << 10) /* RWI-V */ + +/* Bit Clock Output Disable */ + +#define PS3_AUDIO_AO_3WMCTRL_ASOBCLKD (1 << 11) /* RWIVF */ +#define PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_ENABLED (0 << 11) /* RW--V */ +#define PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_DISABLED (1 << 11) /* RWI-V */ + +/* +3-Wire Audio Serial Output Channel 0-3 Operational +Status. Each bit becomes 1 after each 3-Wire Audio +Serial Output Channel N is in action by setting 1 to +asoen. +Each bit becomes 0 after each 3-Wire Audio Serial Output +Channel N is out of action by setting 0 to asoen. +*/ +#define PS3_AUDIO_AO_3WMCTRL_ASORUN(n) (1 << (15 - (n))) /* R-IVF */ +#define PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(n) (0 << (15 - (n))) /* R-I-V */ +#define PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(n) (1 << (15 - (n))) /* R---V */ +#define PS3_AUDIO_AO_3WMCTRL_ASORUN0 \ + PS3_AUDIO_AO_3WMCTRL_ASORUN(0) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN0_STOPPED \ + PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(0) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN0_RUNNING \ + PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(0) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN1 \ + PS3_AUDIO_AO_3WMCTRL_ASORUN(1) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN1_STOPPED \ + PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(1) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN1_RUNNING \ + PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(1) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN2 \ + PS3_AUDIO_AO_3WMCTRL_ASORUN(2) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN2_STOPPED \ + PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(2) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN2_RUNNING \ + PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(2) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN3 \ + PS3_AUDIO_AO_3WMCTRL_ASORUN(3) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN3_STOPPED \ + PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(3) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN3_RUNNING \ + PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(3) + +/* +Sampling Rate +Specifies the divide ratio of the bit clock (clock output +from bclko) used by the 3-wire Audio Output Clock, whcih +is applied to the master clock selected by mcksel. +Data output is synchronized with this clock. +*/ +#define PS3_AUDIO_AO_3WMCTRL_ASOSR_MASK (0xf << 20) /* RWIVF */ +#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV2 (0x1 << 20) /* RWI-V */ +#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV4 (0x2 << 20) /* RW--V */ +#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV8 (0x4 << 20) /* RW--V */ +#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV12 (0x6 << 20) /* RW--V */ + +/* +Master Clock Select +0 - Master Clock 0 +1 - Master Clock 1 +*/ +#define PS3_AUDIO_AO_3WMCTRL_ASOMCKSEL (1 << 24) /* RWIVF */ +#define PS3_AUDIO_AO_3WMCTRL_ASOMCKSEL_CLK0 (0 << 24) /* RWI-V */ +#define PS3_AUDIO_AO_3WMCTRL_ASOMCKSEL_CLK1 (1 << 24) /* RW--V */ + +/* +Enables and disables 4ch 3-Wire Audio Serial Output +operation. Each Bit from 0 to 3 corresponds to an +output channel, which means that each output channel +can be enabled or disabled individually. When +multiple channels are enabled at the same time, output +operations are performed in synchronization. +Bit 0 - Output Channel 0 (SDOUT[0]) +Bit 1 - Output Channel 1 (SDOUT[1]) +Bit 2 - Output Channel 2 (SDOUT[2]) +Bit 3 - Output Channel 3 (SDOUT[3]) +*/ +#define PS3_AUDIO_AO_3WMCTRL_ASOEN(n) (1 << (31 - (n))) /* RWIVF */ +#define PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(n) (0 << (31 - (n))) /* RWI-V */ +#define PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(n) (1 << (31 - (n))) /* RW--V */ + +#define PS3_AUDIO_AO_3WMCTRL_ASOEN0 \ + PS3_AUDIO_AO_3WMCTRL_ASOEN(0) /* RWIVF */ +#define PS3_AUDIO_AO_3WMCTRL_ASOEN0_DISABLED \ + PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(0) /* RWI-V */ +#define PS3_AUDIO_AO_3WMCTRL_ASOEN0_ENABLED \ + PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(0) /* RW--V */ +#define PS3_AUDIO_A1_3WMCTRL_ASOEN0 \ + PS3_AUDIO_AO_3WMCTRL_ASOEN(1) /* RWIVF */ +#define PS3_AUDIO_A1_3WMCTRL_ASOEN0_DISABLED \ + PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(1) /* RWI-V */ +#define PS3_AUDIO_A1_3WMCTRL_ASOEN0_ENABLED \ + PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(1) /* RW--V */ +#define PS3_AUDIO_A2_3WMCTRL_ASOEN0 \ + PS3_AUDIO_AO_3WMCTRL_ASOEN(2) /* RWIVF */ +#define PS3_AUDIO_A2_3WMCTRL_ASOEN0_DISABLED \ + PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(2) /* RWI-V */ +#define PS3_AUDIO_A2_3WMCTRL_ASOEN0_ENABLED \ + PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(2) /* RW--V */ +#define PS3_AUDIO_A3_3WMCTRL_ASOEN0 \ + PS3_AUDIO_AO_3WMCTRL_ASOEN(3) /* RWIVF */ +#define PS3_AUDIO_A3_3WMCTRL_ASOEN0_DISABLED \ + PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(3) /* RWI-V */ +#define PS3_AUDIO_A3_3WMCTRL_ASOEN0_ENABLED \ + PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(3) /* RW--V */ + +/* +3-Wire Audio Serial output Channel 0-3 Control Register +Configures settings for 3-Wire Serial Audio Output Channel 0-3 + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|A|0 0 0 0|A|0|ASO|0 0 0|0|0|0|0|0| AO_3WCTRL + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + +*/ +/* +Data Bit Mode +Specifies the number of data bits +0 - 16 bits +1 - reserved +2 - 20 bits +3 - 24 bits +*/ +#define PS3_AUDIO_AO_3WCTRL_ASODB_MASK (0x3 << 8) /* RWIVF */ +#define PS3_AUDIO_AO_3WCTRL_ASODB_16BIT (0x0 << 8) /* RWI-V */ +#define PS3_AUDIO_AO_3WCTRL_ASODB_RESVD (0x1 << 8) /* RWI-V */ +#define PS3_AUDIO_AO_3WCTRL_ASODB_20BIT (0x2 << 8) /* RW--V */ +#define PS3_AUDIO_AO_3WCTRL_ASODB_24BIT (0x3 << 8) /* RW--V */ +/* +Data Format Mode +Specifies the data format where (LSB side or MSB) the data(in 20 bit +or 24 bit resolution mode) is put in a 32 bit field. +0 - Data put on LSB side +1 - Data put on MSB side +*/ +#define PS3_AUDIO_AO_3WCTRL_ASODF (1 << 11) /* RWIVF */ +#define PS3_AUDIO_AO_3WCTRL_ASODF_LSB (0 << 11) /* RWI-V */ +#define PS3_AUDIO_AO_3WCTRL_ASODF_MSB (1 << 11) /* RW--V */ +/* +Buffer Reset +Performs buffer reset. Writing 1 to this bit initializes the +corresponding 3-Wire Audio Output buffers(both L and R). +*/ +#define PS3_AUDIO_AO_3WCTRL_ASOBRST (1 << 16) /* CWIVF */ +#define PS3_AUDIO_AO_3WCTRL_ASOBRST_IDLE (0 << 16) /* -WI-V */ +#define PS3_AUDIO_AO_3WCTRL_ASOBRST_RESET (1 << 16) /* -W--T */ + +/* +S/PDIF Audio Output Channel 0/1 Control Register +Configures settings for S/PDIF Audio Output Channel 0/1. + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |S|0 0 0|S|0 0|S| SPOSR |0 0|SPO|0 0 0 0|S|0|SPO|0 0 0 0 0 0 0|S| AO_SPDCTRL + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ +/* +Buffer reset. Writing 1 to this bit initializes the +corresponding S/PDIF output buffer pointer. +*/ +#define PS3_AUDIO_AO_SPDCTRL_SPOBRST (1 << 0) /* CWIVF */ +#define PS3_AUDIO_AO_SPDCTRL_SPOBRST_IDLE (0 << 0) /* -WI-V */ +#define PS3_AUDIO_AO_SPDCTRL_SPOBRST_RESET (1 << 0) /* -W--T */ + +/* +Data Bit Mode +Specifies number of data bits +0 - 16 bits +1 - Reserved +2 - 20 bits +3 - 24 bits +*/ +#define PS3_AUDIO_AO_SPDCTRL_SPODB_MASK (0x3 << 8) /* RWIVF */ +#define PS3_AUDIO_AO_SPDCTRL_SPODB_16BIT (0x0 << 8) /* RWI-V */ +#define PS3_AUDIO_AO_SPDCTRL_SPODB_RESVD (0x1 << 8) /* RW--V */ +#define PS3_AUDIO_AO_SPDCTRL_SPODB_20BIT (0x2 << 8) /* RW--V */ +#define PS3_AUDIO_AO_SPDCTRL_SPODB_24BIT (0x3 << 8) /* RW--V */ +/* +Data format Mode +Specifies the data format, where (LSB side or MSB) +the data(in 20 or 24 bit resolution) is put in the +32 bit field. +0 - LSB Side +1 - MSB Side +*/ +#define PS3_AUDIO_AO_SPDCTRL_SPODF (1 << 11) /* RWIVF */ +#define PS3_AUDIO_AO_SPDCTRL_SPODF_LSB (0 << 11) /* RWI-V */ +#define PS3_AUDIO_AO_SPDCTRL_SPODF_MSB (1 << 11) /* RW--V */ +/* +Source Select +Specifies the source of the S/PDIF output. When 0, output +operation is controlled by 3wen[0] of AO_3WMCTRL register. +The SR must have the same setting as the a0_3wmctrl reg. +0 - 3-Wire Audio OUT Ch0 Buffer +1 - S/PDIF buffer +*/ +#define PS3_AUDIO_AO_SPDCTRL_SPOSS_MASK (0x3 << 16) /* RWIVF */ +#define PS3_AUDIO_AO_SPDCTRL_SPOSS_3WEN (0x0 << 16) /* RWI-V */ +#define PS3_AUDIO_AO_SPDCTRL_SPOSS_SPDIF (0x1 << 16) /* RW--V */ +/* +Sampling Rate +Specifies the divide ratio of the bit clock (clock output +from bclko) used by the S/PDIF Output Clock, which +is applied to the master clock selected by mcksel. +*/ +#define PS3_AUDIO_AO_SPDCTRL_SPOSR (0xf << 20) /* RWIVF */ +#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV2 (0x1 << 20) /* RWI-V */ +#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV4 (0x2 << 20) /* RW--V */ +#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV8 (0x4 << 20) /* RW--V */ +#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV12 (0x6 << 20) /* RW--V */ +/* +Master Clock Select +0 - Master Clock 0 +1 - Master Clock 1 +*/ +#define PS3_AUDIO_AO_SPDCTRL_SPOMCKSEL (1 << 24) /* RWIVF */ +#define PS3_AUDIO_AO_SPDCTRL_SPOMCKSEL_CLK0 (0 << 24) /* RWI-V */ +#define PS3_AUDIO_AO_SPDCTRL_SPOMCKSEL_CLK1 (1 << 24) /* RW--V */ + +/* +S/PDIF Output Channel Operational Status +This bit becomes 1 after S/PDIF Output Channel is in +action by setting 1 to spoen. This bit becomes 0 +after S/PDIF Output Channel is out of action by setting +0 to spoen. +*/ +#define PS3_AUDIO_AO_SPDCTRL_SPORUN (1 << 27) /* R-IVF */ +#define PS3_AUDIO_AO_SPDCTRL_SPORUN_STOPPED (0 << 27) /* R-I-V */ +#define PS3_AUDIO_AO_SPDCTRL_SPORUN_RUNNING (1 << 27) /* R---V */ + +/* +S/PDIF Audio Output Channel Output Enable +Enables and disables output operation. This bit is used +only when sposs = 1 +*/ +#define PS3_AUDIO_AO_SPDCTRL_SPOEN (1 << 31) /* RWIVF */ +#define PS3_AUDIO_AO_SPDCTRL_SPOEN_DISABLED (0 << 31) /* RWI-V */ +#define PS3_AUDIO_AO_SPDCTRL_SPOEN_ENABLED (1 << 31) /* RW--V */ + +/* +S/PDIF Audio Output Channel Channel Status +Setting Registers. +Configures channel status bit settings for each block +(192 bits). +Output is performed from the MSB(AO_SPDCS0 register bit 31). +The same value is added for subframes within the same frame. + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + | SPOCS | AO_SPDCS + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + +S/PDIF Audio Output Channel User Bit Setting +Configures user bit settings for each block (384 bits). +Output is performed from the MSB(ao_spdub0 register bit 31). + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + | SPOUB | AO_SPDUB + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ +/***************************************************************************** + * + * DMAC register + * + *****************************************************************************/ +/* +The PS3_AUDIO_KICK register is used to initiate a DMA transfer and monitor +its status + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0|STATU|0 0 0| EVENT |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|R| KICK + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ +/* +The REQUEST field is written to ACTIVE to initiate a DMA request when EVENT +occurs. +It will return to the DONE state when the request is completed. +The registers for a DMA channel should only be written if REQUEST is IDLE. +*/ + +#define PS3_AUDIO_KICK_REQUEST (1 << 0) /* RWIVF */ +#define PS3_AUDIO_KICK_REQUEST_IDLE (0 << 0) /* RWI-V */ +#define PS3_AUDIO_KICK_REQUEST_ACTIVE (1 << 0) /* -W--T */ + +/* + *The EVENT field is used to set the event in which + *the DMA request becomes active. + */ +#define PS3_AUDIO_KICK_EVENT_MASK (0x1f << 16) /* RWIVF */ +#define PS3_AUDIO_KICK_EVENT_ALWAYS (0x00 << 16) /* RWI-V */ +#define PS3_AUDIO_KICK_EVENT_SERIALOUT0_EMPTY (0x01 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SERIALOUT0_UNDERFLOW (0x02 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SERIALOUT1_EMPTY (0x03 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SERIALOUT1_UNDERFLOW (0x04 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SERIALOUT2_EMPTY (0x05 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SERIALOUT2_UNDERFLOW (0x06 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SERIALOUT3_EMPTY (0x07 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SERIALOUT3_UNDERFLOW (0x08 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SPDIF0_BLOCKTRANSFERCOMPLETE \ + (0x09 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SPDIF0_UNDERFLOW (0x0A << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SPDIF0_EMPTY (0x0B << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SPDIF1_BLOCKTRANSFERCOMPLETE \ + (0x0C << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SPDIF1_UNDERFLOW (0x0D << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SPDIF1_EMPTY (0x0E << 16) /* RW--V */ + +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA(n) \ + ((0x13 + (n)) << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA0 (0x13 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA1 (0x14 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA2 (0x15 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA3 (0x16 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA4 (0x17 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA5 (0x18 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA6 (0x19 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA7 (0x1A << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA8 (0x1B << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA9 (0x1C << 16) /* RW--V */ + +/* +The STATUS field can be used to monitor the progress of a DMA request. +DONE indicates the previous request has completed. +EVENT indicates that the DMA engine is waiting for the EVENT to occur. +PENDING indicates that the DMA engine has not started processing this +request, but the EVENT has occured. +DMA indicates that the data transfer is in progress. +NOTIFY indicates that the notifier signalling end of transfer is being written. +CLEAR indicated that the previous transfer was cleared. +ERROR indicates the previous transfer requested an unsupported +source/destination combination. +*/ + +#define PS3_AUDIO_KICK_STATUS_MASK (0x7 << 24) /* R-IVF */ +#define PS3_AUDIO_KICK_STATUS_DONE (0x0 << 24) /* R-I-V */ +#define PS3_AUDIO_KICK_STATUS_EVENT (0x1 << 24) /* R---V */ +#define PS3_AUDIO_KICK_STATUS_PENDING (0x2 << 24) /* R---V */ +#define PS3_AUDIO_KICK_STATUS_DMA (0x3 << 24) /* R---V */ +#define PS3_AUDIO_KICK_STATUS_NOTIFY (0x4 << 24) /* R---V */ +#define PS3_AUDIO_KICK_STATUS_CLEAR (0x5 << 24) /* R---V */ +#define PS3_AUDIO_KICK_STATUS_ERROR (0x6 << 24) /* R---V */ + +/* +The PS3_AUDIO_SOURCE register specifies the source address for transfers. + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + | START |0 0 0 0 0|TAR| SOURCE + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ + +/* +The Audio DMA engine uses 128-byte transfers, thus the address must be aligned +to a 128 byte boundary. The low seven bits are assumed to be 0. +*/ + +#define PS3_AUDIO_SOURCE_START_MASK (0x01FFFFFF << 7) /* RWIUF */ + +/* +The TARGET field specifies the memory space containing the source address. +*/ + +#define PS3_AUDIO_SOURCE_TARGET_MASK (3 << 0) /* RWIVF */ +#define PS3_AUDIO_SOURCE_TARGET_SYSTEM_MEMORY (2 << 0) /* RW--V */ + +/* +The PS3_AUDIO_DEST register specifies the destination address for transfers. + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + | START |0 0 0 0 0|TAR| DEST + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ + +/* +The Audio DMA engine uses 128-byte transfers, thus the address must be aligned +to a 128 byte boundary. The low seven bits are assumed to be 0. +*/ + +#define PS3_AUDIO_DEST_START_MASK (0x01FFFFFF << 7) /* RWIUF */ + +/* +The TARGET field specifies the memory space containing the destination address +AUDIOFIFO = Audio WriteData FIFO, +*/ + +#define PS3_AUDIO_DEST_TARGET_MASK (3 << 0) /* RWIVF */ +#define PS3_AUDIO_DEST_TARGET_AUDIOFIFO (1 << 0) /* RW--V */ + +/* +PS3_AUDIO_DMASIZE specifies the number of 128-byte blocks + 1 to transfer. +So a value of 0 means 128-bytes will get transfered. + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0| BLOCKS | DMASIZE + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ + + +#define PS3_AUDIO_DMASIZE_BLOCKS_MASK (0x7f << 0) /* RWIUF */ + +/* + * source/destination address for internal fifos + */ +#define PS3_AUDIO_AO_3W_LDATA(n) (0x1000 + (0x100 * (n))) +#define PS3_AUDIO_AO_3W_RDATA(n) (0x1080 + (0x100 * (n))) + +#define PS3_AUDIO_AO_SPD_DATA(n) (0x2000 + (0x400 * (n))) + + +/* + * field attiribute + * + * Read + * ' ' = Other Information + * '-' = Field is part of a write-only register + * 'C' = Value read is always the same, constant value line follows (C) + * 'R' = Value is read + * + * Write + * ' ' = Other Information + * '-' = Must not be written (D), value ignored when written (R,A,F) + * 'W' = Can be written + * + * Internal State + * ' ' = Other Information + * '-' = No internal state + * 'X' = Internal state, initial value is unknown + * 'I' = Internal state, initial value is known and follows (I) + * + * Declaration/Size + * ' ' = Other Information + * '-' = Does Not Apply + * 'V' = Type is void + * 'U' = Type is unsigned integer + * 'S' = Type is signed integer + * 'F' = Type is IEEE floating point + * '1' = Byte size (008) + * '2' = Short size (016) + * '3' = Three byte size (024) + * '4' = Word size (032) + * '8' = Double size (064) + * + * Define Indicator + * ' ' = Other Information + * 'D' = Device + * 'M' = Memory + * 'R' = Register + * 'A' = Array of Registers + * 'F' = Field + * 'V' = Value + * 'T' = Task + */ + diff -Nur sound/sh/Kconfig sound/sh/Kconfig --- sound/sh/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ sound/sh/Kconfig 2007-05-19 02:00:07.000000000 +0200 @@ -0,0 +1,14 @@ +# ALSA SH drivers + +menu "SUPERH devices" + depends on SND!=n && SUPERH + +config SND_AICA + tristate "Dreamcast Yamaha AICA sound" + depends on SH_DREAMCAST && SND + select SND_PCM + help + ALSA Sound driver for the SEGA Dreamcast console. + +endmenu + diff -Nur sound/sh/Makefile sound/sh/Makefile --- sound/sh/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ sound/sh/Makefile 2007-05-19 02:00:07.000000000 +0200 @@ -0,0 +1,8 @@ +# +# Makefile for ALSA +# + +snd-aica-objs := aica.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_AICA) += snd-aica.o diff -Nur sound/sh/aica.c sound/sh/aica.c --- sound/sh/aica.c 1970-01-01 01:00:00.000000000 +0100 +++ sound/sh/aica.c 2007-07-24 02:00:10.000000000 +0200 @@ -0,0 +1,657 @@ +/* +* This code is licenced under +* the General Public Licence +* version 2 +* +* Copyright Adrian McMenamin 2005, 2006, 2007 +* +* Requires firmware (BSD licenced) available from: +* http://linuxdc.cvs.sourceforge.net/linuxdc/linux-sh-dc/sound/oss/aica/firmware/ +* or the maintainer +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of version 2 of the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aica.h" + +MODULE_AUTHOR("Adrian McMenamin "); +MODULE_DESCRIPTION("Dreamcast AICA sound (pcm) driver"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{Yamaha/SEGA, AICA}}"); + +/* module parameters */ +#define CARD_NAME "AICA" +static int index = -1; +static char *id; +static int enable = 1; +module_param(index, int, 0444); +MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); +module_param(id, charp, 0444); +MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); +module_param(enable, bool, 0644); +MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); + +/* Use workqueue */ +static struct workqueue_struct *aica_queue; + +/* Simple platform device */ +static struct platform_device *pd; +static struct resource aica_memory_space[2] = { + { + .name = "AICA ARM CONTROL", + .start = ARM_RESET_REGISTER, + .flags = IORESOURCE_MEM, + .end = ARM_RESET_REGISTER + 3, + }, + { + .name = "AICA Sound RAM", + .start = SPU_MEMORY_BASE, + .flags = IORESOURCE_MEM, + .end = SPU_MEMORY_BASE + 0x200000 - 1, + }, +}; + +/* SPU specific functions */ +/* spu_write_wait - wait for G2-SH FIFO to clear */ +static void spu_write_wait(void) +{ + int time_count; + time_count = 0; + while (1) { + if (!(readl(G2_FIFO) & 0x11)) + break; + /* To ensure hardware failure doesn't wedge kernel */ + time_count++; + if (time_count > 0x10000) { + snd_printk + ("WARNING: G2 FIFO appears to be blocked.\n"); + break; + } + } +} + +/* spu_memset - write to memory in SPU address space */ +static void spu_memset(u32 toi, u32 what, int length) +{ + int i; + snd_assert(length % 4 == 0, return); + for (i = 0; i < length; i++) { + if (!(i % 8)) + spu_write_wait(); + writel(what, toi + SPU_MEMORY_BASE); + toi++; + } +} + +/* spu_memload - write to SPU address space */ +static void spu_memload(u32 toi, void *from, int length) +{ + u32 *froml = from; + u32 __iomem *to = (u32 __iomem *) (SPU_MEMORY_BASE + toi); + int i; + u32 val; + length = DIV_ROUND_UP(length, 4); + spu_write_wait(); + for (i = 0; i < length; i++) { + if (!(i % 8)) + spu_write_wait(); + val = *froml; + writel(val, to); + froml++; + to++; + } +} + +/* spu_disable - set spu registers to stop sound output */ +static void spu_disable(void) +{ + int i; + u32 regval; + spu_write_wait(); + regval = readl(ARM_RESET_REGISTER); + regval |= 1; + spu_write_wait(); + writel(regval, ARM_RESET_REGISTER); + for (i = 0; i < 64; i++) { + spu_write_wait(); + regval = readl(SPU_REGISTER_BASE + (i * 0x80)); + regval = (regval & ~0x4000) | 0x8000; + spu_write_wait(); + writel(regval, SPU_REGISTER_BASE + (i * 0x80)); + } +} + +/* spu_enable - set spu registers to enable sound output */ +static void spu_enable(void) +{ + u32 regval = readl(ARM_RESET_REGISTER); + regval &= ~1; + spu_write_wait(); + writel(regval, ARM_RESET_REGISTER); +} + +/* + * Halt the sound processor, clear the memory, + * load some default ARM7 code, and then restart ARM7 +*/ +static void spu_reset(void) +{ + spu_disable(); + spu_memset(0, 0, 0x200000 / 4); + /* Put ARM7 in endless loop */ + ctrl_outl(0xea000002, SPU_MEMORY_BASE); + spu_enable(); +} + +/* aica_chn_start - write to spu to start playback */ +static void aica_chn_start(void) +{ + spu_write_wait(); + writel(AICA_CMD_KICK | AICA_CMD_START, (u32 *) AICA_CONTROL_POINT); +} + +/* aica_chn_halt - write to spu to halt playback */ +static void aica_chn_halt(void) +{ + spu_write_wait(); + writel(AICA_CMD_KICK | AICA_CMD_STOP, (u32 *) AICA_CONTROL_POINT); +} + +/* ALSA code below */ +static struct snd_pcm_hardware snd_pcm_aica_playback_hw = { + .info = (SNDRV_PCM_INFO_NONINTERLEAVED), + .formats = + (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_IMA_ADPCM), + .rates = SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = AICA_BUFFER_SIZE, + .period_bytes_min = AICA_PERIOD_SIZE, + .period_bytes_max = AICA_PERIOD_SIZE, + .periods_min = AICA_PERIOD_NUMBER, + .periods_max = AICA_PERIOD_NUMBER, +}; + +static int aica_dma_transfer(int channels, int buffer_size, + struct snd_pcm_substream *substream) +{ + int q, err, period_offset; + struct snd_card_aica *dreamcastcard; + struct snd_pcm_runtime *runtime; + err = 0; + dreamcastcard = substream->pcm->private_data; + period_offset = dreamcastcard->clicks; + period_offset %= (AICA_PERIOD_NUMBER / channels); + runtime = substream->runtime; + for (q = 0; q < channels; q++) { + err = dma_xfer(AICA_DMA_CHANNEL, + (unsigned long) (runtime->dma_area + + (AICA_BUFFER_SIZE * q) / + channels + + AICA_PERIOD_SIZE * + period_offset), + AICA_CHANNEL0_OFFSET + q * CHANNEL_OFFSET + + AICA_PERIOD_SIZE * period_offset, + buffer_size / channels, AICA_DMA_MODE); + if (unlikely(err < 0)) + break; + dma_wait_for_completion(AICA_DMA_CHANNEL); + } + return err; +} + +static void startup_aica(struct snd_card_aica *dreamcastcard) +{ + spu_memload(AICA_CHANNEL0_CONTROL_OFFSET, + dreamcastcard->channel, sizeof(struct aica_channel)); + aica_chn_start(); +} + +static void run_spu_dma(struct work_struct *work) +{ + int buffer_size; + struct snd_pcm_runtime *runtime; + struct snd_card_aica *dreamcastcard; + dreamcastcard = + container_of(work, struct snd_card_aica, spu_dma_work); + runtime = dreamcastcard->substream->runtime; + if (unlikely(dreamcastcard->dma_check == 0)) { + buffer_size = + frames_to_bytes(runtime, runtime->buffer_size); + if (runtime->channels > 1) + dreamcastcard->channel->flags |= 0x01; + aica_dma_transfer(runtime->channels, buffer_size, + dreamcastcard->substream); + startup_aica(dreamcastcard); + dreamcastcard->clicks = + buffer_size / (AICA_PERIOD_SIZE * runtime->channels); + return; + } else { + aica_dma_transfer(runtime->channels, + AICA_PERIOD_SIZE * runtime->channels, + dreamcastcard->substream); + snd_pcm_period_elapsed(dreamcastcard->substream); + dreamcastcard->clicks++; + if (unlikely(dreamcastcard->clicks >= AICA_PERIOD_NUMBER)) + dreamcastcard->clicks %= AICA_PERIOD_NUMBER; + mod_timer(&dreamcastcard->timer, jiffies + 1); + } +} + +static void aica_period_elapsed(unsigned long timer_var) +{ + /*timer function - so cannot sleep */ + int play_period; + struct snd_pcm_runtime *runtime; + struct snd_pcm_substream *substream; + struct snd_card_aica *dreamcastcard; + substream = (struct snd_pcm_substream *) timer_var; + runtime = substream->runtime; + dreamcastcard = substream->pcm->private_data; + /* Have we played out an additional period? */ + play_period = + frames_to_bytes(runtime, + readl + (AICA_CONTROL_CHANNEL_SAMPLE_NUMBER)) / + AICA_PERIOD_SIZE; + if (play_period == dreamcastcard->current_period) { + /* reschedule the timer */ + mod_timer(&(dreamcastcard->timer), jiffies + 1); + return; + } + if (runtime->channels > 1) + dreamcastcard->current_period = play_period; + if (unlikely(dreamcastcard->dma_check == 0)) + dreamcastcard->dma_check = 1; + queue_work(aica_queue, &(dreamcastcard->spu_dma_work)); +} + +static void spu_begin_dma(struct snd_pcm_substream *substream) +{ + struct snd_card_aica *dreamcastcard; + struct snd_pcm_runtime *runtime; + runtime = substream->runtime; + dreamcastcard = substream->pcm->private_data; + /*get the queue to do the work */ + queue_work(aica_queue, &(dreamcastcard->spu_dma_work)); + /* Timer may already be running */ + if (unlikely(dreamcastcard->timer.data)) { + mod_timer(&dreamcastcard->timer, jiffies + 4); + return; + } + init_timer(&(dreamcastcard->timer)); + dreamcastcard->timer.data = (unsigned long) substream; + dreamcastcard->timer.function = aica_period_elapsed; + dreamcastcard->timer.expires = jiffies + 4; + add_timer(&(dreamcastcard->timer)); +} + +static int snd_aicapcm_pcm_open(struct snd_pcm_substream + *substream) +{ + struct snd_pcm_runtime *runtime; + struct aica_channel *channel; + struct snd_card_aica *dreamcastcard; + if (!enable) + return -ENOENT; + dreamcastcard = substream->pcm->private_data; + channel = kmalloc(sizeof(struct aica_channel), GFP_KERNEL); + if (!channel) + return -ENOMEM; + /* set defaults for channel */ + channel->sfmt = SM_8BIT; + channel->cmd = AICA_CMD_START; + channel->vol = dreamcastcard->master_volume; + channel->pan = 0x80; + channel->pos = 0; + channel->flags = 0; /* default to mono */ + dreamcastcard->channel = channel; + runtime = substream->runtime; + runtime->hw = snd_pcm_aica_playback_hw; + spu_enable(); + dreamcastcard->clicks = 0; + dreamcastcard->current_period = 0; + dreamcastcard->dma_check = 0; + return 0; +} + +static int snd_aicapcm_pcm_close(struct snd_pcm_substream + *substream) +{ + struct snd_card_aica *dreamcastcard = substream->pcm->private_data; + flush_workqueue(aica_queue); + if (dreamcastcard->timer.data) + del_timer(&dreamcastcard->timer); + kfree(dreamcastcard->channel); + spu_disable(); + return 0; +} + +static int snd_aicapcm_pcm_hw_free(struct snd_pcm_substream + *substream) +{ + /* Free the DMA buffer */ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_aicapcm_pcm_hw_params(struct snd_pcm_substream + *substream, struct snd_pcm_hw_params + *hw_params) +{ + /* Allocate a DMA buffer using ALSA built-ins */ + return + snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); +} + +static int snd_aicapcm_pcm_prepare(struct snd_pcm_substream + *substream) +{ + struct snd_card_aica *dreamcastcard = substream->pcm->private_data; + if ((substream->runtime)->format == SNDRV_PCM_FORMAT_S16_LE) + dreamcastcard->channel->sfmt = SM_16BIT; + dreamcastcard->channel->freq = substream->runtime->rate; + dreamcastcard->substream = substream; + return 0; +} + +static int snd_aicapcm_pcm_trigger(struct snd_pcm_substream + *substream, int cmd) +{ + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + spu_begin_dma(substream); + break; + case SNDRV_PCM_TRIGGER_STOP: + aica_chn_halt(); + break; + default: + return -EINVAL; + } + return 0; +} + +static unsigned long snd_aicapcm_pcm_pointer(struct snd_pcm_substream + *substream) +{ + return readl(AICA_CONTROL_CHANNEL_SAMPLE_NUMBER); +} + +static struct snd_pcm_ops snd_aicapcm_playback_ops = { + .open = snd_aicapcm_pcm_open, + .close = snd_aicapcm_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_aicapcm_pcm_hw_params, + .hw_free = snd_aicapcm_pcm_hw_free, + .prepare = snd_aicapcm_pcm_prepare, + .trigger = snd_aicapcm_pcm_trigger, + .pointer = snd_aicapcm_pcm_pointer, +}; + +/* TO DO: set up to handle more than one pcm instance */ +static int __init snd_aicapcmchip(struct snd_card_aica + *dreamcastcard, int pcm_index) +{ + struct snd_pcm *pcm; + int err; + /* AICA has no capture ability */ + err = + snd_pcm_new(dreamcastcard->card, "AICA PCM", pcm_index, 1, 0, + &pcm); + if (unlikely(err < 0)) + return err; + pcm->private_data = dreamcastcard; + strcpy(pcm->name, "AICA PCM"); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_aicapcm_playback_ops); + /* Allocate the DMA buffers */ + err = + snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data + (GFP_KERNEL), + AICA_BUFFER_SIZE, + AICA_BUFFER_SIZE); + return err; +} + +/* Mixer controls */ +#define aica_pcmswitch_info snd_ctl_boolean_mono_info + +static int aica_pcmswitch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = 1; /* TO DO: Fix me */ + return 0; +} + +static int aica_pcmswitch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + if (ucontrol->value.integer.value[0] == 1) + return 0; /* TO DO: Fix me */ + else + aica_chn_halt(); + return 0; +} + +static int aica_pcmvolume_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 = 0xFF; + return 0; +} + +static int aica_pcmvolume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_card_aica *dreamcastcard; + dreamcastcard = kcontrol->private_data; + if (unlikely(!dreamcastcard->channel)) + return -ETXTBSY; /* we've not yet been set up */ + ucontrol->value.integer.value[0] = dreamcastcard->channel->vol; + return 0; +} + +static int aica_pcmvolume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_card_aica *dreamcastcard; + dreamcastcard = kcontrol->private_data; + if (unlikely(!dreamcastcard->channel)) + return -ETXTBSY; + if (unlikely(dreamcastcard->channel->vol == + ucontrol->value.integer.value[0])) + return 0; + dreamcastcard->channel->vol = ucontrol->value.integer.value[0]; + dreamcastcard->master_volume = ucontrol->value.integer.value[0]; + spu_memload(AICA_CHANNEL0_CONTROL_OFFSET, + dreamcastcard->channel, sizeof(struct aica_channel)); + return 1; +} + +static struct snd_kcontrol_new snd_aica_pcmswitch_control __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Switch", + .index = 0, + .info = aica_pcmswitch_info, + .get = aica_pcmswitch_get, + .put = aica_pcmswitch_put +}; + +static struct snd_kcontrol_new snd_aica_pcmvolume_control __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Volume", + .index = 0, + .info = aica_pcmvolume_info, + .get = aica_pcmvolume_get, + .put = aica_pcmvolume_put +}; + +static int load_aica_firmware(void) +{ + int err; + const struct firmware *fw_entry; + spu_reset(); + err = request_firmware(&fw_entry, "aica_firmware.bin", &pd->dev); + if (unlikely(err)) + return err; + /* write firware into memory */ + spu_disable(); + spu_memload(0, fw_entry->data, fw_entry->size); + spu_enable(); + release_firmware(fw_entry); + return err; +} + +static int __devinit add_aicamixer_controls(struct snd_card_aica + *dreamcastcard) +{ + int err; + err = snd_ctl_add + (dreamcastcard->card, + snd_ctl_new1(&snd_aica_pcmvolume_control, dreamcastcard)); + if (unlikely(err < 0)) + return err; + err = snd_ctl_add + (dreamcastcard->card, + snd_ctl_new1(&snd_aica_pcmswitch_control, dreamcastcard)); + if (unlikely(err < 0)) + return err; + return 0; +} + +static int snd_aica_remove(struct platform_device *devptr) +{ + struct snd_card_aica *dreamcastcard; + dreamcastcard = platform_get_drvdata(devptr); + if (unlikely(!dreamcastcard)) + return -ENODEV; + snd_card_free(dreamcastcard->card); + kfree(dreamcastcard); + platform_set_drvdata(devptr, NULL); + return 0; +} + +static int __init snd_aica_probe(struct platform_device *devptr) +{ + int err; + struct snd_card_aica *dreamcastcard; + dreamcastcard = kmalloc(sizeof(struct snd_card_aica), GFP_KERNEL); + if (unlikely(!dreamcastcard)) + return -ENOMEM; + dreamcastcard->card = + snd_card_new(index, SND_AICA_DRIVER, THIS_MODULE, 0); + if (unlikely(!dreamcastcard->card)) { + kfree(dreamcastcard); + return -ENODEV; + } + strcpy(dreamcastcard->card->driver, "snd_aica"); + strcpy(dreamcastcard->card->shortname, SND_AICA_DRIVER); + strcpy(dreamcastcard->card->longname, + "Yamaha AICA Super Intelligent Sound Processor for SEGA Dreamcast"); + /* Prepare to use the queue */ + INIT_WORK(&(dreamcastcard->spu_dma_work), run_spu_dma); + /* Load the PCM 'chip' */ + err = snd_aicapcmchip(dreamcastcard, 0); + if (unlikely(err < 0)) + goto freedreamcast; + snd_card_set_dev(dreamcastcard->card, &devptr->dev); + dreamcastcard->timer.data = 0; + dreamcastcard->channel = NULL; + /* Add basic controls */ + err = add_aicamixer_controls(dreamcastcard); + if (unlikely(err < 0)) + goto freedreamcast; + /* Register the card with ALSA subsystem */ + err = snd_card_register(dreamcastcard->card); + if (unlikely(err < 0)) + goto freedreamcast; + platform_set_drvdata(devptr, dreamcastcard); + aica_queue = create_workqueue(CARD_NAME); + if (unlikely(!aica_queue)) + goto freedreamcast; + snd_printk + ("ALSA Driver for Yamaha AICA Super Intelligent Sound Processor\n"); + return 0; + freedreamcast: + snd_card_free(dreamcastcard->card); + kfree(dreamcastcard); + return err; +} + +static struct platform_driver snd_aica_driver = { + .probe = snd_aica_probe, + .remove = snd_aica_remove, + .driver = { + .name = SND_AICA_DRIVER}, +}; + +static int __init aica_init(void) +{ + int err; + err = platform_driver_register(&snd_aica_driver); + if (unlikely(err < 0)) + return err; + pd = platform_device_register_simple(SND_AICA_DRIVER, -1, + aica_memory_space, 2); + if (unlikely(IS_ERR(pd))) { + platform_driver_unregister(&snd_aica_driver); + return PTR_ERR(pd); + } + /* Load the firmware */ + return load_aica_firmware(); +} + +static void __exit aica_exit(void) +{ + /* Destroy the aica kernel thread * + * being extra cautious to check if it exists*/ + if (likely(aica_queue)) + destroy_workqueue(aica_queue); + platform_device_unregister(pd); + platform_driver_unregister(&snd_aica_driver); + /* Kill any sound still playing and reset ARM7 to safe state */ + spu_reset(); +} + +module_init(aica_init); +module_exit(aica_exit); diff -Nur sound/sh/aica.h sound/sh/aica.h --- sound/sh/aica.h 1970-01-01 01:00:00.000000000 +0100 +++ sound/sh/aica.h 2007-07-10 02:00:20.000000000 +0200 @@ -0,0 +1,81 @@ +/* aica.h + * Header file for ALSA driver for + * Sega Dreamcast Yamaha AICA sound + * Copyright Adrian McMenamin + * + * 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* SPU memory and register constants etc */ +#define G2_FIFO 0xa05f688c +#define SPU_MEMORY_BASE 0xA0800000 +#define ARM_RESET_REGISTER 0xA0702C00 +#define SPU_REGISTER_BASE 0xA0700000 + +/* AICA channels stuff */ +#define AICA_CONTROL_POINT 0xA0810000 +#define AICA_CONTROL_CHANNEL_SAMPLE_NUMBER 0xA0810008 +#define AICA_CHANNEL0_CONTROL_OFFSET 0x10004 + +/* Command values */ +#define AICA_CMD_KICK 0x80000000 +#define AICA_CMD_NONE 0 +#define AICA_CMD_START 1 +#define AICA_CMD_STOP 2 +#define AICA_CMD_VOL 3 + +/* Sound modes */ +#define SM_8BIT 1 +#define SM_16BIT 0 +#define SM_ADPCM 2 + +/* Buffer and period size */ +#define AICA_BUFFER_SIZE 0x8000 +#define AICA_PERIOD_SIZE 0x800 +#define AICA_PERIOD_NUMBER 16 + +#define AICA_CHANNEL0_OFFSET 0x11000 +#define AICA_CHANNEL1_OFFSET 0x21000 +#define CHANNEL_OFFSET 0x10000 + +#define AICA_DMA_CHANNEL 0 +#define AICA_DMA_MODE 5 + +#define SND_AICA_DRIVER "AICA" + +struct aica_channel { + uint32_t cmd; /* Command ID */ + uint32_t pos; /* Sample position */ + uint32_t length; /* Sample length */ + uint32_t freq; /* Frequency */ + uint32_t vol; /* Volume 0-255 */ + uint32_t pan; /* Pan 0-255 */ + uint32_t sfmt; /* Sound format */ + uint32_t flags; /* Bit flags */ +}; + +struct snd_card_aica { + struct work_struct spu_dma_work; + struct snd_card *card; + struct aica_channel *channel; + struct snd_pcm_substream *substream; + int clicks; + int current_period; + struct timer_list timer; + int master_volume; + int dma_check; +}; diff -Nur sound/soc/Kconfig sound/soc/Kconfig --- sound/soc/Kconfig 2007-07-09 01:32:17.000000000 +0200 +++ sound/soc/Kconfig 2007-05-15 02:00:09.000000000 +0200 @@ -27,6 +27,7 @@ source "sound/soc/at91/Kconfig" source "sound/soc/pxa/Kconfig" source "sound/soc/s3c24xx/Kconfig" +source "sound/soc/sh/Kconfig" # Supported codecs source "sound/soc/codecs/Kconfig" diff -Nur sound/soc/Makefile sound/soc/Makefile --- sound/soc/Makefile 2007-07-09 01:32:17.000000000 +0200 +++ sound/soc/Makefile 2007-05-15 02:00:09.000000000 +0200 @@ -1,4 +1,4 @@ snd-soc-core-objs := soc-core.o soc-dapm.o obj-$(CONFIG_SND_SOC) += snd-soc-core.o -obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/ +obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/ sh/ diff -Nur sound/soc/codecs/Kconfig sound/soc/codecs/Kconfig --- sound/soc/codecs/Kconfig 2007-07-09 01:32:17.000000000 +0200 +++ sound/soc/codecs/Kconfig 2007-08-01 02:00:07.000000000 +0200 @@ -17,3 +17,23 @@ config SND_SOC_WM9712 tristate depends on SND_SOC + +# Cirrus Logic CS4270 Codec +config SND_SOC_CS4270 + tristate + depends on SND_SOC + +# Cirrus Logic CS4270 Codec Hardware Mute Support +# Select if you have external muting circuitry attached to your CS4270. +config SND_SOC_CS4270_HWMUTE + bool + depends on SND_SOC_CS4270 + +# Cirrus Logic CS4270 Codec VD = 3.3V Errata +# Select if you are affected by the errata where the part will not function +# if MCLK divide-by-1.5 is selected and VD is set to 3.3V. The driver will +# not select any sample rates that require MCLK to be divided by 1.5. +config SND_SOC_CS4270_VD33_ERRATA + bool + depends on SND_SOC_CS4270 + diff -Nur sound/soc/codecs/Makefile sound/soc/codecs/Makefile --- sound/soc/codecs/Makefile 2007-07-09 01:32:17.000000000 +0200 +++ sound/soc/codecs/Makefile 2007-08-01 02:00:07.000000000 +0200 @@ -3,9 +3,11 @@ snd-soc-wm8750-objs := wm8750.o snd-soc-wm8753-objs := wm8753.o snd-soc-wm9712-objs := wm9712.o +snd-soc-cs4270-objs := cs4270.o obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o +obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o diff -Nur sound/soc/codecs/cs4270.c sound/soc/codecs/cs4270.c --- sound/soc/codecs/cs4270.c 1970-01-01 01:00:00.000000000 +0100 +++ sound/soc/codecs/cs4270.c 2007-08-14 02:00:08.000000000 +0200 @@ -0,0 +1,800 @@ +/* + * CS4270 ALSA SoC (ASoC) codec driver + * + * Author: Timur Tabi + * + * Copyright 2007 Freescale Semiconductor, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + * + * This is an ASoC device driver for the Cirrus Logic CS4270 codec. + * + * Current features/limitations: + * + * 1) Software mode is supported. Stand-alone mode is automatically + * selected if I2C is disabled or if a CS4270 is not found on the I2C + * bus. However, stand-alone mode is only partially implemented because + * there is no mechanism yet for this driver and the machine driver to + * communicate the values of the M0, M1, MCLK1, and MCLK2 pins. + * 2) Only I2C is supported, not SPI + * 3) Only Master mode is supported, not Slave. + * 4) The machine driver's 'startup' function must call + * cs4270_set_dai_sysclk() with the value of MCLK. + * 5) Only I2S and left-justified modes are supported + * 6) Power management is not supported + * 7) The only supported control is volume and hardware mute (if enabled) + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "cs4270.h" + +/* If I2C is defined, then we support software mode. However, if we're + not compiled as module but I2C is, then we can't use I2C calls. */ +#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) +#define USE_I2C +#endif + +/* Private data for the CS4270 */ +struct cs4270_private { + unsigned int mclk; /* Input frequency of the MCLK pin */ + unsigned int mode; /* The mode (I2S or left-justified) */ +}; + +/* The number of MCLK/LRCK ratios supported by the CS4270 */ +#define NUM_MCLK_RATIOS 9 + +/* The actual MCLK/LRCK ratios, in increasing numerical order */ +static unsigned int mclk_ratios[NUM_MCLK_RATIOS] = + {64, 96, 128, 192, 256, 384, 512, 768, 1024}; + +/* + * Determine the CS4270 samples rates. + * + * 'freq' is the input frequency to MCLK. The other parameters are ignored. + * + * The value of MCLK is used to determine which sample rates are supported + * by the CS4270. The ratio of MCLK / Fs must be equal to one of nine + * support values: 64, 96, 128, 192, 256, 384, 512, 768, and 1024. + * + * This function calculates the nine ratios and determines which ones match + * a standard sample rate. If there's a match, then it is added to the list + * of support sample rates. + * + * This function must be called by the machine driver's 'startup' function, + * otherwise the list of supported sample rates will not be available in + * time for ALSA. + * + * Note that in stand-alone mode, the sample rate is determined by input + * pins M0, M1, MDIV1, and MDIV2. Also in stand-alone mode, divide-by-3 + * is not a programmable option. However, divide-by-3 is not an available + * option in stand-alone mode. This cases two problems: a ratio of 768 is + * not available (it requires divide-by-3) and B) ratios 192 and 384 can + * only be selected with divide-by-1.5, but there is an errate that make + * this selection difficult. + * + * In addition, there is no mechanism for communicating with the machine + * driver what the input settings can be. This would need to be implemented + * for stand-alone mode to work. + */ +static int cs4270_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct cs4270_private *cs4270 = codec->private_data; + unsigned int rates = 0; + unsigned int rate_min = -1; + unsigned int rate_max = 0; + unsigned int i; + + cs4270->mclk = freq; + + for (i = 0; i < NUM_MCLK_RATIOS; i++) { + unsigned int rate = freq / mclk_ratios[i]; + rates |= snd_pcm_rate_to_rate_bit(rate); + if (rate < rate_min) + rate_min = rate; + if (rate > rate_max) + rate_max = rate; + } + /* FIXME: soc should support a rate list */ + rates &= ~SNDRV_PCM_RATE_KNOT; + + if (!rates) { + printk(KERN_ERR "cs4270: could not find a valid sample rate\n"); + return -EINVAL; + } + + codec_dai->playback.rates = rates; + codec_dai->playback.rate_min = rate_min; + codec_dai->playback.rate_max = rate_max; + + codec_dai->capture.rates = rates; + codec_dai->capture.rate_min = rate_min; + codec_dai->capture.rate_max = rate_max; + + return 0; +} + +/* + * Configure the codec for the selected audio format + * + * This function takes a bitmask of SND_SOC_DAIFMT_x bits and programs the + * codec accordingly. + * + * Currently, this function only supports SND_SOC_DAIFMT_I2S and + * SND_SOC_DAIFMT_LEFT_J. The CS4270 codec also supports right-justified + * data for playback only, but ASoC currently does not support different + * formats for playback vs. record. + */ +static int cs4270_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, + unsigned int format) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct cs4270_private *cs4270 = codec->private_data; + int ret = 0; + + switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_LEFT_J: + cs4270->mode = format & SND_SOC_DAIFMT_FORMAT_MASK; + break; + default: + printk(KERN_ERR "cs4270: invalid DAI format\n"); + ret = -EINVAL; + } + + return ret; +} + +/* + * The codec isn't really big-endian or little-endian, since the I2S + * interface requires data to be sent serially with the MSbit first. + * However, to support BE and LE I2S devices, we specify both here. That + * way, ALSA will always match the bit patterns. + */ +#define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \ + SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE) + +#ifdef USE_I2C + +/* CS4270 registers addresses */ +#define CS4270_CHIPID 0x01 /* Chip ID */ +#define CS4270_PWRCTL 0x02 /* Power Control */ +#define CS4270_MODE 0x03 /* Mode Control */ +#define CS4270_FORMAT 0x04 /* Serial Format, ADC/DAC Control */ +#define CS4270_TRANS 0x05 /* Transition Control */ +#define CS4270_MUTE 0x06 /* Mute Control */ +#define CS4270_VOLA 0x07 /* DAC Channel A Volume Control */ +#define CS4270_VOLB 0x08 /* DAC Channel B Volume Control */ + +#define CS4270_FIRSTREG 0x01 +#define CS4270_LASTREG 0x08 +#define CS4270_NUMREGS (CS4270_LASTREG - CS4270_FIRSTREG + 1) + +/* Bit masks for the CS4270 registers */ +#define CS4270_CHIPID_ID 0xF0 +#define CS4270_CHIPID_REV 0x0F +#define CS4270_PWRCTL_FREEZE 0x80 +#define CS4270_PWRCTL_PDN_ADC 0x20 +#define CS4270_PWRCTL_PDN_DAC 0x02 +#define CS4270_PWRCTL_PDN 0x01 +#define CS4270_MODE_SPEED_MASK 0x30 +#define CS4270_MODE_1X 0x00 +#define CS4270_MODE_2X 0x10 +#define CS4270_MODE_4X 0x20 +#define CS4270_MODE_SLAVE 0x30 +#define CS4270_MODE_DIV_MASK 0x0E +#define CS4270_MODE_DIV1 0x00 +#define CS4270_MODE_DIV15 0x02 +#define CS4270_MODE_DIV2 0x04 +#define CS4270_MODE_DIV3 0x06 +#define CS4270_MODE_DIV4 0x08 +#define CS4270_MODE_POPGUARD 0x01 +#define CS4270_FORMAT_FREEZE_A 0x80 +#define CS4270_FORMAT_FREEZE_B 0x40 +#define CS4270_FORMAT_LOOPBACK 0x20 +#define CS4270_FORMAT_DAC_MASK 0x18 +#define CS4270_FORMAT_DAC_LJ 0x00 +#define CS4270_FORMAT_DAC_I2S 0x08 +#define CS4270_FORMAT_DAC_RJ16 0x18 +#define CS4270_FORMAT_DAC_RJ24 0x10 +#define CS4270_FORMAT_ADC_MASK 0x01 +#define CS4270_FORMAT_ADC_LJ 0x00 +#define CS4270_FORMAT_ADC_I2S 0x01 +#define CS4270_TRANS_ONE_VOL 0x80 +#define CS4270_TRANS_SOFT 0x40 +#define CS4270_TRANS_ZERO 0x20 +#define CS4270_TRANS_INV_ADC_A 0x08 +#define CS4270_TRANS_INV_ADC_B 0x10 +#define CS4270_TRANS_INV_DAC_A 0x02 +#define CS4270_TRANS_INV_DAC_B 0x04 +#define CS4270_TRANS_DEEMPH 0x01 +#define CS4270_MUTE_AUTO 0x20 +#define CS4270_MUTE_ADC_A 0x08 +#define CS4270_MUTE_ADC_B 0x10 +#define CS4270_MUTE_POLARITY 0x04 +#define CS4270_MUTE_DAC_A 0x01 +#define CS4270_MUTE_DAC_B 0x02 + +/* + * A list of addresses on which this CS4270 could use. I2C addresses are + * 7 bits. For the CS4270, the upper four bits are always 1001, and the + * lower three bits are determined via the AD2, AD1, and AD0 pins + * (respectively). + */ +static unsigned short normal_i2c[] = { + 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, I2C_CLIENT_END +}; +I2C_CLIENT_INSMOD; + +/* + * Pre-fill the CS4270 register cache. + * + * We use the auto-increment feature of the CS4270 to read all registers in + * one shot. + */ +static int cs4270_fill_cache(struct snd_soc_codec *codec) +{ + u8 *cache = codec->reg_cache; + struct i2c_client *i2c_client = codec->control_data; + s32 length; + + length = i2c_smbus_read_i2c_block_data(i2c_client, + CS4270_FIRSTREG | 0x80, CS4270_NUMREGS, cache); + + if (length != CS4270_NUMREGS) { + printk(KERN_ERR "cs4270: I2C read failure, addr=0x%x\n", + i2c_client->addr); + return -EIO; + } + + return 0; +} + +/* + * Read from the CS4270 register cache. + * + * This CS4270 registers are cached to avoid excessive I2C I/O operations. + * After the initial read to pre-fill the cache, the CS4270 never updates + * the register values, so we won't have a cache coherncy problem. + */ +static unsigned int cs4270_read_reg_cache(struct snd_soc_codec *codec, + unsigned int reg) +{ + u8 *cache = codec->reg_cache; + + if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG)) + return -EIO; + + return cache[reg - CS4270_FIRSTREG]; +} + +/* + * Write to a CS4270 register via the I2C bus. + * + * This function writes the given value to the given CS4270 register, and + * also updates the register cache. + * + * Note that we don't use the hw_write function pointer of snd_soc_codec. + * That's because it's too clunky: the hw_write_t prototype does not match + * i2c_smbus_write_byte_data(), and it's just another layer of overhead. + */ +static int cs4270_i2c_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG)) + return -EIO; + + if (i2c_smbus_write_byte_data(codec->control_data, reg, value) == 0) { + /* We've written to the hardware, so update the cache */ + u8 *cache = codec->reg_cache; + cache[reg - CS4270_FIRSTREG] = value; + return 0; + } else { + printk(KERN_ERR "cs4270: I2C write of register %u failed\n", + reg); + return -EIO; + } +} + +/* + * Clock Ratio Selection for Master Mode with I2C enabled + * + * The data for this chart is taken from Table 5 of the CS4270 reference + * manual. + * + * This table is used to determine how to program the Mode Control register. + * It is also used by cs4270_set_dai_sysclk() to tell ALSA which sampling + * rates the CS4270 currently supports. + * + * Each element in this array corresponds to the ratios in mclk_ratios[]. + * These two arrays need to be in sync. + * + * 'speed_mode' is the corresponding bit pattern to be written to the + * MODE bits of the Mode Control Register + * + * 'mclk' is the corresponding bit pattern to be wirten to the MCLK bits of + * the Mode Control Register. + * + * In situations where a single ratio is represented by multiple speed + * modes, we favor the slowest speed. E.g, for a ratio of 128, we pick + * double-speed instead of quad-speed. However, the CS4270 errata states + * that Divide-By-1.5 can cause failures, so we avoid that mode where + * possible. + * + * ERRATA: There is an errata for the CS4270 where divide-by-1.5 does not + * work if VD = 3.3V. If this effects you, select the + * CONFIG_SND_SOC_CS4270_VD33_ERRATA Kconfig option, and the driver will + * never select any sample rates that require divide-by-1.5. + */ +static struct { + u8 speed_mode; + u8 mclk; +} cs4270_mode_ratios[NUM_MCLK_RATIOS] = { + {CS4270_MODE_4X, CS4270_MODE_DIV1}, /* 64 */ +#ifndef CONFIG_SND_SOC_CS4270_VD33_ERRATA + {CS4270_MODE_4X, CS4270_MODE_DIV15}, /* 96 */ +#endif + {CS4270_MODE_2X, CS4270_MODE_DIV1}, /* 128 */ + {CS4270_MODE_4X, CS4270_MODE_DIV3}, /* 192 */ + {CS4270_MODE_1X, CS4270_MODE_DIV1}, /* 256 */ + {CS4270_MODE_2X, CS4270_MODE_DIV3}, /* 384 */ + {CS4270_MODE_1X, CS4270_MODE_DIV2}, /* 512 */ + {CS4270_MODE_1X, CS4270_MODE_DIV3}, /* 768 */ + {CS4270_MODE_1X, CS4270_MODE_DIV4} /* 1024 */ +}; + +/* + * Program the CS4270 with the given hardware parameters. + * + * The .dai_ops functions are used to provide board-specific data, like + * input frequencies, to this driver. This function takes that information, + * combines it with the hardware parameters provided, and programs the + * hardware accordingly. + */ +static int cs4270_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->codec; + struct cs4270_private *cs4270 = codec->private_data; + unsigned int ret = 0; + unsigned int i; + unsigned int rate; + unsigned int ratio; + int reg; + + /* Figure out which MCLK/LRCK ratio to use */ + + rate = params_rate(params); /* Sampling rate, in Hz */ + ratio = cs4270->mclk / rate; /* MCLK/LRCK ratio */ + + for (i = 0; i < NUM_MCLK_RATIOS; i++) { + if (mclk_ratios[i] == ratio) + break; + } + + if (i == NUM_MCLK_RATIOS) { + /* We did not find a matching ratio */ + printk(KERN_ERR "cs4270: could not find matching ratio\n"); + return -EINVAL; + } + + /* Freeze and power-down the codec */ + + ret = snd_soc_write(codec, CS4270_PWRCTL, CS4270_PWRCTL_FREEZE | + CS4270_PWRCTL_PDN_ADC | CS4270_PWRCTL_PDN_DAC | + CS4270_PWRCTL_PDN); + if (ret < 0) { + printk(KERN_ERR "cs4270: I2C write failed\n"); + return ret; + } + + /* Program the mode control register */ + + reg = snd_soc_read(codec, CS4270_MODE); + reg &= ~(CS4270_MODE_SPEED_MASK | CS4270_MODE_DIV_MASK); + reg |= cs4270_mode_ratios[i].speed_mode | cs4270_mode_ratios[i].mclk; + + ret = snd_soc_write(codec, CS4270_MODE, reg); + if (ret < 0) { + printk(KERN_ERR "cs4270: I2C write failed\n"); + return ret; + } + + /* Program the format register */ + + reg = snd_soc_read(codec, CS4270_FORMAT); + reg &= ~(CS4270_FORMAT_DAC_MASK | CS4270_FORMAT_ADC_MASK); + + switch (cs4270->mode) { + case SND_SOC_DAIFMT_I2S: + reg |= CS4270_FORMAT_DAC_I2S | CS4270_FORMAT_ADC_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + reg |= CS4270_FORMAT_DAC_LJ | CS4270_FORMAT_ADC_LJ; + break; + default: + printk(KERN_ERR "cs4270: unknown format\n"); + return -EINVAL; + } + + ret = snd_soc_write(codec, CS4270_FORMAT, reg); + if (ret < 0) { + printk(KERN_ERR "cs4270: I2C write failed\n"); + return ret; + } + + /* Disable auto-mute. This feature appears to be buggy, because in + some situations, auto-mute will not deactivate when it should. */ + + reg = snd_soc_read(codec, CS4270_MUTE); + reg &= ~CS4270_MUTE_AUTO; + ret = snd_soc_write(codec, CS4270_MUTE, reg); + if (ret < 0) { + printk(KERN_ERR "cs4270: I2C write failed\n"); + return ret; + } + + /* Thaw and power-up the codec */ + + ret = snd_soc_write(codec, CS4270_PWRCTL, 0); + if (ret < 0) { + printk(KERN_ERR "cs4270: I2C write failed\n"); + return ret; + } + + return ret; +} + +#ifdef CONFIG_SND_SOC_CS4270_HWMUTE + +/* + * Set the CS4270 external mute + * + * This function toggles the mute bits in the MUTE register. The CS4270's + * mute capability is intended for external muting circuitry, so if the + * board does not have the MUTEA or MUTEB pins connected to such circuitry, + * then this function will do nothing. + */ +static int cs4270_mute(struct snd_soc_codec_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + int reg6; + + reg6 = snd_soc_read(codec, CS4270_MUTE); + + if (mute) + reg6 |= CS4270_MUTE_ADC_A | CS4270_MUTE_ADC_B | + CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B; + else + reg6 &= ~(CS4270_MUTE_ADC_A | CS4270_MUTE_ADC_B | + CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B); + + return snd_soc_write(codec, CS4270_MUTE, reg6); +} + +#endif + +static int cs4270_i2c_probe(struct i2c_adapter *adap, int addr, int kind); + +/* + * Notify the driver that a new I2C bus has been found. + * + * This function is called for each I2C bus in the system. The function + * then asks the I2C subsystem to probe that bus at the addresses on which + * our device (the CS4270) could exist. If a device is found at one of + * those addresses, then our probe function (cs4270_i2c_probe) is called. + */ +static int cs4270_i2c_attach(struct i2c_adapter *adapter) +{ + return i2c_probe(adapter, &addr_data, cs4270_i2c_probe); +} + +static int cs4270_i2c_detach(struct i2c_client *client) +{ + struct snd_soc_codec *codec = i2c_get_clientdata(client); + + i2c_detach_client(client); + codec->control_data = NULL; + + kfree(codec->reg_cache); + codec->reg_cache = NULL; + + kfree(client); + return 0; +} + +/* A list of non-DAPM controls that the CS4270 supports */ +static const struct snd_kcontrol_new cs4270_snd_controls[] = { + SOC_DOUBLE_R("Master Playback Volume", + CS4270_VOLA, CS4270_VOLB, 0, 0xFF, 0) +}; + +static struct i2c_driver cs4270_i2c_driver = { + .driver = { + .name = "CS4270 I2C", + .owner = THIS_MODULE, + }, + .id = I2C_DRIVERID_CS4270, + .attach_adapter = cs4270_i2c_attach, + .detach_client = cs4270_i2c_detach, +}; + +/* + * Global variable to store socdev for i2c probe function. + * + * If struct i2c_driver had a private_data field, we wouldn't need to use + * cs4270_socdec. This is the only way to pass the socdev structure to + * cs4270_i2c_probe(). + * + * The real solution to cs4270_socdev is to create a mechanism + * that maps I2C addresses to snd_soc_device structures. Perhaps the + * creation of the snd_soc_device object should be moved out of + * cs4270_probe() and into cs4270_i2c_probe(), but that would make this + * driver dependent on I2C. The CS4270 supports "stand-alone" mode, whereby + * the chip is *not* connected to the I2C bus, but is instead configured via + * input pins. + */ +static struct snd_soc_device *cs4270_socdev; + +/* + * Initialize the I2C interface of the CS4270 + * + * This function is called for whenever the I2C subsystem finds a device + * at a particular address. + * + * Note: snd_soc_new_pcms() must be called before this function can be called, + * because of snd_ctl_add(). + */ +static int cs4270_i2c_probe(struct i2c_adapter *adapter, int addr, int kind) +{ + struct snd_soc_device *socdev = cs4270_socdev; + struct snd_soc_codec *codec = socdev->codec; + struct i2c_client *i2c_client = NULL; + int i; + int ret = 0; + + /* Probing all possible addresses has one drawback: if there are + multiple CS4270s on the bus, then you cannot specify which + socdev is matched with which CS4270. For now, we just reject + this I2C device if the socdev already has one attached. */ + if (codec->control_data) + return -ENODEV; + + /* Note: codec_dai->codec is NULL here */ + + i2c_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (!i2c_client) { + printk(KERN_ERR "cs4270: could not allocate I2C client\n"); + return -ENOMEM; + } + + codec->reg_cache = kzalloc(CS4270_NUMREGS, GFP_KERNEL); + if (!codec->reg_cache) { + printk(KERN_ERR "cs4270: could not allocate register cache\n"); + ret = -ENOMEM; + goto error; + } + + i2c_set_clientdata(i2c_client, codec); + strcpy(i2c_client->name, "CS4270"); + + i2c_client->driver = &cs4270_i2c_driver; + i2c_client->adapter = adapter; + i2c_client->addr = addr; + + /* Verify that we have a CS4270 */ + + ret = i2c_smbus_read_byte_data(i2c_client, CS4270_CHIPID); + if (ret < 0) { + printk(KERN_ERR "cs4270: failed to read I2C\n"); + goto error; + } + /* The top four bits of the chip ID should be 1100. */ + if ((ret & 0xF0) != 0xC0) { + /* The device at this address is not a CS4270 codec */ + ret = -ENODEV; + goto error; + } + + printk(KERN_INFO "cs4270: found device at I2C address %X\n", addr); + printk(KERN_INFO "cs4270: hardware revision %X\n", ret & 0xF); + + /* Tell the I2C layer a new client has arrived */ + + ret = i2c_attach_client(i2c_client); + if (ret) { + printk(KERN_ERR "cs4270: could not attach codec, " + "I2C address %x, error code %i\n", addr, ret); + goto error; + } + + codec->control_data = i2c_client; + codec->read = cs4270_read_reg_cache; + codec->write = cs4270_i2c_write; + codec->reg_cache_size = CS4270_NUMREGS; + + /* The I2C interface is set up, so pre-fill our register cache */ + + ret = cs4270_fill_cache(codec); + if (ret < 0) { + printk(KERN_ERR "cs4270: failed to fill register cache\n"); + goto error; + } + + /* Add the non-DAPM controls */ + + for (i = 0; i < ARRAY_SIZE(cs4270_snd_controls); i++) { + struct snd_kcontrol *kctrl = + snd_soc_cnew(&cs4270_snd_controls[i], codec, NULL); + + ret = snd_ctl_add(codec->card, kctrl); + if (ret < 0) + goto error; + } + + return 0; + +error: + if (codec->control_data) { + i2c_detach_client(i2c_client); + codec->control_data = NULL; + } + + kfree(codec->reg_cache); + codec->reg_cache = NULL; + codec->reg_cache_size = 0; + + kfree(i2c_client); + + return ret; +} + +#endif + +struct snd_soc_codec_dai cs4270_dai = { + .name = "CS4270", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = 0, + .formats = CS4270_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = 0, + .formats = CS4270_FORMATS, + }, + .dai_ops = { + .set_sysclk = cs4270_set_dai_sysclk, + .set_fmt = cs4270_set_dai_fmt, + } +}; +EXPORT_SYMBOL_GPL(cs4270_dai); + +/* + * ASoC probe function + * + * This function is called when the machine driver calls + * platform_device_add(). + */ +static int cs4270_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + int ret = 0; + + printk(KERN_INFO "CS4270 ALSA SoC Codec\n"); + + /* Allocate enough space for the snd_soc_codec structure + and our private data together. */ + codec = kzalloc(ALIGN(sizeof(struct snd_soc_codec), 4) + + sizeof(struct cs4270_private), GFP_KERNEL); + if (!codec) { + printk(KERN_ERR "cs4270: Could not allocate codec structure\n"); + return -ENOMEM; + } + + mutex_init(&codec->mutex); + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + codec->name = "CS4270"; + codec->owner = THIS_MODULE; + codec->dai = &cs4270_dai; + codec->num_dai = 1; + codec->private_data = codec + ALIGN(sizeof(struct snd_soc_codec), 4); + + socdev->codec = codec; + + /* Register PCMs */ + + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + printk(KERN_ERR "cs4270: failed to create PCMs\n"); + return ret; + } + +#ifdef USE_I2C + cs4270_socdev = socdev; + + ret = i2c_add_driver(&cs4270_i2c_driver); + if (ret) { + printk(KERN_ERR "cs4270: failed to attach driver"); + snd_soc_free_pcms(socdev); + return ret; + } + + /* Did we find a CS4270 on the I2C bus? */ + if (codec->control_data) { + /* Initialize codec ops */ + cs4270_dai.ops.hw_params = cs4270_hw_params; +#ifdef CONFIG_SND_SOC_CS4270_HWMUTE + cs4270_dai.dai_ops.digital_mute = cs4270_mute; +#endif + } else + printk(KERN_INFO "cs4270: no I2C device found, " + "using stand-alone mode\n"); +#else + printk(KERN_INFO "cs4270: I2C disabled, using stand-alone mode\n"); +#endif + + ret = snd_soc_register_card(socdev); + if (ret < 0) { + printk(KERN_ERR "cs4270: failed to register card\n"); + snd_soc_free_pcms(socdev); + return ret; + } + + return ret; +} + +static int cs4270_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + + snd_soc_free_pcms(socdev); + +#ifdef USE_I2C + if (socdev->codec->control_data) + i2c_del_driver(&cs4270_i2c_driver); +#endif + + kfree(socdev->codec); + socdev->codec = NULL; + + return 0; +} + +/* + * ASoC codec device structure + * + * Assign this variable to the codec_dev field of the machine driver's + * snd_soc_device structure. + */ +struct snd_soc_codec_device soc_codec_device_cs4270 = { + .probe = cs4270_probe, + .remove = cs4270_remove +}; +EXPORT_SYMBOL_GPL(soc_codec_device_cs4270); + +MODULE_AUTHOR("Timur Tabi "); +MODULE_DESCRIPTION("Cirrus Logic CS4270 ALSA SoC Codec Driver"); +MODULE_LICENSE("GPL"); diff -Nur sound/soc/codecs/cs4270.h sound/soc/codecs/cs4270.h --- sound/soc/codecs/cs4270.h 1970-01-01 01:00:00.000000000 +0100 +++ sound/soc/codecs/cs4270.h 2007-08-01 02:00:07.000000000 +0200 @@ -0,0 +1,28 @@ +/* + * Cirrus Logic CS4270 ALSA SoC Codec Driver + * + * Author: Timur Tabi + * + * Copyright 2007 Freescale Semiconductor, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifndef _CS4270_H +#define _CS4270_H + +/* + * The ASoC codec DAI structure for the CS4270. Assign this structure to + * the .codec_dai field of your machine driver's snd_soc_dai_link structure. + */ +extern struct snd_soc_codec_dai cs4270_dai; + +/* + * The ASoC codec device structure for the CS4270. Assign this structure + * to the .codec_dev field of your machine driver's snd_soc_device + * structure. + */ +extern struct snd_soc_codec_device soc_codec_device_cs4270; + +#endif diff -Nur sound/soc/pxa/pxa2xx-ac97.c sound/soc/pxa/pxa2xx-ac97.c --- sound/soc/pxa/pxa2xx-ac97.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/soc/pxa/pxa2xx-ac97.c 2007-07-31 02:00:08.000000000 +0200 @@ -160,9 +160,9 @@ gsr_bits = 0; #ifdef CONFIG_PXA27x /* PXA27x Developers Manual section 13.5.2.2.1 */ - pxa_set_cken(1 << 31, 1); + pxa_set_cken(31, 1); udelay(5); - pxa_set_cken(1 << 31, 0); + pxa_set_cken(31, 0); GCR = GCR_COLD_RST; udelay(50); #else diff -Nur sound/soc/pxa/spitz.c sound/soc/pxa/spitz.c --- sound/soc/pxa/spitz.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/soc/pxa/spitz.c 2007-07-24 02:00:10.000000000 +0200 @@ -34,7 +34,6 @@ #include #include #include -#include #include "../codecs/wm8750.h" #include "pxa2xx-pcm.h" #include "pxa2xx-i2s.h" diff -Nur sound/soc/s3c24xx/Kconfig sound/soc/s3c24xx/Kconfig --- sound/soc/s3c24xx/Kconfig 2007-07-09 01:32:17.000000000 +0200 +++ sound/soc/s3c24xx/Kconfig 2007-07-24 02:00:10.000000000 +0200 @@ -1,6 +1,7 @@ config SND_S3C24XX_SOC tristate "SoC Audio for the Samsung S3C24XX chips" depends on ARCH_S3C2410 && SND_SOC + select SND_PCM help Say Y or M if you want to add support for codecs attached to the S3C24XX AC97, I2S or SSP interface. You will also need @@ -8,3 +9,29 @@ config SND_S3C24XX_SOC_I2S tristate + +config SND_S3C2443_SOC_AC97 + tristate + select AC97_BUS + select SND_AC97_CODEC + select SND_SOC_AC97_BUS + +config SND_S3C24XX_SOC_NEO1973_WM8753 + tristate "SoC I2S Audio support for NEO1973 - WM8753" + depends on SND_S3C24XX_SOC && MACH_NEO1973_GTA01 + select SND_S3C24XX_SOC_I2S + select SND_SOC_WM8753 + help + Say Y if you want to add support for SoC audio on smdk2440 + with the WM8753. + +config SND_S3C24XX_SOC_SMDK2443_WM9710 + tristate "SoC AC97 Audio support for SMDK2443 - WM9710" + depends on SND_S3C24XX_SOC && MACH_SMDK2443 + select SND_S3C2443_SOC_AC97 + select SND_SOC_AC97_CODEC + help + Say Y if you want to add support for SoC audio on smdk2443 + with the WM9710. + + diff -Nur sound/soc/s3c24xx/Makefile sound/soc/s3c24xx/Makefile --- sound/soc/s3c24xx/Makefile 2007-07-09 01:32:17.000000000 +0200 +++ sound/soc/s3c24xx/Makefile 2007-05-15 02:00:09.000000000 +0200 @@ -1,6 +1,15 @@ # S3c24XX Platform Support snd-soc-s3c24xx-objs := s3c24xx-pcm.o snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o +snd-soc-s3c2443-ac97-objs := s3c2443-ac97.o obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o +obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.o + +# S3C24XX Machine Support +snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o +snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o + +obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o +obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o diff -Nur sound/soc/s3c24xx/lm4857.h sound/soc/s3c24xx/lm4857.h --- sound/soc/s3c24xx/lm4857.h 1970-01-01 01:00:00.000000000 +0100 +++ sound/soc/s3c24xx/lm4857.h 2007-06-20 02:00:08.000000000 +0200 @@ -0,0 +1,32 @@ +/* + * 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 -Nur sound/soc/s3c24xx/neo1973_wm8753.c sound/soc/s3c24xx/neo1973_wm8753.c --- sound/soc/s3c24xx/neo1973_wm8753.c 1970-01-01 01:00:00.000000000 +0100 +++ sound/soc/s3c24xx/neo1973_wm8753.c 2007-05-15 02:00:09.000000000 +0200 @@ -0,0 +1,670 @@ +/* + * neo1973_wm8753.c -- SoC audio for Neo1973 + * + * 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 + * 20th Jan 2007 Initial version. + * 05th Feb 2007 Rename all to Neo1973 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../codecs/wm8753.h" +#include "lm4857.h" +#include "s3c24xx-pcm.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_machine neo1973; +static struct i2c_client *i2c; + +static int neo1973_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_codec_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_cpu_dai *cpu_dai = rtd->dai->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 = codec_dai->dai_ops.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 = cpu_dai->dai_ops.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 = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_MCLK, pll_out, + SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + /* set MCLK division for sample rate */ + ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, + S3C2410_IISMOD_32FS ); + if (ret < 0) + return ret; + + /* set codec BCLK division for sample rate */ + ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk); + if (ret < 0) + return ret; + + /* set prescaler division for sample rate */ + ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER, + S3C24XX_PRESCALE(4,4)); + if (ret < 0) + return ret; + + /* codec PLL input is PCLK/4 */ + ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1, + iis_clkrate / 4, pll_out); + if (ret < 0) + return ret; + + return 0; +} + +static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai; + + /* disable the PLL */ + return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1, 0, 0); +} + +/* + * Neo1973 WM8753 HiFi DAI opserations. + */ +static struct snd_soc_ops neo1973_hifi_ops = { + .hw_params = neo1973_hifi_hw_params, + .hw_free = neo1973_hifi_hw_free, +}; + +static int neo1973_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_codec_dai *codec_dai = rtd->dai->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 = codec_dai->dai_ops.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 = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_PCMCLK, 12288000, + SND_SOC_CLOCK_IN); + if (ret < 0) + return ret; + + /* set codec PCM division for sample rate */ + ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv); + if (ret < 0) + return ret; + + /* configue and enable PLL for 12.288MHz output */ + ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2, + iis_clkrate / 4, 12288000); + if (ret < 0) + return ret; + + return 0; +} + +static int neo1973_voice_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai; + + /* disable the PLL */ + return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2, 0, 0); +} + +static struct snd_soc_ops neo1973_voice_ops = { + .hw_params = neo1973_voice_hw_params, + .hw_free = neo1973_voice_hw_free, +}; + +static int neo1973_scenario = 0; + +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) +{ + switch(neo1973_scenario) { + case NEO_AUDIO_OFF: + snd_soc_dapm_set_endpoint(codec, "Audio Out", 0); + snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0); + snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0); + snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0); + snd_soc_dapm_set_endpoint(codec, "Call Mic", 0); + break; + case NEO_GSM_CALL_AUDIO_HANDSET: + snd_soc_dapm_set_endpoint(codec, "Audio Out", 1); + snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1); + snd_soc_dapm_set_endpoint(codec, "GSM Line In", 1); + snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0); + snd_soc_dapm_set_endpoint(codec, "Call Mic", 1); + break; + case NEO_GSM_CALL_AUDIO_HEADSET: + snd_soc_dapm_set_endpoint(codec, "Audio Out", 1); + snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1); + snd_soc_dapm_set_endpoint(codec, "GSM Line In", 1); + snd_soc_dapm_set_endpoint(codec, "Headset Mic", 1); + snd_soc_dapm_set_endpoint(codec, "Call Mic", 0); + break; + case NEO_GSM_CALL_AUDIO_BLUETOOTH: + snd_soc_dapm_set_endpoint(codec, "Audio Out", 0); + snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1); + snd_soc_dapm_set_endpoint(codec, "GSM Line In", 1); + snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0); + snd_soc_dapm_set_endpoint(codec, "Call Mic", 0); + break; + case NEO_STEREO_TO_SPEAKERS: + snd_soc_dapm_set_endpoint(codec, "Audio Out", 1); + snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0); + snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0); + snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0); + snd_soc_dapm_set_endpoint(codec, "Call Mic", 0); + break; + case NEO_STEREO_TO_HEADPHONES: + snd_soc_dapm_set_endpoint(codec, "Audio Out", 1); + snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0); + snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0); + snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0); + snd_soc_dapm_set_endpoint(codec, "Call Mic", 0); + break; + case NEO_CAPTURE_HANDSET: + snd_soc_dapm_set_endpoint(codec, "Audio Out", 0); + snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0); + snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0); + snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0); + snd_soc_dapm_set_endpoint(codec, "Call Mic", 1); + break; + case NEO_CAPTURE_HEADSET: + snd_soc_dapm_set_endpoint(codec, "Audio Out", 0); + snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0); + snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0); + snd_soc_dapm_set_endpoint(codec, "Headset Mic", 1); + snd_soc_dapm_set_endpoint(codec, "Call Mic", 0); + break; + case NEO_CAPTURE_BLUETOOTH: + snd_soc_dapm_set_endpoint(codec, "Audio Out", 0); + snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0); + snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0); + snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0); + snd_soc_dapm_set_endpoint(codec, "Call Mic", 0); + break; + default: + snd_soc_dapm_set_endpoint(codec, "Audio Out", 0); + snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0); + snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0); + snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0); + snd_soc_dapm_set_endpoint(codec, "Call Mic", 0); + } + + snd_soc_dapm_sync_endpoints(codec); + + return 0; +} + +static int neo1973_set_scenario(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + + if (neo1973_scenario == ucontrol->value.integer.value[0]) + return 0; + + neo1973_scenario = ucontrol->value.integer.value[0]; + set_scenario_endpoints(codec, neo1973_scenario); + return 1; +} + +static u8 lm4857_regs[4] = {0x00, 0x40, 0x80, 0xC0}; + +static void lm4857_write_regs(void) +{ + if (i2c_master_send(i2c, lm4857_regs, 4) != 4) + printk(KERN_ERR "lm4857: i2c write failed\n"); +} + +static int lm4857_get_reg(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int reg=kcontrol->private_value & 0xFF; + int shift = (kcontrol->private_value >> 8) & 0x0F; + int mask = (kcontrol->private_value >> 16) & 0xFF; + + ucontrol->value.integer.value[0] = (lm4857_regs[reg] >> shift) & mask; + return 0; +} + +static int lm4857_set_reg(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int reg = kcontrol->private_value & 0xFF; + int shift = (kcontrol->private_value >> 8) & 0x0F; + int mask = (kcontrol->private_value >> 16) & 0xFF; + + 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; +} + +static int lm4857_get_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 value = lm4857_regs[LM4857_CTRL] & 0x0F; + + if (value) + value -= 5; + + 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]; + + 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_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), +}; + + +/* example machine audio_mapnections */ +static const char* audio_map[][3] = { + + /* 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"}, + + /* 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, "Call Mic"}, + + /* Connect the ALC pins */ + {"ACIN", NULL, "ACOP"}, + + {NULL, NULL, NULL}, +}; + +static const char *lm4857_mode[] = { + "Off", + "Call Speaker", + "Stereo Speakers", + "Stereo Speakers + Headphones", + "Headphones" +}; + +static const struct soc_enum lm4857_mode_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lm4857_mode), lm4857_mode), +}; + +static const char *neo_scenarios[] = { + "Off", + "GSM Handset", + "GSM Headset", + "GSM Bluetooth", + "Speakers", + "Headphones", + "Capture Handset", + "Capture Headset", + "Capture Bluetooth" +}; + +static const struct soc_enum neo_scenario_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(neo_scenarios),neo_scenarios), +}; + +static const struct snd_kcontrol_new wm8753_neo1973_controls[] = { + SOC_SINGLE_EXT("Amp Left Playback Volume", LM4857_LVOL, 0, 31, 0, + lm4857_get_reg, lm4857_set_reg), + SOC_SINGLE_EXT("Amp Right Playback Volume", LM4857_RVOL, 0, 31, 0, + lm4857_get_reg, lm4857_set_reg), + SOC_SINGLE_EXT("Amp Mono Playback Volume", LM4857_MVOL, 0, 31, 0, + lm4857_get_reg, lm4857_set_reg), + 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), +}; + +/* + * 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_codec *codec) +{ + int i, err; + + /* set up NC codec pins */ + snd_soc_dapm_set_endpoint(codec, "LOUT2", 0); + snd_soc_dapm_set_endpoint(codec, "ROUT2", 0); + snd_soc_dapm_set_endpoint(codec, "OUT3", 0); + snd_soc_dapm_set_endpoint(codec, "OUT4", 0); + snd_soc_dapm_set_endpoint(codec, "LINE1", 0); + snd_soc_dapm_set_endpoint(codec, "LINE2", 0); + + + /* set endpoints to default mode */ + set_scenario_endpoints(codec, NEO_AUDIO_OFF); + + /* Add neo1973 specific widgets */ + for (i = 0; i < ARRAY_SIZE(wm8753_dapm_widgets); i++) + snd_soc_dapm_new_control(codec, &wm8753_dapm_widgets[i]); + + /* add neo1973 specific controls */ + for (i = 0; i < ARRAY_SIZE(wm8753_neo1973_controls); i++) { + err = snd_ctl_add(codec->card, + snd_soc_cnew(&wm8753_neo1973_controls[i], + codec, NULL)); + if (err < 0) + return err; + } + + /* set up neo1973 specific audio path audio_mapnects */ + for (i = 0; audio_map[i][0] != NULL; i++) { + snd_soc_dapm_connect_input(codec, audio_map[i][0], + audio_map[i][1], audio_map[i][2]); + } + + snd_soc_dapm_sync_endpoints(codec); + return 0; +} + +/* + * BT Codec DAI + */ +static struct snd_soc_cpu_dai bt_dai = +{ .name = "Bluetooth", + .id = 0, + .type = SND_SOC_DAI_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_dai_link neo1973_dai[] = { +{ /* Hifi Playback - for similatious use with voice below */ + .name = "WM8753", + .stream_name = "WM8753 HiFi", + .cpu_dai = &s3c24xx_i2s_dai, + .codec_dai = &wm8753_dai[WM8753_DAI_HIFI], + .init = neo1973_wm8753_init, + .ops = &neo1973_hifi_ops, +}, +{ /* Voice via BT */ + .name = "Bluetooth", + .stream_name = "Voice", + .cpu_dai = &bt_dai, + .codec_dai = &wm8753_dai[WM8753_DAI_VOICE], + .ops = &neo1973_voice_ops, +}, +}; + +static struct snd_soc_machine neo1973 = { + .name = "neo1973", + .dai_link = neo1973_dai, + .num_links = ARRAY_SIZE(neo1973_dai), +}; + +static struct wm8753_setup_data neo1973_wm8753_setup = { + .i2c_address = 0x1a, +}; + +static struct snd_soc_device neo1973_snd_devdata = { + .machine = &neo1973, + .platform = &s3c24xx_soc_platform, + .codec_dev = &soc_codec_dev_wm8753, + .codec_data = &neo1973_wm8753_setup, +}; + +static struct i2c_client client_template; + +static unsigned short normal_i2c[] = { 0x7C, I2C_CLIENT_END }; + +/* Magic definition of all other variables and things */ +I2C_CLIENT_INSMOD; + +static int lm4857_amp_probe(struct i2c_adapter *adap, int addr, int kind) +{ + int ret; + + client_template.adapter = adap; + client_template.addr = addr; + + i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); + if (i2c == NULL) + return -ENOMEM; + + ret = i2c_attach_client(i2c); + if (ret < 0) { + printk(KERN_ERR "LM4857 failed to attach at addr %x\n", addr); + goto exit_err; + } + + lm4857_write_regs(); + return ret; + +exit_err: + kfree(i2c); + return ret; +} + +static int lm4857_i2c_detach(struct i2c_client *client) +{ + i2c_detach_client(client); + kfree(client); + return 0; +} + +static int lm4857_i2c_attach(struct i2c_adapter *adap) +{ + return i2c_probe(adap, &addr_data, lm4857_amp_probe); +} + +/* corgi i2c codec control layer */ +static struct i2c_driver lm4857_i2c_driver = { + .driver = { + .name = "LM4857 I2C Amp", + .owner = THIS_MODULE, + }, + .id = I2C_DRIVERID_LM4857, + .attach_adapter = lm4857_i2c_attach, + .detach_client = lm4857_i2c_detach, + .command = NULL, +}; + +static struct i2c_client client_template = { + .name = "LM4857", + .driver = &lm4857_i2c_driver, +}; + +static struct platform_device *neo1973_snd_device; + +static int __init neo1973_init(void) +{ + int ret; + + neo1973_snd_device = platform_device_alloc("soc-audio", -1); + if (!neo1973_snd_device) + return -ENOMEM; + + platform_set_drvdata(neo1973_snd_device, &neo1973_snd_devdata); + neo1973_snd_devdata.dev = &neo1973_snd_device->dev; + ret = platform_device_add(neo1973_snd_device); + + if (ret) + platform_device_put(neo1973_snd_device); + + ret = i2c_add_driver(&lm4857_i2c_driver); + if (ret != 0) + printk(KERN_ERR "can't add i2c driver"); + + return ret; +} + +static void __exit neo1973_exit(void) +{ + platform_device_unregister(neo1973_snd_device); +} + +module_init(neo1973_init); +module_exit(neo1973_exit); + +/* Module information */ +MODULE_AUTHOR("Graeme Gregory, graeme.gregory@wolfsonmicro.com, www.wolfsonmicro.com"); +MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973"); +MODULE_LICENSE("GPL"); diff -Nur sound/soc/s3c24xx/s3c2443-ac97.c sound/soc/s3c24xx/s3c2443-ac97.c --- sound/soc/s3c24xx/s3c2443-ac97.c 1970-01-01 01:00:00.000000000 +0100 +++ sound/soc/s3c24xx/s3c2443-ac97.c 2007-05-15 02:00:09.000000000 +0200 @@ -0,0 +1,401 @@ +/* + * s3c2443-ac97.c -- ALSA Soc Audio Layer + * + * (c) 2007 Wolfson Microelectronics PLC. + * Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com + * + * Copyright (C) 2005, Sean Choi + * 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. + * + * Revision history + * 21st Mar 2007 Initial Version + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "s3c24xx-pcm.h" +#include "s3c24xx-ac97.h" + +struct s3c24xx_ac97_info { + void __iomem *regs; + struct clk *ac97_clk; +}; +static struct s3c24xx_ac97_info s3c24xx_ac97; + +DECLARE_COMPLETION(ac97_completion); +static u32 codec_ready; +static DECLARE_MUTEX(ac97_mutex); + +static unsigned short s3c2443_ac97_read(struct snd_ac97 *ac97, + unsigned short reg) +{ + u32 ac_glbctrl; + u32 ac_codec_cmd; + u32 stat, addr, data; + + down(&ac97_mutex); + + codec_ready = S3C_AC97_GLBSTAT_CODECREADY; + ac_codec_cmd = readl(s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD); + ac_codec_cmd = S3C_AC97_CODEC_CMD_READ | AC_CMD_ADDR(reg); + writel(ac_codec_cmd, s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD); + + udelay(50); + + ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); + ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE; + writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); + + wait_for_completion(&ac97_completion); + + stat = readl(s3c24xx_ac97.regs + S3C_AC97_STAT); + addr = (stat >> 16) & 0x7f; + data = (stat & 0xffff); + + if (addr != reg) + printk(KERN_ERR "s3c24xx-ac97: req addr = %02x," + " rep addr = %02x\n", reg, addr); + + up(&ac97_mutex); + + return (unsigned short)data; +} + +static void s3c2443_ac97_write(struct snd_ac97 *ac97, unsigned short reg, + unsigned short val) +{ + u32 ac_glbctrl; + u32 ac_codec_cmd; + + down(&ac97_mutex); + + codec_ready = S3C_AC97_GLBSTAT_CODECREADY; + ac_codec_cmd = readl(s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD); + ac_codec_cmd = AC_CMD_ADDR(reg) | AC_CMD_DATA(val); + writel(ac_codec_cmd, s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD); + + udelay(50); + + ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); + ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE; + writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); + + wait_for_completion(&ac97_completion); + + ac_codec_cmd = readl(s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD); + ac_codec_cmd |= S3C_AC97_CODEC_CMD_READ; + writel(ac_codec_cmd, s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD); + + up(&ac97_mutex); + +} + +static void s3c2443_ac97_warm_reset(struct snd_ac97 *ac97) +{ + u32 ac_glbctrl; + + ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); + ac_glbctrl = S3C_AC97_GLBCTRL_WARMRESET; + writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); + msleep(1); + + ac_glbctrl = 0; + writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); + msleep(1); +} + +static void s3c2443_ac97_cold_reset(struct snd_ac97 *ac97) +{ + u32 ac_glbctrl; + + ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); + ac_glbctrl = S3C_AC97_GLBCTRL_COLDRESET; + writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); + msleep(1); + + ac_glbctrl = 0; + writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); + msleep(1); + + ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); + ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON; + writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); + msleep(1); + + ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE; + writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); + msleep(1); + + ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA | + S3C_AC97_GLBCTRL_PCMINTM_DMA | S3C_AC97_GLBCTRL_MICINTM_DMA; + writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); +} + +static irqreturn_t s3c2443_ac97_irq(int irq, void *dev_id) +{ + int status; + u32 ac_glbctrl; + + status = readl(s3c24xx_ac97.regs + S3C_AC97_GLBSTAT) & codec_ready; + + if (status) { + ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); + ac_glbctrl &= ~S3C_AC97_GLBCTRL_CODECREADYIE; + writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); + complete(&ac97_completion); + } + return IRQ_HANDLED; +} + +struct snd_ac97_bus_ops soc_ac97_ops = { + .read = s3c2443_ac97_read, + .write = s3c2443_ac97_write, + .warm_reset = s3c2443_ac97_warm_reset, + .reset = s3c2443_ac97_cold_reset, +}; + +static struct s3c2410_dma_client s3c2443_dma_client_out = { + .name = "AC97 PCM Stereo out" +}; + +static struct s3c2410_dma_client s3c2443_dma_client_in = { + .name = "AC97 PCM Stereo in" +}; + +static struct s3c2410_dma_client s3c2443_dma_client_micin = { + .name = "AC97 Mic Mono in" +}; + +static struct s3c24xx_pcm_dma_params s3c2443_ac97_pcm_stereo_out = { + .client = &s3c2443_dma_client_out, + .channel = DMACH_PCM_OUT, + .dma_addr = S3C2440_PA_AC97 + S3C_AC97_PCM_DATA, + .dma_size = 4, +}; + +static struct s3c24xx_pcm_dma_params s3c2443_ac97_pcm_stereo_in = { + .client = &s3c2443_dma_client_in, + .channel = DMACH_PCM_IN, + .dma_addr = S3C2440_PA_AC97 + S3C_AC97_PCM_DATA, + .dma_size = 4, +}; + +static struct s3c24xx_pcm_dma_params s3c2443_ac97_mic_mono_in = { + .client = &s3c2443_dma_client_micin, + .channel = DMACH_MIC_IN, + .dma_addr = S3C2440_PA_AC97 + S3C_AC97_MIC_DATA, + .dma_size = 4, +}; + +static int s3c2443_ac97_probe(struct platform_device *pdev) +{ + int ret; + u32 ac_glbctrl; + + s3c24xx_ac97.regs = ioremap(S3C2440_PA_AC97, 0x100); + if (s3c24xx_ac97.regs == NULL) + return -ENXIO; + + s3c24xx_ac97.ac97_clk = clk_get(&pdev->dev, "ac97"); + if (s3c24xx_ac97.ac97_clk == NULL) { + printk(KERN_ERR "s3c2443-ac97 failed to get ac97_clock\n"); + iounmap(s3c24xx_ac97.regs); + return -ENODEV; + } + clk_enable(s3c24xx_ac97.ac97_clk); + + s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2443_GPE0_AC_nRESET); + s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2443_GPE1_AC_SYNC); + s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2443_GPE2_AC_BITCLK); + s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2443_GPE3_AC_SDI); + s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2443_GPE4_AC_SDO); + + ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); + ac_glbctrl = S3C_AC97_GLBCTRL_COLDRESET; + writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); + msleep(1); + + ac_glbctrl = 0; + writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); + msleep(1); + + ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); + ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON; + writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); + msleep(1); + + ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE; + writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); + + ret = request_irq(IRQ_S3C2443_AC97, s3c2443_ac97_irq, + IRQF_DISABLED, "AC97", NULL); + if (ret < 0) { + printk(KERN_ERR "s3c24xx-ac97: interrupt request failed.\n"); + clk_disable(s3c24xx_ac97.ac97_clk); + clk_put(s3c24xx_ac97.ac97_clk); + iounmap(s3c24xx_ac97.regs); + } + return ret; +} + +static void s3c2443_ac97_remove(struct platform_device *pdev) +{ + free_irq(IRQ_S3C2443_AC97, NULL); + clk_disable(s3c24xx_ac97.ac97_clk); + clk_put(s3c24xx_ac97.ac97_clk); + iounmap(s3c24xx_ac97.regs); +} + +static int s3c2443_ac97_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_cpu_dai *cpu_dai = rtd->dai->cpu_dai; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + cpu_dai->dma_data = &s3c2443_ac97_pcm_stereo_out; + else + cpu_dai->dma_data = &s3c2443_ac97_pcm_stereo_in; + + return 0; +} + +static int s3c2443_ac97_trigger(struct snd_pcm_substream *substream, int cmd) +{ + u32 ac_glbctrl; + + ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); + switch(cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA; + else + ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK; + else + ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMOUTTM_MASK; + break; + } + writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); + + return 0; +} + +static int s3c2443_ac97_hw_mic_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + return -ENODEV; + else + cpu_dai->dma_data = &s3c2443_ac97_mic_mono_in; + + return 0; +} + +static int s3c2443_ac97_mic_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + u32 ac_glbctrl; + + ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); + switch(cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK; + } + writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL); + + return 0; +} + +#define s3c2443_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \ + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) + +struct snd_soc_cpu_dai s3c2443_ac97_dai[] = { +{ + .name = "s3c2443-ac97", + .id = 0, + .type = SND_SOC_DAI_AC97, + .probe = s3c2443_ac97_probe, + .remove = s3c2443_ac97_remove, + .playback = { + .stream_name = "AC97 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = s3c2443_AC97_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .capture = { + .stream_name = "AC97 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = s3c2443_AC97_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .ops = { + .hw_params = s3c2443_ac97_hw_params, + .trigger = s3c2443_ac97_trigger}, +}, +{ + .name = "pxa2xx-ac97-mic", + .id = 1, + .type = SND_SOC_DAI_AC97, + .capture = { + .stream_name = "AC97 Mic Capture", + .channels_min = 1, + .channels_max = 1, + .rates = s3c2443_AC97_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .ops = { + .hw_params = s3c2443_ac97_hw_mic_params, + .trigger = s3c2443_ac97_mic_trigger,}, +}, +}; + +EXPORT_SYMBOL_GPL(s3c2443_ac97_dai); +EXPORT_SYMBOL_GPL(soc_ac97_ops); + +MODULE_AUTHOR("Graeme Gregory"); +MODULE_DESCRIPTION("AC97 driver for the Samsung s3c2443 chip"); +MODULE_LICENSE("GPL"); diff -Nur sound/soc/s3c24xx/s3c24xx-ac97.h sound/soc/s3c24xx/s3c24xx-ac97.h --- sound/soc/s3c24xx/s3c24xx-ac97.h 1970-01-01 01:00:00.000000000 +0100 +++ sound/soc/s3c24xx/s3c24xx-ac97.h 2007-05-15 02:00:09.000000000 +0200 @@ -0,0 +1,25 @@ +/* + * s3c24xx-ac97.c -- ALSA Soc Audio Layer + * + * (c) 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 + * 10th Nov 2006 Initial version. + */ + +#ifndef S3C24XXAC97_H_ +#define S3C24XXAC97_H_ + +#define AC_CMD_ADDR(x) (x << 16) +#define AC_CMD_DATA(x) (x & 0xffff) + +extern struct snd_soc_cpu_dai s3c2443_ac97_dai[]; + +#endif /*S3C24XXAC97_H_*/ diff -Nur sound/soc/s3c24xx/s3c24xx-i2s.c sound/soc/s3c24xx/s3c24xx-i2s.c --- sound/soc/s3c24xx/s3c24xx-i2s.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/soc/s3c24xx/s3c24xx-i2s.c 2007-08-02 02:00:06.000000000 +0200 @@ -344,11 +344,11 @@ DBG("Entered %s\n", __FUNCTION__); switch (div_id) { - case S3C24XX_DIV_MCLK: + case S3C24XX_DIV_BCLK: reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~S3C2410_IISMOD_FS_MASK; writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD); break; - case S3C24XX_DIV_BCLK: + case S3C24XX_DIV_MCLK: reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~(S3C2410_IISMOD_384FS); writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD); break; @@ -385,6 +385,7 @@ s3c24xx_i2s.iis_clk=clk_get(&pdev->dev, "iis"); if (s3c24xx_i2s.iis_clk == NULL) { DBG("failed to get iis_clock\n"); + iounmap(s3c24xx_i2s.regs); return -ENODEV; } clk_enable(s3c24xx_i2s.iis_clk); diff -Nur sound/soc/s3c24xx/s3c24xx-pcm.c sound/soc/s3c24xx/s3c24xx-pcm.c --- sound/soc/s3c24xx/s3c24xx-pcm.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/soc/s3c24xx/s3c24xx-pcm.c 2007-07-25 02:00:06.000000000 +0200 @@ -158,18 +158,22 @@ if (!dma) return 0; - /* prepare DMA */ - prtd->params = dma; - - DBG("params %p, client %p, channel %d\n", prtd->params, - prtd->params->client, prtd->params->channel); - - ret = s3c2410_dma_request(prtd->params->channel, - prtd->params->client, NULL); - - if (ret) { - DBG(KERN_ERR "failed to get dma channel\n"); - return ret; + /* this may get called several times by oss emulation + * with different params -HW */ + if (prtd->params == NULL) { + /* prepare DMA */ + prtd->params = dma; + + DBG("params %p, client %p, channel %d\n", prtd->params, + prtd->params->client, prtd->params->channel); + + ret = s3c2410_dma_request(prtd->params->channel, + prtd->params->client, NULL); + + if (ret) { + DBG(KERN_ERR "failed to get dma channel\n"); + return ret; + } } /* channel needs configuring for mem=>device, increment memory addr, diff -Nur sound/soc/s3c24xx/smdk2443_wm9710.c sound/soc/s3c24xx/smdk2443_wm9710.c --- sound/soc/s3c24xx/smdk2443_wm9710.c 1970-01-01 01:00:00.000000000 +0100 +++ sound/soc/s3c24xx/smdk2443_wm9710.c 2007-05-15 02:00:09.000000000 +0200 @@ -0,0 +1,85 @@ +/* + * smdk2443_wm9710.c -- SoC audio for smdk2443 + * + * 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 + * 8th Mar 2007 Initial version. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "../codecs/ac97.h" +#include "s3c24xx-pcm.h" +#include "s3c24xx-ac97.h" + +static struct snd_soc_machine smdk2443; + +static struct snd_soc_dai_link smdk2443_dai[] = { +{ + .name = "AC97", + .stream_name = "AC97 HiFi", + .cpu_dai = &s3c2443_ac97_dai[0], + .codec_dai = &ac97_dai, +}, +}; + +static struct snd_soc_machine smdk2443 = { + .name = "SMDK2443", + .dai_link = smdk2443_dai, + .num_links = ARRAY_SIZE(smdk2443_dai), +}; + +static struct snd_soc_device smdk2443_snd_ac97_devdata = { + .machine = &smdk2443, + .platform = &s3c24xx_soc_platform, + .codec_dev = &soc_codec_dev_ac97, +}; + +static struct platform_device *smdk2443_snd_ac97_device; + +static int __init smdk2443_init(void) +{ + int ret; + + smdk2443_snd_ac97_device = platform_device_alloc("soc-audio", -1); + if (!smdk2443_snd_ac97_device) + return -ENOMEM; + + platform_set_drvdata(smdk2443_snd_ac97_device, + &smdk2443_snd_ac97_devdata); + smdk2443_snd_ac97_devdata.dev = &smdk2443_snd_ac97_device->dev; + ret = platform_device_add(smdk2443_snd_ac97_device); + + if (ret) + platform_device_put(smdk2443_snd_ac97_device); + + return ret; +} + +static void __exit smdk2443_exit(void) +{ + platform_device_unregister(smdk2443_snd_ac97_device); +} + +module_init(smdk2443_init); +module_exit(smdk2443_exit); + +/* Module information */ +MODULE_AUTHOR("Graeme Gregory, graeme.gregory@wolfsonmicro.com, www.wolfsonmicro.com"); +MODULE_DESCRIPTION("ALSA SoC WM9710 SMDK2443"); +MODULE_LICENSE("GPL"); diff -Nur sound/soc/sh/Kconfig sound/soc/sh/Kconfig --- sound/soc/sh/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ sound/soc/sh/Kconfig 2007-06-09 02:00:13.000000000 +0200 @@ -0,0 +1,38 @@ +menu "SoC Audio support for SuperH" + +config SND_SOC_PCM_SH7760 + tristate "SoC Audio support for Renesas SH7760" + depends on CPU_SUBTYPE_SH7760 && SND_SOC && SH_DMABRG + help + Enable this option for SH7760 AC97/I2S audio support. + + +## +## Audio unit modules +## + +config SND_SOC_SH4_HAC + select AC97_BUS + select SND_SOC_AC97_BUS + select SND_AC97_CODEC + tristate + +config SND_SOC_SH4_SSI + tristate + + + +## +## Boards +## + +config SND_SH7760_AC97 + tristate "SH7760 AC97 sound support" + depends on CPU_SUBTYPE_SH7760 && SND_SOC_PCM_SH7760 + select SND_SOC_SH4_HAC + select SND_SOC_AC97_CODEC + help + This option enables generic sound support for the first + AC97 unit of the SH7760. + +endmenu diff -Nur sound/soc/sh/Makefile sound/soc/sh/Makefile --- sound/soc/sh/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ sound/soc/sh/Makefile 2007-05-15 02:00:09.000000000 +0200 @@ -0,0 +1,14 @@ +## DMA engines +snd-soc-dma-sh7760-objs := dma-sh7760.o +obj-$(CONFIG_SND_SOC_PCM_SH7760) += snd-soc-dma-sh7760.o + +## audio units found on some SH-4 +snd-soc-hac-objs := hac.o +snd-soc-ssi-objs := ssi.o +obj-$(CONFIG_SND_SOC_SH4_HAC) += snd-soc-hac.o +obj-$(CONFIG_SND_SOC_SH4_SSI) += snd-soc-ssi.o + +## boards +snd-soc-sh7760-ac97-objs := sh7760-ac97.o + +obj-$(CONFIG_SND_SH7760_AC97) += snd-soc-sh7760-ac97.o diff -Nur sound/soc/sh/dma-sh7760.c sound/soc/sh/dma-sh7760.c --- sound/soc/sh/dma-sh7760.c 1970-01-01 01:00:00.000000000 +0100 +++ sound/soc/sh/dma-sh7760.c 2007-05-15 02:00:09.000000000 +0200 @@ -0,0 +1,354 @@ +/* + * SH7760 ("camelot") DMABRG audio DMA unit support + * + * Copyright (C) 2007 Manuel Lauss + * licensed under the terms outlined in the file COPYING at the root + * of the linux kernel sources. + * + * The SH7760 DMABRG provides 4 dma channels (2x rec, 2x play), which + * trigger an interrupt when one half of the programmed transfer size + * has been xmitted. + * + * FIXME: little-endian only for now + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* registers and bits */ +#define BRGATXSAR 0x00 +#define BRGARXDAR 0x04 +#define BRGATXTCR 0x08 +#define BRGARXTCR 0x0C +#define BRGACR 0x10 +#define BRGATXTCNT 0x14 +#define BRGARXTCNT 0x18 + +#define ACR_RAR (1 << 18) +#define ACR_RDS (1 << 17) +#define ACR_RDE (1 << 16) +#define ACR_TAR (1 << 2) +#define ACR_TDS (1 << 1) +#define ACR_TDE (1 << 0) + +/* receiver/transmitter data alignment */ +#define ACR_RAM_NONE (0 << 24) +#define ACR_RAM_4BYTE (1 << 24) +#define ACR_RAM_2WORD (2 << 24) +#define ACR_TAM_NONE (0 << 8) +#define ACR_TAM_4BYTE (1 << 8) +#define ACR_TAM_2WORD (2 << 8) + + +struct camelot_pcm { + unsigned long mmio; /* DMABRG audio channel control reg MMIO */ + unsigned int txid; /* ID of first DMABRG IRQ for this unit */ + + struct snd_pcm_substream *tx_ss; + unsigned long tx_period_size; + unsigned int tx_period; + + struct snd_pcm_substream *rx_ss; + unsigned long rx_period_size; + unsigned int rx_period; + +} cam_pcm_data[2] = { + { + .mmio = 0xFE3C0040, + .txid = DMABRGIRQ_A0TXF, + }, + { + .mmio = 0xFE3C0060, + .txid = DMABRGIRQ_A1TXF, + }, +}; + +#define BRGREG(x) (*(unsigned long *)(cam->mmio + (x))) + +/* + * set a minimum of 16kb per period, to avoid interrupt-"storm" and + * resulting skipping. In general, the bigger the minimum size, the + * better for overall system performance. (The SH7760 is a puny CPU + * with a slow SDRAM interface and poor internal bus bandwidth, + * *especially* when the LCDC is active). The minimum for the DMAC + * is 8 bytes; 16kbytes are enough to get skip-free playback of a + * 44kHz/16bit/stereo MP3 on a lightly loaded system, and maintain + * reasonable responsiveness in MPlayer. + */ +#define DMABRG_PERIOD_MIN 16 * 1024 +#define DMABRG_PERIOD_MAX 0x03fffffc +#define DMABRG_PREALLOC_BUFFER 32 * 1024 +#define DMABRG_PREALLOC_BUFFER_MAX 32 * 1024 + +/* support everything the SSI supports */ +#define DMABRG_RATES \ + SNDRV_PCM_RATE_8000_192000 + +#define DMABRG_FMTS \ + (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | \ + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3LE | \ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE) + +static struct snd_pcm_hardware camelot_pcm_hardware = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = DMABRG_FMTS, + .rates = DMABRG_RATES, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 2, + .channels_max = 8, /* max of the SSI */ + .buffer_bytes_max = DMABRG_PERIOD_MAX, + .period_bytes_min = DMABRG_PERIOD_MIN, + .period_bytes_max = DMABRG_PERIOD_MAX / 2, + .periods_min = 2, + .periods_max = 2, + .fifo_size = 128, +}; + +static void camelot_txdma(void *data) +{ + struct camelot_pcm *cam = data; + cam->tx_period ^= 1; + snd_pcm_period_elapsed(cam->tx_ss); +} + +static void camelot_rxdma(void *data) +{ + struct camelot_pcm *cam = data; + cam->rx_period ^= 1; + snd_pcm_period_elapsed(cam->rx_ss); +} + +static int camelot_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id]; + int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1; + int ret, dmairq; + + snd_soc_set_runtime_hwparams(substream, &camelot_pcm_hardware); + + /* DMABRG buffer half/full events */ + dmairq = (recv) ? cam->txid + 2 : cam->txid; + if (recv) { + cam->rx_ss = substream; + ret = dmabrg_request_irq(dmairq, camelot_rxdma, cam); + if (unlikely(ret)) { + pr_debug("audio unit %d irqs already taken!\n", + rtd->dai->cpu_dai->id); + return -EBUSY; + } + (void)dmabrg_request_irq(dmairq + 1,camelot_rxdma, cam); + } else { + cam->tx_ss = substream; + ret = dmabrg_request_irq(dmairq, camelot_txdma, cam); + if (unlikely(ret)) { + pr_debug("audio unit %d irqs already taken!\n", + rtd->dai->cpu_dai->id); + return -EBUSY; + } + (void)dmabrg_request_irq(dmairq + 1, camelot_txdma, cam); + } + return 0; +} + +static int camelot_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id]; + int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1; + int dmairq; + + dmairq = (recv) ? cam->txid + 2 : cam->txid; + + if (recv) + cam->rx_ss = NULL; + else + cam->tx_ss = NULL; + + dmabrg_free_irq(dmairq + 1); + dmabrg_free_irq(dmairq); + + return 0; +} + +static int camelot_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id]; + int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1; + int ret; + + ret = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + if (ret < 0) + return ret; + + if (recv) { + cam->rx_period_size = params_period_bytes(hw_params); + cam->rx_period = 0; + } else { + cam->tx_period_size = params_period_bytes(hw_params); + cam->tx_period = 0; + } + return 0; +} + +static int camelot_hw_free(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int camelot_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id]; + + pr_debug("PCM data: addr 0x%08ulx len %d\n", + (u32)runtime->dma_addr, runtime->dma_bytes); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + BRGREG(BRGATXSAR) = (unsigned long)runtime->dma_area; + BRGREG(BRGATXTCR) = runtime->dma_bytes; + } else { + BRGREG(BRGARXDAR) = (unsigned long)runtime->dma_area; + BRGREG(BRGARXTCR) = runtime->dma_bytes; + } + + return 0; +} + +static inline void dmabrg_play_dma_start(struct camelot_pcm *cam) +{ + unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS); + /* start DMABRG engine: XFER start, auto-addr-reload */ + BRGREG(BRGACR) = acr | ACR_TDE | ACR_TAR | ACR_TAM_2WORD; +} + +static inline void dmabrg_play_dma_stop(struct camelot_pcm *cam) +{ + unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS); + /* forcibly terminate data transmission */ + BRGREG(BRGACR) = acr | ACR_TDS; +} + +static inline void dmabrg_rec_dma_start(struct camelot_pcm *cam) +{ + unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS); + /* start DMABRG engine: recv start, auto-reload */ + BRGREG(BRGACR) = acr | ACR_RDE | ACR_RAR | ACR_RAM_2WORD; +} + +static inline void dmabrg_rec_dma_stop(struct camelot_pcm *cam) +{ + unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS); + /* forcibly terminate data receiver */ + BRGREG(BRGACR) = acr | ACR_RDS; +} + +static int camelot_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id]; + int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (recv) + dmabrg_rec_dma_start(cam); + else + dmabrg_play_dma_start(cam); + break; + case SNDRV_PCM_TRIGGER_STOP: + if (recv) + dmabrg_rec_dma_stop(cam); + else + dmabrg_play_dma_stop(cam); + break; + default: + return -EINVAL; + } + + return 0; +} + +static snd_pcm_uframes_t camelot_pos(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id]; + int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1; + unsigned long pos; + + /* cannot use the DMABRG pointer register: under load, by the + * time ALSA comes around to read the register, it is already + * far ahead (or worse, already done with the fragment) of the + * position at the time the IRQ was triggered, which results in + * fast-playback sound in my test application (ScummVM) + */ + if (recv) + pos = cam->rx_period ? cam->rx_period_size : 0; + else + pos = cam->tx_period ? cam->tx_period_size : 0; + + return bytes_to_frames(runtime, pos); +} + +static struct snd_pcm_ops camelot_pcm_ops = { + .open = camelot_pcm_open, + .close = camelot_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = camelot_hw_params, + .hw_free = camelot_hw_free, + .prepare = camelot_prepare, + .trigger = camelot_trigger, + .pointer = camelot_pos, +}; + +static void camelot_pcm_free(struct snd_pcm *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int camelot_pcm_new(struct snd_card *card, + struct snd_soc_codec_dai *dai, + struct snd_pcm *pcm) +{ + /* dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel + * in MMAP mode (i.e. aplay -M) + */ + snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + DMABRG_PREALLOC_BUFFER, DMABRG_PREALLOC_BUFFER_MAX); + + return 0; +} + +struct snd_soc_platform sh7760_soc_platform = { + .name = "sh7760-pcm", + .pcm_ops = &camelot_pcm_ops, + .pcm_new = camelot_pcm_new, + .pcm_free = camelot_pcm_free, +}; +EXPORT_SYMBOL_GPL(sh7760_soc_platform); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SH7760 Audio DMA (DMABRG) driver"); +MODULE_AUTHOR("Manuel Lauss "); diff -Nur sound/soc/sh/hac.c sound/soc/sh/hac.c --- sound/soc/sh/hac.c 1970-01-01 01:00:00.000000000 +0100 +++ sound/soc/sh/hac.c 2007-05-15 02:00:09.000000000 +0200 @@ -0,0 +1,322 @@ +/* + * Hitachi Audio Controller (AC97) support for SH7760/SH7780 + * + * Copyright (c) 2007 Manuel Lauss + * licensed under the terms outlined in the file COPYING at the root + * of the linux kernel sources. + * + * dont forget to set IPSEL/OMSEL register bits (in your board code) to + * enable HAC output pins! + */ + +/* BIG FAT FIXME: although the SH7760 has 2 independent AC97 units, only + * the FIRST can be used since ASoC does not pass any information to the + * ac97_read/write() functions regarding WHICH unit to use. You'll have + * to edit the code a bit to use the other AC97 unit. --mlau + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* regs and bits */ +#define HACCR 0x08 +#define HACCSAR 0x20 +#define HACCSDR 0x24 +#define HACPCML 0x28 +#define HACPCMR 0x2C +#define HACTIER 0x50 +#define HACTSR 0x54 +#define HACRIER 0x58 +#define HACRSR 0x5C +#define HACACR 0x60 + +#define CR_CR (1 << 15) /* "codec-ready" indicator */ +#define CR_CDRT (1 << 11) /* cold reset */ +#define CR_WMRT (1 << 10) /* warm reset */ +#define CR_B9 (1 << 9) /* the mysterious "bit 9" */ +#define CR_ST (1 << 5) /* AC97 link start bit */ + +#define CSAR_RD (1 << 19) /* AC97 data read bit */ +#define CSAR_WR (0) + +#define TSR_CMDAMT (1 << 31) +#define TSR_CMDDMT (1 << 30) + +#define RSR_STARY (1 << 22) +#define RSR_STDRY (1 << 21) + +#define ACR_DMARX16 (1 << 30) +#define ACR_DMATX16 (1 << 29) +#define ACR_TX12ATOM (1 << 26) +#define ACR_DMARX20 ((1 << 24) | (1 << 22)) +#define ACR_DMATX20 ((1 << 23) | (1 << 21)) + +#define CSDR_SHIFT 4 +#define CSDR_MASK (0xffff << CSDR_SHIFT) +#define CSAR_SHIFT 12 +#define CSAR_MASK (0x7f << CSAR_SHIFT) + +#define AC97_WRITE_RETRY 1 +#define AC97_READ_RETRY 5 + +/* manual-suggested AC97 codec access timeouts (us) */ +#define TMO_E1 500 /* 21 < E1 < 1000 */ +#define TMO_E2 13 /* 13 < E2 */ +#define TMO_E3 21 /* 21 < E3 */ +#define TMO_E4 500 /* 21 < E4 < 1000 */ + +struct hac_priv { + unsigned long mmio; /* HAC base address */ +} hac_cpu_data[] = { +#if defined(CONFIG_CPU_SUBTYPE_SH7760) + { + .mmio = 0xFE240000, + }, + { + .mmio = 0xFE250000, + }, +#elif defined(CONFIG_CPU_SUBTYPE_SH7780) + { + .mmio = 0xFFE40000, + }, +#else +#error "Unsupported SuperH SoC" +#endif +}; + +#define HACREG(reg) (*(unsigned long *)(hac->mmio + (reg))) + +/* + * AC97 read/write flow as outlined in the SH7760 manual (pages 903-906) + */ +static int hac_get_codec_data(struct hac_priv *hac, unsigned short r, + unsigned short *v) +{ + unsigned int to1, to2, i; + unsigned short adr; + + for (i = 0; i < AC97_READ_RETRY; ++i) { + *v = 0; + /* wait for HAC to receive something from the codec */ + for (to1 = TMO_E4; + to1 && !(HACREG(HACRSR) & RSR_STARY); + --to1) + udelay(1); + for (to2 = TMO_E4; + to2 && !(HACREG(HACRSR) & RSR_STDRY); + --to2) + udelay(1); + + if (!to1 && !to2) + return 0; /* codec comm is down */ + + adr = ((HACREG(HACCSAR) & CSAR_MASK) >> CSAR_SHIFT); + *v = ((HACREG(HACCSDR) & CSDR_MASK) >> CSDR_SHIFT); + + HACREG(HACRSR) &= ~(RSR_STDRY | RSR_STARY); + + if (r == adr) + break; + + /* manual says: wait at least 21 usec before retrying */ + udelay(21); + } + HACREG(HACRSR) &= ~(RSR_STDRY | RSR_STARY); + return (i < AC97_READ_RETRY); +} + +static unsigned short hac_read_codec_aux(struct hac_priv *hac, + unsigned short reg) +{ + unsigned short val; + unsigned int i, to; + + for (i = 0; i < AC97_READ_RETRY; i++) { + /* send_read_request */ + local_irq_disable(); + HACREG(HACTSR) &= ~(TSR_CMDAMT); + HACREG(HACCSAR) = (reg << CSAR_SHIFT) | CSAR_RD; + local_irq_enable(); + + for (to = TMO_E3; + to && !(HACREG(HACTSR) & TSR_CMDAMT); + --to) + udelay(1); + + HACREG(HACTSR) &= ~TSR_CMDAMT; + val = 0; + if (hac_get_codec_data(hac, reg, &val) != 0) + break; + } + + if (i == AC97_READ_RETRY) + return ~0; + + return val; +} + +static void hac_ac97_write(struct snd_ac97 *ac97, unsigned short reg, + unsigned short val) +{ + int unit_id = 0 /* ac97->private_data */; + struct hac_priv *hac = &hac_cpu_data[unit_id]; + unsigned int i, to; + /* write_codec_aux */ + for (i = 0; i < AC97_WRITE_RETRY; i++) { + /* send_write_request */ + local_irq_disable(); + HACREG(HACTSR) &= ~(TSR_CMDDMT | TSR_CMDAMT); + HACREG(HACCSDR) = (val << CSDR_SHIFT); + HACREG(HACCSAR) = (reg << CSAR_SHIFT) & (~CSAR_RD); + local_irq_enable(); + + /* poll-wait for CMDAMT and CMDDMT */ + for (to = TMO_E1; + to && !(HACREG(HACTSR) & (TSR_CMDAMT|TSR_CMDDMT)); + --to) + udelay(1); + + HACREG(HACTSR) &= ~(TSR_CMDAMT | TSR_CMDDMT); + if (to) + break; + /* timeout, try again */ + } +} + +static unsigned short hac_ac97_read(struct snd_ac97 *ac97, + unsigned short reg) +{ + int unit_id = 0 /* ac97->private_data */; + struct hac_priv *hac = &hac_cpu_data[unit_id]; + return hac_read_codec_aux(hac, reg); +} + +static void hac_ac97_warmrst(struct snd_ac97 *ac97) +{ + int unit_id = 0 /* ac97->private_data */; + struct hac_priv *hac = &hac_cpu_data[unit_id]; + unsigned int tmo; + + HACREG(HACCR) = CR_WMRT | CR_ST | CR_B9; + msleep(10); + HACREG(HACCR) = CR_ST | CR_B9; + for (tmo = 1000; (tmo > 0) && !(HACREG(HACCR) & CR_CR); tmo--) + udelay(1); + + if (!tmo) + printk(KERN_INFO "hac: reset: AC97 link down!\n"); + /* settings this bit lets us have a conversation with codec */ + HACREG(HACACR) |= ACR_TX12ATOM; +} + +static void hac_ac97_coldrst(struct snd_ac97 *ac97) +{ + int unit_id = 0 /* ac97->private_data */; + struct hac_priv *hac; + hac = &hac_cpu_data[unit_id]; + + HACREG(HACCR) = 0; + HACREG(HACCR) = CR_CDRT | CR_ST | CR_B9; + msleep(10); + hac_ac97_warmrst(ac97); +} + +struct snd_ac97_bus_ops soc_ac97_ops = { + .read = hac_ac97_read, + .write = hac_ac97_write, + .reset = hac_ac97_coldrst, + .warm_reset = hac_ac97_warmrst, +}; +EXPORT_SYMBOL_GPL(soc_ac97_ops); + +static int hac_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct hac_priv *hac = &hac_cpu_data[rtd->dai->cpu_dai->id]; + int d = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; + + switch (params->msbits) { + case 16: + HACREG(HACACR) |= d ? ACR_DMARX16 : ACR_DMATX16; + HACREG(HACACR) &= d ? ~ACR_DMARX20 : ~ACR_DMATX20; + break; + case 20: + HACREG(HACACR) &= d ? ~ACR_DMARX16 : ~ACR_DMATX16; + HACREG(HACACR) |= d ? ACR_DMARX20 : ACR_DMATX20; + break; + default: + pr_debug("hac: invalid depth %d bit\n", params->msbits); + return -EINVAL; + break; + } + + return 0; +} + +#define AC97_RATES \ + SNDRV_PCM_RATE_8000_192000 + +#define AC97_FMTS \ + SNDRV_PCM_FMTBIT_S16_LE + +struct snd_soc_cpu_dai sh4_hac_dai[] = { +{ + .name = "HAC0", + .id = 0, + .type = SND_SOC_DAI_AC97, + .playback = { + .rates = AC97_RATES, + .formats = AC97_FMTS, + .channels_min = 2, + .channels_max = 2, + }, + .capture = { + .rates = AC97_RATES, + .formats = AC97_FMTS, + .channels_min = 2, + .channels_max = 2, + }, + .ops = { + .hw_params = hac_hw_params, + }, +}, +#ifdef CONFIG_CPU_SUBTYPE_SH7760 +{ + .name = "HAC1", + .id = 1, + .type = SND_SOC_DAI_AC97, + .playback = { + .rates = AC97_RATES, + .formats = AC97_FMTS, + .channels_min = 2, + .channels_max = 2, + }, + .capture = { + .rates = AC97_RATES, + .formats = AC97_FMTS, + .channels_min = 2, + .channels_max = 2, + }, + .ops = { + .hw_params = hac_hw_params, + }, + +}, +#endif +}; +EXPORT_SYMBOL_GPL(sh4_hac_dai); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SuperH onchip HAC (AC97) audio driver"); +MODULE_AUTHOR("Manuel Lauss "); diff -Nur sound/soc/sh/sh7760-ac97.c sound/soc/sh/sh7760-ac97.c --- sound/soc/sh/sh7760-ac97.c 1970-01-01 01:00:00.000000000 +0100 +++ sound/soc/sh/sh7760-ac97.c 2007-05-15 02:00:09.000000000 +0200 @@ -0,0 +1,92 @@ +/* + * Generic AC97 sound support for SH7760 + * + * (c) 2007 Manuel Lauss + * + * Licensed under the GPLv2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../codecs/ac97.h" + +#define IPSEL 0xFE400034 + +/* platform specific structs can be declared here */ +extern struct snd_soc_cpu_dai sh4_hac_dai[2]; +extern struct snd_soc_platform sh7760_soc_platform; + +static int machine_init(struct snd_soc_codec *codec) +{ + snd_soc_dapm_sync_endpoints(codec); + return 0; +} + +static struct snd_soc_dai_link sh7760_ac97_dai = { + .name = "AC97", + .stream_name = "AC97 HiFi", + .cpu_dai = &sh4_hac_dai[0], /* HAC0 */ + .codec_dai = &ac97_dai, + .init = machine_init, + .ops = NULL, +}; + +static struct snd_soc_machine sh7760_ac97_soc_machine = { + .name = "SH7760 AC97", + .dai_link = &sh7760_ac97_dai, + .num_links = 1, +}; + +static struct snd_soc_device sh7760_ac97_snd_devdata = { + .machine = &sh7760_ac97_soc_machine, + .platform = &sh7760_soc_platform, + .codec_dev = &soc_codec_dev_ac97, +}; + +static struct platform_device *sh7760_ac97_snd_device; + +static int __init sh7760_ac97_init(void) +{ + int ret; + unsigned short ipsel; + + /* enable both AC97 controllers in pinmux reg */ + ipsel = ctrl_inw(IPSEL); + ctrl_outw(ipsel | (3 << 10), IPSEL); + + ret = -ENOMEM; + sh7760_ac97_snd_device = platform_device_alloc("soc-audio", -1); + if (!sh7760_ac97_snd_device) + goto out; + + platform_set_drvdata(sh7760_ac97_snd_device, + &sh7760_ac97_snd_devdata); + sh7760_ac97_snd_devdata.dev = &sh7760_ac97_snd_device->dev; + ret = platform_device_add(sh7760_ac97_snd_device); + + if (ret) + platform_device_put(sh7760_ac97_snd_device); + +out: + return ret; +} + +static void __exit sh7760_ac97_exit(void) +{ + platform_device_unregister(sh7760_ac97_snd_device); +} + +module_init(sh7760_ac97_init); +module_exit(sh7760_ac97_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic SH7760 AC97 sound machine"); +MODULE_AUTHOR("Manuel Lauss "); diff -Nur sound/soc/sh/ssi.c sound/soc/sh/ssi.c --- sound/soc/sh/ssi.c 1970-01-01 01:00:00.000000000 +0100 +++ sound/soc/sh/ssi.c 2007-05-15 02:00:09.000000000 +0200 @@ -0,0 +1,400 @@ +/* + * Serial Sound Interface (I2S) support for SH7760/SH7780 + * + * Copyright (c) 2007 Manuel Lauss + * + * licensed under the terms outlined in the file COPYING at the root + * of the linux kernel sources. + * + * dont forget to set IPSEL/OMSEL register bits (in your board code) to + * enable SSI output pins! + */ + +/* + * LIMITATIONS: + * The SSI unit has only one physical data line, so full duplex is + * impossible. This can be remedied on the SH7760 by using the + * other SSI unit for recording; however the SH7780 has only 1 SSI + * unit, and its pins are shared with the AC97 unit, among others. + * + * FEATURES: + * The SSI features "compressed mode": in this mode it continuously + * streams PCM data over the I2S lines and uses LRCK as a handshake + * signal. Can be used to send compressed data (AC3/DTS) to a DSP. + * The number of bits sent over the wire in a frame can be adjusted + * and can be independent from the actual sample bit depth. This is + * useful to support TDM mode codecs like the AD1939 which have a + * fixed TDM slot size, regardless of sample resolution. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SSICR 0x00 +#define SSISR 0x04 + +#define CR_DMAEN (1 << 28) +#define CR_CHNL_SHIFT 22 +#define CR_CHNL_MASK (3 << CR_CHNL_SHIFT) +#define CR_DWL_SHIFT 19 +#define CR_DWL_MASK (7 << CR_DWL_SHIFT) +#define CR_SWL_SHIFT 16 +#define CR_SWL_MASK (7 << CR_SWL_SHIFT) +#define CR_SCK_MASTER (1 << 15) /* bitclock master bit */ +#define CR_SWS_MASTER (1 << 14) /* wordselect master bit */ +#define CR_SCKP (1 << 13) /* I2Sclock polarity */ +#define CR_SWSP (1 << 12) /* LRCK polarity */ +#define CR_SPDP (1 << 11) +#define CR_SDTA (1 << 10) /* i2s alignment (msb/lsb) */ +#define CR_PDTA (1 << 9) /* fifo data alignment */ +#define CR_DEL (1 << 8) /* delay data by 1 i2sclk */ +#define CR_BREN (1 << 7) /* clock gating in burst mode */ +#define CR_CKDIV_SHIFT 4 +#define CR_CKDIV_MASK (7 << CR_CKDIV_SHIFT) /* bitclock divider */ +#define CR_MUTE (1 << 3) /* SSI mute */ +#define CR_CPEN (1 << 2) /* compressed mode */ +#define CR_TRMD (1 << 1) /* transmit/receive select */ +#define CR_EN (1 << 0) /* enable SSI */ + +#define SSIREG(reg) (*(unsigned long *)(ssi->mmio + (reg))) + +struct ssi_priv { + unsigned long mmio; + unsigned long sysclk; + int inuse; +} ssi_cpu_data[] = { +#if defined(CONFIG_CPU_SUBTYPE_SH7760) + { + .mmio = 0xFE680000, + }, + { + .mmio = 0xFE690000, + }, +#elif defined(CONFIG_CPU_SUBTYPE_SH7780) + { + .mmio = 0xFFE70000, + }, +#else +#error "Unsupported SuperH SoC" +#endif +}; + +/* + * track usage of the SSI; it is simplex-only so prevent attempts of + * concurrent playback + capture. FIXME: any locking required? + */ +static int ssi_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id]; + if (ssi->inuse) { + pr_debug("ssi: already in use!\n"); + return -EBUSY; + } else + ssi->inuse = 1; + return 0; +} + +static void ssi_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id]; + + ssi->inuse = 0; +} + +static int ssi_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id]; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + SSIREG(SSICR) |= CR_DMAEN | CR_EN; + break; + case SNDRV_PCM_TRIGGER_STOP: + SSIREG(SSICR) &= ~(CR_DMAEN | CR_EN); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ssi_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id]; + unsigned long ssicr = SSIREG(SSICR); + unsigned int bits, channels, swl, recv, i; + + channels = params_channels(params); + bits = params->msbits; + recv = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 0 : 1; + + pr_debug("ssi_hw_params() enter\nssicr was %08lx\n", ssicr); + pr_debug("bits: %d channels: %d\n", bits, channels); + + ssicr &= ~(CR_TRMD | CR_CHNL_MASK | CR_DWL_MASK | CR_PDTA | + CR_SWL_MASK); + + /* direction (send/receive) */ + if (!recv) + ssicr |= CR_TRMD; /* transmit */ + + /* channels */ + if ((channels < 2) || (channels > 8) || (channels & 1)) { + pr_debug("ssi: invalid number of channels\n"); + return -EINVAL; + } + ssicr |= ((channels >> 1) - 1) << CR_CHNL_SHIFT; + + /* DATA WORD LENGTH (DWL): databits in audio sample */ + i = 0; + switch (bits) { + case 32: ++i; + case 24: ++i; + case 22: ++i; + case 20: ++i; + case 18: ++i; + case 16: ++i; + ssicr |= i << CR_DWL_SHIFT; + case 8: break; + default: + pr_debug("ssi: invalid sample width\n"); + return -EINVAL; + } + + /* + * SYSTEM WORD LENGTH: size in bits of half a frame over the I2S + * wires. This is usually bits_per_sample x channels/2; i.e. in + * Stereo mode the SWL equals DWL. SWL can be bigger than the + * product of (channels_per_slot x samplebits), e.g. for codecs + * like the AD1939 which only accept 32bit wide TDM slots. For + * "standard" I2S operation we set SWL = chans / 2 * DWL here. + * Waiting for ASoC to get TDM support ;-) + */ + if ((bits > 16) && (bits <= 24)) { + bits = 24; /* these are padded by the SSI */ + /*ssicr |= CR_PDTA;*/ /* cpu/data endianness ? */ + } + i = 0; + swl = (bits * channels) / 2; + switch (swl) { + case 256: ++i; + case 128: ++i; + case 64: ++i; + case 48: ++i; + case 32: ++i; + case 16: ++i; + ssicr |= i << CR_SWL_SHIFT; + case 8: break; + default: + pr_debug("ssi: invalid system word length computed\n"); + return -EINVAL; + } + + SSIREG(SSICR) = ssicr; + + pr_debug("ssi_hw_params() leave\nssicr is now %08lx\n", ssicr); + return 0; +} + +static int ssi_set_sysclk(struct snd_soc_cpu_dai *cpu_dai, int clk_id, + unsigned int freq, int dir) +{ + struct ssi_priv *ssi = &ssi_cpu_data[cpu_dai->id]; + + ssi->sysclk = freq; + + return 0; +} + +/* + * This divider is used to generate the SSI_SCK (I2S bitclock) from the + * clock at the HAC_BIT_CLK ("oversampling clock") pin. + */ +static int ssi_set_clkdiv(struct snd_soc_cpu_dai *dai, int did, int div) +{ + struct ssi_priv *ssi = &ssi_cpu_data[dai->id]; + unsigned long ssicr; + int i; + + i = 0; + ssicr = SSIREG(SSICR) & ~CR_CKDIV_MASK; + switch (div) { + case 16: ++i; + case 8: ++i; + case 4: ++i; + case 2: ++i; + SSIREG(SSICR) = ssicr | (i << CR_CKDIV_SHIFT); + case 1: break; + default: + pr_debug("ssi: invalid sck divider %d\n", div); + return -EINVAL; + } + + return 0; +} + +static int ssi_set_fmt(struct snd_soc_cpu_dai *dai, unsigned int fmt) +{ + struct ssi_priv *ssi = &ssi_cpu_data[dai->id]; + unsigned long ssicr = SSIREG(SSICR); + + pr_debug("ssi_set_fmt()\nssicr was 0x%08lx\n", ssicr); + + ssicr &= ~(CR_DEL | CR_PDTA | CR_BREN | CR_SWSP | CR_SCKP | + CR_SWS_MASTER | CR_SCK_MASTER); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_RIGHT_J: + ssicr |= CR_DEL | CR_PDTA; + break; + case SND_SOC_DAIFMT_LEFT_J: + ssicr |= CR_DEL; + break; + default: + pr_debug("ssi: unsupported format\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) { + case SND_SOC_DAIFMT_CONT: + break; + case SND_SOC_DAIFMT_GATED: + ssicr |= CR_BREN; + break; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + ssicr |= CR_SCKP; /* sample data at low clkedge */ + break; + case SND_SOC_DAIFMT_NB_IF: + ssicr |= CR_SCKP | CR_SWSP; + break; + case SND_SOC_DAIFMT_IB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + ssicr |= CR_SWSP; /* word select starts low */ + break; + default: + pr_debug("ssi: invalid inversion\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + break; + case SND_SOC_DAIFMT_CBS_CFM: + ssicr |= CR_SCK_MASTER; + break; + case SND_SOC_DAIFMT_CBM_CFS: + ssicr |= CR_SWS_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + ssicr |= CR_SWS_MASTER | CR_SCK_MASTER; + break; + default: + pr_debug("ssi: invalid master/slave configuration\n"); + return -EINVAL; + } + + SSIREG(SSICR) = ssicr; + pr_debug("ssi_set_fmt() leave\nssicr is now 0x%08lx\n", ssicr); + + return 0; +} + +/* the SSI depends on an external clocksource (at HAC_BIT_CLK) even in + * Master mode, so really this is board specific; the SSI can do any + * rate with the right bitclk and divider settings. + */ +#define SSI_RATES \ + SNDRV_PCM_RATE_8000_192000 + +/* the SSI can do 8-32 bit samples, with 8 possible channels */ +#define SSI_FMTS \ + (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | \ + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3LE | \ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE) + +struct snd_soc_cpu_dai sh4_ssi_dai[] = { +{ + .name = "SSI0", + .id = 0, + .type = SND_SOC_DAI_I2S, + .playback = { + .rates = SSI_RATES, + .formats = SSI_FMTS, + .channels_min = 2, + .channels_max = 8, + }, + .capture = { + .rates = SSI_RATES, + .formats = SSI_FMTS, + .channels_min = 2, + .channels_max = 8, + }, + .ops = { + .startup = ssi_startup, + .shutdown = ssi_shutdown, + .trigger = ssi_trigger, + .hw_params = ssi_hw_params, + }, + .dai_ops = { + .set_sysclk = ssi_set_sysclk, + .set_clkdiv = ssi_set_clkdiv, + .set_fmt = ssi_set_fmt, + }, +}, +#ifdef CONFIG_CPU_SUBTYPE_SH7760 +{ + .name = "SSI1", + .id = 1, + .type = SND_SOC_DAI_I2S, + .playback = { + .rates = SSI_RATES, + .formats = SSI_FMTS, + .channels_min = 2, + .channels_max = 8, + }, + .capture = { + .rates = SSI_RATES, + .formats = SSI_FMTS, + .channels_min = 2, + .channels_max = 8, + }, + .ops = { + .startup = ssi_startup, + .shutdown = ssi_shutdown, + .trigger = ssi_trigger, + .hw_params = ssi_hw_params, + }, + .dai_ops = { + .set_sysclk = ssi_set_sysclk, + .set_clkdiv = ssi_set_clkdiv, + .set_fmt = ssi_set_fmt, + }, +}, +#endif +}; +EXPORT_SYMBOL_GPL(sh4_ssi_dai); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SuperH onchip SSI (I2S) audio driver"); +MODULE_AUTHOR("Manuel Lauss "); diff -Nur sound/soc/soc-core.c sound/soc/soc-core.c --- sound/soc/soc-core.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/soc/soc-core.c 2007-07-24 02:00:10.000000000 +0200 @@ -1362,26 +1362,6 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext); /** - * snd_soc_info_bool_ext - external single boolean mixer info callback - * @kcontrol: mixer control - * @uinfo: control element information - * - * Callback to provide information about a single boolean external mixer control. - * - * Returns 0 for success. - */ -int snd_soc_info_bool_ext(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; -} -EXPORT_SYMBOL_GPL(snd_soc_info_bool_ext); - -/** * snd_soc_info_volsw - single mixer info callback * @kcontrol: mixer control * @uinfo: control element information diff -Nur sound/spi/Kconfig sound/spi/Kconfig --- sound/spi/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ sound/spi/Kconfig 2007-07-24 02:00:10.000000000 +0200 @@ -0,0 +1,31 @@ +#SPI drivers + +menu "SPI devices" + depends on SND != n + +config SND_AT73C213 + tristate "Atmel AT73C213 DAC driver" + depends on ATMEL_SSC + select SND_PCM + help + Say Y here if you want to use the Atmel AT73C213 external DAC. This + DAC can be found on Atmel development boards. + + This driver requires the Atmel SSC driver for sound sink, a + peripheral found on most AT91 and AVR32 microprocessors. + + To compile this driver as a module, choose M here: the module will be + called snd-at73c213. + +config SND_AT73C213_TARGET_BITRATE + int "Target bitrate for AT73C213" + depends on SND_AT73C213 + default "48000" + range 8000 50000 + help + Sets the target bitrate for the bitrate calculator in the driver. + Limited by hardware to be between 8000 Hz and 50000 Hz. + + Set to 48000 Hz by default. + +endmenu diff -Nur sound/spi/Makefile sound/spi/Makefile --- sound/spi/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ sound/spi/Makefile 2007-07-24 02:00:10.000000000 +0200 @@ -0,0 +1,5 @@ +# Makefile for SPI drivers + +snd-at73c213-objs := at73c213.o + +obj-$(CONFIG_SND_AT73C213) += snd-at73c213.o diff -Nur sound/spi/at73c213.c sound/spi/at73c213.c --- sound/spi/at73c213.c 1970-01-01 01:00:00.000000000 +0100 +++ sound/spi/at73c213.c 2007-07-24 02:00:10.000000000 +0200 @@ -0,0 +1,1129 @@ +/* + * Driver for AT73C213 16-bit stereo DAC connected to Atmel SSC + * + * Copyright (C) 2006-2007 Atmel Norway + * + * 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. + */ + +/*#define DEBUG*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "at73c213.h" + +#define BITRATE_MIN 8000 /* Hardware limit? */ +#define BITRATE_TARGET CONFIG_SND_AT73C213_TARGET_BITRATE +#define BITRATE_MAX 50000 /* Hardware limit. */ + +/* Initial (hardware reset) AT73C213 register values. */ +static u8 snd_at73c213_original_image[18] = +{ + 0x00, /* 00 - CTRL */ + 0x05, /* 01 - LLIG */ + 0x05, /* 02 - RLIG */ + 0x08, /* 03 - LPMG */ + 0x08, /* 04 - RPMG */ + 0x00, /* 05 - LLOG */ + 0x00, /* 06 - RLOG */ + 0x22, /* 07 - OLC */ + 0x09, /* 08 - MC */ + 0x00, /* 09 - CSFC */ + 0x00, /* 0A - MISC */ + 0x00, /* 0B - */ + 0x00, /* 0C - PRECH */ + 0x05, /* 0D - AUXG */ + 0x00, /* 0E - */ + 0x00, /* 0F - */ + 0x00, /* 10 - RST */ + 0x00, /* 11 - PA_CTRL */ +}; + +struct snd_at73c213 { + struct snd_card *card; + struct snd_pcm *pcm; + struct snd_pcm_substream *substream; + struct at73c213_board_info *board; + int irq; + int period; + unsigned long bitrate; + struct clk *bitclk; + struct ssc_device *ssc; + struct spi_device *spi; + u8 spi_wbuffer[2]; + u8 spi_rbuffer[2]; + /* Image of the SPI registers in AT73C213. */ + u8 reg_image[18]; + /* Protect registers against concurrent access. */ + spinlock_t lock; +}; + +#define get_chip(card) ((struct snd_at73c213 *)card->private_data) + +static int +snd_at73c213_write_reg(struct snd_at73c213 *chip, u8 reg, u8 val) +{ + struct spi_message msg; + struct spi_transfer msg_xfer = { + .len = 2, + .cs_change = 0, + }; + int retval; + + spi_message_init(&msg); + + chip->spi_wbuffer[0] = reg; + chip->spi_wbuffer[1] = val; + + msg_xfer.tx_buf = chip->spi_wbuffer; + msg_xfer.rx_buf = chip->spi_rbuffer; + spi_message_add_tail(&msg_xfer, &msg); + + retval = spi_sync(chip->spi, &msg); + + if (!retval) + chip->reg_image[reg] = val; + + return retval; +} + +static struct snd_pcm_hardware snd_at73c213_playback_hw = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER, + .formats = SNDRV_PCM_FMTBIT_S16_BE, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 8000, /* Replaced by chip->bitrate later. */ + .rate_max = 50000, /* Replaced by chip->bitrate later. */ + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 64 * 1024 - 1, + .period_bytes_min = 512, + .period_bytes_max = 64 * 1024 - 1, + .periods_min = 4, + .periods_max = 1024, +}; + +/* + * Calculate and set bitrate and divisions. + */ +static int snd_at73c213_set_bitrate(struct snd_at73c213 *chip) +{ + unsigned long ssc_rate = clk_get_rate(chip->ssc->clk); + unsigned long dac_rate_new, ssc_div, status; + unsigned long ssc_div_max, ssc_div_min; + int max_tries; + + /* + * We connect two clocks here, picking divisors so the I2S clocks + * out data at the same rate the DAC clocks it in ... and as close + * as practical to the desired target rate. + * + * The DAC master clock (MCLK) is programmable, and is either 256 + * or (not here) 384 times the I2S output clock (BCLK). + */ + + /* SSC clock / (bitrate * stereo * 16-bit). */ + ssc_div = ssc_rate / (BITRATE_TARGET * 2 * 16); + ssc_div_min = ssc_rate / (BITRATE_MAX * 2 * 16); + ssc_div_max = ssc_rate / (BITRATE_MIN * 2 * 16); + max_tries = (ssc_div_max - ssc_div_min) / 2; + + if (max_tries < 1) + max_tries = 1; + + /* ssc_div must be a power of 2. */ + ssc_div = (ssc_div + 1) & ~1UL; + + if ((ssc_rate / (ssc_div * 2 * 16)) < BITRATE_MIN) { + ssc_div -= 2; + if ((ssc_rate / (ssc_div * 2 * 16)) > BITRATE_MAX) + return -ENXIO; + } + + /* Search for a possible bitrate. */ + do { + /* SSC clock / (ssc divider * 16-bit * stereo). */ + if ((ssc_rate / (ssc_div * 2 * 16)) < BITRATE_MIN) + return -ENXIO; + + /* 256 / (2 * 16) = 8 */ + dac_rate_new = 8 * (ssc_rate / ssc_div); + + status = clk_round_rate(chip->board->dac_clk, dac_rate_new); + if (status < 0) + return status; + + /* Ignore difference smaller than 256 Hz. */ + if ((status/256) == (dac_rate_new/256)) + goto set_rate; + + ssc_div += 2; + } while (--max_tries); + + /* Not able to find a valid bitrate. */ + return -ENXIO; + +set_rate: + status = clk_set_rate(chip->board->dac_clk, status); + if (status < 0) + return status; + + /* Set divider in SSC device. */ + ssc_writel(chip->ssc->regs, CMR, ssc_div/2); + + /* SSC clock / (ssc divider * 16-bit * stereo). */ + chip->bitrate = ssc_rate / (ssc_div * 16 * 2); + + dev_info(&chip->spi->dev, + "at73c213: supported bitrate is %lu (%lu divider)\n", + chip->bitrate, ssc_div); + + return 0; +} + +static int snd_at73c213_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_at73c213 *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + + snd_at73c213_playback_hw.rate_min = chip->bitrate; + snd_at73c213_playback_hw.rate_max = chip->bitrate; + runtime->hw = snd_at73c213_playback_hw; + chip->substream = substream; + + return 0; +} + +static int snd_at73c213_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_at73c213 *chip = snd_pcm_substream_chip(substream); + chip->substream = NULL; + return 0; +} + +static int snd_at73c213_pcm_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_at73c213_pcm_hw_free(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int snd_at73c213_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_at73c213 *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int block_size; + + block_size = frames_to_bytes(runtime, runtime->period_size); + + chip->period = 0; + + ssc_writel(chip->ssc->regs, PDC_TPR, + (long)runtime->dma_addr); + ssc_writel(chip->ssc->regs, PDC_TCR, runtime->period_size * 2); + ssc_writel(chip->ssc->regs, PDC_TNPR, + (long)runtime->dma_addr + block_size); + ssc_writel(chip->ssc->regs, PDC_TNCR, runtime->period_size * 2); + + return 0; +} + +static int snd_at73c213_pcm_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct snd_at73c213 *chip = snd_pcm_substream_chip(substream); + int retval = 0; + + spin_lock(&chip->lock); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + ssc_writel(chip->ssc->regs, IER, SSC_BIT(IER_ENDTX)); + ssc_writel(chip->ssc->regs, PDC_PTCR, SSC_BIT(PDC_PTCR_TXTEN)); + break; + case SNDRV_PCM_TRIGGER_STOP: + ssc_writel(chip->ssc->regs, PDC_PTCR, SSC_BIT(PDC_PTCR_TXTDIS)); + ssc_writel(chip->ssc->regs, IDR, SSC_BIT(IDR_ENDTX)); + break; + default: + dev_dbg(&chip->spi->dev, "spurious command %x\n", cmd); + retval = -EINVAL; + break; + } + + spin_unlock(&chip->lock); + + return retval; +} + +static snd_pcm_uframes_t +snd_at73c213_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_at73c213 *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_uframes_t pos; + unsigned long bytes; + + bytes = ssc_readl(chip->ssc->regs, PDC_TPR) + - (unsigned long)runtime->dma_addr; + + pos = bytes_to_frames(runtime, bytes); + if (pos >= runtime->buffer_size) + pos -= runtime->buffer_size; + + return pos; +} + +static struct snd_pcm_ops at73c213_playback_ops = { + .open = snd_at73c213_pcm_open, + .close = snd_at73c213_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_at73c213_pcm_hw_params, + .hw_free = snd_at73c213_pcm_hw_free, + .prepare = snd_at73c213_pcm_prepare, + .trigger = snd_at73c213_pcm_trigger, + .pointer = snd_at73c213_pcm_pointer, +}; + +static void snd_at73c213_pcm_free(struct snd_pcm *pcm) +{ + struct snd_at73c213 *chip = snd_pcm_chip(pcm); + if (chip->pcm) { + snd_pcm_lib_preallocate_free_for_all(chip->pcm); + chip->pcm = NULL; + } +} + +static int __devinit snd_at73c213_pcm_new(struct snd_at73c213 *chip, int device) +{ + struct snd_pcm *pcm; + int retval; + + retval = snd_pcm_new(chip->card, chip->card->shortname, + device, 1, 0, &pcm); + if (retval < 0) + goto out; + + pcm->private_data = chip; + pcm->private_free = snd_at73c213_pcm_free; + pcm->info_flags = SNDRV_PCM_INFO_BLOCK_TRANSFER; + strcpy(pcm->name, "at73c213"); + chip->pcm = pcm; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &at73c213_playback_ops); + + retval = snd_pcm_lib_preallocate_pages_for_all(chip->pcm, + SNDRV_DMA_TYPE_DEV, &chip->ssc->pdev->dev, + 64 * 1024, 64 * 1024); +out: + return retval; +} + +static irqreturn_t snd_at73c213_interrupt(int irq, void *dev_id) +{ + struct snd_at73c213 *chip = dev_id; + struct snd_pcm_runtime *runtime = chip->substream->runtime; + u32 status; + int offset; + int block_size; + int next_period; + int retval = IRQ_NONE; + + spin_lock(&chip->lock); + + block_size = frames_to_bytes(runtime, runtime->period_size); + status = ssc_readl(chip->ssc->regs, IMR); + + if (status & SSC_BIT(IMR_ENDTX)) { + chip->period++; + if (chip->period == runtime->periods) + chip->period = 0; + next_period = chip->period + 1; + if (next_period == runtime->periods) + next_period = 0; + + offset = block_size * next_period; + + ssc_writel(chip->ssc->regs, PDC_TNPR, + (long)runtime->dma_addr + offset); + ssc_writel(chip->ssc->regs, PDC_TNCR, runtime->period_size * 2); + retval = IRQ_HANDLED; + } + + ssc_readl(chip->ssc->regs, IMR); + spin_unlock(&chip->lock); + + if (status & SSC_BIT(IMR_ENDTX)) + snd_pcm_period_elapsed(chip->substream); + + return retval; +} + +/* + * Mixer functions. + */ +static int snd_at73c213_mono_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irq(&chip->lock); + + ucontrol->value.integer.value[0] = + (chip->reg_image[reg] >> shift) & mask; + + if (invert) + ucontrol->value.integer.value[0] = + mask - ucontrol->value.integer.value[0]; + + spin_unlock_irq(&chip->lock); + + return 0; +} + +static int snd_at73c213_mono_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change, retval; + unsigned short val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + + spin_lock_irq(&chip->lock); + + val = (chip->reg_image[reg] & ~(mask << shift)) | val; + change = val != chip->reg_image[reg]; + retval = snd_at73c213_write_reg(chip, reg, val); + + spin_unlock_irq(&chip->lock); + + if (retval) + return retval; + + return change; +} + +static int snd_at73c213_stereo_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xff; + + if (mask == 1) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + + return 0; +} + +static int snd_at73c213_stereo_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol); + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + + spin_lock_irq(&chip->lock); + + ucontrol->value.integer.value[0] = + (chip->reg_image[left_reg] >> shift_left) & mask; + ucontrol->value.integer.value[1] = + (chip->reg_image[right_reg] >> shift_right) & mask; + + if (invert) { + ucontrol->value.integer.value[0] = + mask - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[1] = + mask - ucontrol->value.integer.value[1]; + } + + spin_unlock_irq(&chip->lock); + + return 0; +} + +static int snd_at73c213_stereo_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol); + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change, retval; + unsigned short val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + + spin_lock_irq(&chip->lock); + + val1 = (chip->reg_image[left_reg] & ~(mask << shift_left)) | val1; + val2 = (chip->reg_image[right_reg] & ~(mask << shift_right)) | val2; + change = val1 != chip->reg_image[left_reg] + || val2 != chip->reg_image[right_reg]; + retval = snd_at73c213_write_reg(chip, left_reg, val1); + if (retval) { + spin_unlock_irq(&chip->lock); + goto out; + } + retval = snd_at73c213_write_reg(chip, right_reg, val2); + if (retval) { + spin_unlock_irq(&chip->lock); + goto out; + } + + spin_unlock_irq(&chip->lock); + + return change; + +out: + return retval; +} + +static int snd_at73c213_mono_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + + return 0; +} + +static int snd_at73c213_mono_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irq(&chip->lock); + + ucontrol->value.integer.value[0] = + (chip->reg_image[reg] >> shift) & 0x01; + + if (invert) + ucontrol->value.integer.value[0] = + 0x01 - ucontrol->value.integer.value[0]; + + spin_unlock_irq(&chip->lock); + + return 0; +} + +static int snd_at73c213_mono_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change, retval; + unsigned short val; + + if (ucontrol->value.integer.value[0]) + val = mask; + else + val = 0; + + if (invert) + val = mask - val; + val <<= shift; + + spin_lock_irq(&chip->lock); + + val |= (chip->reg_image[reg] & ~(mask << shift)); + change = val != chip->reg_image[reg]; + + retval = snd_at73c213_write_reg(chip, reg, val); + + spin_unlock_irq(&chip->lock); + + if (retval) + return retval; + + return change; +} + +static int snd_at73c213_pa_volume_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 = ((kcontrol->private_value >> 16) & 0xff) - 1; + + return 0; +} + +static int snd_at73c213_line_capture_volume_info( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + /* When inverted will give values 0x10001 => 0. */ + uinfo->value.integer.min = 14; + uinfo->value.integer.max = 31; + + return 0; +} + +static int snd_at73c213_aux_capture_volume_info( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + /* When inverted will give values 0x10001 => 0. */ + uinfo->value.integer.min = 14; + uinfo->value.integer.max = 31; + + return 0; +} + +#define AT73C213_MONO_SWITCH(xname, xindex, reg, shift, mask, invert) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .info = snd_at73c213_mono_switch_info, \ + .get = snd_at73c213_mono_switch_get, \ + .put = snd_at73c213_mono_switch_put, \ + .private_value = (reg | (shift << 8) | (mask << 16) | (invert << 24)) \ +} + +#define AT73C213_STEREO(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .info = snd_at73c213_stereo_info, \ + .get = snd_at73c213_stereo_get, \ + .put = snd_at73c213_stereo_put, \ + .private_value = (left_reg | (right_reg << 8) \ + | (shift_left << 16) | (shift_right << 19) \ + | (mask << 24) | (invert << 22)) \ +} + +static struct snd_kcontrol_new snd_at73c213_controls[] __devinitdata = { +AT73C213_STEREO("Master Playback Volume", 0, DAC_LMPG, DAC_RMPG, 0, 0, 0x1f, 1), +AT73C213_STEREO("Master Playback Switch", 0, DAC_LMPG, DAC_RMPG, 5, 5, 1, 1), +AT73C213_STEREO("PCM Playback Volume", 0, DAC_LLOG, DAC_RLOG, 0, 0, 0x1f, 1), +AT73C213_STEREO("PCM Playback Switch", 0, DAC_LLOG, DAC_RLOG, 5, 5, 1, 1), +AT73C213_MONO_SWITCH("Mono PA Playback Switch", 0, DAC_CTRL, DAC_CTRL_ONPADRV, + 0x01, 0), +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PA Playback Volume", + .index = 0, + .info = snd_at73c213_pa_volume_info, + .get = snd_at73c213_mono_get, + .put = snd_at73c213_mono_put, + .private_value = PA_CTRL | (PA_CTRL_APAGAIN << 8) | \ + (0x0f << 16) | (1 << 24), +}, +AT73C213_MONO_SWITCH("PA High Gain Playback Switch", 0, PA_CTRL, PA_CTRL_APALP, + 0x01, 1), +AT73C213_MONO_SWITCH("PA Playback Switch", 0, PA_CTRL, PA_CTRL_APAON, 0x01, 0), +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Aux Capture Volume", + .index = 0, + .info = snd_at73c213_aux_capture_volume_info, + .get = snd_at73c213_mono_get, + .put = snd_at73c213_mono_put, + .private_value = DAC_AUXG | (0 << 8) | (0x1f << 16) | (1 << 24), +}, +AT73C213_MONO_SWITCH("Aux Capture Switch", 0, DAC_CTRL, DAC_CTRL_ONAUXIN, + 0x01, 0), +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Line Capture Volume", + .index = 0, + .info = snd_at73c213_line_capture_volume_info, + .get = snd_at73c213_stereo_get, + .put = snd_at73c213_stereo_put, + .private_value = DAC_LLIG | (DAC_RLIG << 8) | (0 << 16) | (0 << 19) + | (0x1f << 24) | (1 << 22), +}, +AT73C213_MONO_SWITCH("Line Capture Switch", 0, DAC_CTRL, 0, 0x03, 0), +}; + +static int __devinit snd_at73c213_mixer(struct snd_at73c213 *chip) +{ + struct snd_card *card; + int errval, idx; + + if (chip == NULL || chip->pcm == NULL) + return -EINVAL; + + card = chip->card; + + strcpy(card->mixername, chip->pcm->name); + + for (idx = 0; idx < ARRAY_SIZE(snd_at73c213_controls); idx++) { + errval = snd_ctl_add(card, + snd_ctl_new1(&snd_at73c213_controls[idx], + chip)); + if (errval < 0) + goto cleanup; + } + + return 0; + +cleanup: + for (idx = 1; idx < ARRAY_SIZE(snd_at73c213_controls) + 1; idx++) { + struct snd_kcontrol *kctl; + kctl = snd_ctl_find_numid(card, idx); + if (kctl) + snd_ctl_remove(card, kctl); + } + return errval; +} + +/* + * Device functions + */ +static int snd_at73c213_ssc_init(struct snd_at73c213 *chip) +{ + /* + * Continuous clock output. + * Starts on falling TF. + * Delay 1 cycle (1 bit). + * Periode is 16 bit (16 - 1). + */ + ssc_writel(chip->ssc->regs, TCMR, + SSC_BF(TCMR_CKO, 1) + | SSC_BF(TCMR_START, 4) + | SSC_BF(TCMR_STTDLY, 1) + | SSC_BF(TCMR_PERIOD, 16 - 1)); + /* + * Data length is 16 bit (16 - 1). + * Transmit MSB first. + * Transmit 2 words each transfer. + * Frame sync length is 16 bit (16 - 1). + * Frame starts on negative pulse. + */ + ssc_writel(chip->ssc->regs, TFMR, + SSC_BF(TFMR_DATLEN, 16 - 1) + | SSC_BIT(TFMR_MSBF) + | SSC_BF(TFMR_DATNB, 1) + | SSC_BF(TFMR_FSLEN, 16 - 1) + | SSC_BF(TFMR_FSOS, 1)); + + return 0; +} + +static int snd_at73c213_chip_init(struct snd_at73c213 *chip) +{ + int retval; + unsigned char dac_ctrl = 0; + + retval = snd_at73c213_set_bitrate(chip); + if (retval) + goto out; + + /* Enable DAC master clock. */ + clk_enable(chip->board->dac_clk); + + /* Initialize at73c213 on SPI bus. */ + retval = snd_at73c213_write_reg(chip, DAC_RST, 0x04); + if (retval) + goto out_clk; + msleep(1); + retval = snd_at73c213_write_reg(chip, DAC_RST, 0x03); + if (retval) + goto out_clk; + + /* Precharge everything. */ + retval = snd_at73c213_write_reg(chip, DAC_PRECH, 0xff); + if (retval) + goto out_clk; + retval = snd_at73c213_write_reg(chip, PA_CTRL, (1<ssc->regs, CR, SSC_BIT(CR_TXEN)); + + goto out; + +out_clk: + clk_disable(chip->board->dac_clk); +out: + return retval; +} + +static int snd_at73c213_dev_free(struct snd_device *device) +{ + struct snd_at73c213 *chip = device->device_data; + + ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS)); + if (chip->irq >= 0) { + free_irq(chip->irq, chip); + chip->irq = -1; + } + + return 0; +} + +static int __devinit snd_at73c213_dev_init(struct snd_card *card, + struct spi_device *spi) +{ + static struct snd_device_ops ops = { + .dev_free = snd_at73c213_dev_free, + }; + struct snd_at73c213 *chip = get_chip(card); + int irq, retval; + + irq = chip->ssc->irq; + if (irq < 0) + return irq; + + spin_lock_init(&chip->lock); + chip->card = card; + chip->irq = -1; + + retval = request_irq(irq, snd_at73c213_interrupt, 0, "at73c213", chip); + if (retval) { + dev_dbg(&chip->spi->dev, "unable to request irq %d\n", irq); + goto out; + } + chip->irq = irq; + + memcpy(&chip->reg_image, &snd_at73c213_original_image, + sizeof(snd_at73c213_original_image)); + + retval = snd_at73c213_ssc_init(chip); + if (retval) + goto out_irq; + + retval = snd_at73c213_chip_init(chip); + if (retval) + goto out_irq; + + retval = snd_at73c213_pcm_new(chip, 0); + if (retval) + goto out_irq; + + retval = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); + if (retval) + goto out_irq; + + retval = snd_at73c213_mixer(chip); + if (retval) + goto out_snd_dev; + + snd_card_set_dev(card, &spi->dev); + + goto out; + +out_snd_dev: + snd_device_free(card, chip); +out_irq: + free_irq(chip->irq, chip); + chip->irq = -1; +out: + return retval; +} + +static int snd_at73c213_probe(struct spi_device *spi) +{ + struct snd_card *card; + struct snd_at73c213 *chip; + struct at73c213_board_info *board; + int retval; + char id[16]; + + board = spi->dev.platform_data; + if (!board) { + dev_dbg(&spi->dev, "no platform_data\n"); + return -ENXIO; + } + + if (!board->dac_clk) { + dev_dbg(&spi->dev, "no DAC clk\n"); + return -ENXIO; + } + + if (IS_ERR(board->dac_clk)) { + dev_dbg(&spi->dev, "no DAC clk\n"); + return PTR_ERR(board->dac_clk); + } + + retval = -ENOMEM; + + /* Allocate "card" using some unused identifiers. */ + snprintf(id, sizeof id, "at73c213_%d", board->ssc_id); + card = snd_card_new(-1, id, THIS_MODULE, sizeof(struct snd_at73c213)); + if (!card) + goto out; + + chip = card->private_data; + chip->spi = spi; + chip->board = board; + + chip->ssc = ssc_request(board->ssc_id); + if (IS_ERR(chip->ssc)) { + dev_dbg(&spi->dev, "could not get ssc%d device\n", + board->ssc_id); + retval = PTR_ERR(chip->ssc); + goto out_card; + } + + retval = snd_at73c213_dev_init(card, spi); + if (retval) + goto out_ssc; + + strcpy(card->driver, "at73c213"); + strcpy(card->shortname, board->shortname); + sprintf(card->longname, "%s on irq %d", card->shortname, chip->irq); + + retval = snd_card_register(card); + if (retval) + goto out_ssc; + + dev_set_drvdata(&spi->dev, card); + + goto out; + +out_ssc: + ssc_free(chip->ssc); +out_card: + snd_card_free(card); +out: + return retval; +} + +static int __devexit snd_at73c213_remove(struct spi_device *spi) +{ + struct snd_card *card = dev_get_drvdata(&spi->dev); + struct snd_at73c213 *chip = card->private_data; + int retval; + + /* Stop playback. */ + ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS)); + + /* Mute sound. */ + retval = snd_at73c213_write_reg(chip, DAC_LMPG, 0x3f); + if (retval) + goto out; + retval = snd_at73c213_write_reg(chip, DAC_RMPG, 0x3f); + if (retval) + goto out; + retval = snd_at73c213_write_reg(chip, DAC_LLOG, 0x3f); + if (retval) + goto out; + retval = snd_at73c213_write_reg(chip, DAC_RLOG, 0x3f); + if (retval) + goto out; + retval = snd_at73c213_write_reg(chip, DAC_LLIG, 0x11); + if (retval) + goto out; + retval = snd_at73c213_write_reg(chip, DAC_RLIG, 0x11); + if (retval) + goto out; + retval = snd_at73c213_write_reg(chip, DAC_AUXG, 0x11); + if (retval) + goto out; + + /* Turn off PA. */ + retval = snd_at73c213_write_reg(chip, PA_CTRL, + chip->reg_image[PA_CTRL] | 0x0f); + if (retval) + goto out; + msleep(10); + retval = snd_at73c213_write_reg(chip, PA_CTRL, + (1 << PA_CTRL_APALP) | 0x0f); + if (retval) + goto out; + + /* Turn off external DAC. */ + retval = snd_at73c213_write_reg(chip, DAC_CTRL, 0x0c); + if (retval) + goto out; + msleep(2); + retval = snd_at73c213_write_reg(chip, DAC_CTRL, 0x00); + if (retval) + goto out; + + /* Turn off master power. */ + retval = snd_at73c213_write_reg(chip, DAC_PRECH, 0x00); + if (retval) + goto out; + +out: + /* Stop DAC master clock. */ + clk_disable(chip->board->dac_clk); + + ssc_free(chip->ssc); + snd_card_free(card); + dev_set_drvdata(&spi->dev, NULL); + + return 0; +} + +#ifdef CONFIG_PM +static int snd_at73c213_suspend(struct spi_device *spi, pm_message_t msg) +{ + struct snd_card *card = dev_get_drvdata(&spi->dev); + struct snd_at73c213 *chip = card->private_data; + + ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS)); + clk_disable(chip->board->dac_clk); + + return 0; +} + +static int snd_at73c213_resume(struct spi_device *spi) +{ + struct snd_card *card = dev_get_drvdata(&spi->dev); + struct snd_at73c213 *chip = card->private_data; + + clk_enable(chip->board->dac_clk); + ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXEN)); + + return 0; +} +#else +#define snd_at73c213_suspend NULL +#define snd_at73c213_resume NULL +#endif + +static struct spi_driver at73c213_driver = { + .driver = { + .name = "at73c213", + }, + .probe = snd_at73c213_probe, + .suspend = snd_at73c213_suspend, + .resume = snd_at73c213_resume, + .remove = __devexit_p(snd_at73c213_remove), +}; + +static int __init at73c213_init(void) +{ + return spi_register_driver(&at73c213_driver); +} +module_init(at73c213_init); + +static void __exit at73c213_exit(void) +{ + spi_unregister_driver(&at73c213_driver); +} +module_exit(at73c213_exit); + +MODULE_AUTHOR("Hans-Christian Egtvedt "); +MODULE_DESCRIPTION("Sound driver for AT73C213 with Atmel SSC"); +MODULE_LICENSE("GPL"); diff -Nur sound/spi/at73c213.h sound/spi/at73c213.h --- sound/spi/at73c213.h 1970-01-01 01:00:00.000000000 +0100 +++ sound/spi/at73c213.h 2007-07-24 02:00:10.000000000 +0200 @@ -0,0 +1,119 @@ +/* + * Driver for the AT73C213 16-bit stereo DAC on Atmel ATSTK1000 + * + * Copyright (C) 2006 - 2007 Atmel Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * The full GNU General Public License is included in this + * distribution in the file called COPYING. + */ + +#ifndef _SND_AT73C213_H +#define _SND_AT73C213_H + +/* DAC control register */ +#define DAC_CTRL 0x00 +#define DAC_CTRL_ONPADRV 7 +#define DAC_CTRL_ONAUXIN 6 +#define DAC_CTRL_ONDACR 5 +#define DAC_CTRL_ONDACL 4 +#define DAC_CTRL_ONLNOR 3 +#define DAC_CTRL_ONLNOL 2 +#define DAC_CTRL_ONLNIR 1 +#define DAC_CTRL_ONLNIL 0 + +/* DAC left line in gain register */ +#define DAC_LLIG 0x01 +#define DAC_LLIG_LLIG 0 + +/* DAC right line in gain register */ +#define DAC_RLIG 0x02 +#define DAC_RLIG_RLIG 0 + +/* DAC Left Master Playback Gain Register */ +#define DAC_LMPG 0x03 +#define DAC_LMPG_LMPG 0 + +/* DAC Right Master Playback Gain Register */ +#define DAC_RMPG 0x04 +#define DAC_RMPG_RMPG 0 + +/* DAC Left Line Out Gain Register */ +#define DAC_LLOG 0x05 +#define DAC_LLOG_LLOG 0 + +/* DAC Right Line Out Gain Register */ +#define DAC_RLOG 0x06 +#define DAC_RLOG_RLOG 0 + +/* DAC Output Level Control Register */ +#define DAC_OLC 0x07 +#define DAC_OLC_RSHORT 7 +#define DAC_OLC_ROLC 4 +#define DAC_OLC_LSHORT 3 +#define DAC_OLC_LOLC 0 + +/* DAC Mixer Control Register */ +#define DAC_MC 0x08 +#define DAC_MC_INVR 5 +#define DAC_MC_INVL 4 +#define DAC_MC_RMSMIN2 3 +#define DAC_MC_RMSMIN1 2 +#define DAC_MC_LMSMIN2 1 +#define DAC_MC_LMSMIN1 0 + +/* DAC Clock and Sampling Frequency Control Register */ +#define DAC_CSFC 0x09 +#define DAC_CSFC_OVRSEL 4 + +/* DAC Miscellaneous Register */ +#define DAC_MISC 0x0A +#define DAC_MISC_VCMCAPSEL 7 +#define DAC_MISC_DINTSEL 4 +#define DAC_MISC_DITHEN 3 +#define DAC_MISC_DEEMPEN 2 +#define DAC_MISC_NBITS 0 + +/* DAC Precharge Control Register */ +#define DAC_PRECH 0x0C +#define DAC_PRECH_PRCHGPDRV 7 +#define DAC_PRECH_PRCHGAUX1 6 +#define DAC_PRECH_PRCHGLNOR 5 +#define DAC_PRECH_PRCHGLNOL 4 +#define DAC_PRECH_PRCHGLNIR 3 +#define DAC_PRECH_PRCHGLNIL 2 +#define DAC_PRECH_PRCHG 1 +#define DAC_PRECH_ONMSTR 0 + +/* DAC Auxiliary Input Gain Control Register */ +#define DAC_AUXG 0x0D +#define DAC_AUXG_AUXG 0 + +/* DAC Reset Register */ +#define DAC_RST 0x10 +#define DAC_RST_RESMASK 2 +#define DAC_RST_RESFILZ 1 +#define DAC_RST_RSTZ 0 + +/* Power Amplifier Control Register */ +#define PA_CTRL 0x11 +#define PA_CTRL_APAON 6 +#define PA_CTRL_APAPRECH 5 +#define PA_CTRL_APALP 4 +#define PA_CTRL_APAGAIN 0 + +#endif /* _SND_AT73C213_H */ diff -Nur sound/synth/util_mem.c sound/synth/util_mem.c --- sound/synth/util_mem.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/synth/util_mem.c 2007-07-31 02:00:08.000000000 +0200 @@ -116,7 +116,7 @@ if (blk == NULL) return NULL; - if (! prev || prev == &hdr->block) + if (prev == &hdr->block) blk->offset = 0; else { struct snd_util_memblk *p = get_memblk(prev); diff -Nur sound/usb/caiaq/caiaq-input.c sound/usb/caiaq/caiaq-input.c --- sound/usb/caiaq/caiaq-input.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/usb/caiaq/caiaq-input.c 2007-07-28 02:00:08.000000000 +0200 @@ -238,7 +238,6 @@ return; input_unregister_device(dev->input_dev); - input_free_device(dev->input_dev); dev->input_dev = NULL; } diff -Nur sound/usb/usbaudio.c sound/usb/usbaudio.c --- sound/usb/usbaudio.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/usb/usbaudio.c 2007-08-14 02:00:08.000000000 +0200 @@ -123,7 +123,6 @@ unsigned int rate_min, rate_max; /* min/max rates */ unsigned int nr_rates; /* number of rate table entries */ unsigned int *rate_table; /* rate table */ - unsigned int needs_knot; /* any unusual rates? */ }; struct snd_usb_substream; @@ -1761,7 +1760,7 @@ channels[f->format] |= (1 << f->channels); rates[f->format] |= f->rates; /* needs knot? */ - if (f->needs_knot) + if (f->rates & SNDRV_PCM_RATE_KNOT) goto __out; } /* check whether channels and rates match for all formats */ @@ -1817,7 +1816,7 @@ if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS) return 0; count += fp->nr_rates; - if (fp->needs_knot) + if (fp->rates & SNDRV_PCM_RATE_KNOT) needs_knot = 1; } if (!needs_knot) @@ -2350,7 +2349,9 @@ return 1; break; case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */ - return 1; + if (device_setup[chip->index] == 0x00 || + fp->altsetting==1 || fp->altsetting==2 || fp->altsetting==3) + return 1; } return 0; } @@ -2451,7 +2452,7 @@ unsigned char *fmt, int offset) { int nr_rates = fmt[offset]; - int found; + if (fmt[0] < offset + 1 + 3 * (nr_rates ? nr_rates : 2)) { snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n", chip->dev->devnum, fp->iface, fp->altsetting); @@ -2462,20 +2463,15 @@ /* * build the rate table and bitmap flags */ - int r, idx, c; + int r, idx; unsigned int nonzero_rates = 0; - /* this table corresponds to the SNDRV_PCM_RATE_XXX bit */ - static unsigned int conv_rates[] = { - 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, - 64000, 88200, 96000, 176400, 192000 - }; + fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL); if (fp->rate_table == NULL) { snd_printk(KERN_ERR "cannot malloc\n"); return -1; } - fp->needs_knot = 0; fp->nr_rates = nr_rates; fp->rate_min = fp->rate_max = combine_triple(&fmt[8]); for (r = 0, idx = offset + 1; r < nr_rates; r++, idx += 3) { @@ -2491,23 +2487,12 @@ fp->rate_min = rate; else if (rate > fp->rate_max) fp->rate_max = rate; - found = 0; - for (c = 0; c < (int)ARRAY_SIZE(conv_rates); c++) { - if (rate == conv_rates[c]) { - found = 1; - fp->rates |= (1 << c); - break; - } - } - if (!found) - fp->needs_knot = 1; + fp->rates |= snd_pcm_rate_to_rate_bit(rate); } if (!nonzero_rates) { hwc_debug("All rates were zero. Skipping format!\n"); return -1; } - if (fp->needs_knot) - fp->rates |= SNDRV_PCM_RATE_KNOT; } else { /* continuous rates */ fp->rates = SNDRV_PCM_RATE_CONTINUOUS; @@ -2530,7 +2515,18 @@ * but we give normal PCM format to get the existing * apps working... */ - pcm_format = SNDRV_PCM_FORMAT_S16_LE; + switch (chip->usb_id) { + + case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */ + if (device_setup[chip->index] == 0x00 && + fp->altsetting == 6) + pcm_format = SNDRV_PCM_FORMAT_S16_BE; + else + pcm_format = SNDRV_PCM_FORMAT_S16_LE; + break; + default: + pcm_format = SNDRV_PCM_FORMAT_S16_LE; + } } else { pcm_format = parse_audio_format_i_type(chip, fp, format, fmt); if (pcm_format < 0) @@ -3251,6 +3247,11 @@ static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip, int iface, int altno) { + /* Reset ALL ifaces to 0 altsetting. + * Call it for every possible altsetting of every interface. + */ + usb_set_interface(chip->dev, iface, 0); + if (device_setup[chip->index] & AUDIOPHILE_SET) { if ((device_setup[chip->index] & AUDIOPHILE_SET_DTS) && altno != 6) diff -Nur sound/usb/usbmidi.c sound/usb/usbmidi.c --- sound/usb/usbmidi.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/usb/usbmidi.c 2007-08-17 02:00:07.000000000 +0200 @@ -407,6 +407,20 @@ } /* + * CME protocol: like the standard protocol, but SysEx commands are sent as a + * single USB packet preceded by a 0x0F byte. + */ +static void snd_usbmidi_cme_input(struct snd_usb_midi_in_endpoint *ep, + uint8_t *buffer, int buffer_length) +{ + if (buffer_length < 2 || (buffer[0] & 0x0f) != 0x0f) + snd_usbmidi_standard_input(ep, buffer, buffer_length); + else + snd_usbmidi_input_data(ep, buffer[0] >> 4, + &buffer[1], buffer_length - 1); +} + +/* * Adds one USB MIDI packet to the output buffer. */ static void snd_usbmidi_output_standard_packet(struct urb* urb, uint8_t p0, @@ -572,6 +586,12 @@ .output_packet = snd_usbmidi_output_standard_packet, }; +static struct usb_protocol_ops snd_usbmidi_cme_ops = { + .input = snd_usbmidi_cme_input, + .output = snd_usbmidi_standard_output, + .output_packet = snd_usbmidi_output_standard_packet, +}; + /* * Novation USB MIDI protocol: number of data bytes is in the first byte * (when receiving) (+1!) or in the second byte (when sending); data begins @@ -1690,6 +1710,7 @@ err = snd_usbmidi_detect_endpoints(umidi, &endpoints[0], 1); break; case QUIRK_MIDI_CME: + umidi->usb_protocol_ops = &snd_usbmidi_cme_ops; err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints); break; default: diff -Nur sound/usb/usbmixer.c sound/usb/usbmixer.c --- sound/usb/usbmixer.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/usb/usbmixer.c 2007-08-07 02:00:07.000000000 +0200 @@ -1483,7 +1483,7 @@ struct snd_kcontrol *kctl; char **namelist; - if (! num_ins || desc[0] < 6 + num_ins) { + if (! num_ins || desc[0] < 5 + num_ins) { snd_printk(KERN_ERR "invalid SELECTOR UNIT descriptor %d\n", unitid); return -EINVAL; } @@ -1888,14 +1888,7 @@ return 0; } -static int snd_audigy2nx_led_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define snd_audigy2nx_led_info snd_ctl_boolean_mono_info static int snd_audigy2nx_led_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { diff -Nur sound/usb/usbquirks.h sound/usb/usbquirks.h --- sound/usb/usbquirks.h 2007-07-09 01:32:17.000000000 +0200 +++ sound/usb/usbquirks.h 2007-08-11 02:00:08.000000000 +0200 @@ -57,6 +57,24 @@ USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS, .idVendor = 0x046d, + .idProduct = 0x08ae, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL +}, +{ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | + USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS, + .idVendor = 0x046d, + .idProduct = 0x08c6, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL +}, +{ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | + USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS, + .idVendor = 0x046d, .idProduct = 0x08f0, .bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL @@ -1051,7 +1069,15 @@ .type = QUIRK_MIDI_STANDARD_INTERFACE } }, - /* TODO: add Roland EXR support */ +{ + USB_DEVICE(0x0582, 0x0060), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "EXR Series", + .ifnum = 0, + .type = QUIRK_MIDI_STANDARD_INTERFACE + } +}, { /* has ID 0x0067 when not in "Advanced Driver" mode */ USB_DEVICE(0x0582, 0x0065), @@ -1094,6 +1120,19 @@ } } }, +{ + USB_DEVICE(0x582, 0x00a6), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "Juno-G", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, { /* * This quirk is for the "Advanced" modes of the Edirol UA-25. * If the switch is not in an advanced setting, the UA-25 has @@ -1215,7 +1254,28 @@ } }, /* TODO: add Edirol PC-80 support */ - /* TODO: add Edirol UA-1EX support */ +{ + USB_DEVICE(0x0582, 0x0096), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "EDIROL", + .product_name = "UA-1EX", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = -1 + } + } + } +}, { USB_DEVICE(0x0582, 0x009a), .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { @@ -1230,6 +1290,37 @@ } }, /* TODO: add Edirol MD-P1 support */ +{ + /* Roland SH-201 */ + USB_DEVICE(0x0582, 0x00ad), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "SH-201", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + }, + { + .ifnum = -1 + } + } + } +}, /* Guillemot devices */ { @@ -1639,6 +1730,24 @@ } }, +/* Stanton/N2IT Final Scratch v1 device ('Scratchamp') */ +{ + USB_DEVICE(0x103d, 0x0100), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Stanton", + .product_name = "ScratchAmp", + .ifnum = QUIRK_NO_INTERFACE + } +}, +{ + USB_DEVICE(0x103d, 0x0101), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Stanton", + .product_name = "ScratchAmp", + .ifnum = QUIRK_NO_INTERFACE + } +}, + /* Novation EMS devices */ { USB_DEVICE_VENDOR_SPEC(0x1235, 0x0001), diff -Nur sound/usb/usx2y/usbusx2yaudio.c sound/usb/usx2y/usbusx2yaudio.c --- sound/usb/usx2y/usbusx2yaudio.c 2007-07-09 01:32:17.000000000 +0200 +++ sound/usb/usx2y/usbusx2yaudio.c 2007-05-15 02:00:09.000000000 +0200 @@ -935,10 +935,9 @@ */ static void usX2Y_audio_stream_free(struct snd_usX2Y_substream **usX2Y_substream) { - if (NULL != usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK]) { - kfree(usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK]); - usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK] = NULL; - } + kfree(usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK]); + usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK] = NULL; + kfree(usX2Y_substream[SNDRV_PCM_STREAM_CAPTURE]); usX2Y_substream[SNDRV_PCM_STREAM_CAPTURE] = NULL; }