Parent repository is http://linux-sound.bkbits.net/linux-sound khc@pm.waw.pl|ChangeSet|20040308002825|48427 khc diff -Nru a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt --- a/Documentation/sound/alsa/ALSA-Configuration.txt Sun Mar 7 18:45:35 2004 +++ b/Documentation/sound/alsa/ALSA-Configuration.txt Sun Mar 7 18:45:35 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 Mar 7 18:45:35 2004 +++ b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl Sun Mar 7 18:45:35 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 Mar 7 18:45:35 2004 +++ b/Documentation/sound/alsa/Joystick.txt Sun Mar 7 18:45:35 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 Mar 7 18:45:36 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/ac97_codec.h b/include/sound/ac97_codec.h --- a/include/sound/ac97_codec.h Sun Mar 7 18:45:35 2004 +++ b/include/sound/ac97_codec.h Sun Mar 7 18:45:35 2004 @@ -484,7 +484,8 @@ AC97_TUNE_HP_ONLY, /* headphone (true line-out) control as master only */ AC97_TUNE_SWAP_HP, /* swap headphone and master controls */ AC97_TUNE_SWAP_SURROUND, /* swap master and surround controls */ - AC97_TUNE_AD_SHARING /* for AD1985, turn on OMS bit and use headphone */ + AC97_TUNE_AD_SHARING, /* for AD1985, turn on OMS bit and use headphone */ + AC97_TUNE_ALC_JACK, /* for Realtek, enable JACK detection */ }; struct ac97_quirk { 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 Mar 7 18:45:36 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 Mar 7 18:45:35 2004 +++ b/include/sound/asequencer.h Sun Mar 7 18:45:35 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/asound.h b/include/sound/asound.h --- a/include/sound/asound.h Sun Mar 7 18:45:35 2004 +++ b/include/sound/asound.h Sun Mar 7 18:45:35 2004 @@ -153,7 +153,7 @@ * * *****************************************************************************/ -#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 5) +#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 6) typedef unsigned long sndrv_pcm_uframes_t; typedef long sndrv_pcm_sframes_t; diff -Nru a/include/sound/cs46xx.h b/include/sound/cs46xx.h --- a/include/sound/cs46xx.h Sun Mar 7 18:45:35 2004 +++ b/include/sound/cs46xx.h Sun Mar 7 18:45:35 2004 @@ -1646,9 +1646,7 @@ typedef struct _snd_cs46xx cs46xx_t; typedef struct _snd_cs46xx_pcm_t { - unsigned char *hw_area; - dma_addr_t hw_addr; /* PCI bus address, not accessible */ - unsigned long hw_size; + struct snd_dma_buffer hw_buf; unsigned int ctl; unsigned int shift; /* Shift count to trasform frames in bytes */ @@ -1693,9 +1691,7 @@ unsigned int mode; struct { - unsigned char *hw_area; - dma_addr_t hw_addr; /* PCI bus address, not accessible */ - unsigned long hw_size; + struct snd_dma_buffer hw_buf; unsigned int ctl; unsigned int shift; /* Shift count to trasform frames in bytes */ @@ -1726,6 +1722,8 @@ spinlock_t reg_lock; unsigned int midcr; unsigned int uartm; + + struct snd_dma_device dma_dev; int amplifier; void (*amplifier_ctrl)(cs46xx_t *, int); diff -Nru a/include/sound/emu10k1.h b/include/sound/emu10k1.h --- a/include/sound/emu10k1.h Sun Mar 7 18:45:35 2004 +++ b/include/sound/emu10k1.h Sun Mar 7 18:45:35 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; @@ -897,9 +901,7 @@ unsigned short extout_mask; /* used external outputs (bitmask) */ unsigned short pad1; unsigned int itram_size; /* internal TRAM size in samples */ - unsigned int etram_size; /* external TRAM size in samples */ - void *etram_pages; /* allocated pages for external TRAM */ - dma_addr_t etram_pages_dmaaddr; + struct snd_dma_buffer etram_pages; /* external TRAM pages and size */ unsigned int dbg; /* FX debugger register */ unsigned char name[128]; int gpr_size; /* size of allocated GPR controls */ @@ -943,11 +945,10 @@ unsigned int card_type; /* EMU10K1_CARD_* */ unsigned int ecard_ctrl; /* ecard control bits */ unsigned long dma_mask; /* PCI DMA mask */ + struct snd_dma_device dma_dev; /* DMA device description */ int max_cache_pages; /* max memory size / PAGE_SIZE */ - void *silent_page; /* silent page */ - dma_addr_t silent_page_dmaaddr; - volatile u32 *ptb_pages; /* page table pages */ - dma_addr_t ptb_pages_dmaaddr; + struct snd_dma_buffer silent_page; /* silent page */ + struct snd_dma_buffer ptb_pages; /* page table pages */ snd_util_memhdr_t *memhdr; /* page allocation list */ emu10k1_memblk_t *reserved_page; /* reserved page */ diff -Nru a/include/sound/info.h b/include/sound/info.h --- a/include/sound/info.h Sun Mar 7 18:45:35 2004 +++ b/include/sound/info.h Sun Mar 7 18:45:35 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/memalloc.h b/include/sound/memalloc.h --- a/include/sound/memalloc.h Sun Mar 7 18:45:35 2004 +++ b/include/sound/memalloc.h Sun Mar 7 18:45:35 2004 @@ -24,49 +24,33 @@ #ifndef __SOUND_MEMALLOC_H #define __SOUND_MEMALLOC_H -#include -#ifdef CONFIG_SBUS -#include -#endif +struct device; /* * buffer device info */ struct snd_dma_device { int type; /* SNDRV_MEM_TYPE_XXX */ - union { - struct pci_dev *pci; /* for PCI and PCI-SG types */ - unsigned int flags; /* GFP_XXX for continous and ISA types */ -#ifdef CONFIG_SBUS - struct sbus_dev *sbus; /* for SBUS type */ -#endif - } dev; + struct device *dev; /* generic device */ unsigned int id; /* a unique ID */ }; +#ifndef snd_dma_pci_data +#define snd_dma_pci_data(pci) (&(pci)->dev) +#define snd_dma_isa_data() NULL +#define snd_dma_sbus_data(sbus) ((struct device *)(sbus)) +#define snd_dma_continuous_data(x) ((struct device *)(unsigned long)(x)) +#endif + + /* * buffer types */ #define SNDRV_DMA_TYPE_UNKNOWN 0 /* not defined */ #define SNDRV_DMA_TYPE_CONTINUOUS 1 /* continuous no-DMA memory */ -#define SNDRV_DMA_TYPE_ISA 2 /* ISA continuous */ -#define SNDRV_DMA_TYPE_PCI 3 /* PCI continuous */ +#define SNDRV_DMA_TYPE_DEV 2 /* generic device continuous */ +#define SNDRV_DMA_TYPE_DEV_SG 3 /* generic device SG-buffer */ #define SNDRV_DMA_TYPE_SBUS 4 /* SBUS continuous */ -#define SNDRV_DMA_TYPE_PCI_SG 5 /* PCI SG-buffer */ - -#ifdef CONFIG_PCI -/* - * compose a snd_dma_device struct for the PCI device - */ -static inline void snd_dma_device_pci(struct snd_dma_device *dev, struct pci_dev *pci, unsigned int id) -{ - memset(dev, 0, sizeof(*dev)); - dev->type = SNDRV_DMA_TYPE_PCI; - dev->dev.pci = pci; - dev->id = id; -} -#endif - /* * info for buffer allocation @@ -78,67 +62,8 @@ void *private_data; /* private for allocator; don't touch */ }; -/* allocate/release a buffer */ -int snd_dma_alloc_pages(const struct snd_dma_device *dev, size_t size, struct snd_dma_buffer *dmab); -void snd_dma_free_pages(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab); - -/* buffer-preservation managements */ -size_t snd_dma_get_reserved(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab); -int snd_dma_free_reserved(const struct snd_dma_device *dev); -int snd_dma_set_reserved(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab); - - -/* - * Generic memory allocators - */ - -/* - * continuous pages - */ -void *snd_malloc_pages(size_t size, unsigned int gfp_flags); -void *snd_malloc_pages_fallback(size_t size, unsigned int gfp_flags, size_t *res_size); -void snd_free_pages(void *ptr, size_t size); - -#ifdef CONFIG_PCI -/* - * PCI continuous pages - */ -void *snd_malloc_pci_pages(struct pci_dev *pci, size_t size, dma_addr_t *dma_addr); -void *snd_malloc_pci_pages_fallback(struct pci_dev *pci, size_t size, dma_addr_t *dma_addr, size_t *res_size); -void snd_free_pci_pages(struct pci_dev *pci, size_t size, void *ptr, dma_addr_t dma_addr); -/* one page allocation */ -void *snd_malloc_pci_page(struct pci_dev *pci, dma_addr_t *dma_addr); -#define snd_free_pci_page(pci,ptr,addr) snd_free_pci_pages(pci,PAGE_SIZE,ptr,addr) -#endif - -#ifdef CONFIG_SBUS -/* - * SBUS continuous pages - */ -void *snd_malloc_sbus_pages(struct sbus_dev *sdev, size_t size, dma_addr_t *dma_addr); -void *snd_malloc_sbus_pages_fallback(struct sbus_dev *sdev, size_t size, dma_addr_t *dma_addr, size_t *res_size); -void snd_free_sbus_pages(struct sbus_dev *sdev, size_t size, void *ptr, dma_addr_t dma_addr); -#endif - -#ifdef CONFIG_ISA /* - * ISA continuous pages - */ -void *snd_malloc_isa_pages(size_t size, dma_addr_t *dma_addr); -void *snd_malloc_isa_pages_fallback(size_t size, dma_addr_t *dma_addr, size_t *res_size); -void snd_free_isa_pages(size_t size, void *ptr, dma_addr_t addr); -#ifdef CONFIG_PCI -#define snd_malloc_isa_pages(size, dma_addr) snd_malloc_pci_pages(NULL, size, dma_addr) -#define snd_malloc_isa_pages_fallback(size, dma_addr, res_size) snd_malloc_pci_pages_fallback(NULL, size, dma_addr, res_size) -#define snd_free_isa_pages(size, ptr, dma_addr) snd_free_pci_pages(NULL, size, ptr, dma_addr) -#else /* !CONFIG_PCI */ -#define snd_free_isa_pages(size, ptr, dma_addr) snd_free_pages(ptr, size) -#endif /* CONFIG_PCI */ -#endif /* CONFIG_ISA */ - -#ifdef CONFIG_PCI -/* - * Scatter-Gather PCI pages + * Scatter-Gather generic device pages */ struct snd_sg_page { void *buf; @@ -151,12 +76,9 @@ int tblsize; /* allocated table size */ struct snd_sg_page *table; /* address table */ struct page **page_table; /* page table (for vmap/vunmap) */ - struct pci_dev *pci; + struct snd_dma_device dev; }; -void *snd_malloc_sgbuf_pages(struct pci_dev *pci, size_t size, struct snd_dma_buffer *dmab); -int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab); - /* * return the pages matching with the given byte size */ @@ -172,6 +94,24 @@ { return sgbuf->table[offset >> PAGE_SHIFT].addr + offset % PAGE_SIZE; } -#endif /* CONFIG_PCI */ + + +/* allocate/release a buffer */ +int snd_dma_alloc_pages(const struct snd_dma_device *dev, size_t size, + struct snd_dma_buffer *dmab); +int snd_dma_alloc_pages_fallback(const struct snd_dma_device *dev, size_t size, + struct snd_dma_buffer *dmab); +void snd_dma_free_pages(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab); + +/* buffer-preservation managements */ +size_t snd_dma_get_reserved(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab); +int snd_dma_free_reserved(const struct snd_dma_device *dev); +int snd_dma_set_reserved(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab); + +/* basic memory allocation functions */ +void *snd_malloc_pages(size_t size, unsigned int gfp_flags); +void *snd_malloc_pages_fallback(size_t size, unsigned int gfp_flags, size_t *res_size); +void snd_free_pages(void *ptr, size_t size); #endif /* __SOUND_MEMALLOC_H */ + diff -Nru a/include/sound/pcm.h b/include/sound/pcm.h --- a/include/sound/pcm.h Sun Mar 7 18:45:35 2004 +++ b/include/sound/pcm.h Sun Mar 7 18:45:35 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 */ @@ -904,49 +907,18 @@ int snd_pcm_lib_preallocate_free(snd_pcm_substream_t *substream); int snd_pcm_lib_preallocate_free_for_all(snd_pcm_t *pcm); int snd_pcm_lib_preallocate_pages(snd_pcm_substream_t *substream, - size_t size, size_t max, - unsigned int flags); + int type, struct device *data, + size_t size, size_t max); int snd_pcm_lib_preallocate_pages_for_all(snd_pcm_t *pcm, - size_t size, size_t max, - unsigned int flags); + int type, void *data, + size_t size, size_t max); int snd_pcm_lib_malloc_pages(snd_pcm_substream_t *substream, size_t size); int snd_pcm_lib_free_pages(snd_pcm_substream_t *substream); -#ifdef CONFIG_ISA -int snd_pcm_lib_preallocate_isa_pages(snd_pcm_substream_t *substream, - size_t size, size_t max); -int snd_pcm_lib_preallocate_isa_pages_for_all(snd_pcm_t *pcm, - size_t size, size_t max); -#endif -#ifdef CONFIG_PCI -int snd_pcm_lib_preallocate_pci_pages(struct pci_dev *pci, - snd_pcm_substream_t *substream, - size_t size, size_t max); -int snd_pcm_lib_preallocate_pci_pages_for_all(struct pci_dev *pci, - snd_pcm_t *pcm, - size_t size, - size_t max); -int snd_pcm_lib_preallocate_sg_pages(struct pci_dev *pci, - snd_pcm_substream_t *substream, - size_t size, size_t max); -int snd_pcm_lib_preallocate_sg_pages_for_all(struct pci_dev *pci, - snd_pcm_t *pcm, - size_t size, size_t max); #define snd_pcm_substream_sgbuf(substream) ((substream)->runtime->dma_private) #define snd_pcm_sgbuf_pages(size) snd_sgbuf_aligned_pages(size) #define snd_pcm_sgbuf_get_addr(sgbuf,ofs) snd_sgbuf_get_addr(sgbuf,ofs) struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset); -#endif - -#ifdef CONFIG_SBUS -int snd_pcm_lib_preallocate_sbus_pages(struct sbus_dev *sdev, - snd_pcm_substream_t *substream, - size_t size, size_t max); -int snd_pcm_lib_preallocate_sbus_pages_for_all(struct sbus_dev *sdev, - snd_pcm_t *pcm, - size_t size, - size_t max); -#endif static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max) { diff -Nru a/include/sound/pcm_oss.h b/include/sound/pcm_oss.h --- a/include/sound/pcm_oss.h Sun Mar 7 18:45:35 2004 +++ b/include/sound/pcm_oss.h Sun Mar 7 18:45:35 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 Mar 7 18:45:35 2004 +++ b/include/sound/sndmagic.h Sun Mar 7 18:45:35 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/include/sound/trident.h b/include/sound/trident.h --- a/include/sound/trident.h Sun Mar 7 18:45:35 2004 +++ b/include/sound/trident.h Sun Mar 7 18:45:35 2004 @@ -308,11 +308,9 @@ unsigned int * entries; /* 16k-aligned TLB table */ dma_addr_t entries_dmaaddr; /* 16k-aligned PCI address to TLB table */ unsigned long * shadow_entries; /* shadow entries with virtual addresses */ - void * buffer; /* pointer for table calloc */ - dma_addr_t buffer_dmaaddr; /* not accessible PCI BUS physical address */ + struct snd_dma_buffer buffer; snd_util_memhdr_t * memhdr; /* page allocation list */ - void * silent_page; /* silent page */ - dma_addr_t silent_page_dmaaddr; /* not accessible PCI BUS physical address */ + struct snd_dma_buffer silent_page; } snd_trident_tlb_t; struct _snd_trident_voice { @@ -434,6 +432,8 @@ spinlock_t event_lock; spinlock_t voice_alloc; + + struct snd_dma_device dma_dev; struct pci_dev *pci; snd_card_t *card; diff -Nru a/include/sound/version.h b/include/sound/version.h --- a/include/sound/version.h Sun Mar 7 18:45:35 2004 +++ b/include/sound/version.h Sun Mar 7 18:45:35 2004 @@ -1,3 +1,3 @@ /* include/version.h. Generated by configure. */ -#define CONFIG_SND_VERSION "1.0.2c" -#define CONFIG_SND_DATE " (Thu Feb 05 15:41:49 2004 UTC)" +#define CONFIG_SND_VERSION "1.0.3" +#define CONFIG_SND_DATE " (Mon Mar 01 10:12:14 2004 UTC)" diff -Nru a/include/sound/ymfpci.h b/include/sound/ymfpci.h --- a/include/sound/ymfpci.h Sun Mar 7 18:45:35 2004 +++ b/include/sound/ymfpci.h Sun Mar 7 18:45:35 2004 @@ -316,9 +316,8 @@ struct gameport gameport; #endif - void *work_ptr; - dma_addr_t work_ptr_addr; - unsigned long work_ptr_size; + struct snd_dma_device dma_dev; + struct snd_dma_buffer work_ptr; unsigned int bank_size_playback; unsigned int bank_size_capture; @@ -333,8 +332,7 @@ dma_addr_t bank_base_capture_addr; dma_addr_t bank_base_effect_addr; dma_addr_t work_base_addr; - void *ac3_tmp_base; - dma_addr_t ac3_tmp_base_addr; + struct snd_dma_buffer ac3_tmp_base; u32 *ctrl_playback; snd_ymfpci_playback_bank_t *bank_playback[YDSXG_PLAYBACK_VOICES][2]; diff -Nru a/sound/arm/Kconfig b/sound/arm/Kconfig --- a/sound/arm/Kconfig Sun Mar 7 18:45:36 2004 +++ b/sound/arm/Kconfig Sun Mar 7 18:45:36 2004 @@ -6,6 +6,7 @@ config SND_SA11XX_UDA1341 tristate "SA11xx UDA1341TS driver (H3600)" depends on ARCH_SA1100 && SND && L3 + select SND_PCM help Say Y or M if you have a Compaq iPaq H3x00 handheld computer and want to use its Philips UDA 1341 audio chip. diff -Nru a/sound/arm/sa11xx-uda1341.c b/sound/arm/sa11xx-uda1341.c --- a/sound/arm/sa11xx-uda1341.c Sun Mar 7 18:45:35 2004 +++ b/sound/arm/sa11xx-uda1341.c Sun Mar 7 18:45:35 2004 @@ -21,7 +21,7 @@ * merged HAL layer (patches from Brian) */ -/* $Id: sa11xx-uda1341.c,v 1.12 2003/08/13 13:14:31 tiwai Exp $ */ +/* $Id: sa11xx-uda1341.c,v 1.13 2004/03/02 15:32:35 perex Exp $ */ /*************************************************************************************************** * @@ -840,7 +840,9 @@ * isa works but I'm not sure why (or if) it's the right choice * this may be too large, trying it for now */ - snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_ISA, + snd_pcm_dma_flags(0), + 64*1024, 64*1024); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_sa11xx_uda1341_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_sa11xx_uda1341_capture_ops); diff -Nru a/sound/core/Kconfig b/sound/core/Kconfig --- a/sound/core/Kconfig Sun Mar 7 18:45:35 2004 +++ b/sound/core/Kconfig Sun Mar 7 18:45:35 2004 @@ -3,9 +3,23 @@ tristate "Emulation for 32-bit applications" depends on SND && (SPARC64 || PPC64 || X86_64 && IA32_EMULATION) +config SND_TIMER + tristate + +config SND_PCM + tristate + select SND_TIMER + +config SND_HWDEP + tristate + +config SND_RAWMIDI + tristate + config SND_SEQUENCER tristate "Sequencer support" depends on SND + select SND_TIMER help Say 'Y' or 'M' to enable MIDI sequencer and router support. This feature allows routing and enqueing MIDI events. Events can be processed at given @@ -20,26 +34,27 @@ immediately. config SND_OSSEMUL - bool "OSS API emulation" - depends on SND - help - Say 'Y' to enable OSS (Open Sound System) API emulation code. + bool config SND_MIXER_OSS tristate "OSS Mixer API" - depends on SND_OSSEMUL && SND + depends on SND + select SND_OSSEMUL help Say 'Y' or 'M' to enable mixer OSS API emulation (/dev/mixer*). config SND_PCM_OSS tristate "OSS PCM (digital audio) API" - depends on SND_OSSEMUL && SND + depends on SND + select SND_OSSEMUL + select SND_PCM help Say 'Y' or 'M' to enable digital audio (PCM) OSS API emulation (/dev/dsp*). config SND_SEQUENCER_OSS bool "OSS Sequencer API" - depends on SND_OSSEMUL && SND_SEQUENCER + depends on SND_SEQUENCER + select SND_OSSEMUL help Say 'Y' to enable OSS sequencer emulation (both /dev/sequencer and /dev/music interfaces). @@ -47,6 +62,7 @@ config SND_RTCTIMER tristate "RTC Timer support" depends on SND && RTC + select SND_TIMER help Say 'Y' or 'M' to enable RTC timer support for ALSA. ALSA code uses RTC timer as precise timing source and maps the RTC timer to the ALSA's timer diff -Nru a/sound/core/Makefile b/sound/core/Makefile --- a/sound/core/Makefile Sun Mar 7 18:45:35 2004 +++ b/sound/core/Makefile Sun Mar 7 18:45:35 2004 @@ -15,97 +15,20 @@ snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ pcm_memory.o -snd-page-alloc-objs := memalloc.o -ifeq ($(CONFIG_PCI),y) -snd-page-alloc-objs += sgbuf.o -endif +snd-page-alloc-objs := memalloc.o sgbuf.o snd-rawmidi-objs := rawmidi.o snd-timer-objs := timer.o snd-rtctimer-objs := rtctimer.o snd-hwdep-objs := hwdep.o -obj-$(CONFIG_SND) += snd.o -ifeq ($(subst m,y,$(CONFIG_RTC)),y) - obj-$(CONFIG_SND_RTCTIMER) += snd-timer.o - obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o -endif -obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o +obj-$(CONFIG_SND) += snd.o +obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o +obj-$(CONFIG_SND_TIMER) += snd-timer.o +obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o +obj-$(CONFIG_SND_PCM) += snd-pcm.o snd-page-alloc.o +obj-$(CONFIG_SND_RAWMIDI) += snd-rawmidi.o -obj-$(CONFIG_SND_MIXER_OSS) += oss/ -obj-$(CONFIG_SND_PCM_OSS) += snd-pcm.o snd-timer.o snd-page-alloc.o oss/ -obj-$(CONFIG_SND_SEQUENCER) += snd-timer.o seq/ +obj-$(CONFIG_SND_OSSEMUL) += oss/ +obj-$(CONFIG_SND_SEQUENCER) += seq/ obj-$(CONFIG_SND_BIT32_EMUL) += ioctl32/ - -# Toplevel Module Dependency -obj-$(CONFIG_SND_DUMMY) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_VIRMIDI) += snd-rawmidi.o snd.o snd-timer.o -obj-$(CONFIG_SND_SERIAL_U16550) += snd-rawmidi.o snd.o snd-timer.o -obj-$(CONFIG_SND_MTPAV) += snd-rawmidi.o snd.o snd-timer.o -obj-$(CONFIG_SND_MPU401) += snd-rawmidi.o snd.o snd-timer.o -obj-$(CONFIG_SND_ALS100) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_AZT2320) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_AZT3328) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_CMI8330) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_DT019X) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_ES18XX) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_OPL3SA2) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_SGALAXY) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_AD1816A) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_AD1848) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_CS4231) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_CS4232) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_CS4236) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_ES1688) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_GUSCLASSIC) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_GUSMAX) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_GUSEXTREME) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_INTERWAVE) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_INTERWAVE_STB) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_OPTI93X) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_SB8) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_SB16) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_SBAWE) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_ES968) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_WAVEFRONT) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_SSCAPE) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_ALS4000) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_CMIPCI) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_CS4281) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_ENS1370) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_ENS1371) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_ES1938) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-hwdep.o snd-rawmidi.o -obj-$(CONFIG_SND_ES1968) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_FM801) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_ICE1712) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_ICE1724) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_INTEL8X0) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_MAESTRO3) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_RME32) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_RME96) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_SONICVIBES) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_VIA82XX) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_ALI5451) += snd.o snd-rawmidi.o snd-timer.o snd-page-alloc.o snd-pcm.o -obj-$(CONFIG_SND_CS46XX) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_EMU10K1) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_KORG1212) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_NM256) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_RME9652) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_HDSP) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_TRIDENT) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_YMFPCI) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_POWERMAC) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_SA11XX_UDA1341) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_PC98_CS4232) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o snd-hwdep.o -obj-$(CONFIG_SND_USB_AUDIO) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o -obj-$(CONFIG_SND_SUN_AMD7930) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_SUN_CS4231) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_HARMONY) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o -obj-$(CONFIG_SND_VXPOCKET) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-hwdep.o -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-m := $(sort $(obj-m)) diff -Nru a/sound/core/init.c b/sound/core/init.c --- a/sound/core/init.c Sun Mar 7 18:45:35 2004 +++ b/sound/core/init.c Sun Mar 7 18:45:35 2004 @@ -79,6 +79,7 @@ goto __error; strlcpy(card->id, xid, sizeof(card->id)); } + err = 0; write_lock(&snd_card_rwlock); if (idx < 0) { int idx2; @@ -94,15 +95,14 @@ idx = snd_ecards_limit++; } else if (idx < snd_ecards_limit) { if (snd_cards_lock & (1 << idx)) - idx = -1; /* invalid */ + err = -ENODEV; /* invalid */ } else if (idx < SNDRV_CARDS) snd_ecards_limit = idx + 1; /* increase the limit */ else - idx = -1; - if (idx < 0) { + err = -ENODEV; + if (idx < 0 || err < 0) { write_unlock(&snd_card_rwlock); - if (idx >= snd_ecards_limit) - snd_printk(KERN_ERR "card %i is out of range (0-%i)\n", idx, snd_ecards_limit-1); + snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i)\n", idx, snd_ecards_limit - 1); goto __error; } snd_cards_lock |= 1 << idx; /* lock it */ diff -Nru a/sound/core/memalloc.c b/sound/core/memalloc.c --- a/sound/core/memalloc.c Sun Mar 7 18:45:36 2004 +++ b/sound/core/memalloc.c Sun Mar 7 18:45:36 2004 @@ -25,10 +25,15 @@ #include #include #include +#include #include #include +#include #include #include +#ifdef CONFIG_SBUS +#include +#endif MODULE_AUTHOR("Takashi Iwai , Jaroslav Kysela "); @@ -43,6 +48,13 @@ MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i"); MODULE_PARM_DESC(enable, "Enable cards to allocate buffers."); +/* + */ + +void *snd_malloc_sgbuf_pages(const struct snd_dma_device *dev, + size_t size, struct snd_dma_buffer *dmab, + size_t *res_size); +int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab); /* */ @@ -73,14 +85,40 @@ #define snd_assert(expr, args...) /**/ #endif -#ifdef CONFIG_PCI +/* + * Hacks + */ + +static void *snd_dma_alloc_coherent1(struct device *dev, size_t size, + dma_addr_t *dma_handle, int flags) +{ + if (dev) + return dma_alloc_coherent(dev, size, dma_handle, flags); + else /* FIXME: dma_alloc_coherent does't always accept dev=NULL */ + return pci_alloc_consistent(NULL, size, dma_handle); +} + +static void snd_dma_free_coherent1(struct device *dev, size_t size, void *dma_addr, + dma_addr_t dma_handle) +{ + if (dev) + return dma_free_coherent(dev, size, dma_addr, dma_handle); + else + return pci_free_consistent(NULL, size, dma_addr, dma_handle); +} + +#undef dma_alloc_coherent +#define dma_alloc_coherent snd_dma_alloc_coherent1 +#undef dma_free_coherent +#define dma_free_coherent snd_dma_free_coherent1 + + #if defined(__i386__) || defined(__ppc__) || defined(__x86_64__) -#define HACK_PCI_ALLOC_CONSISTENT /* - * A hack to allocate large buffers via pci_alloc_consistent() + * A hack to allocate large buffers via dma_alloc_coherent() * - * since pci_alloc_consistent always tries GFP_DMA when the requested + * since dma_alloc_coherent always tries GFP_DMA when the requested * pci memory region is below 32bit, it happens quite often that even * 2 order of pages cannot be allocated. * @@ -88,46 +126,248 @@ * allocation will be done without GFP_DMA. if the area doesn't match * with the requested region, then realloate with the original dma_mask * again. + * + * Really, we want to move this type of thing into dma_alloc_coherent() + * so dma_mask doesn't have to be messed with. */ -static void *snd_pci_hack_alloc_consistent(struct pci_dev *hwdev, size_t size, - dma_addr_t *dma_handle) +static void *snd_dma_hack_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, int flags) { void *ret; - u64 dma_mask, cdma_mask; - unsigned long mask; + u64 dma_mask; - if (hwdev == NULL) - return pci_alloc_consistent(hwdev, size, dma_handle); - dma_mask = hwdev->dma_mask; - cdma_mask = hwdev->consistent_dma_mask; - mask = (unsigned long)dma_mask && (unsigned long)cdma_mask; - hwdev->dma_mask = 0xffffffff; /* do without masking */ - hwdev->consistent_dma_mask = 0xffffffff; /* do without masking */ - ret = pci_alloc_consistent(hwdev, size, dma_handle); - hwdev->dma_mask = dma_mask; /* restore */ - hwdev->consistent_dma_mask = cdma_mask; /* restore */ + if (dev == NULL || !dev->dma_mask) + return dma_alloc_coherent(dev, size, dma_handle, flags); + dma_mask = *dev->dma_mask; + *dev->dma_mask = 0xffffffff; /* do without masking */ + ret = dma_alloc_coherent(dev, size, dma_handle, flags); + *dev->dma_mask = dma_mask; /* restore */ if (ret) { /* obtained address is out of range? */ - if (((unsigned long)*dma_handle + size - 1) & ~mask) { + if (((unsigned long)*dma_handle + size - 1) & ~dma_mask) { /* reallocate with the proper mask */ - pci_free_consistent(hwdev, size, ret, *dma_handle); - ret = pci_alloc_consistent(hwdev, size, dma_handle); + dma_free_coherent(dev, size, ret, *dma_handle); + ret = dma_alloc_coherent(dev, size, dma_handle, flags); } } else { /* wish to success now with the proper mask... */ - if (mask != 0xffffffffUL) - ret = pci_alloc_consistent(hwdev, size, dma_handle); + if (dma_mask != 0xffffffffUL) + ret = dma_alloc_coherent(dev, size, dma_handle, flags); } return ret; } -/* redefine pci_alloc_consistent for some architectures */ -#undef pci_alloc_consistent -#define pci_alloc_consistent snd_pci_hack_alloc_consistent +/* redefine dma_alloc_coherent for some architectures */ +#undef dma_alloc_coherent +#define dma_alloc_coherent snd_dma_hack_alloc_coherent #endif /* arch */ -#endif /* CONFIG_PCI */ + +/* + * + * Generic memory allocators + * + */ + +static long snd_allocated_pages; /* holding the number of allocated pages */ + +static void mark_pages(void *res, int order) +{ + struct page *page = virt_to_page(res); + struct page *last_page = page + (1 << order); + while (page < last_page) + SetPageReserved(page++); + snd_allocated_pages += 1 << order; +} + +static void unmark_pages(void *res, int order) +{ + struct page *page = virt_to_page(res); + struct page *last_page = page + (1 << order); + while (page < last_page) + ClearPageReserved(page++); + snd_allocated_pages -= 1 << order; +} + +/** + * snd_malloc_pages - allocate pages with the given size + * @size: the size to allocate in bytes + * @gfp_flags: the allocation conditions, GFP_XXX + * + * Allocates the physically contiguous pages with the given size. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ +void *snd_malloc_pages(size_t size, unsigned int gfp_flags) +{ + int pg; + void *res; + + snd_assert(size > 0, return NULL); + snd_assert(gfp_flags != 0, return NULL); + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + if ((res = (void *) __get_free_pages(gfp_flags, pg)) != NULL) { + mark_pages(res, pg); + } + return res; +} + +/** + * snd_malloc_pages_fallback - allocate pages with the given size with fallback + * @size: the requested size to allocate in bytes + * @gfp_flags: the allocation conditions, GFP_XXX + * @res_size: the pointer to store the size of buffer actually allocated + * + * Allocates the physically contiguous pages with the given request + * size. When no space is left, this function reduces the size and + * tries to allocate again. The size actually allocated is stored in + * res_size argument. + * + * Returns the pointer of the buffer, or NULL if no enoguh memory. + */ +void *snd_malloc_pages_fallback(size_t size, unsigned int gfp_flags, size_t *res_size) +{ + void *res; + + snd_assert(size > 0, return NULL); + snd_assert(res_size != NULL, return NULL); + do { + if ((res = snd_malloc_pages(size, gfp_flags)) != NULL) { + *res_size = size; + return res; + } + size >>= 1; + } while (size >= PAGE_SIZE); + return NULL; +} + +/** + * snd_free_pages - release the pages + * @ptr: the buffer pointer to release + * @size: the allocated buffer size + * + * Releases the buffer allocated via snd_malloc_pages(). + */ +void snd_free_pages(void *ptr, size_t size) +{ + int pg; + + if (ptr == NULL) + return; + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + unmark_pages(ptr, pg); + free_pages((unsigned long) ptr, pg); +} + +/* + * + * Bus-specific memory allocators + * + */ + +static void *snd_malloc_dev_pages(struct device *dev, size_t size, dma_addr_t *dma) +{ + int pg; + void *res; + unsigned int gfp_flags; + + snd_assert(size > 0, return NULL); + snd_assert(dma != NULL, return NULL); + pg = get_order(size); + gfp_flags = GFP_KERNEL; + if (pg > 0) + gfp_flags |= __GFP_NOWARN; + res = dma_alloc_coherent(dev, PAGE_SIZE << pg, dma, gfp_flags); + if (res != NULL) + mark_pages(res, pg); + + return res; +} + +static void *snd_malloc_dev_pages_fallback(struct device *dev, size_t size, + dma_addr_t *dma, size_t *res_size) +{ + void *res; + + snd_assert(res_size != NULL, return NULL); + do { + if ((res = snd_malloc_dev_pages(dev, size, dma)) != NULL) { + *res_size = size; + return res; + } + size >>= 1; + } while (size >= PAGE_SIZE); + return NULL; +} + +static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr, + dma_addr_t dma) +{ + int pg; + + if (ptr == NULL) + return; + pg = get_order(size); + unmark_pages(ptr, pg); + dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma); +} + +#ifdef CONFIG_SBUS + +static void *snd_malloc_sbus_pages(struct device *dev, size_t size, + dma_addr_t *dma_addr) +{ + struct sbus_dev *sdev = (struct sbus_dev *)dev; + int pg; + void *res; + + snd_assert(size > 0, return NULL); + snd_assert(dma_addr != NULL, return NULL); + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + res = sbus_alloc_consistent(sdev, PAGE_SIZE * (1 << pg), dma_addr); + if (res != NULL) { + mark_pages(res, pg); + } + return res; +} + +static void *snd_malloc_sbus_pages_fallback(struct device *dev, size_t size, + dma_addr_t *dma_addr, size_t *res_size) +{ + void *res; + + snd_assert(res_size != NULL, return NULL); + do { + if ((res = snd_malloc_sbus_pages(dev, size, dma_addr)) != NULL) { + *res_size = size; + return res; + } + size >>= 1; + } while (size >= PAGE_SIZE); + return NULL; +} + +static void snd_free_sbus_pages(struct device *dev, size_t size, + void *ptr, dma_addr_t dma_addr) +{ + struct sbus_dev *sdev = (struct sbus_dev *)dev; + int pg; + + if (ptr == NULL) + return; + for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + unmark_pages(ptr, pg); + sbus_free_consistent(sdev, PAGE_SIZE * (1 << pg), ptr, dma_addr); +} + +#endif /* CONFIG_SBUS */ + +/* + * + * ALSA generic memory management + * + */ /* @@ -142,23 +382,7 @@ if (! allow_unused || (a->id != SNDRV_DMA_DEVICE_UNUSED && b->id != SNDRV_DMA_DEVICE_UNUSED)) return 0; } - switch (a->type) { - case SNDRV_DMA_TYPE_CONTINUOUS: -#ifdef CONFIG_ISA - case SNDRV_DMA_TYPE_ISA: -#endif - return a->dev.flags == b->dev.flags; -#ifdef CONFIG_PCI - case SNDRV_DMA_TYPE_PCI: - case SNDRV_DMA_TYPE_PCI_SG: - return a->dev.pci == b->dev.pci; -#endif -#ifdef CONFIG_SBUS - case SNDRV_DMA_TYPE_SBUS: - return a->dev.sbus == b->dev.sbus; -#endif - } - return 0; + return a->dev == b->dev; } /** @@ -183,27 +407,70 @@ dmab->bytes = 0; switch (dev->type) { case SNDRV_DMA_TYPE_CONTINUOUS: - dmab->area = snd_malloc_pages(size, dev->dev.flags); + dmab->area = snd_malloc_pages(size, (unsigned long)dev->dev); dmab->addr = 0; break; -#ifdef CONFIG_ISA - case SNDRV_DMA_TYPE_ISA: - dmab->area = snd_malloc_isa_pages(size, &dmab->addr); +#ifdef CONFIG_SBUS + case SNDRV_DMA_TYPE_SBUS: + dmab->area = snd_malloc_sbus_pages(dev->dev, size, &dmab->addr); break; #endif -#ifdef CONFIG_PCI - case SNDRV_DMA_TYPE_PCI: - dmab->area = snd_malloc_pci_pages(dev->dev.pci, size, &dmab->addr); + case SNDRV_DMA_TYPE_DEV: + dmab->area = snd_malloc_dev_pages(dev->dev, size, &dmab->addr); break; - case SNDRV_DMA_TYPE_PCI_SG: - snd_malloc_sgbuf_pages(dev->dev.pci, size, dmab); + case SNDRV_DMA_TYPE_DEV_SG: + snd_malloc_sgbuf_pages(dev, size, dmab, NULL); + break; + default: + printk(KERN_ERR "snd-malloc: invalid device type %d\n", dev->type); + dmab->area = NULL; + dmab->addr = 0; + return -ENXIO; + } + if (! dmab->area) + return -ENOMEM; + dmab->bytes = size; + return 0; +} + +/** + * snd_dma_alloc_pages_fallback - allocate the buffer area according to the given type with fallback + * @dev: the buffer device info + * @size: the buffer size to allocate + * @dmab: buffer allocation record to store the allocated data + * + * Calls the memory-allocator function for the corresponding + * buffer type. When no space is left, this function reduces the size and + * tries to allocate again. The size actually allocated is stored in + * res_size argument. + * + * Returns zero if the buffer with the given size is allocated successfuly, + * other a negative value at error. + */ +int snd_dma_alloc_pages_fallback(const struct snd_dma_device *dev, size_t size, + struct snd_dma_buffer *dmab) +{ + snd_assert(dev != NULL, return -ENXIO); + snd_assert(size > 0, return -ENXIO); + snd_assert(dmab != NULL, return -ENXIO); + + dmab->bytes = 0; + switch (dev->type) { + case SNDRV_DMA_TYPE_CONTINUOUS: + dmab->area = snd_malloc_pages_fallback(size, (unsigned long)dev->dev, &dmab->bytes); + dmab->addr = 0; break; -#endif #ifdef CONFIG_SBUS case SNDRV_DMA_TYPE_SBUS: - dmab->area = snd_malloc_sbus_pages(dev->dev.sbus, size, &dmab->addr); + dmab->area = snd_malloc_sbus_pages_fallback(dev->dev, size, &dmab->addr, &dmab->bytes); break; #endif + case SNDRV_DMA_TYPE_DEV: + dmab->area = snd_malloc_dev_pages_fallback(dev->dev, size, &dmab->addr, &dmab->bytes); + break; + case SNDRV_DMA_TYPE_DEV_SG: + snd_malloc_sgbuf_pages(dev, size, dmab, &dmab->bytes); + break; default: printk(KERN_ERR "snd-malloc: invalid device type %d\n", dev->type); dmab->area = NULL; @@ -212,7 +479,6 @@ } if (! dmab->area) return -ENOMEM; - dmab->bytes = size; return 0; } @@ -230,24 +496,17 @@ case SNDRV_DMA_TYPE_CONTINUOUS: snd_free_pages(dmab->area, dmab->bytes); break; -#ifdef CONFIG_ISA - case SNDRV_DMA_TYPE_ISA: - snd_free_isa_pages(dmab->bytes, dmab->area, dmab->addr); +#ifdef CONFIG_SBUS + case SNDRV_DMA_TYPE_SBUS: + snd_free_sbus_pages(dev->dev, dmab->bytes, dmab->area, dmab->addr); break; #endif -#ifdef CONFIG_PCI - case SNDRV_DMA_TYPE_PCI: - snd_free_pci_pages(dev->dev.pci, dmab->bytes, dmab->area, dmab->addr); + case SNDRV_DMA_TYPE_DEV: + snd_free_dev_pages(dev->dev, dmab->bytes, dmab->area, dmab->addr); break; - case SNDRV_DMA_TYPE_PCI_SG: + case SNDRV_DMA_TYPE_DEV_SG: snd_free_sgbuf_pages(dmab); break; -#endif -#ifdef CONFIG_SBUS - case SNDRV_DMA_TYPE_SBUS: - snd_free_sbus_pages(dev->dev.sbus, dmab->bytes, dmab->area, dmab->addr); - break; -#endif default: printk(KERN_ERR "snd-malloc: invalid device type %d\n", dev->type); } @@ -396,394 +655,6 @@ } -/* - * - * Generic memory allocators - * - */ - -static long snd_allocated_pages; /* holding the number of allocated pages */ - -static void mark_pages(void *res, int order) -{ - struct page *page = virt_to_page(res); - struct page *last_page = page + (1 << order); - while (page < last_page) - SetPageReserved(page++); - snd_allocated_pages += 1 << order; -} - -static void unmark_pages(void *res, int order) -{ - struct page *page = virt_to_page(res); - struct page *last_page = page + (1 << order); - while (page < last_page) - ClearPageReserved(page++); - snd_allocated_pages -= 1 << order; -} - -/** - * snd_malloc_pages - allocate pages with the given size - * @size: the size to allocate in bytes - * @gfp_flags: the allocation conditions, GFP_XXX - * - * Allocates the physically contiguous pages with the given size. - * - * Returns the pointer of the buffer, or NULL if no enoguh memory. - */ -void *snd_malloc_pages(size_t size, unsigned int gfp_flags) -{ - int pg; - void *res; - - snd_assert(size > 0, return NULL); - snd_assert(gfp_flags != 0, return NULL); - for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); - if ((res = (void *) __get_free_pages(gfp_flags, pg)) != NULL) { - mark_pages(res, pg); - } - return res; -} - -/** - * snd_malloc_pages_fallback - allocate pages with the given size with fallback - * @size: the requested size to allocate in bytes - * @gfp_flags: the allocation conditions, GFP_XXX - * @res_size: the pointer to store the size of buffer actually allocated - * - * Allocates the physically contiguous pages with the given request - * size. When no space is left, this function reduces the size and - * tries to allocate again. The size actually allocated is stored in - * res_size argument. - * - * Returns the pointer of the buffer, or NULL if no enoguh memory. - */ -void *snd_malloc_pages_fallback(size_t size, unsigned int gfp_flags, size_t *res_size) -{ - void *res; - - snd_assert(size > 0, return NULL); - snd_assert(res_size != NULL, return NULL); - do { - if ((res = snd_malloc_pages(size, gfp_flags)) != NULL) { - *res_size = size; - return res; - } - size >>= 1; - } while (size >= PAGE_SIZE); - return NULL; -} - -/** - * snd_free_pages - release the pages - * @ptr: the buffer pointer to release - * @size: the allocated buffer size - * - * Releases the buffer allocated via snd_malloc_pages(). - */ -void snd_free_pages(void *ptr, size_t size) -{ - int pg; - - if (ptr == NULL) - return; - for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); - unmark_pages(ptr, pg); - free_pages((unsigned long) ptr, pg); -} - -#if defined(CONFIG_ISA) && ! defined(CONFIG_PCI) - -/** - * snd_malloc_isa_pages - allocate pages for ISA bus with the given size - * @size: the size to allocate in bytes - * @dma_addr: the pointer to store the physical address of the buffer - * - * Allocates the physically contiguous pages with the given size for - * ISA bus. - * - * Returns the pointer of the buffer, or NULL if no enoguh memory. - */ -void *snd_malloc_isa_pages(size_t size, dma_addr_t *dma_addr) -{ - void *dma_area; - dma_area = snd_malloc_pages(size, GFP_ATOMIC|GFP_DMA); - *dma_addr = dma_area ? isa_virt_to_bus(dma_area) : 0UL; - return dma_area; -} - -/** - * snd_malloc_isa_pages_fallback - allocate pages with the given size with fallback for ISA bus - * @size: the requested size to allocate in bytes - * @dma_addr: the pointer to store the physical address of the buffer - * @res_size: the pointer to store the size of buffer actually allocated - * - * Allocates the physically contiguous pages with the given request - * size for PCI bus. When no space is left, this function reduces the size and - * tries to allocate again. The size actually allocated is stored in - * res_size argument. - * - * Returns the pointer of the buffer, or NULL if no enoguh memory. - */ -void *snd_malloc_isa_pages_fallback(size_t size, - dma_addr_t *dma_addr, - size_t *res_size) -{ - void *dma_area; - dma_area = snd_malloc_pages_fallback(size, GFP_ATOMIC|GFP_DMA, res_size); - *dma_addr = dma_area ? isa_virt_to_bus(dma_area) : 0UL; - return dma_area; -} - -#endif /* CONFIG_ISA && !CONFIG_PCI */ - -#ifdef CONFIG_PCI - -/** - * snd_malloc_pci_pages - allocate pages for PCI bus with the given size - * @pci: the pci device pointer - * @size: the size to allocate in bytes - * @dma_addr: the pointer to store the physical address of the buffer - * - * Allocates the physically contiguous pages with the given size for - * PCI bus. - * - * Returns the pointer of the buffer, or NULL if no enoguh memory. - */ -void *snd_malloc_pci_pages(struct pci_dev *pci, - size_t size, - dma_addr_t *dma_addr) -{ - int pg; - void *res; - - snd_assert(size > 0, return NULL); - snd_assert(dma_addr != NULL, return NULL); - for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); - res = pci_alloc_consistent(pci, PAGE_SIZE * (1 << pg), dma_addr); - if (res != NULL) { - mark_pages(res, pg); - } - return res; -} - -/** - * snd_malloc_pci_pages_fallback - allocate pages with the given size with fallback for PCI bus - * @pci: pci device pointer - * @size: the requested size to allocate in bytes - * @dma_addr: the pointer to store the physical address of the buffer - * @res_size: the pointer to store the size of buffer actually allocated - * - * Allocates the physically contiguous pages with the given request - * size for PCI bus. When no space is left, this function reduces the size and - * tries to allocate again. The size actually allocated is stored in - * res_size argument. - * - * Returns the pointer of the buffer, or NULL if no enoguh memory. - */ -void *snd_malloc_pci_pages_fallback(struct pci_dev *pci, - size_t size, - dma_addr_t *dma_addr, - size_t *res_size) -{ - void *res; - - snd_assert(res_size != NULL, return NULL); - do { - if ((res = snd_malloc_pci_pages(pci, size, dma_addr)) != NULL) { - *res_size = size; - return res; - } - size >>= 1; - } while (size >= PAGE_SIZE); - return NULL; -} - -/** - * snd_free_pci_pages - release the pages - * @pci: pci device pointer - * @size: the allocated buffer size - * @ptr: the buffer pointer to release - * @dma_addr: the physical address of the buffer - * - * Releases the buffer allocated via snd_malloc_pci_pages(). - */ -void snd_free_pci_pages(struct pci_dev *pci, - size_t size, - void *ptr, - dma_addr_t dma_addr) -{ - int pg; - - if (ptr == NULL) - return; - for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); - unmark_pages(ptr, pg); - pci_free_consistent(pci, PAGE_SIZE * (1 << pg), ptr, dma_addr); -} - - -#if defined(__i386__) -/* - * on ix86, we allocate a page with GFP_KERNEL to assure the - * allocation. the code is almost same with kernel/i386/pci-dma.c but - * it allocates only a single page and checks the validity of the - * page address with the given pci dma mask. - */ - -/** - * snd_malloc_pci_page - allocate a page in the valid pci dma mask - * @pci: pci device pointer - * @addrp: the pointer to store the physical address of the buffer - * - * Allocates a single page for the given PCI device and returns - * the virtual address and stores the physical address on addrp. - * - * This function cannot be called from interrupt handlers or - * within spinlocks. - */ -void *snd_malloc_pci_page(struct pci_dev *pci, dma_addr_t *addrp) -{ - void *ptr; - dma_addr_t addr; - unsigned long mask; - - mask = pci ? (unsigned long)pci->consistent_dma_mask : 0x00ffffffUL; - ptr = (void *)__get_free_page(GFP_KERNEL); - if (ptr) { - addr = virt_to_phys(ptr); - if (((unsigned long)addr + PAGE_SIZE - 1) & ~mask) { - /* try to reallocate with the GFP_DMA */ - free_page((unsigned long)ptr); - /* use GFP_ATOMIC for the DMA zone to avoid stall */ - ptr = (void *)__get_free_page(GFP_ATOMIC | GFP_DMA); - if (ptr) /* ok, the address must be within lower 16MB... */ - addr = virt_to_phys(ptr); - else - addr = 0; - } - } else - addr = 0; - if (ptr) { - memset(ptr, 0, PAGE_SIZE); - mark_pages(ptr, 0); - } - *addrp = addr; - return ptr; -} -#else - -/* on other architectures, call snd_malloc_pci_pages() helper function - * which uses pci_alloc_consistent(). - */ -void *snd_malloc_pci_page(struct pci_dev *pci, dma_addr_t *addrp) -{ - return snd_malloc_pci_pages(pci, PAGE_SIZE, addrp); -} - -#endif - -#if 0 /* for kernel-doc */ -/** - * snd_free_pci_page - release a page - * @pci: pci device pointer - * @ptr: the buffer pointer to release - * @dma_addr: the physical address of the buffer - * - * Releases the buffer allocated via snd_malloc_pci_page(). - */ -void snd_free_pci_page(struct pci_dev *pci, void *ptr, dma_addr_t dma_addr); -#endif /* for kernel-doc */ - -#endif /* CONFIG_PCI */ - -#ifdef CONFIG_SBUS - -/** - * snd_malloc_sbus_pages - allocate pages for SBUS with the given size - * @sdev: sbus device pointer - * @size: the size to allocate in bytes - * @dma_addr: the pointer to store the physical address of the buffer - * - * Allocates the physically contiguous pages with the given size for - * SBUS. - * - * Returns the pointer of the buffer, or NULL if no enoguh memory. - */ -void *snd_malloc_sbus_pages(struct sbus_dev *sdev, - size_t size, - dma_addr_t *dma_addr) -{ - int pg; - void *res; - - snd_assert(size > 0, return NULL); - snd_assert(dma_addr != NULL, return NULL); - for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); - res = sbus_alloc_consistent(sdev, PAGE_SIZE * (1 << pg), dma_addr); - if (res != NULL) { - mark_pages(res, pg); - } - return res; -} - -/** - * snd_malloc_pci_pages_fallback - allocate pages with the given size with fallback for SBUS - * @sdev: sbus device pointer - * @size: the requested size to allocate in bytes - * @dma_addr: the pointer to store the physical address of the buffer - * @res_size: the pointer to store the size of buffer actually allocated - * - * Allocates the physically contiguous pages with the given request - * size for SBUS. When no space is left, this function reduces the size and - * tries to allocate again. The size actually allocated is stored in - * res_size argument. - * - * Returns the pointer of the buffer, or NULL if no enoguh memory. - */ -void *snd_malloc_sbus_pages_fallback(struct sbus_dev *sdev, - size_t size, - dma_addr_t *dma_addr, - size_t *res_size) -{ - void *res; - - snd_assert(res_size != NULL, return NULL); - do { - if ((res = snd_malloc_sbus_pages(sdev, size, dma_addr)) != NULL) { - *res_size = size; - return res; - } - size >>= 1; - } while (size >= PAGE_SIZE); - return NULL; -} - -/** - * snd_free_sbus_pages - release the pages - * @sdev: sbus device pointer - * @size: the allocated buffer size - * @ptr: the buffer pointer to release - * @dma_addr: the physical address of the buffer - * - * Releases the buffer allocated via snd_malloc_pci_pages(). - */ -void snd_free_sbus_pages(struct sbus_dev *sdev, - size_t size, - void *ptr, - dma_addr_t dma_addr) -{ - int pg; - - if (ptr == NULL) - return; - for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); - unmark_pages(ptr, pg); - sbus_free_consistent(sdev, PAGE_SIZE * (1 << pg), ptr, dma_addr); -} - -#endif /* CONFIG_SBUS */ - /* * allocation of buffers for pre-defined devices @@ -821,6 +692,17 @@ { }, /* terminator */ }; +/* + * compose a snd_dma_device struct for the PCI device + */ +static inline void snd_dma_device_pci(struct snd_dma_device *dev, struct pci_dev *pci, unsigned int id) +{ + memset(dev, 0, sizeof(*dev)); + dev->type = SNDRV_DMA_TYPE_DEV; + dev->dev = snd_dma_pci_data(pci); + dev->id = id; +} + static void __init preallocate_cards(void) { struct pci_dev *pci = NULL; @@ -902,29 +784,25 @@ len += sprintf(page + len, " : type "); switch (mem->dev.type) { case SNDRV_DMA_TYPE_CONTINUOUS: - len += sprintf(page + len, "CONT [%x]", mem->dev.dev.flags); - break; -#ifdef CONFIG_PCI - case SNDRV_DMA_TYPE_PCI: - case SNDRV_DMA_TYPE_PCI_SG: - if (mem->dev.dev.pci) { - len += sprintf(page + len, "%s [%04x:%04x]", - mem->dev.type == SNDRV_DMA_TYPE_PCI ? "PCI" : "PCI-SG", - mem->dev.dev.pci->vendor, - mem->dev.dev.pci->device); - } - break; -#endif -#ifdef CONFIG_ISA - case SNDRV_DMA_TYPE_ISA: - len += sprintf(page + len, "ISA [%x]", mem->dev.dev.flags); + len += sprintf(page + len, "CONT [%p]", mem->dev.dev); break; -#endif #ifdef CONFIG_SBUS case SNDRV_DMA_TYPE_SBUS: - len += sprintf(page + len, "SBUS [%x]", mem->dev.dev.sbus->slot); + { + struct sbus_dev *sdev = (struct sbus_dev *)(mem->dev.dev); + len += sprintf(page + len, "SBUS [%x]", sbus->slot); + } break; #endif + case SNDRV_DMA_TYPE_DEV: + case SNDRV_DMA_TYPE_DEV_SG: + if (mem->dev.dev) { + len += sprintf(page + len, "%s [%s]", + mem->dev.type == SNDRV_DMA_TYPE_DEV_SG ? "DEV-SG" : "DEV", + mem->dev.dev->bus_id); + } else + len += sprintf(page + len, "ISA"); + break; default: len += sprintf(page + len, "UNKNOWN"); break; @@ -987,7 +865,9 @@ * exports */ EXPORT_SYMBOL(snd_dma_alloc_pages); +EXPORT_SYMBOL(snd_dma_alloc_pages_fallback); EXPORT_SYMBOL(snd_dma_free_pages); + EXPORT_SYMBOL(snd_dma_get_reserved); EXPORT_SYMBOL(snd_dma_free_reserved); EXPORT_SYMBOL(snd_dma_set_reserved); @@ -995,20 +875,3 @@ EXPORT_SYMBOL(snd_malloc_pages); EXPORT_SYMBOL(snd_malloc_pages_fallback); EXPORT_SYMBOL(snd_free_pages); -#if defined(CONFIG_ISA) && ! defined(CONFIG_PCI) -EXPORT_SYMBOL(snd_malloc_isa_pages); -EXPORT_SYMBOL(snd_malloc_isa_pages_fallback); -#endif -#ifdef CONFIG_PCI -EXPORT_SYMBOL(snd_malloc_pci_pages); -EXPORT_SYMBOL(snd_malloc_pci_pages_fallback); -EXPORT_SYMBOL(snd_malloc_pci_page); -EXPORT_SYMBOL(snd_free_pci_pages); -EXPORT_SYMBOL(snd_malloc_sgbuf_pages); -EXPORT_SYMBOL(snd_free_sgbuf_pages); -#endif -#ifdef CONFIG_SBUS -EXPORT_SYMBOL(snd_malloc_sbus_pages); -EXPORT_SYMBOL(snd_malloc_sbus_pages_fallback); -EXPORT_SYMBOL(snd_free_sbus_pages); -#endif diff -Nru a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c --- a/sound/core/oss/pcm_oss.c Sun Mar 7 18:45:36 2004 +++ b/sound/core/oss/pcm_oss.c Sun Mar 7 18:45:36 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 Mar 7 18:45:36 2004 +++ b/sound/core/pcm.c Sun Mar 7 18:45:36 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_lib.c b/sound/core/pcm_lib.c --- a/sound/core/pcm_lib.c Sun Mar 7 18:45:35 2004 +++ b/sound/core/pcm_lib.c Sun Mar 7 18:45:35 2004 @@ -67,8 +67,11 @@ frames = runtime->silence_size; } else { if (new_hw_ptr == ULONG_MAX) { /* initialization */ - runtime->silence_filled = 0; - runtime->silence_start = runtime->control->appl_ptr; + snd_pcm_sframes_t avail = snd_pcm_playback_hw_avail(runtime); + runtime->silence_filled = avail > 0 ? avail : 0; + runtime->silence_start = (runtime->status->hw_ptr + + runtime->silence_filled) % + runtime->boundary; } else { ofs = runtime->status->hw_ptr; frames = new_hw_ptr - ofs; @@ -2659,20 +2662,6 @@ EXPORT_SYMBOL(snd_pcm_lib_preallocate_free_for_all); EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages); EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all); +EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page); EXPORT_SYMBOL(snd_pcm_lib_malloc_pages); EXPORT_SYMBOL(snd_pcm_lib_free_pages); -#ifdef CONFIG_ISA -EXPORT_SYMBOL(snd_pcm_lib_preallocate_isa_pages); -EXPORT_SYMBOL(snd_pcm_lib_preallocate_isa_pages_for_all); -#endif -#ifdef CONFIG_PCI -EXPORT_SYMBOL(snd_pcm_lib_preallocate_pci_pages); -EXPORT_SYMBOL(snd_pcm_lib_preallocate_pci_pages_for_all); -EXPORT_SYMBOL(snd_pcm_lib_preallocate_sg_pages); -EXPORT_SYMBOL(snd_pcm_lib_preallocate_sg_pages_for_all); -EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page); -#endif -#ifdef CONFIG_SBUS -EXPORT_SYMBOL(snd_pcm_lib_preallocate_sbus_pages); -EXPORT_SYMBOL(snd_pcm_lib_preallocate_sbus_pages_for_all); -#endif diff -Nru a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c --- a/sound/core/pcm_memory.c Sun Mar 7 18:45:35 2004 +++ b/sound/core/pcm_memory.c Sun Mar 7 18:45:35 2004 @@ -229,97 +229,71 @@ } /** - * snd_pcm_lib_preallocate_pages - pre-allocation for the continuous memory type + * snd_pcm_lib_preallocate_pages - pre-allocation for the given DMA type * @substream: the pcm substream instance + * @type: DMA type (SNDRV_DMA_TYPE_*) + * @data: DMA type dependant data * @size: the requested pre-allocation size in bytes * @max: the max. allowed pre-allocation size - * @flags: allocation condition, GFP_XXX * - * Do pre-allocation for the continuous memory type. + * Do pre-allocation for the given DMA type. * * Returns zero if successful, or a negative error code on failure. */ int snd_pcm_lib_preallocate_pages(snd_pcm_substream_t *substream, - size_t size, size_t max, - unsigned int flags) + int type, struct device *data, + size_t size, size_t max) { - substream->dma_device.type = SNDRV_DMA_TYPE_CONTINUOUS; - substream->dma_device.dev.flags = flags; + substream->dma_device.type = type; + substream->dma_device.dev = data; setup_pcm_id(substream); return snd_pcm_lib_preallocate_pages1(substream, size, max); } /** * snd_pcm_lib_preallocate_pages_for_all - pre-allocation for continous memory type (all substreams) - * @pcm: pcm to assign the buffer + * @substream: the pcm substream instance + * @type: DMA type (SNDRV_DMA_TYPE_*) + * @data: DMA type dependant data * @size: the requested pre-allocation size in bytes - * @max: max. buffer size acceptable for the changes via proc file - * @flags: allocation condition, GFP_XXX + * @max: the max. allowed pre-allocation size * * Do pre-allocation to all substreams of the given pcm for the - * continuous memory type. + * specified DMA type. * * Returns zero if successful, or a negative error code on failure. */ int snd_pcm_lib_preallocate_pages_for_all(snd_pcm_t *pcm, - size_t size, size_t max, - unsigned int flags) + int type, void *data, + size_t size, size_t max) { snd_pcm_substream_t *substream; int stream, err; for (stream = 0; stream < 2; stream++) for (substream = pcm->streams[stream].substream; substream; substream = substream->next) - if ((err = snd_pcm_lib_preallocate_pages(substream, size, max, flags)) < 0) + if ((err = snd_pcm_lib_preallocate_pages(substream, type, data, size, max)) < 0) return err; return 0; } -#ifdef CONFIG_ISA /** - * snd_pcm_lib_preallocate_isa_pages - pre-allocation for the ISA bus - * @substream: substream to assign the buffer - * @size: the requested pre-allocation size in bytes - * @max: max. buffer size acceptable for the changes via proc file - * - * Do pre-allocation for the ISA bus. - * - * Returns zero if successful, or a negative error code on failure. - */ -int snd_pcm_lib_preallocate_isa_pages(snd_pcm_substream_t *substream, - size_t size, size_t max) -{ - substream->dma_device.type = SNDRV_DMA_TYPE_ISA; - substream->dma_device.dev.flags = 0; /* FIXME: any good identifier? */ - setup_pcm_id(substream); - return snd_pcm_lib_preallocate_pages1(substream, size, max); -} - -/* - * FIXME: the function name is too long for docbook! - * - * snd_pcm_lib_preallocate_isa_pages_for_all - pre-allocation for the ISA bus (all substreams) - * @pcm: pcm to assign the buffer - * @max: max. buffer size acceptable for the changes via proc file - * - * Do pre-allocation to all substreams of the given pcm for the - * ISA bus. + * snd_pcm_sgbuf_ops_page - get the page struct at the given offset + * @substream: the pcm substream instance + * @offset: the buffer offset * - * Returns zero if successful, or a negative error code on failure. + * Returns the page struct at the given buffer offset. + * Used as the page callback of PCM ops. */ -int snd_pcm_lib_preallocate_isa_pages_for_all(snd_pcm_t *pcm, - size_t size, size_t max) +struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset) { - snd_pcm_substream_t *substream; - int stream, err; + struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream); - for (stream = 0; stream < 2; stream++) - for (substream = pcm->streams[stream].substream; substream; substream = substream->next) - if ((err = snd_pcm_lib_preallocate_isa_pages(substream, size, max)) < 0) - return err; - return 0; + unsigned int idx = offset >> PAGE_SHIFT; + if (idx >= (unsigned int)sgbuf->pages) + return NULL; + return sgbuf->page_table[idx]; } -#endif /* CONFIG_ISA */ /** * snd_pcm_lib_malloc_pages - allocate the DMA buffer @@ -397,183 +371,6 @@ runtime->dma_private = NULL; return 0; } - -#ifdef CONFIG_PCI -/** - * snd_pcm_lib_preallocate_pci_pages - pre-allocation for the PCI bus - * - * @pci: pci device - * @substream: substream to assign the buffer - * @size: the requested pre-allocation size in bytes - * @max: max. buffer size acceptable for the changes via proc file - * - * Do pre-allocation for the PCI bus. - * - * Returns zero if successful, or a negative error code on failure. - */ -int snd_pcm_lib_preallocate_pci_pages(struct pci_dev *pci, - snd_pcm_substream_t *substream, - size_t size, size_t max) -{ - substream->dma_device.type = SNDRV_DMA_TYPE_PCI; - substream->dma_device.dev.pci = pci; - setup_pcm_id(substream); - return snd_pcm_lib_preallocate_pages1(substream, size, max); -} - -/* - * FIXME: the function name is too long for docbook! - * - * snd_pcm_lib_preallocate_pci_pages_for_all - pre-allocation for the PCI bus (all substreams) - * @pci: pci device - * @pcm: pcm to assign the buffer - * @size: the requested pre-allocation size in bytes - * @max: max. buffer size acceptable for the changes via proc file - * - * Do pre-allocation to all substreams of the given pcm for the - * PCI bus. - * - * Returns zero if successful, or a negative error code on failure. - */ -int snd_pcm_lib_preallocate_pci_pages_for_all(struct pci_dev *pci, - snd_pcm_t *pcm, - size_t size, size_t max) -{ - snd_pcm_substream_t *substream; - int stream, err; - - for (stream = 0; stream < 2; stream++) - for (substream = pcm->streams[stream].substream; substream; substream = substream->next) - if ((err = snd_pcm_lib_preallocate_pci_pages(pci, substream, size, max)) < 0) - return err; - return 0; -} - -#endif /* CONFIG_PCI */ - -#ifdef CONFIG_SBUS -/** - * snd_pcm_lib_preallocate_sbus_pages - pre-allocation for the SBUS bus - * - * @sbus: SBUS device - * @substream: substream to assign the buffer - * @max: max. buffer size acceptable for the changes via proc file - * - * Do pre-allocation for the SBUS. - * - * Returns zero if successful, or a negative error code on failure. - */ -int snd_pcm_lib_preallocate_sbus_pages(struct sbus_dev *sdev, - snd_pcm_substream_t *substream, - size_t size, size_t max) -{ - substream->dma_device.type = SNDRV_DMA_TYPE_SBUS; - substream->dma_device.dev.sbus = sdev; - setup_pcm_id(substream); - return snd_pcm_lib_preallocate_pages1(substream, size, max); -} - -/* - * FIXME: the function name is too long for docbook! - * - * snd_pcm_lib_preallocate_sbus_pages_for_all - pre-allocation for the SBUS bus (all substreams) - * @sbus: SBUS device - * @pcm: pcm to assign the buffer - * @size: the requested pre-allocation size in bytes - * @max: max. buffer size acceptable for the changes via proc file - * - * Do pre-allocation to all substreams of the given pcm for the - * SUBS. - * - * Returns zero if successful, or a negative error code on failure. - */ -int snd_pcm_lib_preallocate_sbus_pages_for_all(struct sbus_dev *sdev, - snd_pcm_t *pcm, - size_t size, size_t max) -{ - snd_pcm_substream_t *substream; - int stream, err; - - for (stream = 0; stream < 2; stream++) - for (substream = pcm->streams[stream].substream; substream; substream = substream->next) - if ((err = snd_pcm_lib_preallocate_sbus_pages(sdev, substream, size, max)) < 0) - return err; - return 0; -} - -#endif /* CONFIG_SBUS */ - - -#ifdef CONFIG_PCI -/** - * snd_pcm_lib_preallocate_sg_pages - initialize SG-buffer for the PCI bus - * - * @pci: pci device - * @substream: substream to assign the buffer - * @size: the requested pre-allocation size in bytes - * @max: max. buffer size acceptable for the changes via proc file - * - * Initializes SG-buffer for the PCI bus. - * - * Returns zero if successful, or a negative error code on failure. - */ -int snd_pcm_lib_preallocate_sg_pages(struct pci_dev *pci, - snd_pcm_substream_t *substream, - size_t size, size_t max) -{ - substream->dma_device.type = SNDRV_DMA_TYPE_PCI_SG; - substream->dma_device.dev.pci = pci; - setup_pcm_id(substream); - return snd_pcm_lib_preallocate_pages1(substream, size, max); -} - -/* - * FIXME: the function name is too long for docbook! - * - * snd_pcm_lib_preallocate_sg_pages_for_all - initialize SG-buffer for the PCI bus (all substreams) - * @pci: pci device - * @pcm: pcm to assign the buffer - * @size: the requested pre-allocation size in bytes - * @max: max. buffer size acceptable for the changes via proc file - * - * Initialize the SG-buffer to all substreams of the given pcm for the - * PCI bus. - * - * Returns zero if successful, or a negative error code on failure. - */ -int snd_pcm_lib_preallocate_sg_pages_for_all(struct pci_dev *pci, - snd_pcm_t *pcm, - size_t size, size_t max) -{ - snd_pcm_substream_t *substream; - int stream, err; - - for (stream = 0; stream < 2; stream++) - for (substream = pcm->streams[stream].substream; substream; substream = substream->next) - if ((err = snd_pcm_lib_preallocate_sg_pages(pci, substream, size, max)) < 0) - return err; - return 0; -} - -/** - * snd_pcm_sgbuf_ops_page - get the page struct at the given offset - * @substream: the pcm substream instance - * @offset: the buffer offset - * - * Returns the page struct at the given buffer offset. - * Used as the page callback of PCM ops. - */ -struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset) -{ - struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream); - - unsigned int idx = offset >> PAGE_SHIFT; - if (idx >= (unsigned int)sgbuf->pages) - return NULL; - return sgbuf->page_table[idx]; -} - -#endif /* CONFIG_PCI */ #ifndef MODULE diff -Nru a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c --- a/sound/core/pcm_misc.c Sun Mar 7 18:45:35 2004 +++ b/sound/core/pcm_misc.c Sun Mar 7 18:45:35 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 Mar 7 18:45:35 2004 +++ b/sound/core/seq/oss/seq_oss_midi.c Sun Mar 7 18:45:35 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); } @@ -578,6 +578,7 @@ ossev.v.code = EV_CHN_VOICE; ossev.v.note = ev->data.note.note; ossev.v.parm = ev->data.note.velocity; + ossev.v.chn = ev->data.note.channel; break; case SNDRV_SEQ_EVENT_CONTROLLER: case SNDRV_SEQ_EVENT_PGMCHANGE: @@ -585,10 +586,12 @@ ossev.l.code = EV_CHN_COMMON; ossev.l.p1 = ev->data.control.param; ossev.l.val = ev->data.control.value; + ossev.l.chn = ev->data.control.channel; break; case SNDRV_SEQ_EVENT_PITCHBEND: ossev.l.code = EV_CHN_COMMON; ossev.l.val = ev->data.control.value + 8192; + ossev.l.chn = ev->data.control.channel; break; } 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 Mar 7 18:45:35 2004 +++ b/sound/core/seq/oss/seq_oss_synth.c Sun Mar 7 18:45:35 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 Mar 7 18:45:35 2004 +++ b/sound/core/seq/seq_clientmgr.c Sun Mar 7 18:45:35 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 Mar 7 18:45:35 2004 +++ b/sound/core/seq/seq_fifo.c Sun Mar 7 18:45:35 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 Mar 7 18:45:35 2004 +++ b/sound/core/seq/seq_memory.c Sun Mar 7 18:45:35 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 Mar 7 18:45:36 2004 +++ b/sound/core/seq/seq_midi.c Sun Mar 7 18:45:36 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/core/sgbuf.c b/sound/core/sgbuf.c --- a/sound/core/sgbuf.c Sun Mar 7 18:45:35 2004 +++ b/sound/core/sgbuf.c Sun Mar 7 18:45:35 2004 @@ -20,7 +20,6 @@ */ #include -#include #include #include #include @@ -31,27 +30,42 @@ #define SGBUF_TBL_ALIGN 32 #define sgbuf_align_table(tbl) ((((tbl) + SGBUF_TBL_ALIGN - 1) / SGBUF_TBL_ALIGN) * SGBUF_TBL_ALIGN) +int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab) +{ + struct snd_sg_buf *sgbuf = dmab->private_data; + struct snd_dma_buffer tmpb; + int i; + + if (! sgbuf) + return -EINVAL; + + for (i = 0; i < sgbuf->pages; i++) { + tmpb.area = sgbuf->table[i].buf; + tmpb.addr = sgbuf->table[i].addr; + tmpb.bytes = PAGE_SIZE; + snd_dma_free_pages(&sgbuf->dev, &tmpb); + } + if (dmab->area) + vunmap(dmab->area); + dmab->area = NULL; + + if (sgbuf->table) + kfree(sgbuf->table); + if (sgbuf->page_table) + kfree(sgbuf->page_table); + kfree(sgbuf); + dmab->private_data = NULL; + + return 0; +} -/** - * snd_malloc_sgbuf_pages - allocate the pages for the PCI SG buffer - * @pci: the pci device pointer - * @size: the requested buffer size in bytes - * @dmab: the buffer record to store - * - * Initializes the SG-buffer table and allocates the buffer pages - * for the given size. - * The pages are mapped to the virtually continuous memory. - * - * This function is usually called from the middle-level functions such as - * snd_pcm_lib_malloc_pages(). - * - * Returns the mapped virtual address of the buffer if allocation was - * successful, or NULL at error. - */ -void *snd_malloc_sgbuf_pages(struct pci_dev *pci, size_t size, struct snd_dma_buffer *dmab) +void *snd_malloc_sgbuf_pages(const struct snd_dma_device *dev, + size_t size, struct snd_dma_buffer *dmab, + size_t *res_size) { struct snd_sg_buf *sgbuf; unsigned int i, pages; + struct snd_dma_buffer tmpb; dmab->area = NULL; dmab->addr = 0; @@ -59,7 +73,8 @@ if (! sgbuf) return NULL; memset(sgbuf, 0, sizeof(*sgbuf)); - sgbuf->pci = pci; + sgbuf->dev = *dev; + sgbuf->dev.type = SNDRV_DMA_TYPE_DEV; pages = snd_sgbuf_aligned_pages(size); sgbuf->tblsize = sgbuf_align_table(pages); sgbuf->table = kmalloc(sizeof(*sgbuf->table) * sgbuf->tblsize, GFP_KERNEL); @@ -73,14 +88,15 @@ /* allocate each page */ for (i = 0; i < pages; i++) { - void *ptr; - dma_addr_t addr; - ptr = snd_malloc_pci_page(sgbuf->pci, &addr); - if (! ptr) - goto _failed; - sgbuf->table[i].buf = ptr; - sgbuf->table[i].addr = addr; - sgbuf->page_table[i] = virt_to_page(ptr); + if (snd_dma_alloc_pages(&sgbuf->dev, PAGE_SIZE, &tmpb) < 0) { + if (res_size == NULL) + goto _failed; + *res_size = size = sgbuf->pages * PAGE_SIZE; + break; + } + sgbuf->table[i].buf = tmpb.area; + sgbuf->table[i].addr = tmpb.addr; + sgbuf->page_table[i] = virt_to_page(tmpb.area); sgbuf->pages++; } @@ -93,39 +109,4 @@ _failed: snd_free_sgbuf_pages(dmab); /* free the table */ return NULL; -} - -/** - * snd_free_sgbuf_pages - free the sg buffer - * @dmab: buffer record - * - * Releases the pages and the SG-buffer table. - * - * This function is called usually from the middle-level function - * such as snd_pcm_lib_free_pages(). - * - * Returns zero if successful, or a negative error code on failure. - */ -int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab) -{ - struct snd_sg_buf *sgbuf = dmab->private_data; - int i; - - if (! sgbuf) - return -EINVAL; - - for (i = 0; i < sgbuf->pages; i++) - snd_free_pci_page(sgbuf->pci, sgbuf->table[i].buf, sgbuf->table[i].addr); - if (dmab->area) - vunmap(dmab->area); - dmab->area = NULL; - - if (sgbuf->table) - kfree(sgbuf->table); - if (sgbuf->page_table) - kfree(sgbuf->page_table); - kfree(sgbuf); - dmab->private_data = NULL; - - return 0; } diff -Nru a/sound/drivers/Kconfig b/sound/drivers/Kconfig --- a/sound/drivers/Kconfig Sun Mar 7 18:45:35 2004 +++ b/sound/drivers/Kconfig Sun Mar 7 18:45:35 2004 @@ -3,17 +3,41 @@ menu "Generic devices" depends on SND!=n + +config SND_MPU401_UART + tristate + select SND_TIMER + select SND_RAWMIDI + +config SND_OPL3_LIB + tristate + select SND_TIMER + select SND_HWDEP + +config SND_OPL4_LIB + tristate + select SND_TIMER + select SND_HWDEP + +config SND_VX_LIB + tristate + select SND_HWDEP + select SND_PCM + + config SND_DUMMY tristate "Dummy (/dev/null) soundcard" depends on SND + select SND_PCM help Say 'Y' or 'M' to include dummy driver. This driver does nothing, but emulates various mixer controls and PCM devices. - config SND_VIRMIDI tristate "Virtual MIDI soundcard" depends on SND_SEQUENCER + select SND_TIMER + select SND_RAWMIDI help Say 'Y' or 'M' to include virtual MIDI driver. This driver allows to connect applications using raw MIDI devices to sequencer. @@ -21,6 +45,8 @@ config SND_MTPAV tristate "MOTU MidiTimePiece AV multiport MIDI" depends on SND + select SND_TIMER + select SND_RAWMIDI help Say 'Y' or 'M' to include support for MOTU MidiTimePiece AV multiport MIDI adapter. @@ -28,6 +54,8 @@ config SND_SERIAL_U16550 tristate "UART16550 - MIDI only driver" depends on SND + select SND_TIMER + select SND_RAWMIDI help Say 'Y' or 'M' to include support for MIDI serial port driver. It works with serial UARTs 16550 and better. @@ -35,8 +63,8 @@ config SND_MPU401 tristate "Generic MPU-401 UART driver" depends on SND + select SND_MPU401_UART help Say 'Y' or 'M' to include support for MPU401 hardware using UART access. endmenu - diff -Nru a/sound/drivers/mpu401/Makefile b/sound/drivers/mpu401/Makefile --- a/sound/drivers/mpu401/Makefile Sun Mar 7 18:45:36 2004 +++ b/sound/drivers/mpu401/Makefile Sun Mar 7 18:45:36 2004 @@ -6,40 +6,7 @@ snd-mpu401-objs := mpu401.o snd-mpu401-uart-objs := mpu401_uart.o -# Toplevel Module Dependency -obj-$(CONFIG_SND_MPU401) += snd-mpu401.o snd-mpu401-uart.o -obj-$(CONFIG_SND_ALS100) += snd-mpu401-uart.o -obj-$(CONFIG_SND_AZT2320) += snd-mpu401-uart.o -obj-$(CONFIG_SND_AZT3328) += snd-mpu401-uart.o -obj-$(CONFIG_SND_DT019X) += snd-mpu401-uart.o -obj-$(CONFIG_SND_ES18XX) += snd-mpu401-uart.o -obj-$(CONFIG_SND_OPL3SA2) += snd-mpu401-uart.o -obj-$(CONFIG_SND_AD1816A) += snd-mpu401-uart.o -obj-$(CONFIG_SND_CS4231) += snd-mpu401-uart.o -obj-$(CONFIG_SND_CS4232) += snd-mpu401-uart.o -obj-$(CONFIG_SND_CS4236) += snd-mpu401-uart.o -obj-$(CONFIG_SND_ES1688) += snd-mpu401-uart.o -obj-$(CONFIG_SND_GUSEXTREME) += snd-mpu401-uart.o -obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-mpu401-uart.o -obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-mpu401-uart.o -obj-$(CONFIG_SND_OPTI93X) += snd-mpu401-uart.o -obj-$(CONFIG_SND_SB16) += snd-mpu401-uart.o -obj-$(CONFIG_SND_SBAWE) += snd-mpu401-uart.o -obj-$(CONFIG_SND_WAVEFRONT) += snd-mpu401-uart.o -obj-$(CONFIG_SND_ALS4000) += snd-mpu401-uart.o -obj-$(CONFIG_SND_CMIPCI) += snd-mpu401-uart.o -obj-$(CONFIG_SND_ES1938) += snd-mpu401-uart.o -obj-$(CONFIG_SND_ES1968) += snd-mpu401-uart.o -obj-$(CONFIG_SND_FM801) += snd-mpu401-uart.o -obj-$(CONFIG_SND_ICE1712) += snd-mpu401-uart.o -obj-$(CONFIG_SND_ICE1724) += snd-mpu401-uart.o -obj-$(CONFIG_SND_INTEL8X0) += snd-mpu401-uart.o -obj-$(CONFIG_SND_SONICVIBES) += snd-mpu401-uart.o -obj-$(CONFIG_SND_VIA82XX) += snd-mpu401-uart.o -obj-$(CONFIG_SND_ALI5451) += snd-mpu401-uart.o -obj-$(CONFIG_SND_TRIDENT) += snd-mpu401-uart.o -obj-$(CONFIG_SND_YMFPCI) += snd-mpu401-uart.o -obj-$(CONFIG_SND_PC98_CS4232) += snd-mpu401-uart.o -obj-$(CONFIG_SND_SSCAPE) += snd-mpu401-uart.o +obj-$(CONFIG_SND_MPU401_UART) += snd-mpu401-uart.o -obj-m := $(sort $(obj-m)) +# Toplevel Module Dependency +obj-$(CONFIG_SND_MPU401) += snd-mpu401.o diff -Nru a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c --- a/sound/drivers/mpu401/mpu401_uart.c Sun Mar 7 18:45:35 2004 +++ b/sound/drivers/mpu401/mpu401_uart.c Sun Mar 7 18:45:35 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 Mar 7 18:45:35 2004 +++ b/sound/drivers/opl3/Makefile Sun Mar 7 18:45:35 2004 @@ -9,37 +9,13 @@ snd-opl3-synth-objs += opl3_oss.o endif -OPL3_OBJS = snd-opl3-lib.o -ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y) -OPL3_OBJS += snd-opl3-synth.o -endif +# +# 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-$(CONFIG_SND_AZT2320) += $(OPL3_OBJS) -obj-$(CONFIG_SND_AZT3328) += $(OPL3_OBJS) -obj-$(CONFIG_SND_DT019X) += $(OPL3_OBJS) -obj-$(CONFIG_SND_ES18XX) += $(OPL3_OBJS) -obj-$(CONFIG_SND_OPL3SA2) += $(OPL3_OBJS) -obj-$(CONFIG_SND_AD1816A) += $(OPL3_OBJS) -obj-$(CONFIG_SND_CS4232) += $(OPL3_OBJS) -obj-$(CONFIG_SND_PC98_CS4232) += $(OPL3_OBJS) -obj-$(CONFIG_SND_CS4236) += $(OPL3_OBJS) -obj-$(CONFIG_SND_ES1688) += $(OPL3_OBJS) -obj-$(CONFIG_SND_GUSEXTREME) += $(OPL3_OBJS) -obj-$(CONFIG_SND_OPTI92X_AD1848) += $(OPL3_OBJS) -obj-$(CONFIG_SND_OPTI92X_CS4231) += $(OPL3_OBJS) -obj-$(CONFIG_SND_OPTI93X) += $(OPL3_OBJS) -obj-$(CONFIG_SND_SB8) += $(OPL3_OBJS) -obj-$(CONFIG_SND_SB16) += $(OPL3_OBJS) -obj-$(CONFIG_SND_SBAWE) += $(OPL3_OBJS) -obj-$(CONFIG_SND_WAVEFRONT) += $(OPL3_OBJS) -obj-$(CONFIG_SND_ALS4000) += $(OPL3_OBJS) -obj-$(CONFIG_SND_CMIPCI) += $(OPL3_OBJS) -obj-$(CONFIG_SND_CS4281) += $(OPL3_OBJS) -obj-$(CONFIG_SND_ES1938) += $(OPL3_OBJS) -obj-$(CONFIG_SND_FM801) += $(OPL3_OBJS) -obj-$(CONFIG_SND_SONICVIBES) += $(OPL3_OBJS) -obj-$(CONFIG_SND_YMFPCI) += $(OPL3_OBJS) - -obj-m := $(sort $(obj-m)) +obj-$(CONFIG_SND_OPL3_LIB) += snd-opl3-lib.o +obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-opl3-synth.o diff -Nru a/sound/drivers/opl4/Makefile b/sound/drivers/opl4/Makefile --- a/sound/drivers/opl4/Makefile Sun Mar 7 18:45:36 2004 +++ b/sound/drivers/opl4/Makefile Sun Mar 7 18:45:36 2004 @@ -6,10 +6,13 @@ snd-opl4-lib-objs := opl4_lib.o opl4_mixer.o opl4_proc.o 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 +# +# 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-$(CONFIG_SND_OPTI92X_CS4231) += $(OPL4_OBJS) +obj-$(CONFIG_SND_OPL4_LIB) += snd-opl4-lib.o +obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-opl4-synth.o \ No newline at end of file diff -Nru a/sound/drivers/vx/Makefile b/sound/drivers/vx/Makefile --- a/sound/drivers/vx/Makefile Sun Mar 7 18:45:35 2004 +++ b/sound/drivers/vx/Makefile Sun Mar 7 18:45:35 2004 @@ -5,7 +5,4 @@ snd-vx-lib-objs := vx_core.o vx_hwdep.o vx_pcm.o vx_mixer.o vx_cmd.o vx_uer.o -obj-$(CONFIG_SND_VXPOCKET) += snd-vx-lib.o -obj-$(CONFIG_SND_VXP440) += snd-vx-lib.o -obj-$(CONFIG_SND_VX222) += snd-vx-lib.o - +obj-$(CONFIG_SND_VX_LIB) += snd-vx-lib.o diff -Nru a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c --- a/sound/drivers/vx/vx_core.c Sun Mar 7 18:45:36 2004 +++ b/sound/drivers/vx/vx_core.c Sun Mar 7 18:45:36 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 Mar 7 18:45:35 2004 +++ b/sound/drivers/vx/vx_mixer.c Sun Mar 7 18:45:35 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 Mar 7 18:45:35 2004 +++ b/sound/drivers/vx/vx_pcm.c Sun Mar 7 18:45:35 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 Mar 7 18:45:35 2004 +++ b/sound/i2c/other/Makefile Sun Mar 7 18:45:35 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 Mar 7 18:45:36 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/isa/Kconfig b/sound/isa/Kconfig --- a/sound/isa/Kconfig Sun Mar 7 18:45:36 2004 +++ b/sound/isa/Kconfig Sun Mar 7 18:45:36 2004 @@ -6,6 +6,9 @@ config SND_AD1816A tristate "Analog Devices SoundPort AD1816A" depends on SND && ISAPNP + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Analog Devices SoundPort AD1816A or compatible sound chips. @@ -13,6 +16,7 @@ config SND_AD1848 tristate "Generic AD1848/CS4248 driver" depends on SND + select SND_PCM help Say 'Y' or 'M' to include support for AD1848 (Analog Devices) or CS4248 (Cirrus Logic - Crystal Semiconductors) chips. Please, for newer chips @@ -21,6 +25,8 @@ config SND_CS4231 tristate "Generic Cirrus Logic CS4231 driver" depends on SND + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for CS4231 chips from Cirrus Logic - Crystal Semiconductors. @@ -28,6 +34,9 @@ config SND_CS4232 tristate "Generic Cirrus Logic CS4232 driver" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for CS4232 chips from Cirrus Logic - Crystal Semiconductors. @@ -35,6 +44,9 @@ config SND_CS4236 tristate "Generic Cirrus Logic CS4236+ driver" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for CS4235,CS4236,CS4237B,CS4238B,CS4239 chips from Cirrus Logic - Crystal Semiconductors. @@ -42,6 +54,9 @@ config SND_PC98_CS4232 tristate "NEC PC9800 CS4232 driver" depends on SND && X86_PC9800 + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for NEC PC-9801/PC-9821 on-board soundchip based on CS4232. @@ -49,42 +64,59 @@ config SND_ES968 tristate "Generic ESS ES968 driver" depends on SND && ISAPNP + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for ESS AudioDrive ES968 chip. config SND_ES1688 tristate "Generic ESS ES688/ES1688 driver" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for ESS AudioDrive ES688 or ES1688 chips. config SND_ES18XX tristate "Generic ESS ES18xx driver" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for ESS AudioDrive ES18xx chips. config SND_GUSCLASSIC tristate "Gravis UltraSound Classic" depends on SND + select SND_RAWMIDI + select SND_PCM help Say 'Y' or 'M' to include support for Gravis UltraSound Classic soundcard. config SND_GUSEXTREME tristate "Gravis UltraSound Extreme" depends on SND + select SND_HWDEP + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Gravis UltraSound Extreme soundcard. config SND_GUSMAX tristate "Gravis UltraSound MAX" depends on SND + select SND_RAWMIDI + select SND_PCM help Say 'Y' or 'M' to include support for Gravis UltraSound MAX soundcard. config SND_INTERWAVE tristate "AMD InterWave, Gravis UltraSound PnP" depends on SND + select SND_RAWMIDI + select SND_PCM help Say 'Y' or 'M' to include support for AMD InterWave based soundcards (Gravis UltraSound Plug & Play, STB SoundRage32, MED3210, Dynasonic Pro, @@ -93,6 +125,8 @@ config SND_INTERWAVE_STB tristate "AMD InterWave + TEA6330T (UltraSound 32-Pro)" depends on SND + select SND_RAWMIDI + select SND_PCM help Say 'Y' or 'M' to include support for AMD InterWave based soundcards with TEA6330T bass and treble regulator (UltraSound 32-Pro). @@ -100,6 +134,10 @@ config SND_OPTI92X_AD1848 tristate "OPTi 82C92x - AD1848" depends on SND + select SND_OPL3_LIB + select SND_OPL4_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Opti92x soundcards equiped with AD1848 codec. @@ -107,6 +145,10 @@ config SND_OPTI92X_CS4231 tristate "OPTi 82C92x - CS4231" depends on SND + select SND_OPL3_LIB + select SND_OPL4_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Opti92x soundcards equiped with CS4231 codec. @@ -114,12 +156,18 @@ config SND_OPTI93X tristate "OPTi 82C93x" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Opti93x soundcards. config SND_SB8 tristate "Sound Blaster 1.0/2.0/Pro (8-bit)" depends on SND + select SND_OPL3_LIB + select SND_RAWMIDI + select SND_PCM help Say 'Y' or 'M' to include support for Sound Blaster 1.0/2.0/Pro (8-bit) soundcards or 100% compatible from Creative. @@ -127,6 +175,9 @@ config SND_SB16 tristate "Sound Blaster 16 (PnP)" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Sound Blaster 16 (including Plug and Play version). @@ -134,6 +185,9 @@ config SND_SBAWE tristate "Sound Blaster AWE (32,64) (PnP)" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Sound Blaster AWE (including Plug and Play version). @@ -149,6 +203,9 @@ config SND_WAVEFRONT tristate "Turtle Beach Maui,Tropez,Tropez+ (Wavefront)" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Turtle Beach Maui, Tropez and Tropez+ soundcards based on Wavefront chip. @@ -156,6 +213,9 @@ config SND_ALS100 tristate "Avance Logic ALS100/ALS120" depends on SND && ISAPNP + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Avance Logic ALS100, ALS110, ALS120 and ALS200 soundcards. @@ -163,18 +223,25 @@ config SND_AZT2320 tristate "Aztech Systems AZT2320" depends on SND && ISAPNP + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Aztech Systems AZT2320 soundcard. config SND_CMI8330 tristate "C-Media CMI8330" depends on SND + select SND_PCM help Say 'Y' or 'M' to include support for C-Media CMI8330 based soundcards. config SND_DT019X tristate "Diamond Technologies DT-019X, Avance Logic ALS-007" depends on SND && ISAPNP + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Diamond Technologies DT-019X and Avance Logic ALS-007 soundcards. @@ -182,18 +249,25 @@ config SND_OPL3SA2 tristate "Yamaha OPL3-SA2/SA3" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Yamaha OPL3SA2 or OPL3SA3 chips. config SND_SGALAXY tristate "Aztech Sound Galaxy" depends on SND + select SND_PCM help Say 'Y' or 'M' to include support for Aztech Sound Galaxy. config SND_SSCAPE tristate "Ensoniq SoundScape PnP driver" depends on SND + select SND_HWDEP + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Ensoniq SoundScape PnP soundcard. diff -Nru a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c --- a/sound/isa/ad1816a/ad1816a_lib.c Sun Mar 7 18:45:35 2004 +++ b/sound/isa/ad1816a/ad1816a_lib.c Sun Mar 7 18:45:35 2004 @@ -684,7 +684,9 @@ strcpy(pcm->name, snd_ad1816a_chip_id(chip)); snd_ad1816a_init(chip); - snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_isa_data(), + 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); chip->pcm = pcm; if (rpcm) diff -Nru a/sound/isa/ad1848/ad1848_lib.c b/sound/isa/ad1848/ad1848_lib.c --- a/sound/isa/ad1848/ad1848_lib.c Sun Mar 7 18:45:36 2004 +++ b/sound/isa/ad1848/ad1848_lib.c Sun Mar 7 18:45:36 2004 @@ -1043,7 +1043,9 @@ pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; strcpy(pcm->name, snd_ad1848_chip_id(chip)); - snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, chip->dma > 3 ? 128*1024 : 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_isa_data(), + 64*1024, chip->dma > 3 ? 128*1024 : 64*1024); chip->pcm = pcm; if (rpcm) diff -Nru a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c --- a/sound/isa/cmi8330.c Sun Mar 7 18:45:35 2004 +++ b/sound/isa/cmi8330.c Sun Mar 7 18:45:35 2004 @@ -438,7 +438,9 @@ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &chip->streams[SNDRV_PCM_STREAM_PLAYBACK].ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &chip->streams[SNDRV_PCM_STREAM_CAPTURE].ops); - snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_isa_data(), + 64*1024, 128*1024); chip->pcm = pcm; return 0; diff -Nru a/sound/isa/cs423x/cs4231_lib.c b/sound/isa/cs423x/cs4231_lib.c --- a/sound/isa/cs423x/cs4231_lib.c Sun Mar 7 18:45:35 2004 +++ b/sound/isa/cs423x/cs4231_lib.c Sun Mar 7 18:45:35 2004 @@ -1653,17 +1653,21 @@ strcpy(pcm->name, snd_cs4231_chip_id(chip)); #ifdef LEGACY_SUPPORT - snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_isa_data(), + 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); #else # ifdef EBUS_SUPPORT if (chip->ebus_flag) { - snd_pcm_lib_preallocate_pci_pages_for_all(chip->dev_u.pdev, pcm, - 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_PCI, + chip->dev_u.pdev, + 64*1024, 128*1024); } else { # endif # ifdef SBUS_SUPPORT - snd_pcm_lib_preallocate_sbus_pages_for_all(chip->dev_u.sdev, pcm, - 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_SBUS, + chip->dev_u.sdev, + 64*1024, 128*1024); # endif # ifdef EBUS_SUPPORT } diff -Nru a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c --- a/sound/isa/es1688/es1688_lib.c Sun Mar 7 18:45:35 2004 +++ b/sound/isa/es1688/es1688_lib.c Sun Mar 7 18:45:35 2004 @@ -752,7 +752,9 @@ sprintf(pcm->name, snd_es1688_chip_id(chip)); chip->pcm = pcm; - snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_isa_data(), + 64*1024, 64*1024); if (rpcm) *rpcm = pcm; diff -Nru a/sound/isa/es18xx.c b/sound/isa/es18xx.c --- a/sound/isa/es18xx.c Sun Mar 7 18:45:35 2004 +++ b/sound/isa/es18xx.c Sun Mar 7 18:45:35 2004 @@ -1598,7 +1598,10 @@ sprintf(pcm->name, "ESS AudioDrive ES%x", chip->version); chip->pcm = pcm; - snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_isa_data(), + 64*1024, + chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); if (rpcm) *rpcm = pcm; diff -Nru a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c --- a/sound/isa/gus/gus_pcm.c Sun Mar 7 18:45:35 2004 +++ b/sound/isa/gus/gus_pcm.c Sun Mar 7 18:45:35 2004 @@ -874,7 +874,9 @@ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_gf1_pcm_playback_ops); for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) - snd_pcm_lib_preallocate_isa_pages(substream, 64*1024, gus->gf1.dma1 > 3 ? 128*1024 : 64*1024); + snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, + snd_dma_isa_data(), + 64*1024, gus->gf1.dma1 > 3 ? 128*1024 : 64*1024); pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; @@ -882,7 +884,9 @@ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_gf1_pcm_capture_ops); if (gus->gf1.dma2 == gus->gf1.dma1) pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX; - snd_pcm_lib_preallocate_isa_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, 64*1024, gus->gf1.dma2 > 3 ? 128*1024 : 64*1024); + snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, + SNDRV_DMA_TYPE_DEV, snd_dma_isa_data(), + 64*1024, gus->gf1.dma2 > 3 ? 128*1024 : 64*1024); } strcpy(pcm->name, pcm->id); if (gus->interwave) { diff -Nru a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c --- a/sound/isa/opti9xx/opti92x-ad1848.c Sun Mar 7 18:45:35 2004 +++ b/sound/isa/opti9xx/opti92x-ad1848.c Sun Mar 7 18:45:35 2004 @@ -311,8 +311,12 @@ static long snd_legacy_find_free_ioport(long *port_table, long size) { while (*port_table != -1) { - if (!check_region(*port_table, size)) + struct resource *res; + if ((res = request_region(*port_table, size, "ALSA test")) != NULL) { + release_resource(res); + kfree_nocheck(res); return *port_table; + } port_table++; } return -1; @@ -1399,7 +1403,9 @@ strcpy(pcm->name, snd_opti93x_chip_id(codec)); - snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, codec->dma1 > 3 || codec->dma2 > 3 ? 128*1024 : 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_isa_data(), + 64*1024, codec->dma1 > 3 || codec->dma2 > 3 ? 128*1024 : 64*1024); codec->pcm = pcm; if (rpcm) @@ -1672,13 +1678,18 @@ if ((err = snd_opti9xx_init(chip, i)) < 0) return err; - if (check_region(chip->mc_base, chip->mc_base_size)) + if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL) continue; value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)); if ((value != 0xff) && (value != inb(chip->mc_base + 1))) if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1))) return 1; + + release_resource(chip->res_mc_base); + kfree_nocheck(chip->res_mc_base); + chip->res_mc_base = NULL; + } #else /* OPTi93X */ for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) { @@ -1688,7 +1699,7 @@ if ((err = snd_opti9xx_init(chip, i)) < 0) return err; - if (check_region(chip->mc_base, chip->mc_base_size)) + if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL) continue; spin_lock_irqsave(&chip->lock, flags); @@ -1701,6 +1712,10 @@ snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value); if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value) return 1; + + release_resource(chip->res_mc_base); + kfree_nocheck(chip->res_mc_base); + chip->res_mc_base = NULL; } #endif /* OPTi93X */ @@ -1984,7 +1999,8 @@ } #endif /* CONFIG_PNP */ - if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL) { + if (! chip->res_mc_base && + (chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL) { snd_card_free(card); return -ENOMEM; } diff -Nru a/sound/isa/sb/sb16_main.c b/sound/isa/sb/sb16_main.c --- a/sound/isa/sb/sb16_main.c Sun Mar 7 18:45:35 2004 +++ b/sound/isa/sb/sb16_main.c Sun Mar 7 18:45:35 2004 @@ -881,7 +881,9 @@ else pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; - snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_isa_data(), + 64*1024, 128*1024); if (rpcm) *rpcm = pcm; diff -Nru a/sound/isa/sb/sb8_main.c b/sound/isa/sb/sb8_main.c --- a/sound/isa/sb/sb8_main.c Sun Mar 7 18:45:35 2004 +++ b/sound/isa/sb/sb8_main.c Sun Mar 7 18:45:35 2004 @@ -535,7 +535,9 @@ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb8_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb8_capture_ops); - snd_pcm_lib_preallocate_isa_pages_for_all(pcm, 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_isa_data(), + 64*1024, 64*1024); if (rpcm) *rpcm = pcm; diff -Nru a/sound/isa/sscape.c b/sound/isa/sscape.c --- a/sound/isa/sscape.c Sun Mar 7 18:45:35 2004 +++ b/sound/isa/sscape.c Sun Mar 7 18:45:35 2004 @@ -168,23 +168,20 @@ } -struct dmabuf { - size_t size; - unsigned char *data; - dma_addr_t addr; -}; - /* * Allocates some kernel memory that we can use for DMA. * I think this means that the memory has to map to * contiguous pages of physical memory. */ -static struct dmabuf *get_dmabuf(struct dmabuf *buf, unsigned long s) +static struct snd_dma_buffer *get_dmabuf(struct snd_dma_buffer *buf, unsigned long size) { if (buf) { - buf->data = snd_malloc_isa_pages_fallback(s, &buf->addr, &buf->size); - if (!buf->data) { - snd_printk(KERN_ERR "sscape: Failed to allocate %lu bytes for DMA\n", s); + struct snd_dma_device dev; + memset(&dev, 0, sizeof(dev)); + dev.type = SNDRV_DMA_TYPE_DEV; + dev.dev = snd_dma_isa_data(); + if (snd_dma_alloc_pages_fallback(&dev, size, buf) < 0) { + snd_printk(KERN_ERR "sscape: Failed to allocate %lu bytes for DMA\n", size); return NULL; } } @@ -195,10 +192,15 @@ /* * Release the DMA-able kernel memory ... */ -static void free_dmabuf(struct dmabuf *buf) +static void free_dmabuf(struct snd_dma_buffer *buf) { - if (buf && buf->data) - snd_free_isa_pages(buf->size, buf->data, buf->addr); + if (buf && buf->area) { + struct snd_dma_device dev; + memset(&dev, 0, sizeof(dev)); + dev.type = SNDRV_DMA_TYPE_DEV; + dev.dev = snd_dma_isa_data(); + snd_dma_free_pages(&dev, buf); + } } @@ -456,7 +458,7 @@ size_t size) { unsigned long flags; - struct dmabuf dma; + struct snd_dma_buffer dma; int ret; if (!get_dmabuf(&dma, PAGE_ALIGN(size))) @@ -500,8 +502,8 @@ * comes from USERSPACE. We have already verified * the userspace pointer ... */ - len = min(size, dma.size); - __copy_from_user(dma.data, data, len); + len = min(size, dma.bytes); + __copy_from_user(dma.area, data, len); data += len; size -= len; diff -Nru a/sound/parisc/Kconfig b/sound/parisc/Kconfig --- a/sound/parisc/Kconfig Sun Mar 7 18:45:35 2004 +++ b/sound/parisc/Kconfig Sun Mar 7 18:45:35 2004 @@ -6,6 +6,7 @@ config SND_HARMONY tristate "Harmony/Vivace sound chip" depends on SND + select SND_PCM help Say 'Y' or 'M' to include support for Harmony/Vivace soundchip on HP712s, 715/new and many other GSC based machines. diff -Nru a/sound/parisc/harmony.c b/sound/parisc/harmony.c --- a/sound/parisc/harmony.c Sun Mar 7 18:45:35 2004 +++ b/sound/parisc/harmony.c Sun Mar 7 18:45:35 2004 @@ -205,6 +205,7 @@ unsigned char *silence_addr; dma_addr_t silence_dma; int silence_count; + struct snd_dma_device dma_dev; /* alsa stuff */ snd_card_t *card; @@ -844,22 +845,27 @@ harmony->pcm = pcm; /* initialize graveyard buffer */ - harmony->graveyard_addr = snd_malloc_pci_pages(harmony->fake_pci_dev, + harmony->dma_dev.type = SNDRV_DMA_TYPE_PCI; + harmony->dma_dev.dev = snd_dma_pci_data(harmony->fake_pci_dev); + harmony->graveyard_addr = snd_dma_alloc_pages(&chip->dma_dev, HARMONY_BUF_SIZE*GRAVEYARD_BUFS, &harmony->graveyard_dma); harmony->graveyard_count = 0; /* initialize silence buffers */ - harmony->silence_addr = snd_malloc_pci_pages(harmony->fake_pci_dev, + harmony->silence_addr = snd_dma_alloc_pages(&chip->dma_dev, HARMONY_BUF_SIZE*SILENCE_BUFS, &harmony->silence_dma); harmony->silence_count = 0; - harmony->ply_stopped = harmony->cap_stopped = 1; harmony->playback_substream = NULL; harmony->capture_substream = NULL; harmony->graveyard_count = 0; + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(harmony->fake_pci_dev), + 64 * 1024, 128 * 1024); + return 0; } diff -Nru a/sound/pci/Kconfig b/sound/pci/Kconfig --- a/sound/pci/Kconfig Sun Mar 7 18:45:35 2004 +++ b/sound/pci/Kconfig Sun Mar 7 18:45:35 2004 @@ -3,21 +3,31 @@ menu "PCI devices" depends on SND!=n && PCI +config SND_AC97_CODEC + tristate + select SND_PCM + config SND_ALI5451 tristate "ALi PCI Audio M5451" depends on SND + select SND_MPU401_UART + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for ALI PCI Audio M5451 sound core. config SND_AZT3328 tristate "Aztech AZF3328 / PCI168 (EXPERIMENTAL)" depends on SND && EXPERIMENTAL + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Aztech AZF3328 (PCI168) soundcards. config SND_BT87X tristate "Bt87x Audio Capture" depends on SND + select SND_PCM help Say 'Y' or 'M' to include support for recording audio from TV cards based on Brooktree Bt878/Bt879 chips. @@ -25,7 +35,8 @@ config SND_CS46XX tristate "Cirrus Logic (Sound Fusion) CS4280/CS461x/CS462x/CS463x" depends on SND - select GAMEPORT + select SND_RAWMIDI + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for Cirrus Logic CS4610 / CS4612 / CS4614 / CS4615 / CS4622 / CS4624 / CS4630 / CS4280 chips. @@ -39,12 +50,18 @@ config SND_CS4281 tristate "Cirrus Logic (Sound Fusion) CS4281" depends on SND + select SND_OPL3_LIB + select SND_RAWMIDI + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for Cirrus Logic CS4281. config SND_EMU10K1 tristate "EMU10K1 (SB Live! & Audigy, E-mu APS)" depends on SND + select SND_HWDEP + select SND_RAWMIDI + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for Sound Blaster PCI 512, Live!, Audigy and E-mu APS (partially supported). @@ -52,18 +69,29 @@ config SND_KORG1212 tristate "Korg 1212 IO" depends on SND + select SND_PCM help Say 'Y' or 'M' to include support for Korg 1212IO. +config SND_MIXART + tristate "Digigram miXart" + depends on SND + select SND_HWDEP + select SND_PCM + help + Say 'Y' or 'M' to include support for Digigram miXart soundcard. + config SND_NM256 tristate "NeoMagic NM256AV/ZX" depends on SND + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for NeoMagic NM256AV/ZX chips. config SND_RME32 tristate "RME Digi32, 32/8, 32 PRO" depends on SND + select SND_PCM help Say 'Y' or 'M' to include support for RME Digi32, Digi32 PRO and Digi32/8 (Sek'd Prodif32, Prodif96 and Prodif Gold) audio devices. @@ -71,6 +99,7 @@ config SND_RME96 tristate "RME Digi96, 96/8, 96/8 PRO" depends on SND + select SND_PCM help Say 'Y' or 'M' to include support for RME Digi96, Digi96/8 and Digi96/8 PRO/PAD/PST. @@ -78,6 +107,7 @@ config SND_RME9652 tristate "RME Digi9652 (Hammerfall)" depends on SND + select SND_PCM help Say 'Y' or 'M' to include support for RME Hammerfall (RME Digi9652 / Digi9636) soundcards. @@ -85,6 +115,9 @@ config SND_HDSP tristate "RME Hammerfall DSP Audio" depends on SND + select SND_HWDEP + select SND_RAWMIDI + select SND_PCM help Say 'Y' or 'M' to include support for RME Hammerfall DSP Audio soundcards. @@ -92,6 +125,8 @@ config SND_TRIDENT tristate "Trident 4D-Wave DX/NX; SiS 7018" depends on SND + select SND_MPU401_UART + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for Trident 4D-Wave DX/NX and SiS 7018 soundcards. @@ -99,6 +134,9 @@ config SND_YMFPCI tristate "Yamaha YMF724/740/744/754" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for Yamaha PCI audio chips - YMF724, YMF724F, YMF740, YMF740C, YMF744, YMF754. @@ -106,12 +144,18 @@ config SND_ALS4000 tristate "Avance Logic ALS4000" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for Avance Logic ALS4000. config SND_CMIPCI tristate "C-Media 8738, 8338" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_PCM help Say 'Y' or 'M' to include support for C-Media CMI8338 and 8738 PCI soundcards. @@ -119,12 +163,16 @@ config SND_ENS1370 tristate "(Creative) Ensoniq AudioPCI 1370" depends on SND + select SND_RAWMIDI + select SND_PCM help Say 'Y' or 'M' to include support for Ensoniq AudioPCI ES1370. config SND_ENS1371 tristate "(Creative) Ensoniq AudioPCI 1371/1373" depends on SND + select SND_RAWMIDI + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for Ensoniq AudioPCI ES1371 and Sound Blaster PCI 64 or 128 soundcards. @@ -132,6 +180,9 @@ config SND_ES1938 tristate "ESS ES1938/1946/1969 (Solo-1)" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for ESS Solo-1 (ES1938, ES1946, ES1969) soundcard. @@ -139,24 +190,31 @@ config SND_ES1968 tristate "ESS ES1968/1978 (Maestro-1/2/2E)" depends on SND + select SND_MPU401_UART + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for ESS Maestro 1/2/2E. config SND_MAESTRO3 tristate "ESS Allegro/Maestro3" depends on SND + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for ESS Maestro 3 (Allegro) soundcard. config SND_FM801 tristate "ForteMedia FM801" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for ForteMedia FM801 based soundcards. -config CONFIG_SND_FM801_TEA575X +config SND_FM801_TEA575X tristate "ForteMedia FM801 + TEA5757 tuner" - depends on SND_FM801 && CONFIG_VIDEO_DEV + depends on SND_FM801 + select 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). @@ -164,6 +222,8 @@ config SND_ICE1712 tristate "ICEnsemble ICE1712 (Envy24)" depends on SND + select SND_MPU401_UART + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for ICE1712 (Envy24) based soundcards. Currently supported hardware is: MidiMan M Audio - Delta 1010(LT), Dio 2496, @@ -173,6 +233,8 @@ config SND_ICE1724 tristate "ICE/VT1724 (Envy24HT)" depends on SND + select SND_MPU401_UART + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for ICE/VT1724 (Envy24HT) based soundcards. @@ -182,6 +244,8 @@ config SND_INTEL8X0 tristate "Intel i8x0/MX440, SiS 7012; Ali 5455; NForce Audio; AMD768/8111" depends on SND + select SND_MPU401_UART + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for Intel8x0 based soundcards, SiS 7012, AMD768/8111, NVidia NForce and ALi 5455 chips. @@ -189,18 +253,24 @@ config SND_SONICVIBES tristate "S3 SonicVibes" depends on SND + select SND_OPL3_LIB + select SND_MPU401_UART + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for S3 SonicVibes based soundcards. config SND_VIA82XX tristate "VIA 82C686A/B, 8233 South Bridge" depends on SND + select SND_MPU401_UART + select SND_AC97_CODEC help Say 'Y' or 'M' to include support for VIA VT82C686A/B, VT8233 South Bridge. config SND_VX222 tristate "Digigram VX222" depends on SND + select SND_VX_LIB help Say 'Y' or 'M' to include support for Digigram VX222 soundcards. diff -Nru a/sound/pci/Makefile b/sound/pci/Makefile --- a/sound/pci/Makefile Sun Mar 7 18:45:35 2004 +++ b/sound/pci/Makefile Sun Mar 7 18:45:35 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/Makefile b/sound/pci/ac97/Makefile --- a/sound/pci/ac97/Makefile Sun Mar 7 18:45:35 2004 +++ b/sound/pci/ac97/Makefile Sun Mar 7 18:45:35 2004 @@ -7,21 +7,7 @@ snd-ak4531-codec-objs := ak4531_codec.o # Toplevel Module Dependency -obj-$(CONFIG_SND_CS4281) += snd-ac97-codec.o +obj-$(CONFIG_SND_AC97_CODEC) += snd-ac97-codec.o obj-$(CONFIG_SND_ENS1370) += snd-ak4531-codec.o -obj-$(CONFIG_SND_ENS1371) += snd-ac97-codec.o -obj-$(CONFIG_SND_ES1968) += snd-ac97-codec.o -obj-$(CONFIG_SND_FM801) += snd-ac97-codec.o -obj-$(CONFIG_SND_ICE1712) += snd-ac97-codec.o -obj-$(CONFIG_SND_ICE1724) += snd-ac97-codec.o -obj-$(CONFIG_SND_INTEL8X0) += snd-ac97-codec.o -obj-$(CONFIG_SND_MAESTRO3) += snd-ac97-codec.o -obj-$(CONFIG_SND_VIA82XX) += snd-ac97-codec.o -obj-$(CONFIG_SND_ALI5451) += snd-ac97-codec.o -obj-$(CONFIG_SND_CS46XX) += snd-ac97-codec.o -obj-$(CONFIG_SND_EMU10K1) += snd-ac97-codec.o -obj-$(CONFIG_SND_NM256) += snd-ac97-codec.o -obj-$(CONFIG_SND_TRIDENT) += snd-ac97-codec.o -obj-$(CONFIG_SND_YMFPCI) += snd-ac97-codec.o obj-m := $(sort $(obj-m)) diff -Nru a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c --- a/sound/pci/ac97/ac97_codec.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/ac97/ac97_codec.c Sun Mar 7 18:45:35 2004 @@ -100,13 +100,12 @@ { 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 }, { 0x41445375, 0xffffffff, "AD1985", patch_ad1985, NULL }, -{ 0x414c4300, 0xfffffff0, "RL5306", NULL, NULL }, -{ 0x414c4310, 0xfffffff0, "RL5382", NULL, NULL }, -{ 0x414c4320, 0xfffffff0, "RL5383", NULL, NULL }, +{ 0x414c4300, 0xffffff00, "ALC100/100P", NULL, NULL }, { 0x414c4710, 0xfffffff0, "ALC200/200P", NULL, NULL }, { 0x414c4720, 0xfffffff0, "ALC650", patch_alc650, NULL }, { 0x414c4721, 0xfffffff0, "ALC650D", patch_alc650, NULL }, @@ -273,6 +272,11 @@ { if (!snd_ac97_valid_reg(ac97, reg)) return; + if ((ac97->id & 0xffffff00) == 0x414c4300) { + /* Fix H/W bug of ALC100/100P */ + if (reg == AC97_MASTER || reg == AC97_HEADPHONE) + ac97->bus->write(ac97, AC97_RESET, 0); /* reset audio codec */ + } ac97->bus->write(ac97, reg, value); } @@ -688,7 +692,7 @@ }; static const snd_kcontrol_new_t snd_ac97_control_eapd = -AC97_SINGLE("External Amplifier Power Down", AC97_POWERDOWN, 15, 1, 0); +AC97_SINGLE("External Amplifier", AC97_POWERDOWN, 15, 1, 1); static int snd_ac97_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { @@ -1526,38 +1530,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 +1872,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 +1890,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 */ @@ -1937,6 +1943,15 @@ return -ENOMEM; } } + /* make sure the proper powerdown bits are cleared */ + if (ac97->scaps) { + reg = snd_ac97_read(ac97, AC97_EXTENDED_ID); + if (ac97->scaps & AC97_SCAP_SURROUND_DAC) + reg &= ~AC97_EA_PRJ; + if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) + reg &= ~(AC97_EA_PRI | AC97_EA_PRK); + snd_ac97_write_cache(ac97, AC97_EXTENDED_ID, reg); + } snd_ac97_proc_init(ac97); if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ac97, &ops)) < 0) { snd_ac97_free(ac97); @@ -1991,11 +2006,18 @@ snd_ac97_write(ac97, AC97_GENERAL_PURPOSE, 0); snd_ac97_write(ac97, AC97_POWERDOWN, ac97->regs[AC97_POWERDOWN]); - snd_ac97_write(ac97, AC97_MASTER, 0x8101); + ac97->bus->write(ac97, AC97_MASTER, 0x8101); for (i = 0; i < 10; i++) { if (snd_ac97_read(ac97, AC97_MASTER) == 0x8101) break; - mdelay(1); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + /* FIXME: extra delay */ + ac97->bus->write(ac97, AC97_MASTER, 0x8000); + if (snd_ac97_read(ac97, AC97_MASTER) != 0x8000) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/4); } __reset_ready: @@ -2112,6 +2134,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 { @@ -2134,12 +2158,30 @@ static int tune_ad_sharing(ac97_t *ac97) { unsigned short scfg; + if ((ac97->id & 0xffffff00) != 0x41445300) { + snd_printk(KERN_ERR "ac97_quirk AD_SHARING is only for AD codecs\n"); + return -EINVAL; + } /* Turn on OMS bit to route microphone to back panel */ scfg = snd_ac97_read(ac97, AC97_AD_SERIAL_CFG); snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, scfg | 0x0200); return 0; } +static const snd_kcontrol_new_t snd_ac97_alc_jack_detect = +AC97_SINGLE("Jack Detect", AC97_ALC650_CLOCK, 5, 1, 0); + +static int tune_alc_jack(ac97_t *ac97) +{ + if ((ac97->id & 0xffffff00) != 0x414c4700) { + snd_printk(KERN_ERR "ac97_quirk ALC_JACK is only for Realtek codecs\n"); + return -EINVAL; + } + snd_ac97_update_bits(ac97, 0x7a, 0x20, 0x20); /* select jack detect function */ + snd_ac97_update_bits(ac97, 0x7a, 0x01, 0x01); /* Line-out auto mute */ + return snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&snd_ac97_alc_jack_detect, ac97)); +} + static int apply_quirk(ac97_t *ac97, int quirk) { switch (quirk) { @@ -2153,6 +2195,8 @@ return swap_surround(ac97); case AC97_TUNE_AD_SHARING: return tune_ad_sharing(ac97); + case AC97_TUNE_ALC_JACK: + return tune_alc_jack(ac97); } return -EINVAL; } diff -Nru a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c --- a/sound/pci/ac97/ac97_patch.c Sun Mar 7 18:45:36 2004 +++ b/sound/pci/ac97/ac97_patch.c Sun Mar 7 18:45:36 2004 @@ -292,6 +292,9 @@ return 0; } +/* + * Tritech codec + */ int patch_tritech_tr28028(ac97_t * ac97) { snd_ac97_write_cache(ac97, 0x26, 0x0300); @@ -301,6 +304,9 @@ return 0; } +/* + * Sigmatel STAC97xx codecs + */ static int patch_sigmatel_stac9700_3d(ac97_t * ac97) { snd_kcontrol_t *kctl; @@ -441,6 +447,9 @@ return 0; } +/* + * Cirrus Logic CS42xx codecs + */ static const snd_kcontrol_new_t snd_ac97_cirrus_controls_spdif[2] = { AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), AC97_CSR_SPDIF, 15, 1, 0), AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "AC97-SPSA", AC97_CSR_ACMODE, 0, 3, 0) @@ -499,6 +508,9 @@ return patch_cirrus_spdif(ac97); } +/* + * Conexant codecs + */ static const snd_kcontrol_new_t snd_ac97_conexant_controls_spdif[1] = { AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), AC97_CXR_AUDIO_MISC, 3, 1, 0), }; @@ -530,6 +542,9 @@ return 0; } +/* + * Analog Device AD18xx, AD19xx codecs + */ int patch_ad1819(ac97_t * ac97) { // patch for Analog Devices @@ -652,7 +667,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 +697,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 +817,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 +826,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 +836,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 +847,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 +860,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 +873,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 +889,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 +944,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) @@ -972,6 +1007,36 @@ return 0; } +/* + * realtek ALC65x codecs + */ +static int snd_ac97_alc650_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_alc650_mic_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int change, val; + val = !!(snd_ac97_read(ac97, AC97_ALC650_MULTICH) & (1 << 10)); + change = (ucontrol->value.integer.value[0] != val); + if (change) { + /* disable/enable vref */ + snd_ac97_update_bits(ac97, AC97_ALC650_CLOCK, 1 << 12, + ucontrol->value.integer.value[0] ? (1 << 12) : 0); + /* turn on/off center-on-mic */ + snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 10, + ucontrol->value.integer.value[0] ? (1 << 10) : 0); + /* GPIO0 high for mic */ + snd_ac97_update_bits(ac97, AC97_ALC650_GPIO_STATUS, 0x100, + ucontrol->value.integer.value[0] ? 0 : 0x100); + } + return change; +} + static const snd_kcontrol_new_t snd_ac97_controls_alc650[] = { AC97_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0), AC97_SINGLE("Surround Down Mix", AC97_ALC650_MULTICH, 1, 1, 0), @@ -994,42 +1059,13 @@ AC97_SINGLE("Center/LFE DAC Switch", AC97_ALC650_LFE_DAC_VOL, 15, 1, 1), AC97_DOUBLE("Center/LFE DAC Volume", AC97_ALC650_LFE_DAC_VOL, 8, 0, 31, 1), #endif -}; - -static const snd_kcontrol_new_t snd_ac97_control_alc650_mic = -AC97_SINGLE("Mic As Center/LFE", AC97_ALC650_MULTICH, 10, 1, 0); - -static int snd_ac97_alc650_mic_gpio_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_alc650_mic_gpio_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) -{ - ac97_t *ac97 = snd_kcontrol_chip(kcontrol); - int change; - change = snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 10, - 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); - /* GPIO0 high for mic */ - snd_ac97_update_bits(ac97, 0x78, 0x100, - ucontrol->value.integer.value[0] ? 0 : 0x100); - } - return change; -} - -static const snd_kcontrol_new_t snd_ac97_control_alc650_mic_gpio = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Mic As Center/LFE", - .info = snd_ac97_info_single, - .get = snd_ac97_alc650_mic_gpio_get, - .put = snd_ac97_alc650_mic_gpio_put, - .private_value = (1 << 16), /* for info */ + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Mic As Center/LFE", + .info = snd_ac97_info_single, + .get = snd_ac97_alc650_mic_get, + .put = snd_ac97_alc650_mic_put, + }, }; static const snd_kcontrol_new_t snd_ac97_spdif_controls_alc650[] = { @@ -1044,11 +1080,6 @@ if ((err = patch_build_controls(ac97, snd_ac97_controls_alc650, ARRAY_SIZE(snd_ac97_controls_alc650))) < 0) return err; - if ((err = patch_build_controls(ac97, - ac97->spec.dev_flags ? - &snd_ac97_control_alc650_mic : - &snd_ac97_control_alc650_mic_gpio, 1)) < 0) - return err; if (ac97->ext_id & AC97_EI_SPDIF) { if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc650, ARRAY_SIZE(snd_ac97_spdif_controls_alc650))) < 0) return err; @@ -1076,34 +1107,25 @@ snd_ac97_read(ac97, AC97_ALC650_GPIO_STATUS) | 0x8000); /* Enable SPDIF-IN only on Rev.E and above */ - if (ac97->spec.dev_flags) { - /* enable spdif in */ - snd_ac97_write_cache(ac97, AC97_ALC650_CLOCK, - snd_ac97_read(ac97, AC97_ALC650_CLOCK) | 0x03); - } + val = snd_ac97_read(ac97, AC97_ALC650_CLOCK); + /* SPDIF IN with pin 47 */ + if (ac97->spec.dev_flags) + val |= 0x03; /* enable */ + else + val &= ~0x03; /* disable */ + snd_ac97_write_cache(ac97, AC97_ALC650_CLOCK, val); val = snd_ac97_read(ac97, AC97_ALC650_MULTICH); val &= ~0xc000; /* slot: 3,4,7,8,6,9 */ + val &= ~(1 << 10); /* center-on-mic off */ snd_ac97_write_cache(ac97, AC97_ALC650_MULTICH, val); - if (! ac97->spec.dev_flags) { - /* set GPIO */ - int mic_off; - 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; - snd_ac97_write_cache(ac97, AC97_ALC650_GPIO_SETUP, val); - val = snd_ac97_read(ac97, AC97_ALC650_GPIO_STATUS); - if (mic_off) - val &= ~0x100; - else - val = val | 0x100; - snd_ac97_write_cache(ac97, AC97_ALC650_GPIO_STATUS, val); - } + /* set GPIO0 for mic bias */ + /* GPIO0 pin output, no interrupt, high */ + snd_ac97_write_cache(ac97, AC97_ALC650_GPIO_SETUP, + snd_ac97_read(ac97, AC97_ALC650_GPIO_SETUP) | 0x01); + snd_ac97_write_cache(ac97, AC97_ALC650_GPIO_STATUS, + (snd_ac97_read(ac97, AC97_ALC650_GPIO_STATUS) | 0x100) & ~0x10); /* full DAC volume */ snd_ac97_write_cache(ac97, AC97_ALC650_SURR_DAC_VOL, 0x0808); @@ -1111,10 +1133,37 @@ 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; + + /* misc control; vrefout disable */ + snd_ac97_update_bits(ac97, AC97_ALC650_CLOCK, 1 << 12, + 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 +1236,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); @@ -1203,6 +1258,9 @@ return 0; } +/* + * C-Media CM97xx codecs + */ static const snd_kcontrol_new_t snd_ac97_cm9738_controls[] = { AC97_SINGLE("Line-In As Surround", AC97_CM9738_VENDOR_CTRL, 10, 1, 0), AC97_SINGLE("Duplicate Front", AC97_CM9738_VENDOR_CTRL, 13, 1, 0), @@ -1312,7 +1370,10 @@ /* bit 13: enable internal vref output for mic */ /* bit 12: enable center/lfe */ /* bit 14: 0 = SPDIF, 1 = EAPD */ - snd_ac97_write_cache(ac97, AC97_CM9739_MULTI_CHAN, 0x3000); + val = (1 << 12) | (1 << 13); + if (! (ac97->ext_id & AC97_EI_SPDIF)) + val |= (1 << 14); + snd_ac97_write_cache(ac97, AC97_CM9739_MULTI_CHAN, val); /* FIXME: set up GPIO */ snd_ac97_write_cache(ac97, 0x70, 0x0100); @@ -1321,6 +1382,9 @@ return 0; } +/* + * VIA VT1616 codec + */ static const snd_kcontrol_new_t snd_ac97_controls_vt1616[] = { AC97_SINGLE("DC Offset removal", 0x5a, 10, 1, 0), AC97_SINGLE("Alternate Level to Surround Out", 0x5a, 15, 1, 0), diff -Nru a/sound/pci/ac97/ac97_patch.h b/sound/pci/ac97/ac97_patch.h --- a/sound/pci/ac97/ac97_patch.h Sun Mar 7 18:45:35 2004 +++ b/sound/pci/ac97/ac97_patch.h Sun Mar 7 18:45:35 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 Mar 7 18:45:35 2004 +++ b/sound/pci/ac97/ac97_pcm.c Sun Mar 7 18:45:35 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 Mar 7 18:45:35 2004 +++ b/sound/pci/ac97/ak4531_codec.c Sun Mar 7 18:45:35 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/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c --- a/sound/pci/ali5451/ali5451.c Sun Mar 7 18:45:36 2004 +++ b/sound/pci/ali5451/ali5451.c Sun Mar 7 18:45:36 2004 @@ -1742,7 +1742,8 @@ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ali_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ali_capture_ops); - snd_pcm_lib_preallocate_pci_pages_for_all(codec->pci, pcm, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(codec->pci), 64*1024, 128*1024); pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; diff -Nru a/sound/pci/als4000.c b/sound/pci/als4000.c --- a/sound/pci/als4000.c Sun Mar 7 18:45:36 2004 +++ b/sound/pci/als4000.c Sun Mar 7 18:45:36 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 @@ -521,7 +523,8 @@ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_als4000_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_als4000_capture_ops); - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), + 64*1024, 64*1024); chip->pcm = pcm; diff -Nru a/sound/pci/azt3328.c b/sound/pci/azt3328.c --- a/sound/pci/azt3328.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/azt3328.c Sun Mar 7 18:45:35 2004 @@ -1253,7 +1253,8 @@ strcpy(pcm->name, chip->card->shortname); chip->pcm = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 64*1024); snd_azf3328_dbgcallleave(); return 0; diff -Nru a/sound/pci/bt87x.c b/sound/pci/bt87x.c --- a/sound/pci/bt87x.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/bt87x.c Sun Mar 7 18:45:35 2004 @@ -167,8 +167,8 @@ long opened; snd_pcm_substream_t *substream; - u32 *risc; - dma_addr_t risc_dma; + struct snd_dma_device dma_dev; + struct snd_dma_buffer dma_risc; unsigned int line_bytes; unsigned int lines; @@ -195,13 +195,14 @@ unsigned int i, offset; u32 *risc; - if (!chip->risc) { - chip->risc = (u32*)snd_malloc_pci_pages - (chip->pci, PAGE_ALIGN(MAX_RISC_SIZE), &chip->risc_dma); - if (!chip->risc) + if (chip->dma_risc.area == NULL) { + memset(&chip->dma_dev, 0, sizeof(chip->dma_dev)); + chip->dma_dev.type = SNDRV_DMA_TYPE_DEV; + chip->dma_dev.dev = snd_dma_pci_data(chip->pci); + if (snd_dma_alloc_pages(&chip->dma_dev, PAGE_ALIGN(MAX_RISC_SIZE), &chip->dma_risc) < 0) return -ENOMEM; } - risc = chip->risc; + risc = (u32 *)chip->dma_risc.area; offset = 0; *risc++ = cpu_to_le32(RISC_SYNC | RISC_SYNC_FM1); *risc++ = cpu_to_le32(0); @@ -233,7 +234,7 @@ *risc++ = cpu_to_le32(RISC_SYNC | RISC_SYNC_VRO); *risc++ = cpu_to_le32(0); *risc++ = cpu_to_le32(RISC_JUMP); - *risc++ = cpu_to_le32(chip->risc_dma); + *risc++ = cpu_to_le32(chip->dma_risc.addr); chip->line_bytes = period_bytes; chip->lines = periods; return 0; @@ -241,10 +242,9 @@ static void snd_bt87x_free_risc(bt87x_t *chip) { - if (chip->risc) { - snd_free_pci_pages(chip->pci, PAGE_ALIGN(MAX_RISC_SIZE), - chip->risc, chip->risc_dma); - chip->risc = NULL; + if (chip->dma_risc.area) { + snd_dma_free_pages(&chip->dma_dev, &chip->dma_risc); + chip->dma_risc.area = NULL; } } @@ -459,7 +459,7 @@ spin_lock_irqsave(&chip->reg_lock, flags); chip->current_line = 0; chip->reg_control |= CTL_FIFO_ENABLE | CTL_RISC_ENABLE | CTL_ACAP_EN; - snd_bt87x_writel(chip, REG_RISC_STRT_ADD, chip->risc_dma); + snd_bt87x_writel(chip, REG_RISC_STRT_ADD, chip->dma_risc.addr); snd_bt87x_writel(chip, REG_PACKET_LEN, chip->line_bytes | (chip->lines << 16)); snd_bt87x_writel(chip, REG_INT_MASK, MY_INTERRUPTS); @@ -681,7 +681,9 @@ pcm->private_data = chip; strcpy(pcm->name, name); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_bt87x_pcm_ops); - return snd_pcm_lib_preallocate_sg_pages_for_all(chip->pci, pcm, + return snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(chip->pci), 128 * 1024, (255 * 4092 + 1023) & ~1023); } diff -Nru a/sound/pci/cmipci.c b/sound/pci/cmipci.c --- a/sound/pci/cmipci.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/cmipci.c Sun Mar 7 18:45:35 2004 @@ -2087,7 +2087,8 @@ strcpy(pcm->name, "C-Media PCI DAC/ADC"); cm->pcm = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(cm->pci, pcm, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(cm->pci), 64*1024, 128*1024); return 0; } @@ -2109,7 +2110,8 @@ strcpy(pcm->name, "C-Media PCI 2nd DAC"); cm->pcm2 = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(cm->pci, pcm, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(cm->pci), 64*1024, 128*1024); return 0; } @@ -2139,7 +2141,8 @@ strcpy(pcm->name, "C-Media PCI IEC958"); cm->pcm_spdif = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(cm->pci, pcm, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(cm->pci), 64*1024, 128*1024); return 0; } @@ -3151,7 +3154,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/cs4281.c b/sound/pci/cs4281.c --- a/sound/pci/cs4281.c Sun Mar 7 18:45:36 2004 +++ b/sound/pci/cs4281.c Sun Mar 7 18:45:36 2004 @@ -1039,7 +1039,8 @@ strcpy(pcm->name, "CS4281"); chip->pcm = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 512*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 512*1024); if (rpcm) *rpcm = pcm; diff -Nru a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c --- a/sound/pci/cs46xx/cs46xx_lib.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/cs46xx/cs46xx_lib.c Sun Mar 7 18:45:35 2004 @@ -66,6 +66,8 @@ #include "cs46xx_lib.h" #include "dsp_spos.h" +static void amp_voyetra(cs46xx_t *chip, int change); + static unsigned short snd_cs46xx_codec_read(cs46xx_t *chip, unsigned short reg, int codec_index) @@ -203,6 +205,13 @@ chip->active_ctrl(chip, 1); val = snd_cs46xx_codec_read(chip, reg, codec_index); chip->active_ctrl(chip, -1); + + /* HACK: voyetra uses EAPD bit in the reverse way. + * we flip the bit to show the mixer status correctly + */ + if (reg == AC97_POWERDOWN && chip->amplifier_ctrl == amp_voyetra) + val ^= 0x8000; + return val; } @@ -284,6 +293,12 @@ else snd_assert(0,return); + /* HACK: voyetra uses EAPD bit in the reverse way. + * we flip the bit to show the mixer status correctly + */ + if (reg == AC97_POWERDOWN && chip->amplifier_ctrl == amp_voyetra) + val ^= 0x8000; + chip->active_ctrl(chip, 1); snd_cs46xx_codec_write(chip, reg, val, codec_index); chip->active_ctrl(chip, -1); @@ -699,7 +714,7 @@ bytes = hw_to_end; if (sw_to_end < bytes) bytes = sw_to_end; - memcpy(cpcm->hw_area + cpcm->hw_data, + memcpy(cpcm->hw_buf.area + cpcm->hw_data, runtime->dma_area + cpcm->sw_data, bytes); cpcm->hw_data += bytes; @@ -740,7 +755,7 @@ if (sw_to_end < bytes) bytes = sw_to_end; memcpy(runtime->dma_area + chip->capt.sw_data, - chip->capt.hw_area + chip->capt.hw_data, + chip->capt.hw_buf.area + chip->capt.hw_data, bytes); chip->capt.hw_data += bytes; if ((int)chip->capt.hw_data == buffer_size) @@ -766,7 +781,7 @@ #else ptr = snd_cs46xx_peek(chip, BA1_PBA); #endif - ptr -= cpcm->hw_addr; + ptr -= cpcm->hw_buf.addr; return ptr >> cpcm->shift; } @@ -784,7 +799,7 @@ #else ptr = snd_cs46xx_peek(chip, BA1_PBA); #endif - ptr -= cpcm->hw_addr; + ptr -= cpcm->hw_buf.addr; bytes = ptr - cpcm->hw_io; @@ -802,14 +817,14 @@ static snd_pcm_uframes_t snd_cs46xx_capture_direct_pointer(snd_pcm_substream_t * substream) { cs46xx_t *chip = snd_pcm_substream_chip(substream); - size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_addr; + size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_buf.addr; return ptr >> chip->capt.shift; } static snd_pcm_uframes_t snd_cs46xx_capture_indirect_pointer(snd_pcm_substream_t * substream) { cs46xx_t *chip = snd_pcm_substream_chip(substream); - size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_addr; + size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_buf.addr; ssize_t bytes = ptr - chip->capt.hw_io; int buffer_size = substream->runtime->period_size * CS46XX_FRAGS << chip->capt.shift; @@ -933,7 +948,7 @@ /* If PCMReaderSCB and SrcTaskSCB not created yet ... */ if ( cpcm->pcm_channel == NULL) { cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip, sample_rate, - cpcm, cpcm->hw_addr,cpcm->pcm_channel_id); + cpcm, cpcm->hw_buf.addr,cpcm->pcm_channel_id); if (cpcm->pcm_channel == NULL) { snd_printk(KERN_ERR "cs46xx: failed to create virtual PCM channel\n"); return -ENOMEM; @@ -946,7 +961,7 @@ cs46xx_dsp_destroy_pcm_channel (chip,cpcm->pcm_channel); if ( (cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip, sample_rate, cpcm, - cpcm->hw_addr, + cpcm->hw_buf.addr, cpcm->pcm_channel_id)) == NULL) { snd_printk(KERN_ERR "cs46xx: failed to re-create virtual PCM channel\n"); return -ENOMEM; @@ -1002,11 +1017,11 @@ #endif if (params_periods(hw_params) == CS46XX_FRAGS) { - if (runtime->dma_area != cpcm->hw_area) + if (runtime->dma_area != cpcm->hw_buf.area) snd_pcm_lib_free_pages(substream); - runtime->dma_area = cpcm->hw_area; - runtime->dma_addr = cpcm->hw_addr; - runtime->dma_bytes = cpcm->hw_size; + runtime->dma_area = cpcm->hw_buf.area; + runtime->dma_addr = cpcm->hw_buf.addr; + runtime->dma_bytes = cpcm->hw_buf.bytes; #ifdef CONFIG_SND_CS46XX_NEW_DSP @@ -1026,7 +1041,7 @@ #endif } else { - if (runtime->dma_area == cpcm->hw_area) { + if (runtime->dma_area == cpcm->hw_buf.area) { runtime->dma_area = NULL; runtime->dma_addr = 0; runtime->dma_bytes = 0; @@ -1075,7 +1090,7 @@ is called and cpcm can actually be NULL here */ if (!cpcm) return -ENXIO; - if (runtime->dma_area != cpcm->hw_area) + if (runtime->dma_area != cpcm->hw_buf.area) snd_pcm_lib_free_pages(substream); runtime->dma_area = NULL; @@ -1144,7 +1159,7 @@ /* playback format && interrupt enable */ snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 1) << 2, pfie | cpcm->pcm_channel->pcm_slot); #else - snd_cs46xx_poke(chip, BA1_PBA, cpcm->hw_addr); + snd_cs46xx_poke(chip, BA1_PBA, cpcm->hw_buf.addr); tmp = snd_cs46xx_peek(chip, BA1_PDTC); tmp &= ~0x000003ff; tmp |= (4 << cpcm->shift) - 1; @@ -1167,14 +1182,14 @@ cs46xx_dsp_pcm_ostream_set_period (chip, params_period_bytes(hw_params)); #endif if (runtime->periods == CS46XX_FRAGS) { - if (runtime->dma_area != chip->capt.hw_area) + if (runtime->dma_area != chip->capt.hw_buf.area) snd_pcm_lib_free_pages(substream); - runtime->dma_area = chip->capt.hw_area; - runtime->dma_addr = chip->capt.hw_addr; - runtime->dma_bytes = chip->capt.hw_size; + runtime->dma_area = chip->capt.hw_buf.area; + runtime->dma_addr = chip->capt.hw_buf.addr; + runtime->dma_bytes = chip->capt.hw_buf.bytes; substream->ops = &snd_cs46xx_capture_ops; } else { - if (runtime->dma_area == chip->capt.hw_area) { + if (runtime->dma_area == chip->capt.hw_buf.area) { runtime->dma_area = NULL; runtime->dma_addr = 0; runtime->dma_bytes = 0; @@ -1192,7 +1207,7 @@ cs46xx_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - if (runtime->dma_area != chip->capt.hw_area) + if (runtime->dma_area != chip->capt.hw_buf.area) snd_pcm_lib_free_pages(substream); runtime->dma_area = NULL; runtime->dma_addr = 0; @@ -1206,7 +1221,7 @@ cs46xx_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - snd_cs46xx_poke(chip, BA1_CBA, chip->capt.hw_addr); + snd_cs46xx_poke(chip, BA1_CBA, chip->capt.hw_buf.addr); chip->capt.shift = 2; chip->capt.sw_bufsize = snd_pcm_lib_buffer_bytes(substream); chip->capt.sw_data = chip->capt.sw_io = chip->capt.sw_ready = 0; @@ -1382,8 +1397,7 @@ cpcm = snd_magic_kcalloc(cs46xx_pcm_t, 0, GFP_KERNEL); if (cpcm == NULL) return -ENOMEM; - cpcm->hw_size = PAGE_SIZE; - if ((cpcm->hw_area = snd_malloc_pci_pages(chip->pci, cpcm->hw_size, &cpcm->hw_addr)) == NULL) { + if (snd_dma_alloc_pages(&chip->dma_dev, PAGE_SIZE, &cpcm->hw_buf) < 0) { snd_magic_kfree(cpcm); return -ENOMEM; } @@ -1472,7 +1486,7 @@ { cs46xx_t *chip = snd_pcm_substream_chip(substream); - if ((chip->capt.hw_area = snd_malloc_pci_pages(chip->pci, chip->capt.hw_size, &chip->capt.hw_addr)) == NULL) + if (snd_dma_alloc_pages(&chip->dma_dev, PAGE_SIZE, &chip->capt.hw_buf) < 0) return -ENOMEM; chip->capt.substream = substream; substream->runtime->hw = snd_cs46xx_capture; @@ -1513,7 +1527,7 @@ #endif cpcm->substream = NULL; - snd_free_pci_pages(chip->pci, cpcm->hw_size, cpcm->hw_area, cpcm->hw_addr); + snd_dma_free_pages(&chip->dma_dev, &cpcm->hw_buf); chip->active_ctrl(chip, -1); return 0; @@ -1524,7 +1538,7 @@ cs46xx_t *chip = snd_pcm_substream_chip(substream); chip->capt.substream = NULL; - snd_free_pci_pages(chip->pci, chip->capt.hw_size, chip->capt.hw_area, chip->capt.hw_addr); + snd_dma_free_pages(&chip->dma_dev, &chip->capt.hw_buf); chip->active_ctrl(chip, -1); return 0; @@ -1703,7 +1717,8 @@ strcpy(pcm->name, "CS46xx"); chip->pcm = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 256*1024); if (rpcm) *rpcm = pcm; @@ -1734,7 +1749,8 @@ strcpy(pcm->name, "CS46xx - Rear"); chip->pcm_rear = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 256*1024); if (rpcm) *rpcm = pcm; @@ -1763,7 +1779,8 @@ strcpy(pcm->name, "CS46xx - Center LFE"); chip->pcm_center_lfe = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 256*1024); if (rpcm) *rpcm = pcm; @@ -1792,7 +1809,8 @@ strcpy(pcm->name, "CS46xx - IEC958"); chip->pcm_rear = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 256*1024); if (rpcm) *rpcm = pcm; @@ -2547,7 +2565,7 @@ /* get EAPD mixer switch (for voyetra hack) */ memset(&id, 0, sizeof(id)); id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - strcpy(id.name, "External Amplifier Power Down"); + strcpy(id.name, "External Amplifier"); chip->eapd_switch = snd_ctl_find_id(chip->card, &id); #ifdef CONFIG_SND_CS46XX_NEW_DSP @@ -3877,7 +3895,6 @@ #endif chip->card = card; chip->pci = pci; - chip->capt.hw_size = PAGE_SIZE; chip->irq = -1; chip->ba0_addr = pci_resource_start(pci, 0); chip->ba1_addr = pci_resource_start(pci, 1); @@ -3912,6 +3929,10 @@ strcpy(region->name, "CS46xx_BA1_reg"); region->base = chip->ba1_addr + BA1_SP_REG; region->size = CS46XX_BA1_REG_SIZE; + + memset(&chip->dma_dev, 0, sizeof(chip->dma_dev)); + chip->dma_dev.type = SNDRV_DMA_TYPE_DEV; + chip->dma_dev.dev = snd_dma_pci_data(pci); /* set up amp and clkrun hack */ pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &ss_vendor); diff -Nru a/sound/pci/emu10k1/emu10k1_callback.c b/sound/pci/emu10k1/emu10k1_callback.c --- a/sound/pci/emu10k1/emu10k1_callback.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/emu10k1/emu10k1_callback.c Sun Mar 7 18:45:35 2004 @@ -405,7 +405,7 @@ snd_emu10k1_ptr_write(hw, Z2, ch, 0); /* invalidate maps */ - temp = (hw->silent_page_dmaaddr << 1) | MAP_PTI_MASK; + temp = (hw->silent_page.addr << 1) | MAP_PTI_MASK; snd_emu10k1_ptr_write(hw, MAPA, ch, temp); snd_emu10k1_ptr_write(hw, MAPB, ch, temp); #if 0 diff -Nru a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c --- a/sound/pci/emu10k1/emu10k1_main.c Sun Mar 7 18:45:36 2004 +++ b/sound/pci/emu10k1/emu10k1_main.c Sun Mar 7 18:45:36 2004 @@ -97,7 +97,8 @@ unsigned int silent_page; emu->fx8010.itram_size = (16 * 1024)/2; - emu->fx8010.etram_size = 0; + emu->fx8010.etram_pages.area = NULL; + emu->fx8010.etram_pages.bytes = 0; /* disable audio and lock cache */ outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, emu->port + HCFG); @@ -184,15 +185,15 @@ /* * Clear page with silence & setup all pointers to this page */ - memset(emu->silent_page, 0, PAGE_SIZE); - silent_page = emu->silent_page_dmaaddr << 1; + memset(emu->silent_page.area, 0, PAGE_SIZE); + silent_page = emu->silent_page.addr << 1; for (idx = 0; idx < MAXPAGES; idx++) - emu->ptb_pages[idx] = cpu_to_le32(silent_page | idx); - snd_emu10k1_ptr_write(emu, PTB, 0, emu->ptb_pages_dmaaddr); + ((u32 *)emu->ptb_pages.area)[idx] = cpu_to_le32(silent_page | idx); + snd_emu10k1_ptr_write(emu, PTB, 0, emu->ptb_pages.addr); snd_emu10k1_ptr_write(emu, TCB, 0, 0); /* taken from original driver */ snd_emu10k1_ptr_write(emu, TCBS, 0, 4); /* taken from original driver */ - silent_page = (emu->silent_page_dmaaddr << 1) | MAP_PTI_MASK; + silent_page = (emu->silent_page.addr << 1) | MAP_PTI_MASK; for (ch = 0; ch < NUM_G; ch++) { snd_emu10k1_ptr_write(emu, MAPA, ch, silent_page); snd_emu10k1_ptr_write(emu, MAPB, ch, silent_page); @@ -546,10 +547,10 @@ } if (emu->memhdr) snd_util_memhdr_free(emu->memhdr); - if (emu->silent_page) - snd_free_pci_pages(emu->pci, EMUPAGESIZE, emu->silent_page, emu->silent_page_dmaaddr); - if (emu->ptb_pages) - snd_free_pci_pages(emu->pci, 32 * 1024, (void *)emu->ptb_pages, emu->ptb_pages_dmaaddr); + if (emu->silent_page.area) + snd_dma_free_pages(&emu->dma_dev, &emu->silent_page); + if (emu->ptb_pages.area) + snd_dma_free_pages(&emu->dma_dev, &emu->ptb_pages); if (emu->page_ptr_table) vfree(emu->page_ptr_table); if (emu->page_addr_table) @@ -638,9 +639,12 @@ } emu->irq = pci->irq; + memset(&emu->dma_dev, 0, sizeof(emu->dma_dev)); + emu->dma_dev.type = SNDRV_DMA_TYPE_DEV; + emu->dma_dev.dev = snd_dma_pci_data(pci); + emu->max_cache_pages = max_cache_bytes >> PAGE_SHIFT; - emu->ptb_pages = snd_malloc_pci_pages(pci, 32 * 1024, &emu->ptb_pages_dmaaddr); - if (emu->ptb_pages == NULL) { + if (snd_dma_alloc_pages(&emu->dma_dev, 32 * 1024, &emu->ptb_pages) < 0) { snd_emu10k1_free(emu); return -ENOMEM; } @@ -652,8 +656,7 @@ return -ENOMEM; } - emu->silent_page = snd_malloc_pci_pages(pci, EMUPAGESIZE, &emu->silent_page_dmaaddr); - if (emu->silent_page == NULL) { + if (snd_dma_alloc_pages(&emu->dma_dev, EMUPAGESIZE, &emu->silent_page) < 0) { snd_emu10k1_free(emu); return -ENOMEM; } diff -Nru a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c --- a/sound/pci/emu10k1/emufx.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/emu10k1/emufx.c Sun Mar 7 18:45:35 2004 @@ -26,6 +26,7 @@ */ #include +#include #include #include #include @@ -506,16 +507,16 @@ while (frames > *tram_pos) { count = *tram_pos + 1; - snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages + *tram_pos, - (unsigned short *)emu->fx8010.etram_pages + *tram_pos + tram_size / 2, + snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages.area + *tram_pos, + (unsigned short *)emu->fx8010.etram_pages.area + *tram_pos + tram_size / 2, src, count, *tram_shift); src += count * 2; frames -= count; *tram_pos = (tram_size / 2) - 1; (*tram_shift)++; } - snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages + *tram_pos, - (unsigned short *)emu->fx8010.etram_pages + *tram_pos + tram_size / 2, + snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages.area + *tram_pos, + (unsigned short *)emu->fx8010.etram_pages.area + *tram_pos + tram_size / 2, src, frames, *tram_shift++); *tram_pos -= frames; } @@ -760,7 +761,7 @@ strcpy(pcm->name, "EMU10K1 FX8010"); emu->pcm_fx8010 = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(emu->pci, pcm, 64*1024, 0); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 0); if (rpcm) *rpcm = pcm; @@ -2218,32 +2219,30 @@ } size = 0x2000 << size_reg; } - if (emu->fx8010.etram_size == size) + if (emu->fx8010.etram_pages.bytes == size) return 0; spin_lock_irq(&emu->emu_lock); outl(HCFG_LOCKTANKCACHE_MASK | inl(emu->port + HCFG), emu->port + HCFG); spin_unlock_irq(&emu->emu_lock); snd_emu10k1_ptr_write(emu, TCB, 0, 0); snd_emu10k1_ptr_write(emu, TCBS, 0, 0); - if (emu->fx8010.etram_pages != NULL) { - snd_free_pci_pages(emu->pci, emu->fx8010.etram_size * 2, emu->fx8010.etram_pages, emu->fx8010.etram_pages_dmaaddr); - emu->fx8010.etram_pages = NULL; - emu->fx8010.etram_size = 0; + if (emu->fx8010.etram_pages.area != NULL) { + snd_dma_free_pages(&emu->dma_dev, &emu->fx8010.etram_pages); + emu->fx8010.etram_pages.area = NULL; + emu->fx8010.etram_pages.bytes = 0; } if (size > 0) { - emu->fx8010.etram_pages = snd_malloc_pci_pages(emu->pci, size * 2, &emu->fx8010.etram_pages_dmaaddr); - if (emu->fx8010.etram_pages == NULL) + if (snd_dma_alloc_pages(&emu->dma_dev, size * 2, &emu->fx8010.etram_pages) < 0) return -ENOMEM; - memset(emu->fx8010.etram_pages, 0, size * 2); - snd_emu10k1_ptr_write(emu, TCB, 0, emu->fx8010.etram_pages_dmaaddr); + memset(emu->fx8010.etram_pages.area, 0, size * 2); + snd_emu10k1_ptr_write(emu, TCB, 0, emu->fx8010.etram_pages.addr); snd_emu10k1_ptr_write(emu, TCBS, 0, size_reg); spin_lock_irq(&emu->emu_lock); outl(inl(emu->port + HCFG) & ~HCFG_LOCKTANKCACHE_MASK, emu->port + HCFG); spin_unlock_irq(&emu->emu_lock); } - emu->fx8010.etram_size = size; return 0; } @@ -2269,7 +2268,7 @@ memset(info, 0, sizeof(info)); info->card = emu->card_type; info->internal_tram_size = emu->fx8010.itram_size; - info->external_tram_size = emu->fx8010.etram_size; + info->external_tram_size = emu->fx8010.etram_pages.bytes; fxbus = fxbuses; extin = emu->audigy ? audigy_ins : creative_ins; extout = emu->audigy ? audigy_outs : creative_outs; diff -Nru a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c --- a/sound/pci/emu10k1/emupcm.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/emu10k1/emupcm.c Sun Mar 7 18:45:35 2004 @@ -26,6 +26,7 @@ */ #include +#include #include #include #include @@ -323,7 +324,7 @@ snd_emu10k1_ptr_write(emu, Z1, voice, 0); snd_emu10k1_ptr_write(emu, Z2, voice, 0); // invalidate maps - silent_page = ((unsigned int)emu->silent_page_dmaaddr << 1) | MAP_PTI_MASK; + silent_page = ((unsigned int)emu->silent_page.addr << 1) | MAP_PTI_MASK; snd_emu10k1_ptr_write(emu, MAPA, voice, silent_page); snd_emu10k1_ptr_write(emu, MAPB, voice, silent_page); // modulation envelope @@ -998,11 +999,11 @@ emu->pcm = pcm; for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) - if ((err = snd_pcm_lib_preallocate_sg_pages(emu->pci, substream, 64*1024, 64*1024)) < 0) + if ((err = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(emu->pci), 64*1024, 64*1024)) < 0) return err; for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) - snd_pcm_lib_preallocate_pci_pages(emu->pci, substream, 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024); if (rpcm) *rpcm = pcm; @@ -1048,7 +1049,7 @@ strcpy(pcm->name, "EMU10K1 MIC"); emu->pcm_mic = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(emu->pci, pcm, 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024); if (rpcm) *rpcm = pcm; @@ -1157,7 +1158,7 @@ emu->efx_voices_mask[1] = 0; snd_ctl_add(emu->card, snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu)); - snd_pcm_lib_preallocate_pci_pages_for_all(emu->pci, pcm, 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024); return 0; } diff -Nru a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c --- a/sound/pci/emu10k1/emuproc.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/emu10k1/emuproc.c Sun Mar 7 18:45:35 2004 @@ -117,7 +117,7 @@ snd_iprintf(buffer, "Card : %s\n", emu->audigy ? "Audigy" : (emu->APS ? "EMU APS" : "Creative")); snd_iprintf(buffer, "Internal TRAM (words) : 0x%x\n", emu->fx8010.itram_size); - snd_iprintf(buffer, "External TRAM (words) : 0x%x\n", emu->fx8010.etram_size); + snd_iprintf(buffer, "External TRAM (words) : 0x%x\n", emu->fx8010.etram_pages.bytes); snd_iprintf(buffer, "\n"); if (emu->audigy) { snd_iprintf(buffer, "Effect Send Routing : A=%i, B=%i, C=%i, D=%i\n", diff -Nru a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c --- a/sound/pci/emu10k1/memory.c Sun Mar 7 18:45:36 2004 +++ b/sound/pci/emu10k1/memory.c Sun Mar 7 18:45:36 2004 @@ -30,7 +30,7 @@ * aligned pages in others */ #define __set_ptb_entry(emu,page,addr) \ - ((emu)->ptb_pages[page] = cpu_to_le32(((addr) << 1) | (page))) + (((u32 *)(emu)->ptb_pages.area)[page] = cpu_to_le32(((addr) << 1) | (page))) #define UNIT_PAGES (PAGE_SIZE / EMUPAGESIZE) #define MAX_ALIGN_PAGES (MAXPAGES / UNIT_PAGES) @@ -44,7 +44,7 @@ /* fill PTB entrie(s) corresponding to page with addr */ #define set_ptb_entry(emu,page,addr) __set_ptb_entry(emu,page,addr) /* fill PTB entrie(s) corresponding to page with silence pointer */ -#define set_silent_ptb(emu,page) __set_ptb_entry(emu,page,emu->silent_page_dmaaddr) +#define set_silent_ptb(emu,page) __set_ptb_entry(emu,page,emu->silent_page.addr) #else /* fill PTB entries -- we need to fill UNIT_PAGES entries */ static inline void set_ptb_entry(emu10k1_t *emu, int page, dma_addr_t addr) @@ -297,7 +297,6 @@ int page, err, idx; snd_assert(emu, return NULL); - snd_assert(substream->dma_device.type == SNDRV_DMA_TYPE_PCI_SG, return NULL); snd_assert(runtime->dma_bytes > 0 && runtime->dma_bytes < MAXPAGES * EMUPAGESIZE, return NULL); hdr = emu->memhdr; snd_assert(hdr, return NULL); @@ -436,22 +435,20 @@ static int synth_alloc_pages(emu10k1_t *emu, emu10k1_memblk_t *blk) { int page, first_page, last_page; - void *ptr; - dma_addr_t addr; + struct snd_dma_buffer dmab; emu10k1_memblk_init(blk); get_single_page_range(emu->memhdr, blk, &first_page, &last_page); /* allocate kernel pages */ for (page = first_page; page <= last_page; page++) { - ptr = snd_malloc_pci_page(emu->pci, &addr); - if (ptr == NULL) + if (snd_dma_alloc_pages(&emu->dma_dev, PAGE_SIZE, &dmab) < 0) goto __fail; - if (! is_valid_page(emu, addr)) { - snd_free_pci_page(emu->pci, ptr, addr); + if (! is_valid_page(emu, dmab.addr)) { + snd_dma_free_pages(&emu->dma_dev, &dmab); goto __fail; } - emu->page_addr_table[page] = addr; - emu->page_ptr_table[page] = ptr; + emu->page_addr_table[page] = dmab.addr; + emu->page_ptr_table[page] = dmab.area; } return 0; @@ -459,7 +456,10 @@ /* release allocated pages */ last_page = page - 1; for (page = first_page; page <= last_page; page++) { - snd_free_pci_page(emu->pci, emu->page_ptr_table[page], emu->page_addr_table[page]); + dmab.area = emu->page_ptr_table[page]; + dmab.addr = emu->page_addr_table[page]; + dmab.bytes = PAGE_SIZE; + snd_dma_free_pages(&emu->dma_dev, &dmab); emu->page_addr_table[page] = 0; emu->page_ptr_table[page] = NULL; } @@ -473,11 +473,16 @@ static int synth_free_pages(emu10k1_t *emu, emu10k1_memblk_t *blk) { int page, first_page, last_page; + struct snd_dma_buffer dmab; get_single_page_range(emu->memhdr, blk, &first_page, &last_page); for (page = first_page; page <= last_page; page++) { - if (emu->page_ptr_table[page]) - snd_free_pci_page(emu->pci, emu->page_ptr_table[page], emu->page_addr_table[page]); + if (emu->page_ptr_table[page] == NULL) + continue; + dmab.area = emu->page_ptr_table[page]; + dmab.addr = emu->page_addr_table[page]; + dmab.bytes = PAGE_SIZE; + snd_dma_free_pages(&emu->dma_dev, &dmab); emu->page_addr_table[page] = 0; emu->page_ptr_table[page] = NULL; } diff -Nru a/sound/pci/ens1370.c b/sound/pci/ens1370.c --- a/sound/pci/ens1370.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/ens1370.c Sun Mar 7 18:45:35 2004 @@ -434,8 +434,8 @@ unsigned int spdif_stream; #ifdef CHIP1370 - unsigned char *bugbuf; - dma_addr_t bugbuf_addr; + struct snd_dma_device dma_dev; + struct snd_dma_buffer dma_bug; #endif #ifdef SUPPORT_JOYSTICK @@ -1250,7 +1250,8 @@ #endif ensoniq->pcm1 = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(ensoniq->pci, pcm, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(ensoniq->pci), 64*1024, 128*1024); if (rpcm) *rpcm = pcm; @@ -1294,7 +1295,8 @@ #endif ensoniq->pcm2 = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(ensoniq->pci, pcm, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(ensoniq->pci), 64*1024, 128*1024); if (rpcm) *rpcm = pcm; @@ -1829,8 +1831,8 @@ pci_set_power_state(ensoniq->pci, 3); __hw_end: #ifdef CHIP1370 - if (ensoniq->bugbuf) - snd_free_pci_pages(ensoniq->pci, 16, ensoniq->bugbuf, ensoniq->bugbuf_addr); + if (ensoniq->dma_bug.area) + snd_dma_free_pages(&ensoniq->dma_dev, &ensoniq->dma_bug); #endif if (ensoniq->res_port) { release_resource(ensoniq->res_port); @@ -1911,8 +1913,11 @@ } ensoniq->irq = pci->irq; #ifdef CHIP1370 - if ((ensoniq->bugbuf = snd_malloc_pci_pages(pci, 16, &ensoniq->bugbuf_addr)) == NULL) { - snd_printk("unable to allocate space for phantom area - bugbuf\n"); + memset(&ensoniq->dma_dev, 0, sizeof(ensoniq->dma_dev)); + ensoniq->dma_dev.type = SNDRV_DMA_TYPE_DEV; + ensoniq->dma_dev.dev = snd_dma_pci_data(pci); + if (snd_dma_alloc_pages(&ensoniq->dma_dev, 16, &ensoniq->dma_bug) < 0) { + snd_printk("unable to allocate space for phantom area - dma_bug\n"); snd_ensoniq_free(ensoniq); return -EBUSY; } @@ -1936,7 +1941,7 @@ outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL)); outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE)); - outl(ensoniq->bugbuf_addr, ES_REG(ensoniq, PHANTOM_FRAME)); + outl(ensoniq->dma_bug.addr, ES_REG(ensoniq, PHANTOM_FRAME)); outl(0, ES_REG(ensoniq, PHANTOM_COUNT)); #else ensoniq->ctrl = 0; diff -Nru a/sound/pci/es1938.c b/sound/pci/es1938.c --- a/sound/pci/es1938.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/es1938.c Sun Mar 7 18:45:35 2004 @@ -946,17 +946,14 @@ static int snd_es1938_capture_close(snd_pcm_substream_t * substream) { es1938_t *chip = snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; chip->capture_substream = NULL; - snd_free_pci_pages(chip->pci, runtime->dma_bytes, runtime->dma_area, runtime->dma_addr); return 0; } static int snd_es1938_playback_close(snd_pcm_substream_t * substream) { es1938_t *chip = snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; switch (substream->number) { case 0: @@ -969,7 +966,6 @@ snd_BUG(); return -EINVAL; } - snd_free_pci_pages(chip->pci, runtime->dma_bytes, runtime->dma_area, runtime->dma_addr); return 0; } @@ -1018,7 +1014,8 @@ pcm->info_flags = 0; strcpy(pcm->name, "ESS Solo-1"); - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 64*1024); if (rpcm) *rpcm = pcm; diff -Nru a/sound/pci/es1968.c b/sound/pci/es1968.c --- a/sound/pci/es1968.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/es1968.c Sun Mar 7 18:45:35 2004 @@ -1495,13 +1495,15 @@ static int __devinit snd_es1968_init_dmabuf(es1968_t *chip) { + int err; esm_memory_t *chunk; - snd_dma_device_pci(&chip->dma_dev, chip->pci, 0); + chip->dma_dev.type = SNDRV_DMA_TYPE_DEV; + chip->dma_dev.dev = snd_dma_pci_data(chip->pci); + chip->dma_dev.id = 0; if (! snd_dma_get_reserved(&chip->dma_dev, &chip->dma)) { - chip->dma.area = snd_malloc_pci_pages_fallback(chip->pci, chip->total_bufsize, - &chip->dma.addr, &chip->dma.bytes); - if (chip->dma.area == NULL) { + err = snd_dma_alloc_pages_fallback(&chip->dma_dev, chip->total_bufsize, &chip->dma); + if (err < 0 || ! chip->dma.area) { snd_printk("es1968: can't allocate dma pages for size %d\n", chip->total_bufsize); return -ENOMEM; diff -Nru a/sound/pci/fm801.c b/sound/pci/fm801.c --- a/sound/pci/fm801.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/fm801.c Sun Mar 7 18:45:35 2004 @@ -35,7 +35,7 @@ #include -#if defined(CONFIG_SND_FM801_TEA575X) && (defined(CONFIG_VIDEO_DEV) || defined(CONFIG_VIDEO_DEV_MODULE)) +#if (defined(CONFIG_SND_FM801_TEA575X) || defined(CONFIG_SND_FM801_TEA575X_MODULE)) && (defined(CONFIG_VIDEO_DEV) || defined(CONFIG_VIDEO_DEV_MODULE)) #include #define TEA575X_RADIO 1 #endif @@ -703,7 +703,9 @@ strcpy(pcm->name, "FM801"); chip->pcm = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, chip->multichannel ? 128*1024 : 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + chip->multichannel ? 128*1024 : 64*1024, 128*1024); if (rpcm) *rpcm = pcm; diff -Nru a/sound/pci/ice1712/amp.c b/sound/pci/ice1712/amp.c --- a/sound/pci/ice1712/amp.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/ice1712/amp.c Sun Mar 7 18:45:35 2004 @@ -38,6 +38,7 @@ /* only use basic functionality for now */ ice->num_total_dacs = 2; /* only PSDOUT0 is connected */ + ice->num_total_adcs = 2; return 0; } diff -Nru a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c --- a/sound/pci/ice1712/aureon.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/ice1712/aureon.c Sun Mar 7 18:45:35 2004 @@ -409,10 +409,13 @@ unsigned int tmp; unsigned int i; - if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON51_SKY) + if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON51_SKY) { ice->num_total_dacs = 6; - else + ice->num_total_adcs = 6; + } else { ice->num_total_dacs = 8; + ice->num_total_adcs = 8; + } /* to remeber the register values */ ice->akm = snd_kcalloc(sizeof(akm4xxx_t), GFP_KERNEL); diff -Nru a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c --- a/sound/pci/ice1712/delta.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/ice1712/delta.c Sun Mar 7 18:45:35 2004 @@ -511,20 +511,25 @@ switch (ice->eeprom.subvendor) { case ICE1712_SUBDEVICE_AUDIOPHILE: ice->num_total_dacs = 2; + ice->num_total_adcs = 2; break; case ICE1712_SUBDEVICE_DELTA410: ice->num_total_dacs = 8; + ice->num_total_adcs = 2; break; case ICE1712_SUBDEVICE_DELTA44: case ICE1712_SUBDEVICE_DELTA66: ice->num_total_dacs = ice->omni ? 8 : 4; + ice->num_total_adcs = ice->omni ? 8 : 4; break; case ICE1712_SUBDEVICE_DELTA1010: case ICE1712_SUBDEVICE_DELTA1010LT: ice->num_total_dacs = 8; + ice->num_total_adcs = 8; break; case ICE1712_SUBDEVICE_VX442: ice->num_total_dacs = 4; + ice->num_total_adcs = 4; break; } diff -Nru a/sound/pci/ice1712/ews.c b/sound/pci/ice1712/ews.c --- a/sound/pci/ice1712/ews.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/ice1712/ews.c Sun Mar 7 18:45:35 2004 @@ -406,15 +406,18 @@ switch (ice->eeprom.subvendor) { case ICE1712_SUBDEVICE_EWX2496: ice->num_total_dacs = 2; + ice->num_total_adcs = 2; break; case ICE1712_SUBDEVICE_EWS88MT: case ICE1712_SUBDEVICE_EWS88MT_NEW: ice->num_total_dacs = 8; + ice->num_total_adcs = 8; break; case ICE1712_SUBDEVICE_EWS88D: break; /* no analog */ case ICE1712_SUBDEVICE_DMX6FIRE: ice->num_total_dacs = 6; + ice->num_total_adcs = 6; break; } diff -Nru a/sound/pci/ice1712/hoontech.c b/sound/pci/ice1712/hoontech.c --- a/sound/pci/ice1712/hoontech.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/ice1712/hoontech.c Sun Mar 7 18:45:35 2004 @@ -153,6 +153,7 @@ int box, chn; ice->num_total_dacs = 8; + ice->num_total_adcs = 8; ice->hoontech_boxbits[0] = ice->hoontech_boxbits[1] = diff -Nru a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c --- a/sound/pci/ice1712/ice1712.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/ice1712/ice1712.c Sun Mar 7 18:45:35 2004 @@ -886,7 +886,8 @@ strcpy(pcm->name, "ICE1712 consumer"); ice->pcm = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(ice->pci), 64*1024, 64*1024); if (rpcm) *rpcm = pcm; @@ -922,7 +923,8 @@ strcpy(pcm->name, "ICE1712 consumer (DS)"); ice->pcm_ds = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(ice->pci), 64*1024, 128*1024); if (rpcm) *rpcm = pcm; @@ -1269,7 +1271,8 @@ pcm->info_flags = 0; strcpy(pcm->name, "ICE1712 multi"); - snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 256*1024, 256*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(ice->pci), 256*1024, 256*1024); ice->pcm_pro = pcm; if (rpcm) @@ -1381,7 +1384,7 @@ } -static snd_kcontrol_new_t snd_ice1712_multi_ctrls[] __devinitdata = { +static snd_kcontrol_new_t snd_ice1712_multi_playback_ctrls[] __devinitdata = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Multi Playback Switch", @@ -1400,24 +1403,44 @@ .private_value = 0, .count = 10, }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Multi Capture Switch", - .info = snd_ice1712_pro_mixer_switch_info, - .get = snd_ice1712_pro_mixer_switch_get, - .put = snd_ice1712_pro_mixer_switch_put, - .private_value = 10, - .count = 10, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Multi Capture Volume", - .info = snd_ice1712_pro_mixer_volume_info, - .get = snd_ice1712_pro_mixer_volume_get, - .put = snd_ice1712_pro_mixer_volume_put, - .private_value = 10, - .count = 10, - }, +}; + +static snd_kcontrol_new_t snd_ice1712_multi_capture_analog_switch __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "H/W Multi Capture Switch", + .info = snd_ice1712_pro_mixer_switch_info, + .get = snd_ice1712_pro_mixer_switch_get, + .put = snd_ice1712_pro_mixer_switch_put, + .private_value = 10, +}; + +static snd_kcontrol_new_t snd_ice1712_multi_capture_spdif_switch __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "IEC958 Multi Capture Switch", + .info = snd_ice1712_pro_mixer_switch_info, + .get = snd_ice1712_pro_mixer_switch_get, + .put = snd_ice1712_pro_mixer_switch_put, + .private_value = 18, + .count = 2, +}; + +static snd_kcontrol_new_t snd_ice1712_multi_capture_analog_volume __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "H/W Multi Capture Volume", + .info = snd_ice1712_pro_mixer_volume_info, + .get = snd_ice1712_pro_mixer_volume_get, + .put = snd_ice1712_pro_mixer_volume_put, + .private_value = 10, +}; + +static snd_kcontrol_new_t snd_ice1712_multi_capture_spdif_volume __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "IEC958 Multi Capture Volume", + .info = snd_ice1712_pro_mixer_volume_info, + .get = snd_ice1712_pro_mixer_volume_get, + .put = snd_ice1712_pro_mixer_volume_put, + .private_value = 18, + .count = 2, }; static int __devinit snd_ice1712_build_pro_mixer(ice1712_t *ice) @@ -1427,14 +1450,46 @@ int err; /* multi-channel mixer */ - for (idx = 0; idx < ARRAY_SIZE(snd_ice1712_multi_ctrls); idx++) { - err = snd_ctl_add(card, snd_ctl_new1(&snd_ice1712_multi_ctrls[idx], ice)); + for (idx = 0; idx < ARRAY_SIZE(snd_ice1712_multi_playback_ctrls); idx++) { + err = snd_ctl_add(card, snd_ctl_new1(&snd_ice1712_multi_playback_ctrls[idx], ice)); if (err < 0) return err; } + if (ice->num_total_adcs > 0) { + snd_kcontrol_new_t tmp = snd_ice1712_multi_capture_analog_switch; + tmp.count = ice->num_total_adcs; + err = snd_ctl_add(card, snd_ctl_new1(&tmp, ice)); + if (err < 0) + return err; + } + + err = snd_ctl_add(card, snd_ctl_new1(&snd_ice1712_multi_capture_spdif_switch, ice)); + if (err < 0) + return err; + + if (ice->num_total_adcs > 0) { + snd_kcontrol_new_t tmp = snd_ice1712_multi_capture_analog_volume; + tmp.count = ice->num_total_adcs; + err = snd_ctl_add(card, snd_ctl_new1(&tmp, ice)); + if (err < 0) + return err; + } + + err = snd_ctl_add(card, snd_ctl_new1(&snd_ice1712_multi_capture_spdif_volume, ice)); + if (err < 0) + return err; + /* initialize volumes */ - for (idx = 0; idx < 20; idx++) { + for (idx = 0; idx < 10; idx++) { + ice->pro_volumes[idx] = 0x80008000; /* mute */ + snd_ice1712_update_volume(ice, idx); + } + for (idx = 10; idx < 10 + ice->num_total_adcs; idx++) { + ice->pro_volumes[idx] = 0x80008000; /* mute */ + snd_ice1712_update_volume(ice, idx); + } + for (idx = 18; idx < 20; idx++) { ice->pro_volumes[idx] = 0x80008000; /* mute */ snd_ice1712_update_volume(ice, idx); } @@ -2375,6 +2430,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 Mar 7 18:45:35 2004 +++ b/sound/pci/ice1712/ice1712.h Sun Mar 7 18:45:35 2004 @@ -330,10 +330,14 @@ unsigned int omni: 1; /* Delta Omni I/O */ unsigned int vt1724: 1; unsigned int num_total_dacs; /* total DACs */ + unsigned int num_total_adcs; /* total ADCs */ unsigned char hoontech_boxbits[4]; unsigned int hoontech_config; 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; diff -Nru a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c --- a/sound/pci/ice1712/ice1724.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/ice1712/ice1724.c Sun Mar 7 18:45:35 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) && @@ -723,7 +756,8 @@ pcm->info_flags = 0; strcpy(pcm->name, "ICE1724"); - snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 256*1024, 256*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(ice->pci), 256*1024, 256*1024); ice->pcm_pro = pcm; @@ -735,25 +769,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 +810,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 +836,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); @@ -894,7 +911,8 @@ pcm->info_flags = 0; strcpy(pcm->name, "ICE1724 IEC958"); - snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(ice->pci), 64*1024, 64*1024); ice->pcm = pcm; @@ -903,6 +921,128 @@ /* + * 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_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(ice->pci), 64*1024, 64*1024); + + ice->pcm_ds = pcm; + + return 0; +} + + +/* * Mixer section */ @@ -1808,6 +1948,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 +2073,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/ice1712/prodigy.c b/sound/pci/ice1712/prodigy.c --- a/sound/pci/ice1712/prodigy.c Sun Mar 7 18:45:36 2004 +++ b/sound/pci/ice1712/prodigy.c Sun Mar 7 18:45:36 2004 @@ -585,6 +585,7 @@ printk(KERN_INFO "ice1724: Apostolos Dimitromanolakis \n"); ice->num_total_dacs = 8; + ice->num_total_adcs = 8; /* to remeber the register values */ ice->akm = snd_kcalloc(sizeof(akm4xxx_t), GFP_KERNEL); diff -Nru a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c --- a/sound/pci/ice1712/revo.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/ice1712/revo.c Sun Mar 7 18:45:35 2004 @@ -128,6 +128,7 @@ switch (ice->eeprom.subvendor) { case VT1724_SUBDEVICE_REVOLUTION71: ice->num_total_dacs = 8; + ice->num_total_adcs = 4; break; default: snd_BUG(); diff -Nru a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c --- a/sound/pci/intel8x0.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/intel8x0.c Sun Mar 7 18:45:35 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 }; @@ -396,6 +399,8 @@ unsigned long remap_bmaddr; struct resource *res_bm; + struct snd_dma_device dma_dev; + struct pci_dev *pci; snd_card_t *card; @@ -420,8 +425,7 @@ spinlock_t ac97_lock; u32 bdbars_count; - u32 *bdbars; - dma_addr_t bdbars_addr; + struct snd_dma_buffer bdbars; u32 int_sta_reg; /* interrupt status register */ u32 int_sta_mask; /* interrupt status mask */ unsigned int pcm_pos_shift; @@ -443,6 +447,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 +809,20 @@ 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); + if (chip->device_type != DEVICE_NFORCE) + status ^= igetdword(chip, chip->int_sta_reg); + } spin_unlock(&chip->reg_lock); - return IRQ_NONE; + if (chip->device_type != DEVICE_NFORCE && 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 +1032,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 +1040,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 +1097,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; @@ -1443,8 +1452,8 @@ strcpy(pcm->name, chip->card->shortname); chip->pcm[device] = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, rec->prealloc_size, - rec->prealloc_max_size); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), + rec->prealloc_size, rec->prealloc_max_size); return 0; } @@ -1666,6 +1675,12 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = { { + .vendor = 0x1014, + .device = 0x1f00, + .name = "MS-9128", + .type = AC97_TUNE_ALC_JACK + }, + { .vendor = 0x1028, .device = 0x00d8, .name = "Dell Precision 530", /* AD1885 */ @@ -1741,6 +1756,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 @@ -1759,6 +1780,7 @@ .name = "Intel ICH5/AD1985", .type = AC97_TUNE_AD_SHARING }, +#if 0 /* FIXME: this seems wrong on most boards */ { .vendor = 0x8086, .device = 0xa000, @@ -1766,6 +1788,7 @@ .name = "Intel ICH5/AD1985", .type = AC97_TUNE_HP_ONLY }, +#endif { } /* terminator */ }; @@ -2116,10 +2139,10 @@ /* --- */ synchronize_irq(chip->irq); __hw_end: - if (chip->bdbars) { + if (chip->bdbars.area) { if (chip->fix_nocache) - fill_nocache(chip->bdbars, chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2, 0); - snd_free_pci_pages(chip->pci, chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2, chip->bdbars, chip->bdbars_addr); + fill_nocache(chip->bdbars.area, chip->bdbars.bytes, 0); + snd_dma_free_pages(&chip->dma_dev, &chip->bdbars); } if (chip->remap_addr) iounmap((void *) chip->remap_addr); @@ -2509,23 +2532,27 @@ /* SIS7012 handles the pcm data in bytes, others are in words */ chip->pcm_pos_shift = (device_type == DEVICE_SIS) ? 0 : 1; + memset(&chip->dma_dev, 0, sizeof(chip->dma_dev)); + chip->dma_dev.type = SNDRV_DMA_TYPE_DEV; + chip->dma_dev.dev = snd_dma_pci_data(pci); + /* allocate buffer descriptor lists */ /* the start of each lists must be aligned to 8 bytes */ - chip->bdbars = (u32 *)snd_malloc_pci_pages(pci, chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2, &chip->bdbars_addr); - if (chip->bdbars == NULL) { + if (snd_dma_alloc_pages(&chip->dma_dev, chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2, &chip->bdbars) < 0) { snd_intel8x0_free(chip); + snd_printk(KERN_ERR "intel8x0: cannot allocate buffer descriptors\n"); return -ENOMEM; } /* tables must be aligned to 8 bytes here, but the kernel pages are much bigger, so we don't care (on i386) */ /* workaround for 440MX */ if (chip->fix_nocache) - fill_nocache(chip->bdbars, chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2, 1); + fill_nocache(chip->bdbars.area, chip->bdbars.bytes, 1); int_sta_masks = 0; for (i = 0; i < chip->bdbars_count; i++) { ichdev = &chip->ichd[i]; - ichdev->bdbar = chip->bdbars + (i * ICH_MAX_FRAGS * 2); - ichdev->bdbar_addr = chip->bdbars_addr + (i * sizeof(u32) * ICH_MAX_FRAGS * 2); + ichdev->bdbar = ((u32 *)chip->bdbars.area) + (i * ICH_MAX_FRAGS * 2); + ichdev->bdbar_addr = chip->bdbars.addr + (i * sizeof(u32) * ICH_MAX_FRAGS * 2); int_sta_masks |= ichdev->int_sta_mask; } chip->int_sta_reg = device_type == DEVICE_ALI ? ICH_REG_ALI_INTERRUPTSR : ICH_REG_GLOB_STA; @@ -2567,6 +2594,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/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c --- a/sound/pci/korg1212/korg1212.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/korg1212/korg1212.c Sun Mar 7 18:45:35 2004 @@ -347,9 +347,14 @@ struct resource *res_ioport; struct resource *res_iomem2; + struct snd_dma_device dma_dev; + + struct snd_dma_buffer dma_dsp; + struct snd_dma_buffer dma_play; + struct snd_dma_buffer dma_rec; + struct snd_dma_buffer dma_shared; + u32 dspCodeSize; - u32 dspMemPhy; // DSP memory block handle (Physical Address) - void * dspMemPtr; // block memory (Virtual Address) u32 DataBufsSize; @@ -357,6 +362,7 @@ KorgAudioBuffer * recordDataBufsPtr; KorgSharedBuffer * sharedBufferPtr; + u32 RecDataPhy; u32 PlayDataPhy; unsigned long sharedBufferPhy; @@ -1238,10 +1244,10 @@ snd_korg1212_setCardState(korg1212, K1212_STATE_DSP_IN_PROCESS); - memcpy(korg1212->dspMemPtr, dspCode, korg1212->dspCodeSize); + memcpy(korg1212->dma_dsp.area, dspCode, korg1212->dspCodeSize); rc = snd_korg1212_Send1212Command(korg1212, K1212_DB_StartDSPDownload, - UpperWordSwap(korg1212->dspMemPhy), + UpperWordSwap(korg1212->dma_dsp.addr), 0, 0, 0); #if K1212_DEBUG_LEVEL > 0 @@ -2134,12 +2140,9 @@ // ---------------------------------------------------- // free up memory resources used for the DSP download. // ---------------------------------------------------- - if (korg1212->dspMemPtr) { - snd_free_pci_pages(korg1212->pci, korg1212->dspCodeSize, - korg1212->dspMemPtr, (dma_addr_t)korg1212->dspMemPhy); - korg1212->dspMemPhy = 0; - korg1212->dspMemPtr = 0; - korg1212->dspCodeSize = 0; + if (korg1212->dma_dsp.area) { + snd_dma_free_pages(&korg1212->dma_dev, &korg1212->dma_dsp); + korg1212->dma_dsp.area = NULL; } #ifndef K1212_LARGEALLOC @@ -2147,18 +2150,14 @@ // ------------------------------------------------------ // free up memory resources used for the Play/Rec Buffers // ------------------------------------------------------ - if (korg1212->playDataBufsPtr) { - snd_free_pci_pages(korg1212->pci, korg1212->DataBufsSize, - korg1212->playDataBufsPtr, (dma_addr_t)korg1212->PlayDataPhy); - korg1212->PlayDataPhy = 0; - korg1212->playDataBufsPtr = NULL; + if (korg1212->dma_play.area) { + snd_dma_free_pages(&korg1212->dma_dev, &korg1212->dma_play); + korg1212->dma_play.area = NULL; } - if (korg1212->recordDataBufsPtr) { - snd_free_pci_pages(korg1212->pci, korg1212->DataBufsSize, - korg1212->recordDataBufsPtr, (dma_addr_t)korg1212->RecDataPhy); - korg1212->RecDataPhy = 0; - korg1212->recordDataBufsPtr = NULL; + if (korg1212->dma_rec.area) { + snd_dma_free_pages(&korg1212->dma_dev, &korg1212->dma_rec); + korg1212->dma_rec.area = NULL; } #endif @@ -2166,11 +2165,9 @@ // ---------------------------------------------------- // free up memory resources used for the Shared Buffers // ---------------------------------------------------- - if (korg1212->sharedBufferPtr) { - snd_free_pci_pages(korg1212->pci, (u32) sizeof(KorgSharedBuffer), - korg1212->sharedBufferPtr, (dma_addr_t)korg1212->sharedBufferPhy); - korg1212->sharedBufferPhy = 0; - korg1212->sharedBufferPtr = NULL; + if (korg1212->dma_shared.area) { + snd_dma_free_pages(&korg1212->dma_dev, &korg1212->dma_shared); + korg1212->dma_shared.area = NULL; } snd_magic_kfree(korg1212); @@ -2193,7 +2190,6 @@ int err; unsigned int i; unsigned ioport_size, iomem_size, iomem2_size; - dma_addr_t phys_addr; korg1212_t * korg1212; static snd_device_ops_t ops = { @@ -2332,13 +2328,16 @@ stateName[korg1212->cardState]); #endif - korg1212->sharedBufferPtr = (KorgSharedBuffer *) snd_malloc_pci_pages(korg1212->pci, sizeof(KorgSharedBuffer), &phys_addr); - korg1212->sharedBufferPhy = (unsigned long)phys_addr; + memset(&korg1212->dma_dev, 0, sizeof(korg1212->dma_dev)); + korg1212->dma_dev.type = SNDRV_DMA_TYPE_DEV; + korg1212->dma_dev.dev = snd_dma_pci_data(korg1212->pci); - if (korg1212->sharedBufferPtr == NULL) { + if (snd_dma_alloc_pages(&korg1212->dma_dev, sizeof(KorgSharedBuffer), &korg1212->dma_shared) < 0) { snd_printk(KERN_ERR "can not allocate shared buffer memory (%Zd bytes)\n", sizeof(KorgSharedBuffer)); return -ENOMEM; } + korg1212->sharedBufferPtr = (KorgSharedBuffer *)korg1212->dma_shared.area; + korg1212->sharedBufferPhy = korg1212->dma_shared.addr; #if K1212_DEBUG_LEVEL > 0 K1212_DEBUG_PRINTK("K1212_DEBUG: Shared Buffer Area = 0x%p (0x%08lx), %d bytes\n", korg1212->sharedBufferPtr, korg1212->sharedBufferPhy, sizeof(KorgSharedBuffer)); @@ -2348,30 +2347,28 @@ korg1212->DataBufsSize = sizeof(KorgAudioBuffer) * kNumBuffers; - korg1212->playDataBufsPtr = (KorgAudioBuffer *) snd_malloc_pci_pages(korg1212->pci, korg1212->DataBufsSize, &phys_addr); - korg1212->PlayDataPhy = (u32)phys_addr; - - if (korg1212->playDataBufsPtr == NULL) { + if (snd_dma_alloc_pages(&korg1212->dma_dev, korg1212->DataBufsSize, &korg1212->dma_play) < 0) { snd_printk(KERN_ERR "can not allocate play data buffer memory (%d bytes)\n", korg1212->DataBufsSize); return -ENOMEM; } + korg1212->playDataBufsPtr = (KorgAudioBuffer *)korg1212->dma_play.area; + korg1212->PlayDataPhy = korg1212->dma_play.addr; #if K1212_DEBUG_LEVEL > 0 K1212_DEBUG_PRINTK("K1212_DEBUG: Play Data Area = 0x%p (0x%08x), %d bytes\n", korg1212->playDataBufsPtr, korg1212->PlayDataPhy, korg1212->DataBufsSize); #endif - korg1212->recordDataBufsPtr = (KorgAudioBuffer *) snd_malloc_pci_pages(korg1212->pci, korg1212->DataBufsSize, &phys_addr); - korg1212->RecDataPhy = (u32)phys_addr; - - if (korg1212->recordDataBufsPtr == NULL) { + if (snd_dma_alloc_pages(&korg1212->dma_dev, korg1212->DataBufsSize, &korg1212->dma_rec) < 0) { snd_printk(KERN_ERR "can not allocate record data buffer memory (%d bytes)\n", korg1212->DataBufsSize); return -ENOMEM; } + korg1212->recordDataBufsPtr = (KorgAudioBuffer *)korg1212->dma_rec.area; + korg1212->RecDataPhy = korg1212->dma_rec.addr; #if K1212_DEBUG_LEVEL > 0 K1212_DEBUG_PRINTK("K1212_DEBUG: Record Data Area = 0x%p (0x%08x), %d bytes\n", - korg1212->recordDataBufsPtr, korg1212->RecDataPhy, korg1212->DataBufsSize); + korg1212->recordDataBufsPtr, korg1212->RecDataBufsPhy, korg1212->DataBufsSize); #endif #else // K1212_LARGEALLOC @@ -2392,17 +2389,14 @@ korg1212->AdatTimeCodePhy = korg1212->sharedBufferPhy + offsetof(KorgSharedBuffer, AdatTimeCode); - korg1212->dspMemPtr = snd_malloc_pci_pages(korg1212->pci, korg1212->dspCodeSize, &phys_addr); - korg1212->dspMemPhy = (u32)phys_addr; - - if (korg1212->dspMemPtr == NULL) { + if (snd_dma_alloc_pages(&korg1212->dma_dev, korg1212->dspCodeSize, &korg1212->dma_dsp) < 0) { snd_printk(KERN_ERR "can not allocate dsp code memory (%d bytes)\n", korg1212->dspCodeSize); return -ENOMEM; } #if K1212_DEBUG_LEVEL > 0 K1212_DEBUG_PRINTK("K1212_DEBUG: DSP Code area = 0x%p (0x%08x) %d bytes [%s]\n", - korg1212->dspMemPtr, korg1212->dspMemPhy, korg1212->dspCodeSize, + korg1212->dma_dsp.area, korg1212->dma_dsp.addr, korg1212->dspCodeSize, stateName[korg1212->cardState]); #endif @@ -2425,7 +2419,7 @@ "VolumeTablePhy = %08x L[%08x]\n" "RoutingTablePhy = %08x L[%08x]\n" "AdatTimeCodePhy = %08x L[%08x]\n", - korg1212->dspMemPhy, UpperWordSwap(korg1212->dspMemPhy), + korg1212->dma_dsp.addr, UpperWordSwap(korg1212->dma_dsp.addr), korg1212->PlayDataPhy, LowerWordSwap(korg1212->PlayDataPhy), korg1212->RecDataPhy, LowerWordSwap(korg1212->RecDataPhy), korg1212->VolumeTablePhy, LowerWordSwap(korg1212->VolumeTablePhy), diff -Nru a/sound/pci/maestro3.c b/sound/pci/maestro3.c --- a/sound/pci/maestro3.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/maestro3.c Sun Mar 7 18:45:35 2004 @@ -1816,7 +1816,8 @@ strcpy(pcm->name, chip->card->driver); chip->pcm = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 64*1024); return 0; } 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 Mar 7 18:45:36 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 Mar 7 18:45:36 2004 @@ -0,0 +1,1473 @@ +/* + * 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 +#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; + + flowinfo = (struct mixart_flowinfo *)chip->mgr->flowinfo.area; + flowinfo[j].bufferinfo_array_phy_address = (u32)chip->mgr->bufferinfo.addr + (j * sizeof(mixart_bufferinfo_t)); + flowinfo[j].bufferinfo_count = 1; /* 1 will set the miXart to ring-buffer mode ! */ + + bufferinfo = (struct mixart_bufferinfo *)chip->mgr->bufferinfo.area; + bufferinfo[j].buffer_address = 0; /* buffer is not yet allocated */ + bufferinfo[j].available_length = 0; /* buffer is not yet allocated */ + + /* construct the identifier of the stream buffer received in the interrupts ! */ + bufferinfo[j].buffer_id = (chip->chip_idx << MIXART_NOTIFY_CARD_OFFSET) + (pcm_number << MIXART_NOTIFY_PCM_OFFSET ) + i; + if(capture) { + bufferinfo[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) { + struct mixart_bufferinfo *bufferinfo; + 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 */ + } + + bufferinfo = (struct mixart_bufferinfo *)chip->mgr->bufferinfo.area; + bufferinfo[i].buffer_address = subs->runtime->dma_addr; + bufferinfo[i].available_length = subs->runtime->dma_bytes; + /* bufferinfo[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_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->mgr->pci), 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.area) { + snd_dma_free_pages(&mgr->dma_dev, &mgr->flowinfo); + mgr->flowinfo.area = NULL; + } + /* free bufferarray */ + if(mgr->bufferinfo.area) { + snd_dma_free_pages(&mgr->dma_dev, &mgr->bufferinfo); + mgr->bufferinfo.area = NULL; + } + + 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; + + memset(&mgr->dma_dev, 0, sizeof(mgr->dma_dev)); + mgr->dma_dev.type = SNDRV_DMA_TYPE_DEV; + mgr->dma_dev.dev = snd_dma_pci_data(mgr->pci); + + /* create array of streaminfo */ + size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS * sizeof(mixart_flowinfo_t)) ); + if (snd_dma_alloc_pages(&mgr->dma_dev, size, &mgr->flowinfo) < 0) { + snd_mixart_free(mgr); + return -ENOMEM; + } + /* init streaminfo_array */ + memset(mgr->flowinfo.area, 0, size); + + /* create array of bufferinfo */ + size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS * sizeof(mixart_bufferinfo_t)) ); + if (snd_dma_alloc_pages(&mgr->dma_dev, size, &mgr->bufferinfo) < 0) { + snd_mixart_free(mgr); + return -ENOMEM; + } + /* init bufferinfo_array */ + memset(mgr->bufferinfo.area, 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 Mar 7 18:45:36 2004 @@ -0,0 +1,242 @@ +/* + * 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 */ + + struct snd_dma_device dma_dev; + struct snd_dma_buffer flowinfo; + struct snd_dma_buffer bufferinfo; + + 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 Mar 7 18:45:36 2004 @@ -0,0 +1,585 @@ +/* + * 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 +#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 Mar 7 18:45:36 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 Mar 7 18:45:36 2004 @@ -0,0 +1,572 @@ +/* + * 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 +#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.addr, 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 Mar 7 18:45:36 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 Mar 7 18:45:36 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 Mar 7 18:45:36 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/rme32.c b/sound/pci/rme32.c --- a/sound/pci/rme32.c Sun Mar 7 18:45:36 2004 +++ b/sound/pci/rme32.c Sun Mar 7 18:45:36 2004 @@ -1378,9 +1378,10 @@ rme32->spdif_pcm->info_flags = 0; snd_pcm_lib_preallocate_pages_for_all(rme32->spdif_pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), RME32_BUFFER_SIZE, - RME32_BUFFER_SIZE, - GFP_KERNEL); + RME32_BUFFER_SIZE); /* set up ALSA pcm device for ADAT */ if ((pci->device == PCI_DEVICE_ID_DIGI32) || @@ -1405,9 +1406,10 @@ rme32->adat_pcm->info_flags = 0; snd_pcm_lib_preallocate_pages_for_all(rme32->adat_pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), RME32_BUFFER_SIZE, - RME32_BUFFER_SIZE, - GFP_KERNEL); + RME32_BUFFER_SIZE); } diff -Nru a/sound/pci/rme96.c b/sound/pci/rme96.c --- a/sound/pci/rme96.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/rme96.c Sun Mar 7 18:45:35 2004 @@ -1718,7 +1718,11 @@ rme96->spdif_pcm->info_flags = 0; - snd_pcm_lib_preallocate_pages_for_all(rme96->spdif_pcm, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE, GFP_KERNEL); + snd_pcm_lib_preallocate_pages_for_all(rme96->spdif_pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + RME96_BUFFER_SIZE, + RME96_BUFFER_SIZE); /* set up ALSA pcm device for ADAT */ if (pci->device == PCI_DEVICE_ID_DIGI96) { @@ -1738,7 +1742,11 @@ rme96->adat_pcm->info_flags = 0; - snd_pcm_lib_preallocate_pages_for_all(rme96->adat_pcm, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE, GFP_KERNEL); + snd_pcm_lib_preallocate_pages_for_all(rme96->adat_pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + RME96_BUFFER_SIZE, + RME96_BUFFER_SIZE); } rme96->playback_periodsize = 0; diff -Nru a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c --- a/sound/pci/rme9652/hdsp.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/rme9652/hdsp.c Sun Mar 7 18:45:35 2004 @@ -568,7 +568,10 @@ struct snd_dma_device pdev; struct snd_dma_buffer dmbuf; - snd_dma_device_pci(&pdev, pci, capture); + memset(&pdev, 0, sizeof(pdev)); + pdev.type = SNDRV_DMA_TYPE_DEV; + pdev.dev = snd_dma_pci_data(pci); + pdev.id = capture; dmbuf.bytes = 0; if (! snd_dma_get_reserved(&pdev, &dmbuf)) { if (snd_dma_alloc_pages(&pdev, size, &dmbuf) < 0) @@ -581,9 +584,13 @@ static void snd_hammerfall_free_buffer(struct pci_dev *pci, size_t size, void *ptr, dma_addr_t addr, int capture) { - struct snd_dma_device dev; - snd_dma_device_pci(&dev, pci, capture); - snd_dma_free_reserved(&dev); + struct snd_dma_device pdev; + + memset(&pdev, 0, sizeof(pdev)); + pdev.type = SNDRV_DMA_TYPE_DEV; + pdev.dev = snd_dma_pci_data(pci); + pdev.id = capture; + snd_dma_free_reserved(&pdev); } #else @@ -3810,7 +3817,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 +3840,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 +3855,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/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c --- a/sound/pci/rme9652/rme9652.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/rme9652/rme9652.c Sun Mar 7 18:45:35 2004 @@ -314,7 +314,10 @@ struct snd_dma_device pdev; struct snd_dma_buffer dmbuf; - snd_dma_device_pci(&pdev, pci, capture); + memset(&pdev, 0, sizeof(pdev)); + pdev.type = SNDRV_DMA_TYPE_DEV; + pdev.dev = snd_dma_pci_data(pci); + pdev.id = capture; dmbuf.bytes = 0; if (! snd_dma_get_reserved(&pdev, &dmbuf)) { if (snd_dma_alloc_pages(&pdev, size, &dmbuf) < 0) @@ -327,9 +330,13 @@ static void snd_hammerfall_free_buffer(struct pci_dev *pci, size_t size, void *ptr, dma_addr_t addr, int capture) { - struct snd_dma_device dev; - snd_dma_device_pci(&dev, pci, capture); - snd_dma_free_reserved(&dev); + struct snd_dma_device pdev; + + memset(&pdev, 0, sizeof(pdev)); + pdev.type = SNDRV_DMA_TYPE_DEV; + pdev.dev = snd_dma_pci_data(pci); + pdev.id = capture; + snd_dma_free_reserved(&pdev); } #else diff -Nru a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c --- a/sound/pci/sonicvibes.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/sonicvibes.c Sun Mar 7 18:45:35 2004 @@ -886,7 +886,8 @@ strcpy(pcm->name, "S3 SonicVibes"); sonic->pcm = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(sonic->pci, pcm, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(sonic->pci), 64*1024, 128*1024); if (rpcm) *rpcm = pcm; diff -Nru a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c --- a/sound/pci/trident/trident_main.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/trident/trident_main.c Sun Mar 7 18:45:35 2004 @@ -1009,8 +1009,6 @@ snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; unsigned int val, ESO_bytes; - snd_assert(substream->dma_device.type == SNDRV_DMA_TYPE_PCI, return -EIO); - spin_lock(&trident->reg_lock); // Initilize the channel and set channel Mode @@ -2189,10 +2187,15 @@ if (trident->tlb.entries) { snd_pcm_substream_t *substream; for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) - snd_pcm_lib_preallocate_sg_pages(trident->pci, substream, 64*1024, 128*1024); - snd_pcm_lib_preallocate_pci_pages(trident->pci, pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(trident->pci), + 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, + SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci), + 64*1024, 128*1024); } else { - snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, pcm, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(trident->pci), 64*1024, 128*1024); } if (rpcm) @@ -2246,9 +2249,11 @@ trident->foldback = foldback; if (trident->tlb.entries) - snd_pcm_lib_preallocate_sg_pages_for_all(trident->pci, foldback, 0, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(foldback, SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(trident->pci), 0, 128*1024); else - snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, foldback, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(foldback, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(trident->pci), 64*1024, 128*1024); if (rpcm) *rpcm = foldback; @@ -2287,7 +2292,7 @@ strcpy(spdif->name, "Trident 4DWave IEC958"); trident->spdif = spdif; - snd_pcm_lib_preallocate_pci_pages_for_all(trident->pci, spdif, 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(spdif, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci), 64*1024, 128*1024); if (rpcm) *rpcm = spdif; @@ -3052,29 +3057,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; } @@ -3328,13 +3353,12 @@ /* TLB array must be aligned to 16kB !!! so we allocate 32kB region and correct offset when necessary */ - trident->tlb.buffer = snd_malloc_pci_pages(trident->pci, 2 * SNDRV_TRIDENT_MAX_PAGES * 4, &trident->tlb.buffer_dmaaddr); - if (trident->tlb.buffer == NULL) { + if (snd_dma_alloc_pages(&trident->dma_dev, 2 * SNDRV_TRIDENT_MAX_PAGES * 4, &trident->tlb.buffer) < 0) { snd_printk(KERN_ERR "trident: unable to allocate TLB buffer\n"); return -ENOMEM; } - trident->tlb.entries = (unsigned int*)(((unsigned long)trident->tlb.buffer + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1)); - trident->tlb.entries_dmaaddr = (trident->tlb.buffer_dmaaddr + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1); + trident->tlb.entries = (unsigned int*)(((unsigned long)trident->tlb.buffer.area + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1)); + trident->tlb.entries_dmaaddr = (trident->tlb.buffer.addr + SNDRV_TRIDENT_MAX_PAGES * 4 - 1) & ~(SNDRV_TRIDENT_MAX_PAGES * 4 - 1); /* allocate shadow TLB page table (virtual addresses) */ trident->tlb.shadow_entries = (unsigned long *)vmalloc(SNDRV_TRIDENT_MAX_PAGES*sizeof(unsigned long)); if (trident->tlb.shadow_entries == NULL) { @@ -3342,15 +3366,14 @@ return -ENOMEM; } /* allocate and setup silent page and initialise TLB entries */ - trident->tlb.silent_page = snd_malloc_pci_pages(trident->pci, SNDRV_TRIDENT_PAGE_SIZE, &trident->tlb.silent_page_dmaaddr); - if (trident->tlb.silent_page == 0UL) { + if (snd_dma_alloc_pages(&trident->dma_dev, SNDRV_TRIDENT_PAGE_SIZE, &trident->tlb.silent_page) < 0) { snd_printk(KERN_ERR "trident: unable to allocate silent page\n"); return -ENOMEM; } - memset(trident->tlb.silent_page, 0, SNDRV_TRIDENT_PAGE_SIZE); + memset(trident->tlb.silent_page.area, 0, SNDRV_TRIDENT_PAGE_SIZE); for (i = 0; i < SNDRV_TRIDENT_MAX_PAGES; i++) { - trident->tlb.entries[i] = cpu_to_le32(trident->tlb.silent_page_dmaaddr & ~(SNDRV_TRIDENT_PAGE_SIZE-1)); - trident->tlb.shadow_entries[i] = (unsigned long)trident->tlb.silent_page; + trident->tlb.entries[i] = cpu_to_le32(trident->tlb.silent_page.addr & ~(SNDRV_TRIDENT_PAGE_SIZE-1)); + trident->tlb.shadow_entries[i] = (unsigned long)trident->tlb.silent_page.area; } /* use emu memory block manager code to manage tlb page allocation */ @@ -3565,9 +3588,13 @@ } trident->irq = pci->irq; + memset(&trident->dma_dev, 0, sizeof(trident->dma_dev)); + trident->dma_dev.type = SNDRV_DMA_TYPE_DEV; + trident->dma_dev.dev = snd_dma_pci_data(pci); + /* allocate 16k-aligned TLB for NX cards */ trident->tlb.entries = NULL; - trident->tlb.buffer = NULL; + trident->tlb.buffer.area = NULL; if (trident->device == TRIDENT_DEVICE_ID_NX) { if ((err = snd_trident_tlb_alloc(trident)) < 0) { snd_trident_free(trident); @@ -3661,15 +3688,15 @@ else if (trident->device == TRIDENT_DEVICE_ID_SI7018) { outl(0, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); } - if (trident->tlb.buffer) { + if (trident->tlb.buffer.area) { outl(0, TRID_REG(trident, NX_TLBC)); if (trident->tlb.memhdr) snd_util_memhdr_free(trident->tlb.memhdr); - if (trident->tlb.silent_page) - snd_free_pci_pages(trident->pci, SNDRV_TRIDENT_PAGE_SIZE, trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr); + if (trident->tlb.silent_page.area) + snd_dma_free_pages(&trident->dma_dev, &trident->tlb.silent_page); if (trident->tlb.shadow_entries) vfree(trident->tlb.shadow_entries); - snd_free_pci_pages(trident->pci, 2 * SNDRV_TRIDENT_MAX_PAGES * 4, trident->tlb.buffer, trident->tlb.buffer_dmaaddr); + snd_dma_free_pages(&trident->dma_dev, &trident->tlb.buffer); } if (trident->irq >= 0) free_irq(trident->irq, (void *)trident); diff -Nru a/sound/pci/trident/trident_memory.c b/sound/pci/trident/trident_memory.c --- a/sound/pci/trident/trident_memory.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/trident/trident_memory.c Sun Mar 7 18:45:35 2004 @@ -47,7 +47,7 @@ /* fill TLB entrie(s) corresponding to page with ptr */ #define set_tlb_bus(trident,page,ptr,addr) __set_tlb_bus(trident,page,ptr,addr) /* fill TLB entrie(s) corresponding to page with silence pointer */ -#define set_silent_tlb(trident,page) __set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page, trident->tlb.silent_page_dmaaddr) +#define set_silent_tlb(trident,page) __set_tlb_bus(trident, page, (unsigned long)trident->tlb.silent_page.area, trident->tlb.silent_page.addr) /* get aligned page from offset address */ #define get_aligned_page(offset) ((offset) >> 12) /* get offset address from aligned page */ @@ -191,7 +191,6 @@ int idx, page; struct snd_sg_buf *sgbuf = runtime->dma_private; - snd_assert(substream->dma_device.type == SNDRV_DMA_TYPE_PCI_SG, return NULL); snd_assert(runtime->dma_bytes > 0 && runtime->dma_bytes <= SNDRV_TRIDENT_MAX_PAGES * SNDRV_TRIDENT_PAGE_SIZE, return NULL); hdr = trident->tlb.memhdr; snd_assert(hdr != NULL, return NULL); @@ -240,7 +239,6 @@ dma_addr_t addr; unsigned long ptr; - snd_assert(substream->dma_device.type == SNDRV_DMA_TYPE_PCI, return NULL); snd_assert(runtime->dma_bytes> 0 && runtime->dma_bytes <= SNDRV_TRIDENT_MAX_PAGES * SNDRV_TRIDENT_PAGE_SIZE, return NULL); hdr = trident->tlb.memhdr; snd_assert(hdr != NULL, return NULL); @@ -276,7 +274,7 @@ { snd_assert(trident != NULL, return NULL); snd_assert(substream != NULL, return NULL); - if (substream->dma_device.type == SNDRV_DMA_TYPE_PCI_SG) + if (substream->dma_device.type == SNDRV_DMA_TYPE_DEV_SG) return snd_trident_alloc_sg_pages(trident, substream); else return snd_trident_alloc_cont_pages(trident, substream); @@ -367,8 +365,13 @@ void *ptr = page_to_ptr(trident, page); dma_addr_t addr = page_to_addr(trident, page); set_silent_tlb(trident, page); - if (ptr) - snd_free_pci_pages(trident->pci, ALIGN_PAGE_SIZE, ptr, addr); + if (ptr) { + struct snd_dma_buffer dmab; + dmab.area = ptr; + dmab.addr = addr; + dmab.bytes = ALIGN_PAGE_SIZE; + snd_dma_free_pages(&trident->dma_dev, &dmab); + } } /* check new allocation range */ @@ -399,8 +402,7 @@ static int synth_alloc_pages(trident_t *hw, snd_util_memblk_t *blk) { int page, first_page, last_page; - void *ptr; - dma_addr_t addr; + struct snd_dma_buffer dmab; firstpg(blk) = get_aligned_page(blk->offset); lastpg(blk) = get_aligned_page(blk->offset + blk->size - 1); @@ -410,14 +412,13 @@ * fortunately Trident page size and kernel PAGE_SIZE is identical! */ for (page = first_page; page <= last_page; page++) { - ptr = snd_malloc_pci_pages(hw->pci, ALIGN_PAGE_SIZE, &addr); - if (ptr == NULL) + if (snd_dma_alloc_pages(&hw->dma_dev, ALIGN_PAGE_SIZE, &dmab) < 0) goto __fail; - if (! is_valid_page(addr)) { - snd_free_pci_pages(hw->pci, ALIGN_PAGE_SIZE, ptr, addr); + if (! is_valid_page(dmab.addr)) { + snd_dma_free_pages(&hw->dma_dev, &dmab); goto __fail; } - set_tlb_bus(hw, page, (unsigned long)ptr, addr); + set_tlb_bus(hw, page, (unsigned long)dmab.area, dmab.addr); } return 0; diff -Nru a/sound/pci/trident/trident_synth.c b/sound/pci/trident/trident_synth.c --- a/sound/pci/trident/trident_synth.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/trident/trident_synth.c Sun Mar 7 18:45:35 2004 @@ -507,7 +507,6 @@ char *data, long len, int atomic) { trident_t *trident = snd_magic_cast(trident_t, private_data, return -ENXIO); - unsigned char *block = NULL; int size = instr->size; int shift = 0; @@ -530,7 +529,7 @@ if (trident->tlb.entries) { snd_util_memblk_t *memblk; - memblk = snd_trident_synth_alloc(trident,size); + memblk = snd_trident_synth_alloc(trident, size); if (memblk == NULL) return -ENOMEM; if (snd_trident_synth_copy_from_user(trident, memblk, 0, data, size) ) { @@ -540,17 +539,17 @@ instr->address.ptr = (unsigned char*)memblk; instr->address.memory = memblk->offset; } else { - dma_addr_t addr; - block = (unsigned char *) snd_malloc_pci_pages(trident->pci, size, &addr); - if (block == NULL) + struct snd_dma_buffer dmab; + + if (snd_dma_alloc_pages(&trident->dma_dev, size, &dmab) < 0) return -ENOMEM; - if (copy_from_user(block, data, size)) { - snd_free_pci_pages(trident->pci, size, block, addr); + if (copy_from_user(dmab.area, data, size)) { + snd_dma_free_pages(&trident->dma_dev, &dmab); return -EFAULT; } - instr->address.ptr = block; - instr->address.memory = addr; + instr->address.ptr = dmab.area; + instr->address.memory = dmab.addr; } trident->synth.current_size += size; diff -Nru a/sound/pci/via82xx.c b/sound/pci/via82xx.c --- a/sound/pci/via82xx.c Sun Mar 7 18:45:35 2004 +++ b/sound/pci/via82xx.c Sun Mar 7 18:45:36 2004 @@ -337,8 +337,7 @@ snd_pcm_substream_t *substream; int running; unsigned int tbl_entries; /* # descriptors */ - u32 *table; /* physical address + flag */ - dma_addr_t table_addr; + struct snd_dma_buffer table; struct snd_via_sg_table *idx_table; /* for recovery from the unexpected pointer */ unsigned int lastpos; @@ -347,6 +346,73 @@ }; +enum { TYPE_CARD_VIA686 = 1, TYPE_CARD_VIA8233 }; +enum { TYPE_VIA686, TYPE_VIA8233, TYPE_VIA8233A }; + +#define VIA_MAX_DEVS 7 /* 4 playback, 1 multi, 2 capture */ + +struct via_rate_lock { + spinlock_t lock; + int rate; + int used; +}; + +struct _snd_via82xx { + int irq; + + unsigned long port; + struct resource *res_port; + struct resource *mpu_res; + int chip_type; + unsigned char revision; + + unsigned char old_legacy; + unsigned char old_legacy_cfg; + + unsigned char playback_volume[4][2]; /* for VIA8233/C/8235; default = 0 */ + + unsigned int intr_mask; /* SGD_SHADOW mask to check interrupts */ + + struct pci_dev *pci; + snd_card_t *card; + + unsigned int num_devs; + unsigned int playback_devno, multi_devno, capture_devno; + viadev_t devs[VIA_MAX_DEVS]; + struct via_rate_lock rates[2]; /* playback and capture */ + unsigned int dxs_fixed: 1; /* DXS channel accepts only 48kHz */ + unsigned int no_vra: 1; /* no need to set VRA on DXS channels */ + + snd_rawmidi_t *rmidi; + + ac97_bus_t *ac97_bus; + ac97_t *ac97; + unsigned int ac97_clock; + unsigned int ac97_secondary; /* secondary AC'97 codec is present */ + + spinlock_t reg_lock; + spinlock_t ac97_lock; + snd_info_entry_t *proc_entry; + + struct snd_dma_device dma_dev; + +#ifdef SUPPORT_JOYSTICK + struct gameport gameport; + struct resource *res_joystick; +#endif +}; + +static struct pci_device_id snd_via82xx_ids[] = { + { 0x1106, 0x3058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA686, }, /* 686A */ + { 0x1106, 0x3059, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA8233, }, /* VT8233 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_via82xx_ids); + +/* + */ + /* * allocate and initialize the descriptor buffers * periods = number of periods @@ -357,14 +423,14 @@ unsigned int periods, unsigned int fragsize) { unsigned int i, idx, ofs, rest; + via82xx_t *chip = snd_pcm_substream_chip(substream); struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream); - if (! dev->table) { + if (dev->table.area == NULL) { /* the start of each lists must be aligned to 8 bytes, * but the kernel pages are much bigger, so we don't care */ - dev->table = (u32*)snd_malloc_pci_pages(pci, PAGE_ALIGN(VIA_TABLE_SIZE * 2 * 8), &dev->table_addr); - if (! dev->table) + if (snd_dma_alloc_pages(&chip->dma_dev, PAGE_ALIGN(VIA_TABLE_SIZE * 2 * 8), &dev->table) < 0) return -ENOMEM; } if (! dev->idx_table) { @@ -390,7 +456,7 @@ snd_printk(KERN_ERR "via82xx: too much table size!\n"); return -EINVAL; } - dev->table[idx << 1] = cpu_to_le32((u32)snd_pcm_sgbuf_get_addr(sgbuf, ofs)); + ((u32 *)dev->table.area)[idx << 1] = cpu_to_le32((u32)snd_pcm_sgbuf_get_addr(sgbuf, ofs)); r = PAGE_SIZE - (ofs % PAGE_SIZE); if (rest < r) r = rest; @@ -403,7 +469,7 @@ } else flag = 0; /* period continues to the next */ // printk("via: tbl %d: at %d size %d (rest %d)\n", idx, ofs, r, rest); - dev->table[(idx<<1) + 1] = cpu_to_le32(r | flag); + ((u32 *)dev->table.area)[(idx<<1) + 1] = cpu_to_le32(r | flag); dev->idx_table[idx].offset = ofs; dev->idx_table[idx].size = r; ofs += r; @@ -417,85 +483,21 @@ } -static void clean_via_table(viadev_t *dev, snd_pcm_substream_t *substream, - struct pci_dev *pci) +static int clean_via_table(viadev_t *dev, snd_pcm_substream_t *substream, + struct pci_dev *pci) { - if (dev->table) { - snd_free_pci_pages(pci, PAGE_ALIGN(VIA_TABLE_SIZE * 2 * 8), dev->table, dev->table_addr); - dev->table = NULL; + via82xx_t *chip = snd_pcm_substream_chip(substream); + if (dev->table.area) { + snd_dma_free_pages(&chip->dma_dev, &dev->table); + dev->table.area = NULL; } if (dev->idx_table) { kfree(dev->idx_table); dev->idx_table = NULL; } + return 0; } - -/* - */ - -enum { TYPE_CARD_VIA686 = 1, TYPE_CARD_VIA8233 }; -enum { TYPE_VIA686, TYPE_VIA8233, TYPE_VIA8233A }; - -#define VIA_MAX_DEVS 7 /* 4 playback, 1 multi, 2 capture */ - -struct via_rate_lock { - spinlock_t lock; - int rate; - int used; -}; - -struct _snd_via82xx { - int irq; - - unsigned long port; - struct resource *res_port; - struct resource *mpu_res; - int chip_type; - unsigned char revision; - - unsigned char old_legacy; - unsigned char old_legacy_cfg; - - unsigned char playback_volume[4][2]; /* for VIA8233/C/8235; default = 0 */ - - unsigned int intr_mask; /* SGD_SHADOW mask to check interrupts */ - - struct pci_dev *pci; - snd_card_t *card; - - unsigned int num_devs; - unsigned int playback_devno, multi_devno, capture_devno; - viadev_t devs[VIA_MAX_DEVS]; - struct via_rate_lock rates[2]; /* playback and capture */ - unsigned int dxs_fixed: 1; /* DXS channel accepts only 48kHz */ - unsigned int no_vra: 1; /* no need to set VRA on DXS channels */ - - snd_rawmidi_t *rmidi; - - ac97_bus_t *ac97_bus; - ac97_t *ac97; - unsigned int ac97_clock; - unsigned int ac97_secondary; /* secondary AC'97 codec is present */ - - spinlock_t reg_lock; - spinlock_t ac97_lock; - snd_info_entry_t *proc_entry; - -#ifdef SUPPORT_JOYSTICK - struct gameport gameport; - struct resource *res_joystick; -#endif -}; - -static struct pci_device_id snd_via82xx_ids[] = { - { 0x1106, 0x3058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA686, }, /* 686A */ - { 0x1106, 0x3059, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_CARD_VIA8233, }, /* VT8233 */ - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, snd_via82xx_ids); - /* * Basic I/O */ @@ -756,10 +758,10 @@ * so we need to calculate the index from CURR_PTR. */ ptr = inl(VIADEV_REG(viadev, OFFSET_CURR_PTR)); - if (ptr <= (unsigned int)viadev->table_addr) + if (ptr <= (unsigned int)viadev->table.addr) idx = 0; else /* CURR_PTR holds the address + 8 */ - idx = ((ptr - (unsigned int)viadev->table_addr) / 8 - 1) % viadev->tbl_entries; + idx = ((ptr - (unsigned int)viadev->table.addr) / 8 - 1) % viadev->tbl_entries; res = calc_linear_pos(viadev, idx, count); spin_unlock(&chip->reg_lock); @@ -840,7 +842,7 @@ static void snd_via82xx_set_table_ptr(via82xx_t *chip, viadev_t *viadev) { snd_via82xx_codec_ready(chip, 0); - outl((u32)viadev->table_addr, VIADEV_REG(viadev, OFFSET_TABLE_PTR)); + outl((u32)viadev->table.addr, VIADEV_REG(viadev, OFFSET_TABLE_PTR)); udelay(20); snd_via82xx_codec_ready(chip, 0); } @@ -922,12 +924,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); @@ -1290,7 +1290,8 @@ /* capture */ init_viadev(chip, chip->capture_devno, VIA_REG_CAPTURE_8233_STATUS, 1); - if ((err = snd_pcm_lib_preallocate_sg_pages_for_all(chip->pci, pcm, 64*1024, 128*1024)) < 0) + if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0) return err; /* PCM #1: multi-channel playback and 2nd capture */ @@ -1306,7 +1307,8 @@ /* set up capture */ init_viadev(chip, chip->capture_devno + 1, VIA_REG_CAPTURE_8233_STATUS + 0x10, 1); - if ((err = snd_pcm_lib_preallocate_sg_pages_for_all(chip->pci, pcm, 64*1024, 128*1024)) < 0) + if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0) return err; return 0; @@ -1339,7 +1341,8 @@ /* capture */ init_viadev(chip, chip->capture_devno, VIA_REG_CAPTURE_8233_STATUS, 1); - if ((err = snd_pcm_lib_preallocate_sg_pages_for_all(chip->pci, pcm, 64*1024, 128*1024)) < 0) + if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0) return err; /* PCM #1: DXS3 playback (for spdif) */ @@ -1352,7 +1355,8 @@ /* set up playback */ init_viadev(chip, chip->playback_devno, 0x30, 0); - if ((err = snd_pcm_lib_preallocate_sg_pages_for_all(chip->pci, pcm, 64*1024, 128*1024)) < 0) + if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0) return err; return 0; @@ -1381,7 +1385,8 @@ init_viadev(chip, 0, VIA_REG_PLAYBACK_STATUS, 0); init_viadev(chip, 1, VIA_REG_CAPTURE_STATUS, 1); - if ((err = snd_pcm_lib_preallocate_sg_pages_for_all(chip->pci, pcm, 64*1024, 128*1024)) < 0) + if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0) return err; return 0; @@ -1560,6 +1565,18 @@ .name = "ASRock K7VM2", .type = AC97_TUNE_HP_ONLY /* VT1616 */ }, + { + .vendor = 0x14cd, + .device = 0x7002, + .name = "Unknown", + .type = AC97_TUNE_ALC_JACK + }, + { + .vendor = 0x1071, + .device = 0x8590, + .name = "Mitac Mobo", + .type = AC97_TUNE_ALC_JACK + }, { } /* terminator */ }; @@ -1913,6 +1930,10 @@ chip->pci = pci; chip->irq = -1; + memset(&chip->dma_dev, 0, sizeof(chip->dma_dev)); + chip->dma_dev.type = SNDRV_DMA_TYPE_DEV; + chip->dma_dev.dev = snd_dma_pci_data(pci); + pci_read_config_byte(pci, VIA_FUNC_ENABLE, &chip->old_legacy); pci_read_config_byte(pci, VIA_PNP_CONTROL, &chip->old_legacy_cfg); @@ -1987,13 +2008,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/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c --- a/sound/pci/ymfpci/ymfpci_main.c Sun Mar 7 18:45:36 2004 +++ b/sound/pci/ymfpci/ymfpci_main.c Sun Mar 7 18:45:36 2004 @@ -541,20 +541,15 @@ static int __devinit snd_ymfpci_ac3_init(ymfpci_t *chip) { - unsigned char *ptr; - dma_addr_t ptr_addr; - - if ((ptr = snd_malloc_pci_pages(chip->pci, 4096, &ptr_addr)) == NULL) + if (snd_dma_alloc_pages(&chip->dma_dev, 4096, &chip->ac3_tmp_base) < 0) return -ENOMEM; - chip->ac3_tmp_base = ptr; - chip->ac3_tmp_base_addr = ptr_addr; chip->bank_effect[3][0]->base = - chip->bank_effect[3][1]->base = cpu_to_le32(chip->ac3_tmp_base_addr); + chip->bank_effect[3][1]->base = cpu_to_le32(chip->ac3_tmp_base.addr); chip->bank_effect[3][0]->loop_end = chip->bank_effect[3][1]->loop_end = cpu_to_le32(1024); chip->bank_effect[4][0]->base = - chip->bank_effect[4][1]->base = cpu_to_le32(chip->ac3_tmp_base_addr + 2048); + chip->bank_effect[4][1]->base = cpu_to_le32(chip->ac3_tmp_base.addr + 2048); chip->bank_effect[4][0]->loop_end = chip->bank_effect[4][1]->loop_end = cpu_to_le32(1024); @@ -572,9 +567,9 @@ snd_ymfpci_readl(chip, YDSXGR_MAPOFEFFECT) & ~(3 << 3)); spin_unlock_irq(&chip->reg_lock); // snd_ymfpci_irq_wait(chip); - if (chip->ac3_tmp_base) { - snd_free_pci_pages(chip->pci, 4096, chip->ac3_tmp_base, chip->ac3_tmp_base_addr); - chip->ac3_tmp_base = NULL; + if (chip->ac3_tmp_base.area) { + snd_dma_free_pages(&chip->dma_dev, &chip->ac3_tmp_base); + chip->ac3_tmp_base.area = NULL; } return 0; } @@ -1104,7 +1099,8 @@ strcpy(pcm->name, "YMFPCI"); chip->pcm = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 256*1024); if (rpcm) *rpcm = pcm; @@ -1149,7 +1145,8 @@ chip->device_id == PCI_DEVICE_ID_YAMAHA_754 ? "Direct Recording" : "AC'97"); chip->pcm2 = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 256*1024); if (rpcm) *rpcm = pcm; @@ -1193,7 +1190,8 @@ strcpy(pcm->name, "YMFPCI - IEC958"); chip->pcm_spdif = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 256*1024); if (rpcm) *rpcm = pcm; @@ -1237,7 +1235,8 @@ strcpy(pcm->name, "YMFPCI - Rear PCM"); chip->pcm_4ch = pcm; - snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 256*1024); if (rpcm) *rpcm = pcm; @@ -1976,12 +1975,11 @@ chip->work_size; /* work_ptr must be aligned to 256 bytes, but it's already covered with the kernel page allocation mechanism */ - if ((ptr = snd_malloc_pci_pages(chip->pci, size, &ptr_addr)) == NULL) + if (snd_dma_alloc_pages(&chip->dma_dev, size, &chip->work_ptr) < 0) return -ENOMEM; + ptr = chip->work_ptr.area; + ptr_addr = chip->work_ptr.addr; memset(ptr, 0, size); /* for sure */ - chip->work_ptr = ptr; - chip->work_ptr_addr = ptr_addr; - chip->work_ptr_size = size; chip->bank_base_playback = ptr; chip->bank_base_playback_addr = ptr_addr; @@ -2024,7 +2022,7 @@ chip->work_base = ptr; chip->work_base_addr = ptr_addr; - snd_assert(ptr + chip->work_size == chip->work_ptr + chip->work_ptr_size, ); + snd_assert(ptr + chip->work_size == chip->work_ptr.area + chip->work_ptr.bytes, ); snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, chip->bank_base_playback_addr); snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, chip->bank_base_capture_addr); @@ -2107,8 +2105,8 @@ #endif if (chip->reg_area_virt) iounmap((void *)chip->reg_area_virt); - if (chip->work_ptr) - snd_free_pci_pages(chip->pci, chip->work_ptr_size, chip->work_ptr, chip->work_ptr_addr); + if (chip->work_ptr.area) + snd_dma_free_pages(&chip->dma_dev, &chip->work_ptr); if (chip->irq >= 0) free_irq(chip->irq, (void *)chip); @@ -2275,6 +2273,10 @@ return -EBUSY; } chip->irq = pci->irq; + + memset(&chip->dma_dev, 0, sizeof(chip->dma_dev)); + chip->dma_dev.type = SNDRV_DMA_TYPE_DEV; + chip->dma_dev.dev = snd_dma_pci_data(pci); snd_ymfpci_aclink_reset(pci); if (snd_ymfpci_codec_ready(chip, 0) < 0) { diff -Nru a/sound/pcmcia/Kconfig b/sound/pcmcia/Kconfig --- a/sound/pcmcia/Kconfig Sun Mar 7 18:45:35 2004 +++ b/sound/pcmcia/Kconfig Sun Mar 7 18:45:35 2004 @@ -6,13 +6,21 @@ config SND_VXPOCKET tristate "Digigram VXpocket" depends on SND && PCMCIA && ISA + select SND_VX_LIB help Say 'Y' or 'M' to include support for Digigram VXpocket soundcard. config SND_VXP440 tristate "Digigram VXpocket 440" depends on SND && PCMCIA && ISA + select SND_VX_LIB 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 Mar 7 18:45:35 2004 +++ b/sound/pcmcia/Makefile Sun Mar 7 18:45:35 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 Mar 7 18:45:36 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 Mar 7 18:45:36 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 Mar 7 18:45:36 2004 @@ -0,0 +1,148 @@ +/* + * 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 + +#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 Mar 7 18:45:36 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 Mar 7 18:45:36 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 Mar 7 18:45:36 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/Kconfig b/sound/ppc/Kconfig --- a/sound/ppc/Kconfig Sun Mar 7 18:45:35 2004 +++ b/sound/ppc/Kconfig Sun Mar 7 18:45:35 2004 @@ -6,6 +6,7 @@ config SND_POWERMAC tristate "PowerMac (AWACS, DACA, Burgundy, Tumbler, Keywest)" depends on SND + select SND_PCM endmenu diff -Nru a/sound/ppc/pmac.c b/sound/ppc/pmac.c --- a/sound/ppc/pmac.c Sun Mar 7 18:45:36 2004 +++ b/sound/ppc/pmac.c Sun Mar 7 18:45:36 2004 @@ -664,7 +664,9 @@ chip->capture.cur_freqs = chip->freqs_ok; /* preallocate 64k buffer */ - snd_pcm_lib_preallocate_pages_for_all(pcm, 64 * 1024, 64 * 1024, GFP_KERNEL); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, + snd_pcm_dma_flags(GFP_KERNEL), + 64 * 1024, 64 * 1024); return 0; } diff -Nru a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c --- a/sound/ppc/tumbler.c Sun Mar 7 18:45:35 2004 +++ b/sound/ppc/tumbler.c Sun Mar 7 18:45:35 2004 @@ -136,7 +136,7 @@ /* normal operation, all-pass mode */ TAS_REG_MCS2, (1<<1), /* normal output, no deemphasis, A input, power-up */ - TAS_REG_ACS, 0, + TAS_REG_ACS, 2, 0, /* terminator */ }; return send_init_client(i2c, regs); @@ -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/sparc/Kconfig b/sound/sparc/Kconfig --- a/sound/sparc/Kconfig Sun Mar 7 18:45:35 2004 +++ b/sound/sparc/Kconfig Sun Mar 7 18:45:35 2004 @@ -6,11 +6,13 @@ config SND_SUN_AMD7930 tristate "Sun AMD7930" depends on SBUS && SND + select SND_PCM # dep_tristate 'Sun DBRI' CONFIG_SND_SUN_DBRI $CONFIG_SND config SND_SUN_CS4231 tristate "Sun CS4231" depends on SND + select SND_PCM endmenu diff -Nru a/sound/sparc/amd7930.c b/sound/sparc/amd7930.c --- a/sound/sparc/amd7930.c Sun Mar 7 18:45:35 2004 +++ b/sound/sparc/amd7930.c Sun Mar 7 18:45:35 2004 @@ -791,7 +791,9 @@ strcpy(pcm->name, amd->card->shortname); amd->pcm = pcm; - snd_pcm_lib_preallocate_pages_for_all(pcm, 64*1024, 64*1024, GFP_KERNEL); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + 64*1024, 64*1024); return 0; } diff -Nru a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c --- a/sound/sparc/cs4231.c Sun Mar 7 18:45:35 2004 +++ b/sound/sparc/cs4231.c Sun Mar 7 18:45:35 2004 @@ -1570,13 +1570,15 @@ #ifdef EBUS_SUPPORT if (chip->flags & CS4231_FLAG_EBUS) { - snd_pcm_lib_preallocate_pci_pages_for_all(chip->dev_u.pdev, pcm, - 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_PCI, + snd_dma_pci_data(chip->dev_u.pdev) + 64*1024, 128*1024); } else { #endif #ifdef SBUS_SUPPORT - snd_pcm_lib_preallocate_sbus_pages_for_all(chip->dev_u.sdev, pcm, - 64*1024, 128*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_SBUS, + snd_dma_sbus_data(chip->dev_u.sdev), + 64*1024, 128*1024); #endif #ifdef EBUS_SUPPORT } diff -Nru a/sound/synth/Makefile b/sound/synth/Makefile --- a/sound/synth/Makefile Sun Mar 7 18:45:35 2004 +++ b/sound/synth/Makefile Sun Mar 7 18:45:35 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/Kconfig b/sound/usb/Kconfig --- a/sound/usb/Kconfig Sun Mar 7 18:45:35 2004 +++ b/sound/usb/Kconfig Sun Mar 7 18:45:35 2004 @@ -6,6 +6,8 @@ config SND_USB_AUDIO tristate "USB Audio/MIDI driver" depends on SND && USB + select SND_RAWMIDI + select SND_PCM help Say 'Y' or 'M' to include support for USB audio and USB MIDI devices. diff -Nru a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c --- a/sound/usb/usbaudio.c Sun Mar 7 18:45:35 2004 +++ b/sound/usb/usbaudio.c Sun Mar 7 18:45:35 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 */ @@ -1129,15 +1129,20 @@ (! is_playback && attr == EP_ATTR_ADAPTIVE)) && altsd->bNumEndpoints >= 2) { /* check sync-pipe endpoint */ - if (get_endpoint(alts, 1)->bmAttributes != 0x01 || - get_endpoint(alts, 1)->bSynchAddress != 0) { + /* ... and check descriptor size before accessing bSynchAddress + because there is a version of the SB Audigy 2 NX firmware lacking + the audio fields in the endpoint descriptors */ + if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != 0x01 || + (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && + get_endpoint(alts, 1)->bSynchAddress != 0)) { snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n", dev->devnum, fmt->iface, fmt->altsetting); return -EINVAL; } ep = get_endpoint(alts, 1)->bEndpointAddress; - if ((is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) || - (! is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN))) { + if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && + (( is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) || + (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) { snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n", dev->devnum, fmt->iface, fmt->altsetting); return -EINVAL; @@ -1588,7 +1593,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; @@ -1886,7 +1891,9 @@ subs->dev = as->chip->dev; subs->ops = audio_urb_ops[stream]; snd_pcm_lib_preallocate_pages(as->pcm->streams[stream].substream, - 64 * 1024, 128 * 1024, GFP_ATOMIC); + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + 64 * 1024, 128 * 1024); snd_pcm_set_ops(as->pcm, stream, stream == SNDRV_PCM_STREAM_PLAYBACK ? &snd_usb_playback_ops : &snd_usb_capture_ops); @@ -1895,7 +1902,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 +1961,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 +1976,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 +1995,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 +2221,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 +2246,7 @@ dev->devnum, fp->iface, fp->altsetting, fmt[3]); return -1; } + fp->fmt_type = fmt[3]; if (err < 0) return err; #if 1 @@ -2326,6 +2331,9 @@ } csep = snd_usb_find_desc(alts->endpoint[0].extra, alts->endpoint[0].extralen, NULL, USB_DT_CS_ENDPOINT); + /* Creamware Noah has this descriptor after the 2nd endpoint */ + if (!csep && altsd->bNumEndpoints >= 2) + csep = snd_usb_find_desc(alts->endpoint[1].extra, alts->endpoint[1].extralen, NULL, USB_DT_CS_ENDPOINT); if (!csep || csep[0] < 7 || csep[2] != EP_GENERAL) { snd_printk(KERN_ERR "%d:%u:%d : no or invalid class specific endpoint descriptor\n", dev->devnum, iface_no, altno); diff -Nru a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c --- a/sound/usb/usbmidi.c Sun Mar 7 18:45:36 2004 +++ b/sound/usb/usbmidi.c Sun Mar 7 18:45:36 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,118 @@ 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, 0, "%s MIDI"}, + {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"}, + /* Edirol UA-700 */ + {0x0582, 0x002b, 0, "%s MIDI"}, + {0x0582, 0x002b, 1, "%s Control"}, + /* Roland VariOS */ + {0x0582, 0x002f, 0, "%s MIDI"}, + {0x0582, 0x002f, 1, "%s External MIDI"}, + {0x0582, 0x002f, 2, "%s Sync"}, + /* Edirol PCR */ + {0x0582, 0x0033, 0, "%s MIDI"}, + {0x0582, 0x0033, 1, "%s 1"}, + {0x0582, 0x0033, 2, "%s 2"}, + /* BOSS GS-10 */ + {0x0582, 0x003b, 0, "%s MIDI"}, + {0x0582, 0x003b, 1, "%s Control"}, + /* Edirol UA-1000 */ + {0x0582, 0x0044, 0, "%s MIDI"}, + {0x0582, 0x0044, 1, "%s Control"}, + /* Edirol UR-80 */ + {0x0582, 0x0048, 0, "%s MIDI"}, + {0x0582, 0x0048, 1, "%s 1"}, + {0x0582, 0x0048, 2, "%s 2"}, + /* Edirol PCR-A */ + {0x0582, 0x004d, 0, "%s MIDI"}, + {0x0582, 0x004d, 1, "%s 1"}, + {0x0582, 0x004d, 2, "%s 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 Mar 7 18:45:35 2004 +++ b/sound/usb/usbquirks.h Sun Mar 7 18:45:35 2004 @@ -104,7 +104,7 @@ #undef YAMAHA_INTERFACE /* - * Roland/RolandED/Edirol devices + * Roland/RolandED/Edirol/BOSS devices */ { USB_DEVICE(0x0582, 0x0000), @@ -196,8 +196,8 @@ .ifnum = 2, .type = QUIRK_MIDI_FIXED_ENDPOINT, .data = & (const snd_usb_midi_endpoint_info_t) { - .out_cables = 0x0003, - .in_cables = 0x0003 + .out_cables = 0x0005, + .in_cables = 0x0005 } } }, @@ -393,6 +393,32 @@ } }, { + USB_DEVICE(0x0582, 0x001b), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Roland", + .product_name = "MMP-2", + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ + USB_DEVICE(0x0582, 0x001d), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Roland", + .product_name = "V-SYNTH", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ USB_DEVICE(0x0582, 0x0023), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "EDIROL", @@ -489,6 +515,19 @@ } }, { + USB_DEVICE(0x0582, 0x002f), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Roland", + .product_name = "VariOS", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0007, + .in_cables = 0x0007 + } + } +}, +{ USB_DEVICE(0x0582, 0x0033), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "EDIROL", @@ -502,6 +541,110 @@ } }, { + USB_DEVICE(0x0582, 0x0037), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Roland", + .product_name = "Digital Piano", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ + USB_DEVICE_VENDOR_SPEC(0x0582, 0x003b), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "BOSS", + .product_name = "GS-10", + .ifnum = 3, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0003, + .in_cables = 0x0003 + } + } +}, +{ + USB_DEVICE(0x0582, 0x0040), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Roland", + .product_name = "GI-20", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + } +}, +{ + USB_DEVICE(0x0582, 0x0048), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "EDIROL", + .product_name = "UR-80", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0003, + .in_cables = 0x0007 + } + } +}, +{ + USB_DEVICE(0x0582, 0x004d), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "EDIROL", + .product_name = "PCR-A", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0003, + .in_cables = 0x0007 + } + } +}, +{ + USB_DEVICE(0x0582, 0x0065), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "EDIROL", + .product_name = "PCR-1", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0001, + .in_cables = 0x0003 + } + } +}, +{ + /* + * 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 +829,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