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:
commit
021f163d69
|
@ -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>;
|
||||
};
|
||||
};
|
|
@ -24,6 +24,9 @@ The compatible list for this generic sound card currently:
|
|||
|
||||
"fsl,imx-audio-cs42888"
|
||||
|
||||
"fsl,imx-audio-cs427x"
|
||||
(compatible with CS4271 and CS4272)
|
||||
|
||||
"fsl,imx-audio-wm8962"
|
||||
(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
|
||||
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:
|
||||
sound-cs42888 {
|
||||
compatible = "fsl,imx-audio-cs42888";
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
|
@ -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>;
|
||||
};
|
|
@ -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>;
|
||||
};
|
||||
|
|
@ -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>;
|
||||
};
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
Texas Instruments pcm179x DT bindings
|
||||
|
||||
This driver supports the SPI bus.
|
||||
This driver supports both the I2C and SPI bus.
|
||||
|
||||
Required properties:
|
||||
|
||||
|
@ -9,6 +9,11 @@ Required properties:
|
|||
For required properties on SPI, please consult
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
|
||||
Required properties on I2C:
|
||||
|
||||
- reg: the I2C address
|
||||
|
||||
|
||||
Examples:
|
||||
|
||||
codec_spi: 1792a@0 {
|
||||
|
@ -16,3 +21,7 @@ Examples:
|
|||
spi-max-frequency = <600000>;
|
||||
};
|
||||
|
||||
codec_i2c: 1792a@4c {
|
||||
compatible = "ti,pcm1792a";
|
||||
reg = <0x4c>;
|
||||
};
|
||||
|
|
|
@ -1,6 +1,337 @@
|
|||
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:
|
||||
=============================================
|
||||
|
||||
- compatible : "renesas,rcar_sound-<soctype>", fallbacks
|
||||
"renesas,rcar_sound-gen1" if generation1, and
|
||||
"renesas,rcar_sound-gen2" if generation2
|
||||
|
@ -64,7 +395,10 @@ DAI subnode properties:
|
|||
- playback : list of playback modules
|
||||
- capture : list of capture modules
|
||||
|
||||
|
||||
=============================================
|
||||
Example:
|
||||
=============================================
|
||||
|
||||
rcar_sound: sound@ec500000 {
|
||||
#sound-dai-cells = <1>;
|
||||
|
@ -250,7 +584,9 @@ rcar_sound: sound@ec500000 {
|
|||
};
|
||||
};
|
||||
|
||||
=============================================
|
||||
Example: simple sound card
|
||||
=============================================
|
||||
|
||||
rsnd_ak4643: sound {
|
||||
compatible = "simple-audio-card";
|
||||
|
@ -290,7 +626,9 @@ Example: simple sound card
|
|||
shared-pin;
|
||||
};
|
||||
|
||||
=============================================
|
||||
Example: simple sound card for TDM
|
||||
=============================================
|
||||
|
||||
rsnd_tdm: sound {
|
||||
compatible = "simple-audio-card";
|
||||
|
@ -309,7 +647,9 @@ Example: simple sound card for TDM
|
|||
};
|
||||
};
|
||||
|
||||
=============================================
|
||||
Example: simple sound card for Multi channel
|
||||
=============================================
|
||||
|
||||
&rcar_sound {
|
||||
pinctrl-0 = <&sound_pins &sound_clk_pins>;
|
||||
|
|
|
@ -30,6 +30,7 @@ Optional subnode properties:
|
|||
- frame-inversion : bool property. Add this if the
|
||||
dai-link uses frame clock inversion.
|
||||
- convert-rate : platform specified sampling rate convert
|
||||
- convert-channels : platform specified converted channel size (2 - 8 ch)
|
||||
- audio-prefix : see audio-routing
|
||||
- audio-routing : A list of the connections between audio components.
|
||||
Each entry is a pair of strings, the first being the connection's sink,
|
||||
|
|
|
@ -9,6 +9,7 @@ Required properties:
|
|||
- "rockchip,rk3066-i2s": for rk3066
|
||||
- "rockchip,rk3188-i2s", "rockchip,rk3066-i2s": for rk3188
|
||||
- "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
|
||||
region.
|
||||
- interrupts: should contain the I2S interrupt.
|
||||
|
|
|
@ -7,8 +7,12 @@ a fibre cable.
|
|||
Required properties:
|
||||
|
||||
- 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
|
||||
region.
|
||||
- interrupts: should contain the SPDIF interrupt.
|
||||
|
|
|
@ -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>;
|
||||
};
|
|
@ -8,6 +8,12 @@ Required properties:
|
|||
|
||||
- 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:
|
||||
|
||||
* IN1P
|
||||
|
|
|
@ -12,6 +12,9 @@ Required properties:
|
|||
|
||||
Optional properties:
|
||||
|
||||
- clocks: The phandle of the master clock to the CODEC
|
||||
- clock-names: Should be "mclk"
|
||||
|
||||
- realtek,in1-differential
|
||||
- realtek,in2-differential
|
||||
- realtek,in3-differential
|
||||
|
|
|
@ -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";
|
||||
};
|
|
@ -0,0 +1,11 @@
|
|||
Texas Intstruments ADS117x ADC
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "ti,ads1174" or "ti,ads1178"
|
||||
|
||||
Example:
|
||||
|
||||
ads1178 {
|
||||
compatible = "ti,ads1178";
|
||||
};
|
|
@ -1910,6 +1910,12 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
|||
- Default: 0x0000
|
||||
ignore_ctl_error - Ignore any USB-controller regarding mixer
|
||||
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.
|
||||
|
||||
|
@ -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
|
||||
the mixer element such as URB error -22. This happens on some
|
||||
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
|
||||
--------------------
|
||||
|
|
|
@ -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
|
||||
========================
|
|
@ -34,6 +34,7 @@ struct ssc_device *ssc_request(unsigned int ssc_num)
|
|||
if (ssc->pdev->dev.of_node) {
|
||||
if (of_alias_get_id(ssc->pdev->dev.of_node, "ssc")
|
||||
== ssc_num) {
|
||||
ssc->pdev->id = ssc_num;
|
||||
ssc_valid = 1;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
int (*get_edid_block)(void *data, u8 *buf, unsigned int block,
|
||||
size_t len),
|
||||
|
|
|
@ -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.
|
||||
* Author: Lars-Peter Clausen <lars@metafoo.de>
|
||||
|
|
|
@ -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 */
|
|
@ -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_up_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
|
||||
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_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_keep_power_up(struct hdac_device *codec) { return 0; }
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
|
|
@ -72,14 +72,16 @@ enum snd_jack_types {
|
|||
#define SND_JACK_SWITCH_TYPES 6
|
||||
|
||||
struct snd_jack {
|
||||
struct input_dev *input_dev;
|
||||
struct list_head kctl_list;
|
||||
struct snd_card *card;
|
||||
const char *id;
|
||||
#ifdef CONFIG_SND_JACK_INPUT_DEV
|
||||
struct input_dev *input_dev;
|
||||
int registered;
|
||||
int type;
|
||||
const char *id;
|
||||
char name[100];
|
||||
unsigned int key[6]; /* Keep in sync with definitions above */
|
||||
#endif /* CONFIG_SND_JACK_INPUT_DEV */
|
||||
void *private_data;
|
||||
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,
|
||||
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);
|
||||
#ifdef CONFIG_SND_JACK_INPUT_DEV
|
||||
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 keytype);
|
||||
|
||||
#endif
|
||||
void snd_jack_report(struct snd_jack *jack, int status);
|
||||
|
||||
#else
|
||||
|
@ -107,6 +110,13 @@ static inline int snd_jack_add_new_kctl(struct snd_jack *jack, const char * name
|
|||
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,
|
||||
struct device *parent)
|
||||
{
|
||||
|
@ -118,11 +128,6 @@ static inline int snd_jack_set_key(struct snd_jack *jack,
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void snd_jack_report(struct snd_jack *jack, int status)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* !CONFIG_SND_JACK || !CONFIG_SND_JACK_INPUT_DEV */
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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_mask_intersect(unsigned int rates_a,
|
||||
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
|
||||
|
|
|
@ -56,12 +56,6 @@ struct snd_soc_dobj_widget {
|
|||
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 */
|
||||
struct snd_soc_dobj {
|
||||
enum snd_soc_dobj_type type;
|
||||
|
@ -71,7 +65,6 @@ struct snd_soc_dobj {
|
|||
union {
|
||||
struct snd_soc_dobj_control control;
|
||||
struct snd_soc_dobj_widget widget;
|
||||
struct snd_soc_dobj_pcm_dai pcm_dai;
|
||||
};
|
||||
void *private; /* core does not touch this */
|
||||
};
|
||||
|
@ -126,10 +119,16 @@ struct snd_soc_tplg_ops {
|
|||
int (*widget_unload)(struct snd_soc_component *,
|
||||
struct snd_soc_dobj *);
|
||||
|
||||
/* FE - used for any driver specific init */
|
||||
int (*pcm_dai_load)(struct snd_soc_component *,
|
||||
struct snd_soc_tplg_pcm_dai *pcm_dai, int num_fe);
|
||||
int (*pcm_dai_unload)(struct snd_soc_component *,
|
||||
/* FE DAI - used for any driver specific init */
|
||||
int (*dai_load)(struct snd_soc_component *,
|
||||
struct snd_soc_dai_driver *dai_drv);
|
||||
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 *);
|
||||
|
||||
/* callback to handle vendor bespoke data */
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
#include <sound/compress_driver.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/ac97_codec.h>
|
||||
#include <sound/soc-topology.h>
|
||||
|
||||
/*
|
||||
* Convenience kcontrol builders
|
||||
|
@ -404,6 +403,7 @@ struct snd_soc_jack_zone;
|
|||
struct snd_soc_jack_pin;
|
||||
#include <sound/soc-dapm.h>
|
||||
#include <sound/soc-dpcm.h>
|
||||
#include <sound/soc-topology.h>
|
||||
|
||||
struct snd_soc_jack_gpio;
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include <sound/asound.h>
|
||||
|
||||
/** 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
|
||||
|
@ -357,7 +357,9 @@ struct snd_seq_client_info {
|
|||
unsigned char event_filter[32]; /* event filter bitmap */
|
||||
int num_ports; /* RO: number of ports */
|
||||
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_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_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_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_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)
|
||||
|
|
|
@ -23,7 +23,11 @@
|
|||
#ifndef _UAPI__SOUND_ASOUND_H
|
||||
#define _UAPI__SOUND_ASOUND_H
|
||||
|
||||
#if defined(__KERNEL__) || defined(__linux__)
|
||||
#include <linux/types.h>
|
||||
#else
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
|
||||
#ifndef __KERNEL__
|
||||
#include <stdlib.h>
|
||||
|
|
|
@ -24,12 +24,15 @@ config SND_RAWMIDI
|
|||
config SND_COMPRESS_OFFLOAD
|
||||
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
|
||||
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
|
||||
tristate "Sequencer support"
|
||||
select SND_TIMER
|
||||
|
|
|
@ -69,11 +69,14 @@ struct snd_compr_file {
|
|||
|
||||
/*
|
||||
* 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_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.
|
||||
* 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
|
||||
* decoding/encoding and rendering/capturing data.
|
||||
* 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);
|
||||
/* write is allowed when stream is running or has been steup */
|
||||
if (stream->runtime->state != SNDRV_PCM_STATE_SETUP &&
|
||||
stream->runtime->state != SNDRV_PCM_STATE_PREPARED &&
|
||||
stream->runtime->state != SNDRV_PCM_STATE_RUNNING) {
|
||||
mutex_unlock(&stream->device->lock);
|
||||
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
|
||||
* drain complete notfication from the driver
|
||||
* drain complete notification from the driver
|
||||
*
|
||||
* 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
|
||||
|
@ -755,7 +759,7 @@ static int snd_compr_next_track(struct snd_compr_stream *stream)
|
|||
if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
|
||||
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
|
||||
*/
|
||||
if (stream->metadata_set == false)
|
||||
|
|
|
@ -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);
|
||||
if (! kctl) {
|
||||
up_read(&card->controls_rwsem);
|
||||
return -ENXIO;
|
||||
return -ENOENT;
|
||||
}
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (info == NULL) {
|
||||
|
|
|
@ -32,6 +32,7 @@ struct snd_jack_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] = {
|
||||
SW_HEADPHONE_INSERT,
|
||||
SW_MICROPHONE_INSERT,
|
||||
|
@ -40,9 +41,11 @@ static int jack_switch_types[SND_JACK_SWITCH_TYPES] = {
|
|||
SW_VIDEOOUT_INSERT,
|
||||
SW_LINEIN_INSERT,
|
||||
};
|
||||
#endif /* CONFIG_SND_JACK_INPUT_DEV */
|
||||
|
||||
static int snd_jack_dev_disconnect(struct snd_device *device)
|
||||
{
|
||||
#ifdef CONFIG_SND_JACK_INPUT_DEV
|
||||
struct snd_jack *jack = device->device_data;
|
||||
|
||||
if (!jack->input_dev)
|
||||
|
@ -55,6 +58,7 @@ static int snd_jack_dev_disconnect(struct snd_device *device)
|
|||
else
|
||||
input_free_device(jack->input_dev);
|
||||
jack->input_dev = NULL;
|
||||
#endif /* CONFIG_SND_JACK_INPUT_DEV */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -79,6 +83,7 @@ static int snd_jack_dev_free(struct snd_device *device)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SND_JACK_INPUT_DEV
|
||||
static int snd_jack_dev_register(struct snd_device *device)
|
||||
{
|
||||
struct snd_jack *jack = device->device_data;
|
||||
|
@ -116,6 +121,7 @@ static int snd_jack_dev_register(struct snd_device *device)
|
|||
|
||||
return err;
|
||||
}
|
||||
#endif /* CONFIG_SND_JACK_INPUT_DEV */
|
||||
|
||||
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_kctl *jack_kctl = NULL;
|
||||
int err;
|
||||
int i;
|
||||
static struct snd_device_ops ops = {
|
||||
.dev_free = snd_jack_dev_free,
|
||||
#ifdef CONFIG_SND_JACK_INPUT_DEV
|
||||
.dev_register = snd_jack_dev_register,
|
||||
.dev_disconnect = snd_jack_dev_disconnect,
|
||||
#endif /* CONFIG_SND_JACK_INPUT_DEV */
|
||||
};
|
||||
|
||||
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 */
|
||||
if (!phantom_jack) {
|
||||
#ifdef CONFIG_SND_JACK_INPUT_DEV
|
||||
int i;
|
||||
|
||||
jack->input_dev = input_allocate_device();
|
||||
if (jack->input_dev == NULL) {
|
||||
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,
|
||||
jack_switch_types[i]);
|
||||
|
||||
#endif /* CONFIG_SND_JACK_INPUT_DEV */
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
fail_input:
|
||||
#ifdef CONFIG_SND_JACK_INPUT_DEV
|
||||
input_free_device(jack->input_dev);
|
||||
#endif
|
||||
kfree(jack->id);
|
||||
kfree(jack);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_jack_new);
|
||||
|
||||
#ifdef CONFIG_SND_JACK_INPUT_DEV
|
||||
/**
|
||||
* 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->key[key] = keytype;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_jack_set_key);
|
||||
#endif /* CONFIG_SND_JACK_INPUT_DEV */
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
struct snd_jack_kctl *jack_kctl;
|
||||
#ifdef CONFIG_SND_JACK_INPUT_DEV
|
||||
int i;
|
||||
#endif
|
||||
|
||||
if (!jack)
|
||||
return;
|
||||
|
@ -349,6 +365,7 @@ void snd_jack_report(struct snd_jack *jack, int status)
|
|||
snd_kctl_jack_report(jack->card, jack_kctl->kctl,
|
||||
status & jack_kctl->mask_bits);
|
||||
|
||||
#ifdef CONFIG_SND_JACK_INPUT_DEV
|
||||
if (!jack->input_dev)
|
||||
return;
|
||||
|
||||
|
@ -369,6 +386,6 @@ void snd_jack_report(struct snd_jack *jack, int status)
|
|||
}
|
||||
|
||||
input_sync(jack->input_dev);
|
||||
|
||||
#endif /* CONFIG_SND_JACK_INPUT_DEV */
|
||||
}
|
||||
EXPORT_SYMBOL(snd_jack_report);
|
||||
|
|
|
@ -322,7 +322,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
|
|||
char name[16];
|
||||
snd_pcm_debug_name(substream, name, sizeof(name));
|
||||
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,
|
||||
runtime->period_size);
|
||||
}
|
||||
|
|
|
@ -565,3 +565,33 @@ unsigned int snd_pcm_rate_mask_intersect(unsigned int rates_a,
|
|||
return rates_a & rates_b;
|
||||
}
|
||||
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);
|
||||
|
|
|
@ -364,6 +364,7 @@ static int snd_seq_open(struct inode *inode, struct file *file)
|
|||
/* fill client data */
|
||||
user->file = file;
|
||||
sprintf(client->name, "Client-%d", c);
|
||||
client->data.user.owner = get_pid(task_pid(current));
|
||||
|
||||
/* make others aware this new client */
|
||||
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);
|
||||
if (client->data.user.fifo)
|
||||
snd_seq_fifo_delete(&client->data.user.fifo);
|
||||
put_pid(client->data.user.owner);
|
||||
kfree(client);
|
||||
}
|
||||
|
||||
|
@ -1197,6 +1199,17 @@ static void get_client_info(struct snd_seq_client *cptr,
|
|||
info->event_lost = cptr->event_lost;
|
||||
memcpy(info->event_filter, cptr->event_filter, 32);
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -2271,6 +2284,7 @@ int snd_seq_create_kernel_client(struct snd_card *card, int client_index,
|
|||
|
||||
client->accept_input = 1;
|
||||
client->accept_output = 1;
|
||||
client->data.kernel.card = card;
|
||||
|
||||
va_start(args, name_fmt);
|
||||
vsnprintf(client->name, sizeof(client->name), name_fmt, args);
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
struct snd_seq_user_client {
|
||||
struct file *file; /* file struct of client */
|
||||
/* ... */
|
||||
struct pid *owner;
|
||||
|
||||
/* fifo */
|
||||
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_card *card;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -305,8 +305,6 @@ int snd_timer_open(struct snd_timer_instance **ti,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int _snd_timer_stop(struct snd_timer_instance *timeri, int event);
|
||||
|
||||
/*
|
||||
* close a timer instance
|
||||
*/
|
||||
|
@ -318,25 +316,14 @@ int snd_timer_close(struct snd_timer_instance *timeri)
|
|||
if (snd_BUG_ON(!timeri))
|
||||
return -ENXIO;
|
||||
|
||||
mutex_lock(®ister_mutex);
|
||||
list_del(&timeri->open_list);
|
||||
|
||||
/* force to stop the timer */
|
||||
snd_timer_stop(timeri);
|
||||
|
||||
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
|
||||
/* wait, until the active callback is finished */
|
||||
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(®ister_mutex);
|
||||
list_del(&timeri->open_list);
|
||||
mutex_unlock(®ister_mutex);
|
||||
} else {
|
||||
timer = timeri->timer;
|
||||
if (snd_BUG_ON(!timer))
|
||||
goto out;
|
||||
timer = timeri->timer;
|
||||
if (timer) {
|
||||
/* wait, until the active callback is finished */
|
||||
spin_lock_irq(&timer->lock);
|
||||
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_unlock_irq(&timer->lock);
|
||||
mutex_lock(®ister_mutex);
|
||||
list_del(&timeri->open_list);
|
||||
if (list_empty(&timer->open_list_head) &&
|
||||
timer->hw.close)
|
||||
timer->hw.close(timer);
|
||||
|
||||
/* remove slave links */
|
||||
spin_lock_irq(&slave_active_lock);
|
||||
spin_lock(&timer->lock);
|
||||
|
@ -363,18 +346,27 @@ int snd_timer_close(struct snd_timer_instance *timeri)
|
|||
}
|
||||
spin_unlock(&timer->lock);
|
||||
spin_unlock_irq(&slave_active_lock);
|
||||
/* release a card refcount for safe disconnection */
|
||||
if (timer->card)
|
||||
put_device(&timer->card->card_dev);
|
||||
mutex_unlock(®ister_mutex);
|
||||
|
||||
/* slave doesn't need to release timer resources below */
|
||||
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
|
||||
timer = NULL;
|
||||
}
|
||||
out:
|
||||
|
||||
if (timeri->private_free)
|
||||
timeri->private_free(timeri);
|
||||
kfree(timeri->owner);
|
||||
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);
|
||||
}
|
||||
|
||||
mutex_unlock(®ister_mutex);
|
||||
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)
|
||||
{
|
||||
struct snd_timer *timer;
|
||||
unsigned long flags;
|
||||
unsigned long resolution = 0;
|
||||
struct snd_timer_instance *ts;
|
||||
struct timespec tstamp;
|
||||
|
@ -419,34 +410,66 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
|
|||
return;
|
||||
if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
|
||||
return;
|
||||
spin_lock_irqsave(&timer->lock, flags);
|
||||
list_for_each_entry(ts, &ti->slave_active_head, active_list)
|
||||
if (ts->ccallback)
|
||||
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,
|
||||
unsigned long sticks)
|
||||
/* start/continue a master timer */
|
||||
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);
|
||||
if (timer->running) {
|
||||
if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
|
||||
goto __start_now;
|
||||
timer->flags |= SNDRV_TIMER_FLG_RESCHED;
|
||||
timeri->flags |= SNDRV_TIMER_IFLG_START;
|
||||
return 1; /* delayed start */
|
||||
result = 1; /* delayed start */
|
||||
} else {
|
||||
timer->sticks = sticks;
|
||||
if (start)
|
||||
timer->sticks = ticks;
|
||||
timer->hw.start(timer);
|
||||
__start_now:
|
||||
timer->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;
|
||||
|
||||
|
@ -460,88 +483,37 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri)
|
|||
spin_lock(&timeri->timer->lock);
|
||||
list_add_tail(&timeri->active_list,
|
||||
&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_irqrestore(&slave_active_lock, flags);
|
||||
return 1; /* delayed start */
|
||||
}
|
||||
|
||||
/*
|
||||
* start the timer instance
|
||||
*/
|
||||
int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
|
||||
/* stop/pause a master timer */
|
||||
static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop)
|
||||
{
|
||||
struct snd_timer *timer;
|
||||
int result = -EINVAL;
|
||||
int result = 0;
|
||||
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;
|
||||
if (!timer)
|
||||
return -EINVAL;
|
||||
spin_lock_irqsave(&timer->lock, flags);
|
||||
if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
|
||||
SNDRV_TIMER_IFLG_START))) {
|
||||
spin_unlock_irqrestore(&timer->lock, flags);
|
||||
return -EBUSY;
|
||||
result = -EBUSY;
|
||||
goto unlock;
|
||||
}
|
||||
list_del_init(&timeri->ack_list);
|
||||
list_del_init(&timeri->active_list);
|
||||
if (timer->card && timer->card->shutdown) {
|
||||
spin_unlock_irqrestore(&timer->lock, flags);
|
||||
return 0;
|
||||
if (timer->card && timer->card->shutdown)
|
||||
goto unlock;
|
||||
if (stop) {
|
||||
timeri->cticks = timeri->ticks;
|
||||
timeri->pticks = 0;
|
||||
}
|
||||
if ((timeri->flags & SNDRV_TIMER_IFLG_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);
|
||||
snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
|
||||
SNDRV_TIMER_EVENT_CONTINUE);
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&timer->lock, flags);
|
||||
__end:
|
||||
if (event != SNDRV_TIMER_EVENT_RESOLUTION)
|
||||
snd_timer_notify1(timeri, event);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
|
@ -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)
|
||||
{
|
||||
struct snd_timer *timer;
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
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;
|
||||
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
|
||||
return snd_timer_stop_slave(timeri, true);
|
||||
else
|
||||
return snd_timer_stop1(timeri, true);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -592,32 +589,10 @@ int snd_timer_stop(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)
|
||||
return snd_timer_start_slave(timeri);
|
||||
timer = timeri->timer;
|
||||
if (! timer)
|
||||
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;
|
||||
return snd_timer_start_slave(timeri, false);
|
||||
else
|
||||
return snd_timer_start1(timeri, false, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -625,7 +600,10 @@ int snd_timer_continue(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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -65,8 +65,6 @@ struct mts64 {
|
|||
struct snd_card *card;
|
||||
struct snd_rawmidi *rmidi;
|
||||
struct pardevice *pardev;
|
||||
int pardev_claimed;
|
||||
|
||||
int open_count;
|
||||
int current_midi_output_port;
|
||||
int current_midi_input_port;
|
||||
|
@ -850,30 +848,6 @@ __out:
|
|||
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)
|
||||
{
|
||||
struct platform_device *device;
|
||||
|
@ -907,10 +881,20 @@ static void snd_mts64_detach(struct parport *p)
|
|||
/* 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 = {
|
||||
.name = "mts64",
|
||||
.attach = snd_mts64_attach,
|
||||
.detach = snd_mts64_detach
|
||||
.name = "mts64",
|
||||
.probe = snd_mts64_dev_probe,
|
||||
.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;
|
||||
|
||||
if (pardev) {
|
||||
if (mts->pardev_claimed)
|
||||
parport_release(pardev);
|
||||
parport_release(pardev);
|
||||
parport_unregister_device(pardev);
|
||||
}
|
||||
|
||||
|
@ -938,6 +921,12 @@ static int snd_mts64_probe(struct platform_device *pdev)
|
|||
struct snd_card *card = NULL;
|
||||
struct mts64 *mts = NULL;
|
||||
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);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
@ -946,8 +935,6 @@ static int snd_mts64_probe(struct platform_device *pdev)
|
|||
return -ENODEV;
|
||||
if (!enable[dev])
|
||||
return -ENOENT;
|
||||
if ((err = snd_mts64_probe_port(p)) < 0)
|
||||
return err;
|
||||
|
||||
err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE,
|
||||
0, &card);
|
||||
|
@ -960,39 +947,41 @@ static int snd_mts64_probe(struct platform_device *pdev)
|
|||
sprintf(card->longname, "%s at 0x%lx, irq %i",
|
||||
card->shortname, p->base, p->irq);
|
||||
|
||||
pardev = parport_register_device(p, /* port */
|
||||
DRIVER_NAME, /* name */
|
||||
NULL, /* preempt */
|
||||
NULL, /* wakeup */
|
||||
snd_mts64_interrupt, /* ISR */
|
||||
PARPORT_DEV_EXCL, /* flags */
|
||||
(void *)card); /* private */
|
||||
if (pardev == NULL) {
|
||||
mts64_cb.private = card; /* private */
|
||||
pardev = parport_register_dev_model(p, /* port */
|
||||
DRIVER_NAME, /* name */
|
||||
&mts64_cb, /* callbacks */
|
||||
pdev->id); /* device number */
|
||||
if (!pardev) {
|
||||
snd_printd("Cannot register pardevice\n");
|
||||
err = -EIO;
|
||||
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 */
|
||||
if (parport_claim(pardev)) {
|
||||
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;
|
||||
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 */
|
||||
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);
|
||||
return 0;
|
||||
|
||||
release_pardev:
|
||||
parport_release(pardev);
|
||||
free_pardev:
|
||||
parport_unregister_device(pardev);
|
||||
__err:
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
|
@ -1024,7 +1017,6 @@ static int snd_mts64_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct platform_driver snd_mts64_driver = {
|
||||
.probe = snd_mts64_probe,
|
||||
.remove = snd_mts64_remove,
|
||||
|
|
|
@ -83,8 +83,6 @@ struct portman {
|
|||
struct snd_card *card;
|
||||
struct snd_rawmidi *rmidi;
|
||||
struct pardevice *pardev;
|
||||
int pardev_claimed;
|
||||
|
||||
int open_count;
|
||||
int mode[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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct platform_device *device;
|
||||
|
@ -705,10 +679,20 @@ static void snd_portman_detach(struct parport *p)
|
|||
/* 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 = {
|
||||
.name = "portman2x4",
|
||||
.attach = snd_portman_attach,
|
||||
.detach = snd_portman_detach
|
||||
.name = "portman2x4",
|
||||
.probe = snd_portman_dev_probe,
|
||||
.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;
|
||||
|
||||
if (pardev) {
|
||||
if (pm->pardev_claimed)
|
||||
parport_release(pardev);
|
||||
parport_release(pardev);
|
||||
parport_unregister_device(pardev);
|
||||
}
|
||||
|
||||
|
@ -736,6 +719,12 @@ static int snd_portman_probe(struct platform_device *pdev)
|
|||
struct snd_card *card = NULL;
|
||||
struct portman *pm = NULL;
|
||||
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);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
@ -745,9 +734,6 @@ static int snd_portman_probe(struct platform_device *pdev)
|
|||
if (!enable[dev])
|
||||
return -ENOENT;
|
||||
|
||||
if ((err = snd_portman_probe_port(p)) < 0)
|
||||
return err;
|
||||
|
||||
err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE,
|
||||
0, &card);
|
||||
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",
|
||||
card->shortname, p->base, p->irq);
|
||||
|
||||
pardev = parport_register_device(p, /* port */
|
||||
DRIVER_NAME, /* name */
|
||||
NULL, /* preempt */
|
||||
NULL, /* wakeup */
|
||||
snd_portman_interrupt, /* ISR */
|
||||
PARPORT_DEV_EXCL, /* flags */
|
||||
(void *)card); /* private */
|
||||
portman_cb.private = card; /* private */
|
||||
pardev = parport_register_dev_model(p, /* port */
|
||||
DRIVER_NAME, /* name */
|
||||
&portman_cb, /* callbacks */
|
||||
pdev->id); /* device number */
|
||||
if (pardev == NULL) {
|
||||
snd_printd("Cannot register pardevice\n");
|
||||
err = -EIO;
|
||||
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 */
|
||||
if (parport_claim(pardev)) {
|
||||
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;
|
||||
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 */
|
||||
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);
|
||||
return 0;
|
||||
|
||||
release_pardev:
|
||||
parport_release(pardev);
|
||||
free_pardev:
|
||||
parport_unregister_device(pardev);
|
||||
__err:
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
|
|
|
@ -300,6 +300,22 @@ error:
|
|||
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
|
||||
bebob_update(struct fw_unit *unit)
|
||||
{
|
||||
|
@ -309,7 +325,6 @@ bebob_update(struct fw_unit *unit)
|
|||
return;
|
||||
|
||||
fcp_bus_reset(bebob->unit);
|
||||
snd_bebob_stream_update_duplex(bebob);
|
||||
|
||||
if (bebob->deferred_registration) {
|
||||
if (snd_card_register(bebob->card) < 0) {
|
||||
|
@ -327,10 +342,6 @@ static void bebob_remove(struct fw_unit *unit)
|
|||
if (bebob == NULL)
|
||||
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. */
|
||||
snd_card_free_when_closed(bebob->card);
|
||||
}
|
||||
|
|
|
@ -88,8 +88,6 @@ struct snd_bebob {
|
|||
unsigned int midi_input_ports;
|
||||
unsigned int midi_output_ports;
|
||||
|
||||
/* for bus reset quirk */
|
||||
struct completion bus_reset;
|
||||
bool connected;
|
||||
|
||||
struct amdtp_stream *master;
|
||||
|
@ -97,7 +95,7 @@ struct snd_bebob {
|
|||
struct amdtp_stream rx_stream;
|
||||
struct cmp_connection out_conn;
|
||||
struct cmp_connection in_conn;
|
||||
atomic_t substreams_counter;
|
||||
unsigned int substreams_counter;
|
||||
|
||||
struct snd_bebob_stream_formation
|
||||
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_start_duplex(struct snd_bebob *bebob, unsigned int rate);
|
||||
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_lock_changed(struct snd_bebob *bebob);
|
||||
|
|
|
@ -17,8 +17,10 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream)
|
|||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
atomic_inc(&bebob->substreams_counter);
|
||||
mutex_lock(&bebob->mutex);
|
||||
bebob->substreams_counter++;
|
||||
err = snd_bebob_stream_start_duplex(bebob, 0);
|
||||
mutex_unlock(&bebob->mutex);
|
||||
if (err < 0)
|
||||
snd_bebob_stream_lock_release(bebob);
|
||||
end:
|
||||
|
@ -34,8 +36,10 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream)
|
|||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
atomic_inc(&bebob->substreams_counter);
|
||||
mutex_lock(&bebob->mutex);
|
||||
bebob->substreams_counter++;
|
||||
err = snd_bebob_stream_start_duplex(bebob, 0);
|
||||
mutex_unlock(&bebob->mutex);
|
||||
if (err < 0)
|
||||
snd_bebob_stream_lock_release(bebob);
|
||||
end:
|
||||
|
@ -46,8 +50,10 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream)
|
|||
{
|
||||
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);
|
||||
mutex_unlock(&bebob->mutex);
|
||||
|
||||
snd_bebob_stream_lock_release(bebob);
|
||||
return 0;
|
||||
|
@ -57,8 +63,10 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream)
|
|||
{
|
||||
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);
|
||||
mutex_unlock(&bebob->mutex);
|
||||
|
||||
snd_bebob_stream_lock_release(bebob);
|
||||
return 0;
|
||||
|
|
|
@ -218,8 +218,11 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream,
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
|
||||
atomic_inc(&bebob->substreams_counter);
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||
mutex_lock(&bebob->mutex);
|
||||
bebob->substreams_counter++;
|
||||
mutex_unlock(&bebob->mutex);
|
||||
}
|
||||
|
||||
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)
|
||||
return err;
|
||||
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
|
||||
atomic_inc(&bebob->substreams_counter);
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
|
||||
mutex_lock(&bebob->mutex);
|
||||
bebob->substreams_counter++;
|
||||
mutex_unlock(&bebob->mutex);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
|
||||
atomic_dec(&bebob->substreams_counter);
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
|
||||
mutex_lock(&bebob->mutex);
|
||||
bebob->substreams_counter--;
|
||||
mutex_unlock(&bebob->mutex);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
|
||||
atomic_dec(&bebob->substreams_counter);
|
||||
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
|
||||
mutex_lock(&bebob->mutex);
|
||||
bebob->substreams_counter--;
|
||||
mutex_unlock(&bebob->mutex);
|
||||
}
|
||||
|
||||
snd_bebob_stream_stop_duplex(bebob);
|
||||
|
||||
|
|
|
@ -549,8 +549,7 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
|
|||
destroy_both_connections(bebob);
|
||||
goto end;
|
||||
}
|
||||
/* See comments in next function */
|
||||
init_completion(&bebob->bus_reset);
|
||||
|
||||
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;
|
||||
enum cip_flags sync_mode;
|
||||
unsigned int curr_rate;
|
||||
bool updated = false;
|
||||
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 */
|
||||
if (atomic_read(&bebob->substreams_counter) == 0)
|
||||
if (bebob->substreams_counter == 0)
|
||||
goto end;
|
||||
|
||||
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);
|
||||
if (amdtp_streaming_error(slave))
|
||||
amdtp_stream_stop(slave);
|
||||
if (!updated &&
|
||||
!amdtp_stream_running(master) && !amdtp_stream_running(slave))
|
||||
if (!amdtp_stream_running(master) && !amdtp_stream_running(slave))
|
||||
break_both_connections(bebob);
|
||||
|
||||
/* stop streams if rate is different */
|
||||
|
@ -741,7 +720,6 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
|
|||
}
|
||||
}
|
||||
end:
|
||||
mutex_unlock(&bebob->mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -757,9 +735,7 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
|
|||
master = &bebob->tx_stream;
|
||||
}
|
||||
|
||||
mutex_lock(&bebob->mutex);
|
||||
|
||||
if (atomic_read(&bebob->substreams_counter) == 0) {
|
||||
if (bebob->substreams_counter == 0) {
|
||||
amdtp_stream_pcm_abort(master);
|
||||
amdtp_stream_stop(master);
|
||||
|
||||
|
@ -768,32 +744,6 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -52,10 +52,10 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
|
|||
spin_lock_irqsave(&dice->lock, flags);
|
||||
|
||||
if (up)
|
||||
amdtp_am824_midi_trigger(&dice->tx_stream,
|
||||
amdtp_am824_midi_trigger(&dice->tx_stream[0],
|
||||
substrm->number, substrm);
|
||||
else
|
||||
amdtp_am824_midi_trigger(&dice->tx_stream,
|
||||
amdtp_am824_midi_trigger(&dice->tx_stream[0],
|
||||
substrm->number, NULL);
|
||||
|
||||
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);
|
||||
|
||||
if (up)
|
||||
amdtp_am824_midi_trigger(&dice->rx_stream,
|
||||
amdtp_am824_midi_trigger(&dice->rx_stream[0],
|
||||
substrm->number, substrm);
|
||||
else
|
||||
amdtp_am824_midi_trigger(&dice->rx_stream,
|
||||
amdtp_am824_midi_trigger(&dice->rx_stream[0],
|
||||
substrm->number, NULL);
|
||||
|
||||
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)
|
||||
{
|
||||
__be32 reg;
|
||||
struct snd_rawmidi *rmidi;
|
||||
struct snd_rawmidi_str *str;
|
||||
unsigned int i, midi_in_ports, midi_out_ports;
|
||||
unsigned int midi_in_ports, midi_out_ports;
|
||||
int err;
|
||||
|
||||
midi_in_ports = midi_out_ports = 0;
|
||||
for (i = 0; i < 3; i++) {
|
||||
midi_in_ports = max(dice->tx_midi_ports[i], midi_in_ports);
|
||||
midi_out_ports = max(dice->rx_midi_ports[i], midi_out_ports);
|
||||
}
|
||||
/*
|
||||
* Use the number of MIDI conformant data channel at current sampling
|
||||
* transfer frequency.
|
||||
*/
|
||||
err = snd_dice_transaction_read_tx(dice, TX_NUMBER_MIDI,
|
||||
®, sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
midi_in_ports = be32_to_cpu(reg);
|
||||
|
||||
err = snd_dice_transaction_read_rx(dice, RX_NUMBER_MIDI,
|
||||
®, sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
midi_out_ports = be32_to_cpu(reg);
|
||||
|
||||
if (midi_in_ports + midi_out_ports == 0)
|
||||
return 0;
|
||||
|
|
|
@ -9,99 +9,46 @@
|
|||
|
||||
#include "dice.h"
|
||||
|
||||
static int dice_rate_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 *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)
|
||||
static int limit_channels_and_rates(struct snd_dice *dice,
|
||||
struct snd_pcm_runtime *runtime,
|
||||
enum amdtp_stream_direction dir,
|
||||
unsigned int index, unsigned int size)
|
||||
{
|
||||
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;
|
||||
|
||||
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;
|
||||
hw->rates |= snd_pcm_rate_to_rate_bit(rate);
|
||||
|
||||
if (pcm_channels[mode] == 0)
|
||||
continue;
|
||||
hw->channels_min = min(hw->channels_min, pcm_channels[mode]);
|
||||
hw->channels_max = max(hw->channels_max, pcm_channels[mode]);
|
||||
/*
|
||||
* Retrieve current Multi Bit Linear Audio data channel and limit to
|
||||
* it.
|
||||
*/
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
stream = &dice->tx_stream[index];
|
||||
err = snd_dice_transaction_read_tx(dice,
|
||||
size * index + TX_NUMBER_AUDIO,
|
||||
®, sizeof(reg));
|
||||
} else {
|
||||
stream = &dice->rx_stream[index];
|
||||
err = snd_dice_transaction_read_rx(dice,
|
||||
size * index + RX_NUMBER_AUDIO,
|
||||
®, 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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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_hardware *hw = &runtime->hw;
|
||||
enum amdtp_stream_direction dir;
|
||||
struct amdtp_stream *stream;
|
||||
unsigned int *pcm_channels;
|
||||
__be32 reg[2];
|
||||
unsigned int count, size;
|
||||
int err;
|
||||
|
||||
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) {
|
||||
hw->formats = AM824_IN_PCM_FORMAT_BITS;
|
||||
stream = &dice->tx_stream;
|
||||
pcm_channels = dice->tx_channels;
|
||||
dir = AMDTP_IN_STREAM;
|
||||
stream = &dice->tx_stream[substream->pcm->device];
|
||||
err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg,
|
||||
sizeof(reg));
|
||||
} else {
|
||||
hw->formats = AM824_OUT_PCM_FORMAT_BITS;
|
||||
stream = &dice->rx_stream;
|
||||
pcm_channels = dice->rx_channels;
|
||||
dir = AMDTP_OUT_STREAM;
|
||||
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);
|
||||
|
||||
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
|
||||
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;
|
||||
return amdtp_am824_add_pcm_hw_constraints(stream, runtime);
|
||||
}
|
||||
|
||||
static int pcm_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_dice *dice = substream->private_data;
|
||||
unsigned int source, rate;
|
||||
bool internal;
|
||||
int err;
|
||||
|
||||
err = snd_dice_stream_lock_try(dice);
|
||||
|
@ -176,39 +125,6 @@ static int pcm_open(struct snd_pcm_substream *substream)
|
|||
if (err < 0)
|
||||
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);
|
||||
end:
|
||||
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_dice *dice = substream->private_data;
|
||||
struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
|
||||
int err;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
amdtp_am824_set_pcm_format(&dice->tx_stream, params_format(hw_params));
|
||||
amdtp_am824_set_pcm_format(stream, params_format(hw_params));
|
||||
|
||||
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_dice *dice = substream->private_data;
|
||||
struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
|
||||
int err;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
amdtp_am824_set_pcm_format(&dice->rx_stream, params_format(hw_params));
|
||||
amdtp_am824_set_pcm_format(stream, params_format(hw_params));
|
||||
|
||||
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)
|
||||
{
|
||||
struct snd_dice *dice = substream->private_data;
|
||||
struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
|
||||
int err;
|
||||
|
||||
mutex_lock(&dice->mutex);
|
||||
err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
|
||||
mutex_unlock(&dice->mutex);
|
||||
if (err >= 0)
|
||||
amdtp_stream_pcm_prepare(&dice->tx_stream);
|
||||
amdtp_stream_pcm_prepare(stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_dice *dice = substream->private_data;
|
||||
struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
|
||||
int err;
|
||||
|
||||
mutex_lock(&dice->mutex);
|
||||
err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
|
||||
mutex_unlock(&dice->mutex);
|
||||
if (err >= 0)
|
||||
amdtp_stream_pcm_prepare(&dice->rx_stream);
|
||||
amdtp_stream_pcm_prepare(stream);
|
||||
|
||||
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)
|
||||
{
|
||||
struct snd_dice *dice = substream->private_data;
|
||||
struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
amdtp_stream_pcm_trigger(&dice->tx_stream, substream);
|
||||
amdtp_stream_pcm_trigger(stream, substream);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
amdtp_stream_pcm_trigger(&dice->tx_stream, NULL);
|
||||
amdtp_stream_pcm_trigger(stream, NULL);
|
||||
break;
|
||||
default:
|
||||
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)
|
||||
{
|
||||
struct snd_dice *dice = substream->private_data;
|
||||
struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
amdtp_stream_pcm_trigger(&dice->rx_stream, substream);
|
||||
amdtp_stream_pcm_trigger(stream, substream);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
amdtp_stream_pcm_trigger(&dice->rx_stream, NULL);
|
||||
amdtp_stream_pcm_trigger(stream, NULL);
|
||||
break;
|
||||
default:
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
|
@ -402,29 +326,53 @@ int snd_dice_create_pcm(struct snd_dice *dice)
|
|||
.page = snd_pcm_lib_get_vmalloc_page,
|
||||
.mmap = snd_pcm_lib_mmap_vmalloc,
|
||||
};
|
||||
__be32 reg;
|
||||
struct snd_pcm *pcm;
|
||||
unsigned int i, capture, playback;
|
||||
unsigned int i, max_capture, max_playback, capture, playback;
|
||||
int err;
|
||||
|
||||
capture = playback = 0;
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (dice->tx_channels[i] > 0)
|
||||
capture = 1;
|
||||
if (dice->rx_channels[i] > 0)
|
||||
playback = 1;
|
||||
/* Check whether PCM substreams are required. */
|
||||
if (dice->force_two_pcms) {
|
||||
max_capture = max_playback = 2;
|
||||
} else {
|
||||
max_capture = max_playback = 0;
|
||||
err = snd_dice_transaction_read_tx(dice, TX_NUMBER, ®,
|
||||
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, ®,
|
||||
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);
|
||||
if (err < 0)
|
||||
return err;
|
||||
pcm->private_data = dice;
|
||||
strcpy(pcm->name, dice->card->shortname);
|
||||
for (i = 0; i < MAX_STREAMS; i++) {
|
||||
capture = playback = 0;
|
||||
if (i < max_capture)
|
||||
capture = 1;
|
||||
if (i < max_playback)
|
||||
playback = 1;
|
||||
if (capture == 0 && playback == 0)
|
||||
break;
|
||||
|
||||
if (capture > 0)
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
|
||||
err = snd_pcm_new(dice->card, "DICE", i, playback, capture,
|
||||
&pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
pcm->private_data = dice;
|
||||
strcpy(pcm->name, dice->card->shortname);
|
||||
|
||||
if (playback > 0)
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
|
||||
if (capture > 0)
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,12 @@
|
|||
#include "dice.h"
|
||||
|
||||
#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] = {
|
||||
/* mode 0 */
|
||||
|
@ -24,96 +30,126 @@ const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
|
|||
[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++) {
|
||||
if (!(dice->clock_caps & BIT(i)))
|
||||
continue;
|
||||
if (snd_dice_rates[i] != rate)
|
||||
continue;
|
||||
err = snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT,
|
||||
®, sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
*mode = (i - 1) / 2;
|
||||
return 0;
|
||||
if (completion_done(&dice->clock_accepted))
|
||||
reinit_completion(&dice->clock_accepted);
|
||||
|
||||
err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
|
||||
®, 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,
|
||||
struct fw_iso_resources *resources)
|
||||
static int get_register_params(struct snd_dice *dice,
|
||||
struct reg_params *tx_params,
|
||||
struct reg_params *rx_params)
|
||||
{
|
||||
__be32 channel;
|
||||
__be32 reg[2];
|
||||
int err;
|
||||
|
||||
/* Reset channel number */
|
||||
channel = cpu_to_be32((u32)-1);
|
||||
if (resources == &dice->tx_resources)
|
||||
snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
|
||||
&channel, sizeof(channel));
|
||||
else
|
||||
snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
|
||||
&channel, sizeof(channel));
|
||||
err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg, sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
tx_params->count =
|
||||
min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
|
||||
tx_params->size = be32_to_cpu(reg[1]) * 4;
|
||||
|
||||
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,
|
||||
®, sizeof(reg));
|
||||
} else {
|
||||
snd_dice_transaction_write_rx(dice,
|
||||
params->size * i + RX_ISOCHRONOUS,
|
||||
®, sizeof(reg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int keep_resources(struct snd_dice *dice,
|
||||
struct fw_iso_resources *resources,
|
||||
unsigned int max_payload_bytes)
|
||||
{
|
||||
__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)
|
||||
enum amdtp_stream_direction dir, unsigned int index,
|
||||
unsigned int rate, unsigned int pcm_chs,
|
||||
unsigned int midi_ports)
|
||||
{
|
||||
struct amdtp_stream *stream;
|
||||
struct fw_iso_resources *resources;
|
||||
unsigned int i, mode, pcm_chs, midi_ports;
|
||||
bool double_pcm_frames;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
if (stream == &dice->tx_stream) {
|
||||
resources = &dice->tx_resources;
|
||||
pcm_chs = dice->tx_channels[mode];
|
||||
midi_ports = dice->tx_midi_ports[mode];
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
stream = &dice->tx_stream[index];
|
||||
resources = &dice->tx_resources[index];
|
||||
} else {
|
||||
resources = &dice->rx_resources;
|
||||
pcm_chs = dice->rx_channels[mode];
|
||||
midi_ports = dice->rx_midi_ports[mode];
|
||||
stream = &dice->rx_stream[index];
|
||||
resources = &dice->rx_resources[index];
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
* be aligned to SYT_INTERVAL.
|
||||
*/
|
||||
double_pcm_frames = mode > 1;
|
||||
double_pcm_frames = rate > 96000;
|
||||
if (double_pcm_frames) {
|
||||
rate /= 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,
|
||||
double_pcm_frames);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
return err;
|
||||
|
||||
if (double_pcm_frames) {
|
||||
pcm_chs /= 2;
|
||||
|
@ -147,158 +183,201 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
|
|||
}
|
||||
}
|
||||
|
||||
err = keep_resources(dice, resources,
|
||||
amdtp_stream_get_max_payload(stream));
|
||||
if (err < 0) {
|
||||
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;
|
||||
return fw_iso_resources_allocate(resources,
|
||||
amdtp_stream_get_max_payload(stream),
|
||||
fw_parent_device(dice->unit)->max_speed);
|
||||
}
|
||||
|
||||
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;
|
||||
int err;
|
||||
|
||||
err = snd_dice_transaction_get_clock_source(dice, &source);
|
||||
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;
|
||||
__be32 reg[2];
|
||||
unsigned int i, pcm_chs, midi_ports;
|
||||
struct amdtp_stream *streams;
|
||||
struct fw_iso_resources *resources;
|
||||
int err = 0;
|
||||
|
||||
if (dice->substreams_counter == 0)
|
||||
goto end;
|
||||
|
||||
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;
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
streams = dice->tx_stream;
|
||||
resources = dice->tx_resources;
|
||||
} else {
|
||||
master = &dice->rx_stream;
|
||||
slave = &dice->tx_stream;
|
||||
streams = dice->rx_stream;
|
||||
resources = dice->rx_resources;
|
||||
}
|
||||
|
||||
/* Some packet queueing errors. */
|
||||
if (amdtp_streaming_error(master) || amdtp_streaming_error(slave))
|
||||
stop_stream(dice, master);
|
||||
for (i = 0; i < params->count; i++) {
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
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);
|
||||
if (err < 0) {
|
||||
dev_err(&dice->unit->device,
|
||||
"fail to get sampling rate\n");
|
||||
goto end;
|
||||
return err;
|
||||
}
|
||||
if (rate == 0)
|
||||
rate = curr_rate;
|
||||
if (rate != curr_rate)
|
||||
stop_stream(dice, master);
|
||||
return -EINVAL;
|
||||
|
||||
if (!amdtp_stream_running(master)) {
|
||||
stop_stream(dice, slave);
|
||||
/* Judge to need to restart streams. */
|
||||
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);
|
||||
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 = snd_dice_transaction_set_rate(dice, rate);
|
||||
err = ensure_phase_lock(dice);
|
||||
if (err < 0) {
|
||||
dev_err(&dice->unit->device,
|
||||
"fail to set sampling rate\n");
|
||||
goto end;
|
||||
"fail to ensure phase lock\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Start both streams. */
|
||||
err = start_stream(dice, master, rate);
|
||||
if (err < 0) {
|
||||
dev_err(&dice->unit->device,
|
||||
"fail to start AMDTP master stream\n");
|
||||
goto end;
|
||||
}
|
||||
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 = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_dice_transaction_set_enable(dice);
|
||||
if (err < 0) {
|
||||
dev_err(&dice->unit->device,
|
||||
"fail to enable interface\n");
|
||||
stop_stream(dice, master);
|
||||
stop_stream(dice, slave);
|
||||
goto end;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Wait first callbacks */
|
||||
if (!amdtp_stream_wait_callback(master, CALLBACK_TIMEOUT) ||
|
||||
!amdtp_stream_wait_callback(slave, CALLBACK_TIMEOUT)) {
|
||||
snd_dice_transaction_clear_enable(dice);
|
||||
stop_stream(dice, master);
|
||||
stop_stream(dice, slave);
|
||||
err = -ETIMEDOUT;
|
||||
for (i = 0; i < MAX_STREAMS; i++) {
|
||||
if ((i < tx_params.count &&
|
||||
!amdtp_stream_wait_callback(&dice->tx_stream[i],
|
||||
CALLBACK_TIMEOUT)) ||
|
||||
(i < rx_params.count &&
|
||||
!amdtp_stream_wait_callback(&dice->rx_stream[i],
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
struct reg_params tx_params, rx_params;
|
||||
|
||||
if (dice->substreams_counter > 0)
|
||||
return;
|
||||
|
||||
snd_dice_transaction_clear_enable(dice);
|
||||
|
||||
stop_stream(dice, &dice->tx_stream);
|
||||
stop_stream(dice, &dice->rx_stream);
|
||||
if (get_register_params(dice, &tx_params, &rx_params) == 0) {
|
||||
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;
|
||||
enum amdtp_stream_direction dir;
|
||||
int err;
|
||||
|
||||
if (stream == &dice->tx_stream) {
|
||||
resources = &dice->tx_resources;
|
||||
dir = AMDTP_IN_STREAM;
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
stream = &dice->tx_stream[index];
|
||||
resources = &dice->tx_resources[index];
|
||||
} else {
|
||||
resources = &dice->rx_resources;
|
||||
dir = AMDTP_OUT_STREAM;
|
||||
stream = &dice->rx_stream[index];
|
||||
resources = &dice->rx_resources[index];
|
||||
}
|
||||
|
||||
err = fw_iso_resources_init(resources, dice->unit);
|
||||
|
@ -319,14 +398,20 @@ end:
|
|||
* This function should be called before starting streams or after stopping
|
||||
* 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;
|
||||
|
||||
if (stream == &dice->tx_stream)
|
||||
resources = &dice->tx_resources;
|
||||
else
|
||||
resources = &dice->rx_resources;
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
stream = &dice->tx_stream[index];
|
||||
resources = &dice->tx_resources[index];
|
||||
} else {
|
||||
stream = &dice->rx_stream[index];
|
||||
resources = &dice->rx_resources[index];
|
||||
}
|
||||
|
||||
amdtp_stream_destroy(stream);
|
||||
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 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);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
err = init_stream(dice, &dice->rx_stream);
|
||||
if (err < 0)
|
||||
destroy_stream(dice, &dice->tx_stream);
|
||||
for (i = 0; i < MAX_STREAMS; i++) {
|
||||
err = init_stream(dice, AMDTP_OUT_STREAM, i);
|
||||
if (err < 0) {
|
||||
for (; i >= 0; i--)
|
||||
destroy_stream(dice, AMDTP_OUT_STREAM, i);
|
||||
for (i = 0; i < MAX_STREAMS; i++)
|
||||
destroy_stream(dice, AMDTP_IN_STREAM, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
end:
|
||||
return err;
|
||||
}
|
||||
|
||||
void snd_dice_stream_destroy_duplex(struct snd_dice *dice)
|
||||
{
|
||||
struct reg_params tx_params, rx_params;
|
||||
|
||||
snd_dice_transaction_clear_enable(dice);
|
||||
|
||||
destroy_stream(dice, &dice->tx_stream);
|
||||
destroy_stream(dice, &dice->rx_stream);
|
||||
if (get_register_params(dice, &tx_params, &rx_params) == 0) {
|
||||
stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
|
||||
stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
|
||||
}
|
||||
|
||||
release_resources(dice);
|
||||
|
||||
dice->substreams_counter = 0;
|
||||
}
|
||||
|
||||
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
|
||||
* 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;
|
||||
|
||||
stop_stream(dice, &dice->rx_stream);
|
||||
stop_stream(dice, &dice->tx_stream);
|
||||
|
||||
fw_iso_resources_update(&dice->rx_resources);
|
||||
fw_iso_resources_update(&dice->tx_resources);
|
||||
if (get_register_params(dice, &tx_params, &rx_params) == 0) {
|
||||
stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
|
||||
stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
|
||||
}
|
||||
}
|
||||
|
||||
static void dice_lock_changed(struct snd_dice *dice)
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
|
||||
#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,
|
||||
u64 offset)
|
||||
{
|
||||
|
@ -62,54 +60,6 @@ static unsigned int get_clock_info(struct snd_dice *dice, __be32 *info)
|
|||
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,
|
||||
unsigned int *source)
|
||||
{
|
||||
|
@ -143,10 +93,6 @@ int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate)
|
|||
end:
|
||||
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)
|
||||
{
|
||||
|
@ -210,7 +156,7 @@ static void dice_notification(struct fw_card *card, struct fw_request *request,
|
|||
|
||||
fw_send_response(card, request, RCODE_COMPLETE);
|
||||
|
||||
if (bits & NOTIFY_CLOCK_ACCEPTED)
|
||||
if (bits & NOTIFY_LOCK_CHG)
|
||||
complete(&dice->clock_accepted);
|
||||
wake_up(&dice->hwdep_wait);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ MODULE_LICENSE("GPL v2");
|
|||
|
||||
#define OUI_WEISS 0x001c6a
|
||||
#define OUI_LOUD 0x000ff2
|
||||
#define OUI_FOCUSRITE 0x00130e
|
||||
#define OUI_TCELECTRONIC 0x001486
|
||||
|
||||
#define DICE_CATEGORY_ID 0x04
|
||||
#define WEISS_CATEGORY_ID 0x00
|
||||
|
@ -20,6 +22,36 @@ MODULE_LICENSE("GPL v2");
|
|||
|
||||
#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)
|
||||
{
|
||||
struct fw_device *device = fw_parent_device(unit);
|
||||
|
@ -44,6 +76,12 @@ static int check_dice_category(struct fw_unit *unit)
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (vendor == OUI_FOCUSRITE || vendor == OUI_TCELECTRONIC) {
|
||||
if (force_two_pcm_support(unit))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (vendor == OUI_WEISS)
|
||||
category = WEISS_CATEGORY_ID;
|
||||
else if (vendor == OUI_LOUD)
|
||||
|
@ -57,65 +95,10 @@ static int check_dice_category(struct fw_unit *unit)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int highest_supported_mode_rate(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)
|
||||
static int check_clock_caps(struct snd_dice *dice)
|
||||
{
|
||||
__be32 value;
|
||||
int mode, err;
|
||||
int err;
|
||||
|
||||
/* some very old firmwares don't tell about their clock support */
|
||||
if (dice->clock_caps > 0) {
|
||||
|
@ -133,12 +116,6 @@ static int dice_read_params(struct snd_dice *dice)
|
|||
CLOCK_CAP_SOURCE_INTERNAL;
|
||||
}
|
||||
|
||||
for (mode = 2; mode >= 0; --mode) {
|
||||
err = dice_read_mode_params(dice, mode);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -211,11 +188,14 @@ static void do_registration(struct work_struct *work)
|
|||
if (err < 0)
|
||||
return;
|
||||
|
||||
if (force_two_pcm_support(dice->unit))
|
||||
dice->force_two_pcms = true;
|
||||
|
||||
err = snd_dice_transaction_init(dice);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = dice_read_params(dice);
|
||||
err = check_clock_caps(dice);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
|
|
|
@ -39,6 +39,29 @@
|
|||
#include "../lib.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_card *card;
|
||||
struct fw_unit *unit;
|
||||
|
@ -56,10 +79,6 @@ struct snd_dice {
|
|||
unsigned int rsrv_offset;
|
||||
|
||||
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;
|
||||
int owner_generation;
|
||||
|
@ -71,13 +90,15 @@ struct snd_dice {
|
|||
wait_queue_head_t hwdep_wait;
|
||||
|
||||
/* For streaming */
|
||||
struct fw_iso_resources tx_resources;
|
||||
struct fw_iso_resources rx_resources;
|
||||
struct amdtp_stream tx_stream;
|
||||
struct amdtp_stream rx_stream;
|
||||
struct fw_iso_resources tx_resources[MAX_STREAMS];
|
||||
struct fw_iso_resources rx_resources[MAX_STREAMS];
|
||||
struct amdtp_stream tx_stream[MAX_STREAMS];
|
||||
struct amdtp_stream rx_stream[MAX_STREAMS];
|
||||
bool global_enabled;
|
||||
struct completion clock_accepted;
|
||||
unsigned int substreams_counter;
|
||||
|
||||
bool force_two_pcms;
|
||||
};
|
||||
|
||||
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,
|
||||
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_set_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
|
||||
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);
|
||||
void snd_dice_stream_stop_duplex(struct snd_dice *dice);
|
||||
int snd_dice_stream_init_duplex(struct snd_dice *dice);
|
||||
|
|
|
@ -301,7 +301,10 @@ static void efw_update(struct fw_unit *unit)
|
|||
struct snd_efw *efw = dev_get_drvdata(&unit->device);
|
||||
|
||||
snd_efw_transaction_bus_reset(efw->unit);
|
||||
|
||||
mutex_lock(&efw->mutex);
|
||||
snd_efw_stream_update_duplex(efw);
|
||||
mutex_unlock(&efw->mutex);
|
||||
}
|
||||
|
||||
static void efw_remove(struct fw_unit *unit)
|
||||
|
|
|
@ -313,12 +313,10 @@ void snd_efw_stream_stop_duplex(struct snd_efw *efw)
|
|||
|
||||
void snd_efw_stream_update_duplex(struct snd_efw *efw)
|
||||
{
|
||||
if ((cmp_connection_update(&efw->out_conn) < 0) ||
|
||||
(cmp_connection_update(&efw->in_conn) < 0)) {
|
||||
mutex_lock(&efw->mutex);
|
||||
if (cmp_connection_update(&efw->out_conn) < 0 ||
|
||||
cmp_connection_update(&efw->in_conn) < 0) {
|
||||
stop_stream(efw, &efw->rx_stream);
|
||||
stop_stream(efw, &efw->tx_stream);
|
||||
mutex_unlock(&efw->mutex);
|
||||
} else {
|
||||
amdtp_stream_update(&efw->rx_stream);
|
||||
amdtp_stream_update(&efw->tx_stream);
|
||||
|
|
|
@ -26,11 +26,13 @@ struct fw_scs1x {
|
|||
u8 output_bytes;
|
||||
bool output_escaped;
|
||||
bool output_escape_high_nibble;
|
||||
struct tasklet_struct tasklet;
|
||||
struct work_struct work;
|
||||
wait_queue_head_t idle_wait;
|
||||
u8 buffer[HSS1394_MAX_PACKET_SIZE];
|
||||
bool transaction_running;
|
||||
struct fw_transaction transaction;
|
||||
unsigned int transaction_bytes;
|
||||
bool error;
|
||||
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;
|
||||
|
||||
if (rcode == RCODE_GENERATION)
|
||||
; /* TODO: retry this packet */
|
||||
if (!rcode_is_permanent_error(rcode)) {
|
||||
/* Don't retry for this data. */
|
||||
if (rcode == RCODE_COMPLETE)
|
||||
scs->transaction_bytes = 0;
|
||||
} else {
|
||||
scs->error = true;
|
||||
}
|
||||
|
||||
scs->transaction_running = false;
|
||||
tasklet_schedule(&scs->tasklet);
|
||||
schedule_work(&scs->work);
|
||||
}
|
||||
|
||||
static bool is_valid_running_status(u8 status)
|
||||
|
@ -165,9 +172,9 @@ static bool is_invalid_cmd(u8 status)
|
|||
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;
|
||||
unsigned int i;
|
||||
u8 byte;
|
||||
|
@ -177,12 +184,15 @@ static void scs_output_tasklet(unsigned long data)
|
|||
return;
|
||||
|
||||
stream = ACCESS_ONCE(scs->output);
|
||||
if (!stream) {
|
||||
if (!stream || scs->error) {
|
||||
scs->output_idle = true;
|
||||
wake_up(&scs->idle_wait);
|
||||
return;
|
||||
}
|
||||
|
||||
if (scs->transaction_bytes > 0)
|
||||
goto retry;
|
||||
|
||||
i = scs->output_bytes;
|
||||
for (;;) {
|
||||
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_escaped = false;
|
||||
|
||||
scs->transaction_bytes = i;
|
||||
retry:
|
||||
scs->transaction_running = true;
|
||||
generation = scs->fw_dev->generation;
|
||||
smp_rmb(); /* node_id vs. generation */
|
||||
fw_send_request(scs->fw_dev->card, &scs->transaction,
|
||||
TCODE_WRITE_BLOCK_REQUEST, scs->fw_dev->node_id,
|
||||
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)
|
||||
|
@ -309,9 +322,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *stream, int up)
|
|||
scs->output_bytes = 1;
|
||||
scs->output_escaped = false;
|
||||
scs->output_idle = false;
|
||||
scs->transaction_bytes = 0;
|
||||
scs->error = false;
|
||||
|
||||
ACCESS_ONCE(scs->output) = stream;
|
||||
tasklet_schedule(&scs->tasklet);
|
||||
schedule_work(&scs->work);
|
||||
} else {
|
||||
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,
|
||||
&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);
|
||||
scs->output_idle = true;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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
|
||||
CFLAGS_trace.o := -I$(src)
|
||||
|
|
|
@ -611,6 +611,22 @@ int snd_hdac_power_up_pm(struct hdac_device *codec)
|
|||
}
|
||||
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
|
||||
* @codec: the codec object
|
||||
|
|
|
@ -126,6 +126,8 @@ EXPORT_SYMBOL_GPL(snd_hdac_get_display_clk);
|
|||
*/
|
||||
static int pin2port(hda_nid_t pin_nid)
|
||||
{
|
||||
if (WARN_ON(pin_nid < 5 || pin_nid > 7))
|
||||
return -1;
|
||||
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)
|
||||
{
|
||||
struct i915_audio_component *acomp = bus->audio_component;
|
||||
int port;
|
||||
|
||||
if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate)
|
||||
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);
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
struct i915_audio_component *acomp = bus->audio_component;
|
||||
int port;
|
||||
|
||||
if (!acomp || !acomp->ops || !acomp->ops->get_eld)
|
||||
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);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_acomp_get_eld);
|
||||
|
|
|
@ -21,13 +21,16 @@
|
|||
#include <sound/hdaudio.h>
|
||||
#include <sound/hda_regmap.h>
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#define codec_is_running(codec) \
|
||||
(atomic_read(&(codec)->in_pm) || \
|
||||
!pm_runtime_suspended(&(codec)->dev))
|
||||
#else
|
||||
#define codec_is_running(codec) true
|
||||
#endif
|
||||
static int codec_pm_lock(struct hdac_device *codec)
|
||||
{
|
||||
return snd_hdac_keep_power_up(codec);
|
||||
}
|
||||
|
||||
static void codec_pm_unlock(struct hdac_device *codec, int lock)
|
||||
{
|
||||
if (lock == 1)
|
||||
snd_hdac_power_down_pm(codec);
|
||||
}
|
||||
|
||||
#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;
|
||||
int verb = get_verb(reg);
|
||||
int err;
|
||||
int pm_lock = 0;
|
||||
|
||||
if (!codec_is_running(codec) && verb != AC_VERB_GET_POWER_STATE)
|
||||
return -EAGAIN;
|
||||
if (verb != AC_VERB_GET_POWER_STATE) {
|
||||
pm_lock = codec_pm_lock(codec);
|
||||
if (pm_lock < 0)
|
||||
return -EAGAIN;
|
||||
}
|
||||
reg |= (codec->addr << 28);
|
||||
if (is_stereo_amp_verb(reg))
|
||||
return hda_reg_read_stereo_amp(codec, reg, val);
|
||||
if (verb == AC_VERB_GET_PROC_COEF)
|
||||
return hda_reg_read_coef(codec, reg, val);
|
||||
if (is_stereo_amp_verb(reg)) {
|
||||
err = hda_reg_read_stereo_amp(codec, reg, val);
|
||||
goto out;
|
||||
}
|
||||
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)
|
||||
reg &= ~AC_AMP_FAKE_MUTE;
|
||||
|
||||
err = snd_hdac_exec_verb(codec, reg, 0, val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
goto out;
|
||||
/* special handling for asymmetric reads */
|
||||
if (verb == AC_VERB_GET_POWER_STATE) {
|
||||
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 */
|
||||
*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)
|
||||
|
@ -267,6 +280,7 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
|
|||
struct hdac_device *codec = context;
|
||||
unsigned int verb;
|
||||
int i, bytes, err;
|
||||
int pm_lock = 0;
|
||||
|
||||
if (codec->caps_overwriting)
|
||||
return 0;
|
||||
|
@ -275,14 +289,21 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
|
|||
reg |= (codec->addr << 28);
|
||||
verb = get_verb(reg);
|
||||
|
||||
if (!codec_is_running(codec) && verb != AC_VERB_SET_POWER_STATE)
|
||||
return codec->lazy_cache ? 0 : -EAGAIN;
|
||||
if (verb != AC_VERB_SET_POWER_STATE) {
|
||||
pm_lock = codec_pm_lock(codec);
|
||||
if (pm_lock < 0)
|
||||
return codec->lazy_cache ? 0 : -EAGAIN;
|
||||
}
|
||||
|
||||
if (is_stereo_amp_verb(reg))
|
||||
return hda_reg_write_stereo_amp(codec, reg, val);
|
||||
if (is_stereo_amp_verb(reg)) {
|
||||
err = hda_reg_write_stereo_amp(codec, reg, val);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (verb == AC_VERB_SET_PROC_COEF)
|
||||
return hda_reg_write_coef(codec, reg, val);
|
||||
if (verb == AC_VERB_SET_PROC_COEF) {
|
||||
err = hda_reg_write_coef(codec, reg, val);
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (verb & 0xf00) {
|
||||
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);
|
||||
err = snd_hdac_exec_verb(codec, reg, 0, NULL);
|
||||
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 = {
|
||||
|
|
|
@ -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);
|
|
@ -23,17 +23,5 @@ config SND_SGI_HAL2
|
|||
help
|
||||
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
|
||||
|
||||
|
|
|
@ -2,11 +2,9 @@
|
|||
# Makefile for ALSA
|
||||
#
|
||||
|
||||
snd-au1x00-objs := au1x00.o
|
||||
snd-sgi-o2-objs := sgio2audio.o ad1843.o
|
||||
snd-sgi-hal2-objs := hal2.o
|
||||
|
||||
# Toplevel Module Dependency
|
||||
obj-$(CONFIG_SND_AU1X00) += snd-au1x00.o
|
||||
obj-$(CONFIG_SND_SGI_O2) += snd-sgi-o2.o
|
||||
obj-$(CONFIG_SND_SGI_HAL2) += snd-sgi-hal2.o
|
||||
|
|
|
@ -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);
|
|
@ -866,7 +866,7 @@ config SND_VIRTUOSO
|
|||
select SND_OXYGEN_LIB
|
||||
select SND_PCM
|
||||
select SND_MPU401_UART
|
||||
select SND_JACK if INPUT=y || INPUT=SND
|
||||
select SND_JACK
|
||||
help
|
||||
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,
|
||||
|
|
|
@ -4,7 +4,7 @@ config SND_HDA
|
|||
tristate
|
||||
select SND_PCM
|
||||
select SND_VMASTER
|
||||
select SND_JACK if INPUT=y || INPUT=SND
|
||||
select SND_JACK
|
||||
select SND_HDA_CORE
|
||||
|
||||
config SND_HDA_INTEL
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <sound/hda_chmap.h>
|
||||
#include "hda_codec.h"
|
||||
#include "hda_local.h"
|
||||
|
||||
|
@ -42,20 +43,6 @@ enum cea_edid_versions {
|
|||
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] = {
|
||||
"HDMI",
|
||||
"DisplayPort",
|
||||
|
@ -419,18 +406,6 @@ static void hdmi_show_short_audio_desc(struct hda_codec *codec,
|
|||
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)
|
||||
{
|
||||
int i;
|
||||
|
@ -441,7 +416,7 @@ void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e)
|
|||
|
||||
if (e->spk_alloc) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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, "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, "sad_count\t\t%d\n", e->sad_count);
|
||||
|
|
|
@ -2145,7 +2145,7 @@ static int azx_probe_continue(struct azx *chip)
|
|||
azx_add_card_list(chip);
|
||||
snd_hda_set_power_save(&chip->bus, power_save * 1000);
|
||||
if (azx_has_pm_runtime(chip) || hda->use_vga_switcheroo)
|
||||
pm_runtime_put_noidle(&pci->dev);
|
||||
pm_runtime_put_autosuspend(&pci->dev);
|
||||
|
||||
out_free:
|
||||
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
|
||||
|
|
|
@ -174,8 +174,12 @@ static void cs_automute(struct hda_codec *codec)
|
|||
snd_hda_gen_update_outputs(codec);
|
||||
|
||||
if (spec->gpio_eapd_hp || spec->gpio_eapd_speaker) {
|
||||
spec->gpio_data = spec->gen.hp_jack_present ?
|
||||
spec->gpio_eapd_hp : spec->gpio_eapd_speaker;
|
||||
if (spec->gen.automute_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,
|
||||
AC_VERB_SET_GPIO_DATA, spec->gpio_data);
|
||||
}
|
||||
|
|
|
@ -204,8 +204,13 @@ static void cx_auto_reboot_notify(struct hda_codec *codec)
|
|||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/* Turn the CX20722 codec into D3 to avoid spurious noises
|
||||
from the internal speaker during (and after) reboot */
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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, 0x2233, "Thinkpad", ALC293_FIXUP_LENOVO_SPK_NOISE),
|
||||
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, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC),
|
||||
SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP),
|
||||
|
|
|
@ -10,23 +10,10 @@
|
|||
static int (*led_set_func)(int, bool);
|
||||
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)
|
||||
{
|
||||
bool found = false;
|
||||
if (codec->core.subsystem_id >> 16 != 0x17aa)
|
||||
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;
|
||||
return (codec->core.subsystem_id >> 16 == 0x17aa) &&
|
||||
(acpi_dev_present("LEN0068") || acpi_dev_present("IBM0068"));
|
||||
}
|
||||
|
||||
static void update_tpacpi_mute_led(void *private_data, int enabled)
|
||||
|
|
|
@ -2879,6 +2879,7 @@ static void intel8x0_measure_ac97_clock(struct intel8x0 *chip)
|
|||
|
||||
static struct snd_pci_quirk intel8x0_clock_list[] = {
|
||||
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, 0x0177, "AD1980", 48000),
|
||||
SND_PCI_QUIRK(0x1028, 0x01ad, "AD1981B", 48000),
|
||||
|
|
|
@ -132,7 +132,7 @@ static int mixart_set_pipe_state(struct mixart_mgr *mgr,
|
|||
}
|
||||
|
||||
if(start) {
|
||||
u32 stat;
|
||||
u32 stat = 0;
|
||||
|
||||
group_state.pipe_count = 0; /* in case of start same command once again with pipe_count=0 */
|
||||
|
||||
|
|
|
@ -726,7 +726,7 @@ int mixart_update_playback_stream_level(struct snd_mixart* chip, int is_aes, int
|
|||
int volume[2];
|
||||
struct mixart_msg request;
|
||||
struct mixart_set_out_stream_level_req set_level;
|
||||
u32 status;
|
||||
u32 status = 0;
|
||||
struct mixart_pipe *pipe;
|
||||
|
||||
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_msg request;
|
||||
struct mixart_set_in_audio_level_req set_level;
|
||||
u32 status;
|
||||
u32 status = 0;
|
||||
|
||||
if(is_aes) {
|
||||
idx = 1;
|
||||
|
|
|
@ -6,7 +6,7 @@ menuconfig SND_SOC
|
|||
tristate "ALSA for SoC audio support"
|
||||
select SND_PCM
|
||||
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_SPI if SPI_MASTER
|
||||
---help---
|
||||
|
|
|
@ -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,
|
||||
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;
|
||||
int dir, dir_mask;
|
||||
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,
|
||||
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;
|
||||
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,
|
||||
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;
|
||||
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,
|
||||
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) {
|
||||
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_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 ssc_device *ssc = ssc_p->ssc;
|
||||
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,
|
||||
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;
|
||||
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,
|
||||
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;
|
||||
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)
|
||||
{
|
||||
struct atmel_ssc_info *ssc_p;
|
||||
struct platform_device *pdev = to_platform_device(cpu_dai->dev);
|
||||
|
||||
if (!cpu_dai->active)
|
||||
return 0;
|
||||
|
||||
ssc_p = &ssc_info[cpu_dai->id];
|
||||
ssc_p = &ssc_info[pdev->id];
|
||||
|
||||
/* Save the status register before disabling transmit and receive */
|
||||
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)
|
||||
{
|
||||
struct atmel_ssc_info *ssc_p;
|
||||
struct platform_device *pdev = to_platform_device(cpu_dai->dev);
|
||||
u32 cr;
|
||||
|
||||
if (!cpu_dai->active)
|
||||
return 0;
|
||||
|
||||
ssc_p = &ssc_info[cpu_dai->id];
|
||||
ssc_p = &ssc_info[pdev->id];
|
||||
|
||||
/* restore SSC register settings */
|
||||
ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr);
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
|
@ -46,55 +47,6 @@
|
|||
#include <sound/pcm_params.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 */
|
||||
#define BCM2835_I2S_CS_A_REG 0x00
|
||||
#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_TXW BIT(0)
|
||||
|
||||
/* I2S DMA interface */
|
||||
/* FIXME: Needs IOMMU support */
|
||||
#define BCM2835_VCMMU_SHIFT (0x7E000000 - 0x20000000)
|
||||
|
||||
/* General device struct */
|
||||
struct bcm2835_i2s_dev {
|
||||
struct device *dev;
|
||||
|
@ -169,21 +117,23 @@ struct bcm2835_i2s_dev {
|
|||
unsigned int fmt;
|
||||
unsigned int bclk_ratio;
|
||||
|
||||
struct regmap *i2s_regmap;
|
||||
struct regmap *clk_regmap;
|
||||
struct regmap *i2s_regmap;
|
||||
struct clk *clk;
|
||||
bool clk_prepared;
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
if (dev->clk_prepared)
|
||||
return;
|
||||
|
||||
switch (master) {
|
||||
case SND_SOC_DAIFMT_CBS_CFS:
|
||||
case SND_SOC_DAIFMT_CBS_CFM:
|
||||
regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG,
|
||||
BCM2835_CLK_PASSWD_MASK | BCM2835_CLK_ENAB,
|
||||
BCM2835_CLK_PASSWD | BCM2835_CLK_ENAB);
|
||||
clk_prepare_enable(dev->clk);
|
||||
dev->clk_prepared = true;
|
||||
break;
|
||||
default:
|
||||
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)
|
||||
{
|
||||
uint32_t clkreg;
|
||||
int timeout = 1000;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
if (dev->clk_prepared)
|
||||
clk_disable_unprepare(dev->clk);
|
||||
dev->clk_prepared = false;
|
||||
}
|
||||
|
||||
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 csreg;
|
||||
uint32_t i2s_active_state;
|
||||
uint32_t clkreg;
|
||||
uint32_t clk_active_state;
|
||||
bool clk_was_prepared;
|
||||
uint32_t off;
|
||||
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);
|
||||
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 */
|
||||
if (!clk_active_state) {
|
||||
regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG,
|
||||
BCM2835_CLK_PASSWD_MASK | BCM2835_CLK_ENAB,
|
||||
BCM2835_CLK_PASSWD | BCM2835_CLK_ENAB);
|
||||
}
|
||||
clk_was_prepared = dev->clk_prepared;
|
||||
if (!clk_was_prepared)
|
||||
bcm2835_i2s_start_clock(dev);
|
||||
|
||||
/* Stop I2S module */
|
||||
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");
|
||||
|
||||
/* Stop clock if it was not running before */
|
||||
if (!clk_active_state)
|
||||
if (!clk_was_prepared)
|
||||
bcm2835_i2s_stop_clock(dev);
|
||||
|
||||
/* Restore I2S state */
|
||||
|
@ -309,19 +234,9 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
|
|||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
unsigned int sampling_rate = params_rate(params);
|
||||
unsigned int data_length, data_delay, bclk_ratio;
|
||||
unsigned int ch1pos, ch2pos, mode, format;
|
||||
unsigned int 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;
|
||||
|
||||
/*
|
||||
|
@ -343,11 +258,9 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
|
|||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
data_length = 16;
|
||||
bclk_ratio = 40;
|
||||
break;
|
||||
case SNDRV_PCM_FORMAT_S32_LE:
|
||||
data_length = 32;
|
||||
bclk_ratio = 80;
|
||||
break;
|
||||
default:
|
||||
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 (dev->bclk_ratio)
|
||||
bclk_ratio = dev->bclk_ratio;
|
||||
else
|
||||
/* otherwise calculate a fitting block ratio */
|
||||
bclk_ratio = 2 * data_length;
|
||||
|
||||
/*
|
||||
* Clock Settings
|
||||
*
|
||||
* 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));
|
||||
/* set target clock rate*/
|
||||
clk_set_rate(dev->clk, sampling_rate * bclk_ratio);
|
||||
|
||||
/* Setup the frame format */
|
||||
format = BCM2835_I2S_CHEN;
|
||||
|
@ -692,7 +548,7 @@ static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = {
|
|||
.trigger = bcm2835_i2s_trigger,
|
||||
.hw_params = bcm2835_i2s_hw_params,
|
||||
.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)
|
||||
|
@ -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)
|
||||
{
|
||||
switch (reg) {
|
||||
case BCM2835_CLK_PCMCTL_REG:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
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 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,
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
struct bcm2835_i2s_dev *dev;
|
||||
int i;
|
||||
int ret;
|
||||
struct regmap *regmap[2];
|
||||
struct resource *mem[2];
|
||||
|
||||
/* Request both ioareas */
|
||||
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]);
|
||||
}
|
||||
struct resource *mem;
|
||||
void __iomem *base;
|
||||
const __be32 *addr;
|
||||
dma_addr_t dma_base;
|
||||
|
||||
dev = devm_kzalloc(&pdev->dev, sizeof(*dev),
|
||||
GFP_KERNEL);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev->i2s_regmap = regmap[0];
|
||||
dev->clk_regmap = regmap[1];
|
||||
/* get the clock */
|
||||
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 =
|
||||
(dma_addr_t)mem[0]->start + BCM2835_I2S_FIFO_A_REG
|
||||
+ BCM2835_VCMMU_SHIFT;
|
||||
dma_base + BCM2835_I2S_FIFO_A_REG;
|
||||
|
||||
dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr =
|
||||
(dma_addr_t)mem[0]->start + BCM2835_I2S_FIFO_A_REG
|
||||
+ BCM2835_VCMMU_SHIFT;
|
||||
dma_base + BCM2835_I2S_FIFO_A_REG;
|
||||
|
||||
/* Set the bus width */
|
||||
dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr_width =
|
||||
|
|
|
@ -79,7 +79,9 @@ config SND_SOC_ALL_CODECS
|
|||
select SND_SOC_MAX98090 if I2C
|
||||
select SND_SOC_MAX98095 if I2C
|
||||
select SND_SOC_MAX98357A if GPIOLIB
|
||||
select SND_SOC_MAX9867 if I2C
|
||||
select SND_SOC_MAX98925 if I2C
|
||||
select SND_SOC_MAX98926 if I2C
|
||||
select SND_SOC_MAX9850 if I2C
|
||||
select SND_SOC_MAX9768 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_NAU8825 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_PCM3168A_I2C if I2C
|
||||
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_RT286 if I2C
|
||||
select SND_SOC_RT298 if I2C
|
||||
select SND_SOC_RT5514 if I2C
|
||||
select SND_SOC_RT5616 if I2C
|
||||
select SND_SOC_RT5631 if I2C
|
||||
select SND_SOC_RT5640 if I2C
|
||||
|
@ -490,6 +494,7 @@ config SND_SOC_GTM601
|
|||
config SND_SOC_HDAC_HDMI
|
||||
tristate
|
||||
select SND_HDA_EXT_CORE
|
||||
select SND_PCM_ELD
|
||||
select HDMI
|
||||
|
||||
config SND_SOC_ICS43432
|
||||
|
@ -497,6 +502,7 @@ config SND_SOC_ICS43432
|
|||
|
||||
config SND_SOC_INNO_RK3036
|
||||
tristate "Inno codec driver for RK3036 SoC"
|
||||
select REGMAP_MMIO
|
||||
|
||||
config SND_SOC_ISABELLE
|
||||
tristate
|
||||
|
@ -516,9 +522,15 @@ config SND_SOC_MAX98095
|
|||
config SND_SOC_MAX98357A
|
||||
tristate
|
||||
|
||||
config SND_SOC_MAX9867
|
||||
tristate
|
||||
|
||||
config SND_SOC_MAX98925
|
||||
tristate
|
||||
|
||||
config SND_SOC_MAX98926
|
||||
tristate
|
||||
|
||||
config SND_SOC_MAX9850
|
||||
tristate
|
||||
|
||||
|
@ -527,8 +539,23 @@ config SND_SOC_PCM1681
|
|||
depends on I2C
|
||||
|
||||
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
|
||||
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
|
||||
tristate
|
||||
|
@ -565,6 +592,7 @@ config SND_SOC_PCM512x_SPI
|
|||
|
||||
config SND_SOC_RL6231
|
||||
tristate
|
||||
default y if SND_SOC_RT5514=y
|
||||
default y if SND_SOC_RT5616=y
|
||||
default y if SND_SOC_RT5640=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_RT5670=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_RT5640=m
|
||||
default m if SND_SOC_RT5645=m
|
||||
|
@ -595,9 +624,12 @@ config SND_SOC_RT298
|
|||
tristate
|
||||
depends on I2C
|
||||
|
||||
config SND_SOC_RT5616
|
||||
config SND_SOC_RT5514
|
||||
tristate
|
||||
|
||||
config SND_SOC_RT5616
|
||||
tristate "Realtek RT5616 CODEC"
|
||||
|
||||
config SND_SOC_RT5631
|
||||
tristate "Realtek ALC5631/RT5631 CODEC"
|
||||
depends on I2C
|
||||
|
|
|
@ -74,13 +74,17 @@ snd-soc-max98088-objs := max98088.o
|
|||
snd-soc-max98090-objs := max98090.o
|
||||
snd-soc-max98095-objs := max98095.o
|
||||
snd-soc-max98357a-objs := max98357a.o
|
||||
snd-soc-max9867-objs := max9867.o
|
||||
snd-soc-max98925-objs := max98925.o
|
||||
snd-soc-max98926-objs := max98926.o
|
||||
snd-soc-max9850-objs := max9850.o
|
||||
snd-soc-mc13783-objs := mc13783.o
|
||||
snd-soc-ml26124-objs := ml26124.o
|
||||
snd-soc-nau8825-objs := nau8825.o
|
||||
snd-soc-pcm1681-objs := pcm1681.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-pcm3168a-objs := pcm3168a.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-rt286-objs := rt286.o
|
||||
snd-soc-rt298-objs := rt298.o
|
||||
snd-soc-rt5514-objs := rt5514.o
|
||||
snd-soc-rt5616-objs := rt5616.o
|
||||
snd-soc-rt5631-objs := rt5631.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_MAX98095) += snd-soc-max98095.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_MAX98926) += snd-soc-max98926.o
|
||||
obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
|
||||
obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o
|
||||
obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o
|
||||
obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.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_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_PCM3168A) += snd-soc-pcm3168a.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_RT286) += snd-soc-rt286.o
|
||||
obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o
|
||||
obj-$(CONFIG_SND_SOC_RT5514) += snd-soc-rt5514.o
|
||||
obj-$(CONFIG_SND_SOC_RT5616) += snd-soc-rt5616.o
|
||||
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
|
||||
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
|
||||
|
|
|
@ -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",
|
||||
__func__, fmt & SND_SOC_DAIFMT_MASTER_MASK);
|
||||
return -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
snd_soc_update_bits(codec, AB8500_DIGIFCONF3, mask, val);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec
|
||||
* Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec
|
||||
*
|
||||
* Copyright 2014 Analog Devices Inc.
|
||||
* 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);
|
||||
|
||||
#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 = {
|
||||
.driver = {
|
||||
.name = "adau1761",
|
||||
.of_match_table = of_match_ptr(adau1761_i2c_dt_ids),
|
||||
},
|
||||
.probe = adau1761_i2c_probe,
|
||||
.remove = adau1761_i2c_remove,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec
|
||||
* Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec
|
||||
*
|
||||
* Copyright 2014 Analog Devices Inc.
|
||||
* 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);
|
||||
|
||||
#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 = {
|
||||
.driver = {
|
||||
.name = "adau1761",
|
||||
.of_match_table = of_match_ptr(adau1761_spi_dt_ids),
|
||||
},
|
||||
.probe = adau1761_spi_probe,
|
||||
.remove = adau1761_spi_remove,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec
|
||||
* Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec
|
||||
*
|
||||
* Copyright 2011-2013 Analog Devices Inc.
|
||||
* 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:
|
||||
break;
|
||||
case SND_SOC_BIAS_STANDBY:
|
||||
regcache_cache_only(adau->regmap, false);
|
||||
regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
|
||||
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;
|
||||
case SND_SOC_BIAS_OFF:
|
||||
regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
|
||||
ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, 0);
|
||||
regcache_cache_only(adau->regmap, true);
|
||||
break;
|
||||
|
||||
}
|
||||
|
@ -783,6 +787,10 @@ int adau1761_probe(struct device *dev, struct regmap *regmap,
|
|||
if (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);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adau1761_probe);
|
||||
|
|
|
@ -42,9 +42,19 @@ static const struct i2c_device_id 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 = {
|
||||
.driver = {
|
||||
.name = "adau1781",
|
||||
.of_match_table = of_match_ptr(adau1781_i2c_dt_ids),
|
||||
},
|
||||
.probe = adau1781_i2c_probe,
|
||||
.remove = adau1781_i2c_remove,
|
||||
|
|
|
@ -59,9 +59,19 @@ static const struct spi_device_id 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 = {
|
||||
.driver = {
|
||||
.name = "adau1781",
|
||||
.of_match_table = of_match_ptr(adau1781_spi_dt_ids),
|
||||
},
|
||||
.probe = adau1781_spi_probe,
|
||||
.remove = adau1781_spi_remove,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Driver for ADAU1781/ADAU1781 codec
|
||||
* Driver for ADAU1381/ADAU1781 codec
|
||||
*
|
||||
* Copyright 2011-2013 Analog Devices Inc.
|
||||
* Author: Lars-Peter Clausen <lars@metafoo.de>
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include <sound/initval.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#include <linux/of.h>
|
||||
|
||||
#define ADS117X_RATES (SNDRV_PCM_RATE_8000_48000)
|
||||
#define ADS117X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
|
||||
|
||||
|
@ -75,9 +77,19 @@ static int ads117x_remove(struct platform_device *pdev)
|
|||
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 = {
|
||||
.driver = {
|
||||
.name = "ads117x-codec",
|
||||
.of_match_table = of_match_ptr(ads117x_dt_ids),
|
||||
},
|
||||
|
||||
.probe = ads117x_probe,
|
||||
|
|
|
@ -1398,29 +1398,6 @@ static const int arizona_48k_bclk_rates[] = {
|
|||
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[] = {
|
||||
-1,
|
||||
44100,
|
||||
|
@ -1443,22 +1420,7 @@ static const int arizona_44k1_bclk_rates[] = {
|
|||
22579200,
|
||||
};
|
||||
|
||||
static const unsigned int arizona_44k1_rates[] = {
|
||||
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[] = {
|
||||
static const unsigned int arizona_sr_vals[] = {
|
||||
0,
|
||||
12000,
|
||||
24000,
|
||||
|
@ -1485,13 +1447,21 @@ static int arizona_sr_vals[] = {
|
|||
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,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_codec *codec = dai->codec;
|
||||
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
|
||||
struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
|
||||
const struct snd_pcm_hw_constraint_list *constraint;
|
||||
unsigned int base_rate;
|
||||
|
||||
if (!substream->runtime)
|
||||
|
@ -1509,16 +1479,15 @@ static int arizona_startup(struct snd_pcm_substream *substream,
|
|||
}
|
||||
|
||||
if (base_rate == 0)
|
||||
return 0;
|
||||
|
||||
if (base_rate % 8000)
|
||||
constraint = &arizona_44k1_constraint;
|
||||
dai_priv->constraint.mask = ARIZONA_RATE_MASK;
|
||||
else if (base_rate % 8000)
|
||||
dai_priv->constraint.mask = ARIZONA_44K1_RATE_MASK;
|
||||
else
|
||||
constraint = &arizona_48k_constraint;
|
||||
dai_priv->constraint.mask = ARIZONA_48K_RATE_MASK;
|
||||
|
||||
return snd_pcm_hw_constraint_list(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
constraint);
|
||||
&dai_priv->constraint);
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
dai_priv->clk = ARIZONA_CLK_SYSCLK;
|
||||
dai_priv->constraint = arizona_constraint;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2179,11 +2149,12 @@ static int arizona_calc_fll(struct arizona_fll *fll,
|
|||
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);
|
||||
arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n",
|
||||
cfg->fratio, cfg->fratio, cfg->outdiv, cfg->refdiv);
|
||||
arizona_fll_dbg(fll, "GAIN=%d\n", cfg->gain);
|
||||
arizona_fll_dbg(fll, "FRATIO=0x%x(%d) OUTDIV=%d REFCLK_DIV=0x%x(%d)\n",
|
||||
cfg->fratio, ratio, cfg->outdiv,
|
||||
cfg->refdiv, 1 << cfg->refdiv);
|
||||
arizona_fll_dbg(fll, "GAIN=0x%x(%d)\n", cfg->gain, 1 << cfg->gain);
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
#define ARIZONA_CLK_98MHZ 5
|
||||
#define ARIZONA_CLK_147MHZ 6
|
||||
|
||||
#define ARIZONA_MAX_DAI 8
|
||||
#define ARIZONA_MAX_DAI 10
|
||||
#define ARIZONA_MAX_ADSP 4
|
||||
|
||||
#define ARIZONA_DVFS_SR1_RQ 0x001
|
||||
|
@ -68,6 +68,8 @@ struct wm_adsp;
|
|||
|
||||
struct arizona_dai_priv {
|
||||
int clk;
|
||||
|
||||
struct snd_pcm_hw_constraint_list constraint;
|
||||
};
|
||||
|
||||
struct arizona_priv {
|
||||
|
|
|
@ -44,6 +44,7 @@ struct cs42xx8_priv {
|
|||
|
||||
bool slave_mode;
|
||||
unsigned long sysclk;
|
||||
u32 tx_channels;
|
||||
};
|
||||
|
||||
/* -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 i, fm, val, mask;
|
||||
|
||||
if (tx)
|
||||
cs42xx8->tx_channels = params_channels(params);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) {
|
||||
if (cs42xx8_ratios[i].ratio == ratio)
|
||||
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 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,
|
||||
CS42XX8_DACMUTE_ALL, mute ? CS42XX8_DACMUTE_ALL : 0);
|
||||
regmap_write(cs42xx8->regmap, CS42XX8_DACMUTE,
|
||||
mute ? CS42XX8_DACMUTE_ALL : dac_unmute);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -57,6 +57,25 @@ static const struct wm_adsp_region *cs47l24_dsp_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(digital_tlv, -6400, 50, 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,
|
||||
NULL, 0),
|
||||
|
||||
WM_ADSP2("DSP2", 1),
|
||||
WM_ADSP2("DSP3", 2),
|
||||
WM_ADSP2("DSP2", 1, cs47l24_adsp_power_ev),
|
||||
WM_ADSP2("DSP3", 2, cs47l24_adsp_power_ev),
|
||||
|
||||
SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3,
|
||||
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" },
|
||||
{ "AIF3 Capture", NULL, "SYSCLK" },
|
||||
|
||||
{ "Voice Control DSP", NULL, "DSP3" },
|
||||
{ "Voice Control DSP", NULL, "SYSCLK" },
|
||||
|
||||
{ "IN1L PGA", NULL, "IN1L" },
|
||||
{ "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 |\
|
||||
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_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)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
|
||||
struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec);
|
||||
struct arizona *arizona = priv->core.arizona;
|
||||
int ret;
|
||||
|
||||
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_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);
|
||||
if (ret)
|
||||
goto err_adsp2_codec_probe;
|
||||
|
@ -1014,13 +1100,14 @@ err_adsp2_codec_probe:
|
|||
static int cs47l24_codec_remove(struct snd_soc_codec *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[2], codec);
|
||||
|
||||
priv->core.arizona->dapm = NULL;
|
||||
|
||||
arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1057,6 +1144,19 @@ static struct snd_soc_codec_driver soc_codec_dev_cs47l24 = {
|
|||
.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)
|
||||
{
|
||||
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_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));
|
||||
|
||||
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)
|
||||
{
|
||||
snd_soc_unregister_platform(&pdev->dev);
|
||||
snd_soc_unregister_codec(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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__ */
|
|
@ -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, ®);
|
||||
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");
|
|
@ -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
|
|
@ -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, ®);
|
||||
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");
|
|
@ -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
|
|
@ -84,6 +84,7 @@ static const struct nau8825_fll_attr fll_pre_scalar[] = {
|
|||
|
||||
static const struct reg_default nau8825_reg_defaults[] = {
|
||||
{ NAU8825_REG_ENA_CTRL, 0x00ff },
|
||||
{ NAU8825_REG_IIC_ADDR_SET, 0x0 },
|
||||
{ NAU8825_REG_CLK_DIVIDER, 0x0050 },
|
||||
{ NAU8825_REG_FLL1, 0x0 },
|
||||
{ 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)
|
||||
{
|
||||
switch (reg) {
|
||||
case NAU8825_REG_ENA_CTRL:
|
||||
case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV:
|
||||
case NAU8825_REG_ENA_CTRL ... NAU8825_REG_FLL_VCO_RSV:
|
||||
case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL:
|
||||
case NAU8825_REG_INTERRUPT_MASK ... NAU8825_REG_KEYDET_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)
|
||||
{
|
||||
switch (reg) {
|
||||
case NAU8825_REG_RESET ... NAU8825_REG_ENA_CTRL:
|
||||
case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV:
|
||||
case NAU8825_REG_RESET ... NAU8825_REG_FLL_VCO_RSV:
|
||||
case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL:
|
||||
case NAU8825_REG_INTERRUPT_MASK:
|
||||
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,
|
||||
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_POST_PMU:
|
||||
/* Prevent startup click by letting charge pump to ramp up */
|
||||
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;
|
||||
default:
|
||||
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,
|
||||
NAU8825_SAR_ADC_EN_SFT, 0),
|
||||
|
||||
SND_SOC_DAPM_DAC("ADACL", NULL, NAU8825_REG_RDAC, 12, 0),
|
||||
SND_SOC_DAPM_DAC("ADACR", NULL, NAU8825_REG_RDAC, 13, 0),
|
||||
SND_SOC_DAPM_SUPPLY("ADACL Clock", 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("ADACL", 2, NAU8825_REG_RDAC, 12, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA_S("ADACR", 2, NAU8825_REG_RDAC, 13, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA_S("ADACL Clock", 3, NAU8825_REG_RDAC, 8, 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,
|
||||
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("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("HP amp R", NAU8825_REG_CLASSG_CTRL, 2, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("HP amp power", NAU8825_REG_CLASSG_CTRL, 0, 0, NULL,
|
||||
0),
|
||||
SND_SOC_DAPM_PGA_S("HP amp L", 0,
|
||||
NAU8825_REG_CLASSG_CTRL, 1, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA_S("HP amp R", 0,
|
||||
NAU8825_REG_CLASSG_CTRL, 2, 0, NULL, 0),
|
||||
|
||||
SND_SOC_DAPM_SUPPLY("Charge Pump", NAU8825_REG_CHARGE_PUMP, 5, 0,
|
||||
nau8825_pump_event, SND_SOC_DAPM_POST_PMU),
|
||||
SND_SOC_DAPM_PGA_S("Charge Pump", 1, NAU8825_REG_CHARGE_PUMP, 5, 0,
|
||||
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),
|
||||
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),
|
||||
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),
|
||||
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),
|
||||
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),
|
||||
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),
|
||||
|
||||
SND_SOC_DAPM_PGA_S("Output DACL", 2, NAU8825_REG_CHARGE_PUMP, 8, 1, NULL, 0),
|
||||
SND_SOC_DAPM_PGA_S("Output DACR", 2, NAU8825_REG_CHARGE_PUMP, 9, 1, NULL, 0),
|
||||
SND_SOC_DAPM_PGA_S("Output DACL", 7,
|
||||
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("HPOR"),
|
||||
|
@ -375,24 +425,27 @@ static const struct snd_soc_dapm_route nau8825_dapm_routes[] = {
|
|||
{"DACR Mux", "DACR", "DDACR"},
|
||||
{"HP amp L", NULL, "DACL Mux"},
|
||||
{"HP amp R", NULL, "DACR Mux"},
|
||||
{"HP amp L", NULL, "HP amp power"},
|
||||
{"HP amp R", NULL, "HP amp power"},
|
||||
{"ADACL", NULL, "HP amp L"},
|
||||
{"ADACR", NULL, "HP amp R"},
|
||||
{"ADACL", NULL, "ADACL Clock"},
|
||||
{"ADACR", NULL, "ADACR Clock"},
|
||||
{"Output Driver L Stage 1", NULL, "ADACL"},
|
||||
{"Output Driver R Stage 1", NULL, "ADACR"},
|
||||
{"Charge Pump", NULL, "HP amp L"},
|
||||
{"Charge Pump", NULL, "HP amp R"},
|
||||
{"ADACL", NULL, "Charge Pump"},
|
||||
{"ADACR", NULL, "Charge Pump"},
|
||||
{"ADACL Clock", NULL, "ADACL"},
|
||||
{"ADACR Clock", NULL, "ADACR"},
|
||||
{"Output Driver L Stage 1", NULL, "ADACL Clock"},
|
||||
{"Output Driver R Stage 1", NULL, "ADACR Clock"},
|
||||
{"Output Driver L Stage 2", NULL, "Output Driver L 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 R Stage 3", NULL, "Output Driver R Stage 2"},
|
||||
{"Output DACL", NULL, "Output Driver L Stage 3"},
|
||||
{"Output DACR", NULL, "Output Driver R Stage 3"},
|
||||
{"HPOL", NULL, "Output DACL"},
|
||||
{"HPOR", NULL, "Output DACR"},
|
||||
{"HPOL", NULL, "Charge Pump"},
|
||||
{"HPOR", NULL, "Charge Pump"},
|
||||
{"HPOL Pulldown", NULL, "Output DACL"},
|
||||
{"HPOR Pulldown", NULL, "Output DACR"},
|
||||
{"HP Boost Driver", NULL, "HPOL Pulldown"},
|
||||
{"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,
|
||||
|
@ -659,11 +712,10 @@ static int nau8825_jack_insert(struct nau8825 *nau8825)
|
|||
break;
|
||||
}
|
||||
|
||||
if (type & SND_JACK_HEADPHONE) {
|
||||
/* Unground HPL/R */
|
||||
regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0x3, 0);
|
||||
}
|
||||
|
||||
/* Leaving HPOL/R grounded after jack insert by default. They will be
|
||||
* ungrounded as part of the widget power up sequence at the beginning
|
||||
* of playback to reduce pop.
|
||||
*/
|
||||
return type;
|
||||
}
|
||||
|
||||
|
@ -768,6 +820,8 @@ static void nau8825_init_regs(struct nau8825 *nau8825)
|
|||
{
|
||||
struct regmap *regmap = nau8825->regmap;
|
||||
|
||||
/* Latch IIC LSB value */
|
||||
regmap_write(regmap, NAU8825_REG_IIC_ADDR_SET, 0x0001);
|
||||
/* Enable Bias/Vmid */
|
||||
regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
|
||||
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);
|
||||
/* Disable Boost Driver, Automatic Short circuit protection enable */
|
||||
regmap_update_bits(regmap, NAU8825_REG_BOOST,
|
||||
NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS |
|
||||
NAU8825_SHORT_SHUTDOWN_EN,
|
||||
NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS |
|
||||
NAU8825_SHORT_SHUTDOWN_EN);
|
||||
NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_DIS |
|
||||
NAU8825_HP_BOOST_G_DIS | NAU8825_SHORT_SHUTDOWN_EN,
|
||||
NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_DIS |
|
||||
NAU8825_HP_BOOST_G_DIS | NAU8825_SHORT_SHUTDOWN_EN);
|
||||
|
||||
regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL,
|
||||
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);
|
||||
regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1,
|
||||
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 = {
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#define NAU8825_REG_RESET 0x00
|
||||
#define NAU8825_REG_ENA_CTRL 0x01
|
||||
#define NAU8825_REG_IIC_ADDR_SET 0x02
|
||||
#define NAU8825_REG_CLK_DIVIDER 0x03
|
||||
#define NAU8825_REG_FLL1 0x04
|
||||
#define NAU8825_REG_FLL2 0x05
|
||||
|
@ -129,7 +130,7 @@
|
|||
|
||||
/* HSD_CTRL (0xc) */
|
||||
#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_DWN1L (1 << 0)
|
||||
|
||||
|
@ -251,12 +252,18 @@
|
|||
/* DACR_CTRL (0x34) */
|
||||
#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) */
|
||||
#define NAU8825_GPIO2JD1 (1 << 7)
|
||||
#define NAU8825_SOFTWARE_ID_MASK 0x3
|
||||
#define NAU8825_SOFTWARE_ID_NAU8825 0x0
|
||||
|
||||
/* BIAS_ADJ (0x66) */
|
||||
#define NAU8825_BIAS_TESTDAC_EN (0x3 << 8)
|
||||
#define NAU8825_BIAS_VMID (1 << 6)
|
||||
#define NAU8825_BIAS_VMID_SEL_SFT 4
|
||||
#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_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) */
|
||||
#define NAU8825_MICBIAS_JKSLV (1 << 14)
|
||||
#define NAU8825_MICBIAS_JKR2 (1 << 12)
|
||||
|
@ -284,6 +297,7 @@
|
|||
/* BOOST (0x76) */
|
||||
#define NAU8825_PRECHARGE_DIS (1 << 13)
|
||||
#define NAU8825_GLOBAL_BIAS_EN (1 << 12)
|
||||
#define NAU8825_HP_BOOST_DIS (1 << 9)
|
||||
#define NAU8825_HP_BOOST_G_DIS (1 << 8)
|
||||
#define NAU8825_SHORT_SHUTDOWN_EN (1 << 6)
|
||||
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue