diff options
345 files changed, 9146 insertions, 44387 deletions
diff --git a/Documentation/devicetree/bindings/mfd/arizona.txt b/Documentation/devicetree/bindings/mfd/arizona.txt index b37bdde5cfda..bdd017686ea5 100644 --- a/Documentation/devicetree/bindings/mfd/arizona.txt +++ b/Documentation/devicetree/bindings/mfd/arizona.txt @@ -65,45 +65,6 @@ Optional properties: a value that is out of range for a 16 bit register then the chip default will be used. If present exactly five values must be specified. - - wlf,inmode : A list of INn_MODE register values, where n is the number - of input signals. Valid values are 0 (Differential), 1 (Single-ended) and - 2 (Digital Microphone). If absent, INn_MODE registers set to 0 by default. - If present, values must be specified less than or equal to the number of - input signals. If values less than the number of input signals, elements - that have not been specified are set to 0 by default. Entries are: - <IN1, IN2, IN3, IN4> (wm5102, wm5110, wm8280, wm8997) - <IN1A, IN2A, IN1B, IN2B> (wm8998, wm1814) - - wlf,out-mono : A list of boolean values indicating whether each output is - mono or stereo. Position within the list indicates the output affected - (eg. First entry in the list corresponds to output 1). A non-zero value - indicates a mono output. If present, the number of values should be less - than or equal to the number of outputs, if less values are supplied the - additional outputs will be treated as stereo. - - - wlf,dmic-ref : DMIC reference voltage source for each input, can be - selected from either MICVDD or one of the MICBIAS's, defines - (ARIZONA_DMIC_xxxx) are provided in <dt-bindings/mfd/arizona.txt>. If - present, the number of values should be less than or equal to the - number of inputs, unspecified inputs will use the chip default. - - - wlf,max-channels-clocked : The maximum number of channels to be clocked on - each AIF, useful for I2S systems with multiple data lines being mastered. - Specify one cell for each AIF to be configured, specify zero for AIFs that - should be handled normally. - If present, number of cells must be less than or equal to the number of - AIFs. If less than the number of AIFs, for cells that have not been - specified the corresponding AIFs will be treated as default setting. - - - wlf,spk-fmt : PDM speaker data format, must contain 2 cells (OUT5 and OUT6). - See the datasheet for values. - The second cell is ignored for codecs that do not have OUT6 (wm5102, wm8997, - wm8998, wm1814) - - - wlf,spk-mute : PDM speaker mute setting, must contain 2 cells (OUT5 and OUT6). - See the datasheet for values. - The second cell is ignored for codecs that do not have OUT6 (wm5102, wm8997, - wm8998, wm1814) - - DCVDD-supply, MICVDD-supply : Power supplies, only need to be specified if they are being externally supplied. As covered in Documentation/devicetree/bindings/regulator/regulator.txt @@ -112,6 +73,7 @@ Optional properties: Also see child specific device properties: Regulator - ../regulator/arizona-regulator.txt Extcon - ../extcon/extcon-arizona.txt + Sound - ../sound/arizona.txt Example: diff --git a/Documentation/devicetree/bindings/sound/audio-graph-card.txt b/Documentation/devicetree/bindings/sound/audio-graph-card.txt index 6e6720aa33f1..d04ea3b1a1dd 100644 --- a/Documentation/devicetree/bindings/sound/audio-graph-card.txt +++ b/Documentation/devicetree/bindings/sound/audio-graph-card.txt @@ -17,6 +17,7 @@ Below are same as Simple-Card. - bitclock-master - bitclock-inversion - frame-inversion +- mclk-fs - dai-tdm-slot-num - dai-tdm-slot-width - clocks / system-clock-frequency diff --git a/Documentation/devicetree/bindings/sound/audio-graph-scu-card.txt b/Documentation/devicetree/bindings/sound/audio-graph-scu-card.txt index 8b8afe9fcb31..441dd6f29df1 100644 --- a/Documentation/devicetree/bindings/sound/audio-graph-scu-card.txt +++ b/Documentation/devicetree/bindings/sound/audio-graph-scu-card.txt @@ -43,7 +43,7 @@ Example 1. Sampling Rate Conversion label = "sound-card"; prefix = "codec"; routing = "codec Playback", "DAI0 Playback", - "codec Playback", "DAI1 Playback"; + "DAI0 Capture", "codec Capture"; convert-rate = <48000>; dais = <&cpu_port>; @@ -79,7 +79,8 @@ Example 2. 2 CPU 1 Codec (Mixing) label = "sound-card"; prefix = "codec"; routing = "codec Playback", "DAI0 Playback", - "codec Playback", "DAI1 Playback"; + "codec Playback", "DAI1 Playback", + "DAI0 Capture", "codec Capture"; convert-rate = <48000>; dais = <&cpu_port0 diff --git a/Documentation/devicetree/bindings/sound/cs42l56.txt b/Documentation/devicetree/bindings/sound/cs42l56.txt index 4feb0eb27ea4..4ba520a28ae8 100644 --- a/Documentation/devicetree/bindings/sound/cs42l56.txt +++ b/Documentation/devicetree/bindings/sound/cs42l56.txt @@ -55,7 +55,7 @@ Example: codec: codec@4b { compatible = "cirrus,cs42l56"; reg = <0x4b>; - gpio-reset = <&gpio 10 0>; + cirrus,gpio-nreset = <&gpio 10 0>; cirrus,chgfreq-divisor = <0x05>; cirrus.ain1_ref_cfg; cirrus,micbias-lvl = <5>; diff --git a/Documentation/devicetree/bindings/sound/rt5514.txt b/Documentation/devicetree/bindings/sound/rt5514.txt index 929ca6756b02..4f33b0d96afe 100644 --- a/Documentation/devicetree/bindings/sound/rt5514.txt +++ b/Documentation/devicetree/bindings/sound/rt5514.txt @@ -1,22 +1,27 @@ RT5514 audio CODEC -This device supports I2C only. +This device supports both I2C and SPI. Required properties: - compatible : "realtek,rt5514". -- reg : The I2C address of the device. +- reg : the I2C address of the device for I2C, the chip select + number for SPI. Optional properties: - clocks: The phandle of the master clock to the CODEC - clock-names: Should be "mclk" +- interrupt-parent: The phandle for the interrupt controller. +- interrupts: The interrupt number to the cpu. The interrupt specifier format + depends on the interrupt controller. + - realtek,dmic-init-delay-ms - Set the DMIC initial delay (ms) to wait it ready. + Set the DMIC initial delay (ms) to wait it ready for I2C. -Pins on the device (for linking into audio routes) for RT5514: +Pins on the device (for linking into audio routes) for I2C: * DMIC1L * DMIC1R diff --git a/Documentation/devicetree/bindings/sound/rt5663.txt b/Documentation/devicetree/bindings/sound/rt5663.txt index ff381718c517..497bcfc58b71 100644 --- a/Documentation/devicetree/bindings/sound/rt5663.txt +++ b/Documentation/devicetree/bindings/sound/rt5663.txt @@ -19,6 +19,22 @@ Optional properties: Based on the different PCB layout, add the manual offset value to compensate the DC offset for each L and R channel, and they are different between headphone and headset. +- "realtek,impedance_sensing_num" + The matrix row number of the impedance sensing table. + If the value is 0, it means the impedance sensing is not supported. +- "realtek,impedance_sensing_table" + The matrix rows of the impedance sensing table are consisted by impedance + minimum, impedance maximun, volume, DC offset w/o and w/ mic of each L and + R channel accordingly. Example is shown as following. + < 0 300 7 0xffd160 0xffd1c0 0xff8a10 0xff8ab0 + 301 65535 4 0xffe470 0xffe470 0xffb8e0 0xffb8e0> + The first and second column are defined for the impedance range. If the + detected impedance value is in the range, then the volume value of the + third column will be set to codec. In our codec design, each volume value + should compensate different DC offset to avoid the pop sound, and it is + also different between headphone and headset. In the example, the + "realtek,impedance_sensing_num" is 2. It means that there are 2 ranges of + impedance in the impedance sensing function. Pins on the device (for linking into audio routes) for RT5663: diff --git a/Documentation/devicetree/bindings/sound/sgtl5000.txt b/Documentation/devicetree/bindings/sound/sgtl5000.txt index 7a73a9d62015..060cb4a3b47e 100644 --- a/Documentation/devicetree/bindings/sound/sgtl5000.txt +++ b/Documentation/devicetree/bindings/sound/sgtl5000.txt @@ -37,7 +37,7 @@ VDDIO 1.8V 2.5V 3.3V Example: -codec: sgtl5000@0a { +codec: sgtl5000@a { compatible = "fsl,sgtl5000"; reg = <0x0a>; clocks = <&clks 150>; diff --git a/Documentation/devicetree/bindings/sound/st,stm32-sai.txt b/Documentation/devicetree/bindings/sound/st,stm32-sai.txt index f1c5ae59e7c9..1f9cd7095337 100644 --- a/Documentation/devicetree/bindings/sound/st,stm32-sai.txt +++ b/Documentation/devicetree/bindings/sound/st,stm32-sai.txt @@ -10,13 +10,21 @@ Required properties: - reg: Base address and size of SAI common register set. - clocks: Must contain phandle and clock specifier pairs for each entry in clock-names. - - clock-names: Must contain "x8k" and "x11k" + - clock-names: Must contain "pclk" "x8k" and "x11k" + "pclk": Clock which feeds the peripheral bus interface. + Mandatory for "st,stm32h7-sai" compatible. + Not used for "st,stm32f4-sai" compatible. "x8k": SAI parent clock for sampling rates multiple of 8kHz. "x11k": SAI parent clock for sampling rates multiple of 11.025kHz. - interrupts: cpu DAI interrupt line shared by SAI sub-blocks Optional properties: - resets: Reference to a reset controller asserting the SAI + - st,sync: specify synchronization mode. + By default SAI sub-block is in asynchronous mode. + This property sets SAI sub-block as slave of another SAI sub-block. + Must contain the phandle and index of the sai sub-block providing + the synchronization. SAI subnodes: Two subnodes corresponding to SAI sub-block instances A et B can be defined. @@ -52,8 +60,8 @@ sai1: sai1@40015800 { #size-cells = <1>; ranges = <0 0x40015800 0x400>; reg = <0x40015800 0x4>; - clocks = <&rcc PLL1_Q>, <&rcc PLL2_P>; - clock-names = "x8k", "x11k"; + clocks = <&rcc SAI1_CK>, <&rcc PLL1_Q>, <&rcc PLL2_P>; + clock-names = "pclk", "x8k", "x11k"; interrupts = <87>; sai1a: audio-controller@40015804 { diff --git a/Documentation/devicetree/bindings/sound/tfa9879.txt b/Documentation/devicetree/bindings/sound/tfa9879.txt new file mode 100644 index 000000000000..23ba522d9e2b --- /dev/null +++ b/Documentation/devicetree/bindings/sound/tfa9879.txt @@ -0,0 +1,23 @@ +NXP TFA9879 class-D audio amplifier + +Required properties: + +- compatible : "nxp,tfa9879" + +- reg : the I2C address of the device + +Example: + +&i2c1 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c1>; + status = "okay"; + + codec: tfa9879@6c { + #sound-dai-cells = <0>; + compatible = "nxp,tfa9879"; + reg = <0x6c>; + }; +}; + diff --git a/Documentation/devicetree/bindings/sound/wlf,arizona.txt b/Documentation/devicetree/bindings/sound/wlf,arizona.txt new file mode 100644 index 000000000000..e172c62dc2df --- /dev/null +++ b/Documentation/devicetree/bindings/sound/wlf,arizona.txt @@ -0,0 +1,53 @@ +Cirrus Logic Arizona class audio SoCs + +These devices are audio SoCs with extensive digital capabilities and a range +of analogue I/O. + +This document lists sound specific bindings, see the primary binding +document: + ../mfd/arizona.txt + +Optional properties: + + - wlf,inmode : A list of INn_MODE register values, where n is the number + of input signals. Valid values are 0 (Differential), 1 (Single-ended) and + 2 (Digital Microphone). If absent, INn_MODE registers set to 0 by default. + If present, values must be specified less than or equal to the number of + input signals. If values less than the number of input signals, elements + that have not been specified are set to 0 by default. Entries are: + <IN1, IN2, IN3, IN4> (wm5102, wm5110, wm8280, wm8997) + <IN1A, IN2A, IN1B, IN2B> (wm8998, wm1814) + - wlf,out-mono : A list of boolean values indicating whether each output is + mono or stereo. Position within the list indicates the output affected + (eg. First entry in the list corresponds to output 1). A non-zero value + indicates a mono output. If present, the number of values should be less + than or equal to the number of outputs, if less values are supplied the + additional outputs will be treated as stereo. + + - wlf,dmic-ref : DMIC reference voltage source for each input, can be + selected from either MICVDD or one of the MICBIAS's, defines + (ARIZONA_DMIC_xxxx) are provided in <dt-bindings/mfd/arizona.txt>. If + present, the number of values should be less than or equal to the + number of inputs, unspecified inputs will use the chip default. + + - wlf,max-channels-clocked : The maximum number of channels to be clocked on + each AIF, useful for I2S systems with multiple data lines being mastered. + Specify one cell for each AIF to be configured, specify zero for AIFs that + should be handled normally. + If present, number of cells must be less than or equal to the number of + AIFs. If less than the number of AIFs, for cells that have not been + specified the corresponding AIFs will be treated as default setting. + + - wlf,spk-fmt : PDM speaker data format, must contain 2 cells (OUT5 and OUT6). + See the datasheet for values. + The second cell is ignored for codecs that do not have OUT6 (wm5102, wm8997, + wm8998, wm1814) + + - wlf,spk-mute : PDM speaker mute setting, must contain 2 cells (OUT5 and OUT6). + See the datasheet for values. + The second cell is ignored for codecs that do not have OUT6 (wm5102, wm8997, + wm8998, wm1814) + + - wlf,out-volume-limit : The volume limit value that should be applied to each + output channel. See the datasheet for exact values. Channels are specified + in the order OUT1L, OUT1R, OUT2L, OUT2R, etc. diff --git a/Documentation/sound/hd-audio/models.rst b/Documentation/sound/hd-audio/models.rst index 773d2bfacc6c..1fee5a4f6660 100644 --- a/Documentation/sound/hd-audio/models.rst +++ b/Documentation/sound/hd-audio/models.rst @@ -82,6 +82,8 @@ tpt460 Lenovo Thinkpad T460/560 setup dual-codecs Lenovo laptops with dual codecs +alc700-ref + Intel reference board with ALC700 codec ALC66x/67x/892 ============== diff --git a/Documentation/sound/oss/ALS b/Documentation/sound/oss/ALS deleted file mode 100644 index bf10bed4574b..000000000000 --- a/Documentation/sound/oss/ALS +++ /dev/null @@ -1,66 +0,0 @@ -ALS-007/ALS-100/ALS-200 based sound cards -========================================= - -Support for sound cards based around the Avance Logic -ALS-007/ALS-100/ALS-200 chip is included. These chips are a single -chip PnP sound solution which is mostly hardware compatible with the -Sound Blaster 16 card, with most differences occurring in the use of -the mixer registers. For this reason the ALS code is integrated -as part of the Sound Blaster 16 driver (adding only 800 bytes to the -SB16 driver). - -To use an ALS sound card under Linux, enable the following options as -modules in the sound configuration section of the kernel config: - - 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support - - FM synthesizer (YM3812/OPL-3) support - - standalone MPU401 support may be required for some cards; for the - ALS-007, when using isapnptools, it is required -Since the ALS-007/100/200 are PnP cards, ISAPnP support should probably be -compiled in. If kernel level PnP support is not included, isapnptools will -be required to configure the card before the sound modules are loaded. - -When using kernel level ISAPnP, the kernel should correctly identify and -configure all resources required by the card when the "sb" module is -inserted. Note that the ALS-007 does not have a 16 bit DMA channel and that -the MPU401 interface on this card uses a different interrupt to the audio -section. This should all be correctly configured by the kernel; if problems -with the MPU401 interface surface, try using the standalone MPU401 module, -passing "0" as the "sb" module's "mpu_io" module parameter to prevent the -soundblaster driver attempting to register the MPU401 itself. The onboard -synth device can be accessed using the "opl3" module. - -If isapnptools is used to wake up the sound card (as in 2.2.x), the settings -of the card's resources should be passed to the kernel modules ("sb", "opl3" -and "mpu401") using the module parameters. When configuring an ALS-007, be -sure to specify different IRQs for the audio and MPU401 sections - this card -requires they be different. For "sb", "io", "irq" and "dma" should be set -to the same values used to configure the audio section of the card with -isapnp. "dma16" should be explicitly set to "-1" for an ALS-007 since this -card does not have a 16 bit dma channel; if not specified the kernel will -default to using channel 5 anyway which will cause audio not to work. -"mpu_io" should be set to 0. The "io" parameter of the "opl3" module should -also agree with the setting used by isapnp. To get the MPU401 interface -working on an ALS-007 card, the "mpu401" module will be required since this -card uses separate IRQs for the audio and MPU401 sections and there is no -parameter available to pass a different IRQ to the "sb" driver (whose -inbuilt MPU401 driver would otherwise be fine). Insert the mpu401 module -passing appropriate values using the "io" and "irq" parameters. - -The resulting sound driver will provide the following capabilities: - - 8 and 16 bit audio playback - - 8 and 16 bit audio recording - - Software selection of record source (line in, CD, FM, mic, master) - - Record and playback of midi data via the external MPU-401 - - Playback of midi data using inbuilt FM synthesizer - - Control of the ALS-007 mixer via any OSS-compatible mixer programs. - Controls available are Master (L&R), Line in (L&R), CD (L&R), - DSP/PCM/audio out (L&R), FM (L&R) and Mic in (mono). - -Jonathan Woithe -jwoithe@just42.net -30 March 1998 - -Modified 2000-02-26 by Dave Forrest, drf5n@virginia.edu to add ALS100/ALS200 -Modified 2000-04-10 by Paul Laufer, pelaufer@csupomona.edu to add ISAPnP info. -Modified 2000-11-19 by Jonathan Woithe, jwoithe@just42.net - - updated information for kernel 2.4.x. diff --git a/Documentation/sound/oss/AudioExcelDSP16 b/Documentation/sound/oss/AudioExcelDSP16 deleted file mode 100644 index ea8549faede9..000000000000 --- a/Documentation/sound/oss/AudioExcelDSP16 +++ /dev/null @@ -1,101 +0,0 @@ -Driver ------- - -Information about Audio Excel DSP 16 driver can be found in the source -file aedsp16.c -Please, read the head of the source before using it. It contain useful -information. - -Configuration -------------- - -The Audio Excel configuration, is now done with the standard Linux setup. -You have to configure the sound card (Sound Blaster or Microsoft Sound System) -and, if you want it, the Roland MPU-401 (do not use the Sound Blaster MPU-401, -SB-MPU401) in the main driver menu. Activate the lowlevel drivers then select -the Audio Excel hardware that you want to initialize. Check the IRQ/DMA/MIRQ -of the Audio Excel initialization: it must be the same as the SBPRO (or MSS) -setup. If the parameters are different, correct it. -I you own a Gallant's audio card based on SC-6600, activate the SC-6600 support. -If you want to change the configuration of the sound board, be sure to -check off all the configuration items before re-configure it. - -Module parameters ------------------ -To use this driver as a module, you must configure some module parameters, to -set up I/O addresses, IRQ lines and DMA channels. Some parameters are -mandatory while some others are optional. Here a list of parameters you can -use with this module: - -Name Description -==== =========== -MANDATORY -io I/O base address (0x220 or 0x240) -irq irq line (5, 7, 9, 10 or 11) -dma dma channel (0, 1 or 3) - -OPTIONAL -mss_base I/O base address for activate MSS mode (default SBPRO) - (0x530 or 0xE80) -mpu_base I/O base address for activate MPU-401 mode - (0x300, 0x310, 0x320 or 0x330) -mpu_irq MPU-401 irq line (5, 7, 9, 10 or 0) - -A configuration file in /etc/modprobe.d/ directory will have lines like this: - -options opl3 io=0x388 -options ad1848 io=0x530 irq=11 dma=3 -options aedsp16 io=0x220 irq=11 dma=3 mss_base=0x530 - -Where the aedsp16 options are the options for this driver while opl3 and -ad1848 are the corresponding options for the MSS and OPL3 modules. - -Loading MSS and OPL3 needs to pre load the aedsp16 module to set up correctly -the sound card. Installation dependencies must be written in configuration -files under /etc/modprobe.d/ directory: - -softdep ad1848 pre: aedsp16 -softdep opl3 pre: aedsp16 - -Then you must load the sound modules stack in this order: -sound -> aedsp16 -> [ ad1848, opl3 ] - -With the above configuration, loading ad1848 or opl3 modules, will -automatically load all the sound stack. - -Sound cards supported ---------------------- -This driver supports the SC-6000 and SC-6600 based Gallant's sound card. -It don't support the Audio Excel DSP 16 III (try the SC-6600 code). -I'm working on the III version of the card: if someone have useful -information about it, please let me know. -For all the non-supported audio cards, you have to boot MS-DOS (or WIN95) -activating the audio card with the MS-DOS device driver, then you have to -<ctrl>-<alt>-<del> and boot Linux. -Follow these steps: - -1) Compile Linux kernel with standard sound driver, using the emulation - you want, with the parameters of your audio card, - e.g. Microsoft Sound System irq10 dma3 -2) Install your new kernel as the default boot kernel. -3) Boot MS-DOS and configure the audio card with the boot time device - driver, for MSS irq10 dma3 in our example. -4) <ctrl>-<alt>-<del> and boot Linux. This will maintain the DOS configuration - and will boot the new kernel with sound driver. The sound driver will find - the audio card and will recognize and attach it. - -Reports on User successes -------------------------- - -> Date: Mon, 29 Jul 1996 08:35:40 +0100 -> From: Mr S J Greenaway <sjg95@unixfe.rl.ac.uk> -> To: riccardo@cdc8g5.cdc.polimi.it (Riccardo Facchetti) -> Subject: Re: Audio Excel DSP 16 initialization code -> -> Just to let you know got my Audio Excel (emulating a MSS) working -> with my original SB16, thanks for the driver! - - -Last revised: 20 August 1998 -Riccardo Facchetti -fizban@tin.it diff --git a/Documentation/sound/oss/CMI8330 b/Documentation/sound/oss/CMI8330 deleted file mode 100644 index 8a5fd1611c6f..000000000000 --- a/Documentation/sound/oss/CMI8330 +++ /dev/null @@ -1,152 +0,0 @@ -Documentation for CMI 8330 (SoundPRO) -------------------------------------- -Alessandro Zummo <azummo@ita.flashnet.it> - -( Be sure to read Documentation/sound/oss/SoundPro too ) - - -This adapter is now directly supported by the sb driver. - - The only thing you have to do is to compile the kernel sound -support as a module and to enable kernel ISAPnP support, -as shown below. - - -CONFIG_SOUND=m -CONFIG_SOUND_SB=m - -CONFIG_PNP=y -CONFIG_ISAPNP=y - - -and optionally: - - -CONFIG_SOUND_MPU401=m - - for MPU401 support. - - -(I suggest you to use "make menuconfig" or "make xconfig" - for a more comfortable configuration editing) - - - -Then you can do - - modprobe sb - -and everything will be (hopefully) configured. - -You should get something similar in syslog: - -sb: CMI8330 detected. -sb: CMI8330 sb base located at 0x220 -sb: CMI8330 mpu base located at 0x330 -sb: CMI8330 mail reports to Alessandro Zummo <azummo@ita.flashnet.it> -sb: ISAPnP reports CMI 8330 SoundPRO at i/o 0x220, irq 7, dma 1,5 - - - - -The old documentation file follows for reference -purposes. - - -How to enable CMI 8330 (SOUNDPRO) soundchip on Linux ------------------------------------------- -Stefan Laudat <Stefan.Laudat@asit.ro> - -[Note: The CMI 8338 is unrelated and is supported by cmpci.o] - - - In order to use CMI8330 under Linux you just have to use a proper isapnp.conf, a good isapnp and a little bit of patience. I use isapnp 1.17, but -you may get a better one I guess at http://www.roestock.demon.co.uk/isapnptools/. - - Of course you will have to compile kernel sound support as module, as shown below: - -CONFIG_SOUND=m -CONFIG_SOUND_OSS=m -CONFIG_SOUND_SB=m -CONFIG_SOUND_ADLIB=m -CONFIG_SOUND_MPU401=m -# Mikro$chaft sound system (kinda useful here ;)) -CONFIG_SOUND_MSS=m - - The /etc/isapnp.conf file will be: - -<snip below> - - -(READPORT 0x0203) -(ISOLATE PRESERVE) -(IDENTIFY *) -(VERBOSITY 2) -(CONFLICT (IO FATAL)(IRQ FATAL)(DMA FATAL)(MEM FATAL)) # or WARNING -(VERIFYLD N) - - -# WSS - -(CONFIGURE CMI0001/16777472 (LD 0 -(IO 0 (SIZE 8) (BASE 0x0530)) -(IO 1 (SIZE 8) (BASE 0x0388)) -(INT 0 (IRQ 7 (MODE +E))) -(DMA 0 (CHANNEL 0)) -(NAME "CMI0001/16777472[0]{CMI8330/C3D Audio Adapter}") -(ACT Y) -)) - -# MPU - -(CONFIGURE CMI0001/16777472 (LD 1 -(IO 0 (SIZE 2) (BASE 0x0330)) -(INT 0 (IRQ 11 (MODE +E))) -(NAME "CMI0001/16777472[1]{CMI8330/C3D Audio Adapter}") -(ACT Y) -)) - -# Joystick - -(CONFIGURE CMI0001/16777472 (LD 2 -(IO 0 (SIZE 8) (BASE 0x0200)) -(NAME "CMI0001/16777472[2]{CMI8330/C3D Audio Adapter}") -(ACT Y) -)) - -# SoundBlaster - -(CONFIGURE CMI0001/16777472 (LD 3 -(IO 0 (SIZE 16) (BASE 0x0220)) -(INT 0 (IRQ 5 (MODE +E))) -(DMA 0 (CHANNEL 1)) -(DMA 1 (CHANNEL 5)) -(NAME "CMI0001/16777472[3]{CMI8330/C3D Audio Adapter}") -(ACT Y) -)) - - -(WAITFORKEY) - -<end of snip> - - The module sequence is trivial: - -/sbin/insmod soundcore -/sbin/insmod sound -/sbin/insmod uart401 -# insert this first -/sbin/insmod ad1848 io=0x530 irq=7 dma=0 soundpro=1 -# The sb module is an alternative to the ad1848 (Microsoft Sound System) -# Anyhow, this is full duplex and has MIDI -/sbin/insmod sb io=0x220 dma=1 dma16=5 irq=5 mpu_io=0x330 - - - -Alma Chao <elysian@ethereal.torsion.org> suggests the following in -a /etc/modprobe.d/*conf file: - -alias sound ad1848 -alias synth0 opl3 -options ad1848 io=0x530 irq=7 dma=0 soundpro=1 -options opl3 io=0x388 diff --git a/Documentation/sound/oss/ESS b/Documentation/sound/oss/ESS deleted file mode 100644 index bba93b4d2def..000000000000 --- a/Documentation/sound/oss/ESS +++ /dev/null @@ -1,34 +0,0 @@ -Documentation for the ESS AudioDrive chips - -In 2.4 kernels the SoundBlaster driver not only tries to detect an ESS chip, it -tries to detect the type of ESS chip too. The correct detection of the chip -doesn't always succeed however, so unless you use the kernel isapnp facilities -(and you chip is pnp capable) the default behaviour is 2.0 behaviour which -means: only detect ES688 and ES1688. - -All ESS chips now have a recording level setting. This is a need-to-have for -people who want to use their ESS for recording sound. - -Every chip that's detected as a later-than-es1688 chip has a 6 bits logarithmic -master volume control. - -Every chip that's detected as a ES1887 now has Full Duplex support. Made a -little testprogram that shows that is works, haven't seen a real program that -needs this however. - -For ESS chips an additional parameter "esstype" can be specified. This controls -the (auto) detection of the ESS chips. It can have 3 kinds of values: - --1 Act like 2.0 kernels: only detect ES688 or ES1688. -0 Try to auto-detect the chip (may fail for ES1688) -688 The chip will be treated as ES688 -1688 ,, ,, ,, ,, ,, ,, ES1688 -1868 ,, ,, ,, ,, ,, ,, ES1868 -1869 ,, ,, ,, ,, ,, ,, ES1869 -1788 ,, ,, ,, ,, ,, ,, ES1788 -1887 ,, ,, ,, ,, ,, ,, ES1887 -1888 ,, ,, ,, ,, ,, ,, ES1888 - -Because Full Duplex is supported for ES1887 you can specify a second DMA -channel by specifying module parameter dma16. It can be one of: 0, 1, 3 or 5. - diff --git a/Documentation/sound/oss/ESS1868 b/Documentation/sound/oss/ESS1868 deleted file mode 100644 index 55e922f21bc0..000000000000 --- a/Documentation/sound/oss/ESS1868 +++ /dev/null @@ -1,55 +0,0 @@ -Documentation for the ESS1868F AudioDrive PnP sound card - -The ESS1868 sound card is a PnP ESS1688-compatible 16-bit sound card. - -It should be automatically detected by the Linux Kernel isapnp support when you -load the sb.o module. Otherwise you should take care of: - - * The ESS1868 does not allow use of a 16-bit DMA, thus DMA 0, 1, 2, and 3 - may only be used. - - * isapnptools version 1.14 does work with ESS1868. Earlier versions might - not. - - * Sound support MUST be compiled as MODULES, not statically linked - into the kernel. - - -NOTE: this is only needed when not using the kernel isapnp support! - -For configuring the sound card's I/O addresses, IRQ and DMA, here is a -sample copy of the isapnp.conf directives regarding the ESS1868: - -(CONFIGURE ESS1868/-1 (LD 1 -(IO 0 (BASE 0x0220)) -(IO 1 (BASE 0x0388)) -(IO 2 (BASE 0x0330)) -(DMA 0 (CHANNEL 1)) -(INT 0 (IRQ 5 (MODE +E))) -(ACT Y) -)) - -(for a full working isapnp.conf file, remember the -(ISOLATE) -(IDENTIFY *) -at the beginning and the -(WAITFORKEY) -at the end.) - -In this setup, the main card I/O is 0x0220, FM synthesizer is 0x0388, and -the MPU-401 MIDI port is located at 0x0330. IRQ is IRQ 5, DMA is channel 1. - -After configuring the sound card via isapnp, to use the card you must load -the sound modules with the proper I/O information. Here is my setup: - -# ESS1868F AudioDrive initialization - -/sbin/modprobe sound -/sbin/insmod uart401 -/sbin/insmod sb io=0x220 irq=5 dma=1 dma16=-1 -/sbin/insmod mpu401 io=0x330 -/sbin/insmod opl3 io=0x388 -/sbin/insmod v_midi - -opl3 is the FM synthesizer -/sbin/insmod opl3 io=0x388 diff --git a/Documentation/sound/oss/Introduction b/Documentation/sound/oss/Introduction deleted file mode 100644 index 42da2d8fa372..000000000000 --- a/Documentation/sound/oss/Introduction +++ /dev/null @@ -1,459 +0,0 @@ -Introduction Notes on Modular Sound Drivers and Soundcore -Wade Hampton -2/14/2001 - -Purpose: -======== -This document provides some general notes on the modular -sound drivers and their configuration, along with the -support modules sound.o and soundcore.o. - -Note, some of this probably should be added to the Sound-HOWTO! - -Note, soundlow.o was present with 2.2 kernels but is not -required for 2.4.x kernels. References have been removed -to this. - - -Copying: -======== -none - - -History: -======== -0.1.0 11/20/1998 First version, draft -1.0.0 11/1998 Alan Cox changes, incorporation in 2.2.0 - as Documentation/sound/oss/Introduction -1.1.0 6/30/1999 Second version, added notes on making the drivers, - added info on multiple sound cards of similar types,] - added more diagnostics info, added info about esd. - added info on OSS and ALSA. -1.1.1 19991031 Added notes on sound-slot- and sound-service. - (Alan Cox) -1.1.2 20000920 Modified for Kernel 2.4 (Christoph Hellwig) -1.1.3 20010214 Minor notes and corrections (Wade Hampton) - Added examples of sound-slot-0, etc. - - -Modular Sound Drivers: -====================== - -Thanks to the GREAT work by Alan Cox (alan@lxorguk.ukuu.org.uk), - -[And Oleg Drokin, Thomas Sailer, Andrew Veliath and more than a few - others - not to mention Hannu's original code being designed well - enough to cope with that kind of chopping up](Alan) - -the standard Linux kernels support a modular sound driver. From -Alan's comments in linux/drivers/sound/README.FIRST: - - The modular sound driver patches were funded by Red Hat Software - (www.redhat.com). The sound driver here is thus a modified version of - Hannu's code. Please bear that in mind when considering the appropriate - forums for bug reporting. - -The modular sound drivers may be loaded via insmod or modprobe. -To support all the various sound modules, there are two general -support modules that must be loaded first: - - soundcore.o: Top level handler for the sound system, provides - a set of functions for registration of devices - by type. - - sound.o: Common sound functions required by all modules. - -For the specific sound modules (e.g., sb.o for the Soundblaster), -read the documentation on that module to determine what options -are available, for example IRQ, address, DMA. - -Warning, the options for different cards sometime use different names -for the same or a similar feature (dma1= versus dma16=). As a last -resort, inspect the code (search for module_param). - -Notes: - -1. There is a new OpenSource sound driver called ALSA which is - currently under development: http://www.alsa-project.org/ - The ALSA drivers support some newer hardware that may not - be supported by this sound driver and also provide some - additional features. - -2. The commercial OSS driver may be obtained from the site: - http://www.opensound.com. This may be used for cards that - are unsupported by the kernel driver, or may be used - by other operating systems. - -3. The enlightenment sound daemon may be used for playing - multiple sounds at the same time via a single card, eliminating - some of the requirements for multiple sound card systems. For - more information, see: http://www.tux.org/~ricdude/EsounD.html - The "esd" program may be used with the real-player and mpeg - players like mpg123 and x11amp. The newer real-player - and some games even include built-in support for ESD! - - -Building the Modules: -===================== - -This document does not provide full details on building the -kernel, etc. The notes below apply only to making the kernel -sound modules. If this conflicts with the kernel's README, -the README takes precedence. - -1. To make the kernel sound modules, cd to your /usr/src/linux - directory (typically) and type make config, make menuconfig, - or make xconfig (to start the command line, dialog, or x-based - configuration tool). - -2. Select the Sound option and a dialog will be displayed. - -3. Select M (module) for "Sound card support". - -4. Select your sound driver(s) as a module. For ProAudio, Sound - Blaster, etc., select M (module) for OSS sound modules. - [thanks to Marvin Stodolsky <stodolsk@erols.com>]A - -5. Make the kernel (e.g., make bzImage), and install the kernel. - -6. Make the modules and install them (make modules; make modules_install). - -Note, for 2.5.x kernels, make sure you have the newer module-init-tools -installed or modules will not be loaded properly. 2.5.x requires an -updated module-init-tools. - - -Plug and Play (PnP: -=================== - -If the sound card is an ISA PnP card, isapnp may be used -to configure the card. See the file isapnp.txt in the -directory one level up (e.g., /usr/src/linux/Documentation). - -Also the 2.4.x kernels provide PnP capabilities, see the -file NEWS in this directory. - -PCI sound cards are highly recommended, as they are far -easier to configure and from what I have read, they use -less resources and are more CPU efficient. - - -INSMOD: -======= - -If loading via insmod, the common modules must be loaded in the -order below BEFORE loading the other sound modules. The card-specific -modules may then be loaded (most require parameters). For example, -I use the following via a shell script to load my SoundBlaster: - -SB_BASE=0x240 -SB_IRQ=9 -SB_DMA=3 -SB_DMA2=5 -SB_MPU=0x300 -# -echo Starting sound -/sbin/insmod soundcore -/sbin/insmod sound -# -echo Starting sound blaster.... -/sbin/insmod uart401 -/sbin/insmod sb io=$SB_BASE irq=$SB_IRQ dma=$SB_DMA dma16=$SB_DMA2 mpu_io=$SB_MP - -When using sound as a module, I typically put these commands -in a file such as /root/soundon.sh. - - -MODPROBE: -========= - -If loading via modprobe, these common files are automatically loaded when -requested by modprobe. For example, my /etc/modprobe.d/oss.conf contains: - -alias sound sb -options sb io=0x240 irq=9 dma=3 dma16=5 mpu_io=0x300 - -All you need to do to load the module is: - - /sbin/modprobe sb - - -Sound Status: -============= - -The status of sound may be read/checked by: - cat (anyfile).au >/dev/audio - -[WWH: This may not work properly for SoundBlaster PCI 128 cards -such as the es1370/1 (see the es1370/1 files in this directory) -as they do not automatically support uLaw on /dev/audio.] - -The status of the modules and which modules depend on -which other modules may be checked by: - /sbin/lsmod - -/sbin/lsmod should show something like the following: - sb 26280 0 - uart401 5640 0 [sb] - sound 57112 0 [sb uart401] - soundcore 1968 8 [sb sound] - - -Removing Sound: -=============== - -Sound may be removed by using /sbin/rmmod in the reverse order -in which you load the modules. Note, if a program has a sound device -open (e.g., xmixer), that module (and the modules on which it -depends) may not be unloaded. - -For example, I use the following to remove my Soundblaster (rmmod -in the reverse order in which I loaded the modules): - -/sbin/rmmod sb -/sbin/rmmod uart401 -/sbin/rmmod sound -/sbin/rmmod soundcore - -When using sound as a module, I typically put these commands -in a script such as /root/soundoff.sh. - - -Removing Sound for use with OSS: -================================ - -If you get really stuck or have a card that the kernel modules -will not support, you can get a commercial sound driver from -http://www.opensound.com. Before loading the commercial sound -driver, you should do the following: - -1. remove sound modules (detailed above) -2. remove the sound modules from /etc/modprobe.d/*.conf -3. move the sound modules from /lib/modules/<kernel>/misc - (for example, I make a /lib/modules/<kernel>/misc/tmp - directory and copy the sound module files to that - directory). - - -Multiple Sound Cards: -===================== - -The sound drivers will support multiple sound cards and there -are some great applications like multitrack that support them. -Typically, you need two sound cards of different types. Note, this -uses more precious interrupts and DMA channels and sometimes -can be a configuration nightmare. I have heard reports of 3-4 -sound cards (typically I only use 2). You can sometimes use -multiple PCI sound cards of the same type. - -On my machine I have two sound cards (cs4232 and Soundblaster Vibra -16). By loading sound as modules, I can control which is the first -sound device (/dev/dsp, /dev/audio, /dev/mixer) and which is -the second. Normally, the cs4232 (Dell sound on the motherboard) -would be the first sound device, but I prefer the Soundblaster. -All you have to do is to load the one you want as /dev/dsp -first (in my case "sb") and then load the other one -(in my case "cs4232"). - -If you have two cards of the same type that are jumpered -cards or different PnP revisions, you may load the same -module twice. For example, I have a SoundBlaster vibra 16 -and an older SoundBlaster 16 (jumpers). To load the module -twice, you need to do the following: - -1. Copy the sound modules to a new name. For example - sb.o could be copied (or symlinked) to sb1.o for the - second SoundBlaster. - -2. Make a second entry in /etc/modprobe.d/*conf, for example, - sound1 or sb1. This second entry should refer to the - new module names for example sb1, and should include - the I/O, etc. for the second sound card. - -3. Update your soundon.sh script, etc. - -Warning: I have never been able to get two PnP sound cards of the -same type to load at the same time. I have tried this several times -with the Soundblaster Vibra 16 cards. OSS has indicated that this -is a PnP problem.... If anyone has any luck doing this, please -send me an E-MAIL. PCI sound cards should not have this problem.a -Since this was originally release, I have received a couple of -mails from people who have accomplished this! - -NOTE: In Linux 2.4 the Sound Blaster driver (and only this one yet) -supports multiple cards with one module by default. -Read the file 'Soundblaster' in this directory for details. - - -Sound Problems: -=============== - -First RTFM (including the troubleshooting section -in the Sound-HOWTO). - -1) If you are having problems loading the modules (for - example, if you get device conflict errors) try the - following: - - A) If you have Win95 or NT on the same computer, - write down what addresses, IRQ, and DMA channels - those were using for the same hardware. You probably - can use these addresses, IRQs, and DMA channels. - You should really do this BEFORE attempting to get - sound working! - - B) Check (cat) /proc/interrupts, /proc/ioports, - and /proc/dma. Are you trying to use an address, - IRQ or DMA port that another device is using? - - C) Check (cat) /proc/isapnp - - D) Inspect your /var/log/messages file. Often that will - indicate what IRQ or IO port could not be obtained. - - E) Try another port or IRQ. Note this may involve - using the PnP tools to move the sound card to - another location. Sometimes this is the only way - and it is more or less trial and error. - -2) If you get motor-boating (the same sound or part of a - sound clip repeated), you probably have either an IRQ - or DMA conflict. Move the card to another IRQ or DMA - port. This has happened to me when playing long files - when I had an IRQ conflict. - -3. If you get dropouts or pauses when playing high sample - rate files such as using mpg123 or x11amp/xmms, you may - have too slow of a CPU and may have to use the options to - play the files at 1/2 speed. For example, you may use - the -2 or -4 option on mpg123. You may also get this - when trying to play mpeg files stored on a CD-ROM - (my Toshiba T8000 PII/366 sometimes has this problem). - -4. If you get "cannot access device" errors, your /dev/dsp - files, etc. may be set to owner root, mode 600. You - may have to use the command: - chmod 666 /dev/dsp /dev/mixer /dev/audio - -5. If you get "device busy" errors, another program has the - sound device open. For example, if using the Enlightenment - sound daemon "esd", the "esd" program has the sound device. - If using "esd", please RTFM the docs on ESD. For example, - esddsp <program> may be used to play files via a non-esd - aware program. - -6) Ask for help on the sound list or send E-MAIL to the - sound driver author/maintainer. - -7) Turn on debug in drivers/sound/sound_config.h (DEB, DDB, MDB). - -8) If the system reports insufficient DMA memory then you may want to - load sound with the "dmabufs=1" option. Or in /etc/conf.modules add - - preinstall sound dmabufs=1 - - This makes the sound system allocate its buffers and hang onto them. - - You may also set persistent DMA when building a 2.4.x kernel. - - -Configuring Sound: -================== - -There are several ways of configuring your sound: - -1) On the kernel command line (when using the sound driver(s) - compiled in the kernel). Check the driver source and - documentation for details. - -2) On the command line when using insmod or in a bash script - using command line calls to load sound. - -3) In /etc/modprobe.d/*conf when using modprobe. - -4) Via Red Hat's GPL'd /usr/sbin/sndconfig program (text based). - -5) Via the OSS soundconf program (with the commercial version - of the OSS driver. - -6) By just loading the module and let isapnp do everything relevant - for you. This works only with a few drivers yet and - of course - - only with isapnp hardware. - -And I am sure, several other ways. - -Anyone want to write a linuxconf module for configuring sound? - - -Module Loading: -=============== - -When a sound card is first referenced and sound is modular, the sound system -will ask for the sound devices to be loaded. Initially it requests that -the driver for the sound system is loaded. It then will ask for -sound-slot-0, where 0 is the first sound card. (sound-slot-1 the second and -so on). Thus you can do - -alias sound-slot-0 sb - -To load a soundblaster at this point. If the slot loading does not provide -the desired device - for example a soundblaster does not directly provide -a midi synth in all cases then it will request "sound-service-0-n" where n -is - - 0 Mixer - - 2 MIDI - - 3, 4 DSP audio - - -For example, I use the following to load my Soundblaster PCI 128 -(ES 1371) card first, followed by my SoundBlaster Vibra 16 card, -then by my TV card: - -# Load the Soundblaster PCI 128 as /dev/dsp, /dev/dsp1, /dev/mixer -alias sound-slot-0 es1371 - -# Load the Soundblaster Vibra 16 as /dev/dsp2, /dev/mixer1 -alias sound-slot-1 sb -options sb io=0x240 irq=5 dma=1 dma16=5 mpu_io=0x330 - -# Load the BTTV (TV card) as /dev/mixer2 -alias sound-slot-2 bttv -alias sound-service-2-0 tvmixer - -pre-install bttv modprobe tuner ; modprobe tvmixer -pre-install tvmixer modprobe msp3400; modprobe tvaudio -options tuner debug=0 type=8 -options bttv card=0 radio=0 pll=0 - - -For More Information (RTFM): -============================ -1) Information on kernel modules: manual pages for insmod and modprobe. - -2) Information on PnP, RTFM manual pages for isapnp. - -3) Sound-HOWTO and Sound-Playing-HOWTO. - -4) OSS's WWW site at http://www.opensound.com. - -5) All the files in Documentation/sound. - -6) The comments and code in linux/drivers/sound. - -7) The sndconfig and rhsound documentation from Red Hat. - -8) The Linux-sound mailing list: sound-list@redhat.com. - -9) Enlightenment documentation (for info on esd) - http://www.tux.org/~ricdude/EsounD.html. - -10) ALSA home page: http://www.alsa-project.org/ - - -Contact Information: -==================== -Wade Hampton: (whampton@staffnet.com) - diff --git a/Documentation/sound/oss/MultiSound b/Documentation/sound/oss/MultiSound deleted file mode 100644 index e4a18bb7f73a..000000000000 --- a/Documentation/sound/oss/MultiSound +++ /dev/null @@ -1,1137 +0,0 @@ -#! /bin/sh -# -# Turtle Beach MultiSound Driver Notes -# -- Andrew Veliath <andrewtv@usa.net> -# -# Last update: September 10, 1998 -# Corresponding msnd driver: 0.8.3 -# -# ** This file is a README (top part) and shell archive (bottom part). -# The corresponding archived utility sources can be unpacked by -# running `sh MultiSound' (the utilities are only needed for the -# Pinnacle and Fiji cards). ** -# -# -# -=-=- Getting Firmware -=-=- -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# See the section `Obtaining and Creating Firmware Files' in this -# document for instructions on obtaining the necessary firmware -# files. -# -# -# Supported Features -# ~~~~~~~~~~~~~~~~~~ -# -# Currently, full-duplex digital audio (/dev/dsp only, /dev/audio is -# not currently available) and mixer functionality (/dev/mixer) are -# supported (memory mapped digital audio is not yet supported). -# Digital transfers and monitoring can be done as well if you have -# the digital daughterboard (see the section on using the S/PDIF port -# for more information). -# -# Support for the Turtle Beach MultiSound Hurricane architecture is -# composed of the following modules (these can also operate compiled -# into the kernel): -# -# msnd - MultiSound base (requires soundcore) -# -# msnd_classic - Base audio/mixer support for Classic, Monetery and -# Tahiti cards -# -# msnd_pinnacle - Base audio/mixer support for Pinnacle and Fiji cards -# -# -# Important Notes - Read Before Using -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# The firmware files are not included (may change in future). You -# must obtain these images from Turtle Beach (they are included in -# the MultiSound Development Kits), and place them in /etc/sound for -# example, and give the full paths in the Linux configuration. If -# you are compiling in support for the MultiSound driver rather than -# using it as a module, these firmware files must be accessible -# during kernel compilation. -# -# Please note these files must be binary files, not assembler. See -# the section later in this document for instructions to obtain these -# files. -# -# -# Configuring Card Resources -# ~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# ** This section is very important, as your card may not work at all -# or your machine may crash if you do not do this correctly. ** -# -# * Classic/Monterey/Tahiti -# -# These cards are configured through the driver msnd_classic. You must -# know the io port, then the driver will select the irq and memory resources -# on the card. It is up to you to know if these are free locations or now, -# a conflict can lock the machine up. -# -# * Pinnacle/Fiji -# -# The Pinnacle and Fiji cards have an extra config port, either -# 0x250, 0x260 or 0x270. This port can be disabled to have the card -# configured strictly through PnP, however you lose the ability to -# access the IDE controller and joystick devices on this card when -# using PnP. The included pinnaclecfg program in this shell archive -# can be used to configure the card in non-PnP mode, and in PnP mode -# you can use isapnptools. These are described briefly here. -# -# pinnaclecfg is not required; you can use the msnd_pinnacle module -# to fully configure the card as well. However, pinnaclecfg can be -# used to change the resource values of a particular device after the -# msnd_pinnacle module has been loaded. If you are compiling the -# driver into the kernel, you must set these values during compile -# time, however other peripheral resource values can be changed with -# the pinnaclecfg program after the kernel is loaded. -# -# -# *** PnP mode -# -# Use pnpdump to obtain a sample configuration if you can; I was able -# to obtain one with the command `pnpdump 1 0x203' -- this may vary -# for you (running pnpdump by itself did not work for me). Then, -# edit this file and use isapnp to uncomment and set the card values. -# Use these values when inserting the msnd_pinnacle module. Using -# this method, you can set the resources for the DSP and the Kurzweil -# synth (Pinnacle). Since Linux does not directly support PnP -# devices, you may have difficulty when using the card in PnP mode -# when it the driver is compiled into the kernel. Using non-PnP mode -# is preferable in this case. -# -# Here is an example mypinnacle.conf for isapnp that sets the card to -# io base 0x210, irq 5 and mem 0xd8000, and also sets the Kurzweil -# synth to 0x330 and irq 9 (may need editing for your system): -# -# (READPORT 0x0203) -# (CSN 2) -# (IDENTIFY *) -# -# # DSP -# (CONFIGURE BVJ0440/-1 (LD 0 -# (INT 0 (IRQ 5 (MODE +E))) (IO 0 (BASE 0x0210)) (MEM 0 (BASE 0x0d8000)) -# (ACT Y))) -# -# # Kurzweil Synth (Pinnacle Only) -# (CONFIGURE BVJ0440/-1 (LD 1 -# (IO 0 (BASE 0x0330)) (INT 0 (IRQ 9 (MODE +E))) -# (ACT Y))) -# -# (WAITFORKEY) -# -# -# *** Non-PnP mode -# -# The second way is by running the card in non-PnP mode. This -# actually has some advantages in that you can access some other -# devices on the card, such as the joystick and IDE controller. To -# configure the card, unpack this shell archive and build the -# pinnaclecfg program. Using this program, you can assign the -# resource values to the card's devices, or disable the devices. As -# an alternative to using pinnaclecfg, you can specify many of the -# configuration values when loading the msnd_pinnacle module (or -# during kernel configuration when compiling the driver into the -# kernel). -# -# If you specify cfg=0x250 for the msnd_pinnacle module, it -# automatically configure the card to the given io, irq and memory -# values using that config port (the config port is jumper selectable -# on the card to 0x250, 0x260 or 0x270). -# -# See the `msnd_pinnacle Additional Options' section below for more -# information on these parameters (also, if you compile the driver -# directly into the kernel, these extra parameters can be useful -# here). -# -# -# ** It is very easy to cause problems in your machine if you choose a -# resource value which is incorrect. ** -# -# -# Examples -# ~~~~~~~~ -# -# * MultiSound Classic/Monterey/Tahiti: -# -# modprobe soundcore -# insmod msnd -# insmod msnd_classic io=0x290 irq=7 mem=0xd0000 -# -# * MultiSound Pinnacle in PnP mode: -# -# modprobe soundcore -# insmod msnd -# isapnp mypinnacle.conf -# insmod msnd_pinnacle io=0x210 irq=5 mem=0xd8000 <-- match mypinnacle.conf values -# -# * MultiSound Pinnacle in non-PnP mode (replace 0x250 with your configuration port, -# one of 0x250, 0x260 or 0x270): -# -# insmod soundcore -# insmod msnd -# insmod msnd_pinnacle cfg=0x250 io=0x290 irq=5 mem=0xd0000 -# -# * To use the MPU-compatible Kurzweil synth on the Pinnacle in PnP -# mode, add the following (assumes you did `isapnp mypinnacle.conf'): -# -# insmod sound -# insmod mpu401 io=0x330 irq=9 <-- match mypinnacle.conf values -# -# * To use the MPU-compatible Kurzweil synth on the Pinnacle in non-PnP -# mode, add the following. Note how we first configure the peripheral's -# resources, _then_ install a Linux driver for it: -# -# insmod sound -# pinnaclecfg 0x250 mpu 0x330 9 -# insmod mpu401 io=0x330 irq=9 -# -# -- OR you can use the following sequence without pinnaclecfg in non-PnP mode: -# -# insmod soundcore -# insmod msnd -# insmod msnd_pinnacle cfg=0x250 io=0x290 irq=5 mem=0xd0000 mpu_io=0x330 mpu_irq=9 -# insmod sound -# insmod mpu401 io=0x330 irq=9 -# -# * To setup the joystick port on the Pinnacle in non-PnP mode (though -# you have to find the actual Linux joystick driver elsewhere), you -# can use pinnaclecfg: -# -# pinnaclecfg 0x250 joystick 0x200 -# -# -- OR you can configure this using msnd_pinnacle with the following: -# -# insmod soundcore -# insmod msnd -# insmod msnd_pinnacle cfg=0x250 io=0x290 irq=5 mem=0xd0000 joystick_io=0x200 -# -# -# msnd_classic, msnd_pinnacle Required Options -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# If the following options are not given, the module will not load. -# Examine the kernel message log for informative error messages. -# WARNING--probing isn't supported so try to make sure you have the -# correct shared memory area, otherwise you may experience problems. -# -# io I/O base of DSP, e.g. io=0x210 -# irq IRQ number, e.g. irq=5 -# mem Shared memory area, e.g. mem=0xd8000 -# -# -# msnd_classic, msnd_pinnacle Additional Options -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# fifosize The digital audio FIFOs, in kilobytes. If not -# specified, the default will be used. Increasing -# this value will reduce the chance of a FIFO -# underflow at the expense of increasing overall -# latency. For example, fifosize=512 will -# allocate 512kB read and write FIFOs (1MB total). -# While this may reduce dropouts, a heavy machine -# load will undoubtedly starve the FIFO of data -# and you will eventually get dropouts. One -# option is to alter the scheduling priority of -# the playback process, using `nice' or some form -# of POSIX soft real-time scheduling. -# -# calibrate_signal Setting this to one calibrates the ADCs to the -# signal, zero calibrates to the card (defaults -# to zero). -# -# -# msnd_pinnacle Additional Options -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# digital Specify digital=1 to enable the S/PDIF input -# if you have the digital daughterboard -# adapter. This will enable access to the -# DIGITAL1 input for the soundcard in the mixer. -# Some mixer programs might have trouble setting -# the DIGITAL1 source as an input. If you have -# trouble, you can try the setdigital.c program -# at the bottom of this document. -# -# cfg Non-PnP configuration port for the Pinnacle -# and Fiji (typically 0x250, 0x260 or 0x270, -# depending on the jumper configuration). If -# this option is omitted, then it is assumed -# that the card is in PnP mode, and that the -# specified DSP resource values are already -# configured with PnP (i.e. it won't attempt to -# do any sort of configuration). -# -# When the Pinnacle is in non-PnP mode, you can use the following -# options to configure particular devices. If a full specification -# for a device is not given, then the device is not configured. Note -# that you still must use a Linux driver for any of these devices -# once their resources are setup (such as the Linux joystick driver, -# or the MPU401 driver from OSS for the Kurzweil synth). -# -# mpu_io I/O port of MPU (on-board Kurzweil synth) -# mpu_irq IRQ of MPU (on-board Kurzweil synth) -# ide_io0 First I/O port of IDE controller -# ide_io1 Second I/O port of IDE controller -# ide_irq IRQ IDE controller -# joystick_io I/O port of joystick -# -# -# Obtaining and Creating Firmware Files -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# For the Classic/Tahiti/Monterey -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# Download to /tmp and unzip the following file from Turtle Beach: -# -# ftp://ftp.voyetra.com/pub/tbs/msndcl/msndvkit.zip -# -# When unzipped, unzip the file named MsndFiles.zip. Then copy the -# following firmware files to /etc/sound (note the file renaming): -# -# cp DSPCODE/MSNDINIT.BIN /etc/sound/msndinit.bin -# cp DSPCODE/MSNDPERM.REB /etc/sound/msndperm.bin -# -# When configuring the Linux kernel, specify /etc/sound/msndinit.bin and -# /etc/sound/msndperm.bin for the two firmware files (Linux kernel -# versions older than 2.2 do not ask for firmware paths, and are -# hardcoded to /etc/sound). -# -# If you are compiling the driver into the kernel, these files must -# be accessible during compilation, but will not be needed later. -# The files must remain, however, if the driver is used as a module. -# -# -# For the Pinnacle/Fiji -# ~~~~~~~~~~~~~~~~~~~~~ -# -# Download to /tmp and unzip the following file from Turtle Beach (be -# sure to use the entire URL; some have had trouble navigating to the -# URL): -# -# ftp://ftp.voyetra.com/pub/tbs/pinn/pnddk100.zip -# -# Unpack this shell archive, and run make in the created directory -# (you need a C compiler and flex to build the utilities). This -# should give you the executables conv, pinnaclecfg and setdigital. -# conv is only used temporarily here to create the firmware files, -# while pinnaclecfg is used to configure the Pinnacle or Fiji card in -# non-PnP mode, and setdigital can be used to set the S/PDIF input on -# the mixer (pinnaclecfg and setdigital should be copied to a -# convenient place, possibly run during system initialization). -# -# To generating the firmware files with the `conv' program, we create -# the binary firmware files by doing the following conversion -# (assuming the archive unpacked into a directory named PINNDDK): -# -# ./conv < PINNDDK/dspcode/pndspini.asm > /etc/sound/pndspini.bin -# ./conv < PINNDDK/dspcode/pndsperm.asm > /etc/sound/pndsperm.bin -# -# The conv (and conv.l) program is not needed after conversion and can -# be safely deleted. Then, when configuring the Linux kernel, specify -# /etc/sound/pndspini.bin and /etc/sound/pndsperm.bin for the two -# firmware files (Linux kernel versions older than 2.2 do not ask for -# firmware paths, and are hardcoded to /etc/sound). -# -# If you are compiling the driver into the kernel, these files must -# be accessible during compilation, but will not be needed later. -# The files must remain, however, if the driver is used as a module. -# -# -# Using Digital I/O with the S/PDIF Port -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# -# If you have a Pinnacle or Fiji with the digital daughterboard and -# want to set it as the input source, you can use this program if you -# have trouble trying to do it with a mixer program (be sure to -# insert the module with the digital=1 option, or say Y to the option -# during compiled-in kernel operation). Upon selection of the S/PDIF -# port, you should be able monitor and record from it. -# -# There is something to note about using the S/PDIF port. Digital -# timing is taken from the digital signal, so if a signal is not -# connected to the port and it is selected as recording input, you -# will find PCM playback to be distorted in playback rate. Also, -# attempting to record at a sampling rate other than the DAT rate may -# be problematic (i.e. trying to record at 8000Hz when the DAT signal -# is 44100Hz). If you have a problem with this, set the recording -# input to analog if you need to record at a rate other than that of -# the DAT rate. -# -# -# -- Shell archive attached below, just run `sh MultiSound' to extract. -# Contains Pinnacle/Fiji utilities to convert firmware, configure -# in non-PnP mode, and select the DIGITAL1 input for the mixer. -# -# -#!/bin/sh -# This is a shell archive (produced by GNU sharutils 4.2). -# To extract the files from this archive, save it to some FILE, remove -# everything before the `!/bin/sh' line above, then type `sh FILE'. -# -# Made on 1998-12-04 10:07 EST by <andrewtv@ztransform.velsoft.com>. -# Source directory was `/home/andrewtv/programming/pinnacle/pinnacle'. -# -# Existing files will *not* be overwritten unless `-c' is specified. -# -# This shar contains: -# length mode name -# ------ ---------- ------------------------------------------ -# 2046 -rw-rw-r-- MultiSound.d/setdigital.c -# 10235 -rw-rw-r-- MultiSound.d/pinnaclecfg.c -# 106 -rw-rw-r-- MultiSound.d/Makefile -# 141 -rw-rw-r-- MultiSound.d/conv.l -# 1472 -rw-rw-r-- MultiSound.d/msndreset.c -# -save_IFS="${IFS}" -IFS="${IFS}:" -gettext_dir=FAILED -locale_dir=FAILED -first_param="$1" -for dir in $PATH -do - if test "$gettext_dir" = FAILED && test -f $dir/gettext \ - && ($dir/gettext --version >/dev/null 2>&1) - then - set `$dir/gettext --version 2>&1` - if test "$3" = GNU - then - gettext_dir=$dir - fi - fi - if test "$locale_dir" = FAILED && test -f $dir/shar \ - && ($dir/shar --print-text-domain-dir >/dev/null 2>&1) - then - locale_dir=`$dir/shar --print-text-domain-dir` - fi -done -IFS="$save_IFS" -if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED -then - echo=echo -else - TEXTDOMAINDIR=$locale_dir - export TEXTDOMAINDIR - TEXTDOMAIN=sharutils - export TEXTDOMAIN - echo="$gettext_dir/gettext -s" -fi -touch -am 1231235999 $$.touch >/dev/null 2>&1 -if test ! -f 1231235999 && test -f $$.touch; then - shar_touch=touch -else - shar_touch=: - echo - $echo 'WARNING: not restoring timestamps. Consider getting and' - $echo "installing GNU \`touch', distributed in GNU File Utilities..." - echo -fi -rm -f 1231235999 $$.touch -# -if mkdir _sh01426; then - $echo 'x -' 'creating lock directory' -else - $echo 'failed to create lock directory' - exit 1 -fi -# ============= MultiSound.d/setdigital.c ============== -if test ! -d 'MultiSound.d'; then - $echo 'x -' 'creating directory' 'MultiSound.d' - mkdir 'MultiSound.d' -fi -if test -f 'MultiSound.d/setdigital.c' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'MultiSound.d/setdigital.c' '(file already exists)' -else - $echo 'x -' extracting 'MultiSound.d/setdigital.c' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'MultiSound.d/setdigital.c' && -/********************************************************************* -X * -X * setdigital.c - sets the DIGITAL1 input for a mixer -X * -X * Copyright (C) 1998 Andrew Veliath -X * -X * This program is free software; you can redistribute it and/or modify -X * it under the terms of the GNU General Public License as published by -X * the Free Software Foundation; either version 2 of the License, or -X * (at your option) any later version. -X * -X * This program is distributed in the hope that it will be useful, -X * but WITHOUT ANY WARRANTY; without even the implied warranty of -X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -X * GNU General Public License for more details. -X * -X * You should have received a copy of the GNU General Public License -X * along with this program; if not, write to the Free Software -X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -X * -X ********************************************************************/ -X -#include <stdio.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/ioctl.h> -#include <sys/soundcard.h> -X -int main(int argc, char *argv[]) -{ -X int fd; -X unsigned long recmask, recsrc; -X -X if (argc != 2) { -X fprintf(stderr, "usage: setdigital <mixer device>\n"); -X exit(1); -X } -X -X if ((fd = open(argv[1], O_RDWR)) < 0) { -X perror(argv[1]); -X exit(1); -X } -X -X if (ioctl(fd, SOUND_MIXER_READ_RECMASK, &recmask) < 0) { -X fprintf(stderr, "error: ioctl read recording mask failed\n"); -X perror("ioctl"); -X close(fd); -X exit(1); -X } -X -X if (!(recmask & SOUND_MASK_DIGITAL1)) { -X fprintf(stderr, "error: cannot find DIGITAL1 device in mixer\n"); -X close(fd); -X exit(1); -X } -X -X if (ioctl(fd, SOUND_MIXER_READ_RECSRC, &recsrc) < 0) { -X fprintf(stderr, "error: ioctl read recording source failed\n"); -X perror("ioctl"); -X close(fd); -X exit(1); -X } -X -X recsrc |= SOUND_MASK_DIGITAL1; -X -X if (ioctl(fd, SOUND_MIXER_WRITE_RECSRC, &recsrc) < 0) { -X fprintf(stderr, "error: ioctl write recording source failed\n"); -X perror("ioctl"); -X close(fd); -X exit(1); -X } -X -X close(fd); -X -X return 0; -} -SHAR_EOF - $shar_touch -am 1204092598 'MultiSound.d/setdigital.c' && - chmod 0664 'MultiSound.d/setdigital.c' || - $echo 'restore of' 'MultiSound.d/setdigital.c' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'MultiSound.d/setdigital.c:' 'MD5 check failed' -e87217fc3e71288102ba41fd81f71ec4 MultiSound.d/setdigital.c -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'MultiSound.d/setdigital.c'`" - test 2046 -eq "$shar_count" || - $echo 'MultiSound.d/setdigital.c:' 'original size' '2046,' 'current size' "$shar_count!" - fi -fi -# ============= MultiSound.d/pinnaclecfg.c ============== -if test -f 'MultiSound.d/pinnaclecfg.c' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'MultiSound.d/pinnaclecfg.c' '(file already exists)' -else - $echo 'x -' extracting 'MultiSound.d/pinnaclecfg.c' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'MultiSound.d/pinnaclecfg.c' && -/********************************************************************* -X * -X * pinnaclecfg.c - Pinnacle/Fiji Device Configuration Program -X * -X * This is for NON-PnP mode only. For PnP mode, use isapnptools. -X * -X * This is Linux-specific, and must be run with root permissions. -X * -X * Part of the Turtle Beach MultiSound Sound Card Driver for Linux -X * -X * Copyright (C) 1998 Andrew Veliath -X * -X * This program is free software; you can redistribute it and/or modify -X * it under the terms of the GNU General Public License as published by -X * the Free Software Foundation; either version 2 of the License, or -X * (at your option) any later version. -X * -X * This program is distributed in the hope that it will be useful, -X * but WITHOUT ANY WARRANTY; without even the implied warranty of -X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -X * GNU General Public License for more details. -X * -X * You should have received a copy of the GNU General Public License -X * along with this program; if not, write to the Free Software -X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -X * -X ********************************************************************/ -X -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <unistd.h> -#include <asm/io.h> -#include <asm/types.h> -X -#define IREG_LOGDEVICE 0x07 -#define IREG_ACTIVATE 0x30 -#define LD_ACTIVATE 0x01 -#define LD_DISACTIVATE 0x00 -#define IREG_EECONTROL 0x3F -#define IREG_MEMBASEHI 0x40 -#define IREG_MEMBASELO 0x41 -#define IREG_MEMCONTROL 0x42 -#define IREG_MEMRANGEHI 0x43 -#define IREG_MEMRANGELO 0x44 -#define MEMTYPE_8BIT 0x00 -#define MEMTYPE_16BIT 0x02 -#define MEMTYPE_RANGE 0x00 -#define MEMTYPE_HIADDR 0x01 -#define IREG_IO0_BASEHI 0x60 -#define IREG_IO0_BASELO 0x61 -#define IREG_IO1_BASEHI 0x62 -#define IREG_IO1_BASELO 0x63 -#define IREG_IRQ_NUMBER 0x70 -#define IREG_IRQ_TYPE 0x71 -#define IRQTYPE_HIGH 0x02 -#define IRQTYPE_LOW 0x00 -#define IRQTYPE_LEVEL 0x01 -#define IRQTYPE_EDGE 0x00 -X -#define HIBYTE(w) ((BYTE)(((WORD)(w) >> 8) & 0xFF)) -#define LOBYTE(w) ((BYTE)(w)) -#define MAKEWORD(low,hi) ((WORD)(((BYTE)(low))|(((WORD)((BYTE)(hi)))<<8))) -X -typedef __u8 BYTE; -typedef __u16 USHORT; -typedef __u16 WORD; -X -static int config_port = -1; -X -static int msnd_write_cfg(int cfg, int reg, int value) -{ -X outb(reg, cfg); -X outb(value, cfg + 1); -X if (value != inb(cfg + 1)) { -X fprintf(stderr, "error: msnd_write_cfg: I/O error\n"); -X return -EIO; -X } -X return 0; -} -X -static int msnd_read_cfg(int cfg, int reg) -{ -X outb(reg, cfg); -X return inb(cfg + 1); -} -X -static int msnd_write_cfg_io0(int cfg, int num, WORD io) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_IO0_BASEHI, HIBYTE(io))) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_IO0_BASELO, LOBYTE(io))) -X return -EIO; -X return 0; -} -X -static int msnd_read_cfg_io0(int cfg, int num, WORD *io) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X -X *io = MAKEWORD(msnd_read_cfg(cfg, IREG_IO0_BASELO), -X msnd_read_cfg(cfg, IREG_IO0_BASEHI)); -X -X return 0; -} -X -static int msnd_write_cfg_io1(int cfg, int num, WORD io) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_IO1_BASEHI, HIBYTE(io))) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_IO1_BASELO, LOBYTE(io))) -X return -EIO; -X return 0; -} -X -static int msnd_read_cfg_io1(int cfg, int num, WORD *io) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X -X *io = MAKEWORD(msnd_read_cfg(cfg, IREG_IO1_BASELO), -X msnd_read_cfg(cfg, IREG_IO1_BASEHI)); -X -X return 0; -} -X -static int msnd_write_cfg_irq(int cfg, int num, WORD irq) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_IRQ_NUMBER, LOBYTE(irq))) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_IRQ_TYPE, IRQTYPE_EDGE)) -X return -EIO; -X return 0; -} -X -static int msnd_read_cfg_irq(int cfg, int num, WORD *irq) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X -X *irq = msnd_read_cfg(cfg, IREG_IRQ_NUMBER); -X -X return 0; -} -X -static int msnd_write_cfg_mem(int cfg, int num, int mem) -{ -X WORD wmem; -X -X mem >>= 8; -X mem &= 0xfff; -X wmem = (WORD)mem; -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_MEMBASEHI, HIBYTE(wmem))) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_MEMBASELO, LOBYTE(wmem))) -X return -EIO; -X if (wmem && msnd_write_cfg(cfg, IREG_MEMCONTROL, (MEMTYPE_HIADDR | MEMTYPE_16BIT))) -X return -EIO; -X return 0; -} -X -static int msnd_read_cfg_mem(int cfg, int num, int *mem) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X -X *mem = MAKEWORD(msnd_read_cfg(cfg, IREG_MEMBASELO), -X msnd_read_cfg(cfg, IREG_MEMBASEHI)); -X *mem <<= 8; -X -X return 0; -} -X -static int msnd_activate_logical(int cfg, int num) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X if (msnd_write_cfg(cfg, IREG_ACTIVATE, LD_ACTIVATE)) -X return -EIO; -X return 0; -} -X -static int msnd_write_cfg_logical(int cfg, int num, WORD io0, WORD io1, WORD irq, int mem) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X if (msnd_write_cfg_io0(cfg, num, io0)) -X return -EIO; -X if (msnd_write_cfg_io1(cfg, num, io1)) -X return -EIO; -X if (msnd_write_cfg_irq(cfg, num, irq)) -X return -EIO; -X if (msnd_write_cfg_mem(cfg, num, mem)) -X return -EIO; -X if (msnd_activate_logical(cfg, num)) -X return -EIO; -X return 0; -} -X -static int msnd_read_cfg_logical(int cfg, int num, WORD *io0, WORD *io1, WORD *irq, int *mem) -{ -X if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) -X return -EIO; -X if (msnd_read_cfg_io0(cfg, num, io0)) -X return -EIO; -X if (msnd_read_cfg_io1(cfg, num, io1)) -X return -EIO; -X if (msnd_read_cfg_irq(cfg, num, irq)) -X return -EIO; -X if (msnd_read_cfg_mem(cfg, num, mem)) -X return -EIO; -X return 0; -} -X -static void usage(void) -{ -X fprintf(stderr, -X "\n" -X "pinnaclecfg 1.0\n" -X "\n" -X "usage: pinnaclecfg <config port> [device config]\n" -X "\n" -X "This is for use with the card in NON-PnP mode only.\n" -X "\n" -X "Available devices (not all available for Fiji):\n" -X "\n" -X " Device Description\n" -X " -------------------------------------------------------------------\n" -X " reset Reset all devices (i.e. disable)\n" -X " show Display current device configurations\n" -X "\n" -X " dsp <io> <irq> <mem> Audio device\n" -X " mpu <io> <irq> Internal Kurzweil synth\n" -X " ide <io0> <io1> <irq> On-board IDE controller\n" -X " joystick <io> Joystick port\n" -X "\n"); -X exit(1); -} -X -static int cfg_reset(void) -{ -X int i; -X -X for (i = 0; i < 4; ++i) -X msnd_write_cfg_logical(config_port, i, 0, 0, 0, 0); -X -X return 0; -} -X -static int cfg_show(void) -{ -X int i; -X int count = 0; -X -X for (i = 0; i < 4; ++i) { -X WORD io0, io1, irq; -X int mem; -X msnd_read_cfg_logical(config_port, i, &io0, &io1, &irq, &mem); -X switch (i) { -X case 0: -X if (io0 || irq || mem) { -X printf("dsp 0x%x %d 0x%x\n", io0, irq, mem); -X ++count; -X } -X break; -X case 1: -X if (io0 || irq) { -X printf("mpu 0x%x %d\n", io0, irq); -X ++count; -X } -X break; -X case 2: -X if (io0 || io1 || irq) { -X printf("ide 0x%x 0x%x %d\n", io0, io1, irq); -X ++count; -X } -X break; -X case 3: -X if (io0) { -X printf("joystick 0x%x\n", io0); -X ++count; -X } -X break; -X } -X } -X -X if (count == 0) -X fprintf(stderr, "no devices configured\n"); -X -X return 0; -} -X -static int cfg_dsp(int argc, char *argv[]) -{ -X int io, irq, mem; -X -X if (argc < 3 || -X sscanf(argv[0], "0x%x", &io) != 1 || -X sscanf(argv[1], "%d", &irq) != 1 || -X sscanf(argv[2], "0x%x", &mem) != 1) -X usage(); -X -X if (!(io == 0x290 || -X io == 0x260 || -X io == 0x250 || -X io == 0x240 || -X io == 0x230 || -X io == 0x220 || -X io == 0x210 || -X io == 0x3e0)) { -X fprintf(stderr, "error: io must be one of " -X "210, 220, 230, 240, 250, 260, 290, or 3E0\n"); -X usage(); -X } -X -X if (!(irq == 5 || -X irq == 7 || -X irq == 9 || -X irq == 10 || -X irq == 11 || -X irq == 12)) { -X fprintf(stderr, "error: irq must be one of " -X "5, 7, 9, 10, 11 or 12\n"); -X usage(); -X } -X -X if (!(mem == 0xb0000 || -X mem == 0xc8000 || -X mem == 0xd0000 || -X mem == 0xd8000 || -X mem == 0xe0000 || -X mem == 0xe8000)) { -X fprintf(stderr, "error: mem must be one of " -X "0xb0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000 or 0xe8000\n"); -X usage(); -X } -X -X return msnd_write_cfg_logical(config_port, 0, io, 0, irq, mem); -} -X -static int cfg_mpu(int argc, char *argv[]) -{ -X int io, irq; -X -X if (argc < 2 || -X sscanf(argv[0], "0x%x", &io) != 1 || -X sscanf(argv[1], "%d", &irq) != 1) -X usage(); -X -X return msnd_write_cfg_logical(config_port, 1, io, 0, irq, 0); -} -X -static int cfg_ide(int argc, char *argv[]) -{ -X int io0, io1, irq; -X -X if (argc < 3 || -X sscanf(argv[0], "0x%x", &io0) != 1 || -X sscanf(argv[0], "0x%x", &io1) != 1 || -X sscanf(argv[1], "%d", &irq) != 1) -X usage(); -X -X return msnd_write_cfg_logical(config_port, 2, io0, io1, irq, 0); -} -X -static int cfg_joystick(int argc, char *argv[]) -{ -X int io; -X -X if (argc < 1 || -X sscanf(argv[0], "0x%x", &io) != 1) -X usage(); -X -X return msnd_write_cfg_logical(config_port, 3, io, 0, 0, 0); -} -X -int main(int argc, char *argv[]) -{ -X char *device; -X int rv = 0; -X -X --argc; ++argv; -X -X if (argc < 2) -X usage(); -X -X sscanf(argv[0], "0x%x", &config_port); -X if (config_port != 0x250 && config_port != 0x260 && config_port != 0x270) { -X fprintf(stderr, "error: <config port> must be 0x250, 0x260 or 0x270\n"); -X exit(1); -X } -X if (ioperm(config_port, 2, 1)) { -X perror("ioperm"); -X fprintf(stderr, "note: pinnaclecfg must be run as root\n"); -X exit(1); -X } -X device = argv[1]; -X -X argc -= 2; argv += 2; -X -X if (strcmp(device, "reset") == 0) -X rv = cfg_reset(); -X else if (strcmp(device, "show") == 0) -X rv = cfg_show(); -X else if (strcmp(device, "dsp") == 0) -X rv = cfg_dsp(argc, argv); -X else if (strcmp(device, "mpu") == 0) -X rv = cfg_mpu(argc, argv); -X else if (strcmp(device, "ide") == 0) -X rv = cfg_ide(argc, argv); -X else if (strcmp(device, "joystick") == 0) -X rv = cfg_joystick(argc, argv); -X else { -X fprintf(stderr, "error: unknown device %s\n", device); -X usage(); -X } -X -X if (rv) -X fprintf(stderr, "error: device configuration failed\n"); -X -X return 0; -} -SHAR_EOF - $shar_touch -am 1204092598 'MultiSound.d/pinnaclecfg.c' && - chmod 0664 'MultiSound.d/pinnaclecfg.c' || - $echo 'restore of' 'MultiSound.d/pinnaclecfg.c' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'MultiSound.d/pinnaclecfg.c:' 'MD5 check failed' -366bdf27f0db767a3c7921d0a6db20fe MultiSound.d/pinnaclecfg.c -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'MultiSound.d/pinnaclecfg.c'`" - test 10235 -eq "$shar_count" || - $echo 'MultiSound.d/pinnaclecfg.c:' 'original size' '10235,' 'current size' "$shar_count!" - fi -fi -# ============= MultiSound.d/Makefile ============== -if test -f 'MultiSound.d/Makefile' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'MultiSound.d/Makefile' '(file already exists)' -else - $echo 'x -' extracting 'MultiSound.d/Makefile' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'MultiSound.d/Makefile' && -CC = gcc -CFLAGS = -O -PROGS = setdigital msndreset pinnaclecfg conv -X -all: $(PROGS) -X -clean: -X rm -f $(PROGS) -SHAR_EOF - $shar_touch -am 1204092398 'MultiSound.d/Makefile' && - chmod 0664 'MultiSound.d/Makefile' || - $echo 'restore of' 'MultiSound.d/Makefile' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'MultiSound.d/Makefile:' 'MD5 check failed' -76ca8bb44e3882edcf79c97df6c81845 MultiSound.d/Makefile -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'MultiSound.d/Makefile'`" - test 106 -eq "$shar_count" || - $echo 'MultiSound.d/Makefile:' 'original size' '106,' 'current size' "$shar_count!" - fi -fi -# ============= MultiSound.d/conv.l ============== -if test -f 'MultiSound.d/conv.l' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'MultiSound.d/conv.l' '(file already exists)' -else - $echo 'x -' extracting 'MultiSound.d/conv.l' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'MultiSound.d/conv.l' && -%% -[ \n\t,\r] -\;.* -DB -[0-9A-Fa-f]+H { int n; sscanf(yytext, "%xH", &n); printf("%c", n); } -%% -int yywrap() { return 1; } -main() { yylex(); } -SHAR_EOF - $shar_touch -am 0828231798 'MultiSound.d/conv.l' && - chmod 0664 'MultiSound.d/conv.l' || - $echo 'restore of' 'MultiSound.d/conv.l' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'MultiSound.d/conv.l:' 'MD5 check failed' -d2411fc32cd71a00dcdc1f009e858dd2 MultiSound.d/conv.l -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'MultiSound.d/conv.l'`" - test 141 -eq "$shar_count" || - $echo 'MultiSound.d/conv.l:' 'original size' '141,' 'current size' "$shar_count!" - fi -fi -# ============= MultiSound.d/msndreset.c ============== -if test -f 'MultiSound.d/msndreset.c' && test "$first_param" != -c; then - $echo 'x -' SKIPPING 'MultiSound.d/msndreset.c' '(file already exists)' -else - $echo 'x -' extracting 'MultiSound.d/msndreset.c' '(text)' - sed 's/^X//' << 'SHAR_EOF' > 'MultiSound.d/msndreset.c' && -/********************************************************************* -X * -X * msndreset.c - resets the MultiSound card -X * -X * Copyright (C) 1998 Andrew Veliath -X * -X * This program is free software; you can redistribute it and/or modify -X * it under the terms of the GNU General Public License as published by -X * the Free Software Foundation; either version 2 of the License, or -X * (at your option) any later version. -X * -X * This program is distributed in the hope that it will be useful, -X * but WITHOUT ANY WARRANTY; without even the implied warranty of -X * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -X * GNU General Public License for more details. -X * -X * You should have received a copy of the GNU General Public License -X * along with this program; if not, write to the Free Software -X * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -X * -X ********************************************************************/ -X -#include <stdio.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/ioctl.h> -#include <sys/soundcard.h> -X -int main(int argc, char *argv[]) -{ -X int fd; -X -X if (argc != 2) { -X fprintf(stderr, "usage: msndreset <mixer device>\n"); -X exit(1); -X } -X -X if ((fd = open(argv[1], O_RDWR)) < 0) { -X perror(argv[1]); -X exit(1); -X } -X -X if (ioctl(fd, SOUND_MIXER_PRIVATE1, 0) < 0) { -X fprintf(stderr, "error: msnd ioctl reset failed\n"); -X perror("ioctl"); -X close(fd); -X exit(1); -X } -X -X close(fd); -X -X return 0; -} -SHAR_EOF - $shar_touch -am 1204100698 'MultiSound.d/msndreset.c' && - chmod 0664 'MultiSound.d/msndreset.c' || - $echo 'restore of' 'MultiSound.d/msndreset.c' 'failed' - if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \ - && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then - md5sum -c << SHAR_EOF >/dev/null 2>&1 \ - || $echo 'MultiSound.d/msndreset.c:' 'MD5 check failed' -c52f876521084e8eb25e12e01dcccb8a MultiSound.d/msndreset.c -SHAR_EOF - else - shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'MultiSound.d/msndreset.c'`" - test 1472 -eq "$shar_count" || - $echo 'MultiSound.d/msndreset.c:' 'original size' '1472,' 'current size' "$shar_count!" - fi -fi -rm -fr _sh01426 -exit 0 diff --git a/Documentation/sound/oss/OPL3 b/Documentation/sound/oss/OPL3 deleted file mode 100644 index 2468ff827688..000000000000 --- a/Documentation/sound/oss/OPL3 +++ /dev/null @@ -1,6 +0,0 @@ -A pure OPL3 card is nice and easy to configure. Simply do - -insmod opl3 io=0x388 - -Change the I/O address in the very unlikely case this card is differently -configured diff --git a/Documentation/sound/oss/Opti b/Documentation/sound/oss/Opti deleted file mode 100644 index 4cd5d9ab3580..000000000000 --- a/Documentation/sound/oss/Opti +++ /dev/null @@ -1,218 +0,0 @@ -Support for the OPTi 82C931 chip --------------------------------- -Note: parts of this README file apply also to other -cards that use the mad16 driver. - -Some items in this README file are based on features -added to the sound driver after Linux-2.1.91 was out. -By the time of writing this I do not know which official -kernel release will include these features. -Please do not report inconsistencies on older Linux -kernels. - -The OPTi 82C931 is supported in its non-PnP mode. -Usually you do not need to set jumpers, etc. The sound driver -will check the card status and if it is required it will -force the card into a mode in which it can be programmed. - -If you have another OS installed on your computer it is recommended -that Linux and the other OS use the same resources. - -Also, it is recommended that resources specified in /etc/modprobe.d/*.conf -and resources specified in /etc/isapnp.conf agree. - -Compiling the sound driver --------------------------- -I highly recommend that you build a modularized sound driver. -This document does not cover a sound-driver which is built in -the kernel. - -Sound card support should be enabled as a module (chose m). -Answer 'm' for these items: - Generic OPL2/OPL3 FM synthesizer support (CONFIG_SOUND_ADLIB) - Microsoft Sound System support (CONFIG_SOUND_MSS) - Support for OPTi MAD16 and/or Mozart based cards (CONFIG_SOUND_MAD16) - FM synthesizer (YM3812/OPL-3) support (CONFIG_SOUND_YM3812) - -The configuration menu may ask for addresses, IRQ lines or DMA -channels. If the card is used as a module the module loading -options will override these values. - -For the OPTi 931 you can answer 'n' to: - Support MIDI in older MAD16 based cards (requires SB) (CONFIG_SOUND_MAD16_OLDCARD) -If you do need MIDI support in a Mozart or C928 based card you -need to answer 'm' to the above question. In that case you will -also need to answer 'm' to: - '100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' (CONFIG_SOUND_SB) - -Go on and compile your kernel and modules. Install the modules. Run depmod -a. - -Using isapnptools ------------------ -In most systems with a PnP BIOS you do not need to use isapnp. The -initialization provided by the BIOS is sufficient for the driver -to pick up the card and continue initialization. - -If that fails, or if you have other PnP cards, you need to use isapnp -to initialize the card. -This was tested with isapnptools-1.11 but I recommend that you use -isapnptools-1.13 (or newer). Run pnpdump to dump the information -about your PnP cards. Then edit the resulting file and select -the options of your choice. This file is normally installed as -/etc/isapnp.conf. - -The driver has one limitation with respect to I/O port resources: -IO3 base must be 0x0E0C. Although isapnp allows other ports, this -address is hard-coded into the driver. - -Using kmod and autoloading the sound driver -------------------------------------------- -Config files in '/etc/modprobe.d/' are used as below: - -alias mixer0 mad16 -alias audio0 mad16 -alias midi0 mad16 -alias synth0 opl3 -options sb mad16=1 -options mad16 irq=10 dma=0 dma16=1 io=0x530 joystick=1 cdtype=0 -options opl3 io=0x388 -install mad16 /sbin/modprobe -i mad16 && /sbin/ad1848_mixer_reroute 14 8 15 3 16 6 - -If you have an MPU daughtercard or onboard MPU you will want to add to the -"options mad16" line - eg - -options mad16 irq=5 dma=0 dma16=3 io=0x530 mpu_io=0x330 mpu_irq=9 - -To set the I/O and IRQ of the MPU. - - -Explain: - -alias mixer0 mad16 -alias audio0 mad16 -alias midi0 mad16 -alias synth0 opl3 - -When any sound device is opened the kernel requests auto-loading -of char-major-14. There is a built-in alias that translates this -request to loading the main sound module. - -The sound module in its turn will request loading of a sub-driver -for mixer, audio, midi or synthesizer device. The first 3 are -supported by the mad16 driver. The synth device is supported -by the opl3 driver. - -There is currently no way to autoload the sound device driver -if more than one card is installed. - -options sb mad16=1 - -This is left for historical reasons. If you enable the -config option 'Support MIDI in older MAD16 based cards (requires SB)' -or if you use an older mad16 driver it will force loading of the -SoundBlaster driver. This option tells the SB driver not to look -for a SB card but to wait for the mad16 driver. - -options mad16 irq=10 dma=0 dma16=1 io=0x530 joystick=1 cdtype=0 -options opl3 io=0x388 - -post-install mad16 /sbin/ad1848_mixer_reroute 14 8 15 3 16 6 - -This sets resources and options for the mad16 and opl3 drivers. -I use two DMA channels (only one is required) to enable full duplex. -joystick=1 enables the joystick port. cdtype=0 disables the cd port. -You can also set mpu_io and mpu_irq in the mad16 options for the -uart401 driver. - -This tells modprobe to run /sbin/ad1848_mixer_reroute after -mad16 is successfully loaded and initialized. The source -for ad1848_mixer_reroute is appended to the end of this readme -file. It is impossible for the sound driver to know the actual -connections to the mixer. The 3 inputs intended for cd, synth -and line-in are mapped to the generic inputs line1, line2 and -line3. This program reroutes these mixer channels to their -right names (note the right mapping depends on the actual sound -card that you use). -The numeric parameters mean: - 14=line1 8=cd - reroute line1 to the CD input. - 15=line2 3=synth - reroute line2 to the synthesizer input. - 16=line3 6=line - reroute line3 to the line input. -For reference on other input names look at the file -/usr/include/linux/soundcard.h. - -Using a joystick ------------------ -You must enable a joystick in the mad16 options. (also -in /etc/isapnp.conf if you use it). -Tested with regular analog joysticks. - -A CDROM drive connected to the sound card ------------------------------------------ -The 82C931 chip has support only for secondary ATAPI cdrom. -(cdtype=8). Loading the mad16 driver resets the C931 chip -and if a cdrom was already mounted it may cause a complete -system hang. Do not use the sound card if you have an alternative. -If you do use the sound card it is important that you load -the mad16 driver (use "modprobe mad16" to prevent auto-unloading) -before the cdrom is accessed the first time. - -Using the sound driver built-in to the kernel may help here, but... -Most new systems have a PnP BIOS and also two IDE controllers. -The IDE controller on the sound card may be needed only on older -systems (which have only one IDE controller) but these systems -also do not have a PnP BIOS - requiring isapnptools and a modularized -driver. - -Known problems --------------- -1. See the section on "A CDROM drive connected to the sound card". - -2. On my system the codec cannot capture companded sound samples. - (eg., recording from /dev/audio). When any companded capture is - requested I get stereo-16 bit samples instead. Playback of - companded samples works well. Apparently this problem is not common - to all C931 based cards. I do not know how to identify cards that - have this problem. - -Source for ad1848_mixer_reroute.c ---------------------------------- -#include <stdio.h> -#include <fcntl.h> -#include <linux/soundcard.h> - -static char *mixer_names[SOUND_MIXER_NRDEVICES] = - SOUND_DEVICE_LABELS; - -int -main(int argc, char **argv) { - int val, from, to; - int i, fd; - - fd = open("/dev/mixer", O_RDWR); - if(fd < 0) { - perror("/dev/mixer"); - return 1; - } - - for(i = 2; i < argc; i += 2) { - from = atoi(argv[i-1]); - to = atoi(argv[i]); - - if(to == SOUND_MIXER_NONE) - fprintf(stderr, "%s: turning off mixer %s\n", - argv[0], mixer_names[to]); - else - fprintf(stderr, "%s: rerouting mixer %s to %s\n", - argv[0], mixer_names[from], mixer_names[to]); - - val = from << 8 | to; - - if(ioctl(fd, SOUND_MIXER_PRIVATE2, &val)) { - perror("AD1848 mixer reroute"); - return 1; - } - } - - return 0; -} - diff --git a/Documentation/sound/oss/PAS16 b/Documentation/sound/oss/PAS16 deleted file mode 100644 index 5c27229eec8c..000000000000 --- a/Documentation/sound/oss/PAS16 +++ /dev/null @@ -1,162 +0,0 @@ -Pro Audio Spectrum 16 for 2.3.99 and later -========================================= -by Thomas Molina (tmolina@home.com) -last modified 3 Mar 2001 -Acknowledgement to Axel Boldt (boldt@math.ucsb.edu) for stuff taken -from Configure.help, Riccardo Facchetti for stuff from README.OSS, -and others whose names I could not find. - -This documentation is relevant for the PAS16 driver (pas2_card.c and -friends) under kernel version 2.3.99 and later. If you are -unfamiliar with configuring sound under Linux, please read the -Sound-HOWTO, Documentation/sound/oss/Introduction and other -relevant docs first. - -The following information is relevant information from README.OSS -and legacy docs for the Pro Audio Spectrum 16 (PAS16): -================================================================== - -The pas2_card.c driver supports the following cards -- -Pro Audio Spectrum 16 (PAS16) and compatibles: - Pro Audio Spectrum 16 - Pro Audio Studio 16 - Logitech Sound Man 16 - NOTE! The original Pro Audio Spectrum as well as the PAS+ are not - and will not be supported by the driver. - -The sound driver configuration dialog -------------------------------------- - -Sound configuration starts by making some yes/no questions. Be careful -when answering to these questions since answering y to a question may -prevent some later ones from being asked. For example don't answer y to -the question about (PAS16) if you don't really have a PAS16. Sound -configuration may also be made modular by answering m to configuration -options presented. - -Note also that all questions may not be asked. The configuration program -may disable some questions depending on the earlier choices. It may also -select some options automatically as well. - - "ProAudioSpectrum 16 support", - - Answer 'y'_ONLY_ if you have a Pro Audio Spectrum _16_, - Pro Audio Studio 16 or Logitech SoundMan 16 (be sure that - you read the above list correctly). Don't answer 'y' if you - have some other card made by Media Vision or Logitech since they - are not PAS16 compatible. - NOTE! Since 3.5-beta10 you need to enable SB support (next question) - if you want to use the SB emulation of PAS16. It's also possible to - the emulation if you want to use a true SB card together with PAS16 - (there is another question about this that is asked later). - - "Generic OPL2/OPL3 FM synthesizer support", - - Answer 'y' if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). - The PAS16 has an OPL3-compatible FM chip. - -With PAS16 you can use two audio device files at the same time. /dev/dsp (and -/dev/audio) is connected to the 8/16 bit native codec and the /dev/dsp1 (and -/dev/audio1) is connected to the SB emulation (8 bit mono only). - - -The new stuff for 2.3.99 and later -============================================================================ -The following configuration options are relevant to configuring the PAS16: - -Sound card support -CONFIG_SOUND - If you have a sound card in your computer, i.e. if it can say more - than an occasional beep, say Y. Be sure to have all the information - about your sound card and its configuration down (I/O port, - interrupt and DMA channel), because you will be asked for it. - - You want to read the Sound-HOWTO, available from - http://www.tldp.org/docs.html#howto . General information - about the modular sound system is contained in the files - Documentation/sound/oss/Introduction. The file - Documentation/sound/oss/README.OSS contains some slightly outdated but - still useful information as well. - -OSS sound modules -CONFIG_SOUND_OSS - OSS is the Open Sound System suite of sound card drivers. They make - sound programming easier since they provide a common API. Say Y or M - here (the module will be called sound.o) if you haven't found a - driver for your sound card above, then pick your driver from the - list below. - -Persistent DMA buffers -CONFIG_SOUND_DMAP - Linux can often have problems allocating DMA buffers for ISA sound - cards on machines with more than 16MB of RAM. This is because ISA - DMA buffers must exist below the 16MB boundary and it is quite - possible that a large enough free block in this region cannot be - found after the machine has been running for a while. If you say Y - here the DMA buffers (64Kb) will be allocated at boot time and kept - until the shutdown. This option is only useful if you said Y to - "OSS sound modules", above. If you said M to "OSS sound modules" - then you can get the persistent DMA buffer functionality by passing - the command-line argument "dmabuf=1" to the sound.o module. - - Say y here for PAS16. - -ProAudioSpectrum 16 support -CONFIG_SOUND_PAS - Answer Y only if you have a Pro Audio Spectrum 16, ProAudio Studio - 16 or Logitech SoundMan 16 sound card. Don't answer Y if you have - some other card made by Media Vision or Logitech since they are not - PAS16 compatible. It is not necessary to enable the separate - Sound Blaster support; it is included in the PAS driver. - - If you compile the driver into the kernel, you have to add - "pas2=<io>,<irq>,<dma>,<dma2>,<sbio>,<sbirq>,<sbdma>,<sbdma2> - to the kernel command line. - -FM Synthesizer (YM3812/OPL-3) support -CONFIG_SOUND_YM3812 - Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). - Answering Y is usually a safe and recommended choice, however some - cards may have software (TSR) FM emulation. Enabling FM support with - these cards may cause trouble (I don't currently know of any such - cards, however). - Please read the file Documentation/sound/oss/OPL3 if your card has an - OPL3 chip. - If you compile the driver into the kernel, you have to add - "opl3=<io>" to the kernel command line. - - If you compile your drivers into the kernel, you MUST configure - OPL3 support as a module for PAS16 support to work properly. - You can then get OPL3 functionality by issuing the command: - insmod opl3 - In addition, you must either add the following line to - /etc/modprobe.d/*.conf: - options opl3 io=0x388 - or else add the following line to /etc/lilo.conf: - opl3=0x388 - - -EXAMPLES -=================================================================== -To use the PAS16 in my computer I have enabled the following sound -configuration options: - -CONFIG_SOUND=y -CONFIG_SOUND_OSS=y -CONFIG_SOUND_TRACEINIT=y -CONFIG_SOUND_DMAP=y -CONFIG_SOUND_PAS=y -CONFIG_SOUND_SB=n -CONFIG_SOUND_YM3812=m - -I have also included the following append line in /etc/lilo.conf: -append="pas2=0x388,10,3,-1,0x220,5,1,-1 sb=0x220,5,1,-1 opl3=0x388" - -The io address of 0x388 is default configuration on the PAS16. The -irq of 10 and dma of 3 may not match your installation. The above -configuration enables PAS16, 8-bit Soundblaster and OPL3 -functionality. If Soundblaster functionality is not desired, the -following line would be appropriate: -append="pas2=0x388,10,3,-1,0,-1,-1,-1 opl3=0x388" - -If sound is built totally modular, the above options may be -specified in /etc/modprobe.d/*.conf for pas2, sb and opl3 -respectively. diff --git a/Documentation/sound/oss/PSS b/Documentation/sound/oss/PSS deleted file mode 100644 index 187b9525e1f6..000000000000 --- a/Documentation/sound/oss/PSS +++ /dev/null @@ -1,41 +0,0 @@ -The PSS cards and other ECHO based cards provide an onboard DSP with -downloadable programs and also has an AD1848 "Microsoft Sound System" -device. The PSS driver enables MSS and MPU401 modes of the card. SB -is not enabled since it doesn't work concurrently with MSS. - -If you build this driver as a module then the driver takes the following -parameters - -pss_io. The I/O base the PSS card is configured at (normally 0x220 - or 0x240) - -mss_io The base address of the Microsoft Sound System interface. - This is normally 0x530, but may be 0x604 or other addresses. - -mss_irq The interrupt assigned to the Microsoft Sound System - emulation. IRQ's 3,5,7,9,10,11 and 12 are available. If you - get IRQ errors be sure to check the interrupt is set to - "ISA/Legacy" in the BIOS on modern machines. - -mss_dma The DMA channel used by the Microsoft Sound System. - This can be 0, 1, or 3. DMA 0 is not available on older - machines and will cause a crash on them. - -mpu_io The MPU emulation base address. This sets the base of the - synthesizer. It is typically 0x330 but can be altered. - -mpu_irq The interrupt to use for the synthesizer. It must differ - from the IRQ used by the Microsoft Sound System port. - - -The mpu_io/mpu_irq fields are optional. If they are not specified the -synthesizer parts are not configured. - -When the module is loaded it looks for a file called -/etc/sound/pss_synth. This is the firmware file from the DOS install disks. -This fil holds a general MIDI emulation. The file expected is called -genmidi.ld on newer DOS driver install disks and synth.ld on older ones. - -You can also load alternative DSP algorithms into the card if you wish. One -alternative driver can be found at http://www.mpg123.de/ - diff --git a/Documentation/sound/oss/PSS-updates b/Documentation/sound/oss/PSS-updates deleted file mode 100644 index 11914a1dc7e7..000000000000 --- a/Documentation/sound/oss/PSS-updates +++ /dev/null @@ -1,88 +0,0 @@ - This file contains notes for users of PSS sound cards who wish to use the -newly added features of the newest version of this driver. - - The major enhancements present in this new revision of this driver is the -addition of two new module parameters that allow you to take full advantage of -all the features present on your PSS sound card. These features include the -ability to enable both the builtin CDROM and joystick ports. - -pss_enable_joystick - - This parameter is basically a flag. A 0 will leave the joystick port -disabled, while a non-zero value would enable the joystick port. The default -setting is pss_enable_joystick=0 as this keeps this driver fully compatible -with systems that were using previous versions of this driver. If you wish to -enable the joystick port you will have to add pss_enable_joystick=1 as an -argument to the driver. To actually use the joystick port you will then have -to load the joystick driver itself. Just remember to load the joystick driver -AFTER the pss sound driver. - -pss_cdrom_port - - This parameter takes a port address as its parameter. Any available port -address can be specified to enable the CDROM port, except for 0x0 and -1 as -these values would leave the port disabled. Like the joystick port, the cdrom -port will require that an appropriate CDROM driver be loaded before you can make -use of the newly enabled CDROM port. Like the joystick port option above, -remember to load the CDROM driver AFTER the pss sound driver. While it may -differ on some PSS sound cards, all the PSS sound cards that I have seen have a -builtin Wearnes CDROM port. If this is the case with your PSS sound card you -should load aztcd with the appropriate port option that matches the port you -assigned to the CDROM port when you loaded your pss sound driver. (ex. -modprobe pss pss_cdrom_port=0x340 && modprobe aztcd aztcd=0x340) The default -setting of this parameter leaves the CDROM port disabled to maintain full -compatibility with systems using previous versions of this driver. - - Other options have also been added for the added convenience and utility -of the user. These options are only available if this driver is loaded as a -module. - -pss_no_sound - - This module parameter is a flag that can be used to tell the driver to -just configure non-sound components. 0 configures all components, a non-0 -value will only attempt to configure the CDROM and joystick ports. This -parameter can be used by a user who only wished to use the builtin joystick -and/or CDROM port(s) of his PSS sound card. If this driver is loaded with this -parameter and with the parameter below set to true then a user can safely unload -this driver with the following command "rmmod pss && rmmod ad1848 && rmmod -mpu401 && rmmod sound && rmmod soundcore" and retain the full functionality of -his CDROM and/or joystick port(s) while gaining back the memory previously used -by the sound drivers. This default setting of this parameter is 0 to retain -full behavioral compatibility with previous versions of this driver. - -pss_keep_settings - - This parameter can be used to specify whether you want the driver to reset -all emulations whenever its unloaded. This can be useful for those who are -sharing resources (io ports, IRQ's, DMA's) between different ISA cards. This -flag can also be useful in that future versions of this driver may reset all -emulations by default on the driver's unloading (as it probably should), so -specifying it now will ensure that all future versions of this driver will -continue to work as expected. The default value of this parameter is 1 to -retain full behavioral compatibility with previous versions of this driver. - -pss_firmware - - This parameter can be used to specify the file containing the firmware -code so that a user could tell the driver where that file is located instead -of having to put it in a predefined location with a predefined name. The -default setting of this parameter is "/etc/sound/pss_synth" as this was the -path and filename the hardcoded value in the previous versions of this driver. - -Examples: - -# Normal PSS sound card system, loading of drivers. -# Should be specified in an rc file (ex. Slackware uses /etc/rc.d/rc.modules). - -/sbin/modprobe pss pss_io=0x220 mpu_io=0x338 mpu_irq=9 mss_io=0x530 mss_irq=10 mss_dma=1 pss_cdrom_port=0x340 pss_enable_joystick=1 -/sbin/modprobe aztcd aztcd=0x340 -/sbin/modprobe joystick - -# System using the PSS sound card just for its CDROM and joystick ports. -# Should be specified in an rc file (ex. Slackware uses /etc/rc.d/rc.modules). - -/sbin/modprobe pss pss_io=0x220 pss_cdrom_port=0x340 pss_enable_joystick=1 pss_no_sound=1 -/sbin/rmmod pss && /sbin/rmmod ad1848 && /sbin/rmmod mpu401 && /sbin/rmmod sound && /sbin/rmmod soundcore # This line not needed, but saves memory. -/sbin/modprobe aztcd aztcd=0x340 -/sbin/modprobe joystick diff --git a/Documentation/sound/oss/README.OSS b/Documentation/sound/oss/README.OSS deleted file mode 100644 index a085ea3611a1..000000000000 --- a/Documentation/sound/oss/README.OSS +++ /dev/null @@ -1,1455 +0,0 @@ -Introduction ------------- - -This file is a collection of all the old Readme files distributed with -OSS/Lite by Hannu Savolainen. Since the new Linux sound driver is founded -on it I think these information may still be interesting for users that -have to configure their sound system. - -Be warned: Alan Cox is the current maintainer of the Linux sound driver so if -you have problems with it, please contact him or the current device-specific -driver maintainer (e.g. for aedsp16 specific problems contact me). If you have -patches, contributions or suggestions send them to Alan: I'm sure they are -welcome. - -In this document you will find a lot of references about OSS/Lite or ossfree: -they are gone forever. Keeping this in mind and with a grain of salt this -document can be still interesting and very helpful. - -[ File edited 17.01.1999 - Riccardo Facchetti ] -[ Edited miroSOUND section 19.04.2001 - Robert Siemer ] - -OSS/Free version 3.8 release notes ----------------------------------- - -Please read the SOUND-HOWTO (available from sunsite.unc.edu and other Linux FTP -sites). It gives instructions about using sound with Linux. It's bit out of -date but still very useful. Information about bug fixes and such things -is available from the web page (see above). - -Please check http://www.opensound.com/pguide for more info about programming -with OSS API. - - ==================================================== -- THIS VERSION ____REQUIRES____ Linux 2.1.57 OR LATER. - ==================================================== - -Packages "snd-util-3.8.tar.gz" and "snd-data-0.1.tar.Z" -contain useful utilities to be used with this driver. -See http://www.opensound.com/ossfree/ for -download instructions. - -If you are looking for the installation instructions, please -look forward into this document. - -Supported sound cards ---------------------- - -See below. - -Contributors ------------- - -This driver contains code by several contributors. In addition several other -persons have given useful suggestions. The following is a list of major -contributors. (I could have forgotten some names.) - - Craig Metz 1/2 of the PAS16 Mixer and PCM support - Rob Hooft Volume computation algorithm for the FM synth. - Mika Liljeberg uLaw encoding and decoding routines - Jeff Tranter Linux SOUND HOWTO document - Greg Lee Volume computation algorithm for the GUS and - lots of valuable suggestions. - Andy Warner ISC port - Jim Lowe, - Amancio Hasty Jr FreeBSD/NetBSD port - Anders Baekgaard Bug hunting and valuable suggestions. - Joerg Schubert SB16 DSP support (initial version). - Andrew Robinson Improvements to the GUS driver - Megens SA MIDI recording for SB and SB Pro (initial version). - Mikael Nordqvist Linear volume support for GUS and - nonblocking /dev/sequencer. - Ian Hartas SVR4.2 port - Markus Aroharju and - Risto Kankkunen Major contributions to the mixer support - of GUS v3.7. - Hunyue Yau Mixer support for SG NX Pro. - Marc Hoffman PSS support (initial version). - Rainer Vranken Initialization for Jazz16 (initial version). - Peter Trattler Initial version of loadable module support for Linux. - JRA Gibson 16 bit mode for Jazz16 (initial version) - Davor Jadrijevic MAD16 support (initial version) - Gregor Hoffleit Mozart support (initial version) - Riccardo Facchetti Audio Excel DSP 16 (aedsp16) support - James Hightower Spotting a tiny but important bug in CS423x support. - Denis Sablic OPTi 82C924 specific enhancements (non PnP mode) - Tim MacKenzie Full duplex support for OPTi 82C930. - - Please look at lowlevel/README for more contributors. - -There are probably many other names missing. If you have sent me some -patches and your name is not in the above list, please inform me. - -Sending your contributions or patches -------------------------------------- - -First of all it's highly recommended to contact me before sending anything -or before even starting to do any work. Tell me what you suggest to be -changed or what you have planned to do. Also ensure you are using the -very latest (development) version of OSS/Free since the change may already be -implemented there. In general it's a major waste of time to try to improve a -several months old version. Information about the latest version can be found -from http://www.opensound.com/ossfree. In general there is no point in -sending me patches relative to production kernels. - -Sponsors etc. -------------- - -The following companies have greatly helped development of this driver -in form of a free copy of their product: - -Novell, Inc. UnixWare personal edition + SDK -The Santa Cruz Operation, Inc. A SCO OpenServer + SDK -Ensoniq Corp, a SoundScape card and extensive amount of assistance -MediaTrix Peripherals Inc, a AudioTrix Pro card + SDK -Acer, Inc. a pair of AcerMagic S23 cards. - -In addition the following companies have provided me sufficient amount -of technical information at least some of their products (free or $$$): - -Advanced Gravis Computer Technology Ltd. -Media Vision Inc. -Analog Devices Inc. -Logitech Inc. -Aztech Labs Inc. -Crystal Semiconductor Corporation, -Integrated Circuit Systems Inc. -OAK Technology -OPTi -Turtle Beach -miro -Ad Lib Inc. ($$) -Music Quest Inc. ($$) -Creative Labs ($$$) - -If you have some problems -========================= - -Read the sound HOWTO (sunsite.unc.edu:/pub/Linux/docs/...?). -Also look at the home page (http://www.opensound.com/ossfree). It may -contain info about some recent bug fixes. - -It's likely that you have some problems when trying to use the sound driver -first time. Sound cards don't have standard configuration so there are no -good default configuration to use. Please try to use same I/O, DMA and IRQ -values for the sound card than with DOS. - -If you get an error message when trying to use the driver, please look -at /var/adm/messages for more verbose error message. - - -The following errors are likely with /dev/dsp and /dev/audio. - - - "No such device or address". - This error indicates that there are no suitable hardware for the - device file or the sound driver has been compiled without support for - this particular device. For example /dev/audio and /dev/dsp will not - work if "digitized voice support" was not enabled during "make config". - - - "Device or resource busy". Probably the IRQ (or DMA) channel - required by the sound card is in use by some other device/driver. - - - "I/O error". Almost certainly (99%) it's an IRQ or DMA conflict. - Look at the kernel messages in /var/adm/notice for more info. - - - "Invalid argument". The application is calling ioctl() - with impossible parameters. Check that the application is - for sound driver version 2.X or later. - -Linux installation -================== - -IMPORTANT! Read this if you are installing a separately - distributed version of this driver. - - Check that your kernel version works with this - release of the driver (see Readme). Also verify - that your current kernel version doesn't have more - recent sound driver version than this one. IT'S HIGHLY - RECOMMENDED THAT YOU USE THE SOUND DRIVER VERSION THAT - IS DISTRIBUTED WITH KERNEL SOURCES. - -- When installing separately distributed sound driver you should first - read the above notice. Then try to find proper directory where and how - to install the driver sources. You should not try to install a separately - distributed driver version if you are not able to find the proper way - yourself (in this case use the version that is distributed with kernel - sources). Remove old version of linux/drivers/sound directory before - installing new files. - -- To build the device files you need to run the enclosed shell script - (see below). You need to do this only when installing sound driver - first time or when upgrading to much recent version than the earlier - one. - -- Configure and compile Linux as normally (remember to include the - sound support during "make config"). Please refer to kernel documentation - for instructions about configuring and compiling kernel. File Readme.cards - contains card specific instructions for configuring this driver for - use with various sound cards. - -Boot time configuration (using lilo and insmod) ------------------------------------------------ - -This information has been removed. Too many users didn't believe -that it's really not necessary to use this method. Please look at -Readme of sound driver version 3.0.1 if you still want to use this method. - -Problems --------- - -Common error messages: - -- /dev/???????: No such file or directory. -Run the script at the end of this file. - -- /dev/???????: No such device. -You are not running kernel which contains the sound driver. When using -modularized sound driver this error means that the sound driver is not -loaded. - -- /dev/????: No such device or address. -Sound driver didn't detect suitable card when initializing. Please look at -Readme.cards for info about configuring the driver with your card. Also -check for possible boot (insmod) time error messages in /var/adm/messages. - -- Other messages or problems -Please check http://www.opensound.com/ossfree for more info. - -Configuring version 3.8 (for Linux) with some common sound cards -================================================================ - -This document describes configuring sound cards with the freeware version of -Open Sound Systems (OSS/Free). Information about the commercial version -(OSS/Linux) and its configuration is available from -http://www.opensound.com/linux.html. Information presented here is -not valid for OSS/Linux. - -If you are unsure about how to configure OSS/Free -you can download the free evaluation version of OSS/Linux from the above -address. There is a chance that it can autodetect your sound card. In this case -you can use the information included in soundon.log when configuring OSS/Free. - - -IMPORTANT! This document covers only cards that were "known" when - this driver version was released. Please look at - http://www.opensound.com/ossfree for info about - cards introduced recently. - - When configuring the sound driver, you should carefully - check each sound configuration option (particularly - "Support for /dev/dsp and /dev/audio"). The default values - offered by these programs are not necessarily valid. - - -THE BIGGEST MISTAKES YOU CAN MAKE -================================= - -1. Assuming that the card is Sound Blaster compatible when it's not. --------------------------------------------------------------------- - -The number one mistake is to assume that your card is compatible with -Sound Blaster. Only the cards made by Creative Technology or which have -one or more chips labeled by Creative are SB compatible. In addition there -are few sound chipsets which are SB compatible in Linux such as ESS1688 or -Jazz16. Note that SB compatibility in DOS/Windows does _NOT_ mean anything -in Linux. - -IF YOU REALLY ARE 150% SURE YOU HAVE A SOUND BLASTER YOU CAN SKIP THE REST OF -THIS CHAPTER. - -For most other "supposed to be SB compatible" cards you have to use other -than SB drivers (see below). It is possible to get most sound cards to work -in SB mode but in general it's a complete waste of time. There are several -problems which you will encounter by using SB mode with cards that are not -truly SB compatible: - -- The SB emulation is at most SB Pro (DSP version 3.x) which means that -you get only 8 bit audio (there is always an another ("native") mode which -gives the 16 bit capability). The 8 bit only operation is the reason why -many users claim that sound quality in Linux is much worse than in DOS. -In addition some applications require 16 bit mode and they produce just -noise with a 8 bit only device. -- The card may work only in some cases but refuse to work most of the -time. The SB compatible mode always requires special initialization which is -done by the DOS/Windows drivers. This kind of cards work in Linux after -you have warm booted it after DOS but they don't work after cold boot -(power on or reset). -- You get the famous "DMA timed out" messages. Usually all SB clones have -software selectable IRQ and DMA settings. If the (power on default) values -currently used by the card don't match configuration of the driver you will -get the above error message whenever you try to record or play. There are -few other reasons to the DMA timeout message but using the SB mode seems -to be the most common cause. - -2. Trying to use a PnP (Plug & Play) card just like an ordinary sound card --------------------------------------------------------------------------- - -Plug & Play is a protocol defined by Intel and Microsoft. It lets operating -systems to easily identify and reconfigure I/O ports, IRQs and DMAs of ISA -cards. The problem with PnP cards is that the standard Linux doesn't currently -(versions 2.1.x and earlier) don't support PnP. This means that you will have -to use some special tricks (see later) to get a PnP card alive. Many PnP cards -work after they have been initialized but this is not always the case. - -There are sometimes both PnP and non-PnP versions of the same sound card. -The non-PnP version is the original model which usually has been discontinued -more than an year ago. The PnP version has the same name but with "PnP" -appended to it (sometimes not). This causes major confusion since the non-PnP -model works with Linux but the PnP one doesn't. - -You should carefully check if "Plug & Play" or "PnP" is mentioned in the name -of the card or in the documentation or package that came with the card. -Everything described in the rest of this document is not necessarily valid for -PnP models of sound cards even you have managed to wake up the card properly. -Many PnP cards are simply too different from their non-PnP ancestors which are -covered by this document. - - -Cards that are not (fully) supported by this driver -=================================================== - -See http://www.opensound.com/ossfree for information about sound cards -to be supported in future. - - -How to use sound without recompiling kernel and/or sound driver -=============================================================== - -There is a commercial sound driver which comes in precompiled form and doesn't -require recompiling of the kernel. See http://www.4Front-tech.com/oss.html for -more info. - - -Configuring PnP cards -===================== - -New versions of most sound cards use the so-called ISA PnP protocol for -soft configuring their I/O, IRQ, DMA and shared memory resources. -Currently at least cards made by Creative Technology (SB32 and SB32AWE -PnP), Gravis (GUS PnP and GUS PnP Pro), Ensoniq (Soundscape PnP) and -Aztech (some Sound Galaxy models) use PnP technology. The CS4232/4236 audio -chip by Crystal Semiconductor (Intel Atlantis, HP Pavilion and many other -motherboards) is also based on PnP technology but there is a "native" driver -available for it (see information about CS4232 later in this document). - -PnP sound cards (as well as most other PnP ISA cards) are not supported -by this version of the driver . Proper -support for them should be released during 97 once the kernel level -PnP support is available. - -There is a method to get most of the PnP cards to work. The basic method -is the following: - -1) Boot DOS so the card's DOS drivers have a chance to initialize it. -2) _Cold_ boot to Linux by using "loadlin.exe". Hitting ctrl-alt-del -works with older machines but causes a hard reset of all cards on recent -(Pentium) machines. -3) If you have the sound driver in Linux configured properly, the card should -work now. "Proper" means that I/O, IRQ and DMA settings are the same as in -DOS. The hard part is to find which settings were used. See the documentation of -your card for more info. - -Windows 95 could work as well as DOS but running loadlin may be difficult. -Probably you should "shut down" your machine to MS-DOS mode before running it. - -Some machines have a BIOS utility for setting PnP resources. This is a good -way to configure some cards. In this case you don't need to boot DOS/Win95 -before starting Linux. - -Another way to initialize PnP cards without DOS/Win95 is a Linux based -PnP isolation tool. When writing this there is a pre alpha test version -of such a tool available from ftp://ftp.demon.co.uk/pub/unix/linux/utils. The -file is called isapnptools-*. Please note that this tool is just a temporary -solution which may be incompatible with future kernel versions having proper -support for PnP cards. There are bugs in setting DMA channels in earlier -versions of isapnptools so at least version 1.6 is required with sound cards. - -Yet another way to use PnP cards is to use (commercial) OSS/Linux drivers. See -http://www.opensound.com/linux.html for more info. This is probably the way you -should do it if you don't want to spend time recompiling the kernel and -required tools. - - -Read this before trying to configure the driver -=============================================== - -There are currently many cards that work with this driver. Some of the cards -have native support while others work since they emulate some other -card (usually SB, MSS/WSS and/or MPU401). The following cards have native -support in the driver. Detailed instructions for configuring these cards -will be given later in this document. - -Pro Audio Spectrum 16 (PAS16) and compatibles: - Pro Audio Spectrum 16 - Pro Audio Studio 16 - Logitech Sound Man 16 - NOTE! The original Pro Audio Spectrum as well as the PAS+ are not - and will not be supported by the driver. - -Media Vision Jazz16 based cards - Pro Sonic 16 - Logitech SoundMan Wave - (Other Jazz based cards should work but I don't have any reports - about them). - -Sound Blasters - SB 1.0 to 2.0 - SB Pro - SB 16 - SB32/64/AWE - Configure SB32/64/AWE just like SB16. See lowlevel/README.awe - for information about using the wave table synth. - NOTE! AWE63/Gold and 16/32/AWE "PnP" cards need to be activated - using isapnptools before they work with OSS/Free. - SB16 compatible cards by other manufacturers than Creative. - You have been fooled since there are _no_ SB16 compatible - cards on the market (as of May 1997). It's likely that your card - is compatible just with SB Pro but there is also a non-SB- - compatible 16 bit mode. Usually it's MSS/WSS but it could also - be a proprietary one like MV Jazz16 or ESS ES688. OPTi - MAD16 chips are very common in so called "SB 16 bit cards" - (try with the MAD16 driver). - - ====================================================================== - "Supposed to be SB compatible" cards. - Forget the SB compatibility and check for other alternatives - first. The only cards that work with the SB driver in - Linux have been made by Creative Technology (there is at least - one chip on the card with "CREATIVE" printed on it). The - only other SB compatible chips are ESS and Jazz16 chips - (maybe ALSxxx chips too but they probably don't work). - Most other "16 bit SB compatible" cards such as "OPTi/MAD16" or - "Crystal" are _NOT_ SB compatible in Linux. - - Practically all sound cards have some kind of SB emulation mode - in addition to their native (16 bit) mode. In most cases this - (8 bit only) SB compatible mode doesn't work with Linux. If - you get it working it may cause problems with games and - applications which require 16 bit audio. Some 16 bit only - applications don't check if the card actually supports 16 bits. - They just dump 16 bit data to a 8 bit card which produces just - noise. - - In most cases the 16 bit native mode is supported by Linux. - Use the SB mode with "clones" only if you don't find anything - better from the rest of this doc. - ====================================================================== - -Gravis Ultrasound (GUS) - GUS - GUS + the 16 bit option - GUS MAX - GUS ACE (No MIDI port and audio recording) - GUS PnP (with RAM) - -MPU-401 and compatibles - The driver works both with the full (intelligent mode) MPU-401 - cards (such as MPU IPC-T and MQX-32M) and with the UART only - dumb MIDI ports. MPU-401 is currently the most common MIDI - interface. Most sound cards are compatible with it. However, - don't enable MPU401 mode blindly. Many cards with native support - in the driver have their own MPU401 driver. Enabling the standard one - will cause a conflict with these cards. So check if your card is - in the list of supported cards before enabling MPU401. - -Windows Sound System (MSS/WSS) - Even when Microsoft has discontinued their own Sound System card - they managed to make it a standard. MSS compatible cards are based on - a codec chip which is easily available from at least two manufacturers - (AD1848 by Analog Devices and CS4231/CS4248 by Crystal Semiconductor). - Currently most sound cards are based on one of the MSS compatible codec - chips. The CS4231 is used in the high quality cards such as GUS MAX, - MediaTrix AudioTrix Pro and TB Tropez (GUS MAX is not MSS compatible). - - Having a AD1848, CS4248 or CS4231 codec chip on the card is a good - sign. Even if the card is not MSS compatible, it could be easy to write - support for it. Note also that most MSS compatible cards - require special boot time initialization which may not be present - in the driver. Also, some MSS compatible cards have native support. - Enabling the MSS support with these cards is likely to - cause a conflict. So check if your card is listed in this file before - enabling the MSS support. - -Yamaha FM synthesizers (OPL2, OPL3 (not OPL3-SA) and OPL4) - Most sound cards have a FM synthesizer chip. The OPL2 is a 2 - operator chip used in the original AdLib card. Currently it's used - only in the cheapest (8 bit mono) cards. The OPL3 is a 4 operator - FM chip which provides better sound quality and/or more available - voices than the OPL2. The OPL4 is a new chip that has an OPL3 and - a wave table synthesizer packed onto the same chip. The driver supports - just the OPL3 mode directly. Most cards with an OPL4 (like - SM Wave and AudioTrix Pro) support the OPL4 mode using MPU401 - emulation. Writing a native OPL4 support is difficult - since Yamaha doesn't give information about their sample ROM chip. - - Enable the generic OPL2/OPL3 FM synthesizer support if your - card has a FM chip made by Yamaha. Don't enable it if your card - has a software (TRS) based FM emulator. - - ---------------------------------------------------------------- - NOTE! OPL3-SA is different chip than the ordinary OPL3. In addition - to the FM synth this chip has also digital audio (WSS) and - MIDI (MPU401) capabilities. Support for OPL3-SA is described below. - ---------------------------------------------------------------- - -Yamaha OPL3-SA1 - - Yamaha OPL3-SA1 (YMF701) is an audio controller chip used on some - (Intel) motherboards and on cheap sound cards. It should not be - confused with the original OPL3 chip (YMF278) which is entirely - different chip. OPL3-SA1 has support for MSS, MPU401 and SB Pro - (not used in OSS/Free) in addition to the OPL3 FM synth. - - There are also chips called OPL3-SA2, OPL3-SA3, ..., OPL3SA-N. They - are PnP chips and will not work with the OPL3-SA1 driver. You should - use the standard MSS, MPU401 and OPL3 options with these chips and to - activate the card using isapnptools. - -4Front Technologies SoftOSS - - SoftOSS is a software based wave table emulation which works with - any 16 bit stereo sound card. Due to its nature a fast CPU is - required (P133 is minimum). Although SoftOSS does _not_ use MMX - instructions it has proven out that recent processors (which appear - to have MMX) perform significantly better with SoftOSS than earlier - ones. For example a P166MMX beats a PPro200. SoftOSS should not be used - on 486 or 386 machines. - - The amount of CPU load caused by SoftOSS can be controlled by - selecting the CONFIG_SOFTOSS_RATE and CONFIG_SOFTOSS_VOICES - parameters properly (they will be prompted by make config). It's - recommended to set CONFIG_SOFTOSS_VOICES to 32. If you have a - P166MMX or faster (PPro200 is not faster) you can set - CONFIG_SOFTOSS_RATE to 44100 (kHz). However with slower systems it - recommended to use sampling rates around 22050 or even 16000 kHz. - Selecting too high values for these parameters may hang your - system when playing MIDI files with hight degree of polyphony - (number of concurrently playing notes). It's also possible to - decrease CONFIG_SOFTOSS_VOICES. This makes it possible to use - higher sampling rates. However using fewer voices decreases - playback quality more than decreasing the sampling rate. - - SoftOSS keeps the samples loaded on the system's RAM so much RAM is - required. SoftOSS should never be used on machines with less than 16 MB - of RAM since this is potentially dangerous (you may accidentally run out - of memory which probably crashes the machine). - - SoftOSS implements the wave table API originally designed for GUS. For - this reason all applications designed for GUS should work (at least - after minor modifications). For example gmod/xgmod and playmidi -g are - known to work. - - To work SoftOSS will require GUS compatible - patch files to be installed on the system (in /dos/ultrasnd/midi). You - can use the public domain MIDIA patchset available from several ftp - sites. - - ********************************************************************* - IMPORTANT NOTICE! The original patch set distributed with the Gravis - Ultrasound card is not in public domain (even though it's available from - some FTP sites). You should contact Voice Crystal (www.voicecrystal.com) - if you like to use these patches with SoftOSS included in OSS/Free. - ********************************************************************* - -PSS based cards (AD1848 + ADSP-2115 + Echo ESC614 ASIC) - Analog Devices and Echo Speech have together defined a sound card - architecture based on the above chips. The DSP chip is used - for emulation of SB Pro, FM and General MIDI/MT32. - - There are several cards based on this architecture. The most known - ones are Orchid SW32 and Cardinal DSP16. - - The driver supports downloading DSP algorithms to these cards. - - NOTE! You will have to use the "old" config script when configuring - PSS cards. - -MediaTrix AudioTrix Pro - The ATP card is built around a CS4231 codec and an OPL4 synthesizer - chips. The OPL4 mode is supported by a microcontroller running a - General MIDI emulator. There is also a SB 1.5 compatible playback mode. - -Ensoniq SoundScape and compatibles - Ensoniq has designed a sound card architecture based on the - OTTO synthesizer chip used in their professional MIDI synthesizers. - Several companies (including Ensoniq, Reveal and Spea) are selling - cards based on this architecture. - - NOTE! The SoundScape PnP is not supported by OSS/Free. Ensoniq VIVO and - VIVO90 cards are not compatible with Soundscapes so the Soundscape - driver will not work with them. You may want to use OSS/Linux with these - cards. - -OPTi MAD16 and Mozart based cards - The Mozart (OAK OTI-601), MAD16 (OPTi 82C928), MAD16 Pro (OPTi 82C929), - OPTi 82C924/82C925 (in _non_ PnP mode) and OPTi 82C930 interface - chips are used in many different sound cards, including some - cards by Reveal miro and Turtle Beach (Tropez). The purpose of these - chips is to connect other audio components to the PC bus. The - interface chip performs address decoding for the other chips. - NOTE! Tropez Plus is not MAD16 but CS4232 based. - NOTE! MAD16 PnP cards (82C924, 82C925, 82C931) are not MAD16 compatible - in the PnP mode. You will have to use them in MSS mode after having - initialized them using isapnptools or DOS. 82C931 probably requires - initialization using DOS/Windows (running isapnptools is not enough). - It's possible to use 82C931 with OSS/Free by jumpering it to non-PnP - mode (provided that the card has a jumper for this). In non-PnP mode - 82C931 is compatible with 82C930 and should work with the MAD16 driver - (without need to use isapnptools or DOS to initialize it). All OPTi - chips are supported by OSS/Linux (both in PnP and non-PnP modes). - -Audio Excel DSP16 - Support for this card was written by Riccardo Faccetti - (riccardo@cdc8g5.cdc.polimi.it). The AEDSP16 driver included in - the lowlevel/ directory. To use it you should enable the - "Additional low level drivers" option. - -Crystal CS4232 and CS4236 based cards such as AcerMagic S23, TB Tropez _Plus_ and - many PC motherboards (Compaq, HP, Intel, ...) - CS4232 is a PnP multimedia chip which contains a CS3231A codec, - SB and MPU401 emulations. There is support for OPL3 too. - Unfortunately the MPU401 mode doesn't work (I don't know how to - initialize it). CS4236 is an enhanced (compatible) version of CS4232. - NOTE! Don't ever try to use isapnptools with CS4232 since this will just - freeze your machine (due to chip bugs). If you have problems in getting - CS4232 working you could try initializing it with DOS (CS4232C.EXE) and - then booting Linux using loadlin. CS4232C.EXE loads a secret firmware - patch which is not documented by Crystal. - -Turtle Beach Maui and Tropez "classic" - This driver version supports sample, patch and program loading commands - described in the Maui/Tropez User's manual. - There is now full initialization support too. The audio side of - the Tropez is based on the MAD16 chip (see above). - NOTE! Tropez Plus is different card than Tropez "classic" and will not - work fully in Linux. You can get audio features working by configuring - the card as a CS4232 based card (above). - - -Jumpers and software configuration -================================== - -Some of the earliest sound cards were jumper configurable. You have to -configure the driver use I/O, IRQ and DMA settings -that match the jumpers. Just few 8 bit cards are fully jumper -configurable (SB 1.x/2.x, SB Pro and clones). -Some cards made by Aztech have an EEPROM which contains the -config info. These cards behave much like hardware jumpered cards. - -Most cards have jumper for the base I/O address but other parameters -are software configurable. Sometimes there are few other jumpers too. - -Latest cards are fully software configurable or they are PnP ISA -compatible. There are no jumpers on the board. - -The driver handles software configurable cards automatically. Just configure -the driver to use I/O, IRQ and DMA settings which are known to work. -You could usually use the same values than with DOS and/or Windows. -Using different settings is possible but not recommended since it may cause -some trouble (for example when warm booting from an OS to another or -when installing new hardware to the machine). - -Sound driver sets the soft configurable parameters of the card automatically -during boot. Usually you don't need to run any extra initialization -programs when booting Linux but there are some exceptions. See the -card-specific instructions below for more info. - -The drawback of software configuration is that the driver needs to know -how the card must be initialized. It cannot initialize unknown cards -even if they are otherwise compatible with some other cards (like SB, -MPU401 or Windows Sound System). - - -What if your card was not listed above? -======================================= - -The first thing to do is to look at the major IC chips on the card. -Many of the latest sound cards are based on some standard chips. If you -are lucky, all of them could be supported by the driver. The most common ones -are the OPTi MAD16, Mozart, SoundScape (Ensoniq) and the PSS architectures -listed above. Also look at the end of this file for list of unsupported -cards and the ones which could be supported later. - -The last resort is to send _exact_ name and model information of the card -to me together with a list of the major IC chips (manufactured, model) to -me. I could then try to check if your card looks like something familiar. - -There are many more cards in the world than listed above. The first thing to -do with these cards is to check if they emulate some other card or interface -such as SB, MSS and/or MPU401. In this case there is a chance to get the -card to work by booting DOS before starting Linux (boot DOS, hit ctrl-alt-del -and boot Linux without hard resetting the machine). In this method the -DOS based driver initializes the hardware to use known I/O, IRQ and DMA -settings. If sound driver is configured to use the same settings, everything -should work OK. - - -Configuring sound driver (with Linux) -===================================== - -The sound driver is currently distributed as part of the Linux kernel. The -files are in /usr/src/linux/drivers/sound/. - -**************************************************************************** -* ALWAYS USE THE SOUND DRIVER VERSION WHICH IS DISTRIBUTED WITH * -* THE KERNEL SOURCE PACKAGE YOU ARE USING. SOME ALPHA AND BETA TEST * -* VERSIONS CAN BE INSTALLED FROM A SEPARATELY DISTRIBUTED PACKAGE * -* BUT CHECK THAT THE PACKAGE IS NOT MUCH OLDER (OR NEWER) THAN THE * -* KERNEL YOU ARE USING. IT'S POSSIBLE THAT THE KERNEL/DRIVER * -* INTERFACE CHANGES BETWEEN KERNEL RELEASES WHICH MAY CAUSE SOME * -* INCOMPATIBILITY PROBLEMS. * -* * -* IN CASE YOU INSTALL A SEPARATELY DISTRIBUTED SOUND DRIVER VERSION, * -* BE SURE TO REMOVE OR RENAME THE OLD SOUND DRIVER DIRECTORY BEFORE * -* INSTALLING THE NEW ONE. LEAVING OLD FILES TO THE SOUND DRIVER * -* DIRECTORY _WILL_ CAUSE PROBLEMS WHEN THE DRIVER IS USED OR * -* COMPILED. * -**************************************************************************** - -To configure the driver, run "make config" in the kernel source directory -(/usr/src/linux). Answer "y" or "m" to the question about Sound card support -(after the questions about mouse, CD-ROM, ftape, etc. support). Questions -about options for sound will then be asked. - -After configuring the kernel and sound driver and compile the kernel -following instructions in the kernel README. - -The sound driver configuration dialog -------------------------------------- - -Sound configuration starts by making some yes/no questions. Be careful -when answering to these questions since answering y to a question may -prevent some later ones from being asked. For example don't answer y to -the first question (PAS16) if you don't really have a PAS16. Don't enable -more cards than you really need since they just consume memory. Also -some drivers (like MPU401) may conflict with your SCSI controller and -prevent kernel from booting. If you card was in the list of supported -cards (above), please look at the card specific config instructions -(later in this file) before starting to configure. Some cards must be -configured in way which is not obvious. - -So here is the beginning of the config dialog. Answer 'y' or 'n' to these -questions. The default answer is shown so that (y/n) means 'y' by default and -(n/y) means 'n'. To use the default value, just hit ENTER. But be careful -since using the default _doesn't_ guarantee anything. - -Note also that all questions may not be asked. The configuration program -may disable some questions depending on the earlier choices. It may also -select some options automatically as well. - - "ProAudioSpectrum 16 support", - - Answer 'y'_ONLY_ if you have a Pro Audio Spectrum _16_, - Pro Audio Studio 16 or Logitech SoundMan 16 (be sure that - you read the above list correctly). Don't answer 'y' if you - have some other card made by Media Vision or Logitech since they - are not PAS16 compatible. - NOTE! Since 3.5-beta10 you need to enable SB support (next question) - if you want to use the SB emulation of PAS16. It's also possible to - the emulation if you want to use a true SB card together with PAS16 - (there is another question about this that is asked later). - "Sound Blaster support", - - Answer 'y' if you have an original SB card made by Creative Labs - or a full 100% hardware compatible clone (like Thunderboard or - SM Games). If your card was in the list of supported cards (above), - please look at the card specific instructions later in this file - before answering this question. For an unknown card you may answer - 'y' if the card claims to be SB compatible. - Enable this option also with PAS16 (changed since v3.5-beta9). - - Don't enable SB if you have a MAD16 or Mozart compatible card. - - "Generic OPL2/OPL3 FM synthesizer support", - - Answer 'y' if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). - Answering 'y' is usually a safe and recommended choice. However some - cards may have software (TSR) FM emulation. Enabling FM support - with these cards may cause trouble. However I don't currently know - such cards. - "Gravis Ultrasound support", - - Answer 'y' if you have GUS or GUS MAX. Answer 'n' if you don't - have GUS since the GUS driver consumes much memory. - Currently I don't have experiences with the GUS ACE so I don't - know what to answer with it. - "MPU-401 support (NOT for SB16)", - - Be careful with this question. The MPU401 interface is supported - by almost any sound card today. However some natively supported cards - have their own driver for MPU401. Enabling the MPU401 option with - these cards will cause a conflict. Also enabling MPU401 on a system - that doesn't really have a MPU401 could cause some trouble. If your - card was in the list of supported cards (above), please look at - the card specific instructions later in this file. - - In MOST cases this MPU401 driver should only be used with "true" - MIDI-only MPU401 professional cards. In most other cases there - is another way to get the MPU401 compatible interface of a - sound card to work. - Support for the MPU401 compatible MIDI port of SB16, ESS1688 - and MV Jazz16 cards is included in the SB driver. Use it instead - of this separate MPU401 driver with these cards. As well - Soundscape, PSS and Maui drivers include their own MPU401 - options. - - It's safe to answer 'y' if you have a true MPU401 MIDI interface - card. - "6850 UART Midi support", - - It's safe to answer 'n' to this question in all cases. The 6850 - UART interface is so rarely used. - "PSS (ECHO-ADI2111) support", - - Answer 'y' only if you have Orchid SW32, Cardinal DSP16 or some - other card based on the PSS chipset (AD1848 codec + ADSP-2115 - DSP chip + Echo ESC614 ASIC CHIP). - "16 bit sampling option of GUS (_NOT_ GUS MAX)", - - Answer 'y' if you have installed the 16 bit sampling daughtercard - to your GUS. Answer 'n' if you have GUS MAX. Enabling this option - disables GUS MAX support. - "GUS MAX support", - - Answer 'y' only if you have a GUS MAX. - "Microsoft Sound System support", - - Again think carefully before answering 'y' to this question. It's - safe to answer 'y' in case you have the original Windows Sound - System card made by Microsoft or Aztech SG 16 Pro (or NX16 Pro). - Also you may answer 'y' in case your card was not listed earlier - in this file. For cards having native support in the driver, consult - the card specific instructions later in this file. Some drivers - have their own MSS support and enabling this option will cause a - conflict. - Note! The MSS driver permits configuring two DMA channels. This is a - "nonstandard" feature and works only with very few cards (if any). - In most cases the second DMA channel should be disabled or set to - the same channel than the first one. Trying to configure two separate - channels with cards that don't support this feature will prevent - audio (at least recording) from working. - "Ensoniq Soundscape support", - - Answer 'y' if you have a sound card based on the Ensoniq SoundScape - chipset. Such cards are being manufactured at least by Ensoniq, - Spea and Reveal (note that Reveal makes other cards also). The oldest - cards made by Spea don't work properly with Linux. - Soundscape PnP as well as Ensoniq VIVO work only with the commercial - OSS/Linux version. - "MediaTrix AudioTrix Pro support", - - Answer 'y' if you have the AudioTrix Pro. - "Support for MAD16 and/or Mozart based cards", - - Answer y if your card has a Mozart (OAK OTI-601) or MAD16 - (OPTi 82C928, 82C929, 82C924/82C925 or 82C930) audio interface chip. - These chips are - currently quite common so it's possible that many no-name cards - have one of them. In addition the MAD16 chip is used in some - cards made by known manufacturers such as Turtle Beach (Tropez), - Reveal (some models) and Diamond (some recent models). - Note OPTi 82C924 and 82C925 are MAD16 compatible only in non PnP - mode (jumper selectable on many cards). - "Support for TB Maui" - - This enables TB Maui specific initialization. Works with TB Maui - and TB Tropez (may not work with Tropez Plus). - - -Then the configuration program asks some y/n questions about the higher -level services. It's recommended to answer 'y' to each of these questions. -Answer 'n' only if you know you will not need the option. - - "MIDI interface support", - - Answering 'n' disables /dev/midi## devices and access to any - MIDI ports using /dev/sequencer and /dev/music. This option - also affects any MPU401 and/or General MIDI compatible devices. - "FM synthesizer (YM3812/OPL-3) support", - - Answer 'y' here. - "/dev/sequencer support", - - Answering 'n' disables /dev/sequencer and /dev/music. - -Entering the I/O, IRQ and DMA config parameters ------------------------------------------------ - -After the above questions the configuration program prompts for the -card specific configuration information. Usually just a set of -I/O address, IRQ and DMA numbers are asked. With some cards the program -asks for some files to be used during initialization of the card. For example -many cards have a DSP chip or microprocessor which must be initialized by -downloading a program (microcode) file to the card. - -Instructions for answering these questions are given in the next section. - - -Card specific information -========================= - -This section gives additional instructions about configuring some cards. -Please refer manual of your card for valid I/O, IRQ and DMA numbers. Using -the same settings with DOS/Windows and Linux is recommended. Using -different values could cause some problems when switching between -different operating systems. - -Sound Blasters (the original ones by Creative) ---------------------------------------------- - -NOTE! Check if you have a PnP Sound Blaster (cards sold after summer 1995 - are almost certainly PnP ones). With PnP cards you should use isapnptools - to activate them (see above). - -It's possible to configure these cards to use different I/O, IRQ and -DMA settings. Since the possible/default settings have changed between various -models, you have to consult manual of your card for the proper ones. It's -a good idea to use the same values than with DOS/Windows. With SB and SB Pro -it's the only choice. SB16 has software selectable IRQ and DMA channels but -using different values with DOS and Linux is likely to cause troubles. The -DOS driver is not able to reset the card properly after warm boot from Linux -if Linux has used different IRQ or DMA values. - -The original (steam) Sound Blaster (versions 1.x and 2.x) use always -DMA1. There is no way to change it. - -The SB16 needs two DMA channels. A 8 bit one (1 or 3) is required for -8 bit operation and a 16 bit one (5, 6 or 7) for the 16 bit mode. In theory -it's possible to use just one (8 bit) DMA channel by answering the 8 bit -one when the configuration program asks for the 16 bit one. This may work -in some systems but is likely to cause terrible noise on some other systems. - -It's possible to use two SB16/32/64 at the same time. To do this you should -first configure OSS/Free for one card. Then edit local.h manually and define -SB2_BASE, SB2_IRQ, SB2_DMA and SB2_DMA2 for the second one. You can't get -the OPL3, MIDI and EMU8000 devices of the second card to work. If you are -going to use two PnP Sound Blasters, ensure that they are of different model -and have different PnP IDs. There is no way to get two cards with the same -card ID and serial number to work. The easiest way to check this is trying -if isapnptools can see both cards or just one. - -NOTE! Don't enable the SM Games option (asked by the configuration program) - if you are not 101% sure that your card is a Logitech Soundman Games - (not a SM Wave or SM16). - -SB Clones ---------- - -First of all: There are no SB16 clones. There are SB Pro clones with a -16 bit mode which is not SB16 compatible. The most likely alternative is that -the 16 bit mode means MSS/WSS. - -There are just a few fully 100% hardware SB or SB Pro compatible cards. -I know just Thunderboard and SM Games. Other cards require some kind of -hardware initialization before they become SB compatible. Check if your card -was listed in the beginning of this file. In this case you should follow -instructions for your card later in this file. - -For other not fully SB clones you may try initialization using DOS in -the following way: - - - Boot DOS so that the card specific driver gets run. - - Hit ctrl-alt-del (or use loadlin) to boot Linux. Don't - switch off power or press the reset button. - - If you use the same I/O, IRQ and DMA settings in Linux, the - card should work. - -If your card is both SB and MSS compatible, I recommend using the MSS mode. -Most cards of this kind are not able to work in the SB and the MSS mode -simultaneously. Using the MSS mode provides 16 bit recording and playback. - -ProAudioSpectrum 16 and compatibles ------------------------------------ - -PAS16 has a SB emulation chip which can be used together with the native -(16 bit) mode of the card. To enable this emulation you should configure -the driver to have SB support too (this has been changed since version -3.5-beta9 of this driver). - -With current driver versions it's also possible to use PAS16 together with -another SB compatible card. In this case you should configure SB support -for the other card and to disable the SB emulation of PAS16 (there is a -separate questions about this). - -With PAS16 you can use two audio device files at the same time. /dev/dsp (and -/dev/audio) is connected to the 8/16 bit native codec and the /dev/dsp1 (and -/dev/audio1) is connected to the SB emulation (8 bit mono only). - -Gravis Ultrasound ------------------ - -There are many different revisions of the Ultrasound card (GUS). The -earliest ones (pre 3.7) don't have a hardware mixer. With these cards -the driver uses a software emulation for synth and pcm playbacks. It's -also possible to switch some of the inputs (line in, mic) off by setting -mixer volume of the channel level below 10%. For recording you have -to select the channel as a recording source and to use volume above 10%. - -GUS 3.7 has a hardware mixer. - -GUS MAX and the 16 bit sampling daughtercard have a CS4231 codec chip which -also contains a mixer. - -Configuring GUS is simple. Just enable the GUS support and GUS MAX or -the 16 bit daughtercard if you have them. Note that enabling the daughter -card disables GUS MAX driver. - -NOTE for owners of the 16 bit daughtercard: By default the daughtercard -uses /dev/dsp (and /dev/audio). Command "ln -sf /dev/dsp1 /dev/dsp" -selects the daughter card as the default device. - -With just the standard GUS enabled the configuration program prompts -for the I/O, IRQ and DMA numbers for the card. Use the same values than -with DOS. - -With the daughter card option enabled you will be prompted for the I/O, -IRQ and DMA numbers for the daughter card. You have to use different I/O -and DMA values than for the standard GUS. The daughter card permits -simultaneous recording and playback. Use /dev/dsp (the daughtercard) for -recording and /dev/dsp1 (GUS GF1) for playback. - -GUS MAX uses the same I/O address and IRQ settings than the original GUS -(GUS MAX = GUS + a CS4231 codec). In addition an extra DMA channel may be used. -Using two DMA channels permits simultaneous playback using two devices -(dev/dsp0 and /dev/dsp1). The second DMA channel is required for -full duplex audio. -To enable the second DMA channels, give a valid DMA channel when the config -program asks for the GUS MAX DMA (entering -1 disables the second DMA). -Using 16 bit DMA channels (5,6 or 7) is recommended. - -If you have problems in recording with GUS MAX, you could try to use -just one 8 bit DMA channel. Recording will not work with one DMA -channel if it's a 16 bit one. - -Microphone input of GUS MAX is connected to mixer in little bit nonstandard -way. There is actually two microphone volume controls. Normal "mic" controls -only recording level. Mixer control "speaker" is used to control volume of -microphone signal connected directly to line/speaker out. So just decrease -volume of "speaker" if you have problems with microphone feedback. - -GUS ACE works too but any attempt to record or to use the MIDI port -will fail. - -GUS PnP (with RAM) is partially supported but it needs to be initialized using -DOS or isapnptools before starting the driver. - -MPU401 and Windows Sound System -------------------------------- - -Again. Don't enable these options in case your card is listed -somewhere else in this file. - -Configuring these cards is obvious (or it should be). With MSS -you should probably enable the OPL3 synth also since -most MSS compatible cards have it. However check that this is true -before enabling OPL3. - -Sound driver supports more than one MPU401 compatible cards at the same time -but the config program asks config info for just the first of them. -Adding the second or third MPU interfaces must be done manually by -editing sound/local.h (after running the config program). Add defines for -MPU2_BASE & MPU2_IRQ (and MPU3_BASE & MPU3_IRQ) to the file. - -CAUTION! - -The default I/O base of Adaptec AHA-1542 SCSI controller is 0x330 which -is also the default of the MPU401 driver. Don't configure the sound driver to -use 0x330 as the MPU401 base if you have a AHA1542. The kernel will not boot -if you make this mistake. - -PSS ---- - -Even the PSS cards are compatible with SB, MSS and MPU401, you must not -enable these options when configuring the driver. The configuration -program handles these options itself. (You may use the SB, MPU and MSS options -together with PSS if you have another card on the system). - -The PSS driver enables MSS and MPU401 modes of the card. SB is not enabled -since it doesn't work concurrently with MSS. The driver loads also a -DSP algorithm which is used to for the general MIDI emulation. The -algorithm file (.ld) is read by the config program and written to a -file included when the pss.c is compiled. For this reason the config -program asks if you want to download the file. Use the genmidi.ld file -distributed with the DOS/Windows drivers of the card (don't use the mt32.ld). -With some cards the file is called 'synth.ld'. You must have access to -the file when configuring the driver. The easiest way is to mount the DOS -partition containing the file with Linux. - -It's possible to load your own DSP algorithms and run them with the card. -Look at the directory pss_test of snd-util-3.0.tar.gz for more info. - -AudioTrix Pro -------------- - -You have to enable the OPL3 and SB (not SB Pro or SB16) drivers in addition -to the native AudioTrix driver. Don't enable MSS or MPU drivers. - -Configuring ATP is little bit tricky since it uses so many I/O, IRQ and -DMA numbers. Using the same values than with DOS/Win is a good idea. Don't -attempt to use the same IRQ or DMA channels twice. - -The SB mode of ATP is implemented so the ATP driver just enables SB -in the proper address. The SB driver handles the rest. You have to configure -both the SB driver and the SB mode of ATP to use the same IRQ, DMA and I/O -settings. - -Also the ATP has a microcontroller for the General MIDI emulation (OPL4). -For this reason the driver asks for the name of a file containing the -microcode (TRXPRO.HEX). This file is usually located in the directory -where the DOS drivers were installed. You must have access to this file -when configuring the driver. - -If you have the effects daughtercard, it must be initialized by running -the setfx program of snd-util-3.0.tar.gz package. This step is not required -when using the (future) binary distribution version of the driver. - -Ensoniq SoundScape ------------------- - -NOTE! The new PnP SoundScape is not supported yet. Soundscape compatible - cards made by Reveal don't work with Linux. They use older revision - of the Soundscape chipset which is not fully compatible with - newer cards made by Ensoniq. - -The SoundScape driver handles initialization of MSS and MPU supports -itself so you don't need to enable other drivers than SoundScape -(enable also the /dev/dsp, /dev/sequencer and MIDI supports). - -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!!!!! !!!! -!!!!! NOTE! Before version 3.5-beta6 there WERE two sets of audio !!!! -!!!!! device files (/dev/dsp0 and /dev/dsp1). The first one WAS !!!! -!!!!! used only for card initialization and the second for audio !!!! -!!!!! purposes. It WAS required to change /dev/dsp (a symlink) to !!!! -!!!!! point to /dev/dsp1. !!!! -!!!!! !!!! -!!!!! This is not required with OSS versions 3.5-beta6 and later !!!! -!!!!! since there is now just one audio device file. Please !!!! -!!!!! change /dev/dsp to point back to /dev/dsp0 if you are !!!! -!!!!! upgrading from an earlier driver version using !!!! -!!!!! (cd /dev;rm dsp;ln -s dsp0 dsp). !!!! -!!!!! !!!! -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -The configuration program asks one DMA channel and two interrupts. One IRQ -and one DMA is used by the MSS codec. The second IRQ is required for the -MPU401 mode (you have to use different IRQs for both purposes). -There were earlier two DMA channels for SoundScape but the current driver -version requires just one. - -The SoundScape card has a Motorola microcontroller which must initialized -_after_ boot (the driver doesn't initialize it during boot). -The initialization is done by running the 'ssinit' program which is -distributed in the snd-util-3.0.tar.gz package. You have to edit two -defines in the ssinit.c and then compile the program. You may run ssinit -manually (after each boot) or add it to /etc/rc.d/rc.local. - -The ssinit program needs the microcode file that comes with the DOS/Windows -driver of the card. You will need to use version 1.30.00 or later -of the microcode file (sndscape.co0 or sndscape.co1 depending on -your card model). THE OLD sndscape.cod WILL NOT WORK. IT WILL HANG YOUR -MACHINE. The only way to get the new microcode file is to download -and install the DOS/Windows driver from ftp://ftp.ensoniq.com/pub. - -Then you have to select the proper microcode file to use: soundscape.co0 -is the right one for most cards and sndscape.co1 is for few (older) cards -made by Reveal and/or Spea. The driver has capability to detect the card -version during boot. Look at the boot log messages in /var/adm/messages -and locate the sound driver initialization message for the SoundScape -card. If the driver displays string <Ensoniq Soundscape (old)>, you have -an old card and you will need to use sndscape.co1. For other cards use -soundscape.co0. New Soundscape revisions such as Elite and PnP use -code files with higher numbers (.co2, .co3, etc.). - -NOTE! Ensoniq Soundscape VIVO is not compatible with other Soundscape cards. - Currently it's possible to use it in Linux only with OSS/Linux - drivers. - -Check /var/adm/messages after running ssinit. The driver prints -the board version after downloading the microcode file. That version -number must match the number in the name of the microcode file (extension). - -Running ssinit with a wrong version of the sndscape.co? file is not -dangerous as long as you don't try to use a file called sndscape.cod. -If you have initialized the card using a wrong microcode file (sounds -are terrible), just modify ssinit.c to use another microcode file and try -again. It's possible to use an earlier version of sndscape.co[01] but it -may sound weird. - -MAD16 (Pro) and Mozart ----------------------- - -You need to enable just the MAD16 /Mozart support when configuring -the driver. _Don't_ enable SB, MPU401 or MSS. However you will need the -/dev/audio, /dev/sequencer and MIDI supports. - -Mozart and OPTi 82C928 (the original MAD16) chips don't support -MPU401 mode so enter just 0 when the configuration program asks the -MPU/MIDI I/O base. The MAD16 Pro (OPTi 82C929) and 82C930 chips have MPU401 -mode. - -TB Tropez is based on the 82C929 chip. It has two MIDI ports. -The one connected to the MAD16 chip is the second one (there is a second -MIDI connector/pins somewhere??). If you have not connected the second MIDI -port, just disable the MIDI port of MAD16. The 'Maui' compatible synth of -Tropez is jumper configurable and not connected to the MAD16 chip (the -Maui driver can be used with it). - -Some MAD16 based cards may cause feedback, whistle or terrible noise if the -line3 mixer channel is turned too high. This happens at least with Shuttle -Sound System. Current driver versions set volume of line3 low enough so -this should not be a problem. - -If you have a MAD16 card which have an OPL4 (FM + Wave table) synthesizer -chip (_not_ an OPL3), you have to append a line containing #define MAD16_OPL4 -to the file linux/drivers/sound/local.h (after running make config). - -MAD16 cards having a CS4231 codec support full duplex mode. This mode -can be enabled by configuring the card to use two DMA channels. Possible -DMA channel pairs are: 0&1, 1&0 and 3&0. - -NOTE! Cards having an OPTi 82C924/82C925 chip work with OSS/Free only in -non-PnP mode (usually jumper selectable). The PnP mode is supported only -by OSS/Linux. - -MV Jazz (ProSonic) ------------------- - -The Jazz16 driver is just a hack made to the SB Pro driver. However it works -fairly well. You have to enable SB, SB Pro (_not_ SB16) and MPU401 supports -when configuring the driver. The configuration program asks later if you -want support for MV Jazz16 based cards (after asking SB base address). Answer -'y' here and the driver asks the second (16 bit) DMA channel. - -The Jazz16 driver uses the MPU401 driver in a way which will cause -problems if you have another MPU401 compatible card. In this case you must -give address of the Jazz16 based MPU401 interface when the config -program prompts for the MPU401 information. Then look at the MPU401 -specific section for instructions about configuring more than one MPU401 cards. - -Logitech Soundman Wave ----------------------- - -Read the above MV Jazz specific instructions first. - -The Logitech SoundMan Wave (don't confuse this with the SM16 or SM Games) is -a MV Jazz based card which has an additional OPL4 based wave table -synthesizer. The OPL4 chip is handled by an on board microcontroller -which must be initialized during boot. The config program asks if -you have a SM Wave immediately after asking the second DMA channel of jazz16. -If you answer 'y', the config program will ask name of the file containing -code to be loaded to the microcontroller. The file is usually called -MIDI0001.BIN and it's located in the DOS/Windows driver directory. The file -may also be called as TSUNAMI.BIN or something else (older cards?). - -The OPL4 synth will be inaccessible without loading the microcontroller code. - -Also remember to enable SB MPU401 support if you want to use the OPL4 mode. -(Don't enable the 'normal' MPU401 device as with some earlier driver -versions (pre 3.5-alpha8)). - -NOTE! Don't answer 'y' when the driver asks about SM Games support - (the next question after the MIDI0001.BIN name). However - answering 'y' doesn't cause damage your computer so don't panic. - -Sound Galaxies --------------- - -There are many different Sound Galaxy cards made by Aztech. The 8 bit -ones are fully SB or SB Pro compatible and there should be no problems -with them. - -The older 16 bit cards (SG Pro16, SG NX Pro16, Nova and Lyra) have -an EEPROM chip for storing the configuration data. There is a microcontroller -which initializes the card to match the EEPROM settings when the machine -is powered on. These cards actually behave just like they have jumpers -for all of the settings. Configure driver for MSS, MPU, SB/SB Pro and OPL3 -supports with these cards. - -There are some new Sound Galaxies in the market. I have no experience with -them so read the card's manual carefully. - -ESS ES1688 and ES688 'AudioDrive' based cards ---------------------------------------------- - -Support for these two ESS chips is embedded in the SB driver. -Configure these cards just like SB. Enable the 'SB MPU401 MIDI port' -if you want to use MIDI features of ES1688. ES688 doesn't have MPU mode -so you don't need to enable it (the driver uses normal SB MIDI automatically -with ES688). - -NOTE! ESS cards are not compatible with MSS/WSS so don't worry if MSS support -of OSS doesn't work with it. - -There are some ES1688/688 based sound cards and (particularly) motherboards -which use software configurable I/O port relocation feature of the chip. -This ESS proprietary feature is supported only by OSS/Linux. - -There are ES1688 based cards which use different interrupt pin assignment than -recommended by ESS (5, 7, 9/2 and 10). In this case all IRQs don't work. -At least a card called (Pearl?) Hypersound 16 supports IRQ 15 but it doesn't -work. - -ES1868 is a PnP chip which is (supposed to be) compatible with ESS1688 -probably works with OSS/Free after initialization using isapnptools. - -Reveal cards ------------- - -There are several different cards made/marketed by Reveal. Some of them -are compatible with SoundScape and some use the MAD16 chip. You may have -to look at the card and try to identify its origin. - -Diamond -------- - -The oldest (Sierra Aria based) sound cards made by Diamond are not supported -(they may work if the card is initialized using DOS). The recent (LX?) -models are based on the MAD16 chip which is supported by the driver. - -Audio Excel DSP16 ------------------ - -Support for this card is currently not functional. A new driver for it -should be available later this year. - -PCMCIA cards ------------- - -Sorry, can't help. Some cards may work and some don't. - -TI TM4000M notebooks --------------------- - -These computers have a built in sound support based on the Jazz chipset. -Look at the instructions for MV Jazz (above). It's also important to note -that there is something wrong with the mouse port and sound at least on -some TM models. Don't enable the "C&T 82C710 mouse port support" when -configuring Linux. Having it enabled is likely to cause mysterious problems -and kernel failures when sound is used. - -miroSOUND ---------- - -The miroSOUND PCM1-pro, PCM12 and PCM20 radio has been used -successfully. These cards are based on the MAD16, OPL4, and CS4231A chips -and everything said in the section about MAD16 cards applies here, -too. The only major difference between the PCMxx and other MAD16 cards -is that instead of the mixer in the CS4231 codec a separate mixer -controlled by an on-board 80C32 microcontroller is used. Control of -the mixer takes place via the ACI (miro's audio control interface) -protocol that is implemented in a separate lowlevel driver. Make sure -you compile this ACI driver together with the normal MAD16 support -when you use a miroSOUND PCMxx card. The ACI mixer is controlled by -/dev/mixer and the CS4231 mixer by /dev/mixer1 (depends on load -time). Only in special cases you want to change something regularly on -the CS4231 mixer. - -The miroSOUND PCM12 and PCM20 radio is capable of full duplex -operation (simultaneous PCM replay and recording), which allows you to -implement nice real-time signal processing audio effect software and -network telephones. The ACI mixer has to be switched into the "solo" -mode for duplex operation in order to avoid feedback caused by the -mixer (input hears output signal). You can de-/activate this mode -through toggling the record button for the wave controller with an -OSS-mixer. - -The PCM20 contains a radio tuner, which is also controlled by -ACI. This radio tuner is supported by the ACI driver together with the -miropcm20.o module. Also the 7-band equalizer is integrated -(limited by the OSS-design). Development has started and maybe -finished for the RDS decoder on this card, too. You will be able to -read RadioText, the Programme Service name, Programme TYpe and -others. Even the v4l radio module benefits from it with a refined -strength value. See aci.[ch] and miropcm20*.[ch] for more details. - -The following configuration parameters have worked fine for the PCM12 -in Markus Kuhn's system, many other configurations might work, too: -CONFIG_MAD16_BASE=0x530, CONFIG_MAD16_IRQ=11, CONFIG_MAD16_DMA=3, -CONFIG_MAD16_DMA2=0, CONFIG_MAD16_MPU_BASE=0x330, CONFIG_MAD16_MPU_IRQ=10, -DSP_BUFFSIZE=65536, SELECTED_SOUND_OPTIONS=0x00281000. - -Bas van der Linden is using his PCM1-pro with a configuration that -differs in: CONFIG_MAD16_IRQ=7, CONFIG_MAD16_DMA=1, CONFIG_MAD16_MPU_IRQ=9 - -Compaq Deskpro XL ------------------ - -The builtin sound hardware of Compaq Deskpro XL is now supported. -You need to configure the driver with MSS and OPL3 supports enabled. -In addition you need to manually edit linux/drivers/sound/local.h and -to add a line containing "#define DESKPROXL" if you used -make menuconfig/xconfig. - -Others? -------- - -Since there are so many different sound cards, it's likely that I have -forgotten to mention many of them. Please inform me if you know yet another -card which works with Linux, please inform me (or is anybody else -willing to maintain a database of supported cards (just like in XF86)?). - -Cards not supported yet -======================= - -Please check the version of sound driver you are using before -complaining that your card is not supported. It's possible you are -using a driver version which was released months before your card was -introduced. - -First of all, there is an easy way to make most sound cards work with Linux. -Just use the DOS based driver to initialize the card to a known state, then use -loadlin.exe to boot Linux. If Linux is configured to use the same I/O, IRQ and -DMA numbers as DOS, the card could work. -(ctrl-alt-del can be used in place of loadlin.exe but it doesn't work with -new motherboards). This method works also with all/most PnP sound cards. - -Don't get fooled with SB compatibility. Most cards are compatible with -SB but that may require a TSR which is not possible with Linux. If -the card is compatible with MSS, it's a better choice. Some cards -don't work in the SB and MSS modes at the same time. - -Then there are cards which are no longer manufactured and/or which -are relatively rarely used (such as the 8 bit ProAudioSpectrum -models). It's extremely unlikely that such cards ever get supported. -Adding support for a new card requires much work and increases time -required in maintaining the driver (some changes need to be done -to all low level drivers and be tested too, maybe with multiple -operating systems). For this reason I have made a decision to not support -obsolete cards. It's possible that someone else makes a separately -distributed driver (diffs) for the card. - -Writing a driver for a new card is not possible if there are no -programming information available about the card. If you don't -find your new card from this file, look from the home page -(http://www.opensound.com/ossfree). Then please contact -manufacturer of the card and ask if they have (or are willing to) -released technical details of the card. Do this before contacting me. I -can only answer 'no' if there are no programming information available. - -I have made decision to not accept code based on reverse engineering -to the driver. There are three main reasons: First I don't want to break -relationships to sound card manufacturers. The second reason is that -maintaining and supporting a driver without any specs will be a pain. -The third reason is that companies have freedom to refuse selling their -products to other than Windows users. - -Some companies don't give low level technical information about their -products to public or at least their require signing a NDA. It's not -possible to implement a freeware driver for them. However it's possible -that support for such cards become available in the commercial version -of this driver (see http://www.4Front-tech.com/oss.html for more info). - -There are some common audio chipsets that are not supported yet. For example -Sierra Aria and IBM Mwave. It's possible that these architectures -get some support in future but I can't make any promises. Just look -at the home page (http://www.opensound.com/ossfree/) -for latest info. - -Information about unsupported sound cards and chipsets is welcome as well -as free copies of sound cards, SDKs and operating systems. - -If you have any corrections and/or comments, please contact me. - -Hannu Savolainen -hannu@opensound.com - -home page of OSS/Free: http://www.opensound.com/ossfree - -home page of commercial OSS -(Open Sound System) drivers: http://www.opensound.com/oss.html diff --git a/Documentation/sound/oss/README.modules b/Documentation/sound/oss/README.modules deleted file mode 100644 index cdc039421a46..000000000000 --- a/Documentation/sound/oss/README.modules +++ /dev/null @@ -1,106 +0,0 @@ -Building a modular sound driver -================================ - - The following information is current as of linux-2.1.85. Check the other -readme files, especially README.OSS, for information not specific to -making sound modular. - - First, configure your kernel. This is an idea of what you should be -setting in the sound section: - -<M> Sound card support - -<M> 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support - - I have SoundBlaster. Select your card from the list. - -<M> Generic OPL2/OPL3 FM synthesizer support -<M> FM synthesizer (YM3812/OPL-3) support - - If you don't set these, you will probably find you can play .wav files -but not .midi. As the help for them says, set them unless you know your -card does not use one of these chips for FM support. - - Once you are configured, make zlilo, modules, modules_install; reboot. -Note that it is no longer necessary or possible to configure sound in the -drivers/sound dir. Now one simply configures and makes one's kernel and -modules in the usual way. - - Then, add to your /etc/modprobe.d/oss.conf something like: - -alias char-major-14-* sb -install sb /sbin/modprobe -i sb && /sbin/modprobe adlib_card -options sb io=0x220 irq=7 dma=1 dma16=5 mpu_io=0x330 -options adlib_card io=0x388 # FM synthesizer - - Alternatively, if you have compiled in kernel level ISAPnP support: - -alias char-major-14 sb -softdep sb post: adlib_card -options adlib_card io=0x388 - - The effect of this is that the sound driver and all necessary bits and -pieces autoload on demand, assuming you use kerneld (a sound choice) and -autoclean when not in use. Also, options for the device drivers are -set. They will not work without them. Change as appropriate for your card. -If you are not yet using the very cool kerneld, you will have to "modprobe --k sb" yourself to get things going. Eventually things may be fixed so -that this kludgery is not necessary; for the time being, it seems to work -well. - - Replace 'sb' with the driver for your card, and give it the right -options. To find the filename of the driver, look in -/lib/modules/<kernel-version>/misc. Mine looks like: - -adlib_card.o # This is the generic OPLx driver -opl3.o # The OPL3 driver -sb.o # <<The SoundBlaster driver. Yours may differ.>> -sound.o # The sound driver -uart401.o # Used by sb, maybe other cards - - Whichever card you have, try feeding it the options that would be the -default if you were making the driver wired, not as modules. You can -look at function referred to by module_init() for the card to see what -args are expected. - - Note that at present there is no way to configure the io, irq and other -parameters for the modular drivers as one does for the wired drivers.. One -needs to pass the modules the necessary parameters as arguments, either -with /etc/modprobe.d/*.conf or with command-line args to modprobe, e.g. - -modprobe sb io=0x220 irq=7 dma=1 dma16=5 mpu_io=0x330 -modprobe adlib_card io=0x388 - - recommend using /etc/modprobe.d/*.conf. - -Persistent DMA Buffers: - -The sound modules normally allocate DMA buffers during open() and -deallocate them during close(). Linux can often have problems allocating -DMA buffers for ISA cards on machines with more than 16MB RAM. This is -because ISA DMA buffers must exist below the 16MB boundary and it is quite -possible that we can't find a large enough free block in this region after -the machine has been running for any amount of time. The way to avoid this -problem is to allocate the DMA buffers during module load and deallocate -them when the module is unloaded. For this to be effective we need to load -the sound modules right after the kernel boots, either manually or by an -init script, and keep them around until we shut down. This is a little -wasteful of RAM, but it guarantees that sound always works. - -To make the sound driver use persistent DMA buffers we need to pass the -sound.o module a "dmabuf=1" command-line argument. This is normally done -in /etc/modprobe.d/*.conf files like so: - -options sound dmabuf=1 - -If you have 16MB or less RAM or a PCI sound card, this is wasteful and -unnecessary. It is possible that machine with 16MB or less RAM will find -this option useful, but if your machine is so memory-starved that it -cannot find a 64K block free, you will be wasting even more RAM by keeping -the sound modules loaded and the DMA buffers allocated when they are not -needed. The proper solution is to upgrade your RAM. But you do also have -this improper solution as well. Use it wisely. - - I'm afraid I know nothing about anything but my setup, being more of a -text-mode guy anyway. If you have options for other cards or other helpful -hints, send them to me, Jim Bray, jb@as220.org, http://as220.org/jb. diff --git a/Documentation/sound/oss/README.ymfsb b/Documentation/sound/oss/README.ymfsb deleted file mode 100644 index b6b77906b58d..000000000000 --- a/Documentation/sound/oss/README.ymfsb +++ /dev/null @@ -1,107 +0,0 @@ -Legacy audio driver for YMF7xx PCI cards. - - -FIRST OF ALL -============ - - This code references YAMAHA's sample codes and data sheets. - I respect and thank for all people they made open the information - about YMF7xx cards. - - And this codes heavily based on Jeff Garzik <jgarzik@pobox.com>'s - old VIA 82Cxxx driver (via82cxxx.c). I also respect him. - - -DISCLIMER -========= - - This driver is currently at early ALPHA stage. It may cause serious - damage to your computer when used. - PLEASE USE IT AT YOUR OWN RISK. - - -ABOUT THIS DRIVER -================= - - This code enables you to use your YMF724[A-F], YMF740[A-C], YMF744, YMF754 - cards. When enabled, your card acts as "SoundBlaster Pro" compatible card. - It can only play 22.05kHz / 8bit / Stereo samples, control external MIDI - port. - If you want to use your card as recent "16-bit" card, you should use - Alsa or OSS/Linux driver. Of course you can write native PCI driver for - your cards :) - - -USAGE -===== - - # modprobe ymfsb (options) - - -OPTIONS FOR MODULE -================== - - io : SB base address (0x220, 0x240, 0x260, 0x280) - synth_io : OPL3 base address (0x388, 0x398, 0x3a0, 0x3a8) - dma : DMA number (0,1,3) - master_volume: AC'97 PCM out Vol (0-100) - spdif_out : SPDIF-out flag (0:disable 1:enable) - - These options will change in future... - - -FREQUENCY -========= - - When playing sounds via this driver, you will hear its pitch is slightly - lower than original sounds. Since this driver recognizes your card acts - with 21.739kHz sample rates rather than 22.050kHz (I think it must be - hardware restriction). So many players become tone deafness. - To prevent this, you should express some options to your sound player - that specify correct sample frequency. For example, to play your MP3 file - correctly with mpg123, specify the frequency like following: - - % mpg123 -r 21739 foo.mp3 - - -SPDIF OUT -========= - - With installing modules with option 'spdif_out=1', you can enjoy your - sounds from SPDIF-out of your card (if it had). - Its Fs is fixed to 48kHz (It never means the sample frequency become - up to 48kHz. All sounds via SPDIF-out also 22kHz samples). So your - digital-in capable components has to be able to handle 48kHz Fs. - - -COPYING -======= - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. - - -TODO -==== - * support for multiple cards - (set the different SB_IO,MPU_IO,OPL_IO for each cards) - - * support for OPL (dmfm) : There will be no requirements... :-< - - -AUTHOR -====== - - Daisuke Nagano <breeze.nagano@nifty.ne.jp> - diff --git a/Documentation/sound/oss/SoundPro b/Documentation/sound/oss/SoundPro deleted file mode 100644 index 9d4db1f29d3c..000000000000 --- a/Documentation/sound/oss/SoundPro +++ /dev/null @@ -1,105 +0,0 @@ -Documentation for the SoundPro CMI8330 extensions in the WSS driver (ad1848.o) ------------------------------------------------------------------------------- - -( Be sure to read Documentation/sound/oss/CMI8330 too ) - -Ion Badulescu, ionut@cs.columbia.edu -February 24, 1999 - -(derived from the OPL3-SA2 documentation by Scott Murray) - -The SoundPro CMI8330 (ISA) is a chip usually found on some Taiwanese -motherboards. The official name in the documentation is CMI8330, SoundPro -is the nickname and the big inscription on the chip itself. - -The chip emulates a WSS as well as a SB16, but it has certain differences -in the mixer section which require separate support. It also emulates an -MPU401 and an OPL3 synthesizer, so you probably want to enable support -for these, too. - -The chip identifies itself as an AD1848, but its mixer is significantly -more advanced than the original AD1848 one. If your system works with -either WSS or SB16 and you are having problems with some mixer controls -(no CD audio, no line-in, etc), you might want to give this driver a try. -Detection should work, but it hasn't been widely tested, so it might still -mis-identify the chip. You can still force soundpro=1 in the modprobe -parameters for ad1848. Please let me know if it happens to you, so I can -adjust the detection routine. - -The chip is capable of doing full-duplex, but since the driver sees it as an -AD1848, it cannot take advantage of this. Moreover, the full-duplex mode is -not achievable through the WSS interface, b/c it needs a dma16 line which is -assigned only to the SB16 subdevice (with isapnp). Windows documentation -says the user must use WSS Playback and SB16 Recording for full-duplex, so -it might be possible to do the same thing under Linux. You can try loading -up both ad1848 and sb then use one for playback and the other for -recording. I don't know if this works, b/c I haven't tested it. Anyway, if -you try it, be very careful: the SB16 mixer *mostly* works, but certain -settings can have unexpected effects. Use the WSS mixer for best results. - -There is also a PCI SoundPro chip. I have not seen this chip, so I have -no idea if the driver will work with it. I suspect it won't. - -As with PnP cards, some configuration is required. There are two ways -of doing this. The most common is to use the isapnptools package to -initialize the card, and use the kernel module form of the sound -subsystem and sound drivers. Alternatively, some BIOS's allow manual -configuration of installed PnP devices in a BIOS menu, which should -allow using the non-modular sound drivers, i.e. built into the kernel. -Since in this latter case you cannot use module parameters, you will -have to enable support for the SoundPro at compile time. - -The IRQ and DMA values can be any that are considered acceptable for a -WSS. Assuming you've got isapnp all happy, then you should be able to -do something like the following (which *must* match the isapnp/BIOS -configuration): - -modprobe ad1848 io=0x530 irq=11 dma=0 soundpro=1 --and maybe- -modprobe sb io=0x220 irq=5 dma=1 dma16=5 - --then- -modprobe mpu401 io=0x330 irq=9 -modprobe opl3 io=0x388 - -If all goes well and you see no error messages, you should be able to -start using the sound capabilities of your system. If you get an -error message while trying to insert the module(s), then make -sure that the values of the various arguments match what you specified -in your isapnp configuration file, and that there is no conflict with -another device for an I/O port or interrupt. Checking the contents of -/proc/ioports and /proc/interrupts can be useful to see if you're -butting heads with another device. - -If you do not see the chipset version message, and none of the other -messages present in the system log are helpful, try adding 'debug=1' -to the ad1848 parameters, email me the syslog results and I'll do -my best to help. - -Lastly, if you're using modules and want to set up automatic module -loading with kmod, the kernel module loader, here is the section I -currently use in my conf.modules file: - -# Sound -post-install sound modprobe -k ad1848; modprobe -k mpu401; modprobe -k opl3 -options ad1848 io=0x530 irq=11 dma=0 -options sb io=0x220 irq=5 dma=1 dma16=5 -options mpu401 io=0x330 irq=9 -options opl3 io=0x388 - -The above ensures that ad1848 will be loaded whenever the sound system -is being used. - -Good luck. - -Ion - -NOT REALLY TESTED: -- recording -- recording device selection -- full-duplex - -TODO: -- implement mixer support for surround, loud, digital CD switches. -- come up with a scheme which allows recording volumes for each subdevice. -This is a major OSS API change. diff --git a/Documentation/sound/oss/Soundblaster b/Documentation/sound/oss/Soundblaster deleted file mode 100644 index b288d464ba8b..000000000000 --- a/Documentation/sound/oss/Soundblaster +++ /dev/null @@ -1,53 +0,0 @@ -modprobe sound -insmod uart401 -insmod sb ... - -This loads the driver for the Sound Blaster and assorted clones. Cards that -are covered by other drivers should not be using this driver. - -The Sound Blaster module takes the following arguments - -io I/O address of the Sound Blaster chip (0x220,0x240,0x260,0x280) -irq IRQ of the Sound Blaster chip (5,7,9,10) -dma 8-bit DMA channel for the Sound Blaster (0,1,3) -dma16 16-bit DMA channel for SB16 and equivalent cards (5,6,7) -mpu_io I/O for MPU chip if present (0x300,0x330) - -sm_games=1 Set if you have a Logitech soundman games -acer=1 Set this to detect cards in some ACER notebooks -mwave_bug=1 Set if you are trying to use this driver with mwave (see on) -type Use this to specify a specific card type - -The following arguments are taken if ISAPnP support is compiled in - -isapnp=0 Set this to disable ISAPnP detection (use io=0xXXX etc. above) -multiple=0 Set to disable detection of multiple Soundblaster cards. - Consider it a bug if this option is needed, and send in a - report. -pnplegacy=1 Set this to be able to use a PnP card(s) along with a single - non-PnP (legacy) card. Above options for io, irq, etc. are - needed, and will apply only to the legacy card. -reverse=1 Reverses the order of the search in the PnP table. -uart401=1 Set to enable detection of mpu devices on some clones. -isapnpjump=n Jumps to slot n in the driver's PnP table. Use the source, - Luke. - -You may well want to load the opl3 driver for synth music on most SB and -clone SB devices - -insmod opl3 io=0x388 - -Using Mwave - -To make this driver work with Mwave you must set mwave_bug. You also need -to warm boot from DOS/Windows with the required firmware loaded under this -OS. IBM are being difficult about documenting how to load this firmware. - -Avance Logic ALS007 - -This card is supported; see the separate file ALS007 for full details. - -Avance Logic ALS100 - -This card is supported; setup should be as for a standard Sound Blaster 16. -The driver will identify the audio device as a "Sound Blaster 16 (ALS-100)". diff --git a/Documentation/sound/oss/Tropez+ b/Documentation/sound/oss/Tropez+ deleted file mode 100644 index b93a6b734fc0..000000000000 --- a/Documentation/sound/oss/Tropez+ +++ /dev/null @@ -1,26 +0,0 @@ -From: Paul Barton-Davis <pbd@op.net> - -Here is the configuration I use with a Tropez+ and my modular -driver: - - alias char-major-14 wavefront - alias synth0 wavefront - alias mixer0 cs4232 - alias audio0 cs4232 - pre-install wavefront modprobe "-k" "cs4232" - post-install wavefront modprobe "-k" "opl3" - options wavefront io=0x200 irq=9 - options cs4232 synthirq=9 synthio=0x200 io=0x530 irq=5 dma=1 dma2=0 - options opl3 io=0x388 - -Things to note: - - the wavefront options "io" and "irq" ***MUST*** match the "synthio" - and "synthirq" cs4232 options. - - you can do without the opl3 module if you don't - want to use the OPL/[34] synth on the soundcard - - the opl3 io parameter is conventionally not adjustable. - -Please see drivers/sound/README.wavefront for more details. diff --git a/Documentation/sound/oss/VIBRA16 b/Documentation/sound/oss/VIBRA16 deleted file mode 100644 index 68a5a46beb88..000000000000 --- a/Documentation/sound/oss/VIBRA16 +++ /dev/null @@ -1,80 +0,0 @@ -Sound Blaster 16X Vibra addendum --------------------------------- -by Marius Ilioaea <mariusi@protv.ro> - Stefan Laudat <stefan@asit.ro> - -Sat Mar 6 23:55:27 EET 1999 - - Hello again, - - Playing with a SB Vibra 16x soundcard we found it very difficult -to setup because the kernel reported a lot of DMA errors and wouldn't -simply play any sound. - A good starting point is that the vibra16x chip full-duplex facility -is neither still exploited by the sb driver found in the linux kernel -(tried it with a 2.2.2-ac7), nor in the commercial OSS package (it reports -it as half-duplex soundcard). Oh, I almost forgot, the RedHat sndconfig -failed detecting it ;) - So, the big problem still remains, because the sb module wants a -8-bit and a 16-bit dma, which we could not allocate for vibra... it supports -only two 8-bit dma channels, the second one will be passed to the module -as a 16 bit channel, the kernel will yield about that but everything will -be okay, trust us. - The only inconvenient you may find is that you will have -some sound playing jitters if you have HDD dma support enabled - but this -will happen with almost all soundcards... - - A fully working isapnp.conf is just here: - -<snip here> - -(READPORT 0x0203) -(ISOLATE PRESERVE) -(IDENTIFY *) -(VERBOSITY 2) -(CONFLICT (IO FATAL)(IRQ FATAL)(DMA FATAL)(MEM FATAL)) # or WARNING -# SB 16 and OPL3 devices -(CONFIGURE CTL00f0/-1 (LD 0 -(INT 0 (IRQ 5 (MODE +E))) -(DMA 0 (CHANNEL 1)) -(DMA 1 (CHANNEL 3)) -(IO 0 (SIZE 16) (BASE 0x0220)) -(IO 2 (SIZE 4) (BASE 0x0388)) -(NAME "CTL00f0/-1[0]{Audio }") -(ACT Y) -)) - -# Joystick device - only if you need it :-/ - -(CONFIGURE CTL00f0/-1 (LD 1 -(IO 0 (SIZE 1) (BASE 0x0200)) -(NAME "CTL00f0/-1[1]{Game }") -(ACT Y) -)) -(WAITFORKEY) - -<end of snipping> - - So, after a good kernel modules compilation and a 'depmod -a kernel_ver' -you may want to: - -modprobe sb io=0x220 irq=5 dma=1 dma16=3 - - Or, take the hard way: - -modprobe soundcore -modprobe sound -modprobe uart401 -modprobe sb io=0x220 irq=5 dma=1 dma16=3 -# do you need MIDI? -modprobe opl3=0x388 - - Just in case, the kernel sound support should be: - -CONFIG_SOUND=m -CONFIG_SOUND_OSS=m -CONFIG_SOUND_SB=m - - Enjoy your new noisy Linux box! ;) - - diff --git a/Documentation/sound/oss/WaveArtist b/Documentation/sound/oss/WaveArtist deleted file mode 100644 index f4f3407cd818..000000000000 --- a/Documentation/sound/oss/WaveArtist +++ /dev/null @@ -1,170 +0,0 @@ - - (the following is from the armlinux CVS) - - WaveArtist mixer and volume levels can be accessed via these commands: - - nn30 read registers nn, where nn = 00 - 09 for mixer settings - 0a - 13 for channel volumes - mm31 write the volume setting in pairs, where mm = (nn - 10) / 2 - rr32 write the mixer settings in pairs, where rr = nn/2 - xx33 reset all settings to default - 0y34 select mono source, y=0 = left, y=1 = right - - bits - nn 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 00 | 0 | 0 0 1 1 | left line mixer gain | left aux1 mixer gain |lmute| -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 01 | 0 | 0 1 0 1 | left aux2 mixer gain | right 2 left mic gain |mmute| -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 02 | 0 | 0 1 1 1 | left mic mixer gain | left mic | left mixer gain |dith | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 03 | 0 | 1 0 0 1 | left mixer input select |lrfg | left ADC gain | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 04 | 0 | 1 0 1 1 | right line mixer gain | right aux1 mixer gain |rmute| -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 05 | 0 | 1 1 0 1 | right aux2 mixer gain | left 2 right mic gain |test | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 06 | 0 | 1 1 1 1 | right mic mixer gain | right mic |right mixer gain |rbyps| -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 07 | 1 | 0 0 0 1 | right mixer select |rrfg | right ADC gain | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 08 | 1 | 0 0 1 1 | mono mixer gain |right ADC mux sel|left ADC mux sel | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 09 | 1 | 0 1 0 1 |loopb|left linout|loop|ADCch|TxFch|OffCD|test |loopb|loopb|osamp| -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 0a | 0 | left PCM channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 0b | 0 | right PCM channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 0c | 0 | left FM channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 0d | 0 | right FM channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 0e | 0 | left wavetable channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 0f | 0 | right wavetable channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 10 | 0 | left PCM expansion channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 11 | 0 | right PCM expansion channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 12 | 0 | left FM expansion channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - 13 | 0 | right FM expansion channel volume | -----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ - - lmute: left mute - mmute: mono mute - dith: dithds - lrfg: - rmute: right mute - rbyps: right bypass - rrfg: - ADCch: - TxFch: - OffCD: - osamp: - - And the following diagram is derived from the description in the CVS archive: - - MIC L (mouthpiece) - +------+ - -->PreAmp>-\ - +--^---+ | - | | - r2b4-5 | +--------+ - /----*-------------------------------->5 | - | | | - | /----------------------------------->4 | - | | | | - | | /--------------------------------->3 1of5 | +---+ - | | | | mux >-->AMP>--> ADC L - | | | /------------------------------->2 | +-^-+ - | | | | | | | - Line | | | | +----+ +------+ +---+ /---->1 | r3b3-0 - ------------*->mute>--> Gain >--> | | | | - L | | | +----+ +------+ | | | *->0 | - | | | | | | +---^----+ - Aux2 | | | +----+ +------+ | | | | - ----------*--->mute>--> Gain >--> M | | r8b0-2 - L | | +----+ +------+ | | | - | | | | \------\ - Aux1 | | +----+ +------+ | | | - --------*----->mute>--> Gain >--> I | | - L | +----+ +------+ | | | - | | | | - | +----+ +------+ | | +---+ | - *------->mute>--> Gain >--> X >-->AMP>--* - | +----+ +------+ | | +-^-+ | - | | | | | - | +----+ +------+ | | r2b1-3 | - | /----->mute>--> Gain >--> E | | - | | +----+ +------+ | | | - | | | | | - | | +----+ +------+ | | | - | | /--->mute>--> Gain >--> R | | - | | | +----+ +------+ | | | - | | | | | | r9b8-9 - | | | +----+ +------+ | | | | - | | | /->mute>--> Gain >--> | | +---v---+ - | | | | +----+ +------+ +---+ /-*->0 | - DAC | | | | | | | - ------------*----------------------------------->? | +----+ - L | | | | | Mux >-->mute>--> L output - | | | | /->? | +--^-+ - | | | | | | | | - | | | /--------->? | r0b0 - | | | | | | +-------+ - | | | | | | - Mono | | | | | | +-------+ - ----------* | \---> | +----+ - | | | | | | Mix >-->mute>--> Mono output - | | | | *-> | +--^-+ - | | | | | +-------+ | - | | | | | r1b0 - DAC | | | | | +-------+ - ------------*-------------------------*--------->1 | +----+ - R | | | | | | Mux >-->mute>--> R output - | | | | +----+ +------+ +---+ *->0 | +--^-+ - | | | \->mute>--> Gain >--> | | +---^---+ | - | | | +----+ +------+ | | | | r5b0 - | | | | | | r6b0 - | | | +----+ +------+ | | | - | | \--->mute>--> Gain >--> M | | - | | +----+ +------+ | | | - | | | | | - | | +----+ +------+ | | | - | *----->mute>--> Gain >--> I | | - | | +----+ +------+ | | | - | | | | | - | | +----+ +------+ | | +---+ | - \------->mute>--> Gain >--> X >-->AMP>--* - | +----+ +------+ | | +-^-+ | - /--/ | | | | - Aux1 | +----+ +------+ | | r6b1-3 | - -------*------>mute>--> Gain >--> E | | - R | | +----+ +------+ | | | - | | | | | - Aux2 | | +----+ +------+ | | /------/ - ---------*---->mute>--> Gain >--> R | | - R | | | +----+ +------+ | | | - | | | | | | +--------+ - Line | | | +----+ +------+ | | | *->0 | - -----------*-->mute>--> Gain >--> | | | | - R | | | | +----+ +------+ +---+ \---->1 | - | | | | | | - | | | \-------------------------------->2 | +---+ - | | | | Mux >-->AMP>--> ADC R - | | \---------------------------------->3 | +-^-+ - | | | | | - | \------------------------------------>4 | r7b3-0 - | | | - \-----*-------------------------------->5 | - | +---^----+ - r6b4-5 | | - | | r8b3-5 - +--v---+ | - -->PreAmp>-/ - +------+ - MIC R (electret mic) diff --git a/Documentation/sound/oss/btaudio b/Documentation/sound/oss/btaudio deleted file mode 100644 index effdb9a3f898..000000000000 --- a/Documentation/sound/oss/btaudio +++ /dev/null @@ -1,92 +0,0 @@ - -Intro -===== - -people start bugging me about this with questions, looks like I -should write up some documentation for this beast. That way I -don't have to answer that much mails I hope. Yes, I'm lazy... - - -You might have noticed that the bt878 grabber cards have actually -_two_ PCI functions: - -$ lspci -[ ... ] -00:0a.0 Multimedia video controller: Brooktree Corporation Bt878 (rev 02) -00:0a.1 Multimedia controller: Brooktree Corporation Bt878 (rev 02) -[ ... ] - -The first does video, it is backward compatible to the bt848. The second -does audio. btaudio is a driver for the second function. It's a sound -driver which can be used for recording sound (and _only_ recording, no -playback). As most TV cards come with a short cable which can be plugged -into your sound card's line-in you probably don't need this driver if all -you want to do is just watching TV... - - -Driver Status -============= - -Still somewhat experimental. The driver should work stable, i.e. it -should'nt crash your box. It might not work as expected, have bugs, -not being fully OSS API compliant, ... - -Latest versions are available from http://bytesex.org/bttv/, the -driver is in the bttv tarball. Kernel patches might be available too, -have a look at http://bytesex.org/bttv/listing.html. - -The chip knows two different modes. btaudio registers two dsp -devices, one for each mode. They can not be used at the same time. - - -Digital audio mode -================== - -The chip gives you 16 bit stereo sound. The sample rate depends on -the external source which feeds the bt878 with digital sound via I2S -interface. There is a insmod option (rate) to tell the driver which -sample rate the hardware uses (32000 is the default). - -One possible source for digital sound is the msp34xx audio processor -chip which provides digital sound via I2S with 32 kHz sample rate. My -Hauppauge board works this way. - -The Osprey-200 reportly gives you digital sound with 44100 Hz sample -rate. It is also possible that you get no sound at all. - - -analog mode (A/D) -================= - -You can tell the driver to use this mode with the insmod option "analog=1". -The chip has three analog inputs. Consequently you'll get a mixer device -to control these. - -The analog mode supports mono only. Both 8 + 16 bit. Both are _signed_ -int, which is uncommon for the 8 bit case. Sample rate range is 119 kHz -to 448 kHz. Yes, the number of digits is correct. The driver supports -downsampling by powers of two, so you can ask for more usual sample rates -like 44 kHz too. - -With my Hauppauge I get noisy sound on the second input (mapped to line2 -by the mixer device). Others get a useable signal on line1. - - -some examples -============= - -* read audio data from btaudio (dsp2), send to es1730 (dsp,dsp1): - $ sox -w -r 32000 -t ossdsp /dev/dsp2 -t ossdsp /dev/dsp - -* read audio data from btaudio, send to esound daemon (which might be - running on another host): - $ sox -c 2 -w -r 32000 -t ossdsp /dev/dsp2 -t sw - | esdcat -r 32000 - $ sox -c 1 -w -r 32000 -t ossdsp /dev/dsp2 -t sw - | esdcat -m -r 32000 - - -Have fun, - - Gerd - --- -Gerd Knorr <kraxel@bytesex.org> diff --git a/Documentation/sound/oss/mwave b/Documentation/sound/oss/mwave deleted file mode 100644 index 5fbcb1609275..000000000000 --- a/Documentation/sound/oss/mwave +++ /dev/null @@ -1,185 +0,0 @@ - How to try to survive an IBM Mwave under Linux SB drivers - - -+ IBM have now released documentation of sorts and Torsten is busy - trying to make the Mwave work. This is not however a trivial task. - ----------------------------------------------------------------------------- - -OK, first thing - the IRQ problem IS a problem, whether the test is bypassed or -not. It is NOT a Linux problem, but an MWAVE problem that is fixed with the -latest MWAVE patches. So, in other words, don't bypass the test for MWAVES! - -I have Windows 95 on /dev/hda1, swap on /dev/hda2, and Red Hat 5 on /dev/hda3. - -The steps, then: - - Boot to Linux. - Mount Windows 95 file system (assume mount point = /dos95). - mkdir /dos95/linux - mkdir /dos95/linux/boot - mkdir /dos95/linux/boot/parms - - Copy the kernel, any initrd image, and loadlin to /dos95/linux/boot/. - - Reboot to Windows 95. - - Edit C:/msdos.sys and add or change the following: - - Logo=0 - BootGUI=0 - - Note that msdos.sys is a text file but it needs to be made 'unhidden', - readable and writable before it can be edited. This can be done with - DOS' "attrib" command. - - Edit config.sys to have multiple config menus. I have one for windows 95 and - five for Linux, like this: ------------- -[menu] -menuitem=W95, Windows 95 -menuitem=LINTP, Linux - ThinkPad -menuitem=LINTP3, Linux - ThinkPad Console -menuitem=LINDOC, Linux - Docked -menuitem=LINDOC3, Linux - Docked Console -menuitem=LIN1, Linux - Single User Mode -REM menudefault=W95,10 - -[W95] - -[LINTP] - -[LINDOC] - -[LINTP3] - -[LINDOC3] - -[LIN1] - -[COMMON] -FILES=30 -REM Please read README.TXT in C:\MWW subdirectory before changing the DOS= statement. -DOS=HIGH,UMB -DEVICE=C:\MWW\MANAGER\MWD50430.EXE -SHELL=c:\command.com /e:2048 -------------------- - -The important things are the SHELL and DEVICE statements. - - Then change autoexec.bat. Basically everything in there originally should be - done ONLY when Windows 95 is booted. Then you add new things specifically - for Linux. Mine is as follows - ---------------- -@ECHO OFF -if "%CONFIG%" == "W95" goto W95 - -REM -REM Linux stuff -REM -SET MWPATH=C:\MWW\DLL;C:\MWW\MWGAMES;C:\MWW\DSP -SET BLASTER=A220 I5 D1 -SET MWROOT=C:\MWW -SET LIBPATH=C:\MWW\DLL -SET PATH=C:\WINDOWS;C:\MWW\DLL; -CALL MWAVE START NOSHOW -c:\linux\boot\loadlin.exe @c:\linux\boot\parms\%CONFIG%.par - -:W95 -REM -REM Windows 95 stuff -REM -c:\toolkit\guard -SET MSINPUT=C:\MSINPUT -SET MWPATH=C:\MWW\DLL;C:\MWW\MWGAMES;C:\MWW\DSP -REM The following is used by DOS games to recognize Sound Blaster hardware. -REM If hardware settings are changed, please change this line as well. -REM See the Mwave README file for instructions. -SET BLASTER=A220 I5 D1 -SET MWROOT=C:\MWW -SET LIBPATH=C:\MWW\DLL -SET PATH=C:\WINDOWS;C:\WINDOWS\COMMAND;E:\ORAWIN95\BIN;f:\msdev\bin;e:\v30\bin.dbg;v:\devt\v30\bin;c:\JavaSDK\Bin;C:\MWW\DLL; -SET INCLUDE=f:\MSDEV\INCLUDE;F:\MSDEV\MFC\INCLUDE -SET LIB=F:\MSDEV\LIB;F:\MSDEV\MFC\LIB -win - ------------------------- - -Now build a file in c:\linux\boot\parms for each Linux config that you have. - -For example, my LINDOC3 config is for a docked Thinkpad at runlevel 3 with no -initrd image, and has a parameter file named LINDOC3.PAR in c:\linux\boot\parms: - ------------------------ -# LOADLIN @param_file image=other_image root=/dev/other -# -# Linux Console in docking station -# -c:\linux\boot\zImage.krn # First value must be filename of Linux kernel. -root=/dev/hda3 # device which gets mounted as root FS -ro # Other kernel arguments go here. -apm=off -doc=yes -3 ------------------------ - -The doc=yes parameter is an environment variable used by my init scripts, not -a kernel argument. - -However, the apm=off parameter IS a kernel argument! APM, at least in my setup, -causes the kernel to crash when loaded via loadlin (but NOT when loaded via -LILO). The APM stuff COULD be forced out of the kernel via the kernel compile -options. Instead, I got an unofficial patch to the APM drivers that allows them -to be dynamically deactivated via kernel arguments. Whatever you chose to -document, APM, it seems, MUST be off for setups like mine. - -Now make sure C:\MWW\MWCONFIG.REF looks like this: - ----------------------- -[NativeDOS] -Default=SB1.5 -SBInputSource=CD -SYNTH=FM -QSound=OFF -Reverb=OFF -Chorus=OFF -ReverbDepth=5 -ChorusDepth=5 -SBInputVolume=5 -SBMainVolume=10 -SBWaveVolume=10 -SBSynthVolume=10 -WaveTableVolume=10 -AudioPowerDriver=ON - -[FastCFG] -Show=No -HideOption=Off ------------------------------ - -OR the Default= line COULD be - -Default=SBPRO - -Reboot to Windows 95 and choose Linux. When booted, use sndconfig to configure -the sound modules and voilà - ThinkPad sound with Linux. - -Now the gotchas - you can either have CD sound OR Mixers but not both. That's a -problem with the SB1.5 (CD sound) or SBPRO (Mixers) settings. No one knows why -this is! - -For some reason MPEG3 files, when played through mpg123, sound like they -are playing at 1/8th speed - not very useful! If you have ANY insight -on why this second thing might be happening, I would be grateful. - -=========================================================== - _/ _/_/_/_/ - _/_/ _/_/ _/ - _/ _/_/ _/_/_/_/ Martin John Bartlett - _/ _/ _/ _/ (martin@nitram.demon.co.uk) -_/ _/_/_/_/ - _/ -_/ _/ - _/_/ -=========================================================== diff --git a/Documentation/sound/oss/oss-parameters.txt b/Documentation/sound/oss/oss-parameters.txt deleted file mode 100644 index cc675f25eee4..000000000000 --- a/Documentation/sound/oss/oss-parameters.txt +++ /dev/null @@ -1,51 +0,0 @@ - OSS Kernel Parameters - ~~~~~~~~~~~~~~~~~~~~~ - -See Documentation/admin-guide/kernel-parameters.rst for general information on -specifying module parameters. - -This document may not be entirely up to date and comprehensive. The command -"modinfo -p ${modulename}" shows a current list of all parameters of a loadable -module. Loadable modules, after being loaded into the running kernel, also -reveal their parameters in /sys/module/${modulename}/parameters/. Some of these -parameters may be changed at runtime by the command -"echo -n ${value} > /sys/module/${modulename}/parameters/${parm}". - - - ad1848= [HW,OSS] - Format: <io>,<irq>,<dma>,<dma2>,<type> - - aedsp16= [HW,OSS] Audio Excel DSP 16 - Format: <io>,<irq>,<dma>,<mss_io>,<mpu_io>,<mpu_irq> - See also header of sound/oss/aedsp16.c. - - dmasound= [HW,OSS] Sound subsystem buffers - - mpu401= [HW,OSS] - Format: <io>,<irq> - - opl3= [HW,OSS] - Format: <io> - - pas2= [HW,OSS] Format: - <io>,<irq>,<dma>,<dma16>,<sb_io>,<sb_irq>,<sb_dma>,<sb_dma16> - - pss= [HW,OSS] Personal Sound System (ECHO ESC614) - Format: - <io>,<mss_io>,<mss_irq>,<mss_dma>,<mpu_io>,<mpu_irq> - - sscape= [HW,OSS] - Format: <io>,<irq>,<dma>,<mpu_io>,<mpu_irq> - - trix= [HW,OSS] MediaTrix AudioTrix Pro - Format: - <io>,<irq>,<dma>,<dma2>,<sb_io>,<sb_irq>,<sb_dma>,<mpu_io>,<mpu_irq> - - uart401= [HW,OSS] - Format: <io>,<irq> - - uart6850= [HW,OSS] - Format: <io>,<irq> - - waveartist= [HW,OSS] - Format: <io>,<irq>,<dma>,<dma2> diff --git a/Documentation/sound/oss/ultrasound b/Documentation/sound/oss/ultrasound deleted file mode 100644 index eed331c738a3..000000000000 --- a/Documentation/sound/oss/ultrasound +++ /dev/null @@ -1,30 +0,0 @@ -modprobe sound -insmod ad1848 -insmod gus io=* irq=* dma=* ... - -This loads the driver for the Gravis Ultrasound family of sound cards. - -The gus module takes the following arguments - -io I/O address of the Ultrasound card (eg. io=0x220) -irq IRQ of the Sound Blaster card -dma DMA channel for the Sound Blaster -dma16 2nd DMA channel, only needed for full duplex operation -type 1 for PnP card -gus16 1 for using 16 bit sampling daughter board -no_wave_dma Set to disable DMA usage for wavetable (see note) -db16 ??? - - -no_wave_dma option - -This option defaults to a value of 0, which allows the Ultrasound wavetable -DSP to use DMA for playback and downloading samples. This is the same -as the old behaviour. If set to 1, no DMA is needed for downloading samples, -and allows owners of a GUS MAX to make use of simultaneous digital audio -(/dev/dsp), MIDI, and wavetable playback. - - -If you have problems in recording with GUS MAX, you could try to use -just one 8 bit DMA channel. Recording will not work with one DMA -channel if it's a 16 bit one. diff --git a/MAINTAINERS b/MAINTAINERS index a76f02f64469..fbebd08ae42e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -527,11 +527,6 @@ W: http://ez.analog.com/community/linux-device-drivers S: Supported F: drivers/input/misc/adxl34x.c -AEDSP16 DRIVER -M: Riccardo Facchetti <fizban@tin.it> -S: Maintained -F: sound/oss/aedsp16.c - AF9013 MEDIA DRIVER M: Antti Palosaari <crope@iki.fi> L: linux-media@vger.kernel.org @@ -9222,12 +9217,6 @@ F: include/linux/dt-bindings/mux/ F: include/linux/mux/ F: drivers/mux/ -MULTISOUND SOUND DRIVER -M: Andrew Veliath <andrewtv@usa.net> -S: Maintained -F: Documentation/sound/oss/MultiSound -F: sound/oss/msnd* - MULTITECH MULTIPORT CARD (ISICOM) S: Orphan F: drivers/tty/isicom.c @@ -14638,6 +14627,7 @@ F: Documentation/devicetree/bindings/extcon/extcon-arizona.txt F: Documentation/devicetree/bindings/regulator/arizona-regulator.txt F: Documentation/devicetree/bindings/mfd/arizona.txt F: Documentation/devicetree/bindings/mfd/wm831x.txt +F: Documentation/devicetree/bindings/sound/wlf,arizona.txt F: arch/arm/mach-s3c64xx/mach-crag6410* F: drivers/clk/clk-wm83*.c F: drivers/extcon/extcon-arizona.c diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c index a52795d9b458..ebca22302ebb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c @@ -371,6 +371,8 @@ static int acp_hw_init(void *handle) adev->acp.acp_cell[0].name = "acp_audio_dma"; adev->acp.acp_cell[0].num_resources = 4; adev->acp.acp_cell[0].resources = &adev->acp.acp_res[0]; + adev->acp.acp_cell[0].platform_data = &adev->asic_type; + adev->acp.acp_cell[0].pdata_size = sizeof(adev->asic_type); adev->acp.acp_cell[1].name = "designware-i2s"; adev->acp.acp_cell[1].num_resources = 1; diff --git a/drivers/gpu/drm/amd/include/amd_shared.h b/drivers/gpu/drm/amd/include/amd_shared.h index 70e8c20acb2f..3a49fbd8baf8 100644 --- a/drivers/gpu/drm/amd/include/amd_shared.h +++ b/drivers/gpu/drm/amd/include/amd_shared.h @@ -23,34 +23,9 @@ #ifndef __AMD_SHARED_H__ #define __AMD_SHARED_H__ -#define AMD_MAX_USEC_TIMEOUT 200000 /* 200 ms */ +#include <drm/amd_asic_type.h> -/* - * Supported ASIC types - */ -enum amd_asic_type { - CHIP_TAHITI = 0, - CHIP_PITCAIRN, - CHIP_VERDE, - CHIP_OLAND, - CHIP_HAINAN, - CHIP_BONAIRE, - CHIP_KAVERI, - CHIP_KABINI, - CHIP_HAWAII, - CHIP_MULLINS, - CHIP_TOPAZ, - CHIP_TONGA, - CHIP_FIJI, - CHIP_CARRIZO, - CHIP_STONEY, - CHIP_POLARIS10, - CHIP_POLARIS11, - CHIP_POLARIS12, - CHIP_VEGA10, - CHIP_RAVEN, - CHIP_LAST, -}; +#define AMD_MAX_USEC_TIMEOUT 200000 /* 200 ms */ /* * Chip flags diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 64b30fe273fd..176b1a74b2b7 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -727,7 +727,7 @@ config TOUCHSCREEN_WM831X config TOUCHSCREEN_WM97XX tristate "Support for WM97xx AC97 touchscreen controllers" - depends on AC97_BUS + depends on AC97_BUS || AC97_BUS_NEW help Say Y here if you have a Wolfson Microelectronics WM97xx touchscreen connected to your system. Note that this option diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c index c9d1c91e1887..fd714ee881f7 100644 --- a/drivers/input/touchscreen/wm97xx-core.c +++ b/drivers/input/touchscreen/wm97xx-core.c @@ -44,6 +44,7 @@ #include <linux/pm.h> #include <linux/interrupt.h> #include <linux/bitops.h> +#include <linux/mfd/wm97xx.h> #include <linux/workqueue.h> #include <linux/wm97xx.h> #include <linux/uaccess.h> @@ -581,27 +582,85 @@ static void wm97xx_ts_input_close(struct input_dev *idev) wm->codec->acc_enable(wm, 0); } -static int wm97xx_probe(struct device *dev) +static int wm97xx_register_touch(struct wm97xx *wm) { - struct wm97xx *wm; - struct wm97xx_pdata *pdata = dev_get_platdata(dev); - int ret = 0, id = 0; + struct wm97xx_pdata *pdata = dev_get_platdata(wm->dev); + int ret; - wm = kzalloc(sizeof(struct wm97xx), GFP_KERNEL); - if (!wm) + wm->input_dev = devm_input_allocate_device(wm->dev); + if (wm->input_dev == NULL) return -ENOMEM; - mutex_init(&wm->codec_mutex); - wm->dev = dev; - dev_set_drvdata(dev, wm); - wm->ac97 = to_ac97_t(dev); + /* set up touch configuration */ + wm->input_dev->name = "wm97xx touchscreen"; + wm->input_dev->phys = "wm97xx"; + wm->input_dev->open = wm97xx_ts_input_open; + wm->input_dev->close = wm97xx_ts_input_close; + + __set_bit(EV_ABS, wm->input_dev->evbit); + __set_bit(EV_KEY, wm->input_dev->evbit); + __set_bit(BTN_TOUCH, wm->input_dev->keybit); + + input_set_abs_params(wm->input_dev, ABS_X, abs_x[0], abs_x[1], + abs_x[2], 0); + input_set_abs_params(wm->input_dev, ABS_Y, abs_y[0], abs_y[1], + abs_y[2], 0); + input_set_abs_params(wm->input_dev, ABS_PRESSURE, abs_p[0], abs_p[1], + abs_p[2], 0); + + input_set_drvdata(wm->input_dev, wm); + wm->input_dev->dev.parent = wm->dev; + + ret = input_register_device(wm->input_dev); + if (ret) + return ret; + + /* + * register our extended touch device (for machine specific + * extensions) + */ + wm->touch_dev = platform_device_alloc("wm97xx-touch", -1); + if (!wm->touch_dev) { + ret = -ENOMEM; + goto touch_err; + } + platform_set_drvdata(wm->touch_dev, wm); + wm->touch_dev->dev.parent = wm->dev; + wm->touch_dev->dev.platform_data = pdata; + ret = platform_device_add(wm->touch_dev); + if (ret < 0) + goto touch_reg_err; + + return 0; +touch_reg_err: + platform_device_put(wm->touch_dev); +touch_err: + input_unregister_device(wm->input_dev); + wm->input_dev = NULL; + + return ret; +} + +static void wm97xx_unregister_touch(struct wm97xx *wm) +{ + platform_device_unregister(wm->touch_dev); + input_unregister_device(wm->input_dev); + wm->input_dev = NULL; +} + +static int _wm97xx_probe(struct wm97xx *wm) +{ + int id = 0; + + mutex_init(&wm->codec_mutex); + dev_set_drvdata(wm->dev, wm); /* check that we have a supported codec */ id = wm97xx_reg_read(wm, AC97_VENDOR_ID1); if (id != WM97XX_ID1) { - dev_err(dev, "Device with vendor %04x is not a wm97xx\n", id); - ret = -ENODEV; - goto alloc_err; + dev_err(wm->dev, + "Device with vendor %04x is not a wm97xx\n", id); + return -ENODEV; } wm->id = wm97xx_reg_read(wm, AC97_VENDOR_ID2); @@ -629,8 +688,7 @@ static int wm97xx_probe(struct device *dev) default: dev_err(wm->dev, "Support for wm97%02x not compiled in.\n", wm->id & 0xff); - ret = -ENODEV; - goto alloc_err; + return -ENODEV; } /* set up physical characteristics */ @@ -644,79 +702,58 @@ static int wm97xx_probe(struct device *dev) wm->gpio[4] = wm97xx_reg_read(wm, AC97_GPIO_STATUS); wm->gpio[5] = wm97xx_reg_read(wm, AC97_MISC_AFE); - wm->input_dev = input_allocate_device(); - if (wm->input_dev == NULL) { - ret = -ENOMEM; - goto alloc_err; - } - - /* set up touch configuration */ - wm->input_dev->name = "wm97xx touchscreen"; - wm->input_dev->phys = "wm97xx"; - wm->input_dev->open = wm97xx_ts_input_open; - wm->input_dev->close = wm97xx_ts_input_close; - - __set_bit(EV_ABS, wm->input_dev->evbit); - __set_bit(EV_KEY, wm->input_dev->evbit); - __set_bit(BTN_TOUCH, wm->input_dev->keybit); - - input_set_abs_params(wm->input_dev, ABS_X, abs_x[0], abs_x[1], - abs_x[2], 0); - input_set_abs_params(wm->input_dev, ABS_Y, abs_y[0], abs_y[1], - abs_y[2], 0); - input_set_abs_params(wm->input_dev, ABS_PRESSURE, abs_p[0], abs_p[1], - abs_p[2], 0); + return wm97xx_register_touch(wm); +} - input_set_drvdata(wm->input_dev, wm); - wm->input_dev->dev.parent = dev; +static void wm97xx_remove_battery(struct wm97xx *wm) +{ + platform_device_unregister(wm->battery_dev); +} - ret = input_register_device(wm->input_dev); - if (ret < 0) - goto dev_alloc_err; +static int wm97xx_add_battery(struct wm97xx *wm, + struct wm97xx_batt_pdata *pdata) +{ + int ret; - /* register our battery device */ wm->battery_dev = platform_device_alloc("wm97xx-battery", -1); - if (!wm->battery_dev) { - ret = -ENOMEM; - goto batt_err; - } + if (!wm->battery_dev) + return -ENOMEM; + platform_set_drvdata(wm->battery_dev, wm); - wm->battery_dev->dev.parent = dev; - wm->battery_dev->dev.platform_data = pdata ? pdata->batt_pdata : NULL; + wm->battery_dev->dev.parent = wm->dev; + wm->battery_dev->dev.platform_data = pdata; ret = platform_device_add(wm->battery_dev); - if (ret < 0) - goto batt_reg_err; + if (ret) + platform_device_put(wm->battery_dev); - /* register our extended touch device (for machine specific - * extensions) */ - wm->touch_dev = platform_device_alloc("wm97xx-touch", -1); - if (!wm->touch_dev) { - ret = -ENOMEM; - goto touch_err; - } - platform_set_drvdata(wm->touch_dev, wm); - wm->touch_dev->dev.parent = dev; - wm->touch_dev->dev.platform_data = pdata; - ret = platform_device_add(wm->touch_dev); + return ret; +} + +static int wm97xx_probe(struct device *dev) +{ + struct wm97xx *wm; + int ret; + struct wm97xx_pdata *pdata = dev_get_platdata(dev); + + wm = devm_kzalloc(dev, sizeof(struct wm97xx), GFP_KERNEL); + if (!wm) + return -ENOMEM; + + wm->dev = dev; + wm->ac97 = to_ac97_t(dev); + + ret = _wm97xx_probe(wm); + if (ret) + return ret; + + ret = wm97xx_add_battery(wm, pdata ? pdata->batt_pdata : NULL); if (ret < 0) - goto touch_reg_err; + goto batt_err; return ret; - touch_reg_err: - platform_device_put(wm->touch_dev); - touch_err: - platform_device_del(wm->battery_dev); - batt_reg_err: - platform_device_put(wm->battery_dev); - batt_err: - input_unregister_device(wm->input_dev); - wm->input_dev = NULL; - dev_alloc_err: - input_free_device(wm->input_dev); - alloc_err: - kfree(wm); - +batt_err: + wm97xx_unregister_touch(wm); return ret; } @@ -724,14 +761,45 @@ static int wm97xx_remove(struct device *dev) { struct wm97xx *wm = dev_get_drvdata(dev); - platform_device_unregister(wm->battery_dev); - platform_device_unregister(wm->touch_dev); - input_unregister_device(wm->input_dev); - kfree(wm); + wm97xx_remove_battery(wm); + wm97xx_unregister_touch(wm); return 0; } +static int wm97xx_mfd_probe(struct platform_device *pdev) +{ + struct wm97xx *wm; + struct wm97xx_platform_data *mfd_pdata = dev_get_platdata(&pdev->dev); + int ret; + + wm = devm_kzalloc(&pdev->dev, sizeof(struct wm97xx), GFP_KERNEL); + if (!wm) + return -ENOMEM; + + wm->dev = &pdev->dev; + wm->ac97 = mfd_pdata->ac97; + + ret = _wm97xx_probe(wm); + if (ret) + return ret; + + ret = wm97xx_add_battery(wm, mfd_pdata->batt_pdata); + if (ret < 0) + goto batt_err; + + return ret; + +batt_err: + wm97xx_unregister_touch(wm); + return ret; +} + +static int wm97xx_mfd_remove(struct platform_device *pdev) +{ + return wm97xx_remove(&pdev->dev); +} + static int __maybe_unused wm97xx_suspend(struct device *dev) { struct wm97xx *wm = dev_get_drvdata(dev); @@ -828,21 +896,41 @@ EXPORT_SYMBOL_GPL(wm97xx_unregister_mach_ops); static struct device_driver wm97xx_driver = { .name = "wm97xx-ts", +#ifdef CONFIG_AC97_BUS .bus = &ac97_bus_type, +#endif .owner = THIS_MODULE, .probe = wm97xx_probe, .remove = wm97xx_remove, .pm = &wm97xx_pm_ops, }; +static struct platform_driver wm97xx_mfd_driver = { + .driver = { + .name = "wm97xx-ts", + .pm = &wm97xx_pm_ops, + }, + .probe = wm97xx_mfd_probe, + .remove = wm97xx_mfd_remove, +}; + static int __init wm97xx_init(void) { - return driver_register(&wm97xx_driver); + int ret; + + ret = platform_driver_register(&wm97xx_mfd_driver); + if (ret) + return ret; + + if (IS_BUILTIN(CONFIG_AC97_BUS)) + ret = driver_register(&wm97xx_driver); + return ret; } static void __exit wm97xx_exit(void) { driver_unregister(&wm97xx_driver); + platform_driver_unregister(&wm97xx_mfd_driver); } module_init(wm97xx_init); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index fc5e4fef89d2..ac5ad6d0837c 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1746,6 +1746,20 @@ config MFD_WM8994 core support for the WM8994, in order to use the actual functionaltiy of the device other drivers must be enabled. +config MFD_WM97xx + tristate "Wolfson Microelectronics WM97xx" + select MFD_CORE + select REGMAP_AC97 + select AC97_BUS_COMPAT + depends on AC97_BUS_NEW + help + The WM9705, WM9712 and WM9713 is a highly integrated hi-fi CODEC + designed for smartphone applications. As well as audio functionality + it has on board GPIO and a touchscreen functionality which is + supported via the relevant subsystems. This driver provides core + support for the WM97xx, in order to use the actual functionaltiy of + the device other drivers must be enabled. + config MFD_STW481X tristate "Support for ST Microelectronics STw481x" depends on I2C && (ARCH_NOMADIK || COMPILE_TEST) diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 8703ff17998e..0235e6766bc1 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -74,6 +74,7 @@ obj-$(CONFIG_MFD_WM8350) += wm8350.o obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o wm8994-objs := wm8994-core.o wm8994-irq.o wm8994-regmap.o obj-$(CONFIG_MFD_WM8994) += wm8994.o +obj-$(CONFIG_MFD_WM97xx) += wm97xx-core.o obj-$(CONFIG_TPS6105X) += tps6105x.o obj-$(CONFIG_TPS65010) += tps65010.o diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 8d46e3ad9529..77875250abe5 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -797,12 +797,7 @@ EXPORT_SYMBOL_GPL(arizona_of_get_type); static int arizona_of_get_core_pdata(struct arizona *arizona) { struct arizona_pdata *pdata = &arizona->pdata; - struct property *prop; - const __be32 *cur; - u32 val; - u32 pdm_val[ARIZONA_MAX_PDM_SPK]; int ret, i; - int count = 0; pdata->reset = of_get_named_gpio(arizona->dev->of_node, "wlf,reset", 0); if (pdata->reset == -EPROBE_DEFER) { @@ -836,64 +831,6 @@ static int arizona_of_get_core_pdata(struct arizona *arizona) ret); } - of_property_for_each_u32(arizona->dev->of_node, "wlf,inmode", prop, - cur, val) { - if (count == ARRAY_SIZE(pdata->inmode)) - break; - - pdata->inmode[count] = val; - count++; - } - - count = 0; - of_property_for_each_u32(arizona->dev->of_node, "wlf,dmic-ref", prop, - cur, val) { - if (count == ARRAY_SIZE(pdata->dmic_ref)) - break; - - pdata->dmic_ref[count] = val; - count++; - } - - count = 0; - of_property_for_each_u32(arizona->dev->of_node, "wlf,out-mono", prop, - cur, val) { - if (count == ARRAY_SIZE(pdata->out_mono)) - break; - - pdata->out_mono[count] = !!val; - count++; - } - - count = 0; - of_property_for_each_u32(arizona->dev->of_node, - "wlf,max-channels-clocked", - prop, cur, val) { - if (count == ARRAY_SIZE(pdata->max_channels_clocked)) - break; - - pdata->max_channels_clocked[count] = val; - count++; - } - - ret = of_property_read_u32_array(arizona->dev->of_node, - "wlf,spk-fmt", - pdm_val, - ARRAY_SIZE(pdm_val)); - - if (ret >= 0) - for (count = 0; count < ARRAY_SIZE(pdata->spk_fmt); ++count) - pdata->spk_fmt[count] = pdm_val[count]; - - ret = of_property_read_u32_array(arizona->dev->of_node, - "wlf,spk-mute", - pdm_val, - ARRAY_SIZE(pdm_val)); - - if (ret >= 0) - for (count = 0; count < ARRAY_SIZE(pdata->spk_mute); ++count) - pdata->spk_mute[count] = pdm_val[count]; - return 0; } @@ -1026,7 +963,7 @@ int arizona_dev_init(struct arizona *arizona) const char * const mclk_name[] = { "mclk1", "mclk2" }; struct device *dev = arizona->dev; const char *type_name = NULL; - unsigned int reg, val, mask; + unsigned int reg, val; int (*apply_patch)(struct arizona *) = NULL; const struct mfd_cell *subdevs = NULL; int n_subdevs, ret, i; @@ -1429,73 +1366,6 @@ int arizona_dev_init(struct arizona *arizona) ARIZONA_MICB1_RATE, val); } - for (i = 0; i < ARIZONA_MAX_INPUT; i++) { - /* Default for both is 0 so noop with defaults */ - val = arizona->pdata.dmic_ref[i] - << ARIZONA_IN1_DMIC_SUP_SHIFT; - if (arizona->pdata.inmode[i] & ARIZONA_INMODE_DMIC) - val |= 1 << ARIZONA_IN1_MODE_SHIFT; - - switch (arizona->type) { - case WM8998: - case WM1814: - regmap_update_bits(arizona->regmap, - ARIZONA_ADC_DIGITAL_VOLUME_1L + (i * 8), - ARIZONA_IN1L_SRC_SE_MASK, - (arizona->pdata.inmode[i] & ARIZONA_INMODE_SE) - << ARIZONA_IN1L_SRC_SE_SHIFT); - - regmap_update_bits(arizona->regmap, - ARIZONA_ADC_DIGITAL_VOLUME_1R + (i * 8), - ARIZONA_IN1R_SRC_SE_MASK, - (arizona->pdata.inmode[i] & ARIZONA_INMODE_SE) - << ARIZONA_IN1R_SRC_SE_SHIFT); - - mask = ARIZONA_IN1_DMIC_SUP_MASK | - ARIZONA_IN1_MODE_MASK; - break; - default: - if (arizona->pdata.inmode[i] & ARIZONA_INMODE_SE) - val |= 1 << ARIZONA_IN1_SINGLE_ENDED_SHIFT; - - mask = ARIZONA_IN1_DMIC_SUP_MASK | - ARIZONA_IN1_MODE_MASK | - ARIZONA_IN1_SINGLE_ENDED_MASK; - break; - } - - regmap_update_bits(arizona->regmap, - ARIZONA_IN1L_CONTROL + (i * 8), - mask, val); - } - - for (i = 0; i < ARIZONA_MAX_OUTPUT; i++) { - /* Default is 0 so noop with defaults */ - if (arizona->pdata.out_mono[i]) - val = ARIZONA_OUT1_MONO; - else - val = 0; - - regmap_update_bits(arizona->regmap, - ARIZONA_OUTPUT_PATH_CONFIG_1L + (i * 8), - ARIZONA_OUT1_MONO, val); - } - - for (i = 0; i < ARIZONA_MAX_PDM_SPK; i++) { - if (arizona->pdata.spk_mute[i]) - regmap_update_bits(arizona->regmap, - ARIZONA_PDM_SPK1_CTRL_1 + (i * 2), - ARIZONA_SPK1_MUTE_ENDIAN_MASK | - ARIZONA_SPK1_MUTE_SEQ1_MASK, - arizona->pdata.spk_mute[i]); - - if (arizona->pdata.spk_fmt[i]) - regmap_update_bits(arizona->regmap, - ARIZONA_PDM_SPK1_CTRL_2 + (i * 2), - ARIZONA_SPK1_FMT_MASK, - arizona->pdata.spk_fmt[i]); - } - pm_runtime_set_active(arizona->dev); pm_runtime_enable(arizona->dev); diff --git a/drivers/mfd/wm97xx-core.c b/drivers/mfd/wm97xx-core.c new file mode 100644 index 000000000000..4141ee52a70b --- /dev/null +++ b/drivers/mfd/wm97xx-core.c @@ -0,0 +1,366 @@ +/* + * Wolfson WM97xx -- Core device + * + * Copyright (C) 2017 Robert Jarzmik + * + * 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. + * + * Features: + * - an AC97 audio codec + * - a touchscreen driver + * - a GPIO block + */ + +#include <linux/device.h> +#include <linux/mfd/core.h> +#include <linux/mfd/wm97xx.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/wm97xx.h> +#include <sound/ac97/codec.h> +#include <sound/ac97/compat.h> + +#define WM9705_VENDOR_ID 0x574d4c05 +#define WM9712_VENDOR_ID 0x574d4c12 +#define WM9713_VENDOR_ID 0x574d4c13 +#define WM97xx_VENDOR_ID_MASK 0xffffffff + +struct wm97xx_priv { + struct regmap *regmap; + struct snd_ac97 *ac97; + struct device *dev; + struct wm97xx_platform_data codec_pdata; +}; + +static bool wm97xx_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AC97_RESET ... AC97_PCM_SURR_DAC_RATE: + case AC97_PCM_LR_ADC_RATE: + case AC97_CENTER_LFE_MASTER: + case AC97_SPDIF ... AC97_LINE1_LEVEL: + case AC97_GPIO_CFG ... 0x5c: + case AC97_CODEC_CLASS_REV ... AC97_PCI_SID: + case 0x74 ... AC97_VENDOR_ID2: + return true; + default: + return false; + } +} + +static bool wm97xx_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AC97_VENDOR_ID1: + case AC97_VENDOR_ID2: + return false; + default: + return wm97xx_readable_reg(dev, reg); + } +} + +static const struct reg_default wm9705_reg_defaults[] = { + { 0x02, 0x8000 }, + { 0x04, 0x8000 }, + { 0x06, 0x8000 }, + { 0x0a, 0x8000 }, + { 0x0c, 0x8008 }, + { 0x0e, 0x8008 }, + { 0x10, 0x8808 }, + { 0x12, 0x8808 }, + { 0x14, 0x8808 }, + { 0x16, 0x8808 }, + { 0x18, 0x8808 }, + { 0x1a, 0x0000 }, + { 0x1c, 0x8000 }, + { 0x20, 0x0000 }, + { 0x22, 0x0000 }, + { 0x26, 0x000f }, + { 0x28, 0x0605 }, + { 0x2a, 0x0000 }, + { 0x2c, 0xbb80 }, + { 0x32, 0xbb80 }, + { 0x34, 0x2000 }, + { 0x5a, 0x0000 }, + { 0x5c, 0x0000 }, + { 0x72, 0x0808 }, + { 0x74, 0x0000 }, + { 0x76, 0x0006 }, + { 0x78, 0x0000 }, + { 0x7a, 0x0000 }, +}; + +static const struct regmap_config wm9705_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x7e, + .cache_type = REGCACHE_RBTREE, + + .reg_defaults = wm9705_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm9705_reg_defaults), + .volatile_reg = regmap_ac97_default_volatile, + .readable_reg = wm97xx_readable_reg, + .writeable_reg = wm97xx_writeable_reg, +}; + +static struct mfd_cell wm9705_cells[] = { + { .name = "wm9705-codec", }, + { .name = "wm97xx-ts", }, +}; + +static bool wm9712_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AC97_REC_GAIN: + return true; + default: + return regmap_ac97_default_volatile(dev, reg); + } +} + +static const struct reg_default wm9712_reg_defaults[] = { + { 0x02, 0x8000 }, + { 0x04, 0x8000 }, + { 0x06, 0x8000 }, + { 0x08, 0x0f0f }, + { 0x0a, 0xaaa0 }, + { 0x0c, 0xc008 }, + { 0x0e, 0x6808 }, + { 0x10, 0xe808 }, + { 0x12, 0xaaa0 }, + { 0x14, 0xad00 }, + { 0x16, 0x8000 }, + { 0x18, 0xe808 }, + { 0x1a, 0x3000 }, + { 0x1c, 0x8000 }, + { 0x20, 0x0000 }, + { 0x22, 0x0000 }, + { 0x26, 0x000f }, + { 0x28, 0x0605 }, + { 0x2a, 0x0410 }, + { 0x2c, 0xbb80 }, + { 0x2e, 0xbb80 }, + { 0x32, 0xbb80 }, + { 0x34, 0x2000 }, + { 0x4c, 0xf83e }, + { 0x4e, 0xffff }, + { 0x50, 0x0000 }, + { 0x52, 0x0000 }, + { 0x56, 0xf83e }, + { 0x58, 0x0008 }, + { 0x5c, 0x0000 }, + { 0x60, 0xb032 }, + { 0x62, 0x3e00 }, + { 0x64, 0x0000 }, + { 0x76, 0x0006 }, + { 0x78, 0x0001 }, + { 0x7a, 0x0000 }, +}; + +static const struct regmap_config wm9712_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x7e, + .cache_type = REGCACHE_RBTREE, + + .reg_defaults = wm9712_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm9712_reg_defaults), + .volatile_reg = wm9712_volatile_reg, + .readable_reg = wm97xx_readable_reg, + .writeable_reg = wm97xx_writeable_reg, +}; + +static struct mfd_cell wm9712_cells[] = { + { .name = "wm9712-codec", }, + { .name = "wm97xx-ts", }, +}; + +static const struct reg_default wm9713_reg_defaults[] = { + { 0x02, 0x8080 }, /* Speaker Output Volume */ + { 0x04, 0x8080 }, /* Headphone Output Volume */ + { 0x06, 0x8080 }, /* Out3/OUT4 Volume */ + { 0x08, 0xc880 }, /* Mono Volume */ + { 0x0a, 0xe808 }, /* LINEIN Volume */ + { 0x0c, 0xe808 }, /* DAC PGA Volume */ + { 0x0e, 0x0808 }, /* MIC PGA Volume */ + { 0x10, 0x00da }, /* MIC Routing Control */ + { 0x12, 0x8000 }, /* Record PGA Volume */ + { 0x14, 0xd600 }, /* Record Routing */ + { 0x16, 0xaaa0 }, /* PCBEEP Volume */ + { 0x18, 0xaaa0 }, /* VxDAC Volume */ + { 0x1a, 0xaaa0 }, /* AUXDAC Volume */ + { 0x1c, 0x0000 }, /* Output PGA Mux */ + { 0x1e, 0x0000 }, /* DAC 3D control */ + { 0x20, 0x0f0f }, /* DAC Tone Control*/ + { 0x22, 0x0040 }, /* MIC Input Select & Bias */ + { 0x24, 0x0000 }, /* Output Volume Mapping & Jack */ + { 0x26, 0x7f00 }, /* Powerdown Ctrl/Stat*/ + { 0x28, 0x0405 }, /* Extended Audio ID */ + { 0x2a, 0x0410 }, /* Extended Audio Start/Ctrl */ + { 0x2c, 0xbb80 }, /* Audio DACs Sample Rate */ + { 0x2e, 0xbb80 }, /* AUXDAC Sample Rate */ + { 0x32, 0xbb80 }, /* Audio ADCs Sample Rate */ + { 0x36, 0x4523 }, /* PCM codec control */ + { 0x3a, 0x2000 }, /* SPDIF control */ + { 0x3c, 0xfdff }, /* Powerdown 1 */ + { 0x3e, 0xffff }, /* Powerdown 2 */ + { 0x40, 0x0000 }, /* General Purpose */ + { 0x42, 0x0000 }, /* Fast Power-Up Control */ + { 0x44, 0x0080 }, /* MCLK/PLL Control */ + { 0x46, 0x0000 }, /* MCLK/PLL Control */ + + { 0x4c, 0xfffe }, /* GPIO Pin Configuration */ + { 0x4e, 0xffff }, /* GPIO Pin Polarity / Type */ + { 0x50, 0x0000 }, /* GPIO Pin Sticky */ + { 0x52, 0x0000 }, /* GPIO Pin Wake-Up */ + /* GPIO Pin Status */ + { 0x56, 0xfffe }, /* GPIO Pin Sharing */ + { 0x58, 0x4000 }, /* GPIO PullUp/PullDown */ + { 0x5a, 0x0000 }, /* Additional Functions 1 */ + { 0x5c, 0x0000 }, /* Additional Functions 2 */ + { 0x60, 0xb032 }, /* ALC Control */ + { 0x62, 0x3e00 }, /* ALC / Noise Gate Control */ + { 0x64, 0x0000 }, /* AUXDAC input control */ + { 0x74, 0x0000 }, /* Digitiser Reg 1 */ + { 0x76, 0x0006 }, /* Digitiser Reg 2 */ + { 0x78, 0x0001 }, /* Digitiser Reg 3 */ + { 0x7a, 0x0000 }, /* Digitiser Read Back */ +}; + +static const struct regmap_config wm9713_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x7e, + .cache_type = REGCACHE_RBTREE, + + .reg_defaults = wm9713_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm9713_reg_defaults), + .volatile_reg = regmap_ac97_default_volatile, + .readable_reg = wm97xx_readable_reg, + .writeable_reg = wm97xx_writeable_reg, +}; + +static struct mfd_cell wm9713_cells[] = { + { .name = "wm9713-codec", }, + { .name = "wm97xx-ts", }, +}; + +static int wm97xx_ac97_probe(struct ac97_codec_device *adev) +{ + struct wm97xx_priv *wm97xx; + const struct regmap_config *config; + struct wm97xx_platform_data *codec_pdata; + struct mfd_cell *cells; + int ret = -ENODEV, nb_cells, i; + struct wm97xx_pdata *pdata = snd_ac97_codec_get_platdata(adev); + + wm97xx = devm_kzalloc(ac97_codec_dev2dev(adev), + sizeof(*wm97xx), GFP_KERNEL); + if (!wm97xx) + return -ENOMEM; + + wm97xx->dev = ac97_codec_dev2dev(adev); + wm97xx->ac97 = snd_ac97_compat_alloc(adev); + if (IS_ERR(wm97xx->ac97)) + return PTR_ERR(wm97xx->ac97); + + + ac97_set_drvdata(adev, wm97xx); + dev_info(wm97xx->dev, "wm97xx core found, id=0x%x\n", + adev->vendor_id); + + codec_pdata = &wm97xx->codec_pdata; + codec_pdata->ac97 = wm97xx->ac97; + codec_pdata->batt_pdata = pdata->batt_pdata; + + switch (adev->vendor_id) { + case WM9705_VENDOR_ID: + config = &wm9705_regmap_config; + cells = wm9705_cells; + nb_cells = ARRAY_SIZE(wm9705_cells); + break; + case WM9712_VENDOR_ID: + config = &wm9712_regmap_config; + cells = wm9712_cells; + nb_cells = ARRAY_SIZE(wm9712_cells); + break; + case WM9713_VENDOR_ID: + config = &wm9713_regmap_config; + cells = wm9713_cells; + nb_cells = ARRAY_SIZE(wm9713_cells); + break; + default: + goto err_free_compat; + } + + for (i = 0; i < nb_cells; i++) { + cells[i].platform_data = codec_pdata; + cells[i].pdata_size = sizeof(*codec_pdata); + } + + codec_pdata->regmap = devm_regmap_init_ac97(wm97xx->ac97, config); + if (IS_ERR(codec_pdata->regmap)) { + ret = PTR_ERR(codec_pdata->regmap); + goto err_free_compat; + } + + ret = devm_mfd_add_devices(wm97xx->dev, PLATFORM_DEVID_NONE, + cells, nb_cells, NULL, 0, NULL); + if (ret) + goto err_free_compat; + + return ret; + +err_free_compat: + snd_ac97_compat_release(wm97xx->ac97); + return ret; +} + +static int wm97xx_ac97_remove(struct ac97_codec_device *adev) +{ + struct wm97xx_priv *wm97xx = ac97_get_drvdata(adev); + + snd_ac97_compat_release(wm97xx->ac97); + + return 0; +} + +static const struct ac97_id wm97xx_ac97_ids[] = { + { .id = WM9705_VENDOR_ID, .mask = WM97xx_VENDOR_ID_MASK }, + { .id = WM9712_VENDOR_ID, .mask = WM97xx_VENDOR_ID_MASK }, + { .id = WM9713_VENDOR_ID, .mask = WM97xx_VENDOR_ID_MASK }, + { } +}; + +static struct ac97_codec_driver wm97xx_ac97_driver = { + .driver = { + .name = "wm97xx-core", + }, + .probe = wm97xx_ac97_probe, + .remove = wm97xx_ac97_remove, + .id_table = wm97xx_ac97_ids, +}; + +static int __init wm97xx_module_init(void) +{ + return snd_ac97_codec_driver_register(&wm97xx_ac97_driver); +} +module_init(wm97xx_module_init); + +static void __exit wm97xx_module_exit(void) +{ + snd_ac97_codec_driver_unregister(&wm97xx_ac97_driver); +} +module_exit(wm97xx_module_exit); + +MODULE_DESCRIPTION("WM9712, WM9713 core driver"); +MODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index f501af0b3079..9fdf137c4865 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -187,6 +187,31 @@ EXPORT_SYMBOL_GPL(usb_unanchor_urb); /*-------------------------------------------------------------------*/ +static const int pipetypes[4] = { + PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT +}; + +/** + * usb_urb_ep_type_check - sanity check of endpoint in the given urb + * @urb: urb to be checked + * + * This performs a light-weight sanity check for the endpoint in the + * given urb. It returns 0 if the urb contains a valid endpoint, otherwise + * a negative error code. + */ +int usb_urb_ep_type_check(const struct urb *urb) +{ + const struct usb_host_endpoint *ep; + + ep = usb_pipe_endpoint(urb->dev, urb->pipe); + if (!ep) + return -EINVAL; + if (usb_pipetype(urb->pipe) != pipetypes[usb_endpoint_type(&ep->desc)]) + return -EINVAL; + return 0; +} +EXPORT_SYMBOL_GPL(usb_urb_ep_type_check); + /** * usb_submit_urb - issue an asynchronous transfer request for an endpoint * @urb: pointer to the urb describing the request @@ -326,9 +351,6 @@ EXPORT_SYMBOL_GPL(usb_unanchor_urb); */ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) { - static int pipetypes[4] = { - PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT - }; int xfertype, max; struct usb_device *dev; struct usb_host_endpoint *ep; @@ -444,7 +466,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) */ /* Check that the pipe's type matches the endpoint's type */ - if (usb_pipetype(urb->pipe) != pipetypes[xfertype]) + if (usb_urb_ep_type_check(urb)) dev_WARN(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n", usb_pipetype(urb->pipe), pipetypes[xfertype]); diff --git a/include/drm/amd_asic_type.h b/include/drm/amd_asic_type.h new file mode 100644 index 000000000000..599028f66585 --- /dev/null +++ b/include/drm/amd_asic_type.h @@ -0,0 +1,52 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __AMD_ASIC_TYPE_H__ +#define __AMD_ASIC_TYPE_H__ +/* + * Supported ASIC types + */ +enum amd_asic_type { + CHIP_TAHITI = 0, + CHIP_PITCAIRN, + CHIP_VERDE, + CHIP_OLAND, + CHIP_HAINAN, + CHIP_BONAIRE, + CHIP_KAVERI, + CHIP_KABINI, + CHIP_HAWAII, + CHIP_MULLINS, + CHIP_TOPAZ, + CHIP_TONGA, + CHIP_FIJI, + CHIP_CARRIZO, + CHIP_STONEY, + CHIP_POLARIS10, + CHIP_POLARIS11, + CHIP_POLARIS12, + CHIP_VEGA10, + CHIP_RAVEN, + CHIP_LAST, +}; + +#endif /*__AMD_ASIC_TYPE_H__ */ diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index bfeecf179895..f72dc53848d7 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -174,6 +174,9 @@ struct arizona_pdata { /** Mode for outputs */ int out_mono[ARIZONA_MAX_OUTPUT]; + /** Limit output volumes */ + unsigned int out_vol_limit[2 * ARIZONA_MAX_OUTPUT]; + /** PDM speaker mute setting */ unsigned int spk_mute[ARIZONA_MAX_PDM_SPK]; diff --git a/include/linux/mfd/wm97xx.h b/include/linux/mfd/wm97xx.h new file mode 100644 index 000000000000..45fb54f19d09 --- /dev/null +++ b/include/linux/mfd/wm97xx.h @@ -0,0 +1,25 @@ +/* + * wm97xx client interface + * + * Copyright (C) 2017 Robert Jarzmik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __LINUX_MFD_WM97XX_H +#define __LINUX_MFD_WM97XX_H + +struct regmap; +struct wm97xx_batt_pdata; +struct snd_ac97; + +struct wm97xx_platform_data { + struct snd_ac97 *ac97; + struct regmap *regmap; + struct wm97xx_batt_pdata *batt_pdata; +}; + +#endif diff --git a/include/linux/usb.h b/include/linux/usb.h index f019b0397599..fbbe974661f2 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1729,6 +1729,8 @@ static inline int usb_urb_dir_out(struct urb *urb) return (urb->transfer_flags & URB_DIR_MASK) == URB_DIR_OUT; } +int usb_urb_ep_type_check(const struct urb *urb); + void *usb_alloc_coherent(struct usb_device *dev, size_t size, gfp_t mem_flags, dma_addr_t *dma); void usb_free_coherent(struct usb_device *dev, size_t size, diff --git a/include/sound/ac97/codec.h b/include/sound/ac97/codec.h new file mode 100644 index 000000000000..ec04be9ab119 --- /dev/null +++ b/include/sound/ac97/codec.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __SOUND_AC97_CODEC2_H +#define __SOUND_AC97_CODEC2_H + +#include <linux/device.h> + +#define AC97_ID(vendor_id1, vendor_id2) \ + ((((vendor_id1) & 0xffff) << 16) | ((vendor_id2) & 0xffff)) +#define AC97_DRIVER_ID(vendor_id1, vendor_id2, mask_id1, mask_id2, _data) \ + { .id = (((vendor_id1) & 0xffff) << 16) | ((vendor_id2) & 0xffff), \ + .mask = (((mask_id1) & 0xffff) << 16) | ((mask_id2) & 0xffff), \ + .data = (_data) } + +struct ac97_controller; +struct clk; + +/** + * struct ac97_id - matches a codec device and driver on an ac97 bus + * @id: The significant bits if the codec vendor ID1 and ID2 + * @mask: Bitmask specifying which bits of the id field are significant when + * matching. A driver binds to a device when : + * ((vendorID1 << 8 | vendorID2) & (mask_id1 << 8 | mask_id2)) == id. + * @data: Private data used by the driver. + */ +struct ac97_id { + unsigned int id; + unsigned int mask; + void *data; +}; + +/** + * ac97_codec_device - a ac97 codec + * @dev: the core device + * @vendor_id: the vendor_id of the codec, as sensed on the AC-link + * @num: the codec number, 0 is primary, 1 is first slave, etc ... + * @clk: the clock BIT_CLK provided by the codec + * @ac97_ctrl: ac97 digital controller on the same AC-link + * + * This is the device instantiated for each codec living on a AC-link. There are + * normally 0 to 4 codec devices per AC-link, and all of them are controlled by + * an AC97 digital controller. + */ +struct ac97_codec_device { + struct device dev; + unsigned int vendor_id; + unsigned int num; + struct clk *clk; + struct ac97_controller *ac97_ctrl; +}; + +/** + * ac97_codec_driver - a ac97 codec driver + * @driver: the device driver structure + * @probe: the function called when a ac97_codec_device is matched + * @remove: the function called when the device is unbound/removed + * @shutdown: shutdown function (might be NULL) + * @id_table: ac97 vendor_id match table, { } member terminated + */ +struct ac97_codec_driver { + struct device_driver driver; + int (*probe)(struct ac97_codec_device *); + int (*remove)(struct ac97_codec_device *); + void (*shutdown)(struct ac97_codec_device *); + const struct ac97_id *id_table; +}; + +static inline struct ac97_codec_device *to_ac97_device(struct device *d) +{ + return container_of(d, struct ac97_codec_device, dev); +} + +static inline struct ac97_codec_driver *to_ac97_driver(struct device_driver *d) +{ + return container_of(d, struct ac97_codec_driver, driver); +} + +#if IS_ENABLED(CONFIG_AC97_BUS_NEW) +int snd_ac97_codec_driver_register(struct ac97_codec_driver *drv); +void snd_ac97_codec_driver_unregister(struct ac97_codec_driver *drv); +#else +static inline int +snd_ac97_codec_driver_register(struct ac97_codec_driver *drv) +{ + return 0; +} +static inline void +snd_ac97_codec_driver_unregister(struct ac97_codec_driver *drv) +{ +} +#endif + + +static inline struct device * +ac97_codec_dev2dev(struct ac97_codec_device *adev) +{ + return &adev->dev; +} + +static inline void *ac97_get_drvdata(struct ac97_codec_device *adev) +{ + return dev_get_drvdata(ac97_codec_dev2dev(adev)); +} + +static inline void ac97_set_drvdata(struct ac97_codec_device *adev, + void *data) +{ + dev_set_drvdata(ac97_codec_dev2dev(adev), data); +} + +void *snd_ac97_codec_get_platdata(const struct ac97_codec_device *adev); + +#endif diff --git a/include/sound/ac97/compat.h b/include/sound/ac97/compat.h new file mode 100644 index 000000000000..1351cba40048 --- /dev/null +++ b/include/sound/ac97/compat.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This file is for backward compatibility with snd_ac97 structure and its + * multiple usages, such as the snd_ac97_bus and snd_ac97_build_ops. + * + */ +#ifndef AC97_COMPAT_H +#define AC97_COMPAT_H + +#include <sound/ac97_codec.h> + +struct snd_ac97 *snd_ac97_compat_alloc(struct ac97_codec_device *adev); +void snd_ac97_compat_release(struct snd_ac97 *ac97); + +#endif diff --git a/include/sound/ac97/controller.h b/include/sound/ac97/controller.h new file mode 100644 index 000000000000..b36ecdd64f14 --- /dev/null +++ b/include/sound/ac97/controller.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef AC97_CONTROLLER_H +#define AC97_CONTROLLER_H + +#include <linux/device.h> +#include <linux/list.h> + +#define AC97_BUS_MAX_CODECS 4 +#define AC97_SLOTS_AVAILABLE_ALL 0xf + +struct ac97_controller_ops; + +/** + * struct ac97_controller - The AC97 controller of the AC-Link + * @ops: the AC97 operations. + * @controllers: linked list of all existing controllers. + * @adap: the shell device ac97-%d, ie. ac97 adapter + * @nr: the number of the shell device + * @slots_available: the mask of accessible/scanable codecs. + * @parent: the device providing the AC97 controller. + * @codecs: the 4 possible AC97 codecs (NULL if none found). + * @codecs_pdata: platform_data for each codec (NULL if no pdata). + * + * This structure is internal to AC97 bus, and should not be used by the + * controllers themselves, excepting for using @dev. + */ +struct ac97_controller { + const struct ac97_controller_ops *ops; + struct list_head controllers; + struct device adap; + int nr; + unsigned short slots_available; + struct device *parent; + struct ac97_codec_device *codecs[AC97_BUS_MAX_CODECS]; + void *codecs_pdata[AC97_BUS_MAX_CODECS]; +}; + +/** + * struct ac97_controller_ops - The AC97 operations + * @reset: Cold reset of the AC97 AC-Link. + * @warm_reset: Warm reset of the AC97 AC-Link. + * @read: Read of a single AC97 register. + * Returns the register value or a negative error code. + * @write: Write of a single AC97 register. + * + * These are the basic operation an AC97 controller must provide for an AC97 + * access functions. Amongst these, all but the last 2 are mandatory. + * The slot number is also known as the AC97 codec number, between 0 and 3. + */ +struct ac97_controller_ops { + void (*reset)(struct ac97_controller *adrv); + void (*warm_reset)(struct ac97_controller *adrv); + int (*write)(struct ac97_controller *adrv, int slot, + unsigned short reg, unsigned short val); + int (*read)(struct ac97_controller *adrv, int slot, unsigned short reg); +}; + +#if IS_ENABLED(CONFIG_AC97_BUS_NEW) +struct ac97_controller *snd_ac97_controller_register( + const struct ac97_controller_ops *ops, struct device *dev, + unsigned short slots_available, void **codecs_pdata); +void snd_ac97_controller_unregister(struct ac97_controller *ac97_ctrl); +#else +static inline struct ac97_controller * +snd_ac97_controller_register(const struct ac97_controller_ops *ops, + struct device *dev, + unsigned short slots_available, + void **codecs_pdata) +{ + return ERR_PTR(-ENODEV); +} + +static inline void +snd_ac97_controller_unregister(struct ac97_controller *ac97_ctrl) +{ +} +#endif + +#endif diff --git a/include/sound/ac97/regs.h b/include/sound/ac97/regs.h new file mode 100644 index 000000000000..4bb86d379bd5 --- /dev/null +++ b/include/sound/ac97/regs.h @@ -0,0 +1,262 @@ +/* + * Copyright (c) by Jaroslav Kysela <perex@perex.cz> + * Universal interface for Audio Codec '97 + * + * For more details look to AC '97 component specification revision 2.1 + * by Intel Corporation (http://developer.intel.com). + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; 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 + * + */ + +/* + * AC'97 codec registers + */ + +#define AC97_RESET 0x00 /* Reset */ +#define AC97_MASTER 0x02 /* Master Volume */ +#define AC97_HEADPHONE 0x04 /* Headphone Volume (optional) */ +#define AC97_MASTER_MONO 0x06 /* Master Volume Mono (optional) */ +#define AC97_MASTER_TONE 0x08 /* Master Tone (Bass & Treble) (optional) */ +#define AC97_PC_BEEP 0x0a /* PC Beep Volume (optinal) */ +#define AC97_PHONE 0x0c /* Phone Volume (optional) */ +#define AC97_MIC 0x0e /* MIC Volume */ +#define AC97_LINE 0x10 /* Line In Volume */ +#define AC97_CD 0x12 /* CD Volume */ +#define AC97_VIDEO 0x14 /* Video Volume (optional) */ +#define AC97_AUX 0x16 /* AUX Volume (optional) */ +#define AC97_PCM 0x18 /* PCM Volume */ +#define AC97_REC_SEL 0x1a /* Record Select */ +#define AC97_REC_GAIN 0x1c /* Record Gain */ +#define AC97_REC_GAIN_MIC 0x1e /* Record Gain MIC (optional) */ +#define AC97_GENERAL_PURPOSE 0x20 /* General Purpose (optional) */ +#define AC97_3D_CONTROL 0x22 /* 3D Control (optional) */ +#define AC97_INT_PAGING 0x24 /* Audio Interrupt & Paging (AC'97 2.3) */ +#define AC97_POWERDOWN 0x26 /* Powerdown control / status */ +/* range 0x28-0x3a - AUDIO AC'97 2.0 extensions */ +#define AC97_EXTENDED_ID 0x28 /* Extended Audio ID */ +#define AC97_EXTENDED_STATUS 0x2a /* Extended Audio Status and Control */ +#define AC97_PCM_FRONT_DAC_RATE 0x2c /* PCM Front DAC Rate */ +#define AC97_PCM_SURR_DAC_RATE 0x2e /* PCM Surround DAC Rate */ +#define AC97_PCM_LFE_DAC_RATE 0x30 /* PCM LFE DAC Rate */ +#define AC97_PCM_LR_ADC_RATE 0x32 /* PCM LR ADC Rate */ +#define AC97_PCM_MIC_ADC_RATE 0x34 /* PCM MIC ADC Rate */ +#define AC97_CENTER_LFE_MASTER 0x36 /* Center + LFE Master Volume */ +#define AC97_SURROUND_MASTER 0x38 /* Surround (Rear) Master Volume */ +#define AC97_SPDIF 0x3a /* S/PDIF control */ +/* range 0x3c-0x58 - MODEM */ +#define AC97_EXTENDED_MID 0x3c /* Extended Modem ID */ +#define AC97_EXTENDED_MSTATUS 0x3e /* Extended Modem Status and Control */ +#define AC97_LINE1_RATE 0x40 /* Line1 DAC/ADC Rate */ +#define AC97_LINE2_RATE 0x42 /* Line2 DAC/ADC Rate */ +#define AC97_HANDSET_RATE 0x44 /* Handset DAC/ADC Rate */ +#define AC97_LINE1_LEVEL 0x46 /* Line1 DAC/ADC Level */ +#define AC97_LINE2_LEVEL 0x48 /* Line2 DAC/ADC Level */ +#define AC97_HANDSET_LEVEL 0x4a /* Handset DAC/ADC Level */ +#define AC97_GPIO_CFG 0x4c /* GPIO Configuration */ +#define AC97_GPIO_POLARITY 0x4e /* GPIO Pin Polarity/Type, 0=low, 1=high active */ +#define AC97_GPIO_STICKY 0x50 /* GPIO Pin Sticky, 0=not, 1=sticky */ +#define AC97_GPIO_WAKEUP 0x52 /* GPIO Pin Wakeup, 0=no int, 1=yes int */ +#define AC97_GPIO_STATUS 0x54 /* GPIO Pin Status, slot 12 */ +#define AC97_MISC_AFE 0x56 /* Miscellaneous Modem AFE Status and Control */ +/* range 0x5a-0x7b - Vendor Specific */ +#define AC97_VENDOR_ID1 0x7c /* Vendor ID1 */ +#define AC97_VENDOR_ID2 0x7e /* Vendor ID2 / revision */ +/* range 0x60-0x6f (page 1) - extended codec registers */ +#define AC97_CODEC_CLASS_REV 0x60 /* Codec Class/Revision */ +#define AC97_PCI_SVID 0x62 /* PCI Subsystem Vendor ID */ +#define AC97_PCI_SID 0x64 /* PCI Subsystem ID */ +#define AC97_FUNC_SELECT 0x66 /* Function Select */ +#define AC97_FUNC_INFO 0x68 /* Function Information */ +#define AC97_SENSE_INFO 0x6a /* Sense Details */ + +/* volume controls */ +#define AC97_MUTE_MASK_MONO 0x8000 +#define AC97_MUTE_MASK_STEREO 0x8080 + +/* slot allocation */ +#define AC97_SLOT_TAG 0 +#define AC97_SLOT_CMD_ADDR 1 +#define AC97_SLOT_CMD_DATA 2 +#define AC97_SLOT_PCM_LEFT 3 +#define AC97_SLOT_PCM_RIGHT 4 +#define AC97_SLOT_MODEM_LINE1 5 +#define AC97_SLOT_PCM_CENTER 6 +#define AC97_SLOT_MIC 6 /* input */ +#define AC97_SLOT_SPDIF_LEFT1 6 +#define AC97_SLOT_PCM_SLEFT 7 /* surround left */ +#define AC97_SLOT_PCM_LEFT_0 7 /* double rate operation */ +#define AC97_SLOT_SPDIF_LEFT 7 +#define AC97_SLOT_PCM_SRIGHT 8 /* surround right */ +#define AC97_SLOT_PCM_RIGHT_0 8 /* double rate operation */ +#define AC97_SLOT_SPDIF_RIGHT 8 +#define AC97_SLOT_LFE 9 +#define AC97_SLOT_SPDIF_RIGHT1 9 +#define AC97_SLOT_MODEM_LINE2 10 +#define AC97_SLOT_PCM_LEFT_1 10 /* double rate operation */ +#define AC97_SLOT_SPDIF_LEFT2 10 +#define AC97_SLOT_HANDSET 11 /* output */ +#define AC97_SLOT_PCM_RIGHT_1 11 /* double rate operation */ +#define AC97_SLOT_SPDIF_RIGHT2 11 +#define AC97_SLOT_MODEM_GPIO 12 /* modem GPIO */ +#define AC97_SLOT_PCM_CENTER_1 12 /* double rate operation */ + +/* basic capabilities (reset register) */ +#define AC97_BC_DEDICATED_MIC 0x0001 /* Dedicated Mic PCM In Channel */ +#define AC97_BC_RESERVED1 0x0002 /* Reserved (was Modem Line Codec support) */ +#define AC97_BC_BASS_TREBLE 0x0004 /* Bass & Treble Control */ +#define AC97_BC_SIM_STEREO 0x0008 /* Simulated stereo */ +#define AC97_BC_HEADPHONE 0x0010 /* Headphone Out Support */ +#define AC97_BC_LOUDNESS 0x0020 /* Loudness (bass boost) Support */ +#define AC97_BC_16BIT_DAC 0x0000 /* 16-bit DAC resolution */ +#define AC97_BC_18BIT_DAC 0x0040 /* 18-bit DAC resolution */ +#define AC97_BC_20BIT_DAC 0x0080 /* 20-bit DAC resolution */ +#define AC97_BC_DAC_MASK 0x00c0 +#define AC97_BC_16BIT_ADC 0x0000 /* 16-bit ADC resolution */ +#define AC97_BC_18BIT_ADC 0x0100 /* 18-bit ADC resolution */ +#define AC97_BC_20BIT_ADC 0x0200 /* 20-bit ADC resolution */ +#define AC97_BC_ADC_MASK 0x0300 +#define AC97_BC_3D_TECH_ID_MASK 0x7c00 /* Per-vendor ID of 3D enhancement */ + +/* general purpose */ +#define AC97_GP_DRSS_MASK 0x0c00 /* double rate slot select */ +#define AC97_GP_DRSS_1011 0x0000 /* LR(C) 10+11(+12) */ +#define AC97_GP_DRSS_78 0x0400 /* LR 7+8 */ + +/* powerdown bits */ +#define AC97_PD_ADC_STATUS 0x0001 /* ADC status (RO) */ +#define AC97_PD_DAC_STATUS 0x0002 /* DAC status (RO) */ +#define AC97_PD_MIXER_STATUS 0x0004 /* Analog mixer status (RO) */ +#define AC97_PD_VREF_STATUS 0x0008 /* Vref status (RO) */ +#define AC97_PD_PR0 0x0100 /* Power down PCM ADCs and input MUX */ +#define AC97_PD_PR1 0x0200 /* Power down PCM front DAC */ +#define AC97_PD_PR2 0x0400 /* Power down Mixer (Vref still on) */ +#define AC97_PD_PR3 0x0800 /* Power down Mixer (Vref off) */ +#define AC97_PD_PR4 0x1000 /* Power down AC-Link */ +#define AC97_PD_PR5 0x2000 /* Disable internal clock usage */ +#define AC97_PD_PR6 0x4000 /* Headphone amplifier */ +#define AC97_PD_EAPD 0x8000 /* External Amplifer Power Down (EAPD) */ + +/* extended audio ID bit defines */ +#define AC97_EI_VRA 0x0001 /* Variable bit rate supported */ +#define AC97_EI_DRA 0x0002 /* Double rate supported */ +#define AC97_EI_SPDIF 0x0004 /* S/PDIF out supported */ +#define AC97_EI_VRM 0x0008 /* Variable bit rate supported for MIC */ +#define AC97_EI_DACS_SLOT_MASK 0x0030 /* DACs slot assignment */ +#define AC97_EI_DACS_SLOT_SHIFT 4 +#define AC97_EI_CDAC 0x0040 /* PCM Center DAC available */ +#define AC97_EI_SDAC 0x0080 /* PCM Surround DACs available */ +#define AC97_EI_LDAC 0x0100 /* PCM LFE DAC available */ +#define AC97_EI_AMAP 0x0200 /* indicates optional slot/DAC mapping based on codec ID */ +#define AC97_EI_REV_MASK 0x0c00 /* AC'97 revision mask */ +#define AC97_EI_REV_22 0x0400 /* AC'97 revision 2.2 */ +#define AC97_EI_REV_23 0x0800 /* AC'97 revision 2.3 */ +#define AC97_EI_REV_SHIFT 10 +#define AC97_EI_ADDR_MASK 0xc000 /* physical codec ID (address) */ +#define AC97_EI_ADDR_SHIFT 14 + +/* extended audio status and control bit defines */ +#define AC97_EA_VRA 0x0001 /* Variable bit rate enable bit */ +#define AC97_EA_DRA 0x0002 /* Double-rate audio enable bit */ +#define AC97_EA_SPDIF 0x0004 /* S/PDIF out enable bit */ +#define AC97_EA_VRM 0x0008 /* Variable bit rate for MIC enable bit */ +#define AC97_EA_SPSA_SLOT_MASK 0x0030 /* Mask for slot assignment bits */ +#define AC97_EA_SPSA_SLOT_SHIFT 4 +#define AC97_EA_SPSA_3_4 0x0000 /* Slot assigned to 3 & 4 */ +#define AC97_EA_SPSA_7_8 0x0010 /* Slot assigned to 7 & 8 */ +#define AC97_EA_SPSA_6_9 0x0020 /* Slot assigned to 6 & 9 */ +#define AC97_EA_SPSA_10_11 0x0030 /* Slot assigned to 10 & 11 */ +#define AC97_EA_CDAC 0x0040 /* PCM Center DAC is ready (Read only) */ +#define AC97_EA_SDAC 0x0080 /* PCM Surround DACs are ready (Read only) */ +#define AC97_EA_LDAC 0x0100 /* PCM LFE DAC is ready (Read only) */ +#define AC97_EA_MDAC 0x0200 /* MIC ADC is ready (Read only) */ +#define AC97_EA_SPCV 0x0400 /* S/PDIF configuration valid (Read only) */ +#define AC97_EA_PRI 0x0800 /* Turns the PCM Center DAC off */ +#define AC97_EA_PRJ 0x1000 /* Turns the PCM Surround DACs off */ +#define AC97_EA_PRK 0x2000 /* Turns the PCM LFE DAC off */ +#define AC97_EA_PRL 0x4000 /* Turns the MIC ADC off */ + +/* S/PDIF control bit defines */ +#define AC97_SC_PRO 0x0001 /* Professional status */ +#define AC97_SC_NAUDIO 0x0002 /* Non audio stream */ +#define AC97_SC_COPY 0x0004 /* Copyright status */ +#define AC97_SC_PRE 0x0008 /* Preemphasis status */ +#define AC97_SC_CC_MASK 0x07f0 /* Category Code mask */ +#define AC97_SC_CC_SHIFT 4 +#define AC97_SC_L 0x0800 /* Generation Level status */ +#define AC97_SC_SPSR_MASK 0x3000 /* S/PDIF Sample Rate bits */ +#define AC97_SC_SPSR_SHIFT 12 +#define AC97_SC_SPSR_44K 0x0000 /* Use 44.1kHz Sample rate */ +#define AC97_SC_SPSR_48K 0x2000 /* Use 48kHz Sample rate */ +#define AC97_SC_SPSR_32K 0x3000 /* Use 32kHz Sample rate */ +#define AC97_SC_DRS 0x4000 /* Double Rate S/PDIF */ +#define AC97_SC_V 0x8000 /* Validity status */ + +/* Interrupt and Paging bit defines (AC'97 2.3) */ +#define AC97_PAGE_MASK 0x000f /* Page Selector */ +#define AC97_PAGE_VENDOR 0 /* Vendor-specific registers */ +#define AC97_PAGE_1 1 /* Extended Codec Registers page 1 */ +#define AC97_INT_ENABLE 0x0800 /* Interrupt Enable */ +#define AC97_INT_SENSE 0x1000 /* Sense Cycle */ +#define AC97_INT_CAUSE_SENSE 0x2000 /* Sense Cycle Completed (RO) */ +#define AC97_INT_CAUSE_GPIO 0x4000 /* GPIO bits changed (RO) */ +#define AC97_INT_STATUS 0x8000 /* Interrupt Status */ + +/* extended modem ID bit defines */ +#define AC97_MEI_LINE1 0x0001 /* Line1 present */ +#define AC97_MEI_LINE2 0x0002 /* Line2 present */ +#define AC97_MEI_HANDSET 0x0004 /* Handset present */ +#define AC97_MEI_CID1 0x0008 /* caller ID decode for Line1 is supported */ +#define AC97_MEI_CID2 0x0010 /* caller ID decode for Line2 is supported */ +#define AC97_MEI_ADDR_MASK 0xc000 /* physical codec ID (address) */ +#define AC97_MEI_ADDR_SHIFT 14 + +/* extended modem status and control bit defines */ +#define AC97_MEA_GPIO 0x0001 /* GPIO is ready (ro) */ +#define AC97_MEA_MREF 0x0002 /* Vref is up to nominal level (ro) */ +#define AC97_MEA_ADC1 0x0004 /* ADC1 operational (ro) */ +#define AC97_MEA_DAC1 0x0008 /* DAC1 operational (ro) */ +#define AC97_MEA_ADC2 0x0010 /* ADC2 operational (ro) */ +#define AC97_MEA_DAC2 0x0020 /* DAC2 operational (ro) */ +#define AC97_MEA_HADC 0x0040 /* HADC operational (ro) */ +#define AC97_MEA_HDAC 0x0080 /* HDAC operational (ro) */ +#define AC97_MEA_PRA 0x0100 /* GPIO power down (high) */ +#define AC97_MEA_PRB 0x0200 /* reserved */ +#define AC97_MEA_PRC 0x0400 /* ADC1 power down (high) */ +#define AC97_MEA_PRD 0x0800 /* DAC1 power down (high) */ +#define AC97_MEA_PRE 0x1000 /* ADC2 power down (high) */ +#define AC97_MEA_PRF 0x2000 /* DAC2 power down (high) */ +#define AC97_MEA_PRG 0x4000 /* HADC power down (high) */ +#define AC97_MEA_PRH 0x8000 /* HDAC power down (high) */ + +/* modem gpio status defines */ +#define AC97_GPIO_LINE1_OH 0x0001 /* Off Hook Line1 */ +#define AC97_GPIO_LINE1_RI 0x0002 /* Ring Detect Line1 */ +#define AC97_GPIO_LINE1_CID 0x0004 /* Caller ID path enable Line1 */ +#define AC97_GPIO_LINE1_LCS 0x0008 /* Loop Current Sense Line1 */ +#define AC97_GPIO_LINE1_PULSE 0x0010 /* Opt./ Pulse Dial Line1 (out) */ +#define AC97_GPIO_LINE1_HL1R 0x0020 /* Opt./ Handset to Line1 relay control (out) */ +#define AC97_GPIO_LINE1_HOHD 0x0040 /* Opt./ Handset off hook detect Line1 (in) */ +#define AC97_GPIO_LINE12_AC 0x0080 /* Opt./ Int.bit 1 / Line1/2 AC (out) */ +#define AC97_GPIO_LINE12_DC 0x0100 /* Opt./ Int.bit 2 / Line1/2 DC (out) */ +#define AC97_GPIO_LINE12_RS 0x0200 /* Opt./ Int.bit 3 / Line1/2 RS (out) */ +#define AC97_GPIO_LINE2_OH 0x0400 /* Off Hook Line2 */ +#define AC97_GPIO_LINE2_RI 0x0800 /* Ring Detect Line2 */ +#define AC97_GPIO_LINE2_CID 0x1000 /* Caller ID path enable Line2 */ +#define AC97_GPIO_LINE2_LCS 0x2000 /* Loop Current Sense Line2 */ +#define AC97_GPIO_LINE2_PULSE 0x4000 /* Opt./ Pulse Dial Line2 (out) */ +#define AC97_GPIO_LINE2_HL1R 0x8000 /* Opt./ Handset to Line2 relay control (out) */ + diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h index 15aa5f07c955..89d311a503d3 100644 --- a/include/sound/ac97_codec.h +++ b/include/sound/ac97_codec.h @@ -28,6 +28,7 @@ #include <linux/bitops.h> #include <linux/device.h> #include <linux/workqueue.h> +#include <sound/ac97/regs.h> #include <sound/pcm.h> #include <sound/control.h> #include <sound/info.h> @@ -35,244 +36,6 @@ /* maximum number of devices on the AC97 bus */ #define AC97_BUS_MAX_DEVICES 4 -/* - * AC'97 codec registers - */ - -#define AC97_RESET 0x00 /* Reset */ -#define AC97_MASTER 0x02 /* Master Volume */ -#define AC97_HEADPHONE 0x04 /* Headphone Volume (optional) */ -#define AC97_MASTER_MONO 0x06 /* Master Volume Mono (optional) */ -#define AC97_MASTER_TONE 0x08 /* Master Tone (Bass & Treble) (optional) */ -#define AC97_PC_BEEP 0x0a /* PC Beep Volume (optinal) */ -#define AC97_PHONE 0x0c /* Phone Volume (optional) */ -#define AC97_MIC 0x0e /* MIC Volume */ -#define AC97_LINE 0x10 /* Line In Volume */ -#define AC97_CD 0x12 /* CD Volume */ -#define AC97_VIDEO 0x14 /* Video Volume (optional) */ -#define AC97_AUX 0x16 /* AUX Volume (optional) */ -#define AC97_PCM 0x18 /* PCM Volume */ -#define AC97_REC_SEL 0x1a /* Record Select */ -#define AC97_REC_GAIN 0x1c /* Record Gain */ -#define AC97_REC_GAIN_MIC 0x1e /* Record Gain MIC (optional) */ -#define AC97_GENERAL_PURPOSE 0x20 /* General Purpose (optional) */ -#define AC97_3D_CONTROL 0x22 /* 3D Control (optional) */ -#define AC97_INT_PAGING 0x24 /* Audio Interrupt & Paging (AC'97 2.3) */ -#define AC97_POWERDOWN 0x26 /* Powerdown control / status */ -/* range 0x28-0x3a - AUDIO AC'97 2.0 extensions */ -#define AC97_EXTENDED_ID 0x28 /* Extended Audio ID */ -#define AC97_EXTENDED_STATUS 0x2a /* Extended Audio Status and Control */ -#define AC97_PCM_FRONT_DAC_RATE 0x2c /* PCM Front DAC Rate */ -#define AC97_PCM_SURR_DAC_RATE 0x2e /* PCM Surround DAC Rate */ -#define AC97_PCM_LFE_DAC_RATE 0x30 /* PCM LFE DAC Rate */ -#define AC97_PCM_LR_ADC_RATE 0x32 /* PCM LR ADC Rate */ -#define AC97_PCM_MIC_ADC_RATE 0x34 /* PCM MIC ADC Rate */ -#define AC97_CENTER_LFE_MASTER 0x36 /* Center + LFE Master Volume */ -#define AC97_SURROUND_MASTER 0x38 /* Surround (Rear) Master Volume */ -#define AC97_SPDIF 0x3a /* S/PDIF control */ -/* range 0x3c-0x58 - MODEM */ -#define AC97_EXTENDED_MID 0x3c /* Extended Modem ID */ -#define AC97_EXTENDED_MSTATUS 0x3e /* Extended Modem Status and Control */ -#define AC97_LINE1_RATE 0x40 /* Line1 DAC/ADC Rate */ -#define AC97_LINE2_RATE 0x42 /* Line2 DAC/ADC Rate */ -#define AC97_HANDSET_RATE 0x44 /* Handset DAC/ADC Rate */ -#define AC97_LINE1_LEVEL 0x46 /* Line1 DAC/ADC Level */ -#define AC97_LINE2_LEVEL 0x48 /* Line2 DAC/ADC Level */ -#define AC97_HANDSET_LEVEL 0x4a /* Handset DAC/ADC Level */ -#define AC97_GPIO_CFG 0x4c /* GPIO Configuration */ -#define AC97_GPIO_POLARITY 0x4e /* GPIO Pin Polarity/Type, 0=low, 1=high active */ -#define AC97_GPIO_STICKY 0x50 /* GPIO Pin Sticky, 0=not, 1=sticky */ -#define AC97_GPIO_WAKEUP 0x52 /* GPIO Pin Wakeup, 0=no int, 1=yes int */ -#define AC97_GPIO_STATUS 0x54 /* GPIO Pin Status, slot 12 */ -#define AC97_MISC_AFE 0x56 /* Miscellaneous Modem AFE Status and Control */ -/* range 0x5a-0x7b - Vendor Specific */ -#define AC97_VENDOR_ID1 0x7c /* Vendor ID1 */ -#define AC97_VENDOR_ID2 0x7e /* Vendor ID2 / revision */ -/* range 0x60-0x6f (page 1) - extended codec registers */ -#define AC97_CODEC_CLASS_REV 0x60 /* Codec Class/Revision */ -#define AC97_PCI_SVID 0x62 /* PCI Subsystem Vendor ID */ -#define AC97_PCI_SID 0x64 /* PCI Subsystem ID */ -#define AC97_FUNC_SELECT 0x66 /* Function Select */ -#define AC97_FUNC_INFO 0x68 /* Function Information */ -#define AC97_SENSE_INFO 0x6a /* Sense Details */ - -/* volume controls */ -#define AC97_MUTE_MASK_MONO 0x8000 -#define AC97_MUTE_MASK_STEREO 0x8080 - -/* slot allocation */ -#define AC97_SLOT_TAG 0 -#define AC97_SLOT_CMD_ADDR 1 -#define AC97_SLOT_CMD_DATA 2 -#define AC97_SLOT_PCM_LEFT 3 -#define AC97_SLOT_PCM_RIGHT 4 -#define AC97_SLOT_MODEM_LINE1 5 -#define AC97_SLOT_PCM_CENTER 6 -#define AC97_SLOT_MIC 6 /* input */ -#define AC97_SLOT_SPDIF_LEFT1 6 -#define AC97_SLOT_PCM_SLEFT 7 /* surround left */ -#define AC97_SLOT_PCM_LEFT_0 7 /* double rate operation */ -#define AC97_SLOT_SPDIF_LEFT 7 -#define AC97_SLOT_PCM_SRIGHT 8 /* surround right */ -#define AC97_SLOT_PCM_RIGHT_0 8 /* double rate operation */ -#define AC97_SLOT_SPDIF_RIGHT 8 -#define AC97_SLOT_LFE 9 -#define AC97_SLOT_SPDIF_RIGHT1 9 -#define AC97_SLOT_MODEM_LINE2 10 -#define AC97_SLOT_PCM_LEFT_1 10 /* double rate operation */ -#define AC97_SLOT_SPDIF_LEFT2 10 -#define AC97_SLOT_HANDSET 11 /* output */ -#define AC97_SLOT_PCM_RIGHT_1 11 /* double rate operation */ -#define AC97_SLOT_SPDIF_RIGHT2 11 -#define AC97_SLOT_MODEM_GPIO 12 /* modem GPIO */ -#define AC97_SLOT_PCM_CENTER_1 12 /* double rate operation */ - -/* basic capabilities (reset register) */ -#define AC97_BC_DEDICATED_MIC 0x0001 /* Dedicated Mic PCM In Channel */ -#define AC97_BC_RESERVED1 0x0002 /* Reserved (was Modem Line Codec support) */ -#define AC97_BC_BASS_TREBLE 0x0004 /* Bass & Treble Control */ -#define AC97_BC_SIM_STEREO 0x0008 /* Simulated stereo */ -#define AC97_BC_HEADPHONE 0x0010 /* Headphone Out Support */ -#define AC97_BC_LOUDNESS 0x0020 /* Loudness (bass boost) Support */ -#define AC97_BC_16BIT_DAC 0x0000 /* 16-bit DAC resolution */ -#define AC97_BC_18BIT_DAC 0x0040 /* 18-bit DAC resolution */ -#define AC97_BC_20BIT_DAC 0x0080 /* 20-bit DAC resolution */ -#define AC97_BC_DAC_MASK 0x00c0 -#define AC97_BC_16BIT_ADC 0x0000 /* 16-bit ADC resolution */ -#define AC97_BC_18BIT_ADC 0x0100 /* 18-bit ADC resolution */ -#define AC97_BC_20BIT_ADC 0x0200 /* 20-bit ADC resolution */ -#define AC97_BC_ADC_MASK 0x0300 -#define AC97_BC_3D_TECH_ID_MASK 0x7c00 /* Per-vendor ID of 3D enhancement */ - -/* general purpose */ -#define AC97_GP_DRSS_MASK 0x0c00 /* double rate slot select */ -#define AC97_GP_DRSS_1011 0x0000 /* LR(C) 10+11(+12) */ -#define AC97_GP_DRSS_78 0x0400 /* LR 7+8 */ - -/* powerdown bits */ -#define AC97_PD_ADC_STATUS 0x0001 /* ADC status (RO) */ -#define AC97_PD_DAC_STATUS 0x0002 /* DAC status (RO) */ -#define AC97_PD_MIXER_STATUS 0x0004 /* Analog mixer status (RO) */ -#define AC97_PD_VREF_STATUS 0x0008 /* Vref status (RO) */ -#define AC97_PD_PR0 0x0100 /* Power down PCM ADCs and input MUX */ -#define AC97_PD_PR1 0x0200 /* Power down PCM front DAC */ -#define AC97_PD_PR2 0x0400 /* Power down Mixer (Vref still on) */ -#define AC97_PD_PR3 0x0800 /* Power down Mixer (Vref off) */ -#define AC97_PD_PR4 0x1000 /* Power down AC-Link */ -#define AC97_PD_PR5 0x2000 /* Disable internal clock usage */ -#define AC97_PD_PR6 0x4000 /* Headphone amplifier */ -#define AC97_PD_EAPD 0x8000 /* External Amplifer Power Down (EAPD) */ - -/* extended audio ID bit defines */ -#define AC97_EI_VRA 0x0001 /* Variable bit rate supported */ -#define AC97_EI_DRA 0x0002 /* Double rate supported */ -#define AC97_EI_SPDIF 0x0004 /* S/PDIF out supported */ -#define AC97_EI_VRM 0x0008 /* Variable bit rate supported for MIC */ -#define AC97_EI_DACS_SLOT_MASK 0x0030 /* DACs slot assignment */ -#define AC97_EI_DACS_SLOT_SHIFT 4 -#define AC97_EI_CDAC 0x0040 /* PCM Center DAC available */ -#define AC97_EI_SDAC 0x0080 /* PCM Surround DACs available */ -#define AC97_EI_LDAC 0x0100 /* PCM LFE DAC available */ -#define AC97_EI_AMAP 0x0200 /* indicates optional slot/DAC mapping based on codec ID */ -#define AC97_EI_REV_MASK 0x0c00 /* AC'97 revision mask */ -#define AC97_EI_REV_22 0x0400 /* AC'97 revision 2.2 */ -#define AC97_EI_REV_23 0x0800 /* AC'97 revision 2.3 */ -#define AC97_EI_REV_SHIFT 10 -#define AC97_EI_ADDR_MASK 0xc000 /* physical codec ID (address) */ -#define AC97_EI_ADDR_SHIFT 14 - -/* extended audio status and control bit defines */ -#define AC97_EA_VRA 0x0001 /* Variable bit rate enable bit */ -#define AC97_EA_DRA 0x0002 /* Double-rate audio enable bit */ -#define AC97_EA_SPDIF 0x0004 /* S/PDIF out enable bit */ -#define AC97_EA_VRM 0x0008 /* Variable bit rate for MIC enable bit */ -#define AC97_EA_SPSA_SLOT_MASK 0x0030 /* Mask for slot assignment bits */ -#define AC97_EA_SPSA_SLOT_SHIFT 4 -#define AC97_EA_SPSA_3_4 0x0000 /* Slot assigned to 3 & 4 */ -#define AC97_EA_SPSA_7_8 0x0010 /* Slot assigned to 7 & 8 */ -#define AC97_EA_SPSA_6_9 0x0020 /* Slot assigned to 6 & 9 */ -#define AC97_EA_SPSA_10_11 0x0030 /* Slot assigned to 10 & 11 */ -#define AC97_EA_CDAC 0x0040 /* PCM Center DAC is ready (Read only) */ -#define AC97_EA_SDAC 0x0080 /* PCM Surround DACs are ready (Read only) */ -#define AC97_EA_LDAC 0x0100 /* PCM LFE DAC is ready (Read only) */ -#define AC97_EA_MDAC 0x0200 /* MIC ADC is ready (Read only) */ -#define AC97_EA_SPCV 0x0400 /* S/PDIF configuration valid (Read only) */ -#define AC97_EA_PRI 0x0800 /* Turns the PCM Center DAC off */ -#define AC97_EA_PRJ 0x1000 /* Turns the PCM Surround DACs off */ -#define AC97_EA_PRK 0x2000 /* Turns the PCM LFE DAC off */ -#define AC97_EA_PRL 0x4000 /* Turns the MIC ADC off */ - -/* S/PDIF control bit defines */ -#define AC97_SC_PRO 0x0001 /* Professional status */ -#define AC97_SC_NAUDIO 0x0002 /* Non audio stream */ -#define AC97_SC_COPY 0x0004 /* Copyright status */ -#define AC97_SC_PRE 0x0008 /* Preemphasis status */ -#define AC97_SC_CC_MASK 0x07f0 /* Category Code mask */ -#define AC97_SC_CC_SHIFT 4 -#define AC97_SC_L 0x0800 /* Generation Level status */ -#define AC97_SC_SPSR_MASK 0x3000 /* S/PDIF Sample Rate bits */ -#define AC97_SC_SPSR_SHIFT 12 -#define AC97_SC_SPSR_44K 0x0000 /* Use 44.1kHz Sample rate */ -#define AC97_SC_SPSR_48K 0x2000 /* Use 48kHz Sample rate */ -#define AC97_SC_SPSR_32K 0x3000 /* Use 32kHz Sample rate */ -#define AC97_SC_DRS 0x4000 /* Double Rate S/PDIF */ -#define AC97_SC_V 0x8000 /* Validity status */ - -/* Interrupt and Paging bit defines (AC'97 2.3) */ -#define AC97_PAGE_MASK 0x000f /* Page Selector */ -#define AC97_PAGE_VENDOR 0 /* Vendor-specific registers */ -#define AC97_PAGE_1 1 /* Extended Codec Registers page 1 */ -#define AC97_INT_ENABLE 0x0800 /* Interrupt Enable */ -#define AC97_INT_SENSE 0x1000 /* Sense Cycle */ -#define AC97_INT_CAUSE_SENSE 0x2000 /* Sense Cycle Completed (RO) */ -#define AC97_INT_CAUSE_GPIO 0x4000 /* GPIO bits changed (RO) */ -#define AC97_INT_STATUS 0x8000 /* Interrupt Status */ - -/* extended modem ID bit defines */ -#define AC97_MEI_LINE1 0x0001 /* Line1 present */ -#define AC97_MEI_LINE2 0x0002 /* Line2 present */ -#define AC97_MEI_HANDSET 0x0004 /* Handset present */ -#define AC97_MEI_CID1 0x0008 /* caller ID decode for Line1 is supported */ -#define AC97_MEI_CID2 0x0010 /* caller ID decode for Line2 is supported */ -#define AC97_MEI_ADDR_MASK 0xc000 /* physical codec ID (address) */ -#define AC97_MEI_ADDR_SHIFT 14 - -/* extended modem status and control bit defines */ -#define AC97_MEA_GPIO 0x0001 /* GPIO is ready (ro) */ -#define AC97_MEA_MREF 0x0002 /* Vref is up to nominal level (ro) */ -#define AC97_MEA_ADC1 0x0004 /* ADC1 operational (ro) */ -#define AC97_MEA_DAC1 0x0008 /* DAC1 operational (ro) */ -#define AC97_MEA_ADC2 0x0010 /* ADC2 operational (ro) */ -#define AC97_MEA_DAC2 0x0020 /* DAC2 operational (ro) */ -#define AC97_MEA_HADC 0x0040 /* HADC operational (ro) */ -#define AC97_MEA_HDAC 0x0080 /* HDAC operational (ro) */ -#define AC97_MEA_PRA 0x0100 /* GPIO power down (high) */ -#define AC97_MEA_PRB 0x0200 /* reserved */ -#define AC97_MEA_PRC 0x0400 /* ADC1 power down (high) */ -#define AC97_MEA_PRD 0x0800 /* DAC1 power down (high) */ -#define AC97_MEA_PRE 0x1000 /* ADC2 power down (high) */ -#define AC97_MEA_PRF 0x2000 /* DAC2 power down (high) */ -#define AC97_MEA_PRG 0x4000 /* HADC power down (high) */ -#define AC97_MEA_PRH 0x8000 /* HDAC power down (high) */ - -/* modem gpio status defines */ -#define AC97_GPIO_LINE1_OH 0x0001 /* Off Hook Line1 */ -#define AC97_GPIO_LINE1_RI 0x0002 /* Ring Detect Line1 */ -#define AC97_GPIO_LINE1_CID 0x0004 /* Caller ID path enable Line1 */ -#define AC97_GPIO_LINE1_LCS 0x0008 /* Loop Current Sense Line1 */ -#define AC97_GPIO_LINE1_PULSE 0x0010 /* Opt./ Pulse Dial Line1 (out) */ -#define AC97_GPIO_LINE1_HL1R 0x0020 /* Opt./ Handset to Line1 relay control (out) */ -#define AC97_GPIO_LINE1_HOHD 0x0040 /* Opt./ Handset off hook detect Line1 (in) */ -#define AC97_GPIO_LINE12_AC 0x0080 /* Opt./ Int.bit 1 / Line1/2 AC (out) */ -#define AC97_GPIO_LINE12_DC 0x0100 /* Opt./ Int.bit 2 / Line1/2 DC (out) */ -#define AC97_GPIO_LINE12_RS 0x0200 /* Opt./ Int.bit 3 / Line1/2 RS (out) */ -#define AC97_GPIO_LINE2_OH 0x0400 /* Off Hook Line2 */ -#define AC97_GPIO_LINE2_RI 0x0800 /* Ring Detect Line2 */ -#define AC97_GPIO_LINE2_CID 0x1000 /* Caller ID path enable Line2 */ -#define AC97_GPIO_LINE2_LCS 0x2000 /* Loop Current Sense Line2 */ -#define AC97_GPIO_LINE2_PULSE 0x4000 /* Opt./ Pulse Dial Line2 (out) */ -#define AC97_GPIO_LINE2_HL1R 0x8000 /* Opt./ Handset to Line2 relay control (out) */ - /* specific - SigmaTel */ #define AC97_SIGMATEL_OUTSEL 0x64 /* Output Select, STAC9758 */ #define AC97_SIGMATEL_INSEL 0x66 /* Input Select, STAC9758 */ diff --git a/include/sound/core.h b/include/sound/core.h index 4104a9d1001f..5f181b875c2f 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -133,6 +133,7 @@ struct snd_card { struct device card_dev; /* cardX object for sysfs */ const struct attribute_group *dev_groups[4]; /* assigned sysfs attr */ bool registered; /* card_dev is registered? */ + wait_queue_head_t remove_sleep; #ifdef CONFIG_PM unsigned int power_state; /* power state */ @@ -240,6 +241,7 @@ int snd_card_new(struct device *parent, int idx, const char *xid, struct snd_card **card_ret); int snd_card_disconnect(struct snd_card *card); +void snd_card_disconnect_sync(struct snd_card *card); int snd_card_free(struct snd_card *card); int snd_card_free_when_closed(struct snd_card *card); void snd_card_set_id(struct snd_card *card, const char *id); diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index d8afd8a5bd76..68169e3749de 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -112,8 +112,7 @@ void snd_hdac_device_unregister(struct hdac_device *codec); int snd_hdac_device_set_chip_name(struct hdac_device *codec, const char *name); int snd_hdac_codec_modalias(struct hdac_device *hdac, char *buf, size_t size); -int snd_hdac_refresh_widgets(struct hdac_device *codec); -int snd_hdac_refresh_widget_sysfs(struct hdac_device *codec); +int snd_hdac_refresh_widgets(struct hdac_device *codec, bool sysfs); unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid, unsigned int verb, unsigned int parm); diff --git a/include/sound/pxa2xx-lib.h b/include/sound/pxa2xx-lib.h index 5e710d848bd3..63f75450d3db 100644 --- a/include/sound/pxa2xx-lib.h +++ b/include/sound/pxa2xx-lib.h @@ -2,10 +2,13 @@ #ifndef PXA2XX_LIB_H #define PXA2XX_LIB_H +#include <uapi/sound/asound.h> #include <linux/platform_device.h> -#include <sound/ac97_codec.h> /* PCM */ +struct snd_pcm_substream; +struct snd_pcm_hw_params; +struct snd_pcm; extern int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params); @@ -22,12 +25,12 @@ extern void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm); /* AC97 */ -extern unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg); -extern void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val); +extern int pxa2xx_ac97_read(int slot, unsigned short reg); +extern int pxa2xx_ac97_write(int slot, unsigned short reg, unsigned short val); -extern bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97); -extern bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97); -extern void pxa2xx_ac97_finish_reset(struct snd_ac97 *ac97); +extern bool pxa2xx_ac97_try_warm_reset(void); +extern bool pxa2xx_ac97_try_cold_reset(void); +extern void pxa2xx_ac97_finish_reset(void); extern int pxa2xx_ac97_hw_suspend(void); extern int pxa2xx_ac97_hw_resume(void); diff --git a/include/sound/rt5651.h b/include/sound/rt5651.h index d35de758dfb5..18b79a761f10 100644 --- a/include/sound/rt5651.h +++ b/include/sound/rt5651.h @@ -11,11 +11,19 @@ #ifndef __LINUX_SND_RT5651_H #define __LINUX_SND_RT5651_H +enum rt5651_jd_src { + RT5651_JD_NULL, + RT5651_JD1_1, + RT5651_JD1_2, + RT5651_JD2, +}; + struct rt5651_platform_data { /* IN2 can optionally be differential */ bool in2_diff; bool dmic_en; + enum rt5651_jd_src jd_src; }; #endif diff --git a/include/sound/rt5663.h b/include/sound/rt5663.h index 7d00e5849706..7b90a8f1034c 100644 --- a/include/sound/rt5663.h +++ b/include/sound/rt5663.h @@ -16,6 +16,9 @@ struct rt5663_platform_data { unsigned int dc_offset_r_manual; unsigned int dc_offset_l_manual_mic; unsigned int dc_offset_r_manual_mic; + + unsigned int impedance_sensing_num; + unsigned int *impedance_sensing_table; }; #endif diff --git a/include/sound/snd_wavefront.h b/include/sound/snd_wavefront.h index 6231eb57361f..55053557c898 100644 --- a/include/sound/snd_wavefront.h +++ b/include/sound/snd_wavefront.h @@ -29,6 +29,7 @@ struct _snd_wavefront_midi { struct snd_rawmidi_substream *substream_output[2]; struct snd_rawmidi_substream *substream_input[2]; struct timer_list timer; + snd_wavefront_card_t *timer_card; spinlock_t open; spinlock_t virtual; /* protects isvirtual */ }; diff --git a/include/sound/soc-acpi-intel-match.h b/include/sound/soc-acpi-intel-match.h new file mode 100644 index 000000000000..1a9191cd4bb3 --- /dev/null +++ b/include/sound/soc-acpi-intel-match.h @@ -0,0 +1,32 @@ + +/* + * Copyright (C) 2017, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __LINUX_SND_SOC_ACPI_INTEL_MATCH_H +#define __LINUX_SND_SOC_ACPI_INTEL_MATCH_H + +#include <linux/stddef.h> +#include <linux/acpi.h> + +/* + * these tables are not constants, some fields can be used for + * pdata or machine ops + */ +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_haswell_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_broadwell_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_legacy_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[]; + +#endif diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h new file mode 100644 index 000000000000..a7d8d335b043 --- /dev/null +++ b/include/sound/soc-acpi.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2013-15, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __LINUX_SND_SOC_ACPI_H +#define __LINUX_SND_SOC_ACPI_H + +#include <linux/stddef.h> +#include <linux/acpi.h> + +struct snd_soc_acpi_package_context { + char *name; /* package name */ + int length; /* number of elements */ + struct acpi_buffer *format; + struct acpi_buffer *state; + bool data_valid; +}; + +#if IS_ENABLED(CONFIG_ACPI) +/* translation fron HID to I2C name, needed for DAI codec_name */ +const char *snd_soc_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]); +bool snd_soc_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN], + struct snd_soc_acpi_package_context *ctx); +#else +static inline const char * +snd_soc_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]) +{ + return NULL; +} +static inline bool +snd_soc_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN], + struct snd_soc_acpi_package_context *ctx) +{ + return false; +} +#endif + +/* acpi match */ +struct snd_soc_acpi_mach * +snd_soc_acpi_find_machine(struct snd_soc_acpi_mach *machines); + +/* acpi check hid */ +bool snd_soc_acpi_check_hid(const u8 hid[ACPI_ID_LEN]); + +/** + * snd_soc_acpi_mach: ACPI-based machine descriptor. Most of the fields are + * related to the hardware, except for the firmware and topology file names. + * A platform supported by legacy and Sound Open Firmware (SOF) would expose + * all firmware/topology related fields. + * + * @id: ACPI ID (usually the codec's) used to find a matching machine driver. + * @drv_name: machine driver name + * @fw_filename: firmware file name. Used when SOF is not enabled. + * @board: board name + * @machine_quirk: pointer to quirk, usually based on DMI information when + * ACPI ID alone is not sufficient, wrong or misleading + * @quirk_data: data used to uniquely identify a machine, usually a list of + * audio codecs whose presence if checked with ACPI + * @pdata: intended for platform data or machine specific-ops. This structure + * is not constant since this field may be updated at run-time + * @sof_fw_filename: Sound Open Firmware file name, if enabled + * @sof_tplg_filename: Sound Open Firmware topology file name, if enabled + * @asoc_plat_name: ASoC platform name, used for binding machine drivers + * if non NULL + * @new_mach_data: machine driver private data fixup + */ +/* Descriptor for SST ASoC machine driver */ +struct snd_soc_acpi_mach { + const u8 id[ACPI_ID_LEN]; + const char *drv_name; + const char *fw_filename; + const char *board; + struct snd_soc_acpi_mach * (*machine_quirk)(void *arg); + const void *quirk_data; + void *pdata; + const char *sof_fw_filename; + const char *sof_tplg_filename; + const char *asoc_plat_name; + struct platform_device * (*new_mach_data)(void *pdata); +}; + +#define SND_SOC_ACPI_MAX_CODECS 3 + +/** + * struct snd_soc_acpi_codecs: Structure to hold secondary codec information + * apart from the matched one, this data will be passed to the quirk function + * to match with the ACPI detected devices + * + * @num_codecs: number of secondary codecs used in the platform + * @codecs: holds the codec IDs + * + */ +struct snd_soc_acpi_codecs { + int num_codecs; + u8 codecs[SND_SOC_ACPI_MAX_CODECS][ACPI_ID_LEN]; +}; + +/* check all codecs */ +struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg); + +#endif diff --git a/include/sound/soc.h b/include/sound/soc.h index d22de9712c45..1a7323238c49 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -468,6 +468,11 @@ int snd_soc_register_codec(struct device *dev, const struct snd_soc_codec_driver *codec_drv, struct snd_soc_dai_driver *dai_drv, int num_dai); void snd_soc_unregister_codec(struct device *dev); +int snd_soc_add_component(struct device *dev, + struct snd_soc_component *component, + const struct snd_soc_component_driver *component_driver, + struct snd_soc_dai_driver *dai_drv, + int num_dai); int snd_soc_register_component(struct device *dev, const struct snd_soc_component_driver *component_driver, struct snd_soc_dai_driver *dai_drv, int num_dai); @@ -475,6 +480,8 @@ int devm_snd_soc_register_component(struct device *dev, const struct snd_soc_component_driver *component_driver, struct snd_soc_dai_driver *dai_drv, int num_dai); void snd_soc_unregister_component(struct device *dev); +struct snd_soc_component *snd_soc_lookup_component(struct device *dev, + const char *driver_name); int snd_soc_cache_init(struct snd_soc_codec *codec); int snd_soc_cache_exit(struct snd_soc_codec *codec); @@ -795,6 +802,10 @@ struct snd_soc_component_driver { int (*suspend)(struct snd_soc_component *); int (*resume)(struct snd_soc_component *); + /* pcm creation and destruction */ + int (*pcm_new)(struct snd_soc_pcm_runtime *); + void (*pcm_free)(struct snd_pcm *); + /* component wide operations */ int (*set_sysclk)(struct snd_soc_component *component, int clk_id, int source, unsigned int freq, int dir); @@ -812,10 +823,22 @@ struct snd_soc_component_driver { void (*seq_notifier)(struct snd_soc_component *, enum snd_soc_dapm_type, int subseq); int (*stream_event)(struct snd_soc_component *, int event); + int (*set_bias_level)(struct snd_soc_component *component, + enum snd_soc_bias_level level); + + const struct snd_pcm_ops *ops; + const struct snd_compr_ops *compr_ops; /* probe ordering - for components with runtime dependencies */ int probe_order; int remove_order; + + /* bits */ + unsigned int idle_bias_on:1; + unsigned int suspend_bias_off:1; + unsigned int pmdown_time:1; /* care pmdown_time at stop */ + unsigned int endianness:1; + unsigned int non_legacy_dai_naming:1; }; struct snd_soc_component { @@ -872,6 +895,8 @@ struct snd_soc_component { void (*remove)(struct snd_soc_component *); int (*suspend)(struct snd_soc_component *); int (*resume)(struct snd_soc_component *); + int (*pcm_new)(struct snd_soc_component *, struct snd_soc_pcm_runtime *); + void (*pcm_free)(struct snd_soc_component *, struct snd_pcm *); int (*set_sysclk)(struct snd_soc_component *component, int clk_id, int source, unsigned int freq, int dir); @@ -879,6 +904,8 @@ struct snd_soc_component { int source, unsigned int freq_in, unsigned int freq_out); int (*set_jack)(struct snd_soc_component *component, struct snd_soc_jack *jack, void *data); + int (*set_bias_level)(struct snd_soc_component *component, + enum snd_soc_bias_level level); /* machine specific init */ int (*init)(struct snd_soc_component *component); @@ -1413,6 +1440,21 @@ static inline void snd_soc_codec_init_bias_level(struct snd_soc_codec *codec, } /** + * snd_soc_component_init_bias_level() - Initialize COMPONENT DAPM bias level + * @component: The COMPONENT for which to initialize the DAPM bias level + * @level: The DAPM level to initialize to + * + * Initializes the COMPONENT DAPM bias level. See snd_soc_dapm_init_bias_level(). + */ +static inline void +snd_soc_component_init_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + snd_soc_dapm_init_bias_level( + snd_soc_component_get_dapm(component), level); +} + +/** * snd_soc_dapm_get_bias_level() - Get current CODEC DAPM bias level * @codec: The CODEC for which to get the DAPM bias level * @@ -1425,6 +1467,19 @@ static inline enum snd_soc_bias_level snd_soc_codec_get_bias_level( } /** + * snd_soc_component_get_bias_level() - Get current COMPONENT DAPM bias level + * @component: The COMPONENT for which to get the DAPM bias level + * + * Returns: The current DAPM bias level of the COMPONENT. + */ +static inline enum snd_soc_bias_level +snd_soc_component_get_bias_level(struct snd_soc_component *component) +{ + return snd_soc_dapm_get_bias_level( + snd_soc_component_get_dapm(component)); +} + +/** * snd_soc_codec_force_bias_level() - Set the CODEC DAPM bias level * @codec: The CODEC for which to set the level * @level: The level to set to @@ -1440,6 +1495,23 @@ static inline int snd_soc_codec_force_bias_level(struct snd_soc_codec *codec, } /** + * snd_soc_component_force_bias_level() - Set the COMPONENT DAPM bias level + * @component: The COMPONENT for which to set the level + * @level: The level to set to + * + * Forces the COMPONENT bias level to a specific state. See + * snd_soc_dapm_force_bias_level(). + */ +static inline int +snd_soc_component_force_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + return snd_soc_dapm_force_bias_level( + snd_soc_component_get_dapm(component), + level); +} + +/** * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol * @kcontrol: The kcontrol * @@ -1452,6 +1524,19 @@ static inline struct snd_soc_codec *snd_soc_dapm_kcontrol_codec( return snd_soc_dapm_to_codec(snd_soc_dapm_kcontrol_dapm(kcontrol)); } +/** + * snd_soc_dapm_kcontrol_component() - Returns the component associated to a kcontrol + * @kcontrol: The kcontrol + * + * This function must only be used on DAPM contexts that are known to be part of + * a COMPONENT (e.g. in a COMPONENT driver). Otherwise the behavior is undefined. + */ +static inline struct snd_soc_component *snd_soc_dapm_kcontrol_component( + struct snd_kcontrol *kcontrol) +{ + return snd_soc_dapm_to_component(snd_soc_dapm_kcontrol_dapm(kcontrol)); +} + /* codec IO */ unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg); int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg, @@ -1468,9 +1553,23 @@ static inline int snd_soc_cache_sync(struct snd_soc_codec *codec) return regcache_sync(codec->component.regmap); } +/** + * snd_soc_component_cache_sync() - Sync the register cache with the hardware + * @component: COMPONENT to sync + * + * Note: This function will call regcache_sync() + */ +static inline int snd_soc_component_cache_sync( + struct snd_soc_component *component) +{ + return regcache_sync(component->regmap); +} + /* component IO */ int snd_soc_component_read(struct snd_soc_component *component, unsigned int reg, unsigned int *val); +unsigned int snd_soc_component_read32(struct snd_soc_component *component, + unsigned int reg); int snd_soc_component_write(struct snd_soc_component *component, unsigned int reg, unsigned int val); int snd_soc_component_update_bits(struct snd_soc_component *component, @@ -1487,6 +1586,8 @@ int snd_soc_component_set_sysclk(struct snd_soc_component *component, int snd_soc_component_set_pll(struct snd_soc_component *component, int pll_id, int source, unsigned int freq_in, unsigned int freq_out); +int snd_soc_component_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *data); #ifdef CONFIG_REGMAP @@ -1720,6 +1821,20 @@ struct snd_soc_dai *snd_soc_find_dai( #include <sound/soc-dai.h> +static inline +struct snd_soc_dai *snd_soc_card_get_codec_dai(struct snd_soc_card *card, + const char *dai_name) +{ + struct snd_soc_pcm_runtime *rtd; + + list_for_each_entry(rtd, &card->rtd_list, list) { + if (!strcmp(rtd->codec_dai->name, dai_name)) + return rtd->codec_dai; + } + + return NULL; +} + #ifdef CONFIG_DEBUG_FS extern struct dentry *snd_soc_debugfs_root; #endif diff --git a/sound/Kconfig b/sound/Kconfig index d7d2aac9542e..6833db9002ec 100644 --- a/sound/Kconfig +++ b/sound/Kconfig @@ -3,25 +3,7 @@ menuconfig SOUND depends on HAS_IOMEM help If you have a sound card in your computer, i.e. if it can say more - than an occasional beep, say Y. Be sure to have all the information - about your sound card and its configuration down (I/O port, - interrupt and DMA channel), because you will be asked for it. - - You want to read the Sound-HOWTO, available from - <http://www.tldp.org/docs.html#howto>. General information about - the modular sound system is contained in the files - <file:Documentation/sound/oss/Introduction>. The file - <file:Documentation/sound/oss/README.OSS> contains some slightly - outdated but still useful information as well. Newer sound - driver documentation is found in <file:Documentation/sound/alsa/*>. - - If you have a PnP sound card and you want to configure it at boot - time using the ISA PnP tools (read - <http://www.roestock.demon.co.uk/isapnptools/>), then you need to - compile the sound card support as a module and load that module - after the PnP configuration is finished. To do this, choose M here - and read <file:Documentation/sound/oss/README.modules>; the module - will be called soundcore. + than an occasional beep, say Y. if SOUND @@ -80,6 +62,8 @@ source "sound/hda/Kconfig" source "sound/ppc/Kconfig" +source "sound/ac97/Kconfig" + source "sound/aoa/Kconfig" source "sound/arm/Kconfig" @@ -114,19 +98,6 @@ source "sound/synth/Kconfig" endif # SND -menuconfig SOUND_PRIME - tristate "Open Sound System (DEPRECATED)" - select SOUND_OSS_CORE - depends on BROKEN - help - Say 'Y' or 'M' to enable Open Sound System drivers. - -if SOUND_PRIME - -source "sound/oss/Kconfig" - -endif # SOUND_PRIME - endif # !UML endif # SOUND diff --git a/sound/Makefile b/sound/Makefile index f2d1d093bcdc..99d8c31262c8 100644 --- a/sound/Makefile +++ b/sound/Makefile @@ -3,14 +3,14 @@ # obj-$(CONFIG_SOUND) += soundcore.o -obj-$(CONFIG_SOUND_PRIME) += oss/ -obj-$(CONFIG_DMASOUND) += oss/ +obj-$(CONFIG_DMASOUND) += oss/dmasound/ obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \ firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/ obj-$(CONFIG_SND_AOA) += aoa/ # This one must be compilable even if sound is configured out obj-$(CONFIG_AC97_BUS) += ac97_bus.o +obj-$(CONFIG_AC97_BUS_NEW) += ac97/ ifeq ($(CONFIG_SND),y) obj-y += last.o diff --git a/sound/ac97/Kconfig b/sound/ac97/Kconfig new file mode 100644 index 000000000000..f8a64e15e5bf --- /dev/null +++ b/sound/ac97/Kconfig @@ -0,0 +1,19 @@ +# +# AC97 configuration +# + + +config AC97_BUS_NEW + tristate + select AC97 + help + This is the new AC97 bus type, successor of AC97_BUS. The ported + drivers which benefit from the AC97 automatic probing should "select" + this instead of the AC97_BUS. + Say Y here if you want to have AC97 devices, which are sound oriented + devices around an AC-Link. + +config AC97_BUS_COMPAT + bool + depends on AC97_BUS_NEW + depends on !AC97_BUS diff --git a/sound/ac97/Makefile b/sound/ac97/Makefile new file mode 100644 index 000000000000..f9c2640bfb59 --- /dev/null +++ b/sound/ac97/Makefile @@ -0,0 +1,8 @@ +# +# make for AC97 bus drivers +# + +obj-$(CONFIG_AC97_BUS_NEW) += ac97.o + +ac97-y += bus.o codec.o +ac97-$(CONFIG_AC97_BUS_COMPAT) += snd_ac97_compat.o diff --git a/sound/ac97/ac97_core.h b/sound/ac97/ac97_core.h new file mode 100644 index 000000000000..08441a4fda7c --- /dev/null +++ b/sound/ac97/ac97_core.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +unsigned int snd_ac97_bus_scan_one(struct ac97_controller *ac97, + unsigned int codec_num); + +static inline bool ac97_ids_match(unsigned int id1, unsigned int id2, + unsigned int mask) +{ + return (id1 & mask) == (id2 & mask); +} diff --git a/sound/ac97/bus.c b/sound/ac97/bus.c new file mode 100644 index 000000000000..31f858eceffc --- /dev/null +++ b/sound/ac97/bus.c @@ -0,0 +1,539 @@ +/* + * Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/idr.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <sound/ac97/codec.h> +#include <sound/ac97/controller.h> +#include <sound/ac97/regs.h> + +#include "ac97_core.h" + +/* + * Protects ac97_controllers and each ac97_controller structure. + */ +static DEFINE_MUTEX(ac97_controllers_mutex); +static DEFINE_IDR(ac97_adapter_idr); +static LIST_HEAD(ac97_controllers); + +static struct bus_type ac97_bus_type; + +static inline struct ac97_controller* +to_ac97_controller(struct device *ac97_adapter) +{ + return container_of(ac97_adapter, struct ac97_controller, adap); +} + +static int ac97_unbound_ctrl_write(struct ac97_controller *adrv, int slot, + unsigned short reg, unsigned short val) +{ + return -ENODEV; +} + +static int ac97_unbound_ctrl_read(struct ac97_controller *adrv, int slot, + unsigned short reg) +{ + return -ENODEV; +} + +static const struct ac97_controller_ops ac97_unbound_ctrl_ops = { + .write = ac97_unbound_ctrl_write, + .read = ac97_unbound_ctrl_read, +}; + +static struct ac97_controller ac97_unbound_ctrl = { + .ops = &ac97_unbound_ctrl_ops, +}; + +static struct ac97_codec_device * +ac97_codec_find(struct ac97_controller *ac97_ctrl, unsigned int codec_num) +{ + if (codec_num >= AC97_BUS_MAX_CODECS) + return ERR_PTR(-EINVAL); + + return ac97_ctrl->codecs[codec_num]; +} + +static void ac97_codec_release(struct device *dev) +{ + struct ac97_codec_device *adev; + struct ac97_controller *ac97_ctrl; + + adev = to_ac97_device(dev); + ac97_ctrl = adev->ac97_ctrl; + ac97_ctrl->codecs[adev->num] = NULL; + kfree(adev); +} + +static int ac97_codec_add(struct ac97_controller *ac97_ctrl, int idx, + unsigned int vendor_id) +{ + struct ac97_codec_device *codec; + int ret; + + codec = kzalloc(sizeof(*codec), GFP_KERNEL); + if (!codec) + return -ENOMEM; + ac97_ctrl->codecs[idx] = codec; + codec->vendor_id = vendor_id; + codec->dev.release = ac97_codec_release; + codec->dev.bus = &ac97_bus_type; + codec->dev.parent = &ac97_ctrl->adap; + codec->num = idx; + codec->ac97_ctrl = ac97_ctrl; + + device_initialize(&codec->dev); + dev_set_name(&codec->dev, "%s:%u", dev_name(ac97_ctrl->parent), idx); + + ret = device_add(&codec->dev); + if (ret) + goto err_free_codec; + + return 0; +err_free_codec: + put_device(&codec->dev); + kfree(codec); + ac97_ctrl->codecs[idx] = NULL; + + return ret; +} + +unsigned int snd_ac97_bus_scan_one(struct ac97_controller *adrv, + unsigned int codec_num) +{ + unsigned short vid1, vid2; + int ret; + + ret = adrv->ops->read(adrv, codec_num, AC97_VENDOR_ID1); + vid1 = (ret & 0xffff); + if (ret < 0) + return 0; + + ret = adrv->ops->read(adrv, codec_num, AC97_VENDOR_ID2); + vid2 = (ret & 0xffff); + if (ret < 0) + return 0; + + dev_dbg(&adrv->adap, "%s(codec_num=%u): vendor_id=0x%08x\n", + __func__, codec_num, AC97_ID(vid1, vid2)); + return AC97_ID(vid1, vid2); +} + +static int ac97_bus_scan(struct ac97_controller *ac97_ctrl) +{ + int ret, i; + unsigned int vendor_id; + + for (i = 0; i < AC97_BUS_MAX_CODECS; i++) { + if (ac97_codec_find(ac97_ctrl, i)) + continue; + if (!(ac97_ctrl->slots_available & BIT(i))) + continue; + vendor_id = snd_ac97_bus_scan_one(ac97_ctrl, i); + if (!vendor_id) + continue; + + ret = ac97_codec_add(ac97_ctrl, i, vendor_id); + if (ret < 0) + return ret; + } + return 0; +} + +static int ac97_bus_reset(struct ac97_controller *ac97_ctrl) +{ + ac97_ctrl->ops->reset(ac97_ctrl); + + return 0; +} + +/** + * snd_ac97_codec_driver_register - register an AC97 codec driver + * @dev: AC97 driver codec to register + * + * Register an AC97 codec driver to the ac97 bus driver, aka. the AC97 digital + * controller. + * + * Returns 0 on success or error code + */ +int snd_ac97_codec_driver_register(struct ac97_codec_driver *drv) +{ + drv->driver.bus = &ac97_bus_type; + return driver_register(&drv->driver); +} +EXPORT_SYMBOL_GPL(snd_ac97_codec_driver_register); + +/** + * snd_ac97_codec_driver_unregister - unregister an AC97 codec driver + * @dev: AC97 codec driver to unregister + * + * Unregister a previously registered ac97 codec driver. + */ +void snd_ac97_codec_driver_unregister(struct ac97_codec_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL_GPL(snd_ac97_codec_driver_unregister); + +/** + * snd_ac97_codec_get_platdata - get platform_data + * @adev: the ac97 codec device + * + * For legacy platforms, in order to have platform_data in codec drivers + * available, while ac97 device are auto-created upon probe, this retrieves the + * platdata which was setup on ac97 controller registration. + * + * Returns the platform data pointer + */ +void *snd_ac97_codec_get_platdata(const struct ac97_codec_device *adev) +{ + struct ac97_controller *ac97_ctrl = adev->ac97_ctrl; + + return ac97_ctrl->codecs_pdata[adev->num]; +} +EXPORT_SYMBOL_GPL(snd_ac97_codec_get_platdata); + +static void ac97_ctrl_codecs_unregister(struct ac97_controller *ac97_ctrl) +{ + int i; + + for (i = 0; i < AC97_BUS_MAX_CODECS; i++) + if (ac97_ctrl->codecs[i]) { + ac97_ctrl->codecs[i]->ac97_ctrl = &ac97_unbound_ctrl; + device_unregister(&ac97_ctrl->codecs[i]->dev); + } +} + +static ssize_t cold_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t len) +{ + struct ac97_controller *ac97_ctrl; + + mutex_lock(&ac97_controllers_mutex); + ac97_ctrl = to_ac97_controller(dev); + ac97_ctrl->ops->reset(ac97_ctrl); + mutex_unlock(&ac97_controllers_mutex); + return len; +} +static DEVICE_ATTR_WO(cold_reset); + +static ssize_t warm_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t len) +{ + struct ac97_controller *ac97_ctrl; + + if (!dev) + return -ENODEV; + + mutex_lock(&ac97_controllers_mutex); + ac97_ctrl = to_ac97_controller(dev); + ac97_ctrl->ops->warm_reset(ac97_ctrl); + mutex_unlock(&ac97_controllers_mutex); + return len; +} +static DEVICE_ATTR_WO(warm_reset); + +static struct attribute *ac97_controller_device_attrs[] = { + &dev_attr_cold_reset.attr, + &dev_attr_warm_reset.attr, + NULL +}; + +static struct attribute_group ac97_adapter_attr_group = { + .name = "ac97_operations", + .attrs = ac97_controller_device_attrs, +}; + +static const struct attribute_group *ac97_adapter_groups[] = { + &ac97_adapter_attr_group, + NULL, +}; + +static void ac97_del_adapter(struct ac97_controller *ac97_ctrl) +{ + mutex_lock(&ac97_controllers_mutex); + ac97_ctrl_codecs_unregister(ac97_ctrl); + list_del(&ac97_ctrl->controllers); + mutex_unlock(&ac97_controllers_mutex); + + device_unregister(&ac97_ctrl->adap); +} + +static void ac97_adapter_release(struct device *dev) +{ + struct ac97_controller *ac97_ctrl; + + ac97_ctrl = to_ac97_controller(dev); + idr_remove(&ac97_adapter_idr, ac97_ctrl->nr); + dev_dbg(&ac97_ctrl->adap, "adapter unregistered by %s\n", + dev_name(ac97_ctrl->parent)); +} + +static const struct device_type ac97_adapter_type = { + .groups = ac97_adapter_groups, + .release = ac97_adapter_release, +}; + +static int ac97_add_adapter(struct ac97_controller *ac97_ctrl) +{ + int ret; + + mutex_lock(&ac97_controllers_mutex); + ret = idr_alloc(&ac97_adapter_idr, ac97_ctrl, 0, 0, GFP_KERNEL); + ac97_ctrl->nr = ret; + if (ret >= 0) { + dev_set_name(&ac97_ctrl->adap, "ac97-%d", ret); + ac97_ctrl->adap.type = &ac97_adapter_type; + ac97_ctrl->adap.parent = ac97_ctrl->parent; + ret = device_register(&ac97_ctrl->adap); + if (ret) + put_device(&ac97_ctrl->adap); + } + if (!ret) + list_add(&ac97_ctrl->controllers, &ac97_controllers); + mutex_unlock(&ac97_controllers_mutex); + + if (!ret) + dev_dbg(&ac97_ctrl->adap, "adapter registered by %s\n", + dev_name(ac97_ctrl->parent)); + return ret; +} + +/** + * snd_ac97_controller_register - register an ac97 controller + * @ops: the ac97 bus operations + * @dev: the device providing the ac97 DC function + * @slots_available: mask of the ac97 codecs that can be scanned and probed + * bit0 => codec 0, bit1 => codec 1 ... bit 3 => codec 3 + * + * Register a digital controller which can control up to 4 ac97 codecs. This is + * the controller side of the AC97 AC-link, while the slave side are the codecs. + * + * Returns a valid controller upon success, negative pointer value upon error + */ +struct ac97_controller *snd_ac97_controller_register( + const struct ac97_controller_ops *ops, struct device *dev, + unsigned short slots_available, void **codecs_pdata) +{ + struct ac97_controller *ac97_ctrl; + int ret, i; + + ac97_ctrl = kzalloc(sizeof(*ac97_ctrl), GFP_KERNEL); + if (!ac97_ctrl) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < AC97_BUS_MAX_CODECS && codecs_pdata; i++) + ac97_ctrl->codecs_pdata[i] = codecs_pdata[i]; + + ac97_ctrl->ops = ops; + ac97_ctrl->slots_available = slots_available; + ac97_ctrl->parent = dev; + ret = ac97_add_adapter(ac97_ctrl); + + if (ret) + goto err; + ac97_bus_reset(ac97_ctrl); + ac97_bus_scan(ac97_ctrl); + + return ac97_ctrl; +err: + kfree(ac97_ctrl); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(snd_ac97_controller_register); + +/** + * snd_ac97_controller_unregister - unregister an ac97 controller + * @ac97_ctrl: the device previously provided to ac97_controller_register() + * + */ +void snd_ac97_controller_unregister(struct ac97_controller *ac97_ctrl) +{ + ac97_del_adapter(ac97_ctrl); +} +EXPORT_SYMBOL_GPL(snd_ac97_controller_unregister); + +#ifdef CONFIG_PM +static int ac97_pm_runtime_suspend(struct device *dev) +{ + struct ac97_codec_device *codec = to_ac97_device(dev); + int ret = pm_generic_runtime_suspend(dev); + + if (ret == 0 && dev->driver) { + if (pm_runtime_is_irq_safe(dev)) + clk_disable(codec->clk); + else + clk_disable_unprepare(codec->clk); + } + + return ret; +} + +static int ac97_pm_runtime_resume(struct device *dev) +{ + struct ac97_codec_device *codec = to_ac97_device(dev); + int ret; + + if (dev->driver) { + if (pm_runtime_is_irq_safe(dev)) + ret = clk_enable(codec->clk); + else + ret = clk_prepare_enable(codec->clk); + if (ret) + return ret; + } + + return pm_generic_runtime_resume(dev); +} +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops ac97_pm = { + .suspend = pm_generic_suspend, + .resume = pm_generic_resume, + .freeze = pm_generic_freeze, + .thaw = pm_generic_thaw, + .poweroff = pm_generic_poweroff, + .restore = pm_generic_restore, + SET_RUNTIME_PM_OPS( + ac97_pm_runtime_suspend, + ac97_pm_runtime_resume, + NULL) +}; + +static int ac97_get_enable_clk(struct ac97_codec_device *adev) +{ + int ret; + + adev->clk = clk_get(&adev->dev, "ac97_clk"); + if (IS_ERR(adev->clk)) + return PTR_ERR(adev->clk); + + ret = clk_prepare_enable(adev->clk); + if (ret) + clk_put(adev->clk); + + return ret; +} + +static void ac97_put_disable_clk(struct ac97_codec_device *adev) +{ + clk_disable_unprepare(adev->clk); + clk_put(adev->clk); +} + +static ssize_t vendor_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ac97_codec_device *codec = to_ac97_device(dev); + + return sprintf(buf, "%08x", codec->vendor_id); +} +DEVICE_ATTR_RO(vendor_id); + +static struct attribute *ac97_dev_attrs[] = { + &dev_attr_vendor_id.attr, + NULL, +}; +ATTRIBUTE_GROUPS(ac97_dev); + +static int ac97_bus_match(struct device *dev, struct device_driver *drv) +{ + struct ac97_codec_device *adev = to_ac97_device(dev); + struct ac97_codec_driver *adrv = to_ac97_driver(drv); + const struct ac97_id *id = adrv->id_table; + int i = 0; + + if (adev->vendor_id == 0x0 || adev->vendor_id == 0xffffffff) + return false; + + do { + if (ac97_ids_match(id[i].id, adev->vendor_id, id[i].mask)) + return true; + } while (id[i++].id); + + return false; +} + +static int ac97_bus_probe(struct device *dev) +{ + struct ac97_codec_device *adev = to_ac97_device(dev); + struct ac97_codec_driver *adrv = to_ac97_driver(dev->driver); + int ret; + + ret = ac97_get_enable_clk(adev); + if (ret) + return ret; + + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + ret = adrv->probe(adev); + if (ret == 0) + return 0; + + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_put_noidle(dev); + ac97_put_disable_clk(adev); + + return ret; +} + +static int ac97_bus_remove(struct device *dev) +{ + struct ac97_codec_device *adev = to_ac97_device(dev); + struct ac97_codec_driver *adrv = to_ac97_driver(dev->driver); + int ret; + + ret = pm_runtime_get_sync(dev); + if (ret) + return ret; + + ret = adrv->remove(adev); + pm_runtime_put_noidle(dev); + if (ret == 0) + ac97_put_disable_clk(adev); + + return ret; +} + +static struct bus_type ac97_bus_type = { + .name = "ac97bus", + .dev_groups = ac97_dev_groups, + .match = ac97_bus_match, + .pm = &ac97_pm, + .probe = ac97_bus_probe, + .remove = ac97_bus_remove, +}; + +static int __init ac97_bus_init(void) +{ + return bus_register(&ac97_bus_type); +} +subsys_initcall(ac97_bus_init); + +static void __exit ac97_bus_exit(void) +{ + bus_unregister(&ac97_bus_type); +} +module_exit(ac97_bus_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>"); diff --git a/sound/ac97/codec.c b/sound/ac97/codec.c new file mode 100644 index 000000000000..a835f03744bf --- /dev/null +++ b/sound/ac97/codec.c @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <sound/ac97_codec.h> +#include <sound/ac97/codec.h> +#include <sound/ac97/controller.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <sound/soc.h> /* For compat_ac97_* */ + diff --git a/sound/ac97/snd_ac97_compat.c b/sound/ac97/snd_ac97_compat.c new file mode 100644 index 000000000000..61544e0d8de4 --- /dev/null +++ b/sound/ac97/snd_ac97_compat.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2016 Robert Jarzmik <robert.jarzmik@free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/list.h> +#include <linux/slab.h> +#include <sound/ac97/codec.h> +#include <sound/ac97/compat.h> +#include <sound/ac97/controller.h> +#include <sound/soc.h> + +#include "ac97_core.h" + +static void compat_ac97_reset(struct snd_ac97 *ac97) +{ + struct ac97_codec_device *adev = to_ac97_device(ac97->private_data); + struct ac97_controller *actrl = adev->ac97_ctrl; + + if (actrl->ops->reset) + actrl->ops->reset(actrl); +} + +static void compat_ac97_warm_reset(struct snd_ac97 *ac97) +{ + struct ac97_codec_device *adev = to_ac97_device(ac97->private_data); + struct ac97_controller *actrl = adev->ac97_ctrl; + + if (actrl->ops->warm_reset) + actrl->ops->warm_reset(actrl); +} + +static void compat_ac97_write(struct snd_ac97 *ac97, unsigned short reg, + unsigned short val) +{ + struct ac97_codec_device *adev = to_ac97_device(ac97->private_data); + struct ac97_controller *actrl = adev->ac97_ctrl; + + actrl->ops->write(actrl, ac97->num, reg, val); +} + +static unsigned short compat_ac97_read(struct snd_ac97 *ac97, + unsigned short reg) +{ + struct ac97_codec_device *adev = to_ac97_device(ac97->private_data); + struct ac97_controller *actrl = adev->ac97_ctrl; + + return actrl->ops->read(actrl, ac97->num, reg); +} + +static struct snd_ac97_bus_ops compat_snd_ac97_bus_ops = { + .reset = compat_ac97_reset, + .warm_reset = compat_ac97_warm_reset, + .write = compat_ac97_write, + .read = compat_ac97_read, +}; + +static struct snd_ac97_bus compat_soc_ac97_bus = { + .ops = &compat_snd_ac97_bus_ops, +}; + +struct snd_ac97 *snd_ac97_compat_alloc(struct ac97_codec_device *adev) +{ + struct snd_ac97 *ac97; + + ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL); + if (ac97 == NULL) + return ERR_PTR(-ENOMEM); + + ac97->dev = adev->dev; + ac97->private_data = adev; + ac97->bus = &compat_soc_ac97_bus; + return ac97; +} +EXPORT_SYMBOL_GPL(snd_ac97_compat_alloc); + +void snd_ac97_compat_release(struct snd_ac97 *ac97) +{ + kfree(ac97); +} +EXPORT_SYMBOL_GPL(snd_ac97_compat_release); + +int snd_ac97_reset(struct snd_ac97 *ac97, bool try_warm, unsigned int id, + unsigned int id_mask) +{ + struct ac97_codec_device *adev = to_ac97_device(ac97->private_data); + struct ac97_controller *actrl = adev->ac97_ctrl; + unsigned int scanned; + + if (try_warm) { + compat_ac97_warm_reset(ac97); + scanned = snd_ac97_bus_scan_one(actrl, adev->num); + if (ac97_ids_match(scanned, adev->vendor_id, id_mask)) + return 1; + } + + compat_ac97_reset(ac97); + compat_ac97_warm_reset(ac97); + scanned = snd_ac97_bus_scan_one(actrl, adev->num); + if (ac97_ids_match(scanned, adev->vendor_id, id_mask)) + return 0; + + return -ENODEV; +} +EXPORT_SYMBOL_GPL(snd_ac97_reset); diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c index 39c3969ac1c7..5950a9e218d9 100644 --- a/sound/arm/pxa2xx-ac97-lib.c +++ b/sound/arm/pxa2xx-ac97-lib.c @@ -20,7 +20,6 @@ #include <linux/io.h> #include <linux/gpio.h> -#include <sound/ac97_codec.h> #include <sound/pxa2xx-lib.h> #include <mach/irqs.h> @@ -46,38 +45,41 @@ extern void pxa27x_configure_ac97reset(int reset_gpio, bool to_gpio); * 1 jiffy timeout if interrupt never comes). */ -unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg) +int pxa2xx_ac97_read(int slot, unsigned short reg) { - unsigned short val = -1; + int val = -ENODEV; volatile u32 *reg_addr; + if (slot > 0) + return -ENODEV; + mutex_lock(&car_mutex); /* set up primary or secondary codec space */ if (cpu_is_pxa25x() && reg == AC97_GPIO_STATUS) - reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE; + reg_addr = slot ? &SMC_REG_BASE : &PMC_REG_BASE; else - reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE; + reg_addr = slot ? &SAC_REG_BASE : &PAC_REG_BASE; reg_addr += (reg >> 1); /* start read access across the ac97 link */ GSR = GSR_CDONE | GSR_SDONE; gsr_bits = 0; - val = *reg_addr; + val = (*reg_addr & 0xffff); if (reg == AC97_GPIO_STATUS) goto out; if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1) <= 0 && !((GSR | gsr_bits) & GSR_SDONE)) { printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n", __func__, reg, GSR | gsr_bits); - val = -1; + val = -ETIMEDOUT; goto out; } /* valid data now */ GSR = GSR_CDONE | GSR_SDONE; gsr_bits = 0; - val = *reg_addr; + val = (*reg_addr & 0xffff); /* but we've just started another cycle... */ wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1); @@ -86,29 +88,32 @@ out: mutex_unlock(&car_mutex); } EXPORT_SYMBOL_GPL(pxa2xx_ac97_read); -void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, - unsigned short val) +int pxa2xx_ac97_write(int slot, unsigned short reg, unsigned short val) { volatile u32 *reg_addr; + int ret = 0; mutex_lock(&car_mutex); /* set up primary or secondary codec space */ if (cpu_is_pxa25x() && reg == AC97_GPIO_STATUS) - reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE; + reg_addr = slot ? &SMC_REG_BASE : &PMC_REG_BASE; else - reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE; + reg_addr = slot ? &SAC_REG_BASE : &PAC_REG_BASE; reg_addr += (reg >> 1); GSR = GSR_CDONE | GSR_SDONE; gsr_bits = 0; *reg_addr = val; if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1) <= 0 && - !((GSR | gsr_bits) & GSR_CDONE)) + !((GSR | gsr_bits) & GSR_CDONE)) { printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n", __func__, reg, GSR | gsr_bits); + ret = -EIO; + } mutex_unlock(&car_mutex); + return ret; } EXPORT_SYMBOL_GPL(pxa2xx_ac97_write); @@ -188,7 +193,7 @@ static inline void pxa_ac97_cold_pxa3xx(void) } #endif -bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97) +bool pxa2xx_ac97_try_warm_reset(void) { unsigned long gsr; unsigned int timeout = 100; @@ -225,7 +230,7 @@ bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97) } EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_warm_reset); -bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97) +bool pxa2xx_ac97_try_cold_reset(void) { unsigned long gsr; unsigned int timeout = 1000; @@ -263,7 +268,7 @@ bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97) EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_cold_reset); -void pxa2xx_ac97_finish_reset(struct snd_ac97 *ac97) +void pxa2xx_ac97_finish_reset(void) { GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); GCR |= GCR_SDONE_IE|GCR_CDONE_IE; diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c index fbd5dad0c484..4bc244c40f80 100644 --- a/sound/arm/pxa2xx-ac97.c +++ b/sound/arm/pxa2xx-ac97.c @@ -29,19 +29,38 @@ #include "pxa2xx-pcm.h" -static void pxa2xx_ac97_reset(struct snd_ac97 *ac97) +static void pxa2xx_ac97_legacy_reset(struct snd_ac97 *ac97) { - if (!pxa2xx_ac97_try_cold_reset(ac97)) { - pxa2xx_ac97_try_warm_reset(ac97); - } + if (!pxa2xx_ac97_try_cold_reset()) + pxa2xx_ac97_try_warm_reset(); + + pxa2xx_ac97_finish_reset(); +} + +static unsigned short pxa2xx_ac97_legacy_read(struct snd_ac97 *ac97, + unsigned short reg) +{ + int ret; + + ret = pxa2xx_ac97_read(ac97->num, reg); + if (ret < 0) + return 0; + else + return (unsigned short)(ret & 0xffff); +} + +static void pxa2xx_ac97_legacy_write(struct snd_ac97 *ac97, + unsigned short reg, unsigned short val) +{ + int __always_unused ret; - pxa2xx_ac97_finish_reset(ac97); + ret = pxa2xx_ac97_write(ac97->num, reg, val); } static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { - .read = pxa2xx_ac97_read, - .write = pxa2xx_ac97_write, - .reset = pxa2xx_ac97_reset, + .read = pxa2xx_ac97_legacy_read, + .write = pxa2xx_ac97_legacy_write, + .reset = pxa2xx_ac97_legacy_reset, }; static struct pxad_param pxa2xx_ac97_pcm_out_req = { diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c index 6e47b823bcaa..18cb6f476bf4 100644 --- a/sound/core/hrtimer.c +++ b/sound/core/hrtimer.c @@ -127,7 +127,7 @@ static int snd_hrtimer_stop(struct snd_timer *t) return 0; } -static struct snd_timer_hardware hrtimer_hw = { +static const struct snd_timer_hardware hrtimer_hw __initconst = { .flags = SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_TASKLET, .open = snd_hrtimer_open, .close = snd_hrtimer_close, diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c index a73baa1242be..8faae3d1455d 100644 --- a/sound/core/hwdep.c +++ b/sound/core/hwdep.c @@ -228,6 +228,8 @@ static int snd_hwdep_dsp_load(struct snd_hwdep *hw, memset(&info, 0, sizeof(info)); if (copy_from_user(&info, _info, sizeof(info))) return -EFAULT; + if (info.index >= 32) + return -EINVAL; /* check whether the dsp was already loaded */ if (hw->dsp_loaded & (1 << info.index)) return -EBUSY; diff --git a/sound/core/init.c b/sound/core/init.c index 32ebe2f6bc59..168ae03d3a1c 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -255,6 +255,7 @@ int snd_card_new(struct device *parent, int idx, const char *xid, #ifdef CONFIG_PM init_waitqueue_head(&card->power_sleep); #endif + init_waitqueue_head(&card->remove_sleep); device_initialize(&card->card_dev); card->card_dev.parent = parent; @@ -452,6 +453,35 @@ int snd_card_disconnect(struct snd_card *card) } EXPORT_SYMBOL(snd_card_disconnect); +/** + * snd_card_disconnect_sync - disconnect card and wait until files get closed + * @card: card object to disconnect + * + * This calls snd_card_disconnect() for disconnecting all belonging components + * and waits until all pending files get closed. + * It assures that all accesses from user-space finished so that the driver + * can release its resources gracefully. + */ +void snd_card_disconnect_sync(struct snd_card *card) +{ + int err; + + err = snd_card_disconnect(card); + if (err < 0) { + dev_err(card->dev, + "snd_card_disconnect error (%d), skipping sync\n", + err); + return; + } + + spin_lock_irq(&card->files_lock); + wait_event_lock_irq(card->remove_sleep, + list_empty(&card->files_list), + card->files_lock); + spin_unlock_irq(&card->files_lock); +} +EXPORT_SYMBOL_GPL(snd_card_disconnect_sync); + static int snd_card_do_free(struct snd_card *card) { #if IS_ENABLED(CONFIG_SND_MIXER_OSS) @@ -957,6 +987,8 @@ int snd_card_file_remove(struct snd_card *card, struct file *file) break; } } + if (list_empty(&card->files_list)) + wake_up_all(&card->remove_sleep); spin_unlock(&card->files_lock); if (!found) { dev_err(card->dev, "card file remove problem (%p)\n", file); diff --git a/sound/core/jack.c b/sound/core/jack.c index f652e90efd7e..84c2a17c56ee 100644 --- a/sound/core/jack.c +++ b/sound/core/jack.c @@ -310,7 +310,7 @@ EXPORT_SYMBOL(snd_jack_set_parent); * @type: Jack report type for this key * @keytype: Input layer key type to be reported * - * Map a SND_JACK_BTN_ button type to an input layer key, allowing + * Map a SND_JACK_BTN_* button type to an input layer key, allowing * reporting of keys on accessories via the jack abstraction. If no * mapping is provided but keys are enabled in the jack type then * BTN_n numeric buttons will be reported. diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 7eadb7fd8074..9070f277f8db 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -775,6 +775,9 @@ static int _snd_pcm_new(struct snd_card *card, const char *id, int device, .dev_register = snd_pcm_dev_register, .dev_disconnect = snd_pcm_dev_disconnect, }; + static struct snd_device_ops internal_ops = { + .dev_free = snd_pcm_dev_free, + }; if (snd_BUG_ON(!card)) return -ENXIO; @@ -801,7 +804,8 @@ static int _snd_pcm_new(struct snd_card *card, const char *id, int device, if (err < 0) goto free_pcm; - err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops); + err = snd_device_new(card, SNDRV_DEV_PCM, pcm, + internal ? &internal_ops : &ops); if (err < 0) goto free_pcm; @@ -1099,8 +1103,6 @@ static int snd_pcm_dev_register(struct snd_device *device) if (snd_BUG_ON(!device || !device->device_data)) return -ENXIO; pcm = device->device_data; - if (pcm->internal) - return 0; mutex_lock(®ister_mutex); err = snd_pcm_add(pcm); @@ -1152,6 +1154,10 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) { snd_pcm_stream_lock_irq(substream); if (substream->runtime) { + if (snd_pcm_running(substream)) + snd_pcm_stop(substream, + SNDRV_PCM_STATE_DISCONNECTED); + /* to be sure, set the state unconditionally */ substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED; wake_up(&substream->runtime->sleep); wake_up(&substream->runtime->tsleep); @@ -1159,12 +1165,10 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) snd_pcm_stream_unlock_irq(substream); } } - if (!pcm->internal) { - pcm_call_notify(pcm, n_disconnect); - } + + pcm_call_notify(pcm, n_disconnect); for (cidx = 0; cidx < 2; cidx++) { - if (!pcm->internal) - snd_unregister_device(&pcm->streams[cidx].dev); + snd_unregister_device(&pcm->streams[cidx].dev); free_chmap(&pcm->streams[cidx]); } mutex_unlock(&pcm->open_mutex); diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 2fec2feac387..a4d92e46c459 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -195,7 +195,6 @@ EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irqrestore); int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info) { - struct snd_pcm_runtime *runtime; struct snd_pcm *pcm = substream->pcm; struct snd_pcm_str *pstr = substream->pstr; @@ -211,7 +210,6 @@ int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info) info->subdevices_count = pstr->substream_count; info->subdevices_avail = pstr->substream_count - pstr->substream_opened; strlcpy(info->subname, substream->name, sizeof(info->subname)); - runtime = substream->runtime; return 0; } diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index d10c780dfd54..6e22eea72654 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -802,6 +802,10 @@ static int snd_seq_deliver_event(struct snd_seq_client *client, struct snd_seq_e return -EMLINK; } + if (snd_seq_ev_is_variable(event) && + snd_BUG_ON(atomic && (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR))) + return -EINVAL; + if (event->queue == SNDRV_SEQ_ADDRESS_SUBSCRIBERS || event->dest.client == SNDRV_SEQ_ADDRESS_SUBSCRIBERS) result = deliver_to_subscribers(client, event, atomic, hop); diff --git a/sound/core/timer.c b/sound/core/timer.c index 15e82a656d96..ee09dace8bb1 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -1069,15 +1069,17 @@ EXPORT_SYMBOL(snd_timer_global_register); struct snd_timer_system_private { struct timer_list tlist; + struct snd_timer *snd_timer; unsigned long last_expires; unsigned long last_jiffies; unsigned long correction; }; -static void snd_timer_s_function(unsigned long data) +static void snd_timer_s_function(struct timer_list *t) { - struct snd_timer *timer = (struct snd_timer *)data; - struct snd_timer_system_private *priv = timer->private_data; + struct snd_timer_system_private *priv = from_timer(priv, t, + tlist); + struct snd_timer *timer = priv->snd_timer; unsigned long jiff = jiffies; if (time_after(jiff, priv->last_expires)) priv->correction += (long)jiff - (long)priv->last_expires; @@ -1159,7 +1161,8 @@ static int snd_timer_register_system(void) snd_timer_free(timer); return -ENOMEM; } - setup_timer(&priv->tlist, snd_timer_s_function, (unsigned long) timer); + priv->snd_timer = timer; + timer_setup(&priv->tlist, snd_timer_s_function, 0); timer->private_data = priv; timer->private_free = snd_timer_free_system; return snd_timer_global_register(timer); diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 135adb17703c..afac886ffa28 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -529,9 +529,9 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable) return running; } -static void loopback_timer_function(unsigned long data) +static void loopback_timer_function(struct timer_list *t) { - struct loopback_pcm *dpcm = (struct loopback_pcm *)data; + struct loopback_pcm *dpcm = from_timer(dpcm, t, timer); unsigned long flags; spin_lock_irqsave(&dpcm->cable->lock, flags); @@ -675,8 +675,7 @@ static int loopback_open(struct snd_pcm_substream *substream) } dpcm->loopback = loopback; dpcm->substream = substream; - setup_timer(&dpcm->timer, loopback_timer_function, - (unsigned long)dpcm); + timer_setup(&dpcm->timer, loopback_timer_function, 0); cable = loopback->cables[substream->number][dev]; if (!cable) { diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index c0939a0164a6..7b2b1f766b00 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -306,9 +306,9 @@ static int dummy_systimer_prepare(struct snd_pcm_substream *substream) return 0; } -static void dummy_systimer_callback(unsigned long data) +static void dummy_systimer_callback(struct timer_list *t) { - struct dummy_systimer_pcm *dpcm = (struct dummy_systimer_pcm *)data; + struct dummy_systimer_pcm *dpcm = from_timer(dpcm, t, timer); unsigned long flags; int elapsed = 0; @@ -343,8 +343,7 @@ static int dummy_systimer_create(struct snd_pcm_substream *substream) if (!dpcm) return -ENOMEM; substream->runtime->private_data = dpcm; - setup_timer(&dpcm->timer, dummy_systimer_callback, - (unsigned long) dpcm); + timer_setup(&dpcm->timer, dummy_systimer_callback, 0); spin_lock_init(&dpcm->lock); dpcm->substream = substream; return 0; diff --git a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c index b997222274bd..3e745f47dd2f 100644 --- a/sound/drivers/mpu401/mpu401_uart.c +++ b/sound/drivers/mpu401/mpu401_uart.c @@ -169,9 +169,9 @@ EXPORT_SYMBOL(snd_mpu401_uart_interrupt_tx); * timer callback * reprogram the timer and call the interrupt job */ -static void snd_mpu401_uart_timer(unsigned long data) +static void snd_mpu401_uart_timer(struct timer_list *t) { - struct snd_mpu401 *mpu = (struct snd_mpu401 *)data; + struct snd_mpu401 *mpu = from_timer(mpu, t, timer); unsigned long flags; spin_lock_irqsave(&mpu->timer_lock, flags); @@ -191,8 +191,7 @@ static void snd_mpu401_uart_add_timer (struct snd_mpu401 *mpu, int input) spin_lock_irqsave (&mpu->timer_lock, flags); if (mpu->timer_invoked == 0) { - setup_timer(&mpu->timer, snd_mpu401_uart_timer, - (unsigned long)mpu); + timer_setup(&mpu->timer, snd_mpu401_uart_timer, 0); mod_timer(&mpu->timer, 1 + jiffies); } mpu->timer_invoked |= input ? MPU401_MODE_INPUT_TIMER : diff --git a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c index 0f6392001e30..547662e02fcc 100644 --- a/sound/drivers/mtpav.c +++ b/sound/drivers/mtpav.c @@ -406,10 +406,10 @@ static void snd_mtpav_input_trigger(struct snd_rawmidi_substream *substream, int * timer interrupt for outputs */ -static void snd_mtpav_output_timer(unsigned long data) +static void snd_mtpav_output_timer(struct timer_list *t) { unsigned long flags; - struct mtpav *chip = (struct mtpav *)data; + struct mtpav *chip = from_timer(chip, t, timer); int p; spin_lock_irqsave(&chip->spinlock, flags); @@ -707,8 +707,7 @@ static int snd_mtpav_probe(struct platform_device *dev) mtp_card->share_irq = 0; mtp_card->inmidistate = 0; mtp_card->outmidihwport = 0xffffffff; - setup_timer(&mtp_card->timer, snd_mtpav_output_timer, - (unsigned long) mtp_card); + timer_setup(&mtp_card->timer, snd_mtpav_output_timer, 0); card->private_free = snd_mtpav_free; diff --git a/sound/drivers/opl3/opl3_midi.c b/sound/drivers/opl3/opl3_midi.c index 13c0a7e1bc2b..bb3f3a5a6951 100644 --- a/sound/drivers/opl3/opl3_midi.c +++ b/sound/drivers/opl3/opl3_midi.c @@ -238,10 +238,10 @@ static int opl3_get_voice(struct snd_opl3 *opl3, int instr_4op, /* * System timer interrupt function */ -void snd_opl3_timer_func(unsigned long data) +void snd_opl3_timer_func(struct timer_list *t) { - struct snd_opl3 *opl3 = (struct snd_opl3 *)data; + struct snd_opl3 *opl3 = from_timer(opl3, t, tlist); unsigned long flags; int again = 0; int i; diff --git a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c index d3e91be8b23a..5f881c4cdf1f 100644 --- a/sound/drivers/opl3/opl3_seq.c +++ b/sound/drivers/opl3/opl3_seq.c @@ -248,7 +248,7 @@ static int snd_opl3_seq_probe(struct device *_dev) } /* setup system timer */ - setup_timer(&opl3->tlist, snd_opl3_timer_func, (unsigned long) opl3); + timer_setup(&opl3->tlist, snd_opl3_timer_func, 0); spin_lock_init(&opl3->sys_timer_lock); opl3->sys_timer_status = 0; diff --git a/sound/drivers/opl3/opl3_voice.h b/sound/drivers/opl3/opl3_voice.h index eaef435e0528..a2445163008e 100644 --- a/sound/drivers/opl3/opl3_voice.h +++ b/sound/drivers/opl3/opl3_voice.h @@ -37,7 +37,7 @@ void snd_opl3_nrpn(void *p, struct snd_midi_channel *chan, struct snd_midi_chann void snd_opl3_sysex(void *p, unsigned char *buf, int len, int parsed, struct snd_midi_channel_set *chset); void snd_opl3_calc_volume(unsigned char *reg, int vel, struct snd_midi_channel *chan); -void snd_opl3_timer_func(unsigned long data); +void snd_opl3_timer_func(struct timer_list *t); /* Prototypes for opl3_drums.c */ void snd_opl3_load_drums(struct snd_opl3 *opl3); diff --git a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c index 88e66ea0306d..0a67b8b9f176 100644 --- a/sound/drivers/serial-u16550.c +++ b/sound/drivers/serial-u16550.c @@ -309,12 +309,12 @@ static irqreturn_t snd_uart16550_interrupt(int irq, void *dev_id) } /* When the polling mode, this function calls snd_uart16550_io_loop. */ -static void snd_uart16550_buffer_timer(unsigned long data) +static void snd_uart16550_buffer_timer(struct timer_list *t) { unsigned long flags; struct snd_uart16550 *uart; - uart = (struct snd_uart16550 *)data; + uart = from_timer(uart, t, buffer_timer); spin_lock_irqsave(&uart->open_lock, flags); snd_uart16550_del_timer(uart); snd_uart16550_io_loop(uart); @@ -828,8 +828,7 @@ static int snd_uart16550_create(struct snd_card *card, uart->prev_in = 0; uart->rstatus = 0; memset(uart->prev_status, 0x80, sizeof(unsigned char) * SNDRV_SERIAL_MAX_OUTS); - setup_timer(&uart->buffer_timer, snd_uart16550_buffer_timer, - (unsigned long)uart); + timer_setup(&uart->buffer_timer, snd_uart16550_buffer_timer, 0); uart->timer_running = 0; /* Register device */ diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c index f6d2985b2520..560ec0986e1a 100644 --- a/sound/hda/hdac_controller.c +++ b/sound/hda/hdac_controller.c @@ -319,7 +319,8 @@ int snd_hdac_bus_parse_capabilities(struct hdac_bus *bus) break; default: - dev_dbg(bus->dev, "Unknown capability %d\n", cur_cap); + dev_err(bus->dev, "Unknown capability %d\n", cur_cap); + cur_cap = 0; break; } diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index 19deb306facb..06f845e293cb 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -87,7 +87,7 @@ int snd_hdac_device_init(struct hdac_device *codec, struct hdac_bus *bus, fg = codec->afg ? codec->afg : codec->mfg; - err = snd_hdac_refresh_widgets(codec); + err = snd_hdac_refresh_widgets(codec, false); if (err < 0) goto error; @@ -388,11 +388,12 @@ static void setup_fg_nodes(struct hdac_device *codec) /** * snd_hdac_refresh_widgets - Reset the widget start/end nodes * @codec: the codec object + * @sysfs: re-initialize sysfs tree, too */ -int snd_hdac_refresh_widgets(struct hdac_device *codec) +int snd_hdac_refresh_widgets(struct hdac_device *codec, bool sysfs) { hda_nid_t start_nid; - int nums; + int nums, err; nums = snd_hdac_get_sub_nodes(codec, codec->afg, &start_nid); if (!start_nid || nums <= 0 || nums >= 0xff) { @@ -401,6 +402,12 @@ int snd_hdac_refresh_widgets(struct hdac_device *codec) return -EINVAL; } + if (sysfs) { + err = hda_widget_sysfs_reinit(codec, start_nid, nums); + if (err < 0) + return err; + } + codec->num_nodes = nums; codec->start_nid = start_nid; codec->end_nid = start_nid + nums; @@ -408,36 +415,6 @@ int snd_hdac_refresh_widgets(struct hdac_device *codec) } EXPORT_SYMBOL_GPL(snd_hdac_refresh_widgets); -/** - * snd_hdac_refresh_widget_sysfs - Reset the codec widgets and reinit the - * codec sysfs - * @codec: the codec object - * - * first we need to remove sysfs, then refresh widgets and lastly - * recreate it - */ -int snd_hdac_refresh_widget_sysfs(struct hdac_device *codec) -{ - int ret; - - if (device_is_registered(&codec->dev)) - hda_widget_sysfs_exit(codec); - ret = snd_hdac_refresh_widgets(codec); - if (ret) { - dev_err(&codec->dev, "failed to refresh widget: %d\n", ret); - return ret; - } - if (device_is_registered(&codec->dev)) { - ret = hda_widget_sysfs_init(codec); - if (ret) { - dev_err(&codec->dev, "failed to init sysfs: %d\n", ret); - return ret; - } - } - return ret; -} -EXPORT_SYMBOL_GPL(snd_hdac_refresh_widget_sysfs); - /* return CONNLIST_LEN parameter of the given widget */ static unsigned int get_num_conns(struct hdac_device *codec, hda_nid_t nid) { diff --git a/sound/hda/hdac_sysfs.c b/sound/hda/hdac_sysfs.c index 3c2d45ee6ab8..fb2aa344981e 100644 --- a/sound/hda/hdac_sysfs.c +++ b/sound/hda/hdac_sysfs.c @@ -415,3 +415,50 @@ void hda_widget_sysfs_exit(struct hdac_device *codec) { widget_tree_free(codec); } + +int hda_widget_sysfs_reinit(struct hdac_device *codec, + hda_nid_t start_nid, int num_nodes) +{ + struct hdac_widget_tree *tree; + hda_nid_t end_nid = start_nid + num_nodes; + hda_nid_t nid; + int i; + + if (!codec->widgets) + return hda_widget_sysfs_init(codec); + + tree = kmemdup(codec->widgets, sizeof(*tree), GFP_KERNEL); + if (!tree) + return -ENOMEM; + + tree->nodes = kcalloc(num_nodes + 1, sizeof(*tree->nodes), GFP_KERNEL); + if (!tree->nodes) { + kfree(tree); + return -ENOMEM; + } + + /* prune non-existing nodes */ + for (i = 0, nid = codec->start_nid; i < codec->num_nodes; i++, nid++) { + if (nid < start_nid || nid >= end_nid) + free_widget_node(codec->widgets->nodes[i], + &widget_node_group); + } + + /* add new nodes */ + for (i = 0, nid = start_nid; i < num_nodes; i++, nid++) { + if (nid < codec->start_nid || nid >= codec->end_nid) + add_widget_node(tree->root, nid, &widget_node_group, + &tree->nodes[i]); + else + tree->nodes[i] = + codec->widgets->nodes[nid - codec->start_nid]; + } + + /* replace with the new tree */ + kfree(codec->widgets->nodes); + kfree(codec->widgets); + codec->widgets = tree; + + kobject_uevent(tree->root, KOBJ_CHANGE); + return 0; +} diff --git a/sound/hda/local.h b/sound/hda/local.h index 7258fa8ce268..877631e39373 100644 --- a/sound/hda/local.h +++ b/sound/hda/local.h @@ -29,6 +29,8 @@ static inline unsigned int get_wcaps_channels(u32 wcaps) extern const struct attribute_group *hdac_dev_attr_groups[]; int hda_widget_sysfs_init(struct hdac_device *codec); +int hda_widget_sysfs_reinit(struct hdac_device *codec, hda_nid_t start_nid, + int num_nodes); void hda_widget_sysfs_exit(struct hdac_device *codec); #endif /* __HDAC_LOCAL_H */ diff --git a/sound/i2c/other/ak4117.c b/sound/i2c/other/ak4117.c index 3ab099fb8c15..b923342cadf4 100644 --- a/sound/i2c/other/ak4117.c +++ b/sound/i2c/other/ak4117.c @@ -35,7 +35,7 @@ MODULE_LICENSE("GPL"); #define AK4117_ADDR 0x00 /* fixed address */ -static void snd_ak4117_timer(unsigned long data); +static void snd_ak4117_timer(struct timer_list *t); static void reg_write(struct ak4117 *ak4117, unsigned char reg, unsigned char val) { @@ -91,7 +91,7 @@ int snd_ak4117_create(struct snd_card *card, ak4117_read_t *read, ak4117_write_t chip->read = read; chip->write = write; chip->private_data = private_data; - setup_timer(&chip->timer, snd_ak4117_timer, (unsigned long)chip); + timer_setup(&chip->timer, snd_ak4117_timer, 0); for (reg = 0; reg < 5; reg++) chip->regmap[reg] = pgm[reg]; @@ -529,9 +529,9 @@ int snd_ak4117_check_rate_and_errors(struct ak4117 *ak4117, unsigned int flags) return res; } -static void snd_ak4117_timer(unsigned long data) +static void snd_ak4117_timer(struct timer_list *t) { - struct ak4117 *chip = (struct ak4117 *)data; + struct ak4117 *chip = from_timer(chip, t, timer); if (chip->init) return; diff --git a/sound/isa/sb/emu8000_pcm.c b/sound/isa/sb/emu8000_pcm.c index 8f34551abd8d..bc5af71d3bdb 100644 --- a/sound/isa/sb/emu8000_pcm.c +++ b/sound/isa/sb/emu8000_pcm.c @@ -193,9 +193,9 @@ static inline int emu8k_get_curpos(struct snd_emu8k_pcm *rec, int ch) * timer interrupt handler * check the current position and update the period if necessary. */ -static void emu8k_pcm_timer_func(unsigned long data) +static void emu8k_pcm_timer_func(struct timer_list *t) { - struct snd_emu8k_pcm *rec = (struct snd_emu8k_pcm *)data; + struct snd_emu8k_pcm *rec = from_timer(rec, t, timer); int ptr, delta; spin_lock(&rec->timer_lock); @@ -241,7 +241,7 @@ static int emu8k_pcm_open(struct snd_pcm_substream *subs) runtime->private_data = rec; spin_lock_init(&rec->timer_lock); - setup_timer(&rec->timer, emu8k_pcm_timer_func, (unsigned long)rec); + timer_setup(&rec->timer, emu8k_pcm_timer_func, 0); runtime->hw = emu8k_pcm_hw; runtime->hw.buffer_bytes_max = emu->mem_size - LOOP_BLANK_SIZE * 3; diff --git a/sound/isa/sb/sb8_midi.c b/sound/isa/sb/sb8_midi.c index bd672abb4854..4affdcb78f72 100644 --- a/sound/isa/sb/sb8_midi.c +++ b/sound/isa/sb/sb8_midi.c @@ -138,6 +138,7 @@ static int snd_sb8dsp_midi_output_close(struct snd_rawmidi_substream *substream) struct snd_sb *chip; chip = substream->rmidi->private_data; + del_timer_sync(&chip->midi_timer); spin_lock_irqsave(&chip->open_lock, flags); chip->open &= ~(SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER); chip->midi_substream_output = NULL; @@ -209,10 +210,10 @@ static void snd_sb8dsp_midi_output_write(struct snd_rawmidi_substream *substream } } -static void snd_sb8dsp_midi_output_timer(unsigned long data) +static void snd_sb8dsp_midi_output_timer(struct timer_list *t) { - struct snd_rawmidi_substream *substream = (struct snd_rawmidi_substream *) data; - struct snd_sb * chip = substream->rmidi->private_data; + struct snd_sb *chip = from_timer(chip, t, midi_timer); + struct snd_rawmidi_substream *substream = chip->midi_substream_output; unsigned long flags; spin_lock_irqsave(&chip->open_lock, flags); @@ -230,9 +231,6 @@ static void snd_sb8dsp_midi_output_trigger(struct snd_rawmidi_substream *substre spin_lock_irqsave(&chip->open_lock, flags); if (up) { if (!(chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER)) { - setup_timer(&chip->midi_timer, - snd_sb8dsp_midi_output_timer, - (unsigned long) substream); mod_timer(&chip->midi_timer, 1 + jiffies); chip->open |= SB_OPEN_MIDI_OUTPUT_TRIGGER; } @@ -275,6 +273,7 @@ int snd_sb8dsp_midi(struct snd_sb *chip, int device) if (chip->hardware >= SB_HW_20) rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; rmidi->private_data = chip; + timer_setup(&chip->midi_timer, snd_sb8dsp_midi_output_timer, 0); chip->rmidi = rmidi; return 0; } diff --git a/sound/isa/wavefront/wavefront_midi.c b/sound/isa/wavefront/wavefront_midi.c index 2aa05f3aaa38..556b14738970 100644 --- a/sound/isa/wavefront/wavefront_midi.c +++ b/sound/isa/wavefront/wavefront_midi.c @@ -349,10 +349,10 @@ static void snd_wavefront_midi_input_trigger(struct snd_rawmidi_substream *subst spin_unlock_irqrestore (&midi->virtual, flags); } -static void snd_wavefront_midi_output_timer(unsigned long data) +static void snd_wavefront_midi_output_timer(struct timer_list *t) { - snd_wavefront_card_t *card = (snd_wavefront_card_t *)data; - snd_wavefront_midi_t *midi = &card->wavefront.midi; + snd_wavefront_midi_t *midi = from_timer(midi, t, timer); + snd_wavefront_card_t *card = midi->timer_card; unsigned long flags; spin_lock_irqsave (&midi->virtual, flags); @@ -383,9 +383,9 @@ static void snd_wavefront_midi_output_trigger(struct snd_rawmidi_substream *subs if (up) { if ((midi->mode[mpu] & MPU401_MODE_OUTPUT_TRIGGER) == 0) { if (!midi->istimer) { - setup_timer(&midi->timer, + timer_setup(&midi->timer, snd_wavefront_midi_output_timer, - (unsigned long) substream->rmidi->card->private_data); + 0); mod_timer(&midi->timer, 1 + jiffies); } midi->istimer++; diff --git a/sound/oss/CHANGELOG b/sound/oss/CHANGELOG deleted file mode 100644 index 8706cd66ca1f..000000000000 --- a/sound/oss/CHANGELOG +++ /dev/null @@ -1,369 +0,0 @@ -Note these changes relate to Hannu's code and don't include the changes -made outside of this for modularising the sound - -Changelog for version 3.8o --------------------------- - -Since 3.8h -- Included support for OPL3-SA1 and SoftOSS - -Since 3.8 -- Fixed SNDCTL_DSP_GETOSPACE -- Compatibility fixes for Linux 2.1.47 - -Since 3.8-beta21 -- Fixed all known bugs (I think). - -Since 3.8-beta8 -- Lot of fixes to audio playback code in dmabuf.c - -Since 3.8-beta6 -- Fixed the famous Quake delay bug. - -Since 3.8-beta5 -- Fixed many bugs in audio playback. - -Since 3.8-beta4 -- Just minor changes. - -Since 3.8-beta1 -- Major rewrite of audio playback handling. -- Added AWE32 support by Takashi Iwai (in ./lowlevel/). - -Since 3.7-beta# -- Passing of ioctl() parameters between soundcard.c and other modules has been -changed so that arg always points to kernel space. -- Some bugfixes. - -Since 3.7-beta5 -- Disabled MIDI input with GUS PnP (Interwave). There seems to be constant -stream of received 0x00 bytes when the MIDI receiver is enabled. - -Since 3.5 -- Changes almost everywhere. -- Support for OPTi 82C924-based sound cards. - -Since 3.5.4-beta8 -- Fixed a bug in handling of non-fragment sized writes in 16 bit/stereo mode - with GUS. -- Limited minimum fragment size with some audio devices (GUS=512 and - SB=32). These devices require more time to "recover" from processing - of each fragment. - -Since 3.5.4-beta6/7 -- There seems to be problems in the OPTi 82C930 so cards based on this - chip don't necessarily work yet. There are problems in detecting the - MIDI interface. Also mixer volumes may be seriously wrong on some systems. - You can safely use this driver version with C930 if it looks to work. - However please don't complain if you have problems with it. C930 support - should be fixed in future releases. -- Got initialization of GUS PnP to work. With this version GUS PnP should - work in GUS compatible mode after initialization using isapnptools. -- Fixed a bug in handling of full duplex cards in write only mode. This has - been causing "audio device opening" errors with RealAudio player. - -Since 3.5.4.beta5 -- Changes to OPTi 82C930 driver. -- Major changes to the Soundscape driver. The driver requires now just one - DMA channel. The extra audio/dsp device (the "Not functional" one) used - for code download in the earlier versions has been eliminated. There is now - just one /dev/dsp# device which is used both for code download and audio. - -Since 3.5.4.beta4 -- Minor changes. - -Since 3.5.4-beta2 -- Fixed silent playback with ESS 688/1688. -- Got SB16 to work without the 16 bit DMA channel (only the 8 bit one - is required for 8 and 16 bit modes). -- Added the "lowlevel" subdirectory for additional low level drivers that - are not part of USS core. See lowlevel/README for more info. -- Included support for ACI mixer (by Markus Kuhn). ACI is a mixer used in - miroPCM sound cards. See lowlevel/aci.readme for more info. -- Support for Aztech Washington chipset (AZT2316 ASIC). - -Since 3.5.4-beta1 -- Reduced clicking with AD1848. -- Support for OPTi 82C930. Only half duplex at this time. 16 bit playback - is sometimes just white noise (occurs randomly). - -Since 3.5.2 -- Major changes to the SB/Jazz16/ESS driver (most parts rewritten). - The most noticeable new feature is support for multiple SB cards at the same - time. -- Renamed sb16_midi.c to uart401.c. Also modified it to work also with - other MPU401 UART compatible cards than SB16/ESS/Jazz. -- Some changes which reduce clicking in audio playback. -- Copying policy is now GPL. - -Since 3.5.1 -- TB Maui initialization support -Since 3.5 -- Improved handling of playback underrun situations. - -Since 3.5-beta10 -- Bug fixing - -Since 3.5-beta9 -- Fixed for compatibility with Linux 1.3.70 and later. -- Changed boot time passing of 16 bit DMA channel number to SB driver. - -Since 3.5-beta8 -- Minor changes - -Since 3.5-beta7 -- enhancements to configure program (by Jeff Tranter): - - prompts are in same format as 1.3.x Linux kernel config program - - on-line help for each question - - fixed some compile warnings detected by gcc/g++ -Wall - - minor grammatical changes to prompts - -Since 3.5-beta6 -- Fixed bugs in mmap() support. -- Minor changes to Maui driver. - -Since 3.5-beta5 -- Fixed crash after recording with ESS688. It's generally a good - idea to stop inbound DMA transfers before freeing the memory - buffer. -- Fixed handling of AD1845 codec (for example Shuttle Sound System). -- Few other fixes. - -Since 3.5-beta4 -- Fixed bug in handling of uninitialized instruments with GUS. - -Since 3.5-beta3 -- Few changes which decrease popping at end/beginning of audio playback. - -Since 3.5-beta2 -- Removed MAD16+CS4231 hack made in previous version since it didn't - help. -- Fixed the above bug in proper way and in proper place. Many thanks - to James Hightower. - -Since 3.5-beta1 -- Bug fixes. -- Full duplex audio with MAD16+CS4231 may work now. The driver configures - SB DMA of MAD16 so that it doesn't conflict with codec's DMA channels. - The side effect is that all 8 bit DMA channels (0,1,3) are populated in - duplex mode. - -Since 3.5-alpha9 -- Bug fixes (mostly in Jazz16 and ESS1688/688 supports). -- Temporarily disabled recording with ESS1688/688 since it causes crash. -- Changed audio buffer partitioning algorithm so that it selects - smaller fragment size than earlier. This improves real time capabilities - of the driver and makes recording to disk to work better. Unfortunately - this change breaks some programs which assume that fragments cannot be - shorter than 4096 bytes. - -Since 3.5-alpha8 -- Bug fixes - -Since 3.5-alpha7 -- Linux kernel compatible configuration (_EXPERIMENTAL_). Enable - using command "cd /linux/drivers/sound;make script" and then - just run kernel's make config normally. -- Minor fixes to the SB support. Hopefully the driver works with - all SB models now. -- Added support for ESS ES1688 "AudioDrive" based cards. - -Since 3.5-alpha6 -- SB Pro and SB16 supports are no longer separately selectable options. - Enabling SB enables them too. -- Changed all #ifndef EXCLUDE_xx stuff to #ifdef CONFIG_xx. Modified -configure to handle this. -- Removed initialization messages from the -modularized version. They can be enabled by using init_trace=1 in -the insmod command line (insmod sound init_trace=1). -- More AIX stuff. -- Added support for synchronizing dsp/audio devices with /dev/sequencer. -- mmap() support for dsp/audio devices. - -Since 3.5-alpha5 -- AIX port. -- Changed some xxx_PATCH macros in soundcard.h to work with - big endian machines. - -Since 3.5-alpha4 -- Removed the 'setfx' stuff from the version distributed with kernel - sources. Running 'setfx' is required again. - -Since 3.5-alpha3 -- Moved stuff from the 'setfx' program to the AudioTrix Pro driver. - -Since 3.5-alpha2 -- Modifications to makefile and configure.c. Unnecessary sources - are no longer compiled. Newly created local.h is also copied to - /etc/soundconf. "make oldconfig" reads /etc/soundconf and produces - new local.h which is compatible with current version of the driver. -- Some fixes to the SB16 support. -- Fixed random protection fault in gus_wave.c - -Since 3.5-alpha1 -- Modified to work with Linux-1.3.33 and later -- Some minor changes - -Since 3.0.2 -- Support for CS4232 based PnP cards (AcerMagic S23 etc). -- Full duplex support for some CS4231, CS4232 and AD1845 based cards -(GUS MAX, AudioTrix Pro, AcerMagic S23 and many MAD16/Mozart cards -having a codec mentioned above). -- Almost fully rewritten loadable modules support. -- Fixed some bugs. -- Huge amount of testing (more testing is still required). -- mmap() support (works with some cards). Requires much more testing. -- Sample/patch/program loading for TB Maui/Tropez. No initialization -since TB doesn't allow me to release that code. -- Using CS4231 compatible codecs as timer for /dev/music. - -Since 3.0.1 -- Added allocation of I/O ports, DMA channels and interrupts -to the initialization code. This may break modules support since -the driver may not free some resources on unload. Should be fixed soon. - -Since 3.0 -- Some important bug fixes. -- select() for /dev/dsp and /dev/audio (Linux only). -(To use select() with read, you have to call read() to start -the recording. Calling write() kills recording immediately so -use select() carefully when you are writing a half duplex app. -Full duplex mode is not implemented yet.) Select works also with -/dev/sequencer and /dev/music. Maybe with /dev/midi## too. - -Since 3.0-beta2 -- Minor fixes. -- Added Readme.cards - -Since 3.0-beta1 -- Minor fixes to the modules support. -- Eliminated call to sb_free_irq() in ad1848.c -- Rewritten MAD16&Mozart support (not tested with MAD16 Pro). -- Fix to DMA initialization of PSS cards. -- Some fixes to ad1848/cs42xx mixer support (GUS MAX, MSS, etc.) -- Fixed some bugs in the PSS driver which caused I/O errors with - the MSS mode (/dev/dsp). - -Since 3.0-950506 -- Recording with GUS MAX fixed. It works when the driver is configured - to use two DMA channels with GUS MAX (16 bit ones recommended). - -Since 3.0-94xxxx -- Too many changes - -Since 3.0-940818 -- Fixes for Linux 1.1.4x. -- Disables Disney Sound System with SG NX Pro 16 (less noise). - -Since 2.90-2 -- Fixes to soundcard.h -- Non blocking mode to /dev/sequencer -- Experimental detection code for Ensoniq Soundscape. - -Since 2.90 -- Minor and major bug fixes - -Since pre-3.0-940712 -- GUS MAX support -- Partially working MSS/WSS support (could work with some cards). -- Hardware u-Law and A-Law support with AD1848/CS4248 and CS4231 codecs - (GUS MAX, GUS16, WSS etc). Hardware ADPCM is possible with GUS16 and - GUS MAX, but it doesn't work yet. -Since pre-3.0-940426 -- AD1848/CS4248/CS4231 codec support (MSS, GUS MAX, Aztec, Orchid etc). -This codec chip is used in various sound cards. This version is developed -for the 16 bit daughtercard of GUS. It should work with other cards also -if the following requirements are met: - - The I/O, IRQ and DMA settings are jumper selectable or - the card is initialized by booting DOS before booting Linux (etc.). - - You add the IO, IRQ and DMA settings manually to the local.h. - (Just define GUS16_BASE, GUS16_IRQ and GUS16_DMA). Note that - the base address bust be the base address of the codec chip not the - card itself. For the GUS16 these are the same but most MSS compatible - cards have the codec located at card_base+4. -- Some minor changes - -Since 2.5 (******* MAJOR REWRITE ***********) - -This version is based on v2.3. I have tried to maintain two versions -together so that this one should have the same features than v2.5. -Something may still be missing. If you notice such things, please let me -know. - -The Readme.v30 contains more details. - -- /dev/midi## devices. -- /dev/sequencer2 - -Since 2.5-beta2 -- Some fine tuning to the GUS v3.7 mixer code. -- Fixed speed limits for the plain SB (1.0 to 2.0). - -Since 2.5-beta -- Fixed OPL-3 detection with SB. Caused problems with PAS16. -- GUS v3.7 mixer support. - -Since 2.4 -- Mixer support for Sound Galaxy NX Pro (define __SGNXPRO__ on your local.h). -- Fixed truncated sound on /dev/dsp when the device is closed. -- Linear volume mode for GUS -- Pitch bends larger than +/- 2 octaves. -- MIDI recording for SB and SB Pro. (Untested). -- Some other fixes. -- SB16 MIDI and DSP drivers only initialized if SB16 actually installed. -- Implemented better detection for OPL-3. This should be useful if you - have an old SB Pro (the non-OPL-3 one) or a SB 2.0 clone which has a OPL-3. -- SVR4.2 support by Ian Hartas. Initial ALPHA TEST version (untested). - -Since 2.3b -- Fixed bug which made it impossible to make long recordings to disk. - Recording was not restarted after a buffer overflow situation. -- Limited mixer support for GUS. -- Numerous improvements to the GUS driver by Andrew Robinson. Including - some click removal etc. - -Since 2.3 -- Fixed some minor bugs in the SB16 driver. - -Since 2.2b -- Full SB16 DSP support. 8/16 bit, mono/stereo -- The SCO and FreeBSD versions should be in sync now. There are some - problems with SB16 and GUS in the FreeBSD versions. - The DMA buffer allocation of the SCO version has been polished but - there could still be some problems. At least it hogs memory. - The DMA channel - configuration method used in the SCO/System is a hack. -- Support for the MPU emulation of the SB16. -- Some big arrays are now allocated boot time. This makes the BSS segment - smaller which makes it possible to use the full driver with - NetBSD. These arrays are not allocated if no suitable sound card is available. -- Fixed a bug in the compute_and_set_volume in gus_wave.c -- Fixed the too fast mono playback problem of SB Pro and PAS16. - -Since 2.2 -- Stereo recording for SB Pro. Somehow it was missing and nobody - had noticed it earlier. -- Minor polishing. -- Interpreting of boot time arguments (sound=) for Linux. -- Breakup of sb_dsp.c. Parts of the code has been moved to - sb_mixer.c and sb_midi.c - -Since 2.1 -- Preliminary support for SB16. - - The SB16 mixer is supported in its native mode. - - Digitized voice capability up to 44.1 kHz/8 bit/mono - (16 bit and stereo support coming in the next release). -- Fixed some bugs in the digitized voice driver for PAS16. -- Proper initialization of the SB emulation of latest PAS16 models. - -- Significantly improved /dev/dsp and /dev/audio support. - - Now supports half duplex mode. It's now possible to record and - playback without closing and reopening the device. - - It's possible to use smaller buffers than earlier. There is a new - ioctl(fd, SNDCTL_DSP_SUBDIVIDE, &n) where n should be 1, 2 or 4. - This call instructs the driver to use smaller buffers. The default - buffer size (0.5 to 1.0 seconds) is divided by n. Should be called - immediately after opening the device. - -Since 2.0 -Just cosmetic changes. diff --git a/sound/oss/Kconfig b/sound/oss/Kconfig deleted file mode 100644 index 4033fe58f0cf..000000000000 --- a/sound/oss/Kconfig +++ /dev/null @@ -1,533 +0,0 @@ -# 18 Apr 1998, Michael Elizabeth Chastain, <mailto:mec@shout.net> -# More hacking for modularisation. -# -# Prompt user for primary drivers. - -config SOUND_BCM_CS4297A - tristate "Crystal Sound CS4297a (for Swarm)" - depends on SIBYTE_SWARM - help - The BCM91250A has a Crystal CS4297a on synchronous serial - port B (in addition to the DB-9 serial port). Say Y or M - here to enable the sound chip instead of the UART. Also - note that CONFIG_KGDB should not be enabled at the same - time, since it also attempts to use this UART port. - -config SOUND_MSNDCLAS - tristate "Support for Turtle Beach MultiSound Classic, Tahiti, Monterey" - depends on (m || !STANDALONE) && ISA - help - Say M here if you have a Turtle Beach MultiSound Classic, Tahiti or - Monterey (not for the Pinnacle or Fiji). - - See <file:Documentation/sound/oss/MultiSound> for important information - about this driver. Note that it has been discontinued, but the - Voyetra Turtle Beach knowledge base entry for it is still available - at <http://www.turtlebeach.com/site/kb_ftp/790.asp>. - -comment "Compiled-in MSND Classic support requires firmware during compilation." - depends on SOUND_PRIME && SOUND_MSNDCLAS=y - -config MSNDCLAS_HAVE_BOOT - bool - depends on SOUND_MSNDCLAS=y && !STANDALONE - default y - -config MSNDCLAS_INIT_FILE - string "Full pathname of MSNDINIT.BIN firmware file" - depends on SOUND_MSNDCLAS - default "/etc/sound/msndinit.bin" - help - The MultiSound cards have two firmware files which are required for - operation, and are not currently included. These files can be - obtained from Turtle Beach. See - <file:Documentation/sound/oss/MultiSound> for information on how to - obtain this. - -config MSNDCLAS_PERM_FILE - string "Full pathname of MSNDPERM.BIN firmware file" - depends on SOUND_MSNDCLAS - default "/etc/sound/msndperm.bin" - help - The MultiSound cards have two firmware files which are required for - operation, and are not currently included. These files can be - obtained from Turtle Beach. See - <file:Documentation/sound/oss/MultiSound> for information on how to - obtain this. - -config MSNDCLAS_IRQ - int "MSND Classic IRQ 5, 7, 9, 10, 11, 12" - depends on SOUND_MSNDCLAS=y - default "5" - help - Interrupt Request line for the MultiSound Classic and related cards. - -config MSNDCLAS_MEM - hex "MSND Classic memory B0000, C8000, D0000, D8000, E0000, E8000" - depends on SOUND_MSNDCLAS=y - default "D0000" - help - Memory-mapped I/O base address for the MultiSound Classic and - related cards. - -config MSNDCLAS_IO - hex "MSND Classic I/O 210, 220, 230, 240, 250, 260, 290, 3E0" - depends on SOUND_MSNDCLAS=y - default "290" - help - I/O port address for the MultiSound Classic and related cards. - -config SOUND_MSNDPIN - tristate "Support for Turtle Beach MultiSound Pinnacle, Fiji" - depends on (m || !STANDALONE) && ISA - help - Say M here if you have a Turtle Beach MultiSound Pinnacle or Fiji. - See <file:Documentation/sound/oss/MultiSound> for important information - about this driver. Note that it has been discontinued, but the - Voyetra Turtle Beach knowledge base entry for it is still available - at <http://www.turtlebeach.com/site/kb_ftp/600.asp>. - -comment "Compiled-in MSND Pinnacle support requires firmware during compilation." - depends on SOUND_PRIME && SOUND_MSNDPIN=y - -config MSNDPIN_HAVE_BOOT - bool - depends on SOUND_MSNDPIN=y - default y - -config MSNDPIN_INIT_FILE - string "Full pathname of PNDSPINI.BIN firmware file" - depends on SOUND_MSNDPIN - default "/etc/sound/pndspini.bin" - help - The MultiSound cards have two firmware files which are required - for operation, and are not currently included. These files can be - obtained from Turtle Beach. See - <file:Documentation/sound/oss/MultiSound> for information on how to - obtain this. - -config MSNDPIN_PERM_FILE - string "Full pathname of PNDSPERM.BIN firmware file" - depends on SOUND_MSNDPIN - default "/etc/sound/pndsperm.bin" - help - The MultiSound cards have two firmware files which are required for - operation, and are not currently included. These files can be - obtained from Turtle Beach. See - <file:Documentation/sound/oss/MultiSound> for information on how to - obtain this. - -config MSNDPIN_IRQ - int "MSND Pinnacle IRQ 5, 7, 9, 10, 11, 12" - depends on SOUND_MSNDPIN=y - default "5" - help - Interrupt request line for the primary synthesizer on MultiSound - Pinnacle and Fiji sound cards. - -config MSNDPIN_MEM - hex "MSND Pinnacle memory B0000, C8000, D0000, D8000, E0000, E8000" - depends on SOUND_MSNDPIN=y - default "D0000" - help - Memory-mapped I/O base address for the primary synthesizer on - MultiSound Pinnacle and Fiji sound cards. - -config MSNDPIN_IO - hex "MSND Pinnacle I/O 210, 220, 230, 240, 250, 260, 290, 3E0" - depends on SOUND_MSNDPIN=y - default "290" - help - Memory-mapped I/O base address for the primary synthesizer on - MultiSound Pinnacle and Fiji sound cards. - -config MSNDPIN_DIGITAL - bool "MSND Pinnacle has S/PDIF I/O" - depends on SOUND_MSNDPIN=y - help - If you have the S/PDIF daughter board for the Pinnacle or Fiji, - answer Y here; otherwise, say N. If you have this, you will be able - to play and record from the S/PDIF port (digital signal). See - <file:Documentation/sound/oss/MultiSound> for information on how to make - use of this capability. - -config MSNDPIN_NONPNP - bool "MSND Pinnacle non-PnP Mode" - depends on SOUND_MSNDPIN=y - help - The Pinnacle and Fiji card resources can be configured either with - PnP, or through a configuration port. Say Y here if your card is NOT - in PnP mode. For the Pinnacle, configuration in non-PnP mode allows - use of the IDE and joystick peripherals on the card as well; these - do not show up when the card is in PnP mode. Specifying zero for any - resource of a device will disable the device. If you are running the - card in PnP mode, you must say N here and use isapnptools to - configure the card's resources. - -comment "MSND Pinnacle DSP section will be configured to above parameters." - depends on SOUND_MSNDPIN=y && MSNDPIN_NONPNP - -config MSNDPIN_CFG - hex "MSND Pinnacle config port 250,260,270" - depends on MSNDPIN_NONPNP - default "250" - help - This is the port which the Pinnacle and Fiji uses to configure the - card's resources when not in PnP mode. If your card is in PnP mode, - then be sure to say N to the previous option, "MSND Pinnacle Non-PnP - Mode". - -comment "Pinnacle-specific Device Configuration (0 disables)" - depends on SOUND_MSNDPIN=y && MSNDPIN_NONPNP - -config MSNDPIN_MPU_IO - hex "MSND Pinnacle MPU I/O (e.g. 330)" - depends on MSNDPIN_NONPNP - default "0" - help - Memory-mapped I/O base address for the Kurzweil daughterboard - synthesizer on MultiSound Pinnacle and Fiji sound cards. - -config MSNDPIN_MPU_IRQ - int "MSND Pinnacle MPU IRQ (e.g. 9)" - depends on MSNDPIN_NONPNP - default "0" - help - Interrupt request number for the Kurzweil daughterboard - synthesizer on MultiSound Pinnacle and Fiji sound cards. - -config MSNDPIN_IDE_IO0 - hex "MSND Pinnacle IDE I/O 0 (e.g. 170)" - depends on MSNDPIN_NONPNP - default "0" - help - CD-ROM drive 0 memory-mapped I/O base address for the MultiSound - Pinnacle and Fiji sound cards. - -config MSNDPIN_IDE_IO1 - hex "MSND Pinnacle IDE I/O 1 (e.g. 376)" - depends on MSNDPIN_NONPNP - default "0" - help - CD-ROM drive 1 memory-mapped I/O base address for the MultiSound - Pinnacle and Fiji sound cards. - -config MSNDPIN_IDE_IRQ - int "MSND Pinnacle IDE IRQ (e.g. 15)" - depends on MSNDPIN_NONPNP - default "0" - help - Interrupt request number for the IDE CD-ROM interface on the - MultiSound Pinnacle and Fiji sound cards. - -config MSNDPIN_JOYSTICK_IO - hex "MSND Pinnacle joystick I/O (e.g. 200)" - depends on MSNDPIN_NONPNP - default "0" - help - Memory-mapped I/O base address for the joystick port on MultiSound - Pinnacle and Fiji sound cards. - -config MSND_FIFOSIZE - int "MSND buffer size (kB)" - depends on SOUND_MSNDPIN=y || SOUND_MSNDCLAS=y - default "128" - help - Configures the size of each audio buffer, in kilobytes, for - recording and playing in the MultiSound drivers (both the Classic - and Pinnacle). Larger values reduce the chance of data overruns at - the expense of overall latency. If unsure, use the default. - -menuconfig SOUND_OSS - tristate "OSS sound modules" - depends on ISA_DMA_API && (VIRT_TO_BUS || ARCH_RPC || ARCH_NETWINDER) - depends on !GENERIC_ISA_DMA_SUPPORT_BROKEN - help - OSS is the Open Sound System suite of sound card drivers. They make - sound programming easier since they provide a common API. Say Y or - M here (the module will be called sound) if you haven't found a - driver for your sound card above, then pick your driver from the - list below. - -if SOUND_OSS - -config SOUND_TRACEINIT - bool "Verbose initialisation" - help - Verbose soundcard initialization -- affects the format of autoprobe - and initialization messages at boot time. - -config SOUND_DMAP - bool "Persistent DMA buffers" - ---help--- - Linux can often have problems allocating DMA buffers for ISA sound - cards on machines with more than 16MB of RAM. This is because ISA - DMA buffers must exist below the 16MB boundary and it is quite - possible that a large enough free block in this region cannot be - found after the machine has been running for a while. If you say Y - here the DMA buffers (64Kb) will be allocated at boot time and kept - until the shutdown. This option is only useful if you said Y to - "OSS sound modules", above. If you said M to "OSS sound modules" - then you can get the persistent DMA buffer functionality by passing - the command-line argument "dmabuf=1" to the sound module. - - Say Y unless you have 16MB or more RAM or a PCI sound card. - -config SOUND_VMIDI - tristate "Loopback MIDI device support" - help - Support for MIDI loopback on port 1 or 2. - -config SOUND_TRIX - tristate "MediaTrix AudioTrix Pro support" - help - Answer Y if you have the AudioTriX Pro sound card manufactured - by MediaTrix. - -config TRIX_HAVE_BOOT - bool "Have TRXPRO.HEX firmware file" - depends on SOUND_TRIX=y && !STANDALONE - help - The MediaTrix AudioTrix Pro has an on-board microcontroller which - needs to be initialized by downloading the code from the file - TRXPRO.HEX in the DOS driver directory. If you don't have the - TRXPRO.HEX file handy you may skip this step. However, the SB and - MPU-401 modes of AudioTrix Pro will not work without this file! - -config TRIX_BOOT_FILE - string "Full pathname of TRXPRO.HEX firmware file" - depends on TRIX_HAVE_BOOT - default "/etc/sound/trxpro.hex" - help - Enter the full pathname of your TRXPRO.HEX file, starting from /. - -config SOUND_MSS - tristate "Microsoft Sound System support" - ---help--- - Again think carefully before answering Y to this question. It's - safe to answer Y if you have the original Windows Sound System card - made by Microsoft or Aztech SG 16 Pro (or NX16 Pro). Also you may - say Y in case your card is NOT among these: - - ATI Stereo F/X, AdLib, Audio Excell DSP16, Cardinal DSP16, - Ensoniq SoundScape (and compatibles made by Reveal and Spea), - Gravis Ultrasound, Gravis Ultrasound ACE, Gravis Ultrasound Max, - Gravis Ultrasound with 16 bit option, Logitech Sound Man 16, - Logitech SoundMan Games, Logitech SoundMan Wave, MAD16 Pro (OPTi - 82C929), Media Vision Jazz16, MediaTriX AudioTriX Pro, Microsoft - Windows Sound System (MSS/WSS), Mozart (OAK OTI-601), Orchid - SW32, Personal Sound System (PSS), Pro Audio Spectrum 16, Pro - Audio Studio 16, Pro Sonic 16, Roland MPU-401 MIDI interface, - Sound Blaster 1.0, Sound Blaster 16, Sound Blaster 16ASP, Sound - Blaster 2.0, Sound Blaster AWE32, Sound Blaster Pro, TI TM4000M - notebook, ThunderBoard, Turtle Beach Tropez, Yamaha FM - synthesizers (OPL2, OPL3 and OPL4), 6850 UART MIDI Interface. - - For cards having native support in VoxWare, consult the card - specific instructions in <file:Documentation/sound/oss/README.OSS>. - Some drivers have their own MSS support and saying Y to this option - will cause a conflict. - - If you compile the driver into the kernel, you have to add - "ad1848=<io>,<irq>,<dma>,<dma2>[,<type>]" to the kernel command - line. - -config SOUND_MPU401 - tristate "MPU-401 support (NOT for SB16)" - ---help--- - Be careful with this question. The MPU401 interface is supported by - all sound cards. However, some natively supported cards have their - own driver for MPU401. Enabling this MPU401 option with these cards - will cause a conflict. Also, enabling MPU401 on a system that - doesn't really have a MPU401 could cause some trouble. If your card - was in the list of supported cards, look at the card specific - instructions in the <file:Documentation/sound/oss/README.OSS> file. It - is safe to answer Y if you have a true MPU401 MIDI interface card. - - If you compile the driver into the kernel, you have to add - "mpu401=<io>,<irq>" to the kernel command line. - -config SOUND_PAS - tristate "ProAudioSpectrum 16 support" - ---help--- - Answer Y only if you have a Pro Audio Spectrum 16, ProAudio Studio - 16 or Logitech SoundMan 16 sound card. Answer N if you have some - other card made by Media Vision or Logitech since those are not - PAS16 compatible. Please read <file:Documentation/sound/oss/PAS16>. - It is not necessary to add Sound Blaster support separately; it - is included in PAS support. - - If you compile the driver into the kernel, you have to add - "pas2=<io>,<irq>,<dma>,<dma2>,<sbio>,<sbirq>,<sbdma>,<sbdma2> - to the kernel command line. - -config PAS_JOYSTICK - bool "Enable PAS16 joystick port" - depends on SOUND_PAS=y - help - Say Y here to enable the Pro Audio Spectrum 16's auxiliary joystick - port. - -config SOUND_PSS - tristate "PSS (AD1848, ADSP-2115, ESC614) support" - help - Answer Y or M if you have an Orchid SW32, Cardinal DSP16, Beethoven - ADSP-16 or some other card based on the PSS chipset (AD1848 codec + - ADSP-2115 DSP chip + Echo ESC614 ASIC CHIP). For more information on - how to compile it into the kernel or as a module see the file - <file:Documentation/sound/oss/PSS>. - - If you compile the driver into the kernel, you have to add - "pss=<io>,<mssio>,<mssirq>,<mssdma>,<mpuio>,<mpuirq>" to the kernel - command line. - -config PSS_MIXER - bool "Enable PSS mixer (Beethoven ADSP-16 and other compatible)" - depends on SOUND_PSS - help - Answer Y for Beethoven ADSP-16. You may try to say Y also for other - cards if they have master volume, bass, treble, and you can't - control it under Linux. If you answer N for Beethoven ADSP-16, you - can't control master volume, bass, treble and synth volume. - - If you said M to "PSS support" above, you may enable or disable this - PSS mixer with the module parameter pss_mixer. For more information - see the file <file:Documentation/sound/oss/PSS>. - -config PSS_HAVE_BOOT - bool "Have DSPxxx.LD firmware file" - depends on SOUND_PSS && !STANDALONE - help - If you have the DSPxxx.LD file or SYNTH.LD file for you card, say Y - to include this file. Without this file the synth device (OPL) may - not work. - -config PSS_BOOT_FILE - string "Full pathname of DSPxxx.LD firmware file" - depends on PSS_HAVE_BOOT - default "/etc/sound/dsp001.ld" - help - Enter the full pathname of your DSPxxx.LD file or SYNTH.LD file, - starting from /. - -config SOUND_SB - tristate "100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support" - ---help--- - Answer Y if you have an original Sound Blaster card made by Creative - Labs or a 100% hardware compatible clone (like the Thunderboard or - SM Games). For an unknown card you may answer Y if the card claims - to be Sound Blaster-compatible. - - Please read the file <file:Documentation/sound/oss/Soundblaster>. - - You should also say Y here for cards based on the Avance Logic - ALS-007 and ALS-1X0 chips (read <file:Documentation/sound/oss/ALS>) and - for cards based on ESS chips (read - <file:Documentation/sound/oss/ESS1868> and - <file:Documentation/sound/oss/ESS>). If you have an IBM Mwave - card, say Y here and read <file:Documentation/sound/oss/mwave>. - - If you compile the driver into the kernel and don't want to use - isapnp, you have to add "sb=<io>,<irq>,<dma>,<dma2>" to the kernel - command line. - - You can say M here to compile this driver as a module; the module is - called sb. - -config SOUND_YM3812 - tristate "Yamaha FM synthesizer (YM3812/OPL-3) support" - ---help--- - Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). - Answering Y is usually a safe and recommended choice, however some - cards may have software (TSR) FM emulation. Enabling FM support with - these cards may cause trouble (I don't currently know of any such - cards, however). Please read the file - <file:Documentation/sound/oss/OPL3> if your card has an OPL3 chip. - - If you compile the driver into the kernel, you have to add - "opl3=<io>" to the kernel command line. - - If unsure, say Y. - -config SOUND_UART6850 - tristate "6850 UART support" - help - This option enables support for MIDI interfaces based on the 6850 - UART chip. This interface is rarely found on sound cards. It's safe - to answer N to this question. - - If you compile the driver into the kernel, you have to add - "uart6850=<io>,<irq>" to the kernel command line. - -config SOUND_AEDSP16 - tristate "Gallant Audio Cards (SC-6000 and SC-6600 based)" - ---help--- - Answer Y if you have a Gallant's Audio Excel DSP 16 card. This - driver supports Audio Excel DSP 16 but not the III nor PnP versions - of this card. - - The Gallant's Audio Excel DSP 16 card can emulate either an SBPro or - a Microsoft Sound System card, so you should have said Y to either - "100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support" - or "Microsoft Sound System support", above, and you need to answer - the "MSS emulation" and "SBPro emulation" questions below - accordingly. You should say Y to one and only one of these two - questions. - - Read the <file:Documentation/sound/oss/README.OSS> file and the head of - <file:sound/oss/aedsp16.c> as well as - <file:Documentation/sound/oss/AudioExcelDSP16> to get more information - about this driver and its configuration. - -config SC6600 - bool "SC-6600 based audio cards (new Audio Excel DSP 16)" - depends on SOUND_AEDSP16 - help - The SC6600 is the new version of DSP mounted on the Audio Excel DSP - 16 cards. Find in the manual the FCC ID of your audio card and - answer Y if you have an SC6600 DSP. - -config SC6600_JOY - bool "Activate SC-6600 Joystick Interface" - depends on SC6600 - help - Say Y here in order to use the joystick interface of the Audio Excel - DSP 16 card. - -config SC6600_CDROM - int "SC-6600 CDROM Interface (4=None, 3=IDE, 1=Panasonic, 0=?Sony?)" - depends on SC6600 - default "4" - help - This is used to activate the CD-ROM interface of the Audio Excel - DSP 16 card. Enter: 0 for Sony, 1 for Panasonic, 2 for IDE, 4 for no - CD-ROM present. - -config SC6600_CDROMBASE - hex "SC-6600 CDROM Interface I/O Address" - depends on SC6600 - default "0" - help - Base I/O port address for the CD-ROM interface of the Audio Excel - DSP 16 card. - -config SOUND_VIDC - tristate "VIDC 16-bit sound" - depends on ARM && ARCH_ACORN - help - 16-bit support for the VIDC onboard sound hardware found on Acorn - machines. - -config SOUND_WAVEARTIST - tristate "Netwinder WaveArtist" - depends on ARM && ARCH_NETWINDER - help - Say Y here to include support for the Rockwell WaveArtist sound - system. This driver is mainly for the NetWinder. - -config SOUND_KAHLUA - tristate "XpressAudio Sound Blaster emulation" - depends on SOUND_SB - -endif # SOUND_OSS - diff --git a/sound/oss/Makefile b/sound/oss/Makefile deleted file mode 100644 index 6564eace4749..000000000000 --- a/sound/oss/Makefile +++ /dev/null @@ -1,108 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# Makefile for the Linux sound card driver -# -# 18 Apr 1998, Michael Elizabeth Chastain, <mailto:mec@shout.net> -# Rewritten to use lists instead of if-statements. - -# Each configuration option enables a list of files. - -obj-$(CONFIG_SOUND_OSS) += sound.o - -# Please leave it as is, cause the link order is significant ! - -obj-$(CONFIG_SOUND_AEDSP16) += aedsp16.o -obj-$(CONFIG_SOUND_PSS) += pss.o ad1848.o mpu401.o -obj-$(CONFIG_SOUND_TRIX) += trix.o ad1848.o sb_lib.o uart401.o -obj-$(CONFIG_SOUND_MSS) += ad1848.o -obj-$(CONFIG_SOUND_PAS) += pas2.o sb.o sb_lib.o uart401.o -obj-$(CONFIG_SOUND_SB) += sb.o sb_lib.o uart401.o -obj-$(CONFIG_SOUND_KAHLUA) += kahlua.o -obj-$(CONFIG_SOUND_MPU401) += mpu401.o -obj-$(CONFIG_SOUND_UART6850) += uart6850.o -obj-$(CONFIG_SOUND_YM3812) += opl3.o -obj-$(CONFIG_SOUND_VMIDI) += v_midi.o -obj-$(CONFIG_SOUND_VIDC) += vidc_mod.o -obj-$(CONFIG_SOUND_WAVEARTIST) += waveartist.o -obj-$(CONFIG_SOUND_MSNDCLAS) += msnd.o msnd_classic.o -obj-$(CONFIG_SOUND_MSNDPIN) += msnd.o msnd_pinnacle.o -obj-$(CONFIG_SOUND_BCM_CS4297A) += swarm_cs4297a.o - -obj-$(CONFIG_DMASOUND) += dmasound/ - -# Declare multi-part drivers. - -sound-objs := \ - dev_table.o soundcard.o \ - audio.o dmabuf.o \ - midi_synth.o midibuf.o \ - sequencer.o sound_timer.o sys_timer.o - -pas2-objs := pas2_card.o pas2_midi.o pas2_mixer.o pas2_pcm.o -sb-objs := sb_card.o -sb_lib-objs := sb_common.o sb_audio.o sb_midi.o sb_mixer.o sb_ess.o -vidc_mod-objs := vidc.o vidc_fill.o - -hostprogs-y := bin2hex hex2hex - -# Files generated that shall be removed upon make clean -clean-files := msndperm.c msndinit.c pndsperm.c pndspini.c \ - pss_boot.h trix_boot.h - -# Firmware files that need translation -# -# The translated files are protected by a file that keeps track -# of what name was used to build them. If the name changes, they -# will be forced to be remade. -# - -# Turtle Beach MultiSound - -ifeq ($(CONFIG_MSNDCLAS_HAVE_BOOT),y) - $(obj)/msnd_classic.o: $(obj)/msndperm.c $(obj)/msndinit.c - - $(obj)/msndperm.c: $(patsubst "%", %, $(CONFIG_MSNDCLAS_PERM_FILE)) $(obj)/bin2hex - $(obj)/bin2hex msndperm < $< > $@ - - $(obj)/msndinit.c: $(patsubst "%", %, $(CONFIG_MSNDCLAS_INIT_FILE)) $(obj)/bin2hex - $(obj)/bin2hex msndinit < $< > $@ -endif - -ifeq ($(CONFIG_MSNDPIN_HAVE_BOOT),y) - $(obj)/msnd_pinnacle.o: $(obj)/pndsperm.c $(obj)/pndspini.c - - $(obj)/pndsperm.c: $(patsubst "%", %, $(CONFIG_MSNDPIN_PERM_FILE)) $(obj)/bin2hex - $(obj)/bin2hex pndsperm < $< > $@ - - $(obj)/pndspini.c: $(patsubst "%", %, $(CONFIG_MSNDPIN_INIT_FILE)) $(obj)/bin2hex - $(obj)/bin2hex pndspini < $< > $@ -endif - -# PSS (ECHO-ADI2111) - -$(obj)/pss.o: $(obj)/pss_boot.h - -ifeq ($(CONFIG_PSS_HAVE_BOOT),y) - $(obj)/pss_boot.h: $(patsubst "%", %, $(CONFIG_PSS_BOOT_FILE)) $(obj)/bin2hex - $(obj)/bin2hex pss_synth < $< > $@ -else - $(obj)/pss_boot.h: - $(Q)( \ - echo 'static unsigned char * pss_synth = NULL;'; \ - echo 'static int pss_synthLen = 0;'; \ - ) > $@ -endif - -# MediaTrix AudioTrix Pro - -$(obj)/trix.o: $(obj)/trix_boot.h - -ifeq ($(CONFIG_TRIX_HAVE_BOOT),y) - $(obj)/trix_boot.h: $(patsubst "%", %, $(CONFIG_TRIX_BOOT_FILE)) $(obj)/hex2hex - $(obj)/hex2hex -i trix_boot < $< > $@ -else - $(obj)/trix_boot.h: - $(Q)( \ - echo 'static unsigned char * trix_boot = NULL;'; \ - echo 'static int trix_boot_len = 0;'; \ - ) > $@ -endif diff --git a/sound/oss/README.FIRST b/sound/oss/README.FIRST deleted file mode 100644 index 90fdcf063d2d..000000000000 --- a/sound/oss/README.FIRST +++ /dev/null @@ -1,6 +0,0 @@ -The modular sound driver patches were funded by Red Hat Software -(www.redhat.com). The sound driver here is thus a modified version of -Hannu's code. Please bear that in mind when considering the appropriate -forums for bug reporting. - -Alan Cox diff --git a/sound/oss/ad1848.c b/sound/oss/ad1848.c deleted file mode 100644 index 2421f59cf279..000000000000 --- a/sound/oss/ad1848.c +++ /dev/null @@ -1,3062 +0,0 @@ -/* - * sound/oss/ad1848.c - * - * The low level driver for the AD1848/CS4248 codec chip which - * is used for example in the MS Sound System. - * - * The CS4231 which is used in the GUS MAX and some other cards is - * upwards compatible with AD1848 and this driver is able to drive it. - * - * CS4231A and AD1845 are upward compatible with CS4231. However - * the new features of these chips are different. - * - * CS4232 is a PnP audio chip which contains a CS4231A (and SB, MPU). - * CS4232A is an improved version of CS4232. - * - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - * general sleep/wakeup clean up. - * Alan Cox : reformatted. Fixed SMP bugs. Moved to kernel alloc/free - * of irqs. Use dev_id. - * Christoph Hellwig : adapted to module_init/module_exit - * Aki Laukkanen : added power management support - * Arnaldo C. de Melo : added missing restore_flags in ad1848_resume - * Miguel Freitas : added ISA PnP support - * Alan Cox : Added CS4236->4239 identification - * Daniel T. Cobra : Alernate config/mixer for later chips - * Alan Cox : Merged chip idents and config code - * - * TODO - * APM save restore assist code on IBM thinkpad - * - * Status: - * Tested. Believed fully functional. - */ - -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/module.h> -#include <linux/stddef.h> -#include <linux/slab.h> -#include <linux/isapnp.h> -#include <linux/pnp.h> -#include <linux/spinlock.h> - -#include "sound_config.h" - -#include "ad1848.h" -#include "ad1848_mixer.h" - -typedef struct -{ - spinlock_t lock; - int base; - int irq; - int dma1, dma2; - int dual_dma; /* 1, when two DMA channels allocated */ - int subtype; - unsigned char MCE_bit; - unsigned char saved_regs[64]; /* Includes extended register space */ - int debug_flag; - - int audio_flags; - int record_dev, playback_dev; - - int xfer_count; - int audio_mode; - int open_mode; - int intr_active; - char *chip_name, *name; - int model; -#define MD_1848 1 -#define MD_4231 2 -#define MD_4231A 3 -#define MD_1845 4 -#define MD_4232 5 -#define MD_C930 6 -#define MD_IWAVE 7 -#define MD_4235 8 /* Crystal Audio CS4235 */ -#define MD_1845_SSCAPE 9 /* Ensoniq Soundscape PNP*/ -#define MD_4236 10 /* 4236 and higher */ -#define MD_42xB 11 /* CS 42xB */ -#define MD_4239 12 /* CS4239 */ - - /* Mixer parameters */ - int recmask; - int supported_devices, orig_devices; - int supported_rec_devices, orig_rec_devices; - int *levels; - short mixer_reroute[32]; - int dev_no; - volatile unsigned long timer_ticks; - int timer_running; - int irq_ok; - mixer_ents *mix_devices; - int mixer_output_port; -} ad1848_info; - -typedef struct ad1848_port_info -{ - int open_mode; - int speed; - unsigned char speed_bits; - int channels; - int audio_format; - unsigned char format_bits; -} -ad1848_port_info; - -static struct address_info cfg; -static int nr_ad1848_devs; - -static bool deskpro_xl; -static bool deskpro_m; -static bool soundpro; - -#ifndef EXCLUDE_TIMERS -static int timer_installed = -1; -#endif - -static int loaded; - -static int ad_format_mask[13 /*devc->model */ ] = -{ - 0, - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* AD1845 */ - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, - AFMT_U8 | AFMT_S16_LE /* CS4235 */, - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW /* Ensoniq Soundscape*/, - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, - AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM -}; - -static ad1848_info adev_info[MAX_AUDIO_DEV]; - -#define io_Index_Addr(d) ((d)->base) -#define io_Indexed_Data(d) ((d)->base+1) -#define io_Status(d) ((d)->base+2) -#define io_Polled_IO(d) ((d)->base+3) - -static struct { - unsigned char flags; -#define CAP_F_TIMER 0x01 -} capabilities [10 /*devc->model */ ] = { - {0} - ,{0} /* MD_1848 */ - ,{CAP_F_TIMER} /* MD_4231 */ - ,{CAP_F_TIMER} /* MD_4231A */ - ,{CAP_F_TIMER} /* MD_1845 */ - ,{CAP_F_TIMER} /* MD_4232 */ - ,{0} /* MD_C930 */ - ,{CAP_F_TIMER} /* MD_IWAVE */ - ,{0} /* MD_4235 */ - ,{CAP_F_TIMER} /* MD_1845_SSCAPE */ -}; - -#ifdef CONFIG_PNP -static int isapnp = 1; -static int isapnpjump; -static bool reverse; - -static int audio_activated; -#else -static int isapnp; -#endif - - - -static int ad1848_open(int dev, int mode); -static void ad1848_close(int dev); -static void ad1848_output_block(int dev, unsigned long buf, int count, int intrflag); -static void ad1848_start_input(int dev, unsigned long buf, int count, int intrflag); -static int ad1848_prepare_for_output(int dev, int bsize, int bcount); -static int ad1848_prepare_for_input(int dev, int bsize, int bcount); -static void ad1848_halt(int dev); -static void ad1848_halt_input(int dev); -static void ad1848_halt_output(int dev); -static void ad1848_trigger(int dev, int bits); -static irqreturn_t adintr(int irq, void *dev_id); - -#ifndef EXCLUDE_TIMERS -static int ad1848_tmr_install(int dev); -static void ad1848_tmr_reprogram(int dev); -#endif - -static int ad_read(ad1848_info * devc, int reg) -{ - int x; - int timeout = 900000; - - while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */ - timeout--; - - if(reg < 32) - { - outb(((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr(devc)); - x = inb(io_Indexed_Data(devc)); - } - else - { - int xreg, xra; - - xreg = (reg & 0xff) - 32; - xra = (((xreg & 0x0f) << 4) & 0xf0) | 0x08 | ((xreg & 0x10) >> 2); - outb(((unsigned char) (23 & 0xff) | devc->MCE_bit), io_Index_Addr(devc)); - outb(((unsigned char) (xra & 0xff)), io_Indexed_Data(devc)); - x = inb(io_Indexed_Data(devc)); - } - - return x; -} - -static void ad_write(ad1848_info * devc, int reg, int data) -{ - int timeout = 900000; - - while (timeout > 0 && inb(devc->base) == 0x80) /* Are we initializing */ - timeout--; - - if(reg < 32) - { - outb(((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr(devc)); - outb(((unsigned char) (data & 0xff)), io_Indexed_Data(devc)); - } - else - { - int xreg, xra; - - xreg = (reg & 0xff) - 32; - xra = (((xreg & 0x0f) << 4) & 0xf0) | 0x08 | ((xreg & 0x10) >> 2); - outb(((unsigned char) (23 & 0xff) | devc->MCE_bit), io_Index_Addr(devc)); - outb(((unsigned char) (xra & 0xff)), io_Indexed_Data(devc)); - outb((unsigned char) (data & 0xff), io_Indexed_Data(devc)); - } -} - -static void wait_for_calibration(ad1848_info * devc) -{ - int timeout; - - /* - * Wait until the auto calibration process has finished. - * - * 1) Wait until the chip becomes ready (reads don't return 0x80). - * 2) Wait until the ACI bit of I11 gets on and then off. - */ - - timeout = 100000; - while (timeout > 0 && inb(devc->base) == 0x80) - timeout--; - if (inb(devc->base) & 0x80) - printk(KERN_WARNING "ad1848: Auto calibration timed out(1).\n"); - - timeout = 100; - while (timeout > 0 && !(ad_read(devc, 11) & 0x20)) - timeout--; - if (!(ad_read(devc, 11) & 0x20)) - return; - - timeout = 80000; - while (timeout > 0 && (ad_read(devc, 11) & 0x20)) - timeout--; - if (ad_read(devc, 11) & 0x20) - if ((devc->model != MD_1845) && (devc->model != MD_1845_SSCAPE)) - printk(KERN_WARNING "ad1848: Auto calibration timed out(3).\n"); -} - -static void ad_mute(ad1848_info * devc) -{ - int i; - unsigned char prev; - - /* - * Save old register settings and mute output channels - */ - - for (i = 6; i < 8; i++) - { - prev = devc->saved_regs[i] = ad_read(devc, i); - } - -} - -static void ad_unmute(ad1848_info * devc) -{ -} - -static void ad_enter_MCE(ad1848_info * devc) -{ - int timeout = 1000; - unsigned short prev; - - while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */ - timeout--; - - devc->MCE_bit = 0x40; - prev = inb(io_Index_Addr(devc)); - if (prev & 0x40) - { - return; - } - outb((devc->MCE_bit), io_Index_Addr(devc)); -} - -static void ad_leave_MCE(ad1848_info * devc) -{ - unsigned char prev, acal; - int timeout = 1000; - - while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */ - timeout--; - - acal = ad_read(devc, 9); - - devc->MCE_bit = 0x00; - prev = inb(io_Index_Addr(devc)); - outb((0x00), io_Index_Addr(devc)); /* Clear the MCE bit */ - - if ((prev & 0x40) == 0) /* Not in MCE mode */ - { - return; - } - outb((0x00), io_Index_Addr(devc)); /* Clear the MCE bit */ - if (acal & 0x08) /* Auto calibration is enabled */ - wait_for_calibration(devc); -} - -static int ad1848_set_recmask(ad1848_info * devc, int mask) -{ - unsigned char recdev; - int i, n; - unsigned long flags; - - mask &= devc->supported_rec_devices; - - /* Rename the mixer bits if necessary */ - for (i = 0; i < 32; i++) - { - if (devc->mixer_reroute[i] != i) - { - if (mask & (1 << i)) - { - mask &= ~(1 << i); - mask |= (1 << devc->mixer_reroute[i]); - } - } - } - - n = 0; - for (i = 0; i < 32; i++) /* Count selected device bits */ - if (mask & (1 << i)) - n++; - - spin_lock_irqsave(&devc->lock,flags); - if (!soundpro) { - if (n == 0) - mask = SOUND_MASK_MIC; - else if (n != 1) { /* Too many devices selected */ - mask &= ~devc->recmask; /* Filter out active settings */ - - n = 0; - for (i = 0; i < 32; i++) /* Count selected device bits */ - if (mask & (1 << i)) - n++; - - if (n != 1) - mask = SOUND_MASK_MIC; - } - switch (mask) { - case SOUND_MASK_MIC: - recdev = 2; - break; - - case SOUND_MASK_LINE: - case SOUND_MASK_LINE3: - recdev = 0; - break; - - case SOUND_MASK_CD: - case SOUND_MASK_LINE1: - recdev = 1; - break; - - case SOUND_MASK_IMIX: - recdev = 3; - break; - - default: - mask = SOUND_MASK_MIC; - recdev = 2; - } - - recdev <<= 6; - ad_write(devc, 0, (ad_read(devc, 0) & 0x3f) | recdev); - ad_write(devc, 1, (ad_read(devc, 1) & 0x3f) | recdev); - } else { /* soundpro */ - unsigned char val; - int set_rec_bit; - int j; - - for (i = 0; i < 32; i++) { /* For each bit */ - if ((devc->supported_rec_devices & (1 << i)) == 0) - continue; /* Device not supported */ - - for (j = LEFT_CHN; j <= RIGHT_CHN; j++) { - if (devc->mix_devices[i][j].nbits == 0) /* Inexistent channel */ - continue; - - /* - * This is tricky: - * set_rec_bit becomes 1 if the corresponding bit in mask is set - * then it gets flipped if the polarity is inverse - */ - set_rec_bit = ((mask & (1 << i)) != 0) ^ devc->mix_devices[i][j].recpol; - - val = ad_read(devc, devc->mix_devices[i][j].recreg); - val &= ~(1 << devc->mix_devices[i][j].recpos); - val |= (set_rec_bit << devc->mix_devices[i][j].recpos); - ad_write(devc, devc->mix_devices[i][j].recreg, val); - } - } - } - spin_unlock_irqrestore(&devc->lock,flags); - - /* Rename the mixer bits back if necessary */ - for (i = 0; i < 32; i++) - { - if (devc->mixer_reroute[i] != i) - { - if (mask & (1 << devc->mixer_reroute[i])) - { - mask &= ~(1 << devc->mixer_reroute[i]); - mask |= (1 << i); - } - } - } - devc->recmask = mask; - return mask; -} - -static void oss_change_bits(ad1848_info *devc, unsigned char *regval, - unsigned char *muteval, int dev, int chn, int newval) -{ - unsigned char mask; - int shift; - int mute; - int mutemask; - int set_mute_bit; - - set_mute_bit = (newval == 0) ^ devc->mix_devices[dev][chn].mutepol; - - if (devc->mix_devices[dev][chn].polarity == 1) /* Reverse */ - newval = 100 - newval; - - mask = (1 << devc->mix_devices[dev][chn].nbits) - 1; - shift = devc->mix_devices[dev][chn].bitpos; - - if (devc->mix_devices[dev][chn].mutepos == 8) - { /* if there is no mute bit */ - mute = 0; /* No mute bit; do nothing special */ - mutemask = ~0; /* No mute bit; do nothing special */ - } - else - { - mute = (set_mute_bit << devc->mix_devices[dev][chn].mutepos); - mutemask = ~(1 << devc->mix_devices[dev][chn].mutepos); - } - - newval = (int) ((newval * mask) + 50) / 100; /* Scale it */ - *regval &= ~(mask << shift); /* Clear bits */ - *regval |= (newval & mask) << shift; /* Set new value */ - - *muteval &= mutemask; - *muteval |= mute; -} - -static int ad1848_mixer_get(ad1848_info * devc, int dev) -{ - if (!((1 << dev) & devc->supported_devices)) - return -EINVAL; - - dev = devc->mixer_reroute[dev]; - - return devc->levels[dev]; -} - -static void ad1848_mixer_set_channel(ad1848_info *devc, int dev, int value, int channel) -{ - int regoffs, muteregoffs; - unsigned char val, muteval; - unsigned long flags; - - regoffs = devc->mix_devices[dev][channel].regno; - muteregoffs = devc->mix_devices[dev][channel].mutereg; - val = ad_read(devc, regoffs); - - if (muteregoffs != regoffs) { - muteval = ad_read(devc, muteregoffs); - oss_change_bits(devc, &val, &muteval, dev, channel, value); - } - else - oss_change_bits(devc, &val, &val, dev, channel, value); - - spin_lock_irqsave(&devc->lock,flags); - ad_write(devc, regoffs, val); - devc->saved_regs[regoffs] = val; - if (muteregoffs != regoffs) { - ad_write(devc, muteregoffs, muteval); - devc->saved_regs[muteregoffs] = muteval; - } - spin_unlock_irqrestore(&devc->lock,flags); -} - -static int ad1848_mixer_set(ad1848_info * devc, int dev, int value) -{ - int left = value & 0x000000ff; - int right = (value & 0x0000ff00) >> 8; - int retvol; - - if (dev > 31) - return -EINVAL; - - if (!(devc->supported_devices & (1 << dev))) - return -EINVAL; - - dev = devc->mixer_reroute[dev]; - - if (devc->mix_devices[dev][LEFT_CHN].nbits == 0) - return -EINVAL; - - if (left > 100) - left = 100; - if (right > 100) - right = 100; - - if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0) /* Mono control */ - right = left; - - retvol = left | (right << 8); - - /* Scale volumes */ - left = mix_cvt[left]; - right = mix_cvt[right]; - - devc->levels[dev] = retvol; - - /* - * Set the left channel - */ - ad1848_mixer_set_channel(devc, dev, left, LEFT_CHN); - - /* - * Set the right channel - */ - if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0) - goto out; - ad1848_mixer_set_channel(devc, dev, right, RIGHT_CHN); - - out: - return retvol; -} - -static void ad1848_mixer_reset(ad1848_info * devc) -{ - int i; - char name[32]; - unsigned long flags; - - devc->mix_devices = &(ad1848_mix_devices[0]); - - sprintf(name, "%s_%d", devc->chip_name, nr_ad1848_devs); - - for (i = 0; i < 32; i++) - devc->mixer_reroute[i] = i; - - devc->supported_rec_devices = MODE1_REC_DEVICES; - - switch (devc->model) - { - case MD_4231: - case MD_4231A: - case MD_1845: - case MD_1845_SSCAPE: - devc->supported_devices = MODE2_MIXER_DEVICES; - break; - - case MD_C930: - devc->supported_devices = C930_MIXER_DEVICES; - devc->mix_devices = &(c930_mix_devices[0]); - break; - - case MD_IWAVE: - devc->supported_devices = MODE3_MIXER_DEVICES; - devc->mix_devices = &(iwave_mix_devices[0]); - break; - - case MD_42xB: - case MD_4239: - devc->mix_devices = &(cs42xb_mix_devices[0]); - devc->supported_devices = MODE3_MIXER_DEVICES; - break; - case MD_4232: - case MD_4235: - case MD_4236: - devc->supported_devices = MODE3_MIXER_DEVICES; - break; - - case MD_1848: - if (soundpro) { - devc->supported_devices = SPRO_MIXER_DEVICES; - devc->supported_rec_devices = SPRO_REC_DEVICES; - devc->mix_devices = &(spro_mix_devices[0]); - break; - } - - default: - devc->supported_devices = MODE1_MIXER_DEVICES; - } - - devc->orig_devices = devc->supported_devices; - devc->orig_rec_devices = devc->supported_rec_devices; - - devc->levels = load_mixer_volumes(name, default_mixer_levels, 1); - - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - { - if (devc->supported_devices & (1 << i)) - ad1848_mixer_set(devc, i, devc->levels[i]); - } - - ad1848_set_recmask(devc, SOUND_MASK_MIC); - - devc->mixer_output_port = devc->levels[31] | AUDIO_HEADPHONE | AUDIO_LINE_OUT; - - spin_lock_irqsave(&devc->lock,flags); - if (!soundpro) { - if (devc->mixer_output_port & AUDIO_SPEAKER) - ad_write(devc, 26, ad_read(devc, 26) & ~0x40); /* Unmute mono out */ - else - ad_write(devc, 26, ad_read(devc, 26) | 0x40); /* Mute mono out */ - } else { - /* - * From the "wouldn't it be nice if the mixer API had (better) - * support for custom stuff" category - */ - /* Enable surround mode and SB16 mixer */ - ad_write(devc, 16, 0x60); - } - spin_unlock_irqrestore(&devc->lock,flags); -} - -static int ad1848_mixer_ioctl(int dev, unsigned int cmd, void __user *arg) -{ - ad1848_info *devc = mixer_devs[dev]->devc; - int val; - - if (cmd == SOUND_MIXER_PRIVATE1) - { - if (get_user(val, (int __user *)arg)) - return -EFAULT; - - if (val != 0xffff) - { - unsigned long flags; - val &= (AUDIO_SPEAKER | AUDIO_HEADPHONE | AUDIO_LINE_OUT); - devc->mixer_output_port = val; - val |= AUDIO_HEADPHONE | AUDIO_LINE_OUT; /* Always on */ - devc->mixer_output_port = val; - spin_lock_irqsave(&devc->lock,flags); - if (val & AUDIO_SPEAKER) - ad_write(devc, 26, ad_read(devc, 26) & ~0x40); /* Unmute mono out */ - else - ad_write(devc, 26, ad_read(devc, 26) | 0x40); /* Mute mono out */ - spin_unlock_irqrestore(&devc->lock,flags); - } - val = devc->mixer_output_port; - return put_user(val, (int __user *)arg); - } - if (cmd == SOUND_MIXER_PRIVATE2) - { - if (get_user(val, (int __user *)arg)) - return -EFAULT; - return(ad1848_control(AD1848_MIXER_REROUTE, val)); - } - if (((cmd >> 8) & 0xff) == 'M') - { - if (_SIOC_DIR(cmd) & _SIOC_WRITE) - { - switch (cmd & 0xff) - { - case SOUND_MIXER_RECSRC: - if (get_user(val, (int __user *)arg)) - return -EFAULT; - val = ad1848_set_recmask(devc, val); - break; - - default: - if (get_user(val, (int __user *)arg)) - return -EFAULT; - val = ad1848_mixer_set(devc, cmd & 0xff, val); - break; - } - return put_user(val, (int __user *)arg); - } - else - { - switch (cmd & 0xff) - { - /* - * Return parameters - */ - - case SOUND_MIXER_RECSRC: - val = devc->recmask; - break; - - case SOUND_MIXER_DEVMASK: - val = devc->supported_devices; - break; - - case SOUND_MIXER_STEREODEVS: - val = devc->supported_devices; - if (devc->model != MD_C930) - val &= ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX); - break; - - case SOUND_MIXER_RECMASK: - val = devc->supported_rec_devices; - break; - - case SOUND_MIXER_CAPS: - val=SOUND_CAP_EXCL_INPUT; - break; - - default: - val = ad1848_mixer_get(devc, cmd & 0xff); - break; - } - return put_user(val, (int __user *)arg); - } - } - else - return -EINVAL; -} - -static int ad1848_set_speed(int dev, int arg) -{ - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; - - /* - * The sampling speed is encoded in the least significant nibble of I8. The - * LSB selects the clock source (0=24.576 MHz, 1=16.9344 MHz) and other - * three bits select the divisor (indirectly): - * - * The available speeds are in the following table. Keep the speeds in - * the increasing order. - */ - typedef struct - { - int speed; - unsigned char bits; - } - speed_struct; - - static speed_struct speed_table[] = - { - {5510, (0 << 1) | 1}, - {5510, (0 << 1) | 1}, - {6620, (7 << 1) | 1}, - {8000, (0 << 1) | 0}, - {9600, (7 << 1) | 0}, - {11025, (1 << 1) | 1}, - {16000, (1 << 1) | 0}, - {18900, (2 << 1) | 1}, - {22050, (3 << 1) | 1}, - {27420, (2 << 1) | 0}, - {32000, (3 << 1) | 0}, - {33075, (6 << 1) | 1}, - {37800, (4 << 1) | 1}, - {44100, (5 << 1) | 1}, - {48000, (6 << 1) | 0} - }; - - int i, n, selected = -1; - - n = sizeof(speed_table) / sizeof(speed_struct); - - if (arg <= 0) - return portc->speed; - - if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) /* AD1845 has different timer than others */ - { - if (arg < 4000) - arg = 4000; - if (arg > 50000) - arg = 50000; - - portc->speed = arg; - portc->speed_bits = speed_table[3].bits; - return portc->speed; - } - if (arg < speed_table[0].speed) - selected = 0; - if (arg > speed_table[n - 1].speed) - selected = n - 1; - - for (i = 1 /*really */ ; selected == -1 && i < n; i++) - { - if (speed_table[i].speed == arg) - selected = i; - else if (speed_table[i].speed > arg) - { - int diff1, diff2; - - diff1 = arg - speed_table[i - 1].speed; - diff2 = speed_table[i].speed - arg; - - if (diff1 < diff2) - selected = i - 1; - else - selected = i; - } - } - if (selected == -1) - { - printk(KERN_WARNING "ad1848: Can't find speed???\n"); - selected = 3; - } - portc->speed = speed_table[selected].speed; - portc->speed_bits = speed_table[selected].bits; - return portc->speed; -} - -static short ad1848_set_channels(int dev, short arg) -{ - ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; - - if (arg != 1 && arg != 2) - return portc->channels; - - portc->channels = arg; - return arg; -} - -static unsigned int ad1848_set_bits(int dev, unsigned int arg) -{ - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; - - static struct format_tbl - { - int format; - unsigned char bits; - } - format2bits[] = - { - { - 0, 0 - } - , - { - AFMT_MU_LAW, 1 - } - , - { - AFMT_A_LAW, 3 - } - , - { - AFMT_IMA_ADPCM, 5 - } - , - { - AFMT_U8, 0 - } - , - { - AFMT_S16_LE, 2 - } - , - { - AFMT_S16_BE, 6 - } - , - { - AFMT_S8, 0 - } - , - { - AFMT_U16_LE, 0 - } - , - { - AFMT_U16_BE, 0 - } - }; - int i, n = sizeof(format2bits) / sizeof(struct format_tbl); - - if (arg == 0) - return portc->audio_format; - - if (!(arg & ad_format_mask[devc->model])) - arg = AFMT_U8; - - portc->audio_format = arg; - - for (i = 0; i < n; i++) - if (format2bits[i].format == arg) - { - if ((portc->format_bits = format2bits[i].bits) == 0) - return portc->audio_format = AFMT_U8; /* Was not supported */ - - return arg; - } - /* Still hanging here. Something must be terribly wrong */ - portc->format_bits = 0; - return portc->audio_format = AFMT_U8; -} - -static struct audio_driver ad1848_audio_driver = -{ - .owner = THIS_MODULE, - .open = ad1848_open, - .close = ad1848_close, - .output_block = ad1848_output_block, - .start_input = ad1848_start_input, - .prepare_for_input = ad1848_prepare_for_input, - .prepare_for_output = ad1848_prepare_for_output, - .halt_io = ad1848_halt, - .halt_input = ad1848_halt_input, - .halt_output = ad1848_halt_output, - .trigger = ad1848_trigger, - .set_speed = ad1848_set_speed, - .set_bits = ad1848_set_bits, - .set_channels = ad1848_set_channels -}; - -static struct mixer_operations ad1848_mixer_operations = -{ - .owner = THIS_MODULE, - .id = "SOUNDPORT", - .name = "AD1848/CS4248/CS4231", - .ioctl = ad1848_mixer_ioctl -}; - -static int ad1848_open(int dev, int mode) -{ - ad1848_info *devc; - ad1848_port_info *portc; - unsigned long flags; - - if (dev < 0 || dev >= num_audiodevs) - return -ENXIO; - - devc = (ad1848_info *) audio_devs[dev]->devc; - portc = (ad1848_port_info *) audio_devs[dev]->portc; - - /* here we don't have to protect against intr */ - spin_lock(&devc->lock); - if (portc->open_mode || (devc->open_mode & mode)) - { - spin_unlock(&devc->lock); - return -EBUSY; - } - devc->dual_dma = 0; - - if (audio_devs[dev]->flags & DMA_DUPLEX) - { - devc->dual_dma = 1; - } - devc->intr_active = 0; - devc->audio_mode = 0; - devc->open_mode |= mode; - portc->open_mode = mode; - spin_unlock(&devc->lock); - ad1848_trigger(dev, 0); - - if (mode & OPEN_READ) - devc->record_dev = dev; - if (mode & OPEN_WRITE) - devc->playback_dev = dev; -/* - * Mute output until the playback really starts. This decreases clicking (hope so). - */ - spin_lock_irqsave(&devc->lock,flags); - ad_mute(devc); - spin_unlock_irqrestore(&devc->lock,flags); - - return 0; -} - -static void ad1848_close(int dev) -{ - unsigned long flags; - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; - - devc->intr_active = 0; - ad1848_halt(dev); - - spin_lock_irqsave(&devc->lock,flags); - - devc->audio_mode = 0; - devc->open_mode &= ~portc->open_mode; - portc->open_mode = 0; - - ad_unmute(devc); - spin_unlock_irqrestore(&devc->lock,flags); -} - -static void ad1848_output_block(int dev, unsigned long buf, int count, int intrflag) -{ - unsigned long flags, cnt; - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; - - cnt = count; - - if (portc->audio_format == AFMT_IMA_ADPCM) - { - cnt /= 4; - } - else - { - if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ - cnt >>= 1; - } - if (portc->channels > 1) - cnt >>= 1; - cnt--; - - if ((devc->audio_mode & PCM_ENABLE_OUTPUT) && (audio_devs[dev]->flags & DMA_AUTOMODE) && - intrflag && - cnt == devc->xfer_count) - { - devc->audio_mode |= PCM_ENABLE_OUTPUT; - devc->intr_active = 1; - return; /* - * Auto DMA mode on. No need to react - */ - } - spin_lock_irqsave(&devc->lock,flags); - - ad_write(devc, 15, (unsigned char) (cnt & 0xff)); - ad_write(devc, 14, (unsigned char) ((cnt >> 8) & 0xff)); - - devc->xfer_count = cnt; - devc->audio_mode |= PCM_ENABLE_OUTPUT; - devc->intr_active = 1; - spin_unlock_irqrestore(&devc->lock,flags); -} - -static void ad1848_start_input(int dev, unsigned long buf, int count, int intrflag) -{ - unsigned long flags, cnt; - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; - - cnt = count; - if (portc->audio_format == AFMT_IMA_ADPCM) - { - cnt /= 4; - } - else - { - if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ - cnt >>= 1; - } - if (portc->channels > 1) - cnt >>= 1; - cnt--; - - if ((devc->audio_mode & PCM_ENABLE_INPUT) && (audio_devs[dev]->flags & DMA_AUTOMODE) && - intrflag && - cnt == devc->xfer_count) - { - devc->audio_mode |= PCM_ENABLE_INPUT; - devc->intr_active = 1; - return; /* - * Auto DMA mode on. No need to react - */ - } - spin_lock_irqsave(&devc->lock,flags); - - if (devc->model == MD_1848) - { - ad_write(devc, 15, (unsigned char) (cnt & 0xff)); - ad_write(devc, 14, (unsigned char) ((cnt >> 8) & 0xff)); - } - else - { - ad_write(devc, 31, (unsigned char) (cnt & 0xff)); - ad_write(devc, 30, (unsigned char) ((cnt >> 8) & 0xff)); - } - - ad_unmute(devc); - - devc->xfer_count = cnt; - devc->audio_mode |= PCM_ENABLE_INPUT; - devc->intr_active = 1; - spin_unlock_irqrestore(&devc->lock,flags); -} - -static int ad1848_prepare_for_output(int dev, int bsize, int bcount) -{ - int timeout; - unsigned char fs, old_fs, tmp = 0; - unsigned long flags; - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; - - ad_mute(devc); - - spin_lock_irqsave(&devc->lock,flags); - fs = portc->speed_bits | (portc->format_bits << 5); - - if (portc->channels > 1) - fs |= 0x10; - - ad_enter_MCE(devc); /* Enables changes to the format select reg */ - - if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) /* Use alternate speed select registers */ - { - fs &= 0xf0; /* Mask off the rate select bits */ - - ad_write(devc, 22, (portc->speed >> 8) & 0xff); /* Speed MSB */ - ad_write(devc, 23, portc->speed & 0xff); /* Speed LSB */ - } - old_fs = ad_read(devc, 8); - - if (devc->model == MD_4232 || devc->model >= MD_4236) - { - tmp = ad_read(devc, 16); - ad_write(devc, 16, tmp | 0x30); - } - if (devc->model == MD_IWAVE) - ad_write(devc, 17, 0xc2); /* Disable variable frequency select */ - - ad_write(devc, 8, fs); - - /* - * Write to I8 starts resynchronization. Wait until it completes. - */ - - timeout = 0; - while (timeout < 100 && inb(devc->base) != 0x80) - timeout++; - timeout = 0; - while (timeout < 10000 && inb(devc->base) == 0x80) - timeout++; - - if (devc->model >= MD_4232) - ad_write(devc, 16, tmp & ~0x30); - - ad_leave_MCE(devc); /* - * Starts the calibration process. - */ - spin_unlock_irqrestore(&devc->lock,flags); - devc->xfer_count = 0; - -#ifndef EXCLUDE_TIMERS - if (dev == timer_installed && devc->timer_running) - if ((fs & 0x01) != (old_fs & 0x01)) - { - ad1848_tmr_reprogram(dev); - } -#endif - ad1848_halt_output(dev); - return 0; -} - -static int ad1848_prepare_for_input(int dev, int bsize, int bcount) -{ - int timeout; - unsigned char fs, old_fs, tmp = 0; - unsigned long flags; - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; - - if (devc->audio_mode) - return 0; - - spin_lock_irqsave(&devc->lock,flags); - fs = portc->speed_bits | (portc->format_bits << 5); - - if (portc->channels > 1) - fs |= 0x10; - - ad_enter_MCE(devc); /* Enables changes to the format select reg */ - - if ((devc->model == MD_1845) || (devc->model == MD_1845_SSCAPE)) /* Use alternate speed select registers */ - { - fs &= 0xf0; /* Mask off the rate select bits */ - - ad_write(devc, 22, (portc->speed >> 8) & 0xff); /* Speed MSB */ - ad_write(devc, 23, portc->speed & 0xff); /* Speed LSB */ - } - if (devc->model == MD_4232) - { - tmp = ad_read(devc, 16); - ad_write(devc, 16, tmp | 0x30); - } - if (devc->model == MD_IWAVE) - ad_write(devc, 17, 0xc2); /* Disable variable frequency select */ - - /* - * If mode >= 2 (CS4231), set I28. It's the capture format register. - */ - - if (devc->model != MD_1848) - { - old_fs = ad_read(devc, 28); - ad_write(devc, 28, fs); - - /* - * Write to I28 starts resynchronization. Wait until it completes. - */ - - timeout = 0; - while (timeout < 100 && inb(devc->base) != 0x80) - timeout++; - - timeout = 0; - while (timeout < 10000 && inb(devc->base) == 0x80) - timeout++; - - if (devc->model != MD_1848 && devc->model != MD_1845 && devc->model != MD_1845_SSCAPE) - { - /* - * CS4231 compatible devices don't have separate sampling rate selection - * register for recording an playback. The I8 register is shared so we have to - * set the speed encoding bits of it too. - */ - unsigned char tmp = portc->speed_bits | (ad_read(devc, 8) & 0xf0); - - ad_write(devc, 8, tmp); - /* - * Write to I8 starts resynchronization. Wait until it completes. - */ - timeout = 0; - while (timeout < 100 && inb(devc->base) != 0x80) - timeout++; - - timeout = 0; - while (timeout < 10000 && inb(devc->base) == 0x80) - timeout++; - } - } - else - { /* For AD1848 set I8. */ - - old_fs = ad_read(devc, 8); - ad_write(devc, 8, fs); - /* - * Write to I8 starts resynchronization. Wait until it completes. - */ - timeout = 0; - while (timeout < 100 && inb(devc->base) != 0x80) - timeout++; - timeout = 0; - while (timeout < 10000 && inb(devc->base) == 0x80) - timeout++; - } - - if (devc->model == MD_4232) - ad_write(devc, 16, tmp & ~0x30); - - ad_leave_MCE(devc); /* - * Starts the calibration process. - */ - spin_unlock_irqrestore(&devc->lock,flags); - devc->xfer_count = 0; - -#ifndef EXCLUDE_TIMERS - if (dev == timer_installed && devc->timer_running) - { - if ((fs & 0x01) != (old_fs & 0x01)) - { - ad1848_tmr_reprogram(dev); - } - } -#endif - ad1848_halt_input(dev); - return 0; -} - -static void ad1848_halt(int dev) -{ - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; - - unsigned char bits = ad_read(devc, 9); - - if (bits & 0x01 && (portc->open_mode & OPEN_WRITE)) - ad1848_halt_output(dev); - - if (bits & 0x02 && (portc->open_mode & OPEN_READ)) - ad1848_halt_input(dev); - devc->audio_mode = 0; -} - -static void ad1848_halt_input(int dev) -{ - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - unsigned long flags; - - if (!(ad_read(devc, 9) & 0x02)) - return; /* Capture not enabled */ - - spin_lock_irqsave(&devc->lock,flags); - - ad_mute(devc); - - { - int tmout; - - if(!isa_dma_bridge_buggy) - disable_dma(audio_devs[dev]->dmap_in->dma); - - for (tmout = 0; tmout < 100000; tmout++) - if (ad_read(devc, 11) & 0x10) - break; - ad_write(devc, 9, ad_read(devc, 9) & ~0x02); /* Stop capture */ - - if(!isa_dma_bridge_buggy) - enable_dma(audio_devs[dev]->dmap_in->dma); - devc->audio_mode &= ~PCM_ENABLE_INPUT; - } - - outb(0, io_Status(devc)); /* Clear interrupt status */ - outb(0, io_Status(devc)); /* Clear interrupt status */ - - devc->audio_mode &= ~PCM_ENABLE_INPUT; - - spin_unlock_irqrestore(&devc->lock,flags); -} - -static void ad1848_halt_output(int dev) -{ - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - unsigned long flags; - - if (!(ad_read(devc, 9) & 0x01)) - return; /* Playback not enabled */ - - spin_lock_irqsave(&devc->lock,flags); - - ad_mute(devc); - { - int tmout; - - if(!isa_dma_bridge_buggy) - disable_dma(audio_devs[dev]->dmap_out->dma); - - for (tmout = 0; tmout < 100000; tmout++) - if (ad_read(devc, 11) & 0x10) - break; - ad_write(devc, 9, ad_read(devc, 9) & ~0x01); /* Stop playback */ - - if(!isa_dma_bridge_buggy) - enable_dma(audio_devs[dev]->dmap_out->dma); - - devc->audio_mode &= ~PCM_ENABLE_OUTPUT; - } - - outb((0), io_Status(devc)); /* Clear interrupt status */ - outb((0), io_Status(devc)); /* Clear interrupt status */ - - devc->audio_mode &= ~PCM_ENABLE_OUTPUT; - - spin_unlock_irqrestore(&devc->lock,flags); -} - -static void ad1848_trigger(int dev, int state) -{ - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc; - unsigned long flags; - unsigned char tmp, old; - - spin_lock_irqsave(&devc->lock,flags); - state &= devc->audio_mode; - - tmp = old = ad_read(devc, 9); - - if (portc->open_mode & OPEN_READ) - { - if (state & PCM_ENABLE_INPUT) - tmp |= 0x02; - else - tmp &= ~0x02; - } - if (portc->open_mode & OPEN_WRITE) - { - if (state & PCM_ENABLE_OUTPUT) - tmp |= 0x01; - else - tmp &= ~0x01; - } - /* ad_mute(devc); */ - if (tmp != old) - { - ad_write(devc, 9, tmp); - ad_unmute(devc); - } - spin_unlock_irqrestore(&devc->lock,flags); -} - -static void ad1848_init_hw(ad1848_info * devc) -{ - int i; - int *init_values; - - /* - * Initial values for the indirect registers of CS4248/AD1848. - */ - static int init_values_a[] = - { - 0xa8, 0xa8, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, - 0x00, 0x0c, 0x02, 0x00, 0x8a, 0x01, 0x00, 0x00, - - /* Positions 16 to 31 just for CS4231/2 and ad1845 */ - 0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x1f, 0x40, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - - static int init_values_b[] = - { - /* - Values for the newer chips - Some of the register initialization values were changed. In - order to get rid of the click that preceded PCM playback, - calibration was disabled on the 10th byte. On that same byte, - dual DMA was enabled; on the 11th byte, ADC dithering was - enabled, since that is theoretically desirable; on the 13th - byte, Mode 3 was selected, to enable access to extended - registers. - */ - 0xa8, 0xa8, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, - 0x00, 0x00, 0x06, 0x00, 0xe0, 0x01, 0x00, 0x00, - 0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x1f, 0x40, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - - /* - * Select initialisation data - */ - - init_values = init_values_a; - if(devc->model >= MD_4236) - init_values = init_values_b; - - for (i = 0; i < 16; i++) - ad_write(devc, i, init_values[i]); - - - ad_mute(devc); /* Initialize some variables */ - ad_unmute(devc); /* Leave it unmuted now */ - - if (devc->model > MD_1848) - { - if (devc->model == MD_1845_SSCAPE) - ad_write(devc, 12, ad_read(devc, 12) | 0x50); - else - ad_write(devc, 12, ad_read(devc, 12) | 0x40); /* Mode2 = enabled */ - - if (devc->model == MD_IWAVE) - ad_write(devc, 12, 0x6c); /* Select codec mode 3 */ - - if (devc->model != MD_1845_SSCAPE) - for (i = 16; i < 32; i++) - ad_write(devc, i, init_values[i]); - - if (devc->model == MD_IWAVE) - ad_write(devc, 16, 0x30); /* Playback and capture counters enabled */ - } - if (devc->model > MD_1848) - { - if (devc->audio_flags & DMA_DUPLEX) - ad_write(devc, 9, ad_read(devc, 9) & ~0x04); /* Dual DMA mode */ - else - ad_write(devc, 9, ad_read(devc, 9) | 0x04); /* Single DMA mode */ - - if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) - ad_write(devc, 27, ad_read(devc, 27) | 0x08); /* Alternate freq select enabled */ - - if (devc->model == MD_IWAVE) - { /* Some magic Interwave specific initialization */ - ad_write(devc, 12, 0x6c); /* Select codec mode 3 */ - ad_write(devc, 16, 0x30); /* Playback and capture counters enabled */ - ad_write(devc, 17, 0xc2); /* Alternate feature enable */ - } - } - else - { - devc->audio_flags &= ~DMA_DUPLEX; - ad_write(devc, 9, ad_read(devc, 9) | 0x04); /* Single DMA mode */ - if (soundpro) - ad_write(devc, 12, ad_read(devc, 12) | 0x40); /* Mode2 = enabled */ - } - - outb((0), io_Status(devc)); /* Clear pending interrupts */ - - /* - * Toggle the MCE bit. It completes the initialization phase. - */ - - ad_enter_MCE(devc); /* In case the bit was off */ - ad_leave_MCE(devc); - - ad1848_mixer_reset(devc); -} - -int ad1848_detect(struct resource *ports, int *ad_flags, int *osp) -{ - unsigned char tmp; - ad1848_info *devc = &adev_info[nr_ad1848_devs]; - unsigned char tmp1 = 0xff, tmp2 = 0xff; - int optiC930 = 0; /* OPTi 82C930 flag */ - int interwave = 0; - int ad1847_flag = 0; - int cs4248_flag = 0; - int sscape_flag = 0; - int io_base = ports->start; - - int i; - - DDB(printk("ad1848_detect(%x)\n", io_base)); - - if (ad_flags) - { - if (*ad_flags == 0x12345678) - { - interwave = 1; - *ad_flags = 0; - } - - if (*ad_flags == 0x87654321) - { - sscape_flag = 1; - *ad_flags = 0; - } - - if (*ad_flags == 0x12345677) - { - cs4248_flag = 1; - *ad_flags = 0; - } - } - if (nr_ad1848_devs >= MAX_AUDIO_DEV) - { - printk(KERN_ERR "ad1848 - Too many audio devices\n"); - return 0; - } - spin_lock_init(&devc->lock); - devc->base = io_base; - devc->irq_ok = 0; - devc->timer_running = 0; - devc->MCE_bit = 0x40; - devc->irq = 0; - devc->open_mode = 0; - devc->chip_name = devc->name = "AD1848"; - devc->model = MD_1848; /* AD1848 or CS4248 */ - devc->levels = NULL; - devc->debug_flag = 0; - - /* - * Check that the I/O address is in use. - * - * The bit 0x80 of the base I/O port is known to be 0 after the - * chip has performed its power on initialization. Just assume - * this has happened before the OS is starting. - * - * If the I/O address is unused, it typically returns 0xff. - */ - - if (inb(devc->base) == 0xff) - { - DDB(printk("ad1848_detect: The base I/O address appears to be dead\n")); - } - - /* - * Wait for the device to stop initialization - */ - - DDB(printk("ad1848_detect() - step 0\n")); - - for (i = 0; i < 10000000; i++) - { - unsigned char x = inb(devc->base); - - if (x == 0xff || !(x & 0x80)) - break; - } - - DDB(printk("ad1848_detect() - step A\n")); - - if (inb(devc->base) == 0x80) /* Not ready. Let's wait */ - ad_leave_MCE(devc); - - if ((inb(devc->base) & 0x80) != 0x00) /* Not a AD1848 */ - { - DDB(printk("ad1848 detect error - step A (%02x)\n", (int) inb(devc->base))); - return 0; - } - - /* - * Test if it's possible to change contents of the indirect registers. - * Registers 0 and 1 are ADC volume registers. The bit 0x10 is read only - * so try to avoid using it. - */ - - DDB(printk("ad1848_detect() - step B\n")); - ad_write(devc, 0, 0xaa); - ad_write(devc, 1, 0x45); /* 0x55 with bit 0x10 clear */ - - if ((tmp1 = ad_read(devc, 0)) != 0xaa || (tmp2 = ad_read(devc, 1)) != 0x45) - { - if (tmp2 == 0x65) /* AD1847 has couple of bits hardcoded to 1 */ - ad1847_flag = 1; - else - { - DDB(printk("ad1848 detect error - step B (%x/%x)\n", tmp1, tmp2)); - return 0; - } - } - DDB(printk("ad1848_detect() - step C\n")); - ad_write(devc, 0, 0x45); - ad_write(devc, 1, 0xaa); - - if ((tmp1 = ad_read(devc, 0)) != 0x45 || (tmp2 = ad_read(devc, 1)) != 0xaa) - { - if (tmp2 == 0x8a) /* AD1847 has few bits hardcoded to 1 */ - ad1847_flag = 1; - else - { - DDB(printk("ad1848 detect error - step C (%x/%x)\n", tmp1, tmp2)); - return 0; - } - } - - /* - * The indirect register I12 has some read only bits. Let's - * try to change them. - */ - - DDB(printk("ad1848_detect() - step D\n")); - tmp = ad_read(devc, 12); - ad_write(devc, 12, (~tmp) & 0x0f); - - if ((tmp & 0x0f) != ((tmp1 = ad_read(devc, 12)) & 0x0f)) - { - DDB(printk("ad1848 detect error - step D (%x)\n", tmp1)); - return 0; - } - - /* - * NOTE! Last 4 bits of the reg I12 tell the chip revision. - * 0x01=RevB and 0x0A=RevC. - */ - - /* - * The original AD1848/CS4248 has just 15 indirect registers. This means - * that I0 and I16 should return the same value (etc.). - * However this doesn't work with CS4248. Actually it seems to be impossible - * to detect if the chip is a CS4231 or CS4248. - * Ensure that the Mode2 enable bit of I12 is 0. Otherwise this test fails - * with CS4231. - */ - - /* - * OPTi 82C930 has mode2 control bit in another place. This test will fail - * with it. Accept this situation as a possible indication of this chip. - */ - - DDB(printk("ad1848_detect() - step F\n")); - ad_write(devc, 12, 0); /* Mode2=disabled */ - - for (i = 0; i < 16; i++) - { - if ((tmp1 = ad_read(devc, i)) != (tmp2 = ad_read(devc, i + 16))) - { - DDB(printk("ad1848 detect step F(%d/%x/%x) - OPTi chip???\n", i, tmp1, tmp2)); - if (!ad1847_flag) - optiC930 = 1; - break; - } - } - - /* - * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit (0x40). - * The bit 0x80 is always 1 in CS4248 and CS4231. - */ - - DDB(printk("ad1848_detect() - step G\n")); - - if (ad_flags && *ad_flags == 400) - *ad_flags = 0; - else - ad_write(devc, 12, 0x40); /* Set mode2, clear 0x80 */ - - - if (ad_flags) - *ad_flags = 0; - - tmp1 = ad_read(devc, 12); - if (tmp1 & 0x80) - { - if (ad_flags) - *ad_flags |= AD_F_CS4248; - - devc->chip_name = "CS4248"; /* Our best knowledge just now */ - } - if (optiC930 || (tmp1 & 0xc0) == (0x80 | 0x40)) - { - /* - * CS4231 detected - is it? - * - * Verify that setting I0 doesn't change I16. - */ - - DDB(printk("ad1848_detect() - step H\n")); - ad_write(devc, 16, 0); /* Set I16 to known value */ - - ad_write(devc, 0, 0x45); - if ((tmp1 = ad_read(devc, 16)) != 0x45) /* No change -> CS4231? */ - { - ad_write(devc, 0, 0xaa); - if ((tmp1 = ad_read(devc, 16)) == 0xaa) /* Rotten bits? */ - { - DDB(printk("ad1848 detect error - step H(%x)\n", tmp1)); - return 0; - } - - /* - * Verify that some bits of I25 are read only. - */ - - DDB(printk("ad1848_detect() - step I\n")); - tmp1 = ad_read(devc, 25); /* Original bits */ - ad_write(devc, 25, ~tmp1); /* Invert all bits */ - if ((ad_read(devc, 25) & 0xe7) == (tmp1 & 0xe7)) - { - int id; - - /* - * It's at least CS4231 - */ - - devc->chip_name = "CS4231"; - devc->model = MD_4231; - - /* - * It could be an AD1845 or CS4231A as well. - * CS4231 and AD1845 report the same revision info in I25 - * while the CS4231A reports different. - */ - - id = ad_read(devc, 25); - if ((id & 0xe7) == 0x80) /* Device busy??? */ - id = ad_read(devc, 25); - if ((id & 0xe7) == 0x80) /* Device still busy??? */ - id = ad_read(devc, 25); - DDB(printk("ad1848_detect() - step J (%02x/%02x)\n", id, ad_read(devc, 25))); - - if ((id & 0xe7) == 0x80) { - /* - * It must be a CS4231 or AD1845. The register I23 of - * CS4231 is undefined and it appears to be read only. - * AD1845 uses I23 for setting sample rate. Assume - * the chip is AD1845 if I23 is changeable. - */ - - unsigned char tmp = ad_read(devc, 23); - ad_write(devc, 23, ~tmp); - - if (interwave) - { - devc->model = MD_IWAVE; - devc->chip_name = "IWave"; - } - else if (ad_read(devc, 23) != tmp) /* AD1845 ? */ - { - devc->chip_name = "AD1845"; - devc->model = MD_1845; - } - else if (cs4248_flag) - { - if (ad_flags) - *ad_flags |= AD_F_CS4248; - devc->chip_name = "CS4248"; - devc->model = MD_1848; - ad_write(devc, 12, ad_read(devc, 12) & ~0x40); /* Mode2 off */ - } - ad_write(devc, 23, tmp); /* Restore */ - } - else - { - switch (id & 0x1f) { - case 3: /* CS4236/CS4235/CS42xB/CS4239 */ - { - int xid; - ad_write(devc, 12, ad_read(devc, 12) | 0x60); /* switch to mode 3 */ - ad_write(devc, 23, 0x9c); /* select extended register 25 */ - xid = inb(io_Indexed_Data(devc)); - ad_write(devc, 12, ad_read(devc, 12) & ~0x60); /* back to mode 0 */ - switch (xid & 0x1f) - { - case 0x00: - devc->chip_name = "CS4237B(B)"; - devc->model = MD_42xB; - break; - case 0x08: - /* Seems to be a 4238 ?? */ - devc->chip_name = "CS4238"; - devc->model = MD_42xB; - break; - case 0x09: - devc->chip_name = "CS4238B"; - devc->model = MD_42xB; - break; - case 0x0b: - devc->chip_name = "CS4236B"; - devc->model = MD_4236; - break; - case 0x10: - devc->chip_name = "CS4237B"; - devc->model = MD_42xB; - break; - case 0x1d: - devc->chip_name = "CS4235"; - devc->model = MD_4235; - break; - case 0x1e: - devc->chip_name = "CS4239"; - devc->model = MD_4239; - break; - default: - printk("Chip ident is %X.\n", xid&0x1F); - devc->chip_name = "CS42xx"; - devc->model = MD_4232; - break; - } - } - break; - - case 2: /* CS4232/CS4232A */ - devc->chip_name = "CS4232"; - devc->model = MD_4232; - break; - - case 0: - if ((id & 0xe0) == 0xa0) - { - devc->chip_name = "CS4231A"; - devc->model = MD_4231A; - } - else - { - devc->chip_name = "CS4321"; - devc->model = MD_4231; - } - break; - - default: /* maybe */ - DDB(printk("ad1848: I25 = %02x/%02x\n", ad_read(devc, 25), ad_read(devc, 25) & 0xe7)); - if (optiC930) - { - devc->chip_name = "82C930"; - devc->model = MD_C930; - } - else - { - devc->chip_name = "CS4231"; - devc->model = MD_4231; - } - } - } - } - ad_write(devc, 25, tmp1); /* Restore bits */ - - DDB(printk("ad1848_detect() - step K\n")); - } - } else if (tmp1 == 0x0a) { - /* - * Is it perhaps a SoundPro CMI8330? - * If so, then we should be able to change indirect registers - * greater than I15 after activating MODE2, even though reading - * back I12 does not show it. - */ - - /* - * Let's try comparing register values - */ - for (i = 0; i < 16; i++) { - if ((tmp1 = ad_read(devc, i)) != (tmp2 = ad_read(devc, i + 16))) { - DDB(printk("ad1848 detect step H(%d/%x/%x) - SoundPro chip?\n", i, tmp1, tmp2)); - soundpro = 1; - devc->chip_name = "SoundPro CMI 8330"; - break; - } - } - } - - DDB(printk("ad1848_detect() - step L\n")); - if (ad_flags) - { - if (devc->model != MD_1848) - *ad_flags |= AD_F_CS4231; - } - DDB(printk("ad1848_detect() - Detected OK\n")); - - if (devc->model == MD_1848 && ad1847_flag) - devc->chip_name = "AD1847"; - - - if (sscape_flag == 1) - devc->model = MD_1845_SSCAPE; - - return 1; -} - -int ad1848_init (char *name, struct resource *ports, int irq, int dma_playback, - int dma_capture, int share_dma, int *osp, struct module *owner) -{ - /* - * NOTE! If irq < 0, there is another driver which has allocated the IRQ - * so that this driver doesn't need to allocate/deallocate it. - * The actually used IRQ is ABS(irq). - */ - - int my_dev; - char dev_name[100]; - int e; - - ad1848_info *devc = &adev_info[nr_ad1848_devs]; - - ad1848_port_info *portc = NULL; - - devc->irq = (irq > 0) ? irq : 0; - devc->open_mode = 0; - devc->timer_ticks = 0; - devc->dma1 = dma_playback; - devc->dma2 = dma_capture; - devc->subtype = cfg.card_subtype; - devc->audio_flags = DMA_AUTOMODE; - devc->playback_dev = devc->record_dev = 0; - if (name != NULL) - devc->name = name; - - if (name != NULL && name[0] != 0) - sprintf(dev_name, - "%s (%s)", name, devc->chip_name); - else - sprintf(dev_name, - "Generic audio codec (%s)", devc->chip_name); - - rename_region(ports, devc->name); - - conf_printf2(dev_name, devc->base, devc->irq, dma_playback, dma_capture); - - if (devc->model == MD_1848 || devc->model == MD_C930) - devc->audio_flags |= DMA_HARDSTOP; - - if (devc->model > MD_1848) - { - if (devc->dma1 == devc->dma2 || devc->dma2 == -1 || devc->dma1 == -1) - devc->audio_flags &= ~DMA_DUPLEX; - else - devc->audio_flags |= DMA_DUPLEX; - } - - portc = kmalloc(sizeof(ad1848_port_info), GFP_KERNEL); - if(portc==NULL) { - release_region(devc->base, 4); - return -1; - } - - if ((my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, - dev_name, - &ad1848_audio_driver, - sizeof(struct audio_driver), - devc->audio_flags, - ad_format_mask[devc->model], - devc, - dma_playback, - dma_capture)) < 0) - { - release_region(devc->base, 4); - kfree(portc); - return -1; - } - - audio_devs[my_dev]->portc = portc; - audio_devs[my_dev]->mixer_dev = -1; - if (owner) - audio_devs[my_dev]->d->owner = owner; - memset((char *) portc, 0, sizeof(*portc)); - - nr_ad1848_devs++; - - ad1848_init_hw(devc); - - if (irq > 0) - { - devc->dev_no = my_dev; - if (request_irq(devc->irq, adintr, 0, devc->name, - (void *)(long)my_dev) < 0) - { - printk(KERN_WARNING "ad1848: Unable to allocate IRQ\n"); - /* Don't free it either then.. */ - devc->irq = 0; - } - if (capabilities[devc->model].flags & CAP_F_TIMER) - { -#ifndef CONFIG_SMP - int x; - unsigned char tmp = ad_read(devc, 16); -#endif - - devc->timer_ticks = 0; - - ad_write(devc, 21, 0x00); /* Timer MSB */ - ad_write(devc, 20, 0x10); /* Timer LSB */ -#ifndef CONFIG_SMP - ad_write(devc, 16, tmp | 0x40); /* Enable timer */ - for (x = 0; x < 100000 && devc->timer_ticks == 0; x++); - ad_write(devc, 16, tmp & ~0x40); /* Disable timer */ - - if (devc->timer_ticks == 0) - printk(KERN_WARNING "ad1848: Interrupt test failed (IRQ%d)\n", irq); - else - { - DDB(printk("Interrupt test OK\n")); - devc->irq_ok = 1; - } -#else - devc->irq_ok = 1; -#endif - } - else - devc->irq_ok = 1; /* Couldn't test. assume it's OK */ - } else if (irq < 0) - devc->dev_no = my_dev; - -#ifndef EXCLUDE_TIMERS - if ((capabilities[devc->model].flags & CAP_F_TIMER) && - devc->irq_ok) - ad1848_tmr_install(my_dev); -#endif - - if (!share_dma) - { - if (sound_alloc_dma(dma_playback, devc->name)) - printk(KERN_WARNING "ad1848.c: Can't allocate DMA%d\n", dma_playback); - - if (dma_capture != dma_playback) - if (sound_alloc_dma(dma_capture, devc->name)) - printk(KERN_WARNING "ad1848.c: Can't allocate DMA%d\n", dma_capture); - } - - if ((e = sound_install_mixer(MIXER_DRIVER_VERSION, - dev_name, - &ad1848_mixer_operations, - sizeof(struct mixer_operations), - devc)) >= 0) - { - audio_devs[my_dev]->mixer_dev = e; - if (owner) - mixer_devs[e]->owner = owner; - } - return my_dev; -} - -int ad1848_control(int cmd, int arg) -{ - ad1848_info *devc; - unsigned long flags; - - if (nr_ad1848_devs < 1) - return -ENODEV; - - devc = &adev_info[nr_ad1848_devs - 1]; - - switch (cmd) - { - case AD1848_SET_XTAL: /* Change clock frequency of AD1845 (only ) */ - if (devc->model != MD_1845 && devc->model != MD_1845_SSCAPE) - return -EINVAL; - spin_lock_irqsave(&devc->lock,flags); - ad_enter_MCE(devc); - ad_write(devc, 29, (ad_read(devc, 29) & 0x1f) | (arg << 5)); - ad_leave_MCE(devc); - spin_unlock_irqrestore(&devc->lock,flags); - break; - - case AD1848_MIXER_REROUTE: - { - int o = (arg >> 8) & 0xff; - int n = arg & 0xff; - - if (o < 0 || o >= SOUND_MIXER_NRDEVICES) - return -EINVAL; - - if (!(devc->supported_devices & (1 << o)) && - !(devc->supported_rec_devices & (1 << o))) - return -EINVAL; - - if (n == SOUND_MIXER_NONE) - { /* Just hide this control */ - ad1848_mixer_set(devc, o, 0); /* Shut up it */ - devc->supported_devices &= ~(1 << o); - devc->supported_rec_devices &= ~(1 << o); - break; - } - - /* Make the mixer control identified by o to appear as n */ - if (n < 0 || n >= SOUND_MIXER_NRDEVICES) - return -EINVAL; - - devc->mixer_reroute[n] = o; /* Rename the control */ - if (devc->supported_devices & (1 << o)) - devc->supported_devices |= (1 << n); - if (devc->supported_rec_devices & (1 << o)) - devc->supported_rec_devices |= (1 << n); - - devc->supported_devices &= ~(1 << o); - devc->supported_rec_devices &= ~(1 << o); - } - break; - } - return 0; -} - -void ad1848_unload(int io_base, int irq, int dma_playback, int dma_capture, int share_dma) -{ - int i, mixer, dev = 0; - ad1848_info *devc = NULL; - - for (i = 0; devc == NULL && i < nr_ad1848_devs; i++) - { - if (adev_info[i].base == io_base) - { - devc = &adev_info[i]; - dev = devc->dev_no; - } - } - - if (devc != NULL) - { - kfree(audio_devs[dev]->portc); - release_region(devc->base, 4); - - if (!share_dma) - { - if (devc->irq > 0) /* There is no point in freeing irq, if it wasn't allocated */ - free_irq(devc->irq, (void *)(long)devc->dev_no); - - sound_free_dma(dma_playback); - - if (dma_playback != dma_capture) - sound_free_dma(dma_capture); - - } - mixer = audio_devs[devc->dev_no]->mixer_dev; - if(mixer>=0) - sound_unload_mixerdev(mixer); - - nr_ad1848_devs--; - for ( ; i < nr_ad1848_devs ; i++) - adev_info[i] = adev_info[i+1]; - } - else - printk(KERN_ERR "ad1848: Can't find device to be unloaded. Base=%x\n", io_base); -} - -static irqreturn_t adintr(int irq, void *dev_id) -{ - unsigned char status; - ad1848_info *devc; - int dev; - int alt_stat = 0xff; - unsigned char c930_stat = 0; - int cnt = 0; - - dev = (long)dev_id; - devc = (ad1848_info *) audio_devs[dev]->devc; - -interrupt_again: /* Jump back here if int status doesn't reset */ - - status = inb(io_Status(devc)); - - if (status == 0x80) - printk(KERN_DEBUG "adintr: Why?\n"); - if (devc->model == MD_1848) - outb((0), io_Status(devc)); /* Clear interrupt status */ - - if (status & 0x01) - { - if (devc->model == MD_C930) - { /* 82C930 has interrupt status register in MAD16 register MC11 */ - - spin_lock(&devc->lock); - - /* 0xe0e is C930 address port - * 0xe0f is C930 data port - */ - outb(11, 0xe0e); - c930_stat = inb(0xe0f); - outb((~c930_stat), 0xe0f); - - spin_unlock(&devc->lock); - - alt_stat = (c930_stat << 2) & 0x30; - } - else if (devc->model != MD_1848) - { - spin_lock(&devc->lock); - alt_stat = ad_read(devc, 24); - ad_write(devc, 24, ad_read(devc, 24) & ~alt_stat); /* Selective ack */ - spin_unlock(&devc->lock); - } - - if ((devc->open_mode & OPEN_READ) && (devc->audio_mode & PCM_ENABLE_INPUT) && (alt_stat & 0x20)) - { - DMAbuf_inputintr(devc->record_dev); - } - if ((devc->open_mode & OPEN_WRITE) && (devc->audio_mode & PCM_ENABLE_OUTPUT) && - (alt_stat & 0x10)) - { - DMAbuf_outputintr(devc->playback_dev, 1); - } - if (devc->model != MD_1848 && (alt_stat & 0x40)) /* Timer interrupt */ - { - devc->timer_ticks++; -#ifndef EXCLUDE_TIMERS - if (timer_installed == dev && devc->timer_running) - sound_timer_interrupt(); -#endif - } - } -/* - * Sometimes playback or capture interrupts occur while a timer interrupt - * is being handled. The interrupt will not be retriggered if we don't - * handle it now. Check if an interrupt is still pending and restart - * the handler in this case. - */ - if (inb(io_Status(devc)) & 0x01 && cnt++ < 4) - { - goto interrupt_again; - } - return IRQ_HANDLED; -} - -/* - * Experimental initialization sequence for the integrated sound system - * of the Compaq Deskpro M. - */ - -static int init_deskpro_m(struct address_info *hw_config) -{ - unsigned char tmp; - - if ((tmp = inb(0xc44)) == 0xff) - { - DDB(printk("init_deskpro_m: Dead port 0xc44\n")); - return 0; - } - - outb(0x10, 0xc44); - outb(0x40, 0xc45); - outb(0x00, 0xc46); - outb(0xe8, 0xc47); - outb(0x14, 0xc44); - outb(0x40, 0xc45); - outb(0x00, 0xc46); - outb(0xe8, 0xc47); - outb(0x10, 0xc44); - - return 1; -} - -/* - * Experimental initialization sequence for the integrated sound system - * of Compaq Deskpro XL. - */ - -static int init_deskpro(struct address_info *hw_config) -{ - unsigned char tmp; - - if ((tmp = inb(0xc44)) == 0xff) - { - DDB(printk("init_deskpro: Dead port 0xc44\n")); - return 0; - } - outb((tmp | 0x04), 0xc44); /* Select bank 1 */ - if (inb(0xc44) != 0x04) - { - DDB(printk("init_deskpro: Invalid bank1 signature in port 0xc44\n")); - return 0; - } - /* - * OK. It looks like a Deskpro so let's proceed. - */ - - /* - * I/O port 0xc44 Audio configuration register. - * - * bits 0xc0: Audio revision bits - * 0x00 = Compaq Business Audio - * 0x40 = MS Sound System Compatible (reset default) - * 0x80 = Reserved - * 0xc0 = Reserved - * bit 0x20: No Wait State Enable - * 0x00 = Disabled (reset default, DMA mode) - * 0x20 = Enabled (programmed I/O mode) - * bit 0x10: MS Sound System Decode Enable - * 0x00 = Decoding disabled (reset default) - * 0x10 = Decoding enabled - * bit 0x08: FM Synthesis Decode Enable - * 0x00 = Decoding Disabled (reset default) - * 0x08 = Decoding enabled - * bit 0x04 Bank select - * 0x00 = Bank 0 - * 0x04 = Bank 1 - * bits 0x03 MSS Base address - * 0x00 = 0x530 (reset default) - * 0x01 = 0x604 - * 0x02 = 0xf40 - * 0x03 = 0xe80 - */ - -#ifdef DEBUGXL - /* Debug printing */ - printk("Port 0xc44 (before): "); - outb((tmp & ~0x04), 0xc44); - printk("%02x ", inb(0xc44)); - outb((tmp | 0x04), 0xc44); - printk("%02x\n", inb(0xc44)); -#endif - - /* Set bank 1 of the register */ - tmp = 0x58; /* MSS Mode, MSS&FM decode enabled */ - - switch (hw_config->io_base) - { - case 0x530: - tmp |= 0x00; - break; - case 0x604: - tmp |= 0x01; - break; - case 0xf40: - tmp |= 0x02; - break; - case 0xe80: - tmp |= 0x03; - break; - default: - DDB(printk("init_deskpro: Invalid MSS port %x\n", hw_config->io_base)); - return 0; - } - outb((tmp & ~0x04), 0xc44); /* Write to bank=0 */ - -#ifdef DEBUGXL - /* Debug printing */ - printk("Port 0xc44 (after): "); - outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ - printk("%02x ", inb(0xc44)); - outb((tmp | 0x04), 0xc44); /* Select bank=1 */ - printk("%02x\n", inb(0xc44)); -#endif - - /* - * I/O port 0xc45 FM Address Decode/MSS ID Register. - * - * bank=0, bits 0xfe: FM synthesis Decode Compare bits 7:1 (default=0x88) - * bank=0, bit 0x01: SBIC Power Control Bit - * 0x00 = Powered up - * 0x01 = Powered down - * bank=1, bits 0xfc: MSS ID (default=0x40) - */ - -#ifdef DEBUGXL - /* Debug printing */ - printk("Port 0xc45 (before): "); - outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ - printk("%02x ", inb(0xc45)); - outb((tmp | 0x04), 0xc44); /* Select bank=1 */ - printk("%02x\n", inb(0xc45)); -#endif - - outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ - outb((0x88), 0xc45); /* FM base 7:0 = 0x88 */ - outb((tmp | 0x04), 0xc44); /* Select bank=1 */ - outb((0x10), 0xc45); /* MSS ID = 0x10 (MSS port returns 0x04) */ - -#ifdef DEBUGXL - /* Debug printing */ - printk("Port 0xc45 (after): "); - outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ - printk("%02x ", inb(0xc45)); - outb((tmp | 0x04), 0xc44); /* Select bank=1 */ - printk("%02x\n", inb(0xc45)); -#endif - - - /* - * I/O port 0xc46 FM Address Decode/Address ASIC Revision Register. - * - * bank=0, bits 0xff: FM synthesis Decode Compare bits 15:8 (default=0x03) - * bank=1, bits 0xff: Audio addressing ASIC id - */ - -#ifdef DEBUGXL - /* Debug printing */ - printk("Port 0xc46 (before): "); - outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ - printk("%02x ", inb(0xc46)); - outb((tmp | 0x04), 0xc44); /* Select bank=1 */ - printk("%02x\n", inb(0xc46)); -#endif - - outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ - outb((0x03), 0xc46); /* FM base 15:8 = 0x03 */ - outb((tmp | 0x04), 0xc44); /* Select bank=1 */ - outb((0x11), 0xc46); /* ASIC ID = 0x11 */ - -#ifdef DEBUGXL - /* Debug printing */ - printk("Port 0xc46 (after): "); - outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ - printk("%02x ", inb(0xc46)); - outb((tmp | 0x04), 0xc44); /* Select bank=1 */ - printk("%02x\n", inb(0xc46)); -#endif - - /* - * I/O port 0xc47 FM Address Decode Register. - * - * bank=0, bits 0xff: Decode enable selection for various FM address bits - * bank=1, bits 0xff: Reserved - */ - -#ifdef DEBUGXL - /* Debug printing */ - printk("Port 0xc47 (before): "); - outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ - printk("%02x ", inb(0xc47)); - outb((tmp | 0x04), 0xc44); /* Select bank=1 */ - printk("%02x\n", inb(0xc47)); -#endif - - outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ - outb((0x7c), 0xc47); /* FM decode enable bits = 0x7c */ - outb((tmp | 0x04), 0xc44); /* Select bank=1 */ - outb((0x00), 0xc47); /* Reserved bank1 = 0x00 */ - -#ifdef DEBUGXL - /* Debug printing */ - printk("Port 0xc47 (after): "); - outb((tmp & ~0x04), 0xc44); /* Select bank=0 */ - printk("%02x ", inb(0xc47)); - outb((tmp | 0x04), 0xc44); /* Select bank=1 */ - printk("%02x\n", inb(0xc47)); -#endif - - /* - * I/O port 0xc6f = Audio Disable Function Register - */ - -#ifdef DEBUGXL - printk("Port 0xc6f (before) = %02x\n", inb(0xc6f)); -#endif - - outb((0x80), 0xc6f); - -#ifdef DEBUGXL - printk("Port 0xc6f (after) = %02x\n", inb(0xc6f)); -#endif - - return 1; -} - -int probe_ms_sound(struct address_info *hw_config, struct resource *ports) -{ - unsigned char tmp; - - DDB(printk("Entered probe_ms_sound(%x, %d)\n", hw_config->io_base, hw_config->card_subtype)); - - if (hw_config->card_subtype == 1) /* Has no IRQ/DMA registers */ - { - /* check_opl3(0x388, hw_config); */ - return ad1848_detect(ports, NULL, hw_config->osp); - } - - if (deskpro_xl && hw_config->card_subtype == 2) /* Compaq Deskpro XL */ - { - if (!init_deskpro(hw_config)) - return 0; - } - - if (deskpro_m) /* Compaq Deskpro M */ - { - if (!init_deskpro_m(hw_config)) - return 0; - } - - /* - * Check if the IO port returns valid signature. The original MS Sound - * system returns 0x04 while some cards (AudioTrix Pro for example) - * return 0x00 or 0x0f. - */ - - if ((tmp = inb(hw_config->io_base + 3)) == 0xff) /* Bus float */ - { - int ret; - - DDB(printk("I/O address is inactive (%x)\n", tmp)); - if (!(ret = ad1848_detect(ports, NULL, hw_config->osp))) - return 0; - return 1; - } - DDB(printk("MSS signature = %x\n", tmp & 0x3f)); - if ((tmp & 0x3f) != 0x04 && - (tmp & 0x3f) != 0x0f && - (tmp & 0x3f) != 0x00) - { - int ret; - - MDB(printk(KERN_ERR "No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, (int) inb(hw_config->io_base + 3))); - DDB(printk("Trying to detect codec anyway but IRQ/DMA may not work\n")); - if (!(ret = ad1848_detect(ports, NULL, hw_config->osp))) - return 0; - - hw_config->card_subtype = 1; - return 1; - } - if ((hw_config->irq != 5) && - (hw_config->irq != 7) && - (hw_config->irq != 9) && - (hw_config->irq != 10) && - (hw_config->irq != 11) && - (hw_config->irq != 12)) - { - printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq); - return 0; - } - if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3) - { - printk(KERN_ERR "MSS: Bad DMA %d\n", hw_config->dma); - return 0; - } - /* - * Check that DMA0 is not in use with a 8 bit board. - */ - - if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80) - { - printk(KERN_ERR "MSS: Can't use DMA0 with a 8 bit card/slot\n"); - return 0; - } - if (hw_config->irq > 7 && hw_config->irq != 9 && inb(hw_config->io_base + 3) & 0x80) - { - printk(KERN_ERR "MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq); - return 0; - } - return ad1848_detect(ports, NULL, hw_config->osp); -} - -void attach_ms_sound(struct address_info *hw_config, struct resource *ports, struct module *owner) -{ - static signed char interrupt_bits[12] = - { - -1, -1, -1, -1, -1, 0x00, -1, 0x08, -1, 0x10, 0x18, 0x20 - }; - signed char bits; - char dma2_bit = 0; - - static char dma_bits[4] = - { - 1, 2, 0, 3 - }; - - int config_port = hw_config->io_base + 0; - int version_port = hw_config->io_base + 3; - int dma = hw_config->dma; - int dma2 = hw_config->dma2; - - if (hw_config->card_subtype == 1) /* Has no IRQ/DMA registers */ - { - hw_config->slots[0] = ad1848_init("MS Sound System", ports, - hw_config->irq, - hw_config->dma, - hw_config->dma2, 0, - hw_config->osp, - owner); - return; - } - /* - * Set the IRQ and DMA addresses. - */ - - bits = interrupt_bits[hw_config->irq]; - if (bits == -1) - { - printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq); - release_region(ports->start, 4); - release_region(ports->start - 4, 4); - return; - } - outb((bits | 0x40), config_port); - if ((inb(version_port) & 0x40) == 0) - printk(KERN_ERR "[MSS: IRQ Conflict?]\n"); - -/* - * Handle the capture DMA channel - */ - - if (dma2 != -1 && dma2 != dma) - { - if (!((dma == 0 && dma2 == 1) || - (dma == 1 && dma2 == 0) || - (dma == 3 && dma2 == 0))) - { /* Unsupported combination. Try to swap channels */ - int tmp = dma; - - dma = dma2; - dma2 = tmp; - } - if ((dma == 0 && dma2 == 1) || - (dma == 1 && dma2 == 0) || - (dma == 3 && dma2 == 0)) - { - dma2_bit = 0x04; /* Enable capture DMA */ - } - else - { - printk(KERN_WARNING "MSS: Invalid capture DMA\n"); - dma2 = dma; - } - } - else - { - dma2 = dma; - } - - hw_config->dma = dma; - hw_config->dma2 = dma2; - - outb((bits | dma_bits[dma] | dma2_bit), config_port); /* Write IRQ+DMA setup */ - - hw_config->slots[0] = ad1848_init("MS Sound System", ports, - hw_config->irq, - dma, dma2, 0, - hw_config->osp, - THIS_MODULE); -} - -void unload_ms_sound(struct address_info *hw_config) -{ - ad1848_unload(hw_config->io_base + 4, - hw_config->irq, - hw_config->dma, - hw_config->dma2, 0); - sound_unload_audiodev(hw_config->slots[0]); - release_region(hw_config->io_base, 4); -} - -#ifndef EXCLUDE_TIMERS - -/* - * Timer stuff (for /dev/music). - */ - -static unsigned int current_interval; - -static unsigned int ad1848_tmr_start(int dev, unsigned int usecs) -{ - unsigned long flags; - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - unsigned long xtal_nsecs; /* nanoseconds per xtal oscillator tick */ - unsigned long divider; - - spin_lock_irqsave(&devc->lock,flags); - - /* - * Length of the timer interval (in nanoseconds) depends on the - * selected crystal oscillator. Check this from bit 0x01 of I8. - * - * AD1845 has just one oscillator which has cycle time of 10.050 us - * (when a 24.576 MHz xtal oscillator is used). - * - * Convert requested interval to nanoseconds before computing - * the timer divider. - */ - - if (devc->model == MD_1845 || devc->model == MD_1845_SSCAPE) - xtal_nsecs = 10050; - else if (ad_read(devc, 8) & 0x01) - xtal_nsecs = 9920; - else - xtal_nsecs = 9969; - - divider = (usecs * 1000 + xtal_nsecs / 2) / xtal_nsecs; - - if (divider < 100) /* Don't allow shorter intervals than about 1ms */ - divider = 100; - - if (divider > 65535) /* Overflow check */ - divider = 65535; - - ad_write(devc, 21, (divider >> 8) & 0xff); /* Set upper bits */ - ad_write(devc, 20, divider & 0xff); /* Set lower bits */ - ad_write(devc, 16, ad_read(devc, 16) | 0x40); /* Start the timer */ - devc->timer_running = 1; - spin_unlock_irqrestore(&devc->lock,flags); - - return current_interval = (divider * xtal_nsecs + 500) / 1000; -} - -static void ad1848_tmr_reprogram(int dev) -{ - /* - * Audio driver has changed sampling rate so that a different xtal - * oscillator was selected. We have to reprogram the timer rate. - */ - - ad1848_tmr_start(dev, current_interval); - sound_timer_syncinterval(current_interval); -} - -static void ad1848_tmr_disable(int dev) -{ - unsigned long flags; - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - - spin_lock_irqsave(&devc->lock,flags); - ad_write(devc, 16, ad_read(devc, 16) & ~0x40); - devc->timer_running = 0; - spin_unlock_irqrestore(&devc->lock,flags); -} - -static void ad1848_tmr_restart(int dev) -{ - unsigned long flags; - ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; - - if (current_interval == 0) - return; - - spin_lock_irqsave(&devc->lock,flags); - ad_write(devc, 16, ad_read(devc, 16) | 0x40); - devc->timer_running = 1; - spin_unlock_irqrestore(&devc->lock,flags); -} - -static struct sound_lowlev_timer ad1848_tmr = -{ - 0, - 2, - ad1848_tmr_start, - ad1848_tmr_disable, - ad1848_tmr_restart -}; - -static int ad1848_tmr_install(int dev) -{ - if (timer_installed != -1) - return 0; /* Don't install another timer */ - - timer_installed = ad1848_tmr.dev = dev; - sound_timer_init(&ad1848_tmr, audio_devs[dev]->name); - - return 1; -} -#endif /* EXCLUDE_TIMERS */ - -EXPORT_SYMBOL(ad1848_detect); -EXPORT_SYMBOL(ad1848_init); -EXPORT_SYMBOL(ad1848_unload); -EXPORT_SYMBOL(ad1848_control); -EXPORT_SYMBOL(probe_ms_sound); -EXPORT_SYMBOL(attach_ms_sound); -EXPORT_SYMBOL(unload_ms_sound); - -static int __initdata io = -1; -static int __initdata irq = -1; -static int __initdata dma = -1; -static int __initdata dma2 = -1; -static int __initdata type = 0; - -module_param_hw(io, int, ioport, 0); /* I/O for a raw AD1848 card */ -module_param_hw(irq, int, irq, 0); /* IRQ to use */ -module_param_hw(dma, int, dma, 0); /* First DMA channel */ -module_param_hw(dma2, int, dma, 0); /* Second DMA channel */ -module_param(type, int, 0); /* Card type */ -module_param(deskpro_xl, bool, 0); /* Special magic for Deskpro XL boxen */ -module_param(deskpro_m, bool, 0); /* Special magic for Deskpro M box */ -module_param(soundpro, bool, 0); /* More special magic for SoundPro chips */ - -#ifdef CONFIG_PNP -module_param(isapnp, int, 0); -module_param(isapnpjump, int, 0); -module_param(reverse, bool, 0); -MODULE_PARM_DESC(isapnp, "When set to 0, Plug & Play support will be disabled"); -MODULE_PARM_DESC(isapnpjump, "Jumps to a specific slot in the driver's PnP table. Use the source, Luke."); -MODULE_PARM_DESC(reverse, "When set to 1, will reverse ISAPnP search order"); - -static struct pnp_dev *ad1848_dev = NULL; - -/* Please add new entries at the end of the table */ -static struct { - char *name; - unsigned short card_vendor, card_device, - vendor, function; - short mss_io, irq, dma, dma2; /* index into isapnp table */ - int type; -} ad1848_isapnp_list[] __initdata = { - {"CMI 8330 SoundPRO", - ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), - 0, 0, 0,-1, 0}, - {"CS4232 based card", - ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0000), - 0, 0, 0, 1, 0}, - {"CS4232 based card", - ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0100), - 0, 0, 0, 1, 0}, - {"OPL3-SA2 WSS mode", - ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('Y','M','H'), ISAPNP_FUNCTION(0x0021), - 1, 0, 0, 1, 1}, - {"Advanced Gravis InterWave Audio", - ISAPNP_VENDOR('G','R','V'), ISAPNP_DEVICE(0x0001), - ISAPNP_VENDOR('G','R','V'), ISAPNP_FUNCTION(0x0000), - 0, 0, 0, 1, 0}, - {NULL} -}; - -#ifdef MODULE -static struct isapnp_device_id id_table[] = { - { ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), 0 }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0000), 0 }, - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('C','S','C'), ISAPNP_FUNCTION(0x0100), 0 }, - /* The main driver for this card is opl3sa2 - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('Y','M','H'), ISAPNP_FUNCTION(0x0021), 0 }, - */ - { ISAPNP_VENDOR('G','R','V'), ISAPNP_DEVICE(0x0001), - ISAPNP_VENDOR('G','R','V'), ISAPNP_FUNCTION(0x0000), 0 }, - {0} -}; - -MODULE_DEVICE_TABLE(isapnp, id_table); -#endif - -static struct pnp_dev *activate_dev(char *devname, char *resname, struct pnp_dev *dev) -{ - int err; - - err = pnp_device_attach(dev); - if (err < 0) - return(NULL); - - if((err = pnp_activate_dev(dev)) < 0) { - printk(KERN_ERR "ad1848: %s %s config failed (out of resources?)[%d]\n", devname, resname, err); - - pnp_device_detach(dev); - - return(NULL); - } - audio_activated = 1; - return(dev); -} - -static struct pnp_dev __init *ad1848_init_generic(struct pnp_card *bus, - struct address_info *hw_config, int slot) -{ - - /* Configure Audio device */ - if((ad1848_dev = pnp_find_dev(bus, ad1848_isapnp_list[slot].vendor, ad1848_isapnp_list[slot].function, NULL))) - { - if((ad1848_dev = activate_dev(ad1848_isapnp_list[slot].name, "ad1848", ad1848_dev))) - { - hw_config->io_base = pnp_port_start(ad1848_dev, ad1848_isapnp_list[slot].mss_io); - hw_config->irq = pnp_irq(ad1848_dev, ad1848_isapnp_list[slot].irq); - hw_config->dma = pnp_dma(ad1848_dev, ad1848_isapnp_list[slot].dma); - if(ad1848_isapnp_list[slot].dma2 != -1) - hw_config->dma2 = pnp_dma(ad1848_dev, ad1848_isapnp_list[slot].dma2); - else - hw_config->dma2 = -1; - hw_config->card_subtype = ad1848_isapnp_list[slot].type; - } else - return(NULL); - } else - return(NULL); - - return(ad1848_dev); -} - -static int __init ad1848_isapnp_init(struct address_info *hw_config, struct pnp_card *bus, int slot) -{ - char *busname = bus->name[0] ? bus->name : ad1848_isapnp_list[slot].name; - - /* Initialize this baby. */ - - if(ad1848_init_generic(bus, hw_config, slot)) { - /* We got it. */ - - printk(KERN_NOTICE "ad1848: PnP reports '%s' at i/o %#x, irq %d, dma %d, %d\n", - busname, - hw_config->io_base, hw_config->irq, hw_config->dma, - hw_config->dma2); - return 1; - } - return 0; -} - -static int __init ad1848_isapnp_probe(struct address_info *hw_config) -{ - static int first = 1; - int i; - - /* Count entries in sb_isapnp_list */ - for (i = 0; ad1848_isapnp_list[i].card_vendor != 0; i++); - i--; - - /* Check and adjust isapnpjump */ - if( isapnpjump < 0 || isapnpjump > i) { - isapnpjump = reverse ? i : 0; - printk(KERN_ERR "ad1848: Valid range for isapnpjump is 0-%d. Adjusted to %d.\n", i, isapnpjump); - } - - if(!first || !reverse) - i = isapnpjump; - first = 0; - while(ad1848_isapnp_list[i].card_vendor != 0) { - static struct pnp_card *bus = NULL; - - while ((bus = pnp_find_card( - ad1848_isapnp_list[i].card_vendor, - ad1848_isapnp_list[i].card_device, - bus))) { - - if(ad1848_isapnp_init(hw_config, bus, i)) { - isapnpjump = i; /* start next search from here */ - return 0; - } - } - i += reverse ? -1 : 1; - } - - return -ENODEV; -} -#endif - - -static int __init init_ad1848(void) -{ - printk(KERN_INFO "ad1848/cs4248 codec driver Copyright (C) by Hannu Savolainen 1993-1996\n"); - -#ifdef CONFIG_PNP - if(isapnp && (ad1848_isapnp_probe(&cfg) < 0) ) { - printk(KERN_NOTICE "ad1848: No ISAPnP cards found, trying standard ones...\n"); - isapnp = 0; - } -#endif - - if(io != -1) { - struct resource *ports; - if( isapnp == 0 ) - { - if(irq == -1 || dma == -1) { - printk(KERN_WARNING "ad1848: must give I/O , IRQ and DMA.\n"); - return -EINVAL; - } - - cfg.irq = irq; - cfg.io_base = io; - cfg.dma = dma; - cfg.dma2 = dma2; - cfg.card_subtype = type; - } - - ports = request_region(io + 4, 4, "ad1848"); - - if (!ports) - return -EBUSY; - - if (!request_region(io, 4, "WSS config")) { - release_region(io + 4, 4); - return -EBUSY; - } - - if (!probe_ms_sound(&cfg, ports)) { - release_region(io + 4, 4); - release_region(io, 4); - return -ENODEV; - } - attach_ms_sound(&cfg, ports, THIS_MODULE); - loaded = 1; - } - return 0; -} - -static void __exit cleanup_ad1848(void) -{ - if(loaded) - unload_ms_sound(&cfg); - -#ifdef CONFIG_PNP - if(ad1848_dev){ - if(audio_activated) - pnp_device_detach(ad1848_dev); - } -#endif -} - -module_init(init_ad1848); -module_exit(cleanup_ad1848); - -#ifndef MODULE -static int __init setup_ad1848(char *str) -{ - /* io, irq, dma, dma2, type */ - int ints[6]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - dma = ints[3]; - dma2 = ints[4]; - type = ints[5]; - - return 1; -} - -__setup("ad1848=", setup_ad1848); -#endif -MODULE_LICENSE("GPL"); diff --git a/sound/oss/ad1848.h b/sound/oss/ad1848.h deleted file mode 100644 index 390f03e13d09..000000000000 --- a/sound/oss/ad1848.h +++ /dev/null @@ -1,25 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -#include <linux/interrupt.h> - -#define AD_F_CS4231 0x0001 /* Returned if a CS4232 (or compatible) detected */ -#define AD_F_CS4248 0x0001 /* Returned if a CS4248 (or compatible) detected */ - -#define AD1848_SET_XTAL 1 -#define AD1848_MIXER_REROUTE 2 - -#define AD1848_REROUTE(oldctl, newctl) \ - ad1848_control(AD1848_MIXER_REROUTE, ((oldctl)<<8)|(newctl)) - - -int ad1848_init(char *name, struct resource *ports, int irq, int dma_playback, - int dma_capture, int share_dma, int *osp, struct module *owner); -void ad1848_unload (int io_base, int irq, int dma_playback, int dma_capture, int share_dma); - -int ad1848_detect (struct resource *ports, int *flags, int *osp); -int ad1848_control(int cmd, int arg); - -void attach_ms_sound(struct address_info * hw_config, struct resource *ports, struct module * owner); - -int probe_ms_sound(struct address_info *hw_config, struct resource *ports); -void unload_ms_sound(struct address_info *hw_info); diff --git a/sound/oss/ad1848_mixer.h b/sound/oss/ad1848_mixer.h deleted file mode 100644 index 2cf719b5fbbc..000000000000 --- a/sound/oss/ad1848_mixer.h +++ /dev/null @@ -1,253 +0,0 @@ -/* - * sound/oss/ad1848_mixer.h - * - * Definitions for the mixer of AD1848 and compatible codecs. - */ - -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ - - -/* - * The AD1848 codec has generic input lines called Line, Aux1 and Aux2. - * Sound card manufacturers have connected actual inputs (CD, synth, line, - * etc) to these inputs in different order. Therefore it's difficult - * to assign mixer channels to these inputs correctly. The following - * contains two alternative mappings. The first one is for GUS MAX and - * the second is just a generic one (line1, line2 and line3). - * (Actually this is not a mapping but rather some kind of interleaving - * solution). - */ -#define MODE1_REC_DEVICES (SOUND_MASK_LINE3 | SOUND_MASK_MIC | \ - SOUND_MASK_LINE1 | SOUND_MASK_IMIX) - -#define SPRO_REC_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | \ - SOUND_MASK_CD | SOUND_MASK_LINE1) - -#define MODE1_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_MIC | \ - SOUND_MASK_LINE2 | \ - SOUND_MASK_IGAIN | \ - SOUND_MASK_PCM | SOUND_MASK_IMIX) - -#define MODE2_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | \ - SOUND_MASK_MIC | \ - SOUND_MASK_LINE3 | SOUND_MASK_SPEAKER | \ - SOUND_MASK_IGAIN | \ - SOUND_MASK_PCM | SOUND_MASK_IMIX) - -#define MODE3_MIXER_DEVICES (MODE2_MIXER_DEVICES | SOUND_MASK_VOLUME) - -/* OPTi 82C930 has no IMIX level control, but it can still be selected as an - * input - */ -#define C930_MIXER_DEVICES (SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | \ - SOUND_MASK_MIC | SOUND_MASK_VOLUME | \ - SOUND_MASK_LINE3 | \ - SOUND_MASK_IGAIN | SOUND_MASK_PCM) - -#define SPRO_MIXER_DEVICES (SOUND_MASK_VOLUME | SOUND_MASK_PCM | \ - SOUND_MASK_LINE | SOUND_MASK_SYNTH | \ - SOUND_MASK_CD | SOUND_MASK_MIC | \ - SOUND_MASK_SPEAKER | SOUND_MASK_LINE1 | \ - SOUND_MASK_OGAIN) - -struct mixer_def { - unsigned int regno:6; /* register number for volume */ - unsigned int polarity:1; /* volume polarity: 0=normal, 1=reversed */ - unsigned int bitpos:3; /* position of bits in register for volume */ - unsigned int nbits:3; /* number of bits in register for volume */ - unsigned int mutereg:6; /* register number for mute bit */ - unsigned int mutepol:1; /* mute polarity: 0=normal, 1=reversed */ - unsigned int mutepos:4; /* position of mute bit in register */ - unsigned int recreg:6; /* register number for recording bit */ - unsigned int recpol:1; /* recording polarity: 0=normal, 1=reversed */ - unsigned int recpos:4; /* position of recording bit in register */ -}; - -static char mix_cvt[101] = { - 0, 0, 3, 7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42, - 43,45,46,47,49,50,51,52,53,55,56,57,58,59,60,61,62,63,64,65, - 65,66,67,68,69,70,70,71,72,73,73,74,75,75,76,77,77,78,79,79, - 80,81,81,82,82,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90, - 91,91,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,98,99,99, - 100 -}; - -typedef struct mixer_def mixer_ent; -typedef mixer_ent mixer_ents[2]; - -/* - * Most of the mixer entries work in backwards. Setting the polarity field - * makes them to work correctly. - * - * The channel numbering used by individual sound cards is not fixed. Some - * cards have assigned different meanings for the AUX1, AUX2 and LINE inputs. - * The current version doesn't try to compensate this. - */ - -#define MIX_ENT(name, reg_l, pola_l, pos_l, len_l, reg_r, pola_r, pos_r, len_r, mute_bit) \ - [name] = {{reg_l, pola_l, pos_l, len_l, reg_l, 0, mute_bit, 0, 0, 8}, \ - {reg_r, pola_r, pos_r, len_r, reg_r, 0, mute_bit, 0, 0, 8}} - -#define MIX_ENT2(name, reg_l, pola_l, pos_l, len_l, mute_reg_l, mute_pola_l, mute_pos_l, \ - rec_reg_l, rec_pola_l, rec_pos_l, \ - reg_r, pola_r, pos_r, len_r, mute_reg_r, mute_pola_r, mute_pos_r, \ - rec_reg_r, rec_pola_r, rec_pos_r) \ - [name] = {{reg_l, pola_l, pos_l, len_l, mute_reg_l, mute_pola_l, mute_pos_l, \ - rec_reg_l, rec_pola_l, rec_pos_l}, \ - {reg_r, pola_r, pos_r, len_r, mute_reg_r, mute_pola_r, mute_pos_r, \ - rec_reg_r, rec_pola_r, rec_pos_r}} - -static mixer_ents ad1848_mix_devices[32] = { - MIX_ENT(SOUND_MIXER_VOLUME, 27, 1, 0, 4, 29, 1, 0, 4, 8), - MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7), - MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1, 8), - MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8), - MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5, 7) -}; - -static mixer_ents iwave_mix_devices[32] = { - MIX_ENT(SOUND_MIXER_VOLUME, 25, 1, 0, 5, 27, 1, 0, 5, 8), - MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7), - MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1, 8), - MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_IMIX, 16, 1, 0, 5, 17, 1, 0, 5, 8), - MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8), - MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5, 7) -}; - -static mixer_ents cs42xb_mix_devices[32] = { - /* Digital master volume actually has seven bits, but we only use - six to avoid the discontinuity when the analog gain kicks in. */ - MIX_ENT(SOUND_MIXER_VOLUME, 46, 1, 0, 6, 47, 1, 0, 6, 7), - MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6, 7), - MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_MIC, 34, 1, 0, 5, 35, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5, 7), - /* For the IMIX entry, it was not possible to use the MIX_ENT macro - because the mute bit is in different positions for the two - channels and requires reverse polarity. */ - [SOUND_MIXER_IMIX] = {{13, 1, 2, 6, 13, 1, 0, 0, 0, 8}, - {42, 1, 0, 6, 42, 1, 7, 0, 0, 8}}, - MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8), - MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_LINE3, 38, 1, 0, 6, 39, 1, 0, 6, 7) -}; - -/* OPTi 82C930 has somewhat different port addresses. - * Note: VOLUME == SPEAKER, SYNTH == LINE2, LINE == LINE3, CD == LINE1 - * VOLUME, SYNTH, LINE, CD are not enabled above. - * MIC is level of mic monitoring direct to output. Same for CD, LINE, etc. - */ -static mixer_ents c930_mix_devices[32] = { - MIX_ENT(SOUND_MIXER_VOLUME, 22, 1, 1, 5, 23, 1, 1, 5, 7), - MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 5, 1, 1, 4, 7), - MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 5, 7, 1, 0, 5, 7), - MIX_ENT(SOUND_MIXER_SPEAKER, 22, 1, 1, 5, 23, 1, 1, 5, 7), - MIX_ENT(SOUND_MIXER_LINE, 18, 1, 1, 4, 19, 1, 1, 4, 7), - MIX_ENT(SOUND_MIXER_MIC, 20, 1, 1, 4, 21, 1, 1, 4, 7), - MIX_ENT(SOUND_MIXER_CD, 2, 1, 1, 4, 3, 1, 1, 4, 7), - MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4, 8), - MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 1, 4, 3, 1, 1, 4, 7), - MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 1, 4, 5, 1, 1, 4, 7), - MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 1, 4, 19, 1, 1, 4, 7) -}; - -static mixer_ents spro_mix_devices[32] = { - MIX_ENT (SOUND_MIXER_VOLUME, 19, 0, 4, 4, 19, 0, 0, 4, 8), - MIX_ENT (SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT (SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT2(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 23, 0, 3, 0, 0, 8, - 5, 1, 1, 4, 23, 0, 3, 0, 0, 8), - MIX_ENT (SOUND_MIXER_PCM, 6, 1, 1, 4, 7, 1, 1, 4, 8), - MIX_ENT (SOUND_MIXER_SPEAKER, 18, 0, 3, 2, 0, 0, 0, 0, 8), - MIX_ENT2(SOUND_MIXER_LINE, 20, 0, 4, 4, 17, 1, 4, 16, 0, 2, - 20, 0, 0, 4, 17, 1, 3, 16, 0, 1), - MIX_ENT2(SOUND_MIXER_MIC, 18, 0, 0, 3, 17, 1, 0, 16, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - MIX_ENT2(SOUND_MIXER_CD, 21, 0, 4, 4, 17, 1, 2, 16, 0, 4, - 21, 0, 0, 4, 17, 1, 1, 16, 0, 3), - MIX_ENT (SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT (SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT (SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT (SOUND_MIXER_IGAIN, 0, 0, 0, 0, 0, 0, 0, 0, 8), - MIX_ENT (SOUND_MIXER_OGAIN, 17, 1, 6, 1, 0, 0, 0, 0, 8), - /* This is external wavetable */ - MIX_ENT2(SOUND_MIXER_LINE1, 22, 0, 4, 4, 23, 1, 1, 23, 0, 4, - 22, 0, 0, 4, 23, 1, 0, 23, 0, 5), -}; - -static int default_mixer_levels[32] = -{ - 0x3232, /* Master Volume */ - 0x3232, /* Bass */ - 0x3232, /* Treble */ - 0x4b4b, /* FM */ - 0x3232, /* PCM */ - 0x1515, /* PC Speaker */ - 0x2020, /* Ext Line */ - 0x1010, /* Mic */ - 0x4b4b, /* CD */ - 0x0000, /* Recording monitor */ - 0x4b4b, /* Second PCM */ - 0x4b4b, /* Recording level */ - 0x4b4b, /* Input gain */ - 0x4b4b, /* Output gain */ - 0x2020, /* Line1 */ - 0x2020, /* Line2 */ - 0x1515 /* Line3 (usually line in)*/ -}; - -#define LEFT_CHN 0 -#define RIGHT_CHN 1 - -/* - * Channel enable bits for ioctl(SOUND_MIXER_PRIVATE1) - */ - -#ifndef AUDIO_SPEAKER -#define AUDIO_SPEAKER 0x01 /* Enable mono output */ -#define AUDIO_HEADPHONE 0x02 /* Sparc only */ -#define AUDIO_LINE_OUT 0x04 /* Sparc only */ -#endif diff --git a/sound/oss/aedsp16.c b/sound/oss/aedsp16.c deleted file mode 100644 index f058ed6bdb69..000000000000 --- a/sound/oss/aedsp16.c +++ /dev/null @@ -1,1373 +0,0 @@ -/* - sound/oss/aedsp16.c - - Audio Excel DSP 16 software configuration routines - Copyright (C) 1995,1996,1997,1998 Riccardo Facchetti (fizban@tin.it) - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. - - */ -/* - * Include the main OSS Lite header file. It include all the os, OSS Lite, etc - * headers needed by this source. - */ -#include <linux/delay.h> -#include <linux/module.h> -#include <linux/init.h> -#include "sound_config.h" - -/* - - READ THIS - - This module started to configure the Audio Excel DSP 16 Sound Card. - Now works with the SC-6000 (old aedsp16) and new SC-6600 based cards. - - NOTE: I have NO idea about Audio Excel DSP 16 III. If someone owns this - audio card and want to see the kernel support for it, please contact me. - - Audio Excel DSP 16 is an SB pro II, Microsoft Sound System and MPU-401 - compatible card. - It is software-only configurable (no jumpers to hard-set irq/dma/mpu-irq), - so before this module, the only way to configure the DSP under linux was - boot the MS-DOS loading the sound.sys device driver (this driver soft- - configure the sound board hardware by massaging someone of its registers), - and then ctrl-alt-del to boot linux with the DSP configured by the DOS - driver. - - This module works configuring your Audio Excel DSP 16's irq, dma and - mpu-401-irq. The OSS Lite routines rely on the fact that if the - hardware is there, they can detect it. The problem with AEDSP16 is - that no hardware can be found by the probe routines if the sound card - is not configured properly. Sometimes the kernel probe routines can find - an SBPRO even when the card is not configured (this is the standard setup - of the card), but the SBPRO emulation don't work well if the card is not - properly initialized. For this reason - - aedsp16_init_board() - - routine is called before the OSS Lite probe routines try to detect the - hardware. - - NOTE (READ THE NOTE TOO, IT CONTAIN USEFUL INFORMATIONS) - - NOTE: Now it works with SC-6000 and SC-6600 based audio cards. The new cards - have no jumper switch at all. No more WSS or MPU-401 I/O port switches. They - have to be configured by software. - - NOTE: The driver is merged with the new OSS Lite sound driver. It works - as a lowlevel driver. - - The Audio Excel DSP 16 Sound Card emulates both SBPRO and MSS; - the OSS Lite sound driver can be configured for SBPRO and MSS cards - at the same time, but the aedsp16 can't be two cards!! - When we configure it, we have to choose the SBPRO or the MSS emulation - for AEDSP16. We also can install a *REAL* card of the other type (see [1]). - - NOTE: If someone can test the combination AEDSP16+MSS or AEDSP16+SBPRO - please let me know if it works. - - The MPU-401 support can be compiled in together with one of the other - two operating modes. - - NOTE: This is something like plug-and-play: we have only to plug - the AEDSP16 board in the socket, and then configure and compile - a kernel that uses the AEDSP16 software configuration capability. - No jumper setting is needed! - - For example, if you want AEDSP16 to be an SBPro, on irq 10, dma 3 - you have just to make config the OSS Lite package, configuring - the AEDSP16 sound card, then activating the SBPro emulation mode - and at last configuring IRQ and DMA. - Compile the kernel and run it. - - NOTE: This means for SC-6000 cards that you can choose irq and dma, - but not the I/O addresses. To change I/O addresses you have to set - them with jumpers. For SC-6600 cards you have no jumpers so you have - to set up your full card configuration in the make config. - - You can change the irq/dma/mirq settings WITHOUT THE NEED to open - your computer and massage the jumpers (there are no irq/dma/mirq - jumpers to be configured anyway, only I/O BASE values have to be - configured with jumpers) - - For some ununderstandable reason, the card default of irq 7, dma 1, - don't work for me. Seems to be an IRQ or DMA conflict. Under heavy - HDD work, the kernel start to erupt out a lot of messages like: - - 'Sound: DMA timed out - IRQ/DRQ config error?' - - For what I can say, I have NOT any conflict at irq 7 (under linux I'm - using the lp polling driver), and dma line 1 is unused as stated by - /proc/dma. I can suppose this is a bug of AEDSP16. I know my hardware so - I'm pretty sure I have not any conflict, but may be I'm wrong. Who knows! - Anyway a setting of irq 10, dma 3 works really fine. - - NOTE: if someone can use AEDSP16 with irq 7, dma 1, please let me know - the emulation mode, all the installed hardware and the hardware - configuration (irq and dma settings of all the hardware). - - This init module should work with SBPRO+MSS, when one of the two is - the AEDSP16 emulation and the other the real card. (see [1]) - For example: - - AEDSP16 (0x220) in SBPRO emu (0x220) + real MSS + other - AEDSP16 (0x220) in MSS emu + real SBPRO (0x240) + other - - MPU401 should work. (see [2]) - - [1] - --- - Date: Mon, 29 Jul 1997 08:35:40 +0100 - From: Mr S J Greenaway <sjg95@unixfe.rl.ac.uk> - - [...] - Just to let you know got my Audio Excel (emulating a MSS) working - with my original SB16, thanks for the driver! - [...] - --- - - [2] Not tested by me for lack of hardware. - - TODO, WISHES AND TECH - - - About I/O ports allocation - - - Request the 2x0h region (port base) in any case if we are using this card. - - NOTE: the "aedsp16 (base)" string with which we are requesting the aedsp16 - port base region (see code) does not mean necessarily that we are emulating - sbpro. Even if this region is the sbpro I/O ports region, we use this - region to access the control registers of the card, and if emulating - sbpro, I/O sbpro registers too. If we are emulating MSS, the sbpro - registers are not used, in no way, to emulate an sbpro: they are - used only for configuration purposes. - - Started Fri Mar 17 16:13:18 MET 1995 - - v0.1 (ALPHA, was a user-level program called AudioExcelDSP16.c) - - Initial code. - v0.2 (ALPHA) - - Cleanups. - - Integrated with Linux voxware v 2.90-2 kernel sound driver. - - SoundBlaster Pro mode configuration. - - Microsoft Sound System mode configuration. - - MPU-401 mode configuration. - v0.3 (ALPHA) - - Cleanups. - - Rearranged the code to let aedsp16_init_board be more general. - - Erased the REALLY_SLOW_IO. We don't need it. Erased the linux/io.h - inclusion too. We rely on os.h - - Used the to get a variable - len string (we are not sure about the len of Copyright string). - This works with any SB and compatible. - - Added the code to request_region at device init (should go in - the main body of voxware). - v0.4 (BETA) - - Better configure.c patch for aedsp16 configuration (better - logic of inclusion of AEDSP16 support) - - Modified the conditional compilation to better support more than - one sound card of the emulated type (read the NOTES above) - - Moved the sb init routine from the attach to the very first - probe in sb_card.c - - Rearrangements and cleanups - - Wiped out some unnecessary code and variables: this is kernel - code so it is better save some TEXT and DATA - - Fixed the request_region code. We must allocate the aedsp16 (sbpro) - I/O ports in any case because they are used to access the DSP - configuration registers and we can not allow anyone to get them. - v0.5 - - cleanups on comments - - prep for diffs against v3.0-proto-950402 - v0.6 - - removed the request_region()s when compiling the MODULE sound.o - because we are not allowed (by the actual voxware structure) to - release_region() - v0.7 (pre ALPHA, not distributed) - - started porting this module to kernel 1.3.84. Dummy probe/attach - routines. - v0.8 (ALPHA) - - attached all the init routines. - v0.9 (BETA) - - Integrated with linux-pre2.0.7 - - Integrated with configuration scripts. - - Cleaned up and beautyfied the code. - v0.9.9 (BETA) - - Thanks to Piercarlo Grandi: corrected the conditonal compilation code. - Now only the code configured is compiled in, with some memory saving. - v0.9.10 - - Integration into the sound/lowlevel/ section of the sound driver. - - Re-organized the code. - v0.9.11 (not distributed) - - Rewritten the init interface-routines to initialize the AEDSP16 in - one shot. - - More cosmetics. - - SC-6600 support. - - More soft/hard configuration. - v0.9.12 - - Refined the v0.9.11 code with conditional compilation to distinguish - between SC-6000 and SC-6600 code. - v1.0.0 - - Prep for merging with OSS Lite and Linux kernel 2.1.13 - - Corrected a bug in request/check/release region calls (thanks to the - new kernel exception handling). - v1.1 - - Revamped for integration with new modularized sound drivers: to enhance - the flexibility of modular version, I have removed all the conditional - compilation for SBPRO, MPU and MSS code. Now it is all managed with - the ae_config structure. - v1.2 - - Module informations added. - - Removed aedsp16_delay_10msec(), now using mdelay(10) - - All data and funcs moved to .*.init section. - v1.3 - Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 2000/09/27 - - got rid of check_region - - Known Problems: - - Audio Excel DSP 16 III don't work with this driver. - - Credits: - Many thanks to Gerald Britton <gbritton@CapAccess.org>. He helped me a - lot in testing the 0.9.11 and 0.9.12 versions of this driver. - - */ - - -#define VERSION "1.3" /* Version of Audio Excel DSP 16 driver */ - -#undef AEDSP16_DEBUG /* Define this to 1 to enable debug code */ -#undef AEDSP16_DEBUG_MORE /* Define this to 1 to enable more debug */ -#undef AEDSP16_INFO /* Define this to 1 to enable info code */ - -#if defined(AEDSP16_DEBUG) -# define DBG(x) printk x -# if defined(AEDSP16_DEBUG_MORE) -# define DBG1(x) printk x -# else -# define DBG1(x) -# endif -#else -# define DBG(x) -# define DBG1(x) -#endif - -/* - * Misc definitions - */ -#define TRUE 1 -#define FALSE 0 - -/* - * Region Size for request/check/release region. - */ -#define IOBASE_REGION_SIZE 0x10 - -/* - * Hardware related defaults - */ -#define DEF_AEDSP16_IOB 0x220 /* 0x220(default) 0x240 */ -#define DEF_AEDSP16_IRQ 7 /* 5 7(default) 9 10 11 */ -#define DEF_AEDSP16_MRQ 0 /* 5 7 9 10 0(default), 0 means disable */ -#define DEF_AEDSP16_DMA 1 /* 0 1(default) 3 */ - -/* - * Commands of AEDSP16's DSP (SBPRO+special). - * Some of them are COMMAND_xx, in the future they may change. - */ -#define WRITE_MDIRQ_CFG 0x50 /* Set M&I&DRQ mask (the real config) */ -#define COMMAND_52 0x52 /* */ -#define READ_HARD_CFG 0x58 /* Read Hardware Config (I/O base etc) */ -#define COMMAND_5C 0x5c /* */ -#define COMMAND_60 0x60 /* */ -#define COMMAND_66 0x66 /* */ -#define COMMAND_6C 0x6c /* */ -#define COMMAND_6E 0x6e /* */ -#define COMMAND_88 0x88 /* */ -#define DSP_INIT_MSS 0x8c /* Enable Microsoft Sound System mode */ -#define COMMAND_C5 0xc5 /* */ -#define GET_DSP_VERSION 0xe1 /* Get DSP Version */ -#define GET_DSP_COPYRIGHT 0xe3 /* Get DSP Copyright */ - -/* - * Offsets of AEDSP16 DSP I/O ports. The offset is added to base I/O port - * to have the actual I/O port. - * Register permissions are: - * (wo) == Write Only - * (ro) == Read Only - * (w-) == Write - * (r-) == Read - */ -#define DSP_RESET 0x06 /* offset of DSP RESET (wo) */ -#define DSP_READ 0x0a /* offset of DSP READ (ro) */ -#define DSP_WRITE 0x0c /* offset of DSP WRITE (w-) */ -#define DSP_COMMAND 0x0c /* offset of DSP COMMAND (w-) */ -#define DSP_STATUS 0x0c /* offset of DSP STATUS (r-) */ -#define DSP_DATAVAIL 0x0e /* offset of DSP DATA AVAILABLE (ro) */ - - -#define RETRY 10 /* Various retry values on I/O opera- */ -#define STATUSRETRY 1000 /* tions. Sometimes we have to */ -#define HARDRETRY 500000 /* wait for previous cmd to complete */ - -/* - * Size of character arrays that store name and version of sound card - */ -#define CARDNAMELEN 15 /* Size of the card's name in chars */ -#define CARDVERLEN 10 /* Size of the card's version in chars */ -#define CARDVERDIGITS 2 /* Number of digits in the version */ - -#if defined(CONFIG_SC6600) -/* - * Bitmapped flags of hard configuration - */ -/* - * Decode macros (xl == low byte, xh = high byte) - */ -#define IOBASE(xl) ((xl & 0x01)?0x240:0x220) -#define JOY(xl) (xl & 0x02) -#define MPUADDR(xl) ( \ - (xl & 0x0C)?0x330: \ - (xl & 0x08)?0x320: \ - (xl & 0x04)?0x310: \ - 0x300) -#define WSSADDR(xl) ((xl & 0x10)?0xE80:0x530) -#define CDROM(xh) (xh & 0x20) -#define CDROMADDR(xh) (((xh & 0x1F) << 4) + 0x200) -/* - * Encode macros - */ -#define BLDIOBASE(xl, val) { \ - xl &= ~0x01; \ - if (val == 0x240) \ - xl |= 0x01; \ - } -#define BLDJOY(xl, val) { \ - xl &= ~0x02; \ - if (val == 1) \ - xl |= 0x02; \ - } -#define BLDMPUADDR(xl, val) { \ - xl &= ~0x0C; \ - switch (val) { \ - case 0x330: \ - xl |= 0x0C; \ - break; \ - case 0x320: \ - xl |= 0x08; \ - break; \ - case 0x310: \ - xl |= 0x04; \ - break; \ - case 0x300: \ - xl |= 0x00; \ - break; \ - default: \ - xl |= 0x00; \ - break; \ - } \ - } -#define BLDWSSADDR(xl, val) { \ - xl &= ~0x10; \ - if (val == 0xE80) \ - xl |= 0x10; \ - } -#define BLDCDROM(xh, val) { \ - xh &= ~0x20; \ - if (val == 1) \ - xh |= 0x20; \ - } -#define BLDCDROMADDR(xh, val) { \ - int tmp = val; \ - tmp -= 0x200; \ - tmp >>= 4; \ - tmp &= 0x1F; \ - xh |= tmp; \ - xh &= 0x7F; \ - xh |= 0x40; \ - } -#endif /* CONFIG_SC6600 */ - -/* - * Bit mapped flags for calling aedsp16_init_board(), and saving the current - * emulation mode. - */ -#define INIT_NONE (0 ) -#define INIT_SBPRO (1<<0) -#define INIT_MSS (1<<1) -#define INIT_MPU401 (1<<2) - -static int soft_cfg __initdata = 0; /* bitmapped config */ -static int soft_cfg_mss __initdata = 0; /* bitmapped mss config */ -static int ver[CARDVERDIGITS] __initdata = {0, 0}; /* DSP Ver: - hi->ver[0] lo->ver[1] */ - -#if defined(CONFIG_SC6600) -static int hard_cfg[2] /* lo<-hard_cfg[0] hi<-hard_cfg[1] */ - __initdata = { 0, 0}; -#endif /* CONFIG_SC6600 */ - -#if defined(CONFIG_SC6600) -/* Decoded hard configuration */ -struct d_hcfg { - int iobase; - int joystick; - int mpubase; - int wssbase; - int cdrom; - int cdrombase; -}; - -static struct d_hcfg decoded_hcfg __initdata = {0, }; - -#endif /* CONFIG_SC6600 */ - -/* orVals contain the values to be or'ed */ -struct orVals { - int val; /* irq|mirq|dma */ - int or; /* soft_cfg |= TheStruct.or */ -}; - -/* aedsp16_info contain the audio card configuration */ -struct aedsp16_info { - int base_io; /* base I/O address for accessing card */ - int irq; /* irq value for DSP I/O */ - int mpu_irq; /* irq for mpu401 interface I/O */ - int dma; /* dma value for DSP I/O */ - int mss_base; /* base I/O for Microsoft Sound System */ - int mpu_base; /* base I/O for MPU-401 emulation */ - int init; /* Initialization status of the card */ -}; - -/* - * Magic values that the DSP will eat when configuring irq/mirq/dma - */ -/* DSP IRQ conversion array */ -static struct orVals orIRQ[] __initdata = { - {0x05, 0x28}, - {0x07, 0x08}, - {0x09, 0x10}, - {0x0a, 0x18}, - {0x0b, 0x20}, - {0x00, 0x00} -}; - -/* MPU-401 IRQ conversion array */ -static struct orVals orMIRQ[] __initdata = { - {0x05, 0x04}, - {0x07, 0x44}, - {0x09, 0x84}, - {0x0a, 0xc4}, - {0x00, 0x00} -}; - -/* DMA Channels conversion array */ -static struct orVals orDMA[] __initdata = { - {0x00, 0x01}, - {0x01, 0x02}, - {0x03, 0x03}, - {0x00, 0x00} -}; - -static struct aedsp16_info ae_config = { - .base_io = DEF_AEDSP16_IOB, - .irq = DEF_AEDSP16_IRQ, - .mpu_irq = DEF_AEDSP16_MRQ, - .dma = DEF_AEDSP16_DMA, - .mss_base = -1, - .mpu_base = -1, - .init = INIT_NONE -}; - -/* - * Buffers to store audio card informations - */ -static char DSPCopyright[CARDNAMELEN + 1] __initdata = {0, }; -static char DSPVersion[CARDVERLEN + 1] __initdata = {0, }; - -static int __init aedsp16_wait_data(int port) -{ - int loop = STATUSRETRY; - unsigned char ret = 0; - - DBG1(("aedsp16_wait_data (0x%x): ", port)); - - do { - ret = inb(port + DSP_DATAVAIL); - /* - * Wait for data available (bit 7 of ret == 1) - */ - } while (!(ret & 0x80) && loop--); - - if (ret & 0x80) { - DBG1(("success.\n")); - return TRUE; - } - - DBG1(("failure.\n")); - return FALSE; -} - -static int __init aedsp16_read(int port) -{ - int inbyte; - - DBG((" Read DSP Byte (0x%x): ", port)); - - if (aedsp16_wait_data(port) == FALSE) { - DBG(("failure.\n")); - return -1; - } - - inbyte = inb(port + DSP_READ); - - DBG(("read [0x%x]/{%c}.\n", inbyte, inbyte)); - - return inbyte; -} - -static int __init aedsp16_test_dsp(int port) -{ - return ((aedsp16_read(port) == 0xaa) ? TRUE : FALSE); -} - -static int __init aedsp16_dsp_reset(int port) -{ - /* - * Reset DSP - */ - - DBG(("Reset DSP:\n")); - - outb(1, (port + DSP_RESET)); - udelay(10); - outb(0, (port + DSP_RESET)); - udelay(10); - udelay(10); - if (aedsp16_test_dsp(port) == TRUE) { - DBG(("success.\n")); - return TRUE; - } else - DBG(("failure.\n")); - return FALSE; -} - -static int __init aedsp16_write(int port, int cmd) -{ - unsigned char ret; - int loop = HARDRETRY; - - DBG((" Write DSP Byte (0x%x) [0x%x]: ", port, cmd)); - - do { - ret = inb(port + DSP_STATUS); - /* - * DSP ready to receive data if bit 7 of ret == 0 - */ - if (!(ret & 0x80)) { - outb(cmd, port + DSP_COMMAND); - DBG(("success.\n")); - return 0; - } - } while (loop--); - - DBG(("timeout.\n")); - printk("[AEDSP16] DSP Command (0x%x) timeout.\n", cmd); - - return -1; -} - -#if defined(CONFIG_SC6600) - -#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG) -void __init aedsp16_pinfo(void) { - DBG(("\n Base address: %x\n", decoded_hcfg.iobase)); - DBG((" Joystick : %s present\n", decoded_hcfg.joystick?"":" not")); - DBG((" WSS addr : %x\n", decoded_hcfg.wssbase)); - DBG((" MPU-401 addr: %x\n", decoded_hcfg.mpubase)); - DBG((" CDROM : %s present\n", (decoded_hcfg.cdrom!=4)?"":" not")); - DBG((" CDROMADDR : %x\n\n", decoded_hcfg.cdrombase)); -} -#endif - -static void __init aedsp16_hard_decode(void) { - - DBG((" aedsp16_hard_decode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1])); - -/* - * Decode Cfg Bytes. - */ - decoded_hcfg.iobase = IOBASE(hard_cfg[0]); - decoded_hcfg.joystick = JOY(hard_cfg[0]); - decoded_hcfg.wssbase = WSSADDR(hard_cfg[0]); - decoded_hcfg.mpubase = MPUADDR(hard_cfg[0]); - decoded_hcfg.cdrom = CDROM(hard_cfg[1]); - decoded_hcfg.cdrombase = CDROMADDR(hard_cfg[1]); - -#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG) - printk(" Original sound card configuration:\n"); - aedsp16_pinfo(); -#endif - -/* - * Now set up the real kernel configuration. - */ - decoded_hcfg.iobase = ae_config.base_io; - decoded_hcfg.wssbase = ae_config.mss_base; - decoded_hcfg.mpubase = ae_config.mpu_base; - -#if defined(CONFIG_SC6600_JOY) - decoded_hcfg.joystick = CONFIG_SC6600_JOY; /* Enable */ -#endif -#if defined(CONFIG_SC6600_CDROM) - decoded_hcfg.cdrom = CONFIG_SC6600_CDROM; /* 4:N-3:I-2:G-1:P-0:S */ -#endif -#if defined(CONFIG_SC6600_CDROMBASE) - decoded_hcfg.cdrombase = CONFIG_SC6600_CDROMBASE; /* 0 Disable */ -#endif - -#if defined(AEDSP16_DEBUG) - DBG((" New Values:\n")); - aedsp16_pinfo(); -#endif - - DBG(("success.\n")); -} - -static void __init aedsp16_hard_encode(void) { - - DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1])); - - hard_cfg[0] = 0; - hard_cfg[1] = 0; - - hard_cfg[0] |= 0x20; - - BLDIOBASE (hard_cfg[0], decoded_hcfg.iobase); - BLDWSSADDR(hard_cfg[0], decoded_hcfg.wssbase); - BLDMPUADDR(hard_cfg[0], decoded_hcfg.mpubase); - BLDJOY(hard_cfg[0], decoded_hcfg.joystick); - BLDCDROM(hard_cfg[1], decoded_hcfg.cdrom); - BLDCDROMADDR(hard_cfg[1], decoded_hcfg.cdrombase); - -#if defined(AEDSP16_DEBUG) - aedsp16_pinfo(); -#endif - - DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1])); - DBG(("success.\n")); - -} - -static int __init aedsp16_hard_write(int port) { - - DBG(("aedsp16_hard_write:\n")); - - if (aedsp16_write(port, COMMAND_6C)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6C); - DBG(("failure.\n")); - return FALSE; - } - if (aedsp16_write(port, COMMAND_5C)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C); - DBG(("failure.\n")); - return FALSE; - } - if (aedsp16_write(port, hard_cfg[0])) { - printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[0]); - DBG(("failure.\n")); - return FALSE; - } - if (aedsp16_write(port, hard_cfg[1])) { - printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[1]); - DBG(("failure.\n")); - return FALSE; - } - if (aedsp16_write(port, COMMAND_C5)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_C5); - DBG(("failure.\n")); - return FALSE; - } - - DBG(("success.\n")); - - return TRUE; -} - -static int __init aedsp16_hard_read(int port) { - - DBG(("aedsp16_hard_read:\n")); - - if (aedsp16_write(port, READ_HARD_CFG)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", READ_HARD_CFG); - DBG(("failure.\n")); - return FALSE; - } - - if ((hard_cfg[0] = aedsp16_read(port)) == -1) { - printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n", - READ_HARD_CFG); - DBG(("failure.\n")); - return FALSE; - } - if ((hard_cfg[1] = aedsp16_read(port)) == -1) { - printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n", - READ_HARD_CFG); - DBG(("failure.\n")); - return FALSE; - } - if (aedsp16_read(port) == -1) { - printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n", - READ_HARD_CFG); - DBG(("failure.\n")); - return FALSE; - } - - DBG(("success.\n")); - - return TRUE; -} - -static int __init aedsp16_ext_cfg_write(int port) { - - int extcfg, val; - - if (aedsp16_write(port, COMMAND_66)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_66); - return FALSE; - } - - extcfg = 7; - if (decoded_hcfg.cdrom != 2) - extcfg = 0x0F; - if ((decoded_hcfg.cdrom == 4) || - (decoded_hcfg.cdrom == 3)) - extcfg &= ~2; - if (decoded_hcfg.cdrombase == 0) - extcfg &= ~2; - if (decoded_hcfg.mpubase == 0) - extcfg &= ~1; - - if (aedsp16_write(port, extcfg)) { - printk("[AEDSP16] Write extcfg: failed!\n"); - return FALSE; - } - if (aedsp16_write(port, 0)) { - printk("[AEDSP16] Write extcfg: failed!\n"); - return FALSE; - } - if (decoded_hcfg.cdrom == 3) { - if (aedsp16_write(port, COMMAND_52)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52); - return FALSE; - } - if ((val = aedsp16_read(port)) == -1) { - printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n" - , COMMAND_52); - return FALSE; - } - val &= 0x7F; - if (aedsp16_write(port, COMMAND_60)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60); - return FALSE; - } - if (aedsp16_write(port, val)) { - printk("[AEDSP16] Write val: failed!\n"); - return FALSE; - } - } - - return TRUE; -} - -#endif /* CONFIG_SC6600 */ - -static int __init aedsp16_cfg_write(int port) { - if (aedsp16_write(port, WRITE_MDIRQ_CFG)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG); - return FALSE; - } - if (aedsp16_write(port, soft_cfg)) { - printk("[AEDSP16] Initialization of (M)IRQ and DMA: failed!\n"); - return FALSE; - } - return TRUE; -} - -static int __init aedsp16_init_mss(int port) -{ - DBG(("aedsp16_init_mss:\n")); - - mdelay(10); - - if (aedsp16_write(port, DSP_INIT_MSS)) { - printk("[AEDSP16] aedsp16_init_mss [0x%x]: failed!\n", - DSP_INIT_MSS); - DBG(("failure.\n")); - return FALSE; - } - - mdelay(10); - - if (aedsp16_cfg_write(port) == FALSE) - return FALSE; - - outb(soft_cfg_mss, ae_config.mss_base); - - DBG(("success.\n")); - - return TRUE; -} - -static int __init aedsp16_setup_board(int port) { - int loop = RETRY; - -#if defined(CONFIG_SC6600) - int val = 0; - - if (aedsp16_hard_read(port) == FALSE) { - printk("[AEDSP16] aedsp16_hard_read: failed!\n"); - return FALSE; - } - - if (aedsp16_write(port, COMMAND_52)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52); - return FALSE; - } - - if ((val = aedsp16_read(port)) == -1) { - printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n", - COMMAND_52); - return FALSE; - } -#endif - - do { - if (aedsp16_write(port, COMMAND_88)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_88); - return FALSE; - } - mdelay(10); - } while ((aedsp16_wait_data(port) == FALSE) && loop--); - - if (aedsp16_read(port) == -1) { - printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n", - COMMAND_88); - return FALSE; - } - -#if !defined(CONFIG_SC6600) - if (aedsp16_write(port, COMMAND_5C)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C); - return FALSE; - } -#endif - - if (aedsp16_cfg_write(port) == FALSE) - return FALSE; - -#if defined(CONFIG_SC6600) - if (aedsp16_write(port, COMMAND_60)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60); - return FALSE; - } - if (aedsp16_write(port, val)) { - printk("[AEDSP16] DATA 0x%x: failed!\n", val); - return FALSE; - } - if (aedsp16_write(port, COMMAND_6E)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6E); - return FALSE; - } - if (aedsp16_write(port, ver[0])) { - printk("[AEDSP16] DATA 0x%x: failed!\n", ver[0]); - return FALSE; - } - if (aedsp16_write(port, ver[1])) { - printk("[AEDSP16] DATA 0x%x: failed!\n", ver[1]); - return FALSE; - } - - if (aedsp16_hard_write(port) == FALSE) { - printk("[AEDSP16] aedsp16_hard_write: failed!\n"); - return FALSE; - } - - if (aedsp16_write(port, COMMAND_5C)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C); - return FALSE; - } - -#if defined(THIS_IS_A_THING_I_HAVE_NOT_TESTED_YET) - if (aedsp16_cfg_write(port) == FALSE) - return FALSE; -#endif - -#endif - - return TRUE; -} - -static int __init aedsp16_stdcfg(int port) { - if (aedsp16_write(port, WRITE_MDIRQ_CFG)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG); - return FALSE; - } - /* - * 0x0A == (IRQ 7, DMA 1, MIRQ 0) - */ - if (aedsp16_write(port, 0x0A)) { - printk("[AEDSP16] aedsp16_stdcfg: failed!\n"); - return FALSE; - } - return TRUE; -} - -static int __init aedsp16_dsp_version(int port) -{ - int len = 0; - int ret; - - DBG(("Get DSP Version:\n")); - - if (aedsp16_write(ae_config.base_io, GET_DSP_VERSION)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_VERSION); - DBG(("failed.\n")); - return FALSE; - } - - do { - if ((ret = aedsp16_read(port)) == -1) { - DBG(("failed.\n")); - return FALSE; - } - /* - * We already know how many int are stored (2), so we know when the - * string is finished. - */ - ver[len++] = ret; - } while (len < CARDVERDIGITS); - sprintf(DSPVersion, "%d.%d", ver[0], ver[1]); - - DBG(("success.\n")); - - return TRUE; -} - -static int __init aedsp16_dsp_copyright(int port) -{ - int len = 0; - int ret; - - DBG(("Get DSP Copyright:\n")); - - if (aedsp16_write(ae_config.base_io, GET_DSP_COPYRIGHT)) { - printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_COPYRIGHT); - DBG(("failed.\n")); - return FALSE; - } - - do { - if ((ret = aedsp16_read(port)) == -1) { - /* - * If no more data available, return to the caller, no error if len>0. - * We have no other way to know when the string is finished. - */ - if (len) - break; - else { - DBG(("failed.\n")); - return FALSE; - } - } - - DSPCopyright[len++] = ret; - - } while (len < CARDNAMELEN); - - DBG(("success.\n")); - - return TRUE; -} - -static void __init aedsp16_init_tables(void) -{ - int i = 0; - - memset(DSPCopyright, 0, CARDNAMELEN + 1); - memset(DSPVersion, 0, CARDVERLEN + 1); - - for (i = 0; orIRQ[i].or; i++) - if (orIRQ[i].val == ae_config.irq) { - soft_cfg |= orIRQ[i].or; - soft_cfg_mss |= orIRQ[i].or; - } - - for (i = 0; orMIRQ[i].or; i++) - if (orMIRQ[i].or == ae_config.mpu_irq) - soft_cfg |= orMIRQ[i].or; - - for (i = 0; orDMA[i].or; i++) - if (orDMA[i].val == ae_config.dma) { - soft_cfg |= orDMA[i].or; - soft_cfg_mss |= orDMA[i].or; - } -} - -static int __init aedsp16_init_board(void) -{ - aedsp16_init_tables(); - - if (aedsp16_dsp_reset(ae_config.base_io) == FALSE) { - printk("[AEDSP16] aedsp16_dsp_reset: failed!\n"); - return FALSE; - } - if (aedsp16_dsp_copyright(ae_config.base_io) == FALSE) { - printk("[AEDSP16] aedsp16_dsp_copyright: failed!\n"); - return FALSE; - } - - /* - * My AEDSP16 card return SC-6000 in DSPCopyright, so - * if we have something different, we have to be warned. - */ - if (strcmp("SC-6000", DSPCopyright)) - printk("[AEDSP16] Warning: non SC-6000 audio card!\n"); - - if (aedsp16_dsp_version(ae_config.base_io) == FALSE) { - printk("[AEDSP16] aedsp16_dsp_version: failed!\n"); - return FALSE; - } - - if (aedsp16_stdcfg(ae_config.base_io) == FALSE) { - printk("[AEDSP16] aedsp16_stdcfg: failed!\n"); - return FALSE; - } - -#if defined(CONFIG_SC6600) - if (aedsp16_hard_read(ae_config.base_io) == FALSE) { - printk("[AEDSP16] aedsp16_hard_read: failed!\n"); - return FALSE; - } - - aedsp16_hard_decode(); - - aedsp16_hard_encode(); - - if (aedsp16_hard_write(ae_config.base_io) == FALSE) { - printk("[AEDSP16] aedsp16_hard_write: failed!\n"); - return FALSE; - } - - if (aedsp16_ext_cfg_write(ae_config.base_io) == FALSE) { - printk("[AEDSP16] aedsp16_ext_cfg_write: failed!\n"); - return FALSE; - } -#endif /* CONFIG_SC6600 */ - - if (aedsp16_setup_board(ae_config.base_io) == FALSE) { - printk("[AEDSP16] aedsp16_setup_board: failed!\n"); - return FALSE; - } - - if (ae_config.mss_base != -1) { - if (ae_config.init & INIT_MSS) { - if (aedsp16_init_mss(ae_config.base_io) == FALSE) { - printk("[AEDSP16] Can not initialize" - "Microsoft Sound System mode.\n"); - return FALSE; - } - } - } - -#if !defined(MODULE) || defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG) - - printk("Audio Excel DSP 16 init v%s (%s %s) [", - VERSION, DSPCopyright, - DSPVersion); - - if (ae_config.mpu_base != -1) { - if (ae_config.init & INIT_MPU401) { - printk("MPU401"); - if ((ae_config.init & INIT_MSS) || - (ae_config.init & INIT_SBPRO)) - printk(" "); - } - } - - if (ae_config.mss_base == -1) { - if (ae_config.init & INIT_SBPRO) { - printk("SBPro"); - if (ae_config.init & INIT_MSS) - printk(" "); - } - } - - if (ae_config.mss_base != -1) - if (ae_config.init & INIT_MSS) - printk("MSS"); - - printk("]\n"); -#endif /* MODULE || AEDSP16_INFO || AEDSP16_DEBUG */ - - mdelay(10); - - return TRUE; -} - -static int __init init_aedsp16_sb(void) -{ - DBG(("init_aedsp16_sb: ")); - -/* - * If the card is already init'ed MSS, we can not init it to SBPRO too - * because the board can not emulate simultaneously MSS and SBPRO. - */ - if (ae_config.init & INIT_MSS) - return FALSE; - if (ae_config.init & INIT_SBPRO) - return FALSE; - - ae_config.init |= INIT_SBPRO; - - DBG(("done.\n")); - - return TRUE; -} - -static void uninit_aedsp16_sb(void) -{ - DBG(("uninit_aedsp16_sb: ")); - - ae_config.init &= ~INIT_SBPRO; - - DBG(("done.\n")); -} - -static int __init init_aedsp16_mss(void) -{ - DBG(("init_aedsp16_mss: ")); - -/* - * If the card is already init'ed SBPRO, we can not init it to MSS too - * because the board can not emulate simultaneously MSS and SBPRO. - */ - if (ae_config.init & INIT_SBPRO) - return FALSE; - if (ae_config.init & INIT_MSS) - return FALSE; -/* - * We must allocate the CONFIG_AEDSP16_BASE region too because these are the - * I/O ports to access card's control registers. - */ - if (!(ae_config.init & INIT_MPU401)) { - if (!request_region(ae_config.base_io, IOBASE_REGION_SIZE, - "aedsp16 (base)")) { - printk( - "AEDSP16 BASE I/O port region is already in use.\n"); - return FALSE; - } - } - - ae_config.init |= INIT_MSS; - - DBG(("done.\n")); - - return TRUE; -} - -static void uninit_aedsp16_mss(void) -{ - DBG(("uninit_aedsp16_mss: ")); - - if ((!(ae_config.init & INIT_MPU401)) && - (ae_config.init & INIT_MSS)) { - release_region(ae_config.base_io, IOBASE_REGION_SIZE); - DBG(("AEDSP16 base region released.\n")); - } - - ae_config.init &= ~INIT_MSS; - DBG(("done.\n")); -} - -static int __init init_aedsp16_mpu(void) -{ - DBG(("init_aedsp16_mpu: ")); - - if (ae_config.init & INIT_MPU401) - return FALSE; - -/* - * We must request the CONFIG_AEDSP16_BASE region too because these are the I/O - * ports to access card's control registers. - */ - if (!(ae_config.init & (INIT_MSS | INIT_SBPRO))) { - if (!request_region(ae_config.base_io, IOBASE_REGION_SIZE, - "aedsp16 (base)")) { - printk( - "AEDSP16 BASE I/O port region is already in use.\n"); - return FALSE; - } - } - - ae_config.init |= INIT_MPU401; - - DBG(("done.\n")); - - return TRUE; -} - -static void uninit_aedsp16_mpu(void) -{ - DBG(("uninit_aedsp16_mpu: ")); - - if ((!(ae_config.init & (INIT_MSS | INIT_SBPRO))) && - (ae_config.init & INIT_MPU401)) { - release_region(ae_config.base_io, IOBASE_REGION_SIZE); - DBG(("AEDSP16 base region released.\n")); - } - - ae_config.init &= ~INIT_MPU401; - - DBG(("done.\n")); -} - -static int __init init_aedsp16(void) -{ - int initialized = FALSE; - - DBG(("Initializing BASE[0x%x] IRQ[%d] DMA[%d] MIRQ[%d]\n", - ae_config.base_io,ae_config.irq,ae_config.dma,ae_config.mpu_irq)); - - if (ae_config.mss_base == -1) { - if (init_aedsp16_sb() == FALSE) { - uninit_aedsp16_sb(); - } else { - initialized = TRUE; - } - } - - if (ae_config.mpu_base != -1) { - if (init_aedsp16_mpu() == FALSE) { - uninit_aedsp16_mpu(); - } else { - initialized = TRUE; - } - } - -/* - * In the sequence of init routines, the MSS init MUST be the last! - * This because of the special register programming the MSS mode needs. - * A board reset would disable the MSS mode restoring the default SBPRO - * mode. - */ - if (ae_config.mss_base != -1) { - if (init_aedsp16_mss() == FALSE) { - uninit_aedsp16_mss(); - } else { - initialized = TRUE; - } - } - - if (initialized) - initialized = aedsp16_init_board(); - return initialized; -} - -static void __exit uninit_aedsp16(void) -{ - if (ae_config.mss_base != -1) - uninit_aedsp16_mss(); - else - uninit_aedsp16_sb(); - if (ae_config.mpu_base != -1) - uninit_aedsp16_mpu(); -} - -static int __initdata io = -1; -static int __initdata irq = -1; -static int __initdata dma = -1; -static int __initdata mpu_irq = -1; -static int __initdata mss_base = -1; -static int __initdata mpu_base = -1; - -module_param_hw(io, int, ioport, 0); -MODULE_PARM_DESC(io, "I/O base address (0x220 0x240)"); -module_param_hw(irq, int, irq, 0); -MODULE_PARM_DESC(irq, "IRQ line (5 7 9 10 11)"); -module_param_hw(dma, int, dma, 0); -MODULE_PARM_DESC(dma, "dma line (0 1 3)"); -module_param_hw(mpu_irq, int, irq, 0); -MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ line (5 7 9 10 0)"); -module_param_hw(mss_base, int, ioport, 0); -MODULE_PARM_DESC(mss_base, "MSS emulation I/O base address (0x530 0xE80)"); -module_param_hw(mpu_base, int, ioport, 0); -MODULE_PARM_DESC(mpu_base,"MPU-401 I/O base address (0x300 0x310 0x320 0x330)"); -MODULE_AUTHOR("Riccardo Facchetti <fizban@tin.it>"); -MODULE_DESCRIPTION("Audio Excel DSP 16 Driver Version " VERSION); -MODULE_LICENSE("GPL"); - -static int __init do_init_aedsp16(void) { - printk("Audio Excel DSP 16 init driver Copyright (C) Riccardo Facchetti 1995-98\n"); - if (io == -1 || dma == -1 || irq == -1) { - printk(KERN_INFO "aedsp16: I/O, IRQ and DMA are mandatory\n"); - return -EINVAL; - } - - ae_config.base_io = io; - ae_config.irq = irq; - ae_config.dma = dma; - - ae_config.mss_base = mss_base; - ae_config.mpu_base = mpu_base; - ae_config.mpu_irq = mpu_irq; - - if (init_aedsp16() == FALSE) { - printk(KERN_ERR "aedsp16: initialization failed\n"); - /* - * XXX - * What error should we return here ? - */ - return -EINVAL; - } - return 0; -} - -static void __exit cleanup_aedsp16(void) { - uninit_aedsp16(); -} - -module_init(do_init_aedsp16); -module_exit(cleanup_aedsp16); - -#ifndef MODULE -static int __init setup_aedsp16(char *str) -{ - /* io, irq, dma, mss_io, mpu_io, mpu_irq */ - int ints[7]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - dma = ints[3]; - mss_base = ints[4]; - mpu_base = ints[5]; - mpu_irq = ints[6]; - return 1; -} - -__setup("aedsp16=", setup_aedsp16); -#endif diff --git a/sound/oss/audio.c b/sound/oss/audio.c deleted file mode 100644 index 09c932f899b8..000000000000 --- a/sound/oss/audio.c +++ /dev/null @@ -1,985 +0,0 @@ -/* - * sound/oss/audio.c - * - * Device file manager for /dev/audio - */ - -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ -/* - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - * Thomas Sailer : moved several static variables into struct audio_operations - * (which is grossly misnamed btw.) because they have the same - * lifetime as the rest in there and dynamic allocation saves - * 12k or so - * Thomas Sailer : use more logical O_NONBLOCK semantics - * Daniel Rodriksson: reworked the use of the device specific copy_user - * still generic - * Horst von Brand: Add missing #include <linux/string.h> - * Chris Rankin : Update the module-usage counter for the coprocessor, - * and decrement the counters again if we cannot open - * the audio device. - */ - -#include <linux/stddef.h> -#include <linux/string.h> -#include <linux/kmod.h> - -#include "sound_config.h" -#include "ulaw.h" -#include "coproc.h" - -#define NEUTRAL8 0x80 -#define NEUTRAL16 0x00 - - -static int dma_ioctl(int dev, unsigned int cmd, void __user *arg); - -static int set_format(int dev, int fmt) -{ - if (fmt != AFMT_QUERY) - { - audio_devs[dev]->local_conversion = 0; - - if (!(audio_devs[dev]->format_mask & fmt)) /* Not supported */ - { - if (fmt == AFMT_MU_LAW) - { - fmt = AFMT_U8; - audio_devs[dev]->local_conversion = CNV_MU_LAW; - } - else - fmt = AFMT_U8; /* This is always supported */ - } - audio_devs[dev]->audio_format = audio_devs[dev]->d->set_bits(dev, fmt); - audio_devs[dev]->local_format = fmt; - } - else - return audio_devs[dev]->local_format; - - if (audio_devs[dev]->local_conversion) - return audio_devs[dev]->local_conversion; - else - return audio_devs[dev]->local_format; -} - -int audio_open(int dev, struct file *file) -{ - int ret; - int bits; - int dev_type = dev & 0x0f; - int mode = translate_mode(file); - const struct audio_driver *driver; - const struct coproc_operations *coprocessor; - - dev = dev >> 4; - - if (dev_type == SND_DEV_DSP16) - bits = 16; - else - bits = 8; - - if (dev < 0 || dev >= num_audiodevs) - return -ENXIO; - - driver = audio_devs[dev]->d; - - if (!try_module_get(driver->owner)) - return -ENODEV; - - if ((ret = DMAbuf_open(dev, mode)) < 0) - goto error_1; - - if ( (coprocessor = audio_devs[dev]->coproc) != NULL ) { - if (!try_module_get(coprocessor->owner)) - goto error_2; - - if ((ret = coprocessor->open(coprocessor->devc, COPR_PCM)) < 0) { - printk(KERN_WARNING "Sound: Can't access coprocessor device\n"); - goto error_3; - } - } - - audio_devs[dev]->local_conversion = 0; - - if (dev_type == SND_DEV_AUDIO) - set_format(dev, AFMT_MU_LAW); - else - set_format(dev, bits); - - audio_devs[dev]->audio_mode = AM_NONE; - - return 0; - - /* - * Clean-up stack: this is what needs (un)doing if - * we can't open the audio device ... - */ - error_3: - module_put(coprocessor->owner); - - error_2: - DMAbuf_release(dev, mode); - - error_1: - module_put(driver->owner); - - return ret; -} - -static void sync_output(int dev) -{ - int p, i; - int l; - struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; - - if (dmap->fragment_size <= 0) - return; - dmap->flags |= DMA_POST; - - /* Align the write pointer with fragment boundaries */ - - if ((l = dmap->user_counter % dmap->fragment_size) > 0) - { - int len; - unsigned long offs = dmap->user_counter % dmap->bytes_in_use; - - len = dmap->fragment_size - l; - memset(dmap->raw_buf + offs, dmap->neutral_byte, len); - DMAbuf_move_wrpointer(dev, len); - } - - /* - * Clean all unused buffer fragments. - */ - - p = dmap->qtail; - dmap->flags |= DMA_POST; - - for (i = dmap->qlen + 1; i < dmap->nbufs; i++) - { - p = (p + 1) % dmap->nbufs; - if (((dmap->raw_buf + p * dmap->fragment_size) + dmap->fragment_size) > - (dmap->raw_buf + dmap->buffsize)) - printk(KERN_ERR "audio: Buffer error 2\n"); - - memset(dmap->raw_buf + p * dmap->fragment_size, - dmap->neutral_byte, - dmap->fragment_size); - } - - dmap->flags |= DMA_DIRTY; -} - -void audio_release(int dev, struct file *file) -{ - const struct coproc_operations *coprocessor; - int mode = translate_mode(file); - - dev = dev >> 4; - - /* - * We do this in DMAbuf_release(). Why are we doing it - * here? Why don't we test the file mode before setting - * both flags? DMAbuf_release() does. - * ...pester...pester...pester... - */ - audio_devs[dev]->dmap_out->closing = 1; - audio_devs[dev]->dmap_in->closing = 1; - - /* - * We need to make sure we allocated the dmap_out buffer - * before we go mucking around with it in sync_output(). - */ - if (mode & OPEN_WRITE) - sync_output(dev); - - if ( (coprocessor = audio_devs[dev]->coproc) != NULL ) { - coprocessor->close(coprocessor->devc, COPR_PCM); - module_put(coprocessor->owner); - } - DMAbuf_release(dev, mode); - - module_put(audio_devs[dev]->d->owner); -} - -static void translate_bytes(const unsigned char *table, unsigned char *buff, int n) -{ - unsigned long i; - - if (n <= 0) - return; - - for (i = 0; i < n; ++i) - buff[i] = table[buff[i]]; -} - -int audio_write(int dev, struct file *file, const char __user *buf, int count) -{ - int c, p, l, buf_size, used, returned; - int err; - char *dma_buf; - - dev = dev >> 4; - - p = 0; - c = count; - - if(count < 0) - return -EINVAL; - - if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) - return -EPERM; - - if (audio_devs[dev]->flags & DMA_DUPLEX) - audio_devs[dev]->audio_mode |= AM_WRITE; - else - audio_devs[dev]->audio_mode = AM_WRITE; - - if (!count) /* Flush output */ - { - sync_output(dev); - return 0; - } - - while (c) - { - if ((err = DMAbuf_getwrbuffer(dev, &dma_buf, &buf_size, !!(file->f_flags & O_NONBLOCK))) < 0) - { - /* Handle nonblocking mode */ - if ((file->f_flags & O_NONBLOCK) && err == -EAGAIN) - return p? p : -EAGAIN; /* No more space. Return # of accepted bytes */ - return err; - } - l = c; - - if (l > buf_size) - l = buf_size; - - returned = l; - used = l; - if (!audio_devs[dev]->d->copy_user) - { - if ((dma_buf + l) > - (audio_devs[dev]->dmap_out->raw_buf + audio_devs[dev]->dmap_out->buffsize)) - { - printk(KERN_ERR "audio: Buffer error 3 (%lx,%d), (%lx, %d)\n", (long) dma_buf, l, (long) audio_devs[dev]->dmap_out->raw_buf, (int) audio_devs[dev]->dmap_out->buffsize); - return -EDOM; - } - if (dma_buf < audio_devs[dev]->dmap_out->raw_buf) - { - printk(KERN_ERR "audio: Buffer error 13 (%lx<%lx)\n", (long) dma_buf, (long) audio_devs[dev]->dmap_out->raw_buf); - return -EDOM; - } - if(copy_from_user(dma_buf, &(buf)[p], l)) - return -EFAULT; - } - else audio_devs[dev]->d->copy_user (dev, - dma_buf, 0, - buf, p, - c, buf_size, - &used, &returned, - l); - l = returned; - - if (audio_devs[dev]->local_conversion & CNV_MU_LAW) - { - translate_bytes(ulaw_dsp, (unsigned char *) dma_buf, l); - } - c -= used; - p += used; - DMAbuf_move_wrpointer(dev, l); - - } - - return count; -} - -int audio_read(int dev, struct file *file, char __user *buf, int count) -{ - int c, p, l; - char *dmabuf; - int buf_no; - - dev = dev >> 4; - p = 0; - c = count; - - if (!(audio_devs[dev]->open_mode & OPEN_READ)) - return -EPERM; - - if ((audio_devs[dev]->audio_mode & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX)) - sync_output(dev); - - if (audio_devs[dev]->flags & DMA_DUPLEX) - audio_devs[dev]->audio_mode |= AM_READ; - else - audio_devs[dev]->audio_mode = AM_READ; - - while(c) - { - if ((buf_no = DMAbuf_getrdbuffer(dev, &dmabuf, &l, !!(file->f_flags & O_NONBLOCK))) < 0) - { - /* - * Nonblocking mode handling. Return current # of bytes - */ - - if (p > 0) /* Avoid throwing away data */ - return p; /* Return it instead */ - - if ((file->f_flags & O_NONBLOCK) && buf_no == -EAGAIN) - return -EAGAIN; - - return buf_no; - } - if (l > c) - l = c; - - /* - * Insert any local processing here. - */ - - if (audio_devs[dev]->local_conversion & CNV_MU_LAW) - { - translate_bytes(dsp_ulaw, (unsigned char *) dmabuf, l); - } - - { - char *fixit = dmabuf; - - if(copy_to_user(&(buf)[p], fixit, l)) - return -EFAULT; - } - - DMAbuf_rmchars(dev, buf_no, l); - - p += l; - c -= l; - } - - return count - c; -} - -int audio_ioctl(int dev, struct file *file, unsigned int cmd, void __user *arg) -{ - int val, count; - unsigned long flags; - struct dma_buffparms *dmap; - int __user *p = arg; - - dev = dev >> 4; - - if (_IOC_TYPE(cmd) == 'C') { - if (audio_devs[dev]->coproc) /* Coprocessor ioctl */ - return audio_devs[dev]->coproc->ioctl(audio_devs[dev]->coproc->devc, cmd, arg, 0); - /* else - printk(KERN_DEBUG"/dev/dsp%d: No coprocessor for this device\n", dev); */ - return -ENXIO; - } - else switch (cmd) - { - case SNDCTL_DSP_SYNC: - if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) - return 0; - if (audio_devs[dev]->dmap_out->fragment_size == 0) - return 0; - sync_output(dev); - DMAbuf_sync(dev); - DMAbuf_reset(dev); - return 0; - - case SNDCTL_DSP_POST: - if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) - return 0; - if (audio_devs[dev]->dmap_out->fragment_size == 0) - return 0; - audio_devs[dev]->dmap_out->flags |= DMA_POST | DMA_DIRTY; - sync_output(dev); - dma_ioctl(dev, SNDCTL_DSP_POST, NULL); - return 0; - - case SNDCTL_DSP_RESET: - audio_devs[dev]->audio_mode = AM_NONE; - DMAbuf_reset(dev); - return 0; - - case SNDCTL_DSP_GETFMTS: - val = audio_devs[dev]->format_mask | AFMT_MU_LAW; - break; - - case SNDCTL_DSP_SETFMT: - if (get_user(val, p)) - return -EFAULT; - val = set_format(dev, val); - break; - - case SNDCTL_DSP_GETISPACE: - if (!(audio_devs[dev]->open_mode & OPEN_READ)) - return 0; - if ((audio_devs[dev]->audio_mode & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX)) - return -EBUSY; - return dma_ioctl(dev, cmd, arg); - - case SNDCTL_DSP_GETOSPACE: - if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) - return -EPERM; - if ((audio_devs[dev]->audio_mode & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX)) - return -EBUSY; - return dma_ioctl(dev, cmd, arg); - - case SNDCTL_DSP_NONBLOCK: - spin_lock(&file->f_lock); - file->f_flags |= O_NONBLOCK; - spin_unlock(&file->f_lock); - return 0; - - case SNDCTL_DSP_GETCAPS: - val = 1 | DSP_CAP_MMAP; /* Revision level of this ioctl() */ - if (audio_devs[dev]->flags & DMA_DUPLEX && - audio_devs[dev]->open_mode == OPEN_READWRITE) - val |= DSP_CAP_DUPLEX; - if (audio_devs[dev]->coproc) - val |= DSP_CAP_COPROC; - if (audio_devs[dev]->d->local_qlen) /* Device has hidden buffers */ - val |= DSP_CAP_BATCH; - if (audio_devs[dev]->d->trigger) /* Supports SETTRIGGER */ - val |= DSP_CAP_TRIGGER; - break; - - case SOUND_PCM_WRITE_RATE: - if (get_user(val, p)) - return -EFAULT; - val = audio_devs[dev]->d->set_speed(dev, val); - break; - - case SOUND_PCM_READ_RATE: - val = audio_devs[dev]->d->set_speed(dev, 0); - break; - - case SNDCTL_DSP_STEREO: - if (get_user(val, p)) - return -EFAULT; - if (val > 1 || val < 0) - return -EINVAL; - val = audio_devs[dev]->d->set_channels(dev, val + 1) - 1; - break; - - case SOUND_PCM_WRITE_CHANNELS: - if (get_user(val, p)) - return -EFAULT; - val = audio_devs[dev]->d->set_channels(dev, val); - break; - - case SOUND_PCM_READ_CHANNELS: - val = audio_devs[dev]->d->set_channels(dev, 0); - break; - - case SOUND_PCM_READ_BITS: - val = audio_devs[dev]->d->set_bits(dev, 0); - break; - - case SNDCTL_DSP_SETDUPLEX: - if (audio_devs[dev]->open_mode != OPEN_READWRITE) - return -EPERM; - return (audio_devs[dev]->flags & DMA_DUPLEX) ? 0 : -EIO; - - case SNDCTL_DSP_PROFILE: - if (get_user(val, p)) - return -EFAULT; - if (audio_devs[dev]->open_mode & OPEN_WRITE) - audio_devs[dev]->dmap_out->applic_profile = val; - if (audio_devs[dev]->open_mode & OPEN_READ) - audio_devs[dev]->dmap_in->applic_profile = val; - return 0; - - case SNDCTL_DSP_GETODELAY: - dmap = audio_devs[dev]->dmap_out; - if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) - return -EINVAL; - if (!(dmap->flags & DMA_ALLOC_DONE)) - { - val=0; - break; - } - - spin_lock_irqsave(&dmap->lock,flags); - /* Compute number of bytes that have been played */ - count = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT); - if (count < dmap->fragment_size && dmap->qhead != 0) - count += dmap->bytes_in_use; /* Pointer wrap not handled yet */ - count += dmap->byte_counter; - - /* Subtract current count from the number of bytes written by app */ - count = dmap->user_counter - count; - if (count < 0) - count = 0; - spin_unlock_irqrestore(&dmap->lock,flags); - val = count; - break; - - default: - return dma_ioctl(dev, cmd, arg); - } - return put_user(val, p); -} - -void audio_init_devices(void) -{ - /* - * NOTE! This routine could be called several times during boot. - */ -} - -void reorganize_buffers(int dev, struct dma_buffparms *dmap, int recording) -{ - /* - * This routine breaks the physical device buffers to logical ones. - */ - - struct audio_operations *dsp_dev = audio_devs[dev]; - - unsigned i, n; - unsigned sr, nc, sz, bsz; - - sr = dsp_dev->d->set_speed(dev, 0); - nc = dsp_dev->d->set_channels(dev, 0); - sz = dsp_dev->d->set_bits(dev, 0); - - if (sz == 8) - dmap->neutral_byte = NEUTRAL8; - else - dmap->neutral_byte = NEUTRAL16; - - if (sr < 1 || nc < 1 || sz < 1) - { -/* printk(KERN_DEBUG "Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", dev, sr, nc, sz);*/ - sr = DSP_DEFAULT_SPEED; - nc = 1; - sz = 8; - } - - sz = sr * nc * sz; - - sz /= 8; /* #bits -> #bytes */ - dmap->data_rate = sz; - - if (!dmap->needs_reorg) - return; - dmap->needs_reorg = 0; - - if (dmap->fragment_size == 0) - { - /* Compute the fragment size using the default algorithm */ - - /* - * Compute a buffer size for time not exceeding 1 second. - * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds - * of sound (using the current speed, sample size and #channels). - */ - - bsz = dmap->buffsize; - while (bsz > sz) - bsz /= 2; - - if (bsz == dmap->buffsize) - bsz /= 2; /* Needs at least 2 buffers */ - - /* - * Split the computed fragment to smaller parts. After 3.5a9 - * the default subdivision is 4 which should give better - * results when recording. - */ - - if (dmap->subdivision == 0) /* Not already set */ - { - dmap->subdivision = 4; /* Init to the default value */ - - if ((bsz / dmap->subdivision) > 4096) - dmap->subdivision *= 2; - if ((bsz / dmap->subdivision) < 4096) - dmap->subdivision = 1; - } - bsz /= dmap->subdivision; - - if (bsz < 16) - bsz = 16; /* Just a sanity check */ - - dmap->fragment_size = bsz; - } - else - { - /* - * The process has specified the buffer size with SNDCTL_DSP_SETFRAGMENT or - * the buffer size computation has already been done. - */ - if (dmap->fragment_size > (dmap->buffsize / 2)) - dmap->fragment_size = (dmap->buffsize / 2); - bsz = dmap->fragment_size; - } - - if (audio_devs[dev]->min_fragment) - if (bsz < (1 << audio_devs[dev]->min_fragment)) - bsz = 1 << audio_devs[dev]->min_fragment; - if (audio_devs[dev]->max_fragment) - if (bsz > (1 << audio_devs[dev]->max_fragment)) - bsz = 1 << audio_devs[dev]->max_fragment; - bsz &= ~0x07; /* Force size which is multiple of 8 bytes */ -#ifdef OS_DMA_ALIGN_CHECK - OS_DMA_ALIGN_CHECK(bsz); -#endif - - n = dmap->buffsize / bsz; - if (n > MAX_SUB_BUFFERS) - n = MAX_SUB_BUFFERS; - if (n > dmap->max_fragments) - n = dmap->max_fragments; - - if (n < 2) - { - n = 2; - bsz /= 2; - } - dmap->nbufs = n; - dmap->bytes_in_use = n * bsz; - dmap->fragment_size = bsz; - dmap->max_byte_counter = (dmap->data_rate * 60 * 60) + - dmap->bytes_in_use; /* Approximately one hour */ - - if (dmap->raw_buf) - { - memset(dmap->raw_buf, dmap->neutral_byte, dmap->bytes_in_use); - } - - for (i = 0; i < dmap->nbufs; i++) - { - dmap->counts[i] = 0; - } - - dmap->flags |= DMA_ALLOC_DONE | DMA_EMPTY; -} - -static int dma_subdivide(int dev, struct dma_buffparms *dmap, int fact) -{ - if (fact == 0) - { - fact = dmap->subdivision; - if (fact == 0) - fact = 1; - return fact; - } - if (dmap->subdivision != 0 || dmap->fragment_size) /* Too late to change */ - return -EINVAL; - - if (fact > MAX_REALTIME_FACTOR) - return -EINVAL; - - if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16) - return -EINVAL; - - dmap->subdivision = fact; - return fact; -} - -static int dma_set_fragment(int dev, struct dma_buffparms *dmap, int fact) -{ - int bytes, count; - - if (fact == 0) - return -EIO; - - if (dmap->subdivision != 0 || - dmap->fragment_size) /* Too late to change */ - return -EINVAL; - - bytes = fact & 0xffff; - count = (fact >> 16) & 0x7fff; - - if (count == 0) - count = MAX_SUB_BUFFERS; - else if (count < MAX_SUB_BUFFERS) - count++; - - if (bytes < 4 || bytes > 17) /* <16 || > 512k */ - return -EINVAL; - - if (count < 2) - return -EINVAL; - - if (audio_devs[dev]->min_fragment > 0) - if (bytes < audio_devs[dev]->min_fragment) - bytes = audio_devs[dev]->min_fragment; - - if (audio_devs[dev]->max_fragment > 0) - if (bytes > audio_devs[dev]->max_fragment) - bytes = audio_devs[dev]->max_fragment; - -#ifdef OS_DMA_MINBITS - if (bytes < OS_DMA_MINBITS) - bytes = OS_DMA_MINBITS; -#endif - - dmap->fragment_size = (1 << bytes); - dmap->max_fragments = count; - - if (dmap->fragment_size > dmap->buffsize) - dmap->fragment_size = dmap->buffsize; - - if (dmap->fragment_size == dmap->buffsize && - audio_devs[dev]->flags & DMA_AUTOMODE) - dmap->fragment_size /= 2; /* Needs at least 2 buffers */ - - dmap->subdivision = 1; /* Disable SNDCTL_DSP_SUBDIVIDE */ - return bytes | ((count - 1) << 16); -} - -static int dma_ioctl(int dev, unsigned int cmd, void __user *arg) -{ - struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out; - struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in; - struct dma_buffparms *dmap; - audio_buf_info info; - count_info cinfo; - int fact, ret, changed, bits, count, err; - unsigned long flags; - - switch (cmd) - { - case SNDCTL_DSP_SUBDIVIDE: - ret = 0; - if (get_user(fact, (int __user *)arg)) - return -EFAULT; - if (audio_devs[dev]->open_mode & OPEN_WRITE) - ret = dma_subdivide(dev, dmap_out, fact); - if (ret < 0) - return ret; - if (audio_devs[dev]->open_mode != OPEN_WRITE || - (audio_devs[dev]->flags & DMA_DUPLEX && - audio_devs[dev]->open_mode & OPEN_READ)) - ret = dma_subdivide(dev, dmap_in, fact); - if (ret < 0) - return ret; - break; - - case SNDCTL_DSP_GETISPACE: - case SNDCTL_DSP_GETOSPACE: - dmap = dmap_out; - if (cmd == SNDCTL_DSP_GETISPACE && !(audio_devs[dev]->open_mode & OPEN_READ)) - return -EINVAL; - if (cmd == SNDCTL_DSP_GETOSPACE && !(audio_devs[dev]->open_mode & OPEN_WRITE)) - return -EINVAL; - if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX) - dmap = dmap_in; - if (dmap->mapping_flags & DMA_MAP_MAPPED) - return -EINVAL; - if (!(dmap->flags & DMA_ALLOC_DONE)) - reorganize_buffers(dev, dmap, (cmd == SNDCTL_DSP_GETISPACE)); - info.fragstotal = dmap->nbufs; - if (cmd == SNDCTL_DSP_GETISPACE) - info.fragments = dmap->qlen; - else - { - if (!DMAbuf_space_in_queue(dev)) - info.fragments = 0; - else - { - info.fragments = DMAbuf_space_in_queue(dev); - if (audio_devs[dev]->d->local_qlen) - { - int tmp = audio_devs[dev]->d->local_qlen(dev); - if (tmp && info.fragments) - tmp--; /* - * This buffer has been counted twice - */ - info.fragments -= tmp; - } - } - } - if (info.fragments < 0) - info.fragments = 0; - else if (info.fragments > dmap->nbufs) - info.fragments = dmap->nbufs; - - info.fragsize = dmap->fragment_size; - info.bytes = info.fragments * dmap->fragment_size; - - if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen) - info.bytes -= dmap->counts[dmap->qhead]; - else - { - info.fragments = info.bytes / dmap->fragment_size; - info.bytes -= dmap->user_counter % dmap->fragment_size; - } - if (copy_to_user(arg, &info, sizeof(info))) - return -EFAULT; - return 0; - - case SNDCTL_DSP_SETTRIGGER: - if (get_user(bits, (int __user *)arg)) - return -EFAULT; - bits &= audio_devs[dev]->open_mode; - if (audio_devs[dev]->d->trigger == NULL) - return -EINVAL; - if (!(audio_devs[dev]->flags & DMA_DUPLEX) && (bits & PCM_ENABLE_INPUT) && - (bits & PCM_ENABLE_OUTPUT)) - return -EINVAL; - - if (bits & PCM_ENABLE_INPUT) - { - spin_lock_irqsave(&dmap_in->lock,flags); - changed = (audio_devs[dev]->enable_bits ^ bits) & PCM_ENABLE_INPUT; - if (changed && audio_devs[dev]->go) - { - reorganize_buffers(dev, dmap_in, 1); - if ((err = audio_devs[dev]->d->prepare_for_input(dev, - dmap_in->fragment_size, dmap_in->nbufs)) < 0) { - spin_unlock_irqrestore(&dmap_in->lock,flags); - return err; - } - dmap_in->dma_mode = DMODE_INPUT; - audio_devs[dev]->enable_bits |= PCM_ENABLE_INPUT; - DMAbuf_activate_recording(dev, dmap_in); - } else - audio_devs[dev]->enable_bits &= ~PCM_ENABLE_INPUT; - spin_unlock_irqrestore(&dmap_in->lock,flags); - } - if (bits & PCM_ENABLE_OUTPUT) - { - spin_lock_irqsave(&dmap_out->lock,flags); - changed = (audio_devs[dev]->enable_bits ^ bits) & PCM_ENABLE_OUTPUT; - if (changed && - (dmap_out->mapping_flags & DMA_MAP_MAPPED || dmap_out->qlen > 0) && - audio_devs[dev]->go) - { - if (!(dmap_out->flags & DMA_ALLOC_DONE)) - reorganize_buffers(dev, dmap_out, 0); - dmap_out->dma_mode = DMODE_OUTPUT; - audio_devs[dev]->enable_bits |= PCM_ENABLE_OUTPUT; - dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size; - DMAbuf_launch_output(dev, dmap_out); - } else - audio_devs[dev]->enable_bits &= ~PCM_ENABLE_OUTPUT; - spin_unlock_irqrestore(&dmap_out->lock,flags); - } -#if 0 - if (changed && audio_devs[dev]->d->trigger) - audio_devs[dev]->d->trigger(dev, bits * audio_devs[dev]->go); -#endif - /* Falls through... */ - - case SNDCTL_DSP_GETTRIGGER: - ret = audio_devs[dev]->enable_bits; - break; - - case SNDCTL_DSP_SETSYNCRO: - if (!audio_devs[dev]->d->trigger) - return -EINVAL; - audio_devs[dev]->d->trigger(dev, 0); - audio_devs[dev]->go = 0; - return 0; - - case SNDCTL_DSP_GETIPTR: - if (!(audio_devs[dev]->open_mode & OPEN_READ)) - return -EINVAL; - spin_lock_irqsave(&dmap_in->lock,flags); - cinfo.bytes = dmap_in->byte_counter; - cinfo.ptr = DMAbuf_get_buffer_pointer(dev, dmap_in, DMODE_INPUT) & ~3; - if (cinfo.ptr < dmap_in->fragment_size && dmap_in->qtail != 0) - cinfo.bytes += dmap_in->bytes_in_use; /* Pointer wrap not handled yet */ - cinfo.blocks = dmap_in->qlen; - cinfo.bytes += cinfo.ptr; - if (dmap_in->mapping_flags & DMA_MAP_MAPPED) - dmap_in->qlen = 0; /* Reset interrupt counter */ - spin_unlock_irqrestore(&dmap_in->lock,flags); - if (copy_to_user(arg, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - - case SNDCTL_DSP_GETOPTR: - if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) - return -EINVAL; - - spin_lock_irqsave(&dmap_out->lock,flags); - cinfo.bytes = dmap_out->byte_counter; - cinfo.ptr = DMAbuf_get_buffer_pointer(dev, dmap_out, DMODE_OUTPUT) & ~3; - if (cinfo.ptr < dmap_out->fragment_size && dmap_out->qhead != 0) - cinfo.bytes += dmap_out->bytes_in_use; /* Pointer wrap not handled yet */ - cinfo.blocks = dmap_out->qlen; - cinfo.bytes += cinfo.ptr; - if (dmap_out->mapping_flags & DMA_MAP_MAPPED) - dmap_out->qlen = 0; /* Reset interrupt counter */ - spin_unlock_irqrestore(&dmap_out->lock,flags); - if (copy_to_user(arg, &cinfo, sizeof(cinfo))) - return -EFAULT; - return 0; - - case SNDCTL_DSP_GETODELAY: - if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) - return -EINVAL; - if (!(dmap_out->flags & DMA_ALLOC_DONE)) - { - ret=0; - break; - } - spin_lock_irqsave(&dmap_out->lock,flags); - /* Compute number of bytes that have been played */ - count = DMAbuf_get_buffer_pointer (dev, dmap_out, DMODE_OUTPUT); - if (count < dmap_out->fragment_size && dmap_out->qhead != 0) - count += dmap_out->bytes_in_use; /* Pointer wrap not handled yet */ - count += dmap_out->byte_counter; - /* Subtract current count from the number of bytes written by app */ - count = dmap_out->user_counter - count; - if (count < 0) - count = 0; - spin_unlock_irqrestore(&dmap_out->lock,flags); - ret = count; - break; - - case SNDCTL_DSP_POST: - if (audio_devs[dev]->dmap_out->qlen > 0) - if (!(audio_devs[dev]->dmap_out->flags & DMA_ACTIVE)) - DMAbuf_launch_output(dev, audio_devs[dev]->dmap_out); - return 0; - - case SNDCTL_DSP_GETBLKSIZE: - dmap = dmap_out; - if (audio_devs[dev]->open_mode & OPEN_WRITE) - reorganize_buffers(dev, dmap_out, (audio_devs[dev]->open_mode == OPEN_READ)); - if (audio_devs[dev]->open_mode == OPEN_READ || - (audio_devs[dev]->flags & DMA_DUPLEX && - audio_devs[dev]->open_mode & OPEN_READ)) - reorganize_buffers(dev, dmap_in, (audio_devs[dev]->open_mode == OPEN_READ)); - if (audio_devs[dev]->open_mode == OPEN_READ) - dmap = dmap_in; - ret = dmap->fragment_size; - break; - - case SNDCTL_DSP_SETFRAGMENT: - ret = 0; - if (get_user(fact, (int __user *)arg)) - return -EFAULT; - if (audio_devs[dev]->open_mode & OPEN_WRITE) - ret = dma_set_fragment(dev, dmap_out, fact); - if (ret < 0) - return ret; - if (audio_devs[dev]->open_mode == OPEN_READ || - (audio_devs[dev]->flags & DMA_DUPLEX && - audio_devs[dev]->open_mode & OPEN_READ)) - ret = dma_set_fragment(dev, dmap_in, fact); - if (ret < 0) - return ret; - if (!arg) /* don't know what this is good for, but preserve old semantics */ - return 0; - break; - - default: - if (!audio_devs[dev]->d->ioctl) - return -EINVAL; - return audio_devs[dev]->d->ioctl(dev, cmd, arg); - } - return put_user(ret, (int __user *)arg); -} diff --git a/sound/oss/bin2hex.c b/sound/oss/bin2hex.c deleted file mode 100644 index 26c04ce04d71..000000000000 --- a/sound/oss/bin2hex.c +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include <stdio.h> -#include <string.h> -#include <stdlib.h> - -int main( int argc, const char * argv [] ) -{ - const char * varname; - int i = 0; - int c; - int id = 0; - - if(argv[1] && strcmp(argv[1],"-i")==0) - { - argv++; - argc--; - id=1; - } - - if(argc==1) - { - fprintf(stderr, "bin2hex: [-i] firmware\n"); - exit(1); - } - - varname = argv[1]; - printf( "/* automatically generated by bin2hex */\n" ); - printf( "static unsigned char %s [] %s =\n{\n", varname , id?"__initdata":""); - - while ( ( c = getchar( ) ) != EOF ) - { - if ( i != 0 && i % 10 == 0 ) - printf( "\n" ); - printf( "0x%02lx,", c & 0xFFl ); - i++; - } - - printf( "};\nstatic int %sLen = %d;\n", varname, i ); - return 0; -} diff --git a/sound/oss/coproc.h b/sound/oss/coproc.h deleted file mode 100644 index 7bec21bbdd88..000000000000 --- a/sound/oss/coproc.h +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Definitions for various on board processors on the sound cards. For - * example DSP processors. - */ - -/* - * Coprocessor access types - */ -#define COPR_CUSTOM 0x0001 /* Custom applications */ -#define COPR_MIDI 0x0002 /* MIDI (MPU-401) emulation */ -#define COPR_PCM 0x0004 /* Digitized voice applications */ -#define COPR_SYNTH 0x0008 /* Music synthesis */ diff --git a/sound/oss/dev_table.c b/sound/oss/dev_table.c deleted file mode 100644 index 6dad51596b70..000000000000 --- a/sound/oss/dev_table.c +++ /dev/null @@ -1,256 +0,0 @@ -/* - * sound/oss/dev_table.c - * - * Device call tables. - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ - -#include <linux/init.h> - -#include "sound_config.h" - -struct audio_operations *audio_devs[MAX_AUDIO_DEV]; -EXPORT_SYMBOL(audio_devs); - -int num_audiodevs; -EXPORT_SYMBOL(num_audiodevs); - -struct mixer_operations *mixer_devs[MAX_MIXER_DEV]; -EXPORT_SYMBOL(mixer_devs); - -int num_mixers; -EXPORT_SYMBOL(num_mixers); - -struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV]; -EXPORT_SYMBOL(synth_devs); - -int num_synths; - -struct midi_operations *midi_devs[MAX_MIDI_DEV]; -EXPORT_SYMBOL(midi_devs); - -int num_midis; -EXPORT_SYMBOL(num_midis); - -struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = { - &default_sound_timer, NULL -}; -EXPORT_SYMBOL(sound_timer_devs); - -int num_sound_timers = 1; - - -static int sound_alloc_audiodev(void); - -int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver, - int driver_size, int flags, unsigned int format_mask, - void *devc, int dma1, int dma2) -{ - struct audio_driver *d; - struct audio_operations *op; - int num; - - if (vers != AUDIO_DRIVER_VERSION || driver_size > sizeof(struct audio_driver)) { - printk(KERN_ERR "Sound: Incompatible audio driver for %s\n", name); - return -EINVAL; - } - num = sound_alloc_audiodev(); - - if (num == -1) { - printk(KERN_ERR "sound: Too many audio drivers\n"); - return -EBUSY; - } - d = (struct audio_driver *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct audio_driver))); - sound_nblocks++; - if (sound_nblocks >= MAX_MEM_BLOCKS) - sound_nblocks = MAX_MEM_BLOCKS - 1; - - op = (struct audio_operations *) (sound_mem_blocks[sound_nblocks] = vzalloc(sizeof(struct audio_operations))); - sound_nblocks++; - if (sound_nblocks >= MAX_MEM_BLOCKS) - sound_nblocks = MAX_MEM_BLOCKS - 1; - - if (d == NULL || op == NULL) { - printk(KERN_ERR "Sound: Can't allocate driver for (%s)\n", name); - sound_unload_audiodev(num); - return -ENOMEM; - } - init_waitqueue_head(&op->in_sleeper); - init_waitqueue_head(&op->out_sleeper); - init_waitqueue_head(&op->poll_sleeper); - if (driver_size < sizeof(struct audio_driver)) - memset((char *) d, 0, sizeof(struct audio_driver)); - - memcpy((char *) d, (char *) driver, driver_size); - - op->d = d; - strlcpy(op->name, name, sizeof(op->name)); - op->flags = flags; - op->format_mask = format_mask; - op->devc = devc; - - /* - * Hardcoded defaults - */ - audio_devs[num] = op; - - DMAbuf_init(num, dma1, dma2); - - audio_init_devices(); - return num; -} -EXPORT_SYMBOL(sound_install_audiodrv); - -int sound_install_mixer(int vers, char *name, struct mixer_operations *driver, - int driver_size, void *devc) -{ - struct mixer_operations *op; - - int n = sound_alloc_mixerdev(); - - if (n == -1) { - printk(KERN_ERR "Sound: Too many mixer drivers\n"); - return -EBUSY; - } - if (vers != MIXER_DRIVER_VERSION || - driver_size > sizeof(struct mixer_operations)) { - printk(KERN_ERR "Sound: Incompatible mixer driver for %s\n", name); - return -EINVAL; - } - - /* FIXME: This leaks a mixer_operations struct every time its called - until you unload sound! */ - - op = (struct mixer_operations *) (sound_mem_blocks[sound_nblocks] = vzalloc(sizeof(struct mixer_operations))); - sound_nblocks++; - if (sound_nblocks >= MAX_MEM_BLOCKS) - sound_nblocks = MAX_MEM_BLOCKS - 1; - - if (op == NULL) { - printk(KERN_ERR "Sound: Can't allocate mixer driver for (%s)\n", name); - return -ENOMEM; - } - memcpy((char *) op, (char *) driver, driver_size); - - strlcpy(op->name, name, sizeof(op->name)); - op->devc = devc; - - mixer_devs[n] = op; - return n; -} -EXPORT_SYMBOL(sound_install_mixer); - -void sound_unload_audiodev(int dev) -{ - if (dev != -1) { - DMAbuf_deinit(dev); - audio_devs[dev] = NULL; - unregister_sound_dsp((dev<<4)+3); - } -} -EXPORT_SYMBOL(sound_unload_audiodev); - -static int sound_alloc_audiodev(void) -{ - int i = register_sound_dsp(&oss_sound_fops, -1); - if(i==-1) - return i; - i>>=4; - if(i>=num_audiodevs) - num_audiodevs = i + 1; - return i; -} - -int sound_alloc_mididev(void) -{ - int i = register_sound_midi(&oss_sound_fops, -1); - if(i==-1) - return i; - i>>=4; - if(i>=num_midis) - num_midis = i + 1; - return i; -} -EXPORT_SYMBOL(sound_alloc_mididev); - -int sound_alloc_synthdev(void) -{ - int i; - - for (i = 0; i < MAX_SYNTH_DEV; i++) { - if (synth_devs[i] == NULL) { - if (i >= num_synths) - num_synths++; - return i; - } - } - return -1; -} -EXPORT_SYMBOL(sound_alloc_synthdev); - -int sound_alloc_mixerdev(void) -{ - int i = register_sound_mixer(&oss_sound_fops, -1); - if(i==-1) - return -1; - i>>=4; - if(i>=num_mixers) - num_mixers = i + 1; - return i; -} -EXPORT_SYMBOL(sound_alloc_mixerdev); - -int sound_alloc_timerdev(void) -{ - int i; - - for (i = 0; i < MAX_TIMER_DEV; i++) { - if (sound_timer_devs[i] == NULL) { - if (i >= num_sound_timers) - num_sound_timers++; - return i; - } - } - return -1; -} -EXPORT_SYMBOL(sound_alloc_timerdev); - -void sound_unload_mixerdev(int dev) -{ - if (dev != -1) { - mixer_devs[dev] = NULL; - unregister_sound_mixer(dev<<4); - num_mixers--; - } -} -EXPORT_SYMBOL(sound_unload_mixerdev); - -void sound_unload_mididev(int dev) -{ - if (dev != -1) { - midi_devs[dev] = NULL; - unregister_sound_midi((dev<<4)+2); - } -} -EXPORT_SYMBOL(sound_unload_mididev); - -void sound_unload_synthdev(int dev) -{ - if (dev != -1) - synth_devs[dev] = NULL; -} -EXPORT_SYMBOL(sound_unload_synthdev); - -void sound_unload_timerdev(int dev) -{ - if (dev != -1) - sound_timer_devs[dev] = NULL; -} -EXPORT_SYMBOL(sound_unload_timerdev); - diff --git a/sound/oss/dev_table.h b/sound/oss/dev_table.h deleted file mode 100644 index 0199a317c5a9..000000000000 --- a/sound/oss/dev_table.h +++ /dev/null @@ -1,390 +0,0 @@ -/* - * dev_table.h - * - * Global definitions for device call tables - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ - - -#ifndef _DEV_TABLE_H_ -#define _DEV_TABLE_H_ - -#include <linux/spinlock.h> -/* - * Sound card numbers 27 to 999. (1 to 26 are defined in soundcard.h) - * Numbers 1000 to N are reserved for driver's internal use. - */ - -#define SNDCARD_DESKPROXL 27 /* Compaq Deskpro XL */ -#define SNDCARD_VIDC 28 /* ARMs VIDC */ -#define SNDCARD_SBPNP 29 -#define SNDCARD_SOFTOSS 36 -#define SNDCARD_VMIDI 37 -#define SNDCARD_OPL3SA1 38 /* Note: clash in msnd.h */ -#define SNDCARD_OPL3SA1_SB 39 -#define SNDCARD_OPL3SA1_MPU 40 -#define SNDCARD_WAVEFRONT 41 -#define SNDCARD_OPL3SA2 42 -#define SNDCARD_OPL3SA2_MPU 43 -#define SNDCARD_WAVEARTIST 44 /* Waveartist */ -#define SNDCARD_OPL3SA2_MSS 45 /* Originally missed */ -#define SNDCARD_AD1816 88 - -/* - * NOTE! NOTE! NOTE! NOTE! - * - * If you modify this file, please check the dev_table.c also. - * - * NOTE! NOTE! NOTE! NOTE! - */ - -struct driver_info -{ - char *driver_id; - int card_subtype; /* Driver specific. Usually 0 */ - int card_type; /* From soundcard.h */ - char *name; - void (*attach) (struct address_info *hw_config); - int (*probe) (struct address_info *hw_config); - void (*unload) (struct address_info *hw_config); -}; - -struct card_info -{ - int card_type; /* Link (search key) to the driver list */ - struct address_info config; - int enabled; - void *for_driver_use; -}; - - -/* - * Device specific parameters (used only by dmabuf.c) - */ -#define MAX_SUB_BUFFERS (32*MAX_REALTIME_FACTOR) - -#define DMODE_NONE 0 -#define DMODE_OUTPUT PCM_ENABLE_OUTPUT -#define DMODE_INPUT PCM_ENABLE_INPUT - -struct dma_buffparms -{ - int dma_mode; /* DMODE_INPUT, DMODE_OUTPUT or DMODE_NONE */ - int closing; - - /* - * Pointers to raw buffers - */ - - char *raw_buf; - unsigned long raw_buf_phys; - int buffsize; - - /* - * Device state tables - */ - - unsigned long flags; -#define DMA_BUSY 0x00000001 -#define DMA_RESTART 0x00000002 -#define DMA_ACTIVE 0x00000004 -#define DMA_STARTED 0x00000008 -#define DMA_EMPTY 0x00000010 -#define DMA_ALLOC_DONE 0x00000020 -#define DMA_SYNCING 0x00000040 -#define DMA_DIRTY 0x00000080 -#define DMA_POST 0x00000100 -#define DMA_NODMA 0x00000200 -#define DMA_NOTIMEOUT 0x00000400 - - int open_mode; - - /* - * Queue parameters. - */ - int qlen; - int qhead; - int qtail; - spinlock_t lock; - - int cfrag; /* Current incomplete fragment (write) */ - - int nbufs; - int counts[MAX_SUB_BUFFERS]; - int subdivision; - - int fragment_size; - int needs_reorg; - int max_fragments; - - int bytes_in_use; - - int underrun_count; - unsigned long byte_counter; - unsigned long user_counter; - unsigned long max_byte_counter; - int data_rate; /* Bytes/second */ - - int mapping_flags; -#define DMA_MAP_MAPPED 0x00000001 - char neutral_byte; - int dma; /* DMA channel */ - - int applic_profile; /* Application profile (APF_*) */ - /* Interrupt callback stuff */ - void (*audio_callback) (int dev, int parm); - int callback_parm; - - int buf_flags[MAX_SUB_BUFFERS]; -#define BUFF_EOF 0x00000001 /* Increment eof count */ -#define BUFF_DIRTY 0x00000002 /* Buffer written */ -}; - -/* - * Structure for use with various microcontrollers and DSP processors - * in the recent sound cards. - */ -typedef struct coproc_operations -{ - char name[64]; - struct module *owner; - int (*open) (void *devc, int sub_device); - void (*close) (void *devc, int sub_device); - int (*ioctl) (void *devc, unsigned int cmd, void __user * arg, int local); - void (*reset) (void *devc); - - void *devc; /* Driver specific info */ -} coproc_operations; - -struct audio_driver -{ - struct module *owner; - int (*open) (int dev, int mode); - void (*close) (int dev); - void (*output_block) (int dev, unsigned long buf, - int count, int intrflag); - void (*start_input) (int dev, unsigned long buf, - int count, int intrflag); - int (*ioctl) (int dev, unsigned int cmd, void __user * arg); - int (*prepare_for_input) (int dev, int bufsize, int nbufs); - int (*prepare_for_output) (int dev, int bufsize, int nbufs); - void (*halt_io) (int dev); - int (*local_qlen)(int dev); - void (*copy_user) (int dev, - char *localbuf, int localoffs, - const char __user *userbuf, int useroffs, - int max_in, int max_out, - int *used, int *returned, - int len); - void (*halt_input) (int dev); - void (*halt_output) (int dev); - void (*trigger) (int dev, int bits); - int (*set_speed)(int dev, int speed); - unsigned int (*set_bits)(int dev, unsigned int bits); - short (*set_channels)(int dev, short channels); - void (*postprocess_write)(int dev); /* Device spesific postprocessing for written data */ - void (*preprocess_read)(int dev); /* Device spesific preprocessing for read data */ - void (*mmap)(int dev); -}; - -struct audio_operations -{ - char name[128]; - int flags; -#define NOTHING_SPECIAL 0x00 -#define NEEDS_RESTART 0x01 -#define DMA_AUTOMODE 0x02 -#define DMA_DUPLEX 0x04 -#define DMA_PSEUDO_AUTOMODE 0x08 -#define DMA_HARDSTOP 0x10 -#define DMA_EXACT 0x40 -#define DMA_NORESET 0x80 - int format_mask; /* Bitmask for supported audio formats */ - void *devc; /* Driver specific info */ - struct audio_driver *d; - void *portc; /* Driver specific info */ - struct dma_buffparms *dmap_in, *dmap_out; - struct coproc_operations *coproc; - int mixer_dev; - int enable_bits; - int open_mode; - int go; - int min_fragment; /* 0 == unlimited */ - int max_fragment; /* 0 == unlimited */ - int parent_dev; /* 0 -> no parent, 1 to n -> parent=parent_dev+1 */ - - /* fields formerly in dmabuf.c */ - wait_queue_head_t in_sleeper; - wait_queue_head_t out_sleeper; - wait_queue_head_t poll_sleeper; - - /* fields formerly in audio.c */ - int audio_mode; - -#define AM_NONE 0 -#define AM_WRITE OPEN_WRITE -#define AM_READ OPEN_READ - - int local_format; - int audio_format; - int local_conversion; -#define CNV_MU_LAW 0x00000001 - - /* large structures at the end to keep offsets small */ - struct dma_buffparms dmaps[2]; -}; - -int *load_mixer_volumes(char *name, int *levels, int present); - -struct mixer_operations -{ - struct module *owner; - char id[16]; - char name[64]; - int (*ioctl) (int dev, unsigned int cmd, void __user * arg); - - void *devc; - int modify_counter; -}; - -struct synth_operations -{ - struct module *owner; - char *id; /* Unique identifier (ASCII) max 29 char */ - struct synth_info *info; - int midi_dev; - int synth_type; - int synth_subtype; - - int (*open) (int dev, int mode); - void (*close) (int dev); - int (*ioctl) (int dev, unsigned int cmd, void __user * arg); - int (*kill_note) (int dev, int voice, int note, int velocity); - int (*start_note) (int dev, int voice, int note, int velocity); - int (*set_instr) (int dev, int voice, int instr); - void (*reset) (int dev); - void (*hw_control) (int dev, unsigned char *event); - int (*load_patch) (int dev, int format, const char __user *addr, - int count, int pmgr_flag); - void (*aftertouch) (int dev, int voice, int pressure); - void (*controller) (int dev, int voice, int ctrl_num, int value); - void (*panning) (int dev, int voice, int value); - void (*volume_method) (int dev, int mode); - void (*bender) (int dev, int chn, int value); - int (*alloc_voice) (int dev, int chn, int note, struct voice_alloc_info *alloc); - void (*setup_voice) (int dev, int voice, int chn); - int (*send_sysex)(int dev, unsigned char *bytes, int len); - - struct voice_alloc_info alloc; - struct channel_info chn_info[16]; - int emulation; -#define EMU_GM 1 /* General MIDI */ -#define EMU_XG 2 /* Yamaha XG */ -#define MAX_SYSEX_BUF 64 - unsigned char sysex_buf[MAX_SYSEX_BUF]; - int sysex_ptr; -}; - -struct midi_input_info -{ - /* MIDI input scanner variables */ -#define MI_MAX 10 - volatile int m_busy; - unsigned char m_buf[MI_MAX]; - unsigned char m_prev_status; /* For running status */ - int m_ptr; -#define MST_INIT 0 -#define MST_DATA 1 -#define MST_SYSEX 2 - int m_state; - int m_left; -}; - -struct midi_operations -{ - struct module *owner; - struct midi_info info; - struct synth_operations *converter; - struct midi_input_info in_info; - int (*open) (int dev, int mode, - void (*inputintr)(int dev, unsigned char data), - void (*outputintr)(int dev) - ); - void (*close) (int dev); - int (*ioctl) (int dev, unsigned int cmd, void __user * arg); - int (*outputc) (int dev, unsigned char data); - int (*start_read) (int dev); - int (*end_read) (int dev); - void (*kick)(int dev); - int (*command) (int dev, unsigned char *data); - int (*buffer_status) (int dev); - int (*prefix_cmd) (int dev, unsigned char status); - struct coproc_operations *coproc; - void *devc; -}; - -struct sound_lowlev_timer -{ - int dev; - int priority; - unsigned int (*tmr_start)(int dev, unsigned int usecs); - void (*tmr_disable)(int dev); - void (*tmr_restart)(int dev); -}; - -struct sound_timer_operations -{ - struct module *owner; - struct sound_timer_info info; - int priority; - int devlink; - int (*open)(int dev, int mode); - void (*close)(int dev); - int (*event)(int dev, unsigned char *ev); - unsigned long (*get_time)(int dev); - int (*ioctl) (int dev, unsigned int cmd, void __user * arg); - void (*arm_timer)(int dev, long time); -}; - -extern struct sound_timer_operations default_sound_timer; - -extern struct audio_operations *audio_devs[MAX_AUDIO_DEV]; -extern int num_audiodevs; -extern struct mixer_operations *mixer_devs[MAX_MIXER_DEV]; -extern int num_mixers; -extern struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV]; -extern int num_synths; -extern struct midi_operations *midi_devs[MAX_MIDI_DEV]; -extern int num_midis; -extern struct sound_timer_operations * sound_timer_devs[MAX_TIMER_DEV]; -extern int num_sound_timers; - -extern int sound_map_buffer (int dev, struct dma_buffparms *dmap, buffmem_desc *info); -void sound_timer_init (struct sound_lowlev_timer *t, char *name); -void sound_dma_intr (int dev, struct dma_buffparms *dmap, int chan); - -#define AUDIO_DRIVER_VERSION 2 -#define MIXER_DRIVER_VERSION 2 -int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver, - int driver_size, int flags, unsigned int format_mask, - void *devc, int dma1, int dma2); -int sound_install_mixer(int vers, char *name, struct mixer_operations *driver, - int driver_size, void *devc); - -void sound_unload_audiodev(int dev); -void sound_unload_mixerdev(int dev); -void sound_unload_mididev(int dev); -void sound_unload_synthdev(int dev); -void sound_unload_timerdev(int dev); -int sound_alloc_mixerdev(void); -int sound_alloc_timerdev(void); -int sound_alloc_synthdev(void); -int sound_alloc_mididev(void); -#endif /* _DEV_TABLE_H_ */ - diff --git a/sound/oss/dmabuf.c b/sound/oss/dmabuf.c deleted file mode 100644 index c5dd396c66a2..000000000000 --- a/sound/oss/dmabuf.c +++ /dev/null @@ -1,1268 +0,0 @@ -/* - * sound/oss/dmabuf.c - * - * The DMA buffer manager for digitized voice applications - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * Thomas Sailer : moved several static variables into struct audio_operations - * (which is grossly misnamed btw.) because they have the same - * lifetime as the rest in there and dynamic allocation saves - * 12k or so - * Thomas Sailer : remove {in,out}_sleep_flag. It was used for the sleeper to - * determine if it was woken up by the expiring timeout or by - * an explicit wake_up. The return value from schedule_timeout - * can be used instead; if 0, the wakeup was due to the timeout. - * - * Rob Riggs Added persistent DMA buffers (1998/10/17) - */ - -#define BE_CONSERVATIVE -#define SAMPLE_ROUNDUP 0 - -#include <linux/mm.h> -#include <linux/gfp.h> -#include <linux/sched/signal.h> - -#include "sound_config.h" -#include "sleep.h" - -#define DMAP_FREE_ON_CLOSE 0 -#define DMAP_KEEP_ON_CLOSE 1 -extern int sound_dmap_flag; - -static void dma_reset_output(int dev); -static void dma_reset_input(int dev); -static int local_start_dma(struct audio_operations *adev, unsigned long physaddr, int count, int dma_mode); - - - -static int debugmem; /* switched off by default */ -static int dma_buffsize = DSP_BUFFSIZE; - -static long dmabuf_timeout(struct dma_buffparms *dmap) -{ - long tmout; - - tmout = (dmap->fragment_size * HZ) / dmap->data_rate; - tmout += HZ / 5; /* Some safety distance */ - if (tmout < (HZ / 2)) - tmout = HZ / 2; - if (tmout > 20 * HZ) - tmout = 20 * HZ; - return tmout; -} - -static int sound_alloc_dmap(struct dma_buffparms *dmap) -{ - char *start_addr, *end_addr; - int dma_pagesize; - int sz, size; - struct page *page; - - dmap->mapping_flags &= ~DMA_MAP_MAPPED; - - if (dmap->raw_buf != NULL) - return 0; /* Already done */ - if (dma_buffsize < 4096) - dma_buffsize = 4096; - dma_pagesize = (dmap->dma < 4) ? (64 * 1024) : (128 * 1024); - - /* - * Now check for the Cyrix problem. - */ - - if(isa_dma_bridge_buggy==2) - dma_pagesize=32768; - - dmap->raw_buf = NULL; - dmap->buffsize = dma_buffsize; - if (dmap->buffsize > dma_pagesize) - dmap->buffsize = dma_pagesize; - start_addr = NULL; - /* - * Now loop until we get a free buffer. Try to get smaller buffer if - * it fails. Don't accept smaller than 8k buffer for performance - * reasons. - */ - while (start_addr == NULL && dmap->buffsize > PAGE_SIZE) { - for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1); - dmap->buffsize = PAGE_SIZE * (1 << sz); - start_addr = (char *) __get_free_pages(GFP_ATOMIC|GFP_DMA|__GFP_NOWARN, sz); - if (start_addr == NULL) - dmap->buffsize /= 2; - } - - if (start_addr == NULL) { - printk(KERN_WARNING "Sound error: Couldn't allocate DMA buffer\n"); - return -ENOMEM; - } else { - /* make some checks */ - end_addr = start_addr + dmap->buffsize - 1; - - if (debugmem) - printk(KERN_DEBUG "sound: start 0x%lx, end 0x%lx\n", (long) start_addr, (long) end_addr); - - /* now check if it fits into the same dma-pagesize */ - - if (((long) start_addr & ~(dma_pagesize - 1)) != ((long) end_addr & ~(dma_pagesize - 1)) - || end_addr >= (char *) (MAX_DMA_ADDRESS)) { - printk(KERN_ERR "sound: Got invalid address 0x%lx for %db DMA-buffer\n", (long) start_addr, dmap->buffsize); - return -EFAULT; - } - } - dmap->raw_buf = start_addr; - dmap->raw_buf_phys = dma_map_single(NULL, start_addr, dmap->buffsize, DMA_BIDIRECTIONAL); - - for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++) - SetPageReserved(page); - return 0; -} - -static void sound_free_dmap(struct dma_buffparms *dmap) -{ - int sz, size; - struct page *page; - unsigned long start_addr, end_addr; - - if (dmap->raw_buf == NULL) - return; - if (dmap->mapping_flags & DMA_MAP_MAPPED) - return; /* Don't free mmapped buffer. Will use it next time */ - for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1); - - start_addr = (unsigned long) dmap->raw_buf; - end_addr = start_addr + dmap->buffsize; - - for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++) - ClearPageReserved(page); - - dma_unmap_single(NULL, dmap->raw_buf_phys, dmap->buffsize, DMA_BIDIRECTIONAL); - free_pages((unsigned long) dmap->raw_buf, sz); - dmap->raw_buf = NULL; -} - - -/* Intel version !!!!!!!!! */ - -static int sound_start_dma(struct dma_buffparms *dmap, unsigned long physaddr, int count, int dma_mode) -{ - unsigned long flags; - int chan = dmap->dma; - - /* printk( "Start DMA%d %d, %d\n", chan, (int)(physaddr-dmap->raw_buf_phys), count); */ - - flags = claim_dma_lock(); - disable_dma(chan); - clear_dma_ff(chan); - set_dma_mode(chan, dma_mode); - set_dma_addr(chan, physaddr); - set_dma_count(chan, count); - enable_dma(chan); - release_dma_lock(flags); - - return 0; -} - -static void dma_init_buffers(struct dma_buffparms *dmap) -{ - dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0; - dmap->byte_counter = 0; - dmap->max_byte_counter = 8000 * 60 * 60; - dmap->bytes_in_use = dmap->buffsize; - - dmap->dma_mode = DMODE_NONE; - dmap->mapping_flags = 0; - dmap->neutral_byte = 0x80; - dmap->data_rate = 8000; - dmap->cfrag = -1; - dmap->closing = 0; - dmap->nbufs = 1; - dmap->flags = DMA_BUSY; /* Other flags off */ -} - -static int open_dmap(struct audio_operations *adev, int mode, struct dma_buffparms *dmap) -{ - int err; - - if (dmap->flags & DMA_BUSY) - return -EBUSY; - if ((err = sound_alloc_dmap(dmap)) < 0) - return err; - - if (dmap->raw_buf == NULL) { - printk(KERN_WARNING "Sound: DMA buffers not available\n"); - return -ENOSPC; /* Memory allocation failed during boot */ - } - if (dmap->dma >= 0 && sound_open_dma(dmap->dma, adev->name)) { - printk(KERN_WARNING "Unable to grab(2) DMA%d for the audio driver\n", dmap->dma); - return -EBUSY; - } - dma_init_buffers(dmap); - spin_lock_init(&dmap->lock); - dmap->open_mode = mode; - dmap->subdivision = dmap->underrun_count = 0; - dmap->fragment_size = 0; - dmap->max_fragments = 65536; /* Just a large value */ - dmap->byte_counter = 0; - dmap->max_byte_counter = 8000 * 60 * 60; - dmap->applic_profile = APF_NORMAL; - dmap->needs_reorg = 1; - dmap->audio_callback = NULL; - dmap->callback_parm = 0; - return 0; -} - -static void close_dmap(struct audio_operations *adev, struct dma_buffparms *dmap) -{ - unsigned long flags; - - if (dmap->dma >= 0) { - sound_close_dma(dmap->dma); - flags=claim_dma_lock(); - disable_dma(dmap->dma); - release_dma_lock(flags); - } - if (dmap->flags & DMA_BUSY) - dmap->dma_mode = DMODE_NONE; - dmap->flags &= ~DMA_BUSY; - - if (sound_dmap_flag == DMAP_FREE_ON_CLOSE) - sound_free_dmap(dmap); -} - - -static unsigned int default_set_bits(int dev, unsigned int bits) -{ - mm_segment_t fs = get_fs(); - - set_fs(get_ds()); - audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SETFMT, (void __user *)&bits); - set_fs(fs); - return bits; -} - -static int default_set_speed(int dev, int speed) -{ - mm_segment_t fs = get_fs(); - - set_fs(get_ds()); - audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SPEED, (void __user *)&speed); - set_fs(fs); - return speed; -} - -static short default_set_channels(int dev, short channels) -{ - int c = channels; - mm_segment_t fs = get_fs(); - - set_fs(get_ds()); - audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_CHANNELS, (void __user *)&c); - set_fs(fs); - return c; -} - -static void check_driver(struct audio_driver *d) -{ - if (d->set_speed == NULL) - d->set_speed = default_set_speed; - if (d->set_bits == NULL) - d->set_bits = default_set_bits; - if (d->set_channels == NULL) - d->set_channels = default_set_channels; -} - -int DMAbuf_open(int dev, int mode) -{ - struct audio_operations *adev = audio_devs[dev]; - int retval; - struct dma_buffparms *dmap_in = NULL; - struct dma_buffparms *dmap_out = NULL; - - if (!adev) - return -ENXIO; - if (!(adev->flags & DMA_DUPLEX)) - adev->dmap_in = adev->dmap_out; - check_driver(adev->d); - - if ((retval = adev->d->open(dev, mode)) < 0) - return retval; - dmap_out = adev->dmap_out; - dmap_in = adev->dmap_in; - if (dmap_in == dmap_out) - adev->flags &= ~DMA_DUPLEX; - - if (mode & OPEN_WRITE) { - if ((retval = open_dmap(adev, mode, dmap_out)) < 0) { - adev->d->close(dev); - return retval; - } - } - adev->enable_bits = mode; - - if (mode == OPEN_READ || (mode != OPEN_WRITE && (adev->flags & DMA_DUPLEX))) { - if ((retval = open_dmap(adev, mode, dmap_in)) < 0) { - adev->d->close(dev); - if (mode & OPEN_WRITE) - close_dmap(adev, dmap_out); - return retval; - } - } - adev->open_mode = mode; - adev->go = 1; - - adev->d->set_bits(dev, 8); - adev->d->set_channels(dev, 1); - adev->d->set_speed(dev, DSP_DEFAULT_SPEED); - if (adev->dmap_out->dma_mode == DMODE_OUTPUT) - memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte, - adev->dmap_out->bytes_in_use); - return 0; -} -/* MUST not hold the spinlock */ -void DMAbuf_reset(int dev) -{ - if (audio_devs[dev]->open_mode & OPEN_WRITE) - dma_reset_output(dev); - - if (audio_devs[dev]->open_mode & OPEN_READ) - dma_reset_input(dev); -} - -static void dma_reset_output(int dev) -{ - struct audio_operations *adev = audio_devs[dev]; - unsigned long flags,f ; - struct dma_buffparms *dmap = adev->dmap_out; - - if (!(dmap->flags & DMA_STARTED)) /* DMA is not active */ - return; - - /* - * First wait until the current fragment has been played completely - */ - spin_lock_irqsave(&dmap->lock,flags); - adev->dmap_out->flags |= DMA_SYNCING; - - adev->dmap_out->underrun_count = 0; - if (!signal_pending(current) && adev->dmap_out->qlen && - adev->dmap_out->underrun_count == 0){ - spin_unlock_irqrestore(&dmap->lock,flags); - oss_broken_sleep_on(&adev->out_sleeper, dmabuf_timeout(dmap)); - spin_lock_irqsave(&dmap->lock,flags); - } - adev->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE); - - /* - * Finally shut the device off - */ - if (!(adev->flags & DMA_DUPLEX) || !adev->d->halt_output) - adev->d->halt_io(dev); - else - adev->d->halt_output(dev); - adev->dmap_out->flags &= ~DMA_STARTED; - - f=claim_dma_lock(); - clear_dma_ff(dmap->dma); - disable_dma(dmap->dma); - release_dma_lock(f); - - dmap->byte_counter = 0; - reorganize_buffers(dev, adev->dmap_out, 0); - dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0; - spin_unlock_irqrestore(&dmap->lock,flags); -} - -static void dma_reset_input(int dev) -{ - struct audio_operations *adev = audio_devs[dev]; - unsigned long flags; - struct dma_buffparms *dmap = adev->dmap_in; - - spin_lock_irqsave(&dmap->lock,flags); - if (!(adev->flags & DMA_DUPLEX) || !adev->d->halt_input) - adev->d->halt_io(dev); - else - adev->d->halt_input(dev); - adev->dmap_in->flags &= ~DMA_STARTED; - - dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0; - dmap->byte_counter = 0; - reorganize_buffers(dev, adev->dmap_in, 1); - spin_unlock_irqrestore(&dmap->lock,flags); -} -/* MUST be called with holding the dmap->lock */ -void DMAbuf_launch_output(int dev, struct dma_buffparms *dmap) -{ - struct audio_operations *adev = audio_devs[dev]; - - if (!((adev->enable_bits * adev->go) & PCM_ENABLE_OUTPUT)) - return; /* Don't start DMA yet */ - dmap->dma_mode = DMODE_OUTPUT; - - if (!(dmap->flags & DMA_ACTIVE) || !(adev->flags & DMA_AUTOMODE) || (dmap->flags & DMA_NODMA)) { - if (!(dmap->flags & DMA_STARTED)) { - reorganize_buffers(dev, dmap, 0); - if (adev->d->prepare_for_output(dev, dmap->fragment_size, dmap->nbufs)) - return; - if (!(dmap->flags & DMA_NODMA)) - local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use,DMA_MODE_WRITE); - dmap->flags |= DMA_STARTED; - } - if (dmap->counts[dmap->qhead] == 0) - dmap->counts[dmap->qhead] = dmap->fragment_size; - dmap->dma_mode = DMODE_OUTPUT; - adev->d->output_block(dev, dmap->raw_buf_phys + dmap->qhead * dmap->fragment_size, - dmap->counts[dmap->qhead], 1); - if (adev->d->trigger) - adev->d->trigger(dev,adev->enable_bits * adev->go); - } - dmap->flags |= DMA_ACTIVE; -} - -int DMAbuf_sync(int dev) -{ - struct audio_operations *adev = audio_devs[dev]; - unsigned long flags; - int n = 0; - struct dma_buffparms *dmap; - - if (!adev->go && !(adev->enable_bits & PCM_ENABLE_OUTPUT)) - return 0; - - if (adev->dmap_out->dma_mode == DMODE_OUTPUT) { - dmap = adev->dmap_out; - spin_lock_irqsave(&dmap->lock,flags); - if (dmap->qlen > 0 && !(dmap->flags & DMA_ACTIVE)) - DMAbuf_launch_output(dev, dmap); - adev->dmap_out->flags |= DMA_SYNCING; - adev->dmap_out->underrun_count = 0; - while (!signal_pending(current) && n++ < adev->dmap_out->nbufs && - adev->dmap_out->qlen && adev->dmap_out->underrun_count == 0) { - long t = dmabuf_timeout(dmap); - spin_unlock_irqrestore(&dmap->lock,flags); - /* FIXME: not safe may miss events */ - t = oss_broken_sleep_on(&adev->out_sleeper, t); - spin_lock_irqsave(&dmap->lock,flags); - if (!t) { - adev->dmap_out->flags &= ~DMA_SYNCING; - spin_unlock_irqrestore(&dmap->lock,flags); - return adev->dmap_out->qlen; - } - } - adev->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE); - - /* - * Some devices such as GUS have huge amount of on board RAM for the - * audio data. We have to wait until the device has finished playing. - */ - - /* still holding the lock */ - if (adev->d->local_qlen) { /* Device has hidden buffers */ - while (!signal_pending(current) && - adev->d->local_qlen(dev)){ - spin_unlock_irqrestore(&dmap->lock,flags); - oss_broken_sleep_on(&adev->out_sleeper, - dmabuf_timeout(dmap)); - spin_lock_irqsave(&dmap->lock,flags); - } - } - spin_unlock_irqrestore(&dmap->lock,flags); - } - adev->dmap_out->dma_mode = DMODE_NONE; - return adev->dmap_out->qlen; -} - -int DMAbuf_release(int dev, int mode) -{ - struct audio_operations *adev = audio_devs[dev]; - struct dma_buffparms *dmap; - unsigned long flags; - - dmap = adev->dmap_out; - if (adev->open_mode & OPEN_WRITE) - adev->dmap_out->closing = 1; - - if (adev->open_mode & OPEN_READ){ - adev->dmap_in->closing = 1; - dmap = adev->dmap_in; - } - if (adev->open_mode & OPEN_WRITE) - if (!(adev->dmap_out->mapping_flags & DMA_MAP_MAPPED)) - if (!signal_pending(current) && (adev->dmap_out->dma_mode == DMODE_OUTPUT)) - DMAbuf_sync(dev); - if (adev->dmap_out->dma_mode == DMODE_OUTPUT) - memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte, adev->dmap_out->bytes_in_use); - - DMAbuf_reset(dev); - spin_lock_irqsave(&dmap->lock,flags); - adev->d->close(dev); - - if (adev->open_mode & OPEN_WRITE) - close_dmap(adev, adev->dmap_out); - - if (adev->open_mode == OPEN_READ || - (adev->open_mode != OPEN_WRITE && - (adev->flags & DMA_DUPLEX))) - close_dmap(adev, adev->dmap_in); - adev->open_mode = 0; - spin_unlock_irqrestore(&dmap->lock,flags); - return 0; -} -/* called with dmap->lock dold */ -int DMAbuf_activate_recording(int dev, struct dma_buffparms *dmap) -{ - struct audio_operations *adev = audio_devs[dev]; - int err; - - if (!(adev->open_mode & OPEN_READ)) - return 0; - if (!(adev->enable_bits & PCM_ENABLE_INPUT)) - return 0; - if (dmap->dma_mode == DMODE_OUTPUT) { /* Direction change */ - /* release lock - it's not recursive */ - spin_unlock_irq(&dmap->lock); - DMAbuf_sync(dev); - DMAbuf_reset(dev); - spin_lock_irq(&dmap->lock); - dmap->dma_mode = DMODE_NONE; - } - if (!dmap->dma_mode) { - reorganize_buffers(dev, dmap, 1); - if ((err = adev->d->prepare_for_input(dev, - dmap->fragment_size, dmap->nbufs)) < 0) - return err; - dmap->dma_mode = DMODE_INPUT; - } - if (!(dmap->flags & DMA_ACTIVE)) { - if (dmap->needs_reorg) - reorganize_buffers(dev, dmap, 0); - local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use, DMA_MODE_READ); - adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, - dmap->fragment_size, 0); - dmap->flags |= DMA_ACTIVE; - if (adev->d->trigger) - adev->d->trigger(dev, adev->enable_bits * adev->go); - } - return 0; -} -/* acquires lock */ -int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock) -{ - struct audio_operations *adev = audio_devs[dev]; - unsigned long flags; - int err = 0, n = 0; - struct dma_buffparms *dmap = adev->dmap_in; - - if (!(adev->open_mode & OPEN_READ)) - return -EIO; - spin_lock_irqsave(&dmap->lock,flags); - if (dmap->needs_reorg) - reorganize_buffers(dev, dmap, 0); - if (adev->dmap_in->mapping_flags & DMA_MAP_MAPPED) { -/* printk(KERN_WARNING "Sound: Can't read from mmapped device (1)\n");*/ - spin_unlock_irqrestore(&dmap->lock,flags); - return -EINVAL; - } else while (dmap->qlen <= 0 && n++ < 10) { - long timeout = MAX_SCHEDULE_TIMEOUT; - if (!(adev->enable_bits & PCM_ENABLE_INPUT) || !adev->go) { - spin_unlock_irqrestore(&dmap->lock,flags); - return -EAGAIN; - } - if ((err = DMAbuf_activate_recording(dev, dmap)) < 0) { - spin_unlock_irqrestore(&dmap->lock,flags); - return err; - } - /* Wait for the next block */ - - if (dontblock) { - spin_unlock_irqrestore(&dmap->lock,flags); - return -EAGAIN; - } - if (adev->go) - timeout = dmabuf_timeout(dmap); - - spin_unlock_irqrestore(&dmap->lock,flags); - timeout = oss_broken_sleep_on(&adev->in_sleeper, timeout); - if (!timeout) { - /* FIXME: include device name */ - err = -EIO; - printk(KERN_WARNING "Sound: DMA (input) timed out - IRQ/DRQ config error?\n"); - dma_reset_input(dev); - } else - err = -EINTR; - spin_lock_irqsave(&dmap->lock,flags); - } - spin_unlock_irqrestore(&dmap->lock,flags); - - if (dmap->qlen <= 0) - return err ? err : -EINTR; - *buf = &dmap->raw_buf[dmap->qhead * dmap->fragment_size + dmap->counts[dmap->qhead]]; - *len = dmap->fragment_size - dmap->counts[dmap->qhead]; - - return dmap->qhead; -} - -int DMAbuf_rmchars(int dev, int buff_no, int c) -{ - struct audio_operations *adev = audio_devs[dev]; - struct dma_buffparms *dmap = adev->dmap_in; - int p = dmap->counts[dmap->qhead] + c; - - if (dmap->mapping_flags & DMA_MAP_MAPPED) - { -/* printk("Sound: Can't read from mmapped device (2)\n");*/ - return -EINVAL; - } - else if (dmap->qlen <= 0) - return -EIO; - else if (p >= dmap->fragment_size) { /* This buffer is completely empty */ - dmap->counts[dmap->qhead] = 0; - dmap->qlen--; - dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; - } - else dmap->counts[dmap->qhead] = p; - - return 0; -} -/* MUST be called with dmap->lock hold */ -int DMAbuf_get_buffer_pointer(int dev, struct dma_buffparms *dmap, int direction) -{ - /* - * Try to approximate the active byte position of the DMA pointer within the - * buffer area as well as possible. - */ - - int pos; - unsigned long f; - - if (!(dmap->flags & DMA_ACTIVE)) - pos = 0; - else { - int chan = dmap->dma; - - f=claim_dma_lock(); - clear_dma_ff(chan); - - if(!isa_dma_bridge_buggy) - disable_dma(dmap->dma); - - pos = get_dma_residue(chan); - - pos = dmap->bytes_in_use - pos; - - if (!(dmap->mapping_flags & DMA_MAP_MAPPED)) { - if (direction == DMODE_OUTPUT) { - if (dmap->qhead == 0) - if (pos > dmap->fragment_size) - pos = 0; - } else { - if (dmap->qtail == 0) - if (pos > dmap->fragment_size) - pos = 0; - } - } - if (pos < 0) - pos = 0; - if (pos >= dmap->bytes_in_use) - pos = 0; - - if(!isa_dma_bridge_buggy) - enable_dma(dmap->dma); - - release_dma_lock(f); - } - /* printk( "%04x ", pos); */ - - return pos; -} - -/* - * DMAbuf_start_devices() is called by the /dev/music driver to start - * one or more audio devices at desired moment. - */ - -void DMAbuf_start_devices(unsigned int devmask) -{ - struct audio_operations *adev; - int dev; - - for (dev = 0; dev < num_audiodevs; dev++) { - if (!(devmask & (1 << dev))) - continue; - if (!(adev = audio_devs[dev])) - continue; - if (adev->open_mode == 0) - continue; - if (adev->go) - continue; - /* OK to start the device */ - adev->go = 1; - if (adev->d->trigger) - adev->d->trigger(dev,adev->enable_bits * adev->go); - } -} -/* via poll called without a lock ?*/ -int DMAbuf_space_in_queue(int dev) -{ - struct audio_operations *adev = audio_devs[dev]; - int len, max, tmp; - struct dma_buffparms *dmap = adev->dmap_out; - int lim = dmap->nbufs; - - if (lim < 2) - lim = 2; - - if (dmap->qlen >= lim) /* No space at all */ - return 0; - - /* - * Verify that there are no more pending buffers than the limit - * defined by the process. - */ - - max = dmap->max_fragments; - if (max > lim) - max = lim; - len = dmap->qlen; - - if (adev->d->local_qlen) { - tmp = adev->d->local_qlen(dev); - if (tmp && len) - tmp--; /* This buffer has been counted twice */ - len += tmp; - } - if (dmap->byte_counter % dmap->fragment_size) /* There is a partial fragment */ - len = len + 1; - - if (len >= max) - return 0; - return max - len; -} -/* MUST not hold the spinlock - this function may sleep */ -static int output_sleep(int dev, int dontblock) -{ - struct audio_operations *adev = audio_devs[dev]; - int err = 0; - struct dma_buffparms *dmap = adev->dmap_out; - long timeout; - long timeout_value; - - if (dontblock) - return -EAGAIN; - if (!(adev->enable_bits & PCM_ENABLE_OUTPUT)) - return -EAGAIN; - - /* - * Wait for free space - */ - if (signal_pending(current)) - return -EINTR; - timeout = (adev->go && !(dmap->flags & DMA_NOTIMEOUT)); - if (timeout) - timeout_value = dmabuf_timeout(dmap); - else - timeout_value = MAX_SCHEDULE_TIMEOUT; - timeout_value = oss_broken_sleep_on(&adev->out_sleeper, timeout_value); - if (timeout != MAX_SCHEDULE_TIMEOUT && !timeout_value) { - printk(KERN_WARNING "Sound: DMA (output) timed out - IRQ/DRQ config error?\n"); - dma_reset_output(dev); - } else { - if (signal_pending(current)) - err = -EINTR; - } - return err; -} -/* called with the lock held */ -static int find_output_space(int dev, char **buf, int *size) -{ - struct audio_operations *adev = audio_devs[dev]; - struct dma_buffparms *dmap = adev->dmap_out; - unsigned long active_offs; - long len, offs; - int maxfrags; - int occupied_bytes = (dmap->user_counter % dmap->fragment_size); - - *buf = dmap->raw_buf; - if (!(maxfrags = DMAbuf_space_in_queue(dev)) && !occupied_bytes) - return 0; - -#ifdef BE_CONSERVATIVE - active_offs = dmap->byte_counter + dmap->qhead * dmap->fragment_size; -#else - active_offs = max(DMAbuf_get_buffer_pointer(dev, dmap, DMODE_OUTPUT), 0); - /* Check for pointer wrapping situation */ - if (active_offs >= dmap->bytes_in_use) - active_offs = 0; - active_offs += dmap->byte_counter; -#endif - - offs = (dmap->user_counter % dmap->bytes_in_use) & ~SAMPLE_ROUNDUP; - if (offs < 0 || offs >= dmap->bytes_in_use) { - printk(KERN_ERR "Sound: Got unexpected offs %ld. Giving up.\n", offs); - printk("Counter = %ld, bytes=%d\n", dmap->user_counter, dmap->bytes_in_use); - return 0; - } - *buf = dmap->raw_buf + offs; - - len = active_offs + dmap->bytes_in_use - dmap->user_counter; /* Number of unused bytes in buffer */ - - if ((offs + len) > dmap->bytes_in_use) - len = dmap->bytes_in_use - offs; - if (len < 0) { - return 0; - } - if (len > ((maxfrags * dmap->fragment_size) - occupied_bytes)) - len = (maxfrags * dmap->fragment_size) - occupied_bytes; - *size = len & ~SAMPLE_ROUNDUP; - return (*size > 0); -} -/* acquires lock */ -int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock) -{ - struct audio_operations *adev = audio_devs[dev]; - unsigned long flags; - int err = -EIO; - struct dma_buffparms *dmap = adev->dmap_out; - - if (dmap->mapping_flags & DMA_MAP_MAPPED) { -/* printk(KERN_DEBUG "Sound: Can't write to mmapped device (3)\n");*/ - return -EINVAL; - } - spin_lock_irqsave(&dmap->lock,flags); - if (dmap->needs_reorg) - reorganize_buffers(dev, dmap, 0); - - if (dmap->dma_mode == DMODE_INPUT) { /* Direction change */ - spin_unlock_irqrestore(&dmap->lock,flags); - DMAbuf_reset(dev); - spin_lock_irqsave(&dmap->lock,flags); - } - dmap->dma_mode = DMODE_OUTPUT; - - while (find_output_space(dev, buf, size) <= 0) { - spin_unlock_irqrestore(&dmap->lock,flags); - if ((err = output_sleep(dev, dontblock)) < 0) { - return err; - } - spin_lock_irqsave(&dmap->lock,flags); - } - - spin_unlock_irqrestore(&dmap->lock,flags); - return 0; -} -/* has to acquire dmap->lock */ -int DMAbuf_move_wrpointer(int dev, int l) -{ - struct audio_operations *adev = audio_devs[dev]; - struct dma_buffparms *dmap = adev->dmap_out; - unsigned long ptr; - unsigned long end_ptr, p; - int post; - unsigned long flags; - - spin_lock_irqsave(&dmap->lock,flags); - post= (dmap->flags & DMA_POST); - ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size; - - dmap->flags &= ~DMA_POST; - dmap->cfrag = -1; - dmap->user_counter += l; - dmap->flags |= DMA_DIRTY; - - if (dmap->byte_counter >= dmap->max_byte_counter) { - /* Wrap the byte counters */ - long decr = dmap->byte_counter; - dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use); - decr -= dmap->byte_counter; - dmap->user_counter -= decr; - } - end_ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size; - - p = (dmap->user_counter - 1) % dmap->bytes_in_use; - dmap->neutral_byte = dmap->raw_buf[p]; - - /* Update the fragment based bookkeeping too */ - while (ptr < end_ptr) { - dmap->counts[dmap->qtail] = dmap->fragment_size; - dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; - dmap->qlen++; - ptr += dmap->fragment_size; - } - - dmap->counts[dmap->qtail] = dmap->user_counter - ptr; - - /* - * Let the low level driver perform some postprocessing to - * the written data. - */ - if (adev->d->postprocess_write) - adev->d->postprocess_write(dev); - - if (!(dmap->flags & DMA_ACTIVE)) - if (dmap->qlen > 1 || (dmap->qlen > 0 && (post || dmap->qlen >= dmap->nbufs - 1))) - DMAbuf_launch_output(dev, dmap); - - spin_unlock_irqrestore(&dmap->lock,flags); - return 0; -} - -int DMAbuf_start_dma(int dev, unsigned long physaddr, int count, int dma_mode) -{ - struct audio_operations *adev = audio_devs[dev]; - struct dma_buffparms *dmap = (dma_mode == DMA_MODE_WRITE) ? adev->dmap_out : adev->dmap_in; - - if (dmap->raw_buf == NULL) { - printk(KERN_ERR "sound: DMA buffer(1) == NULL\n"); - printk("Device %d, chn=%s\n", dev, (dmap == adev->dmap_out) ? "out" : "in"); - return 0; - } - if (dmap->dma < 0) - return 0; - sound_start_dma(dmap, physaddr, count, dma_mode); - return count; -} -EXPORT_SYMBOL(DMAbuf_start_dma); - -static int local_start_dma(struct audio_operations *adev, unsigned long physaddr, int count, int dma_mode) -{ - struct dma_buffparms *dmap = (dma_mode == DMA_MODE_WRITE) ? adev->dmap_out : adev->dmap_in; - - if (dmap->raw_buf == NULL) { - printk(KERN_ERR "sound: DMA buffer(2) == NULL\n"); - printk(KERN_ERR "Device %s, chn=%s\n", adev->name, (dmap == adev->dmap_out) ? "out" : "in"); - return 0; - } - if (dmap->flags & DMA_NODMA) - return 1; - if (dmap->dma < 0) - return 0; - sound_start_dma(dmap, dmap->raw_buf_phys, dmap->bytes_in_use, dma_mode | DMA_AUTOINIT); - dmap->flags |= DMA_STARTED; - return count; -} - -static void finish_output_interrupt(int dev, struct dma_buffparms *dmap) -{ - struct audio_operations *adev = audio_devs[dev]; - - if (dmap->audio_callback != NULL) - dmap->audio_callback(dev, dmap->callback_parm); - wake_up(&adev->out_sleeper); - wake_up(&adev->poll_sleeper); -} -/* called with dmap->lock held in irq context*/ -static void do_outputintr(int dev, int dummy) -{ - struct audio_operations *adev = audio_devs[dev]; - struct dma_buffparms *dmap = adev->dmap_out; - int this_fragment; - - if (dmap->raw_buf == NULL) { - printk(KERN_ERR "Sound: Error. Audio interrupt (%d) after freeing buffers.\n", dev); - return; - } - if (dmap->mapping_flags & DMA_MAP_MAPPED) { /* Virtual memory mapped access */ - /* mmapped access */ - dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; - if (dmap->qhead == 0) { /* Wrapped */ - dmap->byte_counter += dmap->bytes_in_use; - if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */ - long decr = dmap->byte_counter; - dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use); - decr -= dmap->byte_counter; - dmap->user_counter -= decr; - } - } - dmap->qlen++; /* Yes increment it (don't decrement) */ - if (!(adev->flags & DMA_AUTOMODE)) - dmap->flags &= ~DMA_ACTIVE; - dmap->counts[dmap->qhead] = dmap->fragment_size; - DMAbuf_launch_output(dev, dmap); - finish_output_interrupt(dev, dmap); - return; - } - - dmap->qlen--; - this_fragment = dmap->qhead; - dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; - - if (dmap->qhead == 0) { /* Wrapped */ - dmap->byte_counter += dmap->bytes_in_use; - if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */ - long decr = dmap->byte_counter; - dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use); - decr -= dmap->byte_counter; - dmap->user_counter -= decr; - } - } - if (!(adev->flags & DMA_AUTOMODE)) - dmap->flags &= ~DMA_ACTIVE; - - /* - * This is dmap->qlen <= 0 except when closing when - * dmap->qlen < 0 - */ - - while (dmap->qlen <= -dmap->closing) { - dmap->underrun_count++; - dmap->qlen++; - if ((dmap->flags & DMA_DIRTY) && dmap->applic_profile != APF_CPUINTENS) { - dmap->flags &= ~DMA_DIRTY; - memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte, - adev->dmap_out->buffsize); - } - dmap->user_counter += dmap->fragment_size; - dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; - } - if (dmap->qlen > 0) - DMAbuf_launch_output(dev, dmap); - finish_output_interrupt(dev, dmap); -} -/* called in irq context */ -void DMAbuf_outputintr(int dev, int notify_only) -{ - struct audio_operations *adev = audio_devs[dev]; - unsigned long flags; - struct dma_buffparms *dmap = adev->dmap_out; - - spin_lock_irqsave(&dmap->lock,flags); - if (!(dmap->flags & DMA_NODMA)) { - int chan = dmap->dma, pos, n; - unsigned long f; - - f=claim_dma_lock(); - - if(!isa_dma_bridge_buggy) - disable_dma(dmap->dma); - clear_dma_ff(chan); - pos = dmap->bytes_in_use - get_dma_residue(chan); - if(!isa_dma_bridge_buggy) - enable_dma(dmap->dma); - release_dma_lock(f); - - pos = pos / dmap->fragment_size; /* Actual qhead */ - if (pos < 0 || pos >= dmap->nbufs) - pos = 0; - n = 0; - while (dmap->qhead != pos && n++ < dmap->nbufs) - do_outputintr(dev, notify_only); - } - else - do_outputintr(dev, notify_only); - spin_unlock_irqrestore(&dmap->lock,flags); -} -EXPORT_SYMBOL(DMAbuf_outputintr); - -/* called with dmap->lock held in irq context */ -static void do_inputintr(int dev) -{ - struct audio_operations *adev = audio_devs[dev]; - struct dma_buffparms *dmap = adev->dmap_in; - - if (dmap->raw_buf == NULL) { - printk(KERN_ERR "Sound: Fatal error. Audio interrupt after freeing buffers.\n"); - return; - } - if (dmap->mapping_flags & DMA_MAP_MAPPED) { - dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; - if (dmap->qtail == 0) { /* Wrapped */ - dmap->byte_counter += dmap->bytes_in_use; - if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */ - long decr = dmap->byte_counter; - dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use; - decr -= dmap->byte_counter; - dmap->user_counter -= decr; - } - } - dmap->qlen++; - - if (!(adev->flags & DMA_AUTOMODE)) { - if (dmap->needs_reorg) - reorganize_buffers(dev, dmap, 0); - local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use,DMA_MODE_READ); - adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, - dmap->fragment_size, 1); - if (adev->d->trigger) - adev->d->trigger(dev, adev->enable_bits * adev->go); - } - dmap->flags |= DMA_ACTIVE; - } else if (dmap->qlen >= (dmap->nbufs - 1)) { - printk(KERN_WARNING "Sound: Recording overrun\n"); - dmap->underrun_count++; - - /* Just throw away the oldest fragment but keep the engine running */ - dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; - dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; - } else if (dmap->qlen >= 0 && dmap->qlen < dmap->nbufs) { - dmap->qlen++; - dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; - if (dmap->qtail == 0) { /* Wrapped */ - dmap->byte_counter += dmap->bytes_in_use; - if (dmap->byte_counter >= dmap->max_byte_counter) { /* Overflow */ - long decr = dmap->byte_counter; - dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use; - decr -= dmap->byte_counter; - dmap->user_counter -= decr; - } - } - } - if (!(adev->flags & DMA_AUTOMODE) || (dmap->flags & DMA_NODMA)) { - local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use, DMA_MODE_READ); - adev->d->start_input(dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, dmap->fragment_size, 1); - if (adev->d->trigger) - adev->d->trigger(dev,adev->enable_bits * adev->go); - } - dmap->flags |= DMA_ACTIVE; - if (dmap->qlen > 0) - { - wake_up(&adev->in_sleeper); - wake_up(&adev->poll_sleeper); - } -} -/* called in irq context */ -void DMAbuf_inputintr(int dev) -{ - struct audio_operations *adev = audio_devs[dev]; - struct dma_buffparms *dmap = adev->dmap_in; - unsigned long flags; - - spin_lock_irqsave(&dmap->lock,flags); - - if (!(dmap->flags & DMA_NODMA)) { - int chan = dmap->dma, pos, n; - unsigned long f; - - f=claim_dma_lock(); - if(!isa_dma_bridge_buggy) - disable_dma(dmap->dma); - clear_dma_ff(chan); - pos = dmap->bytes_in_use - get_dma_residue(chan); - if(!isa_dma_bridge_buggy) - enable_dma(dmap->dma); - release_dma_lock(f); - - pos = pos / dmap->fragment_size; /* Actual qhead */ - if (pos < 0 || pos >= dmap->nbufs) - pos = 0; - - n = 0; - while (dmap->qtail != pos && ++n < dmap->nbufs) - do_inputintr(dev); - } else - do_inputintr(dev); - spin_unlock_irqrestore(&dmap->lock,flags); -} -EXPORT_SYMBOL(DMAbuf_inputintr); - -void DMAbuf_init(int dev, int dma1, int dma2) -{ - struct audio_operations *adev = audio_devs[dev]; - /* - * NOTE! This routine could be called several times. - */ - - if (adev && adev->dmap_out == NULL) { - if (adev->d == NULL) - panic("OSS: audio_devs[%d]->d == NULL\n", dev); - - if (adev->parent_dev) { /* Use DMA map of the parent dev */ - int parent = adev->parent_dev - 1; - adev->dmap_out = audio_devs[parent]->dmap_out; - adev->dmap_in = audio_devs[parent]->dmap_in; - } else { - adev->dmap_out = adev->dmap_in = &adev->dmaps[0]; - adev->dmap_out->dma = dma1; - if (adev->flags & DMA_DUPLEX) { - adev->dmap_in = &adev->dmaps[1]; - adev->dmap_in->dma = dma2; - } - } - /* Persistent DMA buffers allocated here */ - if (sound_dmap_flag == DMAP_KEEP_ON_CLOSE) { - if (adev->dmap_in->raw_buf == NULL) - sound_alloc_dmap(adev->dmap_in); - if (adev->dmap_out->raw_buf == NULL) - sound_alloc_dmap(adev->dmap_out); - } - } -} - -/* No kernel lock - DMAbuf_activate_recording protected by global cli/sti */ -static unsigned int poll_input(struct file * file, int dev, poll_table *wait) -{ - struct audio_operations *adev = audio_devs[dev]; - struct dma_buffparms *dmap = adev->dmap_in; - - if (!(adev->open_mode & OPEN_READ)) - return 0; - if (dmap->mapping_flags & DMA_MAP_MAPPED) { - if (dmap->qlen) - return POLLIN | POLLRDNORM; - return 0; - } - if (dmap->dma_mode != DMODE_INPUT) { - if (dmap->dma_mode == DMODE_NONE && - adev->enable_bits & PCM_ENABLE_INPUT && - !dmap->qlen && adev->go) { - unsigned long flags; - - spin_lock_irqsave(&dmap->lock,flags); - DMAbuf_activate_recording(dev, dmap); - spin_unlock_irqrestore(&dmap->lock,flags); - } - return 0; - } - if (!dmap->qlen) - return 0; - return POLLIN | POLLRDNORM; -} - -static unsigned int poll_output(struct file * file, int dev, poll_table *wait) -{ - struct audio_operations *adev = audio_devs[dev]; - struct dma_buffparms *dmap = adev->dmap_out; - - if (!(adev->open_mode & OPEN_WRITE)) - return 0; - if (dmap->mapping_flags & DMA_MAP_MAPPED) { - if (dmap->qlen) - return POLLOUT | POLLWRNORM; - return 0; - } - if (dmap->dma_mode == DMODE_INPUT) - return 0; - if (dmap->dma_mode == DMODE_NONE) - return POLLOUT | POLLWRNORM; - if (!DMAbuf_space_in_queue(dev)) - return 0; - return POLLOUT | POLLWRNORM; -} - -unsigned int DMAbuf_poll(struct file * file, int dev, poll_table *wait) -{ - struct audio_operations *adev = audio_devs[dev]; - poll_wait(file, &adev->poll_sleeper, wait); - return poll_input(file, dev, wait) | poll_output(file, dev, wait); -} - -void DMAbuf_deinit(int dev) -{ - struct audio_operations *adev = audio_devs[dev]; - /* This routine is called when driver is being unloaded */ - if (!adev) - return; - - /* Persistent DMA buffers deallocated here */ - if (sound_dmap_flag == DMAP_KEEP_ON_CLOSE) { - sound_free_dmap(adev->dmap_out); - if (adev->flags & DMA_DUPLEX) - sound_free_dmap(adev->dmap_in); - } -} diff --git a/sound/oss/hex2hex.c b/sound/oss/hex2hex.c deleted file mode 100644 index f76d729b0196..000000000000 --- a/sound/oss/hex2hex.c +++ /dev/null @@ -1,102 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * hex2hex reads stdin in Intel HEX format and produces an - * (unsigned char) array which contains the bytes and writes it - * to stdout using C syntax - */ - -#include <stdio.h> -#include <string.h> -#include <stdlib.h> - -#define ABANDON(why) { fprintf(stderr, "%s\n", why); exit(1); } -#define MAX_SIZE (256*1024) -unsigned char buf[MAX_SIZE]; - -static int loadhex(FILE *inf, unsigned char *buf) -{ - int l=0, c, i; - - while ((c=getc(inf))!=EOF) - { - if (c == ':') /* Sync with beginning of line */ - { - int n, check; - unsigned char sum; - int addr; - int linetype; - - if (fscanf(inf, "%02x", &n) != 1) - ABANDON("File format error"); - sum = n; - - if (fscanf(inf, "%04x", &addr) != 1) - ABANDON("File format error"); - sum += addr/256; - sum += addr%256; - - if (fscanf(inf, "%02x", &linetype) != 1) - ABANDON("File format error"); - sum += linetype; - - if (linetype != 0) - continue; - - for (i=0;i<n;i++) - { - if (fscanf(inf, "%02x", &c) != 1) - ABANDON("File format error"); - if (addr >= MAX_SIZE) - ABANDON("File too large"); - buf[addr++] = c; - if (addr > l) - l = addr; - sum += c; - } - - if (fscanf(inf, "%02x", &check) != 1) - ABANDON("File format error"); - - sum = ~sum + 1; - if (check != sum) - ABANDON("Line checksum error"); - } - } - - return l; -} - -int main( int argc, const char * argv [] ) -{ - const char * varline; - int i,l; - int id=0; - - if(argv[1] && strcmp(argv[1], "-i")==0) - { - argv++; - argc--; - id=1; - } - if(argv[1]==NULL) - { - fprintf(stderr,"hex2hex: [-i] filename\n"); - exit(1); - } - varline = argv[1]; - l = loadhex(stdin, buf); - - printf("/*\n *\t Computer generated file. Do not edit.\n */\n"); - printf("static int %s_len = %d;\n", varline, l); - printf("static unsigned char %s[] %s = {\n", varline, id?"__initdata":""); - - for (i=0;i<l;i++) - { - if (i) printf(","); - if (i && !(i % 16)) printf("\n"); - printf("0x%02x", buf[i]); - } - - printf("\n};\n\n"); - return 0; -} diff --git a/sound/oss/kahlua.c b/sound/oss/kahlua.c deleted file mode 100644 index c4b0434c7604..000000000000 --- a/sound/oss/kahlua.c +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Initialisation code for Cyrix/NatSemi VSA1 softaudio - * - * (C) Copyright 2003 Red Hat Inc <alan@lxorguk.ukuu.org.uk> - * - * XpressAudio(tm) is used on the Cyrix MediaGX (now NatSemi Geode) systems. - * The older version (VSA1) provides fairly good soundblaster emulation - * although there are a couple of bugs: large DMA buffers break record, - * and the MPU event handling seems suspect. VSA2 allows the native driver - * to control the AC97 audio engine directly and requires a different driver. - * - * Thanks to National Semiconductor for providing the needed information - * on the XpressAudio(tm) internals. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * TO DO: - * Investigate whether we can portably support Cognac (5520) in the - * same manner. - */ - -#include <linux/delay.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/slab.h> - -#include "sound_config.h" - -#include "sb.h" - -/* - * Read a soundblaster compatible mixer register. - * In this case we are actually reading an SMI trap - * not real hardware. - */ - -static u8 mixer_read(unsigned long io, u8 reg) -{ - outb(reg, io + 4); - udelay(20); - reg = inb(io + 5); - udelay(20); - return reg; -} - -static int probe_one(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - struct address_info *hw_config; - unsigned long base; - void __iomem *mem; - unsigned long io; - u16 map; - u8 irq, dma8, dma16; - int oldquiet; - extern int sb_be_quiet; - - base = pci_resource_start(pdev, 0); - if(base == 0UL) - return 1; - - mem = ioremap(base, 128); - if (!mem) - return 1; - map = readw(mem + 0x18); /* Read the SMI enables */ - iounmap(mem); - - /* Map bits - 0:1 * 0x20 + 0x200 = sb base - 2 sb enable - 3 adlib enable - 5 MPU enable 0x330 - 6 MPU enable 0x300 - - The other bits may be used internally so must be masked */ - - io = 0x220 + 0x20 * (map & 3); - - if(map & (1<<2)) - printk(KERN_INFO "kahlua: XpressAudio at 0x%lx\n", io); - else - return 1; - - if(map & (1<<5)) - printk(KERN_INFO "kahlua: MPU at 0x300\n"); - else if(map & (1<<6)) - printk(KERN_INFO "kahlua: MPU at 0x330\n"); - - irq = mixer_read(io, 0x80) & 0x0F; - dma8 = mixer_read(io, 0x81); - - // printk("IRQ=%x MAP=%x DMA=%x\n", irq, map, dma8); - - if(dma8 & 0x20) - dma16 = 5; - else if(dma8 & 0x40) - dma16 = 6; - else if(dma8 & 0x80) - dma16 = 7; - else - { - printk(KERN_ERR "kahlua: No 16bit DMA enabled.\n"); - return 1; - } - - if(dma8 & 0x01) - dma8 = 0; - else if(dma8 & 0x02) - dma8 = 1; - else if(dma8 & 0x08) - dma8 = 3; - else - { - printk(KERN_ERR "kahlua: No 8bit DMA enabled.\n"); - return 1; - } - - if(irq & 1) - irq = 9; - else if(irq & 2) - irq = 5; - else if(irq & 4) - irq = 7; - else if(irq & 8) - irq = 10; - else - { - printk(KERN_ERR "kahlua: SB IRQ not set.\n"); - return 1; - } - - printk(KERN_INFO "kahlua: XpressAudio on IRQ %d, DMA %d, %d\n", - irq, dma8, dma16); - - hw_config = kzalloc(sizeof(struct address_info), GFP_KERNEL); - if(hw_config == NULL) - { - printk(KERN_ERR "kahlua: out of memory.\n"); - return 1; - } - - pci_set_drvdata(pdev, hw_config); - - hw_config->io_base = io; - hw_config->irq = irq; - hw_config->dma = dma8; - hw_config->dma2 = dma16; - hw_config->name = "Cyrix XpressAudio"; - hw_config->driver_use_1 = SB_NO_MIDI | SB_PCI_IRQ; - - if (!request_region(io, 16, "soundblaster")) - goto err_out_free; - - if(sb_dsp_detect(hw_config, 0, 0, NULL)==0) - { - printk(KERN_ERR "kahlua: audio not responding.\n"); - release_region(io, 16); - goto err_out_free; - } - - oldquiet = sb_be_quiet; - sb_be_quiet = 1; - if(sb_dsp_init(hw_config, THIS_MODULE)) - { - sb_be_quiet = oldquiet; - goto err_out_free; - } - sb_be_quiet = oldquiet; - - return 0; - -err_out_free: - kfree(hw_config); - return 1; -} - -static void remove_one(struct pci_dev *pdev) -{ - struct address_info *hw_config = pci_get_drvdata(pdev); - sb_dsp_unload(hw_config, 0); - kfree(hw_config); -} - -MODULE_AUTHOR("Alan Cox"); -MODULE_DESCRIPTION("Kahlua VSA1 PCI Audio"); -MODULE_LICENSE("GPL"); - -/* - * 5530 only. The 5510/5520 decode is different. - */ - -static const struct pci_device_id id_tbl[] = { - { PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5530_AUDIO), 0 }, - { } -}; - -MODULE_DEVICE_TABLE(pci, id_tbl); - -static struct pci_driver kahlua_driver = { - .name = "kahlua", - .id_table = id_tbl, - .probe = probe_one, - .remove = remove_one, -}; - - -static int __init kahlua_init_module(void) -{ - printk(KERN_INFO "Cyrix Kahlua VSA1 XpressAudio support (c) Copyright 2003 Red Hat Inc\n"); - return pci_register_driver(&kahlua_driver); -} - -static void kahlua_cleanup_module(void) -{ - pci_unregister_driver(&kahlua_driver); -} - - -module_init(kahlua_init_module); -module_exit(kahlua_cleanup_module); - diff --git a/sound/oss/midi_ctrl.h b/sound/oss/midi_ctrl.h deleted file mode 100644 index 240d0c719f1e..000000000000 --- a/sound/oss/midi_ctrl.h +++ /dev/null @@ -1,23 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -static unsigned char ctrl_def_values[128] = -{ - 0x40,0x00,0x40,0x40, 0x40,0x40,0x40,0x7f, /* 0 to 7 */ - 0x40,0x40,0x40,0x7f, 0x40,0x40,0x40,0x40, /* 8 to 15 */ - 0x40,0x40,0x40,0x40, 0x40,0x40,0x40,0x40, /* 16 to 23 */ - 0x40,0x40,0x40,0x40, 0x40,0x40,0x40,0x40, /* 24 to 31 */ - - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 32 to 39 */ - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 40 to 47 */ - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 48 to 55 */ - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 56 to 63 */ - - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 64 to 71 */ - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 72 to 79 */ - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 80 to 87 */ - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 88 to 95 */ - - 0x00,0x00,0x7f,0x7f, 0x7f,0x7f,0x00,0x00, /* 96 to 103 */ - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 104 to 111 */ - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 112 to 119 */ - 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 120 to 127 */ -}; diff --git a/sound/oss/midi_synth.c b/sound/oss/midi_synth.c deleted file mode 100644 index 2292c230d7e6..000000000000 --- a/sound/oss/midi_synth.c +++ /dev/null @@ -1,712 +0,0 @@ -/* - * sound/oss/midi_synth.c - * - * High level midi sequencer manager for dumb MIDI interfaces. - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ -/* - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - * Andrew Veliath : fixed running status in MIDI input state machine - */ -#define USE_SEQ_MACROS -#define USE_SIMPLE_MACROS - -#include "sound_config.h" - -#define _MIDI_SYNTH_C_ - -#include "midi_synth.h" - -static int midi2synth[MAX_MIDI_DEV]; -static int sysex_state[MAX_MIDI_DEV] = -{0}; -static unsigned char prev_out_status[MAX_MIDI_DEV]; - -#define STORE(cmd) \ -{ \ - int len; \ - unsigned char obuf[8]; \ - cmd; \ - seq_input_event(obuf, len); \ -} - -#define _seqbuf obuf -#define _seqbufptr 0 -#define _SEQ_ADVBUF(x) len=x - -void -do_midi_msg(int synthno, unsigned char *msg, int mlen) -{ - switch (msg[0] & 0xf0) - { - case 0x90: - if (msg[2] != 0) - { - STORE(SEQ_START_NOTE(synthno, msg[0] & 0x0f, msg[1], msg[2])); - break; - } - msg[2] = 64; - - case 0x80: - STORE(SEQ_STOP_NOTE(synthno, msg[0] & 0x0f, msg[1], msg[2])); - break; - - case 0xA0: - STORE(SEQ_KEY_PRESSURE(synthno, msg[0] & 0x0f, msg[1], msg[2])); - break; - - case 0xB0: - STORE(SEQ_CONTROL(synthno, msg[0] & 0x0f, - msg[1], msg[2])); - break; - - case 0xC0: - STORE(SEQ_SET_PATCH(synthno, msg[0] & 0x0f, msg[1])); - break; - - case 0xD0: - STORE(SEQ_CHN_PRESSURE(synthno, msg[0] & 0x0f, msg[1])); - break; - - case 0xE0: - STORE(SEQ_BENDER(synthno, msg[0] & 0x0f, - (msg[1] & 0x7f) | ((msg[2] & 0x7f) << 7))); - break; - - default: - /* printk( "MPU: Unknown midi channel message %02x\n", msg[0]); */ - ; - } -} -EXPORT_SYMBOL(do_midi_msg); - -static void -midi_outc(int midi_dev, int data) -{ - int timeout; - - for (timeout = 0; timeout < 3200; timeout++) - if (midi_devs[midi_dev]->outputc(midi_dev, (unsigned char) (data & 0xff))) - { - if (data & 0x80) /* - * Status byte - */ - prev_out_status[midi_dev] = - (unsigned char) (data & 0xff); /* - * Store for running status - */ - return; /* - * Mission complete - */ - } - /* - * Sorry! No space on buffers. - */ - printk("Midi send timed out\n"); -} - -static int -prefix_cmd(int midi_dev, unsigned char status) -{ - if ((char *) midi_devs[midi_dev]->prefix_cmd == NULL) - return 1; - - return midi_devs[midi_dev]->prefix_cmd(midi_dev, status); -} - -static void -midi_synth_input(int orig_dev, unsigned char data) -{ - int dev; - struct midi_input_info *inc; - - static unsigned char len_tab[] = /* # of data bytes following a status - */ - { - 2, /* 8x */ - 2, /* 9x */ - 2, /* Ax */ - 2, /* Bx */ - 1, /* Cx */ - 1, /* Dx */ - 2, /* Ex */ - 0 /* Fx */ - }; - - if (orig_dev < 0 || orig_dev > num_midis || midi_devs[orig_dev] == NULL) - return; - - if (data == 0xfe) /* Ignore active sensing */ - return; - - dev = midi2synth[orig_dev]; - inc = &midi_devs[orig_dev]->in_info; - - switch (inc->m_state) - { - case MST_INIT: - if (data & 0x80) /* MIDI status byte */ - { - if ((data & 0xf0) == 0xf0) /* Common message */ - { - switch (data) - { - case 0xf0: /* Sysex */ - inc->m_state = MST_SYSEX; - break; /* Sysex */ - - case 0xf1: /* MTC quarter frame */ - case 0xf3: /* Song select */ - inc->m_state = MST_DATA; - inc->m_ptr = 1; - inc->m_left = 1; - inc->m_buf[0] = data; - break; - - case 0xf2: /* Song position pointer */ - inc->m_state = MST_DATA; - inc->m_ptr = 1; - inc->m_left = 2; - inc->m_buf[0] = data; - break; - - default: - inc->m_buf[0] = data; - inc->m_ptr = 1; - do_midi_msg(dev, inc->m_buf, inc->m_ptr); - inc->m_ptr = 0; - inc->m_left = 0; - } - } else - { - inc->m_state = MST_DATA; - inc->m_ptr = 1; - inc->m_left = len_tab[(data >> 4) - 8]; - inc->m_buf[0] = inc->m_prev_status = data; - } - } else if (inc->m_prev_status & 0x80) { - /* Data byte (use running status) */ - inc->m_ptr = 2; - inc->m_buf[1] = data; - inc->m_buf[0] = inc->m_prev_status; - inc->m_left = len_tab[(inc->m_buf[0] >> 4) - 8] - 1; - if (inc->m_left > 0) - inc->m_state = MST_DATA; /* Not done yet */ - else { - inc->m_state = MST_INIT; - do_midi_msg(dev, inc->m_buf, inc->m_ptr); - inc->m_ptr = 0; - } - } - break; /* MST_INIT */ - - case MST_DATA: - inc->m_buf[inc->m_ptr++] = data; - if (--inc->m_left <= 0) - { - inc->m_state = MST_INIT; - do_midi_msg(dev, inc->m_buf, inc->m_ptr); - inc->m_ptr = 0; - } - break; /* MST_DATA */ - - case MST_SYSEX: - if (data == 0xf7) /* Sysex end */ - { - inc->m_state = MST_INIT; - inc->m_left = 0; - inc->m_ptr = 0; - } - break; /* MST_SYSEX */ - - default: - printk("MIDI%d: Unexpected state %d (%02x)\n", orig_dev, inc->m_state, (int) data); - inc->m_state = MST_INIT; - } -} - -static void -leave_sysex(int dev) -{ - int orig_dev = synth_devs[dev]->midi_dev; - int timeout = 0; - - if (!sysex_state[dev]) - return; - - sysex_state[dev] = 0; - - while (!midi_devs[orig_dev]->outputc(orig_dev, 0xf7) && - timeout < 1000) - timeout++; - - sysex_state[dev] = 0; -} - -static void -midi_synth_output(int dev) -{ - /* - * Currently NOP - */ -} - -int midi_synth_ioctl(int dev, unsigned int cmd, void __user *arg) -{ - /* - * int orig_dev = synth_devs[dev]->midi_dev; - */ - - switch (cmd) { - - case SNDCTL_SYNTH_INFO: - if (__copy_to_user(arg, synth_devs[dev]->info, sizeof(struct synth_info))) - return -EFAULT; - return 0; - - case SNDCTL_SYNTH_MEMAVL: - return 0x7fffffff; - - default: - return -EINVAL; - } -} -EXPORT_SYMBOL(midi_synth_ioctl); - -int -midi_synth_kill_note(int dev, int channel, int note, int velocity) -{ - int orig_dev = synth_devs[dev]->midi_dev; - int msg, chn; - - if (note < 0 || note > 127) - return 0; - if (channel < 0 || channel > 15) - return 0; - if (velocity < 0) - velocity = 0; - if (velocity > 127) - velocity = 127; - - leave_sysex(dev); - - msg = prev_out_status[orig_dev] & 0xf0; - chn = prev_out_status[orig_dev] & 0x0f; - - if (chn == channel && ((msg == 0x90 && velocity == 64) || msg == 0x80)) - { /* - * Use running status - */ - if (!prefix_cmd(orig_dev, note)) - return 0; - - midi_outc(orig_dev, note); - - if (msg == 0x90) /* - * Running status = Note on - */ - midi_outc(orig_dev, 0); /* - * Note on with velocity 0 == note - * off - */ - else - midi_outc(orig_dev, velocity); - } else - { - if (velocity == 64) - { - if (!prefix_cmd(orig_dev, 0x90 | (channel & 0x0f))) - return 0; - midi_outc(orig_dev, 0x90 | (channel & 0x0f)); /* - * Note on - */ - midi_outc(orig_dev, note); - midi_outc(orig_dev, 0); /* - * Zero G - */ - } else - { - if (!prefix_cmd(orig_dev, 0x80 | (channel & 0x0f))) - return 0; - midi_outc(orig_dev, 0x80 | (channel & 0x0f)); /* - * Note off - */ - midi_outc(orig_dev, note); - midi_outc(orig_dev, velocity); - } - } - - return 0; -} -EXPORT_SYMBOL(midi_synth_kill_note); - -int -midi_synth_set_instr(int dev, int channel, int instr_no) -{ - int orig_dev = synth_devs[dev]->midi_dev; - - if (instr_no < 0 || instr_no > 127) - instr_no = 0; - if (channel < 0 || channel > 15) - return 0; - - leave_sysex(dev); - - if (!prefix_cmd(orig_dev, 0xc0 | (channel & 0x0f))) - return 0; - midi_outc(orig_dev, 0xc0 | (channel & 0x0f)); /* - * Program change - */ - midi_outc(orig_dev, instr_no); - - return 0; -} -EXPORT_SYMBOL(midi_synth_set_instr); - -int -midi_synth_start_note(int dev, int channel, int note, int velocity) -{ - int orig_dev = synth_devs[dev]->midi_dev; - int msg, chn; - - if (note < 0 || note > 127) - return 0; - if (channel < 0 || channel > 15) - return 0; - if (velocity < 0) - velocity = 0; - if (velocity > 127) - velocity = 127; - - leave_sysex(dev); - - msg = prev_out_status[orig_dev] & 0xf0; - chn = prev_out_status[orig_dev] & 0x0f; - - if (chn == channel && msg == 0x90) - { /* - * Use running status - */ - if (!prefix_cmd(orig_dev, note)) - return 0; - midi_outc(orig_dev, note); - midi_outc(orig_dev, velocity); - } else - { - if (!prefix_cmd(orig_dev, 0x90 | (channel & 0x0f))) - return 0; - midi_outc(orig_dev, 0x90 | (channel & 0x0f)); /* - * Note on - */ - midi_outc(orig_dev, note); - midi_outc(orig_dev, velocity); - } - return 0; -} -EXPORT_SYMBOL(midi_synth_start_note); - -void -midi_synth_reset(int dev) -{ - - leave_sysex(dev); -} -EXPORT_SYMBOL(midi_synth_reset); - -int -midi_synth_open(int dev, int mode) -{ - int orig_dev = synth_devs[dev]->midi_dev; - int err; - struct midi_input_info *inc; - - if (orig_dev < 0 || orig_dev >= num_midis || midi_devs[orig_dev] == NULL) - return -ENXIO; - - midi2synth[orig_dev] = dev; - sysex_state[dev] = 0; - prev_out_status[orig_dev] = 0; - - if ((err = midi_devs[orig_dev]->open(orig_dev, mode, - midi_synth_input, midi_synth_output)) < 0) - return err; - inc = &midi_devs[orig_dev]->in_info; - - /* save_flags(flags); - cli(); - don't know against what irqhandler to protect*/ - inc->m_busy = 0; - inc->m_state = MST_INIT; - inc->m_ptr = 0; - inc->m_left = 0; - inc->m_prev_status = 0x00; - /* restore_flags(flags); */ - - return 1; -} -EXPORT_SYMBOL(midi_synth_open); - -void -midi_synth_close(int dev) -{ - int orig_dev = synth_devs[dev]->midi_dev; - - leave_sysex(dev); - - /* - * Shut up the synths by sending just single active sensing message. - */ - midi_devs[orig_dev]->outputc(orig_dev, 0xfe); - - midi_devs[orig_dev]->close(orig_dev); -} -EXPORT_SYMBOL(midi_synth_close); - -void -midi_synth_hw_control(int dev, unsigned char *event) -{ -} -EXPORT_SYMBOL(midi_synth_hw_control); - -int -midi_synth_load_patch(int dev, int format, const char __user *addr, - int count, int pmgr_flag) -{ - int orig_dev = synth_devs[dev]->midi_dev; - - struct sysex_info sysex; - int i; - unsigned long left, src_offs, eox_seen = 0; - int first_byte = 1; - int hdr_size = (unsigned long) &sysex.data[0] - (unsigned long) &sysex; - - leave_sysex(dev); - - if (!prefix_cmd(orig_dev, 0xf0)) - return 0; - - /* Invalid patch format */ - if (format != SYSEX_PATCH) - return -EINVAL; - - /* Patch header too short */ - if (count < hdr_size) - return -EINVAL; - - count -= hdr_size; - - /* - * Copy the header from user space - */ - - if (copy_from_user(&sysex, addr, hdr_size)) - return -EFAULT; - - /* Sysex record too short */ - if ((unsigned)count < (unsigned)sysex.len) - sysex.len = count; - - left = sysex.len; - src_offs = 0; - - for (i = 0; i < left && !signal_pending(current); i++) - { - unsigned char data; - - if (get_user(data, - (unsigned char __user *)(addr + hdr_size + i))) - return -EFAULT; - - eox_seen = (i > 0 && data & 0x80); /* End of sysex */ - - if (eox_seen && data != 0xf7) - data = 0xf7; - - if (i == 0) - { - if (data != 0xf0) - { - printk(KERN_WARNING "midi_synth: Sysex start missing\n"); - return -EINVAL; - } - } - while (!midi_devs[orig_dev]->outputc(orig_dev, (unsigned char) (data & 0xff)) && - !signal_pending(current)) - schedule(); - - if (!first_byte && data & 0x80) - return 0; - first_byte = 0; - } - - if (!eox_seen) - midi_outc(orig_dev, 0xf7); - return 0; -} -EXPORT_SYMBOL(midi_synth_load_patch); - -void midi_synth_panning(int dev, int channel, int pressure) -{ -} -EXPORT_SYMBOL(midi_synth_panning); - -void midi_synth_aftertouch(int dev, int channel, int pressure) -{ - int orig_dev = synth_devs[dev]->midi_dev; - int msg, chn; - - if (pressure < 0 || pressure > 127) - return; - if (channel < 0 || channel > 15) - return; - - leave_sysex(dev); - - msg = prev_out_status[orig_dev] & 0xf0; - chn = prev_out_status[orig_dev] & 0x0f; - - if (msg != 0xd0 || chn != channel) /* - * Test for running status - */ - { - if (!prefix_cmd(orig_dev, 0xd0 | (channel & 0x0f))) - return; - midi_outc(orig_dev, 0xd0 | (channel & 0x0f)); /* - * Channel pressure - */ - } else if (!prefix_cmd(orig_dev, pressure)) - return; - - midi_outc(orig_dev, pressure); -} -EXPORT_SYMBOL(midi_synth_aftertouch); - -void -midi_synth_controller(int dev, int channel, int ctrl_num, int value) -{ - int orig_dev = synth_devs[dev]->midi_dev; - int chn, msg; - - if (ctrl_num < 0 || ctrl_num > 127) - return; - if (channel < 0 || channel > 15) - return; - - leave_sysex(dev); - - msg = prev_out_status[orig_dev] & 0xf0; - chn = prev_out_status[orig_dev] & 0x0f; - - if (msg != 0xb0 || chn != channel) - { - if (!prefix_cmd(orig_dev, 0xb0 | (channel & 0x0f))) - return; - midi_outc(orig_dev, 0xb0 | (channel & 0x0f)); - } else if (!prefix_cmd(orig_dev, ctrl_num)) - return; - - midi_outc(orig_dev, ctrl_num); - midi_outc(orig_dev, value & 0x7f); -} -EXPORT_SYMBOL(midi_synth_controller); - -void -midi_synth_bender(int dev, int channel, int value) -{ - int orig_dev = synth_devs[dev]->midi_dev; - int msg, prev_chn; - - if (channel < 0 || channel > 15) - return; - - if (value < 0 || value > 16383) - return; - - leave_sysex(dev); - - msg = prev_out_status[orig_dev] & 0xf0; - prev_chn = prev_out_status[orig_dev] & 0x0f; - - if (msg != 0xd0 || prev_chn != channel) /* - * Test for running status - */ - { - if (!prefix_cmd(orig_dev, 0xe0 | (channel & 0x0f))) - return; - midi_outc(orig_dev, 0xe0 | (channel & 0x0f)); - } else if (!prefix_cmd(orig_dev, value & 0x7f)) - return; - - midi_outc(orig_dev, value & 0x7f); - midi_outc(orig_dev, (value >> 7) & 0x7f); -} -EXPORT_SYMBOL(midi_synth_bender); - -void -midi_synth_setup_voice(int dev, int voice, int channel) -{ -} -EXPORT_SYMBOL(midi_synth_setup_voice); - -int -midi_synth_send_sysex(int dev, unsigned char *bytes, int len) -{ - int orig_dev = synth_devs[dev]->midi_dev; - int i; - - for (i = 0; i < len; i++) - { - switch (bytes[i]) - { - case 0xf0: /* Start sysex */ - if (!prefix_cmd(orig_dev, 0xf0)) - return 0; - sysex_state[dev] = 1; - break; - - case 0xf7: /* End sysex */ - if (!sysex_state[dev]) /* Orphan sysex end */ - return 0; - sysex_state[dev] = 0; - break; - - default: - if (!sysex_state[dev]) - return 0; - - if (bytes[i] & 0x80) /* Error. Another message before sysex end */ - { - bytes[i] = 0xf7; /* Sysex end */ - sysex_state[dev] = 0; - } - } - - if (!midi_devs[orig_dev]->outputc(orig_dev, bytes[i])) - { -/* - * Hardware level buffer is full. Abort the sysex message. - */ - - int timeout = 0; - - bytes[i] = 0xf7; - sysex_state[dev] = 0; - - while (!midi_devs[orig_dev]->outputc(orig_dev, bytes[i]) && - timeout < 1000) - timeout++; - } - if (!sysex_state[dev]) - return 0; - } - - return 0; -} -EXPORT_SYMBOL(midi_synth_send_sysex); - diff --git a/sound/oss/midi_synth.h b/sound/oss/midi_synth.h deleted file mode 100644 index 1cf676c7510e..000000000000 --- a/sound/oss/midi_synth.h +++ /dev/null @@ -1,48 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -int midi_synth_ioctl (int dev, - unsigned int cmd, void __user * arg); -int midi_synth_kill_note (int dev, int channel, int note, int velocity); -int midi_synth_set_instr (int dev, int channel, int instr_no); -int midi_synth_start_note (int dev, int channel, int note, int volume); -void midi_synth_reset (int dev); -int midi_synth_open (int dev, int mode); -void midi_synth_close (int dev); -void midi_synth_hw_control (int dev, unsigned char *event); -int midi_synth_load_patch (int dev, int format, const char __user * addr, - int count, int pmgr_flag); -void midi_synth_panning (int dev, int channel, int pressure); -void midi_synth_aftertouch (int dev, int channel, int pressure); -void midi_synth_controller (int dev, int channel, int ctrl_num, int value); -void midi_synth_bender (int dev, int chn, int value); -void midi_synth_setup_voice (int dev, int voice, int chn); -int midi_synth_send_sysex(int dev, unsigned char *bytes,int len); - -#ifndef _MIDI_SYNTH_C_ -static struct synth_info std_synth_info = -{MIDI_SYNTH_NAME, 0, SYNTH_TYPE_MIDI, 0, 0, 128, 0, 128, MIDI_SYNTH_CAPS}; - -static struct synth_operations std_midi_synth = -{ - .owner = THIS_MODULE, - .id = "MIDI", - .info = &std_synth_info, - .midi_dev = 0, - .synth_type = SYNTH_TYPE_MIDI, - .synth_subtype = 0, - .open = midi_synth_open, - .close = midi_synth_close, - .ioctl = midi_synth_ioctl, - .kill_note = midi_synth_kill_note, - .start_note = midi_synth_start_note, - .set_instr = midi_synth_set_instr, - .reset = midi_synth_reset, - .hw_control = midi_synth_hw_control, - .load_patch = midi_synth_load_patch, - .aftertouch = midi_synth_aftertouch, - .controller = midi_synth_controller, - .panning = midi_synth_panning, - .bender = midi_synth_bender, - .setup_voice = midi_synth_setup_voice, - .send_sysex = midi_synth_send_sysex -}; -#endif diff --git a/sound/oss/midibuf.c b/sound/oss/midibuf.c deleted file mode 100644 index 1277df815d5b..000000000000 --- a/sound/oss/midibuf.c +++ /dev/null @@ -1,427 +0,0 @@ -/* - * sound/oss/midibuf.c - * - * Device file manager for /dev/midi# - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ -/* - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - */ -#include <linux/stddef.h> -#include <linux/kmod.h> -#include <linux/spinlock.h> -#include <linux/sched/signal.h> - -#define MIDIBUF_C - -#include "sound_config.h" - - -/* - * Don't make MAX_QUEUE_SIZE larger than 4000 - */ - -#define MAX_QUEUE_SIZE 4000 - -static wait_queue_head_t midi_sleeper[MAX_MIDI_DEV]; -static wait_queue_head_t input_sleeper[MAX_MIDI_DEV]; - -struct midi_buf -{ - int len, head, tail; - unsigned char queue[MAX_QUEUE_SIZE]; -}; - -struct midi_parms -{ - long prech_timeout; /* - * Timeout before the first ch - */ -}; - -static struct midi_buf *midi_out_buf[MAX_MIDI_DEV] = {NULL}; -static struct midi_buf *midi_in_buf[MAX_MIDI_DEV] = {NULL}; -static struct midi_parms parms[MAX_MIDI_DEV]; - -static void midi_poll(unsigned long dummy); - - -static DEFINE_TIMER(poll_timer, midi_poll); - -static volatile int open_devs; -static DEFINE_SPINLOCK(lock); - -#define DATA_AVAIL(q) (q->len) -#define SPACE_AVAIL(q) (MAX_QUEUE_SIZE - q->len) - -#define QUEUE_BYTE(q, data) \ - if (SPACE_AVAIL(q)) \ - { \ - unsigned long flags; \ - spin_lock_irqsave(&lock, flags); \ - q->queue[q->tail] = (data); \ - q->len++; q->tail = (q->tail+1) % MAX_QUEUE_SIZE; \ - spin_unlock_irqrestore(&lock, flags); \ - } - -#define REMOVE_BYTE(q, data) \ - if (DATA_AVAIL(q)) \ - { \ - unsigned long flags; \ - spin_lock_irqsave(&lock, flags); \ - data = q->queue[q->head]; \ - q->len--; q->head = (q->head+1) % MAX_QUEUE_SIZE; \ - spin_unlock_irqrestore(&lock, flags); \ - } - -static void drain_midi_queue(int dev) -{ - - /* - * Give the Midi driver time to drain its output queues - */ - - if (midi_devs[dev]->buffer_status != NULL) - wait_event_interruptible_timeout(midi_sleeper[dev], - !midi_devs[dev]->buffer_status(dev), HZ/10); -} - -static void midi_input_intr(int dev, unsigned char data) -{ - if (midi_in_buf[dev] == NULL) - return; - - if (data == 0xfe) /* - * Active sensing - */ - return; /* - * Ignore - */ - - if (SPACE_AVAIL(midi_in_buf[dev])) { - QUEUE_BYTE(midi_in_buf[dev], data); - wake_up(&input_sleeper[dev]); - } -} - -static void midi_output_intr(int dev) -{ - /* - * Currently NOP - */ -} - -static void midi_poll(unsigned long dummy) -{ - unsigned long flags; - int dev; - - spin_lock_irqsave(&lock, flags); - if (open_devs) - { - for (dev = 0; dev < num_midis; dev++) - if (midi_devs[dev] != NULL && midi_out_buf[dev] != NULL) - { - while (DATA_AVAIL(midi_out_buf[dev])) - { - int ok; - int c = midi_out_buf[dev]->queue[midi_out_buf[dev]->head]; - - spin_unlock_irqrestore(&lock,flags);/* Give some time to others */ - ok = midi_devs[dev]->outputc(dev, c); - spin_lock_irqsave(&lock, flags); - if (!ok) - break; - midi_out_buf[dev]->head = (midi_out_buf[dev]->head + 1) % MAX_QUEUE_SIZE; - midi_out_buf[dev]->len--; - } - - if (DATA_AVAIL(midi_out_buf[dev]) < 100) - wake_up(&midi_sleeper[dev]); - } - poll_timer.expires = (1) + jiffies; - add_timer(&poll_timer); - /* - * Come back later - */ - } - spin_unlock_irqrestore(&lock, flags); -} - -int MIDIbuf_open(int dev, struct file *file) -{ - int mode, err; - - dev = dev >> 4; - mode = translate_mode(file); - - if (num_midis > MAX_MIDI_DEV) - { - printk(KERN_ERR "midi: Too many midi interfaces\n"); - num_midis = MAX_MIDI_DEV; - } - if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL) - return -ENXIO; - /* - * Interrupts disabled. Be careful - */ - - module_put(midi_devs[dev]->owner); - - if ((err = midi_devs[dev]->open(dev, mode, - midi_input_intr, midi_output_intr)) < 0) - return err; - - parms[dev].prech_timeout = MAX_SCHEDULE_TIMEOUT; - midi_in_buf[dev] = vmalloc(sizeof(struct midi_buf)); - - if (midi_in_buf[dev] == NULL) - { - printk(KERN_WARNING "midi: Can't allocate buffer\n"); - midi_devs[dev]->close(dev); - return -EIO; - } - midi_in_buf[dev]->len = midi_in_buf[dev]->head = midi_in_buf[dev]->tail = 0; - - midi_out_buf[dev] = vmalloc(sizeof(struct midi_buf)); - - if (midi_out_buf[dev] == NULL) - { - printk(KERN_WARNING "midi: Can't allocate buffer\n"); - midi_devs[dev]->close(dev); - vfree(midi_in_buf[dev]); - midi_in_buf[dev] = NULL; - return -EIO; - } - midi_out_buf[dev]->len = midi_out_buf[dev]->head = midi_out_buf[dev]->tail = 0; - open_devs++; - - init_waitqueue_head(&midi_sleeper[dev]); - init_waitqueue_head(&input_sleeper[dev]); - - if (open_devs < 2) /* This was first open */ - { - poll_timer.expires = 1 + jiffies; - add_timer(&poll_timer); /* Start polling */ - } - return err; -} - -void MIDIbuf_release(int dev, struct file *file) -{ - int mode; - - dev = dev >> 4; - mode = translate_mode(file); - - if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL) - return; - - /* - * Wait until the queue is empty - */ - - if (mode != OPEN_READ) - { - midi_devs[dev]->outputc(dev, 0xfe); /* - * Active sensing to shut the - * devices - */ - - wait_event_interruptible(midi_sleeper[dev], - !DATA_AVAIL(midi_out_buf[dev])); - /* - * Sync - */ - - drain_midi_queue(dev); /* - * Ensure the output queues are empty - */ - } - - midi_devs[dev]->close(dev); - - open_devs--; - if (open_devs == 0) - del_timer_sync(&poll_timer); - vfree(midi_in_buf[dev]); - vfree(midi_out_buf[dev]); - midi_in_buf[dev] = NULL; - midi_out_buf[dev] = NULL; - - module_put(midi_devs[dev]->owner); -} - -int MIDIbuf_write(int dev, struct file *file, const char __user *buf, int count) -{ - int c, n, i; - unsigned char tmp_data; - - dev = dev >> 4; - - if (!count) - return 0; - - c = 0; - - while (c < count) - { - n = SPACE_AVAIL(midi_out_buf[dev]); - - if (n == 0) { /* - * No space just now. - */ - - if (file->f_flags & O_NONBLOCK) { - c = -EAGAIN; - goto out; - } - - if (wait_event_interruptible(midi_sleeper[dev], - SPACE_AVAIL(midi_out_buf[dev]))) - { - c = -EINTR; - goto out; - } - n = SPACE_AVAIL(midi_out_buf[dev]); - } - if (n > (count - c)) - n = count - c; - - for (i = 0; i < n; i++) - { - /* BROKE BROKE BROKE - CAN'T DO THIS WITH CLI !! */ - /* yes, think the same, so I removed the cli() brackets - QUEUE_BYTE is protected against interrupts */ - if (copy_from_user((char *) &tmp_data, &(buf)[c], 1)) { - c = -EFAULT; - goto out; - } - QUEUE_BYTE(midi_out_buf[dev], tmp_data); - c++; - } - } -out: - return c; -} - - -int MIDIbuf_read(int dev, struct file *file, char __user *buf, int count) -{ - int n, c = 0; - unsigned char tmp_data; - - dev = dev >> 4; - - if (!DATA_AVAIL(midi_in_buf[dev])) { /* - * No data yet, wait - */ - if (file->f_flags & O_NONBLOCK) { - c = -EAGAIN; - goto out; - } - wait_event_interruptible_timeout(input_sleeper[dev], - DATA_AVAIL(midi_in_buf[dev]), - parms[dev].prech_timeout); - - if (signal_pending(current)) - c = -EINTR; /* The user is getting restless */ - } - if (c == 0 && DATA_AVAIL(midi_in_buf[dev])) /* - * Got some bytes - */ - { - n = DATA_AVAIL(midi_in_buf[dev]); - if (n > count) - n = count; - c = 0; - - while (c < n) - { - char *fixit; - REMOVE_BYTE(midi_in_buf[dev], tmp_data); - fixit = (char *) &tmp_data; - /* BROKE BROKE BROKE */ - /* yes removed the cli() brackets again - should q->len,tail&head be atomic_t? */ - if (copy_to_user(&(buf)[c], fixit, 1)) { - c = -EFAULT; - goto out; - } - c++; - } - } -out: - return c; -} - -int MIDIbuf_ioctl(int dev, struct file *file, - unsigned int cmd, void __user *arg) -{ - int val; - - dev = dev >> 4; - - if (((cmd >> 8) & 0xff) == 'C') - { - if (midi_devs[dev]->coproc) /* Coprocessor ioctl */ - return midi_devs[dev]->coproc->ioctl(midi_devs[dev]->coproc->devc, cmd, arg, 0); -/* printk("/dev/midi%d: No coprocessor for this device\n", dev);*/ - return -ENXIO; - } - else - { - switch (cmd) - { - case SNDCTL_MIDI_PRETIME: - if (get_user(val, (int __user *)arg)) - return -EFAULT; - if (val < 0) - val = 0; - val = (HZ * val) / 10; - parms[dev].prech_timeout = val; - return put_user(val, (int __user *)arg); - - default: - if (!midi_devs[dev]->ioctl) - return -EINVAL; - return midi_devs[dev]->ioctl(dev, cmd, arg); - } - } -} - -/* No kernel lock - fine */ -unsigned int MIDIbuf_poll(int dev, struct file *file, poll_table * wait) -{ - unsigned int mask = 0; - - dev = dev >> 4; - - /* input */ - poll_wait(file, &input_sleeper[dev], wait); - if (DATA_AVAIL(midi_in_buf[dev])) - mask |= POLLIN | POLLRDNORM; - - /* output */ - poll_wait(file, &midi_sleeper[dev], wait); - if (!SPACE_AVAIL(midi_out_buf[dev])) - mask |= POLLOUT | POLLWRNORM; - - return mask; -} - - -int MIDIbuf_avail(int dev) -{ - if (midi_in_buf[dev]) - return DATA_AVAIL (midi_in_buf[dev]); - return 0; -} -EXPORT_SYMBOL(MIDIbuf_avail); - diff --git a/sound/oss/mpu401.c b/sound/oss/mpu401.c deleted file mode 100644 index 20e8fa46f647..000000000000 --- a/sound/oss/mpu401.c +++ /dev/null @@ -1,1804 +0,0 @@ -/* - * sound/oss/mpu401.c - * - * The low level driver for Roland MPU-401 compatible Midi cards. - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * - * Thomas Sailer ioctl code reworked (vmalloc/vfree removed) - * Alan Cox modularisation, use normal request_irq, use dev_id - * Bartlomiej Zolnierkiewicz removed some __init to allow using many drivers - * Chris Rankin Update the module-usage counter for the coprocessor - * Zwane Mwaikambo Changed attach/unload resource freeing - */ - -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/spinlock.h> -#define USE_SEQ_MACROS -#define USE_SIMPLE_MACROS - -#include "sound_config.h" - -#include "coproc.h" -#include "mpu401.h" - -static int timer_mode = TMR_INTERNAL, timer_caps = TMR_INTERNAL; - -struct mpu_config -{ - int base; /* - * I/O base - */ - int irq; - int opened; /* - * Open mode - */ - int devno; - int synthno; - int uart_mode; - int initialized; - int mode; -#define MODE_MIDI 1 -#define MODE_SYNTH 2 - unsigned char version, revision; - unsigned int capabilities; -#define MPU_CAP_INTLG 0x10000000 -#define MPU_CAP_SYNC 0x00000010 -#define MPU_CAP_FSK 0x00000020 -#define MPU_CAP_CLS 0x00000040 -#define MPU_CAP_SMPTE 0x00000080 -#define MPU_CAP_2PORT 0x00000001 - int timer_flag; - -#define MBUF_MAX 10 -#define BUFTEST(dc) if (dc->m_ptr >= MBUF_MAX || dc->m_ptr < 0) \ - {printk( "MPU: Invalid buffer pointer %d/%d, s=%d\n", dc->m_ptr, dc->m_left, dc->m_state);dc->m_ptr--;} - int m_busy; - unsigned char m_buf[MBUF_MAX]; - int m_ptr; - int m_state; - int m_left; - unsigned char last_status; - void (*inputintr) (int dev, unsigned char data); - int shared_irq; - int *osp; - spinlock_t lock; - }; - -#define DATAPORT(base) (base) -#define COMDPORT(base) (base+1) -#define STATPORT(base) (base+1) - - -static void mpu401_close(int dev); - -static inline int mpu401_status(struct mpu_config *devc) -{ - return inb(STATPORT(devc->base)); -} - -#define input_avail(devc) (!(mpu401_status(devc)&INPUT_AVAIL)) -#define output_ready(devc) (!(mpu401_status(devc)&OUTPUT_READY)) - -static inline void write_command(struct mpu_config *devc, unsigned char cmd) -{ - outb(cmd, COMDPORT(devc->base)); -} - -static inline int read_data(struct mpu_config *devc) -{ - return inb(DATAPORT(devc->base)); -} - -static inline void write_data(struct mpu_config *devc, unsigned char byte) -{ - outb(byte, DATAPORT(devc->base)); -} - -#define OUTPUT_READY 0x40 -#define INPUT_AVAIL 0x80 -#define MPU_ACK 0xFE -#define MPU_RESET 0xFF -#define UART_MODE_ON 0x3F - -static struct mpu_config dev_conf[MAX_MIDI_DEV]; - -static int n_mpu_devs; - -static int reset_mpu401(struct mpu_config *devc); -static void set_uart_mode(int dev, struct mpu_config *devc, int arg); - -static int mpu_timer_init(int midi_dev); -static void mpu_timer_interrupt(void); -static void timer_ext_event(struct mpu_config *devc, int event, int parm); - -static struct synth_info mpu_synth_info_proto = { - "MPU-401 MIDI interface", - 0, - SYNTH_TYPE_MIDI, - MIDI_TYPE_MPU401, - 0, 128, - 0, 128, - SYNTH_CAP_INPUT -}; - -static struct synth_info mpu_synth_info[MAX_MIDI_DEV]; - -/* - * States for the input scanner - */ - -#define ST_INIT 0 /* Ready for timing byte or msg */ -#define ST_TIMED 1 /* Leading timing byte rcvd */ -#define ST_DATABYTE 2 /* Waiting for (nr_left) data bytes */ - -#define ST_SYSMSG 100 /* System message (sysx etc). */ -#define ST_SYSEX 101 /* System exclusive msg */ -#define ST_MTC 102 /* Midi Time Code (MTC) qframe msg */ -#define ST_SONGSEL 103 /* Song select */ -#define ST_SONGPOS 104 /* Song position pointer */ - -static unsigned char len_tab[] = /* # of data bytes following a status - */ -{ - 2, /* 8x */ - 2, /* 9x */ - 2, /* Ax */ - 2, /* Bx */ - 1, /* Cx */ - 1, /* Dx */ - 2, /* Ex */ - 0 /* Fx */ -}; - -#define STORE(cmd) \ -{ \ - int len; \ - unsigned char obuf[8]; \ - cmd; \ - seq_input_event(obuf, len); \ -} - -#define _seqbuf obuf -#define _seqbufptr 0 -#define _SEQ_ADVBUF(x) len=x - -static int mpu_input_scanner(struct mpu_config *devc, unsigned char midic) -{ - - switch (devc->m_state) - { - case ST_INIT: - switch (midic) - { - case 0xf8: - /* Timer overflow */ - break; - - case 0xfc: - printk("<all end>"); - break; - - case 0xfd: - if (devc->timer_flag) - mpu_timer_interrupt(); - break; - - case 0xfe: - return MPU_ACK; - - case 0xf0: - case 0xf1: - case 0xf2: - case 0xf3: - case 0xf4: - case 0xf5: - case 0xf6: - case 0xf7: - printk("<Trk data rq #%d>", midic & 0x0f); - break; - - case 0xf9: - printk("<conductor rq>"); - break; - - case 0xff: - devc->m_state = ST_SYSMSG; - break; - - default: - if (midic <= 0xef) - { - /* printk( "mpu time: %d ", midic); */ - devc->m_state = ST_TIMED; - } - else - printk("<MPU: Unknown event %02x> ", midic); - } - break; - - case ST_TIMED: - { - int msg = ((int) (midic & 0xf0) >> 4); - - devc->m_state = ST_DATABYTE; - - if (msg < 8) /* Data byte */ - { - /* printk( "midi msg (running status) "); */ - msg = ((int) (devc->last_status & 0xf0) >> 4); - msg -= 8; - devc->m_left = len_tab[msg] - 1; - - devc->m_ptr = 2; - devc->m_buf[0] = devc->last_status; - devc->m_buf[1] = midic; - - if (devc->m_left <= 0) - { - devc->m_state = ST_INIT; - do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr); - devc->m_ptr = 0; - } - } - else if (msg == 0xf) /* MPU MARK */ - { - devc->m_state = ST_INIT; - - switch (midic) - { - case 0xf8: - /* printk( "NOP "); */ - break; - - case 0xf9: - /* printk( "meas end "); */ - break; - - case 0xfc: - /* printk( "data end "); */ - break; - - default: - printk("Unknown MPU mark %02x\n", midic); - } - } - else - { - devc->last_status = midic; - /* printk( "midi msg "); */ - msg -= 8; - devc->m_left = len_tab[msg]; - - devc->m_ptr = 1; - devc->m_buf[0] = midic; - - if (devc->m_left <= 0) - { - devc->m_state = ST_INIT; - do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr); - devc->m_ptr = 0; - } - } - } - break; - - case ST_SYSMSG: - switch (midic) - { - case 0xf0: - printk("<SYX>"); - devc->m_state = ST_SYSEX; - break; - - case 0xf1: - devc->m_state = ST_MTC; - break; - - case 0xf2: - devc->m_state = ST_SONGPOS; - devc->m_ptr = 0; - break; - - case 0xf3: - devc->m_state = ST_SONGSEL; - break; - - case 0xf6: - /* printk( "tune_request\n"); */ - devc->m_state = ST_INIT; - break; - - /* - * Real time messages - */ - case 0xf8: - /* midi clock */ - devc->m_state = ST_INIT; - timer_ext_event(devc, TMR_CLOCK, 0); - break; - - case 0xfA: - devc->m_state = ST_INIT; - timer_ext_event(devc, TMR_START, 0); - break; - - case 0xFB: - devc->m_state = ST_INIT; - timer_ext_event(devc, TMR_CONTINUE, 0); - break; - - case 0xFC: - devc->m_state = ST_INIT; - timer_ext_event(devc, TMR_STOP, 0); - break; - - case 0xFE: - /* active sensing */ - devc->m_state = ST_INIT; - break; - - case 0xff: - /* printk( "midi hard reset"); */ - devc->m_state = ST_INIT; - break; - - default: - printk("unknown MIDI sysmsg %0x\n", midic); - devc->m_state = ST_INIT; - } - break; - - case ST_MTC: - devc->m_state = ST_INIT; - printk("MTC frame %x02\n", midic); - break; - - case ST_SYSEX: - if (midic == 0xf7) - { - printk("<EOX>"); - devc->m_state = ST_INIT; - } - else - printk("%02x ", midic); - break; - - case ST_SONGPOS: - BUFTEST(devc); - devc->m_buf[devc->m_ptr++] = midic; - if (devc->m_ptr == 2) - { - devc->m_state = ST_INIT; - devc->m_ptr = 0; - timer_ext_event(devc, TMR_SPP, - ((devc->m_buf[1] & 0x7f) << 7) | - (devc->m_buf[0] & 0x7f)); - } - break; - - case ST_DATABYTE: - BUFTEST(devc); - devc->m_buf[devc->m_ptr++] = midic; - if ((--devc->m_left) <= 0) - { - devc->m_state = ST_INIT; - do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr); - devc->m_ptr = 0; - } - break; - - default: - printk("Bad state %d ", devc->m_state); - devc->m_state = ST_INIT; - } - return 1; -} - -static void mpu401_input_loop(struct mpu_config *devc) -{ - unsigned long flags; - int busy; - int n; - - spin_lock_irqsave(&devc->lock,flags); - busy = devc->m_busy; - devc->m_busy = 1; - spin_unlock_irqrestore(&devc->lock,flags); - - if (busy) /* Already inside the scanner */ - return; - - n = 50; - - while (input_avail(devc) && n-- > 0) - { - unsigned char c = read_data(devc); - - if (devc->mode == MODE_SYNTH) - { - mpu_input_scanner(devc, c); - } - else if (devc->opened & OPEN_READ && devc->inputintr != NULL) - devc->inputintr(devc->devno, c); - } - devc->m_busy = 0; -} - -static irqreturn_t mpuintr(int irq, void *dev_id) -{ - struct mpu_config *devc; - int dev = (int)(unsigned long) dev_id; - int handled = 0; - - devc = &dev_conf[dev]; - - if (input_avail(devc)) - { - handled = 1; - if (devc->base != 0 && (devc->opened & OPEN_READ || devc->mode == MODE_SYNTH)) - mpu401_input_loop(devc); - else - { - /* Dummy read (just to acknowledge the interrupt) */ - read_data(devc); - } - } - return IRQ_RETVAL(handled); -} - -static int mpu401_open(int dev, int mode, - void (*input) (int dev, unsigned char data), - void (*output) (int dev) -) -{ - int err; - struct mpu_config *devc; - struct coproc_operations *coprocessor; - - if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL) - return -ENXIO; - - devc = &dev_conf[dev]; - - if (devc->opened) - return -EBUSY; - /* - * Verify that the device is really running. - * Some devices (such as Ensoniq SoundScape don't - * work before the on board processor (OBP) is initialized - * by downloading its microcode. - */ - - if (!devc->initialized) - { - if (mpu401_status(devc) == 0xff) /* Bus float */ - { - printk(KERN_ERR "mpu401: Device not initialized properly\n"); - return -EIO; - } - reset_mpu401(devc); - } - - if ( (coprocessor = midi_devs[dev]->coproc) != NULL ) - { - if (!try_module_get(coprocessor->owner)) { - mpu401_close(dev); - return -ENODEV; - } - - if ((err = coprocessor->open(coprocessor->devc, COPR_MIDI)) < 0) - { - printk(KERN_WARNING "MPU-401: Can't access coprocessor device\n"); - mpu401_close(dev); - return err; - } - } - - set_uart_mode(dev, devc, 1); - devc->mode = MODE_MIDI; - devc->synthno = 0; - - mpu401_input_loop(devc); - - devc->inputintr = input; - devc->opened = mode; - - return 0; -} - -static void mpu401_close(int dev) -{ - struct mpu_config *devc; - struct coproc_operations *coprocessor; - - devc = &dev_conf[dev]; - if (devc->uart_mode) - reset_mpu401(devc); /* - * This disables the UART mode - */ - devc->mode = 0; - devc->inputintr = NULL; - - coprocessor = midi_devs[dev]->coproc; - if (coprocessor) { - coprocessor->close(coprocessor->devc, COPR_MIDI); - module_put(coprocessor->owner); - } - devc->opened = 0; -} - -static int mpu401_out(int dev, unsigned char midi_byte) -{ - int timeout; - unsigned long flags; - - struct mpu_config *devc; - - devc = &dev_conf[dev]; - - /* - * Sometimes it takes about 30000 loops before the output becomes ready - * (After reset). Normally it takes just about 10 loops. - */ - - for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--); - - spin_lock_irqsave(&devc->lock,flags); - if (!output_ready(devc)) - { - printk(KERN_WARNING "mpu401: Send data timeout\n"); - spin_unlock_irqrestore(&devc->lock,flags); - return 0; - } - write_data(devc, midi_byte); - spin_unlock_irqrestore(&devc->lock,flags); - return 1; -} - -static int mpu401_command(int dev, mpu_command_rec * cmd) -{ - int i, timeout, ok; - unsigned long flags; - struct mpu_config *devc; - - devc = &dev_conf[dev]; - - if (devc->uart_mode) /* - * Not possible in UART mode - */ - { - printk(KERN_WARNING "mpu401: commands not possible in the UART mode\n"); - return -EINVAL; - } - /* - * Test for input since pending input seems to block the output. - */ - if (input_avail(devc)) - mpu401_input_loop(devc); - - /* - * Sometimes it takes about 50000 loops before the output becomes ready - * (After reset). Normally it takes just about 10 loops. - */ - - timeout = 50000; -retry: - if (timeout-- <= 0) - { - printk(KERN_WARNING "mpu401: Command (0x%x) timeout\n", (int) cmd->cmd); - return -EIO; - } - spin_lock_irqsave(&devc->lock,flags); - - if (!output_ready(devc)) - { - spin_unlock_irqrestore(&devc->lock,flags); - goto retry; - } - write_command(devc, cmd->cmd); - - ok = 0; - for (timeout = 50000; timeout > 0 && !ok; timeout--) - { - if (input_avail(devc)) - { - if (devc->opened && devc->mode == MODE_SYNTH) - { - if (mpu_input_scanner(devc, read_data(devc)) == MPU_ACK) - ok = 1; - } - else - { - /* Device is not currently open. Use simpler method */ - if (read_data(devc) == MPU_ACK) - ok = 1; - } - } - } - if (!ok) - { - spin_unlock_irqrestore(&devc->lock,flags); - return -EIO; - } - if (cmd->nr_args) - { - for (i = 0; i < cmd->nr_args; i++) - { - for (timeout = 3000; timeout > 0 && !output_ready(devc); timeout--); - - if (!mpu401_out(dev, cmd->data[i])) - { - spin_unlock_irqrestore(&devc->lock,flags); - printk(KERN_WARNING "mpu401: Command (0x%x), parm send failed.\n", (int) cmd->cmd); - return -EIO; - } - } - } - cmd->data[0] = 0; - - if (cmd->nr_returns) - { - for (i = 0; i < cmd->nr_returns; i++) - { - ok = 0; - for (timeout = 5000; timeout > 0 && !ok; timeout--) - if (input_avail(devc)) - { - cmd->data[i] = read_data(devc); - ok = 1; - } - if (!ok) - { - spin_unlock_irqrestore(&devc->lock,flags); - return -EIO; - } - } - } - spin_unlock_irqrestore(&devc->lock,flags); - return 0; -} - -static int mpu_cmd(int dev, int cmd, int data) -{ - int ret; - - static mpu_command_rec rec; - - rec.cmd = cmd & 0xff; - rec.nr_args = ((cmd & 0xf0) == 0xE0); - rec.nr_returns = ((cmd & 0xf0) == 0xA0); - rec.data[0] = data & 0xff; - - if ((ret = mpu401_command(dev, &rec)) < 0) - return ret; - return (unsigned char) rec.data[0]; -} - -static int mpu401_prefix_cmd(int dev, unsigned char status) -{ - struct mpu_config *devc = &dev_conf[dev]; - - if (devc->uart_mode) - return 1; - - if (status < 0xf0) - { - if (mpu_cmd(dev, 0xD0, 0) < 0) - return 0; - return 1; - } - switch (status) - { - case 0xF0: - if (mpu_cmd(dev, 0xDF, 0) < 0) - return 0; - return 1; - - default: - return 0; - } -} - -static int mpu401_start_read(int dev) -{ - return 0; -} - -static int mpu401_end_read(int dev) -{ - return 0; -} - -static int mpu401_ioctl(int dev, unsigned cmd, void __user *arg) -{ - struct mpu_config *devc; - mpu_command_rec rec; - int val, ret; - - devc = &dev_conf[dev]; - switch (cmd) - { - case SNDCTL_MIDI_MPUMODE: - if (!(devc->capabilities & MPU_CAP_INTLG)) { /* No intelligent mode */ - printk(KERN_WARNING "mpu401: Intelligent mode not supported by the HW\n"); - return -EINVAL; - } - if (get_user(val, (int __user *)arg)) - return -EFAULT; - set_uart_mode(dev, devc, !val); - return 0; - - case SNDCTL_MIDI_MPUCMD: - if (copy_from_user(&rec, arg, sizeof(rec))) - return -EFAULT; - if ((ret = mpu401_command(dev, &rec)) < 0) - return ret; - if (copy_to_user(arg, &rec, sizeof(rec))) - return -EFAULT; - return 0; - - default: - return -EINVAL; - } -} - -static void mpu401_kick(int dev) -{ -} - -static int mpu401_buffer_status(int dev) -{ - return 0; /* - * No data in buffers - */ -} - -static int mpu_synth_ioctl(int dev, unsigned int cmd, void __user *arg) -{ - int midi_dev; - struct mpu_config *devc; - - midi_dev = synth_devs[dev]->midi_dev; - - if (midi_dev < 0 || midi_dev >= num_midis || midi_devs[midi_dev] == NULL) - return -ENXIO; - - devc = &dev_conf[midi_dev]; - - switch (cmd) - { - - case SNDCTL_SYNTH_INFO: - if (copy_to_user(arg, &mpu_synth_info[midi_dev], - sizeof(struct synth_info))) - return -EFAULT; - return 0; - - case SNDCTL_SYNTH_MEMAVL: - return 0x7fffffff; - - default: - return -EINVAL; - } -} - -static int mpu_synth_open(int dev, int mode) -{ - int midi_dev, err; - struct mpu_config *devc; - struct coproc_operations *coprocessor; - - midi_dev = synth_devs[dev]->midi_dev; - - if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev] == NULL) - return -ENXIO; - - devc = &dev_conf[midi_dev]; - - /* - * Verify that the device is really running. - * Some devices (such as Ensoniq SoundScape don't - * work before the on board processor (OBP) is initialized - * by downloading its microcode. - */ - - if (!devc->initialized) - { - if (mpu401_status(devc) == 0xff) /* Bus float */ - { - printk(KERN_ERR "mpu401: Device not initialized properly\n"); - return -EIO; - } - reset_mpu401(devc); - } - if (devc->opened) - return -EBUSY; - devc->mode = MODE_SYNTH; - devc->synthno = dev; - - devc->inputintr = NULL; - - coprocessor = midi_devs[midi_dev]->coproc; - if (coprocessor) { - if (!try_module_get(coprocessor->owner)) - return -ENODEV; - - if ((err = coprocessor->open(coprocessor->devc, COPR_MIDI)) < 0) - { - printk(KERN_WARNING "mpu401: Can't access coprocessor device\n"); - return err; - } - } - devc->opened = mode; - reset_mpu401(devc); - - if (mode & OPEN_READ) - { - mpu_cmd(midi_dev, 0x8B, 0); /* Enable data in stop mode */ - mpu_cmd(midi_dev, 0x34, 0); /* Return timing bytes in stop mode */ - mpu_cmd(midi_dev, 0x87, 0); /* Enable pitch & controller */ - } - return 0; -} - -static void mpu_synth_close(int dev) -{ - int midi_dev; - struct mpu_config *devc; - struct coproc_operations *coprocessor; - - midi_dev = synth_devs[dev]->midi_dev; - - devc = &dev_conf[midi_dev]; - mpu_cmd(midi_dev, 0x15, 0); /* Stop recording, playback and MIDI */ - mpu_cmd(midi_dev, 0x8a, 0); /* Disable data in stopped mode */ - - devc->inputintr = NULL; - - coprocessor = midi_devs[midi_dev]->coproc; - if (coprocessor) { - coprocessor->close(coprocessor->devc, COPR_MIDI); - module_put(coprocessor->owner); - } - devc->opened = 0; - devc->mode = 0; -} - -#define MIDI_SYNTH_NAME "MPU-401 UART Midi" -#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT -#include "midi_synth.h" - -static struct synth_operations mpu401_synth_proto = -{ - .owner = THIS_MODULE, - .id = "MPU401", - .info = NULL, - .midi_dev = 0, - .synth_type = SYNTH_TYPE_MIDI, - .synth_subtype = 0, - .open = mpu_synth_open, - .close = mpu_synth_close, - .ioctl = mpu_synth_ioctl, - .kill_note = midi_synth_kill_note, - .start_note = midi_synth_start_note, - .set_instr = midi_synth_set_instr, - .reset = midi_synth_reset, - .hw_control = midi_synth_hw_control, - .load_patch = midi_synth_load_patch, - .aftertouch = midi_synth_aftertouch, - .controller = midi_synth_controller, - .panning = midi_synth_panning, - .bender = midi_synth_bender, - .setup_voice = midi_synth_setup_voice, - .send_sysex = midi_synth_send_sysex -}; - -static struct synth_operations *mpu401_synth_operations[MAX_MIDI_DEV]; - -static struct midi_operations mpu401_midi_proto = -{ - .owner = THIS_MODULE, - .info = {"MPU-401 Midi", 0, MIDI_CAP_MPU401, SNDCARD_MPU401}, - .in_info = {0}, - .open = mpu401_open, - .close = mpu401_close, - .ioctl = mpu401_ioctl, - .outputc = mpu401_out, - .start_read = mpu401_start_read, - .end_read = mpu401_end_read, - .kick = mpu401_kick, - .buffer_status = mpu401_buffer_status, - .prefix_cmd = mpu401_prefix_cmd -}; - -static struct midi_operations mpu401_midi_operations[MAX_MIDI_DEV]; - -static void mpu401_chk_version(int n, struct mpu_config *devc) -{ - int tmp; - - devc->version = devc->revision = 0; - - tmp = mpu_cmd(n, 0xAC, 0); - if (tmp < 0) - return; - if ((tmp & 0xf0) > 0x20) /* Why it's larger than 2.x ??? */ - return; - devc->version = tmp; - - if ((tmp = mpu_cmd(n, 0xAD, 0)) < 0) { - devc->version = 0; - return; - } - devc->revision = tmp; -} - -int attach_mpu401(struct address_info *hw_config, struct module *owner) -{ - unsigned long flags; - char revision_char; - - int m, ret; - struct mpu_config *devc; - - hw_config->slots[1] = -1; - m = sound_alloc_mididev(); - if (m == -1) - { - printk(KERN_WARNING "MPU-401: Too many midi devices detected\n"); - ret = -ENOMEM; - goto out_err; - } - devc = &dev_conf[m]; - devc->base = hw_config->io_base; - devc->osp = hw_config->osp; - devc->irq = hw_config->irq; - devc->opened = 0; - devc->uart_mode = 0; - devc->initialized = 0; - devc->version = 0; - devc->revision = 0; - devc->capabilities = 0; - devc->timer_flag = 0; - devc->m_busy = 0; - devc->m_state = ST_INIT; - devc->shared_irq = hw_config->always_detect; - spin_lock_init(&devc->lock); - - if (devc->irq < 0) - { - devc->irq *= -1; - devc->shared_irq = 1; - } - - if (!hw_config->always_detect) - { - /* Verify the hardware again */ - if (!reset_mpu401(devc)) - { - printk(KERN_WARNING "mpu401: Device didn't respond\n"); - ret = -ENODEV; - goto out_mididev; - } - if (!devc->shared_irq) - { - if (request_irq(devc->irq, mpuintr, 0, "mpu401", - hw_config) < 0) - { - printk(KERN_WARNING "mpu401: Failed to allocate IRQ%d\n", devc->irq); - ret = -ENOMEM; - goto out_mididev; - } - } - spin_lock_irqsave(&devc->lock,flags); - mpu401_chk_version(m, devc); - if (devc->version == 0) - mpu401_chk_version(m, devc); - spin_unlock_irqrestore(&devc->lock, flags); - } - - if (devc->version != 0) - if (mpu_cmd(m, 0xC5, 0) >= 0) /* Set timebase OK */ - if (mpu_cmd(m, 0xE0, 120) >= 0) /* Set tempo OK */ - devc->capabilities |= MPU_CAP_INTLG; /* Supports intelligent mode */ - - - mpu401_synth_operations[m] = kmalloc(sizeof(struct synth_operations), GFP_KERNEL); - - if (mpu401_synth_operations[m] == NULL) - { - printk(KERN_ERR "mpu401: Can't allocate memory\n"); - ret = -ENOMEM; - goto out_irq; - } - if (!(devc->capabilities & MPU_CAP_INTLG)) /* No intelligent mode */ - { - memcpy((char *) mpu401_synth_operations[m], - (char *) &std_midi_synth, - sizeof(struct synth_operations)); - } - else - { - memcpy((char *) mpu401_synth_operations[m], - (char *) &mpu401_synth_proto, - sizeof(struct synth_operations)); - } - if (owner) - mpu401_synth_operations[m]->owner = owner; - - memcpy((char *) &mpu401_midi_operations[m], - (char *) &mpu401_midi_proto, - sizeof(struct midi_operations)); - - mpu401_midi_operations[m].converter = mpu401_synth_operations[m]; - - memcpy((char *) &mpu_synth_info[m], - (char *) &mpu_synth_info_proto, - sizeof(struct synth_info)); - - n_mpu_devs++; - - if (devc->version == 0x20 && devc->revision >= 0x07) /* MusicQuest interface */ - { - int ports = (devc->revision & 0x08) ? 32 : 16; - - devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_SMPTE | - MPU_CAP_CLS | MPU_CAP_2PORT; - - revision_char = (devc->revision == 0x7f) ? 'M' : ' '; - sprintf(mpu_synth_info[m].name, "MQX-%d%c MIDI Interface #%d", - ports, - revision_char, - n_mpu_devs); - } - else - { - revision_char = devc->revision ? devc->revision + '@' : ' '; - if ((int) devc->revision > ('Z' - '@')) - revision_char = '+'; - - devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_FSK; - - if (hw_config->name) - sprintf(mpu_synth_info[m].name, "%s (MPU401)", hw_config->name); - else - sprintf(mpu_synth_info[m].name, - "MPU-401 %d.%d%c MIDI #%d", - (int) (devc->version & 0xf0) >> 4, - devc->version & 0x0f, - revision_char, - n_mpu_devs); - } - - strcpy(mpu401_midi_operations[m].info.name, - mpu_synth_info[m].name); - - conf_printf(mpu_synth_info[m].name, hw_config); - - mpu401_synth_operations[m]->midi_dev = devc->devno = m; - mpu401_synth_operations[devc->devno]->info = &mpu_synth_info[devc->devno]; - - if (devc->capabilities & MPU_CAP_INTLG) /* Intelligent mode */ - hw_config->slots[2] = mpu_timer_init(m); - - midi_devs[m] = &mpu401_midi_operations[devc->devno]; - - if (owner) - midi_devs[m]->owner = owner; - - hw_config->slots[1] = m; - sequencer_init(); - - return 0; - -out_irq: - free_irq(devc->irq, hw_config); -out_mididev: - sound_unload_mididev(m); -out_err: - release_region(hw_config->io_base, 2); - return ret; -} - -static int reset_mpu401(struct mpu_config *devc) -{ - unsigned long flags; - int ok, timeout, n; - int timeout_limit; - - /* - * Send the RESET command. Try again if no success at the first time. - * (If the device is in the UART mode, it will not ack the reset cmd). - */ - - ok = 0; - - timeout_limit = devc->initialized ? 30000 : 100000; - devc->initialized = 1; - - for (n = 0; n < 2 && !ok; n++) - { - for (timeout = timeout_limit; timeout > 0 && !ok; timeout--) - ok = output_ready(devc); - - write_command(devc, MPU_RESET); /* - * Send MPU-401 RESET Command - */ - - /* - * Wait at least 25 msec. This method is not accurate so let's make the - * loop bit longer. Cannot sleep since this is called during boot. - */ - - for (timeout = timeout_limit * 2; timeout > 0 && !ok; timeout--) - { - spin_lock_irqsave(&devc->lock,flags); - if (input_avail(devc)) - if (read_data(devc) == MPU_ACK) - ok = 1; - spin_unlock_irqrestore(&devc->lock,flags); - } - - } - - devc->m_state = ST_INIT; - devc->m_ptr = 0; - devc->m_left = 0; - devc->last_status = 0; - devc->uart_mode = 0; - - return ok; -} - -static void set_uart_mode(int dev, struct mpu_config *devc, int arg) -{ - if (!arg && (devc->capabilities & MPU_CAP_INTLG)) - return; - if ((devc->uart_mode == 0) == (arg == 0)) - return; /* Already set */ - reset_mpu401(devc); /* This exits the uart mode */ - - if (arg) - { - if (mpu_cmd(dev, UART_MODE_ON, 0) < 0) - { - printk(KERN_ERR "mpu401: Can't enter UART mode\n"); - devc->uart_mode = 0; - return; - } - } - devc->uart_mode = arg; - -} - -int probe_mpu401(struct address_info *hw_config, struct resource *ports) -{ - int ok = 0; - struct mpu_config tmp_devc; - - tmp_devc.base = hw_config->io_base; - tmp_devc.irq = hw_config->irq; - tmp_devc.initialized = 0; - tmp_devc.opened = 0; - tmp_devc.osp = hw_config->osp; - - if (hw_config->always_detect) - return 1; - - if (inb(hw_config->io_base + 1) == 0xff) - { - DDB(printk("MPU401: Port %x looks dead.\n", hw_config->io_base)); - return 0; /* Just bus float? */ - } - ok = reset_mpu401(&tmp_devc); - - if (!ok) - { - DDB(printk("MPU401: Reset failed on port %x\n", hw_config->io_base)); - } - return ok; -} - -void unload_mpu401(struct address_info *hw_config) -{ - void *p; - int n=hw_config->slots[1]; - - if (n != -1) { - release_region(hw_config->io_base, 2); - if (hw_config->always_detect == 0 && hw_config->irq > 0) - free_irq(hw_config->irq, hw_config); - p=mpu401_synth_operations[n]; - sound_unload_mididev(n); - sound_unload_timerdev(hw_config->slots[2]); - kfree(p); - } -} - -/***************************************************** - * Timer stuff - ****************************************************/ - -static volatile int timer_initialized = 0, timer_open = 0, tmr_running = 0; -static volatile int curr_tempo, curr_timebase, hw_timebase; -static int max_timebase = 8; /* 8*24=192 ppqn */ -static volatile unsigned long next_event_time; -static volatile unsigned long curr_ticks, curr_clocks; -static unsigned long prev_event_time; -static int metronome_mode; - -static unsigned long clocks2ticks(unsigned long clocks) -{ - /* - * The MPU-401 supports just a limited set of possible timebase values. - * Since the applications require more choices, the driver has to - * program the HW to do its best and to convert between the HW and - * actual timebases. - */ - return ((clocks * curr_timebase) + (hw_timebase / 2)) / hw_timebase; -} - -static void set_timebase(int midi_dev, int val) -{ - int hw_val; - - if (val < 48) - val = 48; - if (val > 1000) - val = 1000; - - hw_val = val; - hw_val = (hw_val + 12) / 24; - if (hw_val > max_timebase) - hw_val = max_timebase; - - if (mpu_cmd(midi_dev, 0xC0 | (hw_val & 0x0f), 0) < 0) - { - printk(KERN_WARNING "mpu401: Can't set HW timebase to %d\n", hw_val * 24); - return; - } - hw_timebase = hw_val * 24; - curr_timebase = val; - -} - -static void tmr_reset(struct mpu_config *devc) -{ - unsigned long flags; - - spin_lock_irqsave(&devc->lock,flags); - next_event_time = (unsigned long) -1; - prev_event_time = 0; - curr_ticks = curr_clocks = 0; - spin_unlock_irqrestore(&devc->lock,flags); -} - -static void set_timer_mode(int midi_dev) -{ - if (timer_mode & TMR_MODE_CLS) - mpu_cmd(midi_dev, 0x3c, 0); /* Use CLS sync */ - else if (timer_mode & TMR_MODE_SMPTE) - mpu_cmd(midi_dev, 0x3d, 0); /* Use SMPTE sync */ - - if (timer_mode & TMR_INTERNAL) - { - mpu_cmd(midi_dev, 0x80, 0); /* Use MIDI sync */ - } - else - { - if (timer_mode & (TMR_MODE_MIDI | TMR_MODE_CLS)) - { - mpu_cmd(midi_dev, 0x82, 0); /* Use MIDI sync */ - mpu_cmd(midi_dev, 0x91, 0); /* Enable ext MIDI ctrl */ - } - else if (timer_mode & TMR_MODE_FSK) - mpu_cmd(midi_dev, 0x81, 0); /* Use FSK sync */ - } -} - -static void stop_metronome(int midi_dev) -{ - mpu_cmd(midi_dev, 0x84, 0); /* Disable metronome */ -} - -static void setup_metronome(int midi_dev) -{ - int numerator, denominator; - int clks_per_click, num_32nds_per_beat; - int beats_per_measure; - - numerator = ((unsigned) metronome_mode >> 24) & 0xff; - denominator = ((unsigned) metronome_mode >> 16) & 0xff; - clks_per_click = ((unsigned) metronome_mode >> 8) & 0xff; - num_32nds_per_beat = (unsigned) metronome_mode & 0xff; - beats_per_measure = (numerator * 4) >> denominator; - - if (!metronome_mode) - mpu_cmd(midi_dev, 0x84, 0); /* Disable metronome */ - else - { - mpu_cmd(midi_dev, 0xE4, clks_per_click); - mpu_cmd(midi_dev, 0xE6, beats_per_measure); - mpu_cmd(midi_dev, 0x83, 0); /* Enable metronome without accents */ - } -} - -static int mpu_start_timer(int midi_dev) -{ - struct mpu_config *devc= &dev_conf[midi_dev]; - - tmr_reset(devc); - set_timer_mode(midi_dev); - - if (tmr_running) - return TIMER_NOT_ARMED; /* Already running */ - - if (timer_mode & TMR_INTERNAL) - { - mpu_cmd(midi_dev, 0x02, 0); /* Send MIDI start */ - tmr_running = 1; - return TIMER_NOT_ARMED; - } - else - { - mpu_cmd(midi_dev, 0x35, 0); /* Enable mode messages to PC */ - mpu_cmd(midi_dev, 0x38, 0); /* Enable sys common messages to PC */ - mpu_cmd(midi_dev, 0x39, 0); /* Enable real time messages to PC */ - mpu_cmd(midi_dev, 0x97, 0); /* Enable system exclusive messages to PC */ - } - return TIMER_ARMED; -} - -static int mpu_timer_open(int dev, int mode) -{ - int midi_dev = sound_timer_devs[dev]->devlink; - struct mpu_config *devc= &dev_conf[midi_dev]; - - if (timer_open) - return -EBUSY; - - tmr_reset(devc); - curr_tempo = 50; - mpu_cmd(midi_dev, 0xE0, 50); - curr_timebase = hw_timebase = 120; - set_timebase(midi_dev, 120); - timer_open = 1; - metronome_mode = 0; - set_timer_mode(midi_dev); - - mpu_cmd(midi_dev, 0xe7, 0x04); /* Send all clocks to host */ - mpu_cmd(midi_dev, 0x95, 0); /* Enable clock to host */ - - return 0; -} - -static void mpu_timer_close(int dev) -{ - int midi_dev = sound_timer_devs[dev]->devlink; - - timer_open = tmr_running = 0; - mpu_cmd(midi_dev, 0x15, 0); /* Stop all */ - mpu_cmd(midi_dev, 0x94, 0); /* Disable clock to host */ - mpu_cmd(midi_dev, 0x8c, 0); /* Disable measure end messages to host */ - stop_metronome(midi_dev); -} - -static int mpu_timer_event(int dev, unsigned char *event) -{ - unsigned char command = event[1]; - unsigned long parm = *(unsigned int *) &event[4]; - int midi_dev = sound_timer_devs[dev]->devlink; - - switch (command) - { - case TMR_WAIT_REL: - parm += prev_event_time; - case TMR_WAIT_ABS: - if (parm > 0) - { - long time; - - if (parm <= curr_ticks) /* It's the time */ - return TIMER_NOT_ARMED; - time = parm; - next_event_time = prev_event_time = time; - - return TIMER_ARMED; - } - break; - - case TMR_START: - if (tmr_running) - break; - return mpu_start_timer(midi_dev); - - case TMR_STOP: - mpu_cmd(midi_dev, 0x01, 0); /* Send MIDI stop */ - stop_metronome(midi_dev); - tmr_running = 0; - break; - - case TMR_CONTINUE: - if (tmr_running) - break; - mpu_cmd(midi_dev, 0x03, 0); /* Send MIDI continue */ - setup_metronome(midi_dev); - tmr_running = 1; - break; - - case TMR_TEMPO: - if (parm) - { - if (parm < 8) - parm = 8; - if (parm > 250) - parm = 250; - if (mpu_cmd(midi_dev, 0xE0, parm) < 0) - printk(KERN_WARNING "mpu401: Can't set tempo to %d\n", (int) parm); - curr_tempo = parm; - } - break; - - case TMR_ECHO: - seq_copy_to_input(event, 8); - break; - - case TMR_TIMESIG: - if (metronome_mode) /* Metronome enabled */ - { - metronome_mode = parm; - setup_metronome(midi_dev); - } - break; - - default:; - } - return TIMER_NOT_ARMED; -} - -static unsigned long mpu_timer_get_time(int dev) -{ - if (!timer_open) - return 0; - - return curr_ticks; -} - -static int mpu_timer_ioctl(int dev, unsigned int command, void __user *arg) -{ - int midi_dev = sound_timer_devs[dev]->devlink; - int __user *p = (int __user *)arg; - - switch (command) - { - case SNDCTL_TMR_SOURCE: - { - int parm; - - if (get_user(parm, p)) - return -EFAULT; - parm &= timer_caps; - - if (parm != 0) - { - timer_mode = parm; - - if (timer_mode & TMR_MODE_CLS) - mpu_cmd(midi_dev, 0x3c, 0); /* Use CLS sync */ - else if (timer_mode & TMR_MODE_SMPTE) - mpu_cmd(midi_dev, 0x3d, 0); /* Use SMPTE sync */ - } - if (put_user(timer_mode, p)) - return -EFAULT; - return timer_mode; - } - break; - - case SNDCTL_TMR_START: - mpu_start_timer(midi_dev); - return 0; - - case SNDCTL_TMR_STOP: - tmr_running = 0; - mpu_cmd(midi_dev, 0x01, 0); /* Send MIDI stop */ - stop_metronome(midi_dev); - return 0; - - case SNDCTL_TMR_CONTINUE: - if (tmr_running) - return 0; - tmr_running = 1; - mpu_cmd(midi_dev, 0x03, 0); /* Send MIDI continue */ - return 0; - - case SNDCTL_TMR_TIMEBASE: - { - int val; - if (get_user(val, p)) - return -EFAULT; - if (val) - set_timebase(midi_dev, val); - if (put_user(curr_timebase, p)) - return -EFAULT; - return curr_timebase; - } - break; - - case SNDCTL_TMR_TEMPO: - { - int val; - int ret; - - if (get_user(val, p)) - return -EFAULT; - - if (val) - { - if (val < 8) - val = 8; - if (val > 250) - val = 250; - if ((ret = mpu_cmd(midi_dev, 0xE0, val)) < 0) - { - printk(KERN_WARNING "mpu401: Can't set tempo to %d\n", (int) val); - return ret; - } - curr_tempo = val; - } - if (put_user(curr_tempo, p)) - return -EFAULT; - return curr_tempo; - } - break; - - case SNDCTL_SEQ_CTRLRATE: - { - int val; - if (get_user(val, p)) - return -EFAULT; - - if (val != 0) /* Can't change */ - return -EINVAL; - val = ((curr_tempo * curr_timebase) + 30)/60; - if (put_user(val, p)) - return -EFAULT; - return val; - } - break; - - case SNDCTL_SEQ_GETTIME: - if (put_user(curr_ticks, p)) - return -EFAULT; - return curr_ticks; - - case SNDCTL_TMR_METRONOME: - if (get_user(metronome_mode, p)) - return -EFAULT; - setup_metronome(midi_dev); - return 0; - - default:; - } - return -EINVAL; -} - -static void mpu_timer_arm(int dev, long time) -{ - if (time < 0) - time = curr_ticks + 1; - else if (time <= curr_ticks) /* It's the time */ - return; - next_event_time = prev_event_time = time; - return; -} - -static struct sound_timer_operations mpu_timer = -{ - .owner = THIS_MODULE, - .info = {"MPU-401 Timer", 0}, - .priority = 10, /* Priority */ - .devlink = 0, /* Local device link */ - .open = mpu_timer_open, - .close = mpu_timer_close, - .event = mpu_timer_event, - .get_time = mpu_timer_get_time, - .ioctl = mpu_timer_ioctl, - .arm_timer = mpu_timer_arm -}; - -static void mpu_timer_interrupt(void) -{ - if (!timer_open) - return; - - if (!tmr_running) - return; - - curr_clocks++; - curr_ticks = clocks2ticks(curr_clocks); - - if (curr_ticks >= next_event_time) - { - next_event_time = (unsigned long) -1; - sequencer_timer(0); - } -} - -static void timer_ext_event(struct mpu_config *devc, int event, int parm) -{ - int midi_dev = devc->devno; - - if (!devc->timer_flag) - return; - - switch (event) - { - case TMR_CLOCK: - printk("<MIDI clk>"); - break; - - case TMR_START: - printk("Ext MIDI start\n"); - if (!tmr_running) - { - if (timer_mode & TMR_EXTERNAL) - { - tmr_running = 1; - setup_metronome(midi_dev); - next_event_time = 0; - STORE(SEQ_START_TIMER()); - } - } - break; - - case TMR_STOP: - printk("Ext MIDI stop\n"); - if (timer_mode & TMR_EXTERNAL) - { - tmr_running = 0; - stop_metronome(midi_dev); - STORE(SEQ_STOP_TIMER()); - } - break; - - case TMR_CONTINUE: - printk("Ext MIDI continue\n"); - if (timer_mode & TMR_EXTERNAL) - { - tmr_running = 1; - setup_metronome(midi_dev); - STORE(SEQ_CONTINUE_TIMER()); - } - break; - - case TMR_SPP: - printk("Songpos: %d\n", parm); - if (timer_mode & TMR_EXTERNAL) - { - STORE(SEQ_SONGPOS(parm)); - } - break; - } -} - -static int mpu_timer_init(int midi_dev) -{ - struct mpu_config *devc; - int n; - - devc = &dev_conf[midi_dev]; - - if (timer_initialized) - return -1; /* There is already a similar timer */ - - timer_initialized = 1; - - mpu_timer.devlink = midi_dev; - dev_conf[midi_dev].timer_flag = 1; - - n = sound_alloc_timerdev(); - if (n == -1) - n = 0; - sound_timer_devs[n] = &mpu_timer; - - if (devc->version < 0x20) /* Original MPU-401 */ - timer_caps = TMR_INTERNAL | TMR_EXTERNAL | TMR_MODE_FSK | TMR_MODE_MIDI; - else - { - /* - * The version number 2.0 is used (at least) by the - * MusicQuest cards and the Roland Super-MPU. - * - * MusicQuest has given a special meaning to the bits of the - * revision number. The Super-MPU returns 0. - */ - - if (devc->revision) - timer_caps |= TMR_EXTERNAL | TMR_MODE_MIDI; - - if (devc->revision & 0x02) - timer_caps |= TMR_MODE_CLS; - - - if (devc->revision & 0x40) - max_timebase = 10; /* Has the 216 and 240 ppqn modes */ - } - - timer_mode = (TMR_INTERNAL | TMR_MODE_MIDI) & timer_caps; - return n; - -} - -EXPORT_SYMBOL(probe_mpu401); -EXPORT_SYMBOL(attach_mpu401); -EXPORT_SYMBOL(unload_mpu401); - -static struct address_info cfg; - -static int io = -1; -static int irq = -1; - -module_param_hw(irq, int, irq, 0); -module_param_hw(io, int, ioport, 0); - -static int __init init_mpu401(void) -{ - int ret; - /* Can be loaded either for module use or to provide functions - to others */ - if (io != -1 && irq != -1) { - struct resource *ports; - cfg.irq = irq; - cfg.io_base = io; - ports = request_region(io, 2, "mpu401"); - if (!ports) - return -EBUSY; - if (probe_mpu401(&cfg, ports) == 0) { - release_region(io, 2); - return -ENODEV; - } - if ((ret = attach_mpu401(&cfg, THIS_MODULE))) - return ret; - } - - return 0; -} - -static void __exit cleanup_mpu401(void) -{ - if (io != -1 && irq != -1) { - /* Check for use by, for example, sscape driver */ - unload_mpu401(&cfg); - } -} - -module_init(init_mpu401); -module_exit(cleanup_mpu401); - -#ifndef MODULE -static int __init setup_mpu401(char *str) -{ - /* io, irq */ - int ints[3]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - - return 1; -} - -__setup("mpu401=", setup_mpu401); -#endif -MODULE_LICENSE("GPL"); diff --git a/sound/oss/mpu401.h b/sound/oss/mpu401.h deleted file mode 100644 index 6beb8c2ae405..000000000000 --- a/sound/oss/mpu401.h +++ /dev/null @@ -1,12 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/* From uart401.c */ -int probe_uart401 (struct address_info *hw_config, struct module *owner); -void unload_uart401 (struct address_info *hw_config); - -irqreturn_t uart401intr (int irq, void *dev_id); - -/* From mpu401.c */ -int probe_mpu401(struct address_info *hw_config, struct resource *ports); -int attach_mpu401(struct address_info * hw_config, struct module *owner); -void unload_mpu401(struct address_info *hw_info); diff --git a/sound/oss/msnd.c b/sound/oss/msnd.c deleted file mode 100644 index b63010ad22f1..000000000000 --- a/sound/oss/msnd.c +++ /dev/null @@ -1,413 +0,0 @@ -/********************************************************************* - * - * msnd.c - Driver Base - * - * Turtle Beach MultiSound Sound Card Driver for Linux - * - * Copyright (C) 1998 Andrew Veliath - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - * - ********************************************************************/ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/vmalloc.h> -#include <linux/types.h> -#include <linux/delay.h> -#include <linux/mm.h> -#include <linux/init.h> -#include <linux/interrupt.h> - -#include <asm/io.h> -#include <linux/uaccess.h> -#include <linux/spinlock.h> -#include <asm/irq.h> -#include "msnd.h" - -#define LOGNAME "msnd" - -#define MSND_MAX_DEVS 4 - -static multisound_dev_t *devs[MSND_MAX_DEVS]; -static int num_devs; - -int msnd_register(multisound_dev_t *dev) -{ - int i; - - for (i = 0; i < MSND_MAX_DEVS; ++i) - if (devs[i] == NULL) - break; - - if (i == MSND_MAX_DEVS) - return -ENOMEM; - - devs[i] = dev; - ++num_devs; - return 0; -} - -void msnd_unregister(multisound_dev_t *dev) -{ - int i; - - for (i = 0; i < MSND_MAX_DEVS; ++i) - if (devs[i] == dev) - break; - - if (i == MSND_MAX_DEVS) { - printk(KERN_WARNING LOGNAME ": Unregistering unknown device\n"); - return; - } - - devs[i] = NULL; - --num_devs; -} - -void msnd_init_queue(void __iomem *base, int start, int size) -{ - writew(PCTODSP_BASED(start), base + JQS_wStart); - writew(PCTODSP_OFFSET(size) - 1, base + JQS_wSize); - writew(0, base + JQS_wHead); - writew(0, base + JQS_wTail); -} - -void msnd_fifo_init(msnd_fifo *f) -{ - f->data = NULL; -} - -void msnd_fifo_free(msnd_fifo *f) -{ - vfree(f->data); - f->data = NULL; -} - -int msnd_fifo_alloc(msnd_fifo *f, size_t n) -{ - msnd_fifo_free(f); - f->data = vmalloc(n); - f->n = n; - f->tail = 0; - f->head = 0; - f->len = 0; - - if (!f->data) - return -ENOMEM; - - return 0; -} - -void msnd_fifo_make_empty(msnd_fifo *f) -{ - f->len = f->tail = f->head = 0; -} - -int msnd_fifo_write_io(msnd_fifo *f, char __iomem *buf, size_t len) -{ - int count = 0; - - while ((count < len) && (f->len != f->n)) { - - int nwritten; - - if (f->head <= f->tail) { - nwritten = len - count; - if (nwritten > f->n - f->tail) - nwritten = f->n - f->tail; - } - else { - nwritten = f->head - f->tail; - if (nwritten > len - count) - nwritten = len - count; - } - - memcpy_fromio(f->data + f->tail, buf, nwritten); - - count += nwritten; - buf += nwritten; - f->len += nwritten; - f->tail += nwritten; - f->tail %= f->n; - } - - return count; -} - -int msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len) -{ - int count = 0; - - while ((count < len) && (f->len != f->n)) { - - int nwritten; - - if (f->head <= f->tail) { - nwritten = len - count; - if (nwritten > f->n - f->tail) - nwritten = f->n - f->tail; - } - else { - nwritten = f->head - f->tail; - if (nwritten > len - count) - nwritten = len - count; - } - - memcpy(f->data + f->tail, buf, nwritten); - - count += nwritten; - buf += nwritten; - f->len += nwritten; - f->tail += nwritten; - f->tail %= f->n; - } - - return count; -} - -int msnd_fifo_read_io(msnd_fifo *f, char __iomem *buf, size_t len) -{ - int count = 0; - - while ((count < len) && (f->len > 0)) { - - int nread; - - if (f->tail <= f->head) { - nread = len - count; - if (nread > f->n - f->head) - nread = f->n - f->head; - } - else { - nread = f->tail - f->head; - if (nread > len - count) - nread = len - count; - } - - memcpy_toio(buf, f->data + f->head, nread); - - count += nread; - buf += nread; - f->len -= nread; - f->head += nread; - f->head %= f->n; - } - - return count; -} - -int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len) -{ - int count = 0; - - while ((count < len) && (f->len > 0)) { - - int nread; - - if (f->tail <= f->head) { - nread = len - count; - if (nread > f->n - f->head) - nread = f->n - f->head; - } - else { - nread = f->tail - f->head; - if (nread > len - count) - nread = len - count; - } - - memcpy(buf, f->data + f->head, nread); - - count += nread; - buf += nread; - f->len -= nread; - f->head += nread; - f->head %= f->n; - } - - return count; -} - -static int msnd_wait_TXDE(multisound_dev_t *dev) -{ - register unsigned int io = dev->io; - register int timeout = 1000; - - while(timeout-- > 0) - if (msnd_inb(io + HP_ISR) & HPISR_TXDE) - return 0; - - return -EIO; -} - -static int msnd_wait_HC0(multisound_dev_t *dev) -{ - register unsigned int io = dev->io; - register int timeout = 1000; - - while(timeout-- > 0) - if (!(msnd_inb(io + HP_CVR) & HPCVR_HC)) - return 0; - - return -EIO; -} - -int msnd_send_dsp_cmd(multisound_dev_t *dev, BYTE cmd) -{ - unsigned long flags; - - spin_lock_irqsave(&dev->lock, flags); - if (msnd_wait_HC0(dev) == 0) { - msnd_outb(cmd, dev->io + HP_CVR); - spin_unlock_irqrestore(&dev->lock, flags); - return 0; - } - spin_unlock_irqrestore(&dev->lock, flags); - - printk(KERN_DEBUG LOGNAME ": Send DSP command timeout\n"); - - return -EIO; -} - -int msnd_send_word(multisound_dev_t *dev, unsigned char high, - unsigned char mid, unsigned char low) -{ - register unsigned int io = dev->io; - - if (msnd_wait_TXDE(dev) == 0) { - msnd_outb(high, io + HP_TXH); - msnd_outb(mid, io + HP_TXM); - msnd_outb(low, io + HP_TXL); - return 0; - } - - printk(KERN_DEBUG LOGNAME ": Send host word timeout\n"); - - return -EIO; -} - -int msnd_upload_host(multisound_dev_t *dev, char *bin, int len) -{ - int i; - - if (len % 3 != 0) { - printk(KERN_WARNING LOGNAME ": Upload host data not multiple of 3!\n"); - return -EINVAL; - } - - for (i = 0; i < len; i += 3) - if (msnd_send_word(dev, bin[i], bin[i + 1], bin[i + 2]) != 0) - return -EIO; - - msnd_inb(dev->io + HP_RXL); - msnd_inb(dev->io + HP_CVR); - - return 0; -} - -int msnd_enable_irq(multisound_dev_t *dev) -{ - unsigned long flags; - - if (dev->irq_ref++) - return 0; - - printk(KERN_DEBUG LOGNAME ": Enabling IRQ\n"); - - spin_lock_irqsave(&dev->lock, flags); - if (msnd_wait_TXDE(dev) == 0) { - msnd_outb(msnd_inb(dev->io + HP_ICR) | HPICR_TREQ, dev->io + HP_ICR); - if (dev->type == msndClassic) - msnd_outb(dev->irqid, dev->io + HP_IRQM); - msnd_outb(msnd_inb(dev->io + HP_ICR) & ~HPICR_TREQ, dev->io + HP_ICR); - msnd_outb(msnd_inb(dev->io + HP_ICR) | HPICR_RREQ, dev->io + HP_ICR); - enable_irq(dev->irq); - msnd_init_queue(dev->DSPQ, dev->dspq_data_buff, dev->dspq_buff_size); - spin_unlock_irqrestore(&dev->lock, flags); - return 0; - } - spin_unlock_irqrestore(&dev->lock, flags); - - printk(KERN_DEBUG LOGNAME ": Enable IRQ failed\n"); - - return -EIO; -} - -int msnd_disable_irq(multisound_dev_t *dev) -{ - unsigned long flags; - - if (--dev->irq_ref > 0) - return 0; - - if (dev->irq_ref < 0) - printk(KERN_DEBUG LOGNAME ": IRQ ref count is %d\n", dev->irq_ref); - - printk(KERN_DEBUG LOGNAME ": Disabling IRQ\n"); - - spin_lock_irqsave(&dev->lock, flags); - if (msnd_wait_TXDE(dev) == 0) { - msnd_outb(msnd_inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR); - if (dev->type == msndClassic) - msnd_outb(HPIRQ_NONE, dev->io + HP_IRQM); - disable_irq(dev->irq); - spin_unlock_irqrestore(&dev->lock, flags); - return 0; - } - spin_unlock_irqrestore(&dev->lock, flags); - - printk(KERN_DEBUG LOGNAME ": Disable IRQ failed\n"); - - return -EIO; -} - -#ifndef LINUX20 -EXPORT_SYMBOL(msnd_register); -EXPORT_SYMBOL(msnd_unregister); - -EXPORT_SYMBOL(msnd_init_queue); - -EXPORT_SYMBOL(msnd_fifo_init); -EXPORT_SYMBOL(msnd_fifo_free); -EXPORT_SYMBOL(msnd_fifo_alloc); -EXPORT_SYMBOL(msnd_fifo_make_empty); -EXPORT_SYMBOL(msnd_fifo_write_io); -EXPORT_SYMBOL(msnd_fifo_read_io); -EXPORT_SYMBOL(msnd_fifo_write); -EXPORT_SYMBOL(msnd_fifo_read); - -EXPORT_SYMBOL(msnd_send_dsp_cmd); -EXPORT_SYMBOL(msnd_send_word); -EXPORT_SYMBOL(msnd_upload_host); - -EXPORT_SYMBOL(msnd_enable_irq); -EXPORT_SYMBOL(msnd_disable_irq); -#endif - -#ifdef MODULE -MODULE_AUTHOR ("Andrew Veliath <andrewtv@usa.net>"); -MODULE_DESCRIPTION ("Turtle Beach MultiSound Driver Base"); -MODULE_LICENSE("GPL"); - - -int init_module(void) -{ - return 0; -} - -void cleanup_module(void) -{ -} -#endif diff --git a/sound/oss/msnd.h b/sound/oss/msnd.h deleted file mode 100644 index c8be47ec2b7e..000000000000 --- a/sound/oss/msnd.h +++ /dev/null @@ -1,278 +0,0 @@ -/********************************************************************* - * - * msnd.h - * - * Turtle Beach MultiSound Sound Card Driver for Linux - * - * Some parts of this header file were derived from the Turtle Beach - * MultiSound Driver Development Kit. - * - * Copyright (C) 1998 Andrew Veliath - * Copyright (C) 1993 Turtle Beach Systems, Inc. - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - * - ********************************************************************/ -#ifndef __MSND_H -#define __MSND_H - -#define VERSION "0.8.3.1" - -#define DEFSAMPLERATE DSP_DEFAULT_SPEED -#define DEFSAMPLESIZE AFMT_U8 -#define DEFCHANNELS 1 - -#define DEFFIFOSIZE 128 - -#define SNDCARD_MSND 38 - -#define SRAM_BANK_SIZE 0x8000 -#define SRAM_CNTL_START 0x7F00 - -#define DSP_BASE_ADDR 0x4000 -#define DSP_BANK_BASE 0x4000 - -#define HP_ICR 0x00 -#define HP_CVR 0x01 -#define HP_ISR 0x02 -#define HP_IVR 0x03 -#define HP_NU 0x04 -#define HP_INFO 0x04 -#define HP_TXH 0x05 -#define HP_RXH 0x05 -#define HP_TXM 0x06 -#define HP_RXM 0x06 -#define HP_TXL 0x07 -#define HP_RXL 0x07 - -#define HP_ICR_DEF 0x00 -#define HP_CVR_DEF 0x12 -#define HP_ISR_DEF 0x06 -#define HP_IVR_DEF 0x0f -#define HP_NU_DEF 0x00 - -#define HP_IRQM 0x09 - -#define HPR_BLRC 0x08 -#define HPR_SPR1 0x09 -#define HPR_SPR2 0x0A -#define HPR_TCL0 0x0B -#define HPR_TCL1 0x0C -#define HPR_TCL2 0x0D -#define HPR_TCL3 0x0E -#define HPR_TCL4 0x0F - -#define HPICR_INIT 0x80 -#define HPICR_HM1 0x40 -#define HPICR_HM0 0x20 -#define HPICR_HF1 0x10 -#define HPICR_HF0 0x08 -#define HPICR_TREQ 0x02 -#define HPICR_RREQ 0x01 - -#define HPCVR_HC 0x80 - -#define HPISR_HREQ 0x80 -#define HPISR_DMA 0x40 -#define HPISR_HF3 0x10 -#define HPISR_HF2 0x08 -#define HPISR_TRDY 0x04 -#define HPISR_TXDE 0x02 -#define HPISR_RXDF 0x01 - -#define HPIO_290 0 -#define HPIO_260 1 -#define HPIO_250 2 -#define HPIO_240 3 -#define HPIO_230 4 -#define HPIO_220 5 -#define HPIO_210 6 -#define HPIO_3E0 7 - -#define HPMEM_NONE 0 -#define HPMEM_B000 1 -#define HPMEM_C800 2 -#define HPMEM_D000 3 -#define HPMEM_D400 4 -#define HPMEM_D800 5 -#define HPMEM_E000 6 -#define HPMEM_E800 7 - -#define HPIRQ_NONE 0 -#define HPIRQ_5 1 -#define HPIRQ_7 2 -#define HPIRQ_9 3 -#define HPIRQ_10 4 -#define HPIRQ_11 5 -#define HPIRQ_12 6 -#define HPIRQ_15 7 - -#define HIMT_PLAY_DONE 0x00 -#define HIMT_RECORD_DONE 0x01 -#define HIMT_MIDI_EOS 0x02 -#define HIMT_MIDI_OUT 0x03 - -#define HIMT_MIDI_IN_UCHAR 0x0E -#define HIMT_DSP 0x0F - -#define HDEX_BASE 0x92 -#define HDEX_PLAY_START (0 + HDEX_BASE) -#define HDEX_PLAY_STOP (1 + HDEX_BASE) -#define HDEX_PLAY_PAUSE (2 + HDEX_BASE) -#define HDEX_PLAY_RESUME (3 + HDEX_BASE) -#define HDEX_RECORD_START (4 + HDEX_BASE) -#define HDEX_RECORD_STOP (5 + HDEX_BASE) -#define HDEX_MIDI_IN_START (6 + HDEX_BASE) -#define HDEX_MIDI_IN_STOP (7 + HDEX_BASE) -#define HDEX_MIDI_OUT_START (8 + HDEX_BASE) -#define HDEX_MIDI_OUT_STOP (9 + HDEX_BASE) -#define HDEX_AUX_REQ (10 + HDEX_BASE) - -#define HIWORD(l) ((WORD)((((DWORD)(l)) >> 16) & 0xFFFF)) -#define LOWORD(l) ((WORD)(DWORD)(l)) -#define HIBYTE(w) ((BYTE)(((WORD)(w) >> 8) & 0xFF)) -#define LOBYTE(w) ((BYTE)(w)) -#define MAKELONG(low,hi) ((long)(((WORD)(low))|(((DWORD)((WORD)(hi)))<<16))) -#define MAKEWORD(low,hi) ((WORD)(((BYTE)(low))|(((WORD)((BYTE)(hi)))<<8))) - -#define PCTODSP_OFFSET(w) (USHORT)((w)/2) -#define PCTODSP_BASED(w) (USHORT)(((w)/2) + DSP_BASE_ADDR) -#define DSPTOPC_BASED(w) (((w) - DSP_BASE_ADDR) * 2) - -#ifdef SLOWIO -#define msnd_outb outb_p -#define msnd_inb inb_p -#else -#define msnd_outb outb -#define msnd_inb inb -#endif - -/* JobQueueStruct */ -#define JQS_wStart 0x00 -#define JQS_wSize 0x02 -#define JQS_wHead 0x04 -#define JQS_wTail 0x06 -#define JQS__size 0x08 - -/* DAQueueDataStruct */ -#define DAQDS_wStart 0x00 -#define DAQDS_wSize 0x02 -#define DAQDS_wFormat 0x04 -#define DAQDS_wSampleSize 0x06 -#define DAQDS_wChannels 0x08 -#define DAQDS_wSampleRate 0x0A -#define DAQDS_wIntMsg 0x0C -#define DAQDS_wFlags 0x0E -#define DAQDS__size 0x10 - -typedef u8 BYTE; -typedef u16 USHORT; -typedef u16 WORD; -typedef u32 DWORD; -typedef void __iomem * LPDAQD; - -/* Generic FIFO */ -typedef struct { - size_t n, len; - char *data; - int head, tail; -} msnd_fifo; - -typedef struct multisound_dev { - /* Linux device info */ - char *name; - int dsp_minor, mixer_minor; - int ext_midi_dev, hdr_midi_dev; - - /* Hardware resources */ - int io, numio; - int memid, irqid; - int irq, irq_ref; - unsigned char info; - void __iomem *base; - - /* Motorola 56k DSP SMA */ - void __iomem *SMA; - void __iomem *DAPQ, *DARQ, *MODQ, *MIDQ, *DSPQ; - void __iomem *pwDSPQData, *pwMIDQData, *pwMODQData; - int dspq_data_buff, dspq_buff_size; - - /* State variables */ - enum { msndClassic, msndPinnacle } type; - fmode_t mode; - unsigned long flags; -#define F_RESETTING 0 -#define F_HAVEDIGITAL 1 -#define F_AUDIO_WRITE_INUSE 2 -#define F_WRITING 3 -#define F_WRITEBLOCK 4 -#define F_WRITEFLUSH 5 -#define F_AUDIO_READ_INUSE 6 -#define F_READING 7 -#define F_READBLOCK 8 -#define F_EXT_MIDI_INUSE 9 -#define F_HDR_MIDI_INUSE 10 -#define F_DISABLE_WRITE_NDELAY 11 - wait_queue_head_t writeblock; - wait_queue_head_t readblock; - wait_queue_head_t writeflush; - spinlock_t lock; - int nresets; - unsigned long recsrc; - int left_levels[32]; - int right_levels[32]; - int mixer_mod_count; - int calibrate_signal; - int play_sample_size, play_sample_rate, play_channels; - int play_ndelay; - int rec_sample_size, rec_sample_rate, rec_channels; - int rec_ndelay; - BYTE bCurrentMidiPatch; - - /* Digital audio FIFOs */ - msnd_fifo DAPF, DARF; - int fifosize; - int last_playbank, last_recbank; - - /* MIDI in callback */ - void (*midi_in_interrupt)(struct multisound_dev *); -} multisound_dev_t; - -#ifndef mdelay -# define mdelay(a) udelay((a) * 1000) -#endif - -int msnd_register(multisound_dev_t *dev); -void msnd_unregister(multisound_dev_t *dev); - -void msnd_init_queue(void __iomem *, int start, int size); - -void msnd_fifo_init(msnd_fifo *f); -void msnd_fifo_free(msnd_fifo *f); -int msnd_fifo_alloc(msnd_fifo *f, size_t n); -void msnd_fifo_make_empty(msnd_fifo *f); -int msnd_fifo_write_io(msnd_fifo *f, char __iomem *buf, size_t len); -int msnd_fifo_read_io(msnd_fifo *f, char __iomem *buf, size_t len); -int msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len); -int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len); - -int msnd_send_dsp_cmd(multisound_dev_t *dev, BYTE cmd); -int msnd_send_word(multisound_dev_t *dev, unsigned char high, - unsigned char mid, unsigned char low); -int msnd_upload_host(multisound_dev_t *dev, char *bin, int len); -int msnd_enable_irq(multisound_dev_t *dev); -int msnd_disable_irq(multisound_dev_t *dev); - -#endif /* __MSND_H */ diff --git a/sound/oss/msnd_classic.c b/sound/oss/msnd_classic.c deleted file mode 100644 index 3b23a096fa4e..000000000000 --- a/sound/oss/msnd_classic.c +++ /dev/null @@ -1,3 +0,0 @@ -/* The work is in msnd_pinnacle.c, just define MSND_CLASSIC before it. */ -#define MSND_CLASSIC -#include "msnd_pinnacle.c" diff --git a/sound/oss/msnd_classic.h b/sound/oss/msnd_classic.h deleted file mode 100644 index 1a17dde2f650..000000000000 --- a/sound/oss/msnd_classic.h +++ /dev/null @@ -1,185 +0,0 @@ -/********************************************************************* - * - * msnd_classic.h - * - * Turtle Beach MultiSound Sound Card Driver for Linux - * - * Some parts of this header file were derived from the Turtle Beach - * MultiSound Driver Development Kit. - * - * Copyright (C) 1998 Andrew Veliath - * Copyright (C) 1993 Turtle Beach Systems, Inc. - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - * - ********************************************************************/ -#ifndef __MSND_CLASSIC_H -#define __MSND_CLASSIC_H - - -#define DSP_NUMIO 0x10 - -#define HP_MEMM 0x08 - -#define HP_BITM 0x0E -#define HP_WAIT 0x0D -#define HP_DSPR 0x0A -#define HP_PROR 0x0B -#define HP_BLKS 0x0C - -#define HPPRORESET_OFF 0 -#define HPPRORESET_ON 1 - -#define HPDSPRESET_OFF 0 -#define HPDSPRESET_ON 1 - -#define HPBLKSEL_0 0 -#define HPBLKSEL_1 1 - -#define HPWAITSTATE_0 0 -#define HPWAITSTATE_1 1 - -#define HPBITMODE_16 0 -#define HPBITMODE_8 1 - -#define HIDSP_INT_PLAY_UNDER 0x00 -#define HIDSP_INT_RECORD_OVER 0x01 -#define HIDSP_INPUT_CLIPPING 0x02 -#define HIDSP_MIDI_IN_OVER 0x10 -#define HIDSP_MIDI_OVERRUN_ERR 0x13 - -#define HDEXAR_CLEAR_PEAKS 1 -#define HDEXAR_IN_SET_POTS 2 -#define HDEXAR_AUX_SET_POTS 3 -#define HDEXAR_CAL_A_TO_D 4 -#define HDEXAR_RD_EXT_DSP_BITS 5 - -#define TIME_PRO_RESET_DONE 0x028A -#define TIME_PRO_SYSEX 0x0040 -#define TIME_PRO_RESET 0x0032 - -#define AGND 0x01 -#define SIGNAL 0x02 - -#define EXT_DSP_BIT_DCAL 0x0001 -#define EXT_DSP_BIT_MIDI_CON 0x0002 - -#define BUFFSIZE 0x8000 -#define HOSTQ_SIZE 0x40 - -#define SRAM_CNTL_START 0x7F00 -#define SMA_STRUCT_START 0x7F40 - -#define DAP_BUFF_SIZE 0x2400 -#define DAR_BUFF_SIZE 0x2000 - -#define DAPQ_STRUCT_SIZE 0x10 -#define DARQ_STRUCT_SIZE 0x10 -#define DAPQ_BUFF_SIZE (3 * 0x10) -#define DARQ_BUFF_SIZE (3 * 0x10) -#define MODQ_BUFF_SIZE 0x400 -#define MIDQ_BUFF_SIZE 0x200 -#define DSPQ_BUFF_SIZE 0x40 - -#define DAPQ_DATA_BUFF 0x6C00 -#define DARQ_DATA_BUFF 0x6C30 -#define MODQ_DATA_BUFF 0x6C60 -#define MIDQ_DATA_BUFF 0x7060 -#define DSPQ_DATA_BUFF 0x7260 - -#define DAPQ_OFFSET SRAM_CNTL_START -#define DARQ_OFFSET (SRAM_CNTL_START + 0x08) -#define MODQ_OFFSET (SRAM_CNTL_START + 0x10) -#define MIDQ_OFFSET (SRAM_CNTL_START + 0x18) -#define DSPQ_OFFSET (SRAM_CNTL_START + 0x20) - -#define MOP_SYNTH 0x10 -#define MOP_EXTOUT 0x32 -#define MOP_EXTTHRU 0x02 -#define MOP_OUTMASK 0x01 - -#define MIP_EXTIN 0x01 -#define MIP_SYNTH 0x00 -#define MIP_INMASK 0x32 - -/* Classic SMA Common Data */ -#define SMA_wCurrPlayBytes 0x0000 -#define SMA_wCurrRecordBytes 0x0002 -#define SMA_wCurrPlayVolLeft 0x0004 -#define SMA_wCurrPlayVolRight 0x0006 -#define SMA_wCurrInVolLeft 0x0008 -#define SMA_wCurrInVolRight 0x000a -#define SMA_wUser_3 0x000c -#define SMA_wUser_4 0x000e -#define SMA_dwUser_5 0x0010 -#define SMA_dwUser_6 0x0014 -#define SMA_wUser_7 0x0018 -#define SMA_wReserved_A 0x001a -#define SMA_wReserved_B 0x001c -#define SMA_wReserved_C 0x001e -#define SMA_wReserved_D 0x0020 -#define SMA_wReserved_E 0x0022 -#define SMA_wReserved_F 0x0024 -#define SMA_wReserved_G 0x0026 -#define SMA_wReserved_H 0x0028 -#define SMA_wCurrDSPStatusFlags 0x002a -#define SMA_wCurrHostStatusFlags 0x002c -#define SMA_wCurrInputTagBits 0x002e -#define SMA_wCurrLeftPeak 0x0030 -#define SMA_wCurrRightPeak 0x0032 -#define SMA_wExtDSPbits 0x0034 -#define SMA_bExtHostbits 0x0036 -#define SMA_bBoardLevel 0x0037 -#define SMA_bInPotPosRight 0x0038 -#define SMA_bInPotPosLeft 0x0039 -#define SMA_bAuxPotPosRight 0x003a -#define SMA_bAuxPotPosLeft 0x003b -#define SMA_wCurrMastVolLeft 0x003c -#define SMA_wCurrMastVolRight 0x003e -#define SMA_bUser_12 0x0040 -#define SMA_bUser_13 0x0041 -#define SMA_wUser_14 0x0042 -#define SMA_wUser_15 0x0044 -#define SMA_wCalFreqAtoD 0x0046 -#define SMA_wUser_16 0x0048 -#define SMA_wUser_17 0x004a -#define SMA__size 0x004c - -#ifdef HAVE_DSPCODEH -# include "msndperm.c" -# include "msndinit.c" -# define PERMCODE msndperm -# define INITCODE msndinit -# define PERMCODESIZE sizeof(msndperm) -# define INITCODESIZE sizeof(msndinit) -#else -# ifndef CONFIG_MSNDCLAS_INIT_FILE -# define CONFIG_MSNDCLAS_INIT_FILE \ - "/etc/sound/msndinit.bin" -# endif -# ifndef CONFIG_MSNDCLAS_PERM_FILE -# define CONFIG_MSNDCLAS_PERM_FILE \ - "/etc/sound/msndperm.bin" -# endif -# define PERMCODEFILE CONFIG_MSNDCLAS_PERM_FILE -# define INITCODEFILE CONFIG_MSNDCLAS_INIT_FILE -# define PERMCODE dspini -# define INITCODE permini -# define PERMCODESIZE sizeof_dspini -# define INITCODESIZE sizeof_permini -#endif -#define LONGNAME "MultiSound (Classic/Monterey/Tahiti)" - -#endif /* __MSND_CLASSIC_H */ diff --git a/sound/oss/msnd_pinnacle.c b/sound/oss/msnd_pinnacle.c deleted file mode 100644 index d2abc2cf3213..000000000000 --- a/sound/oss/msnd_pinnacle.c +++ /dev/null @@ -1,1941 +0,0 @@ -/********************************************************************* - * - * Turtle Beach MultiSound Sound Card Driver for Linux - * Linux 2.0/2.2 Version - * - * msnd_pinnacle.c / msnd_classic.c - * - * -- If MSND_CLASSIC is defined: - * - * -> driver for Turtle Beach Classic/Monterey/Tahiti - * - * -- Else - * - * -> driver for Turtle Beach Pinnacle/Fiji - * - * Copyright (C) 1998 Andrew Veliath - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * 12-3-2000 Modified IO port validation Steve Sycamore - * - ********************************************************************/ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/types.h> -#include <linux/delay.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/mutex.h> -#include <linux/gfp.h> -#include <linux/sched/signal.h> - -#include <asm/irq.h> -#include <asm/io.h> -#include "sound_config.h" -#include "sound_firmware.h" -#ifdef MSND_CLASSIC -# ifndef __alpha__ -# define SLOWIO -# endif -#endif -#include "msnd.h" -#ifdef MSND_CLASSIC -# ifdef CONFIG_MSNDCLAS_HAVE_BOOT -# define HAVE_DSPCODEH -# endif -# include "msnd_classic.h" -# define LOGNAME "msnd_classic" -#else -# ifdef CONFIG_MSNDPIN_HAVE_BOOT -# define HAVE_DSPCODEH -# endif -# include "msnd_pinnacle.h" -# define LOGNAME "msnd_pinnacle" -#endif - -#ifndef CONFIG_MSND_WRITE_NDELAY -# define CONFIG_MSND_WRITE_NDELAY 1 -#endif - -#define get_play_delay_jiffies(size) ((size) * HZ * \ - dev.play_sample_size / 8 / \ - dev.play_sample_rate / \ - dev.play_channels) - -#define get_rec_delay_jiffies(size) ((size) * HZ * \ - dev.rec_sample_size / 8 / \ - dev.rec_sample_rate / \ - dev.rec_channels) - -static DEFINE_MUTEX(msnd_pinnacle_mutex); -static multisound_dev_t dev; - -#ifndef HAVE_DSPCODEH -static char *dspini, *permini; -static int sizeof_dspini, sizeof_permini; -#endif - -static int dsp_full_reset(void); -static void dsp_write_flush(void); - -static __inline__ int chk_send_dsp_cmd(multisound_dev_t *dev, register BYTE cmd) -{ - if (msnd_send_dsp_cmd(dev, cmd) == 0) - return 0; - dsp_full_reset(); - return msnd_send_dsp_cmd(dev, cmd); -} - -static void reset_play_queue(void) -{ - int n; - LPDAQD lpDAQ; - - dev.last_playbank = -1; - writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DAPQ + JQS_wHead); - writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DAPQ + JQS_wTail); - - for (n = 0, lpDAQ = dev.base + DAPQ_DATA_BUFF; n < 3; ++n, lpDAQ += DAQDS__size) { - writew(PCTODSP_BASED((DWORD)(DAP_BUFF_SIZE * n)), lpDAQ + DAQDS_wStart); - writew(0, lpDAQ + DAQDS_wSize); - writew(1, lpDAQ + DAQDS_wFormat); - writew(dev.play_sample_size, lpDAQ + DAQDS_wSampleSize); - writew(dev.play_channels, lpDAQ + DAQDS_wChannels); - writew(dev.play_sample_rate, lpDAQ + DAQDS_wSampleRate); - writew(HIMT_PLAY_DONE * 0x100 + n, lpDAQ + DAQDS_wIntMsg); - writew(n, lpDAQ + DAQDS_wFlags); - } -} - -static void reset_record_queue(void) -{ - int n; - LPDAQD lpDAQ; - unsigned long flags; - - dev.last_recbank = 2; - writew(PCTODSP_OFFSET(0 * DAQDS__size), dev.DARQ + JQS_wHead); - writew(PCTODSP_OFFSET(dev.last_recbank * DAQDS__size), dev.DARQ + JQS_wTail); - - /* Critical section: bank 1 access */ - spin_lock_irqsave(&dev.lock, flags); - msnd_outb(HPBLKSEL_1, dev.io + HP_BLKS); - memset_io(dev.base, 0, DAR_BUFF_SIZE * 3); - msnd_outb(HPBLKSEL_0, dev.io + HP_BLKS); - spin_unlock_irqrestore(&dev.lock, flags); - - for (n = 0, lpDAQ = dev.base + DARQ_DATA_BUFF; n < 3; ++n, lpDAQ += DAQDS__size) { - writew(PCTODSP_BASED((DWORD)(DAR_BUFF_SIZE * n)) + 0x4000, lpDAQ + DAQDS_wStart); - writew(DAR_BUFF_SIZE, lpDAQ + DAQDS_wSize); - writew(1, lpDAQ + DAQDS_wFormat); - writew(dev.rec_sample_size, lpDAQ + DAQDS_wSampleSize); - writew(dev.rec_channels, lpDAQ + DAQDS_wChannels); - writew(dev.rec_sample_rate, lpDAQ + DAQDS_wSampleRate); - writew(HIMT_RECORD_DONE * 0x100 + n, lpDAQ + DAQDS_wIntMsg); - writew(n, lpDAQ + DAQDS_wFlags); - } -} - -static void reset_queues(void) -{ - if (dev.mode & FMODE_WRITE) { - msnd_fifo_make_empty(&dev.DAPF); - reset_play_queue(); - } - if (dev.mode & FMODE_READ) { - msnd_fifo_make_empty(&dev.DARF); - reset_record_queue(); - } -} - -static int dsp_set_format(struct file *file, int val) -{ - int data, i; - LPDAQD lpDAQ, lpDARQ; - - lpDAQ = dev.base + DAPQ_DATA_BUFF; - lpDARQ = dev.base + DARQ_DATA_BUFF; - - switch (val) { - case AFMT_U8: - case AFMT_S16_LE: - data = val; - break; - default: - data = DEFSAMPLESIZE; - break; - } - - for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) { - if (file->f_mode & FMODE_WRITE) - writew(data, lpDAQ + DAQDS_wSampleSize); - if (file->f_mode & FMODE_READ) - writew(data, lpDARQ + DAQDS_wSampleSize); - } - if (file->f_mode & FMODE_WRITE) - dev.play_sample_size = data; - if (file->f_mode & FMODE_READ) - dev.rec_sample_size = data; - - return data; -} - -static int dsp_get_frag_size(void) -{ - int size; - size = dev.fifosize / 4; - if (size > 32 * 1024) - size = 32 * 1024; - return size; -} - -static int dsp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - int val, i, data, tmp; - LPDAQD lpDAQ, lpDARQ; - audio_buf_info abinfo; - unsigned long flags; - int __user *p = (int __user *)arg; - - lpDAQ = dev.base + DAPQ_DATA_BUFF; - lpDARQ = dev.base + DARQ_DATA_BUFF; - - switch (cmd) { - case SNDCTL_DSP_SUBDIVIDE: - case SNDCTL_DSP_SETFRAGMENT: - case SNDCTL_DSP_SETDUPLEX: - case SNDCTL_DSP_POST: - return 0; - - case SNDCTL_DSP_GETIPTR: - case SNDCTL_DSP_GETOPTR: - case SNDCTL_DSP_MAPINBUF: - case SNDCTL_DSP_MAPOUTBUF: - return -EINVAL; - - case SNDCTL_DSP_GETOSPACE: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - spin_lock_irqsave(&dev.lock, flags); - abinfo.fragsize = dsp_get_frag_size(); - abinfo.bytes = dev.DAPF.n - dev.DAPF.len; - abinfo.fragstotal = dev.DAPF.n / abinfo.fragsize; - abinfo.fragments = abinfo.bytes / abinfo.fragsize; - spin_unlock_irqrestore(&dev.lock, flags); - return copy_to_user((void __user *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETISPACE: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - spin_lock_irqsave(&dev.lock, flags); - abinfo.fragsize = dsp_get_frag_size(); - abinfo.bytes = dev.DARF.n - dev.DARF.len; - abinfo.fragstotal = dev.DARF.n / abinfo.fragsize; - abinfo.fragments = abinfo.bytes / abinfo.fragsize; - spin_unlock_irqrestore(&dev.lock, flags); - return copy_to_user((void __user *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_RESET: - dev.nresets = 0; - reset_queues(); - return 0; - - case SNDCTL_DSP_SYNC: - dsp_write_flush(); - return 0; - - case SNDCTL_DSP_GETBLKSIZE: - tmp = dsp_get_frag_size(); - if (put_user(tmp, p)) - return -EFAULT; - return 0; - - case SNDCTL_DSP_GETFMTS: - val = AFMT_S16_LE | AFMT_U8; - if (put_user(val, p)) - return -EFAULT; - return 0; - - case SNDCTL_DSP_SETFMT: - if (get_user(val, p)) - return -EFAULT; - - if (file->f_mode & FMODE_WRITE) - data = val == AFMT_QUERY - ? dev.play_sample_size - : dsp_set_format(file, val); - else - data = val == AFMT_QUERY - ? dev.rec_sample_size - : dsp_set_format(file, val); - - if (put_user(data, p)) - return -EFAULT; - return 0; - - case SNDCTL_DSP_NONBLOCK: - if (!test_bit(F_DISABLE_WRITE_NDELAY, &dev.flags) && - file->f_mode & FMODE_WRITE) - dev.play_ndelay = 1; - if (file->f_mode & FMODE_READ) - dev.rec_ndelay = 1; - return 0; - - case SNDCTL_DSP_GETCAPS: - val = DSP_CAP_DUPLEX | DSP_CAP_BATCH; - if (put_user(val, p)) - return -EFAULT; - return 0; - - case SNDCTL_DSP_SPEED: - if (get_user(val, p)) - return -EFAULT; - - if (val < 8000) - val = 8000; - - if (val > 48000) - val = 48000; - - data = val; - - for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) { - if (file->f_mode & FMODE_WRITE) - writew(data, lpDAQ + DAQDS_wSampleRate); - if (file->f_mode & FMODE_READ) - writew(data, lpDARQ + DAQDS_wSampleRate); - } - if (file->f_mode & FMODE_WRITE) - dev.play_sample_rate = data; - if (file->f_mode & FMODE_READ) - dev.rec_sample_rate = data; - - if (put_user(data, p)) - return -EFAULT; - return 0; - - case SNDCTL_DSP_CHANNELS: - case SNDCTL_DSP_STEREO: - if (get_user(val, p)) - return -EFAULT; - - if (cmd == SNDCTL_DSP_CHANNELS) { - switch (val) { - case 1: - case 2: - data = val; - break; - default: - val = data = 2; - break; - } - } else { - switch (val) { - case 0: - data = 1; - break; - default: - val = 1; - case 1: - data = 2; - break; - } - } - - for (i = 0; i < 3; ++i, lpDAQ += DAQDS__size, lpDARQ += DAQDS__size) { - if (file->f_mode & FMODE_WRITE) - writew(data, lpDAQ + DAQDS_wChannels); - if (file->f_mode & FMODE_READ) - writew(data, lpDARQ + DAQDS_wChannels); - } - if (file->f_mode & FMODE_WRITE) - dev.play_channels = data; - if (file->f_mode & FMODE_READ) - dev.rec_channels = data; - - if (put_user(val, p)) - return -EFAULT; - return 0; - } - - return -EINVAL; -} - -static int mixer_get(int d) -{ - if (d > 31) - return -EINVAL; - - switch (d) { - case SOUND_MIXER_VOLUME: - case SOUND_MIXER_PCM: - case SOUND_MIXER_LINE: - case SOUND_MIXER_IMIX: - case SOUND_MIXER_LINE1: -#ifndef MSND_CLASSIC - case SOUND_MIXER_MIC: - case SOUND_MIXER_SYNTH: -#endif - return (dev.left_levels[d] >> 8) * 100 / 0xff | - (((dev.right_levels[d] >> 8) * 100 / 0xff) << 8); - default: - return 0; - } -} - -#define update_volm(a,b) \ - writew((dev.left_levels[a] >> 1) * \ - readw(dev.SMA + SMA_wCurrMastVolLeft) / 0xffff, \ - dev.SMA + SMA_##b##Left); \ - writew((dev.right_levels[a] >> 1) * \ - readw(dev.SMA + SMA_wCurrMastVolRight) / 0xffff, \ - dev.SMA + SMA_##b##Right); - -#define update_potm(d,s,ar) \ - writeb((dev.left_levels[d] >> 8) * \ - readw(dev.SMA + SMA_wCurrMastVolLeft) / 0xffff, \ - dev.SMA + SMA_##s##Left); \ - writeb((dev.right_levels[d] >> 8) * \ - readw(dev.SMA + SMA_wCurrMastVolRight) / 0xffff, \ - dev.SMA + SMA_##s##Right); \ - if (msnd_send_word(&dev, 0, 0, ar) == 0) \ - chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); - -#define update_pot(d,s,ar) \ - writeb(dev.left_levels[d] >> 8, \ - dev.SMA + SMA_##s##Left); \ - writeb(dev.right_levels[d] >> 8, \ - dev.SMA + SMA_##s##Right); \ - if (msnd_send_word(&dev, 0, 0, ar) == 0) \ - chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); - -static int mixer_set(int d, int value) -{ - int left = value & 0x000000ff; - int right = (value & 0x0000ff00) >> 8; - int bLeft, bRight; - int wLeft, wRight; - int updatemaster = 0; - - if (d > 31) - return -EINVAL; - - bLeft = left * 0xff / 100; - wLeft = left * 0xffff / 100; - - bRight = right * 0xff / 100; - wRight = right * 0xffff / 100; - - dev.left_levels[d] = wLeft; - dev.right_levels[d] = wRight; - - switch (d) { - /* master volume unscaled controls */ - case SOUND_MIXER_LINE: /* line pot control */ - /* scaled by IMIX in digital mix */ - writeb(bLeft, dev.SMA + SMA_bInPotPosLeft); - writeb(bRight, dev.SMA + SMA_bInPotPosRight); - if (msnd_send_word(&dev, 0, 0, HDEXAR_IN_SET_POTS) == 0) - chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); - break; -#ifndef MSND_CLASSIC - case SOUND_MIXER_MIC: /* mic pot control */ - /* scaled by IMIX in digital mix */ - writeb(bLeft, dev.SMA + SMA_bMicPotPosLeft); - writeb(bRight, dev.SMA + SMA_bMicPotPosRight); - if (msnd_send_word(&dev, 0, 0, HDEXAR_MIC_SET_POTS) == 0) - chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); - break; -#endif - case SOUND_MIXER_VOLUME: /* master volume */ - writew(wLeft, dev.SMA + SMA_wCurrMastVolLeft); - writew(wRight, dev.SMA + SMA_wCurrMastVolRight); - /* fall through */ - - case SOUND_MIXER_LINE1: /* aux pot control */ - /* scaled by master volume */ - /* fall through */ - - /* digital controls */ - case SOUND_MIXER_SYNTH: /* synth vol (dsp mix) */ - case SOUND_MIXER_PCM: /* pcm vol (dsp mix) */ - case SOUND_MIXER_IMIX: /* input monitor (dsp mix) */ - /* scaled by master volume */ - updatemaster = 1; - break; - - default: - return 0; - } - - if (updatemaster) { - /* update master volume scaled controls */ - update_volm(SOUND_MIXER_PCM, wCurrPlayVol); - update_volm(SOUND_MIXER_IMIX, wCurrInVol); -#ifndef MSND_CLASSIC - update_volm(SOUND_MIXER_SYNTH, wCurrMHdrVol); -#endif - update_potm(SOUND_MIXER_LINE1, bAuxPotPos, HDEXAR_AUX_SET_POTS); - } - - return mixer_get(d); -} - -static void mixer_setup(void) -{ - update_pot(SOUND_MIXER_LINE, bInPotPos, HDEXAR_IN_SET_POTS); - update_potm(SOUND_MIXER_LINE1, bAuxPotPos, HDEXAR_AUX_SET_POTS); - update_volm(SOUND_MIXER_PCM, wCurrPlayVol); - update_volm(SOUND_MIXER_IMIX, wCurrInVol); -#ifndef MSND_CLASSIC - update_pot(SOUND_MIXER_MIC, bMicPotPos, HDEXAR_MIC_SET_POTS); - update_volm(SOUND_MIXER_SYNTH, wCurrMHdrVol); -#endif -} - -static unsigned long set_recsrc(unsigned long recsrc) -{ - if (dev.recsrc == recsrc) - return dev.recsrc; -#ifdef HAVE_NORECSRC - else if (recsrc == 0) - dev.recsrc = 0; -#endif - else - dev.recsrc ^= recsrc; - -#ifndef MSND_CLASSIC - if (dev.recsrc & SOUND_MASK_IMIX) { - if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_ANA_IN) == 0) - chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); - } - else if (dev.recsrc & SOUND_MASK_SYNTH) { - if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_SYNTH_IN) == 0) - chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); - } - else if ((dev.recsrc & SOUND_MASK_DIGITAL1) && test_bit(F_HAVEDIGITAL, &dev.flags)) { - if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_DAT_IN) == 0) - chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); - } - else { -#ifdef HAVE_NORECSRC - /* Select no input (?) */ - dev.recsrc = 0; -#else - dev.recsrc = SOUND_MASK_IMIX; - if (msnd_send_word(&dev, 0, 0, HDEXAR_SET_ANA_IN) == 0) - chk_send_dsp_cmd(&dev, HDEX_AUX_REQ); -#endif - } -#endif /* MSND_CLASSIC */ - - return dev.recsrc; -} - -static unsigned long force_recsrc(unsigned long recsrc) -{ - dev.recsrc = 0; - return set_recsrc(recsrc); -} - -#define set_mixer_info() \ - memset(&info, 0, sizeof(info)); \ - strlcpy(info.id, "MSNDMIXER", sizeof(info.id)); \ - strlcpy(info.name, "MultiSound Mixer", sizeof(info.name)); - -static int mixer_ioctl(unsigned int cmd, unsigned long arg) -{ - if (cmd == SOUND_MIXER_INFO) { - mixer_info info; - set_mixer_info(); - info.modify_counter = dev.mixer_mod_count; - if (copy_to_user((void __user *)arg, &info, sizeof(info))) - return -EFAULT; - return 0; - } else if (cmd == SOUND_OLD_MIXER_INFO) { - _old_mixer_info info; - set_mixer_info(); - if (copy_to_user((void __user *)arg, &info, sizeof(info))) - return -EFAULT; - return 0; - } else if (cmd == SOUND_MIXER_PRIVATE1) { - dev.nresets = 0; - dsp_full_reset(); - return 0; - } else if (((cmd >> 8) & 0xff) == 'M') { - int val = 0; - - if (_SIOC_DIR(cmd) & _SIOC_WRITE) { - switch (cmd & 0xff) { - case SOUND_MIXER_RECSRC: - if (get_user(val, (int __user *)arg)) - return -EFAULT; - val = set_recsrc(val); - break; - - default: - if (get_user(val, (int __user *)arg)) - return -EFAULT; - val = mixer_set(cmd & 0xff, val); - break; - } - ++dev.mixer_mod_count; - return put_user(val, (int __user *)arg); - } else { - switch (cmd & 0xff) { - case SOUND_MIXER_RECSRC: - val = dev.recsrc; - break; - - case SOUND_MIXER_DEVMASK: - case SOUND_MIXER_STEREODEVS: - val = SOUND_MASK_PCM | - SOUND_MASK_LINE | - SOUND_MASK_IMIX | - SOUND_MASK_LINE1 | -#ifndef MSND_CLASSIC - SOUND_MASK_MIC | - SOUND_MASK_SYNTH | -#endif - SOUND_MASK_VOLUME; - break; - - case SOUND_MIXER_RECMASK: -#ifdef MSND_CLASSIC - val = 0; -#else - val = SOUND_MASK_IMIX | - SOUND_MASK_SYNTH; - if (test_bit(F_HAVEDIGITAL, &dev.flags)) - val |= SOUND_MASK_DIGITAL1; -#endif - break; - - case SOUND_MIXER_CAPS: - val = SOUND_CAP_EXCL_INPUT; - break; - - default: - if ((val = mixer_get(cmd & 0xff)) < 0) - return -EINVAL; - break; - } - } - - return put_user(val, (int __user *)arg); - } - - return -EINVAL; -} - -static long dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - int minor = iminor(file_inode(file)); - int ret; - - if (cmd == OSS_GETVERSION) { - int sound_version = SOUND_VERSION; - return put_user(sound_version, (int __user *)arg); - } - - ret = -EINVAL; - - mutex_lock(&msnd_pinnacle_mutex); - if (minor == dev.dsp_minor) - ret = dsp_ioctl(file, cmd, arg); - else if (minor == dev.mixer_minor) - ret = mixer_ioctl(cmd, arg); - mutex_unlock(&msnd_pinnacle_mutex); - - return ret; -} - -static void dsp_write_flush(void) -{ - int timeout = get_play_delay_jiffies(dev.DAPF.len); - - if (!(dev.mode & FMODE_WRITE) || !test_bit(F_WRITING, &dev.flags)) - return; - set_bit(F_WRITEFLUSH, &dev.flags); - wait_event_interruptible_timeout( - dev.writeflush, - !test_bit(F_WRITEFLUSH, &dev.flags), - timeout); - clear_bit(F_WRITEFLUSH, &dev.flags); - if (!signal_pending(current)) { - __set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(get_play_delay_jiffies(DAP_BUFF_SIZE)); - } - clear_bit(F_WRITING, &dev.flags); -} - -static void dsp_halt(struct file *file) -{ - if ((file ? file->f_mode : dev.mode) & FMODE_READ) { - clear_bit(F_READING, &dev.flags); - chk_send_dsp_cmd(&dev, HDEX_RECORD_STOP); - msnd_disable_irq(&dev); - if (file) { - printk(KERN_DEBUG LOGNAME ": Stopping read for %p\n", file); - dev.mode &= ~FMODE_READ; - } - clear_bit(F_AUDIO_READ_INUSE, &dev.flags); - } - if ((file ? file->f_mode : dev.mode) & FMODE_WRITE) { - if (test_bit(F_WRITING, &dev.flags)) { - dsp_write_flush(); - chk_send_dsp_cmd(&dev, HDEX_PLAY_STOP); - } - msnd_disable_irq(&dev); - if (file) { - printk(KERN_DEBUG LOGNAME ": Stopping write for %p\n", file); - dev.mode &= ~FMODE_WRITE; - } - clear_bit(F_AUDIO_WRITE_INUSE, &dev.flags); - } -} - -static int dsp_release(struct file *file) -{ - dsp_halt(file); - return 0; -} - -static int dsp_open(struct file *file) -{ - if ((file ? file->f_mode : dev.mode) & FMODE_WRITE) { - set_bit(F_AUDIO_WRITE_INUSE, &dev.flags); - clear_bit(F_WRITING, &dev.flags); - msnd_fifo_make_empty(&dev.DAPF); - reset_play_queue(); - if (file) { - printk(KERN_DEBUG LOGNAME ": Starting write for %p\n", file); - dev.mode |= FMODE_WRITE; - } - msnd_enable_irq(&dev); - } - if ((file ? file->f_mode : dev.mode) & FMODE_READ) { - set_bit(F_AUDIO_READ_INUSE, &dev.flags); - clear_bit(F_READING, &dev.flags); - msnd_fifo_make_empty(&dev.DARF); - reset_record_queue(); - if (file) { - printk(KERN_DEBUG LOGNAME ": Starting read for %p\n", file); - dev.mode |= FMODE_READ; - } - msnd_enable_irq(&dev); - } - return 0; -} - -static void set_default_play_audio_parameters(void) -{ - dev.play_sample_size = DEFSAMPLESIZE; - dev.play_sample_rate = DEFSAMPLERATE; - dev.play_channels = DEFCHANNELS; -} - -static void set_default_rec_audio_parameters(void) -{ - dev.rec_sample_size = DEFSAMPLESIZE; - dev.rec_sample_rate = DEFSAMPLERATE; - dev.rec_channels = DEFCHANNELS; -} - -static void set_default_audio_parameters(void) -{ - set_default_play_audio_parameters(); - set_default_rec_audio_parameters(); -} - -static int dev_open(struct inode *inode, struct file *file) -{ - int minor = iminor(inode); - int err = 0; - - mutex_lock(&msnd_pinnacle_mutex); - if (minor == dev.dsp_minor) { - if ((file->f_mode & FMODE_WRITE && - test_bit(F_AUDIO_WRITE_INUSE, &dev.flags)) || - (file->f_mode & FMODE_READ && - test_bit(F_AUDIO_READ_INUSE, &dev.flags))) { - err = -EBUSY; - goto out; - } - - if ((err = dsp_open(file)) >= 0) { - dev.nresets = 0; - if (file->f_mode & FMODE_WRITE) { - set_default_play_audio_parameters(); - if (!test_bit(F_DISABLE_WRITE_NDELAY, &dev.flags)) - dev.play_ndelay = (file->f_flags & O_NDELAY) ? 1 : 0; - else - dev.play_ndelay = 0; - } - if (file->f_mode & FMODE_READ) { - set_default_rec_audio_parameters(); - dev.rec_ndelay = (file->f_flags & O_NDELAY) ? 1 : 0; - } - } - } - else if (minor == dev.mixer_minor) { - /* nothing */ - } else - err = -EINVAL; -out: - mutex_unlock(&msnd_pinnacle_mutex); - return err; -} - -static int dev_release(struct inode *inode, struct file *file) -{ - int minor = iminor(inode); - int err = 0; - - mutex_lock(&msnd_pinnacle_mutex); - if (minor == dev.dsp_minor) - err = dsp_release(file); - else if (minor == dev.mixer_minor) { - /* nothing */ - } else - err = -EINVAL; - mutex_unlock(&msnd_pinnacle_mutex); - return err; -} - -static __inline__ int pack_DARQ_to_DARF(register int bank) -{ - register int size, timeout = 3; - register WORD wTmp; - LPDAQD DAQD; - - /* Increment the tail and check for queue wrap */ - wTmp = readw(dev.DARQ + JQS_wTail) + PCTODSP_OFFSET(DAQDS__size); - if (wTmp > readw(dev.DARQ + JQS_wSize)) - wTmp = 0; - while (wTmp == readw(dev.DARQ + JQS_wHead) && timeout--) - udelay(1); - writew(wTmp, dev.DARQ + JQS_wTail); - - /* Get our digital audio queue struct */ - DAQD = bank * DAQDS__size + dev.base + DARQ_DATA_BUFF; - - /* Get length of data */ - size = readw(DAQD + DAQDS_wSize); - - /* Read data from the head (unprotected bank 1 access okay - since this is only called inside an interrupt) */ - msnd_outb(HPBLKSEL_1, dev.io + HP_BLKS); - msnd_fifo_write_io( - &dev.DARF, - dev.base + bank * DAR_BUFF_SIZE, - size); - msnd_outb(HPBLKSEL_0, dev.io + HP_BLKS); - - return 1; -} - -static __inline__ int pack_DAPF_to_DAPQ(register int start) -{ - register WORD DAPQ_tail; - register int protect = start, nbanks = 0; - LPDAQD DAQD; - - DAPQ_tail = readw(dev.DAPQ + JQS_wTail); - while (DAPQ_tail != readw(dev.DAPQ + JQS_wHead) || start) { - register int bank_num = DAPQ_tail / PCTODSP_OFFSET(DAQDS__size); - register int n; - unsigned long flags; - - /* Write the data to the new tail */ - if (protect) { - /* Critical section: protect fifo in non-interrupt */ - spin_lock_irqsave(&dev.lock, flags); - n = msnd_fifo_read_io( - &dev.DAPF, - dev.base + bank_num * DAP_BUFF_SIZE, - DAP_BUFF_SIZE); - spin_unlock_irqrestore(&dev.lock, flags); - } else { - n = msnd_fifo_read_io( - &dev.DAPF, - dev.base + bank_num * DAP_BUFF_SIZE, - DAP_BUFF_SIZE); - } - if (!n) - break; - - if (start) - start = 0; - - /* Get our digital audio queue struct */ - DAQD = bank_num * DAQDS__size + dev.base + DAPQ_DATA_BUFF; - - /* Write size of this bank */ - writew(n, DAQD + DAQDS_wSize); - ++nbanks; - - /* Then advance the tail */ - DAPQ_tail = (++bank_num % 3) * PCTODSP_OFFSET(DAQDS__size); - writew(DAPQ_tail, dev.DAPQ + JQS_wTail); - /* Tell the DSP to play the bank */ - msnd_send_dsp_cmd(&dev, HDEX_PLAY_START); - } - return nbanks; -} - -static int dsp_read(char __user *buf, size_t len) -{ - int count = len; - char *page = (char *)__get_free_page(GFP_KERNEL); - int timeout = get_rec_delay_jiffies(DAR_BUFF_SIZE); - - if (!page) - return -ENOMEM; - - while (count > 0) { - int n, k; - unsigned long flags; - - k = PAGE_SIZE; - if (k > count) - k = count; - - /* Critical section: protect fifo in non-interrupt */ - spin_lock_irqsave(&dev.lock, flags); - n = msnd_fifo_read(&dev.DARF, page, k); - spin_unlock_irqrestore(&dev.lock, flags); - if (copy_to_user(buf, page, n)) { - free_page((unsigned long)page); - return -EFAULT; - } - buf += n; - count -= n; - - if (n == k && count) - continue; - - if (!test_bit(F_READING, &dev.flags) && dev.mode & FMODE_READ) { - dev.last_recbank = -1; - if (chk_send_dsp_cmd(&dev, HDEX_RECORD_START) == 0) - set_bit(F_READING, &dev.flags); - } - - if (dev.rec_ndelay) { - free_page((unsigned long)page); - return count == len ? -EAGAIN : len - count; - } - - if (count > 0) { - set_bit(F_READBLOCK, &dev.flags); - if (wait_event_interruptible_timeout( - dev.readblock, - test_bit(F_READBLOCK, &dev.flags), - timeout) <= 0) - clear_bit(F_READING, &dev.flags); - if (signal_pending(current)) { - free_page((unsigned long)page); - return -EINTR; - } - } - } - free_page((unsigned long)page); - return len - count; -} - -static int dsp_write(const char __user *buf, size_t len) -{ - int count = len; - char *page = (char *)__get_free_page(GFP_KERNEL); - int timeout = get_play_delay_jiffies(DAP_BUFF_SIZE); - - if (!page) - return -ENOMEM; - - while (count > 0) { - int n, k; - unsigned long flags; - - k = PAGE_SIZE; - if (k > count) - k = count; - - if (copy_from_user(page, buf, k)) { - free_page((unsigned long)page); - return -EFAULT; - } - - /* Critical section: protect fifo in non-interrupt */ - spin_lock_irqsave(&dev.lock, flags); - n = msnd_fifo_write(&dev.DAPF, page, k); - spin_unlock_irqrestore(&dev.lock, flags); - buf += n; - count -= n; - - if (count && n == k) - continue; - - if (!test_bit(F_WRITING, &dev.flags) && (dev.mode & FMODE_WRITE)) { - dev.last_playbank = -1; - if (pack_DAPF_to_DAPQ(1) > 0) - set_bit(F_WRITING, &dev.flags); - } - - if (dev.play_ndelay) { - free_page((unsigned long)page); - return count == len ? -EAGAIN : len - count; - } - - if (count > 0) { - set_bit(F_WRITEBLOCK, &dev.flags); - wait_event_interruptible_timeout( - dev.writeblock, - test_bit(F_WRITEBLOCK, &dev.flags), - timeout); - if (signal_pending(current)) { - free_page((unsigned long)page); - return -EINTR; - } - } - } - - free_page((unsigned long)page); - return len - count; -} - -static ssize_t dev_read(struct file *file, char __user *buf, size_t count, loff_t *off) -{ - int minor = iminor(file_inode(file)); - if (minor == dev.dsp_minor) - return dsp_read(buf, count); - else - return -EINVAL; -} - -static ssize_t dev_write(struct file *file, const char __user *buf, size_t count, loff_t *off) -{ - int minor = iminor(file_inode(file)); - if (minor == dev.dsp_minor) - return dsp_write(buf, count); - else - return -EINVAL; -} - -static __inline__ void eval_dsp_msg(register WORD wMessage) -{ - switch (HIBYTE(wMessage)) { - case HIMT_PLAY_DONE: - if (dev.last_playbank == LOBYTE(wMessage) || !test_bit(F_WRITING, &dev.flags)) - break; - dev.last_playbank = LOBYTE(wMessage); - - if (pack_DAPF_to_DAPQ(0) <= 0) { - if (!test_bit(F_WRITEBLOCK, &dev.flags)) { - if (test_and_clear_bit(F_WRITEFLUSH, &dev.flags)) - wake_up_interruptible(&dev.writeflush); - } - clear_bit(F_WRITING, &dev.flags); - } - - if (test_and_clear_bit(F_WRITEBLOCK, &dev.flags)) - wake_up_interruptible(&dev.writeblock); - break; - - case HIMT_RECORD_DONE: - if (dev.last_recbank == LOBYTE(wMessage)) - break; - dev.last_recbank = LOBYTE(wMessage); - - pack_DARQ_to_DARF(dev.last_recbank); - - if (test_and_clear_bit(F_READBLOCK, &dev.flags)) - wake_up_interruptible(&dev.readblock); - break; - - case HIMT_DSP: - switch (LOBYTE(wMessage)) { -#ifndef MSND_CLASSIC - case HIDSP_PLAY_UNDER: -#endif - case HIDSP_INT_PLAY_UNDER: -/* printk(KERN_DEBUG LOGNAME ": Play underflow\n"); */ - clear_bit(F_WRITING, &dev.flags); - break; - - case HIDSP_INT_RECORD_OVER: -/* printk(KERN_DEBUG LOGNAME ": Record overflow\n"); */ - clear_bit(F_READING, &dev.flags); - break; - - default: -/* printk(KERN_DEBUG LOGNAME ": DSP message %d 0x%02x\n", - LOBYTE(wMessage), LOBYTE(wMessage)); */ - break; - } - break; - - case HIMT_MIDI_IN_UCHAR: - if (dev.midi_in_interrupt) - (*dev.midi_in_interrupt)(&dev); - break; - - default: -/* printk(KERN_DEBUG LOGNAME ": HIMT message %d 0x%02x\n", HIBYTE(wMessage), HIBYTE(wMessage)); */ - break; - } -} - -static irqreturn_t intr(int irq, void *dev_id) -{ - /* Send ack to DSP */ - msnd_inb(dev.io + HP_RXL); - - /* Evaluate queued DSP messages */ - while (readw(dev.DSPQ + JQS_wTail) != readw(dev.DSPQ + JQS_wHead)) { - register WORD wTmp; - - eval_dsp_msg(readw(dev.pwDSPQData + 2*readw(dev.DSPQ + JQS_wHead))); - - if ((wTmp = readw(dev.DSPQ + JQS_wHead) + 1) > readw(dev.DSPQ + JQS_wSize)) - writew(0, dev.DSPQ + JQS_wHead); - else - writew(wTmp, dev.DSPQ + JQS_wHead); - } - return IRQ_HANDLED; -} - -static const struct file_operations dev_fileops = { - .owner = THIS_MODULE, - .read = dev_read, - .write = dev_write, - .unlocked_ioctl = dev_ioctl, - .open = dev_open, - .release = dev_release, - .llseek = noop_llseek, -}; - -static int reset_dsp(void) -{ - int timeout = 100; - - msnd_outb(HPDSPRESET_ON, dev.io + HP_DSPR); - mdelay(1); -#ifndef MSND_CLASSIC - dev.info = msnd_inb(dev.io + HP_INFO); -#endif - msnd_outb(HPDSPRESET_OFF, dev.io + HP_DSPR); - mdelay(1); - while (timeout-- > 0) { - if (msnd_inb(dev.io + HP_CVR) == HP_CVR_DEF) - return 0; - mdelay(1); - } - printk(KERN_ERR LOGNAME ": Cannot reset DSP\n"); - - return -EIO; -} - -static int __init probe_multisound(void) -{ -#ifndef MSND_CLASSIC - char *xv, *rev = NULL; - char *pin = "Pinnacle", *fiji = "Fiji"; - char *pinfiji = "Pinnacle/Fiji"; -#endif - - if (!request_region(dev.io, dev.numio, "probing")) { - printk(KERN_ERR LOGNAME ": I/O port conflict\n"); - return -ENODEV; - } - - if (reset_dsp() < 0) { - release_region(dev.io, dev.numio); - return -ENODEV; - } - -#ifdef MSND_CLASSIC - dev.name = "Classic/Tahiti/Monterey"; - printk(KERN_INFO LOGNAME ": %s, " -#else - switch (dev.info >> 4) { - case 0xf: xv = "<= 1.15"; break; - case 0x1: xv = "1.18/1.2"; break; - case 0x2: xv = "1.3"; break; - case 0x3: xv = "1.4"; break; - default: xv = "unknown"; break; - } - - switch (dev.info & 0x7) { - case 0x0: rev = "I"; dev.name = pin; break; - case 0x1: rev = "F"; dev.name = pin; break; - case 0x2: rev = "G"; dev.name = pin; break; - case 0x3: rev = "H"; dev.name = pin; break; - case 0x4: rev = "E"; dev.name = fiji; break; - case 0x5: rev = "C"; dev.name = fiji; break; - case 0x6: rev = "D"; dev.name = fiji; break; - case 0x7: - rev = "A-B (Fiji) or A-E (Pinnacle)"; - dev.name = pinfiji; - break; - } - printk(KERN_INFO LOGNAME ": %s revision %s, Xilinx version %s, " -#endif /* MSND_CLASSIC */ - "I/O 0x%x-0x%x, IRQ %d, memory mapped to %p-%p\n", - dev.name, -#ifndef MSND_CLASSIC - rev, xv, -#endif - dev.io, dev.io + dev.numio - 1, - dev.irq, - dev.base, dev.base + 0x7fff); - - release_region(dev.io, dev.numio); - return 0; -} - -static int init_sma(void) -{ - static int initted; - WORD mastVolLeft, mastVolRight; - unsigned long flags; - -#ifdef MSND_CLASSIC - msnd_outb(dev.memid, dev.io + HP_MEMM); -#endif - msnd_outb(HPBLKSEL_0, dev.io + HP_BLKS); - if (initted) { - mastVolLeft = readw(dev.SMA + SMA_wCurrMastVolLeft); - mastVolRight = readw(dev.SMA + SMA_wCurrMastVolRight); - } else - mastVolLeft = mastVolRight = 0; - memset_io(dev.base, 0, 0x8000); - - /* Critical section: bank 1 access */ - spin_lock_irqsave(&dev.lock, flags); - msnd_outb(HPBLKSEL_1, dev.io + HP_BLKS); - memset_io(dev.base, 0, 0x8000); - msnd_outb(HPBLKSEL_0, dev.io + HP_BLKS); - spin_unlock_irqrestore(&dev.lock, flags); - - dev.pwDSPQData = (dev.base + DSPQ_DATA_BUFF); - dev.pwMODQData = (dev.base + MODQ_DATA_BUFF); - dev.pwMIDQData = (dev.base + MIDQ_DATA_BUFF); - - /* Motorola 56k shared memory base */ - dev.SMA = dev.base + SMA_STRUCT_START; - - /* Digital audio play queue */ - dev.DAPQ = dev.base + DAPQ_OFFSET; - msnd_init_queue(dev.DAPQ, DAPQ_DATA_BUFF, DAPQ_BUFF_SIZE); - - /* Digital audio record queue */ - dev.DARQ = dev.base + DARQ_OFFSET; - msnd_init_queue(dev.DARQ, DARQ_DATA_BUFF, DARQ_BUFF_SIZE); - - /* MIDI out queue */ - dev.MODQ = dev.base + MODQ_OFFSET; - msnd_init_queue(dev.MODQ, MODQ_DATA_BUFF, MODQ_BUFF_SIZE); - - /* MIDI in queue */ - dev.MIDQ = dev.base + MIDQ_OFFSET; - msnd_init_queue(dev.MIDQ, MIDQ_DATA_BUFF, MIDQ_BUFF_SIZE); - - /* DSP -> host message queue */ - dev.DSPQ = dev.base + DSPQ_OFFSET; - msnd_init_queue(dev.DSPQ, DSPQ_DATA_BUFF, DSPQ_BUFF_SIZE); - - /* Setup some DSP values */ -#ifndef MSND_CLASSIC - writew(1, dev.SMA + SMA_wCurrPlayFormat); - writew(dev.play_sample_size, dev.SMA + SMA_wCurrPlaySampleSize); - writew(dev.play_channels, dev.SMA + SMA_wCurrPlayChannels); - writew(dev.play_sample_rate, dev.SMA + SMA_wCurrPlaySampleRate); -#endif - writew(dev.play_sample_rate, dev.SMA + SMA_wCalFreqAtoD); - writew(mastVolLeft, dev.SMA + SMA_wCurrMastVolLeft); - writew(mastVolRight, dev.SMA + SMA_wCurrMastVolRight); -#ifndef MSND_CLASSIC - writel(0x00010000, dev.SMA + SMA_dwCurrPlayPitch); - writel(0x00000001, dev.SMA + SMA_dwCurrPlayRate); -#endif - writew(0x303, dev.SMA + SMA_wCurrInputTagBits); - - initted = 1; - - return 0; -} - -static int __init calibrate_adc(WORD srate) -{ - writew(srate, dev.SMA + SMA_wCalFreqAtoD); - if (dev.calibrate_signal == 0) - writew(readw(dev.SMA + SMA_wCurrHostStatusFlags) - | 0x0001, dev.SMA + SMA_wCurrHostStatusFlags); - else - writew(readw(dev.SMA + SMA_wCurrHostStatusFlags) - & ~0x0001, dev.SMA + SMA_wCurrHostStatusFlags); - if (msnd_send_word(&dev, 0, 0, HDEXAR_CAL_A_TO_D) == 0 && - chk_send_dsp_cmd(&dev, HDEX_AUX_REQ) == 0) { - schedule_timeout_interruptible(HZ / 3); - return 0; - } - printk(KERN_WARNING LOGNAME ": ADC calibration failed\n"); - - return -EIO; -} - -static int upload_dsp_code(void) -{ - int ret = 0; - - msnd_outb(HPBLKSEL_0, dev.io + HP_BLKS); -#ifndef HAVE_DSPCODEH - INITCODESIZE = mod_firmware_load(INITCODEFILE, &INITCODE); - if (!INITCODE) { - printk(KERN_ERR LOGNAME ": Error loading " INITCODEFILE); - return -EBUSY; - } - - PERMCODESIZE = mod_firmware_load(PERMCODEFILE, &PERMCODE); - if (!PERMCODE) { - printk(KERN_ERR LOGNAME ": Error loading " PERMCODEFILE); - vfree(INITCODE); - return -EBUSY; - } -#endif - memcpy_toio(dev.base, PERMCODE, PERMCODESIZE); - if (msnd_upload_host(&dev, INITCODE, INITCODESIZE) < 0) { - printk(KERN_WARNING LOGNAME ": Error uploading to DSP\n"); - ret = -ENODEV; - goto out; - } -#ifdef HAVE_DSPCODEH - printk(KERN_INFO LOGNAME ": DSP firmware uploaded (resident)\n"); -#else - printk(KERN_INFO LOGNAME ": DSP firmware uploaded\n"); -#endif - -out: -#ifndef HAVE_DSPCODEH - vfree(INITCODE); - vfree(PERMCODE); -#endif - - return ret; -} - -#ifdef MSND_CLASSIC -static void reset_proteus(void) -{ - msnd_outb(HPPRORESET_ON, dev.io + HP_PROR); - mdelay(TIME_PRO_RESET); - msnd_outb(HPPRORESET_OFF, dev.io + HP_PROR); - mdelay(TIME_PRO_RESET_DONE); -} -#endif - -static int initialize(void) -{ - int err, timeout; - -#ifdef MSND_CLASSIC - msnd_outb(HPWAITSTATE_0, dev.io + HP_WAIT); - msnd_outb(HPBITMODE_16, dev.io + HP_BITM); - - reset_proteus(); -#endif - if ((err = init_sma()) < 0) { - printk(KERN_WARNING LOGNAME ": Cannot initialize SMA\n"); - return err; - } - - if ((err = reset_dsp()) < 0) - return err; - - if ((err = upload_dsp_code()) < 0) { - printk(KERN_WARNING LOGNAME ": Cannot upload DSP code\n"); - return err; - } - - timeout = 200; - while (readw(dev.base)) { - mdelay(1); - if (!timeout--) { - printk(KERN_DEBUG LOGNAME ": DSP reset timeout\n"); - return -EIO; - } - } - - mixer_setup(); - - return 0; -} - -static int dsp_full_reset(void) -{ - int rv; - - if (test_bit(F_RESETTING, &dev.flags) || ++dev.nresets > 10) - return 0; - - set_bit(F_RESETTING, &dev.flags); - printk(KERN_INFO LOGNAME ": DSP reset\n"); - dsp_halt(NULL); /* Unconditionally halt */ - if ((rv = initialize())) - printk(KERN_WARNING LOGNAME ": DSP reset failed\n"); - force_recsrc(dev.recsrc); - dsp_open(NULL); - clear_bit(F_RESETTING, &dev.flags); - - return rv; -} - -static int __init attach_multisound(void) -{ - int err; - - if ((err = request_irq(dev.irq, intr, 0, dev.name, &dev)) < 0) { - printk(KERN_ERR LOGNAME ": Couldn't grab IRQ %d\n", dev.irq); - return err; - } - if (request_region(dev.io, dev.numio, dev.name) == NULL) { - free_irq(dev.irq, &dev); - return -EBUSY; - } - - err = dsp_full_reset(); - if (err < 0) { - release_region(dev.io, dev.numio); - free_irq(dev.irq, &dev); - return err; - } - - if ((err = msnd_register(&dev)) < 0) { - printk(KERN_ERR LOGNAME ": Unable to register MultiSound\n"); - release_region(dev.io, dev.numio); - free_irq(dev.irq, &dev); - return err; - } - - if ((dev.dsp_minor = register_sound_dsp(&dev_fileops, -1)) < 0) { - printk(KERN_ERR LOGNAME ": Unable to register DSP operations\n"); - msnd_unregister(&dev); - release_region(dev.io, dev.numio); - free_irq(dev.irq, &dev); - return dev.dsp_minor; - } - - if ((dev.mixer_minor = register_sound_mixer(&dev_fileops, -1)) < 0) { - printk(KERN_ERR LOGNAME ": Unable to register mixer operations\n"); - unregister_sound_mixer(dev.mixer_minor); - msnd_unregister(&dev); - release_region(dev.io, dev.numio); - free_irq(dev.irq, &dev); - return dev.mixer_minor; - } - - dev.ext_midi_dev = dev.hdr_midi_dev = -1; - - disable_irq(dev.irq); - calibrate_adc(dev.play_sample_rate); -#ifndef MSND_CLASSIC - force_recsrc(SOUND_MASK_IMIX); -#endif - - return 0; -} - -static void __exit unload_multisound(void) -{ - release_region(dev.io, dev.numio); - free_irq(dev.irq, &dev); - unregister_sound_mixer(dev.mixer_minor); - unregister_sound_dsp(dev.dsp_minor); - msnd_unregister(&dev); -} - -#ifndef MSND_CLASSIC - -/* Pinnacle/Fiji Logical Device Configuration */ - -static int __init msnd_write_cfg(int cfg, int reg, int value) -{ - msnd_outb(reg, cfg); - msnd_outb(value, cfg + 1); - if (value != msnd_inb(cfg + 1)) { - printk(KERN_ERR LOGNAME ": msnd_write_cfg: I/O error\n"); - return -EIO; - } - return 0; -} - -static int __init msnd_write_cfg_io0(int cfg, int num, WORD io) -{ - if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) - return -EIO; - if (msnd_write_cfg(cfg, IREG_IO0_BASEHI, HIBYTE(io))) - return -EIO; - if (msnd_write_cfg(cfg, IREG_IO0_BASELO, LOBYTE(io))) - return -EIO; - return 0; -} - -static int __init msnd_write_cfg_io1(int cfg, int num, WORD io) -{ - if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) - return -EIO; - if (msnd_write_cfg(cfg, IREG_IO1_BASEHI, HIBYTE(io))) - return -EIO; - if (msnd_write_cfg(cfg, IREG_IO1_BASELO, LOBYTE(io))) - return -EIO; - return 0; -} - -static int __init msnd_write_cfg_irq(int cfg, int num, WORD irq) -{ - if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) - return -EIO; - if (msnd_write_cfg(cfg, IREG_IRQ_NUMBER, LOBYTE(irq))) - return -EIO; - if (msnd_write_cfg(cfg, IREG_IRQ_TYPE, IRQTYPE_EDGE)) - return -EIO; - return 0; -} - -static int __init msnd_write_cfg_mem(int cfg, int num, int mem) -{ - WORD wmem; - - mem >>= 8; - mem &= 0xfff; - wmem = (WORD)mem; - if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) - return -EIO; - if (msnd_write_cfg(cfg, IREG_MEMBASEHI, HIBYTE(wmem))) - return -EIO; - if (msnd_write_cfg(cfg, IREG_MEMBASELO, LOBYTE(wmem))) - return -EIO; - if (wmem && msnd_write_cfg(cfg, IREG_MEMCONTROL, (MEMTYPE_HIADDR | MEMTYPE_16BIT))) - return -EIO; - return 0; -} - -static int __init msnd_activate_logical(int cfg, int num) -{ - if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) - return -EIO; - if (msnd_write_cfg(cfg, IREG_ACTIVATE, LD_ACTIVATE)) - return -EIO; - return 0; -} - -static int __init msnd_write_cfg_logical(int cfg, int num, WORD io0, WORD io1, WORD irq, int mem) -{ - if (msnd_write_cfg(cfg, IREG_LOGDEVICE, num)) - return -EIO; - if (msnd_write_cfg_io0(cfg, num, io0)) - return -EIO; - if (msnd_write_cfg_io1(cfg, num, io1)) - return -EIO; - if (msnd_write_cfg_irq(cfg, num, irq)) - return -EIO; - if (msnd_write_cfg_mem(cfg, num, mem)) - return -EIO; - if (msnd_activate_logical(cfg, num)) - return -EIO; - return 0; -} - -typedef struct msnd_pinnacle_cfg_device { - WORD io0, io1, irq; - int mem; -} msnd_pinnacle_cfg_t[4]; - -static int __init msnd_pinnacle_cfg_devices(int cfg, int reset, msnd_pinnacle_cfg_t device) -{ - int i; - - /* Reset devices if told to */ - if (reset) { - printk(KERN_INFO LOGNAME ": Resetting all devices\n"); - for (i = 0; i < 4; ++i) - if (msnd_write_cfg_logical(cfg, i, 0, 0, 0, 0)) - return -EIO; - } - - /* Configure specified devices */ - for (i = 0; i < 4; ++i) { - - switch (i) { - case 0: /* DSP */ - if (!(device[i].io0 && device[i].irq && device[i].mem)) - continue; - break; - case 1: /* MPU */ - if (!(device[i].io0 && device[i].irq)) - continue; - printk(KERN_INFO LOGNAME - ": Configuring MPU to I/O 0x%x IRQ %d\n", - device[i].io0, device[i].irq); - break; - case 2: /* IDE */ - if (!(device[i].io0 && device[i].io1 && device[i].irq)) - continue; - printk(KERN_INFO LOGNAME - ": Configuring IDE to I/O 0x%x, 0x%x IRQ %d\n", - device[i].io0, device[i].io1, device[i].irq); - break; - case 3: /* Joystick */ - if (!(device[i].io0)) - continue; - printk(KERN_INFO LOGNAME - ": Configuring joystick to I/O 0x%x\n", - device[i].io0); - break; - } - - /* Configure the device */ - if (msnd_write_cfg_logical(cfg, i, device[i].io0, device[i].io1, device[i].irq, device[i].mem)) - return -EIO; - } - - return 0; -} -#endif - -#ifdef MODULE -MODULE_AUTHOR ("Andrew Veliath <andrewtv@usa.net>"); -MODULE_DESCRIPTION ("Turtle Beach " LONGNAME " Linux Driver"); -MODULE_LICENSE("GPL"); - -static int io __initdata = -1; -static int irq __initdata = -1; -static int mem __initdata = -1; -static int write_ndelay __initdata = -1; - -#ifndef MSND_CLASSIC -/* Pinnacle/Fiji non-PnP Config Port */ -static int cfg __initdata = -1; - -/* Extra Peripheral Configuration */ -static int reset __initdata = 0; -static int mpu_io __initdata = 0; -static int mpu_irq __initdata = 0; -static int ide_io0 __initdata = 0; -static int ide_io1 __initdata = 0; -static int ide_irq __initdata = 0; -static int joystick_io __initdata = 0; - -/* If we have the digital daugherboard... */ -static bool digital __initdata = false; -#endif - -static int fifosize __initdata = DEFFIFOSIZE; -static int calibrate_signal __initdata = 0; - -#else /* not a module */ - -static int write_ndelay __initdata = -1; - -#ifdef MSND_CLASSIC -static int io __initdata = CONFIG_MSNDCLAS_IO; -static int irq __initdata = CONFIG_MSNDCLAS_IRQ; -static int mem __initdata = CONFIG_MSNDCLAS_MEM; -#else /* Pinnacle/Fiji */ - -static int io __initdata = CONFIG_MSNDPIN_IO; -static int irq __initdata = CONFIG_MSNDPIN_IRQ; -static int mem __initdata = CONFIG_MSNDPIN_MEM; - -/* Pinnacle/Fiji non-PnP Config Port */ -#ifdef CONFIG_MSNDPIN_NONPNP -# ifndef CONFIG_MSNDPIN_CFG -# define CONFIG_MSNDPIN_CFG 0x250 -# endif -#else -# ifdef CONFIG_MSNDPIN_CFG -# undef CONFIG_MSNDPIN_CFG -# endif -# define CONFIG_MSNDPIN_CFG -1 -#endif -static int cfg __initdata = CONFIG_MSNDPIN_CFG; -/* If not a module, we don't need to bother with reset=1 */ -static int reset; - -/* Extra Peripheral Configuration (Default: Disable) */ -#ifndef CONFIG_MSNDPIN_MPU_IO -# define CONFIG_MSNDPIN_MPU_IO 0 -#endif -static int mpu_io __initdata = CONFIG_MSNDPIN_MPU_IO; - -#ifndef CONFIG_MSNDPIN_MPU_IRQ -# define CONFIG_MSNDPIN_MPU_IRQ 0 -#endif -static int mpu_irq __initdata = CONFIG_MSNDPIN_MPU_IRQ; - -#ifndef CONFIG_MSNDPIN_IDE_IO0 -# define CONFIG_MSNDPIN_IDE_IO0 0 -#endif -static int ide_io0 __initdata = CONFIG_MSNDPIN_IDE_IO0; - -#ifndef CONFIG_MSNDPIN_IDE_IO1 -# define CONFIG_MSNDPIN_IDE_IO1 0 -#endif -static int ide_io1 __initdata = CONFIG_MSNDPIN_IDE_IO1; - -#ifndef CONFIG_MSNDPIN_IDE_IRQ -# define CONFIG_MSNDPIN_IDE_IRQ 0 -#endif -static int ide_irq __initdata = CONFIG_MSNDPIN_IDE_IRQ; - -#ifndef CONFIG_MSNDPIN_JOYSTICK_IO -# define CONFIG_MSNDPIN_JOYSTICK_IO 0 -#endif -static int joystick_io __initdata = CONFIG_MSNDPIN_JOYSTICK_IO; - -/* Have SPDIF (Digital) Daughterboard */ -#ifndef CONFIG_MSNDPIN_DIGITAL -# define CONFIG_MSNDPIN_DIGITAL 0 -#endif -static bool digital __initdata = CONFIG_MSNDPIN_DIGITAL; - -#endif /* MSND_CLASSIC */ - -#ifndef CONFIG_MSND_FIFOSIZE -# define CONFIG_MSND_FIFOSIZE DEFFIFOSIZE -#endif -static int fifosize __initdata = CONFIG_MSND_FIFOSIZE; - -#ifndef CONFIG_MSND_CALSIGNAL -# define CONFIG_MSND_CALSIGNAL 0 -#endif -static int -calibrate_signal __initdata = CONFIG_MSND_CALSIGNAL; -#endif /* MODULE */ - -module_param_hw (io, int, ioport, 0); -module_param_hw (irq, int, irq, 0); -module_param_hw (mem, int, iomem, 0); -module_param (write_ndelay, int, 0); -module_param (fifosize, int, 0); -module_param (calibrate_signal, int, 0); -#ifndef MSND_CLASSIC -module_param (digital, bool, 0); -module_param_hw (cfg, int, ioport, 0); -module_param (reset, int, 0); -module_param_hw (mpu_io, int, ioport, 0); -module_param_hw (mpu_irq, int, irq, 0); -module_param_hw (ide_io0, int, ioport, 0); -module_param_hw (ide_io1, int, ioport, 0); -module_param_hw (ide_irq, int, irq, 0); -module_param_hw (joystick_io, int, ioport, 0); -#endif - -static int __init msnd_init(void) -{ - int err; -#ifndef MSND_CLASSIC - static msnd_pinnacle_cfg_t pinnacle_devs; -#endif /* MSND_CLASSIC */ - - printk(KERN_INFO LOGNAME ": Turtle Beach " LONGNAME " Linux Driver Version " - VERSION ", Copyright (C) 1998 Andrew Veliath\n"); - - if (io == -1 || irq == -1 || mem == -1) - printk(KERN_WARNING LOGNAME ": io, irq and mem must be set\n"); - -#ifdef MSND_CLASSIC - if (io == -1 || - !(io == 0x290 || - io == 0x260 || - io == 0x250 || - io == 0x240 || - io == 0x230 || - io == 0x220 || - io == 0x210 || - io == 0x3e0)) { - printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must be set to 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0x290, or 0x3E0\n"); - return -EINVAL; - } -#else - if (io == -1 || - io < 0x100 || - io > 0x3e0 || - (io % 0x10) != 0) { - printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must within the range 0x100 to 0x3E0 and must be evenly divisible by 0x10\n"); - return -EINVAL; - } -#endif /* MSND_CLASSIC */ - - if (irq == -1 || - !(irq == 5 || - irq == 7 || - irq == 9 || - irq == 10 || - irq == 11 || - irq == 12)) { - printk(KERN_ERR LOGNAME ": \"irq\" - must be set to 5, 7, 9, 10, 11 or 12\n"); - return -EINVAL; - } - - if (mem == -1 || - !(mem == 0xb0000 || - mem == 0xc8000 || - mem == 0xd0000 || - mem == 0xd8000 || - mem == 0xe0000 || - mem == 0xe8000)) { - printk(KERN_ERR LOGNAME ": \"mem\" - must be set to " - "0xb0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000 or 0xe8000\n"); - return -EINVAL; - } - -#ifdef MSND_CLASSIC - switch (irq) { - case 5: dev.irqid = HPIRQ_5; break; - case 7: dev.irqid = HPIRQ_7; break; - case 9: dev.irqid = HPIRQ_9; break; - case 10: dev.irqid = HPIRQ_10; break; - case 11: dev.irqid = HPIRQ_11; break; - case 12: dev.irqid = HPIRQ_12; break; - } - - switch (mem) { - case 0xb0000: dev.memid = HPMEM_B000; break; - case 0xc8000: dev.memid = HPMEM_C800; break; - case 0xd0000: dev.memid = HPMEM_D000; break; - case 0xd8000: dev.memid = HPMEM_D800; break; - case 0xe0000: dev.memid = HPMEM_E000; break; - case 0xe8000: dev.memid = HPMEM_E800; break; - } -#else - if (cfg == -1) { - printk(KERN_INFO LOGNAME ": Assuming PnP mode\n"); - } else if (cfg != 0x250 && cfg != 0x260 && cfg != 0x270) { - printk(KERN_INFO LOGNAME ": Config port must be 0x250, 0x260 or 0x270 (or unspecified for PnP mode)\n"); - return -EINVAL; - } else { - printk(KERN_INFO LOGNAME ": Non-PnP mode: configuring at port 0x%x\n", cfg); - - /* DSP */ - pinnacle_devs[0].io0 = io; - pinnacle_devs[0].irq = irq; - pinnacle_devs[0].mem = mem; - - /* The following are Pinnacle specific */ - - /* MPU */ - pinnacle_devs[1].io0 = mpu_io; - pinnacle_devs[1].irq = mpu_irq; - - /* IDE */ - pinnacle_devs[2].io0 = ide_io0; - pinnacle_devs[2].io1 = ide_io1; - pinnacle_devs[2].irq = ide_irq; - - /* Joystick */ - pinnacle_devs[3].io0 = joystick_io; - - if (!request_region(cfg, 2, "Pinnacle/Fiji Config")) { - printk(KERN_ERR LOGNAME ": Config port 0x%x conflict\n", cfg); - return -EIO; - } - - if (msnd_pinnacle_cfg_devices(cfg, reset, pinnacle_devs)) { - printk(KERN_ERR LOGNAME ": Device configuration error\n"); - release_region(cfg, 2); - return -EIO; - } - release_region(cfg, 2); - } -#endif /* MSND_CLASSIC */ - - if (fifosize < 16) - fifosize = 16; - - if (fifosize > 1024) - fifosize = 1024; - - set_default_audio_parameters(); -#ifdef MSND_CLASSIC - dev.type = msndClassic; -#else - dev.type = msndPinnacle; -#endif - dev.io = io; - dev.numio = DSP_NUMIO; - dev.irq = irq; - dev.base = ioremap(mem, 0x8000); - dev.fifosize = fifosize * 1024; - dev.calibrate_signal = calibrate_signal ? 1 : 0; - dev.recsrc = 0; - dev.dspq_data_buff = DSPQ_DATA_BUFF; - dev.dspq_buff_size = DSPQ_BUFF_SIZE; - if (write_ndelay == -1) - write_ndelay = CONFIG_MSND_WRITE_NDELAY; - if (write_ndelay) - clear_bit(F_DISABLE_WRITE_NDELAY, &dev.flags); - else - set_bit(F_DISABLE_WRITE_NDELAY, &dev.flags); -#ifndef MSND_CLASSIC - if (digital) - set_bit(F_HAVEDIGITAL, &dev.flags); -#endif - init_waitqueue_head(&dev.writeblock); - init_waitqueue_head(&dev.readblock); - init_waitqueue_head(&dev.writeflush); - msnd_fifo_init(&dev.DAPF); - msnd_fifo_init(&dev.DARF); - spin_lock_init(&dev.lock); - printk(KERN_INFO LOGNAME ": %u byte audio FIFOs (x2)\n", dev.fifosize); - if ((err = msnd_fifo_alloc(&dev.DAPF, dev.fifosize)) < 0) { - printk(KERN_ERR LOGNAME ": Couldn't allocate write FIFO\n"); - return err; - } - - if ((err = msnd_fifo_alloc(&dev.DARF, dev.fifosize)) < 0) { - printk(KERN_ERR LOGNAME ": Couldn't allocate read FIFO\n"); - msnd_fifo_free(&dev.DAPF); - return err; - } - - if ((err = probe_multisound()) < 0) { - printk(KERN_ERR LOGNAME ": Probe failed\n"); - msnd_fifo_free(&dev.DAPF); - msnd_fifo_free(&dev.DARF); - return err; - } - - if ((err = attach_multisound()) < 0) { - printk(KERN_ERR LOGNAME ": Attach failed\n"); - msnd_fifo_free(&dev.DAPF); - msnd_fifo_free(&dev.DARF); - return err; - } - - return 0; -} - -static void __exit msdn_cleanup(void) -{ - unload_multisound(); - msnd_fifo_free(&dev.DAPF); - msnd_fifo_free(&dev.DARF); -} - -module_init(msnd_init); -module_exit(msdn_cleanup); diff --git a/sound/oss/msnd_pinnacle.h b/sound/oss/msnd_pinnacle.h deleted file mode 100644 index c18d66cbbe3f..000000000000 --- a/sound/oss/msnd_pinnacle.h +++ /dev/null @@ -1,246 +0,0 @@ -/********************************************************************* - * - * msnd_pinnacle.h - * - * Turtle Beach MultiSound Sound Card Driver for Linux - * - * Some parts of this header file were derived from the Turtle Beach - * MultiSound Driver Development Kit. - * - * Copyright (C) 1998 Andrew Veliath - * Copyright (C) 1993 Turtle Beach Systems, Inc. - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - * - ********************************************************************/ -#ifndef __MSND_PINNACLE_H -#define __MSND_PINNACLE_H - - -#define DSP_NUMIO 0x08 - -#define IREG_LOGDEVICE 0x07 -#define IREG_ACTIVATE 0x30 -#define LD_ACTIVATE 0x01 -#define LD_DISACTIVATE 0x00 -#define IREG_EECONTROL 0x3F -#define IREG_MEMBASEHI 0x40 -#define IREG_MEMBASELO 0x41 -#define IREG_MEMCONTROL 0x42 -#define IREG_MEMRANGEHI 0x43 -#define IREG_MEMRANGELO 0x44 -#define MEMTYPE_8BIT 0x00 -#define MEMTYPE_16BIT 0x02 -#define MEMTYPE_RANGE 0x00 -#define MEMTYPE_HIADDR 0x01 -#define IREG_IO0_BASEHI 0x60 -#define IREG_IO0_BASELO 0x61 -#define IREG_IO1_BASEHI 0x62 -#define IREG_IO1_BASELO 0x63 -#define IREG_IRQ_NUMBER 0x70 -#define IREG_IRQ_TYPE 0x71 -#define IRQTYPE_HIGH 0x02 -#define IRQTYPE_LOW 0x00 -#define IRQTYPE_LEVEL 0x01 -#define IRQTYPE_EDGE 0x00 - -#define HP_DSPR 0x04 -#define HP_BLKS 0x04 - -#define HPDSPRESET_OFF 2 -#define HPDSPRESET_ON 0 - -#define HPBLKSEL_0 2 -#define HPBLKSEL_1 3 - -#define HIMT_DAT_OFF 0x03 - -#define HIDSP_PLAY_UNDER 0x00 -#define HIDSP_INT_PLAY_UNDER 0x01 -#define HIDSP_SSI_TX_UNDER 0x02 -#define HIDSP_RECQ_OVERFLOW 0x08 -#define HIDSP_INT_RECORD_OVER 0x09 -#define HIDSP_SSI_RX_OVERFLOW 0x0a - -#define HIDSP_MIDI_IN_OVER 0x10 - -#define HIDSP_MIDI_FRAME_ERR 0x11 -#define HIDSP_MIDI_PARITY_ERR 0x12 -#define HIDSP_MIDI_OVERRUN_ERR 0x13 - -#define HIDSP_INPUT_CLIPPING 0x20 -#define HIDSP_MIX_CLIPPING 0x30 -#define HIDSP_DAT_IN_OFF 0x21 - -#define HDEXAR_SET_ANA_IN 0 -#define HDEXAR_CLEAR_PEAKS 1 -#define HDEXAR_IN_SET_POTS 2 -#define HDEXAR_AUX_SET_POTS 3 -#define HDEXAR_CAL_A_TO_D 4 -#define HDEXAR_RD_EXT_DSP_BITS 5 - -#define HDEXAR_SET_SYNTH_IN 4 -#define HDEXAR_READ_DAT_IN 5 -#define HDEXAR_MIC_SET_POTS 6 -#define HDEXAR_SET_DAT_IN 7 - -#define HDEXAR_SET_SYNTH_48 8 -#define HDEXAR_SET_SYNTH_44 9 - -#define TIME_PRO_RESET_DONE 0x028A -#define TIME_PRO_SYSEX 0x001E -#define TIME_PRO_RESET 0x0032 - -#define AGND 0x01 -#define SIGNAL 0x02 - -#define EXT_DSP_BIT_DCAL 0x0001 -#define EXT_DSP_BIT_MIDI_CON 0x0002 - -#define BUFFSIZE 0x8000 -#define HOSTQ_SIZE 0x40 - -#define SRAM_CNTL_START 0x7F00 -#define SMA_STRUCT_START 0x7F40 - -#define DAP_BUFF_SIZE 0x2400 -#define DAR_BUFF_SIZE 0x2000 - -#define DAPQ_STRUCT_SIZE 0x10 -#define DARQ_STRUCT_SIZE 0x10 -#define DAPQ_BUFF_SIZE (3 * 0x10) -#define DARQ_BUFF_SIZE (3 * 0x10) -#define MODQ_BUFF_SIZE 0x400 -#define MIDQ_BUFF_SIZE 0x800 -#define DSPQ_BUFF_SIZE 0x5A0 - -#define DAPQ_DATA_BUFF 0x6C00 -#define DARQ_DATA_BUFF 0x6C30 -#define MODQ_DATA_BUFF 0x6C60 -#define MIDQ_DATA_BUFF 0x7060 -#define DSPQ_DATA_BUFF 0x7860 - -#define DAPQ_OFFSET SRAM_CNTL_START -#define DARQ_OFFSET (SRAM_CNTL_START + 0x08) -#define MODQ_OFFSET (SRAM_CNTL_START + 0x10) -#define MIDQ_OFFSET (SRAM_CNTL_START + 0x18) -#define DSPQ_OFFSET (SRAM_CNTL_START + 0x20) - -#define MOP_WAVEHDR 0 -#define MOP_EXTOUT 1 -#define MOP_HWINIT 0xfe -#define MOP_NONE 0xff -#define MOP_MAX 1 - -#define MIP_EXTIN 0 -#define MIP_WAVEHDR 1 -#define MIP_HWINIT 0xfe -#define MIP_MAX 1 - -/* Pinnacle/Fiji SMA Common Data */ -#define SMA_wCurrPlayBytes 0x0000 -#define SMA_wCurrRecordBytes 0x0002 -#define SMA_wCurrPlayVolLeft 0x0004 -#define SMA_wCurrPlayVolRight 0x0006 -#define SMA_wCurrInVolLeft 0x0008 -#define SMA_wCurrInVolRight 0x000a -#define SMA_wCurrMHdrVolLeft 0x000c -#define SMA_wCurrMHdrVolRight 0x000e -#define SMA_dwCurrPlayPitch 0x0010 -#define SMA_dwCurrPlayRate 0x0014 -#define SMA_wCurrMIDIIOPatch 0x0018 -#define SMA_wCurrPlayFormat 0x001a -#define SMA_wCurrPlaySampleSize 0x001c -#define SMA_wCurrPlayChannels 0x001e -#define SMA_wCurrPlaySampleRate 0x0020 -#define SMA_wCurrRecordFormat 0x0022 -#define SMA_wCurrRecordSampleSize 0x0024 -#define SMA_wCurrRecordChannels 0x0026 -#define SMA_wCurrRecordSampleRate 0x0028 -#define SMA_wCurrDSPStatusFlags 0x002a -#define SMA_wCurrHostStatusFlags 0x002c -#define SMA_wCurrInputTagBits 0x002e -#define SMA_wCurrLeftPeak 0x0030 -#define SMA_wCurrRightPeak 0x0032 -#define SMA_bMicPotPosLeft 0x0034 -#define SMA_bMicPotPosRight 0x0035 -#define SMA_bMicPotMaxLeft 0x0036 -#define SMA_bMicPotMaxRight 0x0037 -#define SMA_bInPotPosLeft 0x0038 -#define SMA_bInPotPosRight 0x0039 -#define SMA_bAuxPotPosLeft 0x003a -#define SMA_bAuxPotPosRight 0x003b -#define SMA_bInPotMaxLeft 0x003c -#define SMA_bInPotMaxRight 0x003d -#define SMA_bAuxPotMaxLeft 0x003e -#define SMA_bAuxPotMaxRight 0x003f -#define SMA_bInPotMaxMethod 0x0040 -#define SMA_bAuxPotMaxMethod 0x0041 -#define SMA_wCurrMastVolLeft 0x0042 -#define SMA_wCurrMastVolRight 0x0044 -#define SMA_wCalFreqAtoD 0x0046 -#define SMA_wCurrAuxVolLeft 0x0048 -#define SMA_wCurrAuxVolRight 0x004a -#define SMA_wCurrPlay1VolLeft 0x004c -#define SMA_wCurrPlay1VolRight 0x004e -#define SMA_wCurrPlay2VolLeft 0x0050 -#define SMA_wCurrPlay2VolRight 0x0052 -#define SMA_wCurrPlay3VolLeft 0x0054 -#define SMA_wCurrPlay3VolRight 0x0056 -#define SMA_wCurrPlay4VolLeft 0x0058 -#define SMA_wCurrPlay4VolRight 0x005a -#define SMA_wCurrPlay1PeakLeft 0x005c -#define SMA_wCurrPlay1PeakRight 0x005e -#define SMA_wCurrPlay2PeakLeft 0x0060 -#define SMA_wCurrPlay2PeakRight 0x0062 -#define SMA_wCurrPlay3PeakLeft 0x0064 -#define SMA_wCurrPlay3PeakRight 0x0066 -#define SMA_wCurrPlay4PeakLeft 0x0068 -#define SMA_wCurrPlay4PeakRight 0x006a -#define SMA_wCurrPlayPeakLeft 0x006c -#define SMA_wCurrPlayPeakRight 0x006e -#define SMA_wCurrDATSR 0x0070 -#define SMA_wCurrDATRXCHNL 0x0072 -#define SMA_wCurrDATTXCHNL 0x0074 -#define SMA_wCurrDATRXRate 0x0076 -#define SMA_dwDSPPlayCount 0x0078 -#define SMA__size 0x007c - -#ifdef HAVE_DSPCODEH -# include "pndsperm.c" -# include "pndspini.c" -# define PERMCODE pndsperm -# define INITCODE pndspini -# define PERMCODESIZE sizeof(pndsperm) -# define INITCODESIZE sizeof(pndspini) -#else -# ifndef CONFIG_MSNDPIN_INIT_FILE -# define CONFIG_MSNDPIN_INIT_FILE \ - "/etc/sound/pndspini.bin" -# endif -# ifndef CONFIG_MSNDPIN_PERM_FILE -# define CONFIG_MSNDPIN_PERM_FILE \ - "/etc/sound/pndsperm.bin" -# endif -# define PERMCODEFILE CONFIG_MSNDPIN_PERM_FILE -# define INITCODEFILE CONFIG_MSNDPIN_INIT_FILE -# define PERMCODE dspini -# define INITCODE permini -# define PERMCODESIZE sizeof_dspini -# define INITCODESIZE sizeof_permini -#endif -#define LONGNAME "MultiSound (Pinnacle/Fiji)" - -#endif /* __MSND_PINNACLE_H */ diff --git a/sound/oss/opl3.c b/sound/oss/opl3.c deleted file mode 100644 index f0f5b5be6314..000000000000 --- a/sound/oss/opl3.c +++ /dev/null @@ -1,1255 +0,0 @@ -/* - * sound/oss/opl3.c - * - * A low level driver for Yamaha YM3812 and OPL-3 -chips - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * - * Changes - * Thomas Sailer ioctl code reworked (vmalloc/vfree removed) - * Alan Cox modularisation, fixed sound_mem allocs. - * Christoph Hellwig Adapted to module_init/module_exit - * Arnaldo C. de Melo get rid of check_region, use request_region for - * OPL4, release it on exit, some cleanups. - * - * Status - * Believed to work. Badly needs rewriting a bit to support multiple - * OPL3 devices. - */ - -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/delay.h> - -/* - * Major improvements to the FM handling 30AUG92 by Rob Hooft, - * hooft@chem.ruu.nl - */ - -#include "sound_config.h" - -#include "opl3_hw.h" - -#define MAX_VOICE 18 -#define OFFS_4OP 11 - -struct voice_info -{ - unsigned char keyon_byte; - long bender; - long bender_range; - unsigned long orig_freq; - unsigned long current_freq; - int volume; - int mode; - int panning; /* 0xffff means not set */ -}; - -struct opl_devinfo -{ - int base; - int left_io, right_io; - int nr_voice; - int lv_map[MAX_VOICE]; - - struct voice_info voc[MAX_VOICE]; - struct voice_alloc_info *v_alloc; - struct channel_info *chn_info; - - struct sbi_instrument i_map[SBFM_MAXINSTR]; - struct sbi_instrument *act_i[MAX_VOICE]; - - struct synth_info fm_info; - - int busy; - int model; - unsigned char cmask; - - int is_opl4; -}; - -static struct opl_devinfo *devc = NULL; - -static int detected_model; - -static int store_instr(int instr_no, struct sbi_instrument *instr); -static void freq_to_fnum(int freq, int *block, int *fnum); -static void opl3_command(int io_addr, unsigned int addr, unsigned int val); -static int opl3_kill_note(int dev, int voice, int note, int velocity); - -static void enter_4op_mode(void) -{ - int i; - static int v4op[MAX_VOICE] = { - 0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17 - }; - - devc->cmask = 0x3f; /* Connect all possible 4 OP voice operators */ - opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, 0x3f); - - for (i = 0; i < 3; i++) - pv_map[i].voice_mode = 4; - for (i = 3; i < 6; i++) - pv_map[i].voice_mode = 0; - - for (i = 9; i < 12; i++) - pv_map[i].voice_mode = 4; - for (i = 12; i < 15; i++) - pv_map[i].voice_mode = 0; - - for (i = 0; i < 12; i++) - devc->lv_map[i] = v4op[i]; - devc->v_alloc->max_voice = devc->nr_voice = 12; -} - -static int opl3_ioctl(int dev, unsigned int cmd, void __user * arg) -{ - struct sbi_instrument ins; - - switch (cmd) { - case SNDCTL_FM_LOAD_INSTR: - printk(KERN_WARNING "Warning: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n"); - if (copy_from_user(&ins, arg, sizeof(ins))) - return -EFAULT; - if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) { - printk(KERN_WARNING "FM Error: Invalid instrument number %d\n", ins.channel); - return -EINVAL; - } - return store_instr(ins.channel, &ins); - - case SNDCTL_SYNTH_INFO: - devc->fm_info.nr_voices = (devc->nr_voice == 12) ? 6 : devc->nr_voice; - if (copy_to_user(arg, &devc->fm_info, sizeof(devc->fm_info))) - return -EFAULT; - return 0; - - case SNDCTL_SYNTH_MEMAVL: - return 0x7fffffff; - - case SNDCTL_FM_4OP_ENABLE: - if (devc->model == 2) - enter_4op_mode(); - return 0; - - default: - return -EINVAL; - } -} - -static int opl3_detect(int ioaddr) -{ - /* - * This function returns 1 if the FM chip is present at the given I/O port - * The detection algorithm plays with the timer built in the FM chip and - * looks for a change in the status register. - * - * Note! The timers of the FM chip are not connected to AdLib (and compatible) - * boards. - * - * Note2! The chip is initialized if detected. - */ - - unsigned char stat1, signature; - int i; - - if (devc != NULL) - { - printk(KERN_ERR "opl3: Only one OPL3 supported.\n"); - return 0; - } - - devc = kzalloc(sizeof(*devc), GFP_KERNEL); - - if (devc == NULL) - { - printk(KERN_ERR "opl3: Can't allocate memory for the device control " - "structure \n "); - return 0; - } - - strcpy(devc->fm_info.name, "OPL2"); - - if (!request_region(ioaddr, 4, devc->fm_info.name)) { - printk(KERN_WARNING "opl3: I/O port 0x%x already in use\n", ioaddr); - goto cleanup_devc; - } - - devc->base = ioaddr; - - /* Reset timers 1 and 2 */ - opl3_command(ioaddr, TIMER_CONTROL_REGISTER, TIMER1_MASK | TIMER2_MASK); - - /* Reset the IRQ of the FM chip */ - opl3_command(ioaddr, TIMER_CONTROL_REGISTER, IRQ_RESET); - - signature = stat1 = inb(ioaddr); /* Status register */ - - if (signature != 0x00 && signature != 0x06 && signature != 0x02 && - signature != 0x0f) - { - MDB(printk(KERN_INFO "OPL3 not detected %x\n", signature)); - goto cleanup_region; - } - - if (signature == 0x06) /* OPL2 */ - { - detected_model = 2; - } - else if (signature == 0x00 || signature == 0x0f) /* OPL3 or OPL4 */ - { - unsigned char tmp; - - detected_model = 3; - - /* - * Detect availability of OPL4 (_experimental_). Works probably - * only after a cold boot. In addition the OPL4 port - * of the chip may not be connected to the PC bus at all. - */ - - opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, 0x00); - opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, OPL3_ENABLE | OPL4_ENABLE); - - if ((tmp = inb(ioaddr)) == 0x02) /* Have a OPL4 */ - { - detected_model = 4; - } - - if (request_region(ioaddr - 8, 2, "OPL4")) /* OPL4 port was free */ - { - int tmp; - - outb((0x02), ioaddr - 8); /* Select OPL4 ID register */ - udelay(10); - tmp = inb(ioaddr - 7); /* Read it */ - udelay(10); - - if (tmp == 0x20) /* OPL4 should return 0x20 here */ - { - detected_model = 4; - outb((0xF8), ioaddr - 8); /* Select OPL4 FM mixer control */ - udelay(10); - outb((0x1B), ioaddr - 7); /* Write value */ - udelay(10); - } - else - { /* release OPL4 port */ - release_region(ioaddr - 8, 2); - detected_model = 3; - } - } - opl3_command(ioaddr + 2, OPL3_MODE_REGISTER, 0); - } - for (i = 0; i < 9; i++) - opl3_command(ioaddr, KEYON_BLOCK + i, 0); /* - * Note off - */ - - opl3_command(ioaddr, TEST_REGISTER, ENABLE_WAVE_SELECT); - opl3_command(ioaddr, PERCOSSION_REGISTER, 0x00); /* - * Melodic mode. - */ - return 1; -cleanup_region: - release_region(ioaddr, 4); -cleanup_devc: - kfree(devc); - devc = NULL; - return 0; -} - -static int opl3_kill_note (int devno, int voice, int note, int velocity) -{ - struct physical_voice_info *map; - - if (voice < 0 || voice >= devc->nr_voice) - return 0; - - devc->v_alloc->map[voice] = 0; - - map = &pv_map[devc->lv_map[voice]]; - - if (map->voice_mode == 0) - return 0; - - opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, devc->voc[voice].keyon_byte & ~0x20); - devc->voc[voice].keyon_byte = 0; - devc->voc[voice].bender = 0; - devc->voc[voice].volume = 64; - devc->voc[voice].panning = 0xffff; /* Not set */ - devc->voc[voice].bender_range = 200; - devc->voc[voice].orig_freq = 0; - devc->voc[voice].current_freq = 0; - devc->voc[voice].mode = 0; - return 0; -} - -#define HIHAT 0 -#define CYMBAL 1 -#define TOMTOM 2 -#define SNARE 3 -#define BDRUM 4 -#define UNDEFINED TOMTOM -#define DEFAULT TOMTOM - -static int store_instr(int instr_no, struct sbi_instrument *instr) -{ - if (instr->key != FM_PATCH && (instr->key != OPL3_PATCH || devc->model != 2)) - printk(KERN_WARNING "FM warning: Invalid patch format field (key) 0x%x\n", instr->key); - memcpy((char *) &(devc->i_map[instr_no]), (char *) instr, sizeof(*instr)); - return 0; -} - -static int opl3_set_instr (int dev, int voice, int instr_no) -{ - if (voice < 0 || voice >= devc->nr_voice) - return 0; - if (instr_no < 0 || instr_no >= SBFM_MAXINSTR) - instr_no = 0; /* Acoustic piano (usually) */ - - devc->act_i[voice] = &devc->i_map[instr_no]; - return 0; -} - -/* - * The next table looks magical, but it certainly is not. Its values have - * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception - * for i=0. This log-table converts a linear volume-scaling (0..127) to a - * logarithmic scaling as present in the FM-synthesizer chips. so : Volume - * 64 = 0 db = relative volume 0 and: Volume 32 = -6 db = relative - * volume -8 it was implemented as a table because it is only 128 bytes and - * it saves a lot of log() calculations. (RH) - */ - -static char fm_volume_table[128] = -{ - -64, -48, -40, -35, -32, -29, -27, -26, - -24, -23, -21, -20, -19, -18, -18, -17, - -16, -15, -15, -14, -13, -13, -12, -12, - -11, -11, -10, -10, -10, -9, -9, -8, - -8, -8, -7, -7, -7, -6, -6, -6, - -5, -5, -5, -5, -4, -4, -4, -4, - -3, -3, -3, -3, -2, -2, -2, -2, - -2, -1, -1, -1, -1, 0, 0, 0, - 0, 0, 0, 1, 1, 1, 1, 1, - 1, 2, 2, 2, 2, 2, 2, 2, - 3, 3, 3, 3, 3, 3, 3, 4, - 4, 4, 4, 4, 4, 4, 4, 5, - 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, - 6, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 8, 8, 8, 8, 8 -}; - -static void calc_vol(unsigned char *regbyte, int volume, int main_vol) -{ - int level = (~*regbyte & 0x3f); - - if (main_vol > 127) - main_vol = 127; - volume = (volume * main_vol) / 127; - - if (level) - level += fm_volume_table[volume]; - - if (level > 0x3f) - level = 0x3f; - if (level < 0) - level = 0; - - *regbyte = (*regbyte & 0xc0) | (~level & 0x3f); -} - -static void set_voice_volume(int voice, int volume, int main_vol) -{ - unsigned char vol1, vol2, vol3, vol4; - struct sbi_instrument *instr; - struct physical_voice_info *map; - - if (voice < 0 || voice >= devc->nr_voice) - return; - - map = &pv_map[devc->lv_map[voice]]; - instr = devc->act_i[voice]; - - if (!instr) - instr = &devc->i_map[0]; - - if (instr->channel < 0) - return; - - if (devc->voc[voice].mode == 0) - return; - - if (devc->voc[voice].mode == 2) - { - vol1 = instr->operators[2]; - vol2 = instr->operators[3]; - if ((instr->operators[10] & 0x01)) - { - calc_vol(&vol1, volume, main_vol); - calc_vol(&vol2, volume, main_vol); - } - else - { - calc_vol(&vol2, volume, main_vol); - } - opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], vol1); - opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], vol2); - } - else - { /* - * 4 OP voice - */ - int connection; - - vol1 = instr->operators[2]; - vol2 = instr->operators[3]; - vol3 = instr->operators[OFFS_4OP + 2]; - vol4 = instr->operators[OFFS_4OP + 3]; - - /* - * The connection method for 4 OP devc->voc is defined by the rightmost - * bits at the offsets 10 and 10+OFFS_4OP - */ - - connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01); - - switch (connection) - { - case 0: - calc_vol(&vol4, volume, main_vol); - break; - - case 1: - calc_vol(&vol2, volume, main_vol); - calc_vol(&vol4, volume, main_vol); - break; - - case 2: - calc_vol(&vol1, volume, main_vol); - calc_vol(&vol4, volume, main_vol); - break; - - case 3: - calc_vol(&vol1, volume, main_vol); - calc_vol(&vol3, volume, main_vol); - calc_vol(&vol4, volume, main_vol); - break; - - default: - ; - } - opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], vol1); - opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], vol2); - opl3_command(map->ioaddr, KSL_LEVEL + map->op[2], vol3); - opl3_command(map->ioaddr, KSL_LEVEL + map->op[3], vol4); - } -} - -static int opl3_start_note (int dev, int voice, int note, int volume) -{ - unsigned char data, fpc; - int block, fnum, freq, voice_mode, pan; - struct sbi_instrument *instr; - struct physical_voice_info *map; - - if (voice < 0 || voice >= devc->nr_voice) - return 0; - - map = &pv_map[devc->lv_map[voice]]; - pan = devc->voc[voice].panning; - - if (map->voice_mode == 0) - return 0; - - if (note == 255) /* - * Just change the volume - */ - { - set_voice_volume(voice, volume, devc->voc[voice].volume); - return 0; - } - - /* - * Kill previous note before playing - */ - - opl3_command(map->ioaddr, KSL_LEVEL + map->op[1], 0xff); /* - * Carrier - * volume to - * min - */ - opl3_command(map->ioaddr, KSL_LEVEL + map->op[0], 0xff); /* - * Modulator - * volume to - */ - - if (map->voice_mode == 4) - { - opl3_command(map->ioaddr, KSL_LEVEL + map->op[2], 0xff); - opl3_command(map->ioaddr, KSL_LEVEL + map->op[3], 0xff); - } - - opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, 0x00); /* - * Note - * off - */ - - instr = devc->act_i[voice]; - - if (!instr) - instr = &devc->i_map[0]; - - if (instr->channel < 0) - { - printk(KERN_WARNING "opl3: Initializing voice %d with undefined instrument\n", voice); - return 0; - } - - if (map->voice_mode == 2 && instr->key == OPL3_PATCH) - return 0; /* - * Cannot play - */ - - voice_mode = map->voice_mode; - - if (voice_mode == 4) - { - int voice_shift; - - voice_shift = (map->ioaddr == devc->left_io) ? 0 : 3; - voice_shift += map->voice_num; - - if (instr->key != OPL3_PATCH) /* - * Just 2 OP patch - */ - { - voice_mode = 2; - devc->cmask &= ~(1 << voice_shift); - } - else - { - devc->cmask |= (1 << voice_shift); - } - - opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask); - } - - /* - * Set Sound Characteristics - */ - - opl3_command(map->ioaddr, AM_VIB + map->op[0], instr->operators[0]); - opl3_command(map->ioaddr, AM_VIB + map->op[1], instr->operators[1]); - - /* - * Set Attack/Decay - */ - - opl3_command(map->ioaddr, ATTACK_DECAY + map->op[0], instr->operators[4]); - opl3_command(map->ioaddr, ATTACK_DECAY + map->op[1], instr->operators[5]); - - /* - * Set Sustain/Release - */ - - opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[0], instr->operators[6]); - opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[1], instr->operators[7]); - - /* - * Set Wave Select - */ - - opl3_command(map->ioaddr, WAVE_SELECT + map->op[0], instr->operators[8]); - opl3_command(map->ioaddr, WAVE_SELECT + map->op[1], instr->operators[9]); - - /* - * Set Feedback/Connection - */ - - fpc = instr->operators[10]; - - if (pan != 0xffff) - { - fpc &= ~STEREO_BITS; - if (pan < -64) - fpc |= VOICE_TO_LEFT; - else - if (pan > 64) - fpc |= VOICE_TO_RIGHT; - else - fpc |= (VOICE_TO_LEFT | VOICE_TO_RIGHT); - } - - if (!(fpc & 0x30)) - fpc |= 0x30; /* - * Ensure that at least one chn is enabled - */ - opl3_command(map->ioaddr, FEEDBACK_CONNECTION + map->voice_num, fpc); - - /* - * If the voice is a 4 OP one, initialize the operators 3 and 4 also - */ - - if (voice_mode == 4) - { - /* - * Set Sound Characteristics - */ - - opl3_command(map->ioaddr, AM_VIB + map->op[2], instr->operators[OFFS_4OP + 0]); - opl3_command(map->ioaddr, AM_VIB + map->op[3], instr->operators[OFFS_4OP + 1]); - - /* - * Set Attack/Decay - */ - - opl3_command(map->ioaddr, ATTACK_DECAY + map->op[2], instr->operators[OFFS_4OP + 4]); - opl3_command(map->ioaddr, ATTACK_DECAY + map->op[3], instr->operators[OFFS_4OP + 5]); - - /* - * Set Sustain/Release - */ - - opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[2], instr->operators[OFFS_4OP + 6]); - opl3_command(map->ioaddr, SUSTAIN_RELEASE + map->op[3], instr->operators[OFFS_4OP + 7]); - - /* - * Set Wave Select - */ - - opl3_command(map->ioaddr, WAVE_SELECT + map->op[2], instr->operators[OFFS_4OP + 8]); - opl3_command(map->ioaddr, WAVE_SELECT + map->op[3], instr->operators[OFFS_4OP + 9]); - - /* - * Set Feedback/Connection - */ - - fpc = instr->operators[OFFS_4OP + 10]; - if (!(fpc & 0x30)) - fpc |= 0x30; /* - * Ensure that at least one chn is enabled - */ - opl3_command(map->ioaddr, FEEDBACK_CONNECTION + map->voice_num + 3, fpc); - } - - devc->voc[voice].mode = voice_mode; - set_voice_volume(voice, volume, devc->voc[voice].volume); - - freq = devc->voc[voice].orig_freq = note_to_freq(note) / 1000; - - /* - * Since the pitch bender may have been set before playing the note, we - * have to calculate the bending now. - */ - - freq = compute_finetune(devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range, 0); - devc->voc[voice].current_freq = freq; - - freq_to_fnum(freq, &block, &fnum); - - /* - * Play note - */ - - data = fnum & 0xff; /* - * Least significant bits of fnumber - */ - opl3_command(map->ioaddr, FNUM_LOW + map->voice_num, data); - - data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); - devc->voc[voice].keyon_byte = data; - opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, data); - if (voice_mode == 4) - opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num + 3, data); - - return 0; -} - -static void freq_to_fnum (int freq, int *block, int *fnum) -{ - int f, octave; - - /* - * Converts the note frequency to block and fnum values for the FM chip - */ - /* - * First try to compute the block -value (octave) where the note belongs - */ - - f = freq; - - octave = 5; - - if (f == 0) - octave = 0; - else if (f < 261) - { - while (f < 261) - { - octave--; - f <<= 1; - } - } - else if (f > 493) - { - while (f > 493) - { - octave++; - f >>= 1; - } - } - - if (octave > 7) - octave = 7; - - *fnum = freq * (1 << (20 - octave)) / 49716; - *block = octave; -} - -static void opl3_command (int io_addr, unsigned int addr, unsigned int val) -{ - int i; - - /* - * The original 2-OP synth requires a quite long delay after writing to a - * register. The OPL-3 survives with just two INBs - */ - - outb(((unsigned char) (addr & 0xff)), io_addr); - - if (devc->model != 2) - udelay(10); - else - for (i = 0; i < 2; i++) - inb(io_addr); - - outb(((unsigned char) (val & 0xff)), io_addr + 1); - - if (devc->model != 2) - udelay(30); - else - for (i = 0; i < 2; i++) - inb(io_addr); -} - -static void opl3_reset(int devno) -{ - int i; - - for (i = 0; i < 18; i++) - devc->lv_map[i] = i; - - for (i = 0; i < devc->nr_voice; i++) - { - opl3_command(pv_map[devc->lv_map[i]].ioaddr, - KSL_LEVEL + pv_map[devc->lv_map[i]].op[0], 0xff); - - opl3_command(pv_map[devc->lv_map[i]].ioaddr, - KSL_LEVEL + pv_map[devc->lv_map[i]].op[1], 0xff); - - if (pv_map[devc->lv_map[i]].voice_mode == 4) - { - opl3_command(pv_map[devc->lv_map[i]].ioaddr, - KSL_LEVEL + pv_map[devc->lv_map[i]].op[2], 0xff); - - opl3_command(pv_map[devc->lv_map[i]].ioaddr, - KSL_LEVEL + pv_map[devc->lv_map[i]].op[3], 0xff); - } - - opl3_kill_note(devno, i, 0, 64); - } - - if (devc->model == 2) - { - devc->v_alloc->max_voice = devc->nr_voice = 18; - - for (i = 0; i < 18; i++) - pv_map[i].voice_mode = 2; - - } -} - -static int opl3_open(int dev, int mode) -{ - int i; - - if (devc->busy) - return -EBUSY; - devc->busy = 1; - - devc->v_alloc->max_voice = devc->nr_voice = (devc->model == 2) ? 18 : 9; - devc->v_alloc->timestamp = 0; - - for (i = 0; i < 18; i++) - { - devc->v_alloc->map[i] = 0; - devc->v_alloc->alloc_times[i] = 0; - } - - devc->cmask = 0x00; /* - * Just 2 OP mode - */ - if (devc->model == 2) - opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, devc->cmask); - return 0; -} - -static void opl3_close(int dev) -{ - devc->busy = 0; - devc->v_alloc->max_voice = devc->nr_voice = (devc->model == 2) ? 18 : 9; - - devc->fm_info.nr_drums = 0; - devc->fm_info.perc_mode = 0; - - opl3_reset(dev); -} - -static void opl3_hw_control(int dev, unsigned char *event) -{ -} - -static int opl3_load_patch(int dev, int format, const char __user *addr, - int count, int pmgr_flag) -{ - struct sbi_instrument ins; - - if (count <sizeof(ins)) - { - printk(KERN_WARNING "FM Error: Patch record too short\n"); - return -EINVAL; - } - - if (copy_from_user(&ins, addr, sizeof(ins))) - return -EFAULT; - - if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) - { - printk(KERN_WARNING "FM Error: Invalid instrument number %d\n", ins.channel); - return -EINVAL; - } - ins.key = format; - - return store_instr(ins.channel, &ins); -} - -static void opl3_panning(int dev, int voice, int value) -{ - - if (voice < 0 || voice >= devc->nr_voice) - return; - - devc->voc[voice].panning = value; -} - -static void opl3_volume_method(int dev, int mode) -{ -} - -#define SET_VIBRATO(cell) { \ - tmp = instr->operators[(cell-1)+(((cell-1)/2)*OFFS_4OP)]; \ - if (pressure > 110) \ - tmp |= 0x40; /* Vibrato on */ \ - opl3_command (map->ioaddr, AM_VIB + map->op[cell-1], tmp);} - -static void opl3_aftertouch(int dev, int voice, int pressure) -{ - int tmp; - struct sbi_instrument *instr; - struct physical_voice_info *map; - - if (voice < 0 || voice >= devc->nr_voice) - return; - - map = &pv_map[devc->lv_map[voice]]; - - if (map->voice_mode == 0) - return; - - /* - * Adjust the amount of vibrato depending the pressure - */ - - instr = devc->act_i[voice]; - - if (!instr) - instr = &devc->i_map[0]; - - if (devc->voc[voice].mode == 4) - { - int connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01); - - switch (connection) - { - case 0: - SET_VIBRATO(4); - break; - - case 1: - SET_VIBRATO(2); - SET_VIBRATO(4); - break; - - case 2: - SET_VIBRATO(1); - SET_VIBRATO(4); - break; - - case 3: - SET_VIBRATO(1); - SET_VIBRATO(3); - SET_VIBRATO(4); - break; - - } - /* - * Not implemented yet - */ - } - else - { - SET_VIBRATO(1); - - if ((instr->operators[10] & 0x01)) /* - * Additive synthesis - */ - SET_VIBRATO(2); - } -} - -#undef SET_VIBRATO - -static void bend_pitch(int dev, int voice, int value) -{ - unsigned char data; - int block, fnum, freq; - struct physical_voice_info *map; - - map = &pv_map[devc->lv_map[voice]]; - - if (map->voice_mode == 0) - return; - - devc->voc[voice].bender = value; - if (!value) - return; - if (!(devc->voc[voice].keyon_byte & 0x20)) - return; /* - * Not keyed on - */ - - freq = compute_finetune(devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range, 0); - devc->voc[voice].current_freq = freq; - - freq_to_fnum(freq, &block, &fnum); - - data = fnum & 0xff; /* - * Least significant bits of fnumber - */ - opl3_command(map->ioaddr, FNUM_LOW + map->voice_num, data); - - data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); - devc->voc[voice].keyon_byte = data; - opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, data); -} - -static void opl3_controller (int dev, int voice, int ctrl_num, int value) -{ - if (voice < 0 || voice >= devc->nr_voice) - return; - - switch (ctrl_num) - { - case CTRL_PITCH_BENDER: - bend_pitch(dev, voice, value); - break; - - case CTRL_PITCH_BENDER_RANGE: - devc->voc[voice].bender_range = value; - break; - - case CTL_MAIN_VOLUME: - devc->voc[voice].volume = value / 128; - break; - - case CTL_PAN: - devc->voc[voice].panning = (value * 2) - 128; - break; - } -} - -static void opl3_bender(int dev, int voice, int value) -{ - if (voice < 0 || voice >= devc->nr_voice) - return; - - bend_pitch(dev, voice, value - 8192); -} - -static int opl3_alloc_voice(int dev, int chn, int note, struct voice_alloc_info *alloc) -{ - int i, p, best, first, avail, best_time = 0x7fffffff; - struct sbi_instrument *instr; - int is4op; - int instr_no; - - if (chn < 0 || chn > 15) - instr_no = 0; - else - instr_no = devc->chn_info[chn].pgm_num; - - instr = &devc->i_map[instr_no]; - if (instr->channel < 0 || /* Instrument not loaded */ - devc->nr_voice != 12) /* Not in 4 OP mode */ - is4op = 0; - else if (devc->nr_voice == 12) /* 4 OP mode */ - is4op = (instr->key == OPL3_PATCH); - else - is4op = 0; - - if (is4op) - { - first = p = 0; - avail = 6; - } - else - { - if (devc->nr_voice == 12) /* 4 OP mode. Use the '2 OP only' operators first */ - first = p = 6; - else - first = p = 0; - avail = devc->nr_voice; - } - - /* - * Now try to find a free voice - */ - best = first; - - for (i = 0; i < avail; i++) - { - if (alloc->map[p] == 0) - { - return p; - } - if (alloc->alloc_times[p] < best_time) /* Find oldest playing note */ - { - best_time = alloc->alloc_times[p]; - best = p; - } - p = (p + 1) % avail; - } - - /* - * Insert some kind of priority mechanism here. - */ - - if (best < 0) - best = 0; - if (best > devc->nr_voice) - best -= devc->nr_voice; - - return best; /* All devc->voc in use. Select the first one. */ -} - -static void opl3_setup_voice(int dev, int voice, int chn) -{ - struct channel_info *info; - - if (voice < 0 || voice >= devc->nr_voice) - return; - - if (chn < 0 || chn > 15) - return; - - info = &synth_devs[dev]->chn_info[chn]; - - opl3_set_instr(dev, voice, info->pgm_num); - - devc->voc[voice].bender = 0; - devc->voc[voice].bender_range = info->bender_range; - devc->voc[voice].volume = info->controllers[CTL_MAIN_VOLUME]; - devc->voc[voice].panning = (info->controllers[CTL_PAN] * 2) - 128; -} - -static struct synth_operations opl3_operations = -{ - .owner = THIS_MODULE, - .id = "OPL", - .info = NULL, - .midi_dev = 0, - .synth_type = SYNTH_TYPE_FM, - .synth_subtype = FM_TYPE_ADLIB, - .open = opl3_open, - .close = opl3_close, - .ioctl = opl3_ioctl, - .kill_note = opl3_kill_note, - .start_note = opl3_start_note, - .set_instr = opl3_set_instr, - .reset = opl3_reset, - .hw_control = opl3_hw_control, - .load_patch = opl3_load_patch, - .aftertouch = opl3_aftertouch, - .controller = opl3_controller, - .panning = opl3_panning, - .volume_method = opl3_volume_method, - .bender = opl3_bender, - .alloc_voice = opl3_alloc_voice, - .setup_voice = opl3_setup_voice -}; - -static int opl3_init(int ioaddr, struct module *owner) -{ - int i; - int me; - - if (devc == NULL) - { - printk(KERN_ERR "opl3: Device control structure not initialized.\n"); - return -1; - } - - if ((me = sound_alloc_synthdev()) == -1) - { - printk(KERN_WARNING "opl3: Too many synthesizers\n"); - return -1; - } - - devc->nr_voice = 9; - - devc->fm_info.device = 0; - devc->fm_info.synth_type = SYNTH_TYPE_FM; - devc->fm_info.synth_subtype = FM_TYPE_ADLIB; - devc->fm_info.perc_mode = 0; - devc->fm_info.nr_voices = 9; - devc->fm_info.nr_drums = 0; - devc->fm_info.instr_bank_size = SBFM_MAXINSTR; - devc->fm_info.capabilities = 0; - devc->left_io = ioaddr; - devc->right_io = ioaddr + 2; - - if (detected_model <= 2) - devc->model = 1; - else - { - devc->model = 2; - if (detected_model == 4) - devc->is_opl4 = 1; - } - - opl3_operations.info = &devc->fm_info; - - synth_devs[me] = &opl3_operations; - - if (owner) - synth_devs[me]->owner = owner; - - sequencer_init(); - devc->v_alloc = &opl3_operations.alloc; - devc->chn_info = &opl3_operations.chn_info[0]; - - if (devc->model == 2) - { - if (devc->is_opl4) - strcpy(devc->fm_info.name, "Yamaha OPL4/OPL3 FM"); - else - strcpy(devc->fm_info.name, "Yamaha OPL3"); - - devc->v_alloc->max_voice = devc->nr_voice = 18; - devc->fm_info.nr_drums = 0; - devc->fm_info.synth_subtype = FM_TYPE_OPL3; - devc->fm_info.capabilities |= SYNTH_CAP_OPL3; - - for (i = 0; i < 18; i++) - { - if (pv_map[i].ioaddr == USE_LEFT) - pv_map[i].ioaddr = devc->left_io; - else - pv_map[i].ioaddr = devc->right_io; - } - opl3_command(devc->right_io, OPL3_MODE_REGISTER, OPL3_ENABLE); - opl3_command(devc->right_io, CONNECTION_SELECT_REGISTER, 0x00); - } - else - { - strcpy(devc->fm_info.name, "Yamaha OPL2"); - devc->v_alloc->max_voice = devc->nr_voice = 9; - devc->fm_info.nr_drums = 0; - - for (i = 0; i < 18; i++) - pv_map[i].ioaddr = devc->left_io; - } - conf_printf2(devc->fm_info.name, ioaddr, 0, -1, -1); - - for (i = 0; i < SBFM_MAXINSTR; i++) - devc->i_map[i].channel = -1; - - return me; -} - -static int me; - -static int io = -1; - -module_param_hw(io, int, ioport, 0); - -static int __init init_opl3 (void) -{ - printk(KERN_INFO "YM3812 and OPL-3 driver Copyright (C) by Hannu Savolainen, Rob Hooft 1993-1996\n"); - - if (io != -1) /* User loading pure OPL3 module */ - { - if (!opl3_detect(io)) - { - return -ENODEV; - } - - me = opl3_init(io, THIS_MODULE); - } - - return 0; -} - -static void __exit cleanup_opl3(void) -{ - if (devc && io != -1) - { - if (devc->base) { - release_region(devc->base,4); - if (devc->is_opl4) - release_region(devc->base - 8, 2); - } - kfree(devc); - devc = NULL; - sound_unload_synthdev(me); - } -} - -module_init(init_opl3); -module_exit(cleanup_opl3); - -#ifndef MODULE -static int __init setup_opl3(char *str) -{ - /* io */ - int ints[2]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - - return 1; -} - -__setup("opl3=", setup_opl3); -#endif -MODULE_LICENSE("GPL"); diff --git a/sound/oss/opl3_hw.h b/sound/oss/opl3_hw.h deleted file mode 100644 index 8b11c893e869..000000000000 --- a/sound/oss/opl3_hw.h +++ /dev/null @@ -1,246 +0,0 @@ -/* - * opl3_hw.h - Definitions of the OPL-3 registers - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * - * The OPL-3 mode is switched on by writing 0x01, to the offset 5 - * of the right side. - * - * Another special register at the right side is at offset 4. It contains - * a bit mask defining which voices are used as 4 OP voices. - * - * The percussive mode is implemented in the left side only. - * - * With the above exceptions the both sides can be operated independently. - * - * A 4 OP voice can be created by setting the corresponding - * bit at offset 4 of the right side. - * - * For example setting the rightmost bit (0x01) changes the - * first voice on the right side to the 4 OP mode. The fourth - * voice is made inaccessible. - * - * If a voice is set to the 2 OP mode, it works like 2 OP modes - * of the original YM3812 (AdLib). In addition the voice can - * be connected the left, right or both stereo channels. It can - * even be left unconnected. This works with 4 OP voices also. - * - * The stereo connection bits are located in the FEEDBACK_CONNECTION - * register of the voice (0xC0-0xC8). In 4 OP voices these bits are - * in the second half of the voice. - */ - -/* - * Register numbers for the global registers - */ - -#define TEST_REGISTER 0x01 -#define ENABLE_WAVE_SELECT 0x20 - -#define TIMER1_REGISTER 0x02 -#define TIMER2_REGISTER 0x03 -#define TIMER_CONTROL_REGISTER 0x04 /* Left side */ -#define IRQ_RESET 0x80 -#define TIMER1_MASK 0x40 -#define TIMER2_MASK 0x20 -#define TIMER1_START 0x01 -#define TIMER2_START 0x02 - -#define CONNECTION_SELECT_REGISTER 0x04 /* Right side */ -#define RIGHT_4OP_0 0x01 -#define RIGHT_4OP_1 0x02 -#define RIGHT_4OP_2 0x04 -#define LEFT_4OP_0 0x08 -#define LEFT_4OP_1 0x10 -#define LEFT_4OP_2 0x20 - -#define OPL3_MODE_REGISTER 0x05 /* Right side */ -#define OPL3_ENABLE 0x01 -#define OPL4_ENABLE 0x02 - -#define KBD_SPLIT_REGISTER 0x08 /* Left side */ -#define COMPOSITE_SINE_WAVE_MODE 0x80 /* Don't use with OPL-3? */ -#define KEYBOARD_SPLIT 0x40 - -#define PERCOSSION_REGISTER 0xbd /* Left side only */ -#define TREMOLO_DEPTH 0x80 -#define VIBRATO_DEPTH 0x40 -#define PERCOSSION_ENABLE 0x20 -#define BASSDRUM_ON 0x10 -#define SNAREDRUM_ON 0x08 -#define TOMTOM_ON 0x04 -#define CYMBAL_ON 0x02 -#define HIHAT_ON 0x01 - -/* - * Offsets to the register banks for operators. To get the - * register number just add the operator offset to the bank offset - * - * AM/VIB/EG/KSR/Multiple (0x20 to 0x35) - */ -#define AM_VIB 0x20 -#define TREMOLO_ON 0x80 -#define VIBRATO_ON 0x40 -#define SUSTAIN_ON 0x20 -#define KSR 0x10 /* Key scaling rate */ -#define MULTIPLE_MASK 0x0f /* Frequency multiplier */ - - /* - * KSL/Total level (0x40 to 0x55) - */ -#define KSL_LEVEL 0x40 -#define KSL_MASK 0xc0 /* Envelope scaling bits */ -#define TOTAL_LEVEL_MASK 0x3f /* Strength (volume) of OP */ - -/* - * Attack / Decay rate (0x60 to 0x75) - */ -#define ATTACK_DECAY 0x60 -#define ATTACK_MASK 0xf0 -#define DECAY_MASK 0x0f - -/* - * Sustain level / Release rate (0x80 to 0x95) - */ -#define SUSTAIN_RELEASE 0x80 -#define SUSTAIN_MASK 0xf0 -#define RELEASE_MASK 0x0f - -/* - * Wave select (0xE0 to 0xF5) - */ -#define WAVE_SELECT 0xe0 - -/* - * Offsets to the register banks for voices. Just add to the - * voice number to get the register number. - * - * F-Number low bits (0xA0 to 0xA8). - */ -#define FNUM_LOW 0xa0 - -/* - * F-number high bits / Key on / Block (octave) (0xB0 to 0xB8) - */ -#define KEYON_BLOCK 0xb0 -#define KEYON_BIT 0x20 -#define BLOCKNUM_MASK 0x1c -#define FNUM_HIGH_MASK 0x03 - -/* - * Feedback / Connection (0xc0 to 0xc8) - * - * These registers have two new bits when the OPL-3 mode - * is selected. These bits controls connecting the voice - * to the stereo channels. For 4 OP voices this bit is - * defined in the second half of the voice (add 3 to the - * register offset). - * - * For 4 OP voices the connection bit is used in the - * both halves (gives 4 ways to connect the operators). - */ -#define FEEDBACK_CONNECTION 0xc0 -#define FEEDBACK_MASK 0x0e /* Valid just for 1st OP of a voice */ -#define CONNECTION_BIT 0x01 -/* - * In the 4 OP mode there is four possible configurations how the - * operators can be connected together (in 2 OP modes there is just - * AM or FM). The 4 OP connection mode is defined by the rightmost - * bit of the FEEDBACK_CONNECTION (0xC0-0xC8) on the both halves. - * - * First half Second half Mode - * - * +---+ - * v | - * 0 0 >+-1-+--2--3--4--> - * - * - * - * +---+ - * | | - * 0 1 >+-1-+--2-+ - * |-> - * >--3----4-+ - * - * +---+ - * | | - * 1 0 >+-1-+-----+ - * |-> - * >--2--3--4-+ - * - * +---+ - * | | - * 1 1 >+-1-+--+ - * | - * >--2--3-+-> - * | - * >--4----+ - */ -#define STEREO_BITS 0x30 /* OPL-3 only */ -#define VOICE_TO_LEFT 0x10 -#define VOICE_TO_RIGHT 0x20 - -/* - * Definition table for the physical voices - */ - -struct physical_voice_info { - unsigned char voice_num; - unsigned char voice_mode; /* 0=unavailable, 2=2 OP, 4=4 OP */ - unsigned short ioaddr; /* I/O port (left or right side) */ - unsigned char op[4]; /* Operator offsets */ - }; - -/* - * There is 18 possible 2 OP voices - * (9 in the left and 9 in the right). - * The first OP is the modulator and 2nd is the carrier. - * - * The first three voices in the both sides may be connected - * with another voice to a 4 OP voice. For example voice 0 - * can be connected with voice 3. The operators of voice 3 are - * used as operators 3 and 4 of the new 4 OP voice. - * In this case the 2 OP voice number 0 is the 'first half' and - * voice 3 is the second. - */ - -#define USE_LEFT 0 -#define USE_RIGHT 1 - -static struct physical_voice_info pv_map[18] = -{ -/* No Mode Side OP1 OP2 OP3 OP4 */ -/* --------------------------------------------------- */ - { 0, 2, USE_LEFT, {0x00, 0x03, 0x08, 0x0b}}, - { 1, 2, USE_LEFT, {0x01, 0x04, 0x09, 0x0c}}, - { 2, 2, USE_LEFT, {0x02, 0x05, 0x0a, 0x0d}}, - - { 3, 2, USE_LEFT, {0x08, 0x0b, 0x00, 0x00}}, - { 4, 2, USE_LEFT, {0x09, 0x0c, 0x00, 0x00}}, - { 5, 2, USE_LEFT, {0x0a, 0x0d, 0x00, 0x00}}, - - { 6, 2, USE_LEFT, {0x10, 0x13, 0x00, 0x00}}, /* Used by percussive voices */ - { 7, 2, USE_LEFT, {0x11, 0x14, 0x00, 0x00}}, /* if the percussive mode */ - { 8, 2, USE_LEFT, {0x12, 0x15, 0x00, 0x00}}, /* is selected */ - - { 0, 2, USE_RIGHT, {0x00, 0x03, 0x08, 0x0b}}, - { 1, 2, USE_RIGHT, {0x01, 0x04, 0x09, 0x0c}}, - { 2, 2, USE_RIGHT, {0x02, 0x05, 0x0a, 0x0d}}, - - { 3, 2, USE_RIGHT, {0x08, 0x0b, 0x00, 0x00}}, - { 4, 2, USE_RIGHT, {0x09, 0x0c, 0x00, 0x00}}, - { 5, 2, USE_RIGHT, {0x0a, 0x0d, 0x00, 0x00}}, - - { 6, 2, USE_RIGHT, {0x10, 0x13, 0x00, 0x00}}, - { 7, 2, USE_RIGHT, {0x11, 0x14, 0x00, 0x00}}, - { 8, 2, USE_RIGHT, {0x12, 0x15, 0x00, 0x00}} -}; -/* - * DMA buffer calls - */ diff --git a/sound/oss/os.h b/sound/oss/os.h deleted file mode 100644 index 16f3a069b85c..000000000000 --- a/sound/oss/os.h +++ /dev/null @@ -1,46 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#define ALLOW_SELECT -#undef NO_INLINE_ASM -#define SHORT_BANNERS -#define MANUAL_PNP -#undef DO_TIMINGS - -#include <linux/module.h> - -#ifdef __KERNEL__ -#include <linux/string.h> -#include <linux/fs.h> -#include <asm/dma.h> -#include <asm/io.h> -#include <asm/param.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/ioport.h> -#include <asm/page.h> -#include <linux/vmalloc.h> -#include <linux/uaccess.h> -#include <linux/poll.h> -#include <linux/pci.h> -#endif - -#include <linux/soundcard.h> - -#define FALSE 0 -#define TRUE 1 - -extern int sound_alloc_dma(int chn, char *deviceID); -extern int sound_open_dma(int chn, char *deviceID); -extern void sound_free_dma(int chn); -extern void sound_close_dma(int chn); - -extern void reprogram_timer(void); - -#define USE_AUTOINIT_DMA - -extern void *sound_mem_blocks[1024]; -extern int sound_nblocks; - -#undef PSEUDO_DMA_AUTOINIT -#define ALLOW_BUFFER_MAPPING - -extern const struct file_operations oss_sound_fops; diff --git a/sound/oss/pas2.h b/sound/oss/pas2.h deleted file mode 100644 index 57f476238309..000000000000 --- a/sound/oss/pas2.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/* From pas_card.c */ -int pas_set_intr(int mask); -int pas_remove_intr(int mask); -unsigned char pas_read(int ioaddr); -void pas_write(unsigned char data, int ioaddr); - -/* From pas_audio.c */ -void pas_pcm_interrupt(unsigned char status, int cause); -void pas_pcm_init(struct address_info *hw_config); - -/* From pas_mixer.c */ -int pas_init_mixer(void); - -/* From pas_midi.c */ -void pas_midi_init(void); -void pas_midi_interrupt(void); - -/* From pas2_mixer.c*/ -void mix_write(unsigned char data, int ioaddr); diff --git a/sound/oss/pas2_card.c b/sound/oss/pas2_card.c deleted file mode 100644 index 769fca692d2a..000000000000 --- a/sound/oss/pas2_card.c +++ /dev/null @@ -1,458 +0,0 @@ -/* - * sound/oss/pas2_card.c - * - * Detection routine for the Pro Audio Spectrum cards. - */ - -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/module.h> -#include <linux/spinlock.h> -#include "sound_config.h" - -#include "pas2.h" -#include "sb.h" - -static unsigned char dma_bits[] = { - 4, 1, 2, 3, 0, 5, 6, 7 -}; - -static unsigned char irq_bits[] = { - 0, 0, 1, 2, 3, 4, 5, 6, 0, 1, 7, 8, 9, 0, 10, 11 -}; - -static unsigned char sb_irq_bits[] = { - 0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20, - 0x00, 0x08, 0x28, 0x30, 0x38, 0, 0 -}; - -static unsigned char sb_dma_bits[] = { - 0x00, 0x40, 0x80, 0xC0, 0, 0, 0, 0 -}; - -/* - * The Address Translation code is used to convert I/O register addresses to - * be relative to the given base -register - */ - -int pas_translate_code = 0; -static int pas_intr_mask; -static int pas_irq; -static int pas_sb_base; -DEFINE_SPINLOCK(pas_lock); -#ifndef CONFIG_PAS_JOYSTICK -static bool joystick; -#else -static bool joystick = 1; -#endif -#ifdef SYMPHONY_PAS -static bool symphony = 1; -#else -static bool symphony; -#endif -#ifdef BROKEN_BUS_CLOCK -static bool broken_bus_clock = 1; -#else -static bool broken_bus_clock; -#endif - -static struct address_info cfg; -static struct address_info cfg2; - -char pas_model = 0; -static char *pas_model_names[] = { - "", - "Pro AudioSpectrum+", - "CDPC", - "Pro AudioSpectrum 16", - "Pro AudioSpectrum 16D" -}; - -/* - * pas_read() and pas_write() are equivalents of inb and outb - * These routines perform the I/O address translation required - * to support other than the default base address - */ - -unsigned char pas_read(int ioaddr) -{ - return inb(ioaddr + pas_translate_code); -} - -void pas_write(unsigned char data, int ioaddr) -{ - outb((data), ioaddr + pas_translate_code); -} - -/******************* Begin of the Interrupt Handler ********************/ - -static irqreturn_t pasintr(int irq, void *dev_id) -{ - int status; - - status = pas_read(0x0B89); - pas_write(status, 0x0B89); /* Clear interrupt */ - - if (status & 0x08) - { - pas_pcm_interrupt(status, 1); - status &= ~0x08; - } - if (status & 0x10) - { - pas_midi_interrupt(); - status &= ~0x10; - } - return IRQ_HANDLED; -} - -int pas_set_intr(int mask) -{ - if (!mask) - return 0; - - pas_intr_mask |= mask; - - pas_write(pas_intr_mask, 0x0B8B); - return 0; -} - -int pas_remove_intr(int mask) -{ - if (!mask) - return 0; - - pas_intr_mask &= ~mask; - pas_write(pas_intr_mask, 0x0B8B); - - return 0; -} - -/******************* End of the Interrupt handler **********************/ - -/******************* Begin of the Initialization Code ******************/ - -static int __init config_pas_hw(struct address_info *hw_config) -{ - char ok = 1; - unsigned int_ptrs; /* scsi/sound interrupt pointers */ - - pas_irq = hw_config->irq; - - pas_write(0x00, 0x0B8B); - pas_write(0x36, 0x138B); - pas_write(0x36, 0x1388); - pas_write(0, 0x1388); - pas_write(0x74, 0x138B); - pas_write(0x74, 0x1389); - pas_write(0, 0x1389); - - pas_write(0x80 | 0x40 | 0x20 | 1, 0x0B8A); - pas_write(0x80 | 0x20 | 0x10 | 0x08 | 0x01, 0xF8A); - pas_write(0x01 | 0x02 | 0x04 | 0x10 /* - * | - * 0x80 - */ , 0xB88); - - pas_write(0x80 | (joystick ? 0x40 : 0), 0xF388); - - if (pas_irq < 0 || pas_irq > 15) - { - printk(KERN_ERR "PAS16: Invalid IRQ %d", pas_irq); - hw_config->irq=-1; - ok = 0; - } - else - { - int_ptrs = pas_read(0xF38A); - int_ptrs = (int_ptrs & 0xf0) | irq_bits[pas_irq]; - pas_write(int_ptrs, 0xF38A); - if (!irq_bits[pas_irq]) - { - printk(KERN_ERR "PAS16: Invalid IRQ %d", pas_irq); - hw_config->irq=-1; - ok = 0; - } - else - { - if (request_irq(pas_irq, pasintr, 0, "PAS16",hw_config) < 0) { - printk(KERN_ERR "PAS16: Cannot allocate IRQ %d\n",pas_irq); - hw_config->irq=-1; - ok = 0; - } - } - } - - if (hw_config->dma < 0 || hw_config->dma > 7) - { - printk(KERN_ERR "PAS16: Invalid DMA selection %d", hw_config->dma); - hw_config->dma=-1; - ok = 0; - } - else - { - pas_write(dma_bits[hw_config->dma], 0xF389); - if (!dma_bits[hw_config->dma]) - { - printk(KERN_ERR "PAS16: Invalid DMA selection %d", hw_config->dma); - hw_config->dma=-1; - ok = 0; - } - else - { - if (sound_alloc_dma(hw_config->dma, "PAS16")) - { - printk(KERN_ERR "pas2_card.c: Can't allocate DMA channel\n"); - hw_config->dma=-1; - ok = 0; - } - } - } - - /* - * This fixes the timing problems of the PAS due to the Symphony chipset - * as per Media Vision. Only define this if your PAS doesn't work correctly. - */ - - if(symphony) - { - outb((0x05), 0xa8); - outb((0x60), 0xa9); - } - - if(broken_bus_clock) - pas_write(0x01 | 0x10 | 0x20 | 0x04, 0x8388); - else - /* - * pas_write(0x01, 0x8388); - */ - pas_write(0x01 | 0x10 | 0x20, 0x8388); - - pas_write(0x18, 0x838A); /* ??? */ - pas_write(0x20 | 0x01, 0x0B8A); /* Mute off, filter = 17.897 kHz */ - pas_write(8, 0xBF8A); - - mix_write(0x80 | 5, 0x078B); - mix_write(5, 0x078B); - - { - struct address_info *sb_config; - - sb_config = &cfg2; - if (sb_config->io_base) - { - unsigned char irq_dma; - - /* - * Turn on Sound Blaster compatibility - * bit 1 = SB emulation - * bit 0 = MPU401 emulation (CDPC only :-( ) - */ - - pas_write(0x02, 0xF788); - - /* - * "Emulation address" - */ - - pas_write((sb_config->io_base >> 4) & 0x0f, 0xF789); - pas_sb_base = sb_config->io_base; - - if (!sb_dma_bits[sb_config->dma]) - printk(KERN_ERR "PAS16 Warning: Invalid SB DMA %d\n\n", sb_config->dma); - - if (!sb_irq_bits[sb_config->irq]) - printk(KERN_ERR "PAS16 Warning: Invalid SB IRQ %d\n\n", sb_config->irq); - - irq_dma = sb_dma_bits[sb_config->dma] | - sb_irq_bits[sb_config->irq]; - - pas_write(irq_dma, 0xFB8A); - } - else - pas_write(0x00, 0xF788); - } - - if (!ok) - printk(KERN_WARNING "PAS16: Driver not enabled\n"); - - return ok; -} - -static int __init detect_pas_hw(struct address_info *hw_config) -{ - unsigned char board_id, foo; - - /* - * WARNING: Setting an option like W:1 or so that disables warm boot reset - * of the card will screw up this detect code something fierce. Adding code - * to handle this means possibly interfering with other cards on the bus if - * you have something on base port 0x388. SO be forewarned. - */ - - outb((0xBC), 0x9A01); /* Activate first board */ - outb((hw_config->io_base >> 2), 0x9A01); /* Set base address */ - pas_translate_code = hw_config->io_base - 0x388; - pas_write(1, 0xBF88); /* Select one wait states */ - - board_id = pas_read(0x0B8B); - - if (board_id == 0xff) - return 0; - - /* - * We probably have a PAS-series board, now check for a PAS16-series board - * by trying to change the board revision bits. PAS16-series hardware won't - * let you do this - the bits are read-only. - */ - - foo = board_id ^ 0xe0; - - pas_write(foo, 0x0B8B); - foo = pas_read(0x0B8B); - pas_write(board_id, 0x0B8B); - - if (board_id != foo) - return 0; - - pas_model = pas_read(0xFF88); - - return pas_model; -} - -static void __init attach_pas_card(struct address_info *hw_config) -{ - pas_irq = hw_config->irq; - - if (detect_pas_hw(hw_config)) - { - - if ((pas_model = pas_read(0xFF88))) - { - char temp[100]; - - if (pas_model < 0 || - pas_model >= ARRAY_SIZE(pas_model_names)) { - printk(KERN_ERR "pas2 unrecognized model.\n"); - return; - } - sprintf(temp, - "%s rev %d", pas_model_names[(int) pas_model], - pas_read(0x2789)); - conf_printf(temp, hw_config); - } - if (config_pas_hw(hw_config)) - { - pas_pcm_init(hw_config); - pas_midi_init(); - pas_init_mixer(); - } - } -} - -static inline int __init probe_pas(struct address_info *hw_config) -{ - return detect_pas_hw(hw_config); -} - -static void __exit unload_pas(struct address_info *hw_config) -{ - extern int pas_audiodev; - extern int pas2_mididev; - - if (hw_config->dma>0) - sound_free_dma(hw_config->dma); - if (hw_config->irq>0) - free_irq(hw_config->irq, hw_config); - - if(pas_audiodev!=-1) - sound_unload_mixerdev(audio_devs[pas_audiodev]->mixer_dev); - if(pas2_mididev!=-1) - sound_unload_mididev(pas2_mididev); - if(pas_audiodev!=-1) - sound_unload_audiodev(pas_audiodev); -} - -static int __initdata io = -1; -static int __initdata irq = -1; -static int __initdata dma = -1; -static int __initdata dma16 = -1; /* Set this for modules that need it */ - -static int __initdata sb_io = 0; -static int __initdata sb_irq = -1; -static int __initdata sb_dma = -1; -static int __initdata sb_dma16 = -1; - -module_param_hw(io, int, ioport, 0); -module_param_hw(irq, int, irq, 0); -module_param_hw(dma, int, dma, 0); -module_param_hw(dma16, int, dma, 0); - -module_param_hw(sb_io, int, ioport, 0); -module_param_hw(sb_irq, int, irq, 0); -module_param_hw(sb_dma, int, dma, 0); -module_param_hw(sb_dma16, int, dma, 0); - -module_param(joystick, bool, 0); -module_param(symphony, bool, 0); -module_param(broken_bus_clock, bool, 0); - -MODULE_LICENSE("GPL"); - -static int __init init_pas2(void) -{ - printk(KERN_INFO "Pro Audio Spectrum driver Copyright (C) by Hannu Savolainen 1993-1996\n"); - - cfg.io_base = io; - cfg.irq = irq; - cfg.dma = dma; - cfg.dma2 = dma16; - - cfg2.io_base = sb_io; - cfg2.irq = sb_irq; - cfg2.dma = sb_dma; - cfg2.dma2 = sb_dma16; - - if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) { - printk(KERN_INFO "I/O, IRQ, DMA and type are mandatory\n"); - return -EINVAL; - } - - if (!probe_pas(&cfg)) - return -ENODEV; - attach_pas_card(&cfg); - - return 0; -} - -static void __exit cleanup_pas2(void) -{ - unload_pas(&cfg); -} - -module_init(init_pas2); -module_exit(cleanup_pas2); - -#ifndef MODULE -static int __init setup_pas2(char *str) -{ - /* io, irq, dma, dma2, sb_io, sb_irq, sb_dma, sb_dma2 */ - int ints[9]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - dma = ints[3]; - dma16 = ints[4]; - - sb_io = ints[5]; - sb_irq = ints[6]; - sb_dma = ints[7]; - sb_dma16 = ints[8]; - - return 1; -} - -__setup("pas2=", setup_pas2); -#endif diff --git a/sound/oss/pas2_midi.c b/sound/oss/pas2_midi.c deleted file mode 100644 index 1122d10a20c3..000000000000 --- a/sound/oss/pas2_midi.c +++ /dev/null @@ -1,262 +0,0 @@ -/* - * sound/oss/pas2_midi.c - * - * The low level driver for the PAS Midi Interface. - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * Bartlomiej Zolnierkiewicz : Added __init to pas_init_mixer() - */ - -#include <linux/init.h> -#include <linux/spinlock.h> -#include "sound_config.h" - -#include "pas2.h" - -extern spinlock_t pas_lock; - -static int midi_busy, input_opened; -static int my_dev; - -int pas2_mididev=-1; - -static unsigned char tmp_queue[256]; -static volatile int qlen; -static volatile unsigned char qhead, qtail; - -static void (*midi_input_intr) (int dev, unsigned char data); - -static int pas_midi_open(int dev, int mode, - void (*input) (int dev, unsigned char data), - void (*output) (int dev) -) -{ - int err; - unsigned long flags; - unsigned char ctrl; - - - if (midi_busy) - return -EBUSY; - - /* - * Reset input and output FIFO pointers - */ - pas_write(0x20 | 0x40, - 0x178b); - - spin_lock_irqsave(&pas_lock, flags); - - if ((err = pas_set_intr(0x10)) < 0) - { - spin_unlock_irqrestore(&pas_lock, flags); - return err; - } - /* - * Enable input available and output FIFO empty interrupts - */ - - ctrl = 0; - input_opened = 0; - midi_input_intr = input; - - if (mode == OPEN_READ || mode == OPEN_READWRITE) - { - ctrl |= 0x04; /* Enable input */ - input_opened = 1; - } - if (mode == OPEN_WRITE || mode == OPEN_READWRITE) - { - ctrl |= 0x08 | 0x10; /* Enable output */ - } - pas_write(ctrl, 0x178b); - - /* - * Acknowledge any pending interrupts - */ - - pas_write(0xff, 0x1B88); - - spin_unlock_irqrestore(&pas_lock, flags); - - midi_busy = 1; - qlen = qhead = qtail = 0; - return 0; -} - -static void pas_midi_close(int dev) -{ - - /* - * Reset FIFO pointers, disable intrs - */ - pas_write(0x20 | 0x40, 0x178b); - - pas_remove_intr(0x10); - midi_busy = 0; -} - -static int dump_to_midi(unsigned char midi_byte) -{ - int fifo_space, x; - - fifo_space = ((x = pas_read(0x1B89)) >> 4) & 0x0f; - - /* - * The MIDI FIFO space register and it's documentation is nonunderstandable. - * There seem to be no way to differentiate between buffer full and buffer - * empty situations. For this reason we don't never write the buffer - * completely full. In this way we can assume that 0 (or is it 15) - * means that the buffer is empty. - */ - - if (fifo_space < 2 && fifo_space != 0) /* Full (almost) */ - return 0; /* Ask upper layers to retry after some time */ - - pas_write(midi_byte, 0x178A); - - return 1; -} - -static int pas_midi_out(int dev, unsigned char midi_byte) -{ - - unsigned long flags; - - /* - * Drain the local queue first - */ - - spin_lock_irqsave(&pas_lock, flags); - - while (qlen && dump_to_midi(tmp_queue[qhead])) - { - qlen--; - qhead++; - } - - spin_unlock_irqrestore(&pas_lock, flags); - - /* - * Output the byte if the local queue is empty. - */ - - if (!qlen) - if (dump_to_midi(midi_byte)) - return 1; - - /* - * Put to the local queue - */ - - if (qlen >= 256) - return 0; /* Local queue full */ - - spin_lock_irqsave(&pas_lock, flags); - - tmp_queue[qtail] = midi_byte; - qlen++; - qtail++; - - spin_unlock_irqrestore(&pas_lock, flags); - - return 1; -} - -static int pas_midi_start_read(int dev) -{ - return 0; -} - -static int pas_midi_end_read(int dev) -{ - return 0; -} - -static void pas_midi_kick(int dev) -{ -} - -static int pas_buffer_status(int dev) -{ - return qlen; -} - -#define MIDI_SYNTH_NAME "Pro Audio Spectrum Midi" -#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT -#include "midi_synth.h" - -static struct midi_operations pas_midi_operations = -{ - .owner = THIS_MODULE, - .info = {"Pro Audio Spectrum", 0, 0, SNDCARD_PAS}, - .converter = &std_midi_synth, - .in_info = {0}, - .open = pas_midi_open, - .close = pas_midi_close, - .outputc = pas_midi_out, - .start_read = pas_midi_start_read, - .end_read = pas_midi_end_read, - .kick = pas_midi_kick, - .buffer_status = pas_buffer_status, -}; - -void __init pas_midi_init(void) -{ - int dev = sound_alloc_mididev(); - - if (dev == -1) - { - printk(KERN_WARNING "pas_midi_init: Too many midi devices detected\n"); - return; - } - std_midi_synth.midi_dev = my_dev = dev; - midi_devs[dev] = &pas_midi_operations; - pas2_mididev = dev; - sequencer_init(); -} - -void pas_midi_interrupt(void) -{ - unsigned char stat; - int i, incount; - - stat = pas_read(0x1B88); - - if (stat & 0x04) /* Input data available */ - { - incount = pas_read(0x1B89) & 0x0f; /* Input FIFO size */ - if (!incount) - incount = 16; - - for (i = 0; i < incount; i++) - if (input_opened) - { - midi_input_intr(my_dev, pas_read(0x178A)); - } else - pas_read(0x178A); /* Flush */ - } - if (stat & (0x08 | 0x10)) - { - spin_lock(&pas_lock);/* called in irq context */ - - while (qlen && dump_to_midi(tmp_queue[qhead])) - { - qlen--; - qhead++; - } - - spin_unlock(&pas_lock); - } - if (stat & 0x40) - { - printk(KERN_WARNING "MIDI output overrun %x,%x\n", pas_read(0x1B89), stat); - } - pas_write(stat, 0x1B88); /* Acknowledge interrupts */ -} diff --git a/sound/oss/pas2_mixer.c b/sound/oss/pas2_mixer.c deleted file mode 100644 index 50b5bd501247..000000000000 --- a/sound/oss/pas2_mixer.c +++ /dev/null @@ -1,327 +0,0 @@ - -/* - * sound/oss/pas2_mixer.c - * - * Mixer routines for the Pro Audio Spectrum cards. - */ - -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ -/* - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - * Bartlomiej Zolnierkiewicz : added __init to pas_init_mixer() - */ -#include <linux/init.h> -#include "sound_config.h" - -#include "pas2.h" - -extern int pas_translate_code; -extern char pas_model; -extern int *pas_osp; -extern int pas_audiodev; - -static int rec_devices = (SOUND_MASK_MIC); /* Default recording source */ -static int mode_control; - -#define POSSIBLE_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \ - SOUND_MASK_CD | SOUND_MASK_ALTPCM) - -#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \ - SOUND_MASK_CD | SOUND_MASK_ALTPCM | SOUND_MASK_IMIX | \ - SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_RECLEV) - -static int *levels; - -static int default_levels[32] = -{ - 0x3232, /* Master Volume */ - 0x3232, /* Bass */ - 0x3232, /* Treble */ - 0x5050, /* FM */ - 0x4b4b, /* PCM */ - 0x3232, /* PC Speaker */ - 0x4b4b, /* Ext Line */ - 0x4b4b, /* Mic */ - 0x4b4b, /* CD */ - 0x6464, /* Recording monitor */ - 0x4b4b, /* SB PCM */ - 0x6464 /* Recording level */ -}; - -void -mix_write(unsigned char data, int ioaddr) -{ - /* - * The Revision D cards have a problem with their MVA508 interface. The - * kludge-o-rama fix is to make a 16-bit quantity with identical LSB and - * MSBs out of the output byte and to do a 16-bit out to the mixer port - - * 1. We need to do this because it isn't timing problem but chip access - * sequence problem. - */ - - if (pas_model == 4) - { - outw(data | (data << 8), (ioaddr + pas_translate_code) - 1); - outb((0x80), 0); - } else - pas_write(data, ioaddr); -} - -static int -mixer_output(int right_vol, int left_vol, int div, int bits, - int mixer) /* Input or output mixer */ -{ - int left = left_vol * div / 100; - int right = right_vol * div / 100; - - - if (bits & 0x10) - { - left |= mixer; - right |= mixer; - } - if (bits == 0x03 || bits == 0x04) - { - mix_write(0x80 | bits, 0x078B); - mix_write(left, 0x078B); - right_vol = left_vol; - } else - { - mix_write(0x80 | 0x20 | bits, 0x078B); - mix_write(left, 0x078B); - mix_write(0x80 | 0x40 | bits, 0x078B); - mix_write(right, 0x078B); - } - - return (left_vol | (right_vol << 8)); -} - -static void -set_mode(int new_mode) -{ - mix_write(0x80 | 0x05, 0x078B); - mix_write(new_mode, 0x078B); - - mode_control = new_mode; -} - -static int -pas_mixer_set(int whichDev, unsigned int level) -{ - int left, right, devmask, changed, i, mixer = 0; - - left = level & 0x7f; - right = (level & 0x7f00) >> 8; - - if (whichDev < SOUND_MIXER_NRDEVICES) { - if ((1 << whichDev) & rec_devices) - mixer = 0x20; - else - mixer = 0x00; - } - - switch (whichDev) - { - case SOUND_MIXER_VOLUME: /* Master volume (0-63) */ - levels[whichDev] = mixer_output(right, left, 63, 0x01, 0); - break; - - /* - * Note! Bass and Treble are mono devices. Will use just the left - * channel. - */ - case SOUND_MIXER_BASS: /* Bass (0-12) */ - levels[whichDev] = mixer_output(right, left, 12, 0x03, 0); - break; - case SOUND_MIXER_TREBLE: /* Treble (0-12) */ - levels[whichDev] = mixer_output(right, left, 12, 0x04, 0); - break; - - case SOUND_MIXER_SYNTH: /* Internal synthesizer (0-31) */ - levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x00, mixer); - break; - case SOUND_MIXER_PCM: /* PAS PCM (0-31) */ - levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x05, mixer); - break; - case SOUND_MIXER_ALTPCM: /* SB PCM (0-31) */ - levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x07, mixer); - break; - case SOUND_MIXER_SPEAKER: /* PC speaker (0-31) */ - levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x06, mixer); - break; - case SOUND_MIXER_LINE: /* External line (0-31) */ - levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x02, mixer); - break; - case SOUND_MIXER_CD: /* CD (0-31) */ - levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x03, mixer); - break; - case SOUND_MIXER_MIC: /* External microphone (0-31) */ - levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x04, mixer); - break; - case SOUND_MIXER_IMIX: /* Recording monitor (0-31) (Output mixer only) */ - levels[whichDev] = mixer_output(right, left, 31, 0x10 | 0x01, - 0x00); - break; - case SOUND_MIXER_RECLEV: /* Recording level (0-15) */ - levels[whichDev] = mixer_output(right, left, 15, 0x02, 0); - break; - - - case SOUND_MIXER_RECSRC: - devmask = level & POSSIBLE_RECORDING_DEVICES; - - changed = devmask ^ rec_devices; - rec_devices = devmask; - - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - if (changed & (1 << i)) - { - pas_mixer_set(i, levels[i]); - } - return rec_devices; - break; - - default: - return -EINVAL; - } - - return (levels[whichDev]); -} - -/*****/ - -static void -pas_mixer_reset(void) -{ - int foo; - - for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++) - pas_mixer_set(foo, levels[foo]); - - set_mode(0x04 | 0x01); -} - -static int pas_mixer_ioctl(int dev, unsigned int cmd, void __user *arg) -{ - int level,v ; - int __user *p = (int __user *)arg; - - if (cmd == SOUND_MIXER_PRIVATE1) { /* Set loudness bit */ - if (get_user(level, p)) - return -EFAULT; - if (level == -1) /* Return current settings */ - level = (mode_control & 0x04); - else { - mode_control &= ~0x04; - if (level) - mode_control |= 0x04; - set_mode(mode_control); - } - level = !!level; - return put_user(level, p); - } - if (cmd == SOUND_MIXER_PRIVATE2) { /* Set enhance bit */ - if (get_user(level, p)) - return -EFAULT; - if (level == -1) { /* Return current settings */ - if (!(mode_control & 0x03)) - level = 0; - else - level = ((mode_control & 0x03) + 1) * 20; - } else { - int i = 0; - - level &= 0x7f; - if (level) - i = (level / 20) - 1; - mode_control &= ~0x03; - mode_control |= i & 0x03; - set_mode(mode_control); - if (i) - i = (i + 1) * 20; - level = i; - } - return put_user(level, p); - } - if (cmd == SOUND_MIXER_PRIVATE3) { /* Set mute bit */ - if (get_user(level, p)) - return -EFAULT; - if (level == -1) /* Return current settings */ - level = !(pas_read(0x0B8A) & 0x20); - else { - if (level) - pas_write(pas_read(0x0B8A) & (~0x20), 0x0B8A); - else - pas_write(pas_read(0x0B8A) | 0x20, 0x0B8A); - - level = !(pas_read(0x0B8A) & 0x20); - } - return put_user(level, p); - } - if (((cmd >> 8) & 0xff) == 'M') { - if (get_user(v, p)) - return -EFAULT; - if (_SIOC_DIR(cmd) & _SIOC_WRITE) { - v = pas_mixer_set(cmd & 0xff, v); - } else { - switch (cmd & 0xff) { - case SOUND_MIXER_RECSRC: - v = rec_devices; - break; - - case SOUND_MIXER_STEREODEVS: - v = SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE); - break; - - case SOUND_MIXER_DEVMASK: - v = SUPPORTED_MIXER_DEVICES; - break; - - case SOUND_MIXER_RECMASK: - v = POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES; - break; - - case SOUND_MIXER_CAPS: - v = 0; /* No special capabilities */ - break; - - default: - v = levels[cmd & 0xff]; - break; - } - } - return put_user(v, p); - } - return -EINVAL; -} - -static struct mixer_operations pas_mixer_operations = -{ - .owner = THIS_MODULE, - .id = "PAS16", - .name = "Pro Audio Spectrum 16", - .ioctl = pas_mixer_ioctl -}; - -int __init -pas_init_mixer(void) -{ - int d; - - levels = load_mixer_volumes("PAS16_1", default_levels, 1); - - pas_mixer_reset(); - - if ((d = sound_alloc_mixerdev()) != -1) - { - audio_devs[pas_audiodev]->mixer_dev = d; - mixer_devs[d] = &pas_mixer_operations; - } - return 1; -} diff --git a/sound/oss/pas2_pcm.c b/sound/oss/pas2_pcm.c deleted file mode 100644 index 474803b52f7d..000000000000 --- a/sound/oss/pas2_pcm.c +++ /dev/null @@ -1,419 +0,0 @@ -/* - * pas2_pcm.c Audio routines for PAS16 - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - * Alan Cox : Swatted a double allocation of device bug. Made a few - * more things module options. - * Bartlomiej Zolnierkiewicz : Added __init to pas_pcm_init() - */ - -#include <linux/init.h> -#include <linux/spinlock.h> -#include <linux/timex.h> -#include "sound_config.h" - -#include "pas2.h" - -#define PAS_PCM_INTRBITS (0x08) -/* - * Sample buffer timer interrupt enable - */ - -#define PCM_NON 0 -#define PCM_DAC 1 -#define PCM_ADC 2 - -static unsigned long pcm_speed; /* sampling rate */ -static unsigned char pcm_channels = 1; /* channels (1 or 2) */ -static unsigned char pcm_bits = 8; /* bits/sample (8 or 16) */ -static unsigned char pcm_filter; /* filter FLAG */ -static unsigned char pcm_mode = PCM_NON; -static unsigned long pcm_count; -static unsigned short pcm_bitsok = 8; /* mask of OK bits */ -static int pcm_busy; -int pas_audiodev = -1; -static int open_mode; - -extern spinlock_t pas_lock; - -static int pcm_set_speed(int arg) -{ - int foo, tmp; - unsigned long flags; - - if (arg == 0) - return pcm_speed; - - if (arg > 44100) - arg = 44100; - if (arg < 5000) - arg = 5000; - - if (pcm_channels & 2) - { - foo = ((PIT_TICK_RATE / 2) + (arg / 2)) / arg; - arg = ((PIT_TICK_RATE / 2) + (foo / 2)) / foo; - } - else - { - foo = (PIT_TICK_RATE + (arg / 2)) / arg; - arg = (PIT_TICK_RATE + (foo / 2)) / foo; - } - - pcm_speed = arg; - - tmp = pas_read(0x0B8A); - - /* - * Set anti-aliasing filters according to sample rate. You really *NEED* - * to enable this feature for all normal recording unless you want to - * experiment with aliasing effects. - * These filters apply to the selected "recording" source. - * I (pfw) don't know the encoding of these 5 bits. The values shown - * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/. - * - * I cleared bit 5 of these values, since that bit controls the master - * mute flag. (Olav Wölfelschneider) - * - */ -#if !defined NO_AUTO_FILTER_SET - tmp &= 0xe0; - if (pcm_speed >= 2 * 17897) - tmp |= 0x01; - else if (pcm_speed >= 2 * 15909) - tmp |= 0x02; - else if (pcm_speed >= 2 * 11931) - tmp |= 0x09; - else if (pcm_speed >= 2 * 8948) - tmp |= 0x11; - else if (pcm_speed >= 2 * 5965) - tmp |= 0x19; - else if (pcm_speed >= 2 * 2982) - tmp |= 0x04; - pcm_filter = tmp; -#endif - - spin_lock_irqsave(&pas_lock, flags); - - pas_write(tmp & ~(0x40 | 0x80), 0x0B8A); - pas_write(0x00 | 0x30 | 0x04, 0x138B); - pas_write(foo & 0xff, 0x1388); - pas_write((foo >> 8) & 0xff, 0x1388); - pas_write(tmp, 0x0B8A); - - spin_unlock_irqrestore(&pas_lock, flags); - - return pcm_speed; -} - -static int pcm_set_channels(int arg) -{ - - if ((arg != 1) && (arg != 2)) - return pcm_channels; - - if (arg != pcm_channels) - { - pas_write(pas_read(0xF8A) ^ 0x20, 0xF8A); - - pcm_channels = arg; - pcm_set_speed(pcm_speed); /* The speed must be reinitialized */ - } - return pcm_channels; -} - -static int pcm_set_bits(int arg) -{ - if (arg == 0) - return pcm_bits; - - if ((arg & pcm_bitsok) != arg) - return pcm_bits; - - if (arg != pcm_bits) - { - pas_write(pas_read(0x8389) ^ 0x04, 0x8389); - - pcm_bits = arg; - } - return pcm_bits; -} - -static int pas_audio_ioctl(int dev, unsigned int cmd, void __user *arg) -{ - int val, ret; - int __user *p = arg; - - switch (cmd) - { - case SOUND_PCM_WRITE_RATE: - if (get_user(val, p)) - return -EFAULT; - ret = pcm_set_speed(val); - break; - - case SOUND_PCM_READ_RATE: - ret = pcm_speed; - break; - - case SNDCTL_DSP_STEREO: - if (get_user(val, p)) - return -EFAULT; - ret = pcm_set_channels(val + 1) - 1; - break; - - case SOUND_PCM_WRITE_CHANNELS: - if (get_user(val, p)) - return -EFAULT; - ret = pcm_set_channels(val); - break; - - case SOUND_PCM_READ_CHANNELS: - ret = pcm_channels; - break; - - case SNDCTL_DSP_SETFMT: - if (get_user(val, p)) - return -EFAULT; - ret = pcm_set_bits(val); - break; - - case SOUND_PCM_READ_BITS: - ret = pcm_bits; - break; - - default: - return -EINVAL; - } - return put_user(ret, p); -} - -static void pas_audio_reset(int dev) -{ - pas_write(pas_read(0xF8A) & ~0x40, 0xF8A); /* Disable PCM */ -} - -static int pas_audio_open(int dev, int mode) -{ - int err; - unsigned long flags; - - spin_lock_irqsave(&pas_lock, flags); - if (pcm_busy) - { - spin_unlock_irqrestore(&pas_lock, flags); - return -EBUSY; - } - pcm_busy = 1; - spin_unlock_irqrestore(&pas_lock, flags); - - if ((err = pas_set_intr(PAS_PCM_INTRBITS)) < 0) - return err; - - - pcm_count = 0; - open_mode = mode; - - return 0; -} - -static void pas_audio_close(int dev) -{ - unsigned long flags; - - spin_lock_irqsave(&pas_lock, flags); - - pas_audio_reset(dev); - pas_remove_intr(PAS_PCM_INTRBITS); - pcm_mode = PCM_NON; - - pcm_busy = 0; - spin_unlock_irqrestore(&pas_lock, flags); -} - -static void pas_audio_output_block(int dev, unsigned long buf, int count, - int intrflag) -{ - unsigned long flags, cnt; - - cnt = count; - if (audio_devs[dev]->dmap_out->dma > 3) - cnt >>= 1; - - if (audio_devs[dev]->flags & DMA_AUTOMODE && - intrflag && - cnt == pcm_count) - return; - - spin_lock_irqsave(&pas_lock, flags); - - pas_write(pas_read(0xF8A) & ~0x40, - 0xF8A); - - /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */ - - if (audio_devs[dev]->dmap_out->dma > 3) - count >>= 1; - - if (count != pcm_count) - { - pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A); - pas_write(0x40 | 0x30 | 0x04, 0x138B); - pas_write(count & 0xff, 0x1389); - pas_write((count >> 8) & 0xff, 0x1389); - pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A); - - pcm_count = count; - } - pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A); -#ifdef NO_TRIGGER - pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A); -#endif - - pcm_mode = PCM_DAC; - - spin_unlock_irqrestore(&pas_lock, flags); -} - -static void pas_audio_start_input(int dev, unsigned long buf, int count, - int intrflag) -{ - unsigned long flags; - int cnt; - - cnt = count; - if (audio_devs[dev]->dmap_out->dma > 3) - cnt >>= 1; - - if (audio_devs[pas_audiodev]->flags & DMA_AUTOMODE && - intrflag && - cnt == pcm_count) - return; - - spin_lock_irqsave(&pas_lock, flags); - - /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */ - - if (audio_devs[dev]->dmap_out->dma > 3) - count >>= 1; - - if (count != pcm_count) - { - pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A); - pas_write(0x40 | 0x30 | 0x04, 0x138B); - pas_write(count & 0xff, 0x1389); - pas_write((count >> 8) & 0xff, 0x1389); - pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A); - - pcm_count = count; - } - pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A); -#ifdef NO_TRIGGER - pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A); -#endif - - pcm_mode = PCM_ADC; - - spin_unlock_irqrestore(&pas_lock, flags); -} - -#ifndef NO_TRIGGER -static void pas_audio_trigger(int dev, int state) -{ - unsigned long flags; - - spin_lock_irqsave(&pas_lock, flags); - state &= open_mode; - - if (state & PCM_ENABLE_OUTPUT) - pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A); - else if (state & PCM_ENABLE_INPUT) - pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A); - else - pas_write(pas_read(0xF8A) & ~0x40, 0xF8A); - - spin_unlock_irqrestore(&pas_lock, flags); -} -#endif - -static int pas_audio_prepare_for_input(int dev, int bsize, int bcount) -{ - pas_audio_reset(dev); - return 0; -} - -static int pas_audio_prepare_for_output(int dev, int bsize, int bcount) -{ - pas_audio_reset(dev); - return 0; -} - -static struct audio_driver pas_audio_driver = -{ - .owner = THIS_MODULE, - .open = pas_audio_open, - .close = pas_audio_close, - .output_block = pas_audio_output_block, - .start_input = pas_audio_start_input, - .ioctl = pas_audio_ioctl, - .prepare_for_input = pas_audio_prepare_for_input, - .prepare_for_output = pas_audio_prepare_for_output, - .halt_io = pas_audio_reset, - .trigger = pas_audio_trigger -}; - -void __init pas_pcm_init(struct address_info *hw_config) -{ - pcm_bitsok = 8; - if (pas_read(0xEF8B) & 0x08) - pcm_bitsok |= 16; - - pcm_set_speed(DSP_DEFAULT_SPEED); - - if ((pas_audiodev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, - "Pro Audio Spectrum", - &pas_audio_driver, - sizeof(struct audio_driver), - DMA_AUTOMODE, - AFMT_U8 | AFMT_S16_LE, - NULL, - hw_config->dma, - hw_config->dma)) < 0) - printk(KERN_WARNING "PAS16: Too many PCM devices available\n"); -} - -void pas_pcm_interrupt(unsigned char status, int cause) -{ - if (cause == 1) - { - /* - * Halt the PCM first. Otherwise we don't have time to start a new - * block before the PCM chip proceeds to the next sample - */ - - if (!(audio_devs[pas_audiodev]->flags & DMA_AUTOMODE)) - pas_write(pas_read(0xF8A) & ~0x40, 0xF8A); - - switch (pcm_mode) - { - case PCM_DAC: - DMAbuf_outputintr(pas_audiodev, 1); - break; - - case PCM_ADC: - DMAbuf_inputintr(pas_audiodev); - break; - - default: - printk(KERN_WARNING "PAS: Unexpected PCM interrupt\n"); - } - } -} diff --git a/sound/oss/pss.c b/sound/oss/pss.c deleted file mode 100644 index 33c3a442e162..000000000000 --- a/sound/oss/pss.c +++ /dev/null @@ -1,1270 +0,0 @@ -/* - * sound/oss/pss.c - * - * The low level driver for the Personal Sound System (ECHO ESC614). - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * - * Thomas Sailer ioctl code reworked (vmalloc/vfree removed) - * Alan Cox modularisation, clean up. - * - * 98-02-21: Vladimir Michl <vladimir.michl@upol.cz> - * Added mixer device for Beethoven ADSP-16 (master volume, - * bass, treble, synth), only for speakers. - * Fixed bug in pss_write (exchange parameters) - * Fixed config port of SB - * Requested two regions for PSS (PSS mixer, PSS config) - * Modified pss_download_boot - * To probe_pss_mss added test for initialize AD1848 - * 98-05-28: Vladimir Michl <vladimir.michl@upol.cz> - * Fixed computation of mixer volumes - * 04-05-1999: Anthony Barbachan <barbcode@xmen.cis.fordham.edu> - * Added code that allows the user to enable his cdrom and/or - * joystick through the module parameters pss_cdrom_port and - * pss_enable_joystick. pss_cdrom_port takes a port address as its - * argument. pss_enable_joystick takes either a 0 or a non-0 as its - * argument. - * 04-06-1999: Anthony Barbachan <barbcode@xmen.cis.fordham.edu> - * Separated some code into new functions for easier reuse. - * Cleaned up and streamlined new code. Added code to allow a user - * to only use this driver for enabling non-sound components - * through the new module parameter pss_no_sound (flag). Added - * code that would allow a user to decide whether the driver should - * reset the configured hardware settings for the PSS board through - * the module parameter pss_keep_settings (flag). This flag will - * allow a user to free up resources in use by this card if needbe, - * furthermore it allows him to use this driver to just enable the - * emulations and then be unloaded as it is no longer needed. Both - * new settings are only available to this driver if compiled as a - * module. The default settings of all new parameters are set to - * load the driver as it did in previous versions. - * 04-07-1999: Anthony Barbachan <barbcode@xmen.cis.fordham.edu> - * Added module parameter pss_firmware to allow the user to tell - * the driver where the firmware file is located. The default - * setting is the previous hardcoded setting "/etc/sound/pss_synth". - * 00-03-03: Christoph Hellwig <chhellwig@infradead.org> - * Adapted to module_init/module_exit - * 11-10-2000: Bartlomiej Zolnierkiewicz <bkz@linux-ide.org> - * Added __init to probe_pss(), attach_pss() and probe_pss_mpu() - * 02-Jan-2001: Chris Rankin - * Specify that this module owns the coprocessor - */ - - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/spinlock.h> - -#include "sound_config.h" -#include "sound_firmware.h" - -#include "ad1848.h" -#include "mpu401.h" - -/* - * PSS registers. - */ -#define REG(x) (devc->base+x) -#define PSS_DATA 0 -#define PSS_STATUS 2 -#define PSS_CONTROL 2 -#define PSS_ID 4 -#define PSS_IRQACK 4 -#define PSS_PIO 0x1a - -/* - * Config registers - */ -#define CONF_PSS 0x10 -#define CONF_WSS 0x12 -#define CONF_SB 0x14 -#define CONF_CDROM 0x16 -#define CONF_MIDI 0x18 - -/* - * Status bits. - */ -#define PSS_FLAG3 0x0800 -#define PSS_FLAG2 0x0400 -#define PSS_FLAG1 0x1000 -#define PSS_FLAG0 0x0800 -#define PSS_WRITE_EMPTY 0x8000 -#define PSS_READ_FULL 0x4000 - -/* - * WSS registers - */ -#define WSS_INDEX 4 -#define WSS_DATA 5 - -/* - * WSS status bits - */ -#define WSS_INITIALIZING 0x80 -#define WSS_AUTOCALIBRATION 0x20 - -#define NO_WSS_MIXER -1 - -#include "coproc.h" - -#include "pss_boot.h" - -/* If compiled into kernel, it enable or disable pss mixer */ -#ifdef CONFIG_PSS_MIXER -static bool pss_mixer = 1; -#else -static bool pss_mixer; -#endif - - -struct pss_mixerdata { - unsigned int volume_l; - unsigned int volume_r; - unsigned int bass; - unsigned int treble; - unsigned int synth; -}; - -struct pss_confdata { - int base; - int irq; - int dma; - int *osp; - struct pss_mixerdata mixer; - int ad_mixer_dev; -}; - -static struct pss_confdata pss_data; -static struct pss_confdata *devc = &pss_data; -static DEFINE_SPINLOCK(lock); - -static int pss_initialized; -static int nonstandard_microcode; -static int pss_cdrom_port = -1; /* Parameter for the PSS cdrom port */ -static bool pss_enable_joystick; /* Parameter for enabling the joystick */ -static coproc_operations pss_coproc_operations; - -static void pss_write(struct pss_confdata *devc, int data) -{ - unsigned long i, limit; - - limit = jiffies + HZ/10; /* The timeout is 0.1 seconds */ - /* - * Note! the i<5000000 is an emergency exit. The dsp_command() is sometimes - * called while interrupts are disabled. This means that the timer is - * disabled also. However the timeout situation is a abnormal condition. - * Normally the DSP should be ready to accept commands after just couple of - * loops. - */ - - for (i = 0; i < 5000000 && time_before(jiffies, limit); i++) - { - if (inw(REG(PSS_STATUS)) & PSS_WRITE_EMPTY) - { - outw(data, REG(PSS_DATA)); - return; - } - } - printk(KERN_WARNING "PSS: DSP Command (%04x) Timeout.\n", data); -} - -static int __init probe_pss(struct address_info *hw_config) -{ - unsigned short id; - int irq, dma; - - devc->base = hw_config->io_base; - irq = devc->irq = hw_config->irq; - dma = devc->dma = hw_config->dma; - devc->osp = hw_config->osp; - - if (devc->base != 0x220 && devc->base != 0x240) - if (devc->base != 0x230 && devc->base != 0x250) /* Some cards use these */ - return 0; - - if (!request_region(devc->base, 0x10, "PSS mixer, SB emulation")) { - printk(KERN_ERR "PSS: I/O port conflict\n"); - return 0; - } - id = inw(REG(PSS_ID)); - if ((id >> 8) != 'E') { - printk(KERN_ERR "No PSS signature detected at 0x%x (0x%x)\n", devc->base, id); - release_region(devc->base, 0x10); - return 0; - } - if (!request_region(devc->base + 0x10, 0x9, "PSS config")) { - printk(KERN_ERR "PSS: I/O port conflict\n"); - release_region(devc->base, 0x10); - return 0; - } - return 1; -} - -static int set_irq(struct pss_confdata *devc, int dev, int irq) -{ - static unsigned short irq_bits[16] = - { - 0x0000, 0x0000, 0x0000, 0x0008, - 0x0000, 0x0010, 0x0000, 0x0018, - 0x0000, 0x0020, 0x0028, 0x0030, - 0x0038, 0x0000, 0x0000, 0x0000 - }; - - unsigned short tmp, bits; - - if (irq < 0 || irq > 15) - return 0; - - tmp = inw(REG(dev)) & ~0x38; /* Load confreg, mask IRQ bits out */ - - if ((bits = irq_bits[irq]) == 0 && irq != 0) - { - printk(KERN_ERR "PSS: Invalid IRQ %d\n", irq); - return 0; - } - outw(tmp | bits, REG(dev)); - return 1; -} - -static void set_io_base(struct pss_confdata *devc, int dev, int base) -{ - unsigned short tmp = inw(REG(dev)) & 0x003f; - unsigned short bits = (base & 0x0ffc) << 4; - - outw(bits | tmp, REG(dev)); -} - -static int set_dma(struct pss_confdata *devc, int dev, int dma) -{ - static unsigned short dma_bits[8] = - { - 0x0001, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0005, 0x0006, 0x0007 - }; - - unsigned short tmp, bits; - - if (dma < 0 || dma > 7) - return 0; - - tmp = inw(REG(dev)) & ~0x07; /* Load confreg, mask DMA bits out */ - - if ((bits = dma_bits[dma]) == 0 && dma != 4) - { - printk(KERN_ERR "PSS: Invalid DMA %d\n", dma); - return 0; - } - outw(tmp | bits, REG(dev)); - return 1; -} - -static int pss_reset_dsp(struct pss_confdata *devc) -{ - unsigned long i, limit = jiffies + HZ/10; - - outw(0x2000, REG(PSS_CONTROL)); - for (i = 0; i < 32768 && time_after_eq(limit, jiffies); i++) - inw(REG(PSS_CONTROL)); - outw(0x0000, REG(PSS_CONTROL)); - return 1; -} - -static int pss_put_dspword(struct pss_confdata *devc, unsigned short word) -{ - int i, val; - - for (i = 0; i < 327680; i++) - { - val = inw(REG(PSS_STATUS)); - if (val & PSS_WRITE_EMPTY) - { - outw(word, REG(PSS_DATA)); - return 1; - } - } - return 0; -} - -static int pss_get_dspword(struct pss_confdata *devc, unsigned short *word) -{ - int i, val; - - for (i = 0; i < 327680; i++) - { - val = inw(REG(PSS_STATUS)); - if (val & PSS_READ_FULL) - { - *word = inw(REG(PSS_DATA)); - return 1; - } - } - return 0; -} - -static int pss_download_boot(struct pss_confdata *devc, unsigned char *block, - int size, int flags) -{ - int i, val, count; - unsigned long limit; - - if (flags & CPF_FIRST) - { -/*_____ Warn DSP software that a boot is coming */ - outw(0x00fe, REG(PSS_DATA)); - - limit = jiffies + HZ/10; - for (i = 0; i < 32768 && time_before(jiffies, limit); i++) - if (inw(REG(PSS_DATA)) == 0x5500) - break; - - outw(*block++, REG(PSS_DATA)); - pss_reset_dsp(devc); - } - count = 1; - while ((flags&CPF_LAST) || count<size ) - { - int j; - - for (j = 0; j < 327670; j++) - { -/*_____ Wait for BG to appear */ - if (inw(REG(PSS_STATUS)) & PSS_FLAG3) - break; - } - - if (j == 327670) - { - /* It's ok we timed out when the file was empty */ - if (count >= size && flags & CPF_LAST) - break; - else - { - printk("\n"); - printk(KERN_ERR "PSS: Download timeout problems, byte %d=%d\n", count, size); - return 0; - } - } -/*_____ Send the next byte */ - if (count >= size) - { - /* If not data in block send 0xffff */ - outw (0xffff, REG (PSS_DATA)); - } - else - { - /*_____ Send the next byte */ - outw (*block++, REG (PSS_DATA)); - } - count++; - } - - if (flags & CPF_LAST) - { -/*_____ Why */ - outw(0, REG(PSS_DATA)); - - limit = jiffies + HZ/10; - for (i = 0; i < 32768 && time_after_eq(limit, jiffies); i++) - val = inw(REG(PSS_STATUS)); - - limit = jiffies + HZ/10; - for (i = 0; i < 32768 && time_after_eq(limit, jiffies); i++) - { - val = inw(REG(PSS_STATUS)); - if (val & 0x4000) - break; - } - - /* now read the version */ - for (i = 0; i < 32000; i++) - { - val = inw(REG(PSS_STATUS)); - if (val & PSS_READ_FULL) - break; - } - if (i == 32000) - return 0; - - val = inw(REG(PSS_DATA)); - /* printk( "<PSS: microcode version %d.%d loaded>", val/16, val % 16); */ - } - return 1; -} - -/* Mixer */ -static void set_master_volume(struct pss_confdata *devc, int left, int right) -{ - static unsigned char log_scale[101] = { - 0xdb, 0xe0, 0xe3, 0xe5, 0xe7, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xed, 0xee, - 0xef, 0xef, 0xf0, 0xf0, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, - 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, - 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, - 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, - 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, - 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, - 0xfe, 0xfe, 0xff, 0xff, 0xff - }; - pss_write(devc, 0x0010); - pss_write(devc, log_scale[left] | 0x0000); - pss_write(devc, 0x0010); - pss_write(devc, log_scale[right] | 0x0100); -} - -static void set_synth_volume(struct pss_confdata *devc, int volume) -{ - int vol = ((0x8000*volume)/100L); - pss_write(devc, 0x0080); - pss_write(devc, vol); - pss_write(devc, 0x0081); - pss_write(devc, vol); -} - -static void set_bass(struct pss_confdata *devc, int level) -{ - int vol = (int)(((0xfd - 0xf0) * level)/100L) + 0xf0; - pss_write(devc, 0x0010); - pss_write(devc, vol | 0x0200); -}; - -static void set_treble(struct pss_confdata *devc, int level) -{ - int vol = (((0xfd - 0xf0) * level)/100L) + 0xf0; - pss_write(devc, 0x0010); - pss_write(devc, vol | 0x0300); -}; - -static void pss_mixer_reset(struct pss_confdata *devc) -{ - set_master_volume(devc, 33, 33); - set_bass(devc, 50); - set_treble(devc, 50); - set_synth_volume(devc, 30); - pss_write (devc, 0x0010); - pss_write (devc, 0x0800 | 0xce); /* Stereo */ - - if(pss_mixer) - { - devc->mixer.volume_l = devc->mixer.volume_r = 33; - devc->mixer.bass = 50; - devc->mixer.treble = 50; - devc->mixer.synth = 30; - } -} - -static int set_volume_mono(unsigned __user *p, unsigned int *aleft) -{ - unsigned int left, volume; - if (get_user(volume, p)) - return -EFAULT; - - left = volume & 0xff; - if (left > 100) - left = 100; - *aleft = left; - return 0; -} - -static int set_volume_stereo(unsigned __user *p, - unsigned int *aleft, - unsigned int *aright) -{ - unsigned int left, right, volume; - if (get_user(volume, p)) - return -EFAULT; - - left = volume & 0xff; - if (left > 100) - left = 100; - right = (volume >> 8) & 0xff; - if (right > 100) - right = 100; - *aleft = left; - *aright = right; - return 0; -} - -static int ret_vol_mono(int left) -{ - return ((left << 8) | left); -} - -static int ret_vol_stereo(int left, int right) -{ - return ((right << 8) | left); -} - -static int call_ad_mixer(struct pss_confdata *devc, unsigned int cmd, - void __user *arg) -{ - if (devc->ad_mixer_dev != NO_WSS_MIXER) - return mixer_devs[devc->ad_mixer_dev]->ioctl(devc->ad_mixer_dev, cmd, arg); - else - return -EINVAL; -} - -static int pss_mixer_ioctl (int dev, unsigned int cmd, void __user *arg) -{ - struct pss_confdata *devc = mixer_devs[dev]->devc; - int cmdf = cmd & 0xff; - - if ((cmdf != SOUND_MIXER_VOLUME) && (cmdf != SOUND_MIXER_BASS) && - (cmdf != SOUND_MIXER_TREBLE) && (cmdf != SOUND_MIXER_SYNTH) && - (cmdf != SOUND_MIXER_DEVMASK) && (cmdf != SOUND_MIXER_STEREODEVS) && - (cmdf != SOUND_MIXER_RECMASK) && (cmdf != SOUND_MIXER_CAPS) && - (cmdf != SOUND_MIXER_RECSRC)) - { - return call_ad_mixer(devc, cmd, arg); - } - - if (((cmd >> 8) & 0xff) != 'M') - return -EINVAL; - - if (_SIOC_DIR (cmd) & _SIOC_WRITE) - { - switch (cmdf) - { - case SOUND_MIXER_RECSRC: - if (devc->ad_mixer_dev != NO_WSS_MIXER) - return call_ad_mixer(devc, cmd, arg); - else - { - int v; - if (get_user(v, (int __user *)arg)) - return -EFAULT; - if (v != 0) - return -EINVAL; - return 0; - } - case SOUND_MIXER_VOLUME: - if (set_volume_stereo(arg, - &devc->mixer.volume_l, - &devc->mixer.volume_r)) - return -EFAULT; - set_master_volume(devc, devc->mixer.volume_l, - devc->mixer.volume_r); - return ret_vol_stereo(devc->mixer.volume_l, - devc->mixer.volume_r); - - case SOUND_MIXER_BASS: - if (set_volume_mono(arg, &devc->mixer.bass)) - return -EFAULT; - set_bass(devc, devc->mixer.bass); - return ret_vol_mono(devc->mixer.bass); - - case SOUND_MIXER_TREBLE: - if (set_volume_mono(arg, &devc->mixer.treble)) - return -EFAULT; - set_treble(devc, devc->mixer.treble); - return ret_vol_mono(devc->mixer.treble); - - case SOUND_MIXER_SYNTH: - if (set_volume_mono(arg, &devc->mixer.synth)) - return -EFAULT; - set_synth_volume(devc, devc->mixer.synth); - return ret_vol_mono(devc->mixer.synth); - - default: - return -EINVAL; - } - } - else - { - int val, and_mask = 0, or_mask = 0; - /* - * Return parameters - */ - switch (cmdf) - { - case SOUND_MIXER_DEVMASK: - if (call_ad_mixer(devc, cmd, arg) == -EINVAL) - break; - and_mask = ~0; - or_mask = SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_SYNTH; - break; - - case SOUND_MIXER_STEREODEVS: - if (call_ad_mixer(devc, cmd, arg) == -EINVAL) - break; - and_mask = ~0; - or_mask = SOUND_MASK_VOLUME; - break; - - case SOUND_MIXER_RECMASK: - if (devc->ad_mixer_dev != NO_WSS_MIXER) - return call_ad_mixer(devc, cmd, arg); - break; - - case SOUND_MIXER_CAPS: - if (devc->ad_mixer_dev != NO_WSS_MIXER) - return call_ad_mixer(devc, cmd, arg); - or_mask = SOUND_CAP_EXCL_INPUT; - break; - - case SOUND_MIXER_RECSRC: - if (devc->ad_mixer_dev != NO_WSS_MIXER) - return call_ad_mixer(devc, cmd, arg); - break; - - case SOUND_MIXER_VOLUME: - or_mask = ret_vol_stereo(devc->mixer.volume_l, devc->mixer.volume_r); - break; - - case SOUND_MIXER_BASS: - or_mask = ret_vol_mono(devc->mixer.bass); - break; - - case SOUND_MIXER_TREBLE: - or_mask = ret_vol_mono(devc->mixer.treble); - break; - - case SOUND_MIXER_SYNTH: - or_mask = ret_vol_mono(devc->mixer.synth); - break; - default: - return -EINVAL; - } - if (get_user(val, (int __user *)arg)) - return -EFAULT; - val &= and_mask; - val |= or_mask; - if (put_user(val, (int __user *)arg)) - return -EFAULT; - return val; - } -} - -static struct mixer_operations pss_mixer_operations = -{ - .owner = THIS_MODULE, - .id = "SOUNDPORT", - .name = "PSS-AD1848", - .ioctl = pss_mixer_ioctl -}; - -static void disable_all_emulations(void) -{ - outw(0x0000, REG(CONF_PSS)); /* 0x0400 enables joystick */ - outw(0x0000, REG(CONF_WSS)); - outw(0x0000, REG(CONF_SB)); - outw(0x0000, REG(CONF_MIDI)); - outw(0x0000, REG(CONF_CDROM)); -} - -static void configure_nonsound_components(void) -{ - /* Configure Joystick port */ - - if(pss_enable_joystick) - { - outw(0x0400, REG(CONF_PSS)); /* 0x0400 enables joystick */ - printk(KERN_INFO "PSS: joystick enabled.\n"); - } - else - { - printk(KERN_INFO "PSS: joystick port not enabled.\n"); - } - - /* Configure CDROM port */ - - if (pss_cdrom_port == -1) { /* If cdrom port enablation wasn't requested */ - printk(KERN_INFO "PSS: CDROM port not enabled.\n"); - } else if (!request_region(pss_cdrom_port, 2, "PSS CDROM")) { - pss_cdrom_port = -1; - printk(KERN_ERR "PSS: CDROM I/O port conflict.\n"); - } else { - set_io_base(devc, CONF_CDROM, pss_cdrom_port); - printk(KERN_INFO "PSS: CDROM I/O port set to 0x%x.\n", pss_cdrom_port); - } -} - -static int __init attach_pss(struct address_info *hw_config) -{ - unsigned short id; - char tmp[100]; - - devc->base = hw_config->io_base; - devc->irq = hw_config->irq; - devc->dma = hw_config->dma; - devc->osp = hw_config->osp; - devc->ad_mixer_dev = NO_WSS_MIXER; - - if (!probe_pss(hw_config)) - return 0; - - id = inw(REG(PSS_ID)) & 0x00ff; - - /* - * Disable all emulations. Will be enabled later (if required). - */ - - disable_all_emulations(); - -#ifdef YOU_REALLY_WANT_TO_ALLOCATE_THESE_RESOURCES - if (sound_alloc_dma(hw_config->dma, "PSS")) - { - printk("pss.c: Can't allocate DMA channel.\n"); - release_region(hw_config->io_base, 0x10); - release_region(hw_config->io_base+0x10, 0x9); - return 0; - } - if (!set_irq(devc, CONF_PSS, devc->irq)) - { - printk("PSS: IRQ allocation error.\n"); - release_region(hw_config->io_base, 0x10); - release_region(hw_config->io_base+0x10, 0x9); - return 0; - } - if (!set_dma(devc, CONF_PSS, devc->dma)) - { - printk(KERN_ERR "PSS: DMA allocation error\n"); - release_region(hw_config->io_base, 0x10); - release_region(hw_config->io_base+0x10, 0x9); - return 0; - } -#endif - - configure_nonsound_components(); - pss_initialized = 1; - sprintf(tmp, "ECHO-PSS Rev. %d", id); - conf_printf(tmp, hw_config); - return 1; -} - -static int __init probe_pss_mpu(struct address_info *hw_config) -{ - struct resource *ports; - int timeout; - - if (!pss_initialized) - return 0; - - ports = request_region(hw_config->io_base, 2, "mpu401"); - - if (!ports) { - printk(KERN_ERR "PSS: MPU I/O port conflict\n"); - return 0; - } - set_io_base(devc, CONF_MIDI, hw_config->io_base); - if (!set_irq(devc, CONF_MIDI, hw_config->irq)) { - printk(KERN_ERR "PSS: MIDI IRQ allocation error.\n"); - goto fail; - } - if (!pss_synthLen) { - printk(KERN_ERR "PSS: Can't enable MPU. MIDI synth microcode not available.\n"); - goto fail; - } - if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) { - printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n"); - goto fail; - } - - /* - * Finally wait until the DSP algorithm has initialized itself and - * deactivates receive interrupt. - */ - - for (timeout = 900000; timeout > 0; timeout--) - { - if ((inb(hw_config->io_base + 1) & 0x80) == 0) /* Input data avail */ - inb(hw_config->io_base); /* Discard it */ - else - break; /* No more input */ - } - - if (!probe_mpu401(hw_config, ports)) - goto fail; - - attach_mpu401(hw_config, THIS_MODULE); /* Slot 1 */ - if (hw_config->slots[1] != -1) /* The MPU driver installed itself */ - midi_devs[hw_config->slots[1]]->coproc = &pss_coproc_operations; - return 1; -fail: - release_region(hw_config->io_base, 2); - return 0; -} - -static int pss_coproc_open(void *dev_info, int sub_device) -{ - switch (sub_device) - { - case COPR_MIDI: - if (pss_synthLen == 0) - { - printk(KERN_ERR "PSS: MIDI synth microcode not available.\n"); - return -EIO; - } - if (nonstandard_microcode) - if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) - { - printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n"); - return -EIO; - } - nonstandard_microcode = 0; - break; - - default: - break; - } - return 0; -} - -static void pss_coproc_close(void *dev_info, int sub_device) -{ - return; -} - -static void pss_coproc_reset(void *dev_info) -{ - if (pss_synthLen) - if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) - { - printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n"); - } - nonstandard_microcode = 0; -} - -static int download_boot_block(void *dev_info, copr_buffer * buf) -{ - if (buf->len <= 0 || buf->len > sizeof(buf->data)) - return -EINVAL; - - if (!pss_download_boot(devc, buf->data, buf->len, buf->flags)) - { - printk(KERN_ERR "PSS: Unable to load microcode block to DSP.\n"); - return -EIO; - } - nonstandard_microcode = 1; /* The MIDI microcode has been overwritten */ - return 0; -} - -static int pss_coproc_ioctl(void *dev_info, unsigned int cmd, void __user *arg, int local) -{ - copr_buffer *buf; - copr_msg *mbuf; - copr_debug_buf dbuf; - unsigned short tmp; - unsigned long flags; - unsigned short *data; - int i, err; - /* printk( "PSS coproc ioctl %x %x %d\n", cmd, arg, local); */ - - switch (cmd) - { - case SNDCTL_COPR_RESET: - pss_coproc_reset(dev_info); - return 0; - - case SNDCTL_COPR_LOAD: - buf = vmalloc(sizeof(copr_buffer)); - if (buf == NULL) - return -ENOSPC; - if (copy_from_user(buf, arg, sizeof(copr_buffer))) { - vfree(buf); - return -EFAULT; - } - err = download_boot_block(dev_info, buf); - vfree(buf); - return err; - - case SNDCTL_COPR_SENDMSG: - mbuf = vmalloc(sizeof(copr_msg)); - if (mbuf == NULL) - return -ENOSPC; - if (copy_from_user(mbuf, arg, sizeof(copr_msg))) { - vfree(mbuf); - return -EFAULT; - } - data = (unsigned short *)(mbuf->data); - spin_lock_irqsave(&lock, flags); - for (i = 0; i < mbuf->len; i++) { - if (!pss_put_dspword(devc, *data++)) { - spin_unlock_irqrestore(&lock,flags); - mbuf->len = i; /* feed back number of WORDs sent */ - err = copy_to_user(arg, mbuf, sizeof(copr_msg)); - vfree(mbuf); - return err ? -EFAULT : -EIO; - } - } - spin_unlock_irqrestore(&lock,flags); - vfree(mbuf); - return 0; - - case SNDCTL_COPR_RCVMSG: - err = 0; - mbuf = vmalloc(sizeof(copr_msg)); - if (mbuf == NULL) - return -ENOSPC; - data = (unsigned short *)mbuf->data; - spin_lock_irqsave(&lock, flags); - for (i = 0; i < sizeof(mbuf->data)/sizeof(unsigned short); i++) { - mbuf->len = i; /* feed back number of WORDs read */ - if (!pss_get_dspword(devc, data++)) { - if (i == 0) - err = -EIO; - break; - } - } - spin_unlock_irqrestore(&lock,flags); - if (copy_to_user(arg, mbuf, sizeof(copr_msg))) - err = -EFAULT; - vfree(mbuf); - return err; - - case SNDCTL_COPR_RDATA: - if (copy_from_user(&dbuf, arg, sizeof(dbuf))) - return -EFAULT; - spin_lock_irqsave(&lock, flags); - if (!pss_put_dspword(devc, 0x00d0)) { - spin_unlock_irqrestore(&lock,flags); - return -EIO; - } - if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) { - spin_unlock_irqrestore(&lock,flags); - return -EIO; - } - if (!pss_get_dspword(devc, &tmp)) { - spin_unlock_irqrestore(&lock,flags); - return -EIO; - } - dbuf.parm1 = tmp; - spin_unlock_irqrestore(&lock,flags); - if (copy_to_user(arg, &dbuf, sizeof(dbuf))) - return -EFAULT; - return 0; - - case SNDCTL_COPR_WDATA: - if (copy_from_user(&dbuf, arg, sizeof(dbuf))) - return -EFAULT; - spin_lock_irqsave(&lock, flags); - if (!pss_put_dspword(devc, 0x00d1)) { - spin_unlock_irqrestore(&lock,flags); - return -EIO; - } - if (!pss_put_dspword(devc, (unsigned short) (dbuf.parm1 & 0xffff))) { - spin_unlock_irqrestore(&lock,flags); - return -EIO; - } - tmp = (unsigned int)dbuf.parm2 & 0xffff; - if (!pss_put_dspword(devc, tmp)) { - spin_unlock_irqrestore(&lock,flags); - return -EIO; - } - spin_unlock_irqrestore(&lock,flags); - return 0; - - case SNDCTL_COPR_WCODE: - if (copy_from_user(&dbuf, arg, sizeof(dbuf))) - return -EFAULT; - spin_lock_irqsave(&lock, flags); - if (!pss_put_dspword(devc, 0x00d3)) { - spin_unlock_irqrestore(&lock,flags); - return -EIO; - } - if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) { - spin_unlock_irqrestore(&lock,flags); - return -EIO; - } - tmp = (unsigned int)dbuf.parm2 & 0x00ff; - if (!pss_put_dspword(devc, tmp)) { - spin_unlock_irqrestore(&lock,flags); - return -EIO; - } - tmp = ((unsigned int)dbuf.parm2 >> 8) & 0xffff; - if (!pss_put_dspword(devc, tmp)) { - spin_unlock_irqrestore(&lock,flags); - return -EIO; - } - spin_unlock_irqrestore(&lock,flags); - return 0; - - case SNDCTL_COPR_RCODE: - if (copy_from_user(&dbuf, arg, sizeof(dbuf))) - return -EFAULT; - spin_lock_irqsave(&lock, flags); - if (!pss_put_dspword(devc, 0x00d2)) { - spin_unlock_irqrestore(&lock,flags); - return -EIO; - } - if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) { - spin_unlock_irqrestore(&lock,flags); - return -EIO; - } - if (!pss_get_dspword(devc, &tmp)) { /* Read MSB */ - spin_unlock_irqrestore(&lock,flags); - return -EIO; - } - dbuf.parm1 = tmp << 8; - if (!pss_get_dspword(devc, &tmp)) { /* Read LSB */ - spin_unlock_irqrestore(&lock,flags); - return -EIO; - } - dbuf.parm1 |= tmp & 0x00ff; - spin_unlock_irqrestore(&lock,flags); - if (copy_to_user(arg, &dbuf, sizeof(dbuf))) - return -EFAULT; - return 0; - - default: - return -EINVAL; - } - return -EINVAL; -} - -static coproc_operations pss_coproc_operations = -{ - "ADSP-2115", - THIS_MODULE, - pss_coproc_open, - pss_coproc_close, - pss_coproc_ioctl, - pss_coproc_reset, - &pss_data -}; - -static int __init probe_pss_mss(struct address_info *hw_config) -{ - volatile int timeout; - struct resource *ports; - int my_mix = -999; /* gcc shut up */ - - if (!pss_initialized) - return 0; - - if (!request_region(hw_config->io_base, 4, "WSS config")) { - printk(KERN_ERR "PSS: WSS I/O port conflicts.\n"); - return 0; - } - ports = request_region(hw_config->io_base + 4, 4, "ad1848"); - if (!ports) { - printk(KERN_ERR "PSS: WSS I/O port conflicts.\n"); - release_region(hw_config->io_base, 4); - return 0; - } - set_io_base(devc, CONF_WSS, hw_config->io_base); - if (!set_irq(devc, CONF_WSS, hw_config->irq)) { - printk("PSS: WSS IRQ allocation error.\n"); - goto fail; - } - if (!set_dma(devc, CONF_WSS, hw_config->dma)) { - printk(KERN_ERR "PSS: WSS DMA allocation error\n"); - goto fail; - } - /* - * For some reason the card returns 0xff in the WSS status register - * immediately after boot. Probably MIDI+SB emulation algorithm - * downloaded to the ADSP2115 spends some time initializing the card. - * Let's try to wait until it finishes this task. - */ - for (timeout = 0; timeout < 100000 && (inb(hw_config->io_base + WSS_INDEX) & - WSS_INITIALIZING); timeout++) - ; - - outb((0x0b), hw_config->io_base + WSS_INDEX); /* Required by some cards */ - - for (timeout = 0; (inb(hw_config->io_base + WSS_DATA) & WSS_AUTOCALIBRATION) && - (timeout < 100000); timeout++) - ; - - if (!probe_ms_sound(hw_config, ports)) - goto fail; - - devc->ad_mixer_dev = NO_WSS_MIXER; - if (pss_mixer) - { - if ((my_mix = sound_install_mixer (MIXER_DRIVER_VERSION, - "PSS-SPEAKERS and AD1848 (through MSS audio codec)", - &pss_mixer_operations, - sizeof (struct mixer_operations), - devc)) < 0) - { - printk(KERN_ERR "Could not install PSS mixer\n"); - goto fail; - } - } - pss_mixer_reset(devc); - attach_ms_sound(hw_config, ports, THIS_MODULE); /* Slot 0 */ - - if (hw_config->slots[0] != -1) - { - /* The MSS driver installed itself */ - audio_devs[hw_config->slots[0]]->coproc = &pss_coproc_operations; - if (pss_mixer && (num_mixers == (my_mix + 2))) - { - /* The MSS mixer installed */ - devc->ad_mixer_dev = audio_devs[hw_config->slots[0]]->mixer_dev; - } - } - return 1; -fail: - release_region(hw_config->io_base + 4, 4); - release_region(hw_config->io_base, 4); - return 0; -} - -static inline void __exit unload_pss(struct address_info *hw_config) -{ - release_region(hw_config->io_base, 0x10); - release_region(hw_config->io_base+0x10, 0x9); -} - -static inline void __exit unload_pss_mpu(struct address_info *hw_config) -{ - unload_mpu401(hw_config); -} - -static inline void __exit unload_pss_mss(struct address_info *hw_config) -{ - unload_ms_sound(hw_config); -} - - -static struct address_info cfg; -static struct address_info cfg2; -static struct address_info cfg_mpu; - -static int pss_io __initdata = -1; -static int mss_io __initdata = -1; -static int mss_irq __initdata = -1; -static int mss_dma __initdata = -1; -static int mpu_io __initdata = -1; -static int mpu_irq __initdata = -1; -static bool pss_no_sound = 0; /* Just configure non-sound components */ -static bool pss_keep_settings = 1; /* Keep hardware settings at module exit */ -static char *pss_firmware = "/etc/sound/pss_synth"; - -module_param_hw(pss_io, int, ioport, 0); -MODULE_PARM_DESC(pss_io, "Set i/o base of PSS card (probably 0x220 or 0x240)"); -module_param_hw(mss_io, int, ioport, 0); -MODULE_PARM_DESC(mss_io, "Set WSS (audio) i/o base (0x530, 0x604, 0xE80, 0xF40, or other. Address must end in 0 or 4 and must be from 0x100 to 0xFF4)"); -module_param_hw(mss_irq, int, irq, 0); -MODULE_PARM_DESC(mss_irq, "Set WSS (audio) IRQ (3, 5, 7, 9, 10, 11, 12)"); -module_param_hw(mss_dma, int, dma, 0); -MODULE_PARM_DESC(mss_dma, "Set WSS (audio) DMA (0, 1, 3)"); -module_param_hw(mpu_io, int, ioport, 0); -MODULE_PARM_DESC(mpu_io, "Set MIDI i/o base (0x330 or other. Address must be on 4 location boundaries and must be from 0x100 to 0xFFC)"); -module_param_hw(mpu_irq, int, irq, 0); -MODULE_PARM_DESC(mpu_irq, "Set MIDI IRQ (3, 5, 7, 9, 10, 11, 12)"); -module_param_hw(pss_cdrom_port, int, ioport, 0); -MODULE_PARM_DESC(pss_cdrom_port, "Set the PSS CDROM port i/o base (0x340 or other)"); -module_param(pss_enable_joystick, bool, 0); -MODULE_PARM_DESC(pss_enable_joystick, "Enables the PSS joystick port (1 to enable, 0 to disable)"); -module_param(pss_no_sound, bool, 0); -MODULE_PARM_DESC(pss_no_sound, "Configure sound compoents (0 - no, 1 - yes)"); -module_param(pss_keep_settings, bool, 0); -MODULE_PARM_DESC(pss_keep_settings, "Keep hardware setting at driver unloading (0 - no, 1 - yes)"); -module_param(pss_firmware, charp, 0); -MODULE_PARM_DESC(pss_firmware, "Location of the firmware file (default - /etc/sound/pss_synth)"); -module_param(pss_mixer, bool, 0); -MODULE_PARM_DESC(pss_mixer, "Enable (1) or disable (0) PSS mixer (controlling of output volume, bass, treble, synth volume). The mixer is not available on all PSS cards."); -MODULE_AUTHOR("Hannu Savolainen, Vladimir Michl"); -MODULE_DESCRIPTION("Module for PSS sound cards (based on AD1848, ADSP-2115 and ESC614). This module includes control of output amplifier and synth volume of the Beethoven ADSP-16 card (this may work with other PSS cards)."); -MODULE_LICENSE("GPL"); - - -static int fw_load = 0; -static int pssmpu = 0, pssmss = 0; - -/* - * Load a PSS sound card module - */ - -static int __init init_pss(void) -{ - - if(pss_no_sound) /* If configuring only nonsound components */ - { - cfg.io_base = pss_io; - if(!probe_pss(&cfg)) - return -ENODEV; - printk(KERN_INFO "ECHO-PSS Rev. %d\n", inw(REG(PSS_ID)) & 0x00ff); - printk(KERN_INFO "PSS: loading in no sound mode.\n"); - disable_all_emulations(); - configure_nonsound_components(); - release_region(pss_io, 0x10); - release_region(pss_io + 0x10, 0x9); - return 0; - } - - cfg.io_base = pss_io; - - cfg2.io_base = mss_io; - cfg2.irq = mss_irq; - cfg2.dma = mss_dma; - - cfg_mpu.io_base = mpu_io; - cfg_mpu.irq = mpu_irq; - - if (cfg.io_base == -1 || cfg2.io_base == -1 || cfg2.irq == -1 || cfg.dma == -1) { - printk(KERN_INFO "pss: mss_io, mss_dma, mss_irq and pss_io must be set.\n"); - return -EINVAL; - } - - if (!pss_synth) { - fw_load = 1; - pss_synthLen = mod_firmware_load(pss_firmware, (void *) &pss_synth); - } - if (!attach_pss(&cfg)) - return -ENODEV; - /* - * Attach stuff - */ - if (probe_pss_mpu(&cfg_mpu)) - pssmpu = 1; - - if (probe_pss_mss(&cfg2)) - pssmss = 1; - - return 0; -} - -static void __exit cleanup_pss(void) -{ - if(!pss_no_sound) - { - if (fw_load) - vfree(pss_synth); - if(pssmss) - unload_pss_mss(&cfg2); - if(pssmpu) - unload_pss_mpu(&cfg_mpu); - unload_pss(&cfg); - } else if (pss_cdrom_port != -1) - release_region(pss_cdrom_port, 2); - - if(!pss_keep_settings) /* Keep hardware settings if asked */ - { - disable_all_emulations(); - printk(KERN_INFO "Resetting PSS sound card configurations.\n"); - } -} - -module_init(init_pss); -module_exit(cleanup_pss); - -#ifndef MODULE -static int __init setup_pss(char *str) -{ - /* io, mss_io, mss_irq, mss_dma, mpu_io, mpu_irq */ - int ints[7]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - pss_io = ints[1]; - mss_io = ints[2]; - mss_irq = ints[3]; - mss_dma = ints[4]; - mpu_io = ints[5]; - mpu_irq = ints[6]; - - return 1; -} - -__setup("pss=", setup_pss); -#endif diff --git a/sound/oss/sb.h b/sound/oss/sb.h deleted file mode 100644 index bb1d18709b36..000000000000 --- a/sound/oss/sb.h +++ /dev/null @@ -1,186 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#define DSP_RESET (devc->base + 0x6) -#define DSP_READ (devc->base + 0xA) -#define DSP_WRITE (devc->base + 0xC) -#define DSP_COMMAND (devc->base + 0xC) -#define DSP_STATUS (devc->base + 0xC) -#define DSP_DATA_AVAIL (devc->base + 0xE) -#define DSP_DATA_AVL16 (devc->base + 0xF) -#define MIXER_ADDR (devc->base + 0x4) -#define MIXER_DATA (devc->base + 0x5) -#define OPL3_LEFT (devc->base + 0x0) -#define OPL3_RIGHT (devc->base + 0x2) -#define OPL3_BOTH (devc->base + 0x8) -/* DSP Commands */ - -#define DSP_CMD_SPKON 0xD1 -#define DSP_CMD_SPKOFF 0xD3 -#define DSP_CMD_DMAON 0xD0 -#define DSP_CMD_DMAOFF 0xD4 - -#define IMODE_NONE 0 -#define IMODE_OUTPUT PCM_ENABLE_OUTPUT -#define IMODE_INPUT PCM_ENABLE_INPUT -#define IMODE_INIT 3 -#define IMODE_MIDI 4 - -#define NORMAL_MIDI 0 -#define UART_MIDI 1 - - -/* - * Device models - */ -#define MDL_NONE 0 -#define MDL_SB1 1 /* SB1.0 or 1.5 */ -#define MDL_SB2 2 /* SB2.0 */ -#define MDL_SB201 3 /* SB2.01 */ -#define MDL_SBPRO 4 /* SB Pro */ -#define MDL_SB16 5 /* SB16/32/AWE */ -#define MDL_SBPNP 6 /* SB16/32/AWE PnP */ -#define MDL_JAZZ 10 /* Media Vision Jazz16 */ -#define MDL_SMW 11 /* Logitech SoundMan Wave (Jazz16) */ -#define MDL_ESS 12 /* ESS ES688 and ES1688 */ -#define MDL_AZTECH 13 /* Aztech Sound Galaxy family */ -#define MDL_ES1868MIDI 14 /* MIDI port of ESS1868 */ -#define MDL_AEDSP 15 /* Audio Excel DSP 16 */ -#define MDL_ESSPCI 16 /* ESS PCI card */ -#define MDL_YMPCI 17 /* Yamaha PCI sb in emulation */ - -#define SUBMDL_ALS007 42 /* ALS-007 differs from SB16 only in mixer */ - /* register assignment */ -#define SUBMDL_ALS100 43 /* ALS-100 allows sampling rates of up */ - /* to 48kHz */ - -/* - * Config flags - */ -#define SB_NO_MIDI 0x00000001 -#define SB_NO_MIXER 0x00000002 -#define SB_NO_AUDIO 0x00000004 -#define SB_NO_RECORDING 0x00000008 /* No audio recording */ -#define SB_MIDI_ONLY (SB_NO_AUDIO|SB_NO_MIXER) -#define SB_PCI_IRQ 0x00000010 /* PCI shared IRQ */ - -struct mixer_def { - unsigned int regno: 8; - unsigned int bitoffs:4; - unsigned int nbits:4; -}; - -typedef struct mixer_def mixer_tab[32][2]; -typedef struct mixer_def mixer_ent; - -struct sb_module_options -{ - int esstype; /* ESS chip type */ - int acer; /* Do acer notebook init? */ - int sm_games; /* Logitech soundman games? */ -}; - -typedef struct sb_devc { - int dev; - - /* Hardware parameters */ - int *osp; - int minor, major; - int type; - int model, submodel; - int caps; -# define SBCAP_STEREO 0x00000001 -# define SBCAP_16BITS 0x00000002 - - /* Hardware resources */ - int base; - int irq; - int dma8, dma16; - - int pcibase; /* For ESS Maestro etc */ - - /* State variables */ - int opened; - /* new audio fields for full duplex support */ - int fullduplex; - int duplex; - int speed, bits, channels; - volatile int irq_ok; - volatile int intr_active, irq_mode; - /* duplicate audio fields for full duplex support */ - volatile int intr_active_16, irq_mode_16; - - /* Mixer fields */ - int *levels; - mixer_tab *iomap; - size_t iomap_sz; /* number or records in the iomap table */ - int mixer_caps, recmask, outmask, supported_devices; - int supported_rec_devices, supported_out_devices; - int my_mixerdev; - int sbmixnum; - - /* Audio fields */ - unsigned long trg_buf; - int trigger_bits; - int trg_bytes; - int trg_intrflag; - int trg_restart; - /* duplicate audio fields for full duplex support */ - unsigned long trg_buf_16; - int trigger_bits_16; - int trg_bytes_16; - int trg_intrflag_16; - int trg_restart_16; - - unsigned char tconst; - - /* MIDI fields */ - int my_mididev; - int input_opened; - int midi_broken; - void (*midi_input_intr) (int dev, unsigned char data); - void *midi_irq_cookie; /* IRQ cookie for the midi */ - - spinlock_t lock; - - struct sb_module_options sbmo; /* Module options */ - - } sb_devc; - -/* - * PCI card types - */ - -#define SB_PCI_ESSMAESTRO 1 /* ESS Maestro Legacy */ -#define SB_PCI_YAMAHA 2 /* Yamaha Legacy */ - -/* - * Functions - */ - -int sb_dsp_command (sb_devc *devc, unsigned char val); -int sb_dsp_get_byte(sb_devc * devc); -int sb_dsp_reset (sb_devc *devc); -void sb_setmixer (sb_devc *devc, unsigned int port, unsigned int value); -unsigned int sb_getmixer (sb_devc *devc, unsigned int port); -int sb_dsp_detect (struct address_info *hw_config, int pci, int pciio, struct sb_module_options *sbmo); -int sb_dsp_init (struct address_info *hw_config, struct module *owner); -void sb_dsp_unload(struct address_info *hw_config, int sbmpu); -int sb_mixer_init(sb_devc *devc, struct module *owner); -void sb_mixer_unload(sb_devc *devc); -void sb_mixer_set_stereo (sb_devc *devc, int mode); -void smw_mixer_init(sb_devc *devc); -void sb_dsp_midi_init (sb_devc *devc, struct module *owner); -void sb_audio_init (sb_devc *devc, char *name, struct module *owner); -void sb_midi_interrupt (sb_devc *devc); -void sb_chgmixer (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val); -int sb_common_mixer_set(sb_devc * devc, int dev, int left, int right); - -int sb_audio_open(int dev, int mode); -void sb_audio_close(int dev); - -/* From sb_common.c */ -void sb_dsp_disable_midi(int port); -int probe_sbmpu (struct address_info *hw_config, struct module *owner); -void unload_sbmpu (struct address_info *hw_config); - -void unload_sb16(struct address_info *hw_info); -void unload_sb16midi(struct address_info *hw_info); diff --git a/sound/oss/sb_audio.c b/sound/oss/sb_audio.c deleted file mode 100644 index dc91072f4d82..000000000000 --- a/sound/oss/sb_audio.c +++ /dev/null @@ -1,1097 +0,0 @@ -/* - * sound/oss/sb_audio.c - * - * Audio routines for Sound Blaster compatible cards. - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * Changes - * Alan Cox : Formatting and clean ups - * - * Status - * Mostly working. Weird uart bug causing irq storms - * - * Daniel J. Rodriksson: Changes to make sb16 work full duplex. - * Maybe other 16 bit cards in this code could behave - * the same. - * Chris Rankin: Use spinlocks instead of CLI/STI - */ - -#include <linux/spinlock.h> - -#include "sound_config.h" - -#include "sb_mixer.h" -#include "sb.h" - -#include "sb_ess.h" - -int sb_audio_open(int dev, int mode) -{ - sb_devc *devc = audio_devs[dev]->devc; - unsigned long flags; - - if (devc == NULL) - { - printk(KERN_ERR "Sound Blaster: incomplete initialization.\n"); - return -ENXIO; - } - if (devc->caps & SB_NO_RECORDING && mode & OPEN_READ) - { - if (mode == OPEN_READ) - return -EPERM; - } - spin_lock_irqsave(&devc->lock, flags); - if (devc->opened) - { - spin_unlock_irqrestore(&devc->lock, flags); - return -EBUSY; - } - if (devc->dma16 != -1 && devc->dma16 != devc->dma8 && !devc->duplex) - { - if (sound_open_dma(devc->dma16, "Sound Blaster 16 bit")) - { - spin_unlock_irqrestore(&devc->lock, flags); - return -EBUSY; - } - } - devc->opened = mode; - spin_unlock_irqrestore(&devc->lock, flags); - - devc->irq_mode = IMODE_NONE; - devc->irq_mode_16 = IMODE_NONE; - devc->fullduplex = devc->duplex && - ((mode & OPEN_READ) && (mode & OPEN_WRITE)); - sb_dsp_reset(devc); - - /* At first glance this check isn't enough, some ESS chips might not - * have a RECLEV. However if they don't common_mixer_set will refuse - * cause devc->iomap has no register mapping for RECLEV - */ - if (devc->model == MDL_ESS) ess_mixer_reload (devc, SOUND_MIXER_RECLEV); - - /* The ALS007 seems to require that the DSP be removed from the output */ - /* in order for recording to be activated properly. This is done by */ - /* setting the appropriate bits of the output control register 4ch to */ - /* zero. This code assumes that the output control registers are not */ - /* used anywhere else and therefore the DSP bits are *always* ON for */ - /* output and OFF for sampling. */ - - if (devc->submodel == SUBMDL_ALS007) - { - if (mode & OPEN_READ) - sb_setmixer(devc,ALS007_OUTPUT_CTRL2, - sb_getmixer(devc,ALS007_OUTPUT_CTRL2) & 0xf9); - else - sb_setmixer(devc,ALS007_OUTPUT_CTRL2, - sb_getmixer(devc,ALS007_OUTPUT_CTRL2) | 0x06); - } - return 0; -} - -void sb_audio_close(int dev) -{ - sb_devc *devc = audio_devs[dev]->devc; - - /* fix things if mmap turned off fullduplex */ - if(devc->duplex - && !devc->fullduplex - && (devc->opened & OPEN_READ) && (devc->opened & OPEN_WRITE)) - swap(audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_in); - - audio_devs[dev]->dmap_out->dma = devc->dma8; - audio_devs[dev]->dmap_in->dma = ( devc->duplex ) ? - devc->dma16 : devc->dma8; - - if (devc->dma16 != -1 && devc->dma16 != devc->dma8 && !devc->duplex) - sound_close_dma(devc->dma16); - - /* For ALS007, turn DSP output back on if closing the device for read */ - - if ((devc->submodel == SUBMDL_ALS007) && (devc->opened & OPEN_READ)) - { - sb_setmixer(devc,ALS007_OUTPUT_CTRL2, - sb_getmixer(devc,ALS007_OUTPUT_CTRL2) | 0x06); - } - devc->opened = 0; -} - -static void sb_set_output_parms(int dev, unsigned long buf, int nr_bytes, - int intrflag) -{ - sb_devc *devc = audio_devs[dev]->devc; - - if (!devc->fullduplex || devc->bits == AFMT_S16_LE) - { - devc->trg_buf = buf; - devc->trg_bytes = nr_bytes; - devc->trg_intrflag = intrflag; - devc->irq_mode = IMODE_OUTPUT; - } - else - { - devc->trg_buf_16 = buf; - devc->trg_bytes_16 = nr_bytes; - devc->trg_intrflag_16 = intrflag; - devc->irq_mode_16 = IMODE_OUTPUT; - } -} - -static void sb_set_input_parms(int dev, unsigned long buf, int count, int intrflag) -{ - sb_devc *devc = audio_devs[dev]->devc; - - if (!devc->fullduplex || devc->bits != AFMT_S16_LE) - { - devc->trg_buf = buf; - devc->trg_bytes = count; - devc->trg_intrflag = intrflag; - devc->irq_mode = IMODE_INPUT; - } - else - { - devc->trg_buf_16 = buf; - devc->trg_bytes_16 = count; - devc->trg_intrflag_16 = intrflag; - devc->irq_mode_16 = IMODE_INPUT; - } -} - -/* - * SB1.x compatible routines - */ - -static void sb1_audio_output_block(int dev, unsigned long buf, int nr_bytes, int intrflag) -{ - unsigned long flags; - int count = nr_bytes; - sb_devc *devc = audio_devs[dev]->devc; - - /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */ - - if (audio_devs[dev]->dmap_out->dma > 3) - count >>= 1; - count--; - - devc->irq_mode = IMODE_OUTPUT; - - spin_lock_irqsave(&devc->lock, flags); - if (sb_dsp_command(devc, 0x14)) /* 8 bit DAC using DMA */ - { - sb_dsp_command(devc, (unsigned char) (count & 0xff)); - sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff)); - } - else - printk(KERN_WARNING "Sound Blaster: unable to start DAC.\n"); - spin_unlock_irqrestore(&devc->lock, flags); - devc->intr_active = 1; -} - -static void sb1_audio_start_input(int dev, unsigned long buf, int nr_bytes, int intrflag) -{ - unsigned long flags; - int count = nr_bytes; - sb_devc *devc = audio_devs[dev]->devc; - - /* - * Start a DMA input to the buffer pointed by dmaqtail - */ - - /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */ - - if (audio_devs[dev]->dmap_out->dma > 3) - count >>= 1; - count--; - - devc->irq_mode = IMODE_INPUT; - - spin_lock_irqsave(&devc->lock, flags); - if (sb_dsp_command(devc, 0x24)) /* 8 bit ADC using DMA */ - { - sb_dsp_command(devc, (unsigned char) (count & 0xff)); - sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff)); - } - else - printk(KERN_ERR "Sound Blaster: unable to start ADC.\n"); - spin_unlock_irqrestore(&devc->lock, flags); - - devc->intr_active = 1; -} - -static void sb1_audio_trigger(int dev, int bits) -{ - sb_devc *devc = audio_devs[dev]->devc; - - bits &= devc->irq_mode; - - if (!bits) - sb_dsp_command(devc, 0xd0); /* Halt DMA */ - else - { - switch (devc->irq_mode) - { - case IMODE_INPUT: - sb1_audio_start_input(dev, devc->trg_buf, devc->trg_bytes, - devc->trg_intrflag); - break; - - case IMODE_OUTPUT: - sb1_audio_output_block(dev, devc->trg_buf, devc->trg_bytes, - devc->trg_intrflag); - break; - } - } - devc->trigger_bits = bits; -} - -static int sb1_audio_prepare_for_input(int dev, int bsize, int bcount) -{ - sb_devc *devc = audio_devs[dev]->devc; - unsigned long flags; - - spin_lock_irqsave(&devc->lock, flags); - if (sb_dsp_command(devc, 0x40)) - sb_dsp_command(devc, devc->tconst); - sb_dsp_command(devc, DSP_CMD_SPKOFF); - spin_unlock_irqrestore(&devc->lock, flags); - - devc->trigger_bits = 0; - return 0; -} - -static int sb1_audio_prepare_for_output(int dev, int bsize, int bcount) -{ - sb_devc *devc = audio_devs[dev]->devc; - unsigned long flags; - - spin_lock_irqsave(&devc->lock, flags); - if (sb_dsp_command(devc, 0x40)) - sb_dsp_command(devc, devc->tconst); - sb_dsp_command(devc, DSP_CMD_SPKON); - spin_unlock_irqrestore(&devc->lock, flags); - devc->trigger_bits = 0; - return 0; -} - -static int sb1_audio_set_speed(int dev, int speed) -{ - int max_speed = 23000; - sb_devc *devc = audio_devs[dev]->devc; - int tmp; - - if (devc->opened & OPEN_READ) - max_speed = 13000; - - if (speed > 0) - { - if (speed < 4000) - speed = 4000; - - if (speed > max_speed) - speed = max_speed; - - devc->tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff; - tmp = 256 - devc->tconst; - speed = (1000000 + tmp / 2) / tmp; - - devc->speed = speed; - } - return devc->speed; -} - -static short sb1_audio_set_channels(int dev, short channels) -{ - sb_devc *devc = audio_devs[dev]->devc; - return devc->channels = 1; -} - -static unsigned int sb1_audio_set_bits(int dev, unsigned int bits) -{ - sb_devc *devc = audio_devs[dev]->devc; - return devc->bits = 8; -} - -static void sb1_audio_halt_xfer(int dev) -{ - unsigned long flags; - sb_devc *devc = audio_devs[dev]->devc; - - spin_lock_irqsave(&devc->lock, flags); - sb_dsp_reset(devc); - spin_unlock_irqrestore(&devc->lock, flags); -} - -/* - * SB 2.0 and SB 2.01 compatible routines - */ - -static void sb20_audio_output_block(int dev, unsigned long buf, int nr_bytes, - int intrflag) -{ - unsigned long flags; - int count = nr_bytes; - sb_devc *devc = audio_devs[dev]->devc; - unsigned char cmd; - - /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */ - - if (audio_devs[dev]->dmap_out->dma > 3) - count >>= 1; - count--; - - devc->irq_mode = IMODE_OUTPUT; - - spin_lock_irqsave(&devc->lock, flags); - if (sb_dsp_command(devc, 0x48)) /* DSP Block size */ - { - sb_dsp_command(devc, (unsigned char) (count & 0xff)); - sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff)); - - if (devc->speed * devc->channels <= 23000) - cmd = 0x1c; /* 8 bit PCM output */ - else - cmd = 0x90; /* 8 bit high speed PCM output (SB2.01/Pro) */ - - if (!sb_dsp_command(devc, cmd)) - printk(KERN_ERR "Sound Blaster: unable to start DAC.\n"); - } - else - printk(KERN_ERR "Sound Blaster: unable to start DAC.\n"); - spin_unlock_irqrestore(&devc->lock, flags); - devc->intr_active = 1; -} - -static void sb20_audio_start_input(int dev, unsigned long buf, int nr_bytes, int intrflag) -{ - unsigned long flags; - int count = nr_bytes; - sb_devc *devc = audio_devs[dev]->devc; - unsigned char cmd; - - /* - * Start a DMA input to the buffer pointed by dmaqtail - */ - - /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */ - - if (audio_devs[dev]->dmap_out->dma > 3) - count >>= 1; - count--; - - devc->irq_mode = IMODE_INPUT; - - spin_lock_irqsave(&devc->lock, flags); - if (sb_dsp_command(devc, 0x48)) /* DSP Block size */ - { - sb_dsp_command(devc, (unsigned char) (count & 0xff)); - sb_dsp_command(devc, (unsigned char) ((count >> 8) & 0xff)); - - if (devc->speed * devc->channels <= (devc->major == 3 ? 23000 : 13000)) - cmd = 0x2c; /* 8 bit PCM input */ - else - cmd = 0x98; /* 8 bit high speed PCM input (SB2.01/Pro) */ - - if (!sb_dsp_command(devc, cmd)) - printk(KERN_ERR "Sound Blaster: unable to start ADC.\n"); - } - else - printk(KERN_ERR "Sound Blaster: unable to start ADC.\n"); - spin_unlock_irqrestore(&devc->lock, flags); - devc->intr_active = 1; -} - -static void sb20_audio_trigger(int dev, int bits) -{ - sb_devc *devc = audio_devs[dev]->devc; - bits &= devc->irq_mode; - - if (!bits) - sb_dsp_command(devc, 0xd0); /* Halt DMA */ - else - { - switch (devc->irq_mode) - { - case IMODE_INPUT: - sb20_audio_start_input(dev, devc->trg_buf, devc->trg_bytes, - devc->trg_intrflag); - break; - - case IMODE_OUTPUT: - sb20_audio_output_block(dev, devc->trg_buf, devc->trg_bytes, - devc->trg_intrflag); - break; - } - } - devc->trigger_bits = bits; -} - -/* - * SB2.01 specific speed setup - */ - -static int sb201_audio_set_speed(int dev, int speed) -{ - sb_devc *devc = audio_devs[dev]->devc; - int tmp; - int s; - - if (speed > 0) - { - if (speed < 4000) - speed = 4000; - if (speed > 44100) - speed = 44100; - if (devc->opened & OPEN_READ && speed > 15000) - speed = 15000; - s = speed * devc->channels; - devc->tconst = (256 - ((1000000 + s / 2) / s)) & 0xff; - tmp = 256 - devc->tconst; - speed = ((1000000 + tmp / 2) / tmp) / devc->channels; - - devc->speed = speed; - } - return devc->speed; -} - -/* - * SB Pro specific routines - */ - -static int sbpro_audio_prepare_for_input(int dev, int bsize, int bcount) -{ /* For SB Pro and Jazz16 */ - sb_devc *devc = audio_devs[dev]->devc; - unsigned long flags; - unsigned char bits = 0; - - if (devc->dma16 >= 0 && devc->dma16 != devc->dma8) - audio_devs[dev]->dmap_out->dma = audio_devs[dev]->dmap_in->dma = - devc->bits == 16 ? devc->dma16 : devc->dma8; - - if (devc->model == MDL_JAZZ || devc->model == MDL_SMW) - if (devc->bits == AFMT_S16_LE) - bits = 0x04; /* 16 bit mode */ - - spin_lock_irqsave(&devc->lock, flags); - if (sb_dsp_command(devc, 0x40)) - sb_dsp_command(devc, devc->tconst); - sb_dsp_command(devc, DSP_CMD_SPKOFF); - if (devc->channels == 1) - sb_dsp_command(devc, 0xa0 | bits); /* Mono input */ - else - sb_dsp_command(devc, 0xa8 | bits); /* Stereo input */ - spin_unlock_irqrestore(&devc->lock, flags); - - devc->trigger_bits = 0; - return 0; -} - -static int sbpro_audio_prepare_for_output(int dev, int bsize, int bcount) -{ /* For SB Pro and Jazz16 */ - sb_devc *devc = audio_devs[dev]->devc; - unsigned long flags; - unsigned char tmp; - unsigned char bits = 0; - - if (devc->dma16 >= 0 && devc->dma16 != devc->dma8) - audio_devs[dev]->dmap_out->dma = audio_devs[dev]->dmap_in->dma = devc->bits == 16 ? devc->dma16 : devc->dma8; - if (devc->model == MDL_SBPRO) - sb_mixer_set_stereo(devc, devc->channels == 2); - - spin_lock_irqsave(&devc->lock, flags); - if (sb_dsp_command(devc, 0x40)) - sb_dsp_command(devc, devc->tconst); - sb_dsp_command(devc, DSP_CMD_SPKON); - - if (devc->model == MDL_JAZZ || devc->model == MDL_SMW) - { - if (devc->bits == AFMT_S16_LE) - bits = 0x04; /* 16 bit mode */ - - if (devc->channels == 1) - sb_dsp_command(devc, 0xa0 | bits); /* Mono output */ - else - sb_dsp_command(devc, 0xa8 | bits); /* Stereo output */ - spin_unlock_irqrestore(&devc->lock, flags); - } - else - { - spin_unlock_irqrestore(&devc->lock, flags); - tmp = sb_getmixer(devc, 0x0e); - if (devc->channels == 1) - tmp &= ~0x02; - else - tmp |= 0x02; - sb_setmixer(devc, 0x0e, tmp); - } - devc->trigger_bits = 0; - return 0; -} - -static int sbpro_audio_set_speed(int dev, int speed) -{ - sb_devc *devc = audio_devs[dev]->devc; - - if (speed > 0) - { - if (speed < 4000) - speed = 4000; - if (speed > 44100) - speed = 44100; - if (devc->channels > 1 && speed > 22050) - speed = 22050; - sb201_audio_set_speed(dev, speed); - } - return devc->speed; -} - -static short sbpro_audio_set_channels(int dev, short channels) -{ - sb_devc *devc = audio_devs[dev]->devc; - - if (channels == 1 || channels == 2) - { - if (channels != devc->channels) - { - devc->channels = channels; - if (devc->model == MDL_SBPRO && devc->channels == 2) - sbpro_audio_set_speed(dev, devc->speed); - } - } - return devc->channels; -} - -static int jazz16_audio_set_speed(int dev, int speed) -{ - sb_devc *devc = audio_devs[dev]->devc; - - if (speed > 0) - { - int tmp; - int s; - - if (speed < 5000) - speed = 5000; - if (speed > 44100) - speed = 44100; - - s = speed * devc->channels; - - devc->tconst = (256 - ((1000000 + s / 2) / s)) & 0xff; - - tmp = 256 - devc->tconst; - speed = ((1000000 + tmp / 2) / tmp) / devc->channels; - - devc->speed = speed; - } - return devc->speed; -} - -/* - * SB16 specific routines - */ - -static int sb16_audio_set_speed(int dev, int speed) -{ - sb_devc *devc = audio_devs[dev]->devc; - int max_speed = devc->submodel == SUBMDL_ALS100 ? 48000 : 44100; - - if (speed > 0) - { - if (speed < 5000) - speed = 5000; - - if (speed > max_speed) - speed = max_speed; - - devc->speed = speed; - } - return devc->speed; -} - -static unsigned int sb16_audio_set_bits(int dev, unsigned int bits) -{ - sb_devc *devc = audio_devs[dev]->devc; - - if (bits != 0) - { - if (bits == AFMT_U8 || bits == AFMT_S16_LE) - devc->bits = bits; - else - devc->bits = AFMT_U8; - } - - return devc->bits; -} - -static int sb16_audio_prepare_for_input(int dev, int bsize, int bcount) -{ - sb_devc *devc = audio_devs[dev]->devc; - - if (!devc->fullduplex) - { - audio_devs[dev]->dmap_out->dma = - audio_devs[dev]->dmap_in->dma = - devc->bits == AFMT_S16_LE ? - devc->dma16 : devc->dma8; - } - else if (devc->bits == AFMT_S16_LE) - { - audio_devs[dev]->dmap_out->dma = devc->dma8; - audio_devs[dev]->dmap_in->dma = devc->dma16; - } - else - { - audio_devs[dev]->dmap_out->dma = devc->dma16; - audio_devs[dev]->dmap_in->dma = devc->dma8; - } - - devc->trigger_bits = 0; - return 0; -} - -static int sb16_audio_prepare_for_output(int dev, int bsize, int bcount) -{ - sb_devc *devc = audio_devs[dev]->devc; - - if (!devc->fullduplex) - { - audio_devs[dev]->dmap_out->dma = - audio_devs[dev]->dmap_in->dma = - devc->bits == AFMT_S16_LE ? - devc->dma16 : devc->dma8; - } - else if (devc->bits == AFMT_S16_LE) - { - audio_devs[dev]->dmap_out->dma = devc->dma8; - audio_devs[dev]->dmap_in->dma = devc->dma16; - } - else - { - audio_devs[dev]->dmap_out->dma = devc->dma16; - audio_devs[dev]->dmap_in->dma = devc->dma8; - } - - devc->trigger_bits = 0; - return 0; -} - -static void sb16_audio_output_block(int dev, unsigned long buf, int count, - int intrflag) -{ - unsigned long flags, cnt; - sb_devc *devc = audio_devs[dev]->devc; - unsigned long bits; - - if (!devc->fullduplex || devc->bits == AFMT_S16_LE) - { - devc->irq_mode = IMODE_OUTPUT; - devc->intr_active = 1; - } - else - { - devc->irq_mode_16 = IMODE_OUTPUT; - devc->intr_active_16 = 1; - } - - /* save value */ - spin_lock_irqsave(&devc->lock, flags); - bits = devc->bits; - if (devc->fullduplex) - devc->bits = (devc->bits == AFMT_S16_LE) ? - AFMT_U8 : AFMT_S16_LE; - spin_unlock_irqrestore(&devc->lock, flags); - - cnt = count; - if (devc->bits == AFMT_S16_LE) - cnt >>= 1; - cnt--; - - spin_lock_irqsave(&devc->lock, flags); - - /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */ - - sb_dsp_command(devc, 0x41); - sb_dsp_command(devc, (unsigned char) ((devc->speed >> 8) & 0xff)); - sb_dsp_command(devc, (unsigned char) (devc->speed & 0xff)); - - sb_dsp_command(devc, (devc->bits == AFMT_S16_LE ? 0xb6 : 0xc6)); - sb_dsp_command(devc, ((devc->channels == 2 ? 0x20 : 0) + - (devc->bits == AFMT_S16_LE ? 0x10 : 0))); - sb_dsp_command(devc, (unsigned char) (cnt & 0xff)); - sb_dsp_command(devc, (unsigned char) (cnt >> 8)); - - /* restore real value after all programming */ - devc->bits = bits; - spin_unlock_irqrestore(&devc->lock, flags); -} - - -/* - * This fails on the Cyrix MediaGX. If you don't have the DMA enabled - * before the first sample arrives it locks up. However even if you - * do enable the DMA in time you just get DMA timeouts and missing - * interrupts and stuff, so for now I've not bothered fixing this either. - */ - -static void sb16_audio_start_input(int dev, unsigned long buf, int count, int intrflag) -{ - unsigned long flags, cnt; - sb_devc *devc = audio_devs[dev]->devc; - - if (!devc->fullduplex || devc->bits != AFMT_S16_LE) - { - devc->irq_mode = IMODE_INPUT; - devc->intr_active = 1; - } - else - { - devc->irq_mode_16 = IMODE_INPUT; - devc->intr_active_16 = 1; - } - - cnt = count; - if (devc->bits == AFMT_S16_LE) - cnt >>= 1; - cnt--; - - spin_lock_irqsave(&devc->lock, flags); - - /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */ - - sb_dsp_command(devc, 0x42); - sb_dsp_command(devc, (unsigned char) ((devc->speed >> 8) & 0xff)); - sb_dsp_command(devc, (unsigned char) (devc->speed & 0xff)); - - sb_dsp_command(devc, (devc->bits == AFMT_S16_LE ? 0xbe : 0xce)); - sb_dsp_command(devc, ((devc->channels == 2 ? 0x20 : 0) + - (devc->bits == AFMT_S16_LE ? 0x10 : 0))); - sb_dsp_command(devc, (unsigned char) (cnt & 0xff)); - sb_dsp_command(devc, (unsigned char) (cnt >> 8)); - - spin_unlock_irqrestore(&devc->lock, flags); -} - -static void sb16_audio_trigger(int dev, int bits) -{ - sb_devc *devc = audio_devs[dev]->devc; - - int bits_16 = bits & devc->irq_mode_16; - bits &= devc->irq_mode; - - if (!bits && !bits_16) - sb_dsp_command(devc, 0xd0); /* Halt DMA */ - else - { - if (bits) - { - switch (devc->irq_mode) - { - case IMODE_INPUT: - sb16_audio_start_input(dev, - devc->trg_buf, - devc->trg_bytes, - devc->trg_intrflag); - break; - - case IMODE_OUTPUT: - sb16_audio_output_block(dev, - devc->trg_buf, - devc->trg_bytes, - devc->trg_intrflag); - break; - } - } - if (bits_16) - { - switch (devc->irq_mode_16) - { - case IMODE_INPUT: - sb16_audio_start_input(dev, - devc->trg_buf_16, - devc->trg_bytes_16, - devc->trg_intrflag_16); - break; - - case IMODE_OUTPUT: - sb16_audio_output_block(dev, - devc->trg_buf_16, - devc->trg_bytes_16, - devc->trg_intrflag_16); - break; - } - } - } - - devc->trigger_bits = bits | bits_16; -} - -static unsigned char lbuf8[2048]; -static signed short *lbuf16 = (signed short *)lbuf8; -#define LBUFCOPYSIZE 1024 -static void -sb16_copy_from_user(int dev, - char *localbuf, int localoffs, - const char __user *userbuf, int useroffs, - int max_in, int max_out, - int *used, int *returned, - int len) -{ - sb_devc *devc = audio_devs[dev]->devc; - int i, c, p, locallen; - unsigned char *buf8; - signed short *buf16; - - /* if not duplex no conversion */ - if (!devc->fullduplex) - { - if (copy_from_user(localbuf + localoffs, - userbuf + useroffs, len)) - return; - *used = len; - *returned = len; - } - else if (devc->bits == AFMT_S16_LE) - { - /* 16 -> 8 */ - /* max_in >> 1, max number of samples in ( 16 bits ) */ - /* max_out, max number of samples out ( 8 bits ) */ - /* len, number of samples that will be taken ( 16 bits )*/ - /* c, count of samples remaining in buffer ( 16 bits )*/ - /* p, count of samples already processed ( 16 bits )*/ - len = ( (max_in >> 1) > max_out) ? max_out : (max_in >> 1); - c = len; - p = 0; - buf8 = (unsigned char *)(localbuf + localoffs); - while (c) - { - locallen = (c >= LBUFCOPYSIZE ? LBUFCOPYSIZE : c); - /* << 1 in order to get 16 bit samples */ - if (copy_from_user(lbuf16, - userbuf + useroffs + (p << 1), - locallen << 1)) - return; - for (i = 0; i < locallen; i++) - { - buf8[p+i] = ~((lbuf16[i] >> 8) & 0xff) ^ 0x80; - } - c -= locallen; p += locallen; - } - /* used = ( samples * 16 bits size ) */ - *used = max_in > ( max_out << 1) ? (max_out << 1) : max_in; - /* returned = ( samples * 8 bits size ) */ - *returned = len; - } - else - { - /* 8 -> 16 */ - /* max_in, max number of samples in ( 8 bits ) */ - /* max_out >> 1, max number of samples out ( 16 bits ) */ - /* len, number of samples that will be taken ( 8 bits )*/ - /* c, count of samples remaining in buffer ( 8 bits )*/ - /* p, count of samples already processed ( 8 bits )*/ - len = max_in > (max_out >> 1) ? (max_out >> 1) : max_in; - c = len; - p = 0; - buf16 = (signed short *)(localbuf + localoffs); - while (c) - { - locallen = (c >= LBUFCOPYSIZE ? LBUFCOPYSIZE : c); - if (copy_from_user(lbuf8, - userbuf+useroffs + p, - locallen)) - return; - for (i = 0; i < locallen; i++) - { - buf16[p+i] = (~lbuf8[i] ^ 0x80) << 8; - } - c -= locallen; p += locallen; - } - /* used = ( samples * 8 bits size ) */ - *used = len; - /* returned = ( samples * 16 bits size ) */ - *returned = len << 1; - } -} - -static void -sb16_audio_mmap(int dev) -{ - sb_devc *devc = audio_devs[dev]->devc; - devc->fullduplex = 0; -} - -static struct audio_driver sb1_audio_driver = /* SB1.x */ -{ - .owner = THIS_MODULE, - .open = sb_audio_open, - .close = sb_audio_close, - .output_block = sb_set_output_parms, - .start_input = sb_set_input_parms, - .prepare_for_input = sb1_audio_prepare_for_input, - .prepare_for_output = sb1_audio_prepare_for_output, - .halt_io = sb1_audio_halt_xfer, - .trigger = sb1_audio_trigger, - .set_speed = sb1_audio_set_speed, - .set_bits = sb1_audio_set_bits, - .set_channels = sb1_audio_set_channels -}; - -static struct audio_driver sb20_audio_driver = /* SB2.0 */ -{ - .owner = THIS_MODULE, - .open = sb_audio_open, - .close = sb_audio_close, - .output_block = sb_set_output_parms, - .start_input = sb_set_input_parms, - .prepare_for_input = sb1_audio_prepare_for_input, - .prepare_for_output = sb1_audio_prepare_for_output, - .halt_io = sb1_audio_halt_xfer, - .trigger = sb20_audio_trigger, - .set_speed = sb1_audio_set_speed, - .set_bits = sb1_audio_set_bits, - .set_channels = sb1_audio_set_channels -}; - -static struct audio_driver sb201_audio_driver = /* SB2.01 */ -{ - .owner = THIS_MODULE, - .open = sb_audio_open, - .close = sb_audio_close, - .output_block = sb_set_output_parms, - .start_input = sb_set_input_parms, - .prepare_for_input = sb1_audio_prepare_for_input, - .prepare_for_output = sb1_audio_prepare_for_output, - .halt_io = sb1_audio_halt_xfer, - .trigger = sb20_audio_trigger, - .set_speed = sb201_audio_set_speed, - .set_bits = sb1_audio_set_bits, - .set_channels = sb1_audio_set_channels -}; - -static struct audio_driver sbpro_audio_driver = /* SB Pro */ -{ - .owner = THIS_MODULE, - .open = sb_audio_open, - .close = sb_audio_close, - .output_block = sb_set_output_parms, - .start_input = sb_set_input_parms, - .prepare_for_input = sbpro_audio_prepare_for_input, - .prepare_for_output = sbpro_audio_prepare_for_output, - .halt_io = sb1_audio_halt_xfer, - .trigger = sb20_audio_trigger, - .set_speed = sbpro_audio_set_speed, - .set_bits = sb1_audio_set_bits, - .set_channels = sbpro_audio_set_channels -}; - -static struct audio_driver jazz16_audio_driver = /* Jazz16 and SM Wave */ -{ - .owner = THIS_MODULE, - .open = sb_audio_open, - .close = sb_audio_close, - .output_block = sb_set_output_parms, - .start_input = sb_set_input_parms, - .prepare_for_input = sbpro_audio_prepare_for_input, - .prepare_for_output = sbpro_audio_prepare_for_output, - .halt_io = sb1_audio_halt_xfer, - .trigger = sb20_audio_trigger, - .set_speed = jazz16_audio_set_speed, - .set_bits = sb16_audio_set_bits, - .set_channels = sbpro_audio_set_channels -}; - -static struct audio_driver sb16_audio_driver = /* SB16 */ -{ - .owner = THIS_MODULE, - .open = sb_audio_open, - .close = sb_audio_close, - .output_block = sb_set_output_parms, - .start_input = sb_set_input_parms, - .prepare_for_input = sb16_audio_prepare_for_input, - .prepare_for_output = sb16_audio_prepare_for_output, - .halt_io = sb1_audio_halt_xfer, - .copy_user = sb16_copy_from_user, - .trigger = sb16_audio_trigger, - .set_speed = sb16_audio_set_speed, - .set_bits = sb16_audio_set_bits, - .set_channels = sbpro_audio_set_channels, - .mmap = sb16_audio_mmap -}; - -void sb_audio_init(sb_devc * devc, char *name, struct module *owner) -{ - int audio_flags = 0; - int format_mask = AFMT_U8; - - struct audio_driver *driver = &sb1_audio_driver; - - switch (devc->model) - { - case MDL_SB1: /* SB1.0 or SB 1.5 */ - DDB(printk("Will use standard SB1.x driver\n")); - audio_flags = DMA_HARDSTOP; - break; - - case MDL_SB2: - DDB(printk("Will use SB2.0 driver\n")); - audio_flags = DMA_AUTOMODE; - driver = &sb20_audio_driver; - break; - - case MDL_SB201: - DDB(printk("Will use SB2.01 (high speed) driver\n")); - audio_flags = DMA_AUTOMODE; - driver = &sb201_audio_driver; - break; - - case MDL_JAZZ: - case MDL_SMW: - DDB(printk("Will use Jazz16 driver\n")); - audio_flags = DMA_AUTOMODE; - format_mask |= AFMT_S16_LE; - driver = &jazz16_audio_driver; - break; - - case MDL_ESS: - DDB(printk("Will use ESS ES688/1688 driver\n")); - driver = ess_audio_init (devc, &audio_flags, &format_mask); - break; - - case MDL_SB16: - DDB(printk("Will use SB16 driver\n")); - audio_flags = DMA_AUTOMODE; - format_mask |= AFMT_S16_LE; - if (devc->dma8 != devc->dma16 && devc->dma16 != -1) - { - audio_flags |= DMA_DUPLEX; - devc->duplex = 1; - } - driver = &sb16_audio_driver; - break; - - default: - DDB(printk("Will use SB Pro driver\n")); - audio_flags = DMA_AUTOMODE; - driver = &sbpro_audio_driver; - } - - if (owner) - driver->owner = owner; - - if ((devc->dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, - name,driver, sizeof(struct audio_driver), - audio_flags, format_mask, devc, - devc->dma8, - devc->duplex ? devc->dma16 : devc->dma8)) < 0) - { - printk(KERN_ERR "Sound Blaster: unable to install audio.\n"); - return; - } - audio_devs[devc->dev]->mixer_dev = devc->my_mixerdev; - audio_devs[devc->dev]->min_fragment = 5; -} diff --git a/sound/oss/sb_card.c b/sound/oss/sb_card.c deleted file mode 100644 index 2a92cfe6cfe9..000000000000 --- a/sound/oss/sb_card.c +++ /dev/null @@ -1,354 +0,0 @@ -/* - * sound/oss/sb_card.c - * - * Detection routine for the ISA Sound Blaster and compatible sound - * cards. - * - * This file is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this - * software for more info. - * - * This is a complete rewrite of the detection routines. This was - * prompted by the PnP API change during v2.5 and the ugly state the - * code was in. - * - * Copyright (C) by Paul Laufer 2002. Based on code originally by - * Hannu Savolainen which was modified by many others over the - * years. Authors specifically mentioned in the previous version were: - * Daniel Stone, Alessandro Zummo, Jeff Garzik, Arnaldo Carvalho de - * Melo, Daniel Church, and myself. - * - * 02-05-2003 Original Release, Paul Laufer <paul@laufernet.com> - * 02-07-2003 Bug made it into first release. Take two. - */ - -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/slab.h> -#include <linux/init.h> -#include "sound_config.h" -#include "sb_mixer.h" -#include "sb.h" -#ifdef CONFIG_PNP -#include <linux/pnp.h> -#endif /* CONFIG_PNP */ -#include "sb_card.h" - -MODULE_DESCRIPTION("OSS Soundblaster ISA PnP and legacy sound driver"); -MODULE_LICENSE("GPL"); - -extern void *smw_free; - -static int __initdata mpu_io = 0; -static int __initdata io = -1; -static int __initdata irq = -1; -static int __initdata dma = -1; -static int __initdata dma16 = -1; -static int __initdata type = 0; /* Can set this to a specific card type */ -static int __initdata esstype = 0; /* ESS chip type */ -static int __initdata acer = 0; /* Do acer notebook init? */ -static int __initdata sm_games = 0; /* Logitech soundman games? */ - -static struct sb_card_config *legacy = NULL; - -#ifdef CONFIG_PNP -static int pnp_registered; -static int __initdata pnp = 1; -/* -static int __initdata uart401 = 0; -*/ -#else -static int __initdata pnp = 0; -#endif - -module_param_hw(io, int, ioport, 000); -MODULE_PARM_DESC(io, "Soundblaster i/o base address (0x220,0x240,0x260,0x280)"); -module_param_hw(irq, int, irq, 000); -MODULE_PARM_DESC(irq, "IRQ (5,7,9,10)"); -module_param_hw(dma, int, dma, 000); -MODULE_PARM_DESC(dma, "8-bit DMA channel (0,1,3)"); -module_param_hw(dma16, int, dma, 000); -MODULE_PARM_DESC(dma16, "16-bit DMA channel (5,6,7)"); -module_param_hw(mpu_io, int, ioport, 000); -MODULE_PARM_DESC(mpu_io, "MPU base address"); -module_param(type, int, 000); -MODULE_PARM_DESC(type, "You can set this to specific card type (doesn't " \ - "work with pnp)"); -module_param(sm_games, int, 000); -MODULE_PARM_DESC(sm_games, "Enable support for Logitech soundman games " \ - "(doesn't work with pnp)"); -module_param(esstype, int, 000); -MODULE_PARM_DESC(esstype, "ESS chip type (doesn't work with pnp)"); -module_param(acer, int, 000); -MODULE_PARM_DESC(acer, "Set this to detect cards in some ACER notebooks "\ - "(doesn't work with pnp)"); - -#ifdef CONFIG_PNP -module_param(pnp, int, 000); -MODULE_PARM_DESC(pnp, "Went set to 0 will disable detection using PnP. "\ - "Default is 1.\n"); -/* Not done yet.... */ -/* -module_param(uart401, int, 000); -MODULE_PARM_DESC(uart401, "When set to 1, will attempt to detect and enable"\ - "the mpu on some clones"); -*/ -#endif /* CONFIG_PNP */ - -/* OSS subsystem card registration shared by PnP and legacy routines */ -static int sb_register_oss(struct sb_card_config *scc, struct sb_module_options *sbmo) -{ - if (!request_region(scc->conf.io_base, 16, "soundblaster")) { - printk(KERN_ERR "sb: ports busy.\n"); - kfree(scc); - return -EBUSY; - } - - if (!sb_dsp_detect(&scc->conf, 0, 0, sbmo)) { - release_region(scc->conf.io_base, 16); - printk(KERN_ERR "sb: Failed DSP Detect.\n"); - kfree(scc); - return -ENODEV; - } - if(!sb_dsp_init(&scc->conf, THIS_MODULE)) { - printk(KERN_ERR "sb: Failed DSP init.\n"); - kfree(scc); - return -ENODEV; - } - if(scc->mpucnf.io_base > 0) { - scc->mpu = 1; - printk(KERN_INFO "sb: Turning on MPU\n"); - if(!probe_sbmpu(&scc->mpucnf, THIS_MODULE)) - scc->mpu = 0; - } - - return 1; -} - -static void sb_unload(struct sb_card_config *scc) -{ - sb_dsp_unload(&scc->conf, 0); - if(scc->mpu) - unload_sbmpu(&scc->mpucnf); - kfree(scc); -} - -/* Register legacy card with OSS subsystem */ -static int __init sb_init_legacy(void) -{ - struct sb_module_options sbmo = {0}; - - if((legacy = kzalloc(sizeof(struct sb_card_config), GFP_KERNEL)) == NULL) { - printk(KERN_ERR "sb: Error: Could not allocate memory\n"); - return -ENOMEM; - } - - legacy->conf.io_base = io; - legacy->conf.irq = irq; - legacy->conf.dma = dma; - legacy->conf.dma2 = dma16; - legacy->conf.card_subtype = type; - - legacy->mpucnf.io_base = mpu_io; - legacy->mpucnf.irq = -1; - legacy->mpucnf.dma = -1; - legacy->mpucnf.dma2 = -1; - - sbmo.esstype = esstype; - sbmo.sm_games = sm_games; - sbmo.acer = acer; - - return sb_register_oss(legacy, &sbmo); -} - -#ifdef CONFIG_PNP - -/* Populate the OSS subsystem structures with information from PnP */ -static void sb_dev2cfg(struct pnp_dev *dev, struct sb_card_config *scc) -{ - scc->conf.io_base = -1; - scc->conf.irq = -1; - scc->conf.dma = -1; - scc->conf.dma2 = -1; - scc->mpucnf.io_base = -1; - scc->mpucnf.irq = -1; - scc->mpucnf.dma = -1; - scc->mpucnf.dma2 = -1; - - /* All clones layout their PnP tables differently and some use - different logical devices for the MPU */ - if(!strncmp("CTL",scc->card_id,3)) { - scc->conf.io_base = pnp_port_start(dev,0); - scc->conf.irq = pnp_irq(dev,0); - scc->conf.dma = pnp_dma(dev,0); - scc->conf.dma2 = pnp_dma(dev,1); - scc->mpucnf.io_base = pnp_port_start(dev,1); - return; - } - if(!strncmp("tBA",scc->card_id,3)) { - scc->conf.io_base = pnp_port_start(dev,0); - scc->conf.irq = pnp_irq(dev,0); - scc->conf.dma = pnp_dma(dev,0); - scc->conf.dma2 = pnp_dma(dev,1); - return; - } - if(!strncmp("ESS",scc->card_id,3)) { - scc->conf.io_base = pnp_port_start(dev,0); - scc->conf.irq = pnp_irq(dev,0); - scc->conf.dma = pnp_dma(dev,0); - scc->conf.dma2 = pnp_dma(dev,1); - scc->mpucnf.io_base = pnp_port_start(dev,2); - return; - } - if(!strncmp("CMI",scc->card_id,3)) { - scc->conf.io_base = pnp_port_start(dev,0); - scc->conf.irq = pnp_irq(dev,0); - scc->conf.dma = pnp_dma(dev,0); - scc->conf.dma2 = pnp_dma(dev,1); - return; - } - if(!strncmp("RWB",scc->card_id,3)) { - scc->conf.io_base = pnp_port_start(dev,0); - scc->conf.irq = pnp_irq(dev,0); - scc->conf.dma = pnp_dma(dev,0); - return; - } - if(!strncmp("ALS",scc->card_id,3)) { - if(!strncmp("ALS0007",scc->card_id,7)) { - scc->conf.io_base = pnp_port_start(dev,0); - scc->conf.irq = pnp_irq(dev,0); - scc->conf.dma = pnp_dma(dev,0); - } else { - scc->conf.io_base = pnp_port_start(dev,0); - scc->conf.irq = pnp_irq(dev,0); - scc->conf.dma = pnp_dma(dev,1); - scc->conf.dma2 = pnp_dma(dev,0); - } - return; - } - if(!strncmp("RTL",scc->card_id,3)) { - scc->conf.io_base = pnp_port_start(dev,0); - scc->conf.irq = pnp_irq(dev,0); - scc->conf.dma = pnp_dma(dev,1); - scc->conf.dma2 = pnp_dma(dev,0); - } -} - -static unsigned int sb_pnp_devices; - -/* Probe callback function for the PnP API */ -static int sb_pnp_probe(struct pnp_card_link *card, const struct pnp_card_device_id *card_id) -{ - struct sb_card_config *scc; - struct sb_module_options sbmo = {0}; /* Default to 0 for PnP */ - struct pnp_dev *dev = pnp_request_card_device(card, card_id->devs[0].id, NULL); - - if(!dev){ - return -EBUSY; - } - - if((scc = kzalloc(sizeof(struct sb_card_config), GFP_KERNEL)) == NULL) { - printk(KERN_ERR "sb: Error: Could not allocate memory\n"); - return -ENOMEM; - } - - printk(KERN_INFO "sb: PnP: Found Card Named = \"%s\", Card PnP id = " \ - "%s, Device PnP id = %s\n", card->card->name, card_id->id, - dev->id->id); - - scc->card_id = card_id->id; - scc->dev_id = dev->id->id; - sb_dev2cfg(dev, scc); - - printk(KERN_INFO "sb: PnP: Detected at: io=0x%x, irq=%d, " \ - "dma=%d, dma16=%d\n", scc->conf.io_base, scc->conf.irq, - scc->conf.dma, scc->conf.dma2); - - pnp_set_card_drvdata(card, scc); - sb_pnp_devices++; - - return sb_register_oss(scc, &sbmo); -} - -static void sb_pnp_remove(struct pnp_card_link *card) -{ - struct sb_card_config *scc = pnp_get_card_drvdata(card); - - if(!scc) - return; - - printk(KERN_INFO "sb: PnP: Removing %s\n", scc->card_id); - - sb_unload(scc); -} - -static struct pnp_card_driver sb_pnp_driver = { - .name = "OSS SndBlstr", /* 16 character limit */ - .id_table = sb_pnp_card_table, - .probe = sb_pnp_probe, - .remove = sb_pnp_remove, -}; -MODULE_DEVICE_TABLE(pnp_card, sb_pnp_card_table); -#endif /* CONFIG_PNP */ - -static void sb_unregister_all(void) -{ -#ifdef CONFIG_PNP - if (pnp_registered) - pnp_unregister_card_driver(&sb_pnp_driver); -#endif -} - -static int __init sb_init(void) -{ - int lres = 0; - int pres = 0; - - printk(KERN_INFO "sb: Init: Starting Probe...\n"); - - if(io != -1 && irq != -1 && dma != -1) { - printk(KERN_INFO "sb: Probing legacy card with io=%x, "\ - "irq=%d, dma=%d, dma16=%d\n",io, irq, dma, dma16); - lres = sb_init_legacy(); - } else if((io != -1 || irq != -1 || dma != -1) || - (!pnp && (io == -1 && irq == -1 && dma == -1))) - printk(KERN_ERR "sb: Error: At least io, irq, and dma "\ - "must be set for legacy cards.\n"); - -#ifdef CONFIG_PNP - if(pnp) { - int err = pnp_register_card_driver(&sb_pnp_driver); - if (!err) - pnp_registered = 1; - pres = sb_pnp_devices; - } -#endif - printk(KERN_INFO "sb: Init: Done\n"); - - /* If either PnP or Legacy registered a card then return - * success */ - if (pres == 0 && lres <= 0) { - sb_unregister_all(); - return -ENODEV; - } - return 0; -} - -static void __exit sb_exit(void) -{ - printk(KERN_INFO "sb: Unloading...\n"); - - /* Unload legacy card */ - if (legacy) { - printk (KERN_INFO "sb: Unloading legacy card\n"); - sb_unload(legacy); - } - - sb_unregister_all(); - - vfree(smw_free); - smw_free = NULL; -} - -module_init(sb_init); -module_exit(sb_exit); diff --git a/sound/oss/sb_card.h b/sound/oss/sb_card.h deleted file mode 100644 index 5535cff800df..000000000000 --- a/sound/oss/sb_card.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * sound/oss/sb_card.h - * - * This file is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this - * software for more info. - * - * 02-05-2002 Original Release, Paul Laufer <paul@laufernet.com> - */ - -struct sb_card_config { - struct address_info conf; - struct address_info mpucnf; - const char *card_id; - const char *dev_id; - int mpu; -}; - -#ifdef CONFIG_PNP - -/* - * SoundBlaster PnP tables and structures. - */ - -/* Card PnP ID Table */ -static struct pnp_card_device_id sb_pnp_card_table[] = { - /* Sound Blaster 16 */ - {.id = "CTL0024", .driver_data = 0, .devs = { {.id="CTL0031"}, } }, - /* Sound Blaster 16 */ - {.id = "CTL0025", .driver_data = 0, .devs = { {.id="CTL0031"}, } }, - /* Sound Blaster 16 */ - {.id = "CTL0026", .driver_data = 0, .devs = { {.id="CTL0031"}, } }, - /* Sound Blaster 16 */ - {.id = "CTL0027", .driver_data = 0, .devs = { {.id="CTL0031"}, } }, - /* Sound Blaster 16 */ - {.id = "CTL0028", .driver_data = 0, .devs = { {.id="CTL0031"}, } }, - /* Sound Blaster 16 */ - {.id = "CTL0029", .driver_data = 0, .devs = { {.id="CTL0031"}, } }, - /* Sound Blaster 16 */ - {.id = "CTL002a", .driver_data = 0, .devs = { {.id="CTL0031"}, } }, - /* Sound Blaster 16 */ - {.id = "CTL002b", .driver_data = 0, .devs = { {.id="CTL0031"}, } }, - /* Sound Blaster 16 */ - {.id = "CTL002c", .driver_data = 0, .devs = { {.id="CTL0031"}, } }, - /* Sound Blaster 16 */ - {.id = "CTL00ed", .driver_data = 0, .devs = { {.id="CTL0041"}, } }, - /* Sound Blaster 16 */ - {.id = "CTL0086", .driver_data = 0, .devs = { {.id="CTL0041"}, } }, - /* Sound Blaster Vibra16S */ - {.id = "CTL0051", .driver_data = 0, .devs = { {.id="CTL0001"}, } }, - /* Sound Blaster Vibra16C */ - {.id = "CTL0070", .driver_data = 0, .devs = { {.id="CTL0001"}, } }, - /* Sound Blaster Vibra16CL */ - {.id = "CTL0080", .driver_data = 0, .devs = { {.id="CTL0041"}, } }, - /* Sound Blaster Vibra16CL */ - {.id = "CTL00F0", .driver_data = 0, .devs = { {.id="CTL0043"}, } }, - /* Sound Blaster AWE 32 */ - {.id = "CTL0039", .driver_data = 0, .devs = { {.id="CTL0031"}, } }, - /* Sound Blaster AWE 32 */ - {.id = "CTL0042", .driver_data = 0, .devs = { {.id="CTL0031"}, } }, - /* Sound Blaster AWE 32 */ - {.id = "CTL0043", .driver_data = 0, .devs = { {.id="CTL0031"}, } }, - /* Sound Blaster AWE 32 */ - {.id = "CTL0044", .driver_data = 0, .devs = { {.id="CTL0031"}, } }, - /* Sound Blaster AWE 32 */ - {.id = "CTL0045", .driver_data = 0, .devs = { {.id="CTL0031"}, } }, - /* Sound Blaster AWE 32 */ - {.id = "CTL0046", .driver_data = 0, .devs = { {.id="CTL0031"}, } }, - /* Sound Blaster AWE 32 */ - {.id = "CTL0047", .driver_data = 0, .devs = { {.id="CTL0031"}, } }, - /* Sound Blaster AWE 32 */ - {.id = "CTL0048", .driver_data = 0, .devs = { {.id="CTL0031"}, } }, - /* Sound Blaster AWE 32 */ - {.id = "CTL0054", .driver_data = 0, .devs = { {.id="CTL0031"}, } }, - /* Sound Blaster AWE 32 */ - {.id = "CTL009C", .driver_data = 0, .devs = { {.id="CTL0041"}, } }, - /* Createive SB32 PnP */ - {.id = "CTL009F", .driver_data = 0, .devs = { {.id="CTL0041"}, } }, - /* Sound Blaster AWE 64 */ - {.id = "CTL009D", .driver_data = 0, .devs = { {.id="CTL0042"}, } }, - /* Sound Blaster AWE 64 Gold */ - {.id = "CTL009E", .driver_data = 0, .devs = { {.id="CTL0044"}, } }, - /* Sound Blaster AWE 64 Gold */ - {.id = "CTL00B2", .driver_data = 0, .devs = { {.id="CTL0044"}, } }, - /* Sound Blaster AWE 64 */ - {.id = "CTL00C1", .driver_data = 0, .devs = { {.id="CTL0042"}, } }, - /* Sound Blaster AWE 64 */ - {.id = "CTL00C3", .driver_data = 0, .devs = { {.id="CTL0045"}, } }, - /* Sound Blaster AWE 64 */ - {.id = "CTL00C5", .driver_data = 0, .devs = { {.id="CTL0045"}, } }, - /* Sound Blaster AWE 64 */ - {.id = "CTL00C7", .driver_data = 0, .devs = { {.id="CTL0045"}, } }, - /* Sound Blaster AWE 64 */ - {.id = "CTL00E4", .driver_data = 0, .devs = { {.id="CTL0045"}, } }, - /* Sound Blaster AWE 64 */ - {.id = "CTL00E9", .driver_data = 0, .devs = { {.id="CTL0045"}, } }, - /* ESS 1868 */ - {.id = "ESS0968", .driver_data = 0, .devs = { {.id="ESS0968"}, } }, - /* ESS 1868 */ - {.id = "ESS1868", .driver_data = 0, .devs = { {.id="ESS1868"}, } }, - /* ESS 1868 */ - {.id = "ESS1868", .driver_data = 0, .devs = { {.id="ESS8611"}, } }, - /* ESS 1869 PnP AudioDrive */ - {.id = "ESS0003", .driver_data = 0, .devs = { {.id="ESS1869"}, } }, - /* ESS 1869 */ - {.id = "ESS1869", .driver_data = 0, .devs = { {.id="ESS1869"}, } }, - /* ESS 1878 */ - {.id = "ESS1878", .driver_data = 0, .devs = { {.id="ESS1878"}, } }, - /* ESS 1879 */ - {.id = "ESS1879", .driver_data = 0, .devs = { {.id="ESS1879"}, } }, - /* CMI 8330 SoundPRO */ - {.id = "CMI0001", .driver_data = 0, .devs = { {.id="@X@0001"}, - {.id="@H@0001"}, - {.id="@@@0001"}, } }, - /* Diamond DT0197H */ - {.id = "RWR1688", .driver_data = 0, .devs = { {.id="@@@0001"}, - {.id="@X@0001"}, - {.id="@H@0001"}, } }, - /* ALS007 */ - {.id = "ALS0007", .driver_data = 0, .devs = { {.id="@@@0001"}, - {.id="@X@0001"}, - {.id="@H@0001"}, } }, - /* ALS100 */ - {.id = "ALS0001", .driver_data = 0, .devs = { {.id="@@@0001"}, - {.id="@X@0001"}, - {.id="@H@0001"}, } }, - /* ALS110 */ - {.id = "ALS0110", .driver_data = 0, .devs = { {.id="@@@1001"}, - {.id="@X@1001"}, - {.id="@H@0001"}, } }, - /* ALS120 */ - {.id = "ALS0120", .driver_data = 0, .devs = { {.id="@@@2001"}, - {.id="@X@2001"}, - {.id="@H@0001"}, } }, - /* ALS200 */ - {.id = "ALS0200", .driver_data = 0, .devs = { {.id="@@@0020"}, - {.id="@X@0030"}, - {.id="@H@0001"}, } }, - /* ALS200 */ - {.id = "RTL3000", .driver_data = 0, .devs = { {.id="@@@2001"}, - {.id="@X@2001"}, - {.id="@H@0001"}, } }, - /* Sound Blaster 16 (Virtual PC 2004) */ - {.id = "tBA03b0", .driver_data = 0, .devs = { {.id="PNPb003"}, } }, - /* -end- */ - {.id = "", } -}; - -#endif diff --git a/sound/oss/sb_common.c b/sound/oss/sb_common.c deleted file mode 100644 index 3d50fb4236ed..000000000000 --- a/sound/oss/sb_common.c +++ /dev/null @@ -1,1287 +0,0 @@ -/* - * sound/oss/sb_common.c - * - * Common routines for Sound Blaster compatible cards. - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * - * Daniel J. Rodriksson: Modified sbintr to handle 8 and 16 bit interrupts - * for full duplex support ( only sb16 by now ) - * Rolf Fokkens: Added (BETA?) support for ES1887 chips. - * (fokkensr@vertis.nl) Which means: You can adjust the recording levels. - * - * 2000/01/18 - separated sb_card and sb_common - - * Jeff Garzik <jgarzik@pobox.com> - * - * 2000/09/18 - got rid of attach_uart401 - * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - * - * 2001/01/26 - replaced CLI/STI with spinlocks - * Chris Rankin <rankinc@zipworld.com.au> - */ - -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/spinlock.h> -#include <linux/slab.h> - -#include "sound_config.h" -#include "sound_firmware.h" - -#include "mpu401.h" - -#include "sb_mixer.h" -#include "sb.h" -#include "sb_ess.h" - -/* - * global module flag - */ - -int sb_be_quiet; - -static sb_devc *detected_devc; /* For communication from probe to init */ -static sb_devc *last_devc; /* For MPU401 initialization */ - -static unsigned char jazz_irq_bits[] = { - 0, 0, 2, 3, 0, 1, 0, 4, 0, 2, 5, 0, 0, 0, 0, 6 -}; - -static unsigned char jazz_dma_bits[] = { - 0, 1, 0, 2, 0, 3, 0, 4 -}; - -void *smw_free; - -/* - * Jazz16 chipset specific control variables - */ - -static int jazz16_base; /* Not detected */ -static unsigned char jazz16_bits; /* I/O relocation bits */ -static DEFINE_SPINLOCK(jazz16_lock); - -/* - * Logitech Soundman Wave specific initialization code - */ - -#ifdef SMW_MIDI0001_INCLUDED -#include "smw-midi0001.h" -#else -static unsigned char *smw_ucode; -static int smw_ucodeLen; - -#endif - -static sb_devc *last_sb; /* Last sb loaded */ - -int sb_dsp_command(sb_devc * devc, unsigned char val) -{ - int i; - unsigned long limit; - - limit = jiffies + HZ / 10; /* Timeout */ - - /* - * Note! the i<500000 is an emergency exit. The sb_dsp_command() is sometimes - * called while interrupts are disabled. This means that the timer is - * disabled also. However the timeout situation is a abnormal condition. - * Normally the DSP should be ready to accept commands after just couple of - * loops. - */ - - for (i = 0; i < 500000 && (limit-jiffies)>0; i++) - { - if ((inb(DSP_STATUS) & 0x80) == 0) - { - outb((val), DSP_COMMAND); - return 1; - } - } - printk(KERN_WARNING "Sound Blaster: DSP command(%x) timeout.\n", val); - return 0; -} - -int sb_dsp_get_byte(sb_devc * devc) -{ - int i; - - for (i = 1000; i; i--) - { - if (inb(DSP_DATA_AVAIL) & 0x80) - return inb(DSP_READ); - } - return 0xffff; -} - -static void sb_intr (sb_devc *devc) -{ - int status; - unsigned char src = 0xff; - - if (devc->model == MDL_SB16) - { - src = sb_getmixer(devc, IRQ_STAT); /* Interrupt source register */ - - if (src & 4) /* MPU401 interrupt */ - if(devc->midi_irq_cookie) - uart401intr(devc->irq, devc->midi_irq_cookie); - - if (!(src & 3)) - return; /* Not a DSP interrupt */ - } - if (devc->intr_active && (!devc->fullduplex || (src & 0x01))) - { - switch (devc->irq_mode) - { - case IMODE_OUTPUT: - DMAbuf_outputintr(devc->dev, 1); - break; - - case IMODE_INPUT: - DMAbuf_inputintr(devc->dev); - break; - - case IMODE_INIT: - break; - - case IMODE_MIDI: - sb_midi_interrupt(devc); - break; - - default: - /* printk(KERN_WARNING "Sound Blaster: Unexpected interrupt\n"); */ - ; - } - } - else if (devc->intr_active_16 && (src & 0x02)) - { - switch (devc->irq_mode_16) - { - case IMODE_OUTPUT: - DMAbuf_outputintr(devc->dev, 1); - break; - - case IMODE_INPUT: - DMAbuf_inputintr(devc->dev); - break; - - case IMODE_INIT: - break; - - default: - /* printk(KERN_WARNING "Sound Blaster: Unexpected interrupt\n"); */ - ; - } - } - /* - * Acknowledge interrupts - */ - - if (src & 0x01) - status = inb(DSP_DATA_AVAIL); - - if (devc->model == MDL_SB16 && src & 0x02) - status = inb(DSP_DATA_AVL16); -} - -static void pci_intr(sb_devc *devc) -{ - int src = inb(devc->pcibase+0x1A); - src&=3; - if(src) - sb_intr(devc); -} - -static irqreturn_t sbintr(int irq, void *dev_id) -{ - sb_devc *devc = dev_id; - - devc->irq_ok = 1; - - switch (devc->model) { - case MDL_ESSPCI: - pci_intr (devc); - break; - - case MDL_ESS: - ess_intr (devc); - break; - default: - sb_intr (devc); - break; - } - return IRQ_HANDLED; -} - -int sb_dsp_reset(sb_devc * devc) -{ - int loopc; - - if (devc->model == MDL_ESS) return ess_dsp_reset (devc); - - /* This is only for non-ESS chips */ - - outb(1, DSP_RESET); - - udelay(10); - outb(0, DSP_RESET); - udelay(30); - - for (loopc = 0; loopc < 1000 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++); - - if (inb(DSP_READ) != 0xAA) - { - DDB(printk("sb: No response to RESET\n")); - return 0; /* Sorry */ - } - - return 1; -} - -static void dsp_get_vers(sb_devc * devc) -{ - int i; - - unsigned long flags; - - DDB(printk("Entered dsp_get_vers()\n")); - spin_lock_irqsave(&devc->lock, flags); - devc->major = devc->minor = 0; - sb_dsp_command(devc, 0xe1); /* Get version */ - - for (i = 100000; i; i--) - { - if (inb(DSP_DATA_AVAIL) & 0x80) - { - if (devc->major == 0) - devc->major = inb(DSP_READ); - else - { - devc->minor = inb(DSP_READ); - break; - } - } - } - spin_unlock_irqrestore(&devc->lock, flags); - DDB(printk("DSP version %d.%02d\n", devc->major, devc->minor)); -} - -static int sb16_set_dma_hw(sb_devc * devc) -{ - int bits; - - if (devc->dma8 != 0 && devc->dma8 != 1 && devc->dma8 != 3) - { - printk(KERN_ERR "SB16: Invalid 8 bit DMA (%d)\n", devc->dma8); - return 0; - } - bits = (1 << devc->dma8); - - if (devc->dma16 >= 5 && devc->dma16 <= 7) - bits |= (1 << devc->dma16); - - sb_setmixer(devc, DMA_NR, bits); - return 1; -} - -static void sb16_set_mpu_port(sb_devc * devc, struct address_info *hw_config) -{ - /* - * This routine initializes new MIDI port setup register of SB Vibra (CT2502). - */ - unsigned char bits = sb_getmixer(devc, 0x84) & ~0x06; - - switch (hw_config->io_base) - { - case 0x300: - sb_setmixer(devc, 0x84, bits | 0x04); - break; - - case 0x330: - sb_setmixer(devc, 0x84, bits | 0x00); - break; - - default: - sb_setmixer(devc, 0x84, bits | 0x02); /* Disable MPU */ - printk(KERN_ERR "SB16: Invalid MIDI I/O port %x\n", hw_config->io_base); - } -} - -static int sb16_set_irq_hw(sb_devc * devc, int level) -{ - int ival; - - switch (level) - { - case 5: - ival = 2; - break; - case 7: - ival = 4; - break; - case 9: - ival = 1; - break; - case 10: - ival = 8; - break; - default: - printk(KERN_ERR "SB16: Invalid IRQ%d\n", level); - return 0; - } - sb_setmixer(devc, IRQ_NR, ival); - return 1; -} - -static void relocate_Jazz16(sb_devc * devc, struct address_info *hw_config) -{ - unsigned char bits = 0; - unsigned long flags; - - if (jazz16_base != 0 && jazz16_base != hw_config->io_base) - return; - - switch (hw_config->io_base) - { - case 0x220: - bits = 1; - break; - case 0x240: - bits = 2; - break; - case 0x260: - bits = 3; - break; - default: - return; - } - bits = jazz16_bits = bits << 5; - jazz16_base = hw_config->io_base; - - /* - * Magic wake up sequence by writing to 0x201 (aka Joystick port) - */ - spin_lock_irqsave(&jazz16_lock, flags); - outb((0xAF), 0x201); - outb((0x50), 0x201); - outb((bits), 0x201); - spin_unlock_irqrestore(&jazz16_lock, flags); -} - -static int init_Jazz16(sb_devc * devc, struct address_info *hw_config) -{ - char name[100]; - /* - * First try to check that the card has Jazz16 chip. It identifies itself - * by returning 0x12 as response to DSP command 0xfa. - */ - - if (!sb_dsp_command(devc, 0xfa)) - return 0; - - if (sb_dsp_get_byte(devc) != 0x12) - return 0; - - /* - * OK so far. Now configure the IRQ and DMA channel used by the card. - */ - if (hw_config->irq < 1 || hw_config->irq > 15 || jazz_irq_bits[hw_config->irq] == 0) - { - printk(KERN_ERR "Jazz16: Invalid interrupt (IRQ%d)\n", hw_config->irq); - return 0; - } - if (hw_config->dma < 0 || hw_config->dma > 3 || jazz_dma_bits[hw_config->dma] == 0) - { - printk(KERN_ERR "Jazz16: Invalid 8 bit DMA (DMA%d)\n", hw_config->dma); - return 0; - } - if (hw_config->dma2 < 0) - { - printk(KERN_ERR "Jazz16: No 16 bit DMA channel defined\n"); - return 0; - } - if (hw_config->dma2 < 5 || hw_config->dma2 > 7 || jazz_dma_bits[hw_config->dma2] == 0) - { - printk(KERN_ERR "Jazz16: Invalid 16 bit DMA (DMA%d)\n", hw_config->dma2); - return 0; - } - devc->dma16 = hw_config->dma2; - - if (!sb_dsp_command(devc, 0xfb)) - return 0; - - if (!sb_dsp_command(devc, jazz_dma_bits[hw_config->dma] | - (jazz_dma_bits[hw_config->dma2] << 4))) - return 0; - - if (!sb_dsp_command(devc, jazz_irq_bits[hw_config->irq])) - return 0; - - /* - * Now we have configured a standard Jazz16 device. - */ - devc->model = MDL_JAZZ; - strcpy(name, "Jazz16"); - - hw_config->name = "Jazz16"; - devc->caps |= SB_NO_MIDI; - return 1; -} - -static void relocate_ess1688(sb_devc * devc) -{ - unsigned char bits; - - switch (devc->base) - { - case 0x220: - bits = 0x04; - break; - case 0x230: - bits = 0x05; - break; - case 0x240: - bits = 0x06; - break; - case 0x250: - bits = 0x07; - break; - default: - return; /* Wrong port */ - } - - DDB(printk("Doing ESS1688 address selection\n")); - - /* - * ES1688 supports two alternative ways for software address config. - * First try the so called Read-Sequence-Key method. - */ - - /* Reset the sequence logic */ - inb(0x229); - inb(0x229); - inb(0x229); - - /* Perform the read sequence */ - inb(0x22b); - inb(0x229); - inb(0x22b); - inb(0x229); - inb(0x229); - inb(0x22b); - inb(0x229); - - /* Select the base address by reading from it. Then probe using the port. */ - inb(devc->base); - if (sb_dsp_reset(devc)) /* Bingo */ - return; - -#if 0 /* This causes system lockups (Nokia 386/25 at least) */ - /* - * The last resort is the system control register method. - */ - - outb((0x00), 0xfb); /* 0xFB is the unlock register */ - outb((0x00), 0xe0); /* Select index 0 */ - outb((bits), 0xe1); /* Write the config bits */ - outb((0x00), 0xf9); /* 0xFB is the lock register */ -#endif -} - -int sb_dsp_detect(struct address_info *hw_config, int pci, int pciio, struct sb_module_options *sbmo) -{ - sb_devc sb_info; - sb_devc *devc = &sb_info; - - memset((char *) &sb_info, 0, sizeof(sb_info)); /* Zero everything */ - - /* Copy module options in place */ - if(sbmo) memcpy(&devc->sbmo, sbmo, sizeof(struct sb_module_options)); - - sb_info.my_mididev = -1; - sb_info.my_mixerdev = -1; - sb_info.dev = -1; - - /* - * Initialize variables - */ - - DDB(printk("sb_dsp_detect(%x) entered\n", hw_config->io_base)); - - spin_lock_init(&devc->lock); - devc->type = hw_config->card_subtype; - - devc->base = hw_config->io_base; - devc->irq = hw_config->irq; - devc->dma8 = hw_config->dma; - - devc->dma16 = -1; - devc->pcibase = pciio; - - if(pci == SB_PCI_ESSMAESTRO) - { - devc->model = MDL_ESSPCI; - devc->caps |= SB_PCI_IRQ; - hw_config->driver_use_1 |= SB_PCI_IRQ; - hw_config->card_subtype = MDL_ESSPCI; - } - - if(pci == SB_PCI_YAMAHA) - { - devc->model = MDL_YMPCI; - devc->caps |= SB_PCI_IRQ; - hw_config->driver_use_1 |= SB_PCI_IRQ; - hw_config->card_subtype = MDL_YMPCI; - - printk("Yamaha PCI mode.\n"); - } - - if (devc->sbmo.acer) - { - unsigned long flags; - - spin_lock_irqsave(&devc->lock, flags); - inb(devc->base + 0x09); - inb(devc->base + 0x09); - inb(devc->base + 0x09); - inb(devc->base + 0x0b); - inb(devc->base + 0x09); - inb(devc->base + 0x0b); - inb(devc->base + 0x09); - inb(devc->base + 0x09); - inb(devc->base + 0x0b); - inb(devc->base + 0x09); - inb(devc->base + 0x00); - spin_unlock_irqrestore(&devc->lock, flags); - } - /* - * Detect the device - */ - - if (sb_dsp_reset(devc)) - dsp_get_vers(devc); - else - devc->major = 0; - - if (devc->type == 0 || devc->type == MDL_JAZZ || devc->type == MDL_SMW) - if (devc->major == 0 || (devc->major == 3 && devc->minor == 1)) - relocate_Jazz16(devc, hw_config); - - if (devc->major == 0 && (devc->type == MDL_ESS || devc->type == 0)) - relocate_ess1688(devc); - - if (!sb_dsp_reset(devc)) - { - DDB(printk("SB reset failed\n")); -#ifdef MODULE - printk(KERN_INFO "sb: dsp reset failed.\n"); -#endif - return 0; - } - if (devc->major == 0) - dsp_get_vers(devc); - - if (devc->major == 3 && devc->minor == 1) - { - if (devc->type == MDL_AZTECH) /* SG Washington? */ - { - if (sb_dsp_command(devc, 0x09)) - if (sb_dsp_command(devc, 0x00)) /* Enter WSS mode */ - { - int i; - - /* Have some delay */ - for (i = 0; i < 10000; i++) - inb(DSP_DATA_AVAIL); - devc->caps = SB_NO_AUDIO | SB_NO_MIDI; /* Mixer only */ - devc->model = MDL_AZTECH; - } - } - } - - if(devc->type == MDL_ESSPCI) - devc->model = MDL_ESSPCI; - - if(devc->type == MDL_YMPCI) - { - printk("YMPCI selected\n"); - devc->model = MDL_YMPCI; - } - - /* - * Save device information for sb_dsp_init() - */ - - - detected_devc = kmemdup(devc, sizeof(sb_devc), GFP_KERNEL); - if (detected_devc == NULL) - { - printk(KERN_ERR "sb: Can't allocate memory for device information\n"); - return 0; - } - MDB(printk(KERN_INFO "SB %d.%02d detected OK (%x)\n", devc->major, devc->minor, hw_config->io_base)); - return 1; -} - -int sb_dsp_init(struct address_info *hw_config, struct module *owner) -{ - sb_devc *devc; - char name[100]; - extern int sb_be_quiet; - int mixer22, mixer30; - -/* - * Check if we had detected a SB device earlier - */ - DDB(printk("sb_dsp_init(%x) entered\n", hw_config->io_base)); - name[0] = 0; - - if (detected_devc == NULL) - { - MDB(printk("No detected device\n")); - return 0; - } - devc = detected_devc; - detected_devc = NULL; - - if (devc->base != hw_config->io_base) - { - DDB(printk("I/O port mismatch\n")); - release_region(devc->base, 16); - return 0; - } - /* - * Now continue initialization of the device - */ - - devc->caps = hw_config->driver_use_1; - - if (!((devc->caps & SB_NO_AUDIO) && (devc->caps & SB_NO_MIDI)) && hw_config->irq > 0) - { /* IRQ setup */ - - /* - * ESS PCI cards do shared PCI IRQ stuff. Since they - * will get shared PCI irq lines we must cope. - */ - - int i=(devc->caps&SB_PCI_IRQ)?IRQF_SHARED:0; - - if (request_irq(hw_config->irq, sbintr, i, "soundblaster", devc) < 0) - { - printk(KERN_ERR "SB: Can't allocate IRQ%d\n", hw_config->irq); - release_region(devc->base, 16); - return 0; - } - devc->irq_ok = 0; - - if (devc->major == 4) - if (!sb16_set_irq_hw(devc, devc->irq)) /* Unsupported IRQ */ - { - free_irq(devc->irq, devc); - release_region(devc->base, 16); - return 0; - } - if ((devc->type == 0 || devc->type == MDL_ESS) && - devc->major == 3 && devc->minor == 1) - { /* Handle various chipsets which claim they are SB Pro compatible */ - if ((devc->type != 0 && devc->type != MDL_ESS) || - !ess_init(devc, hw_config)) - { - if ((devc->type != 0 && devc->type != MDL_JAZZ && - devc->type != MDL_SMW) || !init_Jazz16(devc, hw_config)) - { - DDB(printk("This is a genuine SB Pro\n")); - } - } - } - if (devc->major == 4 && devc->minor <= 11 ) /* Won't work */ - devc->irq_ok = 1; - else - { - int n; - - for (n = 0; n < 3 && devc->irq_ok == 0; n++) - { - if (sb_dsp_command(devc, 0xf2)) /* Cause interrupt immediately */ - { - int i; - - for (i = 0; !devc->irq_ok && i < 10000; i++); - } - } - if (!devc->irq_ok) - printk(KERN_WARNING "sb: Interrupt test on IRQ%d failed - Probable IRQ conflict\n", devc->irq); - else - { - DDB(printk("IRQ test OK (IRQ%d)\n", devc->irq)); - } - } - } /* IRQ setup */ - - last_sb = devc; - - switch (devc->major) - { - case 1: /* SB 1.0 or 1.5 */ - devc->model = hw_config->card_subtype = MDL_SB1; - break; - - case 2: /* SB 2.x */ - if (devc->minor == 0) - devc->model = hw_config->card_subtype = MDL_SB2; - else - devc->model = hw_config->card_subtype = MDL_SB201; - break; - - case 3: /* SB Pro and most clones */ - switch (devc->model) { - case 0: - devc->model = hw_config->card_subtype = MDL_SBPRO; - if (hw_config->name == NULL) - hw_config->name = "Sound Blaster Pro (8 BIT ONLY)"; - break; - case MDL_ESS: - ess_dsp_init(devc, hw_config); - break; - } - break; - - case 4: - devc->model = hw_config->card_subtype = MDL_SB16; - /* - * ALS007 and ALS100 return DSP version 4.2 and have 2 post-reset !=0 - * registers at 0x3c and 0x4c (output ctrl registers on ALS007) whereas - * a "standard" SB16 doesn't have a register at 0x4c. ALS100 actively - * updates register 0x22 whenever 0x30 changes, as per the SB16 spec. - * Since ALS007 doesn't, this can be used to differentiate the 2 cards. - */ - if ((devc->minor == 2) && sb_getmixer(devc,0x3c) && sb_getmixer(devc,0x4c)) - { - mixer30 = sb_getmixer(devc,0x30); - sb_setmixer(devc,0x22,(mixer22=sb_getmixer(devc,0x22)) & 0x0f); - sb_setmixer(devc,0x30,0xff); - /* ALS100 will force 0x30 to 0xf8 like SB16; ALS007 will allow 0xff. */ - /* Register 0x22 & 0xf0 on ALS100 == 0xf0; on ALS007 it == 0x10. */ - if ((sb_getmixer(devc,0x30) != 0xff) || ((sb_getmixer(devc,0x22) & 0xf0) != 0x10)) - { - devc->submodel = SUBMDL_ALS100; - if (hw_config->name == NULL) - hw_config->name = "Sound Blaster 16 (ALS-100)"; - } - else - { - sb_setmixer(devc,0x3c,0x1f); /* Enable all inputs */ - sb_setmixer(devc,0x4c,0x1f); - sb_setmixer(devc,0x22,mixer22); /* Restore 0x22 to original value */ - devc->submodel = SUBMDL_ALS007; - if (hw_config->name == NULL) - hw_config->name = "Sound Blaster 16 (ALS-007)"; - } - sb_setmixer(devc,0x30,mixer30); - } - else if (hw_config->name == NULL) - hw_config->name = "Sound Blaster 16"; - - if (hw_config->dma2 == -1) - devc->dma16 = devc->dma8; - else if (hw_config->dma2 < 5 || hw_config->dma2 > 7) - { - printk(KERN_WARNING "SB16: Bad or missing 16 bit DMA channel\n"); - devc->dma16 = devc->dma8; - } - else - devc->dma16 = hw_config->dma2; - - if(!sb16_set_dma_hw(devc)) { - free_irq(devc->irq, devc); - release_region(hw_config->io_base, 16); - return 0; - } - - devc->caps |= SB_NO_MIDI; - } - - if (!(devc->caps & SB_NO_MIXER)) - if (devc->major == 3 || devc->major == 4) - sb_mixer_init(devc, owner); - - if (!(devc->caps & SB_NO_MIDI)) - sb_dsp_midi_init(devc, owner); - - if (hw_config->name == NULL) - hw_config->name = "Sound Blaster (8 BIT/MONO ONLY)"; - - sprintf(name, "%s (%d.%02d)", hw_config->name, devc->major, devc->minor); - conf_printf(name, hw_config); - - /* - * Assuming that a sound card is Sound Blaster (compatible) is the most common - * configuration error and the mother of all problems. Usually sound cards - * emulate SB Pro but in addition they have a 16 bit native mode which should be - * used in Unix. See Readme.cards for more information about configuring OSS/Free - * properly. - */ - if (devc->model <= MDL_SBPRO) - { - if (devc->major == 3 && devc->minor != 1) /* "True" SB Pro should have v3.1 (rare ones may have 3.2). */ - { - printk(KERN_INFO "This sound card may not be fully Sound Blaster Pro compatible.\n"); - printk(KERN_INFO "In many cases there is another way to configure OSS so that\n"); - printk(KERN_INFO "it works properly with OSS (for example in 16 bit mode).\n"); - printk(KERN_INFO "Please ignore this message if you _really_ have a SB Pro.\n"); - } - else if (!sb_be_quiet && devc->model == MDL_SBPRO) - { - printk(KERN_INFO "SB DSP version is just %d.%02d which means that your card is\n", devc->major, devc->minor); - printk(KERN_INFO "several years old (8 bit only device) or alternatively the sound driver\n"); - printk(KERN_INFO "is incorrectly configured.\n"); - } - } - hw_config->card_subtype = devc->model; - hw_config->slots[0]=devc->dev; - last_devc = devc; /* For SB MPU detection */ - - if (!(devc->caps & SB_NO_AUDIO) && devc->dma8 >= 0) - { - if (sound_alloc_dma(devc->dma8, "SoundBlaster8")) - { - printk(KERN_WARNING "Sound Blaster: Can't allocate 8 bit DMA channel %d\n", devc->dma8); - } - if (devc->dma16 >= 0 && devc->dma16 != devc->dma8) - { - if (sound_alloc_dma(devc->dma16, "SoundBlaster16")) - printk(KERN_WARNING "Sound Blaster: can't allocate 16 bit DMA channel %d.\n", devc->dma16); - } - sb_audio_init(devc, name, owner); - hw_config->slots[0]=devc->dev; - } - else - { - MDB(printk("Sound Blaster: no audio devices found.\n")); - } - return 1; -} - -/* if (sbmpu) below we allow mpu401 to manage the midi devs - otherwise we have to unload them. (Andrzej Krzysztofowicz) */ - -void sb_dsp_unload(struct address_info *hw_config, int sbmpu) -{ - sb_devc *devc; - - devc = audio_devs[hw_config->slots[0]]->devc; - - if (devc && devc->base == hw_config->io_base) - { - if ((devc->model & MDL_ESS) && devc->pcibase) - release_region(devc->pcibase, 8); - - release_region(devc->base, 16); - - if (!(devc->caps & SB_NO_AUDIO)) - { - sound_free_dma(devc->dma8); - if (devc->dma16 >= 0) - sound_free_dma(devc->dma16); - } - if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI)) - { - if (devc->irq > 0) - free_irq(devc->irq, devc); - - sb_mixer_unload(devc); - /* We don't have to do this bit any more the UART401 is its own - master -- Krzysztof Halasa */ - /* But we have to do it, if UART401 is not detected */ - if (!sbmpu) - sound_unload_mididev(devc->my_mididev); - sound_unload_audiodev(devc->dev); - } - kfree(devc); - } - else - release_region(hw_config->io_base, 16); - - kfree(detected_devc); -} - -/* - * Mixer access routines - * - * ES1887 modifications: some mixer registers reside in the - * range above 0xa0. These must be accessed in another way. - */ - -void sb_setmixer(sb_devc * devc, unsigned int port, unsigned int value) -{ - unsigned long flags; - - if (devc->model == MDL_ESS) { - ess_setmixer (devc, port, value); - return; - } - - spin_lock_irqsave(&devc->lock, flags); - - outb(((unsigned char) (port & 0xff)), MIXER_ADDR); - udelay(20); - outb(((unsigned char) (value & 0xff)), MIXER_DATA); - udelay(20); - - spin_unlock_irqrestore(&devc->lock, flags); -} - -unsigned int sb_getmixer(sb_devc * devc, unsigned int port) -{ - unsigned int val; - unsigned long flags; - - if (devc->model == MDL_ESS) return ess_getmixer (devc, port); - - spin_lock_irqsave(&devc->lock, flags); - - outb(((unsigned char) (port & 0xff)), MIXER_ADDR); - udelay(20); - val = inb(MIXER_DATA); - udelay(20); - - spin_unlock_irqrestore(&devc->lock, flags); - - return val; -} - -void sb_chgmixer - (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val) -{ - int value; - - value = sb_getmixer(devc, reg); - value = (value & ~mask) | (val & mask); - sb_setmixer(devc, reg, value); -} - -/* - * MPU401 MIDI initialization. - */ - -static void smw_putmem(sb_devc * devc, int base, int addr, unsigned char val) -{ - unsigned long flags; - - spin_lock_irqsave(&jazz16_lock, flags); /* NOT the SB card? */ - - outb((addr & 0xff), base + 1); /* Low address bits */ - outb((addr >> 8), base + 2); /* High address bits */ - outb((val), base); /* Data */ - - spin_unlock_irqrestore(&jazz16_lock, flags); -} - -static unsigned char smw_getmem(sb_devc * devc, int base, int addr) -{ - unsigned long flags; - unsigned char val; - - spin_lock_irqsave(&jazz16_lock, flags); /* NOT the SB card? */ - - outb((addr & 0xff), base + 1); /* Low address bits */ - outb((addr >> 8), base + 2); /* High address bits */ - val = inb(base); /* Data */ - - spin_unlock_irqrestore(&jazz16_lock, flags); - return val; -} - -static int smw_midi_init(sb_devc * devc, struct address_info *hw_config) -{ - int mpu_base = hw_config->io_base; - int mp_base = mpu_base + 4; /* Microcontroller base */ - int i; - unsigned char control; - - - /* - * Reset the microcontroller so that the RAM can be accessed - */ - - control = inb(mpu_base + 7); - outb((control | 3), mpu_base + 7); /* Set last two bits to 1 (?) */ - outb(((control & 0xfe) | 2), mpu_base + 7); /* xxxxxxx0 resets the mc */ - - mdelay(3); /* Wait at least 1ms */ - - outb((control & 0xfc), mpu_base + 7); /* xxxxxx00 enables RAM */ - - /* - * Detect microcontroller by probing the 8k RAM area - */ - smw_putmem(devc, mp_base, 0, 0x00); - smw_putmem(devc, mp_base, 1, 0xff); - udelay(10); - - if (smw_getmem(devc, mp_base, 0) != 0x00 || smw_getmem(devc, mp_base, 1) != 0xff) - { - DDB(printk("SM Wave: No microcontroller RAM detected (%02x, %02x)\n", smw_getmem(devc, mp_base, 0), smw_getmem(devc, mp_base, 1))); - return 0; /* No RAM */ - } - /* - * There is RAM so assume it's really a SM Wave - */ - - devc->model = MDL_SMW; - smw_mixer_init(devc); - -#ifdef MODULE - if (!smw_ucode) - { - smw_ucodeLen = mod_firmware_load("/etc/sound/midi0001.bin", (void *) &smw_ucode); - smw_free = smw_ucode; - } -#endif - if (smw_ucodeLen > 0) - { - if (smw_ucodeLen != 8192) - { - printk(KERN_ERR "SM Wave: Invalid microcode (MIDI0001.BIN) length\n"); - return 1; - } - /* - * Download microcode - */ - - for (i = 0; i < 8192; i++) - smw_putmem(devc, mp_base, i, smw_ucode[i]); - - /* - * Verify microcode - */ - - for (i = 0; i < 8192; i++) - if (smw_getmem(devc, mp_base, i) != smw_ucode[i]) - { - printk(KERN_ERR "SM Wave: Microcode verification failed\n"); - return 0; - } - } - control = 0; -#ifdef SMW_SCSI_IRQ - /* - * Set the SCSI interrupt (IRQ2/9, IRQ3 or IRQ10). The SCSI interrupt - * is disabled by default. - * - * FIXME - make this a module option - * - * BTW the Zilog 5380 SCSI controller is located at MPU base + 0x10. - */ - { - static unsigned char scsi_irq_bits[] = { - 0, 0, 3, 1, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0 - }; - control |= scsi_irq_bits[SMW_SCSI_IRQ] << 6; - } -#endif - -#ifdef SMW_OPL4_ENABLE - /* - * Make the OPL4 chip visible on the PC bus at 0x380. - * - * There is no need to enable this feature since this driver - * doesn't support OPL4 yet. Also there is no RAM in SM Wave so - * enabling OPL4 is pretty useless. - */ - control |= 0x10; /* Uses IRQ12 if bit 0x20 == 0 */ - /* control |= 0x20; Uncomment this if you want to use IRQ7 */ -#endif - outb((control | 0x03), mpu_base + 7); /* xxxxxx11 restarts */ - hw_config->name = "SoundMan Wave"; - return 1; -} - -static int init_Jazz16_midi(sb_devc * devc, struct address_info *hw_config) -{ - int mpu_base = hw_config->io_base; - int sb_base = devc->base; - int irq = hw_config->irq; - - unsigned char bits = 0; - unsigned long flags; - - if (irq < 0) - irq *= -1; - - if (irq < 1 || irq > 15 || - jazz_irq_bits[irq] == 0) - { - printk(KERN_ERR "Jazz16: Invalid MIDI interrupt (IRQ%d)\n", irq); - return 0; - } - switch (sb_base) - { - case 0x220: - bits = 1; - break; - case 0x240: - bits = 2; - break; - case 0x260: - bits = 3; - break; - default: - return 0; - } - bits = jazz16_bits = bits << 5; - switch (mpu_base) - { - case 0x310: - bits |= 1; - break; - case 0x320: - bits |= 2; - break; - case 0x330: - bits |= 3; - break; - default: - printk(KERN_ERR "Jazz16: Invalid MIDI I/O port %x\n", mpu_base); - return 0; - } - /* - * Magic wake up sequence by writing to 0x201 (aka Joystick port) - */ - spin_lock_irqsave(&jazz16_lock, flags); - outb(0xAF, 0x201); - outb(0x50, 0x201); - outb(bits, 0x201); - spin_unlock_irqrestore(&jazz16_lock, flags); - - hw_config->name = "Jazz16"; - smw_midi_init(devc, hw_config); - - if (!sb_dsp_command(devc, 0xfb)) - return 0; - - if (!sb_dsp_command(devc, jazz_dma_bits[devc->dma8] | - (jazz_dma_bits[devc->dma16] << 4))) - return 0; - - if (!sb_dsp_command(devc, jazz_irq_bits[devc->irq] | - (jazz_irq_bits[irq] << 4))) - return 0; - - return 1; -} - -int probe_sbmpu(struct address_info *hw_config, struct module *owner) -{ - sb_devc *devc = last_devc; - int ret; - - if (last_devc == NULL) - return 0; - - last_devc = NULL; - - if (hw_config->io_base <= 0) - { - /* The real vibra16 is fine about this, but we have to go - wipe up after Cyrix again */ - - if(devc->model == MDL_SB16 && devc->minor >= 12) - { - unsigned char bits = sb_getmixer(devc, 0x84) & ~0x06; - sb_setmixer(devc, 0x84, bits | 0x02); /* Disable MPU */ - } - return 0; - } - -#if defined(CONFIG_SOUND_MPU401) - if (devc->model == MDL_ESS) - { - struct resource *ports; - ports = request_region(hw_config->io_base, 2, "mpu401"); - if (!ports) { - printk(KERN_ERR "sbmpu: I/O port conflict (%x)\n", hw_config->io_base); - return 0; - } - if (!ess_midi_init(devc, hw_config)) { - release_region(hw_config->io_base, 2); - return 0; - } - hw_config->name = "ESS1xxx MPU"; - devc->midi_irq_cookie = NULL; - if (!probe_mpu401(hw_config, ports)) { - release_region(hw_config->io_base, 2); - return 0; - } - attach_mpu401(hw_config, owner); - if (last_sb->irq == -hw_config->irq) - last_sb->midi_irq_cookie = - (void *)(long) hw_config->slots[1]; - return 1; - } -#endif - - switch (devc->model) - { - case MDL_SB16: - if (hw_config->io_base != 0x300 && hw_config->io_base != 0x330) - { - printk(KERN_ERR "SB16: Invalid MIDI port %x\n", hw_config->io_base); - return 0; - } - hw_config->name = "Sound Blaster 16"; - if (hw_config->irq < 3 || hw_config->irq == devc->irq) - hw_config->irq = -devc->irq; - if (devc->minor > 12) /* What is Vibra's version??? */ - sb16_set_mpu_port(devc, hw_config); - break; - - case MDL_JAZZ: - if (hw_config->irq < 3 || hw_config->irq == devc->irq) - hw_config->irq = -devc->irq; - if (!init_Jazz16_midi(devc, hw_config)) - return 0; - break; - - case MDL_YMPCI: - hw_config->name = "Yamaha PCI Legacy"; - printk("Yamaha PCI legacy UART401 check.\n"); - break; - default: - return 0; - } - - ret = probe_uart401(hw_config, owner); - if (ret) - last_sb->midi_irq_cookie=midi_devs[hw_config->slots[4]]->devc; - return ret; -} - -void unload_sbmpu(struct address_info *hw_config) -{ -#if defined(CONFIG_SOUND_MPU401) - if (!strcmp (hw_config->name, "ESS1xxx MPU")) { - unload_mpu401(hw_config); - return; - } -#endif - unload_uart401(hw_config); -} - -EXPORT_SYMBOL(sb_dsp_init); -EXPORT_SYMBOL(sb_dsp_detect); -EXPORT_SYMBOL(sb_dsp_unload); -EXPORT_SYMBOL(sb_be_quiet); -EXPORT_SYMBOL(probe_sbmpu); -EXPORT_SYMBOL(unload_sbmpu); -EXPORT_SYMBOL(smw_free); -MODULE_LICENSE("GPL"); diff --git a/sound/oss/sb_ess.c b/sound/oss/sb_ess.c deleted file mode 100644 index 17e3f14318cd..000000000000 --- a/sound/oss/sb_ess.c +++ /dev/null @@ -1,1823 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#undef FKS_LOGGING -#undef FKS_TEST - -/* - * tabs should be 4 spaces, in vi(m): set tabstop=4 - * - * TODO: consistency speed calculations!! - * cleanup! - * ????: Did I break MIDI support? - * - * History: - * - * Rolf Fokkens (Dec 20 1998): ES188x recording level support on a per - * fokkensr@vertis.nl input basis. - * (Dec 24 1998): Recognition of ES1788, ES1887, ES1888, - * ES1868, ES1869 and ES1878. Could be used for - * specific handling in the future. All except - * ES1887 and ES1888 and ES688 are handled like - * ES1688. - * (Dec 27 1998): RECLEV for all (?) ES1688+ chips. ES188x now - * have the "Dec 20" support + RECLEV - * (Jan 2 1999): Preparation for Full Duplex. This means - * Audio 2 is now used for playback when dma16 - * is specified. The next step would be to use - * Audio 1 and Audio 2 at the same time. - * (Jan 9 1999): Put all ESS stuff into sb_ess.[ch], this - * includes both the ESS stuff that has been in - * sb_*[ch] before I touched it and the ESS support - * I added later - * (Jan 23 1999): Full Duplex seems to work. I wrote a small - * test proggy which works OK. Haven't found - * any applications to test it though. So why did - * I bother to create it anyway?? :) Just for - * fun. - * (May 2 1999): I tried to be too smart by "introducing" - * ess_calc_best_speed (). The idea was that two - * dividers could be used to setup a samplerate, - * ess_calc_best_speed () would choose the best. - * This works for playback, but results in - * recording problems for high samplerates. I - * fixed this by removing ess_calc_best_speed () - * and just doing what the documentation says. - * Andy Sloane (Jun 4 1999): Stole some code from ALSA to fix the playback - * andy@guildsoftware.com speed on ES1869, ES1879, ES1887, and ES1888. - * 1879's were previously ignored by this driver; - * added (untested) support for those. - * Cvetan Ivanov (Oct 27 1999): Fixed ess_dsp_init to call ess_set_dma_hw for - * zezo@inet.bg _ALL_ ESS models, not only ES1887 - * - * This files contains ESS chip specifics. It's based on the existing ESS - * handling as it resided in sb_common.c, sb_mixer.c and sb_audio.c. This - * file adds features like: - * - Chip Identification (as shown in /proc/sound) - * - RECLEV support for ES1688 and later - * - 6 bits playback level support chips later than ES1688 - * - Recording level support on a per-device basis for ES1887 - * - Full-Duplex for ES1887 - * - * Full duplex is enabled by specifying dma16. While the normal dma must - * be one of 0, 1 or 3, dma16 can be one of 0, 1, 3 or 5. DMA 5 is a 16 bit - * DMA channel, while the others are 8 bit.. - * - * ESS detection isn't full proof (yet). If it fails an additional module - * parameter esstype can be specified to be one of the following: - * -1, 0, 688, 1688, 1868, 1869, 1788, 1887, 1888 - * -1 means: mimic 2.0 behaviour, - * 0 means: auto detect. - * others: explicitly specify chip - * -1 is default, cause auto detect still doesn't work. - */ - -/* - * About the documentation - * - * I don't know if the chips all are OK, but the documentation is buggy. 'cause - * I don't have all the cips myself, there's a lot I cannot verify. I'll try to - * keep track of my latest insights about his here. If you have additional info, - * please enlighten me (fokkensr@vertis.nl)! - * - * I had the impression that ES1688 also has 6 bit master volume control. The - * documentation about ES1888 (rev C, october '95) claims that ES1888 has - * the following features ES1688 doesn't have: - * - 6 bit master volume - * - Full Duplex - * So ES1688 apparently doesn't have 6 bit master volume control, but the - * ES1688 does have RECLEV control. Makes me wonder: does ES688 have it too? - * Without RECLEV ES688 won't be much fun I guess. - * - * From the ES1888 (rev C, october '95) documentation I got the impression - * that registers 0x68 to 0x6e don't exist which means: no recording volume - * controls. To my surprise the ES888 documentation (1/14/96) claims that - * ES888 does have these record mixer registers, but that ES1888 doesn't have - * 0x69 and 0x6b. So the rest should be there. - * - * I'm trying to get ES1887 Full Duplex. Audio 2 is playback only, while Audio 2 - * is both record and playback. I think I should use Audio 2 for all playback. - * - * The documentation is an adventure: it's close but not fully accurate. I - * found out that after a reset some registers are *NOT* reset, though the - * docs say the would be. Interesting ones are 0x7f, 0x7d and 0x7a. They are - * related to the Audio 2 channel. I also was surprised about the consequences - * of writing 0x00 to 0x7f (which should be done by reset): The ES1887 moves - * into ES1888 mode. This means that it claims IRQ 11, which happens to be my - * ISDN adapter. Needless to say it no longer worked. I now understand why - * after rebooting 0x7f already was 0x05, the value of my choice: the BIOS - * did it. - * - * Oh, and this is another trap: in ES1887 docs mixer register 0x70 is - * described as if it's exactly the same as register 0xa1. This is *NOT* true. - * The description of 0x70 in ES1869 docs is accurate however. - * Well, the assumption about ES1869 was wrong: register 0x70 is very much - * like register 0xa1, except that bit 7 is always 1, whatever you want - * it to be. - * - * When using audio 2 mixer register 0x72 seems te be meaningless. Only 0xa2 - * has effect. - * - * Software reset not being able to reset all registers is great! Especially - * the fact that register 0x78 isn't reset is great when you wanna change back - * to single dma operation (simplex): audio 2 is still operational, and uses - * the same dma as audio 1: your ess changes into a funny echo machine. - * - * Received the news that ES1688 is detected as a ES1788. Did some thinking: - * the ES1887 detection scheme suggests in step 2 to try if bit 3 of register - * 0x64 can be changed. This is inaccurate, first I inverted the * check: "If - * can be modified, it's a 1688", which lead to a correct detection - * of my ES1887. It resulted however in bad detection of 1688 (reported by mail) - * and 1868 (if no PnP detection first): they result in a 1788 being detected. - * I don't have docs on 1688, but I do have docs on 1868: The documentation is - * probably inaccurate in the fact that I should check bit 2, not bit 3. This - * is what I do now. - */ - -/* - * About recognition of ESS chips - * - * The distinction of ES688, ES1688, ES1788, ES1887 and ES1888 is described in - * a (preliminary ??) datasheet on ES1887. Its aim is to identify ES1887, but - * during detection the text claims that "this chip may be ..." when a step - * fails. This scheme is used to distinct between the above chips. - * It appears however that some PnP chips like ES1868 are recognized as ES1788 - * by the ES1887 detection scheme. These PnP chips can be detected in another - * way however: ES1868, ES1869 and ES1878 can be recognized (full proof I think) - * by repeatedly reading mixer register 0x40. This is done by ess_identify in - * sb_common.c. - * This results in the following detection steps: - * - distinct between ES688 and ES1688+ (as always done in this driver) - * if ES688 we're ready - * - try to detect ES1868, ES1869 or ES1878 - * if successful we're ready - * - try to detect ES1888, ES1887 or ES1788 - * if successful we're ready - * - Dunno. Must be 1688. Will do in general - * - * About RECLEV support: - * - * The existing ES1688 support didn't take care of the ES1688+ recording - * levels very well. Whenever a device was selected (recmask) for recording - * its recording level was loud, and it couldn't be changed. The fact that - * internal register 0xb4 could take care of RECLEV, didn't work meaning until - * its value was restored every time the chip was reset; this reset the - * value of 0xb4 too. I guess that's what 4front also had (have?) trouble with. - * - * About ES1887 support: - * - * The ES1887 has separate registers to control the recording levels, for all - * inputs. The ES1887 specific software makes these levels the same as their - * corresponding playback levels, unless recmask says they aren't recorded. In - * the latter case the recording volumes are 0. - * Now recording levels of inputs can be controlled, by changing the playback - * levels. Furthermore several devices can be recorded together (which is not - * possible with the ES1688). - * Besides the separate recording level control for each input, the common - * recording level can also be controlled by RECLEV as described above. - * - * Not only ES1887 have this recording mixer. I know the following from the - * documentation: - * ES688 no - * ES1688 no - * ES1868 no - * ES1869 yes - * ES1878 no - * ES1879 yes - * ES1888 no/yes Contradicting documentation; most recent: yes - * ES1946 yes This is a PCI chip; not handled by this driver - */ - -#include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/spinlock.h> - -#include "sound_config.h" -#include "sb_mixer.h" -#include "sb.h" - -#include "sb_ess.h" - -#define ESSTYPE_LIKE20 -1 /* Mimic 2.0 behaviour */ -#define ESSTYPE_DETECT 0 /* Mimic 2.0 behaviour */ - -#define SUBMDL_ES1788 0x10 /* Subtype ES1788 for specific handling */ -#define SUBMDL_ES1868 0x11 /* Subtype ES1868 for specific handling */ -#define SUBMDL_ES1869 0x12 /* Subtype ES1869 for specific handling */ -#define SUBMDL_ES1878 0x13 /* Subtype ES1878 for specific handling */ -#define SUBMDL_ES1879 0x16 /* ES1879 was initially forgotten */ -#define SUBMDL_ES1887 0x14 /* Subtype ES1887 for specific handling */ -#define SUBMDL_ES1888 0x15 /* Subtype ES1888 for specific handling */ - -#define SB_CAP_ES18XX_RATE 0x100 - -#define ES1688_CLOCK1 795444 /* 128 - div */ -#define ES1688_CLOCK2 397722 /* 256 - div */ -#define ES18XX_CLOCK1 793800 /* 128 - div */ -#define ES18XX_CLOCK2 768000 /* 256 - div */ - -#ifdef FKS_LOGGING -static void ess_show_mixerregs (sb_devc *devc); -#endif -static int ess_read (sb_devc * devc, unsigned char reg); -static int ess_write (sb_devc * devc, unsigned char reg, unsigned char data); -static void ess_chgmixer - (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val); - -/**************************************************************************** - * * - * ESS audio * - * * - ****************************************************************************/ - -struct ess_command {short cmd; short data;}; - -/* - * Commands for initializing Audio 1 for input (record) - */ -static struct ess_command ess_i08m[] = /* input 8 bit mono */ - { {0xb7, 0x51}, {0xb7, 0xd0}, {-1, 0} }; -static struct ess_command ess_i16m[] = /* input 16 bit mono */ - { {0xb7, 0x71}, {0xb7, 0xf4}, {-1, 0} }; -static struct ess_command ess_i08s[] = /* input 8 bit stereo */ - { {0xb7, 0x51}, {0xb7, 0x98}, {-1, 0} }; -static struct ess_command ess_i16s[] = /* input 16 bit stereo */ - { {0xb7, 0x71}, {0xb7, 0xbc}, {-1, 0} }; - -static struct ess_command *ess_inp_cmds[] = - { ess_i08m, ess_i16m, ess_i08s, ess_i16s }; - - -/* - * Commands for initializing Audio 1 for output (playback) - */ -static struct ess_command ess_o08m[] = /* output 8 bit mono */ - { {0xb6, 0x80}, {0xb7, 0x51}, {0xb7, 0xd0}, {-1, 0} }; -static struct ess_command ess_o16m[] = /* output 16 bit mono */ - { {0xb6, 0x00}, {0xb7, 0x71}, {0xb7, 0xf4}, {-1, 0} }; -static struct ess_command ess_o08s[] = /* output 8 bit stereo */ - { {0xb6, 0x80}, {0xb7, 0x51}, {0xb7, 0x98}, {-1, 0} }; -static struct ess_command ess_o16s[] = /* output 16 bit stereo */ - { {0xb6, 0x00}, {0xb7, 0x71}, {0xb7, 0xbc}, {-1, 0} }; - -static struct ess_command *ess_out_cmds[] = - { ess_o08m, ess_o16m, ess_o08s, ess_o16s }; - -static void ess_exec_commands - (sb_devc *devc, struct ess_command *cmdtab[]) -{ - struct ess_command *cmd; - - cmd = cmdtab [ ((devc->channels != 1) << 1) + (devc->bits != AFMT_U8) ]; - - while (cmd->cmd != -1) { - ess_write (devc, cmd->cmd, cmd->data); - cmd++; - } -} - -static void ess_change - (sb_devc *devc, unsigned int reg, unsigned int mask, unsigned int val) -{ - int value; - - value = ess_read (devc, reg); - value = (value & ~mask) | (val & mask); - ess_write (devc, reg, value); -} - -static void ess_set_output_parms - (int dev, unsigned long buf, int nr_bytes, int intrflag) -{ - sb_devc *devc = audio_devs[dev]->devc; - - if (devc->duplex) { - devc->trg_buf_16 = buf; - devc->trg_bytes_16 = nr_bytes; - devc->trg_intrflag_16 = intrflag; - devc->irq_mode_16 = IMODE_OUTPUT; - } else { - devc->trg_buf = buf; - devc->trg_bytes = nr_bytes; - devc->trg_intrflag = intrflag; - devc->irq_mode = IMODE_OUTPUT; - } -} - -static void ess_set_input_parms - (int dev, unsigned long buf, int count, int intrflag) -{ - sb_devc *devc = audio_devs[dev]->devc; - - devc->trg_buf = buf; - devc->trg_bytes = count; - devc->trg_intrflag = intrflag; - devc->irq_mode = IMODE_INPUT; -} - -static int ess_calc_div (int clock, int revert, int *speedp, int *diffp) -{ - int divider; - int speed, diff; - int retval; - - speed = *speedp; - divider = (clock + speed / 2) / speed; - retval = revert - divider; - if (retval > revert - 1) { - retval = revert - 1; - divider = revert - retval; - } - /* This line is suggested. Must be wrong I think - *speedp = (clock + divider / 2) / divider; - So I chose the next one */ - - *speedp = clock / divider; - diff = speed - *speedp; - if (diff < 0) diff =-diff; - *diffp = diff; - - return retval; -} - -static int ess_calc_best_speed - (int clock1, int rev1, int clock2, int rev2, int *divp, int *speedp) -{ - int speed1 = *speedp, speed2 = *speedp; - int div1, div2; - int diff1, diff2; - int retval; - - div1 = ess_calc_div (clock1, rev1, &speed1, &diff1); - div2 = ess_calc_div (clock2, rev2, &speed2, &diff2); - - if (diff1 < diff2) { - *divp = div1; - *speedp = speed1; - retval = 1; - } else { - /* *divp = div2; */ - *divp = 0x80 | div2; - *speedp = speed2; - retval = 2; - } - - return retval; -} - -/* - * Depending on the audiochannel ESS devices can - * have different clock settings. These are made consistent for duplex - * however. - * callers of ess_speed only do an audionum suggestion, which means - * input suggests 1, output suggests 2. This suggestion is only true - * however when doing duplex. - */ -static void ess_common_speed (sb_devc *devc, int *speedp, int *divp) -{ - int diff = 0, div; - - if (devc->duplex) { - /* - * The 0x80 is important for the first audio channel - */ - if (devc->submodel == SUBMDL_ES1888) { - div = 0x80 | ess_calc_div (795500, 256, speedp, &diff); - } else { - div = 0x80 | ess_calc_div (795500, 128, speedp, &diff); - } - } else if(devc->caps & SB_CAP_ES18XX_RATE) { - if (devc->submodel == SUBMDL_ES1888) { - ess_calc_best_speed(397700, 128, 795500, 256, - &div, speedp); - } else { - ess_calc_best_speed(ES18XX_CLOCK1, 128, ES18XX_CLOCK2, 256, - &div, speedp); - } - } else { - if (*speedp > 22000) { - div = 0x80 | ess_calc_div (ES1688_CLOCK1, 256, speedp, &diff); - } else { - div = 0x00 | ess_calc_div (ES1688_CLOCK2, 128, speedp, &diff); - } - } - *divp = div; -} - -static void ess_speed (sb_devc *devc, int audionum) -{ - int speed; - int div, div2; - - ess_common_speed (devc, &(devc->speed), &div); - -#ifdef FKS_REG_LOGGING -printk (KERN_INFO "FKS: ess_speed (%d) b speed = %d, div=%x\n", audionum, devc->speed, div); -#endif - - /* Set filter roll-off to 90% of speed/2 */ - speed = (devc->speed * 9) / 20; - - div2 = 256 - 7160000 / (speed * 82); - - if (!devc->duplex) audionum = 1; - - if (audionum == 1) { - /* Change behaviour of register A1 * - sb_chg_mixer(devc, 0x71, 0x20, 0x20) - * For ES1869 only??? */ - ess_write (devc, 0xa1, div); - ess_write (devc, 0xa2, div2); - } else { - ess_setmixer (devc, 0x70, div); - /* - * FKS: fascinating: 0x72 doesn't seem to work. - */ - ess_write (devc, 0xa2, div2); - ess_setmixer (devc, 0x72, div2); - } -} - -static int ess_audio_prepare_for_input(int dev, int bsize, int bcount) -{ - sb_devc *devc = audio_devs[dev]->devc; - - ess_speed(devc, 1); - - sb_dsp_command(devc, DSP_CMD_SPKOFF); - - ess_write (devc, 0xb8, 0x0e); /* Auto init DMA mode */ - ess_change (devc, 0xa8, 0x03, 3 - devc->channels); /* Mono/stereo */ - ess_write (devc, 0xb9, 2); /* Demand mode (4 bytes/DMA request) */ - - ess_exec_commands (devc, ess_inp_cmds); - - ess_change (devc, 0xb1, 0xf0, 0x50); - ess_change (devc, 0xb2, 0xf0, 0x50); - - devc->trigger_bits = 0; - return 0; -} - -static int ess_audio_prepare_for_output_audio1 (int dev, int bsize, int bcount) -{ - sb_devc *devc = audio_devs[dev]->devc; - - sb_dsp_reset(devc); - ess_speed(devc, 1); - ess_write (devc, 0xb8, 4); /* Auto init DMA mode */ - ess_change (devc, 0xa8, 0x03, 3 - devc->channels); /* Mono/stereo */ - ess_write (devc, 0xb9, 2); /* Demand mode (4 bytes/request) */ - - ess_exec_commands (devc, ess_out_cmds); - - ess_change (devc, 0xb1, 0xf0, 0x50); /* Enable DMA */ - ess_change (devc, 0xb2, 0xf0, 0x50); /* Enable IRQ */ - - sb_dsp_command(devc, DSP_CMD_SPKON); /* There be sound! */ - - devc->trigger_bits = 0; - return 0; -} - -static int ess_audio_prepare_for_output_audio2 (int dev, int bsize, int bcount) -{ - sb_devc *devc = audio_devs[dev]->devc; - unsigned char bits; - -/* FKS: qqq - sb_dsp_reset(devc); -*/ - - /* - * Auto-Initialize: - * DMA mode + demand mode (8 bytes/request, yes I want it all!) - * But leave 16-bit DMA bit untouched! - */ - ess_chgmixer (devc, 0x78, 0xd0, 0xd0); - - ess_speed(devc, 2); - - /* bits 4:3 on ES1887 represent recording source. Keep them! */ - bits = ess_getmixer (devc, 0x7a) & 0x18; - - /* Set stereo/mono */ - if (devc->channels != 1) bits |= 0x02; - - /* Init DACs; UNSIGNED mode for 8 bit; SIGNED mode for 16 bit */ - if (devc->bits != AFMT_U8) bits |= 0x05; /* 16 bit */ - - /* Enable DMA, IRQ will be shared (hopefully)*/ - bits |= 0x60; - - ess_setmixer (devc, 0x7a, bits); - - ess_mixer_reload (devc, SOUND_MIXER_PCM); /* There be sound! */ - - devc->trigger_bits = 0; - return 0; -} - -static int ess_audio_prepare_for_output(int dev, int bsize, int bcount) -{ - sb_devc *devc = audio_devs[dev]->devc; - -#ifdef FKS_REG_LOGGING -printk(KERN_INFO "ess_audio_prepare_for_output: dma_out=%d,dma_in=%d\n" -, audio_devs[dev]->dmap_out->dma, audio_devs[dev]->dmap_in->dma); -#endif - - if (devc->duplex) { - return ess_audio_prepare_for_output_audio2 (dev, bsize, bcount); - } else { - return ess_audio_prepare_for_output_audio1 (dev, bsize, bcount); - } -} - -static void ess_audio_halt_xfer(int dev) -{ - unsigned long flags; - sb_devc *devc = audio_devs[dev]->devc; - - spin_lock_irqsave(&devc->lock, flags); - sb_dsp_reset(devc); - spin_unlock_irqrestore(&devc->lock, flags); - - /* - * Audio 2 may still be operational! Creates awful sounds! - */ - if (devc->duplex) ess_chgmixer(devc, 0x78, 0x03, 0x00); -} - -static void ess_audio_start_input - (int dev, unsigned long buf, int nr_bytes, int intrflag) -{ - int count = nr_bytes; - sb_devc *devc = audio_devs[dev]->devc; - short c = -nr_bytes; - - /* - * Start a DMA input to the buffer pointed by dmaqtail - */ - - if (audio_devs[dev]->dmap_in->dma > 3) count >>= 1; - count--; - - devc->irq_mode = IMODE_INPUT; - - ess_write (devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff)); - ess_write (devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff)); - - ess_change (devc, 0xb8, 0x0f, 0x0f); /* Go */ - devc->intr_active = 1; -} - -static void ess_audio_output_block_audio1 - (int dev, unsigned long buf, int nr_bytes, int intrflag) -{ - int count = nr_bytes; - sb_devc *devc = audio_devs[dev]->devc; - short c = -nr_bytes; - - if (audio_devs[dev]->dmap_out->dma > 3) - count >>= 1; - count--; - - devc->irq_mode = IMODE_OUTPUT; - - ess_write (devc, 0xa4, (unsigned char) ((unsigned short) c & 0xff)); - ess_write (devc, 0xa5, (unsigned char) (((unsigned short) c >> 8) & 0xff)); - - ess_change (devc, 0xb8, 0x05, 0x05); /* Go */ - devc->intr_active = 1; -} - -static void ess_audio_output_block_audio2 - (int dev, unsigned long buf, int nr_bytes, int intrflag) -{ - int count = nr_bytes; - sb_devc *devc = audio_devs[dev]->devc; - short c = -nr_bytes; - - if (audio_devs[dev]->dmap_out->dma > 3) count >>= 1; - count--; - - ess_setmixer (devc, 0x74, (unsigned char) ((unsigned short) c & 0xff)); - ess_setmixer (devc, 0x76, (unsigned char) (((unsigned short) c >> 8) & 0xff)); - ess_chgmixer (devc, 0x78, 0x03, 0x03); /* Go */ - - devc->irq_mode_16 = IMODE_OUTPUT; - devc->intr_active_16 = 1; -} - -static void ess_audio_output_block - (int dev, unsigned long buf, int nr_bytes, int intrflag) -{ - sb_devc *devc = audio_devs[dev]->devc; - - if (devc->duplex) { - ess_audio_output_block_audio2 (dev, buf, nr_bytes, intrflag); - } else { - ess_audio_output_block_audio1 (dev, buf, nr_bytes, intrflag); - } -} - -/* - * FKS: the if-statements for both bits and bits_16 are quite alike. - * Combine this... - */ -static void ess_audio_trigger(int dev, int bits) -{ - sb_devc *devc = audio_devs[dev]->devc; - - int bits_16 = bits & devc->irq_mode_16; - bits &= devc->irq_mode; - - if (!bits && !bits_16) { - /* FKS oh oh.... wrong?? for dma 16? */ - sb_dsp_command(devc, 0xd0); /* Halt DMA */ - } - - if (bits) { - switch (devc->irq_mode) - { - case IMODE_INPUT: - ess_audio_start_input(dev, devc->trg_buf, devc->trg_bytes, - devc->trg_intrflag); - break; - - case IMODE_OUTPUT: - ess_audio_output_block(dev, devc->trg_buf, devc->trg_bytes, - devc->trg_intrflag); - break; - } - } - - if (bits_16) { - switch (devc->irq_mode_16) { - case IMODE_INPUT: - ess_audio_start_input(dev, devc->trg_buf_16, devc->trg_bytes_16, - devc->trg_intrflag_16); - break; - - case IMODE_OUTPUT: - ess_audio_output_block(dev, devc->trg_buf_16, devc->trg_bytes_16, - devc->trg_intrflag_16); - break; - } - } - - devc->trigger_bits = bits | bits_16; -} - -static int ess_audio_set_speed(int dev, int speed) -{ - sb_devc *devc = audio_devs[dev]->devc; - int minspeed, maxspeed, dummydiv; - - if (speed > 0) { - minspeed = (devc->duplex ? 6215 : 5000 ); - maxspeed = (devc->duplex ? 44100 : 48000); - if (speed < minspeed) speed = minspeed; - if (speed > maxspeed) speed = maxspeed; - - ess_common_speed (devc, &speed, &dummydiv); - - devc->speed = speed; - } - return devc->speed; -} - -/* - * FKS: This is a one-on-one copy of sb1_audio_set_bits - */ -static unsigned int ess_audio_set_bits(int dev, unsigned int bits) -{ - sb_devc *devc = audio_devs[dev]->devc; - - if (bits != 0) { - if (bits == AFMT_U8 || bits == AFMT_S16_LE) { - devc->bits = bits; - } else { - devc->bits = AFMT_U8; - } - } - - return devc->bits; -} - -/* - * FKS: This is a one-on-one copy of sbpro_audio_set_channels - * (*) Modified it!! - */ -static short ess_audio_set_channels(int dev, short channels) -{ - sb_devc *devc = audio_devs[dev]->devc; - - if (channels == 1 || channels == 2) devc->channels = channels; - - return devc->channels; -} - -static struct audio_driver ess_audio_driver = /* ESS ES688/1688 */ -{ - .owner = THIS_MODULE, - .open = sb_audio_open, - .close = sb_audio_close, - .output_block = ess_set_output_parms, - .start_input = ess_set_input_parms, - .prepare_for_input = ess_audio_prepare_for_input, - .prepare_for_output = ess_audio_prepare_for_output, - .halt_io = ess_audio_halt_xfer, - .trigger = ess_audio_trigger, - .set_speed = ess_audio_set_speed, - .set_bits = ess_audio_set_bits, - .set_channels = ess_audio_set_channels -}; - -/* - * ess_audio_init must be called from sb_audio_init - */ -struct audio_driver *ess_audio_init - (sb_devc *devc, int *audio_flags, int *format_mask) -{ - *audio_flags = DMA_AUTOMODE; - *format_mask |= AFMT_S16_LE; - - if (devc->duplex) { - int tmp_dma; - /* - * sb_audio_init thinks dma8 is for playback and - * dma16 is for record. Not now! So swap them. - */ - tmp_dma = devc->dma16; - devc->dma16 = devc->dma8; - devc->dma8 = tmp_dma; - - *audio_flags |= DMA_DUPLEX; - } - - return &ess_audio_driver; -} - -/**************************************************************************** - * * - * ESS common * - * * - ****************************************************************************/ -static void ess_handle_channel - (char *channel, int dev, int intr_active, unsigned char flag, int irq_mode) -{ - if (!intr_active || !flag) return; -#ifdef FKS_REG_LOGGING -printk(KERN_INFO "FKS: ess_handle_channel %s irq_mode=%d\n", channel, irq_mode); -#endif - switch (irq_mode) { - case IMODE_OUTPUT: - DMAbuf_outputintr (dev, 1); - break; - - case IMODE_INPUT: - DMAbuf_inputintr (dev); - break; - - case IMODE_INIT: - break; - - default:; - /* printk(KERN_WARNING "ESS: Unexpected interrupt\n"); */ - } -} - -/* - * FKS: TODO!!! Finish this! - * - * I think midi stuff uses uart401, without interrupts. - * So IMODE_MIDI isn't a value for devc->irq_mode. - */ -void ess_intr (sb_devc *devc) -{ - int status; - unsigned char src; - - if (devc->submodel == SUBMDL_ES1887) { - src = ess_getmixer (devc, 0x7f) >> 4; - } else { - src = 0xff; - } - -#ifdef FKS_REG_LOGGING -printk(KERN_INFO "FKS: sbintr src=%x\n",(int)src); -#endif - ess_handle_channel - ( "Audio 1" - , devc->dev, devc->intr_active , src & 0x01, devc->irq_mode ); - ess_handle_channel - ( "Audio 2" - , devc->dev, devc->intr_active_16, src & 0x02, devc->irq_mode_16); - /* - * Acknowledge interrupts - */ - if (devc->submodel == SUBMDL_ES1887 && (src & 0x02)) { - ess_chgmixer (devc, 0x7a, 0x80, 0x00); - } - - if (src & 0x01) { - status = inb(DSP_DATA_AVAIL); - } -} - -static void ess_extended (sb_devc * devc) -{ - /* Enable extended mode */ - - sb_dsp_command(devc, 0xc6); -} - -static int ess_write (sb_devc * devc, unsigned char reg, unsigned char data) -{ -#ifdef FKS_REG_LOGGING -printk(KERN_INFO "FKS: write reg %x: %x\n", reg, data); -#endif - /* Write a byte to an extended mode register of ES1688 */ - - if (!sb_dsp_command(devc, reg)) - return 0; - - return sb_dsp_command(devc, data); -} - -static int ess_read (sb_devc * devc, unsigned char reg) -{ - /* Read a byte from an extended mode register of ES1688 */ - - /* Read register command */ - if (!sb_dsp_command(devc, 0xc0)) return -1; - - if (!sb_dsp_command(devc, reg )) return -1; - - return sb_dsp_get_byte(devc); -} - -int ess_dsp_reset(sb_devc * devc) -{ - int loopc; - -#ifdef FKS_REG_LOGGING -printk(KERN_INFO "FKS: ess_dsp_reset 1\n"); -ess_show_mixerregs (devc); -#endif - - outb(3, DSP_RESET); /* Reset FIFO too */ - - udelay(10); - outb(0, DSP_RESET); - udelay(30); - - for (loopc = 0; loopc < 1000 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++); - - if (inb(DSP_READ) != 0xAA) { - DDB(printk("sb: No response to RESET\n")); - return 0; /* Sorry */ - } - ess_extended (devc); - -#ifdef FKS_LOGGING -printk(KERN_INFO "FKS: dsp_reset 2\n"); -ess_show_mixerregs (devc); -#endif - - return 1; -} - -static int ess_irq_bits (int irq) -{ - switch (irq) { - case 2: - case 9: - return 0; - - case 5: - return 1; - - case 7: - return 2; - - case 10: - return 3; - - default: - printk(KERN_ERR "ESS1688: Invalid IRQ %d\n", irq); - return -1; - } -} - -/* - * Set IRQ configuration register for all ESS models - */ -static int ess_common_set_irq_hw (sb_devc * devc) -{ - int irq_bits; - - if ((irq_bits = ess_irq_bits (devc->irq)) == -1) return 0; - - if (!ess_write (devc, 0xb1, 0x50 | (irq_bits << 2))) { - printk(KERN_ERR "ES1688: Failed to write to IRQ config register\n"); - return 0; - } - return 1; -} - -/* - * I wanna use modern ES1887 mixer irq handling. Funny is the - * fact that my BIOS wants the same. But suppose someone's BIOS - * doesn't do this! - * This is independent of duplex. If there's a 1887 this will - * prevent it from going into 1888 mode. - */ -static void ess_es1887_set_irq_hw (sb_devc * devc) -{ - int irq_bits; - - if ((irq_bits = ess_irq_bits (devc->irq)) == -1) return; - - ess_chgmixer (devc, 0x7f, 0x0f, 0x01 | ((irq_bits + 1) << 1)); -} - -static int ess_set_irq_hw (sb_devc * devc) -{ - if (devc->submodel == SUBMDL_ES1887) ess_es1887_set_irq_hw (devc); - - return ess_common_set_irq_hw (devc); -} - -#ifdef FKS_TEST - -/* - * FKS_test: - * for ES1887: 00, 18, non wr bits: 0001 1000 - * for ES1868: 00, b8, non wr bits: 1011 1000 - * for ES1888: 00, f8, non wr bits: 1111 1000 - * for ES1688: 00, f8, non wr bits: 1111 1000 - * + ES968 - */ - -static void FKS_test (sb_devc * devc) -{ - int val1, val2; - val1 = ess_getmixer (devc, 0x64); - ess_setmixer (devc, 0x64, ~val1); - val2 = ess_getmixer (devc, 0x64) ^ ~val1; - ess_setmixer (devc, 0x64, val1); - val1 ^= ess_getmixer (devc, 0x64); -printk (KERN_INFO "FKS: FKS_test %02x, %02x\n", (val1 & 0x0ff), (val2 & 0x0ff)); -}; -#endif - -static unsigned int ess_identify (sb_devc * devc) -{ - unsigned int val; - unsigned long flags; - - spin_lock_irqsave(&devc->lock, flags); - outb(((unsigned char) (0x40 & 0xff)), MIXER_ADDR); - - udelay(20); - val = inb(MIXER_DATA) << 8; - udelay(20); - val |= inb(MIXER_DATA); - udelay(20); - spin_unlock_irqrestore(&devc->lock, flags); - - return val; -} - -/* - * ESS technology describes a detection scheme in their docs. It involves - * fiddling with the bits in certain mixer registers. ess_probe is supposed - * to help. - * - * FKS: tracing shows ess_probe writes wrong value to 0x64. Bit 3 reads 1, but - * should be written 0 only. Check this. - */ -static int ess_probe (sb_devc * devc, int reg, int xorval) -{ - int val1, val2, val3; - - val1 = ess_getmixer (devc, reg); - val2 = val1 ^ xorval; - ess_setmixer (devc, reg, val2); - val3 = ess_getmixer (devc, reg); - ess_setmixer (devc, reg, val1); - - return (val2 == val3); -} - -int ess_init(sb_devc * devc, struct address_info *hw_config) -{ - unsigned char cfg; - int ess_major = 0, ess_minor = 0; - int i; - static char name[100], modelname[10]; - - /* - * Try to detect ESS chips. - */ - - sb_dsp_command(devc, 0xe7); /* Return identification */ - - for (i = 1000; i; i--) { - if (inb(DSP_DATA_AVAIL) & 0x80) { - if (ess_major == 0) { - ess_major = inb(DSP_READ); - } else { - ess_minor = inb(DSP_READ); - break; - } - } - } - - if (ess_major == 0) return 0; - - if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80) { - sprintf(name, "ESS ES488 AudioDrive (rev %d)", - ess_minor & 0x0f); - hw_config->name = name; - devc->model = MDL_SBPRO; - return 1; - } - - /* - * This the detection heuristic of ESS technology, though somewhat - * changed to actually make it work. - * This results in the following detection steps: - * - distinct between ES688 and ES1688+ (as always done in this driver) - * if ES688 we're ready - * - try to detect ES1868, ES1869 or ES1878 (ess_identify) - * if successful we're ready - * - try to detect ES1888, ES1887 or ES1788 (aim: detect ES1887) - * if successful we're ready - * - Dunno. Must be 1688. Will do in general - * - * This is the most BETA part of the software: Will the detection - * always work? - */ - devc->model = MDL_ESS; - devc->submodel = ess_minor & 0x0f; - - if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80) { - char *chip = NULL; - int submodel = -1; - - switch (devc->sbmo.esstype) { - case ESSTYPE_DETECT: - case ESSTYPE_LIKE20: - break; - case 688: - submodel = 0x00; - break; - case 1688: - submodel = 0x08; - break; - case 1868: - submodel = SUBMDL_ES1868; - break; - case 1869: - submodel = SUBMDL_ES1869; - break; - case 1788: - submodel = SUBMDL_ES1788; - break; - case 1878: - submodel = SUBMDL_ES1878; - break; - case 1879: - submodel = SUBMDL_ES1879; - break; - case 1887: - submodel = SUBMDL_ES1887; - break; - case 1888: - submodel = SUBMDL_ES1888; - break; - default: - printk (KERN_ERR "Invalid esstype=%d specified\n", devc->sbmo.esstype); - return 0; - } - if (submodel != -1) { - devc->submodel = submodel; - sprintf (modelname, "ES%d", devc->sbmo.esstype); - chip = modelname; - } - if (chip == NULL && (ess_minor & 0x0f) < 8) { - chip = "ES688"; - } -#ifdef FKS_TEST -FKS_test (devc); -#endif - /* - * If Nothing detected yet, and we want 2.0 behaviour... - * Then let's assume it's ES1688. - */ - if (chip == NULL && devc->sbmo.esstype == ESSTYPE_LIKE20) { - chip = "ES1688"; - } - - if (chip == NULL) { - int type; - - type = ess_identify (devc); - - switch (type) { - case 0x1868: - chip = "ES1868"; - devc->submodel = SUBMDL_ES1868; - break; - case 0x1869: - chip = "ES1869"; - devc->submodel = SUBMDL_ES1869; - break; - case 0x1878: - chip = "ES1878"; - devc->submodel = SUBMDL_ES1878; - break; - case 0x1879: - chip = "ES1879"; - devc->submodel = SUBMDL_ES1879; - break; - default: - if ((type & 0x00ff) != ((type >> 8) & 0x00ff)) { - printk ("ess_init: Unrecognized %04x\n", type); - } - } - } -#if 0 - /* - * this one failed: - * the probing of bit 4 is another thought: from ES1788 and up, all - * chips seem to have hardware volume control. Bit 4 is readonly to - * check if a hardware volume interrupt has fired. - * Cause ES688/ES1688 don't have this feature, bit 4 might be writeable - * for these chips. - */ - if (chip == NULL && !ess_probe(devc, 0x64, (1 << 4))) { -#endif - /* - * the probing of bit 2 is my idea. The ES1887 docs want me to probe - * bit 3. This results in ES1688 being detected as ES1788. - * Bit 2 is for "Enable HWV IRQE", but as ES(1)688 chips don't have - * HardWare Volume, I think they don't have this IRQE. - */ - if (chip == NULL && ess_probe(devc, 0x64, (1 << 2))) { - if (ess_probe (devc, 0x70, 0x7f)) { - if (ess_probe (devc, 0x64, (1 << 5))) { - chip = "ES1887"; - devc->submodel = SUBMDL_ES1887; - } else { - chip = "ES1888"; - devc->submodel = SUBMDL_ES1888; - } - } else { - chip = "ES1788"; - devc->submodel = SUBMDL_ES1788; - } - } - if (chip == NULL) { - chip = "ES1688"; - } - - printk(KERN_INFO "ESS chip %s %s%s\n", chip, - (devc->sbmo.esstype == ESSTYPE_DETECT || - devc->sbmo.esstype == ESSTYPE_LIKE20) ? - "detected" : "specified", - devc->sbmo.esstype == ESSTYPE_LIKE20 ? - " (kernel 2.0 compatible)" : ""); - - sprintf(name,"ESS %s AudioDrive (rev %d)", chip, ess_minor & 0x0f); - } else { - strcpy(name, "Jazz16"); - } - - /* AAS: info stolen from ALSA: these boards have different clocks */ - switch(devc->submodel) { -/* APPARENTLY NOT 1869 AND 1887 - case SUBMDL_ES1869: - case SUBMDL_ES1887: -*/ - case SUBMDL_ES1888: - devc->caps |= SB_CAP_ES18XX_RATE; - break; - } - - hw_config->name = name; - /* FKS: sb_dsp_reset to enable extended mode???? */ - sb_dsp_reset(devc); /* Turn on extended mode */ - - /* - * Enable joystick and OPL3 - */ - cfg = ess_getmixer (devc, 0x40); - ess_setmixer (devc, 0x40, cfg | 0x03); - if (devc->submodel >= 8) { /* ES1688 */ - devc->caps |= SB_NO_MIDI; /* ES1688 uses MPU401 MIDI mode */ - } - sb_dsp_reset (devc); - - /* - * This is important! If it's not done, the IRQ probe in sb_dsp_init - * may fail. - */ - return ess_set_irq_hw (devc); -} - -static int ess_set_dma_hw(sb_devc * devc) -{ - unsigned char cfg, dma_bits = 0, dma16_bits; - int dma; - -#ifdef FKS_LOGGING -printk(KERN_INFO "ess_set_dma_hw: dma8=%d,dma16=%d,dup=%d\n" -, devc->dma8, devc->dma16, devc->duplex); -#endif - - /* - * FKS: It seems as if this duplex flag isn't set yet. Check it. - */ - dma = devc->dma8; - - if (dma > 3 || dma < 0 || dma == 2) { - dma_bits = 0; - printk(KERN_ERR "ESS1688: Invalid DMA8 %d\n", dma); - return 0; - } else { - /* Extended mode DMA enable */ - cfg = 0x50; - - if (dma == 3) { - dma_bits = 3; - } else { - dma_bits = dma + 1; - } - } - - if (!ess_write (devc, 0xb2, cfg | (dma_bits << 2))) { - printk(KERN_ERR "ESS1688: Failed to write to DMA config register\n"); - return 0; - } - - if (devc->duplex) { - dma = devc->dma16; - dma16_bits = 0; - - if (dma >= 0) { - switch (dma) { - case 0: - dma_bits = 0x04; - break; - case 1: - dma_bits = 0x05; - break; - case 3: - dma_bits = 0x06; - break; - case 5: - dma_bits = 0x07; - dma16_bits = 0x20; - break; - default: - printk(KERN_ERR "ESS1887: Invalid DMA16 %d\n", dma); - return 0; - } - ess_chgmixer (devc, 0x78, 0x20, dma16_bits); - ess_chgmixer (devc, 0x7d, 0x07, dma_bits); - } - } - return 1; -} - -/* - * This one is called from sb_dsp_init. - * - * Return values: - * 0: Failed - * 1: Succeeded or doesn't apply (not SUBMDL_ES1887) - */ -int ess_dsp_init (sb_devc *devc, struct address_info *hw_config) -{ - /* - * Caller also checks this, but anyway - */ - if (devc->model != MDL_ESS) { - printk (KERN_INFO "ess_dsp_init for non ESS chip\n"); - return 1; - } - /* - * This for ES1887 to run Full Duplex. Actually ES1888 - * is allowed to do so too. I have no idea yet if this - * will work for ES1888 however. - * - * For SB16 having both dma8 and dma16 means enable - * Full Duplex. Let's try this for ES1887 too - * - */ - if (devc->submodel == SUBMDL_ES1887) { - if (hw_config->dma2 != -1) { - devc->dma16 = hw_config->dma2; - } - /* - * devc->duplex initialization is put here, cause - * ess_set_dma_hw needs it. - */ - if (devc->dma8 != devc->dma16 && devc->dma16 != -1) { - devc->duplex = 1; - } - } - if (!ess_set_dma_hw (devc)) { - free_irq(devc->irq, devc); - return 0; - } - return 1; -} - -/**************************************************************************** - * * - * ESS mixer * - * * - ****************************************************************************/ - -#define ES688_RECORDING_DEVICES \ - ( SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD ) -#define ES688_MIXER_DEVICES \ - ( SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE \ - | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VOLUME \ - | SOUND_MASK_LINE2 | SOUND_MASK_SPEAKER ) - -#define ES1688_RECORDING_DEVICES \ - ( ES688_RECORDING_DEVICES ) -#define ES1688_MIXER_DEVICES \ - ( ES688_MIXER_DEVICES | SOUND_MASK_RECLEV ) - -#define ES1887_RECORDING_DEVICES \ - ( ES1688_RECORDING_DEVICES | SOUND_MASK_LINE2 | SOUND_MASK_SYNTH) -#define ES1887_MIXER_DEVICES \ - ( ES1688_MIXER_DEVICES ) - -/* - * Mixer registers of ES1887 - * - * These registers specifically take care of recording levels. To make the - * mapping from playback devices to recording devices every recording - * devices = playback device + ES_REC_MIXER_RECDIFF - */ -#define ES_REC_MIXER_RECBASE (SOUND_MIXER_LINE3 + 1) -#define ES_REC_MIXER_RECDIFF (ES_REC_MIXER_RECBASE - SOUND_MIXER_SYNTH) - -#define ES_REC_MIXER_RECSYNTH (SOUND_MIXER_SYNTH + ES_REC_MIXER_RECDIFF) -#define ES_REC_MIXER_RECPCM (SOUND_MIXER_PCM + ES_REC_MIXER_RECDIFF) -#define ES_REC_MIXER_RECSPEAKER (SOUND_MIXER_SPEAKER + ES_REC_MIXER_RECDIFF) -#define ES_REC_MIXER_RECLINE (SOUND_MIXER_LINE + ES_REC_MIXER_RECDIFF) -#define ES_REC_MIXER_RECMIC (SOUND_MIXER_MIC + ES_REC_MIXER_RECDIFF) -#define ES_REC_MIXER_RECCD (SOUND_MIXER_CD + ES_REC_MIXER_RECDIFF) -#define ES_REC_MIXER_RECIMIX (SOUND_MIXER_IMIX + ES_REC_MIXER_RECDIFF) -#define ES_REC_MIXER_RECALTPCM (SOUND_MIXER_ALTPCM + ES_REC_MIXER_RECDIFF) -#define ES_REC_MIXER_RECRECLEV (SOUND_MIXER_RECLEV + ES_REC_MIXER_RECDIFF) -#define ES_REC_MIXER_RECIGAIN (SOUND_MIXER_IGAIN + ES_REC_MIXER_RECDIFF) -#define ES_REC_MIXER_RECOGAIN (SOUND_MIXER_OGAIN + ES_REC_MIXER_RECDIFF) -#define ES_REC_MIXER_RECLINE1 (SOUND_MIXER_LINE1 + ES_REC_MIXER_RECDIFF) -#define ES_REC_MIXER_RECLINE2 (SOUND_MIXER_LINE2 + ES_REC_MIXER_RECDIFF) -#define ES_REC_MIXER_RECLINE3 (SOUND_MIXER_LINE3 + ES_REC_MIXER_RECDIFF) - -static mixer_tab es688_mix = { -MIX_ENT(SOUND_MIXER_VOLUME, 0x32, 7, 4, 0x32, 3, 4), -MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4), -MIX_ENT(SOUND_MIXER_PCM, 0x14, 7, 4, 0x14, 3, 4), -MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4), -MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4), -MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4), -MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4), -MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0) -}; - -/* - * The ES1688 specifics... hopefully correct... - * - 6 bit master volume - * I was wrong, ES1888 docs say ES1688 didn't have it. - * - RECLEV control - * These may apply to ES688 too. I have no idea. - */ -static mixer_tab es1688_mix = { -MIX_ENT(SOUND_MIXER_VOLUME, 0x32, 7, 4, 0x32, 3, 4), -MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4), -MIX_ENT(SOUND_MIXER_PCM, 0x14, 7, 4, 0x14, 3, 4), -MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4), -MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4), -MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4), -MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_RECLEV, 0xb4, 7, 4, 0xb4, 3, 4), -MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4), -MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0) -}; - -static mixer_tab es1688later_mix = { -MIX_ENT(SOUND_MIXER_VOLUME, 0x60, 5, 6, 0x62, 5, 6), -MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4), -MIX_ENT(SOUND_MIXER_PCM, 0x14, 7, 4, 0x14, 3, 4), -MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4), -MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4), -MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4), -MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_RECLEV, 0xb4, 7, 4, 0xb4, 3, 4), -MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4), -MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0) -}; - -/* - * This one is for all ESS chips with a record mixer. - * It's not used (yet) however - */ -static mixer_tab es_rec_mix = { -MIX_ENT(SOUND_MIXER_VOLUME, 0x60, 5, 6, 0x62, 5, 6), -MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4), -MIX_ENT(SOUND_MIXER_PCM, 0x14, 7, 4, 0x14, 3, 4), -MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4), -MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4), -MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4), -MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_RECLEV, 0xb4, 7, 4, 0xb4, 3, 4), -MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4), -MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECSYNTH, 0x6b, 7, 4, 0x6b, 3, 4), -MIX_ENT(ES_REC_MIXER_RECPCM, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECSPEAKER, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECLINE, 0x6e, 7, 4, 0x6e, 3, 4), -MIX_ENT(ES_REC_MIXER_RECMIC, 0x68, 7, 4, 0x68, 3, 4), -MIX_ENT(ES_REC_MIXER_RECCD, 0x6a, 7, 4, 0x6a, 3, 4), -MIX_ENT(ES_REC_MIXER_RECIMIX, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECALTPCM, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECRECLEV, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECIGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECOGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECLINE1, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECLINE2, 0x6c, 7, 4, 0x6c, 3, 4), -MIX_ENT(ES_REC_MIXER_RECLINE3, 0x00, 0, 0, 0x00, 0, 0) -}; - -/* - * This one is for ES1887. It's little different from es_rec_mix: it - * has 0x7c for PCM playback level. This is because ES1887 uses - * Audio 2 for playback. - */ -static mixer_tab es1887_mix = { -MIX_ENT(SOUND_MIXER_VOLUME, 0x60, 5, 6, 0x62, 5, 6), -MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_SYNTH, 0x36, 7, 4, 0x36, 3, 4), -MIX_ENT(SOUND_MIXER_PCM, 0x7c, 7, 4, 0x7c, 3, 4), -MIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 2, 3, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE, 0x3e, 7, 4, 0x3e, 3, 4), -MIX_ENT(SOUND_MIXER_MIC, 0x1a, 7, 4, 0x1a, 3, 4), -MIX_ENT(SOUND_MIXER_CD, 0x38, 7, 4, 0x38, 3, 4), -MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_RECLEV, 0xb4, 7, 4, 0xb4, 3, 4), -MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE1, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE2, 0x3a, 7, 4, 0x3a, 3, 4), -MIX_ENT(SOUND_MIXER_LINE3, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECSYNTH, 0x6b, 7, 4, 0x6b, 3, 4), -MIX_ENT(ES_REC_MIXER_RECPCM, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECSPEAKER, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECLINE, 0x6e, 7, 4, 0x6e, 3, 4), -MIX_ENT(ES_REC_MIXER_RECMIC, 0x68, 7, 4, 0x68, 3, 4), -MIX_ENT(ES_REC_MIXER_RECCD, 0x6a, 7, 4, 0x6a, 3, 4), -MIX_ENT(ES_REC_MIXER_RECIMIX, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECALTPCM, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECRECLEV, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECIGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECOGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECLINE1, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(ES_REC_MIXER_RECLINE2, 0x6c, 7, 4, 0x6c, 3, 4), -MIX_ENT(ES_REC_MIXER_RECLINE3, 0x00, 0, 0, 0x00, 0, 0) -}; - -static int ess_has_rec_mixer (int submodel) -{ - switch (submodel) { - case SUBMDL_ES1887: - return 1; - default: - return 0; - } -}; - -#ifdef FKS_LOGGING -static int ess_mixer_mon_regs[] - = { 0x70, 0x71, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7d, 0x7f - , 0xa1, 0xa2, 0xa4, 0xa5, 0xa8, 0xa9 - , 0xb1, 0xb2, 0xb4, 0xb5, 0xb6, 0xb7, 0xb9 - , 0x00}; - -static void ess_show_mixerregs (sb_devc *devc) -{ - int *mp = ess_mixer_mon_regs; - -return; - - while (*mp != 0) { - printk (KERN_INFO "res (%x)=%x\n", *mp, (int)(ess_getmixer (devc, *mp))); - mp++; - } -} -#endif - -void ess_setmixer (sb_devc * devc, unsigned int port, unsigned int value) -{ - unsigned long flags; - -#ifdef FKS_LOGGING -printk(KERN_INFO "FKS: write mixer %x: %x\n", port, value); -#endif - - spin_lock_irqsave(&devc->lock, flags); - if (port >= 0xa0) { - ess_write (devc, port, value); - } else { - outb(((unsigned char) (port & 0xff)), MIXER_ADDR); - - udelay(20); - outb(((unsigned char) (value & 0xff)), MIXER_DATA); - udelay(20); - } - spin_unlock_irqrestore(&devc->lock, flags); -} - -unsigned int ess_getmixer (sb_devc * devc, unsigned int port) -{ - unsigned int val; - unsigned long flags; - - spin_lock_irqsave(&devc->lock, flags); - - if (port >= 0xa0) { - val = ess_read (devc, port); - } else { - outb(((unsigned char) (port & 0xff)), MIXER_ADDR); - - udelay(20); - val = inb(MIXER_DATA); - udelay(20); - } - spin_unlock_irqrestore(&devc->lock, flags); - - return val; -} - -static void ess_chgmixer - (sb_devc * devc, unsigned int reg, unsigned int mask, unsigned int val) -{ - int value; - - value = ess_getmixer (devc, reg); - value = (value & ~mask) | (val & mask); - ess_setmixer (devc, reg, value); -} - -/* - * ess_mixer_init must be called from sb_mixer_init - */ -void ess_mixer_init (sb_devc * devc) -{ - devc->mixer_caps = SOUND_CAP_EXCL_INPUT; - - /* - * Take care of ES1887 specifics... - */ - switch (devc->submodel) { - case SUBMDL_ES1887: - devc->supported_devices = ES1887_MIXER_DEVICES; - devc->supported_rec_devices = ES1887_RECORDING_DEVICES; -#ifdef FKS_LOGGING -printk (KERN_INFO "FKS: ess_mixer_init dup = %d\n", devc->duplex); -#endif - if (devc->duplex) { - devc->iomap = &es1887_mix; - devc->iomap_sz = ARRAY_SIZE(es1887_mix); - } else { - devc->iomap = &es_rec_mix; - devc->iomap_sz = ARRAY_SIZE(es_rec_mix); - } - break; - default: - if (devc->submodel < 8) { - devc->supported_devices = ES688_MIXER_DEVICES; - devc->supported_rec_devices = ES688_RECORDING_DEVICES; - devc->iomap = &es688_mix; - devc->iomap_sz = ARRAY_SIZE(es688_mix); - } else { - /* - * es1688 has 4 bits master vol. - * later chips have 6 bits (?) - */ - devc->supported_devices = ES1688_MIXER_DEVICES; - devc->supported_rec_devices = ES1688_RECORDING_DEVICES; - if (devc->submodel < 0x10) { - devc->iomap = &es1688_mix; - devc->iomap_sz = ARRAY_SIZE(es688_mix); - } else { - devc->iomap = &es1688later_mix; - devc->iomap_sz = ARRAY_SIZE(es1688later_mix); - } - } - } -} - -/* - * Changing playback levels at an ESS chip with record mixer means having to - * take care of recording levels of recorded inputs (devc->recmask) too! - */ -int ess_mixer_set(sb_devc *devc, int dev, int left, int right) -{ - if (ess_has_rec_mixer (devc->submodel) && (devc->recmask & (1 << dev))) { - sb_common_mixer_set (devc, dev + ES_REC_MIXER_RECDIFF, left, right); - } - return sb_common_mixer_set (devc, dev, left, right); -} - -/* - * After a sb_dsp_reset extended register 0xb4 (RECLEV) is reset too. After - * sb_dsp_reset RECLEV has to be restored. This is where ess_mixer_reload - * helps. - */ -void ess_mixer_reload (sb_devc *devc, int dev) -{ - int left, right, value; - - value = devc->levels[dev]; - left = value & 0x000000ff; - right = (value & 0x0000ff00) >> 8; - - sb_common_mixer_set(devc, dev, left, right); -} - -static int es_rec_set_recmask(sb_devc * devc, int mask) -{ - int i, i_mask, cur_mask, diff_mask; - int value, left, right; - -#ifdef FKS_LOGGING -printk (KERN_INFO "FKS: es_rec_set_recmask mask = %x\n", mask); -#endif - /* - * Changing the recmask on an ESS chip with recording mixer means: - * (1) Find the differences - * (2) For "turned-on" inputs: make the recording level the playback level - * (3) For "turned-off" inputs: make the recording level zero - */ - cur_mask = devc->recmask; - diff_mask = (cur_mask ^ mask); - - for (i = 0; i < 32; i++) { - i_mask = (1 << i); - if (diff_mask & i_mask) { /* Difference? (1) */ - if (mask & i_mask) { /* Turn it on (2) */ - value = devc->levels[i]; - left = value & 0x000000ff; - right = (value & 0x0000ff00) >> 8; - } else { /* Turn it off (3) */ - left = 0; - right = 0; - } - sb_common_mixer_set(devc, i + ES_REC_MIXER_RECDIFF, left, right); - } - } - return mask; -} - -int ess_set_recmask(sb_devc * devc, int *mask) -{ - /* This applies to ESS chips with record mixers only! */ - - if (ess_has_rec_mixer (devc->submodel)) { - *mask = es_rec_set_recmask (devc, *mask); - return 1; /* Applied */ - } else { - return 0; /* Not applied */ - } -} - -/* - * ess_mixer_reset must be called from sb_mixer_reset - */ -int ess_mixer_reset (sb_devc * devc) -{ - /* - * Separate actions for ESS chips with a record mixer: - */ - if (ess_has_rec_mixer (devc->submodel)) { - switch (devc->submodel) { - case SUBMDL_ES1887: - /* - * Separate actions for ES1887: - * Change registers 7a and 1c to make the record mixer the - * actual recording source. - */ - ess_chgmixer(devc, 0x7a, 0x18, 0x08); - ess_chgmixer(devc, 0x1c, 0x07, 0x07); - break; - } - /* - * Call set_recmask for proper initialization - */ - devc->recmask = devc->supported_rec_devices; - es_rec_set_recmask(devc, 0); - devc->recmask = 0; - - return 1; /* We took care of recmask. */ - } else { - return 0; /* We didn't take care; caller do it */ - } -} - -/**************************************************************************** - * * - * ESS midi * - * * - ****************************************************************************/ - -/* - * FKS: IRQ may be shared. Hm. And if so? Then What? - */ -int ess_midi_init(sb_devc * devc, struct address_info *hw_config) -{ - unsigned char cfg, tmp; - - cfg = ess_getmixer (devc, 0x40) & 0x03; - - if (devc->submodel < 8) { - ess_setmixer (devc, 0x40, cfg | 0x03); /* Enable OPL3 & joystick */ - return 0; /* ES688 doesn't support MPU401 mode */ - } - tmp = (hw_config->io_base & 0x0f0) >> 4; - - if (tmp > 3) { - ess_setmixer (devc, 0x40, cfg); - return 0; - } - cfg |= tmp << 3; - - tmp = 1; /* MPU enabled without interrupts */ - - /* May be shared: if so the value is -ve */ - - switch (abs(hw_config->irq)) { - case 9: - tmp = 0x4; - break; - case 5: - tmp = 0x5; - break; - case 7: - tmp = 0x6; - break; - case 10: - tmp = 0x7; - break; - default: - return 0; - } - - cfg |= tmp << 5; - ess_setmixer (devc, 0x40, cfg | 0x03); - - return 1; -} - diff --git a/sound/oss/sb_ess.h b/sound/oss/sb_ess.h deleted file mode 100644 index 1c741212bcfc..000000000000 --- a/sound/oss/sb_ess.h +++ /dev/null @@ -1,35 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Created: 9-Jan-1999 Rolf Fokkens - */ - -extern void ess_intr - (sb_devc *devc); -extern int ess_dsp_init - (sb_devc *devc, struct address_info *hw_config); - -extern struct audio_driver *ess_audio_init - (sb_devc *devc, int *audio_flags, int *format_mask); -extern int ess_midi_init - (sb_devc *devc, struct address_info *hw_config); -extern void ess_mixer_init - (sb_devc *devc); - -extern int ess_init - (sb_devc *devc, struct address_info *hw_config); -extern int ess_dsp_reset - (sb_devc *devc); - -extern void ess_setmixer - (sb_devc *devc, unsigned int port, unsigned int value); -extern unsigned int ess_getmixer - (sb_devc *devc, unsigned int port); -extern int ess_mixer_set - (sb_devc *devc, int dev, int left, int right); -extern int ess_mixer_reset - (sb_devc *devc); -extern void ess_mixer_reload - (sb_devc * devc, int dev); -extern int ess_set_recmask - (sb_devc *devc, int *mask); - diff --git a/sound/oss/sb_midi.c b/sound/oss/sb_midi.c deleted file mode 100644 index 551ee7557b4e..000000000000 --- a/sound/oss/sb_midi.c +++ /dev/null @@ -1,206 +0,0 @@ -/* - * sound/oss/sb_midi.c - * - * The low level driver for the Sound Blaster DS chips. - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ - -#include <linux/spinlock.h> -#include <linux/slab.h> - -#include "sound_config.h" - -#include "sb.h" -#undef SB_TEST_IRQ - -/* - * The DSP channel can be used either for input or output. Variable - * 'sb_irq_mode' will be set when the program calls read or write first time - * after open. Current version doesn't support mode changes without closing - * and reopening the device. Support for this feature may be implemented in a - * future version of this driver. - */ - - -static int sb_midi_open(int dev, int mode, - void (*input) (int dev, unsigned char data), - void (*output) (int dev) -) -{ - sb_devc *devc = midi_devs[dev]->devc; - unsigned long flags; - - if (devc == NULL) - return -ENXIO; - - spin_lock_irqsave(&devc->lock, flags); - if (devc->opened) - { - spin_unlock_irqrestore(&devc->lock, flags); - return -EBUSY; - } - devc->opened = 1; - spin_unlock_irqrestore(&devc->lock, flags); - - devc->irq_mode = IMODE_MIDI; - devc->midi_broken = 0; - - sb_dsp_reset(devc); - - if (!sb_dsp_command(devc, 0x35)) /* Start MIDI UART mode */ - { - devc->opened = 0; - return -EIO; - } - devc->intr_active = 1; - - if (mode & OPEN_READ) - { - devc->input_opened = 1; - devc->midi_input_intr = input; - } - return 0; -} - -static void sb_midi_close(int dev) -{ - sb_devc *devc = midi_devs[dev]->devc; - unsigned long flags; - - if (devc == NULL) - return; - - spin_lock_irqsave(&devc->lock, flags); - sb_dsp_reset(devc); - devc->intr_active = 0; - devc->input_opened = 0; - devc->opened = 0; - spin_unlock_irqrestore(&devc->lock, flags); -} - -static int sb_midi_out(int dev, unsigned char midi_byte) -{ - sb_devc *devc = midi_devs[dev]->devc; - - if (devc == NULL) - return 1; - - if (devc->midi_broken) - return 1; - - if (!sb_dsp_command(devc, midi_byte)) - { - devc->midi_broken = 1; - return 1; - } - return 1; -} - -static int sb_midi_start_read(int dev) -{ - return 0; -} - -static int sb_midi_end_read(int dev) -{ - sb_devc *devc = midi_devs[dev]->devc; - - if (devc == NULL) - return -ENXIO; - - sb_dsp_reset(devc); - devc->intr_active = 0; - return 0; -} - -static int sb_midi_ioctl(int dev, unsigned cmd, void __user *arg) -{ - return -EINVAL; -} - -void sb_midi_interrupt(sb_devc * devc) -{ - unsigned long flags; - unsigned char data; - - if (devc == NULL) - return; - - spin_lock_irqsave(&devc->lock, flags); - - data = inb(DSP_READ); - if (devc->input_opened) - devc->midi_input_intr(devc->my_mididev, data); - - spin_unlock_irqrestore(&devc->lock, flags); -} - -#define MIDI_SYNTH_NAME "Sound Blaster Midi" -#define MIDI_SYNTH_CAPS 0 -#include "midi_synth.h" - -static struct midi_operations sb_midi_operations = -{ - .owner = THIS_MODULE, - .info = {"Sound Blaster", 0, 0, SNDCARD_SB}, - .converter = &std_midi_synth, - .in_info = {0}, - .open = sb_midi_open, - .close = sb_midi_close, - .ioctl = sb_midi_ioctl, - .outputc = sb_midi_out, - .start_read = sb_midi_start_read, - .end_read = sb_midi_end_read, -}; - -void sb_dsp_midi_init(sb_devc * devc, struct module *owner) -{ - int dev; - - if (devc->model < 2) /* No MIDI support for SB 1.x */ - return; - - dev = sound_alloc_mididev(); - - if (dev == -1) - { - printk(KERN_ERR "sb_midi: too many MIDI devices detected\n"); - return; - } - std_midi_synth.midi_dev = devc->my_mididev = dev; - midi_devs[dev] = kmalloc(sizeof(struct midi_operations), GFP_KERNEL); - if (midi_devs[dev] == NULL) - { - printk(KERN_WARNING "Sound Blaster: failed to allocate MIDI memory.\n"); - sound_unload_mididev(dev); - return; - } - memcpy((char *) midi_devs[dev], (char *) &sb_midi_operations, - sizeof(struct midi_operations)); - - if (owner) - midi_devs[dev]->owner = owner; - - midi_devs[dev]->devc = devc; - - - midi_devs[dev]->converter = kmalloc(sizeof(struct synth_operations), GFP_KERNEL); - if (midi_devs[dev]->converter == NULL) - { - printk(KERN_WARNING "Sound Blaster: failed to allocate MIDI memory.\n"); - kfree(midi_devs[dev]); - sound_unload_mididev(dev); - return; - } - memcpy((char *) midi_devs[dev]->converter, (char *) &std_midi_synth, - sizeof(struct synth_operations)); - - midi_devs[dev]->converter->id = "SBMIDI"; - sequencer_init(); -} diff --git a/sound/oss/sb_mixer.c b/sound/oss/sb_mixer.c deleted file mode 100644 index acf7586aeb47..000000000000 --- a/sound/oss/sb_mixer.c +++ /dev/null @@ -1,770 +0,0 @@ -/* - * sound/oss/sb_mixer.c - * - * The low level mixer driver for the Sound Blaster compatible cards. - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - * Rolf Fokkens (Dec 20 1998) : Moved ESS stuff into sb_ess.[ch] - * Stanislav Voronyi <stas@esc.kharkov.com> : Support for AWE 3DSE device (Jun 7 1999) - */ - -#include <linux/slab.h> - -#include "sound_config.h" - -#define __SB_MIXER_C__ - -#include "sb.h" -#include "sb_mixer.h" - -#include "sb_ess.h" - -#define SBPRO_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD) - -/* Same as SB Pro, unless I find otherwise */ -#define SGNXPRO_RECORDING_DEVICES SBPRO_RECORDING_DEVICES - -#define SBPRO_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \ - SOUND_MASK_CD | SOUND_MASK_VOLUME) - -/* SG NX Pro has treble and bass settings on the mixer. The 'speaker' - * channel is the COVOX/DisneySoundSource emulation volume control - * on the mixer. It does NOT control speaker volume. Should have own - * mask eventually? - */ -#define SGNXPRO_MIXER_DEVICES (SBPRO_MIXER_DEVICES|SOUND_MASK_BASS| \ - SOUND_MASK_TREBLE|SOUND_MASK_SPEAKER ) - -#define SB16_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \ - SOUND_MASK_CD) - -#define SB16_OUTFILTER_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | \ - SOUND_MASK_CD) - -#define SB16_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \ - SOUND_MASK_CD | \ - SOUND_MASK_IGAIN | SOUND_MASK_OGAIN | \ - SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | \ - SOUND_MASK_IMIX) - -/* These are the only devices that are working at the moment. Others could - * be added once they are identified and a method is found to control them. - */ -#define ALS007_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | \ - SOUND_MASK_PCM | SOUND_MASK_MIC | \ - SOUND_MASK_CD | \ - SOUND_MASK_VOLUME) - -static mixer_tab sbpro_mix = { -MIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4), -MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4), -MIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4), -MIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4), -MIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4), -MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0) -}; - -static mixer_tab sb16_mix = { -MIX_ENT(SOUND_MIXER_VOLUME, 0x30, 7, 5, 0x31, 7, 5), -MIX_ENT(SOUND_MIXER_BASS, 0x46, 7, 4, 0x47, 7, 4), -MIX_ENT(SOUND_MIXER_TREBLE, 0x44, 7, 4, 0x45, 7, 4), -MIX_ENT(SOUND_MIXER_SYNTH, 0x34, 7, 5, 0x35, 7, 5), -MIX_ENT(SOUND_MIXER_PCM, 0x32, 7, 5, 0x33, 7, 5), -MIX_ENT(SOUND_MIXER_SPEAKER, 0x3b, 7, 2, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE, 0x38, 7, 5, 0x39, 7, 5), -MIX_ENT(SOUND_MIXER_MIC, 0x3a, 7, 5, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_CD, 0x36, 7, 5, 0x37, 7, 5), -MIX_ENT(SOUND_MIXER_IMIX, 0x3c, 0, 1, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_RECLEV, 0x3f, 7, 2, 0x40, 7, 2), /* Obsolete. Use IGAIN */ -MIX_ENT(SOUND_MIXER_IGAIN, 0x3f, 7, 2, 0x40, 7, 2), -MIX_ENT(SOUND_MIXER_OGAIN, 0x41, 7, 2, 0x42, 7, 2) -}; - -static mixer_tab als007_mix = -{ -MIX_ENT(SOUND_MIXER_VOLUME, 0x62, 7, 4, 0x62, 3, 4), -MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_SYNTH, 0x66, 7, 4, 0x66, 3, 4), -MIX_ENT(SOUND_MIXER_PCM, 0x64, 7, 4, 0x64, 3, 4), -MIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_LINE, 0x6e, 7, 4, 0x6e, 3, 4), -MIX_ENT(SOUND_MIXER_MIC, 0x6a, 2, 3, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_CD, 0x68, 7, 4, 0x68, 3, 4), -MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0), /* Obsolete. Use IGAIN */ -MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0), -MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0) -}; - - -/* SM_GAMES Master volume is lower and PCM & FM volumes - higher than with SB Pro. This improves the - sound quality */ - -static int smg_default_levels[32] = -{ - 0x2020, /* Master Volume */ - 0x4b4b, /* Bass */ - 0x4b4b, /* Treble */ - 0x6464, /* FM */ - 0x6464, /* PCM */ - 0x4b4b, /* PC Speaker */ - 0x4b4b, /* Ext Line */ - 0x0000, /* Mic */ - 0x4b4b, /* CD */ - 0x4b4b, /* Recording monitor */ - 0x4b4b, /* SB PCM */ - 0x4b4b, /* Recording level */ - 0x4b4b, /* Input gain */ - 0x4b4b, /* Output gain */ - 0x4040, /* Line1 */ - 0x4040, /* Line2 */ - 0x1515 /* Line3 */ -}; - -static int sb_default_levels[32] = -{ - 0x5a5a, /* Master Volume */ - 0x4b4b, /* Bass */ - 0x4b4b, /* Treble */ - 0x4b4b, /* FM */ - 0x4b4b, /* PCM */ - 0x4b4b, /* PC Speaker */ - 0x4b4b, /* Ext Line */ - 0x1010, /* Mic */ - 0x4b4b, /* CD */ - 0x0000, /* Recording monitor */ - 0x4b4b, /* SB PCM */ - 0x4b4b, /* Recording level */ - 0x4b4b, /* Input gain */ - 0x4b4b, /* Output gain */ - 0x4040, /* Line1 */ - 0x4040, /* Line2 */ - 0x1515 /* Line3 */ -}; - -static unsigned char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] = -{ - 0x00, /* SOUND_MIXER_VOLUME */ - 0x00, /* SOUND_MIXER_BASS */ - 0x00, /* SOUND_MIXER_TREBLE */ - 0x40, /* SOUND_MIXER_SYNTH */ - 0x00, /* SOUND_MIXER_PCM */ - 0x00, /* SOUND_MIXER_SPEAKER */ - 0x10, /* SOUND_MIXER_LINE */ - 0x01, /* SOUND_MIXER_MIC */ - 0x04, /* SOUND_MIXER_CD */ - 0x00, /* SOUND_MIXER_IMIX */ - 0x00, /* SOUND_MIXER_ALTPCM */ - 0x00, /* SOUND_MIXER_RECLEV */ - 0x00, /* SOUND_MIXER_IGAIN */ - 0x00 /* SOUND_MIXER_OGAIN */ -}; - -static unsigned char sb16_recmasks_R[SOUND_MIXER_NRDEVICES] = -{ - 0x00, /* SOUND_MIXER_VOLUME */ - 0x00, /* SOUND_MIXER_BASS */ - 0x00, /* SOUND_MIXER_TREBLE */ - 0x20, /* SOUND_MIXER_SYNTH */ - 0x00, /* SOUND_MIXER_PCM */ - 0x00, /* SOUND_MIXER_SPEAKER */ - 0x08, /* SOUND_MIXER_LINE */ - 0x01, /* SOUND_MIXER_MIC */ - 0x02, /* SOUND_MIXER_CD */ - 0x00, /* SOUND_MIXER_IMIX */ - 0x00, /* SOUND_MIXER_ALTPCM */ - 0x00, /* SOUND_MIXER_RECLEV */ - 0x00, /* SOUND_MIXER_IGAIN */ - 0x00 /* SOUND_MIXER_OGAIN */ -}; - -static char smw_mix_regs[] = /* Left mixer registers */ -{ - 0x0b, /* SOUND_MIXER_VOLUME */ - 0x0d, /* SOUND_MIXER_BASS */ - 0x0d, /* SOUND_MIXER_TREBLE */ - 0x05, /* SOUND_MIXER_SYNTH */ - 0x09, /* SOUND_MIXER_PCM */ - 0x00, /* SOUND_MIXER_SPEAKER */ - 0x03, /* SOUND_MIXER_LINE */ - 0x01, /* SOUND_MIXER_MIC */ - 0x07, /* SOUND_MIXER_CD */ - 0x00, /* SOUND_MIXER_IMIX */ - 0x00, /* SOUND_MIXER_ALTPCM */ - 0x00, /* SOUND_MIXER_RECLEV */ - 0x00, /* SOUND_MIXER_IGAIN */ - 0x00, /* SOUND_MIXER_OGAIN */ - 0x00, /* SOUND_MIXER_LINE1 */ - 0x00, /* SOUND_MIXER_LINE2 */ - 0x00 /* SOUND_MIXER_LINE3 */ -}; - -static int sbmixnum = 1; - -static void sb_mixer_reset(sb_devc * devc); - -void sb_mixer_set_stereo(sb_devc * devc, int mode) -{ - sb_chgmixer(devc, OUT_FILTER, STEREO_DAC, (mode ? STEREO_DAC : MONO_DAC)); -} - -static int detect_mixer(sb_devc * devc) -{ - /* Just trust the mixer is there */ - return 1; -} - -static void oss_change_bits(sb_devc *devc, unsigned char *regval, int dev, int chn, int newval) -{ - unsigned char mask; - int shift; - - mask = (1 << (*devc->iomap)[dev][chn].nbits) - 1; - newval = (int) ((newval * mask) + 50) / 100; /* Scale */ - - shift = (*devc->iomap)[dev][chn].bitoffs - (*devc->iomap)[dev][LEFT_CHN].nbits + 1; - - *regval &= ~(mask << shift); /* Mask out previous value */ - *regval |= (newval & mask) << shift; /* Set the new value */ -} - -static int sb_mixer_get(sb_devc * devc, int dev) -{ - if (!((1 << dev) & devc->supported_devices)) - return -EINVAL; - return devc->levels[dev]; -} - -void smw_mixer_init(sb_devc * devc) -{ - int i; - - sb_setmixer(devc, 0x00, 0x18); /* Mute unused (Telephone) line */ - sb_setmixer(devc, 0x10, 0x38); /* Config register 2 */ - - devc->supported_devices = 0; - for (i = 0; i < sizeof(smw_mix_regs); i++) - if (smw_mix_regs[i] != 0) - devc->supported_devices |= (1 << i); - - devc->supported_rec_devices = devc->supported_devices & - ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_PCM | SOUND_MASK_VOLUME); - sb_mixer_reset(devc); -} - -int sb_common_mixer_set(sb_devc * devc, int dev, int left, int right) -{ - int regoffs; - unsigned char val; - - if ((dev < 0) || (dev >= devc->iomap_sz)) - return -EINVAL; - - regoffs = (*devc->iomap)[dev][LEFT_CHN].regno; - - if (regoffs == 0) - return -EINVAL; - - val = sb_getmixer(devc, regoffs); - oss_change_bits(devc, &val, dev, LEFT_CHN, left); - - if ((*devc->iomap)[dev][RIGHT_CHN].regno != regoffs) /* - * Change register - */ - { - sb_setmixer(devc, regoffs, val); /* - * Save the old one - */ - regoffs = (*devc->iomap)[dev][RIGHT_CHN].regno; - - if (regoffs == 0) - return left | (left << 8); /* - * Just left channel present - */ - - val = sb_getmixer(devc, regoffs); /* - * Read the new one - */ - } - oss_change_bits(devc, &val, dev, RIGHT_CHN, right); - - sb_setmixer(devc, regoffs, val); - - return left | (right << 8); -} - -static int smw_mixer_set(sb_devc * devc, int dev, int left, int right) -{ - int reg, val; - - switch (dev) - { - case SOUND_MIXER_VOLUME: - sb_setmixer(devc, 0x0b, 96 - (96 * left / 100)); /* 96=mute, 0=max */ - sb_setmixer(devc, 0x0c, 96 - (96 * right / 100)); - break; - - case SOUND_MIXER_BASS: - case SOUND_MIXER_TREBLE: - devc->levels[dev] = left | (right << 8); - /* Set left bass and treble values */ - val = ((devc->levels[SOUND_MIXER_TREBLE] & 0xff) * 16 / (unsigned) 100) << 4; - val |= ((devc->levels[SOUND_MIXER_BASS] & 0xff) * 16 / (unsigned) 100) & 0x0f; - sb_setmixer(devc, 0x0d, val); - - /* Set right bass and treble values */ - val = (((devc->levels[SOUND_MIXER_TREBLE] >> 8) & 0xff) * 16 / (unsigned) 100) << 4; - val |= (((devc->levels[SOUND_MIXER_BASS] >> 8) & 0xff) * 16 / (unsigned) 100) & 0x0f; - sb_setmixer(devc, 0x0e, val); - - break; - - default: - /* bounds check */ - if (dev < 0 || dev >= ARRAY_SIZE(smw_mix_regs)) - return -EINVAL; - reg = smw_mix_regs[dev]; - if (reg == 0) - return -EINVAL; - sb_setmixer(devc, reg, (24 - (24 * left / 100)) | 0x20); /* 24=mute, 0=max */ - sb_setmixer(devc, reg + 1, (24 - (24 * right / 100)) | 0x40); - } - - devc->levels[dev] = left | (right << 8); - return left | (right << 8); -} - -static int sb_mixer_set(sb_devc * devc, int dev, int value) -{ - int left = value & 0x000000ff; - int right = (value & 0x0000ff00) >> 8; - int retval; - - if (left > 100) - left = 100; - if (right > 100) - right = 100; - - if ((dev < 0) || (dev > 31)) - return -EINVAL; - - if (!(devc->supported_devices & (1 << dev))) /* - * Not supported - */ - return -EINVAL; - - /* Differentiate depending on the chipsets */ - switch (devc->model) { - case MDL_SMW: - retval = smw_mixer_set(devc, dev, left, right); - break; - case MDL_ESS: - retval = ess_mixer_set(devc, dev, left, right); - break; - default: - retval = sb_common_mixer_set(devc, dev, left, right); - } - if (retval >= 0) devc->levels[dev] = retval; - - return retval; -} - -/* - * set_recsrc doesn't apply to ES188x - */ -static void set_recsrc(sb_devc * devc, int src) -{ - sb_setmixer(devc, RECORD_SRC, (sb_getmixer(devc, RECORD_SRC) & ~7) | (src & 0x7)); -} - -static int set_recmask(sb_devc * devc, int mask) -{ - int devmask, i; - unsigned char regimageL, regimageR; - - devmask = mask & devc->supported_rec_devices; - - switch (devc->model) - { - case MDL_SBPRO: - case MDL_ESS: - case MDL_JAZZ: - case MDL_SMW: - if (devc->model == MDL_ESS && ess_set_recmask (devc, &devmask)) { - break; - } - if (devmask != SOUND_MASK_MIC && - devmask != SOUND_MASK_LINE && - devmask != SOUND_MASK_CD) - { - /* - * More than one device selected. Drop the - * previous selection - */ - devmask &= ~devc->recmask; - } - if (devmask != SOUND_MASK_MIC && - devmask != SOUND_MASK_LINE && - devmask != SOUND_MASK_CD) - { - /* - * More than one device selected. Default to - * mic - */ - devmask = SOUND_MASK_MIC; - } - if (devmask ^ devc->recmask) /* - * Input source changed - */ - { - switch (devmask) - { - case SOUND_MASK_MIC: - set_recsrc(devc, SRC__MIC); - break; - - case SOUND_MASK_LINE: - set_recsrc(devc, SRC__LINE); - break; - - case SOUND_MASK_CD: - set_recsrc(devc, SRC__CD); - break; - - default: - set_recsrc(devc, SRC__MIC); - } - } - break; - - case MDL_SB16: - if (!devmask) - devmask = SOUND_MASK_MIC; - - if (devc->submodel == SUBMDL_ALS007) - { - switch (devmask) - { - case SOUND_MASK_LINE: - sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_LINE); - break; - case SOUND_MASK_CD: - sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_CD); - break; - case SOUND_MASK_SYNTH: - sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_SYNTH); - break; - default: /* Also takes care of SOUND_MASK_MIC case */ - sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_MIC); - break; - } - } - else - { - regimageL = regimageR = 0; - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - { - if ((1 << i) & devmask) - { - regimageL |= sb16_recmasks_L[i]; - regimageR |= sb16_recmasks_R[i]; - } - sb_setmixer (devc, SB16_IMASK_L, regimageL); - sb_setmixer (devc, SB16_IMASK_R, regimageR); - } - } - break; - } - devc->recmask = devmask; - return devc->recmask; -} - -static int set_outmask(sb_devc * devc, int mask) -{ - int devmask, i; - unsigned char regimage; - - devmask = mask & devc->supported_out_devices; - - switch (devc->model) - { - case MDL_SB16: - if (devc->submodel == SUBMDL_ALS007) - break; - else - { - regimage = 0; - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - { - if ((1 << i) & devmask) - { - regimage |= (sb16_recmasks_L[i] | sb16_recmasks_R[i]); - } - sb_setmixer (devc, SB16_OMASK, regimage); - } - } - break; - default: - break; - } - - devc->outmask = devmask; - return devc->outmask; -} - -static int sb_mixer_ioctl(int dev, unsigned int cmd, void __user *arg) -{ - sb_devc *devc = mixer_devs[dev]->devc; - int val, ret; - int __user *p = arg; - - /* - * Use ioctl(fd, SOUND_MIXER_AGC, &mode) to turn AGC off (0) or on (1). - * Use ioctl(fd, SOUND_MIXER_3DSE, &mode) to turn 3DSE off (0) or on (1) - * or mode==2 put 3DSE state to mode. - */ - if (devc->model == MDL_SB16) { - if (cmd == SOUND_MIXER_AGC) - { - if (get_user(val, p)) - return -EFAULT; - sb_setmixer(devc, 0x43, (~val) & 0x01); - return 0; - } - if (cmd == SOUND_MIXER_3DSE) - { - /* I put here 15, but I don't know the exact version. - At least my 4.13 havn't 3DSE, 4.16 has it. */ - if (devc->minor < 15) - return -EINVAL; - if (get_user(val, p)) - return -EFAULT; - if (val == 0 || val == 1) - sb_chgmixer(devc, AWE_3DSE, 0x01, val); - else if (val == 2) - { - ret = sb_getmixer(devc, AWE_3DSE)&0x01; - return put_user(ret, p); - } - else - return -EINVAL; - return 0; - } - } - if (((cmd >> 8) & 0xff) == 'M') - { - if (_SIOC_DIR(cmd) & _SIOC_WRITE) - { - if (get_user(val, p)) - return -EFAULT; - switch (cmd & 0xff) - { - case SOUND_MIXER_RECSRC: - ret = set_recmask(devc, val); - break; - - case SOUND_MIXER_OUTSRC: - ret = set_outmask(devc, val); - break; - - default: - ret = sb_mixer_set(devc, cmd & 0xff, val); - } - } - else switch (cmd & 0xff) - { - case SOUND_MIXER_RECSRC: - ret = devc->recmask; - break; - - case SOUND_MIXER_OUTSRC: - ret = devc->outmask; - break; - - case SOUND_MIXER_DEVMASK: - ret = devc->supported_devices; - break; - - case SOUND_MIXER_STEREODEVS: - ret = devc->supported_devices; - /* The ESS seems to have stereo mic controls */ - if (devc->model == MDL_ESS) - ret &= ~(SOUND_MASK_SPEAKER|SOUND_MASK_IMIX); - else if (devc->model != MDL_JAZZ && devc->model != MDL_SMW) - ret &= ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IMIX); - break; - - case SOUND_MIXER_RECMASK: - ret = devc->supported_rec_devices; - break; - - case SOUND_MIXER_OUTMASK: - ret = devc->supported_out_devices; - break; - - case SOUND_MIXER_CAPS: - ret = devc->mixer_caps; - break; - - default: - ret = sb_mixer_get(devc, cmd & 0xff); - break; - } - return put_user(ret, p); - } else - return -EINVAL; -} - -static struct mixer_operations sb_mixer_operations = -{ - .owner = THIS_MODULE, - .id = "SB", - .name = "Sound Blaster", - .ioctl = sb_mixer_ioctl -}; - -static struct mixer_operations als007_mixer_operations = -{ - .owner = THIS_MODULE, - .id = "ALS007", - .name = "Avance ALS-007", - .ioctl = sb_mixer_ioctl -}; - -static void sb_mixer_reset(sb_devc * devc) -{ - char name[32]; - int i; - - sprintf(name, "SB_%d", devc->sbmixnum); - - if (devc->sbmo.sm_games) - devc->levels = load_mixer_volumes(name, smg_default_levels, 1); - else - devc->levels = load_mixer_volumes(name, sb_default_levels, 1); - - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - sb_mixer_set(devc, i, devc->levels[i]); - - if (devc->model != MDL_ESS || !ess_mixer_reset (devc)) { - set_recmask(devc, SOUND_MASK_MIC); - } -} - -int sb_mixer_init(sb_devc * devc, struct module *owner) -{ - int mixer_type = 0; - int m; - - devc->sbmixnum = sbmixnum++; - devc->levels = NULL; - - sb_setmixer(devc, 0x00, 0); /* Reset mixer */ - - if (!(mixer_type = detect_mixer(devc))) - return 0; /* No mixer. Why? */ - - switch (devc->model) - { - case MDL_ESSPCI: - case MDL_YMPCI: - case MDL_SBPRO: - case MDL_AZTECH: - case MDL_JAZZ: - devc->mixer_caps = SOUND_CAP_EXCL_INPUT; - devc->supported_devices = SBPRO_MIXER_DEVICES; - devc->supported_rec_devices = SBPRO_RECORDING_DEVICES; - devc->iomap = &sbpro_mix; - devc->iomap_sz = ARRAY_SIZE(sbpro_mix); - break; - - case MDL_ESS: - ess_mixer_init (devc); - break; - - case MDL_SMW: - devc->mixer_caps = SOUND_CAP_EXCL_INPUT; - devc->supported_devices = 0; - devc->supported_rec_devices = 0; - devc->iomap = &sbpro_mix; - devc->iomap_sz = ARRAY_SIZE(sbpro_mix); - smw_mixer_init(devc); - break; - - case MDL_SB16: - devc->mixer_caps = 0; - devc->supported_rec_devices = SB16_RECORDING_DEVICES; - devc->supported_out_devices = SB16_OUTFILTER_DEVICES; - if (devc->submodel != SUBMDL_ALS007) - { - devc->supported_devices = SB16_MIXER_DEVICES; - devc->iomap = &sb16_mix; - devc->iomap_sz = ARRAY_SIZE(sb16_mix); - } - else - { - devc->supported_devices = ALS007_MIXER_DEVICES; - devc->iomap = &als007_mix; - devc->iomap_sz = ARRAY_SIZE(als007_mix); - } - break; - - default: - printk(KERN_WARNING "sb_mixer: Unsupported mixer type %d\n", devc->model); - return 0; - } - - m = sound_alloc_mixerdev(); - if (m == -1) - return 0; - - mixer_devs[m] = kmalloc(sizeof(struct mixer_operations), GFP_KERNEL); - if (mixer_devs[m] == NULL) - { - printk(KERN_ERR "sb_mixer: Can't allocate memory\n"); - sound_unload_mixerdev(m); - return 0; - } - - if (devc->submodel != SUBMDL_ALS007) - memcpy ((char *) mixer_devs[m], (char *) &sb_mixer_operations, sizeof (struct mixer_operations)); - else - memcpy ((char *) mixer_devs[m], (char *) &als007_mixer_operations, sizeof (struct mixer_operations)); - - mixer_devs[m]->devc = devc; - - if (owner) - mixer_devs[m]->owner = owner; - - devc->my_mixerdev = m; - sb_mixer_reset(devc); - return 1; -} - -void sb_mixer_unload(sb_devc *devc) -{ - if (devc->my_mixerdev == -1) - return; - - kfree(mixer_devs[devc->my_mixerdev]); - sound_unload_mixerdev(devc->my_mixerdev); - sbmixnum--; -} diff --git a/sound/oss/sb_mixer.h b/sound/oss/sb_mixer.h deleted file mode 100644 index 4b9425f085e3..000000000000 --- a/sound/oss/sb_mixer.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * sound/oss/sb_mixer.h - * - * Definitions for the SB Pro and SB16 mixers - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ - -/* - * Modified: - * Hunyue Yau Jan 6 1994 - * Added defines for the Sound Galaxy NX Pro mixer. - * - * Rolf Fokkens Dec 20 1998 - * Added defines for some ES188x chips. - * - * Rolf Fokkens Dec 27 1998 - * Moved static stuff to sb_mixer.c - * - */ -/* - * Mixer registers - * - * NOTE! RECORD_SRC == IN_FILTER - */ - -/* - * Mixer registers of SB Pro - */ -#define VOC_VOL 0x04 -#define MIC_VOL 0x0A -#define MIC_MIX 0x0A -#define RECORD_SRC 0x0C -#define IN_FILTER 0x0C -#define OUT_FILTER 0x0E -#define MASTER_VOL 0x22 -#define FM_VOL 0x26 -#define CD_VOL 0x28 -#define LINE_VOL 0x2E -#define IRQ_NR 0x80 -#define DMA_NR 0x81 -#define IRQ_STAT 0x82 -#define OPSW 0x3c - -/* - * Additional registers on the SG NX Pro - */ -#define COVOX_VOL 0x42 -#define TREBLE_LVL 0x44 -#define BASS_LVL 0x46 - -#define FREQ_HI (1 << 3)/* Use High-frequency ANFI filters */ -#define FREQ_LOW 0 /* Use Low-frequency ANFI filters */ -#define FILT_ON 0 /* Yes, 0 to turn it on, 1 for off */ -#define FILT_OFF (1 << 5) - -#define MONO_DAC 0x00 -#define STEREO_DAC 0x02 - -/* - * Mixer registers of SB16 - */ -#define SB16_OMASK 0x3c -#define SB16_IMASK_L 0x3d -#define SB16_IMASK_R 0x3e - -#define LEFT_CHN 0 -#define RIGHT_CHN 1 - -/* - * 3DSE register of AWE32/64 - */ -#define AWE_3DSE 0x90 - -/* - * Mixer registers of ALS007 - */ -#define ALS007_RECORD_SRC 0x6c -#define ALS007_OUTPUT_CTRL1 0x3c -#define ALS007_OUTPUT_CTRL2 0x4c - -#define MIX_ENT(name, reg_l, bit_l, len_l, reg_r, bit_r, len_r) \ - {{reg_l, bit_l, len_l}, {reg_r, bit_r, len_r}} - -/* - * Recording sources (SB Pro) - */ - -#define SRC__MIC 1 /* Select Microphone recording source */ -#define SRC__CD 3 /* Select CD recording source */ -#define SRC__LINE 7 /* Use Line-in for recording source */ - -/* - * Recording sources for ALS-007 - */ - -#define ALS007_MIC 4 -#define ALS007_LINE 6 -#define ALS007_CD 2 -#define ALS007_SYNTH 7 diff --git a/sound/oss/sequencer.c b/sound/oss/sequencer.c deleted file mode 100644 index f19da4b47c1d..000000000000 --- a/sound/oss/sequencer.c +++ /dev/null @@ -1,1661 +0,0 @@ -/* - * sound/oss/sequencer.c - * - * The sequencer personality manager. - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ -/* - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - * Alan Cox : reformatted and fixed a pair of null pointer bugs - */ -#include <linux/kmod.h> -#include <linux/spinlock.h> -#include "sound_config.h" - -#include "midi_ctrl.h" -#include "sleep.h" - -static int sequencer_ok; -static struct sound_timer_operations *tmr; -static int tmr_no = -1; /* Currently selected timer */ -static int pending_timer = -1; /* For timer change operation */ -extern unsigned long seq_time; - -static int obsolete_api_used; -static DEFINE_SPINLOCK(lock); - -/* - * Local counts for number of synth and MIDI devices. These are initialized - * by the sequencer_open. - */ -static int max_mididev; -static int max_synthdev; - -/* - * The seq_mode gives the operating mode of the sequencer: - * 1 = level1 (the default) - * 2 = level2 (extended capabilities) - */ - -#define SEQ_1 1 -#define SEQ_2 2 -static int seq_mode = SEQ_1; - -static DECLARE_WAIT_QUEUE_HEAD(seq_sleeper); -static DECLARE_WAIT_QUEUE_HEAD(midi_sleeper); - -static int midi_opened[MAX_MIDI_DEV]; - -static int midi_written[MAX_MIDI_DEV]; - -static unsigned long prev_input_time; -static int prev_event_time; - -#include "tuning.h" - -#define EV_SZ 8 -#define IEV_SZ 8 - -static unsigned char *queue; -static unsigned char *iqueue; - -static volatile int qhead, qtail, qlen; -static volatile int iqhead, iqtail, iqlen; -static volatile int seq_playing; -static volatile int sequencer_busy; -static int output_threshold; -static long pre_event_timeout; -static unsigned synth_open_mask; - -static int seq_queue(unsigned char *note, char nonblock); -static void seq_startplay(void); -static int seq_sync(void); -static void seq_reset(void); - -#if MAX_SYNTH_DEV > 15 -#error Too many synthesizer devices enabled. -#endif - -int sequencer_read(int dev, struct file *file, char __user *buf, int count) -{ - int c = count, p = 0; - int ev_len; - unsigned long flags; - - dev = dev >> 4; - - ev_len = seq_mode == SEQ_1 ? 4 : 8; - - spin_lock_irqsave(&lock,flags); - - if (!iqlen) - { - spin_unlock_irqrestore(&lock,flags); - if (file->f_flags & O_NONBLOCK) { - return -EAGAIN; - } - - oss_broken_sleep_on(&midi_sleeper, pre_event_timeout); - spin_lock_irqsave(&lock,flags); - if (!iqlen) - { - spin_unlock_irqrestore(&lock,flags); - return 0; - } - } - while (iqlen && c >= ev_len) - { - char *fixit = (char *) &iqueue[iqhead * IEV_SZ]; - spin_unlock_irqrestore(&lock,flags); - if (copy_to_user(&(buf)[p], fixit, ev_len)) - return count - c; - p += ev_len; - c -= ev_len; - - spin_lock_irqsave(&lock,flags); - iqhead = (iqhead + 1) % SEQ_MAX_QUEUE; - iqlen--; - } - spin_unlock_irqrestore(&lock,flags); - return count - c; -} - -static void sequencer_midi_output(int dev) -{ - /* - * Currently NOP - */ -} - -void seq_copy_to_input(unsigned char *event_rec, int len) -{ - unsigned long flags; - - /* - * Verify that the len is valid for the current mode. - */ - - if (len != 4 && len != 8) - return; - if ((seq_mode == SEQ_1) != (len == 4)) - return; - - if (iqlen >= (SEQ_MAX_QUEUE - 1)) - return; /* Overflow */ - - spin_lock_irqsave(&lock,flags); - memcpy(&iqueue[iqtail * IEV_SZ], event_rec, len); - iqlen++; - iqtail = (iqtail + 1) % SEQ_MAX_QUEUE; - wake_up(&midi_sleeper); - spin_unlock_irqrestore(&lock,flags); -} -EXPORT_SYMBOL(seq_copy_to_input); - -static void sequencer_midi_input(int dev, unsigned char data) -{ - unsigned int tstamp; - unsigned char event_rec[4]; - - if (data == 0xfe) /* Ignore active sensing */ - return; - - tstamp = jiffies - seq_time; - - if (tstamp != prev_input_time) - { - tstamp = (tstamp << 8) | SEQ_WAIT; - seq_copy_to_input((unsigned char *) &tstamp, 4); - prev_input_time = tstamp; - } - event_rec[0] = SEQ_MIDIPUTC; - event_rec[1] = data; - event_rec[2] = dev; - event_rec[3] = 0; - - seq_copy_to_input(event_rec, 4); -} - -void seq_input_event(unsigned char *event_rec, int len) -{ - unsigned long this_time; - - if (seq_mode == SEQ_2) - this_time = tmr->get_time(tmr_no); - else - this_time = jiffies - seq_time; - - if (this_time != prev_input_time) - { - unsigned char tmp_event[8]; - - tmp_event[0] = EV_TIMING; - tmp_event[1] = TMR_WAIT_ABS; - tmp_event[2] = 0; - tmp_event[3] = 0; - *(unsigned int *) &tmp_event[4] = this_time; - - seq_copy_to_input(tmp_event, 8); - prev_input_time = this_time; - } - seq_copy_to_input(event_rec, len); -} -EXPORT_SYMBOL(seq_input_event); - -int sequencer_write(int dev, struct file *file, const char __user *buf, int count) -{ - unsigned char event_rec[EV_SZ], ev_code; - int p = 0, c, ev_size; - int mode = translate_mode(file); - - dev = dev >> 4; - - if (mode == OPEN_READ) - return -EIO; - - c = count; - - while (c >= 4) - { - if (copy_from_user((char *) event_rec, &(buf)[p], 4)) - goto out; - ev_code = event_rec[0]; - - if (ev_code == SEQ_FULLSIZE) - { - int err, fmt; - - dev = *(unsigned short *) &event_rec[2]; - if (dev < 0 || dev >= max_synthdev || synth_devs[dev] == NULL) - return -ENXIO; - - if (!(synth_open_mask & (1 << dev))) - return -ENXIO; - - fmt = (*(short *) &event_rec[0]) & 0xffff; - err = synth_devs[dev]->load_patch(dev, fmt, buf + p, c, 0); - if (err < 0) - return err; - - return err; - } - if (ev_code >= 128) - { - if (seq_mode == SEQ_2 && ev_code == SEQ_EXTENDED) - { - printk(KERN_WARNING "Sequencer: Invalid level 2 event %x\n", ev_code); - return -EINVAL; - } - ev_size = 8; - - if (c < ev_size) - { - if (!seq_playing) - seq_startplay(); - return count - c; - } - if (copy_from_user((char *)&event_rec[4], - &(buf)[p + 4], 4)) - goto out; - - } - else - { - if (seq_mode == SEQ_2) - { - printk(KERN_WARNING "Sequencer: 4 byte event in level 2 mode\n"); - return -EINVAL; - } - ev_size = 4; - - if (event_rec[0] != SEQ_MIDIPUTC) - obsolete_api_used = 1; - } - - if (event_rec[0] == SEQ_MIDIPUTC) - { - if (!midi_opened[event_rec[2]]) - { - int err, mode; - int dev = event_rec[2]; - - if (dev >= max_mididev || midi_devs[dev]==NULL) - { - /*printk("Sequencer Error: Nonexistent MIDI device %d\n", dev);*/ - return -ENXIO; - } - mode = translate_mode(file); - - if ((err = midi_devs[dev]->open(dev, mode, - sequencer_midi_input, sequencer_midi_output)) < 0) - { - seq_reset(); - printk(KERN_WARNING "Sequencer Error: Unable to open Midi #%d\n", dev); - return err; - } - midi_opened[dev] = 1; - } - } - if (!seq_queue(event_rec, (file->f_flags & (O_NONBLOCK) ? 1 : 0))) - { - int processed = count - c; - - if (!seq_playing) - seq_startplay(); - - if (!processed && (file->f_flags & O_NONBLOCK)) - return -EAGAIN; - else - return processed; - } - p += ev_size; - c -= ev_size; - } - - if (!seq_playing) - seq_startplay(); -out: - return count; -} - -static int seq_queue(unsigned char *note, char nonblock) -{ - - /* - * Test if there is space in the queue - */ - - if (qlen >= SEQ_MAX_QUEUE) - if (!seq_playing) - seq_startplay(); /* - * Give chance to drain the queue - */ - - if (!nonblock && qlen >= SEQ_MAX_QUEUE && !waitqueue_active(&seq_sleeper)) { - /* - * Sleep until there is enough space on the queue - */ - oss_broken_sleep_on(&seq_sleeper, MAX_SCHEDULE_TIMEOUT); - } - if (qlen >= SEQ_MAX_QUEUE) - { - return 0; /* - * To be sure - */ - } - memcpy(&queue[qtail * EV_SZ], note, EV_SZ); - - qtail = (qtail + 1) % SEQ_MAX_QUEUE; - qlen++; - - return 1; -} - -static int extended_event(unsigned char *q) -{ - int dev = q[2]; - - if (dev < 0 || dev >= max_synthdev) - return -ENXIO; - - if (!(synth_open_mask & (1 << dev))) - return -ENXIO; - - switch (q[1]) - { - case SEQ_NOTEOFF: - synth_devs[dev]->kill_note(dev, q[3], q[4], q[5]); - break; - - case SEQ_NOTEON: - if (q[4] > 127 && q[4] != 255) - return 0; - - if (q[5] == 0) - { - synth_devs[dev]->kill_note(dev, q[3], q[4], q[5]); - break; - } - synth_devs[dev]->start_note(dev, q[3], q[4], q[5]); - break; - - case SEQ_PGMCHANGE: - synth_devs[dev]->set_instr(dev, q[3], q[4]); - break; - - case SEQ_AFTERTOUCH: - synth_devs[dev]->aftertouch(dev, q[3], q[4]); - break; - - case SEQ_BALANCE: - synth_devs[dev]->panning(dev, q[3], (char) q[4]); - break; - - case SEQ_CONTROLLER: - synth_devs[dev]->controller(dev, q[3], q[4], (short) (q[5] | (q[6] << 8))); - break; - - case SEQ_VOLMODE: - if (synth_devs[dev]->volume_method != NULL) - synth_devs[dev]->volume_method(dev, q[3]); - break; - - default: - return -EINVAL; - } - return 0; -} - -static int find_voice(int dev, int chn, int note) -{ - unsigned short key; - int i; - - key = (chn << 8) | (note + 1); - for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++) - if (synth_devs[dev]->alloc.map[i] == key) - return i; - return -1; -} - -static int alloc_voice(int dev, int chn, int note) -{ - unsigned short key; - int voice; - - key = (chn << 8) | (note + 1); - - voice = synth_devs[dev]->alloc_voice(dev, chn, note, - &synth_devs[dev]->alloc); - synth_devs[dev]->alloc.map[voice] = key; - synth_devs[dev]->alloc.alloc_times[voice] = - synth_devs[dev]->alloc.timestamp++; - return voice; -} - -static void seq_chn_voice_event(unsigned char *event_rec) -{ -#define dev event_rec[1] -#define cmd event_rec[2] -#define chn event_rec[3] -#define note event_rec[4] -#define parm event_rec[5] - - int voice = -1; - - if ((int) dev > max_synthdev || synth_devs[dev] == NULL) - return; - if (!(synth_open_mask & (1 << dev))) - return; - if (!synth_devs[dev]) - return; - - if (seq_mode == SEQ_2) - { - if (synth_devs[dev]->alloc_voice) - voice = find_voice(dev, chn, note); - - if (cmd == MIDI_NOTEON && parm == 0) - { - cmd = MIDI_NOTEOFF; - parm = 64; - } - } - - switch (cmd) - { - case MIDI_NOTEON: - if (note > 127 && note != 255) /* Not a seq2 feature */ - return; - - if (voice == -1 && seq_mode == SEQ_2 && synth_devs[dev]->alloc_voice) - { - /* Internal synthesizer (FM, GUS, etc) */ - voice = alloc_voice(dev, chn, note); - } - if (voice == -1) - voice = chn; - - if (seq_mode == SEQ_2 && (int) dev < num_synths) - { - /* - * The MIDI channel 10 is a percussive channel. Use the note - * number to select the proper patch (128 to 255) to play. - */ - - if (chn == 9) - { - synth_devs[dev]->set_instr(dev, voice, 128 + note); - synth_devs[dev]->chn_info[chn].pgm_num = 128 + note; - } - synth_devs[dev]->setup_voice(dev, voice, chn); - } - synth_devs[dev]->start_note(dev, voice, note, parm); - break; - - case MIDI_NOTEOFF: - if (voice == -1) - voice = chn; - synth_devs[dev]->kill_note(dev, voice, note, parm); - break; - - case MIDI_KEY_PRESSURE: - if (voice == -1) - voice = chn; - synth_devs[dev]->aftertouch(dev, voice, parm); - break; - - default:; - } -#undef dev -#undef cmd -#undef chn -#undef note -#undef parm -} - - -static void seq_chn_common_event(unsigned char *event_rec) -{ - unsigned char dev = event_rec[1]; - unsigned char cmd = event_rec[2]; - unsigned char chn = event_rec[3]; - unsigned char p1 = event_rec[4]; - - /* unsigned char p2 = event_rec[5]; */ - unsigned short w14 = *(short *) &event_rec[6]; - - if ((int) dev > max_synthdev || synth_devs[dev] == NULL) - return; - if (!(synth_open_mask & (1 << dev))) - return; - if (!synth_devs[dev]) - return; - - switch (cmd) - { - case MIDI_PGM_CHANGE: - if (seq_mode == SEQ_2) - { - if (chn > 15) - break; - - synth_devs[dev]->chn_info[chn].pgm_num = p1; - if ((int) dev >= num_synths) - synth_devs[dev]->set_instr(dev, chn, p1); - } - else - synth_devs[dev]->set_instr(dev, chn, p1); - - break; - - case MIDI_CTL_CHANGE: - if (seq_mode == SEQ_2) - { - if (chn > 15 || p1 > 127) - break; - - synth_devs[dev]->chn_info[chn].controllers[p1] = w14 & 0x7f; - - if (p1 < 32) /* Setting MSB should clear LSB to 0 */ - synth_devs[dev]->chn_info[chn].controllers[p1 + 32] = 0; - - if ((int) dev < num_synths) - { - int val = w14 & 0x7f; - int i, key; - - if (p1 < 64) /* Combine MSB and LSB */ - { - val = ((synth_devs[dev]-> - chn_info[chn].controllers[p1 & ~32] & 0x7f) << 7) - | (synth_devs[dev]-> - chn_info[chn].controllers[p1 | 32] & 0x7f); - p1 &= ~32; - } - /* Handle all playing notes on this channel */ - - key = ((int) chn << 8); - - for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++) - if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key) - synth_devs[dev]->controller(dev, i, p1, val); - } - else - synth_devs[dev]->controller(dev, chn, p1, w14); - } - else /* Mode 1 */ - synth_devs[dev]->controller(dev, chn, p1, w14); - break; - - case MIDI_PITCH_BEND: - if (seq_mode == SEQ_2) - { - if (chn > 15) - break; - - synth_devs[dev]->chn_info[chn].bender_value = w14; - - if ((int) dev < num_synths) - { - /* Handle all playing notes on this channel */ - int i, key; - - key = (chn << 8); - - for (i = 0; i < synth_devs[dev]->alloc.max_voice; i++) - if ((synth_devs[dev]->alloc.map[i] & 0xff00) == key) - synth_devs[dev]->bender(dev, i, w14); - } - else - synth_devs[dev]->bender(dev, chn, w14); - } - else /* MODE 1 */ - synth_devs[dev]->bender(dev, chn, w14); - break; - - default:; - } -} - -static int seq_timing_event(unsigned char *event_rec) -{ - unsigned char cmd = event_rec[1]; - unsigned int parm = *(int *) &event_rec[4]; - - if (seq_mode == SEQ_2) - { - int ret; - - if ((ret = tmr->event(tmr_no, event_rec)) == TIMER_ARMED) - if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) - wake_up(&seq_sleeper); - return ret; - } - switch (cmd) - { - case TMR_WAIT_REL: - parm += prev_event_time; - - /* - * NOTE! No break here. Execution of TMR_WAIT_REL continues in the - * next case (TMR_WAIT_ABS) - */ - - case TMR_WAIT_ABS: - if (parm > 0) - { - long time; - - time = parm; - prev_event_time = time; - - seq_playing = 1; - request_sound_timer(time); - - if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) - wake_up(&seq_sleeper); - return TIMER_ARMED; - } - break; - - case TMR_START: - seq_time = jiffies; - prev_input_time = 0; - prev_event_time = 0; - break; - - case TMR_STOP: - break; - - case TMR_CONTINUE: - break; - - case TMR_TEMPO: - break; - - case TMR_ECHO: - parm = (parm << 8 | SEQ_ECHO); - seq_copy_to_input((unsigned char *) &parm, 4); - break; - - default:; - } - - return TIMER_NOT_ARMED; -} - -static void seq_local_event(unsigned char *event_rec) -{ - unsigned char cmd = event_rec[1]; - unsigned int parm = *((unsigned int *) &event_rec[4]); - - switch (cmd) - { - case LOCL_STARTAUDIO: - DMAbuf_start_devices(parm); - break; - - default:; - } -} - -static void seq_sysex_message(unsigned char *event_rec) -{ - unsigned int dev = event_rec[1]; - int i, l = 0; - unsigned char *buf = &event_rec[2]; - - if (dev > max_synthdev) - return; - if (!(synth_open_mask & (1 << dev))) - return; - if (!synth_devs[dev]) - return; - - l = 0; - for (i = 0; i < 6 && buf[i] != 0xff; i++) - l = i + 1; - - if (!synth_devs[dev]->send_sysex) - return; - if (l > 0) - synth_devs[dev]->send_sysex(dev, buf, l); -} - -static int play_event(unsigned char *q) -{ - /* - * NOTE! This routine returns - * 0 = normal event played. - * 1 = Timer armed. Suspend playback until timer callback. - * 2 = MIDI output buffer full. Restore queue and suspend until timer - */ - unsigned int *delay; - - switch (q[0]) - { - case SEQ_NOTEOFF: - if (synth_open_mask & (1 << 0)) - if (synth_devs[0]) - synth_devs[0]->kill_note(0, q[1], 255, q[3]); - break; - - case SEQ_NOTEON: - if (q[4] < 128 || q[4] == 255) - if (synth_open_mask & (1 << 0)) - if (synth_devs[0]) - synth_devs[0]->start_note(0, q[1], q[2], q[3]); - break; - - case SEQ_WAIT: - delay = (unsigned int *) q; /* - * Bytes 1 to 3 are containing the * - * delay in 'ticks' - */ - *delay = (*delay >> 8) & 0xffffff; - - if (*delay > 0) - { - long time; - - seq_playing = 1; - time = *delay; - prev_event_time = time; - - request_sound_timer(time); - - if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) - wake_up(&seq_sleeper); - /* - * The timer is now active and will reinvoke this function - * after the timer expires. Return to the caller now. - */ - return 1; - } - break; - - case SEQ_PGMCHANGE: - if (synth_open_mask & (1 << 0)) - if (synth_devs[0]) - synth_devs[0]->set_instr(0, q[1], q[2]); - break; - - case SEQ_SYNCTIMER: /* - * Reset timer - */ - seq_time = jiffies; - prev_input_time = 0; - prev_event_time = 0; - break; - - case SEQ_MIDIPUTC: /* - * Put a midi character - */ - if (midi_opened[q[2]]) - { - int dev; - - dev = q[2]; - - if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL) - break; - - if (!midi_devs[dev]->outputc(dev, q[1])) - { - /* - * Output FIFO is full. Wait one timer cycle and try again. - */ - - seq_playing = 1; - request_sound_timer(-1); - return 2; - } - else - midi_written[dev] = 1; - } - break; - - case SEQ_ECHO: - seq_copy_to_input(q, 4); /* - * Echo back to the process - */ - break; - - case SEQ_PRIVATE: - if ((int) q[1] < max_synthdev) - synth_devs[q[1]]->hw_control(q[1], q); - break; - - case SEQ_EXTENDED: - extended_event(q); - break; - - case EV_CHN_VOICE: - seq_chn_voice_event(q); - break; - - case EV_CHN_COMMON: - seq_chn_common_event(q); - break; - - case EV_TIMING: - if (seq_timing_event(q) == TIMER_ARMED) - { - return 1; - } - break; - - case EV_SEQ_LOCAL: - seq_local_event(q); - break; - - case EV_SYSEX: - seq_sysex_message(q); - break; - - default:; - } - return 0; -} - -/* called also as timer in irq context */ -static void seq_startplay(void) -{ - int this_one, action; - unsigned long flags; - - while (qlen > 0) - { - - spin_lock_irqsave(&lock,flags); - qhead = ((this_one = qhead) + 1) % SEQ_MAX_QUEUE; - qlen--; - spin_unlock_irqrestore(&lock,flags); - - seq_playing = 1; - - if ((action = play_event(&queue[this_one * EV_SZ]))) - { /* Suspend playback. Next timer routine invokes this routine again */ - if (action == 2) - { - qlen++; - qhead = this_one; - } - return; - } - } - - seq_playing = 0; - - if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) - wake_up(&seq_sleeper); -} - -static void reset_controllers(int dev, unsigned char *controller, int update_dev) -{ - int i; - for (i = 0; i < 128; i++) - controller[i] = ctrl_def_values[i]; -} - -static void setup_mode2(void) -{ - int dev; - - max_synthdev = num_synths; - - for (dev = 0; dev < num_midis; dev++) - { - if (midi_devs[dev] && midi_devs[dev]->converter != NULL) - { - synth_devs[max_synthdev++] = midi_devs[dev]->converter; - } - } - - for (dev = 0; dev < max_synthdev; dev++) - { - int chn; - - synth_devs[dev]->sysex_ptr = 0; - synth_devs[dev]->emulation = 0; - - for (chn = 0; chn < 16; chn++) - { - synth_devs[dev]->chn_info[chn].pgm_num = 0; - reset_controllers(dev, - synth_devs[dev]->chn_info[chn].controllers,0); - synth_devs[dev]->chn_info[chn].bender_value = (1 << 7); /* Neutral */ - synth_devs[dev]->chn_info[chn].bender_range = 200; - } - } - max_mididev = 0; - seq_mode = SEQ_2; -} - -int sequencer_open(int dev, struct file *file) -{ - int retval, mode, i; - int level, tmp; - - if (!sequencer_ok) - sequencer_init(); - - level = ((dev & 0x0f) == SND_DEV_SEQ2) ? 2 : 1; - - dev = dev >> 4; - mode = translate_mode(file); - - if (!sequencer_ok) - { -/* printk("Sound card: sequencer not initialized\n");*/ - return -ENXIO; - } - if (dev) /* Patch manager device (obsolete) */ - return -ENXIO; - - if(synth_devs[dev] == NULL) - request_module("synth0"); - - if (mode == OPEN_READ) - { - if (!num_midis) - { - /*printk("Sequencer: No MIDI devices. Input not possible\n");*/ - sequencer_busy = 0; - return -ENXIO; - } - } - if (sequencer_busy) - { - return -EBUSY; - } - sequencer_busy = 1; - obsolete_api_used = 0; - - max_mididev = num_midis; - max_synthdev = num_synths; - pre_event_timeout = MAX_SCHEDULE_TIMEOUT; - seq_mode = SEQ_1; - - if (pending_timer != -1) - { - tmr_no = pending_timer; - pending_timer = -1; - } - if (tmr_no == -1) /* Not selected yet */ - { - int i, best; - - best = -1; - for (i = 0; i < num_sound_timers; i++) - if (sound_timer_devs[i] && sound_timer_devs[i]->priority > best) - { - tmr_no = i; - best = sound_timer_devs[i]->priority; - } - if (tmr_no == -1) /* Should not be */ - tmr_no = 0; - } - tmr = sound_timer_devs[tmr_no]; - - if (level == 2) - { - if (tmr == NULL) - { - /*printk("sequencer: No timer for level 2\n");*/ - sequencer_busy = 0; - return -ENXIO; - } - setup_mode2(); - } - if (!max_synthdev && !max_mididev) - { - sequencer_busy=0; - return -ENXIO; - } - - synth_open_mask = 0; - - for (i = 0; i < max_mididev; i++) - { - midi_opened[i] = 0; - midi_written[i] = 0; - } - - for (i = 0; i < max_synthdev; i++) - { - if (synth_devs[i]==NULL) - continue; - - if (!try_module_get(synth_devs[i]->owner)) - continue; - - if ((tmp = synth_devs[i]->open(i, mode)) < 0) - { - printk(KERN_WARNING "Sequencer: Warning! Cannot open synth device #%d (%d)\n", i, tmp); - if (synth_devs[i]->midi_dev) - printk(KERN_WARNING "(Maps to MIDI dev #%d)\n", synth_devs[i]->midi_dev); - } - else - { - synth_open_mask |= (1 << i); - if (synth_devs[i]->midi_dev) - midi_opened[synth_devs[i]->midi_dev] = 1; - } - } - - seq_time = jiffies; - - prev_input_time = 0; - prev_event_time = 0; - - if (seq_mode == SEQ_1 && (mode == OPEN_READ || mode == OPEN_READWRITE)) - { - /* - * Initialize midi input devices - */ - - for (i = 0; i < max_mididev; i++) - if (!midi_opened[i] && midi_devs[i]) - { - if (!try_module_get(midi_devs[i]->owner)) - continue; - - if ((retval = midi_devs[i]->open(i, mode, - sequencer_midi_input, sequencer_midi_output)) >= 0) - { - midi_opened[i] = 1; - } - } - } - - if (seq_mode == SEQ_2) { - if (try_module_get(tmr->owner)) - tmr->open(tmr_no, seq_mode); - } - - init_waitqueue_head(&seq_sleeper); - init_waitqueue_head(&midi_sleeper); - output_threshold = SEQ_MAX_QUEUE / 2; - - return 0; -} - -static void seq_drain_midi_queues(void) -{ - int i, n; - - /* - * Give the Midi drivers time to drain their output queues - */ - - n = 1; - - while (!signal_pending(current) && n) - { - n = 0; - - for (i = 0; i < max_mididev; i++) - if (midi_opened[i] && midi_written[i]) - if (midi_devs[i]->buffer_status != NULL) - if (midi_devs[i]->buffer_status(i)) - n++; - - /* - * Let's have a delay - */ - - if (n) - oss_broken_sleep_on(&seq_sleeper, HZ/10); - } -} - -void sequencer_release(int dev, struct file *file) -{ - int i; - int mode = translate_mode(file); - - dev = dev >> 4; - - /* - * Wait until the queue is empty (if we don't have nonblock) - */ - - if (mode != OPEN_READ && !(file->f_flags & O_NONBLOCK)) - { - while (!signal_pending(current) && qlen > 0) - { - seq_sync(); - oss_broken_sleep_on(&seq_sleeper, 3*HZ); - /* Extra delay */ - } - } - - if (mode != OPEN_READ) - seq_drain_midi_queues(); /* - * Ensure the output queues are empty - */ - seq_reset(); - if (mode != OPEN_READ) - seq_drain_midi_queues(); /* - * Flush the all notes off messages - */ - - for (i = 0; i < max_synthdev; i++) - { - if (synth_open_mask & (1 << i)) /* - * Actually opened - */ - if (synth_devs[i]) - { - synth_devs[i]->close(i); - - module_put(synth_devs[i]->owner); - - if (synth_devs[i]->midi_dev) - midi_opened[synth_devs[i]->midi_dev] = 0; - } - } - - for (i = 0; i < max_mididev; i++) - { - if (midi_opened[i]) { - midi_devs[i]->close(i); - module_put(midi_devs[i]->owner); - } - } - - if (seq_mode == SEQ_2) { - tmr->close(tmr_no); - module_put(tmr->owner); - } - - if (obsolete_api_used) - printk(KERN_WARNING "/dev/music: Obsolete (4 byte) API was used by %s\n", current->comm); - sequencer_busy = 0; -} - -static int seq_sync(void) -{ - if (qlen && !seq_playing && !signal_pending(current)) - seq_startplay(); - - if (qlen > 0) - oss_broken_sleep_on(&seq_sleeper, HZ); - return qlen; -} - -static void midi_outc(int dev, unsigned char data) -{ - /* - * NOTE! Calls sleep(). Don't call this from interrupt. - */ - - int n; - unsigned long flags; - - /* - * This routine sends one byte to the Midi channel. - * If the output FIFO is full, it waits until there - * is space in the queue - */ - - n = 3 * HZ; /* Timeout */ - - spin_lock_irqsave(&lock,flags); - while (n && !midi_devs[dev]->outputc(dev, data)) { - oss_broken_sleep_on(&seq_sleeper, HZ/25); - n--; - } - spin_unlock_irqrestore(&lock,flags); -} - -static void seq_reset(void) -{ - /* - * NOTE! Calls sleep(). Don't call this from interrupt. - */ - - int i; - int chn; - unsigned long flags; - - sound_stop_timer(); - - seq_time = jiffies; - prev_input_time = 0; - prev_event_time = 0; - - qlen = qhead = qtail = 0; - iqlen = iqhead = iqtail = 0; - - for (i = 0; i < max_synthdev; i++) - if (synth_open_mask & (1 << i)) - if (synth_devs[i]) - synth_devs[i]->reset(i); - - if (seq_mode == SEQ_2) - { - for (chn = 0; chn < 16; chn++) - for (i = 0; i < max_synthdev; i++) - if (synth_open_mask & (1 << i)) - if (synth_devs[i]) - { - synth_devs[i]->controller(i, chn, 123, 0); /* All notes off */ - synth_devs[i]->controller(i, chn, 121, 0); /* Reset all ctl */ - synth_devs[i]->bender(i, chn, 1 << 13); /* Bender off */ - } - } - else /* seq_mode == SEQ_1 */ - { - for (i = 0; i < max_mididev; i++) - if (midi_written[i]) /* - * Midi used. Some notes may still be playing - */ - { - /* - * Sending just a ACTIVE SENSING message should be enough to stop all - * playing notes. Since there are devices not recognizing the - * active sensing, we have to send some all notes off messages also. - */ - midi_outc(i, 0xfe); - - for (chn = 0; chn < 16; chn++) - { - midi_outc(i, (unsigned char) (0xb0 + (chn & 0x0f))); /* control change */ - midi_outc(i, 0x7b); /* All notes off */ - midi_outc(i, 0); /* Dummy parameter */ - } - - midi_devs[i]->close(i); - - midi_written[i] = 0; - midi_opened[i] = 0; - } - } - - seq_playing = 0; - - spin_lock_irqsave(&lock,flags); - - if (waitqueue_active(&seq_sleeper)) { - /* printk( "Sequencer Warning: Unexpected sleeping process - Waking up\n"); */ - wake_up(&seq_sleeper); - } - spin_unlock_irqrestore(&lock,flags); -} - -static void seq_panic(void) -{ - /* - * This routine is called by the application in case the user - * wants to reset the system to the default state. - */ - - seq_reset(); - - /* - * Since some of the devices don't recognize the active sensing and - * all notes off messages, we have to shut all notes manually. - * - * TO BE IMPLEMENTED LATER - */ - - /* - * Also return the controllers to their default states - */ -} - -int sequencer_ioctl(int dev, struct file *file, unsigned int cmd, void __user *arg) -{ - int midi_dev, orig_dev, val, err; - int mode = translate_mode(file); - struct synth_info inf; - struct seq_event_rec event_rec; - int __user *p = arg; - - orig_dev = dev = dev >> 4; - - switch (cmd) - { - case SNDCTL_TMR_TIMEBASE: - case SNDCTL_TMR_TEMPO: - case SNDCTL_TMR_START: - case SNDCTL_TMR_STOP: - case SNDCTL_TMR_CONTINUE: - case SNDCTL_TMR_METRONOME: - case SNDCTL_TMR_SOURCE: - if (seq_mode != SEQ_2) - return -EINVAL; - return tmr->ioctl(tmr_no, cmd, arg); - - case SNDCTL_TMR_SELECT: - if (seq_mode != SEQ_2) - return -EINVAL; - if (get_user(pending_timer, p)) - return -EFAULT; - if (pending_timer < 0 || pending_timer >= num_sound_timers || sound_timer_devs[pending_timer] == NULL) - { - pending_timer = -1; - return -EINVAL; - } - val = pending_timer; - break; - - case SNDCTL_SEQ_PANIC: - seq_panic(); - return -EINVAL; - - case SNDCTL_SEQ_SYNC: - if (mode == OPEN_READ) - return 0; - while (qlen > 0 && !signal_pending(current)) - seq_sync(); - return qlen ? -EINTR : 0; - - case SNDCTL_SEQ_RESET: - seq_reset(); - return 0; - - case SNDCTL_SEQ_TESTMIDI: - if (__get_user(midi_dev, p)) - return -EFAULT; - if (midi_dev < 0 || midi_dev >= max_mididev || !midi_devs[midi_dev]) - return -ENXIO; - - if (!midi_opened[midi_dev] && - (err = midi_devs[midi_dev]->open(midi_dev, mode, sequencer_midi_input, - sequencer_midi_output)) < 0) - return err; - midi_opened[midi_dev] = 1; - return 0; - - case SNDCTL_SEQ_GETINCOUNT: - if (mode == OPEN_WRITE) - return 0; - val = iqlen; - break; - - case SNDCTL_SEQ_GETOUTCOUNT: - if (mode == OPEN_READ) - return 0; - val = SEQ_MAX_QUEUE - qlen; - break; - - case SNDCTL_SEQ_GETTIME: - if (seq_mode == SEQ_2) - return tmr->ioctl(tmr_no, cmd, arg); - val = jiffies - seq_time; - break; - - case SNDCTL_SEQ_CTRLRATE: - /* - * If *arg == 0, just return the current rate - */ - if (seq_mode == SEQ_2) - return tmr->ioctl(tmr_no, cmd, arg); - - if (get_user(val, p)) - return -EFAULT; - if (val != 0) - return -EINVAL; - val = HZ; - break; - - case SNDCTL_SEQ_RESETSAMPLES: - case SNDCTL_SYNTH_REMOVESAMPLE: - case SNDCTL_SYNTH_CONTROL: - if (get_user(dev, p)) - return -EFAULT; - if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL) - return -ENXIO; - if (!(synth_open_mask & (1 << dev)) && !orig_dev) - return -EBUSY; - return synth_devs[dev]->ioctl(dev, cmd, arg); - - case SNDCTL_SEQ_NRSYNTHS: - val = max_synthdev; - break; - - case SNDCTL_SEQ_NRMIDIS: - val = max_mididev; - break; - - case SNDCTL_SYNTH_MEMAVL: - if (get_user(dev, p)) - return -EFAULT; - if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL) - return -ENXIO; - if (!(synth_open_mask & (1 << dev)) && !orig_dev) - return -EBUSY; - val = synth_devs[dev]->ioctl(dev, cmd, arg); - break; - - case SNDCTL_FM_4OP_ENABLE: - if (get_user(dev, p)) - return -EFAULT; - if (dev < 0 || dev >= num_synths || synth_devs[dev] == NULL) - return -ENXIO; - if (!(synth_open_mask & (1 << dev))) - return -ENXIO; - synth_devs[dev]->ioctl(dev, cmd, arg); - return 0; - - case SNDCTL_SYNTH_INFO: - if (get_user(dev, &((struct synth_info __user *)arg)->device)) - return -EFAULT; - if (dev < 0 || dev >= max_synthdev) - return -ENXIO; - if (!(synth_open_mask & (1 << dev)) && !orig_dev) - return -EBUSY; - return synth_devs[dev]->ioctl(dev, cmd, arg); - - /* Like SYNTH_INFO but returns ID in the name field */ - case SNDCTL_SYNTH_ID: - if (get_user(dev, &((struct synth_info __user *)arg)->device)) - return -EFAULT; - if (dev < 0 || dev >= max_synthdev) - return -ENXIO; - if (!(synth_open_mask & (1 << dev)) && !orig_dev) - return -EBUSY; - memcpy(&inf, synth_devs[dev]->info, sizeof(inf)); - strlcpy(inf.name, synth_devs[dev]->id, sizeof(inf.name)); - inf.device = dev; - return copy_to_user(arg, &inf, sizeof(inf))?-EFAULT:0; - - case SNDCTL_SEQ_OUTOFBAND: - if (copy_from_user(&event_rec, arg, sizeof(event_rec))) - return -EFAULT; - play_event(event_rec.arr); - return 0; - - case SNDCTL_MIDI_INFO: - if (get_user(dev, &((struct midi_info __user *)arg)->device)) - return -EFAULT; - if (dev < 0 || dev >= max_mididev || !midi_devs[dev]) - return -ENXIO; - midi_devs[dev]->info.device = dev; - return copy_to_user(arg, &midi_devs[dev]->info, sizeof(struct midi_info))?-EFAULT:0; - - case SNDCTL_SEQ_THRESHOLD: - if (get_user(val, p)) - return -EFAULT; - if (val < 1) - val = 1; - if (val >= SEQ_MAX_QUEUE) - val = SEQ_MAX_QUEUE - 1; - output_threshold = val; - return 0; - - case SNDCTL_MIDI_PRETIME: - if (get_user(val, p)) - return -EFAULT; - if (val < 0) - val = 0; - val = (HZ * val) / 10; - pre_event_timeout = val; - break; - - default: - if (mode == OPEN_READ) - return -EIO; - if (!synth_devs[0]) - return -ENXIO; - if (!(synth_open_mask & (1 << 0))) - return -ENXIO; - if (!synth_devs[0]->ioctl) - return -EINVAL; - return synth_devs[0]->ioctl(0, cmd, arg); - } - return put_user(val, p); -} - -/* No kernel lock - we're using the global irq lock here */ -unsigned int sequencer_poll(int dev, struct file *file, poll_table * wait) -{ - unsigned long flags; - unsigned int mask = 0; - - dev = dev >> 4; - - spin_lock_irqsave(&lock,flags); - /* input */ - poll_wait(file, &midi_sleeper, wait); - if (iqlen) - mask |= POLLIN | POLLRDNORM; - - /* output */ - poll_wait(file, &seq_sleeper, wait); - if ((SEQ_MAX_QUEUE - qlen) >= output_threshold) - mask |= POLLOUT | POLLWRNORM; - spin_unlock_irqrestore(&lock,flags); - return mask; -} - - -void sequencer_timer(unsigned long dummy) -{ - seq_startplay(); -} -EXPORT_SYMBOL(sequencer_timer); - -int note_to_freq(int note_num) -{ - - /* - * This routine converts a midi note to a frequency (multiplied by 1000) - */ - - int note, octave, note_freq; - static int notes[] = - { - 261632, 277189, 293671, 311132, 329632, 349232, - 369998, 391998, 415306, 440000, 466162, 493880 - }; - -#define BASE_OCTAVE 5 - - octave = note_num / 12; - note = note_num % 12; - - note_freq = notes[note]; - - if (octave < BASE_OCTAVE) - note_freq >>= (BASE_OCTAVE - octave); - else if (octave > BASE_OCTAVE) - note_freq <<= (octave - BASE_OCTAVE); - - /* - * note_freq >>= 1; - */ - - return note_freq; -} -EXPORT_SYMBOL(note_to_freq); - -unsigned long compute_finetune(unsigned long base_freq, int bend, int range, - int vibrato_cents) -{ - unsigned long amount; - int negative, semitones, cents, multiplier = 1; - - if (!bend) - return base_freq; - if (!range) - return base_freq; - - if (!base_freq) - return base_freq; - - if (range >= 8192) - range = 8192; - - bend = bend * range / 8192; /* Convert to cents */ - bend += vibrato_cents; - - if (!bend) - return base_freq; - - negative = bend < 0 ? 1 : 0; - - if (bend < 0) - bend *= -1; - if (bend > range) - bend = range; - - /* - if (bend > 2399) - bend = 2399; - */ - while (bend > 2399) - { - multiplier *= 4; - bend -= 2400; - } - - semitones = bend / 100; - cents = bend % 100; - - amount = (int) (semitone_tuning[semitones] * multiplier * cent_tuning[cents]) / 10000; - - if (negative) - return (base_freq * 10000) / amount; /* Bend down */ - else - return (base_freq * amount) / 10000; /* Bend up */ -} -EXPORT_SYMBOL(compute_finetune); - -void sequencer_init(void) -{ - if (sequencer_ok) - return; - queue = vmalloc(SEQ_MAX_QUEUE * EV_SZ); - if (queue == NULL) - { - printk(KERN_ERR "sequencer: Can't allocate memory for sequencer output queue\n"); - return; - } - iqueue = vmalloc(SEQ_MAX_QUEUE * IEV_SZ); - if (iqueue == NULL) - { - printk(KERN_ERR "sequencer: Can't allocate memory for sequencer input queue\n"); - vfree(queue); - return; - } - sequencer_ok = 1; -} -EXPORT_SYMBOL(sequencer_init); - -void sequencer_unload(void) -{ - vfree(queue); - vfree(iqueue); - queue = iqueue = NULL; -} diff --git a/sound/oss/sleep.h b/sound/oss/sleep.h deleted file mode 100644 index fd17d44d13dd..000000000000 --- a/sound/oss/sleep.h +++ /dev/null @@ -1,19 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#include <linux/wait.h> - -/* - * Do not use. This is a replacement for the old - * "interruptible_sleep_on_timeout" function that has been - * deprecated for ages. All users should instead try to use - * wait_event_interruptible_timeout. - */ - -static inline long -oss_broken_sleep_on(wait_queue_head_t *q, long timeout) -{ - DEFINE_WAIT(wait); - prepare_to_wait(q, &wait, TASK_INTERRUPTIBLE); - timeout = schedule_timeout(timeout); - finish_wait(q, &wait); - return timeout; -} diff --git a/sound/oss/sound_calls.h b/sound/oss/sound_calls.h deleted file mode 100644 index bcd3f7340ef7..000000000000 --- a/sound/oss/sound_calls.h +++ /dev/null @@ -1,88 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * DMA buffer calls - */ - -int DMAbuf_open(int dev, int mode); -int DMAbuf_release(int dev, int mode); -int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock); -int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock); -int DMAbuf_rmchars(int dev, int buff_no, int c); -int DMAbuf_start_output(int dev, int buff_no, int l); -int DMAbuf_move_wrpointer(int dev, int l); -/* int DMAbuf_ioctl(int dev, unsigned int cmd, void __user *arg, int local); */ -void DMAbuf_init(int dev, int dma1, int dma2); -void DMAbuf_deinit(int dev); -int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode); -void DMAbuf_inputintr(int dev); -void DMAbuf_outputintr(int dev, int underflow_flag); -struct dma_buffparms; -int DMAbuf_space_in_queue (int dev); -int DMAbuf_activate_recording (int dev, struct dma_buffparms *dmap); -int DMAbuf_get_buffer_pointer (int dev, struct dma_buffparms *dmap, int direction); -void DMAbuf_launch_output(int dev, struct dma_buffparms *dmap); -unsigned int DMAbuf_poll(struct file *file, int dev, poll_table *wait); -void DMAbuf_start_devices(unsigned int devmask); -void DMAbuf_reset (int dev); -int DMAbuf_sync (int dev); - -/* - * System calls for /dev/dsp and /dev/audio (audio.c) - */ - -int audio_read (int dev, struct file *file, char __user *buf, int count); -int audio_write (int dev, struct file *file, const char __user *buf, int count); -int audio_open (int dev, struct file *file); -void audio_release (int dev, struct file *file); -int audio_ioctl (int dev, struct file *file, - unsigned int cmd, void __user *arg); -void audio_init_devices (void); -void reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording); - -/* - * System calls for the /dev/sequencer - */ - -int sequencer_read (int dev, struct file *file, char __user *buf, int count); -int sequencer_write (int dev, struct file *file, const char __user *buf, int count); -int sequencer_open (int dev, struct file *file); -void sequencer_release (int dev, struct file *file); -int sequencer_ioctl (int dev, struct file *file, unsigned int cmd, void __user *arg); -unsigned int sequencer_poll(int dev, struct file *file, poll_table * wait); - -void sequencer_init (void); -void sequencer_unload (void); -void sequencer_timer(unsigned long dummy); -int note_to_freq(int note_num); -unsigned long compute_finetune(unsigned long base_freq, int bend, int range, - int vibrato_bend); -void seq_input_event(unsigned char *event, int len); -void seq_copy_to_input (unsigned char *event, int len); - -/* - * System calls for the /dev/midi - */ - -int MIDIbuf_read (int dev, struct file *file, char __user *buf, int count); -int MIDIbuf_write (int dev, struct file *file, const char __user *buf, int count); -int MIDIbuf_open (int dev, struct file *file); -void MIDIbuf_release (int dev, struct file *file); -int MIDIbuf_ioctl (int dev, struct file *file, unsigned int cmd, void __user *arg); -unsigned int MIDIbuf_poll(int dev, struct file *file, poll_table * wait); -int MIDIbuf_avail(int dev); - -void MIDIbuf_bytes_received(int dev, unsigned char *buf, int count); - - -/* From soundcard.c */ -void request_sound_timer (int count); -void sound_stop_timer(void); -void conf_printf(char *name, struct address_info *hw_config); -void conf_printf2(char *name, int base, int irq, int dma, int dma2); - -/* From sound_timer.c */ -void sound_timer_interrupt(void); -void sound_timer_syncinterval(unsigned int new_usecs); - -/* From midi_synth.c */ -void do_midi_msg (int synthno, unsigned char *msg, int mlen); diff --git a/sound/oss/sound_config.h b/sound/oss/sound_config.h deleted file mode 100644 index 5253b0a70437..000000000000 --- a/sound/oss/sound_config.h +++ /dev/null @@ -1,144 +0,0 @@ -/* sound_config.h - * - * A driver for sound cards, misc. configuration parameters. - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ - - -#ifndef _SOUND_CONFIG_H_ -#define _SOUND_CONFIG_H_ - -#include <linux/fs.h> -#include <linux/sound.h> -#include <linux/sched/signal.h> - -#include "os.h" -#include "soundvers.h" - - -#ifndef SND_DEFAULT_ENABLE -#define SND_DEFAULT_ENABLE 1 -#endif - -#ifndef MAX_REALTIME_FACTOR -#define MAX_REALTIME_FACTOR 4 -#endif - -/* - * Use always 64k buffer size. There is no reason to use shorter. - */ -#undef DSP_BUFFSIZE -#define DSP_BUFFSIZE (64*1024) - -#ifndef DSP_BUFFCOUNT -#define DSP_BUFFCOUNT 1 /* 1 is recommended. */ -#endif - -#define FM_MONO 0x388 /* This is the I/O address used by AdLib */ - -#ifndef CONFIG_PAS_BASE -#define CONFIG_PAS_BASE 0x388 -#endif - -/* SEQ_MAX_QUEUE is the maximum number of sequencer events buffered by the - driver. (There is no need to alter this) */ -#define SEQ_MAX_QUEUE 1024 - -#define SBFM_MAXINSTR (256) /* Size of the FM Instrument bank */ -/* 128 instruments for general MIDI setup and 16 unassigned */ - -#define SND_NDEVS 256 /* Number of supported devices */ - -#define DSP_DEFAULT_SPEED 8000 - -#define MAX_AUDIO_DEV 5 -#define MAX_MIXER_DEV 5 -#define MAX_SYNTH_DEV 5 -#define MAX_MIDI_DEV 6 -#define MAX_TIMER_DEV 4 - -struct address_info { - int io_base; - int irq; - int dma; - int dma2; - int always_detect; /* 1=Trust me, it's there */ - char *name; - int driver_use_1; /* Driver defined field 1 */ - int driver_use_2; /* Driver defined field 2 */ - int *osp; /* OS specific info */ - int card_subtype; /* Driver specific. Usually 0 */ - void *memptr; /* Module memory chainer */ - int slots[6]; /* To remember driver slot ids */ -}; - -#define SYNTH_MAX_VOICES 32 - -struct voice_alloc_info { - int max_voice; - int used_voices; - int ptr; /* For device specific use */ - unsigned short map[SYNTH_MAX_VOICES]; /* (ch << 8) | (note+1) */ - int timestamp; - int alloc_times[SYNTH_MAX_VOICES]; - }; - -struct channel_info { - int pgm_num; - int bender_value; - int bender_range; - unsigned char controllers[128]; - }; - -/* - * Process wakeup reasons - */ -#define WK_NONE 0x00 -#define WK_WAKEUP 0x01 -#define WK_TIMEOUT 0x02 -#define WK_SIGNAL 0x04 -#define WK_SLEEP 0x08 -#define WK_SELECT 0x10 -#define WK_ABORT 0x20 - -#define OPEN_READ PCM_ENABLE_INPUT -#define OPEN_WRITE PCM_ENABLE_OUTPUT -#define OPEN_READWRITE (OPEN_READ|OPEN_WRITE) - -static inline int translate_mode(struct file *file) -{ - if (OPEN_READ == (__force int)FMODE_READ && - OPEN_WRITE == (__force int)FMODE_WRITE) - return (__force int)(file->f_mode & (FMODE_READ | FMODE_WRITE)); - else - return ((file->f_mode & FMODE_READ) ? OPEN_READ : 0) | - ((file->f_mode & FMODE_WRITE) ? OPEN_WRITE : 0); -} - -#include "sound_calls.h" -#include "dev_table.h" - -#ifndef DDB -#define DDB(x) do {} while (0) -#endif - -#ifndef MDB -#ifdef MODULE -#define MDB(x) x -#else -#define MDB(x) -#endif -#endif - -#define TIMER_ARMED 121234 -#define TIMER_NOT_ARMED 1 - -#define MAX_MEM_BLOCKS 1024 - -#endif diff --git a/sound/oss/sound_firmware.h b/sound/oss/sound_firmware.h deleted file mode 100644 index ebcbded0e8c2..000000000000 --- a/sound/oss/sound_firmware.h +++ /dev/null @@ -1,30 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#include <linux/fs.h> - -/** - * mod_firmware_load - load sound driver firmware - * @fn: filename - * @fp: return for the buffer. - * - * Load the firmware for a sound module (up to 128K) into a buffer. - * The buffer is returned in *fp. It is allocated with vmalloc so is - * virtually linear and not DMAable. The caller should free it with - * vfree when finished. - * - * The length of the buffer is returned on a successful load, the - * value zero on a failure. - * - * Caution: This API is not recommended. Firmware should be loaded via - * request_firmware. - */ -static inline int mod_firmware_load(const char *fn, char **fp) -{ - loff_t size; - int err; - - err = kernel_read_file_from_path(fn, (void **)fp, &size, - 131072, READING_FIRMWARE); - if (err < 0) - return 0; - return size; -} diff --git a/sound/oss/sound_timer.c b/sound/oss/sound_timer.c deleted file mode 100644 index 3a444a6f10eb..000000000000 --- a/sound/oss/sound_timer.c +++ /dev/null @@ -1,327 +0,0 @@ -/* - * sound/oss/sound_timer.c - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ -/* - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - */ -#include <linux/string.h> -#include <linux/spinlock.h> - -#include "sound_config.h" - -static volatile int initialized, opened, tmr_running; -static volatile unsigned int tmr_offs, tmr_ctr; -static volatile unsigned long ticks_offs; -static volatile int curr_tempo, curr_timebase; -static volatile unsigned long curr_ticks; -static volatile unsigned long next_event_time; -static unsigned long prev_event_time; -static volatile unsigned long usecs_per_tmr; /* Length of the current interval */ - -static struct sound_lowlev_timer *tmr; -static DEFINE_SPINLOCK(lock); - -static unsigned long tmr2ticks(int tmr_value) -{ - /* - * Convert timer ticks to MIDI ticks - */ - - unsigned long tmp; - unsigned long scale; - - tmp = tmr_value * usecs_per_tmr; /* Convert to usecs */ - scale = (60 * 1000000) / (curr_tempo * curr_timebase); /* usecs per MIDI tick */ - return (tmp + (scale / 2)) / scale; -} - -void reprogram_timer(void) -{ - unsigned long usecs_per_tick; - - /* - * The user is changing the timer rate before setting a timer - * slap, bad bad not allowed. - */ - - if(!tmr) - return; - - usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase); - - /* - * Don't kill the system by setting too high timer rate - */ - if (usecs_per_tick < 2000) - usecs_per_tick = 2000; - - usecs_per_tmr = tmr->tmr_start(tmr->dev, usecs_per_tick); -} - -void sound_timer_syncinterval(unsigned int new_usecs) -{ - /* - * This routine is called by the hardware level if - * the clock frequency has changed for some reason. - */ - tmr_offs = tmr_ctr; - ticks_offs += tmr2ticks(tmr_ctr); - tmr_ctr = 0; - usecs_per_tmr = new_usecs; -} -EXPORT_SYMBOL(sound_timer_syncinterval); - -static void tmr_reset(void) -{ - unsigned long flags; - - spin_lock_irqsave(&lock,flags); - tmr_offs = 0; - ticks_offs = 0; - tmr_ctr = 0; - next_event_time = (unsigned long) -1; - prev_event_time = 0; - curr_ticks = 0; - spin_unlock_irqrestore(&lock,flags); -} - -static int timer_open(int dev, int mode) -{ - if (opened) - return -EBUSY; - tmr_reset(); - curr_tempo = 60; - curr_timebase = 100; - opened = 1; - reprogram_timer(); - return 0; -} - -static void timer_close(int dev) -{ - opened = tmr_running = 0; - tmr->tmr_disable(tmr->dev); -} - -static int timer_event(int dev, unsigned char *event) -{ - unsigned char cmd = event[1]; - unsigned long parm = *(int *) &event[4]; - - switch (cmd) - { - case TMR_WAIT_REL: - parm += prev_event_time; - case TMR_WAIT_ABS: - if (parm > 0) - { - long time; - - if (parm <= curr_ticks) /* It's the time */ - return TIMER_NOT_ARMED; - time = parm; - next_event_time = prev_event_time = time; - return TIMER_ARMED; - } - break; - - case TMR_START: - tmr_reset(); - tmr_running = 1; - reprogram_timer(); - break; - - case TMR_STOP: - tmr_running = 0; - break; - - case TMR_CONTINUE: - tmr_running = 1; - reprogram_timer(); - break; - - case TMR_TEMPO: - if (parm) - { - if (parm < 8) - parm = 8; - if (parm > 250) - parm = 250; - tmr_offs = tmr_ctr; - ticks_offs += tmr2ticks(tmr_ctr); - tmr_ctr = 0; - curr_tempo = parm; - reprogram_timer(); - } - break; - - case TMR_ECHO: - seq_copy_to_input(event, 8); - break; - - default:; - } - return TIMER_NOT_ARMED; -} - -static unsigned long timer_get_time(int dev) -{ - if (!opened) - return 0; - return curr_ticks; -} - -static int timer_ioctl(int dev, unsigned int cmd, void __user *arg) -{ - int __user *p = arg; - int val; - - switch (cmd) - { - case SNDCTL_TMR_SOURCE: - val = TMR_INTERNAL; - break; - - case SNDCTL_TMR_START: - tmr_reset(); - tmr_running = 1; - return 0; - - case SNDCTL_TMR_STOP: - tmr_running = 0; - return 0; - - case SNDCTL_TMR_CONTINUE: - tmr_running = 1; - return 0; - - case SNDCTL_TMR_TIMEBASE: - if (get_user(val, p)) - return -EFAULT; - if (val) - { - if (val < 1) - val = 1; - if (val > 1000) - val = 1000; - curr_timebase = val; - } - val = curr_timebase; - break; - - case SNDCTL_TMR_TEMPO: - if (get_user(val, p)) - return -EFAULT; - if (val) - { - if (val < 8) - val = 8; - if (val > 250) - val = 250; - tmr_offs = tmr_ctr; - ticks_offs += tmr2ticks(tmr_ctr); - tmr_ctr = 0; - curr_tempo = val; - reprogram_timer(); - } - val = curr_tempo; - break; - - case SNDCTL_SEQ_CTRLRATE: - if (get_user(val, p)) - return -EFAULT; - if (val != 0) /* Can't change */ - return -EINVAL; - val = ((curr_tempo * curr_timebase) + 30) / 60; - break; - - case SNDCTL_SEQ_GETTIME: - val = curr_ticks; - break; - - case SNDCTL_TMR_METRONOME: - default: - return -EINVAL; - } - return put_user(val, p); -} - -static void timer_arm(int dev, long time) -{ - if (time < 0) - time = curr_ticks + 1; - else if (time <= curr_ticks) /* It's the time */ - return; - - next_event_time = prev_event_time = time; - return; -} - -static struct sound_timer_operations sound_timer = -{ - .owner = THIS_MODULE, - .info = {"Sound Timer", 0}, - .priority = 1, /* Priority */ - .devlink = 0, /* Local device link */ - .open = timer_open, - .close = timer_close, - .event = timer_event, - .get_time = timer_get_time, - .ioctl = timer_ioctl, - .arm_timer = timer_arm -}; - -void sound_timer_interrupt(void) -{ - unsigned long flags; - - if (!opened) - return; - - tmr->tmr_restart(tmr->dev); - - if (!tmr_running) - return; - - spin_lock_irqsave(&lock,flags); - tmr_ctr++; - curr_ticks = ticks_offs + tmr2ticks(tmr_ctr); - - if (curr_ticks >= next_event_time) - { - next_event_time = (unsigned long) -1; - sequencer_timer(0); - } - spin_unlock_irqrestore(&lock,flags); -} -EXPORT_SYMBOL(sound_timer_interrupt); - -void sound_timer_init(struct sound_lowlev_timer *t, char *name) -{ - int n; - - if (initialized) - { - if (t->priority <= tmr->priority) - return; /* There is already a similar or better timer */ - tmr = t; - return; - } - initialized = 1; - tmr = t; - - n = sound_alloc_timerdev(); - if (n == -1) - n = 0; /* Overwrite the system timer */ - strlcpy(sound_timer.info.name, name, sizeof(sound_timer.info.name)); - sound_timer_devs[n] = &sound_timer; -} -EXPORT_SYMBOL(sound_timer_init); - diff --git a/sound/oss/soundcard.c b/sound/oss/soundcard.c deleted file mode 100644 index 4391062e5cfd..000000000000 --- a/sound/oss/soundcard.c +++ /dev/null @@ -1,733 +0,0 @@ -/* - * linux/sound/oss/soundcard.c - * - * Sound card driver for Linux - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - * integrated sound_switch.c - * Stefan Reinauer : integrated /proc/sound (equals to /dev/sndstat, - * which should disappear in the near future) - * Eric Dumas : devfs support (22-Jan-98) <dumas@linux.eu.org> with - * fixups by C. Scott Ananian <cananian@alumni.princeton.edu> - * Richard Gooch : moved common (non OSS-specific) devices to sound_core.c - * Rob Riggs : Added persistent DMA buffers support (1998/10/17) - * Christoph Hellwig : Some cleanup work (2000/03/01) - */ - - -#include "sound_config.h" -#include <linux/init.h> -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/signal.h> -#include <linux/fcntl.h> -#include <linux/ctype.h> -#include <linux/stddef.h> -#include <linux/kmod.h> -#include <linux/kernel.h> -#include <asm/dma.h> -#include <asm/io.h> -#include <linux/wait.h> -#include <linux/ioport.h> -#include <linux/major.h> -#include <linux/delay.h> -#include <linux/proc_fs.h> -#include <linux/mutex.h> -#include <linux/module.h> -#include <linux/mm.h> -#include <linux/device.h> - -/* - * This ought to be moved into include/asm/dma.h - */ -#ifndef valid_dma -#define valid_dma(n) ((n) >= 0 && (n) < MAX_DMA_CHANNELS && (n) != 4) -#endif - -/* - * Table for permanently allocated memory (used when unloading the module) - */ -void * sound_mem_blocks[MAX_MEM_BLOCKS]; -static DEFINE_MUTEX(soundcard_mutex); -int sound_nblocks = 0; - -/* Persistent DMA buffers */ -#ifdef CONFIG_SOUND_DMAP -int sound_dmap_flag = 1; -#else -int sound_dmap_flag = 0; -#endif - -static char dma_alloc_map[MAX_DMA_CHANNELS]; - -#define DMA_MAP_UNAVAIL 0 -#define DMA_MAP_FREE 1 -#define DMA_MAP_BUSY 2 - - -unsigned long seq_time = 0; /* Time for /dev/sequencer */ -extern struct class *sound_class; - -/* - * Table for configurable mixer volume handling - */ -static mixer_vol_table mixer_vols[MAX_MIXER_DEV]; -static int num_mixer_volumes; - -int *load_mixer_volumes(char *name, int *levels, int present) -{ - int i, n; - - for (i = 0; i < num_mixer_volumes; i++) { - if (strncmp(name, mixer_vols[i].name, 32) == 0) { - if (present) - mixer_vols[i].num = i; - return mixer_vols[i].levels; - } - } - if (num_mixer_volumes >= MAX_MIXER_DEV) { - printk(KERN_ERR "Sound: Too many mixers (%s)\n", name); - return levels; - } - n = num_mixer_volumes++; - - strncpy(mixer_vols[n].name, name, 32); - - if (present) - mixer_vols[n].num = n; - else - mixer_vols[n].num = -1; - - for (i = 0; i < 32; i++) - mixer_vols[n].levels[i] = levels[i]; - return mixer_vols[n].levels; -} -EXPORT_SYMBOL(load_mixer_volumes); - -static int set_mixer_levels(void __user * arg) -{ - /* mixer_vol_table is 174 bytes, so IMHO no reason to not allocate it on the stack */ - mixer_vol_table buf; - - if (__copy_from_user(&buf, arg, sizeof(buf))) - return -EFAULT; - load_mixer_volumes(buf.name, buf.levels, 0); - if (__copy_to_user(arg, &buf, sizeof(buf))) - return -EFAULT; - return 0; -} - -static int get_mixer_levels(void __user * arg) -{ - int n; - - if (__get_user(n, (int __user *)(&(((mixer_vol_table __user *)arg)->num)))) - return -EFAULT; - if (n < 0 || n >= num_mixer_volumes) - return -EINVAL; - if (__copy_to_user(arg, &mixer_vols[n], sizeof(mixer_vol_table))) - return -EFAULT; - return 0; -} - -/* 4K page size but our output routines use some slack for overruns */ -#define PROC_BLOCK_SIZE (3*1024) - -static ssize_t sound_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) -{ - int dev = iminor(file_inode(file)); - int ret = -EINVAL; - - /* - * The OSS drivers aren't remotely happy without this locking, - * and unless someone fixes them when they are about to bite the - * big one anyway, we might as well bandage here.. - */ - - mutex_lock(&soundcard_mutex); - - switch (dev & 0x0f) { - case SND_DEV_DSP: - case SND_DEV_DSP16: - case SND_DEV_AUDIO: - ret = audio_read(dev, file, buf, count); - break; - - case SND_DEV_SEQ: - case SND_DEV_SEQ2: - ret = sequencer_read(dev, file, buf, count); - break; - - case SND_DEV_MIDIN: - ret = MIDIbuf_read(dev, file, buf, count); - } - mutex_unlock(&soundcard_mutex); - return ret; -} - -static ssize_t sound_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) -{ - int dev = iminor(file_inode(file)); - int ret = -EINVAL; - - mutex_lock(&soundcard_mutex); - switch (dev & 0x0f) { - case SND_DEV_SEQ: - case SND_DEV_SEQ2: - ret = sequencer_write(dev, file, buf, count); - break; - - case SND_DEV_DSP: - case SND_DEV_DSP16: - case SND_DEV_AUDIO: - ret = audio_write(dev, file, buf, count); - break; - - case SND_DEV_MIDIN: - ret = MIDIbuf_write(dev, file, buf, count); - break; - } - mutex_unlock(&soundcard_mutex); - return ret; -} - -static int sound_open(struct inode *inode, struct file *file) -{ - int dev = iminor(inode); - int retval; - - if ((dev >= SND_NDEVS) || (dev < 0)) { - printk(KERN_ERR "Invalid minor device %d\n", dev); - return -ENXIO; - } - mutex_lock(&soundcard_mutex); - switch (dev & 0x0f) { - case SND_DEV_CTL: - dev >>= 4; - if (dev >= 0 && dev < MAX_MIXER_DEV && mixer_devs[dev] == NULL) { - request_module("mixer%d", dev); - } - retval = -ENXIO; - if (dev && (dev >= num_mixers || mixer_devs[dev] == NULL)) - break; - - if (!try_module_get(mixer_devs[dev]->owner)) - break; - - retval = 0; - break; - - case SND_DEV_SEQ: - case SND_DEV_SEQ2: - retval = sequencer_open(dev, file); - break; - - case SND_DEV_MIDIN: - retval = MIDIbuf_open(dev, file); - break; - - case SND_DEV_DSP: - case SND_DEV_DSP16: - case SND_DEV_AUDIO: - retval = audio_open(dev, file); - break; - - default: - printk(KERN_ERR "Invalid minor device %d\n", dev); - retval = -ENXIO; - } - - mutex_unlock(&soundcard_mutex); - return retval; -} - -static int sound_release(struct inode *inode, struct file *file) -{ - int dev = iminor(inode); - - mutex_lock(&soundcard_mutex); - switch (dev & 0x0f) { - case SND_DEV_CTL: - module_put(mixer_devs[dev >> 4]->owner); - break; - - case SND_DEV_SEQ: - case SND_DEV_SEQ2: - sequencer_release(dev, file); - break; - - case SND_DEV_MIDIN: - MIDIbuf_release(dev, file); - break; - - case SND_DEV_DSP: - case SND_DEV_DSP16: - case SND_DEV_AUDIO: - audio_release(dev, file); - break; - - default: - printk(KERN_ERR "Sound error: Releasing unknown device 0x%02x\n", dev); - } - mutex_unlock(&soundcard_mutex); - - return 0; -} - -static int get_mixer_info(int dev, void __user *arg) -{ - mixer_info info; - memset(&info, 0, sizeof(info)); - strlcpy(info.id, mixer_devs[dev]->id, sizeof(info.id)); - strlcpy(info.name, mixer_devs[dev]->name, sizeof(info.name)); - info.modify_counter = mixer_devs[dev]->modify_counter; - if (__copy_to_user(arg, &info, sizeof(info))) - return -EFAULT; - return 0; -} - -static int get_old_mixer_info(int dev, void __user *arg) -{ - _old_mixer_info info; - memset(&info, 0, sizeof(info)); - strlcpy(info.id, mixer_devs[dev]->id, sizeof(info.id)); - strlcpy(info.name, mixer_devs[dev]->name, sizeof(info.name)); - if (copy_to_user(arg, &info, sizeof(info))) - return -EFAULT; - return 0; -} - -static int sound_mixer_ioctl(int mixdev, unsigned int cmd, void __user *arg) -{ - if (mixdev < 0 || mixdev >= MAX_MIXER_DEV) - return -ENXIO; - /* Try to load the mixer... */ - if (mixer_devs[mixdev] == NULL) { - request_module("mixer%d", mixdev); - } - if (mixdev >= num_mixers || !mixer_devs[mixdev]) - return -ENXIO; - if (cmd == SOUND_MIXER_INFO) - return get_mixer_info(mixdev, arg); - if (cmd == SOUND_OLD_MIXER_INFO) - return get_old_mixer_info(mixdev, arg); - if (_SIOC_DIR(cmd) & _SIOC_WRITE) - mixer_devs[mixdev]->modify_counter++; - if (!mixer_devs[mixdev]->ioctl) - return -EINVAL; - return mixer_devs[mixdev]->ioctl(mixdev, cmd, arg); -} - -static long sound_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - int len = 0, dtype; - int dev = iminor(file_inode(file)); - long ret = -EINVAL; - void __user *p = (void __user *)arg; - - if (_SIOC_DIR(cmd) != _SIOC_NONE && _SIOC_DIR(cmd) != 0) { - /* - * Have to validate the address given by the process. - */ - len = _SIOC_SIZE(cmd); - if (len < 1 || len > 65536 || !p) - return -EFAULT; - if (_SIOC_DIR(cmd) & _SIOC_WRITE) - if (!access_ok(VERIFY_READ, p, len)) - return -EFAULT; - if (_SIOC_DIR(cmd) & _SIOC_READ) - if (!access_ok(VERIFY_WRITE, p, len)) - return -EFAULT; - } - if (cmd == OSS_GETVERSION) - return __put_user(SOUND_VERSION, (int __user *)p); - - mutex_lock(&soundcard_mutex); - if (_IOC_TYPE(cmd) == 'M' && num_mixers > 0 && /* Mixer ioctl */ - (dev & 0x0f) != SND_DEV_CTL) { - dtype = dev & 0x0f; - switch (dtype) { - case SND_DEV_DSP: - case SND_DEV_DSP16: - case SND_DEV_AUDIO: - ret = sound_mixer_ioctl(audio_devs[dev >> 4]->mixer_dev, - cmd, p); - break; - default: - ret = sound_mixer_ioctl(dev >> 4, cmd, p); - break; - } - mutex_unlock(&soundcard_mutex); - return ret; - } - - switch (dev & 0x0f) { - case SND_DEV_CTL: - if (cmd == SOUND_MIXER_GETLEVELS) - ret = get_mixer_levels(p); - else if (cmd == SOUND_MIXER_SETLEVELS) - ret = set_mixer_levels(p); - else - ret = sound_mixer_ioctl(dev >> 4, cmd, p); - break; - - case SND_DEV_SEQ: - case SND_DEV_SEQ2: - ret = sequencer_ioctl(dev, file, cmd, p); - break; - - case SND_DEV_DSP: - case SND_DEV_DSP16: - case SND_DEV_AUDIO: - ret = audio_ioctl(dev, file, cmd, p); - break; - - case SND_DEV_MIDIN: - ret = MIDIbuf_ioctl(dev, file, cmd, p); - break; - - } - mutex_unlock(&soundcard_mutex); - return ret; -} - -static unsigned int sound_poll(struct file *file, poll_table * wait) -{ - struct inode *inode = file_inode(file); - int dev = iminor(inode); - - switch (dev & 0x0f) { - case SND_DEV_SEQ: - case SND_DEV_SEQ2: - return sequencer_poll(dev, file, wait); - - case SND_DEV_MIDIN: - return MIDIbuf_poll(dev, file, wait); - - case SND_DEV_DSP: - case SND_DEV_DSP16: - case SND_DEV_AUDIO: - return DMAbuf_poll(file, dev >> 4, wait); - } - return 0; -} - -static int sound_mmap(struct file *file, struct vm_area_struct *vma) -{ - int dev_class; - unsigned long size; - struct dma_buffparms *dmap = NULL; - int dev = iminor(file_inode(file)); - - dev_class = dev & 0x0f; - dev >>= 4; - - if (dev_class != SND_DEV_DSP && dev_class != SND_DEV_DSP16 && dev_class != SND_DEV_AUDIO) { - printk(KERN_ERR "Sound: mmap() not supported for other than audio devices\n"); - return -EINVAL; - } - mutex_lock(&soundcard_mutex); - if (vma->vm_flags & VM_WRITE) /* Map write and read/write to the output buf */ - dmap = audio_devs[dev]->dmap_out; - else if (vma->vm_flags & VM_READ) - dmap = audio_devs[dev]->dmap_in; - else { - printk(KERN_ERR "Sound: Undefined mmap() access\n"); - mutex_unlock(&soundcard_mutex); - return -EINVAL; - } - - if (dmap == NULL) { - printk(KERN_ERR "Sound: mmap() error. dmap == NULL\n"); - mutex_unlock(&soundcard_mutex); - return -EIO; - } - if (dmap->raw_buf == NULL) { - printk(KERN_ERR "Sound: mmap() called when raw_buf == NULL\n"); - mutex_unlock(&soundcard_mutex); - return -EIO; - } - if (dmap->mapping_flags) { - printk(KERN_ERR "Sound: mmap() called twice for the same DMA buffer\n"); - mutex_unlock(&soundcard_mutex); - return -EIO; - } - if (vma->vm_pgoff != 0) { - printk(KERN_ERR "Sound: mmap() offset must be 0.\n"); - mutex_unlock(&soundcard_mutex); - return -EINVAL; - } - size = vma->vm_end - vma->vm_start; - - if (size != dmap->bytes_in_use) { - printk(KERN_WARNING "Sound: mmap() size = %ld. Should be %d\n", size, dmap->bytes_in_use); - } - if (remap_pfn_range(vma, vma->vm_start, - virt_to_phys(dmap->raw_buf) >> PAGE_SHIFT, - vma->vm_end - vma->vm_start, vma->vm_page_prot)) { - mutex_unlock(&soundcard_mutex); - return -EAGAIN; - } - - dmap->mapping_flags |= DMA_MAP_MAPPED; - - if( audio_devs[dev]->d->mmap) - audio_devs[dev]->d->mmap(dev); - - memset(dmap->raw_buf, - dmap->neutral_byte, - dmap->bytes_in_use); - mutex_unlock(&soundcard_mutex); - return 0; -} - -const struct file_operations oss_sound_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = sound_read, - .write = sound_write, - .poll = sound_poll, - .unlocked_ioctl = sound_ioctl, - .mmap = sound_mmap, - .open = sound_open, - .release = sound_release, -}; - -/* - * Create the required special subdevices - */ - -static int create_special_devices(void) -{ - int seq1,seq2; - seq1=register_sound_special(&oss_sound_fops, 1); - if(seq1==-1) - goto bad; - seq2=register_sound_special(&oss_sound_fops, 8); - if(seq2!=-1) - return 0; - unregister_sound_special(1); -bad: - return -1; -} - - -static int dmabuf; -static int dmabug; - -module_param(dmabuf, int, 0444); -module_param(dmabug, int, 0444); - -/* additional minors for compatibility */ -struct oss_minor_dev { - unsigned short minor; - unsigned int enabled; -} dev_list[] = { - { SND_DEV_DSP16 }, - { SND_DEV_AUDIO }, -}; - -static int __init oss_init(void) -{ - int err; - int i, j; - -#ifdef CONFIG_PCI - if(dmabug) - isa_dma_bridge_buggy = dmabug; -#endif - - err = create_special_devices(); - if (err) { - printk(KERN_ERR "sound: driver already loaded/included in kernel\n"); - return err; - } - - /* Protecting the innocent */ - sound_dmap_flag = (dmabuf > 0 ? 1 : 0); - - for (i = 0; i < ARRAY_SIZE(dev_list); i++) { - j = 0; - do { - unsigned short minor = dev_list[i].minor + j * 0x10; - if (!register_sound_special(&oss_sound_fops, minor)) - dev_list[i].enabled = (1 << j); - } while (++j < num_audiodevs); - } - - if (sound_nblocks >= MAX_MEM_BLOCKS - 1) - printk(KERN_ERR "Sound warning: Deallocation table was too small.\n"); - - return 0; -} - -static void __exit oss_cleanup(void) -{ - int i, j; - - for (i = 0; i < ARRAY_SIZE(dev_list); i++) { - j = 0; - do { - if (dev_list[i].enabled & (1 << j)) - unregister_sound_special(dev_list[i].minor); - } while (++j < num_audiodevs); - } - - unregister_sound_special(1); - unregister_sound_special(8); - - sound_stop_timer(); - - sequencer_unload(); - - for (i = 0; i < MAX_DMA_CHANNELS; i++) - if (dma_alloc_map[i] != DMA_MAP_UNAVAIL) { - printk(KERN_ERR "Sound: Hmm, DMA%d was left allocated - fixed\n", i); - sound_free_dma(i); - } - - for (i = 0; i < sound_nblocks; i++) - vfree(sound_mem_blocks[i]); - -} - -module_init(oss_init); -module_exit(oss_cleanup); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("OSS Sound subsystem"); -MODULE_AUTHOR("Hannu Savolainen, et al."); - - -int sound_alloc_dma(int chn, char *deviceID) -{ - int err; - - if ((err = request_dma(chn, deviceID)) != 0) - return err; - - dma_alloc_map[chn] = DMA_MAP_FREE; - - return 0; -} -EXPORT_SYMBOL(sound_alloc_dma); - -int sound_open_dma(int chn, char *deviceID) -{ - if (!valid_dma(chn)) { - printk(KERN_ERR "sound_open_dma: Invalid DMA channel %d\n", chn); - return 1; - } - - if (dma_alloc_map[chn] != DMA_MAP_FREE) { - printk("sound_open_dma: DMA channel %d busy or not allocated (%d)\n", chn, dma_alloc_map[chn]); - return 1; - } - dma_alloc_map[chn] = DMA_MAP_BUSY; - return 0; -} -EXPORT_SYMBOL(sound_open_dma); - -void sound_free_dma(int chn) -{ - if (dma_alloc_map[chn] == DMA_MAP_UNAVAIL) { - /* printk( "sound_free_dma: Bad access to DMA channel %d\n", chn); */ - return; - } - free_dma(chn); - dma_alloc_map[chn] = DMA_MAP_UNAVAIL; -} -EXPORT_SYMBOL(sound_free_dma); - -void sound_close_dma(int chn) -{ - if (dma_alloc_map[chn] != DMA_MAP_BUSY) { - printk(KERN_ERR "sound_close_dma: Bad access to DMA channel %d\n", chn); - return; - } - dma_alloc_map[chn] = DMA_MAP_FREE; -} -EXPORT_SYMBOL(sound_close_dma); - -static void do_sequencer_timer(unsigned long dummy) -{ - sequencer_timer(0); -} - - -static DEFINE_TIMER(seq_timer, do_sequencer_timer); - -void request_sound_timer(int count) -{ - extern unsigned long seq_time; - - if (count < 0) { - seq_timer.expires = (-count) + jiffies; - add_timer(&seq_timer); - return; - } - count += seq_time; - - count -= jiffies; - - if (count < 1) - count = 1; - - seq_timer.expires = (count) + jiffies; - add_timer(&seq_timer); -} - -void sound_stop_timer(void) -{ - del_timer(&seq_timer); -} - -void conf_printf(char *name, struct address_info *hw_config) -{ -#ifndef CONFIG_SOUND_TRACEINIT - return; -#else - printk("<%s> at 0x%03x", name, hw_config->io_base); - - if (hw_config->irq) - printk(" irq %d", (hw_config->irq > 0) ? hw_config->irq : -hw_config->irq); - - if (hw_config->dma != -1 || hw_config->dma2 != -1) - { - printk(" dma %d", hw_config->dma); - if (hw_config->dma2 != -1) - printk(",%d", hw_config->dma2); - } - printk("\n"); -#endif -} -EXPORT_SYMBOL(conf_printf); - -void conf_printf2(char *name, int base, int irq, int dma, int dma2) -{ -#ifndef CONFIG_SOUND_TRACEINIT - return; -#else - printk("<%s> at 0x%03x", name, base); - - if (irq) - printk(" irq %d", (irq > 0) ? irq : -irq); - - if (dma != -1 || dma2 != -1) - { - printk(" dma %d", dma); - if (dma2 != -1) - printk(",%d", dma2); - } - printk("\n"); -#endif -} -EXPORT_SYMBOL(conf_printf2); - diff --git a/sound/oss/soundvers.h b/sound/oss/soundvers.h deleted file mode 100644 index e9084d2f46a9..000000000000 --- a/sound/oss/soundvers.h +++ /dev/null @@ -1,2 +0,0 @@ -#define SOUND_VERSION_STRING "3.8s2++-971130" -#define SOUND_INTERNAL_VERSION 0x030804 diff --git a/sound/oss/swarm_cs4297a.c b/sound/oss/swarm_cs4297a.c deleted file mode 100644 index 97899352b15f..000000000000 --- a/sound/oss/swarm_cs4297a.c +++ /dev/null @@ -1,2781 +0,0 @@ -/******************************************************************************* -* -* "swarm_cs4297a.c" -- Cirrus Logic-Crystal CS4297a linux audio driver. -* -* Copyright (C) 2001 Broadcom Corporation. -* Copyright (C) 2000,2001 Cirrus Logic Corp. -* -- adapted from drivers by Thomas Sailer, -* -- but don't bug him; Problems should go to: -* -- tom woller (twoller@crystal.cirrus.com) or -* (audio@crystal.cirrus.com). -* -- adapted from cs4281 PCI driver for cs4297a on -* BCM1250 Synchronous Serial interface -* (Kip Walker, Broadcom Corp.) -* Copyright (C) 2004 Maciej W. Rozycki -* Copyright (C) 2005 Ralf Baechle (ralf@linux-mips.org) -* -* 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., 675 Mass Ave, Cambridge, MA 02139, USA. -* -* Module command line parameters: -* none -* -* Supported devices: -* /dev/dsp standard /dev/dsp device, (mostly) OSS compatible -* /dev/mixer standard /dev/mixer device, (mostly) OSS compatible -* /dev/midi simple MIDI UART interface, no ioctl -* -* Modification History -* 08/20/00 trw - silence and no stopping DAC until release -* 08/23/00 trw - added CS_DBG statements, fix interrupt hang issue on DAC stop. -* 09/18/00 trw - added 16bit only record with conversion -* 09/24/00 trw - added Enhanced Full duplex (separate simultaneous -* capture/playback rates) -* 10/03/00 trw - fixed mmap (fixed GRECORD and the XMMS mmap test plugin -* libOSSm.so) -* 10/11/00 trw - modified for 2.4.0-test9 kernel enhancements (NR_MAP removal) -* 11/03/00 trw - fixed interrupt loss/stutter, added debug. -* 11/10/00 bkz - added __devinit to cs4297a_hw_init() -* 11/10/00 trw - fixed SMP and capture spinlock hang. -* 12/04/00 trw - cleaned up CSDEBUG flags and added "defaultorder" moduleparm. -* 12/05/00 trw - fixed polling (myth2), and added underrun swptr fix. -* 12/08/00 trw - added PM support. -* 12/14/00 trw - added wrapper code, builds under 2.4.0, 2.2.17-20, 2.2.17-8 -* (RH/Dell base), 2.2.18, 2.2.12. cleaned up code mods by ident. -* 12/19/00 trw - added PM support for 2.2 base (apm_callback). other PM cleanup. -* 12/21/00 trw - added fractional "defaultorder" inputs. if >100 then use -* defaultorder-100 as power of 2 for the buffer size. example: -* 106 = 2^(106-100) = 2^6 = 64 bytes for the buffer size. -* -*******************************************************************************/ - -#include <linux/list.h> -#include <linux/module.h> -#include <linux/string.h> -#include <linux/ioport.h> -#include <linux/sched/signal.h> -#include <linux/delay.h> -#include <linux/sound.h> -#include <linux/slab.h> -#include <linux/soundcard.h> -#include <linux/pci.h> -#include <linux/bitops.h> -#include <linux/interrupt.h> -#include <linux/init.h> -#include <linux/poll.h> -#include <linux/mutex.h> -#include <linux/kernel.h> - -#include <asm/byteorder.h> -#include <asm/dma.h> -#include <asm/io.h> -#include <linux/uaccess.h> - -#include <asm/sibyte/sb1250_regs.h> -#include <asm/sibyte/sb1250_int.h> -#include <asm/sibyte/sb1250_dma.h> -#include <asm/sibyte/sb1250_scd.h> -#include <asm/sibyte/sb1250_syncser.h> -#include <asm/sibyte/sb1250_mac.h> -#include <asm/sibyte/sb1250.h> - -#include "sleep.h" - -struct cs4297a_state; - -static DEFINE_MUTEX(swarm_cs4297a_mutex); -static void stop_dac(struct cs4297a_state *s); -static void stop_adc(struct cs4297a_state *s); -static void start_dac(struct cs4297a_state *s); -static void start_adc(struct cs4297a_state *s); -#undef OSS_DOCUMENTED_MIXER_SEMANTICS - -// --------------------------------------------------------------------- - -#define CS4297a_MAGIC 0xf00beef1 - -// buffer order determines the size of the dma buffer for the driver. -// under Linux, a smaller buffer allows more responsiveness from many of the -// applications (e.g. games). A larger buffer allows some of the apps (esound) -// to not underrun the dma buffer as easily. As default, use 32k (order=3) -// rather than 64k as some of the games work more responsively. -// log base 2( buff sz = 32k). - -// -// Turn on/off debugging compilation by commenting out "#define CSDEBUG" -// -#define CSDEBUG 0 -#if CSDEBUG -#define CSDEBUG_INTERFACE 1 -#else -#undef CSDEBUG_INTERFACE -#endif -// -// cs_debugmask areas -// -#define CS_INIT 0x00000001 // initialization and probe functions -#define CS_ERROR 0x00000002 // tmp debugging bit placeholder -#define CS_INTERRUPT 0x00000004 // interrupt handler (separate from all other) -#define CS_FUNCTION 0x00000008 // enter/leave functions -#define CS_WAVE_WRITE 0x00000010 // write information for wave -#define CS_WAVE_READ 0x00000020 // read information for wave -#define CS_AC97 0x00000040 // AC97 register access -#define CS_DESCR 0x00000080 // descriptor management -#define CS_OPEN 0x00000400 // all open functions in the driver -#define CS_RELEASE 0x00000800 // all release functions in the driver -#define CS_PARMS 0x00001000 // functional and operational parameters -#define CS_IOCTL 0x00002000 // ioctl (non-mixer) -#define CS_TMP 0x10000000 // tmp debug mask bit - -// -// CSDEBUG is usual mode is set to 1, then use the -// cs_debuglevel and cs_debugmask to turn on or off debugging. -// Debug level of 1 has been defined to be kernel errors and info -// that should be printed on any released driver. -// -#if CSDEBUG -#define CS_DBGOUT(mask,level,x) if((cs_debuglevel >= (level)) && ((mask) & cs_debugmask) ) {x;} -#else -#define CS_DBGOUT(mask,level,x) -#endif - -#if CSDEBUG -static unsigned long cs_debuglevel = 4; // levels range from 1-9 -static unsigned long cs_debugmask = CS_INIT /*| CS_IOCTL*/; -module_param(cs_debuglevel, int, 0); -module_param(cs_debugmask, int, 0); -#endif -#define CS_TRUE 1 -#define CS_FALSE 0 - -#define CS_TYPE_ADC 0 -#define CS_TYPE_DAC 1 - -#define SER_BASE (A_SER_BASE_1 + KSEG1) -#define SS_CSR(t) (SER_BASE+t) -#define SS_TXTBL(t) (SER_BASE+R_SER_TX_TABLE_BASE+(t*8)) -#define SS_RXTBL(t) (SER_BASE+R_SER_RX_TABLE_BASE+(t*8)) - -#define FRAME_BYTES 32 -#define FRAME_SAMPLE_BYTES 4 - -/* Should this be variable? */ -#define SAMPLE_BUF_SIZE (16*1024) -#define SAMPLE_FRAME_COUNT (SAMPLE_BUF_SIZE / FRAME_SAMPLE_BYTES) -/* The driver can explode/shrink the frames to/from a smaller sample - buffer */ -#define DMA_BLOAT_FACTOR 1 -#define DMA_DESCR (SAMPLE_FRAME_COUNT / DMA_BLOAT_FACTOR) -#define DMA_BUF_SIZE (DMA_DESCR * FRAME_BYTES) - -/* Use the maxmium count (255 == 5.1 ms between interrupts) */ -#define DMA_INT_CNT ((1 << S_DMA_INT_PKTCNT) - 1) - -/* Figure this out: how many TX DMAs ahead to schedule a reg access */ -#define REG_LATENCY 150 - -#define FRAME_TX_US 20 - -#define SERDMA_NEXTBUF(d,f) (((d)->f+1) % (d)->ringsz) - -static const char invalid_magic[] = - KERN_CRIT "cs4297a: invalid magic value\n"; - -#define VALIDATE_STATE(s) \ -({ \ - if (!(s) || (s)->magic != CS4297a_MAGIC) { \ - printk(invalid_magic); \ - return -ENXIO; \ - } \ -}) - -/* AC97 registers */ -#define AC97_MASTER_VOL_STEREO 0x0002 /* Line Out */ -#define AC97_PCBEEP_VOL 0x000a /* none */ -#define AC97_PHONE_VOL 0x000c /* TAD Input (mono) */ -#define AC97_MIC_VOL 0x000e /* MIC Input (mono) */ -#define AC97_LINEIN_VOL 0x0010 /* Line Input (stereo) */ -#define AC97_CD_VOL 0x0012 /* CD Input (stereo) */ -#define AC97_AUX_VOL 0x0016 /* Aux Input (stereo) */ -#define AC97_PCMOUT_VOL 0x0018 /* Wave Output (stereo) */ -#define AC97_RECORD_SELECT 0x001a /* */ -#define AC97_RECORD_GAIN 0x001c -#define AC97_GENERAL_PURPOSE 0x0020 -#define AC97_3D_CONTROL 0x0022 -#define AC97_POWER_CONTROL 0x0026 -#define AC97_VENDOR_ID1 0x007c - -struct list_head cs4297a_devs = { &cs4297a_devs, &cs4297a_devs }; - -typedef struct serdma_descr_s { - u64 descr_a; - u64 descr_b; -} serdma_descr_t; - -typedef unsigned long paddr_t; - -typedef struct serdma_s { - unsigned ringsz; - serdma_descr_t *descrtab; - serdma_descr_t *descrtab_end; - paddr_t descrtab_phys; - - serdma_descr_t *descr_add; - serdma_descr_t *descr_rem; - - u64 *dma_buf; // buffer for DMA contents (frames) - paddr_t dma_buf_phys; - u16 *sample_buf; // tmp buffer for sample conversions - u16 *sb_swptr; - u16 *sb_hwptr; - u16 *sb_end; - - dma_addr_t dmaaddr; -// unsigned buforder; // Log base 2 of 'dma_buf' size in bytes.. - unsigned numfrag; // # of 'fragments' in the buffer. - unsigned fragshift; // Log base 2 of fragment size. - unsigned hwptr, swptr; - unsigned total_bytes; // # bytes process since open. - unsigned blocks; // last returned blocks value GETOPTR - unsigned wakeup; // interrupt occurred on block - int count; - unsigned underrun; // underrun flag - unsigned error; // over/underrun - wait_queue_head_t wait; - wait_queue_head_t reg_wait; - // redundant, but makes calculations easier - unsigned fragsize; // 2**fragshift.. - unsigned sbufsz; // 2**buforder. - unsigned fragsamples; - // OSS stuff - unsigned mapped:1; // Buffer mapped in cs4297a_mmap()? - unsigned ready:1; // prog_dmabuf_dac()/adc() successful? - unsigned endcleared:1; - unsigned type:1; // adc or dac buffer (CS_TYPE_XXX) - unsigned ossfragshift; - int ossmaxfrags; - unsigned subdivision; -} serdma_t; - -struct cs4297a_state { - // magic - unsigned int magic; - - struct list_head list; - - // soundcore stuff - int dev_audio; - int dev_mixer; - - // hardware resources - unsigned int irq; - - struct { - unsigned int rx_ovrrn; /* FIFO */ - unsigned int rx_overflow; /* staging buffer */ - unsigned int tx_underrun; - unsigned int rx_bad; - unsigned int rx_good; - } stats; - - // mixer registers - struct { - unsigned short vol[10]; - unsigned int recsrc; - unsigned int modcnt; - unsigned short micpreamp; - } mix; - - // wave stuff - struct properties { - unsigned fmt; - unsigned fmt_original; // original requested format - unsigned channels; - unsigned rate; - } prop_dac, prop_adc; - unsigned conversion:1; // conversion from 16 to 8 bit in progress - unsigned ena; - spinlock_t lock; - struct mutex open_mutex; - struct mutex open_sem_adc; - struct mutex open_sem_dac; - fmode_t open_mode; - wait_queue_head_t open_wait; - wait_queue_head_t open_wait_adc; - wait_queue_head_t open_wait_dac; - - dma_addr_t dmaaddr_sample_buf; - unsigned buforder_sample_buf; // Log base 2 of 'dma_buf' size in bytes.. - - serdma_t dma_dac, dma_adc; - - volatile u16 read_value; - volatile u16 read_reg; - volatile u64 reg_request; -}; - -#if 1 -#define prog_codec(a,b) -#define dealloc_dmabuf(a,b); -#endif - -static int prog_dmabuf_adc(struct cs4297a_state *s) -{ - s->dma_adc.ready = 1; - return 0; -} - - -static int prog_dmabuf_dac(struct cs4297a_state *s) -{ - s->dma_dac.ready = 1; - return 0; -} - -static void clear_advance(void *buf, unsigned bsize, unsigned bptr, - unsigned len, unsigned char c) -{ - if (bptr + len > bsize) { - unsigned x = bsize - bptr; - memset(((char *) buf) + bptr, c, x); - bptr = 0; - len -= x; - } - CS_DBGOUT(CS_WAVE_WRITE, 4, printk(KERN_INFO - "cs4297a: clear_advance(): memset %d at 0x%.8x for %d size \n", - (unsigned)c, (unsigned)((char *) buf) + bptr, len)); - memset(((char *) buf) + bptr, c, len); -} - -#if CSDEBUG - -// DEBUG ROUTINES - -#define SOUND_MIXER_CS_GETDBGLEVEL _SIOWR('M',120, int) -#define SOUND_MIXER_CS_SETDBGLEVEL _SIOWR('M',121, int) -#define SOUND_MIXER_CS_GETDBGMASK _SIOWR('M',122, int) -#define SOUND_MIXER_CS_SETDBGMASK _SIOWR('M',123, int) - -static void cs_printioctl(unsigned int x) -{ - unsigned int i; - unsigned char vidx; - // Index of mixtable1[] member is Device ID - // and must be <= SOUND_MIXER_NRDEVICES. - // Value of array member is index into s->mix.vol[] - static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = { - [SOUND_MIXER_PCM] = 1, // voice - [SOUND_MIXER_LINE1] = 2, // AUX - [SOUND_MIXER_CD] = 3, // CD - [SOUND_MIXER_LINE] = 4, // Line - [SOUND_MIXER_SYNTH] = 5, // FM - [SOUND_MIXER_MIC] = 6, // Mic - [SOUND_MIXER_SPEAKER] = 7, // Speaker - [SOUND_MIXER_RECLEV] = 8, // Recording level - [SOUND_MIXER_VOLUME] = 9 // Master Volume - }; - - switch (x) { - case SOUND_MIXER_CS_GETDBGMASK: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_MIXER_CS_GETDBGMASK:\n")); - break; - case SOUND_MIXER_CS_GETDBGLEVEL: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_MIXER_CS_GETDBGLEVEL:\n")); - break; - case SOUND_MIXER_CS_SETDBGMASK: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_MIXER_CS_SETDBGMASK:\n")); - break; - case SOUND_MIXER_CS_SETDBGLEVEL: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_MIXER_CS_SETDBGLEVEL:\n")); - break; - case OSS_GETVERSION: - CS_DBGOUT(CS_IOCTL, 4, printk("OSS_GETVERSION:\n")); - break; - case SNDCTL_DSP_SYNC: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SYNC:\n")); - break; - case SNDCTL_DSP_SETDUPLEX: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETDUPLEX:\n")); - break; - case SNDCTL_DSP_GETCAPS: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETCAPS:\n")); - break; - case SNDCTL_DSP_RESET: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_RESET:\n")); - break; - case SNDCTL_DSP_SPEED: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SPEED:\n")); - break; - case SNDCTL_DSP_STEREO: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_STEREO:\n")); - break; - case SNDCTL_DSP_CHANNELS: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_CHANNELS:\n")); - break; - case SNDCTL_DSP_GETFMTS: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETFMTS:\n")); - break; - case SNDCTL_DSP_SETFMT: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETFMT:\n")); - break; - case SNDCTL_DSP_POST: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_POST:\n")); - break; - case SNDCTL_DSP_GETTRIGGER: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETTRIGGER:\n")); - break; - case SNDCTL_DSP_SETTRIGGER: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETTRIGGER:\n")); - break; - case SNDCTL_DSP_GETOSPACE: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOSPACE:\n")); - break; - case SNDCTL_DSP_GETISPACE: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETISPACE:\n")); - break; - case SNDCTL_DSP_NONBLOCK: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_NONBLOCK:\n")); - break; - case SNDCTL_DSP_GETODELAY: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETODELAY:\n")); - break; - case SNDCTL_DSP_GETIPTR: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETIPTR:\n")); - break; - case SNDCTL_DSP_GETOPTR: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOPTR:\n")); - break; - case SNDCTL_DSP_GETBLKSIZE: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETBLKSIZE:\n")); - break; - case SNDCTL_DSP_SETFRAGMENT: - CS_DBGOUT(CS_IOCTL, 4, - printk("SNDCTL_DSP_SETFRAGMENT:\n")); - break; - case SNDCTL_DSP_SUBDIVIDE: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SUBDIVIDE:\n")); - break; - case SOUND_PCM_READ_RATE: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_RATE:\n")); - break; - case SOUND_PCM_READ_CHANNELS: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_PCM_READ_CHANNELS:\n")); - break; - case SOUND_PCM_READ_BITS: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_BITS:\n")); - break; - case SOUND_PCM_WRITE_FILTER: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_PCM_WRITE_FILTER:\n")); - break; - case SNDCTL_DSP_SETSYNCRO: - CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETSYNCRO:\n")); - break; - case SOUND_PCM_READ_FILTER: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_FILTER:\n")); - break; - case SOUND_MIXER_PRIVATE1: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE1:\n")); - break; - case SOUND_MIXER_PRIVATE2: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE2:\n")); - break; - case SOUND_MIXER_PRIVATE3: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE3:\n")); - break; - case SOUND_MIXER_PRIVATE4: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE4:\n")); - break; - case SOUND_MIXER_PRIVATE5: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE5:\n")); - break; - case SOUND_MIXER_INFO: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_INFO:\n")); - break; - case SOUND_OLD_MIXER_INFO: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_OLD_MIXER_INFO:\n")); - break; - - default: - switch (_IOC_NR(x)) { - case SOUND_MIXER_VOLUME: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_MIXER_VOLUME:\n")); - break; - case SOUND_MIXER_SPEAKER: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_MIXER_SPEAKER:\n")); - break; - case SOUND_MIXER_RECLEV: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_MIXER_RECLEV:\n")); - break; - case SOUND_MIXER_MIC: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_MIXER_MIC:\n")); - break; - case SOUND_MIXER_SYNTH: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_MIXER_SYNTH:\n")); - break; - case SOUND_MIXER_RECSRC: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_MIXER_RECSRC:\n")); - break; - case SOUND_MIXER_DEVMASK: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_MIXER_DEVMASK:\n")); - break; - case SOUND_MIXER_RECMASK: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_MIXER_RECMASK:\n")); - break; - case SOUND_MIXER_STEREODEVS: - CS_DBGOUT(CS_IOCTL, 4, - printk("SOUND_MIXER_STEREODEVS:\n")); - break; - case SOUND_MIXER_CAPS: - CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CAPS:\n")); - break; - default: - i = _IOC_NR(x); - if (i >= SOUND_MIXER_NRDEVICES - || !(vidx = mixtable1[i])) { - CS_DBGOUT(CS_IOCTL, 4, printk - ("UNKNOWN IOCTL: 0x%.8x NR=%d\n", - x, i)); - } else { - CS_DBGOUT(CS_IOCTL, 4, printk - ("SOUND_MIXER_IOCTL AC9x: 0x%.8x NR=%d\n", - x, i)); - } - break; - } - } -} -#endif - - -static int ser_init(struct cs4297a_state *s) -{ - int i; - - CS_DBGOUT(CS_INIT, 2, - printk(KERN_INFO "cs4297a: Setting up serial parameters\n")); - - __raw_writeq(M_SYNCSER_CMD_RX_RESET | M_SYNCSER_CMD_TX_RESET, SS_CSR(R_SER_CMD)); - - __raw_writeq(M_SYNCSER_MSB_FIRST, SS_CSR(R_SER_MODE)); - __raw_writeq(32, SS_CSR(R_SER_MINFRM_SZ)); - __raw_writeq(32, SS_CSR(R_SER_MAXFRM_SZ)); - - __raw_writeq(1, SS_CSR(R_SER_TX_RD_THRSH)); - __raw_writeq(4, SS_CSR(R_SER_TX_WR_THRSH)); - __raw_writeq(8, SS_CSR(R_SER_RX_RD_THRSH)); - - /* This looks good from experimentation */ - __raw_writeq((M_SYNCSER_TXSYNC_INT | V_SYNCSER_TXSYNC_DLY(0) | M_SYNCSER_TXCLK_EXT | - M_SYNCSER_RXSYNC_INT | V_SYNCSER_RXSYNC_DLY(1) | M_SYNCSER_RXCLK_EXT | M_SYNCSER_RXSYNC_EDGE), - SS_CSR(R_SER_LINE_MODE)); - - /* This looks good from experimentation */ - __raw_writeq(V_SYNCSER_SEQ_COUNT(14) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_STROBE, - SS_TXTBL(0)); - __raw_writeq(V_SYNCSER_SEQ_COUNT(15) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_BYTE, - SS_TXTBL(1)); - __raw_writeq(V_SYNCSER_SEQ_COUNT(13) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_BYTE, - SS_TXTBL(2)); - __raw_writeq(V_SYNCSER_SEQ_COUNT( 0) | M_SYNCSER_SEQ_ENABLE | - M_SYNCSER_SEQ_STROBE | M_SYNCSER_SEQ_LAST, SS_TXTBL(3)); - - __raw_writeq(V_SYNCSER_SEQ_COUNT(14) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_STROBE, - SS_RXTBL(0)); - __raw_writeq(V_SYNCSER_SEQ_COUNT(15) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_BYTE, - SS_RXTBL(1)); - __raw_writeq(V_SYNCSER_SEQ_COUNT(13) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_BYTE, - SS_RXTBL(2)); - __raw_writeq(V_SYNCSER_SEQ_COUNT( 0) | M_SYNCSER_SEQ_ENABLE | M_SYNCSER_SEQ_STROBE | - M_SYNCSER_SEQ_LAST, SS_RXTBL(3)); - - for (i=4; i<16; i++) { - /* Just in case... */ - __raw_writeq(M_SYNCSER_SEQ_LAST, SS_TXTBL(i)); - __raw_writeq(M_SYNCSER_SEQ_LAST, SS_RXTBL(i)); - } - - return 0; -} - -static int init_serdma(serdma_t *dma) -{ - CS_DBGOUT(CS_INIT, 2, - printk(KERN_ERR "cs4297a: desc - %d sbufsize - %d dbufsize - %d\n", - DMA_DESCR, SAMPLE_BUF_SIZE, DMA_BUF_SIZE)); - - /* Descriptors */ - dma->ringsz = DMA_DESCR; - dma->descrtab = kzalloc(dma->ringsz * sizeof(serdma_descr_t), GFP_KERNEL); - if (!dma->descrtab) { - printk(KERN_ERR "cs4297a: kzalloc descrtab failed\n"); - return -1; - } - dma->descrtab_end = dma->descrtab + dma->ringsz; - /* XXX bloddy mess, use proper DMA API here ... */ - dma->descrtab_phys = CPHYSADDR((long)dma->descrtab); - dma->descr_add = dma->descr_rem = dma->descrtab; - - /* Frame buffer area */ - dma->dma_buf = kzalloc(DMA_BUF_SIZE, GFP_KERNEL); - if (!dma->dma_buf) { - printk(KERN_ERR "cs4297a: kzalloc dma_buf failed\n"); - kfree(dma->descrtab); - return -1; - } - dma->dma_buf_phys = CPHYSADDR((long)dma->dma_buf); - - /* Samples buffer area */ - dma->sbufsz = SAMPLE_BUF_SIZE; - dma->sample_buf = kmalloc(dma->sbufsz, GFP_KERNEL); - if (!dma->sample_buf) { - printk(KERN_ERR "cs4297a: kmalloc sample_buf failed\n"); - kfree(dma->descrtab); - kfree(dma->dma_buf); - return -1; - } - dma->sb_swptr = dma->sb_hwptr = dma->sample_buf; - dma->sb_end = (u16 *)((void *)dma->sample_buf + dma->sbufsz); - dma->fragsize = dma->sbufsz >> 1; - - CS_DBGOUT(CS_INIT, 4, - printk(KERN_ERR "cs4297a: descrtab - %08x dma_buf - %x sample_buf - %x\n", - (int)dma->descrtab, (int)dma->dma_buf, - (int)dma->sample_buf)); - - return 0; -} - -static int dma_init(struct cs4297a_state *s) -{ - int i; - - CS_DBGOUT(CS_INIT, 2, - printk(KERN_INFO "cs4297a: Setting up DMA\n")); - - if (init_serdma(&s->dma_adc) || - init_serdma(&s->dma_dac)) - return -1; - - if (__raw_readq(SS_CSR(R_SER_DMA_DSCR_COUNT_RX))|| - __raw_readq(SS_CSR(R_SER_DMA_DSCR_COUNT_TX))) { - panic("DMA state corrupted?!"); - } - - /* Initialize now - the descr/buffer pairings will never - change... */ - for (i=0; i<DMA_DESCR; i++) { - s->dma_dac.descrtab[i].descr_a = M_DMA_SERRX_SOP | V_DMA_DSCRA_A_SIZE(1) | - (s->dma_dac.dma_buf_phys + i*FRAME_BYTES); - s->dma_dac.descrtab[i].descr_b = V_DMA_DSCRB_PKT_SIZE(FRAME_BYTES); - s->dma_adc.descrtab[i].descr_a = V_DMA_DSCRA_A_SIZE(1) | - (s->dma_adc.dma_buf_phys + i*FRAME_BYTES); - s->dma_adc.descrtab[i].descr_b = 0; - } - - __raw_writeq((M_DMA_EOP_INT_EN | V_DMA_INT_PKTCNT(DMA_INT_CNT) | - V_DMA_RINGSZ(DMA_DESCR) | M_DMA_TDX_EN), - SS_CSR(R_SER_DMA_CONFIG0_RX)); - __raw_writeq(M_DMA_L2CA, SS_CSR(R_SER_DMA_CONFIG1_RX)); - __raw_writeq(s->dma_adc.descrtab_phys, SS_CSR(R_SER_DMA_DSCR_BASE_RX)); - - __raw_writeq(V_DMA_RINGSZ(DMA_DESCR), SS_CSR(R_SER_DMA_CONFIG0_TX)); - __raw_writeq(M_DMA_L2CA | M_DMA_NO_DSCR_UPDT, SS_CSR(R_SER_DMA_CONFIG1_TX)); - __raw_writeq(s->dma_dac.descrtab_phys, SS_CSR(R_SER_DMA_DSCR_BASE_TX)); - - /* Prep the receive DMA descriptor ring */ - __raw_writeq(DMA_DESCR, SS_CSR(R_SER_DMA_DSCR_COUNT_RX)); - - __raw_writeq(M_SYNCSER_DMA_RX_EN | M_SYNCSER_DMA_TX_EN, SS_CSR(R_SER_DMA_ENABLE)); - - __raw_writeq((M_SYNCSER_RX_SYNC_ERR | M_SYNCSER_RX_OVERRUN | M_SYNCSER_RX_EOP_COUNT), - SS_CSR(R_SER_INT_MASK)); - - /* Enable the rx/tx; let the codec warm up to the sync and - start sending good frames before the receive FIFO is - enabled */ - __raw_writeq(M_SYNCSER_CMD_TX_EN, SS_CSR(R_SER_CMD)); - udelay(1000); - __raw_writeq(M_SYNCSER_CMD_RX_EN | M_SYNCSER_CMD_TX_EN, SS_CSR(R_SER_CMD)); - - /* XXXKW is this magic? (the "1" part) */ - while ((__raw_readq(SS_CSR(R_SER_STATUS)) & 0xf1) != 1) - ; - - CS_DBGOUT(CS_INIT, 4, - printk(KERN_INFO "cs4297a: status: %08x\n", - (unsigned int)(__raw_readq(SS_CSR(R_SER_STATUS)) & 0xffffffff))); - - return 0; -} - -static int serdma_reg_access(struct cs4297a_state *s, u64 data) -{ - serdma_t *d = &s->dma_dac; - u64 *data_p; - unsigned swptr; - unsigned long flags; - serdma_descr_t *descr; - - if (s->reg_request) { - printk(KERN_ERR "cs4297a: attempt to issue multiple reg_access\n"); - return -1; - } - - if (s->ena & FMODE_WRITE) { - /* Since a writer has the DSP open, we have to mux the - request in */ - s->reg_request = data; - oss_broken_sleep_on(&s->dma_dac.reg_wait, MAX_SCHEDULE_TIMEOUT); - /* XXXKW how can I deal with the starvation case where - the opener isn't writing? */ - } else { - /* Be safe when changing ring pointers */ - spin_lock_irqsave(&s->lock, flags); - if (d->hwptr != d->swptr) { - printk(KERN_ERR "cs4297a: reg access found bookkeeping error (hw/sw = %d/%d\n", - d->hwptr, d->swptr); - spin_unlock_irqrestore(&s->lock, flags); - return -1; - } - swptr = d->swptr; - d->hwptr = d->swptr = (d->swptr + 1) % d->ringsz; - spin_unlock_irqrestore(&s->lock, flags); - - descr = &d->descrtab[swptr]; - data_p = &d->dma_buf[swptr * 4]; - *data_p = cpu_to_be64(data); - __raw_writeq(1, SS_CSR(R_SER_DMA_DSCR_COUNT_TX)); - CS_DBGOUT(CS_DESCR, 4, - printk(KERN_INFO "cs4297a: add_tx %p (%x -> %x)\n", - data_p, swptr, d->hwptr)); - } - - CS_DBGOUT(CS_FUNCTION, 6, - printk(KERN_INFO "cs4297a: serdma_reg_access()-\n")); - - return 0; -} - -//**************************************************************************** -// "cs4297a_read_ac97" -- Reads an AC97 register -//**************************************************************************** -static int cs4297a_read_ac97(struct cs4297a_state *s, u32 offset, - u32 * value) -{ - CS_DBGOUT(CS_AC97, 1, - printk(KERN_INFO "cs4297a: read reg %2x\n", offset)); - if (serdma_reg_access(s, (0xCLL << 60) | (1LL << 47) | ((u64)(offset & 0x7F) << 40))) - return -1; - - oss_broken_sleep_on(&s->dma_adc.reg_wait, MAX_SCHEDULE_TIMEOUT); - *value = s->read_value; - CS_DBGOUT(CS_AC97, 2, - printk(KERN_INFO "cs4297a: rdr reg %x -> %x\n", s->read_reg, s->read_value)); - - return 0; -} - - -//**************************************************************************** -// "cs4297a_write_ac97()"-- writes an AC97 register -//**************************************************************************** -static int cs4297a_write_ac97(struct cs4297a_state *s, u32 offset, - u32 value) -{ - CS_DBGOUT(CS_AC97, 1, - printk(KERN_INFO "cs4297a: write reg %2x -> %04x\n", offset, value)); - return (serdma_reg_access(s, (0xELL << 60) | ((u64)(offset & 0x7F) << 40) | ((value & 0xffff) << 12))); -} - -static void stop_dac(struct cs4297a_state *s) -{ - unsigned long flags; - - CS_DBGOUT(CS_WAVE_WRITE, 3, printk(KERN_INFO "cs4297a: stop_dac():\n")); - spin_lock_irqsave(&s->lock, flags); - s->ena &= ~FMODE_WRITE; -#if 0 - /* XXXKW what do I really want here? My theory for now is - that I just flip the "ena" bit, and the interrupt handler - will stop processing the xmit channel */ - __raw_writeq((s->ena & FMODE_READ) ? M_SYNCSER_DMA_RX_EN : 0, - SS_CSR(R_SER_DMA_ENABLE)); -#endif - - spin_unlock_irqrestore(&s->lock, flags); -} - - -static void start_dac(struct cs4297a_state *s) -{ - unsigned long flags; - - CS_DBGOUT(CS_FUNCTION, 3, printk(KERN_INFO "cs4297a: start_dac()+\n")); - spin_lock_irqsave(&s->lock, flags); - if (!(s->ena & FMODE_WRITE) && (s->dma_dac.mapped || - (s->dma_dac.count > 0 - && s->dma_dac.ready))) { - s->ena |= FMODE_WRITE; - /* XXXKW what do I really want here? My theory for - now is that I just flip the "ena" bit, and the - interrupt handler will start processing the xmit - channel */ - - CS_DBGOUT(CS_WAVE_WRITE | CS_PARMS, 8, printk(KERN_INFO - "cs4297a: start_dac(): start dma\n")); - - } - spin_unlock_irqrestore(&s->lock, flags); - CS_DBGOUT(CS_FUNCTION, 3, - printk(KERN_INFO "cs4297a: start_dac()-\n")); -} - - -static void stop_adc(struct cs4297a_state *s) -{ - unsigned long flags; - - CS_DBGOUT(CS_FUNCTION, 3, - printk(KERN_INFO "cs4297a: stop_adc()+\n")); - - spin_lock_irqsave(&s->lock, flags); - s->ena &= ~FMODE_READ; - - if (s->conversion == 1) { - s->conversion = 0; - s->prop_adc.fmt = s->prop_adc.fmt_original; - } - /* Nothing to do really, I need to keep the DMA going - XXXKW when do I get here, and is there more I should do? */ - spin_unlock_irqrestore(&s->lock, flags); - CS_DBGOUT(CS_FUNCTION, 3, - printk(KERN_INFO "cs4297a: stop_adc()-\n")); -} - - -static void start_adc(struct cs4297a_state *s) -{ - unsigned long flags; - - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4297a: start_adc()+\n")); - - if (!(s->ena & FMODE_READ) && - (s->dma_adc.mapped || s->dma_adc.count <= - (signed) (s->dma_adc.sbufsz - 2 * s->dma_adc.fragsize)) - && s->dma_adc.ready) { - if (s->prop_adc.fmt & AFMT_S8 || s->prop_adc.fmt & AFMT_U8) { - // - // now only use 16 bit capture, due to truncation issue - // in the chip, noticeable distortion occurs. - // allocate buffer and then convert from 16 bit to - // 8 bit for the user buffer. - // - s->prop_adc.fmt_original = s->prop_adc.fmt; - if (s->prop_adc.fmt & AFMT_S8) { - s->prop_adc.fmt &= ~AFMT_S8; - s->prop_adc.fmt |= AFMT_S16_LE; - } - if (s->prop_adc.fmt & AFMT_U8) { - s->prop_adc.fmt &= ~AFMT_U8; - s->prop_adc.fmt |= AFMT_U16_LE; - } - // - // prog_dmabuf_adc performs a stop_adc() but that is - // ok since we really haven't started the DMA yet. - // - prog_codec(s, CS_TYPE_ADC); - - prog_dmabuf_adc(s); - s->conversion = 1; - } - spin_lock_irqsave(&s->lock, flags); - s->ena |= FMODE_READ; - /* Nothing to do really, I am probably already - DMAing... XXXKW when do I get here, and is there - more I should do? */ - spin_unlock_irqrestore(&s->lock, flags); - - CS_DBGOUT(CS_PARMS, 6, printk(KERN_INFO - "cs4297a: start_adc(): start adc\n")); - } - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4297a: start_adc()-\n")); - -} - - -// call with spinlock held! -static void cs4297a_update_ptr(struct cs4297a_state *s, int intflag) -{ - int good_diff, diff, diff2; - u64 *data_p, data; - u32 *s_ptr; - unsigned hwptr; - u32 status; - serdma_t *d; - serdma_descr_t *descr; - - // update ADC pointer - status = intflag ? __raw_readq(SS_CSR(R_SER_STATUS)) : 0; - - if ((s->ena & FMODE_READ) || (status & (M_SYNCSER_RX_EOP_COUNT))) { - d = &s->dma_adc; - hwptr = (unsigned) (((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_RX)) & M_DMA_CURDSCR_ADDR) - - d->descrtab_phys) / sizeof(serdma_descr_t)); - - if (s->ena & FMODE_READ) { - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4297a: upd_rcv sw->hw->hw %x/%x/%x (int-%d)n", - d->swptr, d->hwptr, hwptr, intflag)); - /* Number of DMA buffers available for software: */ - diff2 = diff = (d->ringsz + hwptr - d->hwptr) % d->ringsz; - d->hwptr = hwptr; - good_diff = 0; - s_ptr = (u32 *)&(d->dma_buf[d->swptr*4]); - descr = &d->descrtab[d->swptr]; - while (diff2--) { - u64 data = be64_to_cpu(*(u64 *)s_ptr); - u64 descr_a; - u16 left, right; - descr_a = descr->descr_a; - descr->descr_a &= ~M_DMA_SERRX_SOP; - if ((descr_a & M_DMA_DSCRA_A_ADDR) != CPHYSADDR((long)s_ptr)) { - printk(KERN_ERR "cs4297a: RX Bad address (read)\n"); - } - if (((data & 0x9800000000000000) != 0x9800000000000000) || - (!(descr_a & M_DMA_SERRX_SOP)) || - (G_DMA_DSCRB_PKT_SIZE(descr->descr_b) != FRAME_BYTES)) { - s->stats.rx_bad++; - printk(KERN_DEBUG "cs4297a: RX Bad attributes (read)\n"); - continue; - } - s->stats.rx_good++; - if ((data >> 61) == 7) { - s->read_value = (data >> 12) & 0xffff; - s->read_reg = (data >> 40) & 0x7f; - wake_up(&d->reg_wait); - } - if (d->count && (d->sb_hwptr == d->sb_swptr)) { - s->stats.rx_overflow++; - printk(KERN_DEBUG "cs4297a: RX overflow\n"); - continue; - } - good_diff++; - left = ((be32_to_cpu(s_ptr[1]) & 0xff) << 8) | - ((be32_to_cpu(s_ptr[2]) >> 24) & 0xff); - right = (be32_to_cpu(s_ptr[2]) >> 4) & 0xffff; - *d->sb_hwptr++ = cpu_to_be16(left); - *d->sb_hwptr++ = cpu_to_be16(right); - if (d->sb_hwptr == d->sb_end) - d->sb_hwptr = d->sample_buf; - descr++; - if (descr == d->descrtab_end) { - descr = d->descrtab; - s_ptr = (u32 *)s->dma_adc.dma_buf; - } else { - s_ptr += 8; - } - } - d->total_bytes += good_diff * FRAME_SAMPLE_BYTES; - d->count += good_diff * FRAME_SAMPLE_BYTES; - if (d->count > d->sbufsz) { - printk(KERN_ERR "cs4297a: bogus receive overflow!!\n"); - } - d->swptr = (d->swptr + diff) % d->ringsz; - __raw_writeq(diff, SS_CSR(R_SER_DMA_DSCR_COUNT_RX)); - if (d->mapped) { - if (d->count >= (signed) d->fragsize) - wake_up(&d->wait); - } else { - if (d->count > 0) { - CS_DBGOUT(CS_WAVE_READ, 4, - printk(KERN_INFO - "cs4297a: update count -> %d\n", d->count)); - wake_up(&d->wait); - } - } - } else { - /* Receive is going even if no one is - listening (for register accesses and to - avoid FIFO overrun) */ - diff2 = diff = (hwptr + d->ringsz - d->hwptr) % d->ringsz; - if (!diff) { - printk(KERN_ERR "cs4297a: RX full or empty?\n"); - } - - descr = &d->descrtab[d->swptr]; - data_p = &d->dma_buf[d->swptr*4]; - - /* Force this to happen at least once; I got - here because of an interrupt, so there must - be a buffer to process. */ - do { - data = be64_to_cpu(*data_p); - if ((descr->descr_a & M_DMA_DSCRA_A_ADDR) != CPHYSADDR((long)data_p)) { - printk(KERN_ERR "cs4297a: RX Bad address %d (%llx %lx)\n", d->swptr, - (long long)(descr->descr_a & M_DMA_DSCRA_A_ADDR), - (long)CPHYSADDR((long)data_p)); - } - if (!(data & (1LL << 63)) || - !(descr->descr_a & M_DMA_SERRX_SOP) || - (G_DMA_DSCRB_PKT_SIZE(descr->descr_b) != FRAME_BYTES)) { - s->stats.rx_bad++; - printk(KERN_DEBUG "cs4297a: RX Bad attributes\n"); - } else { - s->stats.rx_good++; - if ((data >> 61) == 7) { - s->read_value = (data >> 12) & 0xffff; - s->read_reg = (data >> 40) & 0x7f; - wake_up(&d->reg_wait); - } - } - descr->descr_a &= ~M_DMA_SERRX_SOP; - descr++; - d->swptr++; - data_p += 4; - if (descr == d->descrtab_end) { - descr = d->descrtab; - d->swptr = 0; - data_p = d->dma_buf; - } - __raw_writeq(1, SS_CSR(R_SER_DMA_DSCR_COUNT_RX)); - } while (--diff); - d->hwptr = hwptr; - - CS_DBGOUT(CS_DESCR, 6, - printk(KERN_INFO "cs4297a: hw/sw %x/%x\n", d->hwptr, d->swptr)); - } - - CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO - "cs4297a: cs4297a_update_ptr(): s=0x%.8x hwptr=%d total_bytes=%d count=%d \n", - (unsigned)s, d->hwptr, - d->total_bytes, d->count)); - } - - /* XXXKW worry about s->reg_request -- there is a starvation - case if s->ena has FMODE_WRITE on, but the client isn't - doing writes */ - - // update DAC pointer - // - // check for end of buffer, means that we are going to wait for another interrupt - // to allow silence to fill the fifos on the part, to keep pops down to a minimum. - // - if (s->ena & FMODE_WRITE) { - serdma_t *d = &s->dma_dac; - hwptr = (unsigned) (((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_TX)) & M_DMA_CURDSCR_ADDR) - - d->descrtab_phys) / sizeof(serdma_descr_t)); - diff = (d->ringsz + hwptr - d->hwptr) % d->ringsz; - CS_DBGOUT(CS_WAVE_WRITE, 4, printk(KERN_INFO - "cs4297a: cs4297a_update_ptr(): hw/hw/sw %x/%x/%x diff %d count %d\n", - d->hwptr, hwptr, d->swptr, diff, d->count)); - d->hwptr = hwptr; - /* XXXKW stereo? conversion? Just assume 2 16-bit samples for now */ - d->total_bytes += diff * FRAME_SAMPLE_BYTES; - if (d->mapped) { - d->count += diff * FRAME_SAMPLE_BYTES; - if (d->count >= d->fragsize) { - d->wakeup = 1; - wake_up(&d->wait); - if (d->count > d->sbufsz) - d->count &= d->sbufsz - 1; - } - } else { - d->count -= diff * FRAME_SAMPLE_BYTES; - if (d->count <= 0) { - // - // fill with silence, and do not shut down the DAC. - // Continue to play silence until the _release. - // - CS_DBGOUT(CS_WAVE_WRITE, 6, printk(KERN_INFO - "cs4297a: cs4297a_update_ptr(): memset %d at 0x%.8x for %d size \n", - (unsigned)(s->prop_dac.fmt & - (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, - (unsigned)d->dma_buf, - d->ringsz)); - memset(d->dma_buf, 0, d->ringsz * FRAME_BYTES); - if (d->count < 0) { - d->underrun = 1; - s->stats.tx_underrun++; - d->count = 0; - CS_DBGOUT(CS_ERROR, 9, printk(KERN_INFO - "cs4297a: cs4297a_update_ptr(): underrun\n")); - } - } else if (d->count <= - (signed) d->fragsize - && !d->endcleared) { - /* XXXKW what is this for? */ - clear_advance(d->dma_buf, - d->sbufsz, - d->swptr, - d->fragsize, - 0); - d->endcleared = 1; - } - if ( (d->count <= (signed) d->sbufsz/2) || intflag) - { - CS_DBGOUT(CS_WAVE_WRITE, 4, - printk(KERN_INFO - "cs4297a: update count -> %d\n", d->count)); - wake_up(&d->wait); - } - } - CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO - "cs4297a: cs4297a_update_ptr(): s=0x%.8x hwptr=%d total_bytes=%d count=%d \n", - (unsigned) s, d->hwptr, - d->total_bytes, d->count)); - } -} - -static int mixer_ioctl(struct cs4297a_state *s, unsigned int cmd, - unsigned long arg) -{ - // Index to mixer_src[] is value of AC97 Input Mux Select Reg. - // Value of array member is recording source Device ID Mask. - static const unsigned int mixer_src[8] = { - SOUND_MASK_MIC, SOUND_MASK_CD, 0, SOUND_MASK_LINE1, - SOUND_MASK_LINE, SOUND_MASK_VOLUME, 0, 0 - }; - - // Index of mixtable1[] member is Device ID - // and must be <= SOUND_MIXER_NRDEVICES. - // Value of array member is index into s->mix.vol[] - static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = { - [SOUND_MIXER_PCM] = 1, // voice - [SOUND_MIXER_LINE1] = 2, // AUX - [SOUND_MIXER_CD] = 3, // CD - [SOUND_MIXER_LINE] = 4, // Line - [SOUND_MIXER_SYNTH] = 5, // FM - [SOUND_MIXER_MIC] = 6, // Mic - [SOUND_MIXER_SPEAKER] = 7, // Speaker - [SOUND_MIXER_RECLEV] = 8, // Recording level - [SOUND_MIXER_VOLUME] = 9 // Master Volume - }; - - static const unsigned mixreg[] = { - AC97_PCMOUT_VOL, - AC97_AUX_VOL, - AC97_CD_VOL, - AC97_LINEIN_VOL - }; - unsigned char l, r, rl, rr, vidx; - unsigned char attentbl[11] = - { 63, 42, 26, 17, 14, 11, 8, 6, 4, 2, 0 }; - unsigned temp1; - int i, val; - - VALIDATE_STATE(s); - CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO - "cs4297a: mixer_ioctl(): s=0x%.8x cmd=0x%.8x\n", - (unsigned) s, cmd)); -#if CSDEBUG - cs_printioctl(cmd); -#endif -#if CSDEBUG_INTERFACE - - if ((cmd == SOUND_MIXER_CS_GETDBGMASK) || - (cmd == SOUND_MIXER_CS_SETDBGMASK) || - (cmd == SOUND_MIXER_CS_GETDBGLEVEL) || - (cmd == SOUND_MIXER_CS_SETDBGLEVEL)) - { - switch (cmd) { - - case SOUND_MIXER_CS_GETDBGMASK: - return put_user(cs_debugmask, - (unsigned long *) arg); - - case SOUND_MIXER_CS_GETDBGLEVEL: - return put_user(cs_debuglevel, - (unsigned long *) arg); - - case SOUND_MIXER_CS_SETDBGMASK: - if (get_user(val, (unsigned long *) arg)) - return -EFAULT; - cs_debugmask = val; - return 0; - - case SOUND_MIXER_CS_SETDBGLEVEL: - if (get_user(val, (unsigned long *) arg)) - return -EFAULT; - cs_debuglevel = val; - return 0; - default: - CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO - "cs4297a: mixer_ioctl(): ERROR unknown debug cmd\n")); - return 0; - } - } -#endif - - if (cmd == SOUND_MIXER_PRIVATE1) { - return -EINVAL; - } - if (cmd == SOUND_MIXER_PRIVATE2) { - // enable/disable/query spatializer - if (get_user(val, (int *) arg)) - return -EFAULT; - if (val != -1) { - temp1 = (val & 0x3f) >> 2; - cs4297a_write_ac97(s, AC97_3D_CONTROL, temp1); - cs4297a_read_ac97(s, AC97_GENERAL_PURPOSE, - &temp1); - cs4297a_write_ac97(s, AC97_GENERAL_PURPOSE, - temp1 | 0x2000); - } - cs4297a_read_ac97(s, AC97_3D_CONTROL, &temp1); - return put_user((temp1 << 2) | 3, (int *) arg); - } - if (cmd == SOUND_MIXER_INFO) { - mixer_info info; - memset(&info, 0, sizeof(info)); - strlcpy(info.id, "CS4297a", sizeof(info.id)); - strlcpy(info.name, "Crystal CS4297a", sizeof(info.name)); - info.modify_counter = s->mix.modcnt; - if (copy_to_user((void *) arg, &info, sizeof(info))) - return -EFAULT; - return 0; - } - if (cmd == SOUND_OLD_MIXER_INFO) { - _old_mixer_info info; - memset(&info, 0, sizeof(info)); - strlcpy(info.id, "CS4297a", sizeof(info.id)); - strlcpy(info.name, "Crystal CS4297a", sizeof(info.name)); - if (copy_to_user((void *) arg, &info, sizeof(info))) - return -EFAULT; - return 0; - } - if (cmd == OSS_GETVERSION) - return put_user(SOUND_VERSION, (int *) arg); - - if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) - return -EINVAL; - - // If ioctl has only the SIOC_READ bit(bit 31) - // on, process the only-read commands. - if (_SIOC_DIR(cmd) == _SIOC_READ) { - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: // Arg contains a bit for each recording source - cs4297a_read_ac97(s, AC97_RECORD_SELECT, - &temp1); - return put_user(mixer_src[temp1 & 7], (int *) arg); - - case SOUND_MIXER_DEVMASK: // Arg contains a bit for each supported device - return put_user(SOUND_MASK_PCM | SOUND_MASK_LINE | - SOUND_MASK_VOLUME | SOUND_MASK_RECLEV, - (int *) arg); - - case SOUND_MIXER_RECMASK: // Arg contains a bit for each supported recording source - return put_user(SOUND_MASK_LINE | SOUND_MASK_VOLUME, - (int *) arg); - - case SOUND_MIXER_STEREODEVS: // Mixer channels supporting stereo - return put_user(SOUND_MASK_PCM | SOUND_MASK_LINE | - SOUND_MASK_VOLUME | SOUND_MASK_RECLEV, - (int *) arg); - - case SOUND_MIXER_CAPS: - return put_user(SOUND_CAP_EXCL_INPUT, (int *) arg); - - default: - i = _IOC_NR(cmd); - if (i >= SOUND_MIXER_NRDEVICES - || !(vidx = mixtable1[i])) - return -EINVAL; - return put_user(s->mix.vol[vidx - 1], (int *) arg); - } - } - // If ioctl doesn't have both the SIOC_READ and - // the SIOC_WRITE bit set, return invalid. - if (_SIOC_DIR(cmd) != (_SIOC_READ | _SIOC_WRITE)) - return -EINVAL; - - // Increment the count of volume writes. - s->mix.modcnt++; - - // Isolate the command; it must be a write. - switch (_IOC_NR(cmd)) { - - case SOUND_MIXER_RECSRC: // Arg contains a bit for each recording source - if (get_user(val, (int *) arg)) - return -EFAULT; - i = hweight32(val); // i = # bits on in val. - if (i != 1) // One & only 1 bit must be on. - return 0; - for (i = 0; i < sizeof(mixer_src) / sizeof(int); i++) { - if (val == mixer_src[i]) { - temp1 = (i << 8) | i; - cs4297a_write_ac97(s, - AC97_RECORD_SELECT, - temp1); - return 0; - } - } - return 0; - - case SOUND_MIXER_VOLUME: - if (get_user(val, (int *) arg)) - return -EFAULT; - l = val & 0xff; - if (l > 100) - l = 100; // Max soundcard.h vol is 100. - if (l < 6) { - rl = 63; - l = 0; - } else - rl = attentbl[(10 * l) / 100]; // Convert 0-100 vol to 63-0 atten. - - r = (val >> 8) & 0xff; - if (r > 100) - r = 100; // Max right volume is 100, too - if (r < 6) { - rr = 63; - r = 0; - } else - rr = attentbl[(10 * r) / 100]; // Convert volume to attenuation. - - if ((rl > 60) && (rr > 60)) // If both l & r are 'low', - temp1 = 0x8000; // turn on the mute bit. - else - temp1 = 0; - - temp1 |= (rl << 8) | rr; - - cs4297a_write_ac97(s, AC97_MASTER_VOL_STEREO, temp1); - cs4297a_write_ac97(s, AC97_PHONE_VOL, temp1); - -#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS - s->mix.vol[8] = ((unsigned int) r << 8) | l; -#else - s->mix.vol[8] = val; -#endif - return put_user(s->mix.vol[8], (int *) arg); - - case SOUND_MIXER_SPEAKER: - if (get_user(val, (int *) arg)) - return -EFAULT; - l = val & 0xff; - if (l > 100) - l = 100; - if (l < 3) { - rl = 0; - l = 0; - } else { - rl = (l * 2 - 5) / 13; // Convert 0-100 range to 0-15. - l = (rl * 13 + 5) / 2; - } - - if (rl < 3) { - temp1 = 0x8000; - rl = 0; - } else - temp1 = 0; - rl = 15 - rl; // Convert volume to attenuation. - temp1 |= rl << 1; - cs4297a_write_ac97(s, AC97_PCBEEP_VOL, temp1); - -#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS - s->mix.vol[6] = l << 8; -#else - s->mix.vol[6] = val; -#endif - return put_user(s->mix.vol[6], (int *) arg); - - case SOUND_MIXER_RECLEV: - if (get_user(val, (int *) arg)) - return -EFAULT; - l = val & 0xff; - if (l > 100) - l = 100; - r = (val >> 8) & 0xff; - if (r > 100) - r = 100; - rl = (l * 2 - 5) / 13; // Convert 0-100 scale to 0-15. - rr = (r * 2 - 5) / 13; - if (rl < 3 && rr < 3) - temp1 = 0x8000; - else - temp1 = 0; - - temp1 = temp1 | (rl << 8) | rr; - cs4297a_write_ac97(s, AC97_RECORD_GAIN, temp1); - -#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS - s->mix.vol[7] = ((unsigned int) r << 8) | l; -#else - s->mix.vol[7] = val; -#endif - return put_user(s->mix.vol[7], (int *) arg); - - case SOUND_MIXER_MIC: - if (get_user(val, (int *) arg)) - return -EFAULT; - l = val & 0xff; - if (l > 100) - l = 100; - if (l < 1) { - l = 0; - rl = 0; - } else { - rl = ((unsigned) l * 5 - 4) / 16; // Convert 0-100 range to 0-31. - l = (rl * 16 + 4) / 5; - } - cs4297a_read_ac97(s, AC97_MIC_VOL, &temp1); - temp1 &= 0x40; // Isolate 20db gain bit. - if (rl < 3) { - temp1 |= 0x8000; - rl = 0; - } - rl = 31 - rl; // Convert volume to attenuation. - temp1 |= rl; - cs4297a_write_ac97(s, AC97_MIC_VOL, temp1); - -#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS - s->mix.vol[5] = val << 8; -#else - s->mix.vol[5] = val; -#endif - return put_user(s->mix.vol[5], (int *) arg); - - - case SOUND_MIXER_SYNTH: - if (get_user(val, (int *) arg)) - return -EFAULT; - l = val & 0xff; - if (l > 100) - l = 100; - if (get_user(val, (int *) arg)) - return -EFAULT; - r = (val >> 8) & 0xff; - if (r > 100) - r = 100; - rl = (l * 2 - 11) / 3; // Convert 0-100 range to 0-63. - rr = (r * 2 - 11) / 3; - if (rl < 3) // If l is low, turn on - temp1 = 0x0080; // the mute bit. - else - temp1 = 0; - - rl = 63 - rl; // Convert vol to attenuation. -// writel(temp1 | rl, s->pBA0 + FMLVC); - if (rr < 3) // If rr is low, turn on - temp1 = 0x0080; // the mute bit. - else - temp1 = 0; - rr = 63 - rr; // Convert vol to attenuation. -// writel(temp1 | rr, s->pBA0 + FMRVC); - -#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS - s->mix.vol[4] = (r << 8) | l; -#else - s->mix.vol[4] = val; -#endif - return put_user(s->mix.vol[4], (int *) arg); - - - default: - CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO - "cs4297a: mixer_ioctl(): default\n")); - - i = _IOC_NR(cmd); - if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i])) - return -EINVAL; - if (get_user(val, (int *) arg)) - return -EFAULT; - l = val & 0xff; - if (l > 100) - l = 100; - if (l < 1) { - l = 0; - rl = 31; - } else - rl = (attentbl[(l * 10) / 100]) >> 1; - - r = (val >> 8) & 0xff; - if (r > 100) - r = 100; - if (r < 1) { - r = 0; - rr = 31; - } else - rr = (attentbl[(r * 10) / 100]) >> 1; - if ((rl > 30) && (rr > 30)) - temp1 = 0x8000; - else - temp1 = 0; - temp1 = temp1 | (rl << 8) | rr; - cs4297a_write_ac97(s, mixreg[vidx - 1], temp1); - -#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS - s->mix.vol[vidx - 1] = ((unsigned int) r << 8) | l; -#else - s->mix.vol[vidx - 1] = val; -#endif - return put_user(s->mix.vol[vidx - 1], (int *) arg); - } -} - - -// --------------------------------------------------------------------- - -static int cs4297a_open_mixdev(struct inode *inode, struct file *file) -{ - int minor = iminor(inode); - struct cs4297a_state *s=NULL; - struct list_head *entry; - - CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, - printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()+\n")); - - mutex_lock(&swarm_cs4297a_mutex); - list_for_each(entry, &cs4297a_devs) - { - s = list_entry(entry, struct cs4297a_state, list); - if(s->dev_mixer == minor) - break; - } - if (!s) - { - CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, - printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()- -ENODEV\n")); - - mutex_unlock(&swarm_cs4297a_mutex); - return -ENODEV; - } - VALIDATE_STATE(s); - file->private_data = s; - - CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, - printk(KERN_INFO "cs4297a: cs4297a_open_mixdev()- 0\n")); - mutex_unlock(&swarm_cs4297a_mutex); - - return nonseekable_open(inode, file); -} - - -static int cs4297a_release_mixdev(struct inode *inode, struct file *file) -{ - struct cs4297a_state *s = - (struct cs4297a_state *) file->private_data; - - VALIDATE_STATE(s); - return 0; -} - - -static int cs4297a_ioctl_mixdev(struct file *file, - unsigned int cmd, unsigned long arg) -{ - int ret; - mutex_lock(&swarm_cs4297a_mutex); - ret = mixer_ioctl((struct cs4297a_state *) file->private_data, cmd, - arg); - mutex_unlock(&swarm_cs4297a_mutex); - return ret; -} - - -// ****************************************************************************************** -// Mixer file operations struct. -// ****************************************************************************************** -static const struct file_operations cs4297a_mixer_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .unlocked_ioctl = cs4297a_ioctl_mixdev, - .open = cs4297a_open_mixdev, - .release = cs4297a_release_mixdev, -}; - -// --------------------------------------------------------------------- - - -static int drain_adc(struct cs4297a_state *s, int nonblock) -{ - /* This routine serves no purpose currently - any samples - sitting in the receive queue will just be processed by the - background consumer. This would be different if DMA - actually stopped when there were no clients. */ - return 0; -} - -static int drain_dac(struct cs4297a_state *s, int nonblock) -{ - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - unsigned hwptr; - unsigned tmo; - int count; - - if (s->dma_dac.mapped) - return 0; - if (nonblock) - return -EBUSY; - add_wait_queue(&s->dma_dac.wait, &wait); - while ((count = __raw_readq(SS_CSR(R_SER_DMA_DSCR_COUNT_TX))) || - (s->dma_dac.count > 0)) { - if (!signal_pending(current)) { - set_current_state(TASK_INTERRUPTIBLE); - /* XXXKW is this calculation working? */ - tmo = ((count * FRAME_TX_US) * HZ) / 1000000; - schedule_timeout(tmo + 1); - } else { - /* XXXKW do I care if there is a signal pending? */ - } - } - spin_lock_irqsave(&s->lock, flags); - /* Reset the bookkeeping */ - hwptr = (int)(((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_TX)) & M_DMA_CURDSCR_ADDR) - - s->dma_dac.descrtab_phys) / sizeof(serdma_descr_t)); - s->dma_dac.hwptr = s->dma_dac.swptr = hwptr; - spin_unlock_irqrestore(&s->lock, flags); - remove_wait_queue(&s->dma_dac.wait, &wait); - __set_current_state(TASK_RUNNING); - return 0; -} - - -// --------------------------------------------------------------------- - -static ssize_t cs4297a_read(struct file *file, char *buffer, size_t count, - loff_t * ppos) -{ - struct cs4297a_state *s = - (struct cs4297a_state *) file->private_data; - ssize_t ret; - unsigned long flags; - int cnt, count_fr, cnt_by; - unsigned copied = 0; - - CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2, - printk(KERN_INFO "cs4297a: cs4297a_read()+ %d \n", count)); - - VALIDATE_STATE(s); - if (s->dma_adc.mapped) - return -ENXIO; - if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) - return ret; - if (!access_ok(VERIFY_WRITE, buffer, count)) - return -EFAULT; - ret = 0; -// -// "count" is the amount of bytes to read (from app), is decremented each loop -// by the amount of bytes that have been returned to the user buffer. -// "cnt" is the running total of each read from the buffer (changes each loop) -// "buffer" points to the app's buffer -// "ret" keeps a running total of the amount of bytes that have been copied -// to the user buffer. -// "copied" is the total bytes copied into the user buffer for each loop. -// - while (count > 0) { - CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO - "_read() count>0 count=%d .count=%d .swptr=%d .hwptr=%d \n", - count, s->dma_adc.count, - s->dma_adc.swptr, s->dma_adc.hwptr)); - spin_lock_irqsave(&s->lock, flags); - - /* cnt will be the number of available samples (16-bit - stereo); it starts out as the maxmimum consequetive - samples */ - cnt = (s->dma_adc.sb_end - s->dma_adc.sb_swptr) / 2; - count_fr = s->dma_adc.count / FRAME_SAMPLE_BYTES; - - // dma_adc.count is the current total bytes that have not been read. - // if the amount of unread bytes from the current sw pointer to the - // end of the buffer is greater than the current total bytes that - // have not been read, then set the "cnt" (unread bytes) to the - // amount of unread bytes. - - if (count_fr < cnt) - cnt = count_fr; - cnt_by = cnt * FRAME_SAMPLE_BYTES; - spin_unlock_irqrestore(&s->lock, flags); - // - // if we are converting from 8/16 then we need to copy - // twice the number of 16 bit bytes then 8 bit bytes. - // - if (s->conversion) { - if (cnt_by > (count * 2)) { - cnt = (count * 2) / FRAME_SAMPLE_BYTES; - cnt_by = count * 2; - } - } else { - if (cnt_by > count) { - cnt = count / FRAME_SAMPLE_BYTES; - cnt_by = count; - } - } - // - // "cnt" NOW is the smaller of the amount that will be read, - // and the amount that is requested in this read (or partial). - // if there are no bytes in the buffer to read, then start the - // ADC and wait for the interrupt handler to wake us up. - // - if (cnt <= 0) { - - // start up the dma engine and then continue back to the top of - // the loop when wake up occurs. - start_adc(s); - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - oss_broken_sleep_on(&s->dma_adc.wait, MAX_SCHEDULE_TIMEOUT); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; - continue; - } - // there are bytes in the buffer to read. - // copy from the hw buffer over to the user buffer. - // user buffer is designated by "buffer" - // virtual address to copy from is dma_buf+swptr - // the "cnt" is the number of bytes to read. - - CS_DBGOUT(CS_WAVE_READ, 2, printk(KERN_INFO - "_read() copy_to cnt=%d count=%d ", cnt_by, count)); - CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO - " .sbufsz=%d .count=%d buffer=0x%.8x ret=%d\n", - s->dma_adc.sbufsz, s->dma_adc.count, - (unsigned) buffer, ret)); - - if (copy_to_user (buffer, ((void *)s->dma_adc.sb_swptr), cnt_by)) - return ret ? ret : -EFAULT; - copied = cnt_by; - - /* Return the descriptors */ - spin_lock_irqsave(&s->lock, flags); - CS_DBGOUT(CS_FUNCTION, 2, - printk(KERN_INFO "cs4297a: upd_rcv sw->hw %x/%x\n", s->dma_adc.swptr, s->dma_adc.hwptr)); - s->dma_adc.count -= cnt_by; - s->dma_adc.sb_swptr += cnt * 2; - if (s->dma_adc.sb_swptr == s->dma_adc.sb_end) - s->dma_adc.sb_swptr = s->dma_adc.sample_buf; - spin_unlock_irqrestore(&s->lock, flags); - count -= copied; - buffer += copied; - ret += copied; - start_adc(s); - } - CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2, - printk(KERN_INFO "cs4297a: cs4297a_read()- %d\n", ret)); - return ret; -} - - -static ssize_t cs4297a_write(struct file *file, const char *buffer, - size_t count, loff_t * ppos) -{ - struct cs4297a_state *s = - (struct cs4297a_state *) file->private_data; - ssize_t ret; - unsigned long flags; - unsigned swptr, hwptr; - int cnt; - - CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2, - printk(KERN_INFO "cs4297a: cs4297a_write()+ count=%d\n", - count)); - VALIDATE_STATE(s); - - if (s->dma_dac.mapped) - return -ENXIO; - if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s))) - return ret; - if (!access_ok(VERIFY_READ, buffer, count)) - return -EFAULT; - ret = 0; - while (count > 0) { - serdma_t *d = &s->dma_dac; - int copy_cnt; - u32 *s_tmpl; - u32 *t_tmpl; - u32 left, right; - int swap = (s->prop_dac.fmt == AFMT_S16_LE) || (s->prop_dac.fmt == AFMT_U16_LE); - - /* XXXXXX this is broken for BLOAT_FACTOR */ - spin_lock_irqsave(&s->lock, flags); - if (d->count < 0) { - d->count = 0; - d->swptr = d->hwptr; - } - if (d->underrun) { - d->underrun = 0; - hwptr = (unsigned) (((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_TX)) & M_DMA_CURDSCR_ADDR) - - d->descrtab_phys) / sizeof(serdma_descr_t)); - d->swptr = d->hwptr = hwptr; - } - swptr = d->swptr; - cnt = d->sbufsz - (swptr * FRAME_SAMPLE_BYTES); - /* Will this write fill up the buffer? */ - if (d->count + cnt > d->sbufsz) - cnt = d->sbufsz - d->count; - spin_unlock_irqrestore(&s->lock, flags); - if (cnt > count) - cnt = count; - if (cnt <= 0) { - start_dac(s); - if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EAGAIN; - oss_broken_sleep_on(&d->wait, MAX_SCHEDULE_TIMEOUT); - if (signal_pending(current)) - return ret ? ret : -ERESTARTSYS; - continue; - } - if (copy_from_user(d->sample_buf, buffer, cnt)) - return ret ? ret : -EFAULT; - - copy_cnt = cnt; - s_tmpl = (u32 *)d->sample_buf; - t_tmpl = (u32 *)(d->dma_buf + (swptr * 4)); - - /* XXXKW assuming 16-bit stereo! */ - do { - u32 tmp; - - t_tmpl[0] = cpu_to_be32(0x98000000); - - tmp = be32_to_cpu(s_tmpl[0]); - left = tmp & 0xffff; - right = tmp >> 16; - if (swap) { - left = swab16(left); - right = swab16(right); - } - t_tmpl[1] = cpu_to_be32(left >> 8); - t_tmpl[2] = cpu_to_be32(((left & 0xff) << 24) | - (right << 4)); - - s_tmpl++; - t_tmpl += 8; - copy_cnt -= 4; - } while (copy_cnt); - - /* Mux in any pending read/write accesses */ - if (s->reg_request) { - *(u64 *)(d->dma_buf + (swptr * 4)) |= - cpu_to_be64(s->reg_request); - s->reg_request = 0; - wake_up(&s->dma_dac.reg_wait); - } - - CS_DBGOUT(CS_WAVE_WRITE, 4, - printk(KERN_INFO - "cs4297a: copy in %d to swptr %x\n", cnt, swptr)); - - swptr = (swptr + (cnt/FRAME_SAMPLE_BYTES)) % d->ringsz; - __raw_writeq(cnt/FRAME_SAMPLE_BYTES, SS_CSR(R_SER_DMA_DSCR_COUNT_TX)); - spin_lock_irqsave(&s->lock, flags); - d->swptr = swptr; - d->count += cnt; - d->endcleared = 0; - spin_unlock_irqrestore(&s->lock, flags); - count -= cnt; - buffer += cnt; - ret += cnt; - start_dac(s); - } - CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2, - printk(KERN_INFO "cs4297a: cs4297a_write()- %d\n", ret)); - return ret; -} - - -static unsigned int cs4297a_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct cs4297a_state *s = - (struct cs4297a_state *) file->private_data; - unsigned long flags; - unsigned int mask = 0; - - CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, - printk(KERN_INFO "cs4297a: cs4297a_poll()+\n")); - VALIDATE_STATE(s); - if (file->f_mode & FMODE_WRITE) { - CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, - printk(KERN_INFO - "cs4297a: cs4297a_poll() wait on FMODE_WRITE\n")); - if(!s->dma_dac.ready && prog_dmabuf_dac(s)) - return 0; - poll_wait(file, &s->dma_dac.wait, wait); - } - if (file->f_mode & FMODE_READ) { - CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, - printk(KERN_INFO - "cs4297a: cs4297a_poll() wait on FMODE_READ\n")); - if(!s->dma_dac.ready && prog_dmabuf_adc(s)) - return 0; - poll_wait(file, &s->dma_adc.wait, wait); - } - spin_lock_irqsave(&s->lock, flags); - cs4297a_update_ptr(s,CS_FALSE); - if (file->f_mode & FMODE_WRITE) { - if (s->dma_dac.mapped) { - if (s->dma_dac.count >= - (signed) s->dma_dac.fragsize) { - if (s->dma_dac.wakeup) - mask |= POLLOUT | POLLWRNORM; - else - mask = 0; - s->dma_dac.wakeup = 0; - } - } else { - if ((signed) (s->dma_dac.sbufsz/2) >= s->dma_dac.count) - mask |= POLLOUT | POLLWRNORM; - } - } else if (file->f_mode & FMODE_READ) { - if (s->dma_adc.mapped) { - if (s->dma_adc.count >= (signed) s->dma_adc.fragsize) - mask |= POLLIN | POLLRDNORM; - } else { - if (s->dma_adc.count > 0) - mask |= POLLIN | POLLRDNORM; - } - } - spin_unlock_irqrestore(&s->lock, flags); - CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, - printk(KERN_INFO "cs4297a: cs4297a_poll()- 0x%.8x\n", - mask)); - return mask; -} - - -static int cs4297a_mmap(struct file *file, struct vm_area_struct *vma) -{ - /* XXXKW currently no mmap support */ - return -EINVAL; - return 0; -} - - -static int cs4297a_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct cs4297a_state *s = - (struct cs4297a_state *) file->private_data; - unsigned long flags; - audio_buf_info abinfo; - count_info cinfo; - int val, mapped, ret; - - CS_DBGOUT(CS_FUNCTION|CS_IOCTL, 4, printk(KERN_INFO - "cs4297a: cs4297a_ioctl(): file=0x%.8x cmd=0x%.8x\n", - (unsigned) file, cmd)); -#if CSDEBUG - cs_printioctl(cmd); -#endif - VALIDATE_STATE(s); - mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || - ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); - switch (cmd) { - case OSS_GETVERSION: - CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO - "cs4297a: cs4297a_ioctl(): SOUND_VERSION=0x%.8x\n", - SOUND_VERSION)); - return put_user(SOUND_VERSION, (int *) arg); - - case SNDCTL_DSP_SYNC: - CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO - "cs4297a: cs4297a_ioctl(): DSP_SYNC\n")); - if (file->f_mode & FMODE_WRITE) - return drain_dac(s, - 0 /*file->f_flags & O_NONBLOCK */ - ); - return 0; - - case SNDCTL_DSP_SETDUPLEX: - return 0; - - case SNDCTL_DSP_GETCAPS: - return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | - DSP_CAP_TRIGGER | DSP_CAP_MMAP, - (int *) arg); - - case SNDCTL_DSP_RESET: - CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO - "cs4297a: cs4297a_ioctl(): DSP_RESET\n")); - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - synchronize_irq(s->irq); - s->dma_dac.count = s->dma_dac.total_bytes = - s->dma_dac.blocks = s->dma_dac.wakeup = 0; - s->dma_dac.swptr = s->dma_dac.hwptr = - (int)(((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_TX)) & M_DMA_CURDSCR_ADDR) - - s->dma_dac.descrtab_phys) / sizeof(serdma_descr_t)); - } - if (file->f_mode & FMODE_READ) { - stop_adc(s); - synchronize_irq(s->irq); - s->dma_adc.count = s->dma_adc.total_bytes = - s->dma_adc.blocks = s->dma_dac.wakeup = 0; - s->dma_adc.swptr = s->dma_adc.hwptr = - (int)(((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_RX)) & M_DMA_CURDSCR_ADDR) - - s->dma_adc.descrtab_phys) / sizeof(serdma_descr_t)); - } - return 0; - - case SNDCTL_DSP_SPEED: - if (get_user(val, (int *) arg)) - return -EFAULT; - CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO - "cs4297a: cs4297a_ioctl(): DSP_SPEED val=%d -> 48000\n", val)); - val = 48000; - return put_user(val, (int *) arg); - - case SNDCTL_DSP_STEREO: - if (get_user(val, (int *) arg)) - return -EFAULT; - CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO - "cs4297a: cs4297a_ioctl(): DSP_STEREO val=%d\n", val)); - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - s->prop_adc.channels = val ? 2 : 1; - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - s->prop_dac.channels = val ? 2 : 1; - } - return 0; - - case SNDCTL_DSP_CHANNELS: - if (get_user(val, (int *) arg)) - return -EFAULT; - CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO - "cs4297a: cs4297a_ioctl(): DSP_CHANNELS val=%d\n", - val)); - if (val != 0) { - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val >= 2) - s->prop_adc.channels = 2; - else - s->prop_adc.channels = 1; - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val >= 2) - s->prop_dac.channels = 2; - else - s->prop_dac.channels = 1; - } - } - - if (file->f_mode & FMODE_WRITE) - val = s->prop_dac.channels; - else if (file->f_mode & FMODE_READ) - val = s->prop_adc.channels; - - return put_user(val, (int *) arg); - - case SNDCTL_DSP_GETFMTS: // Returns a mask - CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO - "cs4297a: cs4297a_ioctl(): DSP_GETFMT val=0x%.8x\n", - AFMT_S16_LE | AFMT_U16_LE | AFMT_S8 | - AFMT_U8)); - return put_user(AFMT_S16_LE | AFMT_U16_LE | AFMT_S8 | - AFMT_U8, (int *) arg); - - case SNDCTL_DSP_SETFMT: - if (get_user(val, (int *) arg)) - return -EFAULT; - CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO - "cs4297a: cs4297a_ioctl(): DSP_SETFMT val=0x%.8x\n", - val)); - if (val != AFMT_QUERY) { - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - if (val != AFMT_S16_LE - && val != AFMT_U16_LE && val != AFMT_S8 - && val != AFMT_U8) - val = AFMT_U8; - s->prop_adc.fmt = val; - s->prop_adc.fmt_original = s->prop_adc.fmt; - } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - if (val != AFMT_S16_LE - && val != AFMT_U16_LE && val != AFMT_S8 - && val != AFMT_U8) - val = AFMT_U8; - s->prop_dac.fmt = val; - s->prop_dac.fmt_original = s->prop_dac.fmt; - } - } else { - if (file->f_mode & FMODE_WRITE) - val = s->prop_dac.fmt_original; - else if (file->f_mode & FMODE_READ) - val = s->prop_adc.fmt_original; - } - CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO - "cs4297a: cs4297a_ioctl(): DSP_SETFMT return val=0x%.8x\n", - val)); - return put_user(val, (int *) arg); - - case SNDCTL_DSP_POST: - CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO - "cs4297a: cs4297a_ioctl(): DSP_POST\n")); - return 0; - - case SNDCTL_DSP_GETTRIGGER: - val = 0; - if (file->f_mode & s->ena & FMODE_READ) - val |= PCM_ENABLE_INPUT; - if (file->f_mode & s->ena & FMODE_WRITE) - val |= PCM_ENABLE_OUTPUT; - return put_user(val, (int *) arg); - - case SNDCTL_DSP_SETTRIGGER: - if (get_user(val, (int *) arg)) - return -EFAULT; - if (file->f_mode & FMODE_READ) { - if (val & PCM_ENABLE_INPUT) { - if (!s->dma_adc.ready - && (ret = prog_dmabuf_adc(s))) - return ret; - start_adc(s); - } else - stop_adc(s); - } - if (file->f_mode & FMODE_WRITE) { - if (val & PCM_ENABLE_OUTPUT) { - if (!s->dma_dac.ready - && (ret = prog_dmabuf_dac(s))) - return ret; - start_dac(s); - } else - stop_dac(s); - } - return 0; - - case SNDCTL_DSP_GETOSPACE: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s))) - return val; - spin_lock_irqsave(&s->lock, flags); - cs4297a_update_ptr(s,CS_FALSE); - abinfo.fragsize = s->dma_dac.fragsize; - if (s->dma_dac.mapped) - abinfo.bytes = s->dma_dac.sbufsz; - else - abinfo.bytes = - s->dma_dac.sbufsz - s->dma_dac.count; - abinfo.fragstotal = s->dma_dac.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; - CS_DBGOUT(CS_FUNCTION | CS_PARMS, 4, printk(KERN_INFO - "cs4297a: cs4297a_ioctl(): GETOSPACE .fragsize=%d .bytes=%d .fragstotal=%d .fragments=%d\n", - abinfo.fragsize,abinfo.bytes,abinfo.fragstotal, - abinfo.fragments)); - spin_unlock_irqrestore(&s->lock, flags); - return copy_to_user((void *) arg, &abinfo, - sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETISPACE: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s))) - return val; - spin_lock_irqsave(&s->lock, flags); - cs4297a_update_ptr(s,CS_FALSE); - if (s->conversion) { - abinfo.fragsize = s->dma_adc.fragsize / 2; - abinfo.bytes = s->dma_adc.count / 2; - abinfo.fragstotal = s->dma_adc.numfrag; - abinfo.fragments = - abinfo.bytes >> (s->dma_adc.fragshift - 1); - } else { - abinfo.fragsize = s->dma_adc.fragsize; - abinfo.bytes = s->dma_adc.count; - abinfo.fragstotal = s->dma_adc.numfrag; - abinfo.fragments = - abinfo.bytes >> s->dma_adc.fragshift; - } - spin_unlock_irqrestore(&s->lock, flags); - return copy_to_user((void *) arg, &abinfo, - sizeof(abinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_NONBLOCK: - spin_lock(&file->f_lock); - file->f_flags |= O_NONBLOCK; - spin_unlock(&file->f_lock); - return 0; - - case SNDCTL_DSP_GETODELAY: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if(!s->dma_dac.ready && prog_dmabuf_dac(s)) - return 0; - spin_lock_irqsave(&s->lock, flags); - cs4297a_update_ptr(s,CS_FALSE); - val = s->dma_dac.count; - spin_unlock_irqrestore(&s->lock, flags); - return put_user(val, (int *) arg); - - case SNDCTL_DSP_GETIPTR: - if (!(file->f_mode & FMODE_READ)) - return -EINVAL; - if(!s->dma_adc.ready && prog_dmabuf_adc(s)) - return 0; - spin_lock_irqsave(&s->lock, flags); - cs4297a_update_ptr(s,CS_FALSE); - cinfo.bytes = s->dma_adc.total_bytes; - if (s->dma_adc.mapped) { - cinfo.blocks = - (cinfo.bytes >> s->dma_adc.fragshift) - - s->dma_adc.blocks; - s->dma_adc.blocks = - cinfo.bytes >> s->dma_adc.fragshift; - } else { - if (s->conversion) { - cinfo.blocks = - s->dma_adc.count / - 2 >> (s->dma_adc.fragshift - 1); - } else - cinfo.blocks = - s->dma_adc.count >> s->dma_adc. - fragshift; - } - if (s->conversion) - cinfo.ptr = s->dma_adc.hwptr / 2; - else - cinfo.ptr = s->dma_adc.hwptr; - if (s->dma_adc.mapped) - s->dma_adc.count &= s->dma_adc.fragsize - 1; - spin_unlock_irqrestore(&s->lock, flags); - return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETOPTR: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if(!s->dma_dac.ready && prog_dmabuf_dac(s)) - return 0; - spin_lock_irqsave(&s->lock, flags); - cs4297a_update_ptr(s,CS_FALSE); - cinfo.bytes = s->dma_dac.total_bytes; - if (s->dma_dac.mapped) { - cinfo.blocks = - (cinfo.bytes >> s->dma_dac.fragshift) - - s->dma_dac.blocks; - s->dma_dac.blocks = - cinfo.bytes >> s->dma_dac.fragshift; - } else { - cinfo.blocks = - s->dma_dac.count >> s->dma_dac.fragshift; - } - cinfo.ptr = s->dma_dac.hwptr; - if (s->dma_dac.mapped) - s->dma_dac.count &= s->dma_dac.fragsize - 1; - spin_unlock_irqrestore(&s->lock, flags); - return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0; - - case SNDCTL_DSP_GETBLKSIZE: - if (file->f_mode & FMODE_WRITE) { - if ((val = prog_dmabuf_dac(s))) - return val; - return put_user(s->dma_dac.fragsize, (int *) arg); - } - if ((val = prog_dmabuf_adc(s))) - return val; - if (s->conversion) - return put_user(s->dma_adc.fragsize / 2, - (int *) arg); - else - return put_user(s->dma_adc.fragsize, (int *) arg); - - case SNDCTL_DSP_SETFRAGMENT: - if (get_user(val, (int *) arg)) - return -EFAULT; - return 0; // Say OK, but do nothing. - - case SNDCTL_DSP_SUBDIVIDE: - if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) - || (file->f_mode & FMODE_WRITE - && s->dma_dac.subdivision)) return -EINVAL; - if (get_user(val, (int *) arg)) - return -EFAULT; - if (val != 1 && val != 2 && val != 4) - return -EINVAL; - if (file->f_mode & FMODE_READ) - s->dma_adc.subdivision = val; - else if (file->f_mode & FMODE_WRITE) - s->dma_dac.subdivision = val; - return 0; - - case SOUND_PCM_READ_RATE: - if (file->f_mode & FMODE_READ) - return put_user(s->prop_adc.rate, (int *) arg); - else if (file->f_mode & FMODE_WRITE) - return put_user(s->prop_dac.rate, (int *) arg); - - case SOUND_PCM_READ_CHANNELS: - if (file->f_mode & FMODE_READ) - return put_user(s->prop_adc.channels, (int *) arg); - else if (file->f_mode & FMODE_WRITE) - return put_user(s->prop_dac.channels, (int *) arg); - - case SOUND_PCM_READ_BITS: - if (file->f_mode & FMODE_READ) - return - put_user( - (s->prop_adc. - fmt & (AFMT_S8 | AFMT_U8)) ? 8 : 16, - (int *) arg); - else if (file->f_mode & FMODE_WRITE) - return - put_user( - (s->prop_dac. - fmt & (AFMT_S8 | AFMT_U8)) ? 8 : 16, - (int *) arg); - - case SOUND_PCM_WRITE_FILTER: - case SNDCTL_DSP_SETSYNCRO: - case SOUND_PCM_READ_FILTER: - return -EINVAL; - } - return mixer_ioctl(s, cmd, arg); -} - -static long cs4297a_unlocked_ioctl(struct file *file, u_int cmd, u_long arg) -{ - int ret; - - mutex_lock(&swarm_cs4297a_mutex); - ret = cs4297a_ioctl(file, cmd, arg); - mutex_unlock(&swarm_cs4297a_mutex); - - return ret; -} - -static int cs4297a_release(struct inode *inode, struct file *file) -{ - struct cs4297a_state *s = - (struct cs4297a_state *) file->private_data; - - CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 2, printk(KERN_INFO - "cs4297a: cs4297a_release(): inode=0x%.8x file=0x%.8x f_mode=0x%x\n", - (unsigned) inode, (unsigned) file, file->f_mode)); - VALIDATE_STATE(s); - - if (file->f_mode & FMODE_WRITE) { - drain_dac(s, file->f_flags & O_NONBLOCK); - mutex_lock(&s->open_sem_dac); - stop_dac(s); - dealloc_dmabuf(s, &s->dma_dac); - s->open_mode &= ~FMODE_WRITE; - mutex_unlock(&s->open_sem_dac); - wake_up(&s->open_wait_dac); - } - if (file->f_mode & FMODE_READ) { - drain_adc(s, file->f_flags & O_NONBLOCK); - mutex_lock(&s->open_sem_adc); - stop_adc(s); - dealloc_dmabuf(s, &s->dma_adc); - s->open_mode &= ~FMODE_READ; - mutex_unlock(&s->open_sem_adc); - wake_up(&s->open_wait_adc); - } - return 0; -} - -static int cs4297a_locked_open(struct inode *inode, struct file *file) -{ - int minor = iminor(inode); - struct cs4297a_state *s=NULL; - struct list_head *entry; - - CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO - "cs4297a: cs4297a_open(): inode=0x%.8x file=0x%.8x f_mode=0x%x\n", - (unsigned) inode, (unsigned) file, file->f_mode)); - CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO - "cs4297a: status = %08x\n", (int)__raw_readq(SS_CSR(R_SER_STATUS_DEBUG)))); - - list_for_each(entry, &cs4297a_devs) - { - s = list_entry(entry, struct cs4297a_state, list); - - if (!((s->dev_audio ^ minor) & ~0xf)) - break; - } - if (entry == &cs4297a_devs) - return -ENODEV; - if (!s) { - CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO - "cs4297a: cs4297a_open(): Error - unable to find audio state struct\n")); - return -ENODEV; - } - VALIDATE_STATE(s); - file->private_data = s; - - // wait for device to become free - if (!(file->f_mode & (FMODE_WRITE | FMODE_READ))) { - CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, printk(KERN_INFO - "cs4297a: cs4297a_open(): Error - must open READ and/or WRITE\n")); - return -ENODEV; - } - if (file->f_mode & FMODE_WRITE) { - if (__raw_readq(SS_CSR(R_SER_DMA_DSCR_COUNT_TX)) != 0) { - printk(KERN_ERR "cs4297a: TX pipe needs to drain\n"); - while (__raw_readq(SS_CSR(R_SER_DMA_DSCR_COUNT_TX))) - ; - } - - mutex_lock(&s->open_sem_dac); - while (s->open_mode & FMODE_WRITE) { - if (file->f_flags & O_NONBLOCK) { - mutex_unlock(&s->open_sem_dac); - return -EBUSY; - } - mutex_unlock(&s->open_sem_dac); - oss_broken_sleep_on(&s->open_wait_dac, MAX_SCHEDULE_TIMEOUT); - - if (signal_pending(current)) { - printk("open - sig pending\n"); - return -ERESTARTSYS; - } - mutex_lock(&s->open_sem_dac); - } - } - if (file->f_mode & FMODE_READ) { - mutex_lock(&s->open_sem_adc); - while (s->open_mode & FMODE_READ) { - if (file->f_flags & O_NONBLOCK) { - mutex_unlock(&s->open_sem_adc); - return -EBUSY; - } - mutex_unlock(&s->open_sem_adc); - oss_broken_sleep_on(&s->open_wait_adc, MAX_SCHEDULE_TIMEOUT); - - if (signal_pending(current)) { - printk("open - sig pending\n"); - return -ERESTARTSYS; - } - mutex_lock(&s->open_sem_adc); - } - } - s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); - if (file->f_mode & FMODE_READ) { - s->prop_adc.fmt = AFMT_S16_BE; - s->prop_adc.fmt_original = s->prop_adc.fmt; - s->prop_adc.channels = 2; - s->prop_adc.rate = 48000; - s->conversion = 0; - s->ena &= ~FMODE_READ; - s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = - s->dma_adc.subdivision = 0; - mutex_unlock(&s->open_sem_adc); - - if (prog_dmabuf_adc(s)) { - CS_DBGOUT(CS_OPEN | CS_ERROR, 2, printk(KERN_ERR - "cs4297a: adc Program dmabufs failed.\n")); - cs4297a_release(inode, file); - return -ENOMEM; - } - } - if (file->f_mode & FMODE_WRITE) { - s->prop_dac.fmt = AFMT_S16_BE; - s->prop_dac.fmt_original = s->prop_dac.fmt; - s->prop_dac.channels = 2; - s->prop_dac.rate = 48000; - s->conversion = 0; - s->ena &= ~FMODE_WRITE; - s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = - s->dma_dac.subdivision = 0; - mutex_unlock(&s->open_sem_dac); - - if (prog_dmabuf_dac(s)) { - CS_DBGOUT(CS_OPEN | CS_ERROR, 2, printk(KERN_ERR - "cs4297a: dac Program dmabufs failed.\n")); - cs4297a_release(inode, file); - return -ENOMEM; - } - } - CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, - printk(KERN_INFO "cs4297a: cs4297a_open()- 0\n")); - return nonseekable_open(inode, file); -} - -static int cs4297a_open(struct inode *inode, struct file *file) -{ - int ret; - - mutex_lock(&swarm_cs4297a_mutex); - ret = cs4297a_open(inode, file); - mutex_unlock(&swarm_cs4297a_mutex); - - return ret; -} - -// ****************************************************************************************** -// Wave (audio) file operations struct. -// ****************************************************************************************** -static const struct file_operations cs4297a_audio_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = cs4297a_read, - .write = cs4297a_write, - .poll = cs4297a_poll, - .unlocked_ioctl = cs4297a_unlocked_ioctl, - .mmap = cs4297a_mmap, - .open = cs4297a_open, - .release = cs4297a_release, -}; - -static void cs4297a_interrupt(int irq, void *dev_id) -{ - struct cs4297a_state *s = (struct cs4297a_state *) dev_id; - u32 status; - - status = __raw_readq(SS_CSR(R_SER_STATUS_DEBUG)); - - CS_DBGOUT(CS_INTERRUPT, 6, printk(KERN_INFO - "cs4297a: cs4297a_interrupt() HISR=0x%.8x\n", status)); - -#if 0 - /* XXXKW what check *should* be done here? */ - if (!(status & (M_SYNCSER_RX_EOP_COUNT | M_SYNCSER_RX_OVERRUN | M_SYNCSER_RX_SYNC_ERR))) { - status = __raw_readq(SS_CSR(R_SER_STATUS)); - printk(KERN_ERR "cs4297a: unexpected interrupt (status %08x)\n", status); - return; - } -#endif - - if (status & M_SYNCSER_RX_SYNC_ERR) { - status = __raw_readq(SS_CSR(R_SER_STATUS)); - printk(KERN_ERR "cs4297a: rx sync error (status %08x)\n", status); - return; - } - - if (status & M_SYNCSER_RX_OVERRUN) { - int newptr, i; - s->stats.rx_ovrrn++; - printk(KERN_ERR "cs4297a: receive FIFO overrun\n"); - - /* Fix things up: get the receive descriptor pool - clean and give them back to the hardware */ - while (__raw_readq(SS_CSR(R_SER_DMA_DSCR_COUNT_RX))) - ; - newptr = (unsigned) (((__raw_readq(SS_CSR(R_SER_DMA_CUR_DSCR_ADDR_RX)) & M_DMA_CURDSCR_ADDR) - - s->dma_adc.descrtab_phys) / sizeof(serdma_descr_t)); - for (i=0; i<DMA_DESCR; i++) { - s->dma_adc.descrtab[i].descr_a &= ~M_DMA_SERRX_SOP; - } - s->dma_adc.swptr = s->dma_adc.hwptr = newptr; - s->dma_adc.count = 0; - s->dma_adc.sb_swptr = s->dma_adc.sb_hwptr = s->dma_adc.sample_buf; - __raw_writeq(DMA_DESCR, SS_CSR(R_SER_DMA_DSCR_COUNT_RX)); - } - - spin_lock(&s->lock); - cs4297a_update_ptr(s,CS_TRUE); - spin_unlock(&s->lock); - - CS_DBGOUT(CS_INTERRUPT, 6, printk(KERN_INFO - "cs4297a: cs4297a_interrupt()-\n")); -} - -#if 0 -static struct initvol { - int mixch; - int vol; -} initvol[] __initdata = { - - {SOUND_MIXER_WRITE_VOLUME, 0x4040}, - {SOUND_MIXER_WRITE_PCM, 0x4040}, - {SOUND_MIXER_WRITE_SYNTH, 0x4040}, - {SOUND_MIXER_WRITE_CD, 0x4040}, - {SOUND_MIXER_WRITE_LINE, 0x4040}, - {SOUND_MIXER_WRITE_LINE1, 0x4040}, - {SOUND_MIXER_WRITE_RECLEV, 0x0000}, - {SOUND_MIXER_WRITE_SPEAKER, 0x4040}, - {SOUND_MIXER_WRITE_MIC, 0x0000} -}; -#endif - -static int __init cs4297a_init(void) -{ - struct cs4297a_state *s; - u32 pwr, id; - mm_segment_t fs; - int rval; - u64 cfg; - int mdio_val; - - CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO - "cs4297a: cs4297a_init_module()+ \n")); - - mdio_val = __raw_readq(KSEG1 + A_MAC_REGISTER(2, R_MAC_MDIO)) & - (M_MAC_MDIO_DIR|M_MAC_MDIO_OUT); - - /* Check syscfg for synchronous serial on port 1 */ - cfg = __raw_readq(KSEG1 + A_SCD_SYSTEM_CFG); - if (!(cfg & M_SYS_SER1_ENABLE)) { - __raw_writeq(cfg | M_SYS_SER1_ENABLE, KSEG1+A_SCD_SYSTEM_CFG); - cfg = __raw_readq(KSEG1 + A_SCD_SYSTEM_CFG); - if (!(cfg & M_SYS_SER1_ENABLE)) { - printk(KERN_INFO "cs4297a: serial port 1 not configured for synchronous operation\n"); - return -1; - } - - printk(KERN_INFO "cs4297a: serial port 1 switching to synchronous operation\n"); - - /* Force the codec (on SWARM) to reset by clearing - GENO, preserving MDIO (no effect on CSWARM) */ - __raw_writeq(mdio_val, KSEG1+A_MAC_REGISTER(2, R_MAC_MDIO)); - udelay(10); - } - - /* Now set GENO */ - __raw_writeq(mdio_val | M_MAC_GENC, KSEG1+A_MAC_REGISTER(2, R_MAC_MDIO)); - /* Give the codec some time to finish resetting (start the bit clock) */ - udelay(100); - - if (!(s = kzalloc(sizeof(struct cs4297a_state), GFP_KERNEL))) { - CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR - "cs4297a: probe() no memory for state struct.\n")); - return -1; - } - s->magic = CS4297a_MAGIC; - init_waitqueue_head(&s->dma_adc.wait); - init_waitqueue_head(&s->dma_dac.wait); - init_waitqueue_head(&s->dma_adc.reg_wait); - init_waitqueue_head(&s->dma_dac.reg_wait); - init_waitqueue_head(&s->open_wait); - init_waitqueue_head(&s->open_wait_adc); - init_waitqueue_head(&s->open_wait_dac); - mutex_init(&s->open_sem_adc); - mutex_init(&s->open_sem_dac); - spin_lock_init(&s->lock); - - s->irq = K_INT_SER_1; - - if (request_irq - (s->irq, cs4297a_interrupt, 0, "Crystal CS4297a", s)) { - CS_DBGOUT(CS_INIT | CS_ERROR, 1, - printk(KERN_ERR "cs4297a: irq %u in use\n", s->irq)); - goto err_irq; - } - if ((s->dev_audio = register_sound_dsp(&cs4297a_audio_fops, -1)) < - 0) { - CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR - "cs4297a: probe() register_sound_dsp() failed.\n")); - goto err_dev1; - } - if ((s->dev_mixer = register_sound_mixer(&cs4297a_mixer_fops, -1)) < - 0) { - CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR - "cs4297a: probe() register_sound_mixer() failed.\n")); - goto err_dev2; - } - - if (ser_init(s) || dma_init(s)) { - CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR - "cs4297a: ser_init failed.\n")); - goto err_dev3; - } - - do { - udelay(4000); - rval = cs4297a_read_ac97(s, AC97_POWER_CONTROL, &pwr); - } while (!rval && (pwr != 0xf)); - - if (!rval) { - char *sb1250_duart_present; - - fs = get_fs(); - set_fs(KERNEL_DS); -#if 0 - val = SOUND_MASK_LINE; - mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long) &val); - for (i = 0; i < ARRAY_SIZE(initvol); i++) { - val = initvol[i].vol; - mixer_ioctl(s, initvol[i].mixch, (unsigned long) &val); - } -// cs4297a_write_ac97(s, 0x18, 0x0808); -#else - // cs4297a_write_ac97(s, 0x5e, 0x180); - cs4297a_write_ac97(s, 0x02, 0x0808); - cs4297a_write_ac97(s, 0x18, 0x0808); -#endif - set_fs(fs); - - list_add(&s->list, &cs4297a_devs); - - cs4297a_read_ac97(s, AC97_VENDOR_ID1, &id); - - sb1250_duart_present = symbol_get(sb1250_duart_present); - if (sb1250_duart_present) - sb1250_duart_present[1] = 0; - - printk(KERN_INFO "cs4297a: initialized (vendor id = %x)\n", id); - - CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, - printk(KERN_INFO "cs4297a: cs4297a_init_module()-\n")); - - return 0; - } - - err_dev3: - unregister_sound_mixer(s->dev_mixer); - err_dev2: - unregister_sound_dsp(s->dev_audio); - err_dev1: - free_irq(s->irq, s); - err_irq: - kfree(s); - - printk(KERN_INFO "cs4297a: initialization failed\n"); - - return -1; -} - -static void __exit cs4297a_cleanup(void) -{ - /* - XXXKW - disable_irq, free_irq - drain DMA queue - disable DMA - disable TX/RX - free memory - */ - CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, - printk(KERN_INFO "cs4297a: cleanup_cs4297a() finished\n")); -} - -// --------------------------------------------------------------------- - -MODULE_AUTHOR("Kip Walker, Broadcom Corp."); -MODULE_DESCRIPTION("Cirrus Logic CS4297a Driver for Broadcom SWARM board"); - -// --------------------------------------------------------------------- - -module_init(cs4297a_init); -module_exit(cs4297a_cleanup); diff --git a/sound/oss/sys_timer.c b/sound/oss/sys_timer.c deleted file mode 100644 index 8a4b5625dba6..000000000000 --- a/sound/oss/sys_timer.c +++ /dev/null @@ -1,280 +0,0 @@ -/* - * sound/oss/sys_timer.c - * - * The default timer for the Level 2 sequencer interface - * Uses the (1/HZ sec) timer of kernel. - */ -/* - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - */ -/* - * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) - * Andrew Veliath : adapted tmr2ticks from level 1 sequencer (avoid overflow) - */ -#include <linux/spinlock.h> -#include "sound_config.h" - -static volatile int opened, tmr_running; -static volatile unsigned int tmr_offs, tmr_ctr; -static volatile unsigned long ticks_offs; -static volatile int curr_tempo, curr_timebase; -static volatile unsigned long curr_ticks; -static volatile unsigned long next_event_time; -static unsigned long prev_event_time; - -static void poll_def_tmr(unsigned long dummy); -static DEFINE_SPINLOCK(lock); -static DEFINE_TIMER(def_tmr, poll_def_tmr); - -static unsigned long -tmr2ticks(int tmr_value) -{ - /* - * Convert timer ticks to MIDI ticks - */ - - unsigned long tmp; - unsigned long scale; - - /* tmr_value (ticks per sec) * - 1000000 (usecs per sec) / HZ (ticks per sec) -=> usecs */ - tmp = tmr_value * (1000000 / HZ); - scale = (60 * 1000000) / (curr_tempo * curr_timebase); /* usecs per MIDI tick */ - return (tmp + scale / 2) / scale; -} - -static void -poll_def_tmr(unsigned long dummy) -{ - if (!opened) - return; - def_tmr.expires = (1) + jiffies; - add_timer(&def_tmr); - - if (!tmr_running) - return; - - spin_lock(&lock); - tmr_ctr++; - curr_ticks = ticks_offs + tmr2ticks(tmr_ctr); - - if (curr_ticks >= next_event_time) { - next_event_time = (unsigned long) -1; - sequencer_timer(0); - } - - spin_unlock(&lock); -} - -static void -tmr_reset(void) -{ - unsigned long flags; - - spin_lock_irqsave(&lock,flags); - tmr_offs = 0; - ticks_offs = 0; - tmr_ctr = 0; - next_event_time = (unsigned long) -1; - prev_event_time = 0; - curr_ticks = 0; - spin_unlock_irqrestore(&lock,flags); -} - -static int -def_tmr_open(int dev, int mode) -{ - if (opened) - return -EBUSY; - - tmr_reset(); - curr_tempo = 60; - curr_timebase = 100; - opened = 1; - { - def_tmr.expires = (1) + jiffies; - add_timer(&def_tmr); - } - - return 0; -} - -static void -def_tmr_close(int dev) -{ - opened = tmr_running = 0; - del_timer(&def_tmr); -} - -static int -def_tmr_event(int dev, unsigned char *event) -{ - unsigned char cmd = event[1]; - unsigned long parm = *(int *) &event[4]; - - switch (cmd) - { - case TMR_WAIT_REL: - parm += prev_event_time; - case TMR_WAIT_ABS: - if (parm > 0) - { - long time; - - if (parm <= curr_ticks) /* It's the time */ - return TIMER_NOT_ARMED; - - time = parm; - next_event_time = prev_event_time = time; - - return TIMER_ARMED; - } - break; - - case TMR_START: - tmr_reset(); - tmr_running = 1; - break; - - case TMR_STOP: - tmr_running = 0; - break; - - case TMR_CONTINUE: - tmr_running = 1; - break; - - case TMR_TEMPO: - if (parm) - { - if (parm < 8) - parm = 8; - if (parm > 360) - parm = 360; - tmr_offs = tmr_ctr; - ticks_offs += tmr2ticks(tmr_ctr); - tmr_ctr = 0; - curr_tempo = parm; - } - break; - - case TMR_ECHO: - seq_copy_to_input(event, 8); - break; - - default:; - } - - return TIMER_NOT_ARMED; -} - -static unsigned long -def_tmr_get_time(int dev) -{ - if (!opened) - return 0; - - return curr_ticks; -} - -/* same as sound_timer.c:timer_ioctl!? */ -static int def_tmr_ioctl(int dev, unsigned int cmd, void __user *arg) -{ - int __user *p = arg; - int val; - - switch (cmd) { - case SNDCTL_TMR_SOURCE: - return __put_user(TMR_INTERNAL, p); - - case SNDCTL_TMR_START: - tmr_reset(); - tmr_running = 1; - return 0; - - case SNDCTL_TMR_STOP: - tmr_running = 0; - return 0; - - case SNDCTL_TMR_CONTINUE: - tmr_running = 1; - return 0; - - case SNDCTL_TMR_TIMEBASE: - if (__get_user(val, p)) - return -EFAULT; - if (val) { - if (val < 1) - val = 1; - if (val > 1000) - val = 1000; - curr_timebase = val; - } - return __put_user(curr_timebase, p); - - case SNDCTL_TMR_TEMPO: - if (__get_user(val, p)) - return -EFAULT; - if (val) { - if (val < 8) - val = 8; - if (val > 250) - val = 250; - tmr_offs = tmr_ctr; - ticks_offs += tmr2ticks(tmr_ctr); - tmr_ctr = 0; - curr_tempo = val; - reprogram_timer(); - } - return __put_user(curr_tempo, p); - - case SNDCTL_SEQ_CTRLRATE: - if (__get_user(val, p)) - return -EFAULT; - if (val != 0) /* Can't change */ - return -EINVAL; - val = ((curr_tempo * curr_timebase) + 30) / 60; - return __put_user(val, p); - - case SNDCTL_SEQ_GETTIME: - return __put_user(curr_ticks, p); - - case SNDCTL_TMR_METRONOME: - /* NOP */ - break; - - default:; - } - return -EINVAL; -} - -static void -def_tmr_arm(int dev, long time) -{ - if (time < 0) - time = curr_ticks + 1; - else if (time <= curr_ticks) /* It's the time */ - return; - - next_event_time = prev_event_time = time; - - return; -} - -struct sound_timer_operations default_sound_timer = -{ - .owner = THIS_MODULE, - .info = {"System clock", 0}, - .priority = 0, /* Priority */ - .devlink = 0, /* Local device link */ - .open = def_tmr_open, - .close = def_tmr_close, - .event = def_tmr_event, - .get_time = def_tmr_get_time, - .ioctl = def_tmr_ioctl, - .arm_timer = def_tmr_arm -}; diff --git a/sound/oss/trix.c b/sound/oss/trix.c deleted file mode 100644 index a57bc635d758..000000000000 --- a/sound/oss/trix.c +++ /dev/null @@ -1,525 +0,0 @@ -/* - * sound/oss/trix.c - * - * Low level driver for the MediaTrix AudioTrix Pro - * (MT-0002-PC Control Chip) - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * Changes - * Alan Cox Modularisation, cleanup. - * Christoph Hellwig Adapted to module_init/module_exit - * Arnaldo C. de Melo Got rid of attach_uart401 - */ - -#include <linux/init.h> -#include <linux/module.h> - -#include "sound_config.h" -#include "sb.h" -#include "sound_firmware.h" - -#include "ad1848.h" -#include "mpu401.h" - -#include "trix_boot.h" - -static int mpu; - -static bool joystick; - -static unsigned char trix_read(int addr) -{ - outb(((unsigned char) addr), 0x390); /* MT-0002-PC ASIC address */ - return inb(0x391); /* MT-0002-PC ASIC data */ -} - -static void trix_write(int addr, int data) -{ - outb(((unsigned char) addr), 0x390); /* MT-0002-PC ASIC address */ - outb(((unsigned char) data), 0x391); /* MT-0002-PC ASIC data */ -} - -static void download_boot(int base) -{ - int i = 0, n = trix_boot_len; - - if (trix_boot_len == 0) - return; - - trix_write(0xf8, 0x00); /* ??????? */ - outb((0x01), base + 6); /* Clear the internal data pointer */ - outb((0x00), base + 6); /* Restart */ - - /* - * Write the boot code to the RAM upload/download register. - * Each write increments the internal data pointer. - */ - outb((0x01), base + 6); /* Clear the internal data pointer */ - outb((0x1A), 0x390); /* Select RAM download/upload port */ - - for (i = 0; i < n; i++) - outb((trix_boot[i]), 0x391); - for (i = n; i < 10016; i++) /* Clear up to first 16 bytes of data RAM */ - outb((0x00), 0x391); - outb((0x00), base + 6); /* Reset */ - outb((0x50), 0x390); /* ?????? */ - -} - -static int trix_set_wss_port(struct address_info *hw_config) -{ - unsigned char addr_bits; - - if (trix_read(0x15) != 0x71) /* No ASIC signature */ - { - MDB(printk(KERN_ERR "No AudioTrix ASIC signature found\n")); - return 0; - } - - /* - * Reset some registers. - */ - - trix_write(0x13, 0); - trix_write(0x14, 0); - - /* - * Configure the ASIC to place the codec to the proper I/O location - */ - - switch (hw_config->io_base) - { - case 0x530: - addr_bits = 0; - break; - case 0x604: - addr_bits = 1; - break; - case 0xE80: - addr_bits = 2; - break; - case 0xF40: - addr_bits = 3; - break; - default: - return 0; - } - - trix_write(0x19, (trix_read(0x19) & 0x03) | addr_bits); - return 1; -} - -/* - * Probe and attach routines for the Windows Sound System mode of - * AudioTrix Pro - */ - -static int __init init_trix_wss(struct address_info *hw_config) -{ - static unsigned char dma_bits[4] = { - 1, 2, 0, 3 - }; - struct resource *ports; - int config_port = hw_config->io_base + 0; - int dma1 = hw_config->dma, dma2 = hw_config->dma2; - int old_num_mixers = num_mixers; - u8 config, bits; - int ret; - - switch(hw_config->irq) { - case 7: - bits = 8; - break; - case 9: - bits = 0x10; - break; - case 10: - bits = 0x18; - break; - case 11: - bits = 0x20; - break; - default: - printk(KERN_ERR "AudioTrix: Bad WSS IRQ %d\n", hw_config->irq); - return 0; - } - - switch (dma1) { - case 0: - case 1: - case 3: - break; - default: - printk(KERN_ERR "AudioTrix: Bad WSS DMA %d\n", dma1); - return 0; - } - - switch (dma2) { - case -1: - case 0: - case 1: - case 3: - break; - default: - printk(KERN_ERR "AudioTrix: Bad capture DMA %d\n", dma2); - return 0; - } - - /* - * Check if the IO port returns valid signature. The original MS Sound - * system returns 0x04 while some cards (AudioTrix Pro for example) - * return 0x00. - */ - ports = request_region(hw_config->io_base + 4, 4, "ad1848"); - if (!ports) { - printk(KERN_ERR "AudioTrix: MSS I/O port conflict (%x)\n", hw_config->io_base); - return 0; - } - - if (!request_region(hw_config->io_base, 4, "MSS config")) { - printk(KERN_ERR "AudioTrix: MSS I/O port conflict (%x)\n", hw_config->io_base); - release_region(hw_config->io_base + 4, 4); - return 0; - } - - if (!trix_set_wss_port(hw_config)) - goto fail; - - config = inb(hw_config->io_base + 3); - - if ((config & 0x3f) != 0x00) - { - MDB(printk(KERN_ERR "No MSS signature detected on port 0x%x\n", hw_config->io_base)); - goto fail; - } - - /* - * Check that DMA0 is not in use with a 8 bit board. - */ - - if (dma1 == 0 && config & 0x80) - { - printk(KERN_ERR "AudioTrix: Can't use DMA0 with a 8 bit card slot\n"); - goto fail; - } - if (hw_config->irq > 9 && config & 0x80) - { - printk(KERN_ERR "AudioTrix: Can't use IRQ%d with a 8 bit card slot\n", hw_config->irq); - goto fail; - } - - ret = ad1848_detect(ports, NULL, hw_config->osp); - if (!ret) - goto fail; - - if (joystick==1) - trix_write(0x15, 0x80); - - /* - * Set the IRQ and DMA addresses. - */ - - outb((bits | 0x40), config_port); - - if (dma2 == -1 || dma2 == dma1) - { - bits |= dma_bits[dma1]; - dma2 = dma1; - } - else - { - unsigned char tmp; - - tmp = trix_read(0x13) & ~30; - trix_write(0x13, tmp | 0x80 | (dma1 << 4)); - - tmp = trix_read(0x14) & ~30; - trix_write(0x14, tmp | 0x80 | (dma2 << 4)); - } - - outb((bits), config_port); /* Write IRQ+DMA setup */ - - hw_config->slots[0] = ad1848_init("AudioTrix Pro", ports, - hw_config->irq, - dma1, - dma2, - 0, - hw_config->osp, - THIS_MODULE); - - if (num_mixers > old_num_mixers) /* Mixer got installed */ - { - AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_LINE); /* Line in */ - AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_CD); - AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH); /* OPL4 */ - AD1848_REROUTE(SOUND_MIXER_SPEAKER, SOUND_MIXER_ALTPCM); /* SB */ - } - return 1; - -fail: - release_region(hw_config->io_base, 4); - release_region(hw_config->io_base + 4, 4); - return 0; -} - -static int __init probe_trix_sb(struct address_info *hw_config) -{ - - int tmp; - unsigned char conf; - extern int sb_be_quiet; - int old_quiet; - static signed char irq_translate[] = { - -1, -1, -1, 0, 1, 2, -1, 3 - }; - - if (trix_boot_len == 0) - return 0; /* No boot code -> no fun */ - - if ((hw_config->io_base & 0xffffff8f) != 0x200) - return 0; - - tmp = hw_config->irq; - if (tmp > 7) - return 0; - if (irq_translate[tmp] == -1) - return 0; - - tmp = hw_config->dma; - if (tmp != 1 && tmp != 3) - return 0; - - if (!request_region(hw_config->io_base, 16, "soundblaster")) { - printk(KERN_ERR "AudioTrix: SB I/O port conflict (%x)\n", hw_config->io_base); - return 0; - } - - conf = 0x84; /* DMA and IRQ enable */ - conf |= hw_config->io_base & 0x70; /* I/O address bits */ - conf |= irq_translate[hw_config->irq]; - if (hw_config->dma == 3) - conf |= 0x08; - trix_write(0x1b, conf); - - download_boot(hw_config->io_base); - - hw_config->name = "AudioTrix SB"; - if (!sb_dsp_detect(hw_config, 0, 0, NULL)) { - release_region(hw_config->io_base, 16); - return 0; - } - - hw_config->driver_use_1 = SB_NO_MIDI | SB_NO_MIXER | SB_NO_RECORDING; - - /* Prevent false alarms */ - old_quiet = sb_be_quiet; - sb_be_quiet = 1; - - sb_dsp_init(hw_config, THIS_MODULE); - - sb_be_quiet = old_quiet; - return 1; -} - -static int __init probe_trix_mpu(struct address_info *hw_config) -{ - unsigned char conf; - static int irq_bits[] = { - -1, -1, -1, 1, 2, 3, -1, 4, -1, 5 - }; - - if (hw_config->irq > 9) - { - printk(KERN_ERR "AudioTrix: Bad MPU IRQ %d\n", hw_config->irq); - return 0; - } - if (irq_bits[hw_config->irq] == -1) - { - printk(KERN_ERR "AudioTrix: Bad MPU IRQ %d\n", hw_config->irq); - return 0; - } - switch (hw_config->io_base) - { - case 0x330: - conf = 0x00; - break; - case 0x370: - conf = 0x04; - break; - case 0x3b0: - conf = 0x08; - break; - case 0x3f0: - conf = 0x0c; - break; - default: - return 0; /* Invalid port */ - } - - conf |= irq_bits[hw_config->irq] << 4; - trix_write(0x19, (trix_read(0x19) & 0x83) | conf); - hw_config->name = "AudioTrix Pro"; - return probe_uart401(hw_config, THIS_MODULE); -} - -static void __exit unload_trix_wss(struct address_info *hw_config) -{ - int dma2 = hw_config->dma2; - - if (dma2 == -1) - dma2 = hw_config->dma; - - release_region(0x390, 2); - release_region(hw_config->io_base, 4); - - ad1848_unload(hw_config->io_base + 4, - hw_config->irq, - hw_config->dma, - dma2, - 0); - sound_unload_audiodev(hw_config->slots[0]); -} - -static inline void __exit unload_trix_mpu(struct address_info *hw_config) -{ - unload_uart401(hw_config); -} - -static inline void __exit unload_trix_sb(struct address_info *hw_config) -{ - sb_dsp_unload(hw_config, mpu); -} - -static struct address_info cfg; -static struct address_info cfg2; -static struct address_info cfg_mpu; - -static int sb; -static int fw_load; - -static int __initdata io = -1; -static int __initdata irq = -1; -static int __initdata dma = -1; -static int __initdata dma2 = -1; /* Set this for modules that need it */ -static int __initdata sb_io = -1; -static int __initdata sb_dma = -1; -static int __initdata sb_irq = -1; -static int __initdata mpu_io = -1; -static int __initdata mpu_irq = -1; - -module_param_hw(io, int, ioport, 0); -module_param_hw(irq, int, irq, 0); -module_param_hw(dma, int, dma, 0); -module_param_hw(dma2, int, dma, 0); -module_param_hw(sb_io, int, ioport, 0); -module_param_hw(sb_dma, int, dma, 0); -module_param_hw(sb_irq, int, irq, 0); -module_param_hw(mpu_io, int, ioport, 0); -module_param_hw(mpu_irq, int, irq, 0); -module_param(joystick, bool, 0); - -static int __init init_trix(void) -{ - printk(KERN_INFO "MediaTrix audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); - - cfg.io_base = io; - cfg.irq = irq; - cfg.dma = dma; - cfg.dma2 = dma2; - - cfg2.io_base = sb_io; - cfg2.irq = sb_irq; - cfg2.dma = sb_dma; - - cfg_mpu.io_base = mpu_io; - cfg_mpu.irq = mpu_irq; - - if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) { - printk(KERN_INFO "I/O, IRQ, DMA and type are mandatory\n"); - return -EINVAL; - } - - if (cfg2.io_base != -1 && (cfg2.irq == -1 || cfg2.dma == -1)) { - printk(KERN_INFO "CONFIG_SB_IRQ and CONFIG_SB_DMA must be specified if SB_IO is set.\n"); - return -EINVAL; - } - if (cfg_mpu.io_base != -1 && cfg_mpu.irq == -1) { - printk(KERN_INFO "CONFIG_MPU_IRQ must be specified if MPU_IO is set.\n"); - return -EINVAL; - } - if (!trix_boot) - { - fw_load = 1; - trix_boot_len = mod_firmware_load("/etc/sound/trxpro.bin", - (char **) &trix_boot); - } - - if (!request_region(0x390, 2, "AudioTrix")) { - printk(KERN_ERR "AudioTrix: Config port I/O conflict\n"); - return -ENODEV; - } - - if (!init_trix_wss(&cfg)) { - release_region(0x390, 2); - return -ENODEV; - } - - /* - * We must attach in the right order to get the firmware - * loaded up in time. - */ - - if (cfg2.io_base != -1) { - sb = probe_trix_sb(&cfg2); - } - - if (cfg_mpu.io_base != -1) - mpu = probe_trix_mpu(&cfg_mpu); - - return 0; -} - -static void __exit cleanup_trix(void) -{ - if (fw_load) - vfree(trix_boot); - if (sb) - unload_trix_sb(&cfg2); - if (mpu) - unload_trix_mpu(&cfg_mpu); - unload_trix_wss(&cfg); -} - -module_init(init_trix); -module_exit(cleanup_trix); - -#ifndef MODULE -static int __init setup_trix (char *str) -{ - /* io, irq, dma, dma2, sb_io, sb_irq, sb_dma, mpu_io, mpu_irq */ - int ints[9]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - dma = ints[3]; - dma2 = ints[4]; - sb_io = ints[5]; - sb_irq = ints[6]; - sb_dma = ints[6]; - mpu_io = ints[7]; - mpu_irq = ints[8]; - - return 1; -} - -__setup("trix=", setup_trix); -#endif -MODULE_LICENSE("GPL"); diff --git a/sound/oss/tuning.h b/sound/oss/tuning.h deleted file mode 100644 index 953539931237..000000000000 --- a/sound/oss/tuning.h +++ /dev/null @@ -1,24 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -static unsigned short semitone_tuning[24] = -{ -/* 0 */ 10000, 10595, 11225, 11892, 12599, 13348, 14142, 14983, -/* 8 */ 15874, 16818, 17818, 18877, 20000, 21189, 22449, 23784, -/* 16 */ 25198, 26697, 28284, 29966, 31748, 33636, 35636, 37755 -}; - -static unsigned short cent_tuning[100] = -{ -/* 0 */ 10000, 10006, 10012, 10017, 10023, 10029, 10035, 10041, -/* 8 */ 10046, 10052, 10058, 10064, 10070, 10075, 10081, 10087, -/* 16 */ 10093, 10099, 10105, 10110, 10116, 10122, 10128, 10134, -/* 24 */ 10140, 10145, 10151, 10157, 10163, 10169, 10175, 10181, -/* 32 */ 10187, 10192, 10198, 10204, 10210, 10216, 10222, 10228, -/* 40 */ 10234, 10240, 10246, 10251, 10257, 10263, 10269, 10275, -/* 48 */ 10281, 10287, 10293, 10299, 10305, 10311, 10317, 10323, -/* 56 */ 10329, 10335, 10341, 10347, 10353, 10359, 10365, 10371, -/* 64 */ 10377, 10383, 10389, 10395, 10401, 10407, 10413, 10419, -/* 72 */ 10425, 10431, 10437, 10443, 10449, 10455, 10461, 10467, -/* 80 */ 10473, 10479, 10485, 10491, 10497, 10503, 10509, 10515, -/* 88 */ 10521, 10528, 10534, 10540, 10546, 10552, 10558, 10564, -/* 96 */ 10570, 10576, 10582, 10589 -}; diff --git a/sound/oss/uart401.c b/sound/oss/uart401.c deleted file mode 100644 index 83dcc85b8688..000000000000 --- a/sound/oss/uart401.c +++ /dev/null @@ -1,477 +0,0 @@ -/* - * sound/oss/uart401.c - * - * MPU-401 UART driver (formerly uart401_midi.c) - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * Changes: - * Alan Cox Reformatted, removed sound_mem usage, use normal Linux - * interrupt allocation. Protect against bogus unload - * Fixed to allow IRQ > 15 - * Christoph Hellwig Adapted to module_init/module_exit - * Arnaldo C. de Melo got rid of check_region - * - * Status: - * Untested - */ - -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include "sound_config.h" - -#include "mpu401.h" - -struct uart401_devc -{ - int base; - int irq; - int *osp; - void (*midi_input_intr) (int dev, unsigned char data); - int opened, disabled; - volatile unsigned char input_byte; - int my_dev; - int share_irq; - spinlock_t lock; -}; - -#define DATAPORT (devc->base) -#define COMDPORT (devc->base+1) -#define STATPORT (devc->base+1) - -static int uart401_status(struct uart401_devc *devc) -{ - return inb(STATPORT); -} - -#define input_avail(devc) (!(uart401_status(devc)&INPUT_AVAIL)) -#define output_ready(devc) (!(uart401_status(devc)&OUTPUT_READY)) - -static void uart401_cmd(struct uart401_devc *devc, unsigned char cmd) -{ - outb((cmd), COMDPORT); -} - -static int uart401_read(struct uart401_devc *devc) -{ - return inb(DATAPORT); -} - -static void uart401_write(struct uart401_devc *devc, unsigned char byte) -{ - outb((byte), DATAPORT); -} - -#define OUTPUT_READY 0x40 -#define INPUT_AVAIL 0x80 -#define MPU_ACK 0xFE -#define MPU_RESET 0xFF -#define UART_MODE_ON 0x3F - -static int reset_uart401(struct uart401_devc *devc); -static void enter_uart_mode(struct uart401_devc *devc); - -static void uart401_input_loop(struct uart401_devc *devc) -{ - int work_limit=30000; - - while (input_avail(devc) && --work_limit) - { - unsigned char c = uart401_read(devc); - - if (c == MPU_ACK) - devc->input_byte = c; - else if (devc->opened & OPEN_READ && devc->midi_input_intr) - devc->midi_input_intr(devc->my_dev, c); - } - if(work_limit==0) - printk(KERN_WARNING "Too much work in interrupt on uart401 (0x%X). UART jabbering ??\n", devc->base); -} - -irqreturn_t uart401intr(int irq, void *dev_id) -{ - struct uart401_devc *devc = dev_id; - - if (devc == NULL) - { - printk(KERN_ERR "uart401: bad devc\n"); - return IRQ_NONE; - } - - if (input_avail(devc)) - uart401_input_loop(devc); - return IRQ_HANDLED; -} - -static int -uart401_open(int dev, int mode, - void (*input) (int dev, unsigned char data), - void (*output) (int dev) -) -{ - struct uart401_devc *devc = (struct uart401_devc *) - midi_devs[dev]->devc; - - if (devc->opened) - return -EBUSY; - - /* Flush the UART */ - - while (input_avail(devc)) - uart401_read(devc); - - devc->midi_input_intr = input; - devc->opened = mode; - enter_uart_mode(devc); - devc->disabled = 0; - - return 0; -} - -static void uart401_close(int dev) -{ - struct uart401_devc *devc = (struct uart401_devc *) - midi_devs[dev]->devc; - - reset_uart401(devc); - devc->opened = 0; -} - -static int uart401_out(int dev, unsigned char midi_byte) -{ - int timeout; - unsigned long flags; - struct uart401_devc *devc = (struct uart401_devc *) - midi_devs[dev]->devc; - - if (devc->disabled) - return 1; - /* - * Test for input since pending input seems to block the output. - */ - - spin_lock_irqsave(&devc->lock,flags); - if (input_avail(devc)) - uart401_input_loop(devc); - - spin_unlock_irqrestore(&devc->lock,flags); - - /* - * Sometimes it takes about 13000 loops before the output becomes ready - * (After reset). Normally it takes just about 10 loops. - */ - - for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--); - - if (!output_ready(devc)) - { - printk(KERN_WARNING "uart401: Timeout - Device not responding\n"); - devc->disabled = 1; - reset_uart401(devc); - enter_uart_mode(devc); - return 1; - } - uart401_write(devc, midi_byte); - return 1; -} - -static inline int uart401_start_read(int dev) -{ - return 0; -} - -static inline int uart401_end_read(int dev) -{ - return 0; -} - -static inline void uart401_kick(int dev) -{ -} - -static inline int uart401_buffer_status(int dev) -{ - return 0; -} - -#define MIDI_SYNTH_NAME "MPU-401 UART" -#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT -#include "midi_synth.h" - -static const struct midi_operations uart401_operations = -{ - .owner = THIS_MODULE, - .info = {"MPU-401 (UART) MIDI", 0, 0, SNDCARD_MPU401}, - .converter = &std_midi_synth, - .in_info = {0}, - .open = uart401_open, - .close = uart401_close, - .outputc = uart401_out, - .start_read = uart401_start_read, - .end_read = uart401_end_read, - .kick = uart401_kick, - .buffer_status = uart401_buffer_status, -}; - -static void enter_uart_mode(struct uart401_devc *devc) -{ - int ok, timeout; - unsigned long flags; - - spin_lock_irqsave(&devc->lock,flags); - for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--); - - devc->input_byte = 0; - uart401_cmd(devc, UART_MODE_ON); - - ok = 0; - for (timeout = 50000; timeout > 0 && !ok; timeout--) - if (devc->input_byte == MPU_ACK) - ok = 1; - else if (input_avail(devc)) - if (uart401_read(devc) == MPU_ACK) - ok = 1; - - spin_unlock_irqrestore(&devc->lock,flags); -} - -static int reset_uart401(struct uart401_devc *devc) -{ - int ok, timeout, n; - - /* - * Send the RESET command. Try again if no success at the first time. - */ - - ok = 0; - - for (n = 0; n < 2 && !ok; n++) - { - for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--); - devc->input_byte = 0; - uart401_cmd(devc, MPU_RESET); - - /* - * Wait at least 25 msec. This method is not accurate so let's make the - * loop bit longer. Cannot sleep since this is called during boot. - */ - - for (timeout = 50000; timeout > 0 && !ok; timeout--) - { - if (devc->input_byte == MPU_ACK) /* Interrupt */ - ok = 1; - else if (input_avail(devc)) - { - if (uart401_read(devc) == MPU_ACK) - ok = 1; - } - } - } - - /* Flush input before enabling interrupts */ - if (ok) - uart401_input_loop(devc); - else - DDB(printk("Reset UART401 failed - No hardware detected.\n")); - - return ok; -} - -int probe_uart401(struct address_info *hw_config, struct module *owner) -{ - struct uart401_devc *devc; - char *name = "MPU-401 (UART) MIDI"; - int ok = 0; - unsigned long flags; - - DDB(printk("Entered probe_uart401()\n")); - - /* Default to "not found" */ - hw_config->slots[4] = -1; - - if (!request_region(hw_config->io_base, 4, "MPU-401 UART")) { - printk(KERN_INFO "uart401: could not request_region(%d, 4)\n", hw_config->io_base); - return 0; - } - - devc = kmalloc(sizeof(struct uart401_devc), GFP_KERNEL); - if (!devc) { - printk(KERN_WARNING "uart401: Can't allocate memory\n"); - goto cleanup_region; - } - - devc->base = hw_config->io_base; - devc->irq = hw_config->irq; - devc->osp = hw_config->osp; - devc->midi_input_intr = NULL; - devc->opened = 0; - devc->input_byte = 0; - devc->my_dev = 0; - devc->share_irq = 0; - spin_lock_init(&devc->lock); - - spin_lock_irqsave(&devc->lock,flags); - ok = reset_uart401(devc); - spin_unlock_irqrestore(&devc->lock,flags); - - if (!ok) - goto cleanup_devc; - - if (hw_config->name) - name = hw_config->name; - - if (devc->irq < 0) { - devc->share_irq = 1; - devc->irq *= -1; - } else - devc->share_irq = 0; - - if (!devc->share_irq) - if (request_irq(devc->irq, uart401intr, 0, "MPU-401 UART", devc) < 0) { - printk(KERN_WARNING "uart401: Failed to allocate IRQ%d\n", devc->irq); - devc->share_irq = 1; - } - devc->my_dev = sound_alloc_mididev(); - enter_uart_mode(devc); - - if (devc->my_dev == -1) { - printk(KERN_INFO "uart401: Too many midi devices detected\n"); - goto cleanup_irq; - } - conf_printf(name, hw_config); - midi_devs[devc->my_dev] = kmemdup(&uart401_operations, - sizeof(struct midi_operations), - GFP_KERNEL); - if (!midi_devs[devc->my_dev]) { - printk(KERN_ERR "uart401: Failed to allocate memory\n"); - goto cleanup_unload_mididev; - } - - if (owner) - midi_devs[devc->my_dev]->owner = owner; - - midi_devs[devc->my_dev]->devc = devc; - midi_devs[devc->my_dev]->converter = kmemdup(&std_midi_synth, - sizeof(struct synth_operations), - GFP_KERNEL); - - if (!midi_devs[devc->my_dev]->converter) { - printk(KERN_WARNING "uart401: Failed to allocate memory\n"); - goto cleanup_midi_devs; - } - strcpy(midi_devs[devc->my_dev]->info.name, name); - midi_devs[devc->my_dev]->converter->id = "UART401"; - midi_devs[devc->my_dev]->converter->midi_dev = devc->my_dev; - - if (owner) - midi_devs[devc->my_dev]->converter->owner = owner; - - hw_config->slots[4] = devc->my_dev; - sequencer_init(); - devc->opened = 0; - return 1; -cleanup_midi_devs: - kfree(midi_devs[devc->my_dev]); -cleanup_unload_mididev: - sound_unload_mididev(devc->my_dev); -cleanup_irq: - if (!devc->share_irq) - free_irq(devc->irq, devc); -cleanup_devc: - kfree(devc); -cleanup_region: - release_region(hw_config->io_base, 4); - return 0; -} - -void unload_uart401(struct address_info *hw_config) -{ - struct uart401_devc *devc; - int n=hw_config->slots[4]; - - /* Not set up */ - if(n==-1 || midi_devs[n]==NULL) - return; - - /* Not allocated (erm ??) */ - - devc = midi_devs[hw_config->slots[4]]->devc; - if (devc == NULL) - return; - - reset_uart401(devc); - release_region(hw_config->io_base, 4); - - if (!devc->share_irq) - free_irq(devc->irq, devc); - kfree(midi_devs[devc->my_dev]->converter); - kfree(midi_devs[devc->my_dev]); - kfree(devc); - - /* This kills midi_devs[x] */ - sound_unload_mididev(hw_config->slots[4]); -} - -EXPORT_SYMBOL(probe_uart401); -EXPORT_SYMBOL(unload_uart401); -EXPORT_SYMBOL(uart401intr); - -static struct address_info cfg_mpu; - -static int io = -1; -static int irq = -1; - -module_param_hw(io, int, ioport, 0444); -module_param_hw(irq, int, irq, 0444); - - -static int __init init_uart401(void) -{ - cfg_mpu.irq = irq; - cfg_mpu.io_base = io; - - /* Can be loaded either for module use or to provide functions - to others */ - if (cfg_mpu.io_base != -1 && cfg_mpu.irq != -1) { - printk(KERN_INFO "MPU-401 UART driver Copyright (C) Hannu Savolainen 1993-1997"); - if (!probe_uart401(&cfg_mpu, THIS_MODULE)) - return -ENODEV; - } - - return 0; -} - -static void __exit cleanup_uart401(void) -{ - if (cfg_mpu.io_base != -1 && cfg_mpu.irq != -1) - unload_uart401(&cfg_mpu); -} - -module_init(init_uart401); -module_exit(cleanup_uart401); - -#ifndef MODULE -static int __init setup_uart401(char *str) -{ - /* io, irq */ - int ints[3]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - - return 1; -} - -__setup("uart401=", setup_uart401); -#endif -MODULE_LICENSE("GPL"); diff --git a/sound/oss/uart6850.c b/sound/oss/uart6850.c deleted file mode 100644 index a9d3f7568525..000000000000 --- a/sound/oss/uart6850.c +++ /dev/null @@ -1,361 +0,0 @@ -/* - * sound/oss/uart6850.c - * - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * Extended by Alan Cox for Red Hat Software. Now a loadable MIDI driver. - * 28/4/97 - (C) Copyright Alan Cox. Released under the GPL version 2. - * - * Alan Cox: Updated for new modular code. Removed snd_* irq handling. Now - * uses native linux resources - * Christoph Hellwig: Adapted to module_init/module_exit - * Jeff Garzik: Made it work again, in theory - * FIXME: If the request_irq() succeeds, the probe succeeds. Ug. - * - * Status: Testing required (no shit -jgarzik) - * - * - */ - -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/module.h> -#include <linux/spinlock.h> -/* Mon Nov 22 22:38:35 MET 1993 marco@driq.home.usn.nl: - * added 6850 support, used with COVOX SoundMaster II and custom cards. - */ - -#include "sound_config.h" - -static int uart6850_base = 0x330; - -static int *uart6850_osp; - -#define DATAPORT (uart6850_base) -#define COMDPORT (uart6850_base+1) -#define STATPORT (uart6850_base+1) - -static int uart6850_status(void) -{ - return inb(STATPORT); -} - -#define input_avail() (uart6850_status()&INPUT_AVAIL) -#define output_ready() (uart6850_status()&OUTPUT_READY) - -static void uart6850_cmd(unsigned char cmd) -{ - outb(cmd, COMDPORT); -} - -static int uart6850_read(void) -{ - return inb(DATAPORT); -} - -static void uart6850_write(unsigned char byte) -{ - outb(byte, DATAPORT); -} - -#define OUTPUT_READY 0x02 /* Mask for data ready Bit */ -#define INPUT_AVAIL 0x01 /* Mask for Data Send Ready Bit */ - -#define UART_RESET 0x95 -#define UART_MODE_ON 0x03 - -static int uart6850_opened; -static int uart6850_irq; -static int uart6850_detected; -static int my_dev; -static DEFINE_SPINLOCK(lock); - -static void (*midi_input_intr) (int dev, unsigned char data); -static void poll_uart6850(unsigned long dummy); - - -static DEFINE_TIMER(uart6850_timer, poll_uart6850); - -static void uart6850_input_loop(void) -{ - int count = 10; - - while (count) - { - /* - * Not timed out - */ - if (input_avail()) - { - unsigned char c = uart6850_read(); - count = 100; - if (uart6850_opened & OPEN_READ) - midi_input_intr(my_dev, c); - } - else - { - while (!input_avail() && count) - count--; - } - } -} - -static irqreturn_t m6850intr(int irq, void *dev_id) -{ - if (input_avail()) - uart6850_input_loop(); - return IRQ_HANDLED; -} - -/* - * It looks like there is no input interrupts in the UART mode. Let's try - * polling. - */ - -static void poll_uart6850(unsigned long dummy) -{ - unsigned long flags; - - if (!(uart6850_opened & OPEN_READ)) - return; /* Device has been closed */ - - spin_lock_irqsave(&lock,flags); - if (input_avail()) - uart6850_input_loop(); - - uart6850_timer.expires = 1 + jiffies; - add_timer(&uart6850_timer); - - /* - * Come back later - */ - - spin_unlock_irqrestore(&lock,flags); -} - -static int uart6850_open(int dev, int mode, - void (*input) (int dev, unsigned char data), - void (*output) (int dev) -) -{ - if (uart6850_opened) - { -/* printk("Midi6850: Midi busy\n");*/ - return -EBUSY; - } - - uart6850_cmd(UART_RESET); - uart6850_input_loop(); - midi_input_intr = input; - uart6850_opened = mode; - poll_uart6850(0); /* - * Enable input polling - */ - - return 0; -} - -static void uart6850_close(int dev) -{ - uart6850_cmd(UART_MODE_ON); - del_timer(&uart6850_timer); - uart6850_opened = 0; -} - -static int uart6850_out(int dev, unsigned char midi_byte) -{ - int timeout; - unsigned long flags; - - /* - * Test for input since pending input seems to block the output. - */ - - spin_lock_irqsave(&lock,flags); - - if (input_avail()) - uart6850_input_loop(); - - spin_unlock_irqrestore(&lock,flags); - - /* - * Sometimes it takes about 13000 loops before the output becomes ready - * (After reset). Normally it takes just about 10 loops. - */ - - for (timeout = 30000; timeout > 0 && !output_ready(); timeout--); /* - * Wait - */ - if (!output_ready()) - { - printk(KERN_WARNING "Midi6850: Timeout\n"); - return 0; - } - uart6850_write(midi_byte); - return 1; -} - -static inline int uart6850_command(int dev, unsigned char *midi_byte) -{ - return 1; -} - -static inline int uart6850_start_read(int dev) -{ - return 0; -} - -static inline int uart6850_end_read(int dev) -{ - return 0; -} - -static inline void uart6850_kick(int dev) -{ -} - -static inline int uart6850_buffer_status(int dev) -{ - return 0; /* - * No data in buffers - */ -} - -#define MIDI_SYNTH_NAME "6850 UART Midi" -#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT -#include "midi_synth.h" - -static struct midi_operations uart6850_operations = -{ - .owner = THIS_MODULE, - .info = {"6850 UART", 0, 0, SNDCARD_UART6850}, - .converter = &std_midi_synth, - .in_info = {0}, - .open = uart6850_open, - .close = uart6850_close, - .outputc = uart6850_out, - .start_read = uart6850_start_read, - .end_read = uart6850_end_read, - .kick = uart6850_kick, - .command = uart6850_command, - .buffer_status = uart6850_buffer_status -}; - - -static void __init attach_uart6850(struct address_info *hw_config) -{ - int ok, timeout; - unsigned long flags; - - if (!uart6850_detected) - return; - - if ((my_dev = sound_alloc_mididev()) == -1) - { - printk(KERN_INFO "uart6850: Too many midi devices detected\n"); - return; - } - uart6850_base = hw_config->io_base; - uart6850_osp = hw_config->osp; - uart6850_irq = hw_config->irq; - - spin_lock_irqsave(&lock,flags); - - for (timeout = 30000; timeout > 0 && !output_ready(); timeout--); /* - * Wait - */ - uart6850_cmd(UART_MODE_ON); - ok = 1; - spin_unlock_irqrestore(&lock,flags); - - conf_printf("6850 Midi Interface", hw_config); - - std_midi_synth.midi_dev = my_dev; - hw_config->slots[4] = my_dev; - midi_devs[my_dev] = &uart6850_operations; - sequencer_init(); -} - -static inline int reset_uart6850(void) -{ - uart6850_read(); - return 1; /* - * OK - */ -} - -static int __init probe_uart6850(struct address_info *hw_config) -{ - int ok; - - uart6850_osp = hw_config->osp; - uart6850_base = hw_config->io_base; - uart6850_irq = hw_config->irq; - - if (request_irq(uart6850_irq, m6850intr, 0, "MIDI6850", NULL) < 0) - return 0; - - ok = reset_uart6850(); - uart6850_detected = ok; - return ok; -} - -static void __exit unload_uart6850(struct address_info *hw_config) -{ - free_irq(hw_config->irq, NULL); - sound_unload_mididev(hw_config->slots[4]); -} - -static struct address_info cfg_mpu; - -static int __initdata io = -1; -static int __initdata irq = -1; - -module_param_hw(io, int, ioport, 0); -module_param_hw(irq, int, irq, 0); - -static int __init init_uart6850(void) -{ - cfg_mpu.io_base = io; - cfg_mpu.irq = irq; - - if (cfg_mpu.io_base == -1 || cfg_mpu.irq == -1) { - printk(KERN_INFO "uart6850: irq and io must be set.\n"); - return -EINVAL; - } - - if (probe_uart6850(&cfg_mpu)) - return -ENODEV; - attach_uart6850(&cfg_mpu); - - return 0; -} - -static void __exit cleanup_uart6850(void) -{ - unload_uart6850(&cfg_mpu); -} - -module_init(init_uart6850); -module_exit(cleanup_uart6850); - -#ifndef MODULE -static int __init setup_uart6850(char *str) -{ - /* io, irq */ - int ints[3]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - - return 1; -} -__setup("uart6850=", setup_uart6850); -#endif -MODULE_LICENSE("GPL"); diff --git a/sound/oss/ulaw.h b/sound/oss/ulaw.h deleted file mode 100644 index ee898a0f78ce..000000000000 --- a/sound/oss/ulaw.h +++ /dev/null @@ -1,70 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -static unsigned char ulaw_dsp[] = { - 3, 7, 11, 15, 19, 23, 27, 31, - 35, 39, 43, 47, 51, 55, 59, 63, - 66, 68, 70, 72, 74, 76, 78, 80, - 82, 84, 86, 88, 90, 92, 94, 96, - 98, 99, 100, 101, 102, 103, 104, 105, - 106, 107, 108, 109, 110, 111, 112, 113, - 113, 114, 114, 115, 115, 116, 116, 117, - 117, 118, 118, 119, 119, 120, 120, 121, - 121, 121, 122, 122, 122, 122, 123, 123, - 123, 123, 124, 124, 124, 124, 125, 125, - 125, 125, 125, 125, 126, 126, 126, 126, - 126, 126, 126, 126, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 253, 249, 245, 241, 237, 233, 229, 225, - 221, 217, 213, 209, 205, 201, 197, 193, - 190, 188, 186, 184, 182, 180, 178, 176, - 174, 172, 170, 168, 166, 164, 162, 160, - 158, 157, 156, 155, 154, 153, 152, 151, - 150, 149, 148, 147, 146, 145, 144, 143, - 143, 142, 142, 141, 141, 140, 140, 139, - 139, 138, 138, 137, 137, 136, 136, 135, - 135, 135, 134, 134, 134, 134, 133, 133, - 133, 133, 132, 132, 132, 132, 131, 131, - 131, 131, 131, 131, 130, 130, 130, 130, - 130, 130, 130, 130, 129, 129, 129, 129, - 129, 129, 129, 129, 129, 129, 129, 129, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, -}; - -static unsigned char dsp_ulaw[] = { - 0, 0, 0, 0, 0, 1, 1, 1, - 1, 2, 2, 2, 2, 3, 3, 3, - 3, 4, 4, 4, 4, 5, 5, 5, - 5, 6, 6, 6, 6, 7, 7, 7, - 7, 8, 8, 8, 8, 9, 9, 9, - 9, 10, 10, 10, 10, 11, 11, 11, - 11, 12, 12, 12, 12, 13, 13, 13, - 13, 14, 14, 14, 14, 15, 15, 15, - 15, 16, 16, 17, 17, 18, 18, 19, - 19, 20, 20, 21, 21, 22, 22, 23, - 23, 24, 24, 25, 25, 26, 26, 27, - 27, 28, 28, 29, 29, 30, 30, 31, - 31, 32, 33, 34, 35, 36, 37, 38, - 39, 40, 41, 42, 43, 44, 45, 46, - 47, 49, 51, 53, 55, 57, 59, 61, - 63, 66, 70, 74, 78, 84, 92, 104, - 254, 231, 219, 211, 205, 201, 197, 193, - 190, 188, 186, 184, 182, 180, 178, 176, - 175, 174, 173, 172, 171, 170, 169, 168, - 167, 166, 165, 164, 163, 162, 161, 160, - 159, 159, 158, 158, 157, 157, 156, 156, - 155, 155, 154, 154, 153, 153, 152, 152, - 151, 151, 150, 150, 149, 149, 148, 148, - 147, 147, 146, 146, 145, 145, 144, 144, - 143, 143, 143, 143, 142, 142, 142, 142, - 141, 141, 141, 141, 140, 140, 140, 140, - 139, 139, 139, 139, 138, 138, 138, 138, - 137, 137, 137, 137, 136, 136, 136, 136, - 135, 135, 135, 135, 134, 134, 134, 134, - 133, 133, 133, 133, 132, 132, 132, 132, - 131, 131, 131, 131, 130, 130, 130, 130, - 129, 129, 129, 129, 128, 128, 128, 128, -}; diff --git a/sound/oss/v_midi.c b/sound/oss/v_midi.c deleted file mode 100644 index fc0ba276cc8f..000000000000 --- a/sound/oss/v_midi.c +++ /dev/null @@ -1,290 +0,0 @@ -/* - * sound/oss/v_midi.c - * - * The low level driver for the Sound Blaster DS chips. - * - * - * Copyright (C) by Hannu Savolainen 1993-1996 - * - * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * ?? - * - * Changes - * Alan Cox Modularisation, changed memory allocations - * Christoph Hellwig Adapted to module_init/module_exit - * - * Status - * Untested - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include "sound_config.h" - -#include "v_midi.h" - -static vmidi_devc *v_devc[2] = { NULL, NULL}; -static int midi1,midi2; -static void *midi_mem = NULL; - -/* - * The DSP channel can be used either for input or output. Variable - * 'sb_irq_mode' will be set when the program calls read or write first time - * after open. Current version doesn't support mode changes without closing - * and reopening the device. Support for this feature may be implemented in a - * future version of this driver. - */ - - -static int v_midi_open (int dev, int mode, - void (*input) (int dev, unsigned char data), - void (*output) (int dev) -) -{ - vmidi_devc *devc = midi_devs[dev]->devc; - unsigned long flags; - - if (devc == NULL) - return -ENXIO; - - spin_lock_irqsave(&devc->lock,flags); - if (devc->opened) - { - spin_unlock_irqrestore(&devc->lock,flags); - return -EBUSY; - } - devc->opened = 1; - spin_unlock_irqrestore(&devc->lock,flags); - - devc->intr_active = 1; - - if (mode & OPEN_READ) - { - devc->input_opened = 1; - devc->midi_input_intr = input; - } - - return 0; -} - -static void v_midi_close (int dev) -{ - vmidi_devc *devc = midi_devs[dev]->devc; - unsigned long flags; - - if (devc == NULL) - return; - - spin_lock_irqsave(&devc->lock,flags); - devc->intr_active = 0; - devc->input_opened = 0; - devc->opened = 0; - spin_unlock_irqrestore(&devc->lock,flags); -} - -static int v_midi_out (int dev, unsigned char midi_byte) -{ - vmidi_devc *devc = midi_devs[dev]->devc; - vmidi_devc *pdevc; - - if (devc == NULL) - return -ENXIO; - - pdevc = midi_devs[devc->pair_mididev]->devc; - if (pdevc->input_opened > 0){ - if (MIDIbuf_avail(pdevc->my_mididev) > 500) - return 0; - pdevc->midi_input_intr (pdevc->my_mididev, midi_byte); - } - return 1; -} - -static inline int v_midi_start_read (int dev) -{ - return 0; -} - -static int v_midi_end_read (int dev) -{ - vmidi_devc *devc = midi_devs[dev]->devc; - if (devc == NULL) - return -ENXIO; - - devc->intr_active = 0; - return 0; -} - -/* why -EPERM and not -EINVAL?? */ - -static inline int v_midi_ioctl (int dev, unsigned cmd, void __user *arg) -{ - return -EPERM; -} - - -#define MIDI_SYNTH_NAME "Loopback MIDI" -#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT - -#include "midi_synth.h" - -static struct midi_operations v_midi_operations = -{ - .owner = THIS_MODULE, - .info = {"Loopback MIDI Port 1", 0, 0, SNDCARD_VMIDI}, - .converter = &std_midi_synth, - .in_info = {0}, - .open = v_midi_open, - .close = v_midi_close, - .ioctl = v_midi_ioctl, - .outputc = v_midi_out, - .start_read = v_midi_start_read, - .end_read = v_midi_end_read, -}; - -static struct midi_operations v_midi_operations2 = -{ - .owner = THIS_MODULE, - .info = {"Loopback MIDI Port 2", 0, 0, SNDCARD_VMIDI}, - .converter = &std_midi_synth, - .in_info = {0}, - .open = v_midi_open, - .close = v_midi_close, - .ioctl = v_midi_ioctl, - .outputc = v_midi_out, - .start_read = v_midi_start_read, - .end_read = v_midi_end_read, -}; - -/* - * We kmalloc just one of these - it makes life simpler and the code - * cleaner and the memory handling far more efficient - */ - -struct vmidi_memory -{ - /* Must be first */ - struct midi_operations m_ops[2]; - struct synth_operations s_ops[2]; - struct vmidi_devc v_ops[2]; -}; - -static void __init attach_v_midi (struct address_info *hw_config) -{ - struct vmidi_memory *m; - /* printk("Attaching v_midi device.....\n"); */ - - midi1 = sound_alloc_mididev(); - if (midi1 == -1) - { - printk(KERN_ERR "v_midi: Too many midi devices detected\n"); - return; - } - - m = kmalloc(sizeof(struct vmidi_memory), GFP_KERNEL); - if (m == NULL) - { - printk(KERN_WARNING "Loopback MIDI: Failed to allocate memory\n"); - sound_unload_mididev(midi1); - return; - } - - midi_mem = m; - - midi_devs[midi1] = &m->m_ops[0]; - - - midi2 = sound_alloc_mididev(); - if (midi2 == -1) - { - printk (KERN_ERR "v_midi: Too many midi devices detected\n"); - kfree(m); - sound_unload_mididev(midi1); - return; - } - - midi_devs[midi2] = &m->m_ops[1]; - - /* printk("VMIDI1: %d VMIDI2: %d\n",midi1,midi2); */ - - /* for MIDI-1 */ - v_devc[0] = &m->v_ops[0]; - memcpy ((char *) midi_devs[midi1], (char *) &v_midi_operations, - sizeof (struct midi_operations)); - - v_devc[0]->my_mididev = midi1; - v_devc[0]->pair_mididev = midi2; - v_devc[0]->opened = v_devc[0]->input_opened = 0; - v_devc[0]->intr_active = 0; - v_devc[0]->midi_input_intr = NULL; - spin_lock_init(&v_devc[0]->lock); - - midi_devs[midi1]->devc = v_devc[0]; - - midi_devs[midi1]->converter = &m->s_ops[0]; - std_midi_synth.midi_dev = midi1; - memcpy ((char *) midi_devs[midi1]->converter, (char *) &std_midi_synth, - sizeof (struct synth_operations)); - midi_devs[midi1]->converter->id = "V_MIDI 1"; - - /* for MIDI-2 */ - v_devc[1] = &m->v_ops[1]; - - memcpy ((char *) midi_devs[midi2], (char *) &v_midi_operations2, - sizeof (struct midi_operations)); - - v_devc[1]->my_mididev = midi2; - v_devc[1]->pair_mididev = midi1; - v_devc[1]->opened = v_devc[1]->input_opened = 0; - v_devc[1]->intr_active = 0; - v_devc[1]->midi_input_intr = NULL; - spin_lock_init(&v_devc[1]->lock); - - midi_devs[midi2]->devc = v_devc[1]; - midi_devs[midi2]->converter = &m->s_ops[1]; - - std_midi_synth.midi_dev = midi2; - memcpy ((char *) midi_devs[midi2]->converter, (char *) &std_midi_synth, - sizeof (struct synth_operations)); - midi_devs[midi2]->converter->id = "V_MIDI 2"; - - sequencer_init(); - /* printk("Attached v_midi device\n"); */ -} - -static inline int __init probe_v_midi(struct address_info *hw_config) -{ - return(1); /* always OK */ -} - - -static void __exit unload_v_midi(struct address_info *hw_config) -{ - sound_unload_mididev(midi1); - sound_unload_mididev(midi2); - kfree(midi_mem); -} - -static struct address_info cfg; /* dummy */ - -static int __init init_vmidi(void) -{ - printk("MIDI Loopback device driver\n"); - if (!probe_v_midi(&cfg)) - return -ENODEV; - attach_v_midi(&cfg); - - return 0; -} - -static void __exit cleanup_vmidi(void) -{ - unload_v_midi(&cfg); -} - -module_init(init_vmidi); -module_exit(cleanup_vmidi); -MODULE_LICENSE("GPL"); diff --git a/sound/oss/v_midi.h b/sound/oss/v_midi.h deleted file mode 100644 index f4fc2bed07f8..000000000000 --- a/sound/oss/v_midi.h +++ /dev/null @@ -1,15 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -typedef struct vmidi_devc { - int dev; - - /* State variables */ - int opened; - spinlock_t lock; - - /* MIDI fields */ - int my_mididev; - int pair_mididev; - int input_opened; - int intr_active; - void (*midi_input_intr) (int dev, unsigned char data); - } vmidi_devc; diff --git a/sound/oss/vidc.c b/sound/oss/vidc.c deleted file mode 100644 index 92ca5bee1860..000000000000 --- a/sound/oss/vidc.c +++ /dev/null @@ -1,557 +0,0 @@ -/* - * linux/drivers/sound/vidc.c - * - * Copyright (C) 1997-2000 by Russell King <rmk@arm.linux.org.uk> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * VIDC20 audio driver. - * - * The VIDC20 sound hardware consists of the VIDC20 itself, a DAC and a DMA - * engine. The DMA transfers fixed-format (16-bit little-endian linear) - * samples to the VIDC20, which then transfers this data serially to the - * DACs. The samplerate is controlled by the VIDC. - * - * We currently support a mixer device, but it is currently non-functional. - */ - -#include <linux/gfp.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/interrupt.h> - -#include <mach/hardware.h> -#include <asm/dma.h> -#include <asm/io.h> -#include <asm/hardware/iomd.h> -#include <asm/irq.h> - -#include "sound_config.h" -#include "vidc.h" - -#ifndef _SIOC_TYPE -#define _SIOC_TYPE(x) _IOC_TYPE(x) -#endif -#ifndef _SIOC_NR -#define _SIOC_NR(x) _IOC_NR(x) -#endif - -#define VIDC_SOUND_CLOCK (250000) -#define VIDC_SOUND_CLOCK_EXT (176400) - -/* - * When using SERIAL SOUND mode (external DAC), the number of physical - * channels is fixed at 2. - */ -static int vidc_busy; -static int vidc_adev; -static int vidc_audio_rate; -static char vidc_audio_format; -static char vidc_audio_channels; - -static unsigned char vidc_level_l[SOUND_MIXER_NRDEVICES] = { - 85, /* master */ - 50, /* bass */ - 50, /* treble */ - 0, /* synth */ - 75, /* pcm */ - 0, /* speaker */ - 100, /* ext line */ - 0, /* mic */ - 100, /* CD */ - 0, -}; - -static unsigned char vidc_level_r[SOUND_MIXER_NRDEVICES] = { - 85, /* master */ - 50, /* bass */ - 50, /* treble */ - 0, /* synth */ - 75, /* pcm */ - 0, /* speaker */ - 100, /* ext line */ - 0, /* mic */ - 100, /* CD */ - 0, -}; - -static unsigned int vidc_audio_volume_l; /* left PCM vol, 0 - 65536 */ -static unsigned int vidc_audio_volume_r; /* right PCM vol, 0 - 65536 */ - -extern void vidc_update_filler(int bits, int channels); -extern int softoss_dev; - -static void -vidc_mixer_set(int mdev, unsigned int level) -{ - unsigned int lev_l = level & 0x007f; - unsigned int lev_r = (level & 0x7f00) >> 8; - unsigned int mlev_l, mlev_r; - - if (lev_l > 100) - lev_l = 100; - if (lev_r > 100) - lev_r = 100; - -#define SCALE(lev,master) ((lev) * (master) * 65536 / 10000) - - mlev_l = vidc_level_l[SOUND_MIXER_VOLUME]; - mlev_r = vidc_level_r[SOUND_MIXER_VOLUME]; - - switch (mdev) { - case SOUND_MIXER_VOLUME: - case SOUND_MIXER_PCM: - vidc_level_l[mdev] = lev_l; - vidc_level_r[mdev] = lev_r; - - vidc_audio_volume_l = SCALE(lev_l, mlev_l); - vidc_audio_volume_r = SCALE(lev_r, mlev_r); -/*printk("VIDC: PCM vol %05X %05X\n", vidc_audio_volume_l, vidc_audio_volume_r);*/ - break; - } -#undef SCALE -} - -static int vidc_mixer_ioctl(int dev, unsigned int cmd, void __user *arg) -{ - unsigned int val; - unsigned int mdev; - - if (_SIOC_TYPE(cmd) != 'M') - return -EINVAL; - - mdev = _SIOC_NR(cmd); - - if (_SIOC_DIR(cmd) & _SIOC_WRITE) { - if (get_user(val, (unsigned int __user *)arg)) - return -EFAULT; - - if (mdev < SOUND_MIXER_NRDEVICES) - vidc_mixer_set(mdev, val); - else - return -EINVAL; - } - - /* - * Return parameters - */ - switch (mdev) { - case SOUND_MIXER_RECSRC: - val = 0; - break; - - case SOUND_MIXER_DEVMASK: - val = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH; - break; - - case SOUND_MIXER_STEREODEVS: - val = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH; - break; - - case SOUND_MIXER_RECMASK: - val = 0; - break; - - case SOUND_MIXER_CAPS: - val = 0; - break; - - default: - if (mdev < SOUND_MIXER_NRDEVICES) - val = vidc_level_l[mdev] | vidc_level_r[mdev] << 8; - else - return -EINVAL; - } - - return put_user(val, (unsigned int __user *)arg) ? -EFAULT : 0; -} - -static unsigned int vidc_audio_set_format(int dev, unsigned int fmt) -{ - switch (fmt) { - default: - fmt = AFMT_S16_LE; - case AFMT_U8: - case AFMT_S8: - case AFMT_S16_LE: - vidc_audio_format = fmt; - vidc_update_filler(vidc_audio_format, vidc_audio_channels); - case AFMT_QUERY: - break; - } - return vidc_audio_format; -} - -#define my_abs(i) ((i)<0 ? -(i) : (i)) - -static int vidc_audio_set_speed(int dev, int rate) -{ - if (rate) { - unsigned int hwctrl, hwrate, hwrate_ext, rate_int, rate_ext; - unsigned int diff_int, diff_ext; - unsigned int newsize, new2size; - - hwctrl = 0x00000003; - - /* Using internal clock */ - hwrate = (((VIDC_SOUND_CLOCK * 2) / rate) + 1) >> 1; - if (hwrate < 3) - hwrate = 3; - if (hwrate > 255) - hwrate = 255; - - /* Using exernal clock */ - hwrate_ext = (((VIDC_SOUND_CLOCK_EXT * 2) / rate) + 1) >> 1; - if (hwrate_ext < 3) - hwrate_ext = 3; - if (hwrate_ext > 255) - hwrate_ext = 255; - - rate_int = VIDC_SOUND_CLOCK / hwrate; - rate_ext = VIDC_SOUND_CLOCK_EXT / hwrate_ext; - - /* Chose between external and internal clock */ - diff_int = my_abs(rate_ext-rate); - diff_ext = my_abs(rate_int-rate); - if (diff_ext < diff_int) { - /*printk("VIDC: external %d %d %d\n", rate, rate_ext, hwrate_ext);*/ - hwrate=hwrate_ext; - hwctrl=0x00000002; - /* Allow roughly 0.4% tolerance */ - if (diff_ext > (rate/256)) - rate=rate_ext; - } else { - /*printk("VIDC: internal %d %d %d\n", rate, rate_int, hwrate);*/ - hwctrl=0x00000003; - /* Allow roughly 0.4% tolerance */ - if (diff_int > (rate/256)) - rate=rate_int; - } - - vidc_writel(0xb0000000 | (hwrate - 2)); - vidc_writel(0xb1000000 | hwctrl); - - newsize = (10000 / hwrate) & ~3; - if (newsize < 208) - newsize = 208; - if (newsize > 4096) - newsize = 4096; - for (new2size = 128; new2size < newsize; new2size <<= 1); - if (new2size - newsize > newsize - (new2size >> 1)) - new2size >>= 1; - if (new2size > 4096) { - printk(KERN_ERR "VIDC: error: dma buffer (%d) %d > 4K\n", - newsize, new2size); - new2size = 4096; - } - /*printk("VIDC: dma size %d\n", new2size);*/ - dma_bufsize = new2size; - vidc_audio_rate = rate; - } - return vidc_audio_rate; -} - -static short vidc_audio_set_channels(int dev, short channels) -{ - switch (channels) { - default: - channels = 2; - case 1: - case 2: - vidc_audio_channels = channels; - vidc_update_filler(vidc_audio_format, vidc_audio_channels); - case 0: - break; - } - return vidc_audio_channels; -} - -/* - * Open the device - */ -static int vidc_audio_open(int dev, int mode) -{ - /* This audio device does not have recording capability */ - if (mode == OPEN_READ) - return -EPERM; - - if (vidc_busy) - return -EBUSY; - - vidc_busy = 1; - return 0; -} - -/* - * Close the device - */ -static void vidc_audio_close(int dev) -{ - vidc_busy = 0; -} - -/* - * Output a block via DMA to sound device. - * - * We just set the DMA start and count; the DMA interrupt routine - * will take care of formatting the samples (via the appropriate - * vidc_filler routine), and flag via vidc_audio_dma_interrupt when - * more data is required. - */ -static void -vidc_audio_output_block(int dev, unsigned long buf, int total_count, int one) -{ - struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; - unsigned long flags; - - local_irq_save(flags); - dma_start = buf - (unsigned long)dmap->raw_buf_phys + (unsigned long)dmap->raw_buf; - dma_count = total_count; - local_irq_restore(flags); -} - -static void -vidc_audio_start_input(int dev, unsigned long buf, int count, int intrflag) -{ -} - -static int vidc_audio_prepare_for_input(int dev, int bsize, int bcount) -{ - return -EINVAL; -} - -static irqreturn_t vidc_audio_dma_interrupt(void) -{ - DMAbuf_outputintr(vidc_adev, 1); - return IRQ_HANDLED; -} - -/* - * Prepare for outputting samples. - * - * Each buffer that will be passed will be `bsize' bytes long, - * with a total of `bcount' buffers. - */ -static int vidc_audio_prepare_for_output(int dev, int bsize, int bcount) -{ - struct audio_operations *adev = audio_devs[dev]; - - dma_interrupt = NULL; - adev->dmap_out->flags |= DMA_NODMA; - - return 0; -} - -/* - * Stop our current operation. - */ -static void vidc_audio_reset(int dev) -{ - dma_interrupt = NULL; -} - -static int vidc_audio_local_qlen(int dev) -{ - return /*dma_count !=*/ 0; -} - -static void vidc_audio_trigger(int dev, int enable_bits) -{ - struct audio_operations *adev = audio_devs[dev]; - - if (enable_bits & PCM_ENABLE_OUTPUT) { - if (!(adev->dmap_out->flags & DMA_ACTIVE)) { - unsigned long flags; - - local_irq_save(flags); - - /* prevent recusion */ - adev->dmap_out->flags |= DMA_ACTIVE; - - dma_interrupt = vidc_audio_dma_interrupt; - vidc_sound_dma_irq(0, NULL); - iomd_writeb(DMA_CR_E | 0x10, IOMD_SD0CR); - - local_irq_restore(flags); - } - } -} - -static struct audio_driver vidc_audio_driver = -{ - .owner = THIS_MODULE, - .open = vidc_audio_open, - .close = vidc_audio_close, - .output_block = vidc_audio_output_block, - .start_input = vidc_audio_start_input, - .prepare_for_input = vidc_audio_prepare_for_input, - .prepare_for_output = vidc_audio_prepare_for_output, - .halt_io = vidc_audio_reset, - .local_qlen = vidc_audio_local_qlen, - .trigger = vidc_audio_trigger, - .set_speed = vidc_audio_set_speed, - .set_bits = vidc_audio_set_format, - .set_channels = vidc_audio_set_channels -}; - -static struct mixer_operations vidc_mixer_operations = { - .owner = THIS_MODULE, - .id = "VIDC", - .name = "VIDCsound", - .ioctl = vidc_mixer_ioctl -}; - -void vidc_update_filler(int format, int channels) -{ -#define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3)) - - switch (TYPE(format, channels)) { - default: - case TYPE(AFMT_U8, 1): - vidc_filler = vidc_fill_1x8_u; - break; - - case TYPE(AFMT_U8, 2): - vidc_filler = vidc_fill_2x8_u; - break; - - case TYPE(AFMT_S8, 1): - vidc_filler = vidc_fill_1x8_s; - break; - - case TYPE(AFMT_S8, 2): - vidc_filler = vidc_fill_2x8_s; - break; - - case TYPE(AFMT_S16_LE, 1): - vidc_filler = vidc_fill_1x16_s; - break; - - case TYPE(AFMT_S16_LE, 2): - vidc_filler = vidc_fill_2x16_s; - break; - } -} - -static void __init attach_vidc(struct address_info *hw_config) -{ - char name[32]; - int i, adev; - - sprintf(name, "VIDC %d-bit sound", hw_config->card_subtype); - conf_printf(name, hw_config); - memset(dma_buf, 0, sizeof(dma_buf)); - - adev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, name, - &vidc_audio_driver, sizeof(vidc_audio_driver), - DMA_AUTOMODE, AFMT_U8 | AFMT_S8 | AFMT_S16_LE, - NULL, hw_config->dma, hw_config->dma2); - - if (adev < 0) - goto audio_failed; - - /* - * 1024 bytes => 64 buffers - */ - audio_devs[adev]->min_fragment = 10; - audio_devs[adev]->mixer_dev = num_mixers; - - audio_devs[adev]->mixer_dev = - sound_install_mixer(MIXER_DRIVER_VERSION, - name, &vidc_mixer_operations, - sizeof(vidc_mixer_operations), NULL); - - if (audio_devs[adev]->mixer_dev < 0) - goto mixer_failed; - - for (i = 0; i < 2; i++) { - dma_buf[i] = get_zeroed_page(GFP_KERNEL); - if (!dma_buf[i]) { - printk(KERN_ERR "%s: can't allocate required buffers\n", - name); - goto mem_failed; - } - dma_pbuf[i] = virt_to_phys((void *)dma_buf[i]); - } - - if (sound_alloc_dma(hw_config->dma, hw_config->name)) { - printk(KERN_ERR "%s: DMA %d is in use\n", name, hw_config->dma); - goto dma_failed; - } - - if (request_irq(hw_config->irq, vidc_sound_dma_irq, 0, - hw_config->name, &dma_start)) { - printk(KERN_ERR "%s: IRQ %d is in use\n", name, hw_config->irq); - goto irq_failed; - } - vidc_adev = adev; - vidc_mixer_set(SOUND_MIXER_VOLUME, (85 | 85 << 8)); - - return; - -irq_failed: - sound_free_dma(hw_config->dma); -dma_failed: -mem_failed: - for (i = 0; i < 2; i++) - free_page(dma_buf[i]); - sound_unload_mixerdev(audio_devs[adev]->mixer_dev); -mixer_failed: - sound_unload_audiodev(adev); -audio_failed: - return; -} - -static int __init probe_vidc(struct address_info *hw_config) -{ - hw_config->irq = IRQ_DMAS0; - hw_config->dma = DMA_VIRTUAL_SOUND; - hw_config->dma2 = -1; - hw_config->card_subtype = 16; - hw_config->name = "VIDC20"; - return 1; -} - -static void __exit unload_vidc(struct address_info *hw_config) -{ - int i, adev = vidc_adev; - - vidc_adev = -1; - - free_irq(hw_config->irq, &dma_start); - sound_free_dma(hw_config->dma); - - if (adev >= 0) { - sound_unload_mixerdev(audio_devs[adev]->mixer_dev); - sound_unload_audiodev(adev); - for (i = 0; i < 2; i++) - free_page(dma_buf[i]); - } -} - -static struct address_info cfg; - -static int __init init_vidc(void) -{ - if (probe_vidc(&cfg) == 0) - return -ENODEV; - - attach_vidc(&cfg); - - return 0; -} - -static void __exit cleanup_vidc(void) -{ - unload_vidc(&cfg); -} - -module_init(init_vidc); -module_exit(cleanup_vidc); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("VIDC20 audio driver"); -MODULE_LICENSE("GPL"); diff --git a/sound/oss/vidc.h b/sound/oss/vidc.h deleted file mode 100644 index 0d1424751ecd..000000000000 --- a/sound/oss/vidc.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * linux/drivers/sound/vidc.h - * - * Copyright (C) 1997 Russell King <rmk@arm.linux.org.uk> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * VIDC sound function prototypes - */ - -/* vidc_fill.S */ - -/* - * Filler routines for different channels and sample sizes - */ - -extern unsigned long vidc_fill_1x8_u(unsigned long ibuf, unsigned long iend, - unsigned long obuf, int mask); -extern unsigned long vidc_fill_2x8_u(unsigned long ibuf, unsigned long iend, - unsigned long obuf, int mask); -extern unsigned long vidc_fill_1x8_s(unsigned long ibuf, unsigned long iend, - unsigned long obuf, int mask); -extern unsigned long vidc_fill_2x8_s(unsigned long ibuf, unsigned long iend, - unsigned long obuf, int mask); -extern unsigned long vidc_fill_1x16_s(unsigned long ibuf, unsigned long iend, - unsigned long obuf, int mask); -extern unsigned long vidc_fill_2x16_s(unsigned long ibuf, unsigned long iend, - unsigned long obuf, int mask); - -/* - * DMA Interrupt handler - */ - -extern irqreturn_t vidc_sound_dma_irq(int irqnr, void *ref); - -/* - * Filler routine pointer - */ - -extern unsigned long (*vidc_filler) (unsigned long ibuf, unsigned long iend, - unsigned long obuf, int mask); - -/* - * Virtual DMA buffer exhausted - */ - -extern irqreturn_t (*dma_interrupt) (void); - -/* - * Virtual DMA buffer addresses - */ - -extern unsigned long dma_start, dma_count, dma_bufsize; -extern unsigned long dma_buf[2], dma_pbuf[2]; - -/* vidc_synth.c */ - -extern void vidc_synth_init(struct address_info *hw_config); -extern void vidc_synth_exit(struct address_info *hw_config); -extern int vidc_synth_get_volume(void); -extern int vidc_synth_set_volume(int vol); diff --git a/sound/oss/vidc_fill.S b/sound/oss/vidc_fill.S deleted file mode 100644 index bed34921d176..000000000000 --- a/sound/oss/vidc_fill.S +++ /dev/null @@ -1,218 +0,0 @@ -/* - * linux/drivers/sound/vidc_fill.S - * - * Copyright (C) 1997 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Filler routines for DMA buffers - */ -#include <linux/linkage.h> -#include <asm/assembler.h> -#include <mach/hardware.h> -#include <asm/hardware/iomd.h> - - .text - -ENTRY(vidc_fill_1x8_u) - mov ip, #0xff00 -1: cmp r0, r1 - bge vidc_clear - ldrb r4, [r0], #1 - eor r4, r4, #0x80 - and r4, ip, r4, lsl #8 - orr r4, r4, r4, lsl #16 - str r4, [r2], #4 - cmp r2, r3 - blt 1b - mov pc, lr - -ENTRY(vidc_fill_2x8_u) - mov ip, #0xff00 -1: cmp r0, r1 - bge vidc_clear - ldr r4, [r0], #2 - and r5, r4, ip - and r4, ip, r4, lsl #8 - orr r4, r4, r5, lsl #16 - orr r4, r4, r4, lsr #8 - str r4, [r2], #4 - cmp r2, r3 - blt 1b - mov pc, lr - -ENTRY(vidc_fill_1x8_s) - mov ip, #0xff00 -1: cmp r0, r1 - bge vidc_clear - ldrb r4, [r0], #1 - and r4, ip, r4, lsl #8 - orr r4, r4, r4, lsl #16 - str r4, [r2], #4 - cmp r2, r3 - blt 1b - mov pc, lr - -ENTRY(vidc_fill_2x8_s) - mov ip, #0xff00 -1: cmp r0, r1 - bge vidc_clear - ldr r4, [r0], #2 - and r5, r4, ip - and r4, ip, r4, lsl #8 - orr r4, r4, r5, lsl #16 - orr r4, r4, r4, lsr #8 - str r4, [r2], #4 - cmp r2, r3 - blt 1b - mov pc, lr - -ENTRY(vidc_fill_1x16_s) - mov ip, #0xff00 - orr ip, ip, ip, lsr #8 -1: cmp r0, r1 - bge vidc_clear - ldr r5, [r0], #2 - and r4, r5, ip - orr r4, r4, r4, lsl #16 - str r4, [r2], #4 - cmp r0, r1 - addlt r0, r0, #2 - andlt r4, r5, ip, lsl #16 - orrlt r4, r4, r4, lsr #16 - strlt r4, [r2], #4 - cmp r2, r3 - blt 1b - mov pc, lr - -ENTRY(vidc_fill_2x16_s) - mov ip, #0xff00 - orr ip, ip, ip, lsr #8 -1: cmp r0, r1 - bge vidc_clear - ldr r4, [r0], #4 - str r4, [r2], #4 - cmp r0, r1 - ldrlt r4, [r0], #4 - strlt r4, [r2], #4 - cmp r2, r3 - blt 1b - mov pc, lr - -ENTRY(vidc_fill_noaudio) - mov r0, #0 - mov r1, #0 -2: mov r4, #0 - mov r5, #0 -1: cmp r2, r3 - stmltia r2!, {r0, r1, r4, r5} - blt 1b - mov pc, lr - -ENTRY(vidc_clear) - mov r0, #0 - mov r1, #0 - tst r2, #4 - str r0, [r2], #4 - tst r2, #8 - stmia r2!, {r0, r1} - b 2b - -/* - * Call filler routines with: - * r0 = phys address - * r1 = phys end - * r2 = buffer - * Returns: - * r0 = new buffer address - * r2 = new buffer finish - * r4 = corrupted - * r5 = corrupted - * ip = corrupted - */ - -ENTRY(vidc_sound_dma_irq) - stmfd sp!, {r4 - r8, lr} - ldr r8, =dma_start - ldmia r8, {r0, r1, r2, r3, r4, r5} - teq r1, #0 - adreq r4, vidc_fill_noaudio - moveq r7, #1 << 31 - movne r7, #0 - mov ip, #IOMD_BASE & 0xff000000 - orr ip, ip, #IOMD_BASE & 0x00ff0000 - ldrb r6, [ip, #IOMD_SD0ST] - tst r6, #DMA_ST_OFL @ Check for overrun - eorne r6, r6, #DMA_ST_AB - tst r6, #DMA_ST_AB - moveq r2, r3 @ DMAing A, update B - add r3, r2, r5 @ End of DMA buffer - add r1, r1, r0 @ End of virtual DMA buffer - mov lr, pc - mov pc, r4 @ Call fill routine (uses r4, ip) - sub r1, r1, r0 @ Remaining length - stmia r8, {r0, r1} - mov r0, #0 - tst r2, #4 @ Round buffer up to 4 words - strne r0, [r2], #4 - tst r2, #8 - strne r0, [r2], #4 - strne r0, [r2], #4 - sub r2, r2, #16 - mov r2, r2, lsl #20 - movs r2, r2, lsr #20 - orreq r2, r2, #1 << 30 @ Set L bit - orr r2, r2, r7 - ldmdb r8, {r3, r4, r5} - tst r6, #DMA_ST_AB - mov ip, #IOMD_BASE & 0xff000000 - orr ip, ip, #IOMD_BASE & 0x00ff0000 - streq r4, [ip, #IOMD_SD0CURB] - strne r5, [ip, #IOMD_SD0CURA] - streq r2, [ip, #IOMD_SD0ENDB] - strne r2, [ip, #IOMD_SD0ENDA] - ldr lr, [ip, #IOMD_SD0ST] - tst lr, #DMA_ST_OFL - bne 1f - tst r6, #DMA_ST_AB - strne r4, [ip, #IOMD_SD0CURB] - streq r5, [ip, #IOMD_SD0CURA] - strne r2, [ip, #IOMD_SD0ENDB] - streq r2, [ip, #IOMD_SD0ENDA] -1: teq r7, #0 - mov r0, #0x10 - strneb r0, [ip, #IOMD_SD0CR] - ldmfd sp!, {r4 - r8, lr} - mov r0, #1 @ IRQ_HANDLED - teq r1, #0 @ If we have no more - movne pc, lr - teq r3, #0 - movne pc, r3 @ Call interrupt routine - mov pc, lr - - .data - .globl dma_interrupt -dma_interrupt: - .long 0 @ r3 - .globl dma_pbuf -dma_pbuf: - .long 0 @ r4 - .long 0 @ r5 - .globl dma_start -dma_start: - .long 0 @ r0 - .globl dma_count -dma_count: - .long 0 @ r1 - .globl dma_buf -dma_buf: - .long 0 @ r2 - .long 0 @ r3 - .globl vidc_filler -vidc_filler: - .long vidc_fill_noaudio @ r4 - .globl dma_bufsize -dma_bufsize: - .long 0x1000 @ r5 diff --git a/sound/oss/waveartist.c b/sound/oss/waveartist.c deleted file mode 100644 index 4f0c3a232e41..000000000000 --- a/sound/oss/waveartist.c +++ /dev/null @@ -1,2043 +0,0 @@ -/* - * linux/sound/oss/waveartist.c - * - * The low level driver for the RWA010 Rockwell Wave Artist - * codec chip used in the Rebel.com NetWinder. - * - * Cleaned up and integrated into 2.1 by Russell King (rmk@arm.linux.org.uk) - * and Pat Beirne (patb@corel.ca) - * - * - * Copyright (C) by Rebel.com 1998-1999 - * - * RWA010 specs received under NDA from Rockwell - * - * Copyright (C) by Hannu Savolainen 1993-1997 - * - * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) - * Version 2 (June 1991). See the "COPYING" file distributed with this software - * for more info. - * - * Changes: - * 11-10-2000 Bartlomiej Zolnierkiewicz <bkz@linux-ide.org> - * Added __init to waveartist_init() - */ - -/* Debugging */ -#define DEBUG_CMD 1 -#define DEBUG_OUT 2 -#define DEBUG_IN 4 -#define DEBUG_INTR 8 -#define DEBUG_MIXER 16 -#define DEBUG_TRIGGER 32 - -#define debug_flg (0) - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/sched.h> -#include <linux/interrupt.h> -#include <linux/delay.h> -#include <linux/spinlock.h> -#include <linux/bitops.h> - - -#include "sound_config.h" -#include "waveartist.h" - -#ifdef CONFIG_ARM -#include <mach/hardware.h> -#include <asm/mach-types.h> -#endif - -#ifndef NO_DMA -#define NO_DMA 255 -#endif - -#define SUPPORTED_MIXER_DEVICES (SOUND_MASK_SYNTH |\ - SOUND_MASK_PCM |\ - SOUND_MASK_LINE |\ - SOUND_MASK_MIC |\ - SOUND_MASK_LINE1 |\ - SOUND_MASK_RECLEV |\ - SOUND_MASK_VOLUME |\ - SOUND_MASK_IMIX) - -static unsigned short levels[SOUND_MIXER_NRDEVICES] = { - 0x5555, /* Master Volume */ - 0x0000, /* Bass */ - 0x0000, /* Treble */ - 0x2323, /* Synth (FM) */ - 0x4b4b, /* PCM */ - 0x6464, /* PC Speaker */ - 0x0000, /* Ext Line */ - 0x0000, /* Mic */ - 0x0000, /* CD */ - 0x6464, /* Recording monitor */ - 0x0000, /* SB PCM (ALT PCM) */ - 0x0000, /* Recording level */ - 0x6464, /* Input gain */ - 0x6464, /* Output gain */ - 0x0000, /* Line1 (Aux1) */ - 0x0000, /* Line2 (Aux2) */ - 0x0000, /* Line3 (Aux3) */ - 0x0000, /* Digital1 */ - 0x0000, /* Digital2 */ - 0x0000, /* Digital3 */ - 0x0000, /* Phone In */ - 0x6464, /* Phone Out */ - 0x0000, /* Video */ - 0x0000, /* Radio */ - 0x0000 /* Monitor */ -}; - -struct wavnc_info { - struct address_info hw; /* hardware */ - char *chip_name; - - int xfer_count; - int audio_mode; - int open_mode; - int audio_flags; - int record_dev; - int playback_dev; - int dev_no; - - /* Mixer parameters */ - const struct waveartist_mixer_info *mix; - - unsigned short *levels; /* cache of volume settings */ - int recmask; /* currently enabled recording device! */ - -#ifdef CONFIG_ARCH_NETWINDER - signed int slider_vol; /* hardware slider volume */ - unsigned int handset_detect :1; - unsigned int telephone_detect:1; - unsigned int no_autoselect :1;/* handset/telephone autoselects a path */ - unsigned int spkr_mute_state :1;/* set by ioctl or autoselect */ - unsigned int line_mute_state :1;/* set by ioctl or autoselect */ - unsigned int use_slider :1;/* use slider setting for o/p vol */ -#endif -}; - -/* - * This is the implementation specific mixer information. - */ -struct waveartist_mixer_info { - unsigned int supported_devs; /* Supported devices */ - unsigned int recording_devs; /* Recordable devies */ - unsigned int stereo_devs; /* Stereo devices */ - - unsigned int (*select_input)(struct wavnc_info *, unsigned int, - unsigned char *, unsigned char *); - int (*decode_mixer)(struct wavnc_info *, int, - unsigned char, unsigned char); - int (*get_mixer)(struct wavnc_info *, int); -}; - -struct wavnc_port_info { - int open_mode; - int speed; - int channels; - int audio_format; -}; - -static int nr_waveartist_devs; -static struct wavnc_info adev_info[MAX_AUDIO_DEV]; -static DEFINE_SPINLOCK(waveartist_lock); - -#ifndef CONFIG_ARCH_NETWINDER -#define machine_is_netwinder() 0 -#else -static struct timer_list vnc_timer; -static void vnc_configure_mixer(struct wavnc_info *devc, - unsigned int input_mask); -static int vnc_private_ioctl(int dev, unsigned int cmd, int __user *arg); -static void vnc_slider_tick(unsigned long data); -#endif - -static inline void -waveartist_set_ctlr(struct address_info *hw, unsigned char clear, unsigned char set) -{ - unsigned int ctlr_port = hw->io_base + CTLR; - - clear = ~clear & inb(ctlr_port); - - outb(clear | set, ctlr_port); -} - -/* Toggle IRQ acknowledge line - */ -static inline void -waveartist_iack(struct wavnc_info *devc) -{ - unsigned int ctlr_port = devc->hw.io_base + CTLR; - int old_ctlr; - - old_ctlr = inb(ctlr_port) & ~IRQ_ACK; - - outb(old_ctlr | IRQ_ACK, ctlr_port); - outb(old_ctlr, ctlr_port); -} - -static inline int -waveartist_sleep(int timeout_ms) -{ - unsigned int timeout = msecs_to_jiffies(timeout_ms*100); - return schedule_timeout_interruptible(timeout); -} - -static int -waveartist_reset(struct wavnc_info *devc) -{ - struct address_info *hw = &devc->hw; - unsigned int timeout, res = -1; - - waveartist_set_ctlr(hw, -1, RESET); - waveartist_sleep(2); - waveartist_set_ctlr(hw, RESET, 0); - - timeout = 500; - do { - mdelay(2); - - if (inb(hw->io_base + STATR) & CMD_RF) { - res = inw(hw->io_base + CMDR); - if (res == 0x55aa) - break; - } - } while (--timeout); - - if (timeout == 0) { - printk(KERN_WARNING "WaveArtist: reset timeout "); - if (res != (unsigned int)-1) - printk("(res=%04X)", res); - printk("\n"); - return 1; - } - return 0; -} - -/* Helper function to send and receive words - * from WaveArtist. It handles all the handshaking - * and can send or receive multiple words. - */ -static int -waveartist_cmd(struct wavnc_info *devc, - int nr_cmd, unsigned int *cmd, - int nr_resp, unsigned int *resp) -{ - unsigned int io_base = devc->hw.io_base; - unsigned int timed_out = 0; - unsigned int i; - - if (debug_flg & DEBUG_CMD) { - printk("waveartist_cmd: cmd="); - - for (i = 0; i < nr_cmd; i++) - printk("%04X ", cmd[i]); - - printk("\n"); - } - - if (inb(io_base + STATR) & CMD_RF) { - int old_data; - - /* flush the port - */ - - old_data = inw(io_base + CMDR); - - if (debug_flg & DEBUG_CMD) - printk("flushed %04X...", old_data); - - udelay(10); - } - - for (i = 0; !timed_out && i < nr_cmd; i++) { - int count; - - for (count = 5000; count; count--) - if (inb(io_base + STATR) & CMD_WE) - break; - - if (!count) - timed_out = 1; - else - outw(cmd[i], io_base + CMDR); - } - - for (i = 0; !timed_out && i < nr_resp; i++) { - int count; - - for (count = 5000; count; count--) - if (inb(io_base + STATR) & CMD_RF) - break; - - if (!count) - timed_out = 1; - else - resp[i] = inw(io_base + CMDR); - } - - if (debug_flg & DEBUG_CMD) { - if (!timed_out) { - printk("waveartist_cmd: resp="); - - for (i = 0; i < nr_resp; i++) - printk("%04X ", resp[i]); - - printk("\n"); - } else - printk("waveartist_cmd: timed out\n"); - } - - return timed_out ? 1 : 0; -} - -/* - * Send one command word - */ -static inline int -waveartist_cmd1(struct wavnc_info *devc, unsigned int cmd) -{ - return waveartist_cmd(devc, 1, &cmd, 0, NULL); -} - -/* - * Send one command, receive one word - */ -static inline unsigned int -waveartist_cmd1_r(struct wavnc_info *devc, unsigned int cmd) -{ - unsigned int ret; - - waveartist_cmd(devc, 1, &cmd, 1, &ret); - - return ret; -} - -/* - * Send a double command, receive one - * word (and throw it away) - */ -static inline int -waveartist_cmd2(struct wavnc_info *devc, unsigned int cmd, unsigned int arg) -{ - unsigned int vals[2]; - - vals[0] = cmd; - vals[1] = arg; - - return waveartist_cmd(devc, 2, vals, 1, vals); -} - -/* - * Send a triple command - */ -static inline int -waveartist_cmd3(struct wavnc_info *devc, unsigned int cmd, - unsigned int arg1, unsigned int arg2) -{ - unsigned int vals[3]; - - vals[0] = cmd; - vals[1] = arg1; - vals[2] = arg2; - - return waveartist_cmd(devc, 3, vals, 0, NULL); -} - -static int -waveartist_getrev(struct wavnc_info *devc, char *rev) -{ - unsigned int temp[2]; - unsigned int cmd = WACMD_GETREV; - - waveartist_cmd(devc, 1, &cmd, 2, temp); - - rev[0] = temp[0] >> 8; - rev[1] = temp[0] & 255; - rev[2] = '\0'; - - return temp[0]; -} - -static void waveartist_halt_output(int dev); -static void waveartist_halt_input(int dev); -static void waveartist_halt(int dev); -static void waveartist_trigger(int dev, int state); - -static int -waveartist_open(int dev, int mode) -{ - struct wavnc_info *devc; - struct wavnc_port_info *portc; - unsigned long flags; - - if (dev < 0 || dev >= num_audiodevs) - return -ENXIO; - - devc = (struct wavnc_info *) audio_devs[dev]->devc; - portc = (struct wavnc_port_info *) audio_devs[dev]->portc; - - spin_lock_irqsave(&waveartist_lock, flags); - if (portc->open_mode || (devc->open_mode & mode)) { - spin_unlock_irqrestore(&waveartist_lock, flags); - return -EBUSY; - } - - devc->audio_mode = 0; - devc->open_mode |= mode; - portc->open_mode = mode; - waveartist_trigger(dev, 0); - - if (mode & OPEN_READ) - devc->record_dev = dev; - if (mode & OPEN_WRITE) - devc->playback_dev = dev; - spin_unlock_irqrestore(&waveartist_lock, flags); - - return 0; -} - -static void -waveartist_close(int dev) -{ - struct wavnc_info *devc = (struct wavnc_info *) - audio_devs[dev]->devc; - struct wavnc_port_info *portc = (struct wavnc_port_info *) - audio_devs[dev]->portc; - unsigned long flags; - - spin_lock_irqsave(&waveartist_lock, flags); - - waveartist_halt(dev); - - devc->audio_mode = 0; - devc->open_mode &= ~portc->open_mode; - portc->open_mode = 0; - - spin_unlock_irqrestore(&waveartist_lock, flags); -} - -static void -waveartist_output_block(int dev, unsigned long buf, int __count, int intrflag) -{ - struct wavnc_port_info *portc = (struct wavnc_port_info *) - audio_devs[dev]->portc; - struct wavnc_info *devc = (struct wavnc_info *) - audio_devs[dev]->devc; - unsigned long flags; - unsigned int count = __count; - - if (debug_flg & DEBUG_OUT) - printk("waveartist: output block, buf=0x%lx, count=0x%x...\n", - buf, count); - /* - * 16 bit data - */ - if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) - count >>= 1; - - if (portc->channels > 1) - count >>= 1; - - count -= 1; - - if (devc->audio_mode & PCM_ENABLE_OUTPUT && - audio_devs[dev]->flags & DMA_AUTOMODE && - intrflag && - count == devc->xfer_count) { - devc->audio_mode |= PCM_ENABLE_OUTPUT; - return; /* - * Auto DMA mode on. No need to react - */ - } - - spin_lock_irqsave(&waveartist_lock, flags); - - /* - * set sample count - */ - waveartist_cmd2(devc, WACMD_OUTPUTSIZE, count); - - devc->xfer_count = count; - devc->audio_mode |= PCM_ENABLE_OUTPUT; - - spin_unlock_irqrestore(&waveartist_lock, flags); -} - -static void -waveartist_start_input(int dev, unsigned long buf, int __count, int intrflag) -{ - struct wavnc_port_info *portc = (struct wavnc_port_info *) - audio_devs[dev]->portc; - struct wavnc_info *devc = (struct wavnc_info *) - audio_devs[dev]->devc; - unsigned long flags; - unsigned int count = __count; - - if (debug_flg & DEBUG_IN) - printk("waveartist: start input, buf=0x%lx, count=0x%x...\n", - buf, count); - - if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ - count >>= 1; - - if (portc->channels > 1) - count >>= 1; - - count -= 1; - - if (devc->audio_mode & PCM_ENABLE_INPUT && - audio_devs[dev]->flags & DMA_AUTOMODE && - intrflag && - count == devc->xfer_count) { - devc->audio_mode |= PCM_ENABLE_INPUT; - return; /* - * Auto DMA mode on. No need to react - */ - } - - spin_lock_irqsave(&waveartist_lock, flags); - - /* - * set sample count - */ - waveartist_cmd2(devc, WACMD_INPUTSIZE, count); - - devc->xfer_count = count; - devc->audio_mode |= PCM_ENABLE_INPUT; - - spin_unlock_irqrestore(&waveartist_lock, flags); -} - -static int -waveartist_ioctl(int dev, unsigned int cmd, void __user * arg) -{ - return -EINVAL; -} - -static unsigned int -waveartist_get_speed(struct wavnc_port_info *portc) -{ - unsigned int speed; - - /* - * program the speed, channels, bits - */ - if (portc->speed == 8000) - speed = 0x2E71; - else if (portc->speed == 11025) - speed = 0x4000; - else if (portc->speed == 22050) - speed = 0x8000; - else if (portc->speed == 44100) - speed = 0x0; - else { - /* - * non-standard - just calculate - */ - speed = portc->speed << 16; - - speed = (speed / 44100) & 65535; - } - - return speed; -} - -static unsigned int -waveartist_get_bits(struct wavnc_port_info *portc) -{ - unsigned int bits; - - if (portc->audio_format == AFMT_S16_LE) - bits = 1; - else if (portc->audio_format == AFMT_S8) - bits = 0; - else - bits = 2; //default AFMT_U8 - - return bits; -} - -static int -waveartist_prepare_for_input(int dev, int bsize, int bcount) -{ - unsigned long flags; - struct wavnc_info *devc = (struct wavnc_info *) - audio_devs[dev]->devc; - struct wavnc_port_info *portc = (struct wavnc_port_info *) - audio_devs[dev]->portc; - unsigned int speed, bits; - - if (devc->audio_mode) - return 0; - - speed = waveartist_get_speed(portc); - bits = waveartist_get_bits(portc); - - spin_lock_irqsave(&waveartist_lock, flags); - - if (waveartist_cmd2(devc, WACMD_INPUTFORMAT, bits)) - printk(KERN_WARNING "waveartist: error setting the " - "record format to %d\n", portc->audio_format); - - if (waveartist_cmd2(devc, WACMD_INPUTCHANNELS, portc->channels)) - printk(KERN_WARNING "waveartist: error setting record " - "to %d channels\n", portc->channels); - - /* - * write cmd SetSampleSpeedTimeConstant - */ - if (waveartist_cmd2(devc, WACMD_INPUTSPEED, speed)) - printk(KERN_WARNING "waveartist: error setting the record " - "speed to %dHz.\n", portc->speed); - - if (waveartist_cmd2(devc, WACMD_INPUTDMA, 1)) - printk(KERN_WARNING "waveartist: error setting the record " - "data path to 0x%X\n", 1); - - if (waveartist_cmd2(devc, WACMD_INPUTFORMAT, bits)) - printk(KERN_WARNING "waveartist: error setting the record " - "format to %d\n", portc->audio_format); - - devc->xfer_count = 0; - spin_unlock_irqrestore(&waveartist_lock, flags); - waveartist_halt_input(dev); - - if (debug_flg & DEBUG_INTR) { - printk("WA CTLR reg: 0x%02X.\n", - inb(devc->hw.io_base + CTLR)); - printk("WA STAT reg: 0x%02X.\n", - inb(devc->hw.io_base + STATR)); - printk("WA IRQS reg: 0x%02X.\n", - inb(devc->hw.io_base + IRQSTAT)); - } - - return 0; -} - -static int -waveartist_prepare_for_output(int dev, int bsize, int bcount) -{ - unsigned long flags; - struct wavnc_info *devc = (struct wavnc_info *) - audio_devs[dev]->devc; - struct wavnc_port_info *portc = (struct wavnc_port_info *) - audio_devs[dev]->portc; - unsigned int speed, bits; - - /* - * program the speed, channels, bits - */ - speed = waveartist_get_speed(portc); - bits = waveartist_get_bits(portc); - - spin_lock_irqsave(&waveartist_lock, flags); - - if (waveartist_cmd2(devc, WACMD_OUTPUTSPEED, speed) && - waveartist_cmd2(devc, WACMD_OUTPUTSPEED, speed)) - printk(KERN_WARNING "waveartist: error setting the playback " - "speed to %dHz.\n", portc->speed); - - if (waveartist_cmd2(devc, WACMD_OUTPUTCHANNELS, portc->channels)) - printk(KERN_WARNING "waveartist: error setting the playback " - "to %d channels\n", portc->channels); - - if (waveartist_cmd2(devc, WACMD_OUTPUTDMA, 0)) - printk(KERN_WARNING "waveartist: error setting the playback " - "data path to 0x%X\n", 0); - - if (waveartist_cmd2(devc, WACMD_OUTPUTFORMAT, bits)) - printk(KERN_WARNING "waveartist: error setting the playback " - "format to %d\n", portc->audio_format); - - devc->xfer_count = 0; - spin_unlock_irqrestore(&waveartist_lock, flags); - waveartist_halt_output(dev); - - if (debug_flg & DEBUG_INTR) { - printk("WA CTLR reg: 0x%02X.\n",inb(devc->hw.io_base + CTLR)); - printk("WA STAT reg: 0x%02X.\n",inb(devc->hw.io_base + STATR)); - printk("WA IRQS reg: 0x%02X.\n",inb(devc->hw.io_base + IRQSTAT)); - } - - return 0; -} - -static void -waveartist_halt(int dev) -{ - struct wavnc_port_info *portc = (struct wavnc_port_info *) - audio_devs[dev]->portc; - struct wavnc_info *devc; - - if (portc->open_mode & OPEN_WRITE) - waveartist_halt_output(dev); - - if (portc->open_mode & OPEN_READ) - waveartist_halt_input(dev); - - devc = (struct wavnc_info *) audio_devs[dev]->devc; - devc->audio_mode = 0; -} - -static void -waveartist_halt_input(int dev) -{ - struct wavnc_info *devc = (struct wavnc_info *) - audio_devs[dev]->devc; - unsigned long flags; - - spin_lock_irqsave(&waveartist_lock, flags); - - /* - * Stop capture - */ - waveartist_cmd1(devc, WACMD_INPUTSTOP); - - devc->audio_mode &= ~PCM_ENABLE_INPUT; - - /* - * Clear interrupt by toggling - * the IRQ_ACK bit in CTRL - */ - if (inb(devc->hw.io_base + STATR) & IRQ_REQ) - waveartist_iack(devc); - -// devc->audio_mode &= ~PCM_ENABLE_INPUT; - - spin_unlock_irqrestore(&waveartist_lock, flags); -} - -static void -waveartist_halt_output(int dev) -{ - struct wavnc_info *devc = (struct wavnc_info *) - audio_devs[dev]->devc; - unsigned long flags; - - spin_lock_irqsave(&waveartist_lock, flags); - - waveartist_cmd1(devc, WACMD_OUTPUTSTOP); - - devc->audio_mode &= ~PCM_ENABLE_OUTPUT; - - /* - * Clear interrupt by toggling - * the IRQ_ACK bit in CTRL - */ - if (inb(devc->hw.io_base + STATR) & IRQ_REQ) - waveartist_iack(devc); - -// devc->audio_mode &= ~PCM_ENABLE_OUTPUT; - - spin_unlock_irqrestore(&waveartist_lock, flags); -} - -static void -waveartist_trigger(int dev, int state) -{ - struct wavnc_info *devc = (struct wavnc_info *) - audio_devs[dev]->devc; - struct wavnc_port_info *portc = (struct wavnc_port_info *) - audio_devs[dev]->portc; - unsigned long flags; - - if (debug_flg & DEBUG_TRIGGER) { - printk("wavnc: audio trigger "); - if (state & PCM_ENABLE_INPUT) - printk("in "); - if (state & PCM_ENABLE_OUTPUT) - printk("out"); - printk("\n"); - } - - spin_lock_irqsave(&waveartist_lock, flags); - - state &= devc->audio_mode; - - if (portc->open_mode & OPEN_READ && - state & PCM_ENABLE_INPUT) - /* - * enable ADC Data Transfer to PC - */ - waveartist_cmd1(devc, WACMD_INPUTSTART); - - if (portc->open_mode & OPEN_WRITE && - state & PCM_ENABLE_OUTPUT) - /* - * enable DAC data transfer from PC - */ - waveartist_cmd1(devc, WACMD_OUTPUTSTART); - - spin_unlock_irqrestore(&waveartist_lock, flags); -} - -static int -waveartist_set_speed(int dev, int arg) -{ - struct wavnc_port_info *portc = (struct wavnc_port_info *) - audio_devs[dev]->portc; - - if (arg <= 0) - return portc->speed; - - if (arg < 5000) - arg = 5000; - if (arg > 44100) - arg = 44100; - - portc->speed = arg; - return portc->speed; - -} - -static short -waveartist_set_channels(int dev, short arg) -{ - struct wavnc_port_info *portc = (struct wavnc_port_info *) - audio_devs[dev]->portc; - - if (arg != 1 && arg != 2) - return portc->channels; - - portc->channels = arg; - return arg; -} - -static unsigned int -waveartist_set_bits(int dev, unsigned int arg) -{ - struct wavnc_port_info *portc = (struct wavnc_port_info *) - audio_devs[dev]->portc; - - if (arg == 0) - return portc->audio_format; - - if ((arg != AFMT_U8) && (arg != AFMT_S16_LE) && (arg != AFMT_S8)) - arg = AFMT_U8; - - portc->audio_format = arg; - - return arg; -} - -static struct audio_driver waveartist_audio_driver = { - .owner = THIS_MODULE, - .open = waveartist_open, - .close = waveartist_close, - .output_block = waveartist_output_block, - .start_input = waveartist_start_input, - .ioctl = waveartist_ioctl, - .prepare_for_input = waveartist_prepare_for_input, - .prepare_for_output = waveartist_prepare_for_output, - .halt_io = waveartist_halt, - .halt_input = waveartist_halt_input, - .halt_output = waveartist_halt_output, - .trigger = waveartist_trigger, - .set_speed = waveartist_set_speed, - .set_bits = waveartist_set_bits, - .set_channels = waveartist_set_channels -}; - - -static irqreturn_t -waveartist_intr(int irq, void *dev_id) -{ - struct wavnc_info *devc = dev_id; - int irqstatus, status; - - spin_lock(&waveartist_lock); - irqstatus = inb(devc->hw.io_base + IRQSTAT); - status = inb(devc->hw.io_base + STATR); - - if (debug_flg & DEBUG_INTR) - printk("waveartist_intr: stat=%02x, irqstat=%02x\n", - status, irqstatus); - - if (status & IRQ_REQ) /* Clear interrupt */ - waveartist_iack(devc); - else - printk(KERN_WARNING "waveartist: unexpected interrupt\n"); - - if (irqstatus & 0x01) { - int temp = 1; - - /* PCM buffer done - */ - if ((status & DMA0) && (devc->audio_mode & PCM_ENABLE_OUTPUT)) { - DMAbuf_outputintr(devc->playback_dev, 1); - temp = 0; - } - if ((status & DMA1) && (devc->audio_mode & PCM_ENABLE_INPUT)) { - DMAbuf_inputintr(devc->record_dev); - temp = 0; - } - if (temp) //default: - printk(KERN_WARNING "waveartist: Unknown interrupt\n"); - } - if (irqstatus & 0x2) - // We do not use SB mode natively... - printk(KERN_WARNING "waveartist: Unexpected SB interrupt...\n"); - spin_unlock(&waveartist_lock); - return IRQ_HANDLED; -} - -/* ------------------------------------------------------------------------- - * Mixer stuff - */ -struct mix_ent { - unsigned char reg_l; - unsigned char reg_r; - unsigned char shift; - unsigned char max; -}; - -static const struct mix_ent mix_devs[SOUND_MIXER_NRDEVICES] = { - { 2, 6, 1, 7 }, /* SOUND_MIXER_VOLUME */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_BASS */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_TREBLE */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_SYNTH */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_PCM */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_SPEAKER */ - { 0, 4, 6, 31 }, /* SOUND_MIXER_LINE */ - { 2, 6, 4, 3 }, /* SOUND_MIXER_MIC */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_CD */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_IMIX */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_ALTPCM */ -#if 0 - { 3, 7, 0, 10 }, /* SOUND_MIXER_RECLEV */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_IGAIN */ -#else - { 0, 0, 0, 0 }, /* SOUND_MIXER_RECLEV */ - { 3, 7, 0, 7 }, /* SOUND_MIXER_IGAIN */ -#endif - { 0, 0, 0, 0 }, /* SOUND_MIXER_OGAIN */ - { 0, 4, 1, 31 }, /* SOUND_MIXER_LINE1 */ - { 1, 5, 6, 31 }, /* SOUND_MIXER_LINE2 */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_LINE3 */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_DIGITAL1 */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_DIGITAL2 */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_DIGITAL3 */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_PHONEIN */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_PHONEOUT */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_VIDEO */ - { 0, 0, 0, 0 }, /* SOUND_MIXER_RADIO */ - { 0, 0, 0, 0 } /* SOUND_MIXER_MONITOR */ -}; - -static void -waveartist_mixer_update(struct wavnc_info *devc, int whichDev) -{ - unsigned int lev_left, lev_right; - - lev_left = devc->levels[whichDev] & 0xff; - lev_right = devc->levels[whichDev] >> 8; - - if (lev_left > 100) - lev_left = 100; - if (lev_right > 100) - lev_right = 100; - -#define SCALE(lev,max) ((lev) * (max) / 100) - - if (machine_is_netwinder() && whichDev == SOUND_MIXER_PHONEOUT) - whichDev = SOUND_MIXER_VOLUME; - - if (mix_devs[whichDev].reg_l || mix_devs[whichDev].reg_r) { - const struct mix_ent *mix = mix_devs + whichDev; - unsigned int mask, left, right; - - mask = mix->max << mix->shift; - lev_left = SCALE(lev_left, mix->max) << mix->shift; - lev_right = SCALE(lev_right, mix->max) << mix->shift; - - /* read left setting */ - left = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | - mix->reg_l << 8); - - /* read right setting */ - right = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | - mix->reg_r << 8); - - left = (left & ~mask) | (lev_left & mask); - right = (right & ~mask) | (lev_right & mask); - - /* write left,right back */ - waveartist_cmd3(devc, WACMD_SET_MIXER, left, right); - } else { - switch(whichDev) { - case SOUND_MIXER_PCM: - waveartist_cmd3(devc, WACMD_SET_LEVEL, - SCALE(lev_left, 32767), - SCALE(lev_right, 32767)); - break; - - case SOUND_MIXER_SYNTH: - waveartist_cmd3(devc, 0x0100 | WACMD_SET_LEVEL, - SCALE(lev_left, 32767), - SCALE(lev_right, 32767)); - break; - } - } -} - -/* - * Set the ADC MUX to the specified values. We do NOT do any - * checking of the values passed, since we assume that the - * relevant *_select_input function has done that for us. - */ -static void -waveartist_set_adc_mux(struct wavnc_info *devc, char left_dev, - char right_dev) -{ - unsigned int reg_08, reg_09; - - reg_08 = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x0800); - reg_09 = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x0900); - - reg_08 = (reg_08 & ~0x3f) | right_dev << 3 | left_dev; - - waveartist_cmd3(devc, WACMD_SET_MIXER, reg_08, reg_09); -} - -/* - * Decode a recording mask into a mixer selection as follows: - * - * OSS Source WA Source Actual source - * SOUND_MASK_IMIX Mixer Mixer output (same as AD1848) - * SOUND_MASK_LINE Line Line in - * SOUND_MASK_LINE1 Aux 1 Aux 1 in - * SOUND_MASK_LINE2 Aux 2 Aux 2 in - * SOUND_MASK_MIC Mic Microphone - */ -static unsigned int -waveartist_select_input(struct wavnc_info *devc, unsigned int recmask, - unsigned char *dev_l, unsigned char *dev_r) -{ - unsigned int recdev = ADC_MUX_NONE; - - if (recmask & SOUND_MASK_IMIX) { - recmask = SOUND_MASK_IMIX; - recdev = ADC_MUX_MIXER; - } else if (recmask & SOUND_MASK_LINE2) { - recmask = SOUND_MASK_LINE2; - recdev = ADC_MUX_AUX2; - } else if (recmask & SOUND_MASK_LINE1) { - recmask = SOUND_MASK_LINE1; - recdev = ADC_MUX_AUX1; - } else if (recmask & SOUND_MASK_LINE) { - recmask = SOUND_MASK_LINE; - recdev = ADC_MUX_LINE; - } else if (recmask & SOUND_MASK_MIC) { - recmask = SOUND_MASK_MIC; - recdev = ADC_MUX_MIC; - } - - *dev_l = *dev_r = recdev; - - return recmask; -} - -static int -waveartist_decode_mixer(struct wavnc_info *devc, int dev, - unsigned char lev_l, - unsigned char lev_r) -{ - switch (dev) { - case SOUND_MIXER_VOLUME: - case SOUND_MIXER_SYNTH: - case SOUND_MIXER_PCM: - case SOUND_MIXER_LINE: - case SOUND_MIXER_MIC: - case SOUND_MIXER_IGAIN: - case SOUND_MIXER_LINE1: - case SOUND_MIXER_LINE2: - devc->levels[dev] = lev_l | lev_r << 8; - break; - - case SOUND_MIXER_IMIX: - break; - - default: - dev = -EINVAL; - break; - } - - return dev; -} - -static int waveartist_get_mixer(struct wavnc_info *devc, int dev) -{ - return devc->levels[dev]; -} - -static const struct waveartist_mixer_info waveartist_mixer = { - .supported_devs = SUPPORTED_MIXER_DEVICES | SOUND_MASK_IGAIN, - .recording_devs = SOUND_MASK_LINE | SOUND_MASK_MIC | - SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | - SOUND_MASK_IMIX, - .stereo_devs = (SUPPORTED_MIXER_DEVICES | SOUND_MASK_IGAIN) & ~ - (SOUND_MASK_SPEAKER | SOUND_MASK_IMIX), - .select_input = waveartist_select_input, - .decode_mixer = waveartist_decode_mixer, - .get_mixer = waveartist_get_mixer, -}; - -static void -waveartist_set_recmask(struct wavnc_info *devc, unsigned int recmask) -{ - unsigned char dev_l, dev_r; - - recmask &= devc->mix->recording_devs; - - /* - * If more than one recording device selected, - * disable the device that is currently in use. - */ - if (hweight32(recmask) > 1) - recmask &= ~devc->recmask; - - /* - * Translate the recording device mask into - * the ADC multiplexer settings. - */ - devc->recmask = devc->mix->select_input(devc, recmask, - &dev_l, &dev_r); - - waveartist_set_adc_mux(devc, dev_l, dev_r); -} - -static int -waveartist_set_mixer(struct wavnc_info *devc, int dev, unsigned int level) -{ - unsigned int lev_left = level & 0x00ff; - unsigned int lev_right = (level & 0xff00) >> 8; - - if (lev_left > 100) - lev_left = 100; - if (lev_right > 100) - lev_right = 100; - - /* - * Mono devices have their right volume forced to their - * left volume. (from ALSA driver OSS emulation). - */ - if (!(devc->mix->stereo_devs & (1 << dev))) - lev_right = lev_left; - - dev = devc->mix->decode_mixer(devc, dev, lev_left, lev_right); - - if (dev >= 0) - waveartist_mixer_update(devc, dev); - - return dev < 0 ? dev : 0; -} - -static int -waveartist_mixer_ioctl(int dev, unsigned int cmd, void __user * arg) -{ - struct wavnc_info *devc = (struct wavnc_info *)audio_devs[dev]->devc; - int ret = 0, val, nr; - - /* - * All SOUND_MIXER_* ioctls use type 'M' - */ - if (((cmd >> 8) & 255) != 'M') - return -ENOIOCTLCMD; - -#ifdef CONFIG_ARCH_NETWINDER - if (machine_is_netwinder()) { - ret = vnc_private_ioctl(dev, cmd, arg); - if (ret != -ENOIOCTLCMD) - return ret; - else - ret = 0; - } -#endif - - nr = cmd & 0xff; - - if (_SIOC_DIR(cmd) & _SIOC_WRITE) { - if (get_user(val, (int __user *)arg)) - return -EFAULT; - - switch (nr) { - case SOUND_MIXER_RECSRC: - waveartist_set_recmask(devc, val); - break; - - default: - ret = -EINVAL; - if (nr < SOUND_MIXER_NRDEVICES && - devc->mix->supported_devs & (1 << nr)) - ret = waveartist_set_mixer(devc, nr, val); - } - } - - if (ret == 0 && _SIOC_DIR(cmd) & _SIOC_READ) { - ret = -EINVAL; - - switch (nr) { - case SOUND_MIXER_RECSRC: - ret = devc->recmask; - break; - - case SOUND_MIXER_DEVMASK: - ret = devc->mix->supported_devs; - break; - - case SOUND_MIXER_STEREODEVS: - ret = devc->mix->stereo_devs; - break; - - case SOUND_MIXER_RECMASK: - ret = devc->mix->recording_devs; - break; - - case SOUND_MIXER_CAPS: - ret = SOUND_CAP_EXCL_INPUT; - break; - - default: - if (nr < SOUND_MIXER_NRDEVICES) - ret = devc->mix->get_mixer(devc, nr); - break; - } - - if (ret >= 0) - ret = put_user(ret, (int __user *)arg) ? -EFAULT : 0; - } - - return ret; -} - -static struct mixer_operations waveartist_mixer_operations = -{ - .owner = THIS_MODULE, - .id = "WaveArtist", - .name = "WaveArtist", - .ioctl = waveartist_mixer_ioctl -}; - -static void -waveartist_mixer_reset(struct wavnc_info *devc) -{ - int i; - - if (debug_flg & DEBUG_MIXER) - printk("%s: mixer_reset\n", devc->hw.name); - - /* - * reset mixer cmd - */ - waveartist_cmd1(devc, WACMD_RST_MIXER); - - /* - * set input for ADC to come from 'quiet' - * turn on default modes - */ - waveartist_cmd3(devc, WACMD_SET_MIXER, 0x9800, 0xa836); - - /* - * set mixer input select to none, RX filter gains 0 dB - */ - waveartist_cmd3(devc, WACMD_SET_MIXER, 0x4c00, 0x8c00); - - /* - * set bit 0 reg 2 to 1 - unmute MonoOut - */ - waveartist_cmd3(devc, WACMD_SET_MIXER, 0x2801, 0x6800); - - /* set default input device = internal mic - * current recording device = none - */ - waveartist_set_recmask(devc, 0); - - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - waveartist_mixer_update(devc, i); -} - -static int __init waveartist_init(struct wavnc_info *devc) -{ - struct wavnc_port_info *portc; - char rev[3], dev_name[64]; - int my_dev; - - if (waveartist_reset(devc)) - return -ENODEV; - - sprintf(dev_name, "%s (%s", devc->hw.name, devc->chip_name); - - if (waveartist_getrev(devc, rev)) { - strcat(dev_name, " rev. "); - strcat(dev_name, rev); - } - strcat(dev_name, ")"); - - conf_printf2(dev_name, devc->hw.io_base, devc->hw.irq, - devc->hw.dma, devc->hw.dma2); - - portc = kzalloc(sizeof(struct wavnc_port_info), GFP_KERNEL); - if (portc == NULL) - goto nomem; - - my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, dev_name, - &waveartist_audio_driver, sizeof(struct audio_driver), - devc->audio_flags, AFMT_U8 | AFMT_S16_LE | AFMT_S8, - devc, devc->hw.dma, devc->hw.dma2); - - if (my_dev < 0) - goto free; - - audio_devs[my_dev]->portc = portc; - - waveartist_mixer_reset(devc); - - /* - * clear any pending interrupt - */ - waveartist_iack(devc); - - if (request_irq(devc->hw.irq, waveartist_intr, 0, devc->hw.name, devc) < 0) { - printk(KERN_ERR "%s: IRQ %d in use\n", - devc->hw.name, devc->hw.irq); - goto uninstall; - } - - if (sound_alloc_dma(devc->hw.dma, devc->hw.name)) { - printk(KERN_ERR "%s: Can't allocate DMA%d\n", - devc->hw.name, devc->hw.dma); - goto uninstall_irq; - } - - if (devc->hw.dma != devc->hw.dma2 && devc->hw.dma2 != NO_DMA) - if (sound_alloc_dma(devc->hw.dma2, devc->hw.name)) { - printk(KERN_ERR "%s: can't allocate DMA%d\n", - devc->hw.name, devc->hw.dma2); - goto uninstall_dma; - } - - waveartist_set_ctlr(&devc->hw, 0, DMA1_IE | DMA0_IE); - - audio_devs[my_dev]->mixer_dev = - sound_install_mixer(MIXER_DRIVER_VERSION, - dev_name, - &waveartist_mixer_operations, - sizeof(struct mixer_operations), - devc); - - return my_dev; - -uninstall_dma: - sound_free_dma(devc->hw.dma); - -uninstall_irq: - free_irq(devc->hw.irq, devc); - -uninstall: - sound_unload_audiodev(my_dev); - -free: - kfree(portc); - -nomem: - return -1; -} - -static int __init probe_waveartist(struct address_info *hw_config) -{ - struct wavnc_info *devc = &adev_info[nr_waveartist_devs]; - - if (nr_waveartist_devs >= MAX_AUDIO_DEV) { - printk(KERN_WARNING "waveartist: too many audio devices\n"); - return 0; - } - - if (!request_region(hw_config->io_base, 15, hw_config->name)) { - printk(KERN_WARNING "WaveArtist: I/O port conflict\n"); - return 0; - } - - if (hw_config->irq > 15 || hw_config->irq < 0) { - release_region(hw_config->io_base, 15); - printk(KERN_WARNING "WaveArtist: Bad IRQ %d\n", - hw_config->irq); - return 0; - } - - if (hw_config->dma != 3) { - release_region(hw_config->io_base, 15); - printk(KERN_WARNING "WaveArtist: Bad DMA %d\n", - hw_config->dma); - return 0; - } - - hw_config->name = "WaveArtist"; - devc->hw = *hw_config; - devc->open_mode = 0; - devc->chip_name = "RWA-010"; - - return 1; -} - -static void __init -attach_waveartist(struct address_info *hw, const struct waveartist_mixer_info *mix) -{ - struct wavnc_info *devc = &adev_info[nr_waveartist_devs]; - - /* - * NOTE! If irq < 0, there is another driver which has allocated the - * IRQ so that this driver doesn't need to allocate/deallocate it. - * The actually used IRQ is ABS(irq). - */ - devc->hw = *hw; - devc->hw.irq = (hw->irq > 0) ? hw->irq : 0; - devc->open_mode = 0; - devc->playback_dev = 0; - devc->record_dev = 0; - devc->audio_flags = DMA_AUTOMODE; - devc->levels = levels; - - if (hw->dma != hw->dma2 && hw->dma2 != NO_DMA) - devc->audio_flags |= DMA_DUPLEX; - - devc->mix = mix; - devc->dev_no = waveartist_init(devc); - - if (devc->dev_no < 0) - release_region(hw->io_base, 15); - else { -#ifdef CONFIG_ARCH_NETWINDER - if (machine_is_netwinder()) { - setup_timer(&vnc_timer, vnc_slider_tick, - nr_waveartist_devs); - mod_timer(&vnc_timer, jiffies); - - vnc_configure_mixer(devc, 0); - - devc->no_autoselect = 1; - } -#endif - nr_waveartist_devs += 1; - } -} - -static void __exit unload_waveartist(struct address_info *hw) -{ - struct wavnc_info *devc = NULL; - int i; - - for (i = 0; i < nr_waveartist_devs; i++) - if (hw->io_base == adev_info[i].hw.io_base) { - devc = adev_info + i; - break; - } - - if (devc != NULL) { - int mixer; - -#ifdef CONFIG_ARCH_NETWINDER - if (machine_is_netwinder()) - del_timer(&vnc_timer); -#endif - - release_region(devc->hw.io_base, 15); - - waveartist_set_ctlr(&devc->hw, DMA1_IE|DMA0_IE, 0); - - if (devc->hw.irq >= 0) - free_irq(devc->hw.irq, devc); - - sound_free_dma(devc->hw.dma); - - if (devc->hw.dma != devc->hw.dma2 && - devc->hw.dma2 != NO_DMA) - sound_free_dma(devc->hw.dma2); - - mixer = audio_devs[devc->dev_no]->mixer_dev; - - if (mixer >= 0) - sound_unload_mixerdev(mixer); - - if (devc->dev_no >= 0) - sound_unload_audiodev(devc->dev_no); - - nr_waveartist_devs -= 1; - - for (; i < nr_waveartist_devs; i++) - adev_info[i] = adev_info[i + 1]; - } else - printk(KERN_WARNING "waveartist: can't find device " - "to unload\n"); -} - -#ifdef CONFIG_ARCH_NETWINDER - -/* - * Rebel.com Netwinder specifics... - */ - -#include <asm/hardware/dec21285.h> - -#define VNC_TIMER_PERIOD (HZ/4) //check slider 4 times/sec - -#define MIXER_PRIVATE3_RESET 0x53570000 -#define MIXER_PRIVATE3_READ 0x53570001 -#define MIXER_PRIVATE3_WRITE 0x53570002 - -#define VNC_MUTE_INTERNAL_SPKR 0x01 //the sw mute on/off control bit -#define VNC_MUTE_LINE_OUT 0x10 -#define VNC_PHONE_DETECT 0x20 -#define VNC_HANDSET_DETECT 0x40 -#define VNC_DISABLE_AUTOSWITCH 0x80 - -static inline void -vnc_mute_spkr(struct wavnc_info *devc) -{ - unsigned long flags; - - raw_spin_lock_irqsave(&nw_gpio_lock, flags); - nw_cpld_modify(CPLD_UNMUTE, devc->spkr_mute_state ? 0 : CPLD_UNMUTE); - raw_spin_unlock_irqrestore(&nw_gpio_lock, flags); -} - -static void -vnc_mute_lout(struct wavnc_info *devc) -{ - unsigned int left, right; - - left = waveartist_cmd1_r(devc, WACMD_GET_LEVEL); - right = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x400); - - if (devc->line_mute_state) { - left &= ~1; - right &= ~1; - } else { - left |= 1; - right |= 1; - } - waveartist_cmd3(devc, WACMD_SET_MIXER, left, right); - -} - -static int -vnc_volume_slider(struct wavnc_info *devc) -{ - static signed int old_slider_volume; - unsigned long flags; - signed int volume = 255; - - *CSR_TIMER1_LOAD = 0x00ffffff; - - spin_lock_irqsave(&waveartist_lock, flags); - - outb(0xFF, 0x201); - *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1; - - while (volume && (inb(0x201) & 0x01)) - volume--; - - *CSR_TIMER1_CNTL = 0; - - spin_unlock_irqrestore(&waveartist_lock,flags); - - volume = 0x00ffffff - *CSR_TIMER1_VALUE; - - -#ifndef REVERSE - volume = 150 - (volume >> 5); -#else - volume = (volume >> 6) - 25; -#endif - - if (volume < 0) - volume = 0; - - if (volume > 100) - volume = 100; - - /* - * slider quite often reads +-8, so debounce this random noise - */ - if (abs(volume - old_slider_volume) > 7) { - old_slider_volume = volume; - - if (debug_flg & DEBUG_MIXER) - printk(KERN_DEBUG "Slider volume: %d.\n", volume); - } - - return old_slider_volume; -} - -/* - * Decode a recording mask into a mixer selection on the NetWinder - * as follows: - * - * OSS Source WA Source Actual source - * SOUND_MASK_IMIX Mixer Mixer output (same as AD1848) - * SOUND_MASK_LINE Line Line in - * SOUND_MASK_LINE1 Left Mic Handset - * SOUND_MASK_PHONEIN Left Aux Telephone microphone - * SOUND_MASK_MIC Right Mic Builtin microphone - */ -static unsigned int -netwinder_select_input(struct wavnc_info *devc, unsigned int recmask, - unsigned char *dev_l, unsigned char *dev_r) -{ - unsigned int recdev_l = ADC_MUX_NONE, recdev_r = ADC_MUX_NONE; - - if (recmask & SOUND_MASK_IMIX) { - recmask = SOUND_MASK_IMIX; - recdev_l = ADC_MUX_MIXER; - recdev_r = ADC_MUX_MIXER; - } else if (recmask & SOUND_MASK_LINE) { - recmask = SOUND_MASK_LINE; - recdev_l = ADC_MUX_LINE; - recdev_r = ADC_MUX_LINE; - } else if (recmask & SOUND_MASK_LINE1) { - recmask = SOUND_MASK_LINE1; - waveartist_cmd1(devc, WACMD_SET_MONO); /* left */ - recdev_l = ADC_MUX_MIC; - recdev_r = ADC_MUX_NONE; - } else if (recmask & SOUND_MASK_PHONEIN) { - recmask = SOUND_MASK_PHONEIN; - waveartist_cmd1(devc, WACMD_SET_MONO); /* left */ - recdev_l = ADC_MUX_AUX1; - recdev_r = ADC_MUX_NONE; - } else if (recmask & SOUND_MASK_MIC) { - recmask = SOUND_MASK_MIC; - waveartist_cmd1(devc, WACMD_SET_MONO | 0x100); /* right */ - recdev_l = ADC_MUX_NONE; - recdev_r = ADC_MUX_MIC; - } - - *dev_l = recdev_l; - *dev_r = recdev_r; - - return recmask; -} - -static int -netwinder_decode_mixer(struct wavnc_info *devc, int dev, unsigned char lev_l, - unsigned char lev_r) -{ - switch (dev) { - case SOUND_MIXER_VOLUME: - case SOUND_MIXER_SYNTH: - case SOUND_MIXER_PCM: - case SOUND_MIXER_LINE: - case SOUND_MIXER_IGAIN: - devc->levels[dev] = lev_l | lev_r << 8; - break; - - case SOUND_MIXER_MIC: /* right mic only */ - devc->levels[SOUND_MIXER_MIC] &= 0xff; - devc->levels[SOUND_MIXER_MIC] |= lev_l << 8; - break; - - case SOUND_MIXER_LINE1: /* left mic only */ - devc->levels[SOUND_MIXER_MIC] &= 0xff00; - devc->levels[SOUND_MIXER_MIC] |= lev_l; - dev = SOUND_MIXER_MIC; - break; - - case SOUND_MIXER_PHONEIN: /* left aux only */ - devc->levels[SOUND_MIXER_LINE1] = lev_l; - dev = SOUND_MIXER_LINE1; - break; - - case SOUND_MIXER_IMIX: - case SOUND_MIXER_PHONEOUT: - break; - - default: - dev = -EINVAL; - break; - } - return dev; -} - -static int netwinder_get_mixer(struct wavnc_info *devc, int dev) -{ - int levels; - - switch (dev) { - case SOUND_MIXER_VOLUME: - case SOUND_MIXER_SYNTH: - case SOUND_MIXER_PCM: - case SOUND_MIXER_LINE: - case SOUND_MIXER_IGAIN: - levels = devc->levels[dev]; - break; - - case SOUND_MIXER_MIC: /* builtin mic: right mic only */ - levels = devc->levels[SOUND_MIXER_MIC] >> 8; - levels |= levels << 8; - break; - - case SOUND_MIXER_LINE1: /* handset mic: left mic only */ - levels = devc->levels[SOUND_MIXER_MIC] & 0xff; - levels |= levels << 8; - break; - - case SOUND_MIXER_PHONEIN: /* phone mic: left aux1 only */ - levels = devc->levels[SOUND_MIXER_LINE1] & 0xff; - levels |= levels << 8; - break; - - default: - levels = 0; - } - - return levels; -} - -/* - * Waveartist specific mixer information. - */ -static const struct waveartist_mixer_info netwinder_mixer = { - .supported_devs = SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | - SOUND_MASK_PCM | SOUND_MASK_SPEAKER | - SOUND_MASK_LINE | SOUND_MASK_MIC | - SOUND_MASK_IMIX | SOUND_MASK_LINE1 | - SOUND_MASK_PHONEIN | SOUND_MASK_PHONEOUT| - SOUND_MASK_IGAIN, - - .recording_devs = SOUND_MASK_LINE | SOUND_MASK_MIC | - SOUND_MASK_IMIX | SOUND_MASK_LINE1 | - SOUND_MASK_PHONEIN, - - .stereo_devs = SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | - SOUND_MASK_PCM | SOUND_MASK_LINE | - SOUND_MASK_IMIX | SOUND_MASK_IGAIN, - - .select_input = netwinder_select_input, - .decode_mixer = netwinder_decode_mixer, - .get_mixer = netwinder_get_mixer, -}; - -static void -vnc_configure_mixer(struct wavnc_info *devc, unsigned int recmask) -{ - if (!devc->no_autoselect) { - if (devc->handset_detect) { - recmask = SOUND_MASK_LINE1; - devc->spkr_mute_state = devc->line_mute_state = 1; - } else if (devc->telephone_detect) { - recmask = SOUND_MASK_PHONEIN; - devc->spkr_mute_state = devc->line_mute_state = 1; - } else { - /* unless someone has asked for LINE-IN, - * we default to MIC - */ - if ((devc->recmask & SOUND_MASK_LINE) == 0) - devc->recmask = SOUND_MASK_MIC; - devc->spkr_mute_state = devc->line_mute_state = 0; - } - vnc_mute_spkr(devc); - vnc_mute_lout(devc); - - if (recmask != devc->recmask) - waveartist_set_recmask(devc, recmask); - } -} - -static int -vnc_slider(struct wavnc_info *devc) -{ - signed int slider_volume; - unsigned int temp, old_hs, old_td; - - /* - * read the "buttons" state. - * Bit 4 = 0 means handset present - * Bit 5 = 1 means phone offhook - */ - temp = inb(0x201); - - old_hs = devc->handset_detect; - old_td = devc->telephone_detect; - - devc->handset_detect = !(temp & 0x10); - devc->telephone_detect = !!(temp & 0x20); - - if (!devc->no_autoselect && - (old_hs != devc->handset_detect || - old_td != devc->telephone_detect)) - vnc_configure_mixer(devc, devc->recmask); - - slider_volume = vnc_volume_slider(devc); - - /* - * If we're using software controlled volume, and - * the slider moves by more than 20%, then we - * switch back to slider controlled volume. - */ - if (abs(devc->slider_vol - slider_volume) > 20) - devc->use_slider = 1; - - /* - * use only left channel - */ - temp = levels[SOUND_MIXER_VOLUME] & 0xFF; - - if (slider_volume != temp && devc->use_slider) { - devc->slider_vol = slider_volume; - - waveartist_set_mixer(devc, SOUND_MIXER_VOLUME, - slider_volume | slider_volume << 8); - - return 1; - } - - return 0; -} - -static void -vnc_slider_tick(unsigned long data) -{ - int next_timeout; - - if (vnc_slider(adev_info + data)) - next_timeout = 5; // mixer reported change - else - next_timeout = VNC_TIMER_PERIOD; - - mod_timer(&vnc_timer, jiffies + next_timeout); -} - -static int -vnc_private_ioctl(int dev, unsigned int cmd, int __user * arg) -{ - struct wavnc_info *devc = (struct wavnc_info *)audio_devs[dev]->devc; - int val; - - switch (cmd) { - case SOUND_MIXER_PRIVATE1: - { - u_int prev_spkr_mute, prev_line_mute, prev_auto_state; - int val; - - if (get_user(val, arg)) - return -EFAULT; - - /* check if parameter is logical */ - if (val & ~(VNC_MUTE_INTERNAL_SPKR | - VNC_MUTE_LINE_OUT | - VNC_DISABLE_AUTOSWITCH)) - return -EINVAL; - - prev_auto_state = devc->no_autoselect; - prev_spkr_mute = devc->spkr_mute_state; - prev_line_mute = devc->line_mute_state; - - devc->no_autoselect = (val & VNC_DISABLE_AUTOSWITCH) ? 1 : 0; - devc->spkr_mute_state = (val & VNC_MUTE_INTERNAL_SPKR) ? 1 : 0; - devc->line_mute_state = (val & VNC_MUTE_LINE_OUT) ? 1 : 0; - - if (prev_spkr_mute != devc->spkr_mute_state) - vnc_mute_spkr(devc); - - if (prev_line_mute != devc->line_mute_state) - vnc_mute_lout(devc); - - if (prev_auto_state != devc->no_autoselect) - vnc_configure_mixer(devc, devc->recmask); - - return 0; - } - - case SOUND_MIXER_PRIVATE2: - if (get_user(val, arg)) - return -EFAULT; - - switch (val) { -#define VNC_SOUND_PAUSE 0x53 //to pause the DSP -#define VNC_SOUND_RESUME 0x57 //to unpause the DSP - case VNC_SOUND_PAUSE: - waveartist_cmd1(devc, 0x16); - break; - - case VNC_SOUND_RESUME: - waveartist_cmd1(devc, 0x18); - break; - - default: - return -EINVAL; - } - return 0; - - /* private ioctl to allow bulk access to waveartist */ - case SOUND_MIXER_PRIVATE3: - { - unsigned long flags; - int mixer_reg[15], i, val; - - if (get_user(val, arg)) - return -EFAULT; - if (copy_from_user(mixer_reg, (void *)val, sizeof(mixer_reg))) - return -EFAULT; - - switch (mixer_reg[14]) { - case MIXER_PRIVATE3_RESET: - waveartist_mixer_reset(devc); - break; - - case MIXER_PRIVATE3_WRITE: - waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[0], mixer_reg[4]); - waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[1], mixer_reg[5]); - waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[2], mixer_reg[6]); - waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[3], mixer_reg[7]); - waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[8], mixer_reg[9]); - - waveartist_cmd3(devc, WACMD_SET_LEVEL, mixer_reg[10], mixer_reg[11]); - waveartist_cmd3(devc, WACMD_SET_LEVEL, mixer_reg[12], mixer_reg[13]); - break; - - case MIXER_PRIVATE3_READ: - spin_lock_irqsave(&waveartist_lock, flags); - - for (i = 0x30; i < 14 << 8; i += 1 << 8) - waveartist_cmd(devc, 1, &i, 1, mixer_reg + (i >> 8)); - - spin_unlock_irqrestore(&waveartist_lock, flags); - - if (copy_to_user((void *)val, mixer_reg, sizeof(mixer_reg))) - return -EFAULT; - break; - - default: - return -EINVAL; - } - return 0; - } - - /* read back the state from PRIVATE1 */ - case SOUND_MIXER_PRIVATE4: - val = (devc->spkr_mute_state ? VNC_MUTE_INTERNAL_SPKR : 0) | - (devc->line_mute_state ? VNC_MUTE_LINE_OUT : 0) | - (devc->handset_detect ? VNC_HANDSET_DETECT : 0) | - (devc->telephone_detect ? VNC_PHONE_DETECT : 0) | - (devc->no_autoselect ? VNC_DISABLE_AUTOSWITCH : 0); - - return put_user(val, arg) ? -EFAULT : 0; - } - - if (_SIOC_DIR(cmd) & _SIOC_WRITE) { - /* - * special case for master volume: if we - * received this call - switch from hw - * volume control to a software volume - * control, till the hw volume is modified - * to signal that user wants to be back in - * hardware... - */ - if ((cmd & 0xff) == SOUND_MIXER_VOLUME) - devc->use_slider = 0; - - /* speaker output */ - if ((cmd & 0xff) == SOUND_MIXER_SPEAKER) { - unsigned int val, l, r; - - if (get_user(val, arg)) - return -EFAULT; - - l = val & 0x7f; - r = (val & 0x7f00) >> 8; - val = (l + r) / 2; - devc->levels[SOUND_MIXER_SPEAKER] = val | (val << 8); - devc->spkr_mute_state = (val <= 50); - vnc_mute_spkr(devc); - return 0; - } - } - - return -ENOIOCTLCMD; -} - -#endif - -static struct address_info cfg; - -static int attached; - -static int __initdata io = 0; -static int __initdata irq = 0; -static int __initdata dma = 0; -static int __initdata dma2 = 0; - - -static int __init init_waveartist(void) -{ - const struct waveartist_mixer_info *mix; - - if (!io && machine_is_netwinder()) { - /* - * The NetWinder WaveArtist is at a fixed address. - * If the user does not supply an address, use the - * well-known parameters. - */ - io = 0x250; - irq = 12; - dma = 3; - dma2 = 7; - } - - mix = &waveartist_mixer; -#ifdef CONFIG_ARCH_NETWINDER - if (machine_is_netwinder()) - mix = &netwinder_mixer; -#endif - - cfg.io_base = io; - cfg.irq = irq; - cfg.dma = dma; - cfg.dma2 = dma2; - - if (!probe_waveartist(&cfg)) - return -ENODEV; - - attach_waveartist(&cfg, mix); - attached = 1; - - return 0; -} - -static void __exit cleanup_waveartist(void) -{ - if (attached) - unload_waveartist(&cfg); -} - -module_init(init_waveartist); -module_exit(cleanup_waveartist); - -#ifndef MODULE -static int __init setup_waveartist(char *str) -{ - /* io, irq, dma, dma2 */ - int ints[5]; - - str = get_options(str, ARRAY_SIZE(ints), ints); - - io = ints[1]; - irq = ints[2]; - dma = ints[3]; - dma2 = ints[4]; - - return 1; -} -__setup("waveartist=", setup_waveartist); -#endif - -MODULE_DESCRIPTION("Rockwell WaveArtist RWA-010 sound driver"); -module_param_hw(io, int, ioport, 0); /* IO base */ -module_param_hw(irq, int, irq, 0); /* IRQ */ -module_param_hw(dma, int, dma, 0); /* DMA */ -module_param_hw(dma2, int, dma, 0); /* DMA2 */ -MODULE_LICENSE("GPL"); diff --git a/sound/oss/waveartist.h b/sound/oss/waveartist.h deleted file mode 100644 index f18d74b26483..000000000000 --- a/sound/oss/waveartist.h +++ /dev/null @@ -1,93 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * linux/sound/oss/waveartist.h - * - * def file for Rockwell RWA010 chip set, as installed in Rebel.com NetWinder - */ - -//registers -#define CMDR 0 -#define DATR 2 -#define CTLR 4 -#define STATR 5 -#define IRQSTAT 12 - -//bit defs -//reg STATR -#define CMD_WE 0x80 -#define CMD_RF 0x40 -#define DAT_WE 0x20 -#define DAT_RF 0x10 - -#define IRQ_REQ 0x08 -#define DMA1 0x04 -#define DMA0 0x02 - -//bit defs -//reg CTLR -#define CMD_WEIE 0x80 -#define CMD_RFIE 0x40 -#define DAT_WEIE 0x20 -#define DAT_RFIE 0x10 - -#define RESET 0x08 -#define DMA1_IE 0x04 -#define DMA0_IE 0x02 -#define IRQ_ACK 0x01 - -//commands - -#define WACMD_SYSTEMID 0x00 -#define WACMD_GETREV 0x00 -#define WACMD_INPUTFORMAT 0x10 //0-8S, 1-16S, 2-8U -#define WACMD_INPUTCHANNELS 0x11 //1-Mono, 2-Stereo -#define WACMD_INPUTSPEED 0x12 //sampling rate -#define WACMD_INPUTDMA 0x13 //0-8bit, 1-16bit, 2-PIO -#define WACMD_INPUTSIZE 0x14 //samples to interrupt -#define WACMD_INPUTSTART 0x15 //start ADC -#define WACMD_INPUTPAUSE 0x16 //pause ADC -#define WACMD_INPUTSTOP 0x17 //stop ADC -#define WACMD_INPUTRESUME 0x18 //resume ADC -#define WACMD_INPUTPIO 0x19 //PIO ADC - -#define WACMD_OUTPUTFORMAT 0x20 //0-8S, 1-16S, 2-8U -#define WACMD_OUTPUTCHANNELS 0x21 //1-Mono, 2-Stereo -#define WACMD_OUTPUTSPEED 0x22 //sampling rate -#define WACMD_OUTPUTDMA 0x23 //0-8bit, 1-16bit, 2-PIO -#define WACMD_OUTPUTSIZE 0x24 //samples to interrupt -#define WACMD_OUTPUTSTART 0x25 //start ADC -#define WACMD_OUTPUTPAUSE 0x26 //pause ADC -#define WACMD_OUTPUTSTOP 0x27 //stop ADC -#define WACMD_OUTPUTRESUME 0x28 //resume ADC -#define WACMD_OUTPUTPIO 0x29 //PIO ADC - -#define WACMD_GET_LEVEL 0x30 -#define WACMD_SET_LEVEL 0x31 -#define WACMD_SET_MIXER 0x32 -#define WACMD_RST_MIXER 0x33 -#define WACMD_SET_MONO 0x34 - -/* - * Definitions for left/right recording input mux - */ -#define ADC_MUX_NONE 0 -#define ADC_MUX_MIXER 1 -#define ADC_MUX_LINE 2 -#define ADC_MUX_AUX2 3 -#define ADC_MUX_AUX1 4 -#define ADC_MUX_MIC 5 - -/* - * Definitions for mixer gain settings - */ -#define MIX_GAIN_LINE 0 /* line in */ -#define MIX_GAIN_AUX1 1 /* aux1 */ -#define MIX_GAIN_AUX2 2 /* aux2 */ -#define MIX_GAIN_XMIC 3 /* crossover mic */ -#define MIX_GAIN_MIC 4 /* normal mic */ -#define MIX_GAIN_PREMIC 5 /* preamp mic */ -#define MIX_GAIN_OUT 6 /* output */ -#define MIX_GAIN_MONO 7 /* mono in */ - -int wa_sendcmd(unsigned int cmd); -int wa_writecmd(unsigned int cmd, unsigned int arg); diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index 70d023a85bf5..720361455c60 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -573,10 +573,8 @@ static void snd_card_asihpi_pcm_int_start(struct snd_pcm_substream *substream) static void snd_card_asihpi_pcm_int_stop(struct snd_pcm_substream *substream) { - struct snd_card_asihpi_pcm *dpcm; struct snd_card_asihpi *card; - dpcm = (struct snd_card_asihpi_pcm *)substream->runtime->private_data; card = snd_pcm_substream_chip(substream); hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index, @@ -749,9 +747,9 @@ static inline unsigned int modulo_min(unsigned int a, unsigned int b, /** Timer function, equivalent to interrupt service routine for cards */ -static void snd_card_asihpi_timer_function(unsigned long data) +static void snd_card_asihpi_timer_function(struct timer_list *t) { - struct snd_card_asihpi_pcm *dpcm = (struct snd_card_asihpi_pcm *)data; + struct snd_card_asihpi_pcm *dpcm = from_timer(dpcm, t, timer); struct snd_pcm_substream *substream = dpcm->substream; struct snd_card_asihpi *card = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime; @@ -863,7 +861,6 @@ static void snd_card_asihpi_timer_function(unsigned long data) snd_pcm_group_for_each_entry(s, substream) { struct snd_card_asihpi_pcm *ds = s->runtime->private_data; - runtime = s->runtime; /* don't link Cap and Play */ if (substream->stream != s->stream) @@ -948,7 +945,7 @@ static void snd_card_asihpi_int_task(unsigned long data) asihpi = (struct snd_card_asihpi *)a->snd_card->private_data; if (asihpi->llmode_streampriv) snd_card_asihpi_timer_function( - (unsigned long)asihpi->llmode_streampriv); + &asihpi->llmode_streampriv->timer); } static void snd_card_asihpi_isr(struct hpi_adapter *a) @@ -1059,8 +1056,7 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream) If internal and other stream playing, can't switch */ - setup_timer(&dpcm->timer, snd_card_asihpi_timer_function, - (unsigned long) dpcm); + timer_setup(&dpcm->timer, snd_card_asihpi_timer_function, 0); dpcm->substream = substream; runtime->private_data = dpcm; runtime->private_free = snd_card_asihpi_runtime_free; @@ -1240,8 +1236,7 @@ static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream) if (err) return -EIO; - setup_timer(&dpcm->timer, snd_card_asihpi_timer_function, - (unsigned long) dpcm); + timer_setup(&dpcm->timer, snd_card_asihpi_timer_function, 0); dpcm->substream = substream; runtime->private_data = dpcm; runtime->private_free = snd_card_asihpi_runtime_free; diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c index c308a4f70550..4083c8b01619 100644 --- a/sound/pci/au88x0/au88x0_core.c +++ b/sound/pci/au88x0/au88x0_core.c @@ -2628,7 +2628,7 @@ static void vortex_spdif_init(vortex_t * vortex, int spdif_sr, int spdif_mode) else edi = 0x1ffff; } else { - i = edi = 0x800; + edi = 0x800; } /* this_04 and this_08 are the CASp4Src's (samplerate converters) */ vortex_src_setupchannel(vortex, this_04, edi, 0, 1, diff --git a/sound/pci/ctxfi/cttimer.c b/sound/pci/ctxfi/cttimer.c index 8f945341720b..08e874e9a7f6 100644 --- a/sound/pci/ctxfi/cttimer.c +++ b/sound/pci/ctxfi/cttimer.c @@ -63,9 +63,9 @@ struct ct_timer { * system-timer-based updates */ -static void ct_systimer_callback(unsigned long data) +static void ct_systimer_callback(struct timer_list *t) { - struct ct_timer_instance *ti = (struct ct_timer_instance *)data; + struct ct_timer_instance *ti = from_timer(ti, t, timer); struct snd_pcm_substream *substream = ti->substream; struct snd_pcm_runtime *runtime = substream->runtime; struct ct_atc_pcm *apcm = ti->apcm; @@ -93,8 +93,7 @@ static void ct_systimer_callback(unsigned long data) static void ct_systimer_init(struct ct_timer_instance *ti) { - setup_timer(&ti->timer, ct_systimer_callback, - (unsigned long)ti); + timer_setup(&ti->timer, ct_systimer_callback, 0); } static void ct_systimer_start(struct ct_timer_instance *ti) diff --git a/sound/pci/echoaudio/midi.c b/sound/pci/echoaudio/midi.c index 8c685ddb1a41..6045a115cffe 100644 --- a/sound/pci/echoaudio/midi.c +++ b/sound/pci/echoaudio/midi.c @@ -199,9 +199,9 @@ static int snd_echo_midi_output_open(struct snd_rawmidi_substream *substream) -static void snd_echo_midi_output_write(unsigned long data) +static void snd_echo_midi_output_write(struct timer_list *t) { - struct echoaudio *chip = (struct echoaudio *)data; + struct echoaudio *chip = from_timer(chip, t, timer); unsigned long flags; int bytes, sent, time; unsigned char buf[MIDI_OUT_BUFFER_SIZE - 1]; @@ -257,8 +257,8 @@ static void snd_echo_midi_output_trigger(struct snd_rawmidi_substream *substream spin_lock_irq(&chip->lock); if (up) { if (!chip->tinuse) { - setup_timer(&chip->timer, snd_echo_midi_output_write, - (unsigned long)chip); + timer_setup(&chip->timer, snd_echo_midi_output_write, + 0); chip->tinuse = 1; } } else { @@ -273,7 +273,7 @@ static void snd_echo_midi_output_trigger(struct snd_rawmidi_substream *substream spin_unlock_irq(&chip->lock); if (up && !chip->midi_full) - snd_echo_midi_output_write((unsigned long)chip); + snd_echo_midi_output_write(&chip->timer); } diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c index cf05229b569b..bde0d1954f56 100644 --- a/sound/pci/emu10k1/emuproc.c +++ b/sound/pci/emu10k1/emuproc.c @@ -280,7 +280,6 @@ static void snd_emu10k1_proc_rates_read(struct snd_info_entry *entry, struct snd_emu10k1 *emu = entry->private_data; unsigned int val, tmp, n; val = snd_emu10k1_ptr20_read(emu, CAPTURE_RATE_STATUS, 0); - tmp = (val >> 16) & 0x8; for (n = 0; n < 4; n++) { tmp = val >> (16 + (n*4)); if (tmp & 0x8) snd_iprintf(buffer, "Channel %d: Rate=%d\n", n, samplerate[tmp & 0x7]); diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index d4cd6451fdca..39f79a6b5283 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -732,7 +732,7 @@ static void snd_es1371_codec_wait(struct snd_ac97 *ac97) static void snd_es1371_adc_rate(struct ensoniq * ensoniq, unsigned int rate) { - unsigned int n, truncm, freq, result; + unsigned int n, truncm, freq; mutex_lock(&ensoniq->src_mutex); n = rate / 3000; @@ -740,7 +740,6 @@ static void snd_es1371_adc_rate(struct ensoniq * ensoniq, unsigned int rate) n--; truncm = (21 * n - 1) | 1; freq = ((48000UL << 15) / rate) * n; - result = (48000UL << 15) / (freq / n); if (rate >= 24000) { if (truncm > 239) truncm = 239; diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index a0989d231fd0..c1f8e5479bf3 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -977,7 +977,7 @@ int snd_hda_codec_update_widgets(struct hda_codec *codec) hda_nid_t fg; int err; - err = snd_hdac_refresh_widget_sysfs(&codec->core); + err = snd_hdac_refresh_widgets(&codec->core, true); if (err < 0) return err; diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 28e265a88383..5cc65093d941 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -795,6 +795,8 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, hda_nid_t nid = path->path[i]; nums = snd_hda_get_conn_list(codec, nid, &conn); + if (nums < 0) + return; type = get_wcaps_type(get_wcaps(codec, nid)); if (type == AC_WID_PIN || (type == AC_WID_AUD_IN && codec->single_adc_amp)) { diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 3e73d5c6ccfc..768ea8651993 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -27,6 +27,7 @@ #include <linux/mutex.h> #include <linux/module.h> #include <linux/firmware.h> +#include <linux/kernel.h> #include <sound/core.h> #include "hda_codec.h" #include "hda_local.h" @@ -3605,8 +3606,7 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol, static int ca0132_voicefx_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - unsigned int items = sizeof(ca0132_voicefx_presets) - / sizeof(struct ct_voicefx_preset); + unsigned int items = ARRAY_SIZE(ca0132_voicefx_presets); uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; @@ -3635,10 +3635,8 @@ static int ca0132_voicefx_put(struct snd_kcontrol *kcontrol, struct ca0132_spec *spec = codec->spec; int i, err = 0; int sel = ucontrol->value.enumerated.item[0]; - unsigned int items = sizeof(ca0132_voicefx_presets) - / sizeof(struct ct_voicefx_preset); - if (sel >= items) + if (sel >= ARRAY_SIZE(ca0132_voicefx_presets)) return 0; codec_dbg(codec, "ca0132_voicefx_put: sel=%d, preset=%s\n", diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index dce0682c5001..db1a376e27c0 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1803,6 +1803,7 @@ enum { ALC887_FIXUP_ASUS_BASS, ALC887_FIXUP_BASS_CHMAP, ALC1220_FIXUP_GB_DUAL_CODECS, + ALC1220_FIXUP_CLEVO_P950, }; static void alc889_fixup_coef(struct hda_codec *codec, @@ -2020,6 +2021,23 @@ static void alc1220_fixup_gb_dual_codecs(struct hda_codec *codec, } } +static void alc1220_fixup_clevo_p950(struct hda_codec *codec, + const struct hda_fixup *fix, + int action) +{ + hda_nid_t conn1[1] = { 0x0c }; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + alc_update_coef_idx(codec, 0x7, 0, 0x3c3); + /* We therefore want to make sure 0x14 (front headphone) and + * 0x1b (speakers) use the stereo DAC 0x02 + */ + snd_hda_override_conn_list(codec, 0x14, 1, conn1); + snd_hda_override_conn_list(codec, 0x1b, 1, conn1); +} + static const struct hda_fixup alc882_fixups[] = { [ALC882_FIXUP_ABIT_AW9D_MAX] = { .type = HDA_FIXUP_PINS, @@ -2260,6 +2278,10 @@ static const struct hda_fixup alc882_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc1220_fixup_gb_dual_codecs, }, + [ALC1220_FIXUP_CLEVO_P950] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc1220_fixup_clevo_p950, + }, }; static const struct snd_pci_quirk alc882_fixup_tbl[] = { @@ -2333,6 +2355,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1462, 0xda57, "MSI Z270-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS), SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3), SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", ALC882_FIXUP_ABIT_AW9D_MAX), + SND_PCI_QUIRK(0x1558, 0x9501, "Clevo P950HR", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD), SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD), SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", ALC882_FIXUP_LENOVO_Y530), @@ -6366,6 +6389,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC292_FIXUP_TPT440, .name = "tpt440"}, {.id = ALC292_FIXUP_TPT460, .name = "tpt460"}, {.id = ALC233_FIXUP_LENOVO_MULTI_CODECS, .name = "dual-codecs"}, + {.id = ALC700_FIXUP_INTEL_REFERENCE, .name = "alc700-ref"}, {} }; #define ALC225_STANDARD_PINS \ diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index 0e66afa403a3..f1fe497c2f9d 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -2285,7 +2285,7 @@ static unsigned char snd_ice1712_read_i2c(struct snd_ice1712 *ice, static int snd_ice1712_read_eeprom(struct snd_ice1712 *ice, const char *modelname) { - int dev = 0xa0; /* EEPROM device address */ + int dev = ICE_I2C_EEPROM_ADDR; /* I2C EEPROM device address */ unsigned int i, size; struct snd_ice1712_card_info * const *tbl, *c; diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h index 5cfba09c9761..8ae8742662a5 100644 --- a/sound/pci/ice1712/ice1712.h +++ b/sound/pci/ice1712/ice1712.h @@ -218,8 +218,9 @@ /* - * + * I2C EEPROM Address */ +#define ICE_I2C_EEPROM_ADDR 0xA0 struct snd_ice1712; diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index 04cd71c74e5c..c7b007164c99 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -599,9 +599,9 @@ static void snd_korg1212_SendStopAndWait(struct snd_korg1212 *korg1212) } /* timer callback for checking the ack of stop request */ -static void snd_korg1212_timer_func(unsigned long data) +static void snd_korg1212_timer_func(struct timer_list *t) { - struct snd_korg1212 *korg1212 = (struct snd_korg1212 *) data; + struct snd_korg1212 *korg1212 = from_timer(korg1212, t, timer); unsigned long flags; spin_lock_irqsave(&korg1212->lock, flags); @@ -2189,8 +2189,7 @@ static int snd_korg1212_create(struct snd_card *card, struct pci_dev *pci, init_waitqueue_head(&korg1212->wait); spin_lock_init(&korg1212->lock); mutex_init(&korg1212->open_mutex); - setup_timer(&korg1212->timer, snd_korg1212_timer_func, - (unsigned long)korg1212); + timer_setup(&korg1212->timer, snd_korg1212_timer_func, 0); korg1212->irq = -1; korg1212->clkSource = K1212_CLKIDX_Local; diff --git a/sound/pci/oxygen/xonar_dg.h b/sound/pci/oxygen/xonar_dg.h index 7a1e8f9c48e7..24d97721c247 100644 --- a/sound/pci/oxygen/xonar_dg.h +++ b/sound/pci/oxygen/xonar_dg.h @@ -52,6 +52,6 @@ void dg_suspend(struct oxygen *chip); void dg_resume(struct oxygen *chip); void dg_cleanup(struct oxygen *chip); -extern struct oxygen_model model_xonar_dg; +extern const struct oxygen_model model_xonar_dg; #endif diff --git a/sound/pci/oxygen/xonar_dg_mixer.c b/sound/pci/oxygen/xonar_dg_mixer.c index b885dac28a09..d22fbe8aebd0 100644 --- a/sound/pci/oxygen/xonar_dg_mixer.c +++ b/sound/pci/oxygen/xonar_dg_mixer.c @@ -449,7 +449,7 @@ static int dg_mixer_init(struct oxygen *chip) return 0; } -struct oxygen_model model_xonar_dg = { +const struct oxygen_model model_xonar_dg = { .longname = "C-Media Oxygen HD Audio", .chip = "CMI8786", .init = dg_init, diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 9f0f73875f01..1bff4b1b39cd 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -1410,9 +1410,9 @@ static void snd_hdsp_midi_input_trigger(struct snd_rawmidi_substream *substream, spin_unlock_irqrestore (&hdsp->lock, flags); } -static void snd_hdsp_midi_output_timer(unsigned long data) +static void snd_hdsp_midi_output_timer(struct timer_list *t) { - struct hdsp_midi *hmidi = (struct hdsp_midi *) data; + struct hdsp_midi *hmidi = from_timer(hmidi, t, timer); unsigned long flags; snd_hdsp_midi_output_write(hmidi); @@ -1439,8 +1439,8 @@ static void snd_hdsp_midi_output_trigger(struct snd_rawmidi_substream *substream spin_lock_irqsave (&hmidi->lock, flags); if (up) { if (!hmidi->istimer) { - setup_timer(&hmidi->timer, snd_hdsp_midi_output_timer, - (unsigned long) hmidi); + timer_setup(&hmidi->timer, snd_hdsp_midi_output_timer, + 0); mod_timer(&hmidi->timer, 1 + jiffies); hmidi->istimer++; } diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index f20d42714e4d..4c59983158e0 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -1946,9 +1946,9 @@ snd_hdspm_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) spin_unlock_irqrestore (&hdspm->lock, flags); } -static void snd_hdspm_midi_output_timer(unsigned long data) +static void snd_hdspm_midi_output_timer(struct timer_list *t) { - struct hdspm_midi *hmidi = (struct hdspm_midi *) data; + struct hdspm_midi *hmidi = from_timer(hmidi, t, timer); unsigned long flags; snd_hdspm_midi_output_write(hmidi); @@ -1976,8 +1976,8 @@ snd_hdspm_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) spin_lock_irqsave (&hmidi->lock, flags); if (up) { if (!hmidi->istimer) { - setup_timer(&hmidi->timer, snd_hdspm_midi_output_timer, - (unsigned long) hmidi); + timer_setup(&hmidi->timer, + snd_hdspm_midi_output_timer, 0); mod_timer(&hmidi->timer, 1 + jiffies); hmidi->istimer++; } diff --git a/sound/sh/aica.c b/sound/sh/aica.c index fdc680ae8aa0..2b26311405a4 100644 --- a/sound/sh/aica.c +++ b/sound/sh/aica.c @@ -299,14 +299,14 @@ static void run_spu_dma(struct work_struct *work) } } -static void aica_period_elapsed(unsigned long timer_var) +static void aica_period_elapsed(struct timer_list *t) { + struct snd_card_aica *dreamcastcard = from_timer(dreamcastcard, + t, timer); + struct snd_pcm_substream *substream = dreamcastcard->timer_substream; /*timer function - so cannot sleep */ int play_period; struct snd_pcm_runtime *runtime; - struct snd_pcm_substream *substream; - struct snd_card_aica *dreamcastcard; - substream = (struct snd_pcm_substream *) timer_var; runtime = substream->runtime; dreamcastcard = substream->pcm->private_data; /* Have we played out an additional period? */ @@ -336,12 +336,12 @@ static void spu_begin_dma(struct snd_pcm_substream *substream) /*get the queue to do the work */ schedule_work(&(dreamcastcard->spu_dma_work)); /* Timer may already be running */ - if (unlikely(dreamcastcard->timer.data)) { + if (unlikely(dreamcastcard->timer_substream)) { mod_timer(&dreamcastcard->timer, jiffies + 4); return; } - setup_timer(&dreamcastcard->timer, aica_period_elapsed, - (unsigned long) substream); + timer_setup(&dreamcastcard->timer, aica_period_elapsed, 0); + dreamcastcard->timer_substream = substream; mod_timer(&dreamcastcard->timer, jiffies + 4); } @@ -379,7 +379,7 @@ static int snd_aicapcm_pcm_close(struct snd_pcm_substream { struct snd_card_aica *dreamcastcard = substream->pcm->private_data; flush_work(&(dreamcastcard->spu_dma_work)); - if (dreamcastcard->timer.data) + if (dreamcastcard->timer_substream) del_timer(&dreamcastcard->timer); kfree(dreamcastcard->channel); spu_disable(); @@ -600,7 +600,7 @@ static int snd_aica_probe(struct platform_device *devptr) { int err; struct snd_card_aica *dreamcastcard; - dreamcastcard = kmalloc(sizeof(struct snd_card_aica), GFP_KERNEL); + dreamcastcard = kzalloc(sizeof(struct snd_card_aica), GFP_KERNEL); if (unlikely(!dreamcastcard)) return -ENOMEM; err = snd_card_new(&devptr->dev, index, SND_AICA_DRIVER, @@ -619,8 +619,6 @@ static int snd_aica_probe(struct platform_device *devptr) err = snd_aicapcmchip(dreamcastcard, 0); if (unlikely(err < 0)) goto freedreamcast; - dreamcastcard->timer.data = 0; - dreamcastcard->channel = NULL; /* Add basic controls */ err = add_aicamixer_controls(dreamcastcard); if (unlikely(err < 0)) diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index c0abad2067e1..d22758165496 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -36,6 +36,9 @@ config SND_SOC_COMPRESS config SND_SOC_TOPOLOGY bool +config SND_SOC_ACPI + tristate + # All the supported SoCs source "sound/soc/adi/Kconfig" source "sound/soc/amd/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index bf8c1e2ce0bf..5327f4d6c668 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -15,6 +15,12 @@ ifneq ($(CONFIG_SND_SOC_AC97_BUS),) snd-soc-core-objs += soc-ac97.o endif +ifneq ($(CONFIG_SND_SOC_ACPI),) +snd-soc-acpi-objs := soc-acpi.o +endif + +obj-$(CONFIG_SND_SOC_ACPI) += snd-soc-acpi.o + obj-$(CONFIG_SND_SOC) += snd-soc-core.o obj-$(CONFIG_SND_SOC) += codecs/ obj-$(CONFIG_SND_SOC) += generic/ diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index 78187eb24f56..d5838402f667 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -2,3 +2,10 @@ config SND_SOC_AMD_ACP tristate "AMD Audio Coprocessor support" help This option enables ACP DMA support on AMD platform. + +config SND_SOC_AMD_CZ_RT5645_MACH + tristate "AMD CZ support for RT5645" + select SND_SOC_RT5645 + depends on SND_SOC_AMD_ACP && I2C + help + This option enables machine driver for rt5645. diff --git a/sound/soc/amd/Makefile b/sound/soc/amd/Makefile index 1a66ec0366b2..f07fd2e2870a 100644 --- a/sound/soc/amd/Makefile +++ b/sound/soc/amd/Makefile @@ -1,3 +1,5 @@ -snd-soc-acp-pcm-objs := acp-pcm-dma.o +acp_audio_dma-objs := acp-pcm-dma.o +snd-soc-acp-rt5645-mach-objs := acp-rt5645.o -obj-$(CONFIG_SND_SOC_AMD_ACP) += snd-soc-acp-pcm.o +obj-$(CONFIG_SND_SOC_AMD_ACP) += acp_audio_dma.o +obj-$(CONFIG_SND_SOC_AMD_CZ_RT5645_MACH) += snd-soc-acp-rt5645-mach.o diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c index 08b1399d1da2..9f521a55d610 100644 --- a/sound/soc/amd/acp-pcm-dma.c +++ b/sound/soc/amd/acp-pcm-dma.c @@ -20,7 +20,7 @@ #include <linux/pm_runtime.h> #include <sound/soc.h> - +#include <drm/amd_asic_type.h> #include "acp.h" #define PLAYBACK_MIN_NUM_PERIODS 2 @@ -35,6 +35,13 @@ #define MAX_BUFFER (PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS) #define MIN_BUFFER MAX_BUFFER +#define ST_PLAYBACK_MAX_PERIOD_SIZE 8192 +#define ST_CAPTURE_MAX_PERIOD_SIZE ST_PLAYBACK_MAX_PERIOD_SIZE +#define ST_MAX_BUFFER (ST_PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS) +#define ST_MIN_BUFFER ST_MAX_BUFFER + +#define DRV_NAME "acp_audio_dma" + static const struct snd_pcm_hardware acp_pcm_hardware_playback = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | @@ -73,10 +80,42 @@ static const struct snd_pcm_hardware acp_pcm_hardware_capture = { .periods_max = CAPTURE_MAX_NUM_PERIODS, }; -struct audio_drv_data { - struct snd_pcm_substream *play_stream; - struct snd_pcm_substream *capture_stream; - void __iomem *acp_mmio; +static const struct snd_pcm_hardware acp_st_pcm_hardware_playback = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_96000, + .rate_min = 8000, + .rate_max = 96000, + .buffer_bytes_max = ST_MAX_BUFFER, + .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE, + .period_bytes_max = ST_PLAYBACK_MAX_PERIOD_SIZE, + .periods_min = PLAYBACK_MIN_NUM_PERIODS, + .periods_max = PLAYBACK_MAX_NUM_PERIODS, +}; + +static const struct snd_pcm_hardware acp_st_pcm_hardware_capture = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .buffer_bytes_max = ST_MAX_BUFFER, + .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE, + .period_bytes_max = ST_CAPTURE_MAX_PERIOD_SIZE, + .periods_min = CAPTURE_MIN_NUM_PERIODS, + .periods_max = CAPTURE_MAX_NUM_PERIODS, }; static u32 acp_reg_read(void __iomem *acp_mmio, u32 reg) @@ -143,8 +182,8 @@ static void config_dma_descriptor_in_sram(void __iomem *acp_mmio, * system memory <-> ACP SRAM */ static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio, - u32 size, int direction, - u32 pte_offset) + u32 size, int direction, + u32 pte_offset, u32 asic_type) { u16 i; u16 dma_dscr_idx = PLAYBACK_START_DMA_DESCR_CH12; @@ -154,24 +193,46 @@ static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio, dmadscr[i].xfer_val = 0; if (direction == SNDRV_PCM_STREAM_PLAYBACK) { dma_dscr_idx = PLAYBACK_START_DMA_DESCR_CH12 + i; - dmadscr[i].dest = ACP_SHARED_RAM_BANK_1_ADDRESS + - (size / 2) - (i * (size/2)); + dmadscr[i].dest = ACP_SHARED_RAM_BANK_1_ADDRESS + + (i * (size/2)); dmadscr[i].src = ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS + (pte_offset * SZ_4K) + (i * (size/2)); - dmadscr[i].xfer_val |= - (ACP_DMA_ATTRIBUTES_DAGB_ONION_TO_SHAREDMEM << 16) | - (size / 2); + switch (asic_type) { + case CHIP_STONEY: + dmadscr[i].xfer_val |= + (ACP_DMA_ATTRIBUTES_DAGB_GARLIC_TO_SHAREDMEM << 16) | + (size / 2); + break; + default: + dmadscr[i].xfer_val |= + (ACP_DMA_ATTRIBUTES_DAGB_ONION_TO_SHAREDMEM << 16) | + (size / 2); + } } else { dma_dscr_idx = CAPTURE_START_DMA_DESCR_CH14 + i; - dmadscr[i].src = ACP_SHARED_RAM_BANK_5_ADDRESS + - (i * (size/2)); - dmadscr[i].dest = ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS - + (pte_offset * SZ_4K) + - (i * (size/2)); - dmadscr[i].xfer_val |= - BIT(22) | - (ACP_DMA_ATTRIBUTES_SHAREDMEM_TO_DAGB_ONION << 16) | - (size / 2); + switch (asic_type) { + case CHIP_STONEY: + dmadscr[i].src = ACP_SHARED_RAM_BANK_3_ADDRESS + + (i * (size/2)); + dmadscr[i].dest = + ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS + + (pte_offset * SZ_4K) + (i * (size/2)); + dmadscr[i].xfer_val |= + BIT(22) | + (ACP_DMA_ATTRIBUTES_SHARED_MEM_TO_DAGB_GARLIC << 16) | + (size / 2); + break; + default: + dmadscr[i].src = ACP_SHARED_RAM_BANK_5_ADDRESS + + (i * (size/2)); + dmadscr[i].dest = + ACP_INTERNAL_APERTURE_WINDOW_0_ADDRESS + + (pte_offset * SZ_4K) + (i * (size/2)); + dmadscr[i].xfer_val |= + BIT(22) | + (ACP_DMA_ATTRIBUTES_SHAREDMEM_TO_DAGB_ONION << 16) | + (size / 2); + } } config_dma_descriptor_in_sram(acp_mmio, dma_dscr_idx, &dmadscr[i]); @@ -192,7 +253,8 @@ static void set_acp_sysmem_dma_descriptors(void __iomem *acp_mmio, * ACP SRAM <-> I2S */ static void set_acp_to_i2s_dma_descriptors(void __iomem *acp_mmio, - u32 size, int direction) + u32 size, int direction, + u32 asic_type) { u16 i; @@ -213,8 +275,17 @@ static void set_acp_to_i2s_dma_descriptors(void __iomem *acp_mmio, dma_dscr_idx = CAPTURE_START_DMA_DESCR_CH15 + i; /* dmadscr[i].src is unused by hardware. */ dmadscr[i].src = 0; - dmadscr[i].dest = ACP_SHARED_RAM_BANK_5_ADDRESS + + switch (asic_type) { + case CHIP_STONEY: + dmadscr[i].dest = + ACP_SHARED_RAM_BANK_3_ADDRESS + (i * (size / 2)); + break; + default: + dmadscr[i].dest = + ACP_SHARED_RAM_BANK_5_ADDRESS + + (i * (size / 2)); + } dmadscr[i].xfer_val |= BIT(22) | (FROM_ACP_I2S_1 << 16) | (size / 2); } @@ -270,7 +341,8 @@ static void acp_pte_config(void __iomem *acp_mmio, struct page *pg, } static void config_acp_dma(void __iomem *acp_mmio, - struct audio_substream_data *audio_config) + struct audio_substream_data *audio_config, + u32 asic_type) { u32 pte_offset; @@ -284,11 +356,11 @@ static void config_acp_dma(void __iomem *acp_mmio, /* Configure System memory <-> ACP SRAM DMA descriptors */ set_acp_sysmem_dma_descriptors(acp_mmio, audio_config->size, - audio_config->direction, pte_offset); + audio_config->direction, pte_offset, asic_type); /* Configure ACP SRAM <-> I2S DMA descriptors */ set_acp_to_i2s_dma_descriptors(acp_mmio, audio_config->size, - audio_config->direction); + audio_config->direction, asic_type); } /* Start a given DMA channel transfer */ @@ -425,7 +497,7 @@ static void acp_set_sram_bank_state(void __iomem *acp_mmio, u16 bank, } /* Initialize and bring ACP hardware to default state. */ -static int acp_init(void __iomem *acp_mmio) +static int acp_init(void __iomem *acp_mmio, u32 asic_type) { u16 bank; u32 val, count, sram_pte_offset; @@ -499,10 +571,21 @@ static int acp_init(void __iomem *acp_mmio) /* When ACP_TILE_P1 is turned on, all SRAM banks get turned on. * Now, turn off all of them. This can't be done in 'poweron' of * ACP pm domain, as this requires ACP to be initialized. + * For Stoney, Memory gating is disabled,i.e SRAM Banks + * won't be turned off. The default state for SRAM banks is ON. + * Setting SRAM bank state code skipped for STONEY platform. */ - for (bank = 1; bank < 48; bank++) - acp_set_sram_bank_state(acp_mmio, bank, false); + if (asic_type != CHIP_STONEY) { + for (bank = 1; bank < 48; bank++) + acp_set_sram_bank_state(acp_mmio, bank, false); + } + /* Stoney supports 16bit resolution */ + if (asic_type == CHIP_STONEY) { + val = acp_reg_read(acp_mmio, mmACP_I2S_16BIT_RESOLUTION_EN); + val |= 0x03; + acp_reg_write(val, acp_mmio, mmACP_I2S_16BIT_RESOLUTION_EN); + } return 0; } @@ -572,9 +655,9 @@ static irqreturn_t dma_irq_handler(int irq, void *arg) valid_irq = true; if (acp_reg_read(acp_mmio, mmACP_DMA_CUR_DSCR_13) == PLAYBACK_START_DMA_DESCR_CH13) - dscr_idx = PLAYBACK_START_DMA_DESCR_CH12; - else dscr_idx = PLAYBACK_END_DMA_DESCR_CH12; + else + dscr_idx = PLAYBACK_START_DMA_DESCR_CH12; config_acp_dma_channel(acp_mmio, SYSRAM_TO_ACP_CH_NUM, dscr_idx, 1, 0); acp_dma_start(acp_mmio, SYSRAM_TO_ACP_CH_NUM, false); @@ -626,10 +709,23 @@ static int acp_dma_open(struct snd_pcm_substream *substream) if (adata == NULL) return -ENOMEM; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - runtime->hw = acp_pcm_hardware_playback; - else - runtime->hw = acp_pcm_hardware_capture; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (intr_data->asic_type) { + case CHIP_STONEY: + runtime->hw = acp_st_pcm_hardware_playback; + break; + default: + runtime->hw = acp_pcm_hardware_playback; + } + } else { + switch (intr_data->asic_type) { + case CHIP_STONEY: + runtime->hw = acp_st_pcm_hardware_capture; + break; + default: + runtime->hw = acp_pcm_hardware_capture; + } + } ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); @@ -652,14 +748,22 @@ static int acp_dma_open(struct snd_pcm_substream *substream) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { intr_data->play_stream = substream; - for (bank = 1; bank <= 4; bank++) - acp_set_sram_bank_state(intr_data->acp_mmio, bank, - true); + /* For Stoney, Memory gating is disabled,i.e SRAM Banks + * won't be turned off. The default state for SRAM banks is ON. + * Setting SRAM bank state code skipped for STONEY platform. + */ + if (intr_data->asic_type != CHIP_STONEY) { + for (bank = 1; bank <= 4; bank++) + acp_set_sram_bank_state(intr_data->acp_mmio, + bank, true); + } } else { intr_data->capture_stream = substream; - for (bank = 5; bank <= 8; bank++) - acp_set_sram_bank_state(intr_data->acp_mmio, bank, - true); + if (intr_data->asic_type != CHIP_STONEY) { + for (bank = 5; bank <= 8; bank++) + acp_set_sram_bank_state(intr_data->acp_mmio, + bank, true); + } } return 0; @@ -673,6 +777,8 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream, struct page *pg; struct snd_pcm_runtime *runtime; struct audio_substream_data *rtd; + struct snd_soc_pcm_runtime *prtd = substream->private_data; + struct audio_drv_data *adata = dev_get_drvdata(prtd->platform->dev); runtime = substream->runtime; rtd = runtime->private_data; @@ -700,7 +806,7 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream, rtd->num_of_pages = PAGE_ALIGN(size) >> PAGE_SHIFT; rtd->direction = substream->stream; - config_acp_dma(rtd->acp_mmio, rtd); + config_acp_dma(rtd->acp_mmio, rtd, adata->asic_type); status = 0; } else { status = -ENOMEM; @@ -713,40 +819,48 @@ static int acp_dma_hw_free(struct snd_pcm_substream *substream) return snd_pcm_lib_free_pages(substream); } +static u64 acp_get_byte_count(void __iomem *acp_mmio, int stream) +{ + union acp_dma_count playback_dma_count; + union acp_dma_count capture_dma_count; + u64 bytescount = 0; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + playback_dma_count.bcount.high = acp_reg_read(acp_mmio, + mmACP_I2S_TRANSMIT_BYTE_CNT_HIGH); + playback_dma_count.bcount.low = acp_reg_read(acp_mmio, + mmACP_I2S_TRANSMIT_BYTE_CNT_LOW); + bytescount = playback_dma_count.bytescount; + } else { + capture_dma_count.bcount.high = acp_reg_read(acp_mmio, + mmACP_I2S_RECEIVED_BYTE_CNT_HIGH); + capture_dma_count.bcount.low = acp_reg_read(acp_mmio, + mmACP_I2S_RECEIVED_BYTE_CNT_LOW); + bytescount = capture_dma_count.bytescount; + } + return bytescount; +} + static snd_pcm_uframes_t acp_dma_pointer(struct snd_pcm_substream *substream) { - u16 dscr; - u32 mul, dma_config, period_bytes; + u32 buffersize; u32 pos = 0; + u64 bytescount = 0; struct snd_pcm_runtime *runtime = substream->runtime; struct audio_substream_data *rtd = runtime->private_data; - period_bytes = frames_to_bytes(runtime, runtime->period_size); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - dscr = acp_reg_read(rtd->acp_mmio, mmACP_DMA_CUR_DSCR_13); + buffersize = frames_to_bytes(runtime, runtime->buffer_size); + bytescount = acp_get_byte_count(rtd->acp_mmio, substream->stream); - if (dscr == PLAYBACK_START_DMA_DESCR_CH13) - mul = 0; - else - mul = 1; - pos = (mul * period_bytes); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (bytescount > rtd->renderbytescount) + bytescount = bytescount - rtd->renderbytescount; } else { - dma_config = acp_reg_read(rtd->acp_mmio, mmACP_DMA_CNTL_14); - if (dma_config != 0) { - dscr = acp_reg_read(rtd->acp_mmio, - mmACP_DMA_CUR_DSCR_14); - if (dscr == CAPTURE_START_DMA_DESCR_CH14) - mul = 1; - else - mul = 2; - pos = (mul * period_bytes); - } - - if (pos >= (2 * period_bytes)) - pos = 0; - + if (bytescount > rtd->capturebytescount) + bytescount = bytescount - rtd->capturebytescount; } + pos = do_div(bytescount, buffersize); return bytes_to_frames(runtime, pos); } @@ -768,23 +882,6 @@ static int acp_dma_prepare(struct snd_pcm_substream *substream) config_acp_dma_channel(rtd->acp_mmio, ACP_TO_I2S_DMA_CH_NUM, PLAYBACK_START_DMA_DESCR_CH13, NUM_DSCRS_PER_CHANNEL, 0); - /* Fill ACP SRAM (2 periods) with zeros from System RAM - * which is zero-ed in hw_params - */ - acp_dma_start(rtd->acp_mmio, SYSRAM_TO_ACP_CH_NUM, false); - - /* ACP SRAM (2 periods of buffer size) is intially filled with - * zeros. Before rendering starts, 2nd half of SRAM will be - * filled with valid audio data DMA'ed from first half of system - * RAM and 1st half of SRAM will be filled with Zeros. This is - * the initial scenario when redering starts from SRAM. Later - * on, 2nd half of system memory will be DMA'ed to 1st half of - * SRAM, 1st half of system memory will be DMA'ed to 2nd half of - * SRAM in ping-pong way till rendering stops. - */ - config_acp_dma_channel(rtd->acp_mmio, SYSRAM_TO_ACP_CH_NUM, - PLAYBACK_START_DMA_DESCR_CH12, - 1, 0); } else { config_acp_dma_channel(rtd->acp_mmio, ACP_TO_SYSRAM_CH_NUM, CAPTURE_START_DMA_DESCR_CH14, @@ -799,7 +896,8 @@ static int acp_dma_prepare(struct snd_pcm_substream *substream) static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd) { int ret; - u32 loops = 1000; + u32 loops = 4000; + u64 bytescount = 0; struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *prtd = substream->private_data; @@ -811,7 +909,11 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: + bytescount = acp_get_byte_count(rtd->acp_mmio, + substream->stream); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (rtd->renderbytescount == 0) + rtd->renderbytescount = bytescount; acp_dma_start(rtd->acp_mmio, SYSRAM_TO_ACP_CH_NUM, false); while (acp_reg_read(rtd->acp_mmio, mmACP_DMA_CH_STS) & @@ -828,6 +930,8 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd) ACP_TO_I2S_DMA_CH_NUM, true); } else { + if (rtd->capturebytescount == 0) + rtd->capturebytescount = bytescount; acp_dma_start(rtd->acp_mmio, I2S_TO_ACP_DMA_CH_NUM, true); } @@ -841,12 +945,15 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd) * channels will stopped automatically after its transfer * completes : SYSRAM_TO_ACP_CH_NUM / ACP_TO_SYSRAM_CH_NUM */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ret = acp_dma_stop(rtd->acp_mmio, ACP_TO_I2S_DMA_CH_NUM); - else + rtd->renderbytescount = 0; + } else { ret = acp_dma_stop(rtd->acp_mmio, I2S_TO_ACP_DMA_CH_NUM); + rtd->capturebytescount = 0; + } break; default: ret = -EINVAL; @@ -857,10 +964,27 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd) static int acp_dma_new(struct snd_soc_pcm_runtime *rtd) { - return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, + int ret; + struct audio_drv_data *adata = dev_get_drvdata(rtd->platform->dev); + + switch (adata->asic_type) { + case CHIP_STONEY: + ret = snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, + SNDRV_DMA_TYPE_DEV, + NULL, ST_MIN_BUFFER, + ST_MAX_BUFFER); + break; + default: + ret = snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, NULL, MIN_BUFFER, MAX_BUFFER); + break; + } + if (ret < 0) + dev_err(rtd->platform->dev, + "buffer preallocation failer error:%d\n", ret); + return ret; } static int acp_dma_close(struct snd_pcm_substream *substream) @@ -875,14 +999,23 @@ static int acp_dma_close(struct snd_pcm_substream *substream) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { adata->play_stream = NULL; - for (bank = 1; bank <= 4; bank++) - acp_set_sram_bank_state(adata->acp_mmio, bank, - false); - } else { + /* For Stoney, Memory gating is disabled,i.e SRAM Banks + * won't be turned off. The default state for SRAM banks is ON. + * Setting SRAM bank state code skipped for STONEY platform. + * added condition checks for Carrizo platform only + */ + if (adata->asic_type != CHIP_STONEY) { + for (bank = 1; bank <= 4; bank++) + acp_set_sram_bank_state(adata->acp_mmio, bank, + false); + } + } else { adata->capture_stream = NULL; - for (bank = 5; bank <= 8; bank++) - acp_set_sram_bank_state(adata->acp_mmio, bank, - false); + if (adata->asic_type != CHIP_STONEY) { + for (bank = 5; bank <= 8; bank++) + acp_set_sram_bank_state(adata->acp_mmio, bank, + false); + } } /* Disable ACP irq, when the current stream is being closed and @@ -916,6 +1049,7 @@ static int acp_audio_probe(struct platform_device *pdev) int status; struct audio_drv_data *audio_drv_data; struct resource *res; + const u32 *pdata = pdev->dev.platform_data; audio_drv_data = devm_kzalloc(&pdev->dev, sizeof(struct audio_drv_data), GFP_KERNEL); @@ -932,6 +1066,7 @@ static int acp_audio_probe(struct platform_device *pdev) audio_drv_data->play_stream = NULL; audio_drv_data->capture_stream = NULL; + audio_drv_data->asic_type = *pdata; res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { @@ -949,7 +1084,7 @@ static int acp_audio_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, audio_drv_data); /* Initialize the ACP */ - acp_init(audio_drv_data->acp_mmio); + acp_init(audio_drv_data->acp_mmio, audio_drv_data->asic_type); status = snd_soc_register_platform(&pdev->dev, &acp_asoc_platform); if (status != 0) { @@ -980,21 +1115,31 @@ static int acp_pcm_resume(struct device *dev) u16 bank; struct audio_drv_data *adata = dev_get_drvdata(dev); - acp_init(adata->acp_mmio); + acp_init(adata->acp_mmio, adata->asic_type); if (adata->play_stream && adata->play_stream->runtime) { - for (bank = 1; bank <= 4; bank++) - acp_set_sram_bank_state(adata->acp_mmio, bank, + /* For Stoney, Memory gating is disabled,i.e SRAM Banks + * won't be turned off. The default state for SRAM banks is ON. + * Setting SRAM bank state code skipped for STONEY platform. + */ + if (adata->asic_type != CHIP_STONEY) { + for (bank = 1; bank <= 4; bank++) + acp_set_sram_bank_state(adata->acp_mmio, bank, true); + } config_acp_dma(adata->acp_mmio, - adata->play_stream->runtime->private_data); + adata->play_stream->runtime->private_data, + adata->asic_type); } if (adata->capture_stream && adata->capture_stream->runtime) { - for (bank = 5; bank <= 8; bank++) - acp_set_sram_bank_state(adata->acp_mmio, bank, + if (adata->asic_type != CHIP_STONEY) { + for (bank = 5; bank <= 8; bank++) + acp_set_sram_bank_state(adata->acp_mmio, bank, true); + } config_acp_dma(adata->acp_mmio, - adata->capture_stream->runtime->private_data); + adata->capture_stream->runtime->private_data, + adata->asic_type); } acp_reg_write(1, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB); return 0; @@ -1013,7 +1158,7 @@ static int acp_pcm_runtime_resume(struct device *dev) { struct audio_drv_data *adata = dev_get_drvdata(dev); - acp_init(adata->acp_mmio); + acp_init(adata->acp_mmio, adata->asic_type); acp_reg_write(1, adata->acp_mmio, mmACP_EXTERNAL_INTR_ENB); return 0; } @@ -1028,14 +1173,15 @@ static struct platform_driver acp_dma_driver = { .probe = acp_audio_probe, .remove = acp_audio_remove, .driver = { - .name = "acp_audio_dma", + .name = DRV_NAME, .pm = &acp_pm_ops, }, }; module_platform_driver(acp_dma_driver); +MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com"); MODULE_DESCRIPTION("AMD ACP PCM Driver"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:acp-dma-audio"); +MODULE_ALIAS("platform:"DRV_NAME); diff --git a/sound/soc/amd/acp-rt5645.c b/sound/soc/amd/acp-rt5645.c new file mode 100644 index 000000000000..941aed6bb364 --- /dev/null +++ b/sound/soc/amd/acp-rt5645.c @@ -0,0 +1,199 @@ +/* + * Machine driver for AMD ACP Audio engine using Realtek RT5645 codec + * + * Copyright 2017 Advanced Micro Devices, Inc. + * + * This file is modified from rt288 machine driver + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * + */ + +#include <sound/core.h> +#include <sound/soc.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc-dapm.h> +#include <sound/jack.h> +#include <linux/gpio.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/acpi.h> + +#include "../codecs/rt5645.h" + +#define CZ_PLAT_CLK 24000000 + +static struct snd_soc_jack cz_jack; + +static int cz_aif1_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + ret = snd_soc_dai_set_pll(codec_dai, 0, RT5645_PLL1_S_MCLK, + CZ_PLAT_CLK, params_rate(params) * 512); + if (ret < 0) { + dev_err(rtd->dev, "can't set codec pll: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_PLL1, + params_rate(params) * 512, SND_SOC_CLOCK_OUT); + if (ret < 0) { + dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret); + return ret; + } + + return ret; +} + +static int cz_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret; + struct snd_soc_card *card; + struct snd_soc_codec *codec; + + codec = rtd->codec; + card = rtd->card; + + ret = snd_soc_card_jack_new(card, "Headset Jack", + SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &cz_jack, NULL, 0); + if (ret) { + dev_err(card->dev, "HP jack creation failed %d\n", ret); + return ret; + } + + rt5645_set_jack_detect(codec, &cz_jack, &cz_jack, &cz_jack); + + return 0; +} + +static struct snd_soc_ops cz_aif1_ops = { + .hw_params = cz_aif1_hw_params, +}; + +static struct snd_soc_dai_link cz_dai_rt5650[] = { + { + .name = "amd-rt5645-play", + .stream_name = "RT5645_AIF1", + .platform_name = "acp_audio_dma.0.auto", + .cpu_dai_name = "designware-i2s.1.auto", + .codec_dai_name = "rt5645-aif1", + .codec_name = "i2c-10EC5650:00", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM, + .init = cz_init, + .ops = &cz_aif1_ops, + }, + { + .name = "amd-rt5645-cap", + .stream_name = "RT5645_AIF1", + .platform_name = "acp_audio_dma.0.auto", + .cpu_dai_name = "designware-i2s.2.auto", + .codec_dai_name = "rt5645-aif1", + .codec_name = "i2c-10EC5650:00", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF + | SND_SOC_DAIFMT_CBM_CFM, + .ops = &cz_aif1_ops, + }, +}; + +static const struct snd_soc_dapm_widget cz_widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_SPK("Speakers", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), +}; + +static const struct snd_soc_dapm_route cz_audio_route[] = { + {"Headphones", NULL, "HPOL"}, + {"Headphones", NULL, "HPOR"}, + {"RECMIXL", NULL, "Headset Mic"}, + {"RECMIXR", NULL, "Headset Mic"}, + {"Speakers", NULL, "SPOL"}, + {"Speakers", NULL, "SPOR"}, + {"DMIC L2", NULL, "Int Mic"}, + {"DMIC R2", NULL, "Int Mic"}, +}; + +static const struct snd_kcontrol_new cz_mc_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphones"), + SOC_DAPM_PIN_SWITCH("Speakers"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Int Mic"), +}; + +static struct snd_soc_card cz_card = { + .name = "acprt5650", + .owner = THIS_MODULE, + .dai_link = cz_dai_rt5650, + .num_links = ARRAY_SIZE(cz_dai_rt5650), + .dapm_widgets = cz_widgets, + .num_dapm_widgets = ARRAY_SIZE(cz_widgets), + .dapm_routes = cz_audio_route, + .num_dapm_routes = ARRAY_SIZE(cz_audio_route), + .controls = cz_mc_controls, + .num_controls = ARRAY_SIZE(cz_mc_controls), +}; + +static int cz_probe(struct platform_device *pdev) +{ + int ret; + struct snd_soc_card *card; + + card = &cz_card; + cz_card.dev = &pdev->dev; + platform_set_drvdata(pdev, card); + ret = devm_snd_soc_register_card(&pdev->dev, &cz_card); + if (ret) { + dev_err(&pdev->dev, + "devm_snd_soc_register_card(%s) failed: %d\n", + cz_card.name, ret); + return ret; + } + return 0; +} + +static const struct acpi_device_id cz_audio_acpi_match[] = { + { "AMDI1002", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, cz_audio_acpi_match); + +static struct platform_driver cz_pcm_driver = { + .driver = { + .name = "cz-rt5645", + .acpi_match_table = ACPI_PTR(cz_audio_acpi_match), + .pm = &snd_soc_pm_ops, + }, + .probe = cz_probe, +}; + +module_platform_driver(cz_pcm_driver); + +MODULE_AUTHOR("akshu.agrawal@amd.com"); +MODULE_DESCRIPTION("cz-rt5645 audio support"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/amd/acp.h b/sound/soc/amd/acp.h index 9d338216c5ae..ecb458935d1e 100644 --- a/sound/soc/amd/acp.h +++ b/sound/soc/amd/acp.h @@ -20,6 +20,7 @@ /* Capture SRAM address (as a source in dma descriptor) */ #define ACP_SHARED_RAM_BANK_5_ADDRESS 0x400A000 +#define ACP_SHARED_RAM_BANK_3_ADDRESS 0x4006000 #define ACP_DMA_RESET_TIME 10000 #define ACP_CLOCK_EN_TIME_OUT_VALUE 0x000000FF @@ -68,6 +69,7 @@ #define CAPTURE_START_DMA_DESCR_CH15 6 #define CAPTURE_END_DMA_DESCR_CH15 7 +#define mmACP_I2S_16BIT_RESOLUTION_EN 0x5209 enum acp_dma_priority_level { /* 0x0 Specifies the DMA channel is given normal priority */ ACP_DMA_PRIORITY_LEVEL_NORMAL = 0x0, @@ -82,9 +84,26 @@ struct audio_substream_data { u16 num_of_pages; u16 direction; uint64_t size; + u64 renderbytescount; + u64 capturebytescount; void __iomem *acp_mmio; }; +struct audio_drv_data { + struct snd_pcm_substream *play_stream; + struct snd_pcm_substream *capture_stream; + void __iomem *acp_mmio; + u32 asic_type; +}; + +union acp_dma_count { + struct { + u32 low; + u32 high; + } bcount; + u64 bytescount; +}; + enum { ACP_TILE_P1 = 0, ACP_TILE_P2, diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c index 6ba20498202e..2e449d7173fc 100644 --- a/sound/soc/bcm/bcm2835-i2s.c +++ b/sound/soc/bcm/bcm2835-i2s.c @@ -31,6 +31,7 @@ * General Public License for more details. */ +#include <linux/bitops.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/device.h> @@ -99,6 +100,8 @@ #define BCM2835_I2S_CHWID(v) (v) #define BCM2835_I2S_CH1(v) ((v) << 16) #define BCM2835_I2S_CH2(v) (v) +#define BCM2835_I2S_CH1_POS(v) BCM2835_I2S_CH1(BCM2835_I2S_CHPOS(v)) +#define BCM2835_I2S_CH2_POS(v) BCM2835_I2S_CH2(BCM2835_I2S_CHPOS(v)) #define BCM2835_I2S_TX_PANIC(v) ((v) << 24) #define BCM2835_I2S_RX_PANIC(v) ((v) << 16) @@ -110,12 +113,19 @@ #define BCM2835_I2S_INT_RXR BIT(1) #define BCM2835_I2S_INT_TXW BIT(0) +/* Frame length register is 10 bit, maximum length 1024 */ +#define BCM2835_I2S_MAX_FRAME_LENGTH 1024 + /* General device struct */ struct bcm2835_i2s_dev { struct device *dev; struct snd_dmaengine_dai_dma_data dma_data[2]; unsigned int fmt; - unsigned int bclk_ratio; + unsigned int tdm_slots; + unsigned int rx_mask; + unsigned int tx_mask; + unsigned int slot_width; + unsigned int frame_length; struct regmap *i2s_regmap; struct clk *clk; @@ -225,19 +235,120 @@ static int bcm2835_i2s_set_dai_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) { struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); - dev->bclk_ratio = ratio; + + if (!ratio) { + dev->tdm_slots = 0; + return 0; + } + + if (ratio > BCM2835_I2S_MAX_FRAME_LENGTH) + return -EINVAL; + + dev->tdm_slots = 2; + dev->rx_mask = 0x03; + dev->tx_mask = 0x03; + dev->slot_width = ratio / 2; + dev->frame_length = ratio; + return 0; } +static int bcm2835_i2s_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int width) +{ + struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); + + if (slots) { + if (slots < 0 || width < 0) + return -EINVAL; + + /* Limit masks to available slots */ + rx_mask &= GENMASK(slots - 1, 0); + tx_mask &= GENMASK(slots - 1, 0); + + /* + * The driver is limited to 2-channel setups. + * Check that exactly 2 bits are set in the masks. + */ + if (hweight_long((unsigned long) rx_mask) != 2 + || hweight_long((unsigned long) tx_mask) != 2) + return -EINVAL; + + if (slots * width > BCM2835_I2S_MAX_FRAME_LENGTH) + return -EINVAL; + } + + dev->tdm_slots = slots; + + dev->rx_mask = rx_mask; + dev->tx_mask = tx_mask; + dev->slot_width = width; + dev->frame_length = slots * width; + + return 0; +} + +/* + * Convert logical slot number into physical slot number. + * + * If odd_offset is 0 sequential number is identical to logical number. + * This is used for DSP modes with slot numbering 0 1 2 3 ... + * + * Otherwise odd_offset defines the physical offset for odd numbered + * slots. This is used for I2S and left/right justified modes to + * translate from logical slot numbers 0 1 2 3 ... into physical slot + * numbers 0 2 ... 3 4 ... + */ +static int bcm2835_i2s_convert_slot(unsigned int slot, unsigned int odd_offset) +{ + if (!odd_offset) + return slot; + + if (slot & 1) + return (slot >> 1) + odd_offset; + + return slot >> 1; +} + +/* + * Calculate channel position from mask and slot width. + * + * Mask must contain exactly 2 set bits. + * Lowest set bit is channel 1 position, highest set bit channel 2. + * The constant offset is added to both channel positions. + * + * If odd_offset is > 0 slot positions are translated to + * I2S-style TDM slot numbering ( 0 2 ... 3 4 ...) with odd + * logical slot numbers starting at physical slot odd_offset. + */ +static void bcm2835_i2s_calc_channel_pos( + unsigned int *ch1_pos, unsigned int *ch2_pos, + unsigned int mask, unsigned int width, + unsigned int bit_offset, unsigned int odd_offset) +{ + *ch1_pos = bcm2835_i2s_convert_slot((ffs(mask) - 1), odd_offset) + * width + bit_offset; + *ch2_pos = bcm2835_i2s_convert_slot((fls(mask) - 1), odd_offset) + * width + bit_offset; +} + static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); - unsigned int sampling_rate = params_rate(params); - unsigned int data_length, data_delay, bclk_ratio; - unsigned int ch1pos, ch2pos, mode, format; + unsigned int data_length, data_delay, framesync_length; + unsigned int slots, slot_width, odd_slot_offset; + int frame_length, bclk_rate; + unsigned int rx_mask, tx_mask; + unsigned int rx_ch1_pos, rx_ch2_pos, tx_ch1_pos, tx_ch2_pos; + unsigned int mode, format; + bool bit_clock_master = false; + bool frame_sync_master = false; + bool frame_start_falling_edge = false; uint32_t csreg; + int ret = 0; /* * If a stream is already enabled, @@ -248,42 +359,70 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, if (csreg & (BCM2835_I2S_TXON | BCM2835_I2S_RXON)) return 0; - /* - * Adjust the data length according to the format. - * We prefill the half frame length with an integer - * divider of 2400 as explained at the clock settings. - * Maybe it is overwritten there, if the Integer mode - * does not apply. - */ - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - data_length = 16; - break; - case SNDRV_PCM_FORMAT_S24_LE: - data_length = 24; + data_length = params_width(params); + data_delay = 0; + odd_slot_offset = 0; + mode = 0; + + if (dev->tdm_slots) { + slots = dev->tdm_slots; + slot_width = dev->slot_width; + frame_length = dev->frame_length; + rx_mask = dev->rx_mask; + tx_mask = dev->tx_mask; + bclk_rate = dev->frame_length * params_rate(params); + } else { + slots = 2; + slot_width = params_width(params); + rx_mask = 0x03; + tx_mask = 0x03; + + frame_length = snd_soc_params_to_frame_size(params); + if (frame_length < 0) + return frame_length; + + bclk_rate = snd_soc_params_to_bclk(params); + if (bclk_rate < 0) + return bclk_rate; + } + + /* Check if data fits into slots */ + if (data_length > slot_width) + return -EINVAL; + + /* Check if CPU is bit clock master */ + switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBS_CFM: + bit_clock_master = true; break; - case SNDRV_PCM_FORMAT_S32_LE: - data_length = 32; + case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBM_CFM: + bit_clock_master = false; break; default: return -EINVAL; } - /* If bclk_ratio already set, use that one. */ - if (dev->bclk_ratio) - bclk_ratio = dev->bclk_ratio; - else - /* otherwise calculate a fitting block ratio */ - bclk_ratio = 2 * data_length; - - /* Clock should only be set up here if CPU is clock master */ + /* Check if CPU is frame sync master */ switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBM_CFS: + frame_sync_master = true; + break; case SND_SOC_DAIFMT_CBS_CFM: - clk_set_rate(dev->clk, sampling_rate * bclk_ratio); + case SND_SOC_DAIFMT_CBM_CFM: + frame_sync_master = false; break; default: - break; + return -EINVAL; + } + + /* Clock should only be set up here if CPU is clock master */ + if (bit_clock_master) { + ret = clk_set_rate(dev->clk, bclk_rate); + if (ret) + return ret; } /* Setup the frame format */ @@ -294,43 +433,94 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, format |= BCM2835_I2S_CHWID((data_length-8)&0xf); + /* CH2 format is the same as for CH1 */ + format = BCM2835_I2S_CH1(format) | BCM2835_I2S_CH2(format); + switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: - data_delay = 1; - break; - default: + /* I2S mode needs an even number of slots */ + if (slots & 1) + return -EINVAL; + /* - * TODO - * Others are possible but are not implemented at the moment. + * Use I2S-style logical slot numbering: even slots + * are in first half of frame, odd slots in second half. */ - dev_err(dev->dev, "%s:bad format\n", __func__); - return -EINVAL; - } + odd_slot_offset = slots >> 1; - ch1pos = data_delay; - ch2pos = bclk_ratio / 2 + data_delay; + /* MSB starts one cycle after frame start */ + data_delay = 1; - switch (params_channels(params)) { - case 2: - format = BCM2835_I2S_CH1(format) | BCM2835_I2S_CH2(format); - format |= BCM2835_I2S_CH1(BCM2835_I2S_CHPOS(ch1pos)); - format |= BCM2835_I2S_CH2(BCM2835_I2S_CHPOS(ch2pos)); + /* Setup frame sync signal for 50% duty cycle */ + framesync_length = frame_length / 2; + frame_start_falling_edge = true; + break; + case SND_SOC_DAIFMT_LEFT_J: + if (slots & 1) + return -EINVAL; + + odd_slot_offset = slots >> 1; + data_delay = 0; + framesync_length = frame_length / 2; + frame_start_falling_edge = false; + break; + case SND_SOC_DAIFMT_RIGHT_J: + if (slots & 1) + return -EINVAL; + + /* Odd frame lengths aren't supported */ + if (frame_length & 1) + return -EINVAL; + + odd_slot_offset = slots >> 1; + data_delay = slot_width - data_length; + framesync_length = frame_length / 2; + frame_start_falling_edge = false; + break; + case SND_SOC_DAIFMT_DSP_A: + data_delay = 1; + framesync_length = 1; + frame_start_falling_edge = false; + break; + case SND_SOC_DAIFMT_DSP_B: + data_delay = 0; + framesync_length = 1; + frame_start_falling_edge = false; break; default: return -EINVAL; } + bcm2835_i2s_calc_channel_pos(&rx_ch1_pos, &rx_ch2_pos, + rx_mask, slot_width, data_delay, odd_slot_offset); + bcm2835_i2s_calc_channel_pos(&tx_ch1_pos, &tx_ch2_pos, + tx_mask, slot_width, data_delay, odd_slot_offset); + + /* + * Transmitting data immediately after frame start, eg + * in left-justified or DSP mode A, only works stable + * if bcm2835 is the frame clock master. + */ + if ((!rx_ch1_pos || !tx_ch1_pos) && !frame_sync_master) + dev_warn(dev->dev, + "Unstable slave config detected, L/R may be swapped"); + /* * Set format for both streams. * We cannot set another frame length * (and therefore word length) anyway, * so the format will be the same. */ - regmap_write(dev->i2s_regmap, BCM2835_I2S_RXC_A_REG, format); - regmap_write(dev->i2s_regmap, BCM2835_I2S_TXC_A_REG, format); + regmap_write(dev->i2s_regmap, BCM2835_I2S_RXC_A_REG, + format + | BCM2835_I2S_CH1_POS(rx_ch1_pos) + | BCM2835_I2S_CH2_POS(rx_ch2_pos)); + regmap_write(dev->i2s_regmap, BCM2835_I2S_TXC_A_REG, + format + | BCM2835_I2S_CH1_POS(tx_ch1_pos) + | BCM2835_I2S_CH2_POS(tx_ch2_pos)); /* Setup the I2S mode */ - mode = 0; if (data_length <= 16) { /* @@ -342,65 +532,41 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, mode |= BCM2835_I2S_FTXP | BCM2835_I2S_FRXP; } - mode |= BCM2835_I2S_FLEN(bclk_ratio - 1); - mode |= BCM2835_I2S_FSLEN(bclk_ratio / 2); + mode |= BCM2835_I2S_FLEN(frame_length - 1); + mode |= BCM2835_I2S_FSLEN(framesync_length); - /* Master or slave? */ - switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: - /* CPU is master */ - break; - case SND_SOC_DAIFMT_CBM_CFS: - /* - * CODEC is bit clock master - * CPU is frame master - */ + /* CLKM selects bcm2835 clock slave mode */ + if (!bit_clock_master) mode |= BCM2835_I2S_CLKM; - break; - case SND_SOC_DAIFMT_CBS_CFM: - /* - * CODEC is frame master - * CPU is bit clock master - */ + + /* FSM selects bcm2835 frame sync slave mode */ + if (!frame_sync_master) mode |= BCM2835_I2S_FSM; + + /* CLKI selects normal clocking mode, sampling on rising edge */ + switch (dev->fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + case SND_SOC_DAIFMT_NB_IF: + mode |= BCM2835_I2S_CLKI; break; - case SND_SOC_DAIFMT_CBM_CFM: - /* CODEC is master */ - mode |= BCM2835_I2S_CLKM; - mode |= BCM2835_I2S_FSM; + case SND_SOC_DAIFMT_IB_NF: + case SND_SOC_DAIFMT_IB_IF: break; default: - dev_err(dev->dev, "%s:bad master\n", __func__); return -EINVAL; } - /* - * Invert clocks? - * - * The BCM approach seems to be inverted to the classical I2S approach. - */ + /* FSI selects frame start on falling edge */ switch (dev->fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: - /* None. Therefore, both for BCM */ - mode |= BCM2835_I2S_CLKI; - mode |= BCM2835_I2S_FSI; - break; - case SND_SOC_DAIFMT_IB_IF: - /* Both. Therefore, none for BCM */ + case SND_SOC_DAIFMT_IB_NF: + if (frame_start_falling_edge) + mode |= BCM2835_I2S_FSI; break; case SND_SOC_DAIFMT_NB_IF: - /* - * Invert only frame sync. Therefore, - * invert only bit clock for BCM - */ - mode |= BCM2835_I2S_CLKI; - break; - case SND_SOC_DAIFMT_IB_NF: - /* - * Invert only bit clock. Therefore, - * invert only frame sync for BCM - */ - mode |= BCM2835_I2S_FSI; + case SND_SOC_DAIFMT_IB_IF: + if (!frame_start_falling_edge) + mode |= BCM2835_I2S_FSI; break; default: return -EINVAL; @@ -423,7 +589,27 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, /* Clear FIFOs */ bcm2835_i2s_clear_fifos(dev, true, true); - return 0; + dev_dbg(dev->dev, + "slots: %d width: %d rx mask: 0x%02x tx_mask: 0x%02x\n", + slots, slot_width, rx_mask, tx_mask); + + dev_dbg(dev->dev, "frame len: %d sync len: %d data len: %d\n", + frame_length, framesync_length, data_length); + + dev_dbg(dev->dev, "rx pos: %d,%d tx pos: %d,%d\n", + rx_ch1_pos, rx_ch2_pos, tx_ch1_pos, tx_ch2_pos); + + dev_dbg(dev->dev, "sampling rate: %d bclk rate: %d\n", + params_rate(params), bclk_rate); + + dev_dbg(dev->dev, "CLKM: %d CLKI: %d FSM: %d FSI: %d frame start: %s edge\n", + !!(mode & BCM2835_I2S_CLKM), + !!(mode & BCM2835_I2S_CLKI), + !!(mode & BCM2835_I2S_FSM), + !!(mode & BCM2835_I2S_FSI), + (mode & BCM2835_I2S_FSI) ? "falling" : "rising"); + + return ret; } static int bcm2835_i2s_prepare(struct snd_pcm_substream *substream, @@ -559,6 +745,7 @@ static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = { .hw_params = bcm2835_i2s_hw_params, .set_fmt = bcm2835_i2s_set_dai_fmt, .set_bclk_ratio = bcm2835_i2s_set_dai_bclk_ratio, + .set_tdm_slot = bcm2835_i2s_set_dai_tdm_slot, }; static int bcm2835_i2s_dai_probe(struct snd_soc_dai *dai) @@ -578,7 +765,9 @@ static struct snd_soc_dai_driver bcm2835_i2s_dai = { .playback = { .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_192000, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 8000, + .rate_max = 384000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE @@ -586,13 +775,16 @@ static struct snd_soc_dai_driver bcm2835_i2s_dai = { .capture = { .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_192000, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 8000, + .rate_max = 384000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE }, .ops = &bcm2835_i2s_dai_ops, - .symmetric_rates = 1 + .symmetric_rates = 1, + .symmetric_samplebits = 1, }; static bool bcm2835_i2s_volatile_reg(struct device *dev, unsigned int reg) @@ -699,9 +891,6 @@ static int bcm2835_i2s_probe(struct platform_device *pdev) dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].flags = SND_DMAENGINE_PCM_DAI_FLAG_PACK; - /* BCLK ratio - use default */ - dev->bclk_ratio = 0; - /* Store the pdev */ dev->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, dev); diff --git a/sound/soc/bcm/cygnus-ssp.c b/sound/soc/bcm/cygnus-ssp.c index 15c438f0f22d..abafadc0b534 100644 --- a/sound/soc/bcm/cygnus-ssp.c +++ b/sound/soc/bcm/cygnus-ssp.c @@ -655,23 +655,10 @@ static int cygnus_ssp_hw_params(struct snd_pcm_substream *substream, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg); value &= ~BIT(BF_SRC_CFGX_BUFFER_PAIR_ENABLE); - /* Configure channels as mono or stereo/TDM */ - if (params_channels(params) == 1) - value |= BIT(BF_SRC_CFGX_SAMPLE_CH_MODE); - else - value &= ~BIT(BF_SRC_CFGX_SAMPLE_CH_MODE); + value &= ~BIT(BF_SRC_CFGX_SAMPLE_CH_MODE); writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg); switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S8: - if (aio->port_type == PORT_SPDIF) { - dev_err(aio->cygaud->dev, - "SPDIF does not support 8bit format\n"); - return -EINVAL; - } - bitres = 8; - break; - case SNDRV_PCM_FORMAT_S16_LE: bitres = 16; break; @@ -842,6 +829,7 @@ int cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai, int len) return -EINVAL; } } +EXPORT_SYMBOL_GPL(cygnus_ssp_set_custom_fsync_width); static int cygnus_ssp_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { @@ -998,7 +986,7 @@ static int cygnus_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, active_slots = hweight32(tx_mask); - if ((active_slots < 0) || (active_slots > 16)) + if (active_slots > 16) return -EINVAL; /* Slot value must be even */ @@ -1136,15 +1124,21 @@ static const struct snd_soc_dai_ops cygnus_ssp_dai_ops = { .set_tdm_slot = cygnus_set_dai_tdm_slot, }; +static const struct snd_soc_dai_ops cygnus_spdif_dai_ops = { + .startup = cygnus_ssp_startup, + .shutdown = cygnus_ssp_shutdown, + .trigger = cygnus_ssp_trigger, + .hw_params = cygnus_ssp_hw_params, + .set_sysclk = cygnus_ssp_set_sysclk, +}; #define INIT_CPU_DAI(num) { \ .name = "cygnus-ssp" #num, \ .playback = { \ - .channels_min = 1, \ + .channels_min = 2, \ .channels_max = 16, \ .rates = SNDRV_PCM_RATE_KNOT, \ - .formats = SNDRV_PCM_FMTBIT_S8 | \ - SNDRV_PCM_FMTBIT_S16_LE | \ + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S32_LE, \ }, \ .capture = { \ @@ -1152,7 +1146,7 @@ static const struct snd_soc_dai_ops cygnus_ssp_dai_ops = { .channels_max = 16, \ .rates = SNDRV_PCM_RATE_KNOT, \ .formats = SNDRV_PCM_FMTBIT_S16_LE | \ - SNDRV_PCM_FMTBIT_S32_LE, \ + SNDRV_PCM_FMTBIT_S32_LE, \ }, \ .ops = &cygnus_ssp_dai_ops, \ .suspend = cygnus_ssp_suspend, \ @@ -1174,7 +1168,7 @@ static const struct snd_soc_dai_driver cygnus_spdif_dai_info = { .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, }, - .ops = &cygnus_ssp_dai_ops, + .ops = &cygnus_spdif_dai_ops, .suspend = cygnus_ssp_suspend, .resume = cygnus_ssp_resume, }; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index c367d11079bc..a42ddbc93f3d 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -214,9 +214,9 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8998 if MFD_WM8998 select SND_SOC_WM9081 if I2C select SND_SOC_WM9090 if I2C - select SND_SOC_WM9705 if SND_SOC_AC97_BUS - select SND_SOC_WM9712 if SND_SOC_AC97_BUS - select SND_SOC_WM9713 if SND_SOC_AC97_BUS + select SND_SOC_WM9705 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) + select SND_SOC_WM9712 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) + select SND_SOC_WM9713 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) help Normally ASoC codec drivers are only built if a machine driver which uses them is also built since they are only usable with a machine @@ -749,6 +749,10 @@ config SND_SOC_RT5514 config SND_SOC_RT5514_SPI tristate +config SND_SOC_RT5514_SPI_BUILTIN + bool # force RT5514_SPI to be built-in to avoid link errors + default SND_SOC_RT5514=y && SND_SOC_RT5514_SPI=m + config SND_SOC_RT5616 tristate "Realtek RT5616 CODEC" depends on I2C @@ -1128,14 +1132,17 @@ config SND_SOC_WM9090 config SND_SOC_WM9705 tristate select REGMAP_AC97 + select AC97_BUS_COMPAT if AC97_BUS_NEW config SND_SOC_WM9712 tristate select REGMAP_AC97 + select AC97_BUS_COMPAT if AC97_BUS_NEW config SND_SOC_WM9713 tristate select REGMAP_AC97 + select AC97_BUS_COMPAT if AC97_BUS_NEW config SND_SOC_ZX_AUD96P22 tristate "ZTE ZX AUD96P22 CODEC" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 05018b7ca72b..0001069ce2a7 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -360,6 +360,7 @@ obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o obj-$(CONFIG_SND_SOC_RT5514) += snd-soc-rt5514.o obj-$(CONFIG_SND_SOC_RT5514_SPI) += snd-soc-rt5514-spi.o +obj-$(CONFIG_SND_SOC_RT5514_SPI_BUILTIN) += snd-soc-rt5514-spi.o obj-$(CONFIG_SND_SOC_RT5616) += snd-soc-rt5616.o obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index a1149f6a8450..b3375e19598a 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -13,6 +13,7 @@ #include <linux/delay.h> #include <linux/gcd.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/pm_runtime.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -293,16 +294,99 @@ int arizona_init_gpio(struct snd_soc_codec *codec) } EXPORT_SYMBOL_GPL(arizona_init_gpio); -int arizona_init_notifiers(struct snd_soc_codec *codec) +int arizona_init_common(struct arizona *arizona) { - struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); - struct arizona *arizona = priv->arizona; + struct arizona_pdata *pdata = &arizona->pdata; + unsigned int val, mask; + int i; BLOCKING_INIT_NOTIFIER_HEAD(&arizona->notifier); + for (i = 0; i < ARIZONA_MAX_OUTPUT; ++i) { + /* Default is 0 so noop with defaults */ + if (pdata->out_mono[i]) + val = ARIZONA_OUT1_MONO; + else + val = 0; + + regmap_update_bits(arizona->regmap, + ARIZONA_OUTPUT_PATH_CONFIG_1L + (i * 8), + ARIZONA_OUT1_MONO, val); + } + + for (i = 0; i < ARIZONA_MAX_PDM_SPK; i++) { + if (pdata->spk_mute[i]) + regmap_update_bits(arizona->regmap, + ARIZONA_PDM_SPK1_CTRL_1 + (i * 2), + ARIZONA_SPK1_MUTE_ENDIAN_MASK | + ARIZONA_SPK1_MUTE_SEQ1_MASK, + pdata->spk_mute[i]); + + if (pdata->spk_fmt[i]) + regmap_update_bits(arizona->regmap, + ARIZONA_PDM_SPK1_CTRL_2 + (i * 2), + ARIZONA_SPK1_FMT_MASK, + pdata->spk_fmt[i]); + } + + for (i = 0; i < ARIZONA_MAX_INPUT; i++) { + /* Default for both is 0 so noop with defaults */ + val = pdata->dmic_ref[i] << ARIZONA_IN1_DMIC_SUP_SHIFT; + if (pdata->inmode[i] & ARIZONA_INMODE_DMIC) + val |= 1 << ARIZONA_IN1_MODE_SHIFT; + + switch (arizona->type) { + case WM8998: + case WM1814: + regmap_update_bits(arizona->regmap, + ARIZONA_ADC_DIGITAL_VOLUME_1L + (i * 8), + ARIZONA_IN1L_SRC_SE_MASK, + (pdata->inmode[i] & ARIZONA_INMODE_SE) + << ARIZONA_IN1L_SRC_SE_SHIFT); + + regmap_update_bits(arizona->regmap, + ARIZONA_ADC_DIGITAL_VOLUME_1R + (i * 8), + ARIZONA_IN1R_SRC_SE_MASK, + (pdata->inmode[i] & ARIZONA_INMODE_SE) + << ARIZONA_IN1R_SRC_SE_SHIFT); + + mask = ARIZONA_IN1_DMIC_SUP_MASK | + ARIZONA_IN1_MODE_MASK; + break; + default: + if (pdata->inmode[i] & ARIZONA_INMODE_SE) + val |= 1 << ARIZONA_IN1_SINGLE_ENDED_SHIFT; + + mask = ARIZONA_IN1_DMIC_SUP_MASK | + ARIZONA_IN1_MODE_MASK | + ARIZONA_IN1_SINGLE_ENDED_MASK; + break; + } + + regmap_update_bits(arizona->regmap, + ARIZONA_IN1L_CONTROL + (i * 8), + mask, val); + } + return 0; } -EXPORT_SYMBOL_GPL(arizona_init_notifiers); +EXPORT_SYMBOL_GPL(arizona_init_common); + +int arizona_init_vol_limit(struct arizona *arizona) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(arizona->pdata.out_vol_limit); ++i) { + if (arizona->pdata.out_vol_limit[i]) + regmap_update_bits(arizona->regmap, + ARIZONA_DAC_VOLUME_LIMIT_1L + i * 4, + ARIZONA_OUT1L_VOL_LIM_MASK, + arizona->pdata.out_vol_limit[i]); + } + + return 0; +} +EXPORT_SYMBOL_GPL(arizona_init_vol_limit); const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { "None", @@ -2695,6 +2779,80 @@ int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(arizona_lhpf_coeff_put); +int arizona_of_get_audio_pdata(struct arizona *arizona) +{ + struct arizona_pdata *pdata = &arizona->pdata; + struct device_node *np = arizona->dev->of_node; + struct property *prop; + const __be32 *cur; + u32 val; + u32 pdm_val[ARIZONA_MAX_PDM_SPK]; + int ret; + int count = 0; + + count = 0; + of_property_for_each_u32(np, "wlf,inmode", prop, cur, val) { + if (count == ARRAY_SIZE(pdata->inmode)) + break; + + pdata->inmode[count] = val; + count++; + } + + count = 0; + of_property_for_each_u32(np, "wlf,dmic-ref", prop, cur, val) { + if (count == ARRAY_SIZE(pdata->dmic_ref)) + break; + + pdata->dmic_ref[count] = val; + count++; + } + + count = 0; + of_property_for_each_u32(np, "wlf,out-mono", prop, cur, val) { + if (count == ARRAY_SIZE(pdata->out_mono)) + break; + + pdata->out_mono[count] = !!val; + count++; + } + + count = 0; + of_property_for_each_u32(np, "wlf,max-channels-clocked", prop, cur, val) { + if (count == ARRAY_SIZE(pdata->max_channels_clocked)) + break; + + pdata->max_channels_clocked[count] = val; + count++; + } + + count = 0; + of_property_for_each_u32(np, "wlf,out-volume-limit", prop, cur, val) { + if (count == ARRAY_SIZE(pdata->out_vol_limit)) + break; + + pdata->out_vol_limit[count] = val; + count++; + } + + ret = of_property_read_u32_array(np, "wlf,spk-fmt", + pdm_val, ARRAY_SIZE(pdm_val)); + + if (ret >= 0) + for (count = 0; count < ARRAY_SIZE(pdata->spk_fmt); ++count) + pdata->spk_fmt[count] = pdm_val[count]; + + ret = of_property_read_u32_array(np, "wlf,spk-mute", + pdm_val, ARRAY_SIZE(pdm_val)); + + if (ret >= 0) + for (count = 0; count < ARRAY_SIZE(pdata->spk_mute); ++count) + pdata->spk_mute[count] = pdm_val[count]; + + return 0; +} +EXPORT_SYMBOL_GPL(arizona_of_get_audio_pdata); + MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support"); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index 1822e3b3de80..dfdf6d8c9687 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -313,7 +313,9 @@ int arizona_set_fll(struct arizona_fll *fll, int source, int arizona_init_spk(struct snd_soc_codec *codec); int arizona_init_gpio(struct snd_soc_codec *codec); int arizona_init_mono(struct snd_soc_codec *codec); -int arizona_init_notifiers(struct snd_soc_codec *codec); + +int arizona_init_common(struct arizona *arizona); +int arizona_init_vol_limit(struct arizona *arizona); int arizona_init_spk_irqs(struct arizona *arizona); int arizona_free_spk_irqs(struct arizona *arizona); @@ -350,4 +352,6 @@ static inline int arizona_unregister_notifier(struct snd_soc_codec *codec, return blocking_notifier_chain_unregister(&arizona->notifier, nb); } +int arizona_of_get_audio_pdata(struct arizona *arizona); + #endif diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c index 643e37fc218e..5ba0edc19df4 100644 --- a/sound/soc/codecs/cs43130.c +++ b/sound/soc/codecs/cs43130.c @@ -909,6 +909,7 @@ static int cs43130_hw_params(struct snd_pcm_substream *substream, regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2, CS43130_DSD_SRC_MASK, CS43130_DSD_SRC_XSP << CS43130_DSD_SRC_SHIFT); + break; } if (!sclk && cs43130->dais[dai->id].dai_mode == SND_SOC_DAIFMT_CBM_CFM) @@ -1039,6 +1040,7 @@ static int cs43130_pcm_ch_put(struct snd_kcontrol *kcontrol, else regmap_multi_reg_write(cs43130->regmap, pcm_ch_dis_seq, ARRAY_SIZE(pcm_ch_dis_seq)); + break; } return snd_soc_put_enum_double(kcontrol, ucontrol); @@ -1152,6 +1154,7 @@ static int cs43130_dsd_event(struct snd_soc_dapm_widget *w, case CS4399_CHIP_ID: regmap_multi_reg_write(cs43130->regmap, dsd_seq, ARRAY_SIZE(dsd_seq)); + break; } break; case SND_SOC_DAPM_POST_PMU: @@ -1162,6 +1165,7 @@ static int cs43130_dsd_event(struct snd_soc_dapm_widget *w, case CS4399_CHIP_ID: regmap_multi_reg_write(cs43130->regmap, unmute_seq, ARRAY_SIZE(unmute_seq)); + break; } break; case SND_SOC_DAPM_PRE_PMD: @@ -1184,6 +1188,7 @@ static int cs43130_dsd_event(struct snd_soc_dapm_widget *w, regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_1, CS43130_MUTE_MASK, CS43130_MUTE_EN); + break; } break; default: @@ -1206,6 +1211,7 @@ static int cs43130_pcm_event(struct snd_soc_dapm_widget *w, case CS4399_CHIP_ID: regmap_multi_reg_write(cs43130->regmap, pcm_seq, ARRAY_SIZE(pcm_seq)); + break; } break; case SND_SOC_DAPM_POST_PMU: @@ -1216,6 +1222,7 @@ static int cs43130_pcm_event(struct snd_soc_dapm_widget *w, case CS4399_CHIP_ID: regmap_multi_reg_write(cs43130->regmap, unmute_seq, ARRAY_SIZE(unmute_seq)); + break; } break; case SND_SOC_DAPM_PRE_PMD: @@ -1238,6 +1245,7 @@ static int cs43130_pcm_event(struct snd_soc_dapm_widget *w, regmap_update_bits(cs43130->regmap, CS43130_PCM_PATH_CTL_1, CS43130_MUTE_MASK, CS43130_MUTE_EN); + break; } break; default: @@ -1277,6 +1285,7 @@ static int cs43130_dac_event(struct snd_soc_dapm_widget *w, case CS43198_CHIP_ID: regmap_multi_reg_write(cs43130->regmap, pop_free_seq2, ARRAY_SIZE(pop_free_seq2)); + break; } break; case SND_SOC_DAPM_POST_PMU: @@ -1301,6 +1310,7 @@ static int cs43130_dac_event(struct snd_soc_dapm_widget *w, case CS43198_CHIP_ID: usleep_range(12000, 12010); regmap_write(cs43130->regmap, CS43130_DXD13, 0); + break; } regmap_write(cs43130->regmap, CS43130_DXD1, 0); @@ -1311,6 +1321,7 @@ static int cs43130_dac_event(struct snd_soc_dapm_widget *w, case CS4399_CHIP_ID: regmap_multi_reg_write(cs43130->regmap, dac_postpmd_seq, ARRAY_SIZE(dac_postpmd_seq)); + break; } break; default: @@ -2133,6 +2144,7 @@ exit: cs43130_hpload_proc(cs43130, hp_dis_cal_seq2, ARRAY_SIZE(hp_dis_cal_seq2), CS43130_HPLOAD_OFF_INT, ac_idx); + break; } regmap_multi_reg_write(cs43130->regmap, hp_cln_seq, @@ -2543,6 +2555,7 @@ static int cs43130_i2c_probe(struct i2c_client *client, digital_hp_routes; soc_codec_dev_cs43130.component_driver.num_dapm_routes = ARRAY_SIZE(digital_hp_routes); + break; } ret = snd_soc_register_codec(&client->dev, &soc_codec_dev_cs43130, @@ -2586,8 +2599,7 @@ static int cs43130_i2c_remove(struct i2c_client *client) device_remove_file(&client->dev, &dev_attr_hpload_ac_r); } - if (cs43130->reset_gpio) - gpiod_set_value_cansleep(cs43130->reset_gpio, 0); + gpiod_set_value_cansleep(cs43130->reset_gpio, 0); pm_runtime_disable(&client->dev); regulator_bulk_disable(CS43130_NUM_SUPPLIES, cs43130->supplies); diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c index e09fc8f037f1..94c0209977d0 100644 --- a/sound/soc/codecs/cs47l24.c +++ b/sound/soc/codecs/cs47l24.c @@ -1130,7 +1130,6 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec) arizona_init_gpio(codec); arizona_init_mono(codec); - arizona_init_notifiers(codec); ret = wm_adsp2_codec_probe(&priv->core.adsp[1], codec); if (ret) @@ -1230,6 +1229,14 @@ static int cs47l24_probe(struct platform_device *pdev) if (!cs47l24) return -ENOMEM; + if (IS_ENABLED(CONFIG_OF)) { + if (!dev_get_platdata(arizona->dev)) { + ret = arizona_of_get_audio_pdata(arizona); + if (ret < 0) + return ret; + } + } + platform_set_drvdata(pdev, cs47l24); cs47l24->core.arizona = arizona; @@ -1288,6 +1295,11 @@ static int cs47l24_probe(struct platform_device *pdev) return ret; } + arizona_init_common(arizona); + + ret = arizona_init_vol_limit(arizona); + if (ret < 0) + goto err_dsp_irq; ret = arizona_init_spk_irqs(arizona); if (ret < 0) goto err_dsp_irq; diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index cc0b2d2eaf15..41d9b1da27c2 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -1220,6 +1220,7 @@ static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) struct snd_soc_codec *codec = codec_dai->codec; struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec); u8 dai_clk_mode = 0, dai_ctrl = 0; + u8 dai_offset = 0; /* Set master/slave mode */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { @@ -1234,17 +1235,46 @@ static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) } /* Set clock normal/inverted */ - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_NF: - break; - case SND_SOC_DAIFMT_NB_IF: - dai_clk_mode |= DA7213_DAI_WCLK_POL_INV; - break; - case SND_SOC_DAIFMT_IB_NF: - dai_clk_mode |= DA7213_DAI_CLK_POL_INV; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_RIGHT_J: + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + dai_clk_mode |= DA7213_DAI_WCLK_POL_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + dai_clk_mode |= DA7213_DAI_CLK_POL_INV; + break; + case SND_SOC_DAIFMT_IB_IF: + dai_clk_mode |= DA7213_DAI_WCLK_POL_INV | + DA7213_DAI_CLK_POL_INV; + break; + default: + return -EINVAL; + } break; - case SND_SOC_DAIFMT_IB_IF: - dai_clk_mode |= DA7213_DAI_WCLK_POL_INV | DA7213_DAI_CLK_POL_INV; + case SND_SOC_DAI_FORMAT_DSP_A: + case SND_SOC_DAI_FORMAT_DSP_B: + /* The bclk is inverted wrt ASoC conventions */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + dai_clk_mode |= DA7213_DAI_CLK_POL_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + dai_clk_mode |= DA7213_DAI_WCLK_POL_INV | + DA7213_DAI_CLK_POL_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + dai_clk_mode |= DA7213_DAI_WCLK_POL_INV; + break; + default: + return -EINVAL; + } break; default: return -EINVAL; @@ -1261,6 +1291,13 @@ static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) case SND_SOC_DAIFMT_RIGHT_J: dai_ctrl |= DA7213_DAI_FORMAT_RIGHT_J; break; + case SND_SOC_DAI_FORMAT_DSP_A: /* L data MSB after FRM LRC */ + dai_ctrl |= DA7213_DAI_FORMAT_DSP; + dai_offset = 1; + break; + case SND_SOC_DAI_FORMAT_DSP_B: /* L data MSB during FRM LRC */ + dai_ctrl |= DA7213_DAI_FORMAT_DSP; + break; default: return -EINVAL; } @@ -1271,6 +1308,7 @@ static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) snd_soc_write(codec, DA7213_DAI_CLK_MODE, dai_clk_mode); snd_soc_update_bits(codec, DA7213_DAI_CTRL, DA7213_DAI_FORMAT_MASK, dai_ctrl); + snd_soc_write(codec, DA7213_DAI_OFFSET, dai_offset); return 0; } diff --git a/sound/soc/codecs/da7213.h b/sound/soc/codecs/da7213.h index 16ef56f77cd4..5a78dba1dcb5 100644 --- a/sound/soc/codecs/da7213.h +++ b/sound/soc/codecs/da7213.h @@ -188,6 +188,7 @@ #define DA7213_DAI_FORMAT_I2S_MODE (0x0 << 0) #define DA7213_DAI_FORMAT_LEFT_J (0x1 << 0) #define DA7213_DAI_FORMAT_RIGHT_J (0x2 << 0) +#define DA7213_DAI_FORMAT_DSP (0x3 << 0) #define DA7213_DAI_FORMAT_MASK (0x3 << 0) #define DA7213_DAI_WORD_LENGTH_S16_LE (0x0 << 2) #define DA7213_DAI_WORD_LENGTH_S20_LE (0x1 << 2) diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index e824d47cc22b..f3b4f4dfae6a 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -942,7 +942,8 @@ static int hdac_hdmi_create_pin_port_muxs(struct hdac_ext_device *edev, if (!se) return -ENOMEM; - sprintf(kc_name, "Pin %d port %d Input", pin->nid, port->id); + snprintf(kc_name, NAME_SIZE, "Pin %d port %d Input", + pin->nid, port->id); kc->name = devm_kstrdup(&edev->hdac.dev, kc_name, GFP_KERNEL); if (!kc->name) return -ENOMEM; @@ -1452,6 +1453,8 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev, int i, num_nodes; struct hdac_device *hdac = &edev->hdac; struct hdac_hdmi_priv *hdmi = edev->private_data; + struct hdac_hdmi_cvt *temp_cvt, *cvt_next; + struct hdac_hdmi_pin *temp_pin, *pin_next; int ret; hdac_hdmi_skl_enable_all_pins(hdac); @@ -1481,32 +1484,54 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev, case AC_WID_AUD_OUT: ret = hdac_hdmi_add_cvt(edev, nid); if (ret < 0) - return ret; + goto free_widgets; break; case AC_WID_PIN: ret = hdac_hdmi_add_pin(edev, nid); if (ret < 0) - return ret; + goto free_widgets; break; } } hdac->end_nid = nid; - if (!hdmi->num_pin || !hdmi->num_cvt) - return -EIO; + if (!hdmi->num_pin || !hdmi->num_cvt) { + ret = -EIO; + goto free_widgets; + } ret = hdac_hdmi_create_dais(hdac, dais, hdmi, hdmi->num_cvt); if (ret) { dev_err(&hdac->dev, "Failed to create dais with err: %d\n", ret); - return ret; + goto free_widgets; } *num_dais = hdmi->num_cvt; + ret = hdac_hdmi_init_dai_map(edev); + if (ret < 0) + goto free_widgets; + + return ret; + +free_widgets: + list_for_each_entry_safe(temp_cvt, cvt_next, &hdmi->cvt_list, head) { + list_del(&temp_cvt->head); + kfree(temp_cvt->name); + kfree(temp_cvt); + } + + list_for_each_entry_safe(temp_pin, pin_next, &hdmi->pin_list, head) { + for (i = 0; i < temp_pin->num_ports; i++) + temp_pin->ports[i].pin = NULL; + kfree(temp_pin->ports); + list_del(&temp_pin->head); + kfree(temp_pin); + } - return hdac_hdmi_init_dai_map(edev); + return ret; } static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe) @@ -1894,6 +1919,9 @@ static void hdac_hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx, struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); struct hdac_hdmi_port *port; + if (!pcm) + return; + if (list_empty(&pcm->port_list)) return; @@ -1912,6 +1940,9 @@ static bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx) struct hdac_hdmi_priv *hdmi = edev->private_data; struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); + if (!pcm) + return false; + if (list_empty(&pcm->port_list)) return false; @@ -1925,6 +1956,9 @@ static int hdac_hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx) struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx); struct hdac_hdmi_port *port; + if (!pcm) + return 0; + if (list_empty(&pcm->port_list)) return 0; @@ -1978,6 +2012,9 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev) hdmi_priv->chmap.ops.is_pcm_attached = is_hdac_hdmi_pcm_attached; hdmi_priv->chmap.ops.get_spk_alloc = hdac_hdmi_get_spk_alloc; + if (!hdac_id) + return -ENODEV; + if (hdac_id->driver_data) hdmi_priv->drv_data = (struct hdac_hdmi_drv_data *)hdac_id->driver_data; diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index 3abf82563408..5672e516bec3 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -303,11 +303,8 @@ enum { static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); - uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; - uinfo->count = sizeof(hcp->eld); + uinfo->count = FIELD_SIZEOF(struct hdmi_codec_priv, eld); return 0; } diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 13bcfb1ef9b4..f5075d1f79e6 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -2115,7 +2115,7 @@ static void max98090_pll_work(struct work_struct *work) if (!snd_soc_codec_is_active(codec)) return; - dev_info(codec->dev, "PLL unlocked\n"); + dev_info_ratelimited(codec->dev, "PLL unlocked\n"); /* Toggle shutdown OFF then ON */ snd_soc_update_bits(codec, M98090_REG_DEVICE_SHUTDOWN, diff --git a/sound/soc/codecs/max98925.c b/sound/soc/codecs/max98925.c index 327eaa25c9bd..921f95fc396d 100644 --- a/sound/soc/codecs/max98925.c +++ b/sound/soc/codecs/max98925.c @@ -579,7 +579,7 @@ static int max98925_i2c_probe(struct i2c_client *i2c, ret = PTR_ERR(max98925->regmap); dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret); - goto err_out; + return ret; } if (!of_property_read_u32(i2c->dev.of_node, "vmon-slot-no", &value)) { @@ -596,16 +596,20 @@ static int max98925_i2c_probe(struct i2c_client *i2c, } max98925->i_slot = value; } - ret = regmap_read(max98925->regmap, - MAX98925_REV_VERSION, ®); - if ((ret < 0) || - ((reg != MAX98925_VERSION) && - (reg != MAX98925_VERSION1))) { - dev_err(&i2c->dev, - "device initialization error (%d 0x%02X)\n", + + ret = regmap_read(max98925->regmap, MAX98925_REV_VERSION, ®); + if (ret < 0) { + dev_err(&i2c->dev, "Read revision failed\n"); + return ret; + } + + if ((reg != MAX98925_VERSION) && (reg != MAX98925_VERSION1)) { + ret = -ENODEV; + dev_err(&i2c->dev, "Invalid revision (%d 0x%02X)\n", ret, reg); - goto err_out; + return ret; } + dev_info(&i2c->dev, "device version 0x%02X\n", reg); ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98925, @@ -613,7 +617,6 @@ static int max98925_i2c_probe(struct i2c_client *i2c, if (ret < 0) dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); -err_out: return ret; } diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c index d9dbbe72f8ad..a1d39353719d 100644 --- a/sound/soc/codecs/max98927.c +++ b/sound/soc/codecs/max98927.c @@ -1,7 +1,7 @@ /* * max98927.c -- MAX98927 ALSA Soc Audio driver * - * Copyright (C) 2016 Maxim Integrated Products + * Copyright (C) 2016-2017 Maxim Integrated Products * Author: Ryan Lee <ryans.lee@maximintegrated.com> * * This program is free software; you can redistribute it and/or modify it @@ -146,6 +146,7 @@ static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec); unsigned int mode = 0; unsigned int format = 0; + bool use_pdm = false; unsigned int invert = 0; dev_dbg(codec->dev, "%s: fmt 0x%08X\n", __func__, fmt); @@ -187,22 +188,27 @@ static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) /* interface format */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: - max98927->iface |= SND_SOC_DAIFMT_I2S; format = MAX98927_PCM_FORMAT_I2S; break; case SND_SOC_DAIFMT_LEFT_J: - max98927->iface |= SND_SOC_DAIFMT_LEFT_J; format = MAX98927_PCM_FORMAT_LJ; break; + case SND_SOC_DAIFMT_DSP_A: + format = MAX98927_PCM_FORMAT_TDM_MODE1; + break; + case SND_SOC_DAIFMT_DSP_B: + format = MAX98927_PCM_FORMAT_TDM_MODE0; + break; case SND_SOC_DAIFMT_PDM: - max98927->iface |= SND_SOC_DAIFMT_PDM; + use_pdm = true; break; default: return -EINVAL; } + max98927->iface = fmt & SND_SOC_DAIFMT_FORMAT_MASK; - /* pcm channel configuration */ - if (max98927->iface & (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J)) { + if (!use_pdm) { + /* pcm channel configuration */ regmap_update_bits(max98927->regmap, MAX98927_R0018_PCM_RX_EN_A, MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN, @@ -217,13 +223,11 @@ static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) MAX98927_R003B_SPK_SRC_SEL, MAX98927_SPK_SRC_MASK, 0); - } else regmap_update_bits(max98927->regmap, - MAX98927_R0018_PCM_RX_EN_A, - MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN, 0); - - /* pdm channel configuration */ - if (max98927->iface & SND_SOC_DAIFMT_PDM) { + MAX98927_R0035_PDM_RX_CTRL, + MAX98927_PDM_RX_EN_MASK, 0); + } else { + /* pdm channel configuration */ regmap_update_bits(max98927->regmap, MAX98927_R0035_PDM_RX_CTRL, MAX98927_PDM_RX_EN_MASK, 1); @@ -231,10 +235,11 @@ static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) regmap_update_bits(max98927->regmap, MAX98927_R003B_SPK_SRC_SEL, MAX98927_SPK_SRC_MASK, 3); - } else + regmap_update_bits(max98927->regmap, - MAX98927_R0035_PDM_RX_CTRL, - MAX98927_PDM_RX_EN_MASK, 0); + MAX98927_R0018_PCM_RX_EN_A, + MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN, 0); + } return 0; } @@ -245,6 +250,21 @@ static const int rate_table[] = { 13000000, 19200000, }; +/* BCLKs per LRCLK */ +static const int bclk_sel_table[] = { + 32, 48, 64, 96, 128, 192, 256, 384, 512, +}; + +static int max98927_get_bclk_sel(int bclk) +{ + int i; + /* match BCLKs per LRCLK */ + for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) { + if (bclk_sel_table[i] == bclk) + return i + 2; + } + return 0; +} static int max98927_set_clock(struct max98927_priv *max98927, struct snd_pcm_hw_params *params) { @@ -270,23 +290,20 @@ static int max98927_set_clock(struct max98927_priv *max98927, i << MAX98927_PCM_MASTER_MODE_MCLK_RATE_SHIFT); } - switch (blr_clk_ratio) { - case 32: - value = 2; - break; - case 48: - value = 3; - break; - case 64: - value = 4; - break; - default: - return -EINVAL; + if (!max98927->tdm_mode) { + /* BCLK configuration */ + value = max98927_get_bclk_sel(blr_clk_ratio); + if (!value) { + dev_err(codec->dev, "format unsupported %d\n", + params_format(params)); + return -EINVAL; + } + + regmap_update_bits(max98927->regmap, + MAX98927_R0022_PCM_CLK_SETUP, + MAX98927_PCM_CLK_SETUP_BSEL_MASK, + value); } - regmap_update_bits(max98927->regmap, - MAX98927_R0022_PCM_CLK_SETUP, - MAX98927_PCM_CLK_SETUP_BSEL_MASK, - value); return 0; } @@ -386,6 +403,78 @@ err: return -EINVAL; } +static int max98927_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_codec *codec = dai->codec; + struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec); + int bsel = 0; + unsigned int chan_sz = 0; + + max98927->tdm_mode = true; + + /* BCLK configuration */ + bsel = max98927_get_bclk_sel(slots * slot_width); + if (bsel == 0) { + dev_err(codec->dev, "BCLK %d not supported\n", + slots * slot_width); + return -EINVAL; + } + + regmap_update_bits(max98927->regmap, + MAX98927_R0022_PCM_CLK_SETUP, + MAX98927_PCM_CLK_SETUP_BSEL_MASK, + bsel); + + /* Channel size configuration */ + switch (slot_width) { + case 16: + chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_16; + break; + case 24: + chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_24; + break; + case 32: + chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_32; + break; + default: + dev_err(codec->dev, "format unsupported %d\n", + slot_width); + return -EINVAL; + } + + regmap_update_bits(max98927->regmap, + MAX98927_R0020_PCM_MODE_CFG, + MAX98927_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + + /* Rx slot configuration */ + regmap_write(max98927->regmap, + MAX98927_R0018_PCM_RX_EN_A, + rx_mask & 0xFF); + regmap_write(max98927->regmap, + MAX98927_R0019_PCM_RX_EN_B, + (rx_mask & 0xFF00) >> 8); + + /* Tx slot configuration */ + regmap_write(max98927->regmap, + MAX98927_R001A_PCM_TX_EN_A, + tx_mask & 0xFF); + regmap_write(max98927->regmap, + MAX98927_R001B_PCM_TX_EN_B, + (tx_mask & 0xFF00) >> 8); + + /* Tx slot Hi-Z configuration */ + regmap_write(max98927->regmap, + MAX98927_R001C_PCM_TX_HIZ_CTRL_A, + ~tx_mask & 0xFF); + regmap_write(max98927->regmap, + MAX98927_R001D_PCM_TX_HIZ_CTRL_B, + (~tx_mask & 0xFF00) >> 8); + + return 0; +} + #define MAX98927_RATES SNDRV_PCM_RATE_8000_48000 #define MAX98927_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ @@ -405,6 +494,7 @@ static const struct snd_soc_dai_ops max98927_dai_ops = { .set_sysclk = max98927_dai_set_sysclk, .set_fmt = max98927_dai_set_fmt, .hw_params = max98927_dai_hw_params, + .set_tdm_slot = max98927_dai_tdm_slot, }; static int max98927_dac_event(struct snd_soc_dapm_widget *w, @@ -414,6 +504,9 @@ static int max98927_dac_event(struct snd_soc_dapm_widget *w, struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec); switch (event) { + case SND_SOC_DAPM_PRE_PMU: + max98927->tdm_mode = 0; + break; case SND_SOC_DAPM_POST_PMU: regmap_update_bits(max98927->regmap, MAX98927_R003A_AMP_EN, diff --git a/sound/soc/codecs/max98927.h b/sound/soc/codecs/max98927.h index ece6a608cbe1..9ea839735433 100644 --- a/sound/soc/codecs/max98927.h +++ b/sound/soc/codecs/max98927.h @@ -1,7 +1,7 @@ /* * max98927.h -- MAX98927 ALSA Soc Audio driver * - * Copyright 2013-15 Maxim Integrated Products + * Copyright (C) 2016-2017 Maxim Integrated Products * Author: Ryan Lee <ryans.lee@maximintegrated.com> * * This program is free software; you can redistribute it and/or modify it @@ -161,7 +161,9 @@ #define MAX98927_PCM_MODE_CFG_FORMAT_SHIFT (3) #define MAX98927_PCM_FORMAT_I2S (0x0 << 0) #define MAX98927_PCM_FORMAT_LJ (0x1 << 0) - +#define MAX98927_PCM_FORMAT_TDM_MODE0 (0x3 << 0) +#define MAX98927_PCM_FORMAT_TDM_MODE1 (0x4 << 0) +#define MAX98927_PCM_FORMAT_TDM_MODE2 (0x5 << 0) #define MAX98927_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6) #define MAX98927_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6) #define MAX98927_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6) @@ -268,5 +270,6 @@ struct max98927_priv { unsigned int iface; unsigned int master; unsigned int digital_gain; + bool tdm_mode; }; #endif diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c index 549c269acc7d..5f3c42c4f74a 100644 --- a/sound/soc/codecs/msm8916-wcd-analog.c +++ b/sound/soc/codecs/msm8916-wcd-analog.c @@ -104,7 +104,7 @@ #define CDC_A_MICB_1_VAL (0xf141) #define MICB_MIN_VAL 1600 #define MICB_STEP_SIZE 50 -#define MICB_VOLTAGE_REGVAL(v) ((v - MICB_MIN_VAL)/MICB_STEP_SIZE) +#define MICB_VOLTAGE_REGVAL(v) (((v - MICB_MIN_VAL)/MICB_STEP_SIZE) << 3) #define MICB_1_VAL_MICB_OUT_VAL_MASK GENMASK(7, 3) #define MICB_1_VAL_MICB_OUT_VAL_V2P70V ((0x16) << 3) #define MICB_1_VAL_MICB_OUT_VAL_V1P80V ((0x4) << 3) @@ -285,7 +285,7 @@ struct pm8916_wcd_analog_priv { u16 codec_version; bool mbhc_btn_enabled; /* special event to detect accessory type */ - bool mbhc_btn0_pressed; + int mbhc_btn0_released; bool detect_accessory_type; struct clk *mclk; struct snd_soc_codec *codec; @@ -349,8 +349,9 @@ static void pm8916_wcd_analog_micbias_enable(struct snd_soc_codec *codec) | MICB_1_CTL_EXT_PRECHARG_EN_ENABLE); if (wcd->micbias_mv) { - snd_soc_write(codec, CDC_A_MICB_1_VAL, - MICB_VOLTAGE_REGVAL(wcd->micbias_mv)); + snd_soc_update_bits(codec, CDC_A_MICB_1_VAL, + MICB_1_VAL_MICB_OUT_VAL_MASK, + MICB_VOLTAGE_REGVAL(wcd->micbias_mv)); /* * Special headset needs MICBIAS as 2.7V so wait for * 50 msec for the MICBIAS to reach 2.7 volts. @@ -443,50 +444,6 @@ static int pm8916_wcd_analog_enable_micbias_int1(struct wcd->micbias1_cap_mode); } -static void pm8916_wcd_setup_mbhc(struct pm8916_wcd_analog_priv *wcd) -{ - struct snd_soc_codec *codec = wcd->codec; - u32 plug_type = 0; - u32 int_en_mask; - - snd_soc_write(codec, CDC_A_MBHC_DET_CTL_1, - CDC_A_MBHC_DET_CTL_L_DET_EN | - CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_INSERTION | - CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_AUTO | - CDC_A_MBHC_DET_CTL_MBHC_BIAS_EN); - - if (wcd->hphl_jack_type_normally_open) - plug_type |= CDC_A_HPHL_PLUG_TYPE_NO; - - if (wcd->gnd_jack_type_normally_open) - plug_type |= CDC_A_GND_PLUG_TYPE_NO; - - snd_soc_write(codec, CDC_A_MBHC_DET_CTL_2, - CDC_A_MBHC_DET_CTL_HS_L_DET_PULL_UP_CTRL_I_3P0 | - CDC_A_MBHC_DET_CTL_HS_L_DET_COMPA_CTRL_V0P9_VDD | - plug_type | - CDC_A_MBHC_DET_CTL_HPHL_100K_TO_GND_EN); - - - snd_soc_write(codec, CDC_A_MBHC_DBNC_TIMER, - CDC_A_MBHC_DBNC_TIMER_INSREM_DBNC_T_256_MS | - CDC_A_MBHC_DBNC_TIMER_BTN_DBNC_T_16MS); - - /* enable MBHC clock */ - snd_soc_update_bits(codec, CDC_D_CDC_DIG_CLK_CTL, - DIG_CLK_CTL_D_MBHC_CLK_EN_MASK, - DIG_CLK_CTL_D_MBHC_CLK_EN); - - int_en_mask = MBHC_SWITCH_INT; - if (wcd->mbhc_btn_enabled) - int_en_mask |= MBHC_BUTTON_PRESS_DET | MBHC_BUTTON_RELEASE_DET; - - snd_soc_update_bits(codec, CDC_D_INT_EN_CLR, int_en_mask, 0); - snd_soc_update_bits(codec, CDC_D_INT_EN_SET, int_en_mask, int_en_mask); - wcd->mbhc_btn0_pressed = false; - wcd->detect_accessory_type = true; -} - static int pm8916_mbhc_configure_bias(struct pm8916_wcd_analog_priv *priv, bool micbias2_enabled) { @@ -534,6 +491,56 @@ static int pm8916_mbhc_configure_bias(struct pm8916_wcd_analog_priv *priv, return 0; } +static void pm8916_wcd_setup_mbhc(struct pm8916_wcd_analog_priv *wcd) +{ + struct snd_soc_codec *codec = wcd->codec; + bool micbias_enabled = false; + u32 plug_type = 0; + u32 int_en_mask; + + snd_soc_write(codec, CDC_A_MBHC_DET_CTL_1, + CDC_A_MBHC_DET_CTL_L_DET_EN | + CDC_A_MBHC_DET_CTL_MECH_DET_TYPE_INSERTION | + CDC_A_MBHC_DET_CTL_MIC_CLAMP_CTL_AUTO | + CDC_A_MBHC_DET_CTL_MBHC_BIAS_EN); + + if (wcd->hphl_jack_type_normally_open) + plug_type |= CDC_A_HPHL_PLUG_TYPE_NO; + + if (wcd->gnd_jack_type_normally_open) + plug_type |= CDC_A_GND_PLUG_TYPE_NO; + + snd_soc_write(codec, CDC_A_MBHC_DET_CTL_2, + CDC_A_MBHC_DET_CTL_HS_L_DET_PULL_UP_CTRL_I_3P0 | + CDC_A_MBHC_DET_CTL_HS_L_DET_COMPA_CTRL_V0P9_VDD | + plug_type | + CDC_A_MBHC_DET_CTL_HPHL_100K_TO_GND_EN); + + + snd_soc_write(codec, CDC_A_MBHC_DBNC_TIMER, + CDC_A_MBHC_DBNC_TIMER_INSREM_DBNC_T_256_MS | + CDC_A_MBHC_DBNC_TIMER_BTN_DBNC_T_16MS); + + /* enable MBHC clock */ + snd_soc_update_bits(codec, CDC_D_CDC_DIG_CLK_CTL, + DIG_CLK_CTL_D_MBHC_CLK_EN_MASK, + DIG_CLK_CTL_D_MBHC_CLK_EN); + + if (snd_soc_read(codec, CDC_A_MICB_2_EN) & CDC_A_MICB_2_EN_ENABLE) + micbias_enabled = true; + + pm8916_mbhc_configure_bias(wcd, micbias_enabled); + + int_en_mask = MBHC_SWITCH_INT; + if (wcd->mbhc_btn_enabled) + int_en_mask |= MBHC_BUTTON_PRESS_DET | MBHC_BUTTON_RELEASE_DET; + + snd_soc_update_bits(codec, CDC_D_INT_EN_CLR, int_en_mask, 0); + snd_soc_update_bits(codec, CDC_D_INT_EN_SET, int_en_mask, int_en_mask); + wcd->mbhc_btn0_released = false; + wcd->detect_accessory_type = true; +} + static int pm8916_wcd_analog_enable_micbias_int2(struct snd_soc_dapm_widget *w, struct snd_kcontrol @@ -614,6 +621,7 @@ static int pm8916_wcd_analog_enable_adc(struct snd_soc_dapm_widget *w, case CDC_A_TX_2_EN: snd_soc_update_bits(codec, CDC_A_MICB_1_CTL, MICB_1_CTL_CFILT_REF_SEL_MASK, 0); + /* fall through */ case CDC_A_TX_3_EN: snd_soc_update_bits(codec, CDC_D_CDC_CONN_TX2_CTL, CONN_TX2_SERIAL_TX2_MUX, @@ -950,7 +958,7 @@ static irqreturn_t mbhc_btn_release_irq_handler(int irq, void *arg) /* check if its BTN0 thats released */ if ((val != -1) && !(val & CDC_A_MBHC_RESULT_1_BTN_RESULT_MASK)) - priv->mbhc_btn0_pressed = false; + priv->mbhc_btn0_released = true; } else { snd_soc_jack_report(priv->jack, 0, btn_mask); @@ -983,9 +991,7 @@ static irqreturn_t mbhc_btn_press_irq_handler(int irq, void *arg) break; case 0x0: /* handle BTN_0 specially for type detection */ - if (priv->detect_accessory_type) - priv->mbhc_btn0_pressed = true; - else + if (!priv->detect_accessory_type) snd_soc_jack_report(priv->jack, SND_JACK_BTN_0, btn_mask); break; @@ -1029,19 +1035,19 @@ static irqreturn_t pm8916_mbhc_switch_irq_handler(int irq, void *arg) * both press and release event received then its * a headset. */ - if (priv->mbhc_btn0_pressed) + if (priv->mbhc_btn0_released) snd_soc_jack_report(priv->jack, - SND_JACK_HEADPHONE, hs_jack_mask); + SND_JACK_HEADSET, hs_jack_mask); else snd_soc_jack_report(priv->jack, - SND_JACK_HEADSET, hs_jack_mask); + SND_JACK_HEADPHONE, hs_jack_mask); priv->detect_accessory_type = false; } else { /* removal */ snd_soc_jack_report(priv->jack, 0, hs_jack_mask); priv->detect_accessory_type = true; - priv->mbhc_btn0_pressed = false; + priv->mbhc_btn0_released = false; } return IRQ_HANDLED; @@ -1241,6 +1247,8 @@ static const struct of_device_id pm8916_wcd_analog_spmi_match_table[] = { { } }; +MODULE_DEVICE_TABLE(of, pm8916_wcd_analog_spmi_match_table); + static struct platform_driver pm8916_wcd_analog_spmi_driver = { .driver = { .name = "qcom,pm8916-wcd-spmi-codec", diff --git a/sound/soc/codecs/msm8916-wcd-digital.c b/sound/soc/codecs/msm8916-wcd-digital.c index 66df8f810f0d..a10a724eb448 100644 --- a/sound/soc/codecs/msm8916-wcd-digital.c +++ b/sound/soc/codecs/msm8916-wcd-digital.c @@ -238,7 +238,7 @@ static const struct soc_enum rx_mix2_inp1_chain_enum = SOC_ENUM_SINGLE( static const struct soc_enum rx2_mix1_inp_enum[] = { SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B1_CTL, 0, 6, rx_mix1_text), SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B1_CTL, 3, 6, rx_mix1_text), - SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B1_CTL, 0, 6, rx_mix1_text), + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX2_B2_CTL, 0, 6, rx_mix1_text), }; /* RX2 MIX2 */ @@ -249,7 +249,7 @@ static const struct soc_enum rx2_mix2_inp1_chain_enum = SOC_ENUM_SINGLE( static const struct soc_enum rx3_mix1_inp_enum[] = { SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B1_CTL, 0, 6, rx_mix1_text), SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B1_CTL, 3, 6, rx_mix1_text), - SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B1_CTL, 0, 6, rx_mix1_text), + SOC_ENUM_SINGLE(LPASS_CDC_CONN_RX3_B2_CTL, 0, 6, rx_mix1_text), }; /* DEC */ diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c index dbff416e38be..5f9c069569d5 100644 --- a/sound/soc/codecs/pcm512x-i2c.c +++ b/sound/soc/codecs/pcm512x-i2c.c @@ -1,7 +1,7 @@ /* * Driver for the PCM512x CODECs * - * Author: Mark Brown <broonie@linaro.org> + * Author: Mark Brown <broonie@kernel.org> * Copyright 2014 Linaro Ltd * * This program is free software; you can redistribute it and/or @@ -75,5 +75,5 @@ static struct i2c_driver pcm512x_i2c_driver = { module_i2c_driver(pcm512x_i2c_driver); MODULE_DESCRIPTION("ASoC PCM512x codec driver - I2C"); -MODULE_AUTHOR("Mark Brown <broonie@linaro.org>"); +MODULE_AUTHOR("Mark Brown <broonie@kernel.org>"); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm512x-spi.c b/sound/soc/codecs/pcm512x-spi.c index 712ed6598c48..25c63510ae15 100644 --- a/sound/soc/codecs/pcm512x-spi.c +++ b/sound/soc/codecs/pcm512x-spi.c @@ -1,7 +1,7 @@ /* * Driver for the PCM512x CODECs * - * Author: Mark Brown <broonie@linaro.org> + * Author: Mark Brown <broonie@kernel.org> * Copyright 2014 Linaro Ltd * * This program is free software; you can redistribute it and/or diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 68feae262476..e0f3556d3872 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -1,7 +1,7 @@ /* * Driver for the PCM512x CODECs * - * Author: Mark Brown <broonie@linaro.org> + * Author: Mark Brown <broonie@kernel.org> * Copyright 2014 Linaro Ltd * * This program is free software; you can redistribute it and/or @@ -1602,5 +1602,5 @@ const struct dev_pm_ops pcm512x_pm_ops = { EXPORT_SYMBOL_GPL(pcm512x_pm_ops); MODULE_DESCRIPTION("ASoC PCM512x codec driver"); -MODULE_AUTHOR("Mark Brown <broonie@linaro.org>"); +MODULE_AUTHOR("Mark Brown <broonie@kernel.org>"); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm512x.h b/sound/soc/codecs/pcm512x.h index b7c310207223..d70d9c0c2088 100644 --- a/sound/soc/codecs/pcm512x.h +++ b/sound/soc/codecs/pcm512x.h @@ -1,7 +1,7 @@ /* * Driver for the PCM512x CODECs * - * Author: Mark Brown <broonie@linaro.org> + * Author: Mark Brown <broonie@kernel.org> * Copyright 2014 Linaro Ltd * * This program is free software; you can redistribute it and/or diff --git a/sound/soc/codecs/rl6231.c b/sound/soc/codecs/rl6231.c index 7b447d0b173a..974a9040651d 100644 --- a/sound/soc/codecs/rl6231.c +++ b/sound/soc/codecs/rl6231.c @@ -71,7 +71,7 @@ EXPORT_SYMBOL_GPL(rl6231_get_pre_div); */ int rl6231_calc_dmic_clk(int rate) { - int div[] = {2, 3, 4, 6, 8, 12}; + static const int div[] = {2, 3, 4, 6, 8, 12}; int i; if (rate < 1000000 * div[0]) { @@ -189,7 +189,8 @@ EXPORT_SYMBOL_GPL(rl6231_pll_calc); int rl6231_get_clk_info(int sclk, int rate) { - int i, pd[] = {1, 2, 3, 4, 6, 8, 12, 16}; + int i; + static const int pd[] = {1, 2, 3, 4, 6, 8, 12, 16}; if (sclk <= 0 || rate <= 0) return -EINVAL; diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c index 12f2ecf3a4fe..2df91db765ac 100644 --- a/sound/soc/codecs/rt5514-spi.c +++ b/sound/soc/codecs/rt5514-spi.c @@ -147,8 +147,13 @@ done: static void rt5514_schedule_copy(struct rt5514_dsp *rt5514_dsp) { + size_t period_bytes; u8 buf[8]; + if (!rt5514_dsp->substream) + return; + + period_bytes = snd_pcm_lib_period_bytes(rt5514_dsp->substream); rt5514_dsp->get_size = 0; /** @@ -176,6 +181,10 @@ static void rt5514_schedule_copy(struct rt5514_dsp *rt5514_dsp) rt5514_dsp->buf_size = rt5514_dsp->buf_limit - rt5514_dsp->buf_base; + if (rt5514_dsp->buf_size % period_bytes) + rt5514_dsp->buf_size = (rt5514_dsp->buf_size / period_bytes) * + period_bytes; + if (rt5514_dsp->buf_base && rt5514_dsp->buf_limit && rt5514_dsp->buf_rp && rt5514_dsp->buf_size) schedule_delayed_work(&rt5514_dsp->copy_work, 0); @@ -447,9 +456,45 @@ static int rt5514_spi_probe(struct spi_device *spi) return ret; } + device_init_wakeup(&spi->dev, true); + + return 0; +} + +static int __maybe_unused rt5514_suspend(struct device *dev) +{ + int irq = to_spi_device(dev)->irq; + + if (device_may_wakeup(dev)) + enable_irq_wake(irq); + + return 0; +} + +static int __maybe_unused rt5514_resume(struct device *dev) +{ + struct snd_soc_platform *platform = snd_soc_lookup_platform(dev); + struct rt5514_dsp *rt5514_dsp = + snd_soc_platform_get_drvdata(platform); + int irq = to_spi_device(dev)->irq; + u8 buf[8]; + + if (device_may_wakeup(dev)) + disable_irq_wake(irq); + + if (rt5514_dsp->substream) { + rt5514_spi_burst_read(RT5514_IRQ_CTRL, (u8 *)&buf, sizeof(buf)); + if (buf[0] & RT5514_IRQ_STATUS_BIT) + rt5514_schedule_copy(rt5514_dsp); + } + return 0; } +static const struct dev_pm_ops rt5514_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(rt5514_suspend, rt5514_resume) +}; + static const struct of_device_id rt5514_of_match[] = { { .compatible = "realtek,rt5514", }, {}, @@ -459,6 +504,7 @@ MODULE_DEVICE_TABLE(of, rt5514_of_match); static struct spi_driver rt5514_spi_driver = { .driver = { .name = "rt5514", + .pm = &rt5514_pm_ops, .of_match_table = of_match_ptr(rt5514_of_match), }, .probe = rt5514_spi_probe, diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c index d7956ababd11..2a5b5d74e697 100644 --- a/sound/soc/codecs/rt5514.c +++ b/sound/soc/codecs/rt5514.c @@ -1143,7 +1143,7 @@ static const struct acpi_device_id rt5514_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, rt5514_acpi_match); #endif -static int rt5514_parse_dt(struct rt5514_priv *rt5514, struct device *dev) +static int rt5514_parse_dp(struct rt5514_priv *rt5514, struct device *dev) { device_property_read_u32(dev, "realtek,dmic-init-delay-ms", &rt5514->pdata.dmic_init_delay); @@ -1183,8 +1183,8 @@ static int rt5514_i2c_probe(struct i2c_client *i2c, if (pdata) rt5514->pdata = *pdata; - else if (i2c->dev.of_node) - rt5514_parse_dt(rt5514, &i2c->dev); + else + rt5514_parse_dp(rt5514, &i2c->dev); rt5514->i2c_regmap = devm_regmap_init_i2c(i2c, &rt5514_i2c_regmap); if (IS_ERR(rt5514->i2c_regmap)) { diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index a98647ac497c..f020d2d1eef4 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -55,6 +55,8 @@ MODULE_PARM_DESC(quirk, "RT5645 pdata quirk override"); #define RT5645_HWEQ_NUM 57 +#define TIME_TO_POWER_MS 400 + static const struct regmap_range_cfg rt5645_ranges[] = { { .name = "PR", @@ -432,6 +434,7 @@ struct rt5645_priv { int jack_type; bool en_button_func; bool hp_on; + int v_id; }; static int rt5645_reset(struct snd_soc_codec *codec) @@ -2516,9 +2519,7 @@ static const struct snd_soc_dapm_route rt5645_dapm_routes[] = { { "SPKVOL L", "Switch", "SPK MIXL" }, { "SPKVOL R", "Switch", "SPK MIXR" }, - { "SPOL MIX", "DAC R1 Switch", "DAC R1" }, { "SPOL MIX", "DAC L1 Switch", "DAC L1" }, - { "SPOL MIX", "SPKVOL R Switch", "SPKVOL R" }, { "SPOL MIX", "SPKVOL L Switch", "SPKVOL L" }, { "SPOR MIX", "DAC R1 Switch", "DAC R1" }, { "SPOR MIX", "SPKVOL R Switch", "SPKVOL R" }, @@ -2707,6 +2708,11 @@ static const struct snd_soc_dapm_route rt5645_specific_dapm_routes[] = { { "DAC R2 Mux", "IF1 DAC", "RT5645 IF1 DAC2 R Mux" }, }; +static const struct snd_soc_dapm_route rt5645_old_dapm_routes[] = { + { "SPOL MIX", "DAC R1 Switch", "DAC R1" }, + { "SPOL MIX", "SPKVOL R Switch", "SPKVOL R" }, +}; + static int rt5645_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { @@ -3340,9 +3346,9 @@ static irqreturn_t rt5645_irq(int irq, void *data) return IRQ_HANDLED; } -static void rt5645_btn_check_callback(unsigned long data) +static void rt5645_btn_check_callback(struct timer_list *t) { - struct rt5645_priv *rt5645 = (struct rt5645_priv *)data; + struct rt5645_priv *rt5645 = from_timer(rt5645, t, btn_check_timer); queue_delayed_work(system_power_efficient_wq, &rt5645->jack_detect_work, msecs_to_jiffies(5)); @@ -3363,6 +3369,11 @@ static int rt5645_probe(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(dapm, rt5645_specific_dapm_routes, ARRAY_SIZE(rt5645_specific_dapm_routes)); + if (rt5645->v_id < 3) { + snd_soc_dapm_add_routes(dapm, + rt5645_old_dapm_routes, + ARRAY_SIZE(rt5645_old_dapm_routes)); + } break; case CODEC_TYPE_RT5650: snd_soc_dapm_new_controls(dapm, @@ -3637,14 +3648,14 @@ static const struct dmi_system_id dmi_platform_gpd_win[] = { {} }; -static struct rt5645_platform_data general_platform_data2 = { +static const struct rt5645_platform_data general_platform_data2 = { .dmic1_data_pin = RT5645_DMIC_DATA_IN2N, .dmic2_data_pin = RT5645_DMIC2_DISABLE, .jd_mode = 3, .inv_jd1_1 = true, }; -static struct dmi_system_id dmi_platform_asus_t100ha[] = { +static const struct dmi_system_id dmi_platform_asus_t100ha[] = { { .ident = "ASUS T100HAN", .matches = { @@ -3655,11 +3666,11 @@ static struct dmi_system_id dmi_platform_asus_t100ha[] = { { } }; -static struct rt5645_platform_data minix_z83_4_platform_data = { +static const struct rt5645_platform_data minix_z83_4_platform_data = { .jd_mode = 3, }; -static struct dmi_system_id dmi_platform_minix_z83_4[] = { +static const struct dmi_system_id dmi_platform_minix_z83_4[] = { { .ident = "MINIX Z83-4", .matches = { @@ -3775,6 +3786,12 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, ret); return ret; } + + /* + * Read after 400msec, as it is the interval required between + * read and power On. + */ + msleep(TIME_TO_POWER_MS); regmap_read(regmap, RT5645_VENDOR_ID2, &val); switch (val) { @@ -3803,6 +3820,9 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, regmap_write(rt5645->regmap, RT5645_RESET, 0); + regmap_read(regmap, RT5645_VENDOR_ID, &val); + rt5645->v_id = val & 0xff; + ret = regmap_register_patch(rt5645->regmap, init_list, ARRAY_SIZE(init_list)); if (ret != 0) @@ -3934,8 +3954,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c, regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2, RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV); } - setup_timer(&rt5645->btn_check_timer, - rt5645_btn_check_callback, (unsigned long)rt5645); + timer_setup(&rt5645->btn_check_timer, rt5645_btn_check_callback, 0); INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work); INIT_DELAYED_WORK(&rt5645->rcclock_work, rt5645_rcclock_work); diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c index da60b28ba3df..831b297978a4 100644 --- a/sound/soc/codecs/rt5651.c +++ b/sound/soc/codecs/rt5651.c @@ -19,6 +19,7 @@ #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/acpi.h> +#include <linux/dmi.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -26,10 +27,15 @@ #include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> +#include <sound/jack.h> #include "rl6231.h" #include "rt5651.h" +#define RT5651_JD_MAP(quirk) ((quirk) & GENMASK(7, 0)) +#define RT5651_IN2_DIFF BIT(16) +#define RT5651_DMIC_EN BIT(17) + #define RT5651_DEVICE_ID_VALUE 0x6281 #define RT5651_PR_RANGE_BASE (0xff + 1) @@ -37,6 +43,8 @@ #define RT5651_PR_BASE (RT5651_PR_RANGE_BASE + (0 * RT5651_PR_SPACING)) +static unsigned long rt5651_quirk; + static const struct regmap_range_cfg rt5651_ranges[] = { { .name = "PR", .range_min = RT5651_PR_BASE, .range_max = RT5651_PR_BASE + 0xb4, @@ -880,11 +888,14 @@ static const struct snd_soc_dapm_widget rt5651_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("PLL1", RT5651_PWR_ANLG2, RT5651_PWR_PLL_BIT, 0, NULL, 0), /* Input Side */ + SND_SOC_DAPM_SUPPLY("JD Power", RT5651_PWR_ANLG2, + RT5651_PWM_JD_M_BIT, 0, NULL, 0), + /* micbias */ SND_SOC_DAPM_SUPPLY("LDO", RT5651_PWR_ANLG1, RT5651_PWR_LDO_BIT, 0, NULL, 0), - SND_SOC_DAPM_MICBIAS("micbias1", RT5651_PWR_ANLG2, - RT5651_PWR_MB1_BIT, 0), + SND_SOC_DAPM_SUPPLY("micbias1", RT5651_PWR_ANLG2, + RT5651_PWR_MB1_BIT, 0, NULL, 0), /* Input Lines */ SND_SOC_DAPM_INPUT("MIC1"), SND_SOC_DAPM_INPUT("MIC2"), @@ -1528,6 +1539,8 @@ static int rt5651_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, static int rt5651_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec); + switch (level) { case SND_SOC_BIAS_PREPARE: if (SND_SOC_BIAS_STANDBY == snd_soc_codec_get_bias_level(codec)) { @@ -1556,8 +1569,13 @@ static int rt5651_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, RT5651_PWR_DIG2, 0x0000); snd_soc_write(codec, RT5651_PWR_VOL, 0x0000); snd_soc_write(codec, RT5651_PWR_MIXER, 0x0000); - snd_soc_write(codec, RT5651_PWR_ANLG1, 0x0000); - snd_soc_write(codec, RT5651_PWR_ANLG2, 0x0000); + if (rt5651->pdata.jd_src) { + snd_soc_write(codec, RT5651_PWR_ANLG2, 0x0204); + snd_soc_write(codec, RT5651_PWR_ANLG1, 0x0002); + } else { + snd_soc_write(codec, RT5651_PWR_ANLG1, 0x0000); + snd_soc_write(codec, RT5651_PWR_ANLG2, 0x0000); + } break; default: @@ -1570,6 +1588,7 @@ static int rt5651_set_bias_level(struct snd_soc_codec *codec, static int rt5651_probe(struct snd_soc_codec *codec) { struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); rt5651->codec = codec; @@ -1585,6 +1604,15 @@ static int rt5651_probe(struct snd_soc_codec *codec) snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); + if (rt5651->pdata.jd_src) { + snd_soc_dapm_force_enable_pin(dapm, "JD Power"); + snd_soc_dapm_force_enable_pin(dapm, "LDO"); + snd_soc_dapm_sync(dapm); + + regmap_update_bits(rt5651->regmap, RT5651_MICBIAS, + 0x38, 0x38); + } + return 0; } @@ -1718,15 +1746,130 @@ static const struct i2c_device_id rt5651_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, rt5651_i2c_id); +static int rt5651_quirk_cb(const struct dmi_system_id *id) +{ + rt5651_quirk = (unsigned long) id->driver_data; + return 1; +} + +static const struct dmi_system_id rt5651_quirk_table[] = { + { + .callback = rt5651_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "KIANO"), + DMI_MATCH(DMI_PRODUCT_NAME, "KIANO SlimNote 14.2"), + }, + .driver_data = (unsigned long *) RT5651_JD1_1, + }, + {} +}; + static int rt5651_parse_dt(struct rt5651_priv *rt5651, struct device_node *np) { - rt5651->pdata.in2_diff = of_property_read_bool(np, - "realtek,in2-differential"); - rt5651->pdata.dmic_en = of_property_read_bool(np, - "realtek,dmic-en"); + if (of_property_read_bool(np, "realtek,in2-differential")) + rt5651_quirk |= RT5651_IN2_DIFF; + if (of_property_read_bool(np, "realtek,dmic-en")) + rt5651_quirk |= RT5651_DMIC_EN; + + return 0; +} + +static void rt5651_set_pdata(struct rt5651_priv *rt5651) +{ + if (rt5651_quirk & RT5651_IN2_DIFF) + rt5651->pdata.in2_diff = true; + if (rt5651_quirk & RT5651_DMIC_EN) + rt5651->pdata.dmic_en = true; + if (RT5651_JD_MAP(rt5651_quirk)) + rt5651->pdata.jd_src = RT5651_JD_MAP(rt5651_quirk); +} + +static irqreturn_t rt5651_irq(int irq, void *data) +{ + struct rt5651_priv *rt5651 = data; + + queue_delayed_work(system_power_efficient_wq, + &rt5651->jack_detect_work, msecs_to_jiffies(250)); + + return IRQ_HANDLED; +} + +static int rt5651_jack_detect(struct snd_soc_codec *codec, int jack_insert) +{ + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int jack_type; + + if (jack_insert) { + snd_soc_dapm_force_enable_pin(dapm, "LDO"); + snd_soc_dapm_sync(dapm); + + snd_soc_update_bits(codec, RT5651_MICBIAS, + RT5651_MIC1_OVCD_MASK | + RT5651_MIC1_OVTH_MASK | + RT5651_PWR_CLK12M_MASK | + RT5651_PWR_MB_MASK, + RT5651_MIC1_OVCD_EN | + RT5651_MIC1_OVTH_600UA | + RT5651_PWR_MB_PU | + RT5651_PWR_CLK12M_PU); + msleep(100); + if (snd_soc_read(codec, RT5651_IRQ_CTRL2) & RT5651_MB1_OC_CLR) + jack_type = SND_JACK_HEADPHONE; + else + jack_type = SND_JACK_HEADSET; + snd_soc_update_bits(codec, RT5651_IRQ_CTRL2, + RT5651_MB1_OC_CLR, 0); + } else { /* jack out */ + jack_type = 0; + + snd_soc_update_bits(codec, RT5651_MICBIAS, + RT5651_MIC1_OVCD_MASK, + RT5651_MIC1_OVCD_DIS); + } + + return jack_type; +} + +static void rt5651_jack_detect_work(struct work_struct *work) +{ + struct rt5651_priv *rt5651 = + container_of(work, struct rt5651_priv, jack_detect_work.work); + + int report, val = 0; + + if (!rt5651->codec) + return; + + switch (rt5651->pdata.jd_src) { + case RT5651_JD1_1: + val = snd_soc_read(rt5651->codec, RT5651_INT_IRQ_ST) & 0x1000; + break; + case RT5651_JD1_2: + val = snd_soc_read(rt5651->codec, RT5651_INT_IRQ_ST) & 0x2000; + break; + case RT5651_JD2: + val = snd_soc_read(rt5651->codec, RT5651_INT_IRQ_ST) & 0x4000; + break; + default: + break; + } + + report = rt5651_jack_detect(rt5651->codec, !val); + + snd_soc_jack_report(rt5651->hp_jack, report, SND_JACK_HEADSET); +} + +int rt5651_set_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *hp_jack) +{ + struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec); + + rt5651->hp_jack = hp_jack; + rt5651_irq(0, rt5651); return 0; } +EXPORT_SYMBOL_GPL(rt5651_set_jack_detect); static int rt5651_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) @@ -1746,6 +1889,10 @@ static int rt5651_i2c_probe(struct i2c_client *i2c, rt5651->pdata = *pdata; else if (i2c->dev.of_node) rt5651_parse_dt(rt5651, i2c->dev.of_node); + else + dmi_check_system(rt5651_quirk_table); + + rt5651_set_pdata(rt5651); rt5651->regmap = devm_regmap_init_i2c(i2c, &rt5651_regmap); if (IS_ERR(rt5651->regmap)) { @@ -1779,6 +1926,59 @@ static int rt5651_i2c_probe(struct i2c_client *i2c, rt5651->hp_mute = 1; + if (rt5651->pdata.jd_src) { + + /* IRQ output on GPIO1 */ + regmap_update_bits(rt5651->regmap, RT5651_GPIO_CTRL1, + RT5651_GP1_PIN_MASK, RT5651_GP1_PIN_IRQ); + + switch (rt5651->pdata.jd_src) { + case RT5651_JD1_1: + regmap_update_bits(rt5651->regmap, RT5651_JD_CTRL2, + RT5651_JD_TRG_SEL_MASK, + RT5651_JD_TRG_SEL_JD1_1); + regmap_update_bits(rt5651->regmap, RT5651_IRQ_CTRL1, + RT5651_JD1_1_IRQ_EN, + RT5651_JD1_1_IRQ_EN); + break; + case RT5651_JD1_2: + regmap_update_bits(rt5651->regmap, RT5651_JD_CTRL2, + RT5651_JD_TRG_SEL_MASK, + RT5651_JD_TRG_SEL_JD1_2); + regmap_update_bits(rt5651->regmap, RT5651_IRQ_CTRL1, + RT5651_JD1_2_IRQ_EN, + RT5651_JD1_2_IRQ_EN); + break; + case RT5651_JD2: + regmap_update_bits(rt5651->regmap, RT5651_JD_CTRL2, + RT5651_JD_TRG_SEL_MASK, + RT5651_JD_TRG_SEL_JD2); + regmap_update_bits(rt5651->regmap, RT5651_IRQ_CTRL1, + RT5651_JD2_IRQ_EN, + RT5651_JD2_IRQ_EN); + break; + case RT5651_JD_NULL: + break; + default: + dev_warn(&i2c->dev, "Currently only JD1_1 / JD1_2 / JD2 are supported\n"); + break; + } + } + + INIT_DELAYED_WORK(&rt5651->jack_detect_work, rt5651_jack_detect_work); + + if (i2c->irq) { + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, + rt5651_irq, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, "rt5651", rt5651); + if (ret) { + dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + return ret; + } + } + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5651, rt5651_dai, ARRAY_SIZE(rt5651_dai)); @@ -1787,6 +1987,9 @@ static int rt5651_i2c_probe(struct i2c_client *i2c, static int rt5651_i2c_remove(struct i2c_client *i2c) { + struct rt5651_priv *rt5651 = i2c_get_clientdata(i2c); + + cancel_delayed_work_sync(&rt5651->jack_detect_work); snd_soc_unregister_codec(&i2c->dev); return 0; diff --git a/sound/soc/codecs/rt5651.h b/sound/soc/codecs/rt5651.h index 1bd33cfa6411..4f8b202121d7 100644 --- a/sound/soc/codecs/rt5651.h +++ b/sound/soc/codecs/rt5651.h @@ -2062,6 +2062,8 @@ struct rt5651_priv { struct snd_soc_codec *codec; struct rt5651_platform_data pdata; struct regmap *regmap; + struct snd_soc_jack *hp_jack; + struct delayed_work jack_detect_work; int sysclk; int sysclk_src; @@ -2077,4 +2079,6 @@ struct rt5651_priv { bool hp_mute; }; +int rt5651_set_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *hp_jack); #endif /* __RT5651_H__ */ diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c index fa66b11df8d4..07e7757417bc 100644 --- a/sound/soc/codecs/rt5659.c +++ b/sound/soc/codecs/rt5659.c @@ -3385,10 +3385,9 @@ static int rt5659_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } -static int rt5659_set_dai_sysclk(struct snd_soc_dai *dai, - int clk_id, unsigned int freq, int dir) +static int rt5659_set_codec_sysclk(struct snd_soc_codec *codec, int clk_id, + int source, unsigned int freq, int dir) { - struct snd_soc_codec *codec = dai->codec; struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec); unsigned int reg_val = 0; @@ -3414,20 +3413,21 @@ static int rt5659_set_dai_sysclk(struct snd_soc_dai *dai, rt5659->sysclk = freq; rt5659->sysclk_src = clk_id; - dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); + dev_dbg(codec->dev, "Sysclk is %dHz and clock id is %d\n", + freq, clk_id); return 0; } -static int rt5659_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source, - unsigned int freq_in, unsigned int freq_out) +static int rt5659_set_codec_pll(struct snd_soc_codec *codec, int pll_id, + int source, unsigned int freq_in, + unsigned int freq_out) { - struct snd_soc_codec *codec = dai->codec; struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec); struct rl6231_pll_code pll_code; int ret; - if (Source == rt5659->pll_src && freq_in == rt5659->pll_in && + if (source == rt5659->pll_src && freq_in == rt5659->pll_in && freq_out == rt5659->pll_out) return 0; @@ -3441,7 +3441,7 @@ static int rt5659_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source, return 0; } - switch (Source) { + switch (source) { case RT5659_PLL1_S_MCLK: snd_soc_update_bits(codec, RT5659_GLB_CLK, RT5659_PLL1_SRC_MASK, RT5659_PLL1_SRC_MCLK); @@ -3459,7 +3459,7 @@ static int rt5659_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source, RT5659_PLL1_SRC_MASK, RT5659_PLL1_SRC_BCLK3); break; default: - dev_err(codec->dev, "Unknown PLL Source %d\n", Source); + dev_err(codec->dev, "Unknown PLL source %d\n", source); return -EINVAL; } @@ -3481,7 +3481,7 @@ static int rt5659_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source, rt5659->pll_in = freq_in; rt5659->pll_out = freq_out; - rt5659->pll_src = Source; + rt5659->pll_src = source; return 0; } @@ -3666,9 +3666,7 @@ static int rt5659_resume(struct snd_soc_codec *codec) static const struct snd_soc_dai_ops rt5659_aif_dai_ops = { .hw_params = rt5659_hw_params, .set_fmt = rt5659_set_dai_fmt, - .set_sysclk = rt5659_set_dai_sysclk, .set_tdm_slot = rt5659_set_tdm_slot, - .set_pll = rt5659_set_dai_pll, .set_bclk_ratio = rt5659_set_bclk_ratio, }; @@ -3747,6 +3745,8 @@ static const struct snd_soc_codec_driver soc_codec_dev_rt5659 = { .dapm_routes = rt5659_dapm_routes, .num_dapm_routes = ARRAY_SIZE(rt5659_dapm_routes), }, + .set_sysclk = rt5659_set_codec_sysclk, + .set_pll = rt5659_set_codec_pll, }; diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c index e45b895d8279..b036c9dc0c8c 100644 --- a/sound/soc/codecs/rt5663.c +++ b/sound/soc/codecs/rt5663.c @@ -38,13 +38,24 @@ enum { CODEC_VER_0, }; +struct impedance_mapping_table { + unsigned int imp_min; + unsigned int imp_max; + unsigned int vol; + unsigned int dc_offset_l_manual; + unsigned int dc_offset_r_manual; + unsigned int dc_offset_l_manual_mic; + unsigned int dc_offset_r_manual_mic; +}; + struct rt5663_priv { struct snd_soc_codec *codec; struct rt5663_platform_data pdata; struct regmap *regmap; - struct delayed_work jack_detect_work; + struct delayed_work jack_detect_work, jd_unplug_work; struct snd_soc_jack *hs_jack; struct timer_list btn_check_timer; + struct impedance_mapping_table *imp_table; int codec_ver; int sysclk; @@ -1575,6 +1586,9 @@ static int rt5663_jack_detect(struct snd_soc_codec *codec, int jack_insert) rt5663->jack_type = SND_JACK_HEADSET; rt5663_enable_push_button_irq(codec, true); + if (rt5663->pdata.impedance_sensing_num) + break; + if (rt5663->pdata.dc_offset_l_manual_mic) { regmap_write(rt5663->regmap, RT5663_MIC_DECRO_2, rt5663->pdata.dc_offset_l_manual_mic >> @@ -1596,6 +1610,9 @@ static int rt5663_jack_detect(struct snd_soc_codec *codec, int jack_insert) default: rt5663->jack_type = SND_JACK_HEADPHONE; + if (rt5663->pdata.impedance_sensing_num) + break; + if (rt5663->pdata.dc_offset_l_manual) { regmap_write(rt5663->regmap, RT5663_MIC_DECRO_2, rt5663->pdata.dc_offset_l_manual >> 16); @@ -1623,6 +1640,177 @@ static int rt5663_jack_detect(struct snd_soc_codec *codec, int jack_insert) return rt5663->jack_type; } +static int rt5663_impedance_sensing(struct snd_soc_codec *codec) +{ + struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec); + unsigned int value, i, reg84, reg26, reg2fa, reg91, reg10, reg80; + + for (i = 0; i < rt5663->pdata.impedance_sensing_num; i++) { + if (rt5663->imp_table[i].vol == 7) + break; + } + + if (rt5663->jack_type == SND_JACK_HEADSET) { + snd_soc_write(codec, RT5663_MIC_DECRO_2, + rt5663->imp_table[i].dc_offset_l_manual_mic >> 16); + snd_soc_write(codec, RT5663_MIC_DECRO_3, + rt5663->imp_table[i].dc_offset_l_manual_mic & 0xffff); + snd_soc_write(codec, RT5663_MIC_DECRO_5, + rt5663->imp_table[i].dc_offset_r_manual_mic >> 16); + snd_soc_write(codec, RT5663_MIC_DECRO_6, + rt5663->imp_table[i].dc_offset_r_manual_mic & 0xffff); + } else { + snd_soc_write(codec, RT5663_MIC_DECRO_2, + rt5663->imp_table[i].dc_offset_l_manual >> 16); + snd_soc_write(codec, RT5663_MIC_DECRO_3, + rt5663->imp_table[i].dc_offset_l_manual & 0xffff); + snd_soc_write(codec, RT5663_MIC_DECRO_5, + rt5663->imp_table[i].dc_offset_r_manual >> 16); + snd_soc_write(codec, RT5663_MIC_DECRO_6, + rt5663->imp_table[i].dc_offset_r_manual & 0xffff); + } + + reg84 = snd_soc_read(codec, RT5663_ASRC_2); + reg26 = snd_soc_read(codec, RT5663_STO1_ADC_MIXER); + reg2fa = snd_soc_read(codec, RT5663_DUMMY_1); + reg91 = snd_soc_read(codec, RT5663_HP_CHARGE_PUMP_1); + reg10 = snd_soc_read(codec, RT5663_RECMIX); + reg80 = snd_soc_read(codec, RT5663_GLB_CLK); + + snd_soc_update_bits(codec, RT5663_STO_DRE_1, 0x8000, 0); + snd_soc_write(codec, RT5663_ASRC_2, 0); + snd_soc_write(codec, RT5663_STO1_ADC_MIXER, 0x4040); + snd_soc_update_bits(codec, RT5663_PWR_ANLG_1, + RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK | + RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK, + RT5663_PWR_VREF1 | RT5663_PWR_VREF2); + usleep_range(10000, 10005); + snd_soc_update_bits(codec, RT5663_PWR_ANLG_1, + RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK, + RT5663_PWR_FV1 | RT5663_PWR_FV2); + snd_soc_update_bits(codec, RT5663_GLB_CLK, RT5663_SCLK_SRC_MASK, + RT5663_SCLK_SRC_RCCLK); + snd_soc_update_bits(codec, RT5663_RC_CLK, RT5663_DIG_25M_CLK_MASK, + RT5663_DIG_25M_CLK_EN); + snd_soc_update_bits(codec, RT5663_ADDA_CLK_1, RT5663_I2S_PD1_MASK, 0); + snd_soc_write(codec, RT5663_PRE_DIV_GATING_1, 0xff00); + snd_soc_write(codec, RT5663_PRE_DIV_GATING_2, 0xfffc); + snd_soc_write(codec, RT5663_HP_CHARGE_PUMP_1, 0x1232); + snd_soc_write(codec, RT5663_HP_LOGIC_2, 0x0005); + snd_soc_write(codec, RT5663_DEPOP_2, 0x3003); + snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0030, 0x0030); + snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0003, 0x0003); + snd_soc_update_bits(codec, RT5663_PWR_DIG_2, + RT5663_PWR_ADC_S1F | RT5663_PWR_DAC_S1F, + RT5663_PWR_ADC_S1F | RT5663_PWR_DAC_S1F); + snd_soc_update_bits(codec, RT5663_PWR_DIG_1, + RT5663_PWR_DAC_L1 | RT5663_PWR_DAC_R1 | + RT5663_PWR_LDO_DACREF_MASK | RT5663_PWR_ADC_L1 | + RT5663_PWR_ADC_R1, + RT5663_PWR_DAC_L1 | RT5663_PWR_DAC_R1 | + RT5663_PWR_LDO_DACREF_ON | RT5663_PWR_ADC_L1 | + RT5663_PWR_ADC_R1); + msleep(40); + snd_soc_update_bits(codec, RT5663_PWR_ANLG_2, + RT5663_PWR_RECMIX1 | RT5663_PWR_RECMIX2, + RT5663_PWR_RECMIX1 | RT5663_PWR_RECMIX2); + msleep(30); + snd_soc_write(codec, RT5663_HP_CHARGE_PUMP_2, 0x1371); + snd_soc_write(codec, RT5663_STO_DAC_MIXER, 0); + snd_soc_write(codec, RT5663_BYPASS_STO_DAC, 0x000c); + snd_soc_write(codec, RT5663_HP_BIAS, 0xafaa); + snd_soc_write(codec, RT5663_CHARGE_PUMP_1, 0x2224); + snd_soc_write(codec, RT5663_HP_OUT_EN, 0x8088); + snd_soc_write(codec, RT5663_CHOP_ADC, 0x3000); + snd_soc_write(codec, RT5663_ADDA_RST, 0xc000); + snd_soc_write(codec, RT5663_STO1_HPF_ADJ1, 0x3320); + snd_soc_write(codec, RT5663_HP_CALIB_2, 0x00c9); + snd_soc_write(codec, RT5663_DUMMY_1, 0x004c); + snd_soc_write(codec, RT5663_ANA_BIAS_CUR_1, 0x7733); + snd_soc_write(codec, RT5663_CHARGE_PUMP_2, 0x7777); + snd_soc_write(codec, RT5663_STO_DRE_9, 0x0007); + snd_soc_write(codec, RT5663_STO_DRE_10, 0x0007); + snd_soc_write(codec, RT5663_DUMMY_2, 0x02a4); + snd_soc_write(codec, RT5663_RECMIX, 0x0005); + snd_soc_write(codec, RT5663_HP_IMP_SEN_1, 0x4334); + snd_soc_update_bits(codec, RT5663_IRQ_3, 0x0004, 0x0004); + snd_soc_write(codec, RT5663_HP_LOGIC_1, 0x2200); + snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x3000, 0x3000); + snd_soc_write(codec, RT5663_HP_LOGIC_1, 0x6200); + + for (i = 0; i < 100; i++) { + msleep(20); + if (snd_soc_read(codec, RT5663_INT_ST_1) & 0x2) + break; + } + + value = snd_soc_read(codec, RT5663_HP_IMP_SEN_4); + + snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x3000, 0); + snd_soc_write(codec, RT5663_INT_ST_1, 0); + snd_soc_write(codec, RT5663_HP_LOGIC_1, 0); + snd_soc_update_bits(codec, RT5663_RC_CLK, RT5663_DIG_25M_CLK_MASK, + RT5663_DIG_25M_CLK_DIS); + snd_soc_write(codec, RT5663_GLB_CLK, reg80); + snd_soc_write(codec, RT5663_RECMIX, reg10); + snd_soc_write(codec, RT5663_DUMMY_2, 0x00a4); + snd_soc_write(codec, RT5663_DUMMY_1, reg2fa); + snd_soc_write(codec, RT5663_HP_CALIB_2, 0x00c8); + snd_soc_write(codec, RT5663_STO1_HPF_ADJ1, 0xb320); + snd_soc_write(codec, RT5663_ADDA_RST, 0xe400); + snd_soc_write(codec, RT5663_CHOP_ADC, 0x2000); + snd_soc_write(codec, RT5663_HP_OUT_EN, 0x0008); + snd_soc_update_bits(codec, RT5663_PWR_ANLG_2, + RT5663_PWR_RECMIX1 | RT5663_PWR_RECMIX2, 0); + snd_soc_update_bits(codec, RT5663_PWR_DIG_1, + RT5663_PWR_DAC_L1 | RT5663_PWR_DAC_R1 | + RT5663_PWR_LDO_DACREF_MASK | RT5663_PWR_ADC_L1 | + RT5663_PWR_ADC_R1, 0); + snd_soc_update_bits(codec, RT5663_PWR_DIG_2, + RT5663_PWR_ADC_S1F | RT5663_PWR_DAC_S1F, 0); + snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0003, 0); + snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0030, 0); + snd_soc_write(codec, RT5663_HP_LOGIC_2, 0); + snd_soc_write(codec, RT5663_HP_CHARGE_PUMP_1, reg91); + snd_soc_update_bits(codec, RT5663_PWR_ANLG_1, + RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK, 0); + snd_soc_write(codec, RT5663_STO1_ADC_MIXER, reg26); + snd_soc_write(codec, RT5663_ASRC_2, reg84); + + for (i = 0; i < rt5663->pdata.impedance_sensing_num; i++) { + if (value >= rt5663->imp_table[i].imp_min && + value <= rt5663->imp_table[i].imp_max) + break; + } + + snd_soc_update_bits(codec, RT5663_STO_DRE_9, RT5663_DRE_GAIN_HP_MASK, + rt5663->imp_table[i].vol); + snd_soc_update_bits(codec, RT5663_STO_DRE_10, RT5663_DRE_GAIN_HP_MASK, + rt5663->imp_table[i].vol); + + if (rt5663->jack_type == SND_JACK_HEADSET) { + snd_soc_write(codec, RT5663_MIC_DECRO_2, + rt5663->imp_table[i].dc_offset_l_manual_mic >> 16); + snd_soc_write(codec, RT5663_MIC_DECRO_3, + rt5663->imp_table[i].dc_offset_l_manual_mic & 0xffff); + snd_soc_write(codec, RT5663_MIC_DECRO_5, + rt5663->imp_table[i].dc_offset_r_manual_mic >> 16); + snd_soc_write(codec, RT5663_MIC_DECRO_6, + rt5663->imp_table[i].dc_offset_r_manual_mic & 0xffff); + } else { + snd_soc_write(codec, RT5663_MIC_DECRO_2, + rt5663->imp_table[i].dc_offset_l_manual >> 16); + snd_soc_write(codec, RT5663_MIC_DECRO_3, + rt5663->imp_table[i].dc_offset_l_manual & 0xffff); + snd_soc_write(codec, RT5663_MIC_DECRO_5, + rt5663->imp_table[i].dc_offset_r_manual >> 16); + snd_soc_write(codec, RT5663_MIC_DECRO_6, + rt5663->imp_table[i].dc_offset_r_manual & 0xffff); + } + + return 0; +} + static int rt5663_button_detect(struct snd_soc_codec *codec) { int btn_type, val; @@ -1702,6 +1890,8 @@ static void rt5663_jack_detect_work(struct work_struct *work) break; case CODEC_VER_0: report = rt5663_jack_detect(rt5663->codec, 1); + if (rt5663->pdata.impedance_sensing_num) + rt5663_impedance_sensing(rt5663->codec); break; default: dev_err(codec->dev, "Unknown CODEC Version\n"); @@ -1751,8 +1941,15 @@ static void rt5663_jack_detect_work(struct work_struct *work) break; } /* button release or spurious interrput*/ - if (btn_type == 0) + if (btn_type == 0) { report = rt5663->jack_type; + cancel_delayed_work_sync( + &rt5663->jd_unplug_work); + } else { + queue_delayed_work(system_wq, + &rt5663->jd_unplug_work, + msecs_to_jiffies(500)); + } } } else { /* jack out */ @@ -1773,6 +1970,37 @@ static void rt5663_jack_detect_work(struct work_struct *work) SND_JACK_BTN_2 | SND_JACK_BTN_3); } +static void rt5663_jd_unplug_work(struct work_struct *work) +{ + struct rt5663_priv *rt5663 = + container_of(work, struct rt5663_priv, jd_unplug_work.work); + struct snd_soc_codec *codec = rt5663->codec; + + if (!codec) + return; + + if (!rt5663_check_jd_status(codec)) { + /* jack out */ + switch (rt5663->codec_ver) { + case CODEC_VER_1: + rt5663_v2_jack_detect(rt5663->codec, 0); + break; + case CODEC_VER_0: + rt5663_jack_detect(rt5663->codec, 0); + break; + default: + dev_err(codec->dev, "Unknown CODEC Version\n"); + } + + snd_soc_jack_report(rt5663->hs_jack, 0, SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + } else { + queue_delayed_work(system_wq, &rt5663->jd_unplug_work, + msecs_to_jiffies(500)); + } +} + static const struct snd_kcontrol_new rt5663_snd_controls[] = { /* DAC Digital Volume */ SOC_DOUBLE_TLV("DAC Playback Volume", RT5663_STO1_DAC_DIG_VOL, @@ -1797,10 +2025,6 @@ static const struct snd_kcontrol_new rt5663_v2_specific_controls[] = { }; static const struct snd_kcontrol_new rt5663_specific_controls[] = { - /* Headphone Output Volume */ - SOC_DOUBLE_R_TLV("Headphone Playback Volume", RT5663_STO_DRE_9, - RT5663_STO_DRE_10, RT5663_DRE_GAIN_HP_SHIFT, 23, 1, - rt5663_hp_vol_tlv), /* Mic Boost Volume*/ SOC_SINGLE_TLV("IN1 Capture Volume", RT5663_CBJ_2, RT5663_GAIN_BST1_SHIFT, 8, 0, in_bst_tlv), @@ -1808,6 +2032,13 @@ static const struct snd_kcontrol_new rt5663_specific_controls[] = { SOC_ENUM("IF1 ADC Data Swap", rt5663_if1_adc_enum), }; +static const struct snd_kcontrol_new rt5663_hpvol_controls[] = { + /* Headphone Output Volume */ + SOC_DOUBLE_R_TLV("Headphone Playback Volume", RT5663_STO_DRE_9, + RT5663_STO_DRE_10, RT5663_DRE_GAIN_HP_SHIFT, 23, 1, + rt5663_hp_vol_tlv), +}; + static int rt5663_is_sys_clk_from_pll(struct snd_soc_dapm_widget *w, struct snd_soc_dapm_widget *sink) { @@ -2890,6 +3121,10 @@ static int rt5663_probe(struct snd_soc_codec *codec) ARRAY_SIZE(rt5663_specific_dapm_routes)); snd_soc_add_codec_controls(codec, rt5663_specific_controls, ARRAY_SIZE(rt5663_specific_controls)); + + if (!rt5663->imp_table) + snd_soc_add_codec_controls(codec, rt5663_hpvol_controls, + ARRAY_SIZE(rt5663_hpvol_controls)); break; } @@ -3178,6 +3413,8 @@ static void rt5663_calibrate(struct rt5663_priv *rt5663) static int rt5663_parse_dp(struct rt5663_priv *rt5663, struct device *dev) { + int table_size; + device_property_read_u32(dev, "realtek,dc_offset_l_manual", &rt5663->pdata.dc_offset_l_manual); device_property_read_u32(dev, "realtek,dc_offset_r_manual", @@ -3186,6 +3423,17 @@ static int rt5663_parse_dp(struct rt5663_priv *rt5663, struct device *dev) &rt5663->pdata.dc_offset_l_manual_mic); device_property_read_u32(dev, "realtek,dc_offset_r_manual_mic", &rt5663->pdata.dc_offset_r_manual_mic); + device_property_read_u32(dev, "realtek,impedance_sensing_num", + &rt5663->pdata.impedance_sensing_num); + + if (rt5663->pdata.impedance_sensing_num) { + table_size = sizeof(struct impedance_mapping_table) * + rt5663->pdata.impedance_sensing_num; + rt5663->imp_table = devm_kzalloc(dev, table_size, GFP_KERNEL); + device_property_read_u32_array(dev, + "realtek,impedance_sensing_table", + (u32 *)rt5663->imp_table, table_size); + } return 0; } @@ -3219,7 +3467,16 @@ static int rt5663_i2c_probe(struct i2c_client *i2c, ret); return ret; } - regmap_read(regmap, RT5663_VENDOR_ID_2, &val); + + ret = regmap_read(regmap, RT5663_VENDOR_ID_2, &val); + if (ret || (val != RT5663_DEVICE_ID_2 && val != RT5663_DEVICE_ID_1)) { + dev_err(&i2c->dev, + "Device with ID register %#x is not rt5663, retry one time.\n", + val); + msleep(100); + regmap_read(regmap, RT5663_VENDOR_ID_2, &val); + } + switch (val) { case RT5663_DEVICE_ID_2: rt5663->regmap = devm_regmap_init_i2c(i2c, &rt5663_v2_regmap); @@ -3338,6 +3595,7 @@ static int rt5663_i2c_probe(struct i2c_client *i2c, } INIT_DELAYED_WORK(&rt5663->jack_detect_work, rt5663_jack_detect_work); + INIT_DELAYED_WORK(&rt5663->jd_unplug_work, rt5663_jd_unplug_work); if (i2c->irq) { ret = request_irq(i2c->irq, rt5663_irq, diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index 9545764ef3eb..c5094b4399e2 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -34,6 +34,24 @@ #include "rt5670.h" #include "rt5670-dsp.h" +#define RT5670_DEV_GPIO BIT(0) +#define RT5670_IN2_DIFF BIT(1) +#define RT5670_DMIC_EN BIT(2) +#define RT5670_DMIC1_IN2P BIT(3) +#define RT5670_DMIC1_GPIO6 BIT(4) +#define RT5670_DMIC1_GPIO7 BIT(5) +#define RT5670_DMIC2_INR BIT(6) +#define RT5670_DMIC2_GPIO8 BIT(7) +#define RT5670_DMIC3_GPIO5 BIT(8) +#define RT5670_JD_MODE1 BIT(9) +#define RT5670_JD_MODE2 BIT(10) +#define RT5670_JD_MODE3 BIT(11) + +static unsigned long rt5670_quirk; +static unsigned int quirk_override; +module_param_named(quirk, quirk_override, uint, 0444); +MODULE_PARM_DESC(quirk, "Board-specific quirk override"); + #define RT5670_DEVICE_ID 0x6271 #define RT5670_PR_RANGE_BASE (0xff + 1) @@ -2582,6 +2600,24 @@ static int rt5670_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, return 0; } +static int rt5670_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) +{ + struct snd_soc_codec *codec = dai->codec; + + dev_dbg(codec->dev, "%s ratio=%d\n", __func__, ratio); + if (dai->id != RT5670_AIF1) + return 0; + + if ((ratio % 50) == 0) + snd_soc_update_bits(codec, RT5670_GEN_CTRL3, + RT5670_TDM_DATA_MODE_SEL, RT5670_TDM_DATA_MODE_50FS); + else + snd_soc_update_bits(codec, RT5670_GEN_CTRL3, + RT5670_TDM_DATA_MODE_SEL, RT5670_TDM_DATA_MODE_NOR); + + return 0; +} + static int rt5670_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { @@ -2712,6 +2748,7 @@ static const struct snd_soc_dai_ops rt5670_aif_dai_ops = { .set_fmt = rt5670_set_dai_fmt, .set_tdm_slot = rt5670_set_tdm_slot, .set_pll = rt5670_set_dai_pll, + .set_bclk_ratio = rt5670_set_bclk_ratio, }; static struct snd_soc_dai_driver rt5670_dai[] = { @@ -2808,56 +2845,84 @@ static const struct acpi_device_id rt5670_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, rt5670_acpi_match); #endif -static const struct dmi_system_id dmi_platform_intel_braswell[] = { +static int rt5670_quirk_cb(const struct dmi_system_id *id) +{ + rt5670_quirk = (unsigned long)id->driver_data; + return 1; +} + +static const struct dmi_system_id dmi_platform_intel_quirks[] = { { + .callback = rt5670_quirk_cb, .ident = "Intel Braswell", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), DMI_MATCH(DMI_BOARD_NAME, "Braswell CRB"), }, + .driver_data = (unsigned long *)(RT5670_DMIC_EN | + RT5670_DMIC1_IN2P | + RT5670_DEV_GPIO | + RT5670_JD_MODE1), }, { + .callback = rt5670_quirk_cb, .ident = "Dell Wyse 3040", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "Wyse 3040"), }, + .driver_data = (unsigned long *)(RT5670_DMIC_EN | + RT5670_DMIC1_IN2P | + RT5670_DEV_GPIO | + RT5670_JD_MODE1), }, { + .callback = rt5670_quirk_cb, .ident = "Lenovo Thinkpad Tablet 10", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 10"), }, + .driver_data = (unsigned long *)(RT5670_DMIC_EN | + RT5670_DMIC1_IN2P | + RT5670_DEV_GPIO | + RT5670_JD_MODE1), }, { + .callback = rt5670_quirk_cb, .ident = "Lenovo Thinkpad Tablet 10", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Tablet B"), }, + .driver_data = (unsigned long *)(RT5670_DMIC_EN | + RT5670_DMIC1_IN2P | + RT5670_DEV_GPIO | + RT5670_JD_MODE1), }, - {} -}; - -static const struct dmi_system_id dmi_platform_intel_bytcht_jdmode2[] = { { + .callback = rt5670_quirk_cb, .ident = "Lenovo Thinkpad Tablet 10", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Miix 2 10"), }, + .driver_data = (unsigned long *)(RT5670_DMIC_EN | + RT5670_DMIC1_IN2P | + RT5670_DEV_GPIO | + RT5670_JD_MODE2), }, - {} -}; - -static const struct dmi_system_id dmi_platform_intel_bytcht_jdmode3[] = { { + .callback = rt5670_quirk_cb, .ident = "Dell Venue 8 Pro 5855", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5855"), }, + .driver_data = (unsigned long *)(RT5670_DMIC_EN | + RT5670_DMIC2_INR | + RT5670_DEV_GPIO | + RT5670_JD_MODE3), }, {} }; @@ -2881,21 +2946,61 @@ static int rt5670_i2c_probe(struct i2c_client *i2c, if (pdata) rt5670->pdata = *pdata; - if (dmi_check_system(dmi_platform_intel_braswell)) { - rt5670->pdata.dmic_en = true; - rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_IN2P; + dmi_check_system(dmi_platform_intel_quirks); + if (quirk_override) { + dev_info(&i2c->dev, "Overriding quirk 0x%x => 0x%x\n", + (unsigned int)rt5670_quirk, quirk_override); + rt5670_quirk = quirk_override; + } + + if (rt5670_quirk & RT5670_DEV_GPIO) { rt5670->pdata.dev_gpio = true; - rt5670->pdata.jd_mode = 1; - } else if (dmi_check_system(dmi_platform_intel_bytcht_jdmode2)) { + dev_info(&i2c->dev, "quirk dev_gpio\n"); + } + if (rt5670_quirk & RT5670_IN2_DIFF) { + rt5670->pdata.in2_diff = true; + dev_info(&i2c->dev, "quirk IN2_DIFF\n"); + } + if (rt5670_quirk & RT5670_DMIC_EN) { rt5670->pdata.dmic_en = true; + dev_info(&i2c->dev, "quirk DMIC enabled\n"); + } + if (rt5670_quirk & RT5670_DMIC1_IN2P) { rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_IN2P; - rt5670->pdata.dev_gpio = true; + dev_info(&i2c->dev, "quirk DMIC1 on IN2P pin\n"); + } + if (rt5670_quirk & RT5670_DMIC1_GPIO6) { + rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_GPIO6; + dev_info(&i2c->dev, "quirk DMIC1 on GPIO6 pin\n"); + } + if (rt5670_quirk & RT5670_DMIC1_GPIO7) { + rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_GPIO7; + dev_info(&i2c->dev, "quirk DMIC1 on GPIO7 pin\n"); + } + if (rt5670_quirk & RT5670_DMIC2_INR) { + rt5670->pdata.dmic2_data_pin = RT5670_DMIC_DATA_IN3N; + dev_info(&i2c->dev, "quirk DMIC2 on INR pin\n"); + } + if (rt5670_quirk & RT5670_DMIC2_GPIO8) { + rt5670->pdata.dmic2_data_pin = RT5670_DMIC_DATA_GPIO8; + dev_info(&i2c->dev, "quirk DMIC2 on GPIO8 pin\n"); + } + if (rt5670_quirk & RT5670_DMIC3_GPIO5) { + rt5670->pdata.dmic3_data_pin = RT5670_DMIC_DATA_GPIO5; + dev_info(&i2c->dev, "quirk DMIC3 on GPIO5 pin\n"); + } + + if (rt5670_quirk & RT5670_JD_MODE1) { + rt5670->pdata.jd_mode = 1; + dev_info(&i2c->dev, "quirk JD mode 1\n"); + } + if (rt5670_quirk & RT5670_JD_MODE2) { rt5670->pdata.jd_mode = 2; - } else if (dmi_check_system(dmi_platform_intel_bytcht_jdmode3)) { - rt5670->pdata.dmic_en = true; - rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_IN2P; - rt5670->pdata.dev_gpio = true; + dev_info(&i2c->dev, "quirk JD mode 2\n"); + } + if (rt5670_quirk & RT5670_JD_MODE3) { rt5670->pdata.jd_mode = 3; + dev_info(&i2c->dev, "quirk JD mode 3\n"); } rt5670->regmap = devm_regmap_init_i2c(i2c, &rt5670_regmap); diff --git a/sound/soc/codecs/rt5670.h b/sound/soc/codecs/rt5670.h index 5ba485cae4e6..265df80d504e 100644 --- a/sound/soc/codecs/rt5670.h +++ b/sound/soc/codecs/rt5670.h @@ -1816,6 +1816,10 @@ #define RT5670_ZCD_HP_DIS (0x0 << 15) #define RT5670_ZCD_HP_EN (0x1 << 15) +/* General Control 3 (0xfc) */ +#define RT5670_TDM_DATA_MODE_SEL (0x1 << 11) +#define RT5670_TDM_DATA_MODE_NOR (0x0 << 11) +#define RT5670_TDM_DATA_MODE_50FS (0x1 << 11) /* Codec Private Register definition */ /* 3D Speaker Control (0x63) */ diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c index 810369f687d7..a09499977be4 100644 --- a/sound/soc/codecs/tas571x.c +++ b/sound/soc/codecs/tas571x.c @@ -697,7 +697,8 @@ static int tas571x_i2c_probe(struct i2c_client *client, return PTR_ERR(priv->mclk); } - BUG_ON(priv->chip->num_supply_names > TAS571X_MAX_SUPPLIES); + if (WARN_ON(priv->chip->num_supply_names > TAS571X_MAX_SUPPLIES)) + return -EINVAL; for (i = 0; i < priv->chip->num_supply_names; i++) priv->supplies[i].supply = priv->chip->supply_names[i]; diff --git a/sound/soc/codecs/tfa9879.c b/sound/soc/codecs/tfa9879.c index 95e0a7abeb7a..f8dd67ca0744 100644 --- a/sound/soc/codecs/tfa9879.c +++ b/sound/soc/codecs/tfa9879.c @@ -312,9 +312,15 @@ static const struct i2c_device_id tfa9879_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, tfa9879_i2c_id); +static const struct of_device_id tfa9879_of_match[] = { + { .compatible = "nxp,tfa9879", }, + { } +}; + static struct i2c_driver tfa9879_i2c_driver = { .driver = { .name = "tfa9879", + .of_match_table = tfa9879_of_match, }, .probe = tfa9879_i2c_probe, .remove = tfa9879_i2c_remove, diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c index 3d42138a7974..74909211c608 100644 --- a/sound/soc/codecs/tlv320aic23.c +++ b/sound/soc/codecs/tlv320aic23.c @@ -454,6 +454,7 @@ static int tlv320aic23_set_dai_fmt(struct snd_soc_dai *codec_dai, break; case SND_SOC_DAIFMT_DSP_A: iface_reg |= TLV320AIC23_LRP_ON; + /* fall through */ case SND_SOC_DAIFMT_DSP_B: iface_reg |= TLV320AIC23_FOR_DSP; break; diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index 54a87a905eb6..e2862372c26e 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -929,7 +929,7 @@ static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai, case SND_SOC_DAIFMT_I2S: break; case SND_SOC_DAIFMT_DSP_A: - dsp_a_val = 0x1; + dsp_a_val = 0x1; /* fall through */ case SND_SOC_DAIFMT_DSP_B: /* NOTE: BCLKINV bit value 1 equas NB and 0 equals IB */ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c index 2e014c80d113..616cd4bebd01 100644 --- a/sound/soc/codecs/tpa6130a2.c +++ b/sound/soc/codecs/tpa6130a2.c @@ -274,6 +274,7 @@ static int tpa6130a2_probe(struct i2c_client *client, default: dev_warn(dev, "Unknown TPA model (%d). Assuming 6130A2\n", data->id); + /* fall through */ case TPA6130A2: regulator = "Vdd"; break; diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c index 43568435c208..738e04b09116 100644 --- a/sound/soc/codecs/ts3a227e.c +++ b/sound/soc/codecs/ts3a227e.c @@ -15,6 +15,7 @@ #include <linux/module.h> #include <linux/of_gpio.h> #include <linux/regmap.h> +#include <linux/acpi.h> #include <sound/core.h> #include <sound/jack.h> @@ -374,11 +375,20 @@ static const struct of_device_id ts3a227e_of_match[] = { }; MODULE_DEVICE_TABLE(of, ts3a227e_of_match); +#ifdef CONFIG_ACPI +static struct acpi_device_id ts3a227e_acpi_match[] = { + { "104C227E", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, ts3a227e_acpi_match); +#endif + static struct i2c_driver ts3a227e_driver = { .driver = { .name = "ts3a227e", .pm = &ts3a227e_pm, .of_match_table = of_match_ptr(ts3a227e_of_match), + .acpi_match_table = ACPI_PTR(ts3a227e_acpi_match), }, .probe = ts3a227e_i2c_probe, .id_table = ts3a227e_i2c_ids, diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 72486bf072f2..4f0481d3c7a7 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -1951,7 +1951,6 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec) return ret; arizona_init_gpio(codec); - arizona_init_notifiers(codec); snd_soc_component_disable_pin(component, "HAPTICS"); @@ -2043,6 +2042,14 @@ static int wm5102_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, wm5102); + if (IS_ENABLED(CONFIG_OF)) { + if (!dev_get_platdata(arizona->dev)) { + ret = arizona_of_get_audio_pdata(arizona); + if (ret < 0) + return ret; + } + } + mutex_init(&arizona->dac_comp_lock); wm5102->core.arizona = arizona; @@ -2098,6 +2105,11 @@ static int wm5102_probe(struct platform_device *pdev) return ret; } + arizona_init_common(arizona); + + ret = arizona_init_vol_limit(arizona); + if (ret < 0) + goto err_dsp_irq; ret = arizona_init_spk_irqs(arizona); if (ret < 0) goto err_dsp_irq; diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 858a24fc28e8..6ed1e1f9ce51 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -2290,7 +2290,6 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec) arizona_init_gpio(codec); arizona_init_mono(codec); - arizona_init_notifiers(codec); for (i = 0; i < WM5110_NUM_ADSP; ++i) { ret = wm_adsp2_codec_probe(&priv->core.adsp[i], codec); @@ -2398,6 +2397,14 @@ static int wm5110_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, wm5110); + if (IS_ENABLED(CONFIG_OF)) { + if (!dev_get_platdata(arizona->dev)) { + ret = arizona_of_get_audio_pdata(arizona); + if (ret < 0) + return ret; + } + } + wm5110->core.arizona = arizona; wm5110->core.num_inputs = 8; @@ -2454,6 +2461,11 @@ static int wm5110_probe(struct platform_device *pdev) return ret; } + arizona_init_common(arizona); + + ret = arizona_init_vol_limit(arizona); + if (ret < 0) + goto err_dsp_irq; ret = arizona_init_spk_irqs(arizona); if (ret < 0) goto err_dsp_irq; diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index b8c1940f2243..a394dbee77aa 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -196,7 +196,7 @@ static int wm8741_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_codec *codec = dai->codec; struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec); - u16 iface = snd_soc_read(codec, WM8741_FORMAT_CONTROL) & 0x1FC; + unsigned int iface; int i; /* The set of sample rates that can be supported depends on the @@ -223,15 +223,16 @@ static int wm8741_hw_params(struct snd_pcm_substream *substream, /* bit size */ switch (params_width(params)) { case 16: + iface = 0x0; break; case 20: - iface |= 0x0001; + iface = 0x1; break; case 24: - iface |= 0x0002; + iface = 0x2; break; case 32: - iface |= 0x0003; + iface = 0x3; break; default: dev_dbg(codec->dev, "wm8741_hw_params: Unsupported bit size param = %d", @@ -242,7 +243,9 @@ static int wm8741_hw_params(struct snd_pcm_substream *substream, dev_dbg(codec->dev, "wm8741_hw_params: bit size param = %d, rate param = %d", params_width(params), params_rate(params)); - snd_soc_write(codec, WM8741_FORMAT_CONTROL, iface); + snd_soc_update_bits(codec, WM8741_FORMAT_CONTROL, WM8741_IWL_MASK, + iface); + return 0; } @@ -295,7 +298,7 @@ static int wm8741_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_codec *codec = codec_dai->codec; - u16 iface = snd_soc_read(codec, WM8741_FORMAT_CONTROL) & 0x1C3; + unsigned int iface; /* check master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { @@ -308,18 +311,19 @@ static int wm8741_set_dai_fmt(struct snd_soc_dai *codec_dai, /* interface format */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: - iface |= 0x0008; + iface = 0x08; break; case SND_SOC_DAIFMT_RIGHT_J: + iface = 0x00; break; case SND_SOC_DAIFMT_LEFT_J: - iface |= 0x0004; + iface = 0x04; break; case SND_SOC_DAIFMT_DSP_A: - iface |= 0x000C; + iface = 0x0C; break; case SND_SOC_DAIFMT_DSP_B: - iface |= 0x001C; + iface = 0x1C; break; default: return -EINVAL; @@ -329,14 +333,14 @@ static int wm8741_set_dai_fmt(struct snd_soc_dai *codec_dai, switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: break; - case SND_SOC_DAIFMT_IB_IF: - iface |= 0x0010; + case SND_SOC_DAIFMT_NB_IF: + iface |= 0x10; break; case SND_SOC_DAIFMT_IB_NF: - iface |= 0x0020; + iface |= 0x20; break; - case SND_SOC_DAIFMT_NB_IF: - iface |= 0x0030; + case SND_SOC_DAIFMT_IB_IF: + iface |= 0x30; break; default: return -EINVAL; @@ -347,7 +351,10 @@ static int wm8741_set_dai_fmt(struct snd_soc_dai *codec_dai, fmt & SND_SOC_DAIFMT_FORMAT_MASK, ((fmt & SND_SOC_DAIFMT_INV_MASK))); - snd_soc_write(codec, WM8741_FORMAT_CONTROL, iface); + snd_soc_update_bits(codec, WM8741_FORMAT_CONTROL, + WM8741_BCP_MASK | WM8741_LRP_MASK | WM8741_FMT_MASK, + iface); + return 0; } diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index d05d76e79c70..0271a5253bd3 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -971,7 +971,7 @@ static int wm8753_pcm_set_dai_fmt(struct snd_soc_codec *codec, case SND_SOC_DAIFMT_CBS_CFS: break; case SND_SOC_DAIFMT_CBM_CFM: - ioctl |= 0x2; + ioctl |= 0x2; /* fall through */ case SND_SOC_DAIFMT_CBM_CFS: voice |= 0x0040; break; @@ -1096,7 +1096,7 @@ static int wm8753_i2s_set_dai_fmt(struct snd_soc_codec *codec, case SND_SOC_DAIFMT_CBS_CFS: break; case SND_SOC_DAIFMT_CBM_CFM: - ioctl |= 0x1; + ioctl |= 0x1; /* fall through */ case SND_SOC_DAIFMT_CBM_CFS: hifi |= 0x0040; break; diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index 195f7bf6eb22..830ffd80de4a 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -1076,6 +1076,7 @@ static int wm8993_set_sysclk(struct snd_soc_dai *codec_dai, switch (clk_id) { case WM8993_SYSCLK_MCLK: wm8993->mclk_rate = freq; + /* fall through */ case WM8993_SYSCLK_FLL: wm8993->sysclk_source = clk_id; break; @@ -1123,6 +1124,7 @@ static int wm8993_set_dai_fmt(struct snd_soc_dai *dai, switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_B: aif1 |= WM8993_AIF_LRCLK_INV; + /* fall through */ case SND_SOC_DAIFMT_DSP_A: aif1 |= 0x18; break; diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 3896523b71e9..f91b49e1ece3 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -860,6 +860,7 @@ static void vmid_reference(struct snd_soc_codec *codec) switch (wm8994->vmid_mode) { default: WARN_ON(NULL == "Invalid VMID mode"); + /* fall through */ case WM8994_VMID_NORMAL: /* Startup bias, VMID ramp & buffer */ snd_soc_update_bits(codec, WM8994_ANTIPOP_2, @@ -2654,6 +2655,7 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) case SND_SOC_DAIFMT_DSP_B: aif1 |= WM8994_AIF1_LRCLK_INV; lrclk |= WM8958_AIF1_LRCLK_INV; + /* fall through */ case SND_SOC_DAIFMT_DSP_A: aif1 |= 0x18; break; diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c index 49401a8aae64..77f512767273 100644 --- a/sound/soc/codecs/wm8997.c +++ b/sound/soc/codecs/wm8997.c @@ -1068,8 +1068,6 @@ static int wm8997_codec_probe(struct snd_soc_codec *codec) if (ret < 0) return ret; - arizona_init_notifiers(codec); - snd_soc_component_disable_pin(component, "HAPTICS"); priv->core.arizona->dapm = dapm; @@ -1136,6 +1134,14 @@ static int wm8997_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, wm8997); + if (IS_ENABLED(CONFIG_OF)) { + if (!dev_get_platdata(arizona->dev)) { + ret = arizona_of_get_audio_pdata(arizona); + if (ret < 0) + return ret; + } + } + wm8997->core.arizona = arizona; wm8997->core.num_inputs = 4; @@ -1168,6 +1174,11 @@ static int wm8997_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_idle(&pdev->dev); + arizona_init_common(arizona); + + ret = arizona_init_vol_limit(arizona); + if (ret < 0) + return ret; ret = arizona_init_spk_irqs(arizona); if (ret < 0) return ret; diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c index 44f447136e22..2d211dbe7422 100644 --- a/sound/soc/codecs/wm8998.c +++ b/sound/soc/codecs/wm8998.c @@ -101,7 +101,7 @@ static int wm8998_asrc_ev(struct snd_soc_dapm_widget *w, return 0; } -static int wm8998_in1mux_put(struct snd_kcontrol *kcontrol, +static int wm8998_inmux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); @@ -109,84 +109,38 @@ static int wm8998_in1mux_put(struct snd_kcontrol *kcontrol, struct wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec); struct arizona *arizona = wm8998->core.arizona; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int mux, inmode; - unsigned int mode_val, src_val; + unsigned int mode_reg, mode_index; + unsigned int mux, inmode, src_val, mode_val; mux = ucontrol->value.enumerated.item[0]; if (mux > 1) return -EINVAL; - /* L and R registers have same shift and mask */ - inmode = arizona->pdata.inmode[2 * mux]; - src_val = mux << ARIZONA_IN1L_SRC_SHIFT; - if (inmode & ARIZONA_INMODE_SE) - src_val |= 1 << ARIZONA_IN1L_SRC_SE_SHIFT; - - switch (arizona->pdata.inmode[0]) { - case ARIZONA_INMODE_DMIC: - if (mux) - mode_val = 0; /* B always analogue */ - else - mode_val = 1 << ARIZONA_IN1_MODE_SHIFT; - - snd_soc_update_bits(codec, ARIZONA_IN1L_CONTROL, - ARIZONA_IN1_MODE_MASK, mode_val); - - /* IN1A is digital so L and R must change together */ - /* src_val setting same for both registers */ - snd_soc_update_bits(codec, - ARIZONA_ADC_DIGITAL_VOLUME_1L, - ARIZONA_IN1L_SRC_MASK | - ARIZONA_IN1L_SRC_SE_MASK, src_val); - snd_soc_update_bits(codec, - ARIZONA_ADC_DIGITAL_VOLUME_1R, - ARIZONA_IN1R_SRC_MASK | - ARIZONA_IN1R_SRC_SE_MASK, src_val); + switch (e->reg) { + case ARIZONA_ADC_DIGITAL_VOLUME_2L: + mode_reg = ARIZONA_IN2L_CONTROL; + mode_index = 1 + (2 * mux); break; default: - /* both analogue */ - snd_soc_update_bits(codec, - e->reg, - ARIZONA_IN1L_SRC_MASK | - ARIZONA_IN1L_SRC_SE_MASK, - src_val); + mode_reg = ARIZONA_IN1L_CONTROL; + mode_index = (2 * mux); break; } - return snd_soc_dapm_mux_update_power(dapm, kcontrol, - ucontrol->value.enumerated.item[0], - e, NULL); -} - -static int wm8998_in2mux_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); - struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); - struct wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec); - struct arizona *arizona = wm8998->core.arizona; - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int mux, inmode, src_val, mode_val; - - mux = ucontrol->value.enumerated.item[0]; - if (mux > 1) - return -EINVAL; - - inmode = arizona->pdata.inmode[1 + (2 * mux)]; + inmode = arizona->pdata.inmode[mode_index]; if (inmode & ARIZONA_INMODE_DMIC) - mode_val = 1 << ARIZONA_IN2_MODE_SHIFT; + mode_val = 1 << ARIZONA_IN1_MODE_SHIFT; else mode_val = 0; - src_val = mux << ARIZONA_IN2L_SRC_SHIFT; + src_val = mux << ARIZONA_IN1L_SRC_SHIFT; if (inmode & ARIZONA_INMODE_SE) - src_val |= 1 << ARIZONA_IN2L_SRC_SE_SHIFT; + src_val |= 1 << ARIZONA_IN1L_SRC_SE_SHIFT; - snd_soc_update_bits(codec, ARIZONA_IN2L_CONTROL, - ARIZONA_IN2_MODE_MASK, mode_val); + snd_soc_update_bits(codec, mode_reg, ARIZONA_IN1_MODE_MASK, mode_val); - snd_soc_update_bits(codec, ARIZONA_ADC_DIGITAL_VOLUME_2L, - ARIZONA_IN2L_SRC_MASK | ARIZONA_IN2L_SRC_SE_MASK, + snd_soc_update_bits(codec, e->reg, + ARIZONA_IN1L_SRC_MASK | ARIZONA_IN1L_SRC_SE_MASK, src_val); return snd_soc_dapm_mux_update_power(dapm, kcontrol, @@ -216,14 +170,14 @@ static SOC_ENUM_SINGLE_DECL(wm8998_in2mux_enum, static const struct snd_kcontrol_new wm8998_in1mux[2] = { SOC_DAPM_ENUM_EXT("IN1L Mux", wm8998_in1muxl_enum, - snd_soc_dapm_get_enum_double, wm8998_in1mux_put), + snd_soc_dapm_get_enum_double, wm8998_inmux_put), SOC_DAPM_ENUM_EXT("IN1R Mux", wm8998_in1muxr_enum, - snd_soc_dapm_get_enum_double, wm8998_in1mux_put), + snd_soc_dapm_get_enum_double, wm8998_inmux_put), }; static const struct snd_kcontrol_new wm8998_in2mux = SOC_DAPM_ENUM_EXT("IN2 Mux", wm8998_in2mux_enum, - snd_soc_dapm_get_enum_double, wm8998_in2mux_put); + snd_soc_dapm_get_enum_double, wm8998_inmux_put); static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); @@ -1330,7 +1284,6 @@ static int wm8998_codec_probe(struct snd_soc_codec *codec) return ret; arizona_init_gpio(codec); - arizona_init_notifiers(codec); snd_soc_component_disable_pin(component, "HAPTICS"); @@ -1399,6 +1352,14 @@ static int wm8998_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, wm8998); + if (IS_ENABLED(CONFIG_OF)) { + if (!dev_get_platdata(arizona->dev)) { + ret = arizona_of_get_audio_pdata(arizona); + if (ret < 0) + return ret; + } + } + wm8998->core.arizona = arizona; wm8998->core.num_inputs = 3; /* IN1L, IN1R, IN2 */ @@ -1423,6 +1384,8 @@ static int wm8998_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_idle(&pdev->dev); + arizona_init_common(arizona); + ret = arizona_init_spk_irqs(arizona); if (ret < 0) return ret; diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c index f6d5c0f2aea5..2c09f71fe433 100644 --- a/sound/soc/codecs/wm9705.c +++ b/sound/soc/codecs/wm9705.c @@ -11,6 +11,7 @@ #include <linux/init.h> #include <linux/slab.h> +#include <linux/mfd/wm97xx.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/device.h> @@ -18,12 +19,19 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/ac97_codec.h> +#include <sound/ac97/codec.h> +#include <sound/ac97/compat.h> #include <sound/initval.h> #include <sound/soc.h> #define WM9705_VENDOR_ID 0x574d4c05 #define WM9705_VENDOR_ID_MASK 0xffffffff +struct wm9705_priv { + struct snd_ac97 *ac97; + struct wm97xx_platform_data *mfd_pdata; +}; + static const struct reg_default wm9705_reg_defaults[] = { { 0x02, 0x8000 }, { 0x04, 0x8000 }, @@ -292,10 +300,10 @@ static int wm9705_soc_suspend(struct snd_soc_codec *codec) static int wm9705_soc_resume(struct snd_soc_codec *codec) { - struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); + struct wm9705_priv *wm9705 = snd_soc_codec_get_drvdata(codec); int ret; - ret = snd_ac97_reset(ac97, true, WM9705_VENDOR_ID, + ret = snd_ac97_reset(wm9705->ac97, true, WM9705_VENDOR_ID, WM9705_VENDOR_ID_MASK); if (ret < 0) return ret; @@ -311,38 +319,45 @@ static int wm9705_soc_resume(struct snd_soc_codec *codec) static int wm9705_soc_probe(struct snd_soc_codec *codec) { - struct snd_ac97 *ac97; + struct wm9705_priv *wm9705 = snd_soc_codec_get_drvdata(codec); struct regmap *regmap; - int ret; - - ac97 = snd_soc_new_ac97_codec(codec, WM9705_VENDOR_ID, - WM9705_VENDOR_ID_MASK); - if (IS_ERR(ac97)) { - dev_err(codec->dev, "Failed to register AC97 codec\n"); - return PTR_ERR(ac97); - } - regmap = regmap_init_ac97(ac97, &wm9705_regmap_config); - if (IS_ERR(regmap)) { - ret = PTR_ERR(regmap); - goto err_free_ac97_codec; + if (wm9705->mfd_pdata) { + wm9705->ac97 = wm9705->mfd_pdata->ac97; + regmap = wm9705->mfd_pdata->regmap; + } else { +#ifdef CONFIG_SND_SOC_AC97_BUS + wm9705->ac97 = snd_soc_new_ac97_codec(codec, WM9705_VENDOR_ID, + WM9705_VENDOR_ID_MASK); + if (IS_ERR(wm9705->ac97)) { + dev_err(codec->dev, "Failed to register AC97 codec\n"); + return PTR_ERR(wm9705->ac97); + } + + regmap = regmap_init_ac97(wm9705->ac97, &wm9705_regmap_config); + if (IS_ERR(regmap)) { + snd_soc_free_ac97_codec(wm9705->ac97); + return PTR_ERR(regmap); + } +#endif } - snd_soc_codec_set_drvdata(codec, ac97); + snd_soc_codec_set_drvdata(codec, wm9705->ac97); snd_soc_codec_init_regmap(codec, regmap); return 0; -err_free_ac97_codec: - snd_soc_free_ac97_codec(ac97); - return ret; } static int wm9705_soc_remove(struct snd_soc_codec *codec) { - struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec); +#ifdef CONFIG_SND_SOC_AC97_BUS + struct wm9705_priv *wm9705 = snd_soc_codec_get_drvdata(codec); - snd_soc_codec_exit_regmap(codec); - snd_soc_free_ac97_codec(ac97); + if (!wm9705->mfd_pdata) { + snd_soc_codec_exit_regmap(codec); + snd_soc_free_ac97_codec(wm9705->ac97); + } +#endif return 0; } @@ -364,6 +379,15 @@ static const struct snd_soc_codec_driver soc_codec_dev_wm9705 = { static int wm9705_probe(struct platform_device *pdev) { + struct wm9705_priv *wm9705; + + wm9705 = devm_kzalloc(&pdev->dev, sizeof(*wm9705), GFP_KERNEL); + if (wm9705 == NULL) + return -ENOMEM; + + wm9705->mfd_pdata = dev_get_platdata(&pdev->dev); + platform_set_drvdata(pdev, wm9705); + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm9705, wm9705_dai, ARRAY_SIZE(wm9705_dai)); } diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 1a3e1797994a..4f6d1a442bc4 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -12,6 +12,7 @@ #include <linux/init.h> #include <linux/slab.h> +#include <linux/mfd/wm97xx.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/device.h> @@ -19,6 +20,8 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/ac97_codec.h> +#include <sound/ac97/codec.h> +#include <sound/ac97/compat.h> #include <sound/initval.h> #include <sound/soc.h> #include <sound/tlv.h> @@ -30,6 +33,7 @@ struct wm9712_priv { struct snd_ac97 *ac97; unsigned int hp_mixer[2]; struct mutex lock; + struct wm97xx_platform_data *mfd_pdata; }; static const struct reg_default wm9712_reg_defaults[] = { @@ -636,18 +640,26 @@ static int wm9712_soc_probe(struct snd_soc_codec *codec) struct regmap *regmap; int ret; - wm9712->ac97 = snd_soc_new_ac97_codec(codec, WM9712_VENDOR_ID, - WM9712_VENDOR_ID_MASK); - if (IS_ERR(wm9712->ac97)) { - ret = PTR_ERR(wm9712->ac97); - dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret); - return ret; - } - - regmap = regmap_init_ac97(wm9712->ac97, &wm9712_regmap_config); - if (IS_ERR(regmap)) { - ret = PTR_ERR(regmap); - goto err_free_ac97_codec; + if (wm9712->mfd_pdata) { + wm9712->ac97 = wm9712->mfd_pdata->ac97; + regmap = wm9712->mfd_pdata->regmap; + } else { +#ifdef CONFIG_SND_SOC_AC97_BUS + wm9712->ac97 = snd_soc_new_ac97_codec(codec, WM9712_VENDOR_ID, + WM9712_VENDOR_ID_MASK); + if (IS_ERR(wm9712->ac97)) { + ret = PTR_ERR(wm9712->ac97); + dev_err(codec->dev, + "Failed to register AC97 codec: %d\n", ret); + return ret; + } + + regmap = regmap_init_ac97(wm9712->ac97, &wm9712_regmap_config); + if (IS_ERR(regmap)) { + snd_soc_free_ac97_codec(wm9712->ac97); + return PTR_ERR(regmap); + } +#endif } snd_soc_codec_init_regmap(codec, regmap); @@ -656,17 +668,18 @@ static int wm9712_soc_probe(struct snd_soc_codec *codec) snd_soc_update_bits(codec, AC97_VIDEO, 0x3000, 0x3000); return 0; -err_free_ac97_codec: - snd_soc_free_ac97_codec(wm9712->ac97); - return ret; } static int wm9712_soc_remove(struct snd_soc_codec *codec) { +#ifdef CONFIG_SND_SOC_AC97_BUS struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec); - snd_soc_codec_exit_regmap(codec); - snd_soc_free_ac97_codec(wm9712->ac97); + if (!wm9712->mfd_pdata) { + snd_soc_codec_exit_regmap(codec); + snd_soc_free_ac97_codec(wm9712->ac97); + } +#endif return 0; } @@ -697,6 +710,7 @@ static int wm9712_probe(struct platform_device *pdev) mutex_init(&wm9712->lock); + wm9712->mfd_pdata = dev_get_platdata(&pdev->dev); platform_set_drvdata(pdev, wm9712); return snd_soc_register_codec(&pdev->dev, diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 7e4822185feb..df7220656d98 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -17,12 +17,15 @@ #include <linux/init.h> #include <linux/slab.h> +#include <linux/mfd/wm97xx.h> #include <linux/module.h> #include <linux/device.h> #include <linux/regmap.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/ac97_codec.h> +#include <sound/ac97/codec.h> +#include <sound/ac97/compat.h> #include <sound/initval.h> #include <sound/pcm_params.h> #include <sound/tlv.h> @@ -38,6 +41,7 @@ struct wm9713_priv { u32 pll_in; /* PLL input frequency */ unsigned int hp_mixer[2]; struct mutex lock; + struct wm97xx_platform_data *mfd_pdata; }; #define HPL_MIXER 0 @@ -1205,17 +1209,23 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec) static int wm9713_soc_probe(struct snd_soc_codec *codec) { struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); - struct regmap *regmap; + struct regmap *regmap = NULL; - wm9713->ac97 = snd_soc_new_ac97_codec(codec, WM9713_VENDOR_ID, - WM9713_VENDOR_ID_MASK); - if (IS_ERR(wm9713->ac97)) - return PTR_ERR(wm9713->ac97); - - regmap = regmap_init_ac97(wm9713->ac97, &wm9713_regmap_config); - if (IS_ERR(regmap)) { - snd_soc_free_ac97_codec(wm9713->ac97); - return PTR_ERR(regmap); + if (wm9713->mfd_pdata) { + wm9713->ac97 = wm9713->mfd_pdata->ac97; + regmap = wm9713->mfd_pdata->regmap; + } else { +#ifdef CONFIG_SND_SOC_AC97_BUS + wm9713->ac97 = snd_soc_new_ac97_codec(codec, WM9713_VENDOR_ID, + WM9713_VENDOR_ID_MASK); + if (IS_ERR(wm9713->ac97)) + return PTR_ERR(wm9713->ac97); + regmap = regmap_init_ac97(wm9713->ac97, &wm9713_regmap_config); + if (IS_ERR(regmap)) { + snd_soc_free_ac97_codec(wm9713->ac97); + return PTR_ERR(regmap); + } +#endif } snd_soc_codec_init_regmap(codec, regmap); @@ -1228,10 +1238,14 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec) static int wm9713_soc_remove(struct snd_soc_codec *codec) { +#ifdef CONFIG_SND_SOC_AC97_BUS struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec); - snd_soc_codec_exit_regmap(codec); - snd_soc_free_ac97_codec(wm9713->ac97); + if (!wm9713->mfd_pdata) { + snd_soc_codec_exit_regmap(codec); + snd_soc_free_ac97_codec(wm9713->ac97); + } +#endif return 0; } @@ -1262,6 +1276,7 @@ static int wm9713_probe(struct platform_device *pdev) mutex_init(&wm9713->lock); + wm9713->mfd_pdata = dev_get_platdata(&pdev->dev); platform_set_drvdata(pdev, wm9713); return snd_soc_register_codec(&pdev->dev, diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index f395bbc7c354..804c6f2bcf21 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -1721,7 +1721,8 @@ static int davinci_mcasp_get_dma_type(struct davinci_mcasp *mcasp) PTR_ERR(chan)); return PTR_ERR(chan); } - BUG_ON(!chan->device || !chan->device->dev); + if (WARN_ON(!chan->device || !chan->device->dev)) + return -EINVAL; if (chan->device->dev->of_node) ret = of_property_read_string(chan->device->dev->of_node, @@ -1867,6 +1868,10 @@ static int davinci_mcasp_probe(struct platform_device *pdev) if (irq >= 0) { irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_common", dev_name(&pdev->dev)); + if (!irq_name) { + ret = -ENOMEM; + goto err; + } ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, davinci_mcasp_common_irq_handler, IRQF_ONESHOT | IRQF_SHARED, @@ -1884,6 +1889,10 @@ static int davinci_mcasp_probe(struct platform_device *pdev) if (irq >= 0) { irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_rx", dev_name(&pdev->dev)); + if (!irq_name) { + ret = -ENOMEM; + goto err; + } ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, davinci_mcasp_rx_irq_handler, IRQF_ONESHOT, irq_name, mcasp); @@ -1899,6 +1908,10 @@ static int davinci_mcasp_probe(struct platform_device *pdev) if (irq >= 0) { irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_tx", dev_name(&pdev->dev)); + if (!irq_name) { + ret = -ENOMEM; + goto err; + } ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, davinci_mcasp_tx_irq_handler, IRQF_ONESHOT, irq_name, mcasp); @@ -1982,8 +1995,10 @@ static int davinci_mcasp_probe(struct platform_device *pdev) GFP_KERNEL); if (!mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list || - !mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list) - return -ENOMEM; + !mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list) { + ret = -ENOMEM; + goto err; + } ret = davinci_mcasp_set_ch_constraints(mcasp); if (ret) diff --git a/sound/soc/dwc/Kconfig b/sound/soc/dwc/Kconfig index c6fd95fa5ca6..aa0c6ec4d93c 100644 --- a/sound/soc/dwc/Kconfig +++ b/sound/soc/dwc/Kconfig @@ -4,8 +4,8 @@ config SND_DESIGNWARE_I2S select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for I2S driver for - Synopsys desigwnware I2S device. The device supports upto - maximum of 8 channels each for play and record. + Synopsys designware I2S device. The device supports up to + a maximum of 8 channels each for play and record. config SND_DESIGNWARE_PCM bool "PCM PIO extension for I2S driver" diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 2db4d0c80d33..1225e0399de8 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -166,7 +166,7 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, cpu_priv->sysclk_id[tx], cpu_priv->sysclk_freq[tx], cpu_priv->sysclk_dir[tx]); - if (ret) { + if (ret && ret != -ENOTSUPP) { dev_err(dev, "failed to set sysclk for cpu dai\n"); return ret; } @@ -174,7 +174,7 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, if (cpu_priv->slot_width) { ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, cpu_priv->slot_width); - if (ret) { + if (ret && ret != -ENOTSUPP) { dev_err(dev, "failed to set TDM slot for cpu dai\n"); return ret; } @@ -270,7 +270,7 @@ static int fsl_asoc_card_set_bias_level(struct snd_soc_card *card, ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->fll_id, pll_out, SND_SOC_CLOCK_IN); - if (ret) { + if (ret && ret != -ENOTSUPP) { dev_err(dev, "failed to set SYSCLK: %d\n", ret); return ret; } @@ -283,7 +283,7 @@ static int fsl_asoc_card_set_bias_level(struct snd_soc_card *card, ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, codec_priv->mclk_freq, SND_SOC_CLOCK_IN); - if (ret) { + if (ret && ret != -ENOTSUPP) { dev_err(dev, "failed to switch away from FLL: %d\n", ret); return ret; } @@ -459,7 +459,7 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card) ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id, codec_priv->mclk_freq, SND_SOC_CLOCK_IN); - if (ret) { + if (ret && ret != -ENOTSUPP) { dev_err(dev, "failed to set sysclk in %s\n", __func__); return ret; } @@ -639,6 +639,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) devm_kasprintf(&pdev->dev, GFP_KERNEL, "ac97-codec.%u", (unsigned int)idx); + if (!priv->dai_link[0].codec_name) { + ret = -ENOMEM; + goto asrc_fail; + } } priv->dai_link[0].platform_of_node = cpu_np; diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 7e6cc4da0088..4f7469c1864c 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -1110,7 +1110,7 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, struct clk *clk, u64 savesub, enum spdif_txrate index, bool round) { - const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 }; + static const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 }; bool is_sysclk = clk_is_match(clk, spdif_priv->sysclk); u64 rate_ideal, rate_actual, sub; u32 sysclk_dfmin, sysclk_dfmax; @@ -1169,7 +1169,7 @@ out: static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv, enum spdif_txrate index) { - const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 }; + static const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 }; struct platform_device *pdev = spdif_priv->pdev; struct device *dev = &pdev->dev; u64 savesub = 100000, ret; diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 64598d1183f8..f2f51e06e22c 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -197,12 +197,13 @@ struct fsl_ssi_soc_data { * @use_dma: DMA is used or FIQ with stream filter * @use_dual_fifo: DMA with support for both FIFOs used * @fifo_deph: Depth of the SSI FIFOs + * @slot_width: width of each DAI slot + * @slots: number of slots * @rxtx_reg_val: Specific register settings for receive/transmit configuration * * @clk: SSI clock * @baudclk: SSI baud clock for master mode * @baudclk_streams: Active streams that are using baudclk - * @bitclk_freq: bitclock frequency set by .set_dai_sysclk * * @dma_params_tx: DMA transmit parameters * @dma_params_rx: DMA receive parameters @@ -233,12 +234,13 @@ struct fsl_ssi_private { bool use_dual_fifo; bool has_ipg_clk_name; unsigned int fifo_depth; + unsigned int slot_width; + unsigned int slots; struct fsl_ssi_rxtx_reg_val rxtx_reg_val; struct clk *clk; struct clk *baudclk; unsigned int baudclk_streams; - unsigned int bitclk_freq; /* regcache for volatile regs */ u32 regcache_sfcsr; @@ -700,8 +702,8 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream, * Note: This function can be only called when using SSI as DAI master * * Quick instruction for parameters: - * freq: Output BCLK frequency = samplerate * 32 (fixed) * channels - * dir: SND_SOC_CLOCK_OUT -> TxBCLK, SND_SOC_CLOCK_IN -> RxBCLK. + * freq: Output BCLK frequency = samplerate * slots * slot_width + * (In 2-channel I2S Master mode, slot_width is fixed 32) */ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai, @@ -712,15 +714,21 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, int synchronous = ssi_private->cpu_dai_drv.symmetric_rates, ret; u32 pm = 999, div2, psr, stccr, mask, afreq, factor, i; unsigned long clkrate, baudrate, tmprate; + unsigned int slots = params_channels(hw_params); + unsigned int slot_width = 32; u64 sub, savesub = 100000; unsigned int freq; bool baudclk_is_used; - /* Prefer the explicitly set bitclock frequency */ - if (ssi_private->bitclk_freq) - freq = ssi_private->bitclk_freq; - else - freq = params_channels(hw_params) * 32 * params_rate(hw_params); + /* Override slots and slot_width if being specifically set... */ + if (ssi_private->slots) + slots = ssi_private->slots; + /* ...but keep 32 bits if slots is 2 -- I2S Master mode */ + if (ssi_private->slot_width && slots != 2) + slot_width = ssi_private->slot_width; + + /* Generate bit clock based on the slot number and slot width */ + freq = slots * slot_width * params_rate(hw_params); /* Don't apply it to any non-baudclk circumstance */ if (IS_ERR(ssi_private->baudclk)) @@ -805,16 +813,6 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, return 0; } -static int fsl_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai, - int clk_id, unsigned int freq, int dir) -{ - struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai); - - ssi_private->bitclk_freq = freq; - - return 0; -} - /** * fsl_ssi_hw_params - program the sample size * @@ -1095,6 +1093,12 @@ static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, struct regmap *regs = ssi_private->regs; u32 val; + /* The word length should be 8, 10, 12, 16, 18, 20, 22 or 24 */ + if (slot_width & 1 || slot_width < 8 || slot_width > 24) { + dev_err(cpu_dai->dev, "invalid slot width: %d\n", slot_width); + return -EINVAL; + } + /* The slot number should be >= 2 if using Network mode or I2S mode */ regmap_read(regs, CCSR_SSI_SCR, &val); val &= CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_NET; @@ -1121,6 +1125,9 @@ static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, regmap_update_bits(regs, CCSR_SSI_SCR, CCSR_SSI_SCR_SSIEN, val); + ssi_private->slot_width = slot_width; + ssi_private->slots = slots; + return 0; } @@ -1191,7 +1198,6 @@ static const struct snd_soc_dai_ops fsl_ssi_dai_ops = { .hw_params = fsl_ssi_hw_params, .hw_free = fsl_ssi_hw_free, .set_fmt = fsl_ssi_set_dai_fmt, - .set_sysclk = fsl_ssi_set_dai_sysclk, .set_tdm_slot = fsl_ssi_set_dai_tdm_slot, .trigger = fsl_ssi_trigger, }; diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 488c52f9405f..1b6164249341 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -29,7 +29,9 @@ struct graph_card_data { struct graph_dai_props { struct asoc_simple_dai cpu_dai; struct asoc_simple_dai codec_dai; + unsigned int mclk_fs; } *dai_props; + unsigned int mclk_fs; struct snd_soc_dai_link *dai_link; struct gpio_desc *pa_gpio; }; @@ -95,9 +97,43 @@ static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream) asoc_simple_card_clk_disable(&dai_props->codec_dai); } +static int asoc_graph_card_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); + unsigned int mclk, mclk_fs = 0; + int ret = 0; + + if (priv->mclk_fs) + mclk_fs = priv->mclk_fs; + else if (dai_props->mclk_fs) + mclk_fs = dai_props->mclk_fs; + + if (mclk_fs) { + mclk = params_rate(params) * mclk_fs; + ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, + SND_SOC_CLOCK_IN); + if (ret && ret != -ENOTSUPP) + goto err; + + ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, + SND_SOC_CLOCK_OUT); + if (ret && ret != -ENOTSUPP) + goto err; + } + return 0; +err: + return ret; +} + static const struct snd_soc_ops asoc_graph_card_ops = { .startup = asoc_graph_card_startup, .shutdown = asoc_graph_card_shutdown, + .hw_params = asoc_graph_card_hw_params, }; static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd) @@ -146,10 +182,7 @@ static int asoc_graph_card_dai_link_of(struct device_node *cpu_port, if (ret < 0) goto dai_link_of_err; - /* - * we need to consider "mclk-fs" around here - * see simple-card - */ + of_property_read_u32(rcpu_ep, "mclk-fs", &dai_props->mclk_fs); ret = asoc_simple_card_parse_graph_cpu(cpu_ep, dai_link); if (ret < 0) @@ -217,10 +250,8 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) if (ret < 0) return ret; - /* - * we need to consider "mclk-fs" around here - * see simple-card - */ + /* Factor to mclk, used in hw_params() */ + of_property_read_u32(node, "mclk-fs", &priv->mclk_fs); of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { ret = asoc_graph_card_dai_link_of(it.node, priv, idx++); diff --git a/sound/soc/img/img-i2s-in.c b/sound/soc/img/img-i2s-in.c index 567f9767fb73..d7fbb0a0a28b 100644 --- a/sound/soc/img/img-i2s-in.c +++ b/sound/soc/img/img-i2s-in.c @@ -16,6 +16,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/reset.h> #include <sound/core.h> @@ -60,8 +61,33 @@ struct img_i2s_in { void __iomem *channel_base; unsigned int active_channels; struct snd_soc_dai_driver dai_driver; + u32 suspend_ctl; + u32 *suspend_ch_ctl; }; +static int img_i2s_in_runtime_suspend(struct device *dev) +{ + struct img_i2s_in *i2s = dev_get_drvdata(dev); + + clk_disable_unprepare(i2s->clk_sys); + + return 0; +} + +static int img_i2s_in_runtime_resume(struct device *dev) +{ + struct img_i2s_in *i2s = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(i2s->clk_sys); + if (ret) { + dev_err(dev, "Unable to enable sys clock\n"); + return ret; + } + + return 0; +} + static inline void img_i2s_in_writel(struct img_i2s_in *i2s, u32 val, u32 reg) { writel(val, i2s->base + reg); @@ -279,7 +305,7 @@ static int img_i2s_in_hw_params(struct snd_pcm_substream *substream, static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct img_i2s_in *i2s = snd_soc_dai_get_drvdata(dai); - int i; + int i, ret; u32 chan_control_mask, lrd_set = 0, blkp_set = 0, chan_control_set = 0; u32 reg; @@ -319,6 +345,10 @@ static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) chan_control_mask = IMG_I2S_IN_CH_CTL_CLK_TRANS_MASK; + ret = pm_runtime_get_sync(i2s->dev); + if (ret < 0) + return ret; + for (i = 0; i < i2s->active_channels; i++) img_i2s_in_ch_disable(i2s, i); @@ -338,6 +368,8 @@ static int img_i2s_in_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) for (i = 0; i < i2s->active_channels; i++) img_i2s_in_ch_enable(i2s, i); + pm_runtime_put(i2s->dev); + return 0; } @@ -427,9 +459,15 @@ static int img_i2s_in_probe(struct platform_device *pdev) return PTR_ERR(i2s->clk_sys); } - ret = clk_prepare_enable(i2s->clk_sys); - if (ret) - return ret; + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = img_i2s_in_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) + goto err_suspend; i2s->active_channels = 1; i2s->dma_data.addr = res->start + IMG_I2S_IN_RX_FIFO; @@ -447,7 +485,7 @@ static int img_i2s_in_probe(struct platform_device *pdev) if (IS_ERR(rst)) { if (PTR_ERR(rst) == -EPROBE_DEFER) { ret = -EPROBE_DEFER; - goto err_clk_disable; + goto err_suspend; } dev_dbg(dev, "No top level reset found\n"); @@ -469,42 +507,110 @@ static int img_i2s_in_probe(struct platform_device *pdev) IMG_I2S_IN_CH_CTL_JUST_MASK | IMG_I2S_IN_CH_CTL_FW_MASK, IMG_I2S_IN_CH_CTL); + pm_runtime_put(&pdev->dev); + + i2s->suspend_ch_ctl = devm_kzalloc(dev, + sizeof(*i2s->suspend_ch_ctl) * i2s->max_i2s_chan, GFP_KERNEL); + if (!i2s->suspend_ch_ctl) { + ret = -ENOMEM; + goto err_suspend; + } + ret = devm_snd_soc_register_component(dev, &img_i2s_in_component, &i2s->dai_driver, 1); if (ret) - goto err_clk_disable; + goto err_suspend; ret = devm_snd_dmaengine_pcm_register(dev, &img_i2s_in_dma_config, 0); if (ret) - goto err_clk_disable; + goto err_suspend; return 0; -err_clk_disable: - clk_disable_unprepare(i2s->clk_sys); +err_suspend: + if (!pm_runtime_enabled(&pdev->dev)) + img_i2s_in_runtime_suspend(&pdev->dev); +err_pm_disable: + pm_runtime_disable(&pdev->dev); return ret; } static int img_i2s_in_dev_remove(struct platform_device *pdev) { - struct img_i2s_in *i2s = platform_get_drvdata(pdev); + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + img_i2s_in_runtime_suspend(&pdev->dev); - clk_disable_unprepare(i2s->clk_sys); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int img_i2s_in_suspend(struct device *dev) +{ + struct img_i2s_in *i2s = dev_get_drvdata(dev); + int i, ret; + u32 reg; + + if (pm_runtime_status_suspended(dev)) { + ret = img_i2s_in_runtime_resume(dev); + if (ret) + return ret; + } + + for (i = 0; i < i2s->max_i2s_chan; i++) { + reg = img_i2s_in_ch_readl(i2s, i, IMG_I2S_IN_CH_CTL); + i2s->suspend_ch_ctl[i] = reg; + } + + i2s->suspend_ctl = img_i2s_in_readl(i2s, IMG_I2S_IN_CTL); + + img_i2s_in_runtime_suspend(dev); return 0; } +static int img_i2s_in_resume(struct device *dev) +{ + struct img_i2s_in *i2s = dev_get_drvdata(dev); + int i, ret; + u32 reg; + + ret = img_i2s_in_runtime_resume(dev); + if (ret) + return ret; + + for (i = 0; i < i2s->max_i2s_chan; i++) { + reg = i2s->suspend_ch_ctl[i]; + img_i2s_in_ch_writel(i2s, i, reg, IMG_I2S_IN_CH_CTL); + } + + img_i2s_in_writel(i2s, i2s->suspend_ctl, IMG_I2S_IN_CTL); + + if (pm_runtime_status_suspended(dev)) + img_i2s_in_runtime_suspend(dev); + + return 0; +} +#endif + static const struct of_device_id img_i2s_in_of_match[] = { { .compatible = "img,i2s-in" }, {} }; MODULE_DEVICE_TABLE(of, img_i2s_in_of_match); +static const struct dev_pm_ops img_i2s_in_pm_ops = { + SET_RUNTIME_PM_OPS(img_i2s_in_runtime_suspend, + img_i2s_in_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(img_i2s_in_suspend, img_i2s_in_resume) +}; + static struct platform_driver img_i2s_in_driver = { .driver = { .name = "img-i2s-in", - .of_match_table = img_i2s_in_of_match + .of_match_table = img_i2s_in_of_match, + .pm = &img_i2s_in_pm_ops }, .probe = img_i2s_in_probe, .remove = img_i2s_in_dev_remove diff --git a/sound/soc/img/img-i2s-out.c b/sound/soc/img/img-i2s-out.c index 78b7f6cd675b..30a95bcef2db 100644 --- a/sound/soc/img/img-i2s-out.c +++ b/sound/soc/img/img-i2s-out.c @@ -63,29 +63,36 @@ struct img_i2s_out { unsigned int active_channels; struct reset_control *rst; struct snd_soc_dai_driver dai_driver; + u32 suspend_ctl; + u32 *suspend_ch_ctl; }; -static int img_i2s_out_suspend(struct device *dev) +static int img_i2s_out_runtime_suspend(struct device *dev) { struct img_i2s_out *i2s = dev_get_drvdata(dev); - if (!i2s->force_clk_active) - clk_disable_unprepare(i2s->clk_ref); + clk_disable_unprepare(i2s->clk_ref); + clk_disable_unprepare(i2s->clk_sys); return 0; } -static int img_i2s_out_resume(struct device *dev) +static int img_i2s_out_runtime_resume(struct device *dev) { struct img_i2s_out *i2s = dev_get_drvdata(dev); int ret; - if (!i2s->force_clk_active) { - ret = clk_prepare_enable(i2s->clk_ref); - if (ret) { - dev_err(dev, "clk_enable failed: %d\n", ret); - return ret; - } + ret = clk_prepare_enable(i2s->clk_sys); + if (ret) { + dev_err(dev, "clk_enable failed: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(i2s->clk_ref); + if (ret) { + dev_err(dev, "clk_enable failed: %d\n", ret); + clk_disable_unprepare(i2s->clk_sys); + return ret; } return 0; @@ -287,7 +294,7 @@ static int img_i2s_out_hw_params(struct snd_pcm_substream *substream, static int img_i2s_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai); - int i; + int i, ret; bool force_clk_active; u32 chan_control_mask, control_mask, chan_control_set = 0; u32 reg, control_set = 0; @@ -342,6 +349,10 @@ static int img_i2s_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) chan_control_mask = IMG_I2S_OUT_CHAN_CTL_CLKT_MASK; + ret = pm_runtime_get_sync(i2s->dev); + if (ret < 0) + return ret; + img_i2s_out_disable(i2s); reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); @@ -361,6 +372,7 @@ static int img_i2s_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) img_i2s_out_ch_enable(i2s, i); img_i2s_out_enable(i2s); + pm_runtime_put(i2s->dev); i2s->force_clk_active = force_clk_active; @@ -467,9 +479,20 @@ static int img_i2s_out_probe(struct platform_device *pdev) return PTR_ERR(i2s->clk_ref); } - ret = clk_prepare_enable(i2s->clk_sys); - if (ret) - return ret; + i2s->suspend_ch_ctl = devm_kzalloc(dev, + sizeof(*i2s->suspend_ch_ctl) * i2s->max_i2s_chan, GFP_KERNEL); + if (!i2s->suspend_ch_ctl) + return -ENOMEM; + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = img_i2s_out_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) + goto err_suspend; reg = IMG_I2S_OUT_CTL_FRM_SIZE_MASK; img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL); @@ -483,13 +506,7 @@ static int img_i2s_out_probe(struct platform_device *pdev) img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL); img_i2s_out_reset(i2s); - - pm_runtime_enable(&pdev->dev); - if (!pm_runtime_enabled(&pdev->dev)) { - ret = img_i2s_out_resume(&pdev->dev); - if (ret) - goto err_pm_disable; - } + pm_runtime_put(&pdev->dev); i2s->active_channels = 1; i2s->dma_data.addr = res->start + IMG_I2S_OUT_TX_FIFO; @@ -517,26 +534,70 @@ static int img_i2s_out_probe(struct platform_device *pdev) err_suspend: if (!pm_runtime_status_suspended(&pdev->dev)) - img_i2s_out_suspend(&pdev->dev); + img_i2s_out_runtime_suspend(&pdev->dev); err_pm_disable: pm_runtime_disable(&pdev->dev); - clk_disable_unprepare(i2s->clk_sys); return ret; } static int img_i2s_out_dev_remove(struct platform_device *pdev) { - struct img_i2s_out *i2s = platform_get_drvdata(pdev); - pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev)) - img_i2s_out_suspend(&pdev->dev); + img_i2s_out_runtime_suspend(&pdev->dev); - clk_disable_unprepare(i2s->clk_sys); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int img_i2s_out_suspend(struct device *dev) +{ + struct img_i2s_out *i2s = dev_get_drvdata(dev); + int i, ret; + u32 reg; + + if (pm_runtime_status_suspended(dev)) { + ret = img_i2s_out_runtime_resume(dev); + if (ret) + return ret; + } + + for (i = 0; i < i2s->max_i2s_chan; i++) { + reg = img_i2s_out_ch_readl(i2s, i, IMG_I2S_OUT_CH_CTL); + i2s->suspend_ch_ctl[i] = reg; + } + + i2s->suspend_ctl = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL); + + img_i2s_out_runtime_suspend(dev); + + return 0; +} + +static int img_i2s_out_resume(struct device *dev) +{ + struct img_i2s_out *i2s = dev_get_drvdata(dev); + int i, ret; + u32 reg; + + ret = img_i2s_out_runtime_resume(dev); + if (ret) + return ret; + + for (i = 0; i < i2s->max_i2s_chan; i++) { + reg = i2s->suspend_ch_ctl[i]; + img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL); + } + + img_i2s_out_writel(i2s, i2s->suspend_ctl, IMG_I2S_OUT_CTL); + + if (pm_runtime_status_suspended(dev)) + img_i2s_out_runtime_suspend(dev); return 0; } +#endif static const struct of_device_id img_i2s_out_of_match[] = { { .compatible = "img,i2s-out" }, @@ -545,8 +606,9 @@ static const struct of_device_id img_i2s_out_of_match[] = { MODULE_DEVICE_TABLE(of, img_i2s_out_of_match); static const struct dev_pm_ops img_i2s_out_pm_ops = { - SET_RUNTIME_PM_OPS(img_i2s_out_suspend, - img_i2s_out_resume, NULL) + SET_RUNTIME_PM_OPS(img_i2s_out_runtime_suspend, + img_i2s_out_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(img_i2s_out_suspend, img_i2s_out_resume) }; static struct platform_driver img_i2s_out_driver = { diff --git a/sound/soc/img/img-parallel-out.c b/sound/soc/img/img-parallel-out.c index 23b0f0f6ec9c..acc005217be0 100644 --- a/sound/soc/img/img-parallel-out.c +++ b/sound/soc/img/img-parallel-out.c @@ -153,6 +153,7 @@ static int img_prl_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct img_prl_out *prl = snd_soc_dai_get_drvdata(dai); u32 reg, control_set = 0; + int ret; switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: @@ -164,9 +165,14 @@ static int img_prl_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return -EINVAL; } + ret = pm_runtime_get_sync(prl->dev); + if (ret < 0) + return ret; + reg = img_prl_out_readl(prl, IMG_PRL_OUT_CTL); reg = (reg & ~IMG_PRL_OUT_CTL_EDGE_MASK) | control_set; img_prl_out_writel(prl, reg, IMG_PRL_OUT_CTL); + pm_runtime_put(prl->dev); return 0; } diff --git a/sound/soc/img/img-spdif-in.c b/sound/soc/img/img-spdif-in.c index 8adfd65d4390..cedd40c8d1f3 100644 --- a/sound/soc/img/img-spdif-in.c +++ b/sound/soc/img/img-spdif-in.c @@ -16,6 +16,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/reset.h> #include <sound/core.h> @@ -82,11 +83,36 @@ struct img_spdif_in { unsigned int single_freq; unsigned int multi_freqs[IMG_SPDIF_IN_NUM_ACLKGEN]; bool active; + u32 suspend_clkgen; + u32 suspend_ctl; /* Write-only registers */ unsigned int aclkgen_regs[IMG_SPDIF_IN_NUM_ACLKGEN]; }; +static int img_spdif_in_runtime_suspend(struct device *dev) +{ + struct img_spdif_in *spdif = dev_get_drvdata(dev); + + clk_disable_unprepare(spdif->clk_sys); + + return 0; +} + +static int img_spdif_in_runtime_resume(struct device *dev) +{ + struct img_spdif_in *spdif = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(spdif->clk_sys); + if (ret) { + dev_err(dev, "Unable to enable sys clock\n"); + return ret; + } + + return 0; +} + static inline void img_spdif_in_writel(struct img_spdif_in *spdif, u32 val, u32 reg) { @@ -723,15 +749,21 @@ static int img_spdif_in_probe(struct platform_device *pdev) return PTR_ERR(spdif->clk_sys); } - ret = clk_prepare_enable(spdif->clk_sys); - if (ret) - return ret; + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = img_spdif_in_runtime_resume(&pdev->dev); + if (ret) + goto err_pm_disable; + } + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) + goto err_suspend; rst = devm_reset_control_get_exclusive(&pdev->dev, "rst"); if (IS_ERR(rst)) { if (PTR_ERR(rst) == -EPROBE_DEFER) { ret = -EPROBE_DEFER; - goto err_clk_disable; + goto err_pm_put; } dev_dbg(dev, "No top level reset found\n"); img_spdif_in_writel(spdif, IMG_SPDIF_IN_SOFT_RESET_MASK, @@ -759,42 +791,98 @@ static int img_spdif_in_probe(struct platform_device *pdev) IMG_SPDIF_IN_CTL_TRK_MASK; img_spdif_in_writel(spdif, reg, IMG_SPDIF_IN_CTL); + pm_runtime_put(&pdev->dev); + ret = devm_snd_soc_register_component(&pdev->dev, &img_spdif_in_component, &img_spdif_in_dai, 1); if (ret) - goto err_clk_disable; + goto err_suspend; ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (ret) - goto err_clk_disable; + goto err_suspend; return 0; -err_clk_disable: - clk_disable_unprepare(spdif->clk_sys); +err_pm_put: + pm_runtime_put(&pdev->dev); +err_suspend: + if (!pm_runtime_enabled(&pdev->dev)) + img_spdif_in_runtime_suspend(&pdev->dev); +err_pm_disable: + pm_runtime_disable(&pdev->dev); return ret; } static int img_spdif_in_dev_remove(struct platform_device *pdev) { - struct img_spdif_in *spdif = platform_get_drvdata(pdev); + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + img_spdif_in_runtime_suspend(&pdev->dev); - clk_disable_unprepare(spdif->clk_sys); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int img_spdif_in_suspend(struct device *dev) +{ + struct img_spdif_in *spdif = dev_get_drvdata(dev); + int ret; + + if (pm_runtime_status_suspended(dev)) { + ret = img_spdif_in_runtime_resume(dev); + if (ret) + return ret; + } + + spdif->suspend_clkgen = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CLKGEN); + spdif->suspend_ctl = img_spdif_in_readl(spdif, IMG_SPDIF_IN_CTL); + + img_spdif_in_runtime_suspend(dev); return 0; } +static int img_spdif_in_resume(struct device *dev) +{ + struct img_spdif_in *spdif = dev_get_drvdata(dev); + int i, ret; + + ret = img_spdif_in_runtime_resume(dev); + if (ret) + return ret; + + for (i = 0; i < IMG_SPDIF_IN_NUM_ACLKGEN; i++) + img_spdif_in_aclkgen_writel(spdif, i); + + img_spdif_in_writel(spdif, spdif->suspend_clkgen, IMG_SPDIF_IN_CLKGEN); + img_spdif_in_writel(spdif, spdif->suspend_ctl, IMG_SPDIF_IN_CTL); + + if (pm_runtime_status_suspended(dev)) + img_spdif_in_runtime_suspend(dev); + + return 0; +} +#endif + static const struct of_device_id img_spdif_in_of_match[] = { { .compatible = "img,spdif-in" }, {} }; MODULE_DEVICE_TABLE(of, img_spdif_in_of_match); +static const struct dev_pm_ops img_spdif_in_pm_ops = { + SET_RUNTIME_PM_OPS(img_spdif_in_runtime_suspend, + img_spdif_in_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(img_spdif_in_suspend, img_spdif_in_resume) +}; + static struct platform_driver img_spdif_in_driver = { .driver = { .name = "img-spdif-in", - .of_match_table = img_spdif_in_of_match + .of_match_table = img_spdif_in_of_match, + .pm = &img_spdif_in_pm_ops }, .probe = img_spdif_in_probe, .remove = img_spdif_in_dev_remove diff --git a/sound/soc/img/img-spdif-out.c b/sound/soc/img/img-spdif-out.c index 383655da2e60..934ed3df2ebf 100644 --- a/sound/soc/img/img-spdif-out.c +++ b/sound/soc/img/img-spdif-out.c @@ -47,25 +47,36 @@ struct img_spdif_out { struct snd_dmaengine_dai_dma_data dma_data; struct device *dev; struct reset_control *rst; + u32 suspend_ctl; + u32 suspend_csl; + u32 suspend_csh; }; -static int img_spdif_out_suspend(struct device *dev) +static int img_spdif_out_runtime_suspend(struct device *dev) { struct img_spdif_out *spdif = dev_get_drvdata(dev); clk_disable_unprepare(spdif->clk_ref); + clk_disable_unprepare(spdif->clk_sys); return 0; } -static int img_spdif_out_resume(struct device *dev) +static int img_spdif_out_runtime_resume(struct device *dev) { struct img_spdif_out *spdif = dev_get_drvdata(dev); int ret; + ret = clk_prepare_enable(spdif->clk_sys); + if (ret) { + dev_err(dev, "clk_enable failed: %d\n", ret); + return ret; + } + ret = clk_prepare_enable(spdif->clk_ref); if (ret) { dev_err(dev, "clk_enable failed: %d\n", ret); + clk_disable_unprepare(spdif->clk_sys); return ret; } @@ -355,21 +366,21 @@ static int img_spdif_out_probe(struct platform_device *pdev) return PTR_ERR(spdif->clk_ref); } - ret = clk_prepare_enable(spdif->clk_sys); - if (ret) - return ret; - - img_spdif_out_writel(spdif, IMG_SPDIF_OUT_CTL_FS_MASK, - IMG_SPDIF_OUT_CTL); - - img_spdif_out_reset(spdif); - pm_runtime_enable(&pdev->dev); if (!pm_runtime_enabled(&pdev->dev)) { - ret = img_spdif_out_resume(&pdev->dev); + ret = img_spdif_out_runtime_resume(&pdev->dev); if (ret) goto err_pm_disable; } + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) + goto err_suspend; + + img_spdif_out_writel(spdif, IMG_SPDIF_OUT_CTL_FS_MASK, + IMG_SPDIF_OUT_CTL); + + img_spdif_out_reset(spdif); + pm_runtime_put(&pdev->dev); spin_lock_init(&spdif->lock); @@ -393,27 +404,62 @@ static int img_spdif_out_probe(struct platform_device *pdev) err_suspend: if (!pm_runtime_status_suspended(&pdev->dev)) - img_spdif_out_suspend(&pdev->dev); + img_spdif_out_runtime_suspend(&pdev->dev); err_pm_disable: pm_runtime_disable(&pdev->dev); - clk_disable_unprepare(spdif->clk_sys); return ret; } static int img_spdif_out_dev_remove(struct platform_device *pdev) { - struct img_spdif_out *spdif = platform_get_drvdata(pdev); - pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev)) - img_spdif_out_suspend(&pdev->dev); + img_spdif_out_runtime_suspend(&pdev->dev); - clk_disable_unprepare(spdif->clk_sys); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int img_spdif_out_suspend(struct device *dev) +{ + struct img_spdif_out *spdif = dev_get_drvdata(dev); + int ret; + + if (pm_runtime_status_suspended(dev)) { + ret = img_spdif_out_runtime_resume(dev); + if (ret) + return ret; + } + + spdif->suspend_ctl = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CTL); + spdif->suspend_csl = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSL); + spdif->suspend_csh = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSH_UV); + + img_spdif_out_runtime_suspend(dev); return 0; } +static int img_spdif_out_resume(struct device *dev) +{ + struct img_spdif_out *spdif = dev_get_drvdata(dev); + int ret; + + ret = img_spdif_out_runtime_resume(dev); + if (ret) + return ret; + + img_spdif_out_writel(spdif, spdif->suspend_ctl, IMG_SPDIF_OUT_CTL); + img_spdif_out_writel(spdif, spdif->suspend_csl, IMG_SPDIF_OUT_CSL); + img_spdif_out_writel(spdif, spdif->suspend_csh, IMG_SPDIF_OUT_CSH_UV); + + if (pm_runtime_status_suspended(dev)) + img_spdif_out_runtime_suspend(dev); + + return 0; +} +#endif static const struct of_device_id img_spdif_out_of_match[] = { { .compatible = "img,spdif-out" }, {} @@ -421,8 +467,9 @@ static const struct of_device_id img_spdif_out_of_match[] = { MODULE_DEVICE_TABLE(of, img_spdif_out_of_match); static const struct dev_pm_ops img_spdif_out_pm_ops = { - SET_RUNTIME_PM_OPS(img_spdif_out_suspend, - img_spdif_out_resume, NULL) + SET_RUNTIME_PM_OPS(img_spdif_out_runtime_suspend, + img_spdif_out_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(img_spdif_out_suspend, img_spdif_out_resume) }; static struct platform_driver img_spdif_out_driver = { diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index b3c7f554ec30..bb8be10b8437 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -1,19 +1,3 @@ -config SND_MFLD_MACHINE - tristate "SOC Machine Audio driver for Intel Medfield MID platform" - depends on INTEL_SCU_IPC - select SND_SOC_SN95031 - select SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_PCI - help - This adds support for ASoC machine driver for Intel(R) MID Medfield platform - used as alsa device in audio substem in Intel(R) MID devices - Say Y if you have such a device. - If unsure select "N". - -config SND_SST_ATOM_HIFI2_PLATFORM - tristate - select SND_SOC_COMPRESS - config SND_SST_IPC tristate @@ -27,10 +11,12 @@ config SND_SST_IPC_ACPI select SND_SOC_INTEL_SST select IOSF_MBI +config SND_SOC_INTEL_COMMON + tristate + config SND_SOC_INTEL_SST tristate select SND_SOC_INTEL_SST_ACPI if ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI config SND_SOC_INTEL_SST_FIRMWARE tristate @@ -39,280 +25,42 @@ config SND_SOC_INTEL_SST_FIRMWARE config SND_SOC_INTEL_SST_ACPI tristate -config SND_SOC_INTEL_SST_MATCH +config SND_SOC_ACPI_INTEL_MATCH tristate + select SND_SOC_ACPI if ACPI + +config SND_SOC_INTEL_SST_TOPLEVEL + tristate "Intel ASoC SST drivers" + depends on X86 || COMPILE_TEST + select SND_SOC_INTEL_MACH + select SND_SOC_INTEL_COMMON config SND_SOC_INTEL_HASWELL - tristate + tristate "Intel ASoC SST driver for Haswell/Broadwell" + depends on SND_SOC_INTEL_SST_TOPLEVEL && SND_DMA_SGBUF + depends on DMADEVICES select SND_SOC_INTEL_SST select SND_SOC_INTEL_SST_FIRMWARE config SND_SOC_INTEL_BAYTRAIL - tristate - select SND_SOC_INTEL_SST - select SND_SOC_INTEL_SST_FIRMWARE - -config SND_SOC_INTEL_HASWELL_MACH - tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint" - depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM - depends on DMADEVICES - select SND_SOC_INTEL_HASWELL - select SND_SOC_RT5640 - help - This adds support for the Lynxpoint Audio DSP on Intel(R) Haswell - Ultrabook platforms. - Say Y if you have such a device. - If unsure select "N". - -config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH - tristate "ASoC Audio driver for Broxton with DA7219 and MAX98357A in I2S Mode" - depends on X86 && ACPI && I2C - select SND_SOC_INTEL_SKYLAKE - select SND_SOC_DA7219 - select SND_SOC_MAX98357A - select SND_SOC_DMIC - select SND_SOC_HDAC_HDMI - select SND_HDA_DSP_LOADER - help - This adds support for ASoC machine driver for Broxton-P platforms - with DA7219 + MAX98357A I2S audio codec. - Say Y if you have such a device. - If unsure select "N". - -config SND_SOC_INTEL_BXT_RT298_MACH - tristate "ASoC Audio driver for Broxton with RT298 I2S mode" - depends on X86 && ACPI && I2C - select SND_SOC_INTEL_SKYLAKE - select SND_SOC_RT298 - select SND_SOC_DMIC - select SND_SOC_HDAC_HDMI - select SND_HDA_DSP_LOADER - help - This adds support for ASoC machine driver for Broxton platforms - with RT286 I2S audio codec. - Say Y if you have such a device. - If unsure select "N". - -config SND_SOC_INTEL_BYT_RT5640_MACH - tristate "ASoC Audio driver for Intel Baytrail with RT5640 codec" - depends on X86_INTEL_LPSS && I2C + tristate "Intel ASoC SST driver for Baytrail (legacy)" + depends on SND_SOC_INTEL_SST_TOPLEVEL depends on DMADEVICES - depends on SND_SST_IPC_ACPI = n - select SND_SOC_INTEL_BAYTRAIL - select SND_SOC_RT5640 - help - This adds audio driver for Intel Baytrail platform based boards - with the RT5640 audio codec. This driver is deprecated, use - SND_SOC_INTEL_BYTCR_RT5640_MACH instead for better functionality. - -config SND_SOC_INTEL_BYT_MAX98090_MACH - tristate "ASoC Audio driver for Intel Baytrail with MAX98090 codec" - depends on X86_INTEL_LPSS && I2C - depends on DMADEVICES - depends on SND_SST_IPC_ACPI = n - select SND_SOC_INTEL_BAYTRAIL - select SND_SOC_MAX98090 - help - This adds audio driver for Intel Baytrail platform based boards - with the MAX98090 audio codec. - -config SND_SOC_INTEL_BDW_RT5677_MACH - tristate "ASoC Audio driver for Intel Broadwell with RT5677 codec" - depends on X86_INTEL_LPSS && GPIOLIB && I2C - depends on DMADEVICES - select SND_SOC_INTEL_HASWELL - select SND_SOC_RT5677 - help - This adds support for Intel Broadwell platform based boards with - the RT5677 audio codec. - -config SND_SOC_INTEL_BROADWELL_MACH - tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint" - depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM - depends on DMADEVICES - select SND_SOC_INTEL_HASWELL - select SND_SOC_RT286 - help - This adds support for the Wilcatpoint Audio DSP on Intel(R) Broadwell - Ultrabook platforms. - Say Y if you have such a device. - If unsure select "N". - -config SND_SOC_INTEL_BYTCR_RT5640_MACH - tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5640 codec" - depends on X86 && I2C && ACPI - select SND_SOC_RT5640 - select SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI - help - This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR - platforms with RT5640 audio codec. - Say Y if you have such a device. - If unsure select "N". - -config SND_SOC_INTEL_BYTCR_RT5651_MACH - tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5651 codec" - depends on X86 && I2C && ACPI - select SND_SOC_RT5651 - select SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI - help - This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR - platforms with RT5651 audio codec. - Say Y if you have such a device. - If unsure select "N". - -config SND_SOC_INTEL_CHT_BSW_RT5672_MACH - tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5672 codec" - depends on X86_INTEL_LPSS && I2C && ACPI - select SND_SOC_RT5670 - select SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI - help - This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell - platforms with RT5672 audio codec. - Say Y if you have such a device. - If unsure select "N". - -config SND_SOC_INTEL_CHT_BSW_RT5645_MACH - tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5645/5650 codec" - depends on X86_INTEL_LPSS && I2C && ACPI - select SND_SOC_RT5645 - select SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI - help - This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell - platforms with RT5645/5650 audio codec. - If unsure select "N". - -config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH - tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with MAX98090 & TI codec" - depends on X86_INTEL_LPSS && I2C && ACPI - select SND_SOC_MAX98090 - select SND_SOC_TS3A227E - select SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI - help - This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell - platforms with MAX98090 audio codec it also can support TI jack chip as aux device. - If unsure select "N". - -config SND_SOC_INTEL_BYT_CHT_DA7213_MACH - tristate "ASoC Audio driver for Intel Baytrail & Cherrytrail with DA7212/7213 codec" - depends on X86_INTEL_LPSS && I2C && ACPI - select SND_SOC_DA7213 - select SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI - help - This adds support for ASoC machine driver for Intel(R) Baytrail & CherryTrail - platforms with DA7212/7213 audio codec. - If unsure select "N". - -config SND_SOC_INTEL_BYT_CHT_ES8316_MACH - tristate "ASoC Audio driver for Intel Baytrail & Cherrytrail with ES8316 codec" - depends on X86_INTEL_LPSS && I2C && ACPI - select SND_SOC_ES8316 - select SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI - help - This adds support for ASoC machine driver for Intel(R) Baytrail & - Cherrytrail platforms with ES8316 audio codec. - If unsure select "N". - -config SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH - tristate "ASoC Audio driver for Intel Baytrail & Cherrytrail platform with no codec (MinnowBoard MAX, Up)" - depends on X86_INTEL_LPSS && I2C && ACPI - select SND_SST_ATOM_HIFI2_PLATFORM - select SND_SST_IPC_ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI - help - This adds support for ASoC machine driver for the MinnowBoard Max or - Up boards and provides access to I2S signals on the Low-Speed - connector - If unsure select "N". - -config SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH - tristate "ASoC Audio driver for KBL with RT5663 and MAX98927 in I2S Mode" - depends on X86_INTEL_LPSS && I2C select SND_SOC_INTEL_SST - select SND_SOC_INTEL_SKYLAKE - select SND_SOC_RT5663 - select SND_SOC_MAX98927 - select SND_SOC_DMIC - select SND_SOC_HDAC_HDMI - help - This adds support for ASoC Onboard Codec I2S machine driver. This will - create an alsa sound card for RT5663 + MAX98927. - Say Y if you have such a device. - If unsure select "N". + select SND_SOC_INTEL_SST_FIRMWARE -config SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH - tristate "ASoC Audio driver for KBL with RT5663, RT5514 and MAX98927 in I2S Mode" - depends on X86_INTEL_LPSS && I2C && SPI - select SND_SOC_INTEL_SST - select SND_SOC_INTEL_SKYLAKE - select SND_SOC_RT5663 - select SND_SOC_RT5514 - select SND_SOC_RT5514_SPI - select SND_SOC_MAX98927 - select SND_SOC_HDAC_HDMI - help - This adds support for ASoC Onboard Codec I2S machine driver. This will - create an alsa sound card for RT5663 + RT5514 + MAX98927. - Say Y if you have such a device. - If unsure select "N". +config SND_SST_ATOM_HIFI2_PLATFORM + tristate "Intel ASoC SST driver for HiFi2 platforms (*field, *trail)" + depends on SND_SOC_INTEL_SST_TOPLEVEL && X86 + select SND_SOC_COMPRESS config SND_SOC_INTEL_SKYLAKE - tristate + tristate "Intel ASoC SST driver for SKL/BXT/KBL/GLK/CNL" + depends on SND_SOC_INTEL_SST_TOPLEVEL && PCI && ACPI select SND_HDA_EXT_CORE select SND_HDA_DSP_LOADER select SND_SOC_TOPOLOGY select SND_SOC_INTEL_SST -config SND_SOC_INTEL_SKL_RT286_MACH - tristate "ASoC Audio driver for SKL with RT286 I2S mode" - depends on X86 && ACPI && I2C - select SND_SOC_INTEL_SKYLAKE - select SND_SOC_RT286 - select SND_SOC_DMIC - select SND_SOC_HDAC_HDMI - help - This adds support for ASoC machine driver for Skylake platforms - with RT286 I2S audio codec. - Say Y if you have such a device. - If unsure select "N". - -config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH - tristate "ASoC Audio driver for SKL with NAU88L25 and SSM4567 in I2S Mode" - depends on X86_INTEL_LPSS && I2C - select SND_SOC_INTEL_SKYLAKE - select SND_SOC_NAU8825 - select SND_SOC_SSM4567 - select SND_SOC_DMIC - select SND_SOC_HDAC_HDMI - help - This adds support for ASoC Onboard Codec I2S machine driver. This will - create an alsa sound card for NAU88L25 + SSM4567. - Say Y if you have such a device. - If unsure select "N". - -config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH - tristate "ASoC Audio driver for SKL with NAU88L25 and MAX98357A in I2S Mode" - depends on X86_INTEL_LPSS && I2C - select SND_SOC_INTEL_SKYLAKE - select SND_SOC_NAU8825 - select SND_SOC_MAX98357A - select SND_SOC_DMIC - select SND_SOC_HDAC_HDMI - help - This adds support for ASoC Onboard Codec I2S machine driver. This will - create an alsa sound card for NAU88L25 + MAX98357A. - Say Y if you have such a device. - If unsure select "N". +# ASoC codec drivers +source "sound/soc/intel/boards/Kconfig" diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index 62f37ffffdf0..b973d457e834 100644 --- a/sound/soc/intel/Makefile +++ b/sound/soc/intel/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 # Core support -obj-$(CONFIG_SND_SOC_INTEL_SST) += common/ +obj-$(CONFIG_SND_SOC_INTEL_COMMON) += common/ # Platform Support obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += haswell/ diff --git a/sound/soc/intel/atom/sst-mfld-platform-compress.c b/sound/soc/intel/atom/sst-mfld-platform-compress.c index 1bead81bb510..1dbcab5a6ff0 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-compress.c +++ b/sound/soc/intel/atom/sst-mfld-platform-compress.c @@ -259,7 +259,7 @@ static int sst_platform_compr_set_metadata(struct snd_compr_stream *cstream, return stream->compr_ops->set_metadata(sst->dev, stream->id, metadata); } -struct snd_compr_ops sst_platform_compr_ops = { +const struct snd_compr_ops sst_platform_compr_ops = { .open = sst_platform_compr_open, .free = sst_platform_compr_free, diff --git a/sound/soc/intel/atom/sst-mfld-platform.h b/sound/soc/intel/atom/sst-mfld-platform.h index cb32cc7e5ec1..31a58c25472c 100644 --- a/sound/soc/intel/atom/sst-mfld-platform.h +++ b/sound/soc/intel/atom/sst-mfld-platform.h @@ -25,7 +25,7 @@ #include "sst-atom-controls.h" extern struct sst_device *sst; -extern struct snd_compr_ops sst_platform_compr_ops; +extern const struct snd_compr_ops sst_platform_compr_ops; #define SST_MONO 1 #define SST_STEREO 2 diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c index 0e928d54305d..32d6e02e2104 100644 --- a/sound/soc/intel/atom/sst/sst_acpi.c +++ b/sound/soc/intel/atom/sst/sst_acpi.c @@ -23,7 +23,6 @@ #include <linux/interrupt.h> #include <linux/slab.h> #include <linux/io.h> -#include <linux/miscdevice.h> #include <linux/platform_device.h> #include <linux/firmware.h> #include <linux/pm_runtime.h> @@ -41,9 +40,10 @@ #include <acpi/acpi_bus.h> #include <asm/cpu_device_id.h> #include <asm/iosf_mbi.h> +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> #include "../sst-mfld-platform.h" #include "../../common/sst-dsp.h" -#include "../../common/sst-acpi.h" #include "sst.h" /* LPE viewpoint addresses */ @@ -239,19 +239,26 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) return 0; } +static int is_byt(void) +{ + bool status = false; + static const struct x86_cpu_id cpu_ids[] = { + { X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */ + {} + }; + if (x86_match_cpu(cpu_ids)) + status = true; + return status; +} static int is_byt_cr(struct device *dev, bool *bytcr) { int status = 0; if (IS_ENABLED(CONFIG_IOSF_MBI)) { - static const struct x86_cpu_id cpu_ids[] = { - { X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */ - {} - }; u32 bios_status; - if (!x86_match_cpu(cpu_ids) || !iosf_mbi_available()) { + if (!is_byt() || !iosf_mbi_available()) { /* bail silently */ return status; } @@ -285,7 +292,7 @@ static int sst_acpi_probe(struct platform_device *pdev) int ret = 0; struct intel_sst_drv *ctx; const struct acpi_device_id *id; - struct sst_acpi_mach *mach; + struct snd_soc_acpi_mach *mach; struct platform_device *mdev; struct platform_device *plat_dev; struct sst_platform_info *pdata; @@ -297,13 +304,17 @@ static int sst_acpi_probe(struct platform_device *pdev) return -ENODEV; dev_dbg(dev, "for %s\n", id->id); - mach = (struct sst_acpi_mach *)id->driver_data; - mach = sst_acpi_find_machine(mach); + mach = (struct snd_soc_acpi_mach *)id->driver_data; + mach = snd_soc_acpi_find_machine(mach); if (mach == NULL) { dev_err(dev, "No matching machine driver found\n"); return -ENODEV; } + if (is_byt()) + mach->pdata = &byt_rvp_platform_data; + else + mach->pdata = &chv_platform_data; pdata = mach->pdata; ret = kstrtouint(id->id, 16, &dev_id); @@ -381,286 +392,9 @@ static int sst_acpi_remove(struct platform_device *pdev) return 0; } -static unsigned long cht_machine_id; - -#define CHT_SURFACE_MACH 1 -#define BYT_THINKPAD_10 2 - -static int cht_surface_quirk_cb(const struct dmi_system_id *id) -{ - cht_machine_id = CHT_SURFACE_MACH; - return 1; -} - -static int byt_thinkpad10_quirk_cb(const struct dmi_system_id *id) -{ - cht_machine_id = BYT_THINKPAD_10; - return 1; -} - - -static const struct dmi_system_id byt_table[] = { - { - .callback = byt_thinkpad10_quirk_cb, - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 10"), - }, - }, - { - .callback = byt_thinkpad10_quirk_cb, - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Tablet B"), - }, - }, - { - .callback = byt_thinkpad10_quirk_cb, - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Miix 2 10"), - }, - }, - { } -}; - -static const struct dmi_system_id cht_table[] = { - { - .callback = cht_surface_quirk_cb, - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"), - }, - }, - { } -}; - - -static struct sst_acpi_mach cht_surface_mach = { - .id = "10EC5640", - .drv_name = "cht-bsw-rt5645", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "cht-bsw", - .pdata = &chv_platform_data, -}; - -static struct sst_acpi_mach byt_thinkpad_10 = { - .id = "10EC5640", - .drv_name = "cht-bsw-rt5672", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "cht-bsw", - .pdata = &byt_rvp_platform_data, -}; - -static struct sst_acpi_mach *cht_quirk(void *arg) -{ - struct sst_acpi_mach *mach = arg; - - dmi_check_system(cht_table); - - if (cht_machine_id == CHT_SURFACE_MACH) - return &cht_surface_mach; - else - return mach; -} - -static struct sst_acpi_mach *byt_quirk(void *arg) -{ - struct sst_acpi_mach *mach = arg; - - dmi_check_system(byt_table); - - if (cht_machine_id == BYT_THINKPAD_10) - return &byt_thinkpad_10; - else - return mach; -} - - -static struct sst_acpi_mach sst_acpi_bytcr[] = { - { - .id = "10EC5640", - .drv_name = "bytcr_rt5640", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "bytcr_rt5640", - .machine_quirk = byt_quirk, - .pdata = &byt_rvp_platform_data, - }, - { - .id = "10EC5642", - .drv_name = "bytcr_rt5640", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "bytcr_rt5640", - .pdata = &byt_rvp_platform_data - }, - { - .id = "INTCCFFD", - .drv_name = "bytcr_rt5640", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "bytcr_rt5640", - .pdata = &byt_rvp_platform_data - }, - { - .id = "10EC5651", - .drv_name = "bytcr_rt5651", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "bytcr_rt5651", - .pdata = &byt_rvp_platform_data - }, - { - .id = "DLGS7212", - .drv_name = "bytcht_da7213", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "bytcht_da7213", - .pdata = &byt_rvp_platform_data - }, - { - .id = "DLGS7213", - .drv_name = "bytcht_da7213", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "bytcht_da7213", - .pdata = &byt_rvp_platform_data - }, - /* some Baytrail platforms rely on RT5645, use CHT machine driver */ - { - .id = "10EC5645", - .drv_name = "cht-bsw-rt5645", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "cht-bsw", - .pdata = &byt_rvp_platform_data - }, - { - .id = "10EC5648", - .drv_name = "cht-bsw-rt5645", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "cht-bsw", - .pdata = &byt_rvp_platform_data - }, -#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) - /* - * This is always last in the table so that it is selected only when - * enabled explicitly and there is no codec-related information in SSDT - */ - { - .id = "80860F28", - .drv_name = "bytcht_nocodec", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "bytcht_nocodec", - .pdata = &byt_rvp_platform_data - }, -#endif - {}, -}; - -/* Cherryview-based platforms: CherryTrail and Braswell */ -static struct sst_acpi_mach sst_acpi_chv[] = { - { - .id = "10EC5670", - .drv_name = "cht-bsw-rt5672", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "cht-bsw", - .pdata = &chv_platform_data - }, - { - .id = "10EC5672", - .drv_name = "cht-bsw-rt5672", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "cht-bsw", - .pdata = &chv_platform_data - }, - { - .id = "10EC5645", - .drv_name = "cht-bsw-rt5645", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "cht-bsw", - .pdata = &chv_platform_data - }, - { - .id = "10EC5650", - .drv_name = "cht-bsw-rt5645", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "cht-bsw", - .pdata = &chv_platform_data - }, - { - .id = "10EC3270", - .drv_name = "cht-bsw-rt5645", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "cht-bsw", - .pdata = &chv_platform_data - }, - - { - .id = "193C9890", - .drv_name = "cht-bsw-max98090", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "cht-bsw", - .pdata = &chv_platform_data - }, - { - .id = "DLGS7212", - .drv_name = "bytcht_da7213", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "bytcht_da7213", - .pdata = &chv_platform_data - }, - { - .id = "DLGS7213", - .drv_name = "bytcht_da7213", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "bytcht_da7213", - .pdata = &chv_platform_data - }, - { - .id = "ESSX8316", - .drv_name = "bytcht_es8316", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "bytcht_es8316", - .pdata = &chv_platform_data - }, - /* some CHT-T platforms rely on RT5640, use Baytrail machine driver */ - { - .id = "10EC5640", - .drv_name = "bytcr_rt5640", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "bytcr_rt5640", - .machine_quirk = cht_quirk, - .pdata = &chv_platform_data - }, - { - .id = "10EC3276", - .drv_name = "bytcr_rt5640", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "bytcr_rt5640", - .pdata = &chv_platform_data - }, - /* some CHT-T platforms rely on RT5651, use Baytrail machine driver */ - { - .id = "10EC5651", - .drv_name = "bytcr_rt5651", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "bytcr_rt5651", - .pdata = &chv_platform_data - }, -#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) - /* - * This is always last in the table so that it is selected only when - * enabled explicitly and there is no codec-related information in SSDT - */ - { - .id = "808622A8", - .drv_name = "bytcht_nocodec", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "bytcht_nocodec", - .pdata = &chv_platform_data - }, -#endif - {}, -}; - static const struct acpi_device_id sst_acpi_ids[] = { - { "80860F28", (unsigned long)&sst_acpi_bytcr}, - { "808622A8", (unsigned long) &sst_acpi_chv}, + { "80860F28", (unsigned long)&snd_soc_acpi_intel_baytrail_machines}, + { "808622A8", (unsigned long)&snd_soc_acpi_intel_cherrytrail_machines}, { }, }; diff --git a/sound/soc/intel/atom/sst/sst_loader.c b/sound/soc/intel/atom/sst/sst_loader.c index 33917146d9c4..a686eef2cf7f 100644 --- a/sound/soc/intel/atom/sst/sst_loader.c +++ b/sound/soc/intel/atom/sst/sst_loader.c @@ -415,7 +415,6 @@ int sst_load_fw(struct intel_sst_drv *sst_drv_ctx) return ret_val; } - BUG_ON(!sst_drv_ctx->fw_in_mem); block = sst_create_block(sst_drv_ctx, 0, FW_DWNL_ID); if (block == NULL) return -ENOMEM; diff --git a/sound/soc/intel/atom/sst/sst_stream.c b/sound/soc/intel/atom/sst/sst_stream.c index 83d8dda15233..65e257b17a7e 100644 --- a/sound/soc/intel/atom/sst/sst_stream.c +++ b/sound/soc/intel/atom/sst/sst_stream.c @@ -45,7 +45,6 @@ int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params) void *data = NULL; dev_dbg(sst_drv_ctx->dev, "Enter\n"); - BUG_ON(!params); str_params = (struct snd_sst_params *)params; memset(&alloc_param, 0, sizeof(alloc_param)); diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig new file mode 100644 index 000000000000..6f754708a48c --- /dev/null +++ b/sound/soc/intel/boards/Kconfig @@ -0,0 +1,265 @@ +config SND_SOC_INTEL_MACH + tristate "Intel Audio machine drivers" + depends on SND_SOC_INTEL_SST_TOPLEVEL + select SND_SOC_ACPI_INTEL_MATCH if ACPI + +if SND_SOC_INTEL_MACH + +config SND_MFLD_MACHINE + tristate "SOC Machine Audio driver for Intel Medfield MID platform" + depends on INTEL_SCU_IPC + select SND_SOC_SN95031 + depends on SND_SST_ATOM_HIFI2_PLATFORM + select SND_SST_IPC_PCI + help + This adds support for ASoC machine driver for Intel(R) MID Medfield platform + used as alsa device in audio substem in Intel(R) MID devices + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_INTEL_HASWELL_MACH + tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint" + depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM + depends on SND_SOC_INTEL_HASWELL + select SND_SOC_RT5640 + help + This adds support for the Lynxpoint Audio DSP on Intel(R) Haswell + Ultrabook platforms. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_INTEL_BDW_RT5677_MACH + tristate "ASoC Audio driver for Intel Broadwell with RT5677 codec" + depends on X86_INTEL_LPSS && GPIOLIB && I2C + depends on SND_SOC_INTEL_HASWELL + select SND_SOC_RT5677 + help + This adds support for Intel Broadwell platform based boards with + the RT5677 audio codec. + +config SND_SOC_INTEL_BROADWELL_MACH + tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint" + depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM + depends on SND_SOC_INTEL_HASWELL + select SND_SOC_RT286 + help + This adds support for the Wilcatpoint Audio DSP on Intel(R) Broadwell + Ultrabook platforms. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_INTEL_BYT_MAX98090_MACH + tristate "ASoC Audio driver for Intel Baytrail with MAX98090 codec" + depends on X86_INTEL_LPSS && I2C + depends on SND_SST_IPC_ACPI = n + depends on SND_SOC_INTEL_BAYTRAIL + select SND_SOC_MAX98090 + help + This adds audio driver for Intel Baytrail platform based boards + with the MAX98090 audio codec. + +config SND_SOC_INTEL_BYT_RT5640_MACH + tristate "ASoC Audio driver for Intel Baytrail with RT5640 codec" + depends on X86_INTEL_LPSS && I2C + depends on SND_SST_IPC_ACPI = n + depends on SND_SOC_INTEL_BAYTRAIL + select SND_SOC_RT5640 + help + This adds audio driver for Intel Baytrail platform based boards + with the RT5640 audio codec. This driver is deprecated, use + SND_SOC_INTEL_BYTCR_RT5640_MACH instead for better functionality. + +config SND_SOC_INTEL_BYTCR_RT5640_MACH + tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5640 codec" + depends on X86 && I2C && ACPI + select SND_SOC_RT5640 + depends on SND_SST_ATOM_HIFI2_PLATFORM + select SND_SST_IPC_ACPI + help + This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR + platforms with RT5640 audio codec. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_INTEL_BYTCR_RT5651_MACH + tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5651 codec" + depends on X86 && I2C && ACPI + select SND_SOC_RT5651 + depends on SND_SST_ATOM_HIFI2_PLATFORM + select SND_SST_IPC_ACPI + help + This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR + platforms with RT5651 audio codec. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_INTEL_CHT_BSW_RT5672_MACH + tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5672 codec" + depends on X86_INTEL_LPSS && I2C && ACPI + select SND_SOC_RT5670 + depends on SND_SST_ATOM_HIFI2_PLATFORM + select SND_SST_IPC_ACPI + help + This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell + platforms with RT5672 audio codec. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_INTEL_CHT_BSW_RT5645_MACH + tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5645/5650 codec" + depends on X86_INTEL_LPSS && I2C && ACPI + select SND_SOC_RT5645 + depends on SND_SST_ATOM_HIFI2_PLATFORM + select SND_SST_IPC_ACPI + help + This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell + platforms with RT5645/5650 audio codec. + If unsure select "N". + +config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH + tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with MAX98090 & TI codec" + depends on X86_INTEL_LPSS && I2C && ACPI + select SND_SOC_MAX98090 + select SND_SOC_TS3A227E + depends on SND_SST_ATOM_HIFI2_PLATFORM + select SND_SST_IPC_ACPI + help + This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell + platforms with MAX98090 audio codec it also can support TI jack chip as aux device. + If unsure select "N". + +config SND_SOC_INTEL_BYT_CHT_DA7213_MACH + tristate "ASoC Audio driver for Intel Baytrail & Cherrytrail with DA7212/7213 codec" + depends on X86_INTEL_LPSS && I2C && ACPI + select SND_SOC_DA7213 + depends on SND_SST_ATOM_HIFI2_PLATFORM + select SND_SST_IPC_ACPI + help + This adds support for ASoC machine driver for Intel(R) Baytrail & CherryTrail + platforms with DA7212/7213 audio codec. + If unsure select "N". + +config SND_SOC_INTEL_BYT_CHT_ES8316_MACH + tristate "ASoC Audio driver for Intel Baytrail & Cherrytrail with ES8316 codec" + depends on X86_INTEL_LPSS && I2C && ACPI + select SND_SOC_ES8316 + depends on SND_SST_ATOM_HIFI2_PLATFORM + select SND_SST_IPC_ACPI + help + This adds support for ASoC machine driver for Intel(R) Baytrail & + Cherrytrail platforms with ES8316 audio codec. + If unsure select "N". + +config SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH + tristate "ASoC Audio driver for Intel Baytrail & Cherrytrail platform with no codec (MinnowBoard MAX, Up)" + depends on X86_INTEL_LPSS && I2C && ACPI + depends on SND_SST_ATOM_HIFI2_PLATFORM + select SND_SST_IPC_ACPI + help + This adds support for ASoC machine driver for the MinnowBoard Max or + Up boards and provides access to I2S signals on the Low-Speed + connector + If unsure select "N". + +config SND_SOC_INTEL_SKL_RT286_MACH + tristate "ASoC Audio driver for SKL with RT286 I2S mode" + depends on X86 && ACPI && I2C + depends on SND_SOC_INTEL_SKYLAKE + select SND_SOC_RT286 + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + help + This adds support for ASoC machine driver for Skylake platforms + with RT286 I2S audio codec. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH + tristate "ASoC Audio driver for SKL with NAU88L25 and SSM4567 in I2S Mode" + depends on X86_INTEL_LPSS && I2C + depends on SND_SOC_INTEL_SKYLAKE + select SND_SOC_NAU8825 + select SND_SOC_SSM4567 + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + help + This adds support for ASoC Onboard Codec I2S machine driver. This will + create an alsa sound card for NAU88L25 + SSM4567. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH + tristate "ASoC Audio driver for SKL with NAU88L25 and MAX98357A in I2S Mode" + depends on X86_INTEL_LPSS && I2C + depends on SND_SOC_INTEL_SKYLAKE + select SND_SOC_NAU8825 + select SND_SOC_MAX98357A + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + help + This adds support for ASoC Onboard Codec I2S machine driver. This will + create an alsa sound card for NAU88L25 + MAX98357A. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH + tristate "ASoC Audio driver for Broxton with DA7219 and MAX98357A in I2S Mode" + depends on X86 && ACPI && I2C + depends on SND_SOC_INTEL_SKYLAKE + select SND_SOC_DA7219 + select SND_SOC_MAX98357A + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + select SND_HDA_DSP_LOADER + help + This adds support for ASoC machine driver for Broxton-P platforms + with DA7219 + MAX98357A I2S audio codec. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_INTEL_BXT_RT298_MACH + tristate "ASoC Audio driver for Broxton with RT298 I2S mode" + depends on X86 && ACPI && I2C + depends on SND_SOC_INTEL_SKYLAKE + select SND_SOC_RT298 + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + select SND_HDA_DSP_LOADER + help + This adds support for ASoC machine driver for Broxton platforms + with RT286 I2S audio codec. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH + tristate "ASoC Audio driver for KBL with RT5663 and MAX98927 in I2S Mode" + depends on X86_INTEL_LPSS && I2C + select SND_SOC_INTEL_SST + depends on SND_SOC_INTEL_SKYLAKE + select SND_SOC_RT5663 + select SND_SOC_MAX98927 + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + help + This adds support for ASoC Onboard Codec I2S machine driver. This will + create an alsa sound card for RT5663 + MAX98927. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH + tristate "ASoC Audio driver for KBL with RT5663, RT5514 and MAX98927 in I2S Mode" + depends on X86_INTEL_LPSS && I2C && SPI + select SND_SOC_INTEL_SST + depends on SND_SOC_INTEL_SKYLAKE + select SND_SOC_RT5663 + select SND_SOC_RT5514 + select SND_SOC_RT5514_SPI + select SND_SOC_MAX98927 + select SND_SOC_HDAC_HDMI + help + This adds support for ASoC Onboard Codec I2S machine driver. This will + create an alsa sound card for RT5663 + RT5514 + MAX98927. + Say Y if you have such a device. + If unsure select "N". + +endif diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c index ce35ec7884d1..f8a91a6f2a17 100644 --- a/sound/soc/intel/boards/bxt_da7219_max98357a.c +++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c @@ -55,20 +55,6 @@ enum { BXT_DPCM_AUDIO_HDMI3_PB, }; -static inline struct snd_soc_dai *bxt_get_codec_dai(struct snd_soc_card *card) -{ - struct snd_soc_pcm_runtime *rtd; - - list_for_each_entry(rtd, &card->rtd_list, list) { - - if (!strncmp(rtd->codec_dai->name, BXT_DIALOG_CODEC_DAI, - strlen(BXT_DIALOG_CODEC_DAI))) - return rtd->codec_dai; - } - - return NULL; -} - static int platform_clock_control(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { @@ -77,7 +63,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, struct snd_soc_card *card = dapm->card; struct snd_soc_dai *codec_dai; - codec_dai = bxt_get_codec_dai(card); + codec_dai = snd_soc_card_get_codec_dai(card, BXT_DIALOG_CODEC_DAI); if (!codec_dai) { dev_err(card->dev, "Codec dai not found; Unable to set/unset codec pll\n"); return -EIO; diff --git a/sound/soc/intel/boards/bytcht_da7213.c b/sound/soc/intel/boards/bytcht_da7213.c index 18873e23f404..c4d82ad41bd7 100644 --- a/sound/soc/intel/boards/bytcht_da7213.c +++ b/sound/soc/intel/boards/bytcht_da7213.c @@ -27,9 +27,9 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/soc-acpi.h> #include "../../codecs/da7213.h" #include "../atom/sst-atom-controls.h" -#include "../common/sst-acpi.h" static const struct snd_kcontrol_new controls[] = { SOC_DAPM_PIN_SWITCH("Headphone Jack"), @@ -185,19 +185,11 @@ static struct snd_soc_dai_link dailink[] = { .dpcm_playback = 1, .ops = &aif1_ops, }, - [MERR_DPCM_COMPR] = { - .name = "Compressed Port", - .stream_name = "Compress", - .cpu_dai_name = "compress-cpu-dai", - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .platform_name = "sst-mfld-platform", - }, /* CODEC<->CODEC link */ /* back ends */ { .name = "SSP2-Codec", - .id = 1, + .id = 0, .cpu_dai_name = "ssp2-port", .platform_name = "sst-mfld-platform", .no_pcm = 1, @@ -231,19 +223,18 @@ static char codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */ static int bytcht_da7213_probe(struct platform_device *pdev) { - int ret_val = 0; - int i; struct snd_soc_card *card; - struct sst_acpi_mach *mach; + struct snd_soc_acpi_mach *mach; const char *i2c_name = NULL; int dai_index = 0; + int ret_val = 0; + int i; mach = (&pdev->dev)->platform_data; card = &bytcht_da7213_card; card->dev = &pdev->dev; /* fix index of codec dai */ - dai_index = MERR_DPCM_COMPR + 1; for (i = 0; i < ARRAY_SIZE(dailink); i++) { if (!strcmp(dailink[i].codec_name, "i2c-DLGS7213:00")) { dai_index = i; @@ -252,8 +243,8 @@ static int bytcht_da7213_probe(struct platform_device *pdev) } /* fixup codec name based on HID */ - i2c_name = sst_acpi_find_name_from_hid(mach->id); - if (i2c_name != NULL) { + i2c_name = snd_soc_acpi_find_name_from_hid(mach->id); + if (i2c_name) { snprintf(codec_name, sizeof(codec_name), "%s%s", "i2c-", i2c_name); dailink[dai_index].codec_name = codec_name; diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c index 52635462dac6..8088396717e3 100644 --- a/sound/soc/intel/boards/bytcht_es8316.c +++ b/sound/soc/intel/boards/bytcht_es8316.c @@ -29,28 +29,14 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/soc-acpi.h> #include "../atom/sst-atom-controls.h" -#include "../common/sst-acpi.h" #include "../common/sst-dsp.h" struct byt_cht_es8316_private { struct clk *mclk; }; -#define CODEC_DAI1 "ES8316 HiFi" - -static inline struct snd_soc_dai *get_codec_dai(struct snd_soc_card *card) -{ - struct snd_soc_pcm_runtime *rtd; - - list_for_each_entry(rtd, &card->rtd_list, list) { - if (!strncmp(rtd->codec_dai->name, CODEC_DAI1, - strlen(CODEC_DAI1))) - return rtd->codec_dai; - } - return NULL; -} - static const struct snd_soc_dapm_widget byt_cht_es8316_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), @@ -208,22 +194,13 @@ static struct snd_soc_dai_link byt_cht_es8316_dais[] = { .ops = &byt_cht_es8316_aif1_ops, }, - [MERR_DPCM_COMPR] = { - .name = "Compressed Port", - .stream_name = "Compress", - .cpu_dai_name = "compress-cpu-dai", - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .platform_name = "sst-mfld-platform", - }, - /* back ends */ { /* Only SSP2 has been tested here, so BYT-CR platforms that * require SSP0 will not work. */ .name = "SSP2-Codec", - .id = 1, + .id = 0, .cpu_dai_name = "ssp2-port", .platform_name = "sst-mfld-platform", .no_pcm = 1, diff --git a/sound/soc/intel/boards/bytcht_nocodec.c b/sound/soc/intel/boards/bytcht_nocodec.c index 1dd9441806fa..b80ec027a0e8 100644 --- a/sound/soc/intel/boards/bytcht_nocodec.c +++ b/sound/soc/intel/boards/bytcht_nocodec.c @@ -133,19 +133,11 @@ static struct snd_soc_dai_link dais[] = { .dpcm_playback = 1, .ops = &aif1_ops, }, - [MERR_DPCM_COMPR] = { - .name = "Compressed Port", - .stream_name = "Compress", - .cpu_dai_name = "compress-cpu-dai", - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .platform_name = "sst-mfld-platform", - }, /* CODEC<->CODEC link */ /* back ends */ { .name = "SSP2-LowSpeed Connector", - .id = 1, + .id = 0, .cpu_dai_name = "ssp2-port", .platform_name = "sst-mfld-platform", .no_pcm = 1, diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 4a76b099a508..f2c0fc415e52 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -22,19 +22,19 @@ #include <linux/moduleparam.h> #include <linux/platform_device.h> #include <linux/acpi.h> +#include <linux/clk.h> #include <linux/device.h> #include <linux/dmi.h> #include <linux/slab.h> #include <asm/cpu_device_id.h> #include <asm/platform_sst_audio.h> -#include <linux/clk.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/jack.h> +#include <sound/soc-acpi.h> #include "../../codecs/rt5640.h" #include "../atom/sst-atom-controls.h" -#include "../common/sst-acpi.h" #include "../common/sst-dsp.h" enum { @@ -44,13 +44,13 @@ enum { BYT_RT5640_IN3_MAP, }; -#define BYT_RT5640_MAP(quirk) ((quirk) & 0xff) +#define BYT_RT5640_MAP(quirk) ((quirk) & GENMASK(7, 0)) #define BYT_RT5640_DMIC_EN BIT(16) #define BYT_RT5640_MONO_SPEAKER BIT(17) #define BYT_RT5640_DIFF_MIC BIT(18) /* defaut is single-ended */ -#define BYT_RT5640_SSP2_AIF2 BIT(19) /* default is using AIF1 */ -#define BYT_RT5640_SSP0_AIF1 BIT(20) -#define BYT_RT5640_SSP0_AIF2 BIT(21) +#define BYT_RT5640_SSP2_AIF2 BIT(19) /* default is using AIF1 */ +#define BYT_RT5640_SSP0_AIF1 BIT(20) +#define BYT_RT5640_SSP0_AIF2 BIT(21) #define BYT_RT5640_MCLK_EN BIT(22) #define BYT_RT5640_MCLK_25MHZ BIT(23) @@ -145,22 +145,6 @@ static void log_quirks(struct device *dev) #define BYT_CODEC_DAI1 "rt5640-aif1" #define BYT_CODEC_DAI2 "rt5640-aif2" -static inline struct snd_soc_dai *byt_get_codec_dai(struct snd_soc_card *card) -{ - struct snd_soc_pcm_runtime *rtd; - - list_for_each_entry(rtd, &card->rtd_list, list) { - if (!strncmp(rtd->codec_dai->name, BYT_CODEC_DAI1, - strlen(BYT_CODEC_DAI1))) - return rtd->codec_dai; - if (!strncmp(rtd->codec_dai->name, BYT_CODEC_DAI2, - strlen(BYT_CODEC_DAI2))) - return rtd->codec_dai; - - } - return NULL; -} - static int platform_clock_control(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { @@ -170,7 +154,10 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card); int ret; - codec_dai = byt_get_codec_dai(card); + codec_dai = snd_soc_card_get_codec_dai(card, BYT_CODEC_DAI1); + if (!codec_dai) + codec_dai = snd_soc_card_get_codec_dai(card, BYT_CODEC_DAI2); + if (!codec_dai) { dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n"); @@ -178,7 +165,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, } if (SND_SOC_DAPM_EVENT_ON(event)) { - if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk) { + if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN) { ret = clk_prepare_enable(priv->mclk); if (ret < 0) { dev_err(card->dev, @@ -199,7 +186,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, 48000 * 512, SND_SOC_CLOCK_IN); if (!ret) { - if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk) + if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN) clk_disable_unprepare(priv->mclk); } } @@ -376,8 +363,8 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"), }, - .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP | - BYT_RT5640_MCLK_EN), + .driver_data = (void *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_MCLK_EN), }, { .callback = byt_rt5640_quirk_cb, @@ -385,12 +372,11 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TAF"), }, - .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP | - BYT_RT5640_MONO_SPEAKER | - BYT_RT5640_DIFF_MIC | - BYT_RT5640_SSP0_AIF2 | - BYT_RT5640_MCLK_EN - ), + .driver_data = (void *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_MONO_SPEAKER | + BYT_RT5640_DIFF_MIC | + BYT_RT5640_SSP0_AIF2 | + BYT_RT5640_MCLK_EN), }, { .callback = byt_rt5640_quirk_cb, @@ -398,9 +384,9 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "DellInc."), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"), }, - .driver_data = (unsigned long *)(BYT_RT5640_DMIC2_MAP | - BYT_RT5640_DMIC_EN | - BYT_RT5640_MCLK_EN), + .driver_data = (void *)(BYT_RT5640_DMIC2_MAP | + BYT_RT5640_DMIC_EN | + BYT_RT5640_MCLK_EN), }, { .callback = byt_rt5640_quirk_cb, @@ -408,8 +394,8 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP ElitePad 1000 G2"), }, - .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP | - BYT_RT5640_MCLK_EN), + .driver_data = (void *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_MCLK_EN), }, { .callback = byt_rt5640_quirk_cb, @@ -417,8 +403,8 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"), DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max B3 PLATFORM"), }, - .driver_data = (unsigned long *)(BYT_RT5640_DMIC1_MAP | - BYT_RT5640_DMIC_EN), + .driver_data = (void *)(BYT_RT5640_DMIC1_MAP | + BYT_RT5640_DMIC_EN), }, { .callback = byt_rt5640_quirk_cb, @@ -426,9 +412,9 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"), DMI_MATCH(DMI_BOARD_NAME, "tPAD"), }, - .driver_data = (unsigned long *)(BYT_RT5640_IN3_MAP | - BYT_RT5640_MCLK_EN | - BYT_RT5640_SSP0_AIF1), + .driver_data = (void *)(BYT_RT5640_IN3_MAP | + BYT_RT5640_MCLK_EN | + BYT_RT5640_SSP0_AIF1), }, { .callback = byt_rt5640_quirk_cb, @@ -436,7 +422,7 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"), }, - .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP | + .driver_data = (void *)(BYT_RT5640_IN1_MAP | BYT_RT5640_MCLK_EN | BYT_RT5640_SSP0_AIF1), @@ -446,9 +432,9 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), }, - .driver_data = (unsigned long *)(BYT_RT5640_IN3_MAP | - BYT_RT5640_MCLK_EN | - BYT_RT5640_SSP0_AIF1), + .driver_data = (void *)(BYT_RT5640_IN3_MAP | + BYT_RT5640_MCLK_EN | + BYT_RT5640_SSP0_AIF1), }, {} @@ -456,12 +442,12 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) { - int ret; - struct snd_soc_codec *codec = runtime->codec; struct snd_soc_card *card = runtime->card; - const struct snd_soc_dapm_route *custom_map; struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_codec *codec = runtime->codec; + const struct snd_soc_dapm_route *custom_map; int num_routes; + int ret; card->dapm.idle_bias_off = true; @@ -549,7 +535,7 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone"); snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker"); - if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk) { + if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN) { /* * The firmware might enable the clock at * boot (this information may or may not @@ -693,18 +679,10 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = { .dpcm_playback = 1, .ops = &byt_rt5640_aif1_ops, }, - [MERR_DPCM_COMPR] = { - .name = "Baytrail Compressed Port", - .stream_name = "Baytrail Compress", - .cpu_dai_name = "compress-cpu-dai", - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .platform_name = "sst-mfld-platform", - }, /* back ends */ { .name = "SSP2-Codec", - .id = 1, + .id = 0, .cpu_dai_name = "ssp2-port", /* overwritten for ssp0 routing */ .platform_name = "sst-mfld-platform", .no_pcm = 1, @@ -758,12 +736,12 @@ struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) { - int ret_val = 0; - struct sst_acpi_mach *mach; + struct byt_rt5640_private *priv; + struct snd_soc_acpi_mach *mach; const char *i2c_name = NULL; + int ret_val = 0; + int dai_index = 0; int i; - int dai_index; - struct byt_rt5640_private *priv; is_bytcr = false; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC); @@ -776,7 +754,6 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(&byt_rt5640_card, priv); /* fix index of codec dai */ - dai_index = MERR_DPCM_COMPR + 1; for (i = 0; i < ARRAY_SIZE(byt_rt5640_dais); i++) { if (!strcmp(byt_rt5640_dais[i].codec_name, "i2c-10EC5640:00")) { dai_index = i; @@ -785,8 +762,8 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) } /* fixup codec name based on HID */ - i2c_name = sst_acpi_find_name_from_hid(mach->id); - if (i2c_name != NULL) { + i2c_name = snd_soc_acpi_find_name_from_hid(mach->id); + if (i2c_name) { snprintf(byt_rt5640_codec_name, sizeof(byt_rt5640_codec_name), "%s%s", "i2c-", i2c_name); @@ -819,7 +796,7 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) /* format specified: 2 64-bit integers */ struct acpi_buffer format = {sizeof("NN"), "NN"}; struct acpi_buffer state = {0, NULL}; - struct sst_acpi_package_context pkg_ctx; + struct snd_soc_acpi_package_context pkg_ctx; bool pkg_found = false; state.length = sizeof(chan_package); @@ -831,7 +808,8 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) pkg_ctx.state = &state; pkg_ctx.data_valid = false; - pkg_found = sst_acpi_find_package_from_hid(mach->id, &pkg_ctx); + pkg_found = snd_soc_acpi_find_package_from_hid(mach->id, + &pkg_ctx); if (pkg_found) { if (chan_package.aif_value == 1) { dev_info(&pdev->dev, "BIOS Routing: AIF1 connected\n"); @@ -891,7 +869,7 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) byt_rt5640_cpu_dai_name; } - if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && (is_valleyview())) { + if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN) { priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); if (IS_ERR(priv->mclk)) { ret_val = PTR_ERR(priv->mclk); diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index 4a3516b38c2c..d955836c6870 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -21,24 +21,124 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/acpi.h> +#include <linux/clk.h> #include <linux/device.h> #include <linux/dmi.h> #include <linux/slab.h> +#include <asm/platform_sst_audio.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/jack.h> +#include <sound/soc-acpi.h> #include "../../codecs/rt5651.h" #include "../atom/sst-atom-controls.h" +enum { + BYT_RT5651_DMIC_MAP, + BYT_RT5651_IN1_MAP, + BYT_RT5651_IN2_MAP, +}; + +#define BYT_RT5651_MAP(quirk) ((quirk) & GENMASK(7, 0)) +#define BYT_RT5651_DMIC_EN BIT(16) +#define BYT_RT5651_MCLK_EN BIT(17) +#define BYT_RT5651_MCLK_25MHZ BIT(18) + +struct byt_rt5651_private { + struct clk *mclk; + struct snd_soc_jack jack; +}; + +static unsigned long byt_rt5651_quirk = BYT_RT5651_DMIC_MAP | + BYT_RT5651_DMIC_EN | + BYT_RT5651_MCLK_EN; + +static void log_quirks(struct device *dev) +{ + if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_DMIC_MAP) + dev_info(dev, "quirk DMIC_MAP enabled"); + if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_IN1_MAP) + dev_info(dev, "quirk IN1_MAP enabled"); + if (BYT_RT5651_MAP(byt_rt5651_quirk) == BYT_RT5651_IN2_MAP) + dev_info(dev, "quirk IN2_MAP enabled"); + if (byt_rt5651_quirk & BYT_RT5651_DMIC_EN) + dev_info(dev, "quirk DMIC enabled"); + if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) + dev_info(dev, "quirk MCLK_EN enabled"); + if (byt_rt5651_quirk & BYT_RT5651_MCLK_25MHZ) + dev_info(dev, "quirk MCLK_25MHZ enabled"); +} + +#define BYT_CODEC_DAI1 "rt5651-aif1" + +static int platform_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct snd_soc_dai *codec_dai; + struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card); + int ret; + + codec_dai = snd_soc_card_get_codec_dai(card, BYT_CODEC_DAI1); + if (!codec_dai) { + dev_err(card->dev, + "Codec dai not found; Unable to set platform clock\n"); + return -EIO; + } + + if (SND_SOC_DAPM_EVENT_ON(event)) { + if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) { + ret = clk_prepare_enable(priv->mclk); + if (ret < 0) { + dev_err(card->dev, + "could not configure MCLK state"); + return ret; + } + } + ret = snd_soc_dai_set_sysclk(codec_dai, RT5651_SCLK_S_PLL1, + 48000 * 512, + SND_SOC_CLOCK_IN); + } else { + /* + * Set codec clock source to internal clock before + * turning off the platform clock. Codec needs clock + * for Jack detection and button press + */ + ret = snd_soc_dai_set_sysclk(codec_dai, RT5651_SCLK_S_RCCLK, + 48000 * 512, + SND_SOC_CLOCK_IN); + if (!ret) + if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) + clk_disable_unprepare(priv->mclk); + } + + if (ret < 0) { + dev_err(card->dev, "can't set codec sysclk: %d\n", ret); + return ret; + } + + return 0; +} + static const struct snd_soc_dapm_widget byt_rt5651_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_MIC("Internal Mic", NULL), SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + platform_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + }; static const struct snd_soc_dapm_route byt_rt5651_audio_map[] = { + {"Headphone", NULL, "Platform Clock"}, + {"Headset Mic", NULL, "Platform Clock"}, + {"Internal Mic", NULL, "Platform Clock"}, + {"Speaker", NULL, "Platform Clock"}, + {"AIF1 Playback", NULL, "ssp2 Tx"}, {"ssp2 Tx", NULL, "codec_out0"}, {"ssp2 Tx", NULL, "codec_out1"}, @@ -47,38 +147,30 @@ static const struct snd_soc_dapm_route byt_rt5651_audio_map[] = { {"ssp2 Rx", NULL, "AIF1 Capture"}, {"Headset Mic", NULL, "micbias1"}, /* lowercase for rt5651 */ - {"IN2P", NULL, "Headset Mic"}, {"Headphone", NULL, "HPOL"}, {"Headphone", NULL, "HPOR"}, {"Speaker", NULL, "LOUTL"}, {"Speaker", NULL, "LOUTR"}, }; -static const struct snd_soc_dapm_route byt_rt5651_intmic_dmic1_map[] = { - {"DMIC1", NULL, "Internal Mic"}, -}; - -static const struct snd_soc_dapm_route byt_rt5651_intmic_dmic2_map[] = { - {"DMIC2", NULL, "Internal Mic"}, +static const struct snd_soc_dapm_route byt_rt5651_intmic_dmic_map[] = { + {"IN2P", NULL, "Headset Mic"}, + {"DMIC L1", NULL, "Internal Mic"}, + {"DMIC R1", NULL, "Internal Mic"}, }; static const struct snd_soc_dapm_route byt_rt5651_intmic_in1_map[] = { {"Internal Mic", NULL, "micbias1"}, + {"IN2P", NULL, "Headset Mic"}, {"IN1P", NULL, "Internal Mic"}, }; -enum { - BYT_RT5651_DMIC1_MAP, - BYT_RT5651_DMIC2_MAP, - BYT_RT5651_IN1_MAP, +static const struct snd_soc_dapm_route byt_rt5651_intmic_in2_map[] = { + {"Internal Mic", NULL, "micbias1"}, + {"IN1P", NULL, "Headset Mic"}, + {"IN2P", NULL, "Internal Mic"}, }; -#define BYT_RT5651_MAP(quirk) ((quirk) & 0xff) -#define BYT_RT5651_DMIC_EN BIT(16) - -static unsigned long byt_rt5651_quirk = BYT_RT5651_DMIC1_MAP | - BYT_RT5651_DMIC_EN; - static const struct snd_kcontrol_new byt_rt5651_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone"), SOC_DAPM_PIN_SWITCH("Headset Mic"), @@ -86,6 +178,17 @@ static const struct snd_kcontrol_new byt_rt5651_controls[] = { SOC_DAPM_PIN_SWITCH("Speaker"), }; +static struct snd_soc_jack_pin bytcr_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + static int byt_rt5651_aif1_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -103,9 +206,26 @@ static int byt_rt5651_aif1_hw_params(struct snd_pcm_substream *substream, return ret; } - ret = snd_soc_dai_set_pll(codec_dai, 0, RT5651_PLL1_S_BCLK1, - params_rate(params) * 50, - params_rate(params) * 512); + if (!(byt_rt5651_quirk & BYT_RT5651_MCLK_EN)) { + /* 2x25 bit slots on SSP2 */ + ret = snd_soc_dai_set_pll(codec_dai, 0, + RT5651_PLL1_S_BCLK1, + params_rate(params) * 50, + params_rate(params) * 512); + } else { + if (byt_rt5651_quirk & BYT_RT5651_MCLK_25MHZ) { + ret = snd_soc_dai_set_pll(codec_dai, 0, + RT5651_PLL1_S_MCLK, + 25000000, + params_rate(params) * 512); + } else { + ret = snd_soc_dai_set_pll(codec_dai, 0, + RT5651_PLL1_S_MCLK, + 19200000, + params_rate(params) * 512); + } + } + if (ret < 0) { dev_err(rtd->dev, "can't set codec pll: %d\n", ret); return ret; @@ -114,33 +234,60 @@ static int byt_rt5651_aif1_hw_params(struct snd_pcm_substream *substream, return 0; } +static int byt_rt5651_quirk_cb(const struct dmi_system_id *id) +{ + byt_rt5651_quirk = (unsigned long)id->driver_data; + return 1; +} + static const struct dmi_system_id byt_rt5651_quirk_table[] = { + { + .callback = byt_rt5651_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"), + DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max B3 PLATFORM"), + }, + .driver_data = (void *)(BYT_RT5651_DMIC_MAP | + BYT_RT5651_DMIC_EN), + }, + { + .callback = byt_rt5651_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "KIANO"), + DMI_MATCH(DMI_PRODUCT_NAME, "KIANO SlimNote 14.2"), + }, + .driver_data = (void *)(BYT_RT5651_IN2_MAP), + }, {} }; static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime) { - int ret; struct snd_soc_card *card = runtime->card; + struct snd_soc_codec *codec = runtime->codec; + struct byt_rt5651_private *priv = snd_soc_card_get_drvdata(card); const struct snd_soc_dapm_route *custom_map; int num_routes; + int ret; card->dapm.idle_bias_off = true; - dmi_check_system(byt_rt5651_quirk_table); switch (BYT_RT5651_MAP(byt_rt5651_quirk)) { case BYT_RT5651_IN1_MAP: custom_map = byt_rt5651_intmic_in1_map; num_routes = ARRAY_SIZE(byt_rt5651_intmic_in1_map); break; - case BYT_RT5651_DMIC2_MAP: - custom_map = byt_rt5651_intmic_dmic2_map; - num_routes = ARRAY_SIZE(byt_rt5651_intmic_dmic2_map); + case BYT_RT5651_IN2_MAP: + custom_map = byt_rt5651_intmic_in2_map; + num_routes = ARRAY_SIZE(byt_rt5651_intmic_in2_map); break; default: - custom_map = byt_rt5651_intmic_dmic1_map; - num_routes = ARRAY_SIZE(byt_rt5651_intmic_dmic1_map); + custom_map = byt_rt5651_intmic_dmic_map; + num_routes = ARRAY_SIZE(byt_rt5651_intmic_dmic_map); } + ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes); + if (ret) + return ret; ret = snd_soc_add_card_controls(card, byt_rt5651_controls, ARRAY_SIZE(byt_rt5651_controls)); @@ -151,6 +298,40 @@ static int byt_rt5651_init(struct snd_soc_pcm_runtime *runtime) snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone"); snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker"); + if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) { + /* + * The firmware might enable the clock at + * boot (this information may or may not + * be reflected in the enable clock register). + * To change the rate we must disable the clock + * first to cover these cases. Due to common + * clock framework restrictions that do not allow + * to disable a clock that has not been enabled, + * we need to enable the clock first. + */ + ret = clk_prepare_enable(priv->mclk); + if (!ret) + clk_disable_unprepare(priv->mclk); + + if (byt_rt5651_quirk & BYT_RT5651_MCLK_25MHZ) + ret = clk_set_rate(priv->mclk, 25000000); + else + ret = clk_set_rate(priv->mclk, 19200000); + + if (ret) + dev_err(card->dev, "unable to set MCLK rate\n"); + } + + ret = snd_soc_card_jack_new(runtime->card, "Headset", + SND_JACK_HEADSET, &priv->jack, + bytcr_jack_pins, ARRAY_SIZE(bytcr_jack_pins)); + if (ret) { + dev_err(runtime->dev, "Headset jack creation failed %d\n", ret); + return ret; + } + + rt5651_set_jack_detect(codec, &priv->jack); + return ret; } @@ -253,19 +434,11 @@ static struct snd_soc_dai_link byt_rt5651_dais[] = { .dpcm_playback = 1, .ops = &byt_rt5651_aif1_ops, }, - [MERR_DPCM_COMPR] = { - .name = "Compressed Port", - .stream_name = "Compress", - .cpu_dai_name = "compress-cpu-dai", - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .platform_name = "sst-mfld-platform", - }, /* CODEC<->CODEC link */ /* back ends */ { .name = "SSP2-Codec", - .id = 1, + .id = 0, .cpu_dai_name = "ssp2-port", .platform_name = "sst-mfld-platform", .no_pcm = 1, @@ -296,13 +469,65 @@ static struct snd_soc_card byt_rt5651_card = { .fully_routed = true, }; +static char byt_rt5651_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */ + static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) { + struct byt_rt5651_private *priv; + struct snd_soc_acpi_mach *mach; + const char *i2c_name = NULL; int ret_val = 0; + int dai_index = 0; + int i; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC); + if (!priv) + return -ENOMEM; /* register the soc card */ byt_rt5651_card.dev = &pdev->dev; + mach = byt_rt5651_card.dev->platform_data; + snd_soc_card_set_drvdata(&byt_rt5651_card, priv); + + /* fix index of codec dai */ + for (i = 0; i < ARRAY_SIZE(byt_rt5651_dais); i++) { + if (!strcmp(byt_rt5651_dais[i].codec_name, "i2c-10EC5651:00")) { + dai_index = i; + break; + } + } + + /* fixup codec name based on HID */ + i2c_name = snd_soc_acpi_find_name_from_hid(mach->id); + if (i2c_name) { + snprintf(byt_rt5651_codec_name, sizeof(byt_rt5651_codec_name), + "%s%s", "i2c-", i2c_name); + + byt_rt5651_dais[dai_index].codec_name = byt_rt5651_codec_name; + } + + /* check quirks before creating card */ + dmi_check_system(byt_rt5651_quirk_table); + log_quirks(&pdev->dev); + + if (byt_rt5651_quirk & BYT_RT5651_MCLK_EN) { + priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); + if (IS_ERR(priv->mclk)) { + dev_err(&pdev->dev, + "Failed to get MCLK from pmc_plt_clk_3: %ld\n", + PTR_ERR(priv->mclk)); + /* + * Fall back to bit clock usage for -ENOENT (clock not + * available likely due to missing dependencies), bail + * for all other errors, including -EPROBE_DEFER + */ + if (ret_val != -ENOENT) + return ret_val; + byt_rt5651_quirk &= ~BYT_RT5651_MCLK_EN; + } + } + ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5651_card); if (ret_val) { diff --git a/sound/soc/intel/boards/cht_bsw_max98090_ti.c b/sound/soc/intel/boards/cht_bsw_max98090_ti.c index 20755ecc7f9e..d3e1c7e12004 100644 --- a/sound/soc/intel/boards/cht_bsw_max98090_ti.c +++ b/sound/soc/intel/boards/cht_bsw_max98090_ti.c @@ -23,6 +23,7 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/acpi.h> +#include <linux/clk.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -35,15 +36,48 @@ #define CHT_CODEC_DAI "HiFi" struct cht_mc_private { + struct clk *mclk; struct snd_soc_jack jack; bool ts3a227e_present; }; +static int platform_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct snd_soc_dai *codec_dai; + struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card); + int ret; + + codec_dai = snd_soc_card_get_codec_dai(card, CHT_CODEC_DAI); + if (!codec_dai) { + dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n"); + return -EIO; + } + + if (SND_SOC_DAPM_EVENT_ON(event)) { + ret = clk_prepare_enable(ctx->mclk); + if (ret < 0) { + dev_err(card->dev, + "could not configure MCLK state"); + return ret; + } + } else { + clk_disable_unprepare(ctx->mclk); + } + + return 0; +} + static const struct snd_soc_dapm_widget cht_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_MIC("Int Mic", NULL), SND_SOC_DAPM_SPK("Ext Spk", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + platform_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), }; static const struct snd_soc_dapm_route cht_audio_map[] = { @@ -60,6 +94,10 @@ static const struct snd_soc_dapm_route cht_audio_map[] = { {"codec_in0", NULL, "ssp2 Rx" }, {"codec_in1", NULL, "ssp2 Rx" }, {"ssp2 Rx", NULL, "HiFi Capture"}, + {"Headphone", NULL, "Platform Clock"}, + {"Headset Mic", NULL, "Platform Clock"}, + {"Int Mic", NULL, "Platform Clock"}, + {"Ext Spk", NULL, "Platform Clock"}, }; static const struct snd_kcontrol_new cht_mc_controls[] = { @@ -109,6 +147,40 @@ static struct notifier_block cht_jack_nb = { .notifier_call = cht_ti_jack_event, }; +static struct snd_soc_jack_pin hs_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static struct snd_soc_jack_gpio hs_jack_gpios[] = { + { + .name = "hp", + .report = SND_JACK_HEADPHONE | SND_JACK_LINEOUT, + .debounce_time = 200, + }, + { + .name = "mic", + .invert = 1, + .report = SND_JACK_MICROPHONE, + .debounce_time = 200, + }, +}; + +static const struct acpi_gpio_params hp_gpios = { 0, 0, false }; +static const struct acpi_gpio_params mic_gpios = { 1, 0, false }; + +static const struct acpi_gpio_mapping acpi_max98090_gpios[] = { + { "hp-gpios", &hp_gpios, 1 }, + { "mic-gpios", &mic_gpios, 1 }, + {}, +}; + static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) { int ret; @@ -116,30 +188,55 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card); struct snd_soc_jack *jack = &ctx->jack; - /** - * TI supports 4 butons headset detection - * KEY_MEDIA - * KEY_VOICECOMMAND - * KEY_VOLUMEUP - * KEY_VOLUMEDOWN - */ - if (ctx->ts3a227e_present) - jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3; - else - jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE; + if (ctx->ts3a227e_present) { + /* + * The jack has already been created in the + * cht_max98090_headset_init() function. + */ + snd_soc_jack_notifier_register(jack, &cht_jack_nb); + return 0; + } - ret = snd_soc_card_jack_new(runtime->card, "Headset Jack", - jack_type, jack, NULL, 0); + jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE; + ret = snd_soc_card_jack_new(runtime->card, "Headset Jack", + jack_type, jack, + hs_jack_pins, ARRAY_SIZE(hs_jack_pins)); if (ret) { dev_err(runtime->dev, "Headset Jack creation failed %d\n", ret); return ret; } - if (ctx->ts3a227e_present) - snd_soc_jack_notifier_register(jack, &cht_jack_nb); + ret = snd_soc_jack_add_gpiods(runtime->card->dev->parent, jack, + ARRAY_SIZE(hs_jack_gpios), + hs_jack_gpios); + if (ret) { + /* + * flag error but don't bail if jack detect is broken + * due to platform issues or bad BIOS/configuration + */ + dev_err(runtime->dev, + "jack detection gpios not added, error %d\n", ret); + } + + /* + * The firmware might enable the clock at + * boot (this information may or may not + * be reflected in the enable clock register). + * To change the rate we must disable the clock + * first to cover these cases. Due to common + * clock framework restrictions that do not allow + * to disable a clock that has not been enabled, + * we need to enable the clock first. + */ + ret = clk_prepare_enable(ctx->mclk); + if (!ret) + clk_disable_unprepare(ctx->mclk); + + ret = clk_set_rate(ctx->mclk, CHT_PLAT_CLK_3_HZ); + + if (ret) + dev_err(runtime->dev, "unable to set MCLK rate\n"); return ret; } @@ -160,7 +257,7 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, return ret; } - fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF + fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS; ret = snd_soc_dai_set_fmt(rtd->cpu_dai, fmt); @@ -173,8 +270,8 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, rate->min = rate->max = 48000; channels->min = channels->max = 2; - /* set SSP2 to 24-bit */ - params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); + /* set SSP2 to 16-bit */ + params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); return 0; } @@ -188,8 +285,29 @@ static int cht_max98090_headset_init(struct snd_soc_component *component) { struct snd_soc_card *card = component->card; struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_jack *jack = &ctx->jack; + int jack_type; + int ret; - return ts3a227e_enable_jack_detect(component, &ctx->jack); + /* + * TI supports 4 butons headset detection + * KEY_MEDIA + * KEY_VOICECOMMAND + * KEY_VOLUMEUP + * KEY_VOLUMEDOWN + */ + jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3; + + ret = snd_soc_card_jack_new(card, "Headset Jack", jack_type, + jack, NULL, 0); + if (ret) { + dev_err(card->dev, "Headset Jack creation failed %d\n", ret); + return ret; + } + + return ts3a227e_enable_jack_detect(component, jack); } static const struct snd_soc_ops cht_aif1_ops = { @@ -232,18 +350,10 @@ static struct snd_soc_dai_link cht_dailink[] = { .dpcm_playback = 1, .ops = &cht_aif1_ops, }, - [MERR_DPCM_COMPR] = { - .name = "Compressed Port", - .stream_name = "Compress", - .cpu_dai_name = "compress-cpu-dai", - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .platform_name = "sst-mfld-platform", - }, /* back ends */ { .name = "SSP2-Codec", - .id = 1, + .id = 0, .cpu_dai_name = "ssp2-port", .platform_name = "sst-mfld-platform", .no_pcm = 1, @@ -277,6 +387,7 @@ static struct snd_soc_card snd_soc_card_cht = { static int snd_cht_mc_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; int ret_val = 0; struct cht_mc_private *drv; @@ -289,11 +400,25 @@ static int snd_cht_mc_probe(struct platform_device *pdev) /* no need probe TI jack detection chip */ snd_soc_card_cht.aux_dev = NULL; snd_soc_card_cht.num_aux_devs = 0; + + ret_val = devm_acpi_dev_add_driver_gpios(dev->parent, + acpi_max98090_gpios); + if (ret_val) + dev_dbg(dev, "Unable to add GPIO mapping table\n"); } /* register the soc card */ snd_soc_card_cht.dev = &pdev->dev; snd_soc_card_set_drvdata(&snd_soc_card_cht, drv); + + drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); + if (IS_ERR(drv->mclk)) { + dev_err(&pdev->dev, + "Failed to get MCLK from pmc_plt_clk_3: %ld\n", + PTR_ERR(drv->mclk)); + return PTR_ERR(drv->mclk); + } + ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht); if (ret_val) { dev_err(&pdev->dev, diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index 5bcde01d15e6..18d129caa974 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -21,20 +21,20 @@ */ #include <linux/module.h> -#include <linux/acpi.h> #include <linux/platform_device.h> +#include <linux/acpi.h> +#include <linux/clk.h> #include <linux/dmi.h> #include <linux/slab.h> #include <asm/cpu_device_id.h> #include <asm/platform_sst_audio.h> -#include <linux/clk.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/jack.h> +#include <sound/soc-acpi.h> #include "../../codecs/rt5645.h" #include "../atom/sst-atom-controls.h" -#include "../common/sst-acpi.h" #define CHT_PLAT_CLK_3_HZ 19200000 #define CHT_CODEC_DAI1 "rt5645-aif1" @@ -53,7 +53,7 @@ struct cht_mc_private { struct clk *mclk; }; -#define CHT_RT5645_MAP(quirk) ((quirk) & 0xff) +#define CHT_RT5645_MAP(quirk) ((quirk) & GENMASK(7, 0)) #define CHT_RT5645_SSP2_AIF2 BIT(16) /* default is using AIF1 */ #define CHT_RT5645_SSP0_AIF1 BIT(17) #define CHT_RT5645_SSP0_AIF2 BIT(18) @@ -70,21 +70,6 @@ static void log_quirks(struct device *dev) dev_info(dev, "quirk SSP0_AIF2 enabled"); } -static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card) -{ - struct snd_soc_pcm_runtime *rtd; - - list_for_each_entry(rtd, &card->rtd_list, list) { - if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI1, - strlen(CHT_CODEC_DAI1))) - return rtd->codec_dai; - if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI2, - strlen(CHT_CODEC_DAI2))) - return rtd->codec_dai; - } - return NULL; -} - static int platform_clock_control(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { @@ -94,20 +79,21 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card); int ret; - codec_dai = cht_get_codec_dai(card); + codec_dai = snd_soc_card_get_codec_dai(card, CHT_CODEC_DAI1); + if (!codec_dai) + codec_dai = snd_soc_card_get_codec_dai(card, CHT_CODEC_DAI2); + if (!codec_dai) { dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n"); return -EIO; } if (SND_SOC_DAPM_EVENT_ON(event)) { - if (ctx->mclk) { - ret = clk_prepare_enable(ctx->mclk); - if (ret < 0) { - dev_err(card->dev, - "could not configure MCLK state"); - return ret; - } + ret = clk_prepare_enable(ctx->mclk); + if (ret < 0) { + dev_err(card->dev, + "could not configure MCLK state"); + return ret; } } else { /* Set codec sysclk source to its internal clock because codec PLL will @@ -122,8 +108,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, return ret; } - if (ctx->mclk) - clk_disable_unprepare(ctx->mclk); + clk_disable_unprepare(ctx->mclk); } return 0; @@ -258,11 +243,11 @@ static const struct dmi_system_id cht_rt5645_quirk_table[] = { static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) { - int ret; - int jack_type; - struct snd_soc_codec *codec = runtime->codec; struct snd_soc_card *card = runtime->card; struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card); + struct snd_soc_codec *codec = runtime->codec; + int jack_type; + int ret; if ((cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2) || (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) { @@ -320,26 +305,26 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) rt5645_set_jack_detect(codec, &ctx->jack, &ctx->jack, &ctx->jack); - if (ctx->mclk) { - /* - * The firmware might enable the clock at - * boot (this information may or may not - * be reflected in the enable clock register). - * To change the rate we must disable the clock - * first to cover these cases. Due to common - * clock framework restrictions that do not allow - * to disable a clock that has not been enabled, - * we need to enable the clock first. - */ - ret = clk_prepare_enable(ctx->mclk); - if (!ret) - clk_disable_unprepare(ctx->mclk); - ret = clk_set_rate(ctx->mclk, CHT_PLAT_CLK_3_HZ); + /* + * The firmware might enable the clock at + * boot (this information may or may not + * be reflected in the enable clock register). + * To change the rate we must disable the clock + * first to cover these cases. Due to common + * clock framework restrictions that do not allow + * to disable a clock that has not been enabled, + * we need to enable the clock first. + */ + ret = clk_prepare_enable(ctx->mclk); + if (!ret) + clk_disable_unprepare(ctx->mclk); + + ret = clk_set_rate(ctx->mclk, CHT_PLAT_CLK_3_HZ); + + if (ret) + dev_err(runtime->dev, "unable to set MCLK rate\n"); - if (ret) - dev_err(runtime->dev, "unable to set MCLK rate\n"); - } return ret; } @@ -460,19 +445,11 @@ static struct snd_soc_dai_link cht_dailink[] = { .dpcm_playback = 1, .ops = &cht_aif1_ops, }, - [MERR_DPCM_COMPR] = { - .name = "Compressed Port", - .stream_name = "Compress", - .cpu_dai_name = "compress-cpu-dai", - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .platform_name = "sst-mfld-platform", - }, /* CODEC<->CODEC link */ /* back ends */ { .name = "SSP2-Codec", - .id = 1, + .id = 0, .cpu_dai_name = "ssp2-port", .platform_name = "sst-mfld-platform", .no_pcm = 1, @@ -545,15 +522,15 @@ struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */ static int snd_cht_mc_probe(struct platform_device *pdev) { - int ret_val = 0; - int i; - struct cht_mc_private *drv; struct snd_soc_card *card = snd_soc_cards[0].soc_card; - struct sst_acpi_mach *mach; + struct snd_soc_acpi_mach *mach; + struct cht_mc_private *drv; const char *i2c_name = NULL; - int dai_index = 0; bool found = false; bool is_bytcr = false; + int dai_index = 0; + int ret_val = 0; + int i; drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC); if (!drv) @@ -589,8 +566,8 @@ static int snd_cht_mc_probe(struct platform_device *pdev) } /* fixup codec name based on HID */ - i2c_name = sst_acpi_find_name_from_hid(mach->id); - if (i2c_name != NULL) { + i2c_name = snd_soc_acpi_find_name_from_hid(mach->id); + if (i2c_name) { snprintf(cht_rt5645_codec_name, sizeof(cht_rt5645_codec_name), "%s%s", "i2c-", i2c_name); cht_dailink[dai_index].codec_name = cht_rt5645_codec_name; @@ -622,7 +599,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) /* format specified: 2 64-bit integers */ struct acpi_buffer format = {sizeof("NN"), "NN"}; struct acpi_buffer state = {0, NULL}; - struct sst_acpi_package_context pkg_ctx; + struct snd_soc_acpi_package_context pkg_ctx; bool pkg_found = false; state.length = sizeof(chan_package); @@ -634,7 +611,8 @@ static int snd_cht_mc_probe(struct platform_device *pdev) pkg_ctx.state = &state; pkg_ctx.data_valid = false; - pkg_found = sst_acpi_find_package_from_hid(mach->id, &pkg_ctx); + pkg_found = snd_soc_acpi_find_package_from_hid(mach->id, + &pkg_ctx); if (pkg_found) { if (chan_package.aif_value == 1) { dev_info(&pdev->dev, "BIOS Routing: AIF1 connected\n"); @@ -682,14 +660,12 @@ static int snd_cht_mc_probe(struct platform_device *pdev) cht_rt5645_cpu_dai_name; } - if (is_valleyview()) { - drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); - if (IS_ERR(drv->mclk)) { - dev_err(&pdev->dev, - "Failed to get MCLK from pmc_plt_clk_3: %ld\n", - PTR_ERR(drv->mclk)); - return PTR_ERR(drv->mclk); - } + drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); + if (IS_ERR(drv->mclk)) { + dev_err(&pdev->dev, + "Failed to get MCLK from pmc_plt_clk_3: %ld\n", + PTR_ERR(drv->mclk)); + return PTR_ERR(drv->mclk); } snd_soc_card_set_drvdata(card, drv); diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index f597d5582223..f8f21eee9b2d 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -20,14 +20,14 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/clk.h> -#include <asm/cpu_device_id.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/jack.h> +#include <sound/soc-acpi.h> #include "../../codecs/rt5670.h" #include "../atom/sst-atom-controls.h" -#include "../common/sst-acpi.h" + /* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */ #define CHT_PLAT_CLK_3_HZ 19200000 @@ -51,18 +51,6 @@ static struct snd_soc_jack_pin cht_bsw_headset_pins[] = { }, }; -static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card) -{ - struct snd_soc_pcm_runtime *rtd; - - list_for_each_entry(rtd, &card->rtd_list, list) { - if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI, - strlen(CHT_CODEC_DAI))) - return rtd->codec_dai; - } - return NULL; -} - static int platform_clock_control(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { @@ -72,7 +60,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card); int ret; - codec_dai = cht_get_codec_dai(card); + codec_dai = snd_soc_card_get_codec_dai(card, CHT_CODEC_DAI); if (!codec_dai) { dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n"); return -EIO; @@ -315,20 +303,12 @@ static struct snd_soc_dai_link cht_dailink[] = { .dpcm_playback = 1, .ops = &cht_aif1_ops, }, - [MERR_DPCM_COMPR] = { - .name = "Compressed Port", - .stream_name = "Compress", - .cpu_dai_name = "compress-cpu-dai", - .codec_dai_name = "snd-soc-dummy-dai", - .codec_name = "snd-soc-dummy", - .platform_name = "sst-mfld-platform", - }, /* Back End DAI links */ { /* SSP2 - Codec */ .name = "SSP2-Codec", - .id = 1, + .id = 0, .cpu_dai_name = "ssp2-port", .platform_name = "sst-mfld-platform", .no_pcm = 1, @@ -348,9 +328,11 @@ static struct snd_soc_dai_link cht_dailink[] = { static int cht_suspend_pre(struct snd_soc_card *card) { struct snd_soc_component *component; + struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card); list_for_each_entry(component, &card->component_dev_list, card_list) { - if (!strcmp(component->name, "i2c-10EC5670:00")) { + if (!strncmp(component->name, + ctx->codec_name, sizeof(ctx->codec_name))) { struct snd_soc_codec *codec = snd_soc_component_to_codec(component); dev_dbg(codec->dev, "disabling jack detect before going to suspend.\n"); @@ -364,9 +346,11 @@ static int cht_suspend_pre(struct snd_soc_card *card) static int cht_resume_post(struct snd_soc_card *card) { struct snd_soc_component *component; + struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card); list_for_each_entry(component, &card->component_dev_list, card_list) { - if (!strcmp(component->name, "i2c-10EC5670:00")) { + if (!strncmp(component->name, + ctx->codec_name, sizeof(ctx->codec_name))) { struct snd_soc_codec *codec = snd_soc_component_to_codec(component); dev_dbg(codec->dev, "enabling jack detect for resume.\n"); @@ -380,7 +364,7 @@ static int cht_resume_post(struct snd_soc_card *card) /* SoC card */ static struct snd_soc_card snd_soc_card_cht = { - .name = "cherrytrailcraudio", + .name = "cht-bsw-rt5672", .owner = THIS_MODULE, .dai_link = cht_dailink, .num_links = ARRAY_SIZE(cht_dailink), @@ -394,25 +378,13 @@ static struct snd_soc_card snd_soc_card_cht = { .resume_post = cht_resume_post, }; -static bool is_valleyview(void) -{ - static const struct x86_cpu_id cpu_ids[] = { - { X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */ - {} - }; - - if (!x86_match_cpu(cpu_ids)) - return false; - return true; -} - #define RT5672_I2C_DEFAULT "i2c-10EC5670:00" static int snd_cht_mc_probe(struct platform_device *pdev) { int ret_val = 0; struct cht_mc_private *drv; - struct sst_acpi_mach *mach = pdev->dev.platform_data; + struct snd_soc_acpi_mach *mach = pdev->dev.platform_data; const char *i2c_name; int i; @@ -424,7 +396,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) /* fixup codec name based on HID */ if (mach) { - i2c_name = sst_acpi_find_name_from_hid(mach->id); + i2c_name = snd_soc_acpi_find_name_from_hid(mach->id); if (i2c_name) { snprintf(drv->codec_name, sizeof(drv->codec_name), "i2c-%s", i2c_name); @@ -439,14 +411,12 @@ static int snd_cht_mc_probe(struct platform_device *pdev) } } - if (is_valleyview()) { - drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); - if (IS_ERR(drv->mclk)) { - dev_err(&pdev->dev, - "Failed to get MCLK from pmc_plt_clk_3: %ld\n", - PTR_ERR(drv->mclk)); - return PTR_ERR(drv->mclk); - } + drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3"); + if (IS_ERR(drv->mclk)) { + dev_err(&pdev->dev, + "Failed to get MCLK from pmc_plt_clk_3: %ld\n", + PTR_ERR(drv->mclk)); + return PTR_ERR(drv->mclk); } snd_soc_card_set_drvdata(&snd_soc_card_cht, drv); diff --git a/sound/soc/intel/boards/kbl_rt5663_max98927.c b/sound/soc/intel/boards/kbl_rt5663_max98927.c index 7f7607420706..6f9a8bcf20f3 100644 --- a/sound/soc/intel/boards/kbl_rt5663_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_max98927.c @@ -17,6 +17,7 @@ * GNU General Public License for more details. */ +#include <linux/input.h> #include <linux/module.h> #include <linux/platform_device.h> #include <sound/core.h> @@ -208,6 +209,7 @@ static int kabylake_rt5663_codec_init(struct snd_soc_pcm_runtime *rtd) int ret; struct kbl_rt5663_private *ctx = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_jack *jack; /* * Headset buttons map to the google Reference headset. @@ -221,6 +223,13 @@ static int kabylake_rt5663_codec_init(struct snd_soc_pcm_runtime *rtd) dev_err(rtd->dev, "Headset Jack creation failed %d\n", ret); return ret; } + + jack = &ctx->kabylake_headset; + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_MEDIA); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + rt5663_set_jack_detect(codec, &ctx->kabylake_headset); return ret; } @@ -341,13 +350,28 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + struct snd_soc_dpcm *dpcm = container_of( + params, struct snd_soc_dpcm, hw_params); + struct snd_soc_dai_link *fe_dai_link = dpcm->fe->dai_link; + struct snd_soc_dai_link *be_dai_link = dpcm->be->dai_link; - /* The ADSP will convert the FE rate to 48k, stereo */ - rate->min = rate->max = 48000; - channels->min = channels->max = 2; - /* set SSP1 to 24 bit */ - snd_mask_none(fmt); - snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + /* + * The ADSP will convert the FE rate to 48k, stereo, 24 bit + */ + if (!strcmp(fe_dai_link->name, "Kbl Audio Port") || + !strcmp(fe_dai_link->name, "Kbl Audio Headset Playback") || + !strcmp(fe_dai_link->name, "Kbl Audio Capture Port")) { + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + } + /* + * The speaker on the SSP0 supports S16_LE and not S24_LE. + * thus changing the mask here + */ + if (!strcmp(be_dai_link->name, "SSP0-Codec")) + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S16_LE); return 0; } @@ -390,6 +414,43 @@ static int kabylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } +static int kabylake_ssp0_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int ret = 0, j; + + for (j = 0; j < rtd->num_codecs; j++) { + struct snd_soc_dai *codec_dai = rtd->codec_dais[j]; + + if (!strcmp(codec_dai->component->name, MAXIM_DEV0_NAME)) { + /* + * Use channel 4 and 5 for the first amp + */ + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x30, 3, 8, 16); + if (ret < 0) { + dev_err(rtd->dev, "set TDM slot err:%d\n", ret); + return ret; + } + } + if (!strcmp(codec_dai->component->name, MAXIM_DEV1_NAME)) { + /* + * Use channel 6 and 7 for the second amp + */ + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xC0, 3, 8, 16); + if (ret < 0) { + dev_err(rtd->dev, "set TDM slot err:%d\n", ret); + return ret; + } + } + } + return ret; +} + +static struct snd_soc_ops kabylake_ssp0_ops = { + .hw_params = kabylake_ssp0_hw_params, +}; + static unsigned int channels_dmic[] = { 2, 4, }; @@ -593,12 +654,13 @@ static struct snd_soc_dai_link kabylake_dais[] = { .no_pcm = 1, .codecs = max98927_codec_components, .num_codecs = ARRAY_SIZE(max98927_codec_components), - .dai_fmt = SND_SOC_DAIFMT_I2S | + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, .ignore_pmdown_time = 1, .be_hw_params_fixup = kabylake_ssp_fixup, .dpcm_playback = 1, + .ops = &kabylake_ssp0_ops, }, { /* SSP1 - Codec */ diff --git a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c index 88ff54220007..6072164f2d43 100644 --- a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c @@ -302,6 +302,7 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, * The ADSP will convert the FE rate to 48k, stereo, 24 bit */ if (!strcmp(fe_dai_link->name, "Kbl Audio Port") || + !strcmp(fe_dai_link->name, "Kbl Audio Headset Playback") || !strcmp(fe_dai_link->name, "Kbl Audio Capture Port")) { rate->min = rate->max = 48000; channels->min = channels->max = 2; @@ -604,6 +605,8 @@ static int kabylake_card_late_probe(struct snd_soc_card *card) list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { codec = pcm->codec_dai->codec; + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP,pcm=%d Jack", pcm->device); err = snd_soc_card_jack_new(card, jack_name, SND_JACK_AVOUT, &ctx->kabylake_hdmi[i], NULL, 0); diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c index 5ed0aa27b467..1b5a689dc99b 100644 --- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c +++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c @@ -54,20 +54,6 @@ enum { SKL_DPCM_AUDIO_HDMI3_PB, }; -static inline struct snd_soc_dai *skl_get_codec_dai(struct snd_soc_card *card) -{ - struct snd_soc_pcm_runtime *rtd; - - list_for_each_entry(rtd, &card->rtd_list, list) { - - if (!strncmp(rtd->codec_dai->name, SKL_NUVOTON_CODEC_DAI, - strlen(SKL_NUVOTON_CODEC_DAI))) - return rtd->codec_dai; - } - - return NULL; -} - static int platform_clock_control(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { @@ -76,7 +62,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, struct snd_soc_dai *codec_dai; int ret; - codec_dai = skl_get_codec_dai(card); + codec_dai = snd_soc_card_get_codec_dai(card, SKL_NUVOTON_CODEC_DAI); if (!codec_dai) { dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n"); return -EIO; diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c index 01b8b140bb08..7bea4bc77481 100644 --- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -57,20 +57,6 @@ enum { SKL_DPCM_AUDIO_HDMI3_PB, }; -static inline struct snd_soc_dai *skl_get_codec_dai(struct snd_soc_card *card) -{ - struct snd_soc_pcm_runtime *rtd; - - list_for_each_entry(rtd, &card->rtd_list, list) { - - if (!strncmp(rtd->codec_dai->name, SKL_NUVOTON_CODEC_DAI, - strlen(SKL_NUVOTON_CODEC_DAI))) - return rtd->codec_dai; - } - - return NULL; -} - static const struct snd_kcontrol_new skylake_controls[] = { SOC_DAPM_PIN_SWITCH("Headphone Jack"), SOC_DAPM_PIN_SWITCH("Headset Mic"), @@ -86,7 +72,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, struct snd_soc_dai *codec_dai; int ret; - codec_dai = skl_get_codec_dai(card); + codec_dai = snd_soc_card_get_codec_dai(card, SKL_NUVOTON_CODEC_DAI); if (!codec_dai) { dev_err(card->dev, "Codec dai not found\n"); return -EIO; diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index 0e029f354f6b..7379d8830c39 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -1,11 +1,11 @@ # SPDX-License-Identifier: GPL-2.0 snd-soc-sst-dsp-objs := sst-dsp.o snd-soc-sst-acpi-objs := sst-acpi.o -snd-soc-sst-match-objs := sst-match-acpi.o snd-soc-sst-ipc-objs := sst-ipc.o snd-soc-sst-firmware-objs := sst-firmware.o +snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-match.o soc-acpi-intel-hsw-bdw-match.o obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o -obj-$(CONFIG_SND_SOC_INTEL_SST_MATCH) += snd-soc-sst-match.o obj-$(CONFIG_SND_SOC_INTEL_SST_FIRMWARE) += snd-soc-sst-firmware.o +obj-$(CONFIG_SND_SOC_ACPI_INTEL_MATCH) += snd-soc-acpi-intel-match.o diff --git a/sound/soc/intel/common/soc-acpi-intel-byt-match.c b/sound/soc/intel/common/soc-acpi-intel-byt-match.c new file mode 100644 index 000000000000..bfe1ca68a542 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-byt-match.c @@ -0,0 +1,196 @@ +/* + * soc-apci-intel-byt-match.c - tables and support for BYT ACPI enumeration. + * + * Copyright (c) 2017, Intel Corporation. + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#include <linux/dmi.h> +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> + +static unsigned long byt_machine_id; + +#define BYT_THINKPAD_10 1 + +static int byt_thinkpad10_quirk_cb(const struct dmi_system_id *id) +{ + byt_machine_id = BYT_THINKPAD_10; + return 1; +} + + +static const struct dmi_system_id byt_table[] = { + { + .callback = byt_thinkpad10_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 10"), + }, + }, + { + .callback = byt_thinkpad10_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Tablet B"), + }, + }, + { + .callback = byt_thinkpad10_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Miix 2 10"), + }, + }, + { } +}; + +static struct snd_soc_acpi_mach byt_thinkpad_10 = { + .id = "10EC5640", + .drv_name = "cht-bsw-rt5672", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "cht-bsw", + .sof_fw_filename = "intel/reef-byt.ri", + .sof_tplg_filename = "intel/reef-byt-rt5670.tplg", + .asoc_plat_name = "sst-mfld-platform", +}; + +static struct snd_soc_acpi_mach *byt_quirk(void *arg) +{ + struct snd_soc_acpi_mach *mach = arg; + + dmi_check_system(byt_table); + + if (byt_machine_id == BYT_THINKPAD_10) + return &byt_thinkpad_10; + else + return mach; +} + +struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_legacy_machines[] = { + { + .id = "10EC5640", + .drv_name = "byt-rt5640", + .fw_filename = "intel/fw_sst_0f28.bin-48kHz_i2s_master", + }, + { + .id = "193C9890", + .drv_name = "byt-max98090", + .fw_filename = "intel/fw_sst_0f28.bin-48kHz_i2s_master", + }, + {} +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_baytrail_legacy_machines); + +struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { + { + .id = "10EC5640", + .drv_name = "bytcr_rt5640", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcr_rt5640", + .machine_quirk = byt_quirk, + .sof_fw_filename = "intel/reef-byt.ri", + .sof_tplg_filename = "intel/reef-byt-rt5640.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "10EC5642", + .drv_name = "bytcr_rt5640", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcr_rt5640", + .sof_fw_filename = "intel/reef-byt.ri", + .sof_tplg_filename = "intel/reef-byt-rt5640.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "INTCCFFD", + .drv_name = "bytcr_rt5640", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcr_rt5640", + .sof_fw_filename = "intel/reef-byt.ri", + .sof_tplg_filename = "intel/reef-byt-rt5640.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "10EC5651", + .drv_name = "bytcr_rt5651", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcr_rt5651", + .sof_fw_filename = "intel/reef-byt.ri", + .sof_tplg_filename = "intel/reef-byt-rt5651.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "DLGS7212", + .drv_name = "bytcht_da7213", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcht_da7213", + .sof_fw_filename = "intel/reef-byt.ri", + .sof_tplg_filename = "intel/reef-byt-da7213.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "DLGS7213", + .drv_name = "bytcht_da7213", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcht_da7213", + .sof_fw_filename = "intel/reef-byt.ri", + .sof_tplg_filename = "intel/reef-byt-da7213.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + /* some Baytrail platforms rely on RT5645, use CHT machine driver */ + { + .id = "10EC5645", + .drv_name = "cht-bsw-rt5645", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "cht-bsw", + .sof_fw_filename = "intel/reef-byt.ri", + .sof_tplg_filename = "intel/reef-byt-rt5645.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "10EC5648", + .drv_name = "cht-bsw-rt5645", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "cht-bsw", + .sof_fw_filename = "intel/reef-byt.ri", + .sof_tplg_filename = "intel/reef-byt-rt5645.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + /* use CHT driver to Baytrail Chromebooks */ + { + .id = "193C9890", + .drv_name = "cht-bsw-max98090", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "cht-bsw", + .sof_fw_filename = "intel/reef-byt.ri", + .sof_tplg_filename = "intel/reef-byt-max98090.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) + /* + * This is always last in the table so that it is selected only when + * enabled explicitly and there is no codec-related information in SSDT + */ + { + .id = "80860F28", + .drv_name = "bytcht_nocodec", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcht_nocodec", + }, +#endif + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_baytrail_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c new file mode 100644 index 000000000000..b50a0d53846b --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c @@ -0,0 +1,194 @@ +/* + * soc-apci-intel-cht-match.c - tables and support for CHT ACPI enumeration. + * + * Copyright (c) 2017, Intel Corporation. + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#include <linux/dmi.h> +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> + +static unsigned long cht_machine_id; + +#define CHT_SURFACE_MACH 1 + +static int cht_surface_quirk_cb(const struct dmi_system_id *id) +{ + cht_machine_id = CHT_SURFACE_MACH; + return 1; +} + +static const struct dmi_system_id cht_table[] = { + { + .callback = cht_surface_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"), + }, + }, + { } +}; + +static struct snd_soc_acpi_mach cht_surface_mach = { + .id = "10EC5640", + .drv_name = "cht-bsw-rt5645", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + .sof_fw_filename = "intel/reef-cht.ri", + .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .asoc_plat_name = "sst-mfld-platform", +}; + +static struct snd_soc_acpi_mach *cht_quirk(void *arg) +{ + struct snd_soc_acpi_mach *mach = arg; + + dmi_check_system(cht_table); + + if (cht_machine_id == CHT_SURFACE_MACH) + return &cht_surface_mach; + else + return mach; +} + +/* Cherryview-based platforms: CherryTrail and Braswell */ +struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { + { + .id = "10EC5670", + .drv_name = "cht-bsw-rt5672", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + .sof_fw_filename = "intel/reef-cht.ri", + .sof_tplg_filename = "intel/reef-cht-rt5670.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "10EC5672", + .drv_name = "cht-bsw-rt5672", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + .sof_fw_filename = "intel/reef-cht.ri", + .sof_tplg_filename = "intel/reef-cht-rt5670.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "10EC5645", + .drv_name = "cht-bsw-rt5645", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + .sof_fw_filename = "intel/reef-cht.ri", + .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "10EC5650", + .drv_name = "cht-bsw-rt5645", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + .sof_fw_filename = "intel/reef-cht.ri", + .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "10EC3270", + .drv_name = "cht-bsw-rt5645", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + .sof_fw_filename = "intel/reef-cht.ri", + .sof_tplg_filename = "intel/reef-cht-rt5645.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "193C9890", + .drv_name = "cht-bsw-max98090", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + .sof_fw_filename = "intel/reef-cht.ri", + .sof_tplg_filename = "intel/reef-cht-max98090.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "DLGS7212", + .drv_name = "bytcht_da7213", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcht_da7213", + .sof_fw_filename = "intel/reef-cht.ri", + .sof_tplg_filename = "intel/reef-cht-da7213.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "DLGS7213", + .drv_name = "bytcht_da7213", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcht_da7213", + .sof_fw_filename = "intel/reef-cht.ri", + .sof_tplg_filename = "intel/reef-cht-da7213.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "ESSX8316", + .drv_name = "bytcht_es8316", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcht_es8316", + .sof_fw_filename = "intel/reef-cht.ri", + .sof_tplg_filename = "intel/reef-cht-es8316.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + /* some CHT-T platforms rely on RT5640, use Baytrail machine driver */ + { + .id = "10EC5640", + .drv_name = "bytcr_rt5640", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcr_rt5640", + .machine_quirk = cht_quirk, + .sof_fw_filename = "intel/reef-cht.ri", + .sof_tplg_filename = "intel/reef-cht-rt5640.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + { + .id = "10EC3276", + .drv_name = "bytcr_rt5640", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcr_rt5640", + .sof_fw_filename = "intel/reef-cht.ri", + .sof_tplg_filename = "intel/reef-cht-rt5640.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, + /* some CHT-T platforms rely on RT5651, use Baytrail machine driver */ + { + .id = "10EC5651", + .drv_name = "bytcr_rt5651", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcr_rt5651", + .sof_fw_filename = "intel/reef-cht.ri", + .sof_tplg_filename = "intel/reef-cht-rt5651.tplg", + .asoc_plat_name = "sst-mfld-platform", + }, +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) + /* + * This is always last in the table so that it is selected only when + * enabled explicitly and there is no codec-related information in SSDT + */ + { + .id = "808622A8", + .drv_name = "bytcht_nocodec", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcht_nocodec", + }, +#endif + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cherrytrail_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c new file mode 100644 index 000000000000..e0e8c8c27528 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c @@ -0,0 +1,64 @@ +/* + * soc-apci-intel-hsw-bdw-match.c - tables and support for ACPI enumeration. + * + * Copyright (c) 2017, Intel Corporation. + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#include <linux/dmi.h> +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> + +struct snd_soc_acpi_mach snd_soc_acpi_intel_haswell_machines[] = { + { + .id = "INT33CA", + .drv_name = "haswell-audio", + .fw_filename = "intel/IntcSST1.bin", + .sof_fw_filename = "intel/reef-hsw.ri", + .sof_tplg_filename = "intel/reef-hsw.tplg", + .asoc_plat_name = "haswell-pcm-audio", + }, + {} +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_haswell_machines); + +struct snd_soc_acpi_mach snd_soc_acpi_intel_broadwell_machines[] = { + { + .id = "INT343A", + .drv_name = "broadwell-audio", + .fw_filename = "intel/IntcSST2.bin", + .sof_fw_filename = "intel/reef-bdw.ri", + .sof_tplg_filename = "intel/reef-bdw-rt286.tplg", + .asoc_plat_name = "haswell-pcm-audio", + }, + { + .id = "RT5677CE", + .drv_name = "bdw-rt5677", + .fw_filename = "intel/IntcSST2.bin", + .sof_fw_filename = "intel/reef-bdw.ri", + .sof_tplg_filename = "intel/reef-bdw-rt286.tplg", + .asoc_plat_name = "haswell-pcm-audio", + }, + { + .id = "INT33CA", + .drv_name = "haswell-audio", + .fw_filename = "intel/IntcSST2.bin", + .sof_fw_filename = "intel/reef-bdw.ri", + .sof_tplg_filename = "intel/reef-bdw-rt5640.tplg", + .asoc_plat_name = "haswell-pcm-audio", + }, + {} +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_broadwell_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/sst-acpi.c b/sound/soc/intel/common/sst-acpi.c index 1285cc597b6b..cf6fbbd4e378 100644 --- a/sound/soc/intel/common/sst-acpi.c +++ b/sound/soc/intel/common/sst-acpi.c @@ -21,7 +21,8 @@ #include <linux/platform_device.h> #include "sst-dsp.h" -#include "sst-acpi.h" +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> #define SST_LPT_DSP_DMA_ADDR_OFFSET 0x0F0000 #define SST_WPT_DSP_DMA_ADDR_OFFSET 0x0FE000 @@ -30,7 +31,7 @@ /* Descriptor for setting up SST platform data */ struct sst_acpi_desc { const char *drv_name; - struct sst_acpi_mach *machines; + struct snd_soc_acpi_mach *machines; /* Platform resource indexes. Must set to -1 if not used */ int resindex_lpe_base; int resindex_pcicfg_base; @@ -49,7 +50,7 @@ struct sst_acpi_priv { struct platform_device *pdev_pcm; struct sst_pdata sst_pdata; struct sst_acpi_desc *desc; - struct sst_acpi_mach *mach; + struct snd_soc_acpi_mach *mach; }; static void sst_acpi_fw_cb(const struct firmware *fw, void *context) @@ -59,7 +60,7 @@ static void sst_acpi_fw_cb(const struct firmware *fw, void *context) struct sst_acpi_priv *sst_acpi = platform_get_drvdata(pdev); struct sst_pdata *sst_pdata = &sst_acpi->sst_pdata; struct sst_acpi_desc *desc = sst_acpi->desc; - struct sst_acpi_mach *mach = sst_acpi->mach; + struct snd_soc_acpi_mach *mach = sst_acpi->mach; sst_pdata->fw = fw; if (!fw) { @@ -85,7 +86,7 @@ static int sst_acpi_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct sst_acpi_priv *sst_acpi; struct sst_pdata *sst_pdata; - struct sst_acpi_mach *mach; + struct snd_soc_acpi_mach *mach; struct sst_acpi_desc *desc; struct resource *mmio; int ret = 0; @@ -99,7 +100,7 @@ static int sst_acpi_probe(struct platform_device *pdev) return -ENODEV; desc = (struct sst_acpi_desc *)id->driver_data; - mach = sst_acpi_find_machine(desc->machines); + mach = snd_soc_acpi_find_machine(desc->machines); if (mach == NULL) { dev_err(dev, "No matching ASoC machine driver found\n"); return -ENODEV; @@ -179,14 +180,9 @@ static int sst_acpi_remove(struct platform_device *pdev) return 0; } -static struct sst_acpi_mach haswell_machines[] = { - { "INT33CA", "haswell-audio", "intel/IntcSST1.bin", NULL, NULL, NULL }, - {} -}; - static struct sst_acpi_desc sst_acpi_haswell_desc = { .drv_name = "haswell-pcm-audio", - .machines = haswell_machines, + .machines = snd_soc_acpi_intel_haswell_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = 1, .resindex_fw_base = -1, @@ -197,15 +193,9 @@ static struct sst_acpi_desc sst_acpi_haswell_desc = { .dma_size = SST_LPT_DSP_DMA_SIZE, }; -static struct sst_acpi_mach broadwell_machines[] = { - { "INT343A", "broadwell-audio", "intel/IntcSST2.bin", NULL, NULL, NULL }, - { "RT5677CE", "bdw-rt5677", "intel/IntcSST2.bin", NULL, NULL, NULL }, - {} -}; - static struct sst_acpi_desc sst_acpi_broadwell_desc = { .drv_name = "haswell-pcm-audio", - .machines = broadwell_machines, + .machines = snd_soc_acpi_intel_broadwell_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = 1, .resindex_fw_base = -1, @@ -217,15 +207,9 @@ static struct sst_acpi_desc sst_acpi_broadwell_desc = { }; #if !IS_ENABLED(CONFIG_SND_SST_IPC_ACPI) -static struct sst_acpi_mach baytrail_machines[] = { - { "10EC5640", "byt-rt5640", "intel/fw_sst_0f28.bin-48kHz_i2s_master", NULL, NULL, NULL }, - { "193C9890", "byt-max98090", "intel/fw_sst_0f28.bin-48kHz_i2s_master", NULL, NULL, NULL }, - {} -}; - static struct sst_acpi_desc sst_acpi_baytrail_desc = { .drv_name = "baytrail-pcm-audio", - .machines = baytrail_machines, + .machines = snd_soc_acpi_intel_baytrail_legacy_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = 1, .resindex_fw_base = 2, diff --git a/sound/soc/intel/common/sst-acpi.h b/sound/soc/intel/common/sst-acpi.h deleted file mode 100644 index afe9b87b8bd5..000000000000 --- a/sound/soc/intel/common/sst-acpi.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2013-15, Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include <linux/stddef.h> -#include <linux/acpi.h> - -struct sst_acpi_package_context { - char *name; /* package name */ - int length; /* number of elements */ - struct acpi_buffer *format; - struct acpi_buffer *state; - bool data_valid; -}; - -#if IS_ENABLED(CONFIG_ACPI) -/* translation fron HID to I2C name, needed for DAI codec_name */ -const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]); -bool sst_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN], - struct sst_acpi_package_context *ctx); -#else -static inline const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]) -{ - return NULL; -} -static inline bool sst_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN], - struct sst_acpi_package_context *ctx) -{ - return false; -} -#endif - -/* acpi match */ -struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines); - -/* acpi check hid */ -bool sst_acpi_check_hid(const u8 hid[ACPI_ID_LEN]); - -/* Descriptor for SST ASoC machine driver */ -struct sst_acpi_mach { - /* ACPI ID for the matching machine driver. Audio codec for instance */ - const u8 id[ACPI_ID_LEN]; - /* machine driver name */ - const char *drv_name; - /* firmware file name */ - const char *fw_filename; - - /* board name */ - const char *board; - struct sst_acpi_mach * (*machine_quirk)(void *arg); - const void *quirk_data; - void *pdata; -}; - -#define SST_ACPI_MAX_CODECS 3 - -/** - * struct sst_codecs: Structure to hold secondary codec information apart from - * the matched one, this data will be passed to the quirk function to match - * with the ACPI detected devices - * - * @num_codecs: number of secondary codecs used in the platform - * @codecs: holds the codec IDs - * - */ -struct sst_codecs { - int num_codecs; - u8 codecs[SST_ACPI_MAX_CODECS][ACPI_ID_LEN]; -}; - -/* check all codecs */ -struct sst_acpi_mach *sst_acpi_codec_list(void *arg); diff --git a/sound/soc/intel/common/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c index a086c35f91bb..657afc02f1c4 100644 --- a/sound/soc/intel/common/sst-firmware.c +++ b/sound/soc/intel/common/sst-firmware.c @@ -19,6 +19,7 @@ #include <linux/sched.h> #include <linux/firmware.h> #include <linux/export.h> +#include <linux/module.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> #include <linux/dmaengine.h> @@ -274,7 +275,6 @@ int sst_dma_new(struct sst_dsp *sst) struct sst_pdata *sst_pdata = sst->pdata; struct sst_dma *dma; struct resource mem; - const char *dma_dev_name; int ret = 0; if (sst->pdata->resindex_dma_base == -1) @@ -285,7 +285,6 @@ int sst_dma_new(struct sst_dsp *sst) * is attached to the ADSP IP. */ switch (sst->pdata->dma_engine) { case SST_DMA_TYPE_DW: - dma_dev_name = "dw_dmac"; break; default: dev_err(sst->dev, "error: invalid DMA engine %d\n", diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 89f70133c8e4..61b5bfa79d13 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -613,8 +613,10 @@ skip_buf_size_calc: } #define DMA_CONTROL_ID 5 +#define DMA_I2S_BLOB_SIZE 21 -int skl_dsp_set_dma_control(struct skl_sst *ctx, struct skl_module_cfg *mconfig) +int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps, + u32 caps_size, u32 node_id) { struct skl_dma_control *dma_ctrl; struct skl_ipc_large_config_msg msg = {0}; @@ -624,24 +626,27 @@ int skl_dsp_set_dma_control(struct skl_sst *ctx, struct skl_module_cfg *mconfig) /* * if blob size zero, then return */ - if (mconfig->formats_config.caps_size == 0) + if (caps_size == 0) return 0; msg.large_param_id = DMA_CONTROL_ID; - msg.param_data_size = sizeof(struct skl_dma_control) + - mconfig->formats_config.caps_size; + msg.param_data_size = sizeof(struct skl_dma_control) + caps_size; dma_ctrl = kzalloc(msg.param_data_size, GFP_KERNEL); if (dma_ctrl == NULL) return -ENOMEM; - dma_ctrl->node_id = skl_get_node_id(ctx, mconfig); + dma_ctrl->node_id = node_id; - /* size in dwords */ - dma_ctrl->config_length = mconfig->formats_config.caps_size / 4; + /* + * NHLT blob may contain additional configs along with i2s blob. + * firmware expects only the i2s blob size as the config_length. + * So fix to i2s blob size. + * size in dwords. + */ + dma_ctrl->config_length = DMA_I2S_BLOB_SIZE; - memcpy(dma_ctrl->config_data, mconfig->formats_config.caps, - mconfig->formats_config.caps_size); + memcpy(dma_ctrl->config_data, caps, caps_size); err = skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)dma_ctrl); @@ -702,18 +707,11 @@ static void skl_set_updown_mixer_format(struct skl_sst *ctx, struct skl_module *module = mconfig->module; struct skl_module_iface *iface = &module->formats[mconfig->fmt_idx]; struct skl_module_fmt *fmt = &iface->outputs[0].fmt; - int i = 0; skl_set_base_module_format(ctx, mconfig, (struct skl_base_cfg *)mixer_mconfig); mixer_mconfig->out_ch_cfg = fmt->ch_cfg; - - /* Select F/W default coefficient */ - mixer_mconfig->coeff_sel = 0x0; - - /* User coeff, don't care since we are selecting F/W defaults */ - for (i = 0; i < UP_DOWN_MIXER_MAX_COEFF; i++) - mixer_mconfig->coeff[i] = 0xDEADBEEF; + mixer_mconfig->ch_map = fmt->ch_map; } /* diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index e7d766d56c8e..d14c50a60289 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -20,6 +20,8 @@ #include <linux/pci.h> #include "skl.h" +#define NHLT_ACPI_HEADER_SIG "NHLT" + /* Unique identification for getting NHLT blobs */ static guid_t osc_guid = GUID_INIT(0xA69F886E, 0x6CEB, 0x4594, @@ -45,6 +47,13 @@ struct nhlt_acpi_table *skl_nhlt_init(struct device *dev) memremap(nhlt_ptr->min_addr, nhlt_ptr->length, MEMREMAP_WB); ACPI_FREE(obj); + if (nhlt_table && (strncmp(nhlt_table->header.signature, + NHLT_ACPI_HEADER_SIG, + strlen(NHLT_ACPI_HEADER_SIG)) != 0)) { + memunmap(nhlt_table); + dev_err(dev, "NHLT ACPI header signature incorrect\n"); + return NULL; + } return nhlt_table; } diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index 2b1e513b1680..1dd97479e0c0 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -355,7 +355,8 @@ static void skl_pcm_close(struct snd_pcm_substream *substream, } mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream); - skl_tplg_d0i3_put(skl, mconfig->d0i3_caps); + if (mconfig) + skl_tplg_d0i3_put(skl, mconfig->d0i3_caps); kfree(dma_params); } @@ -652,7 +653,7 @@ static const struct snd_soc_dai_ops skl_link_dai_ops = { .trigger = skl_link_pcm_trigger, }; -static struct snd_soc_dai_driver skl_platform_dai[] = { +static struct snd_soc_dai_driver skl_fe_dai[] = { { .name = "System Pin", .ops = &skl_pcm_dai_ops, @@ -796,8 +797,10 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { .sig_bits = 32, }, }, +}; /* BE CPU Dais */ +static struct snd_soc_dai_driver skl_platform_dai[] = { { .name = "SSP0 Pin", .ops = &skl_be_ssp_dai_ops, @@ -975,6 +978,14 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { }, }; +int skl_dai_load(struct snd_soc_component *cmp, + struct snd_soc_dai_driver *pcm_dai) +{ + pcm_dai->ops = &skl_pcm_dai_ops; + + return 0; +} + static int skl_platform_open(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -1362,6 +1373,8 @@ int skl_platform_register(struct device *dev) int ret; struct hdac_ext_bus *ebus = dev_get_drvdata(dev); struct skl *skl = ebus_to_skl(ebus); + struct snd_soc_dai_driver *dais; + int num_dais = ARRAY_SIZE(skl_platform_dai); INIT_LIST_HEAD(&skl->ppl_list); INIT_LIST_HEAD(&skl->bind_list); @@ -1371,14 +1384,38 @@ int skl_platform_register(struct device *dev) dev_err(dev, "soc platform registration failed %d\n", ret); return ret; } + + skl->dais = kmemdup(skl_platform_dai, sizeof(skl_platform_dai), + GFP_KERNEL); + if (!skl->dais) { + ret = -ENOMEM; + goto err; + } + + if (!skl->use_tplg_pcm) { + dais = krealloc(skl->dais, sizeof(skl_fe_dai) + + sizeof(skl_platform_dai), GFP_KERNEL); + if (!dais) { + ret = -ENOMEM; + goto err; + } + + skl->dais = dais; + memcpy(&skl->dais[ARRAY_SIZE(skl_platform_dai)], skl_fe_dai, + sizeof(skl_fe_dai)); + num_dais += ARRAY_SIZE(skl_fe_dai); + } + ret = snd_soc_register_component(dev, &skl_component, - skl_platform_dai, - ARRAY_SIZE(skl_platform_dai)); + skl->dais, num_dais); if (ret) { dev_err(dev, "soc component registration failed %d\n", ret); - snd_soc_unregister_platform(dev); + goto err; } + return 0; +err: + snd_soc_unregister_platform(dev); return ret; } @@ -1398,5 +1435,7 @@ int skl_platform_unregister(struct device *dev) snd_soc_unregister_component(dev); snd_soc_unregister_platform(dev); + kfree(skl->dais); + return 0; } diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index 369ef7ce981c..8ff89280d9fd 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -251,6 +251,7 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw, struct uuid_module *module; struct firmware stripped_fw; unsigned int safe_file; + int ret = 0; /* Get the FW pointer to derive ADSP header */ stripped_fw.data = fw->data; @@ -299,8 +300,10 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw, for (i = 0; i < num_entry; i++, mod_entry++) { module = kzalloc(sizeof(*module), GFP_KERNEL); - if (!module) - return -ENOMEM; + if (!module) { + ret = -ENOMEM; + goto free_uuid_list; + } uuid_bin = (uuid_le *)mod_entry->uuid.id; memcpy(&module->uuid, uuid_bin, sizeof(module->uuid)); @@ -311,8 +314,8 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw, size = sizeof(int) * mod_entry->instance_max_count; module->instance_id = devm_kzalloc(ctx->dev, size, GFP_KERNEL); if (!module->instance_id) { - kfree(module); - return -ENOMEM; + ret = -ENOMEM; + goto free_uuid_list; } list_add_tail(&module->list, &skl->uuid_list); @@ -323,6 +326,10 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw, } return 0; + +free_uuid_list: + skl_freeup_uuid_list(skl); + return ret; } void skl_freeup_uuid_list(struct skl_sst *ctx) diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 22f768ca3c73..a072bcf209d2 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -2036,21 +2036,45 @@ static int skl_tplg_add_pipe(struct device *dev, return 0; } -static int skl_tplg_fill_pin(struct device *dev, u32 tkn, +static int skl_tplg_get_uuid(struct device *dev, u8 *guid, + struct snd_soc_tplg_vendor_uuid_elem *uuid_tkn) +{ + if (uuid_tkn->token == SKL_TKN_UUID) { + memcpy(guid, &uuid_tkn->uuid, 16); + return 0; + } + + dev_err(dev, "Not an UUID token %d\n", uuid_tkn->token); + + return -EINVAL; +} + +static int skl_tplg_fill_pin(struct device *dev, + struct snd_soc_tplg_vendor_value_elem *tkn_elem, struct skl_module_pin *m_pin, - int pin_index, u32 value) + int pin_index) { - switch (tkn) { + int ret; + + switch (tkn_elem->token) { case SKL_TKN_U32_PIN_MOD_ID: - m_pin[pin_index].id.module_id = value; + m_pin[pin_index].id.module_id = tkn_elem->value; break; case SKL_TKN_U32_PIN_INST_ID: - m_pin[pin_index].id.instance_id = value; + m_pin[pin_index].id.instance_id = tkn_elem->value; + break; + + case SKL_TKN_UUID: + ret = skl_tplg_get_uuid(dev, m_pin[pin_index].id.mod_uuid.b, + (struct snd_soc_tplg_vendor_uuid_elem *)tkn_elem); + if (ret < 0) + return ret; + break; default: - dev_err(dev, "%d Not a pin token\n", value); + dev_err(dev, "%d Not a pin token\n", tkn_elem->token); return -EINVAL; } @@ -2083,9 +2107,7 @@ static int skl_tplg_fill_pins_info(struct device *dev, return -EINVAL; } - ret = skl_tplg_fill_pin(dev, tkn_elem->token, - m_pin, pin_count, tkn_elem->value); - + ret = skl_tplg_fill_pin(dev, tkn_elem, m_pin, pin_count); if (ret < 0) return ret; @@ -2170,19 +2192,6 @@ static int skl_tplg_widget_fill_fmt(struct device *dev, return skl_tplg_fill_fmt(dev, dst_fmt, tkn, val); } -static int skl_tplg_get_uuid(struct device *dev, struct skl_module_cfg *mconfig, - struct snd_soc_tplg_vendor_uuid_elem *uuid_tkn) -{ - if (uuid_tkn->token == SKL_TKN_UUID) - memcpy(&mconfig->guid, &uuid_tkn->uuid, 16); - else { - dev_err(dev, "Not an UUID token tkn %d\n", uuid_tkn->token); - return -EINVAL; - } - - return 0; -} - static void skl_tplg_fill_pin_dynamic_val( struct skl_module_pin *mpin, u32 pin_count, u32 value) { @@ -2382,7 +2391,7 @@ static int skl_tplg_get_token(struct device *dev, case SKL_TKN_U32_MAX_MCPS: case SKL_TKN_U32_OBS: case SKL_TKN_U32_IBS: - ret = skl_tplg_fill_res_tkn(dev, tkn_elem, res, dir, pin_index); + ret = skl_tplg_fill_res_tkn(dev, tkn_elem, res, pin_index, dir); if (ret < 0) return ret; @@ -2488,6 +2497,7 @@ static int skl_tplg_get_token(struct device *dev, case SKL_TKN_U32_PIN_MOD_ID: case SKL_TKN_U32_PIN_INST_ID: + case SKL_TKN_UUID: ret = skl_tplg_fill_pins_info(dev, mconfig, tkn_elem, dir, pin_index); @@ -2550,6 +2560,7 @@ static int skl_tplg_get_tokens(struct device *dev, struct snd_soc_tplg_vendor_value_elem *tkn_elem; int tkn_count = 0, ret; int off = 0, tuple_size = 0; + bool is_module_guid = true; if (block_size <= 0) return -EINVAL; @@ -2565,7 +2576,15 @@ static int skl_tplg_get_tokens(struct device *dev, continue; case SND_SOC_TPLG_TUPLE_TYPE_UUID: - ret = skl_tplg_get_uuid(dev, mconfig, array->uuid); + if (is_module_guid) { + ret = skl_tplg_get_uuid(dev, mconfig->guid, + array->uuid); + is_module_guid = false; + } else { + ret = skl_tplg_get_token(dev, array->value, skl, + mconfig); + } + if (ret < 0) return ret; @@ -3331,6 +3350,7 @@ static struct snd_soc_tplg_ops skl_tplg_ops = { .io_ops = skl_tplg_kcontrol_ops, .io_ops_count = ARRAY_SIZE(skl_tplg_kcontrol_ops), .manifest = skl_manifest_load, + .dai_load = skl_dai_load, }; /* @@ -3404,7 +3424,7 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus) ret = request_firmware(&fw, skl->tplg_name, bus->dev); if (ret < 0) { - dev_err(bus->dev, "tplg fw %s load failed with %d\n", + dev_info(bus->dev, "tplg fw %s load failed with %d, falling back to dfw_sst.bin", skl->tplg_name, ret); ret = request_firmware(&fw, "dfw_sst.bin", bus->dev); if (ret < 0) { diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 2717db92036b..b6496513fe55 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -34,7 +34,7 @@ #define MAX_FIXED_DMIC_PARAMS_SIZE 727 /* Maximum number of coefficients up down mixer module */ -#define UP_DOWN_MIXER_MAX_COEFF 6 +#define UP_DOWN_MIXER_MAX_COEFF 8 #define MODULE_MAX_IN_PINS 8 #define MODULE_MAX_OUT_PINS 8 @@ -161,6 +161,7 @@ struct skl_up_down_mixer_cfg { u32 coeff_sel; /* Pass the user coeff in this array */ s32 coeff[UP_DOWN_MIXER_MAX_COEFF]; + u32 ch_map; } __packed; struct skl_algo_cfg { @@ -455,8 +456,8 @@ static inline struct skl *get_skl_ctx(struct device *dev) int skl_tplg_be_update_params(struct snd_soc_dai *dai, struct skl_pipe_params *params); -int skl_dsp_set_dma_control(struct skl_sst *ctx, - struct skl_module_cfg *mconfig); +int skl_dsp_set_dma_control(struct skl_sst *ctx, u32 *caps, + u32 caps_size, u32 node_id); void skl_tplg_set_be_dmic_config(struct snd_soc_dai *dai, struct skl_pipe_params *params, int stream); int skl_tplg_init(struct snd_soc_platform *platform, @@ -501,4 +502,7 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params); int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params); + +int skl_dai_load(struct snd_soc_component *cmp, + struct snd_soc_dai_driver *pcm_dai); #endif diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index f94b484abb99..31d8634e8aa1 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -28,7 +28,7 @@ #include <linux/firmware.h> #include <linux/delay.h> #include <sound/pcm.h> -#include "../common/sst-acpi.h" +#include <sound/soc-acpi.h> #include <sound/hda_register.h> #include <sound/hdaudio.h> #include <sound/hda_i915.h> @@ -439,10 +439,10 @@ static int skl_machine_device_register(struct skl *skl, void *driver_data) { struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); struct platform_device *pdev; - struct sst_acpi_mach *mach = driver_data; + struct snd_soc_acpi_mach *mach = driver_data; int ret; - mach = sst_acpi_find_machine(mach); + mach = snd_soc_acpi_find_machine(mach); if (mach == NULL) { dev_err(bus->dev, "No matching machine driver found\n"); return -ENODEV; @@ -462,8 +462,11 @@ static int skl_machine_device_register(struct skl *skl, void *driver_data) return -EIO; } - if (mach->pdata) + if (mach->pdata) { + skl->use_tplg_pcm = + ((struct skl_machine_pdata *)mach->pdata)->use_tplg_pcm; dev_set_drvdata(&pdev->dev, mach->pdata); + } skl->i2s_dev = pdev; @@ -875,33 +878,36 @@ static void skl_remove(struct pci_dev *pci) dev_set_drvdata(&pci->dev, NULL); } -static struct sst_codecs skl_codecs = { +static struct snd_soc_acpi_codecs skl_codecs = { .num_codecs = 1, .codecs = {"10508825"} }; -static struct sst_codecs kbl_codecs = { +static struct snd_soc_acpi_codecs kbl_codecs = { .num_codecs = 1, .codecs = {"10508825"} }; -static struct sst_codecs bxt_codecs = { +static struct snd_soc_acpi_codecs bxt_codecs = { .num_codecs = 1, .codecs = {"MX98357A"} }; -static struct sst_codecs kbl_poppy_codecs = { +static struct snd_soc_acpi_codecs kbl_poppy_codecs = { .num_codecs = 1, .codecs = {"10EC5663"} }; -static struct sst_codecs kbl_5663_5514_codecs = { +static struct snd_soc_acpi_codecs kbl_5663_5514_codecs = { .num_codecs = 2, .codecs = {"10EC5663", "10EC5514"} }; +static struct skl_machine_pdata cnl_pdata = { + .use_tplg_pcm = true, +}; -static struct sst_acpi_mach sst_skl_devdata[] = { +static struct snd_soc_acpi_mach sst_skl_devdata[] = { { .id = "INT343A", .drv_name = "skl_alc286s_i2s", @@ -911,7 +917,7 @@ static struct sst_acpi_mach sst_skl_devdata[] = { .id = "INT343B", .drv_name = "skl_n88l25_s4567", .fw_filename = "intel/dsp_fw_release.bin", - .machine_quirk = sst_acpi_codec_list, + .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &skl_codecs, .pdata = &skl_dmic_data }, @@ -919,14 +925,14 @@ static struct sst_acpi_mach sst_skl_devdata[] = { .id = "MX98357A", .drv_name = "skl_n88l25_m98357a", .fw_filename = "intel/dsp_fw_release.bin", - .machine_quirk = sst_acpi_codec_list, + .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &skl_codecs, .pdata = &skl_dmic_data }, {} }; -static struct sst_acpi_mach sst_bxtp_devdata[] = { +static struct snd_soc_acpi_mach sst_bxtp_devdata[] = { { .id = "INT343A", .drv_name = "bxt_alc298s_i2s", @@ -936,13 +942,13 @@ static struct sst_acpi_mach sst_bxtp_devdata[] = { .id = "DLGS7219", .drv_name = "bxt_da7219_max98357a_i2s", .fw_filename = "intel/dsp_fw_bxtn.bin", - .machine_quirk = sst_acpi_codec_list, + .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &bxt_codecs, }, {} }; -static struct sst_acpi_mach sst_kbl_devdata[] = { +static struct snd_soc_acpi_mach sst_kbl_devdata[] = { { .id = "INT343A", .drv_name = "kbl_alc286s_i2s", @@ -952,7 +958,7 @@ static struct sst_acpi_mach sst_kbl_devdata[] = { .id = "INT343B", .drv_name = "kbl_n88l25_s4567", .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = sst_acpi_codec_list, + .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &kbl_codecs, .pdata = &skl_dmic_data }, @@ -960,7 +966,7 @@ static struct sst_acpi_mach sst_kbl_devdata[] = { .id = "MX98357A", .drv_name = "kbl_n88l25_m98357a", .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = sst_acpi_codec_list, + .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &kbl_codecs, .pdata = &skl_dmic_data }, @@ -968,7 +974,7 @@ static struct sst_acpi_mach sst_kbl_devdata[] = { .id = "MX98927", .drv_name = "kbl_r5514_5663_max", .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = sst_acpi_codec_list, + .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &kbl_5663_5514_codecs, .pdata = &skl_dmic_data }, @@ -976,7 +982,7 @@ static struct sst_acpi_mach sst_kbl_devdata[] = { .id = "MX98927", .drv_name = "kbl_rt5663_m98927", .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = sst_acpi_codec_list, + .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &kbl_poppy_codecs, .pdata = &skl_dmic_data }, @@ -989,7 +995,7 @@ static struct sst_acpi_mach sst_kbl_devdata[] = { {} }; -static struct sst_acpi_mach sst_glk_devdata[] = { +static struct snd_soc_acpi_mach sst_glk_devdata[] = { { .id = "INT343A", .drv_name = "glk_alc298s_i2s", @@ -998,12 +1004,14 @@ static struct sst_acpi_mach sst_glk_devdata[] = { {} }; -static const struct sst_acpi_mach sst_cnl_devdata[] = { +static const struct snd_soc_acpi_mach sst_cnl_devdata[] = { { .id = "INT34C2", .drv_name = "cnl_rt274", .fw_filename = "intel/dsp_fw_cnl.bin", + .pdata = &cnl_pdata, }, + {} }; /* PCI IDs */ diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 8d9d6899f761..e00cde8200dd 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -53,6 +53,7 @@ struct skl { struct platform_device *dmic_dev; struct platform_device *i2s_dev; struct snd_soc_platform *platform; + struct snd_soc_dai_driver *dais; struct nhlt_acpi_table *nhlt; /* nhlt ptr */ struct skl_sst *skl_sst; /* sst skl ctx */ @@ -73,6 +74,7 @@ struct skl { struct skl_debug *debugfs; u8 nr_modules; struct skl_module **modules; + bool use_tplg_pcm; }; #define skl_to_ebus(s) (&(s)->ebus) @@ -85,9 +87,9 @@ struct skl_dma_params { u8 stream_tag; }; -/* to pass dmic data */ struct skl_machine_pdata { u32 dmic_num; + bool use_tplg_pcm; /* use dais and dai links from topology */ }; struct skl_dsp_ops { diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c index cf23af159acf..505b0ff03c3b 100644 --- a/sound/soc/kirkwood/kirkwood-dma.c +++ b/sound/soc/kirkwood/kirkwood-dma.c @@ -318,7 +318,7 @@ static void kirkwood_dma_free_dma_buffers(struct snd_pcm *pcm) } } -struct snd_soc_platform_driver kirkwood_soc_platform = { +const struct snd_soc_platform_driver kirkwood_soc_platform = { .ops = &kirkwood_dma_ops, .pcm_new = kirkwood_dma_new, .pcm_free = kirkwood_dma_free_dma_buffers, diff --git a/sound/soc/kirkwood/kirkwood.h b/sound/soc/kirkwood/kirkwood.h index 90e32a781424..783cb1a4f30e 100644 --- a/sound/soc/kirkwood/kirkwood.h +++ b/sound/soc/kirkwood/kirkwood.h @@ -143,6 +143,6 @@ struct kirkwood_dma_data { int burst; }; -extern struct snd_soc_platform_driver kirkwood_soc_platform; +extern const struct snd_soc_platform_driver kirkwood_soc_platform; #endif diff --git a/sound/soc/omap/ams-delta.c b/sound/soc/omap/ams-delta.c index 6c49f3d6fd96..d40219678700 100644 --- a/sound/soc/omap/ams-delta.c +++ b/sound/soc/omap/ams-delta.c @@ -260,7 +260,7 @@ static bool cx81801_cmd_pending; static bool ams_delta_muted; static DEFINE_SPINLOCK(ams_delta_lock); -static void cx81801_timeout(unsigned long data) +static void cx81801_timeout(struct timer_list *unused) { int muted; @@ -349,7 +349,7 @@ static void cx81801_receive(struct tty_struct *tty, /* First modem response, complete setup procedure */ /* Initialize timer used for config pulse generation */ - setup_timer(&cx81801_timer, cx81801_timeout, 0); + timer_setup(&cx81801_timer, cx81801_timeout, 0); v253_ops.receive_buf(tty, cp, fp, count); diff --git a/sound/soc/omap/omap-hdmi-audio.c b/sound/soc/omap/omap-hdmi-audio.c index 3e9cc4842a1d..8eeac7cab1c1 100644 --- a/sound/soc/omap/omap-hdmi-audio.c +++ b/sound/soc/omap/omap-hdmi-audio.c @@ -362,6 +362,9 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev) card->name = devm_kasprintf(dev, GFP_KERNEL, "HDMI %s", dev_name(ad->dssdev)); + if (!card->name) + return -ENOMEM; + card->owner = THIS_MODULE; card->dai_link = devm_kzalloc(dev, sizeof(*(card->dai_link)), GFP_KERNEL); diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index f49bf02e5ec2..803818aabee9 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -29,21 +29,41 @@ static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97) { - pxa2xx_ac97_try_warm_reset(ac97); + pxa2xx_ac97_try_warm_reset(); - pxa2xx_ac97_finish_reset(ac97); + pxa2xx_ac97_finish_reset(); } static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97) { - pxa2xx_ac97_try_cold_reset(ac97); + pxa2xx_ac97_try_cold_reset(); - pxa2xx_ac97_finish_reset(ac97); + pxa2xx_ac97_finish_reset(); +} + +static unsigned short pxa2xx_ac97_legacy_read(struct snd_ac97 *ac97, + unsigned short reg) +{ + int ret; + + ret = pxa2xx_ac97_read(ac97->num, reg); + if (ret < 0) + return 0; + else + return (unsigned short)(ret & 0xffff); +} + +static void pxa2xx_ac97_legacy_write(struct snd_ac97 *ac97, + unsigned short reg, unsigned short val) +{ + int ret; + + ret = pxa2xx_ac97_write(ac97->num, reg, val); } static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { - .read = pxa2xx_ac97_read, - .write = pxa2xx_ac97_write, + .read = pxa2xx_ac97_legacy_read, + .write = pxa2xx_ac97_legacy_write, .warm_reset = pxa2xx_ac97_warm_reset, .reset = pxa2xx_ac97_cold_reset, }; diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index e1945e1772cd..caf71aab8196 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -74,7 +74,6 @@ static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream) data->i2s_port = cpu_dai->driver->id; runtime->private_data = data; - dma_ch = 0; if (v->alloc_dma_channel) dma_ch = v->alloc_dma_channel(drvdata, dir); else @@ -122,7 +121,6 @@ static int lpass_platform_pcmops_close(struct snd_pcm_substream *substream) struct lpass_pcm_data *data; data = runtime->private_data; - v = drvdata->variant; drvdata->substream[data->dma_ch] = NULL; if (v->free_dma_channel) v->free_dma_channel(drvdata, data->dma_ch); diff --git a/sound/soc/rockchip/rk3399_gru_sound.c b/sound/soc/rockchip/rk3399_gru_sound.c index 0513fe480353..d64fbbd50544 100644 --- a/sound/soc/rockchip/rk3399_gru_sound.c +++ b/sound/soc/rockchip/rk3399_gru_sound.c @@ -23,6 +23,7 @@ #include <linux/of_gpio.h> #include <linux/delay.h> #include <linux/spi/spi.h> +#include <linux/i2c.h> #include <linux/input.h> #include <sound/core.h> #include <sound/jack.h> @@ -47,18 +48,7 @@ static const struct snd_soc_dapm_widget rockchip_dapm_widgets[] = { SND_SOC_DAPM_SPK("Speakers", NULL), SND_SOC_DAPM_MIC("Headset Mic", NULL), SND_SOC_DAPM_MIC("Int Mic", NULL), -}; - -static const struct snd_soc_dapm_route rockchip_dapm_routes[] = { - /* Input Lines */ - {"MIC", NULL, "Headset Mic"}, - {"DMIC1L", NULL, "Int Mic"}, - {"DMIC1R", NULL, "Int Mic"}, - - /* Output Lines */ - {"Headphones", NULL, "HPL"}, - {"Headphones", NULL, "HPR"}, - {"Speakers", NULL, "Speaker"}, + SND_SOC_DAPM_LINE("HDMI", NULL), }; static const struct snd_kcontrol_new rockchip_controls[] = { @@ -66,6 +56,7 @@ static const struct snd_kcontrol_new rockchip_controls[] = { SOC_DAPM_PIN_SWITCH("Speakers"), SOC_DAPM_PIN_SWITCH("Headset Mic"), SOC_DAPM_PIN_SWITCH("Int Mic"), + SOC_DAPM_PIN_SWITCH("HDMI"), }; static int rockchip_sound_max98357a_hw_params(struct snd_pcm_substream *substream, @@ -314,8 +305,6 @@ static struct snd_soc_card rockchip_sound_card = { .owner = THIS_MODULE, .dapm_widgets = rockchip_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(rockchip_dapm_widgets), - .dapm_routes = rockchip_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(rockchip_dapm_routes), .controls = rockchip_controls, .num_controls = ARRAY_SIZE(rockchip_controls), }; @@ -329,15 +318,6 @@ enum { DAILINK_RT5514_DSP, }; -static const char * const dailink_compat[] = { - [DAILINK_CDNDP] = "rockchip,rk3399-cdn-dp", - [DAILINK_DA7219] = "dlg,da7219", - [DAILINK_DMIC] = "dmic-codec", - [DAILINK_MAX98357A] = "maxim,max98357a", - [DAILINK_RT5514] = "realtek,rt5514-i2c", - [DAILINK_RT5514_DSP] = "realtek,rt5514-spi", -}; - static const struct snd_soc_dai_link rockchip_dais[] = { [DAILINK_CDNDP] = { .name = "DP", @@ -391,13 +371,117 @@ static const struct snd_soc_dai_link rockchip_dais[] = { }, }; +static const struct snd_soc_dapm_route rockchip_sound_cdndp_routes[] = { + /* Output */ + {"HDMI", NULL, "TX"}, +}; + +static const struct snd_soc_dapm_route rockchip_sound_da7219_routes[] = { + /* Output */ + {"Headphones", NULL, "HPL"}, + {"Headphones", NULL, "HPR"}, + + /* Input */ + {"MIC", NULL, "Headset Mic"}, +}; + +static const struct snd_soc_dapm_route rockchip_sound_dmic_routes[] = { + /* Input */ + {"DMic", NULL, "Int Mic"}, +}; + +static const struct snd_soc_dapm_route rockchip_sound_max98357a_routes[] = { + /* Output */ + {"Speakers", NULL, "Speaker"}, +}; + +static const struct snd_soc_dapm_route rockchip_sound_rt5514_routes[] = { + /* Input */ + {"DMIC1L", NULL, "Int Mic"}, + {"DMIC1R", NULL, "Int Mic"}, +}; + +struct rockchip_sound_route { + const struct snd_soc_dapm_route *routes; + int num_routes; +}; + +static const struct rockchip_sound_route rockchip_routes[] = { + [DAILINK_CDNDP] = { + .routes = rockchip_sound_cdndp_routes, + .num_routes = ARRAY_SIZE(rockchip_sound_cdndp_routes), + }, + [DAILINK_DA7219] = { + .routes = rockchip_sound_da7219_routes, + .num_routes = ARRAY_SIZE(rockchip_sound_da7219_routes), + }, + [DAILINK_DMIC] = { + .routes = rockchip_sound_dmic_routes, + .num_routes = ARRAY_SIZE(rockchip_sound_dmic_routes), + }, + [DAILINK_MAX98357A] = { + .routes = rockchip_sound_max98357a_routes, + .num_routes = ARRAY_SIZE(rockchip_sound_max98357a_routes), + }, + [DAILINK_RT5514] = { + .routes = rockchip_sound_rt5514_routes, + .num_routes = ARRAY_SIZE(rockchip_sound_rt5514_routes), + }, + [DAILINK_RT5514_DSP] = {}, +}; + +struct dailink_match_data { + const char *compatible; + struct bus_type *bus_type; +}; + +static const struct dailink_match_data dailink_match[] = { + [DAILINK_CDNDP] = { + .compatible = "rockchip,rk3399-cdn-dp", + }, + [DAILINK_DA7219] = { + .compatible = "dlg,da7219", + }, + [DAILINK_DMIC] = { + .compatible = "dmic-codec", + }, + [DAILINK_MAX98357A] = { + .compatible = "maxim,max98357a", + }, + [DAILINK_RT5514] = { + .compatible = "realtek,rt5514", + .bus_type = &i2c_bus_type, + }, + [DAILINK_RT5514_DSP] = { + .compatible = "realtek,rt5514", + .bus_type = &spi_bus_type, + }, +}; + +static int of_dev_node_match(struct device *dev, void *data) +{ + return dev->of_node == data; +} + static int rockchip_sound_codec_node_match(struct device_node *np_codec) { + struct device *dev; int i; - for (i = 0; i < ARRAY_SIZE(dailink_compat); i++) { - if (of_device_is_compatible(np_codec, dailink_compat[i])) - return i; + for (i = 0; i < ARRAY_SIZE(dailink_match); i++) { + if (!of_device_is_compatible(np_codec, + dailink_match[i].compatible)) + continue; + + if (dailink_match[i].bus_type) { + dev = bus_find_device(dailink_match[i].bus_type, NULL, + np_codec, of_dev_node_match); + if (!dev) + continue; + put_device(dev); + } + + return i; } return -1; } @@ -408,16 +492,28 @@ static int rockchip_sound_of_parse_dais(struct device *dev, struct device_node *np_cpu, *np_cpu0, *np_cpu1; struct device_node *np_codec; struct snd_soc_dai_link *dai; + struct snd_soc_dapm_route *routes; int i, index; + int num_routes; card->dai_link = devm_kzalloc(dev, sizeof(rockchip_dais), GFP_KERNEL); if (!card->dai_link) return -ENOMEM; + num_routes = 0; + for (i = 0; i < ARRAY_SIZE(rockchip_routes); i++) + num_routes += rockchip_routes[i].num_routes; + routes = devm_kzalloc(dev, num_routes * sizeof(*routes), + GFP_KERNEL); + if (!routes) + return -ENOMEM; + card->dapm_routes = routes; + np_cpu0 = of_parse_phandle(dev->of_node, "rockchip,cpu", 0); np_cpu1 = of_parse_phandle(dev->of_node, "rockchip,cpu", 1); + card->num_dapm_routes = 0; card->num_links = 0; for (i = 0; i < ARRAY_SIZE(rockchip_dais); i++) { np_codec = of_parse_phandle(dev->of_node, @@ -445,6 +541,17 @@ static int rockchip_sound_of_parse_dais(struct device *dev, dai->codec_of_node = np_codec; dai->platform_of_node = np_cpu; dai->cpu_of_node = np_cpu; + + if (card->num_dapm_routes + rockchip_routes[index].num_routes > + num_routes) { + dev_err(dev, "Too many routes\n"); + return -EINVAL; + } + + memcpy(routes + card->num_dapm_routes, + rockchip_routes[index].routes, + rockchip_routes[index].num_routes * sizeof(*routes)); + card->num_dapm_routes += rockchip_routes[index].num_routes; } return 0; diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index b6590467fd14..908211e1d6fc 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -692,7 +692,6 @@ static int rockchip_i2s_remove(struct platform_device *pdev) if (!pm_runtime_status_suspended(&pdev->dev)) i2s_runtime_suspend(&pdev->dev); - clk_disable_unprepare(i2s->mclk); clk_disable_unprepare(i2s->hclk); return 0; diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 10a4da06c0a1..233f1c9a4b6c 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -552,8 +552,11 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, } ret = clk_prepare_enable(i2s->op_clk); - if (ret) + if (ret) { + clk_put(i2s->op_clk); + i2s->op_clk = NULL; goto err; + } i2s->rclk_srcrate = clk_get_rate(i2s->op_clk); /* Over-ride the other's */ @@ -1096,6 +1099,7 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, i2s->pdev = pdev; i2s->pri_dai = NULL; i2s->sec_dai = NULL; + i2s->i2s_dai_drv.id = 1; i2s->i2s_dai_drv.symmetric_rates = 1; i2s->i2s_dai_drv.probe = samsung_i2s_dai_probe; i2s->i2s_dai_drv.remove = samsung_i2s_dai_remove; @@ -1108,10 +1112,13 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, i2s->i2s_dai_drv.playback.formats = SAMSUNG_I2S_FMTS; if (!sec) { + i2s->i2s_dai_drv.name = SAMSUNG_I2S_DAI; i2s->i2s_dai_drv.capture.channels_min = 1; i2s->i2s_dai_drv.capture.channels_max = 2; i2s->i2s_dai_drv.capture.rates = i2s_dai_data->pcm_rates; i2s->i2s_dai_drv.capture.formats = SAMSUNG_I2S_FMTS; + } else { + i2s->i2s_dai_drv.name = SAMSUNG_I2S_DAI_SEC; } return i2s; } @@ -1285,6 +1292,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) } } } + quirks &= ~(QUIRK_SEC_DAI | QUIRK_SUPPORTS_IDMA); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); pri_dai->addr = devm_ioremap_resource(&pdev->dev, res); diff --git a/sound/soc/samsung/i2s.h b/sound/soc/samsung/i2s.h index 21ff24e930db..79781de2f247 100644 --- a/sound/soc/samsung/i2s.h +++ b/sound/soc/samsung/i2s.h @@ -13,6 +13,9 @@ #ifndef __SND_SOC_SAMSUNG_I2S_H #define __SND_SOC_SAMSUNG_I2S_H +#define SAMSUNG_I2S_DAI "samsung-i2s" +#define SAMSUNG_I2S_DAI_SEC "samsung-i2s-sec" + #define SAMSUNG_I2S_DIV_BCLK 1 #define SAMSUNG_I2S_RCLKSRC_0 0 diff --git a/sound/soc/samsung/tm2_wm5110.c b/sound/soc/samsung/tm2_wm5110.c index 68698f3d72f9..a55d18703fe7 100644 --- a/sound/soc/samsung/tm2_wm5110.c +++ b/sound/soc/samsung/tm2_wm5110.c @@ -383,6 +383,7 @@ static struct snd_soc_dai_link tm2_dai_links[] = { { .name = "WM5110 AIF1", .stream_name = "HiFi Primary", + .cpu_dai_name = SAMSUNG_I2S_DAI, .codec_dai_name = "wm5110-aif1", .ops = &tm2_aif1_ops, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | @@ -390,6 +391,7 @@ static struct snd_soc_dai_link tm2_dai_links[] = { }, { .name = "WM5110 Voice", .stream_name = "Voice call", + .cpu_dai_name = SAMSUNG_I2S_DAI, .codec_dai_name = "wm5110-aif2", .ops = &tm2_aif2_ops, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | @@ -398,6 +400,7 @@ static struct snd_soc_dai_link tm2_dai_links[] = { }, { .name = "WM5110 BT", .stream_name = "Bluetooth", + .cpu_dai_name = SAMSUNG_I2S_DAI, .codec_dai_name = "wm5110-aif3", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM, @@ -436,8 +439,7 @@ static int tm2_probe(struct platform_device *pdev) snd_soc_card_set_drvdata(card, priv); card->dev = dev; - priv->gpio_mic_bias = devm_gpiod_get(dev, "mic-bias", - GPIOF_OUT_INIT_LOW); + priv->gpio_mic_bias = devm_gpiod_get(dev, "mic-bias", GPIOD_OUT_HIGH); if (IS_ERR(priv->gpio_mic_bias)) { dev_err(dev, "Failed to get mic bias gpio\n"); return PTR_ERR(priv->gpio_mic_bias); @@ -477,7 +479,6 @@ static int tm2_probe(struct platform_device *pdev) } for (i = 0; i < card->num_links; i++) { - card->dai_link[i].cpu_dai_name = NULL; card->dai_link[i].cpu_name = NULL; card->dai_link[i].platform_name = NULL; card->dai_link[i].codec_of_node = codec_dai_node; diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 6d3c7706d93f..c3aaf4788557 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -1932,14 +1932,9 @@ static int fsi_probe(struct platform_device *pdev) core = NULL; if (np) { - const struct of_device_id *of_id; - - of_id = of_match_device(fsi_of_match, &pdev->dev); - if (of_id) { - core = of_id->data; - fsi_of_parse("fsia", np, &info.port_a, &pdev->dev); - fsi_of_parse("fsib", np, &info.port_b, &pdev->dev); - } + core = of_device_get_match_data(&pdev->dev); + fsi_of_parse("fsia", np, &info.port_a, &pdev->dev); + fsi_of_parse("fsib", np, &info.port_b, &pdev->dev); } else { const struct platform_device_id *id_entry = pdev->id_entry; if (id_entry) diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 938baff86ef2..8ddb08714faa 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -44,7 +44,6 @@ struct rsnd_adg { #define LRCLK_ASYNC (1 << 0) #define AUDIO_OUT_48 (1 << 1) -#define adg_mode_flags(adg) (adg->flags) #define for_each_rsnd_clk(pos, adg, i) \ for (i = 0; \ @@ -58,6 +57,13 @@ struct rsnd_adg { i++) #define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg) +static const char * const clk_name[] = { + [CLKA] = "clk_a", + [CLKB] = "clk_b", + [CLKC] = "clk_c", + [CLKI] = "clk_i", +}; + static u32 rsnd_adg_calculate_rbgx(unsigned long div) { int i, ratio; @@ -280,6 +286,7 @@ static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val) struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); struct rsnd_adg *adg = rsnd_priv_to_adg(priv); struct rsnd_mod *adg_mod = rsnd_mod_get(adg); + struct device *dev = rsnd_priv_to_dev(priv); int id = rsnd_mod_id(ssi_mod); int shift = (id % 4) * 8; u32 mask = 0xFF << shift; @@ -306,12 +313,13 @@ static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val) rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL2, mask, val); break; } + + dev_dbg(dev, "AUDIO_CLK_SEL is 0x%x\n", val); } int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate) { struct rsnd_adg *adg = rsnd_priv_to_adg(priv); - struct device *dev = rsnd_priv_to_dev(priv); struct clk *clk; int i; int sel_table[] = { @@ -321,8 +329,6 @@ int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate) [CLKI] = 0x0, }; - dev_dbg(dev, "request clock = %d\n", rate); - /* * find suitable clock from * AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC/AUDIO_CLKI. @@ -366,8 +372,8 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) rsnd_adg_set_ssi_clk(ssi_mod, data); - if (adg_mode_flags(adg) & LRCLK_ASYNC) { - if (adg_mode_flags(adg) & AUDIO_OUT_48) + if (rsnd_flags_has(adg, LRCLK_ASYNC)) { + if (rsnd_flags_has(adg, AUDIO_OUT_48)) ckr = 0x80000000; } else { if (0 == (rate % 8000)) @@ -378,9 +384,10 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate) rsnd_mod_write(adg_mod, BRRA, adg->rbga); rsnd_mod_write(adg_mod, BRRB, adg->rbgb); - dev_dbg(dev, "ADG: %s[%d] selects 0x%x for %d\n", - rsnd_mod_name(ssi_mod), rsnd_mod_id(ssi_mod), - data, rate); + dev_dbg(dev, "CLKOUT is based on BRG%c (= %dHz)\n", + (ckr) ? 'B' : 'A', + (ckr) ? adg->rbgb_rate_for_48khz : + adg->rbga_rate_for_441khz); return 0; } @@ -409,21 +416,12 @@ static void rsnd_adg_get_clkin(struct rsnd_priv *priv, { struct device *dev = rsnd_priv_to_dev(priv); struct clk *clk; - static const char * const clk_name[] = { - [CLKA] = "clk_a", - [CLKB] = "clk_b", - [CLKC] = "clk_c", - [CLKI] = "clk_i", - }; int i; for (i = 0; i < CLKMAX; i++) { clk = devm_clk_get(dev, clk_name[i]); adg->clk[i] = IS_ERR(clk) ? NULL : clk; } - - for_each_rsnd_clk(clk, adg, i) - dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk)); } static void rsnd_adg_get_clkout(struct rsnd_priv *priv, @@ -479,10 +477,10 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv, } if (req_rate[0] % 48000 == 0) - adg->flags = AUDIO_OUT_48; + rsnd_flags_set(adg, AUDIO_OUT_48); if (of_get_property(np, "clkout-lr-asynchronous", NULL)) - adg->flags = LRCLK_ASYNC; + rsnd_flags_set(adg, LRCLK_ASYNC); /* * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC @@ -512,7 +510,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv, adg->rbga_rate_for_441khz = rate / div; ckr |= brg_table[i] << 20; if (req_441kHz_rate && - !(adg_mode_flags(adg) & AUDIO_OUT_48)) + !rsnd_flags_has(adg, AUDIO_OUT_48)) parent_clk_name = __clk_get_name(clk); } } @@ -528,7 +526,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv, adg->rbgb_rate_for_48khz = rate / div; ckr |= brg_table[i] << 16; if (req_48kHz_rate && - (adg_mode_flags(adg) & AUDIO_OUT_48)) + rsnd_flags_has(adg, AUDIO_OUT_48)) parent_clk_name = __clk_get_name(clk); } } @@ -572,12 +570,35 @@ rsnd_adg_get_clkout_end: adg->ckr = ckr; adg->rbga = rbga; adg->rbgb = rbgb; +} + +#ifdef DEBUG +static void rsnd_adg_clk_dbg_info(struct rsnd_priv *priv, struct rsnd_adg *adg) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct clk *clk; + int i; + + for_each_rsnd_clk(clk, adg, i) + dev_dbg(dev, "%s : %p : %ld\n", + clk_name[i], clk, clk_get_rate(clk)); - for_each_rsnd_clkout(clk, adg, i) - dev_dbg(dev, "clkout %d : %p : %ld\n", i, clk, clk_get_rate(clk)); dev_dbg(dev, "BRGCKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n", - ckr, rbga, rbgb); + adg->ckr, adg->rbga, adg->rbgb); + dev_dbg(dev, "BRGA (for 44100 base) = %d\n", adg->rbga_rate_for_441khz); + dev_dbg(dev, "BRGB (for 48000 base) = %d\n", adg->rbgb_rate_for_48khz); + + /* + * Actual CLKOUT will be exchanged in rsnd_adg_ssi_clk_try_start() + * by BRGCKR::BRGCKR_31 + */ + for_each_rsnd_clkout(clk, adg, i) + dev_dbg(dev, "clkout %d : %p : %ld\n", i, + clk, clk_get_rate(clk)); } +#else +#define rsnd_adg_clk_dbg_info(priv, adg) +#endif int rsnd_adg_probe(struct rsnd_priv *priv) { @@ -596,6 +617,7 @@ int rsnd_adg_probe(struct rsnd_priv *priv) rsnd_adg_get_clkin(priv, adg); rsnd_adg_get_clkout(priv, adg); + rsnd_adg_clk_dbg_info(priv, adg); priv->adg = adg; diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 107133297e8d..c70eb2097816 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -121,14 +121,6 @@ void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type) } } -char *rsnd_mod_name(struct rsnd_mod *mod) -{ - if (!mod || !mod->ops) - return "unknown"; - - return mod->ops->name; -} - struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { @@ -172,8 +164,7 @@ int rsnd_mod_init(struct rsnd_priv *priv, void rsnd_mod_quit(struct rsnd_mod *mod) { - if (mod->clk) - clk_unprepare(mod->clk); + clk_unprepare(mod->clk); mod->clk = NULL; } @@ -200,7 +191,10 @@ void rsnd_mod_interrupt(struct rsnd_mod *mod, int rsnd_io_is_working(struct rsnd_dai_stream *io) { /* see rsnd_dai_stream_init/quit() */ - return !!io->substream; + if (io->substream) + return snd_pcm_running(io->substream); + + return 0; } int rsnd_runtime_channel_original(struct rsnd_dai_stream *io) @@ -407,11 +401,9 @@ struct rsnd_mod *rsnd_mod_next(int *iterator, for (; *iterator < max; (*iterator)++) { type = (array) ? array[*iterator] : *iterator; - mod = io->mod[type]; - if (!mod) - continue; - - return mod; + mod = rsnd_io_to_mod(io, type); + if (mod) + return mod; } return NULL; @@ -1242,6 +1234,33 @@ struct rsnd_kctrl_cfg *rsnd_kctrl_init_s(struct rsnd_kctrl_cfg_s *cfg) return &cfg->cfg; } +const char * const volume_ramp_rate[] = { + "128 dB/1 step", /* 00000 */ + "64 dB/1 step", /* 00001 */ + "32 dB/1 step", /* 00010 */ + "16 dB/1 step", /* 00011 */ + "8 dB/1 step", /* 00100 */ + "4 dB/1 step", /* 00101 */ + "2 dB/1 step", /* 00110 */ + "1 dB/1 step", /* 00111 */ + "0.5 dB/1 step", /* 01000 */ + "0.25 dB/1 step", /* 01001 */ + "0.125 dB/1 step", /* 01010 = VOLUME_RAMP_MAX_MIX */ + "0.125 dB/2 steps", /* 01011 */ + "0.125 dB/4 steps", /* 01100 */ + "0.125 dB/8 steps", /* 01101 */ + "0.125 dB/16 steps", /* 01110 */ + "0.125 dB/32 steps", /* 01111 */ + "0.125 dB/64 steps", /* 10000 */ + "0.125 dB/128 steps", /* 10001 */ + "0.125 dB/256 steps", /* 10010 */ + "0.125 dB/512 steps", /* 10011 */ + "0.125 dB/1024 steps", /* 10100 */ + "0.125 dB/2048 steps", /* 10101 */ + "0.125 dB/4096 steps", /* 10110 */ + "0.125 dB/8192 steps", /* 10111 = VOLUME_RAMP_MAX_DVC */ +}; + int rsnd_kctrl_new(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd, diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index e7f53f44165d..d201d551866d 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -81,8 +81,11 @@ struct rsnd_ctu { struct rsnd_kctrl_cfg_m sv3; struct rsnd_kctrl_cfg_s reset; int channels; + u32 flags; }; +#define KCTRL_INITIALIZED (1 << 0) + #define rsnd_ctu_nr(priv) ((priv)->ctu_nr) #define for_each_rsnd_ctu(pos, priv, i) \ for ((i) = 0; \ @@ -130,7 +133,7 @@ static void rsnd_ctu_value_init(struct rsnd_dai_stream *io, int i; for (i = 0; i < RSND_MAX_CHANNELS; i++) { - u32 val = ctu->pass.val[i]; + u32 val = rsnd_kctrl_valm(ctu->pass, i); cpmdr |= val << (28 - (i * 4)); @@ -147,44 +150,44 @@ static void rsnd_ctu_value_init(struct rsnd_dai_stream *io, rsnd_mod_write(mod, CTU_SCMDR, scmdr); if (scmdr > 0) { - rsnd_mod_write(mod, CTU_SV00R, ctu->sv0.val[0]); - rsnd_mod_write(mod, CTU_SV01R, ctu->sv0.val[1]); - rsnd_mod_write(mod, CTU_SV02R, ctu->sv0.val[2]); - rsnd_mod_write(mod, CTU_SV03R, ctu->sv0.val[3]); - rsnd_mod_write(mod, CTU_SV04R, ctu->sv0.val[4]); - rsnd_mod_write(mod, CTU_SV05R, ctu->sv0.val[5]); - rsnd_mod_write(mod, CTU_SV06R, ctu->sv0.val[6]); - rsnd_mod_write(mod, CTU_SV07R, ctu->sv0.val[7]); + rsnd_mod_write(mod, CTU_SV00R, rsnd_kctrl_valm(ctu->sv0, 0)); + rsnd_mod_write(mod, CTU_SV01R, rsnd_kctrl_valm(ctu->sv0, 1)); + rsnd_mod_write(mod, CTU_SV02R, rsnd_kctrl_valm(ctu->sv0, 2)); + rsnd_mod_write(mod, CTU_SV03R, rsnd_kctrl_valm(ctu->sv0, 3)); + rsnd_mod_write(mod, CTU_SV04R, rsnd_kctrl_valm(ctu->sv0, 4)); + rsnd_mod_write(mod, CTU_SV05R, rsnd_kctrl_valm(ctu->sv0, 5)); + rsnd_mod_write(mod, CTU_SV06R, rsnd_kctrl_valm(ctu->sv0, 6)); + rsnd_mod_write(mod, CTU_SV07R, rsnd_kctrl_valm(ctu->sv0, 7)); } if (scmdr > 1) { - rsnd_mod_write(mod, CTU_SV10R, ctu->sv1.val[0]); - rsnd_mod_write(mod, CTU_SV11R, ctu->sv1.val[1]); - rsnd_mod_write(mod, CTU_SV12R, ctu->sv1.val[2]); - rsnd_mod_write(mod, CTU_SV13R, ctu->sv1.val[3]); - rsnd_mod_write(mod, CTU_SV14R, ctu->sv1.val[4]); - rsnd_mod_write(mod, CTU_SV15R, ctu->sv1.val[5]); - rsnd_mod_write(mod, CTU_SV16R, ctu->sv1.val[6]); - rsnd_mod_write(mod, CTU_SV17R, ctu->sv1.val[7]); + rsnd_mod_write(mod, CTU_SV10R, rsnd_kctrl_valm(ctu->sv1, 0)); + rsnd_mod_write(mod, CTU_SV11R, rsnd_kctrl_valm(ctu->sv1, 1)); + rsnd_mod_write(mod, CTU_SV12R, rsnd_kctrl_valm(ctu->sv1, 2)); + rsnd_mod_write(mod, CTU_SV13R, rsnd_kctrl_valm(ctu->sv1, 3)); + rsnd_mod_write(mod, CTU_SV14R, rsnd_kctrl_valm(ctu->sv1, 4)); + rsnd_mod_write(mod, CTU_SV15R, rsnd_kctrl_valm(ctu->sv1, 5)); + rsnd_mod_write(mod, CTU_SV16R, rsnd_kctrl_valm(ctu->sv1, 6)); + rsnd_mod_write(mod, CTU_SV17R, rsnd_kctrl_valm(ctu->sv1, 7)); } if (scmdr > 2) { - rsnd_mod_write(mod, CTU_SV20R, ctu->sv2.val[0]); - rsnd_mod_write(mod, CTU_SV21R, ctu->sv2.val[1]); - rsnd_mod_write(mod, CTU_SV22R, ctu->sv2.val[2]); - rsnd_mod_write(mod, CTU_SV23R, ctu->sv2.val[3]); - rsnd_mod_write(mod, CTU_SV24R, ctu->sv2.val[4]); - rsnd_mod_write(mod, CTU_SV25R, ctu->sv2.val[5]); - rsnd_mod_write(mod, CTU_SV26R, ctu->sv2.val[6]); - rsnd_mod_write(mod, CTU_SV27R, ctu->sv2.val[7]); + rsnd_mod_write(mod, CTU_SV20R, rsnd_kctrl_valm(ctu->sv2, 0)); + rsnd_mod_write(mod, CTU_SV21R, rsnd_kctrl_valm(ctu->sv2, 1)); + rsnd_mod_write(mod, CTU_SV22R, rsnd_kctrl_valm(ctu->sv2, 2)); + rsnd_mod_write(mod, CTU_SV23R, rsnd_kctrl_valm(ctu->sv2, 3)); + rsnd_mod_write(mod, CTU_SV24R, rsnd_kctrl_valm(ctu->sv2, 4)); + rsnd_mod_write(mod, CTU_SV25R, rsnd_kctrl_valm(ctu->sv2, 5)); + rsnd_mod_write(mod, CTU_SV26R, rsnd_kctrl_valm(ctu->sv2, 6)); + rsnd_mod_write(mod, CTU_SV27R, rsnd_kctrl_valm(ctu->sv2, 7)); } if (scmdr > 3) { - rsnd_mod_write(mod, CTU_SV30R, ctu->sv3.val[0]); - rsnd_mod_write(mod, CTU_SV31R, ctu->sv3.val[1]); - rsnd_mod_write(mod, CTU_SV32R, ctu->sv3.val[2]); - rsnd_mod_write(mod, CTU_SV33R, ctu->sv3.val[3]); - rsnd_mod_write(mod, CTU_SV34R, ctu->sv3.val[4]); - rsnd_mod_write(mod, CTU_SV35R, ctu->sv3.val[5]); - rsnd_mod_write(mod, CTU_SV36R, ctu->sv3.val[6]); - rsnd_mod_write(mod, CTU_SV37R, ctu->sv3.val[7]); + rsnd_mod_write(mod, CTU_SV30R, rsnd_kctrl_valm(ctu->sv3, 0)); + rsnd_mod_write(mod, CTU_SV31R, rsnd_kctrl_valm(ctu->sv3, 1)); + rsnd_mod_write(mod, CTU_SV32R, rsnd_kctrl_valm(ctu->sv3, 2)); + rsnd_mod_write(mod, CTU_SV33R, rsnd_kctrl_valm(ctu->sv3, 3)); + rsnd_mod_write(mod, CTU_SV34R, rsnd_kctrl_valm(ctu->sv3, 4)); + rsnd_mod_write(mod, CTU_SV35R, rsnd_kctrl_valm(ctu->sv3, 5)); + rsnd_mod_write(mod, CTU_SV36R, rsnd_kctrl_valm(ctu->sv3, 6)); + rsnd_mod_write(mod, CTU_SV37R, rsnd_kctrl_valm(ctu->sv3, 7)); } rsnd_mod_write(mod, CTU_CTUIR, 0); @@ -196,17 +199,17 @@ static void rsnd_ctu_value_reset(struct rsnd_dai_stream *io, struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); int i; - if (!ctu->reset.val) + if (!rsnd_kctrl_vals(ctu->reset)) return; for (i = 0; i < RSND_MAX_CHANNELS; i++) { - ctu->pass.val[i] = 0; - ctu->sv0.val[i] = 0; - ctu->sv1.val[i] = 0; - ctu->sv2.val[i] = 0; - ctu->sv3.val[i] = 0; + rsnd_kctrl_valm(ctu->pass, i) = 0; + rsnd_kctrl_valm(ctu->sv0, i) = 0; + rsnd_kctrl_valm(ctu->sv1, i) = 0; + rsnd_kctrl_valm(ctu->sv2, i) = 0; + rsnd_kctrl_valm(ctu->sv3, i) = 0; } - ctu->reset.val = 0; + rsnd_kctrl_vals(ctu->reset) = 0; } static int rsnd_ctu_init(struct rsnd_mod *mod, @@ -277,6 +280,9 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); int ret; + if (rsnd_flags_has(ctu, KCTRL_INITIALIZED)) + return 0; + /* CTU Pass */ ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU Pass", rsnd_kctrl_accept_anytime, @@ -326,6 +332,8 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, rsnd_ctu_value_reset, &ctu->reset, 1); + rsnd_flags_set(ctu, KCTRL_INITIALIZED); + return ret; } diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index 041ec1080d52..fd557abfe390 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -60,6 +60,14 @@ struct rsnd_dma_ctrl { #define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en) #define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp) +/* for DEBUG */ +static struct rsnd_mod_ops mem_ops = { + .name = "mem", +}; + +static struct rsnd_mod mem = { +}; + /* * Audio DMAC */ @@ -211,11 +219,9 @@ static int rsnd_dmaen_nolock_start(struct rsnd_mod *mod, dma->mod_from, dma->mod_to); if (IS_ERR_OR_NULL(dmaen->chan)) { - int ret = PTR_ERR(dmaen->chan); - dmaen->chan = NULL; dev_err(dev, "can't get dma channel\n"); - return ret; + return -EIO; } return 0; @@ -747,20 +753,22 @@ static void rsnd_dma_of_path(struct rsnd_mod *this, rsnd_mod_name(this), rsnd_mod_id(this)); for (i = 0; i <= idx; i++) { dev_dbg(dev, " %s[%d]%s\n", - rsnd_mod_name(mod[i]), rsnd_mod_id(mod[i]), - (mod[i] == *mod_from) ? " from" : - (mod[i] == *mod_to) ? " to" : ""); + rsnd_mod_name(mod[i] ? mod[i] : &mem), + rsnd_mod_id (mod[i] ? mod[i] : &mem), + (mod[i] == *mod_from) ? " from" : + (mod[i] == *mod_to) ? " to" : ""); } } -int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod, - struct rsnd_mod **dma_mod) +static int rsnd_dma_alloc(struct rsnd_dai_stream *io, struct rsnd_mod *mod, + struct rsnd_mod **dma_mod) { struct rsnd_mod *mod_from = NULL; struct rsnd_mod *mod_to = NULL; struct rsnd_priv *priv = rsnd_io_to_priv(io); struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_dma *dma; struct rsnd_mod_ops *ops; enum rsnd_mod_type type; int (*attach)(struct rsnd_dai_stream *io, struct rsnd_dma *dma, @@ -800,40 +808,47 @@ int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod, type = RSND_MOD_AUDMA; } - if (!(*dma_mod)) { - struct rsnd_dma *dma; + dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); + if (!dma) + return -ENOMEM; - dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); - if (!dma) - return -ENOMEM; + *dma_mod = rsnd_mod_get(dma); - *dma_mod = rsnd_mod_get(dma); + ret = rsnd_mod_init(priv, *dma_mod, ops, NULL, + rsnd_mod_get_status, type, dma_id); + if (ret < 0) + return ret; - ret = rsnd_mod_init(priv, *dma_mod, ops, NULL, - rsnd_mod_get_status, type, dma_id); - if (ret < 0) - return ret; + dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n", + rsnd_mod_name(*dma_mod), rsnd_mod_id(*dma_mod), + rsnd_mod_name(mod_from ? mod_from : &mem), + rsnd_mod_id (mod_from ? mod_from : &mem), + rsnd_mod_name(mod_to ? mod_to : &mem), + rsnd_mod_id (mod_to ? mod_to : &mem)); - dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n", - rsnd_mod_name(*dma_mod), rsnd_mod_id(*dma_mod), - rsnd_mod_name(mod_from), rsnd_mod_id(mod_from), - rsnd_mod_name(mod_to), rsnd_mod_id(mod_to)); + ret = attach(io, dma, mod_from, mod_to); + if (ret < 0) + return ret; + + dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1); + dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0); + dma->mod_from = mod_from; + dma->mod_to = mod_to; + + return 0; +} + +int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod, + struct rsnd_mod **dma_mod) +{ + if (!(*dma_mod)) { + int ret = rsnd_dma_alloc(io, mod, dma_mod); - ret = attach(io, dma, mod_from, mod_to); if (ret < 0) return ret; - - dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1); - dma->dst_addr = rsnd_dma_addr(io, mod_to, is_play, 0); - dma->mod_from = mod_from; - dma->mod_to = mod_to; } - ret = rsnd_dai_connect(*dma_mod, io, type); - if (ret < 0) - return ret; - - return 0; + return rsnd_dai_connect(*dma_mod, io, (*dma_mod)->type); } int rsnd_dma_probe(struct rsnd_priv *priv) @@ -866,5 +881,6 @@ int rsnd_dma_probe(struct rsnd_priv *priv) priv->dma = dmac; - return 0; + /* dummy mem mod for debug */ + return rsnd_mod_init(NULL, &mem, &mem_ops, NULL, NULL, 0, 0); } diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index 1743ade3cc55..dbe54f024d68 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -44,8 +44,11 @@ struct rsnd_dvc { struct rsnd_kctrl_cfg_s ren; /* Ramp Enable */ struct rsnd_kctrl_cfg_s rup; /* Ramp Rate Up */ struct rsnd_kctrl_cfg_s rdown; /* Ramp Rate Down */ + u32 flags; }; +#define KCTRL_INITIALIZED (1 << 0) + #define rsnd_dvc_get(priv, id) ((struct rsnd_dvc *)(priv->dvc) + id) #define rsnd_dvc_nr(priv) ((priv)->dvc_nr) @@ -58,33 +61,6 @@ struct rsnd_dvc { ((pos) = (struct rsnd_dvc *)(priv)->dvc + i); \ i++) -static const char * const dvc_ramp_rate[] = { - "128 dB/1 step", /* 00000 */ - "64 dB/1 step", /* 00001 */ - "32 dB/1 step", /* 00010 */ - "16 dB/1 step", /* 00011 */ - "8 dB/1 step", /* 00100 */ - "4 dB/1 step", /* 00101 */ - "2 dB/1 step", /* 00110 */ - "1 dB/1 step", /* 00111 */ - "0.5 dB/1 step", /* 01000 */ - "0.25 dB/1 step", /* 01001 */ - "0.125 dB/1 step", /* 01010 */ - "0.125 dB/2 steps", /* 01011 */ - "0.125 dB/4 steps", /* 01100 */ - "0.125 dB/8 steps", /* 01101 */ - "0.125 dB/16 steps", /* 01110 */ - "0.125 dB/32 steps", /* 01111 */ - "0.125 dB/64 steps", /* 10000 */ - "0.125 dB/128 steps", /* 10001 */ - "0.125 dB/256 steps", /* 10010 */ - "0.125 dB/512 steps", /* 10011 */ - "0.125 dB/1024 steps", /* 10100 */ - "0.125 dB/2048 steps", /* 10101 */ - "0.125 dB/4096 steps", /* 10110 */ - "0.125 dB/8192 steps", /* 10111 */ -}; - static void rsnd_dvc_activation(struct rsnd_mod *mod) { rsnd_mod_write(mod, DVC_SWRSR, 0); @@ -97,8 +73,9 @@ static void rsnd_dvc_halt(struct rsnd_mod *mod) rsnd_mod_write(mod, DVC_SWRSR, 0); } -#define rsnd_dvc_get_vrpdr(dvc) (dvc->rup.val << 8 | dvc->rdown.val) -#define rsnd_dvc_get_vrdbr(dvc) (0x3ff - (dvc->volume.val[0] >> 13)) +#define rsnd_dvc_get_vrpdr(dvc) (rsnd_kctrl_vals(dvc->rup) << 8 | \ + rsnd_kctrl_vals(dvc->rdown)) +#define rsnd_dvc_get_vrdbr(dvc) (0x3ff - (rsnd_kctrl_valm(dvc->volume, 0) >> 13)) static void rsnd_dvc_volume_parameter(struct rsnd_dai_stream *io, struct rsnd_mod *mod) @@ -108,12 +85,12 @@ static void rsnd_dvc_volume_parameter(struct rsnd_dai_stream *io, int i; /* Enable Ramp */ - if (dvc->ren.val) + if (rsnd_kctrl_vals(dvc->ren)) for (i = 0; i < RSND_MAX_CHANNELS; i++) - val[i] = dvc->volume.cfg.max; + val[i] = rsnd_kctrl_max(dvc->volume); else for (i = 0; i < RSND_MAX_CHANNELS; i++) - val[i] = dvc->volume.val[i]; + val[i] = rsnd_kctrl_valm(dvc->volume, i); /* Enable Digital Volume */ rsnd_mod_write(mod, DVC_VOL0R, val[0]); @@ -143,7 +120,7 @@ static void rsnd_dvc_volume_init(struct rsnd_dai_stream *io, dvucr |= 0x101; /* Enable Ramp */ - if (dvc->ren.val) { + if (rsnd_kctrl_vals(dvc->ren)) { dvucr |= 0x10; /* @@ -185,10 +162,10 @@ static void rsnd_dvc_volume_update(struct rsnd_dai_stream *io, u32 vrdbr = 0; int i; - for (i = 0; i < dvc->mute.cfg.size; i++) - zcmcr |= (!!dvc->mute.cfg.val[i]) << i; + for (i = 0; i < rsnd_kctrl_size(dvc->mute); i++) + zcmcr |= (!!rsnd_kctrl_valm(dvc->mute, i)) << i; - if (dvc->ren.val) { + if (rsnd_kctrl_vals(dvc->ren)) { vrpdr = rsnd_dvc_get_vrpdr(dvc); vrdbr = rsnd_dvc_get_vrdbr(dvc); } @@ -254,6 +231,9 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, int channels = rsnd_rdai_channels_get(rdai); int ret; + if (rsnd_flags_has(dvc, KCTRL_INITIALIZED)) + return 0; + /* Volume */ ret = rsnd_kctrl_new_m(mod, io, rtd, is_play ? @@ -292,7 +272,8 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, rsnd_kctrl_accept_anytime, rsnd_dvc_volume_update, &dvc->rup, - dvc_ramp_rate); + volume_ramp_rate, + VOLUME_RAMP_MAX_DVC); if (ret < 0) return ret; @@ -302,11 +283,14 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, rsnd_kctrl_accept_anytime, rsnd_dvc_volume_update, &dvc->rdown, - dvc_ramp_rate); + volume_ramp_rate, + VOLUME_RAMP_MAX_DVC); if (ret < 0) return ret; + rsnd_flags_set(dvc, KCTRL_INITIALIZED); + return 0; } diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c index 6c4826c189a4..7998380766f6 100644 --- a/sound/soc/sh/rcar/mix.c +++ b/sound/soc/sh/rcar/mix.c @@ -7,6 +7,33 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ + +/* + * CTUn MIXn + * +------+ +------+ + * [SRC3 / SRC6] -> |CTU n0| -> [MIX n0| -> + * [SRC4 / SRC9] -> |CTU n1| -> [MIX n1| -> + * [SRC0 / SRC1] -> |CTU n2| -> [MIX n2| -> + * [SRC2 / SRC5] -> |CTU n3| -> [MIX n3| -> + * +------+ +------+ + * + * ex) + * DAI0 : playback = <&src0 &ctu02 &mix0 &dvc0 &ssi0>; + * DAI1 : playback = <&src2 &ctu03 &mix0 &dvc0 &ssi0>; + * + * MIX Volume + * amixer set "MIX",0 100% // DAI0 Volume + * amixer set "MIX",1 100% // DAI1 Volume + * + * Volume Ramp + * amixer set "MIX Ramp Up Rate" "0.125 dB/1 step" + * amixer set "MIX Ramp Down Rate" "4 dB/1 step" + * amixer set "MIX Ramp" on + * aplay xxx.wav & + * amixer set "MIX",0 80% // DAI0 Volume Down + * amixer set "MIX",1 100% // DAI1 Volume Up + */ + #include "rsnd.h" #define MIX_NAME_SIZE 16 @@ -14,8 +41,27 @@ struct rsnd_mix { struct rsnd_mod mod; + struct rsnd_kctrl_cfg_s volumeA; /* MDBAR */ + struct rsnd_kctrl_cfg_s volumeB; /* MDBBR */ + struct rsnd_kctrl_cfg_s volumeC; /* MDBCR */ + struct rsnd_kctrl_cfg_s volumeD; /* MDBDR */ + struct rsnd_kctrl_cfg_s ren; /* Ramp Enable */ + struct rsnd_kctrl_cfg_s rup; /* Ramp Rate Up */ + struct rsnd_kctrl_cfg_s rdw; /* Ramp Rate Down */ + u32 flags; }; +#define ONCE_KCTRL_INITIALIZED (1 << 0) +#define HAS_VOLA (1 << 1) +#define HAS_VOLB (1 << 2) +#define HAS_VOLC (1 << 3) +#define HAS_VOLD (1 << 4) + +#define VOL_MAX 0x3ff + +#define rsnd_mod_to_mix(_mod) \ + container_of((_mod), struct rsnd_mix, mod) + #define rsnd_mix_get(priv, id) ((struct rsnd_mix *)(priv->mix) + id) #define rsnd_mix_nr(priv) ((priv)->mix_nr) #define for_each_rsnd_mix(pos, priv, i) \ @@ -36,26 +82,43 @@ static void rsnd_mix_halt(struct rsnd_mod *mod) rsnd_mod_write(mod, MIX_SWRSR, 0); } +#define rsnd_mix_get_vol(mix, X) \ + rsnd_flags_has(mix, HAS_VOL##X) ? \ + (VOL_MAX - rsnd_kctrl_vals(mix->volume##X)) : 0 static void rsnd_mix_volume_parameter(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { - rsnd_mod_write(mod, MIX_MDBAR, 0); - rsnd_mod_write(mod, MIX_MDBBR, 0); - rsnd_mod_write(mod, MIX_MDBCR, 0); - rsnd_mod_write(mod, MIX_MDBDR, 0); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_mix *mix = rsnd_mod_to_mix(mod); + u32 volA = rsnd_mix_get_vol(mix, A); + u32 volB = rsnd_mix_get_vol(mix, B); + u32 volC = rsnd_mix_get_vol(mix, C); + u32 volD = rsnd_mix_get_vol(mix, D); + + dev_dbg(dev, "MIX A/B/C/D = %02x/%02x/%02x/%02x\n", + volA, volB, volC, volD); + + rsnd_mod_write(mod, MIX_MDBAR, volA); + rsnd_mod_write(mod, MIX_MDBBR, volB); + rsnd_mod_write(mod, MIX_MDBCR, volC); + rsnd_mod_write(mod, MIX_MDBDR, volD); } static void rsnd_mix_volume_init(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { + struct rsnd_mix *mix = rsnd_mod_to_mix(mod); + rsnd_mod_write(mod, MIX_MIXIR, 1); /* General Information */ rsnd_mod_write(mod, MIX_ADINR, rsnd_runtime_channel_after_ctu(io)); /* volume step */ - rsnd_mod_write(mod, MIX_MIXMR, 0); - rsnd_mod_write(mod, MIX_MVPDR, 0); + rsnd_mod_write(mod, MIX_MIXMR, rsnd_kctrl_vals(mix->ren)); + rsnd_mod_write(mod, MIX_MVPDR, rsnd_kctrl_vals(mix->rup) << 8 | + rsnd_kctrl_vals(mix->rdw)); /* common volume parameter */ rsnd_mix_volume_parameter(io, mod); @@ -109,11 +172,94 @@ static int rsnd_mix_quit(struct rsnd_mod *mod, return 0; } +static int rsnd_mix_pcm_new(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + struct snd_soc_pcm_runtime *rtd) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_mix *mix = rsnd_mod_to_mix(mod); + struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); + struct rsnd_kctrl_cfg_s *volume; + int ret; + + switch (rsnd_mod_id(src_mod)) { + case 3: + case 6: /* MDBAR */ + volume = &mix->volumeA; + rsnd_flags_set(mix, HAS_VOLA); + break; + case 4: + case 9: /* MDBBR */ + volume = &mix->volumeB; + rsnd_flags_set(mix, HAS_VOLB); + break; + case 0: + case 1: /* MDBCR */ + volume = &mix->volumeC; + rsnd_flags_set(mix, HAS_VOLC); + break; + case 2: + case 5: /* MDBDR */ + volume = &mix->volumeD; + rsnd_flags_set(mix, HAS_VOLD); + break; + default: + dev_err(dev, "unknown SRC is connected\n"); + return -EINVAL; + } + + /* Volume */ + ret = rsnd_kctrl_new_s(mod, io, rtd, + "MIX Playback Volume", + rsnd_kctrl_accept_anytime, + rsnd_mix_volume_update, + volume, VOL_MAX); + if (ret < 0) + return ret; + rsnd_kctrl_vals(*volume) = VOL_MAX; + + if (rsnd_flags_has(mix, ONCE_KCTRL_INITIALIZED)) + return ret; + + /* Ramp */ + ret = rsnd_kctrl_new_s(mod, io, rtd, + "MIX Ramp Switch", + rsnd_kctrl_accept_anytime, + rsnd_mix_volume_update, + &mix->ren, 1); + if (ret < 0) + return ret; + + ret = rsnd_kctrl_new_e(mod, io, rtd, + "MIX Ramp Up Rate", + rsnd_kctrl_accept_anytime, + rsnd_mix_volume_update, + &mix->rup, + volume_ramp_rate, + VOLUME_RAMP_MAX_MIX); + if (ret < 0) + return ret; + + ret = rsnd_kctrl_new_e(mod, io, rtd, + "MIX Ramp Down Rate", + rsnd_kctrl_accept_anytime, + rsnd_mix_volume_update, + &mix->rdw, + volume_ramp_rate, + VOLUME_RAMP_MAX_MIX); + + rsnd_flags_set(mix, ONCE_KCTRL_INITIALIZED); + + return ret; +} + static struct rsnd_mod_ops rsnd_mix_ops = { .name = MIX_NAME, .probe = rsnd_mix_probe_, .init = rsnd_mix_init, .quit = rsnd_mix_quit, + .pcm_new = rsnd_mix_pcm_new, }; struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id) diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index c5de71f2dc8c..57cd2bc773c2 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -355,8 +355,9 @@ struct rsnd_mod { #define __rsnd_mod_call_nolock_start 0 #define __rsnd_mod_call_nolock_stop 1 -#define rsnd_mod_to_priv(mod) ((mod)->priv) -#define rsnd_mod_id(mod) ((mod) ? (mod)->id : -1) +#define rsnd_mod_to_priv(mod) ((mod)->priv) +#define rsnd_mod_name(mod) ((mod)->ops->name) +#define rsnd_mod_id(mod) ((mod)->id) #define rsnd_mod_power_on(mod) clk_enable((mod)->clk) #define rsnd_mod_power_off(mod) clk_disable((mod)->clk) #define rsnd_mod_get(ip) (&(ip)->mod) @@ -371,7 +372,6 @@ int rsnd_mod_init(struct rsnd_priv *priv, enum rsnd_mod_type type, int id); void rsnd_mod_quit(struct rsnd_mod *mod); -char *rsnd_mod_name(struct rsnd_mod *mod); struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, struct rsnd_mod *mod); void rsnd_mod_interrupt(struct rsnd_mod *mod, @@ -601,6 +601,10 @@ struct rsnd_priv { #define rsnd_is_gen1(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN1) #define rsnd_is_gen2(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN2) +#define rsnd_flags_has(p, f) ((p)->flags & (f)) +#define rsnd_flags_set(p, f) ((p)->flags |= (f)) +#define rsnd_flags_del(p, f) ((p)->flags &= ~(f)) + /* * rsnd_kctrl */ @@ -627,6 +631,10 @@ struct rsnd_kctrl_cfg_s { struct rsnd_kctrl_cfg cfg; u32 val; }; +#define rsnd_kctrl_size(x) ((x).cfg.size) +#define rsnd_kctrl_max(x) ((x).cfg.max) +#define rsnd_kctrl_valm(x, i) ((x).val[i]) /* = (x).cfg.val[i] */ +#define rsnd_kctrl_vals(x) ((x).val) /* = (x).cfg.val[0] */ int rsnd_kctrl_accept_anytime(struct rsnd_dai_stream *io); int rsnd_kctrl_accept_runtime(struct rsnd_dai_stream *io); @@ -652,9 +660,13 @@ int rsnd_kctrl_new(struct rsnd_mod *mod, rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_s(cfg), \ NULL, 1, max) -#define rsnd_kctrl_new_e(mod, io, rtd, name, accept, update, cfg, texts) \ +#define rsnd_kctrl_new_e(mod, io, rtd, name, accept, update, cfg, texts, size) \ rsnd_kctrl_new(mod, io, rtd, name, accept, update, rsnd_kctrl_init_s(cfg), \ - texts, 1, ARRAY_SIZE(texts)) + texts, 1, size) + +extern const char * const volume_ramp_rate[]; +#define VOLUME_RAMP_MAX_DVC (0x17 + 1) +#define VOLUME_RAMP_MAX_MIX (0x0a + 1) /* * R-Car SSI diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index fffc07e72627..fece1e5f582f 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -101,9 +101,6 @@ struct rsnd_ssi { #define rsnd_ssi_get(priv, id) ((struct rsnd_ssi *)(priv->ssi) + id) #define rsnd_ssi_nr(priv) ((priv)->ssi_nr) #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) -#define rsnd_ssi_flags_has(p, f) ((p)->flags & f) -#define rsnd_ssi_flags_set(p, f) ((p)->flags |= f) -#define rsnd_ssi_flags_del(p, f) ((p)->flags = ((p)->flags & ~f)) #define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io)) #define rsnd_ssi_is_multi_slave(mod, io) \ (rsnd_ssi_multi_slaves(io) & (1 << rsnd_mod_id(mod))) @@ -116,10 +113,10 @@ int rsnd_ssi_hdmi_port(struct rsnd_dai_stream *io) struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - if (rsnd_ssi_flags_has(ssi, RSND_SSI_HDMI0)) + if (rsnd_flags_has(ssi, RSND_SSI_HDMI0)) return RSND_SSI_HDMI_PORT0; - if (rsnd_ssi_flags_has(ssi, RSND_SSI_HDMI1)) + if (rsnd_flags_has(ssi, RSND_SSI_HDMI1)) return RSND_SSI_HDMI_PORT1; return 0; @@ -134,7 +131,7 @@ int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) if (!rsnd_ssi_is_dma_mode(mod)) return 0; - if (!(rsnd_ssi_flags_has(ssi, RSND_SSI_NO_BUSIF))) + if (!(rsnd_flags_has(ssi, RSND_SSI_NO_BUSIF))) use_busif = 1; if (rsnd_io_to_mod_src(io)) use_busif = 1; @@ -198,10 +195,15 @@ static u32 rsnd_ssi_run_mods(struct rsnd_dai_stream *io) { struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io); struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io); + u32 mods; - return rsnd_ssi_multi_slaves_runtime(io) | - 1 << rsnd_mod_id(ssi_mod) | - 1 << rsnd_mod_id(ssi_parent_mod); + mods = rsnd_ssi_multi_slaves_runtime(io) | + 1 << rsnd_mod_id(ssi_mod); + + if (ssi_parent_mod) + mods |= 1 << rsnd_mod_id(ssi_parent_mod); + + return mods; } u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io) @@ -601,15 +603,18 @@ static int rsnd_ssi_stop(struct rsnd_mod *mod, if (rsnd_ssi_is_parent(mod, io)) return 0; - /* - * disable all IRQ, - * and, wait all data was sent - */ cr = ssi->cr_own | ssi->cr_clk; - rsnd_mod_write(mod, SSICR, cr | EN); - rsnd_ssi_status_check(mod, DIRQ); + /* + * disable all IRQ, + * Playback: Wait all data was sent + * Capture: It might not receave data. Do nothing + */ + if (rsnd_io_is_play(io)) { + rsnd_mod_write(mod, SSICR, cr | EN); + rsnd_ssi_status_check(mod, DIRQ); + } /* * disable SSI, @@ -793,13 +798,13 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod, * But it don't need to call request_irq() many times. * Let's control it by RSND_SSI_PROBED flag. */ - if (!rsnd_ssi_flags_has(ssi, RSND_SSI_PROBED)) { + if (!rsnd_flags_has(ssi, RSND_SSI_PROBED)) { ret = request_irq(ssi->irq, rsnd_ssi_interrupt, IRQF_SHARED, dev_name(dev), mod); - rsnd_ssi_flags_set(ssi, RSND_SSI_PROBED); + rsnd_flags_set(ssi, RSND_SSI_PROBED); } return ret; @@ -817,10 +822,10 @@ static int rsnd_ssi_common_remove(struct rsnd_mod *mod, return 0; /* PIO will request IRQ again */ - if (rsnd_ssi_flags_has(ssi, RSND_SSI_PROBED)) { + if (rsnd_flags_has(ssi, RSND_SSI_PROBED)) { free_irq(ssi->irq, mod); - rsnd_ssi_flags_del(ssi, RSND_SSI_PROBED); + rsnd_flags_del(ssi, RSND_SSI_PROBED); } return 0; @@ -1003,13 +1008,13 @@ static void __rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv, ssi = rsnd_mod_to_ssi(mod); if (strstr(remote_ep->full_name, "hdmi0")) { - rsnd_ssi_flags_set(ssi, RSND_SSI_HDMI0); + rsnd_flags_set(ssi, RSND_SSI_HDMI0); dev_dbg(dev, "%s[%d] connected to HDMI0\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); } if (strstr(remote_ep->full_name, "hdmi1")) { - rsnd_ssi_flags_set(ssi, RSND_SSI_HDMI1); + rsnd_flags_set(ssi, RSND_SSI_HDMI1); dev_dbg(dev, "%s[%d] connected to HDMI1\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); } @@ -1042,7 +1047,7 @@ int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - return !!(rsnd_ssi_flags_has(ssi, RSND_SSI_CLK_PIN_SHARE)); + return !!(rsnd_flags_has(ssi, RSND_SSI_CLK_PIN_SHARE)); } static u32 *rsnd_ssi_get_status(struct rsnd_dai_stream *io, @@ -1112,6 +1117,9 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) i = 0; for_each_child_of_node(node, np) { + if (!of_device_is_available(np)) + goto skip; + ssi = rsnd_ssi_get(priv, i); snprintf(name, RSND_SSI_NAME_SIZE, "%s.%d", @@ -1125,10 +1133,10 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) } if (of_get_property(np, "shared-pin", NULL)) - rsnd_ssi_flags_set(ssi, RSND_SSI_CLK_PIN_SHARE); + rsnd_flags_set(ssi, RSND_SSI_CLK_PIN_SHARE); if (of_get_property(np, "no-busif", NULL)) - rsnd_ssi_flags_set(ssi, RSND_SSI_NO_BUSIF); + rsnd_flags_set(ssi, RSND_SSI_NO_BUSIF); ssi->irq = irq_of_parse_and_map(np, 0); if (!ssi->irq) { @@ -1148,7 +1156,7 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) of_node_put(np); goto rsnd_ssi_probe_done; } - +skip: i++; } diff --git a/sound/soc/intel/common/sst-match-acpi.c b/sound/soc/soc-acpi.c index 56d26f36a3cb..f21df28bc28e 100644 --- a/sound/soc/intel/common/sst-match-acpi.c +++ b/sound/soc/soc-acpi.c @@ -1,5 +1,5 @@ /* - * sst_match_apci.c - SST (LPE) match for ACPI enumeration. + * soc-apci.c - support for ACPI enumeration. * * Copyright (c) 2013-15, Intel Corporation. * @@ -14,9 +14,9 @@ * more details. */ -#include "sst-acpi.h" +#include <sound/soc-acpi.h> -static acpi_status sst_acpi_find_name(acpi_handle handle, u32 level, +static acpi_status snd_soc_acpi_find_name(acpi_handle handle, u32 level, void *context, void **ret) { struct acpi_device *adev; @@ -34,12 +34,12 @@ static acpi_status sst_acpi_find_name(acpi_handle handle, u32 level, return AE_OK; } -const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]) +const char *snd_soc_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]) { const char *name = NULL; acpi_status status; - status = acpi_get_devices(hid, sst_acpi_find_name, NULL, + status = acpi_get_devices(hid, snd_soc_acpi_find_name, NULL, (void **)&name); if (ACPI_FAILURE(status) || name[0] == '\0') @@ -47,9 +47,9 @@ const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]) return name; } -EXPORT_SYMBOL_GPL(sst_acpi_find_name_from_hid); +EXPORT_SYMBOL_GPL(snd_soc_acpi_find_name_from_hid); -static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level, +static acpi_status snd_soc_acpi_mach_match(acpi_handle handle, u32 level, void *context, void **ret) { unsigned long long sta; @@ -63,26 +63,27 @@ static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level, return AE_OK; } -bool sst_acpi_check_hid(const u8 hid[ACPI_ID_LEN]) +bool snd_soc_acpi_check_hid(const u8 hid[ACPI_ID_LEN]) { acpi_status status; bool found = false; - status = acpi_get_devices(hid, sst_acpi_mach_match, &found, NULL); + status = acpi_get_devices(hid, snd_soc_acpi_mach_match, &found, NULL); if (ACPI_FAILURE(status)) return false; return found; } -EXPORT_SYMBOL_GPL(sst_acpi_check_hid); +EXPORT_SYMBOL_GPL(snd_soc_acpi_check_hid); -struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines) +struct snd_soc_acpi_mach * +snd_soc_acpi_find_machine(struct snd_soc_acpi_mach *machines) { - struct sst_acpi_mach *mach; + struct snd_soc_acpi_mach *mach; for (mach = machines; mach->id[0]; mach++) { - if (sst_acpi_check_hid(mach->id) == true) { + if (snd_soc_acpi_check_hid(mach->id) == true) { if (mach->machine_quirk == NULL) return mach; @@ -92,14 +93,14 @@ struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines) } return NULL; } -EXPORT_SYMBOL_GPL(sst_acpi_find_machine); +EXPORT_SYMBOL_GPL(snd_soc_acpi_find_machine); -static acpi_status sst_acpi_find_package(acpi_handle handle, u32 level, - void *context, void **ret) +static acpi_status snd_soc_acpi_find_package(acpi_handle handle, u32 level, + void *context, void **ret) { struct acpi_device *adev; acpi_status status = AE_OK; - struct sst_acpi_package_context *pkg_ctx = context; + struct snd_soc_acpi_package_context *pkg_ctx = context; pkg_ctx->data_valid = false; @@ -137,37 +138,38 @@ static acpi_status sst_acpi_find_package(acpi_handle handle, u32 level, return AE_OK; } -bool sst_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN], - struct sst_acpi_package_context *ctx) +bool snd_soc_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN], + struct snd_soc_acpi_package_context *ctx) { acpi_status status; - status = acpi_get_devices(hid, sst_acpi_find_package, ctx, NULL); + status = acpi_get_devices(hid, snd_soc_acpi_find_package, ctx, NULL); if (ACPI_FAILURE(status) || !ctx->data_valid) return false; return true; } -EXPORT_SYMBOL_GPL(sst_acpi_find_package_from_hid); +EXPORT_SYMBOL_GPL(snd_soc_acpi_find_package_from_hid); -struct sst_acpi_mach *sst_acpi_codec_list(void *arg) +struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg) { - struct sst_acpi_mach *mach = arg; - struct sst_codecs *codec_list = (struct sst_codecs *) mach->quirk_data; + struct snd_soc_acpi_mach *mach = arg; + struct snd_soc_acpi_codecs *codec_list = + (struct snd_soc_acpi_codecs *) mach->quirk_data; int i; if (mach->quirk_data == NULL) return mach; for (i = 0; i < codec_list->num_codecs; i++) { - if (sst_acpi_check_hid(codec_list->codecs[i]) != true) + if (snd_soc_acpi_check_hid(codec_list->codecs[i]) != true) return NULL; } return mach; } -EXPORT_SYMBOL_GPL(sst_acpi_codec_list); +EXPORT_SYMBOL_GPL(snd_soc_acpi_codec_list); MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Intel Common ACPI Match module"); +MODULE_DESCRIPTION("ALSA SoC ACPI module"); diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 2cb8d3b55fbc..d9b1e6417fb9 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -30,8 +30,10 @@ static int soc_compr_open(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; + int ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); @@ -44,7 +46,7 @@ static int soc_compr_open(struct snd_compr_stream *cstream) } } - if (platform->driver->compr_ops && platform->driver->compr_ops->open) { + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->open) { ret = platform->driver->compr_ops->open(cstream); if (ret < 0) { pr_err("compress asoc: can't open platform %s\n", @@ -53,6 +55,27 @@ static int soc_compr_open(struct snd_compr_stream *cstream) } } + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->open) + continue; + + __ret = component->driver->compr_ops->open(cstream); + if (__ret < 0) { + pr_err("compress asoc: can't open platform %s\n", + component->name); + ret = __ret; + } + } + if (ret < 0) + goto machine_err; + if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->startup) { ret = rtd->dai_link->compr_ops->startup(cstream); if (ret < 0) { @@ -68,7 +91,21 @@ static int soc_compr_open(struct snd_compr_stream *cstream) return 0; machine_err: - if (platform->driver->compr_ops && platform->driver->compr_ops->free) + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->free) + continue; + + component->driver->compr_ops->free(cstream); + } + + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->free) platform->driver->compr_ops->free(cstream); plat_err: if (cpu_dai->driver->cops && cpu_dai->driver->cops->shutdown) @@ -84,11 +121,13 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) struct snd_pcm_substream *fe_substream = fe->pcm->streams[cstream->direction].substream; struct snd_soc_platform *platform = fe->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = fe->cpu_dai; struct snd_soc_dpcm *dpcm; struct snd_soc_dapm_widget_list *list; int stream; - int ret = 0; + int ret = 0, __ret; if (cstream->direction == SND_COMPRESS_PLAYBACK) stream = SNDRV_PCM_STREAM_PLAYBACK; @@ -107,7 +146,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) } - if (platform->driver->compr_ops && platform->driver->compr_ops->open) { + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->open) { ret = platform->driver->compr_ops->open(cstream); if (ret < 0) { pr_err("compress asoc: can't open platform %s\n", @@ -116,6 +155,27 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) } } + for_each_rtdcom(fe, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->open) + continue; + + __ret = component->driver->compr_ops->open(cstream); + if (__ret < 0) { + pr_err("compress asoc: can't open platform %s\n", + component->name); + ret = __ret; + } + } + if (ret < 0) + goto machine_err; + if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->startup) { ret = fe->dai_link->compr_ops->startup(cstream); if (ret < 0) { @@ -167,7 +227,21 @@ fe_err: if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->shutdown) fe->dai_link->compr_ops->shutdown(cstream); machine_err: - if (platform->driver->compr_ops && platform->driver->compr_ops->free) + for_each_rtdcom(fe, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->free) + continue; + + component->driver->compr_ops->free(cstream); + } + + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->free) platform->driver->compr_ops->free(cstream); plat_err: if (cpu_dai->driver->cops && cpu_dai->driver->cops->shutdown) @@ -210,6 +284,8 @@ static int soc_compr_free(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai = rtd->codec_dai; int stream; @@ -235,7 +311,21 @@ static int soc_compr_free(struct snd_compr_stream *cstream) if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->shutdown) rtd->dai_link->compr_ops->shutdown(cstream); - if (platform->driver->compr_ops && platform->driver->compr_ops->free) + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->free) + continue; + + component->driver->compr_ops->free(cstream); + } + + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->free) platform->driver->compr_ops->free(cstream); if (cpu_dai->driver->cops && cpu_dai->driver->cops->shutdown) @@ -267,6 +357,8 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *fe = cstream->private_data; struct snd_soc_platform *platform = fe->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = fe->cpu_dai; struct snd_soc_dpcm *dpcm; int stream, ret; @@ -304,9 +396,23 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->shutdown) fe->dai_link->compr_ops->shutdown(cstream); - if (platform->driver->compr_ops && platform->driver->compr_ops->free) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->free) platform->driver->compr_ops->free(cstream); + for_each_rtdcom(fe, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->free) + continue; + + component->driver->compr_ops->free(cstream); + } + if (cpu_dai->driver->cops && cpu_dai->driver->cops->shutdown) cpu_dai->driver->cops->shutdown(cstream, cpu_dai); @@ -319,18 +425,38 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; + int ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) { + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->trigger) { ret = platform->driver->compr_ops->trigger(cstream, cmd); if (ret < 0) goto out; } + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->trigger) + continue; + + __ret = component->driver->compr_ops->trigger(cstream, cmd); + if (__ret < 0) + ret = __ret; + } + if (ret < 0) + goto out; + if (cpu_dai->driver->cops && cpu_dai->driver->cops->trigger) cpu_dai->driver->cops->trigger(cstream, cmd, cpu_dai); @@ -353,16 +479,36 @@ static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) { struct snd_soc_pcm_runtime *fe = cstream->private_data; struct snd_soc_platform *platform = fe->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = fe->cpu_dai; - int ret = 0, stream; + int ret = 0, __ret, stream; if (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN || cmd == SND_COMPR_TRIGGER_DRAIN) { - if (platform->driver->compr_ops && + if (platform && + platform->driver->compr_ops && platform->driver->compr_ops->trigger) return platform->driver->compr_ops->trigger(cstream, cmd); + + for_each_rtdcom(fe, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->trigger) + continue; + + __ret = component->driver->compr_ops->trigger(cstream, cmd); + if (__ret < 0) + ret = __ret; + } + return ret; } if (cstream->direction == SND_COMPRESS_PLAYBACK) @@ -379,12 +525,30 @@ static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) goto out; } - if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) { + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->trigger) { ret = platform->driver->compr_ops->trigger(cstream, cmd); if (ret < 0) goto out; } + for_each_rtdcom(fe, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->trigger) + continue; + + __ret = component->driver->compr_ops->trigger(cstream, cmd); + if (__ret < 0) + ret = __ret; + } + if (ret < 0) + goto out; + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; ret = dpcm_be_dai_trigger(fe, stream, cmd); @@ -415,8 +579,10 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; + int ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); @@ -432,12 +598,30 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, goto err; } - if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) { + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->set_params) { ret = platform->driver->compr_ops->set_params(cstream, params); if (ret < 0) goto err; } + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->set_params) + continue; + + __ret = component->driver->compr_ops->set_params(cstream, params); + if (__ret < 0) + ret = __ret; + } + if (ret < 0) + goto err; + if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->set_params) { ret = rtd->dai_link->compr_ops->set_params(cstream); if (ret < 0) @@ -471,8 +655,10 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, struct snd_pcm_substream *fe_substream = fe->pcm->streams[cstream->direction].substream; struct snd_soc_platform *platform = fe->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = fe->cpu_dai; - int ret = 0, stream; + int ret = 0, __ret, stream; if (cstream->direction == SND_COMPRESS_PLAYBACK) stream = SNDRV_PCM_STREAM_PLAYBACK; @@ -487,12 +673,30 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, goto out; } - if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) { + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->set_params) { ret = platform->driver->compr_ops->set_params(cstream, params); if (ret < 0) goto out; } + for_each_rtdcom(fe, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->set_params) + continue; + + __ret = component->driver->compr_ops->set_params(cstream, params); + if (__ret < 0) + ret = __ret; + } + if (ret < 0) + goto out; + if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->set_params) { ret = fe->dai_link->compr_ops->set_params(cstream); if (ret < 0) @@ -531,8 +735,10 @@ static int soc_compr_get_params(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; + int ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); @@ -542,8 +748,27 @@ static int soc_compr_get_params(struct snd_compr_stream *cstream, goto err; } - if (platform->driver->compr_ops && platform->driver->compr_ops->get_params) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->get_params) { ret = platform->driver->compr_ops->get_params(cstream, params); + if (ret < 0) + goto err; + } + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->get_params) + continue; + + __ret = component->driver->compr_ops->get_params(cstream, params); + if (__ret < 0) + ret = __ret; + } err: mutex_unlock(&rtd->pcm_mutex); @@ -555,13 +780,35 @@ static int soc_compr_get_caps(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; - int ret = 0; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; + int ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - if (platform->driver->compr_ops && platform->driver->compr_ops->get_caps) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->get_caps) { ret = platform->driver->compr_ops->get_caps(cstream, caps); + if (ret < 0) + goto err; + } + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + if (!component->driver->compr_ops || + !component->driver->compr_ops->get_caps) + continue; + + __ret = component->driver->compr_ops->get_caps(cstream, caps); + if (__ret < 0) + ret = __ret; + } + +err: mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -571,13 +818,35 @@ static int soc_compr_get_codec_caps(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; - int ret = 0; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; + int ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - if (platform->driver->compr_ops && platform->driver->compr_ops->get_codec_caps) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->get_codec_caps) { ret = platform->driver->compr_ops->get_codec_caps(cstream, codec); + if (ret < 0) + goto err; + } + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + if (!component->driver->compr_ops || + !component->driver->compr_ops->get_codec_caps) + continue; + + __ret = component->driver->compr_ops->get_codec_caps(cstream, codec); + if (__ret < 0) + ret = __ret; + } + +err: mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -586,8 +855,10 @@ static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; + int ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); @@ -597,8 +868,27 @@ static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes) goto err; } - if (platform->driver->compr_ops && platform->driver->compr_ops->ack) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->ack) { ret = platform->driver->compr_ops->ack(cstream, bytes); + if (ret < 0) + goto err; + } + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->ack) + continue; + + __ret = component->driver->compr_ops->ack(cstream, bytes); + if (__ret < 0) + ret = __ret; + } err: mutex_unlock(&rtd->pcm_mutex); @@ -610,7 +900,9 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; - int ret = 0; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; + int ret = 0, __ret; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); @@ -618,9 +910,29 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream, if (cpu_dai->driver->cops && cpu_dai->driver->cops->pointer) cpu_dai->driver->cops->pointer(cstream, tstamp, cpu_dai); - if (platform->driver->compr_ops && platform->driver->compr_ops->pointer) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->pointer) { ret = platform->driver->compr_ops->pointer(cstream, tstamp); + if (ret < 0) + goto err; + } + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->pointer) + continue; + __ret = component->driver->compr_ops->pointer(cstream, tstamp); + if (__ret < 0) + ret = __ret; + } + +err: mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -630,13 +942,34 @@ static int soc_compr_copy(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; - int ret = 0; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; + int ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - if (platform->driver->compr_ops && platform->driver->compr_ops->copy) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->copy) { ret = platform->driver->compr_ops->copy(cstream, buf, count); + if (ret < 0) + goto err; + } + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->copy) + continue; + __ret = component->driver->compr_ops->copy(cstream, buf, count); + if (__ret < 0) + ret = __ret; + } +err: mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -646,8 +979,10 @@ static int soc_compr_set_metadata(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; + int ret = 0, __ret; if (cpu_dai->driver->cops && cpu_dai->driver->cops->set_metadata) { ret = cpu_dai->driver->cops->set_metadata(cstream, metadata, cpu_dai); @@ -655,8 +990,27 @@ static int soc_compr_set_metadata(struct snd_compr_stream *cstream, return ret; } - if (platform->driver->compr_ops && platform->driver->compr_ops->set_metadata) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->set_metadata) { ret = platform->driver->compr_ops->set_metadata(cstream, metadata); + if (ret < 0) + return ret; + } + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->set_metadata) + continue; + + __ret = component->driver->compr_ops->set_metadata(cstream, metadata); + if (__ret < 0) + ret = __ret; + } return ret; } @@ -666,8 +1020,10 @@ static int soc_compr_get_metadata(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; + int ret = 0, __ret; if (cpu_dai->driver->cops && cpu_dai->driver->cops->get_metadata) { ret = cpu_dai->driver->cops->get_metadata(cstream, metadata, cpu_dai); @@ -675,8 +1031,27 @@ static int soc_compr_get_metadata(struct snd_compr_stream *cstream, return ret; } - if (platform->driver->compr_ops && platform->driver->compr_ops->get_metadata) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->get_metadata) { ret = platform->driver->compr_ops->get_metadata(cstream, metadata); + if (ret < 0) + return ret; + } + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->get_metadata) + continue; + + __ret = component->driver->compr_ops->get_metadata(cstream, metadata); + if (__ret < 0) + ret = __ret; + } return ret; } @@ -723,6 +1098,8 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) { struct snd_soc_codec *codec = rtd->codec; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_compr *compr; @@ -798,9 +1175,25 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops)); } + /* Add copy callback for not memory mapped DSPs */ - if (platform->driver->compr_ops && platform->driver->compr_ops->copy) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->copy) + compr->ops->copy = soc_compr_copy; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->copy) + continue; + compr->ops->copy = soc_compr_copy; + } + mutex_init(&compr->lock); ret = snd_compress_new(rtd->card->snd_card, num, direction, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index fee4b0ef5566..c0edac80df34 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -614,6 +614,8 @@ struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(snd_soc_get_dai_substream); +static const struct snd_soc_ops null_snd_soc_ops; + static struct snd_soc_pcm_runtime *soc_new_pcm_runtime( struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) { @@ -626,6 +628,9 @@ static struct snd_soc_pcm_runtime *soc_new_pcm_runtime( INIT_LIST_HEAD(&rtd->component_list); rtd->card = card; rtd->dai_link = dai_link; + if (!rtd->dai_link->ops) + rtd->dai_link->ops = &null_snd_soc_ops; + rtd->codec_dais = kzalloc(sizeof(struct snd_soc_dai *) * dai_link->num_codecs, GFP_KERNEL); @@ -639,8 +644,7 @@ static struct snd_soc_pcm_runtime *soc_new_pcm_runtime( static void soc_free_pcm_runtime(struct snd_soc_pcm_runtime *rtd) { - if (rtd && rtd->codec_dais) - kfree(rtd->codec_dais); + kfree(rtd->codec_dais); snd_soc_rtdcom_del_all(rtd); kfree(rtd); } @@ -2632,7 +2636,7 @@ EXPORT_SYMBOL_GPL(snd_soc_add_dai_controls); int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { - if (dai->driver && dai->driver->ops->set_sysclk) + if (dai->driver->ops->set_sysclk) return dai->driver->ops->set_sysclk(dai, clk_id, freq, dir); return snd_soc_component_set_sysclk(dai->component, clk_id, 0, @@ -2700,7 +2704,7 @@ EXPORT_SYMBOL_GPL(snd_soc_component_set_sysclk); int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div) { - if (dai->driver && dai->driver->ops->set_clkdiv) + if (dai->driver->ops->set_clkdiv) return dai->driver->ops->set_clkdiv(dai, div_id, div); else return -EINVAL; @@ -2720,7 +2724,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv); int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, unsigned int freq_in, unsigned int freq_out) { - if (dai->driver && dai->driver->ops->set_pll) + if (dai->driver->ops->set_pll) return dai->driver->ops->set_pll(dai, pll_id, source, freq_in, freq_out); @@ -2786,7 +2790,7 @@ EXPORT_SYMBOL_GPL(snd_soc_component_set_pll); */ int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) { - if (dai->driver && dai->driver->ops->set_bclk_ratio) + if (dai->driver->ops->set_bclk_ratio) return dai->driver->ops->set_bclk_ratio(dai, ratio); else return -EINVAL; @@ -2796,7 +2800,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_bclk_ratio); /** * snd_soc_dai_set_fmt - configure DAI hardware audio format. * @dai: DAI - * @fmt: SND_SOC_DAIFMT_ format value. + * @fmt: SND_SOC_DAIFMT_* format value. * * Configures the DAI hardware format and clocking. */ @@ -2860,7 +2864,7 @@ static int snd_soc_xlate_tdm_slot_mask(unsigned int slots, int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) { - if (dai->driver && dai->driver->ops->xlate_tdm_slot_mask) + if (dai->driver->ops->xlate_tdm_slot_mask) dai->driver->ops->xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask); else @@ -2869,7 +2873,7 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, dai->tx_mask = tx_mask; dai->rx_mask = rx_mask; - if (dai->driver && dai->driver->ops->set_tdm_slot) + if (dai->driver->ops->set_tdm_slot) return dai->driver->ops->set_tdm_slot(dai, tx_mask, rx_mask, slots, slot_width); else @@ -2893,7 +2897,7 @@ int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_num, unsigned int *tx_slot, unsigned int rx_num, unsigned int *rx_slot) { - if (dai->driver && dai->driver->ops->set_channel_map) + if (dai->driver->ops->set_channel_map) return dai->driver->ops->set_channel_map(dai, tx_num, tx_slot, rx_num, rx_slot); else @@ -2910,7 +2914,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_channel_map); */ int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate) { - if (dai->driver && dai->driver->ops->set_tristate) + if (dai->driver->ops->set_tristate) return dai->driver->ops->set_tristate(dai, tristate); else return -EINVAL; @@ -3250,6 +3254,30 @@ static int snd_soc_component_stream_event(struct snd_soc_dapm_context *dapm, return component->driver->stream_event(component, event); } +static int snd_soc_component_drv_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + if (component->driver->pcm_new) + return component->driver->pcm_new(rtd); + + return 0; +} + +static void snd_soc_component_drv_pcm_free(struct snd_soc_component *component, + struct snd_pcm *pcm) +{ + if (component->driver->pcm_free) + component->driver->pcm_free(pcm); +} + +static int snd_soc_component_set_bias_level(struct snd_soc_dapm_context *dapm, + enum snd_soc_bias_level level) +{ + struct snd_soc_component *component = dapm->component; + + return component->driver->set_bias_level(component, level); +} + static int snd_soc_component_initialize(struct snd_soc_component *component, const struct snd_soc_component_driver *driver, struct device *dev) { @@ -3270,16 +3298,21 @@ static int snd_soc_component_initialize(struct snd_soc_component *component, component->set_sysclk = component->driver->set_sysclk; component->set_pll = component->driver->set_pll; component->set_jack = component->driver->set_jack; + component->pcm_new = snd_soc_component_drv_pcm_new; + component->pcm_free = snd_soc_component_drv_pcm_free; dapm = snd_soc_component_get_dapm(component); dapm->dev = dev; dapm->component = component; dapm->bias_level = SND_SOC_BIAS_OFF; - dapm->idle_bias_off = true; + dapm->idle_bias_off = !driver->idle_bias_on; + dapm->suspend_bias_off = driver->suspend_bias_off; if (driver->seq_notifier) dapm->seq_notifier = snd_soc_component_seq_notifier; if (driver->stream_event) dapm->stream_event = snd_soc_component_stream_event; + if (driver->set_bias_level) + dapm->set_bias_level = snd_soc_component_set_bias_level; INIT_LIST_HEAD(&component->dai_list); mutex_init(&component->io_mutex); @@ -3371,19 +3404,49 @@ static void snd_soc_component_del_unlocked(struct snd_soc_component *component) list_del(&component->list); } -int snd_soc_register_component(struct device *dev, - const struct snd_soc_component_driver *component_driver, - struct snd_soc_dai_driver *dai_drv, - int num_dai) +#define ENDIANNESS_MAP(name) \ + (SNDRV_PCM_FMTBIT_##name##LE | SNDRV_PCM_FMTBIT_##name##BE) +static u64 endianness_format_map[] = { + ENDIANNESS_MAP(S16_), + ENDIANNESS_MAP(U16_), + ENDIANNESS_MAP(S24_), + ENDIANNESS_MAP(U24_), + ENDIANNESS_MAP(S32_), + ENDIANNESS_MAP(U32_), + ENDIANNESS_MAP(S24_3), + ENDIANNESS_MAP(U24_3), + ENDIANNESS_MAP(S20_3), + ENDIANNESS_MAP(U20_3), + ENDIANNESS_MAP(S18_3), + ENDIANNESS_MAP(U18_3), + ENDIANNESS_MAP(FLOAT_), + ENDIANNESS_MAP(FLOAT64_), + ENDIANNESS_MAP(IEC958_SUBFRAME_), +}; + +/* + * Fix up the DAI formats for endianness: codecs don't actually see + * the endianness of the data but we're using the CPU format + * definitions which do need to include endianness so we ensure that + * codec DAIs always have both big and little endian variants set. + */ +static void convert_endianness_formats(struct snd_soc_pcm_stream *stream) { - struct snd_soc_component *component; - int ret; + int i; - component = kzalloc(sizeof(*component), GFP_KERNEL); - if (!component) { - dev_err(dev, "ASoC: Failed to allocate memory\n"); - return -ENOMEM; - } + for (i = 0; i < ARRAY_SIZE(endianness_format_map); i++) + if (stream->formats & endianness_format_map[i]) + stream->formats |= endianness_format_map[i]; +} + +int snd_soc_add_component(struct device *dev, + struct snd_soc_component *component, + const struct snd_soc_component_driver *component_driver, + struct snd_soc_dai_driver *dai_drv, + int num_dai) +{ + int ret; + int i; ret = snd_soc_component_initialize(component, component_driver, dev); if (ret) @@ -3392,7 +3455,15 @@ int snd_soc_register_component(struct device *dev, component->ignore_pmdown_time = true; component->registered_as_component = true; - ret = snd_soc_register_dais(component, dai_drv, num_dai, true); + if (component_driver->endianness) { + for (i = 0; i < num_dai; i++) { + convert_endianness_formats(&dai_drv[i].playback); + convert_endianness_formats(&dai_drv[i].capture); + } + } + + ret = snd_soc_register_dais(component, dai_drv, num_dai, + !component_driver->non_legacy_dai_naming); if (ret < 0) { dev_err(dev, "ASoC: Failed to register DAIs: %d\n", ret); goto err_cleanup; @@ -3408,6 +3479,22 @@ err_free: kfree(component); return ret; } +EXPORT_SYMBOL_GPL(snd_soc_add_component); + +int snd_soc_register_component(struct device *dev, + const struct snd_soc_component_driver *component_driver, + struct snd_soc_dai_driver *dai_drv, + int num_dai) +{ + struct snd_soc_component *component; + + component = kzalloc(sizeof(*component), GFP_KERNEL); + if (!component) + return -ENOMEM; + + return snd_soc_add_component(dev, component, component_driver, + dai_drv, num_dai); +} EXPORT_SYMBOL_GPL(snd_soc_register_component); /** @@ -3448,6 +3535,32 @@ void snd_soc_unregister_component(struct device *dev) } EXPORT_SYMBOL_GPL(snd_soc_unregister_component); +struct snd_soc_component *snd_soc_lookup_component(struct device *dev, + const char *driver_name) +{ + struct snd_soc_component *component; + struct snd_soc_component *ret; + + ret = NULL; + mutex_lock(&client_mutex); + list_for_each_entry(component, &component_list, list) { + if (dev != component->dev) + continue; + + if (driver_name && + (driver_name != component->driver->name) && + (strcmp(component->driver->name, driver_name) != 0)) + continue; + + ret = component; + break; + } + mutex_unlock(&client_mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_lookup_component); + static int snd_soc_platform_drv_probe(struct snd_soc_component *component) { struct snd_soc_platform *platform = snd_soc_component_to_platform(component); @@ -3462,6 +3575,26 @@ static void snd_soc_platform_drv_remove(struct snd_soc_component *component) platform->driver->remove(platform); } +static int snd_soc_platform_drv_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_platform *platform = snd_soc_component_to_platform(component); + + if (platform->driver->pcm_new) + return platform->driver->pcm_new(rtd); + + return 0; +} + +static void snd_soc_platform_drv_pcm_free(struct snd_soc_component *component, + struct snd_pcm *pcm) +{ + struct snd_soc_platform *platform = snd_soc_component_to_platform(component); + + if (platform->driver->pcm_free) + platform->driver->pcm_free(pcm); +} + /** * snd_soc_add_platform - Add a platform to the ASoC core * @dev: The parent device for the platform @@ -3485,6 +3618,10 @@ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, platform->component.probe = snd_soc_platform_drv_probe; if (platform_drv->remove) platform->component.remove = snd_soc_platform_drv_remove; + if (platform_drv->pcm_new) + platform->component.pcm_new = snd_soc_platform_drv_pcm_new; + if (platform_drv->pcm_free) + platform->component.pcm_free = snd_soc_platform_drv_pcm_free; #ifdef CONFIG_DEBUG_FS platform->component.debugfs_prefix = "platform"; @@ -3582,39 +3719,6 @@ void snd_soc_unregister_platform(struct device *dev) } EXPORT_SYMBOL_GPL(snd_soc_unregister_platform); -static u64 codec_format_map[] = { - SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE, - SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE, - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE, - SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE, - SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE, - SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE, - SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3BE, - SNDRV_PCM_FMTBIT_U24_3LE | SNDRV_PCM_FMTBIT_U24_3BE, - SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE, - SNDRV_PCM_FMTBIT_U20_3LE | SNDRV_PCM_FMTBIT_U20_3BE, - SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE, - SNDRV_PCM_FMTBIT_U18_3LE | SNDRV_PCM_FMTBIT_U18_3BE, - SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE, - SNDRV_PCM_FMTBIT_FLOAT64_LE | SNDRV_PCM_FMTBIT_FLOAT64_BE, - SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE - | SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE, -}; - -/* Fix up the DAI formats for endianness: codecs don't actually see - * the endianness of the data but we're using the CPU format - * definitions which do need to include endianness so we ensure that - * codec DAIs always have both big and little endian variants set. - */ -static void fixup_codec_formats(struct snd_soc_pcm_stream *stream) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(codec_format_map); i++) - if (stream->formats & codec_format_map[i]) - stream->formats |= codec_format_map[i]; -} - static int snd_soc_codec_drv_probe(struct snd_soc_component *component) { struct snd_soc_codec *codec = snd_soc_component_to_codec(component); @@ -3765,8 +3869,8 @@ int snd_soc_register_codec(struct device *dev, codec->component.regmap = codec_drv->get_regmap(dev); for (i = 0; i < num_dai; i++) { - fixup_codec_formats(&dai_drv[i].playback); - fixup_codec_formats(&dai_drv[i].capture); + convert_endianness_formats(&dai_drv[i].playback); + convert_endianness_formats(&dai_drv[i].capture); } ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index dcef67a9bd48..a10b21cfc31e 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2884,7 +2884,7 @@ int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, { int i, r, ret = 0; - mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); for (i = 0; i < num; i++) { r = snd_soc_dapm_add_route(dapm, route); if (r < 0) { @@ -2915,7 +2915,7 @@ int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm, { int i; - mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); + mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); for (i = 0; i < num; i++) { snd_soc_dapm_del_route(dapm, route); route++; @@ -3681,7 +3681,7 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: substream.stream = SNDRV_PCM_STREAM_CAPTURE; - if (source->driver->ops && source->driver->ops->startup) { + if (source->driver->ops->startup) { ret = source->driver->ops->startup(&substream, source); if (ret < 0) { dev_err(source->dev, @@ -3695,7 +3695,7 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, goto out; substream.stream = SNDRV_PCM_STREAM_PLAYBACK; - if (sink->driver->ops && sink->driver->ops->startup) { + if (sink->driver->ops->startup) { ret = sink->driver->ops->startup(&substream, sink); if (ret < 0) { dev_err(sink->dev, @@ -3725,13 +3725,13 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, ret = 0; source->active--; - if (source->driver->ops && source->driver->ops->shutdown) { + if (source->driver->ops->shutdown) { substream.stream = SNDRV_PCM_STREAM_CAPTURE; source->driver->ops->shutdown(&substream, source); } sink->active--; - if (sink->driver->ops && sink->driver->ops->shutdown) { + if (sink->driver->ops->shutdown) { substream.stream = SNDRV_PCM_STREAM_PLAYBACK; sink->driver->ops->shutdown(&substream, sink); } @@ -3778,18 +3778,27 @@ static int snd_soc_dapm_dai_link_put(struct snd_kcontrol *kcontrol, return 0; } -int snd_soc_dapm_new_pcm(struct snd_soc_card *card, - const struct snd_soc_pcm_stream *params, - unsigned int num_params, - struct snd_soc_dapm_widget *source, - struct snd_soc_dapm_widget *sink) +static void +snd_soc_dapm_free_kcontrol(struct snd_soc_card *card, + unsigned long *private_value, + int num_params, + const char **w_param_text) +{ + int count; + + devm_kfree(card->dev, (void *)*private_value); + for (count = 0 ; count < num_params; count++) + devm_kfree(card->dev, (void *)w_param_text[count]); + devm_kfree(card->dev, w_param_text); +} + +static struct snd_kcontrol_new * +snd_soc_dapm_alloc_kcontrol(struct snd_soc_card *card, + char *link_name, + const struct snd_soc_pcm_stream *params, + int num_params, const char **w_param_text, + unsigned long *private_value) { - struct snd_soc_dapm_widget template; - struct snd_soc_dapm_widget *w; - char *link_name; - int ret, count; - unsigned long private_value; - const char **w_param_text; struct soc_enum w_param_enum[] = { SOC_ENUM_SINGLE(0, 0, 0, NULL), }; @@ -3798,19 +3807,9 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card, snd_soc_dapm_dai_link_get, snd_soc_dapm_dai_link_put), }; + struct snd_kcontrol_new *kcontrol_news; const struct snd_soc_pcm_stream *config = params; - - w_param_text = devm_kcalloc(card->dev, num_params, - sizeof(char *), GFP_KERNEL); - if (!w_param_text) - return -ENOMEM; - - link_name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-%s", - source->name, sink->name); - if (!link_name) { - ret = -ENOMEM; - goto outfree_w_param; - } + int count; for (count = 0 ; count < num_params; count++) { if (!config->stream_name) { @@ -3821,57 +3820,94 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card, devm_kasprintf(card->dev, GFP_KERNEL, "Anonymous Configuration %d", count); - if (!w_param_text[count]) { - ret = -ENOMEM; - goto outfree_link_name; - } } else { w_param_text[count] = devm_kmemdup(card->dev, config->stream_name, strlen(config->stream_name) + 1, GFP_KERNEL); - if (!w_param_text[count]) { - ret = -ENOMEM; - goto outfree_link_name; - } } + if (!w_param_text[count]) + goto outfree_w_param; config++; } + w_param_enum[0].items = num_params; w_param_enum[0].texts = w_param_text; - memset(&template, 0, sizeof(template)); - template.reg = SND_SOC_NOPM; - template.id = snd_soc_dapm_dai_link; - template.name = link_name; - template.event = snd_soc_dai_link_event; - template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD; - template.num_kcontrols = 1; - /* duplicate w_param_enum on heap so that memory persists */ - private_value = + *private_value = (unsigned long) devm_kmemdup(card->dev, (void *)(kcontrol_dai_link[0].private_value), sizeof(struct soc_enum), GFP_KERNEL); - if (!private_value) { + if (!*private_value) { dev_err(card->dev, "ASoC: Failed to create control for %s widget\n", link_name); - ret = -ENOMEM; - goto outfree_link_name; + goto outfree_w_param; } - kcontrol_dai_link[0].private_value = private_value; + kcontrol_dai_link[0].private_value = *private_value; /* duplicate kcontrol_dai_link on heap so that memory persists */ - template.kcontrol_news = - devm_kmemdup(card->dev, &kcontrol_dai_link[0], + kcontrol_news = devm_kmemdup(card->dev, &kcontrol_dai_link[0], sizeof(struct snd_kcontrol_new), GFP_KERNEL); - if (!template.kcontrol_news) { + if (!kcontrol_news) { dev_err(card->dev, "ASoC: Failed to create control for %s widget\n", link_name); - ret = -ENOMEM; - goto outfree_private_value; + goto outfree_w_param; } + return kcontrol_news; + +outfree_w_param: + snd_soc_dapm_free_kcontrol(card, private_value, num_params, w_param_text); + return NULL; +} + +int snd_soc_dapm_new_pcm(struct snd_soc_card *card, + const struct snd_soc_pcm_stream *params, + unsigned int num_params, + struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_dapm_widget template; + struct snd_soc_dapm_widget *w; + const char **w_param_text; + unsigned long private_value; + char *link_name; + int ret; + + link_name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-%s", + source->name, sink->name); + if (!link_name) + return -ENOMEM; + + memset(&template, 0, sizeof(template)); + template.reg = SND_SOC_NOPM; + template.id = snd_soc_dapm_dai_link; + template.name = link_name; + template.event = snd_soc_dai_link_event; + template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD; + template.kcontrol_news = NULL; + + /* allocate memory for control, only in case of multiple configs */ + if (num_params > 1) { + w_param_text = devm_kcalloc(card->dev, num_params, + sizeof(char *), GFP_KERNEL); + if (!w_param_text) { + ret = -ENOMEM; + goto param_fail; + } + template.num_kcontrols = 1; + template.kcontrol_news = + snd_soc_dapm_alloc_kcontrol(card, + link_name, params, num_params, + w_param_text, &private_value); + if (!template.kcontrol_news) { + ret = -ENOMEM; + goto param_fail; + } + } else { + w_param_text = NULL; + } dev_dbg(card->dev, "ASoC: adding %s widget\n", link_name); w = snd_soc_dapm_new_control_unlocked(&card->dapm, &template); @@ -3903,15 +3939,9 @@ outfree_w: devm_kfree(card->dev, w); outfree_kcontrol_news: devm_kfree(card->dev, (void *)template.kcontrol_news); -outfree_private_value: - devm_kfree(card->dev, (void *)private_value); -outfree_link_name: + snd_soc_dapm_free_kcontrol(card, &private_value, num_params, w_param_text); +param_fail: devm_kfree(card->dev, link_name); -outfree_w_param: - for (count = 0 ; count < num_params; count++) - devm_kfree(card->dev, (void *)w_param_text[count]); - devm_kfree(card->dev, w_param_text); - return ret; } diff --git a/sound/soc/soc-io.c b/sound/soc/soc-io.c index 9b3939049cef..20340ade20a7 100644 --- a/sound/soc/soc-io.c +++ b/sound/soc/soc-io.c @@ -41,6 +41,20 @@ int snd_soc_component_read(struct snd_soc_component *component, } EXPORT_SYMBOL_GPL(snd_soc_component_read); +unsigned int snd_soc_component_read32(struct snd_soc_component *component, + unsigned int reg) +{ + unsigned int val; + int ret; + + ret = snd_soc_component_read(component, reg, &val); + if (ret < 0) + return -1; + + return val; +} +EXPORT_SYMBOL_GPL(snd_soc_component_read32); + /** * snd_soc_component_write() - Write register value * @component: Component to write to diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 94b88b897c3b..8075856668c2 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -133,16 +133,25 @@ void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream) */ bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd) { + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; int i; bool ignore = true; if (!rtd->pmdown_time || rtd->dai_link->ignore_pmdown_time) return true; + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + ignore &= !component->driver->pmdown_time; + } + + /* this will be removed */ for (i = 0; i < rtd->num_codecs; i++) ignore &= rtd->codec_dais[i]->component->ignore_pmdown_time; - return rtd->cpu_dai->component->ignore_pmdown_time && ignore; + return ignore; } /** @@ -459,7 +468,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai; const char *codec_dai_name = "multicodec"; - int i, ret = 0; + int i, ret = 0, __ret; pinctrl_pm_select_default_state(cpu_dai->dev); for (i = 0; i < rtd->num_codecs; i++) @@ -474,7 +483,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); /* startup the audio subsystem */ - if (cpu_dai->driver->ops && cpu_dai->driver->ops->startup) { + if (cpu_dai->driver->ops->startup) { ret = cpu_dai->driver->ops->startup(substream, cpu_dai); if (ret < 0) { dev_err(cpu_dai->dev, "ASoC: can't open interface" @@ -483,7 +492,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) } } - if (platform->driver->ops && platform->driver->ops->open) { + if (platform && platform->driver->ops && platform->driver->ops->open) { ret = platform->driver->ops->open(substream); if (ret < 0) { dev_err(platform->dev, "ASoC: can't open platform" @@ -492,9 +501,32 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) } } + ret = 0; + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->open) + continue; + + __ret = component->driver->ops->open(substream); + if (__ret < 0) { + dev_err(component->dev, + "ASoC: can't open component %s: %d\n", + component->name, ret); + ret = __ret; + } + } + if (ret < 0) + goto component_err; + for (i = 0; i < rtd->num_codecs; i++) { codec_dai = rtd->codec_dais[i]; - if (codec_dai->driver->ops && codec_dai->driver->ops->startup) { + if (codec_dai->driver->ops->startup) { ret = codec_dai->driver->ops->startup(substream, codec_dai); if (ret < 0) { @@ -511,7 +543,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) codec_dai->rx_mask = 0; } - if (rtd->dai_link->ops && rtd->dai_link->ops->startup) { + if (rtd->dai_link->ops->startup) { ret = rtd->dai_link->ops->startup(substream); if (ret < 0) { pr_err("ASoC: %s startup failed: %d\n", @@ -585,7 +617,7 @@ dynamic: return 0; config_err: - if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown) + if (rtd->dai_link->ops->shutdown) rtd->dai_link->ops->shutdown(substream); machine_err: @@ -598,7 +630,22 @@ codec_dai_err: codec_dai->driver->ops->shutdown(substream, codec_dai); } - if (platform->driver->ops && platform->driver->ops->close) +component_err: + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->close) + continue; + + component->driver->ops->close(substream); + } + + if (platform && platform->driver->ops && platform->driver->ops->close) platform->driver->ops->close(substream); platform_err: @@ -692,12 +739,26 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) codec_dai->driver->ops->shutdown(substream, codec_dai); } - if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown) + if (rtd->dai_link->ops->shutdown) rtd->dai_link->ops->shutdown(substream); - if (platform->driver->ops && platform->driver->ops->close) + if (platform && platform->driver->ops && platform->driver->ops->close) platform->driver->ops->close(substream); + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->close) + continue; + + component->driver->ops->close(substream); + } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (snd_soc_runtime_ignore_pmdown_time(rtd)) { /* powered down playback stream now */ @@ -745,13 +806,15 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai; int i, ret = 0; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - if (rtd->dai_link->ops && rtd->dai_link->ops->prepare) { + if (rtd->dai_link->ops->prepare) { ret = rtd->dai_link->ops->prepare(substream); if (ret < 0) { dev_err(rtd->card->dev, "ASoC: machine prepare error:" @@ -760,7 +823,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) } } - if (platform->driver->ops && platform->driver->ops->prepare) { + if (platform && platform->driver->ops && platform->driver->ops->prepare) { ret = platform->driver->ops->prepare(substream); if (ret < 0) { dev_err(platform->dev, "ASoC: platform prepare error:" @@ -769,9 +832,28 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) } } + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->prepare) + continue; + + ret = component->driver->ops->prepare(substream); + if (ret < 0) { + dev_err(component->dev, + "ASoC: platform prepare error: %d\n", ret); + goto out; + } + } + for (i = 0; i < rtd->num_codecs; i++) { codec_dai = rtd->codec_dais[i]; - if (codec_dai->driver->ops && codec_dai->driver->ops->prepare) { + if (codec_dai->driver->ops->prepare) { ret = codec_dai->driver->ops->prepare(substream, codec_dai); if (ret < 0) { @@ -783,7 +865,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) } } - if (cpu_dai->driver->ops && cpu_dai->driver->ops->prepare) { + if (cpu_dai->driver->ops->prepare) { ret = cpu_dai->driver->ops->prepare(substream, cpu_dai); if (ret < 0) { dev_err(cpu_dai->dev, @@ -829,7 +911,7 @@ int soc_dai_hw_params(struct snd_pcm_substream *substream, { int ret; - if (dai->driver->ops && dai->driver->ops->hw_params) { + if (dai->driver->ops->hw_params) { ret = dai->driver->ops->hw_params(substream, params, dai); if (ret < 0) { dev_err(dai->dev, "ASoC: can't set %s hw params: %d\n", @@ -851,16 +933,13 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int i, ret = 0; + int i, ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - - ret = soc_pcm_params_symmetry(substream, params); - if (ret) - goto out; - - if (rtd->dai_link->ops && rtd->dai_link->ops->hw_params) { + if (rtd->dai_link->ops->hw_params) { ret = rtd->dai_link->ops->hw_params(substream, params); if (ret < 0) { dev_err(rtd->card->dev, "ASoC: machine hw_params" @@ -915,7 +994,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, if (ret < 0) goto interface_err; - if (platform->driver->ops && platform->driver->ops->hw_params) { + if (platform && platform->driver->ops && platform->driver->ops->hw_params) { ret = platform->driver->ops->hw_params(substream, params); if (ret < 0) { dev_err(platform->dev, "ASoC: %s hw params failed: %d\n", @@ -924,18 +1003,62 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, } } + ret = 0; + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->hw_params) + continue; + + __ret = component->driver->ops->hw_params(substream, params); + if (__ret < 0) { + dev_err(component->dev, + "ASoC: %s hw params failed: %d\n", + component->name, ret); + ret = __ret; + } + } + if (ret < 0) + goto component_err; + /* store the parameters for each DAIs */ cpu_dai->rate = params_rate(params); cpu_dai->channels = params_channels(params); cpu_dai->sample_bits = snd_pcm_format_physical_width(params_format(params)); + ret = soc_pcm_params_symmetry(substream, params); + if (ret) + goto component_err; out: mutex_unlock(&rtd->pcm_mutex); return ret; +component_err: + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->hw_free) + continue; + + component->driver->ops->hw_free(substream); + } + + if (platform && platform->driver->ops && platform->driver->ops->hw_free) + platform->driver->ops->hw_free(substream); + platform_err: - if (cpu_dai->driver->ops && cpu_dai->driver->ops->hw_free) + if (cpu_dai->driver->ops->hw_free) cpu_dai->driver->ops->hw_free(substream, cpu_dai); interface_err: @@ -944,12 +1067,12 @@ interface_err: codec_err: while (--i >= 0) { struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; - if (codec_dai->driver->ops && codec_dai->driver->ops->hw_free) + if (codec_dai->driver->ops->hw_free) codec_dai->driver->ops->hw_free(substream, codec_dai); codec_dai->rate = 0; } - if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free) + if (rtd->dai_link->ops->hw_free) rtd->dai_link->ops->hw_free(substream); mutex_unlock(&rtd->pcm_mutex); @@ -963,6 +1086,8 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai; bool playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; @@ -995,21 +1120,36 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) } /* free any machine hw params */ - if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free) + if (rtd->dai_link->ops->hw_free) rtd->dai_link->ops->hw_free(substream); /* free any DMA resources */ - if (platform->driver->ops && platform->driver->ops->hw_free) + if (platform && platform->driver->ops && platform->driver->ops->hw_free) platform->driver->ops->hw_free(substream); + /* free any component resources */ + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->hw_free) + continue; + + component->driver->ops->hw_free(substream); + } + /* now free hw params for the DAIs */ for (i = 0; i < rtd->num_codecs; i++) { codec_dai = rtd->codec_dais[i]; - if (codec_dai->driver->ops && codec_dai->driver->ops->hw_free) + if (codec_dai->driver->ops->hw_free) codec_dai->driver->ops->hw_free(substream, codec_dai); } - if (cpu_dai->driver->ops && cpu_dai->driver->ops->hw_free) + if (cpu_dai->driver->ops->hw_free) cpu_dai->driver->ops->hw_free(substream, cpu_dai); mutex_unlock(&rtd->pcm_mutex); @@ -1020,13 +1160,15 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai; int i, ret; for (i = 0; i < rtd->num_codecs; i++) { codec_dai = rtd->codec_dais[i]; - if (codec_dai->driver->ops && codec_dai->driver->ops->trigger) { + if (codec_dai->driver->ops->trigger) { ret = codec_dai->driver->ops->trigger(substream, cmd, codec_dai); if (ret < 0) @@ -1034,19 +1176,35 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) } } - if (platform->driver->ops && platform->driver->ops->trigger) { + if (platform && platform->driver->ops && platform->driver->ops->trigger) { ret = platform->driver->ops->trigger(substream, cmd); if (ret < 0) return ret; } - if (cpu_dai->driver->ops && cpu_dai->driver->ops->trigger) { + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->trigger) + continue; + + ret = component->driver->ops->trigger(substream, cmd); + if (ret < 0) + return ret; + } + + if (cpu_dai->driver->ops->trigger) { ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai); if (ret < 0) return ret; } - if (rtd->dai_link->ops && rtd->dai_link->ops->trigger) { + if (rtd->dai_link->ops->trigger) { ret = rtd->dai_link->ops->trigger(substream, cmd); if (ret < 0) return ret; @@ -1065,8 +1223,7 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream, for (i = 0; i < rtd->num_codecs; i++) { codec_dai = rtd->codec_dais[i]; - if (codec_dai->driver->ops && - codec_dai->driver->ops->bespoke_trigger) { + if (codec_dai->driver->ops->bespoke_trigger) { ret = codec_dai->driver->ops->bespoke_trigger(substream, cmd, codec_dai); if (ret < 0) @@ -1074,7 +1231,7 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream, } } - if (cpu_dai->driver->ops && cpu_dai->driver->ops->bespoke_trigger) { + if (cpu_dai->driver->ops->bespoke_trigger) { ret = cpu_dai->driver->ops->bespoke_trigger(substream, cmd, cpu_dai); if (ret < 0) return ret; @@ -1090,6 +1247,8 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai; struct snd_pcm_runtime *runtime = substream->runtime; @@ -1098,15 +1257,31 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) snd_pcm_sframes_t codec_delay = 0; int i; - if (platform->driver->ops && platform->driver->ops->pointer) + if (platform && platform->driver->ops && platform->driver->ops->pointer) offset = platform->driver->ops->pointer(substream); - if (cpu_dai->driver->ops && cpu_dai->driver->ops->delay) + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->pointer) + continue; + + /* FIXME: use 1st pointer */ + offset = component->driver->ops->pointer(substream); + break; + } + + if (cpu_dai->driver->ops->delay) delay += cpu_dai->driver->ops->delay(substream, cpu_dai); for (i = 0; i < rtd->num_codecs; i++) { codec_dai = rtd->codec_dais[i]; - if (codec_dai->driver->ops && codec_dai->driver->ops->delay) + if (codec_dai->driver->ops->delay) codec_delay = max(codec_delay, codec_dai->driver->ops->delay(substream, codec_dai)); @@ -2285,9 +2460,27 @@ static int soc_pcm_ioctl(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; - if (platform->driver->ops && platform->driver->ops->ioctl) + if (platform && platform->driver->ops && platform->driver->ops->ioctl) return platform->driver->ops->ioctl(substream, cmd, arg); + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->ioctl) + continue; + + /* FIXME: use 1st ioctl */ + return component->driver->ops->ioctl(substream, cmd, arg); + } + return snd_pcm_lib_ioctl(substream, cmd, arg); } @@ -2632,12 +2825,163 @@ static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream) return ret; } +static void soc_pcm_private_free(struct snd_pcm *pcm) +{ + struct snd_soc_pcm_runtime *rtd = pcm->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + /* need to sync the delayed work before releasing resources */ + + flush_delayed_work(&rtd->delayed_work); + component = rtdcom->component; + + if (component->pcm_free) + component->pcm_free(component, pcm); + } +} + +static int soc_rtdcom_ack(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->ops || + !component->driver->ops->ack) + continue; + + /* FIXME. it returns 1st ask now */ + return component->driver->ops->ack(substream); + } + + return -EINVAL; +} + +static int soc_rtdcom_copy_user(struct snd_pcm_substream *substream, int channel, + unsigned long pos, void __user *buf, + unsigned long bytes) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->ops || + !component->driver->ops->copy_user) + continue; + + /* FIXME. it returns 1st copy now */ + return component->driver->ops->copy_user(substream, channel, + pos, buf, bytes); + } + + return -EINVAL; +} + +static int soc_rtdcom_copy_kernel(struct snd_pcm_substream *substream, int channel, + unsigned long pos, void *buf, unsigned long bytes) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->ops || + !component->driver->ops->copy_kernel) + continue; + + /* FIXME. it returns 1st copy now */ + return component->driver->ops->copy_kernel(substream, channel, + pos, buf, bytes); + } + + return -EINVAL; +} + +static int soc_rtdcom_fill_silence(struct snd_pcm_substream *substream, int channel, + unsigned long pos, unsigned long bytes) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->ops || + !component->driver->ops->fill_silence) + continue; + + /* FIXME. it returns 1st silence now */ + return component->driver->ops->fill_silence(substream, channel, + pos, bytes); + } + + return -EINVAL; +} + +static struct page *soc_rtdcom_page(struct snd_pcm_substream *substream, + unsigned long offset) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + struct page *page; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->ops || + !component->driver->ops->page) + continue; + + /* FIXME. it returns 1st page now */ + page = component->driver->ops->page(substream, offset); + if (page) + return page; + } + + return NULL; +} + +static int soc_rtdcom_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->ops || + !component->driver->ops->mmap) + continue; + + /* FIXME. it returns 1st mmap now */ + return component->driver->ops->mmap(substream, vma); + } + + return -EINVAL; +} + /* create a new pcm */ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) { struct snd_soc_platform *platform = rtd->platform; struct snd_soc_dai *codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_pcm *pcm; char new_name[64]; int ret = 0, playback = 0, capture = 0; @@ -2732,7 +3076,28 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) rtd->ops.ioctl = soc_pcm_ioctl; } - if (platform->driver->ops) { + for_each_rtdcom(rtd, rtdcom) { + const struct snd_pcm_ops *ops = rtdcom->component->driver->ops; + + if (!ops) + continue; + + if (ops->ack) + rtd->ops.ack = soc_rtdcom_ack; + if (ops->copy_user) + rtd->ops.copy_user = soc_rtdcom_copy_user; + if (ops->copy_kernel) + rtd->ops.copy_kernel = soc_rtdcom_copy_kernel; + if (ops->fill_silence) + rtd->ops.fill_silence = soc_rtdcom_fill_silence; + if (ops->page) + rtd->ops.page = soc_rtdcom_page; + if (ops->mmap) + rtd->ops.mmap = soc_rtdcom_mmap; + } + + /* overwrite */ + if (platform && platform->driver->ops) { rtd->ops.ack = platform->driver->ops->ack; rtd->ops.copy_user = platform->driver->ops->copy_user; rtd->ops.copy_kernel = platform->driver->ops->copy_kernel; @@ -2747,17 +3112,22 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) if (capture) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops); - if (platform->driver->pcm_new) { - ret = platform->driver->pcm_new(rtd); + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->pcm_new) + continue; + + ret = component->pcm_new(component, rtd); if (ret < 0) { - dev_err(platform->dev, + dev_err(component->dev, "ASoC: pcm constructor failed: %d\n", ret); return ret; } } - pcm->private_free = platform->driver->pcm_free; + pcm->private_free = soc_pcm_private_free; out: dev_info(rtd->card->dev, "%s <-> %s mapping ok\n", (rtd->num_codecs > 1) ? "multicodec" : rtd->codec_dai->name, diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c index 1258bef4dcb3..d6f71a3406e9 100644 --- a/sound/soc/stm/stm32_sai.c +++ b/sound/soc/stm/stm32_sai.c @@ -16,6 +16,7 @@ * details. */ +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/module.h> @@ -27,6 +28,16 @@ #include "stm32_sai.h" +static LIST_HEAD(sync_providers); +static DEFINE_MUTEX(sync_mutex); + +struct sync_provider { + struct list_head link; + struct device_node *node; + int (*sync_conf)(void *data, int synco); + void *data; +}; + static const struct stm32_sai_conf stm32_sai_conf_f4 = { .version = SAI_STM32F4, }; @@ -41,23 +52,143 @@ static const struct of_device_id stm32_sai_ids[] = { {} }; +static int stm32_sai_sync_conf_client(struct stm32_sai_data *sai, int synci) +{ + int ret; + + /* Enable peripheral clock to allow GCR register access */ + ret = clk_prepare_enable(sai->pclk); + if (ret) { + dev_err(&sai->pdev->dev, "failed to enable clock: %d\n", ret); + return ret; + } + + writel_relaxed(FIELD_PREP(SAI_GCR_SYNCIN_MASK, (synci - 1)), sai->base); + + clk_disable_unprepare(sai->pclk); + + return 0; +} + +static int stm32_sai_sync_conf_provider(void *data, int synco) +{ + struct stm32_sai_data *sai = (struct stm32_sai_data *)data; + u32 prev_synco; + int ret; + + /* Enable peripheral clock to allow GCR register access */ + ret = clk_prepare_enable(sai->pclk); + if (ret) { + dev_err(&sai->pdev->dev, "failed to enable clock: %d\n", ret); + return ret; + } + + dev_dbg(&sai->pdev->dev, "Set %s%s as synchro provider\n", + sai->pdev->dev.of_node->name, + synco == STM_SAI_SYNC_OUT_A ? "A" : "B"); + + prev_synco = FIELD_GET(SAI_GCR_SYNCOUT_MASK, readl_relaxed(sai->base)); + if (prev_synco != STM_SAI_SYNC_OUT_NONE && synco != prev_synco) { + dev_err(&sai->pdev->dev, "%s%s already set as sync provider\n", + sai->pdev->dev.of_node->name, + prev_synco == STM_SAI_SYNC_OUT_A ? "A" : "B"); + clk_disable_unprepare(sai->pclk); + return -EINVAL; + } + + writel_relaxed(FIELD_PREP(SAI_GCR_SYNCOUT_MASK, synco), sai->base); + + clk_disable_unprepare(sai->pclk); + + return 0; +} + +static int stm32_sai_set_sync_provider(struct device_node *np, int synco) +{ + struct sync_provider *provider; + int ret; + + mutex_lock(&sync_mutex); + list_for_each_entry(provider, &sync_providers, link) { + if (provider->node == np) { + ret = provider->sync_conf(provider->data, synco); + mutex_unlock(&sync_mutex); + return ret; + } + } + mutex_unlock(&sync_mutex); + + /* SAI sync provider not found */ + return -ENODEV; +} + +static int stm32_sai_set_sync(struct stm32_sai_data *sai, + struct device_node *np_provider, + int synco, int synci) +{ + int ret; + + /* Configure sync client */ + stm32_sai_sync_conf_client(sai, synci); + + /* Configure sync provider */ + ret = stm32_sai_set_sync_provider(np_provider, synco); + + return ret; +} + +static int stm32_sai_sync_add_provider(struct platform_device *pdev, + void *data) +{ + struct sync_provider *sp; + + sp = devm_kzalloc(&pdev->dev, sizeof(*sp), GFP_KERNEL); + if (!sp) + return -ENOMEM; + + sp->node = of_node_get(pdev->dev.of_node); + sp->data = data; + sp->sync_conf = &stm32_sai_sync_conf_provider; + + mutex_lock(&sync_mutex); + list_add(&sp->link, &sync_providers); + mutex_unlock(&sync_mutex); + + return 0; +} + +static void stm32_sai_sync_del_provider(struct device_node *np) +{ + struct sync_provider *sp; + + mutex_lock(&sync_mutex); + list_for_each_entry(sp, &sync_providers, link) { + if (sp->node == np) { + list_del(&sp->link); + of_node_put(sp->node); + break; + } + } + mutex_unlock(&sync_mutex); +} + static int stm32_sai_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct stm32_sai_data *sai; struct reset_control *rst; struct resource *res; - void __iomem *base; const struct of_device_id *of_id; + int ret; sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); if (!sai) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); + sai->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(sai->base)) + return PTR_ERR(sai->base); of_id = of_match_device(stm32_sai_ids, &pdev->dev); if (of_id) @@ -65,6 +196,14 @@ static int stm32_sai_probe(struct platform_device *pdev) else return -EINVAL; + if (!STM_SAI_IS_F4(sai)) { + sai->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(sai->pclk)) { + dev_err(&pdev->dev, "missing bus clock pclk\n"); + return PTR_ERR(sai->pclk); + } + } + sai->clk_x8k = devm_clk_get(&pdev->dev, "x8k"); if (IS_ERR(sai->clk_x8k)) { dev_err(&pdev->dev, "missing x8k parent clock\n"); @@ -85,23 +224,34 @@ static int stm32_sai_probe(struct platform_device *pdev) } /* reset */ - rst = reset_control_get_exclusive(&pdev->dev, NULL); + rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (!IS_ERR(rst)) { reset_control_assert(rst); udelay(2); reset_control_deassert(rst); } + ret = stm32_sai_sync_add_provider(pdev, sai); + if (ret < 0) + return ret; + sai->set_sync = &stm32_sai_set_sync; + sai->pdev = pdev; platform_set_drvdata(pdev, sai); - return of_platform_populate(np, NULL, NULL, &pdev->dev); + ret = of_platform_populate(np, NULL, NULL, &pdev->dev); + if (ret < 0) + stm32_sai_sync_del_provider(np); + + return ret; } static int stm32_sai_remove(struct platform_device *pdev) { of_platform_depopulate(&pdev->dev); + stm32_sai_sync_del_provider(pdev->dev.of_node); + return 0; } diff --git a/sound/soc/stm/stm32_sai.h b/sound/soc/stm/stm32_sai.h index 889974dc62d9..bb062e70de63 100644 --- a/sound/soc/stm/stm32_sai.h +++ b/sound/soc/stm/stm32_sai.h @@ -16,9 +16,11 @@ * details. */ +#include <linux/bitfield.h> + /******************** SAI Register Map **************************************/ -/* common register */ +/* Global configuration register */ #define STM_SAI_GCR 0x00 /* Sub-block A&B registers offsets, relative to A&B sub-block addresses */ @@ -37,12 +39,13 @@ /******************** Bit definition for SAI_GCR register *******************/ #define SAI_GCR_SYNCIN_SHIFT 0 +#define SAI_GCR_SYNCIN_WDTH 2 #define SAI_GCR_SYNCIN_MASK GENMASK(1, SAI_GCR_SYNCIN_SHIFT) -#define SAI_GCR_SYNCIN_SET(x) ((x) << SAI_GCR_SYNCIN_SHIFT) +#define SAI_GCR_SYNCIN_MAX FIELD_GET(SAI_GCR_SYNCIN_MASK,\ + SAI_GCR_SYNCIN_MASK) #define SAI_GCR_SYNCOUT_SHIFT 4 #define SAI_GCR_SYNCOUT_MASK GENMASK(5, SAI_GCR_SYNCOUT_SHIFT) -#define SAI_GCR_SYNCOUT_SET(x) ((x) << SAI_GCR_SYNCOUT_SHIFT) /******************* Bit definition for SAI_XCR1 register *******************/ #define SAI_XCR1_RX_TX_SHIFT 0 @@ -231,6 +234,12 @@ #define STM_SAI_IS_F4(ip) ((ip)->conf->version == SAI_STM32F4) #define STM_SAI_IS_H7(ip) ((ip)->conf->version == SAI_STM32H7) +enum stm32_sai_syncout { + STM_SAI_SYNC_OUT_NONE, + STM_SAI_SYNC_OUT_A, + STM_SAI_SYNC_OUT_B, +}; + enum stm32_sai_version { SAI_STM32F4, SAI_STM32H7 @@ -247,15 +256,22 @@ struct stm32_sai_conf { /** * struct stm32_sai_data - private data of SAI instance driver * @pdev: device data pointer + * @base: common register bank virtual base address + * @pclk: SAI bus clock * @clk_x8k: SAI parent clock for sampling frequencies multiple of 8kHz * @clk_x11k: SAI parent clock for sampling frequencies multiple of 11kHz * @version: SOC version * @irq: SAI interrupt line + * @set_sync: pointer to synchro mode configuration callback */ struct stm32_sai_data { struct platform_device *pdev; + void __iomem *base; + struct clk *pclk; struct clk *clk_x8k; struct clk *clk_x11k; struct stm32_sai_conf *conf; int irq; + int (*set_sync)(struct stm32_sai_data *sai, + struct device_node *np_provider, int synco, int synci); }; diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 90d439613899..08583b958430 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -55,6 +55,12 @@ #define STM_SAI_IS_SUB_B(x) ((x)->id == STM_SAI_B_ID) #define STM_SAI_BLOCK_NAME(x) (((x)->id == STM_SAI_A_ID) ? "A" : "B") +#define SAI_SYNC_NONE 0x0 +#define SAI_SYNC_INTERNAL 0x1 +#define SAI_SYNC_EXTERNAL 0x2 + +#define STM_SAI_HAS_EXT_SYNC(x) (!STM_SAI_IS_F4(sai->pdata)) + /** * struct stm32_sai_sub_data - private data of SAI sub block (block A or B) * @pdev: device data pointer @@ -65,6 +71,7 @@ * @cpu_dai: DAI runtime data pointer * @substream: PCM substream data pointer * @pdata: SAI block parent data pointer + * @np_sync_provider: synchronization provider node * @sai_ck: kernel clock feeding the SAI clock generator * @phys_addr: SAI registers physical base address * @mclk_rate: SAI block master clock frequency (Hz). set at init @@ -73,6 +80,8 @@ * @master: SAI block mode flag. (true=master, false=slave) set at init * @fmt: SAI block format. relevant only for custom protocols. set at init * @sync: SAI block synchronization mode. (none, internal or external) + * @synco: SAI block ext sync source (provider setting). (none, sub-block A/B) + * @synci: SAI block ext sync source (client setting). (SAI sync provider index) * @fs_length: frame synchronization length. depends on protocol settings * @slots: rx or tx slot number * @slot_width: rx or tx slot width in bits @@ -88,6 +97,7 @@ struct stm32_sai_sub_data { struct snd_soc_dai *cpu_dai; struct snd_pcm_substream *substream; struct stm32_sai_data *pdata; + struct device_node *np_sync_provider; struct clk *sai_ck; dma_addr_t phys_addr; unsigned int mclk_rate; @@ -96,6 +106,8 @@ struct stm32_sai_sub_data { bool master; int fmt; int sync; + int synco; + int synci; int fs_length; int slots; int slot_width; @@ -184,7 +196,6 @@ static const struct regmap_config stm32_sai_sub_regmap_config_h7 = { static irqreturn_t stm32_sai_isr(int irq, void *devid) { struct stm32_sai_sub_data *sai = (struct stm32_sai_sub_data *)devid; - struct snd_pcm_substream *substream = sai->substream; struct platform_device *pdev = sai->pdev; unsigned int sr, imr, flags; snd_pcm_state_t status = SNDRV_PCM_STATE_RUNNING; @@ -199,6 +210,11 @@ static irqreturn_t stm32_sai_isr(int irq, void *devid) regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK, SAI_XCLRFR_MASK); + if (!sai->substream) { + dev_err(&pdev->dev, "Device stopped. Spurious IRQ 0x%x\n", sr); + return IRQ_NONE; + } + if (flags & SAI_XIMR_OVRUDRIE) { dev_err(&pdev->dev, "IRQ %s\n", STM_SAI_IS_PLAYBACK(sai) ? "underrun" : "overrun"); @@ -227,9 +243,9 @@ static irqreturn_t stm32_sai_isr(int irq, void *devid) } if (status != SNDRV_PCM_STATE_RUNNING) { - snd_pcm_stream_lock(substream); - snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock(substream); + snd_pcm_stream_lock(sai->substream); + snd_pcm_stop(sai->substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stream_unlock(sai->substream); } return IRQ_HANDLED; @@ -304,12 +320,15 @@ static int stm32_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); - int cr1 = 0, frcr = 0; - int cr1_mask = 0, frcr_mask = 0; + int cr1, frcr = 0; + int cr1_mask, frcr_mask = 0; int ret; dev_dbg(cpu_dai->dev, "fmt %x\n", fmt); + cr1_mask = SAI_XCR1_PRTCFG_MASK; + cr1 = SAI_XCR1_PRTCFG_SET(SAI_FREE_PROTOCOL); + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { /* SCK active high for all protocols */ case SND_SOC_DAIFMT_I2S: @@ -336,7 +355,7 @@ static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) return -EINVAL; } - cr1_mask |= SAI_XCR1_PRTCFG_MASK | SAI_XCR1_CKSTR; + cr1_mask |= SAI_XCR1_CKSTR; frcr_mask |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSOFF | SAI_XFRCR_FSDEF; @@ -380,6 +399,14 @@ static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) fmt & SND_SOC_DAIFMT_MASTER_MASK); return -EINVAL; } + + /* Set slave mode if sub-block is synchronized with another SAI */ + if (sai->sync) { + dev_dbg(cpu_dai->dev, "Synchronized SAI configured as slave\n"); + cr1 |= SAI_XCR1_SLAVE; + sai->master = false; + } + cr1_mask |= SAI_XCR1_SLAVE; /* do not generate master by default */ @@ -412,8 +439,6 @@ static int stm32_sai_startup(struct snd_pcm_substream *substream, } /* Enable ITs */ - regmap_update_bits(sai->regmap, STM_SAI_SR_REGX, - SAI_XSR_MASK, (unsigned int)~SAI_XSR_MASK); regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK, SAI_XCLRFR_MASK); @@ -442,34 +467,33 @@ static int stm32_sai_set_config(struct snd_soc_dai *cpu_dai, { struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); int cr1, cr1_mask, ret; - int fth = STM_SAI_FIFO_TH_HALF; - /* FIFO config */ + /* + * DMA bursts increment is set to 4 words. + * SAI fifo threshold is set to half fifo, to keep enough space + * for DMA incoming bursts. + */ regmap_update_bits(sai->regmap, STM_SAI_CR2_REGX, SAI_XCR2_FFLUSH | SAI_XCR2_FTH_MASK, - SAI_XCR2_FFLUSH | SAI_XCR2_FTH_SET(fth)); + SAI_XCR2_FFLUSH | + SAI_XCR2_FTH_SET(STM_SAI_FIFO_TH_HALF)); /* Mode, data format and channel config */ - cr1 = SAI_XCR1_PRTCFG_SET(SAI_FREE_PROTOCOL); + cr1_mask = SAI_XCR1_DS_MASK; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S8: - cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_8); + cr1 = SAI_XCR1_DS_SET(SAI_DATASIZE_8); break; case SNDRV_PCM_FORMAT_S16_LE: - cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_16); + cr1 = SAI_XCR1_DS_SET(SAI_DATASIZE_16); break; case SNDRV_PCM_FORMAT_S32_LE: - cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_32); + cr1 = SAI_XCR1_DS_SET(SAI_DATASIZE_32); break; default: dev_err(cpu_dai->dev, "Data format not supported"); return -EINVAL; } - cr1_mask = SAI_XCR1_DS_MASK | SAI_XCR1_PRTCFG_MASK; - - cr1_mask |= SAI_XCR1_RX_TX; - if (STM_SAI_IS_CAPTURE(sai)) - cr1 |= SAI_XCR1_RX_TX; cr1_mask |= SAI_XCR1_MONO; if ((sai->slots == 2) && (params_channels(params) == 1)) @@ -481,10 +505,6 @@ static int stm32_sai_set_config(struct snd_soc_dai *cpu_dai, return ret; } - /* DMA config */ - sai->dma_params.maxburst = STM_SAI_FIFO_SIZE * fth / sizeof(u32); - snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)&sai->dma_params); - return 0; } @@ -691,6 +711,9 @@ static int stm32_sai_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_STOP: dev_dbg(cpu_dai->dev, "Disable DMA and SAI\n"); + regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX, + SAI_XIMR_MASK, 0); + regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, SAI_XCR1_SAIEN, (unsigned int)~SAI_XCR1_SAIEN); @@ -725,9 +748,15 @@ static void stm32_sai_shutdown(struct snd_pcm_substream *substream, static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) { struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev); + int cr1 = 0, cr1_mask; sai->dma_params.addr = (dma_addr_t)(sai->phys_addr + STM_SAI_DR_REGX); - sai->dma_params.maxburst = 1; + /* + * DMA supports 4, 8 or 16 burst sizes. Burst size 4 is the best choice, + * as it allows bytes, half-word and words transfers. (See DMA fifos + * constraints). + */ + sai->dma_params.maxburst = 4; /* Buswidth will be set by framework at runtime */ sai->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED; @@ -736,7 +765,21 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) else snd_soc_dai_init_dma_data(cpu_dai, NULL, &sai->dma_params); - return 0; + cr1_mask = SAI_XCR1_RX_TX; + if (STM_SAI_IS_CAPTURE(sai)) + cr1 |= SAI_XCR1_RX_TX; + + /* Configure synchronization */ + if (sai->sync == SAI_SYNC_EXTERNAL) { + /* Configure synchro client and provider */ + sai->pdata->set_sync(sai->pdata, sai->np_sync_provider, + sai->synco, sai->synci); + } + + cr1_mask |= SAI_XCR1_SYNCEN_MASK; + cr1 |= SAI_XCR1_SYNCEN_SET(sai->sync); + + return regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1); } static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops = { @@ -822,6 +865,8 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, struct device_node *np = pdev->dev.of_node; struct resource *res; void __iomem *base; + struct of_phandle_args args; + int ret; if (!np) return -ENODEV; @@ -855,6 +900,69 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, return -EINVAL; } + /* Get synchronization property */ + args.np = NULL; + ret = of_parse_phandle_with_fixed_args(np, "st,sync", 1, 0, &args); + if (ret < 0 && ret != -ENOENT) { + dev_err(&pdev->dev, "Failed to get st,sync property\n"); + return ret; + } + + sai->sync = SAI_SYNC_NONE; + if (args.np) { + if (args.np == np) { + dev_err(&pdev->dev, "%s sync own reference\n", + np->name); + of_node_put(args.np); + return -EINVAL; + } + + sai->np_sync_provider = of_get_parent(args.np); + if (!sai->np_sync_provider) { + dev_err(&pdev->dev, "%s parent node not found\n", + np->name); + of_node_put(args.np); + return -ENODEV; + } + + sai->sync = SAI_SYNC_INTERNAL; + if (sai->np_sync_provider != sai->pdata->pdev->dev.of_node) { + if (!STM_SAI_HAS_EXT_SYNC(sai)) { + dev_err(&pdev->dev, + "External synchro not supported\n"); + of_node_put(args.np); + return -EINVAL; + } + sai->sync = SAI_SYNC_EXTERNAL; + + sai->synci = args.args[0]; + if (sai->synci < 1 || + (sai->synci > (SAI_GCR_SYNCIN_MAX + 1))) { + dev_err(&pdev->dev, "Wrong SAI index\n"); + of_node_put(args.np); + return -EINVAL; + } + + if (of_property_match_string(args.np, "compatible", + "st,stm32-sai-sub-a") >= 0) + sai->synco = STM_SAI_SYNC_OUT_A; + + if (of_property_match_string(args.np, "compatible", + "st,stm32-sai-sub-b") >= 0) + sai->synco = STM_SAI_SYNC_OUT_B; + + if (!sai->synco) { + dev_err(&pdev->dev, "Unknown SAI sub-block\n"); + of_node_put(args.np); + return -EINVAL; + } + } + + dev_dbg(&pdev->dev, "%s synchronized with %s\n", + pdev->name, args.np->full_name); + } + + of_node_put(args.np); sai->sai_ck = devm_clk_get(&pdev->dev, "sai_ck"); if (IS_ERR(sai->sai_ck)) { dev_err(&pdev->dev, "Missing kernel clock sai_ck\n"); diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c index 84cc5678beba..b9bdefcd3e10 100644 --- a/sound/soc/stm/stm32_spdifrx.c +++ b/sound/soc/stm/stm32_spdifrx.c @@ -392,6 +392,12 @@ static int stm32_spdifrx_dma_ctrl_register(struct device *dev, { int ret; + spdifrx->ctrl_chan = dma_request_chan(dev, "rx-ctrl"); + if (IS_ERR(spdifrx->ctrl_chan)) { + dev_err(dev, "dma_request_slave_channel failed\n"); + return PTR_ERR(spdifrx->ctrl_chan); + } + spdifrx->dmab = devm_kzalloc(dev, sizeof(struct snd_dma_buffer), GFP_KERNEL); if (!spdifrx->dmab) @@ -406,12 +412,6 @@ static int stm32_spdifrx_dma_ctrl_register(struct device *dev, return ret; } - spdifrx->ctrl_chan = dma_request_chan(dev, "rx-ctrl"); - if (!spdifrx->ctrl_chan) { - dev_err(dev, "dma_request_slave_channel failed\n"); - return -EINVAL; - } - spdifrx->slave_config.direction = DMA_DEV_TO_MEM; spdifrx->slave_config.src_addr = (dma_addr_t)(spdifrx->phys_addr + STM32_SPDIFRX_CSR); @@ -423,7 +423,6 @@ static int stm32_spdifrx_dma_ctrl_register(struct device *dev, &spdifrx->slave_config); if (ret < 0) { dev_err(dev, "dmaengine_slave_config returned error %d\n", ret); - dma_release_channel(spdifrx->ctrl_chan); spdifrx->ctrl_chan = NULL; } @@ -750,17 +749,21 @@ static int stm32_spdifrx_hw_params(struct snd_pcm_substream *substream, switch (data_size) { case 16: fmt = SPDIFRX_DRFMT_PACKED; - spdifrx->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; break; case 32: fmt = SPDIFRX_DRFMT_LEFT; - spdifrx->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; break; default: dev_err(&spdifrx->pdev->dev, "Unexpected data format\n"); return -EINVAL; } + /* + * Set buswidth to 4 bytes for all data formats. + * Packed format: transfer 2 x 2 bytes samples + * Left format: transfer 1 x 3 bytes samples + 1 dummy byte + */ + spdifrx->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; snd_soc_dai_init_dma_data(cpu_dai, NULL, &spdifrx->dma_params); return regmap_update_bits(spdifrx->regmap, STM32_SPDIFRX_CR, @@ -958,7 +961,7 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) return 0; error: - if (spdifrx->ctrl_chan) + if (!IS_ERR(spdifrx->ctrl_chan)) dma_release_channel(spdifrx->ctrl_chan); if (spdifrx->dmab) snd_dma_free_pages(spdifrx->dmab); diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index baa9007464ed..5da4efe7a550 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -346,11 +346,6 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream, 0x3 << 8, 0x1 << 8); - /* Fill most significant bits with valid data MSB */ - regmap_field_update_bits(scodec->reg_adc_fifoc, - BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE), - BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE)); - return 0; } @@ -490,6 +485,30 @@ static int sun4i_codec_hw_params_capture(struct sun4i_codec *scodec, BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN), 0); + /* Set the number of sample bits to either 16 or 24 bits */ + if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) { + regmap_field_update_bits(scodec->reg_adc_fifoc, + BIT(SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS), + BIT(SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS)); + + regmap_field_update_bits(scodec->reg_adc_fifoc, + BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE), + 0); + + scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + } else { + regmap_field_update_bits(scodec->reg_adc_fifoc, + BIT(SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS), + 0); + + /* Fill most significant bits with valid data MSB */ + regmap_field_update_bits(scodec->reg_adc_fifoc, + BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE), + BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE)); + + scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + } + return 0; } diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index abfb710df7cb..3dd183be08a4 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -73,6 +73,7 @@ #define SUN8I_SYS_SR_CTRL_AIF2_FS_MASK GENMASK(11, 8) #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK GENMASK(5, 4) #define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK GENMASK(8, 6) +#define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK GENMASK(12, 9) struct sun8i_codec { struct device *dev; @@ -170,11 +171,11 @@ static int sun8i_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) /* clock masters */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: /* DAI Slave */ - value = 0x0; /* Codec Master */ + case SND_SOC_DAIFMT_CBS_CFS: /* Codec slave, DAI master */ + value = 0x1; break; - case SND_SOC_DAIFMT_CBM_CFM: /* DAI Master */ - value = 0x1; /* Codec Slave */ + case SND_SOC_DAIFMT_CBM_CFM: /* Codec Master, DAI slave */ + value = 0x0; break; default: return -EINVAL; @@ -197,9 +198,20 @@ static int sun8i_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL, BIT(SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV), value << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV); + + /* + * It appears that the DAI and the codec don't share the same + * polarity for the LRCK signal when they mean 'normal' and + * 'inverted' in the datasheet. + * + * Since the DAI here is our regular i2s driver that have been + * tested with way more codecs than just this one, it means + * that the codec probably gets it backward, and we have to + * invert the value here. + */ regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL, BIT(SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV), - value << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV); + !value << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV); /* DAI format */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { @@ -226,12 +238,57 @@ static int sun8i_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } +struct sun8i_codec_clk_div { + u8 div; + u8 val; +}; + +static const struct sun8i_codec_clk_div sun8i_codec_bclk_div[] = { + { .div = 1, .val = 0 }, + { .div = 2, .val = 1 }, + { .div = 4, .val = 2 }, + { .div = 6, .val = 3 }, + { .div = 8, .val = 4 }, + { .div = 12, .val = 5 }, + { .div = 16, .val = 6 }, + { .div = 24, .val = 7 }, + { .div = 32, .val = 8 }, + { .div = 48, .val = 9 }, + { .div = 64, .val = 10 }, + { .div = 96, .val = 11 }, + { .div = 128, .val = 12 }, + { .div = 192, .val = 13 }, +}; + +static u8 sun8i_codec_get_bclk_div(struct sun8i_codec *scodec, + unsigned int rate, + unsigned int word_size) +{ + unsigned long clk_rate = clk_get_rate(scodec->clk_module); + unsigned int div = clk_rate / rate / word_size / 2; + unsigned int best_val = 0, best_diff = ~0; + int i; + + for (i = 0; i < ARRAY_SIZE(sun8i_codec_bclk_div); i++) { + const struct sun8i_codec_clk_div *bdiv = &sun8i_codec_bclk_div[i]; + unsigned int diff = abs(bdiv->div - div); + + if (diff < best_diff) { + best_diff = diff; + best_val = bdiv->val; + } + } + + return best_val; +} + static int sun8i_codec_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct sun8i_codec *scodec = snd_soc_codec_get_drvdata(dai->codec); int sample_rate; + u8 bclk_div; /* * The CPU DAI handles only a sample of 16 bits. Configure the @@ -241,6 +298,11 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream, SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK, SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16); + bclk_div = sun8i_codec_get_bclk_div(scodec, params_rate(params), 16); + regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL, + SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK, + bclk_div << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV); + regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL, SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK, SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_16); diff --git a/sound/soc/zte/zx-spdif.c b/sound/soc/zte/zx-spdif.c index b143f9f682d2..17b6ce35037a 100644 --- a/sound/soc/zte/zx-spdif.c +++ b/sound/soc/zte/zx-spdif.c @@ -139,11 +139,11 @@ static int zx_spdif_hw_params(struct snd_pcm_substream *substream, { struct zx_spdif_info *zx_spdif = dev_get_drvdata(socdai->dev); struct zx_spdif_info *spdif = snd_soc_dai_get_drvdata(socdai); - struct snd_dmaengine_dai_dma_data *dma_data = &zx_spdif->dma_data; + struct snd_dmaengine_dai_dma_data *dma_data = + snd_soc_dai_get_dma_data(socdai, substream); u32 val, ch_num, rate; int ret; - dma_data = snd_soc_dai_get_dma_data(socdai, substream); dma_data->addr_width = params_width(params) >> 3; val = readl_relaxed(zx_spdif->reg_base + ZX_CTRL); diff --git a/sound/synth/emux/emux.c b/sound/synth/emux/emux.c index b9981e8c0027..b840ff2dcfbb 100644 --- a/sound/synth/emux/emux.c +++ b/sound/synth/emux/emux.c @@ -53,7 +53,7 @@ int snd_emux_new(struct snd_emux **remu) emu->max_voices = 0; emu->use_time = 0; - setup_timer(&emu->tlist, snd_emux_timer_callback, (unsigned long)emu); + timer_setup(&emu->tlist, snd_emux_timer_callback, 0); emu->timer_active = 0; *remu = emu; diff --git a/sound/synth/emux/emux_oss.c b/sound/synth/emux/emux_oss.c index de19e108974a..764ff4bc2089 100644 --- a/sound/synth/emux/emux_oss.c +++ b/sound/synth/emux/emux_oss.c @@ -430,7 +430,6 @@ gusspec_control(struct snd_emux *emu, struct snd_emux_port *port, int cmd, { int voice; unsigned short p1; - short p2; int plong; struct snd_midi_channel *chan; @@ -445,7 +444,6 @@ gusspec_control(struct snd_emux *emu, struct snd_emux_port *port, int cmd, chan = &port->chset.channels[voice]; p1 = *(unsigned short *) &event[4]; - p2 = *(short *) &event[6]; plong = *(int*) &event[4]; switch (cmd) { diff --git a/sound/synth/emux/emux_synth.c b/sound/synth/emux/emux_synth.c index 599551b5af44..9fa696b0dbde 100644 --- a/sound/synth/emux/emux_synth.c +++ b/sound/synth/emux/emux_synth.c @@ -202,9 +202,9 @@ snd_emux_note_off(void *p, int note, int vel, struct snd_midi_channel *chan) * * release the pending note-offs */ -void snd_emux_timer_callback(unsigned long data) +void snd_emux_timer_callback(struct timer_list *t) { - struct snd_emux *emu = (struct snd_emux *) data; + struct snd_emux *emu = from_timer(emu, t, tlist); struct snd_emux_voice *vp; unsigned long flags; int ch, do_again = 0; diff --git a/sound/synth/emux/emux_voice.h b/sound/synth/emux/emux_voice.h index a7073c371bcc..326fa8993d7b 100644 --- a/sound/synth/emux/emux_voice.h +++ b/sound/synth/emux/emux_voice.h @@ -55,7 +55,7 @@ void snd_emux_update_channel(struct snd_emux_port *port, struct snd_midi_channel *chan, int update); void snd_emux_update_port(struct snd_emux_port *port, int update); -void snd_emux_timer_callback(unsigned long data); +void snd_emux_timer_callback(struct timer_list *t); /* emux_effect.c */ #ifdef SNDRV_EMUX_USE_RAW_EFFECT diff --git a/sound/usb/6fire/chip.c b/sound/usb/6fire/chip.c index c7641cb50616..17d5e3ee6d73 100644 --- a/sound/usb/6fire/chip.c +++ b/sound/usb/6fire/chip.c @@ -174,11 +174,9 @@ destroy_chip: static void usb6fire_chip_disconnect(struct usb_interface *intf) { struct sfire_chip *chip; - struct snd_card *card; chip = usb_get_intfdata(intf); if (chip) { /* if !chip, fw upload has been performed */ - card = chip->card; chip->intf_count--; if (!chip->intf_count) { mutex_lock(®ister_mutex); diff --git a/sound/usb/bcd2000/bcd2000.c b/sound/usb/bcd2000/bcd2000.c index fc579f330601..d6c8b29fe430 100644 --- a/sound/usb/bcd2000/bcd2000.c +++ b/sound/usb/bcd2000/bcd2000.c @@ -342,6 +342,13 @@ static int bcd2000_init_midi(struct bcd2000 *bcd2k) bcd2k->midi_out_buf, BUFSIZE, bcd2000_output_complete, bcd2k, 1); + /* sanity checks of EPs before actually submitting */ + if (usb_urb_ep_type_check(bcd2k->midi_in_urb) || + usb_urb_ep_type_check(bcd2k->midi_out_urb)) { + dev_err(&bcd2k->dev->dev, "invalid MIDI EP\n"); + return -EINVAL; + } + bcd2000_init_device(bcd2k); return 0; diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index d8409d9ae55b..d55ca48de3ea 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -461,6 +461,13 @@ static int init_card(struct snd_usb_caiaqdev *cdev) cdev->midi_out_buf, EP1_BUFSIZE, snd_usb_caiaq_midi_output_done, cdev); + /* sanity checks of EPs before actually submitting */ + if (usb_urb_ep_type_check(&cdev->ep1_in_urb) || + usb_urb_ep_type_check(&cdev->midi_out_urb)) { + dev_err(dev, "invalid EPs\n"); + return -EINVAL; + } + init_waitqueue_head(&cdev->ep1_wait_queue); init_waitqueue_head(&cdev->prepare_wait_queue); diff --git a/sound/usb/caiaq/input.c b/sound/usb/caiaq/input.c index 4b3fb91deecd..e883659ea6e7 100644 --- a/sound/usb/caiaq/input.c +++ b/sound/usb/caiaq/input.c @@ -718,6 +718,9 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *cdev) usb_rcvbulkpipe(usb_dev, 0x4), cdev->ep4_in_buf, EP4_BUFSIZE, snd_usb_caiaq_ep4_reply_dispatch, cdev); + ret = usb_urb_ep_type_check(cdev->ep4_in_urb); + if (ret < 0) + goto exit_free_idev; snd_usb_caiaq_set_auto_msg(cdev, 1, 10, 5); @@ -757,6 +760,9 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *cdev) usb_rcvbulkpipe(usb_dev, 0x4), cdev->ep4_in_buf, EP4_BUFSIZE, snd_usb_caiaq_ep4_reply_dispatch, cdev); + ret = usb_urb_ep_type_check(cdev->ep4_in_urb); + if (ret < 0) + goto exit_free_idev; snd_usb_caiaq_set_auto_msg(cdev, 1, 10, 5); @@ -802,6 +808,9 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *cdev) usb_rcvbulkpipe(usb_dev, 0x4), cdev->ep4_in_buf, EP4_BUFSIZE, snd_usb_caiaq_ep4_reply_dispatch, cdev); + ret = usb_urb_ep_type_check(cdev->ep4_in_urb); + if (ret < 0) + goto exit_free_idev; snd_usb_caiaq_set_auto_msg(cdev, 1, 10, 5); break; diff --git a/sound/usb/hiface/pcm.c b/sound/usb/hiface/pcm.c index 175d8d6b7f59..396c317115b1 100644 --- a/sound/usb/hiface/pcm.c +++ b/sound/usb/hiface/pcm.c @@ -541,6 +541,8 @@ static int hiface_pcm_init_urb(struct pcm_urb *urb, usb_fill_bulk_urb(&urb->instance, chip->dev, usb_sndbulkpipe(chip->dev, ep), (void *)urb->buffer, PCM_PACKET_SIZE, handler, urb); + if (usb_urb_ep_type_check(&urb->instance)) + return -EINVAL; init_usb_anchor(&urb->submitted); return 0; @@ -599,9 +601,12 @@ int hiface_pcm_init(struct hiface_chip *chip, u8 extra_freq) mutex_init(&rt->stream_mutex); spin_lock_init(&rt->playback.lock); - for (i = 0; i < PCM_N_URBS; i++) - hiface_pcm_init_urb(&rt->out_urbs[i], chip, OUT_EP, + for (i = 0; i < PCM_N_URBS; i++) { + ret = hiface_pcm_init_urb(&rt->out_urbs[i], chip, OUT_EP, hiface_pcm_out_urb_handler); + if (ret < 0) + return ret; + } ret = snd_pcm_new(chip->card, "USB-SPDIF Audio", 0, 1, 0, &pcm); if (ret < 0) { diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c index 7c812565f90d..947d6168f24a 100644 --- a/sound/usb/line6/capture.c +++ b/sound/usb/line6/capture.c @@ -248,7 +248,7 @@ static int snd_line6_capture_close(struct snd_pcm_substream *substream) } /* capture operators */ -struct snd_pcm_ops snd_line6_capture_ops = { +const struct snd_pcm_ops snd_line6_capture_ops = { .open = snd_line6_capture_open, .close = snd_line6_capture_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/usb/line6/capture.h b/sound/usb/line6/capture.h index 890b21bff18c..b67ccc39fd25 100644 --- a/sound/usb/line6/capture.h +++ b/sound/usb/line6/capture.h @@ -17,7 +17,7 @@ #include "driver.h" #include "pcm.h" -extern struct snd_pcm_ops snd_line6_capture_ops; +extern const struct snd_pcm_ops snd_line6_capture_ops; extern void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize); diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c index c8f723c3a033..4f9613e5fc9e 100644 --- a/sound/usb/line6/driver.c +++ b/sound/usb/line6/driver.c @@ -78,6 +78,13 @@ static int line6_start_listen(struct usb_line6 *line6) line6->buffer_listen, LINE6_BUFSIZE_LISTEN, line6_data_received, line6); } + + /* sanity checks of EP before actually submitting */ + if (usb_urb_ep_type_check(line6->urb_listen)) { + dev_err(line6->ifcdev, "invalid control EP\n"); + return -EINVAL; + } + line6->urb_listen->actual_length = 0; err = usb_submit_urb(line6->urb_listen, GFP_ATOMIC); return err; @@ -168,26 +175,33 @@ static int line6_send_raw_message_async_part(struct message *msg, } msg->done += bytes; - retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval < 0) { - dev_err(line6->ifcdev, "%s: usb_submit_urb failed (%d)\n", - __func__, retval); - usb_free_urb(urb); - kfree(msg); - return retval; - } + /* sanity checks of EP before actually submitting */ + retval = usb_urb_ep_type_check(urb); + if (retval < 0) + goto error; + + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval < 0) + goto error; return 0; + + error: + dev_err(line6->ifcdev, "%s: usb_submit_urb failed (%d)\n", + __func__, retval); + usb_free_urb(urb); + kfree(msg); + return retval; } /* Setup and start timer. */ void line6_start_timer(struct timer_list *timer, unsigned long msecs, - void (*function)(unsigned long), unsigned long data) + void (*function)(struct timer_list *t)) { - setup_timer(timer, function, data); + timer->function = (TIMER_FUNC_TYPE)function; mod_timer(timer, jiffies + msecs_to_jiffies(msecs)); } EXPORT_SYMBOL_GPL(line6_start_timer); diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h index dc97895547be..61425597eb61 100644 --- a/sound/usb/line6/driver.h +++ b/sound/usb/line6/driver.h @@ -198,8 +198,7 @@ extern int line6_send_sysex_message(struct usb_line6 *line6, extern ssize_t line6_set_raw(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); extern void line6_start_timer(struct timer_list *timer, unsigned long msecs, - void (*function)(unsigned long), - unsigned long data); + void (*function)(struct timer_list *t)); extern int line6_version_request_async(struct usb_line6 *line6); extern int line6_write_data(struct usb_line6 *line6, unsigned address, void *data, unsigned datalen); diff --git a/sound/usb/line6/midi.c b/sound/usb/line6/midi.c index 1d3a23b02d68..6d7cde56a355 100644 --- a/sound/usb/line6/midi.c +++ b/sound/usb/line6/midi.c @@ -130,16 +130,21 @@ static int send_midi_async(struct usb_line6 *line6, unsigned char *data, transfer_buffer, length, midi_sent, line6, line6->interval); urb->actual_length = 0; - retval = usb_submit_urb(urb, GFP_ATOMIC); + retval = usb_urb_ep_type_check(urb); + if (retval < 0) + goto error; - if (retval < 0) { - dev_err(line6->ifcdev, "usb_submit_urb failed\n"); - usb_free_urb(urb); - return retval; - } + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval < 0) + goto error; ++line6->line6midi->num_active_send_urbs; return 0; + + error: + dev_err(line6->ifcdev, "usb_submit_urb failed\n"); + usb_free_urb(urb); + return retval; } static int line6_midi_output_open(struct snd_rawmidi_substream *substream) diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c index 812d18191e01..819e9b2d1d6e 100644 --- a/sound/usb/line6/playback.c +++ b/sound/usb/line6/playback.c @@ -393,7 +393,7 @@ static int snd_line6_playback_close(struct snd_pcm_substream *substream) } /* playback operators */ -struct snd_pcm_ops snd_line6_playback_ops = { +const struct snd_pcm_ops snd_line6_playback_ops = { .open = snd_line6_playback_open, .close = snd_line6_playback_close, .ioctl = snd_pcm_lib_ioctl, diff --git a/sound/usb/line6/playback.h b/sound/usb/line6/playback.h index 51fce29e8726..d8d3b8a07a72 100644 --- a/sound/usb/line6/playback.h +++ b/sound/usb/line6/playback.h @@ -27,7 +27,7 @@ */ #define USE_CLEAR_BUFFER_WORKAROUND 1 -extern struct snd_pcm_ops snd_line6_playback_ops; +extern const struct snd_pcm_ops snd_line6_playback_ops; extern int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm); extern int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm); diff --git a/sound/usb/line6/pod.c b/sound/usb/line6/pod.c index 358224cc5638..020c81818951 100644 --- a/sound/usb/line6/pod.c +++ b/sound/usb/line6/pod.c @@ -174,7 +174,7 @@ static const char pod_version_header[] = { }; /* forward declarations: */ -static void pod_startup2(unsigned long data); +static void pod_startup2(struct timer_list *t); static void pod_startup3(struct usb_line6_pod *pod); static char *pod_alloc_sysex_buffer(struct usb_line6_pod *pod, int code, @@ -286,13 +286,12 @@ static void pod_startup1(struct usb_line6_pod *pod) CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_INIT); /* delay startup procedure: */ - line6_start_timer(&pod->startup_timer, POD_STARTUP_DELAY, pod_startup2, - (unsigned long)pod); + line6_start_timer(&pod->startup_timer, POD_STARTUP_DELAY, pod_startup2); } -static void pod_startup2(unsigned long data) +static void pod_startup2(struct timer_list *t) { - struct usb_line6_pod *pod = (struct usb_line6_pod *)data; + struct usb_line6_pod *pod = from_timer(pod, t, startup_timer); struct usb_line6 *line6 = &pod->line6; CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_VERSIONREQ); @@ -413,7 +412,7 @@ static int pod_init(struct usb_line6 *line6, line6->process_message = line6_pod_process_message; line6->disconnect = line6_pod_disconnect; - init_timer(&pod->startup_timer); + timer_setup(&pod->startup_timer, NULL, 0); INIT_WORK(&pod->startup_work, pod_startup4); /* create sysfs entries: */ diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c index 451007c27743..36ed9c85c0eb 100644 --- a/sound/usb/line6/podhd.c +++ b/sound/usb/line6/podhd.c @@ -39,7 +39,8 @@ enum { LINE6_PODHD500_1, LINE6_PODX3, LINE6_PODX3LIVE, - LINE6_PODHD500X + LINE6_PODHD500X, + LINE6_PODHDDESKTOP }; struct usb_line6_podhd { @@ -157,7 +158,7 @@ static struct line6_pcm_properties podx3_pcm_properties = { }; static struct usb_driver podhd_driver; -static void podhd_startup_start_workqueue(unsigned long data); +static void podhd_startup_start_workqueue(struct timer_list *t); static void podhd_startup_workqueue(struct work_struct *work); static int podhd_startup_finalize(struct usb_line6_podhd *pod); @@ -207,12 +208,12 @@ static void podhd_startup(struct usb_line6_podhd *pod) /* delay startup procedure: */ line6_start_timer(&pod->startup_timer, PODHD_STARTUP_DELAY, - podhd_startup_start_workqueue, (unsigned long)pod); + podhd_startup_start_workqueue); } -static void podhd_startup_start_workqueue(unsigned long data) +static void podhd_startup_start_workqueue(struct timer_list *t) { - struct usb_line6_podhd *pod = (struct usb_line6_podhd *)data; + struct usb_line6_podhd *pod = from_timer(pod, t, startup_timer); CHECK_STARTUP_PROGRESS(pod->startup_progress, PODHD_STARTUP_SCHEDULE_WORKQUEUE); @@ -318,7 +319,7 @@ static int podhd_init(struct usb_line6 *line6, line6->disconnect = podhd_disconnect; - init_timer(&pod->startup_timer); + timer_setup(&pod->startup_timer, NULL, 0); INIT_WORK(&pod->startup_work, podhd_startup_workqueue); if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) { @@ -379,6 +380,7 @@ static const struct usb_device_id podhd_id_table[] = { { LINE6_IF_NUM(0x414A, 0), .driver_info = LINE6_PODX3 }, { LINE6_IF_NUM(0x414B, 0), .driver_info = LINE6_PODX3LIVE }, { LINE6_IF_NUM(0x4159, 0), .driver_info = LINE6_PODHD500X }, + { LINE6_IF_NUM(0x4156, 0), .driver_info = LINE6_PODHDDESKTOP }, {} }; @@ -465,6 +467,18 @@ static const struct line6_properties podhd_properties_table[] = { .ep_audio_r = 0x86, .ep_audio_w = 0x02, }, + [LINE6_PODHDDESKTOP] = { + .id = "PODHDDESKTOP", + .name = "POD HDDESKTOP", + .capabilities = LINE6_CAP_CONTROL + | LINE6_CAP_PCM | LINE6_CAP_HWMON, + .altsetting = 1, + .ep_ctrl_r = 0x81, + .ep_ctrl_w = 0x01, + .ctrl_if = 1, + .ep_audio_r = 0x86, + .ep_audio_w = 0x02, + }, }; /* diff --git a/sound/usb/line6/toneport.c b/sound/usb/line6/toneport.c index ba7975c0d03d..750467fb95db 100644 --- a/sound/usb/line6/toneport.c +++ b/sound/usb/line6/toneport.c @@ -241,9 +241,9 @@ static int snd_toneport_source_put(struct snd_kcontrol *kcontrol, return 1; } -static void toneport_start_pcm(unsigned long arg) +static void toneport_start_pcm(struct timer_list *t) { - struct usb_line6_toneport *toneport = (struct usb_line6_toneport *)arg; + struct usb_line6_toneport *toneport = from_timer(toneport, t, timer); struct usb_line6 *line6 = &toneport->line6; line6_pcm_acquire(line6->line6pcm, LINE6_STREAM_MONITOR, true); @@ -415,8 +415,7 @@ static int toneport_init(struct usb_line6 *line6, struct usb_line6_toneport *toneport = (struct usb_line6_toneport *) line6; toneport->type = id->driver_info; - setup_timer(&toneport->timer, toneport_start_pcm, - (unsigned long)toneport); + timer_setup(&toneport->timer, toneport_start_pcm, 0); line6->disconnect = line6_toneport_disconnect; diff --git a/sound/usb/line6/variax.c b/sound/usb/line6/variax.c index 0c4512d0382e..e8c852b2ce35 100644 --- a/sound/usb/line6/variax.c +++ b/sound/usb/line6/variax.c @@ -82,9 +82,9 @@ static const char variax_activate[] = { }; /* forward declarations: */ -static void variax_startup2(unsigned long data); -static void variax_startup4(unsigned long data); -static void variax_startup5(unsigned long data); +static void variax_startup2(struct timer_list *t); +static void variax_startup4(struct timer_list *t); +static void variax_startup5(struct timer_list *t); static void variax_activate_async(struct usb_line6_variax *variax, int a) { @@ -106,12 +106,12 @@ static void variax_startup1(struct usb_line6_variax *variax) /* delay startup procedure: */ line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1, - variax_startup2, (unsigned long)variax); + variax_startup2); } -static void variax_startup2(unsigned long data) +static void variax_startup2(struct timer_list *t) { - struct usb_line6_variax *variax = (struct usb_line6_variax *)data; + struct usb_line6_variax *variax = from_timer(variax, t, startup_timer1); struct usb_line6 *line6 = &variax->line6; /* schedule another startup procedure until startup is complete: */ @@ -120,7 +120,7 @@ static void variax_startup2(unsigned long data) variax->startup_progress = VARIAX_STARTUP_VERSIONREQ; line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1, - variax_startup2, (unsigned long)variax); + variax_startup2); /* request firmware version: */ line6_version_request_async(line6); @@ -132,12 +132,12 @@ static void variax_startup3(struct usb_line6_variax *variax) /* delay startup procedure: */ line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY3, - variax_startup4, (unsigned long)variax); + variax_startup4); } -static void variax_startup4(unsigned long data) +static void variax_startup4(struct timer_list *t) { - struct usb_line6_variax *variax = (struct usb_line6_variax *)data; + struct usb_line6_variax *variax = from_timer(variax, t, startup_timer2); CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_ACTIVATE); @@ -145,12 +145,12 @@ static void variax_startup4(unsigned long data) /* activate device: */ variax_activate_async(variax, 1); line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY4, - variax_startup5, (unsigned long)variax); + variax_startup5); } -static void variax_startup5(unsigned long data) +static void variax_startup5(struct timer_list *t) { - struct usb_line6_variax *variax = (struct usb_line6_variax *)data; + struct usb_line6_variax *variax = from_timer(variax, t, startup_timer2); CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_WORKQUEUE); @@ -190,7 +190,7 @@ static void line6_variax_process_message(struct usb_line6 *line6) } else if (memcmp(buf + 1, variax_init_done + 1, sizeof(variax_init_done) - 1) == 0) { /* notify of complete initialization: */ - variax_startup4((unsigned long)variax); + variax_startup4(&variax->startup_timer2); } break; } @@ -222,8 +222,8 @@ static int variax_init(struct usb_line6 *line6, line6->process_message = line6_variax_process_message; line6->disconnect = line6_variax_disconnect; - init_timer(&variax->startup_timer1); - init_timer(&variax->startup_timer2); + timer_setup(&variax->startup_timer1, NULL, 0); + timer_setup(&variax->startup_timer2, NULL, 0); INIT_WORK(&variax->startup_work, variax_startup6); /* initialize USB buffers: */ diff --git a/sound/usb/midi.c b/sound/usb/midi.c index a92e2b2a91ec..2c1aaa3292bf 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -352,9 +352,9 @@ static void snd_usbmidi_out_tasklet(unsigned long data) } /* called after transfers had been interrupted due to some USB error */ -static void snd_usbmidi_error_timer(unsigned long data) +static void snd_usbmidi_error_timer(struct timer_list *t) { - struct snd_usb_midi *umidi = (struct snd_usb_midi *)data; + struct snd_usb_midi *umidi = from_timer(umidi, t, error_timer); unsigned int i, j; spin_lock(&umidi->disc_lock); @@ -1282,6 +1282,7 @@ static int snd_usbmidi_in_endpoint_create(struct snd_usb_midi *umidi, unsigned int pipe; int length; unsigned int i; + int err; rep->in = NULL; ep = kzalloc(sizeof(*ep), GFP_KERNEL); @@ -1292,8 +1293,8 @@ static int snd_usbmidi_in_endpoint_create(struct snd_usb_midi *umidi, for (i = 0; i < INPUT_URBS; ++i) { ep->urbs[i] = usb_alloc_urb(0, GFP_KERNEL); if (!ep->urbs[i]) { - snd_usbmidi_in_endpoint_delete(ep); - return -ENOMEM; + err = -ENOMEM; + goto error; } } if (ep_info->in_interval) @@ -1305,8 +1306,8 @@ static int snd_usbmidi_in_endpoint_create(struct snd_usb_midi *umidi, buffer = usb_alloc_coherent(umidi->dev, length, GFP_KERNEL, &ep->urbs[i]->transfer_dma); if (!buffer) { - snd_usbmidi_in_endpoint_delete(ep); - return -ENOMEM; + err = -ENOMEM; + goto error; } if (ep_info->in_interval) usb_fill_int_urb(ep->urbs[i], umidi->dev, @@ -1318,10 +1319,20 @@ static int snd_usbmidi_in_endpoint_create(struct snd_usb_midi *umidi, pipe, buffer, length, snd_usbmidi_in_urb_complete, ep); ep->urbs[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + err = usb_urb_ep_type_check(ep->urbs[i]); + if (err < 0) { + dev_err(&umidi->dev->dev, "invalid MIDI in EP %x\n", + ep_info->in_ep); + goto error; + } } rep->in = ep; return 0; + + error: + snd_usbmidi_in_endpoint_delete(ep); + return -ENOMEM; } /* @@ -1357,6 +1368,7 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi *umidi, unsigned int i; unsigned int pipe; void *buffer; + int err; rep->out = NULL; ep = kzalloc(sizeof(*ep), GFP_KERNEL); @@ -1367,8 +1379,8 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi *umidi, for (i = 0; i < OUTPUT_URBS; ++i) { ep->urbs[i].urb = usb_alloc_urb(0, GFP_KERNEL); if (!ep->urbs[i].urb) { - snd_usbmidi_out_endpoint_delete(ep); - return -ENOMEM; + err = -ENOMEM; + goto error; } ep->urbs[i].ep = ep; } @@ -1406,8 +1418,8 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi *umidi, ep->max_transfer, GFP_KERNEL, &ep->urbs[i].urb->transfer_dma); if (!buffer) { - snd_usbmidi_out_endpoint_delete(ep); - return -ENOMEM; + err = -ENOMEM; + goto error; } if (ep_info->out_interval) usb_fill_int_urb(ep->urbs[i].urb, umidi->dev, @@ -1419,6 +1431,12 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi *umidi, pipe, buffer, ep->max_transfer, snd_usbmidi_out_urb_complete, &ep->urbs[i]); + err = usb_urb_ep_type_check(ep->urbs[i].urb); + if (err < 0) { + dev_err(&umidi->dev->dev, "invalid MIDI out EP %x\n", + ep_info->out_ep); + goto error; + } ep->urbs[i].urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; } @@ -1437,6 +1455,10 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi *umidi, rep->out = ep; return 0; + + error: + snd_usbmidi_out_endpoint_delete(ep); + return err; } /* @@ -2347,8 +2369,7 @@ int __snd_usbmidi_create(struct snd_card *card, usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor), le16_to_cpu(umidi->dev->descriptor.idProduct)); umidi->usb_id = usb_id; - setup_timer(&umidi->error_timer, snd_usbmidi_error_timer, - (unsigned long)umidi); + timer_setup(&umidi->error_timer, snd_usbmidi_error_timer, 0); /* detect the endpoint(s) to use */ memset(endpoints, 0, sizeof(endpoints)); diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 20624320b753..77eecaa4db1f 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1128,30 +1128,24 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip) /* devices which do not support reading the sample rate. */ switch (chip->usb_id) { case USB_ID(0x041E, 0x4080): /* Creative Live Cam VF0610 */ - case USB_ID(0x045E, 0x075D): /* MS Lifecam Cinema */ - case USB_ID(0x045E, 0x076D): /* MS Lifecam HD-5000 */ - case USB_ID(0x045E, 0x076E): /* MS Lifecam HD-5001 */ - case USB_ID(0x045E, 0x076F): /* MS Lifecam HD-6000 */ - case USB_ID(0x045E, 0x0772): /* MS Lifecam Studio */ - case USB_ID(0x045E, 0x0779): /* MS Lifecam HD-3000 */ - case USB_ID(0x047F, 0x02F7): /* Plantronics BT-600 */ - case USB_ID(0x047F, 0x0415): /* Plantronics BT-300 */ - case USB_ID(0x047F, 0xAA05): /* Plantronics DA45 */ - case USB_ID(0x047F, 0xC022): /* Plantronics C310 */ - case USB_ID(0x047F, 0xC02F): /* Plantronics P610 */ - case USB_ID(0x047F, 0xC036): /* Plantronics C520-M */ case USB_ID(0x04D8, 0xFEEA): /* Benchmark DAC1 Pre */ case USB_ID(0x0556, 0x0014): /* Phoenix Audio TMX320VC */ case USB_ID(0x05A3, 0x9420): /* ELP HD USB Camera */ case USB_ID(0x074D, 0x3553): /* Outlaw RR2150 (Micronas UAC3553B) */ case USB_ID(0x1395, 0x740a): /* Sennheiser DECT */ case USB_ID(0x1901, 0x0191): /* GE B850V3 CP2114 audio interface */ - case USB_ID(0x1de7, 0x0013): /* Phoenix Audio MT202exe */ - case USB_ID(0x1de7, 0x0014): /* Phoenix Audio TMX320 */ - case USB_ID(0x1de7, 0x0114): /* Phoenix Audio MT202pcs */ case USB_ID(0x21B4, 0x0081): /* AudioQuest DragonFly */ return true; } + + /* devices of these vendors don't support reading rate, either */ + switch (USB_ID_VENDOR(chip->usb_id)) { + case 0x045E: /* MS Lifecam */ + case 0x047F: /* Plantronics */ + case 0x1de7: /* Phoenix Audio */ + return true; + } + return false; } diff --git a/sound/usb/usx2y/usb_stream.c b/sound/usb/usx2y/usb_stream.c index e229abd21652..b0f8979ff2d2 100644 --- a/sound/usb/usx2y/usb_stream.c +++ b/sound/usb/usx2y/usb_stream.c @@ -56,7 +56,7 @@ check: lb, s->period_size); } -static void init_pipe_urbs(struct usb_stream_kernel *sk, unsigned use_packsize, +static int init_pipe_urbs(struct usb_stream_kernel *sk, unsigned use_packsize, struct urb **urbs, char *transfer, struct usb_device *dev, int pipe) { @@ -77,6 +77,8 @@ static void init_pipe_urbs(struct usb_stream_kernel *sk, unsigned use_packsize, urb->interval = 1; if (usb_pipeout(pipe)) continue; + if (usb_urb_ep_type_check(urb)) + return -EINVAL; urb->transfer_buffer_length = transfer_length; desc = urb->iso_frame_desc; @@ -87,9 +89,11 @@ static void init_pipe_urbs(struct usb_stream_kernel *sk, unsigned use_packsize, desc[p].length = maxpacket; } } + + return 0; } -static void init_urbs(struct usb_stream_kernel *sk, unsigned use_packsize, +static int init_urbs(struct usb_stream_kernel *sk, unsigned use_packsize, struct usb_device *dev, int in_pipe, int out_pipe) { struct usb_stream *s = sk->s; @@ -103,9 +107,12 @@ static void init_urbs(struct usb_stream_kernel *sk, unsigned use_packsize, sk->outurb[u] = usb_alloc_urb(sk->n_o_ps, GFP_KERNEL); } - init_pipe_urbs(sk, use_packsize, sk->inurb, indata, dev, in_pipe); - init_pipe_urbs(sk, use_packsize, sk->outurb, sk->write_page, dev, - out_pipe); + if (init_pipe_urbs(sk, use_packsize, sk->inurb, indata, dev, in_pipe) || + init_pipe_urbs(sk, use_packsize, sk->outurb, sk->write_page, dev, + out_pipe)) + return -EINVAL; + + return 0; } @@ -226,7 +233,11 @@ struct usb_stream *usb_stream_new(struct usb_stream_kernel *sk, else sk->freqn = get_usb_high_speed_rate(sample_rate); - init_urbs(sk, use_packsize, dev, in_pipe, out_pipe); + if (init_urbs(sk, use_packsize, dev, in_pipe, out_pipe) < 0) { + usb_stream_free(sk); + return NULL; + } + sk->s->state = usb_stream_stopped; out: return sk->s; diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c index 4569c0efac0a..0ddf29267d70 100644 --- a/sound/usb/usx2y/usbusx2y.c +++ b/sound/usb/usx2y/usbusx2y.c @@ -279,6 +279,9 @@ int usX2Y_AsyncSeq04_init(struct usX2Ydev *usX2Y) usX2Y->AS04.buffer + URB_DataLen_AsyncSeq*i, 0, i_usX2Y_Out04Int, usX2Y ); + err = usb_urb_ep_type_check(usX2Y->AS04.urb[i]); + if (err < 0) + break; } return err; } @@ -298,6 +301,8 @@ int usX2Y_In04_init(struct usX2Ydev *usX2Y) usX2Y->In04Buf, 21, i_usX2Y_In04Int, usX2Y, 10); + if (usb_urb_ep_type_check(usX2Y->In04urb)) + return -EINVAL; return usb_submit_urb(usX2Y->In04urb, GFP_KERNEL); } diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c index f93b355756e6..345e439aa95b 100644 --- a/sound/usb/usx2y/usbusx2yaudio.c +++ b/sound/usb/usx2y/usbusx2yaudio.c @@ -677,6 +677,9 @@ static int usX2Y_rate_set(struct usX2Ydev *usX2Y, int rate) usb_fill_bulk_urb(us->urb[i], usX2Y->dev, usb_sndbulkpipe(usX2Y->dev, 4), usbdata + i, 2, i_usX2Y_04Int, usX2Y); } + err = usb_urb_ep_type_check(us->urb[0]); + if (err < 0) + goto cleanup; us->submitted = 0; us->len = NOOF_SETRATE_URBS; usX2Y->US04 = us; |