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
Also see child specific device properties:
Regulator - ../regulator/arizona-regulator.txt
Extcon - ../extcon/extcon-arizona.txt
+ Sound - ../sound/arizona.txt
Example:
- bitclock-master
- bitclock-inversion
- frame-inversion
+- mclk-fs
- dai-tdm-slot-num
- dai-tdm-slot-width
- clocks / system-clock-frequency
label = "sound-card";
prefix = "codec";
routing = "codec Playback", "DAI0 Playback",
- "codec Playback", "DAI1 Playback";
+ "DAI0 Capture", "codec Capture";
convert-rate = <48000>;
dais = <&cpu_port>;
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
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>;
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
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:
Example:
-codec: sgtl5000@0a {
+codec: sgtl5000@a {
compatible = "fsl,sgtl5000";
reg = <0x0a>;
clocks = <&clks 150>;
- 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.
#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 {
--- /dev/null
+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>;
+ };
+};
+
--- /dev/null
+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.
Lenovo Thinkpad T460/560 setup
dual-codecs
Lenovo laptops with dual codecs
+alc700-ref
+ Intel reference board with ALC700 codec
ALC66x/67x/892
==============
+++ /dev/null
-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.
+++ /dev/null
-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
+++ /dev/null
-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
+++ /dev/null
-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.
-
+++ /dev/null
-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
+++ /dev/null
-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)
-
+++ /dev/null
-#! /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
+++ /dev/null
-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
+++ /dev/null
-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;
-}
-
+++ /dev/null
-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.
+++ /dev/null
-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/
-
+++ /dev/null
- 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
+++ /dev/null
-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
+++ /dev/null
-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.
+++ /dev/null
-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>
-
+++ /dev/null
-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.
+++ /dev/null
-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)".
+++ /dev/null
-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.
+++ /dev/null
-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! ;)
-
-
+++ /dev/null
-
- (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)
+++ /dev/null
-
-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>
+++ /dev/null
- 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)
-_/ _/_/_/_/
- _/
-_/ _/
- _/_/
-===========================================================
+++ /dev/null
- 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>
+++ /dev/null
-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.
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
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
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
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;
#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
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
#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>
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);
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 */
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;
}
{
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);
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);
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)
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
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) {
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;
}
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;
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);
--- /dev/null
+/*
+ * 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");
+
/*-------------------------------------------------------------------*/
+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
*/
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;
*/
/* 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]);
--- /dev/null
+/*
+ * 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__ */
/** 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];
--- /dev/null
+/*
+ * 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
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,
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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) */
+
#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>
/* 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 */
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 */
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);
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);
#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);
/* 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);
#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
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
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 */
};
--- /dev/null
+
+/*
+ * 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
--- /dev/null
+/*
+ * 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
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);
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);
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);
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 {
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);
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);
snd_soc_dapm_init_bias_level(snd_soc_codec_get_dapm(codec), level);
}
+/**
+ * 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
return snd_soc_dapm_get_bias_level(snd_soc_codec_get_dapm(codec));
}
+/**
+ * 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);
}
+/**
+ * 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
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,
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,
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
#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
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
source "sound/ppc/Kconfig"
+source "sound/ac97/Kconfig"
+
source "sound/aoa/Kconfig"
source "sound/arm/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
#
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
--- /dev/null
+#
+# 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
--- /dev/null
+#
+# 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
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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>");
--- /dev/null
+/*
+ * 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_* */
+
--- /dev/null
+/*
+ * 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);
#include <linux/io.h>
#include <linux/gpio.h>
-#include <sound/ac97_codec.h>
#include <sound/pxa2xx-lib.h>
#include <mach/irqs.h>
* 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);
}
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);
}
#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;
}
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;
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;
#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 = {
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,
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;
#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;
}
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)
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);
* @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.
.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;
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;
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);
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);
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);
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;
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;
}
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);
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;
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);
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);
}
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) {
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;
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;
* 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);
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 :
* 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);
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;
/*
* 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;
}
/* 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;
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);
}
/* 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);
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 */
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;
}
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;
/**
* 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) {
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;
}
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)
{
{
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;
+}
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 */
#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)
{
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];
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;
* 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);
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;
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;
}
}
-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);
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;
}
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;
}
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);
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++;
+++ /dev/null
-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.
+++ /dev/null
-# 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
-
+++ /dev/null
-# 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
+++ /dev/null
-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
+++ /dev/null
-/*
- * 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");
+++ /dev/null
-/* 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);
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- 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
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-// 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;
-}
+++ /dev/null
-/*
- * 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 */
+++ /dev/null
-/*
- * 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);
-
+++ /dev/null
-/*
- * 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_ */
-
+++ /dev/null
-/*
- * 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);
- }
-}
+++ /dev/null
-// 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;
-}
+++ /dev/null
-/*
- * 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);
-
+++ /dev/null
-/* 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 */
-};
+++ /dev/null
-/*
- * 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);
-
+++ /dev/null
-/* 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
+++ /dev/null
-/*
- * 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);
-
+++ /dev/null
-/*
- * 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");
+++ /dev/null
-/* 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);
+++ /dev/null
-/*********************************************************************
- *
- * 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
+++ /dev/null
-/*********************************************************************
- *
- * 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 */
+++ /dev/null
-/* The work is in msnd_pinnacle.c, just define MSND_CLASSIC before it. */
-#define MSND_CLASSIC
-#include "msnd_pinnacle.c"
+++ /dev/null
-/*********************************************************************
- *
- * 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 */
+++ /dev/null
-/*********************************************************************
- *
- * 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);
+++ /dev/null
-/*********************************************************************
- *
- * 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 */
+++ /dev/null
-/*
- * 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");
+++ /dev/null
-/*
- * 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
- */
+++ /dev/null
-/* 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;
+++ /dev/null
-/* 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);
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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 */
-}
+++ /dev/null
-
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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");
- }
- }
-}
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/* 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);
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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");
+++ /dev/null
-// 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;
-}
-
+++ /dev/null
-/* 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);
-
+++ /dev/null
-/*
- * 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();
-}
+++ /dev/null
-/*
- * 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--;
-}
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/* 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;
-}
+++ /dev/null
-/* 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);
+++ /dev/null
-/* 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
+++ /dev/null
-/* 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;
-}
+++ /dev/null
-/*
- * 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);
-
+++ /dev/null
-/*
- * 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);
-
+++ /dev/null
-#define SOUND_VERSION_STRING "3.8s2++-971130"
-#define SOUND_INTERNAL_VERSION 0x030804
+++ /dev/null
-/*******************************************************************************
-*
-* "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);
+++ /dev/null
-/*
- * 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
-};
+++ /dev/null
-/*
- * 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");
+++ /dev/null
-/* 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
-};
+++ /dev/null
-/*
- * 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");
+++ /dev/null
-/*
- * 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");
+++ /dev/null
-/* 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,
-};
+++ /dev/null
-/*
- * 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");
+++ /dev/null
-/* 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;
+++ /dev/null
-/*
- * 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");
+++ /dev/null
-/*
- * 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);
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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");
+++ /dev/null
-/* 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);
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,
/** 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;
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)
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)
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;
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;
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,
* 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;
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)
-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];
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 {
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);
}
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]);
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;
n--;
truncm = (21 * n - 1) | 1;
freq = ((48000UL << 15) / rate) * n;
- result = (48000UL << 15) / (freq / n);
if (rate >= 24000) {
if (truncm > 239)
truncm = 239;
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;
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)) {
#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"
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;
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",
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,
}
}
+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,
.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[] = {
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),
{.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 \
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;
/*
- *
+ * I2C EEPROM Address
*/
+#define ICE_I2C_EEPROM_ADDR 0xA0
struct snd_ice1712;
}
/* 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);
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;
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
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,
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);
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++;
}
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);
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++;
}
}
}
-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? */
/*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);
}
{
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();
{
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,
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))
config SND_SOC_TOPOLOGY
bool
+config SND_SOC_ACPI
+ tristate
+
# All the supported SoCs
source "sound/soc/adi/Kconfig"
source "sound/soc/amd/Kconfig"
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/
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.
-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
#include <linux/pm_runtime.h>
#include <sound/soc.h>
-
+#include <drm/amd_asic_type.h>
#include "acp.h"
#define PLAYBACK_MIN_NUM_PERIODS 2
#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 |
.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)
* 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;
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]);
* 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;
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);
}
}
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;
/* 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 */
}
/* 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;
/* 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;
}
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);
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);
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;
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;
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;
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);
}
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,
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;
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) &
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);
}
* 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;
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)
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
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);
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) {
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) {
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;
{
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;
}
.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);
--- /dev/null
+/*
+ * 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");
/* 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
#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,
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,
* General Public License for more details.
*/
+#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#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)
#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;
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,
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 */
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) {
/*
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;
/* 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,
.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)
.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
.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)
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);
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;
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)
{
active_slots = hweight32(tx_mask);
- if ((active_slots < 0) || (active_slots > 16))
+ if (active_slots > 16)
return -EINVAL;
/* Slot value must be even */
.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 = { \
.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, \
.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,
};
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
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
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"
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
#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>
}
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",
}
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");
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);
return blocking_notifier_chain_unregister(&arizona->notifier, nb);
}
+int arizona_of_get_audio_pdata(struct arizona *arizona);
+
#endif
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)
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);
case CS4399_CHIP_ID:
regmap_multi_reg_write(cs43130->regmap, dsd_seq,
ARRAY_SIZE(dsd_seq));
+ break;
}
break;
case SND_SOC_DAPM_POST_PMU:
case CS4399_CHIP_ID:
regmap_multi_reg_write(cs43130->regmap, unmute_seq,
ARRAY_SIZE(unmute_seq));
+ break;
}
break;
case SND_SOC_DAPM_PRE_PMD:
regmap_update_bits(cs43130->regmap,
CS43130_DSD_PATH_CTL_1,
CS43130_MUTE_MASK, CS43130_MUTE_EN);
+ break;
}
break;
default:
case CS4399_CHIP_ID:
regmap_multi_reg_write(cs43130->regmap, pcm_seq,
ARRAY_SIZE(pcm_seq));
+ break;
}
break;
case SND_SOC_DAPM_POST_PMU:
case CS4399_CHIP_ID:
regmap_multi_reg_write(cs43130->regmap, unmute_seq,
ARRAY_SIZE(unmute_seq));
+ break;
}
break;
case SND_SOC_DAPM_PRE_PMD:
regmap_update_bits(cs43130->regmap,
CS43130_PCM_PATH_CTL_1,
CS43130_MUTE_MASK, CS43130_MUTE_EN);
+ break;
}
break;
default:
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:
case CS43198_CHIP_ID:
usleep_range(12000, 12010);
regmap_write(cs43130->regmap, CS43130_DXD13, 0);
+ break;
}
regmap_write(cs43130->regmap, CS43130_DXD1, 0);
case CS4399_CHIP_ID:
regmap_multi_reg_write(cs43130->regmap, dac_postpmd_seq,
ARRAY_SIZE(dac_postpmd_seq));
+ break;
}
break;
default:
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,
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,
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);
arizona_init_gpio(codec);
arizona_init_mono(codec);
- arizona_init_notifiers(codec);
ret = wm_adsp2_codec_probe(&priv->core.adsp[1], codec);
if (ret)
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;
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;
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) {
}
/* 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;
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;
}
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;
}
#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)
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;
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);
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)
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;
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;
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;
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;
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;
}
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,
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)) {
}
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,
if (ret < 0)
dev_err(&i2c->dev,
"Failed to register codec: %d\n", ret);
-err_out:
return ret;
}
/*
* 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
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);
/* 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,
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);
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;
}
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)
{
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;
}
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 | \
.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,
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,
/*
* 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
#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)
unsigned int iface;
unsigned int master;
unsigned int digital_gain;
+ bool tdm_mode;
};
#endif
#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)
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;
| 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.
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)
{
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
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,
/* 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);
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;
* 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;
{ }
};
+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",
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 */
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 */
/*
* 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
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");
/*
* 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
/*
* 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
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");
/*
* 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
*/
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]) {
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;
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;
/**
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);
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", },
{},
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,
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);
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)) {
#define RT5645_HWEQ_NUM 57
+#define TIME_TO_POWER_MS 400
+
static const struct regmap_range_cfg rt5645_ranges[] = {
{
.name = "PR",
int jack_type;
bool en_button_func;
bool hp_on;
+ int v_id;
};
static int rt5645_reset(struct snd_soc_codec *codec)
{ "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" },
{ "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)
{
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));
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,
{}
};
-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 = {
{ }
};
-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 = {
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) {
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)
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);
#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>
#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)
#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,
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"),
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)) {
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:
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;
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;
}
};
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)
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)) {
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));
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;
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;
bool hp_mute;
};
+int rt5651_set_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *hp_jack);
#endif /* __RT5651_H__ */
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;
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;
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);
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;
}
rt5659->pll_in = freq_in;
rt5659->pll_out = freq_out;
- rt5659->pll_src = Source;
+ rt5659->pll_src = source;
return 0;
}
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,
};
.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,
};
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;
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 >>
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);
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;
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");
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 */
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,
};
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),
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)
{
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;
}
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",
&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;
}
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);
}
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,
#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)
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)
{
.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[] = {
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),
},
{}
};
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);
#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) */
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];
};
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,
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;
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) {
default:
dev_warn(dev, "Unknown TPA model (%d). Assuming 6130A2\n",
data->id);
+ /* fall through */
case TPA6130A2:
regulator = "Vdd";
break;
#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>
};
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,
return ret;
arizona_init_gpio(codec);
- arizona_init_notifiers(codec);
snd_soc_component_disable_pin(component, "HAPTICS");
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;
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;
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);
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;
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;
{
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
/* 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",
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;
}
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) {
/* 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;
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;
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;
}
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;
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;
switch (clk_id) {
case WM8993_SYSCLK_MCLK:
wm8993->mclk_rate = freq;
+ /* fall through */
case WM8993_SYSCLK_FLL:
wm8993->sysclk_source = clk_id;
break;
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;
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,
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;
if (ret < 0)
return ret;
- arizona_init_notifiers(codec);
-
snd_soc_component_disable_pin(component, "HAPTICS");
priv->core.arizona->dapm = dapm;
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;
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;
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);
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,
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);
return ret;
arizona_init_gpio(codec);
- arizona_init_notifiers(codec);
snd_soc_component_disable_pin(component, "HAPTICS");
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 */
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;
#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>
#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 },
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;
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;
}
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));
}
#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>
#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>
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[] = {
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);
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;
}
mutex_init(&wm9712->lock);
+ wm9712->mfd_pdata = dev_get_platdata(&pdev->dev);
platform_set_drvdata(pdev, wm9712);
return snd_soc_register_codec(&pdev->dev,
#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>
u32 pll_in; /* PLL input frequency */
unsigned int hp_mixer[2];
struct mutex lock;
+ struct wm97xx_platform_data *mfd_pdata;
};
#define HPL_MIXER 0
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);
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;
}
mutex_init(&wm9713->lock);
+ wm9713->mfd_pdata = dev_get_platdata(&pdev->dev);
platform_set_drvdata(pdev, wm9713);
return snd_soc_register_codec(&pdev->dev,
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,
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,
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);
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);
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)
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"
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;
}
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;
}
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;
}
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;
}
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;
}
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;
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;
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;
* @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
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;
* 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,
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))
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
*
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;
regmap_update_bits(regs, CCSR_SSI_SCR, CCSR_SSI_SCR_SSIEN, val);
+ ssi_private->slot_width = slot_width;
+ ssi_private->slots = slots;
+
return 0;
}
.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,
};
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;
};
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)
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)
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++);
#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>
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);
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;
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);
for (i = 0; i < i2s->active_channels; i++)
img_i2s_in_ch_enable(i2s, i);
+ pm_runtime_put(i2s->dev);
+
return 0;
}
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;
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");
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
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;
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;
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);
img_i2s_out_ch_enable(i2s, i);
img_i2s_out_enable(i2s);
+ pm_runtime_put(i2s->dev);
i2s->force_clk_active = force_clk_active;
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);
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;
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" },
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 = {
{
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:
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;
}
#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>
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)
{
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,
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
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;
}
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);
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" },
{}
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 = {
-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
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
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"
# 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/
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,
#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
#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>
#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 */
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;
}
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;
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);
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},
{ },
};
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;
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));
--- /dev/null
+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
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)
{
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;
#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"),
.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,
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;
}
/* 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;
#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),
.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,
.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,
#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 {
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)
#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)
{
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");
}
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,
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);
}
}
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,
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,
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,
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,
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,
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,
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),
.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),
},
{}
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;
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
.dynamic = 1,
.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,
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);
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;
}
/* 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);
/* 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);
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");
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);
#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"},
{"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"),
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)
{
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;
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));
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;
}
.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,
.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) {
#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>
#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[] = {
{"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[] = {
.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;
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;
}
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);
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;
}
{
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 = {
.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,
static int snd_cht_mc_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
int ret_val = 0;
struct cht_mc_private *drv;
/* 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,
*/
#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"
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)
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)
{
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
return ret;
}
- if (ctx->mclk)
- clk_disable_unprepare(ctx->mclk);
+ clk_disable_unprepare(ctx->mclk);
}
return 0;
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)) {
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;
}
.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,
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)
}
/* 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;
/* 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);
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");
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);
#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
},
};
-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)
{
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;
.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,
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");
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");
/* 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),
.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;
/* 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);
}
}
- 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);
* GNU General Public License for more details.
*/
+#include <linux/input.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/core.h>
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.
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;
}
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;
}
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,
};
.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 */
* 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;
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);
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)
{
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;
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"),
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;
# 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
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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");
#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
/* 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;
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)
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) {
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;
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;
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,
.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,
};
#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,
+++ /dev/null
-/*
- * 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);
#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>
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)
* 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",
+++ /dev/null
-/*
- * sst_match_apci.c - SST (LPE) match for ACPI enumeration.
- *
- * Copyright (c) 2013-15, 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 "sst-acpi.h"
-
-static acpi_status sst_acpi_find_name(acpi_handle handle, u32 level,
- void *context, void **ret)
-{
- struct acpi_device *adev;
- const char *name = NULL;
-
- if (acpi_bus_get_device(handle, &adev))
- return AE_OK;
-
- if (adev->status.present && adev->status.functional) {
- name = acpi_dev_name(adev);
- *(const char **)ret = name;
- return AE_CTRL_TERMINATE;
- }
-
- return AE_OK;
-}
-
-const char *sst_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,
- (void **)&name);
-
- if (ACPI_FAILURE(status) || name[0] == '\0')
- return NULL;
-
- return name;
-}
-EXPORT_SYMBOL_GPL(sst_acpi_find_name_from_hid);
-
-static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level,
- void *context, void **ret)
-{
- unsigned long long sta;
- acpi_status status;
-
- *(bool *)context = true;
- status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
- if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_PRESENT))
- *(bool *)context = false;
-
- return AE_OK;
-}
-
-bool sst_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);
-
- if (ACPI_FAILURE(status))
- return false;
-
- return found;
-}
-EXPORT_SYMBOL_GPL(sst_acpi_check_hid);
-
-struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines)
-{
- struct sst_acpi_mach *mach;
-
- for (mach = machines; mach->id[0]; mach++) {
- if (sst_acpi_check_hid(mach->id) == true) {
- if (mach->machine_quirk == NULL)
- return mach;
-
- if (mach->machine_quirk(mach) != NULL)
- return mach;
- }
- }
- return NULL;
-}
-EXPORT_SYMBOL_GPL(sst_acpi_find_machine);
-
-static acpi_status sst_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;
-
- pkg_ctx->data_valid = false;
-
- if (acpi_bus_get_device(handle, &adev))
- return AE_OK;
-
- if (adev->status.present && adev->status.functional) {
- struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
- union acpi_object *myobj = NULL;
-
- status = acpi_evaluate_object_typed(handle, pkg_ctx->name,
- NULL, &buffer,
- ACPI_TYPE_PACKAGE);
- if (ACPI_FAILURE(status))
- return AE_OK;
-
- myobj = buffer.pointer;
- if (!myobj || myobj->package.count != pkg_ctx->length) {
- kfree(buffer.pointer);
- return AE_OK;
- }
-
- status = acpi_extract_package(myobj,
- pkg_ctx->format, pkg_ctx->state);
- if (ACPI_FAILURE(status)) {
- kfree(buffer.pointer);
- return AE_OK;
- }
-
- kfree(buffer.pointer);
- pkg_ctx->data_valid = true;
- return AE_CTRL_TERMINATE;
- }
-
- return AE_OK;
-}
-
-bool sst_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN],
- struct sst_acpi_package_context *ctx)
-{
- acpi_status status;
-
- status = acpi_get_devices(hid, sst_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);
-
-struct sst_acpi_mach *sst_acpi_codec_list(void *arg)
-{
- struct sst_acpi_mach *mach = arg;
- struct sst_codecs *codec_list = (struct sst_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)
- return NULL;
- }
-
- return mach;
-}
-EXPORT_SYMBOL_GPL(sst_acpi_codec_list);
-
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("Intel Common ACPI Match module");
}
#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};
/*
* 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);
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;
}
/*
#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,
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;
}
}
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);
}
.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,
.sig_bits = 32,
},
},
+};
/* BE CPU Dais */
+static struct snd_soc_dai_driver skl_platform_dai[] = {
{
.name = "SSP0 Pin",
.ops = &skl_be_ssp_dai_ops,
},
};
+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;
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);
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;
}
snd_soc_unregister_component(dev);
snd_soc_unregister_platform(dev);
+ kfree(skl->dais);
+
return 0;
}
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;
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));
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);
}
return 0;
+
+free_uuid_list:
+ skl_freeup_uuid_list(skl);
+ return ret;
}
void skl_freeup_uuid_list(struct skl_sst *ctx)
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;
}
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;
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)
{
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;
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);
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;
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;
.io_ops = skl_tplg_kcontrol_ops,
.io_ops_count = ARRAY_SIZE(skl_tplg_kcontrol_ops),
.manifest = skl_manifest_load,
+ .dai_load = skl_dai_load,
};
/*
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) {
#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
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 {
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,
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
#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>
{
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;
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;
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",
.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
},
.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",
.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",
.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
},
.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
},
.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
},
.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
},
{}
};
-static struct sst_acpi_mach sst_glk_devdata[] = {
+static struct snd_soc_acpi_mach sst_glk_devdata[] = {
{
.id = "INT343A",
.drv_name = "glk_alc298s_i2s",
{}
};
-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 */
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 */
struct skl_debug *debugfs;
u8 nr_modules;
struct skl_module **modules;
+ bool use_tplg_pcm;
};
#define skl_to_ebus(s) (&(s)->ebus)
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 {
}
}
-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,
int burst;
};
-extern struct snd_soc_platform_driver kirkwood_soc_platform;
+extern const struct snd_soc_platform_driver kirkwood_soc_platform;
#endif
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;
/* 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);
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);
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,
};
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
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);
#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>
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[] = {
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,
.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),
};
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",
},
};
+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;
}
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,
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;
if (!pm_runtime_status_suspended(&pdev->dev))
i2s_runtime_suspend(&pdev->dev);
- clk_disable_unprepare(i2s->mclk);
clk_disable_unprepare(i2s->hclk);
return 0;
}
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 */
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;
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;
}
}
}
}
+ quirks &= ~(QUIRK_SEC_DAI | QUIRK_SUPPORTS_IDMA);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pri_dai->addr = devm_ioremap_resource(&pdev->dev, res);
#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
{
.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 |
}, {
.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 |
}, {
.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,
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);
}
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;
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)
#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; \
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;
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;
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[] = {
[CLKI] = 0x0,
};
- dev_dbg(dev, "request clock = %d\n", rate);
-
/*
* find suitable clock from
* AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC/AUDIO_CLKI.
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))
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;
}
{
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,
}
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
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);
}
}
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);
}
}
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)
{
rsnd_adg_get_clkin(priv, adg);
rsnd_adg_get_clkout(priv, adg);
+ rsnd_adg_clk_dbg_info(priv, adg);
priv->adg = adg;
}
}
-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)
{
void rsnd_mod_quit(struct rsnd_mod *mod)
{
- if (mod->clk)
- clk_unprepare(mod->clk);
+ clk_unprepare(mod->clk);
mod->clk = NULL;
}
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)
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;
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,
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; \
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));
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);
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,
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,
rsnd_ctu_value_reset,
&ctu->reset, 1);
+ rsnd_flags_set(ctu, KCTRL_INITIALIZED);
+
return ret;
}
#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
*/
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;
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,
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)
priv->dma = dmac;
- return 0;
+ /* dummy mem mod for debug */
+ return rsnd_mod_init(NULL, &mem, &mem_ops, NULL, NULL, 0, 0);
}
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)
((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);
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)
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]);
dvucr |= 0x101;
/* Enable Ramp */
- if (dvc->ren.val) {
+ if (rsnd_kctrl_vals(dvc->ren)) {
dvucr |= 0x10;
/*
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);
}
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 ?
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;
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;
}
* 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
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) \
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);
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)
#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)
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,
#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
*/
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);
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
#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)))
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;
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;
{
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)
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,
* 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;
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;
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));
}
{
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,
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",
}
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) {
of_node_put(np);
goto rsnd_ssi_probe_done;
}
-
+skip:
i++;
}
--- /dev/null
+/*
+ * soc-apci.c - support for ACPI enumeration.
+ *
+ * Copyright (c) 2013-15, 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 <sound/soc-acpi.h>
+
+static acpi_status snd_soc_acpi_find_name(acpi_handle handle, u32 level,
+ void *context, void **ret)
+{
+ struct acpi_device *adev;
+ const char *name = NULL;
+
+ if (acpi_bus_get_device(handle, &adev))
+ return AE_OK;
+
+ if (adev->status.present && adev->status.functional) {
+ name = acpi_dev_name(adev);
+ *(const char **)ret = name;
+ return AE_CTRL_TERMINATE;
+ }
+
+ return AE_OK;
+}
+
+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, snd_soc_acpi_find_name, NULL,
+ (void **)&name);
+
+ if (ACPI_FAILURE(status) || name[0] == '\0')
+ return NULL;
+
+ return name;
+}
+EXPORT_SYMBOL_GPL(snd_soc_acpi_find_name_from_hid);
+
+static acpi_status snd_soc_acpi_mach_match(acpi_handle handle, u32 level,
+ void *context, void **ret)
+{
+ unsigned long long sta;
+ acpi_status status;
+
+ *(bool *)context = true;
+ status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
+ if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_PRESENT))
+ *(bool *)context = false;
+
+ return AE_OK;
+}
+
+bool snd_soc_acpi_check_hid(const u8 hid[ACPI_ID_LEN])
+{
+ acpi_status status;
+ bool found = false;
+
+ status = acpi_get_devices(hid, snd_soc_acpi_mach_match, &found, NULL);
+
+ if (ACPI_FAILURE(status))
+ return false;
+
+ return found;
+}
+EXPORT_SYMBOL_GPL(snd_soc_acpi_check_hid);
+
+struct snd_soc_acpi_mach *
+snd_soc_acpi_find_machine(struct snd_soc_acpi_mach *machines)
+{
+ struct snd_soc_acpi_mach *mach;
+
+ for (mach = machines; mach->id[0]; mach++) {
+ if (snd_soc_acpi_check_hid(mach->id) == true) {
+ if (mach->machine_quirk == NULL)
+ return mach;
+
+ if (mach->machine_quirk(mach) != NULL)
+ return mach;
+ }
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_acpi_find_machine);
+
+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 snd_soc_acpi_package_context *pkg_ctx = context;
+
+ pkg_ctx->data_valid = false;
+
+ if (acpi_bus_get_device(handle, &adev))
+ return AE_OK;
+
+ if (adev->status.present && adev->status.functional) {
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+ union acpi_object *myobj = NULL;
+
+ status = acpi_evaluate_object_typed(handle, pkg_ctx->name,
+ NULL, &buffer,
+ ACPI_TYPE_PACKAGE);
+ if (ACPI_FAILURE(status))
+ return AE_OK;
+
+ myobj = buffer.pointer;
+ if (!myobj || myobj->package.count != pkg_ctx->length) {
+ kfree(buffer.pointer);
+ return AE_OK;
+ }
+
+ status = acpi_extract_package(myobj,
+ pkg_ctx->format, pkg_ctx->state);
+ if (ACPI_FAILURE(status)) {
+ kfree(buffer.pointer);
+ return AE_OK;
+ }
+
+ kfree(buffer.pointer);
+ pkg_ctx->data_valid = true;
+ return AE_CTRL_TERMINATE;
+ }
+
+ return AE_OK;
+}
+
+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, snd_soc_acpi_find_package, ctx, NULL);
+
+ if (ACPI_FAILURE(status) || !ctx->data_valid)
+ return false;
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(snd_soc_acpi_find_package_from_hid);
+
+struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg)
+{
+ 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 (snd_soc_acpi_check_hid(codec_list->codecs[i]) != true)
+ return NULL;
+ }
+
+ return mach;
+}
+EXPORT_SYMBOL_GPL(snd_soc_acpi_codec_list);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ALSA SoC ACPI module");
{
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);
}
}
- 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",
}
}
+ 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) {
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)
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;
}
- 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",
}
}
+ 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) {
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)
{
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;
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)
{
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;
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);
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);
{
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)
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);
{
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);
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)
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;
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)
{
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);
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);
{
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;
}
{
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;
}
{
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);
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);
{
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);
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;
}
{
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;
}
{
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);
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;
}
{
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);
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;
}
{
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;
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,
}
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)
{
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);
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);
}
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,
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;
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);
*/
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;
/**
* 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.
*/
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
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
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
*/
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;
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)
{
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);
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)
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;
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);
/**
}
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);
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
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";
}
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);
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);
{
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) {
{
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++;
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,
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,
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);
}
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),
};
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) {
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);
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;
}
}
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
*/
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;
}
/**
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++)
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"
}
}
- 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"
}
}
+ 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) {
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",
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:
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:
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 */
{
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:"
}
}
- 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:"
}
}
+ 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) {
}
}
- 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,
{
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",
{
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"
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",
}
}
+ 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:
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);
{
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;
}
/* 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);
{
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)
}
}
- 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;
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)
}
}
- 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;
{
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;
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));
{
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);
}
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;
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;
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,
* details.
*/
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/module.h>
#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,
};
{}
};
+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)
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");
}
/* 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;
}
* 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 */
/******************** 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
#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
/**
* 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);
};
#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
* @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
* @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
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;
bool master;
int fmt;
int sync;
+ int synco;
+ int synci;
int fs_length;
int slots;
int slot_width;
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;
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");
}
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;
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:
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;
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 */
}
/* 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);
{
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))
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;
}
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);
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;
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 = {
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;
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");
{
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)
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);
&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;
}
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,
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);
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;
}
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;
}
#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;
/* 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;
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) {
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
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);
{
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);
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;
{
int voice;
unsigned short p1;
- short p2;
int plong;
struct snd_midi_channel *chan;
chan = &port->chset.channels[voice];
p1 = *(unsigned short *) &event[4];
- p2 = *(short *) &event[6];
plong = *(int*) &event[4];
switch (cmd) {
*
* 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;
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
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);
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;
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);
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);
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);
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;
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;
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) {
}
/* 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,
#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);
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;
}
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);
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);
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)
}
/* 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,
*/
#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);
};
/* 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,
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);
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: */
LINE6_PODHD500_1,
LINE6_PODX3,
LINE6_PODX3LIVE,
- LINE6_PODHD500X
+ LINE6_PODHD500X,
+ LINE6_PODHDDESKTOP
};
struct usb_line6_podhd {
};
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);
/* 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);
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) {
{ 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 },
{}
};
.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,
+ },
};
/*
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);
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;
};
/* 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)
{
/* 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: */
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);
/* 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);
/* 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);
} 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;
}
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: */
}
/* 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);
unsigned int pipe;
int length;
unsigned int i;
+ int err;
rep->in = NULL;
ep = kzalloc(sizeof(*ep), GFP_KERNEL);
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)
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,
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;
}
/*
unsigned int i;
unsigned int pipe;
void *buffer;
+ int err;
rep->out = NULL;
ep = kzalloc(sizeof(*ep), GFP_KERNEL);
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;
}
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,
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;
}
rep->out = ep;
return 0;
+
+ error:
+ snd_usbmidi_out_endpoint_delete(ep);
+ return err;
}
/*
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));
/* 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;
}
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)
{
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;
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;
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;
}
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;
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;
}
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);
}
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;