sound updates for 4.6-rc1

After a heavy storm by syzkaller in 4.5 cycle, we have relatively few
 changes in the core at this time while a lot of changes are found in
 the driver side, unsurprisingly.  Below are some highlights:
 
 ALSA core:
 - A few more hardening in ALSA timer codes
 - An extension of sequencer API for advertising the card / pid
 - Small fixes in compress-offload and jack layers
 
 HD-audio:
 - Dynamic PCM assignment in HDMI/DP codec; preparation for upcoming
   DP-MST support
 - Lots of code refactoring for sharing with ASoC SKL driver
 - Regression fixes for Intel HDMI/DP
 - Fixups for CX20724 codec, Lenovo AiO
 
 USB-audio:
 - Add quirk_alias option to make quirk debugging easier
 - Fixes for possible Oops by malformed firmware
 
 Firewire:
 - Add support for FW-1804 in tascam driver
 - Improvements / changes in card registration, multi stream handling,
   etc for DICE
 - Lots of code refactoring
 
 ASoC:
 - Enhancements of still ongoing topology API
 - Lots of commits for Intel Skylake support including HDMI support
 - A few Intel Atom driver updates for recent devices
 - Lots of improvements to the Renesas drivers
 - Capture support for Qualcomm drivers
 - Support for TI DaVinci DRA7xxx devices
 - New machine drivers for Freescale systems with Cirrus CODECs,
   Mediatek systems with RT5650 CODECs
 - New CPU drivers for Allwinner S/PDIF controllers
 - New CODEC drivers for Maxim MAX9867 and MAX98926 and Realtek RT5514
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQIcBAABCAAGBQJW69UAAAoJEGwxgFQ9KSmkoaAQAJ6uBco1gqTmYkJGyLMRRblT
 BxEQ0NMSlPrNEJpR6GOYknrdPZiA4WfxT1zhswDQoNvSKery3bn3aOGfWHA9I+9j
 TRUwHkOAlRCcwgTfy70pRS0fcAx34y9nTcAWsVn9RbrMP3ydkwKyMXRqTwqYr5u5
 UU53PSdwhUO8q/PomvBeip8nvw7zrV+06nVbEMUnIQlgp165n/qq0sRFBVkRBBJ7
 ooqe6VW6F2Es3Zh+W9Vp/qn9OpZEdDCKvmICX1RIFJUgYRRxbL+L021TGjkaXVmT
 Or9L9StRYePZsCo1I1vsYUbYc6+Y3qDmqViGhREHBZ27EY8G1Utk7wy959vt0eFj
 1xHynw36kmjrw/QlPraJBRuYIQh4SRAcXhw+wQdM5rxdp7gDjikhkezHZQWrvQAJ
 5XXitZhVVNa9DRS5ZRwnW5nT/emQ+KBuJyl9gyAL1HaoZoYnDvRkfe9CGpgj7TRP
 wYcnL+rKL9x8eiJY5VTfL9rIxTgNYXvuPPBgdmJEp8qu5de8O1g5UN0xHSGf3yhr
 DVE5r/2J+gYNprsSF9DV6pfFQuh/Efw9XW2IbK6k6WF4labWGeD7rLrI4t9aJcXv
 PJ1FY/sUFhHZhZjHlGbR9emK+BLtZweFvOAvY7dD74ON65D5tYXvHPo0QTc4V5Op
 d0eDg0pcTdFLDqq8ZLLr
 =Cc+v
 -----END PGP SIGNATURE-----

Merge tag 'sound-4.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound

Pull sound updates from Takashi Iwai:
 "After a heavy storm by syzkaller in 4.5 cycle, we have relatively few
  changes in the core at this time while a lot of changes are found in
  the driver side, unsurprisingly.  Below are some highlights:

  ALSA core:
   - A few more hardening in ALSA timer codes
   - An extension of sequencer API for advertising the card / pid
   - Small fixes in compress-offload and jack layers

  HD-audio:
   - Dynamic PCM assignment in HDMI/DP codec; preparation for upcoming
     DP-MST support
   - Lots of code refactoring for sharing with ASoC SKL driver
   - Regression fixes for Intel HDMI/DP
   - Fixups for CX20724 codec, Lenovo AiO

  USB-audio:
   - Add quirk_alias option to make quirk debugging easier
   - Fixes for possible Oops by malformed firmware

  Firewire:
   - Add support for FW-1804 in tascam driver
   - Improvements / changes in card registration, multi stream handling,
     etc for DICE
   - Lots of code refactoring

  ASoC:
   - Enhancements of still ongoing topology API
   - Lots of commits for Intel Skylake support including HDMI support
   - A few Intel Atom driver updates for recent devices
   - Lots of improvements to the Renesas drivers
   - Capture support for Qualcomm drivers
   - Support for TI DaVinci DRA7xxx devices
   - New machine drivers for Freescale systems with Cirrus CODECs,
     Mediatek systems with RT5650 CODECs
   - New CPU drivers for Allwinner S/PDIF controllers
   - New CODEC drivers for Maxim MAX9867 and MAX98926 and Realtek RT5514"

* tag 'sound-4.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (291 commits)
  ALSA: hda - Fix mutex deadlock at HDMI/DP hotplug
  ALSA: ctl: change return value in compatibility layer so that it's the same value in core implementation
  ALSA: mixart: silence an uninitialized variable warning
  ALSA: usb-audio: Add sanity checks for endpoint accesses
  ALSA: usb-audio: Minor code cleanup in create_fixed_stream_quirk()
  ALSA: usb-audio: Fix NULL dereference in create_fixed_stream_quirk()
  ALSA: hda - Limit i915 HDMI binding only for HSW and later
  ALSA: hda - Fix unconditional GPIO toggle via automute
  ALSA: mixart: silence unitialized variable warnings
  ALSA: hda - Fixes double fault in nvhdmi_chmap_cea_alloc_validate_get_type
  ALSA: intel8x0: Add clock quirk entry for AD1981B on IBM ThinkPad X41.
  ALSA: hda - Add new GPU codec ID 0x10de0082 to snd-hda
  ASoC: rsnd: add simplified module explanation
  ASoC: hdac_hdmi: Add broxton device ID
  ASoC: Intel: Bxtn: Add Broxton PCI ID
  ASoC: Intel: Skylake: Move Skylake dsp ops & loader ops
  ASoC: Intel: add dmabuffer to common sst_dsp
  ASoC: Intel: Skylake: Unstatify skl_dsp_enable_core
  ASoC: Intel: Skylake: Fix whitepsace issues
  ASoC: Intel: Skylake: Move module id defines
  ...
This commit is contained in:
Linus Torvalds 2016-03-18 10:05:46 -07:00
commit 021f163d69
202 changed files with 12802 additions and 4392 deletions

View File

@ -0,0 +1,24 @@
Analog Devices ADAU1361/ADAU1461/ADAU1761/ADAU1961/ADAU1381/ADAU1781
Required properties:
- compatible: Should contain one of the following:
"adi,adau1361"
"adi,adau1461"
"adi,adau1761"
"adi,adau1961"
"adi,adau1381"
"adi,adau1781"
- reg: The i2c address. Value depends on the state of ADDR0
and ADDR1, as wired in hardware.
Examples:
#include <dt-bindings/sound/adau17x1.h>
i2c_bus {
adau1361@38 {
compatible = "adi,adau1761";
reg = <0x38>;
};
};

View File

@ -24,6 +24,9 @@ The compatible list for this generic sound card currently:
"fsl,imx-audio-cs42888" "fsl,imx-audio-cs42888"
"fsl,imx-audio-cs427x"
(compatible with CS4271 and CS4272)
"fsl,imx-audio-wm8962" "fsl,imx-audio-wm8962"
(compatible with Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt) (compatible with Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt)
@ -63,6 +66,12 @@ Optional properties:
- audio-asrc : The phandle of ASRC. It can be absent if there's no - audio-asrc : The phandle of ASRC. It can be absent if there's no
need to add ASRC support via DPCM. need to add ASRC support via DPCM.
Optional unless SSI is selected as a CPU DAI:
- mux-int-port : The internal port of the i.MX audio muxer (AUDMUX)
- mux-ext-port : The external port of the i.MX audio muxer
Example: Example:
sound-cs42888 { sound-cs42888 {
compatible = "fsl,imx-audio-cs42888"; compatible = "fsl,imx-audio-cs42888";

View File

@ -0,0 +1,17 @@
max9867 codec
This device supports I2C mode only.
Required properties:
- compatible : "maxim,max9867"
- reg : The chip select number on the I2C bus
Example:
&i2c {
max9867: max9867@0x18 {
compatible = "maxim,max9867";
reg = <0x18>;
};
};

View File

@ -0,0 +1,32 @@
max98926 audio CODEC
This device supports I2C.
Required properties:
- compatible : "maxim,max98926"
- vmon-slot-no : slot number used to send voltage information
or in inteleave mode this will be used as
interleave slot.
- imon-slot-no : slot number used to send current information
- interleave-mode : When using two MAX98926 in a system it is
possible to create ADC data that that will
overflow the frame size. Digital Audio Interleave
mode provides a means to output VMON and IMON data
from two devices on a single DOUT line when running
smaller frames sizes such as 32 BCLKS per LRCLK or
48 BCLKS per LRCLK.
- reg : the I2C address of the device for I2C
Example:
codec: max98926@1a {
compatible = "maxim,max98926";
vmon-slot-no = <0>;
imon-slot-no = <2>;
reg = <0x1a>;
};

View File

@ -0,0 +1,15 @@
MT8173 with RT5650 RT5514 CODECS
Required properties:
- compatible : "mediatek,mt8173-rt5650-rt5514"
- mediatek,audio-codec: the phandles of rt5650 and rt5514 codecs
- mediatek,platform: the phandle of MT8173 ASoC platform
Example:
sound {
compatible = "mediatek,mt8173-rt5650-rt5514";
mediatek,audio-codec = <&rt5650 &rt5514>;
mediatek,platform = <&afe>;
};

View File

@ -0,0 +1,15 @@
MT8173 with RT5650 CODECS
Required properties:
- compatible : "mediatek,mt8173-rt5650"
- mediatek,audio-codec: the phandles of rt5650 codecs
- mediatek,platform: the phandle of MT8173 ASoC platform
Example:
sound {
compatible = "mediatek,mt8173-rt5650";
mediatek,audio-codec = <&rt5650>;
mediatek,platform = <&afe>;
};

View File

@ -1,6 +1,6 @@
Texas Instruments pcm179x DT bindings Texas Instruments pcm179x DT bindings
This driver supports the SPI bus. This driver supports both the I2C and SPI bus.
Required properties: Required properties:
@ -9,6 +9,11 @@ Required properties:
For required properties on SPI, please consult For required properties on SPI, please consult
Documentation/devicetree/bindings/spi/spi-bus.txt Documentation/devicetree/bindings/spi/spi-bus.txt
Required properties on I2C:
- reg: the I2C address
Examples: Examples:
codec_spi: 1792a@0 { codec_spi: 1792a@0 {
@ -16,3 +21,7 @@ Examples:
spi-max-frequency = <600000>; spi-max-frequency = <600000>;
}; };
codec_i2c: 1792a@4c {
compatible = "ti,pcm1792a";
reg = <0x4c>;
};

View File

@ -1,6 +1,337 @@
Renesas R-Car sound Renesas R-Car sound
=============================================
* Modules
=============================================
Renesas R-Car sound is constructed from below modules
(for Gen2 or later)
SCU : Sampling Rate Converter Unit
- SRC : Sampling Rate Converter
- CMD
- CTU : Channel Transfer Unit
- MIX : Mixer
- DVC : Digital Volume and Mute Function
SSIU : Serial Sound Interface Unit
SSI : Serial Sound Interface
See detail of each module's channels, connection, limitation on datasheet
=============================================
* Multi channel
=============================================
Multi channel is supported by Multi-SSI, or TDM-SSI.
Multi-SSI : 6ch case, you can use stereo x 3 SSI
TDM-SSI : 6ch case, you can use TDM
=============================================
* Enable/Disable each modules
=============================================
See datasheet to check SRC/CTU/MIX/DVC connect-limitation.
DT controls enabling/disabling module.
${LINUX}/arch/arm/boot/dts/r8a7790-lager.dts can be good example.
This is example of
Playback: [MEM] -> [SRC2] -> [DVC0] -> [SSIU0/SSI0] -> [codec]
Capture: [MEM] <- [DVC1] <- [SRC3] <- [SSIU1/SSI1] <- [codec]
&rcar_sound {
...
rcar_sound,dai {
dai0 {
playback = <&ssi0 &src2 &dvc0>;
capture = <&ssi1 &src3 &dvc1>;
};
};
};
You can use below.
${LINUX}/arch/arm/boot/dts/r8a7790.dts can be good example.
&src0 &ctu00 &mix0 &dvc0 &ssi0
&src1 &ctu01 &mix1 &dvc1 &ssi1
&src2 &ctu02 &ssi2
&src3 &ctu03 &ssi3
&src4 &ssi4
&src5 &ctu10 &ssi5
&src6 &ctu11 &ssi6
&src7 &ctu12 &ssi7
&src8 &ctu13 &ssi8
&src9 &ssi9
=============================================
* SRC (Sampling Rate Converter)
=============================================
[xx]Hz [yy]Hz
------> [SRC] ------>
SRC can convert [xx]Hz to [yy]Hz. Then, it has below 2 modes
Asynchronous mode: input data / output data are based on different clocks.
you can use this mode on Playback / Capture
Synchronous mode: input data / output data are based on same clocks.
This mode will be used if system doesn't have its input clock,
for example digital TV case.
you can use this mode on Playback
------------------
** Asynchronous mode
------------------
You need to use "renesas,rsrc-card" sound card for it.
example)
sound {
compatible = "renesas,rsrc-card";
...
/*
* SRC Asynchronous mode setting
* Playback:
* All input data will be converted to 48kHz
* Capture:
* Inputed 48kHz data will be converted to
* system specified Hz
*/
convert-rate = <48000>;
...
cpu {
sound-dai = <&rcar_sound>;
};
codec {
...
};
};
------------------
** Synchronous mode
------------------
> amixer set "SRC Out Rate" on
> aplay xxxx.wav
> amixer set "SRC Out Rate" 48000
> amixer set "SRC Out Rate" 44100
=============================================
* CTU (Channel Transfer Unit)
=============================================
[xx]ch [yy]ch
------> [CTU] -------->
CTU can convert [xx]ch to [yy]ch, or exchange outputed channel.
CTU conversion needs matrix settings.
For more detail information, see below
Renesas R-Car datasheet
- Sampling Rate Converter Unit (SCU)
- SCU Operation
- CMD Block
- Functional Blocks in CMD
Renesas R-Car datasheet
- Sampling Rate Converter Unit (SCU)
- Register Description
- CTUn Scale Value exx Register (CTUn_SVxxR)
${LINUX}/sound/soc/sh/rcar/ctu.c
- comment of header
You need to use "renesas,rsrc-card" sound card for it.
example)
sound {
compatible = "renesas,rsrc-card";
...
/*
* CTU setting
* All input data will be converted to 2ch
* as output data
*/
convert-channels = <2>;
...
cpu {
sound-dai = <&rcar_sound>;
};
codec {
...
};
};
Ex) Exchange output channel
Input -> Output
1ch -> 0ch
0ch -> 1ch
example of using matrix
output 0ch = (input 0ch x 0) + (input 1ch x 1)
output 1ch = (input 0ch x 1) + (input 1ch x 0)
amixer set "CTU Reset" on
amixer set "CTU Pass" 9,10
amixer set "CTU SV0" 0,4194304
amixer set "CTU SV1" 4194304,0
example of changing connection
amixer set "CTU Reset" on
amixer set "CTU Pass" 2,1
=============================================
* MIX (Mixer)
=============================================
MIX merges 2 sounds path. You can see 2 sound interface on system,
and these sounds will be merged by MIX.
aplay -D plughw:0,0 xxxx.wav &
aplay -D plughw:0,1 yyyy.wav
You need to use "renesas,rsrc-card" sound card for it.
Ex)
[MEM] -> [SRC1] -> [CTU02] -+-> [MIX0] -> [DVC0] -> [SSI0]
|
[MEM] -> [SRC2] -> [CTU03] -+
sound {
compatible = "renesas,rsrc-card";
...
cpu@0 {
sound-dai = <&rcar_sound 0>;
};
cpu@1 {
sound-dai = <&rcar_sound 1>;
};
codec {
...
};
};
&rcar_sound {
...
rcar_sound,dai {
dai0 {
playback = <&src1 &ctu02 &mix0 &dvc0 &ssi0>;
};
dai1 {
playback = <&src2 &ctu03 &mix0 &dvc0 &ssi0>;
};
};
};
=============================================
* DVC (Digital Volume and Mute Function)
=============================================
DVC controls Playback/Capture volume.
Playback Volume
amixer set "DVC Out" 100%
Capture Volume
amixer set "DVC In" 100%
Playback Mute
amixer set "DVC Out Mute" on
Capture Mute
amixer set "DVC In Mute" on
Volume Ramp
amixer set "DVC Out Ramp Up Rate" "0.125 dB/64 steps"
amixer set "DVC Out Ramp Down Rate" "0.125 dB/512 steps"
amixer set "DVC Out Ramp" on
aplay xxx.wav &
amixer set "DVC Out" 80% // Volume Down
amixer set "DVC Out" 100% // Volume Up
=============================================
* SSIU (Serial Sound Interface Unit)
=============================================
There is no DT settings for SSIU, because SSIU will be automatically
selected via SSI.
SSIU can avoid some under/over run error, because it has some buffer.
But you can't use it if SSI was PIO mode.
In DMA mode, you can select not to use SSIU by using "no-busif" on DT.
&ssi0 {
no-busif;
};
=============================================
* SSI (Serial Sound Interface)
=============================================
** PIO mode
You can use PIO mode which is for connection check by using.
Note: The system will drop non-SSI modules in PIO mode
even though if DT is selecting other modules.
&ssi0 {
pio-transfer
};
** DMA mode without SSIU
You can use DMA without SSIU.
Note: under/over run, or noise are likely to occur
&ssi0 {
no-busif;
};
** PIN sharing
Each SSI can share WS pin. It is based on platform.
This is example if SSI1 want to share WS pin with SSI0
&ssi1 {
shared-pin;
};
** Multi-SSI
You can use Multi-SSI.
This is example of SSI0/SSI1/SSI2 (= for 6ch)
&rcar_sound {
...
rcar_sound,dai {
dai0 {
playback = <&ssi0 &ssi1 &ssi2 &src0 &dvc0>;
};
};
};
** TDM-SSI
You can use TDM with SSI.
This is example of TDM 6ch.
Driver can automatically switches TDM <-> stereo mode in this case.
rsnd_tdm: sound {
compatible = "simple-audio-card";
...
simple-audio-card,cpu {
/* system can use TDM 6ch */
dai-tdm-slot-num = <6>;
sound-dai = <&rcar_sound>;
};
simple-audio-card,codec {
...
};
};
=============================================
Required properties: Required properties:
=============================================
- compatible : "renesas,rcar_sound-<soctype>", fallbacks - compatible : "renesas,rcar_sound-<soctype>", fallbacks
"renesas,rcar_sound-gen1" if generation1, and "renesas,rcar_sound-gen1" if generation1, and
"renesas,rcar_sound-gen2" if generation2 "renesas,rcar_sound-gen2" if generation2
@ -64,7 +395,10 @@ DAI subnode properties:
- playback : list of playback modules - playback : list of playback modules
- capture : list of capture modules - capture : list of capture modules
=============================================
Example: Example:
=============================================
rcar_sound: sound@ec500000 { rcar_sound: sound@ec500000 {
#sound-dai-cells = <1>; #sound-dai-cells = <1>;
@ -250,7 +584,9 @@ rcar_sound: sound@ec500000 {
}; };
}; };
=============================================
Example: simple sound card Example: simple sound card
=============================================
rsnd_ak4643: sound { rsnd_ak4643: sound {
compatible = "simple-audio-card"; compatible = "simple-audio-card";
@ -290,7 +626,9 @@ Example: simple sound card
shared-pin; shared-pin;
}; };
=============================================
Example: simple sound card for TDM Example: simple sound card for TDM
=============================================
rsnd_tdm: sound { rsnd_tdm: sound {
compatible = "simple-audio-card"; compatible = "simple-audio-card";
@ -309,7 +647,9 @@ Example: simple sound card for TDM
}; };
}; };
=============================================
Example: simple sound card for Multi channel Example: simple sound card for Multi channel
=============================================
&rcar_sound { &rcar_sound {
pinctrl-0 = <&sound_pins &sound_clk_pins>; pinctrl-0 = <&sound_pins &sound_clk_pins>;

View File

@ -30,6 +30,7 @@ Optional subnode properties:
- frame-inversion : bool property. Add this if the - frame-inversion : bool property. Add this if the
dai-link uses frame clock inversion. dai-link uses frame clock inversion.
- convert-rate : platform specified sampling rate convert - convert-rate : platform specified sampling rate convert
- convert-channels : platform specified converted channel size (2 - 8 ch)
- audio-prefix : see audio-routing - audio-prefix : see audio-routing
- audio-routing : A list of the connections between audio components. - audio-routing : A list of the connections between audio components.
Each entry is a pair of strings, the first being the connection's sink, Each entry is a pair of strings, the first being the connection's sink,

View File

@ -9,6 +9,7 @@ Required properties:
- "rockchip,rk3066-i2s": for rk3066 - "rockchip,rk3066-i2s": for rk3066
- "rockchip,rk3188-i2s", "rockchip,rk3066-i2s": for rk3188 - "rockchip,rk3188-i2s", "rockchip,rk3066-i2s": for rk3188
- "rockchip,rk3288-i2s", "rockchip,rk3066-i2s": for rk3288 - "rockchip,rk3288-i2s", "rockchip,rk3066-i2s": for rk3288
- "rockchip,rk3399-i2s", "rockchip,rk3066-i2s": for rk3399
- reg: physical base address of the controller and length of memory mapped - reg: physical base address of the controller and length of memory mapped
region. region.
- interrupts: should contain the I2S interrupt. - interrupts: should contain the I2S interrupt.

View File

@ -7,8 +7,12 @@ a fibre cable.
Required properties: Required properties:
- compatible: should be one of the following: - compatible: should be one of the following:
- "rockchip,rk3288-spdif", "rockchip,rk3188-spdif" or - "rockchip,rk3066-spdif"
"rockchip,rk3066-spdif" - "rockchip,rk3188-spdif"
- "rockchip,rk3288-spdif"
- "rockchip,rk3366-spdif"
- "rockchip,rk3368-spdif"
- "rockchip,rk3399-spdif"
- reg: physical base address of the controller and length of memory mapped - reg: physical base address of the controller and length of memory mapped
region. region.
- interrupts: should contain the SPDIF interrupt. - interrupts: should contain the SPDIF interrupt.

View File

@ -0,0 +1,25 @@
RT5514 audio CODEC
This device supports I2C only.
Required properties:
- compatible : "realtek,rt5514".
- reg : The I2C address of the device.
Pins on the device (for linking into audio routes) for RT5514:
* DMIC1L
* DMIC1R
* DMIC2L
* DMIC2R
* AMICL
* AMICR
Example:
codec: rt5514@57 {
compatible = "realtek,rt5514";
reg = <0x57>;
};

View File

@ -8,6 +8,12 @@ Required properties:
- reg : The I2C address of the device. - reg : The I2C address of the device.
Optional properties:
- clocks: The phandle of the master clock to the CODEC.
- clock-names: Should be "mclk".
Pins on the device (for linking into audio routes) for RT5616: Pins on the device (for linking into audio routes) for RT5616:
* IN1P * IN1P

View File

@ -12,6 +12,9 @@ Required properties:
Optional properties: Optional properties:
- clocks: The phandle of the master clock to the CODEC
- clock-names: Should be "mclk"
- realtek,in1-differential - realtek,in1-differential
- realtek,in2-differential - realtek,in2-differential
- realtek,in3-differential - realtek,in3-differential

View File

@ -0,0 +1,39 @@
Allwinner Sony/Philips Digital Interface Format (S/PDIF) Controller
The Allwinner S/PDIF audio block is a transceiver that allows the
processor to receive and transmit digital audio via an coaxial cable or
a fibre cable.
For now only playback is supported.
Required properties:
- compatible : should be one of the following:
- "allwinner,sun4i-a10-spdif": for the Allwinner A10 SoC
- reg : Offset and length of the register set for the device.
- interrupts : Contains the spdif interrupt.
- dmas : Generic dma devicetree binding as described in
Documentation/devicetree/bindings/dma/dma.txt.
- dma-names : Two dmas have to be defined, "tx" and "rx".
- clocks : Contains an entry for each entry in clock-names.
- clock-names : Includes the following entries:
"apb" clock for the spdif bus.
"spdif" clock for spdif controller.
Example:
spdif: spdif@01c21000 {
compatible = "allwinner,sun4i-a10-spdif";
reg = <0x01c21000 0x40>;
interrupts = <13>;
clocks = <&apb0_gates 1>, <&spdif_clk>;
clock-names = "apb", "spdif";
dmas = <&dma 0 2>, <&dma 0 2>;
dma-names = "rx", "tx";
status = "okay";
};

View File

@ -0,0 +1,11 @@
Texas Intstruments ADS117x ADC
Required properties:
- compatible : "ti,ads1174" or "ti,ads1178"
Example:
ads1178 {
compatible = "ti,ads1178";
};

View File

@ -1910,6 +1910,12 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
- Default: 0x0000 - Default: 0x0000
ignore_ctl_error - Ignore any USB-controller regarding mixer ignore_ctl_error - Ignore any USB-controller regarding mixer
interface (default: no) interface (default: no)
autoclock - Enable auto-clock selection for UAC2 devices
(default: yes)
quirk_alias - Quirk alias list, pass strings like
"0123abcd:5678beef", which applies the existing
quirk for the device 5678:beef to a new device
0123:abcd.
This module supports multiple devices, autoprobe and hotplugging. This module supports multiple devices, autoprobe and hotplugging.
@ -1919,6 +1925,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
NB: ignore_ctl_error=1 may help when you get an error at accessing NB: ignore_ctl_error=1 may help when you get an error at accessing
the mixer element such as URB error -22. This happens on some the mixer element such as URB error -22. This happens on some
buggy USB device or the controller. buggy USB device or the controller.
NB: quirk_alias option is provided only for testing / development.
If you want to have a proper support, contact to upstream for
adding the matching quirk in the driver code statically.
Module snd-usb-caiaq Module snd-usb-caiaq
-------------------- --------------------

View File

@ -0,0 +1,74 @@
To support DP MST audio, HD Audio hdmi codec driver introduces virtual pin
and dynamic pcm assignment.
Virtual pin is an extension of per_pin. The most difference of DP MST
from legacy is that DP MST introduces device entry. Each pin can contain
several device entries. Each device entry behaves as a pin.
As each pin may contain several device entries and each codec may contain
several pins, if we use one pcm per per_pin, there will be many PCMs.
The new solution is to create a few PCMs and to dynamically bind pcm to
per_pin. Driver uses spec->dyn_pcm_assign flag to indicate whether to use
the new solution.
PCM
===
To be added
Jack
====
Presume:
- MST must be dyn_pcm_assign, and it is acomp (for Intel scenario);
- NON-MST may or may not be dyn_pcm_assign, it can be acomp or !acomp;
So there are the following scenarios:
a. MST (&& dyn_pcm_assign && acomp)
b. NON-MST && dyn_pcm_assign && acomp
c. NON-MST && !dyn_pcm_assign && !acomp
Below discussion will ignore MST and NON-MST difference as it doesn't
impact on jack handling too much.
Driver uses struct hdmi_pcm pcm[] array in hdmi_spec and snd_jack is
a member of hdmi_pcm. Each pin has one struct hdmi_pcm * pcm pointer.
For !dyn_pcm_assign, per_pin->pcm will assigned to spec->pcm[n] statically.
For dyn_pcm_assign, per_pin->pcm will assigned to spec->pcm[n]
when monitor is hotplugged.
Build Jack
----------
- dyn_pcm_assign
Will not use hda_jack but use snd_jack in spec->pcm_rec[pcm_idx].jack directly.
- !dyn_pcm_assign
Use hda_jack and assign spec->pcm_rec[pcm_idx].jack = jack->jack statically.
Unsolicited Event Enabling
--------------------------
Enable unsolicited event if !acomp.
Monitor Hotplug Event Handling
------------------------------
- acomp
pin_eld_notify() -> check_presence_and_report() -> hdmi_present_sense() ->
sync_eld_via_acomp().
Use directly snd_jack_report() on spec->pcm_rec[pcm_idx].jack for
both dyn_pcm_assign and !dyn_pcm_assign
- !acomp
Hdmi_unsol_event() -> hdmi_intrinsic_event() -> check_presence_and_report() ->
hdmi_present_sense() -> hdmi_prepsent_sense_via_verbs()
Use directly snd_jack_report() on spec->pcm_rec[pcm_idx].jack for dyn_pcm_assign.
Use hda_jack mechanism to handle jack events.
Others to be added later
========================

View File

@ -34,6 +34,7 @@ struct ssc_device *ssc_request(unsigned int ssc_num)
if (ssc->pdev->dev.of_node) { if (ssc->pdev->dev.of_node) {
if (of_alias_get_id(ssc->pdev->dev.of_node, "ssc") if (of_alias_get_id(ssc->pdev->dev.of_node, "ssc")
== ssc_num) { == ssc_num) {
ssc->pdev->id = ssc_num;
ssc_valid = 1; ssc_valid = 1;
break; break;
} }

View File

@ -403,6 +403,18 @@ static inline int drm_eld_size(const uint8_t *eld)
return DRM_ELD_HEADER_BLOCK_SIZE + eld[DRM_ELD_BASELINE_ELD_LEN] * 4; return DRM_ELD_HEADER_BLOCK_SIZE + eld[DRM_ELD_BASELINE_ELD_LEN] * 4;
} }
/**
* drm_eld_get_conn_type - Get device type hdmi/dp connected
* @eld: pointer to an ELD memory structure
*
* The caller need to use %DRM_ELD_CONN_TYPE_HDMI or %DRM_ELD_CONN_TYPE_DP to
* identify the display type connected.
*/
static inline u8 drm_eld_get_conn_type(const uint8_t *eld)
{
return eld[DRM_ELD_SAD_COUNT_CONN_TYPE] & DRM_ELD_CONN_TYPE_MASK;
}
struct edid *drm_do_get_edid(struct drm_connector *connector, struct edid *drm_do_get_edid(struct drm_connector *connector,
int (*get_edid_block)(void *data, u8 *buf, unsigned int block, int (*get_edid_block)(void *data, u8 *buf, unsigned int block,
size_t len), size_t len),

View File

@ -1,5 +1,5 @@
/* /*
* Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961/ADAU1781/ADAU1781 codecs * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961/ADAU1381/ADAU1781 codecs
* *
* Copyright 2011-2014 Analog Devices Inc. * Copyright 2011-2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de> * Author: Lars-Peter Clausen <lars@metafoo.de>

76
include/sound/hda_chmap.h Normal file
View File

@ -0,0 +1,76 @@
/*
* For multichannel support
*/
#ifndef __SOUND_HDA_CHMAP_H
#define __SOUND_HDA_CHMAP_H
#include <sound/pcm.h>
#include <sound/hdaudio.h>
#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80
struct hdac_cea_channel_speaker_allocation {
int ca_index;
int speakers[8];
/* derived values, just for convenience */
int channels;
int spk_mask;
};
struct hdac_chmap;
struct hdac_chmap_ops {
/*
* Helpers for producing the channel map TLVs. These can be overridden
* for devices that have non-standard mapping requirements.
*/
int (*chmap_cea_alloc_validate_get_type)(struct hdac_chmap *chmap,
struct hdac_cea_channel_speaker_allocation *cap, int channels);
void (*cea_alloc_to_tlv_chmap)(struct hdac_chmap *hchmap,
struct hdac_cea_channel_speaker_allocation *cap,
unsigned int *chmap, int channels);
/* check that the user-given chmap is supported */
int (*chmap_validate)(struct hdac_chmap *hchmap, int ca,
int channels, unsigned char *chmap);
void (*get_chmap)(struct hdac_device *hdac, int pcm_idx,
unsigned char *chmap);
void (*set_chmap)(struct hdac_device *hdac, int pcm_idx,
unsigned char *chmap, int prepared);
bool (*is_pcm_attached)(struct hdac_device *hdac, int pcm_idx);
/* get and set channel assigned to each HDMI ASP (audio sample packet) slot */
int (*pin_get_slot_channel)(struct hdac_device *codec,
hda_nid_t pin_nid, int asp_slot);
int (*pin_set_slot_channel)(struct hdac_device *codec,
hda_nid_t pin_nid, int asp_slot, int channel);
void (*set_channel_count)(struct hdac_device *codec,
hda_nid_t cvt_nid, int chs);
};
struct hdac_chmap {
unsigned int channels_max; /* max over all cvts */
struct hdac_chmap_ops ops;
struct hdac_device *hdac;
};
void snd_hdac_register_chmap_ops(struct hdac_device *hdac,
struct hdac_chmap *chmap);
int snd_hdac_channel_allocation(struct hdac_device *hdac, int spk_alloc,
int channels, bool chmap_set,
bool non_pcm, unsigned char *map);
int snd_hdac_get_active_channels(int ca);
void snd_hdac_setup_channel_mapping(struct hdac_chmap *chmap,
hda_nid_t pin_nid, bool non_pcm, int ca,
int channels, unsigned char *map,
bool chmap_set);
void snd_hdac_print_channel_allocation(int spk_alloc, char *buf, int buflen);
struct hdac_cea_channel_speaker_allocation *snd_hdac_get_ch_alloc_from_ca(int ca);
int snd_hdac_chmap_to_spk_mask(unsigned char c);
int snd_hdac_spk_to_chmap(int spk);
int snd_hdac_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx,
struct hdac_chmap *chmap);
#endif /* __SOUND_HDA_CHMAP_H */

View File

@ -168,11 +168,13 @@ int snd_hdac_power_up(struct hdac_device *codec);
int snd_hdac_power_down(struct hdac_device *codec); int snd_hdac_power_down(struct hdac_device *codec);
int snd_hdac_power_up_pm(struct hdac_device *codec); int snd_hdac_power_up_pm(struct hdac_device *codec);
int snd_hdac_power_down_pm(struct hdac_device *codec); int snd_hdac_power_down_pm(struct hdac_device *codec);
int snd_hdac_keep_power_up(struct hdac_device *codec);
#else #else
static inline int snd_hdac_power_up(struct hdac_device *codec) { return 0; } static inline int snd_hdac_power_up(struct hdac_device *codec) { return 0; }
static inline int snd_hdac_power_down(struct hdac_device *codec) { return 0; } static inline int snd_hdac_power_down(struct hdac_device *codec) { return 0; }
static inline int snd_hdac_power_up_pm(struct hdac_device *codec) { return 0; } static inline int snd_hdac_power_up_pm(struct hdac_device *codec) { return 0; }
static inline int snd_hdac_power_down_pm(struct hdac_device *codec) { return 0; } static inline int snd_hdac_power_down_pm(struct hdac_device *codec) { return 0; }
static inline int snd_hdac_keep_power_up(struct hdac_device *codec) { return 0; }
#endif #endif
/* /*

View File

@ -72,14 +72,16 @@ enum snd_jack_types {
#define SND_JACK_SWITCH_TYPES 6 #define SND_JACK_SWITCH_TYPES 6
struct snd_jack { struct snd_jack {
struct input_dev *input_dev;
struct list_head kctl_list; struct list_head kctl_list;
struct snd_card *card; struct snd_card *card;
const char *id;
#ifdef CONFIG_SND_JACK_INPUT_DEV
struct input_dev *input_dev;
int registered; int registered;
int type; int type;
const char *id;
char name[100]; char name[100];
unsigned int key[6]; /* Keep in sync with definitions above */ unsigned int key[6]; /* Keep in sync with definitions above */
#endif /* CONFIG_SND_JACK_INPUT_DEV */
void *private_data; void *private_data;
void (*private_free)(struct snd_jack *); void (*private_free)(struct snd_jack *);
}; };
@ -89,10 +91,11 @@ struct snd_jack {
int snd_jack_new(struct snd_card *card, const char *id, int type, int snd_jack_new(struct snd_card *card, const char *id, int type,
struct snd_jack **jack, bool initial_kctl, bool phantom_jack); struct snd_jack **jack, bool initial_kctl, bool phantom_jack);
int snd_jack_add_new_kctl(struct snd_jack *jack, const char * name, int mask); int snd_jack_add_new_kctl(struct snd_jack *jack, const char * name, int mask);
#ifdef CONFIG_SND_JACK_INPUT_DEV
void snd_jack_set_parent(struct snd_jack *jack, struct device *parent); void snd_jack_set_parent(struct snd_jack *jack, struct device *parent);
int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type, int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type,
int keytype); int keytype);
#endif
void snd_jack_report(struct snd_jack *jack, int status); void snd_jack_report(struct snd_jack *jack, int status);
#else #else
@ -107,6 +110,13 @@ static inline int snd_jack_add_new_kctl(struct snd_jack *jack, const char * name
return 0; return 0;
} }
static inline void snd_jack_report(struct snd_jack *jack, int status)
{
}
#endif
#if !defined(CONFIG_SND_JACK) || !defined(CONFIG_SND_JACK_INPUT_DEV)
static inline void snd_jack_set_parent(struct snd_jack *jack, static inline void snd_jack_set_parent(struct snd_jack *jack,
struct device *parent) struct device *parent)
{ {
@ -118,11 +128,6 @@ static inline int snd_jack_set_key(struct snd_jack *jack,
{ {
return 0; return 0;
} }
#endif /* !CONFIG_SND_JACK || !CONFIG_SND_JACK_INPUT_DEV */
static inline void snd_jack_report(struct snd_jack *jack, int status)
{
}
#endif
#endif #endif

View File

@ -1093,6 +1093,8 @@ unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate);
unsigned int snd_pcm_rate_bit_to_rate(unsigned int rate_bit); unsigned int snd_pcm_rate_bit_to_rate(unsigned int rate_bit);
unsigned int snd_pcm_rate_mask_intersect(unsigned int rates_a, unsigned int snd_pcm_rate_mask_intersect(unsigned int rates_a,
unsigned int rates_b); unsigned int rates_b);
unsigned int snd_pcm_rate_range_to_bits(unsigned int rate_min,
unsigned int rate_max);
/** /**
* snd_pcm_set_runtime_buffer - Set the PCM runtime buffer * snd_pcm_set_runtime_buffer - Set the PCM runtime buffer

View File

@ -56,12 +56,6 @@ struct snd_soc_dobj_widget {
unsigned int kcontrol_enum:1; /* this widget is an enum kcontrol */ unsigned int kcontrol_enum:1; /* this widget is an enum kcontrol */
}; };
/* dynamic PCM DAI object */
struct snd_soc_dobj_pcm_dai {
struct snd_soc_tplg_pcm_dai *pd;
unsigned int count;
};
/* generic dynamic object - all dynamic objects belong to this struct */ /* generic dynamic object - all dynamic objects belong to this struct */
struct snd_soc_dobj { struct snd_soc_dobj {
enum snd_soc_dobj_type type; enum snd_soc_dobj_type type;
@ -71,7 +65,6 @@ struct snd_soc_dobj {
union { union {
struct snd_soc_dobj_control control; struct snd_soc_dobj_control control;
struct snd_soc_dobj_widget widget; struct snd_soc_dobj_widget widget;
struct snd_soc_dobj_pcm_dai pcm_dai;
}; };
void *private; /* core does not touch this */ void *private; /* core does not touch this */
}; };
@ -126,10 +119,16 @@ struct snd_soc_tplg_ops {
int (*widget_unload)(struct snd_soc_component *, int (*widget_unload)(struct snd_soc_component *,
struct snd_soc_dobj *); struct snd_soc_dobj *);
/* FE - used for any driver specific init */ /* FE DAI - used for any driver specific init */
int (*pcm_dai_load)(struct snd_soc_component *, int (*dai_load)(struct snd_soc_component *,
struct snd_soc_tplg_pcm_dai *pcm_dai, int num_fe); struct snd_soc_dai_driver *dai_drv);
int (*pcm_dai_unload)(struct snd_soc_component *, int (*dai_unload)(struct snd_soc_component *,
struct snd_soc_dobj *);
/* DAI link - used for any driver specific init */
int (*link_load)(struct snd_soc_component *,
struct snd_soc_dai_link *link);
int (*link_unload)(struct snd_soc_component *,
struct snd_soc_dobj *); struct snd_soc_dobj *);
/* callback to handle vendor bespoke data */ /* callback to handle vendor bespoke data */

View File

@ -27,7 +27,6 @@
#include <sound/compress_driver.h> #include <sound/compress_driver.h>
#include <sound/control.h> #include <sound/control.h>
#include <sound/ac97_codec.h> #include <sound/ac97_codec.h>
#include <sound/soc-topology.h>
/* /*
* Convenience kcontrol builders * Convenience kcontrol builders
@ -404,6 +403,7 @@ struct snd_soc_jack_zone;
struct snd_soc_jack_pin; struct snd_soc_jack_pin;
#include <sound/soc-dapm.h> #include <sound/soc-dapm.h>
#include <sound/soc-dpcm.h> #include <sound/soc-dpcm.h>
#include <sound/soc-topology.h>
struct snd_soc_jack_gpio; struct snd_soc_jack_gpio;

View File

@ -25,7 +25,7 @@
#include <sound/asound.h> #include <sound/asound.h>
/** version of the sequencer */ /** version of the sequencer */
#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION (1, 0, 1) #define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 2)
/** /**
* definition of sequencer event types * definition of sequencer event types
@ -357,7 +357,9 @@ struct snd_seq_client_info {
unsigned char event_filter[32]; /* event filter bitmap */ unsigned char event_filter[32]; /* event filter bitmap */
int num_ports; /* RO: number of ports */ int num_ports; /* RO: number of ports */
int event_lost; /* number of lost events */ int event_lost; /* number of lost events */
char reserved[64]; /* for future use */ int card; /* RO: card number[kernel] */
int pid; /* RO: pid[user] */
char reserved[56]; /* for future use */
}; };
@ -594,14 +596,8 @@ struct snd_seq_query_subs {
#define SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS _IOWR('S', 0x40, struct snd_seq_queue_status) #define SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS _IOWR('S', 0x40, struct snd_seq_queue_status)
#define SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO _IOWR('S', 0x41, struct snd_seq_queue_tempo) #define SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO _IOWR('S', 0x41, struct snd_seq_queue_tempo)
#define SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO _IOW ('S', 0x42, struct snd_seq_queue_tempo) #define SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO _IOW ('S', 0x42, struct snd_seq_queue_tempo)
#define SNDRV_SEQ_IOCTL_GET_QUEUE_OWNER _IOWR('S', 0x43, struct snd_seq_queue_owner)
#define SNDRV_SEQ_IOCTL_SET_QUEUE_OWNER _IOW ('S', 0x44, struct snd_seq_queue_owner)
#define SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER _IOWR('S', 0x45, struct snd_seq_queue_timer) #define SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER _IOWR('S', 0x45, struct snd_seq_queue_timer)
#define SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER _IOW ('S', 0x46, struct snd_seq_queue_timer) #define SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER _IOW ('S', 0x46, struct snd_seq_queue_timer)
/* XXX
#define SNDRV_SEQ_IOCTL_GET_QUEUE_SYNC _IOWR('S', 0x53, struct snd_seq_queue_sync)
#define SNDRV_SEQ_IOCTL_SET_QUEUE_SYNC _IOW ('S', 0x54, struct snd_seq_queue_sync)
*/
#define SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT _IOWR('S', 0x49, struct snd_seq_queue_client) #define SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT _IOWR('S', 0x49, struct snd_seq_queue_client)
#define SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT _IOW ('S', 0x4a, struct snd_seq_queue_client) #define SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT _IOW ('S', 0x4a, struct snd_seq_queue_client)
#define SNDRV_SEQ_IOCTL_GET_CLIENT_POOL _IOWR('S', 0x4b, struct snd_seq_client_pool) #define SNDRV_SEQ_IOCTL_GET_CLIENT_POOL _IOWR('S', 0x4b, struct snd_seq_client_pool)

View File

@ -23,7 +23,11 @@
#ifndef _UAPI__SOUND_ASOUND_H #ifndef _UAPI__SOUND_ASOUND_H
#define _UAPI__SOUND_ASOUND_H #define _UAPI__SOUND_ASOUND_H
#if defined(__KERNEL__) || defined(__linux__)
#include <linux/types.h> #include <linux/types.h>
#else
#include <sys/ioctl.h>
#endif
#ifndef __KERNEL__ #ifndef __KERNEL__
#include <stdlib.h> #include <stdlib.h>

View File

@ -24,12 +24,15 @@ config SND_RAWMIDI
config SND_COMPRESS_OFFLOAD config SND_COMPRESS_OFFLOAD
tristate tristate
# To be effective this also requires INPUT - users should say:
# select SND_JACK if INPUT=y || INPUT=SND
# to avoid having to force INPUT on.
config SND_JACK config SND_JACK
bool bool
# enable input device support in jack layer
config SND_JACK_INPUT_DEV
bool
depends on SND_JACK
default y if INPUT=y || INPUT=SND
config SND_SEQUENCER config SND_SEQUENCER
tristate "Sequencer support" tristate "Sequencer support"
select SND_TIMER select SND_TIMER

View File

@ -69,11 +69,14 @@ struct snd_compr_file {
/* /*
* a note on stream states used: * a note on stream states used:
* we use follwing states in the compressed core * we use following states in the compressed core
* SNDRV_PCM_STATE_OPEN: When stream has been opened. * SNDRV_PCM_STATE_OPEN: When stream has been opened.
* SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by
* calling SNDRV_COMPRESS_SET_PARAMS. running streams will come to this * calling SNDRV_COMPRESS_SET_PARAMS. Running streams will come to this
* state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain. * state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain.
* SNDRV_PCM_STATE_PREPARED: When a stream has been written to (for
* playback only). User after setting up stream writes the data buffer
* before starting the stream.
* SNDRV_PCM_STATE_RUNNING: When stream has been started and is * SNDRV_PCM_STATE_RUNNING: When stream has been started and is
* decoding/encoding and rendering/capturing data. * decoding/encoding and rendering/capturing data.
* SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done
@ -286,6 +289,7 @@ static ssize_t snd_compr_write(struct file *f, const char __user *buf,
mutex_lock(&stream->device->lock); mutex_lock(&stream->device->lock);
/* write is allowed when stream is running or has been steup */ /* write is allowed when stream is running or has been steup */
if (stream->runtime->state != SNDRV_PCM_STATE_SETUP && if (stream->runtime->state != SNDRV_PCM_STATE_SETUP &&
stream->runtime->state != SNDRV_PCM_STATE_PREPARED &&
stream->runtime->state != SNDRV_PCM_STATE_RUNNING) { stream->runtime->state != SNDRV_PCM_STATE_RUNNING) {
mutex_unlock(&stream->device->lock); mutex_unlock(&stream->device->lock);
return -EBADFD; return -EBADFD;
@ -700,7 +704,7 @@ static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
/* /*
* We are called with lock held. So drop the lock while we wait for * We are called with lock held. So drop the lock while we wait for
* drain complete notfication from the driver * drain complete notification from the driver
* *
* It is expected that driver will notify the drain completion and then * It is expected that driver will notify the drain completion and then
* stream will be moved to SETUP state, even if draining resulted in an * stream will be moved to SETUP state, even if draining resulted in an
@ -755,7 +759,7 @@ static int snd_compr_next_track(struct snd_compr_stream *stream)
if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
return -EPERM; return -EPERM;
/* you can signal next track isf this is intended to be a gapless stream /* you can signal next track if this is intended to be a gapless stream
* and current track metadata is set * and current track metadata is set
*/ */
if (stream->metadata_set == false) if (stream->metadata_set == false)

View File

@ -196,7 +196,7 @@ static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id,
kctl = snd_ctl_find_id(card, id); kctl = snd_ctl_find_id(card, id);
if (! kctl) { if (! kctl) {
up_read(&card->controls_rwsem); up_read(&card->controls_rwsem);
return -ENXIO; return -ENOENT;
} }
info = kzalloc(sizeof(*info), GFP_KERNEL); info = kzalloc(sizeof(*info), GFP_KERNEL);
if (info == NULL) { if (info == NULL) {

View File

@ -32,6 +32,7 @@ struct snd_jack_kctl {
unsigned int mask_bits; /* only masked status bits are reported via kctl */ unsigned int mask_bits; /* only masked status bits are reported via kctl */
}; };
#ifdef CONFIG_SND_JACK_INPUT_DEV
static int jack_switch_types[SND_JACK_SWITCH_TYPES] = { static int jack_switch_types[SND_JACK_SWITCH_TYPES] = {
SW_HEADPHONE_INSERT, SW_HEADPHONE_INSERT,
SW_MICROPHONE_INSERT, SW_MICROPHONE_INSERT,
@ -40,9 +41,11 @@ static int jack_switch_types[SND_JACK_SWITCH_TYPES] = {
SW_VIDEOOUT_INSERT, SW_VIDEOOUT_INSERT,
SW_LINEIN_INSERT, SW_LINEIN_INSERT,
}; };
#endif /* CONFIG_SND_JACK_INPUT_DEV */
static int snd_jack_dev_disconnect(struct snd_device *device) static int snd_jack_dev_disconnect(struct snd_device *device)
{ {
#ifdef CONFIG_SND_JACK_INPUT_DEV
struct snd_jack *jack = device->device_data; struct snd_jack *jack = device->device_data;
if (!jack->input_dev) if (!jack->input_dev)
@ -55,6 +58,7 @@ static int snd_jack_dev_disconnect(struct snd_device *device)
else else
input_free_device(jack->input_dev); input_free_device(jack->input_dev);
jack->input_dev = NULL; jack->input_dev = NULL;
#endif /* CONFIG_SND_JACK_INPUT_DEV */
return 0; return 0;
} }
@ -79,6 +83,7 @@ static int snd_jack_dev_free(struct snd_device *device)
return 0; return 0;
} }
#ifdef CONFIG_SND_JACK_INPUT_DEV
static int snd_jack_dev_register(struct snd_device *device) static int snd_jack_dev_register(struct snd_device *device)
{ {
struct snd_jack *jack = device->device_data; struct snd_jack *jack = device->device_data;
@ -116,6 +121,7 @@ static int snd_jack_dev_register(struct snd_device *device)
return err; return err;
} }
#endif /* CONFIG_SND_JACK_INPUT_DEV */
static void snd_jack_kctl_private_free(struct snd_kcontrol *kctl) static void snd_jack_kctl_private_free(struct snd_kcontrol *kctl)
{ {
@ -209,11 +215,12 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
struct snd_jack *jack; struct snd_jack *jack;
struct snd_jack_kctl *jack_kctl = NULL; struct snd_jack_kctl *jack_kctl = NULL;
int err; int err;
int i;
static struct snd_device_ops ops = { static struct snd_device_ops ops = {
.dev_free = snd_jack_dev_free, .dev_free = snd_jack_dev_free,
#ifdef CONFIG_SND_JACK_INPUT_DEV
.dev_register = snd_jack_dev_register, .dev_register = snd_jack_dev_register,
.dev_disconnect = snd_jack_dev_disconnect, .dev_disconnect = snd_jack_dev_disconnect,
#endif /* CONFIG_SND_JACK_INPUT_DEV */
}; };
if (initial_kctl) { if (initial_kctl) {
@ -230,6 +237,9 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
/* don't creat input device for phantom jack */ /* don't creat input device for phantom jack */
if (!phantom_jack) { if (!phantom_jack) {
#ifdef CONFIG_SND_JACK_INPUT_DEV
int i;
jack->input_dev = input_allocate_device(); jack->input_dev = input_allocate_device();
if (jack->input_dev == NULL) { if (jack->input_dev == NULL) {
err = -ENOMEM; err = -ENOMEM;
@ -245,6 +255,7 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
input_set_capability(jack->input_dev, EV_SW, input_set_capability(jack->input_dev, EV_SW,
jack_switch_types[i]); jack_switch_types[i]);
#endif /* CONFIG_SND_JACK_INPUT_DEV */
} }
err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops); err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops);
@ -262,13 +273,16 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
return 0; return 0;
fail_input: fail_input:
#ifdef CONFIG_SND_JACK_INPUT_DEV
input_free_device(jack->input_dev); input_free_device(jack->input_dev);
#endif
kfree(jack->id); kfree(jack->id);
kfree(jack); kfree(jack);
return err; return err;
} }
EXPORT_SYMBOL(snd_jack_new); EXPORT_SYMBOL(snd_jack_new);
#ifdef CONFIG_SND_JACK_INPUT_DEV
/** /**
* snd_jack_set_parent - Set the parent device for a jack * snd_jack_set_parent - Set the parent device for a jack
* *
@ -326,10 +340,10 @@ int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type,
jack->type |= type; jack->type |= type;
jack->key[key] = keytype; jack->key[key] = keytype;
return 0; return 0;
} }
EXPORT_SYMBOL(snd_jack_set_key); EXPORT_SYMBOL(snd_jack_set_key);
#endif /* CONFIG_SND_JACK_INPUT_DEV */
/** /**
* snd_jack_report - Report the current status of a jack * snd_jack_report - Report the current status of a jack
@ -340,7 +354,9 @@ EXPORT_SYMBOL(snd_jack_set_key);
void snd_jack_report(struct snd_jack *jack, int status) void snd_jack_report(struct snd_jack *jack, int status)
{ {
struct snd_jack_kctl *jack_kctl; struct snd_jack_kctl *jack_kctl;
#ifdef CONFIG_SND_JACK_INPUT_DEV
int i; int i;
#endif
if (!jack) if (!jack)
return; return;
@ -349,6 +365,7 @@ void snd_jack_report(struct snd_jack *jack, int status)
snd_kctl_jack_report(jack->card, jack_kctl->kctl, snd_kctl_jack_report(jack->card, jack_kctl->kctl,
status & jack_kctl->mask_bits); status & jack_kctl->mask_bits);
#ifdef CONFIG_SND_JACK_INPUT_DEV
if (!jack->input_dev) if (!jack->input_dev)
return; return;
@ -369,6 +386,6 @@ void snd_jack_report(struct snd_jack *jack, int status)
} }
input_sync(jack->input_dev); input_sync(jack->input_dev);
#endif /* CONFIG_SND_JACK_INPUT_DEV */
} }
EXPORT_SYMBOL(snd_jack_report); EXPORT_SYMBOL(snd_jack_report);

View File

@ -322,7 +322,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
char name[16]; char name[16];
snd_pcm_debug_name(substream, name, sizeof(name)); snd_pcm_debug_name(substream, name, sizeof(name));
pcm_err(substream->pcm, pcm_err(substream->pcm,
"BUG: %s, pos = %ld, buffer size = %ld, period size = %ld\n", "invalid position: %s, pos = %ld, buffer size = %ld, period size = %ld\n",
name, pos, runtime->buffer_size, name, pos, runtime->buffer_size,
runtime->period_size); runtime->period_size);
} }

View File

@ -565,3 +565,33 @@ unsigned int snd_pcm_rate_mask_intersect(unsigned int rates_a,
return rates_a & rates_b; return rates_a & rates_b;
} }
EXPORT_SYMBOL_GPL(snd_pcm_rate_mask_intersect); EXPORT_SYMBOL_GPL(snd_pcm_rate_mask_intersect);
/**
* snd_pcm_rate_range_to_bits - converts rate range to SNDRV_PCM_RATE_xxx bit
* @rate_min: the minimum sample rate
* @rate_max: the maximum sample rate
*
* This function has an implicit assumption: the rates in the given range have
* only the pre-defined rates like 44100 or 16000.
*
* Return: The SNDRV_PCM_RATE_xxx flag that corresponds to the given rate range,
* or SNDRV_PCM_RATE_KNOT for an unknown range.
*/
unsigned int snd_pcm_rate_range_to_bits(unsigned int rate_min,
unsigned int rate_max)
{
unsigned int rates = 0;
int i;
for (i = 0; i < snd_pcm_known_rates.count; i++) {
if (snd_pcm_known_rates.list[i] >= rate_min
&& snd_pcm_known_rates.list[i] <= rate_max)
rates |= 1 << i;
}
if (!rates)
rates = SNDRV_PCM_RATE_KNOT;
return rates;
}
EXPORT_SYMBOL_GPL(snd_pcm_rate_range_to_bits);

View File

@ -364,6 +364,7 @@ static int snd_seq_open(struct inode *inode, struct file *file)
/* fill client data */ /* fill client data */
user->file = file; user->file = file;
sprintf(client->name, "Client-%d", c); sprintf(client->name, "Client-%d", c);
client->data.user.owner = get_pid(task_pid(current));
/* make others aware this new client */ /* make others aware this new client */
snd_seq_system_client_ev_client_start(c); snd_seq_system_client_ev_client_start(c);
@ -380,6 +381,7 @@ static int snd_seq_release(struct inode *inode, struct file *file)
seq_free_client(client); seq_free_client(client);
if (client->data.user.fifo) if (client->data.user.fifo)
snd_seq_fifo_delete(&client->data.user.fifo); snd_seq_fifo_delete(&client->data.user.fifo);
put_pid(client->data.user.owner);
kfree(client); kfree(client);
} }
@ -1197,6 +1199,17 @@ static void get_client_info(struct snd_seq_client *cptr,
info->event_lost = cptr->event_lost; info->event_lost = cptr->event_lost;
memcpy(info->event_filter, cptr->event_filter, 32); memcpy(info->event_filter, cptr->event_filter, 32);
info->num_ports = cptr->num_ports; info->num_ports = cptr->num_ports;
if (cptr->type == USER_CLIENT)
info->pid = pid_vnr(cptr->data.user.owner);
else
info->pid = -1;
if (cptr->type == KERNEL_CLIENT)
info->card = cptr->data.kernel.card ? cptr->data.kernel.card->number : -1;
else
info->card = -1;
memset(info->reserved, 0, sizeof(info->reserved)); memset(info->reserved, 0, sizeof(info->reserved));
} }
@ -2271,6 +2284,7 @@ int snd_seq_create_kernel_client(struct snd_card *card, int client_index,
client->accept_input = 1; client->accept_input = 1;
client->accept_output = 1; client->accept_output = 1;
client->data.kernel.card = card;
va_start(args, name_fmt); va_start(args, name_fmt);
vsnprintf(client->name, sizeof(client->name), name_fmt, args); vsnprintf(client->name, sizeof(client->name), name_fmt, args);

View File

@ -33,6 +33,7 @@
struct snd_seq_user_client { struct snd_seq_user_client {
struct file *file; /* file struct of client */ struct file *file; /* file struct of client */
/* ... */ /* ... */
struct pid *owner;
/* fifo */ /* fifo */
struct snd_seq_fifo *fifo; /* queue for incoming events */ struct snd_seq_fifo *fifo; /* queue for incoming events */
@ -41,6 +42,7 @@ struct snd_seq_user_client {
struct snd_seq_kernel_client { struct snd_seq_kernel_client {
/* ... */ /* ... */
struct snd_card *card;
}; };

View File

@ -305,8 +305,6 @@ int snd_timer_open(struct snd_timer_instance **ti,
return 0; return 0;
} }
static int _snd_timer_stop(struct snd_timer_instance *timeri, int event);
/* /*
* close a timer instance * close a timer instance
*/ */
@ -318,25 +316,14 @@ int snd_timer_close(struct snd_timer_instance *timeri)
if (snd_BUG_ON(!timeri)) if (snd_BUG_ON(!timeri))
return -ENXIO; return -ENXIO;
mutex_lock(&register_mutex);
list_del(&timeri->open_list);
/* force to stop the timer */ /* force to stop the timer */
snd_timer_stop(timeri); snd_timer_stop(timeri);
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { timer = timeri->timer;
/* wait, until the active callback is finished */ if (timer) {
spin_lock_irq(&slave_active_lock);
while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
spin_unlock_irq(&slave_active_lock);
udelay(10);
spin_lock_irq(&slave_active_lock);
}
spin_unlock_irq(&slave_active_lock);
mutex_lock(&register_mutex);
list_del(&timeri->open_list);
mutex_unlock(&register_mutex);
} else {
timer = timeri->timer;
if (snd_BUG_ON(!timer))
goto out;
/* wait, until the active callback is finished */ /* wait, until the active callback is finished */
spin_lock_irq(&timer->lock); spin_lock_irq(&timer->lock);
while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
@ -345,11 +332,7 @@ int snd_timer_close(struct snd_timer_instance *timeri)
spin_lock_irq(&timer->lock); spin_lock_irq(&timer->lock);
} }
spin_unlock_irq(&timer->lock); spin_unlock_irq(&timer->lock);
mutex_lock(&register_mutex);
list_del(&timeri->open_list);
if (list_empty(&timer->open_list_head) &&
timer->hw.close)
timer->hw.close(timer);
/* remove slave links */ /* remove slave links */
spin_lock_irq(&slave_active_lock); spin_lock_irq(&slave_active_lock);
spin_lock(&timer->lock); spin_lock(&timer->lock);
@ -363,18 +346,27 @@ int snd_timer_close(struct snd_timer_instance *timeri)
} }
spin_unlock(&timer->lock); spin_unlock(&timer->lock);
spin_unlock_irq(&slave_active_lock); spin_unlock_irq(&slave_active_lock);
/* release a card refcount for safe disconnection */
if (timer->card) /* slave doesn't need to release timer resources below */
put_device(&timer->card->card_dev); if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
mutex_unlock(&register_mutex); timer = NULL;
} }
out:
if (timeri->private_free) if (timeri->private_free)
timeri->private_free(timeri); timeri->private_free(timeri);
kfree(timeri->owner); kfree(timeri->owner);
kfree(timeri); kfree(timeri);
if (timer)
if (timer) {
if (list_empty(&timer->open_list_head) && timer->hw.close)
timer->hw.close(timer);
/* release a card refcount for safe disconnection */
if (timer->card)
put_device(&timer->card->card_dev);
module_put(timer->module); module_put(timer->module);
}
mutex_unlock(&register_mutex);
return 0; return 0;
} }
@ -395,7 +387,6 @@ unsigned long snd_timer_resolution(struct snd_timer_instance *timeri)
static void snd_timer_notify1(struct snd_timer_instance *ti, int event) static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
{ {
struct snd_timer *timer; struct snd_timer *timer;
unsigned long flags;
unsigned long resolution = 0; unsigned long resolution = 0;
struct snd_timer_instance *ts; struct snd_timer_instance *ts;
struct timespec tstamp; struct timespec tstamp;
@ -419,34 +410,66 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
return; return;
if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
return; return;
spin_lock_irqsave(&timer->lock, flags);
list_for_each_entry(ts, &ti->slave_active_head, active_list) list_for_each_entry(ts, &ti->slave_active_head, active_list)
if (ts->ccallback) if (ts->ccallback)
ts->ccallback(ts, event + 100, &tstamp, resolution); ts->ccallback(ts, event + 100, &tstamp, resolution);
spin_unlock_irqrestore(&timer->lock, flags);
} }
static int snd_timer_start1(struct snd_timer *timer, struct snd_timer_instance *timeri, /* start/continue a master timer */
unsigned long sticks) static int snd_timer_start1(struct snd_timer_instance *timeri,
bool start, unsigned long ticks)
{ {
struct snd_timer *timer;
int result;
unsigned long flags;
timer = timeri->timer;
if (!timer)
return -EINVAL;
spin_lock_irqsave(&timer->lock, flags);
if (timer->card && timer->card->shutdown) {
result = -ENODEV;
goto unlock;
}
if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
SNDRV_TIMER_IFLG_START)) {
result = -EBUSY;
goto unlock;
}
if (start)
timeri->ticks = timeri->cticks = ticks;
else if (!timeri->cticks)
timeri->cticks = 1;
timeri->pticks = 0;
list_move_tail(&timeri->active_list, &timer->active_list_head); list_move_tail(&timeri->active_list, &timer->active_list_head);
if (timer->running) { if (timer->running) {
if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
goto __start_now; goto __start_now;
timer->flags |= SNDRV_TIMER_FLG_RESCHED; timer->flags |= SNDRV_TIMER_FLG_RESCHED;
timeri->flags |= SNDRV_TIMER_IFLG_START; timeri->flags |= SNDRV_TIMER_IFLG_START;
return 1; /* delayed start */ result = 1; /* delayed start */
} else { } else {
timer->sticks = sticks; if (start)
timer->sticks = ticks;
timer->hw.start(timer); timer->hw.start(timer);
__start_now: __start_now:
timer->running++; timer->running++;
timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
return 0; result = 0;
} }
snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
SNDRV_TIMER_EVENT_CONTINUE);
unlock:
spin_unlock_irqrestore(&timer->lock, flags);
return result;
} }
static int snd_timer_start_slave(struct snd_timer_instance *timeri) /* start/continue a slave timer */
static int snd_timer_start_slave(struct snd_timer_instance *timeri,
bool start)
{ {
unsigned long flags; unsigned long flags;
@ -460,88 +483,37 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri)
spin_lock(&timeri->timer->lock); spin_lock(&timeri->timer->lock);
list_add_tail(&timeri->active_list, list_add_tail(&timeri->active_list,
&timeri->master->slave_active_head); &timeri->master->slave_active_head);
snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
SNDRV_TIMER_EVENT_CONTINUE);
spin_unlock(&timeri->timer->lock); spin_unlock(&timeri->timer->lock);
} }
spin_unlock_irqrestore(&slave_active_lock, flags); spin_unlock_irqrestore(&slave_active_lock, flags);
return 1; /* delayed start */ return 1; /* delayed start */
} }
/* /* stop/pause a master timer */
* start the timer instance static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop)
*/
int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
{ {
struct snd_timer *timer; struct snd_timer *timer;
int result = -EINVAL; int result = 0;
unsigned long flags; unsigned long flags;
if (timeri == NULL || ticks < 1)
return -EINVAL;
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
result = snd_timer_start_slave(timeri);
if (result >= 0)
snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
return result;
}
timer = timeri->timer;
if (timer == NULL)
return -EINVAL;
if (timer->card && timer->card->shutdown)
return -ENODEV;
spin_lock_irqsave(&timer->lock, flags);
if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
SNDRV_TIMER_IFLG_START)) {
result = -EBUSY;
goto unlock;
}
timeri->ticks = timeri->cticks = ticks;
timeri->pticks = 0;
result = snd_timer_start1(timer, timeri, ticks);
unlock:
spin_unlock_irqrestore(&timer->lock, flags);
if (result >= 0)
snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
return result;
}
static int _snd_timer_stop(struct snd_timer_instance *timeri, int event)
{
struct snd_timer *timer;
unsigned long flags;
if (snd_BUG_ON(!timeri))
return -ENXIO;
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
spin_lock_irqsave(&slave_active_lock, flags);
if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) {
spin_unlock_irqrestore(&slave_active_lock, flags);
return -EBUSY;
}
if (timeri->timer)
spin_lock(&timeri->timer->lock);
timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
list_del_init(&timeri->ack_list);
list_del_init(&timeri->active_list);
if (timeri->timer)
spin_unlock(&timeri->timer->lock);
spin_unlock_irqrestore(&slave_active_lock, flags);
goto __end;
}
timer = timeri->timer; timer = timeri->timer;
if (!timer) if (!timer)
return -EINVAL; return -EINVAL;
spin_lock_irqsave(&timer->lock, flags); spin_lock_irqsave(&timer->lock, flags);
if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
SNDRV_TIMER_IFLG_START))) { SNDRV_TIMER_IFLG_START))) {
spin_unlock_irqrestore(&timer->lock, flags); result = -EBUSY;
return -EBUSY; goto unlock;
} }
list_del_init(&timeri->ack_list); list_del_init(&timeri->ack_list);
list_del_init(&timeri->active_list); list_del_init(&timeri->active_list);
if (timer->card && timer->card->shutdown) { if (timer->card && timer->card->shutdown)
spin_unlock_irqrestore(&timer->lock, flags); goto unlock;
return 0; if (stop) {
timeri->cticks = timeri->ticks;
timeri->pticks = 0;
} }
if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) && if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) &&
!(--timer->running)) { !(--timer->running)) {
@ -556,13 +528,49 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri, int event)
} }
} }
timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START); timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START);
snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
SNDRV_TIMER_EVENT_CONTINUE);
unlock:
spin_unlock_irqrestore(&timer->lock, flags); spin_unlock_irqrestore(&timer->lock, flags);
__end: return result;
if (event != SNDRV_TIMER_EVENT_RESOLUTION) }
snd_timer_notify1(timeri, event);
/* stop/pause a slave timer */
static int snd_timer_stop_slave(struct snd_timer_instance *timeri, bool stop)
{
unsigned long flags;
spin_lock_irqsave(&slave_active_lock, flags);
if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) {
spin_unlock_irqrestore(&slave_active_lock, flags);
return -EBUSY;
}
timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
if (timeri->timer) {
spin_lock(&timeri->timer->lock);
list_del_init(&timeri->ack_list);
list_del_init(&timeri->active_list);
snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
SNDRV_TIMER_EVENT_CONTINUE);
spin_unlock(&timeri->timer->lock);
}
spin_unlock_irqrestore(&slave_active_lock, flags);
return 0; return 0;
} }
/*
* start the timer instance
*/
int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
{
if (timeri == NULL || ticks < 1)
return -EINVAL;
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
return snd_timer_start_slave(timeri, true);
else
return snd_timer_start1(timeri, true, ticks);
}
/* /*
* stop the timer instance. * stop the timer instance.
* *
@ -570,21 +578,10 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri, int event)
*/ */
int snd_timer_stop(struct snd_timer_instance *timeri) int snd_timer_stop(struct snd_timer_instance *timeri)
{ {
struct snd_timer *timer; if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
unsigned long flags; return snd_timer_stop_slave(timeri, true);
int err; else
return snd_timer_stop1(timeri, true);
err = _snd_timer_stop(timeri, SNDRV_TIMER_EVENT_STOP);
if (err < 0)
return err;
timer = timeri->timer;
if (!timer)
return -EINVAL;
spin_lock_irqsave(&timer->lock, flags);
timeri->cticks = timeri->ticks;
timeri->pticks = 0;
spin_unlock_irqrestore(&timer->lock, flags);
return 0;
} }
/* /*
@ -592,32 +589,10 @@ int snd_timer_stop(struct snd_timer_instance *timeri)
*/ */
int snd_timer_continue(struct snd_timer_instance *timeri) int snd_timer_continue(struct snd_timer_instance *timeri)
{ {
struct snd_timer *timer;
int result = -EINVAL;
unsigned long flags;
if (timeri == NULL)
return result;
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
return snd_timer_start_slave(timeri); return snd_timer_start_slave(timeri, false);
timer = timeri->timer; else
if (! timer) return snd_timer_start1(timeri, false, 0);
return -EINVAL;
if (timer->card && timer->card->shutdown)
return -ENODEV;
spin_lock_irqsave(&timer->lock, flags);
if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) {
result = -EBUSY;
goto unlock;
}
if (!timeri->cticks)
timeri->cticks = 1;
timeri->pticks = 0;
result = snd_timer_start1(timer, timeri, timer->sticks);
unlock:
spin_unlock_irqrestore(&timer->lock, flags);
snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_CONTINUE);
return result;
} }
/* /*
@ -625,7 +600,10 @@ int snd_timer_continue(struct snd_timer_instance *timeri)
*/ */
int snd_timer_pause(struct snd_timer_instance * timeri) int snd_timer_pause(struct snd_timer_instance * timeri)
{ {
return _snd_timer_stop(timeri, SNDRV_TIMER_EVENT_PAUSE); if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
return snd_timer_stop_slave(timeri, false);
else
return snd_timer_stop1(timeri, false);
} }
/* /*

View File

@ -65,8 +65,6 @@ struct mts64 {
struct snd_card *card; struct snd_card *card;
struct snd_rawmidi *rmidi; struct snd_rawmidi *rmidi;
struct pardevice *pardev; struct pardevice *pardev;
int pardev_claimed;
int open_count; int open_count;
int current_midi_output_port; int current_midi_output_port;
int current_midi_input_port; int current_midi_input_port;
@ -850,30 +848,6 @@ __out:
spin_unlock(&mts->lock); spin_unlock(&mts->lock);
} }
static int snd_mts64_probe_port(struct parport *p)
{
struct pardevice *pardev;
int res;
pardev = parport_register_device(p, DRIVER_NAME,
NULL, NULL, NULL,
0, NULL);
if (!pardev)
return -EIO;
if (parport_claim(pardev)) {
parport_unregister_device(pardev);
return -EIO;
}
res = mts64_probe(p);
parport_release(pardev);
parport_unregister_device(pardev);
return res;
}
static void snd_mts64_attach(struct parport *p) static void snd_mts64_attach(struct parport *p)
{ {
struct platform_device *device; struct platform_device *device;
@ -907,10 +881,20 @@ static void snd_mts64_detach(struct parport *p)
/* nothing to do here */ /* nothing to do here */
} }
static int snd_mts64_dev_probe(struct pardevice *pardev)
{
if (strcmp(pardev->name, DRIVER_NAME))
return -ENODEV;
return 0;
}
static struct parport_driver mts64_parport_driver = { static struct parport_driver mts64_parport_driver = {
.name = "mts64", .name = "mts64",
.attach = snd_mts64_attach, .probe = snd_mts64_dev_probe,
.detach = snd_mts64_detach .match_port = snd_mts64_attach,
.detach = snd_mts64_detach,
.devmodel = true,
}; };
/********************************************************************* /*********************************************************************
@ -922,8 +906,7 @@ static void snd_mts64_card_private_free(struct snd_card *card)
struct pardevice *pardev = mts->pardev; struct pardevice *pardev = mts->pardev;
if (pardev) { if (pardev) {
if (mts->pardev_claimed) parport_release(pardev);
parport_release(pardev);
parport_unregister_device(pardev); parport_unregister_device(pardev);
} }
@ -938,6 +921,12 @@ static int snd_mts64_probe(struct platform_device *pdev)
struct snd_card *card = NULL; struct snd_card *card = NULL;
struct mts64 *mts = NULL; struct mts64 *mts = NULL;
int err; int err;
struct pardev_cb mts64_cb = {
.preempt = NULL,
.wakeup = NULL,
.irq_func = snd_mts64_interrupt, /* ISR */
.flags = PARPORT_DEV_EXCL, /* flags */
};
p = platform_get_drvdata(pdev); p = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
@ -946,8 +935,6 @@ static int snd_mts64_probe(struct platform_device *pdev)
return -ENODEV; return -ENODEV;
if (!enable[dev]) if (!enable[dev])
return -ENOENT; return -ENOENT;
if ((err = snd_mts64_probe_port(p)) < 0)
return err;
err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE, err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE,
0, &card); 0, &card);
@ -960,39 +947,41 @@ static int snd_mts64_probe(struct platform_device *pdev)
sprintf(card->longname, "%s at 0x%lx, irq %i", sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, p->base, p->irq); card->shortname, p->base, p->irq);
pardev = parport_register_device(p, /* port */ mts64_cb.private = card; /* private */
DRIVER_NAME, /* name */ pardev = parport_register_dev_model(p, /* port */
NULL, /* preempt */ DRIVER_NAME, /* name */
NULL, /* wakeup */ &mts64_cb, /* callbacks */
snd_mts64_interrupt, /* ISR */ pdev->id); /* device number */
PARPORT_DEV_EXCL, /* flags */ if (!pardev) {
(void *)card); /* private */
if (pardev == NULL) {
snd_printd("Cannot register pardevice\n"); snd_printd("Cannot register pardevice\n");
err = -EIO; err = -EIO;
goto __err; goto __err;
} }
if ((err = snd_mts64_create(card, pardev, &mts)) < 0) {
snd_printd("Cannot create main component\n");
parport_unregister_device(pardev);
goto __err;
}
card->private_data = mts;
card->private_free = snd_mts64_card_private_free;
if ((err = snd_mts64_rawmidi_create(card)) < 0) {
snd_printd("Creating Rawmidi component failed\n");
goto __err;
}
/* claim parport */ /* claim parport */
if (parport_claim(pardev)) { if (parport_claim(pardev)) {
snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base); snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base);
err = -EIO;
goto free_pardev;
}
if ((err = snd_mts64_create(card, pardev, &mts)) < 0) {
snd_printd("Cannot create main component\n");
goto release_pardev;
}
card->private_data = mts;
card->private_free = snd_mts64_card_private_free;
err = mts64_probe(p);
if (err) {
err = -EIO; err = -EIO;
goto __err; goto __err;
} }
mts->pardev_claimed = 1;
if ((err = snd_mts64_rawmidi_create(card)) < 0) {
snd_printd("Creating Rawmidi component failed\n");
goto __err;
}
/* init device */ /* init device */
if ((err = mts64_device_init(p)) < 0) if ((err = mts64_device_init(p)) < 0)
@ -1009,6 +998,10 @@ static int snd_mts64_probe(struct platform_device *pdev)
snd_printk(KERN_INFO "ESI Miditerminal 4140 on 0x%lx\n", p->base); snd_printk(KERN_INFO "ESI Miditerminal 4140 on 0x%lx\n", p->base);
return 0; return 0;
release_pardev:
parport_release(pardev);
free_pardev:
parport_unregister_device(pardev);
__err: __err:
snd_card_free(card); snd_card_free(card);
return err; return err;
@ -1024,7 +1017,6 @@ static int snd_mts64_remove(struct platform_device *pdev)
return 0; return 0;
} }
static struct platform_driver snd_mts64_driver = { static struct platform_driver snd_mts64_driver = {
.probe = snd_mts64_probe, .probe = snd_mts64_probe,
.remove = snd_mts64_remove, .remove = snd_mts64_remove,

View File

@ -83,8 +83,6 @@ struct portman {
struct snd_card *card; struct snd_card *card;
struct snd_rawmidi *rmidi; struct snd_rawmidi *rmidi;
struct pardevice *pardev; struct pardevice *pardev;
int pardev_claimed;
int open_count; int open_count;
int mode[PORTMAN_NUM_INPUT_PORTS]; int mode[PORTMAN_NUM_INPUT_PORTS];
struct snd_rawmidi_substream *midi_input[PORTMAN_NUM_INPUT_PORTS]; struct snd_rawmidi_substream *midi_input[PORTMAN_NUM_INPUT_PORTS];
@ -648,30 +646,6 @@ static void snd_portman_interrupt(void *userdata)
spin_unlock(&pm->reg_lock); spin_unlock(&pm->reg_lock);
} }
static int snd_portman_probe_port(struct parport *p)
{
struct pardevice *pardev;
int res;
pardev = parport_register_device(p, DRIVER_NAME,
NULL, NULL, NULL,
0, NULL);
if (!pardev)
return -EIO;
if (parport_claim(pardev)) {
parport_unregister_device(pardev);
return -EIO;
}
res = portman_probe(p);
parport_release(pardev);
parport_unregister_device(pardev);
return res ? -EIO : 0;
}
static void snd_portman_attach(struct parport *p) static void snd_portman_attach(struct parport *p)
{ {
struct platform_device *device; struct platform_device *device;
@ -705,10 +679,20 @@ static void snd_portman_detach(struct parport *p)
/* nothing to do here */ /* nothing to do here */
} }
static int snd_portman_dev_probe(struct pardevice *pardev)
{
if (strcmp(pardev->name, DRIVER_NAME))
return -ENODEV;
return 0;
}
static struct parport_driver portman_parport_driver = { static struct parport_driver portman_parport_driver = {
.name = "portman2x4", .name = "portman2x4",
.attach = snd_portman_attach, .probe = snd_portman_dev_probe,
.detach = snd_portman_detach .match_port = snd_portman_attach,
.detach = snd_portman_detach,
.devmodel = true,
}; };
/********************************************************************* /*********************************************************************
@ -720,8 +704,7 @@ static void snd_portman_card_private_free(struct snd_card *card)
struct pardevice *pardev = pm->pardev; struct pardevice *pardev = pm->pardev;
if (pardev) { if (pardev) {
if (pm->pardev_claimed) parport_release(pardev);
parport_release(pardev);
parport_unregister_device(pardev); parport_unregister_device(pardev);
} }
@ -736,6 +719,12 @@ static int snd_portman_probe(struct platform_device *pdev)
struct snd_card *card = NULL; struct snd_card *card = NULL;
struct portman *pm = NULL; struct portman *pm = NULL;
int err; int err;
struct pardev_cb portman_cb = {
.preempt = NULL,
.wakeup = NULL,
.irq_func = snd_portman_interrupt, /* ISR */
.flags = PARPORT_DEV_EXCL, /* flags */
};
p = platform_get_drvdata(pdev); p = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
@ -745,9 +734,6 @@ static int snd_portman_probe(struct platform_device *pdev)
if (!enable[dev]) if (!enable[dev])
return -ENOENT; return -ENOENT;
if ((err = snd_portman_probe_port(p)) < 0)
return err;
err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE, err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE,
0, &card); 0, &card);
if (err < 0) { if (err < 0) {
@ -759,39 +745,41 @@ static int snd_portman_probe(struct platform_device *pdev)
sprintf(card->longname, "%s at 0x%lx, irq %i", sprintf(card->longname, "%s at 0x%lx, irq %i",
card->shortname, p->base, p->irq); card->shortname, p->base, p->irq);
pardev = parport_register_device(p, /* port */ portman_cb.private = card; /* private */
DRIVER_NAME, /* name */ pardev = parport_register_dev_model(p, /* port */
NULL, /* preempt */ DRIVER_NAME, /* name */
NULL, /* wakeup */ &portman_cb, /* callbacks */
snd_portman_interrupt, /* ISR */ pdev->id); /* device number */
PARPORT_DEV_EXCL, /* flags */
(void *)card); /* private */
if (pardev == NULL) { if (pardev == NULL) {
snd_printd("Cannot register pardevice\n"); snd_printd("Cannot register pardevice\n");
err = -EIO; err = -EIO;
goto __err; goto __err;
} }
if ((err = portman_create(card, pardev, &pm)) < 0) {
snd_printd("Cannot create main component\n");
parport_unregister_device(pardev);
goto __err;
}
card->private_data = pm;
card->private_free = snd_portman_card_private_free;
if ((err = snd_portman_rawmidi_create(card)) < 0) {
snd_printd("Creating Rawmidi component failed\n");
goto __err;
}
/* claim parport */ /* claim parport */
if (parport_claim(pardev)) { if (parport_claim(pardev)) {
snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base); snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base);
err = -EIO;
goto free_pardev;
}
if ((err = portman_create(card, pardev, &pm)) < 0) {
snd_printd("Cannot create main component\n");
goto release_pardev;
}
card->private_data = pm;
card->private_free = snd_portman_card_private_free;
err = portman_probe(p);
if (err) {
err = -EIO; err = -EIO;
goto __err; goto __err;
} }
pm->pardev_claimed = 1;
if ((err = snd_portman_rawmidi_create(card)) < 0) {
snd_printd("Creating Rawmidi component failed\n");
goto __err;
}
/* init device */ /* init device */
if ((err = portman_device_init(pm)) < 0) if ((err = portman_device_init(pm)) < 0)
@ -808,6 +796,10 @@ static int snd_portman_probe(struct platform_device *pdev)
snd_printk(KERN_INFO "Portman 2x4 on 0x%lx\n", p->base); snd_printk(KERN_INFO "Portman 2x4 on 0x%lx\n", p->base);
return 0; return 0;
release_pardev:
parport_release(pardev);
free_pardev:
parport_unregister_device(pardev);
__err: __err:
snd_card_free(card); snd_card_free(card);
return err; return err;

View File

@ -300,6 +300,22 @@ error:
return err; return err;
} }
/*
* This driver doesn't update streams in bus reset handler.
*
* DM1000/ DM1100/DM1500 chipsets with BeBoB firmware transfer packets with
* discontinued counter at bus reset. This discontinuity is immediately
* detected in packet streaming layer, then it sets XRUN to PCM substream.
*
* ALSA PCM applications can know the XRUN by getting -EPIPE from PCM operation.
* Then, they can recover the PCM substream by executing ioctl(2) with
* SNDRV_PCM_IOCTL_PREPARE. 'struct snd_pcm_ops.prepare' is called and drivers
* restart packet streaming.
*
* The above processing may be executed before this bus-reset handler is
* executed. When this handler updates streams with current isochronous
* channels, the streams already have the current ones.
*/
static void static void
bebob_update(struct fw_unit *unit) bebob_update(struct fw_unit *unit)
{ {
@ -309,7 +325,6 @@ bebob_update(struct fw_unit *unit)
return; return;
fcp_bus_reset(bebob->unit); fcp_bus_reset(bebob->unit);
snd_bebob_stream_update_duplex(bebob);
if (bebob->deferred_registration) { if (bebob->deferred_registration) {
if (snd_card_register(bebob->card) < 0) { if (snd_card_register(bebob->card) < 0) {
@ -327,10 +342,6 @@ static void bebob_remove(struct fw_unit *unit)
if (bebob == NULL) if (bebob == NULL)
return; return;
/* Awake bus-reset waiters. */
if (!completion_done(&bebob->bus_reset))
complete_all(&bebob->bus_reset);
/* No need to wait for releasing card object in this context. */ /* No need to wait for releasing card object in this context. */
snd_card_free_when_closed(bebob->card); snd_card_free_when_closed(bebob->card);
} }

View File

@ -88,8 +88,6 @@ struct snd_bebob {
unsigned int midi_input_ports; unsigned int midi_input_ports;
unsigned int midi_output_ports; unsigned int midi_output_ports;
/* for bus reset quirk */
struct completion bus_reset;
bool connected; bool connected;
struct amdtp_stream *master; struct amdtp_stream *master;
@ -97,7 +95,7 @@ struct snd_bebob {
struct amdtp_stream rx_stream; struct amdtp_stream rx_stream;
struct cmp_connection out_conn; struct cmp_connection out_conn;
struct cmp_connection in_conn; struct cmp_connection in_conn;
atomic_t substreams_counter; unsigned int substreams_counter;
struct snd_bebob_stream_formation struct snd_bebob_stream_formation
tx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES]; tx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES];
@ -219,7 +217,6 @@ int snd_bebob_stream_discover(struct snd_bebob *bebob);
int snd_bebob_stream_init_duplex(struct snd_bebob *bebob); int snd_bebob_stream_init_duplex(struct snd_bebob *bebob);
int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate); int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate);
void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob); void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob);
void snd_bebob_stream_update_duplex(struct snd_bebob *bebob);
void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob); void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob);
void snd_bebob_stream_lock_changed(struct snd_bebob *bebob); void snd_bebob_stream_lock_changed(struct snd_bebob *bebob);

View File

@ -17,8 +17,10 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream)
if (err < 0) if (err < 0)
goto end; goto end;
atomic_inc(&bebob->substreams_counter); mutex_lock(&bebob->mutex);
bebob->substreams_counter++;
err = snd_bebob_stream_start_duplex(bebob, 0); err = snd_bebob_stream_start_duplex(bebob, 0);
mutex_unlock(&bebob->mutex);
if (err < 0) if (err < 0)
snd_bebob_stream_lock_release(bebob); snd_bebob_stream_lock_release(bebob);
end: end:
@ -34,8 +36,10 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream)
if (err < 0) if (err < 0)
goto end; goto end;
atomic_inc(&bebob->substreams_counter); mutex_lock(&bebob->mutex);
bebob->substreams_counter++;
err = snd_bebob_stream_start_duplex(bebob, 0); err = snd_bebob_stream_start_duplex(bebob, 0);
mutex_unlock(&bebob->mutex);
if (err < 0) if (err < 0)
snd_bebob_stream_lock_release(bebob); snd_bebob_stream_lock_release(bebob);
end: end:
@ -46,8 +50,10 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream)
{ {
struct snd_bebob *bebob = substream->rmidi->private_data; struct snd_bebob *bebob = substream->rmidi->private_data;
atomic_dec(&bebob->substreams_counter); mutex_lock(&bebob->mutex);
bebob->substreams_counter--;
snd_bebob_stream_stop_duplex(bebob); snd_bebob_stream_stop_duplex(bebob);
mutex_unlock(&bebob->mutex);
snd_bebob_stream_lock_release(bebob); snd_bebob_stream_lock_release(bebob);
return 0; return 0;
@ -57,8 +63,10 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream)
{ {
struct snd_bebob *bebob = substream->rmidi->private_data; struct snd_bebob *bebob = substream->rmidi->private_data;
atomic_dec(&bebob->substreams_counter); mutex_lock(&bebob->mutex);
bebob->substreams_counter--;
snd_bebob_stream_stop_duplex(bebob); snd_bebob_stream_stop_duplex(bebob);
mutex_unlock(&bebob->mutex);
snd_bebob_stream_lock_release(bebob); snd_bebob_stream_lock_release(bebob);
return 0; return 0;

View File

@ -218,8 +218,11 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream,
if (err < 0) if (err < 0)
return err; return err;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
atomic_inc(&bebob->substreams_counter); mutex_lock(&bebob->mutex);
bebob->substreams_counter++;
mutex_unlock(&bebob->mutex);
}
amdtp_am824_set_pcm_format(&bebob->tx_stream, params_format(hw_params)); amdtp_am824_set_pcm_format(&bebob->tx_stream, params_format(hw_params));
@ -237,8 +240,11 @@ pcm_playback_hw_params(struct snd_pcm_substream *substream,
if (err < 0) if (err < 0)
return err; return err;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
atomic_inc(&bebob->substreams_counter); mutex_lock(&bebob->mutex);
bebob->substreams_counter++;
mutex_unlock(&bebob->mutex);
}
amdtp_am824_set_pcm_format(&bebob->rx_stream, params_format(hw_params)); amdtp_am824_set_pcm_format(&bebob->rx_stream, params_format(hw_params));
@ -250,8 +256,11 @@ pcm_capture_hw_free(struct snd_pcm_substream *substream)
{ {
struct snd_bebob *bebob = substream->private_data; struct snd_bebob *bebob = substream->private_data;
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
atomic_dec(&bebob->substreams_counter); mutex_lock(&bebob->mutex);
bebob->substreams_counter--;
mutex_unlock(&bebob->mutex);
}
snd_bebob_stream_stop_duplex(bebob); snd_bebob_stream_stop_duplex(bebob);
@ -262,8 +271,11 @@ pcm_playback_hw_free(struct snd_pcm_substream *substream)
{ {
struct snd_bebob *bebob = substream->private_data; struct snd_bebob *bebob = substream->private_data;
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
atomic_dec(&bebob->substreams_counter); mutex_lock(&bebob->mutex);
bebob->substreams_counter--;
mutex_unlock(&bebob->mutex);
}
snd_bebob_stream_stop_duplex(bebob); snd_bebob_stream_stop_duplex(bebob);

View File

@ -549,8 +549,7 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
destroy_both_connections(bebob); destroy_both_connections(bebob);
goto end; goto end;
} }
/* See comments in next function */
init_completion(&bebob->bus_reset);
bebob->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK; bebob->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK;
/* /*
@ -588,29 +587,10 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
struct amdtp_stream *master, *slave; struct amdtp_stream *master, *slave;
enum cip_flags sync_mode; enum cip_flags sync_mode;
unsigned int curr_rate; unsigned int curr_rate;
bool updated = false;
int err = 0; int err = 0;
/*
* Normal BeBoB firmware has a quirk at bus reset to transmits packets
* with discontinuous value in dbc field.
*
* This 'struct completion' is used to call .update() at first to update
* connections/streams. Next following codes handle streaming error.
*/
if (amdtp_streaming_error(&bebob->tx_stream)) {
if (completion_done(&bebob->bus_reset))
reinit_completion(&bebob->bus_reset);
updated = (wait_for_completion_interruptible_timeout(
&bebob->bus_reset,
msecs_to_jiffies(FW_ISO_RESOURCE_DELAY)) > 0);
}
mutex_lock(&bebob->mutex);
/* Need no substreams */ /* Need no substreams */
if (atomic_read(&bebob->substreams_counter) == 0) if (bebob->substreams_counter == 0)
goto end; goto end;
err = get_sync_mode(bebob, &sync_mode); err = get_sync_mode(bebob, &sync_mode);
@ -642,8 +622,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
amdtp_stream_stop(master); amdtp_stream_stop(master);
if (amdtp_streaming_error(slave)) if (amdtp_streaming_error(slave))
amdtp_stream_stop(slave); amdtp_stream_stop(slave);
if (!updated && if (!amdtp_stream_running(master) && !amdtp_stream_running(slave))
!amdtp_stream_running(master) && !amdtp_stream_running(slave))
break_both_connections(bebob); break_both_connections(bebob);
/* stop streams if rate is different */ /* stop streams if rate is different */
@ -741,7 +720,6 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
} }
} }
end: end:
mutex_unlock(&bebob->mutex);
return err; return err;
} }
@ -757,9 +735,7 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
master = &bebob->tx_stream; master = &bebob->tx_stream;
} }
mutex_lock(&bebob->mutex); if (bebob->substreams_counter == 0) {
if (atomic_read(&bebob->substreams_counter) == 0) {
amdtp_stream_pcm_abort(master); amdtp_stream_pcm_abort(master);
amdtp_stream_stop(master); amdtp_stream_stop(master);
@ -768,32 +744,6 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
break_both_connections(bebob); break_both_connections(bebob);
} }
mutex_unlock(&bebob->mutex);
}
void snd_bebob_stream_update_duplex(struct snd_bebob *bebob)
{
/* vs. XRUN recovery due to discontinuity at bus reset */
mutex_lock(&bebob->mutex);
if ((cmp_connection_update(&bebob->in_conn) < 0) ||
(cmp_connection_update(&bebob->out_conn) < 0)) {
amdtp_stream_pcm_abort(&bebob->rx_stream);
amdtp_stream_pcm_abort(&bebob->tx_stream);
amdtp_stream_stop(&bebob->rx_stream);
amdtp_stream_stop(&bebob->tx_stream);
break_both_connections(bebob);
} else {
amdtp_stream_update(&bebob->rx_stream);
amdtp_stream_update(&bebob->tx_stream);
}
/* wake up stream_start_duplex() */
if (!completion_done(&bebob->bus_reset))
complete_all(&bebob->bus_reset);
mutex_unlock(&bebob->mutex);
} }
/* /*

View File

@ -52,10 +52,10 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&dice->lock, flags); spin_lock_irqsave(&dice->lock, flags);
if (up) if (up)
amdtp_am824_midi_trigger(&dice->tx_stream, amdtp_am824_midi_trigger(&dice->tx_stream[0],
substrm->number, substrm); substrm->number, substrm);
else else
amdtp_am824_midi_trigger(&dice->tx_stream, amdtp_am824_midi_trigger(&dice->tx_stream[0],
substrm->number, NULL); substrm->number, NULL);
spin_unlock_irqrestore(&dice->lock, flags); spin_unlock_irqrestore(&dice->lock, flags);
@ -69,10 +69,10 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
spin_lock_irqsave(&dice->lock, flags); spin_lock_irqsave(&dice->lock, flags);
if (up) if (up)
amdtp_am824_midi_trigger(&dice->rx_stream, amdtp_am824_midi_trigger(&dice->rx_stream[0],
substrm->number, substrm); substrm->number, substrm);
else else
amdtp_am824_midi_trigger(&dice->rx_stream, amdtp_am824_midi_trigger(&dice->rx_stream[0],
substrm->number, NULL); substrm->number, NULL);
spin_unlock_irqrestore(&dice->lock, flags); spin_unlock_irqrestore(&dice->lock, flags);
@ -103,16 +103,27 @@ static void set_midi_substream_names(struct snd_dice *dice,
int snd_dice_create_midi(struct snd_dice *dice) int snd_dice_create_midi(struct snd_dice *dice)
{ {
__be32 reg;
struct snd_rawmidi *rmidi; struct snd_rawmidi *rmidi;
struct snd_rawmidi_str *str; struct snd_rawmidi_str *str;
unsigned int i, midi_in_ports, midi_out_ports; unsigned int midi_in_ports, midi_out_ports;
int err; int err;
midi_in_ports = midi_out_ports = 0; /*
for (i = 0; i < 3; i++) { * Use the number of MIDI conformant data channel at current sampling
midi_in_ports = max(dice->tx_midi_ports[i], midi_in_ports); * transfer frequency.
midi_out_ports = max(dice->rx_midi_ports[i], midi_out_ports); */
} err = snd_dice_transaction_read_tx(dice, TX_NUMBER_MIDI,
&reg, sizeof(reg));
if (err < 0)
return err;
midi_in_ports = be32_to_cpu(reg);
err = snd_dice_transaction_read_rx(dice, RX_NUMBER_MIDI,
&reg, sizeof(reg));
if (err < 0)
return err;
midi_out_ports = be32_to_cpu(reg);
if (midi_in_ports + midi_out_ports == 0) if (midi_in_ports + midi_out_ports == 0)
return 0; return 0;

View File

@ -9,99 +9,46 @@
#include "dice.h" #include "dice.h"
static int dice_rate_constraint(struct snd_pcm_hw_params *params, static int limit_channels_and_rates(struct snd_dice *dice,
struct snd_pcm_hw_rule *rule) struct snd_pcm_runtime *runtime,
{ enum amdtp_stream_direction dir,
struct snd_pcm_substream *substream = rule->private; unsigned int index, unsigned int size)
struct snd_dice *dice = substream->private_data;
const struct snd_interval *c =
hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
struct snd_interval *r =
hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval rates = {
.min = UINT_MAX, .max = 0, .integer = 1
};
unsigned int i, rate, mode, *pcm_channels;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
pcm_channels = dice->tx_channels;
else
pcm_channels = dice->rx_channels;
for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
rate = snd_dice_rates[i];
if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
continue;
if (!snd_interval_test(c, pcm_channels[mode]))
continue;
rates.min = min(rates.min, rate);
rates.max = max(rates.max, rate);
}
return snd_interval_refine(r, &rates);
}
static int dice_channels_constraint(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct snd_pcm_substream *substream = rule->private;
struct snd_dice *dice = substream->private_data;
const struct snd_interval *r =
hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval *c =
hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
struct snd_interval channels = {
.min = UINT_MAX, .max = 0, .integer = 1
};
unsigned int i, rate, mode, *pcm_channels;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
pcm_channels = dice->tx_channels;
else
pcm_channels = dice->rx_channels;
for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
rate = snd_dice_rates[i];
if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
continue;
if (!snd_interval_test(r, rate))
continue;
channels.min = min(channels.min, pcm_channels[mode]);
channels.max = max(channels.max, pcm_channels[mode]);
}
return snd_interval_refine(c, &channels);
}
static void limit_channels_and_rates(struct snd_dice *dice,
struct snd_pcm_runtime *runtime,
unsigned int *pcm_channels)
{ {
struct snd_pcm_hardware *hw = &runtime->hw; struct snd_pcm_hardware *hw = &runtime->hw;
unsigned int i, rate, mode; struct amdtp_stream *stream;
unsigned int rate;
__be32 reg;
int err;
hw->channels_min = UINT_MAX; /*
hw->channels_max = 0; * Retrieve current Multi Bit Linear Audio data channel and limit to
* it.
for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { */
rate = snd_dice_rates[i]; if (dir == AMDTP_IN_STREAM) {
if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) stream = &dice->tx_stream[index];
continue; err = snd_dice_transaction_read_tx(dice,
hw->rates |= snd_pcm_rate_to_rate_bit(rate); size * index + TX_NUMBER_AUDIO,
&reg, sizeof(reg));
if (pcm_channels[mode] == 0) } else {
continue; stream = &dice->rx_stream[index];
hw->channels_min = min(hw->channels_min, pcm_channels[mode]); err = snd_dice_transaction_read_rx(dice,
hw->channels_max = max(hw->channels_max, pcm_channels[mode]); size * index + RX_NUMBER_AUDIO,
&reg, sizeof(reg));
} }
if (err < 0)
return err;
hw->channels_min = hw->channels_max = be32_to_cpu(reg);
/* Retrieve current sampling transfer frequency and limit to it. */
err = snd_dice_transaction_get_rate(dice, &rate);
if (err < 0)
return err;
hw->rates = snd_pcm_rate_to_rate_bit(rate);
snd_pcm_limit_hw_rates(runtime); snd_pcm_limit_hw_rates(runtime);
return 0;
} }
static void limit_period_and_buffer(struct snd_pcm_hardware *hw) static void limit_period_and_buffer(struct snd_pcm_hardware *hw)
@ -121,8 +68,10 @@ static int init_hw_info(struct snd_dice *dice,
{ {
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_hardware *hw = &runtime->hw; struct snd_pcm_hardware *hw = &runtime->hw;
enum amdtp_stream_direction dir;
struct amdtp_stream *stream; struct amdtp_stream *stream;
unsigned int *pcm_channels; __be32 reg[2];
unsigned int count, size;
int err; int err;
hw->info = SNDRV_PCM_INFO_MMAP | hw->info = SNDRV_PCM_INFO_MMAP |
@ -134,38 +83,38 @@ static int init_hw_info(struct snd_dice *dice,
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
hw->formats = AM824_IN_PCM_FORMAT_BITS; hw->formats = AM824_IN_PCM_FORMAT_BITS;
stream = &dice->tx_stream; dir = AMDTP_IN_STREAM;
pcm_channels = dice->tx_channels; stream = &dice->tx_stream[substream->pcm->device];
err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg,
sizeof(reg));
} else { } else {
hw->formats = AM824_OUT_PCM_FORMAT_BITS; hw->formats = AM824_OUT_PCM_FORMAT_BITS;
stream = &dice->rx_stream; dir = AMDTP_OUT_STREAM;
pcm_channels = dice->rx_channels; stream = &dice->rx_stream[substream->pcm->device];
err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg,
sizeof(reg));
} }
limit_channels_and_rates(dice, runtime, pcm_channels); if (err < 0)
return err;
count = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
if (substream->pcm->device >= count)
return -ENXIO;
size = be32_to_cpu(reg[1]) * 4;
err = limit_channels_and_rates(dice, substream->runtime, dir,
substream->pcm->device, size);
if (err < 0)
return err;
limit_period_and_buffer(hw); limit_period_and_buffer(hw);
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, return amdtp_am824_add_pcm_hw_constraints(stream, runtime);
dice_rate_constraint, substream,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
if (err < 0)
goto end;
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
dice_channels_constraint, substream,
SNDRV_PCM_HW_PARAM_RATE, -1);
if (err < 0)
goto end;
err = amdtp_am824_add_pcm_hw_constraints(stream, runtime);
end:
return err;
} }
static int pcm_open(struct snd_pcm_substream *substream) static int pcm_open(struct snd_pcm_substream *substream)
{ {
struct snd_dice *dice = substream->private_data; struct snd_dice *dice = substream->private_data;
unsigned int source, rate;
bool internal;
int err; int err;
err = snd_dice_stream_lock_try(dice); err = snd_dice_stream_lock_try(dice);
@ -176,39 +125,6 @@ static int pcm_open(struct snd_pcm_substream *substream)
if (err < 0) if (err < 0)
goto err_locked; goto err_locked;
err = snd_dice_transaction_get_clock_source(dice, &source);
if (err < 0)
goto err_locked;
switch (source) {
case CLOCK_SOURCE_AES1:
case CLOCK_SOURCE_AES2:
case CLOCK_SOURCE_AES3:
case CLOCK_SOURCE_AES4:
case CLOCK_SOURCE_AES_ANY:
case CLOCK_SOURCE_ADAT:
case CLOCK_SOURCE_TDIF:
case CLOCK_SOURCE_WC:
internal = false;
break;
default:
internal = true;
break;
}
/*
* When source of clock is not internal or any PCM streams are running,
* available sampling rate is limited at current sampling rate.
*/
if (!internal ||
amdtp_stream_pcm_running(&dice->tx_stream) ||
amdtp_stream_pcm_running(&dice->rx_stream)) {
err = snd_dice_transaction_get_rate(dice, &rate);
if (err < 0)
goto err_locked;
substream->runtime->hw.rate_min = rate;
substream->runtime->hw.rate_max = rate;
}
snd_pcm_set_sync(substream); snd_pcm_set_sync(substream);
end: end:
return err; return err;
@ -230,6 +146,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params) struct snd_pcm_hw_params *hw_params)
{ {
struct snd_dice *dice = substream->private_data; struct snd_dice *dice = substream->private_data;
struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
int err; int err;
err = snd_pcm_lib_alloc_vmalloc_buffer(substream, err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
@ -243,7 +160,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream,
mutex_unlock(&dice->mutex); mutex_unlock(&dice->mutex);
} }
amdtp_am824_set_pcm_format(&dice->tx_stream, params_format(hw_params)); amdtp_am824_set_pcm_format(stream, params_format(hw_params));
return 0; return 0;
} }
@ -251,6 +168,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params) struct snd_pcm_hw_params *hw_params)
{ {
struct snd_dice *dice = substream->private_data; struct snd_dice *dice = substream->private_data;
struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
int err; int err;
err = snd_pcm_lib_alloc_vmalloc_buffer(substream, err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
@ -264,7 +182,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream,
mutex_unlock(&dice->mutex); mutex_unlock(&dice->mutex);
} }
amdtp_am824_set_pcm_format(&dice->rx_stream, params_format(hw_params)); amdtp_am824_set_pcm_format(stream, params_format(hw_params));
return 0; return 0;
} }
@ -304,26 +222,28 @@ static int playback_hw_free(struct snd_pcm_substream *substream)
static int capture_prepare(struct snd_pcm_substream *substream) static int capture_prepare(struct snd_pcm_substream *substream)
{ {
struct snd_dice *dice = substream->private_data; struct snd_dice *dice = substream->private_data;
struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
int err; int err;
mutex_lock(&dice->mutex); mutex_lock(&dice->mutex);
err = snd_dice_stream_start_duplex(dice, substream->runtime->rate); err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
mutex_unlock(&dice->mutex); mutex_unlock(&dice->mutex);
if (err >= 0) if (err >= 0)
amdtp_stream_pcm_prepare(&dice->tx_stream); amdtp_stream_pcm_prepare(stream);
return 0; return 0;
} }
static int playback_prepare(struct snd_pcm_substream *substream) static int playback_prepare(struct snd_pcm_substream *substream)
{ {
struct snd_dice *dice = substream->private_data; struct snd_dice *dice = substream->private_data;
struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
int err; int err;
mutex_lock(&dice->mutex); mutex_lock(&dice->mutex);
err = snd_dice_stream_start_duplex(dice, substream->runtime->rate); err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
mutex_unlock(&dice->mutex); mutex_unlock(&dice->mutex);
if (err >= 0) if (err >= 0)
amdtp_stream_pcm_prepare(&dice->rx_stream); amdtp_stream_pcm_prepare(stream);
return err; return err;
} }
@ -331,13 +251,14 @@ static int playback_prepare(struct snd_pcm_substream *substream)
static int capture_trigger(struct snd_pcm_substream *substream, int cmd) static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
{ {
struct snd_dice *dice = substream->private_data; struct snd_dice *dice = substream->private_data;
struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
switch (cmd) { switch (cmd) {
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
amdtp_stream_pcm_trigger(&dice->tx_stream, substream); amdtp_stream_pcm_trigger(stream, substream);
break; break;
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP:
amdtp_stream_pcm_trigger(&dice->tx_stream, NULL); amdtp_stream_pcm_trigger(stream, NULL);
break; break;
default: default:
return -EINVAL; return -EINVAL;
@ -348,13 +269,14 @@ static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
static int playback_trigger(struct snd_pcm_substream *substream, int cmd) static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
{ {
struct snd_dice *dice = substream->private_data; struct snd_dice *dice = substream->private_data;
struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
switch (cmd) { switch (cmd) {
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
amdtp_stream_pcm_trigger(&dice->rx_stream, substream); amdtp_stream_pcm_trigger(stream, substream);
break; break;
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP:
amdtp_stream_pcm_trigger(&dice->rx_stream, NULL); amdtp_stream_pcm_trigger(stream, NULL);
break; break;
default: default:
return -EINVAL; return -EINVAL;
@ -366,14 +288,16 @@ static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream) static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)
{ {
struct snd_dice *dice = substream->private_data; struct snd_dice *dice = substream->private_data;
struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
return amdtp_stream_pcm_pointer(&dice->tx_stream); return amdtp_stream_pcm_pointer(stream);
} }
static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
{ {
struct snd_dice *dice = substream->private_data; struct snd_dice *dice = substream->private_data;
struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
return amdtp_stream_pcm_pointer(&dice->rx_stream); return amdtp_stream_pcm_pointer(stream);
} }
int snd_dice_create_pcm(struct snd_dice *dice) int snd_dice_create_pcm(struct snd_dice *dice)
@ -402,29 +326,53 @@ int snd_dice_create_pcm(struct snd_dice *dice)
.page = snd_pcm_lib_get_vmalloc_page, .page = snd_pcm_lib_get_vmalloc_page,
.mmap = snd_pcm_lib_mmap_vmalloc, .mmap = snd_pcm_lib_mmap_vmalloc,
}; };
__be32 reg;
struct snd_pcm *pcm; struct snd_pcm *pcm;
unsigned int i, capture, playback; unsigned int i, max_capture, max_playback, capture, playback;
int err; int err;
capture = playback = 0; /* Check whether PCM substreams are required. */
for (i = 0; i < 3; i++) { if (dice->force_two_pcms) {
if (dice->tx_channels[i] > 0) max_capture = max_playback = 2;
capture = 1; } else {
if (dice->rx_channels[i] > 0) max_capture = max_playback = 0;
playback = 1; err = snd_dice_transaction_read_tx(dice, TX_NUMBER, &reg,
sizeof(reg));
if (err < 0)
return err;
max_capture = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS);
err = snd_dice_transaction_read_rx(dice, RX_NUMBER, &reg,
sizeof(reg));
if (err < 0)
return err;
max_playback = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS);
} }
err = snd_pcm_new(dice->card, "DICE", 0, playback, capture, &pcm); for (i = 0; i < MAX_STREAMS; i++) {
if (err < 0) capture = playback = 0;
return err; if (i < max_capture)
pcm->private_data = dice; capture = 1;
strcpy(pcm->name, dice->card->shortname); if (i < max_playback)
playback = 1;
if (capture == 0 && playback == 0)
break;
if (capture > 0) err = snd_pcm_new(dice->card, "DICE", i, playback, capture,
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); &pcm);
if (err < 0)
return err;
pcm->private_data = dice;
strcpy(pcm->name, dice->card->shortname);
if (playback > 0) if (capture > 0)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&capture_ops);
if (playback > 0)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&playback_ops);
}
return 0; return 0;
} }

View File

@ -10,6 +10,12 @@
#include "dice.h" #include "dice.h"
#define CALLBACK_TIMEOUT 200 #define CALLBACK_TIMEOUT 200
#define NOTIFICATION_TIMEOUT_MS (2 * MSEC_PER_SEC)
struct reg_params {
unsigned int count;
unsigned int size;
};
const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = { const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
/* mode 0 */ /* mode 0 */
@ -24,96 +30,126 @@ const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
[6] = 192000, [6] = 192000,
}; };
int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate, /*
unsigned int *mode) * This operation has an effect to synchronize GLOBAL_STATUS/GLOBAL_SAMPLE_RATE
* to GLOBAL_STATUS. Especially, just after powering on, these are different.
*/
static int ensure_phase_lock(struct snd_dice *dice)
{ {
int i; __be32 reg, nominal;
int err;
for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) { err = snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT,
if (!(dice->clock_caps & BIT(i))) &reg, sizeof(reg));
continue; if (err < 0)
if (snd_dice_rates[i] != rate) return err;
continue;
*mode = (i - 1) / 2; if (completion_done(&dice->clock_accepted))
return 0; reinit_completion(&dice->clock_accepted);
err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
&reg, sizeof(reg));
if (err < 0)
return err;
if (wait_for_completion_timeout(&dice->clock_accepted,
msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) {
/*
* Old versions of Dice firmware transfer no notification when
* the same clock status as current one is set. In this case,
* just check current clock status.
*/
err = snd_dice_transaction_read_global(dice, GLOBAL_STATUS,
&nominal, sizeof(nominal));
if (err < 0)
return err;
if (!(be32_to_cpu(nominal) & STATUS_SOURCE_LOCKED))
return -ETIMEDOUT;
} }
return -EINVAL;
return 0;
} }
static void release_resources(struct snd_dice *dice, static int get_register_params(struct snd_dice *dice,
struct fw_iso_resources *resources) struct reg_params *tx_params,
struct reg_params *rx_params)
{ {
__be32 channel; __be32 reg[2];
int err;
/* Reset channel number */ err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg, sizeof(reg));
channel = cpu_to_be32((u32)-1); if (err < 0)
if (resources == &dice->tx_resources) return err;
snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, tx_params->count =
&channel, sizeof(channel)); min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
else tx_params->size = be32_to_cpu(reg[1]) * 4;
snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
&channel, sizeof(channel));
fw_iso_resources_free(resources); err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg, sizeof(reg));
if (err < 0)
return err;
rx_params->count =
min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
rx_params->size = be32_to_cpu(reg[1]) * 4;
return 0;
}
static void release_resources(struct snd_dice *dice)
{
unsigned int i;
for (i = 0; i < MAX_STREAMS; i++) {
if (amdtp_stream_running(&dice->tx_stream[i])) {
amdtp_stream_pcm_abort(&dice->tx_stream[i]);
amdtp_stream_stop(&dice->tx_stream[i]);
}
if (amdtp_stream_running(&dice->rx_stream[i])) {
amdtp_stream_pcm_abort(&dice->rx_stream[i]);
amdtp_stream_stop(&dice->rx_stream[i]);
}
fw_iso_resources_free(&dice->tx_resources[i]);
fw_iso_resources_free(&dice->rx_resources[i]);
}
}
static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
struct reg_params *params)
{
__be32 reg;
unsigned int i;
for (i = 0; i < params->count; i++) {
reg = cpu_to_be32((u32)-1);
if (dir == AMDTP_IN_STREAM) {
snd_dice_transaction_write_tx(dice,
params->size * i + TX_ISOCHRONOUS,
&reg, sizeof(reg));
} else {
snd_dice_transaction_write_rx(dice,
params->size * i + RX_ISOCHRONOUS,
&reg, sizeof(reg));
}
}
} }
static int keep_resources(struct snd_dice *dice, static int keep_resources(struct snd_dice *dice,
struct fw_iso_resources *resources, enum amdtp_stream_direction dir, unsigned int index,
unsigned int max_payload_bytes) unsigned int rate, unsigned int pcm_chs,
{ unsigned int midi_ports)
__be32 channel;
int err;
err = fw_iso_resources_allocate(resources, max_payload_bytes,
fw_parent_device(dice->unit)->max_speed);
if (err < 0)
goto end;
/* Set channel number */
channel = cpu_to_be32(resources->channel);
if (resources == &dice->tx_resources)
err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
&channel, sizeof(channel));
else
err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
&channel, sizeof(channel));
if (err < 0)
release_resources(dice, resources);
end:
return err;
}
static void stop_stream(struct snd_dice *dice, struct amdtp_stream *stream)
{
amdtp_stream_pcm_abort(stream);
amdtp_stream_stop(stream);
if (stream == &dice->tx_stream)
release_resources(dice, &dice->tx_resources);
else
release_resources(dice, &dice->rx_resources);
}
static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
unsigned int rate)
{ {
struct amdtp_stream *stream;
struct fw_iso_resources *resources; struct fw_iso_resources *resources;
unsigned int i, mode, pcm_chs, midi_ports;
bool double_pcm_frames; bool double_pcm_frames;
unsigned int i;
int err; int err;
err = snd_dice_stream_get_rate_mode(dice, rate, &mode); if (dir == AMDTP_IN_STREAM) {
if (err < 0) stream = &dice->tx_stream[index];
goto end; resources = &dice->tx_resources[index];
if (stream == &dice->tx_stream) {
resources = &dice->tx_resources;
pcm_chs = dice->tx_channels[mode];
midi_ports = dice->tx_midi_ports[mode];
} else { } else {
resources = &dice->rx_resources; stream = &dice->rx_stream[index];
pcm_chs = dice->rx_channels[mode]; resources = &dice->rx_resources[index];
midi_ports = dice->rx_midi_ports[mode];
} }
/* /*
@ -126,7 +162,7 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
* For this quirk, blocking mode is required and PCM buffer size should * For this quirk, blocking mode is required and PCM buffer size should
* be aligned to SYT_INTERVAL. * be aligned to SYT_INTERVAL.
*/ */
double_pcm_frames = mode > 1; double_pcm_frames = rate > 96000;
if (double_pcm_frames) { if (double_pcm_frames) {
rate /= 2; rate /= 2;
pcm_chs *= 2; pcm_chs *= 2;
@ -135,7 +171,7 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
err = amdtp_am824_set_parameters(stream, rate, pcm_chs, midi_ports, err = amdtp_am824_set_parameters(stream, rate, pcm_chs, midi_ports,
double_pcm_frames); double_pcm_frames);
if (err < 0) if (err < 0)
goto end; return err;
if (double_pcm_frames) { if (double_pcm_frames) {
pcm_chs /= 2; pcm_chs /= 2;
@ -147,158 +183,201 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
} }
} }
err = keep_resources(dice, resources, return fw_iso_resources_allocate(resources,
amdtp_stream_get_max_payload(stream)); amdtp_stream_get_max_payload(stream),
if (err < 0) { fw_parent_device(dice->unit)->max_speed);
dev_err(&dice->unit->device,
"fail to keep isochronous resources\n");
goto end;
}
err = amdtp_stream_start(stream, resources->channel,
fw_parent_device(dice->unit)->max_speed);
if (err < 0)
release_resources(dice, resources);
end:
return err;
} }
static int get_sync_mode(struct snd_dice *dice, enum cip_flags *sync_mode) static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
unsigned int rate, struct reg_params *params)
{ {
u32 source; __be32 reg[2];
int err; unsigned int i, pcm_chs, midi_ports;
struct amdtp_stream *streams;
err = snd_dice_transaction_get_clock_source(dice, &source); struct fw_iso_resources *resources;
if (err < 0)
goto end;
switch (source) {
/* So-called 'SYT Match' modes, sync_to_syt value of packets received */
case CLOCK_SOURCE_ARX4: /* in 4th stream */
case CLOCK_SOURCE_ARX3: /* in 3rd stream */
case CLOCK_SOURCE_ARX2: /* in 2nd stream */
err = -ENOSYS;
break;
case CLOCK_SOURCE_ARX1: /* in 1st stream, which this driver uses */
*sync_mode = 0;
break;
default:
*sync_mode = CIP_SYNC_TO_DEVICE;
break;
}
end:
return err;
}
int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate)
{
struct amdtp_stream *master, *slave;
unsigned int curr_rate;
enum cip_flags sync_mode;
int err = 0; int err = 0;
if (dice->substreams_counter == 0) if (dir == AMDTP_IN_STREAM) {
goto end; streams = dice->tx_stream;
resources = dice->tx_resources;
err = get_sync_mode(dice, &sync_mode);
if (err < 0)
goto end;
if (sync_mode == CIP_SYNC_TO_DEVICE) {
master = &dice->tx_stream;
slave = &dice->rx_stream;
} else { } else {
master = &dice->rx_stream; streams = dice->rx_stream;
slave = &dice->tx_stream; resources = dice->rx_resources;
} }
/* Some packet queueing errors. */ for (i = 0; i < params->count; i++) {
if (amdtp_streaming_error(master) || amdtp_streaming_error(slave)) if (dir == AMDTP_IN_STREAM) {
stop_stream(dice, master); err = snd_dice_transaction_read_tx(dice,
params->size * i + TX_NUMBER_AUDIO,
reg, sizeof(reg));
} else {
err = snd_dice_transaction_read_rx(dice,
params->size * i + RX_NUMBER_AUDIO,
reg, sizeof(reg));
}
if (err < 0)
return err;
pcm_chs = be32_to_cpu(reg[0]);
midi_ports = be32_to_cpu(reg[1]);
err = keep_resources(dice, dir, i, rate, pcm_chs, midi_ports);
if (err < 0)
return err;
reg[0] = cpu_to_be32(resources[i].channel);
if (dir == AMDTP_IN_STREAM) {
err = snd_dice_transaction_write_tx(dice,
params->size * i + TX_ISOCHRONOUS,
reg, sizeof(reg[0]));
} else {
err = snd_dice_transaction_write_rx(dice,
params->size * i + RX_ISOCHRONOUS,
reg, sizeof(reg[0]));
}
if (err < 0)
return err;
err = amdtp_stream_start(&streams[i], resources[i].channel,
fw_parent_device(dice->unit)->max_speed);
if (err < 0)
return err;
}
return err;
}
/*
* MEMO: After this function, there're two states of streams:
* - None streams are running.
* - All streams are running.
*/
int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate)
{
unsigned int curr_rate;
unsigned int i;
struct reg_params tx_params, rx_params;
bool need_to_start;
int err;
if (dice->substreams_counter == 0)
return -EIO;
err = get_register_params(dice, &tx_params, &rx_params);
if (err < 0)
return err;
/* Stop stream if rate is different. */
err = snd_dice_transaction_get_rate(dice, &curr_rate); err = snd_dice_transaction_get_rate(dice, &curr_rate);
if (err < 0) { if (err < 0) {
dev_err(&dice->unit->device, dev_err(&dice->unit->device,
"fail to get sampling rate\n"); "fail to get sampling rate\n");
goto end; return err;
} }
if (rate == 0) if (rate == 0)
rate = curr_rate; rate = curr_rate;
if (rate != curr_rate) if (rate != curr_rate)
stop_stream(dice, master); return -EINVAL;
if (!amdtp_stream_running(master)) { /* Judge to need to restart streams. */
stop_stream(dice, slave); for (i = 0; i < MAX_STREAMS; i++) {
if (i < tx_params.count) {
if (amdtp_streaming_error(&dice->tx_stream[i]) ||
!amdtp_stream_running(&dice->tx_stream[i]))
break;
}
if (i < rx_params.count) {
if (amdtp_streaming_error(&dice->rx_stream[i]) ||
!amdtp_stream_running(&dice->rx_stream[i]))
break;
}
}
need_to_start = (i < MAX_STREAMS);
if (need_to_start) {
/* Stop transmission. */
snd_dice_transaction_clear_enable(dice); snd_dice_transaction_clear_enable(dice);
stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
release_resources(dice);
amdtp_stream_set_sync(sync_mode, master, slave); err = ensure_phase_lock(dice);
err = snd_dice_transaction_set_rate(dice, rate);
if (err < 0) { if (err < 0) {
dev_err(&dice->unit->device, dev_err(&dice->unit->device,
"fail to set sampling rate\n"); "fail to ensure phase lock\n");
goto end; return err;
} }
/* Start both streams. */ /* Start both streams. */
err = start_stream(dice, master, rate); err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params);
if (err < 0) { if (err < 0)
dev_err(&dice->unit->device, goto error;
"fail to start AMDTP master stream\n"); err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params);
goto end; if (err < 0)
} goto error;
err = start_stream(dice, slave, rate);
if (err < 0) {
dev_err(&dice->unit->device,
"fail to start AMDTP slave stream\n");
stop_stream(dice, master);
goto end;
}
err = snd_dice_transaction_set_enable(dice); err = snd_dice_transaction_set_enable(dice);
if (err < 0) { if (err < 0) {
dev_err(&dice->unit->device, dev_err(&dice->unit->device,
"fail to enable interface\n"); "fail to enable interface\n");
stop_stream(dice, master); goto error;
stop_stream(dice, slave);
goto end;
} }
/* Wait first callbacks */ for (i = 0; i < MAX_STREAMS; i++) {
if (!amdtp_stream_wait_callback(master, CALLBACK_TIMEOUT) || if ((i < tx_params.count &&
!amdtp_stream_wait_callback(slave, CALLBACK_TIMEOUT)) { !amdtp_stream_wait_callback(&dice->tx_stream[i],
snd_dice_transaction_clear_enable(dice); CALLBACK_TIMEOUT)) ||
stop_stream(dice, master); (i < rx_params.count &&
stop_stream(dice, slave); !amdtp_stream_wait_callback(&dice->rx_stream[i],
err = -ETIMEDOUT; CALLBACK_TIMEOUT))) {
err = -ETIMEDOUT;
goto error;
}
} }
} }
end:
return err;
error:
snd_dice_transaction_clear_enable(dice);
stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
release_resources(dice);
return err; return err;
} }
/*
* MEMO: After this function, there're two states of streams:
* - None streams are running.
* - All streams are running.
*/
void snd_dice_stream_stop_duplex(struct snd_dice *dice) void snd_dice_stream_stop_duplex(struct snd_dice *dice)
{ {
struct reg_params tx_params, rx_params;
if (dice->substreams_counter > 0) if (dice->substreams_counter > 0)
return; return;
snd_dice_transaction_clear_enable(dice); snd_dice_transaction_clear_enable(dice);
stop_stream(dice, &dice->tx_stream); if (get_register_params(dice, &tx_params, &rx_params) == 0) {
stop_stream(dice, &dice->rx_stream); stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
}
release_resources(dice);
} }
static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream) static int init_stream(struct snd_dice *dice, enum amdtp_stream_direction dir,
unsigned int index)
{ {
int err; struct amdtp_stream *stream;
struct fw_iso_resources *resources; struct fw_iso_resources *resources;
enum amdtp_stream_direction dir; int err;
if (stream == &dice->tx_stream) { if (dir == AMDTP_IN_STREAM) {
resources = &dice->tx_resources; stream = &dice->tx_stream[index];
dir = AMDTP_IN_STREAM; resources = &dice->tx_resources[index];
} else { } else {
resources = &dice->rx_resources; stream = &dice->rx_stream[index];
dir = AMDTP_OUT_STREAM; resources = &dice->rx_resources[index];
} }
err = fw_iso_resources_init(resources, dice->unit); err = fw_iso_resources_init(resources, dice->unit);
@ -319,14 +398,20 @@ end:
* This function should be called before starting streams or after stopping * This function should be called before starting streams or after stopping
* streams. * streams.
*/ */
static void destroy_stream(struct snd_dice *dice, struct amdtp_stream *stream) static void destroy_stream(struct snd_dice *dice,
enum amdtp_stream_direction dir,
unsigned int index)
{ {
struct amdtp_stream *stream;
struct fw_iso_resources *resources; struct fw_iso_resources *resources;
if (stream == &dice->tx_stream) if (dir == AMDTP_IN_STREAM) {
resources = &dice->tx_resources; stream = &dice->tx_stream[index];
else resources = &dice->tx_resources[index];
resources = &dice->rx_resources; } else {
stream = &dice->rx_stream[index];
resources = &dice->rx_resources[index];
}
amdtp_stream_destroy(stream); amdtp_stream_destroy(stream);
fw_iso_resources_destroy(resources); fw_iso_resources_destroy(resources);
@ -334,33 +419,51 @@ static void destroy_stream(struct snd_dice *dice, struct amdtp_stream *stream)
int snd_dice_stream_init_duplex(struct snd_dice *dice) int snd_dice_stream_init_duplex(struct snd_dice *dice)
{ {
int err; int i, err;
dice->substreams_counter = 0; for (i = 0; i < MAX_STREAMS; i++) {
err = init_stream(dice, AMDTP_IN_STREAM, i);
if (err < 0) {
for (; i >= 0; i--)
destroy_stream(dice, AMDTP_OUT_STREAM, i);
goto end;
}
}
err = init_stream(dice, &dice->tx_stream); for (i = 0; i < MAX_STREAMS; i++) {
if (err < 0) err = init_stream(dice, AMDTP_OUT_STREAM, i);
goto end; if (err < 0) {
for (; i >= 0; i--)
err = init_stream(dice, &dice->rx_stream); destroy_stream(dice, AMDTP_OUT_STREAM, i);
if (err < 0) for (i = 0; i < MAX_STREAMS; i++)
destroy_stream(dice, &dice->tx_stream); destroy_stream(dice, AMDTP_IN_STREAM, i);
break;
}
}
end: end:
return err; return err;
} }
void snd_dice_stream_destroy_duplex(struct snd_dice *dice) void snd_dice_stream_destroy_duplex(struct snd_dice *dice)
{ {
struct reg_params tx_params, rx_params;
snd_dice_transaction_clear_enable(dice); snd_dice_transaction_clear_enable(dice);
destroy_stream(dice, &dice->tx_stream); if (get_register_params(dice, &tx_params, &rx_params) == 0) {
destroy_stream(dice, &dice->rx_stream); stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
}
release_resources(dice);
dice->substreams_counter = 0; dice->substreams_counter = 0;
} }
void snd_dice_stream_update_duplex(struct snd_dice *dice) void snd_dice_stream_update_duplex(struct snd_dice *dice)
{ {
struct reg_params tx_params, rx_params;
/* /*
* On a bus reset, the DICE firmware disables streaming and then goes * On a bus reset, the DICE firmware disables streaming and then goes
* off contemplating its own navel for hundreds of milliseconds before * off contemplating its own navel for hundreds of milliseconds before
@ -371,11 +474,10 @@ void snd_dice_stream_update_duplex(struct snd_dice *dice)
*/ */
dice->global_enabled = false; dice->global_enabled = false;
stop_stream(dice, &dice->rx_stream); if (get_register_params(dice, &tx_params, &rx_params) == 0) {
stop_stream(dice, &dice->tx_stream); stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
fw_iso_resources_update(&dice->rx_resources); }
fw_iso_resources_update(&dice->tx_resources);
} }
static void dice_lock_changed(struct snd_dice *dice) static void dice_lock_changed(struct snd_dice *dice)

View File

@ -9,8 +9,6 @@
#include "dice.h" #include "dice.h"
#define NOTIFICATION_TIMEOUT_MS (2 * MSEC_PER_SEC)
static u64 get_subaddr(struct snd_dice *dice, enum snd_dice_addr_type type, static u64 get_subaddr(struct snd_dice *dice, enum snd_dice_addr_type type,
u64 offset) u64 offset)
{ {
@ -62,54 +60,6 @@ static unsigned int get_clock_info(struct snd_dice *dice, __be32 *info)
info, 4); info, 4);
} }
static int set_clock_info(struct snd_dice *dice,
unsigned int rate, unsigned int source)
{
unsigned int i;
__be32 info;
u32 mask;
u32 clock;
int err;
err = get_clock_info(dice, &info);
if (err < 0)
return err;
clock = be32_to_cpu(info);
if (source != UINT_MAX) {
mask = CLOCK_SOURCE_MASK;
clock &= ~mask;
clock |= source;
}
if (rate != UINT_MAX) {
for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) {
if (snd_dice_rates[i] == rate)
break;
}
if (i == ARRAY_SIZE(snd_dice_rates))
return -EINVAL;
mask = CLOCK_RATE_MASK;
clock &= ~mask;
clock |= i << CLOCK_RATE_SHIFT;
}
info = cpu_to_be32(clock);
if (completion_done(&dice->clock_accepted))
reinit_completion(&dice->clock_accepted);
err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
&info, 4);
if (err < 0)
return err;
if (wait_for_completion_timeout(&dice->clock_accepted,
msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0)
return -ETIMEDOUT;
return 0;
}
int snd_dice_transaction_get_clock_source(struct snd_dice *dice, int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
unsigned int *source) unsigned int *source)
{ {
@ -143,10 +93,6 @@ int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate)
end: end:
return err; return err;
} }
int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate)
{
return set_clock_info(dice, rate, UINT_MAX);
}
int snd_dice_transaction_set_enable(struct snd_dice *dice) int snd_dice_transaction_set_enable(struct snd_dice *dice)
{ {
@ -210,7 +156,7 @@ static void dice_notification(struct fw_card *card, struct fw_request *request,
fw_send_response(card, request, RCODE_COMPLETE); fw_send_response(card, request, RCODE_COMPLETE);
if (bits & NOTIFY_CLOCK_ACCEPTED) if (bits & NOTIFY_LOCK_CHG)
complete(&dice->clock_accepted); complete(&dice->clock_accepted);
wake_up(&dice->hwdep_wait); wake_up(&dice->hwdep_wait);
} }

View File

@ -13,6 +13,8 @@ MODULE_LICENSE("GPL v2");
#define OUI_WEISS 0x001c6a #define OUI_WEISS 0x001c6a
#define OUI_LOUD 0x000ff2 #define OUI_LOUD 0x000ff2
#define OUI_FOCUSRITE 0x00130e
#define OUI_TCELECTRONIC 0x001486
#define DICE_CATEGORY_ID 0x04 #define DICE_CATEGORY_ID 0x04
#define WEISS_CATEGORY_ID 0x00 #define WEISS_CATEGORY_ID 0x00
@ -20,6 +22,36 @@ MODULE_LICENSE("GPL v2");
#define PROBE_DELAY_MS (2 * MSEC_PER_SEC) #define PROBE_DELAY_MS (2 * MSEC_PER_SEC)
/*
* Some models support several isochronous channels, while these streams are not
* always available. In this case, add the model name to this list.
*/
static bool force_two_pcm_support(struct fw_unit *unit)
{
const char *const models[] = {
/* TC Electronic models. */
"StudioKonnekt48",
/* Focusrite models. */
"SAFFIRE_PRO_40",
"LIQUID_SAFFIRE_56",
"SAFFIRE_PRO_40_1",
};
char model[32];
unsigned int i;
int err;
err = fw_csr_string(unit->directory, CSR_MODEL, model, sizeof(model));
if (err < 0)
return false;
for (i = 0; i < ARRAY_SIZE(models); i++) {
if (strcmp(models[i], model) == 0)
break;
}
return i < ARRAY_SIZE(models);
}
static int check_dice_category(struct fw_unit *unit) static int check_dice_category(struct fw_unit *unit)
{ {
struct fw_device *device = fw_parent_device(unit); struct fw_device *device = fw_parent_device(unit);
@ -44,6 +76,12 @@ static int check_dice_category(struct fw_unit *unit)
break; break;
} }
} }
if (vendor == OUI_FOCUSRITE || vendor == OUI_TCELECTRONIC) {
if (force_two_pcm_support(unit))
return 0;
}
if (vendor == OUI_WEISS) if (vendor == OUI_WEISS)
category = WEISS_CATEGORY_ID; category = WEISS_CATEGORY_ID;
else if (vendor == OUI_LOUD) else if (vendor == OUI_LOUD)
@ -57,65 +95,10 @@ static int check_dice_category(struct fw_unit *unit)
return 0; return 0;
} }
static int highest_supported_mode_rate(struct snd_dice *dice, static int check_clock_caps(struct snd_dice *dice)
unsigned int mode, unsigned int *rate)
{
unsigned int i, m;
for (i = ARRAY_SIZE(snd_dice_rates); i > 0; i--) {
*rate = snd_dice_rates[i - 1];
if (snd_dice_stream_get_rate_mode(dice, *rate, &m) < 0)
continue;
if (mode == m)
break;
}
if (i == 0)
return -EINVAL;
return 0;
}
static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode)
{
__be32 values[2];
unsigned int rate;
int err;
if (highest_supported_mode_rate(dice, mode, &rate) < 0) {
dice->tx_channels[mode] = 0;
dice->tx_midi_ports[mode] = 0;
dice->rx_channels[mode] = 0;
dice->rx_midi_ports[mode] = 0;
return 0;
}
err = snd_dice_transaction_set_rate(dice, rate);
if (err < 0)
return err;
err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO,
values, sizeof(values));
if (err < 0)
return err;
dice->tx_channels[mode] = be32_to_cpu(values[0]);
dice->tx_midi_ports[mode] = be32_to_cpu(values[1]);
err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO,
values, sizeof(values));
if (err < 0)
return err;
dice->rx_channels[mode] = be32_to_cpu(values[0]);
dice->rx_midi_ports[mode] = be32_to_cpu(values[1]);
return 0;
}
static int dice_read_params(struct snd_dice *dice)
{ {
__be32 value; __be32 value;
int mode, err; int err;
/* some very old firmwares don't tell about their clock support */ /* some very old firmwares don't tell about their clock support */
if (dice->clock_caps > 0) { if (dice->clock_caps > 0) {
@ -133,12 +116,6 @@ static int dice_read_params(struct snd_dice *dice)
CLOCK_CAP_SOURCE_INTERNAL; CLOCK_CAP_SOURCE_INTERNAL;
} }
for (mode = 2; mode >= 0; --mode) {
err = dice_read_mode_params(dice, mode);
if (err < 0)
return err;
}
return 0; return 0;
} }
@ -211,11 +188,14 @@ static void do_registration(struct work_struct *work)
if (err < 0) if (err < 0)
return; return;
if (force_two_pcm_support(dice->unit))
dice->force_two_pcms = true;
err = snd_dice_transaction_init(dice); err = snd_dice_transaction_init(dice);
if (err < 0) if (err < 0)
goto error; goto error;
err = dice_read_params(dice); err = check_clock_caps(dice);
if (err < 0) if (err < 0)
goto error; goto error;

View File

@ -39,6 +39,29 @@
#include "../lib.h" #include "../lib.h"
#include "dice-interface.h" #include "dice-interface.h"
/*
* This module support maximum 2 pairs of tx/rx isochronous streams for
* our convinience.
*
* In documents for ASICs called with a name of 'DICE':
* - ASIC for DICE II:
* - Maximum 2 tx and 4 rx are supported.
* - A packet supports maximum 16 data channels.
* - TCD2210/2210-E (so-called 'Dice Mini'):
* - Maximum 2 tx and 2 rx are supported.
* - A packet supports maximum 16 data channels.
* - TCD2220/2220-E (so-called 'Dice Jr.')
* - 2 tx and 2 rx are supported.
* - A packet supports maximum 16 data channels.
* - TCD3070-CH (so-called 'Dice III')
* - Maximum 2 tx and 2 rx are supported.
* - A packet supports maximum 32 data channels.
*
* For the above, MIDI conformant data channel is just on the first isochronous
* stream.
*/
#define MAX_STREAMS 2
struct snd_dice { struct snd_dice {
struct snd_card *card; struct snd_card *card;
struct fw_unit *unit; struct fw_unit *unit;
@ -56,10 +79,6 @@ struct snd_dice {
unsigned int rsrv_offset; unsigned int rsrv_offset;
unsigned int clock_caps; unsigned int clock_caps;
unsigned int tx_channels[3];
unsigned int rx_channels[3];
unsigned int tx_midi_ports[3];
unsigned int rx_midi_ports[3];
struct fw_address_handler notification_handler; struct fw_address_handler notification_handler;
int owner_generation; int owner_generation;
@ -71,13 +90,15 @@ struct snd_dice {
wait_queue_head_t hwdep_wait; wait_queue_head_t hwdep_wait;
/* For streaming */ /* For streaming */
struct fw_iso_resources tx_resources; struct fw_iso_resources tx_resources[MAX_STREAMS];
struct fw_iso_resources rx_resources; struct fw_iso_resources rx_resources[MAX_STREAMS];
struct amdtp_stream tx_stream; struct amdtp_stream tx_stream[MAX_STREAMS];
struct amdtp_stream rx_stream; struct amdtp_stream rx_stream[MAX_STREAMS];
bool global_enabled; bool global_enabled;
struct completion clock_accepted; struct completion clock_accepted;
unsigned int substreams_counter; unsigned int substreams_counter;
bool force_two_pcms;
}; };
enum snd_dice_addr_type { enum snd_dice_addr_type {
@ -158,7 +179,6 @@ static inline int snd_dice_transaction_read_sync(struct snd_dice *dice,
int snd_dice_transaction_get_clock_source(struct snd_dice *dice, int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
unsigned int *source); unsigned int *source);
int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate);
int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate); int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate);
int snd_dice_transaction_set_enable(struct snd_dice *dice); int snd_dice_transaction_set_enable(struct snd_dice *dice);
void snd_dice_transaction_clear_enable(struct snd_dice *dice); void snd_dice_transaction_clear_enable(struct snd_dice *dice);
@ -169,9 +189,6 @@ void snd_dice_transaction_destroy(struct snd_dice *dice);
#define SND_DICE_RATES_COUNT 7 #define SND_DICE_RATES_COUNT 7
extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT]; extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT];
int snd_dice_stream_get_rate_mode(struct snd_dice *dice,
unsigned int rate, unsigned int *mode);
int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate); int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate);
void snd_dice_stream_stop_duplex(struct snd_dice *dice); void snd_dice_stream_stop_duplex(struct snd_dice *dice);
int snd_dice_stream_init_duplex(struct snd_dice *dice); int snd_dice_stream_init_duplex(struct snd_dice *dice);

View File

@ -301,7 +301,10 @@ static void efw_update(struct fw_unit *unit)
struct snd_efw *efw = dev_get_drvdata(&unit->device); struct snd_efw *efw = dev_get_drvdata(&unit->device);
snd_efw_transaction_bus_reset(efw->unit); snd_efw_transaction_bus_reset(efw->unit);
mutex_lock(&efw->mutex);
snd_efw_stream_update_duplex(efw); snd_efw_stream_update_duplex(efw);
mutex_unlock(&efw->mutex);
} }
static void efw_remove(struct fw_unit *unit) static void efw_remove(struct fw_unit *unit)

View File

@ -313,12 +313,10 @@ void snd_efw_stream_stop_duplex(struct snd_efw *efw)
void snd_efw_stream_update_duplex(struct snd_efw *efw) void snd_efw_stream_update_duplex(struct snd_efw *efw)
{ {
if ((cmp_connection_update(&efw->out_conn) < 0) || if (cmp_connection_update(&efw->out_conn) < 0 ||
(cmp_connection_update(&efw->in_conn) < 0)) { cmp_connection_update(&efw->in_conn) < 0) {
mutex_lock(&efw->mutex);
stop_stream(efw, &efw->rx_stream); stop_stream(efw, &efw->rx_stream);
stop_stream(efw, &efw->tx_stream); stop_stream(efw, &efw->tx_stream);
mutex_unlock(&efw->mutex);
} else { } else {
amdtp_stream_update(&efw->rx_stream); amdtp_stream_update(&efw->rx_stream);
amdtp_stream_update(&efw->tx_stream); amdtp_stream_update(&efw->tx_stream);

View File

@ -26,11 +26,13 @@ struct fw_scs1x {
u8 output_bytes; u8 output_bytes;
bool output_escaped; bool output_escaped;
bool output_escape_high_nibble; bool output_escape_high_nibble;
struct tasklet_struct tasklet; struct work_struct work;
wait_queue_head_t idle_wait; wait_queue_head_t idle_wait;
u8 buffer[HSS1394_MAX_PACKET_SIZE]; u8 buffer[HSS1394_MAX_PACKET_SIZE];
bool transaction_running; bool transaction_running;
struct fw_transaction transaction; struct fw_transaction transaction;
unsigned int transaction_bytes;
bool error;
struct fw_device *fw_dev; struct fw_device *fw_dev;
}; };
@ -125,11 +127,16 @@ static void scs_write_callback(struct fw_card *card, int rcode,
{ {
struct fw_scs1x *scs = callback_data; struct fw_scs1x *scs = callback_data;
if (rcode == RCODE_GENERATION) if (!rcode_is_permanent_error(rcode)) {
; /* TODO: retry this packet */ /* Don't retry for this data. */
if (rcode == RCODE_COMPLETE)
scs->transaction_bytes = 0;
} else {
scs->error = true;
}
scs->transaction_running = false; scs->transaction_running = false;
tasklet_schedule(&scs->tasklet); schedule_work(&scs->work);
} }
static bool is_valid_running_status(u8 status) static bool is_valid_running_status(u8 status)
@ -165,9 +172,9 @@ static bool is_invalid_cmd(u8 status)
status == 0xfd; status == 0xfd;
} }
static void scs_output_tasklet(unsigned long data) static void scs_output_work(struct work_struct *work)
{ {
struct fw_scs1x *scs = (struct fw_scs1x *)data; struct fw_scs1x *scs = container_of(work, struct fw_scs1x, work);
struct snd_rawmidi_substream *stream; struct snd_rawmidi_substream *stream;
unsigned int i; unsigned int i;
u8 byte; u8 byte;
@ -177,12 +184,15 @@ static void scs_output_tasklet(unsigned long data)
return; return;
stream = ACCESS_ONCE(scs->output); stream = ACCESS_ONCE(scs->output);
if (!stream) { if (!stream || scs->error) {
scs->output_idle = true; scs->output_idle = true;
wake_up(&scs->idle_wait); wake_up(&scs->idle_wait);
return; return;
} }
if (scs->transaction_bytes > 0)
goto retry;
i = scs->output_bytes; i = scs->output_bytes;
for (;;) { for (;;) {
if (snd_rawmidi_transmit(stream, &byte, 1) != 1) { if (snd_rawmidi_transmit(stream, &byte, 1) != 1) {
@ -253,13 +263,16 @@ static void scs_output_tasklet(unsigned long data)
scs->output_bytes = 1; scs->output_bytes = 1;
scs->output_escaped = false; scs->output_escaped = false;
scs->transaction_bytes = i;
retry:
scs->transaction_running = true; scs->transaction_running = true;
generation = scs->fw_dev->generation; generation = scs->fw_dev->generation;
smp_rmb(); /* node_id vs. generation */ smp_rmb(); /* node_id vs. generation */
fw_send_request(scs->fw_dev->card, &scs->transaction, fw_send_request(scs->fw_dev->card, &scs->transaction,
TCODE_WRITE_BLOCK_REQUEST, scs->fw_dev->node_id, TCODE_WRITE_BLOCK_REQUEST, scs->fw_dev->node_id,
generation, scs->fw_dev->max_speed, HSS1394_ADDRESS, generation, scs->fw_dev->max_speed, HSS1394_ADDRESS,
scs->buffer, i, scs_write_callback, scs); scs->buffer, scs->transaction_bytes,
scs_write_callback, scs);
} }
static int midi_capture_open(struct snd_rawmidi_substream *stream) static int midi_capture_open(struct snd_rawmidi_substream *stream)
@ -309,9 +322,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *stream, int up)
scs->output_bytes = 1; scs->output_bytes = 1;
scs->output_escaped = false; scs->output_escaped = false;
scs->output_idle = false; scs->output_idle = false;
scs->transaction_bytes = 0;
scs->error = false;
ACCESS_ONCE(scs->output) = stream; ACCESS_ONCE(scs->output) = stream;
tasklet_schedule(&scs->tasklet); schedule_work(&scs->work);
} else { } else {
ACCESS_ONCE(scs->output) = NULL; ACCESS_ONCE(scs->output) = NULL;
} }
@ -395,7 +410,7 @@ int snd_oxfw_scs1x_add(struct snd_oxfw *oxfw)
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
&midi_playback_ops); &midi_playback_ops);
tasklet_init(&scs->tasklet, scs_output_tasklet, (unsigned long)scs); INIT_WORK(&scs->work, scs_output_work);
init_waitqueue_head(&scs->idle_wait); init_waitqueue_head(&scs->idle_wait);
scs->output_idle = true; scs->output_idle = true;

View File

@ -1,5 +1,5 @@
snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \ snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \
hdac_regmap.o hdac_controller.o hdac_stream.o array.o hdac_regmap.o hdac_controller.o hdac_stream.o array.o hdmi_chmap.o
snd-hda-core-objs += trace.o snd-hda-core-objs += trace.o
CFLAGS_trace.o := -I$(src) CFLAGS_trace.o := -I$(src)

View File

@ -611,6 +611,22 @@ int snd_hdac_power_up_pm(struct hdac_device *codec)
} }
EXPORT_SYMBOL_GPL(snd_hdac_power_up_pm); EXPORT_SYMBOL_GPL(snd_hdac_power_up_pm);
/* like snd_hdac_power_up_pm(), but only increment the pm count when
* already powered up. Returns -1 if not powered up, 1 if incremented
* or 0 if unchanged. Only used in hdac_regmap.c
*/
int snd_hdac_keep_power_up(struct hdac_device *codec)
{
if (!atomic_inc_not_zero(&codec->in_pm)) {
int ret = pm_runtime_get_if_in_use(&codec->dev);
if (!ret)
return -1;
if (ret < 0)
return 0;
}
return 1;
}
/** /**
* snd_hdac_power_down_pm - power down the codec * snd_hdac_power_down_pm - power down the codec
* @codec: the codec object * @codec: the codec object

View File

@ -126,6 +126,8 @@ EXPORT_SYMBOL_GPL(snd_hdac_get_display_clk);
*/ */
static int pin2port(hda_nid_t pin_nid) static int pin2port(hda_nid_t pin_nid)
{ {
if (WARN_ON(pin_nid < 5 || pin_nid > 7))
return -1;
return pin_nid - 4; return pin_nid - 4;
} }
@ -144,10 +146,14 @@ static int pin2port(hda_nid_t pin_nid)
int snd_hdac_sync_audio_rate(struct hdac_bus *bus, hda_nid_t nid, int rate) int snd_hdac_sync_audio_rate(struct hdac_bus *bus, hda_nid_t nid, int rate)
{ {
struct i915_audio_component *acomp = bus->audio_component; struct i915_audio_component *acomp = bus->audio_component;
int port;
if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate) if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate)
return -ENODEV; return -ENODEV;
return acomp->ops->sync_audio_rate(acomp->dev, pin2port(nid), rate); port = pin2port(nid);
if (port < 0)
return -EINVAL;
return acomp->ops->sync_audio_rate(acomp->dev, port, rate);
} }
EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate); EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate);
@ -175,11 +181,15 @@ int snd_hdac_acomp_get_eld(struct hdac_bus *bus, hda_nid_t nid,
bool *audio_enabled, char *buffer, int max_bytes) bool *audio_enabled, char *buffer, int max_bytes)
{ {
struct i915_audio_component *acomp = bus->audio_component; struct i915_audio_component *acomp = bus->audio_component;
int port;
if (!acomp || !acomp->ops || !acomp->ops->get_eld) if (!acomp || !acomp->ops || !acomp->ops->get_eld)
return -ENODEV; return -ENODEV;
return acomp->ops->get_eld(acomp->dev, pin2port(nid), audio_enabled, port = pin2port(nid);
if (port < 0)
return -EINVAL;
return acomp->ops->get_eld(acomp->dev, port, audio_enabled,
buffer, max_bytes); buffer, max_bytes);
} }
EXPORT_SYMBOL_GPL(snd_hdac_acomp_get_eld); EXPORT_SYMBOL_GPL(snd_hdac_acomp_get_eld);

View File

@ -21,13 +21,16 @@
#include <sound/hdaudio.h> #include <sound/hdaudio.h>
#include <sound/hda_regmap.h> #include <sound/hda_regmap.h>
#ifdef CONFIG_PM static int codec_pm_lock(struct hdac_device *codec)
#define codec_is_running(codec) \ {
(atomic_read(&(codec)->in_pm) || \ return snd_hdac_keep_power_up(codec);
!pm_runtime_suspended(&(codec)->dev)) }
#else
#define codec_is_running(codec) true static void codec_pm_unlock(struct hdac_device *codec, int lock)
#endif {
if (lock == 1)
snd_hdac_power_down_pm(codec);
}
#define get_verb(reg) (((reg) >> 8) & 0xfff) #define get_verb(reg) (((reg) >> 8) & 0xfff)
@ -238,20 +241,28 @@ static int hda_reg_read(void *context, unsigned int reg, unsigned int *val)
struct hdac_device *codec = context; struct hdac_device *codec = context;
int verb = get_verb(reg); int verb = get_verb(reg);
int err; int err;
int pm_lock = 0;
if (!codec_is_running(codec) && verb != AC_VERB_GET_POWER_STATE) if (verb != AC_VERB_GET_POWER_STATE) {
return -EAGAIN; pm_lock = codec_pm_lock(codec);
if (pm_lock < 0)
return -EAGAIN;
}
reg |= (codec->addr << 28); reg |= (codec->addr << 28);
if (is_stereo_amp_verb(reg)) if (is_stereo_amp_verb(reg)) {
return hda_reg_read_stereo_amp(codec, reg, val); err = hda_reg_read_stereo_amp(codec, reg, val);
if (verb == AC_VERB_GET_PROC_COEF) goto out;
return hda_reg_read_coef(codec, reg, val); }
if (verb == AC_VERB_GET_PROC_COEF) {
err = hda_reg_read_coef(codec, reg, val);
goto out;
}
if ((verb & 0x700) == AC_VERB_SET_AMP_GAIN_MUTE) if ((verb & 0x700) == AC_VERB_SET_AMP_GAIN_MUTE)
reg &= ~AC_AMP_FAKE_MUTE; reg &= ~AC_AMP_FAKE_MUTE;
err = snd_hdac_exec_verb(codec, reg, 0, val); err = snd_hdac_exec_verb(codec, reg, 0, val);
if (err < 0) if (err < 0)
return err; goto out;
/* special handling for asymmetric reads */ /* special handling for asymmetric reads */
if (verb == AC_VERB_GET_POWER_STATE) { if (verb == AC_VERB_GET_POWER_STATE) {
if (*val & AC_PWRST_ERROR) if (*val & AC_PWRST_ERROR)
@ -259,7 +270,9 @@ static int hda_reg_read(void *context, unsigned int reg, unsigned int *val)
else /* take only the actual state */ else /* take only the actual state */
*val = (*val >> 4) & 0x0f; *val = (*val >> 4) & 0x0f;
} }
return 0; out:
codec_pm_unlock(codec, pm_lock);
return err;
} }
static int hda_reg_write(void *context, unsigned int reg, unsigned int val) static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
@ -267,6 +280,7 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
struct hdac_device *codec = context; struct hdac_device *codec = context;
unsigned int verb; unsigned int verb;
int i, bytes, err; int i, bytes, err;
int pm_lock = 0;
if (codec->caps_overwriting) if (codec->caps_overwriting)
return 0; return 0;
@ -275,14 +289,21 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
reg |= (codec->addr << 28); reg |= (codec->addr << 28);
verb = get_verb(reg); verb = get_verb(reg);
if (!codec_is_running(codec) && verb != AC_VERB_SET_POWER_STATE) if (verb != AC_VERB_SET_POWER_STATE) {
return codec->lazy_cache ? 0 : -EAGAIN; pm_lock = codec_pm_lock(codec);
if (pm_lock < 0)
return codec->lazy_cache ? 0 : -EAGAIN;
}
if (is_stereo_amp_verb(reg)) if (is_stereo_amp_verb(reg)) {
return hda_reg_write_stereo_amp(codec, reg, val); err = hda_reg_write_stereo_amp(codec, reg, val);
goto out;
}
if (verb == AC_VERB_SET_PROC_COEF) if (verb == AC_VERB_SET_PROC_COEF) {
return hda_reg_write_coef(codec, reg, val); err = hda_reg_write_coef(codec, reg, val);
goto out;
}
switch (verb & 0xf00) { switch (verb & 0xf00) {
case AC_VERB_SET_AMP_GAIN_MUTE: case AC_VERB_SET_AMP_GAIN_MUTE:
@ -319,10 +340,12 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
reg |= (verb + i) << 8 | ((val >> (8 * i)) & 0xff); reg |= (verb + i) << 8 | ((val >> (8 * i)) & 0xff);
err = snd_hdac_exec_verb(codec, reg, 0, NULL); err = snd_hdac_exec_verb(codec, reg, 0, NULL);
if (err < 0) if (err < 0)
return err; goto out;
} }
return 0; out:
codec_pm_unlock(codec, pm_lock);
return err;
} }
static const struct regmap_config hda_regmap_cfg = { static const struct regmap_config hda_regmap_cfg = {

791
sound/hda/hdmi_chmap.c Normal file
View File

@ -0,0 +1,791 @@
/*
* HDMI Channel map support helpers
*/
#include <linux/module.h>
#include <sound/control.h>
#include <sound/tlv.h>
#include <sound/hda_chmap.h>
/*
* CEA speaker placement:
*
* FLH FCH FRH
* FLW FL FLC FC FRC FR FRW
*
* LFE
* TC
*
* RL RLC RC RRC RR
*
* The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to
* CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC.
*/
enum cea_speaker_placement {
FL = (1 << 0), /* Front Left */
FC = (1 << 1), /* Front Center */
FR = (1 << 2), /* Front Right */
FLC = (1 << 3), /* Front Left Center */
FRC = (1 << 4), /* Front Right Center */
RL = (1 << 5), /* Rear Left */
RC = (1 << 6), /* Rear Center */
RR = (1 << 7), /* Rear Right */
RLC = (1 << 8), /* Rear Left Center */
RRC = (1 << 9), /* Rear Right Center */
LFE = (1 << 10), /* Low Frequency Effect */
FLW = (1 << 11), /* Front Left Wide */
FRW = (1 << 12), /* Front Right Wide */
FLH = (1 << 13), /* Front Left High */
FCH = (1 << 14), /* Front Center High */
FRH = (1 << 15), /* Front Right High */
TC = (1 << 16), /* Top Center */
};
static const char * const cea_speaker_allocation_names[] = {
/* 0 */ "FL/FR",
/* 1 */ "LFE",
/* 2 */ "FC",
/* 3 */ "RL/RR",
/* 4 */ "RC",
/* 5 */ "FLC/FRC",
/* 6 */ "RLC/RRC",
/* 7 */ "FLW/FRW",
/* 8 */ "FLH/FRH",
/* 9 */ "TC",
/* 10 */ "FCH",
};
/*
* ELD SA bits in the CEA Speaker Allocation data block
*/
static int eld_speaker_allocation_bits[] = {
[0] = FL | FR,
[1] = LFE,
[2] = FC,
[3] = RL | RR,
[4] = RC,
[5] = FLC | FRC,
[6] = RLC | RRC,
/* the following are not defined in ELD yet */
[7] = FLW | FRW,
[8] = FLH | FRH,
[9] = TC,
[10] = FCH,
};
/*
* ALSA sequence is:
*
* surround40 surround41 surround50 surround51 surround71
* ch0 front left = = = =
* ch1 front right = = = =
* ch2 rear left = = = =
* ch3 rear right = = = =
* ch4 LFE center center center
* ch5 LFE LFE
* ch6 side left
* ch7 side right
*
* surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR}
*/
static int hdmi_channel_mapping[0x32][8] = {
/* stereo */
[0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
/* 2.1 */
[0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
/* Dolby Surround */
[0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 },
/* surround40 */
[0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 },
/* 4ch */
[0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },
/* surround41 */
[0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 },
/* surround50 */
[0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },
/* surround51 */
[0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 },
/* 7.1 */
[0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 },
};
/*
* This is an ordered list!
*
* The preceding ones have better chances to be selected by
* hdmi_channel_allocation().
*/
static struct hdac_cea_channel_speaker_allocation channel_allocations[] = {
/* channel: 7 6 5 4 3 2 1 0 */
{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } },
/* 2.1 */
{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } },
/* Dolby Surround */
{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } },
/* surround40 */
{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } },
/* surround41 */
{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } },
/* surround50 */
{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } },
/* surround51 */
{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } },
/* 6.1 */
{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } },
/* surround71 */
{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } },
{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } },
{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } },
{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } },
{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } },
{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } },
{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } },
{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } },
{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } },
{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } },
{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } },
{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } },
{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } },
{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } },
{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } },
{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } },
{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } },
{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } },
{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } },
{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } },
{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } },
{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } },
{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } },
{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } },
{ .ca_index = 0x20, .speakers = { 0, FCH, RR, RL, FC, 0, FR, FL } },
{ .ca_index = 0x21, .speakers = { 0, FCH, RR, RL, FC, LFE, FR, FL } },
{ .ca_index = 0x22, .speakers = { TC, 0, RR, RL, FC, 0, FR, FL } },
{ .ca_index = 0x23, .speakers = { TC, 0, RR, RL, FC, LFE, FR, FL } },
{ .ca_index = 0x24, .speakers = { FRH, FLH, RR, RL, 0, 0, FR, FL } },
{ .ca_index = 0x25, .speakers = { FRH, FLH, RR, RL, 0, LFE, FR, FL } },
{ .ca_index = 0x26, .speakers = { FRW, FLW, RR, RL, 0, 0, FR, FL } },
{ .ca_index = 0x27, .speakers = { FRW, FLW, RR, RL, 0, LFE, FR, FL } },
{ .ca_index = 0x28, .speakers = { TC, RC, RR, RL, FC, 0, FR, FL } },
{ .ca_index = 0x29, .speakers = { TC, RC, RR, RL, FC, LFE, FR, FL } },
{ .ca_index = 0x2a, .speakers = { FCH, RC, RR, RL, FC, 0, FR, FL } },
{ .ca_index = 0x2b, .speakers = { FCH, RC, RR, RL, FC, LFE, FR, FL } },
{ .ca_index = 0x2c, .speakers = { TC, FCH, RR, RL, FC, 0, FR, FL } },
{ .ca_index = 0x2d, .speakers = { TC, FCH, RR, RL, FC, LFE, FR, FL } },
{ .ca_index = 0x2e, .speakers = { FRH, FLH, RR, RL, FC, 0, FR, FL } },
{ .ca_index = 0x2f, .speakers = { FRH, FLH, RR, RL, FC, LFE, FR, FL } },
{ .ca_index = 0x30, .speakers = { FRW, FLW, RR, RL, FC, 0, FR, FL } },
{ .ca_index = 0x31, .speakers = { FRW, FLW, RR, RL, FC, LFE, FR, FL } },
};
static int hdmi_pin_set_slot_channel(struct hdac_device *codec,
hda_nid_t pin_nid, int asp_slot, int channel)
{
return snd_hdac_codec_write(codec, pin_nid, 0,
AC_VERB_SET_HDMI_CHAN_SLOT,
(channel << 4) | asp_slot);
}
static int hdmi_pin_get_slot_channel(struct hdac_device *codec,
hda_nid_t pin_nid, int asp_slot)
{
return (snd_hdac_codec_read(codec, pin_nid, 0,
AC_VERB_GET_HDMI_CHAN_SLOT,
asp_slot) & 0xf0) >> 4;
}
static int hdmi_get_channel_count(struct hdac_device *codec, hda_nid_t cvt_nid)
{
return 1 + snd_hdac_codec_read(codec, cvt_nid, 0,
AC_VERB_GET_CVT_CHAN_COUNT, 0);
}
static void hdmi_set_channel_count(struct hdac_device *codec,
hda_nid_t cvt_nid, int chs)
{
if (chs != hdmi_get_channel_count(codec, cvt_nid))
snd_hdac_codec_write(codec, cvt_nid, 0,
AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
}
/*
* Channel mapping routines
*/
/*
* Compute derived values in channel_allocations[].
*/
static void init_channel_allocations(void)
{
int i, j;
struct hdac_cea_channel_speaker_allocation *p;
for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
p = channel_allocations + i;
p->channels = 0;
p->spk_mask = 0;
for (j = 0; j < ARRAY_SIZE(p->speakers); j++)
if (p->speakers[j]) {
p->channels++;
p->spk_mask |= p->speakers[j];
}
}
}
static int get_channel_allocation_order(int ca)
{
int i;
for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
if (channel_allocations[i].ca_index == ca)
break;
}
return i;
}
void snd_hdac_print_channel_allocation(int spk_alloc, char *buf, int buflen)
{
int i, j;
for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) {
if (spk_alloc & (1 << i))
j += snprintf(buf + j, buflen - j, " %s",
cea_speaker_allocation_names[i]);
}
buf[j] = '\0'; /* necessary when j == 0 */
}
EXPORT_SYMBOL_GPL(snd_hdac_print_channel_allocation);
/*
* The transformation takes two steps:
*
* eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask
* spk_mask => (channel_allocations[]) => ai->CA
*
* TODO: it could select the wrong CA from multiple candidates.
*/
static int hdmi_channel_allocation_spk_alloc_blk(struct hdac_device *codec,
int spk_alloc, int channels)
{
int i;
int ca = 0;
int spk_mask = 0;
char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
/*
* CA defaults to 0 for basic stereo audio
*/
if (channels <= 2)
return 0;
/*
* expand ELD's speaker allocation mask
*
* ELD tells the speaker mask in a compact(paired) form,
* expand ELD's notions to match the ones used by Audio InfoFrame.
*/
for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
if (spk_alloc & (1 << i))
spk_mask |= eld_speaker_allocation_bits[i];
}
/* search for the first working match in the CA table */
for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
if (channels == channel_allocations[i].channels &&
(spk_mask & channel_allocations[i].spk_mask) ==
channel_allocations[i].spk_mask) {
ca = channel_allocations[i].ca_index;
break;
}
}
if (!ca) {
/*
* if there was no match, select the regular ALSA channel
* allocation with the matching number of channels
*/
for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
if (channels == channel_allocations[i].channels) {
ca = channel_allocations[i].ca_index;
break;
}
}
}
snd_hdac_print_channel_allocation(spk_alloc, buf, sizeof(buf));
dev_dbg(&codec->dev, "HDMI: select CA 0x%x for %d-channel allocation: %s\n",
ca, channels, buf);
return ca;
}
static void hdmi_debug_channel_mapping(struct hdac_chmap *chmap,
hda_nid_t pin_nid)
{
#ifdef CONFIG_SND_DEBUG_VERBOSE
int i;
int channel;
for (i = 0; i < 8; i++) {
channel = chmap->ops.pin_get_slot_channel(
chmap->hdac, pin_nid, i);
dev_dbg(&chmap->hdac->dev, "HDMI: ASP channel %d => slot %d\n",
channel, i);
}
#endif
}
static void hdmi_std_setup_channel_mapping(struct hdac_chmap *chmap,
hda_nid_t pin_nid,
bool non_pcm,
int ca)
{
struct hdac_cea_channel_speaker_allocation *ch_alloc;
int i;
int err;
int order;
int non_pcm_mapping[8];
order = get_channel_allocation_order(ca);
ch_alloc = &channel_allocations[order];
if (hdmi_channel_mapping[ca][1] == 0) {
int hdmi_slot = 0;
/* fill actual channel mappings in ALSA channel (i) order */
for (i = 0; i < ch_alloc->channels; i++) {
while (!ch_alloc->speakers[7 - hdmi_slot] && !WARN_ON(hdmi_slot >= 8))
hdmi_slot++; /* skip zero slots */
hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++;
}
/* fill the rest of the slots with ALSA channel 0xf */
for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++)
if (!ch_alloc->speakers[7 - hdmi_slot])
hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot;
}
if (non_pcm) {
for (i = 0; i < ch_alloc->channels; i++)
non_pcm_mapping[i] = (i << 4) | i;
for (; i < 8; i++)
non_pcm_mapping[i] = (0xf << 4) | i;
}
for (i = 0; i < 8; i++) {
int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i];
int hdmi_slot = slotsetup & 0x0f;
int channel = (slotsetup & 0xf0) >> 4;
err = chmap->ops.pin_set_slot_channel(chmap->hdac,
pin_nid, hdmi_slot, channel);
if (err) {
dev_dbg(&chmap->hdac->dev, "HDMI: channel mapping failed\n");
break;
}
}
}
struct channel_map_table {
unsigned char map; /* ALSA API channel map position */
int spk_mask; /* speaker position bit mask */
};
static struct channel_map_table map_tables[] = {
{ SNDRV_CHMAP_FL, FL },
{ SNDRV_CHMAP_FR, FR },
{ SNDRV_CHMAP_RL, RL },
{ SNDRV_CHMAP_RR, RR },
{ SNDRV_CHMAP_LFE, LFE },
{ SNDRV_CHMAP_FC, FC },
{ SNDRV_CHMAP_RLC, RLC },
{ SNDRV_CHMAP_RRC, RRC },
{ SNDRV_CHMAP_RC, RC },
{ SNDRV_CHMAP_FLC, FLC },
{ SNDRV_CHMAP_FRC, FRC },
{ SNDRV_CHMAP_TFL, FLH },
{ SNDRV_CHMAP_TFR, FRH },
{ SNDRV_CHMAP_FLW, FLW },
{ SNDRV_CHMAP_FRW, FRW },
{ SNDRV_CHMAP_TC, TC },
{ SNDRV_CHMAP_TFC, FCH },
{} /* terminator */
};
/* from ALSA API channel position to speaker bit mask */
int snd_hdac_chmap_to_spk_mask(unsigned char c)
{
struct channel_map_table *t = map_tables;
for (; t->map; t++) {
if (t->map == c)
return t->spk_mask;
}
return 0;
}
EXPORT_SYMBOL_GPL(snd_hdac_chmap_to_spk_mask);
/* from ALSA API channel position to CEA slot */
static int to_cea_slot(int ordered_ca, unsigned char pos)
{
int mask = snd_hdac_chmap_to_spk_mask(pos);
int i;
if (mask) {
for (i = 0; i < 8; i++) {
if (channel_allocations[ordered_ca].speakers[7 - i] == mask)
return i;
}
}
return -1;
}
/* from speaker bit mask to ALSA API channel position */
int snd_hdac_spk_to_chmap(int spk)
{
struct channel_map_table *t = map_tables;
for (; t->map; t++) {
if (t->spk_mask == spk)
return t->map;
}
return 0;
}
EXPORT_SYMBOL_GPL(snd_hdac_spk_to_chmap);
/* from CEA slot to ALSA API channel position */
static int from_cea_slot(int ordered_ca, unsigned char slot)
{
int mask = channel_allocations[ordered_ca].speakers[7 - slot];
return snd_hdac_spk_to_chmap(mask);
}
/* get the CA index corresponding to the given ALSA API channel map */
static int hdmi_manual_channel_allocation(int chs, unsigned char *map)
{
int i, spks = 0, spk_mask = 0;
for (i = 0; i < chs; i++) {
int mask = snd_hdac_chmap_to_spk_mask(map[i]);
if (mask) {
spk_mask |= mask;
spks++;
}
}
for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
if ((chs == channel_allocations[i].channels ||
spks == channel_allocations[i].channels) &&
(spk_mask & channel_allocations[i].spk_mask) ==
channel_allocations[i].spk_mask)
return channel_allocations[i].ca_index;
}
return -1;
}
/* set up the channel slots for the given ALSA API channel map */
static int hdmi_manual_setup_channel_mapping(struct hdac_chmap *chmap,
hda_nid_t pin_nid,
int chs, unsigned char *map,
int ca)
{
int ordered_ca = get_channel_allocation_order(ca);
int alsa_pos, hdmi_slot;
int assignments[8] = {[0 ... 7] = 0xf};
for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) {
hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]);
if (hdmi_slot < 0)
continue; /* unassigned channel */
assignments[hdmi_slot] = alsa_pos;
}
for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) {
int err;
err = chmap->ops.pin_set_slot_channel(chmap->hdac,
pin_nid, hdmi_slot, assignments[hdmi_slot]);
if (err)
return -EINVAL;
}
return 0;
}
/* store ALSA API channel map from the current default map */
static void hdmi_setup_fake_chmap(unsigned char *map, int ca)
{
int i;
int ordered_ca = get_channel_allocation_order(ca);
for (i = 0; i < 8; i++) {
if (i < channel_allocations[ordered_ca].channels)
map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f);
else
map[i] = 0;
}
}
void snd_hdac_setup_channel_mapping(struct hdac_chmap *chmap,
hda_nid_t pin_nid, bool non_pcm, int ca,
int channels, unsigned char *map,
bool chmap_set)
{
if (!non_pcm && chmap_set) {
hdmi_manual_setup_channel_mapping(chmap, pin_nid,
channels, map, ca);
} else {
hdmi_std_setup_channel_mapping(chmap, pin_nid, non_pcm, ca);
hdmi_setup_fake_chmap(map, ca);
}
hdmi_debug_channel_mapping(chmap, pin_nid);
}
EXPORT_SYMBOL_GPL(snd_hdac_setup_channel_mapping);
int snd_hdac_get_active_channels(int ca)
{
int ordered_ca = get_channel_allocation_order(ca);
return channel_allocations[ordered_ca].channels;
}
EXPORT_SYMBOL_GPL(snd_hdac_get_active_channels);
struct hdac_cea_channel_speaker_allocation *snd_hdac_get_ch_alloc_from_ca(int ca)
{
return &channel_allocations[get_channel_allocation_order(ca)];
}
EXPORT_SYMBOL_GPL(snd_hdac_get_ch_alloc_from_ca);
int snd_hdac_channel_allocation(struct hdac_device *hdac, int spk_alloc,
int channels, bool chmap_set, bool non_pcm, unsigned char *map)
{
int ca;
if (!non_pcm && chmap_set)
ca = hdmi_manual_channel_allocation(channels, map);
else
ca = hdmi_channel_allocation_spk_alloc_blk(hdac,
spk_alloc, channels);
if (ca < 0)
ca = 0;
return ca;
}
EXPORT_SYMBOL_GPL(snd_hdac_channel_allocation);
/*
* ALSA API channel-map control callbacks
*/
static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
struct hdac_chmap *chmap = info->private_data;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = chmap->channels_max;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = SNDRV_CHMAP_LAST;
return 0;
}
static int hdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap,
struct hdac_cea_channel_speaker_allocation *cap, int channels)
{
/* If the speaker allocation matches the channel count, it is OK.*/
if (cap->channels != channels)
return -1;
/* all channels are remappable freely */
return SNDRV_CTL_TLVT_CHMAP_VAR;
}
static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap,
struct hdac_cea_channel_speaker_allocation *cap,
unsigned int *chmap, int channels)
{
int count = 0;
int c;
for (c = 7; c >= 0; c--) {
int spk = cap->speakers[c];
if (!spk)
continue;
chmap[count++] = snd_hdac_spk_to_chmap(spk);
}
WARN_ON(count != channels);
}
static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
unsigned int size, unsigned int __user *tlv)
{
struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
struct hdac_chmap *chmap = info->private_data;
unsigned int __user *dst;
int chs, count = 0;
if (size < 8)
return -ENOMEM;
if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
return -EFAULT;
size -= 8;
dst = tlv + 2;
for (chs = 2; chs <= chmap->channels_max; chs++) {
int i;
struct hdac_cea_channel_speaker_allocation *cap;
cap = channel_allocations;
for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) {
int chs_bytes = chs * 4;
int type = chmap->ops.chmap_cea_alloc_validate_get_type(
chmap, cap, chs);
unsigned int tlv_chmap[8];
if (type < 0)
continue;
if (size < 8)
return -ENOMEM;
if (put_user(type, dst) ||
put_user(chs_bytes, dst + 1))
return -EFAULT;
dst += 2;
size -= 8;
count += 8;
if (size < chs_bytes)
return -ENOMEM;
size -= chs_bytes;
count += chs_bytes;
chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap,
tlv_chmap, chs);
if (copy_to_user(dst, tlv_chmap, chs_bytes))
return -EFAULT;
dst += chs;
}
}
if (put_user(count, tlv + 1))
return -EFAULT;
return 0;
}
static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
struct hdac_chmap *chmap = info->private_data;
int pcm_idx = kcontrol->private_value;
unsigned char pcm_chmap[8];
int i;
memset(pcm_chmap, 0, sizeof(pcm_chmap));
chmap->ops.get_chmap(chmap->hdac, pcm_idx, pcm_chmap);
for (i = 0; i < sizeof(chmap); i++)
ucontrol->value.integer.value[i] = pcm_chmap[i];
return 0;
}
static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
struct hdac_chmap *hchmap = info->private_data;
int pcm_idx = kcontrol->private_value;
unsigned int ctl_idx;
struct snd_pcm_substream *substream;
unsigned char chmap[8], per_pin_chmap[8];
int i, err, ca, prepared = 0;
/* No monitor is connected in dyn_pcm_assign.
* It's invalid to setup the chmap
*/
if (!hchmap->ops.is_pcm_attached(hchmap->hdac, pcm_idx))
return 0;
ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
substream = snd_pcm_chmap_substream(info, ctl_idx);
if (!substream || !substream->runtime)
return 0; /* just for avoiding error from alsactl restore */
switch (substream->runtime->status->state) {
case SNDRV_PCM_STATE_OPEN:
case SNDRV_PCM_STATE_SETUP:
break;
case SNDRV_PCM_STATE_PREPARED:
prepared = 1;
break;
default:
return -EBUSY;
}
memset(chmap, 0, sizeof(chmap));
for (i = 0; i < ARRAY_SIZE(chmap); i++)
chmap[i] = ucontrol->value.integer.value[i];
hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, per_pin_chmap);
if (!memcmp(chmap, per_pin_chmap, sizeof(chmap)))
return 0;
ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap);
if (ca < 0)
return -EINVAL;
if (hchmap->ops.chmap_validate) {
err = hchmap->ops.chmap_validate(hchmap, ca,
ARRAY_SIZE(chmap), chmap);
if (err)
return err;
}
hchmap->ops.set_chmap(hchmap->hdac, pcm_idx, chmap, prepared);
return 0;
}
static const struct hdac_chmap_ops chmap_ops = {
.chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type,
.cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap,
.pin_get_slot_channel = hdmi_pin_get_slot_channel,
.pin_set_slot_channel = hdmi_pin_set_slot_channel,
.set_channel_count = hdmi_set_channel_count,
};
void snd_hdac_register_chmap_ops(struct hdac_device *hdac,
struct hdac_chmap *chmap)
{
chmap->ops = chmap_ops;
chmap->hdac = hdac;
init_channel_allocations();
}
EXPORT_SYMBOL_GPL(snd_hdac_register_chmap_ops);
int snd_hdac_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx,
struct hdac_chmap *hchmap)
{
struct snd_pcm_chmap *chmap;
struct snd_kcontrol *kctl;
int err, i;
err = snd_pcm_add_chmap_ctls(pcm,
SNDRV_PCM_STREAM_PLAYBACK,
NULL, 0, pcm_idx, &chmap);
if (err < 0)
return err;
/* override handlers */
chmap->private_data = hchmap;
kctl = chmap->kctl;
for (i = 0; i < kctl->count; i++)
kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
kctl->info = hdmi_chmap_ctl_info;
kctl->get = hdmi_chmap_ctl_get;
kctl->put = hdmi_chmap_ctl_put;
kctl->tlv.c = hdmi_chmap_ctl_tlv;
return 0;
}
EXPORT_SYMBOL_GPL(snd_hdac_add_chmap_ctls);

View File

@ -23,17 +23,5 @@ config SND_SGI_HAL2
help help
Sound support for the SGI Indy and Indigo2 Workstation. Sound support for the SGI Indy and Indigo2 Workstation.
config SND_AU1X00
tristate "Au1x00 AC97 Port Driver (DEPRECATED)"
depends on MIPS_ALCHEMY
select SND_PCM
select SND_AC97_CODEC
help
ALSA Sound driver for the Au1x00's AC97 port.
Newer drivers for ASoC are available, please do not use
this driver as it will be removed in the future.
endif # SND_MIPS endif # SND_MIPS

View File

@ -2,11 +2,9 @@
# Makefile for ALSA # Makefile for ALSA
# #
snd-au1x00-objs := au1x00.o
snd-sgi-o2-objs := sgio2audio.o ad1843.o snd-sgi-o2-objs := sgio2audio.o ad1843.o
snd-sgi-hal2-objs := hal2.o snd-sgi-hal2-objs := hal2.o
# Toplevel Module Dependency # Toplevel Module Dependency
obj-$(CONFIG_SND_AU1X00) += snd-au1x00.o
obj-$(CONFIG_SND_SGI_O2) += snd-sgi-o2.o obj-$(CONFIG_SND_SGI_O2) += snd-sgi-o2.o
obj-$(CONFIG_SND_SGI_HAL2) += snd-sgi-hal2.o obj-$(CONFIG_SND_SGI_HAL2) += snd-sgi-hal2.o

View File

@ -1,734 +0,0 @@
/*
* BRIEF MODULE DESCRIPTION
* Driver for AMD Au1000 MIPS Processor, AC'97 Sound Port
*
* Copyright 2004 Cooper Street Innovations Inc.
* Author: Charles Eidsness <charles@cooper-street.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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* 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.
*
* History:
*
* 2004-09-09 Charles Eidsness -- Original verion -- based on
* sa11xx-uda1341.c ALSA driver and the
* au1000.c OSS driver.
* 2004-09-09 Matt Porter -- Added support for ALSA 1.0.6
*
*/
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/ac97_codec.h>
#include <asm/mach-au1x00/au1000.h>
#include <asm/mach-au1x00/au1000_dma.h>
MODULE_AUTHOR("Charles Eidsness <charles@cooper-street.com>");
MODULE_DESCRIPTION("Au1000 AC'97 ALSA Driver");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{AMD,Au1000 AC'97}}");
#define PLAYBACK 0
#define CAPTURE 1
#define AC97_SLOT_3 0x01
#define AC97_SLOT_4 0x02
#define AC97_SLOT_6 0x08
#define AC97_CMD_IRQ 31
#define READ 0
#define WRITE 1
#define READ_WAIT 2
#define RW_DONE 3
struct au1000_period
{
u32 start;
u32 relative_end; /*realtive to start of buffer*/
struct au1000_period * next;
};
/*Au1000 AC97 Port Control Reisters*/
struct au1000_ac97_reg {
u32 volatile config;
u32 volatile status;
u32 volatile data;
u32 volatile cmd;
u32 volatile cntrl;
};
struct audio_stream {
struct snd_pcm_substream *substream;
int dma;
spinlock_t dma_lock;
struct au1000_period * buffer;
unsigned int period_size;
unsigned int periods;
};
struct snd_au1000 {
struct snd_card *card;
struct au1000_ac97_reg volatile *ac97_ioport;
struct resource *ac97_res_port;
spinlock_t ac97_lock;
struct snd_ac97 *ac97;
struct snd_pcm *pcm;
struct audio_stream *stream[2]; /* playback & capture */
int dmaid[2]; /* tx(0)/rx(1) DMA ids */
};
/*--------------------------- Local Functions --------------------------------*/
static void
au1000_set_ac97_xmit_slots(struct snd_au1000 *au1000, long xmit_slots)
{
u32 volatile ac97_config;
spin_lock(&au1000->ac97_lock);
ac97_config = au1000->ac97_ioport->config;
ac97_config = ac97_config & ~AC97C_XMIT_SLOTS_MASK;
ac97_config |= (xmit_slots << AC97C_XMIT_SLOTS_BIT);
au1000->ac97_ioport->config = ac97_config;
spin_unlock(&au1000->ac97_lock);
}
static void
au1000_set_ac97_recv_slots(struct snd_au1000 *au1000, long recv_slots)
{
u32 volatile ac97_config;
spin_lock(&au1000->ac97_lock);
ac97_config = au1000->ac97_ioport->config;
ac97_config = ac97_config & ~AC97C_RECV_SLOTS_MASK;
ac97_config |= (recv_slots << AC97C_RECV_SLOTS_BIT);
au1000->ac97_ioport->config = ac97_config;
spin_unlock(&au1000->ac97_lock);
}
static void
au1000_release_dma_link(struct audio_stream *stream)
{
struct au1000_period * pointer;
struct au1000_period * pointer_next;
stream->period_size = 0;
stream->periods = 0;
pointer = stream->buffer;
if (! pointer)
return;
do {
pointer_next = pointer->next;
kfree(pointer);
pointer = pointer_next;
} while (pointer != stream->buffer);
stream->buffer = NULL;
}
static int
au1000_setup_dma_link(struct audio_stream *stream, unsigned int period_bytes,
unsigned int periods)
{
struct snd_pcm_substream *substream = stream->substream;
struct snd_pcm_runtime *runtime = substream->runtime;
struct au1000_period *pointer;
unsigned long dma_start;
int i;
dma_start = virt_to_phys(runtime->dma_area);
if (stream->period_size == period_bytes &&
stream->periods == periods)
return 0; /* not changed */
au1000_release_dma_link(stream);
stream->period_size = period_bytes;
stream->periods = periods;
stream->buffer = kmalloc(sizeof(struct au1000_period), GFP_KERNEL);
if (! stream->buffer)
return -ENOMEM;
pointer = stream->buffer;
for (i = 0; i < periods; i++) {
pointer->start = (u32)(dma_start + (i * period_bytes));
pointer->relative_end = (u32) (((i+1) * period_bytes) - 0x1);
if (i < periods - 1) {
pointer->next = kmalloc(sizeof(struct au1000_period), GFP_KERNEL);
if (! pointer->next) {
au1000_release_dma_link(stream);
return -ENOMEM;
}
pointer = pointer->next;
}
}
pointer->next = stream->buffer;
return 0;
}
static void
au1000_dma_stop(struct audio_stream *stream)
{
if (snd_BUG_ON(!stream->buffer))
return;
disable_dma(stream->dma);
}
static void
au1000_dma_start(struct audio_stream *stream)
{
if (snd_BUG_ON(!stream->buffer))
return;
init_dma(stream->dma);
if (get_dma_active_buffer(stream->dma) == 0) {
clear_dma_done0(stream->dma);
set_dma_addr0(stream->dma, stream->buffer->start);
set_dma_count0(stream->dma, stream->period_size >> 1);
set_dma_addr1(stream->dma, stream->buffer->next->start);
set_dma_count1(stream->dma, stream->period_size >> 1);
} else {
clear_dma_done1(stream->dma);
set_dma_addr1(stream->dma, stream->buffer->start);
set_dma_count1(stream->dma, stream->period_size >> 1);
set_dma_addr0(stream->dma, stream->buffer->next->start);
set_dma_count0(stream->dma, stream->period_size >> 1);
}
enable_dma_buffers(stream->dma);
start_dma(stream->dma);
}
static irqreturn_t
au1000_dma_interrupt(int irq, void *dev_id)
{
struct audio_stream *stream = (struct audio_stream *) dev_id;
struct snd_pcm_substream *substream = stream->substream;
spin_lock(&stream->dma_lock);
switch (get_dma_buffer_done(stream->dma)) {
case DMA_D0:
stream->buffer = stream->buffer->next;
clear_dma_done0(stream->dma);
set_dma_addr0(stream->dma, stream->buffer->next->start);
set_dma_count0(stream->dma, stream->period_size >> 1);
enable_dma_buffer0(stream->dma);
break;
case DMA_D1:
stream->buffer = stream->buffer->next;
clear_dma_done1(stream->dma);
set_dma_addr1(stream->dma, stream->buffer->next->start);
set_dma_count1(stream->dma, stream->period_size >> 1);
enable_dma_buffer1(stream->dma);
break;
case (DMA_D0 | DMA_D1):
printk(KERN_ERR "DMA %d missed interrupt.\n",stream->dma);
au1000_dma_stop(stream);
au1000_dma_start(stream);
break;
case (~DMA_D0 & ~DMA_D1):
printk(KERN_ERR "DMA %d empty irq.\n",stream->dma);
}
spin_unlock(&stream->dma_lock);
snd_pcm_period_elapsed(substream);
return IRQ_HANDLED;
}
/*-------------------------- PCM Audio Streams -------------------------------*/
static unsigned int rates[] = {8000, 11025, 16000, 22050};
static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
.count = ARRAY_SIZE(rates),
.list = rates,
.mask = 0,
};
static struct snd_pcm_hardware snd_au1000_hw =
{
.info = (SNDRV_PCM_INFO_INTERLEAVED | \
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050),
.rate_min = 8000,
.rate_max = 22050,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = 128*1024,
.period_bytes_min = 32,
.period_bytes_max = 16*1024,
.periods_min = 8,
.periods_max = 255,
.fifo_size = 16,
};
static int
snd_au1000_playback_open(struct snd_pcm_substream *substream)
{
struct snd_au1000 *au1000 = substream->pcm->private_data;
au1000->stream[PLAYBACK]->substream = substream;
au1000->stream[PLAYBACK]->buffer = NULL;
substream->private_data = au1000->stream[PLAYBACK];
substream->runtime->hw = snd_au1000_hw;
return (snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates) < 0);
}
static int
snd_au1000_capture_open(struct snd_pcm_substream *substream)
{
struct snd_au1000 *au1000 = substream->pcm->private_data;
au1000->stream[CAPTURE]->substream = substream;
au1000->stream[CAPTURE]->buffer = NULL;
substream->private_data = au1000->stream[CAPTURE];
substream->runtime->hw = snd_au1000_hw;
return (snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates) < 0);
}
static int
snd_au1000_playback_close(struct snd_pcm_substream *substream)
{
struct snd_au1000 *au1000 = substream->pcm->private_data;
au1000->stream[PLAYBACK]->substream = NULL;
return 0;
}
static int
snd_au1000_capture_close(struct snd_pcm_substream *substream)
{
struct snd_au1000 *au1000 = substream->pcm->private_data;
au1000->stream[CAPTURE]->substream = NULL;
return 0;
}
static int
snd_au1000_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct audio_stream *stream = substream->private_data;
int err;
err = snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
if (err < 0)
return err;
return au1000_setup_dma_link(stream,
params_period_bytes(hw_params),
params_periods(hw_params));
}
static int
snd_au1000_hw_free(struct snd_pcm_substream *substream)
{
struct audio_stream *stream = substream->private_data;
au1000_release_dma_link(stream);
return snd_pcm_lib_free_pages(substream);
}
static int
snd_au1000_playback_prepare(struct snd_pcm_substream *substream)
{
struct snd_au1000 *au1000 = substream->pcm->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
if (runtime->channels == 1)
au1000_set_ac97_xmit_slots(au1000, AC97_SLOT_4);
else
au1000_set_ac97_xmit_slots(au1000, AC97_SLOT_3 | AC97_SLOT_4);
snd_ac97_set_rate(au1000->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
return 0;
}
static int
snd_au1000_capture_prepare(struct snd_pcm_substream *substream)
{
struct snd_au1000 *au1000 = substream->pcm->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
if (runtime->channels == 1)
au1000_set_ac97_recv_slots(au1000, AC97_SLOT_4);
else
au1000_set_ac97_recv_slots(au1000, AC97_SLOT_3 | AC97_SLOT_4);
snd_ac97_set_rate(au1000->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
return 0;
}
static int
snd_au1000_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct audio_stream *stream = substream->private_data;
int err = 0;
spin_lock(&stream->dma_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
au1000_dma_start(stream);
break;
case SNDRV_PCM_TRIGGER_STOP:
au1000_dma_stop(stream);
break;
default:
err = -EINVAL;
break;
}
spin_unlock(&stream->dma_lock);
return err;
}
static snd_pcm_uframes_t
snd_au1000_pointer(struct snd_pcm_substream *substream)
{
struct audio_stream *stream = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
long location;
spin_lock(&stream->dma_lock);
location = get_dma_residue(stream->dma);
spin_unlock(&stream->dma_lock);
location = stream->buffer->relative_end - location;
if (location == -1)
location = 0;
return bytes_to_frames(runtime,location);
}
static struct snd_pcm_ops snd_card_au1000_playback_ops = {
.open = snd_au1000_playback_open,
.close = snd_au1000_playback_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_au1000_hw_params,
.hw_free = snd_au1000_hw_free,
.prepare = snd_au1000_playback_prepare,
.trigger = snd_au1000_trigger,
.pointer = snd_au1000_pointer,
};
static struct snd_pcm_ops snd_card_au1000_capture_ops = {
.open = snd_au1000_capture_open,
.close = snd_au1000_capture_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_au1000_hw_params,
.hw_free = snd_au1000_hw_free,
.prepare = snd_au1000_capture_prepare,
.trigger = snd_au1000_trigger,
.pointer = snd_au1000_pointer,
};
static int
snd_au1000_pcm_new(struct snd_au1000 *au1000)
{
struct snd_pcm *pcm;
int err;
unsigned long flags;
if ((err = snd_pcm_new(au1000->card, "AU1000 AC97 PCM", 0, 1, 1, &pcm)) < 0)
return err;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
snd_dma_continuous_data(GFP_KERNEL), 128*1024, 128*1024);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_card_au1000_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_card_au1000_capture_ops);
pcm->private_data = au1000;
pcm->info_flags = 0;
strcpy(pcm->name, "Au1000 AC97 PCM");
spin_lock_init(&au1000->stream[PLAYBACK]->dma_lock);
spin_lock_init(&au1000->stream[CAPTURE]->dma_lock);
flags = claim_dma_lock();
au1000->stream[PLAYBACK]->dma = request_au1000_dma(au1000->dmaid[0],
"AC97 TX", au1000_dma_interrupt, 0,
au1000->stream[PLAYBACK]);
if (au1000->stream[PLAYBACK]->dma < 0) {
release_dma_lock(flags);
return -EBUSY;
}
au1000->stream[CAPTURE]->dma = request_au1000_dma(au1000->dmaid[1],
"AC97 RX", au1000_dma_interrupt, 0,
au1000->stream[CAPTURE]);
if (au1000->stream[CAPTURE]->dma < 0){
release_dma_lock(flags);
return -EBUSY;
}
/* enable DMA coherency in read/write DMA channels */
set_dma_mode(au1000->stream[PLAYBACK]->dma,
get_dma_mode(au1000->stream[PLAYBACK]->dma) & ~DMA_NC);
set_dma_mode(au1000->stream[CAPTURE]->dma,
get_dma_mode(au1000->stream[CAPTURE]->dma) & ~DMA_NC);
release_dma_lock(flags);
au1000->pcm = pcm;
return 0;
}
/*-------------------------- AC97 CODEC Control ------------------------------*/
static unsigned short
snd_au1000_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
{
struct snd_au1000 *au1000 = ac97->private_data;
u32 volatile cmd;
u16 volatile data;
int i;
spin_lock(&au1000->ac97_lock);
/* would rather use the interrupt than this polling but it works and I can't
get the interrupt driven case to work efficiently */
for (i = 0; i < 0x5000; i++)
if (!(au1000->ac97_ioport->status & AC97C_CP))
break;
if (i == 0x5000)
printk(KERN_ERR "au1000 AC97: AC97 command read timeout\n");
cmd = (u32) reg & AC97C_INDEX_MASK;
cmd |= AC97C_READ;
au1000->ac97_ioport->cmd = cmd;
/* now wait for the data */
for (i = 0; i < 0x5000; i++)
if (!(au1000->ac97_ioport->status & AC97C_CP))
break;
if (i == 0x5000) {
printk(KERN_ERR "au1000 AC97: AC97 command read timeout\n");
spin_unlock(&au1000->ac97_lock);
return 0;
}
data = au1000->ac97_ioport->cmd & 0xffff;
spin_unlock(&au1000->ac97_lock);
return data;
}
static void
snd_au1000_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
{
struct snd_au1000 *au1000 = ac97->private_data;
u32 cmd;
int i;
spin_lock(&au1000->ac97_lock);
/* would rather use the interrupt than this polling but it works and I can't
get the interrupt driven case to work efficiently */
for (i = 0; i < 0x5000; i++)
if (!(au1000->ac97_ioport->status & AC97C_CP))
break;
if (i == 0x5000)
printk(KERN_ERR "au1000 AC97: AC97 command write timeout\n");
cmd = (u32) reg & AC97C_INDEX_MASK;
cmd &= ~AC97C_READ;
cmd |= ((u32) val << AC97C_WD_BIT);
au1000->ac97_ioport->cmd = cmd;
spin_unlock(&au1000->ac97_lock);
}
/*------------------------------ Setup / Destroy ----------------------------*/
static void snd_au1000_free(struct snd_card *card)
{
struct snd_au1000 *au1000 = card->private_data;
if (au1000->stream[PLAYBACK]) {
if (au1000->stream[PLAYBACK]->dma >= 0)
free_au1000_dma(au1000->stream[PLAYBACK]->dma);
kfree(au1000->stream[PLAYBACK]);
}
if (au1000->stream[CAPTURE]) {
if (au1000->stream[CAPTURE]->dma >= 0)
free_au1000_dma(au1000->stream[CAPTURE]->dma);
kfree(au1000->stream[CAPTURE]);
}
if (au1000->ac97_res_port) {
/* put internal AC97 block into reset */
if (au1000->ac97_ioport) {
au1000->ac97_ioport->cntrl = AC97C_RS;
iounmap(au1000->ac97_ioport);
au1000->ac97_ioport = NULL;
}
release_and_free_resource(au1000->ac97_res_port);
au1000->ac97_res_port = NULL;
}
}
static struct snd_ac97_bus_ops ops = {
.write = snd_au1000_ac97_write,
.read = snd_au1000_ac97_read,
};
static int au1000_ac97_probe(struct platform_device *pdev)
{
int err;
void __iomem *io;
struct resource *r;
struct snd_card *card;
struct snd_au1000 *au1000;
struct snd_ac97_bus *pbus;
struct snd_ac97_template ac97;
err = snd_card_new(&pdev->dev, -1, "AC97", THIS_MODULE,
sizeof(struct snd_au1000), &card);
if (err < 0)
return err;
au1000 = card->private_data;
au1000->card = card;
spin_lock_init(&au1000->ac97_lock);
/* from here on let ALSA call the special freeing function */
card->private_free = snd_au1000_free;
/* TX DMA ID */
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!r) {
err = -ENODEV;
snd_printk(KERN_INFO "no TX DMA platform resource!\n");
goto out;
}
au1000->dmaid[0] = r->start;
/* RX DMA ID */
r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (!r) {
err = -ENODEV;
snd_printk(KERN_INFO "no RX DMA platform resource!\n");
goto out;
}
au1000->dmaid[1] = r->start;
au1000->stream[PLAYBACK] = kmalloc(sizeof(struct audio_stream),
GFP_KERNEL);
if (!au1000->stream[PLAYBACK]) {
err = -ENOMEM;
goto out;
}
au1000->stream[PLAYBACK]->dma = -1;
au1000->stream[CAPTURE] = kmalloc(sizeof(struct audio_stream),
GFP_KERNEL);
if (!au1000->stream[CAPTURE]) {
err = -ENOMEM;
goto out;
}
au1000->stream[CAPTURE]->dma = -1;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
err = -ENODEV;
goto out;
}
err = -EBUSY;
au1000->ac97_res_port = request_mem_region(r->start, resource_size(r),
pdev->name);
if (!au1000->ac97_res_port) {
snd_printk(KERN_ERR "ALSA AC97: can't grab AC97 port\n");
goto out;
}
io = ioremap(r->start, resource_size(r));
if (!io)
goto out;
au1000->ac97_ioport = (struct au1000_ac97_reg *)io;
/* configure pins for AC'97
TODO: move to board_setup.c */
au_writel(au_readl(SYS_PINFUNC) & ~0x02, SYS_PINFUNC);
/* Initialise Au1000's AC'97 Control Block */
au1000->ac97_ioport->cntrl = AC97C_RS | AC97C_CE;
udelay(10);
au1000->ac97_ioport->cntrl = AC97C_CE;
udelay(10);
/* Initialise External CODEC -- cold reset */
au1000->ac97_ioport->config = AC97C_RESET;
udelay(10);
au1000->ac97_ioport->config = 0x0;
mdelay(5);
/* Initialise AC97 middle-layer */
err = snd_ac97_bus(au1000->card, 0, &ops, au1000, &pbus);
if (err < 0)
goto out;
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = au1000;
err = snd_ac97_mixer(pbus, &ac97, &au1000->ac97);
if (err < 0)
goto out;
err = snd_au1000_pcm_new(au1000);
if (err < 0)
goto out;
strcpy(card->driver, "Au1000-AC97");
strcpy(card->shortname, "AMD Au1000-AC97");
sprintf(card->longname, "AMD Au1000--AC97 ALSA Driver");
err = snd_card_register(card);
if (err < 0)
goto out;
printk(KERN_INFO "ALSA AC97: Driver Initialized\n");
platform_set_drvdata(pdev, card);
return 0;
out:
snd_card_free(card);
return err;
}
static int au1000_ac97_remove(struct platform_device *pdev)
{
return snd_card_free(platform_get_drvdata(pdev));
}
struct platform_driver au1000_ac97c_driver = {
.driver = {
.name = "au1000-ac97c",
.owner = THIS_MODULE,
},
.probe = au1000_ac97_probe,
.remove = au1000_ac97_remove,
};
module_platform_driver(au1000_ac97c_driver);

View File

@ -866,7 +866,7 @@ config SND_VIRTUOSO
select SND_OXYGEN_LIB select SND_OXYGEN_LIB
select SND_PCM select SND_PCM
select SND_MPU401_UART select SND_MPU401_UART
select SND_JACK if INPUT=y || INPUT=SND select SND_JACK
help help
Say Y here to include support for sound cards based on the Say Y here to include support for sound cards based on the
Asus AV66/AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, DS, DSX, Asus AV66/AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, DS, DSX,

View File

@ -4,7 +4,7 @@ config SND_HDA
tristate tristate
select SND_PCM select SND_PCM
select SND_VMASTER select SND_VMASTER
select SND_JACK if INPUT=y || INPUT=SND select SND_JACK
select SND_HDA_CORE select SND_HDA_CORE
config SND_HDA_INTEL config SND_HDA_INTEL

View File

@ -26,6 +26,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <sound/core.h> #include <sound/core.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include <sound/hda_chmap.h>
#include "hda_codec.h" #include "hda_codec.h"
#include "hda_local.h" #include "hda_local.h"
@ -42,20 +43,6 @@ enum cea_edid_versions {
CEA_EDID_VER_RESERVED = 4, CEA_EDID_VER_RESERVED = 4,
}; };
static const char * const cea_speaker_allocation_names[] = {
/* 0 */ "FL/FR",
/* 1 */ "LFE",
/* 2 */ "FC",
/* 3 */ "RL/RR",
/* 4 */ "RC",
/* 5 */ "FLC/FRC",
/* 6 */ "RLC/RRC",
/* 7 */ "FLW/FRW",
/* 8 */ "FLH/FRH",
/* 9 */ "TC",
/* 10 */ "FCH",
};
static const char * const eld_connection_type_names[4] = { static const char * const eld_connection_type_names[4] = {
"HDMI", "HDMI",
"DisplayPort", "DisplayPort",
@ -419,18 +406,6 @@ static void hdmi_show_short_audio_desc(struct hda_codec *codec,
a->channels, buf, buf2); a->channels, buf, buf2);
} }
void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen)
{
int i, j;
for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) {
if (spk_alloc & (1 << i))
j += snprintf(buf + j, buflen - j, " %s",
cea_speaker_allocation_names[i]);
}
buf[j] = '\0'; /* necessary when j == 0 */
}
void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e) void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e)
{ {
int i; int i;
@ -441,7 +416,7 @@ void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e)
if (e->spk_alloc) { if (e->spk_alloc) {
char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); snd_hdac_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
codec_dbg(codec, "HDMI: available speakers:%s\n", buf); codec_dbg(codec, "HDMI: available speakers:%s\n", buf);
} }
@ -516,7 +491,7 @@ void snd_hdmi_print_eld_info(struct hdmi_eld *eld,
snd_iprintf(buffer, "support_ai\t\t%d\n", e->support_ai); snd_iprintf(buffer, "support_ai\t\t%d\n", e->support_ai);
snd_iprintf(buffer, "audio_sync_delay\t%d\n", e->aud_synch_delay); snd_iprintf(buffer, "audio_sync_delay\t%d\n", e->aud_synch_delay);
snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf)); snd_hdac_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
snd_iprintf(buffer, "speakers\t\t[0x%x]%s\n", e->spk_alloc, buf); snd_iprintf(buffer, "speakers\t\t[0x%x]%s\n", e->spk_alloc, buf);
snd_iprintf(buffer, "sad_count\t\t%d\n", e->sad_count); snd_iprintf(buffer, "sad_count\t\t%d\n", e->sad_count);

View File

@ -2145,7 +2145,7 @@ static int azx_probe_continue(struct azx *chip)
azx_add_card_list(chip); azx_add_card_list(chip);
snd_hda_set_power_save(&chip->bus, power_save * 1000); snd_hda_set_power_save(&chip->bus, power_save * 1000);
if (azx_has_pm_runtime(chip) || hda->use_vga_switcheroo) if (azx_has_pm_runtime(chip) || hda->use_vga_switcheroo)
pm_runtime_put_noidle(&pci->dev); pm_runtime_put_autosuspend(&pci->dev);
out_free: out_free:
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL

View File

@ -174,8 +174,12 @@ static void cs_automute(struct hda_codec *codec)
snd_hda_gen_update_outputs(codec); snd_hda_gen_update_outputs(codec);
if (spec->gpio_eapd_hp || spec->gpio_eapd_speaker) { if (spec->gpio_eapd_hp || spec->gpio_eapd_speaker) {
spec->gpio_data = spec->gen.hp_jack_present ? if (spec->gen.automute_speaker)
spec->gpio_eapd_hp : spec->gpio_eapd_speaker; spec->gpio_data = spec->gen.hp_jack_present ?
spec->gpio_eapd_hp : spec->gpio_eapd_speaker;
else
spec->gpio_data =
spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
snd_hda_codec_write(codec, 0x01, 0, snd_hda_codec_write(codec, 0x01, 0,
AC_VERB_SET_GPIO_DATA, spec->gpio_data); AC_VERB_SET_GPIO_DATA, spec->gpio_data);
} }

View File

@ -204,8 +204,13 @@ static void cx_auto_reboot_notify(struct hda_codec *codec)
{ {
struct conexant_spec *spec = codec->spec; struct conexant_spec *spec = codec->spec;
if (codec->core.vendor_id != 0x14f150f2) switch (codec->core.vendor_id) {
case 0x14f150f2: /* CX20722 */
case 0x14f150f4: /* CX20724 */
break;
default:
return; return;
}
/* Turn the CX20722 codec into D3 to avoid spurious noises /* Turn the CX20722 codec into D3 to avoid spurious noises
from the internal speaker during (and after) reboot */ from the internal speaker during (and after) reboot */

File diff suppressed because it is too large Load Diff

View File

@ -5556,6 +5556,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2233, "Thinkpad", ALC293_FIXUP_LENOVO_SPK_NOISE), SND_PCI_QUIRK(0x17aa, 0x2233, "Thinkpad", ALC293_FIXUP_LENOVO_SPK_NOISE),
SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI), SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC),
SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP), SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP),

View File

@ -10,23 +10,10 @@
static int (*led_set_func)(int, bool); static int (*led_set_func)(int, bool);
static void (*old_vmaster_hook)(void *, int); static void (*old_vmaster_hook)(void *, int);
static acpi_status acpi_check_cb(acpi_handle handle, u32 lvl, void *context,
void **rv)
{
bool *found = context;
*found = true;
return AE_OK;
}
static bool is_thinkpad(struct hda_codec *codec) static bool is_thinkpad(struct hda_codec *codec)
{ {
bool found = false; return (codec->core.subsystem_id >> 16 == 0x17aa) &&
if (codec->core.subsystem_id >> 16 != 0x17aa) (acpi_dev_present("LEN0068") || acpi_dev_present("IBM0068"));
return false;
if (ACPI_SUCCESS(acpi_get_devices("LEN0068", acpi_check_cb, &found, NULL)) && found)
return true;
found = false;
return ACPI_SUCCESS(acpi_get_devices("IBM0068", acpi_check_cb, &found, NULL)) && found;
} }
static void update_tpacpi_mute_led(void *private_data, int enabled) static void update_tpacpi_mute_led(void *private_data, int enabled)

View File

@ -2879,6 +2879,7 @@ static void intel8x0_measure_ac97_clock(struct intel8x0 *chip)
static struct snd_pci_quirk intel8x0_clock_list[] = { static struct snd_pci_quirk intel8x0_clock_list[] = {
SND_PCI_QUIRK(0x0e11, 0x008a, "AD1885", 41000), SND_PCI_QUIRK(0x0e11, 0x008a, "AD1885", 41000),
SND_PCI_QUIRK(0x1014, 0x0581, "AD1981B", 48000),
SND_PCI_QUIRK(0x1028, 0x00be, "AD1885", 44100), SND_PCI_QUIRK(0x1028, 0x00be, "AD1885", 44100),
SND_PCI_QUIRK(0x1028, 0x0177, "AD1980", 48000), SND_PCI_QUIRK(0x1028, 0x0177, "AD1980", 48000),
SND_PCI_QUIRK(0x1028, 0x01ad, "AD1981B", 48000), SND_PCI_QUIRK(0x1028, 0x01ad, "AD1981B", 48000),

View File

@ -132,7 +132,7 @@ static int mixart_set_pipe_state(struct mixart_mgr *mgr,
} }
if(start) { if(start) {
u32 stat; u32 stat = 0;
group_state.pipe_count = 0; /* in case of start same command once again with pipe_count=0 */ group_state.pipe_count = 0; /* in case of start same command once again with pipe_count=0 */

View File

@ -726,7 +726,7 @@ int mixart_update_playback_stream_level(struct snd_mixart* chip, int is_aes, int
int volume[2]; int volume[2];
struct mixart_msg request; struct mixart_msg request;
struct mixart_set_out_stream_level_req set_level; struct mixart_set_out_stream_level_req set_level;
u32 status; u32 status = 0;
struct mixart_pipe *pipe; struct mixart_pipe *pipe;
memset(&set_level, 0, sizeof(set_level)); memset(&set_level, 0, sizeof(set_level));
@ -778,7 +778,7 @@ int mixart_update_capture_stream_level(struct snd_mixart* chip, int is_aes)
struct mixart_pipe *pipe; struct mixart_pipe *pipe;
struct mixart_msg request; struct mixart_msg request;
struct mixart_set_in_audio_level_req set_level; struct mixart_set_in_audio_level_req set_level;
u32 status; u32 status = 0;
if(is_aes) { if(is_aes) {
idx = 1; idx = 1;

View File

@ -6,7 +6,7 @@ menuconfig SND_SOC
tristate "ALSA for SoC audio support" tristate "ALSA for SoC audio support"
select SND_PCM select SND_PCM
select AC97_BUS if SND_SOC_AC97_BUS select AC97_BUS if SND_SOC_AC97_BUS
select SND_JACK if INPUT=y || INPUT=SND select SND_JACK
select REGMAP_I2C if I2C select REGMAP_I2C if I2C
select REGMAP_SPI if SPI_MASTER select REGMAP_SPI if SPI_MASTER
---help--- ---help---

View File

@ -285,7 +285,8 @@ static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params,
static int atmel_ssc_startup(struct snd_pcm_substream *substream, static int atmel_ssc_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; struct platform_device *pdev = to_platform_device(dai->dev);
struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
struct atmel_pcm_dma_params *dma_params; struct atmel_pcm_dma_params *dma_params;
int dir, dir_mask; int dir, dir_mask;
int ret; int ret;
@ -346,7 +347,8 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
static void atmel_ssc_shutdown(struct snd_pcm_substream *substream, static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; struct platform_device *pdev = to_platform_device(dai->dev);
struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
struct atmel_pcm_dma_params *dma_params; struct atmel_pcm_dma_params *dma_params;
int dir, dir_mask; int dir, dir_mask;
@ -392,7 +394,8 @@ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
static int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai, static int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt) unsigned int fmt)
{ {
struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id]; struct platform_device *pdev = to_platform_device(cpu_dai->dev);
struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
ssc_p->daifmt = fmt; ssc_p->daifmt = fmt;
return 0; return 0;
@ -404,7 +407,8 @@ static int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
int div_id, int div) int div_id, int div)
{ {
struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id]; struct platform_device *pdev = to_platform_device(cpu_dai->dev);
struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
switch (div_id) { switch (div_id) {
case ATMEL_SSC_CMR_DIV: case ATMEL_SSC_CMR_DIV:
@ -445,7 +449,8 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
int id = dai->id; struct platform_device *pdev = to_platform_device(dai->dev);
int id = pdev->id;
struct atmel_ssc_info *ssc_p = &ssc_info[id]; struct atmel_ssc_info *ssc_p = &ssc_info[id];
struct ssc_device *ssc = ssc_p->ssc; struct ssc_device *ssc = ssc_p->ssc;
struct atmel_pcm_dma_params *dma_params; struct atmel_pcm_dma_params *dma_params;
@ -772,7 +777,8 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
static int atmel_ssc_prepare(struct snd_pcm_substream *substream, static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; struct platform_device *pdev = to_platform_device(dai->dev);
struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
struct atmel_pcm_dma_params *dma_params; struct atmel_pcm_dma_params *dma_params;
int dir; int dir;
@ -795,7 +801,8 @@ static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
static int atmel_ssc_trigger(struct snd_pcm_substream *substream, static int atmel_ssc_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai) int cmd, struct snd_soc_dai *dai)
{ {
struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; struct platform_device *pdev = to_platform_device(dai->dev);
struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
struct atmel_pcm_dma_params *dma_params; struct atmel_pcm_dma_params *dma_params;
int dir; int dir;
@ -824,11 +831,12 @@ static int atmel_ssc_trigger(struct snd_pcm_substream *substream,
static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai) static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai)
{ {
struct atmel_ssc_info *ssc_p; struct atmel_ssc_info *ssc_p;
struct platform_device *pdev = to_platform_device(cpu_dai->dev);
if (!cpu_dai->active) if (!cpu_dai->active)
return 0; return 0;
ssc_p = &ssc_info[cpu_dai->id]; ssc_p = &ssc_info[pdev->id];
/* Save the status register before disabling transmit and receive */ /* Save the status register before disabling transmit and receive */
ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR); ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR);
@ -852,12 +860,13 @@ static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai)
static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai) static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
{ {
struct atmel_ssc_info *ssc_p; struct atmel_ssc_info *ssc_p;
struct platform_device *pdev = to_platform_device(cpu_dai->dev);
u32 cr; u32 cr;
if (!cpu_dai->active) if (!cpu_dai->active)
return 0; return 0;
ssc_p = &ssc_info[cpu_dai->id]; ssc_p = &ssc_info[pdev->id];
/* restore SSC register settings */ /* restore SSC register settings */
ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr); ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr);

View File

@ -37,6 +37,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_address.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <sound/core.h> #include <sound/core.h>
@ -46,55 +47,6 @@
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/soc.h> #include <sound/soc.h>
/* Clock registers */
#define BCM2835_CLK_PCMCTL_REG 0x00
#define BCM2835_CLK_PCMDIV_REG 0x04
/* Clock register settings */
#define BCM2835_CLK_PASSWD (0x5a000000)
#define BCM2835_CLK_PASSWD_MASK (0xff000000)
#define BCM2835_CLK_MASH(v) ((v) << 9)
#define BCM2835_CLK_FLIP BIT(8)
#define BCM2835_CLK_BUSY BIT(7)
#define BCM2835_CLK_KILL BIT(5)
#define BCM2835_CLK_ENAB BIT(4)
#define BCM2835_CLK_SRC(v) (v)
#define BCM2835_CLK_SHIFT (12)
#define BCM2835_CLK_DIVI(v) ((v) << BCM2835_CLK_SHIFT)
#define BCM2835_CLK_DIVF(v) (v)
#define BCM2835_CLK_DIVF_MASK (0xFFF)
enum {
BCM2835_CLK_MASH_0 = 0,
BCM2835_CLK_MASH_1,
BCM2835_CLK_MASH_2,
BCM2835_CLK_MASH_3,
};
enum {
BCM2835_CLK_SRC_GND = 0,
BCM2835_CLK_SRC_OSC,
BCM2835_CLK_SRC_DBG0,
BCM2835_CLK_SRC_DBG1,
BCM2835_CLK_SRC_PLLA,
BCM2835_CLK_SRC_PLLC,
BCM2835_CLK_SRC_PLLD,
BCM2835_CLK_SRC_HDMI,
};
/* Most clocks are not useable (freq = 0) */
static const unsigned int bcm2835_clk_freq[BCM2835_CLK_SRC_HDMI+1] = {
[BCM2835_CLK_SRC_GND] = 0,
[BCM2835_CLK_SRC_OSC] = 19200000,
[BCM2835_CLK_SRC_DBG0] = 0,
[BCM2835_CLK_SRC_DBG1] = 0,
[BCM2835_CLK_SRC_PLLA] = 0,
[BCM2835_CLK_SRC_PLLC] = 0,
[BCM2835_CLK_SRC_PLLD] = 500000000,
[BCM2835_CLK_SRC_HDMI] = 0,
};
/* I2S registers */ /* I2S registers */
#define BCM2835_I2S_CS_A_REG 0x00 #define BCM2835_I2S_CS_A_REG 0x00
#define BCM2835_I2S_FIFO_A_REG 0x04 #define BCM2835_I2S_FIFO_A_REG 0x04
@ -158,10 +110,6 @@ static const unsigned int bcm2835_clk_freq[BCM2835_CLK_SRC_HDMI+1] = {
#define BCM2835_I2S_INT_RXR BIT(1) #define BCM2835_I2S_INT_RXR BIT(1)
#define BCM2835_I2S_INT_TXW BIT(0) #define BCM2835_I2S_INT_TXW BIT(0)
/* I2S DMA interface */
/* FIXME: Needs IOMMU support */
#define BCM2835_VCMMU_SHIFT (0x7E000000 - 0x20000000)
/* General device struct */ /* General device struct */
struct bcm2835_i2s_dev { struct bcm2835_i2s_dev {
struct device *dev; struct device *dev;
@ -169,21 +117,23 @@ struct bcm2835_i2s_dev {
unsigned int fmt; unsigned int fmt;
unsigned int bclk_ratio; unsigned int bclk_ratio;
struct regmap *i2s_regmap; struct regmap *i2s_regmap;
struct regmap *clk_regmap; struct clk *clk;
bool clk_prepared;
}; };
static void bcm2835_i2s_start_clock(struct bcm2835_i2s_dev *dev) static void bcm2835_i2s_start_clock(struct bcm2835_i2s_dev *dev)
{ {
/* Start the clock if in master mode */
unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK; unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK;
if (dev->clk_prepared)
return;
switch (master) { switch (master) {
case SND_SOC_DAIFMT_CBS_CFS: case SND_SOC_DAIFMT_CBS_CFS:
case SND_SOC_DAIFMT_CBS_CFM: case SND_SOC_DAIFMT_CBS_CFM:
regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, clk_prepare_enable(dev->clk);
BCM2835_CLK_PASSWD_MASK | BCM2835_CLK_ENAB, dev->clk_prepared = true;
BCM2835_CLK_PASSWD | BCM2835_CLK_ENAB);
break; break;
default: default:
break; break;
@ -192,28 +142,9 @@ static void bcm2835_i2s_start_clock(struct bcm2835_i2s_dev *dev)
static void bcm2835_i2s_stop_clock(struct bcm2835_i2s_dev *dev) static void bcm2835_i2s_stop_clock(struct bcm2835_i2s_dev *dev)
{ {
uint32_t clkreg; if (dev->clk_prepared)
int timeout = 1000; clk_disable_unprepare(dev->clk);
dev->clk_prepared = false;
/* Stop clock */
regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG,
BCM2835_CLK_PASSWD_MASK | BCM2835_CLK_ENAB,
BCM2835_CLK_PASSWD);
/* Wait for the BUSY flag going down */
while (--timeout) {
regmap_read(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, &clkreg);
if (!(clkreg & BCM2835_CLK_BUSY))
break;
}
if (!timeout) {
/* KILL the clock */
dev_err(dev->dev, "I2S clock didn't stop. Kill the clock!\n");
regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG,
BCM2835_CLK_KILL | BCM2835_CLK_PASSWD_MASK,
BCM2835_CLK_KILL | BCM2835_CLK_PASSWD);
}
} }
static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev, static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev,
@ -223,8 +154,7 @@ static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev,
uint32_t syncval; uint32_t syncval;
uint32_t csreg; uint32_t csreg;
uint32_t i2s_active_state; uint32_t i2s_active_state;
uint32_t clkreg; bool clk_was_prepared;
uint32_t clk_active_state;
uint32_t off; uint32_t off;
uint32_t clr; uint32_t clr;
@ -238,15 +168,10 @@ static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev,
regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &csreg); regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &csreg);
i2s_active_state = csreg & (BCM2835_I2S_RXON | BCM2835_I2S_TXON); i2s_active_state = csreg & (BCM2835_I2S_RXON | BCM2835_I2S_TXON);
regmap_read(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, &clkreg);
clk_active_state = clkreg & BCM2835_CLK_ENAB;
/* Start clock if not running */ /* Start clock if not running */
if (!clk_active_state) { clk_was_prepared = dev->clk_prepared;
regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, if (!clk_was_prepared)
BCM2835_CLK_PASSWD_MASK | BCM2835_CLK_ENAB, bcm2835_i2s_start_clock(dev);
BCM2835_CLK_PASSWD | BCM2835_CLK_ENAB);
}
/* Stop I2S module */ /* Stop I2S module */
regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, off, 0); regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, off, 0);
@ -280,7 +205,7 @@ static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev,
dev_err(dev->dev, "I2S SYNC error!\n"); dev_err(dev->dev, "I2S SYNC error!\n");
/* Stop clock if it was not running before */ /* Stop clock if it was not running before */
if (!clk_active_state) if (!clk_was_prepared)
bcm2835_i2s_stop_clock(dev); bcm2835_i2s_stop_clock(dev);
/* Restore I2S state */ /* Restore I2S state */
@ -309,19 +234,9 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
unsigned int sampling_rate = params_rate(params); unsigned int sampling_rate = params_rate(params);
unsigned int data_length, data_delay, bclk_ratio; unsigned int data_length, data_delay, bclk_ratio;
unsigned int ch1pos, ch2pos, mode, format; unsigned int ch1pos, ch2pos, mode, format;
unsigned int mash = BCM2835_CLK_MASH_1;
unsigned int divi, divf, target_frequency;
int clk_src = -1;
unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK;
bool bit_master = (master == SND_SOC_DAIFMT_CBS_CFS
|| master == SND_SOC_DAIFMT_CBS_CFM);
bool frame_master = (master == SND_SOC_DAIFMT_CBS_CFS
|| master == SND_SOC_DAIFMT_CBM_CFS);
uint32_t csreg; uint32_t csreg;
/* /*
@ -343,11 +258,9 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
switch (params_format(params)) { switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE: case SNDRV_PCM_FORMAT_S16_LE:
data_length = 16; data_length = 16;
bclk_ratio = 40;
break; break;
case SNDRV_PCM_FORMAT_S32_LE: case SNDRV_PCM_FORMAT_S32_LE:
data_length = 32; data_length = 32;
bclk_ratio = 80;
break; break;
default: default:
return -EINVAL; return -EINVAL;
@ -356,69 +269,12 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
/* If bclk_ratio already set, use that one. */ /* If bclk_ratio already set, use that one. */
if (dev->bclk_ratio) if (dev->bclk_ratio)
bclk_ratio = dev->bclk_ratio; bclk_ratio = dev->bclk_ratio;
else
/* otherwise calculate a fitting block ratio */
bclk_ratio = 2 * data_length;
/* /* set target clock rate*/
* Clock Settings clk_set_rate(dev->clk, sampling_rate * bclk_ratio);
*
* The target frequency of the bit clock is
* sampling rate * frame length
*
* Integer mode:
* Sampling rates that are multiples of 8000 kHz
* can be driven by the oscillator of 19.2 MHz
* with an integer divider as long as the frame length
* is an integer divider of 19200000/8000=2400 as set up above.
* This is no longer possible if the sampling rate
* is too high (e.g. 192 kHz), because the oscillator is too slow.
*
* MASH mode:
* For all other sampling rates, it is not possible to
* have an integer divider. Approximate the clock
* with the MASH module that induces a slight frequency
* variance. To minimize that it is best to have the fastest
* clock here. That is PLLD with 500 MHz.
*/
target_frequency = sampling_rate * bclk_ratio;
clk_src = BCM2835_CLK_SRC_OSC;
mash = BCM2835_CLK_MASH_0;
if (bcm2835_clk_freq[clk_src] % target_frequency == 0
&& bit_master && frame_master) {
divi = bcm2835_clk_freq[clk_src] / target_frequency;
divf = 0;
} else {
uint64_t dividend;
if (!dev->bclk_ratio) {
/*
* Overwrite bclk_ratio, because the
* above trick is not needed or can
* not be used.
*/
bclk_ratio = 2 * data_length;
}
target_frequency = sampling_rate * bclk_ratio;
clk_src = BCM2835_CLK_SRC_PLLD;
mash = BCM2835_CLK_MASH_1;
dividend = bcm2835_clk_freq[clk_src];
dividend <<= BCM2835_CLK_SHIFT;
do_div(dividend, target_frequency);
divi = dividend >> BCM2835_CLK_SHIFT;
divf = dividend & BCM2835_CLK_DIVF_MASK;
}
/* Set clock divider */
regmap_write(dev->clk_regmap, BCM2835_CLK_PCMDIV_REG, BCM2835_CLK_PASSWD
| BCM2835_CLK_DIVI(divi)
| BCM2835_CLK_DIVF(divf));
/* Setup clock, but don't start it yet */
regmap_write(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, BCM2835_CLK_PASSWD
| BCM2835_CLK_MASH(mash)
| BCM2835_CLK_SRC(clk_src));
/* Setup the frame format */ /* Setup the frame format */
format = BCM2835_I2S_CHEN; format = BCM2835_I2S_CHEN;
@ -692,7 +548,7 @@ static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = {
.trigger = bcm2835_i2s_trigger, .trigger = bcm2835_i2s_trigger,
.hw_params = bcm2835_i2s_hw_params, .hw_params = bcm2835_i2s_hw_params,
.set_fmt = bcm2835_i2s_set_dai_fmt, .set_fmt = bcm2835_i2s_set_dai_fmt,
.set_bclk_ratio = bcm2835_i2s_set_dai_bclk_ratio .set_bclk_ratio = bcm2835_i2s_set_dai_bclk_ratio,
}; };
static int bcm2835_i2s_dai_probe(struct snd_soc_dai *dai) static int bcm2835_i2s_dai_probe(struct snd_soc_dai *dai)
@ -750,34 +606,14 @@ static bool bcm2835_i2s_precious_reg(struct device *dev, unsigned int reg)
}; };
} }
static bool bcm2835_clk_volatile_reg(struct device *dev, unsigned int reg) static const struct regmap_config bcm2835_regmap_config = {
{ .reg_bits = 32,
switch (reg) { .reg_stride = 4,
case BCM2835_CLK_PCMCTL_REG: .val_bits = 32,
return true; .max_register = BCM2835_I2S_GRAY_REG,
default: .precious_reg = bcm2835_i2s_precious_reg,
return false; .volatile_reg = bcm2835_i2s_volatile_reg,
}; .cache_type = REGCACHE_RBTREE,
}
static const struct regmap_config bcm2835_regmap_config[] = {
{
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = BCM2835_I2S_GRAY_REG,
.precious_reg = bcm2835_i2s_precious_reg,
.volatile_reg = bcm2835_i2s_volatile_reg,
.cache_type = REGCACHE_RBTREE,
},
{
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = BCM2835_CLK_PCMDIV_REG,
.volatile_reg = bcm2835_clk_volatile_reg,
.cache_type = REGCACHE_RBTREE,
},
}; };
static const struct snd_soc_component_driver bcm2835_i2s_component = { static const struct snd_soc_component_driver bcm2835_i2s_component = {
@ -787,42 +623,50 @@ static const struct snd_soc_component_driver bcm2835_i2s_component = {
static int bcm2835_i2s_probe(struct platform_device *pdev) static int bcm2835_i2s_probe(struct platform_device *pdev)
{ {
struct bcm2835_i2s_dev *dev; struct bcm2835_i2s_dev *dev;
int i;
int ret; int ret;
struct regmap *regmap[2]; struct resource *mem;
struct resource *mem[2]; void __iomem *base;
const __be32 *addr;
/* Request both ioareas */ dma_addr_t dma_base;
for (i = 0; i <= 1; i++) {
void __iomem *base;
mem[i] = platform_get_resource(pdev, IORESOURCE_MEM, i);
base = devm_ioremap_resource(&pdev->dev, mem[i]);
if (IS_ERR(base))
return PTR_ERR(base);
regmap[i] = devm_regmap_init_mmio(&pdev->dev, base,
&bcm2835_regmap_config[i]);
if (IS_ERR(regmap[i]))
return PTR_ERR(regmap[i]);
}
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), dev = devm_kzalloc(&pdev->dev, sizeof(*dev),
GFP_KERNEL); GFP_KERNEL);
if (!dev) if (!dev)
return -ENOMEM; return -ENOMEM;
dev->i2s_regmap = regmap[0]; /* get the clock */
dev->clk_regmap = regmap[1]; dev->clk_prepared = false;
dev->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(dev->clk)) {
dev_err(&pdev->dev, "could not get clk: %ld\n",
PTR_ERR(dev->clk));
return PTR_ERR(dev->clk);
}
/* Request ioarea */
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(base))
return PTR_ERR(base);
dev->i2s_regmap = devm_regmap_init_mmio(&pdev->dev, base,
&bcm2835_regmap_config);
if (IS_ERR(dev->i2s_regmap))
return PTR_ERR(dev->i2s_regmap);
/* Set the DMA address - we have to parse DT ourselves */
addr = of_get_address(pdev->dev.of_node, 0, NULL, NULL);
if (!addr) {
dev_err(&pdev->dev, "could not get DMA-register address\n");
return -EINVAL;
}
dma_base = be32_to_cpup(addr);
/* Set the DMA address */
dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr =
(dma_addr_t)mem[0]->start + BCM2835_I2S_FIFO_A_REG dma_base + BCM2835_I2S_FIFO_A_REG;
+ BCM2835_VCMMU_SHIFT;
dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr = dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr =
(dma_addr_t)mem[0]->start + BCM2835_I2S_FIFO_A_REG dma_base + BCM2835_I2S_FIFO_A_REG;
+ BCM2835_VCMMU_SHIFT;
/* Set the bus width */ /* Set the bus width */
dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr_width = dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr_width =

View File

@ -79,7 +79,9 @@ config SND_SOC_ALL_CODECS
select SND_SOC_MAX98090 if I2C select SND_SOC_MAX98090 if I2C
select SND_SOC_MAX98095 if I2C select SND_SOC_MAX98095 if I2C
select SND_SOC_MAX98357A if GPIOLIB select SND_SOC_MAX98357A if GPIOLIB
select SND_SOC_MAX9867 if I2C
select SND_SOC_MAX98925 if I2C select SND_SOC_MAX98925 if I2C
select SND_SOC_MAX98926 if I2C
select SND_SOC_MAX9850 if I2C select SND_SOC_MAX9850 if I2C
select SND_SOC_MAX9768 if I2C select SND_SOC_MAX9768 if I2C
select SND_SOC_MAX9877 if I2C select SND_SOC_MAX9877 if I2C
@ -87,7 +89,8 @@ config SND_SOC_ALL_CODECS
select SND_SOC_ML26124 if I2C select SND_SOC_ML26124 if I2C
select SND_SOC_NAU8825 if I2C select SND_SOC_NAU8825 if I2C
select SND_SOC_PCM1681 if I2C select SND_SOC_PCM1681 if I2C
select SND_SOC_PCM179X if SPI_MASTER select SND_SOC_PCM179X_I2C if I2C
select SND_SOC_PCM179X_SPI if SPI_MASTER
select SND_SOC_PCM3008 select SND_SOC_PCM3008
select SND_SOC_PCM3168A_I2C if I2C select SND_SOC_PCM3168A_I2C if I2C
select SND_SOC_PCM3168A_SPI if SPI_MASTER select SND_SOC_PCM3168A_SPI if SPI_MASTER
@ -95,6 +98,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_PCM512x_SPI if SPI_MASTER select SND_SOC_PCM512x_SPI if SPI_MASTER
select SND_SOC_RT286 if I2C select SND_SOC_RT286 if I2C
select SND_SOC_RT298 if I2C select SND_SOC_RT298 if I2C
select SND_SOC_RT5514 if I2C
select SND_SOC_RT5616 if I2C select SND_SOC_RT5616 if I2C
select SND_SOC_RT5631 if I2C select SND_SOC_RT5631 if I2C
select SND_SOC_RT5640 if I2C select SND_SOC_RT5640 if I2C
@ -490,6 +494,7 @@ config SND_SOC_GTM601
config SND_SOC_HDAC_HDMI config SND_SOC_HDAC_HDMI
tristate tristate
select SND_HDA_EXT_CORE select SND_HDA_EXT_CORE
select SND_PCM_ELD
select HDMI select HDMI
config SND_SOC_ICS43432 config SND_SOC_ICS43432
@ -497,6 +502,7 @@ config SND_SOC_ICS43432
config SND_SOC_INNO_RK3036 config SND_SOC_INNO_RK3036
tristate "Inno codec driver for RK3036 SoC" tristate "Inno codec driver for RK3036 SoC"
select REGMAP_MMIO
config SND_SOC_ISABELLE config SND_SOC_ISABELLE
tristate tristate
@ -516,9 +522,15 @@ config SND_SOC_MAX98095
config SND_SOC_MAX98357A config SND_SOC_MAX98357A
tristate tristate
config SND_SOC_MAX9867
tristate
config SND_SOC_MAX98925 config SND_SOC_MAX98925
tristate tristate
config SND_SOC_MAX98926
tristate
config SND_SOC_MAX9850 config SND_SOC_MAX9850
tristate tristate
@ -527,8 +539,23 @@ config SND_SOC_PCM1681
depends on I2C depends on I2C
config SND_SOC_PCM179X config SND_SOC_PCM179X
tristate "Texas Instruments PCM179X CODEC" tristate
config SND_SOC_PCM179X_I2C
tristate "Texas Instruments PCM179X CODEC (I2C)"
depends on I2C
select SND_SOC_PCM179X
help
Enable support for Texas Instruments PCM179x CODEC.
Select this if your PCM179x is connected via an I2C bus.
config SND_SOC_PCM179X_SPI
tristate "Texas Instruments PCM179X CODEC (SPI)"
depends on SPI_MASTER depends on SPI_MASTER
select SND_SOC_PCM179X
help
Enable support for Texas Instruments PCM179x CODEC.
Select this if your PCM179x is connected via an SPI bus.
config SND_SOC_PCM3008 config SND_SOC_PCM3008
tristate tristate
@ -565,6 +592,7 @@ config SND_SOC_PCM512x_SPI
config SND_SOC_RL6231 config SND_SOC_RL6231
tristate tristate
default y if SND_SOC_RT5514=y
default y if SND_SOC_RT5616=y default y if SND_SOC_RT5616=y
default y if SND_SOC_RT5640=y default y if SND_SOC_RT5640=y
default y if SND_SOC_RT5645=y default y if SND_SOC_RT5645=y
@ -572,6 +600,7 @@ config SND_SOC_RL6231
default y if SND_SOC_RT5659=y default y if SND_SOC_RT5659=y
default y if SND_SOC_RT5670=y default y if SND_SOC_RT5670=y
default y if SND_SOC_RT5677=y default y if SND_SOC_RT5677=y
default m if SND_SOC_RT5514=m
default m if SND_SOC_RT5616=m default m if SND_SOC_RT5616=m
default m if SND_SOC_RT5640=m default m if SND_SOC_RT5640=m
default m if SND_SOC_RT5645=m default m if SND_SOC_RT5645=m
@ -595,9 +624,12 @@ config SND_SOC_RT298
tristate tristate
depends on I2C depends on I2C
config SND_SOC_RT5616 config SND_SOC_RT5514
tristate tristate
config SND_SOC_RT5616
tristate "Realtek RT5616 CODEC"
config SND_SOC_RT5631 config SND_SOC_RT5631
tristate "Realtek ALC5631/RT5631 CODEC" tristate "Realtek ALC5631/RT5631 CODEC"
depends on I2C depends on I2C

View File

@ -74,13 +74,17 @@ snd-soc-max98088-objs := max98088.o
snd-soc-max98090-objs := max98090.o snd-soc-max98090-objs := max98090.o
snd-soc-max98095-objs := max98095.o snd-soc-max98095-objs := max98095.o
snd-soc-max98357a-objs := max98357a.o snd-soc-max98357a-objs := max98357a.o
snd-soc-max9867-objs := max9867.o
snd-soc-max98925-objs := max98925.o snd-soc-max98925-objs := max98925.o
snd-soc-max98926-objs := max98926.o
snd-soc-max9850-objs := max9850.o snd-soc-max9850-objs := max9850.o
snd-soc-mc13783-objs := mc13783.o snd-soc-mc13783-objs := mc13783.o
snd-soc-ml26124-objs := ml26124.o snd-soc-ml26124-objs := ml26124.o
snd-soc-nau8825-objs := nau8825.o snd-soc-nau8825-objs := nau8825.o
snd-soc-pcm1681-objs := pcm1681.o snd-soc-pcm1681-objs := pcm1681.o
snd-soc-pcm179x-codec-objs := pcm179x.o snd-soc-pcm179x-codec-objs := pcm179x.o
snd-soc-pcm179x-i2c-objs := pcm179x-i2c.o
snd-soc-pcm179x-spi-objs := pcm179x-spi.o
snd-soc-pcm3008-objs := pcm3008.o snd-soc-pcm3008-objs := pcm3008.o
snd-soc-pcm3168a-objs := pcm3168a.o snd-soc-pcm3168a-objs := pcm3168a.o
snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o
@ -92,6 +96,7 @@ snd-soc-rl6231-objs := rl6231.o
snd-soc-rl6347a-objs := rl6347a.o snd-soc-rl6347a-objs := rl6347a.o
snd-soc-rt286-objs := rt286.o snd-soc-rt286-objs := rt286.o
snd-soc-rt298-objs := rt298.o snd-soc-rt298-objs := rt298.o
snd-soc-rt5514-objs := rt5514.o
snd-soc-rt5616-objs := rt5616.o snd-soc-rt5616-objs := rt5616.o
snd-soc-rt5631-objs := rt5631.o snd-soc-rt5631-objs := rt5631.o
snd-soc-rt5640-objs := rt5640.o snd-soc-rt5640-objs := rt5640.o
@ -278,13 +283,17 @@ obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o
obj-$(CONFIG_SND_SOC_MAX98090) += snd-soc-max98090.o obj-$(CONFIG_SND_SOC_MAX98090) += snd-soc-max98090.o
obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o
obj-$(CONFIG_SND_SOC_MAX98357A) += snd-soc-max98357a.o obj-$(CONFIG_SND_SOC_MAX98357A) += snd-soc-max98357a.o
obj-$(CONFIG_SND_SOC_MAX9867) += snd-soc-max9867.o
obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o
obj-$(CONFIG_SND_SOC_MAX98926) += snd-soc-max98926.o
obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o
obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o
obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o
obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o
obj-$(CONFIG_SND_SOC_PCM179X) += snd-soc-pcm179x-codec.o obj-$(CONFIG_SND_SOC_PCM179X) += snd-soc-pcm179x-codec.o
obj-$(CONFIG_SND_SOC_PCM179X_I2C) += snd-soc-pcm179x-i2c.o
obj-$(CONFIG_SND_SOC_PCM179X_SPI) += snd-soc-pcm179x-spi.o
obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o
obj-$(CONFIG_SND_SOC_PCM3168A_I2C) += snd-soc-pcm3168a-i2c.o obj-$(CONFIG_SND_SOC_PCM3168A_I2C) += snd-soc-pcm3168a-i2c.o
@ -296,6 +305,7 @@ obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o
obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o
obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o
obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o
obj-$(CONFIG_SND_SOC_RT5514) += snd-soc-rt5514.o
obj-$(CONFIG_SND_SOC_RT5616) += snd-soc-rt5616.o obj-$(CONFIG_SND_SOC_RT5616) += snd-soc-rt5616.o
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o

View File

@ -2134,7 +2134,6 @@ static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
"%s: ERROR: Unsupporter master mask 0x%x\n", "%s: ERROR: Unsupporter master mask 0x%x\n",
__func__, fmt & SND_SOC_DAIFMT_MASTER_MASK); __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK);
return -EINVAL; return -EINVAL;
break;
} }
snd_soc_update_bits(codec, AB8500_DIGIFCONF3, mask, val); snd_soc_update_bits(codec, AB8500_DIGIFCONF3, mask, val);

View File

@ -1,5 +1,5 @@
/* /*
* Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec
* *
* Copyright 2014 Analog Devices Inc. * Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de> * Author: Lars-Peter Clausen <lars@metafoo.de>
@ -44,9 +44,21 @@ static const struct i2c_device_id adau1761_i2c_ids[] = {
}; };
MODULE_DEVICE_TABLE(i2c, adau1761_i2c_ids); MODULE_DEVICE_TABLE(i2c, adau1761_i2c_ids);
#if defined(CONFIG_OF)
static const struct of_device_id adau1761_i2c_dt_ids[] = {
{ .compatible = "adi,adau1361", },
{ .compatible = "adi,adau1461", },
{ .compatible = "adi,adau1761", },
{ .compatible = "adi,adau1961", },
{ },
};
MODULE_DEVICE_TABLE(of, adau1761_i2c_dt_ids);
#endif
static struct i2c_driver adau1761_i2c_driver = { static struct i2c_driver adau1761_i2c_driver = {
.driver = { .driver = {
.name = "adau1761", .name = "adau1761",
.of_match_table = of_match_ptr(adau1761_i2c_dt_ids),
}, },
.probe = adau1761_i2c_probe, .probe = adau1761_i2c_probe,
.remove = adau1761_i2c_remove, .remove = adau1761_i2c_remove,

View File

@ -1,5 +1,5 @@
/* /*
* Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec
* *
* Copyright 2014 Analog Devices Inc. * Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de> * Author: Lars-Peter Clausen <lars@metafoo.de>
@ -61,9 +61,21 @@ static const struct spi_device_id adau1761_spi_id[] = {
}; };
MODULE_DEVICE_TABLE(spi, adau1761_spi_id); MODULE_DEVICE_TABLE(spi, adau1761_spi_id);
#if defined(CONFIG_OF)
static const struct of_device_id adau1761_spi_dt_ids[] = {
{ .compatible = "adi,adau1361", },
{ .compatible = "adi,adau1461", },
{ .compatible = "adi,adau1761", },
{ .compatible = "adi,adau1961", },
{ },
};
MODULE_DEVICE_TABLE(of, adau1761_spi_dt_ids);
#endif
static struct spi_driver adau1761_spi_driver = { static struct spi_driver adau1761_spi_driver = {
.driver = { .driver = {
.name = "adau1761", .name = "adau1761",
.of_match_table = of_match_ptr(adau1761_spi_dt_ids),
}, },
.probe = adau1761_spi_probe, .probe = adau1761_spi_probe,
.remove = adau1761_spi_remove, .remove = adau1761_spi_remove,

View File

@ -1,5 +1,5 @@
/* /*
* Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec
* *
* Copyright 2011-2013 Analog Devices Inc. * Copyright 2011-2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de> * Author: Lars-Peter Clausen <lars@metafoo.de>
@ -456,13 +456,17 @@ static int adau1761_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE: case SND_SOC_BIAS_PREPARE:
break; break;
case SND_SOC_BIAS_STANDBY: case SND_SOC_BIAS_STANDBY:
regcache_cache_only(adau->regmap, false);
regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL, regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, ADAU17X1_CLOCK_CONTROL_SYSCLK_EN,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN); ADAU17X1_CLOCK_CONTROL_SYSCLK_EN);
if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
regcache_sync(adau->regmap);
break; break;
case SND_SOC_BIAS_OFF: case SND_SOC_BIAS_OFF:
regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL, regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, 0); ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, 0);
regcache_cache_only(adau->regmap, true);
break; break;
} }
@ -783,6 +787,10 @@ int adau1761_probe(struct device *dev, struct regmap *regmap,
if (ret) if (ret)
return ret; return ret;
/* Enable cache only mode as we could miss writes before bias level
* reaches standby and the core clock is enabled */
regcache_cache_only(regmap, true);
return snd_soc_register_codec(dev, &adau1761_codec_driver, dai_drv, 1); return snd_soc_register_codec(dev, &adau1761_codec_driver, dai_drv, 1);
} }
EXPORT_SYMBOL_GPL(adau1761_probe); EXPORT_SYMBOL_GPL(adau1761_probe);

View File

@ -42,9 +42,19 @@ static const struct i2c_device_id adau1781_i2c_ids[] = {
}; };
MODULE_DEVICE_TABLE(i2c, adau1781_i2c_ids); MODULE_DEVICE_TABLE(i2c, adau1781_i2c_ids);
#if defined(CONFIG_OF)
static const struct of_device_id adau1781_i2c_dt_ids[] = {
{ .compatible = "adi,adau1381", },
{ .compatible = "adi,adau1781", },
{ },
};
MODULE_DEVICE_TABLE(of, adau1781_i2c_dt_ids);
#endif
static struct i2c_driver adau1781_i2c_driver = { static struct i2c_driver adau1781_i2c_driver = {
.driver = { .driver = {
.name = "adau1781", .name = "adau1781",
.of_match_table = of_match_ptr(adau1781_i2c_dt_ids),
}, },
.probe = adau1781_i2c_probe, .probe = adau1781_i2c_probe,
.remove = adau1781_i2c_remove, .remove = adau1781_i2c_remove,

View File

@ -59,9 +59,19 @@ static const struct spi_device_id adau1781_spi_id[] = {
}; };
MODULE_DEVICE_TABLE(spi, adau1781_spi_id); MODULE_DEVICE_TABLE(spi, adau1781_spi_id);
#if defined(CONFIG_OF)
static const struct of_device_id adau1781_spi_dt_ids[] = {
{ .compatible = "adi,adau1381", },
{ .compatible = "adi,adau1781", },
{ },
};
MODULE_DEVICE_TABLE(of, adau1781_spi_dt_ids);
#endif
static struct spi_driver adau1781_spi_driver = { static struct spi_driver adau1781_spi_driver = {
.driver = { .driver = {
.name = "adau1781", .name = "adau1781",
.of_match_table = of_match_ptr(adau1781_spi_dt_ids),
}, },
.probe = adau1781_spi_probe, .probe = adau1781_spi_probe,
.remove = adau1781_spi_remove, .remove = adau1781_spi_remove,

View File

@ -1,5 +1,5 @@
/* /*
* Driver for ADAU1781/ADAU1781 codec * Driver for ADAU1381/ADAU1781 codec
* *
* Copyright 2011-2013 Analog Devices Inc. * Copyright 2011-2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de> * Author: Lars-Peter Clausen <lars@metafoo.de>

View File

@ -20,6 +20,8 @@
#include <sound/initval.h> #include <sound/initval.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <linux/of.h>
#define ADS117X_RATES (SNDRV_PCM_RATE_8000_48000) #define ADS117X_RATES (SNDRV_PCM_RATE_8000_48000)
#define ADS117X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) #define ADS117X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
@ -75,9 +77,19 @@ static int ads117x_remove(struct platform_device *pdev)
return 0; return 0;
} }
#if defined(CONFIG_OF)
static const struct of_device_id ads117x_dt_ids[] = {
{ .compatible = "ti,ads1174" },
{ .compatible = "ti,ads1178" },
{ },
};
MODULE_DEVICE_TABLE(of, ads117x_dt_ids);
#endif
static struct platform_driver ads117x_codec_driver = { static struct platform_driver ads117x_codec_driver = {
.driver = { .driver = {
.name = "ads117x-codec", .name = "ads117x-codec",
.of_match_table = of_match_ptr(ads117x_dt_ids),
}, },
.probe = ads117x_probe, .probe = ads117x_probe,

View File

@ -1398,29 +1398,6 @@ static const int arizona_48k_bclk_rates[] = {
24576000, 24576000,
}; };
static const unsigned int arizona_48k_rates[] = {
12000,
24000,
48000,
96000,
192000,
384000,
768000,
4000,
8000,
16000,
32000,
64000,
128000,
256000,
512000,
};
static const struct snd_pcm_hw_constraint_list arizona_48k_constraint = {
.count = ARRAY_SIZE(arizona_48k_rates),
.list = arizona_48k_rates,
};
static const int arizona_44k1_bclk_rates[] = { static const int arizona_44k1_bclk_rates[] = {
-1, -1,
44100, 44100,
@ -1443,22 +1420,7 @@ static const int arizona_44k1_bclk_rates[] = {
22579200, 22579200,
}; };
static const unsigned int arizona_44k1_rates[] = { static const unsigned int arizona_sr_vals[] = {
11025,
22050,
44100,
88200,
176400,
352800,
705600,
};
static const struct snd_pcm_hw_constraint_list arizona_44k1_constraint = {
.count = ARRAY_SIZE(arizona_44k1_rates),
.list = arizona_44k1_rates,
};
static int arizona_sr_vals[] = {
0, 0,
12000, 12000,
24000, 24000,
@ -1485,13 +1447,21 @@ static int arizona_sr_vals[] = {
512000, 512000,
}; };
#define ARIZONA_48K_RATE_MASK 0x0F003E
#define ARIZONA_44K1_RATE_MASK 0x003E00
#define ARIZONA_RATE_MASK (ARIZONA_48K_RATE_MASK | ARIZONA_44K1_RATE_MASK)
static const struct snd_pcm_hw_constraint_list arizona_constraint = {
.count = ARRAY_SIZE(arizona_sr_vals),
.list = arizona_sr_vals,
};
static int arizona_startup(struct snd_pcm_substream *substream, static int arizona_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct snd_soc_codec *codec = dai->codec; struct snd_soc_codec *codec = dai->codec;
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
const struct snd_pcm_hw_constraint_list *constraint;
unsigned int base_rate; unsigned int base_rate;
if (!substream->runtime) if (!substream->runtime)
@ -1509,16 +1479,15 @@ static int arizona_startup(struct snd_pcm_substream *substream,
} }
if (base_rate == 0) if (base_rate == 0)
return 0; dai_priv->constraint.mask = ARIZONA_RATE_MASK;
else if (base_rate % 8000)
if (base_rate % 8000) dai_priv->constraint.mask = ARIZONA_44K1_RATE_MASK;
constraint = &arizona_44k1_constraint;
else else
constraint = &arizona_48k_constraint; dai_priv->constraint.mask = ARIZONA_48K_RATE_MASK;
return snd_pcm_hw_constraint_list(substream->runtime, 0, return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, SNDRV_PCM_HW_PARAM_RATE,
constraint); &dai_priv->constraint);
} }
static void arizona_wm5102_set_dac_comp(struct snd_soc_codec *codec, static void arizona_wm5102_set_dac_comp(struct snd_soc_codec *codec,
@ -1911,6 +1880,7 @@ int arizona_init_dai(struct arizona_priv *priv, int id)
struct arizona_dai_priv *dai_priv = &priv->dai[id]; struct arizona_dai_priv *dai_priv = &priv->dai[id];
dai_priv->clk = ARIZONA_CLK_SYSCLK; dai_priv->clk = ARIZONA_CLK_SYSCLK;
dai_priv->constraint = arizona_constraint;
return 0; return 0;
} }
@ -2179,11 +2149,12 @@ static int arizona_calc_fll(struct arizona_fll *fll,
return -EINVAL; return -EINVAL;
} }
arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n", arizona_fll_dbg(fll, "N=%d THETA=%d LAMBDA=%d\n",
cfg->n, cfg->theta, cfg->lambda); cfg->n, cfg->theta, cfg->lambda);
arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n", arizona_fll_dbg(fll, "FRATIO=0x%x(%d) OUTDIV=%d REFCLK_DIV=0x%x(%d)\n",
cfg->fratio, cfg->fratio, cfg->outdiv, cfg->refdiv); cfg->fratio, ratio, cfg->outdiv,
arizona_fll_dbg(fll, "GAIN=%d\n", cfg->gain); cfg->refdiv, 1 << cfg->refdiv);
arizona_fll_dbg(fll, "GAIN=0x%x(%d)\n", cfg->gain, 1 << cfg->gain);
return 0; return 0;

View File

@ -57,7 +57,7 @@
#define ARIZONA_CLK_98MHZ 5 #define ARIZONA_CLK_98MHZ 5
#define ARIZONA_CLK_147MHZ 6 #define ARIZONA_CLK_147MHZ 6
#define ARIZONA_MAX_DAI 8 #define ARIZONA_MAX_DAI 10
#define ARIZONA_MAX_ADSP 4 #define ARIZONA_MAX_ADSP 4
#define ARIZONA_DVFS_SR1_RQ 0x001 #define ARIZONA_DVFS_SR1_RQ 0x001
@ -68,6 +68,8 @@ struct wm_adsp;
struct arizona_dai_priv { struct arizona_dai_priv {
int clk; int clk;
struct snd_pcm_hw_constraint_list constraint;
}; };
struct arizona_priv { struct arizona_priv {

View File

@ -44,6 +44,7 @@ struct cs42xx8_priv {
bool slave_mode; bool slave_mode;
unsigned long sysclk; unsigned long sysclk;
u32 tx_channels;
}; };
/* -127.5dB to 0dB with step of 0.5dB */ /* -127.5dB to 0dB with step of 0.5dB */
@ -257,6 +258,9 @@ static int cs42xx8_hw_params(struct snd_pcm_substream *substream,
u32 ratio = cs42xx8->sysclk / params_rate(params); u32 ratio = cs42xx8->sysclk / params_rate(params);
u32 i, fm, val, mask; u32 i, fm, val, mask;
if (tx)
cs42xx8->tx_channels = params_channels(params);
for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) { for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) {
if (cs42xx8_ratios[i].ratio == ratio) if (cs42xx8_ratios[i].ratio == ratio)
break; break;
@ -283,9 +287,11 @@ static int cs42xx8_digital_mute(struct snd_soc_dai *dai, int mute)
{ {
struct snd_soc_codec *codec = dai->codec; struct snd_soc_codec *codec = dai->codec;
struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec); struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
u8 dac_unmute = cs42xx8->tx_channels ?
~((0x1 << cs42xx8->tx_channels) - 1) : 0;
regmap_update_bits(cs42xx8->regmap, CS42XX8_DACMUTE, regmap_write(cs42xx8->regmap, CS42XX8_DACMUTE,
CS42XX8_DACMUTE_ALL, mute ? CS42XX8_DACMUTE_ALL : 0); mute ? CS42XX8_DACMUTE_ALL : dac_unmute);
return 0; return 0;
} }

View File

@ -57,6 +57,25 @@ static const struct wm_adsp_region *cs47l24_dsp_regions[] = {
cs47l24_dsp3_regions, cs47l24_dsp3_regions,
}; };
static int cs47l24_adsp_power_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
unsigned int v;
int ret;
ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &v);
if (ret != 0) {
dev_err(codec->dev, "Failed to read SYSCLK state: %d\n", ret);
return ret;
}
v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT;
return wm_adsp2_early_event(w, kcontrol, event, v);
}
static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
static DECLARE_TLV_DB_SCALE(noise_tlv, -13200, 600, 0); static DECLARE_TLV_DB_SCALE(noise_tlv, -13200, 600, 0);
@ -405,8 +424,8 @@ SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0,
SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0, SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0,
NULL, 0), NULL, 0),
WM_ADSP2("DSP2", 1), WM_ADSP2("DSP2", 1, cs47l24_adsp_power_ev),
WM_ADSP2("DSP3", 2), WM_ADSP2("DSP3", 2, cs47l24_adsp_power_ev),
SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3, SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3,
ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0), ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0),
@ -779,6 +798,9 @@ static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = {
{ "AIF2 Capture", NULL, "SYSCLK" }, { "AIF2 Capture", NULL, "SYSCLK" },
{ "AIF3 Capture", NULL, "SYSCLK" }, { "AIF3 Capture", NULL, "SYSCLK" },
{ "Voice Control DSP", NULL, "DSP3" },
{ "Voice Control DSP", NULL, "SYSCLK" },
{ "IN1L PGA", NULL, "IN1L" }, { "IN1L PGA", NULL, "IN1L" },
{ "IN1R PGA", NULL, "IN1R" }, { "IN1R PGA", NULL, "IN1R" },
@ -901,7 +923,7 @@ static int cs47l24_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
} }
} }
#define CS47L24_RATES SNDRV_PCM_RATE_8000_192000 #define CS47L24_RATES SNDRV_PCM_RATE_KNOT
#define CS47L24_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ #define CS47L24_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
@ -973,12 +995,68 @@ static struct snd_soc_dai_driver cs47l24_dai[] = {
.symmetric_rates = 1, .symmetric_rates = 1,
.symmetric_samplebits = 1, .symmetric_samplebits = 1,
}, },
{
.name = "cs47l24-cpu-voicectrl",
.capture = {
.stream_name = "Voice Control CPU",
.channels_min = 1,
.channels_max = 1,
.rates = CS47L24_RATES,
.formats = CS47L24_FORMATS,
},
.compress_new = snd_soc_new_compress,
},
{
.name = "cs47l24-dsp-voicectrl",
.capture = {
.stream_name = "Voice Control DSP",
.channels_min = 1,
.channels_max = 1,
.rates = CS47L24_RATES,
.formats = CS47L24_FORMATS,
},
},
}; };
static int cs47l24_open(struct snd_compr_stream *stream)
{
struct snd_soc_pcm_runtime *rtd = stream->private_data;
struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(rtd->codec);
struct arizona *arizona = priv->core.arizona;
int n_adsp;
if (strcmp(rtd->codec_dai->name, "cs47l24-dsp-voicectrl") == 0) {
n_adsp = 2;
} else {
dev_err(arizona->dev,
"No suitable compressed stream for DAI '%s'\n",
rtd->codec_dai->name);
return -EINVAL;
}
return wm_adsp_compr_open(&priv->core.adsp[n_adsp], stream);
}
static irqreturn_t cs47l24_adsp2_irq(int irq, void *data)
{
struct cs47l24_priv *priv = data;
struct arizona *arizona = priv->core.arizona;
int ret;
ret = wm_adsp_compr_handle_irq(&priv->core.adsp[2]);
if (ret == -ENODEV) {
dev_err(arizona->dev, "Spurious compressed data IRQ\n");
return IRQ_NONE;
}
return IRQ_HANDLED;
}
static int cs47l24_codec_probe(struct snd_soc_codec *codec) static int cs47l24_codec_probe(struct snd_soc_codec *codec)
{ {
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec); struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec);
struct arizona *arizona = priv->core.arizona;
int ret; int ret;
priv->core.arizona->dapm = dapm; priv->core.arizona->dapm = dapm;
@ -987,6 +1065,14 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec)
arizona_init_gpio(codec); arizona_init_gpio(codec);
arizona_init_mono(codec); arizona_init_mono(codec);
ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
"ADSP2 Compressed IRQ", cs47l24_adsp2_irq,
priv);
if (ret != 0) {
dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret);
return ret;
}
ret = wm_adsp2_codec_probe(&priv->core.adsp[1], codec); ret = wm_adsp2_codec_probe(&priv->core.adsp[1], codec);
if (ret) if (ret)
goto err_adsp2_codec_probe; goto err_adsp2_codec_probe;
@ -1014,13 +1100,14 @@ err_adsp2_codec_probe:
static int cs47l24_codec_remove(struct snd_soc_codec *codec) static int cs47l24_codec_remove(struct snd_soc_codec *codec)
{ {
struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec); struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec);
struct arizona *arizona = priv->core.arizona;
wm_adsp2_codec_remove(&priv->core.adsp[1], codec); wm_adsp2_codec_remove(&priv->core.adsp[1], codec);
wm_adsp2_codec_remove(&priv->core.adsp[2], codec); wm_adsp2_codec_remove(&priv->core.adsp[2], codec);
priv->core.arizona->dapm = NULL; priv->core.arizona->dapm = NULL;
arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
return 0; return 0;
} }
@ -1057,6 +1144,19 @@ static struct snd_soc_codec_driver soc_codec_dev_cs47l24 = {
.num_dapm_routes = ARRAY_SIZE(cs47l24_dapm_routes), .num_dapm_routes = ARRAY_SIZE(cs47l24_dapm_routes),
}; };
static struct snd_compr_ops cs47l24_compr_ops = {
.open = cs47l24_open,
.free = wm_adsp_compr_free,
.set_params = wm_adsp_compr_set_params,
.get_caps = wm_adsp_compr_get_caps,
.trigger = wm_adsp_compr_trigger,
.pointer = wm_adsp_compr_pointer,
.copy = wm_adsp_compr_copy,
};
static struct snd_soc_platform_driver cs47l24_compr_platform = {
.compr_ops = &cs47l24_compr_ops,
};
static int cs47l24_probe(struct platform_device *pdev) static int cs47l24_probe(struct platform_device *pdev)
{ {
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
@ -1120,12 +1220,25 @@ static int cs47l24_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev); pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev); pm_runtime_idle(&pdev->dev);
return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24, ret = snd_soc_register_platform(&pdev->dev, &cs47l24_compr_platform);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
return ret;
}
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24,
cs47l24_dai, ARRAY_SIZE(cs47l24_dai)); cs47l24_dai, ARRAY_SIZE(cs47l24_dai));
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
snd_soc_unregister_platform(&pdev->dev);
}
return ret;
} }
static int cs47l24_remove(struct platform_device *pdev) static int cs47l24_remove(struct platform_device *pdev)
{ {
snd_soc_unregister_platform(&pdev->dev);
snd_soc_unregister_codec(&pdev->dev); snd_soc_unregister_codec(&pdev->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,6 @@
#ifndef __HDAC_HDMI_H__
#define __HDAC_HDMI_H__
int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int pcm);
#endif /* __HDAC_HDMI_H__ */

546
sound/soc/codecs/max9867.c Executable file
View File

@ -0,0 +1,546 @@
/*
* max9867.c -- max9867 ALSA SoC Audio driver
*
* Copyright 2013-15 Maxim Integrated Products
*
* 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/delay.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include "max9867.h"
static const char *const max9867_spmode[] = {
"Stereo Diff", "Mono Diff",
"Stereo Cap", "Mono Cap",
"Stereo Single", "Mono Single",
"Stereo Single Fast", "Mono Single Fast"
};
static const char *const max9867_sidetone_text[] = {
"None", "Left", "Right", "LeftRight", "LeftRightDiv2",
};
static const char *const max9867_filter_text[] = {"IIR", "FIR"};
static SOC_ENUM_SINGLE_DECL(max9867_filter, MAX9867_CODECFLTR, 7,
max9867_filter_text);
static SOC_ENUM_SINGLE_DECL(max9867_spkmode, MAX9867_MODECONFIG, 0,
max9867_spmode);
static SOC_ENUM_SINGLE_DECL(max9867_sidetone, MAX9867_DACGAIN, 6,
max9867_sidetone_text);
static DECLARE_TLV_DB_SCALE(max9860_capture_tlv, -600, 200, 0);
static DECLARE_TLV_DB_SCALE(max9860_mic_tlv, 2000, 100, 1);
static DECLARE_TLV_DB_SCALE(max9860_adc_left_tlv, -1200, 100, 1);
static DECLARE_TLV_DB_SCALE(max9860_adc_right_tlv, -1200, 100, 1);
static const unsigned int max98088_micboost_tlv[] = {
TLV_DB_RANGE_HEAD(2),
0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0),
2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0),
};
static const struct snd_kcontrol_new max9867_snd_controls[] = {
SOC_DOUBLE_R("Master Playback Volume", MAX9867_LEFTVOL,
MAX9867_RIGHTVOL, 0, 63, 1),
SOC_DOUBLE_R_TLV("Capture Volume", MAX9867_LEFTMICGAIN,
MAX9867_RIGHTMICGAIN,
0, 15, 1, max9860_capture_tlv),
SOC_DOUBLE_R_TLV("Mic Volume", MAX9867_LEFTMICGAIN,
MAX9867_RIGHTMICGAIN, 0, 31, 1, max9860_mic_tlv),
SOC_DOUBLE_R_TLV("Mic Boost Volume", MAX9867_LEFTMICGAIN,
MAX9867_RIGHTMICGAIN, 5, 3, 0, max98088_micboost_tlv),
SOC_ENUM("Digital Sidetone Src", max9867_sidetone),
SOC_SINGLE("Sidetone Volume", MAX9867_DACGAIN, 0, 31, 1),
SOC_SINGLE("DAC Volume", MAX9867_DACLEVEL, 4, 3, 0),
SOC_SINGLE("DAC Attenuation", MAX9867_DACLEVEL, 0, 15, 1),
SOC_SINGLE_TLV("ADC Left Volume", MAX9867_ADCLEVEL,
4, 15, 1, max9860_adc_left_tlv),
SOC_SINGLE_TLV("ADC Right Volume", MAX9867_ADCLEVEL,
0, 15, 1, max9860_adc_right_tlv),
SOC_ENUM("Speaker Mode", max9867_spkmode),
SOC_SINGLE("Volume Smoothing Switch", MAX9867_MODECONFIG, 6, 1, 0),
SOC_SINGLE("ZCD Switch", MAX9867_MODECONFIG, 5, 1, 0),
SOC_ENUM("DSP Filter", max9867_filter),
};
static const char *const max9867_mux[] = {"None", "Mic", "Line", "Mic_Line"};
static SOC_ENUM_SINGLE_DECL(max9867_mux_enum,
MAX9867_INPUTCONFIG, MAX9867_INPUT_SHIFT,
max9867_mux);
static const struct snd_kcontrol_new max9867_dapm_mux_controls =
SOC_DAPM_ENUM("Route", max9867_mux_enum);
static const struct snd_kcontrol_new max9867_left_dapm_control =
SOC_DAPM_SINGLE("Switch", MAX9867_PWRMAN, 6, 1, 0);
static const struct snd_kcontrol_new max9867_right_dapm_control =
SOC_DAPM_SINGLE("Switch", MAX9867_PWRMAN, 5, 1, 0);
static const struct snd_kcontrol_new max9867_line_dapm_control =
SOC_DAPM_SINGLE("Switch", MAX9867_LEFTLINELVL, 6, 1, 1);
static const struct snd_soc_dapm_widget max9867_dapm_widgets[] = {
SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("Left DAC", NULL, MAX9867_PWRMAN, 3, 0),
SND_SOC_DAPM_DAC("Right DAC", NULL, MAX9867_PWRMAN, 2, 0),
SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("HPOUT"),
SND_SOC_DAPM_AIF_IN("DAI_IN", "HiFi Capture", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_ADC("Left ADC", "HiFi Capture", MAX9867_PWRMAN, 1, 0),
SND_SOC_DAPM_ADC("Right ADC", "HiFi Capture", MAX9867_PWRMAN, 0, 0),
SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
&max9867_dapm_mux_controls),
SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_SWITCH("Left Line", MAX9867_LEFTLINELVL, 6, 1,
&max9867_left_dapm_control),
SND_SOC_DAPM_SWITCH("Right Line", MAX9867_RIGTHLINELVL, 6, 1,
&max9867_right_dapm_control),
SND_SOC_DAPM_SWITCH("Line Mixer", SND_SOC_NOPM, 0, 0,
&max9867_line_dapm_control),
SND_SOC_DAPM_INPUT("LINE_IN"),
};
static const struct snd_soc_dapm_route max9867_audio_map[] = {
{"Left DAC", NULL, "DAI_OUT"},
{"Right DAC", NULL, "DAI_OUT"},
{"Output Mixer", NULL, "Left DAC"},
{"Output Mixer", NULL, "Right DAC"},
{"HPOUT", NULL, "Output Mixer"},
{"Left ADC", NULL, "DAI_IN"},
{"Right ADC", NULL, "DAI_IN"},
{"Input Mixer", NULL, "Left ADC"},
{"Input Mixer", NULL, "Right ADC"},
{"Input Mux", "Line", "Input Mixer"},
{"Input Mux", "Mic", "Input Mixer"},
{"Input Mux", "Mic_Line", "Input Mixer"},
{"Right Line", "Switch", "Input Mux"},
{"Left Line", "Switch", "Input Mux"},
{"LINE_IN", NULL, "Left Line"},
{"LINE_IN", NULL, "Right Line"},
};
enum rates {
pcm_rate_8, pcm_rate_16, pcm_rate_24,
pcm_rate_32, pcm_rate_44,
pcm_rate_48, max_pcm_rate,
};
struct ni_div_rates {
u32 mclk;
u16 ni[max_pcm_rate];
} ni_div[] = {
{11289600, {0x116A, 0x22D4, 0x343F, 0x45A9, 0x6000, 0x687D} },
{12000000, {0x1062, 0x20C5, 0x3127, 0x4189, 0x5A51, 0x624E} },
{12288000, {0x1000, 0x2000, 0x3000, 0x4000, 0x5833, 0x6000} },
{13000000, {0x0F20, 0x1E3F, 0x2D5F, 0x3C7F, 0x535F, 0x5ABE} },
{19200000, {0x0A3D, 0x147B, 0x1EB8, 0x28F6, 0x3873, 0x3D71} },
{24000000, {0x1062, 0x20C5, 0x1893, 0x4189, 0x5A51, 0x624E} },
{26000000, {0x0F20, 0x1E3F, 0x16AF, 0x3C7F, 0x535F, 0x5ABE} },
{27000000, {0x0E90, 0x1D21, 0x15D8, 0x3A41, 0x5048, 0x5762} },
};
static inline int get_ni_value(int mclk, int rate)
{
int i, ret = 0;
/* find the closest rate index*/
for (i = 0; i < ARRAY_SIZE(ni_div); i++) {
if (ni_div[i].mclk >= mclk)
break;
}
if (i == ARRAY_SIZE(ni_div))
return -EINVAL;
switch (rate) {
case 8000:
return ni_div[i].ni[pcm_rate_8];
case 16000:
return ni_div[i].ni[pcm_rate_16];
case 32000:
return ni_div[i].ni[pcm_rate_32];
case 44100:
return ni_div[i].ni[pcm_rate_44];
case 48000:
return ni_div[i].ni[pcm_rate_48];
default:
pr_err("%s wrong rate %d\n", __func__, rate);
ret = -EINVAL;
}
return ret;
}
static int max9867_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec);
unsigned int ni_h, ni_l;
int value;
value = get_ni_value(max9867->sysclk, params_rate(params));
if (value < 0)
return value;
ni_h = (0xFF00 & value) >> 8;
ni_l = 0x00FF & value;
/* set up the ni value */
regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH,
MAX9867_NI_HIGH_MASK, ni_h);
regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW,
MAX9867_NI_LOW_MASK, ni_l);
if (!max9867->master) {
/*
* digital pll locks on to any externally supplied LRCLK signal
* and also enable rapid lock mode.
*/
regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW,
MAX9867_RAPID_LOCK, MAX9867_RAPID_LOCK);
regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH,
MAX9867_PLL, MAX9867_PLL);
} else {
unsigned long int bclk_rate, pclk_bclk_ratio;
int bclk_value;
bclk_rate = params_rate(params) * 2 * params_width(params);
pclk_bclk_ratio = max9867->pclk/bclk_rate;
switch (params_width(params)) {
case 8:
case 16:
switch (pclk_bclk_ratio) {
case 2:
bclk_value = MAX9867_IFC1B_PCLK_2;
break;
case 4:
bclk_value = MAX9867_IFC1B_PCLK_4;
break;
case 8:
bclk_value = MAX9867_IFC1B_PCLK_8;
break;
case 16:
bclk_value = MAX9867_IFC1B_PCLK_16;
break;
default:
dev_err(codec->dev,
"unsupported sampling rate\n");
return -EINVAL;
}
break;
case 24:
bclk_value = MAX9867_IFC1B_24BIT;
break;
case 32:
bclk_value = MAX9867_IFC1B_32BIT;
break;
default:
dev_err(codec->dev, "unsupported sampling rate\n");
return -EINVAL;
}
regmap_update_bits(max9867->regmap, MAX9867_IFC1B,
MAX9867_IFC1B_BCLK_MASK, bclk_value);
}
return 0;
}
static int max9867_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec);
regmap_update_bits(max9867->regmap, MAX9867_PWRMAN,
MAX9867_SHTDOWN_MASK, MAX9867_SHTDOWN_MASK);
return 0;
}
static int max9867_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec);
if (mute)
regmap_update_bits(max9867->regmap, MAX9867_DACLEVEL,
MAX9867_DAC_MUTE_MASK, MAX9867_DAC_MUTE_MASK);
else
regmap_update_bits(max9867->regmap, MAX9867_DACLEVEL,
MAX9867_DAC_MUTE_MASK, 0);
return 0;
}
static int max9867_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec);
int value = 0;
/* Set the prescaler based on the master clock frequency*/
if (freq >= 10000000 && freq <= 20000000) {
value |= MAX9867_PSCLK_10_20;
max9867->pclk = freq;
} else if (freq >= 20000000 && freq <= 40000000) {
value |= MAX9867_PSCLK_20_40;
max9867->pclk = freq/2;
} else if (freq >= 40000000 && freq <= 60000000) {
value |= MAX9867_PSCLK_40_60;
max9867->pclk = freq/4;
} else {
pr_err("bad clock frequency %d", freq);
return -EINVAL;
}
value = value << MAX9867_PSCLK_SHIFT;
max9867->sysclk = freq;
/* exact integer mode is not supported */
value &= ~MAX9867_FREQ_MASK;
regmap_update_bits(max9867->regmap, MAX9867_SYSCLK,
MAX9867_PSCLK_MASK, value);
return 0;
}
static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec);
u8 iface1A = 0, iface1B = 0;
int ret;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
max9867->master = 1;
iface1A |= MAX9867_MASTER;
break;
case SND_SOC_DAIFMT_CBS_CFS:
max9867->master = 0;
iface1A &= ~MAX9867_MASTER;
break;
default:
return -EINVAL;
}
/* for i2s compatible mode */
iface1A |= MAX9867_I2S_DLY;
/* SDOUT goes to hiz state after all data is transferred */
iface1A |= MAX9867_SDOUT_HIZ;
/* Clock inversion bits, BCI and WCI */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_IB_IF:
iface1A |= MAX9867_WCI_MODE | MAX9867_BCI_MODE;
break;
case SND_SOC_DAIFMT_IB_NF:
iface1A |= MAX9867_BCI_MODE;
break;
case SND_SOC_DAIFMT_NB_IF:
iface1A |= MAX9867_WCI_MODE;
break;
default:
return -EINVAL;
}
ret = regmap_write(max9867->regmap, MAX9867_IFC1A, iface1A);
ret = regmap_write(max9867->regmap, MAX9867_IFC1B, iface1B);
return 0;
}
static struct snd_soc_dai_ops max9867_dai_ops = {
.set_fmt = max9867_dai_set_fmt,
.set_sysclk = max9867_set_dai_sysclk,
.prepare = max9867_prepare,
.digital_mute = max9867_mute,
.hw_params = max9867_dai_hw_params,
};
#define MAX9867_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
#define MAX9867_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
static struct snd_soc_dai_driver max9867_dai[] = {
{
.name = "max9867-aif1",
.playback = {
.stream_name = "HiFi Playback",
.channels_min = 1,
.channels_max = 2,
.rates = MAX9867_RATES,
.formats = MAX9867_FORMATS,
},
.capture = {
.stream_name = "HiFi Capture",
.channels_min = 1,
.channels_max = 2,
.rates = MAX9867_RATES,
.formats = MAX9867_FORMATS,
},
.ops = &max9867_dai_ops,
}
};
#ifdef CONFIG_PM_SLEEP
static int max9867_suspend(struct device *dev)
{
struct max9867_priv *max9867 = dev_get_drvdata(dev);
/* Drop down to power saving mode when system is suspended */
regmap_update_bits(max9867->regmap, MAX9867_PWRMAN,
MAX9867_SHTDOWN_MASK, ~MAX9867_SHTDOWN_MASK);
return 0;
}
static int max9867_resume(struct device *dev)
{
struct max9867_priv *max9867 = dev_get_drvdata(dev);
regmap_update_bits(max9867->regmap, MAX9867_PWRMAN,
MAX9867_SHTDOWN_MASK, MAX9867_SHTDOWN_MASK);
return 0;
}
#endif
static int max9867_probe(struct snd_soc_codec *codec)
{
struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec);
dev_dbg(codec->dev, "max98090_probe\n");
max9867->codec = codec;
return 0;
}
static struct snd_soc_codec_driver max9867_codec = {
.probe = max9867_probe,
.controls = max9867_snd_controls,
.num_controls = ARRAY_SIZE(max9867_snd_controls),
.dapm_routes = max9867_audio_map,
.num_dapm_routes = ARRAY_SIZE(max9867_audio_map),
.dapm_widgets = max9867_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(max9867_dapm_widgets),
};
static bool max9867_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case MAX9867_STATUS:
case MAX9867_JACKSTATUS:
case MAX9867_AUXHIGH:
case MAX9867_AUXLOW:
return true;
default:
return false;
}
}
static const struct reg_default max9867_reg[] = {
{ 0x04, 0x00 },
{ 0x05, 0x00 },
{ 0x06, 0x00 },
{ 0x07, 0x00 },
{ 0x08, 0x00 },
{ 0x09, 0x00 },
{ 0x0A, 0x00 },
{ 0x0B, 0x00 },
{ 0x0C, 0x00 },
{ 0x0D, 0x00 },
{ 0x0E, 0x00 },
{ 0x0F, 0x00 },
{ 0x10, 0x00 },
{ 0x11, 0x00 },
{ 0x12, 0x00 },
{ 0x13, 0x00 },
{ 0x14, 0x00 },
{ 0x15, 0x00 },
{ 0x16, 0x00 },
{ 0x17, 0x00 },
};
static const struct regmap_config max9867_regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = MAX9867_REVISION,
.reg_defaults = max9867_reg,
.num_reg_defaults = ARRAY_SIZE(max9867_reg),
.volatile_reg = max9867_volatile_register,
.cache_type = REGCACHE_RBTREE,
};
static int max9867_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct max9867_priv *max9867;
int ret = 0, reg;
max9867 = devm_kzalloc(&i2c->dev,
sizeof(*max9867), GFP_KERNEL);
if (!max9867)
return -ENOMEM;
i2c_set_clientdata(i2c, max9867);
max9867->regmap = devm_regmap_init_i2c(i2c, &max9867_regmap);
if (IS_ERR(max9867->regmap)) {
ret = PTR_ERR(max9867->regmap);
dev_err(&i2c->dev,
"Failed to allocate regmap: %d\n", ret);
return ret;
}
ret = regmap_read(max9867->regmap,
MAX9867_REVISION, &reg);
if (ret < 0) {
dev_err(&i2c->dev, "Failed to read: %d\n", ret);
return ret;
}
dev_info(&i2c->dev, "device revision: %x\n", reg);
ret = snd_soc_register_codec(&i2c->dev, &max9867_codec,
max9867_dai, ARRAY_SIZE(max9867_dai));
if (ret < 0) {
dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
return ret;
}
return ret;
}
static int max9867_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
return 0;
}
static const struct i2c_device_id max9867_i2c_id[] = {
{ "max9867", 0 },
{ }
};
static const struct of_device_id max9867_of_match[] = {
{ .compatible = "maxim,max9867", },
{ }
};
MODULE_DEVICE_TABLE(i2c, max9867_i2c_id);
static const struct dev_pm_ops max9867_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(max9867_suspend, max9867_resume)
};
static struct i2c_driver max9867_i2c_driver = {
.driver = {
.name = "max9867",
.of_match_table = of_match_ptr(max9867_of_match),
.pm = &max9867_pm_ops,
},
.probe = max9867_i2c_probe,
.remove = max9867_i2c_remove,
.id_table = max9867_i2c_id,
};
module_i2c_driver(max9867_i2c_driver);
MODULE_AUTHOR("anish kumar <yesanishhere@gmail.com>");
MODULE_DESCRIPTION("ALSA SoC MAX9867 driver");
MODULE_LICENSE("GPL");

83
sound/soc/codecs/max9867.h Executable file
View File

@ -0,0 +1,83 @@
/*
* max9867.h -- MAX9867 ALSA SoC Audio driver
*
* Copyright 2013-2015 Maxim Integrated Products
*
* 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 _MAX9867_H
#define _MAX9867_H
/* MAX9867 register space */
#define MAX9867_STATUS 0x00
#define MAX9867_JACKSTATUS 0x01
#define MAX9867_AUXHIGH 0x02
#define MAX9867_AUXLOW 0x03
#define MAX9867_INTEN 0x04
#define MAX9867_SYSCLK 0x05
#define MAX9867_FREQ_MASK 0xF
#define MAX9867_PSCLK_SHIFT 0x4
#define MAX9867_PSCLK_WIDTH 0x2
#define MAX9867_PSCLK_MASK (0x03<<MAX9867_PSCLK_SHIFT)
#define MAX9867_PSCLK_10_20 0x1
#define MAX9867_PSCLK_20_40 0x2
#define MAX9867_PSCLK_40_60 0x3
#define MAX9867_AUDIOCLKHIGH 0x06
#define MAX9867_NI_HIGH_WIDTH 0x7
#define MAX9867_NI_HIGH_MASK 0x7F
#define MAX9867_NI_LOW_MASK 0x7F
#define MAX9867_NI_LOW_SHIFT 0x1
#define MAX9867_PLL (1<<7)
#define MAX9867_AUDIOCLKLOW 0x07
#define MAX9867_RAPID_LOCK 0x01
#define MAX9867_IFC1A 0x08
#define MAX9867_MASTER (1<<7)
#define MAX9867_I2S_DLY (1<<4)
#define MAX9867_SDOUT_HIZ (1<<3)
#define MAX9867_TDM_MODE (1<<2)
#define MAX9867_WCI_MODE (1<<6)
#define MAX9867_BCI_MODE (1<<5)
#define MAX9867_IFC1B 0x09
#define MAX9867_IFC1B_BCLK_MASK 7
#define MAX9867_IFC1B_32BIT 0x01
#define MAX9867_IFC1B_24BIT 0x02
#define MAX9867_IFC1B_PCLK_2 4
#define MAX9867_IFC1B_PCLK_4 5
#define MAX9867_IFC1B_PCLK_8 6
#define MAX9867_IFC1B_PCLK_16 7
#define MAX9867_CODECFLTR 0x0a
#define MAX9867_DACGAIN 0x0b
#define MAX9867_DACLEVEL 0x0c
#define MAX9867_DAC_MUTE_SHIFT 0x6
#define MAX9867_DAC_MUTE_WIDTH 0x1
#define MAX9867_DAC_MUTE_MASK (0x1<<MAX9867_DAC_MUTE_SHIFT)
#define MAX9867_ADCLEVEL 0x0d
#define MAX9867_LEFTLINELVL 0x0e
#define MAX9867_RIGTHLINELVL 0x0f
#define MAX9867_LEFTVOL 0x10
#define MAX9867_RIGHTVOL 0x11
#define MAX9867_LEFTMICGAIN 0x12
#define MAX9867_RIGHTMICGAIN 0x13
#define MAX9867_INPUTCONFIG 0x14
#define MAX9867_INPUT_SHIFT 0x6
#define MAX9867_MICCONFIG 0x15
#define MAX9867_MODECONFIG 0x16
#define MAX9867_PWRMAN 0x17
#define MAX9867_SHTDOWN_MASK (1<<7)
#define MAX9867_REVISION 0xff
#define MAX9867_CACHEREGNUM 10
/* codec private data */
struct max9867_priv {
struct regmap *regmap;
struct snd_soc_codec *codec;
unsigned int sysclk;
unsigned int pclk;
unsigned int master;
};
#endif

606
sound/soc/codecs/max98926.c Normal file
View File

@ -0,0 +1,606 @@
/*
* max98926.c -- ALSA SoC MAX98926 driver
* Copyright 2013-15 Maxim Integrated Products
* 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/delay.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include "max98926.h"
static const char * const max98926_boost_voltage_txt[] = {
"8.5V", "8.25V", "8.0V", "7.75V", "7.5V", "7.25V", "7.0V", "6.75V",
"6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V"
};
static const char * const max98926_boost_current_txt[] = {
"0.6", "0.8", "1.0", "1.2", "1.4", "1.6", "1.8", "2.0",
"2.2", "2.4", "2.6", "2.8", "3.2", "3.6", "4.0", "4.4"
};
static const char *const max98926_dai_txt[] = {
"Left", "Right", "LeftRight", "LeftRightDiv2",
};
static const char *const max98926_pdm_ch_text[] = {
"Current", "Voltage",
};
static const char *const max98926_hpf_cutoff_txt[] = {
"Disable", "DC Block", "100Hz",
"200Hz", "400Hz", "800Hz",
};
static const struct reg_default max98926_reg[] = {
{ 0x0B, 0x00 }, /* IRQ Enable0 */
{ 0x0C, 0x00 }, /* IRQ Enable1 */
{ 0x0D, 0x00 }, /* IRQ Enable2 */
{ 0x0E, 0x00 }, /* IRQ Clear0 */
{ 0x0F, 0x00 }, /* IRQ Clear1 */
{ 0x10, 0x00 }, /* IRQ Clear2 */
{ 0x11, 0xC0 }, /* Map0 */
{ 0x12, 0x00 }, /* Map1 */
{ 0x13, 0x00 }, /* Map2 */
{ 0x14, 0xF0 }, /* Map3 */
{ 0x15, 0x00 }, /* Map4 */
{ 0x16, 0xAB }, /* Map5 */
{ 0x17, 0x89 }, /* Map6 */
{ 0x18, 0x00 }, /* Map7 */
{ 0x19, 0x00 }, /* Map8 */
{ 0x1A, 0x04 }, /* DAI Clock Mode 1 */
{ 0x1B, 0x00 }, /* DAI Clock Mode 2 */
{ 0x1C, 0x00 }, /* DAI Clock Divider Denominator MSBs */
{ 0x1D, 0x00 }, /* DAI Clock Divider Denominator LSBs */
{ 0x1E, 0xF0 }, /* DAI Clock Divider Numerator MSBs */
{ 0x1F, 0x00 }, /* DAI Clock Divider Numerator LSBs */
{ 0x20, 0x50 }, /* Format */
{ 0x21, 0x00 }, /* TDM Slot Select */
{ 0x22, 0x00 }, /* DOUT Configuration VMON */
{ 0x23, 0x00 }, /* DOUT Configuration IMON */
{ 0x24, 0x00 }, /* DOUT Configuration VBAT */
{ 0x25, 0x00 }, /* DOUT Configuration VBST */
{ 0x26, 0x00 }, /* DOUT Configuration FLAG */
{ 0x27, 0xFF }, /* DOUT HiZ Configuration 1 */
{ 0x28, 0xFF }, /* DOUT HiZ Configuration 2 */
{ 0x29, 0xFF }, /* DOUT HiZ Configuration 3 */
{ 0x2A, 0xFF }, /* DOUT HiZ Configuration 4 */
{ 0x2B, 0x02 }, /* DOUT Drive Strength */
{ 0x2C, 0x90 }, /* Filters */
{ 0x2D, 0x00 }, /* Gain */
{ 0x2E, 0x02 }, /* Gain Ramping */
{ 0x2F, 0x00 }, /* Speaker Amplifier */
{ 0x30, 0x0A }, /* Threshold */
{ 0x31, 0x00 }, /* ALC Attack */
{ 0x32, 0x80 }, /* ALC Atten and Release */
{ 0x33, 0x00 }, /* ALC Infinite Hold Release */
{ 0x34, 0x92 }, /* ALC Configuration */
{ 0x35, 0x01 }, /* Boost Converter */
{ 0x36, 0x00 }, /* Block Enable */
{ 0x37, 0x00 }, /* Configuration */
{ 0x38, 0x00 }, /* Global Enable */
{ 0x3A, 0x00 }, /* Boost Limiter */
};
static const struct soc_enum max98926_voltage_enum[] = {
SOC_ENUM_SINGLE(MAX98926_DAI_CLK_DIV_N_LSBS, 0,
ARRAY_SIZE(max98926_pdm_ch_text),
max98926_pdm_ch_text),
};
static const struct snd_kcontrol_new max98926_voltage_control =
SOC_DAPM_ENUM("Route", max98926_voltage_enum);
static const struct soc_enum max98926_current_enum[] = {
SOC_ENUM_SINGLE(MAX98926_DAI_CLK_DIV_N_LSBS,
MAX98926_PDM_SOURCE_1_SHIFT,
ARRAY_SIZE(max98926_pdm_ch_text),
max98926_pdm_ch_text),
};
static const struct snd_kcontrol_new max98926_current_control =
SOC_DAPM_ENUM("Route", max98926_current_enum);
static const struct snd_kcontrol_new max98926_mixer_controls[] = {
SOC_DAPM_SINGLE("PCM Single Switch", MAX98926_SPK_AMP,
MAX98926_INSELECT_MODE_SHIFT, 0, 0),
SOC_DAPM_SINGLE("PDM Single Switch", MAX98926_SPK_AMP,
MAX98926_INSELECT_MODE_SHIFT, 1, 0),
};
static const struct snd_kcontrol_new max98926_dai_controls[] = {
SOC_DAPM_SINGLE("Left", MAX98926_GAIN,
MAX98926_DAC_IN_SEL_SHIFT, 0, 0),
SOC_DAPM_SINGLE("Right", MAX98926_GAIN,
MAX98926_DAC_IN_SEL_SHIFT, 1, 0),
SOC_DAPM_SINGLE("LeftRight", MAX98926_GAIN,
MAX98926_DAC_IN_SEL_SHIFT, 2, 0),
SOC_DAPM_SINGLE("(Left+Right)/2 Switch", MAX98926_GAIN,
MAX98926_DAC_IN_SEL_SHIFT, 3, 0),
};
static const struct snd_soc_dapm_widget max98926_dapm_widgets[] = {
SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0,
SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("Amp Enable", NULL, MAX98926_BLOCK_ENABLE,
MAX98926_SPK_EN_SHIFT, 0),
SND_SOC_DAPM_SUPPLY("Global Enable", MAX98926_GLOBAL_ENABLE,
MAX98926_EN_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("VI Enable", MAX98926_BLOCK_ENABLE,
MAX98926_ADC_IMON_EN_WIDTH |
MAX98926_ADC_VMON_EN_SHIFT,
0, NULL, 0),
SND_SOC_DAPM_PGA("BST Enable", MAX98926_BLOCK_ENABLE,
MAX98926_BST_EN_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("BE_OUT"),
SND_SOC_DAPM_MIXER("PCM Sel", MAX98926_SPK_AMP,
MAX98926_INSELECT_MODE_SHIFT, 0,
&max98926_mixer_controls[0],
ARRAY_SIZE(max98926_mixer_controls)),
SND_SOC_DAPM_MIXER("DAI Sel",
MAX98926_GAIN, MAX98926_DAC_IN_SEL_SHIFT, 0,
&max98926_dai_controls[0],
ARRAY_SIZE(max98926_dai_controls)),
SND_SOC_DAPM_MUX("PDM CH1 Source",
MAX98926_DAI_CLK_DIV_N_LSBS,
MAX98926_PDM_CURRENT_SHIFT,
0, &max98926_current_control),
SND_SOC_DAPM_MUX("PDM CH0 Source",
MAX98926_DAI_CLK_DIV_N_LSBS,
MAX98926_PDM_VOLTAGE_SHIFT,
0, &max98926_voltage_control),
};
static const struct snd_soc_dapm_route max98926_audio_map[] = {
{"VI Enable", NULL, "DAI_OUT"},
{"DAI Sel", "Left", "VI Enable"},
{"DAI Sel", "Right", "VI Enable"},
{"DAI Sel", "LeftRight", "VI Enable"},
{"DAI Sel", "LeftRightDiv2", "VI Enable"},
{"PCM Sel", "PCM", "DAI Sel"},
{"PDM CH1 Source", "Current", "DAI_OUT"},
{"PDM CH1 Source", "Voltage", "DAI_OUT"},
{"PDM CH0 Source", "Current", "DAI_OUT"},
{"PDM CH0 Source", "Voltage", "DAI_OUT"},
{"PCM Sel", "Analog", "PDM CH1 Source"},
{"PCM Sel", "Analog", "PDM CH0 Source"},
{"Amp Enable", NULL, "PCM Sel"},
{"BST Enable", NULL, "Amp Enable"},
{"BE_OUT", NULL, "BST Enable"},
};
static bool max98926_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case MAX98926_VBAT_DATA:
case MAX98926_VBST_DATA:
case MAX98926_LIVE_STATUS0:
case MAX98926_LIVE_STATUS1:
case MAX98926_LIVE_STATUS2:
case MAX98926_STATE0:
case MAX98926_STATE1:
case MAX98926_STATE2:
case MAX98926_FLAG0:
case MAX98926_FLAG1:
case MAX98926_FLAG2:
case MAX98926_VERSION:
return true;
default:
return false;
}
}
static bool max98926_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case MAX98926_IRQ_CLEAR0:
case MAX98926_IRQ_CLEAR1:
case MAX98926_IRQ_CLEAR2:
case MAX98926_ALC_HOLD_RLS:
return false;
default:
return true;
}
};
DECLARE_TLV_DB_SCALE(max98926_spk_tlv, -600, 100, 0);
DECLARE_TLV_DB_RANGE(max98926_current_tlv,
0, 11, TLV_DB_SCALE_ITEM(20, 20, 0),
12, 15, TLV_DB_SCALE_ITEM(320, 40, 0),
);
static SOC_ENUM_SINGLE_DECL(max98926_dac_hpf_cutoff,
MAX98926_FILTERS, MAX98926_DAC_HPF_SHIFT,
max98926_hpf_cutoff_txt);
static SOC_ENUM_SINGLE_DECL(max98926_boost_voltage,
MAX98926_CONFIGURATION, MAX98926_BST_VOUT_SHIFT,
max98926_boost_voltage_txt);
static const struct snd_kcontrol_new max98926_snd_controls[] = {
SOC_SINGLE_TLV("Speaker Volume", MAX98926_GAIN,
MAX98926_SPK_GAIN_SHIFT,
(1<<MAX98926_SPK_GAIN_WIDTH)-1, 0,
max98926_spk_tlv),
SOC_SINGLE("Ramp Switch", MAX98926_GAIN_RAMPING,
MAX98926_SPK_RMP_EN_SHIFT, 1, 0),
SOC_SINGLE("ZCD Switch", MAX98926_GAIN_RAMPING,
MAX98926_SPK_ZCD_EN_SHIFT, 1, 0),
SOC_SINGLE("ALC Switch", MAX98926_THRESHOLD,
MAX98926_ALC_EN_SHIFT, 1, 0),
SOC_SINGLE("ALC Threshold", MAX98926_THRESHOLD,
MAX98926_ALC_TH_SHIFT,
(1<<MAX98926_ALC_TH_WIDTH)-1, 0),
SOC_ENUM("Boost Output Voltage", max98926_boost_voltage),
SOC_SINGLE_TLV("Boost Current Limit", MAX98926_BOOST_LIMITER,
MAX98926_BST_ILIM_SHIFT,
(1<<MAX98926_BST_ILIM_SHIFT)-1, 0,
max98926_current_tlv),
SOC_ENUM("DAC HPF Cutoff", max98926_dac_hpf_cutoff),
SOC_DOUBLE("PDM Channel One", MAX98926_DAI_CLK_DIV_N_LSBS,
MAX98926_PDM_CHANNEL_1_SHIFT,
MAX98926_PDM_CHANNEL_1_HIZ, 1, 0),
SOC_DOUBLE("PDM Channel Zero", MAX98926_DAI_CLK_DIV_N_LSBS,
MAX98926_PDM_CHANNEL_0_SHIFT,
MAX98926_PDM_CHANNEL_0_HIZ, 1, 0),
};
static const struct {
int rate;
int sr;
} rate_table[] = {
{
.rate = 8000,
.sr = 0,
},
{
.rate = 11025,
.sr = 1,
},
{
.rate = 12000,
.sr = 2,
},
{
.rate = 16000,
.sr = 3,
},
{
.rate = 22050,
.sr = 4,
},
{
.rate = 24000,
.sr = 5,
},
{
.rate = 32000,
.sr = 6,
},
{
.rate = 44100,
.sr = 7,
},
{
.rate = 48000,
.sr = 8,
},
};
static void max98926_set_sense_data(struct max98926_priv *max98926)
{
regmap_update_bits(max98926->regmap,
MAX98926_DOUT_CFG_VMON,
MAX98926_DAI_VMON_EN_MASK,
MAX98926_DAI_VMON_EN_MASK);
regmap_update_bits(max98926->regmap,
MAX98926_DOUT_CFG_IMON,
MAX98926_DAI_IMON_EN_MASK,
MAX98926_DAI_IMON_EN_MASK);
if (!max98926->interleave_mode) {
/* set VMON slots */
regmap_update_bits(max98926->regmap,
MAX98926_DOUT_CFG_VMON,
MAX98926_DAI_VMON_SLOT_MASK,
max98926->v_slot);
/* set IMON slots */
regmap_update_bits(max98926->regmap,
MAX98926_DOUT_CFG_IMON,
MAX98926_DAI_IMON_SLOT_MASK,
max98926->i_slot);
} else {
/* enable interleave mode */
regmap_update_bits(max98926->regmap,
MAX98926_FORMAT,
MAX98926_DAI_INTERLEAVE_MASK,
MAX98926_DAI_INTERLEAVE_MASK);
/* set interleave slots */
regmap_update_bits(max98926->regmap,
MAX98926_DOUT_CFG_VBAT,
MAX98926_DAI_INTERLEAVE_SLOT_MASK,
max98926->v_slot);
}
}
static int max98926_dai_set_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct max98926_priv *max98926 = snd_soc_codec_get_drvdata(codec);
unsigned int invert = 0;
dev_dbg(codec->dev, "%s: fmt 0x%08X\n", __func__, fmt);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
max98926_set_sense_data(max98926);
break;
default:
dev_err(codec->dev, "DAI clock mode unsupported");
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_NB_IF:
invert = MAX98926_DAI_WCI_MASK;
break;
case SND_SOC_DAIFMT_IB_NF:
invert = MAX98926_DAI_BCI_MASK;
break;
case SND_SOC_DAIFMT_IB_IF:
invert = MAX98926_DAI_BCI_MASK | MAX98926_DAI_WCI_MASK;
break;
default:
dev_err(codec->dev, "DAI invert mode unsupported");
return -EINVAL;
}
regmap_write(max98926->regmap,
MAX98926_FORMAT, MAX98926_DAI_DLY_MASK);
regmap_update_bits(max98926->regmap, MAX98926_FORMAT,
MAX98926_DAI_BCI_MASK, invert);
return 0;
}
static int max98926_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
int dai_sr = -EINVAL;
int rate = params_rate(params), i;
struct snd_soc_codec *codec = dai->codec;
struct max98926_priv *max98926 = snd_soc_codec_get_drvdata(codec);
int blr_clk_ratio;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
regmap_update_bits(max98926->regmap,
MAX98926_FORMAT,
MAX98926_DAI_CHANSZ_MASK,
MAX98926_DAI_CHANSZ_16);
max98926->ch_size = 16;
break;
case SNDRV_PCM_FORMAT_S24_LE:
regmap_update_bits(max98926->regmap,
MAX98926_FORMAT,
MAX98926_DAI_CHANSZ_MASK,
MAX98926_DAI_CHANSZ_24);
max98926->ch_size = 24;
break;
case SNDRV_PCM_FORMAT_S32_LE:
regmap_update_bits(max98926->regmap,
MAX98926_FORMAT,
MAX98926_DAI_CHANSZ_MASK,
MAX98926_DAI_CHANSZ_32);
max98926->ch_size = 32;
break;
default:
dev_dbg(codec->dev, "format unsupported %d",
params_format(params));
return -EINVAL;
}
/* BCLK/LRCLK ratio calculation */
blr_clk_ratio = params_channels(params) * max98926->ch_size;
switch (blr_clk_ratio) {
case 32:
regmap_update_bits(max98926->regmap,
MAX98926_DAI_CLK_MODE2,
MAX98926_DAI_BSEL_MASK,
MAX98926_DAI_BSEL_32);
break;
case 48:
regmap_update_bits(max98926->regmap,
MAX98926_DAI_CLK_MODE2,
MAX98926_DAI_BSEL_MASK,
MAX98926_DAI_BSEL_48);
break;
case 64:
regmap_update_bits(max98926->regmap,
MAX98926_DAI_CLK_MODE2,
MAX98926_DAI_BSEL_MASK,
MAX98926_DAI_BSEL_64);
break;
default:
return -EINVAL;
}
/* find the closest rate */
for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
if (rate_table[i].rate >= rate) {
dai_sr = rate_table[i].sr;
break;
}
}
if (dai_sr < 0)
return -EINVAL;
/* set DAI_SR to correct LRCLK frequency */
regmap_update_bits(max98926->regmap,
MAX98926_DAI_CLK_MODE2,
MAX98926_DAI_SR_MASK, dai_sr << MAX98926_DAI_SR_SHIFT);
return 0;
}
#define MAX98926_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_ops max98926_dai_ops = {
.set_fmt = max98926_dai_set_fmt,
.hw_params = max98926_dai_hw_params,
};
static struct snd_soc_dai_driver max98926_dai[] = {
{
.name = "max98926-aif1",
.playback = {
.stream_name = "HiFi Playback",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = MAX98926_FORMATS,
},
.capture = {
.stream_name = "HiFi Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = MAX98926_FORMATS,
},
.ops = &max98926_dai_ops,
}
};
static int max98926_probe(struct snd_soc_codec *codec)
{
struct max98926_priv *max98926 = snd_soc_codec_get_drvdata(codec);
max98926->codec = codec;
codec->control_data = max98926->regmap;
/* Hi-Z all the slots */
regmap_write(max98926->regmap, MAX98926_DOUT_HIZ_CFG4, 0xF0);
return 0;
}
static struct snd_soc_codec_driver soc_codec_dev_max98926 = {
.probe = max98926_probe,
.controls = max98926_snd_controls,
.num_controls = ARRAY_SIZE(max98926_snd_controls),
.dapm_routes = max98926_audio_map,
.num_dapm_routes = ARRAY_SIZE(max98926_audio_map),
.dapm_widgets = max98926_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(max98926_dapm_widgets),
};
static const struct regmap_config max98926_regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = MAX98926_VERSION,
.reg_defaults = max98926_reg,
.num_reg_defaults = ARRAY_SIZE(max98926_reg),
.volatile_reg = max98926_volatile_register,
.readable_reg = max98926_readable_register,
.cache_type = REGCACHE_RBTREE,
};
static int max98926_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
int ret, reg;
u32 value;
struct max98926_priv *max98926;
max98926 = devm_kzalloc(&i2c->dev,
sizeof(*max98926), GFP_KERNEL);
if (!max98926)
return -ENOMEM;
i2c_set_clientdata(i2c, max98926);
max98926->regmap = devm_regmap_init_i2c(i2c, &max98926_regmap);
if (IS_ERR(max98926->regmap)) {
ret = PTR_ERR(max98926->regmap);
dev_err(&i2c->dev,
"Failed to allocate regmap: %d\n", ret);
goto err_out;
}
if (of_property_read_bool(i2c->dev.of_node, "interleave-mode"))
max98926->interleave_mode = true;
if (!of_property_read_u32(i2c->dev.of_node, "vmon-slot-no", &value)) {
if (value > MAX98926_DAI_VMON_SLOT_1E_1F) {
dev_err(&i2c->dev, "vmon slot number is wrong:\n");
return -EINVAL;
}
max98926->v_slot = value;
}
if (!of_property_read_u32(i2c->dev.of_node, "imon-slot-no", &value)) {
if (value > MAX98926_DAI_IMON_SLOT_1E_1F) {
dev_err(&i2c->dev, "imon slot number is wrong:\n");
return -EINVAL;
}
max98926->i_slot = value;
}
ret = regmap_read(max98926->regmap,
MAX98926_VERSION, &reg);
if (ret < 0) {
dev_err(&i2c->dev, "Failed to read: %x\n", reg);
return ret;
}
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98926,
max98926_dai, ARRAY_SIZE(max98926_dai));
if (ret < 0)
dev_err(&i2c->dev,
"Failed to register codec: %d\n", ret);
dev_info(&i2c->dev, "device version: %x\n", reg);
err_out:
return ret;
}
static int max98926_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
return 0;
}
static const struct i2c_device_id max98926_i2c_id[] = {
{ "max98926", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max98926_i2c_id);
static const struct of_device_id max98926_of_match[] = {
{ .compatible = "maxim,max98926", },
{ }
};
MODULE_DEVICE_TABLE(of, max98926_of_match);
static struct i2c_driver max98926_i2c_driver = {
.driver = {
.name = "max98926",
.of_match_table = of_match_ptr(max98926_of_match),
.pm = NULL,
},
.probe = max98926_i2c_probe,
.remove = max98926_i2c_remove,
.id_table = max98926_i2c_id,
};
module_i2c_driver(max98926_i2c_driver)
MODULE_DESCRIPTION("ALSA SoC MAX98926 driver");
MODULE_AUTHOR("Anish kumar <anish.kumar@maximintegrated.com>");
MODULE_LICENSE("GPL");

848
sound/soc/codecs/max98926.h Normal file
View File

@ -0,0 +1,848 @@
/*
* max98926.h -- MAX98926 ALSA SoC Audio driver
* Copyright 2013-2015 Maxim Integrated Products
* 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 _MAX98926_H
#define _MAX98926_H
#define MAX98926_CHIP_VERSION 0x40
#define MAX98926_CHIP_VERSION1 0x50
#define MAX98926_VBAT_DATA 0x00
#define MAX98926_VBST_DATA 0x01
#define MAX98926_LIVE_STATUS0 0x02
#define MAX98926_LIVE_STATUS1 0x03
#define MAX98926_LIVE_STATUS2 0x04
#define MAX98926_STATE0 0x05
#define MAX98926_STATE1 0x06
#define MAX98926_STATE2 0x07
#define MAX98926_FLAG0 0x08
#define MAX98926_FLAG1 0x09
#define MAX98926_FLAG2 0x0A
#define MAX98926_IRQ_ENABLE0 0x0B
#define MAX98926_IRQ_ENABLE1 0x0C
#define MAX98926_IRQ_ENABLE2 0x0D
#define MAX98926_IRQ_CLEAR0 0x0E
#define MAX98926_IRQ_CLEAR1 0x0F
#define MAX98926_IRQ_CLEAR2 0x10
#define MAX98926_MAP0 0x11
#define MAX98926_MAP1 0x12
#define MAX98926_MAP2 0x13
#define MAX98926_MAP3 0x14
#define MAX98926_MAP4 0x15
#define MAX98926_MAP5 0x16
#define MAX98926_MAP6 0x17
#define MAX98926_MAP7 0x18
#define MAX98926_MAP8 0x19
#define MAX98926_DAI_CLK_MODE1 0x1A
#define MAX98926_DAI_CLK_MODE2 0x1B
#define MAX98926_DAI_CLK_DIV_M_MSBS 0x1C
#define MAX98926_DAI_CLK_DIV_M_LSBS 0x1D
#define MAX98926_DAI_CLK_DIV_N_MSBS 0x1E
#define MAX98926_DAI_CLK_DIV_N_LSBS 0x1F
#define MAX98926_FORMAT 0x20
#define MAX98926_TDM_SLOT_SELECT 0x21
#define MAX98926_DOUT_CFG_VMON 0x22
#define MAX98926_DOUT_CFG_IMON 0x23
#define MAX98926_DOUT_CFG_VBAT 0x24
#define MAX98926_DOUT_CFG_VBST 0x25
#define MAX98926_DOUT_CFG_FLAG 0x26
#define MAX98926_DOUT_HIZ_CFG1 0x27
#define MAX98926_DOUT_HIZ_CFG2 0x28
#define MAX98926_DOUT_HIZ_CFG3 0x29
#define MAX98926_DOUT_HIZ_CFG4 0x2A
#define MAX98926_DOUT_DRV_STRENGTH 0x2B
#define MAX98926_FILTERS 0x2C
#define MAX98926_GAIN 0x2D
#define MAX98926_GAIN_RAMPING 0x2E
#define MAX98926_SPK_AMP 0x2F
#define MAX98926_THRESHOLD 0x30
#define MAX98926_ALC_ATTACK 0x31
#define MAX98926_ALC_ATTEN_RLS 0x32
#define MAX98926_ALC_HOLD_RLS 0x33
#define MAX98926_ALC_CONFIGURATION 0x34
#define MAX98926_BOOST_CONVERTER 0x35
#define MAX98926_BLOCK_ENABLE 0x36
#define MAX98926_CONFIGURATION 0x37
#define MAX98926_GLOBAL_ENABLE 0x38
#define MAX98926_BOOST_LIMITER 0x3A
#define MAX98926_VERSION 0xFF
#define MAX98926_REG_CNT (MAX98926_R03A_BOOST_LIMITER+1)
#define MAX98926_PDM_CURRENT_MASK (1<<7)
#define MAX98926_PDM_CURRENT_SHIFT 7
#define MAX98926_PDM_VOLTAGE_MASK (1<<3)
#define MAX98926_PDM_VOLTAGE_SHIFT 3
#define MAX98926_PDM_CHANNEL_0_MASK (1<<2)
#define MAX98926_PDM_CHANNEL_0_SHIFT 2
#define MAX98926_PDM_CHANNEL_1_MASK (1<<6)
#define MAX98926_PDM_CHANNEL_1_SHIFT 6
#define MAX98926_PDM_CHANNEL_1_HIZ 5
#define MAX98926_PDM_CHANNEL_0_HIZ 1
#define MAX98926_PDM_SOURCE_0_SHIFT 0
#define MAX98926_PDM_SOURCE_0_MASK (1<<0)
#define MAX98926_PDM_SOURCE_1_MASK (1<<4)
#define MAX98926_PDM_SOURCE_1_SHIFT 4
/* MAX98926 Register Bit Fields */
/* MAX98926_R002_LIVE_STATUS0 */
#define MAX98926_THERMWARN_STATUS_MASK (1<<3)
#define MAX98926_THERMWARN_STATUS_SHIFT 3
#define MAX98926_THERMWARN_STATUS_WIDTH 1
#define MAX98926_THERMSHDN_STATUS_MASK (1<<1)
#define MAX98926_THERMSHDN_STATUS_SHIFT 1
#define MAX98926_THERMSHDN_STATUS_WIDTH 1
/* MAX98926_R003_LIVE_STATUS1 */
#define MAX98926_SPKCURNT_STATUS_MASK (1<<5)
#define MAX98926_SPKCURNT_STATUS_SHIFT 5
#define MAX98926_SPKCURNT_STATUS_WIDTH 1
#define MAX98926_WATCHFAIL_STATUS_MASK (1<<4)
#define MAX98926_WATCHFAIL_STATUS_SHIFT 4
#define MAX98926_WATCHFAIL_STATUS_WIDTH 1
#define MAX98926_ALCINFH_STATUS_MASK (1<<3)
#define MAX98926_ALCINFH_STATUS_SHIFT 3
#define MAX98926_ALCINFH_STATUS_WIDTH 1
#define MAX98926_ALCACT_STATUS_MASK (1<<2)
#define MAX98926_ALCACT_STATUS_SHIFT 2
#define MAX98926_ALCACT_STATUS_WIDTH 1
#define MAX98926_ALCMUT_STATUS_MASK (1<<1)
#define MAX98926_ALCMUT_STATUS_SHIFT 1
#define MAX98926_ALCMUT_STATUS_WIDTH 1
#define MAX98926_ACLP_STATUS_MASK (1<<0)
#define MAX98926_ACLP_STATUS_SHIFT 0
#define MAX98926_ACLP_STATUS_WIDTH 1
/* MAX98926_R004_LIVE_STATUS2 */
#define MAX98926_SLOTOVRN_STATUS_MASK (1<<6)
#define MAX98926_SLOTOVRN_STATUS_SHIFT 6
#define MAX98926_SLOTOVRN_STATUS_WIDTH 1
#define MAX98926_INVALSLOT_STATUS_MASK (1<<5)
#define MAX98926_INVALSLOT_STATUS_SHIFT 5
#define MAX98926_INVALSLOT_STATUS_WIDTH 1
#define MAX98926_SLOTCNFLT_STATUS_MASK (1<<4)
#define MAX98926_SLOTCNFLT_STATUS_SHIFT 4
#define MAX98926_SLOTCNFLT_STATUS_WIDTH 1
#define MAX98926_VBSTOVFL_STATUS_MASK (1<<3)
#define MAX98926_VBSTOVFL_STATUS_SHIFT 3
#define MAX98926_VBSTOVFL_STATUS_WIDTH 1
#define MAX98926_VBATOVFL_STATUS_MASK (1<<2)
#define MAX98926_VBATOVFL_STATUS_SHIFT 2
#define MAX98926_VBATOVFL_STATUS_WIDTH 1
#define MAX98926_IMONOVFL_STATUS_MASK (1<<1)
#define MAX98926_IMONOVFL_STATUS_SHIFT 1
#define MAX98926_IMONOVFL_STATUS_WIDTH 1
#define MAX98926_VMONOVFL_STATUS_MASK (1<<0)
#define MAX98926_VMONOVFL_STATUS_SHIFT 0
#define MAX98926_VMONOVFL_STATUS_WIDTH 1
/* MAX98926_R005_STATE0 */
#define MAX98926_THERMWARN_END_STATE_MASK (1<<3)
#define MAX98926_THERMWARN_END_STATE_SHIFT 3
#define MAX98926_THERMWARN_END_STATE_WIDTH 1
#define MAX98926_THERMWARN_BGN_STATE_MASK (1<<2)
#define MAX98926_THERMWARN_BGN_STATE_SHIFT 1
#define MAX98926_THERMWARN_BGN_STATE_WIDTH 1
#define MAX98926_THERMSHDN_END_STATE_MASK (1<<1)
#define MAX98926_THERMSHDN_END_STATE_SHIFT 1
#define MAX98926_THERMSHDN_END_STATE_WIDTH 1
#define MAX98926_THERMSHDN_BGN_STATE_MASK (1<<0)
#define MAX98926_THERMSHDN_BGN_STATE_SHIFT 0
#define MAX98926_THERMSHDN_BGN_STATE_WIDTH 1
/* MAX98926_R006_STATE1 */
#define MAX98926_SPRCURNT_STATE_MASK (1<<5)
#define MAX98926_SPRCURNT_STATE_SHIFT 5
#define MAX98926_SPRCURNT_STATE_WIDTH 1
#define MAX98926_WATCHFAIL_STATE_MASK (1<<4)
#define MAX98926_WATCHFAIL_STATE_SHIFT 4
#define MAX98926_WATCHFAIL_STATE_WIDTH 1
#define MAX98926_ALCINFH_STATE_MASK (1<<3)
#define MAX98926_ALCINFH_STATE_SHIFT 3
#define MAX98926_ALCINFH_STATE_WIDTH 1
#define MAX98926_ALCACT_STATE_MASK (1<<2)
#define MAX98926_ALCACT_STATE_SHIFT 2
#define MAX98926_ALCACT_STATE_WIDTH 1
#define MAX98926_ALCMUT_STATE_MASK (1<<1)
#define MAX98926_ALCMUT_STATE_SHIFT 1
#define MAX98926_ALCMUT_STATE_WIDTH 1
#define MAX98926_ALCP_STATE_MASK (1<<0)
#define MAX98926_ALCP_STATE_SHIFT 0
#define MAX98926_ALCP_STATE_WIDTH 1
/* MAX98926_R007_STATE2 */
#define MAX98926_SLOTOVRN_STATE_MASK (1<<6)
#define MAX98926_SLOTOVRN_STATE_SHIFT 6
#define MAX98926_SLOTOVRN_STATE_WIDTH 1
#define MAX98926_INVALSLOT_STATE_MASK (1<<5)
#define MAX98926_INVALSLOT_STATE_SHIFT 5
#define MAX98926_INVALSLOT_STATE_WIDTH 1
#define MAX98926_SLOTCNFLT_STATE_MASK (1<<4)
#define MAX98926_SLOTCNFLT_STATE_SHIFT 4
#define MAX98926_SLOTCNFLT_STATE_WIDTH 1
#define MAX98926_VBSTOVFL_STATE_MASK (1<<3)
#define MAX98926_VBSTOVFL_STATE_SHIFT 3
#define MAX98926_VBSTOVFL_STATE_WIDTH 1
#define MAX98926_VBATOVFL_STATE_MASK (1<<2)
#define MAX98926_VBATOVFL_STATE_SHIFT 2
#define MAX98926_VBATOVFL_STATE_WIDTH 1
#define MAX98926_IMONOVFL_STATE_MASK (1<<1)
#define MAX98926_IMONOVFL_STATE_SHIFT 1
#define MAX98926_IMONOVFL_STATE_WIDTH 1
#define MAX98926_VMONOVFL_STATE_MASK (1<<0)
#define MAX98926_VMONOVFL_STATE_SHIFT 0
#define MAX98926_VMONOVFL_STATE_WIDTH 1
/* MAX98926_R008_FLAG0 */
#define MAX98926_THERMWARN_END_FLAG_MASK (1<<3)
#define MAX98926_THERMWARN_END_FLAG_SHIFT 3
#define MAX98926_THERMWARN_END_FLAG_WIDTH 1
#define MAX98926_THERMWARN_BGN_FLAG_MASK (1<<2)
#define MAX98926_THERMWARN_BGN_FLAG_SHIFT 2
#define MAX98926_THERMWARN_BGN_FLAG_WIDTH 1
#define MAX98926_THERMSHDN_END_FLAG_MASK (1<<1)
#define MAX98926_THERMSHDN_END_FLAG_SHIFT 1
#define MAX98926_THERMSHDN_END_FLAG_WIDTH 1
#define MAX98926_THERMSHDN_BGN_FLAG_MASK (1<<0)
#define MAX98926_THERMSHDN_BGN_FLAG_SHIFT 0
#define MAX98926_THERMSHDN_BGN_FLAG_WIDTH 1
/* MAX98926_R009_FLAG1 */
#define MAX98926_SPKCURNT_FLAG_MASK (1<<5)
#define MAX98926_SPKCURNT_FLAG_SHIFT 5
#define MAX98926_SPKCURNT_FLAG_WIDTH 1
#define MAX98926_WATCHFAIL_FLAG_MASK (1<<4)
#define MAX98926_WATCHFAIL_FLAG_SHIFT 4
#define MAX98926_WATCHFAIL_FLAG_WIDTH 1
#define MAX98926_ALCINFH_FLAG_MASK (1<<3)
#define MAX98926_ALCINFH_FLAG_SHIFT 3
#define MAX98926_ALCINFH_FLAG_WIDTH 1
#define MAX98926_ALCACT_FLAG_MASK (1<<2)
#define MAX98926_ALCACT_FLAG_SHIFT 2
#define MAX98926_ALCACT_FLAG_WIDTH 1
#define MAX98926_ALCMUT_FLAG_MASK (1<<1)
#define MAX98926_ALCMUT_FLAG_SHIFT 1
#define MAX98926_ALCMUT_FLAG_WIDTH 1
#define MAX98926_ALCP_FLAG_MASK (1<<0)
#define MAX98926_ALCP_FLAG_SHIFT 0
#define MAX98926_ALCP_FLAG_WIDTH 1
/* MAX98926_R00A_FLAG2 */
#define MAX98926_SLOTOVRN_FLAG_MASK (1<<6)
#define MAX98926_SLOTOVRN_FLAG_SHIFT 6
#define MAX98926_SLOTOVRN_FLAG_WIDTH 1
#define MAX98926_INVALSLOT_FLAG_MASK (1<<5)
#define MAX98926_INVALSLOT_FLAG_SHIFT 5
#define MAX98926_INVALSLOT_FLAG_WIDTH 1
#define MAX98926_SLOTCNFLT_FLAG_MASK (1<<4)
#define MAX98926_SLOTCNFLT_FLAG_SHIFT 4
#define MAX98926_SLOTCNFLT_FLAG_WIDTH 1
#define MAX98926_VBSTOVFL_FLAG_MASK (1<<3)
#define MAX98926_VBSTOVFL_FLAG_SHIFT 3
#define MAX98926_VBSTOVFL_FLAG_WIDTH 1
#define MAX98926_VBATOVFL_FLAG_MASK (1<<2)
#define MAX98926_VBATOVFL_FLAG_SHIFT 2
#define MAX98926_VBATOVFL_FLAG_WIDTH 1
#define MAX98926_IMONOVFL_FLAG_MASK (1<<1)
#define MAX98926_IMONOVFL_FLAG_SHIFT 1
#define MAX98926_IMONOVFL_FLAG_WIDTH 1
#define MAX98926_VMONOVFL_FLAG_MASK (1<<0)
#define MAX98926_VMONOVFL_FLAG_SHIFT 0
#define MAX98926_VMONOVFL_FLAG_WIDTH 1
/* MAX98926_R00B_IRQ_ENABLE0 */
#define MAX98926_THERMWARN_END_EN_MASK (1<<3)
#define MAX98926_THERMWARN_END_EN_SHIFT 3
#define MAX98926_THERMWARN_END_EN_WIDTH 1
#define MAX98926_THERMWARN_BGN_EN_MASK (1<<2)
#define MAX98926_THERMWARN_BGN_EN_SHIFT 2
#define MAX98926_THERMWARN_BGN_EN_WIDTH 1
#define MAX98926_THERMSHDN_END_EN_MASK (1<<1)
#define MAX98926_THERMSHDN_END_EN_SHIFT 1
#define MAX98926_THERMSHDN_END_EN_WIDTH 1
#define MAX98926_THERMSHDN_BGN_EN_MASK (1<<0)
#define MAX98926_THERMSHDN_BGN_EN_SHIFT 0
#define MAX98926_THERMSHDN_BGN_EN_WIDTH 1
/* MAX98926_R00C_IRQ_ENABLE1 */
#define MAX98926_SPKCURNT_EN_MASK (1<<5)
#define MAX98926_SPKCURNT_EN_SHIFT 5
#define MAX98926_SPKCURNT_EN_WIDTH 1
#define MAX98926_WATCHFAIL_EN_MASK (1<<4)
#define MAX98926_WATCHFAIL_EN_SHIFT 4
#define MAX98926_WATCHFAIL_EN_WIDTH 1
#define MAX98926_ALCINFH_EN_MASK (1<<3)
#define MAX98926_ALCINFH_EN_SHIFT 3
#define MAX98926_ALCINFH_EN_WIDTH 1
#define MAX98926_ALCACT_EN_MASK (1<<2)
#define MAX98926_ALCACT_EN_SHIFT 2
#define MAX98926_ALCACT_EN_WIDTH 1
#define MAX98926_ALCMUT_EN_MASK (1<<1)
#define MAX98926_ALCMUT_EN_SHIFT 1
#define MAX98926_ALCMUT_EN_WIDTH 1
#define MAX98926_ALCP_EN_MASK (1<<0)
#define MAX98926_ALCP_EN_SHIFT 0
#define MAX98926_ALCP_EN_WIDTH 1
/* MAX98926_R00D_IRQ_ENABLE2 */
#define MAX98926_SLOTOVRN_EN_MASK (1<<6)
#define MAX98926_SLOTOVRN_EN_SHIFT 6
#define MAX98926_SLOTOVRN_EN_WIDTH 1
#define MAX98926_INVALSLOT_EN_MASK (1<<5)
#define MAX98926_INVALSLOT_EN_SHIFT 5
#define MAX98926_INVALSLOT_EN_WIDTH 1
#define MAX98926_SLOTCNFLT_EN_MASK (1<<4)
#define MAX98926_SLOTCNFLT_EN_SHIFT 4
#define MAX98926_SLOTCNFLT_EN_WIDTH 1
#define MAX98926_VBSTOVFL_EN_MASK (1<<3)
#define MAX98926_VBSTOVFL_EN_SHIFT 3
#define MAX98926_VBSTOVFL_EN_WIDTH 1
#define MAX98926_VBATOVFL_EN_MASK (1<<2)
#define MAX98926_VBATOVFL_EN_SHIFT 2
#define MAX98926_VBATOVFL_EN_WIDTH 1
#define MAX98926_IMONOVFL_EN_MASK (1<<1)
#define MAX98926_IMONOVFL_EN_SHIFT 1
#define MAX98926_IMONOVFL_EN_WIDTH 1
#define MAX98926_VMONOVFL_EN_MASK (1<<0)
#define MAX98926_VMONOVFL_EN_SHIFT 0
#define MAX98926_VMONOVFL_EN_WIDTH 1
/* MAX98926_R00E_IRQ_CLEAR0 */
#define MAX98926_THERMWARN_END_CLR_MASK (1<<3)
#define MAX98926_THERMWARN_END_CLR_SHIFT 3
#define MAX98926_THERMWARN_END_CLR_WIDTH 1
#define MAX98926_THERMWARN_BGN_CLR_MASK (1<<2)
#define MAX98926_THERMWARN_BGN_CLR_SHIFT 2
#define MAX98926_THERMWARN_BGN_CLR_WIDTH 1
#define MAX98926_THERMSHDN_END_CLR_MASK (1<<1)
#define MAX98926_THERMSHDN_END_CLR_SHIFT 1
#define MAX98926_THERMSHDN_END_CLR_WIDTH 1
#define MAX98926_THERMSHDN_BGN_CLR_MASK (1<<0)
#define MAX98926_THERMSHDN_BGN_CLR_SHIFT 0
#define MAX98926_THERMSHDN_BGN_CLR_WIDTH 1
/* MAX98926_R00F_IRQ_CLEAR1 */
#define MAX98926_SPKCURNT_CLR_MASK (1<<5)
#define MAX98926_SPKCURNT_CLR_SHIFT 5
#define MAX98926_SPKCURNT_CLR_WIDTH 1
#define MAX98926_WATCHFAIL_CLR_MASK (1<<4)
#define MAX98926_WATCHFAIL_CLR_SHIFT 4
#define MAX98926_WATCHFAIL_CLR_WIDTH 1
#define MAX98926_ALCINFH_CLR_MASK (1<<3)
#define MAX98926_ALCINFH_CLR_SHIFT 3
#define MAX98926_ALCINFH_CLR_WIDTH 1
#define MAX98926_ALCACT_CLR_MASK (1<<2)
#define MAX98926_ALCACT_CLR_SHIFT 2
#define MAX98926_ALCACT_CLR_WIDTH 1
#define MAX98926_ALCMUT_CLR_MASK (1<<1)
#define MAX98926_ALCMUT_CLR_SHIFT 1
#define MAX98926_ALCMUT_CLR_WIDTH 1
#define MAX98926_ALCP_CLR_MASK (1<<0)
#define MAX98926_ALCP_CLR_SHIFT 0
#define MAX98926_ALCP_CLR_WIDTH 1
/* MAX98926_R010_IRQ_CLEAR2 */
#define MAX98926_SLOTOVRN_CLR_MASK (1<<6)
#define MAX98926_SLOTOVRN_CLR_SHIFT 6
#define MAX98926_SLOTOVRN_CLR_WIDTH 1
#define MAX98926_INVALSLOT_CLR_MASK (1<<5)
#define MAX98926_INVALSLOT_CLR_SHIFT 5
#define MAX98926_INVALSLOT_CLR_WIDTH 1
#define MAX98926_SLOTCNFLT_CLR_MASK (1<<4)
#define MAX98926_SLOTCNFLT_CLR_SHIFT 4
#define MAX98926_SLOTCNFLT_CLR_WIDTH 1
#define MAX98926_VBSTOVFL_CLR_MASK (1<<3)
#define MAX98926_VBSTOVFL_CLR_SHIFT 3
#define MAX98926_VBSTOVFL_CLR_WIDTH 1
#define MAX98926_VBATOVFL_CLR_MASK (1<<2)
#define MAX98926_VBATOVFL_CLR_SHIFT 2
#define MAX98926_VBATOVFL_CLR_WIDTH 1
#define MAX98926_IMONOVFL_CLR_MASK (1<<1)
#define MAX98926_IMONOVFL_CLR_SHIFT 1
#define MAX98926_IMONOVFL_CLR_WIDTH 1
#define MAX98926_VMONOVFL_CLR_MASK (1<<0)
#define MAX98926_VMONOVFL_CLR_SHIFT 0
#define MAX98926_VMONOVFL_CLR_WIDTH 1
/* MAX98926_R011_MAP0 */
#define MAX98926_ER_THERMWARN_EN_MASK (1<<7)
#define MAX98926_ER_THERMWARN_EN_SHIFT 7
#define MAX98926_ER_THERMWARN_EN_WIDTH 1
#define MAX98926_ER_THERMWARN_MAP_MASK (0x07<<4)
#define MAX98926_ER_THERMWARN_MAP_SHIFT 4
#define MAX98926_ER_THERMWARN_MAP_WIDTH 3
/* MAX98926_R012_MAP1 */
#define MAX98926_ER_ALCMUT_EN_MASK (1<<7)
#define MAX98926_ER_ALCMUT_EN_SHIFT 7
#define MAX98926_ER_ALCMUT_EN_WIDTH 1
#define MAX98926_ER_ALCMUT_MAP_MASK (0x07<<4)
#define MAX98926_ER_ALCMUT_MAP_SHIFT 4
#define MAX98926_ER_ALCMUT_MAP_WIDTH 3
#define MAX98926_ER_ALCP_EN_MASK (1<<3)
#define MAX98926_ER_ALCP_EN_SHIFT 3
#define MAX98926_ER_ALCP_EN_WIDTH 1
#define MAX98926_ER_ALCP_MAP_MASK (0x07<<0)
#define MAX98926_ER_ALCP_MAP_SHIFT 0
#define MAX98926_ER_ALCP_MAP_WIDTH 3
/* MAX98926_R013_MAP2 */
#define MAX98926_ER_ALCINFH_EN_MASK (1<<7)
#define MAX98926_ER_ALCINFH_EN_SHIFT 7
#define MAX98926_ER_ALCINFH_EN_WIDTH 1
#define MAX98926_ER_ALCINFH_MAP_MASK (0x07<<4)
#define MAX98926_ER_ALCINFH_MAP_SHIFT 4
#define MAX98926_ER_ALCINFH_MAP_WIDTH 3
#define MAX98926_ER_ALCACT_EN_MASK (1<<3)
#define MAX98926_ER_ALCACT_EN_SHIFT 3
#define MAX98926_ER_ALCACT_EN_WIDTH 1
#define MAX98926_ER_ALCACT_MAP_MASK (0x07<<0)
#define MAX98926_ER_ALCACT_MAP_SHIFT 0
#define MAX98926_ER_ALCACT_MAP_WIDTH 3
/* MAX98926_R014_MAP3 */
#define MAX98926_ER_SPKCURNT_EN_MASK (1<<7)
#define MAX98926_ER_SPKCURNT_EN_SHIFT 7
#define MAX98926_ER_SPKCURNT_EN_WIDTH 1
#define MAX98926_ER_SPKCURNT_MAP_MASK (0x07<<4)
#define MAX98926_ER_SPKCURNT_MAP_SHIFT 4
#define MAX98926_ER_SPKCURNT_MAP_WIDTH 3
/* MAX98926_R015_MAP4 */
/* RESERVED */
/* MAX98926_R016_MAP5 */
#define MAX98926_ER_IMONOVFL_EN_MASK (1<<7)
#define MAX98926_ER_IMONOVFL_EN_SHIFT 7
#define MAX98926_ER_IMONOVFL_EN_WIDTH 1
#define MAX98926_ER_IMONOVFL_MAP_MASK (0x07<<4)
#define MAX98926_ER_IMONOVFL_MAP_SHIFT 4
#define MAX98926_ER_IMONOVFL_MAP_WIDTH 3
#define MAX98926_ER_VMONOVFL_EN_MASK (1<<3)
#define MAX98926_ER_VMONOVFL_EN_SHIFT 3
#define MAX98926_ER_VMONOVFL_EN_WIDTH 1
#define MAX98926_ER_VMONOVFL_MAP_MASK (0x07<<0)
#define MAX98926_ER_VMONOVFL_MAP_SHIFT 0
#define MAX98926_ER_VMONOVFL_MAP_WIDTH 3
/* MAX98926_R017_MAP6 */
#define MAX98926_ER_VBSTOVFL_EN_MASK (1<<7)
#define MAX98926_ER_VBSTOVFL_EN_SHIFT 7
#define MAX98926_ER_VBSTOVFL_EN_WIDTH 1
#define MAX98926_ER_VBSTOVFL_MAP_MASK (0x07<<4)
#define MAX98926_ER_VBSTOVFL_MAP_SHIFT 4
#define MAX98926_ER_VBSTOVFL_MAP_WIDTH 3
#define MAX98926_ER_VBATOVFL_EN_MASK (1<<3)
#define MAX98926_ER_VBATOVFL_EN_SHIFT 3
#define MAX98926_ER_VBATOVFL_EN_WIDTH 1
#define MAX98926_ER_VBATOVFL_MAP_MASK (0x07<<0)
#define MAX98926_ER_VBATOVFL_MAP_SHIFT 0
#define MAX98926_ER_VBATOVFL_MAP_WIDTH 3
/* MAX98926_R018_MAP7 */
#define MAX98926_ER_INVALSLOT_EN_MASK (1<<7)
#define MAX98926_ER_INVALSLOT_EN_SHIFT 7
#define MAX98926_ER_INVALSLOT_EN_WIDTH 1
#define MAX98926_ER_INVALSLOT_MAP_MASK (0x07<<4)
#define MAX98926_ER_INVALSLOT_MAP_SHIFT 4
#define MAX98926_ER_INVALSLOT_MAP_WIDTH 3
#define MAX98926_ER_SLOTCNFLT_EN_MASK (1<<3)
#define MAX98926_ER_SLOTCNFLT_EN_SHIFT 3
#define MAX98926_ER_SLOTCNFLT_EN_WIDTH 1
#define MAX98926_ER_SLOTCNFLT_MAP_MASK (0x07<<0)
#define MAX98926_ER_SLOTCNFLT_MAP_SHIFT 0
#define MAX98926_ER_SLOTCNFLT_MAP_WIDTH 3
/* MAX98926_R019_MAP8 */
#define MAX98926_ER_SLOTOVRN_EN_MASK (1<<3)
#define MAX98926_ER_SLOTOVRN_EN_SHIFT 3
#define MAX98926_ER_SLOTOVRN_EN_WIDTH 1
#define MAX98926_ER_SLOTOVRN_MAP_MASK (0x07<<0)
#define MAX98926_ER_SLOTOVRN_MAP_SHIFT 0
#define MAX98926_ER_SLOTOVRN_MAP_WIDTH 3
/* MAX98926_R01A_DAI_CLK_MODE1 */
#define MAX98926_DAI_CLK_SOURCE_MASK (1<<6)
#define MAX98926_DAI_CLK_SOURCE_SHIFT 6
#define MAX98926_DAI_CLK_SOURCE_WIDTH 1
#define MAX98926_MDLL_MULT_MASK (0x0F<<0)
#define MAX98926_MDLL_MULT_SHIFT 0
#define MAX98926_MDLL_MULT_WIDTH 4
#define MAX98926_MDLL_MULT_MCLKx8 6
#define MAX98926_MDLL_MULT_MCLKx16 8
/* MAX98926_R01B_DAI_CLK_MODE2 */
#define MAX98926_DAI_SR_MASK (0x0F<<4)
#define MAX98926_DAI_SR_SHIFT 4
#define MAX98926_DAI_SR_WIDTH 4
#define MAX98926_DAI_MAS_MASK (1<<3)
#define MAX98926_DAI_MAS_SHIFT 3
#define MAX98926_DAI_MAS_WIDTH 1
#define MAX98926_DAI_BSEL_MASK (0x07<<0)
#define MAX98926_DAI_BSEL_SHIFT 0
#define MAX98926_DAI_BSEL_WIDTH 3
#define MAX98926_DAI_BSEL_32 (0 << MAX98926_DAI_BSEL_SHIFT)
#define MAX98926_DAI_BSEL_48 (1 << MAX98926_DAI_BSEL_SHIFT)
#define MAX98926_DAI_BSEL_64 (2 << MAX98926_DAI_BSEL_SHIFT)
#define MAX98926_DAI_BSEL_256 (6 << MAX98926_DAI_BSEL_SHIFT)
/* MAX98926_R01C_DAI_CLK_DIV_M_MSBS */
#define MAX98926_DAI_M_MSBS_MASK (0xFF<<0)
#define MAX98926_DAI_M_MSBS_SHIFT 0
#define MAX98926_DAI_M_MSBS_WIDTH 8
/* MAX98926_R01D_DAI_CLK_DIV_M_LSBS */
#define MAX98926_DAI_M_LSBS_MASK (0xFF<<0)
#define MAX98926_DAI_M_LSBS_SHIFT 0
#define MAX98926_DAI_M_LSBS_WIDTH 8
/* MAX98926_R01E_DAI_CLK_DIV_N_MSBS */
#define MAX98926_DAI_N_MSBS_MASK (0x7F<<0)
#define MAX98926_DAI_N_MSBS_SHIFT 0
#define MAX98926_DAI_N_MSBS_WIDTH 7
/* MAX98926_R01F_DAI_CLK_DIV_N_LSBS */
#define MAX98926_DAI_N_LSBS_MASK (0xFF<<0)
#define MAX98926_DAI_N_LSBS_SHIFT 0
#define MAX98926_DAI_N_LSBS_WIDTH 8
/* MAX98926_R020_FORMAT */
#define MAX98926_DAI_CHANSZ_MASK (0x03<<6)
#define MAX98926_DAI_CHANSZ_SHIFT 6
#define MAX98926_DAI_CHANSZ_WIDTH 2
#define MAX98926_DAI_INTERLEAVE_MASK (1<<5)
#define MAX98926_DAI_INTERLEAVE_SHIFT 5
#define MAX98926_DAI_INTERLEAVE_WIDTH 1
#define MAX98926_DAI_EXTBCLK_HIZ_MASK (1<<4)
#define MAX98926_DAI_EXTBCLK_HIZ_SHIFT 4
#define MAX98926_DAI_EXTBCLK_HIZ_WIDTH 1
#define MAX98926_DAI_WCI_MASK (1<<3)
#define MAX98926_DAI_WCI_SHIFT 3
#define MAX98926_DAI_WCI_WIDTH 1
#define MAX98926_DAI_BCI_MASK (1<<2)
#define MAX98926_DAI_BCI_SHIFT 2
#define MAX98926_DAI_BCI_WIDTH 1
#define MAX98926_DAI_DLY_MASK (1<<1)
#define MAX98926_DAI_DLY_SHIFT 1
#define MAX98926_DAI_DLY_WIDTH 1
#define MAX98926_DAI_TDM_MASK (1<<0)
#define MAX98926_DAI_TDM_SHIFT 0
#define MAX98926_DAI_TDM_WIDTH 1
#define MAX98926_DAI_CHANSZ_16 (1 << MAX98926_DAI_CHANSZ_SHIFT)
#define MAX98926_DAI_CHANSZ_24 (2 << MAX98926_DAI_CHANSZ_SHIFT)
#define MAX98926_DAI_CHANSZ_32 (3 << MAX98926_DAI_CHANSZ_SHIFT)
/* MAX98926_R021_TDM_SLOT_SELECT */
#define MAX98926_DAI_DO_EN_MASK (1<<7)
#define MAX98926_DAI_DO_EN_SHIFT 7
#define MAX98926_DAI_DO_EN_WIDTH 1
#define MAX98926_DAI_DIN_EN_MASK (1<<6)
#define MAX98926_DAI_DIN_EN_SHIFT 6
#define MAX98926_DAI_DIN_EN_WIDTH 1
#define MAX98926_DAI_INR_SOURCE_MASK (0x07<<3)
#define MAX98926_DAI_INR_SOURCE_SHIFT 3
#define MAX98926_DAI_INR_SOURCE_WIDTH 3
#define MAX98926_DAI_INL_SOURCE_MASK (0x07<<0)
#define MAX98926_DAI_INL_SOURCE_SHIFT 0
#define MAX98926_DAI_INL_SOURCE_WIDTH 3
/* MAX98926_R022_DOUT_CFG_VMON */
#define MAX98926_DAI_VMON_EN_MASK (1<<5)
#define MAX98926_DAI_VMON_EN_SHIFT 5
#define MAX98926_DAI_VMON_EN_WIDTH 1
#define MAX98926_DAI_VMON_SLOT_MASK (0x1F<<0)
#define MAX98926_DAI_VMON_SLOT_SHIFT 0
#define MAX98926_DAI_VMON_SLOT_WIDTH 5
#define MAX98926_DAI_VMON_SLOT_00_01 (0 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_01_02 (1 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_02_03 (2 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_03_04 (3 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_04_05 (4 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_05_06 (5 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_06_07 (6 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_07_08 (7 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_08_09 (8 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_09_0A (9 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_0A_0B (10 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_0B_0C (11 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_0C_0D (12 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_0D_0E (13 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_0E_0F (14 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_0F_10 (15 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_10_11 (16 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_11_12 (17 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_12_13 (18 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_13_14 (19 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_14_15 (20 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_15_16 (21 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_16_17 (22 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_17_18 (23 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_18_19 (24 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_19_1A (25 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_1A_1B (26 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_1B_1C (27 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_1C_1D (28 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_1D_1E (29 << MAX98926_DAI_VMON_SLOT_SHIFT)
#define MAX98926_DAI_VMON_SLOT_1E_1F (30 << MAX98926_DAI_VMON_SLOT_SHIFT)
/* MAX98926_R023_DOUT_CFG_IMON */
#define MAX98926_DAI_IMON_EN_MASK (1<<5)
#define MAX98926_DAI_IMON_EN_SHIFT 5
#define MAX98926_DAI_IMON_EN_WIDTH 1
#define MAX98926_DAI_IMON_SLOT_MASK (0x1F<<0)
#define MAX98926_DAI_IMON_SLOT_SHIFT 0
#define MAX98926_DAI_IMON_SLOT_WIDTH 5
#define MAX98926_DAI_IMON_SLOT_00_01 (0 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_01_02 (1 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_02_03 (2 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_03_04 (3 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_04_05 (4 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_05_06 (5 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_06_07 (6 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_07_08 (7 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_08_09 (8 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_09_0A (9 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_0A_0B (10 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_0B_0C (11 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_0C_0D (12 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_0D_0E (13 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_0E_0F (14 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_0F_10 (15 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_10_11 (16 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_11_12 (17 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_12_13 (18 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_13_14 (19 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_14_15 (20 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_15_16 (21 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_16_17 (22 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_17_18 (23 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_18_19 (24 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_19_1A (25 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_1A_1B (26 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_1B_1C (27 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_1C_1D (28 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_1D_1E (29 << MAX98926_DAI_IMON_SLOT_SHIFT)
#define MAX98926_DAI_IMON_SLOT_1E_1F (30 << MAX98926_DAI_IMON_SLOT_SHIFT)
/* MAX98926_R024_DOUT_CFG_VBAT */
#define MAX98926_DAI_INTERLEAVE_SLOT_MASK (0x1F<<0)
#define MAX98926_DAI_INTERLEAVE_SLOT_SHIFT 0
#define MAX98926_DAI_INTERLEAVE_SLOT_WIDTH 5
/* MAX98926_R025_DOUT_CFG_VBST */
#define MAX98926_DAI_VBST_EN_MASK (1<<5)
#define MAX98926_DAI_VBST_EN_SHIFT 5
#define MAX98926_DAI_VBST_EN_WIDTH 1
#define MAX98926_DAI_VBST_SLOT_MASK (0x1F<<0)
#define MAX98926_DAI_VBST_SLOT_SHIFT 0
#define MAX98926_DAI_VBST_SLOT_WIDTH 5
/* MAX98926_R026_DOUT_CFG_FLAG */
#define MAX98926_DAI_FLAG_EN_MASK (1<<5)
#define MAX98926_DAI_FLAG_EN_SHIFT 5
#define MAX98926_DAI_FLAG_EN_WIDTH 1
#define MAX98926_DAI_FLAG_SLOT_MASK (0x1F<<0)
#define MAX98926_DAI_FLAG_SLOT_SHIFT 0
#define MAX98926_DAI_FLAG_SLOT_WIDTH 5
/* MAX98926_R027_DOUT_HIZ_CFG1 */
#define MAX98926_DAI_SLOT_HIZ_CFG1_MASK (0xFF<<0)
#define MAX98926_DAI_SLOT_HIZ_CFG1_SHIFT 0
#define MAX98926_DAI_SLOT_HIZ_CFG1_WIDTH 8
/* MAX98926_R028_DOUT_HIZ_CFG2 */
#define MAX98926_DAI_SLOT_HIZ_CFG2_MASK (0xFF<<0)
#define MAX98926_DAI_SLOT_HIZ_CFG2_SHIFT 0
#define MAX98926_DAI_SLOT_HIZ_CFG2_WIDTH 8
/* MAX98926_R029_DOUT_HIZ_CFG3 */
#define MAX98926_DAI_SLOT_HIZ_CFG3_MASK (0xFF<<0)
#define MAX98926_DAI_SLOT_HIZ_CFG3_SHIFT 0
#define MAX98926_DAI_SLOT_HIZ_CFG3_WIDTH 8
/* MAX98926_R02A_DOUT_HIZ_CFG4 */
#define MAX98926_DAI_SLOT_HIZ_CFG4_MASK (0xFF<<0)
#define MAX98926_DAI_SLOT_HIZ_CFG4_SHIFT 0
#define MAX98926_DAI_SLOT_HIZ_CFG4_WIDTH 8
/* MAX98926_R02B_DOUT_DRV_STRENGTH */
#define MAX98926_DAI_OUT_DRIVE_MASK (0x03<<0)
#define MAX98926_DAI_OUT_DRIVE_SHIFT 0
#define MAX98926_DAI_OUT_DRIVE_WIDTH 2
/* MAX98926_R02C_FILTERS */
#define MAX98926_ADC_DITHER_EN_MASK (1<<7)
#define MAX98926_ADC_DITHER_EN_SHIFT 7
#define MAX98926_ADC_DITHER_EN_WIDTH 1
#define MAX98926_IV_DCB_EN_MASK (1<<6)
#define MAX98926_IV_DCB_EN_SHIFT 6
#define MAX98926_IV_DCB_EN_WIDTH 1
#define MAX98926_DAC_DITHER_EN_MASK (1<<4)
#define MAX98926_DAC_DITHER_EN_SHIFT 4
#define MAX98926_DAC_DITHER_EN_WIDTH 1
#define MAX98926_DAC_FILTER_MODE_MASK (1<<3)
#define MAX98926_DAC_FILTER_MODE_SHIFT 3
#define MAX98926_DAC_FILTER_MODE_WIDTH 1
#define MAX98926_DAC_HPF_MASK (0x07<<0)
#define MAX98926_DAC_HPF_SHIFT 0
#define MAX98926_DAC_HPF_WIDTH 3
#define MAX98926_DAC_HPF_DISABLE (0 << MAX98926_DAC_HPF_SHIFT)
#define MAX98926_DAC_HPF_DC_BLOCK (1 << MAX98926_DAC_HPF_SHIFT)
#define MAX98926_DAC_HPF_EN_100 (2 << MAX98926_DAC_HPF_SHIFT)
#define MAX98926_DAC_HPF_EN_200 (3 << MAX98926_DAC_HPF_SHIFT)
#define MAX98926_DAC_HPF_EN_400 (4 << MAX98926_DAC_HPF_SHIFT)
#define MAX98926_DAC_HPF_EN_800 (5 << MAX98926_DAC_HPF_SHIFT)
/* MAX98926_R02D_GAIN */
#define MAX98926_DAC_IN_SEL_MASK (0x03<<5)
#define MAX98926_DAC_IN_SEL_SHIFT 5
#define MAX98926_DAC_IN_SEL_WIDTH 2
#define MAX98926_SPK_GAIN_MASK (0x1F<<0)
#define MAX98926_SPK_GAIN_SHIFT 0
#define MAX98926_SPK_GAIN_WIDTH 5
#define MAX98926_DAC_IN_SEL_LEFT_DAI (0 << MAX98926_DAC_IN_SEL_SHIFT)
#define MAX98926_DAC_IN_SEL_RIGHT_DAI (1 << MAX98926_DAC_IN_SEL_SHIFT)
#define MAX98926_DAC_IN_SEL_SUMMED_DAI (2 << MAX98926_DAC_IN_SEL_SHIFT)
#define MAX98926_DAC_IN_SEL_DIV2_SUMMED_DAI (3 << MAX98926_DAC_IN_SEL_SHIFT)
/* MAX98926_R02E_GAIN_RAMPING */
#define MAX98926_SPK_RMP_EN_MASK (1<<1)
#define MAX98926_SPK_RMP_EN_SHIFT 1
#define MAX98926_SPK_RMP_EN_WIDTH 1
#define MAX98926_SPK_ZCD_EN_MASK (1<<0)
#define MAX98926_SPK_ZCD_EN_SHIFT 0
#define MAX98926_SPK_ZCD_EN_WIDTH 1
/* MAX98926_R02F_SPK_AMP */
#define MAX98926_SPK_MODE_MASK (1<<0)
#define MAX98926_SPK_MODE_SHIFT 0
#define MAX98926_SPK_MODE_WIDTH 1
#define MAX98926_INSELECT_MODE_MASK (1<<1)
#define MAX98926_INSELECT_MODE_SHIFT 1
#define MAX98926_INSELECT_MODE_WIDTH 1
/* MAX98926_R030_THRESHOLD */
#define MAX98926_ALC_EN_MASK (1<<5)
#define MAX98926_ALC_EN_SHIFT 5
#define MAX98926_ALC_EN_WIDTH 1
#define MAX98926_ALC_TH_MASK (0x1F<<0)
#define MAX98926_ALC_TH_SHIFT 0
#define MAX98926_ALC_TH_WIDTH 5
/* MAX98926_R031_ALC_ATTACK */
#define MAX98926_ALC_ATK_STEP_MASK (0x0F<<4)
#define MAX98926_ALC_ATK_STEP_SHIFT 4
#define MAX98926_ALC_ATK_STEP_WIDTH 4
#define MAX98926_ALC_ATK_RATE_MASK (0x7<<0)
#define MAX98926_ALC_ATK_RATE_SHIFT 0
#define MAX98926_ALC_ATK_RATE_WIDTH 3
/* MAX98926_R032_ALC_ATTEN_RLS */
#define MAX98926_ALC_MAX_ATTEN_MASK (0x0F<<4)
#define MAX98926_ALC_MAX_ATTEN_SHIFT 4
#define MAX98926_ALC_MAX_ATTEN_WIDTH 4
#define MAX98926_ALC_RLS_RATE_MASK (0x7<<0)
#define MAX98926_ALC_RLS_RATE_SHIFT 0
#define MAX98926_ALC_RLS_RATE_WIDTH 3
/* MAX98926_R033_ALC_HOLD_RLS */
#define MAX98926_ALC_RLS_TGR_MASK (1<<0)
#define MAX98926_ALC_RLS_TGR_SHIFT 0
#define MAX98926_ALC_RLS_TGR_WIDTH 1
/* MAX98926_R034_ALC_CONFIGURATION */
#define MAX98926_ALC_MUTE_EN_MASK (1<<7)
#define MAX98926_ALC_MUTE_EN_SHIFT 7
#define MAX98926_ALC_MUTE_EN_WIDTH 1
#define MAX98926_ALC_MUTE_DLY_MASK (0x07<<4)
#define MAX98926_ALC_MUTE_DLY_SHIFT 4
#define MAX98926_ALC_MUTE_DLY_WIDTH 3
#define MAX98926_ALC_RLS_DBT_MASK (0x07<<0)
#define MAX98926_ALC_RLS_DBT_SHIFT 0
#define MAX98926_ALC_RLS_DBT_WIDTH 3
/* MAX98926_R035_BOOST_CONVERTER */
#define MAX98926_BST_SYNC_MASK (1<<7)
#define MAX98926_BST_SYNC_SHIFT 7
#define MAX98926_BST_SYNC_WIDTH 1
#define MAX98926_BST_PHASE_MASK (0x03<<4)
#define MAX98926_BST_PHASE_SHIFT 4
#define MAX98926_BST_PHASE_WIDTH 2
#define MAX98926_BST_SKIP_MODE_MASK (0x03<<0)
#define MAX98926_BST_SKIP_MODE_SHIFT 0
#define MAX98926_BST_SKIP_MODE_WIDTH 2
/* MAX98926_R036_BLOCK_ENABLE */
#define MAX98926_BST_EN_MASK (1<<7)
#define MAX98926_BST_EN_SHIFT 7
#define MAX98926_BST_EN_WIDTH 1
#define MAX98926_WATCH_EN_MASK (1<<6)
#define MAX98926_WATCH_EN_SHIFT 6
#define MAX98926_WATCH_EN_WIDTH 1
#define MAX98926_CLKMON_EN_MASK (1<<5)
#define MAX98926_CLKMON_EN_SHIFT 5
#define MAX98926_CLKMON_EN_WIDTH 1
#define MAX98926_SPK_EN_MASK (1<<4)
#define MAX98926_SPK_EN_SHIFT 4
#define MAX98926_SPK_EN_WIDTH 1
#define MAX98926_ADC_VBST_EN_MASK (1<<3)
#define MAX98926_ADC_VBST_EN_SHIFT 3
#define MAX98926_ADC_VBST_EN_WIDTH 1
#define MAX98926_ADC_VBAT_EN_MASK (1<<2)
#define MAX98926_ADC_VBAT_EN_SHIFT 2
#define MAX98926_ADC_VBAT_EN_WIDTH 1
#define MAX98926_ADC_IMON_EN_MASK (1<<1)
#define MAX98926_ADC_IMON_EN_SHIFT 1
#define MAX98926_ADC_IMON_EN_WIDTH 1
#define MAX98926_ADC_VMON_EN_MASK (1<<0)
#define MAX98926_ADC_VMON_EN_SHIFT 0
#define MAX98926_ADC_VMON_EN_WIDTH 1
/* MAX98926_R037_CONFIGURATION */
#define MAX98926_BST_VOUT_MASK (0x0F<<4)
#define MAX98926_BST_VOUT_SHIFT 4
#define MAX98926_BST_VOUT_WIDTH 4
#define MAX98926_THERMWARN_LEVEL_MASK (0x03<<2)
#define MAX98926_THERMWARN_LEVEL_SHIFT 2
#define MAX98926_THERMWARN_LEVEL_WIDTH 2
#define MAX98926_WATCH_TIME_MASK (0x03<<0)
#define MAX98926_WATCH_TIME_SHIFT 0
#define MAX98926_WATCH_TIME_WIDTH 2
/* MAX98926_R038_GLOBAL_ENABLE */
#define MAX98926_EN_MASK (1<<7)
#define MAX98926_EN_SHIFT 7
#define MAX98926_EN_WIDTH 1
/* MAX98926_R03A_BOOST_LIMITER */
#define MAX98926_BST_ILIM_MASK (0xF<<4)
#define MAX98926_BST_ILIM_SHIFT 4
#define MAX98926_BST_ILIM_WIDTH 4
/* MAX98926_R0FF_VERSION */
#define MAX98926_REV_ID_MASK (0xFF<<0)
#define MAX98926_REV_ID_SHIFT 0
#define MAX98926_REV_ID_WIDTH 8
struct max98926_priv {
struct regmap *regmap;
struct snd_soc_codec *codec;
unsigned int sysclk;
unsigned int v_slot;
unsigned int i_slot;
unsigned int ch_size;
unsigned int interleave_mode;
};
#endif

View File

@ -84,6 +84,7 @@ static const struct nau8825_fll_attr fll_pre_scalar[] = {
static const struct reg_default nau8825_reg_defaults[] = { static const struct reg_default nau8825_reg_defaults[] = {
{ NAU8825_REG_ENA_CTRL, 0x00ff }, { NAU8825_REG_ENA_CTRL, 0x00ff },
{ NAU8825_REG_IIC_ADDR_SET, 0x0 },
{ NAU8825_REG_CLK_DIVIDER, 0x0050 }, { NAU8825_REG_CLK_DIVIDER, 0x0050 },
{ NAU8825_REG_FLL1, 0x0 }, { NAU8825_REG_FLL1, 0x0 },
{ NAU8825_REG_FLL2, 0x3126 }, { NAU8825_REG_FLL2, 0x3126 },
@ -158,8 +159,7 @@ static const struct reg_default nau8825_reg_defaults[] = {
static bool nau8825_readable_reg(struct device *dev, unsigned int reg) static bool nau8825_readable_reg(struct device *dev, unsigned int reg)
{ {
switch (reg) { switch (reg) {
case NAU8825_REG_ENA_CTRL: case NAU8825_REG_ENA_CTRL ... NAU8825_REG_FLL_VCO_RSV:
case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV:
case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL: case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL:
case NAU8825_REG_INTERRUPT_MASK ... NAU8825_REG_KEYDET_CTRL: case NAU8825_REG_INTERRUPT_MASK ... NAU8825_REG_KEYDET_CTRL:
case NAU8825_REG_VDET_THRESHOLD_1 ... NAU8825_REG_DACR_CTRL: case NAU8825_REG_VDET_THRESHOLD_1 ... NAU8825_REG_DACR_CTRL:
@ -184,8 +184,7 @@ static bool nau8825_readable_reg(struct device *dev, unsigned int reg)
static bool nau8825_writeable_reg(struct device *dev, unsigned int reg) static bool nau8825_writeable_reg(struct device *dev, unsigned int reg)
{ {
switch (reg) { switch (reg) {
case NAU8825_REG_RESET ... NAU8825_REG_ENA_CTRL: case NAU8825_REG_RESET ... NAU8825_REG_FLL_VCO_RSV:
case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV:
case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL: case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL:
case NAU8825_REG_INTERRUPT_MASK: case NAU8825_REG_INTERRUPT_MASK:
case NAU8825_REG_INT_CLR_KEY_STATUS ... NAU8825_REG_KEYDET_CTRL: case NAU8825_REG_INT_CLR_KEY_STATUS ... NAU8825_REG_KEYDET_CTRL:
@ -227,10 +226,42 @@ static bool nau8825_volatile_reg(struct device *dev, unsigned int reg)
static int nau8825_pump_event(struct snd_soc_dapm_widget *w, static int nau8825_pump_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event) struct snd_kcontrol *kcontrol, int event)
{ {
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
switch (event) { switch (event) {
case SND_SOC_DAPM_POST_PMU: case SND_SOC_DAPM_POST_PMU:
/* Prevent startup click by letting charge pump to ramp up */ /* Prevent startup click by letting charge pump to ramp up */
msleep(10); msleep(10);
regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
NAU8825_JAMNODCLOW, NAU8825_JAMNODCLOW);
break;
case SND_SOC_DAPM_PRE_PMD:
regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
NAU8825_JAMNODCLOW, 0);
break;
default:
return -EINVAL;
}
return 0;
}
static int nau8825_output_dac_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
/* Disables the TESTDAC to let DAC signal pass through. */
regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
NAU8825_BIAS_TESTDAC_EN, 0);
break;
case SND_SOC_DAPM_POST_PMD:
regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN);
break; break;
default: default:
return -EINVAL; return -EINVAL;
@ -316,10 +347,10 @@ static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = {
SND_SOC_DAPM_ADC("SAR", NULL, NAU8825_REG_SAR_CTRL, SND_SOC_DAPM_ADC("SAR", NULL, NAU8825_REG_SAR_CTRL,
NAU8825_SAR_ADC_EN_SFT, 0), NAU8825_SAR_ADC_EN_SFT, 0),
SND_SOC_DAPM_DAC("ADACL", NULL, NAU8825_REG_RDAC, 12, 0), SND_SOC_DAPM_PGA_S("ADACL", 2, NAU8825_REG_RDAC, 12, 0, NULL, 0),
SND_SOC_DAPM_DAC("ADACR", NULL, NAU8825_REG_RDAC, 13, 0), SND_SOC_DAPM_PGA_S("ADACR", 2, NAU8825_REG_RDAC, 13, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADACL Clock", NAU8825_REG_RDAC, 8, 0, NULL, 0), SND_SOC_DAPM_PGA_S("ADACL Clock", 3, NAU8825_REG_RDAC, 8, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADACR Clock", NAU8825_REG_RDAC, 9, 0, NULL, 0), SND_SOC_DAPM_PGA_S("ADACR Clock", 3, NAU8825_REG_RDAC, 9, 0, NULL, 0),
SND_SOC_DAPM_DAC("DDACR", NULL, NAU8825_REG_ENA_CTRL, SND_SOC_DAPM_DAC("DDACR", NULL, NAU8825_REG_ENA_CTRL,
NAU8825_ENABLE_DACR_SFT, 0), NAU8825_ENABLE_DACR_SFT, 0),
@ -330,29 +361,48 @@ static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = {
SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacl_mux), SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacl_mux),
SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacr_mux), SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacr_mux),
SND_SOC_DAPM_PGA("HP amp L", NAU8825_REG_CLASSG_CTRL, 1, 0, NULL, 0), SND_SOC_DAPM_PGA_S("HP amp L", 0,
SND_SOC_DAPM_PGA("HP amp R", NAU8825_REG_CLASSG_CTRL, 2, 0, NULL, 0), NAU8825_REG_CLASSG_CTRL, 1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("HP amp power", NAU8825_REG_CLASSG_CTRL, 0, 0, NULL, SND_SOC_DAPM_PGA_S("HP amp R", 0,
0), NAU8825_REG_CLASSG_CTRL, 2, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Charge Pump", NAU8825_REG_CHARGE_PUMP, 5, 0, SND_SOC_DAPM_PGA_S("Charge Pump", 1, NAU8825_REG_CHARGE_PUMP, 5, 0,
nau8825_pump_event, SND_SOC_DAPM_POST_PMU), nau8825_pump_event, SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_PGA("Output Driver R Stage 1", SND_SOC_DAPM_PGA_S("Output Driver R Stage 1", 4,
NAU8825_REG_POWER_UP_CONTROL, 5, 0, NULL, 0), NAU8825_REG_POWER_UP_CONTROL, 5, 0, NULL, 0),
SND_SOC_DAPM_PGA("Output Driver L Stage 1", SND_SOC_DAPM_PGA_S("Output Driver L Stage 1", 4,
NAU8825_REG_POWER_UP_CONTROL, 4, 0, NULL, 0), NAU8825_REG_POWER_UP_CONTROL, 4, 0, NULL, 0),
SND_SOC_DAPM_PGA("Output Driver R Stage 2", SND_SOC_DAPM_PGA_S("Output Driver R Stage 2", 5,
NAU8825_REG_POWER_UP_CONTROL, 3, 0, NULL, 0), NAU8825_REG_POWER_UP_CONTROL, 3, 0, NULL, 0),
SND_SOC_DAPM_PGA("Output Driver L Stage 2", SND_SOC_DAPM_PGA_S("Output Driver L Stage 2", 5,
NAU8825_REG_POWER_UP_CONTROL, 2, 0, NULL, 0), NAU8825_REG_POWER_UP_CONTROL, 2, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("Output Driver R Stage 3", 1, SND_SOC_DAPM_PGA_S("Output Driver R Stage 3", 6,
NAU8825_REG_POWER_UP_CONTROL, 1, 0, NULL, 0), NAU8825_REG_POWER_UP_CONTROL, 1, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("Output Driver L Stage 3", 1, SND_SOC_DAPM_PGA_S("Output Driver L Stage 3", 6,
NAU8825_REG_POWER_UP_CONTROL, 0, 0, NULL, 0), NAU8825_REG_POWER_UP_CONTROL, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("Output DACL", 2, NAU8825_REG_CHARGE_PUMP, 8, 1, NULL, 0), SND_SOC_DAPM_PGA_S("Output DACL", 7,
SND_SOC_DAPM_PGA_S("Output DACR", 2, NAU8825_REG_CHARGE_PUMP, 9, 1, NULL, 0), NAU8825_REG_CHARGE_PUMP, 8, 1, nau8825_output_dac_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_PGA_S("Output DACR", 7,
NAU8825_REG_CHARGE_PUMP, 9, 1, nau8825_output_dac_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
/* HPOL/R are ungrounded by disabling 16 Ohm pull-downs on playback */
SND_SOC_DAPM_PGA_S("HPOL Pulldown", 8,
NAU8825_REG_HSD_CTRL, 0, 1, NULL, 0),
SND_SOC_DAPM_PGA_S("HPOR Pulldown", 8,
NAU8825_REG_HSD_CTRL, 1, 1, NULL, 0),
/* High current HPOL/R boost driver */
SND_SOC_DAPM_PGA_S("HP Boost Driver", 9,
NAU8825_REG_BOOST, 9, 1, NULL, 0),
/* Class G operation control*/
SND_SOC_DAPM_PGA_S("Class G", 10,
NAU8825_REG_CLASSG_CTRL, 0, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("HPOL"), SND_SOC_DAPM_OUTPUT("HPOL"),
SND_SOC_DAPM_OUTPUT("HPOR"), SND_SOC_DAPM_OUTPUT("HPOR"),
@ -375,24 +425,27 @@ static const struct snd_soc_dapm_route nau8825_dapm_routes[] = {
{"DACR Mux", "DACR", "DDACR"}, {"DACR Mux", "DACR", "DDACR"},
{"HP amp L", NULL, "DACL Mux"}, {"HP amp L", NULL, "DACL Mux"},
{"HP amp R", NULL, "DACR Mux"}, {"HP amp R", NULL, "DACR Mux"},
{"HP amp L", NULL, "HP amp power"}, {"Charge Pump", NULL, "HP amp L"},
{"HP amp R", NULL, "HP amp power"}, {"Charge Pump", NULL, "HP amp R"},
{"ADACL", NULL, "HP amp L"}, {"ADACL", NULL, "Charge Pump"},
{"ADACR", NULL, "HP amp R"}, {"ADACR", NULL, "Charge Pump"},
{"ADACL", NULL, "ADACL Clock"}, {"ADACL Clock", NULL, "ADACL"},
{"ADACR", NULL, "ADACR Clock"}, {"ADACR Clock", NULL, "ADACR"},
{"Output Driver L Stage 1", NULL, "ADACL"}, {"Output Driver L Stage 1", NULL, "ADACL Clock"},
{"Output Driver R Stage 1", NULL, "ADACR"}, {"Output Driver R Stage 1", NULL, "ADACR Clock"},
{"Output Driver L Stage 2", NULL, "Output Driver L Stage 1"}, {"Output Driver L Stage 2", NULL, "Output Driver L Stage 1"},
{"Output Driver R Stage 2", NULL, "Output Driver R Stage 1"}, {"Output Driver R Stage 2", NULL, "Output Driver R Stage 1"},
{"Output Driver L Stage 3", NULL, "Output Driver L Stage 2"}, {"Output Driver L Stage 3", NULL, "Output Driver L Stage 2"},
{"Output Driver R Stage 3", NULL, "Output Driver R Stage 2"}, {"Output Driver R Stage 3", NULL, "Output Driver R Stage 2"},
{"Output DACL", NULL, "Output Driver L Stage 3"}, {"Output DACL", NULL, "Output Driver L Stage 3"},
{"Output DACR", NULL, "Output Driver R Stage 3"}, {"Output DACR", NULL, "Output Driver R Stage 3"},
{"HPOL", NULL, "Output DACL"}, {"HPOL Pulldown", NULL, "Output DACL"},
{"HPOR", NULL, "Output DACR"}, {"HPOR Pulldown", NULL, "Output DACR"},
{"HPOL", NULL, "Charge Pump"}, {"HP Boost Driver", NULL, "HPOL Pulldown"},
{"HPOR", NULL, "Charge Pump"}, {"HP Boost Driver", NULL, "HPOR Pulldown"},
{"Class G", NULL, "HP Boost Driver"},
{"HPOL", NULL, "Class G"},
{"HPOR", NULL, "Class G"},
}; };
static int nau8825_hw_params(struct snd_pcm_substream *substream, static int nau8825_hw_params(struct snd_pcm_substream *substream,
@ -659,11 +712,10 @@ static int nau8825_jack_insert(struct nau8825 *nau8825)
break; break;
} }
if (type & SND_JACK_HEADPHONE) { /* Leaving HPOL/R grounded after jack insert by default. They will be
/* Unground HPL/R */ * ungrounded as part of the widget power up sequence at the beginning
regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0x3, 0); * of playback to reduce pop.
} */
return type; return type;
} }
@ -768,6 +820,8 @@ static void nau8825_init_regs(struct nau8825 *nau8825)
{ {
struct regmap *regmap = nau8825->regmap; struct regmap *regmap = nau8825->regmap;
/* Latch IIC LSB value */
regmap_write(regmap, NAU8825_REG_IIC_ADDR_SET, 0x0001);
/* Enable Bias/Vmid */ /* Enable Bias/Vmid */
regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
NAU8825_BIAS_VMID, NAU8825_BIAS_VMID); NAU8825_BIAS_VMID, NAU8825_BIAS_VMID);
@ -780,10 +834,10 @@ static void nau8825_init_regs(struct nau8825 *nau8825)
nau8825->vref_impedance << NAU8825_BIAS_VMID_SEL_SFT); nau8825->vref_impedance << NAU8825_BIAS_VMID_SEL_SFT);
/* Disable Boost Driver, Automatic Short circuit protection enable */ /* Disable Boost Driver, Automatic Short circuit protection enable */
regmap_update_bits(regmap, NAU8825_REG_BOOST, regmap_update_bits(regmap, NAU8825_REG_BOOST,
NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS | NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_DIS |
NAU8825_SHORT_SHUTDOWN_EN, NAU8825_HP_BOOST_G_DIS | NAU8825_SHORT_SHUTDOWN_EN,
NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS | NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_DIS |
NAU8825_SHORT_SHUTDOWN_EN); NAU8825_HP_BOOST_G_DIS | NAU8825_SHORT_SHUTDOWN_EN);
regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL, regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL,
NAU8825_JKDET_OUTPUT_EN, NAU8825_JKDET_OUTPUT_EN,
@ -822,6 +876,35 @@ static void nau8825_init_regs(struct nau8825 *nau8825)
NAU8825_ADC_SYNC_DOWN_MASK, NAU8825_ADC_SYNC_DOWN_128); NAU8825_ADC_SYNC_DOWN_MASK, NAU8825_ADC_SYNC_DOWN_128);
regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1, regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1,
NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_128); NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_128);
/* Disable DACR/L power */
regmap_update_bits(regmap, NAU8825_REG_CHARGE_PUMP,
NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL,
NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL);
/* Enable TESTDAC. This sets the analog DAC inputs to a '0' input
* signal to avoid any glitches due to power up transients in both
* the analog and digital DAC circuit.
*/
regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN);
/* CICCLP off */
regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1,
NAU8825_DAC_CLIP_OFF, NAU8825_DAC_CLIP_OFF);
/* Class AB bias current to 2x, DAC Capacitor enable MSB/LSB */
regmap_update_bits(regmap, NAU8825_REG_ANALOG_CONTROL_2,
NAU8825_HP_NON_CLASSG_CURRENT_2xADJ |
NAU8825_DAC_CAPACITOR_MSB | NAU8825_DAC_CAPACITOR_LSB,
NAU8825_HP_NON_CLASSG_CURRENT_2xADJ |
NAU8825_DAC_CAPACITOR_MSB | NAU8825_DAC_CAPACITOR_LSB);
/* Class G timer 64ms */
regmap_update_bits(regmap, NAU8825_REG_CLASSG_CTRL,
NAU8825_CLASSG_TIMER_MASK,
0x20 << NAU8825_CLASSG_TIMER_SFT);
/* DAC clock delay 2ns, VREF */
regmap_update_bits(regmap, NAU8825_REG_RDAC,
NAU8825_RDAC_CLK_DELAY_MASK | NAU8825_RDAC_VREF_MASK,
(0x2 << NAU8825_RDAC_CLK_DELAY_SFT) |
(0x3 << NAU8825_RDAC_VREF_SFT));
} }
static const struct regmap_config nau8825_regmap_config = { static const struct regmap_config nau8825_regmap_config = {

View File

@ -14,6 +14,7 @@
#define NAU8825_REG_RESET 0x00 #define NAU8825_REG_RESET 0x00
#define NAU8825_REG_ENA_CTRL 0x01 #define NAU8825_REG_ENA_CTRL 0x01
#define NAU8825_REG_IIC_ADDR_SET 0x02
#define NAU8825_REG_CLK_DIVIDER 0x03 #define NAU8825_REG_CLK_DIVIDER 0x03
#define NAU8825_REG_FLL1 0x04 #define NAU8825_REG_FLL1 0x04
#define NAU8825_REG_FLL2 0x05 #define NAU8825_REG_FLL2 0x05
@ -129,7 +130,7 @@
/* HSD_CTRL (0xc) */ /* HSD_CTRL (0xc) */
#define NAU8825_HSD_AUTO_MODE (1 << 6) #define NAU8825_HSD_AUTO_MODE (1 << 6)
/* 0 - short to GND, 1 - open */ /* 0 - open, 1 - short to GND */
#define NAU8825_SPKR_DWN1R (1 << 1) #define NAU8825_SPKR_DWN1R (1 << 1)
#define NAU8825_SPKR_DWN1L (1 << 0) #define NAU8825_SPKR_DWN1L (1 << 0)
@ -251,12 +252,18 @@
/* DACR_CTRL (0x34) */ /* DACR_CTRL (0x34) */
#define NAU8825_DACR_CH_SEL_SFT 9 #define NAU8825_DACR_CH_SEL_SFT 9
/* CLASSG_CTRL (0x50) */
#define NAU8825_CLASSG_TIMER_SFT 8
#define NAU8825_CLASSG_TIMER_MASK (0x3f << NAU8825_CLASSG_TIMER_SFT)
#define NAU8825_CLASSG_EN (1 << 0)
/* I2C_DEVICE_ID (0x58) */ /* I2C_DEVICE_ID (0x58) */
#define NAU8825_GPIO2JD1 (1 << 7) #define NAU8825_GPIO2JD1 (1 << 7)
#define NAU8825_SOFTWARE_ID_MASK 0x3 #define NAU8825_SOFTWARE_ID_MASK 0x3
#define NAU8825_SOFTWARE_ID_NAU8825 0x0 #define NAU8825_SOFTWARE_ID_NAU8825 0x0
/* BIAS_ADJ (0x66) */ /* BIAS_ADJ (0x66) */
#define NAU8825_BIAS_TESTDAC_EN (0x3 << 8)
#define NAU8825_BIAS_VMID (1 << 6) #define NAU8825_BIAS_VMID (1 << 6)
#define NAU8825_BIAS_VMID_SEL_SFT 4 #define NAU8825_BIAS_VMID_SEL_SFT 4
#define NAU8825_BIAS_VMID_SEL_MASK (3 << NAU8825_BIAS_VMID_SEL_SFT) #define NAU8825_BIAS_VMID_SEL_MASK (3 << NAU8825_BIAS_VMID_SEL_SFT)
@ -274,6 +281,12 @@
#define NAU8825_ADC_VREFSEL_VMID_PLUS_1DB (3 << 8) #define NAU8825_ADC_VREFSEL_VMID_PLUS_1DB (3 << 8)
#define NAU8825_POWERUP_ADCL (1 << 6) #define NAU8825_POWERUP_ADCL (1 << 6)
/* RDAC (0x73) */
#define NAU8825_RDAC_CLK_DELAY_SFT 4
#define NAU8825_RDAC_CLK_DELAY_MASK (0x7 << NAU8825_RDAC_CLK_DELAY_SFT)
#define NAU8825_RDAC_VREF_SFT 2
#define NAU8825_RDAC_VREF_MASK (0x3 << NAU8825_RDAC_VREF_SFT)
/* MIC_BIAS (0x74) */ /* MIC_BIAS (0x74) */
#define NAU8825_MICBIAS_JKSLV (1 << 14) #define NAU8825_MICBIAS_JKSLV (1 << 14)
#define NAU8825_MICBIAS_JKR2 (1 << 12) #define NAU8825_MICBIAS_JKR2 (1 << 12)
@ -284,6 +297,7 @@
/* BOOST (0x76) */ /* BOOST (0x76) */
#define NAU8825_PRECHARGE_DIS (1 << 13) #define NAU8825_PRECHARGE_DIS (1 << 13)
#define NAU8825_GLOBAL_BIAS_EN (1 << 12) #define NAU8825_GLOBAL_BIAS_EN (1 << 12)
#define NAU8825_HP_BOOST_DIS (1 << 9)
#define NAU8825_HP_BOOST_G_DIS (1 << 8) #define NAU8825_HP_BOOST_G_DIS (1 << 8)
#define NAU8825_SHORT_SHUTDOWN_EN (1 << 6) #define NAU8825_SHORT_SHUTDOWN_EN (1 << 6)

View File

@ -0,0 +1,73 @@
/*
* PCM179X ASoC I2C driver
*
* Copyright (c) Teenage Engineering AB 2016
*
* Jacob Siverskog <jacob@teenage.engineering>
*
* 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.
*/
#include <linux/module.h>
#include <linux/of.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include "pcm179x.h"
static int pcm179x_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct regmap *regmap;
int ret;
regmap = devm_regmap_init_i2c(client, &pcm179x_regmap_config);
if (IS_ERR(regmap)) {
ret = PTR_ERR(regmap);
dev_err(&client->dev, "Failed to allocate regmap: %d\n", ret);
return ret;
}
return pcm179x_common_init(&client->dev, regmap);
}
static int pcm179x_i2c_remove(struct i2c_client *client)
{
return pcm179x_common_exit(&client->dev);
}
static const struct of_device_id pcm179x_of_match[] = {
{ .compatible = "ti,pcm1792a", },
{ }
};
MODULE_DEVICE_TABLE(of, pcm179x_of_match);
static const struct i2c_device_id pcm179x_i2c_ids[] = {
{ "pcm179x", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, pcm179x_i2c_ids);
static struct i2c_driver pcm179x_i2c_driver = {
.driver = {
.name = "pcm179x",
.of_match_table = of_match_ptr(pcm179x_of_match),
},
.id_table = pcm179x_i2c_ids,
.probe = pcm179x_i2c_probe,
.remove = pcm179x_i2c_remove,
};
module_i2c_driver(pcm179x_i2c_driver);
MODULE_DESCRIPTION("ASoC PCM179X I2C driver");
MODULE_AUTHOR("Jacob Siverskog <jacob@teenage.engineering>");
MODULE_LICENSE("GPL");

Some files were not shown because too many files have changed in this diff Show More