From c4eeeab42ef7f73f7ba01ec9579d1c762e5f2e33 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela <perex@suse.cz> Date: Tue, 1 Oct 2002 02:09:28 -0700 Subject: [PATCH] [PATCH] ALSA update [8/12] - 2002/09/06 - VIA686 and VIA8233 driver merge to VIA82xx - ioctl32 fixes - fixed OOPS in snd_pcm_sgbuf_delete (not initialized) - I2C - call hw_stop() before returning at the error pointer - AC'97 codec - added more AC97 IDs by Laszlo Melis - CS46xx - mutex initialization fix - ENS1371 - added one more card to S/PDIF capabilities - intel8x0 - fixed secondary and third codec indexes - PPC Keywest - initialize MCS in loop until it succeeds - PPC Tumbler - the initial support for snapper (TAS3004) on some tibook - USB Audio - mixer fixes --- include/sound/sndmagic.h | 3 +- include/sound/version.h | 2 +- sound/core/Makefile | 3 +- sound/core/ioctl32/hwdep32.c | 2 - sound/core/ioctl32/ioctl32.c | 118 +-- sound/core/ioctl32/ioctl32.h | 22 +- sound/core/ioctl32/pcm32.c | 104 +-- sound/core/oss/mixer_oss.c | 8 +- sound/core/pcm_sgbuf.c | 3 + sound/core/seq/Makefile | 2 +- sound/core/seq/oss/seq_oss_synth.c | 4 + sound/drivers/mpu401/Makefile | 2 +- sound/i2c/i2c.c | 21 +- sound/pci/Config.help | 7 +- sound/pci/Config.in | 3 +- sound/pci/Makefile | 6 +- sound/pci/ac97/Makefile | 3 +- sound/pci/ac97/ac97_codec.c | 12 + sound/pci/ac97/ac97_id.h | 1 + sound/pci/cs4281.c | 17 +- sound/pci/cs46xx/cs46xx_lib.c | 3 + sound/pci/ens1370.c | 1 + sound/pci/es1938.c | 4 +- sound/pci/es1968.c | 4 +- sound/pci/ice1712.c | 129 ++- sound/pci/intel8x0.c | 27 +- sound/pci/rme32.c | 8 +- sound/pci/via686.c | 1232 ------------------------- sound/pci/via8233.c | 1022 --------------------- sound/pci/via82xx.c | 1345 ++++++++++++++++++++++++++++ sound/ppc/keywest.c | 5 +- sound/ppc/pmac.c | 12 +- sound/ppc/pmac.h | 3 +- sound/ppc/powermac.c | 6 +- sound/ppc/tumbler.c | 299 ++++++- sound/ppc/tumbler_volume.h | 66 +- sound/usb/usbaudio.c | 39 +- sound/usb/usbmixer.c | 66 +- 38 files changed, 2047 insertions(+), 2567 deletions(-) delete mode 100644 sound/pci/via686.c delete mode 100644 sound/pci/via8233.c create mode 100644 sound/pci/via82xx.c diff --git a/include/sound/sndmagic.h b/include/sound/sndmagic.h index 0911f1fe0865..83495c4ce9e5 100644 --- a/include/sound/sndmagic.h +++ b/include/sound/sndmagic.h @@ -113,7 +113,7 @@ static inline int _snd_magic_bad(void *obj, unsigned long magic) #define intel8x0_t_magic 0xa15a2a01 #define es1968_t_magic 0xa15a2b01 #define esschan_t_magic 0xa15a2b02 -#define via686a_t_magic 0xa15a2c01 +#define via82xx_t_magic 0xa15a2c01 #define pdplus_t_magic 0xa15a2d01 #define cmipci_t_magic 0xa15a2e01 #define ymfpci_t_magic 0xa15a2f01 @@ -126,7 +126,6 @@ static inline int _snd_magic_bad(void *obj, unsigned long magic) #define m3_dma_t_magic 0xa15a3202 #define nm256_t_magic 0xa15a3301 #define nm256_dma_t_magic 0xa15a3302 -#define via8233_t_magic 0xa15a3401 #define pmac_t_magic 0xa15a3501 #define ali_t_magic 0xa15a3601 #define mtpav_t_magic 0xa15a3701 diff --git a/include/sound/version.h b/include/sound/version.h index 2ce8305b4f6c..99faca4263dc 100644 --- a/include/sound/version.h +++ b/include/sound/version.h @@ -1,3 +1,3 @@ /* include/version.h. Generated automatically by configure. */ #define CONFIG_SND_VERSION "0.9.0rc3" -#define CONFIG_SND_DATE " (Mon Aug 26 16:28:35 2002 UTC)" +#define CONFIG_SND_DATE " (Fri Sep 06 15:06:56 2002 UTC)" diff --git a/sound/core/Makefile b/sound/core/Makefile index cf1af3e38260..4dfd3908ccef 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -78,8 +78,7 @@ obj-$(CONFIG_SND_MAESTRO3) += snd-pcm.o snd-timer.o snd.o obj-$(CONFIG_SND_RME32) += snd-pcm.o snd-timer.o snd.o obj-$(CONFIG_SND_RME96) += snd-pcm.o snd-timer.o snd.o obj-$(CONFIG_SND_SONICVIBES) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_VIA686) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_VIA8233) += snd-pcm.o snd-timer.o snd.o +obj-$(CONFIG_SND_VIA82XX) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_ALI5451) += snd.o snd-rawmidi.o snd-timer.o snd-pcm.o obj-$(CONFIG_SND_CS46XX) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o obj-$(CONFIG_SND_EMU10K1) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o diff --git a/sound/core/ioctl32/hwdep32.c b/sound/core/ioctl32/hwdep32.c index 19d189ae88c9..cf968a6c5382 100644 --- a/sound/core/ioctl32/hwdep32.c +++ b/sound/core/ioctl32/hwdep32.c @@ -28,7 +28,5 @@ struct ioctl32_mapper hwdep_mappers[] = { { SNDRV_HWDEP_IOCTL_PVERSION, NULL }, { SNDRV_HWDEP_IOCTL_INFO, NULL }, - { SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE, NULL }, - { SNDRV_CTL_IOCTL_HWDEP_INFO, NULL }, { 0 }, }; diff --git a/sound/core/ioctl32/ioctl32.c b/sound/core/ioctl32/ioctl32.c index 7f52251fecdf..8002791080ec 100644 --- a/sound/core/ioctl32/ioctl32.c +++ b/sound/core/ioctl32/ioctl32.c @@ -47,14 +47,10 @@ int snd_ioctl32_register(struct ioctl32_mapper *mappers) int err; struct ioctl32_mapper *m; - lock_kernel(); for (m = mappers; m->cmd; m++) { err = register_ioctl32_conversion(m->cmd, m->handler); - if (err < 0) { - unlock_kernel(); - return err; - } - m->registered++; + if (err >= 0) + m->registered++; } return 0; } @@ -63,14 +59,12 @@ void snd_ioctl32_unregister(struct ioctl32_mapper *mappers) { struct ioctl32_mapper *m; - lock_kernel(); for (m = mappers; m->cmd; m++) { if (m->registered) { unregister_ioctl32_conversion(m->cmd); m->registered = 0; } } - unlock_kernel(); } @@ -100,36 +94,32 @@ static int _snd_ioctl32_ctl_elem_list(unsigned int fd, unsigned int cmd, unsigne { struct sndrv_ctl_elem_list32 data32; struct sndrv_ctl_elem_list data; - mm_segment_t oldseg = get_fs(); + mm_segment_t oldseg; int err; - set_fs(KERNEL_DS); - if (copy_from_user(&data32, (void*)arg, sizeof(data32))) { - err = -EFAULT; - goto __err; - } + if (copy_from_user(&data32, (void*)arg, sizeof(data32))) + return -EFAULT; memset(&data, 0, sizeof(data)); data.offset = data32.offset; data.space = data32.space; data.used = data32.used; data.count = data32.count; data.pids = A(data32.pids); + oldseg = get_fs(); + set_fs(KERNEL_DS); err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data); + set_fs(oldseg); if (err < 0) - goto __err; + return err; /* copy the result */ data32.offset = data.offset; data32.space = data.space; data32.used = data.used; data32.count = data.count; //data.pids = data.pids; - if (copy_to_user((void*)arg, &data32, sizeof(data32))) { - err = -EFAULT; - goto __err; - } - __err: - set_fs(oldseg); - return err; + if (copy_to_user((void*)arg, &data32, sizeof(data32))) + return -EFAULT; + return 0; } DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_list, ctl_elem_list, SNDRV_CTL_IOCTL_ELEM_LIST); @@ -171,22 +161,22 @@ static int _snd_ioctl32_ctl_elem_info(unsigned int fd, unsigned int cmd, unsigne struct sndrv_ctl_elem_info data; struct sndrv_ctl_elem_info32 data32; int err; - mm_segment_t oldseg = get_fs(); + mm_segment_t oldseg; - set_fs(KERNEL_DS); - if (copy_from_user(&data32, (void*)arg, sizeof(data32))) { - err = -EFAULT; - goto __err; - } + if (copy_from_user(&data32, (void*)arg, sizeof(data32))) + return -EFAULT; memset(&data, 0, sizeof(data)); data.id = data32.id; /* we need to copy the item index. * hope this doesn't break anything.. */ data.value.enumerated.item = data32.value.enumerated.item; + oldseg = get_fs(); + set_fs(KERNEL_DS); err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data); + set_fs(oldseg); if (err < 0) - goto __err; + return err; /* restore info to 32bit */ data32.id = data.id; data32.type = data.type; @@ -215,10 +205,8 @@ static int _snd_ioctl32_ctl_elem_info(unsigned int fd, unsigned int cmd, unsigne break; } if (copy_to_user((void*)arg, &data32, sizeof(data32))) - err = -EFAULT; - __err: - set_fs(oldseg); - return err; + return -EFAULT; + return 0; } DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_info, ctl_elem_info, SNDRV_CTL_IOCTL_ELEM_INFO); @@ -281,26 +269,20 @@ static int _snd_ioctl32_ctl_elem_value(unsigned int fd, unsigned int cmd, unsign struct sndrv_ctl_elem_value32 data32; int err, i; int type; - mm_segment_t oldseg = get_fs(); - - set_fs(KERNEL_DS); + mm_segment_t oldseg; /* FIXME: check the sane ioctl.. */ - if (copy_from_user(&data32, (void*)arg, sizeof(data32))) { - err = -EFAULT; - goto __err; - } + if (copy_from_user(&data32, (void*)arg, sizeof(data32))) + return -EFAULT; memset(&data, 0, sizeof(data)); data.id = data32.id; data.indirect = data32.indirect; if (data.indirect) /* FIXME: this is not correct for long arrays */ data.value.integer.value_ptr = (void*)TO_PTR(data32.value.integer.value_ptr); type = get_ctl_type(file, &data.id); - if (type < 0) { - err = type; - goto __err; - } + if (type < 0) + return type; if (! data.indirect) { switch (type) { case SNDRV_CTL_ELEM_TYPE_BOOLEAN: @@ -329,9 +311,12 @@ static int _snd_ioctl32_ctl_elem_value(unsigned int fd, unsigned int cmd, unsign } } + oldseg = get_fs(); + set_fs(KERNEL_DS); err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data); + set_fs(oldseg); if (err < 0) - goto __err; + return err; /* restore info to 32bit */ if (! data.indirect) { switch (type) { @@ -360,10 +345,8 @@ static int _snd_ioctl32_ctl_elem_value(unsigned int fd, unsigned int cmd, unsign } } if (copy_to_user((void*)arg, &data32, sizeof(data32))) - err = -EFAULT; - __err: - set_fs(oldseg); - return err; + return -EFAULT; + return 0; } DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_read, ctl_elem_value, SNDRV_CTL_IOCTL_ELEM_READ); @@ -392,6 +375,7 @@ static struct ioctl32_mapper control_mappers[] = { { SNDRV_CTL_IOCTL_ELEM_LOCK, NULL }, { SNDRV_CTL_IOCTL_ELEM_UNLOCK, NULL }, { SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, NULL }, + { SNDRV_CTL_IOCTL_HWDEP_INFO, NULL }, { SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE, NULL }, { SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE, NULL }, { SNDRV_CTL_IOCTL_PCM_INFO, NULL }, @@ -427,37 +411,13 @@ static void snd_ioctl32_done(void) static int __init snd_ioctl32_init(void) { - int err; - - err = snd_ioctl32_register(control_mappers); - if (err < 0) - return err; - err = snd_ioctl32_register(pcm_mappers); - if (err < 0) { - snd_ioctl32_done(); - return err; - } - err = snd_ioctl32_register(rawmidi_mappers); - if (err < 0) { - snd_ioctl32_done(); - return err; - } - err = snd_ioctl32_register(timer_mappers); - if (err < 0) { - snd_ioctl32_done(); - return err; - } - err = snd_ioctl32_register(hwdep_mappers); - if (err < 0) { - snd_ioctl32_done(); - return err; - } + snd_ioctl32_register(control_mappers); + snd_ioctl32_register(pcm_mappers); + snd_ioctl32_register(rawmidi_mappers); + snd_ioctl32_register(timer_mappers); + snd_ioctl32_register(hwdep_mappers); #ifdef CONFIG_SND_SEQUENCER - err = snd_ioctl32_register(seq_mappers); - if (err < 0) { - snd_ioctl32_done(); - return err; - } + snd_ioctl32_register(seq_mappers); #endif return 0; } diff --git a/sound/core/ioctl32/ioctl32.h b/sound/core/ioctl32/ioctl32.h index a485e172074b..30cd42921435 100644 --- a/sound/core/ioctl32/ioctl32.h +++ b/sound/core/ioctl32/ioctl32.h @@ -60,27 +60,23 @@ static int _snd_ioctl32_##type(unsigned int fd, unsigned int cmd, unsigned long {\ struct sndrv_##type##32 data32;\ struct sndrv_##type data;\ - mm_segment_t oldseg = get_fs();\ + mm_segment_t oldseg;\ int err;\ - set_fs(KERNEL_DS);\ - if (copy_from_user(&data32, (void*)arg, sizeof(data32))) {\ - err = -EFAULT;\ - goto __err;\ - }\ + if (copy_from_user(&data32, (void*)arg, sizeof(data32)))\ + return -EFAULT;\ memset(&data, 0, sizeof(data));\ convert_from_32(type, &data, &data32);\ + oldseg = get_fs();\ + set_fs(KERNEL_DS);\ err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data);\ if (err < 0) \ - goto __err;\ + return err;\ if (native_ctl & (_IOC_READ << _IOC_DIRSHIFT)) {\ convert_to_32(type, &data32, &data);\ - if (copy_to_user((void*)arg, &data32, sizeof(data32))) {\ - err = -EFAULT;\ - goto __err;\ - }\ + if (copy_to_user((void*)arg, &data32, sizeof(data32)))\ + return -EFAULT;\ }\ - __err: set_fs(oldseg);\ - return err;\ + return 0;\ } #define DEFINE_ALSA_IOCTL_ENTRY(name,type,native_ctl) \ diff --git a/sound/core/ioctl32/pcm32.c b/sound/core/ioctl32/pcm32.c index 2a501fa92c95..66b8a1bf3565 100644 --- a/sound/core/ioctl32/pcm32.c +++ b/sound/core/ioctl32/pcm32.c @@ -189,30 +189,26 @@ static int _snd_ioctl32_xferi(unsigned int fd, unsigned int cmd, unsigned long a { struct sndrv_xferi32 data32; struct sndrv_xferi data; - mm_segment_t oldseg = get_fs(); + mm_segment_t oldseg; int err; - set_fs(KERNEL_DS); - if (copy_from_user(&data32, (void*)arg, sizeof(data32))) { - err = -EFAULT; - goto __err; - } + if (copy_from_user(&data32, (void*)arg, sizeof(data32))) + return -EFAULT; memset(&data, 0, sizeof(data)); data.result = data32.result; data.buf = A(data32.buf); data.frames = data32.frames; + oldseg = get_fs(); + set_fs(KERNEL_DS); err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data); + set_fs(oldseg); if (err < 0) - goto __err; + return err; /* copy the result */ data32.result = data.result; - if (copy_to_user((void*)arg, &data32, sizeof(data32))) { - err = -EFAULT; - goto __err; - } - __err: - set_fs(oldseg); - return err; + if (copy_to_user((void*)arg, &data32, sizeof(data32))) + return -EFAULT; + return 0; } @@ -237,9 +233,7 @@ static int _snd_ioctl32_xfern(unsigned int fd, unsigned int cmd, unsigned long a void *bufs[128]; int err = 0, ch, i; u32 *bufptr; - mm_segment_t oldseg = get_fs(); - - set_fs(KERNEL_DS); + mm_segment_t oldseg; /* FIXME: need to check whether fop->ioctl is sane */ @@ -250,41 +244,31 @@ static int _snd_ioctl32_xfern(unsigned int fd, unsigned int cmd, unsigned long a /* check validty of the command */ switch (native_ctl) { case SNDRV_PCM_IOCTL_WRITEN_FRAMES: - if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) { - err = -EINVAL; - goto __err; - } - if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { - err = -EBADFD; - goto __err; - } + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) + return -EINVAL; + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) + return -EBADFD; break; case SNDRV_PCM_IOCTL_READN_FRAMES: - if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) { - err = -EINVAL; - goto __err; - } + if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) + return -EINVAL; break; } - if ((ch = substream->runtime->channels) > 128) { - err = -EINVAL; - goto __err; - } - if (get_user(data32.frames, &srcptr->frames)) { - err = -EFAULT; - goto __err; - } + if ((ch = substream->runtime->channels) > 128) + return -EINVAL; + if (get_user(data32.frames, &srcptr->frames)) + return -EFAULT; __get_user(data32.bufs, &srcptr->bufs); bufptr = (u32*)TO_PTR(data32.bufs); for (i = 0; i < ch; i++) { u32 ptr; - if (get_user(ptr, bufptr)) { - err = -EFAULT; - goto __err; - } + if (get_user(ptr, bufptr)) + return -EFAULT; bufs[ch] = (void*)TO_PTR(ptr); bufptr++; } + oldseg = get_fs(); + set_fs(KERNEL_DS); switch (native_ctl) { case SNDRV_PCM_IOCTL_WRITEN_FRAMES: err = snd_pcm_lib_writev(substream, bufs, data32.frames); @@ -293,14 +277,12 @@ static int _snd_ioctl32_xfern(unsigned int fd, unsigned int cmd, unsigned long a err = snd_pcm_lib_readv(substream, bufs, data32.frames); break; } - + set_fs(oldseg); if (err < 0) - goto __err; + return err; if (put_user(err, &srcptr->result)) - err = -EFAULT; - __err: - set_fs(oldseg); - return err < 0 ? err : 0; + return -EFAULT; + return 0; } @@ -363,24 +345,22 @@ static int _snd_ioctl32_pcm_hw_params_old(unsigned int fd, unsigned int cmd, uns { struct sndrv_pcm_hw_params_old32 data32; struct sndrv_pcm_hw_params data; - mm_segment_t oldseg = get_fs(); + mm_segment_t oldseg; int err; - set_fs(KERNEL_DS); - if (copy_from_user(&data32, (void*)arg, sizeof(data32))) { - err = -EFAULT; - goto __err; - } + + if (copy_from_user(&data32, (void*)arg, sizeof(data32))) + return -EFAULT; snd_pcm_hw_convert_from_old_params(&data, &data32); + oldseg = get_fs(); + set_fs(KERNEL_DS); err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data); + set_fs(oldseg); if (err < 0) - goto __err; + return err; snd_pcm_hw_convert_to_old_params(&data32, &data); - if (copy_to_user((void*)arg, &data32, sizeof(data32))) { - err = -EFAULT; - goto __err; - } - __err: set_fs(oldseg); - return err; + if (copy_to_user((void*)arg, &data32, sizeof(data32))) + return -EFAULT; + return 0; } @@ -451,9 +431,5 @@ struct ioctl32_mapper pcm_mappers[] = { { SNDRV_PCM_IOCTL_LINK, NULL }, { SNDRV_PCM_IOCTL_UNLINK, NULL }, - { SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE, NULL }, - { SNDRV_CTL_IOCTL_PCM_INFO, NULL }, - { SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE, NULL }, - { 0 }, }; diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 3d638439d648..a949ac79b020 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -1169,10 +1169,14 @@ static int snd_mixer_oss_notify_handler(snd_card_t * card, int free_flag) return err; } mixer->card = card; - strcpy(mixer->name, name); + if (*card->mixername) { + strncpy(mixer->name, card->mixername, sizeof(mixer->name) - 1); + mixer->name[sizeof(mixer->name)-1] = 0; + } else + strcpy(mixer->name, name); snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIXERS, card->number, - name); + mixer->name); for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++) mixer->slots[idx].number = idx; card->mixer_oss = mixer; diff --git a/sound/core/pcm_sgbuf.c b/sound/core/pcm_sgbuf.c index 95651a098296..d1eb0bdc38e8 100644 --- a/sound/core/pcm_sgbuf.c +++ b/sound/core/pcm_sgbuf.c @@ -80,6 +80,9 @@ int snd_pcm_sgbuf_delete(snd_pcm_substream_t *substream) { struct snd_sg_buf *sgbuf; + /* return in case, when sgbuf is not initialized */ + if (substream->dma_private == NULL) + return -EINVAL; sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return -EINVAL); sgbuf_shrink(sgbuf, 0); if (sgbuf->table) diff --git a/sound/core/seq/Makefile b/sound/core/seq/Makefile index 7c5f6f91586c..42328414ff7b 100644 --- a/sound/core/seq/Makefile +++ b/sound/core/seq/Makefile @@ -67,7 +67,7 @@ obj-$(CONFIG_SND_FM801) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-mid obj-$(CONFIG_SND_ICE1712) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o obj-$(CONFIG_SND_INTEL8X0) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o obj-$(CONFIG_SND_SONICVIBES) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o -obj-$(CONFIG_SND_VIA686) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o +obj-$(CONFIG_SND_VIA82XX) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o obj-$(CONFIG_SND_ALI5451) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o obj-$(CONFIG_SND_CS46XX) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o obj-$(CONFIG_SND_EMU10K1) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-virmidi.o diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c index 2174b630df8d..edb37b268090 100644 --- a/sound/core/seq/oss/seq_oss_synth.c +++ b/sound/core/seq/oss/seq_oss_synth.c @@ -146,6 +146,8 @@ snd_seq_oss_synth_register(snd_seq_device_t *dev) debug_printk(("synth %s registered %d\n", rec->name, i)); spin_unlock_irqrestore(®ister_lock, flags); dev->driver_data = rec; + if (i < SNDRV_CARDS) + snd_oss_info_register(SNDRV_OSS_INFO_DEV_SYNTH, i, rec->name); return 0; } @@ -176,6 +178,8 @@ snd_seq_oss_synth_unregister(snd_seq_device_t *dev) max_synth_devs = index + 1; } spin_unlock_irqrestore(®ister_lock, flags); + if (rec->seq_device < SNDRV_CARDS) + snd_oss_info_register(SNDRV_OSS_INFO_DEV_SYNTH, rec->seq_device, NULL); snd_use_lock_sync(&rec->use_lock); kfree(rec); diff --git a/sound/drivers/mpu401/Makefile b/sound/drivers/mpu401/Makefile index d227400254f1..0e98764e844c 100644 --- a/sound/drivers/mpu401/Makefile +++ b/sound/drivers/mpu401/Makefile @@ -35,7 +35,7 @@ obj-$(CONFIG_SND_FM801) += snd-mpu401-uart.o obj-$(CONFIG_SND_ICE1712) += snd-mpu401-uart.o obj-$(CONFIG_SND_INTEL8X0) += snd-mpu401-uart.o obj-$(CONFIG_SND_SONICVIBES) += snd-mpu401-uart.o -obj-$(CONFIG_SND_VIA686) += snd-mpu401-uart.o +obj-$(CONFIG_SND_VIA82XX) += snd-mpu401-uart.o obj-$(CONFIG_SND_ALI5451) += snd-mpu401-uart.o obj-$(CONFIG_SND_TRIDENT) += snd-mpu401-uart.o obj-$(CONFIG_SND_YMFPCI) += snd-mpu401-uart.o diff --git a/sound/i2c/i2c.c b/sound/i2c/i2c.c index 5b38d9ee1c6b..5015a8bc8c0d 100644 --- a/sound/i2c/i2c.c +++ b/sound/i2c/i2c.c @@ -260,11 +260,15 @@ static int snd_i2c_bit_sendbytes(snd_i2c_device_t *device, unsigned char *bytes, if (device->flags & SND_I2C_DEVICE_ADDRTEN) return -EIO; /* not yet implemented */ snd_i2c_bit_start(bus); - if ((err = snd_i2c_bit_sendbyte(bus, device->addr << 1)) < 0) + if ((err = snd_i2c_bit_sendbyte(bus, device->addr << 1)) < 0) { + snd_i2c_bit_hw_stop(bus); return err; + } while (count-- > 0) { - if ((err = snd_i2c_bit_sendbyte(bus, *bytes++)) < 0) + if ((err = snd_i2c_bit_sendbyte(bus, *bytes++)) < 0) { + snd_i2c_bit_hw_stop(bus); return err; + } res++; } snd_i2c_bit_stop(bus); @@ -279,11 +283,15 @@ static int snd_i2c_bit_readbytes(snd_i2c_device_t *device, unsigned char *bytes, if (device->flags & SND_I2C_DEVICE_ADDRTEN) return -EIO; /* not yet implemented */ snd_i2c_bit_start(bus); - if ((err = snd_i2c_bit_sendbyte(bus, (device->addr << 1) | 1)) < 0) + if ((err = snd_i2c_bit_sendbyte(bus, (device->addr << 1) | 1)) < 0) { + snd_i2c_bit_hw_stop(bus); return err; + } while (count-- > 0) { - if ((err = snd_i2c_bit_readbyte(bus, count == 0)) < 0) + if ((err = snd_i2c_bit_readbyte(bus, count == 0)) < 0) { + snd_i2c_bit_hw_stop(bus); return err; + } *bytes++ = (unsigned char)err; res++; } @@ -300,10 +308,9 @@ static int snd_i2c_bit_probeaddr(snd_i2c_bus_t *bus, unsigned short addr) if (addr & 0x7f80) /* invalid address */ return -EINVAL; snd_i2c_bit_start(bus); - if ((err = snd_i2c_bit_sendbyte(bus, addr << 1)) < 0) - return err; + err = snd_i2c_bit_sendbyte(bus, addr << 1); snd_i2c_bit_stop(bus); - return 1; /* present */ + return err; } EXPORT_SYMBOL(snd_i2c_bus_create); diff --git a/sound/pci/Config.help b/sound/pci/Config.help index 6241d287669f..6047d766370d 100644 --- a/sound/pci/Config.help +++ b/sound/pci/Config.help @@ -82,8 +82,5 @@ CONFIG_SND_INTEL8X0 CONFIG_SND_SONICVIBES Say 'Y' or 'M' to include support for S3 SonicVibes based soundcards. -CONFIG_SND_VIA686 - Say 'Y' or 'M' to include support for VIA VT82C686A/B South Bridge. - -CONFIG_SND_VIA8233 - Say 'Y' or 'M' to include support for VIA VT8233 South Bridge. +CONFIG_SND_VIA82XX + Say 'Y' or 'M' to include support for VIA VT82C686A/B, VT8233 South Bridge. diff --git a/sound/pci/Config.in b/sound/pci/Config.in index 1b43fdb7f8fe..4f7cb24ec97a 100644 --- a/sound/pci/Config.in +++ b/sound/pci/Config.in @@ -27,8 +27,7 @@ dep_tristate 'ForteMedia FM801' CONFIG_SND_FM801 $CONFIG_SND dep_tristate 'ICEnsemble ICE1712 (Envy24)' CONFIG_SND_ICE1712 $CONFIG_SND dep_tristate 'Intel i810/i820/i830/i840/MX440 integrated audio' CONFIG_SND_INTEL8X0 $CONFIG_SND dep_tristate 'S3 SonicVibes' CONFIG_SND_SONICVIBES $CONFIG_SND -dep_tristate 'VIA 82C686A/B South Bridge' CONFIG_SND_VIA686 $CONFIG_SND -dep_tristate 'VIA 8233 South Bridge' CONFIG_SND_VIA8233 $CONFIG_SND +dep_tristate 'VIA 82C686A/B, 8233 South Bridge' CONFIG_SND_VIA82XX $CONFIG_SND # define gameport if necessary if [ "$CONFIG_INPUT_GAMEPORT" != "n" ]; then diff --git a/sound/pci/Makefile b/sound/pci/Makefile index 6c9265602890..a2b80c9b6d9a 100644 --- a/sound/pci/Makefile +++ b/sound/pci/Makefile @@ -17,8 +17,7 @@ snd-maestro3-objs := maestro3.o snd-rme32-objs := rme32.o snd-rme96-objs := rme96.o snd-sonicvibes-objs := sonicvibes.o -snd-via686-objs := via686.o -snd-via8233-objs := via8233.o +snd-via82xx-objs := via82xx.o # Toplevel Module Dependency obj-$(CONFIG_SND_ALS4000) += snd-als4000.o @@ -35,8 +34,7 @@ obj-$(CONFIG_SND_MAESTRO3) += snd-maestro3.o obj-$(CONFIG_SND_RME32) += snd-rme32.o obj-$(CONFIG_SND_RME96) += snd-rme96.o obj-$(CONFIG_SND_SONICVIBES) += snd-sonicvibes.o -obj-$(CONFIG_SND_VIA686) += snd-via686.o -obj-$(CONFIG_SND_VIA8233) += snd-via8233.o +obj-$(CONFIG_SND_VIA82XX) += snd-via82xx.o obj-$(CONFIG_SND) += ac97/ ali5451/ cs46xx/ emu10k1/ korg1212/ nm256/ rme9652/ trident/ ymfpci/ diff --git a/sound/pci/ac97/Makefile b/sound/pci/ac97/Makefile index c4bdc870a10c..e1383064eda0 100644 --- a/sound/pci/ac97/Makefile +++ b/sound/pci/ac97/Makefile @@ -17,8 +17,7 @@ obj-$(CONFIG_SND_FM801) += snd-ac97-codec.o obj-$(CONFIG_SND_ICE1712) += snd-ac97-codec.o obj-$(CONFIG_SND_INTEL8X0) += snd-ac97-codec.o obj-$(CONFIG_SND_MAESTRO3) += snd-ac97-codec.o -obj-$(CONFIG_SND_VIA686) += snd-ac97-codec.o -obj-$(CONFIG_SND_VIA8233) += snd-ac97-codec.o +obj-$(CONFIG_SND_VIA82XX) += snd-ac97-codec.o obj-$(CONFIG_SND_ALI5451) += snd-ac97-codec.o obj-$(CONFIG_SND_CS46XX) += snd-ac97-codec.o obj-$(CONFIG_SND_EMU10K1) += snd-ac97-codec.o diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 0d7dbcca22ec..93d847bbc8a9 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -65,6 +65,7 @@ static const ac97_codec_id_t snd_ac97_codec_id_vendors[] = { { 0x41445300, 0xffffff00, "Analog Devices", NULL }, { 0x414c4300, 0xffffff00, "Realtek", NULL }, { 0x414c4700, 0xffffff00, "Avance Logic", NULL }, +{ 0x434d4900, 0xffffff00, "C-Media Electronics", NULL }, { 0x43525900, 0xffffff00, "Cirrus Logic", NULL }, { 0x43585400, 0xffffff00, "Conexant", NULL }, { 0x45838300, 0xffffff00, "ESS Technology", NULL }, @@ -95,6 +96,7 @@ static const ac97_codec_id_t snd_ac97_codec_ids[] = { { 0x41445360, 0xffffffff, "AD1885", patch_ad1885 }, { 0x41445361, 0xffffffff, "AD1886", patch_ad1886 }, { 0x41445362, 0xffffffff, "AD1887", patch_ad1881 }, +{ 0x41445363, 0xffffffff, "AD1886A", patch_ad1881 }, { 0x41445372, 0xffffffff, "AD1981A", patch_ad1881 }, { 0x414c4300, 0xfffffff0, "RL5306", NULL }, { 0x414c4310, 0xfffffff0, "RL5382", NULL }, @@ -104,6 +106,8 @@ static const ac97_codec_id_t snd_ac97_codec_ids[] = { { 0x414c4730, 0xffffffff, "ALC101", NULL }, { 0x414c4740, 0xfffffff0, "ALC202", NULL }, { 0x414c4750, 0xfffffff0, "ALC250", NULL }, +{ 0x434d4941, 0xffffffff, "CMI9738", NULL }, +{ 0x434d4961, 0xffffffff, "CMI9739", NULL }, { 0x43525900, 0xfffffff8, "CS4297", NULL }, { 0x43525910, 0xfffffff8, "CS4297A", patch_cirrus_spdif }, { 0x43525920, 0xfffffff8, "CS4294/4298", NULL }, @@ -122,6 +126,8 @@ static const ac97_codec_id_t snd_ac97_codec_ids[] = { { 0x4e534331, 0xffffffff, "LM4549", NULL }, { 0x53494c22, 0xffffffff, "Si3036", NULL }, { 0x53494c23, 0xffffffff, "Si3038", NULL }, +{ 0x54524102, 0xffffffff, "TR28022", NULL }, +{ 0x54524106, 0xffffffff, "TR28026", NULL }, { 0x54524108, 0xffffffff, "TR28028", patch_tritech_tr28028 }, // added by xin jin [07/09/99] { 0x54524123, 0xffffffff, "TR28602", NULL }, // only guess --jk [TR28023 = eMicro EM28023 (new CT1297)] { 0x54584e20, 0xffffffff, "TLC320AD9xC", NULL }, @@ -130,14 +136,19 @@ static const ac97_codec_id_t snd_ac97_codec_ids[] = { { 0x574d4c00, 0xffffffff, "WM9701A", patch_wolfson00 }, { 0x574d4c03, 0xffffffff, "WM9703/9707", patch_wolfson03 }, { 0x574d4c04, 0xffffffff, "WM9704 (quad)", patch_wolfson04 }, +{ 0x574d4c05, 0xffffffff, "WM9705", NULL }, // patch? { 0x594d4800, 0xffffffff, "YMF743", NULL }, +{ 0x594d4802, 0xffffffff, "YMF752", NULL }, +{ 0x594d4803, 0xffffffff, "YMF753", NULL }, { 0x83847600, 0xffffffff, "STAC9700/83/84", NULL }, { 0x83847604, 0xffffffff, "STAC9701/3/4/5", NULL }, { 0x83847605, 0xffffffff, "STAC9704", NULL }, { 0x83847608, 0xffffffff, "STAC9708/11", patch_sigmatel_stac9708 }, { 0x83847609, 0xffffffff, "STAC9721/23", patch_sigmatel_stac9721 }, { 0x83847644, 0xffffffff, "STAC9744", patch_sigmatel_stac9744 }, +{ 0x83847650, 0xffffffff, "STAC9750/51", NULL }, // patch? { 0x83847656, 0xffffffff, "STAC9756/57", patch_sigmatel_stac9756 }, +{ 0x83847666, 0xffffffff, "STAC9766/67", NULL }, // patch? { 0, 0, NULL, NULL } }; @@ -201,6 +212,7 @@ static int snd_ac97_valid_reg(ac97_t *ac97, unsigned short reg) return 1; case AC97_ID_AD1885: /* AD1885 */ case AC97_ID_AD1886: /* AD1886 */ + case AC97_ID_AD1886A: /* AD1886A - !!verify!! --jk */ case AC97_ID_AD1887: /* AD1887 - !!verify!! --jk */ if (reg == 0x5a) return 1; diff --git a/sound/pci/ac97/ac97_id.h b/sound/pci/ac97/ac97_id.h index 03bd1ad299c0..b5b4dc1d9181 100644 --- a/sound/pci/ac97/ac97_id.h +++ b/sound/pci/ac97/ac97_id.h @@ -30,6 +30,7 @@ #define AC97_ID_AD1885 0x41445360 #define AC97_ID_AD1886 0x41445361 #define AC97_ID_AD1887 0x41445362 +#define AC97_ID_AD1886A 0x41445363 #define AC97_ID_TR28028 0x54524108 #define AC97_ID_STAC9700 0x83847600 #define AC97_ID_STAC9704 0x83847604 diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index ec6e16ac2331..fea1ac014565 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -629,6 +629,9 @@ static unsigned short snd_cs4281_ac97_read(ac97_t *ac97, cs4281_t *chip = snd_magic_cast(cs4281_t, ac97->private_data, return -ENXIO); int count; unsigned short result; + // FIXME: volatile is necessary in the following due to a bug of + // some gcc versions + volatile int ac97_num = ((volatile ac97_t *)ac97)->num; /* * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address @@ -639,7 +642,7 @@ static unsigned short snd_cs4281_ac97_read(ac97_t *ac97, * 6. Read ACSTS = Status Register = 464h, check VSTS bit */ - snd_cs4281_peekBA0(chip, ac97->num ? BA0_ACSDA2 : BA0_ACSDA); + snd_cs4281_peekBA0(chip, ac97_num ? BA0_ACSDA2 : BA0_ACSDA); /* * Setup the AC97 control registers on the CS461x to send the @@ -658,7 +661,7 @@ static unsigned short snd_cs4281_ac97_read(ac97_t *ac97, snd_cs4281_pokeBA0(chip, BA0_ACCDA, 0); snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_DCV | BA0_ACCTL_CRW | BA0_ACCTL_VFRM | BA0_ACCTL_ESYN | - (ac97->num ? BA0_ACCTL_TC : 0)); + (ac97_num ? BA0_ACCTL_TC : 0)); /* @@ -691,7 +694,7 @@ static unsigned short snd_cs4281_ac97_read(ac97_t *ac97, * ACSTS = Status Register = 464h * VSTS - Valid Status */ - if (snd_cs4281_peekBA0(chip, ac97->num ? BA0_ACSTS2 : BA0_ACSTS) & BA0_ACSTS_VSTS) + if (snd_cs4281_peekBA0(chip, ac97_num ? BA0_ACSTS2 : BA0_ACSTS) & BA0_ACSTS_VSTS) goto __ok2; udelay(10); } @@ -705,7 +708,7 @@ static unsigned short snd_cs4281_ac97_read(ac97_t *ac97, * Read the data returned from the AC97 register. * ACSDA = Status Data Register = 474h */ - result = snd_cs4281_peekBA0(chip, ac97->num ? BA0_ACSDA2 : BA0_ACSDA); + result = snd_cs4281_peekBA0(chip, ac97_num ? BA0_ACSDA2 : BA0_ACSDA); __end: return result; @@ -2107,7 +2110,8 @@ static void cs4281_suspend(cs4281_t *chip) /* remember the status registers */ for (i = 0; number_of(saved_regs); i++) - chip->suspend_regs[i] = snd_cs4281_peekBA0(chip, saved_regs[i]); + if (saved_regs[i]) + chip->suspend_regs[i] = snd_cs4281_peekBA0(chip, saved_regs[i]); /* Turn off the serial ports. */ snd_cs4281_pokeBA0(chip, BA0_SERMC, 0); @@ -2150,7 +2154,8 @@ static void cs4281_resume(cs4281_t *chip) /* restore the status registers */ for (i = 0; number_of(saved_regs); i++) - snd_cs4281_pokeBA0(chip, saved_regs[i], chip->suspend_regs[i]); + if (saved_regs[i]) + snd_cs4281_pokeBA0(chip, saved_regs[i], chip->suspend_regs[i]); if (chip->ac97) snd_ac97_resume(chip->ac97); diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 34a69403ae6e..466af9f8673f 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -3239,6 +3239,9 @@ int __devinit snd_cs46xx_create(snd_card_t * card, if (chip == NULL) return -ENOMEM; spin_lock_init(&chip->reg_lock); +#ifdef CONFIG_SND_CS46XX_NEW_DSP + init_MUTEX(&chip->spos_mutex); +#endif chip->card = card; chip->pci = pci; chip->capt.hw_size = PAGE_SIZE; diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index d78f1013ac78..6d248bc04b71 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -1298,6 +1298,7 @@ static struct { { .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 } }; diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index 2951e21beacc..575bc86440aa 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -1,5 +1,5 @@ /* - * Driver for ESS Solo-1 (ES1938, ES1946) soundcard + * Driver for ESS Solo-1 (ES1938, ES1946, ES1969) soundcard * Copyright (c) by Jaromir Koutek <miri@punknet.cz>, * Jaroslav Kysela <perex@suse.cz>, * Thomas Sailer <sailer@ife.ee.ethz.ch>, @@ -70,6 +70,8 @@ MODULE_DESCRIPTION("ESS Solo-1"); MODULE_LICENSE("GPL"); MODULE_CLASSES("{sound}"); MODULE_DEVICES("{{ESS,ES1938}," + "{ESS,ES1946}," + "{ESS,ES1969}," "{TerraTec,128i PCI}}"); #ifndef PCI_VENDOR_ID_ESS diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index aba395239124..75924fb9947f 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -2501,11 +2501,13 @@ static int snd_es1968_set_power_state(snd_card_t *card, unsigned int power_state static int snd_es1968_free(es1968_t *chip) { + if (chip->res_io_port) + snd_es1968_reset(chip); + snd_es1968_set_acpi(chip, ACPI_D3); chip->master_switch = NULL; chip->master_volume = NULL; if (chip->res_io_port) { - snd_es1968_reset(chip); release_resource(chip->res_io_port); kfree_nocheck(chip->res_io_port); } diff --git a/sound/pci/ice1712.c b/sound/pci/ice1712.c index cbd11ca6616c..b7499a05889f 100644 --- a/sound/pci/ice1712.c +++ b/sound/pci/ice1712.c @@ -414,7 +414,8 @@ MODULE_PARM_SYNTAX(snd_omni, SNDRV_ENABLED "," SNDRV_ENABLE_DESC); #define ICE1712_6FIRE_TX2 0x40 /* MIDI2 */ #define ICE1712_6FIRE_RX2 0x80 /* MIDI2 */ -#define ICE1712_6FIRE_CS8427_ADDR (0x22>>1) /* ?? */ +#define ICE1712_6FIRE_PCF9554_ADDR (0x40>>1) +#define ICE1712_6FIRE_CS8427_ADDR (0x22>>1) /* * DMA mode values @@ -509,7 +510,7 @@ struct _snd_ice1712 { snd_i2c_device_t *cs8404; /* CS8404A I2C device */ snd_i2c_device_t *cs8427; /* CS8427 I2C device */ snd_i2c_device_t *pcf8574[2]; /* PCF8574 Output/Input (EWS88MT) */ - snd_i2c_device_t *pcf8575; /* PCF8575 (EWS88D) */ + snd_i2c_device_t *pcf8575; /* PCF8575 (EWS88D) / PCF9554 (6Fire) */ unsigned char cs8403_spdif_bits; unsigned char cs8403_spdif_stream_bits; @@ -2411,14 +2412,13 @@ static int __devinit snd_ice1712_ac97_mixer(ice1712_t * ice) ac97.read = snd_ice1712_ac97_read; ac97.private_data = ice; ac97.private_free = snd_ice1712_mixer_free_ac97; - if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0) { + if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0) printk(KERN_WARNING "ice1712: cannot initialize ac97 for consumer, skipped\n"); - // return err; - } else { + else { if ((err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_digmix_route_ac97, ice))) < 0) return err; + return 0; } - return 0; } /* hmm.. can we have both consumer and pro ac97 mixers? */ if (! (ice->eeprom.aclink & ICE1712_CFG_PRO_I2S)) { @@ -2428,11 +2428,10 @@ static int __devinit snd_ice1712_ac97_mixer(ice1712_t * ice) ac97.read = snd_ice1712_pro_ac97_read; ac97.private_data = ice; ac97.private_free = snd_ice1712_mixer_free_ac97; - if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0) { + if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0) printk(KERN_WARNING "ice1712: cannot initialize pro ac97, skipped\n"); - // return err; - } - return 0; + else + return 0; } /* I2S mixer only */ strcat(ice->card->mixername, "ICE1712 - multitrack"); @@ -3111,7 +3110,7 @@ static snd_kcontrol_new_t snd_ice1712_mixer_pro_peak __devinitdata = { static int snd_ice1712_ewx_io_sense_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){ - static char *texts[4] = { + static char *texts[2] = { "+4dBu", "-10dBV", }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; @@ -3354,26 +3353,32 @@ static snd_kcontrol_new_t snd_ice1712_ews88d_controls[] __devinitdata = { * DMX 6Fire controls */ -#if 0 // XXX not working yet -static int snd_ice1712_6fire_read_pca(ice1712_t *ice) +#define PCF9554_REG_INPUT 0 +#define PCF9554_REG_OUTPUT 1 +#define PCF9554_REG_POLARITY 2 +#define PCF9554_REG_CONFIG 3 + +static int snd_ice1712_6fire_read_pca(ice1712_t *ice, unsigned char reg) { unsigned char byte; snd_i2c_lock(ice->i2c); - byte = 0; /* read port */ + byte = reg; snd_i2c_sendbytes(ice->pcf8575, &byte, 1); + byte = 0; if (snd_i2c_readbytes(ice->pcf8575, &byte, 1) != 1) { snd_i2c_unlock(ice->i2c); + printk("cannot read pca\n"); return -EIO; } snd_i2c_unlock(ice->i2c); return byte; } -static int snd_ice1712_6fire_write_pca(ice1712_t *ice, unsigned char data) +static int snd_ice1712_6fire_write_pca(ice1712_t *ice, unsigned char reg, unsigned char data) { unsigned char bytes[2]; snd_i2c_lock(ice->i2c); - bytes[0] = 1; /* write port */ + bytes[0] = reg; bytes[1] = data; if (snd_i2c_sendbytes(ice->pcf8575, bytes, 2) != 2) { snd_i2c_unlock(ice->i2c); @@ -3399,7 +3404,7 @@ static int snd_ice1712_6fire_control_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_ int invert = (kcontrol->private_value >> 8) & 1; int data; - if ((data = snd_ice1712_6fire_read_pca(ice)) < 0) + if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0) return data; data = (data >> shift) & 1; if (invert) @@ -3415,7 +3420,7 @@ static int snd_ice1712_6fire_control_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_ int invert = (kcontrol->private_value >> 8) & 1; int data, ndata; - if ((data = snd_ice1712_6fire_read_pca(ice)) < 0) + if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0) return data; ndata = data & ~(1 << shift); if (ucontrol->value.integer.value[0]) @@ -3423,27 +3428,77 @@ static int snd_ice1712_6fire_control_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_ if (invert) ndata ^= (1 << shift); if (data != ndata) { - snd_ice1712_6fire_write_pca(ice, (unsigned char)ndata); + snd_ice1712_6fire_write_pca(ice, PCF9554_REG_OUTPUT, (unsigned char)ndata); return 1; } return 0; } -#define DMX6FIRE_CONTROL(xiface, xname, xshift, xinvert, xaccess) \ -{ .iface = xiface,\ +static int snd_ice1712_6fire_select_input_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + static char *texts[4] = { + "Internal", "Front Input", "Rear Input", "Wave Table" + }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item >= 4) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ice1712_6fire_select_input_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int data; + + if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0) + return data; + ucontrol->value.integer.value[0] = data & 3; + return 0; +} + +static int snd_ice1712_6fire_select_input_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int data, ndata; + + if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0) + return data; + ndata = data & ~3; + ndata |= (ucontrol->value.integer.value[0] & 3); + if (data != ndata) { + snd_ice1712_6fire_write_pca(ice, PCF9554_REG_OUTPUT, (unsigned char)ndata); + return 1; + } + return 0; +} + + +#define DMX6FIRE_CONTROL(xname, xshift, xinvert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,\ .name = xname,\ - .access = xaccess,\ .info = snd_ice1712_6fire_control_info,\ .get = snd_ice1712_6fire_control_get,\ .put = snd_ice1712_6fire_control_put,\ .private_value = xshift | (xinvert << 8),\ } -static snd_kcontrol_new_t snd_ice1712_6fire_led __devinitdata = -DMX6FIRE_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "Breakbox LED", 6, 0, 0); - -#endif // XXX not working yet - +static snd_kcontrol_new_t snd_ice1712_6fire_controls[] __devinitdata = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Input Select", + .info = snd_ice1712_6fire_select_input_info, + .get = snd_ice1712_6fire_select_input_get, + .put = snd_ice1712_6fire_select_input_put, + }, + DMX6FIRE_CONTROL("Front Digital Input Switch", 2, 0), + // DMX6FIRE_CONTROL("Master Clock Select", 3, 0), + DMX6FIRE_CONTROL("Optical Digital Input Switch", 4, 0), + DMX6FIRE_CONTROL("Phono Analog Input Switch", 5, 0), + DMX6FIRE_CONTROL("Breakbox LED", 6, 0), +}; /* * @@ -3808,14 +3863,16 @@ static int __devinit snd_ice1712_chip_init(ice1712_t *ice) } break; case ICE1712_SUBDEVICE_DMX6FIRE: -#if 0 // XXX not working yet - if ((err = snd_i2c_device_create(ice->i2c, "PCF9554", 0x40>>1, &ice->pcf8575)) < 0) + if ((err = snd_i2c_device_create(ice->i2c, "PCF9554", ICE1712_6FIRE_PCF9554_ADDR, &ice->pcf8575)) < 0) { + snd_printk("PCF9554 initialization failed\n"); return err; - if ((err = snd_cs8427_create(ice->i2c, 0x11, &ice->cs8427)) < 0) { + } +#if 0 // XXX not working... + if ((err = snd_cs8427_create(ice->i2c, ICE1712_6FIRE_CS8427_ADDR, &ice->cs8427)) < 0) { snd_printk("CS8427 initialization failed\n"); return err; } -#endif // XXX not working yet +#endif break; case ICE1712_SUBDEVICE_EWS88MT: if ((err = snd_i2c_device_create(ice->i2c, "CS8404", ICE1712_EWS88MT_CS8404_ADDR, &ice->cs8404)) < 0) @@ -4052,11 +4109,11 @@ static int __init snd_ice1712_build_controls(ice1712_t *ice) } break; case ICE1712_SUBDEVICE_DMX6FIRE: -#if 0 // XXX not working yet - err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_6fire_led, ice)); - if (err < 0) - return err; -#endif + for (idx = 0; idx < sizeof(snd_ice1712_6fire_controls)/sizeof(snd_ice1712_6fire_controls[0]); idx++) { + err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_6fire_controls[idx], ice)); + if (err < 0) + return err; + } break; } diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 54c4ce5021d2..25592dba6bc2 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -326,10 +326,6 @@ struct _snd_intel8x0 { char ac97_name[32]; char ctrl_name[32]; - unsigned long dma_playback_size; - unsigned long dma_capture_size; - unsigned long dma_mic_size; - int irq; unsigned int mmio; @@ -428,7 +424,7 @@ static void iputbyte(intel8x0_t *chip, u32 offset, u8 val) if (chip->bm_mmio) writeb(val, chip->remap_bmaddr + offset); else - return outb(val, chip->bmaddr + offset); + outb(val, chip->bmaddr + offset); } static void iputword(intel8x0_t *chip, u32 offset, u16 val) @@ -436,7 +432,7 @@ static void iputword(intel8x0_t *chip, u32 offset, u16 val) if (chip->bm_mmio) writew(val, chip->remap_bmaddr + offset); else - return outw(val, chip->bmaddr + offset); + outw(val, chip->bmaddr + offset); } static void iputdword(intel8x0_t *chip, u32 offset, u32 val) @@ -444,7 +440,7 @@ static void iputdword(intel8x0_t *chip, u32 offset, u32 val) if (chip->bm_mmio) writel(val, chip->remap_bmaddr + offset); else - return outl(val, chip->bmaddr + offset); + outl(val, chip->bmaddr + offset); } /* @@ -464,7 +460,7 @@ static void iaputword(intel8x0_t *chip, u32 offset, u16 val) if (chip->mmio) writew(val, chip->remap_addr + offset); else - return outw(val, chip->addr + offset); + outw(val, chip->addr + offset); } /* @@ -1583,6 +1579,7 @@ static int __devinit snd_intel8x0_mixer(intel8x0_t *chip, int ac97_clock) if (codecs < 2) goto __skip_secondary; for (i = 1; i < codecs; i++) { + ac97.num = i; if ((err = snd_ac97_mixer(chip->card, &ac97, &x97)) < 0) return err; chip->ac97[i] = x97; @@ -1995,7 +1992,13 @@ static void __devinit intel8x0_measure_ac97_clock(intel8x0_t *chip) snd_intel8x0_setup_periods(chip, ichdev); port = ichdev->reg_offset; spin_lock_irqsave(&chip->reg_lock, flags); - iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE | ICH_STARTBM); /* trigger */ + /* trigger */ + if (chip->device_type != DEVICE_ALI) + iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE | ICH_STARTBM); + else { + iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE); + iputbyte(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot); + } do_gettimeofday(&start_time); spin_unlock_irqrestore(&chip->reg_lock, flags); #if 0 @@ -2014,7 +2017,10 @@ static void __devinit intel8x0_measure_ac97_clock(intel8x0_t *chip) pos -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << 1; pos += ichdev->position; do_gettimeofday(&stop_time); - iputbyte(chip, port + ICH_REG_OFF_CR, 0); /* stop */ + /* stop */ + if (chip->device_type == DEVICE_ALI) + iputbyte(chip, ICHREG(ALI_DMACR), 1 << (ichdev->ali_slot + 8)); + iputbyte(chip, port + ICH_REG_OFF_CR, 0); /* reset whole DMA things */ while (!(igetbyte(chip, port + ichdev->roff_sr) & ICH_DCH)) ; @@ -2279,6 +2285,7 @@ static struct shortname_table { { PCI_DEVICE_ID_NVIDIA_MCP_AUDIO, "NVidia NForce" }, { 0x746d, "AMD AMD8111" }, { 0x7445, "AMD AMD768" }, + { 0x5455, "ALi M5455" }, { 0, 0 }, }; diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index 7b2d860a992b..5a0c9443e361 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -1517,10 +1517,8 @@ static int snd_rme32_info_inputtype_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) { - static char *_texts[5] = - { "Optical", "Coaxial", "Internal", "XLR" }; rme32_t *rme32 = _snd_kcontrol_chip(kcontrol); - char *texts[4] = { _texts[0], _texts[1], _texts[2], _texts[3] }; + static char *texts[4] = { "Optical", "Coaxial", "Internal", "XLR" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; @@ -1614,8 +1612,8 @@ snd_rme32_info_clockmode_control(snd_kcontrol_t * kcontrol, uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item > 4) { - uinfo->value.enumerated.item = 4; + if (uinfo->value.enumerated.item > 3) { + uinfo->value.enumerated.item = 3; } strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); diff --git a/sound/pci/via686.c b/sound/pci/via686.c deleted file mode 100644 index 19599db855e8..000000000000 --- a/sound/pci/via686.c +++ /dev/null @@ -1,1232 +0,0 @@ -/* - * ALSA driver for VIA VT82C686A (South Bridge) - * - * Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include <sound/driver.h> -#include <asm/io.h> -#include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/init.h> -#include <linux/pci.h> -#include <linux/slab.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_sgbuf.h> -#include <sound/pcm_params.h> -#include <sound/info.h> -#include <sound/ac97_codec.h> -#include <sound/mpu401.h> -#define SNDRV_GET_ID -#include <sound/initval.h> - -MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); -MODULE_DESCRIPTION("VIA VT82C686A"); -MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{VIA,VT82C686A,pci},{VIA,VT82C686B}}"); - -static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ -static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ -static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ -static long snd_mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; -static int snd_ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 48000}; - -MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); -MODULE_PARM_DESC(snd_index, "Index value for VIA 82C686A bridge."); -MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); -MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); -MODULE_PARM_DESC(snd_id, "ID string for VIA 82C686A bridge."); -MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); -MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); -MODULE_PARM_DESC(snd_enable, "Enable audio part of VIA 82C686A bridge."); -MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); -MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); -MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port."); -MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT_DESC); -MODULE_PARM(snd_ac97_clock, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); -MODULE_PARM_DESC(snd_ac97_clock, "AC'97 codec clock (default 48000Hz)."); -MODULE_PARM_SYNTAX(snd_ac97_clock, SNDRV_ENABLED ",default:48000"); - -/* - * Direct registers - */ - -#ifndef PCI_DEVICE_ID_VIA_82C686_5 -#define PCI_DEVICE_ID_VIA_82C686_5 0x3058 -#endif - -#define VIAREG(via, x) ((via)->port + VIA_REG_##x) - -/* offsets */ -#define VIA_REG_OFFSET_STATUS 0x00 /* byte - channel status */ -#define VIA_REG_STAT_ACTIVE 0x80 /* 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_PAUSE 0x08 /* RW */ -#define VIA_REG_CTRL_RESET 0x01 /* RW - probably reset? undocumented */ -#define VIA_REG_OFFSET_TYPE 0x02 /* byte - channel type */ -#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_CURR_COUNT 0x0c /* dword - channel current count */ -/* playback block */ -#define VIA_REG_PLAYBACK_STATUS 0x00 /* byte - channel status */ -#define VIA_REG_PLAYBACK_CONTROL 0x01 /* byte - channel control */ -#define VIA_REG_PLAYBACK_TYPE 0x02 /* byte - channel type */ -#define VIA_REG_PLAYBACK_TABLE_PTR 0x04 /* dword - channel table pointer */ -#define VIA_REG_PLAYBACK_CURR_PTR 0x04 /* dword - channel current pointer */ -#define VIA_REG_PLAYBACK_CURR_COUNT 0x0c /* dword - channel current count */ -/* capture block */ -#define VIA_REG_CAPTURE_STATUS 0x10 /* byte - channel status */ -#define VIA_REG_CAPTURE_CONTROL 0x11 /* byte - channel control */ -#define VIA_REG_CAPTURE_TYPE 0x12 /* byte - channel type */ -#define VIA_REG_CAPTURE_TABLE_PTR 0x14 /* dword - channel table pointer */ -#define VIA_REG_CAPTURE_CURR_PTR 0x14 /* dword - channel current pointer */ -#define VIA_REG_CAPTURE_CURR_COUNT 0x1c /* dword - channel current count */ -/* FM block */ -#define VIA_REG_FM_STATUS 0x20 /* byte - channel status */ -#define VIA_REG_FM_CONTROL 0x21 /* byte - channel control */ -#define VIA_REG_FM_TYPE 0x22 /* byte - channel type */ -#define VIA_REG_FM_TABLE_PTR 0x24 /* dword - channel table pointer */ -#define VIA_REG_FM_CURR_PTR 0x24 /* dword - channel current pointer */ -#define VIA_REG_FM_CURR_COUNT 0x2c /* dword - channel current count */ -/* 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 */ - -#define VIA_TBL_BIT_FLAG 0x40000000 -#define VIA_TBL_BIT_EOL 0x80000000 - -/* - * pcm stream - */ - -typedef struct { - unsigned long reg_offset; - snd_pcm_substream_t *substream; - int running; - unsigned int size; - unsigned int fragsize; - unsigned int frags; - unsigned int lastptr; - unsigned int lastcount; - unsigned int page_per_frag; - unsigned int curidx; - unsigned int tbl_entries; /* number of descriptor table entries */ - unsigned int tbl_size; /* size of a table entry */ - u32 *table; /* physical address + flag */ - dma_addr_t table_addr; -} viadev_t; - - -static int build_via_table(viadev_t *dev, snd_pcm_substream_t *substream, - struct pci_dev *pci) -{ - int i, size; - struct snd_sg_buf *sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return -EINVAL); - - if (dev->table) { - snd_free_pci_pages(pci, PAGE_ALIGN(dev->tbl_entries * 8), dev->table, dev->table_addr); - dev->table = NULL; - } - - /* allocate buffer descriptor lists */ - if (dev->fragsize < PAGE_SIZE) { - dev->tbl_size = dev->fragsize; - dev->tbl_entries = dev->frags; - dev->page_per_frag = 1; - } else { - dev->tbl_size = PAGE_SIZE; - dev->tbl_entries = sgbuf->pages; - dev->page_per_frag = dev->fragsize >> PAGE_SHIFT; - } - /* the start of each lists must be aligned to 8 bytes, - * but the kernel pages are much bigger, so we don't care - */ - dev->table = (u32*)snd_malloc_pci_pages(pci, PAGE_ALIGN(dev->tbl_entries * 8), &dev->table_addr); - if (! dev->table) - return -ENOMEM; - - if (dev->tbl_size < PAGE_SIZE) { - for (i = 0; i < dev->tbl_entries; i++) - dev->table[i << 1] = cpu_to_le32((u32)sgbuf->table[0].addr + dev->fragsize * i); - } else { - for (i = 0; i < dev->tbl_entries; i++) - dev->table[i << 1] = cpu_to_le32((u32)sgbuf->table[i].addr); - } - size = dev->size; - for (i = 0; i < dev->tbl_entries - 1; i++) { - dev->table[(i << 1) + 1] = cpu_to_le32(VIA_TBL_BIT_FLAG | dev->tbl_size); - size -= dev->tbl_size; - } - dev->table[(dev->tbl_entries << 1) - 1] = cpu_to_le32(VIA_TBL_BIT_EOL | size); - - return 0; -} - - -static void clean_via_table(viadev_t *dev, snd_pcm_substream_t *substream, - struct pci_dev *pci) -{ - if (dev->table) { - snd_free_pci_pages(pci, PAGE_ALIGN(dev->tbl_entries * 8), dev->table, dev->table_addr); - dev->table = NULL; - } -} - - -/* - */ - -typedef struct _snd_via686a via686a_t; -#define chip_t via686a_t - -struct _snd_via686a { - int irq; - - unsigned long port; - struct resource *res_port; - unsigned char revision; - - unsigned char old_legacy; - unsigned char old_legacy_cfg; - - struct pci_dev *pci; - snd_card_t *card; - - snd_pcm_t *pcm; - /*snd_pcm_t *pcm_fm;*/ - viadev_t playback; - viadev_t capture; - /*viadev_t playback_fm;*/ - - snd_rawmidi_t *rmidi; - - ac97_t *ac97; - unsigned int ac97_clock; - unsigned int ac97_secondary; /* secondary AC'97 codec is present */ - - spinlock_t reg_lock; - snd_info_entry_t *proc_entry; -}; - -static struct pci_device_id snd_via686a_ids[] __devinitdata = { - { 0x1106, 0x3058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 686A */ - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, snd_via686a_ids); - -/* - * Basic I/O - */ - -static inline unsigned int snd_via686a_codec_xread(via686a_t *chip) -{ - return inl(VIAREG(chip, AC97)); -} - -static inline void snd_via686a_codec_xwrite(via686a_t *chip, unsigned int val) -{ - outl(val, VIAREG(chip, AC97)); -} - -static int snd_via686a_codec_ready(via686a_t *chip, int secondary) -{ - unsigned int timeout = 1000; /* 1ms */ - unsigned int val; - - while (timeout-- > 0) { - udelay(1); - if (!((val = snd_via686a_codec_xread(chip)) & VIA_REG_AC97_BUSY)) - return val & 0xffff; - } - snd_printk("codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_via686a_codec_xread(chip)); - return -EIO; -} - -static int snd_via686a_codec_valid(via686a_t *chip, int secondary) -{ - unsigned int timeout = 1000; /* 1ms */ - unsigned int val; - unsigned int stat = !secondary ? VIA_REG_AC97_PRIMARY_VALID : - VIA_REG_AC97_SECONDARY_VALID; - - while (timeout-- > 0) { - udelay(1); - if ((val = snd_via686a_codec_xread(chip)) & stat) - return val & 0xffff; - } - snd_printk("codec_valid: codec %i is not valid [0x%x]\n", secondary, snd_via686a_codec_xread(chip)); - return -EIO; -} - -static void snd_via686a_codec_wait(ac97_t *ac97) -{ - via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return); - int err; - err = snd_via686a_codec_ready(chip, ac97->num); - /* here we need to wait fairly for long time.. */ - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ/2); -} - -static void snd_via686a_codec_write(ac97_t *ac97, - unsigned short reg, - unsigned short val) -{ - via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return); - 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; - spin_lock(&chip->reg_lock); - snd_via686a_codec_xwrite(chip, xval); - snd_via686a_codec_ready(chip, ac97->num); - spin_unlock(&chip->reg_lock); -} - -static unsigned short snd_via686a_codec_read(ac97_t *ac97, unsigned short reg) -{ - via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return ~0); - unsigned int xval, val = 0xffff; - int again = 0; - - xval = !ac97->num ? VIA_REG_AC97_CODEC_ID_PRIMARY : VIA_REG_AC97_CODEC_ID_SECONDARY; - xval <<= VIA_REG_AC97_CODEC_ID_SHIFT; - xval = (!ac97->num ? VIA_REG_AC97_PRIMARY_VALID : VIA_REG_AC97_SECONDARY_VALID); - xval |= VIA_REG_AC97_READ; - xval |= reg << VIA_REG_AC97_CMD_SHIFT; - spin_lock(&chip->reg_lock); - while (1) { - if (again++ > 3) { - spin_unlock(&chip->reg_lock); - return 0xffff; - } - snd_via686a_codec_xwrite(chip, xval); - if (snd_via686a_codec_ready(chip, ac97->num) < 0) - continue; - if (snd_via686a_codec_valid(chip, ac97->num) >= 0) { - udelay(25); - val = snd_via686a_codec_xread(chip); - break; - } - } - spin_unlock(&chip->reg_lock); - return val & 0xffff; -} - -#if 0 -static void snd_via686a_channel_print(via686a_t *chip, viadev_t *viadev) -{ - unsigned long port = chip->port + viadev->reg_offset; - - printk("[0x%x] status = 0x%x, control = 0x%x, type = 0x%x, ptr = 0x%x, count = 0x%x\n", - port, - inb(port + VIA_REG_OFFSET_STATUS), - inb(port + VIA_REG_OFFSET_CONTROL), - inb(port + VIA_REG_OFFSET_TYPE), - inl(port + VIA_REG_OFFSET_CURR_PTR), - inl(port + VIA_REG_OFFSET_CURR_COUNT)); -} -#endif - -static void snd_via686a_channel_reset(via686a_t *chip, viadev_t *viadev) -{ - unsigned long port = chip->port + viadev->reg_offset; - - outb(VIA_REG_CTRL_PAUSE | VIA_REG_CTRL_TERMINATE | VIA_REG_CTRL_RESET, port + VIA_REG_OFFSET_CONTROL); - udelay(50); - outb(0x00, port + VIA_REG_OFFSET_CONTROL); - outb(0xff, port + VIA_REG_OFFSET_STATUS); - outb(0x00, port + VIA_REG_OFFSET_TYPE); - outl(0, port + VIA_REG_OFFSET_CURR_PTR); -} - -static int snd_via686a_trigger(via686a_t *chip, viadev_t *viadev, int cmd) -{ - unsigned char val = 0; - unsigned long port = chip->port + viadev->reg_offset; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - val = VIA_REG_CTRL_START; - viadev->running = 1; - break; - case SNDRV_PCM_TRIGGER_STOP: - val = VIA_REG_CTRL_TERMINATE | VIA_REG_CTRL_RESET; - viadev->running = 0; - break; - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - val = VIA_REG_CTRL_PAUSE; - viadev->running = 1; - break; - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - val = 0; - viadev->running = 0; - break; - default: - return -EINVAL; - } - outb(val, port + VIA_REG_OFFSET_CONTROL); - if (cmd == SNDRV_PCM_TRIGGER_STOP) - snd_via686a_channel_reset(chip, viadev); - return 0; -} - - -static int snd_via686a_setup_periods(via686a_t *chip, viadev_t *viadev, - snd_pcm_substream_t *substream) -{ - snd_pcm_runtime_t *runtime = substream->runtime; - unsigned long port = chip->port + viadev->reg_offset; - int v, err; - - viadev->size = snd_pcm_lib_buffer_bytes(substream); - viadev->fragsize = snd_pcm_lib_period_bytes(substream); - viadev->frags = runtime->periods; - viadev->lastptr = ~0; - viadev->lastcount = ~0; - viadev->curidx = 0; - - /* the period size must be in power of 2 */ - v = ld2(viadev->fragsize); - if (viadev->fragsize != (1 << v)) { - snd_printd(KERN_ERR "invalid fragment size %d\n", viadev->fragsize); - return -EINVAL; - } - - snd_via686a_channel_reset(chip, viadev); - - err = build_via_table(viadev, substream, chip->pci); - if (err < 0) - return err; - - runtime->dma_bytes = viadev->size; - outl((u32)viadev->table_addr, port + VIA_REG_OFFSET_TABLE_PTR); - 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, port + VIA_REG_OFFSET_TYPE); - return 0; -} - -/* - * Interrupt handler - */ - -static inline void snd_via686a_update(via686a_t *chip, viadev_t *viadev) -{ - outb(VIA_REG_STAT_FLAG | VIA_REG_STAT_EOL, VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset); - if (viadev->substream && viadev->running) { - viadev->curidx++; - if (viadev->curidx >= viadev->page_per_frag) { - viadev->curidx = 0; - spin_unlock(&chip->reg_lock); - snd_pcm_period_elapsed(viadev->substream); - spin_lock(&chip->reg_lock); - } - } -} - -static void snd_via686a_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - via686a_t *chip = snd_magic_cast(via686a_t, dev_id, return); - unsigned int status; - - spin_lock(&chip->reg_lock); - status = inl(VIAREG(chip, SGD_SHADOW)); - if ((status & 0x00000077) == 0) { - spin_unlock(&chip->reg_lock); - if (chip->rmidi != NULL) { - snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); - } - return; - } - if (inb(VIAREG(chip, PLAYBACK_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG)) - snd_via686a_update(chip, &chip->playback); - if (inb(VIAREG(chip, CAPTURE_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG)) - snd_via686a_update(chip, &chip->capture); - /*if (inb(VIAREG(chip, FM_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG)) - snd_via686a_update(chip, &chip->playback_fm);*/ - spin_unlock(&chip->reg_lock); -} - -/* - * PCM part - */ - -static int snd_via686a_playback_trigger(snd_pcm_substream_t * substream, - int cmd) -{ - via686a_t *chip = snd_pcm_substream_chip(substream); - - return snd_via686a_trigger(chip, &chip->playback, cmd); -} - -static int snd_via686a_capture_trigger(snd_pcm_substream_t * substream, - int cmd) -{ - via686a_t *chip = snd_pcm_substream_chip(substream); - - return snd_via686a_trigger(chip, &chip->capture, cmd); -} - -static int snd_via686a_hw_params(snd_pcm_substream_t * substream, - snd_pcm_hw_params_t * hw_params) -{ - return snd_pcm_sgbuf_alloc(substream, params_buffer_bytes(hw_params)); -} - -static int snd_via686a_hw_free(snd_pcm_substream_t * substream) -{ - return 0; -} - -static int snd_via686a_playback_prepare(snd_pcm_substream_t * substream) -{ - via686a_t *chip = snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; - - snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); - return snd_via686a_setup_periods(chip, &chip->playback, substream); -} - -static int snd_via686a_capture_prepare(snd_pcm_substream_t * substream) -{ - via686a_t *chip = snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; - - snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate); - return snd_via686a_setup_periods(chip, &chip->capture, substream); -} - -static inline unsigned int snd_via686a_cur_ptr(via686a_t *chip, viadev_t *viadev) -{ - unsigned int val, ptr, count; - - ptr = inl(VIAREG(chip, OFFSET_CURR_PTR) + viadev->reg_offset); - count = inl(VIAREG(chip, OFFSET_CURR_COUNT) + viadev->reg_offset); - if (ptr == viadev->lastptr && count > viadev->lastcount) - ptr += 8; - if (!(inb(VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset) & VIA_REG_STAT_ACTIVE)) - return 0; - snd_assert(viadev->tbl_entries, return 0); - /* get index */ - if (ptr <= (unsigned int)viadev->table_addr) - val = 0; - else - val = ((ptr - (unsigned int)viadev->table_addr) / 8 - 1) % viadev->tbl_entries; - if (val < viadev->tbl_entries - 1) { - val *= viadev->tbl_size; - val += viadev->tbl_size - count; - } else { - val *= viadev->tbl_size; - val += (viadev->size % viadev->tbl_size) + 1 - count; - } - viadev->lastptr = ptr; - viadev->lastcount = count; - // printk("pointer: ptr = 0x%x (%i), count = 0x%x, val = 0x%x\n", ptr, count, val); - return val; -} - -static snd_pcm_uframes_t snd_via686a_playback_pointer(snd_pcm_substream_t * substream) -{ - via686a_t *chip = snd_pcm_substream_chip(substream); - return bytes_to_frames(substream->runtime, snd_via686a_cur_ptr(chip, &chip->playback)); -} - -static snd_pcm_uframes_t snd_via686a_capture_pointer(snd_pcm_substream_t * substream) -{ - via686a_t *chip = snd_pcm_substream_chip(substream); - return bytes_to_frames(substream->runtime, snd_via686a_cur_ptr(chip, &chip->capture)); -} - -static snd_pcm_hardware_t snd_via686a_playback = -{ - .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE), - .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, - .rates = 0, - .rate_min = 8000, - .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 = 128, - .fifo_size = 0, -}; - -static snd_pcm_hardware_t snd_via686a_capture = -{ - .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID), - .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, - .rates = 0, - .rate_min = 8000, - .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 = 128, - .fifo_size = 0, -}; - -static int snd_via686a_playback_open(snd_pcm_substream_t * substream) -{ - via686a_t *chip = snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; - int err; - - chip->playback.substream = substream; - runtime->hw = snd_via686a_playback; - runtime->hw.rates = chip->ac97->rates[AC97_RATES_FRONT_DAC]; - if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) - runtime->hw.rate_min = 48000; - if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0) - return err; - if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0) - return err; - /* 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; - return 0; -} - -static int snd_via686a_capture_open(snd_pcm_substream_t * substream) -{ - via686a_t *chip = snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; - int err; - - chip->capture.substream = substream; - runtime->hw = snd_via686a_capture; - runtime->hw.rates = chip->ac97->rates[AC97_RATES_ADC]; - if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) - runtime->hw.rate_min = 48000; - if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0) - return err; - if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0) - return err; - if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) - return err; - return 0; -} - -static int snd_via686a_playback_close(snd_pcm_substream_t * substream) -{ - via686a_t *chip = snd_pcm_substream_chip(substream); - - clean_via_table(&chip->playback, substream, chip->pci); - snd_pcm_sgbuf_delete(substream); - chip->playback.substream = NULL; - return 0; -} - -static int snd_via686a_capture_close(snd_pcm_substream_t * substream) -{ - via686a_t *chip = snd_pcm_substream_chip(substream); - - clean_via_table(&chip->capture, substream, chip->pci); - snd_pcm_sgbuf_delete(substream); - chip->capture.substream = NULL; - return 0; -} - -static snd_pcm_ops_t snd_via686a_playback_ops = { - .open = snd_via686a_playback_open, - .close = snd_via686a_playback_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_via686a_hw_params, - .hw_free = snd_via686a_hw_free, - .prepare = snd_via686a_playback_prepare, - .trigger = snd_via686a_playback_trigger, - .pointer = snd_via686a_playback_pointer, - .copy = snd_pcm_sgbuf_ops_copy_playback, - .silence = snd_pcm_sgbuf_ops_silence, - .page = snd_pcm_sgbuf_ops_page, -}; - -static snd_pcm_ops_t snd_via686a_capture_ops = { - .open = snd_via686a_capture_open, - .close = snd_via686a_capture_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_via686a_hw_params, - .hw_free = snd_via686a_hw_free, - .prepare = snd_via686a_capture_prepare, - .trigger = snd_via686a_capture_trigger, - .pointer = snd_via686a_capture_pointer, - .copy = snd_pcm_sgbuf_ops_copy_capture, - .silence = snd_pcm_sgbuf_ops_silence, - .page = snd_pcm_sgbuf_ops_page, -}; - -static void snd_via686a_pcm_free(snd_pcm_t *pcm) -{ - via686a_t *chip = snd_magic_cast(via686a_t, pcm->private_data, return); - chip->pcm = NULL; -} - -static int __devinit snd_via686a_pcm(via686a_t *chip, int device, snd_pcm_t ** rpcm) -{ - snd_pcm_t *pcm; - int err; - - if (rpcm) - *rpcm = NULL; - err = snd_pcm_new(chip->card, "VIA 82C686A", device, 1, 1, &pcm); - if (err < 0) - return err; - - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via686a_playback_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via686a_capture_ops); - - pcm->private_data = chip; - pcm->private_free = snd_via686a_pcm_free; - pcm->info_flags = 0; - strcpy(pcm->name, "VIA 82C686A"); - chip->pcm = pcm; - - if (rpcm) - *rpcm = NULL; - return 0; -} - - -/* - * Mixer part - */ - -static void snd_via686a_mixer_free_ac97(ac97_t *ac97) -{ - via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return); - chip->ac97 = NULL; -} - -static int __devinit snd_via686a_mixer(via686a_t *chip) -{ - ac97_t ac97; - int err; - - memset(&ac97, 0, sizeof(ac97)); - ac97.write = snd_via686a_codec_write; - ac97.read = snd_via686a_codec_read; - ac97.wait = snd_via686a_codec_wait; - ac97.private_data = chip; - ac97.private_free = snd_via686a_mixer_free_ac97; - ac97.clock = chip->ac97_clock; - if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0) - return err; - return 0; -} - -/* - * joystick - */ - -static int snd_via686a_joystick_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *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_via686a_joystick_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) -{ - via686a_t *chip = snd_kcontrol_chip(kcontrol); - u16 val; - - pci_read_config_word(chip->pci, 0x42, &val); - ucontrol->value.integer.value[0] = (val & 0x08) ? 1 : 0; - return 0; -} - -static int snd_via686a_joystick_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) -{ - via686a_t *chip = snd_kcontrol_chip(kcontrol); - u16 val, oval; - - pci_read_config_word(chip->pci, 0x42, &oval); - val = oval & ~0x08; - if (ucontrol->value.integer.value[0]) - val |= 0x08; - if (val != oval) { - pci_write_config_word(chip->pci, 0x42, val); - return 1; - } - return 0; -} - -static snd_kcontrol_new_t snd_via686a_joystick_control __devinitdata = { - .name = "Joystick", - .iface = SNDRV_CTL_ELEM_IFACE_CARD, - .info = snd_via686a_joystick_info, - .get = snd_via686a_joystick_get, - .put = snd_via686a_joystick_put, -}; - -/* - * - */ - -static int __devinit snd_via686a_chip_init(via686a_t *chip) -{ - ac97_t ac97; - unsigned int val; - int max_count; - unsigned char pval; - - memset(&ac97, 0, sizeof(ac97)); - ac97.private_data = chip; - -#if 0 /* broken on K7M? */ - /* disable all legacy ports */ - pci_write_config_byte(chip->pci, 0x42, 0); -#endif - - /* cold reset only when link is down */ - pci_read_config_byte(chip->pci, 0x40, &pval); - if ((pval & 0x01) == 0) { - /* deassert ACLink reset, force SYNC */ - pci_write_config_byte(chip->pci, 0x41, 0xe0); - udelay(100); - pci_write_config_byte(chip->pci, 0x41, 0x00); - udelay(100); - /* 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, 0x41, 0xcc); - udelay(100); - } - - /* Make sure VRA is enabled, in case we didn't do a - * complete codec reset, above */ - pci_read_config_byte(chip->pci, 0x41, &pval); - if ((pval & 0xcc) != 0xcc) { - /* 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, 0x41, 0xcc); - udelay(100); - } - - /* wait until codec ready */ - max_count = ((3 * HZ) / 4) + 1; - do { - pci_read_config_byte(chip->pci, 0x40, &pval); - if (pval & 0x01) /* primary codec ready */ - break; - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - } while (--max_count > 0); - - if ((val = snd_via686a_codec_xread(chip)) & VIA_REG_AC97_BUSY) - snd_printk("AC'97 codec is not ready [0x%x]\n", val); - - /* and then reset codec.. */ - snd_via686a_codec_write(&ac97, AC97_RESET, 0x0000); - - /* check the primary codec */ - snd_via686a_codec_xwrite(chip, VIA_REG_AC97_READ | - VIA_REG_AC97_PRIMARY_VALID | - (VIA_REG_AC97_CODEC_ID_PRIMARY << VIA_REG_AC97_CODEC_ID_SHIFT)); - max_count = ((3 * HZ) / 4) + 1; - do { - if ((val = snd_via686a_codec_xread(chip)) & VIA_REG_AC97_PRIMARY_VALID) - goto __ac97_ok1; - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - } while (--max_count > 0); - snd_printk("Primary AC'97 codec is not valid [0x%x]\n", val); - - __ac97_ok1: -#if 0 /* FIXME: we don't support the second codec yet so skip the detection now.. */ - snd_via686a_codec_xwrite(chip, VIA_REG_AC97_READ | - VIA_REG_AC97_SECONDARY_VALID | - (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT)); - max_count = ((3 * HZ) / 4) + 1; - snd_via686a_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_via686a_codec_xread(chip)) & VIA_REG_AC97_SECONDARY_VALID) { - chip->ac97_secondary = 1; - goto __ac97_ok2; - } - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(1); - } while (--max_count > 0); - /* This is ok, the most of motherboards have only one codec */ - - __ac97_ok2: -#endif - -#if 0 - { - unsigned char cmdb; - - pci_read_config_byte(chip->pci, 0x40, &cmdb); - printk("PCI[0x40] = 0x%x\n", cmdb); - pci_read_config_byte(chip->pci, 0x42, &cmdb); - printk("PCI[0x42] = 0x%x\n", cmdb); - pci_read_config_byte(chip->pci, 0x43, &cmdb); - printk("PCI[0x43] = 0x%x\n", cmdb); - pci_read_config_byte(chip->pci, 0x44, &cmdb); - printk("PCI[0x44] = 0x%x\n", cmdb); - pci_read_config_byte(chip->pci, 0x48, &cmdb); - printk("PCI[0x48] = 0x%x\n", cmdb); - } -#endif - - /* route FM trap to IRQ, disable FM trap */ - pci_write_config_byte(chip->pci, 0x48, 0); - - /* disable all GPI interrupts */ - outl(0, chip->port + 0x8c); - - /* disable interrupts */ - snd_via686a_channel_reset(chip, &chip->playback); - snd_via686a_channel_reset(chip, &chip->capture); - /*snd_via686a_channel_reset(chip, &chip->playback_fm);*/ - return 0; -} - -static int snd_via686a_free(via686a_t *chip) -{ - if (chip->irq < 0) - goto __end_hw; - /* disable interrupts */ - snd_via686a_channel_reset(chip, &chip->playback); - snd_via686a_channel_reset(chip, &chip->capture); - /*snd_via686a_channel_reset(chip, &chip->playback_fm);*/ - /* --- */ - synchronize_irq(chip->irq); - __end_hw: - if (chip->res_port) { - release_resource(chip->res_port); - kfree_nocheck(chip->res_port); - } - if (chip->irq >= 0) - free_irq(chip->irq, (void *)chip); - pci_write_config_byte(chip->pci, 0x42, chip->old_legacy); - pci_write_config_byte(chip->pci, 0x43, chip->old_legacy_cfg); - snd_magic_kfree(chip); - return 0; -} - -static int snd_via686a_dev_free(snd_device_t *device) -{ - via686a_t *chip = snd_magic_cast(via686a_t, device->device_data, return -ENXIO); - return snd_via686a_free(chip); -} - -static int __devinit snd_via686a_create(snd_card_t * card, - struct pci_dev *pci, - unsigned int ac97_clock, - unsigned char old_legacy, - unsigned char old_legacy_cfg, - via686a_t ** r_via) -{ - via686a_t *chip; - int err; - static snd_device_ops_t ops = { - .dev_free = snd_via686a_dev_free, - }; - - if ((err = pci_enable_device(pci)) < 0) - return err; - - if ((chip = snd_magic_kcalloc(via686a_t, 0, GFP_KERNEL)) == NULL) - return -ENOMEM; - - chip->old_legacy = old_legacy; - chip->old_legacy_cfg = old_legacy_cfg; - - spin_lock_init(&chip->reg_lock); - chip->card = card; - chip->pci = pci; - chip->irq = -1; - chip->port = pci_resource_start(pci, 0); - if ((chip->res_port = request_region(chip->port, 256, "VIA686A")) == NULL) { - snd_via686a_free(chip); - snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->port, chip->port + 256 - 1); - return -EBUSY; - } - if (request_irq(pci->irq, snd_via686a_interrupt, SA_INTERRUPT|SA_SHIRQ, "VIA686A", (void *)chip)) { - snd_via686a_free(chip); - snd_printk("unable to grab IRQ %d\n", chip->irq); - return -EBUSY; - } - chip->irq = pci->irq; - if (ac97_clock >= 8000 && ac97_clock <= 48000) - chip->ac97_clock = ac97_clock; - pci_read_config_byte(pci, PCI_REVISION_ID, &chip->revision); - synchronize_irq(chip->irq); - - /* initialize offsets */ - chip->playback.reg_offset = VIA_REG_PLAYBACK_STATUS; - chip->capture.reg_offset = VIA_REG_CAPTURE_STATUS; - /*chip->playback_fm.reg_offset = VIA_REG_FM_STATUS;*/ - - if ((err = snd_via686a_chip_init(chip)) < 0) { - snd_via686a_free(chip); - return err; - } - - if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { - snd_via686a_free(chip); - return err; - } - - *r_via = chip; - return 0; -} - -static int __devinit snd_via686a_probe(struct pci_dev *pci, - const struct pci_device_id *id) -{ - static int dev; - snd_card_t *card; - via686a_t *chip; - int pcm_dev = 0; - unsigned char legacy; - unsigned char legacy_cfg; - int rev_h = 0, err; - - if (dev >= SNDRV_CARDS) - return -ENODEV; - if (!snd_enable[dev]) { - dev++; - return -ENOENT; - } - - card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); - if (card == NULL) - return -ENOMEM; - - pci_read_config_byte(pci, 0x42, &legacy); - pci_read_config_byte(pci, 0x43, &legacy_cfg); - - if ((err = snd_via686a_create(card, - pci, - snd_ac97_clock[dev], - legacy, - legacy_cfg, - &chip)) < 0) { - snd_card_free(card); - return err; - } - - - if (snd_via686a_mixer(chip) < 0) { - snd_card_free(card); - return err; - } - if (snd_via686a_pcm(chip, pcm_dev++, NULL) < 0) { - snd_card_free(card); - return err; - } -#if 0 - if (snd_via686a_pcm_fm(chip, pcm_dev++, NULL) < 0) { - snd_card_free(card); - return err; - } -#endif - - legacy |= 0x40; /* disable MIDI */ - legacy &= ~0x08; /* disable joystick */ - if (chip->revision >= 0x20) { - if (check_region(pci_resource_start(pci, 2), 4)) { - rev_h = 0; - legacy &= ~0x80; /* disable PCI I/O 2 */ - } else { - rev_h = 1; - legacy |= 0x80; /* enable PCI I/O 2 */ - } - } - pci_write_config_byte(pci, 0x42, legacy); - pci_write_config_byte(pci, 0x43, legacy_cfg); - if (rev_h && snd_mpu_port[dev] >= 0x200) { /* force MIDI */ - legacy |= 0x02; /* enable MPU */ - pci_write_config_dword(pci, 0x18, (snd_mpu_port[dev] & 0xfffc) | 0x01); - } else { - if (rev_h && (legacy & 0x02)) { - snd_mpu_port[dev] = pci_resource_start(pci, 2); - if (snd_mpu_port[dev] < 0x200) /* bad value */ - legacy &= ~0x02; /* disable MIDI */ - } else { - switch (snd_mpu_port[dev]) { /* force MIDI */ - case 0x300: - case 0x310: - case 0x320: - case 0x330: - legacy_cfg &= ~(3 << 2); - legacy_cfg |= (snd_mpu_port[dev] & 0x0030) >> 2; - legacy |= 0x02; - break; - default: /* no, use BIOS settings */ - if (legacy & 0x02) - snd_mpu_port[dev] = 0x300 + ((legacy_cfg & 0x000c) << 2); - } - } - } - pci_write_config_byte(pci, 0x42, legacy); - pci_write_config_byte(pci, 0x43, legacy_cfg); - if (legacy & 0x02) { - if (check_region(snd_mpu_port[dev], 2)) { - printk(KERN_WARNING "unable to get MPU-401 port at 0x%lx, skipping\n", snd_mpu_port[dev]); - legacy &= ~0x02; - pci_write_config_byte(pci, 0x42, legacy); - goto __skip_mpu; - } - if (snd_mpu401_uart_new(card, 0, MPU401_HW_VIA686A, - snd_mpu_port[dev], 0, - pci->irq, 0, - &chip->rmidi) < 0) { - printk(KERN_WARNING "unable to initialize MPU-401 at 0x%lx, skipping\n", snd_mpu_port[dev]); - legacy &= ~0x02; - pci_write_config_byte(pci, 0x42, legacy); - goto __skip_mpu; - } - legacy &= ~0x40; /* enable MIDI interrupt */ - pci_write_config_byte(pci, 0x42, legacy); - __skip_mpu: - ; - } - - /* card switches */ - err = snd_ctl_add(card, snd_ctl_new1(&snd_via686a_joystick_control, chip)); - if (err < 0) { - snd_card_free(card); - return err; - } - - strcpy(card->driver, "VIA686A"); - strcpy(card->shortname, "VIA 82C686A/B"); - - sprintf(card->longname, "%s at 0x%lx, irq %d", - card->shortname, chip->port, chip->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_via686a_remove(struct pci_dev *pci) -{ - snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); -} - -static struct pci_driver driver = { - .name = "VIA 82C686A/B", - .id_table = snd_via686a_ids, - .probe = snd_via686a_probe, - .remove = __devexit_p(snd_via686a_remove), -}; - -static int __init alsa_card_via686a_init(void) -{ - int err; - - if ((err = pci_module_init(&driver)) < 0) { -#ifdef MODULE - printk(KERN_ERR "VIA 82C686A soundcard not found or device busy\n"); -#endif - return err; - } - return 0; -} - -static void __exit alsa_card_via686a_exit(void) -{ - pci_unregister_driver(&driver); -} - -module_init(alsa_card_via686a_init) -module_exit(alsa_card_via686a_exit) - -#ifndef MODULE - -/* format is: snd-via686a=snd_enable,snd_index,snd_id, - snd_mpu_port,snd_ac97_clock */ - -static int __init alsa_card_via686a_setup(char *str) -{ - static unsigned __initdata nr_dev = 0; - - if (nr_dev >= SNDRV_CARDS) - return 0; - (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && - get_option(&str,&snd_index[nr_dev]) == 2 && - get_id(&str,&snd_id[nr_dev]) == 2 && - get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 && - get_option(&str,&snd_ac97_clock[nr_dev]) == 2); - nr_dev++; - return 1; -} - -__setup("snd-via686a=", alsa_card_via686a_setup); - -#endif /* ifndef MODULE */ diff --git a/sound/pci/via8233.c b/sound/pci/via8233.c deleted file mode 100644 index 4f17239c2e5b..000000000000 --- a/sound/pci/via8233.c +++ /dev/null @@ -1,1022 +0,0 @@ -/* - * ALSA driver for VIA VT8233 (South Bridge) - * - * Copyright (c) 2000 Tjeerd.Mulder@fujitsu-siemens.com - * This driver is based on VIA686 code by Jaroslav Kysela <perex@suse.cz> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include <sound/driver.h> -#include <asm/io.h> -#include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/init.h> -#include <linux/pci.h> -#include <linux/slab.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_sgbuf.h> -#include <sound/pcm_params.h> -#include <sound/info.h> -#include <sound/ac97_codec.h> -#include <sound/mpu401.h> -#define SNDRV_GET_ID -#include <sound/initval.h> - -MODULE_AUTHOR("Tjeerd.Mulder@fujitsu-siemens.com"); -MODULE_DESCRIPTION("VIA VT8233"); -MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{VIA,VT8233,pci}}"); - -static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ -static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ -static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ -static int snd_ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 48000}; - -MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); -MODULE_PARM_DESC(snd_index, "Index value for VIA 8233 bridge."); -MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); -MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); -MODULE_PARM_DESC(snd_id, "ID string for VIA 8233 bridge."); -MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); -MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); -MODULE_PARM_DESC(snd_enable, "Enable audio part of VIA 8233 bridge."); -MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); -MODULE_PARM(snd_ac97_clock, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); -MODULE_PARM_DESC(snd_ac97_clock, "AC'97 codec clock (default 48000Hz)."); -MODULE_PARM_SYNTAX(snd_ac97_clock, SNDRV_ENABLED ",default:48000"); - - -/* - * Direct registers - */ - -#ifndef PCI_DEVICE_ID_VIA_8233_5 -#define PCI_DEVICE_ID_VIA_8233_5 0x3059 -#endif - -/* revision numbers */ -#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 VIAREG(via, x) ((via)->port + VIA_REG_##x) - -/* offsets */ -#define VIA_REG_OFFSET_STATUS 0x00 /* byte - channel status */ -#define VIA_REG_STAT_ACTIVE 0x80 /* 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_INT (VIA_REG_CTRL_INT_FLAG | VIA_REG_CTRL_INT_EOL | VIA_REG_CTRL_AUTOSTART) -#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 VIA_REG_TYPE_16BIT 0x00200000 /* RW */ -#define VIA_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 */ - -#define VIA_NUM_OF_DMA_CHANNELS 2 - -/* playback block (VT8233/8233C) - channels 0-3 (0-0x3f) */ -#define VIA_REG_PLAYBACK_STATUS 0x00 /* byte - channel status */ -#define VIA_REG_PLAYBACK_CONTROL 0x01 /* byte - channel control */ -#define VIA_REG_PLAYBACK_VOLUME_L 0x02 /* byte */ -#define VIA_REG_PLAYBACK_VOLUME_R 0x03 /* byte */ -#define VIA_REG_PLAYBACK_TABLE_PTR 0x04 /* dword - channel table pointer */ -#define VIA_REG_PLAYBACK_CURR_PTR 0x04 /* dword - channel current pointer */ -#define VIA_REG_PLAYBACK_STOP_IDX 0x08 /* dword - stop index, channel type, sample rate */ -#define VIA_REG_PLAYBACK_CURR_COUNT 0x0c /* dword - channel current count (24 bit) */ -#define VIA_REG_PLAYBACK_CURR_INDEX 0x0f /* byte - channel current index */ - -/* multi-channel playback */ -#define VIA_REG_MULTPLAY_STATUS 0x40 /* byte - channel status */ -#define VIA_REG_MULTPLAY_CONTROL 0x41 /* byte - channel control */ -#define VIA_REG_MULTPLAY_FORMAT 0x42 /* 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_MULTPLAY_SCRATCH 0x43 /* byte - nop */ -#define VIA_REG_MULTPLAY_TABLE_PTR 0x44 /* dword - channel table pointer */ -#define VIA_REG_MULTPLAY_CURR_PTR 0x44 /* dword - channel current pointer */ -#define VIA_REG_MULTPLAY_STOP_IDX 0x48 /* dword - stop index, slots */ -#define VIA_REG_MULTPLAY_CURR_COUNT 0x4c /* dword - channel current count (24 bit) */ -#define VIA_REG_MULTIPLAY_CURR_INDEX 0x4f /* byte - channel current index */ - -/* capture block */ -#define VIA_REG_CAPTURE_STATUS 0x60 /* byte - channel status */ -#define VIA_REG_CAPTURE_CONTROL 0x61 /* byte - channel control */ -#define VIA_REG_CAPTURE_FIFO 0x62 /* byte - bit 6 = fifo enable */ -#define VIA_REG_CAPTURE_FIFO_ENABLE 0x40 -#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_REG_CAPTURE_TABLE_PTR 0x64 /* dword - channel table pointer */ -#define VIA_REG_CAPTURE_CURR_PTR 0x64 /* dword - channel current pointer */ -#define VIA_REG_CAPTURE_STOP_IDX 0x68 /* dword - stop index */ -#define VIA_REG_CAPTURE_CURR_COUNT 0x6c /* dword - channel current count (24 bit) */ -#define VIA_REG_CAPTURE_CURR_INDEX 0x6f /* byte - channel current index */ -/* 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_SECONDARY_VALID (1<<27) -#define VIA_REG_AC97_PRIMARY_VALID (1<<25) -#define VIA_REG_AC97_ANY_VALID (VIA_REG_AC97_PRIMARY_VALID | VIA_REG_AC97_SECONDARY_VALID | (1<<28)| (1<<29)) -#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 */ - -#define VIA_TBL_BIT_FLAG 0x40000000 -#define VIA_TBL_BIT_EOL 0x80000000 - -/* - * - */ - -typedef struct { - unsigned long reg_offset; - snd_pcm_substream_t *substream; - int running; - unsigned int size; - unsigned int fragsize; - unsigned int frags; - unsigned int page_per_frag; - unsigned int curidx; - unsigned int tbl_entries; /* number of descriptor table entries */ - unsigned int tbl_size; /* size of a table entry */ - u32 *table; /* physical address + flag */ - dma_addr_t table_addr; -} viadev_t; - -static int build_via_table(viadev_t *dev, snd_pcm_substream_t *substream, - struct pci_dev *pci) -{ - int i, size; - struct snd_sg_buf *sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return -EINVAL); - - if (dev->table) { - snd_free_pci_pages(pci, PAGE_ALIGN(dev->tbl_entries * 8), dev->table, dev->table_addr); - dev->table = NULL; - } - - /* allocate buffer descriptor lists */ - if (dev->fragsize < PAGE_SIZE) { - dev->tbl_size = dev->fragsize; - dev->tbl_entries = dev->frags; - dev->page_per_frag = 1; - } else { - dev->tbl_size = PAGE_SIZE; - dev->tbl_entries = sgbuf->pages; - dev->page_per_frag = dev->fragsize >> PAGE_SHIFT; - } - /* the start of each lists must be aligned to 8 bytes, - * but the kernel pages are much bigger, so we don't care - */ - dev->table = (u32*)snd_malloc_pci_pages(pci, PAGE_ALIGN(dev->tbl_entries * 8), &dev->table_addr); - if (! dev->table) - return -ENOMEM; - - if (dev->tbl_size < PAGE_SIZE) { - for (i = 0; i < dev->tbl_entries; i++) - dev->table[i << 1] = cpu_to_le32((u32)sgbuf->table[0].addr + dev->fragsize * i); - } else { - for (i = 0; i < dev->tbl_entries; i++) - dev->table[i << 1] = cpu_to_le32((u32)sgbuf->table[i].addr); - } - size = dev->size; - for (i = 0; i < dev->tbl_entries - 1; i++) { - dev->table[(i << 1) + 1] = cpu_to_le32(VIA_TBL_BIT_FLAG | dev->tbl_size); - size -= dev->tbl_size; - } - dev->table[(dev->tbl_entries << 1) - 1] = cpu_to_le32(VIA_TBL_BIT_EOL | size); - - return 0; -} - - -static void clean_via_table(viadev_t *dev, snd_pcm_substream_t *substream, - struct pci_dev *pci) -{ - if (dev->table) { - snd_free_pci_pages(pci, PAGE_ALIGN(dev->tbl_entries * 8), dev->table, dev->table_addr); - dev->table = NULL; - } -} - - -/* - */ - -typedef struct _snd_via8233 via8233_t; -#define chip_t via8233_t - -struct _snd_via8233 { - int irq; - - unsigned long port; - struct resource *res_port; - unsigned char revision; - - struct pci_dev *pci; - snd_card_t *card; - - snd_pcm_t *pcm; - viadev_t playback; - viadev_t capture; - - ac97_t *ac97; - unsigned int ac97_clock; - - spinlock_t reg_lock; - snd_info_entry_t *proc_entry; -}; - -static struct pci_device_id snd_via8233_ids[] __devinitdata = { - { 0x1106, 0x3059, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* VT8233 */ - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, snd_via8233_ids); - -/* - * Basic I/O - */ - -static inline unsigned int snd_via8233_codec_xread(via8233_t *chip) -{ - /* this acces should be atomic */ - return inl(VIAREG(chip, AC97)); -} - -static inline void snd_via8233_codec_xwrite(via8233_t *chip, unsigned int val) -{ - /* this acces should be atomic */ - outl(val, VIAREG(chip, AC97)); -} - -static int snd_via8233_codec_ready(via8233_t *chip, int secondary) -{ - int time; - - time = 1000; - do { - udelay(1); - if ((snd_via8233_codec_xread(chip) & VIA_REG_AC97_BUSY) == 0) - return 0; - } while (time--); - snd_printk("codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_via8233_codec_xread(chip)); - return -EIO; -} - -static void snd_via8233_codec_write(ac97_t *ac97, - unsigned short reg, - unsigned short val) -{ - via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return); - unsigned int xval; - - xval = (ac97->num) << VIA_REG_AC97_CODEC_ID_SHIFT; - xval |= ac97->num ? VIA_REG_AC97_SECONDARY_VALID : VIA_REG_AC97_PRIMARY_VALID; - xval |= (reg & 0x7f) << VIA_REG_AC97_CMD_SHIFT; - xval |= val << VIA_REG_AC97_DATA_SHIFT; - spin_lock(&chip->reg_lock); - snd_via8233_codec_ready(chip, ac97->num); - snd_via8233_codec_xwrite(chip, xval); - snd_via8233_codec_ready(chip, ac97->num); - spin_unlock(&chip->reg_lock); -} - -static unsigned short snd_via8233_codec_read(ac97_t *ac97, unsigned short reg) -{ - via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return ~0); - unsigned int val; - int valid = ac97->num ? VIA_REG_AC97_SECONDARY_VALID : VIA_REG_AC97_PRIMARY_VALID; - int i; - - val = (ac97->num) << VIA_REG_AC97_CODEC_ID_SHIFT; - val |= valid; - val |= VIA_REG_AC97_READ; - val |= (reg & 0x7f) << VIA_REG_AC97_CMD_SHIFT; - spin_lock(&chip->reg_lock); - snd_via8233_codec_ready(chip, ac97->num); - snd_via8233_codec_xwrite(chip, val); - snd_via8233_codec_ready(chip, ac97->num); - for (i=1000; i--;) { - val = snd_via8233_codec_xread(chip); - if (val & valid) { - spin_unlock(&chip->reg_lock); - return (unsigned short)val; - } - } - spin_unlock(&chip->reg_lock); - snd_printk("codec_read: codec %i is not valid [0x%x]\n", ac97->num, val); - /* have to return some value, this is better then 0 */ - return ~0; -} - -#if 0 -static void snd_via8233_channel_print(via8233_t *chip, viadev_t *viadev) -{ - unsigned long port = chip->port + viadev->reg_offset; - - printk("[0x%x] status = 0x%x, control = 0x%x, type = 0x%x, ptr = 0x%x, count = 0x%x\n", - port, - inb(port + VIA_REG_OFFSET_STATUS), - inb(port + VIA_REG_OFFSET_CONTROL), - inl(port + VIA_REG_OFFSET_STOP_IDX), - inl(port + VIA_REG_OFFSET_CURR_PTR), - inl(port + VIA_REG_OFFSET_CURR_COUNT)); -} -#endif - -static void snd_via8233_channel_reset(via8233_t *chip, viadev_t *viadev) -{ - unsigned long port = chip->port + viadev->reg_offset; - - outb(VIA_REG_CTRL_TERMINATE, port + VIA_REG_OFFSET_CONTROL); - udelay(50); - /* disable interrupts */ - outb(0, port + VIA_REG_OFFSET_CONTROL); - /* clear interrupts */ - outb(0x3, port + VIA_REG_OFFSET_STATUS); -} - -static int snd_via8233_trigger(via8233_t *chip, viadev_t *viadev, int cmd) -{ - unsigned char val; - unsigned long port = chip->port + viadev->reg_offset; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - val = VIA_REG_CTRL_INT | VIA_REG_CTRL_START; - viadev->running = 1; - break; - case SNDRV_PCM_TRIGGER_STOP: - val = VIA_REG_CTRL_TERMINATE; - viadev->running = 0; - break; - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - val = VIA_REG_CTRL_INT | VIA_REG_CTRL_PAUSE; - viadev->running = 1; - break; - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - val = VIA_REG_CTRL_INT; - viadev->running = 0; - break; - default: - return -EINVAL; - } - outb(val, port + VIA_REG_OFFSET_CONTROL); - if (cmd == SNDRV_PCM_TRIGGER_STOP) - snd_via8233_channel_reset(chip, viadev); - return 0; -} - -static int snd_via8233_setup_periods(via8233_t *chip, viadev_t *viadev, - snd_pcm_substream_t *substream) -{ - snd_pcm_runtime_t *runtime = substream->runtime; - unsigned long port = chip->port + viadev->reg_offset; - int v, err; - - viadev->size = snd_pcm_lib_buffer_bytes(substream); - viadev->fragsize = snd_pcm_lib_period_bytes(substream); - viadev->frags = runtime->periods; - viadev->curidx = 0; - - /* the period size must be in power of 2 */ - v = ld2(viadev->fragsize); - if (viadev->fragsize != (1 << v)) { - snd_printd(KERN_ERR "invalid fragment size %d\n", viadev->fragsize); - return -EINVAL; - } - - snd_via8233_channel_reset(chip, viadev); - - err = build_via_table(viadev, substream, chip->pci); - if (err < 0) - return err; - - runtime->dma_bytes = viadev->size; - outl((u32)viadev->table_addr, port + VIA_REG_OFFSET_TABLE_PTR); - if (viadev->reg_offset == VIA_REG_MULTPLAY_STATUS) { - unsigned int slots; - int fmt = (runtime->format == SNDRV_PCM_FORMAT_S16_LE) ? VIA_REG_MULTPLAY_FMT_16BIT : VIA_REG_MULTPLAY_FMT_8BIT; - fmt |= runtime->channels << 4; - outb(fmt, chip->port + VIA_REG_MULTPLAY_FORMAT); - /* set sample number to slot 3, 4, 7, 8, 6, 9 */ - switch (runtime->channels) { - case 1: slots = (1<<0) | (1<<4); break; - case 2: slots = (1<<0) | (2<<4); break; - case 4: slots = (1<<0) | (2<<4) | (3<<8) | (4<<12); break; - case 6: slots = (1<<0) | (2<<4) | (5<<8) | (6<<12) | (3<<16) | (4<<20); break; - default: slots = 0; break; - } - /* STOP index is never reached */ - outl(0xff000000 | slots, chip->port + VIA_REG_MULTPLAY_STOP_IDX); - } else { - outl((runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA_REG_TYPE_16BIT : 0) | - (runtime->channels > 1 ? VIA_REG_TYPE_STEREO : 0) | - 0xff000000, /* STOP index is never reached */ - port + VIA_REG_OFFSET_STOP_IDX); - } - return 0; -} - -/* - * Interrupt handler - */ - -static inline void snd_via8233_update(via8233_t *chip, viadev_t *viadev) -{ - outb(VIA_REG_STAT_FLAG | VIA_REG_STAT_EOL, VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset); - if (viadev->substream && viadev->running) { - viadev->curidx++; - if (viadev->curidx >= viadev->page_per_frag) { - viadev->curidx = 0; - spin_unlock(&chip->reg_lock); - snd_pcm_period_elapsed(viadev->substream); - spin_lock(&chip->reg_lock); - } - } -} - -static void snd_via8233_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - via8233_t *chip = snd_magic_cast(via8233_t, dev_id, return); - - spin_lock(&chip->reg_lock); - if (inb(chip->port + chip->playback.reg_offset) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG)) - snd_via8233_update(chip, &chip->playback); - if (inb(chip->port + chip->capture.reg_offset) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG)) - snd_via8233_update(chip, &chip->capture); - spin_unlock(&chip->reg_lock); -} - -/* - * PCM part - */ - -static int snd_via8233_playback_trigger(snd_pcm_substream_t * substream, - int cmd) -{ - via8233_t *chip = snd_pcm_substream_chip(substream); - - return snd_via8233_trigger(chip, &chip->playback, cmd); -} - -static int snd_via8233_capture_trigger(snd_pcm_substream_t * substream, - int cmd) -{ - via8233_t *chip = snd_pcm_substream_chip(substream); - return snd_via8233_trigger(chip, &chip->capture, cmd); -} - -static int snd_via8233_hw_params(snd_pcm_substream_t * substream, - snd_pcm_hw_params_t * hw_params) -{ - return snd_pcm_sgbuf_alloc(substream, params_buffer_bytes(hw_params)); -} - -static int snd_via8233_hw_free(snd_pcm_substream_t * substream) -{ - return 0; -} - -static int snd_via8233_playback_prepare(snd_pcm_substream_t * substream) -{ - via8233_t *chip = snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; - - snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); - if (chip->playback.reg_offset != VIA_REG_MULTPLAY_STATUS) { - unsigned int tmp; - /* I don't understand this stuff but its from the documentation and this way it works */ - outb(0 , VIAREG(chip, PLAYBACK_VOLUME_L)); - outb(0 , VIAREG(chip, PLAYBACK_VOLUME_R)); - tmp = inl(VIAREG(chip, PLAYBACK_STOP_IDX)) & ~0xfffff; - outl(tmp | (0xffff * runtime->rate)/(48000/16), VIAREG(chip, PLAYBACK_STOP_IDX)); - } - return snd_via8233_setup_periods(chip, &chip->playback, substream); -} - -static int snd_via8233_capture_prepare(snd_pcm_substream_t * substream) -{ - via8233_t *chip = snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; - - snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate); - outb(VIA_REG_CAPTURE_CHANNEL_LINE, VIAREG(chip, CAPTURE_CHANNEL)); - outb(VIA_REG_CAPTURE_FIFO_ENABLE, VIAREG(chip, CAPTURE_FIFO)); - return snd_via8233_setup_periods(chip, &chip->capture, substream); -} - -static inline unsigned int snd_via8233_cur_ptr(via8233_t *chip, viadev_t *viadev) -{ - unsigned int val, count; - - count = inl(VIAREG(chip, OFFSET_CURR_COUNT) + viadev->reg_offset) & 0xffffff; - /* The via686a does not have this current index register, - * this register makes life easier for us here. */ - val = inb(VIAREG(chip, OFFSET_CURR_INDEX) + viadev->reg_offset) % viadev->tbl_entries; - if (val < viadev->tbl_entries - 1) { - val *= viadev->tbl_size; - val += viadev->tbl_size - count; - } else { - val *= viadev->tbl_size; - val += (viadev->size % viadev->tbl_size) + 1 - count; - } - // printk("pointer: ptr = 0x%x, count = 0x%x, val = 0x%x\n", ptr, count, val); - return val; -} - -static snd_pcm_uframes_t snd_via8233_playback_pointer(snd_pcm_substream_t * substream) -{ - via8233_t *chip = snd_pcm_substream_chip(substream); - return bytes_to_frames(substream->runtime, snd_via8233_cur_ptr(chip, &chip->playback)); -} - -static snd_pcm_uframes_t snd_via8233_capture_pointer(snd_pcm_substream_t * substream) -{ - via8233_t *chip = snd_pcm_substream_chip(substream); - return bytes_to_frames(substream->runtime, snd_via8233_cur_ptr(chip, &chip->capture)); -} - -static snd_pcm_hardware_t snd_via8233_playback = -{ - .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE), - .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, - .rates = 0, - .rate_min = 8000, - .rate_max = 48000, - .channels_min = 1, - .channels_max = 6, - .buffer_bytes_max = 128 * 1024, - .period_bytes_min = 32, - .period_bytes_max = 128 * 1024, - .periods_min = 2, - .periods_max = 128, - .fifo_size = 0, -}; - -static snd_pcm_hardware_t snd_via8233_capture = -{ - .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID), - .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, - .rates = 0, - .rate_min = 8000, - .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 = 128, - .fifo_size = 0, -}; - -static unsigned int channels[] = { - 1, 2, 4, 6 -}; - -#define CHANNELS sizeof(channels) / sizeof(channels[0]) - -static snd_pcm_hw_constraint_list_t hw_constraints_channels = { - .count = CHANNELS, - .list = channels, - .mask = 0, -}; - -static int snd_via8233_playback_open(snd_pcm_substream_t * substream) -{ - via8233_t *chip = snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; - int err; - - chip->playback.substream = substream; - runtime->hw = snd_via8233_playback; - runtime->hw.rates = chip->ac97->rates[AC97_RATES_FRONT_DAC]; - if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) - runtime->hw.rate_min = 48000; - if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0) - return err; - if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0) - return err; - 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_CHANNELS, &hw_constraints_channels); - return 0; -} - -static int snd_via8233_capture_open(snd_pcm_substream_t * substream) -{ - via8233_t *chip = snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; - int err; - - chip->capture.substream = substream; - runtime->hw = snd_via8233_capture; - runtime->hw.rates = chip->ac97->rates[AC97_RATES_ADC]; - if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) - runtime->hw.rate_min = 48000; - if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0) - return err; - if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0) - return err; - if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) - return err; - return 0; -} - -static int snd_via8233_playback_close(snd_pcm_substream_t * substream) -{ - via8233_t *chip = snd_pcm_substream_chip(substream); - - snd_via8233_channel_reset(chip, &chip->playback); - clean_via_table(&chip->playback, substream, chip->pci); - snd_pcm_sgbuf_delete(substream); - chip->playback.substream = NULL; - return 0; -} - -static int snd_via8233_capture_close(snd_pcm_substream_t * substream) -{ - via8233_t *chip = snd_pcm_substream_chip(substream); - - snd_via8233_channel_reset(chip, &chip->capture); - clean_via_table(&chip->capture, substream, chip->pci); - snd_pcm_sgbuf_delete(substream); - chip->capture.substream = NULL; - return 0; -} - -static snd_pcm_ops_t snd_via8233_playback_ops = { - .open = snd_via8233_playback_open, - .close = snd_via8233_playback_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_via8233_hw_params, - .hw_free = snd_via8233_hw_free, - .prepare = snd_via8233_playback_prepare, - .trigger = snd_via8233_playback_trigger, - .pointer = snd_via8233_playback_pointer, - .copy = snd_pcm_sgbuf_ops_copy_playback, - .silence = snd_pcm_sgbuf_ops_silence, - .page = snd_pcm_sgbuf_ops_page, -}; - -static snd_pcm_ops_t snd_via8233_capture_ops = { - .open = snd_via8233_capture_open, - .close = snd_via8233_capture_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_via8233_hw_params, - .hw_free = snd_via8233_hw_free, - .prepare = snd_via8233_capture_prepare, - .trigger = snd_via8233_capture_trigger, - .pointer = snd_via8233_capture_pointer, - .copy = snd_pcm_sgbuf_ops_copy_capture, - .silence = snd_pcm_sgbuf_ops_silence, - .page = snd_pcm_sgbuf_ops_page, -}; - -static void snd_via8233_pcm_free(snd_pcm_t *pcm) -{ - via8233_t *chip = snd_magic_cast(via8233_t, pcm->private_data, return); - chip->pcm = NULL; -} - -static int __devinit snd_via8233_pcm(via8233_t *chip, int device, snd_pcm_t ** rpcm) -{ - snd_pcm_t *pcm; - int err; - - if (rpcm) - *rpcm = NULL; - err = snd_pcm_new(chip->card, "VIA 8233", device, 1, 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; - pcm->private_free = snd_via8233_pcm_free; - pcm->info_flags = 0; - strcpy(pcm->name, "VIA 8233"); - chip->pcm = pcm; - - if (rpcm) - *rpcm = NULL; - return 0; -} - -/* - * Mixer part - */ - -static void snd_via8233_mixer_free_ac97(ac97_t *ac97) -{ - via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return); - chip->ac97 = NULL; -} - -static int __devinit snd_via8233_mixer(via8233_t *chip) -{ - ac97_t ac97; - int err; - - memset(&ac97, 0, sizeof(ac97)); - ac97.write = snd_via8233_codec_write; - ac97.read = snd_via8233_codec_read; - ac97.private_data = chip; - ac97.private_free = snd_via8233_mixer_free_ac97; - ac97.clock = chip->ac97_clock; - if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0) - return err; - return 0; -} - -/* - * - */ - -static int __devinit snd_via8233_chip_init(via8233_t *chip) -{ - ac97_t ac97; - unsigned char stat; - int i; - - memset(&ac97, 0, sizeof(ac97)); - ac97.private_data = chip; - - /* deassert ACLink reset */ - pci_write_config_byte(chip->pci, 0x41, 0x40); - udelay(100); - /* deassert ACLink reset, force SYNC (warm AC'97 reset) */ - pci_write_config_byte(chip->pci, 0x41, 0x60); - udelay(2); - /* ACLink on, deassert ACLink reset, VSR, SGD data out */ - pci_write_config_byte(chip->pci, 0x41, 0xcc); - - /* Wait for codec ready to be accessed. */ - for (i=HZ; i--; ) { - pci_read_config_byte(chip->pci, 0x40, &stat); - if (stat & 1) - break; - if (!i) { - snd_printk("chip_init: failed to access primary codec.\n"); - return ~0; - } - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - } - snd_via8233_codec_ready(chip, 0); - snd_via8233_codec_write(&ac97, AC97_RESET, 0x0000); - snd_via8233_codec_read(&ac97, 0); - - /* disable interrupts */ - snd_via8233_channel_reset(chip, &chip->playback); - snd_via8233_channel_reset(chip, &chip->capture); - return 0; -} - -static int snd_via8233_free(via8233_t *chip) -{ - if (chip->irq < 0) - goto __end_hw; - /* disable interrupts */ - snd_via8233_channel_reset(chip, &chip->playback); - snd_via8233_channel_reset(chip, &chip->capture); - /* --- */ - synchronize_irq(chip->irq); - __end_hw: - if (chip->res_port) { - release_resource(chip->res_port); - kfree_nocheck(chip->res_port); - } - if (chip->irq >= 0) - free_irq(chip->irq, (void *)chip); - snd_magic_kfree(chip); - return 0; -} - -static int snd_via8233_dev_free(snd_device_t *device) -{ - via8233_t *chip = snd_magic_cast(via8233_t, device->device_data, return -ENXIO); - return snd_via8233_free(chip); -} - -static int __devinit snd_via8233_create(snd_card_t * card, - struct pci_dev *pci, - unsigned int ac97_clock, - via8233_t ** r_via) -{ - via8233_t *chip; - int err; - static snd_device_ops_t ops = { - .dev_free = snd_via8233_dev_free, - }; - - if ((err = pci_enable_device(pci)) < 0) - return err; - - if ((chip = snd_magic_kcalloc(via8233_t, 0, GFP_KERNEL)) == NULL) - return -ENOMEM; - - spin_lock_init(&chip->reg_lock); - chip->card = card; - chip->pci = pci; - chip->irq = -1; - chip->port = pci_resource_start(pci, 0); - if ((chip->res_port = request_region(chip->port, 256, "VIA8233")) == NULL) { - snd_via8233_free(chip); - snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->port, chip->port + 256 - 1); - return -EBUSY; - } - if (request_irq(pci->irq, snd_via8233_interrupt, SA_INTERRUPT|SA_SHIRQ, "VIA8233", (void *)chip)) { - snd_via8233_free(chip); - snd_printk("unable to grab IRQ %d\n", chip->irq); - return -EBUSY; - } - chip->irq = pci->irq; - if (ac97_clock >= 8000 && ac97_clock <= 48000) - chip->ac97_clock = ac97_clock; - pci_read_config_byte(pci, PCI_REVISION_ID, &chip->revision); - synchronize_irq(chip->irq); - - /* initialize offsets */ -#if 0 - chip->playback.reg_offset = VIA_REG_PLAYBACK_STATUS; /* this doesn't work on VIA8233A */ -#endif - /* we use multi-channel playback mode, since this mode is supported - * by all VIA8233 models (and obviously suitable for our purpose). - */ - chip->playback.reg_offset = VIA_REG_MULTPLAY_STATUS; - - chip->capture.reg_offset = VIA_REG_CAPTURE_STATUS; - - if ((err = snd_via8233_chip_init(chip)) < 0) { - snd_via8233_free(chip); - return err; - } - - if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { - snd_via8233_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); - - *r_via = chip; - return 0; -} - -static int __devinit snd_via8233_probe(struct pci_dev *pci, - const struct pci_device_id *id) -{ - static int dev; - snd_card_t *card; - via8233_t *chip; - int pcm_dev = 0; - int err; - - if (dev >= SNDRV_CARDS) - return -ENODEV; - if (!snd_enable[dev]) { - dev++; - return -ENOENT; - } - - card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); - if (card == NULL) - return -ENOMEM; - - if ((err = snd_via8233_create(card, - pci, - snd_ac97_clock[dev], - &chip)) < 0) { - snd_card_free(card); - return err; - } - - - if (snd_via8233_mixer(chip) < 0) { - snd_card_free(card); - return err; - } - if (snd_via8233_pcm(chip, pcm_dev++, NULL) < 0) { - snd_card_free(card); - return err; - } - - strcpy(card->driver, "VIA8233"); - strcpy(card->shortname, "VIA 8233"); - - sprintf(card->longname, "%s at 0x%lx, irq %d", - card->shortname, chip->port, chip->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_via8233_remove(struct pci_dev *pci) -{ - snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); -} - -static struct pci_driver driver = { - .name = "VIA 8233", - .id_table = snd_via8233_ids, - .probe = snd_via8233_probe, - .remove = __devexit_p(snd_via8233_remove), -}; - -static int __init alsa_card_via8233_init(void) -{ - int err; - - if ((err = pci_module_init(&driver)) < 0) { -#ifdef MODULE - printk(KERN_ERR "VIA 8233 soundcard not found or device busy\n"); -#endif - return err; - } - return 0; -} - -static void __exit alsa_card_via8233_exit(void) -{ - pci_unregister_driver(&driver); -} - -module_init(alsa_card_via8233_init) -module_exit(alsa_card_via8233_exit) - -#ifndef MODULE - -/* format is: snd-via8233=snd_enable,snd_index,snd_id,snd_ac97_clock */ - -static int __init alsa_card_via8233_setup(char *str) -{ - static unsigned __initdata nr_dev = 0; - - if (nr_dev >= SNDRV_CARDS) - return 0; - (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && - get_option(&str,&snd_index[nr_dev]) == 2 && - get_id(&str,&snd_id[nr_dev]) == 2 && - get_option(&str,&snd_ac97_clock[nr_dev]) == 2); - nr_dev++; - return 1; -} - -__setup("snd-via8233=", alsa_card_via8233_setup); - -#endif /* ifndef MODULE */ diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c new file mode 100644 index 000000000000..70163c108eff --- /dev/null +++ b/sound/pci/via82xx.c @@ -0,0 +1,1345 @@ +/* + * ALSA driver for VIA VT82xx (South Bridge) + * + * VT82C686A/B/C, VT8233A/C, VT8235 + * + * Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz> + * Tjeerd.Mulder <Tjeerd.Mulder@fujitsu-siemens.com> + * 2002 Takashi Iwai <tiwai@suse.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <sound/driver.h> +#include <asm/io.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_sgbuf.h> +#include <sound/pcm_params.h> +#include <sound/info.h> +#include <sound/ac97_codec.h> +#include <sound/mpu401.h> +#define SNDRV_GET_ID +#include <sound/initval.h> + +MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); +MODULE_DESCRIPTION("VIA VT82xx audio"); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{VIA,VT82C686A/B/C,pci},{VIA,VT8233A/B/C}}"); + +static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static long snd_mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1}; +static int snd_ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 48000}; + +MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_index, "Index value for VIA 82xx bridge."); +MODULE_PARM_SYNTAX(snd_index, SNDRV_INDEX_DESC); +MODULE_PARM(snd_id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(snd_id, "ID string for VIA 82xx bridge."); +MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); +MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_enable, "Enable audio part of VIA 82xx bridge."); +MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port."); +MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT_DESC); +MODULE_PARM(snd_ac97_clock, "1-" __MODULE_STRING(SNDRV_CARDS) "l"); +MODULE_PARM_DESC(snd_ac97_clock, "AC'97 codec clock (default 48000Hz)."); +MODULE_PARM_SYNTAX(snd_ac97_clock, SNDRV_ENABLED ",default:48000"); + + +/* pci ids */ +#ifndef PCI_DEVICE_ID_VIA_82C686_5 +#define PCI_DEVICE_ID_VIA_82C686_5 0x3058 +#endif +#ifndef PCI_DEVICE_ID_VIA_8233_5 +#define PCI_DEVICE_ID_VIA_8233_5 0x3059 +#endif + +/* 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 */ + +/* + * Direct registers + */ + +#define VIAREG(via, x) ((via)->port + VIA_REG_##x) + +/* common offsets */ +#define VIA_REG_OFFSET_STATUS 0x00 /* byte - channel status */ +#define VIA_REG_STAT_ACTIVE 0x80 /* 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 */ + +#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 */ + +/* multi-channel and capture registers for via8233 */ +DEFINE_VIA_REGSET(MULTPLAY, 0x20); +DEFINE_VIA_REGSET(CAPTURE_8233, 0x10); + +/* via8233-specific registers */ +#define VIA_REG_PLAYBACK_VOLUME_L 0x02 /* byte */ +#define VIA_REG_PLAYBACK_VOLUME_R 0x03 /* byte */ +#define VIA_REG_MULTPLAY_FORMAT 0x42 /* 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_CAPTURE_FIFO 0x62 /* byte - bit 6 = fifo enable */ +#define VIA_REG_CAPTURE_FIFO_ENABLE 0x40 +#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 + +/* + * pcm stream + */ + +typedef struct { + unsigned long reg_offset; + snd_pcm_substream_t *substream; + int running; + unsigned int size; + unsigned int fragsize; + unsigned int frags; + unsigned int lastptr; + unsigned int lastcount; + unsigned int page_per_frag; + unsigned int curidx; + unsigned int tbl_entries; /* number of descriptor table entries */ + unsigned int tbl_size; /* size of a table entry */ + u32 *table; /* physical address + flag */ + dma_addr_t table_addr; +} viadev_t; + + +static int build_via_table(viadev_t *dev, snd_pcm_substream_t *substream, + struct pci_dev *pci) +{ + int i, size; + struct snd_sg_buf *sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return -EINVAL); + + if (dev->table) { + snd_free_pci_pages(pci, PAGE_ALIGN(dev->tbl_entries * 8), dev->table, dev->table_addr); + dev->table = NULL; + } + + /* allocate buffer descriptor lists */ + if (dev->fragsize < PAGE_SIZE) { + dev->tbl_size = dev->fragsize; + dev->tbl_entries = dev->frags; + dev->page_per_frag = 1; + } else { + dev->tbl_size = PAGE_SIZE; + dev->tbl_entries = sgbuf->pages; + dev->page_per_frag = dev->fragsize >> PAGE_SHIFT; + } + /* the start of each lists must be aligned to 8 bytes, + * but the kernel pages are much bigger, so we don't care + */ + dev->table = (u32*)snd_malloc_pci_pages(pci, PAGE_ALIGN(dev->tbl_entries * 8), &dev->table_addr); + if (! dev->table) + return -ENOMEM; + + if (dev->tbl_size < PAGE_SIZE) { + for (i = 0; i < dev->tbl_entries; i++) + dev->table[i << 1] = cpu_to_le32((u32)sgbuf->table[0].addr + dev->fragsize * i); + } else { + for (i = 0; i < dev->tbl_entries; i++) + dev->table[i << 1] = cpu_to_le32((u32)sgbuf->table[i].addr); + } + size = dev->size; + for (i = 0; i < dev->tbl_entries - 1; i++) { + dev->table[(i << 1) + 1] = cpu_to_le32(VIA_TBL_BIT_FLAG | dev->tbl_size); + size -= dev->tbl_size; + } + dev->table[(dev->tbl_entries << 1) - 1] = cpu_to_le32(VIA_TBL_BIT_EOL | size); + + return 0; +} + + +static void clean_via_table(viadev_t *dev, snd_pcm_substream_t *substream, + struct pci_dev *pci) +{ + if (dev->table) { + snd_free_pci_pages(pci, PAGE_ALIGN(dev->tbl_entries * 8), dev->table, dev->table_addr); + dev->table = NULL; + } +} + + +/* + */ + +enum { TYPE_VIA686, TYPE_VIA8233 }; + +typedef struct _snd_via82xx via82xx_t; +#define chip_t via82xx_t + +struct _snd_via82xx { + int irq; + + unsigned long port; + struct resource *res_port; + int chip_type; + unsigned char revision; + + unsigned char old_legacy; + unsigned char old_legacy_cfg; + + struct pci_dev *pci; + snd_card_t *card; + + snd_pcm_t *pcm; + viadev_t playback; + viadev_t capture; + + snd_rawmidi_t *rmidi; + + ac97_t *ac97; + unsigned int ac97_clock; + unsigned int ac97_secondary; /* secondary AC'97 codec is present */ + + spinlock_t reg_lock; + spinlock_t ac97_lock; + snd_info_entry_t *proc_entry; +}; + +static struct pci_device_id snd_via82xx_ids[] __devinitdata = { + { 0x1106, 0x3058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 686A */ + { 0x1106, 0x3059, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* VT8233 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_via82xx_ids); + +/* + * Basic I/O + */ + +static inline unsigned int snd_via82xx_codec_xread(via82xx_t *chip) +{ + return inl(VIAREG(chip, AC97)); +} + +static inline void snd_via82xx_codec_xwrite(via82xx_t *chip, unsigned int val) +{ + outl(val, VIAREG(chip, AC97)); +} + +static int snd_via82xx_codec_ready(via82xx_t *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("codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_via82xx_codec_xread(chip)); + return -EIO; +} + +static int snd_via82xx_codec_valid(via82xx_t *chip, int secondary) +{ + unsigned int timeout = 1000; /* 1ms */ + unsigned int val; + unsigned int stat = !secondary ? VIA_REG_AC97_PRIMARY_VALID : + VIA_REG_AC97_SECONDARY_VALID; + + while (timeout-- > 0) { + udelay(1); + if ((val = snd_via82xx_codec_xread(chip)) & stat) + return val & 0xffff; + } + snd_printk("codec_valid: codec %i is not valid [0x%x]\n", secondary, snd_via82xx_codec_xread(chip)); + return -EIO; +} + +static void snd_via82xx_codec_wait(ac97_t *ac97) +{ + via82xx_t *chip = snd_magic_cast(via82xx_t, ac97->private_data, return); + int err; + err = snd_via82xx_codec_ready(chip, ac97->num); + /* here we need to wait fairly for long time.. */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/2); +} + +static void snd_via82xx_codec_write(ac97_t *ac97, + unsigned short reg, + unsigned short val) +{ + via82xx_t *chip = snd_magic_cast(via82xx_t, ac97->private_data, return); + 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; + spin_lock(&chip->ac97_lock); + snd_via82xx_codec_xwrite(chip, xval); + snd_via82xx_codec_ready(chip, ac97->num); + spin_unlock(&chip->ac97_lock); +} + +static unsigned short snd_via82xx_codec_read(ac97_t *ac97, unsigned short reg) +{ + via82xx_t *chip = snd_magic_cast(via82xx_t, ac97->private_data, return ~0); + 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; + spin_lock(&chip->ac97_lock); + while (1) { + if (again++ > 3) { + spin_unlock(&chip->ac97_lock); + return 0xffff; + } + snd_via82xx_codec_xwrite(chip, xval); + if (snd_via82xx_codec_ready(chip, ac97->num) < 0) + continue; + if (snd_via82xx_codec_valid(chip, ac97->num) >= 0) { + udelay(25); + val = snd_via82xx_codec_xread(chip); + break; + } + } + spin_unlock(&chip->ac97_lock); + return val & 0xffff; +} + +static void snd_via82xx_channel_reset(via82xx_t *chip, viadev_t *viadev) +{ + unsigned long port = chip->port + viadev->reg_offset; + + outb(VIA_REG_CTRL_PAUSE | VIA_REG_CTRL_TERMINATE | VIA_REG_CTRL_RESET, port + VIA_REG_OFFSET_CONTROL); + udelay(50); + /* disable interrupts */ + outb(0x00, port + VIA_REG_OFFSET_CONTROL); + /* clear interrupts */ + outb(0x03, port + VIA_REG_OFFSET_STATUS); + outb(0x00, port + VIA_REG_OFFSET_TYPE); /* for via686 */ + outl(0, port + VIA_REG_OFFSET_CURR_PTR); +} + +static int snd_via82xx_trigger(via82xx_t *chip, viadev_t *viadev, int cmd) +{ + unsigned char val; + unsigned long port = chip->port + viadev->reg_offset; + + if (chip->chip_type == TYPE_VIA8233) + val = VIA_REG_CTRL_INT; + else + val = 0; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + val |= VIA_REG_CTRL_START; + viadev->running = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + 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, port + VIA_REG_OFFSET_CONTROL); + if (cmd == SNDRV_PCM_TRIGGER_STOP) + snd_via82xx_channel_reset(chip, viadev); + return 0; +} + + +static int snd_via82xx_setup_periods(via82xx_t *chip, viadev_t *viadev, + snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long port = chip->port + viadev->reg_offset; + int v, err; + + viadev->size = snd_pcm_lib_buffer_bytes(substream); + viadev->fragsize = snd_pcm_lib_period_bytes(substream); + viadev->frags = runtime->periods; + viadev->lastptr = ~0; + viadev->lastcount = ~0; + viadev->curidx = 0; + + /* the period size must be in power of 2 */ + v = ld2(viadev->fragsize); + if (viadev->fragsize != (1 << v)) { + snd_printd(KERN_ERR "invalid fragment size %d\n", viadev->fragsize); + return -EINVAL; + } + + snd_via82xx_channel_reset(chip, viadev); + + err = build_via_table(viadev, substream, chip->pci); + if (err < 0) + return err; + + runtime->dma_bytes = viadev->size; + outl((u32)viadev->table_addr, port + VIA_REG_OFFSET_TABLE_PTR); + switch (chip->chip_type) { + case TYPE_VIA686: + 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, port + VIA_REG_OFFSET_TYPE); + break; + case TYPE_VIA8233: + if (viadev->reg_offset == VIA_REG_MULTPLAY_STATUS) { + unsigned int slots; + int fmt = (runtime->format == SNDRV_PCM_FORMAT_S16_LE) ? VIA_REG_MULTPLAY_FMT_16BIT : VIA_REG_MULTPLAY_FMT_8BIT; + fmt |= runtime->channels << 4; + outb(fmt, port + VIA_REG_OFFSET_TYPE); + /* set sample number to slot 3, 4, 7, 8, 6, 9 */ + switch (runtime->channels) { + case 1: slots = (1<<0) | (1<<4); break; + case 2: slots = (1<<0) | (2<<4); break; + case 4: slots = (1<<0) | (2<<4) | (3<<8) | (4<<12); break; + case 6: slots = (1<<0) | (2<<4) | (5<<8) | (6<<12) | (3<<16) | (4<<20); break; + default: slots = 0; break; + } + /* STOP index is never reached */ + outl(0xff000000 | slots, port + VIA_REG_OFFSET_STOP_IDX); + } else { + 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 */ + port + VIA_REG_OFFSET_STOP_IDX); + } + break; + } + return 0; +} + +/* + * Interrupt handler + */ + +static inline void snd_via82xx_update(via82xx_t *chip, viadev_t *viadev) +{ + outb(VIA_REG_STAT_FLAG | VIA_REG_STAT_EOL, VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset); + if (viadev->substream && viadev->running) { + viadev->curidx++; + if (viadev->curidx >= viadev->page_per_frag) { + viadev->curidx = 0; + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(viadev->substream); + spin_lock(&chip->reg_lock); + } + } +} + +static void snd_via82xx_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + via82xx_t *chip = snd_magic_cast(via82xx_t, dev_id, return); + unsigned int status; + + spin_lock(&chip->reg_lock); + if (chip->chip_type == TYPE_VIA686) { + /* check mpu401 interrupt */ + status = inl(VIAREG(chip, SGD_SHADOW)); + if ((status & 0x00000077) == 0) { + spin_unlock(&chip->reg_lock); + if (chip->rmidi != NULL) + snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); + return; + } + } + /* check status for each stream */ + if (inb(chip->port + chip->playback.reg_offset) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG)) + snd_via82xx_update(chip, &chip->playback); + if (inb(chip->port + chip->capture.reg_offset) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG)) + snd_via82xx_update(chip, &chip->capture); + spin_unlock(&chip->reg_lock); +} + +/* + * PCM part + */ + +static int snd_via82xx_playback_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + + return snd_via82xx_trigger(chip, &chip->playback, cmd); +} + +static int snd_via82xx_capture_trigger(snd_pcm_substream_t * substream, + int cmd) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + + return snd_via82xx_trigger(chip, &chip->capture, cmd); +} + +static int snd_via82xx_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_sgbuf_alloc(substream, params_buffer_bytes(hw_params)); +} + +static int snd_via82xx_hw_free(snd_pcm_substream_t * substream) +{ + return 0; +} + +static int snd_via82xx_playback_prepare(snd_pcm_substream_t * substream) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); + if (chip->chip_type == TYPE_VIA8233 && + chip->playback.reg_offset != VIA_REG_MULTPLAY_STATUS) { + unsigned int tmp; + /* I don't understand this stuff but its from the documentation and this way it works */ + outb(0 , VIAREG(chip, PLAYBACK_VOLUME_L)); + outb(0 , VIAREG(chip, PLAYBACK_VOLUME_R)); + tmp = inl(VIAREG(chip, PLAYBACK_STOP_IDX)) & ~0xfffff; + outl(tmp | (0xffff * runtime->rate)/(48000/16), VIAREG(chip, PLAYBACK_STOP_IDX)); + } + return snd_via82xx_setup_periods(chip, &chip->playback, substream); +} + +static int snd_via82xx_capture_prepare(snd_pcm_substream_t * substream) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate); + if (chip->chip_type == TYPE_VIA8233) { + outb(VIA_REG_CAPTURE_CHANNEL_LINE, VIAREG(chip, CAPTURE_CHANNEL)); + outb(VIA_REG_CAPTURE_FIFO_ENABLE, VIAREG(chip, CAPTURE_FIFO)); + } + return snd_via82xx_setup_periods(chip, &chip->capture, substream); +} + +static inline unsigned int snd_via82xx_cur_ptr(via82xx_t *chip, viadev_t *viadev) +{ + unsigned int val, ptr, count; + + ptr = inl(VIAREG(chip, OFFSET_CURR_PTR) + viadev->reg_offset)/* & 0xffffff*/; + switch (chip->chip_type) { + case TYPE_VIA686: + count = inl(VIAREG(chip, OFFSET_CURR_COUNT) + viadev->reg_offset); + if (ptr == viadev->lastptr && count > viadev->lastcount) + ptr += 8; + if (!(inb(VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset) & VIA_REG_STAT_ACTIVE)) + return 0; + snd_assert(viadev->tbl_entries, return 0); + /* get index */ + if (ptr <= (unsigned int)viadev->table_addr) + val = 0; + else + val = ((ptr - (unsigned int)viadev->table_addr) / 8 - 1) % viadev->tbl_entries; + viadev->lastptr = ptr; + viadev->lastcount = count; + break; + + case TYPE_VIA8233: + default: + count = inl(VIAREG(chip, OFFSET_CURR_COUNT) + viadev->reg_offset) & 0xffffff; + /* The via686a does not have this current index register, + * this register makes life easier for us here. */ + val = inb(VIAREG(chip, OFFSET_CURR_INDEX) + viadev->reg_offset) % viadev->tbl_entries; + break; + } + + /* convert to the linear position */ + if (val < viadev->tbl_entries - 1) { + val *= viadev->tbl_size; + val += viadev->tbl_size - count; + } else { + val *= viadev->tbl_size; + val += (viadev->size % viadev->tbl_size) + 1 - count; + } + // printk("pointer: ptr = 0x%x (%i), count = 0x%x, val = 0x%x\n", ptr, count, val); + return val; +} + +static snd_pcm_uframes_t snd_via82xx_playback_pointer(snd_pcm_substream_t * substream) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + return bytes_to_frames(substream->runtime, snd_via82xx_cur_ptr(chip, &chip->playback)); +} + +static snd_pcm_uframes_t snd_via82xx_capture_pointer(snd_pcm_substream_t * substream) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + return bytes_to_frames(substream->runtime, snd_via82xx_cur_ptr(chip, &chip->capture)); +} + +static snd_pcm_hardware_t snd_via82xx_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = 0, + .rate_min = 8000, + .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 = 128, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_via82xx_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = 0, + .rate_min = 8000, + .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 = 128, + .fifo_size = 0, +}; + +static unsigned int channels[] = { + 1, 2, 4, 6 +}; + +#define CHANNELS sizeof(channels) / sizeof(channels[0]) + +static snd_pcm_hw_constraint_list_t hw_constraints_channels = { + .count = CHANNELS, + .list = channels, + .mask = 0, +}; + +static int snd_via82xx_playback_open(snd_pcm_substream_t * substream) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + chip->playback.substream = substream; + runtime->hw = snd_via82xx_playback; + runtime->hw.rates = chip->ac97->rates[AC97_RATES_FRONT_DAC]; + if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) + runtime->hw.rate_min = 48000; + if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0) + return err; + if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0) + return err; + /* we may remove following constaint when we modify table entries + in interrupt */ +#if 0 + /* applying the following constraint together with the power-of-2 rule + * above may result in too narrow space. + * this one is not strictly necessary, so let's disable it. + */ + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; +#endif + if (chip->chip_type == TYPE_VIA8233) { + runtime->hw.channels_max = 6; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels); + } + return 0; +} + +static int snd_via82xx_capture_open(snd_pcm_substream_t * substream) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + chip->capture.substream = substream; + runtime->hw = snd_via82xx_capture; + runtime->hw.rates = chip->ac97->rates[AC97_RATES_ADC]; + if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000)) + runtime->hw.rate_min = 48000; + if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0) + return err; + if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0) + return err; +#if 0 + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; +#endif + return 0; +} + +static int snd_via82xx_playback_close(snd_pcm_substream_t * substream) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + + clean_via_table(&chip->playback, substream, chip->pci); + snd_pcm_sgbuf_delete(substream); + chip->playback.substream = NULL; + return 0; +} + +static int snd_via82xx_capture_close(snd_pcm_substream_t * substream) +{ + via82xx_t *chip = snd_pcm_substream_chip(substream); + + clean_via_table(&chip->capture, substream, chip->pci); + snd_pcm_sgbuf_delete(substream); + chip->capture.substream = NULL; + return 0; +} + +static snd_pcm_ops_t snd_via82xx_playback_ops = { + .open = snd_via82xx_playback_open, + .close = snd_via82xx_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_via82xx_hw_params, + .hw_free = snd_via82xx_hw_free, + .prepare = snd_via82xx_playback_prepare, + .trigger = snd_via82xx_playback_trigger, + .pointer = snd_via82xx_playback_pointer, + .copy = snd_pcm_sgbuf_ops_copy_playback, + .silence = snd_pcm_sgbuf_ops_silence, + .page = snd_pcm_sgbuf_ops_page, +}; + +static snd_pcm_ops_t snd_via82xx_capture_ops = { + .open = snd_via82xx_capture_open, + .close = snd_via82xx_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_via82xx_hw_params, + .hw_free = snd_via82xx_hw_free, + .prepare = snd_via82xx_capture_prepare, + .trigger = snd_via82xx_capture_trigger, + .pointer = snd_via82xx_capture_pointer, + .copy = snd_pcm_sgbuf_ops_copy_capture, + .silence = snd_pcm_sgbuf_ops_silence, + .page = snd_pcm_sgbuf_ops_page, +}; + +static void snd_via82xx_pcm_free(snd_pcm_t *pcm) +{ + via82xx_t *chip = snd_magic_cast(via82xx_t, pcm->private_data, return); + chip->pcm = NULL; +} + +static int __devinit snd_via82xx_pcm(via82xx_t *chip, int device, snd_pcm_t ** rpcm) +{ + snd_pcm_t *pcm; + int err; + + if (rpcm) + *rpcm = NULL; + err = snd_pcm_new(chip->card, chip->card->shortname, device, 1, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via82xx_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via82xx_capture_ops); + + pcm->private_data = chip; + pcm->private_free = snd_via82xx_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, chip->card->shortname); + chip->pcm = pcm; + + if (rpcm) + *rpcm = NULL; + return 0; +} + + +/* + * Mixer part + */ + +static void snd_via82xx_mixer_free_ac97(ac97_t *ac97) +{ + via82xx_t *chip = snd_magic_cast(via82xx_t, ac97->private_data, return); + chip->ac97 = NULL; +} + +static int __devinit snd_via82xx_mixer(via82xx_t *chip) +{ + ac97_t ac97; + int err; + + memset(&ac97, 0, sizeof(ac97)); + ac97.write = snd_via82xx_codec_write; + ac97.read = snd_via82xx_codec_read; + ac97.wait = snd_via82xx_codec_wait; + ac97.private_data = chip; + ac97.private_free = snd_via82xx_mixer_free_ac97; + ac97.clock = chip->ac97_clock; + if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0) + return err; + return 0; +} + +/* + * joystick + */ + +static int snd_via82xx_joystick_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *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_via82xx_joystick_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + via82xx_t *chip = snd_kcontrol_chip(kcontrol); + u16 val; + + pci_read_config_word(chip->pci, 0x42, &val); + ucontrol->value.integer.value[0] = (val & 0x08) ? 1 : 0; + return 0; +} + +static int snd_via82xx_joystick_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + via82xx_t *chip = snd_kcontrol_chip(kcontrol); + u16 val, oval; + + pci_read_config_word(chip->pci, 0x42, &oval); + val = oval & ~0x08; + if (ucontrol->value.integer.value[0]) + val |= 0x08; + if (val != oval) { + pci_write_config_word(chip->pci, 0x42, val); + return 1; + } + return 0; +} + +static snd_kcontrol_new_t snd_via82xx_joystick_control __devinitdata = { + .name = "Joystick", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = snd_via82xx_joystick_info, + .get = snd_via82xx_joystick_get, + .put = snd_via82xx_joystick_put, +}; + +/* + * + */ + +static int __devinit snd_via82xx_chip_init(via82xx_t *chip) +{ + ac97_t ac97; + unsigned int val; + int max_count; + unsigned char pval; + + memset(&ac97, 0, sizeof(ac97)); + ac97.private_data = chip; + +#if 0 /* broken on K7M? */ + if (chip->chip_type == TYPE_VIA686) + /* disable all legacy ports */ + pci_write_config_byte(chip->pci, 0x42, 0); +#endif + + /* deassert ACLink reset, force SYNC */ + pci_write_config_byte(chip->pci, 0x41, 0xe0); + udelay(100); + /* deassert ACLink reset, force SYNC (warm AC'97 reset) */ + pci_write_config_byte(chip->pci, 0x41, 0x60); + udelay(2); + /* pci_write_config_byte(chip->pci, 0x41, 0x00); + udelay(100); + */ + /* 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, 0x41, 0xcc); + udelay(100); + + /* Make sure VRA is enabled, in case we didn't do a + * complete codec reset, above */ + pci_read_config_byte(chip->pci, 0x41, &pval); + if ((pval & 0xcc) != 0xcc) { + /* 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, 0x41, 0xcc); + udelay(100); + } + + /* wait until codec ready */ + max_count = ((3 * HZ) / 4) + 1; + do { + pci_read_config_byte(chip->pci, 0x40, &pval); + if (pval & 0x01) /* primary codec ready */ + break; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (--max_count > 0); + + if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY) + snd_printk("AC'97 codec is not ready [0x%x]\n", val); + + /* and then reset codec.. */ + snd_via82xx_codec_ready(chip, 0); + snd_via82xx_codec_write(&ac97, AC97_RESET, 0x0000); + snd_via82xx_codec_read(&ac97, 0); + +#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)); + max_count = ((3 * HZ) / 4) + 1; + 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; + } + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + } while (--max_count > 0); + /* 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, 0x48, 0); + /* disable all GPI interrupts */ + outl(0, chip->port + 0x8c); + } + + /* disable interrupts */ + snd_via82xx_channel_reset(chip, &chip->playback); + snd_via82xx_channel_reset(chip, &chip->capture); + return 0; +} + +static int snd_via82xx_free(via82xx_t *chip) +{ + if (chip->irq < 0) + goto __end_hw; + /* disable interrupts */ + snd_via82xx_channel_reset(chip, &chip->playback); + snd_via82xx_channel_reset(chip, &chip->capture); + /* --- */ + synchronize_irq(chip->irq); + __end_hw: + if (chip->res_port) { + release_resource(chip->res_port); + kfree_nocheck(chip->res_port); + } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + if (chip->chip_type == TYPE_VIA686) { + pci_write_config_byte(chip->pci, 0x42, chip->old_legacy); + pci_write_config_byte(chip->pci, 0x43, chip->old_legacy_cfg); + } + snd_magic_kfree(chip); + return 0; +} + +static int snd_via82xx_dev_free(snd_device_t *device) +{ + via82xx_t *chip = snd_magic_cast(via82xx_t, device->device_data, return -ENXIO); + return snd_via82xx_free(chip); +} + +static int __devinit snd_via82xx_create(snd_card_t * card, + struct pci_dev *pci, + int chip_type, + unsigned int ac97_clock, + via82xx_t ** r_via) +{ + via82xx_t *chip; + int err; + static snd_device_ops_t ops = { + .dev_free = snd_via82xx_dev_free, + }; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + if ((chip = snd_magic_kcalloc(via82xx_t, 0, GFP_KERNEL)) == NULL) + return -ENOMEM; + + chip->chip_type = chip_type; + + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->ac97_lock); + chip->card = card; + chip->pci = pci; + chip->irq = -1; + + pci_read_config_byte(pci, 0x42, &chip->old_legacy); + pci_read_config_byte(pci, 0x43, &chip->old_legacy_cfg); + + chip->port = pci_resource_start(pci, 0); + if ((chip->res_port = request_region(chip->port, 256, card->driver)) == NULL) { + snd_via82xx_free(chip); + snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->port, chip->port + 256 - 1); + return -EBUSY; + } + if (request_irq(pci->irq, snd_via82xx_interrupt, SA_INTERRUPT|SA_SHIRQ, + card->driver, (void *)chip)) { + snd_via82xx_free(chip); + snd_printk("unable to grab IRQ %d\n", chip->irq); + return -EBUSY; + } + chip->irq = pci->irq; + if (ac97_clock >= 8000 && ac97_clock <= 48000) + chip->ac97_clock = ac97_clock; + pci_read_config_byte(pci, PCI_REVISION_ID, &chip->revision); + synchronize_irq(chip->irq); + + /* initialize offsets */ + switch (chip->chip_type) { + case TYPE_VIA686: + chip->playback.reg_offset = VIA_REG_PLAYBACK_STATUS; + chip->capture.reg_offset = VIA_REG_CAPTURE_STATUS; + break; + case TYPE_VIA8233: + /* we use multi-channel playback mode, since this mode is supported + * by all VIA8233 models (and obviously suitable for our purpose). + */ + chip->playback.reg_offset = VIA_REG_MULTPLAY_STATUS; + chip->capture.reg_offset = VIA_REG_CAPTURE_8233_STATUS; + break; + } + + 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); + + *r_via = chip; + return 0; +} + +static int __devinit snd_via82xx_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + static int dev; + snd_card_t *card; + via82xx_t *chip; + int pcm_dev = 0; + int chip_type; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!snd_enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + chip_type = id->driver_data; + switch (chip_type) { + case TYPE_VIA686: + strcpy(card->driver, "VIA686A"); + strcpy(card->shortname, "VIA 82C686A/B"); + break; + case TYPE_VIA8233: + strcpy(card->driver, "VIA8233"); + strcpy(card->shortname, "VIA 8233A/C"); + break; + default: + snd_printk(KERN_ERR "invalid chip type %d\n", chip_type); + snd_card_free(card); + return -EINVAL; + } + + if ((err = snd_via82xx_create(card, pci, chip_type, snd_ac97_clock[dev], &chip)) < 0) { + snd_card_free(card); + return err; + } + + + if (snd_via82xx_mixer(chip) < 0) { + snd_card_free(card); + return err; + } + if (snd_via82xx_pcm(chip, pcm_dev++, NULL) < 0) { + snd_card_free(card); + return err; + } +#if 0 + if (snd_via82xx_pcm_fm(chip, pcm_dev++, NULL) < 0) { + snd_card_free(card); + return err; + } +#endif + + if (chip->chip_type == TYPE_VIA686) { + unsigned char legacy, legacy_cfg; + int rev_h = 0; + legacy = chip->old_legacy; + legacy_cfg = chip->old_legacy_cfg; + legacy |= 0x40; /* disable MIDI */ + legacy &= ~0x08; /* disable joystick */ + if (chip->revision >= 0x20) { + if (check_region(pci_resource_start(pci, 2), 4)) { + rev_h = 0; + legacy &= ~0x80; /* disable PCI I/O 2 */ + } else { + rev_h = 1; + legacy |= 0x80; /* enable PCI I/O 2 */ + } + } + pci_write_config_byte(pci, 0x42, legacy); + pci_write_config_byte(pci, 0x43, legacy_cfg); + if (rev_h && snd_mpu_port[dev] >= 0x200) { /* force MIDI */ + legacy |= 0x02; /* enable MPU */ + pci_write_config_dword(pci, 0x18, (snd_mpu_port[dev] & 0xfffc) | 0x01); + } else { + if (rev_h && (legacy & 0x02)) { + snd_mpu_port[dev] = pci_resource_start(pci, 2); + if (snd_mpu_port[dev] < 0x200) /* bad value */ + legacy &= ~0x02; /* disable MIDI */ + } else { + switch (snd_mpu_port[dev]) { /* force MIDI */ + case 0x300: + case 0x310: + case 0x320: + case 0x330: + legacy_cfg &= ~(3 << 2); + legacy_cfg |= (snd_mpu_port[dev] & 0x0030) >> 2; + legacy |= 0x02; + break; + default: /* no, use BIOS settings */ + if (legacy & 0x02) + snd_mpu_port[dev] = 0x300 + ((legacy_cfg & 0x000c) << 2); + } + } + } + pci_write_config_byte(pci, 0x42, legacy); + pci_write_config_byte(pci, 0x43, legacy_cfg); + if (legacy & 0x02) { + if (check_region(snd_mpu_port[dev], 2)) { + printk(KERN_WARNING "unable to get MPU-401 port at 0x%lx, skipping\n", snd_mpu_port[dev]); + legacy &= ~0x02; + pci_write_config_byte(pci, 0x42, legacy); + goto __skip_mpu; + } + if (snd_mpu401_uart_new(card, 0, MPU401_HW_VIA686A, + snd_mpu_port[dev], 0, + pci->irq, 0, + &chip->rmidi) < 0) { + printk(KERN_WARNING "unable to initialize MPU-401 at 0x%lx, skipping\n", snd_mpu_port[dev]); + legacy &= ~0x02; + pci_write_config_byte(pci, 0x42, legacy); + goto __skip_mpu; + } + legacy &= ~0x40; /* enable MIDI interrupt */ + pci_write_config_byte(pci, 0x42, legacy); + __skip_mpu: + ; + } + + /* card switches */ + err = snd_ctl_add(card, snd_ctl_new1(&snd_via82xx_joystick_control, chip)); + if (err < 0) { + snd_card_free(card); + return err; + } + } + + sprintf(card->longname, "%s at 0x%lx, irq %d", + card->shortname, chip->port, chip->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_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), +}; + +static int __init alsa_card_via82xx_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + printk(KERN_ERR "VIA 82xx soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_via82xx_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_via82xx_init) +module_exit(alsa_card_via82xx_exit) + +#ifndef MODULE + +/* format is: snd-via82xx=snd_enable,snd_index,snd_id, + snd_mpu_port,snd_ac97_clock */ + +static int __init alsa_card_via82xx_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&snd_enable[nr_dev]) == 2 && + get_option(&str,&snd_index[nr_dev]) == 2 && + get_id(&str,&snd_id[nr_dev]) == 2 && + get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 && + get_option(&str,&snd_ac97_clock[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-via82xx=", alsa_card_via82xx_setup); + +#endif /* ifndef MODULE */ + diff --git a/sound/ppc/keywest.c b/sound/ppc/keywest.c index 782191b51a7f..b9f3e96741ce 100644 --- a/sound/ppc/keywest.c +++ b/sound/ppc/keywest.c @@ -74,11 +74,14 @@ static int keywest_attach_adapter(struct i2c_adapter *adapter) new_client->id = keywest_ctx->id++; /* Automatically unique */ keywest_ctx->client = new_client; - if ((err = keywest_ctx->init_client(keywest_ctx)) < 0) + if ((err = keywest_ctx->init_client(keywest_ctx)) < 0) { + snd_printk(KERN_ERR "tumbler: cannot initialize the MCS\n"); goto __err; + } /* Tell the i2c layer a new client has arrived */ if (i2c_attach_client(new_client)) { + snd_printk(KERN_ERR "tumbler: cannot attach i2c client\n"); err = -ENODEV; goto __err; } diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index 2adf58920862..190984c2e287 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -1170,8 +1170,7 @@ static int __init snd_pmac_detect(pmac_t *chip) // chip->can_byte_swap = 0; /* FIXME: check this */ chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ } - if (device_is_compatible(sound, "tumbler") || - device_is_compatible(sound, "snapper")) { + if (device_is_compatible(sound, "tumbler")) { chip->model = PMAC_TUMBLER; chip->can_capture = 0; /* no capture */ chip->can_duplex = 0; @@ -1180,6 +1179,15 @@ static int __init snd_pmac_detect(pmac_t *chip) chip->freq_table = tumbler_freqs; chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ } + if (device_is_compatible(sound, "snapper")) { + chip->model = PMAC_SNAPPER; + chip->can_capture = 0; /* no capture */ + chip->can_duplex = 0; + // chip->can_byte_swap = 0; /* FIXME: check this */ + chip->num_freqs = 2; + chip->freq_table = tumbler_freqs; + chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ + } prop = (unsigned int *)get_property(sound, "device-id", 0); if (prop) chip->device_id = *prop; diff --git a/sound/ppc/pmac.h b/sound/ppc/pmac.h index a0960636a71c..bb812a979804 100644 --- a/sound/ppc/pmac.h +++ b/sound/ppc/pmac.h @@ -40,6 +40,7 @@ #endif #endif #include <linux/nvram.h> +#include <linux/tty.h> #include <linux/vt_kern.h> #include <asm/dbdma.h> #include <asm/prom.h> @@ -115,7 +116,7 @@ struct snd_pmac_beep { */ enum snd_pmac_model { - PMAC_AWACS, PMAC_SCREAMER, PMAC_BURGUNDY, PMAC_DACA, PMAC_TUMBLER + PMAC_AWACS, PMAC_SCREAMER, PMAC_BURGUNDY, PMAC_DACA, PMAC_TUMBLER, PMAC_SNAPPER }; struct snd_pmac { diff --git a/sound/ppc/powermac.c b/sound/ppc/powermac.c index 46727219547a..6132cde87ece 100644 --- a/sound/ppc/powermac.c +++ b/sound/ppc/powermac.c @@ -94,8 +94,10 @@ static int __init snd_pmac_probe(void) goto __error; break; case PMAC_TUMBLER: - strcpy(card->driver, "PMac Tumbler"); - strcpy(card->shortname, "PowerMac Tumbler"); + case PMAC_SNAPPER: + name_ext = chip->model == PMAC_TUMBLER ? "Tumbler" : "Snapper"; + sprintf(card->driver, "PMac %s", name_ext); + sprintf(card->shortname, "PowerMac %s", name_ext); sprintf(card->longname, "%s (Dev %d) Sub-frame %d", card->shortname, chip->device_id, chip->subframe); if ((err = snd_pmac_tumbler_init(chip)) < 0) diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c index bf54d8d894d5..43d8da2ec672 100644 --- a/sound/ppc/tumbler.c +++ b/sound/ppc/tumbler.c @@ -1,5 +1,5 @@ /* - * PMac Tumbler lowlevel functions + * PMac Tumbler/Snapper lowlevel functions * * Copyright (c) by Takashi Iwai <tiwai@suse.de> * @@ -46,17 +46,27 @@ #define TAS_REG_VOL 0x04 #define TAS_REG_TREBLE 0x05 #define TAS_REG_BASS 0x06 -#define TAS_REG_INPUT1 0x07 /* pcm */ -#define TAS_REG_INPUT2 0x08 /* ??? */ +#define TAS_REG_INPUT1 0x07 +#define TAS_REG_INPUT2 0x08 +/* tas3001c */ #define TAS_REG_PCM TAS_REG_INPUT1 + +/* tas3004 */ +#define TAS_REG_LMIX TAS_REG_INPUT1 +#define TAS_REG_RMIX TAS_REG_INPUT2 -#define TAS_MIXER_VOL_MAX 200 +/* mono volumes for tas3001c/tas3004 */ +enum { + VOL_IDX_PCM_MONO, /* tas3001c only */ + VOL_IDX_BASS, VOL_IDX_TREBLE, + VOL_IDX_LAST_MONO +}; +/* stereo volumes for tas3004 */ enum { - VOL_IDX_PCM, VOL_IDX_BASS, VOL_IDX_TREBLE, - //VOL_IDX_ALTPCM, - VOL_IDX_LAST + VOL_IDX_PCM, VOL_IDX_PCM2, VOL_IDX_ADC, + VOL_IDX_LAST_MIX }; typedef struct pmac_gpio { @@ -77,7 +87,8 @@ typedef struct pmac_tumber_t { int headphone_irq; unsigned int master_vol[2]; unsigned int master_switch[2]; - unsigned int mono_vol[VOL_IDX_LAST]; + unsigned int mono_vol[VOL_IDX_LAST_MONO]; + unsigned int mix_vol[VOL_IDX_LAST_MIX][2]; /* stereo volumes for tas3004 */ int drc_range; int drc_enable; } pmac_tumbler_t; @@ -90,9 +101,16 @@ typedef struct pmac_tumber_t { static int tumbler_init_client(pmac_keywest_t *i2c) { - /* normal operation, SCLK=64fps, i2s output, i2s input, 16bit width */ - return snd_pmac_keywest_write_byte(i2c, TAS_REG_MCS, - (1<<6)+(2<<4)+(2<<2)+0); + int err, count = 10; + do { + /* normal operation, SCLK=64fps, i2s output, i2s input, 16bit width */ + err = snd_pmac_keywest_write_byte(i2c, TAS_REG_MCS, + (1<<6)+(2<<4)+(2<<2)+0); + if (err >= 0) + return err; + mdelay(10); + } while (count--); + return -ENXIO; } @@ -245,8 +263,11 @@ static int tumbler_put_master_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_valu /* - * dynamic range compression + * TAS3001c dynamic range compression */ + +#define TAS3001_DRC_MAX 0x5f + static int tumbler_set_drc(pmac_tumbler_t *mix) { unsigned char val[2]; @@ -256,7 +277,7 @@ static int tumbler_set_drc(pmac_tumbler_t *mix) if (mix->drc_enable) { val[0] = 0xc1; /* enable, 3:1 compression */ - if (mix->drc_range > 0x5f) + if (mix->drc_range > TAS3001_DRC_MAX) val[1] = 0xf0; else if (mix->drc_range < 0) val[1] = 0x91; @@ -274,12 +295,49 @@ static int tumbler_set_drc(pmac_tumbler_t *mix) return 0; } +/* + * TAS3004 + */ + +#define TAS3004_DRC_MAX 0xef + +static int snapper_set_drc(pmac_tumbler_t *mix) +{ + unsigned char val[6]; + + if (! mix->i2c.client) + return -ENODEV; + + if (mix->drc_enable) + val[0] = 0x50; /* 3:1 above threshold */ + else + val[0] = 0x51; /* disabled */ + val[1] = 0x02; /* 1:1 below threshold */ + if (mix->drc_range > 0xef) + val[2] = 0xef; + else if (mix->drc_range < 0) + val[2] = 0x00; + else + val[2] = mix->drc_range; + val[3] = 0xb0; + val[4] = 0x60; + val[5] = 0xa0; + + if (snd_pmac_keywest_write(&mix->i2c, TAS_REG_DRC, 6, val) < 0) { + snd_printk("failed to set DRC\n"); + return -EINVAL; + } + return 0; +} + static int tumbler_info_drc_value(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) { + pmac_t *chip = snd_kcontrol_chip(kcontrol); uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 0; - uinfo->value.integer.max = 0x5f; + uinfo->value.integer.max = + chip->model == PMAC_TUMBLER ? TAS3001_DRC_MAX : TAS3004_DRC_MAX; return 0; } @@ -304,7 +362,10 @@ static int tumbler_put_drc_value(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t change = mix->drc_range != ucontrol->value.integer.value[0]; if (change) { mix->drc_range = ucontrol->value.integer.value[0]; - tumbler_set_drc(mix); + if (chip->model == PMAC_TUMBLER) + tumbler_set_drc(mix); + else + snapper_set_drc(mix); } return change; } @@ -330,7 +391,10 @@ static int tumbler_put_drc_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t change = mix->drc_enable != ucontrol->value.integer.value[0]; if (change) { mix->drc_enable = !!ucontrol->value.integer.value[0]; - tumbler_set_drc(mix); + if (chip->model == PMAC_TUMBLER) + tumbler_set_drc(mix); + else + snapper_set_drc(mix); } return change; } @@ -409,24 +473,15 @@ static int tumbler_put_mono(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucon return change; } +/* TAS3001c mono volumes */ static struct tumbler_mono_vol tumbler_pcm_vol_info = { - .index = VOL_IDX_PCM, + .index = VOL_IDX_PCM_MONO, .reg = TAS_REG_PCM, .bytes = 3, .max = number_of(mixer_volume_table), .table = mixer_volume_table, }; -#if 0 // for what? -static struct tumbler_mono_vol tumbler_altpcm_vol_info = { - .index = VOL_IDX_ALTPCM, - .reg = TAS_REG_INPUT2, - .bytes = 3, - .max = number_of(mixer_volume_table), - .table = mixer_volume_table, -}; -#endif - static struct tumbler_mono_vol tumbler_bass_vol_info = { .index = VOL_IDX_BASS, .reg = TAS_REG_BASS, @@ -443,6 +498,24 @@ static struct tumbler_mono_vol tumbler_treble_vol_info = { .table = treble_volume_table, }; +/* TAS3004 mono volumes */ +static struct tumbler_mono_vol snapper_bass_vol_info = { + .index = VOL_IDX_BASS, + .reg = TAS_REG_BASS, + .bytes = 1, + .max = number_of(snapper_bass_volume_table), + .table = snapper_bass_volume_table, +}; + +static struct tumbler_mono_vol snapper_treble_vol_info = { + .index = VOL_IDX_TREBLE, + .reg = TAS_REG_TREBLE, + .bytes = 1, + .max = number_of(snapper_treble_volume_table), + .table = snapper_treble_volume_table, +}; + + #define DEFINE_MONO(xname,type) { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,\ .name = xname, \ @@ -452,6 +525,95 @@ static struct tumbler_mono_vol tumbler_treble_vol_info = { .private_value = (unsigned long)(&tumbler_##type##_vol_info), \ } +#define DEFINE_SNAPPER_MONO(xname,type) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER,\ + .name = xname, \ + .info = tumbler_info_mono, \ + .get = tumbler_get_mono, \ + .put = tumbler_put_mono, \ + .private_value = (unsigned long)(&snapper_##type##_vol_info), \ +} + + +/* + * snapper mixer volumes + */ + +static int snapper_set_mix_vol1(pmac_tumbler_t *mix, int idx, int ch, int reg) +{ + int i, j, vol; + unsigned char block[9]; + + vol = mix->mix_vol[idx][ch]; + if (vol >= number_of(mixer_volume_table)) { + vol = number_of(mixer_volume_table) - 1; + mix->mix_vol[idx][ch] = vol; + } + + for (i = 0; i < 3; i++) { + vol = mix->mix_vol[i][ch]; + vol = mixer_volume_table[vol]; + for (j = 0; j < 3; j++) + block[i * 3 + j] = (vol >> ((2 - j) * 8)) & 0xff; + } + if (snd_pmac_keywest_write(&mix->i2c, reg, 9, block) < 0) { + snd_printk("failed to set mono volume %d\n", reg); + return -EINVAL; + } + return 0; +} + +static int snapper_set_mix_vol(pmac_tumbler_t *mix, int idx) +{ + if (! mix->i2c.client) + return -ENODEV; + if (snapper_set_mix_vol1(mix, idx, 0, TAS_REG_LMIX) < 0 || + snapper_set_mix_vol1(mix, idx, 1, TAS_REG_RMIX) < 0) + return -EINVAL; + return 0; +} + +static int snapper_info_mix(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = number_of(mixer_volume_table) - 1; + return 0; +} + +static int snapper_get_mix(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + int idx = (int)kcontrol->private_value; + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix; + if (! (mix = chip->mixer_data)) + return -ENODEV; + ucontrol->value.integer.value[0] = mix->mix_vol[idx][0]; + ucontrol->value.integer.value[1] = mix->mix_vol[idx][1]; + return 0; +} + +static int snapper_put_mix(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + int idx = (int)kcontrol->private_value; + pmac_t *chip = snd_kcontrol_chip(kcontrol); + pmac_tumbler_t *mix; + int change; + + if (! (mix = chip->mixer_data)) + return -ENODEV; + change = mix->mix_vol[idx][0] != ucontrol->value.integer.value[0] || + mix->mix_vol[idx][1] != ucontrol->value.integer.value[1]; + if (change) { + mix->mix_vol[idx][0] = ucontrol->value.integer.value[0]; + mix->mix_vol[idx][1] = ucontrol->value.integer.value[1]; + snapper_set_mix_vol(mix, idx); + } + return change; +} + + /* * mute switches */ @@ -487,6 +649,16 @@ static int tumbler_put_mute_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_ return 0; } +#define DEFINE_SNAPPER_MIX(xname,idx,ofs) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER,\ + .name = xname, \ + .info = snapper_info_mix, \ + .get = snapper_get_mix, \ + .put = snapper_put_mix, \ + .index = idx,\ + .private_value = ofs, \ +} + /* */ @@ -506,7 +678,38 @@ static snd_kcontrol_new_t tumbler_mixers[] __initdata = { DEFINE_MONO("Tone Control - Bass", bass), DEFINE_MONO("Tone Control - Treble", treble), DEFINE_MONO("PCM Playback Volume", pcm), - // DEFINE_MONO("Mixer2 Playback Volume", altpcm), + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DRC Switch", + .info = snd_pmac_boolean_mono_info, + .get = tumbler_get_drc_switch, + .put = tumbler_put_drc_switch + }, + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DRC Range", + .info = tumbler_info_drc_value, + .get = tumbler_get_drc_value, + .put = tumbler_put_drc_value + }, +}; + +static snd_kcontrol_new_t snapper_mixers[] __initdata = { + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Volume", + .info = tumbler_info_master_volume, + .get = tumbler_get_master_volume, + .put = tumbler_put_master_volume + }, + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = snd_pmac_boolean_stereo_info, + .get = tumbler_get_master_switch, + .put = tumbler_put_master_switch + }, + DEFINE_SNAPPER_MIX("PCM Playback Volume", 0, VOL_IDX_PCM), + DEFINE_SNAPPER_MIX("PCM Playback Volume", 1, VOL_IDX_PCM2), + DEFINE_SNAPPER_MIX("Monitor Mix Volume", 0, VOL_IDX_ADC), + DEFINE_SNAPPER_MONO("Tone Control - Bass", bass), + DEFINE_SNAPPER_MONO("Tone Control - Treble", treble), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "DRC Switch", .info = snd_pmac_boolean_mono_info, @@ -675,10 +878,17 @@ static void tumbler_resume(pmac_t *chip) tumbler_reset_audio(chip); if (mix->i2c.client) tumbler_init_client(&mix->i2c); - tumbler_set_mono_volume(mix, &tumbler_pcm_vol_info); - tumbler_set_mono_volume(mix, &tumbler_bass_vol_info); - tumbler_set_mono_volume(mix, &tumbler_treble_vol_info); - // tumbler_set_mono_volume(mix, &tumbler_altpcm_vol_info); + if (chip->model == PMAC_TUMBLER) { + tumbler_set_mono_volume(mix, &tumbler_pcm_vol_info); + tumbler_set_mono_volume(mix, &tumbler_bass_vol_info); + tumbler_set_mono_volume(mix, &tumbler_treble_vol_info); + } else { + snapper_set_mix_vol(mix, VOL_IDX_PCM); + snapper_set_mix_vol(mix, VOL_IDX_PCM2); + snapper_set_mix_vol(mix, VOL_IDX_ADC); + tumbler_set_mono_volume(mix, &tumbler_bass_vol_info); + tumbler_set_mono_volume(mix, &tumbler_treble_vol_info); + } tumbler_set_drc(mix); tumbler_set_master_volume(mix); if (chip->update_automute) @@ -741,6 +951,7 @@ int __init snd_pmac_tumbler_init(pmac_t *chip) pmac_tumbler_t *mix; u32 *paddr; struct device_node *tas_node; + char *chipname; #ifdef CONFIG_KMOD request_module("i2c-keywest"); @@ -770,18 +981,32 @@ int __init snd_pmac_tumbler_init(pmac_t *chip) mix->i2c.addr = TAS_I2C_ADDR; mix->i2c.init_client = tumbler_init_client; - mix->i2c.name = "TAS3001c"; + if (chip->model == PMAC_TUMBLER) { + mix->i2c.name = "TAS3001c"; + chipname = "Tumbler"; + } else { + mix->i2c.name = "TAS3004"; + chipname = "Snapper"; + } + if ((err = snd_pmac_keywest_init(&mix->i2c)) < 0) return err; /* * build mixers */ - strcpy(chip->card->mixername, "PowerMac Tumbler"); + sprintf(chip->card->mixername, "PowerMac %s", chipname); - for (i = 0; i < number_of(tumbler_mixers); i++) { - if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&tumbler_mixers[i], chip))) < 0) - return err; + if (chip->model == PMAC_TUMBLER) { + for (i = 0; i < number_of(tumbler_mixers); i++) { + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&tumbler_mixers[i], chip))) < 0) + return err; + } + } else { + for (i = 0; i < number_of(snapper_mixers); i++) { + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snapper_mixers[i], chip))) < 0) + return err; + } } chip->master_sw_ctl = snd_ctl_new1(&tumbler_hp_sw, chip); if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0) diff --git a/sound/ppc/tumbler_volume.h b/sound/ppc/tumbler_volume.h index a299a9edc07d..ef8d85d58b02 100644 --- a/sound/ppc/tumbler_volume.h +++ b/sound/ppc/tumbler_volume.h @@ -63,7 +63,7 @@ static unsigned int master_volume_table[] = { 0x00071457, 0x00077fbb, 0x0007f17b, }; -/* treble table */ +/* treble table for TAS3001c */ /* 0 = -18 dB, 72 = 18 dB in 0.5 dB step */ static unsigned int treble_volume_table[] = { 0x96, 0x95, 0x94, @@ -93,7 +93,7 @@ static unsigned int treble_volume_table[] = { 0x01, }; -/* bass table */ +/* bass table for TAS3001c */ /* 0 = -18 dB, 72 = 18 dB in 0.5 dB step */ static unsigned int bass_volume_table[] = { 0x86, 0x82, 0x7f, @@ -186,3 +186,65 @@ static unsigned int mixer_volume_table[] = { 0x5f4e52, 0x64f403, 0x6aef5d, 0x714575, 0x77fbaa, 0x7f17af, }; + + +/* treble table for TAS3004 */ +/* 0 = -18 dB, 72 = 18 dB in 0.5 dB step */ +static unsigned int snapper_treble_volume_table[] = { + 0x96, 0x95, 0x94, + 0x93, 0x92, 0x91, + 0x90, 0x8f, 0x8e, + 0x8d, 0x8c, 0x8b, + 0x8a, 0x89, 0x88, + 0x87, 0x86, 0x85, + 0x84, 0x83, 0x82, + 0x81, 0x80, 0x7f, + 0x7e, 0x7d, 0x7c, + 0x7b, 0x7a, 0x79, + 0x78, 0x77, 0x76, + 0x75, 0x74, 0x73, + 0x72, 0x71, 0x70, + 0x6f, 0x6d, 0x6c, + 0x6b, 0x69, 0x68, + 0x67, 0x65, 0x63, + 0x62, 0x60, 0x5d, + 0x5b, 0x59, 0x56, + 0x53, 0x51, 0x4d, + 0x4a, 0x47, 0x43, + 0x3f, 0x3b, 0x36, + 0x31, 0x2c, 0x26, + 0x20, 0x1a, 0x13, + 0x08, 0x04, 0x01, + 0x01, +}; + +/* bass table for TAS3004 */ +/* 0 = -18 dB, 72 = 18 dB in 0.5 dB step */ +static unsigned int snapper_bass_volume_table[] = { + 0x96, 0x95, 0x94, + 0x93, 0x92, 0x91, + 0x90, 0x8f, 0x8e, + 0x8d, 0x8c, 0x8b, + 0x8a, 0x89, 0x88, + 0x87, 0x86, 0x85, + 0x84, 0x83, 0x82, + 0x81, 0x80, 0x7f, + 0x7e, 0x7d, 0x7c, + 0x7b, 0x7a, 0x79, + 0x78, 0x77, 0x76, + 0x75, 0x74, 0x73, + 0x72, 0x71, 0x6f, + 0x6e, 0x6d, 0x6b, + 0x6a, 0x69, 0x67, + 0x66, 0x65, 0x63, + 0x62, 0x61, 0x5f, + 0x5d, 0x5b, 0x58, + 0x55, 0x52, 0x4f, + 0x4c, 0x49, 0x46, + 0x43, 0x3f, 0x3b, + 0x37, 0x33, 0x2e, + 0x29, 0x24, 0x1e, + 0x18, 0x11, 0x0a, + 0x01, +}; + diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index f71a2fd08a6d..d175637dafb8 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -53,6 +53,8 @@ MODULE_DEVICES("{{Generic,USB Audio}}"); static int snd_index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *snd_id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static int snd_enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int snd_vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Vendor ID for this card */ +static int snd_pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Product ID for this card */ MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); MODULE_PARM_DESC(snd_index, "Index value for the USB audio adapter."); @@ -63,6 +65,27 @@ MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC); MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); MODULE_PARM_DESC(snd_enable, "Enable USB audio adapter."); MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC); +MODULE_PARM(snd_vid, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_vid, "Vendor ID for the USB audio device."); +MODULE_PARM_SYNTAX(snd_vid, SNDRV_ENABLED ",allows:{{-1,0xffff}},base:16"); +MODULE_PARM(snd_pid, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(snd_pid, "Product ID for the USB audio device."); +MODULE_PARM_SYNTAX(snd_pid, SNDRV_ENABLED ",allows:{{-1,0xffff}},base:16"); + + +/* + * for using ASYNC unlink mode, define the following. + * this will make the driver quicker response for request to STOP-trigger, + * but it may cause oops by some unknown reason (bug of usb driver?), + * so turning off might be sure. + */ +/* #define SND_USE_ASYNC_UNLINK */ + +#ifdef SND_USB_ASYNC_UNLINK +#define UNLINK_FLAGS USB_ASYNC_UNLINK +#else +#define UNLINK_FLAGS 0 +#endif /* @@ -528,6 +551,10 @@ static int deactivate_urbs(snd_usb_substream_t *subs) subs->running = 0; +#ifndef SND_USB_ASYNC_UNLINK + if (in_interrupt()) + return 0; +#endif alive = 0; for (i = 0; i < subs->nurbs; i++) { if (test_bit(i, &subs->active_mask)) { @@ -545,7 +572,11 @@ static int deactivate_urbs(snd_usb_substream_t *subs) } } } +#ifdef SND_USB_ASYNC_UNLINK return alive; +#else + return 0; +#endif } @@ -803,7 +834,7 @@ static int init_substream_urbs(snd_usb_substream_t *subs, snd_pcm_runtime_t *run } u->urb->dev = subs->dev; u->urb->pipe = subs->datapipe; - u->urb->transfer_flags = USB_ISO_ASAP | USB_ASYNC_UNLINK; + u->urb->transfer_flags = USB_ISO_ASAP | UNLINK_FLAGS; u->urb->number_of_packets = u->packets; u->urb->context = u; u->urb->complete = snd_complete_urb; @@ -825,7 +856,7 @@ static int init_substream_urbs(snd_usb_substream_t *subs, snd_pcm_runtime_t *run u->urb->transfer_buffer_length = NRPACKS * 3; u->urb->dev = subs->dev; u->urb->pipe = subs->syncpipe; - u->urb->transfer_flags = USB_ISO_ASAP | USB_ASYNC_UNLINK; + u->urb->transfer_flags = USB_ISO_ASAP | UNLINK_FLAGS; u->urb->number_of_packets = u->packets; u->urb->context = u; u->urb->complete = snd_complete_sync_urb; @@ -2043,7 +2074,9 @@ static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum, * now look for an empty slot and create a new card instance */ for (i = 0; i < SNDRV_CARDS; i++) - if (snd_enable[i] && ! usb_chip[i]) { + if (snd_enable[i] && ! usb_chip[i] && + (snd_vid[i] == -1 || snd_vid[i] == dev->descriptor.idVendor) && + (snd_pid[i] == -1 || snd_pid[i] == dev->descriptor.idProduct)) { card = snd_card_new(snd_index[i], snd_id[i], THIS_MODULE, 0); if (card == NULL) { snd_printk(KERN_ERR "cannot create a card instance %d\n", i); diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c index 96b0d5f4ca0b..d527455593da 100644 --- a/sound/usb/usbmixer.c +++ b/sound/usb/usbmixer.c @@ -73,6 +73,7 @@ struct usb_mixer_elem_info { int channels; int val_type; int min, max; + unsigned int initialized: 1; }; @@ -498,6 +499,23 @@ static int mixer_ctl_feature_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t uinfo->value.integer.min = 0; uinfo->value.integer.max = 1; } else { + if (! cval->initialized) { + int minchn = 0; + if (cval->cmask) { + int i; + for (i = 0; i < MAX_CHANNELS; i++) + if (cval->cmask & (1 << i)) { + minchn = i + 1; + break; + } + } + if (get_ctl_value(cval, GET_MAX, ((cval->control+1) << 8) | minchn, &cval->max) < 0 || + get_ctl_value(cval, GET_MIN, ((cval->control+1) << 8) | minchn, &cval->min) < 0) { + snd_printk(KERN_ERR "%d:%d: cannot get min/max values for control %d\n", cval->id, cval->ctrlif, cval->control); + return -EINVAL; + } + cval->initialized = 1; + } uinfo->value.integer.min = 0; uinfo->value.integer.max = cval->max - cval->min; } @@ -515,8 +533,10 @@ static int mixer_ctl_feature_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t for (c = 0; c < MAX_CHANNELS; c++) { if (cval->cmask & (1 << c)) { err = get_cur_mix_value(cval, c + 1, &val); - if (err < 0) + if (err < 0) { + printk("cannot get current value for control %d ch %d: err = %d\n", cval->control, c + 1, err); return err; + } val = get_relative_value(cval, val); ucontrol->value.integer.value[cnt] = val; cnt++; @@ -525,8 +545,10 @@ static int mixer_ctl_feature_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t } else { /* master channel */ err = get_cur_mix_value(cval, 0, &val); - if (err < 0) + if (err < 0) { + printk("cannot get current value for control %d master ch: err = %d\n", cval->control, err); return err; + } val = get_relative_value(cval, val); ucontrol->value.integer.value[0] = val; } @@ -592,6 +614,7 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc, int nameid = desc[desc[0] - 1]; snd_kcontrol_t *kctl; usb_mixer_elem_info_t *cval; + int minchn = 0; if (control == USB_FEATURE_GEQ) { /* FIXME: not supported yet */ @@ -614,22 +637,25 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc, else { int i, c = 0; for (i = 0; i < 16; i++) - if (ctl_mask & (1 << i)) + if (ctl_mask & (1 << i)) { + if (! minchn) + minchn = i + 1; c++; + } cval->channels = c; } /* get min/max values */ if (cval->val_type == USB_MIXER_BOOLEAN || - cval->val_type == USB_MIXER_INV_BOOLEAN) + cval->val_type == USB_MIXER_INV_BOOLEAN) { cval->max = 1; - else { - if (get_ctl_value(cval, GET_MAX, ((cval->control+1) << 8) | (ctl_mask ? 1 : 0), &cval->max) < 0 || - get_ctl_value(cval, GET_MIN, ((cval->control+1) << 8) | (ctl_mask ? 1 : 0), &cval->min) < 0) { + cval->initialized = 1; + } else { + if (get_ctl_value(cval, GET_MAX, ((cval->control+1) << 8) | minchn, &cval->max) < 0 || + get_ctl_value(cval, GET_MIN, ((cval->control+1) << 8) | minchn, &cval->min) < 0) snd_printk(KERN_ERR "%d:%d: cannot get min/max values for control %d\n", cval->id, cval->ctrlif, control); - snd_magic_kfree(cval); - return; - } + else + cval->initialized = 1; } kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); @@ -759,6 +785,7 @@ static void build_mixer_unit_ctl(mixer_build_t *state, unsigned char *desc, int i, len; snd_kcontrol_t *kctl; usb_audio_term_t iterm; + int minchn = 0; cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL); if (! cval) @@ -776,16 +803,17 @@ static void build_mixer_unit_ctl(mixer_build_t *state, unsigned char *desc, if (check_matrix_bitmap(desc + 9 + num_ins, in_ch, i, num_outs)) { cval->cmask |= (1 << i); cval->channels++; + if (! minchn) + minchn = i + 1; } } /* get min/max values */ - if (get_ctl_value(cval, GET_MAX, ((in_ch+1) << 8) | 1, &cval->max) < 0 || - get_ctl_value(cval, GET_MIN, ((in_ch+1) << 8) | 1, &cval->min) < 0) { + if (get_ctl_value(cval, GET_MAX, ((in_ch+1) << 8) | minchn, &cval->max) < 0 || + get_ctl_value(cval, GET_MIN, ((in_ch+1) << 8) | minchn, &cval->min) < 0) snd_printk(KERN_ERR "cannot get min/max values for mixer\n"); - snd_magic_kfree(cval); - return; - } + else + cval->initialized = 1; kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); if (! kctl) { @@ -994,11 +1022,10 @@ static int build_audio_procunit(mixer_build_t *state, int unitid, unsigned char /* get min/max values */ if (get_ctl_value(cval, GET_MAX, cval->control, &cval->max) < 0 || - get_ctl_value(cval, GET_MIN, cval->control, &cval->min) < 0) { + get_ctl_value(cval, GET_MIN, cval->control, &cval->min) < 0) snd_printk(KERN_ERR "cannot get min/max values for proc/ext unit\n"); - snd_magic_kfree(cval); - continue; - } + else + cval->initialized = 1; kctl = snd_ctl_new1(&mixer_procunit_ctl, cval); if (! kctl) { @@ -1158,6 +1185,7 @@ static int parse_audio_selector_unit(mixer_build_t *state, int unitid, unsigned cval->channels = 1; cval->min = 1; cval->max = num_ins; + cval->initialized = 1; namelist = kmalloc(sizeof(char *) * num_ins, GFP_KERNEL); if (! namelist) { -- 2.30.9