diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-11-14 18:01:46 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-11-14 18:01:46 -0800 |
commit | 4e4510fec4af08ead21f6934c1410af1f19a8cad (patch) | |
tree | 2bafab4f7cc2cdf2983186b24140f6303d4dfc8c | |
parent | 4008e6a9bcee2f3b61bb11951de0fb0ed764cb91 (diff) | |
parent | 7087cb8fad5e19113d82f47f351fc6b338948d5f (diff) | |
download | linux-4e4510fec4af08ead21f6934c1410af1f19a8cad.tar.bz2 |
Merge tag 'sound-4.15-rc1' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai:
"There are no big surprising changes in this cycle, yet not too boring,
either. The biggest change from diffstat POV is the removal of the
legacy OSS driver codes that have been already disabled for a long
time. This will bring a few trivial merge conflicts.
As new features in ASoC side, there are two things: a new AC97 bus
implementation and AMD Stony platform support. Both include the
relevant changes shared with other subsystems, e.g. AC97 MFD changes
and DRM AMD changes.
Some other highlighted topics are:
- A bunch of USB-audio drivers got the hardening against the
malicious device accesses with a new helper code for endpoint
sanity check
- Lots of cleanups for ASoC Intel platform code, including support
for their open source audio firmware
- Continued ASoC core componentization works
- Support for scaling MCLK with sample rate in ASoC simple-card
- Stabler PCM hot-unplug capability, especially for ASoC usages"
* tag 'sound-4.15-rc1' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (302 commits)
Documentation: sound: hd-audio: notes.rst
ASoC: bcm2835: Support left/right justified and DSP modes
ASoC: bcm2835: Enforce full symmetry
ASoC: bcm2835: Support additional samplerates up to 384kHz
ASoC: bcm2835: Add support for TDM modes
ASoC: add mclk-fs support to audio graph card
ASoC: add mclk-fs to audio graph card binding
ASoC: rt5514: work around link error
ASoC: rt5514: mark PM functions as __maybe_unused
ASoC: rt5663: Check the JD status in the button pushing
ASoC: amd: Modified DMA transfer Mechanism for Playback
ASoC: rt5645: Wait for 400msec before concluding on value of RT5645_VENDOR_ID2
ASoC: sun4i-codec: fixed 32bit audio capture support for H3/H2+
ASoC: da7213: add support for DSP modes
ASoC: sun8i-codec: Add a comment on the LRCK inversion
ASoC: sun8i-codec: Set the BCLK divider
ASoC: rt5663: Delay and retry reading rt5663 ID register
ASoC: amd: use do_div rather than 64 bit division to fix 32 bit builds
ASoC: cs42l56: Fix reset GPIO name in example DT binding
ASoC: rt5514-spi: check irq status to schedule data copy in resume function
...
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; |