Parent repository is http://linux-sound.bkbits.net/linux-sound ======== ChangeSet 1.1564 ======== D 1.1564 04/02/29 12:55:43-08:00 akpm@mnm.(none) 37851 37844 0/0/1 P ChangeSet C Merge http://linux-sound.bkbits.net/linux-sound C into mnm.(none):/usr/src/bk-alsa ------------------------------------------------ diff -Nru a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt --- a/Documentation/sound/alsa/ALSA-Configuration.txt Sun Feb 29 12:58:38 2004 +++ b/Documentation/sound/alsa/ALSA-Configuration.txt Sun Feb 29 12:58:38 2004 @@ -692,6 +692,15 @@ The power-management is supported. + Module snd-mixart + ----------------- + + Module for Digigram miXart8 soundcards. + + Module supports multiple cards. + Note: One miXart8 board will be represented as 4 alsa cards. + See MIXART.txt for details. + Module snd-mpu401 ----------------- @@ -1169,6 +1178,16 @@ Module supports autoprobe and multiple chips (max 8). The power-management is supported. + + Module snd-pdaudiocf + -------------------- + + Module for Sound Core PDAudioCF soundcard. + + irq_mask - IRQ mask (PCMCIA type) + irq_list - List of available interrupts for this soundcard + + Note: the driver is build only when CONFIG_ISA is set. Configuring Non-ISAPNP Cards diff -Nru a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl --- a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl Sun Feb 29 12:58:38 2004 +++ b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl Sun Feb 29 12:58:38 2004 @@ -3769,14 +3769,20 @@ value.enumerated.items = 4; - if (uinfo->value.enumerated.item > 3) - uinfo->value.enumerated.item = 3; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); + snd_ctl_elem_info_t *uinfo) + { + static char *texts[4] = { + "First", "Second", "Third", "Fourth" + }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item > 3) + uinfo->value.enumerated.item = 3; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; + } ]]> diff -Nru a/Documentation/sound/alsa/Joystick.txt b/Documentation/sound/alsa/Joystick.txt --- a/Documentation/sound/alsa/Joystick.txt Sun Feb 29 12:58:39 2004 +++ b/Documentation/sound/alsa/Joystick.txt Sun Feb 29 12:58:39 2004 @@ -43,7 +43,8 @@ ens1370 joystick 0 = disable (default), 1 = enable ens1371 joystick_port 0 = disable (default), 1 = auto-detect, manual: 0x200, 0x208, 0x210, 0x218 - cmipci joystick 0 = disable (default), 1 = enable + cmipci joystick_port 0 = disable (default), 1 = auto-detect, + manual: any address (e.g. 0x200) cs4281 N/A N/A cs46xx N/A N/A es1938 N/A N/A diff -Nru a/Documentation/sound/alsa/MIXART.txt b/Documentation/sound/alsa/MIXART.txt --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/sound/alsa/MIXART.txt Sun Feb 29 12:58:39 2004 @@ -0,0 +1,96 @@ + Alsa driver for Digigram miXart8 and miXart8AES/EBU soundcards + Digigram + + +GENERAL +======= + +The miXart8 is a multichannel audio processing and mixing soundcard +that has 4 stereo audio inputs and 4 stereo audio outputs. +The miXart8AES/EBU is the same with a add-on card that offers further +4 digital stereo audio inputs and outputs. +Furthermore the add-on card offers external clock synchronisation +(AES/EBU, Word Clock, Time Code and Video Synchro) + +The mainboard has a PowerPC that offers onboard mpeg encoding and +decoding, samplerate conversions and various effects. + +The driver don't work properly at all until the certain firmwares +are loaded, i.e. no PCM nor mixer devices will appear. +Use the mixartloader that can be found in the alsa-tools package. + + +VERSION 0.1.0 +============= + +One miXart8 board will be represented as 4 alsa cards, each with 1 +stereo analog capture 'pcm0c' and 1 stereo analog playback 'pcm0p' device. +With a miXart8AES/EBU there is in addition 1 stereo digital input +'pcm1c' and 1 stereo digital output 'pcm1p' per card. + +Formats +------- +U8, S16_LE, S16_BE, S24_3LE, S24_3BE, FLOAT_LE, FLOAT_BE +Sample rates : 8000 - 48000 Hz continously + +Playback +-------- +For instance the playback devices are configured to have max. 4 +substreams performing hardware mixing. This could be changed to a +maximum of 24 substreams if wished. +Mono files will be played on the left and right channel. Each channel +can be muted for each stream to use 8 analog/digital outputs seperately. + +Capture +------- +There is one substream per capture device. For instance only stereo +formats are supported. + +Mixer +----- + and : analog volume control of playback and capture PCM. + and : digital volume control of each analog substream. + and : digital volume control of each AES/EBU substream. + : Loopback from 'pcm0c' to 'pcm0p' with digital volume +and mute control. + +Rem : for best audio quality try to keep a 0 attenuation on the PCM +and AES volume controls which is set by 219 in the range from 0 to 255 +(about 86% with alsamixer) + + +NOT YET IMPLEMENTED +=================== + +- external clock support (AES/EBU, Word Clock, Time Code, Video Sync) +- MPEG audio formats +- mono record +- on-board effects and samplerate conversions +- linked streams + + +FIRMWARE +======== + +For loading the firmware automatically after the module is loaded, use +the post-install command. For example, add the following entry to +/etc/modprobe.conf for miXart driver: + + install snd-mixart /sbin/modprobe --first-time -i snd-mixart && \ + /usr/bin/mixartloader +(for 2.2/2.4 kernels, add "post-install snd-mixart /usr/bin/vxloader" to + /etc/modules.conf, instead.) + +The firmware binaries are installed on /usr/share/alsa/firmware +(or /usr/local/share/alsa/firmware, depending to the prefix option of +configure). There will be a miXart.conf file, which define the dsp image +files. + +The firmware files are copyright by Digigram SA + + +COPYRIGHT +========= + +Copyright (c) 2003 Digigram SA +Distributalbe under GPL. diff -Nru a/include/sound/ak4117.h b/include/sound/ak4117.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/include/sound/ak4117.h Sun Feb 29 12:58:39 2004 @@ -0,0 +1,191 @@ +#ifndef __SOUND_AK4117_H +#define __SOUND_AK4117_H + +/* + * Routines for Asahi Kasei AK4117 + * Copyright (c) by Jaroslav Kysela , + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define AK4117_REG_PWRDN 0x00 /* power down */ +#define AK4117_REG_CLOCK 0x01 /* clock control */ +#define AK4117_REG_IO 0x02 /* input/output control */ +#define AK4117_REG_INT0_MASK 0x03 /* interrupt0 mask */ +#define AK4117_REG_INT1_MASK 0x04 /* interrupt1 mask */ +#define AK4117_REG_RCS0 0x05 /* receiver status 0 */ +#define AK4117_REG_RCS1 0x06 /* receiver status 1 */ +#define AK4117_REG_RCS2 0x07 /* receiver status 2 */ +#define AK4117_REG_RXCSB0 0x08 /* RX channel status byte 0 */ +#define AK4117_REG_RXCSB1 0x09 /* RX channel status byte 1 */ +#define AK4117_REG_RXCSB2 0x0a /* RX channel status byte 2 */ +#define AK4117_REG_RXCSB3 0x0b /* RX channel status byte 3 */ +#define AK4117_REG_RXCSB4 0x0c /* RX channel status byte 4 */ +#define AK4117_REG_Pc0 0x0d /* burst preamble Pc byte 0 */ +#define AK4117_REG_Pc1 0x0e /* burst preamble Pc byte 1 */ +#define AK4117_REG_Pd0 0x0f /* burst preamble Pd byte 0 */ +#define AK4117_REG_Pd1 0x10 /* burst preamble Pd byte 1 */ +#define AK4117_REG_QSUB_ADDR 0x11 /* Q-subcode address + control */ +#define AK4117_REG_QSUB_TRACK 0x12 /* Q-subcode track */ +#define AK4117_REG_QSUB_INDEX 0x13 /* Q-subcode index */ +#define AK4117_REG_QSUB_MINUTE 0x14 /* Q-subcode minute */ +#define AK4117_REG_QSUB_SECOND 0x15 /* Q-subcode second */ +#define AK4117_REG_QSUB_FRAME 0x16 /* Q-subcode frame */ +#define AK4117_REG_QSUB_ZERO 0x17 /* Q-subcode zero */ +#define AK4117_REG_QSUB_ABSMIN 0x18 /* Q-subcode absolute minute */ +#define AK4117_REG_QSUB_ABSSEC 0x19 /* Q-subcode absolute second */ +#define AK4117_REG_QSUB_ABSFRM 0x1a /* Q-subcode absolute frame */ + +/* sizes */ +#define AK4117_REG_RXCSB_SIZE ((AK4117_REG_RXCSB4-AK4117_REG_RXCSB0)+1) +#define AK4117_REG_QSUB_SIZE ((AK4117_REG_QSUB_ABSFRM-AK4117_REG_QSUB_ADDR)+1) + +/* AK4117_REG_PWRDN bits */ +#define AK4117_EXCT (1<<4) /* 0 = X'tal mode, 1 = external clock mode */ +#define AK4117_XTL1 (1<<3) /* XTL1=0,XTL0=0 -> 11.2896Mhz; XTL1=0,XTL0=1 -> 12.288Mhz */ +#define AK4117_XTL0 (1<<2) /* XTL1=1,XTL0=0 -> 24.576Mhz; XTL1=1,XTL0=1 -> use channel status */ +#define AK4117_XTL_11_2896M (0) +#define AK4117_XTL_12_288M AK4117_XTL0 +#define AK4117_XTL_24_576M AK4117_XTL1 +#define AK4117_XTL_EXT (AK4117_XTL1|AK4117_XTL0) +#define AK4117_PWN (1<<1) /* 0 = power down, 1 = normal operation */ +#define AK4117_RST (1<<0) /* 0 = reset & initialize (except this register), 1 = normal operation */ + +/* AK4117_REQ_CLOCK bits */ +#define AK4117_LP (1<<7) /* 0 = normal mode, 1 = low power mode (Fs up to 48kHz only) */ +#define AK4117_PKCS1 (1<<6) /* master clock frequency at PLL mode (when LP == 0) */ +#define AK4117_PKCS0 (1<<5) +#define AK4117_PKCS_512fs (0) +#define AK4117_PKCS_256fs AK4117_PKCS0 +#define AK4117_PKCS_128fs AK4117_PKCS1 +#define AK4117_DIV (1<<4) /* 0 = MCKO == Fs, 1 = MCKO == Fs / 2; X'tal mode only */ +#define AK4117_XCKS1 (1<<3) /* master clock frequency at X'tal mode */ +#define AK4117_XCKS0 (1<<2) +#define AK4117_XCKS_128fs (0) +#define AK4117_XCKS_256fs AK4117_XCKS0 +#define AK4117_XCKS_512fs AK4117_XCKS1 +#define AK4117_XCKS_1024fs (AK4117_XCKS1|AK4117_XCKS0) +#define AK4117_CM1 (1<<1) /* MCKO operation mode select */ +#define AK4117_CM0 (1<<0) +#define AK4117_CM_PLL (0) /* use RX input as master clock */ +#define AK4117_CM_XTAL (AK4117_CM0) /* use X'tal as master clock */ +#define AK4117_CM_PLL_XTAL (AK4117_CM1) /* use Rx input but X'tal when PLL loses lock */ +#define AK4117_CM_MONITOR (AK4117_CM0|AK4117_CM1) /* use X'tal as master clock, but use PLL for monitoring */ + +/* AK4117_REG_IO */ +#define AK4117_IPS (1<<7) /* Input Recovery Data Select, 0 = RX0, 1 = RX1 */ +#define AK4117_UOUTE (1<<6) /* U-bit output enable to UOUT, 0 = disable, 1 = enable */ +#define AK4117_CS12 (1<<5) /* channel status select, 0 = channel1, 1 = channel2 */ +#define AK4117_EFH2 (1<<4) /* INT0 pin hold count select */ +#define AK4117_EFH1 (1<<3) +#define AK4117_EFH_512LRCLK (0) +#define AK4117_EFH_1024LRCLK (AK4117_EFH1) +#define AK4117_EFH_2048LRCLK (AK4117_EFH2) +#define AK4117_EFH_4096LRCLK (AK4117_EFH1|AK4117_EFH2) +#define AK4117_DIF2 (1<<2) /* audio data format control */ +#define AK4117_DIF1 (1<<1) +#define AK4117_DIF0 (1<<0) +#define AK4117_DIF_16R (0) /* STDO: 16-bit, right justified */ +#define AK4117_DIF_18R (AK4117_DIF0) /* STDO: 18-bit, right justified */ +#define AK4117_DIF_20R (AK4117_DIF1) /* STDO: 20-bit, right justified */ +#define AK4117_DIF_24R (AK4117_DIF1|AK4117_DIF0) /* STDO: 24-bit, right justified */ +#define AK4117_DIF_24L (AK4117_DIF2) /* STDO: 24-bit, left justified */ +#define AK4117_DIF_24I2S (AK4117_DIF2|AK4117_DIF0) /* STDO: I2S */ + +/* AK4117_REG_INT0_MASK & AK4117_INT1_MASK */ +#define AK4117_MULK (1<<7) /* mask enable for UNLOCK bit */ +#define AK4117_MPAR (1<<6) /* mask enable for PAR bit */ +#define AK4117_MAUTO (1<<5) /* mask enable for AUTO bit */ +#define AK4117_MV (1<<4) /* mask enable for V bit */ +#define AK4117_MAUD (1<<3) /* mask enable for AUDION bit */ +#define AK4117_MSTC (1<<2) /* mask enable for STC bit */ +#define AK4117_MCIT (1<<1) /* mask enable for CINT bit */ +#define AK4117_MQIT (1<<0) /* mask enable for QINT bit */ + +/* AK4117_REG_RCS0 */ +#define AK4117_UNLCK (1<<7) /* PLL lock status, 0 = lock, 1 = unlock */ +#define AK4117_PAR (1<<6) /* parity error or biphase error status, 0 = no error, 1 = error */ +#define AK4117_AUTO (1<<5) /* Non-PCM or DTS stream auto detection, 0 = no detect, 1 = detect */ +#define AK4117_V (1<<4) /* Validity bit, 0 = valid, 1 = invalid */ +#define AK4117_AUDION (1<<3) /* audio bit output, 0 = audio, 1 = non-audio */ +#define AK4117_STC (1<<2) /* sampling frequency or Pre-emphasis change, 0 = no detect, 1 = detect */ +#define AK4117_CINT (1<<1) /* channel status buffer interrupt, 0 = no change, 1 = change */ +#define AK4117_QINT (1<<0) /* Q-subcode buffer interrupt, 0 = no change, 1 = changed */ + +/* AK4117_REG_RCS1 */ +#define AK4117_DTSCD (1<<6) /* DTS-CD bit audio stream detect, 0 = no detect, 1 = detect */ +#define AK4117_NPCM (1<<5) /* Non-PCM bit stream detection, 0 = no detect, 1 = detect */ +#define AK4117_PEM (1<<4) /* Pre-emphasis detect, 0 = OFF, 1 = ON */ +#define AK4117_FS3 (1<<3) /* sampling frequency detection */ +#define AK4117_FS2 (1<<2) +#define AK4117_FS1 (1<<1) +#define AK4117_FS0 (1<<0) +#define AK4117_FS_44100HZ (0) +#define AK4117_FS_48000HZ (AK4117_FS1) +#define AK4117_FS_32000HZ (AK4117_FS1|AK4117_FS0) +#define AK4117_FS_88200HZ (AK4117_FS3) +#define AK4117_FS_96000HZ (AK4117_FS3|AK4117_FS1) +#define AK4117_FS_176400HZ (AK4117_FS3|AK4117_FS2) +#define AK4117_FS_192000HZ (AK4117_FS3|AK4117_FS2|AK4117_FS1) + +/* AK4117_REG_RCS2 */ +#define AK4117_CCRC (1<<1) /* CRC for channel status, 0 = no error, 1 = error */ +#define AK4117_QCRC (1<<0) /* CRC for Q-subcode, 0 = no error, 1 = error */ + +/* flags for snd_ak4117_check_rate_and_errors() */ +#define AK4117_CHECK_NO_STAT (1<<0) /* no statistics */ +#define AK4117_CHECK_NO_RATE (1<<1) /* no rate check */ + +#define AK4117_CONTROLS 13 + +typedef void (ak4117_write_t)(void *private_data, unsigned char addr, unsigned char data); +typedef unsigned char (ak4117_read_t)(void *private_data, unsigned char addr); + +typedef struct ak4117 ak4117_t; + +struct ak4117 { + snd_card_t * card; + ak4117_write_t * write; + ak4117_read_t * read; + void * private_data; + unsigned int init: 1; + spinlock_t lock; + unsigned char regmap[5]; + snd_kcontrol_t *kctls[AK4117_CONTROLS]; + snd_pcm_substream_t *substream; + unsigned long parity_errors; + unsigned long v_bit_errors; + unsigned long qcrc_errors; + unsigned long ccrc_errors; + unsigned char rcs0; + unsigned char rcs1; + unsigned char rcs2; + struct timer_list timer; /* statistic timer */ + void *change_callback_private; + void (*change_callback)(ak4117_t *ak4117, unsigned char c0, unsigned char c1); +}; + +int snd_ak4117_create(snd_card_t *card, ak4117_read_t *read, ak4117_write_t *write, + unsigned char pgm[5], void *private_data, ak4117_t **r_ak4117); +void snd_ak4117_reg_write(ak4117_t *chip, unsigned char reg, unsigned char mask, unsigned char val); +void snd_ak4117_reinit(ak4117_t *chip); +int snd_ak4117_build(ak4117_t *ak4117, snd_pcm_substream_t *capture_substream); +int snd_ak4117_external_rate(ak4117_t *ak4117); +int snd_ak4117_check_rate_and_errors(ak4117_t *ak4117, unsigned int flags); + +#endif /* __SOUND_AK4117_H */ + diff -Nru a/include/sound/asequencer.h b/include/sound/asequencer.h --- a/include/sound/asequencer.h Sun Feb 29 12:58:39 2004 +++ b/include/sound/asequencer.h Sun Feb 29 12:58:39 2004 @@ -594,6 +594,7 @@ #define SNDRV_SEQ_PORT_TYPE_MIDI_GS (1<<3) /* GS compatible device */ #define SNDRV_SEQ_PORT_TYPE_MIDI_XG (1<<4) /* XG compatible device */ #define SNDRV_SEQ_PORT_TYPE_MIDI_MT32 (1<<5) /* MT-32 compatible device */ +#define SNDRV_SEQ_PORT_TYPE_MIDI_GM2 (1<<6) /* General MIDI 2 compatible device */ /* other standards...*/ #define SNDRV_SEQ_PORT_TYPE_SYNTH (1<<10) /* Synth device (no MIDI compatible - direct wavetable) */ @@ -605,7 +606,7 @@ /* misc. conditioning flags */ #define SNDRV_SEQ_PORT_FLG_GIVEN_PORT (1<<0) #define SNDRV_SEQ_PORT_FLG_TIMESTAMP (1<<1) -#define SNDRV_SEQ_PORT_FLG_TIME_REAL (1<<1) +#define SNDRV_SEQ_PORT_FLG_TIME_REAL (1<<2) struct sndrv_seq_port_info { struct sndrv_seq_addr addr; /* client/port numbers */ diff -Nru a/include/sound/emu10k1.h b/include/sound/emu10k1.h --- a/include/sound/emu10k1.h Sun Feb 29 12:58:38 2004 +++ b/include/sound/emu10k1.h Sun Feb 29 12:58:38 2004 @@ -644,9 +644,13 @@ #define SOLEH 0x5d /* Stop on loop enable high register */ #define SPBYPASS 0x5e /* SPDIF BYPASS mode register */ -#define SPBYPASS_ENABLE 0x00000001 /* Enable SPDIF bypass mode */ +#define SPBYPASS_SPDIF0_MASK 0x00000003 /* SPDIF 0 bypass mode */ +#define SPBYPASS_SPDIF1_MASK 0x0000000c /* SPDIF 1 bypass mode */ +/* bypass mode: 0 - DSP; 1 - SPDIF A, 2 - SPDIF B, 3 - SPDIF C */ +#define SPBYPASS_FORMAT 0x00000f00 /* If 1, SPDIF XX uses 24 bit, if 0 - 20 bit */ #define AC97SLOT 0x5f /* additional AC97 slots enable bits */ +#define AC97SLOT_10K2 0x03 #define AC97SLOT_CNTR 0x10 /* Center enable */ #define AC97SLOT_LFE 0x20 /* LFE enable */ @@ -837,7 +841,7 @@ typedef struct snd_emu10k1_memblk { snd_util_memblk_t mem; /* private part */ - short first_page, last_page, pages, mapped_page; + int first_page, last_page, pages, mapped_page; unsigned int map_locked; struct list_head mapped_link; struct list_head mapped_order_link; diff -Nru a/include/sound/info.h b/include/sound/info.h --- a/include/sound/info.h Sun Feb 29 12:58:39 2004 +++ b/include/sound/info.h Sun Feb 29 12:58:39 2004 @@ -171,7 +171,7 @@ struct proc_dir_entry *de) { ; } #define snd_card_proc_new(card,name,entryp) 0 /* always success */ -#define snd_info_set_text_ops(entry,private_data,read) /*NOP*/ +#define snd_info_set_text_ops(entry,private_data,read_size,read) /*NOP*/ #endif diff -Nru a/include/sound/pcm.h b/include/sound/pcm.h --- a/include/sound/pcm.h Sun Feb 29 12:58:39 2004 +++ b/include/sound/pcm.h Sun Feb 29 12:58:39 2004 @@ -889,6 +889,9 @@ snd_pcm_sframes_t snd_pcm_lib_readv(snd_pcm_substream_t *substream, void **bufs, snd_pcm_uframes_t frames); +int snd_pcm_limit_hw_rates(snd_pcm_runtime_t *runtime); + + /* * Timer interface */ diff -Nru a/include/sound/pcm_oss.h b/include/sound/pcm_oss.h --- a/include/sound/pcm_oss.h Sun Feb 29 12:58:39 2004 +++ b/include/sound/pcm_oss.h Sun Feb 29 12:58:39 2004 @@ -50,6 +50,7 @@ unsigned int maxfrags; unsigned int subdivision; /* requested subdivision */ size_t period_bytes; /* requested period size */ + size_t period_frames; /* period frames for poll */ size_t period_ptr; /* actual write pointer to period */ unsigned int periods; size_t buffer_bytes; /* requested buffer size */ diff -Nru a/include/sound/sndmagic.h b/include/sound/sndmagic.h --- a/include/sound/sndmagic.h Sun Feb 29 12:58:38 2004 +++ b/include/sound/sndmagic.h Sun Feb 29 12:58:38 2004 @@ -200,6 +200,7 @@ #define azf3328_t_magic 0xa15a4200 #define snd_card_harmony_t_magic 0xa15a4300 #define bt87x_t_magic 0xa15a4400 +#define pdacf_t_magic 0xa15a4500 #else diff -Nru a/sound/core/Makefile b/sound/core/Makefile --- a/sound/core/Makefile Sun Feb 29 12:58:39 2004 +++ b/sound/core/Makefile Sun Feb 29 12:58:39 2004 @@ -107,5 +107,7 @@ obj-$(CONFIG_SND_VXP440) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-hwdep.o obj-$(CONFIG_SND_VX222) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-hwdep.o obj-$(CONFIG_SND_BT87X) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o +obj-$(CONFIG_SND_MIXART) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-hwdep.o +obj-$(CONFIG_SND_PDAUDIOCF) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o obj-m := $(sort $(obj-m)) diff -Nru a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c --- a/sound/core/oss/pcm_oss.c Sun Feb 29 12:58:39 2004 +++ b/sound/core/oss/pcm_oss.c Sun Feb 29 12:58:39 2004 @@ -133,6 +133,15 @@ return (runtime->oss.buffer_bytes * frames) / buffer_size; } +static long snd_pcm_alsa_frames(snd_pcm_substream_t *substream, long bytes) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t buffer_size = snd_pcm_lib_buffer_bytes(substream); + if (buffer_size == runtime->oss.buffer_bytes) + return bytes_to_frames(runtime, bytes); + return bytes_to_frames(runtime, (buffer_size * bytes) / runtime->oss.buffer_bytes); +} + static int snd_pcm_oss_format_from(int format) { switch (format) { @@ -254,6 +263,7 @@ snd_assert(oss_period_size >= 16, return -EINVAL); runtime->oss.period_bytes = oss_period_size; + runtime->oss.period_frames = 1; runtime->oss.periods = oss_periods; return 0; } @@ -511,6 +521,8 @@ if (runtime->dma_area) snd_pcm_format_set_silence(runtime->format, runtime->dma_area, bytes_to_samples(runtime, runtime->dma_bytes)); + runtime->oss.period_frames = snd_pcm_alsa_frames(substream, oss_period_size); + err = 0; failure: if (sw_params) @@ -2098,7 +2110,7 @@ if (atomic_read(&runtime->mmap_count)) return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt; else - return snd_pcm_playback_ready(substream); + return snd_pcm_playback_avail(runtime) >= runtime->oss.period_frames; } static int snd_pcm_oss_capture_ready(snd_pcm_substream_t *substream) @@ -2107,7 +2119,7 @@ if (atomic_read(&runtime->mmap_count)) return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt; else - return snd_pcm_capture_ready(substream); + return snd_pcm_capture_avail(runtime) >= runtime->oss.period_frames; } static unsigned int snd_pcm_oss_poll(struct file *file, poll_table * wait) diff -Nru a/sound/core/pcm.c b/sound/core/pcm.c --- a/sound/core/pcm.c Sun Feb 29 12:58:39 2004 +++ b/sound/core/pcm.c Sun Feb 29 12:58:39 2004 @@ -327,8 +327,9 @@ snd_iprintf(buffer, "OSS format: %s\n", snd_pcm_oss_format_name(runtime->oss.format)); snd_iprintf(buffer, "OSS channels: %u\n", runtime->oss.channels); snd_iprintf(buffer, "OSS rate: %u\n", runtime->oss.rate); - snd_iprintf(buffer, "OSS period bytes: %lu\n", (unsigned long)runtime->oss.period_bytes); + snd_iprintf(buffer, "OSS period bytes: %lu\n", (unsigned long)runtime->oss.period_bytes); snd_iprintf(buffer, "OSS periods: %u\n", runtime->oss.periods); + snd_iprintf(buffer, "OSS period frames: %lu\n", (unsigned long)runtime->oss.period_frames); } #endif snd_pcm_stream_unlock_irq(substream); @@ -1060,3 +1061,4 @@ EXPORT_SYMBOL(snd_pcm_format_silence_64); EXPORT_SYMBOL(snd_pcm_format_set_silence); EXPORT_SYMBOL(snd_pcm_build_linear_format); +EXPORT_SYMBOL(snd_pcm_limit_hw_rates); diff -Nru a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c --- a/sound/core/pcm_misc.c Sun Feb 29 12:58:39 2004 +++ b/sound/core/pcm_misc.c Sun Feb 29 12:58:39 2004 @@ -654,3 +654,35 @@ } return snd_int_to_enum(((int(*)[2][2])linear_formats)[width][!!unsignd][!!big_endian]); } + +/** + * snd_pcm_limit_hw_rates - determine rate_min/rate_max fields + * @runtime: the runtime instance + * + * Determines the rate_min and rate_max fields from the rates bits of + * the given runtime->hw. + * + * Returns zero if successful. + */ +int snd_pcm_limit_hw_rates(snd_pcm_runtime_t *runtime) +{ + static unsigned rates[] = { + /* ATTENTION: these values depend on the definition in pcm.h! */ + 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, + 64000, 88200, 96000, 176400, 192000 + }; + int i; + for (i = 0; i < (int)ARRAY_SIZE(rates); i++) { + if (runtime->hw.rates & (1 << i)) { + runtime->hw.rate_min = rates[i]; + break; + } + } + for (i = (int)ARRAY_SIZE(rates) - 1; i >= 0; i--) { + if (runtime->hw.rates & (1 << i)) { + runtime->hw.rate_max = rates[i]; + break; + } + } + return 0; +} diff -Nru a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c --- a/sound/core/seq/oss/seq_oss_midi.c Sun Feb 29 12:58:38 2004 +++ b/sound/core/seq/oss/seq_oss_midi.c Sun Feb 29 12:58:38 2004 @@ -491,7 +491,7 @@ } } } - snd_seq_oss_midi_close(dp, dev); + // snd_seq_oss_midi_close(dp, dev); snd_use_lock_free(&mdev->use_lock); } diff -Nru a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c --- a/sound/core/seq/oss/seq_oss_synth.c Sun Feb 29 12:58:38 2004 +++ b/sound/core/seq/oss/seq_oss_synth.c Sun Feb 29 12:58:38 2004 @@ -410,6 +410,8 @@ if (midi_synth_dev.opened <= 0) return; snd_seq_oss_midi_reset(dp, info->midi_mapped); + /* reopen the device */ + snd_seq_oss_midi_close(dp, dev); if (snd_seq_oss_midi_open(dp, info->midi_mapped, dp->file_mode) < 0) { midi_synth_dev.opened--; diff -Nru a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c --- a/sound/core/seq/seq_clientmgr.c Sun Feb 29 12:58:38 2004 +++ b/sound/core/seq/seq_clientmgr.c Sun Feb 29 12:58:38 2004 @@ -547,6 +547,36 @@ /* + * expand a quoted event. + */ +static int expand_quoted_event(snd_seq_event_t *event) +{ + snd_seq_event_t *quoted; + + quoted = event->data.quote.event; + if (quoted == NULL) { + snd_printd("seq: quoted event is NULL\n"); + return -EINVAL; + } + + event->type = quoted->type; + event->tag = quoted->tag; + event->source = quoted->source; + /* don't use quoted destination */ + event->data = quoted->data; + /* use quoted timestamp only if subscription/port didn't update it */ + if (event->queue == SNDRV_SEQ_QUEUE_DIRECT) { + event->flags = quoted->flags; + event->queue = quoted->queue; + event->time = quoted->time; + } else { + event->flags = (event->flags & SNDRV_SEQ_TIME_STAMP_MASK) + | (quoted->flags & ~SNDRV_SEQ_TIME_STAMP_MASK); + } + return 0; +} + +/* * deliver an event to the specified destination. * if filter is non-zero, client filter bitmap is tested. * @@ -581,12 +611,9 @@ update_timestamp_of_queue(event, dest_port->time_queue, dest_port->time_real); - /* expand the quoted event */ if (event->type == SNDRV_SEQ_EVENT_KERNEL_QUOTE) { quoted = 1; - event = event->data.quote.event; - if (event == NULL) { - snd_printd("seq: quoted event is NULL\n"); + if (expand_quoted_event(event) < 0) { result = 0; /* do not send bounce error */ goto __skip; } @@ -694,8 +721,8 @@ if (dest_client == NULL) return 0; /* no matching destination */ - read_lock(&client->ports_lock); - list_for_each(p, &client->ports_list_head) { + read_lock(&dest_client->ports_lock); + list_for_each(p, &dest_client->ports_list_head) { client_port_t *port = list_entry(p, client_port_t, list); event->dest.port = port->addr.port; /* pass NULL as source client to avoid error bounce */ @@ -706,7 +733,7 @@ break; num_ev++; } - read_unlock(&client->ports_lock); + read_unlock(&dest_client->ports_lock); snd_seq_client_unlock(dest_client); event->dest.port = SNDRV_SEQ_ADDRESS_BROADCAST; /* restore */ return (err < 0) ? err : num_ev; diff -Nru a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c --- a/sound/core/seq/seq_fifo.c Sun Feb 29 12:58:39 2004 +++ b/sound/core/seq/seq_fifo.c Sun Feb 29 12:58:39 2004 @@ -171,10 +171,12 @@ { snd_seq_event_cell_t *cell; unsigned long flags; + wait_queue_t wait; snd_assert(f != NULL, return -EINVAL); *cellp = NULL; + init_waitqueue_entry(&wait, current); spin_lock_irqsave(&f->lock, flags); while ((cell = fifo_cell_out(f)) == NULL) { if (nonblock) { @@ -182,17 +184,19 @@ spin_unlock_irqrestore(&f->lock, flags); return -EAGAIN; } - spin_unlock(&f->lock); - interruptible_sleep_on(&f->input_sleep); - spin_lock(&f->lock); - + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&f->input_sleep, &wait); + spin_unlock_irq(&f->lock); + schedule(); + spin_lock_irq(&f->lock); + remove_wait_queue(&f->input_sleep, &wait); if (signal_pending(current)) { spin_unlock_irqrestore(&f->lock, flags); return -ERESTARTSYS; } } - *cellp = cell; spin_unlock_irqrestore(&f->lock, flags); + *cellp = cell; return 0; } diff -Nru a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c --- a/sound/core/seq/seq_memory.c Sun Feb 29 12:58:38 2004 +++ b/sound/core/seq/seq_memory.c Sun Feb 29 12:58:38 2004 @@ -220,12 +220,14 @@ snd_seq_event_cell_t *cell; unsigned long flags; int err = -EAGAIN; + wait_queue_t wait; if (pool == NULL) return -EINVAL; *cellp = NULL; + init_waitqueue_entry(&wait, current); spin_lock_irqsave(&pool->lock, flags); if (pool->ptr == NULL) { /* not initialized */ snd_printd("seq: pool is not initialized\n"); @@ -234,9 +236,12 @@ } while (pool->free == NULL && ! nonblock && ! pool->closing) { - spin_unlock(&pool->lock); - interruptible_sleep_on(&pool->output_sleep); - spin_lock(&pool->lock); + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&pool->output_sleep, &wait); + spin_unlock_irq(&pool->lock); + schedule(); + spin_lock_irq(&pool->lock); + remove_wait_queue(&pool->output_sleep, &wait); /* interrupted? */ if (signal_pending(current)) { err = -ERESTARTSYS; diff -Nru a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c --- a/sound/core/seq/seq_midi.c Sun Feb 29 12:58:39 2004 +++ b/sound/core/seq/seq_midi.c Sun Feb 29 12:58:39 2004 @@ -285,7 +285,7 @@ cinfo.client = client->seq_client; cinfo.type = KERNEL_CLIENT; name = rmidi->name[0] ? (const char *)rmidi->name : "External MIDI"; - snprintf(cinfo.name, sizeof(cinfo.name), "Rawmidi %d - %s", card->number, name); + snprintf(cinfo.name, sizeof(cinfo.name), "%s - Rawmidi %d", name, card->number); return snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &cinfo); } diff -Nru a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c --- a/sound/drivers/mpu401/mpu401_uart.c Sun Feb 29 12:58:39 2004 +++ b/sound/drivers/mpu401/mpu401_uart.c Sun Feb 29 12:58:39 2004 @@ -528,9 +528,9 @@ mpu->irq = irq; mpu->irq_flags = irq_flags; if (card->shortname[0]) - snprintf(rmidi->name, sizeof(rmidi->name), "%s MPU-401", card->shortname); + snprintf(rmidi->name, sizeof(rmidi->name), "%s MIDI", card->shortname); else - sprintf(rmidi->name, "MPU-401 (UART) %d-%d", card->number, device); + sprintf(rmidi->name, "MPU-401 MIDI %d-%d", card->number, device); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_mpu401_uart_output); snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_mpu401_uart_input); rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | diff -Nru a/sound/drivers/opl3/Makefile b/sound/drivers/opl3/Makefile --- a/sound/drivers/opl3/Makefile Sun Feb 29 12:58:38 2004 +++ b/sound/drivers/opl3/Makefile Sun Feb 29 12:58:38 2004 @@ -10,36 +10,66 @@ endif OPL3_OBJS = snd-opl3-lib.o -ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) -OPL3_OBJS += snd-opl3-synth.o -endif +OPL3_SYNTH_OBJS = snd-opl3-synth.o + +# +# this function returns: +# "m" - CONFIG_SND_SEQUENCER is m +# - CONFIG_SND_SEQUENCER is undefined +# otherwise parameter #1 value +# +sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1))) # Toplevel Module Dependency obj-$(CONFIG_SND_ALS100) += $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_ALS100)) += $(OPL3_SYNTH_OBJS) obj-$(CONFIG_SND_AZT2320) += $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_AZT2320)) += $(OPL3_SYNTH_OBJS) obj-$(CONFIG_SND_AZT3328) += $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_AZT3328)) += $(OPL3_SYNTH_OBJS) obj-$(CONFIG_SND_DT019X) += $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_DT019X)) += $(OPL3_SYNTH_OBJS) obj-$(CONFIG_SND_ES18XX) += $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_ES18XX)) += $(OPL3_SYNTH_OBJS) obj-$(CONFIG_SND_OPL3SA2) += $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_OPL3SA2)) += $(OPL3_SYNTH_OBJS) obj-$(CONFIG_SND_AD1816A) += $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_AD1816A)) += $(OPL3_SYNTH_OBJS) obj-$(CONFIG_SND_CS4232) += $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_CS4232)) += $(OPL3_SYNTH_OBJS) obj-$(CONFIG_SND_PC98_CS4232) += $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_PC98_CS4232)) += $(OPL3_SYNTH_OBJS) obj-$(CONFIG_SND_CS4236) += $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_CS4236)) += $(OPL3_SYNTH_OBJS) obj-$(CONFIG_SND_ES1688) += $(OPL3_OBJS) -obj-$(CONFIG_SND_GUSEXTREME) += $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_GUSEXTREME)) += $(OPL3_SYNTH_OBJS) obj-$(CONFIG_SND_OPTI92X_AD1848) += $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_OPTI92X_AD1848)) += $(OPL3_SYNTH_OBJS) obj-$(CONFIG_SND_OPTI92X_CS4231) += $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_OPTI92X_CS4231)) += $(OPL3_SYNTH_OBJS) obj-$(CONFIG_SND_OPTI93X) += $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_OPTI93X)) += $(OPL3_SYNTH_OBJS) obj-$(CONFIG_SND_SB8) += $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_SB8)) += $(OPL3_SYNTH_OBJS) obj-$(CONFIG_SND_SB16) += $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_SB16)) += $(OPL3_SYNTH_OBJS) obj-$(CONFIG_SND_SBAWE) += $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += $(OPL3_SYNTH_OBJS) obj-$(CONFIG_SND_WAVEFRONT) += $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_WAVEFRONT)) += $(OPL3_SYNTH_OBJS) obj-$(CONFIG_SND_ALS4000) += $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_ALS4000)) += $(OPL3_SYNTH_OBJS) obj-$(CONFIG_SND_CMIPCI) += $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_CMIPCI)) += $(OPL3_SYNTH_OBJS) obj-$(CONFIG_SND_CS4281) += $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_CS4281)) += $(OPL3_SYNTH_OBJS) obj-$(CONFIG_SND_ES1938) += $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_ES1938)) += $(OPL3_SYNTH_OBJS) obj-$(CONFIG_SND_FM801) += $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_FM801)) += $(OPL3_SYNTH_OBJS) obj-$(CONFIG_SND_SONICVIBES) += $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_SONICVIBES)) += $(OPL3_SYNTH_OBJS) obj-$(CONFIG_SND_YMFPCI) += $(OPL3_OBJS) +obj-$(call sequencer,$(CONFIG_SND_YMFPCI)) += $(OPL3_SYNTH_OBJS) obj-m := $(sort $(obj-m)) diff -Nru a/sound/drivers/opl4/Makefile b/sound/drivers/opl4/Makefile --- a/sound/drivers/opl4/Makefile Sun Feb 29 12:58:39 2004 +++ b/sound/drivers/opl4/Makefile Sun Feb 29 12:58:39 2004 @@ -7,9 +7,19 @@ snd-opl4-synth-objs := opl4_seq.o opl4_synth.o yrw801.o OPL4_OBJS := snd-opl4-lib.o -ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) - OPL4_OBJS += snd-opl4-synth.o -endif +OPL4_SYNTH_OBJS := snd-opl4-synth.o + +# +# this function returns: +# "m" - CONFIG_SND_SEQUENCER is m +# - CONFIG_SND_SEQUENCER is undefined +# otherwise parameter #1 value +# +sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1))) obj-$(CONFIG_SND_OPTI92X_AD1848) += $(OPL4_OBJS) +obj-$(call sequencer,$(CONFIG_SND_OPTI92X_AD1848)) += $(OPL4_SYNTH_OBJS) obj-$(CONFIG_SND_OPTI92X_CS4231) += $(OPL4_OBJS) +obj-$(call sequencer,$CONFIG_SND_OPTI92X_CS4231)) += $(OPL4_SYNTH_OBJS) + +obj-m := $(sort $(obj-m)) diff -Nru a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c --- a/sound/drivers/vx/vx_core.c Sun Feb 29 12:58:39 2004 +++ b/sound/drivers/vx/vx_core.c Sun Feb 29 12:58:39 2004 @@ -355,11 +355,12 @@ */ int vx_send_msg(vx_core_t *chip, struct vx_rmh *rmh) { + unsigned long flags; int err; - spin_lock_bh(&chip->lock); + spin_lock_irqsave(&chip->lock, flags); err = vx_send_msg_nolock(chip, rmh); - spin_unlock_bh(&chip->lock); + spin_unlock_irqrestore(&chip->lock, flags); return err; } @@ -414,11 +415,12 @@ */ int vx_send_rih(vx_core_t *chip, int cmd) { + unsigned long flags; int err; - spin_lock_bh(&chip->lock); + spin_lock_irqsave(&chip->lock, flags); err = vx_send_rih_nolock(chip, cmd); - spin_unlock_bh(&chip->lock); + spin_unlock_irqrestore(&chip->lock, flags); return err; } diff -Nru a/sound/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c --- a/sound/drivers/vx/vx_mixer.c Sun Feb 29 12:58:38 2004 +++ b/sound/drivers/vx/vx_mixer.c Sun Feb 29 12:58:38 2004 @@ -919,7 +919,7 @@ if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958, chip))) < 0) return err; /* VU, peak, saturation meters */ - for (c = 0; c < 1; c++) { + for (c = 0; c < 2; c++) { static char *dir[2] = { "Output", "Input" }; for (i = 0; i < chip->hw->num_ins; i++) { int val = (i * 2) | (c << 8); diff -Nru a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c --- a/sound/drivers/vx/vx_pcm.c Sun Feb 29 12:58:38 2004 +++ b/sound/drivers/vx/vx_pcm.c Sun Feb 29 12:58:38 2004 @@ -561,7 +561,7 @@ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, .rate_min = 5000, .rate_max = 48000, - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .buffer_bytes_max = (128*1024), .period_bytes_min = 126, @@ -958,7 +958,7 @@ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, .rate_min = 5000, .rate_max = 48000, - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .buffer_bytes_max = (128*1024), .period_bytes_min = 126, diff -Nru a/sound/i2c/other/Makefile b/sound/i2c/other/Makefile --- a/sound/i2c/other/Makefile Sun Feb 29 12:58:38 2004 +++ b/sound/i2c/other/Makefile Sun Feb 29 12:58:38 2004 @@ -3,10 +3,12 @@ # Copyright (c) 2003 by Jaroslav Kysela # +snd-ak4117-objs := ak4117.o snd-ak4xxx-adda-objs := ak4xxx-adda.o snd-tea575x-tuner-objs := tea575x-tuner.o # Module Dependency +obj-$(CONFIG_SND_PDAUDIOCF) += snd-ak4117.o obj-$(CONFIG_SND_ICE1712) += snd-ak4xxx-adda.o obj-$(CONFIG_SND_ICE1724) += snd-ak4xxx-adda.o obj-$(CONFIG_SND_FM801_TEA575X) += snd-tea575x-tuner.o diff -Nru a/sound/i2c/other/ak4117.c b/sound/i2c/other/ak4117.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/i2c/other/ak4117.c Sun Feb 29 12:58:39 2004 @@ -0,0 +1,561 @@ +/* + * Routines for control of the AK4117 via 4-wire serial interface + * IEC958 (S/PDIF) receiver by Asahi Kasei + * Copyright (c) by Jaroslav Kysela + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("AK4117 IEC958 (S/PDIF) receiver by Asahi Kasei"); +MODULE_LICENSE("GPL"); + +#define chip_t ak4117_t + +#define AK4117_ADDR 0x00 /* fixed address */ + +static void snd_ak4117_timer(unsigned long data); + +static void reg_write(ak4117_t *ak4117, unsigned char reg, unsigned char val) +{ + ak4117->write(ak4117->private_data, reg, val); + if (reg < sizeof(ak4117->regmap)) + ak4117->regmap[reg] = val; +} + +static inline unsigned char reg_read(ak4117_t *ak4117, unsigned char reg) +{ + return ak4117->read(ak4117->private_data, reg); +} + +#if 0 +static void reg_dump(ak4117_t *ak4117) +{ + int i; + + printk("AK4117 REG DUMP:\n"); + for (i = 0; i < 0x1b; i++) + printk("reg[%02x] = %02x (%02x)\n", i, reg_read(ak4117, i), i < sizeof(ak4117->regmap) ? ak4117->regmap[i] : 0); +} +#endif + +static void snd_ak4117_free(ak4117_t *chip) +{ + del_timer(&chip->timer); + snd_magic_kfree(chip); +} + +static int snd_ak4117_dev_free(snd_device_t *device) +{ + ak4117_t *chip = snd_magic_cast(ak4117_t, device->device_data, return -ENXIO); + snd_ak4117_free(chip); + return 0; +} + +int snd_ak4117_create(snd_card_t *card, ak4117_read_t *read, ak4117_write_t *write, + unsigned char pgm[5], void *private_data, ak4117_t **r_ak4117) +{ + ak4117_t *chip; + int err = 0; + unsigned char reg; + static snd_device_ops_t ops = { + .dev_free = snd_ak4117_dev_free, + }; + + chip = (ak4117_t *)snd_magic_kcalloc(ak4117_t, 0, GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + spin_lock_init(&chip->lock); + chip->card = card; + chip->read = read; + chip->write = write; + chip->private_data = private_data; + init_timer(&chip->timer); + chip->timer.data = (unsigned long)chip; + chip->timer.function = snd_ak4117_timer; + + for (reg = 0; reg < 5; reg++) + chip->regmap[reg] = pgm[reg]; + snd_ak4117_reinit(chip); + + chip->rcs0 = reg_read(chip, AK4117_REG_RCS0) & ~(AK4117_QINT | AK4117_CINT | AK4117_STC); + chip->rcs1 = reg_read(chip, AK4117_REG_RCS1); + chip->rcs2 = reg_read(chip, AK4117_REG_RCS2); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) + goto __fail; + + if (r_ak4117) + *r_ak4117 = chip; + return 0; + + __fail: + snd_ak4117_free(chip); + return err < 0 ? err : -EIO; +} + +void snd_ak4117_reg_write(ak4117_t *chip, unsigned char reg, unsigned char mask, unsigned char val) +{ + if (reg >= 5) + return; + reg_write(chip, reg, (chip->regmap[reg] & ~mask) | val); +} + +void snd_ak4117_reinit(ak4117_t *chip) +{ + unsigned char old = chip->regmap[AK4117_REG_PWRDN], reg; + + del_timer(&chip->timer); + chip->init = 1; + /* bring the chip to reset state and powerdown state */ + reg_write(chip, AK4117_REG_PWRDN, 0); + udelay(200); + /* release reset, but leave powerdown */ + reg_write(chip, AK4117_REG_PWRDN, (old | AK4117_RST) & ~AK4117_PWN); + udelay(200); + for (reg = 1; reg < 5; reg++) + reg_write(chip, reg, chip->regmap[reg]); + /* release powerdown, everything is initialized now */ + reg_write(chip, AK4117_REG_PWRDN, old | AK4117_RST | AK4117_PWN); + chip->init = 0; + chip->timer.expires = 1 + jiffies; + add_timer(&chip->timer); +} + +static unsigned int external_rate(unsigned char rcs1) +{ + switch (rcs1 & (AK4117_FS0|AK4117_FS1|AK4117_FS2|AK4117_FS3)) { + case AK4117_FS_32000HZ: return 32000; + case AK4117_FS_44100HZ: return 44100; + case AK4117_FS_48000HZ: return 48000; + case AK4117_FS_88200HZ: return 88200; + case AK4117_FS_96000HZ: return 96000; + case AK4117_FS_176400HZ: return 176400; + case AK4117_FS_192000HZ: return 192000; + default: return 0; + } +} + +static int snd_ak4117_in_error_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = LONG_MAX; + return 0; +} + +static int snd_ak4117_in_error_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + ak4117_t *chip = snd_kcontrol_chip(kcontrol); + long *ptr; + + spin_lock_irq(&chip->lock); + ptr = (long *)(((char *)chip) + kcontrol->private_value); + ucontrol->value.integer.value[0] = *ptr; + *ptr = 0; + spin_unlock_irq(&chip->lock); + return 0; +} + +static int snd_ak4117_in_bit_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_ak4117_in_bit_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + ak4117_t *chip = snd_kcontrol_chip(kcontrol); + unsigned char reg = kcontrol->private_value & 0xff; + unsigned char bit = (kcontrol->private_value >> 8) & 0xff; + unsigned char inv = (kcontrol->private_value >> 31) & 1; + + ucontrol->value.integer.value[0] = ((reg_read(chip, reg) & (1 << bit)) ? 1 : 0) ^ inv; + return 0; +} + +static int snd_ak4117_rx_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_ak4117_rx_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + ak4117_t *chip = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = (chip->regmap[AK4117_REG_IO] & AK4117_IPS) ? 1 : 0; + return 0; +} + +static int snd_ak4117_rx_put(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + ak4117_t *chip = snd_kcontrol_chip(kcontrol); + int change; + u8 old_val; + + spin_lock_irq(&chip->lock); + old_val = chip->regmap[AK4117_REG_IO]; + change = !!ucontrol->value.integer.value[0] != ((old_val & AK4117_IPS) ? 1 : 0); + if (change) + reg_write(chip, AK4117_REG_IO, (old_val & ~AK4117_IPS) | (ucontrol->value.integer.value[0] ? AK4117_IPS : 0)); + spin_unlock_irq(&chip->lock); + return change; +} + +static int snd_ak4117_rate_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 192000; + return 0; +} + +static int snd_ak4117_rate_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + ak4117_t *chip = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = external_rate(reg_read(chip, AK4117_REG_RCS1)); + return 0; +} + +static int snd_ak4117_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ak4117_spdif_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ak4117_t *chip = snd_kcontrol_chip(kcontrol); + unsigned i; + + for (i = 0; i < AK4117_REG_RXCSB_SIZE; i++) + ucontrol->value.iec958.status[i] = reg_read(chip, AK4117_REG_RXCSB0 + i); + return 0; +} + +static int snd_ak4117_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int snd_ak4117_spdif_mask_get(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + memset(ucontrol->value.iec958.status, 0xff, AK4117_REG_RXCSB_SIZE); + return 0; +} + +static int snd_ak4117_spdif_pinfo(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xffff; + uinfo->count = 4; + return 0; +} + +static int snd_ak4117_spdif_pget(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ak4117_t *chip = snd_kcontrol_chip(kcontrol); + unsigned short tmp; + + ucontrol->value.integer.value[0] = 0xf8f2; + ucontrol->value.integer.value[1] = 0x4e1f; + tmp = reg_read(chip, AK4117_REG_Pc0) | (reg_read(chip, AK4117_REG_Pc1) << 8); + ucontrol->value.integer.value[2] = tmp; + tmp = reg_read(chip, AK4117_REG_Pd0) | (reg_read(chip, AK4117_REG_Pd1) << 8); + ucontrol->value.integer.value[3] = tmp; + return 0; +} + +static int snd_ak4117_spdif_qinfo(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = AK4117_REG_QSUB_SIZE; + return 0; +} + +static int snd_ak4117_spdif_qget(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + ak4117_t *chip = snd_kcontrol_chip(kcontrol); + unsigned i; + + for (i = 0; i < AK4117_REG_QSUB_SIZE; i++) + ucontrol->value.bytes.data[i] = reg_read(chip, AK4117_REG_QSUB_ADDR + i); + return 0; +} + +/* Don't forget to change AK4117_CONTROLS define!!! */ +static snd_kcontrol_new_t snd_ak4117_iec958_controls[] = { +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Parity Errors", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4117_in_error_info, + .get = snd_ak4117_in_error_get, + .private_value = offsetof(ak4117_t, parity_errors), +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 V-Bit Errors", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4117_in_error_info, + .get = snd_ak4117_in_error_get, + .private_value = offsetof(ak4117_t, v_bit_errors), +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 C-CRC Errors", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4117_in_error_info, + .get = snd_ak4117_in_error_get, + .private_value = offsetof(ak4117_t, ccrc_errors), +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Q-CRC Errors", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4117_in_error_info, + .get = snd_ak4117_in_error_get, + .private_value = offsetof(ak4117_t, qcrc_errors), +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 External Rate", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4117_rate_info, + .get = snd_ak4117_rate_get, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,MASK), + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = snd_ak4117_spdif_mask_info, + .get = snd_ak4117_spdif_mask_get, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,DEFAULT), + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4117_spdif_info, + .get = snd_ak4117_spdif_get, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Preample Capture Default", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4117_spdif_pinfo, + .get = snd_ak4117_spdif_pget, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Q-subcode Capture Default", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4117_spdif_qinfo, + .get = snd_ak4117_spdif_qget, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Audio", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4117_in_bit_info, + .get = snd_ak4117_in_bit_get, + .private_value = (1<<31) | (3<<8) | AK4117_REG_RCS0, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Non-PCM Bitstream", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4117_in_bit_info, + .get = snd_ak4117_in_bit_get, + .private_value = (5<<8) | AK4117_REG_RCS1, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 DTS Bitstream", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_ak4117_in_bit_info, + .get = snd_ak4117_in_bit_get, + .private_value = (6<<8) | AK4117_REG_RCS1, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "AK4117 Input Select", + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE, + .info = snd_ak4117_rx_info, + .get = snd_ak4117_rx_get, + .put = snd_ak4117_rx_put, +} +}; + +int snd_ak4117_build(ak4117_t *ak4117, snd_pcm_substream_t *cap_substream) +{ + snd_kcontrol_t *kctl; + unsigned int idx; + int err; + + snd_assert(cap_substream, return -EINVAL); + ak4117->substream = cap_substream; + for (idx = 0; idx < AK4117_CONTROLS; idx++) { + kctl = snd_ctl_new1(&snd_ak4117_iec958_controls[idx], ak4117); + if (kctl == NULL) + return -ENOMEM; + kctl->id.device = cap_substream->pcm->device; + kctl->id.subdevice = cap_substream->number; + err = snd_ctl_add(ak4117->card, kctl); + if (err < 0) + return err; + ak4117->kctls[idx] = kctl; + } + return 0; +} + +int snd_ak4117_external_rate(ak4117_t *ak4117) +{ + unsigned char rcs1; + + rcs1 = reg_read(ak4117, AK4117_REG_RCS1); + return external_rate(rcs1); +} + +int snd_ak4117_check_rate_and_errors(ak4117_t *ak4117, unsigned int flags) +{ + snd_pcm_runtime_t *runtime = ak4117->substream ? ak4117->substream->runtime : NULL; + unsigned long _flags; + int res = 0; + unsigned char rcs0, rcs1, rcs2; + unsigned char c0, c1; + + rcs1 = reg_read(ak4117, AK4117_REG_RCS1); + if (flags & AK4117_CHECK_NO_STAT) + goto __rate; + rcs0 = reg_read(ak4117, AK4117_REG_RCS0); + rcs2 = reg_read(ak4117, AK4117_REG_RCS2); + // printk("AK IRQ: rcs0 = 0x%x, rcs1 = 0x%x, rcs2 = 0x%x\n", rcs0, rcs1, rcs2); + spin_lock_irqsave(&ak4117->lock, _flags); + if (rcs0 & AK4117_PAR) + ak4117->parity_errors++; + if (rcs0 & AK4117_V) + ak4117->v_bit_errors++; + if (rcs2 & AK4117_CCRC) + ak4117->ccrc_errors++; + if (rcs2 & AK4117_QCRC) + ak4117->qcrc_errors++; + c0 = (ak4117->rcs0 & (AK4117_QINT | AK4117_CINT | AK4117_STC | AK4117_AUDION | AK4117_AUTO | AK4117_UNLCK)) ^ + (rcs0 & (AK4117_QINT | AK4117_CINT | AK4117_STC | AK4117_AUDION | AK4117_AUTO | AK4117_UNLCK)); + c1 = (ak4117->rcs1 & (AK4117_DTSCD | AK4117_NPCM | AK4117_PEM | 0x0f)) ^ + (rcs1 & (AK4117_DTSCD | AK4117_NPCM | AK4117_PEM | 0x0f)); + ak4117->rcs0 = rcs0 & ~(AK4117_QINT | AK4117_CINT | AK4117_STC); + ak4117->rcs1 = rcs1; + ak4117->rcs2 = rcs2; + spin_unlock_irqrestore(&ak4117->lock, _flags); + + if (rcs0 & AK4117_PAR) + snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[0]->id); + if (rcs0 & AK4117_V) + snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[1]->id); + if (rcs2 & AK4117_CCRC) + snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[2]->id); + if (rcs2 & AK4117_QCRC) + snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[3]->id); + + /* rate change */ + if (c1 & 0x0f) + snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[4]->id); + + if ((c1 & AK4117_PEM) | (c0 & AK4117_CINT)) + snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[6]->id); + if (c0 & AK4117_QINT) + snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[8]->id); + + if (c0 & AK4117_AUDION) + snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[9]->id); + if (c1 & AK4117_NPCM) + snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[10]->id); + if (c1 & AK4117_DTSCD) + snd_ctl_notify(ak4117->card, SNDRV_CTL_EVENT_MASK_VALUE, &ak4117->kctls[11]->id); + + if (ak4117->change_callback && (c0 | c1) != 0) + ak4117->change_callback(ak4117, c0, c1); + + __rate: + /* compare rate */ + res = external_rate(rcs1); + if (!(flags & AK4117_CHECK_NO_RATE) && runtime && runtime->rate != res) { + snd_pcm_stream_lock_irqsave(ak4117->substream, _flags); + if (snd_pcm_running(ak4117->substream)) { + // printk("rate changed (%i <- %i)\n", runtime->rate, res); + snd_pcm_stop(ak4117->substream, SNDRV_PCM_STATE_DRAINING); + wake_up(&runtime->sleep); + res = 1; + } + snd_pcm_stream_unlock_irqrestore(ak4117->substream, _flags); + } + return res; +} + +static void snd_ak4117_timer(unsigned long data) +{ + ak4117_t *chip = snd_magic_cast(ak4117_t, (void *)data, return); + + if (chip->init) + return; + snd_ak4117_check_rate_and_errors(chip, 0); + chip->timer.expires = 1 + jiffies; + add_timer(&chip->timer); +} + +EXPORT_SYMBOL(snd_ak4117_create); +EXPORT_SYMBOL(snd_ak4117_reg_write); +EXPORT_SYMBOL(snd_ak4117_reinit); +EXPORT_SYMBOL(snd_ak4117_build); +EXPORT_SYMBOL(snd_ak4117_external_rate); +EXPORT_SYMBOL(snd_ak4117_check_rate_and_errors); diff -Nru a/sound/pci/Kconfig b/sound/pci/Kconfig --- a/sound/pci/Kconfig Sun Feb 29 12:58:39 2004 +++ b/sound/pci/Kconfig Sun Feb 29 12:58:39 2004 @@ -55,6 +55,12 @@ help Say 'Y' or 'M' to include support for Korg 1212IO. +config SND_MIXART + tristate "Digigram miXart" + depends on SND + help + Say 'Y' or 'M' to include support for Digigram miXart soundcard. + config SND_NM256 tristate "NeoMagic NM256AV/ZX" depends on SND @@ -156,7 +162,8 @@ config CONFIG_SND_FM801_TEA575X tristate "ForteMedia FM801 + TEA5757 tuner" - depends on SND_FM801 && CONFIG_VIDEO_DEV + depends on SND_FM801 + select CONFIG_VIDEO_DEV help Say 'Y' or 'M' to include support for ForteMedia FM801 based soundcards with TEA5757 tuner connected to GPIO1-3 pins (Media Forte SF256-PCS-02). diff -Nru a/sound/pci/Makefile b/sound/pci/Makefile --- a/sound/pci/Makefile Sun Feb 29 12:58:38 2004 +++ b/sound/pci/Makefile Sun Feb 29 12:58:38 2004 @@ -38,4 +38,16 @@ obj-$(CONFIG_SND_SONICVIBES) += snd-sonicvibes.o obj-$(CONFIG_SND_VIA82XX) += snd-via82xx.o -obj-$(CONFIG_SND) += ac97/ ali5451/ cs46xx/ emu10k1/ korg1212/ nm256/ rme9652/ trident/ ymfpci/ ice1712/ vx222/ +obj-$(CONFIG_SND) += \ + ac97/ \ + ali5451/ \ + cs46xx/ \ + emu10k1/ \ + ice1712/ \ + korg1212/ \ + mixart/ \ + nm256/ \ + rme9652/ \ + trident/ \ + ymfpci/ \ + vx222/ diff -Nru a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c --- a/sound/pci/ac97/ac97_codec.c Sun Feb 29 12:58:39 2004 +++ b/sound/pci/ac97/ac97_codec.c Sun Feb 29 12:58:39 2004 @@ -100,6 +100,7 @@ { 0x41445361, 0xffffffff, "AD1886", patch_ad1886, NULL }, { 0x41445362, 0xffffffff, "AD1887", patch_ad1881, NULL }, { 0x41445363, 0xffffffff, "AD1886A", patch_ad1881, NULL }, +{ 0x41445368, 0xffffffff, "AD1888", patch_ad1888, NULL }, { 0x41445370, 0xffffffff, "AD1980", patch_ad1980, NULL }, { 0x41445372, 0xffffffff, "AD1981A", patch_ad1981a, NULL }, { 0x41445374, 0xffffffff, "AD1981B", patch_ad1981b, NULL }, @@ -1526,38 +1527,40 @@ return 0; } -static int snd_ac97_test_rate(ac97_t *ac97, int reg, int rate) +static int snd_ac97_test_rate(ac97_t *ac97, int reg, int shadow_reg, int rate) { unsigned short val; unsigned int tmp; tmp = ((unsigned int)rate * ac97->bus->clock) / 48000; snd_ac97_write_cache(ac97, reg, tmp & 0xffff); + if (shadow_reg) + snd_ac97_write_cache(ac97, shadow_reg, tmp & 0xffff); val = snd_ac97_read(ac97, reg); return val == (tmp & 0xffff); } -static void snd_ac97_determine_rates(ac97_t *ac97, int reg, unsigned int *r_result) +static void snd_ac97_determine_rates(ac97_t *ac97, int reg, int shadow_reg, unsigned int *r_result) { unsigned int result = 0; /* test a non-standard rate */ - if (snd_ac97_test_rate(ac97, reg, 11000)) + if (snd_ac97_test_rate(ac97, reg, shadow_reg, 11000)) result |= SNDRV_PCM_RATE_CONTINUOUS; /* let's try to obtain standard rates */ - if (snd_ac97_test_rate(ac97, reg, 8000)) + if (snd_ac97_test_rate(ac97, reg, shadow_reg, 8000)) result |= SNDRV_PCM_RATE_8000; - if (snd_ac97_test_rate(ac97, reg, 11025)) + if (snd_ac97_test_rate(ac97, reg, shadow_reg, 11025)) result |= SNDRV_PCM_RATE_11025; - if (snd_ac97_test_rate(ac97, reg, 16000)) + if (snd_ac97_test_rate(ac97, reg, shadow_reg, 16000)) result |= SNDRV_PCM_RATE_16000; - if (snd_ac97_test_rate(ac97, reg, 22050)) + if (snd_ac97_test_rate(ac97, reg, shadow_reg, 22050)) result |= SNDRV_PCM_RATE_22050; - if (snd_ac97_test_rate(ac97, reg, 32000)) + if (snd_ac97_test_rate(ac97, reg, shadow_reg, 32000)) result |= SNDRV_PCM_RATE_32000; - if (snd_ac97_test_rate(ac97, reg, 44100)) + if (snd_ac97_test_rate(ac97, reg, shadow_reg, 44100)) result |= SNDRV_PCM_RATE_44100; - if (snd_ac97_test_rate(ac97, reg, 48000)) + if (snd_ac97_test_rate(ac97, reg, shadow_reg, 48000)) result |= SNDRV_PCM_RATE_48000; *r_result = result; } @@ -1866,8 +1869,8 @@ if (ac97->ext_id & 0x0189) /* L/R, MIC, SDAC, LDAC VRA support */ snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, ac97->ext_id & 0x0189); if (ac97->ext_id & AC97_EI_VRA) { /* VRA support */ - snd_ac97_determine_rates(ac97, AC97_PCM_FRONT_DAC_RATE, &ac97->rates[AC97_RATES_FRONT_DAC]); - snd_ac97_determine_rates(ac97, AC97_PCM_LR_ADC_RATE, &ac97->rates[AC97_RATES_ADC]); + snd_ac97_determine_rates(ac97, AC97_PCM_FRONT_DAC_RATE, 0, &ac97->rates[AC97_RATES_FRONT_DAC]); + snd_ac97_determine_rates(ac97, AC97_PCM_LR_ADC_RATE, 0, &ac97->rates[AC97_RATES_ADC]); } else { ac97->rates[AC97_RATES_FRONT_DAC] = SNDRV_PCM_RATE_48000; ac97->rates[AC97_RATES_ADC] = SNDRV_PCM_RATE_48000; @@ -1884,16 +1887,16 @@ SNDRV_PCM_RATE_32000; } if (ac97->ext_id & AC97_EI_VRM) { /* MIC VRA support */ - snd_ac97_determine_rates(ac97, AC97_PCM_MIC_ADC_RATE, &ac97->rates[AC97_RATES_MIC_ADC]); + snd_ac97_determine_rates(ac97, AC97_PCM_MIC_ADC_RATE, 0, &ac97->rates[AC97_RATES_MIC_ADC]); } else { ac97->rates[AC97_RATES_MIC_ADC] = SNDRV_PCM_RATE_48000; } if (ac97->ext_id & AC97_EI_SDAC) { /* SDAC support */ - snd_ac97_determine_rates(ac97, AC97_PCM_SURR_DAC_RATE, &ac97->rates[AC97_RATES_SURR_DAC]); + snd_ac97_determine_rates(ac97, AC97_PCM_SURR_DAC_RATE, AC97_PCM_FRONT_DAC_RATE, &ac97->rates[AC97_RATES_SURR_DAC]); ac97->scaps |= AC97_SCAP_SURROUND_DAC; } if (ac97->ext_id & AC97_EI_LDAC) { /* LDAC support */ - snd_ac97_determine_rates(ac97, AC97_PCM_LFE_DAC_RATE, &ac97->rates[AC97_RATES_LFE_DAC]); + snd_ac97_determine_rates(ac97, AC97_PCM_LFE_DAC_RATE, AC97_PCM_FRONT_DAC_RATE, &ac97->rates[AC97_RATES_LFE_DAC]); ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC; } /* additional initializations */ @@ -2112,6 +2115,8 @@ { /* FIXME: error checks.. */ if (remove_master) { + if (ctl_find(ac97, "Headphone Playback Switch") == NULL) + return 0; snd_ac97_remove_ctl(ac97, "Master Playback Switch"); snd_ac97_remove_ctl(ac97, "Master Playback Volume"); } else { diff -Nru a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c --- a/sound/pci/ac97/ac97_patch.c Sun Feb 29 12:58:39 2004 +++ b/sound/pci/ac97/ac97_patch.c Sun Feb 29 12:58:39 2004 @@ -652,7 +652,7 @@ static const snd_kcontrol_new_t snd_ac97_controls_ad1885[] = { AC97_SINGLE("Digital Mono Direct", AC97_AD_MISC, 11, 1, 0), - AC97_SINGLE("Digital Audio Mode", AC97_AD_MISC, 12, 1, 0), + /* AC97_SINGLE("Digital Audio Mode", AC97_AD_MISC, 12, 1, 0), */ /* seems problematic */ AC97_SINGLE("Low Power Mixer", AC97_AD_MISC, 14, 1, 0), AC97_SINGLE("Zero Fill DAC", AC97_AD_MISC, 15, 1, 0), }; @@ -682,6 +682,9 @@ jack = snd_ac97_read(ac97, AC97_AD_JACK_SPDIF); snd_ac97_write_cache(ac97, AC97_AD_JACK_SPDIF, jack | 0x0300); + /* set default */ + snd_ac97_write_cache(ac97, AC97_AD_MISC, 0x0404); + ac97->build_ops = &patch_ad1885_build_ops; return 0; } @@ -799,7 +802,7 @@ return 0; } -static int snd_ac97_ad1980_lohpsel_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +static int snd_ac97_ad1888_lohpsel_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = 1; @@ -808,7 +811,7 @@ return 0; } -static int snd_ac97_ad1980_lohpsel_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t* ucontrol) +static int snd_ac97_ad1888_lohpsel_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t* ucontrol) { ac97_t *ac97 = snd_kcontrol_chip(kcontrol); unsigned short val; @@ -818,7 +821,7 @@ return 0; } -static int snd_ac97_ad1980_lohpsel_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int snd_ac97_ad1888_lohpsel_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { ac97_t *ac97 = snd_kcontrol_chip(kcontrol); unsigned short val; @@ -829,7 +832,7 @@ AC97_AD198X_LOSEL | AC97_AD198X_HPSEL, val); } -static int snd_ac97_ad1980_downmix_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +static int snd_ac97_ad1888_downmix_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) { static char *texts[3] = {"Off", "6 -> 4", "6 -> 2"}; @@ -842,7 +845,7 @@ return 0; } -static int snd_ac97_ad1980_downmix_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t* ucontrol) +static int snd_ac97_ad1888_downmix_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t* ucontrol) { ac97_t *ac97 = snd_kcontrol_chip(kcontrol); unsigned short val; @@ -855,7 +858,7 @@ return 0; } -static int snd_ac97_ad1980_downmix_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +static int snd_ac97_ad1888_downmix_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { ac97_t *ac97 = snd_kcontrol_chip(kcontrol); unsigned short val; @@ -871,51 +874,47 @@ AC97_AD198X_DMIX0 | AC97_AD198X_DMIX1, val); } -static const snd_kcontrol_new_t snd_ac97_ad1980_controls[] = { +static const snd_kcontrol_new_t snd_ac97_ad1888_controls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Exchange Front/Surround", - .info = snd_ac97_ad1980_lohpsel_info, - .get = snd_ac97_ad1980_lohpsel_get, - .put = snd_ac97_ad1980_lohpsel_put + .info = snd_ac97_ad1888_lohpsel_info, + .get = snd_ac97_ad1888_lohpsel_get, + .put = snd_ac97_ad1888_lohpsel_put }, AC97_SINGLE("Spread Front to Surround and Center/LFE", AC97_AD_MISC, 7, 1, 0), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Downmix", - .info = snd_ac97_ad1980_downmix_info, - .get = snd_ac97_ad1980_downmix_get, - .put = snd_ac97_ad1980_downmix_put + .info = snd_ac97_ad1888_downmix_info, + .get = snd_ac97_ad1888_downmix_get, + .put = snd_ac97_ad1888_downmix_put }, AC97_SINGLE("Surround Jack as Input", AC97_AD_MISC, 12, 1, 0), AC97_SINGLE("Center/LFE Jack as Input", AC97_AD_MISC, 11, 1, 0), }; -static int patch_ad1980_specific(ac97_t *ac97) +static int patch_ad1888_specific(ac97_t *ac97) { - int err; - /* rename 0x04 as "Master" and 0x02 as "Master Surround" */ snd_ac97_rename_ctl(ac97, "Master Playback Switch", "Master Surround Playback Switch"); snd_ac97_rename_ctl(ac97, "Master Playback Volume", "Master Surround Playback Volume"); snd_ac97_rename_ctl(ac97, "Headphone Playback Switch", "Master Playback Switch"); snd_ac97_rename_ctl(ac97, "Headphone Playback Volume", "Master Playback Volume"); - if ((err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1)) < 0) - return err; - return patch_build_controls(ac97, snd_ac97_ad1980_controls, ARRAY_SIZE(snd_ac97_ad1980_controls)); + return patch_build_controls(ac97, snd_ac97_ad1888_controls, ARRAY_SIZE(snd_ac97_ad1888_controls)); } -static struct snd_ac97_build_ops patch_ad1980_build_ops = { +static struct snd_ac97_build_ops patch_ad1888_build_ops = { .build_post_spdif = patch_ad198x_post_spdif, - .build_specific = patch_ad1980_specific + .build_specific = patch_ad1888_specific }; -int patch_ad1980(ac97_t * ac97) +int patch_ad1888(ac97_t * ac97) { unsigned short misc; patch_ad1881(ac97); - ac97->build_ops = &patch_ad1980_build_ops; + ac97->build_ops = &patch_ad1888_build_ops; /* Switch FRONT/SURROUND LINE-OUT/HP-OUT default connection */ /* it seems that most vendors connect line-out connector to headphone out of AC'97 */ /* AD-compatible mode */ @@ -930,6 +929,27 @@ return 0; } +static int patch_ad1980_specific(ac97_t *ac97) +{ + int err; + + if ((err = patch_ad1888_specific(ac97)) < 0) + return err; + return patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1); +} + +static struct snd_ac97_build_ops patch_ad1980_build_ops = { + .build_post_spdif = patch_ad198x_post_spdif, + .build_specific = patch_ad1980_specific +}; + +int patch_ad1980(ac97_t * ac97) +{ + patch_ad1888(ac97); + ac97->build_ops = &patch_ad1980_build_ops; + return 0; +} + static const snd_kcontrol_new_t snd_ac97_ad1985_controls[] = { AC97_SINGLE("Center/LFE Jack as Mic", AC97_AD_SERIAL_CFG, 9, 1, 0), AC97_SINGLE("Exchange Center/LFE", AC97_AD_SERIAL_CFG, 3, 1, 0) @@ -1014,8 +1034,7 @@ ucontrol->value.integer.value[0] ? (1 << 10) : 0); if (change) { /* GPIO0 write for mic */ - snd_ac97_update_bits(ac97, 0x76, 0x01, - ucontrol->value.integer.value[0] ? 0 : 0x01); + snd_ac97_update_bits(ac97, 0x76, 0x01, 0x01); /* GPIO0 high for mic */ snd_ac97_update_bits(ac97, 0x78, 0x100, ucontrol->value.integer.value[0] ? 0 : 0x100); @@ -1092,10 +1111,7 @@ mic_off = snd_ac97_read(ac97, AC97_ALC650_MULTICH) & (1 << 10); /* GPIO0 direction */ val = snd_ac97_read(ac97, AC97_ALC650_GPIO_SETUP); - if (mic_off) - val &= ~0x01; - else - val |= 0x01; + val |= 0x01; snd_ac97_write_cache(ac97, AC97_ALC650_GPIO_SETUP, val); val = snd_ac97_read(ac97, AC97_ALC650_GPIO_STATUS); if (mic_off) @@ -1111,10 +1127,36 @@ return 0; } +static int snd_ac97_alc655_mic_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = (ac97->regs[AC97_ALC650_MULTICH] >> 10) & 1; + return 0; +} + +static int snd_ac97_alc655_mic_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int change; + + snd_ac97_update_bits(ac97, 0x7a, 1 << 12, /* misc control; vrefout disable */ + ucontrol->value.integer.value[0] ? (1 << 12) : 0); + change = snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 10, + ucontrol->value.integer.value[0] ? (1 << 10) : 0); + return change; +} + + static const snd_kcontrol_new_t snd_ac97_controls_alc655[] = { AC97_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0), AC97_SINGLE("Line-In As Surround", AC97_ALC650_MULTICH, 9, 1, 0), - AC97_SINGLE("Mic As Center/LFE", AC97_ALC650_MULTICH, 10, 1, 0), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Mic As Center/LFE", + .info = snd_ac97_info_single, + .get = snd_ac97_alc655_mic_get, + .put = snd_ac97_alc655_mic_put, + }, }; static int alc655_iec958_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) @@ -1187,15 +1229,21 @@ int patch_alc655(ac97_t * ac97) { + unsigned int val; + ac97->spec.dev_flags = (ac97->id == 0x414c4780); /* ALC658 */ ac97->build_ops = &patch_alc655_ops; - /* enable spdif in */ - snd_ac97_write_cache(ac97, AC97_ALC650_CLOCK, - snd_ac97_read(ac97, AC97_ALC650_MULTICH) | 0x8000); - snd_ac97_write_cache(ac97, AC97_ALC650_CLOCK, - snd_ac97_read(ac97, AC97_ALC650_CLOCK) | 0x02); + /* adjust default values */ + val = snd_ac97_read(ac97, 0x7a); /* misc control */ + val |= (1 << 1); /* spdif input pin */ + val &= ~(1 << 12); /* vref enable */ + snd_ac97_write_cache(ac97, 0x7a, val); + val = snd_ac97_read(ac97, AC97_ALC650_MULTICH); + val |= (1 << 15); /* enable spdif in */ + val &= ~(1 << 10); /* disable center on mic */ + snd_ac97_write_cache(ac97, AC97_ALC650_MULTICH, val); /* full DAC volume */ snd_ac97_write_cache(ac97, AC97_ALC650_SURR_DAC_VOL, 0x0808); diff -Nru a/sound/pci/ac97/ac97_patch.h b/sound/pci/ac97/ac97_patch.h --- a/sound/pci/ac97/ac97_patch.h Sun Feb 29 12:58:38 2004 +++ b/sound/pci/ac97/ac97_patch.h Sun Feb 29 12:58:38 2004 @@ -41,6 +41,7 @@ int patch_ad1881(ac97_t * ac97); int patch_ad1885(ac97_t * ac97); int patch_ad1886(ac97_t * ac97); +int patch_ad1888(ac97_t * ac97); int patch_ad1980(ac97_t * ac97); int patch_ad1981a(ac97_t * ac97); int patch_ad1981b(ac97_t * ac97); diff -Nru a/sound/pci/ac97/ac97_pcm.c b/sound/pci/ac97/ac97_pcm.c --- a/sound/pci/ac97/ac97_pcm.c Sun Feb 29 12:58:38 2004 +++ b/sound/pci/ac97/ac97_pcm.c Sun Feb 29 12:58:38 2004 @@ -31,6 +31,7 @@ #include #include #include +#include #include "ac97_patch.h" #include "ac97_id.h" #include "ac97_local.h" @@ -176,6 +177,7 @@ static int set_spdif_rate(ac97_t *ac97, unsigned short rate) { unsigned short old, bits, reg, mask; + unsigned int sbits; if (! (ac97->ext_id & AC97_EI_SPDIF)) return -ENODEV; @@ -213,6 +215,26 @@ if (old != bits) { snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0); snd_ac97_update_bits(ac97, reg, mask, bits); + /* update the internal spdif bits */ + spin_lock(&ac97->reg_lock); + sbits = ac97->spdif_status; + if (sbits & IEC958_AES0_PROFESSIONAL) { + sbits &= ~IEC958_AES0_PRO_FS; + switch (rate) { + case 44100: sbits |= IEC958_AES0_PRO_FS_44100; break; + case 48000: sbits |= IEC958_AES0_PRO_FS_48000; break; + case 32000: sbits |= IEC958_AES0_PRO_FS_32000; break; + } + } else { + sbits &= ~(IEC958_AES3_CON_FS << 24); + switch (rate) { + case 44100: sbits |= IEC958_AES3_CON_FS_44100<<24; break; + case 48000: sbits |= IEC958_AES3_CON_FS_48000<<24; break; + case 32000: sbits |= IEC958_AES3_CON_FS_32000<<24; break; + } + } + ac97->spdif_status = sbits; + spin_unlock(&ac97->reg_lock); } snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF); return 0; diff -Nru a/sound/pci/ac97/ak4531_codec.c b/sound/pci/ac97/ak4531_codec.c --- a/sound/pci/ac97/ak4531_codec.c Sun Feb 29 12:58:38 2004 +++ b/sound/pci/ac97/ak4531_codec.c Sun Feb 29 12:58:38 2004 @@ -285,7 +285,7 @@ AK4531_DOUBLE("Aux Switch", 0, AK4531_LAUXA, AK4531_RAUXA, 7, 7, 1, 1), AK4531_DOUBLE("Aux Volume", 0, AK4531_LAUXA, AK4531_RAUXA, 0, 0, 0x1f, 1), AK4531_DOUBLE("Aux Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 5, 4, 1, 0), -AK4531_INPUT_SW("Aux Input Route", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 4, 3), +AK4531_INPUT_SW("Aux Capture Route", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 4, 3), AK4531_SINGLE("Mono Switch", 0, AK4531_MONO1, 7, 1, 1), AK4531_SINGLE("Mono Volume", 0, AK4531_MONO1, 0, 0x1f, 1), diff -Nru a/sound/pci/als4000.c b/sound/pci/als4000.c --- a/sound/pci/als4000.c Sun Feb 29 12:58:39 2004 +++ b/sound/pci/als4000.c Sun Feb 29 12:58:39 2004 @@ -99,9 +99,11 @@ MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); MODULE_PARM_DESC(enable, "Enable ALS4000 soundcard."); MODULE_PARM_SYNTAX(enable, SNDRV_INDEX_DESC); +#ifdef SUPPORT_JOYSTICK MODULE_PARM(joystick_port, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); MODULE_PARM_DESC(joystick_port, "Joystick port address for ALS4000 soundcard. (0 = disabled)"); MODULE_PARM_SYNTAX(joystick_port, SNDRV_ENABLED); +#endif #define chip_t sb_t diff -Nru a/sound/pci/cmipci.c b/sound/pci/cmipci.c --- a/sound/pci/cmipci.c Sun Feb 29 12:58:39 2004 +++ b/sound/pci/cmipci.c Sun Feb 29 12:58:39 2004 @@ -3151,7 +3151,7 @@ #ifdef SUPPORT_JOYSTICK if (joystick_port[dev] > 0) { if (joystick_port[dev] == 1) { /* auto-detect */ - static int ports[] = { 0x200, 0x201, 0 }; + static int ports[] = { 0x201, 0x200, 0 }; /* FIXME: majority is 0x201? */ int i; for (i = 0; ports[i]; i++) { joystick_port[dev] = ports[i]; diff -Nru a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c --- a/sound/pci/ice1712/ice1712.c Sun Feb 29 12:58:38 2004 +++ b/sound/pci/ice1712/ice1712.c Sun Feb 29 12:58:38 2004 @@ -2375,6 +2375,7 @@ ice->omni = omni ? 1 : 0; spin_lock_init(&ice->reg_lock); init_MUTEX(&ice->gpio_mutex); + init_MUTEX(&ice->open_mutex); ice->gpio.set_mask = snd_ice1712_set_gpio_mask; ice->gpio.set_dir = snd_ice1712_set_gpio_dir; ice->gpio.set_data = snd_ice1712_set_gpio_data; diff -Nru a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h --- a/sound/pci/ice1712/ice1712.h Sun Feb 29 12:58:38 2004 +++ b/sound/pci/ice1712/ice1712.h Sun Feb 29 12:58:38 2004 @@ -335,6 +335,9 @@ unsigned short hoontech_boxconfig[4]; unsigned int cur_rate; /* current rate */ + struct semaphore open_mutex; + snd_pcm_substream_t *pcm_reserved[4]; + unsigned int akm_codecs; akm4xxx_t *akm; struct snd_ice1712_spdif spdif; diff -Nru a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c --- a/sound/pci/ice1712/ice1724.c Sun Feb 29 12:58:39 2004 +++ b/sound/pci/ice1712/ice1724.c Sun Feb 29 12:58:39 2004 @@ -237,6 +237,18 @@ if (ice->capture_pro_substream) snd_pcm_period_elapsed(ice->capture_pro_substream); } + if (mtstat & VT1724_MULTI_PDMA1) { + if (ice->playback_con_substream_ds[0]) + snd_pcm_period_elapsed(ice->playback_con_substream_ds[0]); + } + if (mtstat & VT1724_MULTI_PDMA2) { + if (ice->playback_con_substream_ds[1]) + snd_pcm_period_elapsed(ice->playback_con_substream_ds[1]); + } + if (mtstat & VT1724_MULTI_PDMA3) { + if (ice->playback_con_substream_ds[2]) + snd_pcm_period_elapsed(ice->playback_con_substream_ds[2]); + } if (mtstat & VT1724_MULTI_PDMA4) { if (ice->playback_con_substream) snd_pcm_period_elapsed(ice->playback_con_substream); @@ -282,16 +294,6 @@ .mask = 0, }; -static unsigned int hw_channels[] = { - 2, 4, 6, 8 -}; - -static snd_pcm_hw_constraint_list_t hw_constraints_channels = { - .count = ARRAY_SIZE(hw_channels), - .list = hw_channels, - .mask = 0, -}; - static int snd_vt1724_pcm_trigger(snd_pcm_substream_t *substream, int cmd) { ice1712_t *ice = snd_pcm_substream_chip(substream); @@ -300,21 +302,16 @@ struct list_head *pos; snd_pcm_substream_t *s; + what = 0; + snd_pcm_group_for_each(pos, substream) { + s = snd_pcm_group_substream_entry(pos); + what |= (unsigned long)(s->runtime->private_data); + snd_pcm_trigger_done(s, substream); + } + switch (cmd) { case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - what = 0; - snd_pcm_group_for_each(pos, substream) { - s = snd_pcm_group_substream_entry(pos); - if (s == ice->playback_pro_substream) - what |= VT1724_PDMA0_PAUSE; - else if (s == ice->capture_pro_substream) - what |= VT1724_RDMA0_PAUSE; - else if (s == ice->playback_con_substream) - what |= VT1724_PDMA4_PAUSE; - else if (s == ice->capture_con_substream) - what |= VT1724_RDMA1_PAUSE; - } spin_lock(&ice->reg_lock); old = inb(ICEMT1724(ice, DMA_PAUSE)); if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) @@ -327,24 +324,6 @@ case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_STOP: - what = 0; - s = substream; - snd_pcm_group_for_each(pos, substream) { - s = snd_pcm_group_substream_entry(pos); - if (s == ice->playback_pro_substream) { - what |= VT1724_PDMA0_START; - snd_pcm_trigger_done(s, substream); - } else if (s == ice->capture_pro_substream) { - what |= VT1724_RDMA0_START; - snd_pcm_trigger_done(s, substream); - } else if (s == ice->playback_con_substream) { - what |= VT1724_PDMA4_START; - snd_pcm_trigger_done(s, substream); - } else if (s == ice->capture_con_substream) { - what |= VT1724_RDMA1_START; - snd_pcm_trigger_done(s, substream); - } - } spin_lock(&ice->reg_lock); old = inb(ICEMT1724(ice, DMA_CONTROL)); if (cmd == SNDRV_PCM_TRIGGER_START) @@ -364,8 +343,10 @@ /* */ -#define DMA_STARTS (VT1724_RDMA0_START|VT1724_PDMA0_START|VT1724_RDMA1_START|VT1724_PDMA4_START) -#define DMA_PAUSES (VT1724_RDMA0_PAUSE|VT1724_PDMA0_PAUSE|VT1724_RDMA1_PAUSE|VT1724_PDMA4_PAUSE) +#define DMA_STARTS (VT1724_RDMA0_START|VT1724_PDMA0_START|VT1724_RDMA1_START|\ + VT1724_PDMA1_START|VT1724_PDMA2_START|VT1724_PDMA3_START|VT1724_PDMA4_START) +#define DMA_PAUSES (VT1724_RDMA0_PAUSE|VT1724_PDMA0_PAUSE|VT1724_RDMA1_PAUSE|\ + VT1724_PDMA1_PAUSE|VT1724_PDMA2_PAUSE|VT1724_PDMA3_PAUSE|VT1724_PDMA4_PAUSE) static void snd_vt1724_set_pro_rate(ice1712_t *ice, unsigned int rate, int force) { @@ -448,13 +429,52 @@ snd_pcm_hw_params_t * hw_params) { ice1712_t *ice = snd_pcm_substream_chip(substream); + int i, chs; + chs = params_channels(hw_params); + down(&ice->open_mutex); + /* mark surround channels */ + if (substream == ice->playback_pro_substream) { + chs = chs / 2 - 1; + for (i = 0; i < chs; i++) { + if (ice->pcm_reserved[i] && ice->pcm_reserved[i] != substream) { + up(&ice->open_mutex); + return -EBUSY; + } + ice->pcm_reserved[i] = substream; + } + for (; i < 3; i++) { + if (ice->pcm_reserved[i] == substream) + ice->pcm_reserved[i] = NULL; + } + } else { + for (i = 0; i < 3; i++) { + if (ice->playback_con_substream_ds[i] == substream) { + if (ice->pcm_reserved[i] && ice->pcm_reserved[i] != substream) { + up(&ice->open_mutex); + return -EBUSY; + } + ice->pcm_reserved[i] = substream; + break; + } + } + } + up(&ice->open_mutex); snd_vt1724_set_pro_rate(ice, params_rate(hw_params), 0); return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); } static int snd_vt1724_pcm_hw_free(snd_pcm_substream_t * substream) { + ice1712_t *ice = snd_pcm_substream_chip(substream); + int i; + + down(&ice->open_mutex); + /* unmark surround channels */ + for (i = 0; i < 3; i++) + if (ice->pcm_reserved[i] == substream) + ice->pcm_reserved[i] = NULL; + up(&ice->open_mutex); return snd_pcm_lib_free_pages(substream); } @@ -593,14 +613,14 @@ .rate_max = 192000, .channels_min = 2, .channels_max = 8, - .buffer_bytes_max = (1UL << 21), /* 18bits dword */ + .buffer_bytes_max = (1UL << 21), /* 19bits dword */ .period_bytes_min = 8 * 4 * 2, /* FIXME: constraints needed */ .period_bytes_max = (1UL << 21), - .periods_min = 1, + .periods_min = 2, .periods_max = 1024, }; -static snd_pcm_hardware_t snd_vt1724_capture_pro = +static snd_pcm_hardware_t snd_vt1724_2ch_stereo = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | @@ -612,10 +632,10 @@ .rate_max = 192000, .channels_min = 2, .channels_max = 2, - .buffer_bytes_max = (256*1024), + .buffer_bytes_max = (1UL << 18), /* 16bits dword */ .period_bytes_min = 2 * 4 * 2, - .period_bytes_max = (256*1024), - .periods_min = 1, + .period_bytes_max = (1UL << 18), + .periods_min = 2, .periods_max = 1024, }; @@ -628,7 +648,9 @@ { snd_pcm_runtime_t *runtime = substream->runtime; ice1712_t *ice = snd_pcm_substream_chip(substream); + int chs; + runtime->private_data = (void*)VT1724_PDMA0_START; /* irq/status/trigger bit */ ice->playback_pro_substream = substream; runtime->hw = snd_vt1724_playback_pro; snd_pcm_set_sync(substream); @@ -639,7 +661,17 @@ else snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_96); - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels); + down(&ice->open_mutex); + /* calculate the currently available channels */ + for (chs = 0; chs < 3; chs++) { + if (ice->pcm_reserved[chs]) + break; + } + chs = (chs + 1) * 2; + runtime->hw.channels_max = chs; + if (chs > 2) /* channels must be even */ + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 2); + up(&ice->open_mutex); snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, VT1724_BUFFER_ALIGN); snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, @@ -652,8 +684,9 @@ ice1712_t *ice = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; + runtime->private_data = (void*)VT1724_RDMA0_START; /* irq/status/trigger bit */ ice->capture_pro_substream = substream; - runtime->hw = snd_vt1724_capture_pro; + runtime->hw = snd_vt1724_2ch_stereo; snd_pcm_set_sync(substream); snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); if ((ice->eeprom.data[ICE_EEP2_ACLINK] & 0x80) && @@ -735,25 +768,6 @@ * SPDIF PCM */ -static snd_pcm_hardware_t snd_vt1724_playback_spdif = -{ - .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START), - .formats = SNDRV_PCM_FMTBIT_S32_LE, - .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_192000, - .rate_min = 4000, - .rate_max = 192000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = (256*1024), - .period_bytes_min = 2 * 4 * 2, - .period_bytes_max = (256*1024), - .periods_min = 1, - .periods_max = 1024, -}; - const static struct vt1724_pcm_reg vt1724_playback_spdif_reg = { .addr = VT1724_MT_PDMA4_ADDR, .size = VT1724_MT_PDMA4_SIZE, @@ -795,8 +809,9 @@ ice1712_t *ice = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; + runtime->private_data = (void*)VT1724_PDMA4_START; /* irq/status/trigger bit */ ice->playback_con_substream = substream; - runtime->hw = snd_vt1724_playback_spdif; + runtime->hw = snd_vt1724_2ch_stereo; snd_pcm_set_sync(substream); snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); @@ -820,8 +835,9 @@ ice1712_t *ice = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; + runtime->private_data = (void*)VT1724_RDMA1_START; /* irq/status/trigger bit */ ice->capture_con_substream = substream; - runtime->hw = snd_vt1724_playback_spdif; + runtime->hw = snd_vt1724_2ch_stereo; snd_pcm_set_sync(substream); snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_96); @@ -903,6 +919,127 @@ /* + * independent surround PCMs + */ + +const static struct vt1724_pcm_reg vt1724_playback_dma_regs[3] = { + { + .addr = VT1724_MT_PDMA1_ADDR, + .size = VT1724_MT_PDMA1_SIZE, + .count = VT1724_MT_PDMA1_COUNT, + .start = VT1724_PDMA1_START, + .pause = VT1724_PDMA1_PAUSE, + }, + { + .addr = VT1724_MT_PDMA2_ADDR, + .size = VT1724_MT_PDMA2_SIZE, + .count = VT1724_MT_PDMA2_COUNT, + .start = VT1724_PDMA2_START, + .pause = VT1724_PDMA2_PAUSE, + }, + { + .addr = VT1724_MT_PDMA3_ADDR, + .size = VT1724_MT_PDMA3_SIZE, + .count = VT1724_MT_PDMA3_COUNT, + .start = VT1724_PDMA3_START, + .pause = VT1724_PDMA3_PAUSE, + }, +}; + +static int snd_vt1724_playback_indep_prepare(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + unsigned char val; + + spin_lock(&ice->reg_lock); + val = 3 - substream->number; + if (inb(ICEMT1724(ice, BURST)) < val) + outb(val, ICEMT1724(ice, BURST)); + spin_unlock(&ice->reg_lock); + return snd_vt1724_pcm_prepare(substream, &vt1724_playback_dma_regs[substream->number]); +} + +static snd_pcm_uframes_t snd_vt1724_playback_indep_pointer(snd_pcm_substream_t * substream) +{ + return snd_vt1724_pcm_pointer(substream, &vt1724_playback_dma_regs[substream->number]); +} + +static int snd_vt1724_playback_indep_open(snd_pcm_substream_t *substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + down(&ice->open_mutex); + /* already used by PDMA0? */ + if (ice->pcm_reserved[substream->number]) { + up(&ice->open_mutex); + return -EBUSY; /* FIXME: should handle blocking mode properly */ + } + up(&ice->open_mutex); + runtime->private_data = (void*)(1 << (substream->number + 4)); + ice->playback_con_substream_ds[substream->number] = substream; + runtime->hw = snd_vt1724_2ch_stereo; + snd_pcm_set_sync(substream); + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_192); + return 0; +} + +static int snd_vt1724_playback_indep_close(snd_pcm_substream_t * substream) +{ + ice1712_t *ice = snd_pcm_substream_chip(substream); + + if (PRO_RATE_RESET) + snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0); + ice->playback_con_substream_ds[substream->number] = NULL; + ice->pcm_reserved[substream->number] = NULL; + + return 0; +} + +static snd_pcm_ops_t snd_vt1724_playback_indep_ops = { + .open = snd_vt1724_playback_indep_open, + .close = snd_vt1724_playback_indep_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_vt1724_pcm_hw_params, + .hw_free = snd_vt1724_pcm_hw_free, + .prepare = snd_vt1724_playback_indep_prepare, + .trigger = snd_vt1724_pcm_trigger, + .pointer = snd_vt1724_playback_indep_pointer, +}; + + +static int __devinit snd_vt1724_pcm_indep(ice1712_t * ice, int device) +{ + snd_pcm_t *pcm; + int play; + int err; + + play = ice->num_total_dacs / 2 - 1; + if (play <= 0) + return 0; + + err = snd_pcm_new(ice->card, "ICE1724 Surrounds", device, play, 0, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_vt1724_playback_indep_ops); + + pcm->private_data = ice; + pcm->info_flags = 0; + strcpy(pcm->name, "ICE1724 Surround PCM"); + + snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 64*1024); + + ice->pcm_ds = pcm; + + return 0; +} + + +/* * Mixer section */ @@ -1808,6 +1945,7 @@ ice->vt1724 = 1; spin_lock_init(&ice->reg_lock); init_MUTEX(&ice->gpio_mutex); + init_MUTEX(&ice->open_mutex); ice->gpio.set_mask = snd_vt1724_set_gpio_mask; ice->gpio.set_dir = snd_vt1724_set_gpio_dir; ice->gpio.set_data = snd_vt1724_set_gpio_data; @@ -1932,6 +2070,11 @@ return err; } + if ((err = snd_vt1724_pcm_indep(ice, pcm_dev++)) < 0) { + snd_card_free(card); + return err; + } + if ((err = snd_vt1724_ac97_mixer(ice)) < 0) { snd_card_free(card); return err; diff -Nru a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c --- a/sound/pci/intel8x0.c Sun Feb 29 12:58:39 2004 +++ b/sound/pci/intel8x0.c Sun Feb 29 12:58:39 2004 @@ -143,6 +143,9 @@ #ifndef PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO #define PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO 0x00da #endif +#ifndef PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO +#define PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO 0x00ea +#endif enum { DEVICE_INTEL, DEVICE_INTEL_ICH4, DEVICE_SIS, DEVICE_ALI, DEVICE_NFORCE }; @@ -443,6 +446,7 @@ { 0x10de, 0x01b1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE */ { 0x10de, 0x006a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE2 */ { 0x10de, 0x00da, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE3 */ + { 0x10de, 0x00ea, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* CK8S */ { 0x1022, 0x746d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD8111 */ { 0x1022, 0x7445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD768 */ { 0x10b9, 0x5455, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALI }, /* Ali5455 */ @@ -804,10 +808,19 @@ spin_lock(&chip->reg_lock); status = igetdword(chip, chip->int_sta_reg); if ((status & chip->int_sta_mask) == 0) { - if (status) + static int err_count = 10; + if (status) { + /* ack */ iputdword(chip, chip->int_sta_reg, status); + status ^= igetdword(chip, chip->int_sta_reg); + } spin_unlock(&chip->reg_lock); - return IRQ_NONE; + if (status && err_count) { + err_count--; + snd_printd("intel8x0: unknown IRQ bits 0x%x (sta_mask=0x%x)\n", + status, chip->int_sta_mask); + } + return IRQ_RETVAL(status); } for (i = 0; i < chip->bdbars_count; i++) { @@ -1017,6 +1030,7 @@ { intel8x0_t *chip = snd_pcm_substream_chip(substream); ichdev_t *ichdev = get_ichdev(substream); + unsigned long flags; size_t ptr1, ptr; ptr1 = igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << chip->pcm_pos_shift; @@ -1024,7 +1038,9 @@ ptr = ichdev->fragsize1 - ptr1; else ptr = 0; + spin_lock_irqsave(&chip->reg_lock, flags); ptr += ichdev->position; + spin_unlock_irqrestore(&chip->reg_lock, flags); if (ptr >= ichdev->size) return 0; return bytes_to_frames(substream->runtime, ptr); @@ -1079,21 +1095,12 @@ { intel8x0_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - static unsigned int i, rates[] = { - /* ATTENTION: these values depend on the definition in pcm.h! */ - 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000 - }; int err; ichdev->substream = substream; runtime->hw = snd_intel8x0_stream; runtime->hw.rates = ichdev->pcm->rates; - for (i = 0; i < ARRAY_SIZE(rates); i++) { - if (runtime->hw.rates & (1 << i)) { - runtime->hw.rate_min = rates[i]; - break; - } - } + snd_pcm_limit_hw_rates(runtime); if (chip->device_type == DEVICE_SIS) { runtime->hw.buffer_bytes_max = 64*1024; runtime->hw.period_bytes_max = 64*1024; @@ -1741,6 +1748,12 @@ }, { .vendor = 0x8086, + .device = 0x4856, + .name = "Intel D845WN (82801BA)", + .type = AC97_TUNE_SWAP_HP + }, + { + .vendor = 0x8086, .device = 0x4d44, .name = "Intel D850EMV2", /* AD1885 */ .type = AC97_TUNE_HP_ONLY @@ -2567,6 +2580,7 @@ { PCI_DEVICE_ID_NVIDIA_MCP_AUDIO, "NVidia nForce" }, { PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO, "NVidia nForce2" }, { PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO, "NVidia nForce3" }, + { PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO, "NVidia CK8S" }, { 0x746d, "AMD AMD8111" }, { 0x7445, "AMD AMD768" }, { 0x5455, "ALi M5455" }, diff -Nru a/sound/pci/mixart/Makefile b/sound/pci/mixart/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/mixart/Makefile Sun Feb 29 12:58:39 2004 @@ -0,0 +1,8 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela +# + +snd-mixart-objs := mixart.o mixart_core.o mixart_hwdep.o mixart_mixer.o + +obj-$(CONFIG_SND_MIXART) += snd-mixart.o diff -Nru a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/mixart/mixart.c Sun Feb 29 12:58:39 2004 @@ -0,0 +1,1461 @@ +/* + * Driver for Digigram miXart soundcards + * + * main file with alsa callbacks + * + * Copyright (c) 2003 by Digigram + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#define SNDRV_GET_ID +#include +#include +#include +#include +#include +#include "mixart.h" +#include "mixart_hwdep.h" +#include "mixart_core.h" +#include "mixart_mixer.h" + +#define CARD_NAME "miXart" + +MODULE_AUTHOR("Digigram "); +MODULE_DESCRIPTION("Digigram " CARD_NAME); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Digigram," CARD_NAME "}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ + +#define chip_t mixart_t + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for Digigram " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for Digigram " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable Digigram " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); + +/* + */ + +static struct pci_device_id snd_mixart_ids[] = { + { 0x1057, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* MC8240 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_mixart_ids); + + +static int mixart_set_pipe_state(mixart_mgr_t *mgr, mixart_pipe_t* pipe, int start) +{ + mixart_group_state_req_t group_state; + mixart_group_state_resp_t group_state_resp; + mixart_msg_t request; + int err; + u32 system_msg_uid; + + switch(pipe->status) { + case PIPE_RUNNING: + case PIPE_CLOCK_SET: + if(start) return 0; /* already started */ + break; + case PIPE_STOPPED: + if(!start) return 0; /* already stopped */ + break; + default: + snd_printk(KERN_ERR "error mixart_set_pipe_state called with wrong pipe->status!\n"); + return -EINVAL; /* function called with wrong pipe status */ + } + + system_msg_uid = 0x12345678; /* the event ! (take care: the MSB and two LSB's have to be 0) */ + + /* wait on the last MSG_SYSTEM_SEND_SYNCHRO_CMD command to be really finished */ + + request.message_id = MSG_SYSTEM_WAIT_SYNCHRO_CMD; + request.uid = (mixart_uid_t){0,0}; + request.data = &system_msg_uid; + request.size = sizeof(system_msg_uid); + + err = snd_mixart_send_msg_wait_notif(mgr, &request, system_msg_uid); + if(err) { + snd_printk(KERN_ERR "error : MSG_SYSTEM_WAIT_SYNCHRO_CMD was not notified !\n"); + return err; + } + + /* start or stop the pipe (1 pipe) */ + + memset(&group_state, 0, sizeof(group_state)); + group_state.pipe_count = 1; + group_state.pipe_uid[0] = pipe->group_uid; + + if(start) + request.message_id = MSG_STREAM_START_STREAM_GRP_PACKET; + else + request.message_id = MSG_STREAM_STOP_STREAM_GRP_PACKET; + + request.uid = pipe->group_uid; /*(mixart_uid_t){0,0};*/ + request.data = &group_state; + request.size = sizeof(group_state); + + err = snd_mixart_send_msg(mgr, &request, sizeof(group_state_resp), &group_state_resp); + if (err < 0 || group_state_resp.txx_status != 0) { + snd_printk(KERN_ERR "error MSG_STREAM_ST***_STREAM_GRP_PACKET err=%x stat=%x !\n", err, group_state_resp.txx_status); + return -EINVAL; + } + + if(start) { + u32 stat; + + group_state.pipe_count = 0; /* in case of start same command once again with pipe_count=0 */ + + err = snd_mixart_send_msg(mgr, &request, sizeof(group_state_resp), &group_state_resp); + if (err < 0 || group_state_resp.txx_status != 0) { + snd_printk(KERN_ERR "error MSG_STREAM_START_STREAM_GRP_PACKET err=%x stat=%x !\n", err, group_state_resp.txx_status); + return -EINVAL; + } + + /* in case of start send a synchro top */ + + request.message_id = MSG_SYSTEM_SEND_SYNCHRO_CMD; + request.uid = (mixart_uid_t){0,0}; + request.data = NULL; + request.size = 0; + + err = snd_mixart_send_msg(mgr, &request, sizeof(stat), &stat); + if (err < 0 || stat != 0) { + snd_printk(KERN_ERR "error MSG_SYSTEM_SEND_SYNCHRO_CMD err=%x stat=%x !\n", err, stat); + return -EINVAL; + } + + pipe->status = PIPE_RUNNING; + } + else /* !start */ + pipe->status = PIPE_STOPPED; + + return 0; +} + + +static int mixart_set_clock(mixart_mgr_t *mgr, mixart_pipe_t *pipe, unsigned int rate) +{ + mixart_msg_t request; + mixart_clock_properties_t clock_properties; + mixart_clock_properties_resp_t clock_prop_resp; + int err; + + switch(pipe->status) { + case PIPE_CLOCK_SET: + break; + case PIPE_RUNNING: + if(rate != 0) + break; + default: + if(rate == 0) + return 0; /* nothing to do */ + else { + snd_printk(KERN_ERR "error mixart_set_clock(%d) called with wrong pipe->status !\n", rate); + return -EINVAL; + } + } + + memset(&clock_properties, 0, sizeof(clock_properties)); + clock_properties.clock_generic_type = (rate != 0) ? CGT_INTERNAL_CLOCK : CGT_NO_CLOCK; + clock_properties.clock_mode = CM_STANDALONE; + clock_properties.frequency = rate; + clock_properties.nb_callers = 1; /* only one entry in uid_caller ! */ + clock_properties.uid_caller[0] = pipe->group_uid; + + snd_printdd("mixart_set_clock to %d kHz\n", rate); + + request.message_id = MSG_CLOCK_SET_PROPERTIES; + request.uid = mgr->uid_console_manager; + request.data = &clock_properties; + request.size = sizeof(clock_properties); + + err = snd_mixart_send_msg(mgr, &request, sizeof(clock_prop_resp), &clock_prop_resp); + if (err < 0 || clock_prop_resp.status != 0 || clock_prop_resp.clock_mode != CM_STANDALONE) { + snd_printk(KERN_ERR "error MSG_CLOCK_SET_PROPERTIES err=%x stat=%x mod=%x !\n", err, clock_prop_resp.status, clock_prop_resp.clock_mode); + return -EINVAL; + } + + if(rate) pipe->status = PIPE_CLOCK_SET; + else pipe->status = PIPE_RUNNING; + + return 0; +} + + +/* + * Allocate or reference output pipe for analog IOs (pcmp0/1) + */ +mixart_pipe_t* snd_mixart_add_ref_pipe( mixart_t *chip, int pcm_number, int capture, int monitoring) +{ + int stream_count; + mixart_pipe_t *pipe; + mixart_msg_t request; + + if(capture) { + if (pcm_number == MIXART_PCM_ANALOG) { + pipe = &(chip->pipe_in_ana); /* analog inputs */ + } else { + pipe = &(chip->pipe_in_dig); /* digital inputs */ + } + request.message_id = MSG_STREAM_ADD_OUTPUT_GROUP; + stream_count = MIXART_CAPTURE_STREAMS; + } else { + if (pcm_number == MIXART_PCM_ANALOG) { + pipe = &(chip->pipe_out_ana); /* analog outputs */ + } else { + pipe = &(chip->pipe_out_dig); /* digital outputs */ + } + request.message_id = MSG_STREAM_ADD_INPUT_GROUP; + stream_count = MIXART_PLAYBACK_STREAMS; + } + + /* a new stream is opened and there are already all streams in use */ + if( (monitoring == 0) && (pipe->references >= stream_count) ) { + return NULL; + } + + /* pipe is not yet defined */ + if( pipe->status == PIPE_UNDEFINED ) { + int err, i; + mixart_streaming_group_t streaming_group_resp; + mixart_streaming_group_req_t streaming_group_req; + + snd_printdd("add_ref_pipe audio chip(%d) pcm(%d)\n", chip->chip_idx, pcm_number); + + request.uid = (mixart_uid_t){0,0}; /* should be StreamManagerUID, but zero is OK if there is only one ! */ + request.data = &streaming_group_req; + request.size = sizeof(streaming_group_req); + + memset(&streaming_group_req, 0, sizeof(streaming_group_req)); + + streaming_group_req.stream_count = stream_count; + streaming_group_req.channel_count = 2; + streaming_group_req.latency = 256; + streaming_group_req.connector = pipe->uid_left_connector; /* the left connector */ + + for (i=0; ichip_idx * MIXART_MAX_STREAM_PER_CARD) + (pcm_number * (MIXART_PLAYBACK_STREAMS + MIXART_CAPTURE_STREAMS)) + i; + if(capture) j += MIXART_PLAYBACK_STREAMS; /* in the array capture is behind playback */ + + streaming_group_req.flow_entry[i] = j; + + chip->mgr->flowinfo_array[j].bufferinfo_array_phy_address = (u32)chip->mgr->bufferinfo_physaddr + (j * sizeof(mixart_bufferinfo_t)); + chip->mgr->flowinfo_array[j].bufferinfo_count = 1; /* 1 will set the miXart to ring-buffer mode ! */ + + chip->mgr->bufferinfo_array[j].buffer_address = 0; /* buffer is not yet allocated */ + chip->mgr->bufferinfo_array[j].available_length = 0; /* buffer is not yet allocated */ + + /* construct the identifier of the stream buffer received in the interrupts ! */ + chip->mgr->bufferinfo_array[j].buffer_id = (chip->chip_idx << MIXART_NOTIFY_CARD_OFFSET) + (pcm_number << MIXART_NOTIFY_PCM_OFFSET ) + i; + if(capture) { + chip->mgr->bufferinfo_array[j].buffer_id |= MIXART_NOTIFY_CAPT_MASK; + } + } + + err = snd_mixart_send_msg(chip->mgr, &request, sizeof(streaming_group_resp), &streaming_group_resp); + if((err < 0) || (streaming_group_resp.status != 0)) { + snd_printk(KERN_ERR "error MSG_STREAM_ADD_**PUT_GROUP err=%x stat=%x !\n", err, streaming_group_resp.status); + return NULL; + } + + pipe->group_uid = streaming_group_resp.group; /* id of the pipe, as returned by embedded */ + pipe->stream_count = streaming_group_resp.stream_count; + /* pipe->stream_uid[i] = streaming_group_resp.stream[i].stream_uid; */ + + pipe->status = PIPE_STOPPED; + } + + if(monitoring) pipe->monitoring = 1; + else pipe->references++; + + return pipe; +} + + +int snd_mixart_kill_ref_pipe( mixart_mgr_t *mgr, mixart_pipe_t *pipe, int monitoring) +{ + int err = 0; + + if(pipe->status == PIPE_UNDEFINED) + return 0; + + if(monitoring) + pipe->monitoring = 0; + else + pipe->references--; + + if((pipe->references <= 0) && (pipe->monitoring == 0)) { + + mixart_msg_t request; + mixart_delete_group_resp_t delete_resp; + + /* release the clock */ + err = mixart_set_clock( mgr, pipe, 0); + if( err < 0 ) { + snd_printk(KERN_ERR "mixart_set_clock(0) return error!\n"); + } + + /* stop the pipe */ + err = mixart_set_pipe_state(mgr, pipe, 0); + if( err < 0 ) { + snd_printk(KERN_ERR "error stopping pipe!\n"); + } + + request.message_id = MSG_STREAM_DELETE_GROUP; + request.uid = (mixart_uid_t){0,0}; + request.data = &pipe->group_uid; /* the streaming group ! */ + request.size = sizeof(pipe->group_uid); + + /* delete the pipe */ + err = snd_mixart_send_msg(mgr, &request, sizeof(delete_resp), &delete_resp); + if ((err < 0) || (delete_resp.status != 0)) { + snd_printk(KERN_ERR "error MSG_STREAM_DELETE_GROUP err(%x), status(%x)\n", err, delete_resp.status); + } + + pipe->group_uid = (mixart_uid_t){0,0}; + pipe->stream_count = 0; + pipe->status = PIPE_UNDEFINED; + } + + return err; +} + +static int mixart_set_stream_state(mixart_stream_t *stream, int start) +{ + mixart_t *chip; + mixart_stream_state_req_t stream_state_req; + mixart_msg_t request; + + if(!stream->substream) + return -EINVAL; + + memset(&stream_state_req, 0, sizeof(stream_state_req)); + stream_state_req.stream_count = 1; + stream_state_req.stream_info.stream_desc.uid_pipe = stream->pipe->group_uid; + stream_state_req.stream_info.stream_desc.stream_idx = stream->substream->number; + + if (stream->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + request.message_id = start ? MSG_STREAM_START_INPUT_STAGE_PACKET : MSG_STREAM_STOP_INPUT_STAGE_PACKET; + else + request.message_id = start ? MSG_STREAM_START_OUTPUT_STAGE_PACKET : MSG_STREAM_STOP_OUTPUT_STAGE_PACKET; + + request.uid = (mixart_uid_t){0,0}; + request.data = &stream_state_req; + request.size = sizeof(stream_state_req); + + stream->abs_period_elapsed = 0; /* reset stream pos */ + stream->buf_periods = 0; + stream->buf_period_frag = 0; + + chip = snd_pcm_substream_chip(stream->substream); + + return snd_mixart_send_msg_nonblock(chip->mgr, &request); +} + +/* + * Trigger callback + */ + +static int snd_mixart_trigger(snd_pcm_substream_t *subs, int cmd) +{ + mixart_stream_t *stream = (mixart_stream_t*)subs->runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + + snd_printdd("SNDRV_PCM_TRIGGER_START\n"); + + /* START_STREAM */ + if( mixart_set_stream_state(stream, 1) ) + return -EINVAL; + + stream->status = MIXART_STREAM_STATUS_RUNNING; + + break; + case SNDRV_PCM_TRIGGER_STOP: + + /* STOP_STREAM */ + if( mixart_set_stream_state(stream, 0) ) + return -EINVAL; + + stream->status = MIXART_STREAM_STATUS_OPEN; + + snd_printdd("SNDRV_PCM_TRIGGER_STOP\n"); + + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + /* TODO */ + stream->status = MIXART_STREAM_STATUS_PAUSE; + snd_printdd("SNDRV_PCM_PAUSE_PUSH\n"); + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + /* TODO */ + stream->status = MIXART_STREAM_STATUS_RUNNING; + snd_printdd("SNDRV_PCM_PAUSE_RELEASE\n"); + break; + default: + return -EINVAL; + } + return 0; +} + +static int mixart_sync_nonblock_events(mixart_mgr_t *mgr) +{ + int timeout = HZ; + while (atomic_read(&mgr->msg_processed) > 0) { + if (! timeout--) { + snd_printk(KERN_ERR "mixart: cannot process nonblock events!\n"); + return -EBUSY; + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + return 0; +} + +/* + * prepare callback for all pcms + * + * NOTE: this callback is non-atomic (pcm->info_flags |= SNDRV_PCM_INFO_NONATOMIC_OPS) + */ +static int snd_mixart_prepare(snd_pcm_substream_t *subs) +{ + mixart_t *chip = snd_pcm_substream_chip(subs); + mixart_stream_t *stream = (mixart_stream_t*)subs->runtime->private_data; + + /* TODO de façon non bloquante, réappliquer les hw_params (rate, bits, codec) */ + + snd_printdd("snd_mixart_prepare\n"); + + mixart_sync_nonblock_events(chip->mgr); + + /* only the first stream can choose the sample rate */ + /* the further opened streams will be limited to its frequency (see open) */ + if(chip->mgr->ref_count_rate == 1) + chip->mgr->sample_rate = subs->runtime->rate; + + /* set the clock only once (first stream) on the same pipe */ + if(stream->pipe->references == 1) { + if( mixart_set_clock(chip->mgr, stream->pipe, subs->runtime->rate) ) + return -EINVAL; + } + + return 0; +} + + +static int mixart_set_format(mixart_stream_t *stream, snd_pcm_format_t format) +{ + int err; + mixart_t *chip; + mixart_msg_t request; + mixart_stream_param_desc_t stream_param; + mixart_return_uid_t resp; + + chip = snd_pcm_substream_chip(stream->substream); + + memset(&stream_param, 0, sizeof(stream_param)); + + stream_param.coding_type = CT_LINEAR; + stream_param.number_of_channel = stream->channels; + + stream_param.sampling_freq = chip->mgr->sample_rate; + if(stream_param.sampling_freq == 0) + stream_param.sampling_freq = 44100; /* if frequency not yet defined, use some default */ + + switch(format){ + case SNDRV_PCM_FORMAT_U8: + stream_param.sample_type = ST_INTEGER_8; + stream_param.sample_size = 8; + break; + case SNDRV_PCM_FORMAT_S16_LE: + stream_param.sample_type = ST_INTEGER_16LE; + stream_param.sample_size = 16; + break; + case SNDRV_PCM_FORMAT_S16_BE: + stream_param.sample_type = ST_INTEGER_16BE; + stream_param.sample_size = 16; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + stream_param.sample_type = ST_INTEGER_24LE; + stream_param.sample_size = 24; + break; + case SNDRV_PCM_FORMAT_S24_3BE: + stream_param.sample_type = ST_INTEGER_24BE; + stream_param.sample_size = 24; + break; + case SNDRV_PCM_FMTBIT_FLOAT_LE: + stream_param.sample_type = ST_FLOATING_POINT_32LE; + stream_param.sample_size = 32; + break; + case SNDRV_PCM_FMTBIT_FLOAT_BE: + stream_param.sample_type = ST_FLOATING_POINT_32BE; + stream_param.sample_size = 32; + break; + default: + snd_printk(KERN_ERR "error mixart_set_format() : unknown format\n"); + return -EINVAL; + } + + snd_printdd("set SNDRV_PCM_FORMAT sample_type(%d) sample_size(%d) freq(%d) channels(%d)\n", + stream_param.sample_type, stream_param.sample_size, stream_param.sampling_freq, stream->channels); + + /* TODO: what else to configure ? */ + /* stream_param.samples_per_frame = 2; */ + /* stream_param.bytes_per_frame = 4; */ + /* stream_param.bytes_per_sample = 2; */ + + stream_param.pipe_count = 1; /* set to 1 */ + stream_param.stream_count = 1; /* set to 1 */ + stream_param.stream_desc[0].uid_pipe = stream->pipe->group_uid; + stream_param.stream_desc[0].stream_idx = stream->substream->number; + + request.message_id = MSG_STREAM_SET_INPUT_STAGE_PARAM; + request.uid = (mixart_uid_t){0,0}; + request.data = &stream_param; + request.size = sizeof(stream_param); + + err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp); + if((err < 0) || resp.error_code) { + snd_printk(KERN_ERR "MSG_STREAM_SET_INPUT_STAGE_PARAM err=%x; resp=%x\n", err, resp.error_code); + return -EINVAL; + } + return 0; +} + + +/* + * HW_PARAMS callback for all pcms + */ +static int snd_mixart_hw_params(snd_pcm_substream_t *subs, + snd_pcm_hw_params_t *hw) +{ + mixart_t *chip = snd_pcm_substream_chip(subs); + mixart_mgr_t *mgr = chip->mgr; + mixart_stream_t *stream = (mixart_stream_t*)subs->runtime->private_data; + snd_pcm_format_t format; + int err; + int channels; + + /* set up channels */ + channels = params_channels(hw); + + /* set up format for the stream */ + format = params_format(hw); + + down(&mgr->setup_mutex); + + /* update the stream levels */ + if( stream->pcm_number <= MIXART_PCM_DIGITAL ) { + int is_aes = stream->pcm_number > MIXART_PCM_ANALOG; + if( subs->stream == SNDRV_PCM_STREAM_PLAYBACK ) + mixart_update_playback_stream_level(chip, is_aes, subs->number); + else + mixart_update_capture_stream_level( chip, is_aes); + } + + stream->channels = channels; + + /* set the format to the board */ + err = mixart_set_format(stream, format); + if(err < 0) { + return err; + } + + /* allocate buffer */ + err = snd_pcm_lib_malloc_pages(subs, params_buffer_bytes(hw)); + + if (err > 0) { + int i = (chip->chip_idx * MIXART_MAX_STREAM_PER_CARD) + (stream->pcm_number * (MIXART_PLAYBACK_STREAMS+MIXART_CAPTURE_STREAMS)) + subs->number; + if( subs->stream == SNDRV_PCM_STREAM_CAPTURE ) { + i += MIXART_PLAYBACK_STREAMS; /* in array capture is behind playback */ + } + mgr->bufferinfo_array[i].buffer_address = subs->runtime->dma_addr; + mgr->bufferinfo_array[i].available_length = subs->runtime->dma_bytes; + /* mgr->bufferinfo_array[i].buffer_id is already defined */ + + snd_printdd("snd_mixart_hw_params(pcm %d) : dma_addr(%x) dma_bytes(%x) subs-number(%d)\n", i, subs->runtime->dma_addr, subs->runtime->dma_bytes, subs->number); + } + up(&mgr->setup_mutex); + + return err; +} + +static int snd_mixart_hw_free(snd_pcm_substream_t *subs) +{ + mixart_t *chip = snd_pcm_substream_chip(subs); + snd_pcm_lib_free_pages(subs); + mixart_sync_nonblock_events(chip->mgr); + return 0; +} + + + +/* + * TODO CONFIGURATION SPACE for all pcms, mono pcm must update channels_max + */ +static snd_pcm_hardware_t snd_mixart_analog_caps = +{ + .info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_PAUSE), + .formats = ( SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | + SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE ), + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (32*1024), + .period_bytes_min = 256, /* 256 frames U8 mono*/ + .period_bytes_max = (16*1024), + .periods_min = 2, + .periods_max = (32*1024/256), +}; + +static snd_pcm_hardware_t snd_mixart_digital_caps = +{ + .info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_PAUSE), + .formats = ( SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | + SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE ), + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + .rate_min = 32000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (32*1024), + .period_bytes_min = 256, /* 256 frames U8 mono*/ + .period_bytes_max = (16*1024), + .periods_min = 2, + .periods_max = (32*1024/256), +}; + + +static int snd_mixart_playback_open(snd_pcm_substream_t *subs) +{ + mixart_t *chip = snd_pcm_substream_chip(subs); + mixart_mgr_t *mgr = chip->mgr; + snd_pcm_runtime_t *runtime = subs->runtime; + snd_pcm_t *pcm = subs->pcm; + mixart_stream_t *stream; + mixart_pipe_t *pipe; + int err = 0; + int pcm_number; + + down(&mgr->setup_mutex); + + if ( pcm == chip->pcm ) { + pcm_number = MIXART_PCM_ANALOG; + runtime->hw = snd_mixart_analog_caps; + } else { + snd_assert ( pcm == chip->pcm_dig ); + pcm_number = MIXART_PCM_DIGITAL; + runtime->hw = snd_mixart_digital_caps; + } + snd_printdd("snd_mixart_playback_open C%d/P%d/Sub%d\n", chip->chip_idx, pcm_number, subs->number); + + /* get stream info */ + stream = &(chip->playback_stream[pcm_number][subs->number]); + + if (stream->status != MIXART_STREAM_STATUS_FREE){ + /* streams in use */ + snd_printk(KERN_ERR "snd_mixart_playback_open C%d/P%d/Sub%d in use\n", chip->chip_idx, pcm_number, subs->number); + err = -EBUSY; + goto _exit_open; + } + + /* get pipe pointer (out pipe) */ + pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 0, 0); + + if (pipe == NULL) { + err = -EINVAL; + goto _exit_open; + } + + /* start the pipe if necessary */ + err = mixart_set_pipe_state(chip->mgr, pipe, 1); + if( err < 0 ) { + snd_printk(KERN_ERR "error starting pipe!\n"); + snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0); + err = -EINVAL; + goto _exit_open; + } + + stream->pipe = pipe; + stream->pcm_number = pcm_number; + stream->status = MIXART_STREAM_STATUS_OPEN; + stream->substream = subs; + stream->channels = 0; /* not configured yet */ + + runtime->private_data = stream; + + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 64); + + /* if a sample rate is already used, another stream cannot change */ + if(mgr->ref_count_rate++) { + if(mgr->sample_rate) { + runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate; + } + } + + _exit_open: + up(&mgr->setup_mutex); + + return err; +} + + +static int snd_mixart_capture_open(snd_pcm_substream_t *subs) +{ + mixart_t *chip = snd_pcm_substream_chip(subs); + mixart_mgr_t *mgr = chip->mgr; + snd_pcm_runtime_t *runtime = subs->runtime; + snd_pcm_t *pcm = subs->pcm; + mixart_stream_t *stream; + mixart_pipe_t *pipe; + int err = 0; + int pcm_number; + + down(&mgr->setup_mutex); + + if ( pcm == chip->pcm ) { + pcm_number = MIXART_PCM_ANALOG; + runtime->hw = snd_mixart_analog_caps; + } else { + snd_assert ( pcm == chip->pcm_dig ); + pcm_number = MIXART_PCM_DIGITAL; + runtime->hw = snd_mixart_digital_caps; + } + + runtime->hw.channels_min = 2; /* for instance, no mono */ + + snd_printdd("snd_mixart_capture_open C%d/P%d/Sub%d\n", chip->chip_idx, pcm_number, subs->number); + + /* get stream info */ + stream = &(chip->capture_stream[pcm_number]); + + if (stream->status != MIXART_STREAM_STATUS_FREE){ + /* streams in use */ + snd_printk(KERN_ERR "snd_mixart_capture_open C%d/P%d/Sub%d in use\n", chip->chip_idx, pcm_number, subs->number); + err = -EBUSY; + goto _exit_open; + } + + /* get pipe pointer (in pipe) */ + pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 1, 0); + + if (pipe == NULL) { + err = -EINVAL; + goto _exit_open; + } + + /* start the pipe if necessary */ + err = mixart_set_pipe_state(chip->mgr, pipe, 1); + if( err < 0 ) { + snd_printk(KERN_ERR "error starting pipe!\n"); + snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0); + err = -EINVAL; + goto _exit_open; + } + + stream->pipe = pipe; + stream->pcm_number = pcm_number; + stream->status = MIXART_STREAM_STATUS_OPEN; + stream->substream = subs; + stream->channels = 0; /* not configured yet */ + + runtime->private_data = stream; + + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 64); + + /* if a sample rate is already used, another stream cannot change */ + if(mgr->ref_count_rate++) { + if(mgr->sample_rate) { + runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate; + } + } + + _exit_open: + up(&mgr->setup_mutex); + + return err; +} + + + +static int snd_mixart_close(snd_pcm_substream_t *subs) +{ + mixart_t *chip = snd_pcm_substream_chip(subs); + mixart_mgr_t *mgr = chip->mgr; + mixart_stream_t *stream = (mixart_stream_t*)subs->runtime->private_data; + + down(&mgr->setup_mutex); + + snd_printdd("snd_mixart_close C%d/P%d/Sub%d\n", chip->chip_idx, stream->pcm_number, subs->number); + + /* sample rate released */ + if(--mgr->ref_count_rate == 0) { + mgr->sample_rate = 0; + } + + /* delete pipe */ + if (snd_mixart_kill_ref_pipe(mgr, stream->pipe, 0 ) < 0) { + + snd_printk(KERN_ERR "error snd_mixart_kill_ref_pipe C%dP%d\n", chip->chip_idx, stream->pcm_number); + } + + stream->pipe = NULL; + stream->status = MIXART_STREAM_STATUS_FREE; + stream->substream = NULL; + + up(&mgr->setup_mutex); + return 0; +} + + +static snd_pcm_uframes_t snd_mixart_stream_pointer(snd_pcm_substream_t * subs) +{ + snd_pcm_runtime_t *runtime = subs->runtime; + mixart_stream_t *stream = (mixart_stream_t*)runtime->private_data; + + return (snd_pcm_uframes_t)((stream->buf_periods * runtime->period_size) + stream->buf_period_frag); +} + + + +static snd_pcm_ops_t snd_mixart_playback_ops = { + .open = snd_mixart_playback_open, + .close = snd_mixart_close, + .ioctl = snd_pcm_lib_ioctl, + .prepare = snd_mixart_prepare, + .hw_params = snd_mixart_hw_params, + .hw_free = snd_mixart_hw_free, + .trigger = snd_mixart_trigger, + .pointer = snd_mixart_stream_pointer, +}; + +static snd_pcm_ops_t snd_mixart_capture_ops = { + .open = snd_mixart_capture_open, + .close = snd_mixart_close, + .ioctl = snd_pcm_lib_ioctl, + .prepare = snd_mixart_prepare, + .hw_params = snd_mixart_hw_params, + .hw_free = snd_mixart_hw_free, + .trigger = snd_mixart_trigger, + .pointer = snd_mixart_stream_pointer, +}; + +static void preallocate_buffers(mixart_t *chip, snd_pcm_t *pcm) +{ + snd_pcm_substream_t *subs; + int stream; + + for (stream = 0; stream < 2; stream++) { + int idx = 0; + for (subs = pcm->streams[stream].substream; subs; subs = subs->next, idx++) + /* set up the unique device id with the chip index */ + subs->dma_device.id = subs->pcm->device << 16 | + subs->stream << 8 | (subs->number + 1) | + (chip->chip_idx + 1) << 24; + } + snd_pcm_lib_preallocate_pci_pages_for_all(chip->mgr->pci, pcm, 32*1024, 32*1024); +} + +/* + */ +static int snd_mixart_pcm_analog(mixart_t *chip) +{ + int err; + snd_pcm_t *pcm; + char name[32]; + + sprintf(name, "miXart analog %d", chip->chip_idx); + if ((err = snd_pcm_new(chip->card, name, MIXART_PCM_ANALOG, + MIXART_PLAYBACK_STREAMS, + MIXART_CAPTURE_STREAMS, &pcm)) < 0) { + snd_printk(KERN_ERR "cannot create the analog pcm %d\n", chip->chip_idx); + return err; + } + + pcm->private_data = chip; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mixart_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops); + + pcm->info_flags = SNDRV_PCM_INFO_NONATOMIC_OPS; + strcpy(pcm->name, name); + + preallocate_buffers(chip, pcm); + + chip->pcm = pcm; + return 0; +} + + +/* + */ +static int snd_mixart_pcm_digital(mixart_t *chip) +{ + int err; + snd_pcm_t *pcm; + char name[32]; + + sprintf(name, "miXart AES/EBU %d", chip->chip_idx); + if ((err = snd_pcm_new(chip->card, name, MIXART_PCM_DIGITAL, + MIXART_PLAYBACK_STREAMS, + MIXART_CAPTURE_STREAMS, &pcm)) < 0) { + snd_printk(KERN_ERR "cannot create the digital pcm %d\n", chip->chip_idx); + return err; + } + + pcm->private_data = chip; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mixart_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops); + + pcm->info_flags = SNDRV_PCM_INFO_NONATOMIC_OPS; + strcpy(pcm->name, name); + + preallocate_buffers(chip, pcm); + + chip->pcm_dig = pcm; + return 0; +} + +static int snd_mixart_chip_free(mixart_t *chip) +{ + snd_magic_kfree(chip); + return 0; +} + +static int snd_mixart_chip_dev_free(snd_device_t *device) +{ + mixart_t *chip = snd_magic_cast(mixart_t, device->device_data, return -ENXIO); + return snd_mixart_chip_free(chip); +} + + +/* + */ +static int __devinit snd_mixart_create(mixart_mgr_t *mgr, snd_card_t *card, int idx) +{ + int err; + mixart_t *chip; + static snd_device_ops_t ops = { + .dev_free = snd_mixart_chip_dev_free, + }; + + mgr->chip[idx] = chip = snd_magic_kcalloc(mixart_t, 0, GFP_KERNEL); + if (! chip) { + snd_printk(KERN_ERR "cannot allocate chip\n"); + return -ENOMEM; + } + + chip->card = card; + chip->chip_idx = idx; + chip->mgr = mgr; + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_mixart_chip_free(chip); + return err; + } + + if (idx == 0) { + /* create a DSP loader only on first cardX*/ + err = snd_mixart_hwdep_new(mgr); + if (err < 0) + return err; + } + + snd_card_set_dev(card, &mgr->pci->dev); + + return 0; +} + +int snd_mixart_create_pcm(mixart_t* chip) +{ + int err; + + err = snd_mixart_pcm_analog(chip); + if (err < 0) + return err; + + if(chip->mgr->board_type == MIXART_DAUGHTER_TYPE_AES) { + + err = snd_mixart_pcm_digital(chip); + if (err < 0) + return err; + } + return err; +} + + +/* + * release all the cards assigned to a manager instance + */ +static int snd_mixart_free(mixart_mgr_t *mgr) +{ + unsigned int i; + + for (i = 0; i < mgr->num_cards; i++) { + if (mgr->chip[i]) + snd_card_free(mgr->chip[i]->card); + } + + /* stop mailbox */ + snd_mixart_exit_mailbox(mgr); + + /* release irq */ + if (mgr->irq >= 0) + free_irq(mgr->irq, (void *)mgr); + + /* reset board if some firmware was loaded */ + if(mgr->hwdep->dsp_loaded) { + snd_mixart_reset_board(mgr); + snd_printdd("reset miXart !\n"); + } + + /* release the i/o ports */ + for (i = 0; i < 2; i++) { + if (mgr->mem[i].virt) + iounmap((void *)mgr->mem[i].virt); + if (mgr->mem[i].res) { + release_resource(mgr->mem[i].res); + kfree_nocheck(mgr->mem[i].res); + } + } + + /* free flowarray */ + if(mgr->flowinfo_array) { + snd_free_pci_pages(mgr->pci, PAGE_ALIGN(MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS * sizeof(mixart_flowinfo_t)), + mgr->flowinfo_array, mgr->flowinfo_physaddr); + } + /* free bufferarray */ + if(mgr->bufferinfo_array) { + snd_free_pci_pages(mgr->pci, PAGE_ALIGN(MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS * sizeof(mixart_bufferinfo_t)), + mgr->bufferinfo_array, mgr->bufferinfo_physaddr); + } + + snd_magic_kfree(mgr); + return 0; +} + +/* + * proc interface + */ +static long long snd_mixart_BA0_llseek(snd_info_entry_t *entry, + void *private_file_data, + struct file *file, + long long offset, + int orig) +{ + offset = offset & ~3; /* 4 bytes aligned */ + + switch(orig) { + case 0: /* SEEK_SET */ + file->f_pos = offset; + break; + case 1: /* SEEK_CUR */ + file->f_pos += offset; + break; + case 2: /* SEEK_END, offset is negative */ + file->f_pos = MIXART_BA0_SIZE + offset; + break; + default: + return -EINVAL; + } + if(file->f_pos > MIXART_BA0_SIZE) + file->f_pos = MIXART_BA0_SIZE; + return file->f_pos; +} + +static long long snd_mixart_BA1_llseek(snd_info_entry_t *entry, + void *private_file_data, + struct file *file, + long long offset, + int orig) +{ + offset = offset & ~3; /* 4 bytes aligned */ + + switch(orig) { + case 0: /* SEEK_SET */ + file->f_pos = offset; + break; + case 1: /* SEEK_CUR */ + file->f_pos += offset; + break; + case 2: /* SEEK_END, offset is negative */ + file->f_pos = MIXART_BA1_SIZE + offset; + break; + default: + return -EINVAL; + } + if(file->f_pos > MIXART_BA1_SIZE) + file->f_pos = MIXART_BA1_SIZE; + return file->f_pos; +} + +/* + mixart_BA0 proc interface for BAR 0 - read callback + */ +static long snd_mixart_BA0_read(snd_info_entry_t *entry, void *file_private_data, + struct file *file, char *buf, long count) +{ + mixart_mgr_t *mgr = snd_magic_cast(mixart_mgr_t, entry->private_data, return -ENXIO); + + count = count & ~3; /* make sure the read size is a multiple of 4 bytes */ + if(count <= 0) + return 0; + if(file->f_pos + count > MIXART_BA0_SIZE) + count = (long)(MIXART_BA0_SIZE - file->f_pos); + if(copy_to_user_fromio(buf, MIXART_MEM( mgr, file->f_pos ), count)) + return -EFAULT; + file->f_pos += count; + return count; +} + +/* + mixart_BA1 proc interface for BAR 1 - read callback + */ +static long snd_mixart_BA1_read(snd_info_entry_t *entry, void *file_private_data, + struct file *file, char *buf, long count) +{ + mixart_mgr_t *mgr = snd_magic_cast(mixart_mgr_t, entry->private_data, return -ENXIO); + + count = count & ~3; /* make sure the read size is a multiple of 4 bytes */ + if(count <= 0) + return 0; + if(file->f_pos + count > MIXART_BA1_SIZE) + count = (long)(MIXART_BA1_SIZE - file->f_pos); + if(copy_to_user_fromio(buf, MIXART_REG( mgr, file->f_pos ), count)) + return -EFAULT; + file->f_pos += count; + return count; +} + +static struct snd_info_entry_ops snd_mixart_proc_ops_BA0 = { + .read = snd_mixart_BA0_read, + .llseek = snd_mixart_BA0_llseek +}; + +static struct snd_info_entry_ops snd_mixart_proc_ops_BA1 = { + .read = snd_mixart_BA1_read, + .llseek = snd_mixart_BA1_llseek +}; + + +static void snd_mixart_proc_read(snd_info_entry_t *entry, + snd_info_buffer_t * buffer) +{ + mixart_t *chip = snd_magic_cast(mixart_t, entry->private_data, return); + u32 ref; + + snd_iprintf(buffer, "Digigram miXart (alsa card %d)\n\n", chip->chip_idx); + + /* stats available when embedded OS is running */ + if (chip->mgr->hwdep->dsp_loaded & ( 1 << MIXART_MOTHERBOARD_ELF_INDEX)) { + snd_iprintf(buffer, "- hardware -\n"); + switch (chip->mgr->board_type ) { + case MIXART_DAUGHTER_TYPE_NONE : snd_iprintf(buffer, "\tmiXart8 (no daughter board)\n\n"); break; + case MIXART_DAUGHTER_TYPE_AES : snd_iprintf(buffer, "\tmiXart8 AES/EBU\n\n"); break; + case MIXART_DAUGHTER_TYPE_COBRANET : snd_iprintf(buffer, "\tmiXart8 Cobranet\n\n"); break; + default: snd_iprintf(buffer, "\tUNKNOWN!\n\n"); break; + } + + snd_iprintf(buffer, "- system load -\n"); + + /* get perf reference */ + + ref = readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_SYSTEM_LOAD_OFFSET)); + + if (ref) { + u32 mailbox = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_MAILBX_LOAD_OFFSET)) / ref; + u32 streaming = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_STREAM_LOAD_OFFSET)) / ref; + u32 interr = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_INTERR_LOAD_OFFSET)) / ref; + + snd_iprintf(buffer, "\tstreaming : %d\n", streaming); + snd_iprintf(buffer, "\tmailbox : %d\n", mailbox); + snd_iprintf(buffer, "\tinterrups handling : %d\n\n", interr); + } + } /* endif elf loaded */ +} + +static void __devinit snd_mixart_proc_init(mixart_t *chip) +{ + snd_info_entry_t *entry; + + /* text interface to read perf and temp meters */ + if (! snd_card_proc_new(chip->card, "board_info", &entry)) { + entry->private_data = chip; + entry->c.text.read_size = 1024; + entry->c.text.read = snd_mixart_proc_read; + } + + if (! snd_card_proc_new(chip->card, "mixart_BA0", &entry)) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = chip->mgr; + entry->c.ops = &snd_mixart_proc_ops_BA0; + entry->size = MIXART_BA0_SIZE; + } + if (! snd_card_proc_new(chip->card, "mixart_BA1", &entry)) { + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->private_data = chip->mgr; + entry->c.ops = &snd_mixart_proc_ops_BA1; + entry->size = MIXART_BA1_SIZE; + } +} +/* end of proc interface */ + + +/* + * probe function - creates the card manager + */ +static int __devinit snd_mixart_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + mixart_mgr_t *mgr; + unsigned int i; + int err; + size_t size; + + /* + */ + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (! enable[dev]) { + dev++; + return -ENOENT; + } + + /* enable PCI device */ + if ((err = pci_enable_device(pci)) < 0) + return err; + pci_set_master(pci); + + /* check if we can restrict PCI DMA transfers to 32 bits */ + if (!pci_dma_supported(pci, 0xffffffff)) { + snd_printk(KERN_ERR "architecture does not support 32bit PCI busmaster DMA\n"); + return -ENXIO; + } + pci_set_dma_mask(pci, 0xffffffff); + + /* + */ + mgr = snd_magic_kcalloc(mixart_mgr_t, 0, GFP_KERNEL); + if (! mgr) + return -ENOMEM; + + mgr->pci = pci; + mgr->irq = -1; + + /* resource assignment */ + for (i = 0; i < 2; i++) { + static int memory_sizes[2] = { + MIXART_BA0_SIZE, /* 16M */ + MIXART_BA1_SIZE /* 4 k */ + }; + mgr->mem[i].phys = pci_resource_start(pci, i); + mgr->mem[i].res = request_mem_region(mgr->mem[i].phys, memory_sizes[i], CARD_NAME); + if (! mgr->mem[i].res) { + snd_printk(KERN_ERR "unable to grab memory 0x%lx\n", mgr->mem[i].phys); + snd_mixart_free(mgr); + return -EBUSY; + } + mgr->mem[i].virt = (unsigned long)ioremap_nocache(mgr->mem[i].phys, memory_sizes[i]); + } + + if (request_irq(pci->irq, snd_mixart_interrupt, SA_INTERRUPT|SA_SHIRQ, CARD_NAME, (void *)mgr)) { + snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); + snd_mixart_free(mgr); + return -EBUSY; + } + mgr->irq = pci->irq; + + sprintf(mgr->shortname, "Digigram miXart"); + sprintf(mgr->longname, "%s at 0x%lx & 0x%lx, irq %i", mgr->shortname, mgr->mem[0].phys, mgr->mem[1].phys, mgr->irq); + + /* ISR spinlock */ + mgr->lock = SPIN_LOCK_UNLOCKED; + + /* init mailbox */ + mgr->msg_fifo_readptr = 0; + mgr->msg_fifo_writeptr = 0; + + mgr->msg_lock = SPIN_LOCK_UNLOCKED; + init_MUTEX(&mgr->msg_mutex); + init_waitqueue_head(&mgr->msg_sleep); + atomic_set(&mgr->msg_processed, 0); + + /* init setup mutex*/ + init_MUTEX(&mgr->setup_mutex); + + /* init message taslket */ + tasklet_init( &mgr->msg_taskq, snd_mixart_msg_tasklet, (unsigned long) mgr); + + /* card assignment */ + mgr->num_cards = MIXART_MAX_CARDS; /* 4 FIXME: configurable? */ + for (i = 0; i < mgr->num_cards; i++) { + snd_card_t *card; + char tmpid[16]; + int idx; + + if (index[dev] < 0) + idx = index[dev]; + else + idx = index[dev] + i; + snprintf(tmpid, sizeof(tmpid), "%s-%d", id[dev], i); + card = snd_card_new(idx, tmpid, THIS_MODULE, 0); + + if (! card) { + snd_printk(KERN_ERR "cannot allocate the card %d\n", i); + snd_mixart_free(mgr); + return -ENOMEM; + } + + strcpy(card->driver, CARD_NAME); + sprintf(card->shortname, "%s [PCM #%d]", mgr->shortname, i); + sprintf(card->longname, "%s [PCM #%d]", mgr->longname, i); + + if ((err = snd_mixart_create(mgr, card, i)) < 0) { + snd_mixart_free(mgr); + return err; + } + + if(i==0) { + /* init proc interface only for chip0 */ + snd_mixart_proc_init(mgr->chip[i]); + } + + if ((err = snd_card_register(card)) < 0) { + snd_mixart_free(mgr); + return err; + } + } + + /* init firmware status (mgr->hwdep->dsp_loaded reset in hwdep_new) */ + mgr->board_type = MIXART_DAUGHTER_TYPE_NONE; + + /* create array of streaminfo */ + size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS * sizeof(mixart_flowinfo_t)) ); + mgr->flowinfo_array = snd_malloc_pci_pages(mgr->pci, size, &mgr->flowinfo_physaddr); + if(!mgr->flowinfo_array) { + snd_mixart_free(mgr); + return -ENOMEM; + } + /* init streaminfo_array */ + memset(mgr->flowinfo_array, 0, size); + + /* create array of bufferinfo */ + size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS * sizeof(mixart_bufferinfo_t)) ); + mgr->bufferinfo_array = snd_malloc_pci_pages(mgr->pci, size, &mgr->bufferinfo_physaddr); + if(!mgr->bufferinfo_array) { + snd_mixart_free(mgr); + return -ENOMEM; + } + /* init bufferinfo_array */ + memset(mgr->bufferinfo_array, 0, size); + + pci_set_drvdata(pci, mgr); + dev++; + return 0; +} + +static void __devexit snd_mixart_remove(struct pci_dev *pci) +{ + snd_mixart_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "Digigram miXart", + .id_table = snd_mixart_ids, + .probe = snd_mixart_probe, + .remove = __devexit_p(snd_mixart_remove), +}; + +static int __init alsa_card_mixart_init(void) +{ + int err; + + if ((err = pci_module_init(&driver)) < 0) { +#ifdef MODULE + snd_printk(KERN_ERR "Digigram miXart soundcard not found or device busy\n"); +#endif + return err; + } + return 0; +} + +static void __exit alsa_card_mixart_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_mixart_init) +module_exit(alsa_card_mixart_exit) + +#ifndef MODULE + +/* format is: snd-mixart=enable,index,id */ + +static int __init alsa_card_mixart_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= SNDRV_CARDS) + return 0; + (void)(get_option(&str,&enable[nr_dev]) == 2 && + get_option(&str,&index[nr_dev]) == 2 && + get_id(&str,&id[nr_dev]) == 2); + nr_dev++; + return 1; +} + +__setup("snd-mixart=", alsa_card_mixart_setup); + +#endif /* ifndef MODULE */ diff -Nru a/sound/pci/mixart/mixart.h b/sound/pci/mixart/mixart.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/mixart/mixart.h Sun Feb 29 12:58:39 2004 @@ -0,0 +1,243 @@ +/* + * Driver for Digigram miXart soundcards + * + * main header file + * + * Copyright (c) 2003 by Digigram + * + * 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 + */ + +#ifndef __SOUND_MIXART_H +#define __SOUND_MIXART_H + +#include + +#define MIXART_DRIVER_VERSION 0x000100 /* 0.1.0 */ + + +/* + */ + +#define mixart_t_magic 0xa17a3e01 +#define mixart_mgr_t_magic 0xa17a3e02 + +typedef struct snd_mixart mixart_t; +typedef struct snd_mixart_mgr mixart_mgr_t; + +typedef struct snd_mixart_stream mixart_stream_t; +typedef struct snd_mixart_pipe mixart_pipe_t; + +typedef struct mixart_bufferinfo mixart_bufferinfo_t; +typedef struct mixart_flowinfo mixart_flowinfo_t; +typedef struct mixart_uid mixart_uid_t; + +struct mixart_uid +{ + u32 object_id; + u32 desc; +}; + +struct mem_area { + unsigned long phys; + unsigned long virt; + struct resource *res; +}; + + +typedef struct mixart_route mixart_route_t; +struct mixart_route { + unsigned char connected; + unsigned char phase_inv; + int volume; +}; + + +/* firmware status codes */ +#define MIXART_MOTHERBOARD_XLX_INDEX 0 +#define MIXART_MOTHERBOARD_ELF_INDEX 1 +#define MIXART_AESEBUBOARD_XLX_INDEX 2 +#define MIXART_HARDW_FILES_MAX_INDEX 3 /* xilinx, elf, AESEBU xilinx */ + +#define MIXART_MAX_CARDS 4 +#define MSG_FIFO_SIZE 16 + +#define MIXART_MAX_PHYS_CONNECTORS (MIXART_MAX_CARDS * 2 * 2) /* 4 * stereo * (analog+digital) */ + +struct snd_mixart_mgr { + unsigned int num_cards; + mixart_t *chip[MIXART_MAX_CARDS]; + + struct pci_dev *pci; + + int irq; + + /* memory-maps */ + struct mem_area mem[2]; + + /* share the name */ + char shortname[32]; /* short name of this soundcard */ + char longname[80]; /* name of this soundcard */ + + /* message tasklet */ + struct tasklet_struct msg_taskq; + + /* one and only blocking message or notification may be pending */ + u32 pending_event; + wait_queue_head_t msg_sleep; + + /* messages stored for tasklet */ + u32 msg_fifo[MSG_FIFO_SIZE]; + int msg_fifo_readptr; + int msg_fifo_writeptr; + atomic_t msg_processed; /* number of messages to be processed in takslet */ + + spinlock_t lock; /* interrupt spinlock */ + spinlock_t msg_lock; /* mailbox spinlock */ + struct semaphore msg_mutex; /* mutex for blocking_requests */ + + struct semaphore setup_mutex; /* mutex used in hw_params, open and close */ + + /* hardware interface */ + snd_hwdep_t *hwdep; + unsigned int board_type; /* read from embedded once elf file is loaded, 250 = miXart8, 251 = with AES, 252 = with Cobranet */ + + mixart_flowinfo_t* flowinfo_array; + dma_addr_t flowinfo_physaddr; + mixart_bufferinfo_t* bufferinfo_array; + dma_addr_t bufferinfo_physaddr; + + mixart_uid_t uid_console_manager; + int sample_rate; + int ref_count_rate; + + struct semaphore mixer_mutex; /* mutex for mixer */ + +}; + + +#define MIXART_STREAM_STATUS_FREE 0 +#define MIXART_STREAM_STATUS_OPEN 1 +#define MIXART_STREAM_STATUS_RUNNING 2 +#define MIXART_STREAM_STATUS_DRAINING 3 +#define MIXART_STREAM_STATUS_PAUSE 4 + +#define MIXART_PLAYBACK_STREAMS 4 +#define MIXART_CAPTURE_STREAMS 1 + +#define MIXART_PCM_ANALOG 0 +#define MIXART_PCM_DIGITAL 1 +#define MIXART_PCM_TOTAL 2 + +#define MIXART_MAX_STREAM_PER_CARD (MIXART_PCM_TOTAL * (MIXART_PLAYBACK_STREAMS + MIXART_CAPTURE_STREAMS) ) + + +#define MIXART_NOTIFY_CARD_MASK 0xF000 +#define MIXART_NOTIFY_CARD_OFFSET 12 +#define MIXART_NOTIFY_PCM_MASK 0x0F00 +#define MIXART_NOTIFY_PCM_OFFSET 8 +#define MIXART_NOTIFY_CAPT_MASK 0x0080 +#define MIXART_NOTIFY_SUBS_MASK 0x007F + + +struct snd_mixart_stream { + snd_pcm_substream_t *substream; + mixart_pipe_t *pipe; + int pcm_number; + + int status; /* nothing, running, draining */ + + u64 abs_period_elapsed; /* last absolute stream position where period_elapsed was called (multiple of runtime->period_size) */ + u32 buf_periods; /* periods counter in the buffer (< runtime->periods) */ + u32 buf_period_frag; /* defines with buf_period_pos the exact position in the buffer (< runtime->period_size) */ + + int channels; +}; + + +enum mixart_pipe_status { + PIPE_UNDEFINED, + PIPE_STOPPED, + PIPE_RUNNING, + PIPE_CLOCK_SET +}; + +struct snd_mixart_pipe { + mixart_uid_t group_uid; /* id of the pipe, as returned by embedded */ + int stream_count; + mixart_uid_t uid_left_connector; /* UID's for the audio connectors */ + mixart_uid_t uid_right_connector; + enum mixart_pipe_status status; + int references; /* number of subs openned */ + int monitoring; /* pipe used for monitoring issue */ +}; + + +struct snd_mixart { + snd_card_t *card; + mixart_mgr_t *mgr; + int chip_idx; /* zero based */ + snd_hwdep_t *hwdep; /* DSP loader, only for the first card */ + + snd_pcm_t *pcm; /* PCM analog i/o */ + snd_pcm_t *pcm_dig; /* PCM digital i/o */ + + /* allocate stereo pipe for instance */ + mixart_pipe_t pipe_in_ana; + mixart_pipe_t pipe_out_ana; + + /* if AES/EBU daughter board is available, additional pipes possible on pcm_dig */ + mixart_pipe_t pipe_in_dig; + mixart_pipe_t pipe_out_dig; + + mixart_stream_t playback_stream[MIXART_PCM_TOTAL][MIXART_PLAYBACK_STREAMS]; /* 0 = pcm, 1 = pcm_dig */ + mixart_stream_t capture_stream[MIXART_PCM_TOTAL]; /* 0 = pcm, 1 = pcm_dig */ + + /* UID's for the physical io's */ + mixart_uid_t uid_out_analog_physio; + mixart_uid_t uid_in_analog_physio; + + int analog_playback_active[2]; /* Mixer : Master Playback active (!mute) */ + int analog_playback_volume[2]; /* Mixer : Master Playback Volume */ + int analog_capture_volume[2]; /* Mixer : Master Capture Volume */ + int digital_playback_active[2*MIXART_PLAYBACK_STREAMS][2]; /* Mixer : Digital Playback Active [(analog+AES output)*streams][stereo]*/ + int digital_playback_volume[2*MIXART_PLAYBACK_STREAMS][2]; /* Mixer : Digital Playback Volume [(analog+AES output)*streams][stereo]*/ + int digital_capture_volume[2][2]; /* Mixer : Digital Capture Volume [analog+AES output][stereo] */ + int monitoring_active[2]; /* Mixer : Monitoring Active */ + int monitoring_volume[2]; /* Mixer : Monitoring Volume */ +}; + +struct mixart_bufferinfo +{ + u32 buffer_address; + u32 reserved[5]; + u32 available_length; + u32 buffer_id; +}; + +struct mixart_flowinfo +{ + u32 bufferinfo_array_phy_address; + u32 reserved[11]; + u32 bufferinfo_count; + u32 capture; +}; + +/* exported */ +int snd_mixart_create_pcm(mixart_t* chip); +mixart_pipe_t* snd_mixart_add_ref_pipe( mixart_t *chip, int pcm_number, int capture, int monitoring); +int snd_mixart_kill_ref_pipe( mixart_mgr_t *mgr, mixart_pipe_t *pipe, int monitoring); + +#endif /* __SOUND_MIXART_H */ diff -Nru a/sound/pci/mixart/mixart_core.c b/sound/pci/mixart/mixart_core.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/mixart/mixart_core.c Sun Feb 29 12:58:39 2004 @@ -0,0 +1,584 @@ +/* + * Driver for Digigram miXart soundcards + * + * low level interface with interrupt handling and mail box implementation + * + * Copyright (c) 2003 by Digigram + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include "mixart.h" +#include "mixart_hwdep.h" +#include "mixart_core.h" + + +#define MSG_TIMEOUT_JIFFIES (400 * HZ) / 1000 /* 400 ms */ + +#define MSG_DESCRIPTOR_SIZE 0x24 +#define MSG_HEADER_SIZE (MSG_DESCRIPTOR_SIZE + 4) + +#define MSG_DEFAULT_SIZE 512 + +#define MSG_TYPE_MASK 0x00000003 /* mask for following types */ +#define MSG_TYPE_NOTIFY 0 /* embedded -> driver (only notification, do not get_msg() !) */ +#define MSG_TYPE_COMMAND 1 /* driver <-> embedded (a command has no answer) */ +#define MSG_TYPE_REQUEST 2 /* driver -> embedded (request will get an answer back) */ +#define MSG_TYPE_ANSWER 3 /* embedded -> driver */ +#define MSG_CANCEL_NOTIFY_MASK 0x80000000 /* this bit is set for a notification that has been canceled */ + + +static int retrieve_msg_frame(mixart_mgr_t *mgr, u32 *msg_frame) +{ + /* read the message frame fifo */ + u32 headptr, tailptr; + + tailptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_POST_TAIL)); + headptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_POST_HEAD)); + + if (tailptr == headptr) + return 0; /* no message posted */ + + snd_assert( tailptr >= MSG_OUTBOUND_POST_STACK, return 0); /* error */ + snd_assert( tailptr < (MSG_OUTBOUND_POST_STACK+MSG_BOUND_STACK_SIZE), return 0); /* error */ + + *msg_frame = readl_be(MIXART_MEM(mgr, tailptr)); + + /* increment the tail index */ + tailptr += 4; + if( tailptr >= (MSG_OUTBOUND_POST_STACK+MSG_BOUND_STACK_SIZE) ) + tailptr = MSG_OUTBOUND_POST_STACK; + writel_be(tailptr, MIXART_MEM(mgr, MSG_OUTBOUND_POST_TAIL)); + + return 1; +} + +static int get_msg(mixart_mgr_t *mgr, mixart_msg_t *resp, u32 msg_frame_address ) +{ + unsigned long flags; + u32 headptr, i; + u32 size; + int err; + + spin_lock_irqsave(&mgr->msg_lock, flags); + err = 0; + + /* copy message descriptor from miXart to driver */ + size = readl_be(MIXART_MEM(mgr, msg_frame_address)); /* size of descriptor + response */ + resp->message_id = readl_be(MIXART_MEM(mgr, msg_frame_address + 4)); /* dwMessageID */ + resp->uid.object_id = readl_be(MIXART_MEM(mgr, msg_frame_address + 8)); /* uidDest */ + resp->uid.desc = readl_be(MIXART_MEM(mgr, msg_frame_address + 12)); /* */ + + if( (size < MSG_DESCRIPTOR_SIZE) || (resp->size < (size - MSG_DESCRIPTOR_SIZE))) { + err = -EINVAL; + snd_printk(KERN_ERR "problem with response size = %d\n", size); + goto _clean_exit; + } + size -= MSG_DESCRIPTOR_SIZE; + + memcpy_fromio(resp->data, MIXART_MEM(mgr, msg_frame_address + MSG_HEADER_SIZE ), size); + resp->size = size; + + /* swap if necessary */ +#ifndef __BIG_ENDIAN + size /= 4; /* u32 size */ + for(i=0; i < size; i++) { + ((u32*)resp->data)[i] = be32_to_cpu(((u32*)resp->data)[i]); + } +#endif + + /* + * free message frame address + */ + headptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_FREE_HEAD)); + + if( (headptr < MSG_OUTBOUND_FREE_STACK) || ( headptr >= (MSG_OUTBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE))) { + err = -EINVAL; + goto _clean_exit; + } + + /* give address back to outbound fifo */ + writel_be(msg_frame_address, MIXART_MEM(mgr, headptr)); + + /* increment the outbound free head */ + headptr += 4; + if( headptr >= (MSG_OUTBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE) ) + headptr = MSG_OUTBOUND_FREE_STACK; + + writel_be(headptr, MIXART_MEM(mgr, MSG_OUTBOUND_FREE_HEAD)); + + _clean_exit: + spin_unlock_irqrestore(&mgr->msg_lock, flags); + + return err; +} + + +/* + * send a message to miXart. return: the msg_frame used for this message + */ +/* call with mgr->msg_lock held! */ +static int send_msg( mixart_mgr_t *mgr, + mixart_msg_t *msg, + int max_answersize, + int mark_pending, + u32 *msg_event) +{ + u32 headptr, tailptr; + u32 msg_frame_address; + int err, i; + + snd_assert(msg->size % 4 == 0, return -EINVAL); + + err = 0; + + /* get message frame address */ + tailptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_FREE_TAIL)); + headptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_FREE_HEAD)); + + if (tailptr == headptr) { + snd_printk(KERN_ERR "error: no message frame available\n"); + return -EBUSY; + } + + if( (tailptr < MSG_INBOUND_FREE_STACK) || (tailptr >= (MSG_INBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE))) { + return -EINVAL; + } + + msg_frame_address = readl_be(MIXART_MEM(mgr, tailptr)); + writel(0, MIXART_MEM(mgr, tailptr)); /* set address to zero on this fifo position */ + + /* increment the inbound free tail */ + tailptr += 4; + if( tailptr >= (MSG_INBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE) ) + tailptr = MSG_INBOUND_FREE_STACK; + + writel_be(tailptr, MIXART_MEM(mgr, MSG_INBOUND_FREE_TAIL)); + + /* TODO : use memcpy_toio() with intermediate buffer to copy the message */ + + /* copy message descriptor to card memory */ + writel_be( msg->size + MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address) ); /* size of descriptor + request */ + writel_be( msg->message_id , MIXART_MEM(mgr, msg_frame_address + 4) ); /* dwMessageID */ + writel_be( msg->uid.object_id, MIXART_MEM(mgr, msg_frame_address + 8) ); /* uidDest */ + writel_be( msg->uid.desc, MIXART_MEM(mgr, msg_frame_address + 12) ); /* */ + writel_be( MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address + 16) ); /* SizeHeader */ + writel_be( MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address + 20) ); /* OffsetDLL_T16 */ + writel_be( msg->size, MIXART_MEM(mgr, msg_frame_address + 24) ); /* SizeDLL_T16 */ + writel_be( MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address + 28) ); /* OffsetDLL_DRV */ + writel_be( 0, MIXART_MEM(mgr, msg_frame_address + 32) ); /* SizeDLL_DRV */ + writel_be( MSG_DESCRIPTOR_SIZE + max_answersize, MIXART_MEM(mgr, msg_frame_address + 36) ); /* dwExpectedAnswerSize */ + + /* copy message data to card memory */ + for( i=0; i < msg->size; i+=4 ) { + writel_be( *(u32*)(msg->data + i), MIXART_MEM(mgr, MSG_HEADER_SIZE + msg_frame_address + i) ); + } + + if( mark_pending ) { + if( *msg_event ) { + /* the pending event is the notification we wait for ! */ + mgr->pending_event = *msg_event; + } + else { + /* the pending event is the answer we wait for (same address than the request)! */ + mgr->pending_event = msg_frame_address; + + /* copy address back to caller */ + *msg_event = msg_frame_address; + } + } + + /* mark the frame as a request (will have an answer) */ + msg_frame_address |= MSG_TYPE_REQUEST; + + /* post the frame */ + headptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_POST_HEAD)); + + if( (headptr < MSG_INBOUND_POST_STACK) || (headptr >= (MSG_INBOUND_POST_STACK+MSG_BOUND_STACK_SIZE))) { + return -EINVAL; + } + + writel_be(msg_frame_address, MIXART_MEM(mgr, headptr)); + + /* increment the inbound post head */ + headptr += 4; + if( headptr >= (MSG_INBOUND_POST_STACK+MSG_BOUND_STACK_SIZE) ) + headptr = MSG_INBOUND_POST_STACK; + + writel_be(headptr, MIXART_MEM(mgr, MSG_INBOUND_POST_HEAD)); + + return 0; +} + + +int snd_mixart_send_msg(mixart_mgr_t *mgr, mixart_msg_t *request, int max_resp_size, void *resp_data) +{ + mixart_msg_t resp; + u32 msg_frame = 0; /* set to 0, so it's no notification to wait for, but the answer */ + int err; + wait_queue_t wait; + long timeout; + + down(&mgr->msg_mutex); + + init_waitqueue_entry(&wait, current); + + spin_lock_irq(&mgr->msg_lock); + /* send the message */ + err = send_msg(mgr, request, max_resp_size, 1, &msg_frame); /* send and mark the answer pending */ + if (err) { + spin_unlock_irq(&mgr->msg_lock); + up(&mgr->msg_mutex); + return err; + } + + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&mgr->msg_sleep, &wait); + spin_unlock_irq(&mgr->msg_lock); + timeout = schedule_timeout(MSG_TIMEOUT_JIFFIES); + remove_wait_queue(&mgr->msg_sleep, &wait); + + if (! timeout) { + /* error - no ack */ + up(&mgr->msg_mutex); + snd_printk(KERN_ERR "error: no reponse on msg %x\n", msg_frame); + return -EIO; + } + + /* retrieve the answer into the same mixart_msg_t */ + resp.message_id = 0; + resp.uid = (mixart_uid_t){0,0}; + resp.data = resp_data; + resp.size = max_resp_size; + + err = get_msg(mgr, &resp, msg_frame); + + if( request->message_id != resp.message_id ) + snd_printk(KERN_ERR "REPONSE ERROR!\n"); + + up(&mgr->msg_mutex); + return err; +} + + +int snd_mixart_send_msg_wait_notif(mixart_mgr_t *mgr, mixart_msg_t *request, u32 notif_event) +{ + int err; + wait_queue_t wait; + long timeout; + + snd_assert(notif_event != 0, return -EINVAL); + snd_assert((notif_event & MSG_TYPE_MASK) == MSG_TYPE_NOTIFY, return -EINVAL); + snd_assert((notif_event & MSG_CANCEL_NOTIFY_MASK) == 0, return -EINVAL); + + down(&mgr->msg_mutex); + + init_waitqueue_entry(&wait, current); + + spin_lock_irq(&mgr->msg_lock); + /* send the message */ + err = send_msg(mgr, request, MSG_DEFAULT_SIZE, 1, ¬if_event); /* send and mark the notification event pending */ + if(err) { + spin_unlock_irq(&mgr->msg_lock); + up(&mgr->msg_mutex); + return err; + } + + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&mgr->msg_sleep, &wait); + spin_unlock_irq(&mgr->msg_lock); + timeout = schedule_timeout(MSG_TIMEOUT_JIFFIES); + remove_wait_queue(&mgr->msg_sleep, &wait); + + if (! timeout) { + /* error - no ack */ + up(&mgr->msg_mutex); + snd_printk(KERN_ERR "error: notification %x not received\n", notif_event); + return -EIO; + } + + up(&mgr->msg_mutex); + return 0; +} + + +int snd_mixart_send_msg_nonblock(mixart_mgr_t *mgr, mixart_msg_t *request) +{ + u32 message_frame; + unsigned long flags; + int err; + + /* just send the message (do not mark it as a pending one) */ + spin_lock_irqsave(&mgr->msg_lock, flags); + err = send_msg(mgr, request, MSG_DEFAULT_SIZE, 0, &message_frame); + spin_unlock_irqrestore(&mgr->msg_lock, flags); + + /* the answer will be handled by snd_mixart_msg_tasklet() */ + atomic_inc(&mgr->msg_processed); + + return err; +} + + +/* common buffer of tasklet and interrupt to send/receive messages */ +static u32 mixart_msg_data[MSG_DEFAULT_SIZE / 4]; + + +void snd_mixart_msg_tasklet( unsigned long arg) +{ + mixart_mgr_t *mgr = ( mixart_mgr_t*)(arg); + mixart_msg_t resp; + u32 msg, addr, type; + int err; + + spin_lock(&mgr->lock); + + while (mgr->msg_fifo_readptr != mgr->msg_fifo_writeptr) { + msg = mgr->msg_fifo[mgr->msg_fifo_readptr]; + mgr->msg_fifo_readptr++; + mgr->msg_fifo_readptr %= MSG_FIFO_SIZE; + + /* process the message ... */ + addr = msg & ~MSG_TYPE_MASK; + type = msg & MSG_TYPE_MASK; + + switch (type) { + case MSG_TYPE_ANSWER: + /* answer to a message on that we did not wait for (send_msg_nonblock) */ + resp.message_id = 0; + resp.data = mixart_msg_data; + resp.size = sizeof(mixart_msg_data); + err = get_msg(mgr, &resp, addr); + if( err < 0 ) { + snd_printk(KERN_ERR "tasklet: error(%d) reading mf %x\n", err, msg); + break; + } + + switch(resp.message_id) { + case MSG_STREAM_START_INPUT_STAGE_PACKET: + case MSG_STREAM_START_OUTPUT_STAGE_PACKET: + case MSG_STREAM_STOP_INPUT_STAGE_PACKET: + case MSG_STREAM_STOP_OUTPUT_STAGE_PACKET: + if(mixart_msg_data[0]) + snd_printk(KERN_ERR "tasklet : error MSG_STREAM_ST***_***PUT_STAGE_PACKET status=%x\n", mixart_msg_data[0]); + break; + default: + snd_printdd("tasklet received mf(%x) : msg_id(%x) uid(%x, %x) size(%d)\n", + msg, resp.message_id, resp.uid.object_id, resp.uid.desc, resp.size); + break; + } + break; + case MSG_TYPE_NOTIFY: + /* msg contains no address ! do not get_msg() ! */ + case MSG_TYPE_COMMAND: + /* get_msg() necessary */ + default: + snd_printk(KERN_ERR "tasklet doesn't know what to do with message %x\n", msg); + } /* switch type */ + + /* decrement counter */ + atomic_dec(&mgr->msg_processed); + + } /* while there is a msg in fifo */ + + spin_unlock(&mgr->lock); +} + + +irqreturn_t snd_mixart_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + mixart_mgr_t *mgr = snd_magic_cast(mixart_mgr_t, dev_id, return IRQ_NONE); + int err; + mixart_msg_t resp; + + u32 msg; + u32 it_reg; + + spin_lock(&mgr->lock); + + it_reg = readl_le(MIXART_REG(mgr, MIXART_PCI_OMISR_OFFSET)); + if( !(it_reg & MIXART_OIDI) ) { + /* this device did not cause the interrupt */ + spin_unlock(&mgr->lock); + return IRQ_NONE; + } + + /* mask all interrupts */ + writel_le(MIXART_HOST_ALL_INTERRUPT_MASKED, MIXART_REG(mgr, MIXART_PCI_OMIMR_OFFSET)); + + /* outdoorbell register clear */ + it_reg = readl(MIXART_REG(mgr, MIXART_PCI_ODBR_OFFSET)); + writel(it_reg, MIXART_REG(mgr, MIXART_PCI_ODBR_OFFSET)); + + /* clear interrupt */ + writel_le( MIXART_OIDI, MIXART_REG(mgr, MIXART_PCI_OMISR_OFFSET) ); + + /* process interrupt */ + while (retrieve_msg_frame(mgr, &msg)) { + + switch (msg & MSG_TYPE_MASK) { + case MSG_TYPE_COMMAND: + resp.message_id = 0; + resp.data = mixart_msg_data; + resp.size = sizeof(mixart_msg_data); + err = get_msg(mgr, &resp, msg & ~MSG_TYPE_MASK); + if( err < 0 ) { + snd_printk(KERN_ERR "interrupt: error(%d) reading mf %x\n", err, msg); + break; + } + + if(resp.message_id == MSG_SERVICES_TIMER_NOTIFY) { + int i; + mixart_timer_notify_t *notify = (mixart_timer_notify_t*)mixart_msg_data; + + for(i=0; istream_count; i++) { + + u32 buffer_id = notify->streams[i].buffer_id; + unsigned int chip_number = (buffer_id & MIXART_NOTIFY_CARD_MASK) >> MIXART_NOTIFY_CARD_OFFSET; /* card0 to 3 */ + unsigned int pcm_number = (buffer_id & MIXART_NOTIFY_PCM_MASK ) >> MIXART_NOTIFY_PCM_OFFSET; /* pcm0 to 3 */ + unsigned int sub_number = buffer_id & MIXART_NOTIFY_SUBS_MASK; /* 0 to MIXART_PLAYBACK_STREAMS */ + unsigned int is_capture = ((buffer_id & MIXART_NOTIFY_CAPT_MASK) != 0); /* playback == 0 / capture == 1 */ + + mixart_t *chip = mgr->chip[chip_number]; + mixart_stream_t *stream; + + if ((chip_number >= mgr->num_cards) || (pcm_number >= MIXART_PCM_TOTAL) || (sub_number >= MIXART_PLAYBACK_STREAMS)) { + snd_printk(KERN_ERR "error MSG_SERVICES_TIMER_NOTIFY buffer_id (%x) pos(%d)\n", + buffer_id, notify->streams[i].sample_pos_low_part); + break; + } + + if (is_capture) + stream = &chip->capture_stream[pcm_number]; + else + stream = &chip->playback_stream[pcm_number][sub_number]; + + if (stream->substream && (stream->status == MIXART_STREAM_STATUS_RUNNING)) { + snd_pcm_runtime_t *runtime = stream->substream->runtime; + int elapsed = 0; + u64 sample_count = ((u64)notify->streams[i].sample_pos_high_part) << 32; + sample_count |= notify->streams[i].sample_pos_low_part; + + while (1) { + u64 new_elapse_pos = stream->abs_period_elapsed + runtime->period_size; + + if (new_elapse_pos > sample_count) { + break; /* while */ + } + else { + elapsed = 1; + stream->buf_periods++; + if (stream->buf_periods >= runtime->periods) + stream->buf_periods = 0; + + stream->abs_period_elapsed = new_elapse_pos; + } + } + stream->buf_period_frag = (u32)( sample_count - stream->abs_period_elapsed ); + + if(elapsed) { + spin_unlock(&mgr->lock); + snd_pcm_period_elapsed(stream->substream); + spin_lock(&mgr->lock); + } + } + } + break; + } + if(resp.message_id == MSG_SERVICES_REPORT_TRACES) { + if(resp.size > 1) { +#ifndef __BIG_ENDIAN + /* Traces are text: the swapped msg_data has to be swapped back ! */ + int i; + for(i=0; i<(resp.size/4); i++) { + (mixart_msg_data)[i] = cpu_to_be32((mixart_msg_data)[i]); + } +#endif + ((char*)mixart_msg_data)[resp.size - 1] = 0; + snd_printdd("MIXART TRACE : %s\n", (char*)mixart_msg_data); + } + break; + } + + snd_printdd("command %x not handled\n", resp.message_id); + break; + + case MSG_TYPE_NOTIFY: + if(msg & MSG_CANCEL_NOTIFY_MASK) { + msg &= ~MSG_CANCEL_NOTIFY_MASK; + snd_printk(KERN_ERR "canceled notification %x !\n", msg); + } + /* no break, continue ! */ + case MSG_TYPE_ANSWER: + /* answer or notification to a message we are waiting for*/ + spin_lock(&mgr->msg_lock); + if( (msg & ~MSG_TYPE_MASK) == mgr->pending_event ) { + wake_up(&mgr->msg_sleep); + mgr->pending_event = 0; + } + /* answer to a message we did't want to wait for */ + else { + mgr->msg_fifo[mgr->msg_fifo_writeptr] = msg; + mgr->msg_fifo_writeptr++; + mgr->msg_fifo_writeptr %= MSG_FIFO_SIZE; + tasklet_hi_schedule(&mgr->msg_taskq); + } + spin_unlock(&mgr->msg_lock); + break; + case MSG_TYPE_REQUEST: + default: + snd_printdd("interrupt received request %x\n", msg); + /* TODO : are there things to do here ? */ + break; + } /* switch on msg type */ + } /* while there are msgs */ + + /* allow interrupt again */ + writel_le( MIXART_ALLOW_OUTBOUND_DOORBELL, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET)); + + spin_unlock(&mgr->lock); + + return IRQ_HANDLED; +} + + +void snd_mixart_init_mailbox(mixart_mgr_t *mgr) +{ + writel( 0, MIXART_MEM( mgr, MSG_HOST_RSC_PROTECTION ) ); + writel( 0, MIXART_MEM( mgr, MSG_AGENT_RSC_PROTECTION ) ); + + /* allow outbound messagebox to generate interrupts */ + if(mgr->irq >= 0) { + writel_le( MIXART_ALLOW_OUTBOUND_DOORBELL, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET)); + } + return; +} + +void snd_mixart_exit_mailbox(mixart_mgr_t *mgr) +{ + /* no more interrupts on outbound messagebox */ + writel_le( MIXART_HOST_ALL_INTERRUPT_MASKED, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET)); + return; +} + +void snd_mixart_reset_board(mixart_mgr_t *mgr) +{ + /* reset miXart */ + writel_be( 1, MIXART_REG(mgr, MIXART_BA1_BRUTAL_RESET_OFFSET) ); + return; +} diff -Nru a/sound/pci/mixart/mixart_core.h b/sound/pci/mixart/mixart_core.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/mixart/mixart_core.h Sun Feb 29 12:58:39 2004 @@ -0,0 +1,607 @@ +/* + * Driver for Digigram miXart soundcards + * + * low level interface with interrupt handling and mail box implementation + * + * Copyright (c) 2003 by Digigram + * + * 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 + */ + +#ifndef __SOUND_MIXART_CORE_H +#define __SOUND_MIXART_CORE_H + + +enum mixart_message_id { + MSG_CONNECTOR_GET_AUDIO_INFO = 0x050008, + MSG_CONNECTOR_GET_OUT_AUDIO_LEVEL = 0x050009, + MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL = 0x05000A, + + MSG_CONSOLE_MANAGER = 0x070000, + MSG_CONSOLE_GET_CLOCK_UID = 0x070003, + + MSG_PHYSICALIO_SET_LEVEL = 0x0F0008, + + MSG_STREAM_ADD_INPUT_GROUP = 0x130000, + MSG_STREAM_ADD_OUTPUT_GROUP = 0x130001, + MSG_STREAM_DELETE_GROUP = 0x130004, + MSG_STREAM_START_STREAM_GRP_PACKET = 0x130006, + MSG_STREAM_START_INPUT_STAGE_PACKET = 0x130007, + MSG_STREAM_START_OUTPUT_STAGE_PACKET = 0x130008, + MSG_STREAM_STOP_STREAM_GRP_PACKET = 0x130009, + MSG_STREAM_STOP_INPUT_STAGE_PACKET = 0x13000A, + MSG_STREAM_STOP_OUTPUT_STAGE_PACKET = 0x13000B, + MSG_STREAM_SET_INPUT_STAGE_PARAM = 0x13000F, + MSG_STREAM_SET_OUTPUT_STAGE_PARAM = 0x130010, + MSG_STREAM_SET_IN_AUDIO_LEVEL = 0x130015, + MSG_STREAM_SET_OUT_STREAM_LEVEL = 0x130017, + + MSG_SYSTEM_FIRST_ID = 0x160000, + MSG_SYSTEM_ENUM_PHYSICAL_IO = 0x16000E, + MSG_SYSTEM_ENUM_PLAY_CONNECTOR = 0x160017, + MSG_SYSTEM_ENUM_RECORD_CONNECTOR = 0x160018, + MSG_SYSTEM_WAIT_SYNCHRO_CMD = 0x16002C, + MSG_SYSTEM_SEND_SYNCHRO_CMD = 0x16002D, + + MSG_SERVICES_TIMER_NOTIFY = 0x1D0404, + MSG_SERVICES_REPORT_TRACES = 0x1D0700, + + MSG_CLOCK_CHECK_PROPERTIES = 0x200001, + MSG_CLOCK_SET_PROPERTIES = 0x200002, +}; + + +typedef struct mixart_msg mixart_msg_t; +struct mixart_msg +{ + u32 message_id; + mixart_uid_t uid; + void* data; + size_t size; +}; + +/* structs used to communicate with miXart */ + +typedef struct mixart_enum_connector_resp mixart_enum_connector_resp_t; +struct mixart_enum_connector_resp +{ + u32 error_code; + u32 first_uid_offset; + u32 uid_count; + u32 current_uid_index; + mixart_uid_t uid[MIXART_MAX_PHYS_CONNECTORS]; +} __attribute__((packed)); + + +/* used for following struct */ +#define MIXART_FLOAT_P_22_0_TO_HEX 0x41b00000 /* 22.0f */ +#define MIXART_FLOAT_M_20_0_TO_HEX 0xc1a00000 /* -20.0f */ +#define MIXART_FLOAT____0_0_TO_HEX 0x00000000 /* 0.0f */ + +typedef struct mixart_audio_info_req mixart_audio_info_req_t; +struct mixart_audio_info_req +{ + u32 line_max_level; /* float */ + u32 micro_max_level; /* float */ + u32 cd_max_level; /* float */ +} __attribute__((packed)); + +typedef struct mixart_analog_hw_info mixart_analog_hw_info_t; +struct mixart_analog_hw_info +{ + u32 is_present; + u32 hw_connection_type; + u32 max_level; /* float */ + u32 min_var_level; /* float */ + u32 max_var_level; /* float */ + u32 step_var_level; /* float */ + u32 fix_gain; /* float */ + u32 zero_var; /* float */ +} __attribute__((packed)); + +typedef struct mixart_digital_hw_info mixart_digital_hw_info_t; +struct mixart_digital_hw_info +{ + u32 hw_connection_type; + u32 presence; + u32 clock; + u32 reserved; +} __attribute__((packed)); + +typedef struct mixart_analog_info mixart_analog_info_t; +struct mixart_analog_info +{ + u32 type_mask; + mixart_analog_hw_info_t micro_info; + mixart_analog_hw_info_t line_info; + mixart_analog_hw_info_t cd_info; + u32 analog_level_present; +} __attribute__((packed)); + +typedef struct mixart_digital_info mixart_digital_info_t; +struct mixart_digital_info +{ + u32 type_mask; + mixart_digital_hw_info_t aes_info; + mixart_digital_hw_info_t adat_info; +} __attribute__((packed)); + +typedef struct mixart_audio_info mixart_audio_info_t; +struct mixart_audio_info +{ + u32 clock_type_mask; + mixart_analog_info_t analog_info; + mixart_digital_info_t digital_info; +} __attribute__((packed)); + +typedef struct mixart_audio_info_resp mixart_audio_info_resp_t; +struct mixart_audio_info_resp +{ + u32 txx_status; + mixart_audio_info_t info; +} __attribute__((packed)); + + +/* used for nb_bytes_max_per_sample */ +#define MIXART_FLOAT_P__4_0_TO_HEX 0x40800000 /* +4.0f */ +#define MIXART_FLOAT_P__8_0_TO_HEX 0x41000000 /* +8.0f */ + +typedef struct mixart_stream_info mixart_stream_info_t; +struct mixart_stream_info +{ + u32 size_max_byte_frame; + u32 size_max_sample_frame; + u32 nb_bytes_max_per_sample; /* float */ +} __attribute__((packed)); + +/* MSG_STREAM_ADD_INPUT_GROUP */ +/* MSG_STREAM_ADD_OUTPUT_GROUP */ + +typedef struct mixart_streaming_group_req mixart_streaming_group_req_t; +struct mixart_streaming_group_req +{ + u32 stream_count; + u32 channel_count; + u32 user_grp_number; + u32 first_phys_audio; + u32 latency; + mixart_stream_info_t stream_info[32]; + mixart_uid_t connector; + u32 flow_entry[32]; +} __attribute__((packed)); + +typedef struct mixart_stream_desc mixart_stream_desc_t; +struct mixart_stream_desc +{ + mixart_uid_t stream_uid; + u32 stream_desc; +} __attribute__((packed)); + +typedef struct mixart_streaming_group mixart_streaming_group_t; +struct mixart_streaming_group +{ + u32 status; + mixart_uid_t group; + u32 pipe_desc; + u32 stream_count; + mixart_stream_desc_t stream[32]; +} __attribute__((packed)); + +/* MSG_STREAM_DELETE_GROUP */ + +/* request : mixart_uid_t group */ + +typedef struct mixart_delete_group_resp mixart_delete_group_resp_t; +struct mixart_delete_group_resp +{ + u32 status; + u32 unused[2]; +} __attribute__((packed)); + + +/* MSG_STREAM_START_INPUT_STAGE_PACKET = 0x130000 + 7, + MSG_STREAM_START_OUTPUT_STAGE_PACKET = 0x130000 + 8, + MSG_STREAM_STOP_INPUT_STAGE_PACKET = 0x130000 + 10, + MSG_STREAM_STOP_OUTPUT_STAGE_PACKET = 0x130000 + 11, + */ + +typedef struct mixart_fx_couple_uid mixart_fx_couple_uid_t; +struct mixart_fx_couple_uid +{ + mixart_uid_t uid_fx_code; + mixart_uid_t uid_fx_data; +} __attribute__((packed)); + +typedef struct mixart_txx_stream_desc mixart_txx_stream_desc_t; +struct mixart_txx_stream_desc +{ + mixart_uid_t uid_pipe; + u32 stream_idx; + u32 fx_number; + mixart_fx_couple_uid_t uid_fx[4]; +} __attribute__((packed)); + +typedef struct mixart_flow_info mixart_flow_info_t; +struct mixart_flow_info +{ + mixart_txx_stream_desc_t stream_desc; + u32 flow_entry; + u32 flow_phy_addr; +} __attribute__((packed)); + +typedef struct mixart_stream_state_req mixart_stream_state_req_t; +struct mixart_stream_state_req +{ + u32 delayed; + u64 scheduler; + u32 reserved4np[3]; + u32 stream_count; /* set to 1 for instance */ + mixart_flow_info_t stream_info; /* could be an array[stream_count] */ +} __attribute__((packed)); + +/* MSG_STREAM_START_STREAM_GRP_PACKET = 0x130000 + 6 + MSG_STREAM_STOP_STREAM_GRP_PACKET = 0x130000 + 9 + */ + +typedef struct mixart_group_state_req mixart_group_state_req_t; +struct mixart_group_state_req +{ + u32 delayed; + u64 scheduler; + u32 reserved4np[2]; + u32 pipe_count; /* set to 1 for instance */ + mixart_uid_t pipe_uid[1]; /* could be an array[pipe_count] */ +} __attribute__((packed)); + +typedef struct mixart_group_state_resp mixart_group_state_resp_t; +struct mixart_group_state_resp +{ + u32 txx_status; + u64 scheduler; +} __attribute__((packed)); + + + +/* Structures used by the MSG_SERVICES_TIMER_NOTIFY command */ + +typedef struct mixart_sample_pos mixart_sample_pos_t; +struct mixart_sample_pos +{ + u32 buffer_id; + u32 validity; + u32 sample_pos_high_part; + u32 sample_pos_low_part; +} __attribute__((packed)); + +typedef struct mixart_timer_notify mixart_timer_notify_t; +struct mixart_timer_notify +{ + u32 stream_count; + mixart_sample_pos_t streams[MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS]; +} __attribute__((packed)); + + +/* MSG_CONSOLE_GET_CLOCK_UID = 0x070003, + */ + +/* request is a uid with desc = MSG_CONSOLE_MANAGER | cardindex */ + +typedef struct mixart_return_uid mixart_return_uid_t; +struct mixart_return_uid +{ + u32 error_code; + mixart_uid_t uid; +} __attribute__((packed)); + +/* MSG_CLOCK_CHECK_PROPERTIES = 0x200001, + MSG_CLOCK_SET_PROPERTIES = 0x200002, +*/ + +enum mixart_clock_generic_type { + CGT_NO_CLOCK, + CGT_INTERNAL_CLOCK, + CGT_PROGRAMMABLE_CLOCK, + CGT_INTERNAL_ENSLAVED_CLOCK, + CGT_EXTERNAL_CLOCK, + CGT_CURRENT_CLOCK +}; + +enum mixart_clock_mode { + CM_UNDEFINED, + CM_MASTER, + CM_SLAVE, + CM_STANDALONE, + CM_NOT_CONCERNED +}; + + +typedef struct mixart_clock_properties mixart_clock_properties_t; +struct mixart_clock_properties +{ + u32 error_code; + u32 validation_mask; + u32 frequency; + u32 reference_frequency; + u32 clock_generic_type; + u32 clock_mode; + mixart_uid_t uid_clock_source; + mixart_uid_t uid_event_source; + u32 event_mode; + u32 synchro_signal_presence; + u32 format; + u32 board_mask; + u32 nb_callers; /* set to 1 (see below) */ + mixart_uid_t uid_caller[1]; +} __attribute__((packed)); + +typedef struct mixart_clock_properties_resp mixart_clock_properties_resp_t; +struct mixart_clock_properties_resp +{ + u32 status; + u32 clock_mode; +} __attribute__((packed)); + + +/* MSG_STREAM_SET_INPUT_STAGE_PARAM = 0x13000F */ +/* MSG_STREAM_SET_OUTPUT_STAGE_PARAM = 0x130010 */ + +enum mixart_coding_type { + CT_NOT_DEFINED, + CT_LINEAR, + CT_MPEG_L1, + CT_MPEG_L2, + CT_MPEG_L3, + CT_MPEG_L3_LSF, + CT_GSM +}; +enum mixart_sample_type { + ST_NOT_DEFINED, + ST_FLOATING_POINT_32BE, + ST_FLOATING_POINT_32LE, + ST_FLOATING_POINT_64BE, + ST_FLOATING_POINT_64LE, + ST_FIXED_POINT_8, + ST_FIXED_POINT_16BE, + ST_FIXED_POINT_16LE, + ST_FIXED_POINT_24BE, + ST_FIXED_POINT_24LE, + ST_FIXED_POINT_32BE, + ST_FIXED_POINT_32LE, + ST_INTEGER_8, + ST_INTEGER_16BE, + ST_INTEGER_16LE, + ST_INTEGER_24BE, + ST_INTEGER_24LE, + ST_INTEGER_32BE, + ST_INTEGER_32LE +}; + +typedef struct mixart_stream_param_desc mixart_stream_param_desc_t; +struct mixart_stream_param_desc +{ + u32 coding_type; /* use enum mixart_coding_type */ + u32 sample_type; /* use enum mixart_sample_type */ + + union { + struct { + u32 linear_endian_ness; + u32 linear_bits; + u32 is_signed; + u32 is_float; + } linear_format_info; + + struct { + u32 mpeg_layer; + u32 mpeg_mode; + u32 mpeg_mode_extension; + u32 mpeg_pre_emphasis; + u32 mpeg_has_padding_bit; + u32 mpeg_has_crc; + u32 mpeg_has_extension; + u32 mpeg_is_original; + u32 mpeg_has_copyright; + } mpeg_format_info; + } format_info; + + u32 delayed; + u64 scheduler; + u32 sample_size; + u32 has_header; + u32 has_suffix; + u32 has_bitrate; + u32 samples_per_frame; + u32 bytes_per_frame; + u32 bytes_per_sample; + u32 sampling_freq; + u32 number_of_channel; + u32 stream_number; + u32 buffer_size; + u32 differed_time; + u32 reserved4np[3]; + u32 pipe_count; /* set to 1 (array size !) */ + u32 stream_count; /* set to 1 (array size !) */ + mixart_txx_stream_desc_t stream_desc[1]; /* only one stream per command, but this could be an array */ + +} __attribute__((packed)); + + +/* MSG_CONNECTOR_GET_OUT_AUDIO_LEVEL = 0x050009, + */ + + +typedef struct mixart_get_out_audio_level mixart_get_out_audio_level_t; +struct mixart_get_out_audio_level +{ + u32 txx_status; + u32 digital_level; /* float */ + u32 analog_level; /* float */ + u32 monitor_level; /* float */ + u32 mute; + u32 monitor_mute1; + u32 monitor_mute2; +} __attribute__((packed)); + + +/* MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL = 0x05000A, + */ + +/* used for valid_mask below */ +#define MIXART_AUDIO_LEVEL_ANALOG_MASK 0x01 +#define MIXART_AUDIO_LEVEL_DIGITAL_MASK 0x02 +#define MIXART_AUDIO_LEVEL_MONITOR_MASK 0x04 +#define MIXART_AUDIO_LEVEL_MUTE_MASK 0x08 +#define MIXART_AUDIO_LEVEL_MUTE_M1_MASK 0x10 +#define MIXART_AUDIO_LEVEL_MUTE_M2_MASK 0x20 + +typedef struct mixart_set_out_audio_level mixart_set_out_audio_level_t; +struct mixart_set_out_audio_level +{ + u32 delayed; + u64 scheduler; + u32 valid_mask1; + u32 valid_mask2; + u32 digital_level; /* float */ + u32 analog_level; /* float */ + u32 monitor_level; /* float */ + u32 mute; + u32 monitor_mute1; + u32 monitor_mute2; + u32 reserved4np; +} __attribute__((packed)); + + +/* MSG_SYSTEM_ENUM_PHYSICAL_IO = 0x16000E, + */ + +#define MIXART_MAX_PHYS_IO (MIXART_MAX_CARDS * 2 * 2) /* 4 * (analog+digital) * (playback+capture) */ + +typedef struct mixart_uid_enumeration mixart_uid_enumeration_t; +struct mixart_uid_enumeration +{ + u32 error_code; + u32 first_uid_offset; + u32 nb_uid; + u32 current_uid_index; + mixart_uid_t uid[MIXART_MAX_PHYS_IO]; +} __attribute__((packed)); + + +/* MSG_PHYSICALIO_SET_LEVEL = 0x0F0008, + MSG_PHYSICALIO_GET_LEVEL = 0x0F000C, +*/ + +typedef struct mixart_io_channel_level mixart_io_channel_level_t; +struct mixart_io_channel_level +{ + u32 analog_level; /* float */ + u32 unused[2]; +} __attribute__((packed)); + +typedef struct mixart_io_level mixart_io_level_t; +struct mixart_io_level +{ + s32 channel; /* 0=left, 1=right, -1=both, -2=both same */ + mixart_io_channel_level_t level[2]; +} __attribute__((packed)); + + +/* MSG_STREAM_SET_IN_AUDIO_LEVEL = 0x130015, + */ + +typedef struct mixart_in_audio_level_info mixart_in_audio_level_info_t; +struct mixart_in_audio_level_info +{ + mixart_uid_t connector; + u32 valid_mask1; + u32 valid_mask2; + u32 digital_level; + u32 analog_level; +} __attribute__((packed)); + +typedef struct mixart_set_in_audio_level_req mixart_set_in_audio_level_req_t; +struct mixart_set_in_audio_level_req +{ + u32 delayed; + u64 scheduler; + u32 audio_count; /* set to <= 2 */ + u32 reserved4np; + mixart_in_audio_level_info_t level[2]; +} __attribute__((packed)); + +/* response is a 32 bit status */ + + +/* MSG_STREAM_SET_OUT_STREAM_LEVEL = 0x130017, + */ + +/* defines used for valid_mask1 */ +#define MIXART_OUT_STREAM_SET_LEVEL_LEFT_AUDIO1 0x01 +#define MIXART_OUT_STREAM_SET_LEVEL_LEFT_AUDIO2 0x02 +#define MIXART_OUT_STREAM_SET_LEVEL_RIGHT_AUDIO1 0x04 +#define MIXART_OUT_STREAM_SET_LEVEL_RIGHT_AUDIO2 0x08 +#define MIXART_OUT_STREAM_SET_LEVEL_STREAM_1 0x10 +#define MIXART_OUT_STREAM_SET_LEVEL_STREAM_2 0x20 +#define MIXART_OUT_STREAM_SET_LEVEL_MUTE_1 0x40 +#define MIXART_OUT_STREAM_SET_LEVEL_MUTE_2 0x80 + +typedef struct mixart_out_stream_level_info mixart_out_stream_level_info_t; +struct mixart_out_stream_level_info +{ + u32 valid_mask1; + u32 valid_mask2; + u32 left_to_out1_level; + u32 left_to_out2_level; + u32 right_to_out1_level; + u32 right_to_out2_level; + u32 digital_level1; + u32 digital_level2; + u32 mute1; + u32 mute2; +} __attribute__((packed)); + +typedef struct mixart_set_out_stream_level mixart_set_out_stream_level_t; +struct mixart_set_out_stream_level +{ + mixart_txx_stream_desc_t desc; + mixart_out_stream_level_info_t out_level; +} __attribute__((packed)); + +typedef struct mixart_set_out_stream_level_req mixart_set_out_stream_level_req_t; +struct mixart_set_out_stream_level_req +{ + u32 delayed; + u64 scheduler; + u32 reserved4np[2]; + u32 nb_of_stream; /* set to 1 */ + mixart_set_out_stream_level_t stream_level; /* could be an array */ +} __attribute__((packed)); + +/* response to this request is a u32 status value */ + + +/* exported */ +void snd_mixart_init_mailbox(mixart_mgr_t *mgr); +void snd_mixart_exit_mailbox(mixart_mgr_t *mgr); + +int snd_mixart_send_msg(mixart_mgr_t *mgr, mixart_msg_t *request, int max_resp_size, void *resp_data); +int snd_mixart_send_msg_wait_notif(mixart_mgr_t *mgr, mixart_msg_t *request, u32 notif_event); +int snd_mixart_send_msg_nonblock(mixart_mgr_t *mgr, mixart_msg_t *request); + +irqreturn_t snd_mixart_interrupt(int irq, void *dev_id, struct pt_regs *regs); +void snd_mixart_msg_tasklet( unsigned long arg); + +void snd_mixart_reset_board(mixart_mgr_t *mgr); + +#endif /* __SOUND_MIXART_CORE_H */ diff -Nru a/sound/pci/mixart/mixart_hwdep.c b/sound/pci/mixart/mixart_hwdep.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/mixart/mixart_hwdep.c Sun Feb 29 12:58:39 2004 @@ -0,0 +1,571 @@ +/* + * Driver for Digigram miXart soundcards + * + * hwdep device manager + * + * Copyright (c) 2003 by Digigram + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include "mixart.h" +#include "mixart_mixer.h" +#include "mixart_core.h" +#include "mixart_hwdep.h" + + +/* miXart hwdep interface id string */ +#define SND_MIXART_HWDEP_ID "miXart Loader" + +static int mixart_hwdep_open(snd_hwdep_t *hw, struct file *file) +{ + return 0; +} + +static int mixart_hwdep_release(snd_hwdep_t *hw, struct file *file) +{ + return 0; +} + +/** + * wait for a value on a peudo register, exit with a timeout + * + * @param mgr pointer to miXart manager structure + * @param offset unsigned pseudo_register base + offset of value + * @param value value + * @param timeout timeout in centisenconds + */ +static int mixart_wait_nice_for_register_value(mixart_mgr_t *mgr, u32 offset, int is_egal, u32 value, unsigned long timeout) +{ + unsigned long end_time = jiffies + (timeout * HZ / 100); + u32 read; + + do { /* we may take too long time in this loop. + * so give controls back to kernel if needed. + */ + cond_resched(); + + read = readl_be( MIXART_MEM( mgr, offset )); + if(is_egal) { + if(read == value) return 0; + } + else { /* wait for different value */ + if(read != value) return 0; + } + } while ( time_after_eq(end_time, jiffies) ); + + return -EBUSY; +} + + +/* + structures needed to upload elf code packets + */ +typedef struct snd_mixart_elf32_ehdr snd_mixart_elf32_ehdr_t; + +struct snd_mixart_elf32_ehdr { + u8 e_ident[16]; + u16 e_type; + u16 e_machine; + u32 e_version; + u32 e_entry; + u32 e_phoff; + u32 e_shoff; + u32 e_flags; + u16 e_ehsize; + u16 e_phentsize; + u16 e_phnum; + u16 e_shentsize; + u16 e_shnum; + u16 e_shstrndx; +}; + +typedef struct snd_mixart_elf32_phdr snd_mixart_elf32_phdr_t; + +struct snd_mixart_elf32_phdr { + u32 p_type; + u32 p_offset; + u32 p_vaddr; + u32 p_paddr; + u32 p_filesz; + u32 p_memsz; + u32 p_flags; + u32 p_align; +}; + +static int mixart_load_elf(mixart_mgr_t *mgr, snd_hwdep_dsp_image_t *dsp ) +{ + char elf32_magic_number[4] = {0x7f,'E','L','F'}; + snd_mixart_elf32_ehdr_t elf_header; + int i; + + if ( copy_from_user(&elf_header, dsp->image , sizeof(snd_mixart_elf32_ehdr_t)) ) + return -EFAULT; + + for( i=0; i<4; i++ ) + if ( elf32_magic_number[i] != elf_header.e_ident[i] ) + return -EINVAL; + + if( elf_header.e_phoff != 0 ) { + snd_mixart_elf32_phdr_t elf_programheader; + + for( i=0; i < be16_to_cpu(elf_header.e_phnum); i++ ) { + u32 pos = be32_to_cpu(elf_header.e_phoff) + (u32)(i * be16_to_cpu(elf_header.e_phentsize)); + + if( copy_from_user( &elf_programheader, dsp->image + pos, sizeof(elf_programheader) ) ) + return -EFAULT; + + if(elf_programheader.p_type != 0) { + if( elf_programheader.p_filesz != 0 ) { + if(copy_from_user_toio( MIXART_MEM( mgr, be32_to_cpu(elf_programheader.p_vaddr)), + dsp->image + be32_to_cpu( elf_programheader.p_offset ), + be32_to_cpu( elf_programheader.p_filesz ))) + return -EFAULT; + } + } + } + } + return 0; +} + +static int mixart_hwdep_dsp_status(snd_hwdep_t *hw, snd_hwdep_dsp_status_t *info) +{ + mixart_mgr_t *mgr = snd_magic_cast(mixart_mgr_t, hw->private_data, return -ENXIO); + + strcpy(info->id, "miXart"); + info->num_dsps = MIXART_HARDW_FILES_MAX_INDEX; + + if (mgr->hwdep->dsp_loaded & (1 << MIXART_MOTHERBOARD_ELF_INDEX)) + info->chip_ready = 1; + + info->version = MIXART_DRIVER_VERSION; + return 0; +} + +/* + * get basic information and init miXart + */ + +/* audio IDs for request to the board */ +#define MIXART_FIRST_ANA_AUDIO_ID 0 +#define MIXART_FIRST_DIG_AUDIO_ID 8 + +static int mixart_enum_connectors(mixart_mgr_t *mgr) +{ + u32 k; + int err; + mixart_msg_t request; + mixart_enum_connector_resp_t connector; + mixart_audio_info_req_t audio_info_req; + mixart_audio_info_resp_t audio_info; + + audio_info_req.line_max_level = MIXART_FLOAT_P_22_0_TO_HEX; + audio_info_req.micro_max_level = MIXART_FLOAT_M_20_0_TO_HEX; + audio_info_req.cd_max_level = MIXART_FLOAT____0_0_TO_HEX; + + request.message_id = MSG_SYSTEM_ENUM_PLAY_CONNECTOR; + request.uid = (mixart_uid_t){0,0}; /* board num = 0 */ + request.data = NULL; + request.size = 0; + + err = snd_mixart_send_msg(mgr, &request, sizeof(connector), &connector); + if((err < 0) || (connector.error_code) || (connector.uid_count > MIXART_MAX_PHYS_CONNECTORS)) { + snd_printk(KERN_ERR "error MSG_SYSTEM_ENUM_PLAY_CONNECTOR\n"); + return -EINVAL; + } + + for(k=0; k < connector.uid_count; k++) { + mixart_pipe_t* pipe; + + if(k < MIXART_FIRST_DIG_AUDIO_ID) { + pipe = &mgr->chip[k/2]->pipe_out_ana; + } else { + pipe = &mgr->chip[(k-MIXART_FIRST_DIG_AUDIO_ID)/2]->pipe_out_dig; + } + if(k & 1) { + pipe->uid_right_connector = connector.uid[k]; /* odd */ + } else { + pipe->uid_left_connector = connector.uid[k]; /* even */ + } + + /* snd_printk(KERN_DEBUG "playback connector[%d].object_id = %x\n", k, connector.uid[k].object_id); */ + + /* TODO: really need send_msg MSG_CONNECTOR_GET_AUDIO_INFO for each connector ? perhaps for analog level caps ? */ + request.message_id = MSG_CONNECTOR_GET_AUDIO_INFO; + request.uid = connector.uid[k]; + request.data = &audio_info_req; + request.size = sizeof(audio_info_req); + + err = snd_mixart_send_msg(mgr, &request, sizeof(audio_info), &audio_info); + if( err < 0 ) { + snd_printk(KERN_ERR "error MSG_CONNECTOR_GET_AUDIO_INFO\n"); + return err; + } + /*snd_printk(KERN_DEBUG "play analog_info.analog_level_present = %x\n", audio_info.info.analog_info.analog_level_present);*/ + } + + request.message_id = MSG_SYSTEM_ENUM_RECORD_CONNECTOR; + request.uid = (mixart_uid_t){0,0}; /* board num = 0 */ + request.data = NULL; + request.size = 0; + + err = snd_mixart_send_msg(mgr, &request, sizeof(connector), &connector); + if((err < 0) || (connector.error_code) || (connector.uid_count > MIXART_MAX_PHYS_CONNECTORS)) { + snd_printk(KERN_ERR "error MSG_SYSTEM_ENUM_RECORD_CONNECTOR\n"); + return -EINVAL; + } + + for(k=0; k < connector.uid_count; k++) { + mixart_pipe_t* pipe; + + if(k < MIXART_FIRST_DIG_AUDIO_ID) { + pipe = &mgr->chip[k/2]->pipe_in_ana; + } else { + pipe = &mgr->chip[(k-MIXART_FIRST_DIG_AUDIO_ID)/2]->pipe_in_dig; + } + if(k & 1) { + pipe->uid_right_connector = connector.uid[k]; /* odd */ + } else { + pipe->uid_left_connector = connector.uid[k]; /* even */ + } + + /* snd_printk(KERN_DEBUG "capture connector[%d].object_id = %x\n", k, connector.uid[k].object_id); */ + + /* TODO: really need send_msg MSG_CONNECTOR_GET_AUDIO_INFO for each connector ? perhaps for analog level caps ? */ + request.message_id = MSG_CONNECTOR_GET_AUDIO_INFO; + request.uid = connector.uid[k]; + request.data = &audio_info_req; + request.size = sizeof(audio_info_req); + + err = snd_mixart_send_msg(mgr, &request, sizeof(audio_info), &audio_info); + if( err < 0 ) { + snd_printk(KERN_ERR "error MSG_CONNECTOR_GET_AUDIO_INFO\n"); + return err; + } + /*snd_printk(KERN_DEBUG "rec analog_info.analog_level_present = %x\n", audio_info.info.analog_info.analog_level_present);*/ + } + + return 0; +} + +static int mixart_enum_physio(mixart_mgr_t *mgr) +{ + u32 k; + int err; + mixart_msg_t request; + mixart_uid_t get_console_mgr; + mixart_return_uid_t console_mgr; + mixart_uid_enumeration_t phys_io; + + /* get the uid for the console manager */ + get_console_mgr.object_id = 0; + get_console_mgr.desc = MSG_CONSOLE_MANAGER | 0; /* cardindex = 0 */ + + request.message_id = MSG_CONSOLE_GET_CLOCK_UID; + request.uid = get_console_mgr; + request.data = &get_console_mgr; + request.size = sizeof(get_console_mgr); + + err = snd_mixart_send_msg(mgr, &request, sizeof(console_mgr), &console_mgr); + + if( (err < 0) || (console_mgr.error_code != 0) ) { + snd_printk(KERN_DEBUG "error MSG_CONSOLE_GET_CLOCK_UID : err=%x\n", console_mgr.error_code); + return -EINVAL; + } + + /* used later for clock issues ! */ + mgr->uid_console_manager = console_mgr.uid; + + request.message_id = MSG_SYSTEM_ENUM_PHYSICAL_IO; + request.uid = (mixart_uid_t){0,0}; + request.data = &console_mgr.uid; + request.size = sizeof(console_mgr.uid); + + err = snd_mixart_send_msg(mgr, &request, sizeof(phys_io), &phys_io); + if( (err < 0) || ( phys_io.error_code != 0 ) ) { + snd_printk(KERN_ERR "error MSG_SYSTEM_ENUM_PHYSICAL_IO err(%x) error_code(%x)\n", err, phys_io.error_code ); + return -EINVAL; + } + + snd_assert(phys_io.nb_uid >= (MIXART_MAX_CARDS * 2), return -EINVAL); /* min 2 phys io per card (analog in + analog out) */ + + for(k=0; knum_cards; k++) { + mgr->chip[k]->uid_in_analog_physio = phys_io.uid[k]; + mgr->chip[k]->uid_out_analog_physio = phys_io.uid[phys_io.nb_uid/2 + k]; + } + + return 0; +} + + +static int mixart_first_init(mixart_mgr_t *mgr) +{ + u32 k; + int err; + mixart_msg_t request; + + if((err = mixart_enum_connectors(mgr)) < 0) return err; + + if((err = mixart_enum_physio(mgr)) < 0) return err; + + /* send a synchro command to card (necessary to do this before first MSG_STREAM_START_STREAM_GRP_PACKET) */ + /* though why not here */ + request.message_id = MSG_SYSTEM_SEND_SYNCHRO_CMD; + request.uid = (mixart_uid_t){0,0}; + request.data = NULL; + request.size = 0; + /* this command has no data. response is a 32 bit status */ + err = snd_mixart_send_msg(mgr, &request, sizeof(k), &k); + if( (err < 0) || (k != 0) ) { + snd_printk(KERN_ERR "error MSG_SYSTEM_SEND_SYNCHRO_CMD\n"); + return err == 0 ? -EINVAL : err; + } + + return 0; +} + + +/* firmware base addresses (when hard coded) */ +#define MIXART_MOTHERBOARD_XLX_BASE_ADDRESS 0x00600000 + +static int mixart_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *dsp) +{ + mixart_mgr_t* mgr = snd_magic_cast(mixart_mgr_t, hw->private_data, return -ENXIO); + int err, card_index; + u32 status_xilinx, status_elf, status_daught; + u32 val; + + /* read motherboard xilinx status */ + status_xilinx = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_MXLX_STATUS_OFFSET )); + /* read elf status */ + status_elf = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_ELF_STATUS_OFFSET )); + /* read daughterboard xilinx status */ + status_daught = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_DXLX_STATUS_OFFSET )); + + /* motherboard xilinx status 5 will say that the board is performing a reset */ + if( status_xilinx == 5 ) { + snd_printk( KERN_ERR "miXart is resetting !\n"); + return -EAGAIN; /* try again later */ + } + + switch (dsp->index) { + case MIXART_MOTHERBOARD_XLX_INDEX: + + /* xilinx already loaded ? */ + if( status_xilinx == 4 ) { + snd_printk( KERN_DEBUG "xilinx is already loaded !\n"); + return 0; + } + /* the status should be 0 == "idle" */ + if( status_xilinx != 0 ) { + snd_printk( KERN_ERR "xilinx load error ! status = %d\n", status_xilinx); + return -EIO; /* modprob -r may help ? */ + } + + /* check xilinx validity */ + snd_assert(((u32*)(dsp->image))[0]==0xFFFFFFFF, return -EINVAL); + snd_assert(dsp->length % 4 == 0, return -EINVAL); + + /* set xilinx status to copying */ + writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET )); + + /* setup xilinx base address */ + writel_be( MIXART_MOTHERBOARD_XLX_BASE_ADDRESS, MIXART_MEM( mgr,MIXART_PSEUDOREG_MXLX_BASE_ADDR_OFFSET )); + /* setup code size for xilinx file */ + writel_be( dsp->length, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_SIZE_OFFSET )); + + /* copy xilinx code */ + if (copy_from_user_toio( MIXART_MEM( mgr, MIXART_MOTHERBOARD_XLX_BASE_ADDRESS), dsp->image, dsp->length)) + return -EFAULT; + + /* set xilinx status to copy finished */ + writel_be( 2, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET )); + + /* return, because no further processing needed */ + return 0; + + case MIXART_MOTHERBOARD_ELF_INDEX: + + if( status_elf == 4 ) { + snd_printk( KERN_DEBUG "elf file already loaded !\n"); + return 0; + } + + /* the status should be 0 == "idle" */ + if( status_elf != 0 ) { + snd_printk( KERN_ERR "elf load error ! status = %d\n", status_elf); + return -EIO; /* modprob -r may help ? */ + } + + /* wait for xilinx status == 4 */ + err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET, 1, 4, 500); /* 5sec */ + if (err < 0) { + snd_printk( KERN_ERR "xilinx was not loaded or could not be started\n"); + return err; + } + + /* init some data on the card */ + writel_be( 0, MIXART_MEM( mgr, MIXART_PSEUDOREG_BOARDNUMBER ) ); /* set miXart boardnumber to 0 */ + writel_be( 0, MIXART_MEM( mgr, MIXART_FLOWTABLE_PTR ) ); /* reset pointer to flow table on miXart */ + + /* set elf status to copying */ + writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET )); + + /* process the copying of the elf packets */ + err = mixart_load_elf( mgr, dsp); + if (err < 0) return err; + + /* set elf status to copy finished */ + writel_be( 2, MIXART_MEM( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET )); + + /* wait for elf status == 4 */ + err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET, 1, 4, 300); /* 3sec */ + if (err < 0) { + snd_printk( KERN_ERR "elf could not be started\n"); + return err; + } + + /* miXart waits at this point on the pointer to the flow table */ + writel_be( (u32)mgr->flowinfo_physaddr, MIXART_MEM( mgr, MIXART_FLOWTABLE_PTR ) ); /* give pointer of flow table to miXart */ + + return 0; /* return, another xilinx file has to be loaded before */ + + case MIXART_AESEBUBOARD_XLX_INDEX: + default: + + /* elf and xilinx should be loaded */ + if( (status_elf != 4) || (status_xilinx != 4) ) { + printk( KERN_ERR "xilinx or elf not successfully loaded\n"); + return -EIO; /* modprob -r may help ? */ + } + + /* wait for daughter detection != 0 */ + err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DBRD_PRESENCE_OFFSET, 0, 0, 30); /* 300msec */ + if (err < 0) { + snd_printk( KERN_ERR "error starting elf file\n"); + return err; + } + + /* the board type can now be retrieved */ + mgr->board_type = (DAUGHTER_TYPE_MASK & readl_be( MIXART_MEM( mgr, MIXART_PSEUDOREG_DBRD_TYPE_OFFSET))); + + if (mgr->board_type == MIXART_DAUGHTER_TYPE_NONE) + break; /* no daughter board; the file does not have to be loaded, continue after the switch */ + + /* only if aesebu daughter board presence (elf code must run) */ + if (mgr->board_type != MIXART_DAUGHTER_TYPE_AES ) + return -EINVAL; + + /* daughter should be idle */ + if( status_daught != 0 ) { + printk( KERN_ERR "daughter load error ! status = %d\n", status_daught); + return -EIO; /* modprob -r may help ? */ + } + + /* check daughterboard xilinx validity */ + snd_assert(((u32*)(dsp->image))[0]==0xFFFFFFFF, return -EINVAL); + snd_assert(dsp->length % 4 == 0, return -EINVAL); + + /* inform mixart about the size of the file */ + writel_be( dsp->length, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_SIZE_OFFSET )); + + /* set daughterboard status to 1 */ + writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET )); + + /* wait for status == 2 */ + err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET, 1, 2, 30); /* 300msec */ + if (err < 0) { + snd_printk( KERN_ERR "daughter board load error\n"); + return err; + } + + /* get the address where to write the file */ + val = readl_be( MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_BASE_ADDR_OFFSET )); + snd_assert(val != 0, return -EINVAL); + + /* copy daughterboard xilinx code */ + if (copy_from_user_toio( MIXART_MEM( mgr, val), dsp->image, dsp->length)) + return -EFAULT; + + /* set daughterboard status to 4 */ + writel_be( 4, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET )); + + /* continue with init */ + break; + } /* end of switch file index*/ + + /* wait for daughter status == 3 */ + err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET, 1, 3, 300); /* 3sec */ + if (err < 0) { + snd_printk( KERN_ERR "daughter board could not be initialised\n"); + return err; + } + + /* init mailbox (communication with embedded) */ + snd_mixart_init_mailbox(mgr); + + /* first communication with embedded */ + err = mixart_first_init(mgr); + if (err < 0) { + snd_printk( KERN_ERR "miXart could not be set up\n"); + return err; + } + + /* create devices and mixer in accordance with HW options*/ + for (card_index = 0; card_index < mgr->num_cards; card_index++) { + mixart_t *chip = mgr->chip[card_index]; + + if ((err = snd_mixart_create_pcm(chip)) < 0) + return err; + + if (card_index == 0) { + if ((err = snd_mixart_create_mixer(chip->mgr)) < 0) + return err; + } + + if ((err = snd_card_register(chip->card)) < 0) + return err; + }; + + snd_printdd("miXart firmware downloaded and successfully set up\n"); + + return 0; +} + + +int snd_mixart_hwdep_new(mixart_mgr_t *mgr) +{ + int err; + snd_hwdep_t *hw; + + /* only create hwdep interface for first cardX (see "index" module parameter)*/ + if ((err = snd_hwdep_new(mgr->chip[0]->card, SND_MIXART_HWDEP_ID, 0, &hw)) < 0) + return err; + + hw->iface = SNDRV_HWDEP_IFACE_MIXART; + hw->private_data = mgr; + hw->ops.open = mixart_hwdep_open; + hw->ops.release = mixart_hwdep_release; + hw->ops.dsp_status = mixart_hwdep_dsp_status; + hw->ops.dsp_load = mixart_hwdep_dsp_load; + hw->exclusive = 1; + sprintf(hw->name, SND_MIXART_HWDEP_ID); + mgr->hwdep = hw; + mgr->hwdep->dsp_loaded = 0; + return 0; +} diff -Nru a/sound/pci/mixart/mixart_hwdep.h b/sound/pci/mixart/mixart_hwdep.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/mixart/mixart_hwdep.h Sun Feb 29 12:58:39 2004 @@ -0,0 +1,146 @@ +/* + * Driver for Digigram miXart soundcards + * + * definitions and makros for basic card access + * + * Copyright (c) 2003 by Digigram + * + * 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 + */ + +#ifndef __SOUND_MIXART_HWDEP_H +#define __SOUND_MIXART_HWDEP_H + +#include + +#define readl_be(x) be32_to_cpu(__raw_readl(x)) +#define writel_be(data,addr) __raw_writel(cpu_to_be32(data),addr) + +#define readl_le(x) le32_to_cpu(__raw_readl(x)) +#define writel_le(data,addr) __raw_writel(cpu_to_le32(data),addr) + +#define MIXART_MEM(mgr,x) ((mgr)->mem[0].virt + (x)) +#define MIXART_REG(mgr,x) ((mgr)->mem[1].virt + (x)) + + +/* Daughter board Type */ +#define DAUGHTER_TYPE_MASK 0x0F +#define DAUGHTER_VER_MASK 0xF0 +#define DAUGHTER_TYPEVER_MASK (DAUGHTER_TYPE_MASK|DAUGHTER_VER_MASK) + +#define MIXART_DAUGHTER_TYPE_NONE 0x00 +#define MIXART_DAUGHTER_TYPE_COBRANET 0x08 +#define MIXART_DAUGHTER_TYPE_AES 0x0E + + + +#define MIXART_BA0_SIZE (16 * 1024 * 1024) /* 16M */ +#define MIXART_BA1_SIZE (4 * 1024) /* 4k */ + +/* + * -----------BAR 0 -------------------------------------------------------------------------------------------------------- + */ +#define MIXART_PSEUDOREG 0x2000 /* base address for pseudoregister */ + +#define MIXART_PSEUDOREG_BOARDNUMBER MIXART_PSEUDOREG+0 /* board number */ + +/* perfmeter (available when elf loaded)*/ +#define MIXART_PSEUDOREG_PERF_STREAM_LOAD_OFFSET MIXART_PSEUDOREG+0x70 /* streaming load */ +#define MIXART_PSEUDOREG_PERF_SYSTEM_LOAD_OFFSET MIXART_PSEUDOREG+0x78 /* system load (reference)*/ +#define MIXART_PSEUDOREG_PERF_MAILBX_LOAD_OFFSET MIXART_PSEUDOREG+0x7C /* mailbox load */ +#define MIXART_PSEUDOREG_PERF_INTERR_LOAD_OFFSET MIXART_PSEUDOREG+0x74 /* interrupt handling load */ + +/* motherboard xilinx loader info */ +#define MIXART_PSEUDOREG_MXLX_BASE_ADDR_OFFSET MIXART_PSEUDOREG+0x9C /* 0x00600000 */ +#define MIXART_PSEUDOREG_MXLX_SIZE_OFFSET MIXART_PSEUDOREG+0xA0 /* xilinx size in bytes */ +#define MIXART_PSEUDOREG_MXLX_STATUS_OFFSET MIXART_PSEUDOREG+0xA4 /* status = EMBEBBED_STAT_XXX */ + +/* elf loader info */ +#define MIXART_PSEUDOREG_ELF_STATUS_OFFSET MIXART_PSEUDOREG+0xB0 /* status = EMBEBBED_STAT_XXX */ + +/* +* after the elf code is loaded, and the flowtable info was passed to it, +* the driver polls on this address, until it shows 1 (presence) or 2 (absence) +* once it is non-zero, the daughter board type may be read +*/ +#define MIXART_PSEUDOREG_DBRD_PRESENCE_OFFSET MIXART_PSEUDOREG+0x990 + +/* Global info structure */ +#define MIXART_PSEUDOREG_DBRD_TYPE_OFFSET MIXART_PSEUDOREG+0x994 /* Type and version of daughterboard */ + + +/* daughterboard xilinx loader info */ +#define MIXART_PSEUDOREG_DXLX_BASE_ADDR_OFFSET MIXART_PSEUDOREG+0x998 /* get the address here where to write the file */ +#define MIXART_PSEUDOREG_DXLX_SIZE_OFFSET MIXART_PSEUDOREG+0x99C /* xilinx size in bytes */ +#define MIXART_PSEUDOREG_DXLX_STATUS_OFFSET MIXART_PSEUDOREG+0x9A0 /* status = EMBEBBED_STAT_XXX */ + +/* */ +#define MIXART_FLOWTABLE_PTR 0x3000 /* pointer to flow table */ + +/* mailbox addresses */ + +/* message DRV -> EMB */ +#define MSG_INBOUND_POST_HEAD 0x010008 /* DRV posts MF + increment4 */ +#define MSG_INBOUND_POST_TAIL 0x01000C /* EMB gets MF + increment4 */ +/* message EMB -> DRV */ +#define MSG_OUTBOUND_POST_TAIL 0x01001C /* DRV gets MF + increment4 */ +#define MSG_OUTBOUND_POST_HEAD 0x010018 /* EMB posts MF + increment4 */ +/* Get Free Frames */ +#define MSG_INBOUND_FREE_TAIL 0x010004 /* DRV gets MFA + increment4 */ +#define MSG_OUTBOUND_FREE_TAIL 0x010014 /* EMB gets MFA + increment4 */ +/* Put Free Frames */ +#define MSG_OUTBOUND_FREE_HEAD 0x010010 /* DRV puts MFA + increment4 */ +#define MSG_INBOUND_FREE_HEAD 0x010000 /* EMB puts MFA + increment4 */ + +/* firmware addresses of the message fifos */ +#define MSG_BOUND_STACK_SIZE 0x004000 /* size of each following stack */ +/* posted messages */ +#define MSG_OUTBOUND_POST_STACK 0x108000 /* stack of messages to the DRV */ +#define MSG_INBOUND_POST_STACK 0x104000 /* stack of messages to the EMB */ +/* available empty messages */ +#define MSG_OUTBOUND_FREE_STACK 0x10C000 /* stack of free enveloped for EMB */ +#define MSG_INBOUND_FREE_STACK 0x100000 /* stack of free enveloped for DRV */ + + +/* defines for mailbox message frames */ +#define MSG_FRAME_OFFSET 0x64 +#define MSG_FRAME_SIZE 0x6400 +#define MSG_FRAME_NUMBER 32 +#define MSG_FROM_AGENT_ITMF_OFFSET (MSG_FRAME_OFFSET + (MSG_FRAME_SIZE * MSG_FRAME_NUMBER)) +#define MSG_TO_AGENT_ITMF_OFFSET (MSG_FROM_AGENT_ITMF_OFFSET + MSG_FRAME_SIZE) +#define MSG_HOST_RSC_PROTECTION (MSG_TO_AGENT_ITMF_OFFSET + MSG_FRAME_SIZE) +#define MSG_AGENT_RSC_PROTECTION (MSG_HOST_RSC_PROTECTION + 4) + + +/* + * -----------BAR 1 -------------------------------------------------------------------------------------------------------- + */ + +/* interrupt addresses and constants */ +#define MIXART_PCI_OMIMR_OFFSET 0x34 /* outbound message interrupt mask register */ +#define MIXART_PCI_OMISR_OFFSET 0x30 /* outbound message interrupt status register */ +#define MIXART_PCI_ODBR_OFFSET 0x60 /* outbound doorbell register */ + +#define MIXART_BA1_BRUTAL_RESET_OFFSET 0x68 /* write 1 in LSBit to reset board */ + +#define MIXART_HOST_ALL_INTERRUPT_MASKED 0x02B /* 0000 0010 1011 */ +#define MIXART_ALLOW_OUTBOUND_DOORBELL 0x023 /* 0000 0010 0011 */ +#define MIXART_OIDI 0x008 /* 0000 0000 1000 */ + + +/* exported */ +int snd_mixart_hwdep_new(mixart_mgr_t *mgr); + +#endif /* __SOUND_MIXART_HWDEP_H */ diff -Nru a/sound/pci/mixart/mixart_mixer.c b/sound/pci/mixart/mixart_mixer.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/mixart/mixart_mixer.c Sun Feb 29 12:58:39 2004 @@ -0,0 +1,1138 @@ +/* + * Driver for Digigram miXart soundcards + * + * mixer callbacks + * + * Copyright (c) 2003 by Digigram + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include "mixart.h" +#include "mixart_core.h" +#include "mixart_hwdep.h" +#include +#include "mixart_mixer.h" + +#define chip_t mixart_t + +static u32 mixart_analog_level[256] = { + 0xc2c00000, /* [000] -96.0 dB */ + 0xc2bf0000, /* [001] -95.5 dB */ + 0xc2be0000, /* [002] -95.0 dB */ + 0xc2bd0000, /* [003] -94.5 dB */ + 0xc2bc0000, /* [004] -94.0 dB */ + 0xc2bb0000, /* [005] -93.5 dB */ + 0xc2ba0000, /* [006] -93.0 dB */ + 0xc2b90000, /* [007] -92.5 dB */ + 0xc2b80000, /* [008] -92.0 dB */ + 0xc2b70000, /* [009] -91.5 dB */ + 0xc2b60000, /* [010] -91.0 dB */ + 0xc2b50000, /* [011] -90.5 dB */ + 0xc2b40000, /* [012] -90.0 dB */ + 0xc2b30000, /* [013] -89.5 dB */ + 0xc2b20000, /* [014] -89.0 dB */ + 0xc2b10000, /* [015] -88.5 dB */ + 0xc2b00000, /* [016] -88.0 dB */ + 0xc2af0000, /* [017] -87.5 dB */ + 0xc2ae0000, /* [018] -87.0 dB */ + 0xc2ad0000, /* [019] -86.5 dB */ + 0xc2ac0000, /* [020] -86.0 dB */ + 0xc2ab0000, /* [021] -85.5 dB */ + 0xc2aa0000, /* [022] -85.0 dB */ + 0xc2a90000, /* [023] -84.5 dB */ + 0xc2a80000, /* [024] -84.0 dB */ + 0xc2a70000, /* [025] -83.5 dB */ + 0xc2a60000, /* [026] -83.0 dB */ + 0xc2a50000, /* [027] -82.5 dB */ + 0xc2a40000, /* [028] -82.0 dB */ + 0xc2a30000, /* [029] -81.5 dB */ + 0xc2a20000, /* [030] -81.0 dB */ + 0xc2a10000, /* [031] -80.5 dB */ + 0xc2a00000, /* [032] -80.0 dB */ + 0xc29f0000, /* [033] -79.5 dB */ + 0xc29e0000, /* [034] -79.0 dB */ + 0xc29d0000, /* [035] -78.5 dB */ + 0xc29c0000, /* [036] -78.0 dB */ + 0xc29b0000, /* [037] -77.5 dB */ + 0xc29a0000, /* [038] -77.0 dB */ + 0xc2990000, /* [039] -76.5 dB */ + 0xc2980000, /* [040] -76.0 dB */ + 0xc2970000, /* [041] -75.5 dB */ + 0xc2960000, /* [042] -75.0 dB */ + 0xc2950000, /* [043] -74.5 dB */ + 0xc2940000, /* [044] -74.0 dB */ + 0xc2930000, /* [045] -73.5 dB */ + 0xc2920000, /* [046] -73.0 dB */ + 0xc2910000, /* [047] -72.5 dB */ + 0xc2900000, /* [048] -72.0 dB */ + 0xc28f0000, /* [049] -71.5 dB */ + 0xc28e0000, /* [050] -71.0 dB */ + 0xc28d0000, /* [051] -70.5 dB */ + 0xc28c0000, /* [052] -70.0 dB */ + 0xc28b0000, /* [053] -69.5 dB */ + 0xc28a0000, /* [054] -69.0 dB */ + 0xc2890000, /* [055] -68.5 dB */ + 0xc2880000, /* [056] -68.0 dB */ + 0xc2870000, /* [057] -67.5 dB */ + 0xc2860000, /* [058] -67.0 dB */ + 0xc2850000, /* [059] -66.5 dB */ + 0xc2840000, /* [060] -66.0 dB */ + 0xc2830000, /* [061] -65.5 dB */ + 0xc2820000, /* [062] -65.0 dB */ + 0xc2810000, /* [063] -64.5 dB */ + 0xc2800000, /* [064] -64.0 dB */ + 0xc27e0000, /* [065] -63.5 dB */ + 0xc27c0000, /* [066] -63.0 dB */ + 0xc27a0000, /* [067] -62.5 dB */ + 0xc2780000, /* [068] -62.0 dB */ + 0xc2760000, /* [069] -61.5 dB */ + 0xc2740000, /* [070] -61.0 dB */ + 0xc2720000, /* [071] -60.5 dB */ + 0xc2700000, /* [072] -60.0 dB */ + 0xc26e0000, /* [073] -59.5 dB */ + 0xc26c0000, /* [074] -59.0 dB */ + 0xc26a0000, /* [075] -58.5 dB */ + 0xc2680000, /* [076] -58.0 dB */ + 0xc2660000, /* [077] -57.5 dB */ + 0xc2640000, /* [078] -57.0 dB */ + 0xc2620000, /* [079] -56.5 dB */ + 0xc2600000, /* [080] -56.0 dB */ + 0xc25e0000, /* [081] -55.5 dB */ + 0xc25c0000, /* [082] -55.0 dB */ + 0xc25a0000, /* [083] -54.5 dB */ + 0xc2580000, /* [084] -54.0 dB */ + 0xc2560000, /* [085] -53.5 dB */ + 0xc2540000, /* [086] -53.0 dB */ + 0xc2520000, /* [087] -52.5 dB */ + 0xc2500000, /* [088] -52.0 dB */ + 0xc24e0000, /* [089] -51.5 dB */ + 0xc24c0000, /* [090] -51.0 dB */ + 0xc24a0000, /* [091] -50.5 dB */ + 0xc2480000, /* [092] -50.0 dB */ + 0xc2460000, /* [093] -49.5 dB */ + 0xc2440000, /* [094] -49.0 dB */ + 0xc2420000, /* [095] -48.5 dB */ + 0xc2400000, /* [096] -48.0 dB */ + 0xc23e0000, /* [097] -47.5 dB */ + 0xc23c0000, /* [098] -47.0 dB */ + 0xc23a0000, /* [099] -46.5 dB */ + 0xc2380000, /* [100] -46.0 dB */ + 0xc2360000, /* [101] -45.5 dB */ + 0xc2340000, /* [102] -45.0 dB */ + 0xc2320000, /* [103] -44.5 dB */ + 0xc2300000, /* [104] -44.0 dB */ + 0xc22e0000, /* [105] -43.5 dB */ + 0xc22c0000, /* [106] -43.0 dB */ + 0xc22a0000, /* [107] -42.5 dB */ + 0xc2280000, /* [108] -42.0 dB */ + 0xc2260000, /* [109] -41.5 dB */ + 0xc2240000, /* [110] -41.0 dB */ + 0xc2220000, /* [111] -40.5 dB */ + 0xc2200000, /* [112] -40.0 dB */ + 0xc21e0000, /* [113] -39.5 dB */ + 0xc21c0000, /* [114] -39.0 dB */ + 0xc21a0000, /* [115] -38.5 dB */ + 0xc2180000, /* [116] -38.0 dB */ + 0xc2160000, /* [117] -37.5 dB */ + 0xc2140000, /* [118] -37.0 dB */ + 0xc2120000, /* [119] -36.5 dB */ + 0xc2100000, /* [120] -36.0 dB */ + 0xc20e0000, /* [121] -35.5 dB */ + 0xc20c0000, /* [122] -35.0 dB */ + 0xc20a0000, /* [123] -34.5 dB */ + 0xc2080000, /* [124] -34.0 dB */ + 0xc2060000, /* [125] -33.5 dB */ + 0xc2040000, /* [126] -33.0 dB */ + 0xc2020000, /* [127] -32.5 dB */ + 0xc2000000, /* [128] -32.0 dB */ + 0xc1fc0000, /* [129] -31.5 dB */ + 0xc1f80000, /* [130] -31.0 dB */ + 0xc1f40000, /* [131] -30.5 dB */ + 0xc1f00000, /* [132] -30.0 dB */ + 0xc1ec0000, /* [133] -29.5 dB */ + 0xc1e80000, /* [134] -29.0 dB */ + 0xc1e40000, /* [135] -28.5 dB */ + 0xc1e00000, /* [136] -28.0 dB */ + 0xc1dc0000, /* [137] -27.5 dB */ + 0xc1d80000, /* [138] -27.0 dB */ + 0xc1d40000, /* [139] -26.5 dB */ + 0xc1d00000, /* [140] -26.0 dB */ + 0xc1cc0000, /* [141] -25.5 dB */ + 0xc1c80000, /* [142] -25.0 dB */ + 0xc1c40000, /* [143] -24.5 dB */ + 0xc1c00000, /* [144] -24.0 dB */ + 0xc1bc0000, /* [145] -23.5 dB */ + 0xc1b80000, /* [146] -23.0 dB */ + 0xc1b40000, /* [147] -22.5 dB */ + 0xc1b00000, /* [148] -22.0 dB */ + 0xc1ac0000, /* [149] -21.5 dB */ + 0xc1a80000, /* [150] -21.0 dB */ + 0xc1a40000, /* [151] -20.5 dB */ + 0xc1a00000, /* [152] -20.0 dB */ + 0xc19c0000, /* [153] -19.5 dB */ + 0xc1980000, /* [154] -19.0 dB */ + 0xc1940000, /* [155] -18.5 dB */ + 0xc1900000, /* [156] -18.0 dB */ + 0xc18c0000, /* [157] -17.5 dB */ + 0xc1880000, /* [158] -17.0 dB */ + 0xc1840000, /* [159] -16.5 dB */ + 0xc1800000, /* [160] -16.0 dB */ + 0xc1780000, /* [161] -15.5 dB */ + 0xc1700000, /* [162] -15.0 dB */ + 0xc1680000, /* [163] -14.5 dB */ + 0xc1600000, /* [164] -14.0 dB */ + 0xc1580000, /* [165] -13.5 dB */ + 0xc1500000, /* [166] -13.0 dB */ + 0xc1480000, /* [167] -12.5 dB */ + 0xc1400000, /* [168] -12.0 dB */ + 0xc1380000, /* [169] -11.5 dB */ + 0xc1300000, /* [170] -11.0 dB */ + 0xc1280000, /* [171] -10.5 dB */ + 0xc1200000, /* [172] -10.0 dB */ + 0xc1180000, /* [173] -9.5 dB */ + 0xc1100000, /* [174] -9.0 dB */ + 0xc1080000, /* [175] -8.5 dB */ + 0xc1000000, /* [176] -8.0 dB */ + 0xc0f00000, /* [177] -7.5 dB */ + 0xc0e00000, /* [178] -7.0 dB */ + 0xc0d00000, /* [179] -6.5 dB */ + 0xc0c00000, /* [180] -6.0 dB */ + 0xc0b00000, /* [181] -5.5 dB */ + 0xc0a00000, /* [182] -5.0 dB */ + 0xc0900000, /* [183] -4.5 dB */ + 0xc0800000, /* [184] -4.0 dB */ + 0xc0600000, /* [185] -3.5 dB */ + 0xc0400000, /* [186] -3.0 dB */ + 0xc0200000, /* [187] -2.5 dB */ + 0xc0000000, /* [188] -2.0 dB */ + 0xbfc00000, /* [189] -1.5 dB */ + 0xbf800000, /* [190] -1.0 dB */ + 0xbf000000, /* [191] -0.5 dB */ + 0x00000000, /* [192] 0.0 dB */ + 0x3f000000, /* [193] 0.5 dB */ + 0x3f800000, /* [194] 1.0 dB */ + 0x3fc00000, /* [195] 1.5 dB */ + 0x40000000, /* [196] 2.0 dB */ + 0x40200000, /* [197] 2.5 dB */ + 0x40400000, /* [198] 3.0 dB */ + 0x40600000, /* [199] 3.5 dB */ + 0x40800000, /* [200] 4.0 dB */ + 0x40900000, /* [201] 4.5 dB */ + 0x40a00000, /* [202] 5.0 dB */ + 0x40b00000, /* [203] 5.5 dB */ + 0x40c00000, /* [204] 6.0 dB */ + 0x40d00000, /* [205] 6.5 dB */ + 0x40e00000, /* [206] 7.0 dB */ + 0x40f00000, /* [207] 7.5 dB */ + 0x41000000, /* [208] 8.0 dB */ + 0x41080000, /* [209] 8.5 dB */ + 0x41100000, /* [210] 9.0 dB */ + 0x41180000, /* [211] 9.5 dB */ + 0x41200000, /* [212] 10.0 dB */ + 0x41280000, /* [213] 10.5 dB */ + 0x41300000, /* [214] 11.0 dB */ + 0x41380000, /* [215] 11.5 dB */ + 0x41400000, /* [216] 12.0 dB */ + 0x41480000, /* [217] 12.5 dB */ + 0x41500000, /* [218] 13.0 dB */ + 0x41580000, /* [219] 13.5 dB */ + 0x41600000, /* [220] 14.0 dB */ + 0x41680000, /* [221] 14.5 dB */ + 0x41700000, /* [222] 15.0 dB */ + 0x41780000, /* [223] 15.5 dB */ + 0x41800000, /* [224] 16.0 dB */ + 0x41840000, /* [225] 16.5 dB */ + 0x41880000, /* [226] 17.0 dB */ + 0x418c0000, /* [227] 17.5 dB */ + 0x41900000, /* [228] 18.0 dB */ + 0x41940000, /* [229] 18.5 dB */ + 0x41980000, /* [230] 19.0 dB */ + 0x419c0000, /* [231] 19.5 dB */ + 0x41a00000, /* [232] 20.0 dB */ + 0x41a40000, /* [233] 20.5 dB */ + 0x41a80000, /* [234] 21.0 dB */ + 0x41ac0000, /* [235] 21.5 dB */ + 0x41b00000, /* [236] 22.0 dB */ + 0x41b40000, /* [237] 22.5 dB */ + 0x41b80000, /* [238] 23.0 dB */ + 0x41bc0000, /* [239] 23.5 dB */ + 0x41c00000, /* [240] 24.0 dB */ + 0x41c40000, /* [241] 24.5 dB */ + 0x41c80000, /* [242] 25.0 dB */ + 0x41cc0000, /* [243] 25.5 dB */ + 0x41d00000, /* [244] 26.0 dB */ + 0x41d40000, /* [245] 26.5 dB */ + 0x41d80000, /* [246] 27.0 dB */ + 0x41dc0000, /* [247] 27.5 dB */ + 0x41e00000, /* [248] 28.0 dB */ + 0x41e40000, /* [249] 28.5 dB */ + 0x41e80000, /* [250] 29.0 dB */ + 0x41ec0000, /* [251] 29.5 dB */ + 0x41f00000, /* [252] 30.0 dB */ + 0x41f40000, /* [253] 30.5 dB */ + 0x41f80000, /* [254] 31.0 dB */ + 0x41fc0000, /* [255] 31.5 dB */ +}; + +#define MIXART_ANALOG_CAPTURE_LEVEL_MIN 0 /* -96.0 dB + 8.0 dB = -88.0 dB */ +#define MIXART_ANALOG_CAPTURE_LEVEL_MAX 255 /* 31.5 dB + 8.0 dB = 39.5 dB */ +#define MIXART_ANALOG_CAPTURE_ZERO_LEVEL 176 /* -8.0 dB + 8.0 dB = 0.0 dB */ + +#define MIXART_ANALOG_PLAYBACK_LEVEL_MIN 0 /* -96.0 dB + 1.5 dB = -94.5 dB (possible is down to (-114.0+1.5)dB) */ +#define MIXART_ANALOG_PLAYBACK_LEVEL_MAX 192 /* 0.0 dB + 1.5 dB = 1.5 dB */ +#define MIXART_ANALOG_PLAYBACK_ZERO_LEVEL 189 /* -1.5 dB + 1.5 dB = 0.0 dB */ + +static int mixart_update_analog_audio_level(mixart_t* chip, int is_capture) +{ + int i, err; + mixart_msg_t request; + mixart_io_level_t io_level; + mixart_return_uid_t resp; + + memset(&io_level, 0, sizeof(io_level)); + io_level.channel = -1; /* left and right */ + + for(i=0; i<2; i++) { + if(is_capture) { + io_level.level[i].analog_level = mixart_analog_level[chip->analog_capture_volume[i]]; + } else { + if(chip->analog_playback_active[i]) + io_level.level[i].analog_level = mixart_analog_level[chip->analog_playback_volume[i]]; + else + io_level.level[i].analog_level = mixart_analog_level[MIXART_ANALOG_PLAYBACK_LEVEL_MIN]; + } + } + + if(is_capture) request.uid = chip->uid_in_analog_physio; + else request.uid = chip->uid_out_analog_physio; + request.message_id = MSG_PHYSICALIO_SET_LEVEL; + request.data = &io_level; + request.size = sizeof(io_level); + + err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp); + if((err<0) || (resp.error_code)) { + snd_printk(KERN_DEBUG "error MSG_PHYSICALIO_SET_LEVEL card(%d) is_capture(%d) error_code(%x)\n", chip->chip_idx, is_capture, resp.error_code); + return -EINVAL; + } + return 0; +} + +/* + * analog level control + */ +static int mixart_analog_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + if(kcontrol->private_value == 0) { /* playback */ + uinfo->value.integer.min = MIXART_ANALOG_PLAYBACK_LEVEL_MIN; /* -96 dB */ + uinfo->value.integer.max = MIXART_ANALOG_PLAYBACK_LEVEL_MAX; /* 0 dB */ + } else { /* capture */ + uinfo->value.integer.min = MIXART_ANALOG_CAPTURE_LEVEL_MIN; /* -96 dB */ + uinfo->value.integer.max = MIXART_ANALOG_CAPTURE_LEVEL_MAX; /* 31.5 dB */ + } + return 0; +} + +static int mixart_analog_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + mixart_t *chip = snd_kcontrol_chip(kcontrol); + down(&chip->mgr->mixer_mutex); + if(kcontrol->private_value == 0) { /* playback */ + ucontrol->value.integer.value[0] = chip->analog_playback_volume[0]; + ucontrol->value.integer.value[1] = chip->analog_playback_volume[1]; + } else { /* capture */ + ucontrol->value.integer.value[0] = chip->analog_capture_volume[0]; + ucontrol->value.integer.value[1] = chip->analog_capture_volume[1]; + } + up(&chip->mgr->mixer_mutex); + return 0; +} + +static int mixart_analog_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + mixart_t *chip = snd_kcontrol_chip(kcontrol); + int changed = 0; + int is_capture, i; + + down(&chip->mgr->mixer_mutex); + is_capture = (kcontrol->private_value != 0); + for(i=0; i<2; i++) { + int new_volume = ucontrol->value.integer.value[i]; + int* stored_volume = is_capture ? &chip->analog_capture_volume[i] : &chip->analog_playback_volume[i]; + if(*stored_volume != new_volume) { + *stored_volume = new_volume; + changed = 1; + } + } + if(changed) mixart_update_analog_audio_level(chip, is_capture); + up(&chip->mgr->mixer_mutex); + return changed; +} + +static snd_kcontrol_new_t mixart_control_analog_level = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* name will be filled later */ + .info = mixart_analog_vol_info, + .get = mixart_analog_vol_get, + .put = mixart_analog_vol_put, +}; + +/* shared */ +static int mixart_sw_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int mixart_audio_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + mixart_t *chip = snd_kcontrol_chip(kcontrol); + + down(&chip->mgr->mixer_mutex); + ucontrol->value.integer.value[0] = chip->analog_playback_active[0]; + ucontrol->value.integer.value[1] = chip->analog_playback_active[1]; + up(&chip->mgr->mixer_mutex); + return 0; +} + +static int mixart_audio_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + mixart_t *chip = snd_kcontrol_chip(kcontrol); + int i, changed = 0; + down(&chip->mgr->mixer_mutex); + for(i=0; i<2; i++) { + if(chip->analog_playback_active[i] != ucontrol->value.integer.value[i]) { + chip->analog_playback_active[i] = ucontrol->value.integer.value[i]; + changed = 1; + } + } + if(changed) mixart_update_analog_audio_level(chip, 0); /* update playback levels */ + up(&chip->mgr->mixer_mutex); + return changed; +} + +static snd_kcontrol_new_t mixart_control_output_switch = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = mixart_sw_info, /* shared */ + .get = mixart_audio_sw_get, + .put = mixart_audio_sw_put +}; + +static u32 mixart_digital_level[256] = { + 0x00000000, /* [000] = 0.00e+000 = mute if <= -109.5dB */ + 0x366e1c7a, /* [001] = 3.55e-006 = pow(10.0, 0.05 * -109.0dB) */ + 0x367c3860, /* [002] = 3.76e-006 = pow(10.0, 0.05 * -108.5dB) */ + 0x36859525, /* [003] = 3.98e-006 = pow(10.0, 0.05 * -108.0dB) */ + 0x368d7f74, /* [004] = 4.22e-006 = pow(10.0, 0.05 * -107.5dB) */ + 0x3695e1d4, /* [005] = 4.47e-006 = pow(10.0, 0.05 * -107.0dB) */ + 0x369ec362, /* [006] = 4.73e-006 = pow(10.0, 0.05 * -106.5dB) */ + 0x36a82ba8, /* [007] = 5.01e-006 = pow(10.0, 0.05 * -106.0dB) */ + 0x36b222a0, /* [008] = 5.31e-006 = pow(10.0, 0.05 * -105.5dB) */ + 0x36bcb0c1, /* [009] = 5.62e-006 = pow(10.0, 0.05 * -105.0dB) */ + 0x36c7defd, /* [010] = 5.96e-006 = pow(10.0, 0.05 * -104.5dB) */ + 0x36d3b6d3, /* [011] = 6.31e-006 = pow(10.0, 0.05 * -104.0dB) */ + 0x36e0424e, /* [012] = 6.68e-006 = pow(10.0, 0.05 * -103.5dB) */ + 0x36ed8c14, /* [013] = 7.08e-006 = pow(10.0, 0.05 * -103.0dB) */ + 0x36fb9f6c, /* [014] = 7.50e-006 = pow(10.0, 0.05 * -102.5dB) */ + 0x37054423, /* [015] = 7.94e-006 = pow(10.0, 0.05 * -102.0dB) */ + 0x370d29a5, /* [016] = 8.41e-006 = pow(10.0, 0.05 * -101.5dB) */ + 0x371586f0, /* [017] = 8.91e-006 = pow(10.0, 0.05 * -101.0dB) */ + 0x371e631b, /* [018] = 9.44e-006 = pow(10.0, 0.05 * -100.5dB) */ + 0x3727c5ac, /* [019] = 1.00e-005 = pow(10.0, 0.05 * -100.0dB) */ + 0x3731b69a, /* [020] = 1.06e-005 = pow(10.0, 0.05 * -99.5dB) */ + 0x373c3e53, /* [021] = 1.12e-005 = pow(10.0, 0.05 * -99.0dB) */ + 0x374765c8, /* [022] = 1.19e-005 = pow(10.0, 0.05 * -98.5dB) */ + 0x3753366f, /* [023] = 1.26e-005 = pow(10.0, 0.05 * -98.0dB) */ + 0x375fba4f, /* [024] = 1.33e-005 = pow(10.0, 0.05 * -97.5dB) */ + 0x376cfc07, /* [025] = 1.41e-005 = pow(10.0, 0.05 * -97.0dB) */ + 0x377b06d5, /* [026] = 1.50e-005 = pow(10.0, 0.05 * -96.5dB) */ + 0x3784f352, /* [027] = 1.58e-005 = pow(10.0, 0.05 * -96.0dB) */ + 0x378cd40b, /* [028] = 1.68e-005 = pow(10.0, 0.05 * -95.5dB) */ + 0x37952c42, /* [029] = 1.78e-005 = pow(10.0, 0.05 * -95.0dB) */ + 0x379e030e, /* [030] = 1.88e-005 = pow(10.0, 0.05 * -94.5dB) */ + 0x37a75fef, /* [031] = 2.00e-005 = pow(10.0, 0.05 * -94.0dB) */ + 0x37b14ad5, /* [032] = 2.11e-005 = pow(10.0, 0.05 * -93.5dB) */ + 0x37bbcc2c, /* [033] = 2.24e-005 = pow(10.0, 0.05 * -93.0dB) */ + 0x37c6ecdd, /* [034] = 2.37e-005 = pow(10.0, 0.05 * -92.5dB) */ + 0x37d2b65a, /* [035] = 2.51e-005 = pow(10.0, 0.05 * -92.0dB) */ + 0x37df32a3, /* [036] = 2.66e-005 = pow(10.0, 0.05 * -91.5dB) */ + 0x37ec6c50, /* [037] = 2.82e-005 = pow(10.0, 0.05 * -91.0dB) */ + 0x37fa6e9b, /* [038] = 2.99e-005 = pow(10.0, 0.05 * -90.5dB) */ + 0x3804a2b3, /* [039] = 3.16e-005 = pow(10.0, 0.05 * -90.0dB) */ + 0x380c7ea4, /* [040] = 3.35e-005 = pow(10.0, 0.05 * -89.5dB) */ + 0x3814d1cc, /* [041] = 3.55e-005 = pow(10.0, 0.05 * -89.0dB) */ + 0x381da33c, /* [042] = 3.76e-005 = pow(10.0, 0.05 * -88.5dB) */ + 0x3826fa6f, /* [043] = 3.98e-005 = pow(10.0, 0.05 * -88.0dB) */ + 0x3830df51, /* [044] = 4.22e-005 = pow(10.0, 0.05 * -87.5dB) */ + 0x383b5a49, /* [045] = 4.47e-005 = pow(10.0, 0.05 * -87.0dB) */ + 0x3846743b, /* [046] = 4.73e-005 = pow(10.0, 0.05 * -86.5dB) */ + 0x38523692, /* [047] = 5.01e-005 = pow(10.0, 0.05 * -86.0dB) */ + 0x385eab48, /* [048] = 5.31e-005 = pow(10.0, 0.05 * -85.5dB) */ + 0x386bdcf1, /* [049] = 5.62e-005 = pow(10.0, 0.05 * -85.0dB) */ + 0x3879d6bc, /* [050] = 5.96e-005 = pow(10.0, 0.05 * -84.5dB) */ + 0x38845244, /* [051] = 6.31e-005 = pow(10.0, 0.05 * -84.0dB) */ + 0x388c2971, /* [052] = 6.68e-005 = pow(10.0, 0.05 * -83.5dB) */ + 0x3894778d, /* [053] = 7.08e-005 = pow(10.0, 0.05 * -83.0dB) */ + 0x389d43a4, /* [054] = 7.50e-005 = pow(10.0, 0.05 * -82.5dB) */ + 0x38a6952c, /* [055] = 7.94e-005 = pow(10.0, 0.05 * -82.0dB) */ + 0x38b0740f, /* [056] = 8.41e-005 = pow(10.0, 0.05 * -81.5dB) */ + 0x38bae8ac, /* [057] = 8.91e-005 = pow(10.0, 0.05 * -81.0dB) */ + 0x38c5fbe2, /* [058] = 9.44e-005 = pow(10.0, 0.05 * -80.5dB) */ + 0x38d1b717, /* [059] = 1.00e-004 = pow(10.0, 0.05 * -80.0dB) */ + 0x38de2440, /* [060] = 1.06e-004 = pow(10.0, 0.05 * -79.5dB) */ + 0x38eb4de8, /* [061] = 1.12e-004 = pow(10.0, 0.05 * -79.0dB) */ + 0x38f93f3a, /* [062] = 1.19e-004 = pow(10.0, 0.05 * -78.5dB) */ + 0x39040206, /* [063] = 1.26e-004 = pow(10.0, 0.05 * -78.0dB) */ + 0x390bd472, /* [064] = 1.33e-004 = pow(10.0, 0.05 * -77.5dB) */ + 0x39141d84, /* [065] = 1.41e-004 = pow(10.0, 0.05 * -77.0dB) */ + 0x391ce445, /* [066] = 1.50e-004 = pow(10.0, 0.05 * -76.5dB) */ + 0x39263027, /* [067] = 1.58e-004 = pow(10.0, 0.05 * -76.0dB) */ + 0x3930090d, /* [068] = 1.68e-004 = pow(10.0, 0.05 * -75.5dB) */ + 0x393a7753, /* [069] = 1.78e-004 = pow(10.0, 0.05 * -75.0dB) */ + 0x394583d2, /* [070] = 1.88e-004 = pow(10.0, 0.05 * -74.5dB) */ + 0x395137ea, /* [071] = 2.00e-004 = pow(10.0, 0.05 * -74.0dB) */ + 0x395d9d8a, /* [072] = 2.11e-004 = pow(10.0, 0.05 * -73.5dB) */ + 0x396abf37, /* [073] = 2.24e-004 = pow(10.0, 0.05 * -73.0dB) */ + 0x3978a814, /* [074] = 2.37e-004 = pow(10.0, 0.05 * -72.5dB) */ + 0x3983b1f8, /* [075] = 2.51e-004 = pow(10.0, 0.05 * -72.0dB) */ + 0x398b7fa6, /* [076] = 2.66e-004 = pow(10.0, 0.05 * -71.5dB) */ + 0x3993c3b2, /* [077] = 2.82e-004 = pow(10.0, 0.05 * -71.0dB) */ + 0x399c8521, /* [078] = 2.99e-004 = pow(10.0, 0.05 * -70.5dB) */ + 0x39a5cb5f, /* [079] = 3.16e-004 = pow(10.0, 0.05 * -70.0dB) */ + 0x39af9e4d, /* [080] = 3.35e-004 = pow(10.0, 0.05 * -69.5dB) */ + 0x39ba063f, /* [081] = 3.55e-004 = pow(10.0, 0.05 * -69.0dB) */ + 0x39c50c0b, /* [082] = 3.76e-004 = pow(10.0, 0.05 * -68.5dB) */ + 0x39d0b90a, /* [083] = 3.98e-004 = pow(10.0, 0.05 * -68.0dB) */ + 0x39dd1726, /* [084] = 4.22e-004 = pow(10.0, 0.05 * -67.5dB) */ + 0x39ea30db, /* [085] = 4.47e-004 = pow(10.0, 0.05 * -67.0dB) */ + 0x39f81149, /* [086] = 4.73e-004 = pow(10.0, 0.05 * -66.5dB) */ + 0x3a03621b, /* [087] = 5.01e-004 = pow(10.0, 0.05 * -66.0dB) */ + 0x3a0b2b0d, /* [088] = 5.31e-004 = pow(10.0, 0.05 * -65.5dB) */ + 0x3a136a16, /* [089] = 5.62e-004 = pow(10.0, 0.05 * -65.0dB) */ + 0x3a1c2636, /* [090] = 5.96e-004 = pow(10.0, 0.05 * -64.5dB) */ + 0x3a2566d5, /* [091] = 6.31e-004 = pow(10.0, 0.05 * -64.0dB) */ + 0x3a2f33cd, /* [092] = 6.68e-004 = pow(10.0, 0.05 * -63.5dB) */ + 0x3a399570, /* [093] = 7.08e-004 = pow(10.0, 0.05 * -63.0dB) */ + 0x3a44948c, /* [094] = 7.50e-004 = pow(10.0, 0.05 * -62.5dB) */ + 0x3a503a77, /* [095] = 7.94e-004 = pow(10.0, 0.05 * -62.0dB) */ + 0x3a5c9112, /* [096] = 8.41e-004 = pow(10.0, 0.05 * -61.5dB) */ + 0x3a69a2d7, /* [097] = 8.91e-004 = pow(10.0, 0.05 * -61.0dB) */ + 0x3a777ada, /* [098] = 9.44e-004 = pow(10.0, 0.05 * -60.5dB) */ + 0x3a83126f, /* [099] = 1.00e-003 = pow(10.0, 0.05 * -60.0dB) */ + 0x3a8ad6a8, /* [100] = 1.06e-003 = pow(10.0, 0.05 * -59.5dB) */ + 0x3a9310b1, /* [101] = 1.12e-003 = pow(10.0, 0.05 * -59.0dB) */ + 0x3a9bc784, /* [102] = 1.19e-003 = pow(10.0, 0.05 * -58.5dB) */ + 0x3aa50287, /* [103] = 1.26e-003 = pow(10.0, 0.05 * -58.0dB) */ + 0x3aaec98e, /* [104] = 1.33e-003 = pow(10.0, 0.05 * -57.5dB) */ + 0x3ab924e5, /* [105] = 1.41e-003 = pow(10.0, 0.05 * -57.0dB) */ + 0x3ac41d56, /* [106] = 1.50e-003 = pow(10.0, 0.05 * -56.5dB) */ + 0x3acfbc31, /* [107] = 1.58e-003 = pow(10.0, 0.05 * -56.0dB) */ + 0x3adc0b51, /* [108] = 1.68e-003 = pow(10.0, 0.05 * -55.5dB) */ + 0x3ae91528, /* [109] = 1.78e-003 = pow(10.0, 0.05 * -55.0dB) */ + 0x3af6e4c6, /* [110] = 1.88e-003 = pow(10.0, 0.05 * -54.5dB) */ + 0x3b02c2f2, /* [111] = 2.00e-003 = pow(10.0, 0.05 * -54.0dB) */ + 0x3b0a8276, /* [112] = 2.11e-003 = pow(10.0, 0.05 * -53.5dB) */ + 0x3b12b782, /* [113] = 2.24e-003 = pow(10.0, 0.05 * -53.0dB) */ + 0x3b1b690d, /* [114] = 2.37e-003 = pow(10.0, 0.05 * -52.5dB) */ + 0x3b249e76, /* [115] = 2.51e-003 = pow(10.0, 0.05 * -52.0dB) */ + 0x3b2e5f8f, /* [116] = 2.66e-003 = pow(10.0, 0.05 * -51.5dB) */ + 0x3b38b49f, /* [117] = 2.82e-003 = pow(10.0, 0.05 * -51.0dB) */ + 0x3b43a669, /* [118] = 2.99e-003 = pow(10.0, 0.05 * -50.5dB) */ + 0x3b4f3e37, /* [119] = 3.16e-003 = pow(10.0, 0.05 * -50.0dB) */ + 0x3b5b85e0, /* [120] = 3.35e-003 = pow(10.0, 0.05 * -49.5dB) */ + 0x3b6887cf, /* [121] = 3.55e-003 = pow(10.0, 0.05 * -49.0dB) */ + 0x3b764f0e, /* [122] = 3.76e-003 = pow(10.0, 0.05 * -48.5dB) */ + 0x3b8273a6, /* [123] = 3.98e-003 = pow(10.0, 0.05 * -48.0dB) */ + 0x3b8a2e77, /* [124] = 4.22e-003 = pow(10.0, 0.05 * -47.5dB) */ + 0x3b925e89, /* [125] = 4.47e-003 = pow(10.0, 0.05 * -47.0dB) */ + 0x3b9b0ace, /* [126] = 4.73e-003 = pow(10.0, 0.05 * -46.5dB) */ + 0x3ba43aa2, /* [127] = 5.01e-003 = pow(10.0, 0.05 * -46.0dB) */ + 0x3badf5d1, /* [128] = 5.31e-003 = pow(10.0, 0.05 * -45.5dB) */ + 0x3bb8449c, /* [129] = 5.62e-003 = pow(10.0, 0.05 * -45.0dB) */ + 0x3bc32fc3, /* [130] = 5.96e-003 = pow(10.0, 0.05 * -44.5dB) */ + 0x3bcec08a, /* [131] = 6.31e-003 = pow(10.0, 0.05 * -44.0dB) */ + 0x3bdb00c0, /* [132] = 6.68e-003 = pow(10.0, 0.05 * -43.5dB) */ + 0x3be7facc, /* [133] = 7.08e-003 = pow(10.0, 0.05 * -43.0dB) */ + 0x3bf5b9b0, /* [134] = 7.50e-003 = pow(10.0, 0.05 * -42.5dB) */ + 0x3c02248a, /* [135] = 7.94e-003 = pow(10.0, 0.05 * -42.0dB) */ + 0x3c09daac, /* [136] = 8.41e-003 = pow(10.0, 0.05 * -41.5dB) */ + 0x3c1205c6, /* [137] = 8.91e-003 = pow(10.0, 0.05 * -41.0dB) */ + 0x3c1aacc8, /* [138] = 9.44e-003 = pow(10.0, 0.05 * -40.5dB) */ + 0x3c23d70a, /* [139] = 1.00e-002 = pow(10.0, 0.05 * -40.0dB) */ + 0x3c2d8c52, /* [140] = 1.06e-002 = pow(10.0, 0.05 * -39.5dB) */ + 0x3c37d4dd, /* [141] = 1.12e-002 = pow(10.0, 0.05 * -39.0dB) */ + 0x3c42b965, /* [142] = 1.19e-002 = pow(10.0, 0.05 * -38.5dB) */ + 0x3c4e4329, /* [143] = 1.26e-002 = pow(10.0, 0.05 * -38.0dB) */ + 0x3c5a7bf1, /* [144] = 1.33e-002 = pow(10.0, 0.05 * -37.5dB) */ + 0x3c676e1e, /* [145] = 1.41e-002 = pow(10.0, 0.05 * -37.0dB) */ + 0x3c7524ac, /* [146] = 1.50e-002 = pow(10.0, 0.05 * -36.5dB) */ + 0x3c81d59f, /* [147] = 1.58e-002 = pow(10.0, 0.05 * -36.0dB) */ + 0x3c898712, /* [148] = 1.68e-002 = pow(10.0, 0.05 * -35.5dB) */ + 0x3c91ad39, /* [149] = 1.78e-002 = pow(10.0, 0.05 * -35.0dB) */ + 0x3c9a4efc, /* [150] = 1.88e-002 = pow(10.0, 0.05 * -34.5dB) */ + 0x3ca373af, /* [151] = 2.00e-002 = pow(10.0, 0.05 * -34.0dB) */ + 0x3cad2314, /* [152] = 2.11e-002 = pow(10.0, 0.05 * -33.5dB) */ + 0x3cb76563, /* [153] = 2.24e-002 = pow(10.0, 0.05 * -33.0dB) */ + 0x3cc24350, /* [154] = 2.37e-002 = pow(10.0, 0.05 * -32.5dB) */ + 0x3ccdc614, /* [155] = 2.51e-002 = pow(10.0, 0.05 * -32.0dB) */ + 0x3cd9f773, /* [156] = 2.66e-002 = pow(10.0, 0.05 * -31.5dB) */ + 0x3ce6e1c6, /* [157] = 2.82e-002 = pow(10.0, 0.05 * -31.0dB) */ + 0x3cf49003, /* [158] = 2.99e-002 = pow(10.0, 0.05 * -30.5dB) */ + 0x3d0186e2, /* [159] = 3.16e-002 = pow(10.0, 0.05 * -30.0dB) */ + 0x3d0933ac, /* [160] = 3.35e-002 = pow(10.0, 0.05 * -29.5dB) */ + 0x3d1154e1, /* [161] = 3.55e-002 = pow(10.0, 0.05 * -29.0dB) */ + 0x3d19f169, /* [162] = 3.76e-002 = pow(10.0, 0.05 * -28.5dB) */ + 0x3d231090, /* [163] = 3.98e-002 = pow(10.0, 0.05 * -28.0dB) */ + 0x3d2cba15, /* [164] = 4.22e-002 = pow(10.0, 0.05 * -27.5dB) */ + 0x3d36f62b, /* [165] = 4.47e-002 = pow(10.0, 0.05 * -27.0dB) */ + 0x3d41cd81, /* [166] = 4.73e-002 = pow(10.0, 0.05 * -26.5dB) */ + 0x3d4d494a, /* [167] = 5.01e-002 = pow(10.0, 0.05 * -26.0dB) */ + 0x3d597345, /* [168] = 5.31e-002 = pow(10.0, 0.05 * -25.5dB) */ + 0x3d6655c3, /* [169] = 5.62e-002 = pow(10.0, 0.05 * -25.0dB) */ + 0x3d73fbb4, /* [170] = 5.96e-002 = pow(10.0, 0.05 * -24.5dB) */ + 0x3d813856, /* [171] = 6.31e-002 = pow(10.0, 0.05 * -24.0dB) */ + 0x3d88e078, /* [172] = 6.68e-002 = pow(10.0, 0.05 * -23.5dB) */ + 0x3d90fcbf, /* [173] = 7.08e-002 = pow(10.0, 0.05 * -23.0dB) */ + 0x3d99940e, /* [174] = 7.50e-002 = pow(10.0, 0.05 * -22.5dB) */ + 0x3da2adad, /* [175] = 7.94e-002 = pow(10.0, 0.05 * -22.0dB) */ + 0x3dac5156, /* [176] = 8.41e-002 = pow(10.0, 0.05 * -21.5dB) */ + 0x3db68738, /* [177] = 8.91e-002 = pow(10.0, 0.05 * -21.0dB) */ + 0x3dc157fb, /* [178] = 9.44e-002 = pow(10.0, 0.05 * -20.5dB) */ + 0x3dcccccd, /* [179] = 1.00e-001 = pow(10.0, 0.05 * -20.0dB) */ + 0x3dd8ef67, /* [180] = 1.06e-001 = pow(10.0, 0.05 * -19.5dB) */ + 0x3de5ca15, /* [181] = 1.12e-001 = pow(10.0, 0.05 * -19.0dB) */ + 0x3df367bf, /* [182] = 1.19e-001 = pow(10.0, 0.05 * -18.5dB) */ + 0x3e00e9f9, /* [183] = 1.26e-001 = pow(10.0, 0.05 * -18.0dB) */ + 0x3e088d77, /* [184] = 1.33e-001 = pow(10.0, 0.05 * -17.5dB) */ + 0x3e10a4d3, /* [185] = 1.41e-001 = pow(10.0, 0.05 * -17.0dB) */ + 0x3e1936ec, /* [186] = 1.50e-001 = pow(10.0, 0.05 * -16.5dB) */ + 0x3e224b06, /* [187] = 1.58e-001 = pow(10.0, 0.05 * -16.0dB) */ + 0x3e2be8d7, /* [188] = 1.68e-001 = pow(10.0, 0.05 * -15.5dB) */ + 0x3e361887, /* [189] = 1.78e-001 = pow(10.0, 0.05 * -15.0dB) */ + 0x3e40e2bb, /* [190] = 1.88e-001 = pow(10.0, 0.05 * -14.5dB) */ + 0x3e4c509b, /* [191] = 2.00e-001 = pow(10.0, 0.05 * -14.0dB) */ + 0x3e586bd9, /* [192] = 2.11e-001 = pow(10.0, 0.05 * -13.5dB) */ + 0x3e653ebb, /* [193] = 2.24e-001 = pow(10.0, 0.05 * -13.0dB) */ + 0x3e72d424, /* [194] = 2.37e-001 = pow(10.0, 0.05 * -12.5dB) */ + 0x3e809bcc, /* [195] = 2.51e-001 = pow(10.0, 0.05 * -12.0dB) */ + 0x3e883aa8, /* [196] = 2.66e-001 = pow(10.0, 0.05 * -11.5dB) */ + 0x3e904d1c, /* [197] = 2.82e-001 = pow(10.0, 0.05 * -11.0dB) */ + 0x3e98da02, /* [198] = 2.99e-001 = pow(10.0, 0.05 * -10.5dB) */ + 0x3ea1e89b, /* [199] = 3.16e-001 = pow(10.0, 0.05 * -10.0dB) */ + 0x3eab8097, /* [200] = 3.35e-001 = pow(10.0, 0.05 * -9.5dB) */ + 0x3eb5aa1a, /* [201] = 3.55e-001 = pow(10.0, 0.05 * -9.0dB) */ + 0x3ec06dc3, /* [202] = 3.76e-001 = pow(10.0, 0.05 * -8.5dB) */ + 0x3ecbd4b4, /* [203] = 3.98e-001 = pow(10.0, 0.05 * -8.0dB) */ + 0x3ed7e89b, /* [204] = 4.22e-001 = pow(10.0, 0.05 * -7.5dB) */ + 0x3ee4b3b6, /* [205] = 4.47e-001 = pow(10.0, 0.05 * -7.0dB) */ + 0x3ef240e2, /* [206] = 4.73e-001 = pow(10.0, 0.05 * -6.5dB) */ + 0x3f004dce, /* [207] = 5.01e-001 = pow(10.0, 0.05 * -6.0dB) */ + 0x3f07e80b, /* [208] = 5.31e-001 = pow(10.0, 0.05 * -5.5dB) */ + 0x3f0ff59a, /* [209] = 5.62e-001 = pow(10.0, 0.05 * -5.0dB) */ + 0x3f187d50, /* [210] = 5.96e-001 = pow(10.0, 0.05 * -4.5dB) */ + 0x3f21866c, /* [211] = 6.31e-001 = pow(10.0, 0.05 * -4.0dB) */ + 0x3f2b1896, /* [212] = 6.68e-001 = pow(10.0, 0.05 * -3.5dB) */ + 0x3f353bef, /* [213] = 7.08e-001 = pow(10.0, 0.05 * -3.0dB) */ + 0x3f3ff911, /* [214] = 7.50e-001 = pow(10.0, 0.05 * -2.5dB) */ + 0x3f4b5918, /* [215] = 7.94e-001 = pow(10.0, 0.05 * -2.0dB) */ + 0x3f5765ac, /* [216] = 8.41e-001 = pow(10.0, 0.05 * -1.5dB) */ + 0x3f642905, /* [217] = 8.91e-001 = pow(10.0, 0.05 * -1.0dB) */ + 0x3f71adf9, /* [218] = 9.44e-001 = pow(10.0, 0.05 * -0.5dB) */ + 0x3f800000, /* [219] = 1.00e+000 = pow(10.0, 0.05 * 0.0dB) */ + 0x3f8795a0, /* [220] = 1.06e+000 = pow(10.0, 0.05 * 0.5dB) */ + 0x3f8f9e4d, /* [221] = 1.12e+000 = pow(10.0, 0.05 * 1.0dB) */ + 0x3f9820d7, /* [222] = 1.19e+000 = pow(10.0, 0.05 * 1.5dB) */ + 0x3fa12478, /* [223] = 1.26e+000 = pow(10.0, 0.05 * 2.0dB) */ + 0x3faab0d5, /* [224] = 1.33e+000 = pow(10.0, 0.05 * 2.5dB) */ + 0x3fb4ce08, /* [225] = 1.41e+000 = pow(10.0, 0.05 * 3.0dB) */ + 0x3fbf84a6, /* [226] = 1.50e+000 = pow(10.0, 0.05 * 3.5dB) */ + 0x3fcaddc8, /* [227] = 1.58e+000 = pow(10.0, 0.05 * 4.0dB) */ + 0x3fd6e30d, /* [228] = 1.68e+000 = pow(10.0, 0.05 * 4.5dB) */ + 0x3fe39ea9, /* [229] = 1.78e+000 = pow(10.0, 0.05 * 5.0dB) */ + 0x3ff11b6a, /* [230] = 1.88e+000 = pow(10.0, 0.05 * 5.5dB) */ + 0x3fff64c1, /* [231] = 2.00e+000 = pow(10.0, 0.05 * 6.0dB) */ + 0x40074368, /* [232] = 2.11e+000 = pow(10.0, 0.05 * 6.5dB) */ + 0x400f4735, /* [233] = 2.24e+000 = pow(10.0, 0.05 * 7.0dB) */ + 0x4017c496, /* [234] = 2.37e+000 = pow(10.0, 0.05 * 7.5dB) */ + 0x4020c2bf, /* [235] = 2.51e+000 = pow(10.0, 0.05 * 8.0dB) */ + 0x402a4952, /* [236] = 2.66e+000 = pow(10.0, 0.05 * 8.5dB) */ + 0x40346063, /* [237] = 2.82e+000 = pow(10.0, 0.05 * 9.0dB) */ + 0x403f1082, /* [238] = 2.99e+000 = pow(10.0, 0.05 * 9.5dB) */ + 0x404a62c2, /* [239] = 3.16e+000 = pow(10.0, 0.05 * 10.0dB) */ + 0x405660bd, /* [240] = 3.35e+000 = pow(10.0, 0.05 * 10.5dB) */ + 0x406314a0, /* [241] = 3.55e+000 = pow(10.0, 0.05 * 11.0dB) */ + 0x40708933, /* [242] = 3.76e+000 = pow(10.0, 0.05 * 11.5dB) */ + 0x407ec9e1, /* [243] = 3.98e+000 = pow(10.0, 0.05 * 12.0dB) */ + 0x4086f161, /* [244] = 4.22e+000 = pow(10.0, 0.05 * 12.5dB) */ + 0x408ef052, /* [245] = 4.47e+000 = pow(10.0, 0.05 * 13.0dB) */ + 0x4097688d, /* [246] = 4.73e+000 = pow(10.0, 0.05 * 13.5dB) */ + 0x40a06142, /* [247] = 5.01e+000 = pow(10.0, 0.05 * 14.0dB) */ + 0x40a9e20e, /* [248] = 5.31e+000 = pow(10.0, 0.05 * 14.5dB) */ + 0x40b3f300, /* [249] = 5.62e+000 = pow(10.0, 0.05 * 15.0dB) */ + 0x40be9ca5, /* [250] = 5.96e+000 = pow(10.0, 0.05 * 15.5dB) */ + 0x40c9e807, /* [251] = 6.31e+000 = pow(10.0, 0.05 * 16.0dB) */ + 0x40d5debc, /* [252] = 6.68e+000 = pow(10.0, 0.05 * 16.5dB) */ + 0x40e28aeb, /* [253] = 7.08e+000 = pow(10.0, 0.05 * 17.0dB) */ + 0x40eff755, /* [254] = 7.50e+000 = pow(10.0, 0.05 * 17.5dB) */ + 0x40fe2f5e, /* [255] = 7.94e+000 = pow(10.0, 0.05 * 18.0dB) */ +}; + +#define MIXART_DIGITAL_LEVEL_MIN 0 /* -109.5 dB */ +#define MIXART_DIGITAL_LEVEL_MAX 255 /* 18.0 dB */ +#define MIXART_DIGITAL_ZERO_LEVEL 219 /* 0.0 dB */ + + +int mixart_update_playback_stream_level(mixart_t* chip, int is_aes, int idx) +{ + int err, i; + int volume[2]; + mixart_msg_t request; + mixart_set_out_stream_level_req_t set_level; + u32 status; + mixart_pipe_t *pipe; + + memset(&set_level, 0, sizeof(set_level)); + set_level.nb_of_stream = 1; + set_level.stream_level.desc.stream_idx = idx; + + if(is_aes) { + pipe = &chip->pipe_out_dig; /* AES playback */ + idx += MIXART_PLAYBACK_STREAMS; + } else { + pipe = &chip->pipe_out_ana; /* analog playback */ + } + + /* only when pipe exists ! */ + if(pipe->status == PIPE_UNDEFINED) + return 0; + + set_level.stream_level.desc.uid_pipe = pipe->group_uid; + + for(i=0; i<2; i++) { + if(chip->digital_playback_active[idx][i]) + volume[i] = chip->digital_playback_volume[idx][i]; + else + volume[i] = MIXART_DIGITAL_LEVEL_MIN; + } + + set_level.stream_level.out_level.valid_mask1 = MIXART_OUT_STREAM_SET_LEVEL_LEFT_AUDIO1 | MIXART_OUT_STREAM_SET_LEVEL_RIGHT_AUDIO2; + set_level.stream_level.out_level.left_to_out1_level = mixart_digital_level[volume[0]]; + set_level.stream_level.out_level.right_to_out2_level = mixart_digital_level[volume[1]]; + + request.message_id = MSG_STREAM_SET_OUT_STREAM_LEVEL; + request.uid = (mixart_uid_t){0,0}; + request.data = &set_level; + request.size = sizeof(set_level); + + err = snd_mixart_send_msg(chip->mgr, &request, sizeof(status), &status); + if((err<0) || status) { + snd_printk(KERN_DEBUG "error MSG_STREAM_SET_OUT_STREAM_LEVEL card(%d) status(%x)\n", chip->chip_idx, status); + return -EINVAL; + } + return 0; +} + +int mixart_update_capture_stream_level(mixart_t* chip, int is_aes) +{ + int err, i, idx; + mixart_pipe_t* pipe; + mixart_msg_t request; + mixart_set_in_audio_level_req_t set_level; + u32 status; + + if(is_aes) { + idx = 1; + pipe = &chip->pipe_in_dig; + } else { + idx = 0; + pipe = &chip->pipe_in_ana; + } + + /* only when pipe exists ! */ + if(pipe->status == PIPE_UNDEFINED) + return 0; + + memset(&set_level, 0, sizeof(set_level)); + set_level.audio_count = 2; + set_level.level[0].connector = pipe->uid_left_connector; + set_level.level[1].connector = pipe->uid_right_connector; + + for(i=0; i<2; i++) { + set_level.level[i].valid_mask1 = MIXART_AUDIO_LEVEL_DIGITAL_MASK; + set_level.level[i].digital_level = mixart_digital_level[chip->digital_capture_volume[idx][i]]; + } + + request.message_id = MSG_STREAM_SET_IN_AUDIO_LEVEL; + request.uid = (mixart_uid_t){0,0}; + request.data = &set_level; + request.size = sizeof(set_level); + + err = snd_mixart_send_msg(chip->mgr, &request, sizeof(status), &status); + if((err<0) || status) { + snd_printk(KERN_DEBUG "error MSG_STREAM_SET_IN_AUDIO_LEVEL card(%d) status(%x)\n", chip->chip_idx, status); + return -EINVAL; + } + return 0; +} + + +/* shared */ +static int mixart_digital_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = MIXART_DIGITAL_LEVEL_MIN; /* -109.5 dB */ + uinfo->value.integer.max = MIXART_DIGITAL_LEVEL_MAX; /* 18.0 dB */ + return 0; +} + +#define MIXART_VOL_REC_MASK 1 +#define MIXART_VOL_AES_MASK 2 + +static int mixart_pcm_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + mixart_t *chip = snd_kcontrol_chip(kcontrol); + int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */ + int *stored_volume; + int is_capture = kcontrol->private_value & MIXART_VOL_REC_MASK; + int is_aes = kcontrol->private_value & MIXART_VOL_AES_MASK; + down(&chip->mgr->mixer_mutex); + if(is_capture) { + if(is_aes) stored_volume = chip->digital_capture_volume[1]; /* AES capture */ + else stored_volume = chip->digital_capture_volume[0]; /* analog capture */ + } else { + snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); + if(is_aes) stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx]; /* AES playback */ + else stored_volume = chip->digital_playback_volume[idx]; /* analog playback */ + } + ucontrol->value.integer.value[0] = stored_volume[0]; + ucontrol->value.integer.value[1] = stored_volume[1]; + up(&chip->mgr->mixer_mutex); + return 0; +} + +static int mixart_pcm_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + mixart_t *chip = snd_kcontrol_chip(kcontrol); + int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */ + int changed = 0; + int is_capture = kcontrol->private_value & MIXART_VOL_REC_MASK; + int is_aes = kcontrol->private_value & MIXART_VOL_AES_MASK; + int* stored_volume; + int i; + down(&chip->mgr->mixer_mutex); + if(is_capture) { + if(is_aes) stored_volume = chip->digital_capture_volume[1]; /* AES capture */ + else stored_volume = chip->digital_capture_volume[0]; /* analog capture */ + } else { + snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); + if(is_aes) stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx]; /* AES playback */ + else stored_volume = chip->digital_playback_volume[idx]; /* analog playback */ + } + for(i=0; i<2; i++) { + if(stored_volume[i] != ucontrol->value.integer.value[i]) { + stored_volume[i] = ucontrol->value.integer.value[i]; + changed = 1; + } + } + if(changed) { + if(is_capture) mixart_update_capture_stream_level(chip, is_aes); + else mixart_update_playback_stream_level(chip, is_aes, idx); + } + up(&chip->mgr->mixer_mutex); + return changed; +} + +static snd_kcontrol_new_t snd_mixart_pcm_vol = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* name will be filled later */ + /* count will be filled later */ + .info = mixart_digital_vol_info, /* shared */ + .get = mixart_pcm_vol_get, + .put = mixart_pcm_vol_put, +}; + + +static int mixart_pcm_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + mixart_t *chip = snd_kcontrol_chip(kcontrol); + int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */ + snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); + down(&chip->mgr->mixer_mutex); + if(kcontrol->private_value & MIXART_VOL_AES_MASK) /* AES playback */ + idx += MIXART_PLAYBACK_STREAMS; + ucontrol->value.integer.value[0] = chip->digital_playback_active[idx][0]; + ucontrol->value.integer.value[1] = chip->digital_playback_active[idx][1]; + up(&chip->mgr->mixer_mutex); + return 0; +} + +static int mixart_pcm_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + mixart_t *chip = snd_kcontrol_chip(kcontrol); + int changed = 0; + int is_aes = kcontrol->private_value & MIXART_VOL_AES_MASK; + int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */ + int i, j; + snd_assert ( idx < MIXART_PLAYBACK_STREAMS ); + down(&chip->mgr->mixer_mutex); + j = idx; + if(is_aes) j += MIXART_PLAYBACK_STREAMS; + for(i=0; i<2; i++) { + if(chip->digital_playback_active[j][i] != ucontrol->value.integer.value[i]) { + chip->digital_playback_active[j][i] = ucontrol->value.integer.value[i]; + changed = 1; + } + } + if(changed) mixart_update_playback_stream_level(chip, is_aes, idx); + up(&chip->mgr->mixer_mutex); + return changed; +} + +static snd_kcontrol_new_t mixart_control_pcm_switch = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* name will be filled later */ + .count = MIXART_PLAYBACK_STREAMS, + .info = mixart_sw_info, /* shared */ + .get = mixart_pcm_sw_get, + .put = mixart_pcm_sw_put +}; + +static int mixart_update_monitoring(mixart_t* chip, int channel) +{ + int err; + mixart_msg_t request; + mixart_set_out_audio_level_t audio_level; + u32 resp; + + if(chip->pipe_out_ana.status == PIPE_UNDEFINED) + return -EINVAL; /* no pipe defined */ + + if(!channel) request.uid = chip->pipe_out_ana.uid_left_connector; + else request.uid = chip->pipe_out_ana.uid_right_connector; + request.message_id = MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL; + request.data = &audio_level; + request.size = sizeof(audio_level); + + memset(&audio_level, 0, sizeof(audio_level)); + audio_level.valid_mask1 = MIXART_AUDIO_LEVEL_MONITOR_MASK | MIXART_AUDIO_LEVEL_MUTE_M1_MASK; + audio_level.monitor_level = mixart_digital_level[chip->monitoring_volume[channel!=0]]; + audio_level.monitor_mute1 = !chip->monitoring_active[channel!=0]; + + err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp); + if((err<0) || resp) { + snd_printk(KERN_DEBUG "error MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL card(%d) resp(%x)\n", chip->chip_idx, resp); + return -EINVAL; + } + return 0; +} + +/* + * monitoring level control + */ + +static int mixart_monitor_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + mixart_t *chip = snd_kcontrol_chip(kcontrol); + down(&chip->mgr->mixer_mutex); + ucontrol->value.integer.value[0] = chip->monitoring_volume[0]; + ucontrol->value.integer.value[1] = chip->monitoring_volume[1]; + up(&chip->mgr->mixer_mutex); + return 0; +} + +static int mixart_monitor_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + mixart_t *chip = snd_kcontrol_chip(kcontrol); + int changed = 0; + int i; + down(&chip->mgr->mixer_mutex); + for(i=0; i<2; i++) { + if(chip->monitoring_volume[i] != ucontrol->value.integer.value[i]) { + chip->monitoring_volume[i] = ucontrol->value.integer.value[i]; + mixart_update_monitoring(chip, i); + changed = 1; + } + } + up(&chip->mgr->mixer_mutex); + return changed; +} + +static snd_kcontrol_new_t mixart_control_monitor_vol = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Monitoring Volume", + .info = mixart_digital_vol_info, /* shared */ + .get = mixart_monitor_vol_get, + .put = mixart_monitor_vol_put, +}; + +/* + * monitoring switch control + */ + +static int mixart_monitor_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + mixart_t *chip = snd_kcontrol_chip(kcontrol); + down(&chip->mgr->mixer_mutex); + ucontrol->value.integer.value[0] = chip->monitoring_active[0]; + ucontrol->value.integer.value[1] = chip->monitoring_active[1]; + up(&chip->mgr->mixer_mutex); + return 0; +} + +static int mixart_monitor_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + mixart_t *chip = snd_kcontrol_chip(kcontrol); + int changed = 0; + int i; + down(&chip->mgr->mixer_mutex); + for(i=0; i<2; i++) { + if(chip->monitoring_active[i] != ucontrol->value.integer.value[i]) { + chip->monitoring_active[i] = ucontrol->value.integer.value[i]; + changed |= (1<monitoring_active[0] || chip->monitoring_active[1]; + if(allocate) { + snd_mixart_add_ref_pipe( chip, MIXART_PCM_ANALOG, 0, 1); /* allocate the playback pipe for monitoring */ + snd_mixart_add_ref_pipe( chip, MIXART_PCM_ANALOG, 1, 1); /* allocate the capture pipe for monitoring */ + } + if(changed & 0x01) mixart_update_monitoring(chip, 0); + if(changed & 0x02) mixart_update_monitoring(chip, 1); + if(!allocate) { + snd_mixart_kill_ref_pipe( chip->mgr, &chip->pipe_in_ana, 1); /* release the capture pipe for monitoring */ + snd_mixart_kill_ref_pipe( chip->mgr, &chip->pipe_out_ana, 1); /* release the playback pipe for monitoring */ + } + } + + up(&chip->mgr->mixer_mutex); + return (changed != 0); +} + +static snd_kcontrol_new_t mixart_control_monitor_sw = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Monitoring Switch", + .info = mixart_sw_info, /* shared */ + .get = mixart_monitor_sw_get, + .put = mixart_monitor_sw_put +}; + + +static void mixart_reset_audio_levels(mixart_t *chip) +{ + /* analog volumes can be set even if there is no pipe */ + mixart_update_analog_audio_level(chip, 0); + /* analog levels for capture only on the first two chips */ + if(chip->chip_idx < 2) { + mixart_update_analog_audio_level(chip, 1); + } + return; +} + + +int snd_mixart_create_mixer(mixart_mgr_t *mgr) +{ + mixart_t *chip; + int err, i; + + init_MUTEX(&mgr->mixer_mutex); /* can be in another place */ + + for(i=0; inum_cards; i++) { + snd_kcontrol_new_t temp; + chip = mgr->chip[i]; + + /* analog output level control */ + temp = mixart_control_analog_level; + temp.name = "Master Playback Volume"; + temp.private_value = 0; /* playback */ + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0) + return err; + /* output mute controls */ + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixart_control_output_switch, chip))) < 0) + return err; + + /* analog input level control only on first two chips !*/ + if(i<2) { + temp = mixart_control_analog_level; + temp.name = "Master Capture Volume"; + temp.private_value = 1; /* capture */ + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0) + return err; + } + + temp = snd_mixart_pcm_vol; + temp.name = "PCM Playback Volume"; + temp.count = MIXART_PLAYBACK_STREAMS; + temp.private_value = 0; /* playback analog */ + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0) + return err; + + temp.name = "PCM Capture Volume"; + temp.count = 1; + temp.private_value = MIXART_VOL_REC_MASK; /* capture analog */ + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0) + return err; + + if(mgr->board_type == MIXART_DAUGHTER_TYPE_AES) { + temp.name = "AES Playback Volume"; + temp.count = MIXART_PLAYBACK_STREAMS; + temp.private_value = MIXART_VOL_AES_MASK; /* playback AES/EBU */ + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0) + return err; + + temp.name = "AES Capture Volume"; + temp.count = 0; + temp.private_value = MIXART_VOL_REC_MASK | MIXART_VOL_AES_MASK; /* capture AES/EBU */ + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0) + return err; + } + temp = mixart_control_pcm_switch; + temp.name = "PCM Playback Switch"; + temp.private_value = 0; /* playback analog */ + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0) + return err; + + if(mgr->board_type == MIXART_DAUGHTER_TYPE_AES) { + temp.name = "AES Playback Switch"; + temp.private_value = MIXART_VOL_AES_MASK; /* playback AES/EBU */ + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0) + return err; + } + + /* monitoring */ + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixart_control_monitor_vol, chip))) < 0) + return err; + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixart_control_monitor_sw, chip))) < 0) + return err; + + /* init all mixer data and program the master volumes/switches */ + mixart_reset_audio_levels(chip); + } + return 0; +} diff -Nru a/sound/pci/mixart/mixart_mixer.h b/sound/pci/mixart/mixart_mixer.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pci/mixart/mixart_mixer.h Sun Feb 29 12:58:39 2004 @@ -0,0 +1,31 @@ +/* + * Driver for Digigram miXart soundcards + * + * include file for mixer + * + * Copyright (c) 2003 by Digigram + * + * 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 + */ + +#ifndef __SOUND_MIXART_MIXER_H +#define __SOUND_MIXART_MIXER_H + +/* exported */ +int mixart_update_playback_stream_level(mixart_t* chip, int is_aes, int idx); +int mixart_update_capture_stream_level(mixart_t* chip, int is_aes); +int snd_mixart_create_mixer(mixart_mgr_t* mgr); + +#endif /* __SOUND_MIXART_MIXER_H */ diff -Nru a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c --- a/sound/pci/rme9652/hdsp.c Sun Feb 29 12:58:38 2004 +++ b/sound/pci/rme9652/hdsp.c Sun Feb 29 12:58:38 2004 @@ -3810,7 +3810,7 @@ { int mapped_channel; - snd_assert(channel >= 0 || channel < hdsp->max_channels, return NULL); + snd_assert(channel >= 0 && channel < hdsp->max_channels, return NULL); if ((mapped_channel = hdsp->channel_map[channel]) < 0) { return NULL; @@ -3833,7 +3833,8 @@ channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel); snd_assert(channel_buf != NULL, return -EIO); - copy_from_user(channel_buf + pos * 4, src, count * 4); + if (copy_from_user(channel_buf + pos * 4, src, count * 4)) + return -EFAULT; return count; } @@ -3847,7 +3848,8 @@ channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel); snd_assert(channel_buf != NULL, return -EIO); - copy_to_user(dst, channel_buf + pos * 4, count * 4); + if (copy_to_user(dst, channel_buf + pos * 4, count * 4)) + return -EFAULT; return count; } diff -Nru a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c --- a/sound/pci/trident/trident_main.c Sun Feb 29 12:58:39 2004 +++ b/sound/pci/trident/trident_main.c Sun Feb 29 12:58:39 2004 @@ -3052,29 +3052,49 @@ } if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018) { - if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_control, trident))) < 0) + kctl = snd_ctl_new1(&snd_trident_spdif_control, trident); + if (kctl == NULL) { + err = -ENOMEM; goto __out; + } if (trident->ac97->ext_id & AC97_EI_SPDIF) kctl->id.index++; if (trident->ac97_sec && (trident->ac97_sec->ext_id & AC97_EI_SPDIF)) kctl->id.index++; idx = kctl->id.index; + if ((err = snd_ctl_add(card, kctl)) < 0) + goto __out; kctl->put(kctl, uctl); - if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_default, trident))) < 0) + kctl = snd_ctl_new1(&snd_trident_spdif_default, trident); + if (kctl == NULL) { + err = -ENOMEM; goto __out; + } kctl->id.index = idx; kctl->id.device = pcm_spdif_device; + if ((err = snd_ctl_add(card, kctl)) < 0) + goto __out; - if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_mask, trident))) < 0) + kctl = snd_ctl_new1(&snd_trident_spdif_mask, trident); + if (kctl == NULL) { + err = -ENOMEM; goto __out; + } kctl->id.index = idx; kctl->id.device = pcm_spdif_device; + if ((err = snd_ctl_add(card, kctl)) < 0) + goto __out; - if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_spdif_stream, trident))) < 0) + kctl = snd_ctl_new1(&snd_trident_spdif_stream, trident); + if (kctl == NULL) { + err = -ENOMEM; goto __out; + } kctl->id.index = idx; kctl->id.device = pcm_spdif_device; + if ((err = snd_ctl_add(card, kctl)) < 0) + goto __out; trident->spdif_pcm_ctl = kctl; } diff -Nru a/sound/pci/via82xx.c b/sound/pci/via82xx.c --- a/sound/pci/via82xx.c Sun Feb 29 12:58:39 2004 +++ b/sound/pci/via82xx.c Sun Feb 29 12:58:39 2004 @@ -922,12 +922,10 @@ chip->no_vra ? 48000 : runtime->rate); snd_ac97_set_rate(chip->ac97, AC97_SPDIF, runtime->rate); } -#if 0 - if (chip->revision == VIA_REV_8233A) - rbits = 0; + if (runtime->rate == 48000) + rbits = 0xfffff; else -#endif - rbits = (0xfffff / 48000) * runtime->rate + ((0xfffff % 48000) * runtime->rate) / 48000; + rbits = (0x100000 / 48000) * runtime->rate + ((0x100000 % 48000) * runtime->rate) / 48000; snd_assert((rbits & ~0xfffff) == 0, return -EINVAL); snd_via82xx_channel_reset(chip, viadev); snd_via82xx_set_table_ptr(chip, viadev); @@ -1987,13 +1985,16 @@ { .vendor = 0x1043, .device = 0x80b0, .action = VIA_DXS_NO_VRA }, /* ASUS A7V600 & K8V*/ { .vendor = 0x10cf, .device = 0x118e, .action = VIA_DXS_ENABLE }, /* FSC laptop */ { .vendor = 0x1106, .device = 0x4161, .action = VIA_DXS_NO_VRA }, /* ASRock K7VT2 */ + { .vendor = 0x1106, .device = 0xaa01, .action = VIA_DXS_NO_VRA }, /* EPIA MII */ { .vendor = 0x1297, .device = 0xa232, .action = VIA_DXS_ENABLE }, /* Shuttle ?? */ { .vendor = 0x1297, .device = 0xc160, .action = VIA_DXS_ENABLE }, /* Shuttle SK41G */ - { .vendor = 0x1458, .device = 0xa002, .action = VIA_DXS_ENABLE }, /* Gigabyte GA-7VAXP */ + { .vendor = 0x1458, .device = 0xa002, .action = VIA_DXS_NO_VRA }, /* Gigabyte GA-7VAXP (FIXME: or DXS_ENABLE?) */ { .vendor = 0x147b, .device = 0x1401, .action = VIA_DXS_ENABLE }, /* ABIT KD7(-RAID) */ { .vendor = 0x14ff, .device = 0x0403, .action = VIA_DXS_ENABLE }, /* Twinhead mobo */ { .vendor = 0x1462, .device = 0x3800, .action = VIA_DXS_ENABLE }, /* MSI KT266 */ { .vendor = 0x1462, .device = 0x7120, .action = VIA_DXS_ENABLE }, /* MSI KT4V */ + { .vendor = 0x1462, .device = 0x5901, .action = VIA_DXS_NO_VRA }, /* MSI KT6 Delta-SR */ + { .vendor = 0x1584, .device = 0x8120, .action = VIA_DXS_ENABLE }, /* Gericom/Targa/Vobis/Uniwill laptop */ { .vendor = 0x1631, .device = 0xe004, .action = VIA_DXS_ENABLE }, /* Easy Note 3174, Packard Bell */ { .vendor = 0x1695, .device = 0x3005, .action = VIA_DXS_ENABLE }, /* EPoX EP-8K9A */ { .vendor = 0x1849, .device = 0x3059, .action = VIA_DXS_NO_VRA }, /* ASRock K7VM2 */ diff -Nru a/sound/pcmcia/Kconfig b/sound/pcmcia/Kconfig --- a/sound/pcmcia/Kconfig Sun Feb 29 12:58:39 2004 +++ b/sound/pcmcia/Kconfig Sun Feb 29 12:58:39 2004 @@ -15,4 +15,10 @@ help Say 'Y' or 'M' to include support for Digigram VXpocket 440 soundcard. +config SND_PDAUDIOCF + tristate "Sound Core PDAudioCF" + depends on SND && PCMCIA && ISA + help + Say 'Y' or 'M' to include support for Sound Core PDAudioCF soundcard. + endmenu diff -Nru a/sound/pcmcia/Makefile b/sound/pcmcia/Makefile --- a/sound/pcmcia/Makefile Sun Feb 29 12:58:38 2004 +++ b/sound/pcmcia/Makefile Sun Feb 29 12:58:38 2004 @@ -3,6 +3,4 @@ # Copyright (c) 2001 by Jaroslav Kysela # -obj-$(CONFIG_SND) += vx/ - - +obj-$(CONFIG_SND) += vx/ pdaudiocf/ diff -Nru a/sound/pcmcia/pdaudiocf/Makefile b/sound/pcmcia/pdaudiocf/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pcmcia/pdaudiocf/Makefile Sun Feb 29 12:58:39 2004 @@ -0,0 +1,8 @@ +# +# Makefile for ALSA +# Copyright (c) 2004 by Jaroslav Kysela +# + +snd-pdaudiocf-objs := pdaudiocf.o pdaudiocf_core.o pdaudiocf_irq.o pdaudiocf_pcm.o + +obj-$(CONFIG_SND_PDAUDIOCF) += snd-pdaudiocf.o diff -Nru a/sound/pcmcia/pdaudiocf/pdaudiocf.c b/sound/pcmcia/pdaudiocf/pdaudiocf.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pcmcia/pdaudiocf/pdaudiocf.c Sun Feb 29 12:58:39 2004 @@ -0,0 +1,426 @@ +/* + * Driver for Sound Core PDAudioCF soundcard + * + * Copyright (c) 2003 by Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include "pdaudiocf.h" +#define SNDRV_GET_ID +#include + +/* + */ + +#define CARD_NAME "PDAudio-CF" + +MODULE_AUTHOR("Jaroslav Kysela "); +MODULE_DESCRIPTION("Sound Core " CARD_NAME); +MODULE_LICENSE("GPL"); +MODULE_CLASSES("{sound}"); +MODULE_DEVICES("{{Sound Core," CARD_NAME "}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */ +static unsigned int irq_mask = 0xffff; +static int irq_list[4] = { -1 }; + +MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); +MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s"); +MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); +MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); +MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); +MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); +MODULE_PARM(irq_mask, "i"); +MODULE_PARM_DESC(irq_mask, "IRQ bitmask for " CARD_NAME " soundcard."); +MODULE_PARM(irq_list, "1-4i"); +MODULE_PARM_DESC(irq_list, "List of Available interrupts for " CARD_NAME " soundcard."); + + +/* + */ + +static dev_info_t dev_info = "snd-pdaudiocf"; +static snd_card_t *card_list[SNDRV_CARDS]; +static dev_link_t *dev_list; + +/* + * prototypes + */ +static void pdacf_config(dev_link_t *link); +static int pdacf_event(event_t event, int priority, event_callback_args_t *args); +static void snd_pdacf_detach(dev_link_t *link); + +static void pdacf_release(dev_link_t *link) +{ + if (link->state & DEV_CONFIG) { + /* release cs resources */ + pcmcia_release_configuration(link->handle); + pcmcia_release_io(link->handle, &link->io); + pcmcia_release_irq(link->handle, &link->irq); + link->state &= ~DEV_CONFIG; + } +} + +/* + * destructor + */ +static int snd_pdacf_free(pdacf_t *pdacf) +{ + dev_link_t *link = &pdacf->link; + + pdacf_release(link); + + /* Break the link with Card Services */ + if (link->handle) + pcmcia_deregister_client(link->handle); + + card_list[pdacf->index] = NULL; + pdacf->card = NULL; + + snd_magic_kfree(pdacf); + return 0; +} + +static int snd_pdacf_dev_free(snd_device_t *device) +{ + pdacf_t *chip = snd_magic_cast(pdacf_t, device->device_data, return -ENXIO); + return snd_pdacf_free(chip); +} + +/* + * snd_pdacf_attach - attach callback for cs + */ +static dev_link_t *snd_pdacf_attach(void) +{ + client_reg_t client_reg; /* Register with cardmgr */ + dev_link_t *link; /* Info for cardmgr */ + int i, ret; + pdacf_t *pdacf; + snd_card_t *card; + static snd_device_ops_t ops = { + .dev_free = snd_pdacf_dev_free, + }; + + snd_printdd(KERN_DEBUG "pdacf_attach called\n"); + /* find an empty slot from the card list */ + for (i = 0; i < SNDRV_CARDS; i++) { + if (! card_list[i]) + break; + } + if (i >= SNDRV_CARDS) { + snd_printk(KERN_ERR "pdacf: too many cards found\n"); + return NULL; + } + if (! enable[i]) + return NULL; /* disabled explicitly */ + + /* ok, create a card instance */ + card = snd_card_new(index[i], id[i], THIS_MODULE, 0); + if (card == NULL) { + snd_printk(KERN_ERR "pdacf: cannot create a card instance\n"); + return NULL; + } + + pdacf = snd_pdacf_create(card); + if (! pdacf) + return NULL; + + if (snd_device_new(card, SNDRV_DEV_LOWLEVEL, pdacf, &ops) < 0) { + snd_magic_kfree(pdacf); + snd_card_free(card); + return NULL; + } + + pdacf->index = i; + card_list[i] = card; + + link = &pdacf->link; + link->priv = pdacf; + + link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; + link->io.NumPorts1 = 16; + + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT | IRQ_FORCED_PULSE; + // link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED; + + link->irq.IRQInfo1 = IRQ_INFO2_VALID /* | IRQ_LEVEL_ID */; + if (irq_list[0] == -1) + link->irq.IRQInfo2 = irq_mask; + else + for (i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + link->irq.Handler = pdacf_interrupt; + link->irq.Instance = pdacf; + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.IntType = INT_MEMORY_AND_IO; + link->conf.ConfigIndex = 1; + link->conf.Present = PRESENT_OPTION; + + /* Chain drivers */ + link->next = dev_list; + dev_list = link; + + /* Register with Card Services */ + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.EventMask = + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL +#ifdef CONFIG_PM + | CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET + | CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME +#endif + ; + client_reg.event_handler = &pdacf_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + + ret = pcmcia_register_client(&link->handle, &client_reg); + if (ret != CS_SUCCESS) { + cs_error(link->handle, RegisterClient, ret); + snd_pdacf_detach(link); + return NULL; + } + + return link; +} + + +/** + * snd_pdacf_assign_resources - initialize the hardware and card instance. + * @port: i/o port for the card + * @irq: irq number for the card + * + * this function assigns the specified port and irq, boot the card, + * create pcm and control instances, and initialize the rest hardware. + * + * returns 0 if successful, or a negative error code. + */ +static int snd_pdacf_assign_resources(pdacf_t *pdacf, int port, int irq) +{ + int err; + snd_card_t *card = pdacf->card; + + snd_printdd(KERN_DEBUG "pdacf assign resources: port = 0x%x, irq = %d\n", port, irq); + pdacf->port = port; + pdacf->irq = irq; + pdacf->chip_status |= PDAUDIOCF_STAT_IS_CONFIGURED; + + err = snd_pdacf_ak4117_create(pdacf); + if (err < 0) + return err; + + strcpy(card->driver, "PDAudio-CF"); + sprintf(card->shortname, "Core Sound %s", card->driver); + sprintf(card->longname, "%s at 0x%x, irq %i", + card->shortname, port, irq); + + err = snd_pdacf_pcm_new(pdacf); + if (err < 0) + return err; + +#ifdef CONFIG_PM + card->power_state_private_data = pdacf; + card->set_power_state = snd_pdacf_set_power_state; +#endif + + if ((err = snd_card_register(card)) < 0) + return err; + + return 0; +} + + +/* + * snd_pdacf_detach - detach callback for cs + */ +static void snd_pdacf_detach(dev_link_t *link) +{ + pdacf_t *chip = snd_magic_cast(pdacf_t, link->priv, return); + + snd_printdd(KERN_DEBUG "pdacf_detach called\n"); + /* Remove the interface data from the linked list */ + { + dev_link_t **linkp; + /* Locate device structure */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) + break; + if (*linkp) + *linkp = link->next; + } + if (chip->chip_status & PDAUDIOCF_STAT_IS_CONFIGURED) + snd_pdacf_powerdown(chip); + chip->chip_status |= PDAUDIOCF_STAT_IS_STALE; /* to be sure */ + snd_card_disconnect(chip->card); + snd_card_free_in_thread(chip->card); +} + +/* + * snd_pdacf_detach_all - detach all instances linked to the hw + */ +static void snd_pdacf_detach_all(void) +{ + while (dev_list != NULL) + snd_pdacf_detach(dev_list); +} + +/* + * configuration callback + */ + +#define CS_CHECK(fn, ret) \ +do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) + +static void pdacf_config(dev_link_t *link) +{ + client_handle_t handle = link->handle; + pdacf_t *pdacf = snd_magic_cast(pdacf_t, link->priv, return); + tuple_t tuple; + cisparse_t parse; + config_info_t conf; + u_short buf[32]; + int last_fn, last_ret; + + snd_printdd(KERN_DEBUG "pdacf_config called\n"); + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + tuple.Attributes = 0; + tuple.TupleData = (cisdata_t *)buf; + tuple.TupleDataMax = sizeof(buf); + tuple.TupleOffset = 0; + tuple.DesiredTuple = CISTPL_CONFIG; + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + link->conf.ConfigBase = parse.config.base; + link->conf.ConfigIndex = 0x5; + + CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf)); + link->conf.Vcc = conf.Vcc; + + /* Configure card */ + link->state |= DEV_CONFIG; + + CS_CHECK(RequestIO, pcmcia_request_io(handle, &link->io)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf)); + + if (snd_pdacf_assign_resources(pdacf, link->io.BasePort1, link->irq.AssignedIRQ) < 0) + goto failed; + + link->dev = &pdacf->node; + link->state &= ~DEV_CONFIG_PENDING; + return; + +cs_failed: + cs_error(link->handle, last_fn, last_ret); +failed: + pcmcia_release_configuration(link->handle); + pcmcia_release_io(link->handle, &link->io); + pcmcia_release_irq(link->handle, &link->irq); +} + +/* + * event callback + */ +static int pdacf_event(event_t event, int priority, event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + pdacf_t *chip = link->priv; + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + snd_printdd(KERN_DEBUG "CARD_REMOVAL..\n"); + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) { + chip->chip_status |= PDAUDIOCF_STAT_IS_STALE; + } + break; + case CS_EVENT_CARD_INSERTION: + snd_printdd(KERN_DEBUG "CARD_INSERTION..\n"); + link->state |= DEV_PRESENT; + pdacf_config(link); + break; +#ifdef CONFIG_PM + case CS_EVENT_PM_SUSPEND: + snd_printdd(KERN_DEBUG "SUSPEND\n"); + link->state |= DEV_SUSPEND; + if (chip) { + snd_printdd(KERN_DEBUG "snd_pdacf_suspend calling\n"); + snd_pdacf_suspend(chip); + } + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + snd_printdd(KERN_DEBUG "RESET_PHYSICAL\n"); + if (link->state & DEV_CONFIG) + pcmcia_release_configuration(link->handle); + break; + case CS_EVENT_PM_RESUME: + snd_printdd(KERN_DEBUG "RESUME\n"); + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + snd_printdd(KERN_DEBUG "CARD_RESET\n"); + if (DEV_OK(link)) { + snd_printdd(KERN_DEBUG "requestconfig...\n"); + pcmcia_request_configuration(link->handle, &link->conf); + if (chip) { + snd_printdd(KERN_DEBUG "calling snd_pdacf_resume\n"); + snd_pdacf_resume(chip); + } + } + snd_printdd(KERN_DEBUG "resume done!\n"); + break; +#endif + } + return 0; +} + +/* + * Module entry points + */ +static struct pcmcia_driver pdacf_cs_driver = { + .owner = THIS_MODULE, + .drv = { + .name = "snd-pdaudiocf", + }, + .attach = snd_pdacf_attach, + .detach = snd_pdacf_detach +}; + +static int __init init_pdacf(void) +{ + return pcmcia_register_driver(&pdacf_cs_driver); +} + +static void __exit exit_pdacf(void) +{ + pcmcia_unregister_driver(&pdacf_cs_driver); + snd_pdacf_detach_all(); +} + +module_init(init_pdacf); +module_exit(exit_pdacf); diff -Nru a/sound/pcmcia/pdaudiocf/pdaudiocf.h b/sound/pcmcia/pdaudiocf/pdaudiocf.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pcmcia/pdaudiocf/pdaudiocf.h Sun Feb 29 12:58:39 2004 @@ -0,0 +1,147 @@ +/* + * Driver for Sound Cors PDAudioCF soundcard + * + * Copyright (c) 2003 by Jaroslav Kysela + * + * 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 + */ + +#ifndef __PDAUDIOCF_H +#define __PDAUDIOCF_H + +#include +#include +#include +#include +#include +#include + +#include + +/* PDAUDIOCF registers */ +#define PDAUDIOCF_REG_MD 0x00 /* music data, R/O */ +#define PDAUDIOCF_REG_WDP 0x02 /* write data pointer / 2, R/O */ +#define PDAUDIOCF_REG_RDP 0x04 /* read data pointer / 2, R/O */ +#define PDAUDIOCF_REG_TCR 0x06 /* test control register W/O */ +#define PDAUDIOCF_REG_SCR 0x08 /* status and control, R/W (see bit description) */ +#define PDAUDIOCF_REG_ISR 0x0a /* interrupt status, R/O */ +#define PDAUDIOCF_REG_IER 0x0c /* interrupt enable, R/W */ +#define PDAUDIOCF_REG_AK_IFR 0x0e /* AK interface register, R/W */ + +/* PDAUDIOCF_REG_TCR */ +#define PDAUDIOCF_ELIMAKMBIT (1<<0) /* simulate AKM music data */ +#define PDAUDIOCF_TESTDATASEL (1<<1) /* test data selection, 0 = 0x55, 1 = pseudo-random */ + +/* PDAUDIOCF_REG_SCR */ +#define PDAUDIOCF_AK_SBP (1<<0) /* serial port busy flag */ +#define PDAUDIOCF_RST (1<<2) /* FPGA, AKM + SRAM buffer reset */ +#define PDAUDIOCF_PDN (1<<3) /* power down bit */ +#define PDAUDIOCF_CLKDIV0 (1<<4) /* choose 24.576Mhz clock divided by 1,2,3 or 4 */ +#define PDAUDIOCF_CLKDIV1 (1<<5) +#define PDAUDIOCF_RECORD (1<<6) /* start capturing to SRAM */ +#define PDAUDIOCF_AK_SDD (1<<7) /* music data detected */ +#define PDAUDIOCF_RED_LED_OFF (1<<8) /* red LED off override */ +#define PDAUDIOCF_BLUE_LED_OFF (1<<9) /* blue LED off override */ +#define PDAUDIOCF_DATAFMT0 (1<<10) /* data format bits: 00 = 16-bit, 01 = 18-bit */ +#define PDAUDIOCF_DATAFMT1 (1<<11) /* 10 = 20-bit, 11 = 24-bit, all right justified */ +#define PDAUDIOCF_FPGAREV(x) ((x>>12)&0x0f) /* FPGA revision */ + +/* PDAUDIOCF_REG_ISR */ +#define PDAUDIOCF_IRQLVL (1<<0) /* Buffer level IRQ */ +#define PDAUDIOCF_IRQOVR (1<<1) /* Overrun IRQ */ +#define PDAUDIOCF_IRQAKM (1<<2) /* AKM IRQ */ + +/* PDAUDIOCF_REG_IER */ +#define PDAUDIOCF_IRQLVLEN0 (1<<0) /* fill threshold levels; 00 = none, 01 = 1/8th of buffer */ +#define PDAUDIOCF_IRQLVLEN1 (1<<1) /* 10 = 1/4th of buffer, 11 = 1/2th of buffer */ +#define PDAUDIOCF_IRQOVREN (1<<2) /* enable overrun IRQ */ +#define PDAUDIOCF_IRQAKMEN (1<<3) /* enable AKM IRQ */ +#define PDAUDIOCF_BLUEDUTY0 (1<<8) /* blue LED duty cycle; 00 = 100%, 01 = 50% */ +#define PDAUDIOCF_BLUEDUTY1 (1<<9) /* 02 = 25%, 11 = 12% */ +#define PDAUDIOCF_REDDUTY0 (1<<10) /* red LED duty cycle; 00 = 100%, 01 = 50% */ +#define PDAUDIOCF_REDDUTY1 (1<<11) /* 02 = 25%, 11 = 12% */ +#define PDAUDIOCF_BLUESDD (1<<12) /* blue LED against SDD bit */ +#define PDAUDIOCF_BLUEMODULATE (1<<13) /* save power when 100% duty cycle selected */ +#define PDAUDIOCF_REDMODULATE (1<<14) /* save power when 100% duty cycle selected */ +#define PDAUDIOCF_HALFRATE (1<<15) /* slow both LED blinks by half (also spdif detect rate) */ + +/* chip status */ +#define PDAUDIOCF_STAT_IS_STALE (1<<0) +#define PDAUDIOCF_STAT_IS_CONFIGURED (1<<1) +#define PDAUDIOCF_STAT_IS_SUSPENDED (1<<2) + +typedef struct { + snd_card_t *card; + int index; + + unsigned long port; + int irq; + + spinlock_t reg_lock; + unsigned short regmap[8]; + unsigned short suspend_reg_scr; + struct tasklet_struct tq; + + spinlock_t ak4117_lock; + ak4117_t *ak4117; + + unsigned int chip_status; + + snd_pcm_t *pcm; + snd_pcm_substream_t *pcm_substream; + unsigned int pcm_running: 1; + unsigned int pcm_channels; + unsigned int pcm_swab; + unsigned int pcm_little; + unsigned int pcm_frame; + unsigned int pcm_sample; + unsigned int pcm_xor; + unsigned int pcm_size; + unsigned int pcm_period; + unsigned int pcm_tdone; + unsigned int pcm_hwptr; + void *pcm_area; + + /* pcmcia stuff */ + dev_link_t link; + dev_node_t node; +} pdacf_t; + +static inline void pdacf_reg_write(pdacf_t *chip, unsigned char reg, unsigned short val) +{ + outw(chip->regmap[reg>>1] = val, chip->port + reg); +} + +static inline unsigned short pdacf_reg_read(pdacf_t *chip, unsigned char reg) +{ + return inw(chip->port + reg); +} + +unsigned char pdacf_ak4117_read(void *private_data, unsigned char reg); +void pdacf_ak4117_write(void *private_data, unsigned char reg, unsigned char val); +pdacf_t *snd_pdacf_create(snd_card_t *card); +int snd_pdacf_ak4117_create(pdacf_t *pdacf); +void snd_pdacf_powerdown(pdacf_t *chip); +#ifdef CONFIG_PM +void snd_pdacf_suspend(pdacf_t *chip); +void snd_pdacf_resume(pdacf_t *chip); +int snd_pdacf_set_power_state(snd_card_t *card, unsigned int power_state); +#endif +int snd_pdacf_pcm_new(pdacf_t *chip); +void pdacf_interrupt(int irq, void *dev, struct pt_regs *regs); +void pdacf_tasklet(unsigned long private_data); +void pdacf_reinit(pdacf_t *chip, int resume); + +#endif /* __PDAUDIOCF_H */ diff -Nru a/sound/pcmcia/pdaudiocf/pdaudiocf_core.c b/sound/pcmcia/pdaudiocf/pdaudiocf_core.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pcmcia/pdaudiocf/pdaudiocf_core.c Sun Feb 29 12:58:39 2004 @@ -0,0 +1,317 @@ +/* + * Driver for Sound Core PDAudioCF soundcard + * + * Copyright (c) 2003 by Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include "pdaudiocf.h" +#define SNDRV_GET_ID +#include + +/* + * + */ +unsigned char pdacf_ak4117_read(void *private_data, unsigned char reg) +{ + pdacf_t *chip = snd_magic_cast(pdacf_t, private_data, return 0); + unsigned long timeout; + unsigned long flags; + unsigned char res; + + spin_lock_irqsave(&chip->ak4117_lock, flags); + timeout = 1000; + while (pdacf_reg_read(chip, PDAUDIOCF_REG_SCR) & PDAUDIOCF_AK_SBP) { + udelay(5); + if (--timeout == 0) { + spin_unlock_irqrestore(&chip->ak4117_lock, flags); + snd_printk(KERN_ERR "AK4117 ready timeout (read)\n"); + return 0; + } + } + pdacf_reg_write(chip, PDAUDIOCF_REG_AK_IFR, (u16)reg << 8); + timeout = 1000; + while (pdacf_reg_read(chip, PDAUDIOCF_REG_SCR) & PDAUDIOCF_AK_SBP) { + udelay(5); + if (--timeout == 0) { + spin_unlock_irqrestore(&chip->ak4117_lock, flags); + snd_printk(KERN_ERR "AK4117 read timeout (read2)\n"); + return 0; + } + } + res = (unsigned char)pdacf_reg_read(chip, PDAUDIOCF_REG_AK_IFR); + spin_unlock_irqrestore(&chip->ak4117_lock, flags); + return res; +} + +void pdacf_ak4117_write(void *private_data, unsigned char reg, unsigned char val) +{ + pdacf_t *chip = snd_magic_cast(pdacf_t, private_data, return); + unsigned long timeout; + unsigned long flags; + + spin_lock_irqsave(&chip->ak4117_lock, flags); + timeout = 1000; + while (inw(chip->port + PDAUDIOCF_REG_SCR) & PDAUDIOCF_AK_SBP) { + udelay(5); + if (--timeout == 0) { + spin_unlock_irqrestore(&chip->ak4117_lock, flags); + snd_printk(KERN_ERR "AK4117 ready timeout (write)\n"); + return; + } + } + outw((u16)reg << 8 | val | (1<<13), chip->port + PDAUDIOCF_REG_AK_IFR); + spin_unlock_irqrestore(&chip->ak4117_lock, flags); +} + +#if 0 +void pdacf_dump(pdacf_t *chip) +{ + printk("PDAUDIOCF DUMP (0x%lx):\n", chip->port); + printk("WPD : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_WDP)); + printk("RDP : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_RDP)); + printk("TCR : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_TCR)); + printk("SCR : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_SCR)); + printk("ISR : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_ISR)); + printk("IER : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_IER)); + printk("AK_IFR : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_AK_IFR)); +} +#endif + +static int pdacf_reset(pdacf_t *chip, int powerdown) +{ + u16 val; + + val = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); + val |= PDAUDIOCF_PDN; + val &= ~PDAUDIOCF_RECORD; /* for sure */ + pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); + udelay(5); + val |= PDAUDIOCF_RST; + pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); + udelay(200); + val &= ~PDAUDIOCF_RST; + pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); + udelay(5); + if (!powerdown) { + val &= ~PDAUDIOCF_PDN; + pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); + udelay(200); + } + return 0; +} + +void pdacf_reinit(pdacf_t *chip, int resume) +{ + pdacf_reset(chip, 0); + if (resume) + pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, chip->suspend_reg_scr); + snd_ak4117_reinit(chip->ak4117); + pdacf_reg_write(chip, PDAUDIOCF_REG_TCR, chip->regmap[PDAUDIOCF_REG_TCR>>1]); + pdacf_reg_write(chip, PDAUDIOCF_REG_IER, chip->regmap[PDAUDIOCF_REG_IER>>1]); +} + +static void pdacf_proc_read(snd_info_entry_t * entry, + snd_info_buffer_t * buffer) +{ + pdacf_t *chip = snd_magic_cast(pdacf_t, entry->private_data, return); + u16 tmp; + + snd_iprintf(buffer, "PDAudioCF\n\n"); + tmp = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); + snd_iprintf(buffer, "FPGA revision : 0x%x\n", PDAUDIOCF_FPGAREV(tmp)); + +} + +static void pdacf_proc_init(pdacf_t *chip) +{ + snd_info_entry_t *entry; + + if (! snd_card_proc_new(chip->card, "pdaudiocf", &entry)) + snd_info_set_text_ops(entry, chip, 1024, pdacf_proc_read); +} + +pdacf_t *snd_pdacf_create(snd_card_t *card) +{ + pdacf_t *chip; + + chip = snd_magic_kcalloc(pdacf_t, 0, GFP_KERNEL); + if (chip == NULL) + return NULL; + chip->card = card; + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->ak4117_lock); + tasklet_init(&chip->tq, pdacf_tasklet, (unsigned long)chip); + card->private_data = chip; + + pdacf_proc_init(chip); + return chip; +} + +static void snd_pdacf_ak4117_change(ak4117_t *ak4117, unsigned char c0, unsigned char c1) +{ + pdacf_t *chip = ak4117->change_callback_private; + unsigned long flags; + u16 val; + + if (!(c0 & AK4117_UNLCK)) + return; + spin_lock_irqsave(&chip->reg_lock, flags); + val = chip->regmap[PDAUDIOCF_REG_SCR>>1]; + if (ak4117->rcs0 & AK4117_UNLCK) + val |= PDAUDIOCF_BLUE_LED_OFF; + else + val &= ~PDAUDIOCF_BLUE_LED_OFF; + pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); + spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +int snd_pdacf_ak4117_create(pdacf_t *chip) +{ + int err; + u16 val; + /* design note: if we unmask PLL unlock, parity, valid, audio or auto bit interrupts */ + /* from AK4117 then INT1 pin from AK4117 will be high all time, because PCMCIA interrupts are */ + /* egde based and FPGA does logical OR for all interrupt sources, we cannot use these */ + /* high-rate sources */ + static unsigned char pgm[5] = { + AK4117_XTL_24_576M | AK4117_EXCT, /* AK4117_REG_PWRDN */ + AK4117_CM_PLL_XTAL | AK4117_PKCS_128fs | AK4117_XCKS_128fs, /* AK4117_REQ_CLOCK */ + AK4117_EFH_1024LRCLK | AK4117_DIF_24R | AK4117_IPS, /* AK4117_REG_IO */ + 0xff, /* AK4117_REG_INT0_MASK */ + AK4117_MAUTO | AK4117_MAUD | AK4117_MULK | AK4117_MPAR | AK4117_MV, /* AK4117_REG_INT1_MASK */ + }; + + err = pdacf_reset(chip, 0); + if (err < 0) + return err; + err = snd_ak4117_create(chip->card, pdacf_ak4117_read, pdacf_ak4117_write, pgm, chip, &chip->ak4117); + if (err < 0) + return err; + + val = pdacf_reg_read(chip, PDAUDIOCF_REG_TCR); +#if 1 /* normal operation */ + val &= ~(PDAUDIOCF_ELIMAKMBIT|PDAUDIOCF_TESTDATASEL); +#else /* debug */ + val |= PDAUDIOCF_ELIMAKMBIT; + val &= ~PDAUDIOCF_TESTDATASEL; +#endif + pdacf_reg_write(chip, PDAUDIOCF_REG_TCR, val); + + /* setup the FPGA to match AK4117 setup */ + val = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); + val &= ~(PDAUDIOCF_CLKDIV0 | PDAUDIOCF_CLKDIV1); /* use 24.576Mhz clock */ + val &= ~(PDAUDIOCF_RED_LED_OFF|PDAUDIOCF_BLUE_LED_OFF); + val |= PDAUDIOCF_DATAFMT0 | PDAUDIOCF_DATAFMT1; /* 24-bit data */ + pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); + + /* setup LEDs and IRQ */ + val = pdacf_reg_read(chip, PDAUDIOCF_REG_IER); + val &= ~(PDAUDIOCF_IRQLVLEN0 | PDAUDIOCF_IRQLVLEN1); + val &= ~(PDAUDIOCF_BLUEDUTY0 | PDAUDIOCF_REDDUTY0 | PDAUDIOCF_REDDUTY1); + val |= PDAUDIOCF_BLUEDUTY1 | PDAUDIOCF_HALFRATE; + val |= PDAUDIOCF_IRQOVREN | PDAUDIOCF_IRQAKMEN; + pdacf_reg_write(chip, PDAUDIOCF_REG_IER, val); + + chip->ak4117->change_callback_private = chip; + chip->ak4117->change_callback = snd_pdacf_ak4117_change; + + /* update LED status */ + snd_pdacf_ak4117_change(chip->ak4117, AK4117_UNLCK, 0); + + return 0; +} + +void snd_pdacf_powerdown(pdacf_t *chip) +{ + u16 val; + + val = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); + chip->suspend_reg_scr = val; + val |= PDAUDIOCF_RED_LED_OFF | PDAUDIOCF_BLUE_LED_OFF; + pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); + /* disable interrupts, but use direct write to preserve old register value in chip->regmap */ + val = inw(chip->port + PDAUDIOCF_REG_IER); + val &= ~(PDAUDIOCF_IRQOVREN|PDAUDIOCF_IRQAKMEN|PDAUDIOCF_IRQLVLEN0|PDAUDIOCF_IRQLVLEN1); + outw(val, chip->port + PDAUDIOCF_REG_IER); + pdacf_reset(chip, 1); +} + +#ifdef CONFIG_PM + +void snd_pdacf_suspend(pdacf_t *chip) +{ + snd_card_t *card = chip->card; + u16 val; + + if (card->power_state == SNDRV_CTL_POWER_D3hot) + return; + snd_pcm_suspend_all(chip->pcm); + /* disable interrupts, but use direct write to preserve old register value in chip->regmap */ + val = inw(chip->port + PDAUDIOCF_REG_IER); + val &= ~(PDAUDIOCF_IRQOVREN|PDAUDIOCF_IRQAKMEN|PDAUDIOCF_IRQLVLEN0|PDAUDIOCF_IRQLVLEN1); + outw(val, chip->port + PDAUDIOCF_REG_IER); + chip->chip_status |= PDAUDIOCF_STAT_IS_SUSPENDED; /* ignore interrupts from now */ + snd_pdacf_powerdown(chip); + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); +} + +static inline int check_signal(pdacf_t *chip) +{ + return (chip->ak4117->rcs0 & AK4117_UNLCK) == 0; +} + +void snd_pdacf_resume(pdacf_t *chip) +{ + snd_card_t *card = chip->card; + int timeout = 40; + + if (card->power_state == SNDRV_CTL_POWER_D0) + return; + pdacf_reinit(chip, 1); + /* wait for AK4117's PLL */ + while (timeout-- > 0 && + (snd_ak4117_external_rate(chip->ak4117) <= 0 || !check_signal(chip))) + mdelay(1); + chip->chip_status &= ~PDAUDIOCF_STAT_IS_SUSPENDED; + snd_power_change_state(card, SNDRV_CTL_POWER_D0); +} + +int snd_pdacf_set_power_state(snd_card_t *card, unsigned int power_state) +{ + pdacf_t *chip = snd_magic_cast(pdacf_t, card->power_state_private_data, return -ENXIO); + + switch (power_state) { + case SNDRV_CTL_POWER_D0: + case SNDRV_CTL_POWER_D1: + case SNDRV_CTL_POWER_D2: + snd_pdacf_resume(chip); + break; + case SNDRV_CTL_POWER_D3hot: + case SNDRV_CTL_POWER_D3cold: + snd_pdacf_suspend(chip); + break; + default: + return -EINVAL; + } + return 0; +} + +#endif diff -Nru a/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c b/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c Sun Feb 29 12:58:39 2004 @@ -0,0 +1,325 @@ +/* + * Driver for Sound Core PDAudioCF soundcard + * + * Copyright (c) 2003 by Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include "pdaudiocf.h" +#define SNDRV_GET_ID +#include + +/* + * + */ +void pdacf_interrupt(int irq, void *dev, struct pt_regs *regs) +{ + pdacf_t *chip = snd_magic_cast(pdacf_t, dev, return); + unsigned short stat; + + if ((chip->chip_status & (PDAUDIOCF_STAT_IS_STALE| + PDAUDIOCF_STAT_IS_CONFIGURED| + PDAUDIOCF_STAT_IS_SUSPENDED)) != PDAUDIOCF_STAT_IS_CONFIGURED) + return; + + stat = inw(chip->port + PDAUDIOCF_REG_ISR); + if (stat & (PDAUDIOCF_IRQLVL|PDAUDIOCF_IRQOVR)) { + if (stat & PDAUDIOCF_IRQOVR) /* should never happen */ + snd_printk(KERN_ERR "PDAUDIOCF SRAM buffer overrun detected!\n"); + if (chip->pcm_substream) + tasklet_hi_schedule(&chip->tq); + if (!(stat & PDAUDIOCF_IRQAKM)) + stat |= PDAUDIOCF_IRQAKM; /* check rate */ + } + if (regs != NULL) + snd_ak4117_check_rate_and_errors(chip->ak4117, 0); +} + +static inline void pdacf_transfer_mono16(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port) +{ + while (size-- > 0) { + *dst++ = inw(rdp_port) ^ xor; + inw(rdp_port); + } +} + +static inline void pdacf_transfer_mono32(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port) +{ + register u16 val1, val2; + + while (size-- > 0) { + val1 = inw(rdp_port); + val2 = inw(rdp_port); + inw(rdp_port); + *dst++ = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor; + } +} + +static inline void pdacf_transfer_stereo16(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port) +{ + while (size-- > 0) { + *dst++ = inw(rdp_port) ^ xor; + *dst++ = inw(rdp_port) ^ xor; + } +} + +static inline void pdacf_transfer_stereo32(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port) +{ + register u16 val1, val2, val3; + + while (size-- > 0) { + val1 = inw(rdp_port); + val2 = inw(rdp_port); + val3 = inw(rdp_port); + *dst++ = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor; + *dst++ = (((u32)val3 << 16) | (val2 & 0xff00)) ^ xor; + } +} + +static inline void pdacf_transfer_mono16sw(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port) +{ + while (size-- > 0) { + *dst++ = swab16(inw(rdp_port) ^ xor); + inw(rdp_port); + } +} + +static inline void pdacf_transfer_mono32sw(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port) +{ + register u16 val1, val2; + + while (size-- > 0) { + val1 = inw(rdp_port); + val2 = inw(rdp_port); + inw(rdp_port); + *dst++ = swab32((((val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor); + } +} + +static inline void pdacf_transfer_stereo16sw(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port) +{ + while (size-- > 0) { + *dst++ = swab16(inw(rdp_port) ^ xor); + *dst++ = swab16(inw(rdp_port) ^ xor); + } +} + +static inline void pdacf_transfer_stereo32sw(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port) +{ + register u16 val1, val2, val3; + + while (size-- > 0) { + val1 = inw(rdp_port); + val2 = inw(rdp_port); + val3 = inw(rdp_port); + *dst++ = swab32((((val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor); + *dst++ = swab32((((u32)val3 << 16) | (val2 & 0xff00)) ^ xor); + } +} + +static inline void pdacf_transfer_mono24le(u8 *dst, u16 xor, unsigned int size, unsigned long rdp_port) +{ + register u16 val1, val2; + register u32 xval1; + + while (size-- > 0) { + val1 = inw(rdp_port); + val2 = inw(rdp_port); + inw(rdp_port); + xval1 = (((val2 & 0xff) << 8) | (val1 << 16)) ^ xor; + *dst++ = (u8)(xval1 >> 8); + *dst++ = (u8)(xval1 >> 16); + *dst++ = (u8)(xval1 >> 24); + } +} + +static inline void pdacf_transfer_mono24be(u8 *dst, u16 xor, unsigned int size, unsigned long rdp_port) +{ + register u16 val1, val2; + register u32 xval1; + + while (size-- > 0) { + val1 = inw(rdp_port); + val2 = inw(rdp_port); + inw(rdp_port); + xval1 = (((val2 & 0xff) << 8) | (val1 << 16)) ^ xor; + *dst++ = (u8)(xval1 >> 24); + *dst++ = (u8)(xval1 >> 16); + *dst++ = (u8)(xval1 >> 8); + } +} + +static inline void pdacf_transfer_stereo24le(u8 *dst, u32 xor, unsigned int size, unsigned long rdp_port) +{ + register u16 val1, val2, val3; + register u32 xval1, xval2; + + while (size-- > 0) { + val1 = inw(rdp_port); + val2 = inw(rdp_port); + val3 = inw(rdp_port); + xval1 = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor; + xval2 = (((u32)val3 << 16) | (val2 & 0xff00)) ^ xor; + *dst++ = (u8)(xval1 >> 8); + *dst++ = (u8)(xval1 >> 16); + *dst++ = (u8)(xval1 >> 24); + *dst++ = (u8)(xval2 >> 8); + *dst++ = (u8)(xval2 >> 16); + *dst++ = (u8)(xval2 >> 24); + } +} + +static inline void pdacf_transfer_stereo24be(u8 *dst, u32 xor, unsigned int size, unsigned long rdp_port) +{ + register u16 val1, val2, val3; + register u32 xval1, xval2; + + while (size-- > 0) { + val1 = inw(rdp_port); + val2 = inw(rdp_port); + val3 = inw(rdp_port); + xval1 = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor; + xval2 = (((u32)val3 << 16) | (val2 & 0xff00)) ^ xor; + *dst++ = (u8)(xval1 >> 24); + *dst++ = (u8)(xval1 >> 16); + *dst++ = (u8)(xval1 >> 8); + *dst++ = (u8)(xval2 >> 24); + *dst++ = (u8)(xval2 >> 16); + *dst++ = (u8)(xval2 >> 8); + } +} + +static void pdacf_transfer(pdacf_t *chip, unsigned int size, unsigned int off) +{ + unsigned long rdp_port = chip->port + PDAUDIOCF_REG_MD; + unsigned int xor = chip->pcm_xor; + + if (chip->pcm_sample == 3) { + if (chip->pcm_little) { + if (chip->pcm_channels == 1) { + pdacf_transfer_mono24le((char *)chip->pcm_area + (off * 3), xor, size, rdp_port); + } else { + pdacf_transfer_stereo24le((char *)chip->pcm_area + (off * 6), xor, size, rdp_port); + } + } else { + if (chip->pcm_channels == 1) { + pdacf_transfer_mono24be((char *)chip->pcm_area + (off * 3), xor, size, rdp_port); + } else { + pdacf_transfer_stereo24be((char *)chip->pcm_area + (off * 6), xor, size, rdp_port); + } + } + return; + } + if (chip->pcm_swab == 0) { + if (chip->pcm_channels == 1) { + if (chip->pcm_frame == 2) { + pdacf_transfer_mono16((u16 *)chip->pcm_area + off, xor, size, rdp_port); + } else { + pdacf_transfer_mono32((u32 *)chip->pcm_area + off, xor, size, rdp_port); + } + } else { + if (chip->pcm_frame == 2) { + pdacf_transfer_stereo16((u16 *)chip->pcm_area + (off * 2), xor, size, rdp_port); + } else { + pdacf_transfer_stereo32((u32 *)chip->pcm_area + (off * 2), xor, size, rdp_port); + } + } + } else { + if (chip->pcm_channels == 1) { + if (chip->pcm_frame == 2) { + pdacf_transfer_mono16sw((u16 *)chip->pcm_area + off, xor, size, rdp_port); + } else { + pdacf_transfer_mono32sw((u32 *)chip->pcm_area + off, xor, size, rdp_port); + } + } else { + if (chip->pcm_frame == 2) { + pdacf_transfer_stereo16sw((u16 *)chip->pcm_area + (off * 2), xor, size, rdp_port); + } else { + pdacf_transfer_stereo32sw((u32 *)chip->pcm_area + (off * 2), xor, size, rdp_port); + } + } + } +} + +void pdacf_tasklet(unsigned long private_data) +{ + pdacf_t *chip = snd_magic_cast(pdacf_t, (void *)private_data, return); + int size, off, cont, rdp, wdp; + + if ((chip->chip_status & (PDAUDIOCF_STAT_IS_STALE|PDAUDIOCF_STAT_IS_CONFIGURED)) != PDAUDIOCF_STAT_IS_CONFIGURED) + return; + + if (chip->pcm_substream == NULL || chip->pcm_substream->runtime == NULL || !snd_pcm_running(chip->pcm_substream)) + return; + + rdp = inw(chip->port + PDAUDIOCF_REG_RDP); + wdp = inw(chip->port + PDAUDIOCF_REG_WDP); + // printk("TASKLET: rdp = %x, wdp = %x\n", rdp, wdp); + size = wdp - rdp; + if (size < 0) + size += 0x10000; + if (size == 0) + size = 0x10000; + size /= chip->pcm_frame; + if (size > 64) + size -= 32; + +#if 0 + chip->pcm_hwptr += size; + chip->pcm_hwptr %= chip->pcm_size; + chip->pcm_tdone += size; + if (chip->pcm_frame == 2) { + unsigned long rdp_port = chip->port + PDAUDIOCF_REG_MD; + while (size-- > 0) { + inw(rdp_port); + inw(rdp_port); + } + } else { + unsigned long rdp_port = chip->port + PDAUDIOCF_REG_MD; + while (size-- > 0) { + inw(rdp_port); + inw(rdp_port); + inw(rdp_port); + } + } +#else + off = chip->pcm_hwptr + chip->pcm_tdone; + off %= chip->pcm_size; + chip->pcm_tdone += size; + while (size > 0) { + cont = chip->pcm_size - off; + if (cont > size) + cont = size; + pdacf_transfer(chip, cont, off); + off += cont; + off %= chip->pcm_size; + size -= cont; + } +#endif + spin_lock(&chip->reg_lock); + while (chip->pcm_tdone >= chip->pcm_period) { + chip->pcm_hwptr += chip->pcm_period; + chip->pcm_hwptr %= chip->pcm_size; + chip->pcm_tdone -= chip->pcm_period; + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(chip->pcm_substream); + spin_lock(&chip->reg_lock); + } + spin_unlock(&chip->reg_lock); + // printk("TASKLET: end\n"); +} diff -Nru a/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c b/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c Sun Feb 29 12:58:39 2004 @@ -0,0 +1,363 @@ +/* + * Driver for Sound Core PDAudioCF soundcards + * + * PCM part + * + * Copyright (c) 2003 by Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include "pdaudiocf.h" + +#define chip_t pdacf_t + + +/* + * we use a vmalloc'ed (sg-)buffer + */ + +/* get the physical page pointer on the given offset */ +static struct page *snd_pcm_get_vmalloc_page(snd_pcm_substream_t *subs, unsigned long offset) +{ + void *pageptr = subs->runtime->dma_area + offset; + return vmalloc_to_page(pageptr); +} + +/* + * hw_params callback + * NOTE: this may be called not only once per pcm open! + */ +static int snd_pcm_alloc_vmalloc_buffer(snd_pcm_substream_t *subs, size_t size) +{ + snd_pcm_runtime_t *runtime = subs->runtime; + if (runtime->dma_area) { + if (runtime->dma_bytes >= size) + return 0; /* already enough large */ + vfree_nocheck(runtime->dma_area); + } + runtime->dma_area = vmalloc_nocheck(size); + if (! runtime->dma_area) + return -ENOMEM; + runtime->dma_bytes = size; + return 0; +} + +/* + * hw_free callback + * NOTE: this may be called not only once per pcm open! + */ +static int snd_pcm_free_vmalloc_buffer(snd_pcm_substream_t *subs) +{ + snd_pcm_runtime_t *runtime = subs->runtime; + if (runtime->dma_area) { + vfree_nocheck(runtime->dma_area); + runtime->dma_area = NULL; + } + return 0; +} + +/* + * clear the SRAM contents + */ +static int pdacf_pcm_clear_sram(pdacf_t *chip) +{ + int max_loop = 64 * 1024; + + while (inw(chip->port + PDAUDIOCF_REG_RDP) != inw(chip->port + PDAUDIOCF_REG_WDP)) { + if (max_loop-- < 0) + return -EIO; + inw(chip->port + PDAUDIOCF_REG_MD); + } + return 0; +} + +/* + * pdacf_pcm_trigger - trigger callback for capture + */ +static int pdacf_pcm_trigger(snd_pcm_substream_t *subs, int cmd) +{ + pdacf_t *chip = snd_pcm_substream_chip(subs); + snd_pcm_runtime_t *runtime = subs->runtime; + int inc, ret = 0, rate; + unsigned short mask, val, tmp; + + if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE) + return -EBUSY; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + chip->pcm_hwptr = 0; + chip->pcm_tdone = 0; + /* fall thru */ + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + mask = 0; + val = PDAUDIOCF_RECORD; + inc = 1; + rate = snd_ak4117_check_rate_and_errors(chip->ak4117, AK4117_CHECK_NO_STAT|AK4117_CHECK_NO_RATE); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + mask = PDAUDIOCF_RECORD; + val = 0; + inc = -1; + rate = 0; + break; + default: + return -EINVAL; + } + spin_lock(&chip->reg_lock); + chip->pcm_running += inc; + tmp = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); + if (chip->pcm_running) { + if ((chip->ak4117->rcs0 & AK4117_UNLCK) || runtime->rate != rate) { + chip->pcm_running -= inc; + ret = -EIO; + goto __end; + } + } + tmp &= ~mask; + tmp |= val; + pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, tmp); + __end: + spin_unlock(&chip->reg_lock); + snd_ak4117_check_rate_and_errors(chip->ak4117, AK4117_CHECK_NO_RATE); + return ret; +} + +/* + * pdacf_pcm_hw_params - hw_params callback for playback and capture + */ +static int pdacf_pcm_hw_params(snd_pcm_substream_t *subs, + snd_pcm_hw_params_t *hw_params) +{ + return snd_pcm_alloc_vmalloc_buffer(subs, params_buffer_bytes(hw_params)); +} + +/* + * pdacf_pcm_hw_free - hw_free callback for playback and capture + */ +static int pdacf_pcm_hw_free(snd_pcm_substream_t *subs) +{ + return snd_pcm_free_vmalloc_buffer(subs); +} + +/* + * pdacf_pcm_prepare - prepare callback for playback and capture + */ +static int pdacf_pcm_prepare(snd_pcm_substream_t *subs) +{ + pdacf_t *chip = snd_pcm_substream_chip(subs); + snd_pcm_runtime_t *runtime = subs->runtime; + u16 val, nval, aval; + + if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE) + return -EBUSY; + + chip->pcm_channels = runtime->channels; + + chip->pcm_little = snd_pcm_format_little_endian(runtime->format) > 0; +#ifdef SNDRV_LITTLE_ENDIAN + chip->pcm_swab = snd_pcm_format_big_endian(runtime->format) > 0; +#else + chip->pcm_swab = chip->pcm_little; +#endif + + if (snd_pcm_format_unsigned(runtime->format)) + chip->pcm_xor = 0x80008000; + + if (pdacf_pcm_clear_sram(chip) < 0) + return -EIO; + + val = nval = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); + nval &= ~(PDAUDIOCF_DATAFMT0|PDAUDIOCF_DATAFMT1); + switch (runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + break; + default: /* 24-bit */ + nval |= PDAUDIOCF_DATAFMT0 | PDAUDIOCF_DATAFMT1; + break; + } + aval = 0; + chip->pcm_sample = 4; + switch (runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + aval = AK4117_DIF_16R; + chip->pcm_frame = 2; + chip->pcm_sample = 2; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S24_3BE: + chip->pcm_sample = 3; + /* fall trough */ + default: /* 24-bit */ + aval = AK4117_DIF_24R; + chip->pcm_frame = 3; + chip->pcm_xor &= 0xffff0000; + break; + } + + if (val != nval) { + snd_ak4117_reg_write(chip->ak4117, AK4117_REG_IO, AK4117_DIF2|AK4117_DIF1|AK4117_DIF0, aval); + pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, nval); + } + + val = pdacf_reg_read(chip, PDAUDIOCF_REG_IER); + val &= ~(PDAUDIOCF_IRQLVLEN1); + val |= PDAUDIOCF_IRQLVLEN0; + pdacf_reg_write(chip, PDAUDIOCF_REG_IER, val); + + chip->pcm_size = runtime->buffer_size; + chip->pcm_period = runtime->period_size; + chip->pcm_area = runtime->dma_area; + + return 0; +} + + +/* + * capture hw information + */ + +static snd_pcm_hardware_t pdacf_pcm_capture_hw = { + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .rate_min = 32000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (512*1024), + .period_bytes_min = 8*1024, + .period_bytes_max = (64*1024), + .periods_min = 2, + .periods_max = 128, + .fifo_size = 0, +}; + + +/* + * pdacf_pcm_capture_open - open callback for capture + */ +static int pdacf_pcm_capture_open(snd_pcm_substream_t *subs) +{ + snd_pcm_runtime_t *runtime = subs->runtime; + pdacf_t *chip = snd_pcm_substream_chip(subs); + + if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE) + return -EBUSY; + + runtime->hw = pdacf_pcm_capture_hw; + runtime->private_data = chip; + chip->pcm_substream = subs; + + return 0; +} + +/* + * pdacf_pcm_capture_close - close callback for capture + */ +static int pdacf_pcm_capture_close(snd_pcm_substream_t *subs) +{ + pdacf_t *chip = snd_pcm_substream_chip(subs); + + if (!chip) + return -EINVAL; + pdacf_reinit(chip, 0); + chip->pcm_substream = NULL; + return 0; +} + + +/* + * pdacf_pcm_capture_pointer - pointer callback for capture + */ +static snd_pcm_uframes_t pdacf_pcm_capture_pointer(snd_pcm_substream_t *subs) +{ + pdacf_t *chip = snd_pcm_substream_chip(subs); + return chip->pcm_hwptr; +} + +/* + * operators for PCM capture + */ +static snd_pcm_ops_t pdacf_pcm_capture_ops = { + .open = pdacf_pcm_capture_open, + .close = pdacf_pcm_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pdacf_pcm_hw_params, + .hw_free = pdacf_pcm_hw_free, + .prepare = pdacf_pcm_prepare, + .trigger = pdacf_pcm_trigger, + .pointer = pdacf_pcm_capture_pointer, + .page = snd_pcm_get_vmalloc_page, +}; + + +/* + * free callback for pcm + */ +static void snd_pdacf_pcm_free(snd_pcm_t *pcm) +{ + pdacf_t *chip = snd_magic_cast(pdacf_t, pcm->private_data, return); + chip->pcm = NULL; +} + +/* + * snd_pdacf_pcm_new - create and initialize a pcm + */ +int snd_pdacf_pcm_new(pdacf_t *chip) +{ + snd_pcm_t *pcm; + int err; + + err = snd_pcm_new(chip->card, "PDAudioCF", 0, 0, 1, &pcm); + if (err < 0) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pdacf_pcm_capture_ops); + + pcm->private_data = chip; + pcm->private_free = snd_pdacf_pcm_free; + pcm->info_flags = 0; + strcpy(pcm->name, chip->card->shortname); + chip->pcm = pcm; + + err = snd_ak4117_build(chip->ak4117, pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); + if (err < 0) + return err; + + return 0; +} diff -Nru a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c --- a/sound/ppc/tumbler.c Sun Feb 29 12:58:38 2004 +++ b/sound/ppc/tumbler.c Sun Feb 29 12:58:38 2004 @@ -929,8 +929,8 @@ 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_mono_volume(mix, &snapper_bass_vol_info); + tumbler_set_mono_volume(mix, &snapper_treble_vol_info); snapper_set_drc(mix); } tumbler_set_master_volume(mix); diff -Nru a/sound/synth/Makefile b/sound/synth/Makefile --- a/sound/synth/Makefile Sun Feb 29 12:58:38 2004 +++ b/sound/synth/Makefile Sun Feb 29 12:58:38 2004 @@ -5,10 +5,16 @@ snd-util-mem-objs := util_mem.o +# +# this function returns: +# "m" - CONFIG_SND_SEQUENCER is m +# - CONFIG_SND_SEQUENCER is undefined +# otherwise parameter #1 value +# +sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1))) + # Toplevel Module Dependency obj-$(CONFIG_SND_EMU10K1) += snd-util-mem.o obj-$(CONFIG_SND_TRIDENT) += snd-util-mem.o -ifdef CONFIG_SND_SEQUENCER - obj-$(CONFIG_SND_SBAWE) += snd-util-mem.o - obj-$(CONFIG_SND) += emux/ -endif +obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-util-mem.o +obj-$(call sequencer,$(CONFIG_SND)) += emux/ diff -Nru a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c --- a/sound/usb/usbaudio.c Sun Feb 29 12:58:39 2004 +++ b/sound/usb/usbaudio.c Sun Feb 29 12:58:39 2004 @@ -45,7 +45,6 @@ #include #include #include -#include #include #include #include @@ -117,7 +116,7 @@ struct list_head list; snd_pcm_format_t format; /* format type */ unsigned int channels; /* # channels */ - unsigned int nonaudio: 1; /* non-audio (type II) */ + unsigned int fmt_type; /* USB audio format type (1-3) */ unsigned int frame_size; /* samples per frame for non-audio */ int iface; /* interface number */ unsigned char altsetting; /* corresponding alternate setting */ @@ -171,7 +170,7 @@ unsigned int curpacksize; /* current packet size in bytes (for capture) */ unsigned int curframesize; /* current packet size in frames (for capture) */ unsigned int fill_max: 1; /* fill max packet size always */ - unsigned int nonaudio: 1; /* Type II format (MPEG, AC3) */ + unsigned int fmt_type; /* USB audio format type (1-3) */ unsigned int running: 1; /* running status */ @@ -201,6 +200,7 @@ snd_usb_audio_t *chip; snd_pcm_t *pcm; int pcm_index; + unsigned int fmt_type; /* USB audio format type (1-3) */ snd_usb_substream_t substream[2]; struct list_head list; }; @@ -477,7 +477,7 @@ subs->transfer_sched += counts; if (subs->transfer_sched >= runtime->period_size) { subs->transfer_sched -= runtime->period_size; - if (subs->nonaudio) { + if (subs->fmt_type == USB_FORMAT_TYPE_II) { if (subs->transfer_sched > 0) { /* FIXME: fill-max mode is not supported yet */ offs -= subs->transfer_sched; @@ -894,7 +894,7 @@ u->subs = subs; u->transfer = 0; u->packets = npacks[i]; - if (subs->nonaudio) + if (subs->fmt_type == USB_FORMAT_TYPE_II) u->packets++; /* for transfer delimiter */ if (! is_playback) { /* allocate a capture buffer per urb */ @@ -1588,7 +1588,7 @@ runtime->hw.channels_min = fp->channels; if (runtime->hw.channels_max < fp->channels) runtime->hw.channels_max = fp->channels; - if (fp->nonaudio && fp->frame_size > 0) { + if (fp->fmt_type == USB_FORMAT_TYPE_II && fp->frame_size > 0) { /* FIXME: there might be more than one audio formats... */ runtime->hw.period_bytes_min = runtime->hw.period_bytes_max = fp->frame_size; @@ -1895,7 +1895,7 @@ subs->formats |= 1ULL << fp->format; subs->endpoint = fp->endpoint; subs->num_formats++; - subs->nonaudio = fp->nonaudio; + subs->fmt_type = fp->fmt_type; } @@ -1954,17 +1954,12 @@ list_for_each(p, &chip->pcm_list) { as = list_entry(p, snd_usb_stream_t, list); + if (as->fmt_type != fp->fmt_type) + continue; subs = &as->substream[stream]; if (! subs->endpoint) - break; + continue; if (subs->endpoint == fp->endpoint) { - if (fp->nonaudio) { - if (!subs->nonaudio || subs->formats != (1ULL << fp->format)) - continue; /* non-linear formats are handled exclusively */ - } else { - if (subs->nonaudio) - continue; - } list_add_tail(&fp->list, &subs->fmt_list); subs->num_formats++; subs->formats |= 1ULL << fp->format; @@ -1974,6 +1969,8 @@ /* look for an empty stream */ list_for_each(p, &chip->pcm_list) { as = list_entry(p, snd_usb_stream_t, list); + if (as->fmt_type != fp->fmt_type) + continue; subs = &as->substream[stream]; if (subs->endpoint) continue; @@ -1991,6 +1988,7 @@ memset(as, 0, sizeof(*as)); as->pcm_index = chip->pcm_devs; as->chip = chip; + as->fmt_type = fp->fmt_type; err = snd_pcm_new(chip->card, "USB Audio", chip->pcm_devs, stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0, stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1, @@ -2216,7 +2214,6 @@ break; } fp->channels = 1; - fp->nonaudio = 1; brate = combine_word(&fmt[4]); /* fmt[4,5] : wMaxBitRate (in kbps) */ framesize = combine_word(&fmt[6]); /* fmt[6,7]: wSamplesPerFrame */ snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize); @@ -2242,6 +2239,7 @@ dev->devnum, fp->iface, fp->altsetting, fmt[3]); return -1; } + fp->fmt_type = fmt[3]; if (err < 0) return err; #if 1 diff -Nru a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c --- a/sound/usb/usbmidi.c Sun Feb 29 12:58:39 2004 +++ b/sound/usb/usbmidi.c Sun Feb 29 12:58:39 2004 @@ -1,7 +1,7 @@ /* * usbmidi.c - ALSA USB MIDI driver * - * Copyright (c) 2002 Clemens Ladisch + * Copyright (c) 2002-2004 Clemens Ladisch * All rights reserved. * * Based on the OSS usb-midi driver by NAGANO Daisuke, @@ -727,18 +727,92 @@ return NULL; } +/* + * This list specifies names for ports that do not fit into the standard + * "(product) MIDI (n)" schema because they aren't external MIDI ports, + * such as internal control or synthesizer ports. + */ +static struct { + __u16 vendor; + __u16 product; + int port; + const char *name_format; +} snd_usbmidi_port_names[] = { + /* Roland UA-100 */ + {0x0582, 0x0000, 2, "%s Control"}, + /* Roland SC-8850 */ + {0x0582, 0x0003, 0, "%s Part A"}, + {0x0582, 0x0003, 1, "%s Part B"}, + {0x0582, 0x0003, 2, "%s Part C"}, + {0x0582, 0x0003, 3, "%s Part D"}, + {0x0582, 0x0003, 4, "%s MIDI 1"}, + {0x0582, 0x0003, 5, "%s MIDI 2"}, + /* Roland U-8 */ + {0x0582, 0x0004, 1, "%s Control"}, + /* Roland SC-8820 */ + {0x0582, 0x0007, 0, "%s Part A"}, + {0x0582, 0x0007, 1, "%s Part B"}, + {0x0582, 0x0007, 2, "%s MIDI"}, + /* Roland SK-500 */ + {0x0582, 0x000b, 0, "%s Part A"}, + {0x0582, 0x000b, 1, "%s Part B"}, + {0x0582, 0x000b, 2, "%s MIDI"}, + /* Roland SC-D70 */ + {0x0582, 0x000c, 0, "%s Part A"}, + {0x0582, 0x000c, 1, "%s Part B"}, + {0x0582, 0x000c, 2, "%s MIDI"}, + /* Edirol UM-880 */ + {0x0582, 0x0014, 8, "%s Control"}, + /* Edirol SD-90 */ + {0x0582, 0x0016, 0, "%s Part A"}, + {0x0582, 0x0016, 1, "%s Part B"}, + {0x0582, 0x0016, 2, "%s MIDI 1"}, + {0x0582, 0x0016, 3, "%s MIDI 2"}, + /* Edirol UM-550 */ + {0x0582, 0x0023, 5, "%s Control"}, + /* Edirol SD-20 */ + {0x0582, 0x0027, 0, "%s Part A"}, + {0x0582, 0x0027, 1, "%s Part B"}, + {0x0582, 0x0027, 2, "%s MIDI"}, + /* Edirol SD-80 */ + {0x0582, 0x0029, 0, "%s Part A"}, + {0x0582, 0x0029, 1, "%s Part B"}, + {0x0582, 0x0029, 2, "%s MIDI 1"}, + {0x0582, 0x0029, 3, "%s MIDI 2"}, + /* M-Audio MidiSport 8x8 */ + {0x0763, 0x1031, 8, "%s Control"}, + {0x0763, 0x1033, 8, "%s Control"}, +}; + static void snd_usbmidi_init_substream(snd_usb_midi_t* umidi, int stream, int number, snd_rawmidi_substream_t** rsubstream) { + int i; + __u16 vendor, product; + const char *name_format; + snd_rawmidi_substream_t* substream = snd_usbmidi_find_substream(umidi, stream, number); if (!substream) { snd_printd(KERN_ERR "substream %d:%d not found\n", stream, number); return; } + /* TODO: read port name from jack descriptor */ + name_format = "%s MIDI %d"; + vendor = umidi->chip->dev->descriptor.idVendor; + product = umidi->chip->dev->descriptor.idProduct; + for (i = 0; i < ARRAY_SIZE(snd_usbmidi_port_names); ++i) { + if (snd_usbmidi_port_names[i].vendor == vendor && + snd_usbmidi_port_names[i].product == product && + snd_usbmidi_port_names[i].port == number) { + name_format = snd_usbmidi_port_names[i].name_format; + break; + } + } snprintf(substream->name, sizeof(substream->name), - "%s Port %d", umidi->chip->card->shortname, number); + name_format, umidi->chip->card->shortname, number + 1); + *rsubstream = substream; } diff -Nru a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h --- a/sound/usb/usbquirks.h Sun Feb 29 12:58:39 2004 +++ b/sound/usb/usbquirks.h Sun Feb 29 12:58:39 2004 @@ -502,6 +502,32 @@ } }, { + /* + * This quirk is for the "Advanced Driver" mode. If off, the UA-3FX + * is standard compliant, but has only 16-bit PCM. + */ + USB_DEVICE(0x0582, 0x0050), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "EDIROL", + .product_name = "UA-3FX", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = & (const snd_usb_audio_quirk_t[]) { + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = -1 + } + } + } +}, +{ USB_DEVICE(0x0582, 0x0052), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "EDIROL", @@ -686,6 +712,16 @@ .ifnum = QUIRK_NO_INTERFACE } +}, + +{ + USB_DEVICE_VENDOR_SPEC(0x0ccd, 0x0013), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Terratec", + .product_name = "PHASE 26", + .ifnum = 3, + .type = QUIRK_MIDI_STANDARD_INTERFACE + } }, #undef USB_DEVICE_VENDOR_SPEC